diff --git a/src/java/lang/Integer.java b/src/java/lang/Integer.java index 63f5069c..6f3387a3 100644 --- a/src/java/lang/Integer.java +++ b/src/java/lang/Integer.java @@ -1292,8 +1292,7 @@ public static int remainderUnsigned(int dividend, int divisor) { // Bit twiddling /** - * The number of bits used to represent an {@code int} value in two's - * complement binary form. + * The number of bits used to represent an {@code int} value in 二进制补码. * * @since 1.5 */ @@ -1349,11 +1348,7 @@ public static int lowestOneBit(int i) { } /** - * Returns the number of zero bits preceding the highest-order - * ("leftmost") one-bit in the two's complement binary representation - * of the specified {@code int} value. Returns 32 if the - * specified value has no one-bits in its two's complement representation, - * in other words if it is equal to zero. + * 入参的二进制补码左边0的数量 如果入参是0那么结果就是32. * *

Note that this method is closely related to the logarithm base 2. * For all positive {@code int} values x: @@ -1371,8 +1366,9 @@ public static int lowestOneBit(int i) { */ public static int numberOfLeadingZeros(int i) { // HD, Figure 5-6 - if (i == 0) + if (i == 0) { return 32; + } int n = 1; if (i >>> 16 == 0) { n += 16; i <<= 16; } if (i >>> 24 == 0) { n += 8; i <<= 8; } diff --git a/src/java/lang/Thread.java b/src/java/lang/Thread.java index 3da9770f..8f0abc15 100644 --- a/src/java/lang/Thread.java +++ b/src/java/lang/Thread.java @@ -383,19 +383,21 @@ private void init(ThreadGroup g, Runnable target, String name, this.daemon = parent.isDaemon(); // 子线程继承父线程的优先级属性 this.priority = parent.getPriority(); - if (security == null || isCCLOverridden(parent.getClass())) + if (security == null || isCCLOverridden(parent.getClass())) { this.contextClassLoader = parent.getContextClassLoader(); - else + } else { this.contextClassLoader = parent.contextClassLoader; + } this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; setPriority(priority); // 当父线程的 inheritableThreadLocals 的值不为空时 // 会把 inheritableThreadLocals 里面的值全部传递给子线程 - if (inheritThreadLocals && parent.inheritableThreadLocals != null) + if (inheritThreadLocals && parent.inheritableThreadLocals != null) { this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); + } /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; @@ -667,8 +669,9 @@ public Thread(ThreadGroup group, Runnable target, String name, */ public synchronized void start() { // 如果没有初始化,抛异常 - if (threadStatus != 0) + if (threadStatus != 0) { throw new IllegalThreadStateException(); + } // 将当前线程加入到所在的线程组,记录为活跃线程 group.add(this); @@ -844,8 +847,9 @@ public final synchronized void stop(Throwable obj) { */ public void interrupt() { // 如果由别的线程对当前线程发起中断 - if (this != Thread.currentThread()) + if (this != Thread.currentThread()) { checkAccess(); + } synchronized (blockerLock) { Interruptible b = blocker; @@ -1243,6 +1247,7 @@ public final void checkAccess() { * * @return 该线程的字符串表示 */ + @Override public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { @@ -1264,8 +1269,9 @@ public String toString() { */ @CallerSensitive public ClassLoader getContextClassLoader() { - if (contextClassLoader == null) + if (contextClassLoader == null) { return null; + } SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(contextClassLoader, @@ -1401,8 +1407,9 @@ private static class Caches { * "enableContextClassLoaderOverride" RuntimePermission is checked. */ private static boolean isCCLOverridden(Class cl) { - if (cl == Thread.class) + if (cl == Thread.class) { return false; + } processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); @@ -1423,6 +1430,7 @@ private static boolean isCCLOverridden(Class cl) { private static boolean auditSubclass(final Class subcl) { Boolean result = AccessController.doPrivileged( new PrivilegedAction() { + @Override public Boolean run() { for (Class cl = subcl; cl != Thread.class; @@ -1486,6 +1494,7 @@ public enum State { /** * 运行中的线程 + * RUNNABLE_READY调用start方法 RUNNABLE_RUNNING调用run方法 */ RUNNABLE, @@ -1698,8 +1707,9 @@ public int hashCode() { */ @Override public boolean equals(Object obj) { - if (obj == this) + if (obj == this) { return true; + } if (obj instanceof WeakClassKey) { Object referent = get(); diff --git a/src/java/lang/ThreadGroup.java b/src/java/lang/ThreadGroup.java index 1d957362..6d03a714 100644 --- a/src/java/lang/ThreadGroup.java +++ b/src/java/lang/ThreadGroup.java @@ -1047,6 +1047,7 @@ void list(PrintStream out, int indent) { * @param e the uncaught exception. * @since JDK1.0 */ + @Override public void uncaughtException(Thread t, Throwable e) { if (parent != null) { parent.uncaughtException(t, e); diff --git a/src/java/lang/ThreadLocal.java b/src/java/lang/ThreadLocal.java index 72610902..43d79314 100644 --- a/src/java/lang/ThreadLocal.java +++ b/src/java/lang/ThreadLocal.java @@ -324,8 +324,9 @@ private ThreadLocalMap(ThreadLocalMap parentMap) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); - while (table[h] != null) + while (table[h] != null) { h = nextIndex(h, len); + } table[h] = c; size++; } @@ -466,11 +467,16 @@ private void replaceStaleEntry(ThreadLocal key, Object value, // incremental rehashing due to garbage collector freeing // up refs in bunches (i.e., whenever the collector runs). int slotToExpunge = staleSlot; + // 往左找直到遇到空插孔 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; - i = prevIndex(i, len)) - if (e.get() == null) + i = prevIndex(i, len)) { + // 如果遇到垃圾值 + if (e.get() == null) { + // 垃圾值所在插孔下标记作slotToExpunge slotToExpunge = i; + } + } // Find either the key or trailing null slot of run, whichever // occurs first @@ -491,8 +497,9 @@ private void replaceStaleEntry(ThreadLocal key, Object value, tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists - if (slotToExpunge == staleSlot) + if (slotToExpunge == staleSlot) { slotToExpunge = i; + } cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } @@ -500,8 +507,9 @@ private void replaceStaleEntry(ThreadLocal key, Object value, // If we didn't find stale entry on backward scan, the // first stale entry seen while scanning for key is the // first still present in the run. - if (k == null && slotToExpunge == staleSlot) + if (k == null && slotToExpunge == staleSlot) { slotToExpunge = i; + } } // If key not found, put new entry in stale slot @@ -509,8 +517,9 @@ private void replaceStaleEntry(ThreadLocal key, Object value, tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them - if (slotToExpunge != staleSlot) + if (slotToExpunge != staleSlot) { cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); + } } /** @@ -547,8 +556,9 @@ private int expungeStaleEntry(int staleSlot) { // Unlike Knuth 6.4 Algorithm R, we must scan until // null because multiple entries could have been stale. - while (tab[h] != null) + while (tab[h] != null) { h = nextIndex(h, len); + } tab[h] = e; } } @@ -605,8 +615,9 @@ private void rehash() { expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis - if (size >= threshold - threshold / 4) + if (size >= threshold - threshold / 4) { resize(); + } } /** @@ -654,8 +665,9 @@ private void expungeStaleEntries() { int len = tab.length; for (int j = 0; j < len; j++) { Entry e = tab[j]; - if (e != null && e.get() == null) + if (e != null && e.get() == null) { expungeStaleEntry(j); + } } } } diff --git a/src/java/util/ArrayList.java b/src/java/util/ArrayList.java index a3c96265..65a14436 100644 --- a/src/java/util/ArrayList.java +++ b/src/java/util/ArrayList.java @@ -435,8 +435,10 @@ public E remove(int index) { int numMoved = size - index - 1; if (numMoved > 0) // 将elementData数组index+1位置开始拷贝到elementData从index开始的空间 + { System.arraycopy(elementData, index + 1, elementData, index, numMoved); + } // 使size-1 ,设置elementData的size位置为空,让GC来清理内存空间 elementData[--size] = null; //便于垃圾回收器回收 @@ -769,28 +771,33 @@ private class Itr implements Iterator { int expectedModCount = modCount;//迭代过程不运行修改数组,否则就抛出异常 //是否还有下一个 + @Override public boolean hasNext() { return cursor != size; } //下一个元素 + @Override @SuppressWarnings("unchecked") public E next() { - checkForComodification();//检查数组是否被修改 + checkForComodification();// 检查数组是否被修改 int i = cursor; - if (i >= size) + if (i >= size) { throw new NoSuchElementException(); + } Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); - cursor = i + 1;//向后移动游标 - return (E) elementData[lastRet = i];//设置访问的位置并返回这个值 + cursor = i + 1;// 向后移动游标 + return (E) elementData[lastRet = i];// 设置访问的位置并返回这个值 } //删除元素 + @Override public void remove() { - if (lastRet < 0) + if (lastRet < 0) { throw new IllegalStateException(); + } checkForComodification();//检查数组是否被修改 try { @@ -827,8 +834,9 @@ public void forEachRemaining(Consumer consumer) { //检查数组是否被修改 final void checkForComodification() { - if (modCount != expectedModCount) + if (modCount != expectedModCount) { throw new ConcurrentModificationException(); + } } } @@ -841,34 +849,42 @@ private class ListItr extends Itr implements ListIterator { cursor = index; } + @Override public boolean hasPrevious() { return cursor != 0; } + @Override public int nextIndex() { return cursor; } + @Override public int previousIndex() { return cursor - 1; } + @Override @SuppressWarnings("unchecked") public E previous() { checkForComodification(); int i = cursor - 1; - if (i < 0) + if (i < 0) { throw new NoSuchElementException(); + } Object[] elementData = ArrayList.this.elementData; - if (i >= elementData.length) + if (i >= elementData.length) { throw new ConcurrentModificationException(); + } cursor = i; return (E) elementData[lastRet = i]; } + @Override public void set(E e) { - if (lastRet < 0) + if (lastRet < 0) { throw new IllegalStateException(); + } checkForComodification(); try { @@ -878,6 +894,7 @@ public void set(E e) { } } + @Override public void add(E e) { checkForComodification(); diff --git a/src/java/util/HashMap.java b/src/java/util/HashMap.java index bdebd912..16334c56 100644 --- a/src/java/util/HashMap.java +++ b/src/java/util/HashMap.java @@ -660,9 +660,10 @@ public void putAll(Map m) { * @param key 参数key * @return 如果没有映射到node,返回null,否则返回对应的value */ + @Override public V remove(Object key) { Node e; - //根据key来删除node。removeNode方法的具体实现在下面 + // 根据key来删除node。removeNode方法的具体实现在下面 return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value; } diff --git a/src/java/util/HashSet.java b/src/java/util/HashSet.java index fbc106e5..659d1e8b 100644 --- a/src/java/util/HashSet.java +++ b/src/java/util/HashSet.java @@ -232,6 +232,7 @@ public boolean add(E e) { * @param o object to be removed from this set, if present * @return true if the set contained the specified element */ + @Override public boolean remove(Object o) { return map.remove(o)==PRESENT; } diff --git a/src/java/util/LinkedHashMap.java b/src/java/util/LinkedHashMap.java index bd06d86f..5f25d864 100644 --- a/src/java/util/LinkedHashMap.java +++ b/src/java/util/LinkedHashMap.java @@ -190,6 +190,7 @@ public class LinkedHashMap * HashMap.Node subclass for normal LinkedHashMap entries. */ static class Entry extends HashMap.Node { + // 双向链表 Entry before, after; Entry(int hash, K key, V value, Node next) { super(hash, key, value, next); @@ -218,13 +219,15 @@ static class Entry extends HashMap.Node { // internal utilities - // link at the end of list + // link at the end of list 把新节点连接到链表尾部 private void linkNodeLast(LinkedHashMap.Entry p) { LinkedHashMap.Entry last = tail; tail = p; - if (last == null) + // 尾巴没有 头也没有? 所以 新节点就是头 + if (last == null) { head = p; - else { + } else { + // 否则新节点的前一个节点是之前的尾巴 p.before = last; last.after = p; } @@ -255,6 +258,7 @@ void reinitialize() { Node newNode(int hash, K key, V value, Node e) { LinkedHashMap.Entry p = new LinkedHashMap.Entry(hash, key, value, e); + // linkNodeLast(p); return p; } diff --git a/src/java/util/ListIterator.java b/src/java/util/ListIterator.java index 5150f4f1..364befdf 100644 --- a/src/java/util/ListIterator.java +++ b/src/java/util/ListIterator.java @@ -147,6 +147,7 @@ public interface ListIterator extends Iterator { * {@code add} have been called after the last call to * {@code next} or {@code previous} */ + @Override void remove(); /** diff --git a/src/java/util/concurrent/ArrayBlockingQueue.java b/src/java/util/concurrent/ArrayBlockingQueue.java index 2e8b761b..919dfb4f 100644 --- a/src/java/util/concurrent/ArrayBlockingQueue.java +++ b/src/java/util/concurrent/ArrayBlockingQueue.java @@ -159,8 +159,9 @@ private void enqueue(E x) { // assert items[putIndex] == null; final Object[] items = this.items; items[putIndex] = x; - if (++putIndex == items.length) + if (++putIndex == items.length) { putIndex = 0; + } count++; notEmpty.signal(); } @@ -321,14 +322,15 @@ public boolean add(E e) { * * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { - if (count == items.length) + if (count == items.length) { return false; - else { + } else { enqueue(e); return true; } @@ -395,26 +397,34 @@ public E poll() { } } + @Override public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - while (count == 0) + while (count == 0) { notEmpty.await(); + } return dequeue(); } finally { lock.unlock(); } } + @Override public E poll(long timeout, TimeUnit unit) throws InterruptedException { + // 和时间单位组合一下变成纳秒 long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { + // 队列里没有了 while (count == 0) { - if (nanos <= 0) + // 等够超时时间就return null + if (nanos <= 0) { return null; + } + // 等待timeout时间 nanos = notEmpty.awaitNanos(nanos); } return dequeue(); diff --git a/src/java/util/concurrent/BlockingDeque.java b/src/java/util/concurrent/BlockingDeque.java index dd23eb34..b2253ec2 100644 --- a/src/java/util/concurrent/BlockingDeque.java +++ b/src/java/util/concurrent/BlockingDeque.java @@ -466,6 +466,7 @@ E pollLast(long timeout, TimeUnit unit) * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this deque */ + @Override boolean offer(E e); /** diff --git a/src/java/util/concurrent/BlockingQueue.java b/src/java/util/concurrent/BlockingQueue.java index 5110d62b..fd70503a 100644 --- a/src/java/util/concurrent/BlockingQueue.java +++ b/src/java/util/concurrent/BlockingQueue.java @@ -215,6 +215,7 @@ public interface BlockingQueue extends Queue { * @throws IllegalArgumentException if some property of the specified * element prevents it from being added to this queue */ + @Override boolean offer(E e); /** diff --git a/src/java/util/concurrent/ConcurrentHashMap.java b/src/java/util/concurrent/ConcurrentHashMap.java index 0b6047ba..eb9a48f4 100644 --- a/src/java/util/concurrent/ConcurrentHashMap.java +++ b/src/java/util/concurrent/ConcurrentHashMap.java @@ -12,8 +12,8 @@ import java.util.stream.Stream; -public class ConcurrentHashMap extends AbstractMap - implements ConcurrentMap, Serializable { +public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, Serializable { + private static final long serialVersionUID = 7249069246763182397L; @@ -62,10 +62,10 @@ public class ConcurrentHashMap extends AbstractMap static final int MIN_TREEIFY_CAPACITY = 64; /** - * Minimum number of rebinnings per transfer step. Ranges are - * subdivided to allow multiple resizer threads. This value - * serves as a lower bound to avoid resizers encountering - * excessive memory contention. The value should be at least + * 每次转移元素最少是16个格子. Ranges are + * subdivided to allow multiple resizer threads. + * 这个值用来避免过度的内存争用 避免过多的CPU空转 + * . The value should be at least * DEFAULT_CAPACITY. */ private static final int MIN_TRANSFER_STRIDE = 16; @@ -90,8 +90,15 @@ public class ConcurrentHashMap extends AbstractMap * Encodings for Node hash fields. See above for explanation. */ static final int MOVED = -1; // hash for forwarding nodes (forwarding nodes的hash值)、标示位 + static final int TREEBIN = -2; // hash值是-2 表示这是一个TreeBin节点 - static final int RESERVED = -3; // hash for transient reservations + + static final int RESERVED = -3; // hash for 序列化预留的值 + + /** + * Integer.MAX_VALUE + * 01111111 11111111 11111111 11111111 + */ static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash (ReservationNode的hash值) /** @@ -102,11 +109,9 @@ public class ConcurrentHashMap extends AbstractMap /** * For serialization compatibility. */ - private static final ObjectStreamField[] serialPersistentFields = { - new ObjectStreamField("segments", Segment[].class), - new ObjectStreamField("segmentMask", Integer.TYPE), - new ObjectStreamField("segmentShift", Integer.TYPE) - }; + private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("segments", + Segment[].class), new ObjectStreamField( + "segmentMask", Integer.TYPE), new ObjectStreamField("segmentShift", Integer.TYPE) }; /* ---------------- Nodes -------------- */ @@ -116,10 +121,14 @@ public class ConcurrentHashMap extends AbstractMap * 它不允许调用setValue方法直接改变Node的value域,它增加了find方法辅助map.get()方法。 */ static class Node implements Map.Entry { + final int hash; + final K key; + //val和next都会在扩容时发生变化,所以加上volatile来保持可见性和禁止重排序 volatile V val; + volatile Node next; Node(int hash, K key, V val, Node next) { @@ -129,10 +138,12 @@ static class Node implements Map.Entry { this.next = next; } + @Override public final K getKey() { return key; } + @Override public final V getValue() { return val; } @@ -157,14 +168,12 @@ public final V setValue(V value) { /** * HashMap使用if (o == this),且嵌套if;ConcurrentHashMap使用&& */ + @Override public final boolean equals(Object o) { Object k, v, u; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry) o).getKey()) != null && - (v = e.getValue()) != null && - (k == key || k.equals(key)) && - (v == (u = val) || v.equals(u))); + return ((o instanceof Map.Entry) && (k = (e = (Map.Entry) o).getKey()) != null && (v = e.getValue()) != null && (k == key || k.equals( + key)) && (v == (u = val) || v.equals(u))); } /** @@ -175,8 +184,7 @@ Node find(int h, Object k) { if (k != null) { do { K ek; - if (e.hash == h && - ((ek = e.key) == k || (ek != null && k.equals(ek)))) + if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; } while ((e = e.next) != null); } @@ -190,6 +198,8 @@ Node find(int h, Object k) { * 对hashCode进行再散列,算法为(h ^ (h >>> 16)) & HASH_BITS */ static final int spread(int h) { + // HASH_BITS二进制最高位是0 &HASH_BITS 只是为了保证第一位是0 保证hash值是正数 + // h ^ (h >>> 16) 高16位右移异或 去扰动低16位 降低hash冲突 return (h ^ (h >>> 16)) & HASH_BITS; } @@ -209,6 +219,7 @@ private static final int tableSizeFor(int c) { /** * Returns x's Class if it is of the form "class C implements * Comparable", else null. + * 看一下key是否可以比较 */ static Class comparableClassFor(Object x) { if (x instanceof Comparable) { @@ -216,16 +227,17 @@ static Class comparableClassFor(Object x) { Type[] ts, as; Type t; ParameterizedType p; + // String则直接返回Class if ((c = x.getClass()) == String.class) // bypass checks + { return c; + } if ((ts = c.getGenericInterfaces()) != null) { for (int i = 0; i < ts.length; ++i) { - if (((t = ts[i]) instanceof ParameterizedType) && - ((p = (ParameterizedType) t).getRawType() == - Comparable.class) && - (as = p.getActualTypeArguments()) != null && - as.length == 1 && as[0] == c) // type arg is c + if (((t = ts[i]) instanceof ParameterizedType) && ((p = (ParameterizedType) t).getRawType() == Comparable.class) && (as = p.getActualTypeArguments()) != null && as.length == 1 && as[0] == c) // type arg is c + { return c; + } } } } @@ -236,10 +248,10 @@ static Class comparableClassFor(Object x) { * Returns k.compareTo(x) if x matches kc (k's screened comparable * class), else 0. */ - @SuppressWarnings({"rawtypes", "unchecked"}) // for cast to Comparable + @SuppressWarnings( { "rawtypes", "unchecked" }) // for cast to Comparable static int compareComparables(Class kc, Object k, Object x) { - return (x == null || x.getClass() != kc ? 0 : - ((Comparable) k).compareTo(x)); + // 父节点是null 或者 父节点的class和如惨k的class不一样 则返回0 能比较就比一下 + return (x == null || x.getClass() != kc ? 0 : ((Comparable) k).compareTo(x)); } /* ---------------- Table element access -------------- */ @@ -265,14 +277,15 @@ static int compareComparables(Class kc, Object k, Object x) { */ @SuppressWarnings("unchecked") static final Node tabAt(Node[] tab, int i) { + // 2i+16 return (Node) U.getObjectVolatile(tab, ((long) i << ASHIFT) + ABASE); } /** * 利用CAS算法设置i位置上的Node节点(将c和table[i]比较,相同则插入v)。 + * ASHIFT = 2 ABASE = 16 其实我看不太懂 */ - static final boolean casTabAt(Node[] tab, int i, - Node c, Node v) { + static final boolean casTabAt(Node[] tab, int i, Node c, Node v) { return U.compareAndSwapObject(tab, ((long) i << ASHIFT) + ABASE, c, v); } @@ -305,7 +318,7 @@ static final void setTabAt(Node[] tab, int i, Node v) { * 正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小,类似于扩容阈值。 * 它的值始终是当前ConcurrentHashMap容量的0.75倍,这与loadfactor是对应的。实际容量>=sizeCtl,则扩容。 */ - private transient volatile int sizeCtl;//控制标识符 + private transient volatile int sizeCtl;// 控制标识符 /** * The next table index (plus one) to split while resizing. @@ -325,7 +338,9 @@ static final void setTabAt(Node[] tab, int i, Node v) { // views private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; @@ -341,15 +356,15 @@ public ConcurrentHashMap() { * 指定容量的构造函数 * * @param initialCapacity 初始化容量 + * * @throws IllegalArgumentException if the initial capacity of * elements is negative */ public ConcurrentHashMap(int initialCapacity) { if (initialCapacity < 0) throw new IllegalArgumentException(); - int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? - MAXIMUM_CAPACITY : - tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor( + initialCapacity + (initialCapacity >>> 1) + 1)); this.sizeCtl = cap;//初始化sizeCtl } @@ -370,6 +385,7 @@ public ConcurrentHashMap(Map m) { * * @param initialCapacity 初始容量 * @param loadFactor 负载因子,当容量达到initialCapacity*loadFactor时,执行扩容 + * * @throws IllegalArgumentException if the initial capacity of * elements is negative or the load factor is nonpositive * @since 1.6 @@ -387,19 +403,18 @@ public ConcurrentHashMap(int initialCapacity, float loadFactor) { * @param initialCapacity 初始容量 * @param loadFactor 负载因子,当容量达到initialCapacity*loadFactor时,执行扩容 * @param concurrencyLevel 预估的并发更新线程数 + * * @throws IllegalArgumentException if the initial capacity is * negative or the load factor or concurrencyLevel are * nonpositive */ - public ConcurrentHashMap(int initialCapacity, - float loadFactor, int concurrencyLevel) { + public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException(); if (initialCapacity < concurrencyLevel) // Use at least as many bins initialCapacity = concurrencyLevel; // as estimated threads long size = (long) (1.0 + (long) initialCapacity / loadFactor); - int cap = (size >= (long) MAXIMUM_CAPACITY) ? - MAXIMUM_CAPACITY : tableSizeFor((int) size); + int cap = (size >= (long) MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int) size); this.sizeCtl = cap; } @@ -408,16 +423,16 @@ public ConcurrentHashMap(int initialCapacity, /** * {@inheritDoc} */ + @Override public int size() { long n = sumCount(); - return ((n < 0L) ? 0 : - (n > (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE : - (int) n); + return ((n < 0L) ? 0 : (n > (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) n); } /** * {@inheritDoc} */ + @Override public boolean isEmpty() { return sumCount() <= 0L; // ignore transient negative values } @@ -429,6 +444,7 @@ public boolean isEmpty() { * * @throws NullPointerException if the specified key is null */ + @Override public V get(Object key) { Node[] tab; Node e, p; @@ -436,19 +452,21 @@ public V get(Object key) { K ek; int h = spread(key.hashCode());//两次hash计算出hash值 //根据hash值确定节点位置 - if ((tab = table) != null && (n = tab.length) > 0 && - (e = tabAt(tab, (n - 1) & h)) != null) { + if ((tab = table) != null && (n = tab.length) > 0 && (e = tabAt(tab, (n - 1) & h)) != null) { // 搜索到的节点key与传入的key相同且不为null,直接返回这个节点 if ((eh = e.hash) == h) { - if ((ek = e.key) == key || (ek != null && key.equals(ek))) + if ((ek = e.key) == key || (ek != null && key.equals(ek))) { return e.val; + } } else if (eh < 0)//如果eh<0 说明这个节点在树上 直接寻找 + { return (p = e.find(h, key)) != null ? p.val : null; + } //否则遍历链表 找到对应的值并返回 while ((e = e.next) != null) { - if (e.hash == h && - ((ek = e.key) == key || (ek != null && key.equals(ek)))) + if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { return e.val; + } } } return null; @@ -458,11 +476,13 @@ public V get(Object key) { * 检查table中是否含有key * * @param key possible key + * * @return {@code true} if and only if the specified object * is a key in this table, as determined by the * {@code equals} method; {@code false} otherwise * @throws NullPointerException if the specified key is null */ + @Override public boolean containsKey(Object key) { //直接调用get(int key)方法即可,如果有返回值,则说明是包含key的 return get(key) != null; @@ -473,6 +493,7 @@ public boolean containsKey(Object key) { * 这个方法可能需要完全遍历Map,因此比containsKey要慢的多 * * @param value value whose presence in this map is to be tested + * * @return {@code true} if this map maps one or more keys to the * specified value * @throws NullPointerException if the specified value is null @@ -497,10 +518,12 @@ public boolean containsValue(Object value) { * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key + * * @return the previous value associated with {@code key}, or * {@code null} if there was no mapping for {@code key} * @throws NullPointerException if the specified key or value is null */ + @Override public V put(K key, V value) { return putVal(key, value, false); } @@ -520,70 +543,94 @@ public V put(K key, V value) { * 5、如果table[i]的节点是链表节点,则检查table的第i个位置的链表是否需要转化为数,如果需要则调用treeifyBin函数进行转化 */ final V putVal(K key, V value, boolean onlyIfAbsent) { - if (key == null || value == null) throw new NullPointerException();// key和value不允许null - int hash = spread(key.hashCode());//两次hash,减少hash冲突,可以均匀分布 - int binCount = 0;//i处结点标志,0: 未加入新结点, 2: TreeBin或链表结点数, 其它:链表结点数。主要用于每次加入结点后查看是否要由链表转为红黑树 - for (Node[] tab = table; ; ) {//CAS经典写法,不成功无限重试 + // key和value不允许null + if (key == null || value == null) { + throw new NullPointerException(); + } + // 两次hash,减少hash冲突,可以均匀分布 + int hash = spread(key.hashCode()); + // 插孔节点数标志 + int binCount = 0;// i处结点标志,0: 未加入新结点, 2: TreeBin或链表结点数, 其它:链表结点数。主要用于每次加入结点后查看是否要由链表转为红黑树 + for (Node[] tab = table; ; ) {// CAS经典写法,不成功无限重试 Node f; int n, i, fh; - //检查是否初始化了,如果没有,则初始化 - if (tab == null || (n = tab.length) == 0) + // 检查是否初始化了,如果没有,则初始化 + if (tab == null || (n = tab.length) == 0) { tab = initTable(); - /** + } + /* * i=(n-1)&hash 等价于i=hash%n(前提是n为2的幂次方).即取出table中位置的节点用f表示。 有如下两种情况: * 1、如果table[i]==null(即该位置的节点为空,没有发生碰撞),则利用CAS操作直接存储在该位置, 如果CAS操作成功则退出死循环。 * 2、如果table[i]!=null(即该位置已经有其它节点,发生碰撞) */ - else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { - if (casTabAt(tab, i, null, - new Node(hash, key, value, null))) + else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 如果插孔是空的 + // 则通过cas往插孔里面放一个新节点 放不进去会通过上面的for循环一直往里放 直到某个线程放进去了 + if (casTabAt(tab, i, null, new Node(hash, key, value, null))) { break; // no lock when adding to empty bin - } else if ((fh = f.hash) == MOVED)//检查table[i]的节点的hash是否等于MOVED,如果等于,则检测到正在扩容,则帮助其扩容 + } + } + // 检查头节点是不是fwd + else if ((fh = f.hash) == MOVED)// 检查table[i]的节点的hash是否等于MOVED,如果等于,则检测到正在扩容,则帮助其扩容 + { tab = helpTransfer(tab, f); - else {//table[i]的节点的hash值不等于MOVED。 + } else {// table[i]的节点的hash值不等于MOVED。 V oldVal = null; // 针对首个节点进行加锁操作,而不是segment,进一步减少线程冲突 synchronized (f) { + // 确认一下头节点是否变化 防止抢到头节点已经被别的线程改过了 if (tabAt(tab, i) == f) { + // first hash 再次通过头节点hash确认 这个插孔不是正在迁移的插孔 + // 只有链表大于0? if (fh >= 0) { + // 进来啦 binCount = 1; for (Node e = f; ; ++binCount) { K ek; // 如果在链表中找到值为key的节点e,直接设置e.val = value即可 - if (e.hash == hash && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { + // 记录老值 oldVal = e.val; - if (!onlyIfAbsent) + // 如果可以覆盖 + if (!onlyIfAbsent) { + // 覆盖老值 e.val = value; + } + // 覆盖后跳出循环 break; } // 如果没有找到值为key的节点,直接新建Node并加入链表即可 Node pred = e; if ((e = e.next) == null) {//插入到链表末尾并跳出循环 - pred.next = new Node(hash, key, - value, null); + pred.next = new Node(hash, key, value, null); break; } } - } else if (f instanceof TreeBin) {// 如果首节点为TreeBin类型,说明为红黑树结构,执行putTreeVal操作 + } + // 红黑树节点 + else if (f instanceof TreeBin) {// 如果首节点为TreeBin类型,说明为红黑树结构,执行putTreeVal操作 Node p; binCount = 2; - if ((p = ((TreeBin) f).putTreeVal(hash, key, - value)) != null) { + + // 如果要插入的值已经存在了就替换 不然就插进去 + if ((p = ((TreeBin) f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; - if (!onlyIfAbsent) + if (!onlyIfAbsent) { p.val = value; + } } } } } + // 红黑树节点数一定不是0 if (binCount != 0) { // 如果节点数>=8,那么转换链表结构为红黑树结构 - if (binCount >= TREEIFY_THRESHOLD) - treeifyBin(tab, i);//若length<64,直接tryPresize,两倍table.length;不转红黑树 - if (oldVal != null) + if (binCount >= TREEIFY_THRESHOLD) { + treeifyBin(tab, i);// 若length<64,直接tryPresize,两倍table.length;不转红黑树 + } + // 如果是替换的 就不增加总数量 直接返回 + if (oldVal != null) { return oldVal; + } break; } } @@ -600,10 +647,12 @@ else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { * * @param m mappings to be stored in this map */ + @Override public void putAll(Map m) { tryPresize(m.size()); - for (Map.Entry e : m.entrySet()) + for (Map.Entry e : m.entrySet()) { putVal(e.getKey(), e.getValue(), false); + } } /** @@ -611,10 +660,12 @@ public void putAll(Map m) { * This method does nothing if the key is not in the map. * * @param key the key that needs to be removed + * * @return the previous value associated with {@code key}, or * {@code null} if there was no mapping for {@code key} * @throws NullPointerException if the specified key is null */ + @Override public V remove(Object key) { return replaceNode(key, null, null); } @@ -629,12 +680,11 @@ final V replaceNode(Object key, V value, Object cv) { for (Node[] tab = table; ; ) { Node f; int n, i, fh; - if (tab == null || (n = tab.length) == 0 || - (f = tabAt(tab, i = (n - 1) & hash)) == null) + if (tab == null || (n = tab.length) == 0 || (f = tabAt(tab, i = (n - 1) & hash)) == null) { break; - else if ((fh = f.hash) == MOVED) + } else if ((fh = f.hash) == MOVED) { tab = helpTransfer(tab, f); - else { + } else { V oldVal = null; boolean validated = false; synchronized (f) { @@ -643,40 +693,38 @@ else if ((fh = f.hash) == MOVED) validated = true; for (Node e = f, pred = null; ; ) { K ek; - if (e.hash == hash && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { V ev = e.val; - if (cv == null || cv == ev || - (ev != null && cv.equals(ev))) { + if (cv == null || cv == ev || (ev != null && cv.equals(ev))) { oldVal = ev; - if (value != null) + if (value != null) { e.val = value; - else if (pred != null) + } else if (pred != null) { pred.next = e.next; - else + } else { setTabAt(tab, i, e.next); + } } break; } pred = e; - if ((e = e.next) == null) + if ((e = e.next) == null) { break; + } } } else if (f instanceof TreeBin) { validated = true; TreeBin t = (TreeBin) f; TreeNode r, p; - if ((r = t.root) != null && - (p = r.findTreeNode(hash, key, null)) != null) { + if ((r = t.root) != null && (p = r.findTreeNode(hash, key, null)) != null) { V pv = p.val; - if (cv == null || cv == pv || - (pv != null && cv.equals(pv))) { + if (cv == null || cv == pv || (pv != null && cv.equals(pv))) { oldVal = pv; - if (value != null) + if (value != null) { p.val = value; - else if (t.removeTreeNode(p)) + } else if (t.removeTreeNode(p)) { setTabAt(tab, i, untreeify(t.first)); + } } } } @@ -684,8 +732,9 @@ else if (t.removeTreeNode(p)) } if (validated) { if (oldVal != null) { - if (value == null) + if (value == null) { addCount(-1L, -1); + } return oldVal; } break; @@ -698,6 +747,7 @@ else if (t.removeTreeNode(p)) /** * Removes all of the mappings from this map. */ + @Override public void clear() { long delta = 0L; // negative number of deletions int i = 0; @@ -713,9 +763,7 @@ else if ((fh = f.hash) == MOVED) { } else { synchronized (f) { if (tabAt(tab, i) == f) { - Node p = (fh >= 0 ? f : - (f instanceof TreeBin) ? - ((TreeBin) f).first : null); + Node p = (fh >= 0 ? f : (f instanceof TreeBin) ? ((TreeBin) f).first : null); while (p != null) { --delta; p = p.next; @@ -725,8 +773,9 @@ else if ((fh = f.hash) == MOVED) { } } } - if (delta != 0L) + if (delta != 0L) { addCount(delta, -1); + } } /** @@ -747,6 +796,7 @@ else if ((fh = f.hash) == MOVED) { * * @return the set view */ + @Override public KeySetView keySet() { KeySetView ks; return (ks = keySet) != null ? ks : (keySet = new KeySetView(this, null)); @@ -770,6 +820,7 @@ public KeySetView keySet() { * * @return the collection view */ + @Override public Collection values() { ValuesView vs; return (vs = values) != null ? vs : (values = new ValuesView(this)); @@ -792,6 +843,7 @@ public Collection values() { * * @return the set view */ + @Override public Set> entrySet() { EntrySetView es; return (es = entrySet) != null ? es : (entrySet = new EntrySetView(this)); @@ -804,6 +856,7 @@ public Set> entrySet() { * * @return the hash code value for this map */ + @Override public int hashCode() { int h = 0; Node[] t; @@ -826,6 +879,7 @@ public int hashCode() { * * @return a string representation of this map */ + @Override public String toString() { Node[] t; int f = (t = table) == null ? 0 : t.length; @@ -856,12 +910,15 @@ public String toString() { * of this method. * * @param o object to be compared for equality with this map + * * @return {@code true} if the specified object is equal to this map */ + @Override public boolean equals(Object o) { if (o != this) { - if (!(o instanceof Map)) + if (!(o instanceof Map)) { return false; + } Map m = (Map) o; Node[] t; int f = (t = table) == null ? 0 : t.length; @@ -869,15 +926,14 @@ public boolean equals(Object o) { for (Node p; (p = it.advance()) != null; ) { V val = p.val; Object v = m.get(p.key); - if (v == null || (v != val && !v.equals(val))) + if (v == null || (v != val && !v.equals(val))) { return false; + } } for (Map.Entry e : m.entrySet()) { Object mk, mv, v; - if ((mk = e.getKey()) == null || - (mv = e.getValue()) == null || - (v = get(mk)) == null || - (mv != v && !mv.equals(v))) + if ((mk = e.getKey()) == null || (mv = e.getValue()) == null || (v = get( + mk)) == null || (mv != v && !mv.equals(v))) return false; } } @@ -889,7 +945,9 @@ public boolean equals(Object o) { * declared for the sake of serialization compatibility */ static class Segment extends ReentrantLock implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; Segment(float lf) { @@ -902,13 +960,13 @@ static class Segment extends ReentrantLock implements Serializable { * stream (i.e., serializes it). * * @param s the stream + * * @throws java.io.IOException if an I/O error occurs * @serialData the key (Object) and value (Object) * for each key-value mapping, followed by a null pair. * The key-value mappings are emitted in no particular order. */ - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // For serialization compatibility // Emulate segment calculation from previous version of this class int sshift = 0; @@ -920,8 +978,7 @@ private void writeObject(java.io.ObjectOutputStream s) int segmentShift = 32 - sshift; int segmentMask = ssize - 1; @SuppressWarnings("unchecked") - Segment[] segments = (Segment[]) - new Segment[DEFAULT_CONCURRENCY_LEVEL]; + Segment[] segments = (Segment[]) new Segment[DEFAULT_CONCURRENCY_LEVEL]; for (int i = 0; i < segments.length; ++i) segments[i] = new Segment(LOAD_FACTOR); s.putFields().put("segments", segments); @@ -946,12 +1003,12 @@ private void writeObject(java.io.ObjectOutputStream s) * Reconstitutes the instance from a stream (that is, deserializes it). * * @param s the stream + * * @throws ClassNotFoundException if the class of a serialized object * could not be found * @throws java.io.IOException if an I/O error occurs */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { + private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { /* * To improve performance in typical cases, we create nodes * while reading, then place in table once size is known. @@ -974,13 +1031,13 @@ private void readObject(java.io.ObjectInputStream s) } else break; } - if (size == 0L) + if (size == 0L) { sizeCtl = 0; - else { + } else { int n; - if (size >= (long) (MAXIMUM_CAPACITY >>> 1)) + if (size >= (long) (MAXIMUM_CAPACITY >>> 1)) { n = MAXIMUM_CAPACITY; - else { + } else { int sz = (int) size; n = tableSizeFor(sz + (sz >>> 1) + 1); } @@ -992,14 +1049,15 @@ private void readObject(java.io.ObjectInputStream s) boolean insertAtFront; Node next = p.next, first; int h = p.hash, j = h & mask; - if ((first = tabAt(tab, j)) == null) + if ((first = tabAt(tab, j)) == null) { insertAtFront = true; - else { + } else { K k = p.key; if (first.hash < 0) { TreeBin t = (TreeBin) first; - if (t.putTreeVal(h, k, p.val) == null) + if (t.putTreeVal(h, k, p.val) == null) { ++added; + } insertAtFront = false; } else { int binCount = 0; @@ -1007,9 +1065,7 @@ private void readObject(java.io.ObjectInputStream s) Node q; K qk; for (q = first; q != null; q = q.next) { - if (q.hash == h && - ((qk = q.key) == k || - (qk != null && k.equals(qk)))) { + if (q.hash == h && ((qk = q.key) == k || (qk != null && k.equals(qk)))) { insertAtFront = false; break; } @@ -1021,12 +1077,12 @@ private void readObject(java.io.ObjectInputStream s) p.next = first; TreeNode hd = null, tl = null; for (q = p; q != null; q = q.next) { - TreeNode t = new TreeNode - (q.hash, q.key, q.val, null, null); - if ((t.prev = tl) == null) + TreeNode t = new TreeNode(q.hash, q.key, q.val, null, null); + if ((t.prev = tl) == null) { hd = t; - else + } else { tl.next = t; + } tl = t; } setTabAt(tab, j, new TreeBin(hd)); @@ -1055,6 +1111,7 @@ private void readObject(java.io.ObjectInputStream s) * or {@code null} if there was no mapping for the key * @throws NullPointerException if the specified key or value is null */ + @Override public V putIfAbsent(K key, V value) { return putVal(key, value, true); } @@ -1064,9 +1121,11 @@ public V putIfAbsent(K key, V value) { * * @throws NullPointerException if the specified key is null */ + @Override public boolean remove(Object key, Object value) { - if (key == null) + if (key == null) { throw new NullPointerException(); + } return value != null && replaceNode(key, null, value) != null; } @@ -1075,9 +1134,11 @@ public boolean remove(Object key, Object value) { * * @throws NullPointerException if any of the arguments are null */ + @Override public boolean replace(K key, V oldValue, V newValue) { - if (key == null || oldValue == null || newValue == null) + if (key == null || oldValue == null || newValue == null) { throw new NullPointerException(); + } return replaceNode(key, newValue, oldValue) != null; } @@ -1088,9 +1149,11 @@ public boolean replace(K key, V oldValue, V newValue) { * or {@code null} if there was no mapping for the key * @throws NullPointerException if the specified key or value is null */ + @Override public V replace(K key, V value) { - if (key == null || value == null) + if (key == null || value == null) { throw new NullPointerException(); + } return replaceNode(key, value, null); } @@ -1104,16 +1167,20 @@ public V replace(K key, V value) { * @param key the key whose associated value is to be returned * @param defaultValue the value to return if this map contains * no mapping for the given key + * * @return the mapping for the key, if present; else the default value * @throws NullPointerException if the specified key is null */ + @Override public V getOrDefault(Object key, V defaultValue) { V v; return (v = get(key)) == null ? defaultValue : v; } + @Override public void forEach(BiConsumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node[] t; if ((t = table) != null) { Traverser it = new Traverser(t, t.length, 0, t.length); @@ -1124,7 +1191,8 @@ public void forEach(BiConsumer action) { } public void replaceAll(BiFunction function) { - if (function == null) throw new NullPointerException(); + if (function == null) + throw new NullPointerException(); Node[] t; if ((t = table) != null) { Traverser it = new Traverser(t, t.length, 0, t.length); @@ -1134,8 +1202,7 @@ public void replaceAll(BiFunction function) { V newValue = function.apply(key, oldValue); if (newValue == null) throw new NullPointerException(); - if (replaceNode(key, newValue, oldValue) != null || - (oldValue = get(key)) == null) + if (replaceNode(key, newValue, oldValue) != null || (oldValue = get(key)) == null) break; } } @@ -1154,6 +1221,7 @@ public void replaceAll(BiFunction function) { * * @param key key with which the specified value is to be associated * @param mappingFunction the function to compute a value + * * @return the current (existing or computed) value associated with * the specified key, or null if the computed value is null * @throws NullPointerException if the specified key or mappingFunction @@ -1202,9 +1270,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { for (Node e = f; ; ++binCount) { K ek; V ev; - if (e.hash == h && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { val = e.val; break; } @@ -1221,8 +1287,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { binCount = 2; TreeBin t = (TreeBin) f; TreeNode r, p; - if ((r = t.root) != null && - (p = r.findTreeNode(h, key, null)) != null) + if ((r = t.root) != null && (p = r.findTreeNode(h, key, null)) != null) val = p.val; else if ((val = mappingFunction.apply(key)) != null) { added = true; @@ -1256,6 +1321,7 @@ else if ((val = mappingFunction.apply(key)) != null) { * * @param key key with which a value may be associated * @param remappingFunction the function to compute a value + * * @return the new value associated with the specified key, or null if none * @throws NullPointerException if the specified key or remappingFunction * is null @@ -1288,9 +1354,7 @@ else if ((fh = f.hash) == MOVED) binCount = 1; for (Node e = f, pred = null; ; ++binCount) { K ek; - if (e.hash == h && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { val = remappingFunction.apply(key, e.val); if (val != null) e.val = val; @@ -1312,15 +1376,15 @@ else if ((fh = f.hash) == MOVED) binCount = 2; TreeBin t = (TreeBin) f; TreeNode r, p; - if ((r = t.root) != null && - (p = r.findTreeNode(h, key, null)) != null) { + if ((r = t.root) != null && (p = r.findTreeNode(h, key, null)) != null) { val = remappingFunction.apply(key, p.val); if (val != null) p.val = val; else { delta = -1; - if (t.removeTreeNode(p)) + if (t.removeTreeNode(p)) { setTabAt(tab, i, untreeify(t.first)); + } } } } @@ -1346,6 +1410,7 @@ else if ((fh = f.hash) == MOVED) * * @param key key with which the specified value is to be associated * @param remappingFunction the function to compute a value + * * @return the new value associated with the specified key, or null if none * @throws NullPointerException if the specified key or remappingFunction * is null @@ -1355,8 +1420,7 @@ else if ((fh = f.hash) == MOVED) * @throws RuntimeException or Error if the remappingFunction does so, * in which case the mapping is unchanged */ - public V compute(K key, - BiFunction remappingFunction) { + public V compute(K key, BiFunction remappingFunction) { if (key == null || remappingFunction == null) throw new NullPointerException(); int h = spread(key.hashCode()); @@ -1395,9 +1459,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { binCount = 1; for (Node e = f, pred = null; ; ++binCount) { K ek; - if (e.hash == h && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { val = remappingFunction.apply(key, e.val); if (val != null) e.val = val; @@ -1416,8 +1478,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { val = remappingFunction.apply(key, null); if (val != null) { delta = 1; - pred.next = - new Node(h, key, val, null); + pred.next = new Node(h, key, val, null); } break; } @@ -1473,6 +1534,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { * @param key key with which the specified value is to be associated * @param value the value to use if absent * @param remappingFunction the function to recompute a value if present + * * @return the new value associated with the specified key, or null if none * @throws NullPointerException if the specified key or the * remappingFunction is null @@ -1506,9 +1568,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { binCount = 1; for (Node e = f, pred = null; ; ++binCount) { K ek; - if (e.hash == h && - ((ek = e.key) == key || - (ek != null && key.equals(ek)))) { + if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { val = remappingFunction.apply(e.val, value); if (val != null) e.val = val; @@ -1526,8 +1586,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { if ((e = e.next) == null) { delta = 1; val = value; - pred.next = - new Node(h, key, val, null); + pred.next = new Node(h, key, val, null); break; } } @@ -1535,10 +1594,8 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { binCount = 2; TreeBin t = (TreeBin) f; TreeNode r = t.root; - TreeNode p = (r == null) ? null : - r.findTreeNode(h, key, null); - val = (p == null) ? value : - remappingFunction.apply(p.val, value); + TreeNode p = (r == null) ? null : r.findTreeNode(h, key, null); + val = (p == null) ? value : remappingFunction.apply(p.val, value); if (val != null) { if (p != null) p.val = val; @@ -1577,6 +1634,7 @@ else if ((f = tabAt(tab, i = (n - 1) & h)) == null) { * Java Collections framework. * * @param value a value to search for + * * @return {@code true} if and only if some key maps to the * {@code value} argument in this table as * determined by the {@code equals} method; @@ -1633,12 +1691,12 @@ public long mappingCount() { * from the given type to {@code Boolean.TRUE}. * * @param the element type of the returned set + * * @return the new set * @since 1.8 */ public static KeySetView newKeySet() { - return new KeySetView - (new ConcurrentHashMap(), Boolean.TRUE); + return new KeySetView(new ConcurrentHashMap(), Boolean.TRUE); } /** @@ -1648,14 +1706,14 @@ public static KeySetView newKeySet() { * @param initialCapacity The implementation performs internal * sizing to accommodate this many elements. * @param the element type of the returned set + * * @return the new set * @throws IllegalArgumentException if the initial capacity of * elements is negative * @since 1.8 */ public static KeySetView newKeySet(int initialCapacity) { - return new KeySetView - (new ConcurrentHashMap(initialCapacity), Boolean.TRUE); + return new KeySetView(new ConcurrentHashMap(initialCapacity), Boolean.TRUE); } /** @@ -1666,6 +1724,7 @@ public static KeySetView newKeySet(int initialCapacity) { * the same value for all additions from this view. * * @param mappedValue the mapped value to use for any additions + * * @return the set view * @throws NullPointerException if the mappedValue is null */ @@ -1681,6 +1740,7 @@ public KeySetView keySet(V mappedValue) { * A node inserted at head of bins during transfer operations. */ static final class ForwardingNode extends Node { + final Node[] nextTable; ForwardingNode(Node[] tab) { @@ -1688,21 +1748,21 @@ static final class ForwardingNode extends Node { this.nextTable = tab; } + @Override Node find(int h, Object k) { // loop to avoid arbitrarily deep recursion on forwarding nodes outer: for (Node[] tab = nextTable; ; ) { Node e; int n; - if (k == null || tab == null || (n = tab.length) == 0 || - (e = tabAt(tab, (n - 1) & h)) == null) + if (k == null || tab == null || (n = tab.length) == 0 || (e = tabAt(tab, (n - 1) & h)) == null) return null; for (; ; ) { int eh; K ek; - if ((eh = e.hash) == h && - ((ek = e.key) == k || (ek != null && k.equals(ek)))) + if ((eh = e.hash) == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) { return e; + } if (eh < 0) { if (e instanceof ForwardingNode) { tab = ((ForwardingNode) e).nextTable; @@ -1721,10 +1781,12 @@ Node find(int h, Object k) { * A place-holder node used in computeIfAbsent and compute */ static final class ReservationNode extends Node { + ReservationNode() { super(RESERVED, null, null, null); } + @Override Node find(int h, Object k) { return null; } @@ -1734,31 +1796,47 @@ Node find(int h, Object k) { /** * Returns the stamp bits for resizing a table of size n. - * Must be negative when shifted left by RESIZE_STAMP_SHIFT. + * Must be negative负数 when shifted(移位) left by RESIZE_STAMP_SHIFT. */ static final int resizeStamp(int n) { + // (1 << (RESIZE_STAMP_BITS - 1)) -> 1000 0000 0000 0000 + // n表示老数组的长度 如果入参length是16 + // 如果n是16 则是 0001 1011|1000 0000 0000 10000 + // 结果是 0000 0000 0000 0000 1000 0000 0001 1011 return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); } /** - * Initializes table, using the size recorded in sizeCtl. + * 初始hash表, using the size recorded in sizeCtl. */ private final Node[] initTable() { + // 数组的临时变量 Node[] tab; + // 尺寸控制器临时变量 int sc; + // 初始化不成功就一直搞 while ((tab = table) == null || tab.length == 0) { - if ((sc = sizeCtl) < 0) + // 负数代表正在扩容 + if ((sc = sizeCtl) < 0) { + // 此线程不去竞争 只做自旋 Thread.yield(); // lost initialization race; just spin - else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { + } else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {// 对象 老值 副本 期望值 自旋修改sc + // 如果自旋成功返回true会往下走 否则回while处 try { + // 按理说 能自旋成功的只有一个线程 这里可能是为了double check if ((tab = table) == null || tab.length == 0) { + // 尺寸控制器赋值 int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + // 创建节点对象 @SuppressWarnings("unchecked") Node[] nt = (Node[]) new Node[n]; + // 给数组赋值 table = tab = nt; + // sc = 0.75 * n 即阈值 sc = n - (n >>> 2); } } finally { + // sc之前的赋值不会影响sizeCtl 在这里来给sizeCtl赋值 sizeCtl = sc; } break; @@ -1780,39 +1858,36 @@ else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { private final void addCount(long x, int check) { CounterCell[] as; long b, s; - if ((as = counterCells) != null || - !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { + if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) { CounterCell a; long v; int m; boolean uncontended = true; - if (as == null || (m = as.length - 1) < 0 || - (a = as[ThreadLocalRandom.getProbe() & m]) == null || - !(uncontended = - U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { + if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong( + a, CELLVALUE, v = a.value, v + x))) { fullAddCount(x, uncontended); return; } - if (check <= 1) + if (check <= 1) { return; + } s = sumCount(); } if (check >= 0) { Node[] tab, nt; int n, sc; - while (s >= (long) (sc = sizeCtl) && (tab = table) != null && - (n = tab.length) < MAXIMUM_CAPACITY) { + while (s >= (long) (sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) { int rs = resizeStamp(n); if (sc < 0) { - if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || - sc == rs + MAX_RESIZERS || (nt = nextTable) == null || - transferIndex <= 0) + if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) { break; - if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) + } + if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { transfer(tab, nt); - } else if (U.compareAndSwapInt(this, SIZECTL, sc, - (rs << RESIZE_STAMP_SHIFT) + 2)) + } + } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) { transfer(tab, null); + } s = sumCount(); } } @@ -1822,34 +1897,44 @@ private final void addCount(long x, int check) { * Helps transfer if a resize is in progress. */ final Node[] helpTransfer(Node[] tab, Node f) { + // 新数组 Node[] nextTab; int sc; - if (tab != null && (f instanceof ForwardingNode) && - (nextTab = ((ForwardingNode) f).nextTable) != null) { + // 如果正在扩容 + if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode) f).nextTable) != null) { + // 生成一个和扩容有关的扩容戳 扩容戳的最高位1是相对固定的 1左移15位 后面几位表示数组的长度 + // 如果n是16 rs是 0000 0000 0000 0000 1000 0000 0001 1011 int rs = resizeStamp(tab.length); - while (nextTab == nextTable && table == tab && - (sc = sizeCtl) < 0) { - if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || - sc == rs + MAX_RESIZERS || transferIndex <= 0) + // 如果还在扩容则协助扩容 + // sc = sizeCtl -N 表示有N-1个线程正在进行扩容操作 如果不能协助扩容 扩容又没有完成就一直空转 + while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) { + // 高16位代表扩容的标记、低16位代表并行扩容的线程数 + // 如果(sc >>> RESIZE_STAMP_SHIFT) != rs 则表示扩容已经完成了 因为生成了新的扩容戳 扩容戳与数组的length相关 length变化则戳记变化 + // sc == rs + MAX_RESIZERS 达到了最大扩容线程数 + // 这里简单记作 如果扩容完成了就不协助了 我也没太看懂这个表达式 + if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || transferIndex <= 0) { break; + } + // 扩容线程数加一 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { + // 协助扩容 传入老表 和 新表 transfer(tab, nextTab); break; } } + // 返回新的数组 return nextTab; } return table; } /** - * Tries to presize table to accommodate the given number of elements. + * 尝试预扩容表 以容纳给定的元素个数 * * @param size number of elements (doesn't need to be perfectly accurate) */ private final void tryPresize(int size) { - int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : - tableSizeFor(size + (size >>> 1) + 1); + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node[] tab = table; @@ -1874,14 +1959,11 @@ else if (tab == table) { int rs = resizeStamp(n); if (sc < 0) { Node[] nt; - if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || - sc == rs + MAX_RESIZERS || (nt = nextTable) == null || - transferIndex <= 0) + if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); - } else if (U.compareAndSwapInt(this, SIZECTL, sc, - (rs << RESIZE_STAMP_SHIFT) + 2)) + } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } @@ -1892,125 +1974,197 @@ else if (tab == table) { * above for explanation. */ private final void transfer(Node[] tab, Node[] nextTab) { + // 把老表长赋给n 并声明一个int型步长变量 int n = tab.length, stride; - if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) + // 多线程补偿是 老表长除以8再除以CPU核数 8核电脑NCPU=16 + // 单线程的步长是n + if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) { + // 步长 stride = MIN_TRANSFER_STRIDE; // subdivide range + } + // 新表还没创建 if (nextTab == null) { // initiating try { + // 创建新表 @SuppressWarnings("unchecked") Node[] nt = (Node[]) new Node[n << 1]; nextTab = nt; } catch (Throwable ex) { // try to cope with OOME + // 捕获 OOM Exception 为啥给这个值? sizeCtl = Integer.MAX_VALUE; return; } nextTable = nextTab; + // 老表长 transferIndex = n; } + // 新表长 int nextn = nextTab.length; + // 创建一个hash值是-1的扩容节点 ForwardingNode fwd = new ForwardingNode(nextTab); + // 向前为真 boolean advance = true; + // 结束为假 boolean finishing = false; // to ensure sweep before committing nextTab + // for (int i = 0, bound = 0; ; ) { Node f; int fh; + // 如果需要向前 while (advance) { + // 下一个下标和边界 int nextIndex, nextBound; - if (--i >= bound || finishing) + // 当前线程到达处理边界就停下来 或者处理完了也停下来 + if (--i >= bound || finishing) { advance = false; + } + // 下一个要处理完的下标小于0也停下来 else if ((nextIndex = transferIndex) <= 0) { i = -1; advance = false; - } else if (U.compareAndSwapInt - (this, TRANSFERINDEX, nextIndex, - nextBound = (nextIndex > stride ? - nextIndex - stride : 0))) { + } + // CAS修改transferIndex。 进入该if的情况 + //1.第一次进入循环,分配每个线程处理的桶的区间 + //2. 不满足上面两个情况,说明当前线程任务已完成,但总扩容任务还没完成,再次分配扩容任务 + // 四个入参分别是 当前对象 拿到值需要的偏移量 老值 新值 + // 偏移TRANSFERINDEX拿到transferIndex在堆里面的值和传入的nextIndex比较是不是一样(没被人改过) 一样就替换并且返回true + // 简单理解为划分任务区域 + else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex, + nextBound = (nextIndex > stride ? nextIndex - stride : 0))) { + //更新桶边界以及当前桶下标 bound = nextBound; + // 注意,从i--以及这里的赋值,可以得知扩容是从高位到低位,即逆序处理,而HashMap中是顺序处理的 i = nextIndex - 1; + // 任务分配好了,跳出循环处理当前i advance = false; } } + // 笔者认为这里只有 i<0 的条件是有意义的,其余两个都无意义, + // 因为i能被赋值的最大值是(nextIndex - 1)=(transferIndex -1)<=(n -1 ),因为transferIndex初始值为n,而且transferIndex与i都是只减不增,不可能达到其他两个条件 + // i<0,只对应上方while循环中的 i=-1 ,不会对应第一个if中的 --i>=bound + // 因为bound = nextBound >=0 ,所以要满足if (--i >= bound),--i>=0必须成立,所以 i<0必不可能成立。而若不满足 1、2个if,就会如上文注释所说再次获取扩容任务 if (i < 0 || i >= n || i + n >= nextn) { int sc; + // 总扩容任务完成 if (finishing) { + // 置null, 保持nextTable只会在扩容时不为null的特性 nextTable = null; table = nextTab; + // 0.75n n是老表长 sizeCtl = (n << 1) - (n >>> 1); return; } + // 总扩容任务未完成,但当前线程已完成,CAS减少扩容线程数量 if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { - if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) + // 这里看似迷的操作,其实是一一对应回addCount / TryPresize中把sizeCtl置为负值时的操作: + // U.compareAndSwapInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2 + if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) { return; + } + //能到这个逻辑时,说明没有其他扩容线程了,即当前线程是最后的扩容线程 + //总扩容已完成,更新finishing,advance设为true是为了能进入上述的while循环,而且不会有影响,因为一定满足if(finnishing) ,会直接跳出循环 finishing = advance = true; + //稳如老狗 ,重新检查一遍全部桶 + //注意:这里不会受到当前线程的bound影响,执行代码顺序就是 + //1. while (advance) if (--i >= bound || finishing) ,借助这里完成i递减,然后便跳出循环 (妙,膜拜) + //2. 下方的if ((fh = f.hash) == MOVED) advance = true; + //3. 1、2循环,至 i<0, 进行本if (i < 0) {if (finishing) } ,至此结束。这里也只是对i<0起作用,跟上面说"另两个if无意义"并不冲突 i = n; // recheck before commit } - } else if ((f = tabAt(tab, i)) == null) + } + // 若当前桶为空,直接放一个fwd占位,表示这个插槽已转移完 + else if ((f = tabAt(tab, i)) == null) { advance = casTabAt(tab, i, null, fwd); - else if ((fh = f.hash) == MOVED) + } + // 当前桶还没处理,加锁进行处理 + else if ((fh = f.hash) == MOVED) { advance = true; // already processed - else { + } else { synchronized (f) { + // 再次确认当前 i 位置,确保没被其他线程修改 if (tabAt(tab, i) == f) { + // ln、hn,按笔者理解,其实就是lowNode 跟 highNode 缩写 Node ln, hn; if (fh >= 0) { + // 这里的逻辑跟HashMap的扩容一样,不了解的可以看我另一篇博文:https://blog.csdn.net/Unknownfuture/article/details/105181447 int runBit = fh & n; + //注意这里的实现 Node lastRun = f; for (Node p = f.next; p != null; p = p.next) { int b = p.hash & n; + //runBit跟lastRun 只会在 runBit发生变化时进行更改 + // 这样在runBit都是一样的情况下,只需要指向lastRun就可以结束了,而不需要再连接上lastRun后面那些runBit一样的结点 if (b != runBit) { runBit = b; lastRun = p; } } + //runBit为0,说明最后更新的runBit=0,同时lastRun也应该继续保留在当前位置 if (runBit == 0) { + //更新ln、hn,注意hn为null ln = lastRun; hn = null; } else { hn = lastRun; ln = null; } + //结束条件是 p != lastRun ,而不是p != null ,原因如上,节省了循环时间 for (Node p = f; p != lastRun; p = p.next) { int ph = p.hash; K pk = p.key; V pv = p.val; - if ((ph & n) == 0) + // 为0,留在本位置,否则应放到高位 + // 注:循环结束后,会分为两条链表(ln、hn分别为两条链表头结点) + // 一条为逆序链表 (顺序指的是按runBit分类,在原数组的位置) + // 另一条链表顺序不定,而不是有的资料所说的顺序/逆序链表 + // 而为逆序链表的那条,就是在循环前为null的那条,比如若hn=null, 那hn就是逆序链表 + // 原因可以自己画一画,或者看笔者的图 + + // 此外,注意这里的实现是复制新的结点,而不是像hashMap中那样直接另指针指向原结点 + // 笔者的理解:不修改原结点顺序,保证了在扩容期间,get操作也能正常使用且能返回正确结果 + if ((ph & n) == 0) { ln = new Node(ph, pk, pv, ln); - else + } else { hn = new Node(ph, pk, pv, hn); + } } + //ln放到原位置,hn放到扩容后的位置 +n ,不多解释 setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); + //fwd占位 setTabAt(tab, i, fwd); + //标记当前桶处理完了,可以往前推进 advance = true; - } else if (f instanceof TreeBin) { + } + // 红黑树类型,逻辑差不多,笔者也不是很懂红黑树,就不班门弄斧了 + else if (f instanceof TreeBin) { TreeBin t = (TreeBin) f; TreeNode lo = null, loTail = null; TreeNode hi = null, hiTail = null; int lc = 0, hc = 0; for (Node e = t.first; e != null; e = e.next) { int h = e.hash; - TreeNode p = new TreeNode - (h, e.key, e.val, null, null); + TreeNode p = new TreeNode(h, e.key, e.val, null, null); if ((h & n) == 0) { - if ((p.prev = loTail) == null) + if ((p.prev = loTail) == null) { lo = p; - else + } else { loTail.next = p; + } loTail = p; ++lc; } else { - if ((p.prev = hiTail) == null) + if ((p.prev = hiTail) == null) { hi = p; - else + } else { hiTail.next = p; + } hiTail = p; ++hc; } } - ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : - (hc != 0) ? new TreeBin(lo) : t; - hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : - (lc != 0) ? new TreeBin(hi) : t; + ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin(lo) : t; + hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin(hi) : t; setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); setTabAt(tab, i, fwd); @@ -2030,6 +2184,7 @@ else if ((fh = f.hash) == MOVED) */ @sun.misc.Contended static final class CounterCell { + volatile long value; CounterCell(long x) { @@ -2043,8 +2198,9 @@ final long sumCount() { long sum = baseCount; if (as != null) { for (int i = 0; i < as.length; ++i) { - if ((a = as[i]) != null) + if ((a = as[i]) != null) { sum += a.value; + } } } return sum; @@ -2068,23 +2224,21 @@ private final void fullAddCount(long x, boolean wasUncontended) { if ((a = as[(n - 1) & h]) == null) { if (cellsBusy == 0) { // Try to attach new Cell CounterCell r = new CounterCell(x); // Optimistic create - if (cellsBusy == 0 && - U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean created = false; try { // Recheck under lock CounterCell[] rs; int m, j; - if ((rs = counterCells) != null && - (m = rs.length) > 0 && - rs[j = (m - 1) & h] == null) { + if ((rs = counterCells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { rs[j] = r; created = true; } } finally { cellsBusy = 0; } - if (created) + if (created) { break; + } continue; // Slot is now non-empty } } @@ -2097,13 +2251,13 @@ else if (counterCells != as || n >= NCPU) collide = false; // At max size or stale else if (!collide) collide = true; - else if (cellsBusy == 0 && - U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + else if (cellsBusy == 0 && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { try { if (counterCells == as) {// Expand table unless stale CounterCell[] rs = new CounterCell[n << 1]; - for (int i = 0; i < n; ++i) + for (int i = 0; i < n; ++i) { rs[i] = as[i]; + } counterCells = rs; } } finally { @@ -2113,8 +2267,7 @@ else if (cellsBusy == 0 && continue; // Retry with expanded table } h = ThreadLocalRandom.advanceProbe(h); - } else if (cellsBusy == 0 && counterCells == as && - U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + } else if (cellsBusy == 0 && counterCells == as && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { boolean init = false; try { // Initialize table if (counterCells == as) { @@ -2143,20 +2296,20 @@ private final void treeifyBin(Node[] tab, int index) { Node b; int n, sc; if (tab != null) { - if ((n = tab.length) < MIN_TREEIFY_CAPACITY) + if ((n = tab.length) < MIN_TREEIFY_CAPACITY) { + // 不到64 先扩容 tryPresize(n << 1); - else if ((b = tabAt(tab, index)) != null && b.hash >= 0) { + } else if ((b = tabAt(tab, index)) != null && b.hash >= 0) { synchronized (b) { if (tabAt(tab, index) == b) { TreeNode hd = null, tl = null; for (Node e = b; e != null; e = e.next) { - TreeNode p = - new TreeNode(e.hash, e.key, e.val, - null, null); - if ((p.prev = tl) == null) + TreeNode p = new TreeNode(e.hash, e.key, e.val, null, null); + if ((p.prev = tl) == null) { hd = p; - else + } else { tl.next = p; + } tl = p; } setTabAt(tab, index, new TreeBin(hd)); @@ -2173,10 +2326,11 @@ static Node untreeify(Node b) { Node hd = null, tl = null; for (Node q = b; q != null; q = q.next) { Node p = new Node(q.hash, q.key, q.val, null); - if (tl == null) + if (tl == null) { hd = p; - else + } else { tl.next = p; + } tl = p; } return hd; @@ -2188,18 +2342,23 @@ static Node untreeify(Node b) { * Nodes for use in TreeBins */ static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; - TreeNode(int hash, K key, V val, Node next, - TreeNode parent) { + TreeNode(int hash, K key, V val, Node next, TreeNode parent) { super(hash, key, val, next); this.parent = parent; } + @Override Node find(int h, Object k) { return findTreeNode(h, k, null); } @@ -2216,24 +2375,24 @@ final TreeNode findTreeNode(int h, Object k, Class kc) { K pk; TreeNode q; TreeNode pl = p.left, pr = p.right; - if ((ph = p.hash) > h) + if ((ph = p.hash) > h) { p = pl; - else if (ph < h) + } else if (ph < h) { p = pr; - else if ((pk = p.key) == k || (pk != null && k.equals(pk))) + } else if ((pk = p.key) == k || (pk != null && k.equals(pk))) { return p; - else if (pl == null) + } else if (pl == null) { p = pr; - else if (pr == null) + } else if (pr == null) { p = pl; - else if ((kc != null || - (kc = comparableClassFor(k)) != null) && - (dir = compareComparables(kc, k, pk)) != 0) + } else if ((kc != null || (kc = comparableClassFor(k)) != null) && (dir = compareComparables(kc, k, + pk)) != 0) { p = (dir < 0) ? pl : pr; - else if ((q = pr.findTreeNode(h, k, kc)) != null) + } else if ((q = pr.findTreeNode(h, k, kc)) != null) { return q; - else + } else { p = pl; + } } while (p != null); } return null; @@ -2250,13 +2409,20 @@ else if ((q = pr.findTreeNode(h, k, kc)) != null) * not) to complete before tree restructuring operations. */ static final class TreeBin extends Node { + TreeNode root; + volatile TreeNode first; + volatile Thread waiter; + volatile int lockState; + // values for lockState static final int WRITER = 1; // set while holding write lock + static final int WAITER = 2; // set when waiting for write lock + static final int READER = 4; // increment value for setting read lock /** @@ -2268,11 +2434,10 @@ static final class TreeBin extends Node { */ static int tieBreakOrder(Object a, Object b) { int d; - if (a == null || b == null || - (d = a.getClass().getName(). - compareTo(b.getClass().getName())) == 0) - d = (System.identityHashCode(a) <= System.identityHashCode(b) ? - -1 : 1); + if (a == null || b == null || (d = a.getClass().getName(). + compareTo(b.getClass().getName())) == 0) { + d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 : 1); + } return d; } @@ -2297,21 +2462,23 @@ static int tieBreakOrder(Object a, Object b) { for (TreeNode p = r; ; ) { int dir, ph; K pk = p.key; - if ((ph = p.hash) > h) + if ((ph = p.hash) > h) { dir = -1; - else if (ph < h) + } else if (ph < h) { dir = 1; - else if ((kc == null && - (kc = comparableClassFor(k)) == null) || - (dir = compareComparables(kc, k, pk)) == 0) + } else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, + k, + pk)) == 0) { dir = tieBreakOrder(k, pk); + } TreeNode xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { x.parent = xp; - if (dir <= 0) + if (dir <= 0) { xp.left = x; - else + } else { xp.right = x; + } r = balanceInsertion(r, x); break; } @@ -2326,8 +2493,9 @@ else if ((kc == null && * Acquires write lock for tree restructuring. */ private final void lockRoot() { - if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER)) + if (!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER)) { contendedLock(); // offload to separate method + } } /** @@ -2364,26 +2532,23 @@ private final void contendedLock() { * using tree comparisons from root, but continues linear * search when lock not available. */ + @Override final Node find(int h, Object k) { if (k != null) { for (Node e = first; e != null; ) { int s; K ek; if (((s = lockState) & (WAITER | WRITER)) != 0) { - if (e.hash == h && - ((ek = e.key) == k || (ek != null && k.equals(ek)))) + if (e.hash == h && ((ek = e.key) == k || (ek != null && k.equals(ek)))) return e; e = e.next; - } else if (U.compareAndSwapInt(this, LOCKSTATE, s, - s + READER)) { + } else if (U.compareAndSwapInt(this, LOCKSTATE, s, s + READER)) { TreeNode r, p; try { - p = ((r = root) == null ? null : - r.findTreeNode(h, k, null)); + p = ((r = root) == null ? null : r.findTreeNode(h, k, null)); } finally { Thread w; - if (U.getAndAddInt(this, LOCKSTATE, -READER) == - (READER | WAITER) && (w = waiter) != null) + if (U.getAndAddInt(this, LOCKSTATE, -READER) == (READER | WAITER) && (w = waiter) != null) LockSupport.unpark(w); } return p; @@ -2400,47 +2565,65 @@ final Node find(int h, Object k) { */ final TreeNode putTreeVal(int h, K k, V v) { Class kc = null; + // 是否找到了 boolean searched = false; + // p是parent的意思 for (TreeNode p = root; ; ) { - int dir, ph; + // direction 方向 -1往左 1往右 + int dir; + // 父节点hash值 + int ph; + // 父节点的key K pk; + // 如果是空树则直接放 if (p == null) { first = root = new TreeNode(h, k, v, null, null); break; - } else if ((ph = p.hash) > h) + } else if ((ph = p.hash) > h) {// 往左走 dir = -1; - else if (ph < h) + } else if (ph < h) {// 往右走 dir = 1; - else if ((pk = p.key) == k || (pk != null && k.equals(pk))) + } else if ((pk = p.key) == k || (pk != null && k.equals(pk))) {// 插入的节点已经存在了 return p; - else if ((kc == null && - (kc = comparableClassFor(k)) == null) || - (dir = compareComparables(kc, k, pk)) == 0) { + } + // key的class不存在且key不可比较 或者比较结果是相等的 + else if ((kc == null && (kc = comparableClassFor(k)) == null) + || (dir = compareComparables(kc, k, pk)) == 0) { + // 如果还没找到就认为是找到了 if (!searched) { - TreeNode q, ch; + // 查询结果 + TreeNode q; + // 孩子节点 + TreeNode ch; searched = true; - if (((ch = p.left) != null && - (q = ch.findTreeNode(h, k, kc)) != null) || - ((ch = p.right) != null && - (q = ch.findTreeNode(h, k, kc)) != null)) + + if (((ch = p.left) != null && (q = ch.findTreeNode(h, k, kc)) != null) + || ((ch = p.right) != null && (q = ch.findTreeNode(h, k, kc)) != null)) { + // 找到了相同key的节点就返回 return q; + } } + // 解决hash值相同但是key不可比较的问题 dir = tieBreakOrder(k, pk); } + // 当前节点的父节点xp TreeNode xp = p; if ((p = (dir <= 0) ? p.left : p.right) == null) { TreeNode x, f = first; first = x = new TreeNode(h, k, v, f, xp); - if (f != null) + if (f != null) { f.prev = x; - if (dir <= 0) + } + if (dir <= 0) { xp.left = x; - else + } else { xp.right = x; - if (!xp.red) + } + if (!xp.red) { + // 新节点初始颜色一定是红色的 x.red = true; - else { + } else { lockRoot(); try { root = balanceInsertion(root, x); @@ -2564,8 +2747,7 @@ else if (p == pp.right) /* ------------------------------------------------------------ */ // Red-black tree methods, all adapted from CLR - static TreeNode rotateLeft(TreeNode root, - TreeNode p) { + static TreeNode rotateLeft(TreeNode root, TreeNode p) { TreeNode r, pp, rl; if (p != null && (r = p.right) != null) { if ((rl = p.right = r.left) != null) @@ -2582,8 +2764,7 @@ else if (pp.left == p) return root; } - static TreeNode rotateRight(TreeNode root, - TreeNode p) { + static TreeNode rotateRight(TreeNode root, TreeNode p) { TreeNode l, pp, lr; if (p != null && (l = p.left) != null) { if ((lr = p.left = l.right) != null) @@ -2600,8 +2781,7 @@ else if (pp.right == p) return root; } - static TreeNode balanceInsertion(TreeNode root, - TreeNode x) { + static TreeNode balanceInsertion(TreeNode root, TreeNode x) { x.red = true; for (TreeNode xp, xpp, xppl, xppr; ; ) { if ((xp = x.parent) == null) { @@ -2651,8 +2831,7 @@ static TreeNode balanceInsertion(TreeNode root, } } - static TreeNode balanceDeletion(TreeNode root, - TreeNode x) { + static TreeNode balanceDeletion(TreeNode root, TreeNode x) { for (TreeNode xp, xpl, xpr; ; ) { if (x == null || x == root) return root; @@ -2673,8 +2852,7 @@ else if ((xp = x.parent) == null) { x = xp; else { TreeNode sl = xpr.left, sr = xpr.right; - if ((sr == null || !sr.red) && - (sl == null || !sl.red)) { + if ((sr == null || !sr.red) && (sl == null || !sl.red)) { xpr.red = true; x = xp; } else { @@ -2683,8 +2861,7 @@ else if ((xp = x.parent) == null) { sl.red = false; xpr.red = true; root = rotateRight(root, xpr); - xpr = (xp = x.parent) == null ? - null : xp.right; + xpr = (xp = x.parent) == null ? null : xp.right; } if (xpr != null) { xpr.red = (xp == null) ? false : xp.red; @@ -2709,8 +2886,7 @@ else if ((xp = x.parent) == null) { x = xp; else { TreeNode sl = xpl.left, sr = xpl.right; - if ((sl == null || !sl.red) && - (sr == null || !sr.red)) { + if ((sl == null || !sl.red) && (sr == null || !sr.red)) { xpl.red = true; x = xp; } else { @@ -2719,8 +2895,7 @@ else if ((xp = x.parent) == null) { sr.red = false; xpl.red = true; root = rotateLeft(root, xpl); - xpl = (xp = x.parent) == null ? - null : xp.left; + xpl = (xp = x.parent) == null ? null : xp.left; } if (xpl != null) { xpl.red = (xp == null) ? false : xp.red; @@ -2742,8 +2917,7 @@ else if ((xp = x.parent) == null) { * Recursive invariant check */ static boolean checkInvariants(TreeNode t) { - TreeNode tp = t.parent, tl = t.left, tr = t.right, - tb = t.prev, tn = (TreeNode) t.next; + TreeNode tp = t.parent, tl = t.left, tr = t.right, tb = t.prev, tn = (TreeNode) t.next; if (tb != null && tb.next != t) return false; if (tn != null && tn.prev != t) @@ -2764,14 +2938,14 @@ static boolean checkInvariants(TreeNode t) { } private static final sun.misc.Unsafe U; + private static final long LOCKSTATE; static { try { U = sun.misc.Unsafe.getUnsafe(); Class k = TreeBin.class; - LOCKSTATE = U.objectFieldOffset - (k.getDeclaredField("lockState")); + LOCKSTATE = U.objectFieldOffset(k.getDeclaredField("lockState")); } catch (Exception e) { throw new Error(e); } @@ -2786,9 +2960,13 @@ static boolean checkInvariants(TreeNode t) { * proceeding with current table. */ static final class TableStack { + int length; + int index; + Node[] tab; + TableStack next; } @@ -2814,12 +2992,19 @@ static final class TableStack { * for a table read. */ static class Traverser { + Node[] tab; // current table; updated if resized + Node next; // the next entry to use + TableStack stack, spare; // to save/restore on ForwardingNodes + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + final int baseSize; // initial table size Traverser(Node[] tab, int size, int index, int limit) { @@ -2842,8 +3027,7 @@ final Node advance() { int i, n; // must use locals in checks if (e != null) return next = e; - if (baseIndex >= baseLimit || (t = tab) == null || - (n = t.length) <= (i = index) || i < 0) + if (baseIndex >= baseLimit || (t = tab) == null || (n = t.length) <= (i = index) || i < 0) return next = null; if ((e = tabAt(t, i)) != null && e.hash < 0) { if (e instanceof ForwardingNode) { @@ -2907,11 +3091,12 @@ private void recoverState(int n) { * Traverser to support iterator.remove. */ static class BaseIterator extends Traverser { + final ConcurrentHashMap map; + Node lastReturned; - BaseIterator(Node[] tab, int size, int index, int limit, - ConcurrentHashMap map) { + BaseIterator(Node[] tab, int size, int index, int limit, ConcurrentHashMap map) { super(tab, size, index, limit); this.map = map; advance(); @@ -2934,10 +3119,9 @@ public final void remove() { } } - static final class KeyIterator extends BaseIterator - implements Iterator, Enumeration { - KeyIterator(Node[] tab, int index, int size, int limit, - ConcurrentHashMap map) { + static final class KeyIterator extends BaseIterator implements Iterator, Enumeration { + + KeyIterator(Node[] tab, int index, int size, int limit, ConcurrentHashMap map) { super(tab, index, size, limit, map); } @@ -2956,10 +3140,9 @@ public final K nextElement() { } } - static final class ValueIterator extends BaseIterator - implements Iterator, Enumeration { - ValueIterator(Node[] tab, int index, int size, int limit, - ConcurrentHashMap map) { + static final class ValueIterator extends BaseIterator implements Iterator, Enumeration { + + ValueIterator(Node[] tab, int index, int size, int limit, ConcurrentHashMap map) { super(tab, index, size, limit, map); } @@ -2978,10 +3161,9 @@ public final V nextElement() { } } - static final class EntryIterator extends BaseIterator - implements Iterator> { - EntryIterator(Node[] tab, int index, int size, int limit, - ConcurrentHashMap map) { + static final class EntryIterator extends BaseIterator implements Iterator> { + + EntryIterator(Node[] tab, int index, int size, int limit, ConcurrentHashMap map) { super(tab, index, size, limit, map); } @@ -3001,8 +3183,11 @@ public final Map.Entry next() { * Exported Entry for EntryIterator */ static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMap map; MapEntry(K key, V val, ConcurrentHashMap map) { @@ -3030,11 +3215,8 @@ public String toString() { public boolean equals(Object o) { Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry) o).getKey()) != null && - (v = e.getValue()) != null && - (k == key || k.equals(key)) && - (v == val || v.equals(val))); + return ((o instanceof Map.Entry) && (k = (e = (Map.Entry) o).getKey()) != null && (v = e.getValue()) != null && (k == key || k.equals( + key)) && (v == val || v.equals(val))); } /** @@ -3046,7 +3228,8 @@ public boolean equals(Object o) { * re-establish). We do not and cannot guarantee more. */ public V setValue(V value) { - if (value == null) throw new NullPointerException(); + if (value == null) + throw new NullPointerException(); V v = val; val = value; map.put(key, value); @@ -3054,31 +3237,33 @@ public V setValue(V value) { } } - static final class KeySpliterator extends Traverser - implements Spliterator { + static final class KeySpliterator extends Traverser implements Spliterator { + long est; // size estimate - KeySpliterator(Node[] tab, int size, int index, int limit, - long est) { + KeySpliterator(Node[] tab, int size, int index, int limit, long est) { super(tab, size, index, limit); this.est = est; } public Spliterator trySplit() { int i, f, h; - return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : - new KeySpliterator(tab, baseSize, baseLimit = h, - f, est >>>= 1); + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new KeySpliterator(tab, baseSize, + baseLimit = h, + f, + est >>>= 1); } public void forEachRemaining(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); for (Node p; (p = advance()) != null; ) action.accept(p.key); } public boolean tryAdvance(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node p; if ((p = advance()) == null) return false; @@ -3091,36 +3276,38 @@ public long estimateSize() { } public int characteristics() { - return Spliterator.DISTINCT | Spliterator.CONCURRENT | - Spliterator.NONNULL; + return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; } } - static final class ValueSpliterator extends Traverser - implements Spliterator { + static final class ValueSpliterator extends Traverser implements Spliterator { + long est; // size estimate - ValueSpliterator(Node[] tab, int size, int index, int limit, - long est) { + ValueSpliterator(Node[] tab, int size, int index, int limit, long est) { super(tab, size, index, limit); this.est = est; } public Spliterator trySplit() { int i, f, h; - return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : - new ValueSpliterator(tab, baseSize, baseLimit = h, - f, est >>>= 1); + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new ValueSpliterator(tab, + baseSize, + baseLimit = h, + f, + est >>>= 1); } public void forEachRemaining(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); for (Node p; (p = advance()) != null; ) action.accept(p.val); } public boolean tryAdvance(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node p; if ((p = advance()) == null) return false; @@ -3137,13 +3324,13 @@ public int characteristics() { } } - static final class EntrySpliterator extends Traverser - implements Spliterator> { + static final class EntrySpliterator extends Traverser implements Spliterator> { + final ConcurrentHashMap map; // To export MapEntry + long est; // size estimate - EntrySpliterator(Node[] tab, int size, int index, int limit, - long est, ConcurrentHashMap map) { + EntrySpliterator(Node[] tab, int size, int index, int limit, long est, ConcurrentHashMap map) { super(tab, size, index, limit); this.map = map; this.est = est; @@ -3151,19 +3338,24 @@ static final class EntrySpliterator extends Traverser public Spliterator> trySplit() { int i, f, h; - return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : - new EntrySpliterator(tab, baseSize, baseLimit = h, - f, est >>>= 1, map); + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null : new EntrySpliterator(tab, + baseSize, + baseLimit = h, + f, + est >>>= 1, + map); } public void forEachRemaining(Consumer> action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); for (Node p; (p = advance()) != null; ) action.accept(new MapEntry(p.key, p.val, map)); } public boolean tryAdvance(Consumer> action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node p; if ((p = advance()) == null) return false; @@ -3176,8 +3368,7 @@ public long estimateSize() { } public int characteristics() { - return Spliterator.DISTINCT | Spliterator.CONCURRENT | - Spliterator.NONNULL; + return Spliterator.DISTINCT | Spliterator.CONCURRENT | Spliterator.NONNULL; } } @@ -3205,14 +3396,13 @@ final int batchFor(long b) { * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param action the action + * * @since 1.8 */ - public void forEach(long parallelismThreshold, - BiConsumer action) { - if (action == null) throw new NullPointerException(); - new ForEachMappingTask - (null, batchFor(parallelismThreshold), 0, 0, table, - action).invoke(); + public void forEach(long parallelismThreshold, BiConsumer action) { + if (action == null) + throw new NullPointerException(); + new ForEachMappingTask(null, batchFor(parallelismThreshold), 0, 0, table, action).invoke(); } /** @@ -3226,16 +3416,15 @@ public void forEach(long parallelismThreshold, * which case the action is not applied) * @param action the action * @param the return type of the transformer + * * @since 1.8 */ - public void forEach(long parallelismThreshold, - BiFunction transformer, - Consumer action) { + public void forEach(long parallelismThreshold, BiFunction transformer, + Consumer action) { if (transformer == null || action == null) throw new NullPointerException(); - new ForEachTransformedMappingTask - (null, batchFor(parallelismThreshold), 0, 0, table, - transformer, action).invoke(); + new ForEachTransformedMappingTask(null, batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); } /** @@ -3250,16 +3439,16 @@ public void forEach(long parallelismThreshold, * @param searchFunction a function returning a non-null * result on success, else null * @param the return type of the search function + * * @return a non-null result from applying the given search * function on each (key, value), or null if none * @since 1.8 */ - public U search(long parallelismThreshold, - BiFunction searchFunction) { - if (searchFunction == null) throw new NullPointerException(); - return new SearchMappingsTask - (null, batchFor(parallelismThreshold), 0, 0, table, - searchFunction, new AtomicReference()).invoke(); + public U search(long parallelismThreshold, BiFunction searchFunction) { + if (searchFunction == null) + throw new NullPointerException(); + return new SearchMappingsTask(null, batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); } /** @@ -3274,18 +3463,17 @@ public U search(long parallelismThreshold, * which case it is not combined) * @param reducer a commutative associative combining function * @param the return type of the transformer + * * @return the result of accumulating the given transformation * of all (key, value) pairs * @since 1.8 */ - public U reduce(long parallelismThreshold, - BiFunction transformer, - BiFunction reducer) { + public U reduce(long parallelismThreshold, BiFunction transformer, + BiFunction reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceMappingsTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, reducer).invoke(); + return new MapReduceMappingsTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); } /** @@ -3299,19 +3487,17 @@ public U reduce(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all (key, value) pairs * @since 1.8 */ - public double reduceToDouble(long parallelismThreshold, - ToDoubleBiFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + public double reduceToDouble(long parallelismThreshold, ToDoubleBiFunction transformer, + double basis, DoubleBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceMappingsToDoubleTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceMappingsToDoubleTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3325,19 +3511,17 @@ public double reduceToDouble(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all (key, value) pairs * @since 1.8 */ - public long reduceToLong(long parallelismThreshold, - ToLongBiFunction transformer, - long basis, - LongBinaryOperator reducer) { + public long reduceToLong(long parallelismThreshold, ToLongBiFunction transformer, long basis, + LongBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceMappingsToLongTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceMappingsToLongTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3351,19 +3535,17 @@ public long reduceToLong(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all (key, value) pairs * @since 1.8 */ - public int reduceToInt(long parallelismThreshold, - ToIntBiFunction transformer, - int basis, - IntBinaryOperator reducer) { + public int reduceToInt(long parallelismThreshold, ToIntBiFunction transformer, int basis, + IntBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceMappingsToIntTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceMappingsToIntTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3372,14 +3554,13 @@ public int reduceToInt(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param action the action + * * @since 1.8 */ - public void forEachKey(long parallelismThreshold, - Consumer action) { - if (action == null) throw new NullPointerException(); - new ForEachKeyTask - (null, batchFor(parallelismThreshold), 0, 0, table, - action).invoke(); + public void forEachKey(long parallelismThreshold, Consumer action) { + if (action == null) + throw new NullPointerException(); + new ForEachKeyTask(null, batchFor(parallelismThreshold), 0, 0, table, action).invoke(); } /** @@ -3393,16 +3574,15 @@ public void forEachKey(long parallelismThreshold, * which case the action is not applied) * @param action the action * @param the return type of the transformer + * * @since 1.8 */ - public void forEachKey(long parallelismThreshold, - Function transformer, - Consumer action) { + public void forEachKey(long parallelismThreshold, Function transformer, + Consumer action) { if (transformer == null || action == null) throw new NullPointerException(); - new ForEachTransformedKeyTask - (null, batchFor(parallelismThreshold), 0, 0, table, - transformer, action).invoke(); + new ForEachTransformedKeyTask(null, batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); } /** @@ -3417,16 +3597,16 @@ public void forEachKey(long parallelismThreshold, * @param searchFunction a function returning a non-null * result on success, else null * @param the return type of the search function + * * @return a non-null result from applying the given search * function on each key, or null if none * @since 1.8 */ - public U searchKeys(long parallelismThreshold, - Function searchFunction) { - if (searchFunction == null) throw new NullPointerException(); - return new SearchKeysTask - (null, batchFor(parallelismThreshold), 0, 0, table, - searchFunction, new AtomicReference()).invoke(); + public U searchKeys(long parallelismThreshold, Function searchFunction) { + if (searchFunction == null) + throw new NullPointerException(); + return new SearchKeysTask(null, batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); } /** @@ -3436,16 +3616,15 @@ public U searchKeys(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param reducer a commutative associative combining function + * * @return the result of accumulating all keys using the given * reducer to combine values, or null if none * @since 1.8 */ - public K reduceKeys(long parallelismThreshold, - BiFunction reducer) { - if (reducer == null) throw new NullPointerException(); - return new ReduceKeysTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, reducer).invoke(); + public K reduceKeys(long parallelismThreshold, BiFunction reducer) { + if (reducer == null) + throw new NullPointerException(); + return new ReduceKeysTask(null, batchFor(parallelismThreshold), 0, 0, table, null, reducer).invoke(); } /** @@ -3460,18 +3639,17 @@ public K reduceKeys(long parallelismThreshold, * which case it is not combined) * @param reducer a commutative associative combining function * @param the return type of the transformer + * * @return the result of accumulating the given transformation * of all keys * @since 1.8 */ - public U reduceKeys(long parallelismThreshold, - Function transformer, - BiFunction reducer) { + public U reduceKeys(long parallelismThreshold, Function transformer, + BiFunction reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceKeysTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, reducer).invoke(); + return new MapReduceKeysTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); } /** @@ -3485,19 +3663,17 @@ public U reduceKeys(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all keys * @since 1.8 */ - public double reduceKeysToDouble(long parallelismThreshold, - ToDoubleFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + public double reduceKeysToDouble(long parallelismThreshold, ToDoubleFunction transformer, double basis, + DoubleBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceKeysToDoubleTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceKeysToDoubleTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } /** @@ -3511,19 +3687,17 @@ public double reduceKeysToDouble(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all keys * @since 1.8 */ - public long reduceKeysToLong(long parallelismThreshold, - ToLongFunction transformer, - long basis, - LongBinaryOperator reducer) { + public long reduceKeysToLong(long parallelismThreshold, ToLongFunction transformer, long basis, + LongBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceKeysToLongTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceKeysToLongTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } /** @@ -3537,19 +3711,17 @@ public long reduceKeysToLong(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all keys * @since 1.8 */ - public int reduceKeysToInt(long parallelismThreshold, - ToIntFunction transformer, - int basis, - IntBinaryOperator reducer) { + public int reduceKeysToInt(long parallelismThreshold, ToIntFunction transformer, int basis, + IntBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceKeysToIntTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceKeysToIntTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } /** @@ -3558,15 +3730,13 @@ public int reduceKeysToInt(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param action the action + * * @since 1.8 */ - public void forEachValue(long parallelismThreshold, - Consumer action) { + public void forEachValue(long parallelismThreshold, Consumer action) { if (action == null) throw new NullPointerException(); - new ForEachValueTask - (null, batchFor(parallelismThreshold), 0, 0, table, - action).invoke(); + new ForEachValueTask(null, batchFor(parallelismThreshold), 0, 0, table, action).invoke(); } /** @@ -3580,16 +3750,15 @@ public void forEachValue(long parallelismThreshold, * which case the action is not applied) * @param action the action * @param the return type of the transformer + * * @since 1.8 */ - public void forEachValue(long parallelismThreshold, - Function transformer, - Consumer action) { + public void forEachValue(long parallelismThreshold, Function transformer, + Consumer action) { if (transformer == null || action == null) throw new NullPointerException(); - new ForEachTransformedValueTask - (null, batchFor(parallelismThreshold), 0, 0, table, - transformer, action).invoke(); + new ForEachTransformedValueTask(null, batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); } /** @@ -3604,16 +3773,16 @@ public void forEachValue(long parallelismThreshold, * @param searchFunction a function returning a non-null * result on success, else null * @param the return type of the search function + * * @return a non-null result from applying the given search * function on each value, or null if none * @since 1.8 */ - public U searchValues(long parallelismThreshold, - Function searchFunction) { - if (searchFunction == null) throw new NullPointerException(); - return new SearchValuesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - searchFunction, new AtomicReference()).invoke(); + public U searchValues(long parallelismThreshold, Function searchFunction) { + if (searchFunction == null) + throw new NullPointerException(); + return new SearchValuesTask(null, batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); } /** @@ -3623,15 +3792,14 @@ public U searchValues(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param reducer a commutative associative combining function + * * @return the result of accumulating all values * @since 1.8 */ - public V reduceValues(long parallelismThreshold, - BiFunction reducer) { - if (reducer == null) throw new NullPointerException(); - return new ReduceValuesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, reducer).invoke(); + public V reduceValues(long parallelismThreshold, BiFunction reducer) { + if (reducer == null) + throw new NullPointerException(); + return new ReduceValuesTask(null, batchFor(parallelismThreshold), 0, 0, table, null, reducer).invoke(); } /** @@ -3646,18 +3814,17 @@ public V reduceValues(long parallelismThreshold, * which case it is not combined) * @param reducer a commutative associative combining function * @param the return type of the transformer + * * @return the result of accumulating the given transformation * of all values * @since 1.8 */ - public U reduceValues(long parallelismThreshold, - Function transformer, - BiFunction reducer) { + public U reduceValues(long parallelismThreshold, Function transformer, + BiFunction reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceValuesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, reducer).invoke(); + return new MapReduceValuesTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); } /** @@ -3671,19 +3838,17 @@ public U reduceValues(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all values * @since 1.8 */ - public double reduceValuesToDouble(long parallelismThreshold, - ToDoubleFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + public double reduceValuesToDouble(long parallelismThreshold, ToDoubleFunction transformer, double basis, + DoubleBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceValuesToDoubleTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceValuesToDoubleTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3697,19 +3862,17 @@ public double reduceValuesToDouble(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all values * @since 1.8 */ - public long reduceValuesToLong(long parallelismThreshold, - ToLongFunction transformer, - long basis, - LongBinaryOperator reducer) { + public long reduceValuesToLong(long parallelismThreshold, ToLongFunction transformer, long basis, + LongBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceValuesToLongTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceValuesToLongTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } /** @@ -3723,19 +3886,17 @@ public long reduceValuesToLong(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all values * @since 1.8 */ - public int reduceValuesToInt(long parallelismThreshold, - ToIntFunction transformer, - int basis, - IntBinaryOperator reducer) { + public int reduceValuesToInt(long parallelismThreshold, ToIntFunction transformer, int basis, + IntBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceValuesToIntTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceValuesToIntTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } /** @@ -3744,13 +3905,13 @@ public int reduceValuesToInt(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param action the action + * * @since 1.8 */ - public void forEachEntry(long parallelismThreshold, - Consumer> action) { - if (action == null) throw new NullPointerException(); - new ForEachEntryTask(null, batchFor(parallelismThreshold), 0, 0, table, - action).invoke(); + public void forEachEntry(long parallelismThreshold, Consumer> action) { + if (action == null) + throw new NullPointerException(); + new ForEachEntryTask(null, batchFor(parallelismThreshold), 0, 0, table, action).invoke(); } /** @@ -3764,16 +3925,15 @@ public void forEachEntry(long parallelismThreshold, * which case the action is not applied) * @param action the action * @param the return type of the transformer + * * @since 1.8 */ - public void forEachEntry(long parallelismThreshold, - Function, ? extends U> transformer, - Consumer action) { + public void forEachEntry(long parallelismThreshold, Function, ? extends U> transformer, + Consumer action) { if (transformer == null || action == null) throw new NullPointerException(); - new ForEachTransformedEntryTask - (null, batchFor(parallelismThreshold), 0, 0, table, - transformer, action).invoke(); + new ForEachTransformedEntryTask(null, batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); } /** @@ -3788,16 +3948,16 @@ public void forEachEntry(long parallelismThreshold, * @param searchFunction a function returning a non-null * result on success, else null * @param the return type of the search function + * * @return a non-null result from applying the given search * function on each entry, or null if none * @since 1.8 */ - public U searchEntries(long parallelismThreshold, - Function, ? extends U> searchFunction) { - if (searchFunction == null) throw new NullPointerException(); - return new SearchEntriesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - searchFunction, new AtomicReference()).invoke(); + public U searchEntries(long parallelismThreshold, Function, ? extends U> searchFunction) { + if (searchFunction == null) + throw new NullPointerException(); + return new SearchEntriesTask(null, batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); } /** @@ -3807,15 +3967,15 @@ public U searchEntries(long parallelismThreshold, * @param parallelismThreshold the (estimated) number of elements * needed for this operation to be executed in parallel * @param reducer a commutative associative combining function + * * @return the result of accumulating all entries * @since 1.8 */ public Map.Entry reduceEntries(long parallelismThreshold, - BiFunction, Map.Entry, ? extends Map.Entry> reducer) { - if (reducer == null) throw new NullPointerException(); - return new ReduceEntriesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, reducer).invoke(); + BiFunction, Map.Entry, ? extends Map.Entry> reducer) { + if (reducer == null) + throw new NullPointerException(); + return new ReduceEntriesTask(null, batchFor(parallelismThreshold), 0, 0, table, null, reducer).invoke(); } /** @@ -3830,18 +3990,17 @@ public Map.Entry reduceEntries(long parallelismThreshold, * which case it is not combined) * @param reducer a commutative associative combining function * @param the return type of the transformer + * * @return the result of accumulating the given transformation * of all entries * @since 1.8 */ - public U reduceEntries(long parallelismThreshold, - Function, ? extends U> transformer, - BiFunction reducer) { + public U reduceEntries(long parallelismThreshold, Function, ? extends U> transformer, + BiFunction reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceEntriesTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, reducer).invoke(); + return new MapReduceEntriesTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); } /** @@ -3855,19 +4014,17 @@ public U reduceEntries(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all entries * @since 1.8 */ - public double reduceEntriesToDouble(long parallelismThreshold, - ToDoubleFunction> transformer, - double basis, - DoubleBinaryOperator reducer) { + public double reduceEntriesToDouble(long parallelismThreshold, ToDoubleFunction> transformer, + double basis, DoubleBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceEntriesToDoubleTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceEntriesToDoubleTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3881,19 +4038,17 @@ public double reduceEntriesToDouble(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all entries * @since 1.8 */ - public long reduceEntriesToLong(long parallelismThreshold, - ToLongFunction> transformer, - long basis, - LongBinaryOperator reducer) { + public long reduceEntriesToLong(long parallelismThreshold, ToLongFunction> transformer, long basis, + LongBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceEntriesToLongTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceEntriesToLongTask(null, batchFor(parallelismThreshold), 0, 0, table, null, + transformer, basis, reducer).invoke(); } /** @@ -3907,19 +4062,17 @@ public long reduceEntriesToLong(long parallelismThreshold, * for an element * @param basis the identity (initial default value) for the reduction * @param reducer a commutative associative combining function + * * @return the result of accumulating the given transformation * of all entries * @since 1.8 */ - public int reduceEntriesToInt(long parallelismThreshold, - ToIntFunction> transformer, - int basis, - IntBinaryOperator reducer) { + public int reduceEntriesToInt(long parallelismThreshold, ToIntFunction> transformer, int basis, + IntBinaryOperator reducer) { if (transformer == null || reducer == null) throw new NullPointerException(); - return new MapReduceEntriesToIntTask - (null, batchFor(parallelismThreshold), 0, 0, table, - null, transformer, basis, reducer).invoke(); + return new MapReduceEntriesToIntTask(null, batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); } @@ -3928,9 +4081,10 @@ public int reduceEntriesToInt(long parallelismThreshold, /** * Base class for views. */ - abstract static class CollectionView - implements Collection, java.io.Serializable { + abstract static class CollectionView implements Collection, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + final ConcurrentHashMap map; CollectionView(ConcurrentHashMap map) { @@ -4009,9 +4163,7 @@ public final T[] toArray(T[] a) { if (sz > MAX_ARRAY_SIZE) throw new OutOfMemoryError(oomeMsg); int m = (int) sz; - T[] r = (a.length >= m) ? a : - (T[]) java.lang.reflect.Array - .newInstance(a.getClass().getComponentType(), m); + T[] r = (a.length >= m) ? a : (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), m); int n = r.length; int i = 0; for (E e : this) { @@ -4071,7 +4223,8 @@ public final boolean containsAll(Collection c) { } public final boolean removeAll(Collection c) { - if (c == null) throw new NullPointerException(); + if (c == null) + throw new NullPointerException(); boolean modified = false; for (Iterator it = iterator(); it.hasNext(); ) { if (c.contains(it.next())) { @@ -4083,7 +4236,8 @@ public final boolean removeAll(Collection c) { } public final boolean retainAll(Collection c) { - if (c == null) throw new NullPointerException(); + if (c == null) + throw new NullPointerException(); boolean modified = false; for (Iterator it = iterator(); it.hasNext(); ) { if (!c.contains(it.next())) { @@ -4107,9 +4261,10 @@ public final boolean retainAll(Collection c) { * * @since 1.8 */ - public static class KeySetView extends CollectionView - implements Set, java.io.Serializable { + public static class KeySetView extends CollectionView implements Set, java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; KeySetView(ConcurrentHashMap map, V value) { // non-public @@ -4143,6 +4298,7 @@ public boolean contains(Object o) { * nothing if the key is not in the map. * * @param o the key to be removed from the backing map + * * @return {@code true} if the backing map contained the specified key * @throws NullPointerException if the specified key is null */ @@ -4165,6 +4321,7 @@ public Iterator iterator() { * the default mapped value in the backing map, if defined. * * @param e key to be added + * * @return {@code true} if this set changed as a result of the call * @throws NullPointerException if the specified key is null * @throws UnsupportedOperationException if no default mapped value @@ -4182,6 +4339,7 @@ public boolean add(K e) { * as if by calling {@link #add} on each one. * * @param c the elements to be inserted into this set + * * @return {@code true} if this set changed as a result of the call * @throws NullPointerException if the collection or any of its * elements are {@code null} @@ -4209,9 +4367,7 @@ public int hashCode() { public boolean equals(Object o) { Set c; - return ((o instanceof Set) && - ((c = (Set) o) == this || - (containsAll(c) && c.containsAll(this)))); + return ((o instanceof Set) && ((c = (Set) o) == this || (containsAll(c) && c.containsAll(this)))); } public Spliterator spliterator() { @@ -4223,7 +4379,8 @@ public Spliterator spliterator() { } public void forEach(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node[] t; if ((t = map.table) != null) { Traverser it = new Traverser(t, t.length, 0, t.length); @@ -4238,8 +4395,8 @@ public void forEach(Consumer action) { * values, in which additions are disabled. This class cannot be * directly instantiated. See {@link #values()}. */ - static final class ValuesView extends CollectionView - implements Collection, java.io.Serializable { + static final class ValuesView extends CollectionView implements Collection, java.io.Serializable { + private static final long serialVersionUID = 2249069246763182397L; ValuesView(ConcurrentHashMap map) { @@ -4286,7 +4443,8 @@ public Spliterator spliterator() { } public void forEach(Consumer action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node[] t; if ((t = map.table) != null) { Traverser it = new Traverser(t, t.length, 0, t.length); @@ -4301,8 +4459,8 @@ public void forEach(Consumer action) { * entries. This class cannot be directly instantiated. See * {@link #entrySet()}. */ - static final class EntrySetView extends CollectionView> - implements Set>, java.io.Serializable { + static final class EntrySetView extends CollectionView> implements Set>, java.io.Serializable { + private static final long serialVersionUID = 2249069246763182397L; EntrySetView(ConcurrentHashMap map) { @@ -4312,20 +4470,15 @@ static final class EntrySetView extends CollectionView e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry) o).getKey()) != null && - (r = map.get(k)) != null && - (v = e.getValue()) != null && - (v == r || v.equals(r))); + return ((o instanceof Map.Entry) && (k = (e = (Map.Entry) o).getKey()) != null && (r = map.get( + k)) != null && (v = e.getValue()) != null && (v == r || v.equals(r))); } public boolean remove(Object o) { Object k, v; Map.Entry e; - return ((o instanceof Map.Entry) && - (k = (e = (Map.Entry) o).getKey()) != null && - (v = e.getValue()) != null && - map.remove(k, v)); + return ((o instanceof Map.Entry) && (k = (e = (Map.Entry) o).getKey()) != null && (v = e.getValue()) != null && map.remove( + k, v)); } /** @@ -4365,9 +4518,7 @@ public final int hashCode() { public final boolean equals(Object o) { Set c; - return ((o instanceof Set) && - ((c = (Set) o) == this || - (containsAll(c) && c.containsAll(this)))); + return ((o instanceof Set) && ((c = (Set) o) == this || (containsAll(c) && c.containsAll(this)))); } public Spliterator> spliterator() { @@ -4379,7 +4530,8 @@ public Spliterator> spliterator() { } public void forEach(Consumer> action) { - if (action == null) throw new NullPointerException(); + if (action == null) + throw new NullPointerException(); Node[] t; if ((t = map.table) != null) { Traverser it = new Traverser(t, t.length, 0, t.length); @@ -4398,13 +4550,21 @@ public void forEach(Consumer> action) { */ @SuppressWarnings("serial") abstract static class BulkTask extends CountedCompleter { + Node[] tab; // same as Traverser + Node next; + TableStack stack, spare; + int index; + int baseIndex; + int baseLimit; + final int baseSize; + int batch; // split control BulkTask(BulkTask par, int b, int i, int f, Node[] t) { @@ -4433,8 +4593,7 @@ final Node advance() { int i, n; if (e != null) return next = e; - if (baseIndex >= baseLimit || (t = tab) == null || - (n = t.length) <= (i = index) || i < 0) + if (baseIndex >= baseLimit || (t = tab) == null || (n = t.length) <= (i = index) || i < 0) return next = null; if ((e = tabAt(t, i)) != null && e.hash < 0) { if (e instanceof ForwardingNode) { @@ -4493,13 +4652,11 @@ private void recoverState(int n) { * simplest hoisted bypass to help avoid convoluted traps. */ @SuppressWarnings("serial") - static final class ForEachKeyTask - extends BulkTask { + static final class ForEachKeyTask extends BulkTask { + final Consumer action; - ForEachKeyTask - (BulkTask p, int b, int i, int f, Node[] t, - Consumer action) { + ForEachKeyTask(BulkTask p, int b, int i, int f, Node[] t, Consumer action) { super(p, b, i, f, t); this.action = action; } @@ -4507,12 +4664,9 @@ static final class ForEachKeyTask public final void compute() { final Consumer action; if ((action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachKeyTask - (this, batch >>>= 1, baseLimit = h, f, tab, - action).fork(); + new ForEachKeyTask(this, batch >>>= 1, baseLimit = h, f, tab, action).fork(); } for (Node p; (p = advance()) != null; ) action.accept(p.key); @@ -4522,13 +4676,11 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachValueTask - extends BulkTask { + static final class ForEachValueTask extends BulkTask { + final Consumer action; - ForEachValueTask - (BulkTask p, int b, int i, int f, Node[] t, - Consumer action) { + ForEachValueTask(BulkTask p, int b, int i, int f, Node[] t, Consumer action) { super(p, b, i, f, t); this.action = action; } @@ -4536,12 +4688,9 @@ static final class ForEachValueTask public final void compute() { final Consumer action; if ((action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachValueTask - (this, batch >>>= 1, baseLimit = h, f, tab, - action).fork(); + new ForEachValueTask(this, batch >>>= 1, baseLimit = h, f, tab, action).fork(); } for (Node p; (p = advance()) != null; ) action.accept(p.val); @@ -4551,13 +4700,12 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachEntryTask - extends BulkTask { + static final class ForEachEntryTask extends BulkTask { + final Consumer> action; - ForEachEntryTask - (BulkTask p, int b, int i, int f, Node[] t, - Consumer> action) { + ForEachEntryTask(BulkTask p, int b, int i, int f, Node[] t, + Consumer> action) { super(p, b, i, f, t); this.action = action; } @@ -4565,12 +4713,9 @@ static final class ForEachEntryTask public final void compute() { final Consumer> action; if ((action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachEntryTask - (this, batch >>>= 1, baseLimit = h, f, tab, - action).fork(); + new ForEachEntryTask(this, batch >>>= 1, baseLimit = h, f, tab, action).fork(); } for (Node p; (p = advance()) != null; ) action.accept(p); @@ -4580,13 +4725,12 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachMappingTask - extends BulkTask { + static final class ForEachMappingTask extends BulkTask { + final BiConsumer action; - ForEachMappingTask - (BulkTask p, int b, int i, int f, Node[] t, - BiConsumer action) { + ForEachMappingTask(BulkTask p, int b, int i, int f, Node[] t, + BiConsumer action) { super(p, b, i, f, t); this.action = action; } @@ -4594,12 +4738,9 @@ static final class ForEachMappingTask public final void compute() { final BiConsumer action; if ((action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachMappingTask - (this, batch >>>= 1, baseLimit = h, f, tab, - action).fork(); + new ForEachMappingTask(this, batch >>>= 1, baseLimit = h, f, tab, action).fork(); } for (Node p; (p = advance()) != null; ) action.accept(p.key, p.val); @@ -4609,14 +4750,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachTransformedKeyTask - extends BulkTask { + static final class ForEachTransformedKeyTask extends BulkTask { + final Function transformer; + final Consumer action; - ForEachTransformedKeyTask - (BulkTask p, int b, int i, int f, Node[] t, - Function transformer, Consumer action) { + ForEachTransformedKeyTask(BulkTask p, int b, int i, int f, Node[] t, + Function transformer, Consumer action) { super(p, b, i, f, t); this.transformer = transformer; this.action = action; @@ -4625,14 +4766,11 @@ static final class ForEachTransformedKeyTask public final void compute() { final Function transformer; final Consumer action; - if ((transformer = this.transformer) != null && - (action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachTransformedKeyTask - (this, batch >>>= 1, baseLimit = h, f, tab, - transformer, action).fork(); + new ForEachTransformedKeyTask(this, batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); } for (Node p; (p = advance()) != null; ) { U u; @@ -4645,14 +4783,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachTransformedValueTask - extends BulkTask { + static final class ForEachTransformedValueTask extends BulkTask { + final Function transformer; + final Consumer action; - ForEachTransformedValueTask - (BulkTask p, int b, int i, int f, Node[] t, - Function transformer, Consumer action) { + ForEachTransformedValueTask(BulkTask p, int b, int i, int f, Node[] t, + Function transformer, Consumer action) { super(p, b, i, f, t); this.transformer = transformer; this.action = action; @@ -4661,14 +4799,11 @@ static final class ForEachTransformedValueTask public final void compute() { final Function transformer; final Consumer action; - if ((transformer = this.transformer) != null && - (action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachTransformedValueTask - (this, batch >>>= 1, baseLimit = h, f, tab, - transformer, action).fork(); + new ForEachTransformedValueTask(this, batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); } for (Node p; (p = advance()) != null; ) { U u; @@ -4681,14 +4816,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachTransformedEntryTask - extends BulkTask { + static final class ForEachTransformedEntryTask extends BulkTask { + final Function, ? extends U> transformer; + final Consumer action; - ForEachTransformedEntryTask - (BulkTask p, int b, int i, int f, Node[] t, - Function, ? extends U> transformer, Consumer action) { + ForEachTransformedEntryTask(BulkTask p, int b, int i, int f, Node[] t, + Function, ? extends U> transformer, Consumer action) { super(p, b, i, f, t); this.transformer = transformer; this.action = action; @@ -4697,14 +4832,11 @@ static final class ForEachTransformedEntryTask public final void compute() { final Function, ? extends U> transformer; final Consumer action; - if ((transformer = this.transformer) != null && - (action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachTransformedEntryTask - (this, batch >>>= 1, baseLimit = h, f, tab, - transformer, action).fork(); + new ForEachTransformedEntryTask(this, batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); } for (Node p; (p = advance()) != null; ) { U u; @@ -4717,15 +4849,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ForEachTransformedMappingTask - extends BulkTask { + static final class ForEachTransformedMappingTask extends BulkTask { + final BiFunction transformer; + final Consumer action; - ForEachTransformedMappingTask - (BulkTask p, int b, int i, int f, Node[] t, - BiFunction transformer, - Consumer action) { + ForEachTransformedMappingTask(BulkTask p, int b, int i, int f, Node[] t, + BiFunction transformer, Consumer action) { super(p, b, i, f, t); this.transformer = transformer; this.action = action; @@ -4734,14 +4865,11 @@ static final class ForEachTransformedMappingTask public final void compute() { final BiFunction transformer; final Consumer action; - if ((transformer = this.transformer) != null && - (action = this.action) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - new ForEachTransformedMappingTask - (this, batch >>>= 1, baseLimit = h, f, tab, - transformer, action).fork(); + new ForEachTransformedMappingTask(this, batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); } for (Node p; (p = advance()) != null; ) { U u; @@ -4754,15 +4882,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class SearchKeysTask - extends BulkTask { + static final class SearchKeysTask extends BulkTask { + final Function searchFunction; + final AtomicReference result; - SearchKeysTask - (BulkTask p, int b, int i, int f, Node[] t, - Function searchFunction, - AtomicReference result) { + SearchKeysTask(BulkTask p, int b, int i, int f, Node[] t, + Function searchFunction, AtomicReference result) { super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; @@ -4775,16 +4902,13 @@ public final U getRawResult() { public final void compute() { final Function searchFunction; final AtomicReference result; - if ((searchFunction = this.searchFunction) != null && - (result = this.result) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((searchFunction = this.searchFunction) != null && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { if (result.get() != null) return; addToPendingCount(1); - new SearchKeysTask - (this, batch >>>= 1, baseLimit = h, f, tab, - searchFunction, result).fork(); + new SearchKeysTask(this, batch >>>= 1, baseLimit = h, f, tab, searchFunction, + result).fork(); } while (result.get() == null) { U u; @@ -4804,15 +4928,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class SearchValuesTask - extends BulkTask { + static final class SearchValuesTask extends BulkTask { + final Function searchFunction; + final AtomicReference result; - SearchValuesTask - (BulkTask p, int b, int i, int f, Node[] t, - Function searchFunction, - AtomicReference result) { + SearchValuesTask(BulkTask p, int b, int i, int f, Node[] t, + Function searchFunction, AtomicReference result) { super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; @@ -4825,16 +4948,13 @@ public final U getRawResult() { public final void compute() { final Function searchFunction; final AtomicReference result; - if ((searchFunction = this.searchFunction) != null && - (result = this.result) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((searchFunction = this.searchFunction) != null && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { if (result.get() != null) return; addToPendingCount(1); - new SearchValuesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - searchFunction, result).fork(); + new SearchValuesTask(this, batch >>>= 1, baseLimit = h, f, tab, searchFunction, + result).fork(); } while (result.get() == null) { U u; @@ -4854,15 +4974,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class SearchEntriesTask - extends BulkTask { + static final class SearchEntriesTask extends BulkTask { + final Function, ? extends U> searchFunction; + final AtomicReference result; - SearchEntriesTask - (BulkTask p, int b, int i, int f, Node[] t, - Function, ? extends U> searchFunction, - AtomicReference result) { + SearchEntriesTask(BulkTask p, int b, int i, int f, Node[] t, + Function, ? extends U> searchFunction, AtomicReference result) { super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; @@ -4875,16 +4994,13 @@ public final U getRawResult() { public final void compute() { final Function, ? extends U> searchFunction; final AtomicReference result; - if ((searchFunction = this.searchFunction) != null && - (result = this.result) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((searchFunction = this.searchFunction) != null && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { if (result.get() != null) return; addToPendingCount(1); - new SearchEntriesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - searchFunction, result).fork(); + new SearchEntriesTask(this, batch >>>= 1, baseLimit = h, f, tab, searchFunction, + result).fork(); } while (result.get() == null) { U u; @@ -4904,15 +5020,14 @@ public final void compute() { } @SuppressWarnings("serial") - static final class SearchMappingsTask - extends BulkTask { + static final class SearchMappingsTask extends BulkTask { + final BiFunction searchFunction; + final AtomicReference result; - SearchMappingsTask - (BulkTask p, int b, int i, int f, Node[] t, - BiFunction searchFunction, - AtomicReference result) { + SearchMappingsTask(BulkTask p, int b, int i, int f, Node[] t, + BiFunction searchFunction, AtomicReference result) { super(p, b, i, f, t); this.searchFunction = searchFunction; this.result = result; @@ -4925,16 +5040,13 @@ public final U getRawResult() { public final void compute() { final BiFunction searchFunction; final AtomicReference result; - if ((searchFunction = this.searchFunction) != null && - (result = this.result) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((searchFunction = this.searchFunction) != null && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { if (result.get() != null) return; addToPendingCount(1); - new SearchMappingsTask - (this, batch >>>= 1, baseLimit = h, f, tab, - searchFunction, result).fork(); + new SearchMappingsTask(this, batch >>>= 1, baseLimit = h, f, tab, searchFunction, + result).fork(); } while (result.get() == null) { U u; @@ -4954,16 +5066,16 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ReduceKeysTask - extends BulkTask { + static final class ReduceKeysTask extends BulkTask { + final BiFunction reducer; + K result; + ReduceKeysTask rights, nextRight; - ReduceKeysTask - (BulkTask p, int b, int i, int f, Node[] t, - ReduceKeysTask nextRight, - BiFunction reducer) { + ReduceKeysTask(BulkTask p, int b, int i, int f, Node[] t, ReduceKeysTask nextRight, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.reducer = reducer; @@ -4976,12 +5088,10 @@ public final K getRawResult() { public final void compute() { final BiFunction reducer; if ((reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new ReduceKeysTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, reducer)).fork(); + (rights = new ReduceKeysTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + reducer)).fork(); } K r = null; for (Node p; (p = advance()) != null; ) { @@ -4992,14 +5102,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - ReduceKeysTask - t = (ReduceKeysTask) c, - s = t.rights; + ReduceKeysTask t = (ReduceKeysTask) c, s = t.rights; while (s != null) { K tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5008,16 +5115,16 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ReduceValuesTask - extends BulkTask { + static final class ReduceValuesTask extends BulkTask { + final BiFunction reducer; + V result; + ReduceValuesTask rights, nextRight; - ReduceValuesTask - (BulkTask p, int b, int i, int f, Node[] t, - ReduceValuesTask nextRight, - BiFunction reducer) { + ReduceValuesTask(BulkTask p, int b, int i, int f, Node[] t, ReduceValuesTask nextRight, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.reducer = reducer; @@ -5030,12 +5137,10 @@ public final V getRawResult() { public final void compute() { final BiFunction reducer; if ((reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new ReduceValuesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, reducer)).fork(); + (rights = new ReduceValuesTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + reducer)).fork(); } V r = null; for (Node p; (p = advance()) != null; ) { @@ -5046,14 +5151,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - ReduceValuesTask - t = (ReduceValuesTask) c, - s = t.rights; + ReduceValuesTask t = (ReduceValuesTask) c, s = t.rights; while (s != null) { V tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5062,16 +5164,16 @@ public final void compute() { } @SuppressWarnings("serial") - static final class ReduceEntriesTask - extends BulkTask> { + static final class ReduceEntriesTask extends BulkTask> { + final BiFunction, Map.Entry, ? extends Map.Entry> reducer; + Map.Entry result; + ReduceEntriesTask rights, nextRight; - ReduceEntriesTask - (BulkTask p, int b, int i, int f, Node[] t, - ReduceEntriesTask nextRight, - BiFunction, Map.Entry, ? extends Map.Entry> reducer) { + ReduceEntriesTask(BulkTask p, int b, int i, int f, Node[] t, ReduceEntriesTask nextRight, + BiFunction, Map.Entry, ? extends Map.Entry> reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.reducer = reducer; @@ -5084,12 +5186,10 @@ public final Map.Entry getRawResult() { public final void compute() { final BiFunction, Map.Entry, ? extends Map.Entry> reducer; if ((reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new ReduceEntriesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, reducer)).fork(); + (rights = new ReduceEntriesTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + reducer)).fork(); } Map.Entry r = null; for (Node p; (p = advance()) != null; ) @@ -5098,14 +5198,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - ReduceEntriesTask - t = (ReduceEntriesTask) c, - s = t.rights; + ReduceEntriesTask t = (ReduceEntriesTask) c, s = t.rights; while (s != null) { Map.Entry tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5114,18 +5211,19 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceKeysTask - extends BulkTask { + static final class MapReduceKeysTask extends BulkTask { + final Function transformer; + final BiFunction reducer; + U result; + MapReduceKeysTask rights, nextRight; - MapReduceKeysTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceKeysTask nextRight, - Function transformer, - BiFunction reducer) { + MapReduceKeysTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceKeysTask nextRight, Function transformer, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5139,14 +5237,11 @@ public final U getRawResult() { public final void compute() { final Function transformer; final BiFunction reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceKeysTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, reducer)).fork(); + (rights = new MapReduceKeysTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); } U r = null; for (Node p; (p = advance()) != null; ) { @@ -5158,14 +5253,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceKeysTask - t = (MapReduceKeysTask) c, - s = t.rights; + MapReduceKeysTask t = (MapReduceKeysTask) c, s = t.rights; while (s != null) { U tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5174,18 +5266,19 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceValuesTask - extends BulkTask { + static final class MapReduceValuesTask extends BulkTask { + final Function transformer; + final BiFunction reducer; + U result; + MapReduceValuesTask rights, nextRight; - MapReduceValuesTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceValuesTask nextRight, - Function transformer, - BiFunction reducer) { + MapReduceValuesTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceValuesTask nextRight, Function transformer, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5199,14 +5292,11 @@ public final U getRawResult() { public final void compute() { final Function transformer; final BiFunction reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceValuesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, reducer)).fork(); + (rights = new MapReduceValuesTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); } U r = null; for (Node p; (p = advance()) != null; ) { @@ -5218,14 +5308,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceValuesTask - t = (MapReduceValuesTask) c, - s = t.rights; + MapReduceValuesTask t = (MapReduceValuesTask) c, s = t.rights; while (s != null) { U tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5234,18 +5321,19 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceEntriesTask - extends BulkTask { + static final class MapReduceEntriesTask extends BulkTask { + final Function, ? extends U> transformer; + final BiFunction reducer; + U result; + MapReduceEntriesTask rights, nextRight; - MapReduceEntriesTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceEntriesTask nextRight, - Function, ? extends U> transformer, - BiFunction reducer) { + MapReduceEntriesTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceEntriesTask nextRight, Function, ? extends U> transformer, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5259,14 +5347,11 @@ public final U getRawResult() { public final void compute() { final Function, ? extends U> transformer; final BiFunction reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceEntriesTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, reducer)).fork(); + (rights = new MapReduceEntriesTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); } U r = null; for (Node p; (p = advance()) != null; ) { @@ -5278,14 +5363,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceEntriesTask - t = (MapReduceEntriesTask) c, - s = t.rights; + MapReduceEntriesTask t = (MapReduceEntriesTask) c, s = t.rights; while (s != null) { U tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5294,18 +5376,19 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceMappingsTask - extends BulkTask { + static final class MapReduceMappingsTask extends BulkTask { + final BiFunction transformer; + final BiFunction reducer; + U result; + MapReduceMappingsTask rights, nextRight; - MapReduceMappingsTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceMappingsTask nextRight, - BiFunction transformer, - BiFunction reducer) { + MapReduceMappingsTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceMappingsTask nextRight, BiFunction transformer, + BiFunction reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5319,14 +5402,11 @@ public final U getRawResult() { public final void compute() { final BiFunction transformer; final BiFunction reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceMappingsTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, reducer)).fork(); + (rights = new MapReduceMappingsTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); } U r = null; for (Node p; (p = advance()) != null; ) { @@ -5338,14 +5418,11 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceMappingsTask - t = (MapReduceMappingsTask) c, - s = t.rights; + MapReduceMappingsTask t = (MapReduceMappingsTask) c, s = t.rights; while (s != null) { U tr, sr; if ((sr = s.result) != null) - t.result = (((tr = t.result) == null) ? sr : - reducer.apply(tr, sr)); + t.result = (((tr = t.result) == null) ? sr : reducer.apply(tr, sr)); s = t.rights = s.nextRight; } } @@ -5354,20 +5431,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceKeysToDoubleTask - extends BulkTask { + static final class MapReduceKeysToDoubleTask extends BulkTask { + final ToDoubleFunction transformer; + final DoubleBinaryOperator reducer; + final double basis; + double result; + MapReduceKeysToDoubleTask rights, nextRight; - MapReduceKeysToDoubleTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceKeysToDoubleTask nextRight, - ToDoubleFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + MapReduceKeysToDoubleTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceKeysToDoubleTask nextRight, ToDoubleFunction transformer, double basis, + DoubleBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5382,15 +5460,12 @@ public final Double getRawResult() { public final void compute() { final ToDoubleFunction transformer; final DoubleBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { double r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceKeysToDoubleTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceKeysToDoubleTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.key)); @@ -5398,9 +5473,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceKeysToDoubleTask - t = (MapReduceKeysToDoubleTask) c, - s = t.rights; + MapReduceKeysToDoubleTask t = (MapReduceKeysToDoubleTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsDouble(t.result, s.result); s = t.rights = s.nextRight; @@ -5411,20 +5484,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceValuesToDoubleTask - extends BulkTask { + static final class MapReduceValuesToDoubleTask extends BulkTask { + final ToDoubleFunction transformer; + final DoubleBinaryOperator reducer; + final double basis; + double result; + MapReduceValuesToDoubleTask rights, nextRight; - MapReduceValuesToDoubleTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceValuesToDoubleTask nextRight, - ToDoubleFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + MapReduceValuesToDoubleTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceValuesToDoubleTask nextRight, ToDoubleFunction transformer, double basis, + DoubleBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5439,15 +5513,12 @@ public final Double getRawResult() { public final void compute() { final ToDoubleFunction transformer; final DoubleBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { double r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceValuesToDoubleTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceValuesToDoubleTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.val)); @@ -5455,9 +5526,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceValuesToDoubleTask - t = (MapReduceValuesToDoubleTask) c, - s = t.rights; + MapReduceValuesToDoubleTask t = (MapReduceValuesToDoubleTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsDouble(t.result, s.result); s = t.rights = s.nextRight; @@ -5468,20 +5537,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceEntriesToDoubleTask - extends BulkTask { + static final class MapReduceEntriesToDoubleTask extends BulkTask { + final ToDoubleFunction> transformer; + final DoubleBinaryOperator reducer; + final double basis; + double result; + MapReduceEntriesToDoubleTask rights, nextRight; - MapReduceEntriesToDoubleTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceEntriesToDoubleTask nextRight, - ToDoubleFunction> transformer, - double basis, - DoubleBinaryOperator reducer) { + MapReduceEntriesToDoubleTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceEntriesToDoubleTask nextRight, ToDoubleFunction> transformer, + double basis, DoubleBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5496,15 +5566,12 @@ public final Double getRawResult() { public final void compute() { final ToDoubleFunction> transformer; final DoubleBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { double r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceEntriesToDoubleTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceEntriesToDoubleTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsDouble(r, transformer.applyAsDouble(p)); @@ -5512,9 +5579,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceEntriesToDoubleTask - t = (MapReduceEntriesToDoubleTask) c, - s = t.rights; + MapReduceEntriesToDoubleTask t = (MapReduceEntriesToDoubleTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsDouble(t.result, s.result); s = t.rights = s.nextRight; @@ -5525,20 +5590,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceMappingsToDoubleTask - extends BulkTask { + static final class MapReduceMappingsToDoubleTask extends BulkTask { + final ToDoubleBiFunction transformer; + final DoubleBinaryOperator reducer; + final double basis; + double result; + MapReduceMappingsToDoubleTask rights, nextRight; - MapReduceMappingsToDoubleTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceMappingsToDoubleTask nextRight, - ToDoubleBiFunction transformer, - double basis, - DoubleBinaryOperator reducer) { + MapReduceMappingsToDoubleTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceMappingsToDoubleTask nextRight, ToDoubleBiFunction transformer, + double basis, DoubleBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5553,15 +5619,12 @@ public final Double getRawResult() { public final void compute() { final ToDoubleBiFunction transformer; final DoubleBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { double r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceMappingsToDoubleTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceMappingsToDoubleTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsDouble(r, transformer.applyAsDouble(p.key, p.val)); @@ -5569,9 +5632,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceMappingsToDoubleTask - t = (MapReduceMappingsToDoubleTask) c, - s = t.rights; + MapReduceMappingsToDoubleTask t = (MapReduceMappingsToDoubleTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsDouble(t.result, s.result); s = t.rights = s.nextRight; @@ -5582,20 +5643,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceKeysToLongTask - extends BulkTask { + static final class MapReduceKeysToLongTask extends BulkTask { + final ToLongFunction transformer; + final LongBinaryOperator reducer; + final long basis; + long result; + MapReduceKeysToLongTask rights, nextRight; - MapReduceKeysToLongTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceKeysToLongTask nextRight, - ToLongFunction transformer, - long basis, - LongBinaryOperator reducer) { + MapReduceKeysToLongTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceKeysToLongTask nextRight, ToLongFunction transformer, long basis, + LongBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5610,15 +5672,12 @@ public final Long getRawResult() { public final void compute() { final ToLongFunction transformer; final LongBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { long r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceKeysToLongTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceKeysToLongTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsLong(r, transformer.applyAsLong(p.key)); @@ -5626,9 +5685,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceKeysToLongTask - t = (MapReduceKeysToLongTask) c, - s = t.rights; + MapReduceKeysToLongTask t = (MapReduceKeysToLongTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsLong(t.result, s.result); s = t.rights = s.nextRight; @@ -5639,20 +5696,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceValuesToLongTask - extends BulkTask { + static final class MapReduceValuesToLongTask extends BulkTask { + final ToLongFunction transformer; + final LongBinaryOperator reducer; + final long basis; + long result; + MapReduceValuesToLongTask rights, nextRight; - MapReduceValuesToLongTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceValuesToLongTask nextRight, - ToLongFunction transformer, - long basis, - LongBinaryOperator reducer) { + MapReduceValuesToLongTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceValuesToLongTask nextRight, ToLongFunction transformer, long basis, + LongBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5667,15 +5725,12 @@ public final Long getRawResult() { public final void compute() { final ToLongFunction transformer; final LongBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { long r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceValuesToLongTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceValuesToLongTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsLong(r, transformer.applyAsLong(p.val)); @@ -5683,9 +5738,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceValuesToLongTask - t = (MapReduceValuesToLongTask) c, - s = t.rights; + MapReduceValuesToLongTask t = (MapReduceValuesToLongTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsLong(t.result, s.result); s = t.rights = s.nextRight; @@ -5696,20 +5749,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceEntriesToLongTask - extends BulkTask { + static final class MapReduceEntriesToLongTask extends BulkTask { + final ToLongFunction> transformer; + final LongBinaryOperator reducer; + final long basis; + long result; + MapReduceEntriesToLongTask rights, nextRight; - MapReduceEntriesToLongTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceEntriesToLongTask nextRight, - ToLongFunction> transformer, - long basis, - LongBinaryOperator reducer) { + MapReduceEntriesToLongTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceEntriesToLongTask nextRight, ToLongFunction> transformer, long basis, + LongBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5724,15 +5778,12 @@ public final Long getRawResult() { public final void compute() { final ToLongFunction> transformer; final LongBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { long r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceEntriesToLongTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceEntriesToLongTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsLong(r, transformer.applyAsLong(p)); @@ -5740,9 +5791,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceEntriesToLongTask - t = (MapReduceEntriesToLongTask) c, - s = t.rights; + MapReduceEntriesToLongTask t = (MapReduceEntriesToLongTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsLong(t.result, s.result); s = t.rights = s.nextRight; @@ -5753,20 +5802,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceMappingsToLongTask - extends BulkTask { + static final class MapReduceMappingsToLongTask extends BulkTask { + final ToLongBiFunction transformer; + final LongBinaryOperator reducer; + final long basis; + long result; + MapReduceMappingsToLongTask rights, nextRight; - MapReduceMappingsToLongTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceMappingsToLongTask nextRight, - ToLongBiFunction transformer, - long basis, - LongBinaryOperator reducer) { + MapReduceMappingsToLongTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceMappingsToLongTask nextRight, ToLongBiFunction transformer, + long basis, LongBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5781,15 +5831,12 @@ public final Long getRawResult() { public final void compute() { final ToLongBiFunction transformer; final LongBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { long r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceMappingsToLongTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceMappingsToLongTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsLong(r, transformer.applyAsLong(p.key, p.val)); @@ -5797,9 +5844,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceMappingsToLongTask - t = (MapReduceMappingsToLongTask) c, - s = t.rights; + MapReduceMappingsToLongTask t = (MapReduceMappingsToLongTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsLong(t.result, s.result); s = t.rights = s.nextRight; @@ -5810,20 +5855,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceKeysToIntTask - extends BulkTask { + static final class MapReduceKeysToIntTask extends BulkTask { + final ToIntFunction transformer; + final IntBinaryOperator reducer; + final int basis; + int result; + MapReduceKeysToIntTask rights, nextRight; - MapReduceKeysToIntTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceKeysToIntTask nextRight, - ToIntFunction transformer, - int basis, - IntBinaryOperator reducer) { + MapReduceKeysToIntTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceKeysToIntTask nextRight, ToIntFunction transformer, int basis, + IntBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5838,15 +5884,12 @@ public final Integer getRawResult() { public final void compute() { final ToIntFunction transformer; final IntBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { int r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceKeysToIntTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceKeysToIntTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsInt(r, transformer.applyAsInt(p.key)); @@ -5854,9 +5897,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceKeysToIntTask - t = (MapReduceKeysToIntTask) c, - s = t.rights; + MapReduceKeysToIntTask t = (MapReduceKeysToIntTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsInt(t.result, s.result); s = t.rights = s.nextRight; @@ -5867,20 +5908,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceValuesToIntTask - extends BulkTask { + static final class MapReduceValuesToIntTask extends BulkTask { + final ToIntFunction transformer; + final IntBinaryOperator reducer; + final int basis; + int result; + MapReduceValuesToIntTask rights, nextRight; - MapReduceValuesToIntTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceValuesToIntTask nextRight, - ToIntFunction transformer, - int basis, - IntBinaryOperator reducer) { + MapReduceValuesToIntTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceValuesToIntTask nextRight, ToIntFunction transformer, int basis, + IntBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5895,15 +5937,12 @@ public final Integer getRawResult() { public final void compute() { final ToIntFunction transformer; final IntBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { int r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceValuesToIntTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceValuesToIntTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsInt(r, transformer.applyAsInt(p.val)); @@ -5911,9 +5950,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceValuesToIntTask - t = (MapReduceValuesToIntTask) c, - s = t.rights; + MapReduceValuesToIntTask t = (MapReduceValuesToIntTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsInt(t.result, s.result); s = t.rights = s.nextRight; @@ -5924,20 +5961,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceEntriesToIntTask - extends BulkTask { + static final class MapReduceEntriesToIntTask extends BulkTask { + final ToIntFunction> transformer; + final IntBinaryOperator reducer; + final int basis; + int result; + MapReduceEntriesToIntTask rights, nextRight; - MapReduceEntriesToIntTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceEntriesToIntTask nextRight, - ToIntFunction> transformer, - int basis, - IntBinaryOperator reducer) { + MapReduceEntriesToIntTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceEntriesToIntTask nextRight, ToIntFunction> transformer, int basis, + IntBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -5952,15 +5990,12 @@ public final Integer getRawResult() { public final void compute() { final ToIntFunction> transformer; final IntBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { int r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceEntriesToIntTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceEntriesToIntTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsInt(r, transformer.applyAsInt(p)); @@ -5968,9 +6003,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceEntriesToIntTask - t = (MapReduceEntriesToIntTask) c, - s = t.rights; + MapReduceEntriesToIntTask t = (MapReduceEntriesToIntTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsInt(t.result, s.result); s = t.rights = s.nextRight; @@ -5981,20 +6014,21 @@ public final void compute() { } @SuppressWarnings("serial") - static final class MapReduceMappingsToIntTask - extends BulkTask { + static final class MapReduceMappingsToIntTask extends BulkTask { + final ToIntBiFunction transformer; + final IntBinaryOperator reducer; + final int basis; + int result; + MapReduceMappingsToIntTask rights, nextRight; - MapReduceMappingsToIntTask - (BulkTask p, int b, int i, int f, Node[] t, - MapReduceMappingsToIntTask nextRight, - ToIntBiFunction transformer, - int basis, - IntBinaryOperator reducer) { + MapReduceMappingsToIntTask(BulkTask p, int b, int i, int f, Node[] t, + MapReduceMappingsToIntTask nextRight, ToIntBiFunction transformer, + int basis, IntBinaryOperator reducer) { super(p, b, i, f, t); this.nextRight = nextRight; this.transformer = transformer; @@ -6002,22 +6036,21 @@ static final class MapReduceMappingsToIntTask this.reducer = reducer; } + @Override public final Integer getRawResult() { return result; } + @Override public final void compute() { final ToIntBiFunction transformer; final IntBinaryOperator reducer; - if ((transformer = this.transformer) != null && - (reducer = this.reducer) != null) { + if ((transformer = this.transformer) != null && (reducer = this.reducer) != null) { int r = this.basis; - for (int i = baseIndex, f, h; batch > 0 && - (h = ((f = baseLimit) + i) >>> 1) > i; ) { + for (int i = baseIndex, f, h; batch > 0 && (h = ((f = baseLimit) + i) >>> 1) > i; ) { addToPendingCount(1); - (rights = new MapReduceMappingsToIntTask - (this, batch >>>= 1, baseLimit = h, f, tab, - rights, transformer, r, reducer)).fork(); + (rights = new MapReduceMappingsToIntTask(this, batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); } for (Node p; (p = advance()) != null; ) r = reducer.applyAsInt(r, transformer.applyAsInt(p.key, p.val)); @@ -6025,9 +6058,7 @@ public final void compute() { CountedCompleter c; for (c = firstComplete(); c != null; c = c.nextComplete()) { @SuppressWarnings("unchecked") - MapReduceMappingsToIntTask - t = (MapReduceMappingsToIntTask) c, - s = t.rights; + MapReduceMappingsToIntTask t = (MapReduceMappingsToIntTask) c, s = t.rights; while (s != null) { t.result = reducer.applyAsInt(t.result, s.result); s = t.rights = s.nextRight; @@ -6039,34 +6070,47 @@ public final void compute() { // Unsafe mechanics private static final sun.misc.Unsafe U; + private static final long SIZECTL; + private static final long TRANSFERINDEX; + private static final long BASECOUNT; + private static final long CELLSBUSY; + private static final long CELLVALUE; + private static final long ABASE; + private static final int ASHIFT; static { try { U = sun.misc.Unsafe.getUnsafe(); Class k = ConcurrentHashMap.class; - SIZECTL = U.objectFieldOffset - (k.getDeclaredField("sizeCtl")); - TRANSFERINDEX = U.objectFieldOffset - (k.getDeclaredField("transferIndex")); - BASECOUNT = U.objectFieldOffset - (k.getDeclaredField("baseCount")); - CELLSBUSY = U.objectFieldOffset - (k.getDeclaredField("cellsBusy")); + SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl")); + // 初始值是32 class信息偏移32位能拿到这个字段 这个值是固定常量 + TRANSFERINDEX = U.objectFieldOffset(k.getDeclaredField("transferIndex")); + BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount")); + CELLSBUSY = U.objectFieldOffset(k.getDeclaredField("cellsBusy")); Class ck = CounterCell.class; - CELLVALUE = U.objectFieldOffset - (ck.getDeclaredField("value")); + CELLVALUE = U.objectFieldOffset(ck.getDeclaredField("value")); + // 数组的Class Class ak = Node[].class; + // 数组class中第一个元素的偏移量(字节数),可以配合'每个元素的偏移量'形成固定的偏移量到数组class指定的位置拿到元素. + // 这里是16 ABASE = U.arrayBaseOffset(ak); + // 数组中每个元素占几个字节 这里是4 int scale = U.arrayIndexScale(ak); - if ((scale & (scale - 1)) != 0) + // scale必须是2的幂次 + if ((scale & (scale - 1)) != 0) { throw new Error("data type scale not a power of two"); + } + // 返回指定int值二进制补码最高位左边0的个数.如果入参是0就返回32 这里入参是4 00000000 00000000 00000000 00000100 返回29 + // 正数的补码就是其本身 + // 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1) + // 31-29 = 2 ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); } catch (Exception e) { throw new Error(e); diff --git a/src/java/util/concurrent/DelayQueue.java b/src/java/util/concurrent/DelayQueue.java index 4c319fd6..9c5450ab 100644 --- a/src/java/util/concurrent/DelayQueue.java +++ b/src/java/util/concurrent/DelayQueue.java @@ -133,6 +133,7 @@ public boolean add(E e) { * @return {@code true} * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { final ReentrantLock lock = this.lock; lock.lock(); diff --git a/src/java/util/concurrent/Executors.java b/src/java/util/concurrent/Executors.java index 88bf8491..640fa2d8 100644 --- a/src/java/util/concurrent/Executors.java +++ b/src/java/util/concurrent/Executors.java @@ -573,14 +573,17 @@ static class DefaultThreadFactory implements ThreadFactory { "-thread-"; } + @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - if (t.isDaemon()) + if (t.isDaemon()) { t.setDaemon(false); - if (t.getPriority() != Thread.NORM_PRIORITY) + } + if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); + } return t; } } @@ -608,6 +611,7 @@ static class PrivilegedThreadFactory extends DefaultThreadFactory { this.ccl = Thread.currentThread().getContextClassLoader(); } + @Override public Thread newThread(final Runnable r) { return super.newThread(new Runnable() { public void run() { diff --git a/src/java/util/concurrent/LinkedBlockingDeque.java b/src/java/util/concurrent/LinkedBlockingDeque.java index d1b53c15..865ade80 100644 --- a/src/java/util/concurrent/LinkedBlockingDeque.java +++ b/src/java/util/concurrent/LinkedBlockingDeque.java @@ -637,6 +637,7 @@ public boolean add(E e) { /** * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { return offerLast(e); } diff --git a/src/java/util/concurrent/LinkedBlockingQueue.java b/src/java/util/concurrent/LinkedBlockingQueue.java index 338d10d1..6e76821c 100644 --- a/src/java/util/concurrent/LinkedBlockingQueue.java +++ b/src/java/util/concurrent/LinkedBlockingQueue.java @@ -407,6 +407,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) * * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { if (e == null) throw new NullPointerException(); final AtomicInteger count = this.count; diff --git a/src/java/util/concurrent/LinkedTransferQueue.java b/src/java/util/concurrent/LinkedTransferQueue.java index d7bb978b..ebacec65 100644 --- a/src/java/util/concurrent/LinkedTransferQueue.java +++ b/src/java/util/concurrent/LinkedTransferQueue.java @@ -1192,6 +1192,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) { * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { xfer(e, true, ASYNC, 0); return true; diff --git a/src/java/util/concurrent/PriorityBlockingQueue.java b/src/java/util/concurrent/PriorityBlockingQueue.java index cb4e52e6..a8b38918 100644 --- a/src/java/util/concurrent/PriorityBlockingQueue.java +++ b/src/java/util/concurrent/PriorityBlockingQueue.java @@ -475,6 +475,7 @@ public boolean add(E e) { * priority queue's ordering * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { if (e == null) throw new NullPointerException(); diff --git a/src/java/util/concurrent/ScheduledThreadPoolExecutor.java b/src/java/util/concurrent/ScheduledThreadPoolExecutor.java index 8c0a4519..27628b39 100644 --- a/src/java/util/concurrent/ScheduledThreadPoolExecutor.java +++ b/src/java/util/concurrent/ScheduledThreadPoolExecutor.java @@ -757,6 +757,7 @@ public boolean getRemoveOnCancelPolicy() { * * @throws SecurityException {@inheritDoc} */ + @Override public void shutdown() { super.shutdown(); } @@ -1002,6 +1003,7 @@ public RunnableScheduledFuture peek() { } } + @Override public boolean offer(Runnable x) { if (x == null) throw new NullPointerException(); diff --git a/src/java/util/concurrent/SynchronousQueue.java b/src/java/util/concurrent/SynchronousQueue.java index 39e5f04f..c53917a7 100644 --- a/src/java/util/concurrent/SynchronousQueue.java +++ b/src/java/util/concurrent/SynchronousQueue.java @@ -908,6 +908,7 @@ public boolean offer(E e, long timeout, TimeUnit unit) * {@code false} * @throws NullPointerException if the specified element is null */ + @Override public boolean offer(E e) { if (e == null) throw new NullPointerException(); return transferer.transfer(e, true, 0) != null; diff --git a/src/java/util/concurrent/ThreadPoolExecutor.java b/src/java/util/concurrent/ThreadPoolExecutor.java index e2fba877..deab6068 100644 --- a/src/java/util/concurrent/ThreadPoolExecutor.java +++ b/src/java/util/concurrent/ThreadPoolExecutor.java @@ -287,10 +287,9 @@ */ public class ThreadPoolExecutor extends AbstractExecutorService { /** - * The main pool control state, ctl, is an atomic integer packing - * two conceptual fields - * workerCount, indicating the effective number of threads - * runState, indicating whether running, shutting down etc + * 线程池主要的状态标志, ctl,包含了下列两个值的原子类 + * workerCount, 有效的线程数 + * runState, 运行状态 * * In order to pack them into one int, we limit workerCount to * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 @@ -299,28 +298,25 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * and the shift/mask constants below adjusted. But until the need * arises, this code is a bit faster and simpler using an int. * - * The workerCount is the number of workers that have been - * permitted to start and not permitted to stop. The value may be - * transiently different from the actual number of live threads, + * The workerCount 调了开始方法但是没有调用停止方法的线程数. + * 和真正活着的线程数有点不一样, * for example when a ThreadFactory fails to create a thread when * asked, and when exiting threads are still performing * bookkeeping before terminating. The user-visible pool size is * reported as the current size of the workers set. * - * The runState provides the main lifecycle control, taking on values: + * 运行状态提供了主要的生命周期控制, taking on values: * - * RUNNING: Accept new tasks and process queued tasks - * SHUTDOWN: Don't accept new tasks, but process queued tasks - * STOP: Don't accept new tasks, don't process queued tasks, - * and interrupt in-progress tasks - * TIDYING: All tasks have terminated, workerCount is zero, + * RUNNING: 可以接受新任务 可以处理队列 + * SHUTDOWN: 不接受新任务 但还在处理队列 + * STOP: 不接受新任务 不处理队列 还要打断正在处理的任务 + * TIDYING整理: 所有任务都结束了, workerCount 是0, * the thread transitioning to state TIDYING * will run the terminated() hook method - * TERMINATED: terminated() has completed + * TERMINATED: terminated() 调用完了 * * The numerical order among these values matters, to allow - * ordered comparisons. The runState monotonically increases over - * time, but need not hit each state. The transitions are: + * ordered comparisons. The runState 随时间单调递增, 但不用每个状态都到达一次. 变化过程如下: * * RUNNING -> SHUTDOWN * On invocation of shutdown(), perhaps implicitly in finalize() @@ -342,19 +338,44 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * we can only terminate if, after seeing that it is empty, we see * that workerCount is 0 (which sometimes entails a recheck -- see * below). + * + * 默认值1010 0000 00000000 00000000 00000000 */ private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + + /** + * 29 + */ private static final int COUNT_BITS = Integer.SIZE - 3; + + /** + * 00001111 11111111 11111111 11111111 + */ private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits + /** + * 高四位1010 + */ private static final int RUNNING = -1 << COUNT_BITS; + /** + * 高四位0000 + */ private static final int SHUTDOWN = 0 << COUNT_BITS; + /** + * 高四位0010 + */ private static final int STOP = 1 << COUNT_BITS; + /** + * 高四位0100 + */ private static final int TIDYING = 2 << COUNT_BITS; + /** + * 高四位0110 + */ private static final int TERMINATED = 3 << COUNT_BITS; - // Packing and unpacking ctl + // Packing and unpacking ctl ~'非运算' private static int runStateOf(int c) { return c & ~CAPACITY; } private static int workerCountOf(int c) { return c & CAPACITY; } private static int ctlOf(int rs, int wc) { return rs | wc; } @@ -429,7 +450,7 @@ private void decrementWorkerCount() { /** * Set containing all worker threads in pool. Accessed only when - * holding mainLock. + * holding mainLock.包含池里的所有工作线程 */ private final HashSet workers = new HashSet(); @@ -564,6 +585,7 @@ private final class Worker /** * This class will never be serialized, but we provide a * serialVersionUID to suppress a javac warning. + * 这玩意儿没啥用 为了不要编译报警 */ private static final long serialVersionUID = 6138294804551838833L; @@ -585,6 +607,7 @@ private final class Worker } /** Delegates main run loop to outer runWorker */ + @Override public void run() { runWorker(this); } @@ -594,10 +617,12 @@ public void run() { // The value 0 represents the unlocked state. // The value 1 represents the locked state. + @Override protected boolean isHeldExclusively() { return getState() != 0; } + @Override protected boolean tryAcquire(int unused) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); @@ -606,6 +631,7 @@ protected boolean tryAcquire(int unused) { return false; } + @Override protected boolean tryRelease(int unused) { setExclusiveOwnerThread(null); setState(0); @@ -643,8 +669,9 @@ private void advanceRunState(int targetState) { for (;;) { int c = ctl.get(); if (runStateAtLeast(c, targetState) || - ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) + ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) { break; + } } } @@ -663,8 +690,9 @@ final void tryTerminate() { int c = ctl.get(); if (isRunning(c) || runStateAtLeast(c, TIDYING) || - (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) + (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) { return; + } if (workerCountOf(c) != 0) { // Eligible to terminate interruptIdleWorkers(ONLY_ONE); return; @@ -708,8 +736,9 @@ private void checkShutdownAccess() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - for (Worker w : workers) + for (Worker w : workers) { security.checkAccess(w.thread); + } } finally { mainLock.unlock(); } @@ -724,8 +753,9 @@ private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - for (Worker w : workers) + for (Worker w : workers) { w.interruptIfStarted(); + } } finally { mainLock.unlock(); } @@ -869,21 +899,26 @@ private boolean addWorker(Runnable firstTask, boolean core) { int c = ctl.get(); int rs = runStateOf(c); - // Check if queue empty only if necessary. + // Check if queue empty only if necessary.只在线程池是SHUTDOWN状态时检查队列是否为空 if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())) { return false; } for (;;) { int wc = workerCountOf(c); + // wc是否超标 if (wc >= CAPACITY || - wc >= (core ? corePoolSize : maximumPoolSize)) + wc >= (core ? corePoolSize : maximumPoolSize)) { return false; - if (compareAndIncrementWorkerCount(c)) + } + // 工人数量自增成功则直接跳出外层循环 + if (compareAndIncrementWorkerCount(c)) { break retry; + } c = ctl.get(); // Re-read ctl - if (runStateOf(c) != rs) + if (runStateOf(c) != rs) {// 两次运行状态不同 continue retry; + } // else CAS failed due to workerCount change; retry inner loop } } @@ -892,25 +927,28 @@ private boolean addWorker(Runnable firstTask, boolean core) { boolean workerAdded = false; Worker w = null; try { + // 创建工人 也会创建一个线程 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - // Recheck while holding lock. - // Back out on ThreadFactory failure or if - // shut down before lock acquired. + // 拿到锁后再检查一边. + // 创建线程失败 or 获得锁之前SHUTDOWN了 就退出. int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable + { throw new IllegalThreadStateException(); + } workers.add(w); int s = workers.size(); - if (s > largestPoolSize) + if (s > largestPoolSize) { largestPoolSize = s; + } workerAdded = true; } } finally { @@ -923,8 +961,10 @@ private boolean addWorker(Runnable firstTask, boolean core) { } } } finally { - if (! workerStarted) + // 添加失败 + if (! workerStarted) { addWorkerFailed(w); + } } return workerStarted; } @@ -940,8 +980,10 @@ private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (w != null) + if (w != null) { workers.remove(w); + } + // wc减1 decrementWorkerCount(); tryTerminate(); } finally { @@ -964,7 +1006,9 @@ private void addWorkerFailed(Worker w) { */ private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted + { decrementWorkerCount(); + } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); @@ -981,10 +1025,12 @@ private void processWorkerExit(Worker w, boolean completedAbruptly) { if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; - if (min == 0 && ! workQueue.isEmpty()) + if (min == 0 && ! workQueue.isEmpty()) { min = 1; - if (workerCountOf(c) >= min) + } + if (workerCountOf(c) >= min) { return; // replacement not needed + } } addWorker(null, false); } @@ -1022,20 +1068,25 @@ private Runnable getTask() { int wc = workerCountOf(c); - // Are workers subject to culling? + // Are workers subject to culling? worker可以消灭吗 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { - if (compareAndDecrementWorkerCount(c)) + if (compareAndDecrementWorkerCount(c)) { return null; + } continue; } try { + // 线程可以消灭就poll一段时间(keepAliveTime),拿到任务就返回任务,否则就是超时了 就干掉worker + // 线程不能干掉 如果有任务就返回 没有就一直空转 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); - if (r != null) + if (r != null) { return r; + } + timedOut = true; } catch (InterruptedException retry) { timedOut = false; @@ -1089,6 +1140,7 @@ private Runnable getTask() { final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; + // 如果firstTask不是null 第一次执行的时候会把变成null worker后面在执行其他任务也都是null w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; @@ -1266,10 +1318,12 @@ public ThreadPoolExecutor(int corePoolSize, if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || - keepAliveTime < 0) + keepAliveTime < 0) { throw new IllegalArgumentException(); - if (workQueue == null || threadFactory == null || handler == null) + } + if (workQueue == null || threadFactory == null || handler == null) { throw new NullPointerException(); + } this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); @@ -1295,6 +1349,7 @@ public ThreadPoolExecutor(int corePoolSize, * cannot be accepted for execution * @throws NullPointerException if {@code command} is null */ + @Override public void execute(Runnable command) { // 若任务为空,则抛 NPE,不能执行空任务 if (command == null) { @@ -1303,15 +1358,17 @@ public void execute(Runnable command) { int c = ctl.get(); // 若工作线程数小于核心线程数,则创建新的线程,并把当前任务 command 作为这个线程的第一个任务 if (workerCountOf(c) < corePoolSize) { + // 核心线程数不够优先创建核心线程 if (addWorker(command, true)) { return; } + // 添加失败往下走 c = ctl.get(); } /** * 至此,有以下两种情况: * 1.当前工作线程数大于等于核心线程数 - * 2.新建线程失败 + * 2.核心线程数不够且新建线程失败 * 此时会尝试将任务添加到阻塞队列 workQueue */ // 若线程池处于 RUNNING 状态,将任务添加到阻塞队列 workQueue 中 @@ -1351,6 +1408,7 @@ else if (!addWorker(command, false)) { * * @throws SecurityException {@inheritDoc} */ + @Override public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); @@ -1619,8 +1677,9 @@ public void allowCoreThreadTimeOut(boolean value) { throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); if (value != allowCoreThreadTimeOut) { allowCoreThreadTimeOut = value; - if (value) + if (value) { interruptIdleWorkers(); + } } } @@ -1994,6 +2053,7 @@ public CallerRunsPolicy() { } * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ + @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); @@ -2018,6 +2078,7 @@ public AbortPolicy() { } * @param e the executor attempting to execute this task * @throws RejectedExecutionException always */ + @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + @@ -2041,6 +2102,7 @@ public DiscardPolicy() { } * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ + @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } } diff --git a/src/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/src/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index d2fc4a7d..09ae35af 100644 --- a/src/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/src/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -1841,6 +1841,7 @@ public final void await() throws InterruptedException { *

  • If interrupted while blocked in step 4, throw InterruptedException. * */ + @Override public final long awaitNanos(long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) diff --git a/src/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/src/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 65270e65..eab7de6b 100644 --- a/src/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/src/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -1671,8 +1671,9 @@ final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ - if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) + if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) { return false; + } /* * Splice onto queue and try to set waitStatus of predecessor to @@ -1682,8 +1683,9 @@ final boolean transferForSignal(Node node) { */ Node p = enq(node); int ws = p.waitStatus; - if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) + if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) { LockSupport.unpark(node.thread); + } return true; } @@ -1853,10 +1855,11 @@ private Node addConditionWaiter() { t = lastWaiter; } Node node = new Node(Thread.currentThread(), Node.CONDITION); - if (t == null) + if (t == null) { firstWaiter = node; - else + } else { t.nextWaiter = node; + } lastWaiter = node; return node; } @@ -1869,8 +1872,9 @@ private Node addConditionWaiter() { */ private void doSignal(Node first) { do { - if ( (firstWaiter = first.nextWaiter) == null) + if ( (firstWaiter = first.nextWaiter) == null) { lastWaiter = null; + } first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); @@ -1911,9 +1915,9 @@ private void unlinkCancelledWaiters() { Node next = t.nextWaiter; if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; - if (trail == null) + if (trail == null) { firstWaiter = next; - else + } else trail.nextWaiter = next; if (next == null) lastWaiter = trail; @@ -1934,12 +1938,15 @@ private void unlinkCancelledWaiters() { * @throws IllegalMonitorStateException if {@link #isHeldExclusively} * returns {@code false} */ + @Override public final void signal() { - if (!isHeldExclusively()) + if (!isHeldExclusively()) { throw new IllegalMonitorStateException(); + } Node first = firstWaiter; - if (first != null) + if (first != null) { doSignal(first); + } } /** @@ -2029,23 +2036,30 @@ else if (interruptMode == REINTERRUPT) *
  • If interrupted while blocked in step 4, throw InterruptedException. * */ + @Override public final void await() throws InterruptedException { - if (Thread.interrupted()) + if (Thread.interrupted()) { throw new InterruptedException(); + } Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { LockSupport.park(this); - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) + if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { break; + } } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) + if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { interruptMode = REINTERRUPT; + } if (node.nextWaiter != null) // clean up if cancelled + { unlinkCancelledWaiters(); - if (interruptMode != 0) + } + if (interruptMode != 0) { reportInterruptAfterWait(interruptMode); + } } /** @@ -2061,10 +2075,12 @@ public final void await() throws InterruptedException { *
  • If interrupted while blocked in step 4, throw InterruptedException. * */ + @Override public final long awaitNanos(long nanosTimeout) throws InterruptedException { - if (Thread.interrupted()) + if (Thread.interrupted()) { throw new InterruptedException(); + } Node node = addConditionWaiter(); int savedState = fullyRelease(node); final long deadline = System.nanoTime() + nanosTimeout; @@ -2074,18 +2090,23 @@ public final long awaitNanos(long nanosTimeout) transferAfterCancelledWait(node); break; } - if (nanosTimeout >= spinForTimeoutThreshold) + if (nanosTimeout >= spinForTimeoutThreshold) { LockSupport.parkNanos(this, nanosTimeout); - if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) + } + if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) { break; + } nanosTimeout = deadline - System.nanoTime(); } - if (acquireQueued(node, savedState) && interruptMode != THROW_IE) + if (acquireQueued(node, savedState) && interruptMode != THROW_IE) { interruptMode = REINTERRUPT; - if (node.nextWaiter != null) + } + if (node.nextWaiter != null) { unlinkCancelledWaiters(); - if (interruptMode != 0) + } + if (interruptMode != 0) { reportInterruptAfterWait(interruptMode); + } return deadline - System.nanoTime(); } diff --git a/src/java/util/concurrent/locks/ReentrantLock.java b/src/java/util/concurrent/locks/ReentrantLock.java index c3ec2eab..b8a38467 100644 --- a/src/java/util/concurrent/locks/ReentrantLock.java +++ b/src/java/util/concurrent/locks/ReentrantLock.java @@ -496,6 +496,7 @@ public void unlock() { * * @return the Condition object */ + @Override public Condition newCondition() { return sync.newCondition(); } diff --git a/src/javax/management/monitor/Monitor.java b/src/javax/management/monitor/Monitor.java index f87eb1b3..08f2f9f6 100644 --- a/src/javax/management/monitor/Monitor.java +++ b/src/javax/management/monitor/Monitor.java @@ -1635,6 +1635,7 @@ public ThreadGroup getThreadGroup() { return group; } + @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, diff --git a/src/javax/swing/SwingWorker.java b/src/javax/swing/SwingWorker.java index 7de0edf1..cf4c05a1 100644 --- a/src/javax/swing/SwingWorker.java +++ b/src/javax/swing/SwingWorker.java @@ -763,6 +763,7 @@ private static synchronized ExecutorService getWorkersExecutorService() { new ThreadFactory() { final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); + @Override public Thread newThread(final Runnable r) { Thread thread = defaultFactory.newThread(r); diff --git a/src/sun/applet/AppletAudioClip.java b/src/sun/applet/AppletAudioClip.java new file mode 100644 index 00000000..8554e78c --- /dev/null +++ b/src/sun/applet/AppletAudioClip.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1995, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.applet.AudioClip; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import com.sun.media.sound.JavaSoundAudioClip; + + +/** + * Applet audio clip; + * + * @author Arthur van Hoff, Kara Kytle + */ + +public class AppletAudioClip implements AudioClip { + + // url that this AudioClip is based on + private URL url = null; + + // the audio clip implementation + private AudioClip audioClip = null; + + boolean DEBUG = false /*true*/; + + /** + * Constructs an AppletAudioClip from an URL. + */ + public AppletAudioClip(URL url) { + + // store the url + this.url = url; + + try { + // create a stream from the url, and use it + // in the clip. + InputStream in = url.openStream(); + createAppletAudioClip(in); + + } catch (IOException e) { + /* just quell it */ + if (DEBUG) { + System.err.println("IOException creating AppletAudioClip" + e); + } + } + } + + /** + * Constructs an AppletAudioClip from a URLConnection. + */ + public AppletAudioClip(URLConnection uc) { + + try { + // create a stream from the url, and use it + // in the clip. + createAppletAudioClip(uc.getInputStream()); + + } catch (IOException e) { + /* just quell it */ + if (DEBUG) { + System.err.println("IOException creating AppletAudioClip" + e); + } + } + } + + + /** + * For constructing directly from Jar entries, or any other + * raw Audio data. Note that the data provided must include the format + * header. + */ + public AppletAudioClip(byte [] data) { + + try { + + // construct a stream from the byte array + InputStream in = new ByteArrayInputStream(data); + + createAppletAudioClip(in); + + } catch (IOException e) { + /* just quell it */ + if (DEBUG) { + System.err.println("IOException creating AppletAudioClip " + e); + } + } + } + + + /* + * Does the real work of creating an AppletAudioClip from an InputStream. + * This function is used by both constructors. + */ + void createAppletAudioClip(InputStream in) throws IOException { + + try { + audioClip = new JavaSoundAudioClip(in); + } catch (Exception e3) { + // no matter what happened, we throw an IOException to avoid changing the interfaces.... + throw new IOException("Failed to construct the AudioClip: " + e3); + } + } + + + public synchronized void play() { + + if (audioClip != null) + audioClip.play(); + } + + + public synchronized void loop() { + + if (audioClip != null) + audioClip.loop(); + } + + public synchronized void stop() { + + if (audioClip != null) + audioClip.stop(); + } +} diff --git a/src/sun/applet/AppletClassLoader.java b/src/sun/applet/AppletClassLoader.java new file mode 100644 index 00000000..136d9c18 --- /dev/null +++ b/src/sun/applet/AppletClassLoader.java @@ -0,0 +1,871 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.NoSuchElementException; + +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import sun.misc.IOUtils; +import sun.net.www.ParseUtil; +import sun.security.util.SecurityConstants; + +/** + * This class defines the class loader for loading applet classes and + * resources. It extends URLClassLoader to search the applet code base + * for the class or resource after checking any loaded JAR files. + */ +public class AppletClassLoader extends URLClassLoader { + private URL base; /* applet code base URL */ + private CodeSource codesource; /* codesource for the base URL */ + private AccessControlContext acc; + private boolean exceptionStatus = false; + + private final Object threadGroupSynchronizer = new Object(); + private final Object grabReleaseSynchronizer = new Object(); + + private boolean codebaseLookup = true; + private volatile boolean allowRecursiveDirectoryRead = true; + + /* + * Creates a new AppletClassLoader for the specified base URL. + */ + protected AppletClassLoader(URL base) { + super(new URL[0]); + this.base = base; + this.codesource = + new CodeSource(base, (java.security.cert.Certificate[]) null); + acc = AccessController.getContext(); + } + + public void disableRecursiveDirectoryRead() { + allowRecursiveDirectoryRead = false; + } + + + /** + * Set the codebase lookup flag. + */ + void setCodebaseLookup(boolean codebaseLookup) { + this.codebaseLookup = codebaseLookup; + } + + /* + * Returns the applet code base URL. + */ + URL getBaseURL() { + return base; + } + + /* + * Returns the URLs used for loading classes and resources. + */ + public URL[] getURLs() { + URL[] jars = super.getURLs(); + URL[] urls = new URL[jars.length + 1]; + System.arraycopy(jars, 0, urls, 0, jars.length); + urls[urls.length - 1] = base; + return urls; + } + + /* + * Adds the specified JAR file to the search path of loaded JAR files. + * Changed modifier to protected in order to be able to overwrite addJar() + * in PluginClassLoader.java + */ + protected void addJar(String name) throws IOException { + URL url; + try { + url = new URL(base, name); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("name"); + } + addURL(url); + // DEBUG + //URL[] urls = getURLs(); + //for (int i = 0; i < urls.length; i++) { + // System.out.println("url[" + i + "] = " + urls[i]); + //} + } + + /* + * Override loadClass so that class loading errors can be caught in + * order to print better error messages. + */ + public synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // First check if we have permission to access the package. This + // should go away once we've added support for exported packages. + int i = name.lastIndexOf('.'); + if (i != -1) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPackageAccess(name.substring(0, i)); + } + try { + return super.loadClass(name, resolve); + } catch (ClassNotFoundException e) { + //printError(name, e.getException()); + throw e; + } catch (RuntimeException e) { + //printError(name, e); + throw e; + } catch (Error e) { + //printError(name, e); + throw e; + } + } + + /* + * Finds the applet class with the specified name. First searches + * loaded JAR files then the applet code base for the class. + */ + protected Class findClass(String name) throws ClassNotFoundException { + + int index = name.indexOf(";"); + String cookie = ""; + if(index != -1) { + cookie = name.substring(index, name.length()); + name = name.substring(0, index); + } + + // check loaded JAR files + try { + return super.findClass(name); + } catch (ClassNotFoundException e) { + } + + // Otherwise, try loading the class from the code base URL + + // 4668479: Option to turn off codebase lookup in AppletClassLoader + // during resource requests. [stanley.ho] + if (codebaseLookup == false) + throw new ClassNotFoundException(name); + +// final String path = name.replace('.', '/').concat(".class").concat(cookie); + String encodedName = ParseUtil.encodePath(name.replace('.', '/'), false); + final String path = (new StringBuffer(encodedName)).append(".class").append(cookie).toString(); + try { + byte[] b = (byte[]) AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Object run() throws IOException { + try { + URL finalURL = new URL(base, path); + + // Make sure the codebase won't be modified + if (base.getProtocol().equals(finalURL.getProtocol()) && + base.getHost().equals(finalURL.getHost()) && + base.getPort() == finalURL.getPort()) { + return getBytes(finalURL); + } + else { + return null; + } + } catch (Exception e) { + return null; + } + } + }, acc); + + if (b != null) { + return defineClass(name, b, 0, b.length, codesource); + } else { + throw new ClassNotFoundException(name); + } + } catch (PrivilegedActionException e) { + throw new ClassNotFoundException(name, e.getException()); + } + } + + /** + * Returns the permissions for the given codesource object. + * The implementation of this method first calls super.getPermissions, + * to get the permissions + * granted by the super class, and then adds additional permissions + * based on the URL of the codesource. + *

    + * If the protocol is "file" + * and the path specifies a file, permission is granted to read all files + * and (recursively) all files and subdirectories contained in + * that directory. This is so applets with a codebase of + * file:/blah/some.jar can read in file:/blah/, which is needed to + * be backward compatible. We also add permission to connect back to + * the "localhost". + * + * @param codesource the codesource + * @throws NullPointerException if {@code codesource} is {@code null}. + * @return the permissions granted to the codesource + */ + protected PermissionCollection getPermissions(CodeSource codesource) + { + final PermissionCollection perms = super.getPermissions(codesource); + + URL url = codesource.getLocation(); + + String path = null; + Permission p; + + try { + p = url.openConnection().getPermission(); + } catch (IOException ioe) { + p = null; + } + + if (p instanceof FilePermission) { + path = p.getName(); + } else if ((p == null) && (url.getProtocol().equals("file"))) { + path = url.getFile().replace('/', File.separatorChar); + path = ParseUtil.decode(path); + } + + if (path != null) { + final String rawPath = path; + if (!path.endsWith(File.separator)) { + int endIndex = path.lastIndexOf(File.separatorChar); + if (endIndex != -1) { + path = path.substring(0, endIndex + 1) + "-"; + perms.add(new FilePermission(path, + SecurityConstants.FILE_READ_ACTION)); + } + } + final File f = new File(rawPath); + final boolean isDirectory = f.isDirectory(); + // grant codebase recursive read permission + // this should only be granted to non-UNC file URL codebase and + // the codesource path must either be a directory, or a file + // that ends with .jar or .zip + if (allowRecursiveDirectoryRead && (isDirectory || + rawPath.toLowerCase().endsWith(".jar") || + rawPath.toLowerCase().endsWith(".zip"))) { + + Permission bperm; + try { + bperm = base.openConnection().getPermission(); + } catch (IOException ioe) { + bperm = null; + } + if (bperm instanceof FilePermission) { + String bpath = bperm.getName(); + if (bpath.endsWith(File.separator)) { + bpath += "-"; + } + perms.add(new FilePermission(bpath, + SecurityConstants.FILE_READ_ACTION)); + } else if ((bperm == null) && (base.getProtocol().equals("file"))) { + String bpath = base.getFile().replace('/', File.separatorChar); + bpath = ParseUtil.decode(bpath); + if (bpath.endsWith(File.separator)) { + bpath += "-"; + } + perms.add(new FilePermission(bpath, SecurityConstants.FILE_READ_ACTION)); + } + + } + } + return perms; + } + + /* + * Returns the contents of the specified URL as an array of bytes. + */ + private static byte[] getBytes(URL url) throws IOException { + URLConnection uc = url.openConnection(); + if (uc instanceof java.net.HttpURLConnection) { + java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc; + int code = huc.getResponseCode(); + if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) { + throw new IOException("open HTTP connection failed."); + } + } + int len = uc.getContentLength(); + + // Fixed #4507227: Slow performance to load + // class and resources. [stanleyh] + // + // Use buffered input stream [stanleyh] + InputStream in = new BufferedInputStream(uc.getInputStream()); + + byte[] b; + try { + b = IOUtils.readFully(in, len, true); + } finally { + in.close(); + } + return b; + } + + // Object for synchronization around getResourceAsStream() + private Object syncResourceAsStream = new Object(); + private Object syncResourceAsStreamFromJar = new Object(); + + // Flag to indicate getResourceAsStream() is in call + private boolean resourceAsStreamInCall = false; + private boolean resourceAsStreamFromJarInCall = false; + + /** + * Returns an input stream for reading the specified resource. + * + * The search order is described in the documentation for {@link + * #getResource(String)}.

    + * + * @param name the resource name + * @return an input stream for reading the resource, or null + * if the resource could not be found + * @since JDK1.1 + */ + public InputStream getResourceAsStream(String name) + { + + if (name == null) { + throw new NullPointerException("name"); + } + + try + { + InputStream is = null; + + // Fixed #4507227: Slow performance to load + // class and resources. [stanleyh] + // + // The following is used to avoid calling + // AppletClassLoader.findResource() in + // super.getResourceAsStream(). Otherwise, + // unnecessary connection will be made. + // + synchronized(syncResourceAsStream) + { + resourceAsStreamInCall = true; + + // Call super class + is = super.getResourceAsStream(name); + + resourceAsStreamInCall = false; + } + + // 4668479: Option to turn off codebase lookup in AppletClassLoader + // during resource requests. [stanley.ho] + if (codebaseLookup == true && is == null) + { + // If resource cannot be obtained, + // try to download it from codebase + URL url = new URL(base, ParseUtil.encodePath(name, false)); + is = url.openStream(); + } + + return is; + } + catch (Exception e) + { + return null; + } + } + + + /** + * Returns an input stream for reading the specified resource from the + * the loaded jar files. + * + * The search order is described in the documentation for {@link + * #getResource(String)}.

    + * + * @param name the resource name + * @return an input stream for reading the resource, or null + * if the resource could not be found + * @since JDK1.1 + */ + public InputStream getResourceAsStreamFromJar(String name) { + + if (name == null) { + throw new NullPointerException("name"); + } + + try { + InputStream is = null; + synchronized(syncResourceAsStreamFromJar) { + resourceAsStreamFromJarInCall = true; + // Call super class + is = super.getResourceAsStream(name); + resourceAsStreamFromJarInCall = false; + } + + return is; + } catch (Exception e) { + return null; + } + } + + + /* + * Finds the applet resource with the specified name. First checks + * loaded JAR files then the applet code base for the resource. + */ + public URL findResource(String name) { + // check loaded JAR files + URL url = super.findResource(name); + + // 6215746: Disable META-INF/* lookup from codebase in + // applet/plugin classloader. [stanley.ho] + if (name.startsWith("META-INF/")) + return url; + + // 4668479: Option to turn off codebase lookup in AppletClassLoader + // during resource requests. [stanley.ho] + if (codebaseLookup == false) + return url; + + if (url == null) + { + //#4805170, if it is a call from Applet.getImage() + //we should check for the image only in the archives + boolean insideGetResourceAsStreamFromJar = false; + synchronized(syncResourceAsStreamFromJar) { + insideGetResourceAsStreamFromJar = resourceAsStreamFromJarInCall; + } + + if (insideGetResourceAsStreamFromJar) { + return null; + } + + // Fixed #4507227: Slow performance to load + // class and resources. [stanleyh] + // + // Check if getResourceAsStream is called. + // + boolean insideGetResourceAsStream = false; + + synchronized(syncResourceAsStream) + { + insideGetResourceAsStream = resourceAsStreamInCall; + } + + // If getResourceAsStream is called, don't + // trigger the following code. Otherwise, + // unnecessary connection will be made. + // + if (insideGetResourceAsStream == false) + { + // otherwise, try the code base + try { + url = new URL(base, ParseUtil.encodePath(name, false)); + // check if resource exists + if(!resourceExists(url)) + url = null; + } catch (Exception e) { + // all exceptions, including security exceptions, are caught + url = null; + } + } + } + return url; + } + + + private boolean resourceExists(URL url) { + // Check if the resource exists. + // It almost works to just try to do an openConnection() but + // HttpURLConnection will return true on HTTP_BAD_REQUEST + // when the requested name ends in ".html", ".htm", and ".txt" + // and we want to be able to handle these + // + // Also, cannot just open a connection for things like FileURLConnection, + // because they succeed when connecting to a nonexistent file. + // So, in those cases we open and close an input stream. + boolean ok = true; + try { + URLConnection conn = url.openConnection(); + if (conn instanceof java.net.HttpURLConnection) { + java.net.HttpURLConnection hconn = + (java.net.HttpURLConnection) conn; + + // To reduce overhead, using http HEAD method instead of GET method + hconn.setRequestMethod("HEAD"); + + int code = hconn.getResponseCode(); + if (code == java.net.HttpURLConnection.HTTP_OK) { + return true; + } + if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) { + return false; + } + } else { + /** + * Fix for #4182052 - stanleyh + * + * The same connection should be reused to avoid multiple + * HTTP connections + */ + + // our best guess for the other cases + InputStream is = conn.getInputStream(); + is.close(); + } + } catch (Exception ex) { + ok = false; + } + return ok; + } + + /* + * Returns an enumeration of all the applet resources with the specified + * name. First checks loaded JAR files then the applet code base for all + * available resources. + */ + public Enumeration findResources(String name) throws IOException { + + final Enumeration e = super.findResources(name); + + // 6215746: Disable META-INF/* lookup from codebase in + // applet/plugin classloader. [stanley.ho] + if (name.startsWith("META-INF/")) + return e; + + // 4668479: Option to turn off codebase lookup in AppletClassLoader + // during resource requests. [stanley.ho] + if (codebaseLookup == false) + return e; + + URL u = new URL(base, ParseUtil.encodePath(name, false)); + if (!resourceExists(u)) { + u = null; + } + + final URL url = u; + return new Enumeration() { + private boolean done; + public Object nextElement() { + if (!done) { + if (e.hasMoreElements()) { + return e.nextElement(); + } + done = true; + if (url != null) { + return url; + } + } + throw new NoSuchElementException(); + } + public boolean hasMoreElements() { + return !done && (e.hasMoreElements() || url != null); + } + }; + } + + /* + * Load and resolve the file specified by the applet tag CODE + * attribute. The argument can either be the relative path + * of the class file itself or just the name of the class. + */ + Class loadCode(String name) throws ClassNotFoundException { + // first convert any '/' or native file separator to . + name = name.replace('/', '.'); + name = name.replace(File.separatorChar, '.'); + + // deal with URL rewriting + String cookie = null; + int index = name.indexOf(";"); + if(index != -1) { + cookie = name.substring(index, name.length()); + name = name.substring(0, index); + } + + // save that name for later + String fullName = name; + // then strip off any suffixes + if (name.endsWith(".class") || name.endsWith(".java")) { + name = name.substring(0, name.lastIndexOf('.')); + } + try { + if(cookie != null) + name = (new StringBuffer(name)).append(cookie).toString(); + return loadClass(name); + } catch (ClassNotFoundException e) { + } + // then if it didn't end with .java or .class, or in the + // really pathological case of a class named class or java + if(cookie != null) + fullName = (new StringBuffer(fullName)).append(cookie).toString(); + + return loadClass(fullName); + } + + /* + * The threadgroup that the applets loaded by this classloader live + * in. In the sun.* implementation of applets, the security manager's + * (AppletSecurity) getThreadGroup returns the thread group of the + * first applet on the stack, which is the applet's thread group. + */ + private AppletThreadGroup threadGroup; + private AppContext appContext; + + public ThreadGroup getThreadGroup() { + synchronized (threadGroupSynchronizer) { + if (threadGroup == null || threadGroup.isDestroyed()) { + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + threadGroup = new AppletThreadGroup(base + "-threadGroup"); + // threadGroup.setDaemon(true); + // threadGroup is now destroyed by AppContext.dispose() + + // Create the new AppContext from within a Thread belonging + // to the newly created ThreadGroup, and wait for the + // creation to complete before returning from this method. + AppContextCreator creatorThread = new AppContextCreator(threadGroup); + + // Since this thread will later be used to launch the + // applet's AWT-event dispatch thread and we want the applet + // code executing the AWT callbacks to use their own class + // loader rather than the system class loader, explicitly + // set the context class loader to the AppletClassLoader. + creatorThread.setContextClassLoader(AppletClassLoader.this); + + creatorThread.start(); + try { + synchronized(creatorThread.syncObject) { + while (!creatorThread.created) { + creatorThread.syncObject.wait(); + } + } + } catch (InterruptedException e) { } + appContext = creatorThread.appContext; + return null; + } + }); + } + return threadGroup; + } + } + + /* + * Get the AppContext, if any, corresponding to this AppletClassLoader. + */ + public AppContext getAppContext() { + return appContext; + } + + int usageCount = 0; + + /** + * Grab this AppletClassLoader and its ThreadGroup/AppContext, so they + * won't be destroyed. + */ +public void grab() { + synchronized(grabReleaseSynchronizer) { + usageCount++; + } + getThreadGroup(); // Make sure ThreadGroup/AppContext exist + } + + protected void setExceptionStatus() + { + exceptionStatus = true; + } + + public boolean getExceptionStatus() + { + return exceptionStatus; + } + + /** + * Release this AppletClassLoader and its ThreadGroup/AppContext. + * If nothing else has grabbed this AppletClassLoader, its ThreadGroup + * and AppContext will be destroyed. + * + * Because this method may destroy the AppletClassLoader's ThreadGroup, + * this method should NOT be called from within the AppletClassLoader's + * ThreadGroup. + * + * Changed modifier to protected in order to be able to overwrite this + * function in PluginClassLoader.java + */ + protected void release() { + + AppContext tempAppContext = null; + + synchronized(grabReleaseSynchronizer) { + if (usageCount > 1) { + --usageCount; + } else { + synchronized(threadGroupSynchronizer) { + tempAppContext = resetAppContext(); + } + } + } + + // Dispose appContext outside any sync block to + // prevent potential deadlock. + if (tempAppContext != null) { + try { + tempAppContext.dispose(); // nuke the world! + } catch (IllegalThreadStateException e) { } + } + } + + /* + * reset classloader's AppContext and ThreadGroup + * This method is for subclass PluginClassLoader to + * reset superclass's AppContext and ThreadGroup but do + * not dispose the AppContext. PluginClassLoader does not + * use UsageCount to decide whether to dispose AppContext + * + * @return previous AppContext + */ + protected AppContext resetAppContext() { + AppContext tempAppContext = null; + + synchronized(threadGroupSynchronizer) { + // Store app context in temp variable + tempAppContext = appContext; + usageCount = 0; + appContext = null; + threadGroup = null; + } + return tempAppContext; + } + + + // Hash map to store applet compatibility info + private HashMap jdk11AppletInfo = new HashMap(); + private HashMap jdk12AppletInfo = new HashMap(); + + /** + * Set applet target level as JDK 1.1. + * + * @param clazz Applet class. + * @param bool true if JDK is targeted for JDK 1.1; + * false otherwise. + */ + void setJDK11Target(Class clazz, boolean bool) + { + jdk11AppletInfo.put(clazz.toString(), Boolean.valueOf(bool)); + } + + /** + * Set applet target level as JDK 1.2. + * + * @param clazz Applet class. + * @param bool true if JDK is targeted for JDK 1.2; + * false otherwise. + */ + void setJDK12Target(Class clazz, boolean bool) + { + jdk12AppletInfo.put(clazz.toString(), Boolean.valueOf(bool)); + } + + /** + * Determine if applet is targeted for JDK 1.1. + * + * @param applet Applet class. + * @return TRUE if applet is targeted for JDK 1.1; + * FALSE if applet is not; + * null if applet is unknown. + */ + Boolean isJDK11Target(Class clazz) + { + return (Boolean) jdk11AppletInfo.get(clazz.toString()); + } + + /** + * Determine if applet is targeted for JDK 1.2. + * + * @param applet Applet class. + * @return TRUE if applet is targeted for JDK 1.2; + * FALSE if applet is not; + * null if applet is unknown. + */ + Boolean isJDK12Target(Class clazz) + { + return (Boolean) jdk12AppletInfo.get(clazz.toString()); + } + + private static AppletMessageHandler mh = + new AppletMessageHandler("appletclassloader"); + + /* + * Prints a class loading error message. + */ + private static void printError(String name, Throwable e) { + String s = null; + if (e == null) { + s = mh.getMessage("filenotfound", name); + } else if (e instanceof IOException) { + s = mh.getMessage("fileioexception", name); + } else if (e instanceof ClassFormatError) { + s = mh.getMessage("fileformat", name); + } else if (e instanceof ThreadDeath) { + s = mh.getMessage("filedeath", name); + } else if (e instanceof Error) { + s = mh.getMessage("fileerror", e.toString(), name); + } + if (s != null) { + System.err.println(s); + } + } +} + +/* + * The AppContextCreator class is used to create an AppContext from within + * a Thread belonging to the new AppContext's ThreadGroup. To wait for + * this operation to complete before continuing, wait for the notifyAll() + * operation on the syncObject to occur. + */ +class AppContextCreator extends Thread { + Object syncObject = new Object(); + AppContext appContext = null; + volatile boolean created = false; + + AppContextCreator(ThreadGroup group) { + super(group, "AppContextCreator"); + } + + public void run() { + appContext = SunToolkit.createNewAppContext(); + created = true; + synchronized(syncObject) { + syncObject.notifyAll(); + } + } // run() + +} // class AppContextCreator diff --git a/src/sun/applet/AppletEvent.java b/src/sun/applet/AppletEvent.java new file mode 100644 index 00000000..6095a01e --- /dev/null +++ b/src/sun/applet/AppletEvent.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.util.EventObject; + + +/** + * AppletEvent class. + * + * @author Sunita Mani + */ + +public class AppletEvent extends EventObject { + + private Object arg; + private int id; + + + public AppletEvent(Object source, int id, Object argument) { + super(source); + this.arg = argument; + this.id = id; + } + + public int getID() { + return id; + } + + public Object getArgument() { + return arg; + } + + public String toString() { + String str = getClass().getName() + "[source=" + source + " + id="+ id; + if (arg != null) { + str += " + arg=" + arg; + } + str += " ]"; + return str; + } +} diff --git a/src/sun/applet/AppletEventMulticaster.java b/src/sun/applet/AppletEventMulticaster.java new file mode 100644 index 00000000..1df4c7e2 --- /dev/null +++ b/src/sun/applet/AppletEventMulticaster.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +/** + * AppletEventMulticaster class. This class manages an immutable + * structure consisting of a chain of AppletListeners and is + * responsible for dispatching events to them. + * + * @author Sunita Mani + */ +public class AppletEventMulticaster implements AppletListener { + + private final AppletListener a, b; + + public AppletEventMulticaster(AppletListener a, AppletListener b) { + this.a = a; this.b = b; + } + + public void appletStateChanged(AppletEvent e) { + a.appletStateChanged(e); + b.appletStateChanged(e); + } + + /** + * Adds Applet-listener-a with Applet-listener-b and + * returns the resulting multicast listener. + * @param a Applet-listener-a + * @param b Applet-listener-b + */ + public static AppletListener add(AppletListener a, AppletListener b) { + return addInternal(a, b); + } + + /** + * Removes the old Applet-listener from Applet-listener-l and + * returns the resulting multicast listener. + * @param l Applet-listener-l + * @param oldl the Applet-listener being removed + */ + public static AppletListener remove(AppletListener l, AppletListener oldl) { + return removeInternal(l, oldl); + } + + /** + * Returns the resulting multicast listener from adding listener-a + * and listener-b together. + * If listener-a is null, it returns listener-b; + * If listener-b is null, it returns listener-a + * If neither are null, then it creates and returns + * a new AppletEventMulticaster instance which chains a with b. + * @param a event listener-a + * @param b event listener-b + */ + private static AppletListener addInternal(AppletListener a, AppletListener b) { + if (a == null) return b; + if (b == null) return a; + return new AppletEventMulticaster(a, b); + } + + + /** + * Removes a listener from this multicaster and returns the + * resulting multicast listener. + * @param oldl the listener to be removed + */ + protected AppletListener remove(AppletListener oldl) { + if (oldl == a) return b; + if (oldl == b) return a; + AppletListener a2 = removeInternal(a, oldl); + AppletListener b2 = removeInternal(b, oldl); + if (a2 == a && b2 == b) { + return this; // it's not here + } + return addInternal(a2, b2); + } + + + /** + * Returns the resulting multicast listener after removing the + * old listener from listener-l. + * If listener-l equals the old listener OR listener-l is null, + * returns null. + * Else if listener-l is an instance of AppletEventMulticaster + * then it removes the old listener from it. + * Else, returns listener l. + * @param l the listener being removed from + * @param oldl the listener being removed + */ + private static AppletListener removeInternal(AppletListener l, AppletListener oldl) { + if (l == oldl || l == null) { + return null; + } else if (l instanceof AppletEventMulticaster) { + return ((AppletEventMulticaster)l).remove(oldl); + } else { + return l; // it's not here + } + } +} diff --git a/src/sun/applet/AppletIOException.java b/src/sun/applet/AppletIOException.java new file mode 100644 index 00000000..3034678b --- /dev/null +++ b/src/sun/applet/AppletIOException.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.io.IOException; + +/** + * An applet IO exception. + * + * @author Koji Uno + */ +public +class AppletIOException extends IOException { + private String key = null; + private Object msgobj = null; + + public AppletIOException(String key) { + super(key); + this.key = key; + + } + public AppletIOException(String key, Object arg) { + this(key); + msgobj = arg; + } + + public String getLocalizedMessage() { + if( msgobj != null) + return amh.getMessage(key, msgobj); + else + return amh.getMessage(key); + } + + private static AppletMessageHandler amh = new AppletMessageHandler("appletioexception"); + +} diff --git a/src/sun/applet/AppletIllegalArgumentException.java b/src/sun/applet/AppletIllegalArgumentException.java new file mode 100644 index 00000000..3203a7cc --- /dev/null +++ b/src/sun/applet/AppletIllegalArgumentException.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +/** + * An applet security exception. + * + * @author Arthur van Hoff + */ +public +class AppletIllegalArgumentException extends IllegalArgumentException { + private String key = null; + + public AppletIllegalArgumentException(String key) { + super(key); + this.key = key; + + } + + public String getLocalizedMessage() { + return amh.getMessage(key); + } + + private static AppletMessageHandler amh = new AppletMessageHandler("appletillegalargumentexception"); +} diff --git a/src/sun/applet/AppletImageRef.java b/src/sun/applet/AppletImageRef.java new file mode 100644 index 00000000..4756d413 --- /dev/null +++ b/src/sun/applet/AppletImageRef.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.awt.*; +import java.net.URL; + +import sun.awt.image.URLImageSource; + +class AppletImageRef extends sun.misc.Ref { + URL url; + + /** + * Create the Ref + */ + AppletImageRef(URL url) { + this.url = url; + } + + public void flush() { + super.flush(); + } + + /** + * Reconsitute the image. Only called when the ref has been flushed. + */ + public Object reconstitute() { + Image img = Toolkit.getDefaultToolkit().createImage(new URLImageSource(url)); + return img; + } +} diff --git a/src/sun/applet/AppletListener.java b/src/sun/applet/AppletListener.java new file mode 100644 index 00000000..00664938 --- /dev/null +++ b/src/sun/applet/AppletListener.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.util.EventListener; + +/** + * Applet Listener interface. This interface is to be implemented + * by objects interested in AppletEvents. + * + * @author Sunita Mani + */ + +public interface AppletListener extends EventListener { + public void appletStateChanged(AppletEvent e); +} diff --git a/src/sun/applet/AppletMessageHandler.java b/src/sun/applet/AppletMessageHandler.java new file mode 100644 index 00000000..e9c225c5 --- /dev/null +++ b/src/sun/applet/AppletMessageHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * An hanlder of localized messages. + * + * @author Koji Uno + */ +class AppletMessageHandler { + private static ResourceBundle rb; + private String baseKey = null; + + static { + try { + rb = ResourceBundle.getBundle + ("sun.applet.resources.MsgAppletViewer"); + } catch (MissingResourceException e) { + System.out.println(e.getMessage()); + System.exit(1); + } + } + + AppletMessageHandler(String baseKey) { + this.baseKey = baseKey; + } + + String getMessage(String key) { + return (String)rb.getString(getQualifiedKey(key)); + } + + String getMessage(String key, Object arg){ + String basemsgfmt = (String)rb.getString(getQualifiedKey(key)); + MessageFormat msgfmt = new MessageFormat(basemsgfmt); + Object msgobj[] = new Object[1]; + if (arg == null) { + arg = "null"; // mimic java.io.PrintStream.print(String) + } + msgobj[0] = arg; + return msgfmt.format(msgobj); + } + + String getMessage(String key, Object arg1, Object arg2) { + String basemsgfmt = (String)rb.getString(getQualifiedKey(key)); + MessageFormat msgfmt = new MessageFormat(basemsgfmt); + Object msgobj[] = new Object[2]; + if (arg1 == null) { + arg1 = "null"; + } + if (arg2 == null) { + arg2 = "null"; + } + msgobj[0] = arg1; + msgobj[1] = arg2; + return msgfmt.format(msgobj); + } + + String getMessage(String key, Object arg1, Object arg2, Object arg3) { + String basemsgfmt = (String)rb.getString(getQualifiedKey(key)); + MessageFormat msgfmt = new MessageFormat(basemsgfmt); + Object msgobj[] = new Object[3]; + if (arg1 == null) { + arg1 = "null"; + } + if (arg2 == null) { + arg2 = "null"; + } + if (arg3 == null) { + arg3 = "null"; + } + msgobj[0] = arg1; + msgobj[1] = arg2; + msgobj[2] = arg3; + return msgfmt.format(msgobj); + } + + String getMessage(String key, Object arg[]) { + String basemsgfmt = (String)rb.getString(getQualifiedKey(key)); + MessageFormat msgfmt = new MessageFormat(basemsgfmt); + return msgfmt.format(arg); + } + + String getQualifiedKey(String subKey) { + return baseKey + "." + subKey; + } +} diff --git a/src/sun/applet/AppletObjectInputStream.java b/src/sun/applet/AppletObjectInputStream.java new file mode 100644 index 00000000..49330805 --- /dev/null +++ b/src/sun/applet/AppletObjectInputStream.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + * COPYRIGHT goes here + */ + +package sun.applet; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.io.StreamCorruptedException; +import java.lang.reflect.Array; + +/** + * This subclass of ObjectInputStream delegates loading of classes to + * an existing ClassLoader. + */ + +class AppletObjectInputStream extends ObjectInputStream +{ + private AppletClassLoader loader; + + /** + * Loader must be non-null; + */ + + public AppletObjectInputStream(InputStream in, AppletClassLoader loader) + throws IOException, StreamCorruptedException { + + super(in); + if (loader == null) { + throw new AppletIllegalArgumentException("appletillegalargumentexception.objectinputstream"); + + } + this.loader = loader; + } + + /** + * Make a primitive array class + */ + + private Class primitiveType(char type) { + switch (type) { + case 'B': return byte.class; + case 'C': return char.class; + case 'D': return double.class; + case 'F': return float.class; + case 'I': return int.class; + case 'J': return long.class; + case 'S': return short.class; + case 'Z': return boolean.class; + default: return null; + } + } + + /** + * Use the given ClassLoader rather than using the system class + */ + protected Class resolveClass(ObjectStreamClass classDesc) + throws IOException, ClassNotFoundException { + + String cname = classDesc.getName(); + if (cname.startsWith("[")) { + // An array + Class component; // component class + int dcount; // dimension + for (dcount=1; cname.charAt(dcount)=='['; dcount++) ; + if (cname.charAt(dcount) == 'L') { + component = loader.loadClass(cname.substring(dcount+1, + cname.length()-1)); + } else { + if (cname.length() != dcount+1) { + throw new ClassNotFoundException(cname);// malformed + } + component = primitiveType(cname.charAt(dcount)); + } + int dim[] = new int[dcount]; + for (int i=0; i APPLET_LOAD -- (applet init called)--> APPLET_INIT -- ( + * applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet + * destroyed called) --> APPLET_DESTROY -->(applet gets disposed) --> + * APPLET_DISPOSE -->.... + * + * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays + * in the APPLET_START state unless the applet goes away(refresh page or leave the page). + * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet + * is revisited, it will call applet start method and enter the APPLET_START state and stay there. + * + * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle + * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP + * state and then applet destroyed method gets called and enters APPLET_DESTROY state. + * + * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from + * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT . + * + * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case). + * Same as APPLET_LOAD to + * APPLET_DISPOSE since all of this are triggered by browser. + * + * + */ + @Override + public void run() { + + Thread curThread = Thread.currentThread(); + if (curThread == loaderThread) { + // if we are in the loader thread, cause + // loading to occur. We may exit this with + // status being APPLET_DISPOSE, APPLET_ERROR, + // or APPLET_LOAD + runLoader(); + return; + } + + boolean disposed = false; + while (!disposed && !curThread.isInterrupted()) { + AppletEvent evt; + try { + evt = getNextEvent(); + } catch (InterruptedException e) { + showAppletStatus("bail"); + return; + } + + //showAppletStatus("EVENT = " + evt.getID()); + try { + switch (evt.getID()) { + case APPLET_LOAD: + if (!okToLoad()) { + break; + } + // This complexity allows loading of applets to be + // interruptable. The actual thread loading runs + // in a separate thread, so it can be interrupted + // without harming the applet thread. + // So that we don't have to worry about + // concurrency issues, the main applet thread waits + // until the loader thread terminates. + // (one way or another). + if (loaderThread == null) { + // REMIND: do we want a name? + //System.out.println("------------------- loading applet"); + setLoaderThread(new Thread(this)); + loaderThread.start(); + // we get to go to sleep while this runs + loaderThread.join(); + setLoaderThread(null); + } else { + // REMIND: issue an error -- this case should never + // occur. + } + break; + + case APPLET_INIT: + // AppletViewer "Restart" will jump from destroy method to + // init, that is why we need to check status w/ APPLET_DESTROY + if (status != APPLET_LOAD && status != APPLET_DESTROY) { + showAppletStatus("notloaded"); + break; + } + applet.resize(defaultAppletSize); + if (doInit) { + if (PerformanceLogger.loggingEnabled()) { + PerformanceLogger.setTime("Applet Init"); + PerformanceLogger.outputLog(); + } + applet.init(); + } + + //Need the default(fallback) font to be created in this AppContext + Font f = getFont(); + if (f == null || + "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) && + f.getSize() == 12 && f.getStyle() == Font.PLAIN) { + setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); + } + + doInit = true; // allow restarts + + // Validate the applet in event dispatch thread + // to avoid deadlock. + try { + final AppletPanel p = this; + Runnable r = new Runnable() { + @Override + public void run() { + p.validate(); + } + }; + AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); + } + catch(InterruptedException ie) { + } + catch(InvocationTargetException ite) { + } + + status = APPLET_INIT; + showAppletStatus("inited"); + break; + + case APPLET_START: + { + if (status != APPLET_INIT && status != APPLET_STOP) { + showAppletStatus("notinited"); + break; + } + applet.resize(currentAppletSize); + applet.start(); + + // Validate and show the applet in event dispatch thread + // to avoid deadlock. + try { + final AppletPanel p = this; + final Applet a = applet; + Runnable r = new Runnable() { + @Override + public void run() { + p.validate(); + a.setVisible(true); + + // Fix for BugTraq ID 4041703. + // Set the default focus for an applet. + if (hasInitialFocus()) { + setDefaultFocus(); + } + } + }; + AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); + } + catch(InterruptedException ie) { + } + catch(InvocationTargetException ite) { + } + + status = APPLET_START; + showAppletStatus("started"); + break; + } + + case APPLET_STOP: + if (status != APPLET_START) { + showAppletStatus("notstarted"); + break; + } + status = APPLET_STOP; + + // Hide the applet in event dispatch thread + // to avoid deadlock. + try { + final Applet a = applet; + Runnable r = new Runnable() { + @Override + public void run() { + a.setVisible(false); + } + }; + AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); + } + catch(InterruptedException ie) { + } + catch(InvocationTargetException ite) { + } + + + // During Applet.stop(), any AccessControlException on an involved Class remains in + // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is + // reused, the same exception will occur during class loading. Set the AppletClassLoader's + // exceptionStatusSet flag to allow recognition of what had happened + // when reusing AppletClassLoader object. + try { + applet.stop(); + } catch (AccessControlException e) { + setExceptionStatus(e); + // rethrow exception to be handled as it normally would be. + throw e; + } + showAppletStatus("stopped"); + break; + + case APPLET_DESTROY: + if (status != APPLET_STOP && status != APPLET_INIT) { + showAppletStatus("notstopped"); + break; + } + status = APPLET_DESTROY; + + // During Applet.destroy(), any AccessControlException on an involved Class remains in + // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is + // reused, the same exception will occur during class loading. Set the AppletClassLoader's + // exceptionStatusSet flag to allow recognition of what had happened + // when reusing AppletClassLoader object. + try { + applet.destroy(); + } catch (AccessControlException e) { + setExceptionStatus(e); + // rethrow exception to be handled as it normally would be. + throw e; + } + showAppletStatus("destroyed"); + break; + + case APPLET_DISPOSE: + if (status != APPLET_DESTROY && status != APPLET_LOAD) { + showAppletStatus("notdestroyed"); + break; + } + status = APPLET_DISPOSE; + + try { + final Applet a = applet; + Runnable r = new Runnable() { + @Override + public void run() { + remove(a); + } + }; + AWTAccessor.getEventQueueAccessor().invokeAndWait(applet, r); + } + catch(InterruptedException ie) + { + } + catch(InvocationTargetException ite) + { + } + applet = null; + showAppletStatus("disposed"); + disposed = true; + break; + + case APPLET_QUIT: + return; + } + } catch (Exception e) { + status = APPLET_ERROR; + if (e.getMessage() != null) { + showAppletStatus("exception2", e.getClass().getName(), + e.getMessage()); + } else { + showAppletStatus("exception", e.getClass().getName()); + } + showAppletException(e); + } catch (ThreadDeath e) { + showAppletStatus("death"); + return; + } catch (Error e) { + status = APPLET_ERROR; + if (e.getMessage() != null) { + showAppletStatus("error2", e.getClass().getName(), + e.getMessage()); + } else { + showAppletStatus("error", e.getClass().getName()); + } + showAppletException(e); + } + clearLoadAbortRequest(); + } + } + + /** + * Gets most recent focus owner component associated with the given window. + * It does that without calling Window.getMostRecentFocusOwner since it + * provides its own logic contradicting with setDefautlFocus. Instead, it + * calls KeyboardFocusManager directly. + */ + private Component getMostRecentFocusOwnerForWindow(Window w) { + Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + Method meth = null; + try { + meth = KeyboardFocusManager.class.getDeclaredMethod( + "getMostRecentFocusOwner", + new Class[]{Window.class}); + meth.setAccessible(true); + } catch (Exception e) { + // Must never happen + e.printStackTrace(); + } + return meth; + } + }); + if (meth != null) { + // Meth refers static method + try { + return (Component)meth.invoke(null, new Object[] {w}); + } catch (Exception e) { + // Must never happen + e.printStackTrace(); + } + } + // Will get here if exception was thrown or meth is null + return w.getMostRecentFocusOwner(); + } + + /* + * Fix for BugTraq ID 4041703. + * Set the focus to a reasonable default for an Applet. + */ + private void setDefaultFocus() { + Component toFocus = null; + Container parent = getParent(); + + if(parent != null) { + if (parent instanceof Window) { + toFocus = getMostRecentFocusOwnerForWindow((Window)parent); + if (toFocus == parent || toFocus == null) { + toFocus = parent.getFocusTraversalPolicy(). + getInitialComponent((Window)parent); + } + } else if (parent.isFocusCycleRoot()) { + toFocus = parent.getFocusTraversalPolicy(). + getDefaultComponent(parent); + } + } + + if (toFocus != null) { + if (parent instanceof EmbeddedFrame) { + ((EmbeddedFrame)parent).synthesizeWindowActivation(true); + } + // EmbeddedFrame might have focus before the applet was added. + // Thus after its activation the most recent focus owner will be + // restored. We need the applet's initial focusabled component to + // be focused here. + toFocus.requestFocusInWindow(); + } + } + + /** + * Load the applet into memory. + * Runs in a seperate (and interruptible) thread from the rest of the + * applet event processing so that it can be gracefully interrupted from + * things like HotJava. + */ + private void runLoader() { + if (status != APPLET_DISPOSE) { + showAppletStatus("notdisposed"); + return; + } + + dispatchAppletEvent(APPLET_LOADING, null); + + // REMIND -- might be cool to visually indicate loading here -- + // maybe do animation? + status = APPLET_LOAD; + + // Create a class loader + loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); + + // Load the archives if present. + // REMIND - this probably should be done in a separate thread, + // or at least the additional archives (epll). + + String code = getCode(); + + // setup applet AppContext + // this must be called before loadJarFiles + setupAppletAppContext(); + + try { + loadJarFiles(loader); + applet = createApplet(loader); + } catch (ClassNotFoundException e) { + status = APPLET_ERROR; + showAppletStatus("notfound", code); + showAppletLog("notfound", code); + showAppletException(e); + return; + } catch (InstantiationException e) { + status = APPLET_ERROR; + showAppletStatus("nocreate", code); + showAppletLog("nocreate", code); + showAppletException(e); + return; + } catch (IllegalAccessException e) { + status = APPLET_ERROR; + showAppletStatus("noconstruct", code); + showAppletLog("noconstruct", code); + showAppletException(e); + // sbb -- I added a return here + return; + } catch (Exception e) { + status = APPLET_ERROR; + showAppletStatus("exception", e.getMessage()); + showAppletException(e); + return; + } catch (ThreadDeath e) { + status = APPLET_ERROR; + showAppletStatus("death"); + return; + } catch (Error e) { + status = APPLET_ERROR; + showAppletStatus("error", e.getMessage()); + showAppletException(e); + return; + } finally { + // notify that loading is no longer going on + dispatchAppletEvent(APPLET_LOADING_COMPLETED, null); + } + + // Fixed #4508194: NullPointerException thrown during + // quick page switch + // + if (applet != null) + { + // Stick it in the frame + applet.setStub(this); + applet.hide(); + add("Center", applet); + showAppletStatus("loaded"); + validate(); + } + } + + protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException, + IllegalAccessException, IOException, InstantiationException, InterruptedException { + final String serName = getSerializedObject(); + String code = getCode(); + + if (code != null && serName != null) { + System.err.println(amh.getMessage("runloader.err")); +// return null; + throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both."); + } + if (code == null && serName == null) { + String msg = "nocode"; + status = APPLET_ERROR; + showAppletStatus(msg); + showAppletLog(msg); + repaint(); + } + if (code != null) { + applet = (Applet)loader.loadCode(code).newInstance(); + doInit = true; + } else { + // serName is not null; + try (InputStream is = AccessController.doPrivileged( + (PrivilegedAction)() -> loader.getResourceAsStream(serName)); + ObjectInputStream ois = new AppletObjectInputStream(is, loader)) { + + applet = (Applet) ois.readObject(); + doInit = false; // skip over the first init + } + } + + // Determine the JDK level that the applet targets. + // This is critical for enabling certain backward + // compatibility switch if an applet is a JDK 1.1 + // applet. [stanley.ho] + findAppletJDKLevel(applet); + + if (Thread.interrupted()) { + try { + status = APPLET_DISPOSE; // APPLET_ERROR? + applet = null; + // REMIND: This may not be exactly the right thing: the + // status is set by the stop button and not necessarily + // here. + showAppletStatus("death"); + } finally { + Thread.currentThread().interrupt(); // resignal interrupt + } + return null; + } + return applet; + } + + protected void loadJarFiles(AppletClassLoader loader) throws IOException, + InterruptedException { + // Load the archives if present. + // REMIND - this probably should be done in a separate thread, + // or at least the additional archives (epll). + String jarFiles = getJarFiles(); + + if (jarFiles != null) { + StringTokenizer st = new StringTokenizer(jarFiles, ",", false); + while(st.hasMoreTokens()) { + String tok = st.nextToken().trim(); + try { + loader.addJar(tok); + } catch (IllegalArgumentException e) { + // bad archive name + continue; + } + } + } + } + + /** + * Request that the loading of the applet be stopped. + */ + protected synchronized void stopLoading() { + // REMIND: fill in the body + if (loaderThread != null) { + //System.out.println("Interrupting applet loader thread: " + loaderThread); + loaderThread.interrupt(); + } else { + setLoadAbortRequest(); + } + } + + + protected synchronized boolean okToLoad() { + return !loadAbortRequest; + } + + protected synchronized void clearLoadAbortRequest() { + loadAbortRequest = false; + } + + protected synchronized void setLoadAbortRequest() { + loadAbortRequest = true; + } + + + private synchronized void setLoaderThread(Thread loaderThread) { + this.loaderThread = loaderThread; + } + + /** + * Return true when the applet has been started. + */ + @Override + public boolean isActive() { + return status == APPLET_START; + } + + + private EventQueue appEvtQ = null; + /** + * Is called when the applet wants to be resized. + */ + @Override + public void appletResize(int width, int height) { + currentAppletSize.width = width; + currentAppletSize.height = height; + final Dimension currentSize = new Dimension(currentAppletSize.width, + currentAppletSize.height); + + if(loader != null) { + AppContext appCtxt = loader.getAppContext(); + if(appCtxt != null) + appEvtQ = (EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY); + } + + final AppletPanel ap = this; + if (appEvtQ != null){ + appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), + new Runnable() { + @Override + public void run() { + if (ap != null) { + ap.dispatchAppletEvent( + APPLET_RESIZE, + currentSize); + } + } + })); + } + } + + @Override + public void setBounds(int x, int y, int width, int height) { + super.setBounds(x, y, width, height); + currentAppletSize.width = width; + currentAppletSize.height = height; + } + + public Applet getApplet() { + return applet; + } + + /** + * Status line. Called by the AppletPanel to provide + * feedback on the Applet's state. + */ + protected void showAppletStatus(String status) { + getAppletContext().showStatus(amh.getMessage(status)); + } + + protected void showAppletStatus(String status, Object arg) { + getAppletContext().showStatus(amh.getMessage(status, arg)); + } + protected void showAppletStatus(String status, Object arg1, Object arg2) { + getAppletContext().showStatus(amh.getMessage(status, arg1, arg2)); + } + + /** + * Called by the AppletPanel to print to the log. + */ + protected void showAppletLog(String msg) { + System.out.println(amh.getMessage(msg)); + } + + protected void showAppletLog(String msg, Object arg) { + System.out.println(amh.getMessage(msg, arg)); + } + + /** + * Called by the AppletPanel to provide + * feedback when an exception has happened. + */ + protected void showAppletException(Throwable t) { + t.printStackTrace(); + repaint(); + } + + /** + * Get caching key for classloader cache + */ + public String getClassLoaderCacheKey() + { + /** + * Fixed #4501142: Classloader sharing policy doesn't + * take "archive" into account. This will be overridden + * by Java Plug-in. [stanleyh] + */ + return getCodeBase().toString(); + } + + /** + * The class loaders + */ + private static HashMap classloaders = new HashMap(); + + /** + * Flush a class loader. + */ + public static synchronized void flushClassLoader(String key) { + classloaders.remove(key); + } + + /** + * Flush all class loaders. + */ + public static synchronized void flushClassLoaders() { + classloaders = new HashMap(); + } + + /** + * This method actually creates an AppletClassLoader. + * + * It can be override by subclasses (such as the Plug-in) + * to provide different classloaders. + */ + protected AppletClassLoader createClassLoader(final URL codebase) { + return new AppletClassLoader(codebase); + } + + /** + * Get a class loader. Create in a restricted context + */ + synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) { + AppletClassLoader c = (AppletClassLoader)classloaders.get(key); + if (c == null) { + AccessControlContext acc = + getAccessControlContext(codebase); + c = (AppletClassLoader) + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + AppletClassLoader ac = createClassLoader(codebase); + /* Should the creation of the classloader be + * within the class synchronized block? Since + * this class is used by the plugin, take care + * to avoid deadlocks, or specialize + * AppletPanel within the plugin. It may take + * an arbitrary amount of time to create a + * class loader (involving getting Jar files + * etc.) and may block unrelated applets from + * finishing createAppletThread (due to the + * class synchronization). If + * createAppletThread does not finish quickly, + * the applet cannot process other messages, + * particularly messages such as destroy + * (which timeout when called from the browser). + */ + synchronized (getClass()) { + AppletClassLoader res = + (AppletClassLoader)classloaders.get(key); + if (res == null) { + classloaders.put(key, ac); + return ac; + } else { + return res; + } + } + } + },acc); + } + return c; + } + + /** + * get the context for the AppletClassLoader we are creating. + * the context is granted permission to create the class loader, + * connnect to the codebase, and whatever else the policy grants + * to all codebases. + */ + private AccessControlContext getAccessControlContext(final URL codebase) { + + PermissionCollection perms = (PermissionCollection) + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + Policy p = Policy.getPolicy(); + if (p != null) { + return p.getPermissions(new CodeSource(null, + (java.security.cert.Certificate[]) null)); + } else { + return null; + } + } + }); + + if (perms == null) + perms = new Permissions(); + + //XXX: this is needed to be able to create the classloader itself! + + perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); + + Permission p; + java.net.URLConnection urlConnection = null; + try { + urlConnection = codebase.openConnection(); + p = urlConnection.getPermission(); + } catch (IOException ioe) { + p = null; + } + + if (p != null) + perms.add(p); + + if (p instanceof FilePermission) { + + String path = p.getName(); + + int endIndex = path.lastIndexOf(File.separatorChar); + + if (endIndex != -1) { + path = path.substring(0, endIndex+1); + + if (path.endsWith(File.separator)) { + path += "-"; + } + perms.add(new FilePermission(path, + SecurityConstants.FILE_READ_ACTION)); + } + } else { + URL locUrl = codebase; + if (urlConnection instanceof JarURLConnection) { + locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); + } + String host = locUrl.getHost(); + if (host != null && (host.length() > 0)) + perms.add(new SocketPermission(host, + SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION)); + } + + ProtectionDomain domain = + new ProtectionDomain(new CodeSource(codebase, + (java.security.cert.Certificate[]) null), perms); + AccessControlContext acc = + new AccessControlContext(new ProtectionDomain[] { domain }); + + return acc; + } + + public Thread getAppletHandlerThread() { + return handler; + } + + public int getAppletWidth() { + return currentAppletSize.width; + } + + public int getAppletHeight() { + return currentAppletSize.height; + } + + public static void changeFrameAppContext(Frame frame, AppContext newAppContext) + { + // Fixed #4754451: Applet can have methods running on main + // thread event queue. + // + // The cause of this bug is that the frame of the applet + // is created in main thread group. Thus, when certain + // AWT/Swing events are generated, the events will be + // dispatched through the wrong event dispatch thread. + // + // To fix this, we rearrange the AppContext with the frame, + // so the proper event queue will be looked up. + // + // Swing also maintains a Frame list for the AppContext, + // so we will have to rearrange it as well. + + // Check if frame's AppContext has already been set properly + AppContext oldAppContext = SunToolkit.targetToAppContext(frame); + + if (oldAppContext == newAppContext) + return; + + // Synchronization on Window.class is needed for locking the + // critical section of the window list in AppContext. + synchronized (Window.class) + { + WeakReference weakRef = null; + // Remove frame from the Window list in wrong AppContext + { + // Lookup current frame's AppContext + Vector> windowList = (Vector>)oldAppContext.get(Window.class); + if (windowList != null) { + for (WeakReference ref : windowList) { + if (ref.get() == frame) { + weakRef = ref; + break; + } + } + // Remove frame from wrong AppContext + if (weakRef != null) + windowList.remove(weakRef); + } + } + + // Put the frame into the applet's AppContext map + SunToolkit.insertTargetMapping(frame, newAppContext); + + // Insert frame into the Window list in the applet's AppContext map + { + Vector> windowList = (Vector)newAppContext.get(Window.class); + if (windowList == null) { + windowList = new Vector>(); + newAppContext.put(Window.class, windowList); + } + // use the same weakRef here as it is used elsewhere + windowList.add(weakRef); + } + } + } + + // Flag to indicate if applet is targeted for JDK 1.1. + private boolean jdk11Applet = false; + + // Flag to indicate if applet is targeted for JDK 1.2. + private boolean jdk12Applet = false; + + /** + * Determine JDK level of an applet. + */ + private void findAppletJDKLevel(Applet applet) + { + // To determine the JDK level of an applet, the + // most reliable way is to check the major version + // of the applet class file. + + // synchronized on applet class object, so calling from + // different instances of the same applet will be + // serialized. + Class appletClass = applet.getClass(); + + synchronized(appletClass) { + // Determine if the JDK level of an applet has been + // checked before. + Boolean jdk11Target = (Boolean) loader.isJDK11Target(appletClass); + Boolean jdk12Target = (Boolean) loader.isJDK12Target(appletClass); + + // if applet JDK level has been checked before, retrieve + // value and return. + if (jdk11Target != null || jdk12Target != null) { + jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue(); + jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue(); + return; + } + + String name = appletClass.getName(); + + // first convert any '.' to '/' + name = name.replace('.', '/'); + + // append .class + final String resourceName = name + ".class"; + + byte[] classHeader = new byte[8]; + + try (InputStream is = AccessController.doPrivileged( + (PrivilegedAction) () -> loader.getResourceAsStream(resourceName))) { + + // Read the first 8 bytes of the class file + int byteRead = is.read(classHeader, 0, 8); + + // return if the header is not read in entirely + // for some reasons. + if (byteRead != 8) + return; + } + catch (IOException e) { + return; + } + + // Check major version in class file header + int major_version = readShort(classHeader, 6); + + // Major version in class file is as follows: + // 45 - JDK 1.1 + // 46 - JDK 1.2 + // 47 - JDK 1.3 + // 48 - JDK 1.4 + // 49 - JDK 1.5 + if (major_version < 46) + jdk11Applet = true; + else if (major_version == 46) + jdk12Applet = true; + + // Store applet JDK level in AppContext for later lookup, + // e.g. page switch. + loader.setJDK11Target(appletClass, jdk11Applet); + loader.setJDK12Target(appletClass, jdk12Applet); + } + } + + /** + * Return true if applet is targeted to JDK 1.1. + */ + protected boolean isJDK11Applet() { + return jdk11Applet; + } + + /** + * Return true if applet is targeted to JDK1.2. + */ + protected boolean isJDK12Applet() { + return jdk12Applet; + } + + /** + * Read short from byte array. + */ + private int readShort(byte[] b, int off) { + int hi = readByte(b[off]); + int lo = readByte(b[off + 1]); + return (hi << 8) | lo; + } + + private int readByte(byte b) { + return ((int)b) & 0xFF; + } + + + private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel"); +} diff --git a/src/sun/applet/AppletProps.java b/src/sun/applet/AppletProps.java new file mode 100644 index 00000000..8c8e1b26 --- /dev/null +++ b/src/sun/applet/AppletProps.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1995, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.awt.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Properties; + +import sun.security.action.*; + +class AppletProps extends Frame { + + TextField proxyHost; + TextField proxyPort; + Choice accessMode; + + AppletProps() { + setTitle(amh.getMessage("title")); + Panel p = new Panel(); + p.setLayout(new GridLayout(0, 2)); + + p.add(new Label(amh.getMessage("label.http.server", "Http proxy server:"))); + p.add(proxyHost = new TextField()); + + p.add(new Label(amh.getMessage("label.http.proxy"))); + p.add(proxyPort = new TextField()); + + p.add(new Label(amh.getMessage("label.class"))); + p.add(accessMode = new Choice()); + accessMode.addItem(amh.getMessage("choice.class.item.restricted")); + accessMode.addItem(amh.getMessage("choice.class.item.unrestricted")); + + add("Center", p); + p = new Panel(); + p.add(new Button(amh.getMessage("button.apply"))); + p.add(new Button(amh.getMessage("button.reset"))); + p.add(new Button(amh.getMessage("button.cancel"))); + add("South", p); + move(200, 150); + pack(); + reset(); + } + + void reset() { + AppletSecurity security = (AppletSecurity) System.getSecurityManager(); + if (security != null) + security.reset(); + + String proxyhost = (String) AccessController.doPrivileged( + new GetPropertyAction("http.proxyHost")); + String proxyport = (String) AccessController.doPrivileged( + new GetPropertyAction("http.proxyPort")); + + Boolean tmp = (Boolean) AccessController.doPrivileged( + new GetBooleanAction("package.restrict.access.sun")); + + boolean packageRestrict = tmp.booleanValue(); + if (packageRestrict) { + accessMode.select(amh.getMessage("choice.class.item.restricted")); + } else { + accessMode.select(amh.getMessage("choice.class.item.unrestricted")); + } + + if (proxyhost != null) { + proxyHost.setText(proxyhost); + proxyPort.setText(proxyport); + } else { + proxyHost.setText(""); + proxyPort.setText(""); + } + } + + void apply() { + String proxyHostValue = proxyHost.getText().trim(); + String proxyPortValue = proxyPort.getText().trim(); + + // Get properties + final Properties props = (Properties) AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + return System.getProperties(); + } + }); + + if (proxyHostValue.length() != 0) { + /* 4066402 */ + /* Check for parsable value in proxy port number field before */ + /* applying. Display warning to user until parsable value is */ + /* entered. */ + int proxyPortNumber = 0; + try { + proxyPortNumber = Integer.parseInt(proxyPortValue); + } catch (NumberFormatException e) {} + + if (proxyPortNumber <= 0) { + proxyPort.selectAll(); + proxyPort.requestFocus(); + (new AppletPropsErrorDialog(this, + amh.getMessage("title.invalidproxy"), + amh.getMessage("label.invalidproxy"), + amh.getMessage("button.ok"))).show(); + return; + } + /* end 4066402 */ + + props.put("http.proxyHost", proxyHostValue); + props.put("http.proxyPort", proxyPortValue); + } else { + props.put("http.proxyHost", ""); + } + + if (amh.getMessage("choice.class.item.restricted").equals(accessMode.getSelectedItem())) { + props.put("package.restrict.access.sun", "true"); + } else { + props.put("package.restrict.access.sun", "false"); + } + + // Save properties + try { + reset(); + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws IOException { + File dotAV = Main.theUserPropertiesFile; + FileOutputStream out = new FileOutputStream(dotAV); + Properties avProps = new Properties(); + for (int i = 0; i < Main.avDefaultUserProps.length; i++) { + String avKey = Main.avDefaultUserProps[i][0]; + avProps.setProperty(avKey, props.getProperty(avKey)); + } + avProps.store(out, amh.getMessage("prop.store")); + out.close(); + return null; + } + }); + hide(); + } catch (PrivilegedActionException e) { + System.out.println(amh.getMessage("apply.exception", + e.getException())); + // XXX what's the general feeling on stack traces to System.out? + e.printStackTrace(); + reset(); + } + } + + public boolean action(Event evt, Object obj) { + if (amh.getMessage("button.apply").equals(obj)) { + apply(); + return true; + } + if (amh.getMessage("button.reset").equals(obj)) { + reset(); + return true; + } + if (amh.getMessage("button.cancel").equals(obj)) { + reset(); + hide(); + return true; + } + return false; + } + + private static AppletMessageHandler amh = new AppletMessageHandler("appletprops"); + +} + +/* 4066432 */ +/* Dialog class to display property-related errors to user */ + +class AppletPropsErrorDialog extends Dialog { + public AppletPropsErrorDialog(Frame parent, String title, String message, + String buttonText) { + super(parent, title, true); + Panel p = new Panel(); + add("Center", new Label(message)); + p.add(new Button(buttonText)); + add("South", p); + pack(); + + Dimension dDim = size(); + Rectangle fRect = parent.bounds(); + move(fRect.x + ((fRect.width - dDim.width) / 2), + fRect.y + ((fRect.height - dDim.height) / 2)); + } + + public boolean action(Event event, Object object) { + hide(); + dispose(); + return true; + } +} + +/* end 4066432 */ diff --git a/src/sun/applet/AppletResourceLoader.java b/src/sun/applet/AppletResourceLoader.java new file mode 100644 index 00000000..f073e07f --- /dev/null +++ b/src/sun/applet/AppletResourceLoader.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.awt.*; +import java.net.URL; + +import sun.misc.Ref; + +/** + * Part of this class still remains only to support legacy, 100%-impure + * applications such as HotJava 1.0.1. + */ +public class AppletResourceLoader { + public static Image getImage(URL url) { + return AppletViewer.getCachedImage(url); + } + + public static Ref getImageRef(URL url) { + return AppletViewer.getCachedImageRef(url); + } + + public static void flushImages() { + AppletViewer.flushImageCache(); + } +} diff --git a/src/sun/applet/AppletSecurity.java b/src/sun/applet/AppletSecurity.java new file mode 100644 index 00000000..08fe72c4 --- /dev/null +++ b/src/sun/applet/AppletSecurity.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.lang.reflect.Field; +import java.net.URLClassLoader; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; + +import sun.awt.AWTSecurityManager; +import sun.awt.AppContext; +import sun.security.provider.*; +import sun.security.util.SecurityConstants; + + +/** + * This class defines an applet security policy + * + */ +public +class AppletSecurity extends AWTSecurityManager { + + //URLClassLoader.acc + private static Field facc = null; + + //AccessControlContext.context; + private static Field fcontext = null; + + static { + try { + facc = URLClassLoader.class.getDeclaredField("acc"); + facc.setAccessible(true); + fcontext = AccessControlContext.class.getDeclaredField("context"); + fcontext.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new UnsupportedOperationException(e); + } + } + + + /** + * Construct and initialize. + */ + public AppletSecurity() { + reset(); + } + + // Cache to store known restricted packages + private HashSet restrictedPackages = new HashSet(); + + /** + * Reset from Properties + */ + public void reset() + { + // Clear cache + restrictedPackages.clear(); + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() + { + // Enumerate system properties + Enumeration e = System.getProperties().propertyNames(); + + while (e.hasMoreElements()) + { + String name = (String) e.nextElement(); + + if (name != null && name.startsWith("package.restrict.access.")) + { + String value = System.getProperty(name); + + if (value != null && value.equalsIgnoreCase("true")) + { + String pkg = name.substring(24); + + // Cache restricted packages + restrictedPackages.add(pkg); + } + } + } + return null; + } + }); + } + + /** + * get the current (first) instance of an AppletClassLoader on the stack. + */ + private AppletClassLoader currentAppletClassLoader() + { + // try currentClassLoader first + ClassLoader loader = currentClassLoader(); + + if ((loader == null) || (loader instanceof AppletClassLoader)) + return (AppletClassLoader)loader; + + // if that fails, get all the classes on the stack and check them. + Class[] context = getClassContext(); + for (int i = 0; i < context.length; i++) { + loader = context[i].getClassLoader(); + if (loader instanceof AppletClassLoader) + return (AppletClassLoader)loader; + } + + /* + * fix bug # 6433620 the logic here is : try to find URLClassLoader from + * class context, check its AccessControlContext to see if + * AppletClassLoader is in stack when it's created. for this kind of + * URLClassLoader, return the AppContext associated with the + * AppletClassLoader. + */ + for (int i = 0; i < context.length; i++) { + final ClassLoader currentLoader = context[i].getClassLoader(); + + if (currentLoader instanceof URLClassLoader) { + loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + + AccessControlContext acc = null; + ProtectionDomain[] pds = null; + + try { + acc = (AccessControlContext) facc.get(currentLoader); + if (acc == null) { + return null; + } + + pds = (ProtectionDomain[]) fcontext.get(acc); + if (pds == null) { + return null; + } + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + + for (int i=0; iSecurityException if the + * calling thread is not allowed to access the package specified by + * the argument. + *

    + * This method is used by the loadClass method of class + * loaders. + *

    + * The checkPackageAccess method for class + * SecurityManager calls + * checkPermission with the + * RuntimePermission("accessClassInPackage."+pkg) + * permission. + * + * @param pkg the package name. + * @exception SecurityException if the caller does not have + * permission to access the specified package. + * @see ClassLoader#loadClass(String, boolean) + */ + public void checkPackageAccess(final String pkgname) { + + // first see if the VM-wide policy allows access to this package + super.checkPackageAccess(pkgname); + + // now check the list of restricted packages + for (Iterator iter = restrictedPackages.iterator(); iter.hasNext();) + { + String pkg = (String) iter.next(); + + // Prevent matching "sun" and "sunir" even if they + // starts with similar beginning characters + // + if (pkgname.equals(pkg) || pkgname.startsWith(pkg + ".")) + { + checkPermission(new RuntimePermission + ("accessClassInPackage." + pkgname)); + } + } + } + + /** + * Tests if a client can get access to the AWT event queue. + *

    + * This method calls checkPermission with the + * AWTPermission("accessEventQueue") permission. + * + * @since JDK1.1 + * @exception SecurityException if the caller does not have + * permission to access the AWT event queue. + */ + public void checkAwtEventQueueAccess() { + AppContext appContext = AppContext.getAppContext(); + AppletClassLoader appletClassLoader = currentAppletClassLoader(); + + if (AppContext.isMainContext(appContext) && (appletClassLoader != null)) { + // If we're about to allow access to the main EventQueue, + // and anything untrusted is on the class context stack, + // disallow access. + super.checkPermission(SecurityConstants.AWT.CHECK_AWT_EVENTQUEUE_PERMISSION); + } + } // checkAwtEventQueueAccess() + + /** + * Returns the thread group of the applet. We consult the classloader + * if there is one. + */ + public ThreadGroup getThreadGroup() { + /* If any applet code is on the execution stack, we return + that applet's ThreadGroup. Otherwise, we use the default + behavior. */ + AppletClassLoader appletLoader = currentAppletClassLoader(); + ThreadGroup loaderGroup = (appletLoader == null) ? null + : appletLoader.getThreadGroup(); + if (loaderGroup != null) { + return loaderGroup; + } else { + return super.getThreadGroup(); + } + } // getThreadGroup() + + /** + * Get the AppContext corresponding to the current context. + * The default implementation returns null, but this method + * may be overridden by various SecurityManagers + * (e.g. AppletSecurity) to index AppContext objects by the + * calling context. + * + * @return the AppContext corresponding to the current context. + * @see sun.awt.AppContext + * @see SecurityManager + * @since JDK1.2.1 + */ + public AppContext getAppContext() { + AppletClassLoader appletLoader = currentAppletClassLoader(); + + if (appletLoader == null) { + return null; + } else { + AppContext context = appletLoader.getAppContext(); + + // context == null when some thread in applet thread group + // has not been destroyed in AppContext.dispose() + if (context == null) { + throw new SecurityException("Applet classloader has invalid AppContext"); + } + + return context; + } + } + +} // class AppletSecurity diff --git a/src/sun/applet/AppletSecurityException.java b/src/sun/applet/AppletSecurityException.java new file mode 100644 index 00000000..138e9279 --- /dev/null +++ b/src/sun/applet/AppletSecurityException.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1995, 1998, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +/** + * An applet security exception. + * + * @author Arthur van Hoff + */ +public +class AppletSecurityException extends SecurityException { + private String key = null; + private Object msgobj[] = null; + + public AppletSecurityException(String name) { + super(name); + this.key = name; + } + + public AppletSecurityException(String name, String arg) { + this(name); + msgobj = new Object[1]; + msgobj[0] = (Object)arg; + } + + public AppletSecurityException(String name, String arg1, String arg2) { + this(name); + msgobj = new Object[2]; + msgobj[0] = (Object)arg1; + msgobj[1] = (Object)arg2; + } + + public String getLocalizedMessage() { + if( msgobj != null) + return amh.getMessage(key, msgobj); + else + return amh.getMessage(key); + } + + private static AppletMessageHandler amh = new AppletMessageHandler("appletsecurityexception"); + +} diff --git a/src/sun/applet/AppletThreadGroup.java b/src/sun/applet/AppletThreadGroup.java new file mode 100644 index 00000000..9d678e15 --- /dev/null +++ b/src/sun/applet/AppletThreadGroup.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1995, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +/** + * This class defines an applet thread group. + * + * @author Arthur van Hoff + */ +public class AppletThreadGroup extends ThreadGroup { + + /** + * Constructs a new thread group for an applet. + * The parent of this new group is the thread + * group of the currently running thread. + * + * @param name the name of the new thread group. + */ + public AppletThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Creates a new thread group for an applet. + * The parent of this new group is the specified + * thread group. + * + * @param parent the parent thread group. + * @param name the name of the new thread group. + * @exception NullPointerException if the thread group argument is + * null. + * @exception SecurityException if the current thread cannot create a + * thread in the specified thread group. + * @see SecurityException + * @since JDK1.1.1 + */ + public AppletThreadGroup(ThreadGroup parent, String name) { + super(parent, name); + setMaxPriority(Thread.NORM_PRIORITY - 1); + } +} diff --git a/src/sun/applet/AppletViewer.java b/src/sun/applet/AppletViewer.java new file mode 100644 index 00000000..bb3f9dad --- /dev/null +++ b/src/sun/applet/AppletViewer.java @@ -0,0 +1,1324 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.applet.Applet; +import java.applet.AppletContext; +import java.applet.AudioClip; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.Reader; +import java.net.SocketPermission; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Vector; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; + +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import sun.misc.Ref; + +/** + * A frame to show the applet tag in. + */ +final class TextFrame extends Frame { + + /** + * Create the tag frame. + */ + TextFrame(int x, int y, String title, String text) { + setTitle(title); + TextArea txt = new TextArea(20, 60); + txt.setText(text); + txt.setEditable(false); + + add("Center", txt); + + Panel p = new Panel(); + add("South", p); + Button b = new Button(amh.getMessage("button.dismiss", "Dismiss")); + p.add(b); + + class ActionEventListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent evt) { + dispose(); + } + } + b.addActionListener(new ActionEventListener()); + + pack(); + move(x, y); + setVisible(true); + + WindowListener windowEventListener = new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent evt) { + dispose(); + } + }; + + addWindowListener(windowEventListener); + } + private static AppletMessageHandler amh = new AppletMessageHandler("textframe"); + +} + +/** + * Lets us construct one using unix-style one shot behaviors. + */ +final class StdAppletViewerFactory implements AppletViewerFactory { + + @Override + public AppletViewer createAppletViewer(int x, int y, + URL doc, Hashtable atts) { + return new AppletViewer(x, y, doc, atts, System.out, this); + } + + @Override + public MenuBar getBaseMenuBar() { + return new MenuBar(); + } + + @Override + public boolean isStandalone() { + return true; + } +} + +/** + * The applet viewer makes it possible to run a Java applet without using a browser. + * For details on the syntax that appletviewer supports, see + * AppletViewer Tags. + * (The document named appletviewertags.html in the JDK's docs/tooldocs directory, + * once the JDK docs have been installed.) + */ +public class AppletViewer extends Frame implements AppletContext, Printable { + + /** + * Some constants... + */ + private static String defaultSaveFile = "Applet.ser"; + + /** + * The panel in which the applet is being displayed. + */ + AppletViewerPanel panel; + + /** + * The status line. + */ + Label label; + + /** + * output status messages to this stream + */ + + PrintStream statusMsgStream; + + /** + * For cloning + */ + AppletViewerFactory factory; + + + private final class UserActionListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent evt) { + processUserAction(evt); + } + } + + /** + * Create the applet viewer. + */ + public AppletViewer(int x, int y, URL doc, Hashtable atts, + PrintStream statusMsgStream, AppletViewerFactory factory) { + this.factory = factory; + this.statusMsgStream = statusMsgStream; + setTitle(amh.getMessage("tool.title", atts.get("code"))); + + MenuBar mb = factory.getBaseMenuBar(); + + Menu m = new Menu(amh.getMessage("menu.applet")); + + addMenuItem(m, "menuitem.restart"); + addMenuItem(m, "menuitem.reload"); + addMenuItem(m, "menuitem.stop"); + addMenuItem(m, "menuitem.save"); + addMenuItem(m, "menuitem.start"); + addMenuItem(m, "menuitem.clone"); + m.add(new MenuItem("-")); + addMenuItem(m, "menuitem.tag"); + addMenuItem(m, "menuitem.info"); + addMenuItem(m, "menuitem.edit").disable(); + addMenuItem(m, "menuitem.encoding"); + m.add(new MenuItem("-")); + addMenuItem(m, "menuitem.print"); + m.add(new MenuItem("-")); + addMenuItem(m, "menuitem.props"); + m.add(new MenuItem("-")); + addMenuItem(m, "menuitem.close"); + if (factory.isStandalone()) { + addMenuItem(m, "menuitem.quit"); + } + + mb.add(m); + + setMenuBar(mb); + + add("Center", panel = new AppletViewerPanel(doc, atts)); + add("South", label = new Label(amh.getMessage("label.hello"))); + panel.init(); + appletPanels.addElement(panel); + + pack(); + move(x, y); + setVisible(true); + + WindowListener windowEventListener = new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent evt) { + appletClose(); + } + + @Override + public void windowIconified(WindowEvent evt) { + appletStop(); + } + + @Override + public void windowDeiconified(WindowEvent evt) { + appletStart(); + } + }; + + class AppletEventListener implements AppletListener + { + final Frame frame; + + public AppletEventListener(Frame frame) + { + this.frame = frame; + } + + @Override + public void appletStateChanged(AppletEvent evt) + { + AppletPanel src = (AppletPanel)evt.getSource(); + + switch (evt.getID()) { + case AppletPanel.APPLET_RESIZE: { + if(src != null) { + resize(preferredSize()); + validate(); + } + break; + } + case AppletPanel.APPLET_LOADING_COMPLETED: { + Applet a = src.getApplet(); // sun.applet.AppletPanel + + // Fixed #4754451: Applet can have methods running on main + // thread event queue. + // + // The cause of this bug is that the frame of the applet + // is created in main thread group. Thus, when certain + // AWT/Swing events are generated, the events will be + // dispatched through the wrong event dispatch thread. + // + // To fix this, we rearrange the AppContext with the frame, + // so the proper event queue will be looked up. + // + // Swing also maintains a Frame list for the AppContext, + // so we will have to rearrange it as well. + // + if (a != null) + AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a)); + else + AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext()); + + break; + } + } + } + }; + + addWindowListener(windowEventListener); + panel.addAppletListener(new AppletEventListener(this)); + + // Start the applet + showStatus(amh.getMessage("status.start")); + initEventQueue(); + } + + // XXX 99/9/10 probably should be "private" + public MenuItem addMenuItem(Menu m, String s) { + MenuItem mItem = new MenuItem(amh.getMessage(s)); + mItem.addActionListener(new UserActionListener()); + return m.add(mItem); + } + + /** + * Send the initial set of events to the appletviewer event queue. + * On start-up the current behaviour is to load the applet and call + * Applet.init() and Applet.start(). + */ + private void initEventQueue() { + // appletviewer.send.event is an undocumented and unsupported system + // property which is used exclusively for testing purposes. + String eventList = System.getProperty("appletviewer.send.event"); + + if (eventList == null) { + // Add the standard events onto the event queue. + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } else { + // We're testing AppletViewer. Force the specified set of events + // onto the event queue, wait for the events to be processed, and + // exit. + + // The list of events that will be executed is provided as a + // ","-separated list. No error-checking will be done on the list. + String [] events = splitSeparator(",", eventList); + + for (int i = 0; i < events.length; i++) { + System.out.println("Adding event to queue: " + events[i]); + if (events[i].equals("dispose")) + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + else if (events[i].equals("load")) + panel.sendEvent(AppletPanel.APPLET_LOAD); + else if (events[i].equals("init")) + panel.sendEvent(AppletPanel.APPLET_INIT); + else if (events[i].equals("start")) + panel.sendEvent(AppletPanel.APPLET_START); + else if (events[i].equals("stop")) + panel.sendEvent(AppletPanel.APPLET_STOP); + else if (events[i].equals("destroy")) + panel.sendEvent(AppletPanel.APPLET_DESTROY); + else if (events[i].equals("quit")) + panel.sendEvent(AppletPanel.APPLET_QUIT); + else if (events[i].equals("error")) + panel.sendEvent(AppletPanel.APPLET_ERROR); + else + // non-fatal error if we get an unrecognized event + System.out.println("Unrecognized event name: " + events[i]); + } + + while (!panel.emptyEventQueue()) ; + appletSystemExit(); + } + } + + /** + * Split a string based on the presence of a specified separator. Returns + * an array of arbitrary length. The end of each element in the array is + * indicated by the separator of the end of the string. If there is a + * separator immediately before the end of the string, the final element + * will be empty. None of the strings will contain the separator. Useful + * when separating strings such as "foo/bar/bas" using separator "/". + * + * @param sep The separator. + * @param s The string to split. + * @return An array of strings. Each string in the array is determined + * by the location of the provided sep in the original string, + * s. Whitespace not stripped. + */ + private String [] splitSeparator(String sep, String s) { + Vector v = new Vector(); + int tokenStart = 0; + int tokenEnd = 0; + + while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) { + v.addElement(s.substring(tokenStart, tokenEnd)); + tokenStart = tokenEnd+1; + } + // Add the final element. + v.addElement(s.substring(tokenStart)); + + String [] retVal = new String[v.size()]; + v.copyInto(retVal); + return retVal; + } + + /* + * Methods for java.applet.AppletContext + */ + + private static Map audioClips = new HashMap(); + + /** + * Get an audio clip. + */ + @Override + public AudioClip getAudioClip(URL url) { + checkConnect(url); + synchronized (audioClips) { + AudioClip clip = (AudioClip)audioClips.get(url); + if (clip == null) { + audioClips.put(url, clip = new AppletAudioClip(url)); + } + return clip; + } + } + + private static Map imageRefs = new HashMap(); + + /** + * Get an image. + */ + @Override + public Image getImage(URL url) { + return getCachedImage(url); + } + + static Image getCachedImage(URL url) { + // System.getSecurityManager().checkConnection(url.getHost(), url.getPort()); + return (Image)getCachedImageRef(url).get(); + } + + /** + * Get an image ref. + */ + static Ref getCachedImageRef(URL url) { + synchronized (imageRefs) { + AppletImageRef ref = (AppletImageRef)imageRefs.get(url); + if (ref == null) { + ref = new AppletImageRef(url); + imageRefs.put(url, ref); + } + return ref; + } + } + + /** + * Flush the image cache. + */ + static void flushImageCache() { + imageRefs.clear(); + } + + static Vector appletPanels = new Vector(); + + /** + * Get an applet by name. + */ + @Override + public Applet getApplet(String name) { + AppletSecurity security = (AppletSecurity)System.getSecurityManager(); + name = name.toLowerCase(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + String param = p.getParameter("name"); + if (param != null) { + param = param.toLowerCase(); + } + if (name.equals(param) && + p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + + if (panelSp.implies(sp)) { + return p.applet; + } + } + } + return null; + } + + /** + * Return an enumeration of all the accessible + * applets on this page. + */ + @Override + public Enumeration getApplets() { + AppletSecurity security = (AppletSecurity)System.getSecurityManager(); + Vector v = new Vector(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + if (p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + if (panelSp.implies(sp)) { + v.addElement(p.applet); + } + } + } + return v.elements(); + } + + /** + * Ignore. + */ + @Override + public void showDocument(URL url) { + } + + /** + * Ignore. + */ + @Override + public void showDocument(URL url, String target) { + } + + /** + * Show status. + */ + @Override + public void showStatus(String status) { + label.setText(status); + } + + @Override + public void setStream(String key, InputStream stream)throws IOException{ + // We do nothing. + } + + @Override + public InputStream getStream(String key){ + // We do nothing. + return null; + } + + @Override + public Iterator getStreamKeys(){ + // We do nothing. + return null; + } + + /** + * System parameters. + */ + static Hashtable systemParam = new Hashtable(); + + static { + systemParam.put("codebase", "codebase"); + systemParam.put("code", "code"); + systemParam.put("alt", "alt"); + systemParam.put("width", "width"); + systemParam.put("height", "height"); + systemParam.put("align", "align"); + systemParam.put("vspace", "vspace"); + systemParam.put("hspace", "hspace"); + } + + /** + * Print the HTML tag. + */ + public static void printTag(PrintStream out, Hashtable atts) { + out.print(""); + + // A very slow sorting algorithm + int len = atts.size(); + String params[] = new String[len]; + len = 0; + for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) { + String param = (String)e.nextElement(); + int i = 0; + for (; i < len ; i++) { + if (params[i].compareTo(param) >= 0) { + break; + } + } + System.arraycopy(params, i, params, i + 1, len - i); + params[i] = param; + len++; + } + + for (int i = 0 ; i < len ; i++) { + String param = params[i]; + if (systemParam.get(param) == null) { + out.println(""); + } + } + out.println(""); + } + + /** + * Make sure the atrributes are uptodate. + */ + public void updateAtts() { + Dimension d = panel.size(); + Insets in = panel.insets(); + panel.atts.put("width", + Integer.toString(d.width - (in.left + in.right))); + panel.atts.put("height", + Integer.toString(d.height - (in.top + in.bottom))); + } + + /** + * Restart the applet. + */ + void appletRestart() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Reload the applet. + */ + void appletReload() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + + /** + * Fixed #4501142: Classloader sharing policy doesn't + * take "archive" into account. This will be overridden + * by Java Plug-in. [stanleyh] + */ + AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey()); + + /* + * Make sure we don't have two threads running through the event queue + * at the same time. + */ + try { + panel.joinAppletThread(); + panel.release(); + } catch (InterruptedException e) { + return; // abort the reload + } + + panel.createAppletThread(); + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Save the applet to a well known file (for now) as a serialized object + */ + void appletSave() { + AccessController.doPrivileged(new PrivilegedAction() { + + @Override + public Object run() { + // XXX: this privileged block should be made smaller + // by initializing a private static variable with "user.dir" + + // Applet needs to be stopped for serialization to succeed. + // Since panel.sendEvent only queues the event, there is a + // chance that the event will not be processed before + // serialization begins. However, by sending the event before + // FileDialog is created, enough time is given such that this + // situation is unlikely to ever occur. + + panel.sendEvent(AppletPanel.APPLET_STOP); + FileDialog fd = new FileDialog(AppletViewer.this, + amh.getMessage("appletsave.filedialogtitle"), + FileDialog.SAVE); + // needed for a bug under Solaris... + fd.setDirectory(System.getProperty("user.dir")); + fd.setFile(defaultSaveFile); + fd.show(); + String fname = fd.getFile(); + if (fname == null) { + // Restart applet if Save is cancelled. + panel.sendEvent(AppletPanel.APPLET_START); + return null; // cancelled + } + String dname = fd.getDirectory(); + File file = new File(dname, fname); + + try (FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream bos = new BufferedOutputStream(fos); + ObjectOutputStream os = new ObjectOutputStream(bos)) { + + showStatus(amh.getMessage("appletsave.err1", panel.applet.toString(), file.toString())); + os.writeObject(panel.applet); + } catch (IOException ex) { + System.err.println(amh.getMessage("appletsave.err2", ex)); + } finally { + panel.sendEvent(AppletPanel.APPLET_START); + } + return null; + } + }); + } + + /** + * Clone the viewer and the applet. + */ + void appletClone() { + Point p = location(); + updateAtts(); + factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA, + panel.documentURL, (Hashtable)panel.atts.clone()); + } + + /** + * Show the applet tag. + */ + void appletTag() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + updateAtts(); + printTag(new PrintStream(out), panel.atts); + showStatus(amh.getMessage("applettag")); + + Point p = location(); + new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString()); + } + + /** + * Show the applet info. + */ + void appletInfo() { + String str = panel.applet.getAppletInfo(); + if (str == null) { + str = amh.getMessage("appletinfo.applet"); + } + str += "\n\n"; + + String atts[][] = panel.applet.getParameterInfo(); + if (atts != null) { + for (int i = 0 ; i < atts.length ; i++) { + str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n"; + } + } else { + str += amh.getMessage("appletinfo.param"); + } + + Point p = location(); + new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str); + + } + + /** + * Show character encoding type + */ + void appletCharacterEncoding() { + showStatus(amh.getMessage("appletencoding", encoding)); + } + + /** + * Edit the applet. + */ + void appletEdit() { + } + + /** + * Print the applet. + */ + void appletPrint() { + PrinterJob pj = PrinterJob.getPrinterJob(); + + if (pj != null) { + PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); + if (pj.printDialog(aset)) { + pj.setPrintable(this); + try { + pj.print(aset); + statusMsgStream.println(amh.getMessage("appletprint.finish")); + } catch (PrinterException e) { + statusMsgStream.println(amh.getMessage("appletprint.fail")); + } + } else { + statusMsgStream.println(amh.getMessage("appletprint.cancel")); + } + } else { + statusMsgStream.println(amh.getMessage("appletprint.fail")); + } + } + + @Override + public int print(Graphics graphics, PageFormat pf, int pageIndex) { + if (pageIndex > 0) { + return Printable.NO_SUCH_PAGE; + } else { + Graphics2D g2d = (Graphics2D)graphics; + g2d.translate(pf.getImageableX(), pf.getImageableY()); + panel.applet.printAll(graphics); + return Printable.PAGE_EXISTS; + } + } + + /** + * Properties. + */ + static AppletProps props; + public static synchronized void networkProperties() { + if (props == null) { + props = new AppletProps(); + } + props.addNotify(); + props.setVisible(true); + } + + /** + * Start the applet. + */ + void appletStart() { + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Stop the applet. + */ + void appletStop() { + panel.sendEvent(AppletPanel.APPLET_STOP); + } + + /** + * Shutdown a viewer. + * Stop, Destroy, Dispose and Quit a viewer + */ + private void appletShutdown(AppletPanel p) { + p.sendEvent(AppletPanel.APPLET_STOP); + p.sendEvent(AppletPanel.APPLET_DESTROY); + p.sendEvent(AppletPanel.APPLET_DISPOSE); + p.sendEvent(AppletPanel.APPLET_QUIT); + } + + /** + * Close this viewer. + * Stop, Destroy, Dispose and Quit an AppletView, then + * reclaim resources and exit the program if this is + * the last applet. + */ + void appletClose() { + + // The caller thread is event dispatch thread, so + // spawn a new thread to avoid blocking the event queue + // when calling appletShutdown. + // + final AppletPanel p = panel; + + new Thread(new Runnable() + { + @Override + public void run() + { + appletShutdown(p); + appletPanels.removeElement(p); + dispose(); + + if (countApplets() == 0) { + appletSystemExit(); + } + } + }).start(); + } + + /** + * Exit the program. + * Exit from the program (if not stand alone) - do no clean-up + */ + private void appletSystemExit() { + if (factory.isStandalone()) + System.exit(0); + } + + /** + * Quit all viewers. + * Shutdown all viewers properly then + * exit from the program (if not stand alone) + */ + protected void appletQuit() + { + // The caller thread is event dispatch thread, so + // spawn a new thread to avoid blocking the event queue + // when calling appletShutdown. + // + new Thread(new Runnable() + { + @Override + public void run() + { + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + appletShutdown(p); + } + appletSystemExit(); + } + }).start(); + } + + /** + * Handle events. + */ + public void processUserAction(ActionEvent evt) { + + String label = ((MenuItem)evt.getSource()).getLabel(); + + if (amh.getMessage("menuitem.restart").equals(label)) { + appletRestart(); + return; + } + + if (amh.getMessage("menuitem.reload").equals(label)) { + appletReload(); + return; + } + + if (amh.getMessage("menuitem.clone").equals(label)) { + appletClone(); + return; + } + + if (amh.getMessage("menuitem.stop").equals(label)) { + appletStop(); + return; + } + + if (amh.getMessage("menuitem.save").equals(label)) { + appletSave(); + return; + } + + if (amh.getMessage("menuitem.start").equals(label)) { + appletStart(); + return; + } + + if (amh.getMessage("menuitem.tag").equals(label)) { + appletTag(); + return; + } + + if (amh.getMessage("menuitem.info").equals(label)) { + appletInfo(); + return; + } + + if (amh.getMessage("menuitem.encoding").equals(label)) { + appletCharacterEncoding(); + return; + } + + if (amh.getMessage("menuitem.edit").equals(label)) { + appletEdit(); + return; + } + + if (amh.getMessage("menuitem.print").equals(label)) { + appletPrint(); + return; + } + + if (amh.getMessage("menuitem.props").equals(label)) { + networkProperties(); + return; + } + + if (amh.getMessage("menuitem.close").equals(label)) { + appletClose(); + return; + } + + if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) { + appletQuit(); + return; + } + //statusMsgStream.println("evt = " + evt); + } + + /** + * How many applets are running? + */ + + public static int countApplets() { + return appletPanels.size(); + } + + + /** + * The current character. + */ + static int c; + + /** + * Scan spaces. + */ + public static void skipSpace(Reader in) throws IOException { + while ((c >= 0) && + ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) { + c = in.read(); + } + } + + /** + * Scan identifier + */ + public static String scanIdentifier(Reader in) throws IOException { + StringBuffer buf = new StringBuffer(); + while (true) { + if (((c >= 'a') && (c <= 'z')) || + ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')) || (c == '_')) { + buf.append((char)c); + c = in.read(); + } else { + return buf.toString(); + } + } + } + + /** + * Scan tag + */ + public static Hashtable scanTag(Reader in) throws IOException { + Hashtable atts = new Hashtable(); + skipSpace(in); + while (c >= 0 && c != '>') { + String att = scanIdentifier(in); + String val = ""; + skipSpace(in); + if (c == '=') { + int quote = -1; + c = in.read(); + skipSpace(in); + if ((c == '\'') || (c == '\"')) { + quote = c; + c = in.read(); + } + StringBuffer buf = new StringBuffer(); + while ((c > 0) && + (((quote < 0) && (c != ' ') && (c != '\t') && + (c != '\n') && (c != '\r') && (c != '>')) + || ((quote >= 0) && (c != quote)))) { + buf.append((char)c); + c = in.read(); + } + if (c == quote) { + c = in.read(); + } + skipSpace(in); + val = buf.toString(); + } + //statusMsgStream.println("PUT " + att + " = '" + val + "'"); + if (! val.equals("")) { + atts.put(att.toLowerCase(Locale.ENGLISH), val); + } + while (true) { + if ((c == '>') || (c < 0) || + ((c >= 'a') && (c <= 'z')) || + ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')) || (c == '_')) + break; + c = in.read(); + } + //skipSpace(in); + } + return atts; + } + + /* values used for placement of AppletViewer's frames */ + private static int x = 0; + private static int y = 0; + private static final int XDELTA = 30; + private static final int YDELTA = XDELTA; + + static String encoding = null; + + static private Reader makeReader(InputStream is) { + if (encoding != null) { + try { + return new BufferedReader(new InputStreamReader(is, encoding)); + } catch (IOException x) { } + } + InputStreamReader r = new InputStreamReader(is); + encoding = r.getEncoding(); + return new BufferedReader(r); + } + + /** + * Scan an html file for tags + */ + public static void parse(URL url, String enc) throws IOException { + encoding = enc; + parse(url, System.out, new StdAppletViewerFactory()); + } + + public static void parse(URL url) throws IOException { + parse(url, System.out, new StdAppletViewerFactory()); + } + + public static void parse(URL url, PrintStream statusMsgStream, + AppletViewerFactory factory) throws IOException { + // tag flags + boolean isAppletTag = false; + boolean isObjectTag = false; + boolean isEmbedTag = false; + + // warning messages + String requiresNameWarning = amh.getMessage("parse.warning.requiresname"); + String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside"); + String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode"); + String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight"); + String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth"); + String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode"); + String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight"); + String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth"); + String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode"); + String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight"); + String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth"); + String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported"); + + java.net.URLConnection conn = url.openConnection(); + Reader in = makeReader(conn.getInputStream()); + /* The original URL may have been redirected - this + * sets it to whatever URL/codebase we ended up getting + */ + url = conn.getURL(); + + int ydisp = 1; + Hashtable atts = null; + + while(true) { + c = in.read(); + if (c == -1) + break; + + if (c == '<') { + c = in.read(); + if (c == '/') { + c = in.read(); + String nm = scanIdentifier(in); + if (nm.equalsIgnoreCase("applet") || + nm.equalsIgnoreCase("object") || + nm.equalsIgnoreCase("embed")) { + + // We can't test for a code tag until + // because it is a parameter, not an attribute. + if(isObjectTag) { + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(objectRequiresCodeWarning); + atts = null; + } + } + + if (atts != null) { + // XXX 5/18 In general this code just simply + // shouldn't be part of parsing. It's presence + // causes things to be a little too much of a + // hack. + factory.createAppletViewer(x, y, url, atts); + x += XDELTA; + y += YDELTA; + // make sure we don't go too far! + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + if ((x > d.width - 300) || (y > d.height - 300)) { + x = 0; + y = 2 * ydisp * YDELTA; + ydisp++; + } + } + atts = null; + isAppletTag = false; + isObjectTag = false; + isEmbedTag = false; + } + } + else { + String nm = scanIdentifier(in); + if (nm.equalsIgnoreCase("param")) { + Hashtable t = scanTag(in); + String att = (String)t.get("name"); + if (att == null) { + statusMsgStream.println(requiresNameWarning); + } else { + String val = (String)t.get("value"); + if (val == null) { + statusMsgStream.println(requiresNameWarning); + } else if (atts != null) { + atts.put(att.toLowerCase(), val); + } else { + statusMsgStream.println(paramOutsideWarning); + } + } + } + else if (nm.equalsIgnoreCase("applet")) { + isAppletTag = true; + atts = scanTag(in); + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(appletRequiresCodeWarning); + atts = null; + } else if (atts.get("width") == null) { + statusMsgStream.println(appletRequiresWidthWarning); + atts = null; + } else if (atts.get("height") == null) { + statusMsgStream.println(appletRequiresHeightWarning); + atts = null; + } + } + else if (nm.equalsIgnoreCase("object")) { + isObjectTag = true; + atts = scanTag(in); + // The attribute codebase isn't what + // we want. If its defined, remove it. + if(atts.get("codebase") != null) { + atts.remove("codebase"); + } + + if (atts.get("width") == null) { + statusMsgStream.println(objectRequiresWidthWarning); + atts = null; + } else if (atts.get("height") == null) { + statusMsgStream.println(objectRequiresHeightWarning); + atts = null; + } + } + else if (nm.equalsIgnoreCase("embed")) { + isEmbedTag = true; + atts = scanTag(in); + + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(embedRequiresCodeWarning); + atts = null; + } else if (atts.get("width") == null) { + statusMsgStream.println(embedRequiresWidthWarning); + atts = null; + } else if (atts.get("height") == null) { + statusMsgStream.println(embedRequiresHeightWarning); + atts = null; + } + } + else if (nm.equalsIgnoreCase("app")) { + statusMsgStream.println(appNotLongerSupportedWarning); + Hashtable atts2 = scanTag(in); + nm = (String)atts2.get("class"); + if (nm != null) { + atts2.remove("class"); + atts2.put("code", nm + ".class"); + } + nm = (String)atts2.get("src"); + if (nm != null) { + atts2.remove("src"); + atts2.put("codebase", nm); + } + if (atts2.get("width") == null) { + atts2.put("width", "100"); + } + if (atts2.get("height") == null) { + atts2.put("height", "100"); + } + printTag(statusMsgStream, atts2); + statusMsgStream.println(); + } + } + } + } + in.close(); + } + + /** + * Old main entry point. + * + * @deprecated + */ + @Deprecated + public static void main(String argv[]) { + // re-route everything to the new main entry point + Main.main(argv); + } + + private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); + + private static void checkConnect(URL url) + { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + java.security.Permission perm = + url.openConnection().getPermission(); + if (perm != null) + security.checkPermission(perm); + else + security.checkConnect(url.getHost(), url.getPort()); + } catch (IOException ioe) { + security.checkConnect(url.getHost(), url.getPort()); + } + } + } +} diff --git a/src/sun/applet/AppletViewerFactory.java b/src/sun/applet/AppletViewerFactory.java new file mode 100644 index 00000000..69900e9a --- /dev/null +++ b/src/sun/applet/AppletViewerFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * AppletViewerFactory.java + */ + +package sun.applet; + +import java.awt.*; +import java.net.URL; +import java.util.Hashtable; + +public +interface AppletViewerFactory { + public AppletViewer createAppletViewer(int x, int y, URL doc, Hashtable atts); + public MenuBar getBaseMenuBar(); + public boolean isStandalone(); +} diff --git a/src/sun/applet/AppletViewerPanel.java b/src/sun/applet/AppletViewerPanel.java new file mode 100644 index 00000000..66d876ea --- /dev/null +++ b/src/sun/applet/AppletViewerPanel.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 1995, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.applet.AppletContext; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Hashtable; + + +/** + * Sample applet panel class. The panel manages and manipulates the + * applet as it is being loaded. It forks a seperate thread in a new + * thread group to call the applet's init(), start(), stop(), and + * destroy() methods. + * + * @author Arthur van Hoff + */ +class AppletViewerPanel extends AppletPanel { + + /* Are we debugging? */ + static boolean debug = false; + + /** + * The document url. + */ + URL documentURL; + + /** + * The base url. + */ + URL baseURL; + + /** + * The attributes of the applet. + */ + Hashtable atts; + + /* + * JDK 1.1 serialVersionUID + */ + private static final long serialVersionUID = 8890989370785545619L; + + /** + * Construct an applet viewer and start the applet. + */ + AppletViewerPanel(URL documentURL, Hashtable atts) { + this.documentURL = documentURL; + this.atts = atts; + + String att = getParameter("codebase"); + if (att != null) { + if (!att.endsWith("/")) { + att += "/"; + } + try { + baseURL = new URL(documentURL, att); + } catch (MalformedURLException e) { + } + } + if (baseURL == null) { + String file = documentURL.getFile(); + int i = file.lastIndexOf('/'); + if (i >= 0 && i < file.length() - 1) { + try { + baseURL = new URL(documentURL, file.substring(0, i + 1)); + } catch (MalformedURLException e) { + } + } + } + + // when all is said & done, baseURL shouldn't be null + if (baseURL == null) + baseURL = documentURL; + + + } + + /** + * Get an applet parameter. + */ + public String getParameter(String name) { + return (String)atts.get(name.toLowerCase()); + } + + /** + * Get the document url. + */ + public URL getDocumentBase() { + return documentURL; + + } + + /** + * Get the base url. + */ + public URL getCodeBase() { + return baseURL; + } + + /** + * Get the width. + */ + public int getWidth() { + String w = getParameter("width"); + if (w != null) { + return Integer.valueOf(w).intValue(); + } + return 0; + } + + + /** + * Get the height. + */ + public int getHeight() { + String h = getParameter("height"); + if (h != null) { + return Integer.valueOf(h).intValue(); + } + return 0; + } + + /** + * Get initial_focus + */ + public boolean hasInitialFocus() + { + + // 6234219: Do not set initial focus on an applet + // during startup if applet is targeted for + // JDK 1.1/1.2. [stanley.ho] + if (isJDK11Applet() || isJDK12Applet()) + return false; + + String initialFocus = getParameter("initial_focus"); + + if (initialFocus != null) + { + if (initialFocus.toLowerCase().equals("false")) + return false; + } + + return true; + } + + /** + * Get the code parameter + */ + public String getCode() { + return getParameter("code"); + } + + + /** + * Return the list of jar files if specified. + * Otherwise return null. + */ + public String getJarFiles() { + return getParameter("archive"); + } + + /** + * Return the value of the object param + */ + public String getSerializedObject() { + return getParameter("object");// another name? + } + + + /** + * Get the applet context. For now this is + * also implemented by the AppletPanel class. + */ + public AppletContext getAppletContext() { + return (AppletContext)getParent(); + } + + static void debug(String s) { + if(debug) + System.err.println("AppletViewerPanel:::" + s); + } + + static void debug(String s, Throwable t) { + if(debug) { + t.printStackTrace(); + debug(s); + } + } +} diff --git a/src/sun/applet/Main.java b/src/sun/applet/Main.java new file mode 100644 index 00000000..73ad18ea --- /dev/null +++ b/src/sun/applet/Main.java @@ -0,0 +1,529 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.applet; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Vector; + +import sun.net.www.ParseUtil; + +/** + * The main entry point into AppletViewer. + */ +public class Main { + /** + * The file which contains all of the AppletViewer specific properties. + */ + static File theUserPropertiesFile; + + /** + * The default key/value pairs for the required user-specific properties. + */ + static final String [][] avDefaultUserProps = { + // There's a bootstrapping problem here. If we don't have a proxyHost, + // then we will not be able to connect to a URL outside the firewall; + // however, there's no way for us to set the proxyHost without starting + // AppletViewer. This problem existed before the re-write. + {"http.proxyHost", ""}, + {"http.proxyPort", "80"}, + {"package.restrict.access.sun", "true"} + }; + + static { + File userHome = new File(System.getProperty("user.home")); + // make sure we can write to this location + userHome.canWrite(); + + theUserPropertiesFile = new File(userHome, ".appletviewer"); + } + + // i18n + private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); + + /** + * Member variables set according to options passed in to AppletViewer. + */ + private boolean debugFlag = false; + private boolean helpFlag = false; + private String encoding = null; + private boolean noSecurityFlag = false; + private static boolean cmdLineTestFlag = false; + + /** + * The list of valid URLs passed in to AppletViewer. + */ + private static Vector urlList = new Vector(1); + + // This is used in init(). Getting rid of this is desirable but depends + // on whether the property that uses it is necessary/standard. + public static final String theVersion = System.getProperty("java.version"); + + /** + * The main entry point into AppletViewer. + */ + public static void main(String [] args) { + Main m = new Main(); + int ret = m.run(args); + + // Exit immediately if we got some sort of error along the way. + // For debugging purposes, if we have passed in "-XcmdLineTest" we + // force a premature exit. + if ((ret != 0) || (cmdLineTestFlag)) + System.exit(ret); + } + + private int run(String [] args) { + // DECODE ARGS + try { + if (args.length == 0) { + usage(); + return 0; + } + for (int i = 0; i < args.length; ) { + int j = decodeArg(args, i); + if (j == 0) { + throw new ParseException(lookup("main.err.unrecognizedarg", + args[i])); + } + i += j; + } + } catch (ParseException e) { + System.err.println(e.getMessage()); + return 1; + } + + // CHECK ARGUMENTS + if (helpFlag) { + usage(); + return 0; + } + + if (urlList.size() == 0) { + System.err.println(lookup("main.err.inputfile")); + return 1; + } + + if (debugFlag) { + // START A DEBUG SESSION + // Given the current architecture, we will end up decoding the + // arguments again, but at least we are guaranteed to have + // arguments which are valid. + return invokeDebugger(args); + } + + // INSTALL THE SECURITY MANAGER (if necessary) + if (!noSecurityFlag && (System.getSecurityManager() == null)) + init(); + + // LAUNCH APPLETVIEWER FOR EACH URL + for (int i = 0; i < urlList.size(); i++) { + try { + // XXX 5/17 this parsing method should be changed/fixed so that + // it doesn't do both parsing of the html file and launching of + // the AppletPanel + AppletViewer.parse((URL) urlList.elementAt(i), encoding); + } catch (IOException e) { + System.err.println(lookup("main.err.io", e.getMessage())); + return 1; + } + } + return 0; + } + + private static void usage() { + System.out.println(lookup("usage")); + } + + /** + * Decode a single argument in an array and return the number of elements + * used. + * + * @param args The array of arguments. + * @param i The argument to decode. + * @return The number of array elements used when the argument was + * decoded. + * @exception ParseException + * Thrown when there is a problem with something in the + * argument array. + */ + private int decodeArg(String [] args, int i) throws ParseException { + String arg = args[i]; + int argc = args.length; + + if ("-help".equalsIgnoreCase(arg) || "-?".equals(arg)) { + helpFlag = true; + return 1; + } else if ("-encoding".equals(arg) && (i < argc - 1)) { + if (encoding != null) + throw new ParseException(lookup("main.err.dupoption", arg)); + encoding = args[++i]; + return 2; + } else if ("-debug".equals(arg)) { + debugFlag = true; + return 1; + } else if ("-Xnosecurity".equals(arg)) { + // This is an undocumented (and, in the future, unsupported) + // flag which prevents AppletViewer from installing its own + // SecurityManager. + + System.err.println(); + System.err.println(lookup("main.warn.nosecmgr")); + System.err.println(); + + noSecurityFlag = true; + return 1; + } else if ("-XcmdLineTest".equals(arg)) { + // This is an internal flag which should be used for command-line + // testing. It instructs AppletViewer to force a premature exit + // immediately after the applet has been launched. + cmdLineTestFlag = true; + return 1; + } else if (arg.startsWith("-")) { + throw new ParseException(lookup("main.err.unsupportedopt", arg)); + } else { + // we found what we hope is a url + URL url = parseURL(arg); + if (url != null) { + urlList.addElement(url); + return 1; + } + } + return 0; + } + + /** + * Following the relevant RFC, construct a valid URL based on the passed in + * string. + * + * @param url a string which represents either a relative or absolute URL. + * @return a URL when the passed in string can be interpreted according + * to the RFC, null otherwise. + * @exception ParseException + * Thrown when we are unable to construct a proper URL from the + * passed in string. + */ + private URL parseURL(String url) throws ParseException { + URL u = null; + // prefix of the urls with 'file' scheme + String prefix = "file:"; + + try { + if (url.indexOf(':') <= 1) + { + // appletviewer accepts only unencoded filesystem paths + u = ParseUtil.fileToEncodedURL(new File(url)); + } else if (url.startsWith(prefix) && + url.length() != prefix.length() && + !(new File(url.substring(prefix.length())).isAbsolute())) + { + // relative file URL, like this "file:index.html" + // ensure that this file URL is absolute + // ParseUtil.fileToEncodedURL should be done last (see 6329251) + String path = ParseUtil.fileToEncodedURL(new File(System.getProperty("user.dir"))).getPath() + + url.substring(prefix.length()); + u = new URL("file", "", path); + } else { + // appletviewer accepts only encoded urls + u = new URL(url); + } + } catch (MalformedURLException e) { + throw new ParseException(lookup("main.err.badurl", + url, e.getMessage())); + } + + return u; + } + + /** + * Invoke the debugger with the arguments passed in to appletviewer. + * + * @param args The arguments passed into the debugger. + * @return 0 if the debugger is invoked successfully, + * 1 otherwise. + */ + private int invokeDebugger(String [] args) { + // CONSTRUCT THE COMMAND LINE + String [] newArgs = new String[args.length + 1]; + int current = 0; + + // Add a -classpath argument that prevents + // the debugger from launching appletviewer with the default of + // ".". appletviewer's classpath should never contain valid + // classes since they will result in security exceptions. + // Ideally, the classpath should be set to "", but the VM won't + // allow an empty classpath, so a phony directory name is used. + String phonyDir = System.getProperty("java.home") + + File.separator + "phony"; + newArgs[current++] = "-Djava.class.path=" + phonyDir; + + // Appletviewer's main class is the debuggee + newArgs[current++] = "sun.applet.Main"; + + // Append all the of the original appletviewer arguments, + // leaving out the "-debug" option. + for (int i = 0; i < args.length; i++) { + if (!("-debug".equals(args[i]))) { + newArgs[current++] = args[i]; + } + } + + // LAUNCH THE DEBUGGER + // Reflection is used for two reasons: + // 1) The debugger classes are on classpath and thus must be loaded + // by the application class loader. (Currently, appletviewer are + // loaded through the boot class path out of rt.jar.) + // 2) Reflection removes any build dependency between appletviewer + // and jdb. + try { + Class c = Class.forName("com.sun.tools.example.debug.tty.TTY", true, + ClassLoader.getSystemClassLoader()); + Method m = c.getDeclaredMethod("main", + new Class[] { String[].class }); + m.invoke(null, new Object[] { newArgs }); + } catch (ClassNotFoundException cnfe) { + System.err.println(lookup("main.debug.cantfinddebug")); + return 1; + } catch (NoSuchMethodException nsme) { + System.err.println(lookup("main.debug.cantfindmain")); + return 1; + } catch (InvocationTargetException ite) { + System.err.println(lookup("main.debug.exceptionindebug")); + return 1; + } catch (IllegalAccessException iae) { + System.err.println(lookup("main.debug.cantaccess")); + return 1; + } + return 0; + } + + private void init() { + // GET APPLETVIEWER USER-SPECIFIC PROPERTIES + Properties avProps = getAVProps(); + + // ADD OTHER RANDOM PROPERTIES + // XXX 5/18 need to revisit why these are here, is there some + // standard for what is available? + + // Standard browser properties + avProps.put("browser", "sun.applet.AppletViewer"); + avProps.put("browser.version", "1.06"); + avProps.put("browser.vendor", "Oracle Corporation"); + avProps.put("http.agent", "Java(tm) 2 SDK, Standard Edition v" + theVersion); + + // Define which packages can be extended by applets + // XXX 5/19 probably not needed, not checked in AppletSecurity + avProps.put("package.restrict.definition.java", "true"); + avProps.put("package.restrict.definition.sun", "true"); + + // Define which properties can be read by applets. + // A property named by "key" can be read only when its twin + // property "key.applet" is true. The following ten properties + // are open by default. Any other property can be explicitly + // opened up by the browser user by calling appletviewer with + // -J-Dkey.applet=true + avProps.put("java.version.applet", "true"); + avProps.put("java.vendor.applet", "true"); + avProps.put("java.vendor.url.applet", "true"); + avProps.put("java.class.version.applet", "true"); + avProps.put("os.name.applet", "true"); + avProps.put("os.version.applet", "true"); + avProps.put("os.arch.applet", "true"); + avProps.put("file.separator.applet", "true"); + avProps.put("path.separator.applet", "true"); + avProps.put("line.separator.applet", "true"); + + // Read in the System properties. If something is going to be + // over-written, warn about it. + Properties sysProps = System.getProperties(); + for (Enumeration e = sysProps.propertyNames(); e.hasMoreElements(); ) { + String key = (String) e.nextElement(); + String val = (String) sysProps.getProperty(key); + String oldVal; + if ((oldVal = (String) avProps.setProperty(key, val)) != null) + System.err.println(lookup("main.warn.prop.overwrite", key, + oldVal, val)); + } + + // INSTALL THE PROPERTY LIST + System.setProperties(avProps); + + // Create and install the security manager + if (!noSecurityFlag) { + System.setSecurityManager(new AppletSecurity()); + } else { + System.err.println(lookup("main.nosecmgr")); + } + + // REMIND: Create and install a socket factory! + } + + /** + * Read the AppletViewer user-specific properties. Typically, these + * properties should reside in the file $USER/.appletviewer. If this file + * does not exist, one will be created. Information for this file will + * be gleaned from $USER/.hotjava/properties. If that file does not exist, + * then default values will be used. + * + * @return A Properties object containing all of the AppletViewer + * user-specific properties. + */ + private Properties getAVProps() { + Properties avProps = new Properties(); + + File dotAV = theUserPropertiesFile; + if (dotAV.exists()) { + // we must have already done the conversion + if (dotAV.canRead()) { + // just read the file + avProps = getAVProps(dotAV); + } else { + // send out warning and use defaults + System.err.println(lookup("main.warn.cantreadprops", + dotAV.toString())); + avProps = setDefaultAVProps(); + } + } else { + // create the $USER/.appletviewer file + + // see if $USER/.hotjava/properties exists + File userHome = new File(System.getProperty("user.home")); + File dotHJ = new File(userHome, ".hotjava"); + dotHJ = new File(dotHJ, "properties"); + if (dotHJ.exists()) { + // just read the file + avProps = getAVProps(dotHJ); + } else { + // send out warning and use defaults + System.err.println(lookup("main.warn.cantreadprops", + dotHJ.toString())); + avProps = setDefaultAVProps(); + } + + // SAVE THE FILE + try (FileOutputStream out = new FileOutputStream(dotAV)) { + avProps.store(out, lookup("main.prop.store")); + } catch (IOException e) { + System.err.println(lookup("main.err.prop.cantsave", + dotAV.toString())); + } + } + return avProps; + } + + /** + * Set the AppletViewer user-specific properties to be the default values. + * + * @return A Properties object containing all of the AppletViewer + * user-specific properties, set to the default values. + */ + private Properties setDefaultAVProps() { + Properties avProps = new Properties(); + for (int i = 0; i < avDefaultUserProps.length; i++) { + avProps.setProperty(avDefaultUserProps[i][0], + avDefaultUserProps[i][1]); + } + return avProps; + } + + /** + * Given a file, find only the properties that are setable by AppletViewer. + * + * @param inFile A Properties file from which we select the properties of + * interest. + * @return A Properties object containing all of the AppletViewer + * user-specific properties. + */ + private Properties getAVProps(File inFile) { + Properties avProps = new Properties(); + + // read the file + Properties tmpProps = new Properties(); + try (FileInputStream in = new FileInputStream(inFile)) { + tmpProps.load(new BufferedInputStream(in)); + } catch (IOException e) { + System.err.println(lookup("main.err.prop.cantread", inFile.toString())); + } + + // pick off the properties we care about + for (int i = 0; i < avDefaultUserProps.length; i++) { + String value = tmpProps.getProperty(avDefaultUserProps[i][0]); + if (value != null) { + // the property exists in the file, so replace the default + avProps.setProperty(avDefaultUserProps[i][0], value); + } else { + // just use the default + avProps.setProperty(avDefaultUserProps[i][0], + avDefaultUserProps[i][1]); + } + } + return avProps; + } + + /** + * Methods for easier i18n handling. + */ + + private static String lookup(String key) { + return amh.getMessage(key); + } + + private static String lookup(String key, String arg0) { + return amh.getMessage(key, arg0); + } + + private static String lookup(String key, String arg0, String arg1) { + return amh.getMessage(key, arg0, arg1); + } + + private static String lookup(String key, String arg0, String arg1, + String arg2) { + return amh.getMessage(key, arg0, arg1, arg2); + } + + class ParseException extends RuntimeException + { + public ParseException(String msg) { + super(msg); + } + + public ParseException(Throwable t) { + super(t.getMessage()); + this.t = t; + } + + Throwable t = null; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer.java b/src/sun/applet/resources/MsgAppletViewer.java new file mode 100644 index 00000000..af1f3d5a --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Dismiss"}, + {"appletviewer.tool.title", "Applet Viewer: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Restart"}, + {"appletviewer.menuitem.reload", "Reload"}, + {"appletviewer.menuitem.stop", "Stop"}, + {"appletviewer.menuitem.save", "Save..."}, + {"appletviewer.menuitem.start", "Start"}, + {"appletviewer.menuitem.clone", "Clone..."}, + {"appletviewer.menuitem.tag", "Tag..."}, + {"appletviewer.menuitem.info", "Info..."}, + {"appletviewer.menuitem.edit", "Edit"}, + {"appletviewer.menuitem.encoding", "Character Encoding"}, + {"appletviewer.menuitem.print", "Print..."}, + {"appletviewer.menuitem.props", "Properties..."}, + {"appletviewer.menuitem.close", "Close"}, + {"appletviewer.menuitem.quit", "Quit"}, + {"appletviewer.label.hello", "Hello..."}, + {"appletviewer.status.start", "starting applet..."}, + {"appletviewer.appletsave.filedialogtitle","Serialize Applet into File"}, + {"appletviewer.appletsave.err1", "serializing an {0} to {1}"}, + {"appletviewer.appletsave.err2", "in appletSave: {0}"}, + {"appletviewer.applettag", "Tag shown"}, + {"appletviewer.applettag.textframe", "Applet HTML Tag"}, + {"appletviewer.appletinfo.applet", "-- no applet info --"}, + {"appletviewer.appletinfo.param", "-- no parameter info --"}, + {"appletviewer.appletinfo.textframe", "Applet Info"}, + {"appletviewer.appletprint.fail", "Printing failed."}, + {"appletviewer.appletprint.finish", "Finished printing."}, + {"appletviewer.appletprint.cancel", "Printing cancelled."}, + {"appletviewer.appletencoding", "Character Encoding: {0}"}, + {"appletviewer.parse.warning.requiresname", "Warning: tag requires name attribute."}, + {"appletviewer.parse.warning.paramoutside", "Warning: tag outside ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Warning: tag requires code attribute."}, + {"appletviewer.parse.warning.applet.requiresheight", "Warning: tag requires height attribute."}, + {"appletviewer.parse.warning.applet.requireswidth", "Warning: tag requires width attribute."}, + {"appletviewer.parse.warning.object.requirescode", "Warning: tag requires code attribute."}, + {"appletviewer.parse.warning.object.requiresheight", "Warning: tag requires height attribute."}, + {"appletviewer.parse.warning.object.requireswidth", "Warning: tag requires width attribute."}, + {"appletviewer.parse.warning.embed.requirescode", "Warning: tag requires code attribute."}, + {"appletviewer.parse.warning.embed.requiresheight", "Warning: tag requires height attribute."}, + {"appletviewer.parse.warning.embed.requireswidth", "Warning: tag requires width attribute."}, + {"appletviewer.parse.warning.appnotLongersupported", "Warning: tag no longer supported, use instead:"}, + {"appletviewer.usage", "Usage: appletviewer url(s)\n\nwhere include:\n -debug Start the applet viewer in the Java debugger\n -encoding Specify character encoding used by HTML files\n -J Pass argument to the java interpreter\n\nThe -J option is non-standard and subject to change without notice."}, + {"appletviewer.main.err.unsupportedopt", "Unsupported option: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Unrecognized argument: {0}"}, + {"appletviewer.main.err.dupoption", "Duplicate use of option: {0}"}, + {"appletviewer.main.err.inputfile", "No input files specified."}, + {"appletviewer.main.err.badurl", "Bad URL: {0} ( {1} )"}, + {"appletviewer.main.err.io", "I/O exception while reading: {0}"}, + {"appletviewer.main.err.readablefile", "Make sure that {0} is a file and is readable."}, + {"appletviewer.main.err.correcturl", "Is {0} the correct URL?"}, + {"appletviewer.main.prop.store", "User-specific properties for AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "Can''t read user properties file: {0}"}, + {"appletviewer.main.err.prop.cantsave", "Can''t save user properties file: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Warning: disabling security."}, + {"appletviewer.main.debug.cantfinddebug", "Can''t find the debugger!"}, + {"appletviewer.main.debug.cantfindmain", "Can''t find main method in the debugger!"}, + {"appletviewer.main.debug.exceptionindebug", "Exception in the debugger!"}, + {"appletviewer.main.debug.cantaccess", "Can''t access the debugger!"}, + {"appletviewer.main.nosecmgr", "Warning: SecurityManager not installed!"}, + {"appletviewer.main.warning", "Warning: No applets were started. Make sure the input contains an tag."}, + {"appletviewer.main.warn.prop.overwrite", "Warning: Temporarily overwriting system property at user''s request: key: {0} old value: {1} new value: {2}"}, + {"appletviewer.main.warn.cantreadprops", "Warning: Can''t read AppletViewer properties file: {0} Using defaults."}, + {"appletioexception.loadclass.throw.interrupted", "class loading interrupted: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "class not loaded: {0}"}, + {"appletclassloader.loadcode.verbose", "Opening stream to: {0} to get {1}"}, + {"appletclassloader.filenotfound", "File not found when looking for: {0}"}, + {"appletclassloader.fileformat", "File format exception when loading: {0}"}, + {"appletclassloader.fileioexception", "I/O exception when loading: {0}"}, + {"appletclassloader.fileexception", "{0} exception when loading: {1}"}, + {"appletclassloader.filedeath", "{0} killed when loading: {1}"}, + {"appletclassloader.fileerror", "{0} error when loading: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "Opening stream to: {0} to get {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource for name: {0}"}, + {"appletclassloader.getresource.verbose.found", "Found resource: {0} as a system resource"}, + {"appletclassloader.getresourceasstream.verbose", "Found resource: {0} as a system resource"}, + {"appletpanel.runloader.err", "Either object or code parameter!"}, + {"appletpanel.runloader.exception", "exception while deserializing {0}"}, + {"appletpanel.destroyed", "Applet destroyed."}, + {"appletpanel.loaded", "Applet loaded."}, + {"appletpanel.started", "Applet started."}, + {"appletpanel.inited", "Applet initialized."}, + {"appletpanel.stopped", "Applet stopped."}, + {"appletpanel.disposed", "Applet disposed."}, + {"appletpanel.nocode", "APPLET tag missing CODE parameter."}, + {"appletpanel.notfound", "load: class {0} not found."}, + {"appletpanel.nocreate", "load: {0} can''t be instantiated."}, + {"appletpanel.noconstruct", "load: {0} is not public or has no public constructor."}, + {"appletpanel.death", "killed"}, + {"appletpanel.exception", "exception: {0}."}, + {"appletpanel.exception2", "exception: {0}: {1}."}, + {"appletpanel.error", "error: {0}."}, + {"appletpanel.error2", "error: {0}: {1}."}, + {"appletpanel.notloaded", "Init: applet not loaded."}, + {"appletpanel.notinited", "Start: applet not initialized."}, + {"appletpanel.notstarted", "Stop: applet not started."}, + {"appletpanel.notstopped", "Destroy: applet not stopped."}, + {"appletpanel.notdestroyed", "Dispose: applet not destroyed."}, + {"appletpanel.notdisposed", "Load: applet not disposed."}, + {"appletpanel.bail", "Interrupted: bailing out."}, + {"appletpanel.filenotfound", "File not found when looking for: {0}"}, + {"appletpanel.fileformat", "File format exception when loading: {0}"}, + {"appletpanel.fileioexception", "I/O exception when loading: {0}"}, + {"appletpanel.fileexception", "{0} exception when loading: {1}"}, + {"appletpanel.filedeath", "{0} killed when loading: {1}"}, + {"appletpanel.fileerror", "{0} error when loading: {1}"}, + {"appletpanel.badattribute.exception", "HTML parsing: incorrect value for width/height attribute"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream requires non-null loader"}, + {"appletprops.title", "AppletViewer Properties"}, + {"appletprops.label.http.server", "Http proxy server:"}, + {"appletprops.label.http.proxy", "Http proxy port:"}, + {"appletprops.label.network", "Network access:"}, + {"appletprops.choice.network.item.none", "None"}, + {"appletprops.choice.network.item.applethost", "Applet Host"}, + {"appletprops.choice.network.item.unrestricted", "Unrestricted"}, + {"appletprops.label.class", "Class access:"}, + {"appletprops.choice.class.item.restricted", "Restricted"}, + {"appletprops.choice.class.item.unrestricted", "Unrestricted"}, + {"appletprops.label.unsignedapplet", "Allow unsigned applets:"}, + {"appletprops.choice.unsignedapplet.no", "No"}, + {"appletprops.choice.unsignedapplet.yes", "Yes"}, + {"appletprops.button.apply", "Apply"}, + {"appletprops.button.cancel", "Cancel"}, + {"appletprops.button.reset", "Reset"}, + {"appletprops.apply.exception", "Failed to save properties: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Invalid Entry"}, + {"appletprops.label.invalidproxy", "Proxy Port must be a positive integer value."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "User-specific properties for AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Security Exception: classloader"}, + {"appletsecurityexception.checkaccess.thread", "Security Exception: thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Security Exception: threadgroup: {0}"}, + {"appletsecurityexception.checkexit", "Security Exception: exit: {0}"}, + {"appletsecurityexception.checkexec", "Security Exception: exec: {0}"}, + {"appletsecurityexception.checklink", "Security Exception: link: {0}"}, + {"appletsecurityexception.checkpropsaccess", "Security Exception: properties"}, + {"appletsecurityexception.checkpropsaccess.key", "Security Exception: properties access {0}"}, + {"appletsecurityexception.checkread.exception1", "Security Exception: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Security Exception: file.read: {0}"}, + {"appletsecurityexception.checkread", "Security Exception: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Security Exception: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Security Exception: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Security Exception: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Security Exception: fd.write"}, + {"appletsecurityexception.checklisten", "Security Exception: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "Security Exception: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "Security Exception: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Security Exception: Couldn''t connect to {0} with origin from {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "Security Exception: Couldn''t resolve IP for host {0} or for {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Security Exception: Could not resolve IP for host {0}. See the trustProxy property."}, + {"appletsecurityexception.checkconnect", "Security Exception: connect: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "Security Exception: cannot access package: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Security Exception: cannot define package: {0}"}, + {"appletsecurityexception.cannotsetfactory", "Security Exception: cannot set factory"}, + {"appletsecurityexception.checkmemberaccess", "Security Exception: check member access"}, + {"appletsecurityexception.checkgetprintjob", "Security Exception: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Security Exception: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Security Exception: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Security Exception: security operation: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "unknown class loader type. unable to check for getContext"}, + {"appletsecurityexception.checkread.unknown", "unknown class loader type. unable to check for checking read {0}"}, + {"appletsecurityexception.checkconnect.unknown", "unknown class loader type. unable to check for checking connect"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_de.java b/src/sun/applet/resources/MsgAppletViewer_de.java new file mode 100644 index 00000000..cab08eb8 --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_de.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_de extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Verwerfen"}, + {"appletviewer.tool.title", "Applet Viewer: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Neu starten"}, + {"appletviewer.menuitem.reload", "Neu laden"}, + {"appletviewer.menuitem.stop", "Stoppen"}, + {"appletviewer.menuitem.save", "Speichern..."}, + {"appletviewer.menuitem.start", "Starten..."}, + {"appletviewer.menuitem.clone", "Clonen..."}, + {"appletviewer.menuitem.tag", "Tag..."}, + {"appletviewer.menuitem.info", "Informationen..."}, + {"appletviewer.menuitem.edit", "Bearbeiten"}, + {"appletviewer.menuitem.encoding", "Zeichencodierung"}, + {"appletviewer.menuitem.print", "Drucken..."}, + {"appletviewer.menuitem.props", "Eigenschaften..."}, + {"appletviewer.menuitem.close", "Schlie\u00DFen"}, + {"appletviewer.menuitem.quit", "Beenden"}, + {"appletviewer.label.hello", "Hallo..."}, + {"appletviewer.status.start", "Applet wird gestartet..."}, + {"appletviewer.appletsave.filedialogtitle","Applet in Datei serialisieren"}, + {"appletviewer.appletsave.err1", "{0} in {1} serialisieren"}, + {"appletviewer.appletsave.err2", "in appletSave: {0}"}, + {"appletviewer.applettag", "Angezeigtes Tag"}, + {"appletviewer.applettag.textframe", "Applet-HTML-Tag"}, + {"appletviewer.appletinfo.applet", "-- keine Applet-Informationen --"}, + {"appletviewer.appletinfo.param", "-- keine Parameterinformationen --"}, + {"appletviewer.appletinfo.textframe", "Applet-Informationen"}, + {"appletviewer.appletprint.fail", "Druck nicht erfolgreich."}, + {"appletviewer.appletprint.finish", "Druck abgeschlossen."}, + {"appletviewer.appletprint.cancel", "Druck abgebrochen."}, + {"appletviewer.appletencoding", "Zeichencodierung: {0}"}, + {"appletviewer.parse.warning.requiresname", "Warnung: F\u00FCr -Tag ist ein \"name\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.paramoutside", "Warnung: -Tag au\u00DFerhalb von ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Warnung: F\u00FCr -Tag ist ein \"code\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.applet.requiresheight", "Warnung: F\u00FCr -Tag ist ein \"height\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.applet.requireswidth", "Warnung: F\u00FCr -Tag ist ein \"width\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.object.requirescode", "Warnung: F\u00FCr -Tag ist ein \"code\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.object.requiresheight", "Warnung: F\u00FCr -Tag ist ein \"height\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.object.requireswidth", "Warnung: F\u00FCr -Tag ist ein \"width\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.embed.requirescode", "Warnung: F\u00FCr -Tag ist ein \"code\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.embed.requiresheight", "Warnung: F\u00FCr -Tag ist ein \"height\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.embed.requireswidth", "Warnung: F\u00FCr -Tag ist ein \"width\"-Attribut erforderlich."}, + {"appletviewer.parse.warning.appnotLongersupported", "Warnung: -Tag wird nicht mehr unterst\u00FCtzt. Verwenden Sie stattdessen :"}, + {"appletviewer.usage", "Verwendung: appletviewer url(s)\n\nwobei die Folgendes umfassen:\n -debug Applet Viewer im Java-Debugger starten\n -encoding Zeichencodierung f\u00FCr HTML-Dateien angeben\n -J Argument an den Java-Interpreter \u00FCbergeben\n\nDie Option \"-J\" ist nicht standardm\u00E4\u00DFig und kann ohne vorherige Ank\u00FCndigung ge\u00E4ndert werden."}, + {"appletviewer.main.err.unsupportedopt", "Nicht unterst\u00FCtzte Option: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Unbekanntes Argument: {0}"}, + {"appletviewer.main.err.dupoption", "Doppelte Verwendung von Option: {0}"}, + {"appletviewer.main.err.inputfile", "Keine Eingabedateien angegeben."}, + {"appletviewer.main.err.badurl", "Ung\u00FCltige URL: {0} ( {1} )"}, + {"appletviewer.main.err.io", "I/O-Ausnahme beim Lesen von: {0}"}, + {"appletviewer.main.err.readablefile", "Stellen Sie sicher, dass {0} eine lesbare Datei ist."}, + {"appletviewer.main.err.correcturl", "Ist {0} die richtige URL?"}, + {"appletviewer.main.prop.store", "Benutzerspezifische Eigenschaften f\u00FCr AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "Benutzereigenschaftendatei kann nicht gelesen werden: {0}"}, + {"appletviewer.main.err.prop.cantsave", "Benutzereigenschaftendatei kann nicht gespeichert werden: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Warnung: Sicherheit wird deaktiviert."}, + {"appletviewer.main.debug.cantfinddebug", "Debugger kann nicht gefunden werden."}, + {"appletviewer.main.debug.cantfindmain", "Hauptmethode im Debugger kann nicht gefunden werden."}, + {"appletviewer.main.debug.exceptionindebug", "Ausnahme im Debugger."}, + {"appletviewer.main.debug.cantaccess", "Zugriff auf Debugger nicht m\u00F6glich."}, + {"appletviewer.main.nosecmgr", "Warnung: SecurityManager nicht installiert."}, + {"appletviewer.main.warning", "Warnung: Es wurden keine Applets gestartet. Stellen Sie sicher, dass die Eingabe ein -Tag enth\u00E4lt."}, + {"appletviewer.main.warn.prop.overwrite", "Warnung: Systemeigenschaft wird tempor\u00E4r aufgrund von Benutzeranforderung \u00FCberschrieben: Schl\u00FCssel: {0} Alter Wert: {1} Neuer Wert: {2}"}, + {"appletviewer.main.warn.cantreadprops", "Warnung: AppletViewer-Eigenschaftendatei kann nicht gelesen werden: {0} Standardwerte werden verwendet."}, + {"appletioexception.loadclass.throw.interrupted", "Laden der Klasse unterbrochen: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "Klasse nicht geladen: {0}"}, + {"appletclassloader.loadcode.verbose", "\u00D6ffnen von Stream zu: {0}, um {1} abzurufen"}, + {"appletclassloader.filenotfound", "Datei nicht gefunden beim Suchen nach: {0}"}, + {"appletclassloader.fileformat", "Dateiformatausnahme beim Laden von: {0}"}, + {"appletclassloader.fileioexception", "I/O-Ausnahme beim Laden von: {0}"}, + {"appletclassloader.fileexception", "{0}-Ausnahme beim Laden von: {1}"}, + {"appletclassloader.filedeath", "{0} abgebrochen beim Laden von: {1}"}, + {"appletclassloader.fileerror", "{0}-Fehler beim Laden von: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "\u00D6ffnen von Stream zu: {0}, um {1} abzurufen"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource f\u00FCr Name: {0}"}, + {"appletclassloader.getresource.verbose.found", "Ressource {0} als Systemressource gefunden"}, + {"appletclassloader.getresourceasstream.verbose", "Ressource {0} als Systemressource gefunden"}, + {"appletpanel.runloader.err", "Objekt oder Codeparameter."}, + {"appletpanel.runloader.exception", "Ausnahme beim Deserialisieren von {0}"}, + {"appletpanel.destroyed", "Applet zerst\u00F6rt."}, + {"appletpanel.loaded", "Applet geladen."}, + {"appletpanel.started", "Applet gestartet."}, + {"appletpanel.inited", "Applet initialisiert."}, + {"appletpanel.stopped", "Applet gestoppt."}, + {"appletpanel.disposed", "Applet verworfen."}, + {"appletpanel.nocode", "Bei APPLET-Tag fehlt CODE-Parameter."}, + {"appletpanel.notfound", "Laden: Klasse {0} nicht gefunden."}, + {"appletpanel.nocreate", "Laden: {0} kann nicht instanziiert werden."}, + {"appletpanel.noconstruct", "Laden: {0} ist nicht \"public\" oder hat keinen \"public\"-Constructor."}, + {"appletpanel.death", "abgebrochen"}, + {"appletpanel.exception", "Ausnahme: {0}."}, + {"appletpanel.exception2", "Ausnahme: {0}: {1}."}, + {"appletpanel.error", "Fehler: {0}."}, + {"appletpanel.error2", "Fehler: {0}: {1}."}, + {"appletpanel.notloaded", "Init.: Applet nicht geladen."}, + {"appletpanel.notinited", "Starten: Applet nicht initialisiert."}, + {"appletpanel.notstarted", "Stoppen: Applet nicht gestartet."}, + {"appletpanel.notstopped", "Zerst\u00F6ren: Applet nicht gestoppt."}, + {"appletpanel.notdestroyed", "Verwerfen: Applet nicht zerst\u00F6rt."}, + {"appletpanel.notdisposed", "Laden: Applet nicht verworfen."}, + {"appletpanel.bail", "Unterbrochen: Zur\u00FCckziehen."}, + {"appletpanel.filenotfound", "Datei nicht gefunden beim Suchen nach: {0}"}, + {"appletpanel.fileformat", "Dateiformatausnahme beim Laden von: {0}"}, + {"appletpanel.fileioexception", "I/O-Ausnahme beim Laden von: {0}"}, + {"appletpanel.fileexception", "{0}-Ausnahme beim Laden von: {1}"}, + {"appletpanel.filedeath", "{0} abgebrochen beim Laden von: {1}"}, + {"appletpanel.fileerror", "{0}-Fehler beim Laden von: {1}"}, + {"appletpanel.badattribute.exception", "HTML-Parsing: Falscher Wert f\u00FCr \"width/height\"-Attribut"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream erfordert Loader ungleich null"}, + {"appletprops.title", "AppletViewer-Eigenschaften"}, + {"appletprops.label.http.server", "HTTP-Proxyserver:"}, + {"appletprops.label.http.proxy", "HTTP-Proxyport:"}, + {"appletprops.label.network", "Netzwerkzugriff:"}, + {"appletprops.choice.network.item.none", "Keine"}, + {"appletprops.choice.network.item.applethost", "Applet-Host"}, + {"appletprops.choice.network.item.unrestricted", "Uneingeschr\u00E4nkt"}, + {"appletprops.label.class", "Klassenzugriff:"}, + {"appletprops.choice.class.item.restricted", "Eingeschr\u00E4nkt"}, + {"appletprops.choice.class.item.unrestricted", "Uneingeschr\u00E4nkt"}, + {"appletprops.label.unsignedapplet", "Nicht signierte Applets zulassen:"}, + {"appletprops.choice.unsignedapplet.no", "Nein"}, + {"appletprops.choice.unsignedapplet.yes", "Ja"}, + {"appletprops.button.apply", "Anwenden"}, + {"appletprops.button.cancel", "Abbrechen"}, + {"appletprops.button.reset", "Zur\u00FCcksetzen"}, + {"appletprops.apply.exception", "Eigenschaften konnten nicht gespeichert werden: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Ung\u00FCltiger Eintrag"}, + {"appletprops.label.invalidproxy", "Proxyport muss ein positiver Ganzzahlwert sein."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "Benutzerspezifische Eigenschaften f\u00FCr AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Sicherheitsausnahme: Class Loader"}, + {"appletsecurityexception.checkaccess.thread", "Sicherheitsausnahme: Thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Sicherheitsausnahme: Threadgruppe: {0}"}, + {"appletsecurityexception.checkexit", "Sicherheitsausnahme: Beenden: {0}"}, + {"appletsecurityexception.checkexec", "Sicherheitsausnahme: Ausf\u00FChrung: {0}"}, + {"appletsecurityexception.checklink", "Sicherheitsausnahme: Link: {0}"}, + {"appletsecurityexception.checkpropsaccess", "Sicherheitsausnahme: Eigenschaften"}, + {"appletsecurityexception.checkpropsaccess.key", "Sicherheitsausnahme: Eigenschaftszugriff {0}"}, + {"appletsecurityexception.checkread.exception1", "Sicherheitsausnahme: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Sicherheitsausnahme: file.read: {0}"}, + {"appletsecurityexception.checkread", "Sicherheitsausnahme: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Sicherheitsausnahme: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Sicherheitsausnahme: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Sicherheitsausnahme: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Sicherheitsausnahme: fd.write"}, + {"appletsecurityexception.checklisten", "Sicherheitsausnahme: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "Sicherheitsausnahme: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "Sicherheitsausnahme: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Sicherheitsausnahme: Verbindung mit {0} mit Ursprung aus {1} konnte nicht hergestellt werden."}, + {"appletsecurityexception.checkconnect.networkhost2", "Sicherheitsausnahme: IP f\u00FCr Host {0} oder f\u00FCr {1} konnte nicht aufgel\u00F6st werden. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Sicherheitsausnahme: IP f\u00FCr Host {0} konnte nicht aufgel\u00F6st werden. Siehe trustProxy-Eigenschaft."}, + {"appletsecurityexception.checkconnect", "Sicherheitsausnahme: Verbinden: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "Sicherheitsausnahme: Zugriff auf Package nicht m\u00F6glich: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Sicherheitsausnahme: Package kann nicht definiert werden: {0}"}, + {"appletsecurityexception.cannotsetfactory", "Sicherheitsausnahme: Factory kann nicht festgelegt werden"}, + {"appletsecurityexception.checkmemberaccess", "Sicherheitsausnahme: Mitgliedszugriff pr\u00FCfen"}, + {"appletsecurityexception.checkgetprintjob", "Sicherheitsausnahme: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Sicherheitsausnahme: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Sicherheitsausnahme: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Sicherheitsausnahme: Sicherheitsvorgang: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "Unbekannter Class Loader-Typ. Pr\u00FCfen auf getContext nicht m\u00F6glich"}, + {"appletsecurityexception.checkread.unknown", "Unbekannter Class Loader-Typ. Pr\u00FCfen auf checkRead {0} nicht m\u00F6glich"}, + {"appletsecurityexception.checkconnect.unknown", "Unbekannter Class Loader-Typ. Pr\u00FCfen auf checkConnect nicht m\u00F6glich"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_es.java b/src/sun/applet/resources/MsgAppletViewer_es.java new file mode 100644 index 00000000..d282fd92 --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_es.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_es extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Descartar"}, + {"appletviewer.tool.title", "Visor de Applet: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Reiniciar"}, + {"appletviewer.menuitem.reload", "Volver a Cargar"}, + {"appletviewer.menuitem.stop", "Parar"}, + {"appletviewer.menuitem.save", "Guardar..."}, + {"appletviewer.menuitem.start", "Iniciar"}, + {"appletviewer.menuitem.clone", "Clonar..."}, + {"appletviewer.menuitem.tag", "Etiqueta..."}, + {"appletviewer.menuitem.info", "Informaci\u00F3n..."}, + {"appletviewer.menuitem.edit", "Editar"}, + {"appletviewer.menuitem.encoding", "Codificaci\u00F3n de Caracteres"}, + {"appletviewer.menuitem.print", "Imprimir..."}, + {"appletviewer.menuitem.props", "Propiedades..."}, + {"appletviewer.menuitem.close", "Cerrar"}, + {"appletviewer.menuitem.quit", "Salir"}, + {"appletviewer.label.hello", "Hola..."}, + {"appletviewer.status.start", "iniciando applet..."}, + {"appletviewer.appletsave.filedialogtitle","Serializar Applet en Archivo"}, + {"appletviewer.appletsave.err1", "serializando {0} en {1}"}, + {"appletviewer.appletsave.err2", "en appletSave: {0}"}, + {"appletviewer.applettag", "Etiqueta Mostrada"}, + {"appletviewer.applettag.textframe", "Etiqueta HTML de Applet"}, + {"appletviewer.appletinfo.applet", "-- ninguna informaci\u00F3n de applet --"}, + {"appletviewer.appletinfo.param", "-- ninguna informaci\u00F3n de par\u00E1metros --"}, + {"appletviewer.appletinfo.textframe", "Informaci\u00F3n del Applet"}, + {"appletviewer.appletprint.fail", "Fallo de impresi\u00F3n."}, + {"appletviewer.appletprint.finish", "Impresi\u00F3n terminada."}, + {"appletviewer.appletprint.cancel", "Impresi\u00F3n cancelada."}, + {"appletviewer.appletencoding", "Codificaci\u00F3n de Caracteres: {0}"}, + {"appletviewer.parse.warning.requiresname", "Advertencia: la etiqueta requiere un atributo name."}, + {"appletviewer.parse.warning.paramoutside", "Advertencia: la etiqueta est\u00E1 fuera de ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Advertencia: la etiqueta requiere el atributo code."}, + {"appletviewer.parse.warning.applet.requiresheight", "Advertencia: la etiqueta requiere el atributo height."}, + {"appletviewer.parse.warning.applet.requireswidth", "Advertencia: la etiqueta requiere el atributo width."}, + {"appletviewer.parse.warning.object.requirescode", "Advertencia: la etiqueta requiere el atributo code."}, + {"appletviewer.parse.warning.object.requiresheight", "Advertencia: la etiqueta requiere el atributo height."}, + {"appletviewer.parse.warning.object.requireswidth", "Advertencia: la etiqueta requiere el atributo width."}, + {"appletviewer.parse.warning.embed.requirescode", "Advertencia: la etiqueta requiere el atributo code."}, + {"appletviewer.parse.warning.embed.requiresheight", "Advertencia: la etiqueta requiere el atributo height."}, + {"appletviewer.parse.warning.embed.requireswidth", "Advertencia: la etiqueta requiere el atributo width."}, + {"appletviewer.parse.warning.appnotLongersupported", "Advertencia: la etiqueta ya no est\u00E1 soportada, utilice en su lugar:"}, + {"appletviewer.usage", "Sintaxis: appletviewer url(s)\n\ndonde incluye:\n -debug Iniciar el visor de applet en el depurador Java\n -encoding Especificar la codificaci\u00F3n de caracteres utilizada por los archivos HTML\n -J Transferir argumento al int\u00E9rprete de Java\n\nLa opci\u00F3n -J es no est\u00E1ndar y est\u00E1 sujeta a cambios sin previo aviso."}, + {"appletviewer.main.err.unsupportedopt", "Opci\u00F3n no soportada: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Argumento no reconocido: {0}"}, + {"appletviewer.main.err.dupoption", "Uso duplicado de la opci\u00F3n: {0}"}, + {"appletviewer.main.err.inputfile", "No se ha especificado ning\u00FAn archivo de entrada."}, + {"appletviewer.main.err.badurl", "URL Err\u00F3nea: {0} ( {1} )"}, + {"appletviewer.main.err.io", "Excepci\u00F3n de E/S durante la lectura: {0}"}, + {"appletviewer.main.err.readablefile", "Aseg\u00FArese de que {0} es un archivo y que se puede leer."}, + {"appletviewer.main.err.correcturl", "\u00BFEs {0} la URL correcta?"}, + {"appletviewer.main.prop.store", "Propiedades Espec\u00EDficas del Usuario para AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "No se puede leer el archivo de propiedades del usuario: {0}"}, + {"appletviewer.main.err.prop.cantsave", "No se puede guardar el archivo de propiedades del usuario: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Advertencia: desactivando seguridad."}, + {"appletviewer.main.debug.cantfinddebug", "No se ha encontrado el depurador."}, + {"appletviewer.main.debug.cantfindmain", "No se ha encontrado el m\u00E9todo principal en el depurador."}, + {"appletviewer.main.debug.exceptionindebug", "Excepci\u00F3n en el depurador."}, + {"appletviewer.main.debug.cantaccess", "No se puede acceder al depurador."}, + {"appletviewer.main.nosecmgr", "Advertencia: no se ha instalado SecurityManager."}, + {"appletviewer.main.warning", "Advertencia: no se ha iniciado ning\u00FAn applet. Aseg\u00FArese de que la entrada contiene una etiqueta ."}, + {"appletviewer.main.warn.prop.overwrite", "Advertencia: se sobrescribir\u00E1 temporalmente la propiedad del sistema cuando lo solicite el usuario: clave: {0} valor anterior: {1} nuevo valor: {2}"}, + {"appletviewer.main.warn.cantreadprops", "Advertencia: no se puede leer el archivo de propiedades de AppletViewer: {0}. Utilizando valores por defecto."}, + {"appletioexception.loadclass.throw.interrupted", "carga de clase interrumpida: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "clase no cargada: {0}"}, + {"appletclassloader.loadcode.verbose", "Abriendo flujo a: {0} para obtener {1}"}, + {"appletclassloader.filenotfound", "No se ha encontrado el archivo al buscar: {0}"}, + {"appletclassloader.fileformat", "Excepci\u00F3n de formato de archivo al cargar: {0}"}, + {"appletclassloader.fileioexception", "Excepci\u00F3n de E/S al cargar: {0}"}, + {"appletclassloader.fileexception", "Excepci\u00F3n de {0} al cargar: {1}"}, + {"appletclassloader.filedeath", "{0} interrumpido al cargar: {1}"}, + {"appletclassloader.fileerror", "error de {0} al cargar: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "Abriendo flujo a: {0} para obtener {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource para nombre: {0}"}, + {"appletclassloader.getresource.verbose.found", "Recurso encontrado: {0} como un recurso de sistema"}, + {"appletclassloader.getresourceasstream.verbose", "Recurso encontrado: {0} como un recurso de sistema"}, + {"appletpanel.runloader.err", "Par\u00E1metro de c\u00F3digo u objeto."}, + {"appletpanel.runloader.exception", "excepci\u00F3n al deserializar {0}"}, + {"appletpanel.destroyed", "Applet destruido."}, + {"appletpanel.loaded", "Applet cargado."}, + {"appletpanel.started", "Applet iniciado."}, + {"appletpanel.inited", "Applet inicializado."}, + {"appletpanel.stopped", "Applet parado."}, + {"appletpanel.disposed", "Applet desechado."}, + {"appletpanel.nocode", "Falta el par\u00E1metro CODE en la etiqueta APPLET."}, + {"appletpanel.notfound", "cargar: clase {0} no encontrada."}, + {"appletpanel.nocreate", "cargar: {0} no se puede instanciar."}, + {"appletpanel.noconstruct", "cargar: {0} no es p\u00FAblico o no tiene un constructor p\u00FAblico."}, + {"appletpanel.death", "interrumpido"}, + {"appletpanel.exception", "excepci\u00F3n: {0}."}, + {"appletpanel.exception2", "excepci\u00F3n: {0}: {1}."}, + {"appletpanel.error", "error: {0}."}, + {"appletpanel.error2", "error: {0}: {1}."}, + {"appletpanel.notloaded", "Iniciaci\u00F3n: applet no cargado."}, + {"appletpanel.notinited", "Iniciar: applet no inicializado."}, + {"appletpanel.notstarted", "Parar: applet no iniciado."}, + {"appletpanel.notstopped", "Destruir: applet no parado."}, + {"appletpanel.notdestroyed", "Desechar: applet no destruido."}, + {"appletpanel.notdisposed", "Cargar: applet no desechado."}, + {"appletpanel.bail", "Interrumpido: rescatando."}, + {"appletpanel.filenotfound", "No se ha encontrado el archivo al buscar: {0}"}, + {"appletpanel.fileformat", "Excepci\u00F3n de formato de archivo al cargar: {0}"}, + {"appletpanel.fileioexception", "Excepci\u00F3n de E/S al cargar: {0}"}, + {"appletpanel.fileexception", "Excepci\u00F3n de {0} al cargar: {1}"}, + {"appletpanel.filedeath", "{0} interrumpido al cargar: {1}"}, + {"appletpanel.fileerror", "error de {0} al cargar: {1}"}, + {"appletpanel.badattribute.exception", "An\u00E1lisis HTML: valor incorrecto para el atributo width/height."}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream requiere un cargador no nulo"}, + {"appletprops.title", "Propiedades de AppletViewer"}, + {"appletprops.label.http.server", "Servidor Proxy HTTP:"}, + {"appletprops.label.http.proxy", "Puerto Proxy HTTP:"}, + {"appletprops.label.network", "Acceso de Red:"}, + {"appletprops.choice.network.item.none", "Ninguno"}, + {"appletprops.choice.network.item.applethost", "Host del Applet"}, + {"appletprops.choice.network.item.unrestricted", "No Restringido"}, + {"appletprops.label.class", "Acceso de Clase:"}, + {"appletprops.choice.class.item.restricted", "Restringido"}, + {"appletprops.choice.class.item.unrestricted", "No Restringido"}, + {"appletprops.label.unsignedapplet", "Permitir Applets no Firmados:"}, + {"appletprops.choice.unsignedapplet.no", "No"}, + {"appletprops.choice.unsignedapplet.yes", "S\u00ED"}, + {"appletprops.button.apply", "Aplicar"}, + {"appletprops.button.cancel", "Cancelar"}, + {"appletprops.button.reset", "Restablecer"}, + {"appletprops.apply.exception", "Fallo al guardar las propiedades: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Entrada no V\u00E1lida"}, + {"appletprops.label.invalidproxy", "El puerto proxy debe ser un valor entero positivo."}, + {"appletprops.button.ok", "Aceptar"}, + /* end 4066432 */ + {"appletprops.prop.store", "Propiedades espec\u00EDficas del usuario para AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Excepci\u00F3n de Seguridad: classloader"}, + {"appletsecurityexception.checkaccess.thread", "Excepci\u00F3n de Seguridad: thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Excepci\u00F3n de Seguridad: threadgroup: {0}"}, + {"appletsecurityexception.checkexit", "Excepci\u00F3n de Seguridad: salir: {0}"}, + {"appletsecurityexception.checkexec", "Excepci\u00F3n de Seguridad: ejecutar: {0}"}, + {"appletsecurityexception.checklink", "Excepci\u00F3n de Seguridad: enlace: {0}"}, + {"appletsecurityexception.checkpropsaccess", "Excepci\u00F3n de Seguridad: propiedades"}, + {"appletsecurityexception.checkpropsaccess.key", "Excepci\u00F3n de Seguridad: acceso a propiedades {0}"}, + {"appletsecurityexception.checkread.exception1", "Excepci\u00F3n de Seguridad: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Excepci\u00F3n de Seguridad: file.read: {0}"}, + {"appletsecurityexception.checkread", "Excepci\u00F3n de Seguridad: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Excepci\u00F3n de Seguridad: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Excepci\u00F3n de Seguridad: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Excepci\u00F3n de Seguridad: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Excepci\u00F3n de Seguridad: fd.write"}, + {"appletsecurityexception.checklisten", "Excepci\u00F3n de Seguridad: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "Excepci\u00F3n de Seguridad: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "Excepci\u00F3n de Seguridad: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Excepci\u00F3n de Seguridad: no se puede conectar a {0} con origen de {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "Excepci\u00F3n de Seguridad: no se puede resolver la IP para el host {0} o para {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Excepci\u00F3n de Seguridad: no se puede resolver la IP para el host {0}. Consulte la propiedad trustProxy."}, + {"appletsecurityexception.checkconnect", "Excepci\u00F3n de Seguridad: conexi\u00F3n: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "Excepci\u00F3n de Seguridad: no se puede acceder al paquete: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Excepci\u00F3n de Seguridad: no se puede definir el paquete: {0}"}, + {"appletsecurityexception.cannotsetfactory", "Excepci\u00F3n de Seguridad: no se puede definir el valor de f\u00E1brica"}, + {"appletsecurityexception.checkmemberaccess", "Excepci\u00F3n de Seguridad: comprobar el acceso de miembro"}, + {"appletsecurityexception.checkgetprintjob", "Excepci\u00F3n de Seguridad: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Excepci\u00F3n de Seguridad: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Excepci\u00F3n de Seguridad: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Excepci\u00F3n de Seguridad: operaci\u00F3n de seguridad: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "tipo de cargador de clase desconocido. no se puede comprobar para getContext"}, + {"appletsecurityexception.checkread.unknown", "tipo de cargador de clase desconocido. no se puede comprobar para lectura de comprobaci\u00F3n {0}"}, + {"appletsecurityexception.checkconnect.unknown", "tipo de cargador de clase desconocido. no se puede comprobar para conexi\u00F3n de comprobaci\u00F3n"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_fr.java b/src/sun/applet/resources/MsgAppletViewer_fr.java new file mode 100644 index 00000000..cc6e257b --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_fr.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_fr extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Abandonner"}, + {"appletviewer.tool.title", "Visualiseur d''applets : {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Red\u00E9marrer"}, + {"appletviewer.menuitem.reload", "Recharger"}, + {"appletviewer.menuitem.stop", "Arr\u00EAter"}, + {"appletviewer.menuitem.save", "Enregistrer..."}, + {"appletviewer.menuitem.start", "D\u00E9marrer"}, + {"appletviewer.menuitem.clone", "Cloner..."}, + {"appletviewer.menuitem.tag", "Baliser..."}, + {"appletviewer.menuitem.info", "Informations..."}, + {"appletviewer.menuitem.edit", "Modifier"}, + {"appletviewer.menuitem.encoding", "Encodage de caract\u00E8res"}, + {"appletviewer.menuitem.print", "Imprimer..."}, + {"appletviewer.menuitem.props", "Propri\u00E9t\u00E9s..."}, + {"appletviewer.menuitem.close", "Fermer"}, + {"appletviewer.menuitem.quit", "Quitter"}, + {"appletviewer.label.hello", "Bonjour..."}, + {"appletviewer.status.start", "d\u00E9marrage de l'applet..."}, + {"appletviewer.appletsave.filedialogtitle","S\u00E9rialiser l'applet dans le fichier"}, + {"appletviewer.appletsave.err1", "S\u00E9rialisation de {0} vers {1}"}, + {"appletviewer.appletsave.err2", "dans appletSave : {0}"}, + {"appletviewer.applettag", "Balise affich\u00E9e"}, + {"appletviewer.applettag.textframe", "Balise HTML d'applet"}, + {"appletviewer.appletinfo.applet", "-- aucune information d'applet --"}, + {"appletviewer.appletinfo.param", "-- aucune information de param\u00E8tre --"}, + {"appletviewer.appletinfo.textframe", "Informations d'applet"}, + {"appletviewer.appletprint.fail", "Echec de l'impression."}, + {"appletviewer.appletprint.finish", "Impression termin\u00E9e."}, + {"appletviewer.appletprint.cancel", "Impression annul\u00E9e."}, + {"appletviewer.appletencoding", "Encodage de caract\u00E8res : {0}"}, + {"appletviewer.parse.warning.requiresname", "Avertissement : la balise requiert un attribut de nom."}, + {"appletviewer.parse.warning.paramoutside", "Avertissement : la balise est en dehors des balises ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Avertissement : la balise requiert un attribut de code."}, + {"appletviewer.parse.warning.applet.requiresheight", "Avertissement : la balise requiert un attribut de hauteur."}, + {"appletviewer.parse.warning.applet.requireswidth", "Avertissement : la balise requiert un attribut de largeur."}, + {"appletviewer.parse.warning.object.requirescode", "Avertissement : la balise requiert un attribut de code."}, + {"appletviewer.parse.warning.object.requiresheight", "Avertissement : la balise requiert un attribut de hauteur."}, + {"appletviewer.parse.warning.object.requireswidth", "Avertissement : la balise requiert un attribut de largeur."}, + {"appletviewer.parse.warning.embed.requirescode", "Avertissement : la balise requiert un attribut de code."}, + {"appletviewer.parse.warning.embed.requiresheight", "Avertissement : la balise requiert un attribut de hauteur."}, + {"appletviewer.parse.warning.embed.requireswidth", "Avertissement : la balise requiert un attribut de largeur."}, + {"appletviewer.parse.warning.appnotLongersupported", "Avertissement : la balise n'est plus prise en charge, utilisez \u00E0 la place :"}, + {"appletviewer.usage", "Syntaxe : appletviewer url(s)\n\no\u00F9 inclut :\n -debug D\u00E9marrer le visualiseur d'applets dans le d\u00E9bogueur Java\n -encoding Indiquer l'encodage de caract\u00E8res utilis\u00E9 par les fichiers HTML\n -J Transmettre l'argument \u00E0 l'interpr\u00E9teur Java\n\nL'option -J n'est pas standard et elle peut \u00EAtre modifi\u00E9e sans pr\u00E9avis."}, + {"appletviewer.main.err.unsupportedopt", "Option non prise en charge : {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Argument non reconnu : {0}"}, + {"appletviewer.main.err.dupoption", "Utilisation en double de l''option : {0}"}, + {"appletviewer.main.err.inputfile", "Aucun fichier d'entr\u00E9e indiqu\u00E9."}, + {"appletviewer.main.err.badurl", "URL incorrecte : {0} ({1})"}, + {"appletviewer.main.err.io", "Exception d''E/S lors de la lecture : {0}"}, + {"appletviewer.main.err.readablefile", "Assurez-vous que {0} est un fichier accessible en lecture."}, + {"appletviewer.main.err.correcturl", "L''\u00E9l\u00E9ment {0} est-il l''URL correcte ?"}, + {"appletviewer.main.prop.store", "Propri\u00E9t\u00E9s utilisateur pour AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "Impossible de lire le fichier de propri\u00E9t\u00E9s utilisateur : {0}"}, + {"appletviewer.main.err.prop.cantsave", "Impossible d''enregistrer le fichier de propri\u00E9t\u00E9s utilisateur : {0}"}, + {"appletviewer.main.warn.nosecmgr", "Avertissement : d\u00E9sactivation de la s\u00E9curit\u00E9."}, + {"appletviewer.main.debug.cantfinddebug", "D\u00E9bogueur introuvable."}, + {"appletviewer.main.debug.cantfindmain", "La m\u00E9thode principale est introuvable dans le d\u00E9bogueur."}, + {"appletviewer.main.debug.exceptionindebug", "Exception d\u00E9tect\u00E9e dans le d\u00E9bogueur."}, + {"appletviewer.main.debug.cantaccess", "Impossible d'acc\u00E9der au d\u00E9bogueur."}, + {"appletviewer.main.nosecmgr", "Avertissement : SecurityManager n'est pas install\u00E9."}, + {"appletviewer.main.warning", "Avertissement : aucune applet n'a \u00E9t\u00E9 d\u00E9marr\u00E9e. Assurez-vous que l'entr\u00E9e contient une balise ."}, + {"appletviewer.main.warn.prop.overwrite", "Avertissement : remplacement temporaire de la propri\u00E9t\u00E9 syst\u00E8me \u00E0 la demande de l''utilisateur - Cl\u00E9 : {0}, ancienne valeur : {1}, nouvelle valeur : {2}"}, + {"appletviewer.main.warn.cantreadprops", "Avertissement : impossible de lire le fichier de propri\u00E9t\u00E9s d''AppletViewer : {0} Utilisation des valeurs par d\u00E9faut."}, + {"appletioexception.loadclass.throw.interrupted", "chargement de classe interrompu : {0}"}, + {"appletioexception.loadclass.throw.notloaded", "classe non charg\u00E9e : {0}"}, + {"appletclassloader.loadcode.verbose", "Ouverture du flux de donn\u00E9es dans {0} pour obtenir {1}"}, + {"appletclassloader.filenotfound", "Fichier introuvable lors de la recherche de {0}"}, + {"appletclassloader.fileformat", "Exception de format de fichier d\u00E9tect\u00E9e lors du chargement de : {0}"}, + {"appletclassloader.fileioexception", "Exception d''E/S lors du chargement de : {0}"}, + {"appletclassloader.fileexception", "Exception {0} lors du chargement de : {1}"}, + {"appletclassloader.filedeath", "Fermeture de {0} lors du chargement de : {1}"}, + {"appletclassloader.fileerror", "Erreur {0} lors du chargement de : {1}"}, + {"appletclassloader.findclass.verbose.openstream", "Ouverture du flux de donn\u00E9es dans {0} pour obtenir {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource pour le nom : {0}"}, + {"appletclassloader.getresource.verbose.found", "Ressource {0} trouv\u00E9e en tant que ressource syst\u00E8me"}, + {"appletclassloader.getresourceasstream.verbose", "Ressource {0} trouv\u00E9e en tant que ressource syst\u00E8me"}, + {"appletpanel.runloader.err", "Param\u00E8tre d'objet ou de code."}, + {"appletpanel.runloader.exception", "exception lors de la d\u00E9s\u00E9rialisation de {0}"}, + {"appletpanel.destroyed", "Applet d\u00E9truite."}, + {"appletpanel.loaded", "Applet charg\u00E9e."}, + {"appletpanel.started", "Applet d\u00E9marr\u00E9e."}, + {"appletpanel.inited", "Applet initialis\u00E9e."}, + {"appletpanel.stopped", "Applet arr\u00EAt\u00E9e."}, + {"appletpanel.disposed", "Applet \u00E9limin\u00E9e."}, + {"appletpanel.nocode", "Param\u00E8tre CODE manquant dans la balise APPLET."}, + {"appletpanel.notfound", "Charger : la classe {0} est introuvable."}, + {"appletpanel.nocreate", "Charger : impossible d''instantier {0}."}, + {"appletpanel.noconstruct", "Charger : l''\u00E9l\u00E9ment {0} n''est pas public ou ne poss\u00E8de aucun constructeur public."}, + {"appletpanel.death", "arr\u00EAt\u00E9"}, + {"appletpanel.exception", "exception : {0}."}, + {"appletpanel.exception2", "exception : {0} : {1}."}, + {"appletpanel.error", "erreur : {0}."}, + {"appletpanel.error2", "erreur : {0} : {1}."}, + {"appletpanel.notloaded", "Initialiser : applet non charg\u00E9e."}, + {"appletpanel.notinited", "D\u00E9marrer : applet non initialis\u00E9e."}, + {"appletpanel.notstarted", "Arr\u00EAter : applet non d\u00E9marr\u00E9e."}, + {"appletpanel.notstopped", "D\u00E9truire : applet non arr\u00EAt\u00E9e."}, + {"appletpanel.notdestroyed", "Eliminer : applet non d\u00E9truite."}, + {"appletpanel.notdisposed", "Charger : applet non \u00E9limin\u00E9e."}, + {"appletpanel.bail", "Interrompu : r\u00E9solution."}, + {"appletpanel.filenotfound", "Fichier introuvable lors de la recherche de {0}"}, + {"appletpanel.fileformat", "Exception de format de fichier d\u00E9tect\u00E9e lors du chargement de : {0}"}, + {"appletpanel.fileioexception", "Exception d''E/S lors du chargement de : {0}"}, + {"appletpanel.fileexception", "Exception {0} lors du chargement de : {1}"}, + {"appletpanel.filedeath", "Fermeture de {0} lors du chargement de : {1}"}, + {"appletpanel.fileerror", "Erreur {0} lors du chargement de : {1}"}, + {"appletpanel.badattribute.exception", "Analyse HTML : valeur incorrecte pour l'attribut de largeur/hauteur"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream requiert un chargeur non NULL"}, + {"appletprops.title", "Propri\u00E9t\u00E9s d'AppletViewer"}, + {"appletprops.label.http.server", "Serveur proxy HTTP :"}, + {"appletprops.label.http.proxy", "Port proxy HTTP :"}, + {"appletprops.label.network", "Acc\u00E8s au r\u00E9seau :"}, + {"appletprops.choice.network.item.none", "Aucun"}, + {"appletprops.choice.network.item.applethost", "H\u00F4te de l'applet"}, + {"appletprops.choice.network.item.unrestricted", "Sans restriction"}, + {"appletprops.label.class", "Acc\u00E8s \u00E0 la classe :"}, + {"appletprops.choice.class.item.restricted", "Avec restriction"}, + {"appletprops.choice.class.item.unrestricted", "Sans restriction"}, + {"appletprops.label.unsignedapplet", "Autoriser les applets non sign\u00E9es :"}, + {"appletprops.choice.unsignedapplet.no", "Non"}, + {"appletprops.choice.unsignedapplet.yes", "Oui"}, + {"appletprops.button.apply", "Appliquer"}, + {"appletprops.button.cancel", "Annuler"}, + {"appletprops.button.reset", "R\u00E9initialiser"}, + {"appletprops.apply.exception", "Echec de l''enregistrement des propri\u00E9t\u00E9s : {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Entr\u00E9e non valide"}, + {"appletprops.label.invalidproxy", "Le port proxy doit \u00EAtre un entier positif."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "Propri\u00E9t\u00E9s utilisateur pour AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Exception de s\u00E9curit\u00E9 : chargeur de classe"}, + {"appletsecurityexception.checkaccess.thread", "Exception de s\u00E9curit\u00E9 : thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Exception de s\u00E9curit\u00E9 : groupe de threads : {0}"}, + {"appletsecurityexception.checkexit", "Exception de s\u00E9curit\u00E9 : sortie : {0}"}, + {"appletsecurityexception.checkexec", "Exception de s\u00E9curit\u00E9 : ex\u00E9cution : {0}"}, + {"appletsecurityexception.checklink", "Exception de s\u00E9curit\u00E9 : lien : {0}"}, + {"appletsecurityexception.checkpropsaccess", "Exception de s\u00E9curit\u00E9 : propri\u00E9t\u00E9s"}, + {"appletsecurityexception.checkpropsaccess.key", "Exception de s\u00E9curit\u00E9 : acc\u00E8s aux propri\u00E9t\u00E9s {0}"}, + {"appletsecurityexception.checkread.exception1", "Exception de s\u00E9curit\u00E9 : {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Exception de s\u00E9curit\u00E9 : file.read : {0}"}, + {"appletsecurityexception.checkread", "Exception de s\u00E9curit\u00E9 : file.read : {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Exception de s\u00E9curit\u00E9 : {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Exception de s\u00E9curit\u00E9 : file.write : {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Exception de s\u00E9curit\u00E9 : fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Exception de s\u00E9curit\u00E9 : fd.write"}, + {"appletsecurityexception.checklisten", "Exception de s\u00E9curit\u00E9 : socket.listen : {0}"}, + {"appletsecurityexception.checkaccept", "Exception de s\u00E9curit\u00E9 : socket.accept : {0} : {1}"}, + {"appletsecurityexception.checkconnect.networknone", "Exception de s\u00E9curit\u00E9 : socket.connect : {0} -> {1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Exception de s\u00E9curit\u00E9 : impossible de se connecter \u00E0 {0} dont l''origine est {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "Exception de s\u00E9curit\u00E9 : impossible de r\u00E9soudre l''adresse IP pour l''h\u00F4te {0} ou pour {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Exception de s\u00E9curit\u00E9 : impossible de r\u00E9soudre l''adresse IP pour l''h\u00F4te {0}. Voir la propri\u00E9t\u00E9 trustProxy."}, + {"appletsecurityexception.checkconnect", "Exception de s\u00E9curit\u00E9 : connexion : {0} -> {1}"}, + {"appletsecurityexception.checkpackageaccess", "Exception de s\u00E9curit\u00E9 : impossible d''acc\u00E9der au package : {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Exception de s\u00E9curit\u00E9 : impossible de d\u00E9finir le package : {0}"}, + {"appletsecurityexception.cannotsetfactory", "Exception de s\u00E9curit\u00E9 : impossible de d\u00E9finir la fabrique"}, + {"appletsecurityexception.checkmemberaccess", "Exception de s\u00E9curit\u00E9 : v\u00E9rifier l'acc\u00E8s des membres"}, + {"appletsecurityexception.checkgetprintjob", "Exception de s\u00E9curit\u00E9 : getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Exception de s\u00E9curit\u00E9 : getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Exception de s\u00E9curit\u00E9 : getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Exception de s\u00E9curit\u00E9 : op\u00E9ration de s\u00E9curit\u00E9 : {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "type de chargeur de classe inconnu, impossible de rechercher getContext"}, + {"appletsecurityexception.checkread.unknown", "type de chargeur de classe inconnu, impossible de rechercher la v\u00E9rification de lecture {0}"}, + {"appletsecurityexception.checkconnect.unknown", "type de chargeur de classe inconnu, impossible de rechercher la v\u00E9rification de connexion"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_it.java b/src/sun/applet/resources/MsgAppletViewer_it.java new file mode 100644 index 00000000..1e666a8d --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_it.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_it extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Chiudi"}, + {"appletviewer.tool.title", "Visualizzatore applet: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Riavvia"}, + {"appletviewer.menuitem.reload", "Ricarica"}, + {"appletviewer.menuitem.stop", "Arresta"}, + {"appletviewer.menuitem.save", "Salva..."}, + {"appletviewer.menuitem.start", "Avvia"}, + {"appletviewer.menuitem.clone", "Copia..."}, + {"appletviewer.menuitem.tag", "Tag..."}, + {"appletviewer.menuitem.info", "Informazioni..."}, + {"appletviewer.menuitem.edit", "Modifica"}, + {"appletviewer.menuitem.encoding", "Codifica caratteri"}, + {"appletviewer.menuitem.print", "Stampa..."}, + {"appletviewer.menuitem.props", "Propriet\u00E0..."}, + {"appletviewer.menuitem.close", "Chiudi"}, + {"appletviewer.menuitem.quit", "Esci"}, + {"appletviewer.label.hello", "Benvenuti..."}, + {"appletviewer.status.start", "avvio applet in corso..."}, + {"appletviewer.appletsave.filedialogtitle","Serializza applet in file"}, + {"appletviewer.appletsave.err1", "serializzazione di {0} in {1}"}, + {"appletviewer.appletsave.err2", "in appletSave: {0}"}, + {"appletviewer.applettag", "Tag visualizzata"}, + {"appletviewer.applettag.textframe", "Applet tag HTML"}, + {"appletviewer.appletinfo.applet", "-- nessuna informazione sull'applet --"}, + {"appletviewer.appletinfo.param", "-- nessuna informazione sul parametro --"}, + {"appletviewer.appletinfo.textframe", "Informazioni applet"}, + {"appletviewer.appletprint.fail", "Stampa non riuscita."}, + {"appletviewer.appletprint.finish", "Stampa completata."}, + {"appletviewer.appletprint.cancel", "Stampa annullata."}, + {"appletviewer.appletencoding", "Codifica caratteri: {0}"}, + {"appletviewer.parse.warning.requiresname", "Avvertenza: la tag richiede un attributo name."}, + {"appletviewer.parse.warning.paramoutside", "Avvertenza: la tag non rientra in ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Avvertenza: la tag richiede un attributo code."}, + {"appletviewer.parse.warning.applet.requiresheight", "Avvertenza: la tag richiede un attributo height."}, + {"appletviewer.parse.warning.applet.requireswidth", "Avvertenza: la tag richiede un attributo width."}, + {"appletviewer.parse.warning.object.requirescode", "Avvertenza: la tag richiede un attributo code."}, + {"appletviewer.parse.warning.object.requiresheight", "Avvertenza: la tag richiede un attributo height."}, + {"appletviewer.parse.warning.object.requireswidth", "Avvertenza: la tag richiede un attributo width."}, + {"appletviewer.parse.warning.embed.requirescode", "Avvertenza: la tag richiede un attributo code."}, + {"appletviewer.parse.warning.embed.requiresheight", "Avvertenza: la tag richiede un attributo height."}, + {"appletviewer.parse.warning.embed.requireswidth", "Avvertenza: la tag richiede un attributo width."}, + {"appletviewer.parse.warning.appnotLongersupported", "Avvertenza: la tag non \u00E8 pi\u00F9 supportata. Utilizzare :"}, + {"appletviewer.usage", "Uso: appletviewer url(s)\n\ndove includono:\n -debug Avvia il visualizzatore applet nel debugger Java\n -encoding Specifica la codifica dei caratteri utilizzata dai file HTML\n -J Passa l'argomento all'interpreter Java\n\nL'opzione -J non \u00E8 standard ed \u00E8 soggetta a modifica senza preavviso."}, + {"appletviewer.main.err.unsupportedopt", "Opzione non supportata: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Argomento non riconosciuto: {0}"}, + {"appletviewer.main.err.dupoption", "Uso duplicato dell''opzione: {0}"}, + {"appletviewer.main.err.inputfile", "Nessun file di input specificato."}, + {"appletviewer.main.err.badurl", "URL non valido: {0} ( {1} )"}, + {"appletviewer.main.err.io", "Eccezione I/O durante la lettura di {0}"}, + {"appletviewer.main.err.readablefile", "Assicurarsi che {0} sia un file e che sia leggibile."}, + {"appletviewer.main.err.correcturl", "{0} \u00E8 l''URL corretto?"}, + {"appletviewer.main.prop.store", "Propriet\u00E0 specifiche dell'utente per AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "Impossibile leggere il file delle propriet\u00E0 utente: {0}"}, + {"appletviewer.main.err.prop.cantsave", "Impossibile salvare il file delle propriet\u00E0 utente: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Avvertenza: la sicurezza verr\u00E0 disabilitata."}, + {"appletviewer.main.debug.cantfinddebug", "Impossibile trovare il debugger."}, + {"appletviewer.main.debug.cantfindmain", "Impossibile trovare il metodo principale nel debugger."}, + {"appletviewer.main.debug.exceptionindebug", "Eccezione nel debugger."}, + {"appletviewer.main.debug.cantaccess", "Impossibile accedere al debugger."}, + {"appletviewer.main.nosecmgr", "Avvertenza: SecurityManager non installato."}, + {"appletviewer.main.warning", "Avvertenza: nessuna applet avviata. Assicurarsi che l'input contenga una tag ."}, + {"appletviewer.main.warn.prop.overwrite", "Avvertenza: la propriet\u00E0 di sistema verr\u00E0 sovrascritta temporaneamente su richiesta dell''utente. Chiave {0}, valore precedente {1}, nuovo valore {2}."}, + {"appletviewer.main.warn.cantreadprops", "Avvertenza: impossibile leggere il file delle propriet\u00E0 AppletViewer {0}. Verranno utilizzate le impostazioni predefinite."}, + {"appletioexception.loadclass.throw.interrupted", "caricamento della classe interrotto: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "classe non caricata: {0}"}, + {"appletclassloader.loadcode.verbose", "Apertura del flusso per {0} per recuperare {1}"}, + {"appletclassloader.filenotfound", "File non trovato durante la ricerca di {0}"}, + {"appletclassloader.fileformat", "Eccezione di formato file durante il caricamento di {0}"}, + {"appletclassloader.fileioexception", "Eccezione I/O durante il caricamento di {0}"}, + {"appletclassloader.fileexception", "Eccezione {0} durante il caricamento di {1}"}, + {"appletclassloader.filedeath", "{0} terminato durante il caricamento di {1}"}, + {"appletclassloader.fileerror", "Errore {0} durante il caricamento di {1}"}, + {"appletclassloader.findclass.verbose.openstream", "Apertura del flusso per {0} per recuperare {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource per il nome: {0}"}, + {"appletclassloader.getresource.verbose.found", "\u00C8 stata trovata la risorsa {0} come risorsa di sistema"}, + {"appletclassloader.getresourceasstream.verbose", "\u00C8 stata trovata la risorsa {0} come risorsa di sistema"}, + {"appletpanel.runloader.err", "Parametro di oggetto o di codice."}, + {"appletpanel.runloader.exception", "eccezione durante la deserializzazione di {0}"}, + {"appletpanel.destroyed", "Applet rimossa."}, + {"appletpanel.loaded", "Applet caricata."}, + {"appletpanel.started", "Applet avviata."}, + {"appletpanel.inited", "Applet inizializzata."}, + {"appletpanel.stopped", "Applet arrestata."}, + {"appletpanel.disposed", "Applet eliminata."}, + {"appletpanel.nocode", "Nella tag APPLET manca il parametro CODE."}, + {"appletpanel.notfound", "caricamento: classe {0} non trovata."}, + {"appletpanel.nocreate", "caricamento: impossibile creare un''istanza di {0}."}, + {"appletpanel.noconstruct", "caricamento: {0} non \u00E8 pubblico o non ha un costruttore pubblico."}, + {"appletpanel.death", "terminato"}, + {"appletpanel.exception", "eccezione: {0}"}, + {"appletpanel.exception2", "eccezione: {0}: {1}."}, + {"appletpanel.error", "errore: {0}."}, + {"appletpanel.error2", "errore: {0}: {1}."}, + {"appletpanel.notloaded", "Inizializzazione: applet non caricata."}, + {"appletpanel.notinited", "Avvio: applet non inizializzata."}, + {"appletpanel.notstarted", "Arresto: applet non avviata."}, + {"appletpanel.notstopped", "Rimozione: applet non arrestata."}, + {"appletpanel.notdestroyed", "Eliminazione: applet non rimossa."}, + {"appletpanel.notdisposed", "Caricamento: applet non eliminata."}, + {"appletpanel.bail", "Interrotto: chiusura."}, + {"appletpanel.filenotfound", "File non trovato durante la ricerca di {0}"}, + {"appletpanel.fileformat", "Eccezione di formato file durante il caricamento di {0}"}, + {"appletpanel.fileioexception", "Eccezione I/O durante il caricamento di {0}"}, + {"appletpanel.fileexception", "Eccezione {0} durante il caricamento di {1}"}, + {"appletpanel.filedeath", "{0} terminato durante il caricamento di {1}"}, + {"appletpanel.fileerror", "Errore {0} durante il caricamento di {1}"}, + {"appletpanel.badattribute.exception", "Analisi HTML: valore errato per l'attributo width/height"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream richiede un loader non nullo"}, + {"appletprops.title", "Propriet\u00E0 AppletViewer"}, + {"appletprops.label.http.server", "Server proxy http:"}, + {"appletprops.label.http.proxy", "Porta proxy http:"}, + {"appletprops.label.network", "Accesso alla rete:"}, + {"appletprops.choice.network.item.none", "Nessuno"}, + {"appletprops.choice.network.item.applethost", "Host applet"}, + {"appletprops.choice.network.item.unrestricted", "Non limitato"}, + {"appletprops.label.class", "Accesso alla classe:"}, + {"appletprops.choice.class.item.restricted", "Limitato"}, + {"appletprops.choice.class.item.unrestricted", "Non limitato"}, + {"appletprops.label.unsignedapplet", "Consenti applet senza firma:"}, + {"appletprops.choice.unsignedapplet.no", "No"}, + {"appletprops.choice.unsignedapplet.yes", "S\u00EC"}, + {"appletprops.button.apply", "Applica"}, + {"appletprops.button.cancel", "Annulla"}, + {"appletprops.button.reset", "Reimposta"}, + {"appletprops.apply.exception", "Salvataggio delle propriet\u00E0 non riuscito: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Voce non valida"}, + {"appletprops.label.invalidproxy", "La porta del proxy deve essere un valore intero positivo."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "Propriet\u00E0 specifiche dell'utente per AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Eccezione di sicurezza: classloader"}, + {"appletsecurityexception.checkaccess.thread", "Eccezione di sicurezza: thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Eccezione di sicurezza: threadgroup: {0}"}, + {"appletsecurityexception.checkexit", "Eccezione di sicurezza: exit: {0}"}, + {"appletsecurityexception.checkexec", "Eccezione di sicurezza: exec: {0}"}, + {"appletsecurityexception.checklink", "Eccezione di sicurezza: link: {0}"}, + {"appletsecurityexception.checkpropsaccess", "Eccezione di sicurezza: properties"}, + {"appletsecurityexception.checkpropsaccess.key", "Eccezione di sicurezza: properties access {0}"}, + {"appletsecurityexception.checkread.exception1", "Eccezione di sicurezza: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Eccezione di sicurezza: file.read: {0}"}, + {"appletsecurityexception.checkread", "Eccezione di sicurezza: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Eccezione di sicurezza: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Eccezione di sicurezza: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Eccezione di sicurezza: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Eccezione di sicurezza: fd.write"}, + {"appletsecurityexception.checklisten", "Eccezione di sicurezza: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "Eccezione di sicurezza: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "Eccezione di sicurezza: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Eccezione di sicurezza: impossibile connettersi a {0} con origine da {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "Eccezione di sicurezza: impossibile risolvere l''IP per l''host {0} o per {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Eccezione di sicurezza: impossibile non risolvere l''IP per l''host {0}. Vedere la propriet\u00E0 trustProxy."}, + {"appletsecurityexception.checkconnect", "Eccezione di sicurezza: connect: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "Eccezione di sicurezza: impossibile accedere al package {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Eccezione di sicurezza: impossibile definire il package {0}"}, + {"appletsecurityexception.cannotsetfactory", "Eccezione di sicurezza: impossibile impostare il factory"}, + {"appletsecurityexception.checkmemberaccess", "Eccezione di sicurezza: controllare l'accesso dei membri"}, + {"appletsecurityexception.checkgetprintjob", "Eccezione di sicurezza: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Eccezione di sicurezza: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Eccezione di sicurezza: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Eccezione di sicurezza: operazione di sicurezza {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "tipo di loader della classe sconosciuto. Impossibile verificare la presenza di getContext."}, + {"appletsecurityexception.checkread.unknown", "tipo di loader della classe sconosciuto. Impossibile verificare la presenza della lettura di controllo {0}."}, + {"appletsecurityexception.checkconnect.unknown", "tipo di loader della classe sconosciuto. Impossibile verificare la presenza della connessione di controllo."}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_ja.java b/src/sun/applet/resources/MsgAppletViewer_ja.java new file mode 100644 index 00000000..70647782 --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_ja.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_ja extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "\u53D6\u6D88"}, + {"appletviewer.tool.title", "\u30A2\u30D7\u30EC\u30C3\u30C8\u30FB\u30D3\u30E5\u30FC\u30A2: {0}"}, + {"appletviewer.menu.applet", "\u30A2\u30D7\u30EC\u30C3\u30C8"}, + {"appletviewer.menuitem.restart", "\u518D\u8D77\u52D5"}, + {"appletviewer.menuitem.reload", "\u518D\u30ED\u30FC\u30C9"}, + {"appletviewer.menuitem.stop", "\u505C\u6B62"}, + {"appletviewer.menuitem.save", "\u4FDD\u5B58..."}, + {"appletviewer.menuitem.start", "\u958B\u59CB"}, + {"appletviewer.menuitem.clone", "\u30AF\u30ED\u30FC\u30F3..."}, + {"appletviewer.menuitem.tag", "\u30BF\u30B0..."}, + {"appletviewer.menuitem.info", "\u60C5\u5831..."}, + {"appletviewer.menuitem.edit", "\u7DE8\u96C6"}, + {"appletviewer.menuitem.encoding", "\u6587\u5B57\u30A8\u30F3\u30B3\u30FC\u30C7\u30A3\u30F3\u30B0"}, + {"appletviewer.menuitem.print", "\u5370\u5237..."}, + {"appletviewer.menuitem.props", "\u30D7\u30ED\u30D1\u30C6\u30A3..."}, + {"appletviewer.menuitem.close", "\u9589\u3058\u308B"}, + {"appletviewer.menuitem.quit", "\u7D42\u4E86"}, + {"appletviewer.label.hello", "Hello..."}, + {"appletviewer.status.start", "\u30A2\u30D7\u30EC\u30C3\u30C8\u3092\u958B\u59CB\u3057\u3066\u3044\u307E\u3059..."}, + {"appletviewer.appletsave.filedialogtitle","\u30A2\u30D7\u30EC\u30C3\u30C8\u3092\u30D5\u30A1\u30A4\u30EB\u306B\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA"}, + {"appletviewer.appletsave.err1", "{0}\u3092{1}\u306B\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA"}, + {"appletviewer.appletsave.err2", "appletSave\u5185: {0}"}, + {"appletviewer.applettag", "\u30BF\u30B0\u306E\u8868\u793A"}, + {"appletviewer.applettag.textframe", "\u30A2\u30D7\u30EC\u30C3\u30C8HTML\u30BF\u30B0"}, + {"appletviewer.appletinfo.applet", "-- \u30A2\u30D7\u30EC\u30C3\u30C8\u60C5\u5831\u306A\u3057 --"}, + {"appletviewer.appletinfo.param", "-- \u30D1\u30E9\u30E1\u30FC\u30BF\u60C5\u5831\u306A\u3057 --"}, + {"appletviewer.appletinfo.textframe", "\u30A2\u30D7\u30EC\u30C3\u30C8\u60C5\u5831"}, + {"appletviewer.appletprint.fail", "\u5370\u5237\u304C\u5931\u6557\u3057\u307E\u3057\u305F\u3002"}, + {"appletviewer.appletprint.finish", "\u5370\u5237\u3092\u7D42\u4E86\u3057\u307E\u3057\u305F\u3002"}, + {"appletviewer.appletprint.cancel", "\u5370\u5237\u304C\u53D6\u308A\u6D88\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletviewer.appletencoding", "\u6587\u5B57\u30A8\u30F3\u30B3\u30FC\u30C7\u30A3\u30F3\u30B0: {0}"}, + {"appletviewer.parse.warning.requiresname", "\u8B66\u544A: \u30BF\u30B0\u306Bname\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.paramoutside", "\u8B66\u544A: \u30BF\u30B0\u304C ... \u306E\u5916\u5074\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.applet.requirescode", "\u8B66\u544A: \u30BF\u30B0\u306Bcode\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.applet.requiresheight", "\u8B66\u544A: \u30BF\u30B0\u306Bheight\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.applet.requireswidth", "\u8B66\u544A: \u30BF\u30B0\u306Bwidth\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.object.requirescode", "\u8B66\u544A: \u30BF\u30B0\u306Bcode\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.object.requiresheight", "\u8B66\u544A: \u30BF\u30B0\u306Bheight\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.object.requireswidth", "\u8B66\u544A: \u30BF\u30B0\u306Bwidth\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.embed.requirescode", "\u8B66\u544A: \u30BF\u30B0\u306Bcode\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.embed.requiresheight", "\u8B66\u544A: \u30BF\u30B0\u306Bheight\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.embed.requireswidth", "\u8B66\u544A: \u30BF\u30B0\u306Bwidth\u5C5E\u6027\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"appletviewer.parse.warning.appnotLongersupported", "\u8B66\u544A: \u30BF\u30B0\u306F\u73FE\u5728\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u304B\u308F\u308A\u306B\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"appletviewer.usage", "\u4F7F\u7528\u65B9\u6CD5: appletviewer url(s)\n\n\u306B\u306F\u6B21\u306E\u3082\u306E\u304C\u3042\u308A\u307E\u3059:\n -debug Java\u30C7\u30D0\u30C3\u30AC\u3067\u30A2\u30D7\u30EC\u30C3\u30C8\u30FB\u30D3\u30E5\u30FC\u30A2\u3092\u958B\u59CB\u3059\u308B\n -encoding HTML\u30D5\u30A1\u30A4\u30EB\u306B\u3088\u3063\u3066\u4F7F\u7528\u3055\u308C\u308B\u6587\u5B57\u30A8\u30F3\u30B3\u30FC\u30C7\u30A3\u30F3\u30B0\u3092\u6307\u5B9A\u3059\u308B\n -J \u5F15\u6570\u3092Java\u30A4\u30F3\u30BF\u30D7\u30EA\u30BF\u306B\u6E21\u3059\n\n-J\u306F\u975E\u6A19\u6E96\u30AA\u30D7\u30B7\u30E7\u30F3\u3067\u3042\u308A\u3001\u4E88\u544A\u306A\u3057\u306B\u5909\u66F4\u3055\u308C\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"appletviewer.main.err.unsupportedopt", "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u30AA\u30D7\u30B7\u30E7\u30F3: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "\u8A8D\u8B58\u3055\u308C\u306A\u3044\u5F15\u6570: {0}"}, + {"appletviewer.main.err.dupoption", "\u30AA\u30D7\u30B7\u30E7\u30F3\u306E\u4F7F\u7528\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059: {0}"}, + {"appletviewer.main.err.inputfile", "\u5165\u529B\u30D5\u30A1\u30A4\u30EB\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletviewer.main.err.badurl", "\u4E0D\u6B63\u306AURL: {0} ( {1} )"}, + {"appletviewer.main.err.io", "\u8AAD\u8FBC\u307F\u4E2D\u306E\u5165\u51FA\u529B\u4F8B\u5916\u3067\u3059: {0}"}, + {"appletviewer.main.err.readablefile", "{0}\u304C\u30D5\u30A1\u30A4\u30EB\u3067\u3042\u308A\u3001\u8AAD\u8FBC\u307F\u53EF\u80FD\u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"appletviewer.main.err.correcturl", "{0}\u306F\u6B63\u3057\u3044URL\u3067\u3059\u304B\u3002"}, + {"appletviewer.main.prop.store", "AppletViewer\u7528\u306E\u30E6\u30FC\u30B6\u30FC\u304C\u6307\u5B9A\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3"}, + {"appletviewer.main.err.prop.cantread", "\u30E6\u30FC\u30B6\u30FC\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093: {0}"}, + {"appletviewer.main.err.prop.cantsave", "\u30E6\u30FC\u30B6\u30FC\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093: {0}"}, + {"appletviewer.main.warn.nosecmgr", "\u8B66\u544A: \u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002"}, + {"appletviewer.main.debug.cantfinddebug", "\u30C7\u30D0\u30C3\u30AC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002"}, + {"appletviewer.main.debug.cantfindmain", "\u30C7\u30D0\u30C3\u30AC\u306E\u30E1\u30A4\u30F3\u30FB\u30E1\u30BD\u30C3\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002"}, + {"appletviewer.main.debug.exceptionindebug", "\u30C7\u30D0\u30C3\u30AC\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002"}, + {"appletviewer.main.debug.cantaccess", "\u30C7\u30D0\u30C3\u30AC\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093\u3002"}, + {"appletviewer.main.nosecmgr", "\u8B66\u544A: SecurityManager\u304C\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletviewer.main.warning", "\u8B66\u544A: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u958B\u59CB\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5165\u529B\u306B\u30BF\u30B0\u304C\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"appletviewer.main.warn.prop.overwrite", "\u8B66\u544A: \u30E6\u30FC\u30B6\u30FC\u306E\u30EA\u30AF\u30A8\u30B9\u30C8\u3067\u30B7\u30B9\u30C6\u30E0\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u4E00\u6642\u7684\u306B\u4E0A\u66F8\u304D\u3057\u307E\u3059: \u30AD\u30FC: {0} \u53E4\u3044\u5024: {1} \u65B0\u3057\u3044\u5024: {2}"}, + {"appletviewer.main.warn.cantreadprops", "\u8B66\u544A: AppletViewer\u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30D5\u30A1\u30A4\u30EB{0}\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u3092\u4F7F\u7528\u3057\u307E\u3059\u3002"}, + {"appletioexception.loadclass.throw.interrupted", "\u30AF\u30E9\u30B9\u306E\u30ED\u30FC\u30C9\u304C\u4E2D\u65AD\u3057\u307E\u3057\u305F: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "\u30AF\u30E9\u30B9\u304C\u30ED\u30FC\u30C9\u3055\u308C\u307E\u305B\u3093: {0}"}, + {"appletclassloader.loadcode.verbose", "{1}\u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306E{0}\u3078\u306E\u30B9\u30C8\u30EA\u30FC\u30E0\u3092\u958B\u304D\u307E\u3059"}, + {"appletclassloader.filenotfound", "{0}\u306E\u691C\u7D22\u4E2D\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093"}, + {"appletclassloader.fileformat", "{0}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletclassloader.fileioexception", "{0}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B\u5165\u51FA\u529B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletclassloader.fileexception", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletclassloader.filedeath", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u304C\u5F37\u5236\u7D42\u4E86\u3057\u307E\u3057\u305F"}, + {"appletclassloader.fileerror", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletclassloader.findclass.verbose.openstream", "{1}\u3092\u53D6\u5F97\u3059\u308B\u305F\u3081\u306E{0}\u3078\u306E\u30B9\u30C8\u30EA\u30FC\u30E0\u3092\u958B\u304D\u307E\u3059"}, + {"appletclassloader.getresource.verbose.forname", "\u540D\u524D{0}\u306EAppletClassLoader.getResource\u3067\u3059"}, + {"appletclassloader.getresource.verbose.found", "\u30EA\u30BD\u30FC\u30B9{0}\u304C\u30B7\u30B9\u30C6\u30E0\u30FB\u30EA\u30BD\u30FC\u30B9\u3068\u3057\u3066\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"appletclassloader.getresourceasstream.verbose", "\u30EA\u30BD\u30FC\u30B9{0}\u304C\u30B7\u30B9\u30C6\u30E0\u30FB\u30EA\u30BD\u30FC\u30B9\u3068\u3057\u3066\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"appletpanel.runloader.err", "\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u307E\u305F\u306F\u30B3\u30FC\u30C9\u30FB\u30D1\u30E9\u30E1\u30FC\u30BF\u306E\u3044\u305A\u308C\u304B\u3067\u3059\u3002"}, + {"appletpanel.runloader.exception", "{0}\u306E\u30C7\u30B7\u30EA\u30A2\u30E9\u30A4\u30BA\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletpanel.destroyed", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u7834\u68C4\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.loaded", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u30ED\u30FC\u30C9\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.started", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u958B\u59CB\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.inited", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u521D\u671F\u5316\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.stopped", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u505C\u6B62\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.disposed", "\u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u7834\u68C4\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"appletpanel.nocode", "APPLET\u30BF\u30B0\u306BCODE\u30D1\u30E9\u30E1\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093\u3002"}, + {"appletpanel.notfound", "\u30ED\u30FC\u30C9: \u30AF\u30E9\u30B9{0}\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002"}, + {"appletpanel.nocreate", "\u30ED\u30FC\u30C9: {0}\u3092\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u5316\u3067\u304D\u307E\u305B\u3093\u3002"}, + {"appletpanel.noconstruct", "\u30ED\u30FC\u30C9: {0}\u306Fpublic\u3067\u306A\u3044\u304B\u3001public\u30B3\u30F3\u30B9\u30C8\u30E9\u30AF\u30BF\u3092\u6301\u3063\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.death", "\u5F37\u5236\u7D42\u4E86\u3055\u308C\u307E\u3057\u305F"}, + {"appletpanel.exception", "\u4F8B\u5916: {0}\u3002"}, + {"appletpanel.exception2", "\u4F8B\u5916: {0}: {1}\u3002"}, + {"appletpanel.error", "\u30A8\u30E9\u30FC: {0}\u3002"}, + {"appletpanel.error2", "\u30A8\u30E9\u30FC: {0}: {1}\u3002"}, + {"appletpanel.notloaded", "\u521D\u671F\u5316: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u30ED\u30FC\u30C9\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.notinited", "\u958B\u59CB: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.notstarted", "\u505C\u6B62: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u958B\u59CB\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.notstopped", "\u7834\u68C4: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u505C\u6B62\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.notdestroyed", "\u7834\u68C4: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u7834\u68C4\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.notdisposed", "\u30ED\u30FC\u30C9: \u30A2\u30D7\u30EC\u30C3\u30C8\u304C\u7834\u68C4\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"appletpanel.bail", "\u4E2D\u65AD\u6E08: \u7D42\u4E86\u3057\u3066\u3044\u307E\u3059\u3002"}, + {"appletpanel.filenotfound", "{0}\u306E\u691C\u7D22\u4E2D\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093"}, + {"appletpanel.fileformat", "{0}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletpanel.fileioexception", "{0}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B\u5165\u51FA\u529B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletpanel.fileexception", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletpanel.filedeath", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u304C\u5F37\u5236\u7D42\u4E86\u3057\u307E\u3057\u305F"}, + {"appletpanel.fileerror", "{1}\u306E\u30ED\u30FC\u30C9\u4E2D\u306B{0}\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}, + {"appletpanel.badattribute.exception", "HTML\u89E3\u6790: width\u307E\u305F\u306Fheight\u5C5E\u6027\u306E\u5024\u304C\u4E0D\u6B63\u3067\u3059"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream\u306F\u975Enull\u306E\u30ED\u30FC\u30C0\u30FC\u304C\u5FC5\u8981\u3067\u3059"}, + {"appletprops.title", "AppletViewer\u30D7\u30ED\u30D1\u30C6\u30A3"}, + {"appletprops.label.http.server", "Http\u30D7\u30ED\u30AD\u30B7\u30FB\u30B5\u30FC\u30D0\u30FC:"}, + {"appletprops.label.http.proxy", "Http\u30D7\u30ED\u30AD\u30B7\u30FB\u30DD\u30FC\u30C8:"}, + {"appletprops.label.network", "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30FB\u30A2\u30AF\u30BB\u30B9:"}, + {"appletprops.choice.network.item.none", "\u306A\u3057"}, + {"appletprops.choice.network.item.applethost", "\u30A2\u30D7\u30EC\u30C3\u30C8\u30FB\u30DB\u30B9\u30C8"}, + {"appletprops.choice.network.item.unrestricted", "\u5236\u9650\u306A\u3057"}, + {"appletprops.label.class", "\u30AF\u30E9\u30B9\u30FB\u30A2\u30AF\u30BB\u30B9:"}, + {"appletprops.choice.class.item.restricted", "\u5236\u9650\u4ED8\u304D"}, + {"appletprops.choice.class.item.unrestricted", "\u5236\u9650\u306A\u3057"}, + {"appletprops.label.unsignedapplet", "\u7F72\u540D\u3055\u308C\u3066\u3044\u306A\u3044\u30A2\u30D7\u30EC\u30C3\u30C8\u3092\u8A31\u53EF:"}, + {"appletprops.choice.unsignedapplet.no", "\u3044\u3044\u3048"}, + {"appletprops.choice.unsignedapplet.yes", "\u306F\u3044"}, + {"appletprops.button.apply", "\u9069\u7528"}, + {"appletprops.button.cancel", "\u53D6\u6D88"}, + {"appletprops.button.reset", "\u30EA\u30BB\u30C3\u30C8"}, + {"appletprops.apply.exception", "\u30D7\u30ED\u30D1\u30C6\u30A3{0}\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "\u30A8\u30F3\u30C8\u30EA\u304C\u7121\u52B9\u3067\u3059"}, + {"appletprops.label.invalidproxy", "\u30D7\u30ED\u30AD\u30B7\u30FB\u30DD\u30FC\u30C8\u306F\u6B63\u306E\u6574\u6570\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "AppletViewer\u7528\u306E\u30E6\u30FC\u30B6\u30FC\u304C\u6307\u5B9A\u3057\u305F\u30D7\u30ED\u30D1\u30C6\u30A3"}, + {"appletsecurityexception.checkcreateclassloader", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30AF\u30E9\u30B9\u30ED\u30FC\u30C0\u30FC"}, + {"appletsecurityexception.checkaccess.thread", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30B9\u30EC\u30C3\u30C9"}, + {"appletsecurityexception.checkaccess.threadgroup", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30B9\u30EC\u30C3\u30C9\u30B0\u30EB\u30FC\u30D7: {0}"}, + {"appletsecurityexception.checkexit", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u7D42\u4E86: {0}"}, + {"appletsecurityexception.checkexec", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u5B9F\u884C: {0}"}, + {"appletsecurityexception.checklink", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30EA\u30F3\u30AF: {0}"}, + {"appletsecurityexception.checkpropsaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30D7\u30ED\u30D1\u30C6\u30A3"}, + {"appletsecurityexception.checkpropsaccess.key", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30D7\u30ED\u30D1\u30C6\u30A3\u30FB\u30A2\u30AF\u30BB\u30B9{0}"}, + {"appletsecurityexception.checkread.exception1", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: file.read: {0}"}, + {"appletsecurityexception.checkread", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: fd.write"}, + {"appletsecurityexception.checklisten", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: {1}\u306E\u8D77\u70B9\u3092\u4F7F\u7528\u3057\u3066{0}\u306B\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002"}, + {"appletsecurityexception.checkconnect.networkhost2", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30DB\u30B9\u30C8{0}\u307E\u305F\u306F{1}\u306EIP\u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 "}, + {"appletsecurityexception.checkconnect.networkhost3", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30DB\u30B9\u30C8{0}\u306EIP\u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002trustProxy\u30D7\u30ED\u30D1\u30C6\u30A3\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"appletsecurityexception.checkconnect", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u63A5\u7D9A: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30D1\u30C3\u30B1\u30FC\u30B8\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u5B9A\u7FA9\u3067\u304D\u307E\u305B\u3093: {0}"}, + {"appletsecurityexception.cannotsetfactory", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30D5\u30A1\u30AF\u30C8\u30EA\u3092\u8A2D\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"appletsecurityexception.checkmemberaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30E1\u30F3\u30D0\u30FC\u30FB\u30A2\u30AF\u30BB\u30B9\u306E\u78BA\u8A8D"}, + {"appletsecurityexception.checkgetprintjob", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u306E\u4F8B\u5916: \u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u64CD\u4F5C: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "\u4E0D\u660E\u306A\u30AF\u30E9\u30B9\u30ED\u30FC\u30C0\u30FC\u30FB\u30BF\u30A4\u30D7\u3067\u3059\u3002getContext\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093"}, + {"appletsecurityexception.checkread.unknown", "\u4E0D\u660E\u306A\u30AF\u30E9\u30B9\u30ED\u30FC\u30C0\u30FC\u30FB\u30BF\u30A4\u30D7\u3067\u3059\u3002{0}\u306E\u8AAD\u53D6\u308A\u30C1\u30A7\u30C3\u30AF\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093"}, + {"appletsecurityexception.checkconnect.unknown", "\u4E0D\u660E\u306A\u30AF\u30E9\u30B9\u30ED\u30FC\u30C0\u30FC\u30FB\u30BF\u30A4\u30D7\u3067\u3059\u3002\u63A5\u7D9A\u30C1\u30A7\u30C3\u30AF\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_ko.java b/src/sun/applet/resources/MsgAppletViewer_ko.java new file mode 100644 index 00000000..6e2af43a --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_ko.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_ko extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "\uD574\uC81C"}, + {"appletviewer.tool.title", "\uC560\uD50C\uB9BF \uBDF0\uC5B4: {0}"}, + {"appletviewer.menu.applet", "\uC560\uD50C\uB9BF"}, + {"appletviewer.menuitem.restart", "\uC7AC\uC2DC\uC791"}, + {"appletviewer.menuitem.reload", "\uC7AC\uB85C\uB4DC"}, + {"appletviewer.menuitem.stop", "\uC815\uC9C0"}, + {"appletviewer.menuitem.save", "\uC800\uC7A5..."}, + {"appletviewer.menuitem.start", "\uC2DC\uC791"}, + {"appletviewer.menuitem.clone", "\uBCF5\uC81C..."}, + {"appletviewer.menuitem.tag", "\uD0DC\uADF8 \uC9C0\uC815..."}, + {"appletviewer.menuitem.info", "\uC815\uBCF4..."}, + {"appletviewer.menuitem.edit", "\uD3B8\uC9D1"}, + {"appletviewer.menuitem.encoding", "\uBB38\uC790 \uC778\uCF54\uB529"}, + {"appletviewer.menuitem.print", "\uC778\uC1C4..."}, + {"appletviewer.menuitem.props", "\uC18D\uC131..."}, + {"appletviewer.menuitem.close", "\uB2EB\uAE30"}, + {"appletviewer.menuitem.quit", "\uC885\uB8CC"}, + {"appletviewer.label.hello", "\uC2DC\uC791..."}, + {"appletviewer.status.start", "\uC560\uD50C\uB9BF\uC744 \uC2DC\uC791\uD558\uB294 \uC911..."}, + {"appletviewer.appletsave.filedialogtitle","\uD30C\uC77C\uB85C \uC560\uD50C\uB9BF \uC9C1\uB82C\uD654"}, + {"appletviewer.appletsave.err1", "{0}\uC744(\uB97C) {1}(\uC73C)\uB85C \uC9C1\uB82C\uD654\uD558\uB294 \uC911"}, + {"appletviewer.appletsave.err2", "appletSave\uC5D0 \uC624\uB958 \uBC1C\uC0DD: {0}"}, + {"appletviewer.applettag", "\uD0DC\uADF8\uAC00 \uD45C\uC2DC\uB428"}, + {"appletviewer.applettag.textframe", "\uC560\uD50C\uB9BF HTML \uD0DC\uADF8"}, + {"appletviewer.appletinfo.applet", "-- \uC560\uD50C\uB9BF \uC815\uBCF4 \uC5C6\uC74C --"}, + {"appletviewer.appletinfo.param", "-- \uB9E4\uAC1C\uBCC0\uC218 \uC815\uBCF4 \uC5C6\uC74C --"}, + {"appletviewer.appletinfo.textframe", "\uC560\uD50C\uB9BF \uC815\uBCF4"}, + {"appletviewer.appletprint.fail", "\uC778\uC1C4\uB97C \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.appletprint.finish", "\uC778\uC1C4\uB97C \uC644\uB8CC\uD588\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.appletprint.cancel", "\uC778\uC1C4\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.appletencoding", "\uBB38\uC790 \uC778\uCF54\uB529: {0}"}, + {"appletviewer.parse.warning.requiresname", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 name \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.paramoutside", "\uACBD\uACE0: \uD0DC\uADF8\uAC00 ... \uBC16\uC5D0 \uC788\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.applet.requirescode", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 code \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.applet.requiresheight", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 height \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.applet.requireswidth", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 width \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.object.requirescode", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 code \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.object.requiresheight", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 height \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.object.requireswidth", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 width \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.embed.requirescode", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 code \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.embed.requiresheight", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 height \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.embed.requireswidth", "\uACBD\uACE0: \uD0DC\uADF8\uC5D0\uB294 width \uC18D\uC131\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletviewer.parse.warning.appnotLongersupported", "\uACBD\uACE0: \uD0DC\uADF8\uB294 \uB354 \uC774\uC0C1 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB300\uC2E0 \uC744 \uC0AC\uC6A9\uD558\uC2ED\uC2DC\uC624."}, + {"appletviewer.usage", "\uC0AC\uC6A9\uBC95: appletviewer url(s)\n\n\uC5EC\uAE30\uC11C \uB294 \uB2E4\uC74C\uACFC \uAC19\uC2B5\uB2C8\uB2E4.\n -debug Java \uB514\uBC84\uAC70\uC5D0\uC11C \uC560\uD50C\uB9BF \uBDF0\uC5B4\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.\n -encoding HTML \uD30C\uC77C\uC5D0 \uC0AC\uC6A9\uB420 \uBB38\uC790 \uC778\uCF54\uB529\uC744 \uC9C0\uC815\uD569\uB2C8\uB2E4.\n -J Java \uC778\uD130\uD504\uB9AC\uD130\uB85C \uC778\uC218\uB97C \uC804\uB2EC\uD569\uB2C8\uB2E4.\n\n-J \uC635\uC158\uC740 \uD45C\uC900\uC774 \uC544\uB2C8\uBA70 \uC608\uACE0 \uC5C6\uC774 \uBCC0\uACBD\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.main.err.unsupportedopt", "\uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 \uC635\uC158: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "\uC54C \uC218 \uC5C6\uB294 \uC778\uC218: {0}"}, + {"appletviewer.main.err.dupoption", "\uC911\uBCF5\uB41C \uC635\uC158 \uC0AC\uC6A9: {0}"}, + {"appletviewer.main.err.inputfile", "\uC9C0\uC815\uB41C \uC785\uB825 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletviewer.main.err.badurl", "\uC798\uBABB\uB41C URL: {0}({1})"}, + {"appletviewer.main.err.io", "\uC77D\uB294 \uC911 I/O \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {0}"}, + {"appletviewer.main.err.readablefile", "{0}\uC774(\uAC00) \uD30C\uC77C\uC774\uBA70 \uC77D\uAE30 \uAC00\uB2A5\uD55C \uC0C1\uD0DC\uC778\uC9C0 \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + {"appletviewer.main.err.correcturl", "{0}\uC774(\uAC00) \uC62C\uBC14\uB978 URL\uC785\uB2C8\uAE4C?"}, + {"appletviewer.main.prop.store", "\uC0AC\uC6A9\uC790 \uAD00\uB828 AppletViewer \uC18D\uC131"}, + {"appletviewer.main.err.prop.cantread", "\uC0AC\uC6A9\uC790 \uC18D\uC131 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC74C: {0}"}, + {"appletviewer.main.err.prop.cantsave", "\uC0AC\uC6A9\uC790 \uC18D\uC131 \uD30C\uC77C\uC744 \uC800\uC7A5\uD560 \uC218 \uC5C6\uC74C: {0}"}, + {"appletviewer.main.warn.nosecmgr", "\uACBD\uACE0: \uBCF4\uC548\uC744 \uC0AC\uC6A9 \uC548\uD568\uC73C\uB85C \uC124\uC815\uD558\uB294 \uC911\uC785\uB2C8\uB2E4."}, + {"appletviewer.main.debug.cantfinddebug", "\uB514\uBC84\uAC70\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4!"}, + {"appletviewer.main.debug.cantfindmain", "\uB514\uBC84\uAC70\uC5D0\uC11C \uAE30\uBCF8 \uBA54\uC18C\uB4DC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4!"}, + {"appletviewer.main.debug.exceptionindebug", "\uB514\uBC84\uAC70\uC5D0 \uC608\uC678 \uC0AC\uD56D\uC774 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4!"}, + {"appletviewer.main.debug.cantaccess", "\uB514\uBC84\uAC70\uC5D0 \uC561\uC138\uC2A4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4!"}, + {"appletviewer.main.nosecmgr", "\uACBD\uACE0: SecurityManager\uAC00 \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4!"}, + {"appletviewer.main.warning", "\uACBD\uACE0: \uC2DC\uC791\uB41C \uC560\uD50C\uB9BF\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD0DC\uADF8\uAC00 \uC785\uB825\uB418\uC5C8\uB294\uC9C0 \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + {"appletviewer.main.warn.prop.overwrite", "\uACBD\uACE0: \uC0AC\uC6A9\uC790\uC758 \uC694\uCCAD\uC5D0 \uB530\uB77C \uC77C\uC2DC\uC801\uC73C\uB85C \uC2DC\uC2A4\uD15C \uC18D\uC131\uC744 \uACB9\uCCD0 \uC4F0\uB294 \uC911: \uD0A4: {0}, \uC774\uC804 \uAC12: {1}, \uC0C8 \uAC12: {2}"}, + {"appletviewer.main.warn.cantreadprops", "\uACBD\uACE0: AppletViewer \uC18D\uC131 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC74C: {0}. \uAE30\uBCF8\uAC12\uC744 \uC0AC\uC6A9\uD558\uB294 \uC911\uC785\uB2C8\uB2E4."}, + {"appletioexception.loadclass.throw.interrupted", "\uD074\uB798\uC2A4 \uB85C\uB4DC\uAC00 \uC911\uB2E8\uB428: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "\uD074\uB798\uC2A4\uAC00 \uB85C\uB4DC\uB418\uC9C0 \uC54A\uC74C: {0}"}, + {"appletclassloader.loadcode.verbose", "{1}\uC744(\uB97C) \uAC00\uC838\uC624\uAE30 \uC704\uD574 {0}\uC5D0 \uB300\uD55C \uC2A4\uD2B8\uB9BC\uC744 \uC5EC\uB294 \uC911"}, + {"appletclassloader.filenotfound", "{0}\uC744(\uB97C) \uAC80\uC0C9\uD558\uB294 \uC911 \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."}, + {"appletclassloader.fileformat", "\uB85C\uB4DC \uC911 \uD30C\uC77C \uD615\uC2DD \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {0}"}, + {"appletclassloader.fileioexception", "\uB85C\uB4DC \uC911 I/O \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {0}"}, + {"appletclassloader.fileexception", "\uB85C\uB4DC \uC911 {0} \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {1}"}, + {"appletclassloader.filedeath", "\uB85C\uB4DC \uC911 {0}\uC774(\uAC00) \uC885\uB8CC\uB428: {1}"}, + {"appletclassloader.fileerror", "\uB85C\uB4DC \uC911 {0} \uC624\uB958 \uBC1C\uC0DD: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "{1}\uC744(\uB97C) \uAC00\uC838\uC624\uAE30 \uC704\uD574 {0}\uC5D0 \uB300\uD55C \uC2A4\uD2B8\uB9BC\uC744 \uC5EC\uB294 \uC911"}, + {"appletclassloader.getresource.verbose.forname", "\uC774\uB984\uC5D0 \uB300\uD55C AppletClassLoader.getResource: {0}"}, + {"appletclassloader.getresource.verbose.found", "\uC2DC\uC2A4\uD15C \uB9AC\uC18C\uC2A4\uB85C {0} \uB9AC\uC18C\uC2A4\uB97C \uCC3E\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletclassloader.getresourceasstream.verbose", "\uC2DC\uC2A4\uD15C \uB9AC\uC18C\uC2A4\uB85C {0} \uB9AC\uC18C\uC2A4\uB97C \uCC3E\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.runloader.err", "\uAC1D\uCCB4 \uB610\uB294 \uCF54\uB4DC \uB9E4\uAC1C\uBCC0\uC218\uC785\uB2C8\uB2E4!"}, + {"appletpanel.runloader.exception", "{0}\uC758 \uC9C1\uB82C\uD654\uB97C \uD574\uC81C\uD558\uB294 \uC911 \uC608\uC678 \uC0AC\uD56D\uC774 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.destroyed", "\uC560\uD50C\uB9BF\uC774 \uC0AD\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.loaded", "\uC560\uD50C\uB9BF\uC774 \uB85C\uB4DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.started", "\uC560\uD50C\uB9BF\uC774 \uC2DC\uC791\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.inited", "\uC560\uD50C\uB9BF\uC774 \uCD08\uAE30\uD654\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.stopped", "\uC560\uD50C\uB9BF\uC774 \uC815\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.disposed", "\uC560\uD50C\uB9BF\uC774 \uBC30\uCE58\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.nocode", "APPLET \uD0DC\uADF8\uC5D0 CODE \uB9E4\uAC1C\uBCC0\uC218\uAC00 \uB204\uB77D\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notfound", "\uB85C\uB4DC: {0} \uD074\uB798\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.nocreate", "\uB85C\uB4DC: {0}\uC744(\uB97C) \uC778\uC2A4\uD134\uC2A4\uD654\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.noconstruct", "\uB85C\uB4DC: {0}\uC740(\uB294) \uACF5\uC6A9\uC774 \uC544\uB2C8\uAC70\uB098 \uACF5\uC6A9 \uC0DD\uC131\uC790\uB97C \uD3EC\uD568\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.death", "\uC885\uB8CC\uB428"}, + {"appletpanel.exception", "\uC608\uC678 \uC0AC\uD56D: {0}."}, + {"appletpanel.exception2", "\uC608\uC678 \uC0AC\uD56D: {0}: {1}."}, + {"appletpanel.error", "\uC624\uB958: {0}."}, + {"appletpanel.error2", "\uC624\uB958: {0}: {1}."}, + {"appletpanel.notloaded", "\uCD08\uAE30\uD654: \uC560\uD50C\uB9BF\uC774 \uB85C\uB4DC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notinited", "\uC2DC\uC791: \uC560\uD50C\uB9BF\uC774 \uCD08\uAE30\uD654\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notstarted", "\uC815\uC9C0: \uC560\uD50C\uB9BF\uC774 \uC2DC\uC791\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notstopped", "\uC0AD\uC81C: \uC560\uD50C\uB9BF\uC774 \uC815\uC9C0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notdestroyed", "\uBC30\uCE58: \uC560\uD50C\uB9BF\uC774 \uC0AD\uC81C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.notdisposed", "\uB85C\uB4DC: \uC560\uD50C\uB9BF\uC774 \uBC30\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.bail", "\uC911\uB2E8\uB428: \uC911\uB2E8\uD558\uB294 \uC911\uC785\uB2C8\uB2E4."}, + {"appletpanel.filenotfound", "{0}\uC744(\uB97C) \uAC80\uC0C9\uD558\uB294 \uC911 \uD30C\uC77C\uC744 \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."}, + {"appletpanel.fileformat", "\uB85C\uB4DC \uC911 \uD30C\uC77C \uD615\uC2DD \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {0}"}, + {"appletpanel.fileioexception", "\uB85C\uB4DC \uC911 I/O \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {0}"}, + {"appletpanel.fileexception", "\uB85C\uB4DC \uC911 {0} \uC608\uC678 \uC0AC\uD56D \uBC1C\uC0DD: {1}"}, + {"appletpanel.filedeath", "\uB85C\uB4DC \uC911 {0}\uC774(\uAC00) \uC885\uB8CC\uB428: {1}"}, + {"appletpanel.fileerror", "\uB85C\uB4DC \uC911 {0} \uC624\uB958 \uBC1C\uC0DD: {1}"}, + {"appletpanel.badattribute.exception", "HTML \uAD6C\uBB38 \uBD84\uC11D \uC911: width/height \uC18D\uC131\uC5D0 \uB300\uD55C \uAC12\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream\uC5D0 \uB110\uC774 \uC544\uB2CC \uB85C\uB354\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"appletprops.title", "AppletViewer \uC18D\uC131"}, + {"appletprops.label.http.server", "HTTP \uD504\uB85D\uC2DC \uC11C\uBC84:"}, + {"appletprops.label.http.proxy", "HTTP \uD504\uB85D\uC2DC \uD3EC\uD2B8:"}, + {"appletprops.label.network", "\uB124\uD2B8\uC6CC\uD06C \uC561\uC138\uC2A4:"}, + {"appletprops.choice.network.item.none", "\uC5C6\uC74C"}, + {"appletprops.choice.network.item.applethost", "\uC560\uD50C\uB9BF \uD638\uC2A4\uD2B8"}, + {"appletprops.choice.network.item.unrestricted", "\uC81C\uD55C\uB418\uC9C0 \uC54A\uC74C"}, + {"appletprops.label.class", "\uD074\uB798\uC2A4 \uC561\uC138\uC2A4:"}, + {"appletprops.choice.class.item.restricted", "\uC81C\uD55C\uB428"}, + {"appletprops.choice.class.item.unrestricted", "\uC81C\uD55C\uB418\uC9C0 \uC54A\uC74C"}, + {"appletprops.label.unsignedapplet", "\uC11C\uBA85\uB418\uC9C0 \uC54A\uC740 \uC560\uD50C\uB9BF \uD5C8\uC6A9:"}, + {"appletprops.choice.unsignedapplet.no", "\uC544\uB2C8\uC624"}, + {"appletprops.choice.unsignedapplet.yes", "\uC608"}, + {"appletprops.button.apply", "\uC801\uC6A9"}, + {"appletprops.button.cancel", "\uCDE8\uC18C"}, + {"appletprops.button.reset", "\uC7AC\uC124\uC815"}, + {"appletprops.apply.exception", "\uC18D\uC131 \uC800\uC7A5 \uC2E4\uD328: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "\uBD80\uC801\uD569\uD55C \uD56D\uBAA9"}, + {"appletprops.label.invalidproxy", "\uD504\uB85D\uC2DC \uD3EC\uD2B8\uB294 \uC591\uC758 \uC815\uC218 \uAC12\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"appletprops.button.ok", "\uD655\uC778"}, + /* end 4066432 */ + {"appletprops.prop.store", "\uC0AC\uC6A9\uC790 \uAD00\uB828 AppletViewer \uC18D\uC131"}, + {"appletsecurityexception.checkcreateclassloader", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uD074\uB798\uC2A4 \uB85C\uB354"}, + {"appletsecurityexception.checkaccess.thread", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC2A4\uB808\uB4DC"}, + {"appletsecurityexception.checkaccess.threadgroup", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC2A4\uB808\uB4DC \uADF8\uB8F9: {0}"}, + {"appletsecurityexception.checkexit", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC885\uB8CC: {0}"}, + {"appletsecurityexception.checkexec", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC2E4\uD589: {0}"}, + {"appletsecurityexception.checklink", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uB9C1\uD06C: {0}"}, + {"appletsecurityexception.checkpropsaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC18D\uC131"}, + {"appletsecurityexception.checkpropsaccess.key", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC18D\uC131 \uC561\uC138\uC2A4 {0}"}, + {"appletsecurityexception.checkread.exception1", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: file.read: {0}"}, + {"appletsecurityexception.checkread", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: fd.write"}, + {"appletsecurityexception.checklisten", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: {1}\uC5D0\uC11C {0}\uC5D0 \uC811\uC18D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletsecurityexception.checkconnect.networkhost2", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: {0} \uD638\uC2A4\uD2B8 \uB610\uB294 {1}\uC5D0 \uB300\uD55C IP\uB97C \uBD84\uC11D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. "}, + {"appletsecurityexception.checkconnect.networkhost3", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: {0} \uD638\uC2A4\uD2B8\uC5D0 \uB300\uD55C IP\uB97C \uBD84\uC11D\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. trustProxy \uC18D\uC131\uC744 \uCC38\uC870\uD558\uC2ED\uC2DC\uC624."}, + {"appletsecurityexception.checkconnect", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uC811\uC18D: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uD328\uD0A4\uC9C0\uC5D0 \uC561\uC138\uC2A4\uD560 \uC218 \uC5C6\uC74C: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uD328\uD0A4\uC9C0\uB97C \uC815\uC758\uD560 \uC218 \uC5C6\uC74C: {0}"}, + {"appletsecurityexception.cannotsetfactory", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uD329\uD1A0\uB9AC\uB97C \uC124\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletsecurityexception.checkmemberaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uBA64\uBC84 \uC561\uC138\uC2A4\uB97C \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + {"appletsecurityexception.checkgetprintjob", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "\uBCF4\uC548 \uC608\uC678 \uC0AC\uD56D: \uBCF4\uC548 \uC791\uC5C5: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "\uC54C \uC218 \uC5C6\uB294 \uD074\uB798\uC2A4 \uB85C\uB354 \uC720\uD615\uC785\uB2C8\uB2E4. getContext\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletsecurityexception.checkread.unknown", "\uC54C \uC218 \uC5C6\uB294 \uD074\uB798\uC2A4 \uB85C\uB354 \uC720\uD615\uC785\uB2C8\uB2E4. {0} \uC77D\uAE30\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"appletsecurityexception.checkconnect.unknown", "\uC54C \uC218 \uC5C6\uB294 \uD074\uB798\uC2A4 \uB85C\uB354 \uC720\uD615\uC785\uB2C8\uB2E4. \uC811\uC18D\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_pt_BR.java b/src/sun/applet/resources/MsgAppletViewer_pt_BR.java new file mode 100644 index 00000000..128d3b4c --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_pt_BR.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_pt_BR extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "Rejeitar"}, + {"appletviewer.tool.title", "Visualizador do Applet: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Reiniciar"}, + {"appletviewer.menuitem.reload", "Recarregar"}, + {"appletviewer.menuitem.stop", "Interromper"}, + {"appletviewer.menuitem.save", "Salvar"}, + {"appletviewer.menuitem.start", "Iniciar"}, + {"appletviewer.menuitem.clone", "Clonar..."}, + {"appletviewer.menuitem.tag", "Tag..."}, + {"appletviewer.menuitem.info", "Informa\u00E7\u00F5es..."}, + {"appletviewer.menuitem.edit", "Editar"}, + {"appletviewer.menuitem.encoding", "Codifica\u00E7\u00E3o do Caractere"}, + {"appletviewer.menuitem.print", "Imprimir..."}, + {"appletviewer.menuitem.props", "Propriedades..."}, + {"appletviewer.menuitem.close", "Fechar"}, + {"appletviewer.menuitem.quit", "Sair"}, + {"appletviewer.label.hello", "Ol\u00E1..."}, + {"appletviewer.status.start", "iniciando o applet..."}, + {"appletviewer.appletsave.filedialogtitle","Serializar Applet no Arquivo"}, + {"appletviewer.appletsave.err1", "serializando um {0} para {1}"}, + {"appletviewer.appletsave.err2", "no appletSave: {0}"}, + {"appletviewer.applettag", "Tag mostrada"}, + {"appletviewer.applettag.textframe", "Tag HTML do Applet"}, + {"appletviewer.appletinfo.applet", "-- nenhuma informa\u00E7\u00E3o do applet --"}, + {"appletviewer.appletinfo.param", "-- sem informa\u00E7\u00E3o de par\u00E2metro --"}, + {"appletviewer.appletinfo.textframe", "Informa\u00E7\u00F5es do Applet"}, + {"appletviewer.appletprint.fail", "Falha na impress\u00E3o."}, + {"appletviewer.appletprint.finish", "Impress\u00E3o finalizada."}, + {"appletviewer.appletprint.cancel", "Impress\u00E3o cancelada."}, + {"appletviewer.appletencoding", "Codifica\u00E7\u00E3o de Caractere: {0}"}, + {"appletviewer.parse.warning.requiresname", "Advert\u00EAncia: a tag requer um atributo de nome."}, + {"appletviewer.parse.warning.paramoutside", "Advert\u00EAncia: a tag externa ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Advert\u00EAncia: a tag requer um atributo de c\u00F3digo."}, + {"appletviewer.parse.warning.applet.requiresheight", "Advert\u00EAncia: a tag requer um atributo de altura."}, + {"appletviewer.parse.warning.applet.requireswidth", "Advert\u00EAncia: a tag requer um atributo de largura."}, + {"appletviewer.parse.warning.object.requirescode", "Advert\u00EAncia: a tag requer um atributo de c\u00F3digo."}, + {"appletviewer.parse.warning.object.requiresheight", "Advert\u00EAncia: a tag requer um atributo de altura."}, + {"appletviewer.parse.warning.object.requireswidth", "Advert\u00EAncia: a tag requer um atributo de largura."}, + {"appletviewer.parse.warning.embed.requirescode", "Advert\u00EAncia: a tag requer um atributo de c\u00F3digo."}, + {"appletviewer.parse.warning.embed.requiresheight", "Advert\u00EAncia: a tag requer um atributo de altura."}, + {"appletviewer.parse.warning.embed.requireswidth", "Advert\u00EAncia: a tag requer um atributo de largura."}, + {"appletviewer.parse.warning.appnotLongersupported", "Advert\u00EAncia: a tag n\u00E3o \u00E9 mais suportada; use :"}, + {"appletviewer.usage", "Uso: appletviewer url(s)\n\nem que as incluem:\n -debug Inicia o visualizador do applet no depurador Java\n -encoding Especifica a codifica\u00E7\u00E3o de caractere usada pelos arquivos HTML\n -J Informa o argumento ao intepretador java\n\nA op\u00E7\u00E3o -J n\u00E3o \u00E9 padr\u00E3o e est\u00E1 sujeita \u00E0 altera\u00E7\u00E3o sem notifica\u00E7\u00E3o."}, + {"appletviewer.main.err.unsupportedopt", "Op\u00E7\u00E3o n\u00E3o suportada: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Argumento n\u00E3o reconhecido: {0}"}, + {"appletviewer.main.err.dupoption", "Uso duplicado da op\u00E7\u00E3o: {0}"}, + {"appletviewer.main.err.inputfile", "Nenhum arquivo de entrada especificado."}, + {"appletviewer.main.err.badurl", "URL Inv\u00E1lido: {0} ( {1} )"}, + {"appletviewer.main.err.io", "Exce\u00E7\u00E3o de E/S ao ler: {0}"}, + {"appletviewer.main.err.readablefile", "Certifique-se de que {0} seja um arquivo e seja leg\u00EDvel."}, + {"appletviewer.main.err.correcturl", "O URL {0} est\u00E1 correto?"}, + {"appletviewer.main.prop.store", "Propriedades espec\u00EDficas do usu\u00E1rio do AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "N\u00E3o \u00E9 poss\u00EDvel ler o arquivo de propriedades do usu\u00E1rio: {0}"}, + {"appletviewer.main.err.prop.cantsave", "N\u00E3o \u00E9 poss\u00EDvel salvar o arquivo de propriedades do usu\u00E1rio: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Advert\u00EAncia: desativando a seguran\u00E7a."}, + {"appletviewer.main.debug.cantfinddebug", "N\u00E3o \u00E9 poss\u00EDvel localizar o depurador!"}, + {"appletviewer.main.debug.cantfindmain", "N\u00E3o \u00E9 poss\u00EDvel localizar o m\u00E9todo main no depurador!"}, + {"appletviewer.main.debug.exceptionindebug", "Exce\u00E7\u00E3o no depurador!"}, + {"appletviewer.main.debug.cantaccess", "N\u00E3o \u00E9 poss\u00EDvel acessar o depurador!"}, + {"appletviewer.main.nosecmgr", "Advert\u00EAncia: SecurityManager n\u00E3o instalado!"}, + {"appletviewer.main.warning", "Advert\u00EAncia: Nenhum applet iniciado. Certifique-se de que a entrada contenha uma tag ."}, + {"appletviewer.main.warn.prop.overwrite", "Advert\u00EAncia: Substituindo a propriedade do sistema temporariamente a pedido do usu\u00E1rio: chave: {0} valor antigo: {1} valor novo: {2}"}, + {"appletviewer.main.warn.cantreadprops", "Advert\u00EAncia: N\u00E3o \u00E9 poss\u00EDvel ler o arquivo de propriedades AppletViewer: {0} Usando padr\u00F5es."}, + {"appletioexception.loadclass.throw.interrupted", "carregamento de classe interrompido: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "classe n\u00E3o carregada: {0}"}, + {"appletclassloader.loadcode.verbose", "Fluxo de abertura para: {0} para obter {1}"}, + {"appletclassloader.filenotfound", "Arquivo n\u00E3o encontrado ao procurar: {0}"}, + {"appletclassloader.fileformat", "Exce\u00E7\u00E3o de formato do arquivo ao carregar: {0}"}, + {"appletclassloader.fileioexception", "Exce\u00E7\u00E3o de E/S ao carregar: {0}"}, + {"appletclassloader.fileexception", "exce\u00E7\u00E3o de {0} ao carregar: {1}"}, + {"appletclassloader.filedeath", "{0} eliminado ao carregar: {1}"}, + {"appletclassloader.fileerror", "erro de {0} ao carregar: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "Fluxo de abertura para: {0} para obter {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource do nome: {0}"}, + {"appletclassloader.getresource.verbose.found", "Recurso encontrado: {0} como um recurso do sistema"}, + {"appletclassloader.getresourceasstream.verbose", "Recurso encontrado: {0} como um recurso do sistema"}, + {"appletpanel.runloader.err", "Par\u00E2metro de c\u00F3digo ou objeto!"}, + {"appletpanel.runloader.exception", "exce\u00E7\u00E3o ao desserializar {0}"}, + {"appletpanel.destroyed", "Applet destru\u00EDdo."}, + {"appletpanel.loaded", "Applet carregado."}, + {"appletpanel.started", "Applet iniciado."}, + {"appletpanel.inited", "Applet inicializado."}, + {"appletpanel.stopped", "Applet interrompido."}, + {"appletpanel.disposed", "Applet descartado."}, + {"appletpanel.nocode", "A tag APPLET n\u00E3o encontrou o par\u00E2metro CODE."}, + {"appletpanel.notfound", "carga: classe {0} n\u00E3o encontrada."}, + {"appletpanel.nocreate", "carga: {0} n\u00E3o pode ser instanciada."}, + {"appletpanel.noconstruct", "carga: {0} n\u00E3o \u00E9 p\u00FAblica ou n\u00E3o tem construtor p\u00FAblico."}, + {"appletpanel.death", "eliminado"}, + {"appletpanel.exception", "exce\u00E7\u00E3o: {0}."}, + {"appletpanel.exception2", "exce\u00E7\u00E3o: {0}: {1}."}, + {"appletpanel.error", "erro: {0}."}, + {"appletpanel.error2", "erro: {0}: {1}."}, + {"appletpanel.notloaded", "Inic: applet n\u00E3o carregado."}, + {"appletpanel.notinited", "Iniciar: applet n\u00E3o inicializado."}, + {"appletpanel.notstarted", "Interromper: applet n\u00E3o inicializado."}, + {"appletpanel.notstopped", "Destruir: applet n\u00E3o interrompido."}, + {"appletpanel.notdestroyed", "Descartar: applet n\u00E3o destru\u00EDdo."}, + {"appletpanel.notdisposed", "Carregar: applet n\u00E3o descartado."}, + {"appletpanel.bail", "Interrompido: esvaziando."}, + {"appletpanel.filenotfound", "Arquivo n\u00E3o encontrado ao procurar: {0}"}, + {"appletpanel.fileformat", "Exce\u00E7\u00E3o de formato do arquivo ao carregar: {0}"}, + {"appletpanel.fileioexception", "Exce\u00E7\u00E3o de E/S ao carregar: {0}"}, + {"appletpanel.fileexception", "exce\u00E7\u00E3o de {0} ao carregar: {1}"}, + {"appletpanel.filedeath", "{0} eliminado ao carregar: {1}"}, + {"appletpanel.fileerror", "erro de {0} ao carregar: {1}"}, + {"appletpanel.badattribute.exception", "Parsing de HTML: valor incorreto do atributo de largura/altura"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream requer um carregador n\u00E3o nulo"}, + {"appletprops.title", "Propriedades do AppletViewer"}, + {"appletprops.label.http.server", "Servidor proxy Http:"}, + {"appletprops.label.http.proxy", "Porta proxy Http:"}, + {"appletprops.label.network", "Acesso de rede:"}, + {"appletprops.choice.network.item.none", "Nenhum"}, + {"appletprops.choice.network.item.applethost", "Host do Applet"}, + {"appletprops.choice.network.item.unrestricted", "Irrestrito"}, + {"appletprops.label.class", "Acesso \u00E0 classe:"}, + {"appletprops.choice.class.item.restricted", "Restrito"}, + {"appletprops.choice.class.item.unrestricted", "Irrestrito"}, + {"appletprops.label.unsignedapplet", "Permitir applets n\u00E3o assinados:"}, + {"appletprops.choice.unsignedapplet.no", "N\u00E3o"}, + {"appletprops.choice.unsignedapplet.yes", "Sim"}, + {"appletprops.button.apply", "Aplicar"}, + {"appletprops.button.cancel", "Cancelar"}, + {"appletprops.button.reset", "Redefinir"}, + {"appletprops.apply.exception", "Falha ao salvar as propriedades: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Entrada Inv\u00E1lida"}, + {"appletprops.label.invalidproxy", "A Porta Proxy deve ser um valor inteiro positivo."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "Propriedades espec\u00EDficas do usu\u00E1rio do AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "Exce\u00E7\u00E3o de Seguran\u00E7a: carregador de classes"}, + {"appletsecurityexception.checkaccess.thread", "Exce\u00E7\u00E3o de Seguran\u00E7a: thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "Exce\u00E7\u00E3o de Seguran\u00E7a: grupo de threads: {0}"}, + {"appletsecurityexception.checkexit", "Exce\u00E7\u00E3o de Seguran\u00E7a: sa\u00EDda: {0}"}, + {"appletsecurityexception.checkexec", "Exce\u00E7\u00E3o de Seguran\u00E7a: exec.: {0}"}, + {"appletsecurityexception.checklink", "Exce\u00E7\u00E3o de Seguran\u00E7a: link: {0}"}, + {"appletsecurityexception.checkpropsaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: propriedades"}, + {"appletsecurityexception.checkpropsaccess.key", "Exce\u00E7\u00E3o de Seguran\u00E7a: acesso \u00E0s propriedades {0}"}, + {"appletsecurityexception.checkread.exception1", "Exce\u00E7\u00E3o de Seguran\u00E7a: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "Exce\u00E7\u00E3o de Seguran\u00E7a: file.read: {0}"}, + {"appletsecurityexception.checkread", "Exce\u00E7\u00E3o de Seguran\u00E7a: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "Exce\u00E7\u00E3o de Seguran\u00E7a: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "Exce\u00E7\u00E3o de Seguran\u00E7a: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "Exce\u00E7\u00E3o de Seguran\u00E7a: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "Exce\u00E7\u00E3o de Seguran\u00E7a: fd.write"}, + {"appletsecurityexception.checklisten", "Exce\u00E7\u00E3o de Seguran\u00E7a: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "Exce\u00E7\u00E3o de Seguran\u00E7a: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "Exce\u00E7\u00E3o de Seguran\u00E7a: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "Exce\u00E7\u00E3o de Seguran\u00E7a: N\u00E3o foi poss\u00EDvel estabelecer conex\u00E3o com {0} com a origem de {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "Exce\u00E7\u00E3o de Seguran\u00E7a: N\u00E3o foi poss\u00EDvel resolver o IP para o host {0} ou para {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "Exce\u00E7\u00E3o de Seguran\u00E7a: N\u00E3o foi poss\u00EDvel resolver o IP para o host {0}. Consulte a propriedade trustProxy."}, + {"appletsecurityexception.checkconnect", "Exce\u00E7\u00E3o de Seguran\u00E7a: conectar: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: n\u00E3o \u00E9 poss\u00EDvel acessar o pacote: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "Exce\u00E7\u00E3o de Seguran\u00E7a: n\u00E3o \u00E9 poss\u00EDvel definir o pacote: {0}"}, + {"appletsecurityexception.cannotsetfactory", "Exce\u00E7\u00E3o de Seguran\u00E7a: n\u00E3o \u00E9 poss\u00EDvel definir o factory"}, + {"appletsecurityexception.checkmemberaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: verificar acesso do membro"}, + {"appletsecurityexception.checkgetprintjob", "Exce\u00E7\u00E3o de Seguran\u00E7a: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "Exce\u00E7\u00E3o de Seguran\u00E7a: opera\u00E7\u00E3o de seguran\u00E7a: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "tipo de carregador de classe desconhecido. n\u00E3o \u00E9 poss\u00EDvel verificar getContext"}, + {"appletsecurityexception.checkread.unknown", "tipo de carregador de classe desconhecido. n\u00E3o \u00E9 poss\u00EDvel verificar a leitura {0}"}, + {"appletsecurityexception.checkconnect.unknown", "tipo de carregador de classe desconhecido. n\u00E3o \u00E9 poss\u00EDvel verificar a conex\u00E3o"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_sv.java b/src/sun/applet/resources/MsgAppletViewer_sv.java new file mode 100644 index 00000000..4da37a2d --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_sv.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_sv extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "St\u00E4ng"}, + {"appletviewer.tool.title", "Applet Viewer: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "Starta om"}, + {"appletviewer.menuitem.reload", "Ladda om"}, + {"appletviewer.menuitem.stop", "Stopp"}, + {"appletviewer.menuitem.save", "Spara..."}, + {"appletviewer.menuitem.start", "Starta"}, + {"appletviewer.menuitem.clone", "Klona..."}, + {"appletviewer.menuitem.tag", "Tagg..."}, + {"appletviewer.menuitem.info", "Information..."}, + {"appletviewer.menuitem.edit", "Redigera"}, + {"appletviewer.menuitem.encoding", "Teckenkodning"}, + {"appletviewer.menuitem.print", "Skriv ut..."}, + {"appletviewer.menuitem.props", "Egenskaper..."}, + {"appletviewer.menuitem.close", "St\u00E4ng"}, + {"appletviewer.menuitem.quit", "Avsluta"}, + {"appletviewer.label.hello", "Hej..."}, + {"appletviewer.status.start", "startar applet..."}, + {"appletviewer.appletsave.filedialogtitle","Serialisera applet till fil"}, + {"appletviewer.appletsave.err1", "serialiserar {0} till {1}"}, + {"appletviewer.appletsave.err2", "i appletSave: {0}"}, + {"appletviewer.applettag", "Tagg visas"}, + {"appletviewer.applettag.textframe", "HTML-tagg f\u00F6r applet"}, + {"appletviewer.appletinfo.applet", "-- ingen appletinformation --"}, + {"appletviewer.appletinfo.param", "-- ingen parameterinformation --"}, + {"appletviewer.appletinfo.textframe", "Appletinformation"}, + {"appletviewer.appletprint.fail", "Kunde inte skriva ut."}, + {"appletviewer.appletprint.finish", "Utskriften klar."}, + {"appletviewer.appletprint.cancel", "Utskriften avbruten."}, + {"appletviewer.appletencoding", "Teckenkodning: {0}"}, + {"appletviewer.parse.warning.requiresname", "Varning: -taggen kr\u00E4ver ett namnattribut."}, + {"appletviewer.parse.warning.paramoutside", "Varning: -taggen finns utanf\u00F6r ... ."}, + {"appletviewer.parse.warning.applet.requirescode", "Varning: -taggen kr\u00E4ver ett kodattribut."}, + {"appletviewer.parse.warning.applet.requiresheight", "Varning: -taggen kr\u00E4ver ett h\u00F6jdattribut."}, + {"appletviewer.parse.warning.applet.requireswidth", "Varning: -taggen kr\u00E4ver ett breddattribut."}, + {"appletviewer.parse.warning.object.requirescode", "Varning: -taggen kr\u00E4ver ett kodattribut."}, + {"appletviewer.parse.warning.object.requiresheight", "Varning: -taggen kr\u00E4ver ett h\u00F6jdattribut."}, + {"appletviewer.parse.warning.object.requireswidth", "Varning: -taggen kr\u00E4ver ett breddattribut."}, + {"appletviewer.parse.warning.embed.requirescode", "Varning: -taggen kr\u00E4ver ett kodattribut."}, + {"appletviewer.parse.warning.embed.requiresheight", "Varning: -taggen kr\u00E4ver ett h\u00F6jdattribut."}, + {"appletviewer.parse.warning.embed.requireswidth", "Varning: -taggen kr\u00E4ver ett breddattribut."}, + {"appletviewer.parse.warning.appnotLongersupported", "Varning: -taggen st\u00F6ds inte l\u00E4ngre, anv\u00E4nd ist\u00E4llet:"}, + {"appletviewer.usage", "Syntax: appletviewer- url:er \n\nd\u00E4r inkluderar:\n -debug Startar appletvisning i Java-fels\u00F6kningen\n -encoding Anger teckenkodning som anv\u00E4nds i HTML-filer\n -J \u00D6verf\u00F6r argument till Java-tolkningen\n\nAlternativet -J \u00E4r inte standard och kan \u00E4ndras utan f\u00F6reg\u00E5ende meddelande."}, + {"appletviewer.main.err.unsupportedopt", "Alternativ som inte st\u00F6ds: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "Ok\u00E4nt argument: {0}"}, + {"appletviewer.main.err.dupoption", "Duplicerat alternativ: {0}"}, + {"appletviewer.main.err.inputfile", "Inga angivna indatafiler."}, + {"appletviewer.main.err.badurl", "Felaktig URL: {0} ( {1} )"}, + {"appletviewer.main.err.io", "I/O-undantag vid l\u00E4sning: {0}"}, + {"appletviewer.main.err.readablefile", "Kontrollera att {0} \u00E4r en fil som \u00E4r l\u00E4sbar."}, + {"appletviewer.main.err.correcturl", "\u00C4r {0} den korrekta URL:en?"}, + {"appletviewer.main.prop.store", "Anv\u00E4ndarspecifika egenskaper f\u00F6r AppletViewer"}, + {"appletviewer.main.err.prop.cantread", "Kan inte l\u00E4sa egenskapsfilen: {0}"}, + {"appletviewer.main.err.prop.cantsave", "Kan inte spara egenskapsfilen: {0}"}, + {"appletviewer.main.warn.nosecmgr", "Varning! S\u00E4kerheten avaktiveras."}, + {"appletviewer.main.debug.cantfinddebug", "Hittar inte fels\u00F6kningsprogrammet!"}, + {"appletviewer.main.debug.cantfindmain", "Hittar inte huvudmetoden i fels\u00F6kningsprogrammet!"}, + {"appletviewer.main.debug.exceptionindebug", "Undantag i fels\u00F6kningsprogrammet!"}, + {"appletviewer.main.debug.cantaccess", "Det finns ingen \u00E5tkomst till fels\u00F6kningsprogrammet!"}, + {"appletviewer.main.nosecmgr", "Varning: SecurityManager har inte installerats!"}, + {"appletviewer.main.warning", "Varning: Inga appletar har startats. Kontrollera att indata inneh\u00E5ller -tagg."}, + {"appletviewer.main.warn.prop.overwrite", "Varning: Skriver tillf\u00E4lligt \u00F6ver systemegenskap enligt beg\u00E4ran fr\u00E5n anv\u00E4ndare: nyckel: {0} gammalt v\u00E4rde: {1} nytt v\u00E4rde: {2}"}, + {"appletviewer.main.warn.cantreadprops", "Varning: Kan inte l\u00E4sa egenskapsfil f\u00F6r AppletViewer: {0} Standardv\u00E4rden anv\u00E4nds."}, + {"appletioexception.loadclass.throw.interrupted", "klassinl\u00E4sning avbr\u00F6ts: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "klass inte inl\u00E4st: {0}"}, + {"appletclassloader.loadcode.verbose", "\u00D6ppnar str\u00F6m till: {0} f\u00F6r h\u00E4mtning av {1}"}, + {"appletclassloader.filenotfound", "Hittade inte fil vid s\u00F6kning efter: {0}"}, + {"appletclassloader.fileformat", "Undantag av filformat vid laddning av: {0}"}, + {"appletclassloader.fileioexception", "I/O-undantag vid laddning: {0}"}, + {"appletclassloader.fileexception", "{0} undantag vid laddning: {1}"}, + {"appletclassloader.filedeath", "{0} avslutad vid laddning: {1}"}, + {"appletclassloader.fileerror", "{0} fel vid laddning: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "\u00D6ppnar str\u00F6m till: {0} f\u00F6r h\u00E4mtning av {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource f\u00F6r namnet: {0}"}, + {"appletclassloader.getresource.verbose.found", "Hittade resursen: {0} som systemresurs"}, + {"appletclassloader.getresourceasstream.verbose", "Hittade resursen: {0} som systemresurs"}, + {"appletpanel.runloader.err", "Antingen objekt- eller kodparameter!"}, + {"appletpanel.runloader.exception", "undantag vid avserialisering {0}"}, + {"appletpanel.destroyed", "Applet raderad."}, + {"appletpanel.loaded", "Applet laddad."}, + {"appletpanel.started", "Applet startad."}, + {"appletpanel.inited", "Applet initierad."}, + {"appletpanel.stopped", "Applet stoppad."}, + {"appletpanel.disposed", "Applet kasserad."}, + {"appletpanel.nocode", "APPLET-tagg saknar CODE-parameter."}, + {"appletpanel.notfound", "load: hittade inte klassen {0}."}, + {"appletpanel.nocreate", "load: {0} kan inte instansieras."}, + {"appletpanel.noconstruct", "load: {0} \u00E4r inte allm\u00E4n eller saknar allm\u00E4n konstruktor."}, + {"appletpanel.death", "avslutad"}, + {"appletpanel.exception", "undantag: {0}."}, + {"appletpanel.exception2", "undantag: {0}: {1}."}, + {"appletpanel.error", "fel: {0}."}, + {"appletpanel.error2", "fel {0}: {1}."}, + {"appletpanel.notloaded", "Initiera: applet \u00E4r inte inl\u00E4st."}, + {"appletpanel.notinited", "Starta: applet \u00E4r inte initierad."}, + {"appletpanel.notstarted", "Stoppa: applet har inte startats."}, + {"appletpanel.notstopped", "Radera: applet har inte stoppats."}, + {"appletpanel.notdestroyed", "Kassera: applet har inte raderats."}, + {"appletpanel.notdisposed", "Ladda: applet har inte kasserats."}, + {"appletpanel.bail", "Avbruten."}, + {"appletpanel.filenotfound", "Hittade inte fil vid s\u00F6kning efter: {0}"}, + {"appletpanel.fileformat", "Undantag av filformat vid laddning av: {0}"}, + {"appletpanel.fileioexception", "I/O-undantag vid laddning: {0}"}, + {"appletpanel.fileexception", "{0} undantag vid laddning: {1}"}, + {"appletpanel.filedeath", "{0} avslutad vid laddning: {1}"}, + {"appletpanel.fileerror", "{0} fel vid laddning: {1}"}, + {"appletpanel.badattribute.exception", "HTML-tolkning: felaktigt v\u00E4rde f\u00F6r bredd-/h\u00F6jdattribut"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream kr\u00E4ver laddare med icke-null"}, + {"appletprops.title", "AppletViewer-egenskaper"}, + {"appletprops.label.http.server", "HTTP-proxyserver:"}, + {"appletprops.label.http.proxy", "HTTP-proxyport:"}, + {"appletprops.label.network", "N\u00E4tverks\u00E5tkomst:"}, + {"appletprops.choice.network.item.none", "Ingen"}, + {"appletprops.choice.network.item.applethost", "Appletv\u00E4rd"}, + {"appletprops.choice.network.item.unrestricted", "Obegr\u00E4nsad"}, + {"appletprops.label.class", "Klass\u00E5tkomst:"}, + {"appletprops.choice.class.item.restricted", "Begr\u00E4nsad"}, + {"appletprops.choice.class.item.unrestricted", "Obegr\u00E4nsad"}, + {"appletprops.label.unsignedapplet", "Till\u00E5t osignerade appletar:"}, + {"appletprops.choice.unsignedapplet.no", "Nej"}, + {"appletprops.choice.unsignedapplet.yes", "Ja"}, + {"appletprops.button.apply", "Anv\u00E4nd"}, + {"appletprops.button.cancel", "Avbryt"}, + {"appletprops.button.reset", "\u00C5terst\u00E4ll"}, + {"appletprops.apply.exception", "Kunde inte spara egenskaper: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "Ogiltig post"}, + {"appletprops.label.invalidproxy", "Proxyport m\u00E5ste vara ett positivt heltal."}, + {"appletprops.button.ok", "OK"}, + /* end 4066432 */ + {"appletprops.prop.store", "Anv\u00E4ndarspecifika egenskaper f\u00F6r AppletViewer"}, + {"appletsecurityexception.checkcreateclassloader", "S\u00E4kerhetsundantag: klassladdare"}, + {"appletsecurityexception.checkaccess.thread", "S\u00E4kerhetsundantag: tr\u00E5d"}, + {"appletsecurityexception.checkaccess.threadgroup", "S\u00E4kerhetsundantag: tr\u00E5dgrupp: {0}"}, + {"appletsecurityexception.checkexit", "S\u00E4kerhetsundantag: utg\u00E5ng: {0}"}, + {"appletsecurityexception.checkexec", "S\u00E4kerhetsundantag: exec: {0}"}, + {"appletsecurityexception.checklink", "S\u00E4kerhetsundantag: l\u00E4nk: {0}"}, + {"appletsecurityexception.checkpropsaccess", "S\u00E4kerhetsundantag: egenskaper"}, + {"appletsecurityexception.checkpropsaccess.key", "S\u00E4kerhetsundantag: egenskaps\u00E5tkomst {0}"}, + {"appletsecurityexception.checkread.exception1", "S\u00E4kerhetsundantag: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "S\u00E4kerhetsundantag: file.read: {0}"}, + {"appletsecurityexception.checkread", "S\u00E4kerhetsundantag: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "S\u00E4kerhetsundantag: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "S\u00E4kerhetsundantag: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "S\u00E4kerhetsundantag: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "S\u00E4kerhetsundantag: fd.write"}, + {"appletsecurityexception.checklisten", "S\u00E4kerhetsundantag: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "S\u00E4kerhetsundantag: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "S\u00E4kerhetsundantag: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "S\u00E4kerhetsundantag: Kunde inte ansluta till {0} med ursprung fr\u00E5n {1}."}, + {"appletsecurityexception.checkconnect.networkhost2", "S\u00E4kerhetsundantag: Kunde inte matcha IP f\u00F6r v\u00E4rd {0} eller f\u00F6r {1}. "}, + {"appletsecurityexception.checkconnect.networkhost3", "S\u00E4kerhetsundantag: Kunde inte matcha IP f\u00F6r v\u00E4rd {0}. Se egenskapen trustProxy."}, + {"appletsecurityexception.checkconnect", "S\u00E4kerhetsundantag: connect: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "S\u00E4kerhetsundantag: ingen \u00E5tkomst till paket: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "S\u00E4kerhetsundantag: kan inte definiera paket: {0}"}, + {"appletsecurityexception.cannotsetfactory", "S\u00E4kerhetsundantag: kan inte ange fabrik"}, + {"appletsecurityexception.checkmemberaccess", "S\u00E4kerhetsundantag: kontrollera medlems\u00E5tkomst"}, + {"appletsecurityexception.checkgetprintjob", "S\u00E4kerhetsundantag: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "S\u00E4kerhetsundantag: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "S\u00E4kerhetsundantag: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "S\u00E4kerhetsundantag: s\u00E4kerhets\u00E5tg\u00E4rd: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "ok\u00E4nd typ av klassladdare. kan inte kontrollera getContext"}, + {"appletsecurityexception.checkread.unknown", "ok\u00E4nd typ av klassladdare. kan inte kontrollera kontroll\u00E4sning {0}"}, + {"appletsecurityexception.checkconnect.unknown", "ok\u00E4nd typ av klassladdare. kan inte kontrollera kontrollanslutning"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_zh_CN.java b/src/sun/applet/resources/MsgAppletViewer_zh_CN.java new file mode 100644 index 00000000..05171be4 --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_zh_CN.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_zh_CN extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "\u5173\u95ED"}, + {"appletviewer.tool.title", "\u5C0F\u5E94\u7528\u7A0B\u5E8F\u67E5\u770B\u5668: {0}"}, + {"appletviewer.menu.applet", "\u5C0F\u5E94\u7528\u7A0B\u5E8F"}, + {"appletviewer.menuitem.restart", "\u91CD\u65B0\u542F\u52A8"}, + {"appletviewer.menuitem.reload", "\u91CD\u65B0\u52A0\u8F7D"}, + {"appletviewer.menuitem.stop", "\u505C\u6B62"}, + {"appletviewer.menuitem.save", "\u4FDD\u5B58..."}, + {"appletviewer.menuitem.start", "\u542F\u52A8"}, + {"appletviewer.menuitem.clone", "\u514B\u9686..."}, + {"appletviewer.menuitem.tag", "\u6807\u8BB0..."}, + {"appletviewer.menuitem.info", "\u4FE1\u606F..."}, + {"appletviewer.menuitem.edit", "\u7F16\u8F91"}, + {"appletviewer.menuitem.encoding", "\u5B57\u7B26\u7F16\u7801"}, + {"appletviewer.menuitem.print", "\u6253\u5370..."}, + {"appletviewer.menuitem.props", "\u5C5E\u6027..."}, + {"appletviewer.menuitem.close", "\u5173\u95ED"}, + {"appletviewer.menuitem.quit", "\u9000\u51FA"}, + {"appletviewer.label.hello", "\u60A8\u597D..."}, + {"appletviewer.status.start", "\u6B63\u5728\u542F\u52A8\u5C0F\u5E94\u7528\u7A0B\u5E8F..."}, + {"appletviewer.appletsave.filedialogtitle","\u5C06\u5C0F\u5E94\u7528\u7A0B\u5E8F\u5E8F\u5217\u5316\u4E3A\u6587\u4EF6"}, + {"appletviewer.appletsave.err1", "\u5C06{0}\u5E8F\u5217\u5316\u4E3A{1}"}, + {"appletviewer.appletsave.err2", "\u5728 appletSave \u4E2D: {0}"}, + {"appletviewer.applettag", "\u663E\u793A\u7684\u6807\u8BB0"}, + {"appletviewer.applettag.textframe", "\u5C0F\u5E94\u7528\u7A0B\u5E8F HTML \u6807\u8BB0"}, + {"appletviewer.appletinfo.applet", "-- \u6CA1\u6709\u5C0F\u5E94\u7528\u7A0B\u5E8F\u4FE1\u606F --"}, + {"appletviewer.appletinfo.param", "-- \u6CA1\u6709\u53C2\u6570\u4FE1\u606F --"}, + {"appletviewer.appletinfo.textframe", "\u5C0F\u5E94\u7528\u7A0B\u5E8F\u4FE1\u606F"}, + {"appletviewer.appletprint.fail", "\u6253\u5370\u5931\u8D25\u3002"}, + {"appletviewer.appletprint.finish", "\u5DF2\u5B8C\u6210\u6253\u5370\u3002"}, + {"appletviewer.appletprint.cancel", "\u6253\u5370\u5DF2\u53D6\u6D88\u3002"}, + {"appletviewer.appletencoding", "\u5B57\u7B26\u7F16\u7801: {0}"}, + {"appletviewer.parse.warning.requiresname", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u540D\u79F0\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.paramoutside", "\u8B66\u544A: \u6807\u8BB0\u5728 ... \u5916\u90E8\u3002"}, + {"appletviewer.parse.warning.applet.requirescode", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u4EE3\u7801\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.applet.requiresheight", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u9AD8\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.applet.requireswidth", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u5BBD\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.object.requirescode", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u4EE3\u7801\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.object.requiresheight", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u9AD8\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.object.requireswidth", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u5BBD\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requirescode", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u4EE3\u7801\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requiresheight", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u9AD8\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requireswidth", "\u8B66\u544A: \u6807\u8BB0\u9700\u8981\u5BBD\u5EA6\u5C5E\u6027\u3002"}, + {"appletviewer.parse.warning.appnotLongersupported", "\u8B66\u544A: \u4E0D\u518D\u652F\u6301 \u6807\u8BB0, \u8BF7\u6539\u7528 :"}, + {"appletviewer.usage", "\u7528\u6CD5: appletviewer url\n\n\u5176\u4E2D, \u5305\u62EC:\n -debug \u5728 Java \u8C03\u8BD5\u5668\u4E2D\u542F\u52A8\u5C0F\u5E94\u7528\u7A0B\u5E8F\u67E5\u770B\u5668\n -encoding \u6307\u5B9A HTML \u6587\u4EF6\u4F7F\u7528\u7684\u5B57\u7B26\u7F16\u7801\n -J \u5C06\u53C2\u6570\u4F20\u9012\u5230 java \u89E3\u91CA\u5668\n\n-J \u9009\u9879\u662F\u975E\u6807\u51C6\u9009\u9879, \u5982\u6709\u66F4\u6539, \u6055\u4E0D\u53E6\u884C\u901A\u77E5\u3002"}, + {"appletviewer.main.err.unsupportedopt", "\u4E0D\u652F\u6301\u7684\u9009\u9879: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "\u65E0\u6CD5\u8BC6\u522B\u7684\u53C2\u6570: {0}"}, + {"appletviewer.main.err.dupoption", "\u91CD\u590D\u4F7F\u7528\u9009\u9879: {0}"}, + {"appletviewer.main.err.inputfile", "\u672A\u6307\u5B9A\u8F93\u5165\u6587\u4EF6\u3002"}, + {"appletviewer.main.err.badurl", "\u9519\u8BEF URL: {0} ({1})"}, + {"appletviewer.main.err.io", "\u8BFB\u53D6{0}\u65F6\u51FA\u73B0 I/O \u5F02\u5E38\u9519\u8BEF"}, + {"appletviewer.main.err.readablefile", "\u786E\u4FDD{0}\u662F\u6587\u4EF6\u4E14\u53EF\u8BFB\u3002"}, + {"appletviewer.main.err.correcturl", "{0} \u662F\u5426\u662F\u6B63\u786E\u7684 URL?"}, + {"appletviewer.main.prop.store", "AppletViewer \u7684\u7528\u6237\u7279\u5B9A\u5C5E\u6027"}, + {"appletviewer.main.err.prop.cantread", "\u65E0\u6CD5\u8BFB\u53D6\u7528\u6237\u5C5E\u6027\u6587\u4EF6: {0}"}, + {"appletviewer.main.err.prop.cantsave", "\u65E0\u6CD5\u4FDD\u5B58\u7528\u6237\u5C5E\u6027\u6587\u4EF6: {0}"}, + {"appletviewer.main.warn.nosecmgr", "\u8B66\u544A: \u7981\u7528\u5B89\u5168\u3002"}, + {"appletviewer.main.debug.cantfinddebug", "\u627E\u4E0D\u5230\u8C03\u8BD5\u5668!"}, + {"appletviewer.main.debug.cantfindmain", "\u5728\u8C03\u8BD5\u5668\u4E2D\u627E\u4E0D\u5230 main \u65B9\u6CD5!"}, + {"appletviewer.main.debug.exceptionindebug", "\u8C03\u8BD5\u5668\u4E2D\u5B58\u5728\u5F02\u5E38\u9519\u8BEF!"}, + {"appletviewer.main.debug.cantaccess", "\u65E0\u6CD5\u8BBF\u95EE\u8C03\u8BD5\u5668!"}, + {"appletviewer.main.nosecmgr", "\u8B66\u544A: \u672A\u5B89\u88C5 SecurityManager!"}, + {"appletviewer.main.warning", "\u8B66\u544A: \u672A\u542F\u52A8\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002\u786E\u4FDD\u8F93\u5165\u5305\u542B \u6807\u8BB0\u3002"}, + {"appletviewer.main.warn.prop.overwrite", "\u8B66\u544A: \u6839\u636E\u7528\u6237\u8BF7\u6C42\u4E34\u65F6\u8986\u76D6\u7CFB\u7EDF\u5C5E\u6027: \u5173\u952E\u5B57: {0}, \u65E7\u503C: {1}, \u65B0\u503C: {2}"}, + {"appletviewer.main.warn.cantreadprops", "\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AppletViewer \u5C5E\u6027\u6587\u4EF6: {0}\u3002\u8BF7\u4F7F\u7528\u9ED8\u8BA4\u503C\u3002"}, + {"appletioexception.loadclass.throw.interrupted", "\u7C7B\u52A0\u8F7D\u4E2D\u65AD: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "\u672A\u52A0\u8F7D\u7C7B: {0}"}, + {"appletclassloader.loadcode.verbose", "\u6253\u5F00\u5230{0}\u7684\u6D41\u4EE5\u83B7\u53D6{1}"}, + {"appletclassloader.filenotfound", "\u67E5\u627E\u65F6\u627E\u4E0D\u5230\u6587\u4EF6: {0}"}, + {"appletclassloader.fileformat", "\u52A0\u8F7D\u65F6\u51FA\u73B0\u6587\u4EF6\u683C\u5F0F\u5F02\u5E38\u9519\u8BEF: {0}"}, + {"appletclassloader.fileioexception", "\u52A0\u8F7D\u65F6\u51FA\u73B0 I/O \u5F02\u5E38\u9519\u8BEF: {0}"}, + {"appletclassloader.fileexception", "\u52A0\u8F7D\u65F6\u51FA\u73B0{0}\u5F02\u5E38\u9519\u8BEF: {1}"}, + {"appletclassloader.filedeath", "\u52A0\u8F7D\u65F6\u5DF2\u7EC8\u6B62{0}: {1}"}, + {"appletclassloader.fileerror", "\u52A0\u8F7D\u65F6\u51FA\u73B0{0}\u9519\u8BEF: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "\u6253\u5F00\u5230{0}\u7684\u6D41\u4EE5\u83B7\u53D6{1}"}, + {"appletclassloader.getresource.verbose.forname", "\u540D\u79F0\u7684 AppletClassLoader.getResource: {0}"}, + {"appletclassloader.getresource.verbose.found", "\u5DF2\u627E\u5230\u4F5C\u4E3A\u7CFB\u7EDF\u8D44\u6E90\u7684\u8D44\u6E90{0}"}, + {"appletclassloader.getresourceasstream.verbose", "\u5DF2\u627E\u5230\u4F5C\u4E3A\u7CFB\u7EDF\u8D44\u6E90\u7684\u8D44\u6E90{0}"}, + {"appletpanel.runloader.err", "\u5BF9\u8C61\u6216\u4EE3\u7801\u53C2\u6570!"}, + {"appletpanel.runloader.exception", "\u53CD\u5E8F\u5217\u5316{0}\u65F6\u51FA\u73B0\u5F02\u5E38\u9519\u8BEF"}, + {"appletpanel.destroyed", "\u5DF2\u9500\u6BC1\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.loaded", "\u5DF2\u52A0\u8F7D\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.started", "\u5DF2\u542F\u52A8\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.inited", "\u5DF2\u521D\u59CB\u5316\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.stopped", "\u5DF2\u505C\u6B62\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.disposed", "\u5DF2\u5904\u7406\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.nocode", "APPLET \u6807\u8BB0\u7F3A\u5C11 CODE \u53C2\u6570\u3002"}, + {"appletpanel.notfound", "\u52A0\u8F7D: \u627E\u4E0D\u5230\u7C7B{0}\u3002"}, + {"appletpanel.nocreate", "\u52A0\u8F7D: \u65E0\u6CD5\u5B9E\u4F8B\u5316{0}\u3002"}, + {"appletpanel.noconstruct", "\u52A0\u8F7D: {0}\u4E0D\u662F\u516C\u5171\u7684, \u6216\u8005\u6CA1\u6709\u516C\u5171\u6784\u9020\u5668\u3002"}, + {"appletpanel.death", "\u5DF2\u7EC8\u6B62"}, + {"appletpanel.exception", "\u5F02\u5E38\u9519\u8BEF: {0}\u3002"}, + {"appletpanel.exception2", "\u5F02\u5E38\u9519\u8BEF: {0}: {1}\u3002"}, + {"appletpanel.error", "\u9519\u8BEF: {0}\u3002"}, + {"appletpanel.error2", "\u9519\u8BEF: {0}: {1}\u3002"}, + {"appletpanel.notloaded", "\u521D\u59CB\u5316: \u672A\u52A0\u8F7D\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.notinited", "\u542F\u52A8: \u672A\u521D\u59CB\u5316\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.notstarted", "\u505C\u6B62: \u672A\u542F\u52A8\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.notstopped", "\u9500\u6BC1: \u672A\u505C\u6B62\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.notdestroyed", "\u5904\u7406: \u672A\u9500\u6BC1\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.notdisposed", "\u52A0\u8F7D: \u672A\u5904\u7406\u5C0F\u5E94\u7528\u7A0B\u5E8F\u3002"}, + {"appletpanel.bail", "\u5DF2\u4E2D\u65AD: \u79BB\u5F00\u3002"}, + {"appletpanel.filenotfound", "\u67E5\u627E\u65F6\u627E\u4E0D\u5230\u6587\u4EF6: {0}"}, + {"appletpanel.fileformat", "\u52A0\u8F7D\u65F6\u51FA\u73B0\u6587\u4EF6\u683C\u5F0F\u5F02\u5E38\u9519\u8BEF: {0}"}, + {"appletpanel.fileioexception", "\u52A0\u8F7D\u65F6\u51FA\u73B0 I/O \u5F02\u5E38\u9519\u8BEF: {0}"}, + {"appletpanel.fileexception", "\u52A0\u8F7D\u65F6\u51FA\u73B0{0}\u5F02\u5E38\u9519\u8BEF: {1}"}, + {"appletpanel.filedeath", "\u52A0\u8F7D\u65F6\u5DF2\u7EC8\u6B62{0}: {1}"}, + {"appletpanel.fileerror", "\u52A0\u8F7D\u65F6\u51FA\u73B0{0}\u9519\u8BEF: {1}"}, + {"appletpanel.badattribute.exception", "HTML \u89E3\u6790: \u5BBD\u5EA6/\u9AD8\u5EA6\u5C5E\u6027\u7684\u503C\u4E0D\u6B63\u786E"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream \u9700\u8981\u975E\u7A7A\u52A0\u8F7D\u5668"}, + {"appletprops.title", "AppletViewer \u5C5E\u6027"}, + {"appletprops.label.http.server", "Http \u4EE3\u7406\u670D\u52A1\u5668:"}, + {"appletprops.label.http.proxy", "Http \u4EE3\u7406\u7AEF\u53E3:"}, + {"appletprops.label.network", "\u7F51\u7EDC\u8BBF\u95EE\u6743\u9650:"}, + {"appletprops.choice.network.item.none", "\u65E0"}, + {"appletprops.choice.network.item.applethost", "\u5C0F\u5E94\u7528\u7A0B\u5E8F\u4E3B\u673A"}, + {"appletprops.choice.network.item.unrestricted", "\u4E0D\u53D7\u9650\u5236"}, + {"appletprops.label.class", "\u7C7B\u8BBF\u95EE\u6743\u9650:"}, + {"appletprops.choice.class.item.restricted", "\u53D7\u9650\u5236"}, + {"appletprops.choice.class.item.unrestricted", "\u4E0D\u53D7\u9650\u5236"}, + {"appletprops.label.unsignedapplet", "\u5141\u8BB8\u672A\u7B7E\u540D\u5C0F\u5E94\u7528\u7A0B\u5E8F:"}, + {"appletprops.choice.unsignedapplet.no", "\u5426"}, + {"appletprops.choice.unsignedapplet.yes", "\u662F"}, + {"appletprops.button.apply", "\u5E94\u7528"}, + {"appletprops.button.cancel", "\u53D6\u6D88"}, + {"appletprops.button.reset", "\u91CD\u7F6E"}, + {"appletprops.apply.exception", "\u65E0\u6CD5\u4FDD\u5B58\u5C5E\u6027: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "\u6761\u76EE\u65E0\u6548"}, + {"appletprops.label.invalidproxy", "\u4EE3\u7406\u7AEF\u53E3\u5FC5\u987B\u662F\u4E00\u4E2A\u6B63\u6574\u6570\u503C\u3002"}, + {"appletprops.button.ok", "\u786E\u5B9A"}, + /* end 4066432 */ + {"appletprops.prop.store", "AppletViewer \u7684\u7528\u6237\u7279\u5B9A\u5C5E\u6027"}, + {"appletsecurityexception.checkcreateclassloader", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u7C7B\u52A0\u8F7D\u5668"}, + {"appletsecurityexception.checkaccess.thread", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u7EBF\u7A0B"}, + {"appletsecurityexception.checkaccess.threadgroup", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u7EBF\u7A0B\u7EC4: {0}"}, + {"appletsecurityexception.checkexit", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u9000\u51FA: {0}"}, + {"appletsecurityexception.checkexec", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u6267\u884C: {0}"}, + {"appletsecurityexception.checklink", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u94FE\u63A5: {0}"}, + {"appletsecurityexception.checkpropsaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u5C5E\u6027"}, + {"appletsecurityexception.checkpropsaccess.key", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u5C5E\u6027\u8BBF\u95EE{0}"}, + {"appletsecurityexception.checkread.exception1", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: {0}, {1}"}, + {"appletsecurityexception.checkread.exception2", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: file.read: {0}"}, + {"appletsecurityexception.checkread", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: {0}, {1}"}, + {"appletsecurityexception.checkwrite", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: fd.write"}, + {"appletsecurityexception.checklisten", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u8FDE\u63A5\u5230\u6E90\u81EA{1}\u7684{0}\u3002"}, + {"appletsecurityexception.checkconnect.networkhost2", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u89E3\u6790\u4E3B\u673A{0}\u6216{1}\u7684 IP\u3002"}, + {"appletsecurityexception.checkconnect.networkhost3", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u89E3\u6790\u4E3B\u673A{0}\u7684 IP\u3002\u8BF7\u53C2\u9605 trustProxy \u5C5E\u6027\u3002"}, + {"appletsecurityexception.checkconnect", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u8FDE\u63A5: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u8BBF\u95EE\u7A0B\u5E8F\u5305: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u5B9A\u4E49\u7A0B\u5E8F\u5305: {0}"}, + {"appletsecurityexception.cannotsetfactory", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u65E0\u6CD5\u8BBE\u7F6E\u5DE5\u5382"}, + {"appletsecurityexception.checkmemberaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u68C0\u67E5\u6210\u5458\u8BBF\u95EE\u6743\u9650"}, + {"appletsecurityexception.checkgetprintjob", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "\u5B89\u5168\u5F02\u5E38\u9519\u8BEF: \u5B89\u5168\u64CD\u4F5C: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "\u7C7B\u52A0\u8F7D\u5668\u7C7B\u578B\u672A\u77E5\u3002\u65E0\u6CD5\u68C0\u67E5 getContext"}, + {"appletsecurityexception.checkread.unknown", "\u7C7B\u52A0\u8F7D\u5668\u7C7B\u578B\u672A\u77E5\u3002\u65E0\u6CD5\u4E3A\u68C0\u67E5\u8BFB\u53D6\u6743\u9650{0}\u800C\u8FDB\u884C\u68C0\u67E5"}, + {"appletsecurityexception.checkconnect.unknown", "\u7C7B\u52A0\u8F7D\u5668\u7C7B\u578B\u672A\u77E5\u3002\u65E0\u6CD5\u4E3A\u68C0\u67E5\u8FDE\u63A5\u800C\u8FDB\u884C\u68C0\u67E5"}, + }; + + return temp; + } +} diff --git a/src/sun/applet/resources/MsgAppletViewer_zh_TW.java b/src/sun/applet/resources/MsgAppletViewer_zh_TW.java new file mode 100644 index 00000000..65b079db --- /dev/null +++ b/src/sun/applet/resources/MsgAppletViewer_zh_TW.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.applet.resources; + +import java.util.ListResourceBundle; + +public class MsgAppletViewer_zh_TW extends ListResourceBundle { + + public Object[][] getContents() { + Object[][] temp = new Object[][] { + {"textframe.button.dismiss", "\u95DC\u9589"}, + {"appletviewer.tool.title", "Applet \u6AA2\u8996\u5668: {0}"}, + {"appletviewer.menu.applet", "Applet"}, + {"appletviewer.menuitem.restart", "\u91CD\u65B0\u555F\u52D5"}, + {"appletviewer.menuitem.reload", "\u91CD\u65B0\u8F09\u5165"}, + {"appletviewer.menuitem.stop", "\u505C\u6B62"}, + {"appletviewer.menuitem.save", "\u5132\u5B58..."}, + {"appletviewer.menuitem.start", "\u555F\u52D5"}, + {"appletviewer.menuitem.clone", "\u8907\u88FD..."}, + {"appletviewer.menuitem.tag", "\u6A19\u8A18..."}, + {"appletviewer.menuitem.info", "\u8CC7\u8A0A..."}, + {"appletviewer.menuitem.edit", "\u7DE8\u8F2F"}, + {"appletviewer.menuitem.encoding", "\u5B57\u5143\u7DE8\u78BC"}, + {"appletviewer.menuitem.print", "\u5217\u5370..."}, + {"appletviewer.menuitem.props", "\u5C6C\u6027..."}, + {"appletviewer.menuitem.close", "\u95DC\u9589"}, + {"appletviewer.menuitem.quit", "\u7D50\u675F"}, + {"appletviewer.label.hello", "\u60A8\u597D..."}, + {"appletviewer.status.start", "\u6B63\u5728\u555F\u52D5 Applet..."}, + {"appletviewer.appletsave.filedialogtitle","\u5C07 Applet \u5E8F\u5217\u5316\u70BA\u6A94\u6848"}, + {"appletviewer.appletsave.err1", "\u5C07 {0} \u5E8F\u5217\u5316\u70BA {1}"}, + {"appletviewer.appletsave.err2", "\u5728 appletSave \u4E2D: {0}"}, + {"appletviewer.applettag", "\u986F\u793A\u7684\u6A19\u8A18"}, + {"appletviewer.applettag.textframe", "Applet HTML \u6A19\u8A18"}, + {"appletviewer.appletinfo.applet", "-- \u7121 Applet \u8CC7\u8A0A --"}, + {"appletviewer.appletinfo.param", "-- \u7121\u53C3\u6578\u8CC7\u8A0A --"}, + {"appletviewer.appletinfo.textframe", "Applet \u8CC7\u8A0A"}, + {"appletviewer.appletprint.fail", "\u5217\u5370\u5931\u6557\u3002"}, + {"appletviewer.appletprint.finish", "\u5B8C\u6210\u5217\u5370\u3002"}, + {"appletviewer.appletprint.cancel", "\u5217\u5370\u53D6\u6D88\u3002"}, + {"appletviewer.appletencoding", "\u5B57\u5143\u7DE8\u78BC: {0}"}, + {"appletviewer.parse.warning.requiresname", "\u8B66\u544A: <\u53C3\u6578\u540D\u7A31=... \u503C=...> \u6A19\u8A18\u9700\u8981\u540D\u7A31\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.paramoutside", "\u8B66\u544A: \u6A19\u8A18\u5728 ... \u4E4B\u5916\u3002"}, + {"appletviewer.parse.warning.applet.requirescode", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u4EE3\u78BC\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.applet.requiresheight", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u9AD8\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.applet.requireswidth", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u5BEC\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.object.requirescode", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u4EE3\u78BC\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.object.requiresheight", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u9AD8\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.object.requireswidth", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u5BEC\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requirescode", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u4EE3\u78BC\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requiresheight", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u9AD8\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.embed.requireswidth", "\u8B66\u544A: \u6A19\u8A18\u9700\u8981\u5BEC\u5EA6\u5C6C\u6027\u3002"}, + {"appletviewer.parse.warning.appnotLongersupported", "\u8B66\u544A: \u4E0D\u518D\u652F\u63F4 \u6A19\u8A18\uFF0C\u8ACB\u6539\u7528 :"}, + {"appletviewer.usage", "\u7528\u6CD5: appletviewer url(s)\n\n\u5176\u4E2D\u7684 \u5305\u62EC:\n -debug \u5728 Java \u9664\u932F\u7A0B\u5F0F\u4E2D\u555F\u52D5 Applet \u6AA2\u8996\u5668\n -encoding \u6307\u5B9A HTML \u6A94\u6848\u4F7F\u7528\u7684\u5B57\u5143\u7DE8\u78BC\n -J \u5C07\u5F15\u6578\u50B3\u9001\u81F3 java \u89E3\u8B6F\u5668\n\n -J \u9078\u9805\u4E0D\u662F\u6A19\u6E96\u9078\u9805\uFF0C\u82E5\u6709\u8B8A\u66F4\u4E0D\u53E6\u884C\u901A\u77E5\u3002"}, + {"appletviewer.main.err.unsupportedopt", "\u4E0D\u652F\u63F4\u7684\u9078\u9805: {0}"}, + {"appletviewer.main.err.unrecognizedarg", "\u7121\u6CD5\u8FA8\u8B58\u7684\u5F15\u6578: {0}"}, + {"appletviewer.main.err.dupoption", "\u91CD\u8907\u4F7F\u7528\u9078\u9805: {0}"}, + {"appletviewer.main.err.inputfile", "\u672A\u6307\u5B9A\u8F38\u5165\u6A94\u6848\u3002"}, + {"appletviewer.main.err.badurl", "\u932F\u8AA4\u7684 URL: {0} ( {1} )"}, + {"appletviewer.main.err.io", "\u8B80\u53D6\u6642\u767C\u751F I/O \u7570\u5E38\u72C0\u6CC1: {0}"}, + {"appletviewer.main.err.readablefile", "\u78BA\u8A8D {0} \u70BA\u6A94\u6848\u4E14\u53EF\u8B80\u53D6\u3002"}, + {"appletviewer.main.err.correcturl", "{0} \u662F\u5426\u70BA\u6B63\u78BA\u7684 URL\uFF1F"}, + {"appletviewer.main.prop.store", "AppletViewer \u7684\u4F7F\u7528\u8005\u7279\u5B9A\u5C6C\u6027"}, + {"appletviewer.main.err.prop.cantread", "\u7121\u6CD5\u8B80\u53D6\u4F7F\u7528\u8005\u5C6C\u6027\u6A94\u6848: {0}"}, + {"appletviewer.main.err.prop.cantsave", "\u7121\u6CD5\u5132\u5B58\u4F7F\u7528\u8005\u5C6C\u6027\u6A94\u6848: {0}"}, + {"appletviewer.main.warn.nosecmgr", "\u8B66\u544A: \u505C\u7528\u5B89\u5168\u529F\u80FD\u3002"}, + {"appletviewer.main.debug.cantfinddebug", "\u627E\u4E0D\u5230\u9664\u932F\u7A0B\u5F0F\uFF01"}, + {"appletviewer.main.debug.cantfindmain", "\u5728\u9664\u932F\u7A0B\u5F0F\u4E2D\u627E\u4E0D\u5230\u4E3B\u8981\u65B9\u6CD5\uFF01"}, + {"appletviewer.main.debug.exceptionindebug", "\u9664\u932F\u7A0B\u5F0F\u767C\u751F\u7570\u5E38\u72C0\u6CC1\uFF01"}, + {"appletviewer.main.debug.cantaccess", "\u7121\u6CD5\u5B58\u53D6\u9664\u932F\u7A0B\u5F0F\uFF01"}, + {"appletviewer.main.nosecmgr", "\u8B66\u544A: \u672A\u5B89\u88DD SecurityManager\uFF01"}, + {"appletviewer.main.warning", "\u8B66\u544A: \u672A\u555F\u52D5 Applet\u3002\u8ACB\u78BA\u8A8D\u8F38\u5165\u5305\u542B \u6A19\u8A18\u3002"}, + {"appletviewer.main.warn.prop.overwrite", "\u8B66\u544A: \u4F9D\u7167\u4F7F\u7528\u8005\u8981\u6C42\uFF0C\u66AB\u6642\u8986\u5BEB\u7CFB\u7D71\u5C6C\u6027: \u7D22\u5F15\u9375: {0} \u820A\u503C: {1} \u65B0\u503C: {2}"}, + {"appletviewer.main.warn.cantreadprops", "\u8B66\u544A: \u7121\u6CD5\u8B80\u53D6 AppletViewer \u5C6C\u6027\u6A94\u6848: {0} \u4F7F\u7528\u9810\u8A2D\u503C\u3002"}, + {"appletioexception.loadclass.throw.interrupted", "\u985E\u5225\u8F09\u5165\u4E2D\u65B7: {0}"}, + {"appletioexception.loadclass.throw.notloaded", "\u672A\u8F09\u5165\u985E\u5225: {0}"}, + {"appletclassloader.loadcode.verbose", "\u958B\u555F {0} \u7684\u4E32\u6D41\u4EE5\u53D6\u5F97 {1}"}, + {"appletclassloader.filenotfound", "\u5C0B\u627E {0} \u6642\u627E\u4E0D\u5230\u6A94\u6848"}, + {"appletclassloader.fileformat", "\u8F09\u5165\u6642\u767C\u751F\u6A94\u6848\u683C\u5F0F\u7570\u5E38\u72C0\u6CC1: {0}"}, + {"appletclassloader.fileioexception", "\u8F09\u5165\u6642\u767C\u751F I/O \u7570\u5E38\u72C0\u6CC1: {0}"}, + {"appletclassloader.fileexception", "\u8F09\u5165\u6642\u767C\u751F {0} \u7570\u5E38\u72C0\u6CC1: {1}"}, + {"appletclassloader.filedeath", "\u8F09\u5165\u6642\u522A\u9664 {0}: {1}"}, + {"appletclassloader.fileerror", "\u8F09\u5165\u6642\u767C\u751F {0} \u932F\u8AA4: {1}"}, + {"appletclassloader.findclass.verbose.openstream", "\u958B\u555F {0} \u7684\u4E32\u6D41\u4EE5\u53D6\u5F97 {1}"}, + {"appletclassloader.getresource.verbose.forname", "AppletClassLoader.getResource \u7684\u540D\u7A31: {0}"}, + {"appletclassloader.getresource.verbose.found", "\u627E\u5230\u8CC7\u6E90: {0} \u4F5C\u70BA\u7CFB\u7D71\u8CC7\u6E90"}, + {"appletclassloader.getresourceasstream.verbose", "\u627E\u5230\u8CC7\u6E90: {0} \u4F5C\u70BA\u7CFB\u7D71\u8CC7\u6E90"}, + {"appletpanel.runloader.err", "\u7269\u4EF6\u6216\u4EE3\u78BC\u53C3\u6578\uFF01"}, + {"appletpanel.runloader.exception", "\u9084\u539F\u5E8F\u5217\u5316 {0} \u6642\u767C\u751F\u7570\u5E38\u72C0\u6CC1"}, + {"appletpanel.destroyed", "\u5DF2\u640D\u6BC0 Applet\u3002"}, + {"appletpanel.loaded", "\u5DF2\u8F09\u5165 Applet\u3002"}, + {"appletpanel.started", "\u5DF2\u555F\u7528 Applet\u3002"}, + {"appletpanel.inited", "\u5DF2\u8D77\u59CB Applet\u3002"}, + {"appletpanel.stopped", "\u5DF2\u505C\u6B62 Applet\u3002"}, + {"appletpanel.disposed", "\u5DF2\u8655\u7F6E Applet\u3002"}, + {"appletpanel.nocode", "APPLET \u6A19\u8A18\u907A\u6F0F CODE \u53C3\u6578\u3002"}, + {"appletpanel.notfound", "\u8F09\u5165: \u627E\u4E0D\u5230\u985E\u5225 {0}\u3002"}, + {"appletpanel.nocreate", "\u8F09\u5165: \u7121\u6CD5\u5EFA\u7ACB {0}\u3002"}, + {"appletpanel.noconstruct", "\u8F09\u5165: {0} \u975E\u516C\u7528\u6216\u6C92\u6709\u516C\u7528\u5EFA\u69CB\u5B50\u3002"}, + {"appletpanel.death", "\u5DF2\u522A\u9664"}, + {"appletpanel.exception", "\u7570\u5E38\u72C0\u6CC1: {0}\u3002"}, + {"appletpanel.exception2", "\u7570\u5E38\u72C0\u6CC1: {0}: {1}\u3002"}, + {"appletpanel.error", "\u932F\u8AA4: {0}\u3002"}, + {"appletpanel.error2", "\u932F\u8AA4: {0}: {1}\u3002"}, + {"appletpanel.notloaded", "\u8D77\u59CB: \u672A\u8F09\u5165 Applet\u3002"}, + {"appletpanel.notinited", "\u555F\u52D5: \u672A\u8D77\u59CB Applet\u3002"}, + {"appletpanel.notstarted", "\u505C\u6B62: \u672A\u555F\u52D5 Applet\u3002"}, + {"appletpanel.notstopped", "\u640D\u6BC0: \u672A\u505C\u6B62 Applet\u3002"}, + {"appletpanel.notdestroyed", "\u8655\u7F6E: \u672A\u640D\u6BC0 Applet\u3002"}, + {"appletpanel.notdisposed", "\u8F09\u5165: \u672A\u8655\u7F6E Applet\u3002"}, + {"appletpanel.bail", "\u5DF2\u4E2D\u65B7: \u6B63\u5728\u7D50\u675F\u3002"}, + {"appletpanel.filenotfound", "\u5C0B\u627E {0} \u6642\u627E\u4E0D\u5230\u6A94\u6848"}, + {"appletpanel.fileformat", "\u8F09\u5165\u6642\u767C\u751F\u6A94\u6848\u683C\u5F0F\u7570\u5E38\u72C0\u6CC1: {0}"}, + {"appletpanel.fileioexception", "\u8F09\u5165\u6642\u767C\u751F I/O \u7570\u5E38\u72C0\u6CC1: {0}"}, + {"appletpanel.fileexception", "\u8F09\u5165\u6642\u767C\u751F {0} \u7570\u5E38\u72C0\u6CC1: {1}"}, + {"appletpanel.filedeath", "\u8F09\u5165\u6642\u522A\u9664 {0}: {1}"}, + {"appletpanel.fileerror", "\u8F09\u5165\u6642\u767C\u751F {0} \u932F\u8AA4: {1}"}, + {"appletpanel.badattribute.exception", "HTML \u5256\u6790: \u5BEC\u5EA6/\u9AD8\u5EA6\u5C6C\u6027\u7684\u503C\u4E0D\u6B63\u78BA"}, + {"appletillegalargumentexception.objectinputstream", "AppletObjectInputStream \u9700\u8981\u975E\u7A7A\u503C\u8F09\u5165\u5668"}, + {"appletprops.title", "AppletViewer \u5C6C\u6027"}, + {"appletprops.label.http.server", "Http \u4EE3\u7406\u4E3B\u6A5F\u4F3A\u670D\u5668:"}, + {"appletprops.label.http.proxy", "Http \u4EE3\u7406\u4E3B\u6A5F\u9023\u63A5\u57E0:"}, + {"appletprops.label.network", "\u7DB2\u8DEF\u5B58\u53D6:"}, + {"appletprops.choice.network.item.none", "\u7121"}, + {"appletprops.choice.network.item.applethost", "Applet \u4E3B\u6A5F"}, + {"appletprops.choice.network.item.unrestricted", "\u4E0D\u53D7\u9650\u5236"}, + {"appletprops.label.class", "\u985E\u5225\u5B58\u53D6:"}, + {"appletprops.choice.class.item.restricted", "\u53D7\u9650\u5236"}, + {"appletprops.choice.class.item.unrestricted", "\u4E0D\u53D7\u9650\u5236"}, + {"appletprops.label.unsignedapplet", "\u5141\u8A31\u672A\u7C3D\u7F72\u7684 Applet:"}, + {"appletprops.choice.unsignedapplet.no", "\u5426"}, + {"appletprops.choice.unsignedapplet.yes", "\u662F"}, + {"appletprops.button.apply", "\u5957\u7528"}, + {"appletprops.button.cancel", "\u53D6\u6D88"}, + {"appletprops.button.reset", "\u91CD\u8A2D"}, + {"appletprops.apply.exception", "\u7121\u6CD5\u5132\u5B58\u5C6C\u6027: {0}"}, + /* 4066432 */ + {"appletprops.title.invalidproxy", "\u7121\u6548\u7684\u9805\u76EE"}, + {"appletprops.label.invalidproxy", "\u4EE3\u7406\u4E3B\u6A5F\u9023\u63A5\u57E0\u5FC5\u9808\u662F\u6B63\u6574\u6578\u503C\u3002"}, + {"appletprops.button.ok", "\u78BA\u5B9A"}, + /* end 4066432 */ + {"appletprops.prop.store", "AppletViewer \u7684\u4F7F\u7528\u8005\u7279\u5B9A\u5C6C\u6027"}, + {"appletsecurityexception.checkcreateclassloader", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: classloader"}, + {"appletsecurityexception.checkaccess.thread", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: thread"}, + {"appletsecurityexception.checkaccess.threadgroup", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: threadgroup: {0}"}, + {"appletsecurityexception.checkexit", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: exit: {0}"}, + {"appletsecurityexception.checkexec", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: exec: {0}"}, + {"appletsecurityexception.checklink", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: link: {0}"}, + {"appletsecurityexception.checkpropsaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u5C6C\u6027"}, + {"appletsecurityexception.checkpropsaccess.key", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u5C6C\u6027\u5B58\u53D6 {0}"}, + {"appletsecurityexception.checkread.exception1", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: {0}\uFF0C{1}"}, + {"appletsecurityexception.checkread.exception2", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: file.read: {0}"}, + {"appletsecurityexception.checkread", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: file.read: {0} == {1}"}, + {"appletsecurityexception.checkwrite.exception", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: {0}\uFF0C{1}"}, + {"appletsecurityexception.checkwrite", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: file.write: {0} == {1}"}, + {"appletsecurityexception.checkread.fd", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: fd.read"}, + {"appletsecurityexception.checkwrite.fd", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: fd.write"}, + {"appletsecurityexception.checklisten", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: socket.listen: {0}"}, + {"appletsecurityexception.checkaccept", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: socket.accept: {0}:{1}"}, + {"appletsecurityexception.checkconnect.networknone", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: socket.connect: {0}->{1}"}, + {"appletsecurityexception.checkconnect.networkhost1", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u5F9E\u4F86\u6E90 {1} \u9023\u7DDA\u81F3 {0}\u3002"}, + {"appletsecurityexception.checkconnect.networkhost2", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u89E3\u6790\u4E3B\u6A5F {0} \u6216 {1} \u7684 IP\u3002"}, + {"appletsecurityexception.checkconnect.networkhost3", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u89E3\u6790\u4E3B\u6A5F {0} \u7684 IP\u3002\u8ACB\u53C3\u95B1 trustProxy \u5C6C\u6027\u3002"}, + {"appletsecurityexception.checkconnect", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: connect: {0}->{1}"}, + {"appletsecurityexception.checkpackageaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u5B58\u53D6\u5957\u88DD\u7A0B\u5F0F: {0}"}, + {"appletsecurityexception.checkpackagedefinition", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u5B9A\u7FA9\u5957\u88DD\u7A0B\u5F0F: {0}"}, + {"appletsecurityexception.cannotsetfactory", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u7121\u6CD5\u8A2D\u5B9A\u8655\u7406\u7AD9"}, + {"appletsecurityexception.checkmemberaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u6AA2\u67E5\u6210\u54E1\u5B58\u53D6"}, + {"appletsecurityexception.checkgetprintjob", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: getPrintJob"}, + {"appletsecurityexception.checksystemclipboardaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: getSystemClipboard"}, + {"appletsecurityexception.checkawteventqueueaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: getEventQueue"}, + {"appletsecurityexception.checksecurityaccess", "\u5B89\u5168\u7570\u5E38\u72C0\u6CC1: \u5B89\u5168\u4F5C\u696D: {0}"}, + {"appletsecurityexception.getsecuritycontext.unknown", "\u4E0D\u660E\u7684\u985E\u5225\u8F09\u5165\u5668\u985E\u578B\u3002\u7121\u6CD5\u6AA2\u67E5 getContext"}, + {"appletsecurityexception.checkread.unknown", "\u4E0D\u660E\u7684\u985E\u5225\u8F09\u5165\u5668\u985E\u578B\u3002\u7121\u6CD5\u6AA2\u67E5 read {0}"}, + {"appletsecurityexception.checkconnect.unknown", "\u4E0D\u660E\u7684\u985E\u5225\u8F09\u5165\u5668\u985E\u578B\u3002\u7121\u6CD5\u6AA2\u67E5\u9023\u7DDA"}, + }; + + return temp; + } +} diff --git a/src/sun/misc/ASCIICaseInsensitiveComparator.java b/src/sun/misc/ASCIICaseInsensitiveComparator.java new file mode 100644 index 00000000..9fdc6b53 --- /dev/null +++ b/src/sun/misc/ASCIICaseInsensitiveComparator.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.Comparator; + +/** Implements a locale and case insensitive comparator suitable for + strings that are known to only contain ASCII characters. Some + tables internal to the JDK contain only ASCII data and are using + the "generalized" java.lang.String case-insensitive comparator + which converts each character to both upper and lower case. */ + +public class ASCIICaseInsensitiveComparator implements Comparator { + public static final Comparator CASE_INSENSITIVE_ORDER = + new ASCIICaseInsensitiveComparator(); + + public int compare(String s1, String s2) { + int n1=s1.length(), n2=s2.length(); + int minLen = n1 < n2 ? n1 : n2; + for (int i=0; i < minLen; i++) { + char c1 = s1.charAt(i); + char c2 = s2.charAt(i); + assert c1 <= '\u007F' && c2 <= '\u007F'; + if (c1 != c2) { + c1 = (char)toLower(c1); + c2 = (char)toLower(c2); + if (c1 != c2) { + return c1 - c2; + } + } + } + return n1 - n2; + } + + /** + * A case insensitive hash code method to go with the case insensitive + * compare() method. + * + * Returns a hash code for this ASCII string as if it were lower case. + * + * returns same answer as:

    + * s.toLowerCase(Locale.US).hashCode();

    + * but does not allocate memory (it does NOT have the special + * case Turkish rules). + * + * @param s a String to compute the hashcode on. + * @return a hash code value for this object. + */ + public static int lowerCaseHashCode(String s) { + int h = 0; + int len = s.length(); + + for (int i = 0; i < len; i++) { + h = 31*h + toLower(s.charAt(i)); + } + + return h; + } + + /* If java.util.regex.ASCII ever becomes public or sun.*, use its code instead:*/ + static boolean isLower(int ch) { + return ((ch-'a')|('z'-ch)) >= 0; + } + + static boolean isUpper(int ch) { + return ((ch-'A')|('Z'-ch)) >= 0; + } + + static int toLower(int ch) { + return isUpper(ch) ? (ch + 0x20) : ch; + } + + static int toUpper(int ch) { + return isLower(ch) ? (ch - 0x20) : ch; + } +} diff --git a/src/sun/misc/BASE64Decoder.java b/src/sun/misc/BASE64Decoder.java new file mode 100644 index 00000000..2c892ce6 --- /dev/null +++ b/src/sun/misc/BASE64Decoder.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.OutputStream; +import java.io.PushbackInputStream; + +/** + * This class implements a BASE64 Character decoder as specified in RFC1521. + * + * This RFC is part of the MIME specification which is published by the + * Internet Engineering Task Force (IETF). Unlike some other encoding + * schemes there is nothing in this encoding that tells the decoder + * where a buffer starts or stops, so to use it you will need to isolate + * your encoded data into a single chunk and then feed them this decoder. + * The simplest way to do that is to read all of the encoded data into a + * string and then use: + *

    + *      byte    mydata[];
    + *      BASE64Decoder base64 = new BASE64Decoder();
    + *
    + *      mydata = base64.decodeBuffer(bufferString);
    + * 
    + * This will decode the String in bufferString and give you an array + * of bytes in the array myData. + * + * On errors, this class throws a CEFormatException with the following detail + * strings: + *
    + *    "BASE64Decoder: Not enough bytes for an atom."
    + * 
    + * + * @author Chuck McManis + * @see CharacterEncoder + * @see BASE64Decoder + */ + +public class BASE64Decoder extends CharacterDecoder { + + /** This class has 4 bytes per atom */ + protected int bytesPerAtom() { + return (4); + } + + /** Any multiple of 4 will do, 72 might be common */ + protected int bytesPerLine() { + return (72); + } + + /** + * This character array provides the character to value map + * based on RFC1521. + */ + private final static char pem_array[] = { + // 0 1 2 3 4 5 6 7 + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+','/' // 7 + }; + + private final static byte pem_convert_array[] = new byte[256]; + + static { + for (int i = 0; i < 255; i++) { + pem_convert_array[i] = -1; + } + for (int i = 0; i < pem_array.length; i++) { + pem_convert_array[pem_array[i]] = (byte) i; + } + } + + byte decode_buffer[] = new byte[4]; + + /** + * Decode one BASE64 atom into 1, 2, or 3 bytes of data. + */ + @SuppressWarnings("fallthrough") + protected void decodeAtom(PushbackInputStream inStream, OutputStream outStream, int rem) + throws java.io.IOException + { + int i; + byte a = -1, b = -1, c = -1, d = -1; + + if (rem < 2) { + throw new CEFormatException("BASE64Decoder: Not enough bytes for an atom."); + } + do { + i = inStream.read(); + if (i == -1) { + throw new CEStreamExhausted(); + } + } while (i == '\n' || i == '\r'); + decode_buffer[0] = (byte) i; + + i = readFully(inStream, decode_buffer, 1, rem-1); + if (i == -1) { + throw new CEStreamExhausted(); + } + + if (rem > 3 && decode_buffer[3] == '=') { + rem = 3; + } + if (rem > 2 && decode_buffer[2] == '=') { + rem = 2; + } + switch (rem) { + case 4: + d = pem_convert_array[decode_buffer[3] & 0xff]; + // NOBREAK + case 3: + c = pem_convert_array[decode_buffer[2] & 0xff]; + // NOBREAK + case 2: + b = pem_convert_array[decode_buffer[1] & 0xff]; + a = pem_convert_array[decode_buffer[0] & 0xff]; + break; + } + + switch (rem) { + case 2: + outStream.write( (byte)(((a << 2) & 0xfc) | ((b >>> 4) & 3)) ); + break; + case 3: + outStream.write( (byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)) ); + outStream.write( (byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)) ); + break; + case 4: + outStream.write( (byte) (((a << 2) & 0xfc) | ((b >>> 4) & 3)) ); + outStream.write( (byte) (((b << 4) & 0xf0) | ((c >>> 2) & 0xf)) ); + outStream.write( (byte) (((c << 6) & 0xc0) | (d & 0x3f)) ); + break; + } + return; + } +} diff --git a/src/sun/misc/BASE64Encoder.java b/src/sun/misc/BASE64Encoder.java new file mode 100644 index 00000000..a090d865 --- /dev/null +++ b/src/sun/misc/BASE64Encoder.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1995, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * This class implements a BASE64 Character encoder as specified in RFC1521. + * This RFC is part of the MIME specification as published by the Internet + * Engineering Task Force (IETF). Unlike some other encoding schemes there + * is nothing in this encoding that indicates + * where a buffer starts or ends. + * + * This means that the encoded text will simply start with the first line + * of encoded text and end with the last line of encoded text. + * + * @author Chuck McManis + * @see CharacterEncoder + * @see BASE64Decoder + */ + +public class BASE64Encoder extends CharacterEncoder { + + /** this class encodes three bytes per atom. */ + protected int bytesPerAtom() { + return (3); + } + + /** + * this class encodes 57 bytes per line. This results in a maximum + * of 57/3 * 4 or 76 characters per output line. Not counting the + * line termination. + */ + protected int bytesPerLine() { + return (57); + } + + /** This array maps the characters to their 6 bit values */ + private final static char pem_array[] = { + // 0 1 2 3 4 5 6 7 + 'A','B','C','D','E','F','G','H', // 0 + 'I','J','K','L','M','N','O','P', // 1 + 'Q','R','S','T','U','V','W','X', // 2 + 'Y','Z','a','b','c','d','e','f', // 3 + 'g','h','i','j','k','l','m','n', // 4 + 'o','p','q','r','s','t','u','v', // 5 + 'w','x','y','z','0','1','2','3', // 6 + '4','5','6','7','8','9','+','/' // 7 + }; + + /** + * encodeAtom - Take three bytes of input and encode it as 4 + * printable characters. Note that if the length in len is less + * than three is encodes either one or two '=' signs to indicate + * padding characters. + */ + protected void encodeAtom(OutputStream outStream, byte data[], int offset, int len) + throws IOException { + byte a, b, c; + + if (len == 1) { + a = data[offset]; + b = 0; + c = 0; + outStream.write(pem_array[(a >>> 2) & 0x3F]); + outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + outStream.write('='); + outStream.write('='); + } else if (len == 2) { + a = data[offset]; + b = data[offset+1]; + c = 0; + outStream.write(pem_array[(a >>> 2) & 0x3F]); + outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + outStream.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); + outStream.write('='); + } else { + a = data[offset]; + b = data[offset+1]; + c = data[offset+2]; + outStream.write(pem_array[(a >>> 2) & 0x3F]); + outStream.write(pem_array[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); + outStream.write(pem_array[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); + outStream.write(pem_array[c & 0x3F]); + } + } +} diff --git a/src/sun/misc/CEFormatException.java b/src/sun/misc/CEFormatException.java new file mode 100644 index 00000000..6d53fa23 --- /dev/null +++ b/src/sun/misc/CEFormatException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.IOException; + +public class CEFormatException extends IOException { + static final long serialVersionUID = -7139121221067081482L; + public CEFormatException(String s) { + super(s); + } +} + diff --git a/src/sun/misc/CEStreamExhausted.java b/src/sun/misc/CEStreamExhausted.java new file mode 100644 index 00000000..fb2054ea --- /dev/null +++ b/src/sun/misc/CEStreamExhausted.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; + +/** This exception is thrown when EOF is reached */ +public class CEStreamExhausted extends IOException { + static final long serialVersionUID = -5889118049525891904L; +} + diff --git a/src/sun/misc/CRC16.java b/src/sun/misc/CRC16.java new file mode 100644 index 00000000..3ec66a0f --- /dev/null +++ b/src/sun/misc/CRC16.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * The CRC-16 class calculates a 16 bit cyclic redundancy check of a set + * of bytes. This error detecting code is used to determine if bit rot + * has occurred in a byte stream. + */ + +public class CRC16 { + + /** value contains the currently computed CRC, set it to 0 initally */ + public int value; + + public CRC16() { + value = 0; + } + + /** update CRC with byte b */ + public void update(byte aByte) { + int a, b; + + a = (int) aByte; + for (int count = 7; count >=0; count--) { + a = a << 1; + b = (a >>> 8) & 1; + if ((value & 0x8000) != 0) { + value = ((value << 1) + b) ^ 0x1021; + } else { + value = (value << 1) + b; + } + } + value = value & 0xffff; + return; + } + + /** reset CRC value to 0 */ + public void reset() { + value = 0; + } +} diff --git a/src/sun/misc/Cache.java b/src/sun/misc/Cache.java new file mode 100644 index 00000000..bf48111f --- /dev/null +++ b/src/sun/misc/Cache.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 1995, 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * Caches the collision list. + */ +class CacheEntry extends Ref { + int hash; + Object key; + CacheEntry next; + public Object reconstitute() { + return null; + } +} + +/** + * The Cache class. Maps keys to values. Any object can be used as + * a key and/or value. This is very similar to the Hashtable + * class, except that after putting an object into the Cache, + * it is not guaranteed that a subsequent get will return it. + * The Cache will automatically remove entries if memory is + * getting tight and if the entry is not referenced from outside + * the Cache.

    + * + * To sucessfully store and retrieve objects from a hash table the + * object used as the key must implement the hashCode() and equals() + * methods.

    + * + * This example creates a Cache of numbers. It uses the names of + * the numbers as keys: + *

    + *      Cache numbers = new Cache();
    + *      numbers.put("one", new Integer(1));
    + *      numbers.put("two", new Integer(1));
    + *      numbers.put("three", new Integer(1));
    + * 
    + * To retrieve a number use: + *
    + *      Integer n = (Integer)numbers.get("two");
    + *      if (n != null) {
    + *          System.out.println("two = " + n);
    + *      }
    + * 
    + * + * @see Object#hashCode + * @see Object#equals + * @see Ref + */ +public +class Cache extends Dictionary { + /** + * The hash table data. + */ + private CacheEntry table[]; + + /** + * The total number of entries in the hash table. + */ + private int count; + + /** + * Rehashes the table when count exceeds this threshold. + */ + private int threshold; + + /** + * The load factor for the hashtable. + */ + private float loadFactor; + + private void init(int initialCapacity, float loadFactor) { + if ((initialCapacity <= 0) || (loadFactor <= 0.0)) { + throw new IllegalArgumentException(); + } + this.loadFactor = loadFactor; + table = new CacheEntry[initialCapacity]; + threshold = (int) (initialCapacity * loadFactor); + } + + /** + * Constructs a new, empty Cache with the specified initial + * capacity and the specified load factor. + * @param initialCapacity the initial number of buckets + * @param loadFactor a number between 0.0 and 1.0, it defines + * the threshold for rehashing the Cache into + * a bigger one. + * @exception IllegalArgumentException If the initial capacity + * is less than or equal to zero. + * @exception IllegalArgumentException If the load factor is + * less than or equal to zero. + */ + public Cache (int initialCapacity, float loadFactor) { + init(initialCapacity, loadFactor); + } + + /** + * Constructs a new, empty Cache with the specified initial + * capacity. + * @param initialCapacity the initial number of buckets + */ + public Cache (int initialCapacity) { + init(initialCapacity, 0.75f); + } + + /** + * Constructs a new, empty Cache. A default capacity and load factor + * is used. Note that the Cache will automatically grow when it gets + * full. + */ + public Cache () { + try { + init(101, 0.75f); + } catch (IllegalArgumentException ex) { + // This should never happen + throw new Error("panic"); + } + } + + /** + * Returns the number of elements contained within the Cache. + */ + public int size() { + return count; + } + + /** + * Returns true if the Cache contains no elements. + */ + public boolean isEmpty() { + return count == 0; + } + + /** + * Returns an enumeration of the Cache's keys. + * @see Cache#elements + * @see Enumeration + */ + public synchronized Enumeration keys() { + return new CacheEnumerator(table, true); + } + + /** + * Returns an enumeration of the elements. Use the Enumeration methods + * on the returned object to fetch the elements sequentially. + * @see Cache#keys + * @see Enumeration + */ + public synchronized Enumeration elements() { + return new CacheEnumerator(table, false); + } + + /** + * Gets the object associated with the specified key in the Cache. + * @param key the key in the hash table + * @returns the element for the key or null if the key + * is not defined in the hash table. + * @see Cache#put + */ + public synchronized Object get(Object key) { + CacheEntry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (CacheEntry e = tab[index]; e != null; e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + return e.check(); + } + } + return null; + } + + /** + * Rehashes the contents of the table into a bigger table. + * This is method is called automatically when the Cache's + * size exceeds the threshold. + */ + protected void rehash() { + int oldCapacity = table.length; + CacheEntry oldTable[] = table; + + int newCapacity = oldCapacity * 2 + 1; + CacheEntry newTable[] = new CacheEntry[newCapacity]; + + threshold = (int) (newCapacity * loadFactor); + table = newTable; + + // System.out.println("rehash old=" + oldCapacity + ", new=" + + // newCapacity + ", thresh=" + threshold + ", count=" + count); + + for (int i = oldCapacity; i-- > 0;) { + for (CacheEntry old = oldTable[i]; old != null;) { + CacheEntry e = old; + old = old.next; + if (e.check() != null) { + int index = (e.hash & 0x7FFFFFFF) % newCapacity; + e.next = newTable[index]; + newTable[index] = e; + } else + count--; /* remove entries that have disappeared */ + } + } + } + + /** + * Puts the specified element into the Cache, using the specified + * key. The element may be retrieved by doing a get() with the same + * key. The key and the element cannot be null. + * @param key the specified hashtable key + * @param value the specified element + * @return the old value of the key, or null if it did not have one. + * @exception NullPointerException If the value of the specified + * element is null. + * @see Cache#get + */ + public synchronized Object put(Object key, Object value) { + // Make sure the value is not null + if (value == null) { + throw new NullPointerException(); + } + // Makes sure the key is not already in the cache. + CacheEntry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + CacheEntry ne = null; + for (CacheEntry e = tab[index]; e != null; e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + Object old = e.check(); + e.setThing(value); + return old; + } else if (e.check() == null) + ne = e; /* reuse old flushed value */ + } + + if (count >= threshold) { + // Rehash the table if the threshold is exceeded + rehash(); + return put(key, value); + } + // Creates the new entry. + if (ne == null) { + ne = new CacheEntry (); + ne.next = tab[index]; + tab[index] = ne; + count++; + } + ne.hash = hash; + ne.key = key; + ne.setThing(value); + return null; + } + + /** + * Removes the element corresponding to the key. Does nothing if the + * key is not present. + * @param key the key that needs to be removed + * @return the value of key, or null if the key was not found. + */ + public synchronized Object remove(Object key) { + CacheEntry tab[] = table; + int hash = key.hashCode(); + int index = (hash & 0x7FFFFFFF) % tab.length; + for (CacheEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) { + if ((e.hash == hash) && e.key.equals(key)) { + if (prev != null) { + prev.next = e.next; + } else { + tab[index] = e.next; + } + count--; + return e.check(); + } + } + return null; + } +} + +/** + * A Cache enumerator class. This class should remain opaque + * to the client. It will use the Enumeration interface. + */ +class CacheEnumerator implements Enumeration { + boolean keys; + int index; + CacheEntry table[]; + CacheEntry entry; + + CacheEnumerator (CacheEntry table[], boolean keys) { + this.table = table; + this.keys = keys; + this.index = table.length; + } + + public boolean hasMoreElements() { + while (index >= 0) { + while (entry != null) + if (entry.check() != null) + return true; + else + entry = entry.next; + while (--index >= 0 && (entry = table[index]) == null) ; + } + return false; + } + + public Object nextElement() { + while (index >= 0) { + if (entry == null) + while (--index >= 0 && (entry = table[index]) == null) ; + if (entry != null) { + CacheEntry e = entry; + entry = e.next; + if (e.check() != null) + return keys ? e.key : e.check(); + } + } + throw new NoSuchElementException("CacheEnumerator"); + } + +} diff --git a/src/sun/misc/CharacterDecoder.java b/src/sun/misc/CharacterDecoder.java new file mode 100644 index 00000000..135dc026 --- /dev/null +++ b/src/sun/misc/CharacterDecoder.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.nio.ByteBuffer; + +/** + * This class defines the decoding half of character encoders. + * A character decoder is an algorithim for transforming 8 bit + * binary data that has been encoded into text by a character + * encoder, back into original binary form. + * + * The character encoders, in general, have been structured + * around a central theme that binary data can be encoded into + * text that has the form: + * + *
    + *      [Buffer Prefix]
    + *      [Line Prefix][encoded data atoms][Line Suffix]
    + *      [Buffer Suffix]
    + * 
    + * + * Of course in the simplest encoding schemes, the buffer has no + * distinct prefix of suffix, however all have some fixed relationship + * between the text in an 'atom' and the binary data itself. + * + * In the CharacterEncoder and CharacterDecoder classes, one complete + * chunk of data is referred to as a buffer. Encoded buffers + * are all text, and decoded buffers (sometimes just referred to as + * buffers) are binary octets. + * + * To create a custom decoder, you must, at a minimum, overide three + * abstract methods in this class. + *
    + *
    bytesPerAtom which tells the decoder how many bytes to + * expect from decodeAtom + *
    decodeAtom which decodes the bytes sent to it as text. + *
    bytesPerLine which tells the encoder the maximum number of + * bytes per line. + *
    + * + * In general, the character decoders return error in the form of a + * CEFormatException. The syntax of the detail string is + *
    + *      DecoderClassName: Error message.
    + * 
    + * + * Several useful decoders have already been written and are + * referenced in the See Also list below. + * + * @author Chuck McManis + * @see CEFormatException + * @see CharacterEncoder + * @see UCDecoder + * @see UUDecoder + * @see BASE64Decoder + */ + +public abstract class CharacterDecoder { + + /** Return the number of bytes per atom of decoding */ + abstract protected int bytesPerAtom(); + + /** Return the maximum number of bytes that can be encoded per line */ + abstract protected int bytesPerLine(); + + /** decode the beginning of the buffer, by default this is a NOP. */ + protected void decodeBufferPrefix(PushbackInputStream aStream, OutputStream bStream) throws IOException { } + + /** decode the buffer suffix, again by default it is a NOP. */ + protected void decodeBufferSuffix(PushbackInputStream aStream, OutputStream bStream) throws IOException { } + + /** + * This method should return, if it knows, the number of bytes + * that will be decoded. Many formats such as uuencoding provide + * this information. By default we return the maximum bytes that + * could have been encoded on the line. + */ + protected int decodeLinePrefix(PushbackInputStream aStream, OutputStream bStream) throws IOException { + return (bytesPerLine()); + } + + /** + * This method post processes the line, if there are error detection + * or correction codes in a line, they are generally processed by + * this method. The simplest version of this method looks for the + * (newline) character. + */ + protected void decodeLineSuffix(PushbackInputStream aStream, OutputStream bStream) throws IOException { } + + /** + * This method does an actual decode. It takes the decoded bytes and + * writes them to the OutputStream. The integer l tells the + * method how many bytes are required. This is always <= bytesPerAtom(). + */ + protected void decodeAtom(PushbackInputStream aStream, OutputStream bStream, int l) throws IOException { + throw new CEStreamExhausted(); + } + + /** + * This method works around the bizarre semantics of BufferedInputStream's + * read method. + */ + protected int readFully(InputStream in, byte buffer[], int offset, int len) + throws IOException { + for (int i = 0; i < len; i++) { + int q = in.read(); + if (q == -1) + return ((i == 0) ? -1 : i); + buffer[i+offset] = (byte)q; + } + return len; + } + + /** + * Decode the text from the InputStream and write the decoded + * octets to the OutputStream. This method runs until the stream + * is exhausted. + * @exception CEFormatException An error has occurred while decoding + * @exception CEStreamExhausted The input stream is unexpectedly out of data + */ + public void decodeBuffer(InputStream aStream, OutputStream bStream) throws IOException { + int i; + int totalBytes = 0; + + PushbackInputStream ps = new PushbackInputStream (aStream); + decodeBufferPrefix(ps, bStream); + while (true) { + int length; + + try { + length = decodeLinePrefix(ps, bStream); + for (i = 0; (i+bytesPerAtom()) < length; i += bytesPerAtom()) { + decodeAtom(ps, bStream, bytesPerAtom()); + totalBytes += bytesPerAtom(); + } + if ((i + bytesPerAtom()) == length) { + decodeAtom(ps, bStream, bytesPerAtom()); + totalBytes += bytesPerAtom(); + } else { + decodeAtom(ps, bStream, length - i); + totalBytes += (length - i); + } + decodeLineSuffix(ps, bStream); + } catch (CEStreamExhausted e) { + break; + } + } + decodeBufferSuffix(ps, bStream); + } + + /** + * Alternate decode interface that takes a String containing the encoded + * buffer and returns a byte array containing the data. + * @exception CEFormatException An error has occurred while decoding + */ + public byte decodeBuffer(String inputString)[] throws IOException { + byte inputBuffer[] = new byte[inputString.length()]; + ByteArrayInputStream inStream; + ByteArrayOutputStream outStream; + + inputString.getBytes(0, inputString.length(), inputBuffer, 0); + inStream = new ByteArrayInputStream(inputBuffer); + outStream = new ByteArrayOutputStream(); + decodeBuffer(inStream, outStream); + return (outStream.toByteArray()); + } + + /** + * Decode the contents of the inputstream into a buffer. + */ + public byte decodeBuffer(InputStream in)[] throws IOException { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + decodeBuffer(in, outStream); + return (outStream.toByteArray()); + } + + /** + * Decode the contents of the String into a ByteBuffer. + */ + public ByteBuffer decodeBufferToByteBuffer(String inputString) + throws IOException { + return ByteBuffer.wrap(decodeBuffer(inputString)); + } + + /** + * Decode the contents of the inputStream into a ByteBuffer. + */ + public ByteBuffer decodeBufferToByteBuffer(InputStream in) + throws IOException { + return ByteBuffer.wrap(decodeBuffer(in)); + } +} diff --git a/src/sun/misc/CharacterEncoder.java b/src/sun/misc/CharacterEncoder.java new file mode 100644 index 00000000..d45e07af --- /dev/null +++ b/src/sun/misc/CharacterEncoder.java @@ -0,0 +1,354 @@ +/* + * Copyright (c) 1995, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.ByteBuffer; + + +/** + * This class defines the encoding half of character encoders. + * A character encoder is an algorithim for transforming 8 bit binary + * data into text (generally 7 bit ASCII or 8 bit ISO-Latin-1 text) + * for transmition over text channels such as e-mail and network news. + * + * The character encoders have been structured around a central theme + * that, in general, the encoded text has the form: + * + *
    + *      [Buffer Prefix]
    + *      [Line Prefix][encoded data atoms][Line Suffix]
    + *      [Buffer Suffix]
    + * 
    + * + * In the CharacterEncoder and CharacterDecoder classes, one complete + * chunk of data is referred to as a buffer. Encoded buffers + * are all text, and decoded buffers (sometimes just referred to as + * buffers) are binary octets. + * + * To create a custom encoder, you must, at a minimum, overide three + * abstract methods in this class. + *
    + *
    bytesPerAtom which tells the encoder how many bytes to + * send to encodeAtom + *
    encodeAtom which encodes the bytes sent to it as text. + *
    bytesPerLine which tells the encoder the maximum number of + * bytes per line. + *
    + * + * Several useful encoders have already been written and are + * referenced in the See Also list below. + * + * @author Chuck McManis + * @see CharacterDecoder; + * @see UCEncoder + * @see UUEncoder + * @see BASE64Encoder + */ +public abstract class CharacterEncoder { + + /** Stream that understands "printing" */ + protected PrintStream pStream; + + /** Return the number of bytes per atom of encoding */ + abstract protected int bytesPerAtom(); + + /** Return the number of bytes that can be encoded per line */ + abstract protected int bytesPerLine(); + + /** + * Encode the prefix for the entire buffer. By default is simply + * opens the PrintStream for use by the other functions. + */ + protected void encodeBufferPrefix(OutputStream aStream) throws IOException { + pStream = new PrintStream(aStream); + } + + /** + * Encode the suffix for the entire buffer. + */ + protected void encodeBufferSuffix(OutputStream aStream) throws IOException { + } + + /** + * Encode the prefix that starts every output line. + */ + protected void encodeLinePrefix(OutputStream aStream, int aLength) + throws IOException { + } + + /** + * Encode the suffix that ends every output line. By default + * this method just prints a into the output stream. + */ + protected void encodeLineSuffix(OutputStream aStream) throws IOException { + pStream.println(); + } + + /** Encode one "atom" of information into characters. */ + abstract protected void encodeAtom(OutputStream aStream, byte someBytes[], + int anOffset, int aLength) throws IOException; + + /** + * This method works around the bizarre semantics of BufferedInputStream's + * read method. + */ + protected int readFully(InputStream in, byte buffer[]) + throws IOException { + for (int i = 0; i < buffer.length; i++) { + int q = in.read(); + if (q == -1) + return i; + buffer[i] = (byte)q; + } + return buffer.length; + } + + /** + * Encode bytes from the input stream, and write them as text characters + * to the output stream. This method will run until it exhausts the + * input stream, but does not print the line suffix for a final + * line that is shorter than bytesPerLine(). + */ + public void encode(InputStream inStream, OutputStream outStream) + throws IOException { + int j; + int numBytes; + byte tmpbuffer[] = new byte[bytesPerLine()]; + + encodeBufferPrefix(outStream); + + while (true) { + numBytes = readFully(inStream, tmpbuffer); + if (numBytes == 0) { + break; + } + encodeLinePrefix(outStream, numBytes); + for (j = 0; j < numBytes; j += bytesPerAtom()) { + + if ((j + bytesPerAtom()) <= numBytes) { + encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); + } else { + encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); + } + } + if (numBytes < bytesPerLine()) { + break; + } else { + encodeLineSuffix(outStream); + } + } + encodeBufferSuffix(outStream); + } + + /** + * Encode the buffer in aBuffer and write the encoded + * result to the OutputStream aStream. + */ + public void encode(byte aBuffer[], OutputStream aStream) + throws IOException { + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + encode(inStream, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a buffer of + * bytes and returns a string containing the encoded buffer. + */ + public String encode(byte aBuffer[]) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + String retVal = null; + try { + encode(inStream, outStream); + // explicit ascii->unicode conversion + retVal = outStream.toString("8859_1"); + } catch (Exception IOException) { + // This should never happen. + throw new Error("CharacterEncoder.encode internal error"); + } + return (retVal); + } + + /** + * Return a byte array from the remaining bytes in this ByteBuffer. + *

    + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + *

    + * To avoid an extra copy, the implementation will attempt to return the + * byte array backing the ByteBuffer. If this is not possible, a + * new byte array will be created. + */ + private byte [] getBytes(ByteBuffer bb) { + /* + * This should never return a BufferOverflowException, as we're + * careful to allocate just the right amount. + */ + byte [] buf = null; + + /* + * If it has a usable backing byte buffer, use it. Use only + * if the array exactly represents the current ByteBuffer. + */ + if (bb.hasArray()) { + byte [] tmp = bb.array(); + if ((tmp.length == bb.capacity()) && + (tmp.length == bb.remaining())) { + buf = tmp; + bb.position(bb.limit()); + } + } + + if (buf == null) { + /* + * This class doesn't have a concept of encode(buf, len, off), + * so if we have a partial buffer, we must reallocate + * space. + */ + buf = new byte[bb.remaining()]; + + /* + * position() automatically updated + */ + bb.get(buf); + } + + return buf; + } + + /** + * Encode the aBuffer ByteBuffer and write the encoded + * result to the OutputStream aStream. + *

    + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public void encode(ByteBuffer aBuffer, OutputStream aStream) + throws IOException { + byte [] buf = getBytes(aBuffer); + encode(buf, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a ByteBuffer + * and returns a string containing the encoded buffer. + *

    + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public String encode(ByteBuffer aBuffer) { + byte [] buf = getBytes(aBuffer); + return encode(buf); + } + + /** + * Encode bytes from the input stream, and write them as text characters + * to the output stream. This method will run until it exhausts the + * input stream. It differs from encode in that it will add the + * line at the end of a final line that is shorter than bytesPerLine(). + */ + public void encodeBuffer(InputStream inStream, OutputStream outStream) + throws IOException { + int j; + int numBytes; + byte tmpbuffer[] = new byte[bytesPerLine()]; + + encodeBufferPrefix(outStream); + + while (true) { + numBytes = readFully(inStream, tmpbuffer); + if (numBytes == 0) { + break; + } + encodeLinePrefix(outStream, numBytes); + for (j = 0; j < numBytes; j += bytesPerAtom()) { + if ((j + bytesPerAtom()) <= numBytes) { + encodeAtom(outStream, tmpbuffer, j, bytesPerAtom()); + } else { + encodeAtom(outStream, tmpbuffer, j, (numBytes)- j); + } + } + encodeLineSuffix(outStream); + if (numBytes < bytesPerLine()) { + break; + } + } + encodeBufferSuffix(outStream); + } + + /** + * Encode the buffer in aBuffer and write the encoded + * result to the OutputStream aStream. + */ + public void encodeBuffer(byte aBuffer[], OutputStream aStream) + throws IOException { + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + encodeBuffer(inStream, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a buffer of + * bytes and returns a string containing the encoded buffer. + */ + public String encodeBuffer(byte aBuffer[]) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + ByteArrayInputStream inStream = new ByteArrayInputStream(aBuffer); + try { + encodeBuffer(inStream, outStream); + } catch (Exception IOException) { + // This should never happen. + throw new Error("CharacterEncoder.encodeBuffer internal error"); + } + return (outStream.toString()); + } + + /** + * Encode the aBuffer ByteBuffer and write the encoded + * result to the OutputStream aStream. + *

    + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public void encodeBuffer(ByteBuffer aBuffer, OutputStream aStream) + throws IOException { + byte [] buf = getBytes(aBuffer); + encodeBuffer(buf, aStream); + } + + /** + * A 'streamless' version of encode that simply takes a ByteBuffer + * and returns a string containing the encoded buffer. + *

    + * The ByteBuffer's position will be advanced to ByteBuffer's limit. + */ + public String encodeBuffer(ByteBuffer aBuffer) { + byte [] buf = getBytes(aBuffer); + return encodeBuffer(buf); + } + +} diff --git a/src/sun/misc/ClassFileTransformer.java b/src/sun/misc/ClassFileTransformer.java new file mode 100644 index 00000000..06a19598 --- /dev/null +++ b/src/sun/misc/ClassFileTransformer.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.util.ArrayList; +import java.util.List; + +/** + * This is an abstract base class originally intended to be called by + * {@code java.lang.ClassLoader} when {@code ClassFormatError} is + * thrown inside {@code defineClass()}. It is no longer hooked into + * {@code ClassLoader} and will be removed in a future release. + * + * @author Stanley Man-Kit Ho + */ + +@Deprecated +public abstract class ClassFileTransformer { + + private static final List transformers + = new ArrayList(); + + /** + * Add the class file transformer object. + * + * @param t Class file transformer instance + */ + public static void add(ClassFileTransformer t) { + synchronized (transformers) { + transformers.add(t); + } + } + + /** + * Get the array of ClassFileTransformer object. + * + * @return ClassFileTransformer object array + */ + public static ClassFileTransformer[] getTransformers() { + synchronized (transformers) { + ClassFileTransformer[] result = new ClassFileTransformer[transformers.size()]; + return transformers.toArray(result); + } + } + + + /** + * Transform a byte array from one to the other. + * + * @param b Byte array + * @param off Offset + * @param len Length of byte array + * @return Transformed byte array + */ + public abstract byte[] transform(byte[] b, int off, int len) + throws ClassFormatError; +} diff --git a/src/sun/misc/ClassLoaderUtil.java b/src/sun/misc/ClassLoaderUtil.java new file mode 100644 index 00000000..10801463 --- /dev/null +++ b/src/sun/misc/ClassLoaderUtil.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * Provides utility functions related to URLClassLoaders or subclasses of it. + * + * W A R N I N G + * + * This class uses undocumented, unpublished, private data structures inside + * java.net.URLClassLoader and sun.misc.URLClassPath. Use with extreme caution. + * + * @author tjquinn + */ + + +import java.io.IOException; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; +import java.util.jar.JarFile; + +public class ClassLoaderUtil { + + /** + * Releases resources held by a URLClassLoader. A new classloader must + * be created before the underlying resources can be accessed again. + * @param classLoader the instance of URLClassLoader (or a subclass) + */ + public static void releaseLoader(URLClassLoader classLoader) { + releaseLoader(classLoader, null); + } + + /** + * Releases resources held by a URLClassLoader. Notably, close the jars + * opened by the loader. Initializes and updates the List of + * jars that have been successfully closed. + *

    + * @param classLoader the instance of URLClassLoader (or a subclass) + * @param jarsClosed a List of Strings that will contain the names of jars + * successfully closed; can be null if the caller does not need the information returned + * @return a List of IOExceptions reporting jars that failed to close; null + * indicates that an error other than an IOException occurred attempting to + * release the loader; empty indicates a successful release; non-empty + * indicates at least one error attempting to close an open jar. + */ + public static List releaseLoader(URLClassLoader classLoader, List jarsClosed) { + + List ioExceptions = new LinkedList(); + + try { + /* Records all IOExceptions thrown while closing jar files. */ + + if (jarsClosed != null) { + jarsClosed.clear(); + } + + URLClassPath ucp = SharedSecrets.getJavaNetAccess() + .getURLClassPath(classLoader); + ArrayList loaders = ucp.loaders; + Stack urls = ucp.urls; + HashMap lmap = ucp.lmap; + + /* + *The urls variable in the URLClassPath object holds URLs that have not yet + *been used to resolve a resource or load a class and, therefore, do + *not yet have a loader associated with them. Clear the stack so any + *future requests that might incorrectly reach the loader cannot be + *resolved and cannot open a jar file after we think we've closed + *them all. + */ + synchronized(urls) { + urls.clear(); + } + + /* + *Also clear the map of URLs to loaders so the class loader cannot use + *previously-opened jar files - they are about to be closed. + */ + synchronized(lmap) { + lmap.clear(); + } + + /* + *The URLClassPath object's path variable records the list of all URLs that are on + *the URLClassPath's class path. Leave that unchanged. This might + *help someone trying to debug why a released class loader is still used. + *Because the stack and lmap are now clear, code that incorrectly uses a + *the released class loader will trigger an exception if the + *class or resource would have been resolved by the class + *loader (and no other) if it had not been released. + * + *The list of URLs might provide some hints to the person as to where + *in the code the class loader was set up, which might in turn suggest + *where in the code the class loader needs to stop being used. + *The URLClassPath does not use the path variable to open new jar + *files - it uses the urls Stack for that - so leaving the path variable + *will not by itself allow the class loader to continue handling requests. + */ + + /* + *For each loader, close the jar file associated with that loader. + * + *The URLClassPath's use of loaders is sync-ed on the entire URLClassPath + *object. + */ + synchronized (ucp) { + for (Object o : loaders) { + if (o != null) { + /* + *If the loader is a JarLoader inner class and its jarFile + *field is non-null then try to close that jar file. Add + *it to the list of closed files if successful. + */ + if (o instanceof URLClassPath.JarLoader) { + URLClassPath.JarLoader jl = (URLClassPath.JarLoader)o; + JarFile jarFile = jl.getJarFile(); + try { + if (jarFile != null) { + jarFile.close(); + if (jarsClosed != null) { + jarsClosed.add(jarFile.getName()); + } + } + } catch (IOException ioe) { + /* + *Wrap the IOException to identify which jar + *could not be closed and add it to the list + *of IOExceptions to be returned to the caller. + */ + String jarFileName = (jarFile == null) ? "filename not available":jarFile.getName(); + String msg = "Error closing JAR file: " + jarFileName; + IOException newIOE = new IOException(msg); + newIOE.initCause(ioe); + ioExceptions.add(newIOE); + } + } + } + } + /* + *Now clear the loaders ArrayList. + */ + loaders.clear(); + } + } catch (Throwable t) { + throw new RuntimeException (t); + } + return ioExceptions; + } +} diff --git a/src/sun/misc/Cleaner.java b/src/sun/misc/Cleaner.java new file mode 100644 index 00000000..3cf89c5e --- /dev/null +++ b/src/sun/misc/Cleaner.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.security.AccessController; +import java.security.PrivilegedAction; + + +/** + * General-purpose phantom-reference-based cleaners. + * + *

    Cleaners are a lightweight and more robust alternative to finalization. + * They are lightweight because they are not created by the VM and thus do not + * require a JNI upcall to be created, and because their cleanup code is + * invoked directly by the reference-handler thread rather than by the + * finalizer thread. They are more robust because they use phantom references, + * the weakest type of reference object, thereby avoiding the nasty ordering + * problems inherent to finalization. + * + *

    A cleaner tracks a referent object and encapsulates a thunk of arbitrary + * cleanup code. Some time after the GC detects that a cleaner's referent has + * become phantom-reachable, the reference-handler thread will run the cleaner. + * Cleaners may also be invoked directly; they are thread safe and ensure that + * they run their thunks at most once. + * + *

    Cleaners are not a replacement for finalization. They should be used + * only when the cleanup code is extremely simple and straightforward. + * Nontrivial cleaners are inadvisable since they risk blocking the + * reference-handler thread and delaying further cleanup and finalization. + * + * + * @author Mark Reinhold + */ + +public class Cleaner + extends PhantomReference +{ + + // Dummy reference queue, needed because the PhantomReference constructor + // insists that we pass a queue. Nothing will ever be placed on this queue + // since the reference handler invokes cleaners explicitly. + // + private static final ReferenceQueue dummyQueue = new ReferenceQueue<>(); + + // Doubly-linked list of live cleaners, which prevents the cleaners + // themselves from being GC'd before their referents + // + static private Cleaner first = null; + + private Cleaner + next = null, + prev = null; + + private static synchronized Cleaner add(Cleaner cl) { + if (first != null) { + cl.next = first; + first.prev = cl; + } + first = cl; + return cl; + } + + private static synchronized boolean remove(Cleaner cl) { + + // If already removed, do nothing + if (cl.next == cl) + return false; + + // Update list + if (first == cl) { + if (cl.next != null) + first = cl.next; + else + first = cl.prev; + } + if (cl.next != null) + cl.next.prev = cl.prev; + if (cl.prev != null) + cl.prev.next = cl.next; + + // Indicate removal by pointing the cleaner to itself + cl.next = cl; + cl.prev = cl; + return true; + + } + + private final Runnable thunk; + + private Cleaner(Object referent, Runnable thunk) { + super(referent, dummyQueue); + this.thunk = thunk; + } + + /** + * Creates a new cleaner. + * + * @param ob the referent object to be cleaned + * @param thunk + * The cleanup code to be run when the cleaner is invoked. The + * cleanup code is run directly from the reference-handler thread, + * so it should be as simple and straightforward as possible. + * + * @return The new cleaner + */ + public static Cleaner create(Object ob, Runnable thunk) { + if (thunk == null) + return null; + return add(new Cleaner(ob, thunk)); + } + + /** + * Runs this cleaner, if it has not been run before. + */ + public void clean() { + if (!remove(this)) + return; + try { + thunk.run(); + } catch (final Throwable x) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + if (System.err != null) + new Error("Cleaner terminated abnormally", x) + .printStackTrace(); + System.exit(1); + return null; + }}); + } + } + +} diff --git a/src/sun/misc/CompoundEnumeration.java b/src/sun/misc/CompoundEnumeration.java new file mode 100644 index 00000000..a89ec5f5 --- /dev/null +++ b/src/sun/misc/CompoundEnumeration.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/* + * A useful utility class that will enumerate over an array of + * enumerations. + */ +public class CompoundEnumeration implements Enumeration { + private Enumeration[] enums; + private int index = 0; + + public CompoundEnumeration(Enumeration[] enums) { + this.enums = enums; + } + + private boolean next() { + while (index < enums.length) { + if (enums[index] != null && enums[index].hasMoreElements()) { + return true; + } + index++; + } + return false; + } + + public boolean hasMoreElements() { + return next(); + } + + public E nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + return enums[index].nextElement(); + } +} diff --git a/src/sun/misc/ConditionLock.java b/src/sun/misc/ConditionLock.java new file mode 100644 index 00000000..6a779883 --- /dev/null +++ b/src/sun/misc/ConditionLock.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * ConditionLock is a Lock with a built in state variable. This class + * provides the ability to wait for the state variable to be set to a + * desired value and then acquire the lock.

    + * + * The lockWhen() and unlockWith() methods can be safely intermixed + * with the lock() and unlock() methods. However if there is a thread + * waiting for the state variable to become a particular value and you + * simply call Unlock(), that thread will not be able to acquire the + * lock until the state variable equals its desired value.

    + * + * @author Peter King + */ +public final +class ConditionLock extends Lock { + private int state = 0; + + /** + * Creates a ConditionLock. + */ + public ConditionLock () { + } + + /** + * Creates a ConditionLock in an initialState. + */ + public ConditionLock (int initialState) { + state = initialState; + } + + /** + * Acquires the lock when the state variable equals the desired state. + * + * @param desiredState the desired state + * @exception InterruptedException if any thread has + * interrupted this thread. + */ + public synchronized void lockWhen(int desiredState) + throws InterruptedException + { + while (state != desiredState) { + wait(); + } + lock(); + } + + /** + * Releases the lock, and sets the state to a new value. + * @param newState the new state + */ + public synchronized void unlockWith(int newState) { + state = newState; + unlock(); + } +} diff --git a/src/sun/misc/Contended.java b/src/sun/misc/Contended.java new file mode 100644 index 00000000..2269687a --- /dev/null +++ b/src/sun/misc/Contended.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + *

    An annotation expressing that objects and/or their fields are + * expected to encounter memory contention, generally in the form of + * "false sharing". This annotation serves as a hint that such objects + * and fields should reside in locations isolated from those of other + * objects or fields. Susceptibility to memory contention is a + * property of the intended usages of objects and fields, not their + * types or qualifiers. The effects of this annotation will nearly + * always add significant space overhead to objects. The use of + * {@code @Contended} is warranted only when the performance impact of + * this time/space tradeoff is intrinsically worthwhile; for example, + * in concurrent contexts in which each instance of the annotated + * class is often accessed by a different thread. + * + *

    A {@code @Contended} field annotation may optionally include a + * contention group tag. A contention group defines a set of one + * or more fields that collectively must be isolated from all other + * contention groups. The fields in the same contention group may not be + * pairwise isolated. With no contention group tag (or with the default + * empty tag: "") each {@code @Contended} field resides in its own + * distinct and anonymous contention group. + * + *

    When the annotation is used at the class level, the effect is + * equivalent to grouping all the declared fields not already having the + * {@code @Contended} annotation into the same anonymous group. + * With the class level annotation, implementations may choose different + * isolation techniques, such as isolating the entire object, rather than + * isolating distinct fields. A contention group tag has no meaning + * in a class level {@code @Contended} annotation, and is ignored. + * + *

    The class level {@code @Contended} annotation is not inherited and has + * no effect on the fields declared in any sub-classes. The effects of all + * {@code @Contended} annotations, however, remain in force for all + * subclass instances, providing isolation of all the defined contention + * groups. Contention group tags are not inherited, and the same tag used + * in a superclass and subclass, represent distinct contention groups. + * + * @since 1.8 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.TYPE}) +public @interface Contended { + + /** + * The (optional) contention group tag. + * This tag is only meaningful for field level annotations. + * + * @return contention group tag. + */ + String value() default ""; +} diff --git a/src/sun/misc/DoubleConsts.java b/src/sun/misc/DoubleConsts.java new file mode 100644 index 00000000..4087446d --- /dev/null +++ b/src/sun/misc/DoubleConsts.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * This class contains additional constants documenting limits of the + * double type. + * + * @author Joseph D. Darcy + */ + +public class DoubleConsts { + /** + * Don't let anyone instantiate this class. + */ + private DoubleConsts() {} + + public static final double POSITIVE_INFINITY = Double.POSITIVE_INFINITY; + public static final double NEGATIVE_INFINITY = Double.NEGATIVE_INFINITY; + public static final double NaN = Double.NaN; + public static final double MAX_VALUE = Double.MAX_VALUE; + public static final double MIN_VALUE = Double.MIN_VALUE; + + /** + * A constant holding the smallest positive normal value of type + * double, 2-1022. It is equal to the + * value returned by + * Double.longBitsToDouble(0x0010000000000000L). + * + * @since 1.5 + */ + public static final double MIN_NORMAL = 2.2250738585072014E-308; + + + /** + * The number of logical bits in the significand of a + * double number, including the implicit bit. + */ + public static final int SIGNIFICAND_WIDTH = 53; + + /** + * Maximum exponent a finite double number may have. + * It is equal to the value returned by + * Math.ilogb(Double.MAX_VALUE). + */ + public static final int MAX_EXPONENT = 1023; + + /** + * Minimum exponent a normalized double number may + * have. It is equal to the value returned by + * Math.ilogb(Double.MIN_NORMAL). + */ + public static final int MIN_EXPONENT = -1022; + + /** + * The exponent the smallest positive double + * subnormal value would have if it could be normalized. It is + * equal to the value returned by + * FpUtils.ilogb(Double.MIN_VALUE). + */ + public static final int MIN_SUB_EXPONENT = MIN_EXPONENT - + (SIGNIFICAND_WIDTH - 1); + + /** + * Bias used in representing a double exponent. + */ + public static final int EXP_BIAS = 1023; + + /** + * Bit mask to isolate the sign bit of a double. + */ + public static final long SIGN_BIT_MASK = 0x8000000000000000L; + + /** + * Bit mask to isolate the exponent field of a + * double. + */ + public static final long EXP_BIT_MASK = 0x7FF0000000000000L; + + /** + * Bit mask to isolate the significand field of a + * double. + */ + public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL; + + static { + // verify bit masks cover all bit positions and that the bit + // masks are non-overlapping + assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) && + (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) && + ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) && + ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L))); + } +} diff --git a/src/sun/misc/ExtensionDependency.java b/src/sun/misc/ExtensionDependency.java new file mode 100644 index 00000000..51caac7f --- /dev/null +++ b/src/sun/misc/ExtensionDependency.java @@ -0,0 +1,570 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import sun.net.www.ParseUtil; + +/** + *

    + * This class checks dependent extensions a particular jar file may have + * declared through its manifest attributes. + *

    + * Jar file declared dependent extensions through the extension-list + * attribute. The extension-list contains a list of keys used to + * fetch the other attributes describing the required extension. + * If key is the extension key declared in the extension-list + * attribute, the following describing attribute can be found in + * the manifest : + * key-Extension-Name: (Specification package name) + * key-Specification-Version: (Specification-Version) + * key-Implementation-Version: (Implementation-Version) + * key-Implementation-Vendor-Id: (Imlementation-Vendor-Id) + * key-Implementation-Version: (Implementation version) + * key-Implementation-URL: (URL to download the requested extension) + *

    + * This class also maintain versioning consistency of installed + * extensions dependencies declared in jar file manifest. + *

    + * @author Jerome Dochez + */ +public class ExtensionDependency { + + /* Callbak interfaces to delegate installation of missing extensions */ + private static Vector providers; + + /** + *

    + * Register an ExtensionInstallationProvider. The provider is responsible + * for handling the installation (upgrade) of any missing extensions. + *

    + * @param eip ExtensionInstallationProvider implementation + */ + public synchronized static void addExtensionInstallationProvider + (ExtensionInstallationProvider eip) + { + if (providers == null) { + providers = new Vector<>(); + } + providers.add(eip); + } + + /** + *

    + * Unregister a previously installed installation provider + *

    + */ + public synchronized static void removeExtensionInstallationProvider + (ExtensionInstallationProvider eip) + { + providers.remove(eip); + } + + /** + *

    + * Checks the dependencies of the jar file on installed extension. + *

    + * @param jarFile containing the attriutes declaring the dependencies + */ + public static boolean checkExtensionsDependencies(JarFile jar) + { + if (providers == null) { + // no need to bother, nobody is registered to install missing + // extensions + return true; + } + + try { + ExtensionDependency extDep = new ExtensionDependency(); + return extDep.checkExtensions(jar); + } catch (ExtensionInstallationException e) { + debug(e.getMessage()); + } + return false; + } + + /* + * Check for all declared required extensions in the jar file + * manifest. + */ + protected boolean checkExtensions(JarFile jar) + throws ExtensionInstallationException + { + Manifest man; + try { + man = jar.getManifest(); + } catch (IOException e) { + return false; + } + + if (man == null) { + // The applet does not define a manifest file, so + // we just assume all dependencies are satisfied. + return true; + } + + boolean result = true; + Attributes attr = man.getMainAttributes(); + if (attr != null) { + // Let's get the list of declared dependencies + String value = attr.getValue(Name.EXTENSION_LIST); + if (value != null) { + StringTokenizer st = new StringTokenizer(value); + // Iterate over all declared dependencies + while (st.hasMoreTokens()) { + String extensionName = st.nextToken(); + debug("The file " + jar.getName() + + " appears to depend on " + extensionName); + // Sanity Check + String extName = extensionName + "-" + + Name.EXTENSION_NAME.toString(); + if (attr.getValue(extName) == null) { + debug("The jar file " + jar.getName() + + " appers to depend on " + + extensionName + " but does not define the " + + extName + " attribute in its manifest "); + + } else { + if (!checkExtension(extensionName, attr)) { + debug("Failed installing " + extensionName); + result = false; + } + } + } + } else { + debug("No dependencies for " + jar.getName()); + } + } + return result; + } + + + /* + *

    + * Check that a particular dependency on an extension is satisfied. + *

    + * @param extensionName is the key used for the attributes in the manifest + * @param attr is the attributes of the manifest file + * + * @return true if the dependency is satisfied by the installed extensions + */ + protected synchronized boolean checkExtension(final String extensionName, + final Attributes attr) + throws ExtensionInstallationException + { + debug("Checking extension " + extensionName); + if (checkExtensionAgainstInstalled(extensionName, attr)) + return true; + + debug("Extension not currently installed "); + ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); + return installExtension(reqInfo, null); + } + + /* + *

    + * Check if a particular extension is part of the currently installed + * extensions. + *

    + * @param extensionName is the key for the attributes in the manifest + * @param attr is the attributes of the manifest + * + * @return true if the requested extension is already installed + */ + boolean checkExtensionAgainstInstalled(String extensionName, + Attributes attr) + throws ExtensionInstallationException + { + File fExtension = checkExtensionExists(extensionName); + + if (fExtension != null) { + // Extension already installed, just check against this one + try { + if (checkExtensionAgainst(extensionName, attr, fExtension)) + return true; + } catch (FileNotFoundException e) { + debugException(e); + } catch (IOException e) { + debugException(e); + } + return false; + + } else { + // Not sure if extension is already installed, so check all the + // installed extension jar files to see if we get a match + + File[] installedExts; + + try { + // Get the list of installed extension jar files so we can + // compare the installed versus the requested extension + installedExts = getInstalledExtensions(); + } catch(IOException e) { + debugException(e); + return false; + } + + for (int i=0;i + * Check if the requested extension described by the attributes + * in the manifest under the key extensionName is compatible with + * the jar file. + *

    + * + * @param extensionName key in the attribute list + * @param attr manifest file attributes + * @param file installed extension jar file to compare the requested + * extension against. + */ + protected boolean checkExtensionAgainst(String extensionName, + Attributes attr, + final File file) + throws IOException, + FileNotFoundException, + ExtensionInstallationException + { + + debug("Checking extension " + extensionName + + " against " + file.getName()); + + // Load the jar file ... + Manifest man; + try { + man = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Manifest run() + throws IOException, FileNotFoundException { + if (!file.exists()) + throw new FileNotFoundException(file.getName()); + JarFile jarFile = new JarFile(file); + return jarFile.getManifest(); + } + }); + } catch(PrivilegedActionException e) { + if (e.getException() instanceof FileNotFoundException) + throw (FileNotFoundException) e.getException(); + throw (IOException) e.getException(); + } + + // Construct the extension information object + ExtensionInfo reqInfo = new ExtensionInfo(extensionName, attr); + debug("Requested Extension : " + reqInfo); + + int isCompatible = ExtensionInfo.INCOMPATIBLE; + ExtensionInfo instInfo = null; + + if (man != null) { + Attributes instAttr = man.getMainAttributes(); + if (instAttr != null) { + instInfo = new ExtensionInfo(null, instAttr); + debug("Extension Installed " + instInfo); + isCompatible = instInfo.isCompatibleWith(reqInfo); + switch(isCompatible) { + case ExtensionInfo.COMPATIBLE: + debug("Extensions are compatible"); + return true; + + case ExtensionInfo.INCOMPATIBLE: + debug("Extensions are incompatible"); + return false; + + default: + // everything else + debug("Extensions require an upgrade or vendor switch"); + return installExtension(reqInfo, instInfo); + + } + } + } + return false; + } + + /* + *

    + * An required extension is missing, if an ExtensionInstallationProvider is + * registered, delegate the installation of that particular extension to it. + *

    + * + * @param reqInfo Missing extension information + * @param instInfo Older installed version information + * + * @return true if the installation is successful + */ + protected boolean installExtension(ExtensionInfo reqInfo, + ExtensionInfo instInfo) + throws ExtensionInstallationException + { + Vector currentProviders; + synchronized(providers) { + @SuppressWarnings("unchecked") + Vector tmp = + (Vector) providers.clone(); + currentProviders = tmp; + } + for (Enumeration e = currentProviders.elements(); + e.hasMoreElements();) { + ExtensionInstallationProvider eip = e.nextElement(); + + if (eip!=null) { + // delegate the installation to the provider + if (eip.installExtension(reqInfo, instInfo)) { + debug(reqInfo.name + " installation successful"); + Launcher.ExtClassLoader cl = (Launcher.ExtClassLoader) + Launcher.getLauncher().getClassLoader().getParent(); + addNewExtensionsToClassLoader(cl); + return true; + } + } + } + // We have tried all of our providers, noone could install this + // extension, we just return failure at this point + debug(reqInfo.name + " installation failed"); + return false; + } + + /** + *

    + * Checks if the extension, that is specified in the extension-list in + * the applet jar manifest, is already installed (i.e. exists in the + * extension directory). + *

    + * + * @param extensionName extension name in the extension-list + * + * @return the extension if it exists in the extension directory + */ + private File checkExtensionExists(String extensionName) { + // Function added to fix bug 4504166 + final String extName = extensionName; + final String[] fileExt = {".jar", ".zip"}; + + return AccessController.doPrivileged( + new PrivilegedAction() { + public File run() { + try { + File fExtension; + File[] dirs = getExtDirs(); + + // Search the extension directories for the extension that is specified + // in the attribute extension-list in the applet jar manifest + for (int i=0;i + * @return the java.ext.dirs property as a list of directory + *

    + */ + private static File[] getExtDirs() { + String s = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("java.ext.dirs")); + + File[] dirs; + if (s != null) { + StringTokenizer st = + new StringTokenizer(s, File.pathSeparator); + int count = st.countTokens(); + debug("getExtDirs count " + count); + dirs = new File[count]; + for (int i = 0; i < count; i++) { + dirs[i] = new File(st.nextToken()); + debug("getExtDirs dirs["+i+"] "+ dirs[i]); + } + } else { + dirs = new File[0]; + debug("getExtDirs dirs " + dirs); + } + debug("getExtDirs dirs.length " + dirs.length); + return dirs; + } + + /* + *

    + * Scan the directories and return all files installed in those + *

    + * @param dirs list of directories to scan + * + * @return the list of files installed in all the directories + */ + private static File[] getExtFiles(File[] dirs) throws IOException { + Vector urls = new Vector(); + for (int i = 0; i < dirs.length; i++) { + String[] files = dirs[i].list(new JarFilter()); + if (files != null) { + debug("getExtFiles files.length " + files.length); + for (int j = 0; j < files.length; j++) { + File f = new File(dirs[i], files[j]); + urls.add(f); + debug("getExtFiles f["+j+"] "+ f); + } + } + } + File[] ua = new File[urls.size()]; + urls.copyInto(ua); + debug("getExtFiles ua.length " + ua.length); + return ua; + } + + /* + *

    + * @return the list of installed extensions jar files + *

    + */ + private File[] getInstalledExtensions() throws IOException { + return AccessController.doPrivileged( + new PrivilegedAction() { + public File[] run() { + try { + return getExtFiles(getExtDirs()); + } catch(IOException e) { + debug("Cannot get list of installed extensions"); + debugException(e); + return new File[0]; + } + } + }); + } + + /* + *

    + * Add the newly installed jar file to the extension class loader. + *

    + * + * @param cl the current installed extension class loader + * + * @return true if successful + */ + private Boolean addNewExtensionsToClassLoader(Launcher.ExtClassLoader cl) { + try { + File[] installedExts = getInstalledExtensions(); + for (int i=0;i() { + public URL run() { + try { + return ParseUtil.fileToEncodedURL(instFile); + } catch (MalformedURLException e) { + debugException(e); + return null; + } + } + }); + if (instURL != null) { + URL[] urls = cl.getURLs(); + boolean found=false; + for (int j = 0; j + * public static values returned by the isCompatible method + *

    + */ + public static final int COMPATIBLE = 0; + public static final int REQUIRE_SPECIFICATION_UPGRADE = 1; + public static final int REQUIRE_IMPLEMENTATION_UPGRADE = 2; + public static final int REQUIRE_VENDOR_SWITCH = 3; + public static final int INCOMPATIBLE = 4; + + /** + *

    + * attributes fully describer an extension. The underlying described + * extension may be installed and requested. + *

    + */ + public String title; + public String name; + public String specVersion; + public String specVendor; + public String implementationVersion; + public String vendor; + public String vendorId; + public String url; + + // For I18N support + private static final ResourceBundle rb = + ResourceBundle.getBundle("sun.misc.resources.Messages"); + + + /** + *

    + * Create a new uninitialized extension information object + *

    + */ + public ExtensionInfo() { + } + + /** + *

    + * Create and initialize an extension information object. + * The initialization uses the attributes passed as being + * the content of a manifest file to load the extension + * information from. + * Since manifest file may contain information on several + * extension they may depend on, the extension key parameter + * is prepanded to the attribute name to make the key used + * to retrieve the attribute from the manifest file + *

    + * @param extensionKey unique extension key in the manifest + * @param attr Attributes of a manifest file + */ + public ExtensionInfo(String extensionKey, Attributes attr) + throws NullPointerException + { + String s; + if (extensionKey!=null) { + s = extensionKey + "-"; + } else { + s =""; + } + + String attrKey = s + Name.EXTENSION_NAME.toString(); + name = attr.getValue(attrKey); + if (name != null) + name = name.trim(); + + attrKey = s + Name.SPECIFICATION_TITLE.toString(); + title = attr.getValue(attrKey); + if (title != null) + title = title.trim(); + + attrKey = s + Name.SPECIFICATION_VERSION.toString(); + specVersion = attr.getValue(attrKey); + if (specVersion != null) + specVersion = specVersion.trim(); + + attrKey = s + Name.SPECIFICATION_VENDOR.toString(); + specVendor = attr.getValue(attrKey); + if (specVendor != null) + specVendor = specVendor.trim(); + + attrKey = s + Name.IMPLEMENTATION_VERSION.toString(); + implementationVersion = attr.getValue(attrKey); + if (implementationVersion != null) + implementationVersion = implementationVersion.trim(); + + attrKey = s + Name.IMPLEMENTATION_VENDOR.toString(); + vendor = attr.getValue(attrKey); + if (vendor != null) + vendor = vendor.trim(); + + attrKey = s + Name.IMPLEMENTATION_VENDOR_ID.toString(); + vendorId = attr.getValue(attrKey); + if (vendorId != null) + vendorId = vendorId.trim(); + + attrKey =s + Name.IMPLEMENTATION_URL.toString(); + url = attr.getValue(attrKey); + if (url != null) + url = url.trim(); + } + + /** + *

    + * @return true if the extension described by this extension information + * is compatible with the extension described by the extension + * information passed as a parameter + *

    + * + * @param the requested extension information to compare to + */ + public int isCompatibleWith(ExtensionInfo ei) { + + if (name == null || ei.name == null) + return INCOMPATIBLE; + if (name.compareTo(ei.name)==0) { + // is this true, if not spec version is specified, we consider + // the value as being "any". + if (specVersion == null || ei.specVersion == null) + return COMPATIBLE; + + int version = compareExtensionVersion(specVersion, ei.specVersion); + if (version<0) { + // this extension specification is "older" + if (vendorId != null && ei.vendorId !=null) { + if (vendorId.compareTo(ei.vendorId)!=0) { + return REQUIRE_VENDOR_SWITCH; + } + } + return REQUIRE_SPECIFICATION_UPGRADE; + } else { + // the extension spec is compatible, let's look at the + // implementation attributes + if (vendorId != null && ei.vendorId != null) { + // They care who provides the extension + if (vendorId.compareTo(ei.vendorId)!=0) { + // They want to use another vendor implementation + return REQUIRE_VENDOR_SWITCH; + } else { + // Vendor matches, let's see the implementation version + if (implementationVersion != null && ei.implementationVersion != null) { + // they care about the implementation version + version = compareExtensionVersion(implementationVersion, ei.implementationVersion); + if (version<0) { + // This extension is an older implementation + return REQUIRE_IMPLEMENTATION_UPGRADE; + } + } + } + } + // All othe cases, we consider the extensions to be compatible + return COMPATIBLE; + } + } + return INCOMPATIBLE; + } + + /** + *

    + * helper method to print sensible information on the undelying described + * extension + *

    + */ + public String toString() { + return "Extension : title(" + title + "), name(" + name + "), spec vendor(" + + specVendor + "), spec version(" + specVersion + "), impl vendor(" + + vendor + "), impl vendor id(" + vendorId + "), impl version(" + + implementationVersion + "), impl url(" + url + ")"; + } + + /* + *

    + * helper method to compare two versions. + * version are in the x.y.z.t pattern. + *

    + * @param source version to compare to + * @param target version used to compare against + * @return < 0 if source < version + * > 0 if source > version + * = 0 if source = version + */ + private int compareExtensionVersion(String source, String target) + throws NumberFormatException + { + source = source.toLowerCase(); + target = target.toLowerCase(); + + return strictCompareExtensionVersion(source, target); + } + + + /* + *

    + * helper method to compare two versions. + * version are in the x.y.z.t pattern. + *

    + * @param source version to compare to + * @param target version used to compare against + * @return < 0 if source < version + * > 0 if source > version + * = 0 if source = version + */ + private int strictCompareExtensionVersion(String source, String target) + throws NumberFormatException + { + if (source.equals(target)) + return 0; + + StringTokenizer stk = new StringTokenizer(source, ".,"); + StringTokenizer ttk = new StringTokenizer(target, ".,"); + + // Compare number + int n = 0, m = 0, result = 0; + + // Convert token into meaning number for comparision + if (stk.hasMoreTokens()) + n = convertToken(stk.nextToken().toString()); + + // Convert token into meaning number for comparision + if (ttk.hasMoreTokens()) + m = convertToken(ttk.nextToken().toString()); + + if (n > m) + return 1; + else if (m > n) + return -1; + else + { + // Look for index of "." in the string + int sIdx = source.indexOf("."); + int tIdx = target.indexOf("."); + + if (sIdx == -1) + sIdx = source.length() - 1; + + if (tIdx == -1) + tIdx = target.length() - 1; + + return strictCompareExtensionVersion(source.substring(sIdx + 1), + target.substring(tIdx + 1)); + } + } + + private int convertToken(String token) + { + if (token == null || token.equals("")) + return 0; + + int charValue = 0; + int charVersion = 0; + int patchVersion = 0; + int strLength = token.length(); + int endIndex = strLength; + char lastChar; + + Object[] args = {name}; + MessageFormat mf = new MessageFormat(rb.getString("optpkg.versionerror")); + String versionError = mf.format(args); + + // Look for "-" for pre-release + int prIndex = token.indexOf("-"); + + // Look for "_" for patch release + int patchIndex = token.indexOf("_"); + + if (prIndex == -1 && patchIndex == -1) + { + // This is a FCS release + try { + return Integer.parseInt(token) * 100; + } catch (NumberFormatException e) { + System.out.println(versionError); + return 0; + } + } + else if (patchIndex != -1) + { + // This is a patch (update) release + int prversion; + try { + // Obtain the version + prversion = Integer.parseInt(token.substring(0, patchIndex)); + + // Check to see if the patch version is in the n.n.n_nnl format (special release) + lastChar = token.charAt(strLength-1); + if (Character.isLetter(lastChar)) { + // letters a-z have values from 10-35 + charValue = Character.getNumericValue(lastChar); + endIndex = strLength-1; + + // Obtain the patch version id + patchVersion = Integer.parseInt(token.substring(patchIndex+1, endIndex)); + + if (charValue >= Character.getNumericValue('a') && charValue <= Character.getNumericValue('z')) { + // This is a special release + charVersion = (patchVersion * 100) + charValue; + } else { + // character is not a a-z letter, ignore + charVersion = 0; + System.out.println(versionError); + } + } else { + // This is a regular update release. Obtain the patch version id + patchVersion = Integer.parseInt(token.substring(patchIndex+1, endIndex)); + } + } catch (NumberFormatException e) { + System.out.println(versionError); + return 0; + } + return prversion * 100 + (patchVersion + charVersion); + } + else + { + //This is a milestone release, either a early access, alpha, beta, or RC + + // Obtain the version + int mrversion; + try { + mrversion = Integer.parseInt(token.substring(0, prIndex)); + } catch (NumberFormatException e) { + System.out.println(versionError); + return 0; + } + + // Obtain the patch version string, including the milestone + version + String prString = token.substring(prIndex + 1); + + // Milestone version + String msVersion = ""; + int delta = 0; + + if (prString.indexOf("ea") != -1) + { + msVersion = prString.substring(2); + delta = 50; + } + else if (prString.indexOf("alpha") != -1) + { + msVersion = prString.substring(5); + delta = 40; + } + else if (prString.indexOf("beta") != -1) + { + msVersion = prString.substring(4); + delta = 30; + } + else if (prString.indexOf("rc") != -1) + { + msVersion = prString.substring(2); + delta = 20; + } + + if (msVersion == null || msVersion.equals("")) + { + // No version after the milestone, assume 0 + return mrversion * 100 - delta ; + } + else + { + // Convert the milestone version + try { + return mrversion * 100 - delta + Integer.parseInt(msVersion); + } catch (NumberFormatException e) { + System.out.println(versionError); + return 0; + } + } + } + } +} diff --git a/src/sun/misc/ExtensionInstallationException.java b/src/sun/misc/ExtensionInstallationException.java new file mode 100644 index 00000000..1bf6ff97 --- /dev/null +++ b/src/sun/misc/ExtensionInstallationException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/* + * Exception when installation of an extension has failed for + * any reason + * + * @author Jerome Dochez + */ + +public class ExtensionInstallationException extends Exception { + + static final long serialVersionUID = 3139688306909345924L; + + /* + *

    + * Construct a new exception with an exception reason + *

    + */ + public ExtensionInstallationException(String s) { + super(s); + } +} diff --git a/src/sun/misc/ExtensionInstallationProvider.java b/src/sun/misc/ExtensionInstallationProvider.java new file mode 100644 index 00000000..0b24a135 --- /dev/null +++ b/src/sun/misc/ExtensionInstallationProvider.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * This interface defines the contract a extension installation capable + * provided to the extension installation dependency mechanism to + * install new extensions on the user's disk + * + * @author Jerome Dochez + */ +public interface ExtensionInstallationProvider { + + /* + *

    + * Request the installation of an extension in the extension directory + *

    + * + * @param requestExtInfo information on the extension that need to be + * installed + * @param installedExtInfo information on the current compatible installed + * extension. Can be null if no current installation has been found. + * @return true if the installation is successful, false if the + * installation could not be attempted. + * @exception ExtensionInstallationException if an installation was + * attempted but did not succeed. + */ + boolean installExtension(ExtensionInfo requestExtInfo, + ExtensionInfo installedExtInfo) + throws ExtensionInstallationException; +} diff --git a/src/sun/misc/FDBigInteger.java b/src/sun/misc/FDBigInteger.java new file mode 100644 index 00000000..77d6fbc0 --- /dev/null +++ b/src/sun/misc/FDBigInteger.java @@ -0,0 +1,1508 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.math.BigInteger; +import java.util.Arrays; +//@ model import org.jmlspecs.models.JMLMath; + +/** + * A simple big integer package specifically for floating point base conversion. + */ +public /*@ spec_bigint_math @*/ class FDBigInteger { + + // + // This class contains many comments that start with "/*@" mark. + // They are behavourial specification in + // the Java Modelling Language (JML): + // http://www.eecs.ucf.edu/~leavens/JML//index.shtml + // + + /*@ + @ public pure model static \bigint UNSIGNED(int v) { + @ return v >= 0 ? v : v + (((\bigint)1) << 32); + @ } + @ + @ public pure model static \bigint UNSIGNED(long v) { + @ return v >= 0 ? v : v + (((\bigint)1) << 64); + @ } + @ + @ public pure model static \bigint AP(int[] data, int len) { + @ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32)); + @ } + @ + @ public pure model static \bigint pow52(int p5, int p2) { + @ ghost \bigint v = 1; + @ for (int i = 0; i < p5; i++) v *= 5; + @ return v << p2; + @ } + @ + @ public pure model static \bigint pow10(int p10) { + @ return pow52(p10, p10); + @ } + @*/ + + static final int[] SMALL_5_POW = { + 1, + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + + static final long[] LONG_5_POW = { + 1L, + 5L, + 5L * 5, + 5L * 5 * 5, + 5L * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + }; + + // Maximum size of cache of powers of 5 as FDBigIntegers. + private static final int MAX_FIVE_POW = 340; + + // Cache of big powers of 5 as FDBigIntegers. + private static final FDBigInteger POW_5_CACHE[]; + + // Initialize FDBigInteger cache of powers of 5. + static { + POW_5_CACHE = new FDBigInteger[MAX_FIVE_POW]; + int i = 0; + while (i < SMALL_5_POW.length) { + FDBigInteger pow5 = new FDBigInteger(new int[]{SMALL_5_POW[i]}, 0); + pow5.makeImmutable(); + POW_5_CACHE[i] = pow5; + i++; + } + FDBigInteger prev = POW_5_CACHE[i - 1]; + while (i < MAX_FIVE_POW) { + POW_5_CACHE[i] = prev = prev.mult(5); + prev.makeImmutable(); + i++; + } + } + + // Zero as an FDBigInteger. + public static final FDBigInteger ZERO = new FDBigInteger(new int[0], 0); + + // Ensure ZERO is immutable. + static { + ZERO.makeImmutable(); + } + + // Constant for casting an int to a long via bitwise AND. + private final static long LONG_MASK = 0xffffffffL; + + //@ spec_public non_null; + private int data[]; // value: data[0] is least significant + //@ spec_public; + private int offset; // number of least significant zero padding ints + //@ spec_public; + private int nWords; // data[nWords-1]!=0, all values above are zero + // if nWords==0 -> this FDBigInteger is zero + //@ spec_public; + private boolean isImmutable = false; + + /*@ + @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0; + @ public invariant nWords == 0 ==> offset == 0; + @ public invariant nWords > 0 ==> data[nWords - 1] != 0; + @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0); + @ public pure model \bigint value() { + @ return AP(data, nWords) << (offset*32); + @ } + @*/ + + /** + * Constructs an FDBigInteger from data and padding. The + * data parameter has the least significant int at + * the zeroth index. The offset parameter gives the number of + * zero ints to be inferred below the least significant element + * of data. + * + * @param data An array containing all non-zero ints of the value. + * @param offset An offset indicating the number of zero ints to pad + * below the least significant element of data. + */ + /*@ + @ requires data != null && offset >= 0; + @ ensures this.value() == \old(AP(data, data.length) << (offset*32)); + @ ensures this.data == \old(data); + @*/ + private FDBigInteger(int[] data, int offset) { + this.data = data; + this.offset = offset; + this.nWords = data.length; + trimLeadingZeros(); + } + + /** + * Constructs an FDBigInteger from a starting value and some + * decimal digits. + * + * @param lValue The starting value. + * @param digits The decimal digits. + * @param kDigits The initial index into digits. + * @param nDigits The final index into digits. + */ + /*@ + @ requires digits != null; + @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length; + @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9'); + @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1))); + @*/ + public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) { + int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed. + data = new int[n]; // allocate enough space + data[0] = (int) lValue; // starting value + data[1] = (int) (lValue >>> 32); + offset = 0; + nWords = 2; + int i = kDigits; + int limit = nDigits - 5; // slurp digits 5 at a time. + int v; + while (i < limit) { + int ilim = i + 5; + v = (int) digits[i++] - (int) '0'; + while (i < ilim) { + v = 10 * v + (int) digits[i++] - (int) '0'; + } + multAddMe(100000, v); // ... where 100000 is 10^5. + } + int factor = 1; + v = 0; + while (i < nDigits) { + v = 10 * v + (int) digits[i++] - (int) '0'; + factor *= 10; + } + if (factor != 1) { + multAddMe(factor, v); + } + trimLeadingZeros(); + } + + /** + * Returns an FDBigInteger with the numerical value + * 5p5 * 2p2. + * + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return 5p5 * 2p2 + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == \old(pow52(p5, p2)); + @*/ + public static FDBigInteger valueOfPow52(int p5, int p2) { + if (p5 != 0) { + if (p2 == 0) { + return big5pow(p5); + } else if (p5 < SMALL_5_POW.length) { + int pow5 = SMALL_5_POW[p5]; + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + if (bitcount == 0) { + return new FDBigInteger(new int[]{pow5}, wordcount); + } else { + return new FDBigInteger(new int[]{ + pow5 << bitcount, + pow5 >>> (32 - bitcount) + }, wordcount); + } + } else { + return big5pow(p5).leftShift(p2); + } + } else { + return valueOfPow2(p2); + } + } + + /** + * Returns an FDBigInteger with the numerical value + * value * 5p5 * 2p2. + * + * @param value The constant factor. + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return value * 5p5 * 2p2 + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2)); + @*/ + public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) { + assert p5 >= 0 : p5; + assert p2 >= 0 : p2; + int v0 = (int) value; + int v1 = (int) (value >>> 32); + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + if (p5 != 0) { + if (p5 < SMALL_5_POW.length) { + long pow5 = SMALL_5_POW[p5] & LONG_MASK; + long carry = (v0 & LONG_MASK) * pow5; + v0 = (int) carry; + carry >>>= 32; + carry = (v1 & LONG_MASK) * pow5 + carry; + v1 = (int) carry; + int v2 = (int) (carry >>> 32); + if (bitcount == 0) { + return new FDBigInteger(new int[]{v0, v1, v2}, wordcount); + } else { + return new FDBigInteger(new int[]{ + v0 << bitcount, + (v1 << bitcount) | (v0 >>> (32 - bitcount)), + (v2 << bitcount) | (v1 >>> (32 - bitcount)), + v2 >>> (32 - bitcount) + }, wordcount); + } + } else { + FDBigInteger pow5 = big5pow(p5); + int[] r; + if (v1 == 0) { + r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)]; + mult(pow5.data, pow5.nWords, v0, r); + } else { + r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)]; + mult(pow5.data, pow5.nWords, v0, v1, r); + } + return (new FDBigInteger(r, pow5.offset)).leftShift(p2); + } + } else if (p2 != 0) { + if (bitcount == 0) { + return new FDBigInteger(new int[]{v0, v1}, wordcount); + } else { + return new FDBigInteger(new int[]{ + v0 << bitcount, + (v1 << bitcount) | (v0 >>> (32 - bitcount)), + v1 >>> (32 - bitcount) + }, wordcount); + } + } + return new FDBigInteger(new int[]{v0, v1}, 0); + } + + /** + * Returns an FDBigInteger with the numerical value + * 2p2. + * + * @param p2 The exponent of 2. + * @return 2p2 + */ + /*@ + @ requires p2 >= 0; + @ assignable \nothing; + @ ensures \result.value() == pow52(0, p2); + @*/ + private static FDBigInteger valueOfPow2(int p2) { + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + return new FDBigInteger(new int[]{1 << bitcount}, wordcount); + } + + /** + * Removes all leading zeros from this FDBigInteger adjusting + * the offset and number of non-zero leading words accordingly. + */ + /*@ + @ requires data != null; + @ requires 0 <= nWords && nWords <= data.length && offset >= 0; + @ requires nWords == 0 ==> offset == 0; + @ ensures nWords == 0 ==> offset == 0; + @ ensures nWords > 0 ==> data[nWords - 1] != 0; + @*/ + private /*@ helper @*/ void trimLeadingZeros() { + int i = nWords; + if (i > 0 && (data[--i] == 0)) { + //for (; i > 0 && data[i - 1] == 0; i--) ; + while(i > 0 && data[i - 1] == 0) { + i--; + } + this.nWords = i; + if (i == 0) { // all words are zero + this.offset = 0; + } + } + } + + /** + * Retrieves the normalization bias of the FDBigIntger. The + * normalization bias is a left shift such that after it the highest word + * of the value will have the 4 highest bits equal to zero: + * (highestWord & 0xf0000000) == 0, but the next bit should be 1 + * (highestWord & 0x08000000) != 0. + * + * @return The normalization bias. + */ + /*@ + @ requires this.value() > 0; + @*/ + public /*@ pure @*/ int getNormalizationBias() { + if (nWords == 0) { + throw new IllegalArgumentException("Zero value cannot be normalized"); + } + int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]); + return (zeros < 4) ? 28 + zeros : zeros - 4; + } + + // TODO: Why is anticount param needed if it is always 32 - bitcount? + /** + * Left shifts the contents of one int array into another. + * + * @param src The source array. + * @param idx The initial index of the source array. + * @param result The destination array. + * @param bitcount The left shift. + * @param anticount The left anti-shift, e.g., 32-bitcount. + * @param prev The prior source value. + */ + /*@ + @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount; + @ requires src.length >= idx && result.length > idx; + @ assignable result[*]; + @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount); + @*/ + private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){ + for (; idx > 0; idx--) { + int v = (prev << bitcount); + prev = src[idx - 1]; + v |= (prev >>> anticount); + result[idx] = v; + } + int v = prev << bitcount; + result[0] = v; + } + + /** + * Shifts this FDBigInteger to the left. The shift is performed + * in-place unless the FDBigInteger is immutable in which case + * a new instance of FDBigInteger is returned. + * + * @param shift The number of bits to shift left. + * @return The shifted FDBigInteger. + */ + /*@ + @ requires this.value() == 0 || shift == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && shift > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() << shift); + @ + @ also + @ + @ requires this.value() > 0 && shift > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() << shift); + @*/ + public FDBigInteger leftShift(int shift) { + if (shift == 0 || nWords == 0) { + return this; + } + int wordcount = shift >> 5; + int bitcount = shift & 0x1f; + if (this.isImmutable) { + if (bitcount == 0) { + return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount); + } else { + int anticount = 32 - bitcount; + int idx = nWords - 1; + int prev = data[idx]; + int hi = prev >>> anticount; + int[] result; + if (hi != 0) { + result = new int[nWords + 1]; + result[nWords] = hi; + } else { + result = new int[nWords]; + } + leftShift(data,idx,result,bitcount,anticount,prev); + return new FDBigInteger(result, offset + wordcount); + } + } else { + if (bitcount != 0) { + int anticount = 32 - bitcount; + if ((data[0] << bitcount) == 0) { + int idx = 0; + int prev = data[idx]; + for (; idx < nWords - 1; idx++) { + int v = (prev >>> anticount); + prev = data[idx + 1]; + v |= (prev << bitcount); + data[idx] = v; + } + int v = prev >>> anticount; + data[idx] = v; + if(v==0) { + nWords--; + } + offset++; + } else { + int idx = nWords - 1; + int prev = data[idx]; + int hi = prev >>> anticount; + int[] result = data; + int[] src = data; + if (hi != 0) { + if(nWords == data.length) { + data = result = new int[nWords + 1]; + } + result[nWords++] = hi; + } + leftShift(src,idx,result,bitcount,anticount,prev); + } + } + offset += wordcount; + return this; + } + } + + /** + * Returns the number of ints this FDBigInteger represents. + * + * @return Number of ints required to represent this FDBigInteger. + */ + /*@ + @ requires this.value() == 0; + @ ensures \result == 0; + @ + @ also + @ + @ requires this.value() > 0; + @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result; + @*/ + private /*@ pure @*/ int size() { + return nWords + offset; + } + + + /** + * Computes + *
    +     * q = (int)( this / S )
    +     * this = 10 * ( this mod S )
    +     * Return q.
    +     * 
    + * This is the iteration step of digit development for output. + * We assume that S has been normalized, as above, and that + * "this" has been left-shifted accordingly. + * Also assumed, of course, is that the result, q, can be expressed + * as an integer, 0 <= q < 10. + * + * @param The divisor of this FDBigInteger. + * @return q = (int)(this / S). + */ + /*@ + @ requires !this.isImmutable; + @ requires this.size() <= S.size(); + @ requires this.data.length + this.offset >= S.size(); + @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4); + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures \result == \old(this.value() / S.value()); + @ ensures this.value() == \old(10 * (this.value() % S.value())); + @*/ + public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException { + assert !this.isImmutable : "cannot modify immutable value"; + // ensure that this and S have the same number of + // digits. If S is properly normalized and q < 10 then + // this must be so. + int thSize = this.size(); + int sSize = S.size(); + if (thSize < sSize) { + // this value is significantly less than S, result of division is zero. + // just mult this by 10. + int p = multAndCarryBy10(this.data, this.nWords, this.data); + if(p!=0) { + this.data[nWords++] = p; + } else { + trimLeadingZeros(); + } + return 0; + } else if (thSize > sSize) { + throw new IllegalArgumentException("disparate values"); + } + // estimate q the obvious way. We will usually be + // right. If not, then we're only off by a little and + // will re-add. + long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK); + long diff = multDiffMe(q, S); + if (diff != 0L) { + //@ assert q != 0; + //@ assert this.offset == \old(Math.min(this.offset, S.offset)); + //@ assert this.offset <= S.offset; + + // q is too big. + // add S back in until this turns +. This should + // not be very many times! + long sum = 0L; + int tStart = S.offset - this.offset; + //@ assert tStart >= 0; + int[] sd = S.data; + int[] td = this.data; + while (sum == 0L) { + for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) { + sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK); + td[tIndex] = (int) sum; + sum >>>= 32; // Signed or unsigned, answer is 0 or 1 + } + // + // Originally the following line read + // "if ( sum !=0 && sum != -1 )" + // but that would be wrong, because of the + // treatment of the two values as entirely unsigned, + // it would be impossible for a carry-out to be interpreted + // as -1 -- it would have to be a single-bit carry-out, or +1. + // + assert sum == 0 || sum == 1 : sum; // carry out of division correction + q -= 1; + } + } + // finally, we can multiply this by 10. + // it cannot overflow, right, as the high-order word has + // at least 4 high-order zeros! + int p = multAndCarryBy10(this.data, this.nWords, this.data); + assert p == 0 : p; // Carry out of *10 + trimLeadingZeros(); + return (int) q; + } + + /** + * Multiplies this FDBigInteger by 10. The operation will be + * performed in place unless the FDBigInteger is immutable in + * which case a new FDBigInteger will be returned. + * + * @return The FDBigInteger multiplied by 10. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && this.isImmutable; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * 10); + @ + @ also + @ + @ requires this.value() > 0 && !this.isImmutable; + @ assignable this.nWords, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() * 10); + @*/ + public FDBigInteger multBy10() { + if (nWords == 0) { + return this; + } + if (isImmutable) { + int[] res = new int[nWords + 1]; + res[nWords] = multAndCarryBy10(data, nWords, res); + return new FDBigInteger(res, offset); + } else { + int p = multAndCarryBy10(this.data, this.nWords, this.data); + if (p != 0) { + if (nWords == data.length) { + if (data[0] == 0) { + System.arraycopy(data, 1, data, 0, --nWords); + offset++; + } else { + data = Arrays.copyOf(data, data.length + 1); + } + } + data[nWords++] = p; + } else { + trimLeadingZeros(); + } + return this; + } + } + + /** + * Multiplies this FDBigInteger by + * 5p5 * 2p2. The operation will be + * performed in place if possible, otherwise a new FDBigInteger + * will be returned. + * + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return + */ + /*@ + @ requires this.value() == 0 || p5 == 0 && p2 == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); + @ + @ also + @ + @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable; + @ assignable this.nWords, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() * pow52(p5, p2)); + @*/ + public FDBigInteger multByPow52(int p5, int p2) { + if (this.nWords == 0) { + return this; + } + FDBigInteger res = this; + if (p5 != 0) { + int[] r; + int extraSize = (p2 != 0) ? 1 : 0; + if (p5 < SMALL_5_POW.length) { + r = new int[this.nWords + 1 + extraSize]; + mult(this.data, this.nWords, SMALL_5_POW[p5], r); + res = new FDBigInteger(r, this.offset); + } else { + FDBigInteger pow5 = big5pow(p5); + r = new int[this.nWords + pow5.size() + extraSize]; + mult(this.data, this.nWords, pow5.data, pow5.nWords, r); + res = new FDBigInteger(r, this.offset + pow5.offset); + } + } + return res.leftShift(p2); + } + + /** + * Multiplies two big integers represented as int arrays. + * + * @param s1 The first array factor. + * @param s1Len The number of elements of s1 to use. + * @param s2 The second array factor. + * @param s2Len The number of elements of s2 to use. + * @param dst The product array. + */ + /*@ + @ requires s1 != dst && s2 != dst; + @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len; + @ assignable dst[0 .. s1Len + s2Len - 1]; + @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len)); + @*/ + private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) { + for (int i = 0; i < s1Len; i++) { + long v = s1[i] & LONG_MASK; + long p = 0L; + for (int j = 0; j < s2Len; j++) { + p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK); + dst[i + j] = (int) p; + p >>>= 32; + } + dst[i + s2Len] = (int) p; + } + } + + /** + * Subtracts the supplied FDBigInteger subtrahend from this + * FDBigInteger. Assert that the result is positive. + * If the subtrahend is immutable, store the result in this(minuend). + * If this(minuend) is immutable a new FDBigInteger is created. + * + * @param subtrahend The FDBigInteger to be subtracted. + * @return This FDBigInteger less the subtrahend. + */ + /*@ + @ requires this.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @ + @ also + @ + @ requires !subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures \result == this; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @*/ + public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) { + assert this.size() >= subtrahend.size() : "result should be positive"; + FDBigInteger minuend; + if (this.isImmutable) { + minuend = new FDBigInteger(this.data.clone(), this.offset); + } else { + minuend = this; + } + int offsetDiff = subtrahend.offset - minuend.offset; + int[] sData = subtrahend.data; + int[] mData = minuend.data; + int subLen = subtrahend.nWords; + int minLen = minuend.nWords; + if (offsetDiff < 0) { + // need to expand minuend + int rLen = minLen - offsetDiff; + if (rLen < mData.length) { + System.arraycopy(mData, 0, mData, -offsetDiff, minLen); + Arrays.fill(mData, 0, -offsetDiff, 0); + } else { + int[] r = new int[rLen]; + System.arraycopy(mData, 0, r, -offsetDiff, minLen); + minuend.data = mData = r; + } + minuend.offset = subtrahend.offset; + minuend.nWords = minLen = rLen; + offsetDiff = 0; + } + long borrow = 0L; + int mIndex = offsetDiff; + for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) { + long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; + mData[mIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + for (; borrow != 0 && mIndex < minLen; mIndex++) { + long diff = (mData[mIndex] & LONG_MASK) + borrow; + mData[mIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + assert borrow == 0L : borrow; // borrow out of subtract, + // result should be positive + minuend.trimLeadingZeros(); + return minuend; + } + + /** + * Subtracts the supplied FDBigInteger subtrahend from this + * FDBigInteger. Assert that the result is positive. + * If the this(minuend) is immutable, store the result in subtrahend. + * If subtrahend is immutable a new FDBigInteger is created. + * + * @param subtrahend The FDBigInteger to be subtracted. + * @return This FDBigInteger less the subtrahend. + */ + /*@ + @ requires subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @ + @ also + @ + @ requires !subtrahend.isImmutable; + @ requires this.value() >= subtrahend.value(); + @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*]; + @ ensures \result == subtrahend; + @ ensures \result.value() == \old(this.value() - subtrahend.value()); + @*/ + public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) { + assert this.size() >= subtrahend.size() : "result should be positive"; + FDBigInteger minuend = this; + if (subtrahend.isImmutable) { + subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset); + } + int offsetDiff = minuend.offset - subtrahend.offset; + int[] sData = subtrahend.data; + int[] mData = minuend.data; + int subLen = subtrahend.nWords; + int minLen = minuend.nWords; + if (offsetDiff < 0) { + int rLen = minLen; + if (rLen < sData.length) { + System.arraycopy(sData, 0, sData, -offsetDiff, subLen); + Arrays.fill(sData, 0, -offsetDiff, 0); + } else { + int[] r = new int[rLen]; + System.arraycopy(sData, 0, r, -offsetDiff, subLen); + subtrahend.data = sData = r; + } + subtrahend.offset = minuend.offset; + subLen -= offsetDiff; + offsetDiff = 0; + } else { + int rLen = minLen + offsetDiff; + if (rLen >= sData.length) { + subtrahend.data = sData = Arrays.copyOf(sData, rLen); + } + } + //@ assert minuend == this && minuend.value() == \old(this.value()); + //@ assert mData == minuend.data && minLen == minuend.nWords; + //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size(); + //@ assert sData == subtrahend.data; + //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value()); + //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset); + //@ assert offsetDiff == minuend.offset - subtrahend.offset; + //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length; + int sIndex = 0; + long borrow = 0L; + for (; sIndex < offsetDiff; sIndex++) { + long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow; + sData[sIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + //@ assert sIndex == offsetDiff; + for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) { + //@ assert sIndex == offsetDiff + mIndex; + long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow; + sData[sIndex] = (int) diff; + borrow = diff >> 32; // signed shift + } + assert borrow == 0L : borrow; // borrow out of subtract, + // result should be positive + subtrahend.nWords = sIndex; + subtrahend.trimLeadingZeros(); + return subtrahend; + + } + + /** + * Determines whether all elements of an array are zero for all indices less + * than a given index. + * + * @param a The array to be examined. + * @param from The index strictly below which elements are to be examined. + * @return Zero if all elements in range are zero, 1 otherwise. + */ + /*@ + @ requires 0 <= from && from <= a.length; + @ ensures \result == (AP(a, from) == 0 ? 0 : 1); + @*/ + private /*@ pure @*/ static int checkZeroTail(int[] a, int from) { + while (from > 0) { + if (a[--from] != 0) { + return 1; + } + } + return 0; + } + + /** + * Compares the parameter with this FDBigInteger. Returns an + * integer accordingly as: + *
    +     * >0: this > other
    +     *  0: this == other
    +     * <0: this < other
    +     * 
    + * + * @param other The FDBigInteger to compare. + * @return A negative value, zero, or a positive value according to the + * result of the comparison. + */ + /*@ + @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0); + @*/ + public /*@ pure @*/ int cmp(FDBigInteger other) { + int aSize = nWords + offset; + int bSize = other.nWords + other.offset; + if (aSize > bSize) { + return 1; + } else if (aSize < bSize) { + return -1; + } + int aLen = nWords; + int bLen = other.nWords; + while (aLen > 0 && bLen > 0) { + int a = data[--aLen]; + int b = other.data[--bLen]; + if (a != b) { + return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; + } + } + if (aLen > 0) { + return checkZeroTail(data, aLen); + } + if (bLen > 0) { + return -checkZeroTail(other.data, bLen); + } + return 0; + } + + /** + * Compares this FDBigInteger with + * 5p5 * 2p2. + * Returns an integer accordingly as: + *
    +     * >0: this > other
    +     *  0: this == other
    +     * <0: this < other
    +     * 
    + * @param p5 The exponent of the power-of-five factor. + * @param p2 The exponent of the power-of-two factor. + * @return A negative value, zero, or a positive value according to the + * result of the comparison. + */ + /*@ + @ requires p5 >= 0 && p2 >= 0; + @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0); + @*/ + public /*@ pure @*/ int cmpPow52(int p5, int p2) { + if (p5 == 0) { + int wordcount = p2 >> 5; + int bitcount = p2 & 0x1f; + int size = this.nWords + this.offset; + if (size > wordcount + 1) { + return 1; + } else if (size < wordcount + 1) { + return -1; + } + int a = this.data[this.nWords -1]; + int b = 1 << bitcount; + if (a != b) { + return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1; + } + return checkZeroTail(this.data, this.nWords - 1); + } + return this.cmp(big5pow(p5).leftShift(p2)); + } + + /** + * Compares this FDBigInteger with x + y. Returns a + * value according to the comparison as: + *
    +     * -1: this <  x + y
    +     *  0: this == x + y
    +     *  1: this >  x + y
    +     * 
    + * @param x The first addend of the sum to compare. + * @param y The second addend of the sum to compare. + * @return -1, 0, or 1 according to the result of the comparison. + */ + /*@ + @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0); + @*/ + public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) { + FDBigInteger big; + FDBigInteger small; + int xSize = x.size(); + int ySize = y.size(); + int bSize; + int sSize; + if (xSize >= ySize) { + big = x; + small = y; + bSize = xSize; + sSize = ySize; + } else { + big = y; + small = x; + bSize = ySize; + sSize = xSize; + } + int thSize = this.size(); + if (bSize == 0) { + return thSize == 0 ? 0 : 1; + } + if (sSize == 0) { + return this.cmp(big); + } + if (bSize > thSize) { + return -1; + } + if (bSize + 1 < thSize) { + return 1; + } + long top = (big.data[big.nWords - 1] & LONG_MASK); + if (sSize == bSize) { + top += (small.data[small.nWords - 1] & LONG_MASK); + } + if ((top >>> 32) == 0) { + if (((top + 1) >>> 32) == 0) { + // good case - no carry extension + if (bSize < thSize) { + return 1; + } + // here sum.nWords == this.nWords + long v = (this.data[this.nWords - 1] & LONG_MASK); + if (v < top) { + return -1; + } + if (v > top + 1) { + return 1; + } + } + } else { // (top>>>32)!=0 guaranteed carry extension + if (bSize + 1 > thSize) { + return -1; + } + // here sum.nWords == this.nWords + top >>>= 32; + long v = (this.data[this.nWords - 1] & LONG_MASK); + if (v < top) { + return -1; + } + if (v > top + 1) { + return 1; + } + } + return this.cmp(big.add(small)); + } + + /** + * Makes this FDBigInteger immutable. + */ + /*@ + @ assignable this.isImmutable; + @ ensures this.isImmutable; + @*/ + public void makeImmutable() { + this.isImmutable = true; + } + + /** + * Multiplies this FDBigInteger by an integer. + * + * @param i The factor by which to multiply this FDBigInteger. + * @return This FDBigInteger multiplied by an integer. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() != 0; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * UNSIGNED(i)); + @*/ + private FDBigInteger mult(int i) { + if (this.nWords == 0) { + return this; + } + int[] r = new int[nWords + 1]; + mult(data, nWords, i, r); + return new FDBigInteger(r, offset); + } + + /** + * Multiplies this FDBigInteger by another FDBigInteger. + * + * @param other The FDBigInteger factor by which to multiply. + * @return The product of this and the parameter FDBigIntegers. + */ + /*@ + @ requires this.value() == 0; + @ assignable \nothing; + @ ensures \result == this; + @ + @ also + @ + @ requires this.value() != 0 && other.value() == 0; + @ assignable \nothing; + @ ensures \result == other; + @ + @ also + @ + @ requires this.value() != 0 && other.value() != 0; + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() * other.value()); + @*/ + private FDBigInteger mult(FDBigInteger other) { + if (this.nWords == 0) { + return this; + } + if (this.size() == 1) { + return other.mult(data[0]); + } + if (other.nWords == 0) { + return other; + } + if (other.size() == 1) { + return this.mult(other.data[0]); + } + int[] r = new int[nWords + other.nWords]; + mult(this.data, this.nWords, other.data, other.nWords, r); + return new FDBigInteger(r, this.offset + other.offset); + } + + /** + * Adds another FDBigInteger to this FDBigInteger. + * + * @param other The FDBigInteger to add. + * @return The sum of the FDBigIntegers. + */ + /*@ + @ assignable \nothing; + @ ensures \result.value() == \old(this.value() + other.value()); + @*/ + private FDBigInteger add(FDBigInteger other) { + FDBigInteger big, small; + int bigLen, smallLen; + int tSize = this.size(); + int oSize = other.size(); + if (tSize >= oSize) { + big = this; + bigLen = tSize; + small = other; + smallLen = oSize; + } else { + big = other; + bigLen = oSize; + small = this; + smallLen = tSize; + } + int[] r = new int[bigLen + 1]; + int i = 0; + long carry = 0L; + for (; i < smallLen; i++) { + carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ) + + ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK))); + r[i] = (int) carry; + carry >>= 32; // signed shift. + } + for (; i < bigLen; i++) { + carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) ); + r[i] = (int) carry; + carry >>= 32; // signed shift. + } + r[bigLen] = (int) carry; + return new FDBigInteger(r, 0); + } + + + /** + * Multiplies a FDBigInteger by an int and adds another int. The + * result is computed in place. This method is intended only to be invoked + * from + * + * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) + * . + * + * @param iv The factor by which to multiply this FDBigInteger. + * @param addend The value to add to the product of this + * FDBigInteger and iv. + */ + /*@ + @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32); + @ assignable this.data[*]; + @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend)); + @*/ + private /*@ helper @*/ void multAddMe(int iv, int addend) { + long v = iv & LONG_MASK; + // unroll 0th iteration, doing addition. + long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK); + data[0] = (int) p; + p >>>= 32; + for (int i = 1; i < nWords; i++) { + p += v * (data[i] & LONG_MASK); + data[i] = (int) p; + p >>>= 32; + } + if (p != 0L) { + data[nWords++] = (int) p; // will fail noisily if illegal! + } + } + + // + // original doc: + // + // do this -=q*S + // returns borrow + // + /** + * Multiplies the parameters and subtracts them from this + * FDBigInteger. + * + * @param q The integer parameter. + * @param S The FDBigInteger parameter. + * @return this - q*S. + */ + /*@ + @ ensures nWords == 0 ==> offset == 0; + @ ensures nWords > 0 ==> data[nWords - 1] != 0; + @*/ + /*@ + @ requires 0 < q && q <= (1L << 31); + @ requires data != null; + @ requires 0 <= nWords && nWords <= data.length && offset >= 0; + @ requires !this.isImmutable; + @ requires this.size() == S.size(); + @ requires this != S; + @ assignable this.nWords, this.offset, this.data, this.data[*]; + @ ensures -q <= \result && \result <= 0; + @ ensures this.size() == \old(this.size()); + @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value()); + @ ensures this.offset == \old(Math.min(this.offset, S.offset)); + @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords); + @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset); + @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data); + @ + @ also + @ + @ requires q == 0; + @ assignable \nothing; + @ ensures \result == 0; + @*/ + private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) { + long diff = 0L; + if (q != 0) { + int deltaSize = S.offset - this.offset; + if (deltaSize >= 0) { + int[] sd = S.data; + int[] td = this.data; + for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) { + diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); + td[tIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + } else { + deltaSize = -deltaSize; + int[] rd = new int[nWords + deltaSize]; + int sIndex = 0; + int rIndex = 0; + int[] sd = S.data; + for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) { + diff -= q * (sd[sIndex] & LONG_MASK); + rd[rIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + int tIndex = 0; + int[] td = this.data; + for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) { + diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK); + rd[rIndex] = (int) diff; + diff >>= 32; // N.B. SIGNED shift. + } + this.nWords += deltaSize; + this.offset -= deltaSize; + this.data = rd; + } + } + return diff; + } + + + /** + * Multiplies by 10 a big integer represented as an array. The final carry + * is returned. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param dst The product array. + * @return The final carry of the multiplication. + */ + /*@ + @ requires src.length >= srcLen && dst.length >= srcLen; + @ assignable dst[0 .. srcLen - 1]; + @ ensures 0 <= \result && \result < 10; + @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10); + @*/ + private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) { + long carry = 0; + for (int i = 0; i < srcLen; i++) { + long product = (src[i] & LONG_MASK) * 10L + carry; + dst[i] = (int) product; + carry = product >>> 32; + } + return (int) carry; + } + + /** + * Multiplies by a constant value a big integer represented as an array. + * The constant factor is an int. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param value The constant factor by which to multiply. + * @param dst The product array. + */ + /*@ + @ requires src.length >= srcLen && dst.length >= srcLen + 1; + @ assignable dst[0 .. srcLen]; + @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value)); + @*/ + private static void mult(int[] src, int srcLen, int value, int[] dst) { + long val = value & LONG_MASK; + long carry = 0; + for (int i = 0; i < srcLen; i++) { + long product = (src[i] & LONG_MASK) * val + carry; + dst[i] = (int) product; + carry = product >>> 32; + } + dst[srcLen] = (int) carry; + } + + /** + * Multiplies by a constant value a big integer represented as an array. + * The constant factor is a long represent as two ints. + * + * @param src The array representation of the big integer. + * @param srcLen The number of elements of src to use. + * @param v0 The lower 32 bits of the long factor. + * @param v1 The upper 32 bits of the long factor. + * @param dst The product array. + */ + /*@ + @ requires src != dst; + @ requires src.length >= srcLen && dst.length >= srcLen + 2; + @ assignable dst[0 .. srcLen + 1]; + @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32))); + @*/ + private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) { + long v = v0 & LONG_MASK; + long carry = 0; + for (int j = 0; j < srcLen; j++) { + long product = v * (src[j] & LONG_MASK) + carry; + dst[j] = (int) product; + carry = product >>> 32; + } + dst[srcLen] = (int) carry; + v = v1 & LONG_MASK; + carry = 0; + for (int j = 0; j < srcLen; j++) { + long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry; + dst[j + 1] = (int) product; + carry = product >>> 32; + } + dst[srcLen + 1] = (int) carry; + } + + // Fails assertion for negative exponent. + /** + * Computes 5 raised to a given power. + * + * @param p The exponent of 5. + * @return 5p. + */ + private static FDBigInteger big5pow(int p) { + assert p >= 0 : p; // negative power of 5 + if (p < MAX_FIVE_POW) { + return POW_5_CACHE[p]; + } + return big5powRec(p); + } + + // slow path + /** + * Computes 5 raised to a given power. + * + * @param p The exponent of 5. + * @return 5p. + */ + private static FDBigInteger big5powRec(int p) { + if (p < MAX_FIVE_POW) { + return POW_5_CACHE[p]; + } + // construct the value. + // recursively. + int q, r; + // in order to compute 5^p, + // compute its square root, 5^(p/2) and square. + // or, let q = p / 2, r = p -q, then + // 5^p = 5^(q+r) = 5^q * 5^r + q = p >> 1; + r = p - q; + FDBigInteger bigq = big5powRec(q); + if (r < SMALL_5_POW.length) { + return bigq.mult(SMALL_5_POW[r]); + } else { + return bigq.mult(big5powRec(r)); + } + } + + // for debugging ... + /** + * Converts this FDBigInteger to a hexadecimal string. + * + * @return The hexadecimal string representation. + */ + public String toHexString(){ + if(nWords ==0) { + return "0"; + } + StringBuilder sb = new StringBuilder((nWords +offset)*8); + for(int i= nWords -1; i>=0; i--) { + String subStr = Integer.toHexString(data[i]); + for(int j = subStr.length(); j<8; j++) { + sb.append('0'); + } + sb.append(subStr); + } + for(int i=offset; i>0; i--) { + sb.append("00000000"); + } + return sb.toString(); + } + + // for debugging ... + /** + * Converts this FDBigInteger to a BigInteger. + * + * @return The BigInteger representation. + */ + public BigInteger toBigInteger() { + byte[] magnitude = new byte[nWords * 4 + 1]; + for (int i = 0; i < nWords; i++) { + int w = data[i]; + magnitude[magnitude.length - 4 * i - 1] = (byte) w; + magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8); + magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16); + magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24); + } + return new BigInteger(magnitude).shiftLeft(offset * 32); + } + + // for debugging ... + /** + * Converts this FDBigInteger to a string. + * + * @return The string representation. + */ + @Override + public String toString(){ + return toBigInteger().toString(); + } +} diff --git a/src/sun/misc/FloatConsts.java b/src/sun/misc/FloatConsts.java new file mode 100644 index 00000000..26b72bc0 --- /dev/null +++ b/src/sun/misc/FloatConsts.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * This class contains additional constants documenting limits of the + * float type. + * + * @author Joseph D. Darcy + */ + +public class FloatConsts { + /** + * Don't let anyone instantiate this class. + */ + private FloatConsts() {} + + public static final float POSITIVE_INFINITY = Float.POSITIVE_INFINITY; + public static final float NEGATIVE_INFINITY = Float.NEGATIVE_INFINITY; + public static final float NaN = Float.NaN; + public static final float MAX_VALUE = Float.MAX_VALUE; + public static final float MIN_VALUE = Float.MIN_VALUE; + + /** + * A constant holding the smallest positive normal value of type + * float, 2-126. It is equal to the value + * returned by Float.intBitsToFloat(0x00800000). + */ + public static final float MIN_NORMAL = 1.17549435E-38f; + + /** + * The number of logical bits in the significand of a + * float number, including the implicit bit. + */ + public static final int SIGNIFICAND_WIDTH = 24; + + /** + * Maximum exponent a finite float number may have. + * It is equal to the value returned by + * Math.ilogb(Float.MAX_VALUE). + */ + public static final int MAX_EXPONENT = 127; + + /** + * Minimum exponent a normalized float number may + * have. It is equal to the value returned by + * Math.ilogb(Float.MIN_NORMAL). + */ + public static final int MIN_EXPONENT = -126; + + /** + * The exponent the smallest positive float subnormal + * value would have if it could be normalized. It is equal to the + * value returned by FpUtils.ilogb(Float.MIN_VALUE). + */ + public static final int MIN_SUB_EXPONENT = MIN_EXPONENT - + (SIGNIFICAND_WIDTH - 1); + + /** + * Bias used in representing a float exponent. + */ + public static final int EXP_BIAS = 127; + + /** + * Bit mask to isolate the sign bit of a float. + */ + public static final int SIGN_BIT_MASK = 0x80000000; + + /** + * Bit mask to isolate the exponent field of a + * float. + */ + public static final int EXP_BIT_MASK = 0x7F800000; + + /** + * Bit mask to isolate the significand field of a + * float. + */ + public static final int SIGNIF_BIT_MASK = 0x007FFFFF; + + static { + // verify bit masks cover all bit positions and that the bit + // masks are non-overlapping + assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) && + (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) && + ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) && + ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0))); + } +} diff --git a/src/sun/misc/FloatingDecimal.java b/src/sun/misc/FloatingDecimal.java new file mode 100644 index 00000000..a4f8cc4a --- /dev/null +++ b/src/sun/misc/FloatingDecimal.java @@ -0,0 +1,2542 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A class for converting between ASCII and decimal representations of a single + * or double precision floating point number. Most conversions are provided via + * static convenience methods, although a BinaryToASCIIConverter + * instance may be obtained and reused. + */ +public class FloatingDecimal{ + // + // Constants of the implementation; + // most are IEEE-754 related. + // (There are more really boring constants at the end.) + // + static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1; + static final long FRACT_HOB = ( 1L<String. + * + * @param d The double precision value. + * @return The value converted to a String. + */ + public static String toJavaFormatString(double d) { + return getBinaryToASCIIConverter(d).toJavaFormatString(); + } + + /** + * Converts a single precision floating point value to a String. + * + * @param f The single precision value. + * @return The value converted to a String. + */ + public static String toJavaFormatString(float f) { + return getBinaryToASCIIConverter(f).toJavaFormatString(); + } + + /** + * Appends a double precision floating point value to an Appendable. + * @param d The double precision value. + * @param buf The Appendable with the value appended. + */ + public static void appendTo(double d, Appendable buf) { + getBinaryToASCIIConverter(d).appendTo(buf); + } + + /** + * Appends a single precision floating point value to an Appendable. + * @param f The single precision value. + * @param buf The Appendable with the value appended. + */ + public static void appendTo(float f, Appendable buf) { + getBinaryToASCIIConverter(f).appendTo(buf); + } + + /** + * Converts a String to a double precision floating point value. + * + * @param s The String to convert. + * @return The double precision value. + * @throws NumberFormatException If the String does not + * represent a properly formatted double precision value. + */ + public static double parseDouble(String s) throws NumberFormatException { + return readJavaFormatString(s).doubleValue(); + } + + /** + * Converts a String to a single precision floating point value. + * + * @param s The String to convert. + * @return The single precision value. + * @throws NumberFormatException If the String does not + * represent a properly formatted single precision value. + */ + public static float parseFloat(String s) throws NumberFormatException { + return readJavaFormatString(s).floatValue(); + } + + /** + * A converter which can process single or double precision floating point + * values into an ASCII String representation. + */ + public interface BinaryToASCIIConverter { + /** + * Converts a floating point value into an ASCII String. + * @return The value converted to a String. + */ + public String toJavaFormatString(); + + /** + * Appends a floating point value to an Appendable. + * @param buf The Appendable to receive the value. + */ + public void appendTo(Appendable buf); + + /** + * Retrieves the decimal exponent most closely corresponding to this value. + * @return The decimal exponent. + */ + public int getDecimalExponent(); + + /** + * Retrieves the value as an array of digits. + * @param digits The digit array. + * @return The number of valid digits copied into the array. + */ + public int getDigits(char[] digits); + + /** + * Indicates the sign of the value. + * @return value < 0.0. + */ + public boolean isNegative(); + + /** + * Indicates whether the value is either infinite or not a number. + * + * @return true if and only if the value is NaN + * or infinite. + */ + public boolean isExceptional(); + + /** + * Indicates whether the value was rounded up during the binary to ASCII + * conversion. + * + * @return true if and only if the value was rounded up. + */ + public boolean digitsRoundedUp(); + + /** + * Indicates whether the binary to ASCII conversion was exact. + * + * @return true if any only if the conversion was exact. + */ + public boolean decimalDigitsExact(); + } + + /** + * A BinaryToASCIIConverter which represents NaN + * and infinite values. + */ + private static class ExceptionalBinaryToASCIIBuffer implements BinaryToASCIIConverter { + final private String image; + private boolean isNegative; + + public ExceptionalBinaryToASCIIBuffer(String image, boolean isNegative) { + this.image = image; + this.isNegative = isNegative; + } + + @Override + public String toJavaFormatString() { + return image; + } + + @Override + public void appendTo(Appendable buf) { + if (buf instanceof StringBuilder) { + ((StringBuilder) buf).append(image); + } else if (buf instanceof StringBuffer) { + ((StringBuffer) buf).append(image); + } else { + assert false; + } + } + + @Override + public int getDecimalExponent() { + throw new IllegalArgumentException("Exceptional value does not have an exponent"); + } + + @Override + public int getDigits(char[] digits) { + throw new IllegalArgumentException("Exceptional value does not have digits"); + } + + @Override + public boolean isNegative() { + return isNegative; + } + + @Override + public boolean isExceptional() { + return true; + } + + @Override + public boolean digitsRoundedUp() { + throw new IllegalArgumentException("Exceptional value is not rounded"); + } + + @Override + public boolean decimalDigitsExact() { + throw new IllegalArgumentException("Exceptional value is not exact"); + } + } + + private static final String INFINITY_REP = "Infinity"; + private static final int INFINITY_LENGTH = INFINITY_REP.length(); + private static final String NAN_REP = "NaN"; + private static final int NAN_LENGTH = NAN_REP.length(); + + private static final BinaryToASCIIConverter B2AC_POSITIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer(INFINITY_REP, false); + private static final BinaryToASCIIConverter B2AC_NEGATIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer("-" + INFINITY_REP, true); + private static final BinaryToASCIIConverter B2AC_NOT_A_NUMBER = new ExceptionalBinaryToASCIIBuffer(NAN_REP, false); + private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new char[]{'0'}); + private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new char[]{'0'}); + + /** + * A buffered implementation of BinaryToASCIIConverter. + */ + static class BinaryToASCIIBuffer implements BinaryToASCIIConverter { + private boolean isNegative; + private int decExponent; + private int firstDigitIndex; + private int nDigits; + private final char[] digits; + private final char[] buffer = new char[26]; + + // + // The fields below provide additional information about the result of + // the binary to decimal digits conversion done in dtoa() and roundup() + // methods. They are changed if needed by those two methods. + // + + // True if the dtoa() binary to decimal conversion was exact. + private boolean exactDecimalConversion = false; + + // True if the result of the binary to decimal conversion was rounded-up + // at the end of the conversion process, i.e. roundUp() method was called. + private boolean decimalDigitsRoundedUp = false; + + /** + * Default constructor; used for non-zero values, + * BinaryToASCIIBuffer may be thread-local and reused + */ + BinaryToASCIIBuffer(){ + this.digits = new char[20]; + } + + /** + * Creates a specialized value (positive and negative zeros). + */ + BinaryToASCIIBuffer(boolean isNegative, char[] digits){ + this.isNegative = isNegative; + this.decExponent = 0; + this.digits = digits; + this.firstDigitIndex = 0; + this.nDigits = digits.length; + } + + @Override + public String toJavaFormatString() { + int len = getChars(buffer); + return new String(buffer, 0, len); + } + + @Override + public void appendTo(Appendable buf) { + int len = getChars(buffer); + if (buf instanceof StringBuilder) { + ((StringBuilder) buf).append(buffer, 0, len); + } else if (buf instanceof StringBuffer) { + ((StringBuffer) buf).append(buffer, 0, len); + } else { + assert false; + } + } + + @Override + public int getDecimalExponent() { + return decExponent; + } + + @Override + public int getDigits(char[] digits) { + System.arraycopy(this.digits,firstDigitIndex,digits,0,this.nDigits); + return this.nDigits; + } + + @Override + public boolean isNegative() { + return isNegative; + } + + @Override + public boolean isExceptional() { + return false; + } + + @Override + public boolean digitsRoundedUp() { + return decimalDigitsRoundedUp; + } + + @Override + public boolean decimalDigitsExact() { + return exactDecimalConversion; + } + + private void setSign(boolean isNegative) { + this.isNegative = isNegative; + } + + /** + * This is the easy subcase -- + * all the significant bits, after scaling, are held in lvalue. + * negSign and decExponent tell us what processing and scaling + * has already been done. Exceptional cases have already been + * stripped out. + * In particular: + * lvalue is a finite number (not Inf, nor NaN) + * lvalue > 0L (not zero, nor negative). + * + * The only reason that we develop the digits here, rather than + * calling on Long.toString() is that we can do it a little faster, + * and besides want to treat trailing 0s specially. If Long.toString + * changes, we should re-evaluate this strategy! + */ + private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){ + if ( insignificantDigits != 0 ){ + // Discard non-significant low-order bits, while rounding, + // up to insignificant value. + long pow10 = FDBigInteger.LONG_5_POW[insignificantDigits] << insignificantDigits; // 10^i == 5^i * 2^i; + long residue = lvalue % pow10; + lvalue /= pow10; + decExponent += insignificantDigits; + if ( residue >= (pow10>>1) ){ + // round up based on the low-order bits we're discarding + lvalue++; + } + } + int digitno = digits.length -1; + int c; + if ( lvalue <= Integer.MAX_VALUE ){ + assert lvalue > 0L : lvalue; // lvalue <= 0 + // even easier subcase! + // can do int arithmetic rather than long! + int ivalue = (int)lvalue; + c = ivalue%10; + ivalue /= 10; + while ( c == 0 ){ + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + while ( ivalue != 0){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = ivalue%10; + ivalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } else { + // same algorithm as above (same bugs, too ) + // but using long arithmetic. + c = (int)(lvalue%10L); + lvalue /= 10L; + while ( c == 0 ){ + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10L; + } + while ( lvalue != 0L ){ + digits[digitno--] = (char)(c+'0'); + decExponent++; + c = (int)(lvalue%10L); + lvalue /= 10; + } + digits[digitno] = (char)(c+'0'); + } + this.decExponent = decExponent+1; + this.firstDigitIndex = digitno; + this.nDigits = this.digits.length - digitno; + } + + private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat) + { + assert fractBits > 0 ; // fractBits here can't be zero or negative + assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set + // Examine number. Determine if it is an easy case, + // which we can do pretty trivially using float/long conversion, + // or whether we must do real work. + final int tailZeros = Long.numberOfTrailingZeros(fractBits); + + // number of significant bits of fractBits; + final int nFractBits = EXP_SHIFT+1-tailZeros; + + // reset flags to default values as dtoa() does not always set these + // flags and a prior call to dtoa() might have set them to incorrect + // values with respect to the current state. + decimalDigitsRoundedUp = false; + exactDecimalConversion = false; + + // number of significant bits to the right of the point. + int nTinyBits = Math.max( 0, nFractBits - binExp - 1 ); + if ( binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP ){ + // Look more closely at the number to decide if, + // with scaling by 10^nTinyBits, the result will fit in + // a long. + if ( (nTinyBits < FDBigInteger.LONG_5_POW.length) && ((nFractBits + N_5_BITS[nTinyBits]) < 64 ) ){ + // + // We can do this: + // take the fraction bits, which are normalized. + // (a) nTinyBits == 0: Shift left or right appropriately + // to align the binary point at the extreme right, i.e. + // where a long int point is expected to be. The integer + // result is easily converted to a string. + // (b) nTinyBits > 0: Shift right by EXP_SHIFT-nFractBits, + // which effectively converts to long and scales by + // 2^nTinyBits. Then multiply by 5^nTinyBits to + // complete the scaling. We know this won't overflow + // because we just counted the number of bits necessary + // in the result. The integer you get from this can + // then be converted to a string pretty easily. + // + if ( nTinyBits == 0 ) { + int insignificant; + if ( binExp > nSignificantBits ){ + insignificant = insignificantDigitsForPow2(binExp-nSignificantBits-1); + } else { + insignificant = 0; + } + if ( binExp >= EXP_SHIFT ){ + fractBits <<= (binExp-EXP_SHIFT); + } else { + fractBits >>>= (EXP_SHIFT-binExp) ; + } + developLongDigits( 0, fractBits, insignificant ); + return; + } + // + // The following causes excess digits to be printed + // out in the single-float case. Our manipulation of + // halfULP here is apparently not correct. If we + // better understand how this works, perhaps we can + // use this special case again. But for the time being, + // we do not. + // else { + // fractBits >>>= EXP_SHIFT+1-nFractBits; + // fractBits//= long5pow[ nTinyBits ]; + // halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits); + // developLongDigits( -nTinyBits, fractBits, insignificantDigits(halfULP) ); + // return; + // } + // + } + } + // + // This is the hard case. We are going to compute large positive + // integers B and S and integer decExp, s.t. + // d = ( B / S )// 10^decExp + // 1 <= B / S < 10 + // Obvious choices are: + // decExp = floor( log10(d) ) + // B = d// 2^nTinyBits// 10^max( 0, -decExp ) + // S = 10^max( 0, decExp)// 2^nTinyBits + // (noting that nTinyBits has already been forced to non-negative) + // I am also going to compute a large positive integer + // M = (1/2^nSignificantBits)// 2^nTinyBits// 10^max( 0, -decExp ) + // i.e. M is (1/2) of the ULP of d, scaled like B. + // When we iterate through dividing B/S and picking off the + // quotient bits, we will know when to stop when the remainder + // is <= M. + // + // We keep track of powers of 2 and powers of 5. + // + int decExp = estimateDecExp(fractBits,binExp); + int B2, B5; // powers of 2 and powers of 5, respectively, in B + int S2, S5; // powers of 2 and powers of 5, respectively, in S + int M2, M5; // powers of 2 and powers of 5, respectively, in M + + B5 = Math.max( 0, -decExp ); + B2 = B5 + nTinyBits + binExp; + + S5 = Math.max( 0, decExp ); + S2 = S5 + nTinyBits; + + M5 = B5; + M2 = B2 - nSignificantBits; + + // + // the long integer fractBits contains the (nFractBits) interesting + // bits from the mantissa of d ( hidden 1 added if necessary) followed + // by (EXP_SHIFT+1-nFractBits) zeros. In the interest of compactness, + // I will shift out those zeros before turning fractBits into a + // FDBigInteger. The resulting whole number will be + // d * 2^(nFractBits-1-binExp). + // + fractBits >>>= tailZeros; + B2 -= nFractBits-1; + int common2factor = Math.min( B2, S2 ); + B2 -= common2factor; + S2 -= common2factor; + M2 -= common2factor; + + // + // HACK!! For exact powers of two, the next smallest number + // is only half as far away as we think (because the meaning of + // ULP changes at power-of-two bounds) for this reason, we + // hack M2. Hope this works. + // + if ( nFractBits == 1 ) { + M2 -= 1; + } + + if ( M2 < 0 ){ + // oops. + // since we cannot scale M down far enough, + // we must scale the other values up. + B2 -= M2; + S2 -= M2; + M2 = 0; + } + // + // Construct, Scale, iterate. + // Some day, we'll write a stopping test that takes + // account of the asymmetry of the spacing of floating-point + // numbers below perfect powers of 2 + // 26 Sept 96 is not that day. + // So we use a symmetric test. + // + int ndigit = 0; + boolean low, high; + long lowDigitDifference; + int q; + + // + // Detect the special cases where all the numbers we are about + // to compute will fit in int or long integers. + // In these cases, we will avoid doing FDBigInteger arithmetic. + // We use the same algorithms, except that we "normalize" + // our FDBigIntegers before iterating. This is to make division easier, + // as it makes our fist guess (quotient of high-order words) + // more accurate! + // + // Some day, we'll write a stopping test that takes + // account of the asymmetry of the spacing of floating-point + // numbers below perfect powers of 2 + // 26 Sept 96 is not that day. + // So we use a symmetric test. + // + // binary digits needed to represent B, approx. + int Bbits = nFractBits + B2 + (( B5 < N_5_BITS.length )? N_5_BITS[B5] : ( B5*3 )); + + // binary digits needed to represent 10*S, approx. + int tenSbits = S2+1 + (( (S5+1) < N_5_BITS.length )? N_5_BITS[(S5+1)] : ( (S5+1)*3 )); + if ( Bbits < 64 && tenSbits < 64){ + if ( Bbits < 32 && tenSbits < 32){ + // wa-hoo! They're all ints! + int b = ((int)fractBits * FDBigInteger.SMALL_5_POW[B5] ) << B2; + int s = FDBigInteger.SMALL_5_POW[S5] << S2; + int m = FDBigInteger.SMALL_5_POW[M5] << M2; + int tens = s * 10; + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = b / s; + b = 10 * ( b % s ); + m *= 10; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = b / s; + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } else { + // still good! they're all longs! + long b = (fractBits * FDBigInteger.LONG_5_POW[B5] ) << B2; + long s = FDBigInteger.LONG_5_POW[S5] << S2; + long m = FDBigInteger.LONG_5_POW[M5] << M2; + long tens = s * 10L; + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = (int) ( b / s ); + b = 10L * ( b % s ); + m *= 10L; + low = (b < m ); + high = (b+m > tens ); + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = (int) ( b / s ); + b = 10 * ( b % s ); + m *= 10; + assert q < 10 : q; // excessively large digit + if ( m > 0L ){ + low = (b < m ); + high = (b+m > tens ); + } else { + // hack -- m might overflow! + // in this case, it is certainly > b, + // which won't + // and b+m > tens, too, since that has overflowed + // either! + low = true; + high = true; + } + digits[ndigit++] = (char)('0' + q); + } + lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); + } + } else { + // + // We really must do FDBigInteger arithmetic. + // Fist, construct our FDBigInteger initial values. + // + FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2); + int shiftBias = Sval.getNormalizationBias(); + Sval = Sval.leftShift(shiftBias); // normalize so that division works better + + FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias); + FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1); + + FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 ); + // + // Unroll the first iteration. If our decExp estimate + // was too high, our first quotient will be zero. In this + // case, we discard it and decrement decExp. + // + ndigit = 0; + q = Bval.quoRemIteration( Sval ); + low = (Bval.cmp( Mval ) < 0); + high = tenSval.addAndCmp(Bval,Mval)<=0; + + assert q < 10 : q; // excessively large digit + if ( (q == 0) && ! high ){ + // oops. Usually ignore leading zero. + decExp--; + } else { + digits[ndigit++] = (char)('0' + q); + } + // + // HACK! Java spec sez that we always have at least + // one digit after the . in either F- or E-form output. + // Thus we will need more than one digit if we're using + // E-form + // + if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){ + high = low = false; + } + while( ! low && ! high ){ + q = Bval.quoRemIteration( Sval ); + assert q < 10 : q; // excessively large digit + Mval = Mval.multBy10(); //Mval = Mval.mult( 10 ); + low = (Bval.cmp( Mval ) < 0); + high = tenSval.addAndCmp(Bval,Mval)<=0; + digits[ndigit++] = (char)('0' + q); + } + if ( high && low ){ + Bval = Bval.leftShift(1); + lowDigitDifference = Bval.cmp(tenSval); + } else { + lowDigitDifference = 0L; // this here only for flow analysis! + } + exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0); + } + this.decExponent = decExp+1; + this.firstDigitIndex = 0; + this.nDigits = ndigit; + // + // Last digit gets rounded based on stopping condition. + // + if ( high ){ + if ( low ){ + if ( lowDigitDifference == 0L ){ + // it's a tie! + // choose based on which digits we like. + if ( (digits[firstDigitIndex+nDigits-1]&1) != 0 ) { + roundup(); + } + } else if ( lowDigitDifference > 0 ){ + roundup(); + } + } else { + roundup(); + } + } + } + + // add one to the least significant digit. + // in the unlikely event there is a carry out, deal with it. + // assert that this will only happen where there + // is only one digit, e.g. (float)1e-44 seems to do it. + // + private void roundup() { + int i = (firstDigitIndex + nDigits - 1); + int q = digits[i]; + if (q == '9') { + while (q == '9' && i > firstDigitIndex) { + digits[i] = '0'; + q = digits[--i]; + } + if (q == '9') { + // carryout! High-order 1, rest 0s, larger exp. + decExponent += 1; + digits[firstDigitIndex] = '1'; + return; + } + // else fall through. + } + digits[i] = (char) (q + 1); + decimalDigitsRoundedUp = true; + } + + /** + * Estimate decimal exponent. (If it is small-ish, + * we could double-check.) + * + * First, scale the mantissa bits such that 1 <= d2 < 2. + * We are then going to estimate + * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5) + * and so we can estimate + * log10(d) ~=~ log10(d2) + binExp * log10(2) + * take the floor and call it decExp. + */ + static int estimateDecExp(long fractBits, int binExp) { + double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) ); + double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981; + long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw + int exponent = (int)((dBits & DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT) - DoubleConsts.EXP_BIAS; + boolean isNegative = (dBits & DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign + if(exponent>=0 && exponent<52) { // hot path + long mask = DoubleConsts.SIGNIF_BIT_MASK >> exponent; + int r = (int)(( (dBits&DoubleConsts.SIGNIF_BIT_MASK) | FRACT_HOB )>>(EXP_SHIFT-exponent)); + return isNegative ? (((mask & dBits) == 0L ) ? -r : -r-1 ) : r; + } else if (exponent < 0) { + return (((dBits&~DoubleConsts.SIGN_BIT_MASK) == 0) ? 0 : + ( (isNegative) ? -1 : 0) ); + } else { //if (exponent >= 52) + return (int)d; + } + } + + private static int insignificantDigits(int insignificant) { + int i; + for ( i = 0; insignificant >= 10L; i++ ) { + insignificant /= 10L; + } + return i; + } + + /** + * Calculates + *
    +         * insignificantDigitsForPow2(v) == insignificantDigits(1L<
    +         */
    +        private static int insignificantDigitsForPow2(int p2) {
    +            if(p2>1 && p2 < insignificantDigitsNumber.length) {
    +                return insignificantDigitsNumber[p2];
    +            }
    +            return 0;
    +        }
    +
    +        /**
    +         *  If insignificant==(1L << ixd)
    +         *  i = insignificantDigitsNumber[idx] is the same as:
    +         *  int i;
    +         *  for ( i = 0; insignificant >= 10L; i++ )
    +         *         insignificant /= 10L;
    +         */
    +        private static int[] insignificantDigitsNumber = {
    +            0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
    +            4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
    +            8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
    +            12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
    +            15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
    +            18, 18, 18, 19
    +        };
    +
    +        // approximately ceil( log2( long5pow[i] ) )
    +        private static final int[] N_5_BITS = {
    +                0,
    +                3,
    +                5,
    +                7,
    +                10,
    +                12,
    +                14,
    +                17,
    +                19,
    +                21,
    +                24,
    +                26,
    +                28,
    +                31,
    +                33,
    +                35,
    +                38,
    +                40,
    +                42,
    +                45,
    +                47,
    +                49,
    +                52,
    +                54,
    +                56,
    +                59,
    +                61,
    +        };
    +
    +        private int getChars(char[] result) {
    +            assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
    +            int i = 0;
    +            if (isNegative) {
    +                result[0] = '-';
    +                i = 1;
    +            }
    +            if (decExponent > 0 && decExponent < 8) {
    +                // print digits.digits.
    +                int charLength = Math.min(nDigits, decExponent);
    +                System.arraycopy(digits, firstDigitIndex, result, i, charLength);
    +                i += charLength;
    +                if (charLength < decExponent) {
    +                    charLength = decExponent - charLength;
    +                    Arrays.fill(result,i,i+charLength,'0');
    +                    i += charLength;
    +                    result[i++] = '.';
    +                    result[i++] = '0';
    +                } else {
    +                    result[i++] = '.';
    +                    if (charLength < nDigits) {
    +                        int t = nDigits - charLength;
    +                        System.arraycopy(digits, firstDigitIndex+charLength, result, i, t);
    +                        i += t;
    +                    } else {
    +                        result[i++] = '0';
    +                    }
    +                }
    +            } else if (decExponent <= 0 && decExponent > -3) {
    +                result[i++] = '0';
    +                result[i++] = '.';
    +                if (decExponent != 0) {
    +                    Arrays.fill(result, i, i-decExponent, '0');
    +                    i -= decExponent;
    +                }
    +                System.arraycopy(digits, firstDigitIndex, result, i, nDigits);
    +                i += nDigits;
    +            } else {
    +                result[i++] = digits[firstDigitIndex];
    +                result[i++] = '.';
    +                if (nDigits > 1) {
    +                    System.arraycopy(digits, firstDigitIndex+1, result, i, nDigits - 1);
    +                    i += nDigits - 1;
    +                } else {
    +                    result[i++] = '0';
    +                }
    +                result[i++] = 'E';
    +                int e;
    +                if (decExponent <= 0) {
    +                    result[i++] = '-';
    +                    e = -decExponent + 1;
    +                } else {
    +                    e = decExponent - 1;
    +                }
    +                // decExponent has 1, 2, or 3, digits
    +                if (e <= 9) {
    +                    result[i++] = (char) (e + '0');
    +                } else if (e <= 99) {
    +                    result[i++] = (char) (e / 10 + '0');
    +                    result[i++] = (char) (e % 10 + '0');
    +                } else {
    +                    result[i++] = (char) (e / 100 + '0');
    +                    e %= 100;
    +                    result[i++] = (char) (e / 10 + '0');
    +                    result[i++] = (char) (e % 10 + '0');
    +                }
    +            }
    +            return i;
    +        }
    +
    +    }
    +
    +    private static final ThreadLocal threadLocalBinaryToASCIIBuffer =
    +            new ThreadLocal() {
    +                @Override
    +                protected BinaryToASCIIBuffer initialValue() {
    +                    return new BinaryToASCIIBuffer();
    +                }
    +            };
    +
    +    private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
    +        return threadLocalBinaryToASCIIBuffer.get();
    +    }
    +
    +    /**
    +     * A converter which can process an ASCII String representation
    +     * of a single or double precision floating point value into a
    +     * float or a double.
    +     */
    +    interface ASCIIToBinaryConverter {
    +
    +        double doubleValue();
    +
    +        float floatValue();
    +
    +    }
    +
    +    /**
    +     * A ASCIIToBinaryConverter container for a double.
    +     */
    +    static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    +        final private double doubleVal;
    +        final private float floatVal;
    +
    +        public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
    +            this.doubleVal = doubleVal;
    +            this.floatVal = floatVal;
    +        }
    +
    +        @Override
    +        public double doubleValue() {
    +            return doubleVal;
    +        }
    +
    +        @Override
    +        public float floatValue() {
    +            return floatVal;
    +        }
    +    }
    +
    +    static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
    +    static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
    +    static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER  = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
    +    static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
    +    static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
    +
    +    /**
    +     * A buffered implementation of ASCIIToBinaryConverter.
    +     */
    +    static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
    +        boolean     isNegative;
    +        int         decExponent;
    +        char        digits[];
    +        int         nDigits;
    +
    +        ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n)
    +        {
    +            this.isNegative = negSign;
    +            this.decExponent = decExponent;
    +            this.digits = digits;
    +            this.nDigits = n;
    +        }
    +
    +        /**
    +         * Takes a FloatingDecimal, which we presumably just scanned in,
    +         * and finds out what its value is, as a double.
    +         *
    +         * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
    +         * ROUNDING DIRECTION in case the result is really destined
    +         * for a single-precision float.
    +         */
    +        @Override
    +        public double doubleValue() {
    +            int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
    +            //
    +            // convert the lead kDigits to a long integer.
    +            //
    +            // (special performance hack: start to do it using int)
    +            int iValue = (int) digits[0] - (int) '0';
    +            int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
    +            for (int i = 1; i < iDigits; i++) {
    +                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    +            }
    +            long lValue = (long) iValue;
    +            for (int i = iDigits; i < kDigits; i++) {
    +                lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    +            }
    +            double dValue = (double) lValue;
    +            int exp = decExponent - kDigits;
    +            //
    +            // lValue now contains a long integer with the value of
    +            // the first kDigits digits of the number.
    +            // dValue contains the (double) of the same.
    +            //
    +
    +            if (nDigits <= MAX_DECIMAL_DIGITS) {
    +                //
    +                // possibly an easy case.
    +                // We know that the digits can be represented
    +                // exactly. And if the exponent isn't too outrageous,
    +                // the whole thing can be done with one operation,
    +                // thus one rounding error.
    +                // Note that all our constructors trim all leading and
    +                // trailing zeros, so simple values (including zero)
    +                // will always end up here
    +                //
    +                if (exp == 0 || dValue == 0.0) {
    +                    return (isNegative) ? -dValue : dValue; // small floating integer
    +                }
    +                else if (exp >= 0) {
    +                    if (exp <= MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer with one operation,
    +                        // thus one roundoff.
    +                        //
    +                        double rValue = dValue * SMALL_10_POW[exp];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    int slop = MAX_DECIMAL_DIGITS - kDigits;
    +                    if (exp <= MAX_SMALL_TEN + slop) {
    +                        //
    +                        // We can multiply dValue by 10^(slop)
    +                        // and it is still "small" and exact.
    +                        // Then we can multiply by 10^(exp-slop)
    +                        // with one rounding.
    +                        //
    +                        dValue *= SMALL_10_POW[slop];
    +                        double rValue = dValue * SMALL_10_POW[exp - slop];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a positive exp.
    +                    //
    +                } else {
    +                    if (exp >= -MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer in one division.
    +                        //
    +                        double rValue = dValue / SMALL_10_POW[-exp];
    +                        return (isNegative) ? -rValue : rValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a negative exp.
    +                    //
    +                }
    +            }
    +
    +            //
    +            // Harder cases:
    +            // The sum of digits plus exponent is greater than
    +            // what we think we can do with one error.
    +            //
    +            // Start by approximating the right answer by,
    +            // naively, scaling by powers of 10.
    +            //
    +            if (exp > 0) {
    +                if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // Infinity. Cut to the chase.
    +                    //
    +                    return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue *= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 1; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= BIG_10_POW[j];
    +                        }
    +                    }
    +                    //
    +                    // The reason for the weird exp > 1 condition
    +                    // in the above loop was so that the last multiply
    +                    // would get unrolled. We handle it here.
    +                    // It could overflow.
    +                    //
    +                    double t = dValue * BIG_10_POW[j];
    +                    if (Double.isInfinite(t)) {
    +                        //
    +                        // It did overflow.
    +                        // Look more closely at the result.
    +                        // If the exponent is just one too large,
    +                        // then use the maximum finite as our estimate
    +                        // value. Else call the result infinity
    +                        // and punt it.
    +                        // ( I presume this could happen because
    +                        // rounding forces the result here to be
    +                        // an ULP or two larger than
    +                        // Double.MAX_VALUE ).
    +                        //
    +                        t = dValue / 2.0;
    +                        t *= BIG_10_POW[j];
    +                        if (Double.isInfinite(t)) {
    +                            return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
    +                        }
    +                        t = Double.MAX_VALUE;
    +                    }
    +                    dValue = t;
    +                }
    +            } else if (exp < 0) {
    +                exp = -exp;
    +                if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // zero. Cut to the chase.
    +                    //
    +                    return (isNegative) ? -0.0 : 0.0;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue /= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 1; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= TINY_10_POW[j];
    +                        }
    +                    }
    +                    //
    +                    // The reason for the weird exp > 1 condition
    +                    // in the above loop was so that the last multiply
    +                    // would get unrolled. We handle it here.
    +                    // It could underflow.
    +                    //
    +                    double t = dValue * TINY_10_POW[j];
    +                    if (t == 0.0) {
    +                        //
    +                        // It did underflow.
    +                        // Look more closely at the result.
    +                        // If the exponent is just one too small,
    +                        // then use the minimum finite as our estimate
    +                        // value. Else call the result 0.0
    +                        // and punt it.
    +                        // ( I presume this could happen because
    +                        // rounding forces the result here to be
    +                        // an ULP or two less than
    +                        // Double.MIN_VALUE ).
    +                        //
    +                        t = dValue * 2.0;
    +                        t *= TINY_10_POW[j];
    +                        if (t == 0.0) {
    +                            return (isNegative) ? -0.0 : 0.0;
    +                        }
    +                        t = Double.MIN_VALUE;
    +                    }
    +                    dValue = t;
    +                }
    +            }
    +
    +            //
    +            // dValue is now approximately the result.
    +            // The hard part is adjusting it, by comparison
    +            // with FDBigInteger arithmetic.
    +            // Formulate the EXACT big-number result as
    +            // bigD0 * 10^exp
    +            //
    +            if (nDigits > MAX_NDIGITS) {
    +                nDigits = MAX_NDIGITS + 1;
    +                digits[MAX_NDIGITS] = '1';
    +            }
    +            FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
    +            exp = decExponent - nDigits;
    +
    +            long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
    +            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    +            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    +            bigD0 = bigD0.multByPow52(D5, 0);
    +            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    +            FDBigInteger bigD = null;
    +            int prevD2 = 0;
    +
    +            correctionLoop:
    +            while (true) {
    +                // here ieeeBits can't be NaN, Infinity or zero
    +                int binexp = (int) (ieeeBits >>> EXP_SHIFT);
    +                long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
    +                if (binexp > 0) {
    +                    bigBbits |= FRACT_HOB;
    +                } else { // Normalize denormalized numbers.
    +                    assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
    +                    int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
    +                    int shift = leadingZeros - (63 - EXP_SHIFT);
    +                    bigBbits <<= shift;
    +                    binexp = 1 - shift;
    +                }
    +                binexp -= DoubleConsts.EXP_BIAS;
    +                int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
    +                bigBbits >>>= lowOrderZeros;
    +                final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
    +                final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
    +
    +                //
    +                // Scale bigD, bigB appropriately for
    +                // big-integer operations.
    +                // Naively, we multiply by powers of ten
    +                // and powers of two. What we actually do
    +                // is keep track of the powers of 5 and
    +                // powers of 2 we would use, then factor out
    +                // common divisors before doing the work.
    +                //
    +                int B2 = B5; // powers of 2 in bigB
    +                int D2 = D5; // powers of 2 in bigD
    +                int Ulp2;   // powers of 2 in halfUlp.
    +                if (bigIntExp >= 0) {
    +                    B2 += bigIntExp;
    +                } else {
    +                    D2 -= bigIntExp;
    +                }
    +                Ulp2 = B2;
    +                // shift bigB and bigD left by a number s. t.
    +                // halfUlp is still an integer.
    +                int hulpbias;
    +                if (binexp <= -DoubleConsts.EXP_BIAS) {
    +                    // This is going to be a denormalized number
    +                    // (if not actually zero).
    +                    // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
    +                    hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
    +                } else {
    +                    hulpbias = 1 + lowOrderZeros;
    +                }
    +                B2 += hulpbias;
    +                D2 += hulpbias;
    +                // if there are common factors of 2, we might just as well
    +                // factor them out, as they add nothing useful.
    +                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    +                B2 -= common2;
    +                D2 -= common2;
    +                Ulp2 -= common2;
    +                // do multiplications by powers of 5 and 2
    +                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    +                if (bigD == null || prevD2 != D2) {
    +                    bigD = bigD0.leftShift(D2);
    +                    prevD2 = D2;
    +                }
    +                //
    +                // to recap:
    +                // bigB is the scaled-big-int version of our floating-point
    +                // candidate.
    +                // bigD is the scaled-big-int version of the exact value
    +                // as we understand it.
    +                // halfUlp is 1/2 an ulp of bigB, except for special cases
    +                // of exact powers of 2
    +                //
    +                // the plan is to compare bigB with bigD, and if the difference
    +                // is less than halfUlp, then we're satisfied. Otherwise,
    +                // use the ratio of difference to halfUlp to calculate a fudge
    +                // factor to add to the floating value, then go 'round again.
    +                //
    +                FDBigInteger diff;
    +                int cmpResult;
    +                boolean overvalue;
    +                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    +                    overvalue = true; // our candidate is too big.
    +                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    +                    if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
    +                        // candidate is a normalized exact power of 2 and
    +                        // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
    +                        // For our purposes, ulp is the ulp of the
    +                        // next smaller range.
    +                        Ulp2 -= 1;
    +                        if (Ulp2 < 0) {
    +                            // rats. Cannot de-scale ulp this far.
    +                            // must scale diff in other direction.
    +                            Ulp2 = 0;
    +                            diff = diff.leftShift(1);
    +                        }
    +                    }
    +                } else if (cmpResult < 0) {
    +                    overvalue = false; // our candidate is too small.
    +                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    +                } else {
    +                    // the candidate is exactly right!
    +                    // this happens with surprising frequency
    +                    break correctionLoop;
    +                }
    +                cmpResult = diff.cmpPow52(B5, Ulp2);
    +                if ((cmpResult) < 0) {
    +                    // difference is small.
    +                    // this is close enough
    +                    break correctionLoop;
    +                } else if (cmpResult == 0) {
    +                    // difference is exactly half an ULP
    +                    // round to some other value maybe, then finish
    +                    if ((ieeeBits & 1) != 0) { // half ties to even
    +                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    }
    +                    break correctionLoop;
    +                } else {
    +                    // difference is non-trivial.
    +                    // could scale addend by ratio of difference to
    +                    // halfUlp here, if we bothered to compute that difference.
    +                    // Most of the time ( I hope ) it is about 1 anyway.
    +                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
    +                        break correctionLoop; // oops. Fell off end of range.
    +                    }
    +                    continue; // try again.
    +                }
    +
    +            }
    +            if (isNegative) {
    +                ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
    +            }
    +            return Double.longBitsToDouble(ieeeBits);
    +        }
    +
    +        /**
    +         * Takes a FloatingDecimal, which we presumably just scanned in,
    +         * and finds out what its value is, as a float.
    +         * This is distinct from doubleValue() to avoid the extremely
    +         * unlikely case of a double rounding error, wherein the conversion
    +         * to double has one rounding error, and the conversion of that double
    +         * to a float has another rounding error, IN THE WRONG DIRECTION,
    +         * ( because of the preference to a zero low-order bit ).
    +         */
    +        @Override
    +        public float floatValue() {
    +            int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
    +            //
    +            // convert the lead kDigits to an integer.
    +            //
    +            int iValue = (int) digits[0] - (int) '0';
    +            for (int i = 1; i < kDigits; i++) {
    +                iValue = iValue * 10 + (int) digits[i] - (int) '0';
    +            }
    +            float fValue = (float) iValue;
    +            int exp = decExponent - kDigits;
    +            //
    +            // iValue now contains an integer with the value of
    +            // the first kDigits digits of the number.
    +            // fValue contains the (float) of the same.
    +            //
    +
    +            if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
    +                //
    +                // possibly an easy case.
    +                // We know that the digits can be represented
    +                // exactly. And if the exponent isn't too outrageous,
    +                // the whole thing can be done with one operation,
    +                // thus one rounding error.
    +                // Note that all our constructors trim all leading and
    +                // trailing zeros, so simple values (including zero)
    +                // will always end up here.
    +                //
    +                if (exp == 0 || fValue == 0.0f) {
    +                    return (isNegative) ? -fValue : fValue; // small floating integer
    +                } else if (exp >= 0) {
    +                    if (exp <= SINGLE_MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer with one operation,
    +                        // thus one roundoff.
    +                        //
    +                        fValue *= SINGLE_SMALL_10_POW[exp];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
    +                    if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
    +                        //
    +                        // We can multiply fValue by 10^(slop)
    +                        // and it is still "small" and exact.
    +                        // Then we can multiply by 10^(exp-slop)
    +                        // with one rounding.
    +                        //
    +                        fValue *= SINGLE_SMALL_10_POW[slop];
    +                        fValue *= SINGLE_SMALL_10_POW[exp - slop];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a positive exp.
    +                    //
    +                } else {
    +                    if (exp >= -SINGLE_MAX_SMALL_TEN) {
    +                        //
    +                        // Can get the answer in one division.
    +                        //
    +                        fValue /= SINGLE_SMALL_10_POW[-exp];
    +                        return (isNegative) ? -fValue : fValue;
    +                    }
    +                    //
    +                    // Else we have a hard case with a negative exp.
    +                    //
    +                }
    +            } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
    +                //
    +                // In double-precision, this is an exact floating integer.
    +                // So we can compute to double, then shorten to float
    +                // with one round, and get the right answer.
    +                //
    +                // First, finish accumulating digits.
    +                // Then convert that integer to a double, multiply
    +                // by the appropriate power of ten, and convert to float.
    +                //
    +                long lValue = (long) iValue;
    +                for (int i = kDigits; i < nDigits; i++) {
    +                    lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
    +                }
    +                double dValue = (double) lValue;
    +                exp = decExponent - nDigits;
    +                dValue *= SMALL_10_POW[exp];
    +                fValue = (float) dValue;
    +                return (isNegative) ? -fValue : fValue;
    +
    +            }
    +            //
    +            // Harder cases:
    +            // The sum of digits plus exponent is greater than
    +            // what we think we can do with one error.
    +            //
    +            // Start by approximating the right answer by,
    +            // naively, scaling by powers of 10.
    +            // Scaling uses doubles to avoid overflow/underflow.
    +            //
    +            double dValue = fValue;
    +            if (exp > 0) {
    +                if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // Infinity. Cut to the chase.
    +                    //
    +                    return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue *= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 0; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= BIG_10_POW[j];
    +                        }
    +                    }
    +                }
    +            } else if (exp < 0) {
    +                exp = -exp;
    +                if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
    +                    //
    +                    // Lets face it. This is going to be
    +                    // zero. Cut to the chase.
    +                    //
    +                    return (isNegative) ? -0.0f : 0.0f;
    +                }
    +                if ((exp & 15) != 0) {
    +                    dValue /= SMALL_10_POW[exp & 15];
    +                }
    +                if ((exp >>= 4) != 0) {
    +                    int j;
    +                    for (j = 0; exp > 0; j++, exp >>= 1) {
    +                        if ((exp & 1) != 0) {
    +                            dValue *= TINY_10_POW[j];
    +                        }
    +                    }
    +                }
    +            }
    +            fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));
    +
    +            //
    +            // fValue is now approximately the result.
    +            // The hard part is adjusting it, by comparison
    +            // with FDBigInteger arithmetic.
    +            // Formulate the EXACT big-number result as
    +            // bigD0 * 10^exp
    +            //
    +            if (nDigits > SINGLE_MAX_NDIGITS) {
    +                nDigits = SINGLE_MAX_NDIGITS + 1;
    +                digits[SINGLE_MAX_NDIGITS] = '1';
    +            }
    +            FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
    +            exp = decExponent - nDigits;
    +
    +            int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
    +            final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
    +            final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
    +            bigD0 = bigD0.multByPow52(D5, 0);
    +            bigD0.makeImmutable();   // prevent bigD0 modification inside correctionLoop
    +            FDBigInteger bigD = null;
    +            int prevD2 = 0;
    +
    +            correctionLoop:
    +            while (true) {
    +                // here ieeeBits can't be NaN, Infinity or zero
    +                int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
    +                int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
    +                if (binexp > 0) {
    +                    bigBbits |= SINGLE_FRACT_HOB;
    +                } else { // Normalize denormalized numbers.
    +                    assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
    +                    int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
    +                    int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
    +                    bigBbits <<= shift;
    +                    binexp = 1 - shift;
    +                }
    +                binexp -= FloatConsts.EXP_BIAS;
    +                int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
    +                bigBbits >>>= lowOrderZeros;
    +                final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
    +                final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
    +
    +                //
    +                // Scale bigD, bigB appropriately for
    +                // big-integer operations.
    +                // Naively, we multiply by powers of ten
    +                // and powers of two. What we actually do
    +                // is keep track of the powers of 5 and
    +                // powers of 2 we would use, then factor out
    +                // common divisors before doing the work.
    +                //
    +                int B2 = B5; // powers of 2 in bigB
    +                int D2 = D5; // powers of 2 in bigD
    +                int Ulp2;   // powers of 2 in halfUlp.
    +                if (bigIntExp >= 0) {
    +                    B2 += bigIntExp;
    +                } else {
    +                    D2 -= bigIntExp;
    +                }
    +                Ulp2 = B2;
    +                // shift bigB and bigD left by a number s. t.
    +                // halfUlp is still an integer.
    +                int hulpbias;
    +                if (binexp <= -FloatConsts.EXP_BIAS) {
    +                    // This is going to be a denormalized number
    +                    // (if not actually zero).
    +                    // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
    +                    hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
    +                } else {
    +                    hulpbias = 1 + lowOrderZeros;
    +                }
    +                B2 += hulpbias;
    +                D2 += hulpbias;
    +                // if there are common factors of 2, we might just as well
    +                // factor them out, as they add nothing useful.
    +                int common2 = Math.min(B2, Math.min(D2, Ulp2));
    +                B2 -= common2;
    +                D2 -= common2;
    +                Ulp2 -= common2;
    +                // do multiplications by powers of 5 and 2
    +                FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
    +                if (bigD == null || prevD2 != D2) {
    +                    bigD = bigD0.leftShift(D2);
    +                    prevD2 = D2;
    +                }
    +                //
    +                // to recap:
    +                // bigB is the scaled-big-int version of our floating-point
    +                // candidate.
    +                // bigD is the scaled-big-int version of the exact value
    +                // as we understand it.
    +                // halfUlp is 1/2 an ulp of bigB, except for special cases
    +                // of exact powers of 2
    +                //
    +                // the plan is to compare bigB with bigD, and if the difference
    +                // is less than halfUlp, then we're satisfied. Otherwise,
    +                // use the ratio of difference to halfUlp to calculate a fudge
    +                // factor to add to the floating value, then go 'round again.
    +                //
    +                FDBigInteger diff;
    +                int cmpResult;
    +                boolean overvalue;
    +                if ((cmpResult = bigB.cmp(bigD)) > 0) {
    +                    overvalue = true; // our candidate is too big.
    +                    diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
    +                    if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
    +                        // candidate is a normalized exact power of 2 and
    +                        // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
    +                        // For our purposes, ulp is the ulp of the
    +                        // next smaller range.
    +                        Ulp2 -= 1;
    +                        if (Ulp2 < 0) {
    +                            // rats. Cannot de-scale ulp this far.
    +                            // must scale diff in other direction.
    +                            Ulp2 = 0;
    +                            diff = diff.leftShift(1);
    +                        }
    +                    }
    +                } else if (cmpResult < 0) {
    +                    overvalue = false; // our candidate is too small.
    +                    diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
    +                } else {
    +                    // the candidate is exactly right!
    +                    // this happens with surprising frequency
    +                    break correctionLoop;
    +                }
    +                cmpResult = diff.cmpPow52(B5, Ulp2);
    +                if ((cmpResult) < 0) {
    +                    // difference is small.
    +                    // this is close enough
    +                    break correctionLoop;
    +                } else if (cmpResult == 0) {
    +                    // difference is exactly half an ULP
    +                    // round to some other value maybe, then finish
    +                    if ((ieeeBits & 1) != 0) { // half ties to even
    +                        ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    }
    +                    break correctionLoop;
    +                } else {
    +                    // difference is non-trivial.
    +                    // could scale addend by ratio of difference to
    +                    // halfUlp here, if we bothered to compute that difference.
    +                    // Most of the time ( I hope ) it is about 1 anyway.
    +                    ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
    +                    if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
    +                        break correctionLoop; // oops. Fell off end of range.
    +                    }
    +                    continue; // try again.
    +                }
    +
    +            }
    +            if (isNegative) {
    +                ieeeBits |= FloatConsts.SIGN_BIT_MASK;
    +            }
    +            return Float.intBitsToFloat(ieeeBits);
    +        }
    +
    +
    +        /**
    +         * All the positive powers of 10 that can be
    +         * represented exactly in double/float.
    +         */
    +        private static final double[] SMALL_10_POW = {
    +            1.0e0,
    +            1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
    +            1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
    +            1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
    +            1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
    +            1.0e21, 1.0e22
    +        };
    +
    +        private static final float[] SINGLE_SMALL_10_POW = {
    +            1.0e0f,
    +            1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
    +            1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
    +        };
    +
    +        private static final double[] BIG_10_POW = {
    +            1e16, 1e32, 1e64, 1e128, 1e256 };
    +        private static final double[] TINY_10_POW = {
    +            1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
    +
    +        private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
    +        private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
    +
    +    }
    +
    +    /**
    +     * Returns a BinaryToASCIIConverter for a double.
    +     * The returned object is a ThreadLocal variable of this class.
    +     *
    +     * @param d The double precision value to convert.
    +     * @return The converter.
    +     */
    +    public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
    +        return getBinaryToASCIIConverter(d, true);
    +    }
    +
    +    /**
    +     * Returns a BinaryToASCIIConverter for a double.
    +     * The returned object is a ThreadLocal variable of this class.
    +     *
    +     * @param d The double precision value to convert.
    +     * @param isCompatibleFormat
    +     * @return The converter.
    +     */
    +    static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
    +        long dBits = Double.doubleToRawLongBits(d);
    +        boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
    +        long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
    +        int  binExp = (int)( (dBits&DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT );
    +        // Discover obvious special cases of NaN and Infinity.
    +        if ( binExp == (int)(DoubleConsts.EXP_BIT_MASK>>EXP_SHIFT) ) {
    +            if ( fractBits == 0L ){
    +                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    +            } else {
    +                return B2AC_NOT_A_NUMBER;
    +            }
    +        }
    +        // Finish unpacking
    +        // Normalize denormalized numbers.
    +        // Insert assumed high-order bit for normalized numbers.
    +        // Subtract exponent bias.
    +        int  nSignificantBits;
    +        if ( binExp == 0 ){
    +            if ( fractBits == 0L ){
    +                // not a denorm, just a 0!
    +                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    +            }
    +            int leadingZeros = Long.numberOfLeadingZeros(fractBits);
    +            int shift = leadingZeros-(63-EXP_SHIFT);
    +            fractBits <<= shift;
    +            binExp = 1 - shift;
    +            nSignificantBits =  64-leadingZeros; // recall binExp is  - shift count.
    +        } else {
    +            fractBits |= FRACT_HOB;
    +            nSignificantBits = EXP_SHIFT+1;
    +        }
    +        binExp -= DoubleConsts.EXP_BIAS;
    +        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    +        buf.setSign(isNegative);
    +        // call the routine that actually does all the hard work.
    +        buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
    +        return buf;
    +    }
    +
    +    private static BinaryToASCIIConverter getBinaryToASCIIConverter(float f) {
    +        int fBits = Float.floatToRawIntBits( f );
    +        boolean isNegative = (fBits&FloatConsts.SIGN_BIT_MASK) != 0;
    +        int fractBits = fBits&FloatConsts.SIGNIF_BIT_MASK;
    +        int binExp = (fBits&FloatConsts.EXP_BIT_MASK) >> SINGLE_EXP_SHIFT;
    +        // Discover obvious special cases of NaN and Infinity.
    +        if ( binExp == (FloatConsts.EXP_BIT_MASK>>SINGLE_EXP_SHIFT) ) {
    +            if ( fractBits == 0L ){
    +                return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
    +            } else {
    +                return B2AC_NOT_A_NUMBER;
    +            }
    +        }
    +        // Finish unpacking
    +        // Normalize denormalized numbers.
    +        // Insert assumed high-order bit for normalized numbers.
    +        // Subtract exponent bias.
    +        int  nSignificantBits;
    +        if ( binExp == 0 ){
    +            if ( fractBits == 0 ){
    +                // not a denorm, just a 0!
    +                return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
    +            }
    +            int leadingZeros = Integer.numberOfLeadingZeros(fractBits);
    +            int shift = leadingZeros-(31-SINGLE_EXP_SHIFT);
    +            fractBits <<= shift;
    +            binExp = 1 - shift;
    +            nSignificantBits =  32 - leadingZeros; // recall binExp is  - shift count.
    +        } else {
    +            fractBits |= SINGLE_FRACT_HOB;
    +            nSignificantBits = SINGLE_EXP_SHIFT+1;
    +        }
    +        binExp -= FloatConsts.EXP_BIAS;
    +        BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
    +        buf.setSign(isNegative);
    +        // call the routine that actually does all the hard work.
    +        buf.dtoa(binExp, ((long)fractBits)<<(EXP_SHIFT-SINGLE_EXP_SHIFT), nSignificantBits, true);
    +        return buf;
    +    }
    +
    +    @SuppressWarnings("fallthrough")
    +    static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
    +        boolean isNegative = false;
    +        boolean signSeen   = false;
    +        int     decExp;
    +        char    c;
    +
    +    parseNumber:
    +        try{
    +            in = in.trim(); // don't fool around with white space.
    +                            // throws NullPointerException if null
    +            int len = in.length();
    +            if ( len == 0 ) {
    +                throw new NumberFormatException("empty String");
    +            }
    +            int i = 0;
    +            switch (in.charAt(i)){
    +            case '-':
    +                isNegative = true;
    +                //FALLTHROUGH
    +            case '+':
    +                i++;
    +                signSeen = true;
    +            }
    +            c = in.charAt(i);
    +            if(c == 'N') { // Check for NaN
    +                if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
    +                    return A2BC_NOT_A_NUMBER;
    +                }
    +                // something went wrong, throw exception
    +                break parseNumber;
    +            } else if(c == 'I') { // Check for Infinity strings
    +                if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
    +                    return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    +                }
    +                // something went wrong, throw exception
    +                break parseNumber;
    +            } else if (c == '0')  { // check for hexadecimal floating-point number
    +                if (len > i+1 ) {
    +                    char ch = in.charAt(i+1);
    +                    if (ch == 'x' || ch == 'X' ) { // possible hex string
    +                        return parseHexString(in);
    +                    }
    +                }
    +            }  // look for and process decimal floating-point string
    +
    +            char[] digits = new char[ len ];
    +            int    nDigits= 0;
    +            boolean decSeen = false;
    +            int decPt = 0;
    +            int nLeadZero = 0;
    +            int nTrailZero= 0;
    +
    +        skipLeadingZerosLoop:
    +            while (i < len) {
    +                c = in.charAt(i);
    +                if (c == '0') {
    +                    nLeadZero++;
    +                } else if (c == '.') {
    +                    if (decSeen) {
    +                        // already saw one ., this is the 2nd.
    +                        throw new NumberFormatException("multiple points");
    +                    }
    +                    decPt = i;
    +                    if (signSeen) {
    +                        decPt -= 1;
    +                    }
    +                    decSeen = true;
    +                } else {
    +                    break skipLeadingZerosLoop;
    +                }
    +                i++;
    +            }
    +        digitLoop:
    +            while (i < len) {
    +                c = in.charAt(i);
    +                if (c >= '1' && c <= '9') {
    +                    digits[nDigits++] = c;
    +                    nTrailZero = 0;
    +                } else if (c == '0') {
    +                    digits[nDigits++] = c;
    +                    nTrailZero++;
    +                } else if (c == '.') {
    +                    if (decSeen) {
    +                        // already saw one ., this is the 2nd.
    +                        throw new NumberFormatException("multiple points");
    +                    }
    +                    decPt = i;
    +                    if (signSeen) {
    +                        decPt -= 1;
    +                    }
    +                    decSeen = true;
    +                } else {
    +                    break digitLoop;
    +                }
    +                i++;
    +            }
    +            nDigits -=nTrailZero;
    +            //
    +            // At this point, we've scanned all the digits and decimal
    +            // point we're going to see. Trim off leading and trailing
    +            // zeros, which will just confuse us later, and adjust
    +            // our initial decimal exponent accordingly.
    +            // To review:
    +            // we have seen i total characters.
    +            // nLeadZero of them were zeros before any other digits.
    +            // nTrailZero of them were zeros after any other digits.
    +            // if ( decSeen ), then a . was seen after decPt characters
    +            // ( including leading zeros which have been discarded )
    +            // nDigits characters were neither lead nor trailing
    +            // zeros, nor point
    +            //
    +            //
    +            // special hack: if we saw no non-zero digits, then the
    +            // answer is zero!
    +            // Unfortunately, we feel honor-bound to keep parsing!
    +            //
    +            boolean isZero = (nDigits == 0);
    +            if ( isZero &&  nLeadZero == 0 ){
    +                // we saw NO DIGITS AT ALL,
    +                // not even a crummy 0!
    +                // this is not allowed.
    +                break parseNumber; // go throw exception
    +            }
    +            //
    +            // Our initial exponent is decPt, adjusted by the number of
    +            // discarded zeros. Or, if there was no decPt,
    +            // then its just nDigits adjusted by discarded trailing zeros.
    +            //
    +            if ( decSeen ){
    +                decExp = decPt - nLeadZero;
    +            } else {
    +                decExp = nDigits + nTrailZero;
    +            }
    +
    +            //
    +            // Look for 'e' or 'E' and an optionally signed integer.
    +            //
    +            if ( (i < len) &&  (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
    +                int expSign = 1;
    +                int expVal  = 0;
    +                int reallyBig = Integer.MAX_VALUE / 10;
    +                boolean expOverflow = false;
    +                switch( in.charAt(++i) ){
    +                case '-':
    +                    expSign = -1;
    +                    //FALLTHROUGH
    +                case '+':
    +                    i++;
    +                }
    +                int expAt = i;
    +            expLoop:
    +                while ( i < len  ){
    +                    if ( expVal >= reallyBig ){
    +                        // the next character will cause integer
    +                        // overflow.
    +                        expOverflow = true;
    +                    }
    +                    c = in.charAt(i++);
    +                    if(c>='0' && c<='9') {
    +                        expVal = expVal*10 + ( (int)c - (int)'0' );
    +                    } else {
    +                        i--;           // back up.
    +                        break expLoop; // stop parsing exponent.
    +                    }
    +                }
    +                int expLimit = BIG_DECIMAL_EXPONENT+nDigits+nTrailZero;
    +                if ( expOverflow || ( expVal > expLimit ) ){
    +                    //
    +                    // The intent here is to end up with
    +                    // infinity or zero, as appropriate.
    +                    // The reason for yielding such a small decExponent,
    +                    // rather than something intuitive such as
    +                    // expSign*Integer.MAX_VALUE, is that this value
    +                    // is subject to further manipulation in
    +                    // doubleValue() and floatValue(), and I don't want
    +                    // it to be able to cause overflow there!
    +                    // (The only way we can get into trouble here is for
    +                    // really outrageous nDigits+nTrailZero, such as 2 billion. )
    +                    //
    +                    decExp = expSign*expLimit;
    +                } else {
    +                    // this should not overflow, since we tested
    +                    // for expVal > (MAX+N), where N >= abs(decExp)
    +                    decExp = decExp + expSign*expVal;
    +                }
    +
    +                // if we saw something not a digit ( or end of string )
    +                // after the [Ee][+-], without seeing any digits at all
    +                // this is certainly an error. If we saw some digits,
    +                // but then some trailing garbage, that might be ok.
    +                // so we just fall through in that case.
    +                // HUMBUG
    +                if ( i == expAt ) {
    +                    break parseNumber; // certainly bad
    +                }
    +            }
    +            //
    +            // We parsed everything we could.
    +            // If there are leftovers, then this is not good input!
    +            //
    +            if ( i < len &&
    +                ((i != len - 1) ||
    +                (in.charAt(i) != 'f' &&
    +                 in.charAt(i) != 'F' &&
    +                 in.charAt(i) != 'd' &&
    +                 in.charAt(i) != 'D'))) {
    +                break parseNumber; // go throw exception
    +            }
    +            if(isZero) {
    +                return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +            }
    +            return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
    +        } catch ( StringIndexOutOfBoundsException e ){ }
    +        throw new NumberFormatException("For input string: \"" + in + "\"");
    +    }
    +
    +    private static class HexFloatPattern {
    +        /**
    +         * Grammar is compatible with hexadecimal floating-point constants
    +         * described in section 6.4.4.2 of the C99 specification.
    +         */
    +        private static final Pattern VALUE = Pattern.compile(
    +                   //1           234                   56                7                   8      9
    +                    "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
    +                    );
    +    }
    +
    +    /**
    +     * Converts string s to a suitable floating decimal; uses the
    +     * double constructor and sets the roundDir variable appropriately
    +     * in case the value is later converted to a float.
    +     *
    +     * @param s The String to parse.
    +     */
    +   static ASCIIToBinaryConverter parseHexString(String s) {
    +            // Verify string is a member of the hexadecimal floating-point
    +            // string language.
    +            Matcher m = HexFloatPattern.VALUE.matcher(s);
    +            boolean validInput = m.matches();
    +            if (!validInput) {
    +                // Input does not match pattern
    +                throw new NumberFormatException("For input string: \"" + s + "\"");
    +            } else { // validInput
    +                //
    +                // We must isolate the sign, significand, and exponent
    +                // fields.  The sign value is straightforward.  Since
    +                // floating-point numbers are stored with a normalized
    +                // representation, the significand and exponent are
    +                // interrelated.
    +                //
    +                // After extracting the sign, we normalized the
    +                // significand as a hexadecimal value, calculating an
    +                // exponent adjust for any shifts made during
    +                // normalization.  If the significand is zero, the
    +                // exponent doesn't need to be examined since the output
    +                // will be zero.
    +                //
    +                // Next the exponent in the input string is extracted.
    +                // Afterwards, the significand is normalized as a *binary*
    +                // value and the input value's normalized exponent can be
    +                // computed.  The significand bits are copied into a
    +                // double significand; if the string has more logical bits
    +                // than can fit in a double, the extra bits affect the
    +                // round and sticky bits which are used to round the final
    +                // value.
    +                //
    +                //  Extract significand sign
    +                String group1 = m.group(1);
    +                boolean isNegative = ((group1 != null) && group1.equals("-"));
    +
    +                //  Extract Significand magnitude
    +                //
    +                // Based on the form of the significand, calculate how the
    +                // binary exponent needs to be adjusted to create a
    +                // normalized//hexadecimal* floating-point number; that
    +                // is, a number where there is one nonzero hex digit to
    +                // the left of the (hexa)decimal point.  Since we are
    +                // adjusting a binary, not hexadecimal exponent, the
    +                // exponent is adjusted by a multiple of 4.
    +                //
    +                // There are a number of significand scenarios to consider;
    +                // letters are used in indicate nonzero digits:
    +                //
    +                // 1. 000xxxx       =>      x.xxx   normalized
    +                //    increase exponent by (number of x's - 1)*4
    +                //
    +                // 2. 000xxx.yyyy =>        x.xxyyyy        normalized
    +                //    increase exponent by (number of x's - 1)*4
    +                //
    +                // 3. .000yyy  =>   y.yy    normalized
    +                //    decrease exponent by (number of zeros + 1)*4
    +                //
    +                // 4. 000.00000yyy => y.yy normalized
    +                //    decrease exponent by (number of zeros to right of point + 1)*4
    +                //
    +                // If the significand is exactly zero, return a properly
    +                // signed zero.
    +                //
    +
    +                String significandString = null;
    +                int signifLength = 0;
    +                int exponentAdjust = 0;
    +                {
    +                    int leftDigits = 0; // number of meaningful digits to
    +                    // left of "decimal" point
    +                    // (leading zeros stripped)
    +                    int rightDigits = 0; // number of digits to right of
    +                    // "decimal" point; leading zeros
    +                    // must always be accounted for
    +                    //
    +                    // The significand is made up of either
    +                    //
    +                    // 1. group 4 entirely (integer portion only)
    +                    //
    +                    // OR
    +                    //
    +                    // 2. the fractional portion from group 7 plus any
    +                    // (optional) integer portions from group 6.
    +                    //
    +                    String group4;
    +                    if ((group4 = m.group(4)) != null) {  // Integer-only significand
    +                        // Leading zeros never matter on the integer portion
    +                        significandString = stripLeadingZeros(group4);
    +                        leftDigits = significandString.length();
    +                    } else {
    +                        // Group 6 is the optional integer; leading zeros
    +                        // never matter on the integer portion
    +                        String group6 = stripLeadingZeros(m.group(6));
    +                        leftDigits = group6.length();
    +
    +                        // fraction
    +                        String group7 = m.group(7);
    +                        rightDigits = group7.length();
    +
    +                        // Turn "integer.fraction" into "integer"+"fraction"
    +                        significandString =
    +                                ((group6 == null) ? "" : group6) + // is the null
    +                                        // check necessary?
    +                                        group7;
    +                    }
    +
    +                    significandString = stripLeadingZeros(significandString);
    +                    signifLength = significandString.length();
    +
    +                    //
    +                    // Adjust exponent as described above
    +                    //
    +                    if (leftDigits >= 1) {  // Cases 1 and 2
    +                        exponentAdjust = 4 * (leftDigits - 1);
    +                    } else {                // Cases 3 and 4
    +                        exponentAdjust = -4 * (rightDigits - signifLength + 1);
    +                    }
    +
    +                    // If the significand is zero, the exponent doesn't
    +                    // matter; return a properly signed zero.
    +
    +                    if (signifLength == 0) { // Only zeros in input
    +                        return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +                    }
    +                }
    +
    +                //  Extract Exponent
    +                //
    +                // Use an int to read in the exponent value; this should
    +                // provide more than sufficient range for non-contrived
    +                // inputs.  If reading the exponent in as an int does
    +                // overflow, examine the sign of the exponent and
    +                // significand to determine what to do.
    +                //
    +                String group8 = m.group(8);
    +                boolean positiveExponent = (group8 == null) || group8.equals("+");
    +                long unsignedRawExponent;
    +                try {
    +                    unsignedRawExponent = Integer.parseInt(m.group(9));
    +                }
    +                catch (NumberFormatException e) {
    +                    // At this point, we know the exponent is
    +                    // syntactically well-formed as a sequence of
    +                    // digits.  Therefore, if an NumberFormatException
    +                    // is thrown, it must be due to overflowing int's
    +                    // range.  Also, at this point, we have already
    +                    // checked for a zero significand.  Thus the signs
    +                    // of the exponent and significand determine the
    +                    // final result:
    +                    //
    +                    //                      significand
    +                    //                      +               -
    +                    // exponent     +       +infinity       -infinity
    +                    //              -       +0.0            -0.0
    +                    return isNegative ?
    +                              (positiveExponent ? A2BC_NEGATIVE_INFINITY : A2BC_NEGATIVE_ZERO)
    +                            : (positiveExponent ? A2BC_POSITIVE_INFINITY : A2BC_POSITIVE_ZERO);
    +
    +                }
    +
    +                long rawExponent =
    +                        (positiveExponent ? 1L : -1L) * // exponent sign
    +                                unsignedRawExponent;            // exponent magnitude
    +
    +                // Calculate partially adjusted exponent
    +                long exponent = rawExponent + exponentAdjust;
    +
    +                // Starting copying non-zero bits into proper position in
    +                // a long; copy explicit bit too; this will be masked
    +                // later for normal values.
    +
    +                boolean round = false;
    +                boolean sticky = false;
    +                int nextShift = 0;
    +                long significand = 0L;
    +                // First iteration is different, since we only copy
    +                // from the leading significand bit; one more exponent
    +                // adjust will be needed...
    +
    +                // IMPORTANT: make leadingDigit a long to avoid
    +                // surprising shift semantics!
    +                long leadingDigit = getHexDigit(significandString, 0);
    +
    +                //
    +                // Left shift the leading digit (53 - (bit position of
    +                // leading 1 in digit)); this sets the top bit of the
    +                // significand to 1.  The nextShift value is adjusted
    +                // to take into account the number of bit positions of
    +                // the leadingDigit actually used.  Finally, the
    +                // exponent is adjusted to normalize the significand
    +                // as a binary value, not just a hex value.
    +                //
    +                if (leadingDigit == 1) {
    +                    significand |= leadingDigit << 52;
    +                    nextShift = 52 - 4;
    +                    // exponent += 0
    +                } else if (leadingDigit <= 3) { // [2, 3]
    +                    significand |= leadingDigit << 51;
    +                    nextShift = 52 - 5;
    +                    exponent += 1;
    +                } else if (leadingDigit <= 7) { // [4, 7]
    +                    significand |= leadingDigit << 50;
    +                    nextShift = 52 - 6;
    +                    exponent += 2;
    +                } else if (leadingDigit <= 15) { // [8, f]
    +                    significand |= leadingDigit << 49;
    +                    nextShift = 52 - 7;
    +                    exponent += 3;
    +                } else {
    +                    throw new AssertionError("Result from digit conversion too large!");
    +                }
    +                // The preceding if-else could be replaced by a single
    +                // code block based on the high-order bit set in
    +                // leadingDigit.  Given leadingOnePosition,
    +
    +                // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
    +                // nextShift = 52 - (3 + leadingOnePosition);
    +                // exponent += (leadingOnePosition-1);
    +
    +                //
    +                // Now the exponent variable is equal to the normalized
    +                // binary exponent.  Code below will make representation
    +                // adjustments if the exponent is incremented after
    +                // rounding (includes overflows to infinity) or if the
    +                // result is subnormal.
    +                //
    +
    +                // Copy digit into significand until the significand can't
    +                // hold another full hex digit or there are no more input
    +                // hex digits.
    +                int i = 0;
    +                for (i = 1;
    +                     i < signifLength && nextShift >= 0;
    +                     i++) {
    +                    long currentDigit = getHexDigit(significandString, i);
    +                    significand |= (currentDigit << nextShift);
    +                    nextShift -= 4;
    +                }
    +
    +                // After the above loop, the bulk of the string is copied.
    +                // Now, we must copy any partial hex digits into the
    +                // significand AND compute the round bit and start computing
    +                // sticky bit.
    +
    +                if (i < signifLength) { // at least one hex input digit exists
    +                    long currentDigit = getHexDigit(significandString, i);
    +
    +                    // from nextShift, figure out how many bits need
    +                    // to be copied, if any
    +                    switch (nextShift) { // must be negative
    +                        case -1:
    +                            // three bits need to be copied in; can
    +                            // set round bit
    +                            significand |= ((currentDigit & 0xEL) >> 1);
    +                            round = (currentDigit & 0x1L) != 0L;
    +                            break;
    +
    +                        case -2:
    +                            // two bits need to be copied in; can
    +                            // set round and start sticky
    +                            significand |= ((currentDigit & 0xCL) >> 2);
    +                            round = (currentDigit & 0x2L) != 0L;
    +                            sticky = (currentDigit & 0x1L) != 0;
    +                            break;
    +
    +                        case -3:
    +                            // one bit needs to be copied in
    +                            significand |= ((currentDigit & 0x8L) >> 3);
    +                            // Now set round and start sticky, if possible
    +                            round = (currentDigit & 0x4L) != 0L;
    +                            sticky = (currentDigit & 0x3L) != 0;
    +                            break;
    +
    +                        case -4:
    +                            // all bits copied into significand; set
    +                            // round and start sticky
    +                            round = ((currentDigit & 0x8L) != 0);  // is top bit set?
    +                            // nonzeros in three low order bits?
    +                            sticky = (currentDigit & 0x7L) != 0;
    +                            break;
    +
    +                        default:
    +                            throw new AssertionError("Unexpected shift distance remainder.");
    +                            // break;
    +                    }
    +
    +                    // Round is set; sticky might be set.
    +
    +                    // For the sticky bit, it suffices to check the
    +                    // current digit and test for any nonzero digits in
    +                    // the remaining unprocessed input.
    +                    i++;
    +                    while (i < signifLength && !sticky) {
    +                        currentDigit = getHexDigit(significandString, i);
    +                        sticky = sticky || (currentDigit != 0);
    +                        i++;
    +                    }
    +
    +                }
    +                // else all of string was seen, round and sticky are
    +                // correct as false.
    +
    +                // Float calculations
    +                int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0;
    +                if (exponent >= FloatConsts.MIN_EXPONENT) {
    +                    if (exponent > FloatConsts.MAX_EXPONENT) {
    +                        // Float.POSITIVE_INFINITY
    +                        floatBits |= FloatConsts.EXP_BIT_MASK;
    +                    } else {
    +                        int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1;
    +                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    +                        int iValue = (int) (significand >>> threshShift);
    +                        if ((iValue & 3) != 1 || floatSticky) {
    +                            iValue++;
    +                        }
    +                        floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1);
    +                    }
    +                } else {
    +                    if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) {
    +                        // 0
    +                    } else {
    +                        // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24
    +                        int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent);
    +                        assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH;
    +                        assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH;
    +                        boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
    +                        int iValue = (int) (significand >>> threshShift);
    +                        if ((iValue & 3) != 1 || floatSticky) {
    +                            iValue++;
    +                        }
    +                        floatBits |= iValue >> 1;
    +                    }
    +                }
    +                float fValue = Float.intBitsToFloat(floatBits);
    +
    +                // Check for overflow and update exponent accordingly.
    +                if (exponent > DoubleConsts.MAX_EXPONENT) {         // Infinite result
    +                    // overflow to properly signed infinity
    +                    return isNegative ? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
    +                } else {  // Finite return value
    +                    if (exponent <= DoubleConsts.MAX_EXPONENT && // (Usually) normal result
    +                            exponent >= DoubleConsts.MIN_EXPONENT) {
    +
    +                        // The result returned in this block cannot be a
    +                        // zero or subnormal; however after the
    +                        // significand is adjusted from rounding, we could
    +                        // still overflow in infinity.
    +
    +                        // AND exponent bits into significand; if the
    +                        // significand is incremented and overflows from
    +                        // rounding, this combination will update the
    +                        // exponent correctly, even in the case of
    +                        // Double.MAX_VALUE overflowing to infinity.
    +
    +                        significand = ((( exponent +
    +                                (long) DoubleConsts.EXP_BIAS) <<
    +                                (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    +                                & DoubleConsts.EXP_BIT_MASK) |
    +                                (DoubleConsts.SIGNIF_BIT_MASK & significand);
    +
    +                    } else {  // Subnormal or zero
    +                        // (exponent < DoubleConsts.MIN_EXPONENT)
    +
    +                        if (exponent < (DoubleConsts.MIN_SUB_EXPONENT - 1)) {
    +                            // No way to round back to nonzero value
    +                            // regardless of significand if the exponent is
    +                            // less than -1075.
    +                            return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
    +                        } else { //  -1075 <= exponent <= MIN_EXPONENT -1 = -1023
    +                            //
    +                            // Find bit position to round to; recompute
    +                            // round and sticky bits, and shift
    +                            // significand right appropriately.
    +                            //
    +
    +                            sticky = sticky || round;
    +                            round = false;
    +
    +                            // Number of bits of significand to preserve is
    +                            // exponent - abs_min_exp +1
    +                            // check:
    +                            // -1075 +1074 + 1 = 0
    +                            // -1023 +1074 + 1 = 52
    +
    +                            int bitsDiscarded = 53 -
    +                                    ((int) exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
    +                            assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
    +
    +                            // What to do here:
    +                            // First, isolate the new round bit
    +                            round = (significand & (1L << (bitsDiscarded - 1))) != 0L;
    +                            if (bitsDiscarded > 1) {
    +                                // create mask to update sticky bits; low
    +                                // order bitsDiscarded bits should be 1
    +                                long mask = ~((~0L) << (bitsDiscarded - 1));
    +                                sticky = sticky || ((significand & mask) != 0L);
    +                            }
    +
    +                            // Now, discard the bits
    +                            significand = significand >> bitsDiscarded;
    +
    +                            significand = ((((long) (DoubleConsts.MIN_EXPONENT - 1) + // subnorm exp.
    +                                    (long) DoubleConsts.EXP_BIAS) <<
    +                                    (DoubleConsts.SIGNIFICAND_WIDTH - 1))
    +                                    & DoubleConsts.EXP_BIT_MASK) |
    +                                    (DoubleConsts.SIGNIF_BIT_MASK & significand);
    +                        }
    +                    }
    +
    +                    // The significand variable now contains the currently
    +                    // appropriate exponent bits too.
    +
    +                    //
    +                    // Determine if significand should be incremented;
    +                    // making this determination depends on the least
    +                    // significant bit and the round and sticky bits.
    +                    //
    +                    // Round to nearest even rounding table, adapted from
    +                    // table 4.7 in "Computer Arithmetic" by IsraelKoren.
    +                    // The digit to the left of the "decimal" point is the
    +                    // least significant bit, the digits to the right of
    +                    // the point are the round and sticky bits
    +                    //
    +                    // Number       Round(x)
    +                    // x0.00        x0.
    +                    // x0.01        x0.
    +                    // x0.10        x0.
    +                    // x0.11        x1. = x0. +1
    +                    // x1.00        x1.
    +                    // x1.01        x1.
    +                    // x1.10        x1. + 1
    +                    // x1.11        x1. + 1
    +                    //
    +                    boolean leastZero = ((significand & 1L) == 0L);
    +                    if ((leastZero && round && sticky) ||
    +                            ((!leastZero) && round)) {
    +                        significand++;
    +                    }
    +
    +                    double value = isNegative ?
    +                            Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) :
    +                            Double.longBitsToDouble(significand );
    +
    +                    return new PreparedASCIIToBinaryBuffer(value, fValue);
    +                }
    +            }
    +    }
    +
    +    /**
    +     * Returns s with any leading zeros removed.
    +     */
    +    static String stripLeadingZeros(String s) {
    +//        return  s.replaceFirst("^0+", "");
    +        if(!s.isEmpty() && s.charAt(0)=='0') {
    +            for(int i=1; iposition
    +     * of string s.
    +     */
    +    static int getHexDigit(String s, int position) {
    +        int value = Character.digit(s.charAt(position), 16);
    +        if (value <= -1 || value >= 16) {
    +            throw new AssertionError("Unexpected failure of digit conversion of " +
    +                                     s.charAt(position));
    +        }
    +        return value;
    +    }
    +}
    diff --git a/src/sun/misc/FormattedFloatingDecimal.java b/src/sun/misc/FormattedFloatingDecimal.java
    new file mode 100644
    index 00000000..fc53920e
    --- /dev/null
    +++ b/src/sun/misc/FormattedFloatingDecimal.java
    @@ -0,0 +1,349 @@
    +/*
    + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code 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
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package sun.misc;
    +
    +import java.util.Arrays;
    +
    +public class FormattedFloatingDecimal{
    +
    +    public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
    +
    +
    +    public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
    +        FloatingDecimal.BinaryToASCIIConverter fdConverter =
    +                FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
    +        return new FormattedFloatingDecimal(precision,form, fdConverter);
    +    }
    +
    +    private int decExponentRounded;
    +    private char[] mantissa;
    +    private char[] exponent;
    +
    +    private static final ThreadLocal threadLocalCharBuffer =
    +            new ThreadLocal() {
    +                @Override
    +                protected Object initialValue() {
    +                    return new char[20];
    +                }
    +            };
    +
    +    private static char[] getBuffer(){
    +        return (char[]) threadLocalCharBuffer.get();
    +    }
    +
    +    private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
    +        if (fdConverter.isExceptional()) {
    +            this.mantissa = fdConverter.toJavaFormatString().toCharArray();
    +            this.exponent = null;
    +            return;
    +        }
    +        char[] digits = getBuffer();
    +        int nDigits = fdConverter.getDigits(digits);
    +        int decExp = fdConverter.getDecimalExponent();
    +        int exp;
    +        boolean isNegative = fdConverter.isNegative();
    +        switch (form) {
    +            case COMPATIBLE:
    +                exp = decExp;
    +                this.decExponentRounded = exp;
    +                fillCompatible(precision, digits, nDigits, exp, isNegative);
    +                break;
    +            case DECIMAL_FLOAT:
    +                exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
    +                fillDecimal(precision, digits, nDigits, exp, isNegative);
    +                this.decExponentRounded = exp;
    +                break;
    +            case SCIENTIFIC:
    +                exp = applyPrecision(decExp, digits, nDigits, precision + 1);
    +                fillScientific(precision, digits, nDigits, exp, isNegative);
    +                this.decExponentRounded = exp;
    +                break;
    +            case GENERAL:
    +                exp = applyPrecision(decExp, digits, nDigits, precision);
    +                // adjust precision to be the number of digits to right of decimal
    +                // the real exponent to be output is actually exp - 1, not exp
    +                if (exp - 1 < -4 || exp - 1 >= precision) {
    +                    // form = Form.SCIENTIFIC;
    +                    precision--;
    +                    fillScientific(precision, digits, nDigits, exp, isNegative);
    +                } else {
    +                    // form = Form.DECIMAL_FLOAT;
    +                    precision = precision - exp;
    +                    fillDecimal(precision, digits, nDigits, exp, isNegative);
    +                }
    +                this.decExponentRounded = exp;
    +                break;
    +            default:
    +                assert false;
    +        }
    +    }
    +
    +    // returns the exponent after rounding has been done by applyPrecision
    +    public int getExponentRounded() {
    +        return decExponentRounded - 1;
    +    }
    +
    +    public char[] getMantissa(){
    +        return mantissa;
    +    }
    +
    +    public char[] getExponent(){
    +        return exponent;
    +    }
    +
    +    /**
    +     * Returns new decExp in case of overflow.
    +     */
    +    private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
    +        if (prec >= nDigits || prec < 0) {
    +            // no rounding necessary
    +            return decExp;
    +        }
    +        if (prec == 0) {
    +            // only one digit (0 or 1) is returned because the precision
    +            // excludes all significant digits
    +            if (digits[0] >= '5') {
    +                digits[0] = '1';
    +                Arrays.fill(digits, 1, nDigits, '0');
    +                return decExp + 1;
    +            } else {
    +                Arrays.fill(digits, 0, nDigits, '0');
    +                return decExp;
    +            }
    +        }
    +        int q = digits[prec];
    +        if (q >= '5') {
    +            int i = prec;
    +            q = digits[--i];
    +            if ( q == '9' ) {
    +                while ( q == '9' && i > 0 ){
    +                    q = digits[--i];
    +                }
    +                if ( q == '9' ){
    +                    // carryout! High-order 1, rest 0s, larger exp.
    +                    digits[0] = '1';
    +                    Arrays.fill(digits, 1, nDigits, '0');
    +                    return decExp+1;
    +                }
    +            }
    +            digits[i] = (char)(q + 1);
    +            Arrays.fill(digits, i+1, nDigits, '0');
    +        } else {
    +            Arrays.fill(digits, prec, nDigits, '0');
    +        }
    +        return decExp;
    +    }
    +
    +    /**
    +     * Fills mantissa and exponent char arrays for compatible format.
    +     */
    +    private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        if (exp > 0 && exp < 8) {
    +            // print digits.digits.
    +            if (nDigits < exp) {
    +                int extraZeros = exp - nDigits;
    +                mantissa = create(isNegative, nDigits + extraZeros + 2);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
    +                mantissa[startIndex + nDigits + extraZeros] = '.';
    +                mantissa[startIndex + nDigits + extraZeros+1] = '0';
    +            } else if (exp < nDigits) {
    +                int t = Math.min(nDigits - exp, precision);
    +                mantissa = create(isNegative, exp + 1 + t);
    +                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    +                mantissa[startIndex + exp ] = '.';
    +                System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
    +            } else { // exp == digits.length
    +                mantissa = create(isNegative, nDigits + 2);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                mantissa[startIndex + nDigits ] = '.';
    +                mantissa[startIndex + nDigits +1] = '0';
    +            }
    +        } else if (exp <= 0 && exp > -3) {
    +            int zeros = Math.max(0, Math.min(-exp, precision));
    +            int t = Math.max(0, Math.min(nDigits, precision + exp));
    +            // write '0' s before the significant digits
    +            if (zeros > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex+1] = '.';
    +                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    +                if (t > 0) {
    +                    // copy only when significant digits are within the precision
    +                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    +                }
    +            } else if (t > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex + 1] = '.';
    +                // copy only when significant digits are within the precision
    +                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    +            } else {
    +                this.mantissa = create(isNegative, 1);
    +                this.mantissa[startIndex] = '0';
    +            }
    +        } else {
    +            if (nDigits > 1) {
    +                mantissa = create(isNegative, nDigits + 1);
    +                mantissa[startIndex] = digits[0];
    +                mantissa[startIndex + 1] = '.';
    +                System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
    +            } else {
    +                mantissa = create(isNegative, 3);
    +                mantissa[startIndex] = digits[0];
    +                mantissa[startIndex + 1] = '.';
    +                mantissa[startIndex + 2] = '0';
    +            }
    +            int e, expStartIntex;
    +            boolean isNegExp = (exp <= 0);
    +            if (isNegExp) {
    +                e = -exp + 1;
    +                expStartIntex = 1;
    +            } else {
    +                e = exp - 1;
    +                expStartIntex = 0;
    +            }
    +            // decExponent has 1, 2, or 3, digits
    +            if (e <= 9) {
    +                exponent = create(isNegExp,1);
    +                exponent[expStartIntex] = (char) (e + '0');
    +            } else if (e <= 99) {
    +                exponent = create(isNegExp,2);
    +                exponent[expStartIntex] = (char) (e / 10 + '0');
    +                exponent[expStartIntex+1] = (char) (e % 10 + '0');
    +            } else {
    +                exponent = create(isNegExp,3);
    +                exponent[expStartIntex] = (char) (e / 100 + '0');
    +                e %= 100;
    +                exponent[expStartIntex+1] = (char) (e / 10 + '0');
    +                exponent[expStartIntex+2] = (char) (e % 10 + '0');
    +            }
    +        }
    +    }
    +
    +    private static char[] create(boolean isNegative, int size) {
    +        if(isNegative) {
    +            char[] r = new char[size +1];
    +            r[0] = '-';
    +            return r;
    +        } else {
    +            return new char[size];
    +        }
    +    }
    +
    +    /*
    +     * Fills mantissa char arrays for DECIMAL_FLOAT format.
    +     * Exponent should be equal to null.
    +     */
    +    private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        if (exp > 0) {
    +            // print digits.digits.
    +            if (nDigits < exp) {
    +                mantissa = create(isNegative,exp);
    +                System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
    +                Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
    +                // Do not append ".0" for formatted floats since the user
    +                // may request that it be omitted. It is added as necessary
    +                // by the Formatter.
    +            } else {
    +                int t = Math.min(nDigits - exp, precision);
    +                mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
    +                System.arraycopy(digits, 0, mantissa, startIndex, exp);
    +                // Do not append ".0" for formatted floats since the user
    +                // may request that it be omitted. It is added as necessary
    +                // by the Formatter.
    +                if (t > 0) {
    +                    mantissa[startIndex + exp] = '.';
    +                    System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
    +                }
    +            }
    +        } else if (exp <= 0) {
    +            int zeros = Math.max(0, Math.min(-exp, precision));
    +            int t = Math.max(0, Math.min(nDigits, precision + exp));
    +            // write '0' s before the significant digits
    +            if (zeros > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex+1] = '.';
    +                Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
    +                if (t > 0) {
    +                    // copy only when significant digits are within the precision
    +                    System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
    +                }
    +            } else if (t > 0) {
    +                mantissa = create(isNegative, zeros + 2 + t);
    +                mantissa[startIndex] = '0';
    +                mantissa[startIndex + 1] = '.';
    +                // copy only when significant digits are within the precision
    +                System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
    +            } else {
    +                this.mantissa = create(isNegative, 1);
    +                this.mantissa[startIndex] = '0';
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Fills mantissa and exponent char arrays for SCIENTIFIC format.
    +     */
    +    private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
    +        int startIndex = isNegative ? 1 : 0;
    +        int t = Math.max(0, Math.min(nDigits - 1, precision));
    +        if (t > 0) {
    +            mantissa = create(isNegative, t + 2);
    +            mantissa[startIndex] = digits[0];
    +            mantissa[startIndex + 1] = '.';
    +            System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
    +        } else {
    +            mantissa = create(isNegative, 1);
    +            mantissa[startIndex] = digits[0];
    +        }
    +        char expSign;
    +        int e;
    +        if (exp <= 0) {
    +            expSign = '-';
    +            e = -exp + 1;
    +        } else {
    +            expSign = '+' ;
    +            e = exp - 1;
    +        }
    +        // decExponent has 1, 2, or 3, digits
    +        if (e <= 9) {
    +            exponent = new char[] { expSign,
    +                    '0', (char) (e + '0') };
    +        } else if (e <= 99) {
    +            exponent = new char[] { expSign,
    +                    (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    +        } else {
    +            char hiExpChar = (char) (e / 100 + '0');
    +            e %= 100;
    +            exponent = new char[] { expSign,
    +                    hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
    +        }
    +    }
    +}
    diff --git a/src/sun/misc/FpUtils.java b/src/sun/misc/FpUtils.java
    new file mode 100644
    index 00000000..b0fc6120
    --- /dev/null
    +++ b/src/sun/misc/FpUtils.java
    @@ -0,0 +1,928 @@
    +/*
    + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
    + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    + *
    + * This code is free software; you can redistribute it and/or modify it
    + * under the terms of the GNU General Public License version 2 only, as
    + * published by the Free Software Foundation.  Oracle designates this
    + * particular file as subject to the "Classpath" exception as provided
    + * by Oracle in the LICENSE file that accompanied this code.
    + *
    + * This code 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
    + * version 2 for more details (a copy is included in the LICENSE file that
    + * accompanied this code).
    + *
    + * You should have received a copy of the GNU General Public License version
    + * 2 along with this work; if not, write to the Free Software Foundation,
    + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    + *
    + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    + * or visit www.oracle.com if you need additional information or have any
    + * questions.
    + */
    +
    +package sun.misc;
    +
    +/**
    + * The class {@code FpUtils} contains static utility methods for
    + * manipulating and inspecting {@code float} and
    + * {@code double} floating-point numbers.  These methods include
    + * functionality recommended or required by the IEEE 754
    + * floating-point standard.
    + *
    + * @author Joseph D. Darcy
    + */
    +
    +public class FpUtils {
    +    /*
    +     * The methods in this class are reasonably implemented using
    +     * direct or indirect bit-level manipulation of floating-point
    +     * values.  However, having access to the IEEE 754 recommended
    +     * functions would obviate the need for most programmers to engage
    +     * in floating-point bit-twiddling.
    +     *
    +     * An IEEE 754 number has three fields, from most significant bit
    +     * to to least significant, sign, exponent, and significand.
    +     *
    +     *  msb                                lsb
    +     * [sign|exponent|  fractional_significand]
    +     *
    +     * Using some encoding cleverness, explained below, the high order
    +     * bit of the logical significand does not need to be explicitly
    +     * stored, thus "fractional_significand" instead of simply
    +     * "significand" in the figure above.
    +     *
    +     * For finite normal numbers, the numerical value encoded is
    +     *
    +     * (-1)^sign * 2^(exponent)*(1.fractional_significand)
    +     *
    +     * Most finite floating-point numbers are normalized; the exponent
    +     * value is reduced until the leading significand bit is 1.
    +     * Therefore, the leading 1 is redundant and is not explicitly
    +     * stored.  If a numerical value is so small it cannot be
    +     * normalized, it has a subnormal representation. Subnormal
    +     * numbers don't have a leading 1 in their significand; subnormals
    +     * are encoding using a special exponent value.  In other words,
    +     * the high-order bit of the logical significand can be elided in
    +     * from the representation in either case since the bit's value is
    +     * implicit from the exponent value.
    +     *
    +     * The exponent field uses a biased representation; if the bits of
    +     * the exponent are interpreted as a unsigned integer E, the
    +     * exponent represented is E - E_bias where E_bias depends on the
    +     * floating-point format.  E can range between E_min and E_max,
    +     * constants which depend on the floating-point format.  E_min and
    +     * E_max are -126 and +127 for float, -1022 and +1023 for double.
    +     *
    +     * The 32-bit float format has 1 sign bit, 8 exponent bits, and 23
    +     * bits for the significand (which is logically 24 bits wide
    +     * because of the implicit bit).  The 64-bit double format has 1
    +     * sign bit, 11 exponent bits, and 52 bits for the significand
    +     * (logically 53 bits).
    +     *
    +     * Subnormal numbers and zero have the special exponent value
    +     * E_min -1; the numerical value represented by a subnormal is:
    +     *
    +     * (-1)^sign * 2^(E_min)*(0.fractional_significand)
    +     *
    +     * Zero is represented by all zero bits in the exponent and all
    +     * zero bits in the significand; zero can have either sign.
    +     *
    +     * Infinity and NaN are encoded using the exponent value E_max +
    +     * 1.  Signed infinities have all significand bits zero; NaNs have
    +     * at least one non-zero significand bit.
    +     *
    +     * The details of IEEE 754 floating-point encoding will be used in
    +     * the methods below without further comment.  For further
    +     * exposition on IEEE 754 numbers, see "IEEE Standard for Binary
    +     * Floating-Point Arithmetic" ANSI/IEEE Std 754-1985 or William
    +     * Kahan's "Lecture Notes on the Status of IEEE Standard 754 for
    +     * Binary Floating-Point Arithmetic",
    +     * http://www.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps.
    +     *
    +     * Many of this class's methods are members of the set of IEEE 754
    +     * recommended functions or similar functions recommended or
    +     * required by IEEE 754R.  Discussion of various implementation
    +     * techniques for these functions have occurred in:
    +     *
    +     * W.J. Cody and Jerome T. Coonen, "Algorithm 772 Functions to
    +     * Support the IEEE Standard for Binary Floating-Point
    +     * Arithmetic," ACM Transactions on Mathematical Software,
    +     * vol. 19, no. 4, December 1993, pp. 443-451.
    +     *
    +     * Joseph D. Darcy, "Writing robust IEEE recommended functions in
    +     * ``100% Pure Java''(TM)," University of California, Berkeley
    +     * technical report UCB//CSD-98-1009.
    +     */
    +
    +    /**
    +     * Don't let anyone instantiate this class.
    +     */
    +    private FpUtils() {}
    +
    +    // Helper Methods
    +
    +    // The following helper methods are used in the implementation of
    +    // the public recommended functions; they generally omit certain
    +    // tests for exception cases.
    +
    +    /**
    +     * Returns unbiased exponent of a {@code double}.
    +     * @deprecated Use Math.getExponent.
    +     */
    +    @Deprecated
    +    public static int getExponent(double d){
    +        return Math.getExponent(d);
    +    }
    +
    +    /**
    +     * Returns unbiased exponent of a {@code float}.
    +     * @deprecated Use Math.getExponent.
    +     */
    +    @Deprecated
    +    public static int getExponent(float f){
    +        return Math.getExponent(f);
    +    }
    +
    +
    +    /**
    +     * Returns the first floating-point argument with the sign of the
    +     * second floating-point argument.  Note that unlike the {@link
    +     * FpUtils#copySign(double, double) copySign} method, this method
    +     * does not require NaN {@code sign} arguments to be treated
    +     * as positive values; implementations are permitted to treat some
    +     * NaN arguments as positive and other NaN arguments as negative
    +     * to allow greater performance.
    +     *
    +     * @param magnitude  the parameter providing the magnitude of the result
    +     * @param sign   the parameter providing the sign of the result
    +     * @return a value with the magnitude of {@code magnitude}
    +     * and the sign of {@code sign}.
    +     * @author Joseph D. Darcy
    +     * @deprecated Use Math.copySign.
    +     */
    +    @Deprecated
    +    public static double rawCopySign(double magnitude, double sign) {
    +        return Math.copySign(magnitude, sign);
    +    }
    +
    +    /**
    +     * Returns the first floating-point argument with the sign of the
    +     * second floating-point argument.  Note that unlike the {@link
    +     * FpUtils#copySign(float, float) copySign} method, this method
    +     * does not require NaN {@code sign} arguments to be treated
    +     * as positive values; implementations are permitted to treat some
    +     * NaN arguments as positive and other NaN arguments as negative
    +     * to allow greater performance.
    +     *
    +     * @param magnitude  the parameter providing the magnitude of the result
    +     * @param sign   the parameter providing the sign of the result
    +     * @return a value with the magnitude of {@code magnitude}
    +     * and the sign of {@code sign}.
    +     * @author Joseph D. Darcy
    +     * @deprecated Use Math.copySign.
    +     */
    +    @Deprecated
    +    public static float rawCopySign(float magnitude, float sign) {
    +        return Math.copySign(magnitude, sign);
    +    }
    +
    +    /* ***************************************************************** */
    +
    +    /**
    +     * Returns {@code true} if the argument is a finite
    +     * floating-point value; returns {@code false} otherwise (for
    +     * NaN and infinity arguments).
    +     *
    +     * @param d the {@code double} value to be tested
    +     * @return {@code true} if the argument is a finite
    +     * floating-point value, {@code false} otherwise.
    +     * @deprecated Use Double.isFinite.
    +     */
    +    @Deprecated
    +    public static boolean isFinite(double d) {
    +        return Double.isFinite(d);
    +    }
    +
    +    /**
    +     * Returns {@code true} if the argument is a finite
    +     * floating-point value; returns {@code false} otherwise (for
    +     * NaN and infinity arguments).
    +     *
    +     * @param f the {@code float} value to be tested
    +     * @return {@code true} if the argument is a finite
    +     * floating-point value, {@code false} otherwise.
    +     * @deprecated Use Float.isFinite.
    +     */
    +     @Deprecated
    +     public static boolean isFinite(float f) {
    +         return Float.isFinite(f);
    +    }
    +
    +    /**
    +     * Returns {@code true} if the specified number is infinitely
    +     * large in magnitude, {@code false} otherwise.
    +     *
    +     * 

    Note that this method is equivalent to the {@link + * Double#isInfinite(double) Double.isInfinite} method; the + * functionality is included in this class for convenience. + * + * @param d the value to be tested. + * @return {@code true} if the value of the argument is positive + * infinity or negative infinity; {@code false} otherwise. + */ + public static boolean isInfinite(double d) { + return Double.isInfinite(d); + } + + /** + * Returns {@code true} if the specified number is infinitely + * large in magnitude, {@code false} otherwise. + * + *

    Note that this method is equivalent to the {@link + * Float#isInfinite(float) Float.isInfinite} method; the + * functionality is included in this class for convenience. + * + * @param f the value to be tested. + * @return {@code true} if the argument is positive infinity or + * negative infinity; {@code false} otherwise. + */ + public static boolean isInfinite(float f) { + return Float.isInfinite(f); + } + + /** + * Returns {@code true} if the specified number is a + * Not-a-Number (NaN) value, {@code false} otherwise. + * + *

    Note that this method is equivalent to the {@link + * Double#isNaN(double) Double.isNaN} method; the functionality is + * included in this class for convenience. + * + * @param d the value to be tested. + * @return {@code true} if the value of the argument is NaN; + * {@code false} otherwise. + */ + public static boolean isNaN(double d) { + return Double.isNaN(d); + } + + /** + * Returns {@code true} if the specified number is a + * Not-a-Number (NaN) value, {@code false} otherwise. + * + *

    Note that this method is equivalent to the {@link + * Float#isNaN(float) Float.isNaN} method; the functionality is + * included in this class for convenience. + * + * @param f the value to be tested. + * @return {@code true} if the argument is NaN; + * {@code false} otherwise. + */ + public static boolean isNaN(float f) { + return Float.isNaN(f); + } + + /** + * Returns {@code true} if the unordered relation holds + * between the two arguments. When two floating-point values are + * unordered, one value is neither less than, equal to, nor + * greater than the other. For the unordered relation to be true, + * at least one argument must be a {@code NaN}. + * + * @param arg1 the first argument + * @param arg2 the second argument + * @return {@code true} if at least one argument is a NaN, + * {@code false} otherwise. + */ + public static boolean isUnordered(double arg1, double arg2) { + return isNaN(arg1) || isNaN(arg2); + } + + /** + * Returns {@code true} if the unordered relation holds + * between the two arguments. When two floating-point values are + * unordered, one value is neither less than, equal to, nor + * greater than the other. For the unordered relation to be true, + * at least one argument must be a {@code NaN}. + * + * @param arg1 the first argument + * @param arg2 the second argument + * @return {@code true} if at least one argument is a NaN, + * {@code false} otherwise. + */ + public static boolean isUnordered(float arg1, float arg2) { + return isNaN(arg1) || isNaN(arg2); + } + + /** + * Returns unbiased exponent of a {@code double}; for + * subnormal values, the number is treated as if it were + * normalized. That is for all finite, non-zero, positive numbers + * x, scalb(x, -ilogb(x)) is + * always in the range [1, 2). + *

    + * Special cases: + *

      + *
    • If the argument is NaN, then the result is 230. + *
    • If the argument is infinite, then the result is 228. + *
    • If the argument is zero, then the result is -(228). + *
    + * + * @param d floating-point number whose exponent is to be extracted + * @return unbiased exponent of the argument. + * @author Joseph D. Darcy + */ + public static int ilogb(double d) { + int exponent = getExponent(d); + + switch (exponent) { + case DoubleConsts.MAX_EXPONENT+1: // NaN or infinity + if( isNaN(d) ) + return (1<<30); // 2^30 + else // infinite value + return (1<<28); // 2^28 + + case DoubleConsts.MIN_EXPONENT-1: // zero or subnormal + if(d == 0.0) { + return -(1<<28); // -(2^28) + } + else { + long transducer = Double.doubleToRawLongBits(d); + + /* + * To avoid causing slow arithmetic on subnormals, + * the scaling to determine when d's significand + * is normalized is done in integer arithmetic. + * (there must be at least one "1" bit in the + * significand since zero has been screened out. + */ + + // isolate significand bits + transducer &= DoubleConsts.SIGNIF_BIT_MASK; + assert(transducer != 0L); + + // This loop is simple and functional. We might be + // able to do something more clever that was faster; + // e.g. number of leading zero detection on + // (transducer << (# exponent and sign bits). + while (transducer < + (1L << (DoubleConsts.SIGNIFICAND_WIDTH - 1))) { + transducer *= 2; + exponent--; + } + exponent++; + assert( exponent >= + DoubleConsts.MIN_EXPONENT - (DoubleConsts.SIGNIFICAND_WIDTH-1) && + exponent < DoubleConsts.MIN_EXPONENT); + return exponent; + } + + default: + assert( exponent >= DoubleConsts.MIN_EXPONENT && + exponent <= DoubleConsts.MAX_EXPONENT); + return exponent; + } + } + + /** + * Returns unbiased exponent of a {@code float}; for + * subnormal values, the number is treated as if it were + * normalized. That is for all finite, non-zero, positive numbers + * x, scalb(x, -ilogb(x)) is + * always in the range [1, 2). + *

    + * Special cases: + *

      + *
    • If the argument is NaN, then the result is 230. + *
    • If the argument is infinite, then the result is 228. + *
    • If the argument is zero, then the result is -(228). + *
    + * + * @param f floating-point number whose exponent is to be extracted + * @return unbiased exponent of the argument. + * @author Joseph D. Darcy + */ + public static int ilogb(float f) { + int exponent = getExponent(f); + + switch (exponent) { + case FloatConsts.MAX_EXPONENT+1: // NaN or infinity + if( isNaN(f) ) + return (1<<30); // 2^30 + else // infinite value + return (1<<28); // 2^28 + + case FloatConsts.MIN_EXPONENT-1: // zero or subnormal + if(f == 0.0f) { + return -(1<<28); // -(2^28) + } + else { + int transducer = Float.floatToRawIntBits(f); + + /* + * To avoid causing slow arithmetic on subnormals, + * the scaling to determine when f's significand + * is normalized is done in integer arithmetic. + * (there must be at least one "1" bit in the + * significand since zero has been screened out. + */ + + // isolate significand bits + transducer &= FloatConsts.SIGNIF_BIT_MASK; + assert(transducer != 0); + + // This loop is simple and functional. We might be + // able to do something more clever that was faster; + // e.g. number of leading zero detection on + // (transducer << (# exponent and sign bits). + while (transducer < + (1 << (FloatConsts.SIGNIFICAND_WIDTH - 1))) { + transducer *= 2; + exponent--; + } + exponent++; + assert( exponent >= + FloatConsts.MIN_EXPONENT - (FloatConsts.SIGNIFICAND_WIDTH-1) && + exponent < FloatConsts.MIN_EXPONENT); + return exponent; + } + + default: + assert( exponent >= FloatConsts.MIN_EXPONENT && + exponent <= FloatConsts.MAX_EXPONENT); + return exponent; + } + } + + + /* + * The scalb operation should be reasonably fast; however, there + * are tradeoffs in writing a method to minimize the worst case + * performance and writing a method to minimize the time for + * expected common inputs. Some processors operate very slowly on + * subnormal operands, taking hundreds or thousands of cycles for + * one floating-point add or multiply as opposed to, say, four + * cycles for normal operands. For processors with very slow + * subnormal execution, scalb would be fastest if written entirely + * with integer operations; in other words, scalb would need to + * include the logic of performing correct rounding of subnormal + * values. This could be reasonably done in at most a few hundred + * cycles. However, this approach may penalize normal operations + * since at least the exponent of the floating-point argument must + * be examined. + * + * The approach taken in this implementation is a compromise. + * Floating-point multiplication is used to do most of the work; + * but knowingly multiplying by a subnormal scaling factor is + * avoided. However, the floating-point argument is not examined + * to see whether or not it is subnormal since subnormal inputs + * are assumed to be rare. At most three multiplies are needed to + * scale from the largest to smallest exponent ranges (scaling + * down, at most two multiplies are needed if subnormal scaling + * factors are allowed). However, in this implementation an + * expensive integer remainder operation is avoided at the cost of + * requiring five floating-point multiplies in the worst case, + * which should still be a performance win. + * + * If scaling of entire arrays is a concern, it would probably be + * more efficient to provide a double[] scalb(double[], int) + * version of scalb to avoid having to recompute the needed + * scaling factors for each floating-point value. + */ + + /** + * Return {@code d} × + * 2{@code scale_factor} rounded as if performed + * by a single correctly rounded floating-point multiply to a + * member of the double value set. See section 4.2.3 of + * The Java™ Language Specification + * for a discussion of floating-point + * value sets. If the exponent of the result is between the + * {@code double}'s minimum exponent and maximum exponent, + * the answer is calculated exactly. If the exponent of the + * result would be larger than {@code doubles}'s maximum + * exponent, an infinity is returned. Note that if the result is + * subnormal, precision may be lost; that is, when {@code scalb(x, + * n)} is subnormal, {@code scalb(scalb(x, n), -n)} may + * not equal x. When the result is non-NaN, the result has + * the same sign as {@code d}. + * + *

    + * Special cases: + *

      + *
    • If the first argument is NaN, NaN is returned. + *
    • If the first argument is infinite, then an infinity of the + * same sign is returned. + *
    • If the first argument is zero, then a zero of the same + * sign is returned. + *
    + * + * @param d number to be scaled by a power of two. + * @param scale_factor power of 2 used to scale {@code d} + * @return {@code d * }2{@code scale_factor} + * @author Joseph D. Darcy + * @deprecated Use Math.scalb. + */ + @Deprecated + public static double scalb(double d, int scale_factor) { + return Math.scalb(d, scale_factor); + } + + /** + * Return {@code f} × + * 2{@code scale_factor} rounded as if performed + * by a single correctly rounded floating-point multiply to a + * member of the float value set. See section 4.2.3 of + * The Java™ Language Specification + * for a discussion of floating-point + * value sets. If the exponent of the result is between the + * {@code float}'s minimum exponent and maximum exponent, the + * answer is calculated exactly. If the exponent of the result + * would be larger than {@code float}'s maximum exponent, an + * infinity is returned. Note that if the result is subnormal, + * precision may be lost; that is, when {@code scalb(x, n)} + * is subnormal, {@code scalb(scalb(x, n), -n)} may not equal + * x. When the result is non-NaN, the result has the same + * sign as {@code f}. + * + *

    + * Special cases: + *

      + *
    • If the first argument is NaN, NaN is returned. + *
    • If the first argument is infinite, then an infinity of the + * same sign is returned. + *
    • If the first argument is zero, then a zero of the same + * sign is returned. + *
    + * + * @param f number to be scaled by a power of two. + * @param scale_factor power of 2 used to scale {@code f} + * @return {@code f * }2{@code scale_factor} + * @author Joseph D. Darcy + * @deprecated Use Math.scalb. + */ + @Deprecated + public static float scalb(float f, int scale_factor) { + return Math.scalb(f, scale_factor); + } + + /** + * Returns the floating-point number adjacent to the first + * argument in the direction of the second argument. If both + * arguments compare as equal the second argument is returned. + * + *

    + * Special cases: + *

      + *
    • If either argument is a NaN, then NaN is returned. + * + *
    • If both arguments are signed zeros, {@code direction} + * is returned unchanged (as implied by the requirement of + * returning the second argument if the arguments compare as + * equal). + * + *
    • If {@code start} is + * ±{@code Double.MIN_VALUE} and {@code direction} + * has a value such that the result should have a smaller + * magnitude, then a zero with the same sign as {@code start} + * is returned. + * + *
    • If {@code start} is infinite and + * {@code direction} has a value such that the result should + * have a smaller magnitude, {@code Double.MAX_VALUE} with the + * same sign as {@code start} is returned. + * + *
    • If {@code start} is equal to ± + * {@code Double.MAX_VALUE} and {@code direction} has a + * value such that the result should have a larger magnitude, an + * infinity with same sign as {@code start} is returned. + *
    + * + * @param start starting floating-point value + * @param direction value indicating which of + * {@code start}'s neighbors or {@code start} should + * be returned + * @return The floating-point number adjacent to {@code start} in the + * direction of {@code direction}. + * @author Joseph D. Darcy + * @deprecated Use Math.nextAfter + */ + @Deprecated + public static double nextAfter(double start, double direction) { + return Math.nextAfter(start, direction); + } + + /** + * Returns the floating-point number adjacent to the first + * argument in the direction of the second argument. If both + * arguments compare as equal, the second argument is returned. + * + *

    + * Special cases: + *

      + *
    • If either argument is a NaN, then NaN is returned. + * + *
    • If both arguments are signed zeros, a {@code float} + * zero with the same sign as {@code direction} is returned + * (as implied by the requirement of returning the second argument + * if the arguments compare as equal). + * + *
    • If {@code start} is + * ±{@code Float.MIN_VALUE} and {@code direction} + * has a value such that the result should have a smaller + * magnitude, then a zero with the same sign as {@code start} + * is returned. + * + *
    • If {@code start} is infinite and + * {@code direction} has a value such that the result should + * have a smaller magnitude, {@code Float.MAX_VALUE} with the + * same sign as {@code start} is returned. + * + *
    • If {@code start} is equal to ± + * {@code Float.MAX_VALUE} and {@code direction} has a + * value such that the result should have a larger magnitude, an + * infinity with same sign as {@code start} is returned. + *
    + * + * @param start starting floating-point value + * @param direction value indicating which of + * {@code start}'s neighbors or {@code start} should + * be returned + * @return The floating-point number adjacent to {@code start} in the + * direction of {@code direction}. + * @author Joseph D. Darcy + * @deprecated Use Math.nextAfter. + */ + @Deprecated + public static float nextAfter(float start, double direction) { + return Math.nextAfter(start, direction); + } + + /** + * Returns the floating-point value adjacent to {@code d} in + * the direction of positive infinity. This method is + * semantically equivalent to {@code nextAfter(d, + * Double.POSITIVE_INFINITY)}; however, a {@code nextUp} + * implementation may run faster than its equivalent + * {@code nextAfter} call. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, the result is NaN. + * + *
    • If the argument is positive infinity, the result is + * positive infinity. + * + *
    • If the argument is zero, the result is + * {@code Double.MIN_VALUE} + * + *
    + * + * @param d starting floating-point value + * @return The adjacent floating-point value closer to positive + * infinity. + * @author Joseph D. Darcy + * @deprecated use Math.nextUp. + */ + @Deprecated + public static double nextUp(double d) { + return Math.nextUp(d); + } + + /** + * Returns the floating-point value adjacent to {@code f} in + * the direction of positive infinity. This method is + * semantically equivalent to {@code nextAfter(f, + * Double.POSITIVE_INFINITY)}; however, a {@code nextUp} + * implementation may run faster than its equivalent + * {@code nextAfter} call. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, the result is NaN. + * + *
    • If the argument is positive infinity, the result is + * positive infinity. + * + *
    • If the argument is zero, the result is + * {@code Float.MIN_VALUE} + * + *
    + * + * @param f starting floating-point value + * @return The adjacent floating-point value closer to positive + * infinity. + * @author Joseph D. Darcy + * @deprecated Use Math.nextUp. + */ + @Deprecated + public static float nextUp(float f) { + return Math.nextUp(f); + } + + /** + * Returns the floating-point value adjacent to {@code d} in + * the direction of negative infinity. This method is + * semantically equivalent to {@code nextAfter(d, + * Double.NEGATIVE_INFINITY)}; however, a + * {@code nextDown} implementation may run faster than its + * equivalent {@code nextAfter} call. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, the result is NaN. + * + *
    • If the argument is negative infinity, the result is + * negative infinity. + * + *
    • If the argument is zero, the result is + * {@code -Double.MIN_VALUE} + * + *
    + * + * @param d starting floating-point value + * @return The adjacent floating-point value closer to negative + * infinity. + * @author Joseph D. Darcy + * @deprecated Use Math.nextDown. + */ + @Deprecated + public static double nextDown(double d) { + return Math.nextDown(d); + } + + /** + * Returns the floating-point value adjacent to {@code f} in + * the direction of negative infinity. This method is + * semantically equivalent to {@code nextAfter(f, + * Float.NEGATIVE_INFINITY)}; however, a + * {@code nextDown} implementation may run faster than its + * equivalent {@code nextAfter} call. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, the result is NaN. + * + *
    • If the argument is negative infinity, the result is + * negative infinity. + * + *
    • If the argument is zero, the result is + * {@code -Float.MIN_VALUE} + * + *
    + * + * @param f starting floating-point value + * @return The adjacent floating-point value closer to negative + * infinity. + * @author Joseph D. Darcy + * @deprecated Use Math.nextDown. + */ + @Deprecated + public static double nextDown(float f) { + return Math.nextDown(f); + } + + /** + * Returns the first floating-point argument with the sign of the + * second floating-point argument. For this method, a NaN + * {@code sign} argument is always treated as if it were + * positive. + * + * @param magnitude the parameter providing the magnitude of the result + * @param sign the parameter providing the sign of the result + * @return a value with the magnitude of {@code magnitude} + * and the sign of {@code sign}. + * @author Joseph D. Darcy + * @since 1.5 + * @deprecated Use StrictMath.copySign. + */ + @Deprecated + public static double copySign(double magnitude, double sign) { + return StrictMath.copySign(magnitude, sign); + } + + /** + * Returns the first floating-point argument with the sign of the + * second floating-point argument. For this method, a NaN + * {@code sign} argument is always treated as if it were + * positive. + * + * @param magnitude the parameter providing the magnitude of the result + * @param sign the parameter providing the sign of the result + * @return a value with the magnitude of {@code magnitude} + * and the sign of {@code sign}. + * @author Joseph D. Darcy + * @deprecated Use StrictMath.copySign. + */ + @Deprecated + public static float copySign(float magnitude, float sign) { + return StrictMath.copySign(magnitude, sign); + } + + /** + * Returns the size of an ulp of the argument. An ulp of a + * {@code double} value is the positive distance between this + * floating-point value and the {@code double} value next + * larger in magnitude. Note that for non-NaN x, + * ulp(-x) == ulp(x). + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, then the result is NaN. + *
    • If the argument is positive or negative infinity, then the + * result is positive infinity. + *
    • If the argument is positive or negative zero, then the result is + * {@code Double.MIN_VALUE}. + *
    • If the argument is ±{@code Double.MAX_VALUE}, then + * the result is equal to 2971. + *
    + * + * @param d the floating-point value whose ulp is to be returned + * @return the size of an ulp of the argument + * @author Joseph D. Darcy + * @since 1.5 + * @deprecated Use Math.ulp. + */ + @Deprecated + public static double ulp(double d) { + return Math.ulp(d); + } + + /** + * Returns the size of an ulp of the argument. An ulp of a + * {@code float} value is the positive distance between this + * floating-point value and the {@code float} value next + * larger in magnitude. Note that for non-NaN x, + * ulp(-x) == ulp(x). + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, then the result is NaN. + *
    • If the argument is positive or negative infinity, then the + * result is positive infinity. + *
    • If the argument is positive or negative zero, then the result is + * {@code Float.MIN_VALUE}. + *
    • If the argument is ±{@code Float.MAX_VALUE}, then + * the result is equal to 2104. + *
    + * + * @param f the floating-point value whose ulp is to be returned + * @return the size of an ulp of the argument + * @author Joseph D. Darcy + * @since 1.5 + * @deprecated Use Math.ulp. + */ + @Deprecated + public static float ulp(float f) { + return Math.ulp(f); + } + + /** + * Returns the signum function of the argument; zero if the argument + * is zero, 1.0 if the argument is greater than zero, -1.0 if the + * argument is less than zero. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, then the result is NaN. + *
    • If the argument is positive zero or negative zero, then the + * result is the same as the argument. + *
    + * + * @param d the floating-point value whose signum is to be returned + * @return the signum function of the argument + * @author Joseph D. Darcy + * @since 1.5 + * @deprecated Use Math.signum. + */ + @Deprecated + public static double signum(double d) { + return Math.signum(d); + } + + /** + * Returns the signum function of the argument; zero if the argument + * is zero, 1.0f if the argument is greater than zero, -1.0f if the + * argument is less than zero. + * + *

    Special Cases: + *

      + *
    • If the argument is NaN, then the result is NaN. + *
    • If the argument is positive zero or negative zero, then the + * result is the same as the argument. + *
    + * + * @param f the floating-point value whose signum is to be returned + * @return the signum function of the argument + * @author Joseph D. Darcy + * @since 1.5 + * @deprecated Use Math.signum. + */ + @Deprecated + public static float signum(float f) { + return Math.signum(f); + } +} diff --git a/src/sun/misc/GC.java b/src/sun/misc/GC.java new file mode 100644 index 00000000..c77bf1d0 --- /dev/null +++ b/src/sun/misc/GC.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.SortedSet; +import java.util.TreeSet; + + +/** + * Support for garbage-collection latency requests. + * + * @author Mark Reinhold + * @since 1.2 + */ + +public class GC { + + private GC() { } /* To prevent instantiation */ + + + /* Latency-target value indicating that there's no active target + */ + private static final long NO_TARGET = Long.MAX_VALUE; + + /* The current latency target, or NO_TARGET if there is no target + */ + private static long latencyTarget = NO_TARGET; + + /* The daemon thread that implements the latency-target mechanism, + * or null if there is presently no daemon thread + */ + private static Thread daemon = null; + + /* The lock object for the latencyTarget and daemon fields. The daemon + * thread, if it exists, waits on this lock for notification that the + * latency target has changed. + */ + private static class LatencyLock extends Object { }; + private static Object lock = new LatencyLock(); + + + /** + * Returns the maximum object-inspection age, which is the number + * of real-time milliseconds that have elapsed since the + * least-recently-inspected heap object was last inspected by the garbage + * collector. + * + *

    For simple stop-the-world collectors this value is just the time + * since the most recent collection. For generational collectors it is the + * time since the oldest generation was most recently collected. Other + * collectors are free to return a pessimistic estimate of the elapsed + * time, or simply the time since the last full collection was performed. + * + *

    Note that in the presence of reference objects, a given object that + * is no longer strongly reachable may have to be inspected multiple times + * before it can be reclaimed. + */ + public static native long maxObjectInspectionAge(); + + + private static class Daemon extends Thread { + + public void run() { + for (;;) { + long l; + synchronized (lock) { + + l = latencyTarget; + if (l == NO_TARGET) { + /* No latency target, so exit */ + GC.daemon = null; + return; + } + + long d = maxObjectInspectionAge(); + if (d >= l) { + /* Do a full collection. There is a remote possibility + * that a full collection will occurr between the time + * we sample the inspection age and the time the GC + * actually starts, but this is sufficiently unlikely + * that it doesn't seem worth the more expensive JVM + * interface that would be required. + */ + System.gc(); + d = 0; + } + + /* Wait for the latency period to expire, + * or for notification that the period has changed + */ + try { + lock.wait(l - d); + } catch (InterruptedException x) { + continue; + } + } + } + } + + private Daemon(ThreadGroup tg) { + super(tg, "GC Daemon"); + } + + /* Create a new daemon thread in the root thread group */ + public static void create() { + PrivilegedAction pa = new PrivilegedAction() { + public Void run() { + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + for (ThreadGroup tgn = tg; + tgn != null; + tg = tgn, tgn = tg.getParent()); + Daemon d = new Daemon(tg); + d.setDaemon(true); + d.setPriority(Thread.MIN_PRIORITY + 1); + d.start(); + GC.daemon = d; + return null; + }}; + AccessController.doPrivileged(pa); + } + + } + + + /* Sets the latency target to the given value. + * Must be invoked while holding the lock. + */ + private static void setLatencyTarget(long ms) { + latencyTarget = ms; + if (daemon == null) { + /* Create a new daemon thread */ + Daemon.create(); + } else { + /* Notify the existing daemon thread + * that the lateency target has changed + */ + lock.notify(); + } + } + + + /** + * Represents an active garbage-collection latency request. Instances of + * this class are created by the {@link #requestLatency} + * method. Given a request, the only interesting operation is that of + * cancellation. + */ + public static class LatencyRequest + implements Comparable { + + /* Instance counter, used to generate unique identifers */ + private static long counter = 0; + + /* Sorted set of active latency requests */ + private static SortedSet requests = null; + + /* Examine the request set and reset the latency target if necessary. + * Must be invoked while holding the lock. + */ + private static void adjustLatencyIfNeeded() { + if ((requests == null) || requests.isEmpty()) { + if (latencyTarget != NO_TARGET) { + setLatencyTarget(NO_TARGET); + } + } else { + LatencyRequest r = requests.first(); + if (r.latency != latencyTarget) { + setLatencyTarget(r.latency); + } + } + } + + /* The requested latency, or NO_TARGET + * if this request has been cancelled + */ + private long latency; + + /* Unique identifier for this request */ + private long id; + + private LatencyRequest(long ms) { + if (ms <= 0) { + throw new IllegalArgumentException("Non-positive latency: " + + ms); + } + this.latency = ms; + synchronized (lock) { + this.id = ++counter; + if (requests == null) { + requests = new TreeSet(); + } + requests.add(this); + adjustLatencyIfNeeded(); + } + } + + /** + * Cancels this latency request. + * + * @throws IllegalStateException + * If this request has already been cancelled + */ + public void cancel() { + synchronized (lock) { + if (this.latency == NO_TARGET) { + throw new IllegalStateException("Request already" + + " cancelled"); + } + if (!requests.remove(this)) { + throw new InternalError("Latency request " + + this + " not found"); + } + if (requests.isEmpty()) requests = null; + this.latency = NO_TARGET; + adjustLatencyIfNeeded(); + } + } + + public int compareTo(LatencyRequest r) { + long d = this.latency - r.latency; + if (d == 0) d = this.id - r.id; + return (d < 0) ? -1 : ((d > 0) ? +1 : 0); + } + + public String toString() { + return (LatencyRequest.class.getName() + + "[" + latency + "," + id + "]"); + } + + } + + + /** + * Makes a new request for a garbage-collection latency of the given + * number of real-time milliseconds. A low-priority daemon thread makes a + * best effort to ensure that the maximum object-inspection age never + * exceeds the smallest of the currently active requests. + * + * @param latency + * The requested latency + * + * @throws IllegalArgumentException + * If the given latency is non-positive + */ + public static LatencyRequest requestLatency(long latency) { + return new LatencyRequest(latency); + } + + + /** + * Returns the current smallest garbage-collection latency request, or zero + * if there are no active requests. + */ + public static long currentLatencyTarget() { + long t = latencyTarget; + return (t == NO_TARGET) ? 0 : t; + } + +} diff --git a/src/sun/misc/HexDumpEncoder.java b/src/sun/misc/HexDumpEncoder.java new file mode 100644 index 00000000..174b028a --- /dev/null +++ b/src/sun/misc/HexDumpEncoder.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1995, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.misc; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * This class encodes a buffer into the classic: "Hexadecimal Dump" format of + * the past. It is useful for analyzing the contents of binary buffers. + * The format produced is as follows: + *

    + * xxxx: 00 11 22 33 44 55 66 77   88 99 aa bb cc dd ee ff ................
    + * 
    + * Where xxxx is the offset into the buffer in 16 byte chunks, followed + * by ascii coded hexadecimal bytes followed by the ASCII representation of + * the bytes or '.' if they are not valid bytes. + * + * @author Chuck McManis + */ + +public class HexDumpEncoder extends CharacterEncoder { + + private int offset; + private int thisLineLength; + private int currentByte; + private byte thisLine[] = new byte[16]; + + static void hexDigit(PrintStream p, byte x) { + char c; + + c = (char) ((x >> 4) & 0xf); + if (c > 9) + c = (char) ((c-10) + 'A'); + else + c = (char)(c + '0'); + p.write(c); + c = (char) (x & 0xf); + if (c > 9) + c = (char)((c-10) + 'A'); + else + c = (char)(c + '0'); + p.write(c); + } + + protected int bytesPerAtom() { + return (1); + } + + protected int bytesPerLine() { + return (16); + } + + protected void encodeBufferPrefix(OutputStream o) throws IOException { + offset = 0; + super.encodeBufferPrefix(o); + } + + protected void encodeLinePrefix(OutputStream o, int len) throws IOException { + hexDigit(pStream, (byte)((offset >>> 8) & 0xff)); + hexDigit(pStream, (byte)(offset & 0xff)); + pStream.print(": "); + currentByte = 0; + thisLineLength = len; + } + + protected void encodeAtom(OutputStream o, byte buf[], int off, int len) throws IOException { + thisLine[currentByte] = buf[off]; + hexDigit(pStream, buf[off]); + pStream.print(" "); + currentByte++; + if (currentByte == 8) + pStream.print(" "); + } + + protected void encodeLineSuffix(OutputStream o) throws IOException { + if (thisLineLength < 16) { + for (int i = thisLineLength; i < 16; i++) { + pStream.print(" "); + if (i == 7) + pStream.print(" "); + } + } + pStream.print(" "); + for (int i = 0; i < thisLineLength; i++) { + if ((thisLine[i] < ' ') || (thisLine[i] > 'z')) { + pStream.print("."); + } else { + pStream.write(thisLine[i]); + } + } + pStream.println(); + offset += thisLineLength; + } + +} diff --git a/src/sun/misc/IOUtils.java b/src/sun/misc/IOUtils.java new file mode 100644 index 00000000..57f89189 --- /dev/null +++ b/src/sun/misc/IOUtils.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * IOUtils: A collection of IO-related public static methods. + */ + +package sun.misc; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +public class IOUtils { + + /** + * Read up to length of bytes from in + * until EOF is detected. + * @param in input stream, must not be null + * @param length number of bytes to read, -1 or Integer.MAX_VALUE means + * read as much as possible + * @param readAll if true, an EOFException will be thrown if not enough + * bytes are read. Ignored when length is -1 or Integer.MAX_VALUE + * @return bytes read + * @throws IOException Any IO error or a premature EOF is detected + */ + public static byte[] readFully(InputStream is, int length, boolean readAll) + throws IOException { + byte[] output = {}; + if (length == -1) length = Integer.MAX_VALUE; + int pos = 0; + while (pos < length) { + int bytesToRead; + if (pos >= output.length) { // Only expand when there's no room + bytesToRead = Math.min(length - pos, output.length + 1024); + if (output.length < pos + bytesToRead) { + output = Arrays.copyOf(output, pos + bytesToRead); + } + } else { + bytesToRead = output.length - pos; + } + int cc = is.read(output, pos, bytesToRead); + if (cc < 0) { + if (readAll && length != Integer.MAX_VALUE) { + throw new EOFException("Detect premature EOF"); + } else { + if (output.length != pos) { + output = Arrays.copyOf(output, pos); + } + break; + } + } + pos += cc; + } + return output; + } +} diff --git a/src/sun/misc/InnocuousThread.java b/src/sun/misc/InnocuousThread.java new file mode 100644 index 00000000..db431029 --- /dev/null +++ b/src/sun/misc/InnocuousThread.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.security.AccessControlContext; +import java.security.ProtectionDomain; + +/** + * A thread that has no permissions, is not a member of any user-defined + * ThreadGroup and supports the ability to erase ThreadLocals. + * + * @implNote Based on the implementation of InnocuousForkJoinWorkerThread. + */ +public final class InnocuousThread extends Thread { + private static final Unsafe UNSAFE; + private static final ThreadGroup THREADGROUP; + private static final AccessControlContext ACC; + private static final long THREADLOCALS; + private static final long INHERITABLETHREADLOCALS; + private static final long INHERITEDACCESSCONTROLCONTEXT; + + public InnocuousThread(Runnable target) { + super(THREADGROUP, target, "anInnocuousThread"); + UNSAFE.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, ACC); + eraseThreadLocals(); + } + + @Override + public ClassLoader getContextClassLoader() { + // always report system class loader + return ClassLoader.getSystemClassLoader(); + } + + @Override + public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { + // silently fail + } + + @Override + public void setContextClassLoader(ClassLoader cl) { + throw new SecurityException("setContextClassLoader"); + } + + // ensure run method is run only once + private volatile boolean hasRun; + + @Override + public void run() { + if (Thread.currentThread() == this && !hasRun) { + hasRun = true; + super.run(); + } + } + + /** + * Drops all thread locals (and inherited thread locals). + */ + public void eraseThreadLocals() { + UNSAFE.putObject(this, THREADLOCALS, null); + UNSAFE.putObject(this, INHERITABLETHREADLOCALS, null); + } + + // Use Unsafe to access Thread group and ThreadGroup parent fields + static { + try { + ACC = new AccessControlContext(new ProtectionDomain[] { + new ProtectionDomain(null, null) + }); + + // Find and use topmost ThreadGroup as parent of new group + UNSAFE = Unsafe.getUnsafe(); + Class tk = Thread.class; + Class gk = ThreadGroup.class; + + THREADLOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocals")); + INHERITABLETHREADLOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("inheritableThreadLocals")); + INHERITEDACCESSCONTROLCONTEXT = UNSAFE.objectFieldOffset + (tk.getDeclaredField("inheritedAccessControlContext")); + + long tg = UNSAFE.objectFieldOffset(tk.getDeclaredField("group")); + long gp = UNSAFE.objectFieldOffset(gk.getDeclaredField("parent")); + ThreadGroup group = (ThreadGroup) + UNSAFE.getObject(Thread.currentThread(), tg); + + while (group != null) { + ThreadGroup parent = (ThreadGroup)UNSAFE.getObject(group, gp); + if (parent == null) + break; + group = parent; + } + THREADGROUP = new ThreadGroup(group, "InnocuousThreadGroup"); + } catch (Exception e) { + throw new Error(e); + } + } +} diff --git a/src/sun/misc/InvalidJarIndexException.java b/src/sun/misc/InvalidJarIndexException.java new file mode 100644 index 00000000..427491db --- /dev/null +++ b/src/sun/misc/InvalidJarIndexException.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * Thrown if the URLClassLoader finds the INDEX.LIST file of + * a jar file contains incorrect information. + * + * @author Zhenghua Li + * @since 1.3 + */ + +public +class InvalidJarIndexException extends RuntimeException { + + static final long serialVersionUID = -6159797516569680148L; + + /** + * Constructs an InvalidJarIndexException with no + * detail message. + */ + public InvalidJarIndexException() { + super(); + } + + /** + * Constructs an InvalidJarIndexException with the + * specified detail message. + * + * @param s the detail message. + */ + public InvalidJarIndexException(String s) { + super(s); + } +} diff --git a/src/sun/misc/JarFilter.java b/src/sun/misc/JarFilter.java new file mode 100644 index 00000000..0af055f7 --- /dev/null +++ b/src/sun/misc/JarFilter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.File; +import java.io.FilenameFilter; + +/** + *

    + * This class checks that only jar and zip files are included in the file list. + * This class is used in extension installation support (ExtensionDependency). + *

    + * + * @author Michael Colburn + */ +public class JarFilter implements FilenameFilter { + + public boolean accept(File dir, String name) { + String lower = name.toLowerCase(); + return lower.endsWith(".jar") || lower.endsWith(".zip"); + } +} diff --git a/src/sun/misc/JarIndex.java b/src/sun/misc/JarIndex.java new file mode 100644 index 00000000..6cf876b8 --- /dev/null +++ b/src/sun/misc/JarIndex.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * This class is used to maintain mappings from packages, classes + * and resources to their enclosing JAR files. Mappings are kept + * at the package level except for class or resource files that + * are located at the root directory. URLClassLoader uses the mapping + * information to determine where to fetch an extension class or + * resource from. + * + * @author Zhenghua Li + * @since 1.3 + */ + +public class JarIndex { + + /** + * The hash map that maintains mappings from + * package/classe/resource to jar file list(s) + */ + private HashMap> indexMap; + + /** + * The hash map that maintains mappings from + * jar file to package/class/resource lists + */ + private HashMap> jarMap; + + /* + * An ordered list of jar file names. + */ + private String[] jarFiles; + + /** + * The index file name. + */ + public static final String INDEX_NAME = "META-INF/INDEX.LIST"; + + /** + * true if, and only if, sun.misc.JarIndex.metaInfFilenames is set to true. + * If true, the names of the files in META-INF, and its subdirectories, will + * be added to the index. Otherwise, just the directory names are added. + */ + private static final boolean metaInfFilenames = + "true".equals(System.getProperty("sun.misc.JarIndex.metaInfFilenames")); + + /** + * Constructs a new, empty jar index. + */ + public JarIndex() { + indexMap = new HashMap<>(); + jarMap = new HashMap<>(); + } + + /** + * Constructs a new index from the specified input stream. + * + * @param is the input stream containing the index data + */ + public JarIndex(InputStream is) throws IOException { + this(); + read(is); + } + + /** + * Constructs a new index for the specified list of jar files. + * + * @param files the list of jar files to construct the index from. + */ + public JarIndex(String[] files) throws IOException { + this(); + this.jarFiles = files; + parseJars(files); + } + + /** + * Returns the jar index, or null if none. + * + * This single parameter version of the method is retained + * for binary compatibility with earlier releases. + * + * @param jar the JAR file to get the index from. + * @exception IOException if an I/O error has occurred. + */ + public static JarIndex getJarIndex(JarFile jar) throws IOException { + return getJarIndex(jar, null); + } + + /** + * Returns the jar index, or null if none. + * + * @param jar the JAR file to get the index from. + * @exception IOException if an I/O error has occurred. + */ + public static JarIndex getJarIndex(JarFile jar, MetaIndex metaIndex) throws IOException { + JarIndex index = null; + /* If metaIndex is not null, check the meta index to see + if META-INF/INDEX.LIST is contained in jar file or not. + */ + if (metaIndex != null && + !metaIndex.mayContain(INDEX_NAME)) { + return null; + } + JarEntry e = jar.getJarEntry(INDEX_NAME); + // if found, then load the index + if (e != null) { + index = new JarIndex(jar.getInputStream(e)); + } + return index; + } + + /** + * Returns the jar files that are defined in this index. + */ + public String[] getJarFiles() { + return jarFiles; + } + + /* + * Add the key, value pair to the hashmap, the value will + * be put in a linked list which is created if necessary. + */ + private void addToList(String key, String value, + HashMap> t) { + LinkedList list = t.get(key); + if (list == null) { + list = new LinkedList<>(); + list.add(value); + t.put(key, list); + } else if (!list.contains(value)) { + list.add(value); + } + } + + /** + * Returns the list of jar files that are mapped to the file. + * + * @param fileName the key of the mapping + */ + public LinkedList get(String fileName) { + LinkedList jarFiles = null; + if ((jarFiles = indexMap.get(fileName)) == null) { + /* try the package name again */ + int pos; + if((pos = fileName.lastIndexOf("/")) != -1) { + jarFiles = indexMap.get(fileName.substring(0, pos)); + } + } + return jarFiles; + } + + /** + * Add the mapping from the specified file to the specified + * jar file. If there were no mapping for the package of the + * specified file before, a new linked list will be created, + * the jar file is added to the list and a new mapping from + * the package to the jar file list is added to the hashmap. + * Otherwise, the jar file will be added to the end of the + * existing list. + * + * @param fileName the file name + * @param jarName the jar file that the file is mapped to + * + */ + public void add(String fileName, String jarName) { + String packageName; + int pos; + if((pos = fileName.lastIndexOf("/")) != -1) { + packageName = fileName.substring(0, pos); + } else { + packageName = fileName; + } + + addMapping(packageName, jarName); + } + + /** + * Same as add(String,String) except that it doesn't strip off from the + * last index of '/'. It just adds the jarItem (filename or package) + * as it is received. + */ + private void addMapping(String jarItem, String jarName) { + // add the mapping to indexMap + addToList(jarItem, jarName, indexMap); + + // add the mapping to jarMap + addToList(jarName, jarItem, jarMap); + } + + /** + * Go through all the jar files and construct the + * index table. + */ + private void parseJars(String[] files) throws IOException { + if (files == null) { + return; + } + + String currentJar = null; + + for (int i = 0; i < files.length; i++) { + currentJar = files[i]; + ZipFile zrf = new ZipFile(currentJar.replace + ('/', File.separatorChar)); + + Enumeration entries = zrf.entries(); + while(entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String fileName = entry.getName(); + + // Skip the META-INF directory, the index, and manifest. + // Any files in META-INF/ will be indexed explicitly + if (fileName.equals("META-INF/") || + fileName.equals(INDEX_NAME) || + fileName.equals(JarFile.MANIFEST_NAME)) + continue; + + if (!metaInfFilenames || !fileName.startsWith("META-INF/")) { + add(fileName, currentJar); + } else if (!entry.isDirectory()) { + // Add files under META-INF explicitly so that certain + // services, like ServiceLoader, etc, can be located + // with greater accuracy. Directories can be skipped + // since each file will be added explicitly. + addMapping(fileName, currentJar); + } + } + + zrf.close(); + } + } + + /** + * Writes the index to the specified OutputStream + * + * @param out the output stream + * @exception IOException if an I/O error has occurred + */ + public void write(OutputStream out) throws IOException { + BufferedWriter bw = new BufferedWriter + (new OutputStreamWriter(out, "UTF8")); + bw.write("JarIndex-Version: 1.0\n\n"); + + if (jarFiles != null) { + for (int i = 0; i < jarFiles.length; i++) { + /* print out the jar file name */ + String jar = jarFiles[i]; + bw.write(jar + "\n"); + LinkedList jarlist = jarMap.get(jar); + if (jarlist != null) { + Iterator listitr = jarlist.iterator(); + while(listitr.hasNext()) { + bw.write(listitr.next() + "\n"); + } + } + bw.write("\n"); + } + bw.flush(); + } + } + + + /** + * Reads the index from the specified InputStream. + * + * @param is the input stream + * @exception IOException if an I/O error has occurred + */ + public void read(InputStream is) throws IOException { + BufferedReader br = new BufferedReader + (new InputStreamReader(is, "UTF8")); + String line = null; + String currentJar = null; + + /* an ordered list of jar file names */ + Vector jars = new Vector<>(); + + /* read until we see a .jar line */ + while((line = br.readLine()) != null && !line.endsWith(".jar")); + + for(;line != null; line = br.readLine()) { + if (line.length() == 0) + continue; + + if (line.endsWith(".jar")) { + currentJar = line; + jars.add(currentJar); + } else { + String name = line; + addMapping(name, currentJar); + } + } + + jarFiles = jars.toArray(new String[jars.size()]); + } + + /** + * Merges the current index into another index, taking into account + * the relative path of the current index. + * + * @param toIndex The destination index which the current index will + * merge into. + * @param path The relative path of the this index to the destination + * index. + * + */ + public void merge(JarIndex toIndex, String path) { + Iterator>> itr = indexMap.entrySet().iterator(); + while(itr.hasNext()) { + Map.Entry> e = itr.next(); + String packageName = e.getKey(); + LinkedList from_list = e.getValue(); + Iterator listItr = from_list.iterator(); + while(listItr.hasNext()) { + String jarName = listItr.next(); + if (path != null) { + jarName = path.concat(jarName); + } + toIndex.addMapping(packageName, jarName); + } + } + } +} diff --git a/src/sun/misc/JavaAWTAccess.java b/src/sun/misc/JavaAWTAccess.java new file mode 100644 index 00000000..7b24dc45 --- /dev/null +++ b/src/sun/misc/JavaAWTAccess.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +public interface JavaAWTAccess { + + // Returns the AppContext used for applet logging isolation, or null if + // no isolation is required. + // If there's no applet, or if the caller is a stand alone application, + // or running in the main app context, returns null. + // Otherwise, returns the AppContext of the calling applet. + public Object getAppletContext(); +} diff --git a/src/sun/misc/JavaIOAccess.java b/src/sun/misc/JavaIOAccess.java new file mode 100644 index 00000000..ab2888bf --- /dev/null +++ b/src/sun/misc/JavaIOAccess.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; +import java.io.Console; +import java.nio.charset.Charset; + +public interface JavaIOAccess { + public Console console(); + public Charset charset(); +} diff --git a/src/sun/misc/JavaIOFileDescriptorAccess.java b/src/sun/misc/JavaIOFileDescriptorAccess.java new file mode 100644 index 00000000..9e987e6c --- /dev/null +++ b/src/sun/misc/JavaIOFileDescriptorAccess.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.FileDescriptor; + +/* + * @author Chris Hegarty + */ + +public interface JavaIOFileDescriptorAccess { + public void set(FileDescriptor obj, int fd); + public int get(FileDescriptor fd); + + // Only valid on Windows + public void setHandle(FileDescriptor obj, long handle); + public long getHandle(FileDescriptor obj); +} diff --git a/src/sun/misc/JavaLangAccess.java b/src/sun/misc/JavaLangAccess.java new file mode 100644 index 00000000..3a7cda20 --- /dev/null +++ b/src/sun/misc/JavaLangAccess.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Executable; +import java.security.AccessControlContext; +import java.util.Map; + +import sun.nio.ch.Interruptible; +import sun.reflect.ConstantPool; +import sun.reflect.annotation.AnnotationType; + +public interface JavaLangAccess { + /** Return the constant pool for a class. */ + ConstantPool getConstantPool(Class klass); + + /** + * Compare-And-Swap the AnnotationType instance corresponding to this class. + * (This method only applies to annotation types.) + */ + boolean casAnnotationType(Class klass, AnnotationType oldType, AnnotationType newType); + + /** + * Get the AnnotationType instance corresponding to this class. + * (This method only applies to annotation types.) + */ + AnnotationType getAnnotationType(Class klass); + + /** + * Get the declared annotations for a given class, indexed by their types. + */ + Map, Annotation> getDeclaredAnnotationMap(Class klass); + + /** + * Get the array of bytes that is the class-file representation + * of this Class' annotations. + */ + byte[] getRawClassAnnotations(Class klass); + + /** + * Get the array of bytes that is the class-file representation + * of this Class' type annotations. + */ + byte[] getRawClassTypeAnnotations(Class klass); + + /** + * Get the array of bytes that is the class-file representation + * of this Executable's type annotations. + */ + byte[] getRawExecutableTypeAnnotations(Executable executable); + + /** + * Returns the elements of an enum class or null if the + * Class object does not represent an enum type; + * the result is uncloned, cached, and shared by all callers. + */ + > E[] getEnumConstantsShared(Class klass); + + /** Set thread's blocker field. */ + void blockedOn(Thread t, Interruptible b); + + /** + * Registers a shutdown hook. + * + * It is expected that this method with registerShutdownInProgress=true + * is only used to register DeleteOnExitHook since the first file + * may be added to the delete on exit list by the application shutdown + * hooks. + * + * @params slot the slot in the shutdown hook array, whose element + * will be invoked in order during shutdown + * @params registerShutdownInProgress true to allow the hook + * to be registered even if the shutdown is in progress. + * @params hook the hook to be registered + * + * @throw IllegalStateException if shutdown is in progress and + * the slot is not valid to register. + */ + void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook); + + /** + * Returns the number of stack frames represented by the given throwable. + */ + int getStackTraceDepth(Throwable t); + + /** + * Returns the ith StackTraceElement for the given throwable. + */ + StackTraceElement getStackTraceElement(Throwable t, int i); + + /** + * Returns a new string backed by the provided character array. The + * character array is not copied and must never be modified after the + * String is created, in order to fulfill String's contract. + * + * @param chars the character array to back the string + * @return a newly created string whose content is the character array + */ + String newStringUnsafe(char[] chars); + + /** + * Returns a new Thread with the given Runnable and an + * inherited AccessControlContext. + */ + Thread newThreadWithAcc(Runnable target, AccessControlContext acc); + + /** + * Invokes the finalize method of the given object. + */ + void invokeFinalize(Object o) throws Throwable; +} diff --git a/src/sun/misc/JavaNetAccess.java b/src/sun/misc/JavaNetAccess.java new file mode 100644 index 00000000..cc7bec12 --- /dev/null +++ b/src/sun/misc/JavaNetAccess.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.net.URLClassLoader; + +public interface JavaNetAccess { + /** + * return the URLClassPath belonging to the given loader + */ + URLClassPath getURLClassPath (URLClassLoader u); +} diff --git a/src/sun/misc/JavaNetHttpCookieAccess.java b/src/sun/misc/JavaNetHttpCookieAccess.java new file mode 100644 index 00000000..8149a222 --- /dev/null +++ b/src/sun/misc/JavaNetHttpCookieAccess.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.net.HttpCookie; +import java.util.List; + +public interface JavaNetHttpCookieAccess { + /* + * Constructs cookies from Set-Cookie or Set-Cookie2 header string, + * retaining the original header String in the cookie itself. + */ + public List parse(String header); + + /* + * Returns the original header this cookie was consructed from, if it was + * constructed by parsing a header, otherwise null. + */ + public String header(HttpCookie cookie); +} + diff --git a/src/sun/misc/JavaNioAccess.java b/src/sun/misc/JavaNioAccess.java new file mode 100644 index 00000000..e4f0a781 --- /dev/null +++ b/src/sun/misc/JavaNioAccess.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.nio.Buffer; +import java.nio.ByteBuffer; + +public interface JavaNioAccess { + /** + * Provides access to information on buffer usage. + */ + interface BufferPool { + String getName(); + long getCount(); + long getTotalCapacity(); + long getMemoryUsed(); + } + BufferPool getDirectBufferPool(); + + /** + * Constructs a direct ByteBuffer referring to the block of memory starting + * at the given memory address and and extending {@code cap} bytes. + * The {@code ob} parameter is an arbitrary object that is attached + * to the resulting buffer. + */ + ByteBuffer newDirectByteBuffer(long addr, int cap, Object ob); + + /** + * Truncates a buffer by changing its capacity to 0. + */ + void truncate(Buffer buf); + +} diff --git a/src/sun/misc/JavaSecurityAccess.java b/src/sun/misc/JavaSecurityAccess.java new file mode 100644 index 00000000..ac305826 --- /dev/null +++ b/src/sun/misc/JavaSecurityAccess.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.security.AccessControlContext; +import java.security.PrivilegedAction; + +public interface JavaSecurityAccess { + + T doIntersectionPrivilege(PrivilegedAction action, + AccessControlContext stack, + AccessControlContext context); + + T doIntersectionPrivilege(PrivilegedAction action, + AccessControlContext context); + +} diff --git a/src/sun/misc/JavaSecurityProtectionDomainAccess.java b/src/sun/misc/JavaSecurityProtectionDomainAccess.java new file mode 100644 index 00000000..95560ffa --- /dev/null +++ b/src/sun/misc/JavaSecurityProtectionDomainAccess.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.security.PermissionCollection; +import java.security.ProtectionDomain; + +public interface JavaSecurityProtectionDomainAccess { + interface ProtectionDomainCache { + void put(ProtectionDomain pd, PermissionCollection pc); + PermissionCollection get(ProtectionDomain pd); + } + /** + * Returns the ProtectionDomainCache. + */ + ProtectionDomainCache getProtectionDomainCache(); +} diff --git a/src/sun/misc/JavaSecuritySignatureAccess.java b/src/sun/misc/JavaSecuritySignatureAccess.java new file mode 100644 index 00000000..b0ca7351 --- /dev/null +++ b/src/sun/misc/JavaSecuritySignatureAccess.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.spec.AlgorithmParameterSpec; + +public interface JavaSecuritySignatureAccess { + + void initVerify(Signature s, PublicKey publicKey, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + void initVerify(Signature s, java.security.cert.Certificate certificate, + AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException; + + void initSign(Signature s, PrivateKey privateKey, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException; +} diff --git a/src/sun/misc/JavaUtilJarAccess.java b/src/sun/misc/JavaUtilJarAccess.java new file mode 100644 index 00000000..3453855d --- /dev/null +++ b/src/sun/misc/JavaUtilJarAccess.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.IOException; +import java.net.URL; +import java.security.CodeSource; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public interface JavaUtilJarAccess { + public boolean jarFileHasClassPathAttribute(JarFile jar) throws IOException; + public CodeSource[] getCodeSources(JarFile jar, URL url); + public CodeSource getCodeSource(JarFile jar, URL url, String name); + public Enumeration entryNames(JarFile jar, CodeSource[] cs); + public Enumeration entries2(JarFile jar); + public void setEagerValidation(JarFile jar, boolean eager); + public List getManifestDigests(JarFile jar); +} diff --git a/src/sun/misc/JavaUtilZipFileAccess.java b/src/sun/misc/JavaUtilZipFileAccess.java new file mode 100644 index 00000000..0534f340 --- /dev/null +++ b/src/sun/misc/JavaUtilZipFileAccess.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.zip.ZipFile; + +public interface JavaUtilZipFileAccess { + public boolean startsWithLocHeader(ZipFile zip); +} + diff --git a/src/sun/misc/LRUCache.java b/src/sun/misc/LRUCache.java new file mode 100644 index 00000000..8c14c174 --- /dev/null +++ b/src/sun/misc/LRUCache.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * Utility class for small LRU caches. + * + * @author Mark Reinhold + */ +public abstract class LRUCache { + + private V[] oa = null; + private final int size; + + public LRUCache(int size) { + this.size = size; + } + + abstract protected V create(N name); + + abstract protected boolean hasName(V ob, N name); + + public static void moveToFront(Object[] oa, int i) { + Object ob = oa[i]; + for (int j = i; j > 0; j--) + oa[j] = oa[j - 1]; + oa[0] = ob; + } + + public V forName(N name) { + if (oa == null) { + @SuppressWarnings("unchecked") + V[] temp = (V[])new Object[size]; + oa = temp; + } else { + for (int i = 0; i < oa.length; i++) { + V ob = oa[i]; + if (ob == null) + continue; + if (hasName(ob, name)) { + if (i > 0) + moveToFront(oa, i); + return ob; + } + } + } + + // Create a new object + V ob = create(name); + oa[oa.length - 1] = ob; + moveToFront(oa, oa.length - 1); + return ob; + } + +} diff --git a/src/sun/misc/Launcher.java b/src/sun/misc/Launcher.java new file mode 100644 index 00000000..78868fb5 --- /dev/null +++ b/src/sun/misc/Launcher.java @@ -0,0 +1,607 @@ +/* + * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.security.ProtectionDomain; +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Vector; + +import sun.net.www.ParseUtil; +import sun.security.util.SecurityConstants; + +/** + * This class is used by the system to launch the main application. +Launcher */ +public class Launcher { + private static URLStreamHandlerFactory factory = new Factory(); + private static Launcher launcher = new Launcher(); + private static String bootClassPath = + System.getProperty("sun.boot.class.path"); + + public static Launcher getLauncher() { + return launcher; + } + + private ClassLoader loader; + + public Launcher() { + // Create the extension class loader + ClassLoader extcl; + try { + extcl = ExtClassLoader.getExtClassLoader(); + } catch (IOException e) { + throw new InternalError( + "Could not create extension class loader", e); + } + + // Now create the class loader to use to launch the application + try { + loader = AppClassLoader.getAppClassLoader(extcl); + } catch (IOException e) { + throw new InternalError( + "Could not create application class loader", e); + } + + // Also set the context class loader for the primordial thread. + Thread.currentThread().setContextClassLoader(loader); + + // Finally, install a security manager if requested + String s = System.getProperty("java.security.manager"); + if (s != null) { + SecurityManager sm = null; + if ("".equals(s) || "default".equals(s)) { + sm = new SecurityManager(); + } else { + try { + sm = (SecurityManager)loader.loadClass(s).newInstance(); + } catch (IllegalAccessException e) { + } catch (InstantiationException e) { + } catch (ClassNotFoundException e) { + } catch (ClassCastException e) { + } + } + if (sm != null) { + System.setSecurityManager(sm); + } else { + throw new InternalError( + "Could not create SecurityManager: " + s); + } + } + } + + /* + * Returns the class loader used to launch the main application. + */ + public ClassLoader getClassLoader() { + return loader; + } + + /* + * The class loader used for loading installed extensions. + */ + static class ExtClassLoader extends URLClassLoader { + + static { + ClassLoader.registerAsParallelCapable(); + } + + /** + * create an ExtClassLoader. The ExtClassLoader is created + * within a context that limits which files it can read + */ + public static ExtClassLoader getExtClassLoader() throws IOException + { + final File[] dirs = getExtDirs(); + + try { + // Prior implementations of this doPrivileged() block supplied + // aa synthesized ACC via a call to the private method + // ExtClassLoader.getContext(). + + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public ExtClassLoader run() throws IOException { + int len = dirs.length; + for (int i = 0; i < len; i++) { + MetaIndex.registerDirectory(dirs[i]); + } + return new ExtClassLoader(dirs); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } + + void addExtURL(URL url) { + super.addURL(url); + } + + /* + * Creates a new ExtClassLoader for the specified directories. + */ + public ExtClassLoader(File[] dirs) throws IOException { + super(getExtURLs(dirs), null, factory); + SharedSecrets.getJavaNetAccess(). + getURLClassPath(this).initLookupCache(this); + } + + private static File[] getExtDirs() { + String s = System.getProperty("java.ext.dirs"); + File[] dirs; + if (s != null) { + StringTokenizer st = + new StringTokenizer(s, File.pathSeparator); + int count = st.countTokens(); + dirs = new File[count]; + for (int i = 0; i < count; i++) { + dirs[i] = new File(st.nextToken()); + } + } else { + dirs = new File[0]; + } + return dirs; + } + + private static URL[] getExtURLs(File[] dirs) throws IOException { + Vector urls = new Vector(); + for (int i = 0; i < dirs.length; i++) { + String[] files = dirs[i].list(); + if (files != null) { + for (int j = 0; j < files.length; j++) { + if (!files[j].equals("meta-index")) { + File f = new File(dirs[i], files[j]); + urls.add(getFileURL(f)); + } + } + } + } + URL[] ua = new URL[urls.size()]; + urls.copyInto(ua); + return ua; + } + + /* + * Searches the installed extension directories for the specified + * library name. For each extension directory, we first look for + * the native library in the subdirectory whose name is the value + * of the system property os.arch. Failing that, we + * look in the extension directory itself. + */ + public String findLibrary(String name) { + name = System.mapLibraryName(name); + URL[] urls = super.getURLs(); + File prevDir = null; + for (int i = 0; i < urls.length; i++) { + // Get the ext directory from the URL + File dir = new File(urls[i].getPath()).getParentFile(); + if (dir != null && !dir.equals(prevDir)) { + // Look in architecture-specific subdirectory first + // Read from the saved system properties to avoid deadlock + String arch = VM.getSavedProperty("os.arch"); + if (arch != null) { + File file = new File(new File(dir, arch), name); + if (file.exists()) { + return file.getAbsolutePath(); + } + } + // Then check the extension directory + File file = new File(dir, name); + if (file.exists()) { + return file.getAbsolutePath(); + } + } + prevDir = dir; + } + return null; + } + + private static AccessControlContext getContext(File[] dirs) + throws IOException + { + PathPermissions perms = + new PathPermissions(dirs); + + ProtectionDomain domain = new ProtectionDomain( + new CodeSource(perms.getCodeBase(), + (java.security.cert.Certificate[]) null), + perms); + + AccessControlContext acc = + new AccessControlContext(new ProtectionDomain[] { domain }); + + return acc; + } + } + + /** + * The class loader used for loading from java.class.path. + * runs in a restricted security context. + */ + static class AppClassLoader extends URLClassLoader { + + static { + ClassLoader.registerAsParallelCapable(); + } + + public static ClassLoader getAppClassLoader(final ClassLoader extcl) + throws IOException + { + final String s = System.getProperty("java.class.path"); + final File[] path = (s == null) ? new File[0] : getClassPath(s); + + // Note: on bugid 4256530 + // Prior implementations of this doPrivileged() block supplied + // a rather restrictive ACC via a call to the private method + // AppClassLoader.getContext(). This proved overly restrictive + // when loading classes. Specifically it prevent + // accessClassInPackage.sun.* grants from being honored. + // + return AccessController.doPrivileged( + new PrivilegedAction() { + public AppClassLoader run() { + URL[] urls = + (s == null) ? new URL[0] : pathToURLs(path); + return new AppClassLoader(urls, extcl); + } + }); + } + + final URLClassPath ucp; + + /* + * Creates a new AppClassLoader + */ + AppClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent, factory); + ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this); + ucp.initLookupCache(this); + } + + /** + * Override loadClass so we can checkPackageAccess. + */ + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + int i = name.lastIndexOf('.'); + if (i != -1) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPackageAccess(name.substring(0, i)); + } + } + + if (ucp.knownToNotExist(name)) { + // The class of the given name is not found in the parent + // class loader as well as its local URLClassPath. + // Check if this class has already been defined dynamically; + // if so, return the loaded class; otherwise, skip the parent + // delegation and findClass. + Class c = findLoadedClass(name); + if (c != null) { + if (resolve) { + resolveClass(c); + } + return c; + } + throw new ClassNotFoundException(name); + } + + return (super.loadClass(name, resolve)); + } + + /** + * allow any classes loaded from classpath to exit the VM. + */ + protected PermissionCollection getPermissions(CodeSource codesource) + { + PermissionCollection perms = super.getPermissions(codesource); + perms.add(new RuntimePermission("exitVM")); + return perms; + } + + /** + * This class loader supports dynamic additions to the class path + * at runtime. + * + * @see java.lang.instrument.Instrumentation#appendToSystemClassPathSearch + */ + private void appendToClassPathForInstrumentation(String path) { + assert(Thread.holdsLock(this)); + + // addURL is a no-op if path already contains the URL + super.addURL( getFileURL(new File(path)) ); + } + + /** + * create a context that can read any directories (recursively) + * mentioned in the class path. In the case of a jar, it has to + * be the directory containing the jar, not just the jar, as jar + * files might refer to other jar files. + */ + + private static AccessControlContext getContext(File[] cp) + throws MalformedURLException + { + PathPermissions perms = + new PathPermissions(cp); + + ProtectionDomain domain = + new ProtectionDomain(new CodeSource(perms.getCodeBase(), + (java.security.cert.Certificate[]) null), + perms); + + AccessControlContext acc = + new AccessControlContext(new ProtectionDomain[] { domain }); + + return acc; + } + } + + private static class BootClassPathHolder { + static final URLClassPath bcp; + static { + URL[] urls; + if (bootClassPath != null) { + urls = AccessController.doPrivileged( + new PrivilegedAction() { + public URL[] run() { + File[] classPath = getClassPath(bootClassPath); + int len = classPath.length; + Set seenDirs = new HashSet(); + for (int i = 0; i < len; i++) { + File curEntry = classPath[i]; + // Negative test used to properly handle + // nonexistent jars on boot class path + if (!curEntry.isDirectory()) { + curEntry = curEntry.getParentFile(); + } + if (curEntry != null && seenDirs.add(curEntry)) { + MetaIndex.registerDirectory(curEntry); + } + } + return pathToURLs(classPath); + } + } + ); + } else { + urls = new URL[0]; + } + bcp = new URLClassPath(urls, factory); + bcp.initLookupCache(null); + } + } + + public static URLClassPath getBootstrapClassPath() { + return BootClassPathHolder.bcp; + } + + private static URL[] pathToURLs(File[] path) { + URL[] urls = new URL[path.length]; + for (int i = 0; i < path.length; i++) { + urls[i] = getFileURL(path[i]); + } + // DEBUG + //for (int i = 0; i < urls.length; i++) { + // System.out.println("urls[" + i + "] = " + '"' + urls[i] + '"'); + //} + return urls; + } + + private static File[] getClassPath(String cp) { + File[] path; + if (cp != null) { + int count = 0, maxCount = 1; + int pos = 0, lastPos = 0; + // Count the number of separators first + while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { + maxCount++; + lastPos = pos + 1; + } + path = new File[maxCount]; + lastPos = pos = 0; + // Now scan for each path component + while ((pos = cp.indexOf(File.pathSeparator, lastPos)) != -1) { + if (pos - lastPos > 0) { + path[count++] = new File(cp.substring(lastPos, pos)); + } else { + // empty path component translates to "." + path[count++] = new File("."); + } + lastPos = pos + 1; + } + // Make sure we include the last path component + if (lastPos < cp.length()) { + path[count++] = new File(cp.substring(lastPos)); + } else { + path[count++] = new File("."); + } + // Trim array to correct size + if (count != maxCount) { + File[] tmp = new File[count]; + System.arraycopy(path, 0, tmp, 0, count); + path = tmp; + } + } else { + path = new File[0]; + } + // DEBUG + //for (int i = 0; i < path.length; i++) { + // System.out.println("path[" + i + "] = " + '"' + path[i] + '"'); + //} + return path; + } + + private static URLStreamHandler fileHandler; + + static URL getFileURL(File file) { + try { + file = file.getCanonicalFile(); + } catch (IOException e) {} + + try { + return ParseUtil.fileToEncodedURL(file); + } catch (MalformedURLException e) { + // Should never happen since we specify the protocol... + throw new InternalError(e); + } + } + + /* + * The stream handler factory for loading system protocol handlers. + */ + private static class Factory implements URLStreamHandlerFactory { + private static String PREFIX = "sun.net.www.protocol"; + + public URLStreamHandler createURLStreamHandler(String protocol) { + String name = PREFIX + "." + protocol + ".Handler"; + try { + Class c = Class.forName(name); + return (URLStreamHandler)c.newInstance(); + } catch (ReflectiveOperationException e) { + throw new InternalError("could not load " + protocol + + "system protocol handler", e); + } + } + } +} + +class PathPermissions extends PermissionCollection { + // use serialVersionUID from JDK 1.2.2 for interoperability + private static final long serialVersionUID = 8133287259134945693L; + + private File path[]; + private Permissions perms; + + URL codeBase; + + PathPermissions(File path[]) + { + this.path = path; + this.perms = null; + this.codeBase = null; + } + + URL getCodeBase() + { + return codeBase; + } + + public void add(Permission permission) { + throw new SecurityException("attempt to add a permission"); + } + + private synchronized void init() + { + if (perms != null) + return; + + perms = new Permissions(); + + // this is needed to be able to create the classloader itself! + perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); + + // add permission to read any "java.*" property + perms.add(new java.util.PropertyPermission("java.*", + SecurityConstants.PROPERTY_READ_ACTION)); + + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + for (int i=0; i < path.length; i++) { + File f = path[i]; + String path; + try { + path = f.getCanonicalPath(); + } catch (IOException ioe) { + path = f.getAbsolutePath(); + } + if (i == 0) { + codeBase = Launcher.getFileURL(new File(path)); + } + if (f.isDirectory()) { + if (path.endsWith(File.separator)) { + perms.add(new FilePermission(path+"-", + SecurityConstants.FILE_READ_ACTION)); + } else { + perms.add(new FilePermission( + path + File.separator+"-", + SecurityConstants.FILE_READ_ACTION)); + } + } else { + int endIndex = path.lastIndexOf(File.separatorChar); + if (endIndex != -1) { + path = path.substring(0, endIndex+1) + "-"; + perms.add(new FilePermission(path, + SecurityConstants.FILE_READ_ACTION)); + } else { + // XXX? + } + } + } + return null; + } + }); + } + + public boolean implies(Permission permission) { + if (perms == null) + init(); + return perms.implies(permission); + } + + public java.util.Enumeration elements() { + if (perms == null) + init(); + synchronized (perms) { + return perms.elements(); + } + } + + public String toString() { + if (perms == null) + init(); + return perms.toString(); + } +} diff --git a/src/sun/misc/Lock.java b/src/sun/misc/Lock.java new file mode 100644 index 00000000..2b060fee --- /dev/null +++ b/src/sun/misc/Lock.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1994, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * The Lock class provides a simple, useful interface to a lock. + * Unlike monitors which synchronize access to an object, locks + * synchronize access to an arbitrary set of resources (objects, + * methods, variables, etc.).

    + * + * The programmer using locks must be responsible for clearly defining + * the semantics of their use and should handle deadlock avoidance in + * the face of exceptions.

    + * + * For example, if you want to protect a set of method invocations with + * a lock, and one of the methods may throw an exception, you must be + * prepared to release the lock similarly to the following example: + *

    + *      class SomeClass {
    + *          Lock myLock = new Lock();
    +
    + *          void someMethod() {
    + *              myLock.lock();
    + *              try {
    + *                  StartOperation();
    + *                  ContinueOperation();
    + *                  EndOperation();
    + *              } finally {
    + *                  myLock.unlock();
    + *              }
    + *          }
    + *      }
    + * 
    + * + * @author Peter King + */ +public +class Lock { + private boolean locked = false; + + /** + * Create a lock, which is initially not locked. + */ + public Lock () { + } + + /** + * Acquire the lock. If someone else has the lock, wait until it + * has been freed, and then try to acquire it again. This method + * will not return until the lock has been acquired. + * + * @exception InterruptedException if any thread has + * interrupted this thread. + */ + public final synchronized void lock() throws InterruptedException { + while (locked) { + wait(); + } + locked = true; + } + + /** + * Release the lock. If someone else is waiting for the lock, the + * will be notitified so they can try to acquire the lock again. + */ + public final synchronized void unlock() { + locked = false; + notifyAll(); + } +} diff --git a/src/sun/misc/MessageUtils.java b/src/sun/misc/MessageUtils.java new file mode 100644 index 00000000..ec826c05 --- /dev/null +++ b/src/sun/misc/MessageUtils.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1995, 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * MessageUtils: miscellaneous utilities for handling error and status + * properties and messages. + * + * @author Herb Jellinek + */ + +public class MessageUtils { + // can instantiate it for to allow less verbose use - via instance + // instead of classname + + public MessageUtils() { } + + public static String subst(String patt, String arg) { + String args[] = { arg }; + return subst(patt, args); + } + + public static String subst(String patt, String arg1, String arg2) { + String args[] = { arg1, arg2 }; + return subst(patt, args); + } + + public static String subst(String patt, String arg1, String arg2, + String arg3) { + String args[] = { arg1, arg2, arg3 }; + return subst(patt, args); + } + + public static String subst(String patt, String args[]) { + StringBuffer result = new StringBuffer(); + int len = patt.length(); + for (int i = 0; i >= 0 && i < len; i++) { + char ch = patt.charAt(i); + if (ch == '%') { + if (i != len) { + int index = Character.digit(patt.charAt(i + 1), 10); + if (index == -1) { + result.append(patt.charAt(i + 1)); + i++; + } else if (index < args.length) { + result.append(args[index]); + i++; + } + } + } else { + result.append(ch); + } + } + return result.toString(); + } + + public static String substProp(String propName, String arg) { + return subst(System.getProperty(propName), arg); + } + + public static String substProp(String propName, String arg1, String arg2) { + return subst(System.getProperty(propName), arg1, arg2); + } + + public static String substProp(String propName, String arg1, String arg2, + String arg3) { + return subst(System.getProperty(propName), arg1, arg2, arg3); + } + + /** + * Print a message directly to stderr, bypassing all the + * character conversion methods. + * @param msg message to print + */ + public static native void toStderr(String msg); + + /** + * Print a message directly to stdout, bypassing all the + * character conversion methods. + * @param msg message to print + */ + public static native void toStdout(String msg); + + + // Short forms of the above + + public static void err(String s) { + toStderr(s + "\n"); + } + + public static void out(String s) { + toStdout(s + "\n"); + } + + // Print a stack trace to stderr + // + public static void where() { + Throwable t = new Throwable(); + StackTraceElement[] es = t.getStackTrace(); + for (int i = 1; i < es.length; i++) + toStderr("\t" + es[i].toString() + "\n"); + } + +} diff --git a/src/sun/misc/MetaIndex.java b/src/sun/misc/MetaIndex.java new file mode 100644 index 00000000..54b8ee07 --- /dev/null +++ b/src/sun/misc/MetaIndex.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/* + * MetaIndex is intended to decrease startup time (in particular cold + * start, when files are not yet in the disk cache) by providing a + * quick reject mechanism for probes into jar files. The on-disk + * representation of the meta-index is a flat text file with per-jar + * entries indicating (generally speaking) prefixes of package names + * contained in the jar. As an example, here is an edited excerpt of + * the meta-index generated for jre/lib in the current build: + * +
    +% VERSION 1
    +# charsets.jar
    +sun/
    +# jce.jar
    +javax/
    +! jsse.jar
    +sun/
    +com/sun/net/
    +javax/
    +com/sun/security/
    +@ resources.jar
    +com/sun/xml/
    +com/sun/rowset/
    +com/sun/org/
    +sun/
    +com/sun/imageio/
    +javax/
    +com/sun/java/swing/
    +META-INF/services/
    +com/sun/java/util/jar/pack/
    +com/sun/corba/
    +com/sun/jndi/
    +! rt.jar
    +org/w3c/
    +com/sun/imageio/
    +javax/
    +java/
    +sun/
    +...
    +
    + *

    A few notes about the design of the meta-index: + * + *

      + * + *
    • It contains entries for multiple jar files. This is + * intentional, to reduce the number of disk accesses that need to be + * performed during startup. + * + *
    • It is only intended to act as a fast reject mechanism to + * prevent application and other classes from forcing all jar files on + * the boot and extension class paths to be opened. It is not intended + * as a precise index of the contents of the jar. + * + *
    • It should be as small as possible to reduce the amount of time + * required to parse it during startup. For example, adding on the + * secondary package element to java/ and javax/ packages + * ("javax/swing/", for example) causes the meta-index to grow + * significantly. This is why substrings of the packages have been + * chosen as the principal contents. + * + *
    • It is versioned, and optional, to prevent strong dependencies + * between the JVM and JDK. It is also potentially applicable to more + * than just the boot and extension class paths. + * + *
    • Precisely speaking, it plays different role in JVM and J2SE + * side. On the JVM side, meta-index file is used to speed up locating the + * class files only while on the J2SE side, meta-index file is used to speed + * up the resources file & class file. + * To help the JVM and J2SE code to better utilize the information in meta-index + * file, we mark the jar file differently. Here is the current rule we use. + * For jar file containing only class file, we put '!' before the jar file name; + * for jar file containing only resources file, we put '@' before the jar file name; + * for jar file containing both resources and class file, we put '#' before the + * jar name. + * Notice the fact that every jar file contains at least the manifest file, so when + * we say "jar file containing only class file", we don't include that file. + * + *
    + * + *

    To avoid changing the behavior of the current application + * loader and other loaders, the current MetaIndex implementation in + * the JDK requires that the directory containing the meta-index be + * registered with the MetaIndex class before construction of the + * associated URLClassPath. This prevents the need for automatic + * searching for the meta-index in the URLClassPath code and potential + * changes in behavior for non-core ClassLoaders. + * + * This class depends on make/tools/MetaIndex/BuildMetaIndex.java and + * is used principally by sun.misc.URLClassPath. + */ + +public class MetaIndex { + // Maps jar file names in registered directories to meta-indices + private static volatile Map jarMap; + + // List of contents of this meta-index + private String[] contents; + + // Indicate whether the coresponding jar file is a pure class jar file or not + private boolean isClassOnlyJar; + + //---------------------------------------------------------------------- + // Registration of directories (which can cause parsing of the + // meta-index file if it is present), and fetching of parsed + // meta-indices + // jarMap is not strictly thread-safe when the meta index mechanism + // is extended for user-provided jar files in future. + + public static MetaIndex forJar(File jar) { + return getJarMap().get(jar); + } + + // 'synchronized' is added to protect the jarMap from being modified + // by multiple threads. + public static synchronized void registerDirectory(File dir) { + // Note that this does not currently check to see whether the + // directory has previously been registered, since the meta-index + // in a particular directory creates multiple entries in the + // jarMap. If this mechanism is extended beyond the boot and + // extension class paths (for example, automatically searching for + // meta-index files in directories containing jars which have been + // explicitly opened) then this code should be generalized. + // + // This method must be called from a privileged context. + File indexFile = new File(dir, "meta-index"); + if (indexFile.exists()) { + try { + BufferedReader reader = new BufferedReader(new FileReader(indexFile)); + String line = null; + String curJarName = null; + boolean isCurJarContainClassOnly = false; + List contents = new ArrayList(); + Map map = getJarMap(); + + /* Convert dir into canonical form. */ + dir = dir.getCanonicalFile(); + /* Note: The first line should contain the version of + * the meta-index file. We have to match the right version + * before trying to parse this file. */ + line = reader.readLine(); + if (line == null || + !line.equals("% VERSION 2")) { + reader.close(); + return; + } + while ((line = reader.readLine()) != null) { + switch (line.charAt(0)) { + case '!': + case '#': + case '@': { + // Store away current contents, if any + if ((curJarName != null) && (contents.size() > 0)) { + map.put(new File(dir, curJarName), + new MetaIndex(contents, + isCurJarContainClassOnly)); + + contents.clear(); + } + // Fetch new current jar file name + curJarName = line.substring(2); + if (line.charAt(0) == '!') { + isCurJarContainClassOnly = true; + } else if (isCurJarContainClassOnly) { + isCurJarContainClassOnly = false; + } + + break; + } + case '%': + break; + default: { + contents.add(line); + } + } + } + // Store away current contents, if any + if ((curJarName != null) && (contents.size() > 0)) { + map.put(new File(dir, curJarName), + new MetaIndex(contents, isCurJarContainClassOnly)); + } + + reader.close(); + + } catch (IOException e) { + // Silently fail for now (similar behavior to elsewhere in + // extension and core loaders) + } + } + } + + //---------------------------------------------------------------------- + // Public APIs + // + + public boolean mayContain(String entry) { + // Ask non-class file from class only jar returns false + // This check is important to avoid some class only jar + // files such as rt.jar are opened for resource request. + if (isClassOnlyJar && !entry.endsWith(".class")){ + return false; + } + + String[] conts = contents; + for (int i = 0; i < conts.length; i++) { + if (entry.startsWith(conts[i])) { + return true; + } + } + return false; + } + + + //---------------------------------------------------------------------- + // Implementation only below this point + // @IllegalArgumentException if entries is null. + private MetaIndex(List entries, boolean isClassOnlyJar) + throws IllegalArgumentException { + if (entries == null) { + throw new IllegalArgumentException(); + } + + contents = entries.toArray(new String[0]); + this.isClassOnlyJar = isClassOnlyJar; + } + + private static Map getJarMap() { + if (jarMap == null) { + synchronized (MetaIndex.class) { + if (jarMap == null) { + jarMap = new HashMap(); + } + } + } + assert jarMap != null; + return jarMap; + } +} diff --git a/src/sun/misc/NativeSignalHandler.java b/src/sun/misc/NativeSignalHandler.java new file mode 100644 index 00000000..39c68c29 --- /dev/null +++ b/src/sun/misc/NativeSignalHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/* A package-private class implementing a signal handler in native code. */ + +final class NativeSignalHandler implements SignalHandler { + + private final long handler; + + long getHandler() { + return handler; + } + + NativeSignalHandler(long handler) { + this.handler = handler; + } + + public void handle(Signal sig) { + handle0(sig.getNumber(), handler); + } + + private static native void handle0(int number, long handler); +} diff --git a/src/sun/misc/Perf.java b/src/sun/misc/Perf.java new file mode 100644 index 00000000..0252f38e --- /dev/null +++ b/src/sun/misc/Perf.java @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.security.Permission; +import java.security.PrivilegedAction; + +/** + * The Perf class provides the ability to attach to an instrumentation + * buffer maintained by a Java virtual machine. The instrumentation + * buffer may be for the Java virtual machine running the methods of + * this class or it may be for another Java virtual machine on the + * same system. + *

    + * In addition, this class provides methods to create instrumentation + * objects in the instrumentation buffer for the Java virtual machine + * that is running these methods. It also contains methods for acquiring + * the value of a platform specific high resolution clock for time + * stamp and interval measurement purposes. + * + * @author Brian Doherty + * @since 1.4.2 + * @see #getPerf + * @see sun.misc.Perf$GetPerfAction + * @see ByteBuffer + */ +public final class Perf { + + private static Perf instance; + + private static final int PERF_MODE_RO = 0; + private static final int PERF_MODE_RW = 1; + + private Perf() { } // prevent instantiation + + /** + * The GetPerfAction class is a convenience class for acquiring access + * to the singleton Perf instance using the + * AccessController.doPrivileged() method. + *

    + * An instance of this class can be used as the argument to + * AccessController.doPrivileged(PrivilegedAction). + *

    Here is a suggested idiom for use of this class: + * + *

    +     * class MyTrustedClass {
    +     *   private static final Perf perf =
    +     *       AccessController.doPrivileged(new Perf.GetPerfAction());
    +     *   ...
    +     * }
    +     * 
    + *

    + * In the presence of a security manager, the MyTrustedClass + * class in the above example will need to be granted the + * "sun.misc.Perf.getPerf" RuntimePermission + * permission in order to successfully acquire the singleton Perf instance. + *

    + * Please note that the "sun.misc.Perf.getPerf" permission + * is not a JDK specified permission. + * + * @see java.security.AccessController#doPrivileged(PrivilegedAction) + * @see RuntimePermission + */ + public static class GetPerfAction implements PrivilegedAction + { + /** + * Run the Perf.getPerf() method in a privileged context. + * + * @see #getPerf + */ + public Perf run() { + return getPerf(); + } + } + + /** + * Return a reference to the singleton Perf instance. + *

    + * The getPerf() method returns the singleton instance of the Perf + * class. The returned object provides the caller with the capability + * for accessing the instrumentation buffer for this or another local + * Java virtual machine. + *

    + * If a security manager is installed, its checkPermission + * method is called with a RuntimePermission with a target + * of "sun.misc.Perf.getPerf". A security exception will result + * if the caller has not been granted this permission. + *

    + * Access to the returned Perf object should be protected + * by its caller and not passed on to untrusted code. This object can + * be used to attach to the instrumentation buffer provided by this Java + * virtual machine or for those of other Java virtual machines running + * on the same system. The instrumentation buffer may contain senstitive + * information. API's built on top of this interface may want to provide + * finer grained access control to the contents of individual + * instrumentation objects contained within the buffer. + *

    + * Please note that the "sun.misc.Perf.getPerf" permission + * is not a JDK specified permission. + * + * @return A reference to the singleton Perf instance. + * @throws AccessControlException if a security manager exists and + * its checkPermission method doesn't allow + * access to the "sun.misc.Perf.getPerf" target. + * @see RuntimePermission + * @see #attach + */ + public static Perf getPerf() + { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + Permission perm = new RuntimePermission("sun.misc.Perf.getPerf"); + security.checkPermission(perm); + } + + return instance; + } + + /** + * Attach to the instrumentation buffer for the specified Java virtual + * machine. + *

    + * This method will attach to the instrumentation buffer for the + * specified virtual machine. It returns a ByteBuffer object + * that is initialized to access the instrumentation buffer for the + * indicated Java virtual machine. The lvmid parameter is + * a integer value that uniquely identifies the target local Java virtual + * machine. It is typically, but not necessarily, the process id of + * the target Java virtual machine. + *

    + * If the lvmid identifies a Java virtual machine different + * from the one running this method, then the coherency characteristics + * of the buffer are implementation dependent. Implementations that do + * not support named, coherent, shared memory may return a + * ByteBuffer object that contains only a snap shot of the + * data in the instrumentation buffer. Implementations that support named, + * coherent, shared memory, may return a ByteBuffer object + * that will be changing dynamically over time as the target Java virtual + * machine updates its mapping of this buffer. + *

    + * If the lvmid is 0 or equal to the actual lvmid + * for the Java virtual machine running this method, then the returned + * ByteBuffer object will always be coherent and dynamically + * changing. + *

    + * The attach mode specifies the access permissions requested for the + * instrumentation buffer of the target virtual machine. The permitted + * access permissions are: + *

    + * + *

  • "r" - Read only access. This Java virtual machine has only + * read access to the instrumentation buffer for the target Java + * virtual machine. + *
  • "rw" - Read/Write access. This Java virtual machine has read and + * write access to the instrumentation buffer for the target Java virtual + * machine. This mode is currently not supported and is reserved for + * future enhancements. + * + * + * @param lvmid an integer that uniquely identifies the + * target local Java virtual machine. + * @param mode a string indicating the attach mode. + * @return ByteBuffer a direct allocated byte buffer + * @throws IllegalArgumentException The lvmid or mode was invalid. + * @throws IOException An I/O error occurred while trying to acquire + * the instrumentation buffer. + * @throws OutOfMemoryError The instrumentation buffer could not be mapped + * into the virtual machine's address space. + * @see ByteBuffer + */ + public ByteBuffer attach(int lvmid, String mode) + throws IllegalArgumentException, IOException + { + if (mode.compareTo("r") == 0) { + return attachImpl(null, lvmid, PERF_MODE_RO); + } + else if (mode.compareTo("rw") == 0) { + return attachImpl(null, lvmid, PERF_MODE_RW); + } + else { + throw new IllegalArgumentException("unknown mode"); + } + } + + /** + * Attach to the instrumentation buffer for the specified Java virtual + * machine owned by the given user. + *

    + * This method behaves just as the attach(int lvmid, String mode) + * method, except that it only searches for Java virtual machines + * owned by the specified user. + * + * @param user A String object containing the + * name of the user that owns the target Java + * virtual machine. + * @param lvmid an integer that uniquely identifies the + * target local Java virtual machine. + * @param mode a string indicating the attach mode. + * @return ByteBuffer a direct allocated byte buffer + * @throws IllegalArgumentException The lvmid or mode was invalid. + * @throws IOException An I/O error occurred while trying to acquire + * the instrumentation buffer. + * @throws OutOfMemoryError The instrumentation buffer could not be mapped + * into the virtual machine's address space. + * @see ByteBuffer + */ + public ByteBuffer attach(String user, int lvmid, String mode) + throws IllegalArgumentException, IOException + { + if (mode.compareTo("r") == 0) { + return attachImpl(user, lvmid, PERF_MODE_RO); + } + else if (mode.compareTo("rw") == 0) { + return attachImpl(user, lvmid, PERF_MODE_RW); + } + else { + throw new IllegalArgumentException("unknown mode"); + } + } + + /** + * Call the implementation specific attach method. + *

    + * This method calls into the Java virtual machine to perform the platform + * specific attach method. Buffers returned from this method are + * internally managed as PhantomRefereces to provide for + * guaranteed, secure release of the native resources. + * + * @param user A String object containing the + * name of the user that owns the target Java + * virtual machine. + * @param lvmid an integer that uniquely identifies the + * target local Java virtual machine. + * @param mode a string indicating the attach mode. + * @return ByteBuffer a direct allocated byte buffer + * @throws IllegalArgumentException The lvmid or mode was invalid. + * @throws IOException An I/O error occurred while trying to acquire + * the instrumentation buffer. + * @throws OutOfMemoryError The instrumentation buffer could not be mapped + * into the virtual machine's address space. + */ + private ByteBuffer attachImpl(String user, int lvmid, int mode) + throws IllegalArgumentException, IOException + { + final ByteBuffer b = attach(user, lvmid, mode); + + if (lvmid == 0) { + // The native instrumentation buffer for this Java virtual + // machine is never unmapped. + return b; + } + else { + // This is an instrumentation buffer for another Java virtual + // machine with native resources that need to be managed. We + // create a duplicate of the native ByteBuffer and manage it + // with a Cleaner object (PhantomReference). When the duplicate + // becomes only phantomly reachable, the native resources will + // be released. + + final ByteBuffer dup = b.duplicate(); + Cleaner.create(dup, new Runnable() { + public void run() { + try { + instance.detach(b); + } + catch (Throwable th) { + // avoid crashing the reference handler thread, + // but provide for some diagnosability + assert false : th.toString(); + } + } + }); + return dup; + } + } + + /** + * Native method to perform the implementation specific attach mechanism. + *

    + * The implementation of this method may return distinct or identical + * ByteBuffer objects for two distinct calls requesting + * attachment to the same Java virtual machine. + *

    + * For the Sun HotSpot JVM, two distinct calls to attach to the same + * target Java virtual machine will result in two distinct ByteBuffer + * objects returned by this method. This may change in a future release. + * + * @param user A String object containing the + * name of the user that owns the target Java + * virtual machine. + * @param lvmid an integer that uniquely identifies the + * target local Java virtual machine. + * @param mode a string indicating the attach mode. + * @return ByteBuffer a direct allocated byte buffer + * @throws IllegalArgumentException The lvmid or mode was invalid. + * @throws IOException An I/O error occurred while trying to acquire + * the instrumentation buffer. + * @throws OutOfMemoryError The instrumentation buffer could not be mapped + * into the virtual machine's address space. + */ + private native ByteBuffer attach(String user, int lvmid, int mode) + throws IllegalArgumentException, IOException; + + /** + * Native method to perform the implementation specific detach mechanism. + *

    + * If this method is passed a ByteBuffer object that is + * not created by the attach method, then the results of + * this method are undefined, with unpredictable and potentially damaging + * effects to the Java virtual machine. To prevent accidental or malicious + * use of this method, all native ByteBuffer created by the + * attach method are managed internally as PhantomReferences + * and resources are freed by the system. + *

    + * If this method is passed a ByteBuffer object created + * by the attach method with a lvmid for the Java virtual + * machine running this method (lvmid=0, for example), then the detach + * request is silently ignored. + * + * @param ByteBuffer A direct allocated byte buffer created by the + * attach method. + * @see ByteBuffer + * @see #attach + */ + private native void detach(ByteBuffer bb); + + /** + * Create a long scalar entry in the instrumentation buffer + * with the given variability characteristic, units, and initial value. + *

    + * Access to the instrument is provided through the returned + * ByteBuffer object. Typically, this object should be wrapped + * with LongBuffer view object. + * + * @param variability the variability characteristic for this entry. + * @param units the units for this entry. + * @param name the name of this entry. + * @param value the initial value for this entry. + * @return ByteBuffer a direct allocated ByteBuffer object that + * allows write access to a native memory location + * containing a long value. + * + * see sun.misc.perf.Variability + * see sun.misc.perf.Units + * @see ByteBuffer + */ + public native ByteBuffer createLong(String name, int variability, + int units, long value); + + /** + * Create a String entry in the instrumentation buffer with + * the given variability characteristic, units, and initial value. + *

    + * The maximum length of the String stored in this string + * instrument is given in by maxLength parameter. Updates + * to this instrument with String values with lengths greater + * than maxLength will be truncated to maxLength. + * The truncated value will be terminated by a null character. + *

    + * The underlying implementation may further limit the length of the + * value, but will continue to preserve the null terminator. + *

    + * Access to the instrument is provided through the returned + * ByteBuffer object. + * + * @param variability the variability characteristic for this entry. + * @param units the units for this entry. + * @param name the name of this entry. + * @param value the initial value for this entry. + * @param maxLength the maximum string length for this string + * instrument. + * @return ByteBuffer a direct allocated ByteBuffer that allows + * write access to a native memory location + * containing a long value. + * + * see sun.misc.perf.Variability + * see sun.misc.perf.Units + * @see ByteBuffer + */ + public ByteBuffer createString(String name, int variability, + int units, String value, int maxLength) + { + byte[] v = getBytes(value); + byte[] v1 = new byte[v.length+1]; + System.arraycopy(v, 0, v1, 0, v.length); + v1[v.length] = '\0'; + return createByteArray(name, variability, units, v1, Math.max(v1.length, maxLength)); + } + + /** + * Create a String entry in the instrumentation buffer with + * the given variability characteristic, units, and initial value. + *

    + * The maximum length of the String stored in this string + * instrument is implied by the length of the value parameter. + * Subsequent updates to the value of this instrument will be truncated + * to this implied maximum length. The truncated value will be terminated + * by a null character. + *

    + * The underlying implementation may further limit the length of the + * initial or subsequent value, but will continue to preserve the null + * terminator. + *

    + * Access to the instrument is provided through the returned + * ByteBuffer object. + * + * @param variability the variability characteristic for this entry. + * @param units the units for this entry. + * @param name the name of this entry. + * @param value the initial value for this entry. + * @return ByteBuffer a direct allocated ByteBuffer that allows + * write access to a native memory location + * containing a long value. + * + * see sun.misc.perf.Variability + * see sun.misc.perf.Units + * @see ByteBuffer + */ + public ByteBuffer createString(String name, int variability, + int units, String value) + { + byte[] v = getBytes(value); + byte[] v1 = new byte[v.length+1]; + System.arraycopy(v, 0, v1, 0, v.length); + v1[v.length] = '\0'; + return createByteArray(name, variability, units, v1, v1.length); + } + + /** + * Create a byte vector entry in the instrumentation buffer + * with the given variability characteristic, units, and initial value. + *

    + * The maxLength parameter limits the size of the byte + * array instrument such that the initial or subsequent updates beyond + * this length are silently ignored. No special handling of truncated + * updates is provided. + *

    + * The underlying implementation may further limit the length of the + * length of the initial or subsequent value. + *

    + * Access to the instrument is provided through the returned + * ByteBuffer object. + * + * @param variability the variability characteristic for this entry. + * @param units the units for this entry. + * @param name the name of this entry. + * @param value the initial value for this entry. + * @param maxLength the maximum length of this byte array. + * @return ByteBuffer a direct allocated byte buffer that allows + * write access to a native memory location + * containing a long value. + * + * see sun.misc.perf.Variability + * see sun.misc.perf.Units + * @see ByteBuffer + */ + public native ByteBuffer createByteArray(String name, int variability, + int units, byte[] value, + int maxLength); + + + /** + * convert string to an array of UTF-8 bytes + */ + private static byte[] getBytes(String s) + { + byte[] bytes = null; + + try { + bytes = s.getBytes("UTF-8"); + } + catch (UnsupportedEncodingException e) { + // ignore, UTF-8 encoding is always known + } + + return bytes; + } + + /** + * Return the value of the High Resolution Counter. + * + * The High Resolution Counter returns the number of ticks since + * since the start of the Java virtual machine. The resolution of + * the counter is machine dependent and can be determined from the + * value return by the {@link #highResFrequency} method. + * + * @return the number of ticks of machine dependent resolution since + * the start of the Java virtual machine. + * + * @see #highResFrequency + * @see System#currentTimeMillis() + */ + public native long highResCounter(); + + /** + * Returns the frequency of the High Resolution Counter, in ticks per + * second. + * + * This value can be used to convert the value of the High Resolution + * Counter, as returned from a call to the {@link #highResCounter} method, + * into the number of seconds since the start of the Java virtual machine. + * + * @return the frequency of the High Resolution Counter. + * @see #highResCounter + */ + public native long highResFrequency(); + + private static native void registerNatives(); + + static { + registerNatives(); + instance = new Perf(); + } +} diff --git a/src/sun/misc/PerfCounter.java b/src/sun/misc/PerfCounter.java new file mode 100644 index 00000000..6d6969a1 --- /dev/null +++ b/src/sun/misc/PerfCounter.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.LongBuffer; +import java.security.AccessController; + +/** + * Performance counter support for internal JRE classes. + * This class defines a fixed list of counters for the platform + * to use as an interim solution until RFE# 6209222 is implemented. + * The perf counters will be created in the jvmstat perf buffer + * that the HotSpot VM creates. The default size is 32K and thus + * the number of counters is bounded. You can alter the size + * with -XX:PerfDataMemorySize= option. If there is + * insufficient memory in the jvmstat perf buffer, the C heap memory + * will be used and thus the application will continue to run if + * the counters added exceeds the buffer size but the counters + * will be missing. + * + * See HotSpot jvmstat implementation for certain circumstances + * that the jvmstat perf buffer is not supported. + * + */ +public class PerfCounter { + private static final Perf perf = + AccessController.doPrivileged(new Perf.GetPerfAction()); + + // Must match values defined in hotspot/src/share/vm/runtime/perfdata.hpp + private final static int V_Constant = 1; + private final static int V_Monotonic = 2; + private final static int V_Variable = 3; + private final static int U_None = 1; + + private final String name; + private final LongBuffer lb; + + private PerfCounter(String name, int type) { + this.name = name; + ByteBuffer bb = perf.createLong(name, type, U_None, 0L); + bb.order(ByteOrder.nativeOrder()); + this.lb = bb.asLongBuffer(); + } + + static PerfCounter newPerfCounter(String name) { + return new PerfCounter(name, V_Variable); + } + + static PerfCounter newConstantPerfCounter(String name) { + PerfCounter c = new PerfCounter(name, V_Constant); + return c; + } + + /** + * Returns the current value of the perf counter. + */ + public synchronized long get() { + return lb.get(0); + } + + /** + * Sets the value of the perf counter to the given newValue. + */ + public synchronized void set(long newValue) { + lb.put(0, newValue); + } + + /** + * Adds the given value to the perf counter. + */ + public synchronized void add(long value) { + long res = get() + value; + lb.put(0, res); + } + + /** + * Increments the perf counter with 1. + */ + public void increment() { + add(1); + } + + /** + * Adds the given interval to the perf counter. + */ + public void addTime(long interval) { + add(interval); + } + + /** + * Adds the elapsed time from the given start time (ns) to the perf counter. + */ + public void addElapsedTimeFrom(long startTime) { + add(System.nanoTime() - startTime); + } + + @Override + public String toString() { + return name + " = " + get(); + } + + static class CoreCounters { + static final PerfCounter pdt = newPerfCounter("sun.classloader.parentDelegationTime"); + static final PerfCounter lc = newPerfCounter("sun.classloader.findClasses"); + static final PerfCounter lct = newPerfCounter("sun.classloader.findClassTime"); + static final PerfCounter rcbt = newPerfCounter("sun.urlClassLoader.readClassBytesTime"); + static final PerfCounter zfc = newPerfCounter("sun.zip.zipFiles"); + static final PerfCounter zfot = newPerfCounter("sun.zip.zipFile.openTime"); + } + + static class WindowsClientCounters { + static final PerfCounter d3dAvailable = newConstantPerfCounter("sun.java2d.d3d.available"); + } + + /** + * Number of findClass calls + */ + public static PerfCounter getFindClasses() { + return CoreCounters.lc; + } + + /** + * Time (ns) spent in finding classes that includes + * lookup and read class bytes and defineClass + */ + public static PerfCounter getFindClassTime() { + return CoreCounters.lct; + } + + /** + * Time (ns) spent in finding classes + */ + public static PerfCounter getReadClassBytesTime() { + return CoreCounters.rcbt; + } + + /** + * Time (ns) spent in the parent delegation to + * the parent of the defining class loader + */ + public static PerfCounter getParentDelegationTime() { + return CoreCounters.pdt; + } + + /** + * Number of zip files opened. + */ + public static PerfCounter getZipFileCount() { + return CoreCounters.zfc; + } + + /** + * Time (ns) spent in opening the zip files that + * includes building the entries hash table + */ + public static PerfCounter getZipFileOpenTime() { + return CoreCounters.zfot; + } + + /** + * D3D graphic pipeline available + */ + public static PerfCounter getD3DAvailable() { + return WindowsClientCounters.d3dAvailable; + } +} diff --git a/src/sun/misc/PerformanceLogger.java b/src/sun/misc/PerformanceLogger.java new file mode 100644 index 00000000..02918755 --- /dev/null +++ b/src/sun/misc/PerformanceLogger.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + + +package sun.misc; + +import java.io.File; +import java.io.FileWriter; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Vector; + +/** + * This class is intended to be a central place for the jdk to + * log timing events of interest. There is pre-defined event + * of startTime, as well as a general + * mechanism of setting arbitrary times in an array. + * All unreserved times in the array can be used by callers + * in application-defined situations. The caller is responsible + * for setting and getting all times and for doing whatever + * analysis is interesting; this class is merely a central container + * for those timing values. + * Note that, due to the variables in this class being static, + * use of particular time values by multiple applets will cause + * confusing results. For example, if plugin runs two applets + * simultaneously, the initTime for those applets will collide + * and the results may be undefined. + *

    + * To automatically track startup performance in an app or applet, + * use the command-line parameter sun.perflog as follows:
    + * -Dsun.perflog[=file:] + *
    + * where simply using the parameter with no value will enable output + * to the console and a value of "file:" will cause + * that given filename to be created and used for all output. + *

    + * By default, times are measured using System.currentTimeMillis(). To use + * System.nanoTime() instead, add the command-line parameter:
    + -Dsun.perflog.nano=true + *
    + *

    + * Warning: Use at your own risk! + * This class is intended for internal testing + * purposes only and may be removed at any time. More + * permanent monitoring and profiling APIs are expected to be + * developed for future releases and this class will cease to + * exist once those APIs are in place. + * @author Chet Haase + */ +public class PerformanceLogger { + + // Timing values of global interest + private static final int START_INDEX = 0; // VM start + private static final int LAST_RESERVED = START_INDEX; + + private static boolean perfLoggingOn = false; + private static boolean useNanoTime = false; + private static Vector times; + private static String logFileName = null; + private static Writer logWriter = null; + private static long baseTime; + + static { + String perfLoggingProp = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("sun.perflog")); + if (perfLoggingProp != null) { + perfLoggingOn = true; + + // Check if we should use nanoTime + String perfNanoProp = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("sun.perflog.nano")); + if (perfNanoProp != null) { + useNanoTime = true; + } + + // Now, figure out what the user wants to do with the data + if (perfLoggingProp.regionMatches(true, 0, "file:", 0, 5)) { + logFileName = perfLoggingProp.substring(5); + } + if (logFileName != null) { + if (logWriter == null) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + try { + File logFile = new File(logFileName); + logFile.createNewFile(); + logWriter = new FileWriter(logFile); + } catch (Exception e) { + System.out.println(e + ": Creating logfile " + + logFileName + + ". Log to console"); + } + return null; + } + }); + } + } + if (logWriter == null) { + logWriter = new OutputStreamWriter(System.out); + } + } + times = new Vector(10); + // Reserve predefined slots + for (int i = 0; i <= LAST_RESERVED; ++i) { + times.add(new TimeData("Time " + i + " not set", 0)); + } + } + + /** + * Returns status of whether logging is enabled or not. This is + * provided as a convenience method so that users do not have to + * perform the same GetPropertyAction check as above to determine whether + * to enable performance logging. + */ + public static boolean loggingEnabled() { + return perfLoggingOn; + } + + + /** + * Internal class used to store time/message data together. + */ + static class TimeData { + String message; + long time; + + TimeData(String message, long time) { + this.message = message; + this.time = time; + } + + String getMessage() { + return message; + } + + long getTime() { + return time; + } + } + + /** + * Return the current time, in millis or nanos as appropriate + */ + private static long getCurrentTime() { + if (useNanoTime) { + return System.nanoTime(); + } else { + return System.currentTimeMillis(); + } + } + + /** + * Sets the start time. Ideally, this is the earliest time available + * during the startup of a Java applet or application. This time is + * later used to analyze the difference between the initial startup + * time and other events in the system (such as an applet's init time). + */ + public static void setStartTime(String message) { + if (loggingEnabled()) { + long nowTime = getCurrentTime(); + setStartTime(message, nowTime); + } + } + + /** + * Sets the base time, output can then + * be displayed as offsets from the base time;. + */ + public static void setBaseTime(long time) { + if (loggingEnabled()) { + baseTime = time; + } + } + + /** + * Sets the start time. + * This version of the method is + * given the time to log, instead of expecting this method to + * get the time itself. This is done in case the time was + * recorded much earlier than this method was called. + */ + public static void setStartTime(String message, long time) { + if (loggingEnabled()) { + times.set(START_INDEX, new TimeData(message, time)); + } + } + + /** + * Gets the start time, which should be the time when + * the java process started, prior to the VM actually being + * loaded. + */ + public static long getStartTime() { + if (loggingEnabled()) { + return times.get(START_INDEX).getTime(); + } else { + return 0; + } + } + + /** + * Sets the value of a given time and returns the index of the + * slot that that time was stored in. + */ + public static int setTime(String message) { + if (loggingEnabled()) { + long nowTime = getCurrentTime(); + return setTime(message, nowTime); + } else { + return 0; + } + } + + /** + * Sets the value of a given time and returns the index of the + * slot that that time was stored in. + * This version of the method is + * given the time to log, instead of expecting this method to + * get the time itself. This is done in case the time was + * recorded much earlier than this method was called. + */ + public static int setTime(String message, long time) { + if (loggingEnabled()) { + // times is already synchronized, but we need to ensure that + // the size used in times.set() is the same used when returning + // the index of that operation. + synchronized (times) { + times.add(new TimeData(message, time)); + return (times.size() - 1); + } + } else { + return 0; + } + } + + /** + * Returns time at given index. + */ + public static long getTimeAtIndex(int index) { + if (loggingEnabled()) { + return times.get(index).getTime(); + } else { + return 0; + } + } + + /** + * Returns message at given index. + */ + public static String getMessageAtIndex(int index) { + if (loggingEnabled()) { + return times.get(index).getMessage(); + } else { + return null; + } + } + + /** + * Outputs all data to parameter-specified Writer object + */ + public static void outputLog(Writer writer) { + if (loggingEnabled()) { + try { + synchronized(times) { + for (int i = 0; i < times.size(); ++i) { + TimeData td = times.get(i); + if (td != null) { + writer.write(i + " " + td.getMessage() + ": " + + (td.getTime() - baseTime) + "\n"); + + } + } + } + writer.flush(); + } catch (Exception e) { + System.out.println(e + ": Writing performance log to " + + writer); + } + } + } + + /** + * Outputs all data to whatever location the user specified + * via sun.perflog command-line parameter. + */ + public static void outputLog() { + outputLog(logWriter); + } +} diff --git a/src/sun/misc/ProxyGenerator.java b/src/sun/misc/ProxyGenerator.java new file mode 100644 index 00000000..6df0bec2 --- /dev/null +++ b/src/sun/misc/ProxyGenerator.java @@ -0,0 +1,2032 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import sun.security.action.GetBooleanAction; + +/** + * ProxyGenerator contains the code to generate a dynamic proxy class + * for the java.lang.reflect.Proxy API. + * + * The external interfaces to ProxyGenerator is the static + * "generateProxyClass" method. + * + * @author Peter Jones + * @since 1.3 + */ +public class ProxyGenerator { + /* + * In the comments below, "JVMS" refers to The Java Virtual Machine + * Specification Second Edition and "JLS" refers to the original + * version of The Java Language Specification, unless otherwise + * specified. + */ + + /* generate 1.5-era class file version */ + private static final int CLASSFILE_MAJOR_VERSION = 49; + private static final int CLASSFILE_MINOR_VERSION = 0; + + /* + * beginning of constants copied from + * sun.tools.java.RuntimeConstants (which no longer exists): + */ + + /* constant pool tags */ + private static final int CONSTANT_UTF8 = 1; + private static final int CONSTANT_UNICODE = 2; + private static final int CONSTANT_INTEGER = 3; + private static final int CONSTANT_FLOAT = 4; + private static final int CONSTANT_LONG = 5; + private static final int CONSTANT_DOUBLE = 6; + private static final int CONSTANT_CLASS = 7; + private static final int CONSTANT_STRING = 8; + private static final int CONSTANT_FIELD = 9; + private static final int CONSTANT_METHOD = 10; + private static final int CONSTANT_INTERFACEMETHOD = 11; + private static final int CONSTANT_NAMEANDTYPE = 12; + + /* access and modifier flags */ + private static final int ACC_PUBLIC = 0x00000001; + private static final int ACC_PRIVATE = 0x00000002; +// private static final int ACC_PROTECTED = 0x00000004; + private static final int ACC_STATIC = 0x00000008; + private static final int ACC_FINAL = 0x00000010; +// private static final int ACC_SYNCHRONIZED = 0x00000020; +// private static final int ACC_VOLATILE = 0x00000040; +// private static final int ACC_TRANSIENT = 0x00000080; +// private static final int ACC_NATIVE = 0x00000100; +// private static final int ACC_INTERFACE = 0x00000200; +// private static final int ACC_ABSTRACT = 0x00000400; + private static final int ACC_SUPER = 0x00000020; +// private static final int ACC_STRICT = 0x00000800; + + /* opcodes */ +// private static final int opc_nop = 0; + private static final int opc_aconst_null = 1; +// private static final int opc_iconst_m1 = 2; + private static final int opc_iconst_0 = 3; +// private static final int opc_iconst_1 = 4; +// private static final int opc_iconst_2 = 5; +// private static final int opc_iconst_3 = 6; +// private static final int opc_iconst_4 = 7; +// private static final int opc_iconst_5 = 8; +// private static final int opc_lconst_0 = 9; +// private static final int opc_lconst_1 = 10; +// private static final int opc_fconst_0 = 11; +// private static final int opc_fconst_1 = 12; +// private static final int opc_fconst_2 = 13; +// private static final int opc_dconst_0 = 14; +// private static final int opc_dconst_1 = 15; + private static final int opc_bipush = 16; + private static final int opc_sipush = 17; + private static final int opc_ldc = 18; + private static final int opc_ldc_w = 19; +// private static final int opc_ldc2_w = 20; + private static final int opc_iload = 21; + private static final int opc_lload = 22; + private static final int opc_fload = 23; + private static final int opc_dload = 24; + private static final int opc_aload = 25; + private static final int opc_iload_0 = 26; +// private static final int opc_iload_1 = 27; +// private static final int opc_iload_2 = 28; +// private static final int opc_iload_3 = 29; + private static final int opc_lload_0 = 30; +// private static final int opc_lload_1 = 31; +// private static final int opc_lload_2 = 32; +// private static final int opc_lload_3 = 33; + private static final int opc_fload_0 = 34; +// private static final int opc_fload_1 = 35; +// private static final int opc_fload_2 = 36; +// private static final int opc_fload_3 = 37; + private static final int opc_dload_0 = 38; +// private static final int opc_dload_1 = 39; +// private static final int opc_dload_2 = 40; +// private static final int opc_dload_3 = 41; + private static final int opc_aload_0 = 42; +// private static final int opc_aload_1 = 43; +// private static final int opc_aload_2 = 44; +// private static final int opc_aload_3 = 45; +// private static final int opc_iaload = 46; +// private static final int opc_laload = 47; +// private static final int opc_faload = 48; +// private static final int opc_daload = 49; +// private static final int opc_aaload = 50; +// private static final int opc_baload = 51; +// private static final int opc_caload = 52; +// private static final int opc_saload = 53; +// private static final int opc_istore = 54; +// private static final int opc_lstore = 55; +// private static final int opc_fstore = 56; +// private static final int opc_dstore = 57; + private static final int opc_astore = 58; +// private static final int opc_istore_0 = 59; +// private static final int opc_istore_1 = 60; +// private static final int opc_istore_2 = 61; +// private static final int opc_istore_3 = 62; +// private static final int opc_lstore_0 = 63; +// private static final int opc_lstore_1 = 64; +// private static final int opc_lstore_2 = 65; +// private static final int opc_lstore_3 = 66; +// private static final int opc_fstore_0 = 67; +// private static final int opc_fstore_1 = 68; +// private static final int opc_fstore_2 = 69; +// private static final int opc_fstore_3 = 70; +// private static final int opc_dstore_0 = 71; +// private static final int opc_dstore_1 = 72; +// private static final int opc_dstore_2 = 73; +// private static final int opc_dstore_3 = 74; + private static final int opc_astore_0 = 75; +// private static final int opc_astore_1 = 76; +// private static final int opc_astore_2 = 77; +// private static final int opc_astore_3 = 78; +// private static final int opc_iastore = 79; +// private static final int opc_lastore = 80; +// private static final int opc_fastore = 81; +// private static final int opc_dastore = 82; + private static final int opc_aastore = 83; +// private static final int opc_bastore = 84; +// private static final int opc_castore = 85; +// private static final int opc_sastore = 86; + private static final int opc_pop = 87; +// private static final int opc_pop2 = 88; + private static final int opc_dup = 89; +// private static final int opc_dup_x1 = 90; +// private static final int opc_dup_x2 = 91; +// private static final int opc_dup2 = 92; +// private static final int opc_dup2_x1 = 93; +// private static final int opc_dup2_x2 = 94; +// private static final int opc_swap = 95; +// private static final int opc_iadd = 96; +// private static final int opc_ladd = 97; +// private static final int opc_fadd = 98; +// private static final int opc_dadd = 99; +// private static final int opc_isub = 100; +// private static final int opc_lsub = 101; +// private static final int opc_fsub = 102; +// private static final int opc_dsub = 103; +// private static final int opc_imul = 104; +// private static final int opc_lmul = 105; +// private static final int opc_fmul = 106; +// private static final int opc_dmul = 107; +// private static final int opc_idiv = 108; +// private static final int opc_ldiv = 109; +// private static final int opc_fdiv = 110; +// private static final int opc_ddiv = 111; +// private static final int opc_irem = 112; +// private static final int opc_lrem = 113; +// private static final int opc_frem = 114; +// private static final int opc_drem = 115; +// private static final int opc_ineg = 116; +// private static final int opc_lneg = 117; +// private static final int opc_fneg = 118; +// private static final int opc_dneg = 119; +// private static final int opc_ishl = 120; +// private static final int opc_lshl = 121; +// private static final int opc_ishr = 122; +// private static final int opc_lshr = 123; +// private static final int opc_iushr = 124; +// private static final int opc_lushr = 125; +// private static final int opc_iand = 126; +// private static final int opc_land = 127; +// private static final int opc_ior = 128; +// private static final int opc_lor = 129; +// private static final int opc_ixor = 130; +// private static final int opc_lxor = 131; +// private static final int opc_iinc = 132; +// private static final int opc_i2l = 133; +// private static final int opc_i2f = 134; +// private static final int opc_i2d = 135; +// private static final int opc_l2i = 136; +// private static final int opc_l2f = 137; +// private static final int opc_l2d = 138; +// private static final int opc_f2i = 139; +// private static final int opc_f2l = 140; +// private static final int opc_f2d = 141; +// private static final int opc_d2i = 142; +// private static final int opc_d2l = 143; +// private static final int opc_d2f = 144; +// private static final int opc_i2b = 145; +// private static final int opc_i2c = 146; +// private static final int opc_i2s = 147; +// private static final int opc_lcmp = 148; +// private static final int opc_fcmpl = 149; +// private static final int opc_fcmpg = 150; +// private static final int opc_dcmpl = 151; +// private static final int opc_dcmpg = 152; +// private static final int opc_ifeq = 153; +// private static final int opc_ifne = 154; +// private static final int opc_iflt = 155; +// private static final int opc_ifge = 156; +// private static final int opc_ifgt = 157; +// private static final int opc_ifle = 158; +// private static final int opc_if_icmpeq = 159; +// private static final int opc_if_icmpne = 160; +// private static final int opc_if_icmplt = 161; +// private static final int opc_if_icmpge = 162; +// private static final int opc_if_icmpgt = 163; +// private static final int opc_if_icmple = 164; +// private static final int opc_if_acmpeq = 165; +// private static final int opc_if_acmpne = 166; +// private static final int opc_goto = 167; +// private static final int opc_jsr = 168; +// private static final int opc_ret = 169; +// private static final int opc_tableswitch = 170; +// private static final int opc_lookupswitch = 171; + private static final int opc_ireturn = 172; + private static final int opc_lreturn = 173; + private static final int opc_freturn = 174; + private static final int opc_dreturn = 175; + private static final int opc_areturn = 176; + private static final int opc_return = 177; + private static final int opc_getstatic = 178; + private static final int opc_putstatic = 179; + private static final int opc_getfield = 180; +// private static final int opc_putfield = 181; + private static final int opc_invokevirtual = 182; + private static final int opc_invokespecial = 183; + private static final int opc_invokestatic = 184; + private static final int opc_invokeinterface = 185; + private static final int opc_new = 187; +// private static final int opc_newarray = 188; + private static final int opc_anewarray = 189; +// private static final int opc_arraylength = 190; + private static final int opc_athrow = 191; + private static final int opc_checkcast = 192; +// private static final int opc_instanceof = 193; +// private static final int opc_monitorenter = 194; +// private static final int opc_monitorexit = 195; + private static final int opc_wide = 196; +// private static final int opc_multianewarray = 197; +// private static final int opc_ifnull = 198; +// private static final int opc_ifnonnull = 199; +// private static final int opc_goto_w = 200; +// private static final int opc_jsr_w = 201; + + // end of constants copied from sun.tools.java.RuntimeConstants + + /** name of the superclass of proxy classes */ + private final static String superclassName = "java/lang/reflect/Proxy"; + + /** name of field for storing a proxy instance's invocation handler */ + private final static String handlerFieldName = "h"; + + /** debugging flag for saving generated class files */ + private final static boolean saveGeneratedFiles = + java.security.AccessController.doPrivileged( + new GetBooleanAction( + "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue(); + + /** + * Generate a public proxy class given a name and a list of proxy interfaces. + */ + public static byte[] generateProxyClass(final String name, + Class[] interfaces) { + return generateProxyClass(name, interfaces, (ACC_PUBLIC | ACC_FINAL | ACC_SUPER)); + } + + /** + * Generate a proxy class given a name and a list of proxy interfaces. + * + * @param name the class name of the proxy class + * @param interfaces proxy interfaces + * @param accessFlags access flags of the proxy class + */ + public static byte[] generateProxyClass(final String name, + Class[] interfaces, + int accessFlags) + { + ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); + final byte[] classFile = gen.generateClassFile(); + + if (saveGeneratedFiles) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + try { + int i = name.lastIndexOf('.'); + Path path; + if (i > 0) { + Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar)); + Files.createDirectories(dir); + path = dir.resolve(name.substring(i+1, name.length()) + ".class"); + } else { + path = Paths.get(name + ".class"); + } + Files.write(path, classFile); + return null; + } catch (IOException e) { + throw new InternalError( + "I/O exception saving generated file: " + e); + } + } + }); + } + + return classFile; + } + + /* preloaded Method objects for methods in java.lang.Object */ + private static Method hashCodeMethod; + private static Method equalsMethod; + private static Method toStringMethod; + static { + try { + hashCodeMethod = Object.class.getMethod("hashCode"); + equalsMethod = + Object.class.getMethod("equals", new Class[] { Object.class }); + toStringMethod = Object.class.getMethod("toString"); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + /** name of proxy class */ + private String className; + + /** proxy interfaces */ + private Class[] interfaces; + + /** proxy class access flags */ + private int accessFlags; + + /** constant pool of class being generated */ + private ConstantPool cp = new ConstantPool(); + + /** FieldInfo struct for each field of generated class */ + private List fields = new ArrayList<>(); + + /** MethodInfo struct for each method of generated class */ + private List methods = new ArrayList<>(); + + /** + * maps method signature string to list of ProxyMethod objects for + * proxy methods with that signature + */ + private Map> proxyMethods = new HashMap<>(); + + /** count of ProxyMethod objects added to proxyMethods */ + private int proxyMethodCount = 0; + + /** + * Construct a ProxyGenerator to generate a proxy class with the + * specified name and for the given interfaces. + * + * A ProxyGenerator object contains the state for the ongoing + * generation of a particular proxy class. + */ + private ProxyGenerator(String className, Class[] interfaces, int accessFlags) { + this.className = className; + this.interfaces = interfaces; + this.accessFlags = accessFlags; + } + + /** + * Generate a class file for the proxy class. This method drives the + * class file generation process. + */ + private byte[] generateClassFile() { + + /* ============================================================ + * Step 1: Assemble ProxyMethod objects for all methods to + * generate proxy dispatching code for. + */ + + /* + * Record that proxy methods are needed for the hashCode, equals, + * and toString methods of java.lang.Object. This is done before + * the methods from the proxy interfaces so that the methods from + * java.lang.Object take precedence over duplicate methods in the + * proxy interfaces. + */ + addProxyMethod(hashCodeMethod, Object.class); + addProxyMethod(equalsMethod, Object.class); + addProxyMethod(toStringMethod, Object.class); + + /* + * Now record all of the methods from the proxy interfaces, giving + * earlier interfaces precedence over later ones with duplicate + * methods. + */ + for (Class intf : interfaces) { + for (Method m : intf.getMethods()) { + addProxyMethod(m, intf); + } + } + + /* + * For each set of proxy methods with the same signature, + * verify that the methods' return types are compatible. + */ + for (List sigmethods : proxyMethods.values()) { + checkReturnTypes(sigmethods); + } + + /* ============================================================ + * Step 2: Assemble FieldInfo and MethodInfo structs for all of + * fields and methods in the class we are generating. + */ + try { + methods.add(generateConstructor()); + + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + + // add static field for method's Method object + fields.add(new FieldInfo(pm.methodFieldName, + "Ljava/lang/reflect/Method;", + ACC_PRIVATE | ACC_STATIC)); + + // generate code for proxy method and add it + methods.add(pm.generateMethod()); + } + } + + methods.add(generateStaticInitializer()); + + } catch (IOException e) { + throw new InternalError("unexpected I/O Exception", e); + } + + if (methods.size() > 65535) { + throw new IllegalArgumentException("method limit exceeded"); + } + if (fields.size() > 65535) { + throw new IllegalArgumentException("field limit exceeded"); + } + + /* ============================================================ + * Step 3: Write the final class file. + */ + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getClass(dotToSlash(className)); + cp.getClass(superclassName); + for (Class intf: interfaces) { + cp.getClass(dotToSlash(intf.getName())); + } + + /* + * Disallow new constant pool additions beyond this point, since + * we are about to write the final constant pool table. + */ + cp.setReadOnly(); + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + DataOutputStream dout = new DataOutputStream(bout); + + try { + /* + * Write all the items of the "ClassFile" structure. + * See JVMS section 4.1. + */ + // u4 magic; + dout.writeInt(0xCAFEBABE); + // u2 minor_version; + dout.writeShort(CLASSFILE_MINOR_VERSION); + // u2 major_version; + dout.writeShort(CLASSFILE_MAJOR_VERSION); + + cp.write(dout); // (write constant pool) + + // u2 access_flags; + dout.writeShort(accessFlags); + // u2 this_class; + dout.writeShort(cp.getClass(dotToSlash(className))); + // u2 super_class; + dout.writeShort(cp.getClass(superclassName)); + + // u2 interfaces_count; + dout.writeShort(interfaces.length); + // u2 interfaces[interfaces_count]; + for (Class intf : interfaces) { + dout.writeShort(cp.getClass( + dotToSlash(intf.getName()))); + } + + // u2 fields_count; + dout.writeShort(fields.size()); + // field_info fields[fields_count]; + for (FieldInfo f : fields) { + f.write(dout); + } + + // u2 methods_count; + dout.writeShort(methods.size()); + // method_info methods[methods_count]; + for (MethodInfo m : methods) { + m.write(dout); + } + + // u2 attributes_count; + dout.writeShort(0); // (no ClassFile attributes for proxy classes) + + } catch (IOException e) { + throw new InternalError("unexpected I/O Exception", e); + } + + return bout.toByteArray(); + } + + /** + * Add another method to be proxied, either by creating a new + * ProxyMethod object or augmenting an old one for a duplicate + * method. + * + * "fromClass" indicates the proxy interface that the method was + * found through, which may be different from (a subinterface of) + * the method's "declaring class". Note that the first Method + * object passed for a given name and descriptor identifies the + * Method object (and thus the declaring class) that will be + * passed to the invocation handler's "invoke" method for a given + * set of duplicate methods. + */ + private void addProxyMethod(Method m, Class fromClass) { + String name = m.getName(); + Class[] parameterTypes = m.getParameterTypes(); + Class returnType = m.getReturnType(); + Class[] exceptionTypes = m.getExceptionTypes(); + + String sig = name + getParameterDescriptors(parameterTypes); + List sigmethods = proxyMethods.get(sig); + if (sigmethods != null) { + for (ProxyMethod pm : sigmethods) { + if (returnType == pm.returnType) { + /* + * Found a match: reduce exception types to the + * greatest set of exceptions that can thrown + * compatibly with the throws clauses of both + * overridden methods. + */ + List> legalExceptions = new ArrayList<>(); + collectCompatibleTypes( + exceptionTypes, pm.exceptionTypes, legalExceptions); + collectCompatibleTypes( + pm.exceptionTypes, exceptionTypes, legalExceptions); + pm.exceptionTypes = new Class[legalExceptions.size()]; + pm.exceptionTypes = + legalExceptions.toArray(pm.exceptionTypes); + return; + } + } + } else { + sigmethods = new ArrayList<>(3); + proxyMethods.put(sig, sigmethods); + } + sigmethods.add(new ProxyMethod(name, parameterTypes, returnType, + exceptionTypes, fromClass)); + } + + /** + * For a given set of proxy methods with the same signature, check + * that their return types are compatible according to the Proxy + * specification. + * + * Specifically, if there is more than one such method, then all + * of the return types must be reference types, and there must be + * one return type that is assignable to each of the rest of them. + */ + private static void checkReturnTypes(List methods) { + /* + * If there is only one method with a given signature, there + * cannot be a conflict. This is the only case in which a + * primitive (or void) return type is allowed. + */ + if (methods.size() < 2) { + return; + } + + /* + * List of return types that are not yet known to be + * assignable from ("covered" by) any of the others. + */ + LinkedList> uncoveredReturnTypes = new LinkedList<>(); + + nextNewReturnType: + for (ProxyMethod pm : methods) { + Class newReturnType = pm.returnType; + if (newReturnType.isPrimitive()) { + throw new IllegalArgumentException( + "methods with same signature " + + getFriendlyMethodSignature(pm.methodName, + pm.parameterTypes) + + " but incompatible return types: " + + newReturnType.getName() + " and others"); + } + boolean added = false; + + /* + * Compare the new return type to the existing uncovered + * return types. + */ + ListIterator> liter = uncoveredReturnTypes.listIterator(); + while (liter.hasNext()) { + Class uncoveredReturnType = liter.next(); + + /* + * If an existing uncovered return type is assignable + * to this new one, then we can forget the new one. + */ + if (newReturnType.isAssignableFrom(uncoveredReturnType)) { + assert !added; + continue nextNewReturnType; + } + + /* + * If the new return type is assignable to an existing + * uncovered one, then should replace the existing one + * with the new one (or just forget the existing one, + * if the new one has already be put in the list). + */ + if (uncoveredReturnType.isAssignableFrom(newReturnType)) { + // (we can assume that each return type is unique) + if (!added) { + liter.set(newReturnType); + added = true; + } else { + liter.remove(); + } + } + } + + /* + * If we got through the list of existing uncovered return + * types without an assignability relationship, then add + * the new return type to the list of uncovered ones. + */ + if (!added) { + uncoveredReturnTypes.add(newReturnType); + } + } + + /* + * We shouldn't end up with more than one return type that is + * not assignable from any of the others. + */ + if (uncoveredReturnTypes.size() > 1) { + ProxyMethod pm = methods.get(0); + throw new IllegalArgumentException( + "methods with same signature " + + getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) + + " but incompatible return types: " + uncoveredReturnTypes); + } + } + + /** + * A FieldInfo object contains information about a particular field + * in the class being generated. The class mirrors the data items of + * the "field_info" structure of the class file format (see JVMS 4.5). + */ + private class FieldInfo { + public int accessFlags; + public String name; + public String descriptor; + + public FieldInfo(String name, String descriptor, int accessFlags) { + this.name = name; + this.descriptor = descriptor; + this.accessFlags = accessFlags; + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getUtf8(name); + cp.getUtf8(descriptor); + } + + public void write(DataOutputStream out) throws IOException { + /* + * Write all the items of the "field_info" structure. + * See JVMS section 4.5. + */ + // u2 access_flags; + out.writeShort(accessFlags); + // u2 name_index; + out.writeShort(cp.getUtf8(name)); + // u2 descriptor_index; + out.writeShort(cp.getUtf8(descriptor)); + // u2 attributes_count; + out.writeShort(0); // (no field_info attributes for proxy classes) + } + } + + /** + * An ExceptionTableEntry object holds values for the data items of + * an entry in the "exception_table" item of the "Code" attribute of + * "method_info" structures (see JVMS 4.7.3). + */ + private static class ExceptionTableEntry { + public short startPc; + public short endPc; + public short handlerPc; + public short catchType; + + public ExceptionTableEntry(short startPc, short endPc, + short handlerPc, short catchType) + { + this.startPc = startPc; + this.endPc = endPc; + this.handlerPc = handlerPc; + this.catchType = catchType; + } + }; + + /** + * A MethodInfo object contains information about a particular method + * in the class being generated. This class mirrors the data items of + * the "method_info" structure of the class file format (see JVMS 4.6). + */ + private class MethodInfo { + public int accessFlags; + public String name; + public String descriptor; + public short maxStack; + public short maxLocals; + public ByteArrayOutputStream code = new ByteArrayOutputStream(); + public List exceptionTable = + new ArrayList(); + public short[] declaredExceptions; + + public MethodInfo(String name, String descriptor, int accessFlags) { + this.name = name; + this.descriptor = descriptor; + this.accessFlags = accessFlags; + + /* + * Make sure that constant pool indexes are reserved for the + * following items before starting to write the final class file. + */ + cp.getUtf8(name); + cp.getUtf8(descriptor); + cp.getUtf8("Code"); + cp.getUtf8("Exceptions"); + } + + public void write(DataOutputStream out) throws IOException { + /* + * Write all the items of the "method_info" structure. + * See JVMS section 4.6. + */ + // u2 access_flags; + out.writeShort(accessFlags); + // u2 name_index; + out.writeShort(cp.getUtf8(name)); + // u2 descriptor_index; + out.writeShort(cp.getUtf8(descriptor)); + // u2 attributes_count; + out.writeShort(2); // (two method_info attributes:) + + // Write "Code" attribute. See JVMS section 4.7.3. + + // u2 attribute_name_index; + out.writeShort(cp.getUtf8("Code")); + // u4 attribute_length; + out.writeInt(12 + code.size() + 8 * exceptionTable.size()); + // u2 max_stack; + out.writeShort(maxStack); + // u2 max_locals; + out.writeShort(maxLocals); + // u2 code_length; + out.writeInt(code.size()); + // u1 code[code_length]; + code.writeTo(out); + // u2 exception_table_length; + out.writeShort(exceptionTable.size()); + for (ExceptionTableEntry e : exceptionTable) { + // u2 start_pc; + out.writeShort(e.startPc); + // u2 end_pc; + out.writeShort(e.endPc); + // u2 handler_pc; + out.writeShort(e.handlerPc); + // u2 catch_type; + out.writeShort(e.catchType); + } + // u2 attributes_count; + out.writeShort(0); + + // write "Exceptions" attribute. See JVMS section 4.7.4. + + // u2 attribute_name_index; + out.writeShort(cp.getUtf8("Exceptions")); + // u4 attributes_length; + out.writeInt(2 + 2 * declaredExceptions.length); + // u2 number_of_exceptions; + out.writeShort(declaredExceptions.length); + // u2 exception_index_table[number_of_exceptions]; + for (short value : declaredExceptions) { + out.writeShort(value); + } + } + + } + + /** + * A ProxyMethod object represents a proxy method in the proxy class + * being generated: a method whose implementation will encode and + * dispatch invocations to the proxy instance's invocation handler. + */ + private class ProxyMethod { + + public String methodName; + public Class[] parameterTypes; + public Class returnType; + public Class[] exceptionTypes; + public Class fromClass; + public String methodFieldName; + + private ProxyMethod(String methodName, Class[] parameterTypes, + Class returnType, Class[] exceptionTypes, + Class fromClass) + { + this.methodName = methodName; + this.parameterTypes = parameterTypes; + this.returnType = returnType; + this.exceptionTypes = exceptionTypes; + this.fromClass = fromClass; + this.methodFieldName = "m" + proxyMethodCount++; + } + + /** + * Return a MethodInfo object for this method, including generating + * the code and exception table entry. + */ + private MethodInfo generateMethod() throws IOException { + String desc = getMethodDescriptor(parameterTypes, returnType); + MethodInfo minfo = new MethodInfo(methodName, desc, + ACC_PUBLIC | ACC_FINAL); + + int[] parameterSlot = new int[parameterTypes.length]; + int nextSlot = 1; + for (int i = 0; i < parameterSlot.length; i++) { + parameterSlot[i] = nextSlot; + nextSlot += getWordsPerType(parameterTypes[i]); + } + int localSlot0 = nextSlot; + short pc, tryBegin = 0, tryEnd; + + DataOutputStream out = new DataOutputStream(minfo.code); + + code_aload(0, out); + + out.writeByte(opc_getfield); + out.writeShort(cp.getFieldRef( + superclassName, + handlerFieldName, "Ljava/lang/reflect/InvocationHandler;")); + + code_aload(0, out); + + out.writeByte(opc_getstatic); + out.writeShort(cp.getFieldRef( + dotToSlash(className), + methodFieldName, "Ljava/lang/reflect/Method;")); + + if (parameterTypes.length > 0) { + + code_ipush(parameterTypes.length, out); + + out.writeByte(opc_anewarray); + out.writeShort(cp.getClass("java/lang/Object")); + + for (int i = 0; i < parameterTypes.length; i++) { + + out.writeByte(opc_dup); + + code_ipush(i, out); + + codeWrapArgument(parameterTypes[i], parameterSlot[i], out); + + out.writeByte(opc_aastore); + } + } else { + + out.writeByte(opc_aconst_null); + } + + out.writeByte(opc_invokeinterface); + out.writeShort(cp.getInterfaceMethodRef( + "java/lang/reflect/InvocationHandler", + "invoke", + "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + + "[Ljava/lang/Object;)Ljava/lang/Object;")); + out.writeByte(4); + out.writeByte(0); + + if (returnType == void.class) { + + out.writeByte(opc_pop); + + out.writeByte(opc_return); + + } else { + + codeUnwrapReturnValue(returnType, out); + } + + tryEnd = pc = (short) minfo.code.size(); + + List> catchList = computeUniqueCatchList(exceptionTypes); + if (catchList.size() > 0) { + + for (Class ex : catchList) { + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass(dotToSlash(ex.getName())))); + } + + out.writeByte(opc_athrow); + + pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass( + "java/lang/reflect/UndeclaredThrowableException")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokespecial); + + out.writeShort(cp.getMethodRef( + "java/lang/reflect/UndeclaredThrowableException", + "", "(Ljava/lang/Throwable;)V")); + + out.writeByte(opc_athrow); + } + + if (minfo.code.size() > 65535) { + throw new IllegalArgumentException("code size limit exceeded"); + } + + minfo.maxStack = 10; + minfo.maxLocals = (short) (localSlot0 + 1); + minfo.declaredExceptions = new short[exceptionTypes.length]; + for (int i = 0; i < exceptionTypes.length; i++) { + minfo.declaredExceptions[i] = cp.getClass( + dotToSlash(exceptionTypes[i].getName())); + } + + return minfo; + } + + /** + * Generate code for wrapping an argument of the given type + * whose value can be found at the specified local variable + * index, in order for it to be passed (as an Object) to the + * invocation handler's "invoke" method. The code is written + * to the supplied stream. + */ + private void codeWrapArgument(Class type, int slot, + DataOutputStream out) + throws IOException + { + if (type.isPrimitive()) { + PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); + + if (type == int.class || + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) + { + code_iload(slot, out); + } else if (type == long.class) { + code_lload(slot, out); + } else if (type == float.class) { + code_fload(slot, out); + } else if (type == double.class) { + code_dload(slot, out); + } else { + throw new AssertionError(); + } + + out.writeByte(opc_invokestatic); + out.writeShort(cp.getMethodRef( + prim.wrapperClassName, + "valueOf", prim.wrapperValueOfDesc)); + + } else { + + code_aload(slot, out); + } + } + + /** + * Generate code for unwrapping a return value of the given + * type from the invocation handler's "invoke" method (as type + * Object) to its correct type. The code is written to the + * supplied stream. + */ + private void codeUnwrapReturnValue(Class type, DataOutputStream out) + throws IOException + { + if (type.isPrimitive()) { + PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type); + + out.writeByte(opc_checkcast); + out.writeShort(cp.getClass(prim.wrapperClassName)); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + prim.wrapperClassName, + prim.unwrapMethodName, prim.unwrapMethodDesc)); + + if (type == int.class || + type == boolean.class || + type == byte.class || + type == char.class || + type == short.class) + { + out.writeByte(opc_ireturn); + } else if (type == long.class) { + out.writeByte(opc_lreturn); + } else if (type == float.class) { + out.writeByte(opc_freturn); + } else if (type == double.class) { + out.writeByte(opc_dreturn); + } else { + throw new AssertionError(); + } + + } else { + + out.writeByte(opc_checkcast); + out.writeShort(cp.getClass(dotToSlash(type.getName()))); + + out.writeByte(opc_areturn); + } + } + + /** + * Generate code for initializing the static field that stores + * the Method object for this proxy method. The code is written + * to the supplied stream. + */ + private void codeFieldInitialization(DataOutputStream out) + throws IOException + { + codeClassForName(fromClass, out); + + code_ldc(cp.getString(methodName), out); + + code_ipush(parameterTypes.length, out); + + out.writeByte(opc_anewarray); + out.writeShort(cp.getClass("java/lang/Class")); + + for (int i = 0; i < parameterTypes.length; i++) { + + out.writeByte(opc_dup); + + code_ipush(i, out); + + if (parameterTypes[i].isPrimitive()) { + PrimitiveTypeInfo prim = + PrimitiveTypeInfo.get(parameterTypes[i]); + + out.writeByte(opc_getstatic); + out.writeShort(cp.getFieldRef( + prim.wrapperClassName, "TYPE", "Ljava/lang/Class;")); + + } else { + codeClassForName(parameterTypes[i], out); + } + + out.writeByte(opc_aastore); + } + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Class", + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)" + + "Ljava/lang/reflect/Method;")); + + out.writeByte(opc_putstatic); + out.writeShort(cp.getFieldRef( + dotToSlash(className), + methodFieldName, "Ljava/lang/reflect/Method;")); + } + } + + /** + * Generate the constructor method for the proxy class. + */ + private MethodInfo generateConstructor() throws IOException { + MethodInfo minfo = new MethodInfo( + "", "(Ljava/lang/reflect/InvocationHandler;)V", + ACC_PUBLIC); + + DataOutputStream out = new DataOutputStream(minfo.code); + + code_aload(0, out); + + code_aload(1, out); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + superclassName, + "", "(Ljava/lang/reflect/InvocationHandler;)V")); + + out.writeByte(opc_return); + + minfo.maxStack = 10; + minfo.maxLocals = 2; + minfo.declaredExceptions = new short[0]; + + return minfo; + } + + /** + * Generate the static initializer method for the proxy class. + */ + private MethodInfo generateStaticInitializer() throws IOException { + MethodInfo minfo = new MethodInfo( + "", "()V", ACC_STATIC); + + int localSlot0 = 1; + short pc, tryBegin = 0, tryEnd; + + DataOutputStream out = new DataOutputStream(minfo.code); + + for (List sigmethods : proxyMethods.values()) { + for (ProxyMethod pm : sigmethods) { + pm.codeFieldInitialization(out); + } + } + + out.writeByte(opc_return); + + tryEnd = pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass("java/lang/NoSuchMethodException"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass("java/lang/NoSuchMethodError")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + "java/lang/NoSuchMethodError", "", "(Ljava/lang/String;)V")); + + out.writeByte(opc_athrow); + + pc = (short) minfo.code.size(); + + minfo.exceptionTable.add(new ExceptionTableEntry( + tryBegin, tryEnd, pc, + cp.getClass("java/lang/ClassNotFoundException"))); + + code_astore(localSlot0, out); + + out.writeByte(opc_new); + out.writeShort(cp.getClass("java/lang/NoClassDefFoundError")); + + out.writeByte(opc_dup); + + code_aload(localSlot0, out); + + out.writeByte(opc_invokevirtual); + out.writeShort(cp.getMethodRef( + "java/lang/Throwable", "getMessage", "()Ljava/lang/String;")); + + out.writeByte(opc_invokespecial); + out.writeShort(cp.getMethodRef( + "java/lang/NoClassDefFoundError", + "", "(Ljava/lang/String;)V")); + + out.writeByte(opc_athrow); + + if (minfo.code.size() > 65535) { + throw new IllegalArgumentException("code size limit exceeded"); + } + + minfo.maxStack = 10; + minfo.maxLocals = (short) (localSlot0 + 1); + minfo.declaredExceptions = new short[0]; + + return minfo; + } + + + /* + * =============== Code Generation Utility Methods =============== + */ + + /* + * The following methods generate code for the load or store operation + * indicated by their name for the given local variable. The code is + * written to the supplied stream. + */ + + private void code_iload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_iload, opc_iload_0, out); + } + + private void code_lload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_lload, opc_lload_0, out); + } + + private void code_fload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_fload, opc_fload_0, out); + } + + private void code_dload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_dload, opc_dload_0, out); + } + + private void code_aload(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_aload, opc_aload_0, out); + } + +// private void code_istore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_istore, opc_istore_0, out); +// } + +// private void code_lstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_lstore, opc_lstore_0, out); +// } + +// private void code_fstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_fstore, opc_fstore_0, out); +// } + +// private void code_dstore(int lvar, DataOutputStream out) +// throws IOException +// { +// codeLocalLoadStore(lvar, opc_dstore, opc_dstore_0, out); +// } + + private void code_astore(int lvar, DataOutputStream out) + throws IOException + { + codeLocalLoadStore(lvar, opc_astore, opc_astore_0, out); + } + + /** + * Generate code for a load or store instruction for the given local + * variable. The code is written to the supplied stream. + * + * "opcode" indicates the opcode form of the desired load or store + * instruction that takes an explicit local variable index, and + * "opcode_0" indicates the corresponding form of the instruction + * with the implicit index 0. + */ + private void codeLocalLoadStore(int lvar, int opcode, int opcode_0, + DataOutputStream out) + throws IOException + { + assert lvar >= 0 && lvar <= 0xFFFF; + if (lvar <= 3) { + out.writeByte(opcode_0 + lvar); + } else if (lvar <= 0xFF) { + out.writeByte(opcode); + out.writeByte(lvar & 0xFF); + } else { + /* + * Use the "wide" instruction modifier for local variable + * indexes that do not fit into an unsigned byte. + */ + out.writeByte(opc_wide); + out.writeByte(opcode); + out.writeShort(lvar & 0xFFFF); + } + } + + /** + * Generate code for an "ldc" instruction for the given constant pool + * index (the "ldc_w" instruction is used if the index does not fit + * into an unsigned byte). The code is written to the supplied stream. + */ + private void code_ldc(int index, DataOutputStream out) + throws IOException + { + assert index >= 0 && index <= 0xFFFF; + if (index <= 0xFF) { + out.writeByte(opc_ldc); + out.writeByte(index & 0xFF); + } else { + out.writeByte(opc_ldc_w); + out.writeShort(index & 0xFFFF); + } + } + + /** + * Generate code to push a constant integer value on to the operand + * stack, using the "iconst_", "bipush", or "sipush" instructions + * depending on the size of the value. The code is written to the + * supplied stream. + */ + private void code_ipush(int value, DataOutputStream out) + throws IOException + { + if (value >= -1 && value <= 5) { + out.writeByte(opc_iconst_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + out.writeByte(opc_bipush); + out.writeByte(value & 0xFF); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + out.writeByte(opc_sipush); + out.writeShort(value & 0xFFFF); + } else { + throw new AssertionError(); + } + } + + /** + * Generate code to invoke the Class.forName with the name of the given + * class to get its Class object at runtime. The code is written to + * the supplied stream. Note that the code generated by this method + * may caused the checked ClassNotFoundException to be thrown. + */ + private void codeClassForName(Class cl, DataOutputStream out) + throws IOException + { + code_ldc(cp.getString(cl.getName()), out); + + out.writeByte(opc_invokestatic); + out.writeShort(cp.getMethodRef( + "java/lang/Class", + "forName", "(Ljava/lang/String;)Ljava/lang/Class;")); + } + + + /* + * ==================== General Utility Methods ==================== + */ + + /** + * Convert a fully qualified class name that uses '.' as the package + * separator, the external representation used by the Java language + * and APIs, to a fully qualified class name that uses '/' as the + * package separator, the representation used in the class file + * format (see JVMS section 4.2). + */ + private static String dotToSlash(String name) { + return name.replace('.', '/'); + } + + /** + * Return the "method descriptor" string for a method with the given + * parameter types and return type. See JVMS section 4.3.3. + */ + private static String getMethodDescriptor(Class[] parameterTypes, + Class returnType) + { + return getParameterDescriptors(parameterTypes) + + ((returnType == void.class) ? "V" : getFieldType(returnType)); + } + + /** + * Return the list of "parameter descriptor" strings enclosed in + * parentheses corresponding to the given parameter types (in other + * words, a method descriptor without a return descriptor). This + * string is useful for constructing string keys for methods without + * regard to their return type. + */ + private static String getParameterDescriptors(Class[] parameterTypes) { + StringBuilder desc = new StringBuilder("("); + for (int i = 0; i < parameterTypes.length; i++) { + desc.append(getFieldType(parameterTypes[i])); + } + desc.append(')'); + return desc.toString(); + } + + /** + * Return the "field type" string for the given type, appropriate for + * a field descriptor, a parameter descriptor, or a return descriptor + * other than "void". See JVMS section 4.3.2. + */ + private static String getFieldType(Class type) { + if (type.isPrimitive()) { + return PrimitiveTypeInfo.get(type).baseTypeString; + } else if (type.isArray()) { + /* + * According to JLS 20.3.2, the getName() method on Class does + * return the VM type descriptor format for array classes (only); + * using that should be quicker than the otherwise obvious code: + * + * return "[" + getTypeDescriptor(type.getComponentType()); + */ + return type.getName().replace('.', '/'); + } else { + return "L" + dotToSlash(type.getName()) + ";"; + } + } + + /** + * Returns a human-readable string representing the signature of a + * method with the given name and parameter types. + */ + private static String getFriendlyMethodSignature(String name, + Class[] parameterTypes) + { + StringBuilder sig = new StringBuilder(name); + sig.append('('); + for (int i = 0; i < parameterTypes.length; i++) { + if (i > 0) { + sig.append(','); + } + Class parameterType = parameterTypes[i]; + int dimensions = 0; + while (parameterType.isArray()) { + parameterType = parameterType.getComponentType(); + dimensions++; + } + sig.append(parameterType.getName()); + while (dimensions-- > 0) { + sig.append("[]"); + } + } + sig.append(')'); + return sig.toString(); + } + + /** + * Return the number of abstract "words", or consecutive local variable + * indexes, required to contain a value of the given type. See JVMS + * section 3.6.1. + * + * Note that the original version of the JVMS contained a definition of + * this abstract notion of a "word" in section 3.4, but that definition + * was removed for the second edition. + */ + private static int getWordsPerType(Class type) { + if (type == long.class || type == double.class) { + return 2; + } else { + return 1; + } + } + + /** + * Add to the given list all of the types in the "from" array that + * are not already contained in the list and are assignable to at + * least one of the types in the "with" array. + * + * This method is useful for computing the greatest common set of + * declared exceptions from duplicate methods inherited from + * different interfaces. + */ + private static void collectCompatibleTypes(Class[] from, + Class[] with, + List> list) + { + for (Class fc: from) { + if (!list.contains(fc)) { + for (Class wc: with) { + if (wc.isAssignableFrom(fc)) { + list.add(fc); + break; + } + } + } + } + } + + /** + * Given the exceptions declared in the throws clause of a proxy method, + * compute the exceptions that need to be caught from the invocation + * handler's invoke method and rethrown intact in the method's + * implementation before catching other Throwables and wrapping them + * in UndeclaredThrowableExceptions. + * + * The exceptions to be caught are returned in a List object. Each + * exception in the returned list is guaranteed to not be a subclass of + * any of the other exceptions in the list, so the catch blocks for + * these exceptions may be generated in any order relative to each other. + * + * Error and RuntimeException are each always contained by the returned + * list (if none of their superclasses are contained), since those + * unchecked exceptions should always be rethrown intact, and thus their + * subclasses will never appear in the returned list. + * + * The returned List will be empty if java.lang.Throwable is in the + * given list of declared exceptions, indicating that no exceptions + * need to be caught. + */ + private static List> computeUniqueCatchList(Class[] exceptions) { + List> uniqueList = new ArrayList<>(); + // unique exceptions to catch + + uniqueList.add(Error.class); // always catch/rethrow these + uniqueList.add(RuntimeException.class); + + nextException: + for (Class ex: exceptions) { + if (ex.isAssignableFrom(Throwable.class)) { + /* + * If Throwable is declared to be thrown by the proxy method, + * then no catch blocks are necessary, because the invoke + * can, at most, throw Throwable anyway. + */ + uniqueList.clear(); + break; + } else if (!Throwable.class.isAssignableFrom(ex)) { + /* + * Ignore types that cannot be thrown by the invoke method. + */ + continue; + } + /* + * Compare this exception against the current list of + * exceptions that need to be caught: + */ + for (int j = 0; j < uniqueList.size();) { + Class ex2 = uniqueList.get(j); + if (ex2.isAssignableFrom(ex)) { + /* + * if a superclass of this exception is already on + * the list to catch, then ignore this one and continue; + */ + continue nextException; + } else if (ex.isAssignableFrom(ex2)) { + /* + * if a subclass of this exception is on the list + * to catch, then remove it; + */ + uniqueList.remove(j); + } else { + j++; // else continue comparing. + } + } + // This exception is unique (so far): add it to the list to catch. + uniqueList.add(ex); + } + return uniqueList; + } + + /** + * A PrimitiveTypeInfo object contains assorted information about + * a primitive type in its public fields. The struct for a particular + * primitive type can be obtained using the static "get" method. + */ + private static class PrimitiveTypeInfo { + + /** "base type" used in various descriptors (see JVMS section 4.3.2) */ + public String baseTypeString; + + /** name of corresponding wrapper class */ + public String wrapperClassName; + + /** method descriptor for wrapper class "valueOf" factory method */ + public String wrapperValueOfDesc; + + /** name of wrapper class method for retrieving primitive value */ + public String unwrapMethodName; + + /** descriptor of same method */ + public String unwrapMethodDesc; + + private static Map,PrimitiveTypeInfo> table = new HashMap<>(); + static { + add(byte.class, Byte.class); + add(char.class, Character.class); + add(double.class, Double.class); + add(float.class, Float.class); + add(int.class, Integer.class); + add(long.class, Long.class); + add(short.class, Short.class); + add(boolean.class, Boolean.class); + } + + private static void add(Class primitiveClass, Class wrapperClass) { + table.put(primitiveClass, + new PrimitiveTypeInfo(primitiveClass, wrapperClass)); + } + + private PrimitiveTypeInfo(Class primitiveClass, Class wrapperClass) { + assert primitiveClass.isPrimitive(); + + baseTypeString = + Array.newInstance(primitiveClass, 0) + .getClass().getName().substring(1); + wrapperClassName = dotToSlash(wrapperClass.getName()); + wrapperValueOfDesc = + "(" + baseTypeString + ")L" + wrapperClassName + ";"; + unwrapMethodName = primitiveClass.getName() + "Value"; + unwrapMethodDesc = "()" + baseTypeString; + } + + public static PrimitiveTypeInfo get(Class cl) { + return table.get(cl); + } + } + + + /** + * A ConstantPool object represents the constant pool of a class file + * being generated. This representation of a constant pool is designed + * specifically for use by ProxyGenerator; in particular, it assumes + * that constant pool entries will not need to be resorted (for example, + * by their type, as the Java compiler does), so that the final index + * value can be assigned and used when an entry is first created. + * + * Note that new entries cannot be created after the constant pool has + * been written to a class file. To prevent such logic errors, a + * ConstantPool instance can be marked "read only", so that further + * attempts to add new entries will fail with a runtime exception. + * + * See JVMS section 4.4 for more information about the constant pool + * of a class file. + */ + private static class ConstantPool { + + /** + * list of constant pool entries, in constant pool index order. + * + * This list is used when writing the constant pool to a stream + * and for assigning the next index value. Note that element 0 + * of this list corresponds to constant pool index 1. + */ + private List pool = new ArrayList<>(32); + + /** + * maps constant pool data of all types to constant pool indexes. + * + * This map is used to look up the index of an existing entry for + * values of all types. + */ + private Map map = new HashMap<>(16); + + /** true if no new constant pool entries may be added */ + private boolean readOnly = false; + + /** + * Get or assign the index for a CONSTANT_Utf8 entry. + */ + public short getUtf8(String s) { + if (s == null) { + throw new NullPointerException(); + } + return getValue(s); + } + + /** + * Get or assign the index for a CONSTANT_Integer entry. + */ + public short getInteger(int i) { + return getValue(new Integer(i)); + } + + /** + * Get or assign the index for a CONSTANT_Float entry. + */ + public short getFloat(float f) { + return getValue(new Float(f)); + } + + /** + * Get or assign the index for a CONSTANT_Class entry. + */ + public short getClass(String name) { + short utf8Index = getUtf8(name); + return getIndirect(new IndirectEntry( + CONSTANT_CLASS, utf8Index)); + } + + /** + * Get or assign the index for a CONSTANT_String entry. + */ + public short getString(String s) { + short utf8Index = getUtf8(s); + return getIndirect(new IndirectEntry( + CONSTANT_STRING, utf8Index)); + } + + /** + * Get or assign the index for a CONSTANT_FieldRef entry. + */ + public short getFieldRef(String className, + String name, String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_FIELD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_MethodRef entry. + */ + public short getMethodRef(String className, + String name, String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_METHOD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_InterfaceMethodRef entry. + */ + public short getInterfaceMethodRef(String className, String name, + String descriptor) + { + short classIndex = getClass(className); + short nameAndTypeIndex = getNameAndType(name, descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_INTERFACEMETHOD, classIndex, nameAndTypeIndex)); + } + + /** + * Get or assign the index for a CONSTANT_NameAndType entry. + */ + public short getNameAndType(String name, String descriptor) { + short nameIndex = getUtf8(name); + short descriptorIndex = getUtf8(descriptor); + return getIndirect(new IndirectEntry( + CONSTANT_NAMEANDTYPE, nameIndex, descriptorIndex)); + } + + /** + * Set this ConstantPool instance to be "read only". + * + * After this method has been called, further requests to get + * an index for a non-existent entry will cause an InternalError + * to be thrown instead of creating of the entry. + */ + public void setReadOnly() { + readOnly = true; + } + + /** + * Write this constant pool to a stream as part of + * the class file format. + * + * This consists of writing the "constant_pool_count" and + * "constant_pool[]" items of the "ClassFile" structure, as + * described in JVMS section 4.1. + */ + public void write(OutputStream out) throws IOException { + DataOutputStream dataOut = new DataOutputStream(out); + + // constant_pool_count: number of entries plus one + dataOut.writeShort(pool.size() + 1); + + for (Entry e : pool) { + e.write(dataOut); + } + } + + /** + * Add a new constant pool entry and return its index. + */ + private short addEntry(Entry entry) { + pool.add(entry); + /* + * Note that this way of determining the index of the + * added entry is wrong if this pool supports + * CONSTANT_Long or CONSTANT_Double entries. + */ + if (pool.size() >= 65535) { + throw new IllegalArgumentException( + "constant pool size limit exceeded"); + } + return (short) pool.size(); + } + + /** + * Get or assign the index for an entry of a type that contains + * a direct value. The type of the given object determines the + * type of the desired entry as follows: + * + * java.lang.String CONSTANT_Utf8 + * java.lang.Integer CONSTANT_Integer + * java.lang.Float CONSTANT_Float + * java.lang.Long CONSTANT_Long + * java.lang.Double CONSTANT_DOUBLE + */ + private short getValue(Object key) { + Short index = map.get(key); + if (index != null) { + return index.shortValue(); + } else { + if (readOnly) { + throw new InternalError( + "late constant pool addition: " + key); + } + short i = addEntry(new ValueEntry(key)); + map.put(key, new Short(i)); + return i; + } + } + + /** + * Get or assign the index for an entry of a type that contains + * references to other constant pool entries. + */ + private short getIndirect(IndirectEntry e) { + Short index = map.get(e); + if (index != null) { + return index.shortValue(); + } else { + if (readOnly) { + throw new InternalError("late constant pool addition"); + } + short i = addEntry(e); + map.put(e, new Short(i)); + return i; + } + } + + /** + * Entry is the abstact superclass of all constant pool entry types + * that can be stored in the "pool" list; its purpose is to define a + * common method for writing constant pool entries to a class file. + */ + private static abstract class Entry { + public abstract void write(DataOutputStream out) + throws IOException; + } + + /** + * ValueEntry represents a constant pool entry of a type that + * contains a direct value (see the comments for the "getValue" + * method for a list of such types). + * + * ValueEntry objects are not used as keys for their entries in the + * Map "map", so no useful hashCode or equals methods are defined. + */ + private static class ValueEntry extends Entry { + private Object value; + + public ValueEntry(Object value) { + this.value = value; + } + + public void write(DataOutputStream out) throws IOException { + if (value instanceof String) { + out.writeByte(CONSTANT_UTF8); + out.writeUTF((String) value); + } else if (value instanceof Integer) { + out.writeByte(CONSTANT_INTEGER); + out.writeInt(((Integer) value).intValue()); + } else if (value instanceof Float) { + out.writeByte(CONSTANT_FLOAT); + out.writeFloat(((Float) value).floatValue()); + } else if (value instanceof Long) { + out.writeByte(CONSTANT_LONG); + out.writeLong(((Long) value).longValue()); + } else if (value instanceof Double) { + out.writeDouble(CONSTANT_DOUBLE); + out.writeDouble(((Double) value).doubleValue()); + } else { + throw new InternalError("bogus value entry: " + value); + } + } + } + + /** + * IndirectEntry represents a constant pool entry of a type that + * references other constant pool entries, i.e., the following types: + * + * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref, + * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and + * CONSTANT_NameAndType. + * + * Each of these entry types contains either one or two indexes of + * other constant pool entries. + * + * IndirectEntry objects are used as the keys for their entries in + * the Map "map", so the hashCode and equals methods are overridden + * to allow matching. + */ + private static class IndirectEntry extends Entry { + private int tag; + private short index0; + private short index1; + + /** + * Construct an IndirectEntry for a constant pool entry type + * that contains one index of another entry. + */ + public IndirectEntry(int tag, short index) { + this.tag = tag; + this.index0 = index; + this.index1 = 0; + } + + /** + * Construct an IndirectEntry for a constant pool entry type + * that contains two indexes for other entries. + */ + public IndirectEntry(int tag, short index0, short index1) { + this.tag = tag; + this.index0 = index0; + this.index1 = index1; + } + + public void write(DataOutputStream out) throws IOException { + out.writeByte(tag); + out.writeShort(index0); + /* + * If this entry type contains two indexes, write + * out the second, too. + */ + if (tag == CONSTANT_FIELD || + tag == CONSTANT_METHOD || + tag == CONSTANT_INTERFACEMETHOD || + tag == CONSTANT_NAMEANDTYPE) + { + out.writeShort(index1); + } + } + + public int hashCode() { + return tag + index0 + index1; + } + + public boolean equals(Object obj) { + if (obj instanceof IndirectEntry) { + IndirectEntry other = (IndirectEntry) obj; + if (tag == other.tag && + index0 == other.index0 && index1 == other.index1) + { + return true; + } + } + return false; + } + } + } +} diff --git a/src/sun/misc/Queue.java b/src/sun/misc/Queue.java new file mode 100644 index 00000000..da76fe6a --- /dev/null +++ b/src/sun/misc/Queue.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +/** + * Queue: implements a simple queue mechanism. Allows for enumeration of the + * elements. + * + * @author Herb Jellinek + */ + +public class Queue { + + int length = 0; + + QueueElement head = null; + QueueElement tail = null; + + public Queue() { + } + + /** + * Enqueue an object. + */ + public synchronized void enqueue(T obj) { + + QueueElement newElt = new QueueElement<>(obj); + + if (head == null) { + head = newElt; + tail = newElt; + length = 1; + } else { + newElt.next = head; + head.prev = newElt; + head = newElt; + length++; + } + notify(); + } + + /** + * Dequeue the oldest object on the queue. Will wait indefinitely. + * + * @return the oldest object on the queue. + * @exception InterruptedException if any thread has + * interrupted this thread. + */ + public T dequeue() throws InterruptedException { + return dequeue(0L); + } + + /** + * Dequeue the oldest object on the queue. + * @param timeOut the number of milliseconds to wait for something + * to arrive. + * + * @return the oldest object on the queue. + * @exception InterruptedException if any thread has + * interrupted this thread. + */ + public synchronized T dequeue(long timeOut) + throws InterruptedException { + + while (tail == null) { + wait(timeOut); + } + QueueElement elt = tail; + tail = elt.prev; + if (tail == null) { + head = null; + } else { + tail.next = null; + } + length--; + return elt.obj; + } + + /** + * Is the queue empty? + * @return true if the queue is empty. + */ + public synchronized boolean isEmpty() { + return (tail == null); + } + + /** + * Returns an enumeration of the elements in Last-In, First-Out + * order. Use the Enumeration methods on the returned object to + * fetch the elements sequentially. + */ + public final synchronized Enumeration elements() { + return new LIFOQueueEnumerator<>(this); + } + + /** + * Returns an enumeration of the elements in First-In, First-Out + * order. Use the Enumeration methods on the returned object to + * fetch the elements sequentially. + */ + public final synchronized Enumeration reverseElements() { + return new FIFOQueueEnumerator<>(this); + } + + public synchronized void dump(String msg) { + System.err.println(">> "+msg); + System.err.println("["+length+" elt(s); head = "+ + (head == null ? "null" : (head.obj)+"")+ + " tail = "+(tail == null ? "null" : (tail.obj)+"")); + QueueElement cursor = head; + QueueElement last = null; + while (cursor != null) { + System.err.println(" "+cursor); + last = cursor; + cursor = cursor.next; + } + if (last != tail) { + System.err.println(" tail != last: "+tail+", "+last); + } + System.err.println("]"); + } +} + +final class FIFOQueueEnumerator implements Enumeration { + Queue queue; + QueueElement cursor; + + FIFOQueueEnumerator(Queue q) { + queue = q; + cursor = q.tail; + } + + public boolean hasMoreElements() { + return (cursor != null); + } + + public T nextElement() { + synchronized (queue) { + if (cursor != null) { + QueueElement result = cursor; + cursor = cursor.prev; + return result.obj; + } + } + throw new NoSuchElementException("FIFOQueueEnumerator"); + } +} + +final class LIFOQueueEnumerator implements Enumeration { + Queue queue; + QueueElement cursor; + + LIFOQueueEnumerator(Queue q) { + queue = q; + cursor = q.head; + } + + public boolean hasMoreElements() { + return (cursor != null); + } + + public T nextElement() { + synchronized (queue) { + if (cursor != null) { + QueueElement result = cursor; + cursor = cursor.next; + return result.obj; + } + } + throw new NoSuchElementException("LIFOQueueEnumerator"); + } +} + +class QueueElement { + QueueElement next = null; + QueueElement prev = null; + + T obj = null; + + QueueElement(T obj) { + this.obj = obj; + } + + public String toString() { + return "QueueElement[obj="+obj+(prev == null ? " null" : " prev")+ + (next == null ? " null" : " next")+"]"; + } +} diff --git a/src/sun/misc/REException.java b/src/sun/misc/REException.java new file mode 100644 index 00000000..db68992d --- /dev/null +++ b/src/sun/misc/REException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * A class to signal exception from the RegexpPool class. + * @author James Gosling + */ + +public class REException extends Exception { + + private static final long serialVersionUID = 4656584872733646963L; + + REException (String s) { + super(s); + } +} diff --git a/src/sun/misc/Ref.java b/src/sun/misc/Ref.java new file mode 100644 index 00000000..770cb3d4 --- /dev/null +++ b/src/sun/misc/Ref.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; +import java.lang.ref.SoftReference; + + +/** + * A "Ref" is an indirect reference to an object that the garbage collector + * knows about. An application should override the reconstitute() method with one + * that will construct the object based on information in the Ref, often by + * reading from a file. The get() method retains a cache of the result of the last call to + * reconstitute() in the Ref. When space gets tight, the garbage collector + * will clear old Ref cache entries when there are no other pointers to the + * object. In normal usage, Ref will always be subclassed. The subclass will add the + * instance variables necessary for the reconstitute() method to work. It will also add a + * constructor to set them up, and write a version of reconstitute(). + * + * @deprecated This class has been replaced by + * java.util.SoftReference. + * + * @see java.util.SoftReference + * + */ +@Deprecated + +public abstract class Ref { + + private SoftReference soft = null; + + /** + * Returns a pointer to the object referenced by this Ref. If the object + * has been thrown away by the garbage collector, it will be + * reconstituted. This method does everything necessary to ensure that the garbage + * collector throws things away in Least Recently Used(LRU) order. Applications should + * never override this method. The get() method effectively caches calls to + * reconstitute(). + */ + public synchronized Object get() { + Object t = check(); + if (t == null) { + t = reconstitute(); + setThing(t); + } + return t; + } + + /** + * Returns a pointer to the object referenced by this Ref by + * reconstituting it from some external source (such as a file). This method should not + * bother with caching since the method get() will deal with that. + *

    + * In normal usage, Ref will always be subclassed. The subclass will add + * the instance variables necessary for reconstitute() to work. It will + * also add a constructor to set them up, and write a version of + * reconstitute(). + */ + public abstract Object reconstitute(); + + /** + * Flushes the cached object. Forces the next invocation of get() to + * invoke reconstitute(). + */ + public synchronized void flush() { + SoftReference s = soft; + if (s != null) s.clear(); + soft = null; + } + + /** + * Sets the thing to the specified object. + * @param thing the specified object + */ + public synchronized void setThing(Object thing) { + flush(); + soft = new SoftReference(thing); + } + + /** + * Checks to see what object is being pointed at by this Ref and returns it. + */ + public synchronized Object check() { + SoftReference s = soft; + if (s == null) return null; + return s.get(); + } + + /** + * Constructs a new Ref. + */ + public Ref() { } + + /** + * Constructs a new Ref that initially points to thing. + */ + public Ref(Object thing) { + setThing(thing); + } + +} diff --git a/src/sun/misc/Regexp.java b/src/sun/misc/Regexp.java new file mode 100644 index 00000000..4d488363 --- /dev/null +++ b/src/sun/misc/Regexp.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * A class to represent a regular expression. Only handles '*'s. + * @author James Gosling + */ + +public class Regexp { + /** if true then the matching process ignores case. */ + public boolean ignoreCase; + + /* + * regular expressions are carved into three regions: a constant string + * prefix, a constant string suffix, and a series of floating strings in + * between. In the input regular expression, they are separated by *s + */ + public String exp; + public String prefix, suffix; + public boolean exact; + public int prefixLen, suffixLen, totalLen; + public String mids[]; + + /** Create a new regular expression object. The regular expression + is a series of constant strings separated by *s. For example: +

    +
    *.gif
    Matches any string that ends in ".gif". +
    /tmp/*
    Matches any string that starts with "/tmp/". +
    /tmp/*.gif
    Matches any string that starts with "/tmp/" and ends + with ".gif". +
    /tmp/*new*.gif
    Matches any string that starts with "/tmp/" + and ends with ".gif" and has "new" somewhere in between. +
    + */ + public Regexp (String s) { + exp = s; + int firstst = s.indexOf('*'); + int lastst = s.lastIndexOf('*'); + if (firstst < 0) { + totalLen = s.length(); + exact = true; // no * s + } else { + prefixLen = firstst; + if (firstst == 0) + prefix = null; + else + prefix = s.substring(0, firstst); + suffixLen = s.length() - lastst - 1; + if (suffixLen == 0) + suffix = null; + else + suffix = s.substring(lastst + 1); + int nmids = 0; + int pos = firstst; + while (pos < lastst && pos >= 0) { + nmids++; + pos = s.indexOf('*', pos + 1); + } + totalLen = prefixLen + suffixLen; + if (nmids > 0) { + mids = new String[nmids]; + pos = firstst; + for (int i = 0; i < nmids; i++) { + pos++; + int npos = s.indexOf('*', pos); + if (pos < npos) { + mids[i] = s.substring(pos, npos); + totalLen += mids[i].length(); + } + pos = npos; + } + } + } + } + + /** Returns true iff the String s matches this regular expression. */ + final boolean matches(String s) { + return matches(s, 0, s.length()); + } + + /** Returns true iff the substring of s from offset for len characters + matches this regular expression. */ + boolean matches(String s, int offset, int len) { + if (exact) + return len == totalLen && + exp.regionMatches(ignoreCase, 0, s, offset, len); + if (len < totalLen) + return false; + if (prefixLen > 0 && + !prefix.regionMatches(ignoreCase, + 0, s, offset, prefixLen) + || + suffixLen > 0 && + !suffix.regionMatches(ignoreCase, + 0, s, offset + len - suffixLen, + suffixLen)) + return false; + if (mids == null) + return true; + int nmids = mids.length; + int spos = offset + prefixLen; + int limit = offset+len-suffixLen; + for (int i = 0; ilimit) + return false; + spos+=ml; + } + return true; + } + +} diff --git a/src/sun/misc/RegexpPool.java b/src/sun/misc/RegexpPool.java new file mode 100644 index 00000000..30eb5844 --- /dev/null +++ b/src/sun/misc/RegexpPool.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 1995, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.PrintStream; + +/** + * A class to represent a pool of regular expressions. A string + * can be matched against the whole pool all at once. It is much + * faster than doing individual regular expression matches one-by-one. + * + * @see java.misc.RegexpTarget + * @author James Gosling + */ + +public class RegexpPool { + private RegexpNode prefixMachine = new RegexpNode(); + private RegexpNode suffixMachine = new RegexpNode(); + private static final int BIG = 0x7FFFFFFF; + private int lastDepth = BIG; + + public RegexpPool () { + } + + /** + * Add a regular expression to the pool of regular expressions. + * @param re The regular expression to add to the pool. + For now, only handles strings that either begin or end with + a '*'. + * @param ret The object to be returned when this regular expression is + matched. If ret is an instance of the RegexpTarget class, ret.found + is called with the string fragment that matched the '*' as its + parameter. + * @exception REException error + */ + public void add(String re, Object ret) throws REException { + add(re, ret, false); + } + + /** + * Replace the target for the regular expression with a different + * target. + * + * @param re The regular expression to be replaced in the pool. + * For now, only handles strings that either begin or end with + * a '*'. + * @param ret The object to be returned when this regular expression is + * matched. If ret is an instance of the RegexpTarget class, ret.found + * is called with the string fragment that matched the '*' as its + * parameter. + */ + public void replace(String re, Object ret) { + try { + add(re, ret, true); + } catch(Exception e) { + // should never occur if replace is true + } + } + + /** + * Delete the regular expression and its target. + * @param re The regular expression to be deleted from the pool. + * must begin or end with a '*' + * @return target - the old target. + */ + public Object delete(String re) { + Object o = null; + RegexpNode p = prefixMachine; + RegexpNode best = p; + int len = re.length() - 1; + int i; + boolean prefix = true; + + if (!re.startsWith("*") || + !re.endsWith("*")) + len++; + + if (len <= 0) + return null; + + /* March forward through the prefix machine */ + for (i = 0; p != null; i++) { + if (p.result != null && p.depth < BIG + && (!p.exact || i == len)) { + best = p; + } + if (i >= len) + break; + p = p.find(re.charAt(i)); + } + + /* march backward through the suffix machine */ + p = suffixMachine; + for (i = len; --i >= 0 && p != null;) { + if (p.result != null && p.depth < BIG) { + prefix = false; + best = p; + } + p = p.find(re.charAt(i)); + } + + // delete only if there is an exact match + if (prefix) { + if (re.equals(best.re)) { + o = best.result; + best.result = null; + + } + } else { + if (re.equals(best.re)) { + o = best.result; + best.result = null; + } + } + return o; + } + + /** Search for a match to a string & return the object associated + with it with the match. When multiple regular expressions + would match the string, the best match is returned first. + The next best match is returned the next time matchNext is + called. + @param s The string to match against the regular expressions + in the pool. + @return null on failure, otherwise the object associated with + the regular expression when it was added to the pool. + If the object is an instance of RegexpTarget, then + the return value is the result from calling + return.found(string_that_matched_wildcard). + */ + public Object match(String s) { + return matchAfter(s, BIG); + } + + /** Identical to match except that it will only find matches to + regular expressions that were added to the pool after + the last regular expression that matched in the last call + to match() or matchNext() */ + public Object matchNext(String s) { + return matchAfter(s, lastDepth); + } + + private void add(String re, Object ret, boolean replace) throws REException { + int len = re.length(); + RegexpNode p; + if (re.charAt(0) == '*') { + p = suffixMachine; + while (len > 1) + p = p.add(re.charAt(--len)); + } else { + boolean exact = false; + if (re.charAt(len - 1) == '*') + len--; + else + exact = true; + p = prefixMachine; + for (int i = 0; i < len; i++) + p = p.add(re.charAt(i)); + p.exact = exact; + } + + if (p.result != null && !replace) + throw new REException(re + " is a duplicate"); + + p.re = re; + p.result = ret; + } + + private Object matchAfter(String s, int lastMatchDepth) { + RegexpNode p = prefixMachine; + RegexpNode best = p; + int bst = 0; + int bend = 0; + int len = s.length(); + int i; + if (len <= 0) + return null; + /* March forward through the prefix machine */ + for (i = 0; p != null; i++) { + if (p.result != null && p.depth < lastMatchDepth + && (!p.exact || i == len)) { + lastDepth = p.depth; + best = p; + bst = i; + bend = len; + } + if (i >= len) + break; + p = p.find(s.charAt(i)); + } + /* march backward through the suffix machine */ + p = suffixMachine; + for (i = len; --i >= 0 && p != null;) { + if (p.result != null && p.depth < lastMatchDepth) { + lastDepth = p.depth; + best = p; + bst = 0; + bend = i+1; + } + p = p.find(s.charAt(i)); + } + Object o = best.result; + if (o != null && o instanceof RegexpTarget) + o = ((RegexpTarget) o).found(s.substring(bst, bend)); + return o; + } + + /** Resets the pool so that the next call to matchNext looks + at all regular expressions in the pool. match(s); is equivalent + to reset(); matchNext(s); +

    Multithreading note: reset/nextMatch leave state in the + regular expression pool. If multiple threads could be using this + pool this way, they should be syncronized to avoid race hazards. + match() was done in such a way that there are no such race + hazards: multiple threads can be matching in the same pool + simultaneously. */ + public void reset() { + lastDepth = BIG; + } + + /** Print this pool to standard output */ + public void print(PrintStream out) { + out.print("Regexp pool:\n"); + if (suffixMachine.firstchild != null) { + out.print(" Suffix machine: "); + suffixMachine.firstchild.print(out); + out.print("\n"); + } + if (prefixMachine.firstchild != null) { + out.print(" Prefix machine: "); + prefixMachine.firstchild.print(out); + out.print("\n"); + } + } + +} + +/* A node in a regular expression finite state machine. */ +class RegexpNode { + char c; + RegexpNode firstchild; + RegexpNode nextsibling; + int depth; + boolean exact; + Object result; + String re = null; + + RegexpNode () { + c = '#'; + depth = 0; + } + RegexpNode (char C, int depth) { + c = C; + this.depth = depth; + } + RegexpNode add(char C) { + RegexpNode p = firstchild; + if (p == null) + p = new RegexpNode (C, depth+1); + else { + while (p != null) + if (p.c == C) + return p; + else + p = p.nextsibling; + p = new RegexpNode (C, depth+1); + p.nextsibling = firstchild; + } + firstchild = p; + return p; + } + RegexpNode find(char C) { + for (RegexpNode p = firstchild; + p != null; + p = p.nextsibling) + if (p.c == C) + return p; + return null; + } + void print(PrintStream out) { + if (nextsibling != null) { + RegexpNode p = this; + out.print("("); + while (p != null) { + out.write(p.c); + if (p.firstchild != null) + p.firstchild.print(out); + p = p.nextsibling; + out.write(p != null ? '|' : ')'); + } + } else { + out.write(c); + if (firstchild != null) + firstchild.print(out); + } + } +} diff --git a/src/sun/misc/RegexpTarget.java b/src/sun/misc/RegexpTarget.java new file mode 100644 index 00000000..a44028ac --- /dev/null +++ b/src/sun/misc/RegexpTarget.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1995, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * A class to define actions to be performed when a regular expression match + * occurs. + * @author James Gosling + */ + +public interface RegexpTarget { + /** Gets called when a pattern in a RegexpPool matches. + * This method is called by RegexpPool.match() who passes the return + * value from found() back to its caller. + * @param remainder the string that matched the * in the pattern. + */ + Object found(String remainder); +} diff --git a/src/sun/misc/Request.java b/src/sun/misc/Request.java new file mode 100644 index 00000000..5301e4cc --- /dev/null +++ b/src/sun/misc/Request.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * Requests are functor objects; that is, they provide part of the mechanism + * for deferred function application. + * + * @author Steven B. Byrne + */ + +abstract public class Request { + /** + * The main task of the Request object is to be exectuted from a request + * queue. + */ + abstract public void execute(); +} diff --git a/src/sun/misc/RequestProcessor.java b/src/sun/misc/RequestProcessor.java new file mode 100644 index 00000000..54a62724 --- /dev/null +++ b/src/sun/misc/RequestProcessor.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * The request processor allows functors (Request instances) to be created + * in arbitrary threads, and to be posted for execution in a non-restricted + * thread. + * + * @author Steven B. Byrne + */ + + +public class RequestProcessor implements Runnable { + + private static Queue requestQueue; + private static Thread dispatcher; + + /** + * Queues a Request instance for execution by the request procesor + * thread. + */ + public static void postRequest(Request req) { + lazyInitialize(); + requestQueue.enqueue(req); + } + + /** + * Process requests as they are queued. + */ + public void run() { + lazyInitialize(); + while (true) { + try { + Request req = requestQueue.dequeue(); + try { + req.execute(); + } catch (Throwable t) { + // do nothing at the moment...maybe report an error + // in the future + } + } catch (InterruptedException e) { + // do nothing at the present time. + } + } + } + + + /** + * This method initiates the request processor thread. It is safe + * to call it after the thread has been started. It provides a way for + * clients to deliberately control the context in which the request + * processor thread is created + */ + public static synchronized void startProcessing() { + if (dispatcher == null) { + dispatcher = new Thread(new RequestProcessor(), "Request Processor"); + dispatcher.setPriority(Thread.NORM_PRIORITY + 2); + dispatcher.start(); + } + } + + + /** + * This method performs lazy initialization. + */ + private static synchronized void lazyInitialize() { + if (requestQueue == null) { + requestQueue = new Queue(); + } + } + +} diff --git a/src/sun/misc/Resource.java b/src/sun/misc/Resource.java new file mode 100644 index 00000000..4c0df219 --- /dev/null +++ b/src/sun/misc/Resource.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.security.CodeSigner; +import java.util.Arrays; +import java.util.jar.Manifest; + +import sun.nio.ByteBuffered; + +/** + * This class is used to represent a Resource that has been loaded + * from the class path. + * + * @author David Connelly + * @since 1.2 + */ +public abstract class Resource { + /** + * Returns the name of the Resource. + */ + public abstract String getName(); + + /** + * Returns the URL of the Resource. + */ + public abstract URL getURL(); + + /** + * Returns the CodeSource URL for the Resource. + */ + public abstract URL getCodeSourceURL(); + + /** + * Returns an InputStream for reading the Resource data. + */ + public abstract InputStream getInputStream() throws IOException; + + /** + * Returns the length of the Resource data, or -1 if unknown. + */ + public abstract int getContentLength() throws IOException; + + private InputStream cis; + + /* Cache result in case getBytes is called after getByteBuffer. */ + private synchronized InputStream cachedInputStream() throws IOException { + if (cis == null) { + cis = getInputStream(); + } + return cis; + } + + /** + * Returns the Resource data as an array of bytes. + */ + public byte[] getBytes() throws IOException { + byte[] b; + // Get stream before content length so that a FileNotFoundException + // can propagate upwards without being caught too early + InputStream in = cachedInputStream(); + + // This code has been uglified to protect against interrupts. + // Even if a thread has been interrupted when loading resources, + // the IO should not abort, so must carefully retry, failing only + // if the retry leads to some other IO exception. + + boolean isInterrupted = Thread.interrupted(); + int len; + for (;;) { + try { + len = getContentLength(); + break; + } catch (InterruptedIOException iioe) { + Thread.interrupted(); + isInterrupted = true; + } + } + + try { + b = new byte[0]; + if (len == -1) len = Integer.MAX_VALUE; + int pos = 0; + while (pos < len) { + int bytesToRead; + if (pos >= b.length) { // Only expand when there's no room + bytesToRead = Math.min(len - pos, b.length + 1024); + if (b.length < pos + bytesToRead) { + b = Arrays.copyOf(b, pos + bytesToRead); + } + } else { + bytesToRead = b.length - pos; + } + int cc = 0; + try { + cc = in.read(b, pos, bytesToRead); + } catch (InterruptedIOException iioe) { + Thread.interrupted(); + isInterrupted = true; + } + if (cc < 0) { + if (len != Integer.MAX_VALUE) { + throw new EOFException("Detect premature EOF"); + } else { + if (b.length != pos) { + b = Arrays.copyOf(b, pos); + } + break; + } + } + pos += cc; + } + } finally { + try { + in.close(); + } catch (InterruptedIOException iioe) { + isInterrupted = true; + } catch (IOException ignore) {} + + if (isInterrupted) { + Thread.currentThread().interrupt(); + } + } + return b; + } + + /** + * Returns the Resource data as a ByteBuffer, but only if the input stream + * was implemented on top of a ByteBuffer. Return null otherwise. + */ + public ByteBuffer getByteBuffer() throws IOException { + InputStream in = cachedInputStream(); + if (in instanceof ByteBuffered) { + return ((ByteBuffered)in).getByteBuffer(); + } + return null; + } + + /** + * Returns the Manifest for the Resource, or null if none. + */ + public Manifest getManifest() throws IOException { + return null; + } + + /** + * Returns theCertificates for the Resource, or null if none. + */ + public java.security.cert.Certificate[] getCertificates() { + return null; + } + + /** + * Returns the code signers for the Resource, or null if none. + */ + public CodeSigner[] getCodeSigners() { + return null; + } +} diff --git a/src/sun/misc/Service.java b/src/sun/misc/Service.java new file mode 100644 index 00000000..29cc43d5 --- /dev/null +++ b/src/sun/misc/Service.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.TreeSet; + + +/** + * A simple service-provider lookup mechanism. A service is a + * well-known set of interfaces and (usually abstract) classes. A service + * provider is a specific implementation of a service. The classes in a + * provider typically implement the interfaces and subclass the classes defined + * in the service itself. Service providers may be installed in an + * implementation of the Java platform in the form of extensions, that is, jar + * files placed into any of the usual extension directories. Providers may + * also be made available by adding them to the applet or application class + * path or by some other platform-specific means. + * + *

    In this lookup mechanism a service is represented by an interface or an + * abstract class. (A concrete class may be used, but this is not + * recommended.) A provider of a given service contains one or more concrete + * classes that extend this service class with data and code specific to + * the provider. This provider class will typically not be the entire + * provider itself but rather a proxy that contains enough information to + * decide whether the provider is able to satisfy a particular request together + * with code that can create the actual provider on demand. The details of + * provider classes tend to be highly service-specific; no single class or + * interface could possibly unify them, so no such class has been defined. The + * only requirement enforced here is that provider classes must have a + * zero-argument constructor so that they may be instantiated during lookup. + * + *

    A service provider identifies itself by placing a provider-configuration + * file in the resource directory META-INF/services. The file's name + * should consist of the fully-qualified name of the abstract service class. + * The file should contain a list of fully-qualified concrete provider-class + * names, one per line. Space and tab characters surrounding each name, as + * well as blank lines, are ignored. The comment character is '#' + * (0x23); on each line all characters following the first comment + * character are ignored. The file must be encoded in UTF-8. + * + *

    If a particular concrete provider class is named in more than one + * configuration file, or is named in the same configuration file more than + * once, then the duplicates will be ignored. The configuration file naming a + * particular provider need not be in the same jar file or other distribution + * unit as the provider itself. The provider must be accessible from the same + * class loader that was initially queried to locate the configuration file; + * note that this is not necessarily the class loader that found the file. + * + *

    Example: Suppose we have a service class named + * java.io.spi.CharCodec. It has two abstract methods: + * + *

    + *   public abstract CharEncoder getEncoder(String encodingName);
    + *   public abstract CharDecoder getDecoder(String encodingName);
    + * 
    + * + * Each method returns an appropriate object or null if it cannot + * translate the given encoding. Typical CharCodec providers will + * support more than one encoding. + * + *

    If sun.io.StandardCodec is a provider of the CharCodec + * service then its jar file would contain the file + * META-INF/services/java.io.spi.CharCodec. This file would contain + * the single line: + * + *

    + *   sun.io.StandardCodec    # Standard codecs for the platform
    + * 
    + * + * To locate an encoder for a given encoding name, the internal I/O code would + * do something like this: + * + *
    + *   CharEncoder getEncoder(String encodingName) {
    + *       Iterator ps = Service.providers(CharCodec.class);
    + *       while (ps.hasNext()) {
    + *           CharCodec cc = (CharCodec)ps.next();
    + *           CharEncoder ce = cc.getEncoder(encodingName);
    + *           if (ce != null)
    + *               return ce;
    + *       }
    + *       return null;
    + *   }
    + * 
    + * + * The provider-lookup mechanism always executes in the security context of the + * caller. Trusted system code should typically invoke the methods in this + * class from within a privileged security context. + * + * @author Mark Reinhold + * @since 1.3 + */ + +public final class Service { + + private static final String prefix = "META-INF/services/"; + + private Service() { } + + private static void fail(Class service, String msg, Throwable cause) + throws ServiceConfigurationError + { + ServiceConfigurationError sce + = new ServiceConfigurationError(service.getName() + ": " + msg); + sce.initCause(cause); + throw sce; + } + + private static void fail(Class service, String msg) + throws ServiceConfigurationError + { + throw new ServiceConfigurationError(service.getName() + ": " + msg); + } + + private static void fail(Class service, URL u, int line, String msg) + throws ServiceConfigurationError + { + fail(service, u + ":" + line + ": " + msg); + } + + /** + * Parse a single line from the given configuration file, adding the name + * on the line to both the names list and the returned set iff the name is + * not already a member of the returned set. + */ + private static int parseLine(Class service, URL u, BufferedReader r, int lc, + List names, Set returned) + throws IOException, ServiceConfigurationError + { + String ln = r.readLine(); + if (ln == null) { + return -1; + } + int ci = ln.indexOf('#'); + if (ci >= 0) ln = ln.substring(0, ci); + ln = ln.trim(); + int n = ln.length(); + if (n != 0) { + if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) + fail(service, u, lc, "Illegal configuration-file syntax"); + int cp = ln.codePointAt(0); + if (!Character.isJavaIdentifierStart(cp)) + fail(service, u, lc, "Illegal provider-class name: " + ln); + for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) { + cp = ln.codePointAt(i); + if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) + fail(service, u, lc, "Illegal provider-class name: " + ln); + } + if (!returned.contains(ln)) { + names.add(ln); + returned.add(ln); + } + } + return lc + 1; + } + + /** + * Parse the content of the given URL as a provider-configuration file. + * + * @param service + * The service class for which providers are being sought; + * used to construct error detail strings + * + * @param url + * The URL naming the configuration file to be parsed + * + * @param returned + * A Set containing the names of provider classes that have already + * been returned. This set will be updated to contain the names + * that will be yielded from the returned Iterator. + * + * @return A (possibly empty) Iterator that will yield the + * provider-class names in the given configuration file that are + * not yet members of the returned set + * + * @throws ServiceConfigurationError + * If an I/O error occurs while reading from the given URL, or + * if a configuration-file format error is detected + */ + private static Iterator parse(Class service, URL u, Set returned) + throws ServiceConfigurationError + { + InputStream in = null; + BufferedReader r = null; + ArrayList names = new ArrayList<>(); + try { + in = u.openStream(); + r = new BufferedReader(new InputStreamReader(in, "utf-8")); + int lc = 1; + while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0); + } catch (IOException x) { + fail(service, ": " + x); + } finally { + try { + if (r != null) r.close(); + if (in != null) in.close(); + } catch (IOException y) { + fail(service, ": " + y); + } + } + return names.iterator(); + } + + + /** + * Private inner class implementing fully-lazy provider lookup + */ + private static class LazyIterator implements Iterator { + + Class service; + ClassLoader loader; + Enumeration configs = null; + Iterator pending = null; + Set returned = new TreeSet<>(); + String nextName = null; + + private LazyIterator(Class service, ClassLoader loader) { + this.service = service; + this.loader = loader; + } + + public boolean hasNext() throws ServiceConfigurationError { + if (nextName != null) { + return true; + } + if (configs == null) { + try { + String fullName = prefix + service.getName(); + if (loader == null) + configs = ClassLoader.getSystemResources(fullName); + else + configs = loader.getResources(fullName); + } catch (IOException x) { + fail(service, ": " + x); + } + } + while ((pending == null) || !pending.hasNext()) { + if (!configs.hasMoreElements()) { + return false; + } + pending = parse(service, configs.nextElement(), returned); + } + nextName = pending.next(); + return true; + } + + public S next() throws ServiceConfigurationError { + if (!hasNext()) { + throw new NoSuchElementException(); + } + String cn = nextName; + nextName = null; + Class c = null; + try { + c = Class.forName(cn, false, loader); + } catch (ClassNotFoundException x) { + fail(service, + "Provider " + cn + " not found"); + } + if (!service.isAssignableFrom(c)) { + fail(service, + "Provider " + cn + " not a subtype"); + } + try { + return service.cast(c.newInstance()); + } catch (Throwable x) { + fail(service, + "Provider " + cn + " could not be instantiated", + x); + } + return null; /* This cannot happen */ + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + + /** + * Locates and incrementally instantiates the available providers of a + * given service using the given class loader. + * + *

    This method transforms the name of the given service class into a + * provider-configuration filename as described above and then uses the + * getResources method of the given class loader to find all + * available files with that name. These files are then read and parsed to + * produce a list of provider-class names. The iterator that is returned + * uses the given class loader to lookup and then instantiate each element + * of the list. + * + *

    Because it is possible for extensions to be installed into a running + * Java virtual machine, this method may return different results each time + * it is invoked.

    + * + * @param service + * The service's abstract service class + * + * @param loader + * The class loader to be used to load provider-configuration files + * and instantiate provider classes, or null if the system + * class loader (or, failing that the bootstrap class loader) is to + * be used + * + * @return An Iterator that yields provider objects for the given + * service, in some arbitrary order. The iterator will throw a + * ServiceConfigurationError if a provider-configuration + * file violates the specified format or if a provider class cannot + * be found and instantiated. + * + * @throws ServiceConfigurationError + * If a provider-configuration file violates the specified format + * or names a provider class that cannot be found and instantiated + * + * @see #providers(Class) + * @see #installedProviders(Class) + */ + public static Iterator providers(Class service, ClassLoader loader) + throws ServiceConfigurationError + { + return new LazyIterator(service, loader); + } + + + /** + * Locates and incrementally instantiates the available providers of a + * given service using the context class loader. This convenience method + * is equivalent to + * + *

    +     *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
    +     *   return Service.providers(service, cl);
    +     * 
    + * + * @param service + * The service's abstract service class + * + * @return An Iterator that yields provider objects for the given + * service, in some arbitrary order. The iterator will throw a + * ServiceConfigurationError if a provider-configuration + * file violates the specified format or if a provider class cannot + * be found and instantiated. + * + * @throws ServiceConfigurationError + * If a provider-configuration file violates the specified format + * or names a provider class that cannot be found and instantiated + * + * @see #providers(Class, ClassLoader) + */ + public static Iterator providers(Class service) + throws ServiceConfigurationError + { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return Service.providers(service, cl); + } + + + /** + * Locates and incrementally instantiates the available providers of a + * given service using the extension class loader. This convenience method + * simply locates the extension class loader, call it + * extClassLoader, and then does + * + *
    +     *   return Service.providers(service, extClassLoader);
    +     * 
    + * + * If the extension class loader cannot be found then the system class + * loader is used; if there is no system class loader then the bootstrap + * class loader is used. + * + * @param service + * The service's abstract service class + * + * @return An Iterator that yields provider objects for the given + * service, in some arbitrary order. The iterator will throw a + * ServiceConfigurationError if a provider-configuration + * file violates the specified format or if a provider class cannot + * be found and instantiated. + * + * @throws ServiceConfigurationError + * If a provider-configuration file violates the specified format + * or names a provider class that cannot be found and instantiated + * + * @see #providers(Class, ClassLoader) + */ + public static Iterator installedProviders(Class service) + throws ServiceConfigurationError + { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + ClassLoader prev = null; + while (cl != null) { + prev = cl; + cl = cl.getParent(); + } + return Service.providers(service, prev); + } + +} diff --git a/src/sun/misc/ServiceConfigurationError.java b/src/sun/misc/ServiceConfigurationError.java new file mode 100644 index 00000000..1060f207 --- /dev/null +++ b/src/sun/misc/ServiceConfigurationError.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + + +/** + * Error thrown when something goes wrong while looking up service providers. + * In particular, this error will be thrown in the following situations: + * + *
      + *
    • A concrete provider class cannot be found, + *
    • A concrete provider class cannot be instantiated, + *
    • The format of a provider-configuration file is illegal, or + *
    • An IOException occurs while reading a provider-configuration file. + *
    + * + * @author Mark Reinhold + * @since 1.3 + */ + +public class ServiceConfigurationError extends Error { + + static final long serialVersionUID = 8769866263384244465L; + + /** + * Constructs a new instance with the specified detail string. + */ + public ServiceConfigurationError(String msg) { + super(msg); + } + + /** + * Constructs a new instance that wraps the specified throwable. + */ + public ServiceConfigurationError(Throwable x) { + super(x); + } + +} diff --git a/src/sun/misc/SharedSecrets.java b/src/sun/misc/SharedSecrets.java new file mode 100644 index 00000000..9b1e7f3f --- /dev/null +++ b/src/sun/misc/SharedSecrets.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.Console; +import java.io.FileDescriptor; +import java.security.AccessController; +import java.security.ProtectionDomain; +import java.security.Signature; +import java.util.jar.JarFile; + +/** A repository of "shared secrets", which are a mechanism for + calling implementation-private methods in another package without + using reflection. A package-private class implements a public + interface and provides the ability to call package-private methods + within that package; the object implementing that interface is + provided through a third package to which access is restricted. + This framework avoids the primary disadvantage of using reflection + for this purpose, namely the loss of compile-time checking. */ + +public class SharedSecrets { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static JavaUtilJarAccess javaUtilJarAccess; + private static JavaLangAccess javaLangAccess; + private static JavaIOAccess javaIOAccess; + private static JavaNetAccess javaNetAccess; + private static JavaNetHttpCookieAccess javaNetHttpCookieAccess; + private static JavaNioAccess javaNioAccess; + private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; + private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess; + private static JavaSecurityAccess javaSecurityAccess; + private static JavaUtilZipFileAccess javaUtilZipFileAccess; + private static JavaAWTAccess javaAWTAccess; + private static JavaSecuritySignatureAccess javaSecuritySignatureAccess; + + public static JavaUtilJarAccess javaUtilJarAccess() { + if (javaUtilJarAccess == null) { + // Ensure JarFile is initialized; we know that that class + // provides the shared secret + unsafe.ensureClassInitialized(JarFile.class); + } + return javaUtilJarAccess; + } + + public static void setJavaUtilJarAccess(JavaUtilJarAccess access) { + javaUtilJarAccess = access; + } + + public static void setJavaLangAccess(JavaLangAccess jla) { + javaLangAccess = jla; + } + + public static JavaLangAccess getJavaLangAccess() { + return javaLangAccess; + } + + public static void setJavaNetAccess(JavaNetAccess jna) { + javaNetAccess = jna; + } + + public static JavaNetAccess getJavaNetAccess() { + return javaNetAccess; + } + + public static void setJavaNetHttpCookieAccess(JavaNetHttpCookieAccess a) { + javaNetHttpCookieAccess = a; + } + + public static JavaNetHttpCookieAccess getJavaNetHttpCookieAccess() { + if (javaNetHttpCookieAccess == null) + unsafe.ensureClassInitialized(java.net.HttpCookie.class); + return javaNetHttpCookieAccess; + } + + public static void setJavaNioAccess(JavaNioAccess jna) { + javaNioAccess = jna; + } + + public static JavaNioAccess getJavaNioAccess() { + if (javaNioAccess == null) { + // Ensure java.nio.ByteOrder is initialized; we know that + // this class initializes java.nio.Bits that provides the + // shared secret. + unsafe.ensureClassInitialized(java.nio.ByteOrder.class); + } + return javaNioAccess; + } + + public static void setJavaIOAccess(JavaIOAccess jia) { + javaIOAccess = jia; + } + + public static JavaIOAccess getJavaIOAccess() { + if (javaIOAccess == null) { + unsafe.ensureClassInitialized(Console.class); + } + return javaIOAccess; + } + + public static void setJavaIOFileDescriptorAccess(JavaIOFileDescriptorAccess jiofda) { + javaIOFileDescriptorAccess = jiofda; + } + + public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() { + if (javaIOFileDescriptorAccess == null) + unsafe.ensureClassInitialized(FileDescriptor.class); + + return javaIOFileDescriptorAccess; + } + + public static void setJavaSecurityProtectionDomainAccess + (JavaSecurityProtectionDomainAccess jspda) { + javaSecurityProtectionDomainAccess = jspda; + } + + public static JavaSecurityProtectionDomainAccess + getJavaSecurityProtectionDomainAccess() { + if (javaSecurityProtectionDomainAccess == null) + unsafe.ensureClassInitialized(ProtectionDomain.class); + return javaSecurityProtectionDomainAccess; + } + + public static void setJavaSecurityAccess(JavaSecurityAccess jsa) { + javaSecurityAccess = jsa; + } + + public static JavaSecurityAccess getJavaSecurityAccess() { + if (javaSecurityAccess == null) { + unsafe.ensureClassInitialized(AccessController.class); + } + return javaSecurityAccess; + } + + public static JavaUtilZipFileAccess getJavaUtilZipFileAccess() { + if (javaUtilZipFileAccess == null) + unsafe.ensureClassInitialized(java.util.zip.ZipFile.class); + return javaUtilZipFileAccess; + } + + public static void setJavaUtilZipFileAccess(JavaUtilZipFileAccess access) { + javaUtilZipFileAccess = access; + } + + public static void setJavaAWTAccess(JavaAWTAccess jaa) { + javaAWTAccess = jaa; + } + + public static JavaAWTAccess getJavaAWTAccess() { + // this may return null in which case calling code needs to + // provision for. + if (javaAWTAccess == null) { + return null; + } + return javaAWTAccess; + } + + public static void setJavaSecuritySignatureAccess(JavaSecuritySignatureAccess jssa) { + javaSecuritySignatureAccess = jssa; + } + + public static JavaSecuritySignatureAccess getJavaSecuritySignatureAccess() { + if (javaSecuritySignatureAccess == null) { + unsafe.ensureClassInitialized(Signature.class); + } + return javaSecuritySignatureAccess; + } +} diff --git a/src/sun/misc/Signal.java b/src/sun/misc/Signal.java new file mode 100644 index 00000000..a8eddf14 --- /dev/null +++ b/src/sun/misc/Signal.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; +import java.util.Hashtable; + +/** + * This class provides ANSI/ISO C signal support. A Java program can register + * signal handlers for the current process. There are two restrictions: + *
      + *
    • + * Java code cannot register a handler for signals that are already used + * by the Java VM implementation. The Signal.handle + * function raises an IllegalArgumentException if such an attempt + * is made. + *
    • + * When Signal.handle is called, the VM internally registers a + * special C signal handler. There is no way to force the Java signal handler + * to run synchronously before the C signal handler returns. Instead, when the + * VM receives a signal, the special C signal handler creates a new thread + * (at priority Thread.MAX_PRIORITY) to + * run the registered Java signal handler. The C signal handler immediately + * returns. Note that because the Java signal handler runs in a newly created + * thread, it may not actually be executed until some time after the C signal + * handler returns. + *
    + *

    + * Signal objects are created based on their names. For example: + *

    + * new Signal("INT");
    + * 
    + * constructs a signal object corresponding to SIGINT, which is + * typically produced when the user presses Ctrl-C at the command line. + * The Signal constructor throws IllegalArgumentException + * when it is passed an unknown signal. + *

    + * This is an example of how Java code handles SIGINT: + *

    + * SignalHandler handler = new SignalHandler () {
    + *     public void handle(Signal sig) {
    + *       ... // handle SIGINT
    + *     }
    + * };
    + * Signal.handle(new Signal("INT"), handler);
    + * 
    + * + * @author Sheng Liang + * @author Bill Shannon + * @see SignalHandler + * @since 1.2 + */ +public final class Signal { + private static Hashtable handlers = new Hashtable<>(4); + private static Hashtable signals = new Hashtable<>(4); + + private int number; + private String name; + + /* Returns the signal number */ + public int getNumber() { + return number; + } + + /** + * Returns the signal name. + * + * @return the name of the signal. + * @see Signal#Signal(String name) + */ + public String getName() { + return name; + } + + /** + * Compares the equality of two Signal objects. + * + * @param other the object to compare with. + * @return whether two Signal objects are equal. + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || !(other instanceof Signal)) { + return false; + } + Signal other1 = (Signal)other; + return name.equals(other1.name) && (number == other1.number); + } + + /** + * Returns a hashcode for this Signal. + * + * @return a hash code value for this object. + */ + public int hashCode() { + return number; + } + + /** + * Returns a string representation of this signal. For example, "SIGINT" + * for an object constructed using new Signal ("INT"). + * + * @return a string representation of the signal + */ + public String toString() { + return "SIG" + name; + } + + /** + * Constructs a signal from its name. + * + * @param name the name of the signal. + * @exception IllegalArgumentException unknown signal + * @see Signal#getName() + */ + public Signal(String name) { + number = findSignal(name); + this.name = name; + if (number < 0) { + throw new IllegalArgumentException("Unknown signal: " + name); + } + } + + /** + * Registers a signal handler. + * + * @param sig a signal + * @param handler the handler to be registered with the given signal. + * @result the old handler + * @exception IllegalArgumentException the signal is in use by the VM + * @see Signal#raise(Signal sig) + * @see SignalHandler + * @see SignalHandler#SIG_DFL + * @see SignalHandler#SIG_IGN + */ + public static synchronized SignalHandler handle(Signal sig, + SignalHandler handler) + throws IllegalArgumentException { + long newH = (handler instanceof NativeSignalHandler) ? + ((NativeSignalHandler)handler).getHandler() : 2; + long oldH = handle0(sig.number, newH); + if (oldH == -1) { + throw new IllegalArgumentException + ("Signal already used by VM or OS: " + sig); + } + signals.put(sig.number, sig); + synchronized (handlers) { + SignalHandler oldHandler = handlers.get(sig); + handlers.remove(sig); + if (newH == 2) { + handlers.put(sig, handler); + } + if (oldH == 0) { + return SignalHandler.SIG_DFL; + } else if (oldH == 1) { + return SignalHandler.SIG_IGN; + } else if (oldH == 2) { + return oldHandler; + } else { + return new NativeSignalHandler(oldH); + } + } + } + + /** + * Raises a signal in the current process. + * + * @param sig a signal + * @see Signal#handle(Signal sig, SignalHandler handler) + */ + public static void raise(Signal sig) throws IllegalArgumentException { + if (handlers.get(sig) == null) { + throw new IllegalArgumentException("Unhandled signal: " + sig); + } + raise0(sig.number); + } + + /* Called by the VM to execute Java signal handlers. */ + private static void dispatch(final int number) { + final Signal sig = signals.get(number); + final SignalHandler handler = handlers.get(sig); + + Runnable runnable = new Runnable () { + public void run() { + // Don't bother to reset the priority. Signal handler will + // run at maximum priority inherited from the VM signal + // dispatch thread. + // Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + handler.handle(sig); + } + }; + if (handler != null) { + new Thread(runnable, sig + " handler").start(); + } + } + + /* Find the signal number, given a name. Returns -1 for unknown signals. */ + private static native int findSignal(String sigName); + /* Registers a native signal handler, and returns the old handler. + * Handler values: + * 0 default handler + * 1 ignore the signal + * 2 call back to Signal.dispatch + * other arbitrary native signal handlers + */ + private static native long handle0(int sig, long nativeH); + /* Raise a given signal number */ + private static native void raise0(int sig); +} diff --git a/src/sun/misc/SignalHandler.java b/src/sun/misc/SignalHandler.java new file mode 100644 index 00000000..fe820949 --- /dev/null +++ b/src/sun/misc/SignalHandler.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * This is the signal handler interface expected in Signal.handle. + * + * @author Sheng Liang + * @author Bill Shannon + * @see Signal + * @since 1.2 + */ + +public interface SignalHandler { + + /** + * The default signal handler + */ + public static final SignalHandler SIG_DFL = new NativeSignalHandler(0); + /** + * Ignore the signal + */ + public static final SignalHandler SIG_IGN = new NativeSignalHandler(1); + + /** + * Handle the given signal + * + * @param sig a signal object + */ + public void handle(Signal sig); +} diff --git a/src/sun/misc/SoftCache.java b/src/sun/misc/SoftCache.java new file mode 100644 index 00000000..048fa7e3 --- /dev/null +++ b/src/sun/misc/SoftCache.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + + +/** + * A memory-sensitive implementation of the Map interface. + * + *

    A SoftCache object uses {@link SoftReference + * soft references} to implement a memory-sensitive hash map. If the garbage + * collector determines at a certain point in time that a value object in a + * SoftCache entry is no longer strongly reachable, then it may + * remove that entry in order to release the memory occupied by the value + * object. All SoftCache objects are guaranteed to be completely + * cleared before the virtual machine will throw an + * OutOfMemoryError. Because of this automatic clearing feature, + * the behavior of this class is somewhat different from that of other + * Map implementations. + * + *

    Both null values and the null key are supported. This class has the + * same performance characteristics as the HashMap class, and has + * the same efficiency parameters of initial capacity and load + * factor. + * + *

    Like most collection classes, this class is not synchronized. A + * synchronized SoftCache may be constructed using the + * Collections.synchronizedMap method. + * + *

    In typical usage this class will be subclassed and the fill + * method will be overridden. When the get method is invoked on a + * key for which there is no mapping in the cache, it will in turn invoke the + * fill method on that key in an attempt to construct a + * corresponding value. If the fill method returns such a value + * then the cache will be updated and the new value will be returned. Thus, + * for example, a simple URL-content cache can be constructed as follows: + * + *

    + *     public class URLCache extends SoftCache {
    + *         protected Object fill(Object key) {
    + *             return ((URL)key).getContent();
    + *         }
    + *     }
    + * 
    + * + *

    The behavior of the SoftCache class depends in part upon + * the actions of the garbage collector, so several familiar (though not + * required) Map invariants do not hold for this class.

    + * Because entries are removed from a SoftCache in response to + * dynamic advice from the garbage collector, a SoftCache may + * behave as though an unknown thread is silently removing entries. In + * particular, even if you synchronize on a SoftCache instance and + * invoke none of its mutator methods, it is possible for the size + * method to return smaller values over time, for the isEmpty + * method to return false and then true, for the + * containsKey method to return true and later + * false for a given key, for the get method to + * return a value for a given key but later return null, for the + * put method to return null and the + * remove method to return false for a key that + * previously appeared to be in the map, and for successive examinations of the + * key set, the value set, and the entry set to yield successively smaller + * numbers of elements. + * + * @author Mark Reinhold + * @since 1.2 + * @see HashMap + * @see SoftReference + */ + + +public class SoftCache extends AbstractMap implements Map { + + /* The basic idea of this implementation is to maintain an internal HashMap + that maps keys to soft references whose referents are the keys' values; + the various accessor methods dereference these soft references before + returning values. Because we don't have access to the innards of the + HashMap, each soft reference must contain the key that maps to it so + that the processQueue method can remove keys whose values have been + discarded. Thus the HashMap actually maps keys to instances of the + ValueCell class, which is a simple extension of the SoftReference class. + */ + + + static private class ValueCell extends SoftReference { + static private Object INVALID_KEY = new Object(); + static private int dropped = 0; + private Object key; + + private ValueCell(Object key, Object value, ReferenceQueue queue) { + super(value, queue); + this.key = key; + } + + private static ValueCell create(Object key, Object value, + ReferenceQueue queue) + { + if (value == null) return null; + return new ValueCell(key, value, queue); + } + + private static Object strip(Object val, boolean drop) { + if (val == null) return null; + ValueCell vc = (ValueCell)val; + Object o = vc.get(); + if (drop) vc.drop(); + return o; + } + + private boolean isValid() { + return (key != INVALID_KEY); + } + + private void drop() { + super.clear(); + key = INVALID_KEY; + dropped++; + } + + } + + + /* Hash table mapping keys to ValueCells */ + private Map hash; + + /* Reference queue for cleared ValueCells */ + private ReferenceQueue queue = new ReferenceQueue(); + + + /* Process any ValueCells that have been cleared and enqueued by the + garbage collector. This method should be invoked once by each public + mutator in this class. We don't invoke this method in public accessors + because that can lead to surprising ConcurrentModificationExceptions. + */ + private void processQueue() { + ValueCell vc; + while ((vc = (ValueCell)queue.poll()) != null) { + if (vc.isValid()) hash.remove(vc.key); + else ValueCell.dropped--; + } + } + + + /* -- Constructors -- */ + + /** + * Construct a new, empty SoftCache with the given + * initial capacity and the given load factor. + * + * @param initialCapacity The initial capacity of the cache + * + * @param loadFactor A number between 0.0 and 1.0 + * + * @throws IllegalArgumentException If the initial capacity is less than + * or equal to zero, or if the load + * factor is less than zero + */ + public SoftCache(int initialCapacity, float loadFactor) { + hash = new HashMap(initialCapacity, loadFactor); + } + + /** + * Construct a new, empty SoftCache with the given + * initial capacity and the default load factor. + * + * @param initialCapacity The initial capacity of the cache + * + * @throws IllegalArgumentException If the initial capacity is less than + * or equal to zero + */ + public SoftCache(int initialCapacity) { + hash = new HashMap(initialCapacity); + } + + /** + * Construct a new, empty SoftCache with the default + * capacity and the default load factor. + */ + public SoftCache() { + hash = new HashMap(); + } + + + /* -- Simple queries -- */ + + /** + * Return the number of key-value mappings in this cache. The time + * required by this operation is linear in the size of the map. + */ + public int size() { + return entrySet().size(); + } + + /** + * Return true if this cache contains no key-value mappings. + */ + public boolean isEmpty() { + return entrySet().isEmpty(); + } + + /** + * Return true if this cache contains a mapping for the + * specified key. If there is no mapping for the key, this method will not + * attempt to construct one by invoking the fill method. + * + * @param key The key whose presence in the cache is to be tested + */ + public boolean containsKey(Object key) { + return ValueCell.strip(hash.get(key), false) != null; + } + + + /* -- Lookup and modification operations -- */ + + /** + * Create a value object for the given key. This method is + * invoked by the get method when there is no entry for + * key. If this method returns a non-null value, + * then the cache will be updated to map key to that value, + * and that value will be returned by the get method. + * + *

    The default implementation of this method simply returns + * null for every key value. A subclass may + * override this method to provide more useful behavior. + * + * @param key The key for which a value is to be computed + * + * @return A value for key, or null if one + * could not be computed + * @see #get + */ + protected Object fill(Object key) { + return null; + } + + /** + * Return the value to which this cache maps the specified + * key. If the cache does not presently contain a value for + * this key, then invoke the fill method in an attempt to + * compute such a value. If that method returns a non-null + * value, then update the cache and return the new value. Otherwise, + * return null. + * + *

    Note that because this method may update the cache, it is considered + * a mutator and may cause ConcurrentModificationExceptions to + * be thrown if invoked while an iterator is in use. + * + * @param key The key whose associated value, if any, is to be returned + * + * @see #fill + */ + public Object get(Object key) { + processQueue(); + Object v = hash.get(key); + if (v == null) { + v = fill(key); + if (v != null) { + hash.put(key, ValueCell.create(key, v, queue)); + return v; + } + } + return ValueCell.strip(v, false); + } + + /** + * Update this cache so that the given key maps to the given + * value. If the cache previously contained a mapping for + * key then that mapping is replaced and the old value is + * returned. + * + * @param key The key that is to be mapped to the given + * value + * @param value The value to which the given key is to be + * mapped + * + * @return The previous value to which this key was mapped, or + * null if if there was no mapping for the key + */ + public Object put(Object key, Object value) { + processQueue(); + ValueCell vc = ValueCell.create(key, value, queue); + return ValueCell.strip(hash.put(key, vc), true); + } + + /** + * Remove the mapping for the given key from this cache, if + * present. + * + * @param key The key whose mapping is to be removed + * + * @return The value to which this key was mapped, or null if + * there was no mapping for the key + */ + public Object remove(Object key) { + processQueue(); + return ValueCell.strip(hash.remove(key), true); + } + + /** + * Remove all mappings from this cache. + */ + public void clear() { + processQueue(); + hash.clear(); + } + + + /* -- Views -- */ + + private static boolean valEquals(Object o1, Object o2) { + return (o1 == null) ? (o2 == null) : o1.equals(o2); + } + + + /* Internal class for entries. + Because it uses SoftCache.this.queue, this class cannot be static. + */ + private class Entry implements Map.Entry { + private Map.Entry ent; + private Object value; /* Strong reference to value, to prevent the GC + from flushing the value while this Entry + exists */ + + Entry(Map.Entry ent, Object value) { + this.ent = ent; + this.value = value; + } + + public Object getKey() { + return ent.getKey(); + } + + public Object getValue() { + return value; + } + + public Object setValue(Object value) { + return ent.setValue(ValueCell.create(ent.getKey(), value, queue)); + } + + public boolean equals(Object o) { + if (! (o instanceof Map.Entry)) return false; + Map.Entry e = (Map.Entry)o; + return (valEquals(ent.getKey(), e.getKey()) + && valEquals(value, e.getValue())); + } + + public int hashCode() { + Object k; + return ((((k = getKey()) == null) ? 0 : k.hashCode()) + ^ ((value == null) ? 0 : value.hashCode())); + } + + } + + + /* Internal class for entry sets */ + private class EntrySet extends AbstractSet { + Set hashEntries = hash.entrySet(); + + public Iterator iterator() { + + return new Iterator() { + Iterator hashIterator = hashEntries.iterator(); + Entry next = null; + + public boolean hasNext() { + while (hashIterator.hasNext()) { + Map.Entry ent = (Map.Entry)hashIterator.next(); + ValueCell vc = (ValueCell)ent.getValue(); + Object v = null; + if ((vc != null) && ((v = vc.get()) == null)) { + /* Value has been flushed by GC */ + continue; + } + next = new Entry(ent, v); + return true; + } + return false; + } + + public Object next() { + if ((next == null) && !hasNext()) + throw new NoSuchElementException(); + Entry e = next; + next = null; + return e; + } + + public void remove() { + hashIterator.remove(); + } + + }; + } + + public boolean isEmpty() { + return !(iterator().hasNext()); + } + + public int size() { + int j = 0; + for (Iterator i = iterator(); i.hasNext(); i.next()) j++; + return j; + } + + public boolean remove(Object o) { + processQueue(); + if (o instanceof Entry) return hashEntries.remove(((Entry)o).ent); + else return false; + } + + } + + + private Set entrySet = null; + + /** + * Return a Set view of the mappings in this cache. + */ + public Set entrySet() { + if (entrySet == null) entrySet = new EntrySet(); + return entrySet; + } + +} diff --git a/src/sun/misc/ThreadGroupUtils.java b/src/sun/misc/ThreadGroupUtils.java new file mode 100644 index 00000000..066a8002 --- /dev/null +++ b/src/sun/misc/ThreadGroupUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * A utility class needed to access the root {@code ThreadGroup} + * + * The class should not depend on any others, because it' called from JNI_OnLoad of the AWT + * native library. Triggering class loading could could lead to a deadlock. + */ +public final class ThreadGroupUtils { + + private ThreadGroupUtils() { + // Avoid instantiation + } + + /** + * Returns a root thread group. + * Should be called with {@link sun.security.util.SecurityConstants#MODIFY_THREADGROUP_PERMISSION} + * + * @return a root {@code ThreadGroup} + */ + public static ThreadGroup getRootThreadGroup() { + ThreadGroup currentTG = Thread.currentThread().getThreadGroup(); + ThreadGroup parentTG = currentTG.getParent(); + while (parentTG != null) { + currentTG = parentTG; + parentTG = currentTG.getParent(); + } + return currentTG; + } +} diff --git a/src/sun/misc/Timeable.java b/src/sun/misc/Timeable.java new file mode 100644 index 00000000..da6c60db --- /dev/null +++ b/src/sun/misc/Timeable.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1995, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + * This interface is used by the Timer class. A class that uses + * Timer objects must implement this interface. + * + * @see Timer + * @author Patrick Chan + */ +public interface Timeable { + /** + * This method is executed every time a timer owned by this + * object ticks. An object can own more than one timer but + * all timers call this method when they tick; + * you can use the supplied timer parameter to determine + * which timer has ticked. + * @param timer a handle to the timer that has just ticked. + */ + public void tick(Timer timer); +} diff --git a/src/sun/misc/Timer.java b/src/sun/misc/Timer.java new file mode 100644 index 00000000..71e0881b --- /dev/null +++ b/src/sun/misc/Timer.java @@ -0,0 +1,647 @@ +/* + * Copyright (c) 1995, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** + A Timer object is used by algorithms that require timed events. + For example, in an animation loop, a timer would help in + determining when to change frames. + + A timer has an interval which determines when it "ticks"; + that is, a timer delays for the specified interval and then + it calls the owner's tick() method. + + Here's an example of creating a timer with a 5 sec interval: + +

    +    class Main implements Timeable {
    +        public void tick(Timer timer) {
    +            System.out.println("tick");
    +        }
    +        public static void main(String args[]) {
    +            (new Timer(this, 5000)).cont();
    +        }
    +    }
    +    
    + + A timer can be stopped, continued, or reset at any time. + A timer's state is not stopped while it's calling the + owner's tick() method. + + A timer can be regular or irregular. If in regular mode, + a timer ticks at the specified interval, regardless of + how long the owner's tick() method takes. While the timer + is running, no ticks are ever discarded. That means that if + the owner's tick() method takes longer than the interval, + the ticks that would have occurred are delivered immediately. + + In irregular mode, a timer starts delaying for exactly + the specified interval only after the tick() method returns. + + Synchronization issues: do not hold the timer's monitor + while calling any of the Timer operations below otherwise + the Timer class will deadlock. + + @author Patrick Chan +*/ + +/* + Synchronization issues: there are two data structures that + require locking. A Timer object and the Timer queue + (described in the TimerThread class). To avoid deadlock, + the timer queue monitor is always acquired before the timer + object's monitor. However, the timer queue monitor is acquired + only if the timer operation will make use of the timer + queue, e.g. stop(). + + The class monitor on the class TimerThread severs as the monitor + to the timer queue. + + Possible feature: perhaps a timer should have an associated + thread priority. The thread that makes the callback temporarily + takes on that priority before calling the owner's tick() method. +*/ + +public class Timer { + /** + * This is the owner of the timer. Its tick method is + * called when the timer ticks. + */ + public Timeable owner; + + /* + * This is the interval of time in ms. + */ + long interval; + + /* + * This variable is used for two different purposes. + * This is done in order to save space. + * If 'stopped' is true, this variable holds the time + * that the timer was stopped; otherwise, this variable + * is used by the TimerThread to determine when the timer + * should tick. + */ + long sleepUntil; + + /* + * This is the time remaining before the timer ticks. It + * is only valid if 'stopped' is true. If the timer is + * continued, the next tick will happen remaingTime + * milliseconds later. + */ + long remainingTime; + + /* + * True iff the timer is in regular mode. + */ + boolean regular; + + /* + * True iff the timer has been stopped. + */ + boolean stopped; + + /* ************************************************************** + * Timer queue-related variables + * ************************************************************** */ + + /* + * A link to another timer object. This is used while the + * timer object is enqueued in the timer queue. + */ + Timer next; + + /* ************************************************************** + * Timer methods + * ************************************************************** */ + + /* + * This variable holds a handle to the TimerThread class for + * the purpose of getting at the class monitor. The reason + * why Class.forName("TimerThread") is not used is because it + * doesn't appear to work when loaded via a net class loader. + */ + static TimerThread timerThread = null; + + /** + * Creates a timer object that is owned by 'owner' and + * with the interval 'interval' milliseconds. The new timer + * object is stopped and is regular. getRemainingTime() + * return 'interval' at this point. getStopTime() returns + * the time this object was created. + * @param owner owner of the timer object + * @param interval interval of the timer in milliseconds + */ + public Timer(Timeable owner, long interval) { + this.owner = owner; + this.interval = interval; + remainingTime = interval; + regular = true; + sleepUntil = System.currentTimeMillis(); + stopped = true; + synchronized (getClass()) { + if (timerThread == null) { + timerThread = new TimerThread(); + } + } + } + + /** + * Returns true if this timer is stopped. + */ + public synchronized boolean isStopped() { + return stopped; + } + + /** + * Stops the timer. The amount of time the timer has already + * delayed is saved so if the timer is continued, it will only + * delay for the amount of time remaining. + * Note that even after stopping a timer, one more tick may + * still occur. + * This method is MT-safe; i.e. it is synchronized but for + * implementation reasons, the synchronized modifier cannot + * be included in the method declaration. + */ + public void stop() { + long now = System.currentTimeMillis(); + + synchronized (timerThread) { + synchronized (this) { + if (!stopped) { + TimerThread.dequeue(this); + remainingTime = Math.max(0, sleepUntil - now); + sleepUntil = now; // stop time + stopped = true; + } + } + } + } + + /** + * Continue the timer. The next tick will come at getRemainingTime() + * milliseconds later. If the timer is not stopped, this + * call will be a no-op. + * This method is MT-safe; i.e. it is synchronized but for + * implementation reasons, the synchronized modifier cannot + * be included in the method declaration. + */ + public void cont() { + synchronized (timerThread) { + synchronized (this) { + if (stopped) { + // The TimerTickThread avoids requeuing the + // timer only if the sleepUntil value has changed. + // The following guarantees that the sleepUntil + // value will be different; without this guarantee, + // it's theoretically possible for the timer to be + // inserted twice. + sleepUntil = Math.max(sleepUntil + 1, + System.currentTimeMillis() + remainingTime); + TimerThread.enqueue(this); + stopped = false; + } + } + } + } + + /** + * Resets the timer's remaining time to the timer's interval. + * If the timer's running state is not altered. + */ + public void reset() { + synchronized (timerThread) { + synchronized (this) { + setRemainingTime(interval); + } + } + } + + /** + * Returns the time at which the timer was last stopped. The + * return value is valid only if the timer is stopped. + */ + public synchronized long getStopTime() { + return sleepUntil; + } + + /** + * Returns the timer's interval. + */ + public synchronized long getInterval() { + return interval; + } + + /** + * Changes the timer's interval. The new interval setting + * does not take effect until after the next tick. + * This method does not alter the remaining time or the + * running state of the timer. + * @param interval new interval of the timer in milliseconds + */ + public synchronized void setInterval(long interval) { + this.interval = interval; + } + + /** + * Returns the remaining time before the timer's next tick. + * The return value is valid only if timer is stopped. + */ + public synchronized long getRemainingTime() { + return remainingTime; + } + + /** + * Sets the remaining time before the timer's next tick. + * This method does not alter the timer's running state. + * This method is MT-safe; i.e. it is synchronized but for + * implementation reasons, the synchronized modifier cannot + * be included in the method declaration. + * @param time new remaining time in milliseconds. + */ + public void setRemainingTime(long time) { + synchronized (timerThread) { + synchronized (this) { + if (stopped) { + remainingTime = time; + } else { + stop(); + remainingTime = time; + cont(); + } + } + } + } + + /** + * In regular mode, a timer ticks at the specified interval, + * regardless of how long the owner's tick() method takes. + * While the timer is running, no ticks are ever discarded. + * That means that if the owner's tick() method takes longer + * than the interval, the ticks that would have occurred are + * delivered immediately. + * + * In irregular mode, a timer starts delaying for exactly + * the specified interval only after the tick() method returns. + */ + public synchronized void setRegular(boolean regular) { + this.regular = regular; + } + + /* + * This method is used only for testing purposes. + */ + protected Thread getTimerThread() { + return TimerThread.timerThread; + } +} + + +/* + +This class implements the timer queue and is exclusively used by the +Timer class. There are only two methods exported to the Timer class - +enqueue, for inserting a timer into queue and dequeue, for removing +a timer from the queue. + +A timer in the timer queue is awaiting a tick. When a timer is to be +ticked, it is removed from the timer queue before the owner's tick() +method is called. + +A single timer thread manages the timer queue. This timer thread +looks at the head of the timer queue and delays until it's time for +the timer to tick. When the time comes, the timer thread creates a +callback thread to call the timer owner's tick() method. The timer +thread then processes the next timer in the queue. + +When a timer is inserted at the head of the queue, the timer thread is +notified. This causes the timer thread to prematurely wake up and +process the new head of the queue. + +*/ + +class TimerThread extends Thread { + /* + * Set to true to get debugging output. + */ + public static boolean debug = false; + + /* + * This is a handle to the thread managing the thread queue. + */ + static TimerThread timerThread; + + /* + * This flag is set if the timer thread has been notified + * while it was in the timed wait. This flag allows the + * timer thread to tell whether or not the wait completed. + */ + static boolean notified = false; + + protected TimerThread() { + super("TimerThread"); + timerThread = this; + start(); + } + + public synchronized void run() { + while (true) { + long delay; + + while (timerQueue == null) { + try { + wait(); + } catch (InterruptedException ex) { + // Just drop through and check timerQueue. + } + } + notified = false; + delay = timerQueue.sleepUntil - System.currentTimeMillis(); + if (delay > 0) { + try { + wait(delay); + } catch (InterruptedException ex) { + // Just drop through. + } + } + // remove from timer queue. + if (!notified) { + Timer timer = timerQueue; + timerQueue = timerQueue.next; + TimerTickThread thr = TimerTickThread.call( + timer, timer.sleepUntil); + if (debug) { + long delta = (System.currentTimeMillis() - timer.sleepUntil); + System.out.println("tick(" + thr.getName() + "," + + timer.interval + ","+delta+ ")"); + if (delta > 250) { + System.out.println("*** BIG DELAY ***"); + } + } + } + } + } + + /* ******************************************************* + Timer Queue + ******************************************************* */ + + /* + * The timer queue is a queue of timers waiting to tick. + */ + static Timer timerQueue = null; + + /* + * Uses timer.sleepUntil to determine where in the queue + * to insert the timer object. + * A new ticker thread is created only if the timer + * is inserted at the beginning of the queue. + * The timer must not already be in the queue. + * Assumes the caller has the TimerThread monitor. + */ + static protected void enqueue(Timer timer) { + Timer prev = null; + Timer cur = timerQueue; + + if (cur == null || timer.sleepUntil <= cur.sleepUntil) { + // insert at front of queue + timer.next = timerQueue; + timerQueue = timer; + notified = true; + timerThread.notify(); + } else { + do { + prev = cur; + cur = cur.next; + } while (cur != null && timer.sleepUntil > cur.sleepUntil); + // insert or append to the timer queue + timer.next = cur; + prev.next = timer; + } + if (debug) { + long now = System.currentTimeMillis(); + + System.out.print(Thread.currentThread().getName() + + ": enqueue " + timer.interval + ": "); + cur = timerQueue; + while(cur != null) { + long delta = cur.sleepUntil - now; + System.out.print(cur.interval + "(" + delta + ") "); + cur = cur.next; + } + System.out.println(); + } + } + + /* + * If the timer is not in the queue, returns false; + * otherwise removes the timer from the timer queue and returns true. + * Assumes the caller has the TimerThread monitor. + */ + static protected boolean dequeue(Timer timer) { + Timer prev = null; + Timer cur = timerQueue; + + while (cur != null && cur != timer) { + prev = cur; + cur = cur.next; + } + if (cur == null) { + if (debug) { + System.out.println(Thread.currentThread().getName() + + ": dequeue " + timer.interval + ": no-op"); + } + return false; + } if (prev == null) { + timerQueue = timer.next; + notified = true; + timerThread.notify(); + } else { + prev.next = timer.next; + } + timer.next = null; + if (debug) { + long now = System.currentTimeMillis(); + + System.out.print(Thread.currentThread().getName() + + ": dequeue " + timer.interval + ": "); + cur = timerQueue; + while(cur != null) { + long delta = cur.sleepUntil - now; + System.out.print(cur.interval + "(" + delta + ") "); + cur = cur.next; + } + System.out.println(); + } + return true; + } + + /* + * Inserts the timer back into the queue. This method + * is used by a callback thread after it has called the + * timer owner's tick() method. This method recomputes + * the sleepUntil field. + * Assumes the caller has the TimerThread and Timer monitor. + */ + protected static void requeue(Timer timer) { + if (!timer.stopped) { + long now = System.currentTimeMillis(); + if (timer.regular) { + timer.sleepUntil += timer.interval; + } else { + timer.sleepUntil = now + timer.interval; + } + enqueue(timer); + } else if (debug) { + System.out.println(Thread.currentThread().getName() + + ": requeue " + timer.interval + ": no-op"); + } + } +} + +/* + +This class implements a simple thread whose only purpose is to call a +timer owner's tick() method. A small fixed-sized pool of threads is +maintained and is protected by the class monitor. If the pool is +exhausted, a new thread is temporarily created and destroyed when +done. + +A thread that's in the pool waits on it's own monitor. When the +thread is retrieved from the pool, the retriever notifies the thread's +monitor. + +*/ + +class TimerTickThread extends Thread { + /* + * Maximum size of the thread pool. + */ + static final int MAX_POOL_SIZE = 3; + + /* + * Number of threads in the pool. + */ + static int curPoolSize = 0; + + /* + * The pool of timer threads. + */ + static TimerTickThread pool = null; + + /* + * Is used when linked into the thread pool. + */ + TimerTickThread next = null; + + /* + * This is the handle to the timer whose owner's + * tick() method will be called. + */ + Timer timer; + + /* + * The value of a timer's sleepUntil value is captured here. + * This is used to determine whether or not the timer should + * be reinserted into the queue. If the timer's sleepUntil + * value has changed, the timer is not reinserted. + */ + long lastSleepUntil; + + /* + * Creates a new callback thread to call the timer owner's + * tick() method. A thread is taken from the pool if one + * is available, otherwise, a new thread is created. + * The thread handle is returned. + */ + protected static synchronized TimerTickThread call( + Timer timer, long sleepUntil) { + TimerTickThread thread = pool; + + if (thread == null) { + // create one. + thread = new TimerTickThread(); + thread.timer = timer; + thread.lastSleepUntil = sleepUntil; + thread.start(); + } else { + pool = pool.next; + thread.timer = timer; + thread.lastSleepUntil = sleepUntil; + synchronized (thread) { + thread.notify(); + } + } + return thread; + } + + /* + * Returns false if the thread should simply exit; + * otherwise the thread is returned the pool, where + * it waits to be notified. (I did try to use the + * class monitor but the time between the notify + * and breaking out of the wait seemed to take + * significantly longer; need to look into this later.) + */ + private boolean returnToPool() { + synchronized (getClass()) { + if (curPoolSize >= MAX_POOL_SIZE) { + return false; + } + next = pool; + pool = this; + curPoolSize++; + timer = null; + } + while (timer == null) { + synchronized (this) { + try { + wait(); + } catch (InterruptedException ex) { + // Just drop through and retest timer. + } + } + } + synchronized (getClass()) { + curPoolSize--; + } + return true; + } + + public void run() { + do { + timer.owner.tick(timer); + synchronized (TimerThread.timerThread) { + synchronized (timer) { + if (lastSleepUntil == timer.sleepUntil) { + TimerThread.requeue(timer); + } + } + } + } while (returnToPool()); + } +} diff --git a/src/sun/misc/UCDecoder.java b/src/sun/misc/UCDecoder.java new file mode 100644 index 00000000..96ea093d --- /dev/null +++ b/src/sun/misc/UCDecoder.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1995, 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; + +/** + * This class implements a robust character decoder. The decoder will + * converted encoded text into binary data. + * + * The basic encoding unit is a 3 character atom. It encodes two bytes + * of data. Bytes are encoded into a 64 character set, the characters + * were chosen specifically because they appear in all codesets. + * We don't care what their numerical equivalent is because + * we use a character array to map them. This is like UUencoding + * with the dependency on ASCII removed. + * + * The three chars that make up an atom are encoded as follows: + *
    + *      00xxxyyy 00axxxxx 00byyyyy
    + *      00 = leading zeros, all values are 0 - 63
    + *      xxxyyy - Top 3 bits of X, Top 3 bits of Y
    + *      axxxxx - a = X parity bit, xxxxx lower 5 bits of X
    + *      byyyyy - b = Y parity bit, yyyyy lower 5 bits of Y
    + * 
    + * + * The atoms are arranged into lines suitable for inclusion into an + * email message or text file. The number of bytes that are encoded + * per line is 48 which keeps the total line length under 80 chars) + * + * Each line has the form( + *
    + *  *(LLSS)(DDDD)(DDDD)(DDDD)...(CRC)
    + *  Where each (xxx) represents a three character atom.
    + *  (LLSS) - 8 bit length (high byte), and sequence number
    + *           modulo 256;
    + *  (DDDD) - Data byte atoms, if length is odd, last data
    + *           atom has (DD00) (high byte data, low byte 0)
    + *  (CRC)  - 16 bit CRC for the line, includes length,
    + *           sequence, and all data bytes. If there is a
    + *           zero pad byte (odd length) it is _NOT_
    + *           included in the CRC.
    + * 
    + * + * If an error is encountered during decoding this class throws a + * CEFormatException. The specific detail messages are: + * + *
    + *    "UCDecoder: High byte parity error."
    + *    "UCDecoder: Low byte parity error."
    + *    "UCDecoder: Out of sequence line."
    + *    "UCDecoder: CRC check failed."
    + * 
    + * + * @author Chuck McManis + * @see CharacterEncoder + * @see UCEncoder + */ +public class UCDecoder extends CharacterDecoder { + + /** This class encodes two bytes per atom. */ + protected int bytesPerAtom() { + return (2); + } + + /** this class encodes 48 bytes per line */ + protected int bytesPerLine() { + return (48); + } + + /* this is the UCE mapping of 0-63 to characters .. */ + private final static byte map_array[] = { + // 0 1 2 3 4 5 6 7 + (byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7', // 0 + (byte)'8',(byte)'9',(byte)'A',(byte)'B',(byte)'C',(byte)'D',(byte)'E',(byte)'F', // 1 + (byte)'G',(byte)'H',(byte)'I',(byte)'J',(byte)'K',(byte)'L',(byte)'M',(byte)'N', // 2 + (byte)'O',(byte)'P',(byte)'Q',(byte)'R',(byte)'S',(byte)'T',(byte)'U',(byte)'V', // 3 + (byte)'W',(byte)'X',(byte)'Y',(byte)'Z',(byte)'a',(byte)'b',(byte)'c',(byte)'d', // 4 + (byte)'e',(byte)'f',(byte)'g',(byte)'h',(byte)'i',(byte)'j',(byte)'k',(byte)'l', // 5 + (byte)'m',(byte)'n',(byte)'o',(byte)'p',(byte)'q',(byte)'r',(byte)'s',(byte)'t', // 6 + (byte)'u',(byte)'v',(byte)'w',(byte)'x',(byte)'y',(byte)'z',(byte)'(',(byte)')' // 7 + }; + + private int sequence; + private byte tmp[] = new byte[2]; + private CRC16 crc = new CRC16(); + + /** + * Decode one atom - reads the characters from the input stream, decodes + * them, and checks for valid parity. + */ + protected void decodeAtom(PushbackInputStream inStream, OutputStream outStream, int l) throws IOException { + int i, p1, p2, np1, np2; + byte a = -1, b = -1, c = -1; + byte high_byte, low_byte; + byte tmp[] = new byte[3]; + + i = inStream.read(tmp); + if (i != 3) { + throw new CEStreamExhausted(); + } + for (i = 0; (i < 64) && ((a == -1) || (b == -1) || (c == -1)); i++) { + if (tmp[0] == map_array[i]) { + a = (byte) i; + } + if (tmp[1] == map_array[i]) { + b = (byte) i; + } + if (tmp[2] == map_array[i]) { + c = (byte) i; + } + } + high_byte = (byte) (((a & 0x38) << 2) + (b & 0x1f)); + low_byte = (byte) (((a & 0x7) << 5) + (c & 0x1f)); + p1 = 0; + p2 = 0; + for (i = 1; i < 256; i = i * 2) { + if ((high_byte & i) != 0) + p1++; + if ((low_byte & i) != 0) + p2++; + } + np1 = (b & 32) / 32; + np2 = (c & 32) / 32; + if ((p1 & 1) != np1) { + throw new CEFormatException("UCDecoder: High byte parity error."); + } + if ((p2 & 1) != np2) { + throw new CEFormatException("UCDecoder: Low byte parity error."); + } + outStream.write(high_byte); + crc.update(high_byte); + if (l == 2) { + outStream.write(low_byte); + crc.update(low_byte); + } + } + + private ByteArrayOutputStream lineAndSeq = new ByteArrayOutputStream(2); + + /** + * decodeBufferPrefix initializes the sequence number to zero. + */ + protected void decodeBufferPrefix(PushbackInputStream inStream, OutputStream outStream) { + sequence = 0; + } + + /** + * decodeLinePrefix reads the sequence number and the number of + * encoded bytes from the line. If the sequence number is not the + * previous sequence number + 1 then an exception is thrown. + * UCE lines are line terminator immune, they all start with * + * so the other thing this method does is scan for the next line + * by looking for the * character. + * + * @exception CEFormatException out of sequence lines detected. + */ + protected int decodeLinePrefix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int i; + int nLen, nSeq; + byte xtmp[]; + int c; + + crc.value = 0; + while (true) { + c = inStream.read(tmp, 0, 1); + if (c == -1) { + throw new CEStreamExhausted(); + } + if (tmp[0] == '*') { + break; + } + } + lineAndSeq.reset(); + decodeAtom(inStream, lineAndSeq, 2); + xtmp = lineAndSeq.toByteArray(); + nLen = xtmp[0] & 0xff; + nSeq = xtmp[1] & 0xff; + if (nSeq != sequence) { + throw new CEFormatException("UCDecoder: Out of sequence line."); + } + sequence = (sequence + 1) & 0xff; + return (nLen); + } + + + /** + * this method reads the CRC that is at the end of every line and + * verifies that it matches the computed CRC. + * + * @exception CEFormatException if CRC check fails. + */ + protected void decodeLineSuffix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int i; + int lineCRC = crc.value; + int readCRC; + byte tmp[]; + + lineAndSeq.reset(); + decodeAtom(inStream, lineAndSeq, 2); + tmp = lineAndSeq.toByteArray(); + readCRC = ((tmp[0] << 8) & 0xFF00) + (tmp[1] & 0xff); + if (readCRC != lineCRC) { + throw new CEFormatException("UCDecoder: CRC check failed."); + } + } +} diff --git a/src/sun/misc/UCEncoder.java b/src/sun/misc/UCEncoder.java new file mode 100644 index 00000000..7ee650ec --- /dev/null +++ b/src/sun/misc/UCEncoder.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1995, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * This class implements a robust character encoder. The encoder is designed + * to convert binary data into printable characters. The characters are + * assumed to exist but they are not assumed to be ASCII, the complete set + * is 0-9, A-Z, a-z, "(", and ")". + * + * The basic encoding unit is a 3 character atom. It encodes two bytes + * of data. Bytes are encoded into a 64 character set, the characters + * were chosen specifically because they appear in all codesets. + * We don't care what their numerical equivalent is because + * we use a character array to map them. This is like UUencoding + * with the dependency on ASCII removed. + * + * The three chars that make up an atom are encoded as follows: + *
    + *      00xxxyyy 00axxxxx 00byyyyy
    + *      00 = leading zeros, all values are 0 - 63
    + *      xxxyyy - Top 3 bits of X, Top 3 bits of Y
    + *      axxxxx - a = X parity bit, xxxxx lower 5 bits of X
    + *      byyyyy - b = Y parity bit, yyyyy lower 5 bits of Y
    + * 
    + * + * The atoms are arranged into lines suitable for inclusion into an + * email message or text file. The number of bytes that are encoded + * per line is 48 which keeps the total line length under 80 chars) + * + * Each line has the form( + *
    + *  *(LLSS)(DDDD)(DDDD)(DDDD)...(CRC)
    + *  Where each (xxx) represents a three character atom.
    + *  (LLSS) - 8 bit length (high byte), and sequence number
    + *           modulo 256;
    + *  (DDDD) - Data byte atoms, if length is odd, last data
    + *           atom has (DD00) (high byte data, low byte 0)
    + *  (CRC)  - 16 bit CRC for the line, includes length,
    + *           sequence, and all data bytes. If there is a
    + *           zero pad byte (odd length) it is _NOT_
    + *           included in the CRC.
    + * 
    + * + * @author Chuck McManis + * @see CharacterEncoder + * @see UCDecoder + */ +public class UCEncoder extends CharacterEncoder { + + /** this clase encodes two bytes per atom */ + protected int bytesPerAtom() { + return (2); + } + + /** this class encodes 48 bytes per line */ + protected int bytesPerLine() { + return (48); + } + + /* this is the UCE mapping of 0-63 to characters .. */ + private final static byte map_array[] = { + // 0 1 2 3 4 5 6 7 + (byte)'0',(byte)'1',(byte)'2',(byte)'3',(byte)'4',(byte)'5',(byte)'6',(byte)'7', // 0 + (byte)'8',(byte)'9',(byte)'A',(byte)'B',(byte)'C',(byte)'D',(byte)'E',(byte)'F', // 1 + (byte)'G',(byte)'H',(byte)'I',(byte)'J',(byte)'K',(byte)'L',(byte)'M',(byte)'N', // 2 + (byte)'O',(byte)'P',(byte)'Q',(byte)'R',(byte)'S',(byte)'T',(byte)'U',(byte)'V', // 3 + (byte)'W',(byte)'X',(byte)'Y',(byte)'Z',(byte)'a',(byte)'b',(byte)'c',(byte)'d', // 4 + (byte)'e',(byte)'f',(byte)'g',(byte)'h',(byte)'i',(byte)'j',(byte)'k',(byte)'l', // 5 + (byte)'m',(byte)'n',(byte)'o',(byte)'p',(byte)'q',(byte)'r',(byte)'s',(byte)'t', // 6 + (byte)'u',(byte)'v',(byte)'w',(byte)'x',(byte)'y',(byte)'z',(byte)'(',(byte)')' // 7 + }; + + private int sequence; + private byte tmp[] = new byte[2]; + private CRC16 crc = new CRC16(); + + /** + * encodeAtom - take two bytes and encode them into the correct + * three characters. If only one byte is to be encoded, the other + * must be zero. The padding byte is not included in the CRC computation. + */ + protected void encodeAtom(OutputStream outStream, byte data[], int offset, int len) throws IOException + { + int i; + int p1, p2; // parity bits + byte a, b; + + a = data[offset]; + if (len == 2) { + b = data[offset+1]; + } else { + b = 0; + } + crc.update(a); + if (len == 2) { + crc.update(b); + } + outStream.write(map_array[((a >>> 2) & 0x38) + ((b >>> 5) & 0x7)]); + p1 = 0; p2 = 0; + for (i = 1; i < 256; i = i * 2) { + if ((a & i) != 0) { + p1++; + } + if ((b & i) != 0) { + p2++; + } + } + p1 = (p1 & 1) * 32; + p2 = (p2 & 1) * 32; + outStream.write(map_array[(a & 31) + p1]); + outStream.write(map_array[(b & 31) + p2]); + return; + } + + /** + * Each UCE encoded line starts with a prefix of '*[XXX]', where + * the sequence number and the length are encoded in the first + * atom. + */ + protected void encodeLinePrefix(OutputStream outStream, int length) throws IOException { + outStream.write('*'); + crc.value = 0; + tmp[0] = (byte) length; + tmp[1] = (byte) sequence; + sequence = (sequence + 1) & 0xff; + encodeAtom(outStream, tmp, 0, 2); + } + + + /** + * each UCE encoded line ends with YYY and encoded version of the + * 16 bit checksum. The most significant byte of the check sum + * is always encoded FIRST. + */ + protected void encodeLineSuffix(OutputStream outStream) throws IOException { + tmp[0] = (byte) ((crc.value >>> 8) & 0xff); + tmp[1] = (byte) (crc.value & 0xff); + encodeAtom(outStream, tmp, 0, 2); + super.pStream.println(); + } + + /** + * The buffer prefix code is used to initialize the sequence number + * to zero. + */ + protected void encodeBufferPrefix(OutputStream a) throws IOException { + sequence = 0; + super.encodeBufferPrefix(a); + } +} diff --git a/src/sun/misc/URLClassPath.java b/src/sun/misc/URLClassPath.java new file mode 100644 index 00000000..4a44613a --- /dev/null +++ b/src/sun/misc/URLClassPath.java @@ -0,0 +1,1275 @@ +/* + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilePermission; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.CodeSigner; +import java.security.Permission; +import java.security.PrivilegedExceptionAction; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import sun.misc.FileURLMapper; +import sun.net.util.URLUtil; +import sun.net.www.ParseUtil; +import sun.security.action.GetPropertyAction; + +/** + * This class is used to maintain a search path of URLs for loading classes + * and resources from both JAR files and directories. + * + * @author David Connelly + */ +public class URLClassPath { + final static String USER_AGENT_JAVA_VERSION = "UA-Java-Version"; + final static String JAVA_VERSION; + private static final boolean DEBUG; + private static final boolean DEBUG_LOOKUP_CACHE; + private static final boolean DISABLE_JAR_CHECKING; + + static { + JAVA_VERSION = AccessController.doPrivileged( + new GetPropertyAction("java.version")); + DEBUG = (AccessController.doPrivileged( + new GetPropertyAction("sun.misc.URLClassPath.debug")) != null); + DEBUG_LOOKUP_CACHE = (AccessController.doPrivileged( + new GetPropertyAction("sun.misc.URLClassPath.debugLookupCache")) != null); + String p = AccessController.doPrivileged( + new GetPropertyAction("sun.misc.URLClassPath.disableJarChecking")); + DISABLE_JAR_CHECKING = p != null ? p.equals("true") || p.equals("") : false; + } + + /* The original search path of URLs. */ + private ArrayList path = new ArrayList(); + + /* The stack of unopened URLs */ + Stack urls = new Stack(); + + /* The resulting search path of Loaders */ + ArrayList loaders = new ArrayList(); + + /* Map of each URL opened to its corresponding Loader */ + HashMap lmap = new HashMap(); + + /* The jar protocol handler to use when creating new URLs */ + private URLStreamHandler jarHandler; + + /* Whether this URLClassLoader has been closed yet */ + private boolean closed = false; + + /** + * Creates a new URLClassPath for the given URLs. The URLs will be + * searched in the order specified for classes and resources. A URL + * ending with a '/' is assumed to refer to a directory. Otherwise, + * the URL is assumed to refer to a JAR file. + * + * @param urls the directory and JAR file URLs to search for classes + * and resources + * @param factory the URLStreamHandlerFactory to use when creating new URLs + */ + public URLClassPath(URL[] urls, URLStreamHandlerFactory factory) { + for (int i = 0; i < urls.length; i++) { + path.add(urls[i]); + } + push(urls); + if (factory != null) { + jarHandler = factory.createURLStreamHandler("jar"); + } + } + + public URLClassPath(URL[] urls) { + this(urls, null); + } + + public synchronized List closeLoaders() { + if (closed) { + return Collections.emptyList(); + } + List result = new LinkedList(); + for (Loader loader : loaders) { + try { + loader.close(); + } catch (IOException e) { + result.add (e); + } + } + closed = true; + return result; + } + + /** + * Appends the specified URL to the search path of directory and JAR + * file URLs from which to load classes and resources. + *

    + * If the URL specified is null or is already in the list of + * URLs, then invoking this method has no effect. + */ + public synchronized void addURL(URL url) { + if (closed) + return; + synchronized (urls) { + if (url == null || path.contains(url)) + return; + + urls.add(0, url); + path.add(url); + + if (lookupCacheURLs != null) { + // The lookup cache is no longer valid, since getLookupCache() + // does not consider the newly added url. + disableAllLookupCaches(); + } + } + } + + /** + * Returns the original search path of URLs. + */ + public URL[] getURLs() { + synchronized (urls) { + return path.toArray(new URL[path.size()]); + } + } + + /** + * Finds the resource with the specified name on the URL search path + * or null if not found or security check fails. + * + * @param name the name of the resource + * @param check whether to perform a security check + * @return a URL for the resource, or null + * if the resource could not be found. + */ + public URL findResource(String name, boolean check) { + Loader loader; + int[] cache = getLookupCache(name); + for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) { + URL url = loader.findResource(name, check); + if (url != null) { + return url; + } + } + return null; + } + + /** + * Finds the first Resource on the URL search path which has the specified + * name. Returns null if no Resource could be found. + * + * @param name the name of the Resource + * @param check whether to perform a security check + * @return the Resource, or null if not found + */ + public Resource getResource(String name, boolean check) { + if (DEBUG) { + System.err.println("URLClassPath.getResource(\"" + name + "\")"); + } + + Loader loader; + int[] cache = getLookupCache(name); + for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) { + Resource res = loader.getResource(name, check); + if (res != null) { + return res; + } + } + return null; + } + + /** + * Finds all resources on the URL search path with the given name. + * Returns an enumeration of the URL objects. + * + * @param name the resource name + * @return an Enumeration of all the urls having the specified name + */ + public Enumeration findResources(final String name, + final boolean check) { + return new Enumeration() { + private int index = 0; + private int[] cache = getLookupCache(name); + private URL url = null; + + private boolean next() { + if (url != null) { + return true; + } else { + Loader loader; + while ((loader = getNextLoader(cache, index++)) != null) { + url = loader.findResource(name, check); + if (url != null) { + return true; + } + } + return false; + } + } + + public boolean hasMoreElements() { + return next(); + } + + public URL nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + URL u = url; + url = null; + return u; + } + }; + } + + public Resource getResource(String name) { + return getResource(name, true); + } + + /** + * Finds all resources on the URL search path with the given name. + * Returns an enumeration of the Resource objects. + * + * @param name the resource name + * @return an Enumeration of all the resources having the specified name + */ + public Enumeration getResources(final String name, + final boolean check) { + return new Enumeration() { + private int index = 0; + private int[] cache = getLookupCache(name); + private Resource res = null; + + private boolean next() { + if (res != null) { + return true; + } else { + Loader loader; + while ((loader = getNextLoader(cache, index++)) != null) { + res = loader.getResource(name, check); + if (res != null) { + return true; + } + } + return false; + } + } + + public boolean hasMoreElements() { + return next(); + } + + public Resource nextElement() { + if (!next()) { + throw new NoSuchElementException(); + } + Resource r = res; + res = null; + return r; + } + }; + } + + public Enumeration getResources(final String name) { + return getResources(name, true); + } + + private static volatile boolean lookupCacheEnabled + = "true".equals(VM.getSavedProperty("sun.cds.enableSharedLookupCache")); + private URL[] lookupCacheURLs; + private ClassLoader lookupCacheLoader; + + synchronized void initLookupCache(ClassLoader loader) { + if ((lookupCacheURLs = getLookupCacheURLs(loader)) != null) { + lookupCacheLoader = loader; + } else { + // This JVM instance does not support lookup cache. + disableAllLookupCaches(); + } + } + + static void disableAllLookupCaches() { + lookupCacheEnabled = false; + } + + private static native URL[] getLookupCacheURLs(ClassLoader loader); + private static native int[] getLookupCacheForClassLoader(ClassLoader loader, + String name); + private static native boolean knownToNotExist0(ClassLoader loader, + String className); + + synchronized boolean knownToNotExist(String className) { + if (lookupCacheURLs != null && lookupCacheEnabled) { + return knownToNotExist0(lookupCacheLoader, className); + } + + // Don't know if this class exists or not -- need to do a full search. + return false; + } + + /** + * Returns an array of the index to lookupCacheURLs that may + * contain the specified resource. The values in the returned + * array are in strictly ascending order and must be a valid index + * to lookupCacheURLs array. + * + * This method returns an empty array if the specified resource + * cannot be found in this URLClassPath. If there is no lookup + * cache or it's disabled, this method returns null and the lookup + * should search the entire classpath. + * + * Example: if lookupCacheURLs contains {a.jar, b.jar, c.jar, d.jar} + * and package "foo" only exists in a.jar and c.jar, + * getLookupCache("foo/Bar.class") will return {0, 2} + * + * @param name the resource name + * @return an array of the index to lookupCacheURLs that may contain the + * specified resource; or null if no lookup cache is used. + */ + private synchronized int[] getLookupCache(String name) { + if (lookupCacheURLs == null || !lookupCacheEnabled) { + return null; + } + + int[] cache = getLookupCacheForClassLoader(lookupCacheLoader, name); + if (cache != null && cache.length > 0) { + int maxindex = cache[cache.length - 1]; // cache[] is strictly ascending. + if (!ensureLoaderOpened(maxindex)) { + if (DEBUG_LOOKUP_CACHE) { + System.out.println("Expanded loaders FAILED " + + loaders.size() + " for maxindex=" + maxindex); + } + return null; + } + } + + return cache; + } + + private boolean ensureLoaderOpened(int index) { + if (loaders.size() <= index) { + // Open all Loaders up to, and including, index + if (getLoader(index) == null) { + return false; + } + if (!lookupCacheEnabled) { + // cache was invalidated as the result of the above call. + return false; + } + if (DEBUG_LOOKUP_CACHE) { + System.out.println("Expanded loaders " + loaders.size() + + " to index=" + index); + } + } + return true; + } + + /* + * The CLASS-PATH attribute was expanded by the VM when building + * the resource lookup cache in the same order as the getLoader + * method does. This method validates if the URL from the lookup + * cache matches the URL of the Loader at the given index; + * otherwise, this method disables the lookup cache. + */ + private synchronized void validateLookupCache(int index, + String urlNoFragString) { + if (lookupCacheURLs != null && lookupCacheEnabled) { + if (index < lookupCacheURLs.length && + urlNoFragString.equals( + URLUtil.urlNoFragString(lookupCacheURLs[index]))) { + return; + } + if (DEBUG || DEBUG_LOOKUP_CACHE) { + System.out.println("WARNING: resource lookup cache invalidated " + + "for lookupCacheLoader at " + index); + } + disableAllLookupCaches(); + } + } + + /** + * Returns the next Loader that may contain the resource to + * lookup. If the given cache is null, return loaders.get(index) + * that may be lazily created; otherwise, cache[index] is the next + * Loader that may contain the resource to lookup and so returns + * loaders.get(cache[index]). + * + * If cache is non-null, loaders.get(cache[index]) must be present. + * + * @param cache lookup cache. If null, search the entire class path + * @param index index to the given cache array; or to the loaders list. + */ + private synchronized Loader getNextLoader(int[] cache, int index) { + if (closed) { + return null; + } + if (cache != null) { + if (index < cache.length) { + Loader loader = loaders.get(cache[index]); + if (DEBUG_LOOKUP_CACHE) { + System.out.println("HASCACHE: Loading from : " + cache[index] + + " = " + loader.getBaseURL()); + } + return loader; + } else { + return null; // finished iterating over cache[] + } + } else { + return getLoader(index); + } + } + + /* + * Returns the Loader at the specified position in the URL search + * path. The URLs are opened and expanded as needed. Returns null + * if the specified index is out of range. + */ + private synchronized Loader getLoader(int index) { + if (closed) { + return null; + } + // Expand URL search path until the request can be satisfied + // or the URL stack is empty. + while (loaders.size() < index + 1) { + // Pop the next URL from the URL stack + URL url; + synchronized (urls) { + if (urls.empty()) { + return null; + } else { + url = urls.pop(); + } + } + // Skip this URL if it already has a Loader. (Loader + // may be null in the case where URL has not been opened + // but is referenced by a JAR index.) + String urlNoFragString = URLUtil.urlNoFragString(url); + if (lmap.containsKey(urlNoFragString)) { + continue; + } + // Otherwise, create a new Loader for the URL. + Loader loader; + try { + loader = getLoader(url); + // If the loader defines a local class path then add the + // URLs to the list of URLs to be opened. + URL[] urls = loader.getClassPath(); + if (urls != null) { + push(urls); + } + } catch (IOException e) { + // Silently ignore for now... + continue; + } + // Finally, add the Loader to the search path. + validateLookupCache(loaders.size(), urlNoFragString); + loaders.add(loader); + lmap.put(urlNoFragString, loader); + } + if (DEBUG_LOOKUP_CACHE) { + System.out.println("NOCACHE: Loading from : " + index ); + } + return loaders.get(index); + } + + /* + * Returns the Loader for the specified base URL. + */ + private Loader getLoader(final URL url) throws IOException { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Loader run() throws IOException { + String file = url.getFile(); + if (file != null && file.endsWith("/")) { + if ("file".equals(url.getProtocol())) { + return new FileLoader(url); + } else { + return new Loader(url); + } + } else { + return new JarLoader(url, jarHandler, lmap); + } + } + }); + } catch (java.security.PrivilegedActionException pae) { + throw (IOException)pae.getException(); + } + } + + /* + * Pushes the specified URLs onto the list of unopened URLs. + */ + private void push(URL[] us) { + synchronized (urls) { + for (int i = us.length - 1; i >= 0; --i) { + urls.push(us[i]); + } + } + } + + /** + * Convert class path specification into an array of file URLs. + * + * The path of the file is encoded before conversion into URL + * form so that reserved characters can safely appear in the path. + */ + public static URL[] pathToURLs(String path) { + StringTokenizer st = new StringTokenizer(path, File.pathSeparator); + URL[] urls = new URL[st.countTokens()]; + int count = 0; + while (st.hasMoreTokens()) { + File f = new File(st.nextToken()); + try { + f = new File(f.getCanonicalPath()); + } catch (IOException x) { + // use the non-canonicalized filename + } + try { + urls[count++] = ParseUtil.fileToEncodedURL(f); + } catch (IOException x) { } + } + + if (urls.length != count) { + URL[] tmp = new URL[count]; + System.arraycopy(urls, 0, tmp, 0, count); + urls = tmp; + } + return urls; + } + + /* + * Check whether the resource URL should be returned. + * Return null on security check failure. + * Called by java.net.URLClassLoader. + */ + public URL checkURL(URL url) { + try { + check(url); + } catch (Exception e) { + return null; + } + + return url; + } + + /* + * Check whether the resource URL should be returned. + * Throw exception on failure. + * Called internally within this file. + */ + static void check(URL url) throws IOException { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + URLConnection urlConnection = url.openConnection(); + Permission perm = urlConnection.getPermission(); + if (perm != null) { + try { + security.checkPermission(perm); + } catch (SecurityException se) { + // fallback to checkRead/checkConnect for pre 1.2 + // security managers + if ((perm instanceof FilePermission) && + perm.getActions().indexOf("read") != -1) { + security.checkRead(perm.getName()); + } else if ((perm instanceof + java.net.SocketPermission) && + perm.getActions().indexOf("connect") != -1) { + URL locUrl = url; + if (urlConnection instanceof JarURLConnection) { + locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); + } + security.checkConnect(locUrl.getHost(), + locUrl.getPort()); + } else { + throw se; + } + } + } + } + } + + /** + * Inner class used to represent a loader of resources and classes + * from a base URL. + */ + private static class Loader implements Closeable { + private final URL base; + private JarFile jarfile; // if this points to a jar file + + /* + * Creates a new Loader for the specified URL. + */ + Loader(URL url) { + base = url; + } + + /* + * Returns the base URL for this Loader. + */ + URL getBaseURL() { + return base; + } + + URL findResource(final String name, boolean check) { + URL url; + try { + url = new URL(base, ParseUtil.encodePath(name, false)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("name"); + } + + try { + if (check) { + URLClassPath.check(url); + } + + /* + * For a HTTP connection we use the HEAD method to + * check if the resource exists. + */ + URLConnection uc = url.openConnection(); + if (uc instanceof HttpURLConnection) { + HttpURLConnection hconn = (HttpURLConnection)uc; + hconn.setRequestMethod("HEAD"); + if (hconn.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) { + return null; + } + } else { + // our best guess for the other cases + uc.setUseCaches(false); + InputStream is = uc.getInputStream(); + is.close(); + } + return url; + } catch (Exception e) { + return null; + } + } + + Resource getResource(final String name, boolean check) { + final URL url; + try { + url = new URL(base, ParseUtil.encodePath(name, false)); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("name"); + } + final URLConnection uc; + try { + if (check) { + URLClassPath.check(url); + } + uc = url.openConnection(); + InputStream in = uc.getInputStream(); + if (uc instanceof JarURLConnection) { + /* Need to remember the jar file so it can be closed + * in a hurry. + */ + JarURLConnection juc = (JarURLConnection)uc; + jarfile = JarLoader.checkJar(juc.getJarFile()); + } + } catch (Exception e) { + return null; + } + return new Resource() { + public String getName() { return name; } + public URL getURL() { return url; } + public URL getCodeSourceURL() { return base; } + public InputStream getInputStream() throws IOException { + return uc.getInputStream(); + } + public int getContentLength() throws IOException { + return uc.getContentLength(); + } + }; + } + + /* + * Returns the Resource for the specified name, or null if not + * found or the caller does not have the permission to get the + * resource. + */ + Resource getResource(final String name) { + return getResource(name, true); + } + + /* + * close this loader and release all resources + * method overridden in sub-classes + */ + public void close () throws IOException { + if (jarfile != null) { + jarfile.close(); + } + } + + /* + * Returns the local class path for this loader, or null if none. + */ + URL[] getClassPath() throws IOException { + return null; + } + } + + /* + * Inner class used to represent a Loader of resources from a JAR URL. + */ + static class JarLoader extends Loader { + private JarFile jar; + private URL csu; + private JarIndex index; + private MetaIndex metaIndex; + private URLStreamHandler handler; + private HashMap lmap; + private boolean closed = false; + private static final JavaUtilZipFileAccess zipAccess = + SharedSecrets.getJavaUtilZipFileAccess(); + + /* + * Creates a new JarLoader for the specified URL referring to + * a JAR file. + */ + JarLoader(URL url, URLStreamHandler jarHandler, + HashMap loaderMap) + throws IOException + { + super(new URL("jar", "", -1, url + "!/", jarHandler)); + csu = url; + handler = jarHandler; + lmap = loaderMap; + + if (!isOptimizable(url)) { + ensureOpen(); + } else { + String fileName = url.getFile(); + if (fileName != null) { + fileName = ParseUtil.decode(fileName); + File f = new File(fileName); + metaIndex = MetaIndex.forJar(f); + // If the meta index is found but the file is not + // installed, set metaIndex to null. A typical + // senario is charsets.jar which won't be installed + // when the user is running in certain locale environment. + // The side effect of null metaIndex will cause + // ensureOpen get called so that IOException is thrown. + if (metaIndex != null && !f.exists()) { + metaIndex = null; + } + } + + // metaIndex is null when either there is no such jar file + // entry recorded in meta-index file or such jar file is + // missing in JRE. See bug 6340399. + if (metaIndex == null) { + ensureOpen(); + } + } + } + + @Override + public void close () throws IOException { + // closing is synchronized at higher level + if (!closed) { + closed = true; + // in case not already open. + ensureOpen(); + jar.close(); + } + } + + JarFile getJarFile () { + return jar; + } + + private boolean isOptimizable(URL url) { + return "file".equals(url.getProtocol()); + } + + private void ensureOpen() throws IOException { + if (jar == null) { + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Void run() throws IOException { + if (DEBUG) { + System.err.println("Opening " + csu); + Thread.dumpStack(); + } + + jar = getJarFile(csu); + index = JarIndex.getJarIndex(jar, metaIndex); + if (index != null) { + String[] jarfiles = index.getJarFiles(); + // Add all the dependent URLs to the lmap so that loaders + // will not be created for them by URLClassPath.getLoader(int) + // if the same URL occurs later on the main class path. We set + // Loader to null here to avoid creating a Loader for each + // URL until we actually need to try to load something from them. + for(int i = 0; i < jarfiles.length; i++) { + try { + URL jarURL = new URL(csu, jarfiles[i]); + // If a non-null loader already exists, leave it alone. + String urlNoFragString = URLUtil.urlNoFragString(jarURL); + if (!lmap.containsKey(urlNoFragString)) { + lmap.put(urlNoFragString, null); + } + } catch (MalformedURLException e) { + continue; + } + } + } + return null; + } + } + ); + } catch (java.security.PrivilegedActionException pae) { + throw (IOException)pae.getException(); + } + } + } + + /* Throws if the given jar file is does not start with the correct LOC */ + static JarFile checkJar(JarFile jar) throws IOException { + if (System.getSecurityManager() != null && !DISABLE_JAR_CHECKING + && !zipAccess.startsWithLocHeader(jar)) { + IOException x = new IOException("Invalid Jar file"); + try { + jar.close(); + } catch (IOException ex) { + x.addSuppressed(ex); + } + throw x; + } + + return jar; + } + + private JarFile getJarFile(URL url) throws IOException { + // Optimize case where url refers to a local jar file + if (isOptimizable(url)) { + FileURLMapper p = new FileURLMapper (url); + if (!p.exists()) { + throw new FileNotFoundException(p.getPath()); + } + return checkJar(new JarFile(p.getPath())); + } + URLConnection uc = getBaseURL().openConnection(); + uc.setRequestProperty(USER_AGENT_JAVA_VERSION, JAVA_VERSION); + JarFile jarFile = ((JarURLConnection)uc).getJarFile(); + return checkJar(jarFile); + } + + /* + * Returns the index of this JarLoader if it exists. + */ + JarIndex getIndex() { + try { + ensureOpen(); + } catch (IOException e) { + throw new InternalError(e); + } + return index; + } + + /* + * Creates the resource and if the check flag is set to true, checks if + * is its okay to return the resource. + */ + Resource checkResource(final String name, boolean check, + final JarEntry entry) { + + final URL url; + try { + url = new URL(getBaseURL(), ParseUtil.encodePath(name, false)); + if (check) { + URLClassPath.check(url); + } + } catch (MalformedURLException e) { + return null; + // throw new IllegalArgumentException("name"); + } catch (IOException e) { + return null; + } catch (AccessControlException e) { + return null; + } + + return new Resource() { + public String getName() { return name; } + public URL getURL() { return url; } + public URL getCodeSourceURL() { return csu; } + public InputStream getInputStream() throws IOException + { return jar.getInputStream(entry); } + public int getContentLength() + { return (int)entry.getSize(); } + public Manifest getManifest() throws IOException + { return jar.getManifest(); }; + public Certificate[] getCertificates() + { return entry.getCertificates(); }; + public CodeSigner[] getCodeSigners() + { return entry.getCodeSigners(); }; + }; + } + + + /* + * Returns true iff atleast one resource in the jar file has the same + * package name as that of the specified resource name. + */ + boolean validIndex(final String name) { + String packageName = name; + int pos; + if((pos = name.lastIndexOf("/")) != -1) { + packageName = name.substring(0, pos); + } + + String entryName; + ZipEntry entry; + Enumeration enum_ = jar.entries(); + while (enum_.hasMoreElements()) { + entry = enum_.nextElement(); + entryName = entry.getName(); + if((pos = entryName.lastIndexOf("/")) != -1) + entryName = entryName.substring(0, pos); + if (entryName.equals(packageName)) { + return true; + } + } + return false; + } + + /* + * Returns the URL for a resource with the specified name + */ + URL findResource(final String name, boolean check) { + Resource rsc = getResource(name, check); + if (rsc != null) { + return rsc.getURL(); + } + return null; + } + + /* + * Returns the JAR Resource for the specified name. + */ + Resource getResource(final String name, boolean check) { + if (metaIndex != null) { + if (!metaIndex.mayContain(name)) { + return null; + } + } + + try { + ensureOpen(); + } catch (IOException e) { + throw new InternalError(e); + } + final JarEntry entry = jar.getJarEntry(name); + if (entry != null) + return checkResource(name, check, entry); + + if (index == null) + return null; + + HashSet visited = new HashSet(); + return getResource(name, check, visited); + } + + /* + * Version of getResource() that tracks the jar files that have been + * visited by linking through the index files. This helper method uses + * a HashSet to store the URLs of jar files that have been searched and + * uses it to avoid going into an infinite loop, looking for a + * non-existent resource + */ + Resource getResource(final String name, boolean check, + Set visited) { + + Resource res; + String[] jarFiles; + int count = 0; + LinkedList jarFilesList = null; + + /* If there no jar files in the index that can potential contain + * this resource then return immediately. + */ + if((jarFilesList = index.get(name)) == null) + return null; + + do { + int size = jarFilesList.size(); + jarFiles = jarFilesList.toArray(new String[size]); + /* loop through the mapped jar file list */ + while(count < size) { + String jarName = jarFiles[count++]; + JarLoader newLoader; + final URL url; + + try{ + url = new URL(csu, jarName); + String urlNoFragString = URLUtil.urlNoFragString(url); + if ((newLoader = (JarLoader)lmap.get(urlNoFragString)) == null) { + /* no loader has been set up for this jar file + * before + */ + newLoader = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public JarLoader run() throws IOException { + return new JarLoader(url, handler, + lmap); + } + }); + + /* this newly opened jar file has its own index, + * merge it into the parent's index, taking into + * account the relative path. + */ + JarIndex newIndex = newLoader.getIndex(); + if(newIndex != null) { + int pos = jarName.lastIndexOf("/"); + newIndex.merge(this.index, (pos == -1 ? + null : jarName.substring(0, pos + 1))); + } + + /* put it in the global hashtable */ + lmap.put(urlNoFragString, newLoader); + } + } catch (java.security.PrivilegedActionException pae) { + continue; + } catch (MalformedURLException e) { + continue; + } + + + /* Note that the addition of the url to the list of visited + * jars incorporates a check for presence in the hashmap + */ + boolean visitedURL = !visited.add(URLUtil.urlNoFragString(url)); + if (!visitedURL) { + try { + newLoader.ensureOpen(); + } catch (IOException e) { + throw new InternalError(e); + } + final JarEntry entry = newLoader.jar.getJarEntry(name); + if (entry != null) { + return newLoader.checkResource(name, check, entry); + } + + /* Verify that at least one other resource with the + * same package name as the lookedup resource is + * present in the new jar + */ + if (!newLoader.validIndex(name)) { + /* the mapping is wrong */ + throw new InvalidJarIndexException("Invalid index"); + } + } + + /* If newLoader is the current loader or if it is a + * loader that has already been searched or if the new + * loader does not have an index then skip it + * and move on to the next loader. + */ + if (visitedURL || newLoader == this || + newLoader.getIndex() == null) { + continue; + } + + /* Process the index of the new loader + */ + if((res = newLoader.getResource(name, check, visited)) + != null) { + return res; + } + } + // Get the list of jar files again as the list could have grown + // due to merging of index files. + jarFilesList = index.get(name); + + // If the count is unchanged, we are done. + } while(count < jarFilesList.size()); + return null; + } + + + /* + * Returns the JAR file local class path, or null if none. + */ + URL[] getClassPath() throws IOException { + if (index != null) { + return null; + } + + if (metaIndex != null) { + return null; + } + + ensureOpen(); + parseExtensionsDependencies(); + + if (SharedSecrets.javaUtilJarAccess().jarFileHasClassPathAttribute(jar)) { // Only get manifest when necessary + Manifest man = jar.getManifest(); + if (man != null) { + Attributes attr = man.getMainAttributes(); + if (attr != null) { + String value = attr.getValue(Name.CLASS_PATH); + if (value != null) { + return parseClassPath(csu, value); + } + } + } + } + return null; + } + + /* + * parse the standard extension dependencies + */ + private void parseExtensionsDependencies() throws IOException { + ExtensionDependency.checkExtensionsDependencies(jar); + } + + /* + * Parses value of the Class-Path manifest attribute and returns + * an array of URLs relative to the specified base URL. + */ + private URL[] parseClassPath(URL base, String value) + throws MalformedURLException + { + StringTokenizer st = new StringTokenizer(value); + URL[] urls = new URL[st.countTokens()]; + int i = 0; + while (st.hasMoreTokens()) { + String path = st.nextToken(); + urls[i] = new URL(base, path); + i++; + } + return urls; + } + } + + /* + * Inner class used to represent a loader of classes and resources + * from a file URL that refers to a directory. + */ + private static class FileLoader extends Loader { + /* Canonicalized File */ + private File dir; + + FileLoader(URL url) throws IOException { + super(url); + if (!"file".equals(url.getProtocol())) { + throw new IllegalArgumentException("url"); + } + String path = url.getFile().replace('/', File.separatorChar); + path = ParseUtil.decode(path); + dir = (new File(path)).getCanonicalFile(); + } + + /* + * Returns the URL for a resource with the specified name + */ + URL findResource(final String name, boolean check) { + Resource rsc = getResource(name, check); + if (rsc != null) { + return rsc.getURL(); + } + return null; + } + + Resource getResource(final String name, boolean check) { + final URL url; + try { + URL normalizedBase = new URL(getBaseURL(), "."); + url = new URL(getBaseURL(), ParseUtil.encodePath(name, false)); + + if (url.getFile().startsWith(normalizedBase.getFile()) == false) { + // requested resource had ../..'s in path + return null; + } + + if (check) + URLClassPath.check(url); + + final File file; + if (name.indexOf("..") != -1) { + file = (new File(dir, name.replace('/', File.separatorChar))) + .getCanonicalFile(); + if ( !((file.getPath()).startsWith(dir.getPath())) ) { + /* outside of base dir */ + return null; + } + } else { + file = new File(dir, name.replace('/', File.separatorChar)); + } + + if (file.exists()) { + return new Resource() { + public String getName() { return name; }; + public URL getURL() { return url; }; + public URL getCodeSourceURL() { return getBaseURL(); }; + public InputStream getInputStream() throws IOException + { return new FileInputStream(file); }; + public int getContentLength() throws IOException + { return (int)file.length(); }; + }; + } + } catch (Exception e) { + return null; + } + return null; + } + } +} diff --git a/src/sun/misc/UUDecoder.java b/src/sun/misc/UUDecoder.java new file mode 100644 index 00000000..c6e695ff --- /dev/null +++ b/src/sun/misc/UUDecoder.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1995, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; + +/** + * This class implements a Berkeley uu character decoder. This decoder + * was made famous by the uudecode program. + * + * The basic character coding is algorithmic, taking 6 bits of binary + * data and adding it to an ASCII ' ' (space) character. This converts + * these six bits into a printable representation. Note that it depends + * on the ASCII character encoding standard for english. Groups of three + * bytes are converted into 4 characters by treating the three bytes + * a four 6 bit groups, group 1 is byte 1's most significant six bits, + * group 2 is byte 1's least significant two bits plus byte 2's four + * most significant bits. etc. + * + * In this encoding, the buffer prefix is: + *

    + *     begin [mode] [filename]
    + * 
    + * + * This is followed by one or more lines of the form: + *
    + *      (len)(data)(data)(data) ...
    + * 
    + * where (len) is the number of bytes on this line. Note that groupings + * are always four characters, even if length is not a multiple of three + * bytes. When less than three characters are encoded, the values of the + * last remaining bytes is undefined and should be ignored. + * + * The last line of data in a uuencoded buffer is represented by a single + * space character. This is translated by the decoding engine to a line + * length of zero. This is immediately followed by a line which contains + * the word 'end[newline]' + * + * If an error is encountered during decoding this class throws a + * CEFormatException. The specific detail messages are: + * + *
    + *      "UUDecoder: No begin line."
    + *      "UUDecoder: Malformed begin line."
    + *      "UUDecoder: Short Buffer."
    + *      "UUDecoder: Bad Line Length."
    + *      "UUDecoder: Missing 'end' line."
    + * 
    + * + * @author Chuck McManis + * @see CharacterDecoder + * @see UUEncoder + */ +public class UUDecoder extends CharacterDecoder { + + /** + * This string contains the name that was in the buffer being decoded. + */ + public String bufferName; + + /** + * Represents UNIX(tm) mode bits. Generally three octal digits + * representing read, write, and execute permission of the owner, + * group owner, and others. They should be interpreted as the bit groups: + *
    +     * (owner) (group) (others)
    +     *  rwx      rwx     rwx    (r = read, w = write, x = execute)
    +     *
    + * + */ + public int mode; + + + /** + * UU encoding specifies 3 bytes per atom. + */ + protected int bytesPerAtom() { + return (3); + } + + /** + * All UU lines have 45 bytes on them, for line length of 15*4+1 or 61 + * characters per line. + */ + protected int bytesPerLine() { + return (45); + } + + /** This is used to decode the atoms */ + private byte decoderBuffer[] = new byte[4]; + + /** + * Decode a UU atom. Note that if l is less than 3 we don't write + * the extra bits, however the encoder always encodes 4 character + * groups even when they are not needed. + */ + protected void decodeAtom(PushbackInputStream inStream, OutputStream outStream, int l) + throws IOException { + int i, c1, c2, c3, c4; + int a, b, c; + StringBuffer x = new StringBuffer(); + + for (i = 0; i < 4; i++) { + c1 = inStream.read(); + if (c1 == -1) { + throw new CEStreamExhausted(); + } + x.append((char)c1); + decoderBuffer[i] = (byte) ((c1 - ' ') & 0x3f); + } + a = ((decoderBuffer[0] << 2) & 0xfc) | ((decoderBuffer[1] >>> 4) & 3); + b = ((decoderBuffer[1] << 4) & 0xf0) | ((decoderBuffer[2] >>> 2) & 0xf); + c = ((decoderBuffer[2] << 6) & 0xc0) | (decoderBuffer[3] & 0x3f); + outStream.write((byte)(a & 0xff)); + if (l > 1) { + outStream.write((byte)( b & 0xff)); + } + if (l > 2) { + outStream.write((byte)(c&0xff)); + } + } + + /** + * For uuencoded buffers, the data begins with a line of the form: + * begin MODE FILENAME + * This line always starts in column 1. + */ + protected void decodeBufferPrefix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int c; + StringBuffer q = new StringBuffer(32); + String r; + boolean sawNewLine; + + /* + * This works by ripping through the buffer until it finds a 'begin' + * line or the end of the buffer. + */ + sawNewLine = true; + while (true) { + c = inStream.read(); + if (c == -1) { + throw new CEFormatException("UUDecoder: No begin line."); + } + if ((c == 'b') && sawNewLine){ + c = inStream.read(); + if (c == 'e') { + break; + } + } + sawNewLine = (c == '\n') || (c == '\r'); + } + + /* + * Now we think its begin, (we've seen ^be) so verify it here. + */ + while ((c != '\n') && (c != '\r')) { + c = inStream.read(); + if (c == -1) { + throw new CEFormatException("UUDecoder: No begin line."); + } + if ((c != '\n') && (c != '\r')) { + q.append((char)c); + } + } + r = q.toString(); + if (r.indexOf(' ') != 3) { + throw new CEFormatException("UUDecoder: Malformed begin line."); + } + mode = Integer.parseInt(r.substring(4,7)); + bufferName = r.substring(r.indexOf(' ',6)+1); + /* + * Check for \n after \r + */ + if (c == '\r') { + c = inStream.read (); + if ((c != '\n') && (c != -1)) + inStream.unread (c); + } + } + + /** + * In uuencoded buffers, encoded lines start with a character that + * represents the number of bytes encoded in this line. The last + * line of input is always a line that starts with a single space + * character, which would be a zero length line. + */ + protected int decodeLinePrefix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int c; + + c = inStream.read(); + if (c == ' ') { + c = inStream.read(); /* discard the (first)trailing CR or LF */ + c = inStream.read(); /* check for a second one */ + if ((c != '\n') && (c != -1)) + inStream.unread (c); + throw new CEStreamExhausted(); + } else if (c == -1) { + throw new CEFormatException("UUDecoder: Short Buffer."); + } + + c = (c - ' ') & 0x3f; + if (c > bytesPerLine()) { + throw new CEFormatException("UUDecoder: Bad Line Length."); + } + return (c); + } + + + /** + * Find the end of the line for the next operation. + * The following sequences are recognized as end-of-line + * CR, CR LF, or LF + */ + protected void decodeLineSuffix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int c; + while (true) { + c = inStream.read(); + if (c == -1) { + throw new CEStreamExhausted(); + } + if (c == '\n') { + break; + } + if (c == '\r') { + c = inStream.read(); + if ((c != '\n') && (c != -1)) { + inStream.unread (c); + } + break; + } + } + } + + /** + * UUencoded files have a buffer suffix which consists of the word + * end. This line should immediately follow the line with a single + * space in it. + */ + protected void decodeBufferSuffix(PushbackInputStream inStream, OutputStream outStream) throws IOException { + int c; + + c = inStream.read(decoderBuffer); + if ((decoderBuffer[0] != 'e') || (decoderBuffer[1] != 'n') || + (decoderBuffer[2] != 'd')) { + throw new CEFormatException("UUDecoder: Missing 'end' line."); + } + } + +} diff --git a/src/sun/misc/UUEncoder.java b/src/sun/misc/UUEncoder.java new file mode 100644 index 00000000..923d34bb --- /dev/null +++ b/src/sun/misc/UUEncoder.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * This class implements a Berkeley uu character encoder. This encoder + * was made famous by uuencode program. + * + * The basic character coding is algorithmic, taking 6 bits of binary + * data and adding it to an ASCII ' ' (space) character. This converts + * these six bits into a printable representation. Note that it depends + * on the ASCII character encoding standard for english. Groups of three + * bytes are converted into 4 characters by treating the three bytes + * a four 6 bit groups, group 1 is byte 1's most significant six bits, + * group 2 is byte 1's least significant two bits plus byte 2's four + * most significant bits. etc. + * + * In this encoding, the buffer prefix is: + *
    + *     begin [mode] [filename]
    + * 
    + * + * This is followed by one or more lines of the form: + *
    + *      (len)(data)(data)(data) ...
    + * 
    + * where (len) is the number of bytes on this line. Note that groupings + * are always four characters, even if length is not a multiple of three + * bytes. When less than three characters are encoded, the values of the + * last remaining bytes is undefined and should be ignored. + * + * The last line of data in a uuencoded file is represented by a single + * space character. This is translated by the decoding engine to a line + * length of zero. This is immediately followed by a line which contains + * the word 'end[newline]' + * + * @author Chuck McManis + * @see CharacterEncoder + * @see UUDecoder + */ +public class UUEncoder extends CharacterEncoder { + + /** + * This name is stored in the begin line. + */ + private String bufferName; + + /** + * Represents UNIX(tm) mode bits. Generally three octal digits representing + * read, write, and execute permission of the owner, group owner, and + * others. They should be interpreted as the bit groups: + * (owner) (group) (others) + * rwx rwx rwx (r = read, w = write, x = execute) + * + * By default these are set to 644 (UNIX rw-r--r-- permissions). + */ + private int mode; + + + /** + * Default - buffer begin line will be: + *
    +     *  begin 644 encoder.buf
    +     * 
    + */ + public UUEncoder() { + bufferName = "encoder.buf"; + mode = 644; + } + + /** + * Specifies a name for the encoded buffer, begin line will be: + *
    +     *  begin 644 [FNAME]
    +     * 
    + */ + public UUEncoder(String fname) { + bufferName = fname; + mode = 644; + } + + /** + * Specifies a name and mode for the encoded buffer, begin line will be: + *
    +     *  begin [MODE] [FNAME]
    +     * 
    + */ + public UUEncoder(String fname, int newMode) { + bufferName = fname; + mode = newMode; + } + + /** number of bytes per atom in uuencoding is 3 */ + protected int bytesPerAtom() { + return (3); + } + + /** number of bytes per line in uuencoding is 45 */ + protected int bytesPerLine() { + return (45); + } + + /** + * encodeAtom - take three bytes and encodes them into 4 characters + * If len is less than 3 then remaining bytes are filled with '1'. + * This insures that the last line won't end in spaces and potentiallly + * be truncated. + */ + protected void encodeAtom(OutputStream outStream, byte data[], int offset, int len) + throws IOException { + byte a, b = 1, c = 1; + int c1, c2, c3, c4; + + a = data[offset]; + if (len > 1) { + b = data[offset+1]; + } + if (len > 2) { + c = data[offset+2]; + } + + c1 = (a >>> 2) & 0x3f; + c2 = ((a << 4) & 0x30) | ((b >>> 4) & 0xf); + c3 = ((b << 2) & 0x3c) | ((c >>> 6) & 0x3); + c4 = c & 0x3f; + outStream.write(c1 + ' '); + outStream.write(c2 + ' '); + outStream.write(c3 + ' '); + outStream.write(c4 + ' '); + return; + } + + /** + * Encode the line prefix which consists of the single character. The + * lenght is added to the value of ' ' (32 decimal) and printed. + */ + protected void encodeLinePrefix(OutputStream outStream, int length) + throws IOException { + outStream.write((length & 0x3f) + ' '); + } + + + /** + * The line suffix for uuencoded files is simply a new line. + */ + protected void encodeLineSuffix(OutputStream outStream) throws IOException { + pStream.println(); + } + + /** + * encodeBufferPrefix writes the begin line to the output stream. + */ + protected void encodeBufferPrefix(OutputStream a) throws IOException { + super.pStream = new PrintStream(a); + super.pStream.print("begin "+mode+" "); + if (bufferName != null) { + super.pStream.println(bufferName); + } else { + super.pStream.println("encoder.bin"); + } + super.pStream.flush(); + } + + /** + * encodeBufferSuffix writes the single line containing space (' ') and + * the line containing the word 'end' to the output stream. + */ + protected void encodeBufferSuffix(OutputStream a) throws IOException { + super.pStream.println(" \nend"); + super.pStream.flush(); + } + +} diff --git a/src/sun/misc/Unsafe.java b/src/sun/misc/Unsafe.java new file mode 100644 index 00000000..8f058968 --- /dev/null +++ b/src/sun/misc/Unsafe.java @@ -0,0 +1,1140 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; + +import sun.reflect.CallerSensitive; +import sun.reflect.Reflection; + + +/** + * A collection of methods for performing low-level, unsafe operations. + * Although the class and all methods are public, use of this class is + * limited because only trusted code can obtain instances of it. + * + * @author John R. Rose + * @see #getUnsafe + */ + +public final class Unsafe { + + private static native void registerNatives(); + static { + registerNatives(); + sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); + } + + private Unsafe() {} + + private static final Unsafe theUnsafe = new Unsafe(); + + /** + * Provides the caller with the capability of performing unsafe + * operations. + * + *

    The returned Unsafe object should be carefully guarded + * by the caller, since it can be used to read and write data at arbitrary + * memory addresses. It must never be passed to untrusted code. + * + *

    Most methods in this class are very low-level, and correspond to a + * small number of hardware instructions (on typical machines). Compilers + * are encouraged to optimize these methods accordingly. + * + *

    Here is a suggested idiom for using unsafe operations: + * + *

    +     * class MyTrustedClass {
    +     *   private static final Unsafe unsafe = Unsafe.getUnsafe();
    +     *   ...
    +     *   private long myCountAddress = ...;
    +     *   public int getCount() { return unsafe.getByte(myCountAddress); }
    +     * }
    +     * 
    + * + * (It may assist compilers to make the local variable be + * final.) + * + * @exception SecurityException if a security manager exists and its + * checkPropertiesAccess method doesn't allow + * access to the system properties. + */ + @CallerSensitive + public static Unsafe getUnsafe() { + Class caller = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(caller.getClassLoader())) + throw new SecurityException("Unsafe"); + return theUnsafe; + } + + /// peek and poke operations + /// (compilers should optimize these to memory ops) + + // These work on object fields in the Java heap. + // They will not work on elements of packed arrays. + + /** + * Fetches a value from a given Java variable. + * More specifically, fetches a field or array element within the given + * object o at the given offset, or (if o is + * null) from the memory address whose numerical value is the given + * offset. + *

    + * The results are undefined unless one of the following cases is true: + *

      + *
    • The offset was obtained from {@link #objectFieldOffset} on + * the {@link Field} of some Java field and the object + * referred to by o is of a class compatible with that + * field's class. + * + *
    • The offset and object reference o (either null or + * non-null) were both obtained via {@link #staticFieldOffset} + * and {@link #staticFieldBase} (respectively) from the + * reflective {@link Field} representation of some Java field. + * + *
    • The object referred to by o is an array, and the offset + * is an integer of the form B+N*S, where N is + * a valid index into the array, and B and S are + * the values obtained by {@link #arrayBaseOffset} and {@link + * #arrayIndexScale} (respectively) from the array's class. The value + * referred to is the Nth element of the array. + * + *
    + *

    + * If one of the above cases is true, the call references a specific Java + * variable (field or array element). However, the results are undefined + * if that variable is not in fact of the type returned by this method. + *

    + * This method refers to a variable by means of two parameters, and so + * it provides (in effect) a double-register addressing mode + * for Java variables. When the object reference is null, this method + * uses its offset as an absolute address. This is similar in operation + * to methods such as {@link #getInt(long)}, which provide (in effect) a + * single-register addressing mode for non-Java variables. + * However, because Java variables may have a different layout in memory + * from non-Java variables, programmers should not assume that these + * two addressing modes are ever equivalent. Also, programmers should + * remember that offsets from the double-register addressing mode cannot + * be portably confused with longs used in the single-register addressing + * mode. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @return the value fetched from the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + */ + public native int getInt(Object o, long offset); + + /** + * Stores a value into a given Java variable. + *

    + * The first two parameters are interpreted exactly as with + * {@link #getInt(Object, long)} to refer to a specific + * Java variable (field or array element). The given value + * is stored into that variable. + *

    + * The variable must be of the same type as the method + * parameter x. + * + * @param o Java heap object in which the variable resides, if any, else + * null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * @param x the value to store into the indicated Java variable + * @throws RuntimeException No defined exceptions are thrown, not even + * {@link NullPointerException} + */ + public native void putInt(Object o, long offset, int x); + + /** + * Fetches a reference value from a given Java variable. + * @see #getInt(Object, long) + */ + public native Object getObject(Object o, long offset); + + /** + * Stores a reference value into a given Java variable. + *

    + * Unless the reference x being stored is either null + * or matches the field type, the results are undefined. + * If the reference o is non-null, car marks or + * other store barriers for that object (if the VM requires them) + * are updated. + * @see #putInt(Object, int, int) + */ + public native void putObject(Object o, long offset, Object x); + + /** @see #getInt(Object, long) */ + public native boolean getBoolean(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putBoolean(Object o, long offset, boolean x); + /** @see #getInt(Object, long) */ + public native byte getByte(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putByte(Object o, long offset, byte x); + /** @see #getInt(Object, long) */ + public native short getShort(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putShort(Object o, long offset, short x); + /** @see #getInt(Object, long) */ + public native char getChar(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putChar(Object o, long offset, char x); + /** @see #getInt(Object, long) */ + public native long getLong(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putLong(Object o, long offset, long x); + /** @see #getInt(Object, long) */ + public native float getFloat(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putFloat(Object o, long offset, float x); + /** @see #getInt(Object, long) */ + public native double getDouble(Object o, long offset); + /** @see #putInt(Object, int, int) */ + public native void putDouble(Object o, long offset, double x); + + /** + * This method, like all others with 32-bit offsets, was native + * in a previous release but is now a wrapper which simply casts + * the offset to a long value. It provides backward compatibility + * with bytecodes compiled against 1.4. + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public int getInt(Object o, int offset) { + return getInt(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putInt(Object o, int offset, int x) { + putInt(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public Object getObject(Object o, int offset) { + return getObject(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putObject(Object o, int offset, Object x) { + putObject(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public boolean getBoolean(Object o, int offset) { + return getBoolean(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putBoolean(Object o, int offset, boolean x) { + putBoolean(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public byte getByte(Object o, int offset) { + return getByte(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putByte(Object o, int offset, byte x) { + putByte(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public short getShort(Object o, int offset) { + return getShort(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putShort(Object o, int offset, short x) { + putShort(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public char getChar(Object o, int offset) { + return getChar(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putChar(Object o, int offset, char x) { + putChar(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public long getLong(Object o, int offset) { + return getLong(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putLong(Object o, int offset, long x) { + putLong(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public float getFloat(Object o, int offset) { + return getFloat(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putFloat(Object o, int offset, float x) { + putFloat(o, (long)offset, x); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public double getDouble(Object o, int offset) { + return getDouble(o, (long)offset); + } + + /** + * @deprecated As of 1.4.1, cast the 32-bit offset argument to a long. + * See {@link #staticFieldOffset}. + */ + @Deprecated + public void putDouble(Object o, int offset, double x) { + putDouble(o, (long)offset, x); + } + + // These work on values in the C heap. + + /** + * Fetches a value from a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #allocateMemory + */ + public native byte getByte(long address); + + /** + * Stores a value into a given memory address. If the address is zero, or + * does not point into a block obtained from {@link #allocateMemory}, the + * results are undefined. + * + * @see #getByte(long) + */ + public native void putByte(long address, byte x); + + /** @see #getByte(long) */ + public native short getShort(long address); + /** @see #putByte(long, byte) */ + public native void putShort(long address, short x); + /** @see #getByte(long) */ + public native char getChar(long address); + /** @see #putByte(long, byte) */ + public native void putChar(long address, char x); + /** @see #getByte(long) */ + public native int getInt(long address); + /** @see #putByte(long, byte) */ + public native void putInt(long address, int x); + /** @see #getByte(long) */ + public native long getLong(long address); + /** @see #putByte(long, byte) */ + public native void putLong(long address, long x); + /** @see #getByte(long) */ + public native float getFloat(long address); + /** @see #putByte(long, byte) */ + public native void putFloat(long address, float x); + /** @see #getByte(long) */ + public native double getDouble(long address); + /** @see #putByte(long, byte) */ + public native void putDouble(long address, double x); + + /** + * Fetches a native pointer from a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

    If the native pointer is less than 64 bits wide, it is extended as + * an unsigned number to a Java long. The pointer may be indexed by any + * given byte offset, simply by adding that offset (as a simple integer) to + * the long representing the pointer. The number of bytes actually read + * from the target address maybe determined by consulting {@link + * #addressSize}. + * + * @see #allocateMemory + */ + public native long getAddress(long address); + + /** + * Stores a native pointer into a given memory address. If the address is + * zero, or does not point into a block obtained from {@link + * #allocateMemory}, the results are undefined. + * + *

    The number of bytes actually written at the target address maybe + * determined by consulting {@link #addressSize}. + * + * @see #getAddress(long) + */ + public native void putAddress(long address, long x); + + /// wrappers for malloc, realloc, free: + + /** + * Allocates a new block of native memory, of the given size in bytes. The + * contents of the memory are uninitialized; they will generally be + * garbage. The resulting native pointer will never be zero, and will be + * aligned for all value types. Dispose of this memory by calling {@link + * #freeMemory}, or resize it with {@link #reallocateMemory}. + * + * @throws IllegalArgumentException if the size is negative or too large + * for the native size_t type + * + * @throws OutOfMemoryError if the allocation is refused by the system + * + * @see #getByte(long) + * @see #putByte(long, byte) + */ + public native long allocateMemory(long bytes); + + /** + * Resizes a new block of native memory, to the given size in bytes. The + * contents of the new block past the size of the old block are + * uninitialized; they will generally be garbage. The resulting native + * pointer will be zero if and only if the requested size is zero. The + * resulting native pointer will be aligned for all value types. Dispose + * of this memory by calling {@link #freeMemory}, or resize it with {@link + * #reallocateMemory}. The address passed to this method may be null, in + * which case an allocation will be performed. + * + * @throws IllegalArgumentException if the size is negative or too large + * for the native size_t type + * + * @throws OutOfMemoryError if the allocation is refused by the system + * + * @see #allocateMemory + */ + public native long reallocateMemory(long address, long bytes); + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). + * + *

    This method determines a block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

    The stores are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective address and + * length are all even modulo 8, the stores take place in 'long' units. + * If the effective address and length are (resp.) even modulo 4 or 2, + * the stores take place in units of 'int' or 'short'. + * + * @since 1.7 + */ + public native void setMemory(Object o, long offset, long bytes, byte value); + + /** + * Sets all bytes in a given block of memory to a fixed value + * (usually zero). This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. + * + *

    Equivalent to setMemory(null, address, bytes, value). + */ + public void setMemory(long address, long bytes, byte value) { + setMemory(null, address, bytes, value); + } + + /** + * Sets all bytes in a given block of memory to a copy of another + * block. + * + *

    This method determines each block's base address by means of two parameters, + * and so it provides (in effect) a double-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. When the object reference is null, + * the offset supplies an absolute base address. + * + *

    The transfers are in coherent (atomic) units of a size determined + * by the address and length parameters. If the effective addresses and + * length are all even modulo 8, the transfer takes place in 'long' units. + * If the effective addresses and length are (resp.) even modulo 4 or 2, + * the transfer takes place in units of 'int' or 'short'. + * + * @since 1.7 + */ + public native void copyMemory(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes); + /** + * Sets all bytes in a given block of memory to a copy of another + * block. This provides a single-register addressing mode, + * as discussed in {@link #getInt(Object,long)}. + * + * Equivalent to copyMemory(null, srcAddress, null, destAddress, bytes). + */ + public void copyMemory(long srcAddress, long destAddress, long bytes) { + copyMemory(null, srcAddress, null, destAddress, bytes); + } + + /** + * Disposes of a block of native memory, as obtained from {@link + * #allocateMemory} or {@link #reallocateMemory}. The address passed to + * this method may be null, in which case no action is taken. + * + * @see #allocateMemory + */ + public native void freeMemory(long address); + + /// random queries + + /** + * This constant differs from all results that will ever be returned from + * {@link #staticFieldOffset}, {@link #objectFieldOffset}, + * or {@link #arrayBaseOffset}. + */ + public static final int INVALID_FIELD_OFFSET = -1; + + /** + * Returns the offset of a field, truncated to 32 bits. + * This method is implemented as follows: + *

    +     * public int fieldOffset(Field f) {
    +     *     if (Modifier.isStatic(f.getModifiers()))
    +     *         return (int) staticFieldOffset(f);
    +     *     else
    +     *         return (int) objectFieldOffset(f);
    +     * }
    +     * 
    + * @deprecated As of 1.4.1, use {@link #staticFieldOffset} for static + * fields and {@link #objectFieldOffset} for non-static fields. + */ + @Deprecated + public int fieldOffset(Field f) { + if (Modifier.isStatic(f.getModifiers())) + return (int) staticFieldOffset(f); + else + return (int) objectFieldOffset(f); + } + + /** + * Returns the base address for accessing some static field + * in the given class. This method is implemented as follows: + *
    +     * public Object staticFieldBase(Class c) {
    +     *     Field[] fields = c.getDeclaredFields();
    +     *     for (int i = 0; i < fields.length; i++) {
    +     *         if (Modifier.isStatic(fields[i].getModifiers())) {
    +     *             return staticFieldBase(fields[i]);
    +     *         }
    +     *     }
    +     *     return null;
    +     * }
    +     * 
    + * @deprecated As of 1.4.1, use {@link #staticFieldBase(Field)} + * to obtain the base pertaining to a specific {@link Field}. + * This method works only for JVMs which store all statics + * for a given class in one place. + */ + @Deprecated + public Object staticFieldBase(Class c) { + Field[] fields = c.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if (Modifier.isStatic(fields[i].getModifiers())) { + return staticFieldBase(fields[i]); + } + } + return null; + } + + /** + * Report the location of a given field in the storage allocation of its + * class. Do not expect to perform any sort of arithmetic on this offset; + * it is just a cookie which is passed to the unsafe heap memory accessors. + * + *

    Any given field will always have the same offset and base, and no + * two distinct fields of the same class will ever have the same offset + * and base. + * + *

    As of 1.4.1, offsets for fields are represented as long values, + * although the Sun JVM does not use the most significant 32 bits. + * However, JVM implementations which store static fields at absolute + * addresses can use long offsets and null base pointers to express + * the field locations in a form usable by {@link #getInt(Object,long)}. + * Therefore, code which will be ported to such JVMs on 64-bit platforms + * must preserve all bits of static field offsets. + * @see #getInt(Object, long) + */ + public native long staticFieldOffset(Field f); + + /** + * Report the location of a given static field, in conjunction with {@link + * #staticFieldBase}. + *

    Do not expect to perform any sort of arithmetic on this offset; + * it is just a cookie which is passed to the unsafe heap memory accessors. + * + *

    Any given field will always have the same offset, and no two distinct + * fields of the same class will ever have the same offset. + * + *

    As of 1.4.1, offsets for fields are represented as long values, + * although the Sun JVM does not use the most significant 32 bits. + * It is hard to imagine a JVM technology which needs more than + * a few bits to encode an offset within a non-array object, + * However, for consistency with other methods in this class, + * this method reports its result as a long value. + * @see #getInt(Object, long) + */ + public native long objectFieldOffset(Field f); + + /** + * Report the location of a given static field, in conjunction with {@link + * #staticFieldOffset}. + *

    Fetch the base "Object", if any, with which static fields of the + * given class can be accessed via methods like {@link #getInt(Object, + * long)}. This value may be null. This value may refer to an object + * which is a "cookie", not guaranteed to be a real Object, and it should + * not be used in any way except as argument to the get and put routines in + * this class. + */ + public native Object staticFieldBase(Field f); + + /** + * Detect if the given class may need to be initialized. This is often + * needed in conjunction with obtaining the static field base of a + * class. + * @return false only if a call to {@code ensureClassInitialized} would have no effect + */ + public native boolean shouldBeInitialized(Class c); + + /** + * Ensure the given class has been initialized. This is often + * needed in conjunction with obtaining the static field base of a + * class. + */ + public native void ensureClassInitialized(Class c); + + /** + * 返回 类信息中数组第一个元素的偏移量. If {@link #arrayIndexScale} returns a non-zero value + * for the same class, 可以配合'每个元素的偏移量'形成固定的偏移量到数组class指定的位置拿到元素. + * + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + */ + public native int arrayBaseOffset(Class arrayClass); + + /** The value of {@code arrayBaseOffset(boolean[].class)} */ + public static final int ARRAY_BOOLEAN_BASE_OFFSET + = theUnsafe.arrayBaseOffset(boolean[].class); + + /** The value of {@code arrayBaseOffset(byte[].class)} */ + public static final int ARRAY_BYTE_BASE_OFFSET + = theUnsafe.arrayBaseOffset(byte[].class); + + /** The value of {@code arrayBaseOffset(short[].class)} */ + public static final int ARRAY_SHORT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(short[].class); + + /** The value of {@code arrayBaseOffset(char[].class)} */ + public static final int ARRAY_CHAR_BASE_OFFSET + = theUnsafe.arrayBaseOffset(char[].class); + + /** The value of {@code arrayBaseOffset(int[].class)} */ + public static final int ARRAY_INT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(int[].class); + + /** The value of {@code arrayBaseOffset(long[].class)} */ + public static final int ARRAY_LONG_BASE_OFFSET + = theUnsafe.arrayBaseOffset(long[].class); + + /** The value of {@code arrayBaseOffset(float[].class)} */ + public static final int ARRAY_FLOAT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(float[].class); + + /** The value of {@code arrayBaseOffset(double[].class)} */ + public static final int ARRAY_DOUBLE_BASE_OFFSET + = theUnsafe.arrayBaseOffset(double[].class); + + /** The value of {@code arrayBaseOffset(Object[].class)} */ + public static final int ARRAY_OBJECT_BASE_OFFSET + = theUnsafe.arrayBaseOffset(Object[].class); + + /** + * Report the scale factor for addressing elements in the storage + * allocation of a given array class. However, arrays of "narrow" types + * will generally not work properly with accessors like {@link + * #getByte(Object, int)}, so the scale factor for such classes is reported + * as zero. + * + * @see #arrayBaseOffset + * @see #getInt(Object, long) + * @see #putInt(Object, long, int) + */ + public native int arrayIndexScale(Class arrayClass); + + /** The value of {@code arrayIndexScale(boolean[].class)} */ + public static final int ARRAY_BOOLEAN_INDEX_SCALE + = theUnsafe.arrayIndexScale(boolean[].class); + + /** The value of {@code arrayIndexScale(byte[].class)} */ + public static final int ARRAY_BYTE_INDEX_SCALE + = theUnsafe.arrayIndexScale(byte[].class); + + /** The value of {@code arrayIndexScale(short[].class)} */ + public static final int ARRAY_SHORT_INDEX_SCALE + = theUnsafe.arrayIndexScale(short[].class); + + /** The value of {@code arrayIndexScale(char[].class)} */ + public static final int ARRAY_CHAR_INDEX_SCALE + = theUnsafe.arrayIndexScale(char[].class); + + /** The value of {@code arrayIndexScale(int[].class)} */ + public static final int ARRAY_INT_INDEX_SCALE + = theUnsafe.arrayIndexScale(int[].class); + + /** The value of {@code arrayIndexScale(long[].class)} */ + public static final int ARRAY_LONG_INDEX_SCALE + = theUnsafe.arrayIndexScale(long[].class); + + /** The value of {@code arrayIndexScale(float[].class)} */ + public static final int ARRAY_FLOAT_INDEX_SCALE + = theUnsafe.arrayIndexScale(float[].class); + + /** The value of {@code arrayIndexScale(double[].class)} */ + public static final int ARRAY_DOUBLE_INDEX_SCALE + = theUnsafe.arrayIndexScale(double[].class); + + /** The value of {@code arrayIndexScale(Object[].class)} */ + public static final int ARRAY_OBJECT_INDEX_SCALE + = theUnsafe.arrayIndexScale(Object[].class); + + /** + * Report the size in bytes of a native pointer, as stored via {@link + * #putAddress}. This value will be either 4 or 8. Note that the sizes of + * other primitive types (as stored in native memory blocks) is determined + * fully by their information content. + */ + public native int addressSize(); + + /** The value of {@code addressSize()} */ + public static final int ADDRESS_SIZE = theUnsafe.addressSize(); + + /** + * Report the size in bytes of a native memory page (whatever that is). + * This value will always be a power of two. + */ + public native int pageSize(); + + + /// random trusted operations from JNI: + + /** + * Tell the VM to define a class, without security checks. By default, the + * class loader and protection domain come from the caller's class. + */ + public native Class defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain); + + /** + * Define a class but do not make it known to the class loader or system dictionary. + *

    + * For each CP entry, the corresponding CP patch must either be null or have + * the a format that matches its tag: + *

      + *
    • Integer, Long, Float, Double: the corresponding wrapper object type from java.lang + *
    • Utf8: a string (must have suitable syntax if used as signature or name) + *
    • Class: any java.lang.Class object + *
    • String: any object (not just a java.lang.String) + *
    • InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments + *
    + * @params hostClass context for linkage, access control, protection domain, and class loader + * @params data bytes of a class file + * @params cpPatches where non-null entries exist, they replace corresponding CP entries in data + */ + public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + + + /** Allocate an instance but do not run any constructor. + Initializes the class if it has not yet been. */ + public native Object allocateInstance(Class cls) + throws InstantiationException; + + /** Lock the object. It must get unlocked via {@link #monitorExit}. */ + public native void monitorEnter(Object o); + + /** + * Unlock the object. It must have been locked via {@link + * #monitorEnter}. + */ + public native void monitorExit(Object o); + + /** + * Tries to lock the object. Returns true or false to indicate + * whether the lock succeeded. If it did, the object must be + * unlocked via {@link #monitorExit}. + */ + public native boolean tryMonitorEnter(Object o); + + /** Throw the exception without telling the verifier. */ + public native void throwException(Throwable ee); + + + /** + * Atomically update Java variable to x if it is currently + * holding expected. + * @return true if successful + */ + public final native boolean compareAndSwapObject(Object o, long offset, + Object expected, + Object x); + + /** + * Atomically update Java variable to x if it is currently + * holding expected. + * @return true if successful + */ + public final native boolean compareAndSwapInt(Object o, long offset, + int expected, + int x); + + /** + * Atomically update Java variable to x if it is currently + * holding expected. + * @return true if successful + */ + public final native boolean compareAndSwapLong(Object o, long offset, + long expected, + long x); + + /** + * Fetches a reference value from a given Java variable, with volatile + * load semantics. Otherwise identical to {@link #getObject(Object, long)} + */ + public native Object getObjectVolatile(Object o, long offset); + + /** + * Stores a reference value into a given Java variable, with + * volatile store semantics. Otherwise identical to {@link #putObject(Object, long, Object)} + */ + public native void putObjectVolatile(Object o, long offset, Object x); + + /** Volatile version of {@link #getInt(Object, long)} */ + public native int getIntVolatile(Object o, long offset); + + /** Volatile version of {@link #putInt(Object, long, int)} */ + public native void putIntVolatile(Object o, long offset, int x); + + /** Volatile version of {@link #getBoolean(Object, long)} */ + public native boolean getBooleanVolatile(Object o, long offset); + + /** Volatile version of {@link #putBoolean(Object, long, boolean)} */ + public native void putBooleanVolatile(Object o, long offset, boolean x); + + /** Volatile version of {@link #getByte(Object, long)} */ + public native byte getByteVolatile(Object o, long offset); + + /** Volatile version of {@link #putByte(Object, long, byte)} */ + public native void putByteVolatile(Object o, long offset, byte x); + + /** Volatile version of {@link #getShort(Object, long)} */ + public native short getShortVolatile(Object o, long offset); + + /** Volatile version of {@link #putShort(Object, long, short)} */ + public native void putShortVolatile(Object o, long offset, short x); + + /** Volatile version of {@link #getChar(Object, long)} */ + public native char getCharVolatile(Object o, long offset); + + /** Volatile version of {@link #putChar(Object, long, char)} */ + public native void putCharVolatile(Object o, long offset, char x); + + /** Volatile version of {@link #getLong(Object, long)} */ + public native long getLongVolatile(Object o, long offset); + + /** Volatile version of {@link #putLong(Object, long, long)} */ + public native void putLongVolatile(Object o, long offset, long x); + + /** Volatile version of {@link #getFloat(Object, long)} */ + public native float getFloatVolatile(Object o, long offset); + + /** Volatile version of {@link #putFloat(Object, long, float)} */ + public native void putFloatVolatile(Object o, long offset, float x); + + /** Volatile version of {@link #getDouble(Object, long)} */ + public native double getDoubleVolatile(Object o, long offset); + + /** Volatile version of {@link #putDouble(Object, long, double)} */ + public native void putDoubleVolatile(Object o, long offset, double x); + + /** + * Version of {@link #putObjectVolatile(Object, long, Object)} + * that does not guarantee immediate visibility of the store to + * other threads. This method is generally only useful if the + * underlying field is a Java volatile (or if an array cell, one + * that is otherwise only accessed using volatile accesses). + */ + public native void putOrderedObject(Object o, long offset, Object x); + + /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ + public native void putOrderedInt(Object o, long offset, int x); + + /** Ordered/Lazy version of {@link #putLongVolatile(Object, long, long)} */ + public native void putOrderedLong(Object o, long offset, long x); + + /** + * Unblock the given thread blocked on park, or, if it is + * not blocked, cause the subsequent call to park not to + * block. Note: this operation is "unsafe" solely because the + * caller must somehow ensure that the thread has not been + * destroyed. Nothing special is usually required to ensure this + * when called from Java (in which there will ordinarily be a live + * reference to the thread) but this is not nearly-automatically + * so when calling from native code. + * @param thread the thread to unpark. + * + */ + public native void unpark(Object thread); + + /** + * Block current thread, returning when a balancing + * unpark occurs, or a balancing unpark has + * already occurred, or the thread is interrupted, or, if not + * absolute and time is not zero, the given time nanoseconds have + * elapsed, or if absolute, the given deadline in milliseconds + * since Epoch has passed, or spuriously (i.e., returning for no + * "reason"). Note: This operation is in the Unsafe class only + * because unpark is, so it would be strange to place it + * elsewhere. + */ + public native void park(boolean isAbsolute, long time); + + /** + * Gets the load average in the system run queue assigned + * to the available processors averaged over various periods of time. + * This method retrieves the given nelem samples and + * assigns to the elements of the given loadavg array. + * The system imposes a maximum of 3 samples, representing + * averages over the last 1, 5, and 15 minutes, respectively. + * + * @params loadavg an array of double of size nelems + * @params nelems the number of samples to be retrieved and + * must be 1 to 3. + * + * @return the number of samples actually retrieved; or -1 + * if the load average is unobtainable. + */ + public native int getLoadAverage(double[] loadavg, int nelems); + + // The following contain CAS-based Java implementations used on + // platforms not supporting native instructions + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object o + * at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since 1.8 + */ + public final int getAndAddInt(Object o, long offset, int delta) { + int v; + do { + v = getIntVolatile(o, offset); + } while (!compareAndSwapInt(o, offset, v, v + delta)); + return v; + } + + /** + * Atomically adds the given value to the current value of a field + * or array element within the given object o + * at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param delta the value to add + * @return the previous value + * @since 1.8 + */ + public final long getAndAddLong(Object o, long offset, long delta) { + long v; + do { + v = getLongVolatile(o, offset); + } while (!compareAndSwapLong(o, offset, v, v + delta)); + return v; + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object o + * at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + public final int getAndSetInt(Object o, long offset, int newValue) { + int v; + do { + v = getIntVolatile(o, offset); + } while (!compareAndSwapInt(o, offset, v, newValue)); + return v; + } + + /** + * Atomically exchanges the given value with the current value of + * a field or array element within the given object o + * at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + public final long getAndSetLong(Object o, long offset, long newValue) { + long v; + do { + v = getLongVolatile(o, offset); + } while (!compareAndSwapLong(o, offset, v, newValue)); + return v; + } + + /** + * Atomically exchanges the given reference value with the current + * reference value of a field or array element within the given + * object o at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + public final Object getAndSetObject(Object o, long offset, Object newValue) { + Object v; + do { + v = getObjectVolatile(o, offset); + } while (!compareAndSwapObject(o, offset, v, newValue)); + return v; + } + + + /** + * Ensures lack of reordering of loads before the fence + * with loads or stores after the fence. + * @since 1.8 + */ + public native void loadFence(); + + /** + * Ensures lack of reordering of stores before the fence + * with loads or stores after the fence. + * @since 1.8 + */ + public native void storeFence(); + + /** + * Ensures lack of reordering of loads or stores before the fence + * with loads or stores after the fence. + * @since 1.8 + */ + public native void fullFence(); + + /** + * Throws IllegalAccessError; for use by the VM. + * @since 1.8 + */ + private static void throwIllegalAccessError() { + throw new IllegalAccessError(); + } + +} diff --git a/src/sun/misc/VM.java b/src/sun/misc/VM.java new file mode 100644 index 00000000..31144eea --- /dev/null +++ b/src/sun/misc/VM.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import static java.lang.Thread.State.BLOCKED; +import static java.lang.Thread.State.NEW; +import static java.lang.Thread.State.RUNNABLE; +import static java.lang.Thread.State.TERMINATED; +import static java.lang.Thread.State.TIMED_WAITING; +import static java.lang.Thread.State.WAITING; + +import java.util.Properties; + +public class VM { + + /* The following methods used to be native methods that instruct + * the VM to selectively suspend certain threads in low-memory + * situations. They are inherently dangerous and not implementable + * on native threads. We removed them in JDK 1.2. The skeletons + * remain so that existing applications that use these methods + * will still work. + */ + private static boolean suspended = false; + + /** @deprecated */ + @Deprecated + public static boolean threadsSuspended() { + return suspended; + } + + @SuppressWarnings("deprecation") + public static boolean allowThreadSuspension(ThreadGroup g, boolean b) { + return g.allowThreadSuspension(b); + } + + /** @deprecated */ + @Deprecated + public static boolean suspendThreads() { + suspended = true; + return true; + } + + // Causes any suspended threadgroups to be resumed. + /** @deprecated */ + @Deprecated + public static void unsuspendThreads() { + suspended = false; + } + + // Causes threadgroups no longer marked suspendable to be resumed. + /** @deprecated */ + @Deprecated + public static void unsuspendSomeThreads() { + } + + /* Deprecated fields and methods -- Memory advice not supported in 1.2 */ + + /** @deprecated */ + @Deprecated + public static final int STATE_GREEN = 1; + + /** @deprecated */ + @Deprecated + public static final int STATE_YELLOW = 2; + + /** @deprecated */ + @Deprecated + public static final int STATE_RED = 3; + + /** @deprecated */ + @Deprecated + public static final int getState() { + return STATE_GREEN; + } + + /** @deprecated */ + @Deprecated + public static void registerVMNotification(VMNotification n) { } + + /** @deprecated */ + @Deprecated + public static void asChange(int as_old, int as_new) { } + + /** @deprecated */ + @Deprecated + public static void asChange_otherthread(int as_old, int as_new) { } + + /* + * Not supported in 1.2 because these will have to be exported as + * JVM functions, and we are not sure we want do that. Leaving + * here so it can be easily resurrected -- just remove the // + * comments. + */ + + /** + * Resume Java profiling. All profiling data is added to any + * earlier profiling, unless resetJavaProfiler is + * called in between. If profiling was not started from the + * command line, resumeJavaProfiler will start it. + *

    + * + * NOTE: Profiling must be enabled from the command line for a + * java.prof report to be automatically generated on exit; if not, + * writeJavaProfilerReport must be invoked to write a report. + * + * @see resetJavaProfiler + * @see writeJavaProfilerReport + */ + + // public native static void resumeJavaProfiler(); + + /** + * Suspend Java profiling. + */ + // public native static void suspendJavaProfiler(); + + /** + * Initialize Java profiling. Any accumulated profiling + * information is discarded. + */ + // public native static void resetJavaProfiler(); + + /** + * Write the current profiling contents to the file "java.prof". + * If the file already exists, it will be overwritten. + */ + // public native static void writeJavaProfilerReport(); + + + private static volatile boolean booted = false; + private static final Object lock = new Object(); + + // Invoked by by System.initializeSystemClass just before returning. + // Subsystems that are invoked during initialization can check this + // property in order to avoid doing things that should wait until the + // application class loader has been set up. + // + public static void booted() { + synchronized (lock) { + booted = true; + lock.notifyAll(); + } + } + + public static boolean isBooted() { + return booted; + } + + // Waits until VM completes initialization + // + // This method is invoked by the Finalizer thread + public static void awaitBooted() throws InterruptedException { + synchronized (lock) { + while (!booted) { + lock.wait(); + } + } + } + + // A user-settable upper limit on the maximum amount of allocatable direct + // buffer memory. This value may be changed during VM initialization if + // "java" is launched with "-XX:MaxDirectMemorySize=". + // + // The initial value of this field is arbitrary; during JRE initialization + // it will be reset to the value specified on the command line, if any, + // otherwise to Runtime.getRuntime().maxMemory(). + // + private static long directMemory = 64 * 1024 * 1024; + + // Returns the maximum amount of allocatable direct buffer memory. + // The directMemory variable is initialized during system initialization + // in the saveAndRemoveProperties method. + // + public static long maxDirectMemory() { + return directMemory; + } + + // User-controllable flag that determines if direct buffers should be page + // aligned. The "-XX:+PageAlignDirectMemory" option can be used to force + // buffers, allocated by ByteBuffer.allocateDirect, to be page aligned. + private static boolean pageAlignDirectMemory; + + // Returns {@code true} if the direct buffers should be page aligned. This + // variable is initialized by saveAndRemoveProperties. + public static boolean isDirectMemoryPageAligned() { + return pageAlignDirectMemory; + } + + // A user-settable boolean to determine whether ClassLoader.loadClass should + // accept array syntax. This value may be changed during VM initialization + // via the system property "sun.lang.ClassLoader.allowArraySyntax". + // + // The default for 1.5 is "true", array syntax is allowed. In 1.6, the + // default will be "false". The presence of this system property to + // control array syntax allows applications the ability to preview this new + // behaviour. + // + private static boolean defaultAllowArraySyntax = false; + private static boolean allowArraySyntax = defaultAllowArraySyntax; + + // The allowArraySyntax boolean is initialized during system initialization + // in the saveAndRemoveProperties method. + // + // It is initialized based on the value of the system property + // "sun.lang.ClassLoader.allowArraySyntax". If the system property is not + // provided, the default for 1.5 is "true". In 1.6, the default will be + // "false". If the system property is provided, then the value of + // allowArraySyntax will be equal to "true" if Boolean.parseBoolean() + // returns "true". Otherwise, the field will be set to "false". + // + public static boolean allowArraySyntax() { + return allowArraySyntax; + } + + /** + * Returns true if the given class loader is in the system domain + * in which all permissions are granted. + */ + public static boolean isSystemDomainLoader(ClassLoader loader) { + return loader == null; + } + + /** + * Returns the system property of the specified key saved at + * system initialization time. This method should only be used + * for the system properties that are not changed during runtime. + * It accesses a private copy of the system properties so + * that user's locking of the system properties object will not + * cause the library to deadlock. + * + * Note that the saved system properties do not include + * the ones set by sun.misc.Version.init(). + * + */ + public static String getSavedProperty(String key) { + if (savedProps.isEmpty()) + throw new IllegalStateException("Should be non-empty if initialized"); + + return savedProps.getProperty(key); + } + + // TODO: the Property Management needs to be refactored and + // the appropriate prop keys need to be accessible to the + // calling classes to avoid duplication of keys. + private static final Properties savedProps = new Properties(); + + // Save a private copy of the system properties and remove + // the system properties that are not intended for public access. + // + // This method can only be invoked during system initialization. + public static void saveAndRemoveProperties(Properties props) { + if (booted) + throw new IllegalStateException("System initialization has completed"); + + savedProps.putAll(props); + + // Set the maximum amount of direct memory. This value is controlled + // by the vm option -XX:MaxDirectMemorySize=. + // The maximum amount of allocatable direct buffer memory (in bytes) + // from the system property sun.nio.MaxDirectMemorySize set by the VM. + // The system property will be removed. + String s = (String)props.remove("sun.nio.MaxDirectMemorySize"); + if (s != null) { + if (s.equals("-1")) { + // -XX:MaxDirectMemorySize not given, take default + directMemory = Runtime.getRuntime().maxMemory(); + } else { + long l = Long.parseLong(s); + if (l > -1) + directMemory = l; + } + } + + // Check if direct buffers should be page aligned + s = (String)props.remove("sun.nio.PageAlignDirectMemory"); + if ("true".equals(s)) + pageAlignDirectMemory = true; + + // Set a boolean to determine whether ClassLoader.loadClass accepts + // array syntax. This value is controlled by the system property + // "sun.lang.ClassLoader.allowArraySyntax". + s = props.getProperty("sun.lang.ClassLoader.allowArraySyntax"); + allowArraySyntax = (s == null + ? defaultAllowArraySyntax + : Boolean.parseBoolean(s)); + + // Remove other private system properties + // used by java.lang.Integer.IntegerCache + props.remove("java.lang.Integer.IntegerCache.high"); + + // used by java.util.zip.ZipFile + props.remove("sun.zip.disableMemoryMapping"); + + // used by sun.launcher.LauncherHelper + props.remove("sun.java.launcher.diag"); + + // used by sun.misc.URLClassPath + props.remove("sun.cds.enableSharedLookupCache"); + } + + // Initialize any miscellenous operating system settings that need to be + // set for the class libraries. + // + public static void initializeOSEnvironment() { + if (!booted) { + OSEnvironment.initialize(); + } + } + + /* Current count of objects pending for finalization */ + private static volatile int finalRefCount = 0; + + /* Peak count of objects pending for finalization */ + private static volatile int peakFinalRefCount = 0; + + /* + * Gets the number of objects pending for finalization. + * + * @return the number of objects pending for finalization. + */ + public static int getFinalRefCount() { + return finalRefCount; + } + + /* + * Gets the peak number of objects pending for finalization. + * + * @return the peak number of objects pending for finalization. + */ + public static int getPeakFinalRefCount() { + return peakFinalRefCount; + } + + /* + * Add n to the objects pending for finalization count. + * + * @param n an integer value to be added to the objects pending + * for finalization count + */ + public static void addFinalRefCount(int n) { + // The caller must hold lock to synchronize the update. + + finalRefCount += n; + if (finalRefCount > peakFinalRefCount) { + peakFinalRefCount = finalRefCount; + } + } + + /** + * Returns Thread.State for the given threadStatus + */ + public static Thread.State toThreadState(int threadStatus) { + if ((threadStatus & JVMTI_THREAD_STATE_RUNNABLE) != 0) { + return RUNNABLE; + } else if ((threadStatus & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) { + return BLOCKED; + } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) { + return WAITING; + } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) { + return TIMED_WAITING; + } else if ((threadStatus & JVMTI_THREAD_STATE_TERMINATED) != 0) { + return TERMINATED; + } else if ((threadStatus & JVMTI_THREAD_STATE_ALIVE) == 0) { + return NEW; + } else { + return RUNNABLE; + } + } + + /* The threadStatus field is set by the VM at state transition + * in the hotspot implementation. Its value is set according to + * the JVM TI specification GetThreadState function. + */ + private final static int JVMTI_THREAD_STATE_ALIVE = 0x0001; + private final static int JVMTI_THREAD_STATE_TERMINATED = 0x0002; + private final static int JVMTI_THREAD_STATE_RUNNABLE = 0x0004; + private final static int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400; + private final static int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010; + private final static int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020; + + /* + * Returns the first non-null class loader up the execution stack, + * or null if only code from the null class loader is on the stack. + */ + public static native ClassLoader latestUserDefinedLoader(); + + static { + initialize(); + } + private native static void initialize(); +} diff --git a/src/sun/misc/VMNotification.java b/src/sun/misc/VMNotification.java new file mode 100644 index 00000000..bf4abef3 --- /dev/null +++ b/src/sun/misc/VMNotification.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +/** @deprecated */ +@Deprecated +public interface VMNotification { + + // when the vm switches allocation states, we get notified + // (possible semantics: if the state changes while in this + // notification, don't recursively notify). + // oldState and newState may be the same if we are just releasing + // suspended threads. + void newAllocState(int oldState, int newState, + boolean threadsSuspended); +} diff --git a/src/sun/misc/VMSupport.java b/src/sun/misc/VMSupport.java new file mode 100644 index 00000000..801585ec --- /dev/null +++ b/src/sun/misc/VMSupport.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.misc; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/* + * Support class used by JVMTI and VM attach mechanism. + */ +public class VMSupport { + + private static Properties agentProps = null; + /** + * Returns the agent properties. + */ + public static synchronized Properties getAgentProperties() { + if (agentProps == null) { + agentProps = new Properties(); + initAgentProperties(agentProps); + } + return agentProps; + } + private static native Properties initAgentProperties(Properties props); + + /** + * Write the given properties list to a byte array and return it. Properties with + * a key or value that is not a String is filtered out. The stream written to the byte + * array is ISO 8859-1 encoded. + */ + private static byte[] serializePropertiesToByteArray(Properties p) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(4096); + + Properties props = new Properties(); + + // stringPropertyNames() returns a snapshot of the property keys + Set keyset = p.stringPropertyNames(); + for (String key : keyset) { + String value = p.getProperty(key); + props.put(key, value); + } + + props.store(out, null); + return out.toByteArray(); + } + + public static byte[] serializePropertiesToByteArray() throws IOException { + return serializePropertiesToByteArray(System.getProperties()); + } + + public static byte[] serializeAgentPropertiesToByteArray() throws IOException { + return serializePropertiesToByteArray(getAgentProperties()); + } + + /* + * Returns true if the given JAR file has the Class-Path attribute in the + * main section of the JAR manifest. Throws RuntimeException if the given + * path is not a JAR file or some other error occurs. + */ + public static boolean isClassPathAttributePresent(String path) { + try { + Manifest man = (new JarFile(path)).getManifest(); + if (man != null) { + if (man.getMainAttributes().getValue(Attributes.Name.CLASS_PATH) != null) { + return true; + } + } + return false; + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage()); + } + } + + /* + * Return the temporary directory that the VM uses for the attach + * and perf data files. + * + * It is important that this directory is well-known and the + * same for all VM instances. It cannot be affected by configuration + * variables such as java.io.tmpdir. + */ + public static native String getVMTemporaryDirectory(); +} diff --git a/src/sun/misc/Version.java.template b/src/sun/misc/Version.java.template new file mode 100644 index 00000000..dd47869a --- /dev/null +++ b/src/sun/misc/Version.java.template @@ -0,0 +1,348 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; +import java.io.PrintStream; + +public class Version { + + + private static final String launcher_name = + "@@launcher_name@@"; + + private static final String java_version = + "@@java_version@@"; + + private static final String java_runtime_name = + "@@java_runtime_name@@"; + + private static final String java_profile_name = + "@@java_profile_name@@"; + + private static final String java_runtime_version = + "@@java_runtime_version@@"; + + static { + init(); + } + + public static void init() { + System.setProperty("java.version", java_version); + System.setProperty("java.runtime.version", java_runtime_version); + System.setProperty("java.runtime.name", java_runtime_name); + } + + private static boolean versionsInitialized = false; + private static int jvm_major_version = 0; + private static int jvm_minor_version = 0; + private static int jvm_micro_version = 0; + private static int jvm_update_version = 0; + private static int jvm_build_number = 0; + private static String jvm_special_version = null; + private static int jdk_major_version = 0; + private static int jdk_minor_version = 0; + private static int jdk_micro_version = 0; + private static int jdk_update_version = 0; + private static int jdk_build_number = 0; + private static String jdk_special_version = null; + + /** + * In case you were wondering this method is called by java -version. + * Sad that it prints to stderr; would be nicer if default printed on + * stdout. + */ + public static void print() { + print(System.err); + } + + /** + * This is the same as print except that it adds an extra line-feed + * at the end, typically used by the -showversion in the launcher + */ + public static void println() { + print(System.err); + System.err.println(); + } + + /** + * Give a stream, it will print version info on it. + */ + public static void print(PrintStream ps) { + boolean isHeadless = false; + + /* Report that we're running headless if the property is true */ + String headless = System.getProperty("java.awt.headless"); + if ( (headless != null) && (headless.equalsIgnoreCase("true")) ) { + isHeadless = true; + } + + /* First line: platform version. */ + ps.println(launcher_name + " version \"" + java_version + "\""); + + /* Second line: runtime version (ie, libraries). */ + + ps.print(java_runtime_name + " (build " + java_runtime_version); + + if (java_profile_name.length() > 0) { + // profile name + ps.print(", profile " + java_profile_name); + } + + if (java_runtime_name.indexOf("Embedded") != -1 && isHeadless) { + // embedded builds report headless state + ps.print(", headless"); + } + ps.println(')'); + + /* Third line: JVM information. */ + String java_vm_name = System.getProperty("java.vm.name"); + String java_vm_version = System.getProperty("java.vm.version"); + String java_vm_info = System.getProperty("java.vm.info"); + ps.println(java_vm_name + " (build " + java_vm_version + ", " + + java_vm_info + ")"); + } + + + /** + * Returns the major version of the running JVM if it's 1.6 or newer + * or any RE VM build. It will return 0 if it's an internal 1.5 or + * 1.4.x build. + * + * @since 1.6 + */ + public static synchronized int jvmMajorVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jvm_major_version; + } + + /** + * Returns the minor version of the running JVM if it's 1.6 or newer + * or any RE VM build. It will return 0 if it's an internal 1.5 or + * 1.4.x build. + * @since 1.6 + */ + public static synchronized int jvmMinorVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jvm_minor_version; + } + + + /** + * Returns the micro version of the running JVM if it's 1.6 or newer + * or any RE VM build. It will return 0 if it's an internal 1.5 or + * 1.4.x build. + * @since 1.6 + */ + public static synchronized int jvmMicroVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jvm_micro_version; + } + + /** + * Returns the update release version of the running JVM if it's + * a RE build. It will return 0 if it's an internal build. + * @since 1.6 + */ + public static synchronized int jvmUpdateVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jvm_update_version; + } + + public static synchronized String jvmSpecialVersion() { + if (!versionsInitialized) { + initVersions(); + } + if (jvm_special_version == null) { + jvm_special_version = getJvmSpecialVersion(); + } + return jvm_special_version; + } + public static native String getJvmSpecialVersion(); + + /** + * Returns the build number of the running JVM if it's a RE build + * It will return 0 if it's an internal build. + * @since 1.6 + */ + public static synchronized int jvmBuildNumber() { + if (!versionsInitialized) { + initVersions(); + } + return jvm_build_number; + } + + /** + * Returns the major version of the running JDK. + * + * @since 1.6 + */ + public static synchronized int jdkMajorVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jdk_major_version; + } + + /** + * Returns the minor version of the running JDK. + * @since 1.6 + */ + public static synchronized int jdkMinorVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jdk_minor_version; + } + + /** + * Returns the micro version of the running JDK. + * @since 1.6 + */ + public static synchronized int jdkMicroVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jdk_micro_version; + } + + /** + * Returns the update release version of the running JDK if it's + * a RE build. It will return 0 if it's an internal build. + * @since 1.6 + */ + public static synchronized int jdkUpdateVersion() { + if (!versionsInitialized) { + initVersions(); + } + return jdk_update_version; + } + + public static synchronized String jdkSpecialVersion() { + if (!versionsInitialized) { + initVersions(); + } + if (jdk_special_version == null) { + jdk_special_version = getJdkSpecialVersion(); + } + return jdk_special_version; + } + public static native String getJdkSpecialVersion(); + + /** + * Returns the build number of the running JDK if it's a RE build + * It will return 0 if it's an internal build. + * @since 1.6 + */ + public static synchronized int jdkBuildNumber() { + if (!versionsInitialized) { + initVersions(); + } + return jdk_build_number; + } + + // true if JVM exports the version info including the capabilities + private static boolean jvmVersionInfoAvailable; + private static synchronized void initVersions() { + if (versionsInitialized) { + return; + } + jvmVersionInfoAvailable = getJvmVersionInfo(); + if (!jvmVersionInfoAvailable) { + // parse java.vm.version for older JVM before the + // new JVM_GetVersionInfo is added. + // valid format of the version string is: + // n.n.n[_uu[c]][-]-bxx + CharSequence cs = System.getProperty("java.vm.version"); + if (cs.length() >= 5 && + Character.isDigit(cs.charAt(0)) && cs.charAt(1) == '.' && + Character.isDigit(cs.charAt(2)) && cs.charAt(3) == '.' && + Character.isDigit(cs.charAt(4))) { + jvm_major_version = Character.digit(cs.charAt(0), 10); + jvm_minor_version = Character.digit(cs.charAt(2), 10); + jvm_micro_version = Character.digit(cs.charAt(4), 10); + cs = cs.subSequence(5, cs.length()); + if (cs.charAt(0) == '_' && cs.length() >= 3 && + Character.isDigit(cs.charAt(1)) && + Character.isDigit(cs.charAt(2))) { + int nextChar = 3; + try { + String uu = cs.subSequence(1, 3).toString(); + jvm_update_version = Integer.valueOf(uu).intValue(); + if (cs.length() >= 4) { + char c = cs.charAt(3); + if (c >= 'a' && c <= 'z') { + jvm_special_version = Character.toString(c); + nextChar++; + } + } + } catch (NumberFormatException e) { + // not conforming to the naming convention + return; + } + cs = cs.subSequence(nextChar, cs.length()); + } + if (cs.charAt(0) == '-') { + // skip the first character + // valid format: -bxx or bxx + // non-product VM will have -debug|-release appended + cs = cs.subSequence(1, cs.length()); + String[] res = cs.toString().split("-"); + for (String s : res) { + if (s.charAt(0) == 'b' && s.length() == 3 && + Character.isDigit(s.charAt(1)) && + Character.isDigit(s.charAt(2))) { + jvm_build_number = + Integer.valueOf(s.substring(1, 3)).intValue(); + break; + } + } + } + } + } + getJdkVersionInfo(); + versionsInitialized = true; + } + + // Gets the JVM version info if available and sets the jvm_*_version fields + // and its capabilities. + // + // Return false if not available which implies an old VM (Tiger or before). + private static native boolean getJvmVersionInfo(); + private static native void getJdkVersionInfo(); +} + +// Help Emacs a little because this file doesn't end in .java. +// +// Local Variables: *** +// mode: java *** +// End: *** diff --git a/src/sun/misc/resources/Messages.java b/src/sun/misc/resources/Messages.java new file mode 100644 index 00000000..56f76bb2 --- /dev/null +++ b/src/sun/misc/resources/Messages.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERROR: Invalid version format used in {0} JAR file. Check the documentation for the supported version format." }, + { "optpkg.attributeerror", "ERROR: The required {0} JAR manifest attribute is not set in {1} JAR file." }, + { "optpkg.attributeserror", "ERROR: Some required JAR manifest attributes are not set in {0} JAR file." } + }; + +} diff --git a/src/sun/misc/resources/Messages_de.java b/src/sun/misc/resources/Messages_de.java new file mode 100644 index 00000000..e2961c52 --- /dev/null +++ b/src/sun/misc/resources/Messages_de.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_de extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERROR: In JAR-Datei {0} wurde ein ung\u00FCltiges Versionsformat verwendet. Pr\u00FCfen Sie in der Dokumentation, welches Versionsformat unterst\u00FCtzt wird." }, + { "optpkg.attributeerror", "ERROR: In JAR-Datei {1} ist das erforderliche JAR-Manifestattribut {0} nicht festgelegt." }, + { "optpkg.attributeserror", "ERROR: In JAR-Datei {0} sind einige erforderliche JAR-Manifestattribute nicht festgelegt." } + }; + +} diff --git a/src/sun/misc/resources/Messages_es.java b/src/sun/misc/resources/Messages_es.java new file mode 100644 index 00000000..9e762017 --- /dev/null +++ b/src/sun/misc/resources/Messages_es.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_es extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERROR: el formato del archivo JAR {0} pertenece a una versi\u00F3n no v\u00E1lida. Busque en la documentaci\u00F3n el formato de una versi\u00F3n soportada." }, + { "optpkg.attributeerror", "ERROR: el atributo obligatorio JAR manifest {0} no est\u00E1 definido en el archivo JAR {1}." }, + { "optpkg.attributeserror", "ERROR: algunos atributos obligatorios JAR manifest no est\u00E1n definidos en el archivo JAR {0}." } + }; + +} diff --git a/src/sun/misc/resources/Messages_fr.java b/src/sun/misc/resources/Messages_fr.java new file mode 100644 index 00000000..b742b26e --- /dev/null +++ b/src/sun/misc/resources/Messages_fr.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_fr extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERREUR\u00A0: le format de version utilis\u00E9 pour le fichier JAR {0} n''est pas valide. Pour conna\u00EEtre le format de version pris en charge, consultez la documentation." }, + { "optpkg.attributeerror", "ERREUR\u00A0: l''attribut manifest JAR {0} obligatoire n''est pas d\u00E9fini dans le fichier JAR {1}." }, + { "optpkg.attributeserror", "ERREUR\u00A0: certains attributs manifest JAR obligatoires ne sont pas d\u00E9finis dans le fichier JAR {0}." } + }; + +} diff --git a/src/sun/misc/resources/Messages_it.java b/src/sun/misc/resources/Messages_it.java new file mode 100644 index 00000000..ee7dd0a3 --- /dev/null +++ b/src/sun/misc/resources/Messages_it.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_it extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERRORE: Formato versione non valido nel file JAR {0}. Verificare nella documentazione il formato della versione supportato." }, + { "optpkg.attributeerror", "ERRORE: L''attributo manifest JAR {0} richiesto non \u00E8 impostato nel file JAR {1}." }, + { "optpkg.attributeserror", "ERRORE: Alcuni attributi manifesti JAR obbligatori non sono impostati nel file JAR {0}." } + }; + +} diff --git a/src/sun/misc/resources/Messages_ja.java b/src/sun/misc/resources/Messages_ja.java new file mode 100644 index 00000000..4f145518 --- /dev/null +++ b/src/sun/misc/resources/Messages_ja.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_ja extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "\u30A8\u30E9\u30FC: JAR\u30D5\u30A1\u30A4\u30EB{0}\u3067\u7121\u52B9\u306A\u30D0\u30FC\u30B8\u30E7\u30F3\u5F62\u5F0F\u304C\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u308B\u30D0\u30FC\u30B8\u30E7\u30F3\u5F62\u5F0F\u306B\u3064\u3044\u3066\u306E\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }, + { "optpkg.attributeerror", "\u30A8\u30E9\u30FC: \u5FC5\u8981\u306AJAR\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u5C5E\u6027{0}\u304CJAR\u30D5\u30A1\u30A4\u30EB{1}\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" }, + { "optpkg.attributeserror", "\u30A8\u30E9\u30FC: \u8907\u6570\u306E\u5FC5\u8981\u306AJAR\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u5C5E\u6027\u304CJAR\u30D5\u30A1\u30A4\u30EB{0}\u306B\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002" } + }; + +} diff --git a/src/sun/misc/resources/Messages_ko.java b/src/sun/misc/resources/Messages_ko.java new file mode 100644 index 00000000..53c32fc3 --- /dev/null +++ b/src/sun/misc/resources/Messages_ko.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_ko extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "\uC624\uB958: {0} JAR \uD30C\uC77C\uC5D0 \uBD80\uC801\uD569\uD55C \uBC84\uC804 \uD615\uC2DD\uC774 \uC0AC\uC6A9\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC124\uBA85\uC11C\uC5D0\uC11C \uC9C0\uC6D0\uB418\uB294 \uBC84\uC804 \uD615\uC2DD\uC744 \uD655\uC778\uD558\uC2ED\uC2DC\uC624." }, + { "optpkg.attributeerror", "\uC624\uB958: \uD544\uC694\uD55C {0} JAR manifest \uC18D\uC131\uC774 {1} JAR \uD30C\uC77C\uC5D0 \uC124\uC815\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." }, + { "optpkg.attributeserror", "\uC624\uB958: \uD544\uC694\uD55C \uC77C\uBD80 JAR manifest \uC18D\uC131\uC774 {0} JAR \uD30C\uC77C\uC5D0 \uC124\uC815\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." } + }; + +} diff --git a/src/sun/misc/resources/Messages_pt_BR.java b/src/sun/misc/resources/Messages_pt_BR.java new file mode 100644 index 00000000..4781700e --- /dev/null +++ b/src/sun/misc/resources/Messages_pt_BR.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_pt_BR extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "ERRO: formato de vers\u00E3o inv\u00E1lido usado no arquivo JAR {0}. Verifique a documenta\u00E7\u00E3o para obter o formato de vers\u00E3o suportado." }, + { "optpkg.attributeerror", "ERRO: o atributo de manifesto JAR {0} necess\u00E1rio n\u00E3o est\u00E1 definido no arquivo JAR {1}." }, + { "optpkg.attributeserror", "ERRO: alguns atributos de manifesto JAR necess\u00E1rios n\u00E3o est\u00E3o definidos no arquivo JAR {0}." } + }; + +} diff --git a/src/sun/misc/resources/Messages_sv.java b/src/sun/misc/resources/Messages_sv.java new file mode 100644 index 00000000..a813852c --- /dev/null +++ b/src/sun/misc/resources/Messages_sv.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_sv extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "FEL: Ogiltigt versionsformat i {0} JAR-fil. Kontrollera i dokumentationen vilket versionsformat som st\u00F6ds." }, + { "optpkg.attributeerror", "FEL: Obligatoriskt JAR manifest-attribut {0} \u00E4r inte inst\u00E4llt i {1} JAR-filen." }, + { "optpkg.attributeserror", "FEL: Vissa obligatoriska JAR manifest-attribut \u00E4r inte inst\u00E4llda i {0} JAR-filen." } + }; + +} diff --git a/src/sun/misc/resources/Messages_zh_CN.java b/src/sun/misc/resources/Messages_zh_CN.java new file mode 100644 index 00000000..d884f74a --- /dev/null +++ b/src/sun/misc/resources/Messages_zh_CN.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_zh_CN extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "\u9519\u8BEF: {0} JAR \u6587\u4EF6\u4E2D\u4F7F\u7528\u7684\u7248\u672C\u683C\u5F0F\u65E0\u6548\u3002\u8BF7\u68C0\u67E5\u6587\u6863\u4EE5\u4E86\u89E3\u652F\u6301\u7684\u7248\u672C\u683C\u5F0F\u3002" }, + { "optpkg.attributeerror", "\u9519\u8BEF: \u5FC5\u8981\u7684{0} JAR \u6E05\u5355\u5C5E\u6027\u672A\u5728{1} JAR \u6587\u4EF6\u4E2D\u8BBE\u7F6E\u3002" }, + { "optpkg.attributeserror", "\u9519\u8BEF: \u67D0\u4E9B\u5FC5\u8981\u7684 JAR \u6E05\u5355\u5C5E\u6027\u672A\u5728{0} JAR \u6587\u4EF6\u4E2D\u8BBE\u7F6E\u3002" } + }; + +} diff --git a/src/sun/misc/resources/Messages_zh_TW.java b/src/sun/misc/resources/Messages_zh_TW.java new file mode 100644 index 00000000..530a7cde --- /dev/null +++ b/src/sun/misc/resources/Messages_zh_TW.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc.resources; + +/** + *

    This class represents the ResourceBundle + * for sun.misc. + * + * @author Michael Colburn + */ + +public class Messages_zh_TW extends java.util.ListResourceBundle { + + /** + * Returns the contents of this ResourceBundle. + *

    + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } + + private static final Object[][] contents = { + { "optpkg.versionerror", "\u932F\u8AA4: {0} JAR \u6A94\u4F7F\u7528\u4E86\u7121\u6548\u7684\u7248\u672C\u683C\u5F0F\u3002\u8ACB\u6AA2\u67E5\u6587\u4EF6\uFF0C\u4EE5\u7372\u5F97\u652F\u63F4\u7684\u7248\u672C\u683C\u5F0F\u3002" }, + { "optpkg.attributeerror", "\u932F\u8AA4: {1} JAR \u6A94\u4E2D\u672A\u8A2D\u5B9A\u5FC5\u8981\u7684 {0} JAR \u8CC7\u8A0A\u6E05\u55AE\u5C6C\u6027\u3002" }, + { "optpkg.attributeserror", "\u932F\u8AA4: {0} JAR \u6A94\u4E2D\u672A\u8A2D\u5B9A\u67D0\u4E9B\u5FC5\u8981\u7684 JAR \u8CC7\u8A0A\u6E05\u55AE\u5C6C\u6027\u3002" } + }; + +} diff --git a/src/sun/net/ApplicationProxy.java b/src/sun/net/ApplicationProxy.java new file mode 100644 index 00000000..a369b666 --- /dev/null +++ b/src/sun/net/ApplicationProxy.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.net.Proxy; +import java.net.SocketAddress; + +/** + * Proxy wrapper class so that we can determine application set + * proxies by type. + */ +public final class ApplicationProxy extends Proxy { + private ApplicationProxy(Proxy proxy) { + super(proxy.type(), proxy.address()); + } + + public static ApplicationProxy create(Proxy proxy) { + return new ApplicationProxy(proxy); + } +} diff --git a/src/sun/net/ConnectionResetException.java b/src/sun/net/ConnectionResetException.java new file mode 100644 index 00000000..ccaf0c1b --- /dev/null +++ b/src/sun/net/ConnectionResetException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.net.SocketException; + +/** + * Thrown to indicate a connection reset. + * + * @since 1.4.1 + */ +public +class ConnectionResetException extends SocketException { + private static final long serialVersionUID = -7633185991801851556L; + + public ConnectionResetException(String msg) { + super(msg); + } + + public ConnectionResetException() { + } +} diff --git a/src/sun/net/ExtendedOptionsImpl.java b/src/sun/net/ExtendedOptionsImpl.java new file mode 100644 index 00000000..8fbcdd7b --- /dev/null +++ b/src/sun/net/ExtendedOptionsImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.net.*; +import jdk.net.*; +import java.io.IOException; +import java.io.FileDescriptor; +import java.security.PrivilegedAction; +import java.security.AccessController; +import java.lang.reflect.Field; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Collections; + +/** + * Contains the native implementation for extended socket options + * together with some other static utilities + */ +public class ExtendedOptionsImpl { + + static { + AccessController.doPrivileged((PrivilegedAction)() -> { + System.loadLibrary("net"); + return null; + }); + init(); + } + + private ExtendedOptionsImpl() {} + + public static void checkSetOptionPermission(SocketOption option) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return; + } + String check = "setOption." + option.name(); + sm.checkPermission(new NetworkPermission(check)); + } + + public static void checkGetOptionPermission(SocketOption option) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { + return; + } + String check = "getOption." + option.name(); + sm.checkPermission(new NetworkPermission(check)); + } + + public static void checkValueType(Object value, Class type) { + if (!type.isAssignableFrom(value.getClass())) { + String s = "Found: " + value.getClass().toString() + " Expected: " + + type.toString(); + throw new IllegalArgumentException(s); + } + } + + private static native void init(); + + /* + * Extension native implementations + * + * SO_FLOW_SLA + */ + public static native void setFlowOption(FileDescriptor fd, SocketFlow f); + public static native void getFlowOption(FileDescriptor fd, SocketFlow f); + public static native boolean flowSupported(); +} diff --git a/src/sun/net/InetAddressCachePolicy.java b/src/sun/net/InetAddressCachePolicy.java new file mode 100644 index 00000000..31578b50 --- /dev/null +++ b/src/sun/net/InetAddressCachePolicy.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.security.PrivilegedAction; +import java.security.Security; + +public final class InetAddressCachePolicy { + + // Controls the cache policy for successful lookups only + private static final String cachePolicyProp = "networkaddress.cache.ttl"; + private static final String cachePolicyPropFallback = + "sun.net.inetaddr.ttl"; + + // Controls the cache policy for negative lookups only + private static final String negativeCachePolicyProp = + "networkaddress.cache.negative.ttl"; + private static final String negativeCachePolicyPropFallback = + "sun.net.inetaddr.negative.ttl"; + + public static final int FOREVER = -1; + public static final int NEVER = 0; + + /* default value for positive lookups */ + public static final int DEFAULT_POSITIVE = 30; + + /* The Java-level namelookup cache policy for successful lookups: + * + * -1: caching forever + * any positive value: the number of seconds to cache an address for + * + * default value is forever (FOREVER), as we let the platform do the + * caching. For security reasons, this caching is made forever when + * a security manager is set. + */ + private static int cachePolicy = FOREVER; + + /* The Java-level namelookup cache policy for negative lookups: + * + * -1: caching forever + * any positive value: the number of seconds to cache an address for + * + * default value is 0. It can be set to some other value for + * performance reasons. + */ + private static int negativeCachePolicy = NEVER; + + /* + * Whether or not the cache policy for successful lookups was set + * using a property (cmd line). + */ + private static boolean propertySet; + + /* + * Whether or not the cache policy for negative lookups was set + * using a property (cmd line). + */ + private static boolean propertyNegativeSet; + + /* + * Initialize + */ + static { + + Integer tmp = java.security.AccessController.doPrivileged( + new PrivilegedAction() { + public Integer run() { + try { + String tmpString = Security.getProperty(cachePolicyProp); + if (tmpString != null) { + return Integer.valueOf(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + + try { + String tmpString = System.getProperty(cachePolicyPropFallback); + if (tmpString != null) { + return Integer.decode(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + return null; + } + }); + + if (tmp != null) { + cachePolicy = tmp.intValue(); + if (cachePolicy < 0) { + cachePolicy = FOREVER; + } + propertySet = true; + } else { + /* No properties defined for positive caching. If there is no + * security manager then use the default positive cache value. + */ + if (System.getSecurityManager() == null) { + cachePolicy = DEFAULT_POSITIVE; + } + } + tmp = java.security.AccessController.doPrivileged ( + new PrivilegedAction() { + public Integer run() { + try { + String tmpString = Security.getProperty(negativeCachePolicyProp); + if (tmpString != null) { + return Integer.valueOf(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + + try { + String tmpString = System.getProperty(negativeCachePolicyPropFallback); + if (tmpString != null) { + return Integer.decode(tmpString); + } + } catch (NumberFormatException ignored) { + // Ignore + } + return null; + } + }); + + if (tmp != null) { + negativeCachePolicy = tmp.intValue(); + if (negativeCachePolicy < 0) { + negativeCachePolicy = FOREVER; + } + propertyNegativeSet = true; + } + } + + public static synchronized int get() { + return cachePolicy; + } + + public static synchronized int getNegative() { + return negativeCachePolicy; + } + + /** + * Sets the cache policy for successful lookups if the user has not + * already specified a cache policy for it using a + * command-property. + * @param newPolicy the value in seconds for how long the lookup + * should be cached + */ + public static synchronized void setIfNotSet(int newPolicy) { + /* + * When setting the new value we may want to signal that the + * cache should be flushed, though this doesn't seem strictly + * necessary. + */ + if (!propertySet) { + checkValue(newPolicy, cachePolicy); + cachePolicy = newPolicy; + } + } + + /** + * Sets the cache policy for negative lookups if the user has not + * already specified a cache policy for it using a + * command-property. + * @param newPolicy the value in seconds for how long the lookup + * should be cached + */ + public static synchronized void setNegativeIfNotSet(int newPolicy) { + /* + * When setting the new value we may want to signal that the + * cache should be flushed, though this doesn't seem strictly + * necessary. + */ + if (!propertyNegativeSet) { + // Negative caching does not seem to have any security + // implications. + // checkValue(newPolicy, negativeCachePolicy); + negativeCachePolicy = newPolicy; + } + } + + private static void checkValue(int newPolicy, int oldPolicy) { + /* + * If malicious code gets a hold of this method, prevent + * setting the cache policy to something laxer or some + * invalid negative value. + */ + if (newPolicy == FOREVER) + return; + + if ((oldPolicy == FOREVER) || + (newPolicy < oldPolicy) || + (newPolicy < FOREVER)) { + + throw new + SecurityException("can't make InetAddress cache more lax"); + } + } +} diff --git a/src/sun/net/NetProperties.java b/src/sun/net/NetProperties.java new file mode 100644 index 00000000..24105bb2 --- /dev/null +++ b/src/sun/net/NetProperties.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.io.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Properties; + +/* + * This class allows for centralized access to Networking properties. + * Default values are loaded from the file jre/lib/net.properties + * + * + * @author Jean-Christophe Collet + * + */ + +public class NetProperties { + static private Properties props = new Properties(); + static { + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + loadDefaultProperties(); + return null; + }}); + } + + private NetProperties() { }; + + + /* + * Loads the default networking system properties + * the file is in jre/lib/net.properties + */ + static private void loadDefaultProperties() { + String fname = System.getProperty("java.home"); + if (fname == null) { + throw new Error("Can't find java.home ??"); + } + try { + File f = new File(fname, "lib"); + f = new File(f, "net.properties"); + fname = f.getCanonicalPath(); + InputStream in = new FileInputStream(fname); + BufferedInputStream bin = new BufferedInputStream(in); + props.load(bin); + bin.close(); + } catch (Exception e) { + // Do nothing. We couldn't find or access the file + // so we won't have default properties... + } + } + + /** + * Get a networking system property. If no system property was defined + * returns the default value, if it exists, otherwise returns + * null. + * @param key the property name. + * @throws SecurityException if a security manager exists and its + * checkPropertiesAccess method doesn't allow access + * to the system properties. + * @return the String value for the property, + * or null + */ + static public String get(String key) { + String def = props.getProperty(key); + try { + return System.getProperty(key, def); + } catch (IllegalArgumentException e) { + } catch (NullPointerException e) { + } + return null; + } + + /** + * Get an Integer networking system property. If no system property was + * defined returns the default value, if it exists, otherwise returns + * null. + * @param key the property name. + * @param defval the default value to use if the property is not found + * @throws SecurityException if a security manager exists and its + * checkPropertiesAccess method doesn't allow access + * to the system properties. + * @return the Integer value for the property, + * or null + */ + static public Integer getInteger(String key, int defval) { + String val = null; + + try { + val = System.getProperty(key, props.getProperty(key)); + } catch (IllegalArgumentException e) { + } catch (NullPointerException e) { + } + + if (val != null) { + try { + return Integer.decode(val); + } catch (NumberFormatException ex) { + } + } + return new Integer(defval); + } + + /** + * Get a Boolean networking system property. If no system property was + * defined returns the default value, if it exists, otherwise returns + * null. + * @param key the property name. + * @throws SecurityException if a security manager exists and its + * checkPropertiesAccess method doesn't allow access + * to the system properties. + * @return the Boolean value for the property, + * or null + */ + static public Boolean getBoolean(String key) { + String val = null; + + try { + val = System.getProperty(key, props.getProperty(key)); + } catch (IllegalArgumentException e) { + } catch (NullPointerException e) { + } + + if (val != null) { + try { + return Boolean.valueOf(val); + } catch (NumberFormatException ex) { + } + } + return null; + } + +} diff --git a/src/sun/net/NetworkClient.java b/src/sun/net/NetworkClient.java new file mode 100644 index 00000000..cf330fff --- /dev/null +++ b/src/sun/net/NetworkClient.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.io.*; +import java.net.Socket; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.net.Proxy; +import java.util.Arrays; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This is the base class for network clients. + * + * @author Jonathan Payne + */ +public class NetworkClient { + /* Default value of read timeout, if not specified (infinity) */ + public static final int DEFAULT_READ_TIMEOUT = -1; + + /* Default value of connect timeout, if not specified (infinity) */ + public static final int DEFAULT_CONNECT_TIMEOUT = -1; + + protected Proxy proxy = Proxy.NO_PROXY; + /** Socket for communicating with server. */ + protected Socket serverSocket = null; + + /** Stream for printing to the server. */ + public PrintStream serverOutput; + + /** Buffered stream for reading replies from server. */ + public InputStream serverInput; + + protected static int defaultSoTimeout; + protected static int defaultConnectTimeout; + + protected int readTimeout = DEFAULT_READ_TIMEOUT; + protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT; + /* Name of encoding to use for output */ + protected static String encoding; + + static { + final int vals[] = {0, 0}; + final String encs[] = { null }; + + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 0).intValue(); + vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 0).intValue(); + encs[0] = System.getProperty("file.encoding", "ISO8859_1"); + return null; + } + }); + if (vals[0] != 0) { + defaultSoTimeout = vals[0]; + } + if (vals[1] != 0) { + defaultConnectTimeout = vals[1]; + } + + encoding = encs[0]; + try { + if (!isASCIISuperset (encoding)) { + encoding = "ISO8859_1"; + } + } catch (Exception e) { + encoding = "ISO8859_1"; + } + } + + + /** + * Test the named character encoding to verify that it converts ASCII + * characters correctly. We have to use an ASCII based encoding, or else + * the NetworkClients will not work correctly in EBCDIC based systems. + * However, we cannot just use ASCII or ISO8859_1 universally, because in + * Asian locales, non-ASCII characters may be embedded in otherwise + * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398) + * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1] + * says that the HTTP request URI should be escaped using a defined + * mechanism, but there is no way to specify in the escaped string what + * the original character set is. It is not correct to assume that + * UTF-8 is always used (as in URLs in HTML 4.0). For this reason, + * until the specifications are updated to deal with this issue more + * comprehensively, and more importantly, HTTP servers are known to + * support these mechanisms, we will maintain the current behavior + * where it is possible to send non-ASCII characters in their original + * unescaped form. + */ + private static boolean isASCIISuperset (String encoding) throws Exception { + String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+ + "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,"; + + // Expected byte sequence for string above + byte[] chkB = { 48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72, + 73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99, + 100,101,102,103,104,105,106,107,108,109,110,111,112,113,114, + 115,116,117,118,119,120,121,122,45,95,46,33,126,42,39,40,41,59, + 47,63,58,64,38,61,43,36,44}; + + byte[] b = chkS.getBytes (encoding); + return Arrays.equals (b, chkB); + } + + /** Open a connection to the server. */ + public void openServer(String server, int port) + throws IOException, UnknownHostException { + if (serverSocket != null) + closeServer(); + serverSocket = doConnect (server, port); + try { + serverOutput = new PrintStream(new BufferedOutputStream( + serverSocket.getOutputStream()), + true, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding +"encoding not found", e); + } + serverInput = new BufferedInputStream(serverSocket.getInputStream()); + } + + /** + * Return a socket connected to the server, with any + * appropriate options pre-established + */ + protected Socket doConnect (String server, int port) + throws IOException, UnknownHostException { + Socket s; + if (proxy != null) { + if (proxy.type() == Proxy.Type.SOCKS) { + s = AccessController.doPrivileged( + new PrivilegedAction() { + public Socket run() { + return new Socket(proxy); + }}); + } else if (proxy.type() == Proxy.Type.DIRECT) { + s = createSocket(); + } else { + // Still connecting through a proxy + // server & port will be the proxy address and port + s = new Socket(Proxy.NO_PROXY); + } + } else + s = createSocket(); + // Instance specific timeouts do have priority, that means + // connectTimeout & readTimeout (-1 means not set) + // Then global default timeouts + // Then no timeout. + if (connectTimeout >= 0) { + s.connect(new InetSocketAddress(server, port), connectTimeout); + } else { + if (defaultConnectTimeout > 0) { + s.connect(new InetSocketAddress(server, port), defaultConnectTimeout); + } else { + s.connect(new InetSocketAddress(server, port)); + } + } + if (readTimeout >= 0) + s.setSoTimeout(readTimeout); + else if (defaultSoTimeout > 0) { + s.setSoTimeout(defaultSoTimeout); + } + return s; + } + + /** + * The following method, createSocket, is provided to allow the + * https client to override it so that it may use its socket factory + * to create the socket. + */ + protected Socket createSocket() throws IOException { + return new Socket(); + } + + protected InetAddress getLocalAddress() throws IOException { + if (serverSocket == null) + throw new IOException("not connected"); + return AccessController.doPrivileged( + new PrivilegedAction() { + public InetAddress run() { + return serverSocket.getLocalAddress(); + + } + }); + } + + /** Close an open connection to the server. */ + public void closeServer() throws IOException { + if (! serverIsOpen()) { + return; + } + serverSocket.close(); + serverSocket = null; + serverInput = null; + serverOutput = null; + } + + /** Return server connection status */ + public boolean serverIsOpen() { + return serverSocket != null; + } + + /** Create connection with host host on port port */ + public NetworkClient(String host, int port) throws IOException { + openServer(host, port); + } + + public NetworkClient() {} + + public void setConnectTimeout(int timeout) { + connectTimeout = timeout; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Sets the read timeout. + * + * Note: Public URLConnection (and protocol specific implementations) + * protect against negative timeout values being set. This implementation, + * and protocol specific implementations, use -1 to represent the default + * read timeout. + * + * This method may be invoked with the default timeout value when the + * protocol handler is trying to reset the timeout after doing a + * potentially blocking internal operation, e.g. cleaning up unread + * response data, buffering error stream response data, etc + */ + public void setReadTimeout(int timeout) { + if (timeout == DEFAULT_READ_TIMEOUT) + timeout = defaultSoTimeout; + + if (serverSocket != null && timeout >= 0) { + try { + serverSocket.setSoTimeout(timeout); + } catch(IOException e) { + // We tried... + } + } + readTimeout = timeout; + } + + public int getReadTimeout() { + return readTimeout; + } +} diff --git a/src/sun/net/NetworkServer.java b/src/sun/net/NetworkServer.java new file mode 100644 index 00000000..d4a9cc78 --- /dev/null +++ b/src/sun/net/NetworkServer.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.io.*; +import java.net.Socket; +import java.net.ServerSocket; + +/** + * This is the base class for network servers. To define a new type + * of server define a new subclass of NetworkServer with a serviceRequest + * method that services one request. Start the server by executing: + *

    + *      new MyServerClass().startServer(port);
    + * 
    + */ +public class NetworkServer implements Runnable, Cloneable { + /** Socket for communicating with client. */ + public Socket clientSocket = null; + private Thread serverInstance; + private ServerSocket serverSocket; + + /** Stream for printing to the client. */ + public PrintStream clientOutput; + + /** Buffered stream for reading replies from client. */ + public InputStream clientInput; + + /** Close an open connection to the client. */ + public void close() throws IOException { + clientSocket.close(); + clientSocket = null; + clientInput = null; + clientOutput = null; + } + + /** Return client connection status */ + public boolean clientIsOpen() { + return clientSocket != null; + } + + final public void run() { + if (serverSocket != null) { + Thread.currentThread().setPriority(Thread.MAX_PRIORITY); + // System.out.print("Server starts " + serverSocket + "\n"); + while (true) { + try { + Socket ns = serverSocket.accept(); +// System.out.print("New connection " + ns + "\n"); + NetworkServer n = (NetworkServer)clone(); + n.serverSocket = null; + n.clientSocket = ns; + new Thread(n).start(); + } catch(Exception e) { + System.out.print("Server failure\n"); + e.printStackTrace(); + try { + serverSocket.close(); + } catch(IOException e2) {} + System.out.print("cs="+serverSocket+"\n"); + break; + } + } +// close(); + } else { + try { + clientOutput = new PrintStream( + new BufferedOutputStream(clientSocket.getOutputStream()), + false, "ISO8859_1"); + clientInput = new BufferedInputStream(clientSocket.getInputStream()); + serviceRequest(); + // System.out.print("Service handler exits + // "+clientSocket+"\n"); + } catch(Exception e) { + // System.out.print("Service handler failure\n"); + // e.printStackTrace(); + } + try { + close(); + } catch(IOException e2) {} + } + } + + /** Start a server on port port. It will call serviceRequest() + for each new connection. */ + final public void startServer(int port) throws IOException { + serverSocket = new ServerSocket(port, 50); + serverInstance = new Thread(this); + serverInstance.start(); + } + + /** Service one request. It is invoked with the clientInput and + clientOutput streams initialized. This method handles one client + connection. When it is done, it can simply exit. The default + server just echoes it's input. It is invoked in it's own private + thread. */ + public void serviceRequest() throws IOException { + byte buf[] = new byte[300]; + int n; + clientOutput.print("Echo server " + getClass().getName() + "\n"); + clientOutput.flush(); + while ((n = clientInput.read(buf, 0, buf.length)) >= 0) { + clientOutput.write(buf, 0, n); + } + } + + public static void main(String argv[]) { + try { + new NetworkServer ().startServer(8888); + } catch (IOException e) { + System.out.print("Server failed: "+e+"\n"); + } + } + + /** + * Clone this object; + */ + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(e); + } + } + + public NetworkServer () { + } +} diff --git a/src/sun/net/ProgressEvent.java b/src/sun/net/ProgressEvent.java new file mode 100644 index 00000000..00ef84e5 --- /dev/null +++ b/src/sun/net/ProgressEvent.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.util.EventObject; +import java.net.URL; + +/** + * ProgressEvent represents an progress event in monitering network input stream. + * + * @author Stanley Man-Kit Ho + */ +@SuppressWarnings("serial") // never serialized +public class ProgressEvent extends EventObject { + // URL of the stream + private URL url; + // content type of the stream + private String contentType; + // method associated with URL + private String method; + // bytes read + private long progress; + // bytes expected + private long expected; + // the last thing to happen + private ProgressSource.State state; + + /** + * Construct a ProgressEvent object. + */ + public ProgressEvent(ProgressSource source, URL url, String method, String contentType, ProgressSource.State state, long progress, long expected) { + super(source); + this.url = url; + this.method = method; + this.contentType = contentType; + this.progress = progress; + this.expected = expected; + this.state = state; + } + + /** + * Return URL related to the progress. + */ + public URL getURL() + { + return url; + } + + /** + * Return method associated with URL. + */ + public String getMethod() + { + return method; + } + + /** + * Return content type of the URL. + */ + public String getContentType() + { + return contentType; + } + + /** + * Return current progress value. + */ + public long getProgress() + { + return progress; + } + + /** + * Return expected maximum progress value; -1 if expected is unknown. + */ + public long getExpected() { + return expected; + } + + /** + * Return state. + */ + public ProgressSource.State getState() { + return state; + } + + public String toString() { + return getClass().getName() + "[url=" + url + ", method=" + method + ", state=" + state + + ", content-type=" + contentType + ", progress=" + progress + ", expected=" + expected + "]"; + } +} diff --git a/src/sun/net/ProgressListener.java b/src/sun/net/ProgressListener.java new file mode 100644 index 00000000..140744bd --- /dev/null +++ b/src/sun/net/ProgressListener.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.util.EventListener; + +/** + * ProgressListener is an interface to be implemented by parties + * interested to be notified of progress in network input stream. + * + * @author Stanley Man-Kit Ho + */ +public interface ProgressListener extends EventListener +{ + /** + * Start progress. + */ + public void progressStart(ProgressEvent evt); + + /** + * Update progress. + */ + public void progressUpdate(ProgressEvent evt); + + /** + * Finish progress. + */ + public void progressFinish(ProgressEvent evt); +} diff --git a/src/sun/net/ProgressMeteringPolicy.java b/src/sun/net/ProgressMeteringPolicy.java new file mode 100644 index 00000000..f6e3ea94 --- /dev/null +++ b/src/sun/net/ProgressMeteringPolicy.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.net.URL; + +/** + * ProgressMeteringPolicy is an interface for determining progress metering policy. + * + * @author Stanley Man-Kit Ho + */ +public interface ProgressMeteringPolicy +{ + /** + * Return true if metering should be turned on for a particular network input stream. + */ + public boolean shouldMeterInput(URL url, String method); + + /** + * Return update notification threshold. + */ + public int getProgressUpdateThreshold(); +} diff --git a/src/sun/net/ProgressMonitor.java b/src/sun/net/ProgressMonitor.java new file mode 100644 index 00000000..e9ddce85 --- /dev/null +++ b/src/sun/net/ProgressMonitor.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.util.ArrayList; +import java.util.Iterator; +import java.net.URL; + +/** + * ProgressMonitor is a class for monitoring progress in network input stream. + * + * @author Stanley Man-Kit Ho + */ +public class ProgressMonitor +{ + /** + * Return default ProgressMonitor. + */ + public static synchronized ProgressMonitor getDefault() { + return pm; + } + + /** + * Change default ProgressMonitor implementation. + */ + public static synchronized void setDefault(ProgressMonitor m) { + if (m != null) + pm = m; + } + + /** + * Change progress metering policy. + */ + public static synchronized void setMeteringPolicy(ProgressMeteringPolicy policy) { + if (policy != null) + meteringPolicy = policy; + } + + + /** + * Return a snapshot of the ProgressSource list + */ + public ArrayList getProgressSources() { + ArrayList snapshot = new ArrayList(); + + try { + synchronized(progressSourceList) { + for (Iterator iter = progressSourceList.iterator(); iter.hasNext();) { + ProgressSource pi = iter.next(); + + // Clone ProgressSource and add to snapshot + snapshot.add((ProgressSource)pi.clone()); + } + } + } + catch(CloneNotSupportedException e) { + e.printStackTrace(); + } + + return snapshot; + } + + /** + * Return update notification threshold + */ + public synchronized int getProgressUpdateThreshold() { + return meteringPolicy.getProgressUpdateThreshold(); + } + + /** + * Return true if metering should be turned on + * for a particular URL input stream. + */ + public boolean shouldMeterInput(URL url, String method) { + return meteringPolicy.shouldMeterInput(url, method); + } + + /** + * Register progress source when progress is began. + */ + public void registerSource(ProgressSource pi) { + + synchronized(progressSourceList) { + if (progressSourceList.contains(pi)) + return; + + progressSourceList.add(pi); + } + + // Notify only if there is at least one listener + if (progressListenerList.size() > 0) + { + // Notify progress listener if there is progress change + ArrayList listeners = new ArrayList(); + + // Copy progress listeners to another list to avoid holding locks + synchronized(progressListenerList) { + for (Iterator iter = progressListenerList.iterator(); iter.hasNext();) { + listeners.add(iter.next()); + } + } + + // Fire event on each progress listener + for (Iterator iter = listeners.iterator(); iter.hasNext();) { + ProgressListener pl = iter.next(); + ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected()); + pl.progressStart(pe); + } + } + } + + /** + * Unregister progress source when progress is finished. + */ + public void unregisterSource(ProgressSource pi) { + + synchronized(progressSourceList) { + // Return if ProgressEvent does not exist + if (progressSourceList.contains(pi) == false) + return; + + // Close entry and remove from map + pi.close(); + progressSourceList.remove(pi); + } + + // Notify only if there is at least one listener + if (progressListenerList.size() > 0) + { + // Notify progress listener if there is progress change + ArrayList listeners = new ArrayList(); + + // Copy progress listeners to another list to avoid holding locks + synchronized(progressListenerList) { + for (Iterator iter = progressListenerList.iterator(); iter.hasNext();) { + listeners.add(iter.next()); + } + } + + // Fire event on each progress listener + for (Iterator iter = listeners.iterator(); iter.hasNext();) { + ProgressListener pl = iter.next(); + ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected()); + pl.progressFinish(pe); + } + } + } + + /** + * Progress source is updated. + */ + public void updateProgress(ProgressSource pi) { + + synchronized (progressSourceList) { + if (progressSourceList.contains(pi) == false) + return; + } + + // Notify only if there is at least one listener + if (progressListenerList.size() > 0) + { + // Notify progress listener if there is progress change + ArrayList listeners = new ArrayList(); + + // Copy progress listeners to another list to avoid holding locks + synchronized(progressListenerList) { + for (Iterator iter = progressListenerList.iterator(); iter.hasNext();) { + listeners.add(iter.next()); + } + } + + // Fire event on each progress listener + for (Iterator iter = listeners.iterator(); iter.hasNext();) { + ProgressListener pl = iter.next(); + ProgressEvent pe = new ProgressEvent(pi, pi.getURL(), pi.getMethod(), pi.getContentType(), pi.getState(), pi.getProgress(), pi.getExpected()); + pl.progressUpdate(pe); + } + } + } + + /** + * Add progress listener in progress monitor. + */ + public void addProgressListener(ProgressListener l) { + synchronized(progressListenerList) { + progressListenerList.add(l); + } + } + + /** + * Remove progress listener from progress monitor. + */ + public void removeProgressListener(ProgressListener l) { + synchronized(progressListenerList) { + progressListenerList.remove(l); + } + } + + // Metering policy + private static ProgressMeteringPolicy meteringPolicy = new DefaultProgressMeteringPolicy(); + + // Default implementation + private static ProgressMonitor pm = new ProgressMonitor(); + + // ArrayList for outstanding progress sources + private ArrayList progressSourceList = new ArrayList(); + + // ArrayList for progress listeners + private ArrayList progressListenerList = new ArrayList(); +} + + +/** + * Default progress metering policy. + */ +class DefaultProgressMeteringPolicy implements ProgressMeteringPolicy { + /** + * Return true if metering should be turned on for a particular network input stream. + */ + public boolean shouldMeterInput(URL url, String method) + { + // By default, no URL input stream is metered for + // performance reason. + return false; + } + + /** + * Return update notification threshold. + */ + public int getProgressUpdateThreshold() { + // 8K - same as default I/O buffer size + return 8192; + } +} diff --git a/src/sun/net/ProgressSource.java b/src/sun/net/ProgressSource.java new file mode 100644 index 00000000..9bbd1414 --- /dev/null +++ b/src/sun/net/ProgressSource.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.net.URL; + +/** + * ProgressSource represents the source of progress changes. + * + * @author Stanley Man-Kit Ho + */ +public class ProgressSource +{ + public enum State { NEW, CONNECTED, UPDATE, DELETE }; + + // URL + private URL url; + // URL method + private String method; + // Content type + private String contentType; + // bytes read + private long progress = 0; + // last bytes read + private long lastProgress = 0; + //bytes expected + private long expected = -1; + // the last thing to happen with this source + private State state; + // connect flag + private boolean connected = false; + // threshold for notification + private int threshold = 8192; + // progress monitor + private ProgressMonitor progressMonitor; + + /** + * Construct progress source object. + */ + public ProgressSource(URL url, String method) { + this(url, method, -1); + } + + /** + * Construct progress source object. + */ + public ProgressSource(URL url, String method, long expected) { + this.url = url; + this.method = method; + this.contentType = "content/unknown"; + this.progress = 0; + this.lastProgress = 0; + this.expected = expected; + this.state = State.NEW; + this.progressMonitor = ProgressMonitor.getDefault(); + this.threshold = progressMonitor.getProgressUpdateThreshold(); + } + + public boolean connected() { + if (!connected) { + connected = true; + state = State.CONNECTED; + return false; + } + return true; + } + + /** + * Close progress source. + */ + public void close() { + state = State.DELETE; + } + + /** + * Return URL of progress source. + */ + public URL getURL() { + return url; + } + + /** + * Return method of URL. + */ + public String getMethod() { + return method; + } + + /** + * Return content type of URL. + */ + public String getContentType() { + return contentType; + } + + // Change content type + public void setContentType(String ct) { + contentType = ct; + } + + /** + * Return current progress. + */ + public long getProgress() { + return progress; + } + + /** + * Return expected maximum progress; -1 if expected is unknown. + */ + public long getExpected() { + return expected; + } + + /** + * Return state. + */ + public State getState() { + return state; + } + + /** + * Begin progress tracking. + */ + public void beginTracking() { + progressMonitor.registerSource(this); + } + + /** + * Finish progress tracking. + */ + public void finishTracking() { + progressMonitor.unregisterSource(this); + } + + /** + * Update progress. + */ + public void updateProgress(long latestProgress, long expectedProgress) { + lastProgress = progress; + progress = latestProgress; + expected = expectedProgress; + + if (connected() == false) + state = State.CONNECTED; + else + state = State.UPDATE; + + // The threshold effectively divides the progress into + // different set of ranges: + // + // Range 0: 0..threshold-1, + // Range 1: threshold .. 2*threshold-1 + // .... + // Range n: n*threshold .. (n+1)*threshold-1 + // + // To determine which range the progress belongs to, it + // would be calculated as follow: + // + // range number = progress / threshold + // + // Notification should only be triggered when the current + // progress and the last progress are in different ranges, + // i.e. they have different range numbers. + // + // Using this range scheme, notification will be generated + // only once when the progress reaches each range. + // + if (lastProgress / threshold != progress / threshold) { + progressMonitor.updateProgress(this); + } + + // Detect read overrun + if (expected != -1) { + if (progress >= expected && progress != 0) + close(); + } + } + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public String toString() { + return getClass().getName() + "[url=" + url + ", method=" + method + ", state=" + state + + ", content-type=" + contentType + ", progress=" + progress + ", expected=" + expected + "]"; + } +} diff --git a/src/sun/net/RegisteredDomain.java b/src/sun/net/RegisteredDomain.java new file mode 100644 index 00000000..7e75b321 --- /dev/null +++ b/src/sun/net/RegisteredDomain.java @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/* + * The naming tables listed below were gathered from publicly available data such as + * the subdomain registration websites listed for each top-level domain by the Internet + * Assigned Numbers Authority and the website of the Internet Corporation for Assigned Names + * and Numbers as well as Wikipedia. + */ + +public class RegisteredDomain { + +// XX.AA +private static Set top1Set = new HashSet(Arrays.asList("asia", "biz", "cat", "coop", + "edu", "info", "gov", "jobs", "travel", "am", "aq", "ax", "cc", "cf", "cg", "ch", "cv", "cz", + "de", "dj", "dk", "fm", "fo", "ga", "gd", "gf", "gl", "gm", "gq", "gs", "gw", "hm", + "li", "lu", "md", "mh", "mil", "mobi", "mq", "ms", "ms", "ne", "nl", "nu", "si", + "sm", "sr", "su", "tc", "td", "tf", "tg", "tk", "tm", "tv", "va", "vg", + /* ae */ "xn--mgbaam7a8h", /* cn s */ "xn--fiqs8s", /* cn t */ "xn--fiqz9s", + /* eg */ "xn--wgbh1c", /* hk */ "xn--j6w193g", /* jo */ "xn--mgbayh7gpa", + /* lk */ "xn--fzc2c9e2c", /* ps */ "xn--ygbi2ammx", /* ru */ "xn--p1ai", + /* qa */ "xn--wgbl6a", /* sa */ "xn--mgberp4a5d4ar", /* sg */ "xn--yfro4i67o", + /* th */ "xn--o3cw4h", /* tn */ "xn--pgbs0dh", /* tw s */ "xn--kpry57d", + /* tw */ "xn--kprw13d", /* sg tamil */ "xn--clchc0ea0b2g2a9gcd")); + +// common pattern: XX.AA or XX.GOV.AA +private static Set top2Set = new HashSet(Arrays.asList("as", "bf", "cd", "cx", + "ie", "lt", "mr", "tl")); + +// common pattern: XX.AA or XX.COM.AA or XX.EDU.AA or XX.NET.AA or XX.ORG.AA or XX.GOV.AA +private static Set top4Set = new HashSet(Arrays.asList("af", "bm", "bs", "bt", + "bz", "dm", "ky", "lb", "lr", "mo", "sc", "sl", "ws")); + +// AA or less than 3 other XX.BB.AA possible matches +private static Set top3Set = new HashSet(Arrays.asList("ad", "aw", "be", "bw", + "cl", "fi", "int", "io", "mc")); + +// AA.UK exceptions +private static Set ukSet = new HashSet(Arrays.asList( "bl", "british-library", + "jet", "nhs", "nls", "parliament", "mod", "police")); + +// AA.AR exceptions +private static Set arSet = new HashSet(Arrays.asList( "argentina", "educ", + "gobiernoelectronico", "nic", "promocion", "retina", "uba")); + +// AA.OM exceptions +private static Set omSet = new HashSet(Arrays.asList("mediaphone", "nawrastelecom", + "nawras", "omanmobile", "omanpost", "omantel", "rakpetroleum", "siemens", "songfest", + "statecouncil", "shura", "peie", "omran", "omnic", "omanet", "oman", "muriya", "kom")); + +// any XX.BB.AA +private static Set top5Set = new HashSet(Arrays.asList("au", "arpa", "bd", "bn", "ck", + "cy", "er", "et", "fj", "fk", "gt", "gu", "il", "jm", "ke", "kh", "kw", + "mm", "mt", "mz", "ni", "np", "nz", "pg", "sb", "sv", "tz", "uy", "ve", "ye", + "za", "zm", "zw")); + +// XX.CC.BB.JP +private static Set jpSet = new HashSet(Arrays.asList("aichi", "akita", "aomori", + "chiba", "ehime", "fukui", "fukuoka", "fukushima", "gifu", "gunma", "hiroshima", "hokkaido", + "hyogo", "ibaraki", "ishikawa", "iwate", "kagawa", "kagoshima", "kanagawa", "kawasaki", + "kitakyushu", "kobe", "kochi", "kumamoto", "kyoto", "mie", "miyagi", "miyazaki", "nagano", + "nagasaki", "nagoya", "nara", "niigata", "oita", "okayama", "okinawa", "osaka", "saga", + "saitama", "sapporo", "sendai", "shiga", "shimane", "shizuoka", "tochigi", "tokushima", + "tokyo", "tottori", "toyama", "wakayama", "yamagata", "yamaguchi", "yamanashi", "yokohama")); + +// CC.BB.JP exceptions +private static Set jp2Set = new HashSet(Arrays.asList("metro.tokyo.jp", + "pref.aichi.jp", "pref.akita.jp", "pref.aomori.jp", "pref.chiba.jp", "pref.ehime.jp", + "pref.fukui.jp", "pref.fukuoka.jp", "pref.fukushima.jp", "pref.gifu.jp", "pref.gunma.jp", + "pref.hiroshima.jp", "pref.hokkaido.jp", "pref.hyogo.jp", "pref.ibaraki.jp", "pref.ishikawa.jp", + "pref.iwate.jp", "pref.kagawa.jp", "pref.kagoshima.jp", "pref.kanagawa.jp", "pref.kochi.jp", + "pref.kumamoto.jp", "pref.kyoto.jp", "pref.mie.jp", "pref.miyagi.jp", "pref.miyazaki.jp", + "pref.nagano.jp", "pref.nagasaki.jp", "pref.nara.jp", "pref.niigata.jp", "pref.oita.jp", + "pref.okayama.jp", "pref.okinawa.jp", "pref.osaka.jp", "pref.saga.jp", "pref.saitama.jp", + "pref.shiga.jp", "pref.shimane.jp", "pref.shizuoka.jp", "pref.tochigi.jp", "pref.tokushima.jp", + "pref.tottori.jp", "pref.toyama.jp", "pref.wakayama.jp", "pref.yamagata.jp", "pref.yamaguchi.jp", + "pref.yamanashi.jp", "city.chiba.jp", "city.fukuoka.jp", "city.hamamatsu.jp", "city.hiroshima.jp", "city.kawasaki.jp", + "city.kitakyushu.jp", "city.kobe.jp", "city.kyoto.jp", "city.nagoya.jp", "city.niigata.jp", + "city.okayama.jp", "city.osaka.jp", "city.sagamihara.jp", "city.saitama.jp", "city.sapporo.jp", "city.sendai.jp", + "city.shizuoka.jp", "city.yokohama.jp")); + +private static Set usStateSet = new HashSet(Arrays.asList("ak", + "al", "ar", "as", "az", "ca", "co", "ct", "dc", "de", "fl", "ga", "gu", "hi", "ia", + "id", "il", "in", "ks", "ky", "la", "ma", "md", "me", "mi", "mn", "mo", "ms", "mt", + "nc", "nd", "ne", "nh", "nj", "nm", "nv", "ny", "oh", "ok", "or", "pa", "pr", "ri", + "sc", "sd", "tn", "tx", "ut", "vi", "vt", "va", "wa", "wi", "wv", "wy")); + +private static Set usSubStateSet = new HashSet(Arrays.asList("state", + "lib", "k12", "cc", "tec", "gen", "cog", "mus", "dst")); + +private static Map> topMap = new HashMap<>(); +private static Map> top3Map = new HashMap<>(); + +static { + /* + * XX.AA or XX.BB.AA + */ + topMap.put("ac", new HashSet(Arrays.asList("com", "co", "edu", "gov", "net", "mil", "org"))); + topMap.put("ae", new HashSet(Arrays.asList("co", "net", "org", "sch", "ac", "gov", "mil"))); + topMap.put("aero", new HashSet(Arrays.asList("accident-investigation", + "accident-prevention", "aerobatic", "aeroclub", "aerodrome", "agents", "aircraft", + "airline", "airport", "air-surveillance", "airtraffic", "air-traffic-control", + "ambulance", "amusement", "association", "author", "ballooning", "broker", "caa", + "cargo", "catering", "certification", "championship", "charter", "civilaviation", + "club", "conference", "consultant", "consulting", "control", "council", "crew", + "design", "dgca", "educator", "emergency", "engine", "engineer", "entertainment", + "equipment", "exchange", "express", "federation", "flight", "freight", "fuel", + "gliding", "government", "groundhandling", "group", "hanggliding", "homebuilt", + "insurance", "journal", "journalist", "leasing", "logistics", "magazine", + "maintenance", "marketplace", "media", "microlight", "modelling", "navigation", + "parachuting", "paragliding", "passenger-association", "pilot", "press", "production", + "recreation", "repbody", "res", "research", "rotorcraft", "safety", "scientist", + "services", "show", "skydiving", "software", "student", "taxi", "trader", "trading", + "trainer", "union", "workinggroup", "works" ))); + topMap.put( "ag", new HashSet(Arrays.asList("com", "org", "net", "co", "nom"))); + topMap.put( "ai", new HashSet(Arrays.asList("off", "com", "net", "org"))); + topMap.put( "al", new HashSet(Arrays.asList("com", "edu", "gov", "mil", "net", "org"))); + topMap.put( "an", new HashSet(Arrays.asList("com"))); + topMap.put( "ao", new HashSet(Arrays.asList("ed", "gv", "og", "co", "pb", "it"))); + topMap.put( "at", new HashSet(Arrays.asList("ac", "co", "gv", "or", "biz", "info", "priv"))); + topMap.put( "az", new HashSet(Arrays.asList("com", "net", "int", "gov", "org", "edu", "info", + "pp", "mil", "name", "biz"))); + topMap.put( "ba", new HashSet(Arrays.asList("org", "net", "edu", "gov", "mil", "unbi", + "unmo", "unsa", "untz", "unze", "co", "com", "rs"))); + topMap.put( "bb", new HashSet(Arrays.asList("biz", "com", "edu", "gov", "info", "net", "org", + "store"))); + topMap.put( "bg", new HashSet(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", + "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", + "2", "3", "4", "5", "6", "7", "8", "9"))); + topMap.put( "bh", new HashSet(Arrays.asList("com", "info", "cc", "edu", "biz", "net", + "org", "gov"))); + topMap.put( "bi", new HashSet(Arrays.asList("co", "com", "edu", "gov", "info", "or", "org"))); + topMap.put( "bj", new HashSet(Arrays.asList("asso", "barreau", "com", "edu", "gouv", "gov", "mil"))); + topMap.put( "bo", new HashSet(Arrays.asList("com", "edu", "gov", "gob", "int", "org", "net", + "mil", "tv"))); + topMap.put( "br", new HashSet(Arrays.asList("adm", "adv", "agr", "am", "arq", "art", "ato", + "b", "bio", "blog", "bmd", "cim", "cng", "cnt", "com", "coop", "ecn", "edu", "emp", "eng", + "esp", "etc", "eti", "far", "flog", "fm", "fnd", "fot", "fst", "g12", "ggf", "gov", + "imb", "ind", "inf", "jor", "jus", "lel", "mat", "med", "mil", "mus", "net", "nom", + "not", "ntr", "odo", "org", "ppg", "pro", "psc", "psi", "qsl", "radio", "rec", "slg", + "srv", "taxi", "teo", "tmp", "trd", "tur", "tv", "vet", "vlog", "wiki", "zlg"))); + topMap.put( "bw", new HashSet(Arrays.asList("co", "gov", "org"))); + topMap.put( "by", new HashSet(Arrays.asList("gov", "mil", "com", "of"))); + topMap.put( "ca", new HashSet(Arrays.asList("ab", "bc", "mb", "nb", "nf", + "nl", "ns", "nt", "nu", "on", "pe", "qc", "sk", "yk", "gc"))); + topMap.put( "ci", new HashSet(Arrays.asList("org", "or", "com", "co", "edu", + "ed", "ac", "net", "go", "asso", "xn--aroport-bya", "int", + "presse", "md", "gouv"))); + topMap.put( "com", new HashSet(Arrays.asList("ad", "ar", "br", "cn", "de", "eu", "gb", + "gr", "hu", "jpn", "kr", "no", "qc", "ru", "sa", "se", "uk", "us", "uy", "za"))); + topMap.put( "cm", new HashSet(Arrays.asList("co", "com", "gov", "net"))); + topMap.put( "cn", new HashSet(Arrays.asList("ac", "com", "edu", "gov", "net", + "org", "mil", "xn--55qx5d", "xn--io0a7i", + "ah", "bj", "cq", "fj", "gd", "gs", "gz", "gx", + "ha", "hb", "he", "hi", "hl", "hn", "jl", "js", "jx", "ln", "nm", "nx", "qh", + "sc", "sd", "sh", "sn", "sx", "tj", "xj", "xz", "yn", "zj", "hk", "mo", "tw"))); + topMap.put( "co", new HashSet(Arrays.asList("arts", "com", "edu", "firm", "gov", "info", + "int", "mil", "net", "nom", "org", "rec", "web"))); + topMap.put( "cr", new HashSet(Arrays.asList("ac", "co", "ed", "fi", "go", "or", "sa"))); + topMap.put( "cu", new HashSet(Arrays.asList("com", "edu", "org", "net", "gov", "inf"))); + topMap.put( "do", new HashSet(Arrays.asList("com", "edu", "org", "net", "gov", "gob", + "web", "art", "sld", "mil"))); + topMap.put( "dz", new HashSet(Arrays.asList("com", "org", "net", "gov", "edu", "asso", + "pol", "art"))); + topMap.put( "ec", new HashSet(Arrays.asList("com", "info", "net", "fin", "k12", "med", + "pro", "org", "edu", "gov", "gob", "mil"))); + topMap.put( "ee", new HashSet(Arrays.asList("edu", "gov", "riik", "lib", "med", "com", + "pri", "aip", "org", "fie"))); + topMap.put( "eg", new HashSet(Arrays.asList("com", "edu", "eun", "gov", "mil", "name", + "net", "org", "sci"))); + topMap.put( "es", new HashSet(Arrays.asList("com", "nom", "org", "gob", "edu"))); + topMap.put( "eu", new HashSet(Arrays.asList("europa"))); + topMap.put( "fr", new HashSet(Arrays.asList("com", "asso", "nom", "prd", "presse", + "tm", "aeroport", "assedic", "avocat", "avoues", "cci", "chambagri", + "chirurgiens-dentistes", "experts-comptables", "geometre-expert", "gouv", "greta", + "huissier-justice", "medecin", "notaires", "pharmacien", "port", "veterinaire"))); + topMap.put( "ge", new HashSet(Arrays.asList("com", "edu", "gov", "org", "mil", "net", "pvt"))); + topMap.put( "gg", new HashSet(Arrays.asList("co", "org", "net", "sch", "gov"))); + topMap.put( "gh", new HashSet(Arrays.asList("com", "edu", "gov", "org", "mil"))); + topMap.put( "gi", new HashSet(Arrays.asList("com", "ltd", "gov", "mod", "edu", "org"))); + topMap.put( "gn", new HashSet(Arrays.asList("ac", "com", "edu", "gov", "org", "net"))); + topMap.put( "gp", new HashSet(Arrays.asList("com", "net", "mobi", "edu", "org", "asso"))); + topMap.put( "gr", new HashSet(Arrays.asList("com", "co", "net", "edu", "org", "gov", + "mil", "mod", "sch"))); + topMap.put( "gy", new HashSet(Arrays.asList("co", "com", "net", "org", "edu", "gov"))); + topMap.put( "hk", new HashSet(Arrays.asList("com", "edu", "gov", "idv", "net", "org", + /* com */ "xn--55qx5d", /* edu */ "xn--wcvs22d", /* gov */"xn--mxtq1m", + /* idv */ "xn--gmqw5a", /* net */ "xn--od0alg", /*org*/ "xn--uc0atv"))); + topMap.put( /* hk */ "xn--j6w193g", new HashSet(Arrays.asList( + /* com */ "xn--55qx5d", /* edu */ "xn--wcvs22d", /* gov */"xn--mxtq1m", + /* idv */ "xn--gmqw5a", /* net */ "xn--od0alg", /*org*/ "xn--uc0atv"))); + topMap.put( "hn", new HashSet(Arrays.asList("com", "edu", "org", "net", "mil", "gob"))); + topMap.put( "hr", new HashSet(Arrays.asList("iz.hr", "from.hr", "name.hr", "com.hr"))); + topMap.put( "ht", new HashSet(Arrays.asList("com", "shop", "firm", "info", "adult", + "net", "pro", "org", "med", "art", "coop", "pol", "asso", "edu", "rel", "gouv", "perso"))); + topMap.put( "hu", new HashSet(Arrays.asList("co", "info", "org", "priv", "sport", "tm", + "2000", "agrar", "bolt", "casino", "city", "erotica", "erotika", "film", "forum", + "games", "hotel", "ingatlan", "jogasz", "konyvelo", "lakas", "media", "news", "reklam", + "sex", "shop", "suli", "szex", "tozsde", "utazas", "video"))); + topMap.put( "id", new HashSet(Arrays.asList("ac", "co", "go", "mil", "net", "or", "sch", + "web"))); + topMap.put( "im", new HashSet(Arrays.asList("co.im", "com", "net.im", "gov.im", "org.im", + "ac.im"))); + topMap.put( "in", new HashSet(Arrays.asList("co", "firm", "ernet", "net", "org", "gen", "ind", + "nic", "ac", "edu", "res", "gov", "mil"))); + topMap.put( "iq", new HashSet(Arrays.asList("gov", "edu", "mil", "com", "org", "net" ))); + topMap.put( "ir", new HashSet(Arrays.asList("ac", "co", "gov", "id", "net", "org", "sch" + ))); + topMap.put( "is", new HashSet(Arrays.asList("net", "com", "edu", "gov", "org", "int"))); + topMap.put( "it", new HashSet(Arrays.asList("gov", "edu", "agrigento", "ag", "alessandria", + "al", "ancona", "an", "aosta", "aoste", "ao", "arezzo", "ar", "ascoli-piceno", + "ascolipiceno", "ap", "asti", "at", "avellino", "av", "bari", "ba", + "andria-barletta-trani", "andriabarlettatrani", "trani-barletta-andria", + "tranibarlettaandria", "barletta-trani-andria", "barlettatraniandria", + "andria-trani-barletta", "andriatranibarletta", "trani-andria-barletta", + "traniandriabarletta", "bt", "belluno", "bl", "benevento", "bn", "bergamo", "bg", + "biella", "bi", "bologna", "bo", "bolzano", "bozen", "balsan", "alto-adige", + "altoadige", "suedtirol", "bz", "brescia", "bs", "brindisi", "br", "cagliari", + "ca", "caltanissetta", "cl", "campobasso", "cb", "carboniaiglesias", "carbonia-iglesias", + "iglesias-carbonia", "iglesiascarbonia", "ci", "caserta", "ce", "catania", "ct", + "catanzaro", "cz", "chieti", "ch", "como", "co", "cosenza", "cs", "cremona", "cr", + "crotone", "kr", "cuneo", "cn", "dell-ogliastra", "dellogliastra", "ogliastra", "og", + "enna", "en", "ferrara", "fe", "fermo", "fm", "firenze", "florence", "fi", "foggia", + "fg", "forli-cesena", "forlicesena", "cesena-forli", "cesenaforli", "fc", "frosinone", + "fr", "genova", "genoa", "ge", "gorizia", "go", "grosseto", "gr", "imperia", "im", + "isernia", "is", "laquila", "aquila", "aq", "la-spezia", "laspezia", "sp", "latina", + "lt", "lecce", "le", "lecco", "lc", "livorno", "li", "lodi", "lo", "lucca", "lu", + "macerata", "mc", "mantova", "mn", "massa-carrara", "massacarrara", "carrara-massa", + "carraramassa", "ms", "matera", "mt", "medio-campidano", "mediocampidano", + "campidano-medio", "campidanomedio", "vs", "messina", "me", "milano", "milan", + "mi", "modena", "mo", "monza", "monza-brianza", "monzabrianza", "monzaebrianza", + "monzaedellabrianza", "monza-e-della-brianza", "mb", "napoli", "naples", "na", + "novara", "no", "nuoro", "nu", "oristano", "or", "padova", "padua", "pd", "palermo", + "pa", "parma", "pr", "pavia", "pv", "perugia", "pg", "pescara", "pe", "pesaro-urbino", + "pesarourbino", "urbino-pesaro", "urbinopesaro", "pu", "piacenza", "pc", "pisa", + "pi", "pistoia", "pt", "pordenone", "pn", "potenza", "pz", "prato", "po", "ragusa", + "rg", "ravenna", "ra", "reggio-calabria", "reggiocalabria", "rc", "reggio-emilia", + "reggioemilia", "re", "rieti", "ri", "rimini", "rn", "roma", "rome", "rm", "rovigo", + "ro", "salerno", "sa", "sassari", "ss", "savona", "sv", "siena", "si", "siracusa", + "sr", "sondrio", "so", "taranto", "ta", "tempio-olbia", "tempioolbia", "olbia-tempio", + "olbiatempio", "ot", "teramo", "te", "terni", "tr", "torino", "turin", "to", + "trapani", "tp", "trento", "trentino", "tn", "treviso", "tv", "trieste", "ts", + "udine", "ud", "varese", "va", "venezia", "venice", "ve", "verbania", "vb", + "vercelli", "vc", "verona", "vr", "vibo-valentia", "vibovalentia", "vv", "vicenza", + "vi", "viterbo", "vt"))); + topMap.put( "je", new HashSet(Arrays.asList("co", "org", "net", "sch", "gov"))); + topMap.put( "jo", new HashSet(Arrays.asList("com", "org", "net", "edu", "sch", + "gov", "mil", "name"))); + topMap.put( "jp", new HashSet(Arrays.asList("ac", "ad", "co", "ed", "go", "gr", "lg", + "ne", "or"))); + topMap.put( "kg", new HashSet(Arrays.asList("org", "net", "com", "edu", "gov", "mil"))); + topMap.put( "ki", new HashSet(Arrays.asList("edu", "biz", "net", "org", "gov", + "info", "com"))); + topMap.put( "km", new HashSet(Arrays.asList("org", "nom", "gov", "prd", "tm", "edu", + "mil", "ass", "com", "coop", "asso", "presse", "medecin", "notaires", "pharmaciens", + "veterinaire", "gouv"))); + topMap.put( "kn", new HashSet(Arrays.asList("net", "org", "edu", "gov"))); + topMap.put( "kp", new HashSet(Arrays.asList("com", "edu", "gov", "org", "rep", "tra"))); + topMap.put( "kr", new HashSet(Arrays.asList("ac", "co", "es", "go", "hs", "kg", "mil", + "ms", "ne", "or", "pe", "re", "sc", "busan", "chungbuk", "chungnam", "daegu", + "daejeon", "gangwon", "gwangju", "gyeongbuk", "gyeonggi", "gyeongnam", "incheon", + "jeju", "jeonbuk", "jeonnam", "seoul", "ulsan"))); + topMap.put( "kz", new HashSet(Arrays.asList("org", "edu", "net", "gov", "mil", "com"))); + topMap.put( "la", new HashSet(Arrays.asList("int", "net", "info", "edu", "gov", "per", + "com", "org", "c"))); + topMap.put( "lc", new HashSet(Arrays.asList("com", "net", "co", "org", "edu", "gov", + "l.lc", "p.lc"))); + topMap.put( "lk", new HashSet(Arrays.asList("gov", "sch", "net", "int", "com", "org", + "edu", "ngo", "soc", "web", "ltd", "assn", "grp", "hotel"))); + topMap.put( "ls", new HashSet(Arrays.asList("co", "gov", "ac", "org"))); + topMap.put( "lv", new HashSet(Arrays.asList("com", "edu", "gov", "org", "mil", + "id", "net", "asn", "conf"))); + topMap.put( "ly", new HashSet(Arrays.asList("com", "net", "gov", "plc", "edu", "sch", + "med", "org", "id"))); + topMap.put( "ma", new HashSet(Arrays.asList("co", "net", "gov", "org", "ac", "press"))); + topMap.put( "me", new HashSet(Arrays.asList("co", "net", "org", "edu", "ac", "gov", + "its", "priv"))); + topMap.put( "mg", new HashSet(Arrays.asList("org", "nom", "gov", "prd", "tm", + "edu", "mil", "com"))); + topMap.put( "mk", new HashSet(Arrays.asList("com", "org", "net", "edu", "gov", "inf", + "name", "pro"))); + topMap.put( "ml", new HashSet(Arrays.asList("com", "edu", "gouv", "gov", "net", + "org", "presse"))); + topMap.put( "mn", new HashSet(Arrays.asList("gov", "edu", "org"))); + topMap.put( "mp", new HashSet(Arrays.asList("gov", "co", "org"))); + topMap.put( "mu", new HashSet(Arrays.asList("com", "net", "org", "gov", "ac", + "co", "or"))); + topMap.put( "museum", new HashSet(Arrays.asList("academy", "agriculture", "air", + "airguard", "alabama", "alaska", "amber", "ambulance", "american", "americana", + "americanantiques", "americanart", "amsterdam", "and", "annefrank", "anthro", + "anthropology", "antiques", "aquarium", "arboretum", "archaeological", "archaeology", + "architecture", "art", "artanddesign", "artcenter", "artdeco", "arteducation", + "artgallery", "arts", "artsandcrafts", "asmatart", "assassination", "assisi", + "association", "astronomy", "atlanta", "austin", "australia", "automotive", "aviation", + "axis", "badajoz", "baghdad", "bahn", "bale", "baltimore", "barcelona", "baseball", + "basel", "baths", "bauern", "beauxarts", "beeldengeluid", "bellevue", "bergbau", + "berkeley", "berlin", "bern", "bible", "bilbao", "bill", "birdart", "birthplace", + "bonn", "boston", "botanical", "botanicalgarden", "botanicgarden", "botany", + "brandywinevalley", "brasil", "bristol", "british", "britishcolumbia", "broadcast", + "brunel", "brussel", "brussels", "bruxelles", "building", "burghof", "bus", "bushey", + "cadaques", "california", "cambridge", "can", "canada", "capebreton", "carrier", + "cartoonart", "casadelamoneda", "castle", "castres", "celtic", "center", "chattanooga", + "cheltenham", "chesapeakebay", "chicago", "children", "childrens", "childrensgarden", + "chiropractic", "chocolate", "christiansburg", "cincinnati", "cinema", "circus", + "civilisation", "civilization", "civilwar", "clinton", "clock", "coal", "coastaldefence", + "cody", "coldwar", "collection", "colonialwilliamsburg", "coloradoplateau", "columbia", + "columbus", "communication", "communications", "community", "computer", + "computerhistory", "xn--comunicaes-v6a2o", "contemporary", "contemporaryart", + "convent", "copenhagen", "corporation", "xn--correios-e-telecomunicaes-ghc29a", + "corvette", "costume", "countryestate", "county", "crafts", "cranbrook", "creation", + "cultural", "culturalcenter", "culture", "cyber", "cymru", "dali", "dallas", "database", + "ddr", "decorativearts", "delaware", "delmenhorst", "denmark", "depot", "design", + "detroit", "dinosaur", "discovery", "dolls", "donostia", "durham", "eastafrica", + "eastcoast", "education", "educational", "egyptian", "eisenbahn", "elburg", + "elvendrell", "embroidery", "encyclopedic", "england", "entomology", "environment", + "environmentalconservation", "epilepsy", "essex", "estate", "ethnology", "exeter", + "exhibition", "family", "farm", "farmequipment", "farmers", "farmstead", "field", + "figueres", "filatelia", "film", "fineart", "finearts", "finland", "flanders", "florida", + "force", "fortmissoula", "fortworth", "foundation", "francaise", "frankfurt", + "franziskaner", "freemasonry", "freiburg", "fribourg", "frog", "fundacio", "furniture", + "gallery", "garden", "gateway", "geelvinck", "gemological", "geology", "georgia", + "giessen", "glas", "glass", "gorge", "grandrapids", "graz", "guernsey", "halloffame", + "hamburg", "handson", "harvestcelebration", "hawaii", "health", "heimatunduhren", + "hellas", "helsinki", "hembygdsforbund", "heritage", "histoire", "historical", + "historicalsociety", "historichouses", "historisch", "historisches", "history", + "historyofscience", "horology", "house", "humanities", "illustration", "imageandsound", + "indian", "indiana", "indianapolis", "indianmarket", "intelligence", "interactive", + "iraq", "iron", "isleofman", "jamison", "jefferson", "jerusalem", "jewelry", + "jewish", "jewishart", "jfk", "journalism", "judaica", "judygarland", "juedisches", + "juif", "karate", "karikatur", "kids", "koebenhavn", "koeln", "kunst", "kunstsammlung", + "kunstunddesign", "labor", "labour", "lajolla", "lancashire", "landes", "lans", + "xn--lns-qla", "larsson", "lewismiller", "lincoln", "linz", "living", "livinghistory", + "localhistory", "london", "losangeles", "louvre", "loyalist", "lucerne", "luxembourg", + "luzern", "mad", "madrid", "mallorca", "manchester", "mansion", "mansions", "manx", + "marburg", "maritime", "maritimo", "maryland", "marylhurst", "media", "medical", + "medizinhistorisches", "meeres", "memorial", "mesaverde", "michigan", "midatlantic", + "military", "mill", "miners", "mining", "minnesota", "missile", "missoula", "modern", + "moma", "money", "monmouth", "monticello", "montreal", "moscow", "motorcycle", "muenchen", + "muenster", "mulhouse", "muncie", "museet", "museumcenter", "museumvereniging", "music", + "national", "nationalfirearms", "nationalheritage", "nativeamerican", "naturalhistory", + "naturalhistorymuseum", "naturalsciences", "nature", "naturhistorisches", + "natuurwetenschappen", "naumburg", "naval", "nebraska", "neues", "newhampshire", + "newjersey", "newmexico", "newport", "newspaper", "newyork", "niepce", "norfolk", + "north", "nrw", "nuernberg", "nuremberg", "nyc", "nyny", "oceanographic", + "oceanographique", "omaha", "online", "ontario", "openair", "oregon", "oregontrail", + "otago", "oxford", "pacific", "paderborn", "palace", "paleo", "palmsprings", "panama", + "paris", "pasadena", "pharmacy", "philadelphia", "philadelphiaarea", "philately", + "phoenix", "photography", "pilots", "pittsburgh", "planetarium", "plantation", + "plants", "plaza", "portal", "portland", "portlligat", "posts-and-telecommunications", + "preservation", "presidio", "press", "project", "public", "pubol", "quebec", + "railroad", "railway", "research", "resistance", "riodejaneiro", "rochester", "rockart", + "roma", "russia", "saintlouis", "salem", "salvadordali", "salzburg", "sandiego", + "sanfrancisco", "santabarbara", "santacruz", "santafe", "saskatchewan", "satx", + "savannahga", "schlesisches", "schoenbrunn", "schokoladen", "school", "schweiz", + "science", "scienceandhistory", "scienceandindustry", "sciencecenter", "sciencecenters", + "science-fiction", "sciencehistory", "sciences", "sciencesnaturelles", "scotland", + "seaport", "settlement", "settlers", "shell", "sherbrooke", "sibenik", "silk", "ski", + "skole", "society", "sologne", "soundandvision", "southcarolina", "southwest", "space", + "spy", "square", "stadt", "stalbans", "starnberg", "state", "stateofdelaware", + "station", "steam", "steiermark", "stjohn", "stockholm", "stpetersburg", "stuttgart", + "suisse", "surgeonshall", "surrey", "svizzera", "sweden", "sydney", "tank", "tcm", + "technology", "telekommunikation", "television", "texas", "textile", "theater", + "time", "timekeeping", "topology", "torino", "touch", "town", "transport", "tree", + "trolley", "trust", "trustee", "uhren", "ulm", "undersea", "university", "usa", + "usantiques", "usarts", "uscountryestate", "usculture", "usdecorativearts", "usgarden", + "ushistory", "ushuaia", "uslivinghistory", "utah", "uvic", "valley", "vantaa", + "versailles", "viking", "village", "virginia", "virtual", "virtuel", "vlaanderen", + "volkenkunde", "wales", "wallonie", "war", "washingtondc", "watchandclock", + "watch-and-clock", "western", "westfalen", "whaling", "wildlife", "williamsburg", + "windmill", "workshop", "york", "yorkshire", "yosemite", "youth", "zoological", + "zoology", "xn--9dbhblg6di", "xn--h1aegh"))); + topMap.put( "mv", new HashSet(Arrays.asList("aero", "biz", "com", "coop", "edu", "gov", + "info", "int", "mil", "museum", "name", "net", "org", "pro"))); + topMap.put( "mw", new HashSet(Arrays.asList("ac", "biz", "co", "com", "coop", "edu", + "gov", "int", "museum", "net", "org"))); + topMap.put( "mx", new HashSet(Arrays.asList("com", "org", "gob", "edu", "net"))); + topMap.put( "my", new HashSet(Arrays.asList("com", "net", "org", "gov", "edu", + "mil", "name", "sch"))); + topMap.put( "na", new HashSet(Arrays.asList("co", "com", "org", "edu", "edunet", "net", + "alt", "biz", "info"))); + topMap.put( "nc", new HashSet(Arrays.asList("asso", "nom"))); + topMap.put( "net", new HashSet(Arrays.asList("gb", "se", "uk", "za"))); + topMap.put( "ng", new HashSet(Arrays.asList("name", "sch", "mil", "mobi", "com", + "edu", "gov", "net", "org"))); + topMap.put( "nf", new HashSet(Arrays.asList("com", "net", "per", "rec", "web", + "arts", "firm", "info", "other", "store"))); + topMap.put( "no", new HashSet(Arrays.asList("fhs", "vgs", "fylkesbibl", "folkebibl", + "museum", "idrett", "priv", "mil", "stat", "dep", "kommune", "herad", "aa", + "ah", "bu", "fm", "hl", "hm", "jan-mayen", "mr", "nl", "nt", "of", "ol", "oslo", + "rl", "sf", "st", "svalbard", "tm", "tr", "va", "vf", "akrehamn", + "xn--krehamn-dxa", "algard", "xn--lgrd-poac", "arna", "brumunddal", + "bryne", "bronnoysund", "xn--brnnysund-m8ac", "drobak", + "xn--drbak-wua", "egersund", "fetsund", "floro", "xn--flor-jra", + "fredrikstad", "hokksund", "honefoss", "xn--hnefoss-q1a", + "jessheim", "jorpeland", "xn--jrpeland-54a", "kirkenes", "kopervik", + "krokstadelva", "langevag", "xn--langevg-jxa", "leirvik", "mjondalen", + "xn--mjndalen-64a", "mo-i-rana", "mosjoen", "xn--mosjen-eya", + "nesoddtangen", "orkanger", "osoyro", "xn--osyro-wua", + "raholt", "xn--rholt-mra", "sandnessjoen", "xn--sandnessjen-ogb", + "skedsmokorset", "slattum", "spjelkavik", "stathelle", "stavern", "stjordalshalsen", + "xn--stjrdalshalsen-sqb", "tananger", "tranby", "vossevangen", "tranby", + "vossevangen", "afjord", "xn--fjord-lra", "agdenes", "al", + "xn--l-1fa", "alesund", "xn--lesund-hua", + "alstahaug", "alta", "xn--lt-liac", "alaheadju", + "xn--laheadju-7ya", "alvdal", "amli", "xn--mli-tla", + "amot", "xn--mot-tla", "andebu", "andoy", "xn--andy-ira", + "andasuolo", "ardal", "xn--rdal-poa", "aremark", "arendal", + "xn--s-1fa", "aseral", "xn--seral-lra", + "asker", "askim", "askvoll", "askoy", "xn--asky-ira", + "asnes", "xn--snes-poa", "audnedaln", "aukra", "aure", "aurland", + "aurskog-holand", "xn--aurskog-hland-jnb", + "austevoll", "austrheim", "averoy", "xn--avery-yua", + "balestrand", "ballangen", "balat", "xn--blt-elab", + "balsfjord", "bahccavuotna", "xn--bhccavuotna-k7a", + "bamble", "bardu", "beardu", "beiarn", "bajddar", "xn--bjddar-pta", + "baidar", "xn--bidr-5nac", "berg", "bergen", "berlevag", "xn--berlevg-jxa", + "bearalvahki", "xn--bearalvhki-y4a", "bindal", "birkenes", "bjarkoy", + "xn--bjarky-fya", "bjerkreim", "bjugn", "bodo", "xn--bod-2na", + "badaddja", "xn--bdddj-mrabd", "budejju", "bokn", + "bremanger", "bronnoy", "xn--brnny-wuac", "bygland", + "bykle", "barum", "xn--brum-voa", "bievat", "xn--bievt-0qa", + "bomlo", "xn--bmlo-gra", "batsfjord", "xn--btsfjord-9za", "bahcavuotna", + "xn--bhcavuotna-s4a", "dovre", "drammen", "drangedal", "dyroy", + "xn--dyry-ira", "donna", "xn--dnna-gra", + "eid", "eidfjord", "eidsberg", "eidskog", "eidsvoll", "eigersund", "elverum", + "enebakk", "engerdal", "etne", "etnedal", "evenes", "evenassi", + "xn--eveni-0qa01ga", "evje-og-hornnes", "farsund", "fauske", + "fuossko", "fuoisku", "fedje", "fet", "finnoy", "xn--finny-yua", + "fitjar", "fjaler", "fjell", "flakstad", "flatanger", "flekkefjord", "flesberg", + "flora", "fla", "xn--fl-zia", "folldal", "forsand", "fosnes", "frei", + "frogn", "froland", "frosta", "frana", "xn--frna-woa", + "froya", "xn--frya-hra", "fusa", "fyresdal", "forde", + "xn--frde-gra", "gamvik", "gangaviika", "xn--ggaviika-8ya47h", + "gaular", "gausdal", "gildeskal", "xn--gildeskl-g0a", + "giske", "gjemnes", "gjerdrum", "gjerstad", "gjesdal", "gjovik", + "xn--gjvik-wua", "gloppen", "gol", "gran", "grane", "granvin", + "gratangen", "grimstad", "grong", "kraanghke", "xn--kranghke-b0a", + "grue", "gulen", "hadsel", "halden", "halsa", "hamar", "hamaroy", "habmer", + "xn--hbmer-xqa", "hapmir", "xn--hpmir-xqa", + "hammerfest", "hammarfeasta", "xn--hmmrfeasta-s4ac", + "haram", "hareid", "harstad", "hasvik", "aknoluokta", "xn--koluokta-7ya57h", + "hattfjelldal", "aarborte", "haugesund", "hemne", "hemnes", "hemsedal", + "hitra", "hjartdal", "hjelmeland", + "hobol", "xn--hobl-ira", "hof", "hol", "hole", "holmestrand", "holtalen", + "xn--holtlen-hxa", "hornindal", "horten", "hurdal", "hurum", "hvaler", + "hyllestad", "hagebostad", "xn--hgebostad-g3a", "hoyanger", + "xn--hyanger-q1a", "hoylandet", "xn--hylandet-54a", + "ha", "xn--h-2fa", "ibestad", "inderoy", "xn--indery-fya", + "iveland", "jevnaker", "jondal", "jolster", "xn--jlster-bya", + "karasjok", "karasjohka", "xn--krjohka-hwab49j", + "karlsoy", "galsa", "xn--gls-elac", "karmoy", + "xn--karmy-yua", "kautokeino", "guovdageaidnu", "klepp", "klabu", + "xn--klbu-woa", "kongsberg", "kongsvinger", "kragero", "xn--krager-gya", + "kristiansand", "kristiansund", "krodsherad", "xn--krdsherad-m8a", + "kvalsund", "rahkkeravju", "xn--rhkkervju-01af", + "kvam", "kvinesdal", "kvinnherad", "kviteseid", "kvitsoy", "xn--kvitsy-fya", + "kvafjord", "xn--kvfjord-nxa", "giehtavuoatna", "kvanangen", + "xn--kvnangen-k0a", "navuotna", "xn--nvuotna-hwa", + "kafjord", "xn--kfjord-iua", "gaivuotna", "xn--givuotna-8ya", + "larvik", "lavangen", "lavagis", "loabat", "xn--loabt-0qa", + "lebesby", "davvesiida", "leikanger", "leirfjord", "leka", "leksvik", "lenvik", + "leangaviika", "xn--leagaviika-52b", "lesja", "levanger", "lier", "lierne", + "lillehammer", "lillesand", "lindesnes", "lindas", "xn--linds-pra", + "lom", "loppa", "lahppi", "xn--lhppi-xqa", "lund", "lunner", "luroy", + "xn--lury-ira", "luster", "lyngdal", "lyngen", "ivgu", "lardal", "lerdal", + "xn--lrdal-sra", "lodingen", "xn--ldingen-q1a", "lorenskog", + "xn--lrenskog-54a", "loten", "xn--lten-gra", "malvik", + "masoy", "xn--msy-ula0h", "muosat", "xn--muost-0qa", + "mandal", "marker", "marnardal", "masfjorden", "meland", "meldal", "melhus", + "meloy", "xn--mely-ira", "meraker", "xn--merker-kua", "moareke", + "xn--moreke-jua", "midsund", "midtre-gauldal", "modalen", "modum", + "molde", "moskenes", "moss", "mosvik", "malselv", "xn--mlselv-iua", + "malatvuopmi", "xn--mlatvuopmi-s4a", "namdalseid", "aejrie", "namsos", + "namsskogan", "naamesjevuemie", "xn--nmesjevuemie-tcba", + "laakesvuemie", "nannestad", "narvik", "narviika", "naustdal", "nedre-eiker", + "nesna", "nesodden", "nesseby", "unjarga", "xn--unjrga-rta", "nesset", + "nissedal", "nittedal", "nord-aurdal", "nord-fron", "nord-odal", "norddal", + "nordkapp", "davvenjarga", "xn--davvenjrga-y4a", "nordre-land", + "nordreisa", "raisa", "xn--risa-5na", "nore-og-uvdal", "notodden", "naroy", + "xn--nry-yla5g", "notteroy", "xn--nttery-byae", + "odda", "oksnes", "xn--ksnes-uua", "oppdal", "oppegard", + "xn--oppegrd-ixa", "orkdal", "orland", "xn--rland-uua", + "orskog", "xn--rskog-uua", "orsta", "xn--rsta-fra", + "os.hedmark", "os.hordaland", "osen", "osteroy", "xn--ostery-fya", + "ostre-toten", "xn--stre-toten-zcb", "overhalla", "ovre-eiker", + "xn--vre-eiker-k8a", "oyer", "xn--yer-zna", + "oygarden", "xn--ygarden-p1a", "oystre-slidre", "xn--ystre-slidre-ujb", + "porsanger", "porsangu", "xn--porsgu-sta26f", "porsgrunn", + "radoy", "xn--rady-ira", "rakkestad", "rana", "ruovat", "randaberg", + "rauma", "rendalen", "rennebu", "rennesoy", "xn--rennesy-v1a", + "rindal", "ringebu", "ringerike", "ringsaker", "rissa", "risor", + "xn--risr-ira", "roan", "rollag", "rygge", "ralingen", "xn--rlingen-mxa", + "rodoy", "xn--rdy-0nab", "romskog", "xn--rmskog-bya", + "roros", "xn--rros-gra", "rost", "xn--rst-0na", + "royken", "xn--ryken-vua", "royrvik", "xn--ryrvik-bya", + "rade", "xn--rde-ula", "salangen", "siellak", "saltdal", "salat", + "xn--slt-elab", "xn--slat-5na", "samnanger", + "sandefjord", "sandnes", "sandoy", "xn--sandy-yua", "sarpsborg", + "sauda", "sauherad", "sel", "selbu", "selje", "seljord", "sigdal", "siljan", + "sirdal", "skaun", "skedsmo", "ski", "skien", "skiptvet", "skjervoy", + "xn--skjervy-v1a", "skierva", "xn--skierv-uta", + "skjak", "xn--skjk-soa", "skodje", "skanland", "xn--sknland-fxa", + "skanit", "xn--sknit-yqa", "smola", "xn--smla-hra", + "snillfjord", "snasa", "xn--snsa-roa", "snoasa", "snaase", + "xn--snase-nra", "sogndal", "sokndal", "sola", "solund", "songdalen", + "sortland", "spydeberg", "stange", "stavanger", "steigen", "steinkjer", + "stjordal", "xn--stjrdal-s1a", "stokke", "stor-elvdal", "stord", "stordal", + "storfjord", "omasvuotna", "strand", "stranda", "stryn", "sula", "suldal", + "sund", "sunndal", "surnadal", "sveio", "svelvik", "sykkylven", "sogne", + "xn--sgne-gra", "somna", "xn--smna-gra", "sondre-land", + "xn--sndre-land-0cb", "sor-aurdal", "xn--sr-aurdal-l8a", + "sor-fron", "xn--sr-fron-q1a", "sor-odal", "xn--sr-odal-q1a", + "sor-varanger", "xn--sr-varanger-ggb", "matta-varjjat", + "xn--mtta-vrjjat-k7af", "sorfold", "xn--srfold-bya", + "sorreisa", "xn--srreisa-q1a", "sorum", "xn--srum-gra", + "tana", "deatnu", "time", "tingvoll", "tinn", "tjeldsund", "dielddanuorri", + "tjome", "xn--tjme-hra", "tokke", "tolga", "torsken", "tranoy", + "xn--trany-yua", "tromso", "xn--troms-zua", "tromsa", "romsa", + "trondheim", "troandin", "trysil", "trana", "xn--trna-woa", + "trogstad", "xn--trgstad-r1a", "tvedestrand", "tydal", "tynset", + "tysfjord", "divtasvuodna", "divttasvuotna", "tysnes", "tysvar", + "xn--tysvr-vra", "tonsberg", "xn--tnsberg-q1a", + "ullensaker", "ullensvang", "ulvik", "utsira", "vadso", "xn--vads-jra", + "cahcesuolo", "xn--hcesuolo-7ya35b", "vaksdal", "valle", "vang", + "vanylven", "vardo", "xn--vard-jra", "varggat", "xn--vrggt-xqad", + "vefsn", "vaapste", "vega", "vegarshei", "xn--vegrshei-c0a", "vennesla", + "verdal", "verran", "vestby", "vestnes", "vestre-slidre", "vestre-toten", + "vestvagoy", "xn--vestvgy-ixa6o", "vevelstad", "vik", "vikna", + "vindafjord", "volda", "voss", "varoy", "xn--vry-yla5g", + "vagan", "xn--vgan-qoa", "voagat", "vagsoy", "xn--vgsy-qoa0j", + "vaga", "xn--vg-yiab"))); + + topMap.put( "nr", new HashSet(Arrays.asList("biz", "info", "gov", "edu", "org", + "net", "com", "co"))); + topMap.put( "pa", new HashSet(Arrays.asList("ac", "gob", "com", "org", + "sld", "edu", "net", "ing", "abo", "med", "nom"))); + topMap.put( "pe", new HashSet(Arrays.asList("edu", "gob", "nom", "mil", "org", "com", + "net", "sld"))); + topMap.put( "pf", new HashSet(Arrays.asList( "com"))); + topMap.put( "ph", new HashSet(Arrays.asList("com", "net", "org", "gov", "edu", "ngo", "mil"))); + topMap.put( "pk", new HashSet(Arrays.asList("com", "net", "edu", "org", "fam", "biz", + "web", "gov", "gob", "gok", "gon", "gop", "gos", "gog", "gkp", "info"))); + topMap.put( "pl", new HashSet(Arrays.asList("aid", "agro", "atm", "auto", "biz", "com", + "edu", "gmina", "gsm", "info", "mail", "miasta", "media", "mil", "net", "nieruchomosci", + "nom", "org", "pc", "powiat", "priv", "realestate", "rel", "sex", "shop", "sklep", + "sos", "szkola", "targi", "tm", "tourism", "travel", "turystyka", "art", + "gov", "ngo", "augustow", "babia-gora", "bedzin", "beskidy", + "bialowieza", "bialystok", "bielawa", "bieszczady", "boleslawiec", "bydgoszcz", + "bytom", "cieszyn", "czeladz", "czest", "dlugoleka", "elblag", "elk", "glogow", + "gniezno", "gorlice", "grajewo", "ilawa", "jaworzno", "jelenia-gora", "jgora", + "kalisz", "kazimierz-dolny", "karpacz", "kartuzy", "kaszuby", "katowice", "kepno", + "ketrzyn", "klodzko", "kobierzyce", "kolobrzeg", "konin", "konskowola", "kutno", + "lapy", "lebork", "legnica", "lezajsk", "limanowa", "lomza", "lowicz", "lubin", + "lukow", "malbork", "malopolska", "mazowsze", "mazury", "mielec", "mielno", "mragowo", + "naklo", "nowaruda", "nysa", "olawa", "olecko", "olkusz", "olsztyn", "opoczno", + "opole", "ostroda", "ostroleka", "ostrowiec", "ostrowwlkp", "pila", "pisz", "podhale", + "podlasie", "polkowice", "pomorze", "pomorskie", "prochowice", "pruszkow", "przeworsk", + "pulawy", "radom", "rawa-maz", "rybnik", "rzeszow", "sanok", "sejny", "siedlce", + "slask", "slupsk", "sosnowiec", "stalowa-wola", "skoczow", "starachowice", "stargard", + "suwalki", "swidnica", "swiebodzin", "swinoujscie", "szczecin", "szczytno", "tarnobrzeg", + "tgory", "turek", "tychy", "ustka", "walbrzych", "warmia", "warszawa", "waw", + "wegrow", "wielun", "wlocl", "wloclawek", "wodzislaw", "wolomin", "wroclaw", + "zachpomor", "zagan", "zarow", "zgora", "zgorzelec", "gda", "gdansk", + "krakow", "poznan", "wroc", "co", + "lodz", "lublin", "torun"))); + topMap.put( "pn", new HashSet(Arrays.asList("gov", "co", "org", "edu", "net"))); + topMap.put( "pr", new HashSet(Arrays.asList("com", "net", "org", "gov", "edu", "isla", + "pro", "biz", "info", "name", "est", "prof", "ac", "gobierno"))); + topMap.put( "pro", new HashSet(Arrays.asList("aca", "bar", "cpa", "jur", "law", + "med", "eng"))); + topMap.put( "ps", new HashSet(Arrays.asList("edu", "gov", "sec", "plo", "com", "org", "net"))); + topMap.put( "pt", new HashSet(Arrays.asList("net", "gov", "org", "edu", "int", "publ", + "com", "nome"))); + topMap.put( "pw", new HashSet(Arrays.asList("co", "ne", "or", "ed", "go", "belau"))); + topMap.put( "qa", new HashSet(Arrays.asList("com", "net", "org", "gov", "edu", "mil"))); + topMap.put( "re", new HashSet(Arrays.asList("com", "asso", "nom"))); + topMap.put( "ro", new HashSet(Arrays.asList("com", "org", "tm", "nt", "nom", "info", + "rec", "arts", "firm", "store", "www"))); + topMap.put( "rs", new HashSet(Arrays.asList("co", "org", "edu", "ac", "gov", "in"))); + topMap.put( "ru", new HashSet(Arrays.asList("ac", "com", "edu", "int", "net", "org", + "pp", "adygeya", "altai", "amur", "arkhangelsk", "astrakhan", "bashkiria", + "belgorod", "bir", "bryansk", "buryatia", "cap", "cbg", "chel", "chelyabinsk", "chita", + "chukotka", "dagestan", "e-burg", "grozny", "irkutsk", + "ivanovo", "izhevsk", "jar", "joshkar-ola", "kalmykia", "kaluga", "kamchatka", + "karelia", "kazan", "kchr", "kemerovo", "khabarovsk", "khakassia", "khv", "kirov", + "koenig", "komi", "kostroma", "krasnoyarsk", "kuban", "kurgan", "kursk", "lipetsk", + "magadan", "mari", "mari-el", "marine", "mordovia", "mosreg", "msk", "murmansk", + "nalchik", "nnov", "nov", "novosibirsk", "nsk", "omsk", "orenburg", "oryol", + "palana", "penza", "perm", "pskov", "ptz", "rnd", "ryazan", "sakhalin", "samara", + "saratov", "simbirsk", "smolensk", "spb", "stavropol", "stv", "surgut", "tambov", + "tatarstan", "tom", "tomsk", "tsaritsyn", "tsk", "tula", "tuva", "tver", "tyumen", + "udm", "udmurtia", "ulan-ude", "vladikavkaz", "vladimir", "vladivostok", "volgograd", + "vologda", "voronezh", "vrn", "vyatka", "yakutia", "yamal", "yaroslavl", + "yekaterinburg", "yuzhno-sakhalinsk", "amursk", "baikal", "cmw", "fareast", + "jamal", "kms", "k-uralsk", "kustanai", "kuzbass", "magnitka", "mytis", + "nakhodka", "nkz", "norilsk", "oskol", "pyatigorsk", "rubtsovsk", "snz", "syzran", + "vdonsk", "zgrad", "gov", "mil", "test"))); + topMap.put( "rw", new HashSet(Arrays.asList("gov", "net", "edu", "ac", "com", "co", + "int", "mil", "gouv"))); + topMap.put( "sa", new HashSet(Arrays.asList("com", "net", "org", "gov", "med", "pub", + "edu", "sch"))); + topMap.put( "sd", new HashSet(Arrays.asList("com", "net", "org", "edu", "med", "gov", + "info", "tv"))); + topMap.put( "se", new HashSet(Arrays.asList("a", "ac", "b", "bd", "brand", "c", "d", + "e", "f", "fh", "fhsk", "fhv", "g", "h", "i", "k", "komforb", "kommunalforbund", + "komvux", "l", "lanarb", "lanbib", "m", "n", "naturbruksgymn", "o", "org", "p", "parti", + "pp", "press", "r", "s", "sshn", "t", "tm", "u", "w", "x", "y", "z"))); + topMap.put( "sg", new HashSet(Arrays.asList("com", "net", "org", "gov", "edu", "per"))); + topMap.put( "sh", new HashSet(Arrays.asList("co", "com", "net", "org", "gov", "edu", "nom"))); + topMap.put( "sk", new HashSet(Arrays.asList("gov", "edu"))); + topMap.put( "sn", new HashSet(Arrays.asList("art", "com", "edu", "gouv", "org", "perso", + "univ"))); + topMap.put( "so", new HashSet(Arrays.asList("com", "net", "org"))); + topMap.put( "sr", new HashSet(Arrays.asList("co", "com", "consulado", "edu", "embaixada", + "gov", "mil", "net", "org", "principe", "saotome", "store"))); + topMap.put( "sy", new HashSet(Arrays.asList("edu", "gov", "net", "mil", "com", "org", "news"))); + topMap.put( "sz", new HashSet(Arrays.asList("co", "ac", "org"))); + topMap.put( "th", new HashSet(Arrays.asList("ac", "co", "go", "in", "mi", "net", "or"))); + topMap.put( "tj", new HashSet(Arrays.asList("ac", "biz", "co", "com", "edu", "go", "gov", + "int", "mil", "name", "net", "nic", "org", "test", "web"))); + topMap.put( "tn", new HashSet(Arrays.asList("com", "ens", "fin", "gov", "ind", "intl", + "nat", "net", "org", "info", "perso", "tourism", "edunet", "rnrt", "rns", "rnu", + "mincom", "agrinet", "defense", "turen"))); + topMap.put( "to", new HashSet(Arrays.asList("gov"))); + topMap.put( "tt", new HashSet(Arrays.asList("co", "com", "org", "net", "biz", "info", + "pro", "int", "coop", "jobs", "mobi", "travel", "museum", "aero", "name", "gov", + "edu", "cat", "tel", "mil"))); + topMap.put( "tw", new HashSet(Arrays.asList("edu", "gov", "mil", "com", "net", "org", + "idv", "game", "ebiz", "club", "xn--zf0ao64a", "xn--uc0atv", "xn--czrw28b"))); + topMap.put( "ua", new HashSet(Arrays.asList("com", "edu", "gov", "in", "net", "org", + "cherkassy", "chernigov", "chernovtsy", "ck", "cn", "crimea", "cv", "dn", + "dnepropetrovsk", "donetsk", "dp", "if", "ivano-frankivsk", "kh", "kharkov", + "kherson", "kiev", "kirovograd", "km", "kr", "ks", "lg", + "lugansk", "lutsk", "lviv", "mk", "nikolaev", "od", "odessa", "pl", "poltava", + "rovno", "rv", "sebastopol", "sumy", "te", "ternopil", "uzhgorod", "vinnica", "vn", + "zaporizhzhe", "zp", "zhitomir", "zt", "cr", "lt", "lv", "sb", "sm", "tr", + "co", "biz", "in", "ne", "pp", "uz", "dominic"))); + topMap.put( "ug", new HashSet(Arrays.asList("co", "ac", "sc", "go", "ne", "or", "org", "com"))); + topMap.put( "us", new HashSet(Arrays.asList("dni", "fed", "isa", "kids", "nsn", "kyschools"))); + topMap.put( "uz", new HashSet(Arrays.asList("co", "com", "org", "gov", "ac", "edu", "int", "pp", "net"))); + topMap.put( "vc", new HashSet(Arrays.asList("com", "net", "org", "gov"))); + topMap.put( "vi", new HashSet(Arrays.asList("co", "com", "k12", "net", "org"))); + topMap.put( "vn", new HashSet(Arrays.asList( "com", "net", "org", "edu", "gov", "int", + "ac", "biz", "info", "name", "pro", "health"))); + topMap.put( "vu", new HashSet(Arrays.asList("co", "com", "net", "org", "edu", "gov", "de"))); + topMap.put("org", new HashSet(Arrays.asList("ae", "za"))); + topMap.put("pro", new HashSet(Arrays.asList("aca", "bar", "cpa", "jur", "law", "med", "eng"))); + + top3Map.put("au", new HashSet(Arrays.asList("act.edu.au", "eq.edu.au", + "nsw.edu.au", "nt.edu.au", "qld.edu.au", "sa.edu.au", "tas.edu.au", "vic.edu.au", + "wa.edu.au", "act.gov.au", "nsw.gov.au", "nt.gov.au", "qld.gov.au", "sa.gov.au", + "tas.gov.au", "vic.gov.au", "wa.gov.au"))); + top3Map.put("im", new HashSet(Arrays.asList("ltd.co.im", "plc.co.im"))); + top3Map.put("no", new HashSet(Arrays.asList("gs.aa.no", "gs.ah.no", "gs.bu.no", + "gs.fm.no", "gs.hl.no", "gs.hm.no", "gs.jan-mayen.no", "gs.mr.no", "gs.nl.no", + "gs.nt.no", "gs.of.no", "gs.ol.no", "gs.oslo.no", "gs.rl.no", "gs.sf.no", + "gs.st.no", "gs.svalbard.no", "gs.tm.no", "gs.tr.no", "gs.va.no", "gs.vf.no", + "bo.telemark.no", "xn--b-5ga.telemark.no", "bo.nordland.no", + "xn--b-5ga.nordland.no", "heroy.more-og-romsdal.no", + "xn--hery-ira.xn--mre-og-romsdal-qqb.no", "heroy.nordland.no", + "xn--hery-ira.nordland.no", "nes.akershus.no", "nes.buskerud.no", + "os.hedmark.no", "os.hordaland.no", + "sande.more-og-romsdal.no", "sande.xn--mre-og-romsdal-qqb.no", + "sande.vestfold.no", "valer.ostfold.no", "xn--vler-qoa.xn--stfold-9xa.no", + "valer.hedmark.no", "xn--vler-qoa.hedmark.no"))); + top3Map.put("tr", new HashSet(Arrays.asList("gov.nc.tr"))); +} + + + /* + * Return the registered part of a qualified domain + * name or the original if no match is found. + */ + public static String getRegisteredDomain(String cname) { + int dot; + + /* + * If one dot or less than just return. + */ + dot = cname.lastIndexOf('.'); + if (dot == -1) + return cname; + if (dot == 0) + return ""; + if (dot == cname.length() - 1) { + cname = cname.substring(0, cname.length() -1); + dot = cname.lastIndexOf('.'); + if (dot == -1) + return cname; + if (dot == 0) + return ""; + } + if (dot == cname.length() - 1) + return ""; + + /* + * Break it up into seperate labels. + */ + int second = cname.lastIndexOf('.', dot - 1); + if (second == -1) + return cname; + if (second == 0) + return ""; + int third = cname.lastIndexOf('.', second - 1); + int fourth = -1; + if (third > 0) { + fourth = cname.lastIndexOf('.', third - 1); + } + int fifth = -1; + if (fourth > 0) { + fifth = cname.lastIndexOf('.', fourth - 1); + } + String s = cname.substring(dot + 1); + String s2 = cname.substring(second + 1, dot); + + /* + * Look for longest matches first. + * XX.PVT.K12.MA.US etc. + */ + if (fourth != -1 && s.equals("us") && usStateSet.contains(s2)) { + String s3 = cname.substring(third + 1, second); + String s4 = cname.substring(fourth + 1, third); + if (s3.equals("k12")) { + if (s2.equals("ma") && (s4.equals("chtr") || s4.equals("paroch"))) { + return cname.substring(fifth + 1); + } else if (s4.equals("pvt")) { + return cname.substring(fifth + 1); + } + } + } + + /* + * XX.K12.MA.US. + */ + String str = cname.substring(third + 1); + if (third != -1) { + Set set = top3Map.get(s); + if (set != null) { + if (set.contains(str)) { + return cname.substring(fourth + 1); + } + } else if (s.equals("us") && usStateSet.contains(s2)) { + // check for known third level labels + String s3 = cname.substring(third + 1, second); + if (usSubStateSet.contains(s3)) { + return fourth != -1? cname.substring(fourth + 1): cname; + } else { + return cname.substring(third + 1); + } + } else if (s.equals("uk")) { + if (s2.equals("sch")) { + return cname.substring(fourth + 1); + } + } else if (s.equals("jp")) { + if (jpSet.contains(s2)) { + if (jp2Set.contains(str)) { + return cname.substring(third + 1); + } + return cname.substring(fourth + 1); + } + } + } + + /* + * PREF.AKITA.JP etc. + */ + if (jp2Set.contains(str)) { + return cname.substring(third + 1); + } + + /* + * XX.MA.US. + */ + Set topSet = topMap.get(s); + if (topSet != null) { + if (topSet.contains(s2)) { + return cname.substring(third + 1); + } + if (!((s.equals("us") && usStateSet.contains(s2)) || (s.equals("jp") && jpSet.contains(s2)))) { + return cname.substring(second + 1); + } + } else if (top2Set.contains(s)) { + if (s2.equals("gov")) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (top3Set.contains(s)) { + if (s.equals("ad") && s2.equals("nom") || + s.equals("aw") && s2.equals("com") || + s.equals("be") && s2.equals("ac") || + s.equals("cl") && s2.equals("gov") || + s.equals("cl") && s2.equals("gob") || + s.equals("fi") && s2.equals("aland") || + s.equals("int") && s2.equals("eu") || + s.equals("io") && s2.equals("com") || + s.equals("mc") && s2.equals("tm") || + s.equals("mc") && s2.equals("asso") || + s.equals("vc") && s2.equals("com")) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (top4Set.contains(s)) { + if (s2.equals("com") || s2.equals("edu") || s2.equals("gov") || + s2.equals("net") || s2.equals("org")) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (top5Set.contains(s)) { + return cname.substring(third + 1); + } + + /* + * BB.AA exception cases. + */ + if (s.equals("tr")) { + if (!s2.equals("nic") && !s2.equals("tsk")) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (s.equals("uk")) { + if (!ukSet.contains(s2)) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (s.equals("ar")) { + if (!arSet.contains(s2)) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } else if (s.equals("om")) { + if (!omSet.contains(s2)) { + return cname.substring(third + 1); + } + return cname.substring(second + 1); + } + + /* + * XX.AA + */ + if (top1Set.contains(s)) { + return cname.substring(second + 1); + } + + /* + * Nothing matched so we can't shorten the string. + */ + return cname; + } +} diff --git a/src/sun/net/ResourceManager.java b/src/sun/net/ResourceManager.java new file mode 100644 index 00000000..068b8484 --- /dev/null +++ b/src/sun/net/ResourceManager.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.net.SocketException; +import java.util.concurrent.atomic.AtomicInteger; +import sun.security.action.GetPropertyAction; + +/** + * Manages count of total number of UDP sockets and ensures + * that exception is thrown if we try to create more than the + * configured limit. + * + * This functionality could be put in NetHooks some time in future. + */ + +public class ResourceManager { + + /* default maximum number of udp sockets per VM + * when a security manager is enabled. + * The default is 25 which is high enough to be useful + * but low enough to be well below the maximum number + * of port numbers actually available on all OSes + * when multiplied by the maximum feasible number of VM processes + * that could practically be spawned. + */ + + private static final int DEFAULT_MAX_SOCKETS = 25; + private static final int maxSockets; + private static final AtomicInteger numSockets; + + static { + String prop = java.security.AccessController.doPrivileged( + new GetPropertyAction("sun.net.maxDatagramSockets") + ); + int defmax = DEFAULT_MAX_SOCKETS; + try { + if (prop != null) { + defmax = Integer.parseInt(prop); + } + } catch (NumberFormatException e) {} + maxSockets = defmax; + numSockets = new AtomicInteger(0); + } + + public static void beforeUdpCreate() throws SocketException { + if (System.getSecurityManager() != null) { + if (numSockets.incrementAndGet() > maxSockets) { + numSockets.decrementAndGet(); + throw new SocketException("maximum number of DatagramSockets reached"); + } + } + } + + public static void afterUdpClose() { + if (System.getSecurityManager() != null) { + numSockets.decrementAndGet(); + } + } +} diff --git a/src/sun/net/SocksProxy.java b/src/sun/net/SocksProxy.java new file mode 100644 index 00000000..6efadcc0 --- /dev/null +++ b/src/sun/net/SocksProxy.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.net.Proxy; +import java.net.SocketAddress; + +/** + * Proxy wrapper class so we can determine the socks protocol version. + */ +public final class SocksProxy extends Proxy { + private final int version; + + private SocksProxy(SocketAddress addr, int version) { + super(Type.SOCKS, addr); + this.version = version; + } + + public static SocksProxy create(SocketAddress addr, int version) { + return new SocksProxy(addr, version); + } + + public int protocolVersion() { + return version; + } +} diff --git a/src/sun/net/TelnetInputStream.java b/src/sun/net/TelnetInputStream.java new file mode 100644 index 00000000..3b27edcb --- /dev/null +++ b/src/sun/net/TelnetInputStream.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1994, 1995, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.io.*; + +/** + * This class provides input and output streams for telnet clients. + * This class overrides read to do CRLF processing as specified in + * RFC 854. The class assumes it is running on a system where lines + * are terminated with a single newline character. + * + * This is the relevant section of RFC 824 regarding CRLF processing: + * + *
    + * The sequence "CR LF", as defined, will cause the NVT to be
    + * positioned at the left margin of the next print line (as would,
    + * for example, the sequence "LF CR").  However, many systems and
    + * terminals do not treat CR and LF independently, and will have to
    + * go to some effort to simulate their effect.  (For example, some
    + * terminals do not have a CR independent of the LF, but on such
    + * terminals it may be possible to simulate a CR by backspacing.)
    + * Therefore, the sequence "CR LF" must be treated as a single "new
    + * line" character and used whenever their combined action is
    + * intended; the sequence "CR NUL" must be used where a carriage
    + * return alone is actually desired; and the CR character must be
    + * avoided in other contexts.  This rule gives assurance to systems
    + * which must decide whether to perform a "new line" function or a
    + * multiple-backspace that the TELNET stream contains a character
    + * following a CR that will allow a rational decision.
    + *
    + *    Note that "CR LF" or "CR NUL" is required in both directions
    + *    (in the default ASCII mode), to preserve the symmetry of the
    + *    NVT model.  Even though it may be known in some situations
    + *    (e.g., with remote echo and suppress go ahead options in
    + *    effect) that characters are not being sent to an actual
    + *    printer, nonetheless, for the sake of consistency, the protocol
    + *    requires that a NUL be inserted following a CR not followed by
    + *    a LF in the data stream.  The converse of this is that a NUL
    + *    received in the data stream after a CR (in the absence of
    + *    options negotiations which explicitly specify otherwise) should
    + *    be stripped out prior to applying the NVT to local character
    + *    set mapping.
    + * 
    + * + * @author Jonathan Payne + */ + +public class TelnetInputStream extends FilterInputStream { + /** If stickyCRLF is true, then we're a machine, like an IBM PC, + where a Newline is a CR followed by LF. On UNIX, this is false + because Newline is represented with just a LF character. */ + boolean stickyCRLF = false; + boolean seenCR = false; + + public boolean binaryMode = false; + + public TelnetInputStream(InputStream fd, boolean binary) { + super(fd); + binaryMode = binary; + } + + public void setStickyCRLF(boolean on) { + stickyCRLF = on; + } + + public int read() throws IOException { + if (binaryMode) + return super.read(); + + int c; + + /* If last time we determined we saw a CRLF pair, and we're + not turning that into just a Newline (that is, we're + stickyCRLF), then return the LF part of that sticky + pair now. */ + + if (seenCR) { + seenCR = false; + return '\n'; + } + + if ((c = super.read()) == '\r') { /* CR */ + switch (c = super.read()) { + default: + case -1: /* this is an error */ + throw new TelnetProtocolException("misplaced CR in input"); + + case 0: /* NUL - treat CR as CR */ + return '\r'; + + case '\n': /* CRLF - treat as NL */ + if (stickyCRLF) { + seenCR = true; + return '\r'; + } else { + return '\n'; + } + } + } + return c; + } + + /** read into a byte array */ + public int read(byte bytes[]) throws IOException { + return read(bytes, 0, bytes.length); + } + + /** + * Read into a byte array at offset off for length length + * bytes. + */ + public int read(byte bytes[], int off, int length) throws IOException { + if (binaryMode) + return super.read(bytes, off, length); + + int c; + int offStart = off; + + while (--length >= 0) { + c = read(); + if (c == -1) + break; + bytes[off++] = (byte)c; + } + return (off > offStart) ? off - offStart : -1; + } +} diff --git a/src/sun/net/TelnetOutputStream.java b/src/sun/net/TelnetOutputStream.java new file mode 100644 index 00000000..74f6c9ca --- /dev/null +++ b/src/sun/net/TelnetOutputStream.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.io.*; + +/** + * This class provides input and output streams for telnet clients. + * This class overrides write to do CRLF processing as specified in + * RFC 854. The class assumes it is running on a system where lines + * are terminated with a single newline character. + * + * This is the relevant section of RFC 824 regarding CRLF processing: + * + *
    + * The sequence "CR LF", as defined, will cause the NVT to be
    + * positioned at the left margin of the next print line (as would,
    + * for example, the sequence "LF CR").  However, many systems and
    + * terminals do not treat CR and LF independently, and will have to
    + * go to some effort to simulate their effect.  (For example, some
    + * terminals do not have a CR independent of the LF, but on such
    + * terminals it may be possible to simulate a CR by backspacing.)
    + * Therefore, the sequence "CR LF" must be treated as a single "new
    + * line" character and used whenever their combined action is
    + * intended; the sequence "CR NUL" must be used where a carriage
    + * return alone is actually desired; and the CR character must be
    + * avoided in other contexts.  This rule gives assurance to systems
    + * which must decide whether to perform a "new line" function or a
    + * multiple-backspace that the TELNET stream contains a character
    + * following a CR that will allow a rational decision.
    + *
    + *    Note that "CR LF" or "CR NUL" is required in both directions
    + *    (in the default ASCII mode), to preserve the symmetry of the
    + *    NVT model.  Even though it may be known in some situations
    + *    (e.g., with remote echo and suppress go ahead options in
    + *    effect) that characters are not being sent to an actual
    + *    printer, nonetheless, for the sake of consistency, the protocol
    + *    requires that a NUL be inserted following a CR not followed by
    + *    a LF in the data stream.  The converse of this is that a NUL
    + *    received in the data stream after a CR (in the absence of
    + *    options negotiations which explicitly specify otherwise) should
    + *    be stripped out prior to applying the NVT to local character
    + *    set mapping.
    + * 
    + * + * @author Jonathan Payne + */ + +public class TelnetOutputStream extends BufferedOutputStream { + boolean stickyCRLF = false; + boolean seenCR = false; + + public boolean binaryMode = false; + + public TelnetOutputStream(OutputStream fd, boolean binary) { + super(fd); + binaryMode = binary; + } + + /** + * set the stickyCRLF flag. Tells whether the terminal considers CRLF as a single + * char. + * + * @param on the boolean to set the flag to. + */ + public void setStickyCRLF(boolean on) { + stickyCRLF = on; + } + + /** + * Writes the int to the stream and does CR LF processing if necessary. + */ + public void write(int c) throws IOException { + if (binaryMode) { + super.write(c); + return; + } + + if (seenCR) { + if (c != '\n') + super.write(0); + super.write(c); + if (c != '\r') + seenCR = false; + } else { // !seenCR + if (c == '\n') { + super.write('\r'); + super.write('\n'); + return; + } + if (c == '\r') { + if (stickyCRLF) + seenCR = true; + else { + super.write('\r'); + c = 0; + } + } + super.write(c); + } + } + + /** + * Write the bytes at offset off in buffer bytes for + * length bytes. + */ + public void write(byte bytes[], int off, int length) throws IOException { + if (binaryMode) { + super.write(bytes, off, length); + return; + } + + while (--length >= 0) { + write(bytes[off++]); + } + } +} diff --git a/src/sun/net/TelnetProtocolException.java b/src/sun/net/TelnetProtocolException.java new file mode 100644 index 00000000..80cfdc94 --- /dev/null +++ b/src/sun/net/TelnetProtocolException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.io.*; + +/** + * An unexpected result was received by the client when talking to the + * telnet server. + * + * @author Jonathan Payne + */ + +public class TelnetProtocolException extends IOException { + private static final long serialVersionUID = 8509127047257111343L; + + public TelnetProtocolException(String s) { + super(s); + } +} diff --git a/src/sun/net/TransferProtocolClient.java b/src/sun/net/TransferProtocolClient.java new file mode 100644 index 00000000..79f9e135 --- /dev/null +++ b/src/sun/net/TransferProtocolClient.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +import java.io.*; +import java.util.Vector; + +/** + * This class implements that basic intefaces of transfer protocols. + * It is used by subclasses implementing specific protocols. + * + * @author Jonathan Payne + * @see sun.net.ftp.FtpClient + * @see sun.net.nntp.NntpClient + */ + +public class TransferProtocolClient extends NetworkClient { + static final boolean debug = false; + + /** Array of strings (usually 1 entry) for the last reply + from the server. */ + protected Vector serverResponse = new Vector<>(1); + + /** code for last reply */ + protected int lastReplyCode; + + + /** + * Pulls the response from the server and returns the code as a + * number. Returns -1 on failure. + */ + public int readServerResponse() throws IOException { + StringBuffer replyBuf = new StringBuffer(32); + int c; + int continuingCode = -1; + int code; + String response; + + serverResponse.setSize(0); + while (true) { + while ((c = serverInput.read()) != -1) { + if (c == '\r') { + if ((c = serverInput.read()) != '\n') + replyBuf.append('\r'); + } + replyBuf.append((char)c); + if (c == '\n') + break; + } + response = replyBuf.toString(); + replyBuf.setLength(0); + if (debug) { + System.out.print(response); + } + + if (response.length() == 0) { + code = -1; + } else { + try { + code = Integer.parseInt(response.substring(0, 3)); + } catch (NumberFormatException e) { + code = -1; + } catch (StringIndexOutOfBoundsException e) { + /* this line doesn't contain a response code, so + we just completely ignore it */ + continue; + } + } + serverResponse.addElement(response); + if (continuingCode != -1) { + /* we've seen a XXX- sequence */ + if (code != continuingCode || + (response.length() >= 4 && response.charAt(3) == '-')) { + continue; + } else { + /* seen the end of code sequence */ + continuingCode = -1; + break; + } + } else if (response.length() >= 4 && response.charAt(3) == '-') { + continuingCode = code; + continue; + } else { + break; + } + } + + return lastReplyCode = code; + } + + /** Sends command cmd to the server. */ + public void sendServer(String cmd) { + serverOutput.print(cmd); + if (debug) { + System.out.print("Sending: " + cmd); + } + } + + /** converts the server response into a string. */ + public String getResponseString() { + return serverResponse.elementAt(0); + } + + /** Returns all server response strings. */ + public Vector getResponseStrings() { + return serverResponse; + } + + /** standard constructor to host host, port port. */ + public TransferProtocolClient(String host, int port) throws IOException { + super(host, port); + } + + /** creates an uninitialized instance of this class. */ + public TransferProtocolClient() {} +} diff --git a/src/sun/net/URLCanonicalizer.java b/src/sun/net/URLCanonicalizer.java new file mode 100644 index 00000000..dfa84bbd --- /dev/null +++ b/src/sun/net/URLCanonicalizer.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net; + +/** + * Helper class to map URL "abbreviations" to real URLs. + * The default implementation supports the following mappings: + * ftp.mumble.bar/... => ftp://ftp.mumble.bar/... + * gopher.mumble.bar/... => gopher://gopher.mumble.bar/... + * other.name.dom/... => http://other.name.dom/... + * /foo/... => file:/foo/... + * + * Full URLs (those including a protocol name) are passed through unchanged. + * + * Subclassers can override or extend this behavior to support different + * or additional canonicalization policies. + * + * @author Steve Byrne + */ + +public class URLCanonicalizer { + /** + * Creates the default canonicalizer instance. + */ + public URLCanonicalizer() { } + + /** + * Given a possibly abbreviated URL (missing a protocol name, typically), + * this method's job is to transform that URL into a canonical form, + * by including a protocol name and additional syntax, if necessary. + * + * For a correctly formed URL, this method should just return its argument. + */ + public String canonicalize(String simpleURL) { + String resultURL = simpleURL; + if (simpleURL.startsWith("ftp.")) { + resultURL = "ftp://" + simpleURL; + } else if (simpleURL.startsWith("gopher.")) { + resultURL = "gopher://" + simpleURL; + } else if (simpleURL.startsWith("/")) { + resultURL = "file:" + simpleURL; + } else if (!hasProtocolName(simpleURL)) { + if (isSimpleHostName(simpleURL)) { + simpleURL = "www." + simpleURL + ".com"; + } + resultURL = "http://" + simpleURL; + } + + return resultURL; + } + + /** + * Given a possibly abbreviated URL, this predicate function returns + * true if it appears that the URL contains a protocol name + */ + public boolean hasProtocolName(String url) { + int index = url.indexOf(':'); + if (index <= 0) { // treat ":foo" as not having a protocol spec + return false; + } + + for (int i = 0; i < index; i++) { + char c = url.charAt(i); + + // REMIND: this is a guess at legal characters in a protocol -- + // need to be verified + if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c == '-')) { + continue; + } + + // found an illegal character + return false; + } + + return true; + } + + /** + * Returns true if the URL is just a single name, no periods or + * slashes, false otherwise + **/ + protected boolean isSimpleHostName(String url) { + + for (int i = 0; i < url.length(); i++) { + char c = url.charAt(i); + + // REMIND: this is a guess at legal characters in a protocol -- + // need to be verified + if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') + || (c == '-')) { + continue; + } + + // found an illegal character + return false; + } + + return true; + } +} diff --git a/src/sun/net/dns/ResolverConfiguration.java b/src/sun/net/dns/ResolverConfiguration.java new file mode 100644 index 00000000..76c2c354 --- /dev/null +++ b/src/sun/net/dns/ResolverConfiguration.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.dns; + +import java.util.List; + +/** + * The configuration of the client resolver. + * + *

    A ResolverConfiguration is a singleton that represents the + * configuration of the client resolver. The ResolverConfiguration + * is opened by invoking the {@link #open() open} method. + * + * @since 1.4 + */ + +public abstract class ResolverConfiguration { + + private static final Object lock = new Object(); + + private static ResolverConfiguration provider; + + protected ResolverConfiguration() { } + + /** + * Opens the resolver configuration. + * + * @return the resolver configuration + */ + public static ResolverConfiguration open() { + synchronized (lock) { + if (provider == null) { + provider = new sun.net.dns.ResolverConfigurationImpl(); + } + return provider; + } + } + + /** + * Returns a list corresponding to the domain search path. The + * list is ordered by the search order used for host name lookup. + * Each element in the list returns a {@link String} + * containing a domain name or suffix. + * + * @return list of domain names + */ + public abstract List searchlist(); + + /** + * Returns a list of name servers used for host name lookup. + * Each element in the list returns a {@link String} + * containing the textual representation of the IP address of + * the name server. + * + * @return list of the name servers + */ + public abstract List nameservers(); + + + /** + * Options representing certain resolver variables of + * a {@link ResolverConfiguration}. + */ + public static abstract class Options { + + /** + * Returns the maximum number of attempts the resolver + * will connect to each name server before giving up + * and returning an error. + * + * @return the resolver attempts value or -1 is unknown + */ + public int attempts() { + return -1; + } + + /** + * Returns the basic retransmit timeout, in milliseconds, + * used by the resolver. The resolver will typically use + * an exponential backoff algorithm where the timeout is + * doubled for every retransmit attempt. The basic + * retransmit timeout, returned here, is the initial + * timeout for the exponential backoff algorithm. + * + * @return the basic retransmit timeout value or -1 + * if unknown + */ + public int retrans() { + return -1; + } + } + + /** + * Returns the {@link #Options} for the resolver. + * + * @return options for the resolver + */ + public abstract Options options(); +} diff --git a/src/sun/net/ftp/FtpClient.java b/src/sun/net/ftp/FtpClient.java new file mode 100644 index 00000000..b66b4f3d --- /dev/null +++ b/src/sun/net/ftp/FtpClient.java @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp; + +import java.net.*; +import java.io.*; +import java.util.Date; +import java.util.List; +import java.util.Iterator; + +/** + * A class that implements the FTP protocol according to + * RFCs 959, + * 2228, + * 2389, + * 2428, + * 3659, + * 4217. + * Which includes support for FTP over SSL/TLS (aka ftps). + * + * {@code FtpClient} provides all the functionalities of a typical FTP + * client, like storing or retrieving files, listing or creating directories. + * A typical usage would consist of connecting the client to the server, + * log in, issue a few commands then logout. + * Here is a code example: + *

    + * FtpClient cl = FtpClient.create();
    + * cl.connect("ftp.gnu.org").login("anonymous", "john.doe@mydomain.com".toCharArray())).changeDirectory("pub/gnu");
    + * Iterator<FtpDirEntry> dir = cl.listFiles();
    + *     while (dir.hasNext()) {
    + *         FtpDirEntry f = dir.next();
    + *         System.err.println(f.getName());
    + *     }
    + *     cl.close();
    + * }
    + * 
    + *

    Error reporting: There are, mostly, two families of errors that + * can occur during an FTP session. The first kind are the network related issues + * like a connection reset, and they are usually fatal to the session, meaning, + * in all likelyhood the connection to the server has been lost and the session + * should be restarted from scratch. These errors are reported by throwing an + * {@link IOException}. The second kind are the errors reported by the FTP server, + * like when trying to download a non-existing file for example. These errors + * are usually non fatal to the session, meaning more commands can be sent to the + * server. In these cases, a {@link FtpProtocolException} is thrown.

    + *

    + * It should be noted that this is not a thread-safe API, as it wouldn't make + * too much sense, due to the very sequential nature of FTP, to provide a + * client able to be manipulated from multiple threads. + * + * @since 1.7 + */ +public abstract class FtpClient implements Closeable { + + private static final int FTP_PORT = 21; + + public static enum TransferType { + + ASCII, BINARY, EBCDIC + }; + + /** + * Returns the default FTP port number. + * + * @return the port number. + */ + public static final int defaultPort() { + return FTP_PORT; + } + + /** + * Creates an instance of FtpClient. The client is not connected to any + * server yet. + * + */ + protected FtpClient() { + } + + /** + * Creates an instance of {@code FtpClient}. The client is not connected to any + * server yet. + * + * @return the created {@code FtpClient} + */ + public static FtpClient create() { + FtpClientProvider provider = FtpClientProvider.provider(); + return provider.createFtpClient(); + } + + /** + * Creates an instance of FtpClient and connects it to the specified + * address. + * + * @param dest the {@code InetSocketAddress} to connect to. + * @return The created {@code FtpClient} + * @throws IOException if the connection fails + * @see #connect(SocketAddress) + */ + public static FtpClient create(InetSocketAddress dest) throws FtpProtocolException, IOException { + FtpClient client = create(); + if (dest != null) { + client.connect(dest); + } + return client; + } + + /** + * Creates an instance of {@code FtpClient} and connects it to the + * specified host on the default FTP port. + * + * @param dest the {@code String} containing the name of the host + * to connect to. + * @return The created {@code FtpClient} + * @throws IOException if the connection fails. + * @throws FtpProtocolException if the server rejected the connection + */ + public static FtpClient create(String dest) throws FtpProtocolException, IOException { + return create(new InetSocketAddress(dest, FTP_PORT)); + } + + /** + * Enables, or disables, the use of the passive mode. In that mode, + * data connections are established by having the client connect to the server. + * This is the recommended default mode as it will work best through + * firewalls and NATs. If set to {@code false} the mode is said to be + * active which means the server will connect back to the client + * after a PORT command to establish a data connection. + * + *

    Note: Since the passive mode might not be supported by all + * FTP servers, enabling it means the client will try to use it. If the + * server rejects it, then the client will attempt to fall back to using + * the active mode by issuing a {@code PORT} command instead.

    + * + * @param passive {@code true} to force passive mode. + * @return This FtpClient + * @see #isPassiveModeEnabled() + */ + public abstract FtpClient enablePassiveMode(boolean passive); + + /** + * Tests whether passive mode is enabled. + * + * @return {@code true} if the passive mode has been enabled. + * @see #enablePassiveMode(boolean) + */ + public abstract boolean isPassiveModeEnabled(); + + /** + * Sets the default timeout value to use when connecting to the server, + * + * @param timeout the timeout value, in milliseconds, to use for the connect + * operation. A value of zero or less, means use the default timeout. + * + * @return This FtpClient + */ + public abstract FtpClient setConnectTimeout(int timeout); + + /** + * Returns the current default connection timeout value. + * + * @return the value, in milliseconds, of the current connect timeout. + * @see #setConnectTimeout(int) + */ + public abstract int getConnectTimeout(); + + /** + * Sets the timeout value to use when reading from the server, + * + * @param timeout the timeout value, in milliseconds, to use for the read + * operation. A value of zero or less, means use the default timeout. + * @return This FtpClient + */ + public abstract FtpClient setReadTimeout(int timeout); + + /** + * Returns the current read timeout value. + * + * @return the value, in milliseconds, of the current read timeout. + * @see #setReadTimeout(int) + */ + public abstract int getReadTimeout(); + + /** + * Set the {@code Proxy} to be used for the next connection. + * If the client is already connected, it doesn't affect the current + * connection. However it is not recommended to change this during a session. + * + * @param p the {@code Proxy} to use, or {@code null} for no proxy. + * @return This FtpClient + */ + public abstract FtpClient setProxy(Proxy p); + + /** + * Get the proxy of this FtpClient + * + * @return the {@code Proxy}, this client is using, or {@code null} + * if none is used. + * @see #setProxy(Proxy) + */ + public abstract Proxy getProxy(); + + /** + * Tests whether this client is connected or not to a server. + * + * @return {@code true} if the client is connected. + */ + public abstract boolean isConnected(); + + /** + * Connects the {@code FtpClient} to the specified destination server. + * + * @param dest the address of the destination server + * @return this FtpClient + * @throws IOException if connection failed. + * @throws SecurityException if there is a SecurityManager installed and it + * denied the authorization to connect to the destination. + * @throws FtpProtocolException + */ + public abstract FtpClient connect(SocketAddress dest) throws FtpProtocolException, IOException; + + /** + * Connects the FtpClient to the specified destination server. + * + * @param dest the address of the destination server + * @param timeout the value, in milliseconds, to use as a connection timeout + * @return this FtpClient + * @throws IOException if connection failed. + * @throws SecurityException if there is a SecurityManager installed and it + * denied the authorization to connect to the destination. + * @throws FtpProtocolException + */ + public abstract FtpClient connect(SocketAddress dest, int timeout) throws FtpProtocolException, IOException; + + /** + * Retrieves the address of the FTP server this client is connected to. + * + * @return the {@link SocketAddress} of the server, or {@code null} if this + * client is not connected yet. + */ + public abstract SocketAddress getServerAddress(); + + /** + * Attempts to log on the server with the specified user name and password. + * + * @param user The user name + * @param password The password for that user + * @return this FtpClient + * @throws IOException if an error occurred during the transmission + * @throws FtpProtocolException if the login was refused by the server + */ + public abstract FtpClient login(String user, char[] password) throws FtpProtocolException, IOException; + + /** + * Attempts to log on the server with the specified user name, password and + * account name. + * + * @param user The user name + * @param password The password for that user. + * @param account The account name for that user. + * @return this FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the login was refused by the server + */ + public abstract FtpClient login(String user, char[] password, String account) throws FtpProtocolException, IOException; + + /** + * Closes the current connection. Logs out the current user, if any, by + * issuing the QUIT command to the server. + * This is in effect terminates the current + * session and the connection to the server will be closed. + *

    After a close, the client can then be connected to another server + * to start an entirely different session.

    + * + * @throws IOException if an error occurs during transmission + */ + public abstract void close() throws IOException; + + /** + * Checks whether the client is logged in to the server or not. + * + * @return {@code true} if the client has already completed a login. + */ + public abstract boolean isLoggedIn(); + + /** + * Changes to a specific directory on a remote FTP server + * + * @param remoteDirectory path of the directory to CD to. + * @return this FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was refused by the server + */ + public abstract FtpClient changeDirectory(String remoteDirectory) throws FtpProtocolException, IOException; + + /** + * Changes to the parent directory, sending the CDUP command to the server. + * + * @return this FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was refused by the server + */ + public abstract FtpClient changeToParentDirectory() throws FtpProtocolException, IOException; + + /** + * Retrieve the server current working directory using the PWD command. + * + * @return a {@code String} containing the current working directory + * @throws IOException if an error occurs during transmission + * @throws FtpProtocolException if the command was refused by the server, + */ + public abstract String getWorkingDirectory() throws FtpProtocolException, IOException; + + /** + * Sets the restart offset to the specified value. That value will be + * sent through a {@code REST} command to server before the next file + * transfer and has the effect of resuming a file transfer from the + * specified point. After the transfer the restart offset is set back to + * zero. + * + * @param offset the offset in the remote file at which to start the next + * transfer. This must be a value greater than or equal to zero. + * @return this FtpClient + * @throws IllegalArgumentException if the offset is negative. + */ + public abstract FtpClient setRestartOffset(long offset); + + /** + * Retrieves a file from the ftp server and writes its content to the specified + * {@code OutputStream}. + *

    If the restart offset was set, then a {@code REST} command will be + * sent before the {@code RETR} in order to restart the tranfer from the specified + * offset.

    + *

    The {@code OutputStream} is not closed by this method at the end + * of the transfer.

    + *

    This method will block until the transfer is complete or an exception + * is thrown.

    + * + * @param name a {@code String} containing the name of the file to + * retreive from the server. + * @param local the {@code OutputStream} the file should be written to. + * @return this FtpClient + * @throws IOException if the transfer fails. + * @throws FtpProtocolException if the command was refused by the server + * @see #setRestartOffset(long) + */ + public abstract FtpClient getFile(String name, OutputStream local) throws FtpProtocolException, IOException; + + /** + * Retrieves a file from the ftp server, using the {@code RETR} command, and + * returns the InputStream from the established data connection. + * {@link #completePending()} has to be called once the application + * is done reading from the returned stream. + *

    If the restart offset was set, then a {@code REST} command will be + * sent before the {@code RETR} in order to restart the tranfer from the specified + * offset.

    + * + * @param name the name of the remote file + * @return the {@link InputStream} from the data connection + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was refused by the server + * @see #setRestartOffset(long) + */ + public abstract InputStream getFileStream(String name) throws FtpProtocolException, IOException; + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR command, and returns the {@code OutputStream} + * from the established data connection. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + * {@link #completePending()} has to be called once the application + * is finished writing to the returned stream. + * + * @param name the name of the remote file to write. + * @return the {@link OutputStream} from the data connection or + * {@code null} if the command was unsuccessful. + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public OutputStream putFileStream(String name) throws FtpProtocolException, IOException { + return putFileStream(name, false); + } + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR or STOU command, depending on the + * {@code unique} argument, and returns the {@code OutputStream} + * from the established data connection. + * {@link #completePending()} has to be called once the application + * is finished writing to the stream. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + * If {@code unique} is set to {@code true}, the resultant file + * is to be created under a name unique to that directory, meaning + * it will not overwrite an existing file, instead the server will + * generate a new, unique, file name. + * The name of the remote file can be retrieved, after completion of the + * transfer, by calling {@link #getLastFileName()}. + * + * @param name the name of the remote file to write. + * @param unique {@code true} if the remote files should be unique, + * in which case the STOU command will be used. + * @return the {@link OutputStream} from the data connection. + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract OutputStream putFileStream(String name, boolean unique) throws FtpProtocolException, IOException; + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR or STOU command, depending on the + * {@code unique} argument. The content of the {@code InputStream} + * passed in argument is written into the remote file, overwriting any + * existing data. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + * If {@code unique} is set to {@code true}, the resultant file + * is to be created under a name unique to that directory, meaning + * it will not overwrite an existing file, instead the server will + * generate a new, unique, file name. + * The name of the remote file can be retrieved, after completion of the + * transfer, by calling {@link #getLastFileName()}. + * + *

    This method will block until the transfer is complete or an exception + * is thrown.

    + * + * @param name the name of the remote file to write. + * @param local the {@code InputStream} that points to the data to + * transfer. + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public FtpClient putFile(String name, InputStream local) throws FtpProtocolException, IOException { + return putFile(name, local, false); + } + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR command. The content of the {@code InputStream} + * passed in argument is written into the remote file, overwriting any + * existing data. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + *

    This method will block until the transfer is complete or an exception + * is thrown.

    + * + * @param name the name of the remote file to write. + * @param local the {@code InputStream} that points to the data to + * transfer. + * @param unique {@code true} if the remote file should be unique + * (i.e. not already existing), {@code false} otherwise. + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #getLastFileName() + */ + public abstract FtpClient putFile(String name, InputStream local, boolean unique) throws FtpProtocolException, IOException; + + /** + * Sends the APPE command to the server in order to transfer a data stream + * passed in argument and append it to the content of the specified remote + * file. + * + *

    This method will block until the transfer is complete or an exception + * is thrown.

    + * + * @param name A {@code String} containing the name of the remote file + * to append to. + * @param local The {@code InputStream} providing access to the data + * to be appended. + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient appendFile(String name, InputStream local) throws FtpProtocolException, IOException; + + /** + * Renames a file on the server. + * + * @param from the name of the file being renamed + * @param to the new name for the file + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient rename(String from, String to) throws FtpProtocolException, IOException; + + /** + * Deletes a file on the server. + * + * @param name a {@code String} containing the name of the file + * to delete. + * @return this FtpClient + * @throws IOException if an error occurred during the exchange + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient deleteFile(String name) throws FtpProtocolException, IOException; + + /** + * Creates a new directory on the server. + * + * @param name a {@code String} containing the name of the directory + * to create. + * @return this FtpClient + * @throws IOException if an error occurred during the exchange + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient makeDirectory(String name) throws FtpProtocolException, IOException; + + /** + * Removes a directory on the server. + * + * @param name a {@code String} containing the name of the directory + * to remove. + * + * @return this FtpClient + * @throws IOException if an error occurred during the exchange. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient removeDirectory(String name) throws FtpProtocolException, IOException; + + /** + * Sends a No-operation command. It's useful for testing the connection + * status or as a keep alive mechanism. + * + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient noop() throws FtpProtocolException, IOException; + + /** + * Sends the {@code STAT} command to the server. + * This can be used while a data connection is open to get a status + * on the current transfer, in that case the parameter should be + * {@code null}. + * If used between file transfers, it may have a pathname as argument + * in which case it will work as the LIST command except no data + * connection will be created. + * + * @param name an optional {@code String} containing the pathname + * the STAT command should apply to. + * @return the response from the server + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract String getStatus(String name) throws FtpProtocolException, IOException; + + /** + * Sends the {@code FEAT} command to the server and returns the list of supported + * features in the form of strings. + * + * The features are the supported commands, like AUTH TLS, PROT or PASV. + * See the RFCs for a complete list. + * + * Note that not all FTP servers support that command, in which case + * a {@link FtpProtocolException} will be thrown. + * + * @return a {@code List} of {@code Strings} describing the + * supported additional features + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command is rejected by the server + */ + public abstract List getFeatures() throws FtpProtocolException, IOException; + + /** + * Sends the {@code ABOR} command to the server. + *

    It tells the server to stop the previous command or transfer. No action + * will be taken if the previous command has already been completed.

    + *

    This doesn't abort the current session, more commands can be issued + * after an abort.

    + * + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient abort() throws FtpProtocolException, IOException; + + /** + * Some methods do not wait until completion before returning, so this + * method can be called to wait until completion. This is typically the case + * with commands that trigger a transfer like {@link #getFileStream(String)}. + * So this method should be called before accessing information related to + * such a command. + *

    This method will actually block reading on the command channel for a + * notification from the server that the command is finished. Such a + * notification often carries extra information concerning the completion + * of the pending action (e.g. number of bytes transfered).

    + *

    Note that this will return immediately if no command or action + * is pending

    + *

    It should be also noted that most methods issuing commands to the ftp + * server will call this method if a previous command is pending. + *

    Example of use: + *

    +     * InputStream in = cl.getFileStream("file");
    +     * ...
    +     * cl.completePending();
    +     * long size = cl.getLastTransferSize();
    +     * 
    + * On the other hand, it's not necessary in a case like: + *
    +     * InputStream in = cl.getFileStream("file");
    +     * // read content
    +     * ...
    +     * cl.close();
    +     * 
    + *

    Since {@link #close()} will call completePending() if necessary.

    + * @return this FtpClient + * @throws IOException if an error occurred during the transfer + * @throws FtpProtocolException if the command didn't complete successfully + */ + public abstract FtpClient completePending() throws FtpProtocolException, IOException; + + /** + * Reinitializes the USER parameters on the FTP server + * + * @return this FtpClient + * @throws IOException if an error occurs during transmission + * @throws FtpProtocolException if the command fails + */ + public abstract FtpClient reInit() throws FtpProtocolException, IOException; + + /** + * Changes the transfer type (binary, ascii, ebcdic) and issue the + * proper command (e.g. TYPE A) to the server. + * + * @param type the {@code TransferType} to use. + * @return This FtpClient + * @throws IOException if an error occurs during transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient setType(TransferType type) throws FtpProtocolException, IOException; + + /** + * Changes the current transfer type to binary. + * This is a convenience method that is equivalent to + * {@code setType(TransferType.BINARY)} + * + * @return This FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #setType(TransferType) + */ + public FtpClient setBinaryType() throws FtpProtocolException, IOException { + setType(TransferType.BINARY); + return this; + } + + /** + * Changes the current transfer type to ascii. + * This is a convenience method that is equivalent to + * {@code setType(TransferType.ASCII)} + * + * @return This FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #setType(TransferType) + */ + public FtpClient setAsciiType() throws FtpProtocolException, IOException { + setType(TransferType.ASCII); + return this; + } + + /** + * Issues a {@code LIST} command to the server to get the current directory + * listing, and returns the InputStream from the data connection. + * + *

    {@link #completePending()} has to be called once the application + * is finished reading from the stream.

    + * + * @param path the pathname of the directory to list, or {@code null} + * for the current working directory. + * @return the {@code InputStream} from the resulting data connection + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #changeDirectory(String) + * @see #listFiles(String) + */ + public abstract InputStream list(String path) throws FtpProtocolException, IOException; + + /** + * Issues a {@code NLST path} command to server to get the specified directory + * content. It differs from {@link #list(String)} method by the fact that + * it will only list the file names which would make the parsing of the + * somewhat easier. + * + *

    {@link #completePending()} has to be called once the application + * is finished reading from the stream.

    + * + * @param path a {@code String} containing the pathname of the + * directory to list or {@code null} for the current working directory. + * @return the {@code InputStream} from the resulting data connection + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract InputStream nameList(String path) throws FtpProtocolException, IOException; + + /** + * Issues the {@code SIZE [path]} command to the server to get the size of a + * specific file on the server. + * Note that this command may not be supported by the server. In which + * case -1 will be returned. + * + * @param path a {@code String} containing the pathname of the + * file. + * @return a {@code long} containing the size of the file or -1 if + * the server returned an error, which can be checked with + * {@link #getLastReplyCode()}. + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract long getSize(String path) throws FtpProtocolException, IOException; + + /** + * Issues the {@code MDTM [path]} command to the server to get the modification + * time of a specific file on the server. + * Note that this command may not be supported by the server, in which + * case {@code null} will be returned. + * + * @param path a {@code String} containing the pathname of the file. + * @return a {@code Date} representing the last modification time + * or {@code null} if the server returned an error, which + * can be checked with {@link #getLastReplyCode()}. + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract Date getLastModified(String path) throws FtpProtocolException, IOException; + + /** + * Sets the parser used to handle the directory output to the specified + * one. By default the parser is set to one that can handle most FTP + * servers output (Unix base mostly). However it may be necessary for + * and application to provide its own parser due to some uncommon + * output format. + * + * @param p The {@code FtpDirParser} to use. + * @return this FtpClient + * @see #listFiles(String) + */ + public abstract FtpClient setDirParser(FtpDirParser p); + + /** + * Issues a {@code MLSD} command to the server to get the specified directory + * listing and applies the internal parser to create an Iterator of + * {@link java.net.FtpDirEntry}. Note that the Iterator returned is also a + * {@link Closeable}. + *

    If the server doesn't support the MLSD command, the LIST command is used + * instead and the parser set by {@link #setDirParser(java.net.FtpDirParser) } + * is used instead.

    + * + * {@link #completePending()} has to be called once the application + * is finished iterating through the files. + * + * @param path the pathname of the directory to list or {@code null} + * for the current working directoty. + * @return a {@code Iterator} of files or {@code null} if the + * command failed. + * @throws IOException if an error occurred during the transmission + * @see #setDirParser(FtpDirParser) + * @see #changeDirectory(String) + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract Iterator listFiles(String path) throws FtpProtocolException, IOException; + + /** + * Attempts to use Kerberos GSSAPI as an authentication mechanism with the + * ftp server. This will issue an {@code AUTH GSSAPI} command, and if + * it is accepted by the server, will followup with {@code ADAT} + * command to exchange the various tokens until authentication is + * successful. This conforms to Appendix I of RFC 2228. + * + * @return this FtpClient + * @throws IOException if an error occurs during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient useKerberos() throws FtpProtocolException, IOException; + + /** + * Returns the Welcome string the server sent during initial connection. + * + * @return a {@code String} containing the message the server + * returned during connection or {@code null}. + */ + public abstract String getWelcomeMsg(); + + /** + * Returns the last reply code sent by the server. + * + * @return the lastReplyCode or {@code null} if none were received yet. + */ + public abstract FtpReplyCode getLastReplyCode(); + + /** + * Returns the last response string sent by the server. + * + * @return the message string, which can be quite long, last returned + * by the server, or {@code null} if no response were received yet. + */ + public abstract String getLastResponseString(); + + /** + * Returns, when available, the size of the latest started transfer. + * This is retreived by parsing the response string received as an initial + * response to a {@code RETR} or similar request. + * + * @return the size of the latest transfer or -1 if either there was no + * transfer or the information was unavailable. + */ + public abstract long getLastTransferSize(); + + /** + * Returns, when available, the remote name of the last transfered file. + * This is mainly useful for "put" operation when the unique flag was + * set since it allows to recover the unique file name created on the + * server which may be different from the one submitted with the command. + * + * @return the name the latest transfered file remote name, or + * {@code null} if that information is unavailable. + */ + public abstract String getLastFileName(); + + /** + * Attempts to switch to a secure, encrypted connection. This is done by + * sending the {@code AUTH TLS} command. + *

    See RFC 4217

    + * If successful this will establish a secure command channel with the + * server, it will also make it so that all other transfers (e.g. a RETR + * command) will be done over an encrypted channel as well unless a + * {@link #reInit()} command or a {@link #endSecureSession()} command is issued. + *

    This method should be called after a successful {@link #connect(InetSocketAddress) } + * but before calling {@link #login(String, char[]) }.

    + * + * @return this FtpCLient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #endSecureSession() + */ + public abstract FtpClient startSecureSession() throws FtpProtocolException, IOException; + + /** + * Sends a {@code CCC} command followed by a {@code PROT C} + * command to the server terminating an encrypted session and reverting + * back to a non encrypted transmission. + * + * @return this FtpClient + * @throws IOException if an error occurred during transmission. + * @throws FtpProtocolException if the command was rejected by the server + * @see #startSecureSession() + */ + public abstract FtpClient endSecureSession() throws FtpProtocolException, IOException; + + /** + * Sends the "Allocate" ({@code ALLO}) command to the server telling it to + * pre-allocate the specified number of bytes for the next transfer. + * + * @param size The number of bytes to allocate. + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient allocate(long size) throws FtpProtocolException, IOException; + + /** + * Sends the "Structure Mount" ({@code SMNT}) command to the server. This let the + * user mount a different file system data structure without altering his + * login or accounting information. + * + * @param struct a {@code String} containing the name of the + * structure to mount. + * @return this FtpClient + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient structureMount(String struct) throws FtpProtocolException, IOException; + + /** + * Sends a System ({@code SYST}) command to the server and returns the String + * sent back by the server describing the operating system at the + * server. + * + * @return a {@code String} describing the OS, or {@code null} + * if the operation was not successful. + * @throws IOException if an error occurred during the transmission. + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract String getSystem() throws FtpProtocolException, IOException; + + /** + * Sends the {@code HELP} command to the server, with an optional command, like + * SITE, and returns the text sent back by the server. + * + * @param cmd the command for which the help is requested or + * {@code null} for the general help + * @return a {@code String} containing the text sent back by the + * server, or {@code null} if the command failed. + * @throws IOException if an error occurred during transmission + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract String getHelp(String cmd) throws FtpProtocolException, IOException; + + /** + * Sends the {@code SITE} command to the server. This is used by the server + * to provide services specific to his system that are essential + * to file transfer. + * + * @param cmd the command to be sent. + * @return this FtpClient + * @throws IOException if an error occurred during transmission + * @throws FtpProtocolException if the command was rejected by the server + */ + public abstract FtpClient siteCmd(String cmd) throws FtpProtocolException, IOException; +} diff --git a/src/sun/net/ftp/FtpClientProvider.java b/src/sun/net/ftp/FtpClientProvider.java new file mode 100644 index 00000000..06deae65 --- /dev/null +++ b/src/sun/net/ftp/FtpClientProvider.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ServiceConfigurationError; +//import java.util.ServiceLoader; + +/** + * Service provider class for FtpClient. + * Sub-classes of FtpClientProvider provide an implementation of {@link FtpClient} + * and associated classes. Applications do not normally use this class directly. + * See {@link #provider() } for how providers are found and loaded. + * + * @since 1.7 + */ +public abstract class FtpClientProvider { + + /** + * Creates a FtpClient from this provider. + * + * @return The created {@link FtpClient}. + */ + public abstract FtpClient createFtpClient(); + private static final Object lock = new Object(); + private static FtpClientProvider provider = null; + + /** + * Initializes a new instance of this class. + * + * @throws SecurityException if a security manager is installed and it denies + * {@link RuntimePermission}("ftpClientProvider") + */ + protected FtpClientProvider() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("ftpClientProvider")); + } + } + + private static boolean loadProviderFromProperty() { + String cm = System.getProperty("sun.net.ftpClientProvider"); + if (cm == null) { + return false; + } + try { + Class c = Class.forName(cm, true, null); + provider = (FtpClientProvider) c.newInstance(); + return true; + } catch (ClassNotFoundException | + IllegalAccessException | + InstantiationException | + SecurityException x) { + throw new ServiceConfigurationError(x.toString()); + } + } + + private static boolean loadProviderAsService() { +// Iterator i = +// ServiceLoader.load(FtpClientProvider.class, +// ClassLoader.getSystemClassLoader()).iterator(); +// +// while (i.hasNext()) { +// try { +// provider = i.next(); +// return true; +// } catch (ServiceConfigurationError sce) { +// if (sce.getCause() instanceof SecurityException) { +// // Ignore, try next provider, if any +// continue; +// } +// throw sce; +// } +// } + return false; + } + + /** + * Returns the system wide default FtpClientProvider for this invocation of + * the Java virtual machine. + * + *

    The first invocation of this method locates the default provider + * object as follows:

    + * + *
      + * + *
    1. If the system property + * java.net.FtpClientProvider is defined then it is + * taken to be the fully-qualified name of a concrete provider class. + * The class is loaded and instantiated; if this process fails then an + * unspecified unchecked error or exception is thrown.

    2. + * + *
    3. If a provider class has been installed in a jar file that is + * visible to the system class loader, and that jar file contains a + * provider-configuration file named + * java.net.FtpClientProvider in the resource + * directory META-INF/services, then the first class name + * specified in that file is taken. The class is loaded and + * instantiated; if this process fails then an unspecified unchecked error or exception is + * thrown.

    4. + * + *
    5. Finally, if no provider has been specified by any of the above + * means then the system-default provider class is instantiated and the + * result is returned.

    6. + * + *
    + * + *

    Subsequent invocations of this method return the provider that was + * returned by the first invocation.

    + * + * @return The system-wide default FtpClientProvider + */ + public static FtpClientProvider provider() { + synchronized (lock) { + if (provider != null) { + return provider; + } + return (FtpClientProvider) AccessController.doPrivileged( + new PrivilegedAction() { + + public Object run() { + if (loadProviderFromProperty()) { + return provider; + } + if (loadProviderAsService()) { + return provider; + } + provider = new sun.net.ftp.impl.DefaultFtpClientProvider(); + return provider; + } + }); + } + } +} diff --git a/src/sun/net/ftp/FtpDirEntry.java b/src/sun/net/ftp/FtpDirEntry.java new file mode 100644 index 00000000..81c0105c --- /dev/null +++ b/src/sun/net/ftp/FtpDirEntry.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp; + +import java.util.Date; +import java.util.HashMap; + +/** + * A {@code FtpDirEntry} is a class agregating all the information that the FTP client + * can gather from the server by doing a {@code LST} (or {@code NLST}) command and + * parsing the output. It will typically contain the name, type, size, last modification + * time, owner and group of the file, although some of these could be unavailable + * due to specific FTP server limitations. + * + * @see FtpDirParser + * @since 1.7 + */ +public class FtpDirEntry { + + public enum Type { + + FILE, DIR, PDIR, CDIR, LINK + }; + + public enum Permission { + + USER(0), GROUP(1), OTHERS(2); + int value; + + Permission(int v) { + value = v; + } + }; + private final String name; + private String user = null; + private String group = null; + private long size = -1; + private Date created = null; + private Date lastModified = null; + private Type type = Type.FILE; + private boolean[][] permissions = null; + private HashMap facts = new HashMap(); + + private FtpDirEntry() { + name = null; + } + + /** + * Creates an FtpDirEntry instance with only the name being set. + * + * @param name The name of the file + */ + public FtpDirEntry(String name) { + this.name = name; + } + + /** + * Returns the name of the remote file. + * + * @return a {@code String} containing the name of the remote file. + */ + public String getName() { + return name; + } + + /** + * Returns the user name of the owner of the file as returned by the FTP + * server, if provided. This could be a name or a user id (number). + * + * @return a {@code String} containing the user name or + * {@code null} if that information is not available. + */ + public String getUser() { + return user; + } + + /** + * Sets the user name of the owner of the file. Intended mostly to be + * used from inside a {@link java.net.FtpDirParser} implementation. + * + * @param user The user name of the owner of the file, or {@code null} + * if that information is not available. + * @return this FtpDirEntry + */ + public FtpDirEntry setUser(String user) { + this.user = user; + return this; + } + + /** + * Returns the group name of the file as returned by the FTP + * server, if provided. This could be a name or a group id (number). + * + * @return a {@code String} containing the group name or + * {@code null} if that information is not available. + */ + public String getGroup() { + return group; + } + + /** + * Sets the name of the group to which the file belong. Intended mostly to be + * used from inside a {@link java.net.FtpDirParser} implementation. + * + * @param group The name of the group to which the file belong, or {@code null} + * if that information is not available. + * @return this FtpDirEntry + */ + public FtpDirEntry setGroup(String group) { + this.group = group; + return this; + } + + /** + * Returns the size of the remote file as it was returned by the FTP + * server, if provided. + * + * @return the size of the file or -1 if that information is not available. + */ + public long getSize() { + return size; + } + + /** + * Sets the size of that file. Intended mostly to be used from inside an + * {@link java.net.FtpDirParser} implementation. + * + * @param size The size, in bytes, of that file. or -1 if unknown. + * @return this FtpDirEntry + */ + public FtpDirEntry setSize(long size) { + this.size = size; + return this; + } + + /** + * Returns the type of the remote file as it was returned by the FTP + * server, if provided. + * It returns a FtpDirEntry.Type enum and the values can be: + * - FtpDirEntry.Type.FILE for a normal file + * - FtpDirEntry.Type.DIR for a directory + * - FtpDirEntry.Type.LINK for a symbolic link + * + * @return a {@code FtpDirEntry.Type} describing the type of the file + * or {@code null} if that information is not available. + */ + public Type getType() { + return type; + } + + /** + * Sets the type of the file. Intended mostly to be used from inside an + * {@link java.net.FtpDirParser} implementation. + * + * @param type the type of this file or {@code null} if that information + * is not available. + * @return this FtpDirEntry + */ + public FtpDirEntry setType(Type type) { + this.type = type; + return this; + } + + /** + * Returns the last modification time of the remote file as it was returned + * by the FTP server, if provided, {@code null} otherwise. + * + * @return a Date representing the last time the file was + * modified on the server, or {@code null} if that + * information is not available. + */ + public Date getLastModified() { + return this.lastModified; + } + + /** + * Sets the last modification time of the file. Intended mostly to be used + * from inside an {@link java.net.FtpDirParser} implementation. + * + * @param lastModified The Date representing the last modification time, or + * {@code null} if that information is not available. + * @return this FtpDirEntry + */ + public FtpDirEntry setLastModified(Date lastModified) { + this.lastModified = lastModified; + return this; + } + + /** + * Returns whether read access is granted for a specific permission. + * + * @param p the Permission (user, group, others) to check. + * @return {@code true} if read access is granted. + */ + public boolean canRead(Permission p) { + if (permissions != null) { + return permissions[p.value][0]; + } + return false; + } + + /** + * Returns whether write access is granted for a specific permission. + * + * @param p the Permission (user, group, others) to check. + * @return {@code true} if write access is granted. + */ + public boolean canWrite(Permission p) { + if (permissions != null) { + return permissions[p.value][1]; + } + return false; + } + + /** + * Returns whether execute access is granted for a specific permission. + * + * @param p the Permission (user, group, others) to check. + * @return {@code true} if execute access is granted. + */ + public boolean canExexcute(Permission p) { + if (permissions != null) { + return permissions[p.value][2]; + } + return false; + } + + /** + * Sets the permissions for that file. Intended mostly to be used + * from inside an {@link java.net.FtpDirParser} implementation. + * The permissions array is a 3x3 {@code boolean} array, the first index being + * the User, group or owner (0, 1 and 2 respectively) while the second + * index is read, write or execute (0, 1 and 2 respectively again). + *

    E.G.: {@code permissions[1][2]} is the group/execute permission.

    + * + * @param permissions a 3x3 {@code boolean} array + * @return this {@code FtpDirEntry} + */ + public FtpDirEntry setPermissions(boolean[][] permissions) { + this.permissions = permissions; + return this; + } + + /** + * Adds a 'fact', as defined in RFC 3659, to the list of facts of this file. + * Intended mostly to be used from inside a {@link java.net.FtpDirParser} + * implementation. + * + * @param fact the name of the fact (e.g. "Media-Type"). It is not case-sensitive. + * @param value the value associated with this fact. + * @return this {@code FtpDirEntry} + */ + public FtpDirEntry addFact(String fact, String value) { + facts.put(fact.toLowerCase(), value); + return this; + } + + /** + * Returns the requested 'fact', as defined in RFC 3659, if available. + * + * @param fact The name of the fact *e.g. "Media-Type"). It is not case sensitive. + * @return The value of the fact or, {@code null} if that fact wasn't + * provided by the server. + */ + public String getFact(String fact) { + return facts.get(fact.toLowerCase()); + } + + /** + * Returns the creation time of the file, when provided by the server. + * + * @return The Date representing the creation time, or {@code null} + * if the server didn't provide that information. + */ + public Date getCreated() { + return created; + } + + /** + * Sets the creation time for that file. Intended mostly to be used from + * inside a {@link java.net.FtpDirParser} implementation. + * + * @param created the Date representing the creation time for that file, or + * {@code null} if that information is not available. + * @return this FtpDirEntry + */ + public FtpDirEntry setCreated(Date created) { + this.created = created; + return this; + } + + /** + * Returns a string representation of the object. + * The {@code toString} method for class {@code FtpDirEntry} + * returns a string consisting of the name of the file, followed by its + * type between brackets, followed by the user and group between + * parenthesis, then size between '{', and, finally, the lastModified of last + * modification if it's available. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + if (lastModified == null) { + return name + " [" + type + "] (" + user + " / " + group + ") " + size; + } + return name + " [" + type + "] (" + user + " / " + group + ") {" + size + "} " + java.text.DateFormat.getDateInstance().format(lastModified); + } +} diff --git a/src/sun/net/ftp/FtpDirParser.java b/src/sun/net/ftp/FtpDirParser.java new file mode 100644 index 00000000..f79a6775 --- /dev/null +++ b/src/sun/net/ftp/FtpDirParser.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.ftp; + +/** + * This interface describes a parser for the FtpClient class. Such a parser is + * used when listing a remote directory to transform text lines like: + * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog + * into FtpDirEntry instances. + * + * @see java.net.FtpClient#setFileParser(FtpDirParser) + * @since 1.7 + */ +public interface FtpDirParser { + + /** + * Takes one line from a directory listing and returns an FtpDirEntry instance + * based on the information contained. + * + * @param line a String, a line sent by the FTP server as a + * result of the LST command. + * @return an FtpDirEntry instance. + * @see java.net.FtpDirEntry + */ + public FtpDirEntry parseLine(String line); +} diff --git a/src/sun/net/ftp/FtpLoginException.java b/src/sun/net/ftp/FtpLoginException.java new file mode 100644 index 00000000..c2f777c2 --- /dev/null +++ b/src/sun/net/ftp/FtpLoginException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1994, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.ftp; + +import java.io.IOException; + +/** + * This exception is thrown when an error is encountered during an + * FTP login operation. + * + * @author Jonathan Payne + */ +public class FtpLoginException extends IOException { + private static final long serialVersionUID = 2218162403237941536L; + + public FtpLoginException(String s) { + super(s); + } +} diff --git a/src/sun/net/ftp/FtpProtocolException.java b/src/sun/net/ftp/FtpProtocolException.java new file mode 100644 index 00000000..009887d6 --- /dev/null +++ b/src/sun/net/ftp/FtpProtocolException.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1994, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp; + +/** + * Thrown to indicate that the FTP server reported an error. + * For instance that the requested file doesn't exist or + * that a command isn't supported. + *

    The specific error code can be retreived with {@link #getReplyCode() }.

    + * @author Jonathan Payne + */ +public class FtpProtocolException extends Exception { + private static final long serialVersionUID = 5978077070276545054L; + private final FtpReplyCode code; + + /** + * Constructs a new {@code FtpProtocolException} from the + * specified detail message. The reply code is set to unknow error. + * + * @param detail the detail message. + */ + public FtpProtocolException(String detail) { + super(detail); + code = FtpReplyCode.UNKNOWN_ERROR; + } + + /** + * Constructs a new {@code FtpProtocolException} from the + * specified response code and exception detail message + * + * @param detail the detail message. + * @param code The {@code FtpRelyCode} received from server. + */ + public FtpProtocolException(String detail, FtpReplyCode code) { + super(detail); + this.code = code; + } + + /** + * Gets the reply code sent by the server that led to this exception + * being thrown. + * + * @return The {@link FtpReplyCode} associated with that exception. + */ + public FtpReplyCode getReplyCode() { + return code; + } +} diff --git a/src/sun/net/ftp/FtpReplyCode.java b/src/sun/net/ftp/FtpReplyCode.java new file mode 100644 index 00000000..4c22889b --- /dev/null +++ b/src/sun/net/ftp/FtpReplyCode.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp; + +/** + * This class describes a FTP protocol reply code and associates a meaning + * to the numerical value according to the various RFCs (RFC 959 in + * particular). + * + */ +public enum FtpReplyCode { + + RESTART_MARKER(110), + SERVICE_READY_IN(120), + DATA_CONNECTION_ALREADY_OPEN(125), + FILE_STATUS_OK(150), + COMMAND_OK(200), + NOT_IMPLEMENTED(202), + SYSTEM_STATUS(211), + DIRECTORY_STATUS(212), + FILE_STATUS(213), + HELP_MESSAGE(214), + NAME_SYSTEM_TYPE(215), + SERVICE_READY(220), + SERVICE_CLOSING(221), + DATA_CONNECTION_OPEN(225), + CLOSING_DATA_CONNECTION(226), + ENTERING_PASSIVE_MODE(227), + ENTERING_EXT_PASSIVE_MODE(229), + LOGGED_IN(230), + SECURELY_LOGGED_IN(232), + SECURITY_EXCHANGE_OK(234), + SECURITY_EXCHANGE_COMPLETE(235), + FILE_ACTION_OK(250), + PATHNAME_CREATED(257), + NEED_PASSWORD(331), + NEED_ACCOUNT(332), + NEED_ADAT(334), + NEED_MORE_ADAT(335), + FILE_ACTION_PENDING(350), + SERVICE_NOT_AVAILABLE(421), + CANT_OPEN_DATA_CONNECTION(425), + CONNECTION_CLOSED(426), + NEED_SECURITY_RESOURCE(431), + FILE_ACTION_NOT_TAKEN(450), + ACTION_ABORTED(451), + INSUFFICIENT_STORAGE(452), + COMMAND_UNRECOGNIZED(500), + INVALID_PARAMETER(501), + BAD_SEQUENCE(503), + NOT_IMPLEMENTED_FOR_PARAMETER(504), + NOT_LOGGED_IN(530), + NEED_ACCOUNT_FOR_STORING(532), + PROT_LEVEL_DENIED(533), + REQUEST_DENIED(534), + FAILED_SECURITY_CHECK(535), + UNSUPPORTED_PROT_LEVEL(536), + PROT_LEVEL_NOT_SUPPORTED_BY_SECURITY(537), + FILE_UNAVAILABLE(550), + PAGE_TYPE_UNKNOWN(551), + EXCEEDED_STORAGE(552), + FILE_NAME_NOT_ALLOWED(553), + PROTECTED_REPLY(631), + UNKNOWN_ERROR(999); + private final int value; + + FtpReplyCode(int val) { + this.value = val; + } + + /** + * Returns the numerical value of the code. + * + * @return the numerical value. + */ + public int getValue() { + return value; + } + + /** + * Determines if the code is a Positive Preliminary response. + * This means beginning with a 1 (which means a value between 100 and 199) + * + * @return true if the reply code is a positive preliminary + * response. + */ + public boolean isPositivePreliminary() { + return value >= 100 && value < 200; + } + + /** + * Determines if the code is a Positive Completion response. + * This means beginning with a 2 (which means a value between 200 and 299) + * + * @return true if the reply code is a positive completion + * response. + */ + public boolean isPositiveCompletion() { + return value >= 200 && value < 300; + } + + /** + * Determines if the code is a positive internediate response. + * This means beginning with a 3 (which means a value between 300 and 399) + * + * @return true if the reply code is a positive intermediate + * response. + */ + public boolean isPositiveIntermediate() { + return value >= 300 && value < 400; + } + + /** + * Determines if the code is a transient negative response. + * This means beginning with a 4 (which means a value between 400 and 499) + * + * @return true if the reply code is a transient negative + * response. + */ + public boolean isTransientNegative() { + return value >= 400 && value < 500; + } + + /** + * Determines if the code is a permanent negative response. + * This means beginning with a 5 (which means a value between 500 and 599) + * + * @return true if the reply code is a permanent negative + * response. + */ + public boolean isPermanentNegative() { + return value >= 500 && value < 600; + } + + /** + * Determines if the code is a protected reply response. + * This means beginning with a 6 (which means a value between 600 and 699) + * + * @return true if the reply code is a protected reply + * response. + */ + public boolean isProtectedReply() { + return value >= 600 && value < 700; + } + + /** + * Determines if the code is a syntax related response. + * This means the second digit is a 0. + * + * @return true if the reply code is a syntax related + * response. + */ + public boolean isSyntax() { + return ((value / 10) - ((value / 100) * 10)) == 0; + } + + /** + * Determines if the code is an information related response. + * This means the second digit is a 1. + * + * @return true if the reply code is an information related + * response. + */ + public boolean isInformation() { + return ((value / 10) - ((value / 100) * 10)) == 1; + } + + /** + * Determines if the code is a connection related response. + * This means the second digit is a 2. + * + * @return true if the reply code is a connection related + * response. + */ + public boolean isConnection() { + return ((value / 10) - ((value / 100) * 10)) == 2; + } + + /** + * Determines if the code is an authentication related response. + * This means the second digit is a 3. + * + * @return true if the reply code is an authentication related + * response. + */ + public boolean isAuthentication() { + return ((value / 10) - ((value / 100) * 10)) == 3; + } + + /** + * Determines if the code is an unspecified type of response. + * This means the second digit is a 4. + * + * @return true if the reply code is an unspecified type of + * response. + */ + public boolean isUnspecified() { + return ((value / 10) - ((value / 100) * 10)) == 4; + } + + /** + * Determines if the code is a file system related response. + * This means the second digit is a 5. + * + * @return true if the reply code is a file system related + * response. + */ + public boolean isFileSystem() { + return ((value / 10) - ((value / 100) * 10)) == 5; + } + + /** + * Static utility method to convert a value into a FtpReplyCode. + * + * @param v the value to convert + * @return the FtpReplyCode associated with the value. + */ + public static FtpReplyCode find(int v) { + for (FtpReplyCode code : FtpReplyCode.values()) { + if (code.getValue() == v) { + return code; + } + } + return UNKNOWN_ERROR; + } +} diff --git a/src/sun/net/ftp/impl/DefaultFtpClientProvider.java b/src/sun/net/ftp/impl/DefaultFtpClientProvider.java new file mode 100644 index 00000000..9b8c7ec8 --- /dev/null +++ b/src/sun/net/ftp/impl/DefaultFtpClientProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp.impl; + +/** + * Default FtpClientProvider. + * Uses sun.net.ftp.FtpCLient. + */ +public class DefaultFtpClientProvider extends sun.net.ftp.FtpClientProvider { + + @Override + public sun.net.ftp.FtpClient createFtpClient() { + return FtpClient.create(); + } + +} diff --git a/src/sun/net/ftp/impl/FtpClient.java b/src/sun/net/ftp/impl/FtpClient.java new file mode 100644 index 00000000..755e2a2c --- /dev/null +++ b/src/sun/net/ftp/impl/FtpClient.java @@ -0,0 +1,2218 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.ftp.impl; + +import java.net.*; +import java.io.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.TimeZone; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import sun.misc.BASE64Decoder; +import sun.misc.BASE64Encoder; +import sun.net.ftp.*; +import sun.util.logging.PlatformLogger; + + +public class FtpClient extends sun.net.ftp.FtpClient { + + private static int defaultSoTimeout; + private static int defaultConnectTimeout; + private static final PlatformLogger logger = + PlatformLogger.getLogger("sun.net.ftp.FtpClient"); + private Proxy proxy; + private Socket server; + private PrintStream out; + private InputStream in; + private int readTimeout = -1; + private int connectTimeout = -1; + + /* Name of encoding to use for output */ + private static String encoding = "ISO8859_1"; + /** remember the ftp server name because we may need it */ + private InetSocketAddress serverAddr; + private boolean replyPending = false; + private boolean loggedIn = false; + private boolean useCrypto = false; + private SSLSocketFactory sslFact; + private Socket oldSocket; + /** Array of strings (usually 1 entry) for the last reply from the server. */ + private Vector serverResponse = new Vector(1); + /** The last reply code from the ftp daemon. */ + private FtpReplyCode lastReplyCode = null; + /** Welcome message from the server, if any. */ + private String welcomeMsg; + /** + * Only passive mode used in JDK. See Bug 8010784. + */ + private final boolean passiveMode = true; + private TransferType type = TransferType.BINARY; + private long restartOffset = 0; + private long lastTransSize = -1; // -1 means 'unknown size' + private String lastFileName; + /** + * Static members used by the parser + */ + private static String[] patStrings = { + // drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog + "([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d\\d:\\d\\d)\\s*(\\p{Print}*)", + // drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog + "([\\-ld](?:[r\\-][w\\-][x\\-]){3})\\s*\\d+ (\\w+)\\s*(\\w+)\\s*(\\d+)\\s*([A-Z][a-z][a-z]\\s*\\d+)\\s*(\\d{4})\\s*(\\p{Print}*)", + // 04/28/2006 09:12a 3,563 genBuffer.sh + "(\\d{2}/\\d{2}/\\d{4})\\s*(\\d{2}:\\d{2}[ap])\\s*((?:[0-9,]+)|(?:))\\s*(\\p{Graph}*)", + // 01-29-97 11:32PM prog + "(\\d{2}-\\d{2}-\\d{2})\\s*(\\d{2}:\\d{2}[AP]M)\\s*((?:[0-9,]+)|(?:))\\s*(\\p{Graph}*)" + }; + private static int[][] patternGroups = { + // 0 - file, 1 - size, 2 - date, 3 - time, 4 - year, 5 - permissions, + // 6 - user, 7 - group + {7, 4, 5, 6, 0, 1, 2, 3}, + {7, 4, 5, 0, 6, 1, 2, 3}, + {4, 3, 1, 2, 0, 0, 0, 0}, + {4, 3, 1, 2, 0, 0, 0, 0}}; + private static Pattern[] patterns; + private static Pattern linkp = Pattern.compile("(\\p{Print}+) \\-\\> (\\p{Print}+)$"); + private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, java.util.Locale.US); + + static { + final int vals[] = {0, 0}; + final String encs[] = {null}; + + AccessController.doPrivileged( + new PrivilegedAction() { + + public Object run() { + vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 0).intValue(); + vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 0).intValue(); + encs[0] = System.getProperty("file.encoding", "ISO8859_1"); + return null; + } + }); + if (vals[0] == 0) { + defaultSoTimeout = -1; + } else { + defaultSoTimeout = vals[0]; + } + + if (vals[1] == 0) { + defaultConnectTimeout = -1; + } else { + defaultConnectTimeout = vals[1]; + } + + encoding = encs[0]; + try { + if (!isASCIISuperset(encoding)) { + encoding = "ISO8859_1"; + } + } catch (Exception e) { + encoding = "ISO8859_1"; + } + + patterns = new Pattern[patStrings.length]; + for (int i = 0; i < patStrings.length; i++) { + patterns[i] = Pattern.compile(patStrings[i]); + } + } + + /** + * Test the named character encoding to verify that it converts ASCII + * characters correctly. We have to use an ASCII based encoding, or else + * the NetworkClients will not work correctly in EBCDIC based systems. + * However, we cannot just use ASCII or ISO8859_1 universally, because in + * Asian locales, non-ASCII characters may be embedded in otherwise + * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398) + * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1] + * says that the HTTP request URI should be escaped using a defined + * mechanism, but there is no way to specify in the escaped string what + * the original character set is. It is not correct to assume that + * UTF-8 is always used (as in URLs in HTML 4.0). For this reason, + * until the specifications are updated to deal with this issue more + * comprehensively, and more importantly, HTTP servers are known to + * support these mechanisms, we will maintain the current behavior + * where it is possible to send non-ASCII characters in their original + * unescaped form. + */ + private static boolean isASCIISuperset(String encoding) throws Exception { + String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,"; + + // Expected byte sequence for string above + byte[] chkB = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 45, 95, 46, 33, 126, 42, 39, 40, 41, 59, + 47, 63, 58, 64, 38, 61, 43, 36, 44}; + + byte[] b = chkS.getBytes(encoding); + return java.util.Arrays.equals(b, chkB); + } + + private class DefaultParser implements FtpDirParser { + + /** + * Possible patterns: + * + * drwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog + * drwxr-xr-x 1 user01 ftp 512 Jan 29 1997 prog + * drwxr-xr-x 1 1 1 512 Jan 29 23:32 prog + * lrwxr-xr-x 1 user01 ftp 512 Jan 29 23:32 prog -> prog2000 + * drwxr-xr-x 1 username ftp 512 Jan 29 23:32 prog + * -rw-r--r-- 1 jcc staff 105009 Feb 3 15:05 test.1 + * + * 01-29-97 11:32PM prog + * 04/28/2006 09:12a 3,563 genBuffer.sh + * + * drwxr-xr-x folder 0 Jan 29 23:32 prog + * + * 0 DIR 01-29-97 23:32 PROG + */ + private DefaultParser() { + } + + public FtpDirEntry parseLine(String line) { + String fdate = null; + String fsize = null; + String time = null; + String filename = null; + String permstring = null; + String username = null; + String groupname = null; + boolean dir = false; + Calendar now = Calendar.getInstance(); + int year = now.get(Calendar.YEAR); + + Matcher m = null; + for (int j = 0; j < patterns.length; j++) { + m = patterns[j].matcher(line); + if (m.find()) { + // 0 - file, 1 - size, 2 - date, 3 - time, 4 - year, + // 5 - permissions, 6 - user, 7 - group + filename = m.group(patternGroups[j][0]); + fsize = m.group(patternGroups[j][1]); + fdate = m.group(patternGroups[j][2]); + if (patternGroups[j][4] > 0) { + fdate += (", " + m.group(patternGroups[j][4])); + } else if (patternGroups[j][3] > 0) { + fdate += (", " + String.valueOf(year)); + } + if (patternGroups[j][3] > 0) { + time = m.group(patternGroups[j][3]); + } + if (patternGroups[j][5] > 0) { + permstring = m.group(patternGroups[j][5]); + dir = permstring.startsWith("d"); + } + if (patternGroups[j][6] > 0) { + username = m.group(patternGroups[j][6]); + } + if (patternGroups[j][7] > 0) { + groupname = m.group(patternGroups[j][7]); + } + // Old DOS format + if ("".equals(fsize)) { + dir = true; + fsize = null; + } + } + } + + if (filename != null) { + Date d; + try { + d = df.parse(fdate); + } catch (Exception e) { + d = null; + } + if (d != null && time != null) { + int c = time.indexOf(":"); + now.setTime(d); + now.set(Calendar.HOUR, Integer.parseInt(time.substring(0, c))); + now.set(Calendar.MINUTE, Integer.parseInt(time.substring(c + 1))); + d = now.getTime(); + } + // see if it's a symbolic link, i.e. the name if followed + // by a -> and a path + Matcher m2 = linkp.matcher(filename); + if (m2.find()) { + // Keep only the name then + filename = m2.group(1); + } + boolean[][] perms = new boolean[3][3]; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + perms[i][j] = (permstring.charAt((i * 3) + j) != '-'); + } + } + FtpDirEntry file = new FtpDirEntry(filename); + file.setUser(username).setGroup(groupname); + file.setSize(Long.parseLong(fsize)).setLastModified(d); + file.setPermissions(perms); + file.setType(dir ? FtpDirEntry.Type.DIR : (line.charAt(0) == 'l' ? FtpDirEntry.Type.LINK : FtpDirEntry.Type.FILE)); + return file; + } + return null; + } + } + + private class MLSxParser implements FtpDirParser { + + private SimpleDateFormat df = new SimpleDateFormat("yyyyMMddhhmmss"); + + public FtpDirEntry parseLine(String line) { + String name = null; + int i = line.lastIndexOf(";"); + if (i > 0) { + name = line.substring(i + 1).trim(); + line = line.substring(0, i); + } else { + name = line.trim(); + line = ""; + } + FtpDirEntry file = new FtpDirEntry(name); + while (!line.isEmpty()) { + String s; + i = line.indexOf(";"); + if (i > 0) { + s = line.substring(0, i); + line = line.substring(i + 1); + } else { + s = line; + line = ""; + } + i = s.indexOf("="); + if (i > 0) { + String fact = s.substring(0, i); + String value = s.substring(i + 1); + file.addFact(fact, value); + } + } + String s = file.getFact("Size"); + if (s != null) { + file.setSize(Long.parseLong(s)); + } + s = file.getFact("Modify"); + if (s != null) { + Date d = null; + try { + d = df.parse(s); + } catch (ParseException ex) { + } + if (d != null) { + file.setLastModified(d); + } + } + s = file.getFact("Create"); + if (s != null) { + Date d = null; + try { + d = df.parse(s); + } catch (ParseException ex) { + } + if (d != null) { + file.setCreated(d); + } + } + s = file.getFact("Type"); + if (s != null) { + if (s.equalsIgnoreCase("file")) { + file.setType(FtpDirEntry.Type.FILE); + } + if (s.equalsIgnoreCase("dir")) { + file.setType(FtpDirEntry.Type.DIR); + } + if (s.equalsIgnoreCase("cdir")) { + file.setType(FtpDirEntry.Type.CDIR); + } + if (s.equalsIgnoreCase("pdir")) { + file.setType(FtpDirEntry.Type.PDIR); + } + } + return file; + } + }; + private FtpDirParser parser = new DefaultParser(); + private FtpDirParser mlsxParser = new MLSxParser(); + private static Pattern transPat = null; + + private void getTransferSize() { + lastTransSize = -1; + /** + * If it's a start of data transfer response, let's try to extract + * the size from the response string. Usually it looks like that: + * + * 150 Opening BINARY mode data connection for foo (6701 bytes). + */ + String response = getLastResponseString(); + if (transPat == null) { + transPat = Pattern.compile("150 Opening .*\\((\\d+) bytes\\)."); + } + Matcher m = transPat.matcher(response); + if (m.find()) { + String s = m.group(1); + lastTransSize = Long.parseLong(s); + } + } + + /** + * extract the created file name from the response string: + * 226 Transfer complete (unique file name:toto.txt.1). + * Usually happens when a STOU (store unique) command had been issued. + */ + private void getTransferName() { + lastFileName = null; + String response = getLastResponseString(); + int i = response.indexOf("unique file name:"); + int e = response.lastIndexOf(')'); + if (i >= 0) { + i += 17; // Length of "unique file name:" + lastFileName = response.substring(i, e); + } + } + + /** + * Pulls the response from the server and returns the code as a + * number. Returns -1 on failure. + */ + private int readServerResponse() throws IOException { + StringBuffer replyBuf = new StringBuffer(32); + int c; + int continuingCode = -1; + int code; + String response; + + serverResponse.setSize(0); + while (true) { + while ((c = in.read()) != -1) { + if (c == '\r') { + if ((c = in.read()) != '\n') { + replyBuf.append('\r'); + } + } + replyBuf.append((char) c); + if (c == '\n') { + break; + } + } + response = replyBuf.toString(); + replyBuf.setLength(0); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Server [" + serverAddr + "] --> " + response); + } + + if (response.length() == 0) { + code = -1; + } else { + try { + code = Integer.parseInt(response.substring(0, 3)); + } catch (NumberFormatException e) { + code = -1; + } catch (StringIndexOutOfBoundsException e) { + /* this line doesn't contain a response code, so + we just completely ignore it */ + continue; + } + } + serverResponse.addElement(response); + if (continuingCode != -1) { + /* we've seen a ###- sequence */ + if (code != continuingCode || + (response.length() >= 4 && response.charAt(3) == '-')) { + continue; + } else { + /* seen the end of code sequence */ + continuingCode = -1; + break; + } + } else if (response.length() >= 4 && response.charAt(3) == '-') { + continuingCode = code; + continue; + } else { + break; + } + } + + return code; + } + + /** Sends command cmd to the server. */ + private void sendServer(String cmd) { + out.print(cmd); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Server [" + serverAddr + "] <-- " + cmd); + } + } + + /** converts the server response into a string. */ + private String getResponseString() { + return serverResponse.elementAt(0); + } + + /** Returns all server response strings. */ + private Vector getResponseStrings() { + return serverResponse; + } + + /** + * Read the reply from the FTP server. + * + * @return true if the command was successful + * @throws IOException if an error occurred + */ + private boolean readReply() throws IOException { + lastReplyCode = FtpReplyCode.find(readServerResponse()); + + if (lastReplyCode.isPositivePreliminary()) { + replyPending = true; + return true; + } + if (lastReplyCode.isPositiveCompletion() || lastReplyCode.isPositiveIntermediate()) { + if (lastReplyCode == FtpReplyCode.CLOSING_DATA_CONNECTION) { + getTransferName(); + } + return true; + } + return false; + } + + /** + * Sends a command to the FTP server and returns the error code + * (which can be a "success") sent by the server. + * + * @param cmd + * @return true if the command was successful + * @throws IOException + */ + private boolean issueCommand(String cmd) throws IOException { + if (!isConnected()) { + throw new IllegalStateException("Not connected"); + } + if (replyPending) { + try { + completePending(); + } catch (FtpProtocolException e) { + // ignore... + } + } + sendServer(cmd + "\r\n"); + return readReply(); + } + + /** + * Send a command to the FTP server and check for success. + * + * @param cmd String containing the command + * + * @throws FtpProtocolException if an error occurred + */ + private void issueCommandCheck(String cmd) throws FtpProtocolException, IOException { + if (!issueCommand(cmd)) { + throw new FtpProtocolException(cmd + ":" + getResponseString(), getLastReplyCode()); + } + } + private static Pattern epsvPat = null; + private static Pattern pasvPat = null; + + /** + * Opens a "PASSIVE" connection with the server and returns the connected + * Socket. + * + * @return the connected Socket + * @throws IOException if the connection was unsuccessful. + */ + private Socket openPassiveDataConnection(String cmd) throws FtpProtocolException, IOException { + String serverAnswer; + int port; + InetSocketAddress dest = null; + + /** + * Here is the idea: + * + * - First we want to try the new (and IPv6 compatible) EPSV command + * But since we want to be nice with NAT software, we'll issue the + * EPSV ALL command first. + * EPSV is documented in RFC2428 + * - If EPSV fails, then we fall back to the older, yet ok, PASV + * - If PASV fails as well, then we throw an exception and the calling + * method will have to try the EPRT or PORT command + */ + if (issueCommand("EPSV ALL")) { + // We can safely use EPSV commands + issueCommandCheck("EPSV"); + serverAnswer = getResponseString(); + + // The response string from a EPSV command will contain the port number + // the format will be : + // 229 Entering Extended PASSIVE Mode (|||58210|) + // + // So we'll use the regular expresions package to parse the output. + + if (epsvPat == null) { + epsvPat = Pattern.compile("^229 .* \\(\\|\\|\\|(\\d+)\\|\\)"); + } + Matcher m = epsvPat.matcher(serverAnswer); + if (!m.find()) { + throw new FtpProtocolException("EPSV failed : " + serverAnswer); + } + // Yay! Let's extract the port number + String s = m.group(1); + port = Integer.parseInt(s); + InetAddress add = server.getInetAddress(); + if (add != null) { + dest = new InetSocketAddress(add, port); + } else { + // This means we used an Unresolved address to connect in + // the first place. Most likely because the proxy is doing + // the name resolution for us, so let's keep using unresolved + // address. + dest = InetSocketAddress.createUnresolved(serverAddr.getHostName(), port); + } + } else { + // EPSV ALL failed, so Let's try the regular PASV cmd + issueCommandCheck("PASV"); + serverAnswer = getResponseString(); + + // Let's parse the response String to get the IP & port to connect + // to. The String should be in the following format : + // + // 227 Entering PASSIVE Mode (A1,A2,A3,A4,p1,p2) + // + // Note that the two parenthesis are optional + // + // The IP address is A1.A2.A3.A4 and the port is p1 * 256 + p2 + // + // The regular expression is a bit more complex this time, because + // the parenthesis are optionals and we have to use 3 groups. + + if (pasvPat == null) { + pasvPat = Pattern.compile("227 .* \\(?(\\d{1,3},\\d{1,3},\\d{1,3},\\d{1,3}),(\\d{1,3}),(\\d{1,3})\\)?"); + } + Matcher m = pasvPat.matcher(serverAnswer); + if (!m.find()) { + throw new FtpProtocolException("PASV failed : " + serverAnswer); + } + // Get port number out of group 2 & 3 + port = Integer.parseInt(m.group(3)) + (Integer.parseInt(m.group(2)) << 8); + // IP address is simple + String s = m.group(1).replace(',', '.'); + dest = new InetSocketAddress(s, port); + } + // Got everything, let's open the socket! + Socket s; + if (proxy != null) { + if (proxy.type() == Proxy.Type.SOCKS) { + s = AccessController.doPrivileged( + new PrivilegedAction() { + + public Socket run() { + return new Socket(proxy); + } + }); + } else { + s = new Socket(Proxy.NO_PROXY); + } + } else { + s = new Socket(); + } + + InetAddress serverAddress = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public InetAddress run() { + return server.getLocalAddress(); + } + }); + + // Bind the socket to the same address as the control channel. This + // is needed in case of multi-homed systems. + s.bind(new InetSocketAddress(serverAddress, 0)); + if (connectTimeout >= 0) { + s.connect(dest, connectTimeout); + } else { + if (defaultConnectTimeout > 0) { + s.connect(dest, defaultConnectTimeout); + } else { + s.connect(dest); + } + } + if (readTimeout >= 0) { + s.setSoTimeout(readTimeout); + } else if (defaultSoTimeout > 0) { + s.setSoTimeout(defaultSoTimeout); + } + if (useCrypto) { + try { + s = sslFact.createSocket(s, dest.getHostName(), dest.getPort(), true); + } catch (Exception e) { + throw new FtpProtocolException("Can't open secure data channel: " + e); + } + } + if (!issueCommand(cmd)) { + s.close(); + if (getLastReplyCode() == FtpReplyCode.FILE_UNAVAILABLE) { + // Ensure backward compatibility + throw new FileNotFoundException(cmd); + } + throw new FtpProtocolException(cmd + ":" + getResponseString(), getLastReplyCode()); + } + return s; + } + + /** + * Opens a data connection with the server according to the set mode + * (ACTIVE or PASSIVE) then send the command passed as an argument. + * + * @param cmd the String containing the command to execute + * @return the connected Socket + * @throws IOException if the connection or command failed + */ + private Socket openDataConnection(String cmd) throws FtpProtocolException, IOException { + Socket clientSocket; + + if (passiveMode) { + try { + return openPassiveDataConnection(cmd); + } catch (FtpProtocolException e) { + // If Passive mode failed, fall back on PORT + // Otherwise throw exception + String errmsg = e.getMessage(); + if (!errmsg.startsWith("PASV") && !errmsg.startsWith("EPSV")) { + throw e; + } + } + } + ServerSocket portSocket; + InetAddress myAddress; + String portCmd; + + if (proxy != null && proxy.type() == Proxy.Type.SOCKS) { + // We're behind a firewall and the passive mode fail, + // since we can't accept a connection through SOCKS (yet) + // throw an exception + throw new FtpProtocolException("Passive mode failed"); + } + // Bind the ServerSocket to the same address as the control channel + // This is needed for multi-homed systems + portSocket = new ServerSocket(0, 1, server.getLocalAddress()); + try { + myAddress = portSocket.getInetAddress(); + if (myAddress.isAnyLocalAddress()) { + myAddress = server.getLocalAddress(); + } + // Let's try the new, IPv6 compatible EPRT command + // See RFC2428 for specifics + // Some FTP servers (like the one on Solaris) are bugged, they + // will accept the EPRT command but then, the subsequent command + // (e.g. RETR) will fail, so we have to check BOTH results (the + // EPRT cmd then the actual command) to decide whether we should + // fall back on the older PORT command. + portCmd = "EPRT |" + ((myAddress instanceof Inet6Address) ? "2" : "1") + "|" + + myAddress.getHostAddress() + "|" + portSocket.getLocalPort() + "|"; + if (!issueCommand(portCmd) || !issueCommand(cmd)) { + // The EPRT command failed, let's fall back to good old PORT + portCmd = "PORT "; + byte[] addr = myAddress.getAddress(); + + /* append host addr */ + for (int i = 0; i < addr.length; i++) { + portCmd = portCmd + (addr[i] & 0xFF) + ","; + } + + /* append port number */ + portCmd = portCmd + ((portSocket.getLocalPort() >>> 8) & 0xff) + "," + (portSocket.getLocalPort() & 0xff); + issueCommandCheck(portCmd); + issueCommandCheck(cmd); + } + // Either the EPRT or the PORT command was successful + // Let's create the client socket + if (connectTimeout >= 0) { + portSocket.setSoTimeout(connectTimeout); + } else { + if (defaultConnectTimeout > 0) { + portSocket.setSoTimeout(defaultConnectTimeout); + } + } + clientSocket = portSocket.accept(); + if (readTimeout >= 0) { + clientSocket.setSoTimeout(readTimeout); + } else { + if (defaultSoTimeout > 0) { + clientSocket.setSoTimeout(defaultSoTimeout); + } + } + } finally { + portSocket.close(); + } + if (useCrypto) { + try { + clientSocket = sslFact.createSocket(clientSocket, serverAddr.getHostName(), serverAddr.getPort(), true); + } catch (Exception ex) { + throw new IOException(ex.getLocalizedMessage()); + } + } + return clientSocket; + } + + private InputStream createInputStream(InputStream in) { + if (type == TransferType.ASCII) { + return new sun.net.TelnetInputStream(in, false); + } + return in; + } + + private OutputStream createOutputStream(OutputStream out) { + if (type == TransferType.ASCII) { + return new sun.net.TelnetOutputStream(out, false); + } + return out; + } + + /** + * Creates an instance of FtpClient. The client is not connected to any + * server yet. + * + */ + protected FtpClient() { + } + + /** + * Creates an instance of FtpClient. The client is not connected to any + * server yet. + * + */ + public static sun.net.ftp.FtpClient create() { + return new FtpClient(); + } + + /** + * Set the transfer mode to passive. In that mode, data connections + * are established by having the client connect to the server. + * This is the recommended default mode as it will work best through + * firewalls and NATs. + * + * @return This FtpClient + * @see #setActiveMode() + */ + public sun.net.ftp.FtpClient enablePassiveMode(boolean passive) { + + // Only passive mode used in JDK. See Bug 8010784. + // passiveMode = passive; + return this; + } + + /** + * Gets the current transfer mode. + * + * @return the current FtpTransferMode + */ + public boolean isPassiveModeEnabled() { + return passiveMode; + } + + /** + * Sets the timeout value to use when connecting to the server, + * + * @param timeout the timeout value, in milliseconds, to use for the connect + * operation. A value of zero or less, means use the default timeout. + * + * @return This FtpClient + */ + public sun.net.ftp.FtpClient setConnectTimeout(int timeout) { + connectTimeout = timeout; + return this; + } + + /** + * Returns the current connection timeout value. + * + * @return the value, in milliseconds, of the current connect timeout. + * @see #setConnectTimeout(int) + */ + public int getConnectTimeout() { + return connectTimeout; + } + + /** + * Sets the timeout value to use when reading from the server, + * + * @param timeout the timeout value, in milliseconds, to use for the read + * operation. A value of zero or less, means use the default timeout. + * @return This FtpClient + */ + public sun.net.ftp.FtpClient setReadTimeout(int timeout) { + readTimeout = timeout; + return this; + } + + /** + * Returns the current read timeout value. + * + * @return the value, in milliseconds, of the current read timeout. + * @see #setReadTimeout(int) + */ + public int getReadTimeout() { + return readTimeout; + } + + public sun.net.ftp.FtpClient setProxy(Proxy p) { + proxy = p; + return this; + } + + /** + * Get the proxy of this FtpClient + * + * @return the Proxy, this client is using, or null + * if none is used. + * @see #setProxy(Proxy) + */ + public Proxy getProxy() { + return proxy; + } + + /** + * Connects to the specified destination. + * + * @param dest the InetSocketAddress to connect to. + * @throws IOException if the connection fails. + */ + private void tryConnect(InetSocketAddress dest, int timeout) throws IOException { + if (isConnected()) { + disconnect(); + } + server = doConnect(dest, timeout); + try { + out = new PrintStream(new BufferedOutputStream(server.getOutputStream()), + true, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding + "encoding not found", e); + } + in = new BufferedInputStream(server.getInputStream()); + } + + private Socket doConnect(InetSocketAddress dest, int timeout) throws IOException { + Socket s; + if (proxy != null) { + if (proxy.type() == Proxy.Type.SOCKS) { + s = AccessController.doPrivileged( + new PrivilegedAction() { + + public Socket run() { + return new Socket(proxy); + } + }); + } else { + s = new Socket(Proxy.NO_PROXY); + } + } else { + s = new Socket(); + } + // Instance specific timeouts do have priority, that means + // connectTimeout & readTimeout (-1 means not set) + // Then global default timeouts + // Then no timeout. + if (timeout >= 0) { + s.connect(dest, timeout); + } else { + if (connectTimeout >= 0) { + s.connect(dest, connectTimeout); + } else { + if (defaultConnectTimeout > 0) { + s.connect(dest, defaultConnectTimeout); + } else { + s.connect(dest); + } + } + } + if (readTimeout >= 0) { + s.setSoTimeout(readTimeout); + } else if (defaultSoTimeout > 0) { + s.setSoTimeout(defaultSoTimeout); + } + return s; + } + + private void disconnect() throws IOException { + if (isConnected()) { + server.close(); + } + server = null; + in = null; + out = null; + lastTransSize = -1; + lastFileName = null; + restartOffset = 0; + welcomeMsg = null; + lastReplyCode = null; + serverResponse.setSize(0); + } + + /** + * Tests whether this client is connected or not to a server. + * + * @return true if the client is connected. + */ + public boolean isConnected() { + return server != null; + } + + public SocketAddress getServerAddress() { + return server == null ? null : server.getRemoteSocketAddress(); + } + + public sun.net.ftp.FtpClient connect(SocketAddress dest) throws FtpProtocolException, IOException { + return connect(dest, -1); + } + + /** + * Connects the FtpClient to the specified destination. + * + * @param dest the address of the destination server + * @throws IOException if connection failed. + */ + public sun.net.ftp.FtpClient connect(SocketAddress dest, int timeout) throws FtpProtocolException, IOException { + if (!(dest instanceof InetSocketAddress)) { + throw new IllegalArgumentException("Wrong address type"); + } + serverAddr = (InetSocketAddress) dest; + tryConnect(serverAddr, timeout); + if (!readReply()) { + throw new FtpProtocolException("Welcome message: " + + getResponseString(), lastReplyCode); + } + welcomeMsg = getResponseString().substring(4); + return this; + } + + private void tryLogin(String user, char[] password) throws FtpProtocolException, IOException { + issueCommandCheck("USER " + user); + + /* + * Checks for "331 User name okay, need password." answer + */ + if (lastReplyCode == FtpReplyCode.NEED_PASSWORD) { + if ((password != null) && (password.length > 0)) { + issueCommandCheck("PASS " + String.valueOf(password)); + } + } + } + + /** + * Attempts to log on the server with the specified user name and password. + * + * @param user The user name + * @param password The password for that user + * @return true if the login was successful. + * @throws IOException if an error occurred during the transmission + */ + public sun.net.ftp.FtpClient login(String user, char[] password) throws FtpProtocolException, IOException { + if (!isConnected()) { + throw new FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE); + } + if (user == null || user.length() == 0) { + throw new IllegalArgumentException("User name can't be null or empty"); + } + tryLogin(user, password); + + // keep the welcome message around so we can + // put it in the resulting HTML page. + String l; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < serverResponse.size(); i++) { + l = serverResponse.elementAt(i); + if (l != null) { + if (l.length() >= 4 && l.startsWith("230")) { + // get rid of the "230-" prefix + l = l.substring(4); + } + sb.append(l); + } + } + welcomeMsg = sb.toString(); + loggedIn = true; + return this; + } + + /** + * Attempts to log on the server with the specified user name, password and + * account name. + * + * @param user The user name + * @param password The password for that user. + * @param account The account name for that user. + * @return true if the login was successful. + * @throws IOException if an error occurs during the transmission. + */ + public sun.net.ftp.FtpClient login(String user, char[] password, String account) throws FtpProtocolException, IOException { + + if (!isConnected()) { + throw new FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE); + } + if (user == null || user.length() == 0) { + throw new IllegalArgumentException("User name can't be null or empty"); + } + tryLogin(user, password); + + /* + * Checks for "332 Need account for login." answer + */ + if (lastReplyCode == FtpReplyCode.NEED_ACCOUNT) { + issueCommandCheck("ACCT " + account); + } + + // keep the welcome message around so we can + // put it in the resulting HTML page. + StringBuffer sb = new StringBuffer(); + if (serverResponse != null) { + for (String l : serverResponse) { + if (l != null) { + if (l.length() >= 4 && l.startsWith("230")) { + // get rid of the "230-" prefix + l = l.substring(4); + } + sb.append(l); + } + } + } + welcomeMsg = sb.toString(); + loggedIn = true; + return this; + } + + /** + * Logs out the current user. This is in effect terminates the current + * session and the connection to the server will be closed. + * + */ + public void close() throws IOException { + if (isConnected()) { + issueCommand("QUIT"); + loggedIn = false; + } + disconnect(); + } + + /** + * Checks whether the client is logged in to the server or not. + * + * @return true if the client has already completed a login. + */ + public boolean isLoggedIn() { + return loggedIn; + } + + /** + * Changes to a specific directory on a remote FTP server + * + * @param remoteDirectory path of the directory to CD to. + * @return true if the operation was successful. + * @exception FtpProtocolException + */ + public sun.net.ftp.FtpClient changeDirectory(String remoteDirectory) throws FtpProtocolException, IOException { + if (remoteDirectory == null || "".equals(remoteDirectory)) { + throw new IllegalArgumentException("directory can't be null or empty"); + } + + issueCommandCheck("CWD " + remoteDirectory); + return this; + } + + /** + * Changes to the parent directory, sending the CDUP command to the server. + * + * @return true if the command was successful. + * @throws IOException + */ + public sun.net.ftp.FtpClient changeToParentDirectory() throws FtpProtocolException, IOException { + issueCommandCheck("CDUP"); + return this; + } + + /** + * Returns the server current working directory, or null if + * the PWD command failed. + * + * @return a String containing the current working directory, + * or null + * @throws IOException + */ + public String getWorkingDirectory() throws FtpProtocolException, IOException { + issueCommandCheck("PWD"); + /* + * answer will be of the following format : + * + * 257 "/" is current directory. + */ + String answ = getResponseString(); + if (!answ.startsWith("257")) { + return null; + } + return answ.substring(5, answ.lastIndexOf('"')); + } + + /** + * Sets the restart offset to the specified value. That value will be + * sent through a REST command to server before a file + * transfer and has the effect of resuming a file transfer from the + * specified point. After a transfer the restart offset is set back to + * zero. + * + * @param offset the offset in the remote file at which to start the next + * transfer. This must be a value greater than or equal to zero. + * @throws IllegalArgumentException if the offset is negative. + */ + public sun.net.ftp.FtpClient setRestartOffset(long offset) { + if (offset < 0) { + throw new IllegalArgumentException("offset can't be negative"); + } + restartOffset = offset; + return this; + } + + /** + * Retrieves a file from the ftp server and writes it to the specified + * OutputStream. + * If the restart offset was set, then a REST command will be + * sent before the RETR in order to restart the tranfer from the specified + * offset. + * The OutputStream is not closed by this method at the end + * of the transfer. + * + * @param name a String containing the name of the file to + * retreive from the server. + * @param local the OutputStream the file should be written to. + * @throws IOException if the transfer fails. + */ + public sun.net.ftp.FtpClient getFile(String name, OutputStream local) throws FtpProtocolException, IOException { + int mtu = 1500; + if (restartOffset > 0) { + Socket s; + try { + s = openDataConnection("REST " + restartOffset); + } finally { + restartOffset = 0; + } + issueCommandCheck("RETR " + name); + getTransferSize(); + InputStream remote = createInputStream(s.getInputStream()); + byte[] buf = new byte[mtu * 10]; + int l; + while ((l = remote.read(buf)) >= 0) { + if (l > 0) { + local.write(buf, 0, l); + } + } + remote.close(); + } else { + Socket s = openDataConnection("RETR " + name); + getTransferSize(); + InputStream remote = createInputStream(s.getInputStream()); + byte[] buf = new byte[mtu * 10]; + int l; + while ((l = remote.read(buf)) >= 0) { + if (l > 0) { + local.write(buf, 0, l); + } + } + remote.close(); + } + return completePending(); + } + + /** + * Retrieves a file from the ftp server, using the RETR command, and + * returns the InputStream from* the established data connection. + * {@link #completePending()} has to be called once the application + * is done reading from the returned stream. + * + * @param name the name of the remote file + * @return the {@link InputStream} from the data connection, or + * null if the command was unsuccessful. + * @throws IOException if an error occurred during the transmission. + */ + public InputStream getFileStream(String name) throws FtpProtocolException, IOException { + Socket s; + if (restartOffset > 0) { + try { + s = openDataConnection("REST " + restartOffset); + } finally { + restartOffset = 0; + } + if (s == null) { + return null; + } + issueCommandCheck("RETR " + name); + getTransferSize(); + return createInputStream(s.getInputStream()); + } + + s = openDataConnection("RETR " + name); + if (s == null) { + return null; + } + getTransferSize(); + return createInputStream(s.getInputStream()); + } + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR or STOU command, depending on the + * unique argument, and returns the OutputStream + * from the established data connection. + * {@link #completePending()} has to be called once the application + * is finished writing to the stream. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + * If unique is set to true, the resultant file + * is to be created under a name unique to that directory, meaning + * it will not overwrite an existing file, instead the server will + * generate a new, unique, file name. + * The name of the remote file can be retrieved, after completion of the + * transfer, by calling {@link #getLastFileName()}. + * + * @param name the name of the remote file to write. + * @param unique true if the remote files should be unique, + * in which case the STOU command will be used. + * @return the {@link OutputStream} from the data connection or + * null if the command was unsuccessful. + * @throws IOException if an error occurred during the transmission. + */ + public OutputStream putFileStream(String name, boolean unique) + throws FtpProtocolException, IOException + { + String cmd = unique ? "STOU " : "STOR "; + Socket s = openDataConnection(cmd + name); + if (s == null) { + return null; + } + boolean bm = (type == TransferType.BINARY); + return new sun.net.TelnetOutputStream(s.getOutputStream(), bm); + } + + /** + * Transfers a file from the client to the server (aka a put) + * by sending the STOR command. The content of the InputStream + * passed in argument is written into the remote file, overwriting any + * existing data. + * + * A new file is created at the server site if the file specified does + * not already exist. + * + * @param name the name of the remote file to write. + * @param local the InputStream that points to the data to + * transfer. + * @param unique true if the remote file should be unique + * (i.e. not already existing), false otherwise. + * @return true if the transfer was successful. + * @throws IOException if an error occurred during the transmission. + * @see #getLastFileName() + */ + public sun.net.ftp.FtpClient putFile(String name, InputStream local, boolean unique) throws FtpProtocolException, IOException { + String cmd = unique ? "STOU " : "STOR "; + int mtu = 1500; + if (type == TransferType.BINARY) { + Socket s = openDataConnection(cmd + name); + OutputStream remote = createOutputStream(s.getOutputStream()); + byte[] buf = new byte[mtu * 10]; + int l; + while ((l = local.read(buf)) >= 0) { + if (l > 0) { + remote.write(buf, 0, l); + } + } + remote.close(); + } + return completePending(); + } + + /** + * Sends the APPE command to the server in order to transfer a data stream + * passed in argument and append it to the content of the specified remote + * file. + * + * @param name A String containing the name of the remote file + * to append to. + * @param local The InputStream providing access to the data + * to be appended. + * @return true if the transfer was successful. + * @throws IOException if an error occurred during the transmission. + */ + public sun.net.ftp.FtpClient appendFile(String name, InputStream local) throws FtpProtocolException, IOException { + int mtu = 1500; + Socket s = openDataConnection("APPE " + name); + OutputStream remote = createOutputStream(s.getOutputStream()); + byte[] buf = new byte[mtu * 10]; + int l; + while ((l = local.read(buf)) >= 0) { + if (l > 0) { + remote.write(buf, 0, l); + } + } + remote.close(); + return completePending(); + } + + /** + * Renames a file on the server. + * + * @param from the name of the file being renamed + * @param to the new name for the file + * @throws IOException if the command fails + */ + public sun.net.ftp.FtpClient rename(String from, String to) throws FtpProtocolException, IOException { + issueCommandCheck("RNFR " + from); + issueCommandCheck("RNTO " + to); + return this; + } + + /** + * Deletes a file on the server. + * + * @param name a String containing the name of the file + * to delete. + * @return true if the command was successful + * @throws IOException if an error occurred during the exchange + */ + public sun.net.ftp.FtpClient deleteFile(String name) throws FtpProtocolException, IOException { + issueCommandCheck("DELE " + name); + return this; + } + + /** + * Creates a new directory on the server. + * + * @param name a String containing the name of the directory + * to create. + * @return true if the operation was successful. + * @throws IOException if an error occurred during the exchange + */ + public sun.net.ftp.FtpClient makeDirectory(String name) throws FtpProtocolException, IOException { + issueCommandCheck("MKD " + name); + return this; + } + + /** + * Removes a directory on the server. + * + * @param name a String containing the name of the directory + * to remove. + * + * @return true if the operation was successful. + * @throws IOException if an error occurred during the exchange. + */ + public sun.net.ftp.FtpClient removeDirectory(String name) throws FtpProtocolException, IOException { + issueCommandCheck("RMD " + name); + return this; + } + + /** + * Sends a No-operation command. It's useful for testing the connection + * status or as a keep alive mechanism. + * + * @throws FtpProtocolException if the command fails + */ + public sun.net.ftp.FtpClient noop() throws FtpProtocolException, IOException { + issueCommandCheck("NOOP"); + return this; + } + + /** + * Sends the STAT command to the server. + * This can be used while a data connection is open to get a status + * on the current transfer, in that case the parameter should be + * null. + * If used between file transfers, it may have a pathname as argument + * in which case it will work as the LIST command except no data + * connection will be created. + * + * @param name an optional String containing the pathname + * the STAT command should apply to. + * @return the response from the server or null if the + * command failed. + * @throws IOException if an error occurred during the transmission. + */ + public String getStatus(String name) throws FtpProtocolException, IOException { + issueCommandCheck((name == null ? "STAT" : "STAT " + name)); + /* + * A typical response will be: + * 213-status of t32.gif: + * -rw-r--r-- 1 jcc staff 247445 Feb 17 1998 t32.gif + * 213 End of Status + * + * or + * + * 211-jsn FTP server status: + * Version wu-2.6.2+Sun + * Connected to localhost (::1) + * Logged in as jccollet + * TYPE: ASCII, FORM: Nonprint; STRUcture: File; transfer MODE: Stream + * No data connection + * 0 data bytes received in 0 files + * 0 data bytes transmitted in 0 files + * 0 data bytes total in 0 files + * 53 traffic bytes received in 0 transfers + * 485 traffic bytes transmitted in 0 transfers + * 587 traffic bytes total in 0 transfers + * 211 End of status + * + * So we need to remove the 1st and last line + */ + Vector resp = getResponseStrings(); + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < resp.size() - 1; i++) { + sb.append(resp.get(i)); + } + return sb.toString(); + } + + /** + * Sends the FEAT command to the server and returns the list of supported + * features in the form of strings. + * + * The features are the supported commands, like AUTH TLS, PROT or PASV. + * See the RFCs for a complete list. + * + * Note that not all FTP servers support that command, in which case + * the method will return null + * + * @return a List of Strings describing the + * supported additional features, or null + * if the command is not supported. + * @throws IOException if an error occurs during the transmission. + */ + public List getFeatures() throws FtpProtocolException, IOException { + /* + * The FEAT command, when implemented will return something like: + * + * 211-Features: + * AUTH TLS + * PBSZ + * PROT + * EPSV + * EPRT + * PASV + * REST STREAM + * 211 END + */ + ArrayList features = new ArrayList(); + issueCommandCheck("FEAT"); + Vector resp = getResponseStrings(); + // Note that we start at index 1 to skip the 1st line (211-...) + // and we stop before the last line. + for (int i = 1; i < resp.size() - 1; i++) { + String s = resp.get(i); + // Get rid of leading space and trailing newline + features.add(s.substring(1, s.length() - 1)); + } + return features; + } + + /** + * sends the ABOR command to the server. + * It tells the server to stop the previous command or transfer. + * + * @return true if the command was successful. + * @throws IOException if an error occurred during the transmission. + */ + public sun.net.ftp.FtpClient abort() throws FtpProtocolException, IOException { + issueCommandCheck("ABOR"); + // TODO: Must check the ReplyCode: + /* + * From the RFC: + * There are two cases for the server upon receipt of this + * command: (1) the FTP service command was already completed, + * or (2) the FTP service command is still in progress. + * In the first case, the server closes the data connection + * (if it is open) and responds with a 226 reply, indicating + * that the abort command was successfully processed. + * In the second case, the server aborts the FTP service in + * progress and closes the data connection, returning a 426 + * reply to indicate that the service request terminated + * abnormally. The server then sends a 226 reply, + * indicating that the abort command was successfully + * processed. + */ + + + return this; + } + + /** + * Some methods do not wait until completion before returning, so this + * method can be called to wait until completion. This is typically the case + * with commands that trigger a transfer like {@link #getFileStream(String)}. + * So this method should be called before accessing information related to + * such a command. + *

    This method will actually block reading on the command channel for a + * notification from the server that the command is finished. Such a + * notification often carries extra information concerning the completion + * of the pending action (e.g. number of bytes transfered).

    + *

    Note that this will return true immediately if no command or action + * is pending

    + *

    It should be also noted that most methods issuing commands to the ftp + * server will call this method if a previous command is pending. + *

    Example of use: + *

    +     * InputStream in = cl.getFileStream("file");
    +     * ...
    +     * cl.completePending();
    +     * long size = cl.getLastTransferSize();
    +     * 
    + * On the other hand, it's not necessary in a case like: + *
    +     * InputStream in = cl.getFileStream("file");
    +     * // read content
    +     * ...
    +     * cl.logout();
    +     * 
    + *

    Since {@link #logout()} will call completePending() if necessary.

    + * @return true if the completion was successful or if no + * action was pending. + * @throws IOException + */ + public sun.net.ftp.FtpClient completePending() throws FtpProtocolException, IOException { + while (replyPending) { + replyPending = false; + if (!readReply()) { + throw new FtpProtocolException(getLastResponseString(), lastReplyCode); + } + } + return this; + } + + /** + * Reinitializes the USER parameters on the FTP server + * + * @throws FtpProtocolException if the command fails + */ + public sun.net.ftp.FtpClient reInit() throws FtpProtocolException, IOException { + issueCommandCheck("REIN"); + loggedIn = false; + if (useCrypto) { + if (server instanceof SSLSocket) { + javax.net.ssl.SSLSession session = ((SSLSocket) server).getSession(); + session.invalidate(); + // Restore previous socket and streams + server = oldSocket; + oldSocket = null; + try { + out = new PrintStream(new BufferedOutputStream(server.getOutputStream()), + true, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding + "encoding not found", e); + } + in = new BufferedInputStream(server.getInputStream()); + } + } + useCrypto = false; + return this; + } + + /** + * Changes the transfer type (binary, ascii, ebcdic) and issue the + * proper command (e.g. TYPE A) to the server. + * + * @param type the FtpTransferType to use. + * @return This FtpClient + * @throws IOException if an error occurs during transmission. + */ + public sun.net.ftp.FtpClient setType(TransferType type) throws FtpProtocolException, IOException { + String cmd = "NOOP"; + + this.type = type; + if (type == TransferType.ASCII) { + cmd = "TYPE A"; + } + if (type == TransferType.BINARY) { + cmd = "TYPE I"; + } + if (type == TransferType.EBCDIC) { + cmd = "TYPE E"; + } + issueCommandCheck(cmd); + return this; + } + + /** + * Issues a LIST command to the server to get the current directory + * listing, and returns the InputStream from the data connection. + * {@link #completePending()} has to be called once the application + * is finished writing to the stream. + * + * @param path the pathname of the directory to list, or null + * for the current working directory. + * @return the InputStream from the resulting data connection + * @throws IOException if an error occurs during the transmission. + * @see #changeDirectory(String) + * @see #listFiles(String) + */ + public InputStream list(String path) throws FtpProtocolException, IOException { + Socket s; + s = openDataConnection(path == null ? "LIST" : "LIST " + path); + if (s != null) { + return createInputStream(s.getInputStream()); + } + return null; + } + + /** + * Issues a NLST path command to server to get the specified directory + * content. It differs from {@link #list(String)} method by the fact that + * it will only list the file names which would make the parsing of the + * somewhat easier. + * + * {@link #completePending()} has to be called once the application + * is finished writing to the stream. + * + * @param path a String containing the pathname of the + * directory to list or null for the current working + * directory. + * @return the InputStream from the resulting data connection + * @throws IOException if an error occurs during the transmission. + */ + public InputStream nameList(String path) throws FtpProtocolException, IOException { + Socket s; + s = openDataConnection("NLST " + path); + if (s != null) { + return createInputStream(s.getInputStream()); + } + return null; + } + + /** + * Issues the SIZE [path] command to the server to get the size of a + * specific file on the server. + * Note that this command may not be supported by the server. In which + * case -1 will be returned. + * + * @param path a String containing the pathname of the + * file. + * @return a long containing the size of the file or -1 if + * the server returned an error, which can be checked with + * {@link #getLastReplyCode()}. + * @throws IOException if an error occurs during the transmission. + */ + public long getSize(String path) throws FtpProtocolException, IOException { + if (path == null || path.length() == 0) { + throw new IllegalArgumentException("path can't be null or empty"); + } + issueCommandCheck("SIZE " + path); + if (lastReplyCode == FtpReplyCode.FILE_STATUS) { + String s = getResponseString(); + s = s.substring(4, s.length() - 1); + return Long.parseLong(s); + } + return -1; + } + private static String[] MDTMformats = { + "yyyyMMddHHmmss.SSS", + "yyyyMMddHHmmss" + }; + private static SimpleDateFormat[] dateFormats = new SimpleDateFormat[MDTMformats.length]; + + static { + for (int i = 0; i < MDTMformats.length; i++) { + dateFormats[i] = new SimpleDateFormat(MDTMformats[i]); + dateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT")); + } + } + + /** + * Issues the MDTM [path] command to the server to get the modification + * time of a specific file on the server. + * Note that this command may not be supported by the server, in which + * case null will be returned. + * + * @param path a String containing the pathname of the file. + * @return a Date representing the last modification time + * or null if the server returned an error, which + * can be checked with {@link #getLastReplyCode()}. + * @throws IOException if an error occurs during the transmission. + */ + public Date getLastModified(String path) throws FtpProtocolException, IOException { + issueCommandCheck("MDTM " + path); + if (lastReplyCode == FtpReplyCode.FILE_STATUS) { + String s = getResponseString().substring(4); + Date d = null; + for (SimpleDateFormat dateFormat : dateFormats) { + try { + d = dateFormat.parse(s); + } catch (ParseException ex) { + } + if (d != null) { + return d; + } + } + } + return null; + } + + /** + * Sets the parser used to handle the directory output to the specified + * one. By default the parser is set to one that can handle most FTP + * servers output (Unix base mostly). However it may be necessary for + * and application to provide its own parser due to some uncommon + * output format. + * + * @param p The FtpDirParser to use. + * @see #listFiles(String) + */ + public sun.net.ftp.FtpClient setDirParser(FtpDirParser p) { + parser = p; + return this; + } + + private class FtpFileIterator implements Iterator, Closeable { + + private BufferedReader in = null; + private FtpDirEntry nextFile = null; + private FtpDirParser fparser = null; + private boolean eof = false; + + public FtpFileIterator(FtpDirParser p, BufferedReader in) { + this.in = in; + this.fparser = p; + readNext(); + } + + private void readNext() { + nextFile = null; + if (eof) { + return; + } + String line = null; + try { + do { + line = in.readLine(); + if (line != null) { + nextFile = fparser.parseLine(line); + if (nextFile != null) { + return; + } + } + } while (line != null); + in.close(); + } catch (IOException iOException) { + } + eof = true; + } + + public boolean hasNext() { + return nextFile != null; + } + + public FtpDirEntry next() { + FtpDirEntry ret = nextFile; + readNext(); + return ret; + } + + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void close() throws IOException { + if (in != null && !eof) { + in.close(); + } + eof = true; + nextFile = null; + } + } + + /** + * Issues a MLSD command to the server to get the specified directory + * listing and applies the current parser to create an Iterator of + * {@link java.net.ftp.FtpDirEntry}. Note that the Iterator returned is also a + * {@link Closeable}. + * If the server doesn't support the MLSD command, the LIST command is used + * instead. + * + * {@link #completePending()} has to be called once the application + * is finished iterating through the files. + * + * @param path the pathname of the directory to list or null + * for the current working directoty. + * @return a Iterator of files or null if the + * command failed. + * @throws IOException if an error occurred during the transmission + * @see #setDirParser(FtpDirParser) + * @see #changeDirectory(String) + */ + public Iterator listFiles(String path) throws FtpProtocolException, IOException { + Socket s = null; + BufferedReader sin = null; + try { + s = openDataConnection(path == null ? "MLSD" : "MLSD " + path); + } catch (FtpProtocolException FtpException) { + // The server doesn't understand new MLSD command, ignore and fall + // back to LIST + } + + if (s != null) { + sin = new BufferedReader(new InputStreamReader(s.getInputStream())); + return new FtpFileIterator(mlsxParser, sin); + } else { + s = openDataConnection(path == null ? "LIST" : "LIST " + path); + if (s != null) { + sin = new BufferedReader(new InputStreamReader(s.getInputStream())); + return new FtpFileIterator(parser, sin); + } + } + return null; + } + + private boolean sendSecurityData(byte[] buf) throws IOException { + BASE64Encoder encoder = new BASE64Encoder(); + String s = encoder.encode(buf); + return issueCommand("ADAT " + s); + } + + private byte[] getSecurityData() { + String s = getLastResponseString(); + if (s.substring(4, 9).equalsIgnoreCase("ADAT=")) { + BASE64Decoder decoder = new BASE64Decoder(); + try { + // Need to get rid of the leading '315 ADAT=' + // and the trailing newline + return decoder.decodeBuffer(s.substring(9, s.length() - 1)); + } catch (IOException e) { + // + } + } + return null; + } + + /** + * Attempts to use Kerberos GSSAPI as an authentication mechanism with the + * ftp server. This will issue an AUTH GSSAPI command, and if + * it is accepted by the server, will followup with ADAT + * command to exchange the various tokens until authentification is + * successful. This conforms to Appendix I of RFC 2228. + * + * @return true if authentication was successful. + * @throws IOException if an error occurs during the transmission. + */ + public sun.net.ftp.FtpClient useKerberos() throws FtpProtocolException, IOException { + /* + * Comment out for the moment since it's not in use and would create + * needless cross-package links. + * + issueCommandCheck("AUTH GSSAPI"); + if (lastReplyCode != FtpReplyCode.NEED_ADAT) + throw new sun.net.ftp.FtpProtocolException("Unexpected reply from server"); + try { + GSSManager manager = GSSManager.getInstance(); + GSSName name = manager.createName("SERVICE:ftp@"+ + serverAddr.getHostName(), null); + GSSContext context = manager.createContext(name, null, null, + GSSContext.DEFAULT_LIFETIME); + context.requestMutualAuth(true); + context.requestReplayDet(true); + context.requestSequenceDet(true); + context.requestCredDeleg(true); + byte []inToken = new byte[0]; + while (!context.isEstablished()) { + byte[] outToken + = context.initSecContext(inToken, 0, inToken.length); + // send the output token if generated + if (outToken != null) { + if (sendSecurityData(outToken)) { + inToken = getSecurityData(); + } + } + } + loggedIn = true; + } catch (GSSException e) { + + } + */ + return this; + } + + /** + * Returns the Welcome string the server sent during initial connection. + * + * @return a String containing the message the server + * returned during connection or null. + */ + public String getWelcomeMsg() { + return welcomeMsg; + } + + /** + * Returns the last reply code sent by the server. + * + * @return the lastReplyCode + */ + public FtpReplyCode getLastReplyCode() { + return lastReplyCode; + } + + /** + * Returns the last response string sent by the server. + * + * @return the message string, which can be quite long, last returned + * by the server. + */ + public String getLastResponseString() { + StringBuffer sb = new StringBuffer(); + if (serverResponse != null) { + for (String l : serverResponse) { + if (l != null) { + sb.append(l); + } + } + } + return sb.toString(); + } + + /** + * Returns, when available, the size of the latest started transfer. + * This is retreived by parsing the response string received as an initial + * response to a RETR or similar request. + * + * @return the size of the latest transfer or -1 if either there was no + * transfer or the information was unavailable. + */ + public long getLastTransferSize() { + return lastTransSize; + } + + /** + * Returns, when available, the remote name of the last transfered file. + * This is mainly useful for "put" operation when the unique flag was + * set since it allows to recover the unique file name created on the + * server which may be different from the one submitted with the command. + * + * @return the name the latest transfered file remote name, or + * null if that information is unavailable. + */ + public String getLastFileName() { + return lastFileName; + } + + /** + * Attempts to switch to a secure, encrypted connection. This is done by + * sending the "AUTH TLS" command. + *

    See RFC 4217

    + * If successful this will establish a secure command channel with the + * server, it will also make it so that all other transfers (e.g. a RETR + * command) will be done over an encrypted channel as well unless a + * {@link #reInit()} command or a {@link #endSecureSession()} command is issued. + * + * @return true if the operation was successful. + * @throws IOException if an error occurred during the transmission. + * @see #endSecureSession() + */ + public sun.net.ftp.FtpClient startSecureSession() throws FtpProtocolException, IOException { + if (!isConnected()) { + throw new FtpProtocolException("Not connected yet", FtpReplyCode.BAD_SEQUENCE); + } + if (sslFact == null) { + try { + sslFact = (SSLSocketFactory) SSLSocketFactory.getDefault(); + } catch (Exception e) { + throw new IOException(e.getLocalizedMessage()); + } + } + issueCommandCheck("AUTH TLS"); + Socket s = null; + try { + s = sslFact.createSocket(server, serverAddr.getHostName(), serverAddr.getPort(), true); + } catch (javax.net.ssl.SSLException ssle) { + try { + disconnect(); + } catch (Exception e) { + } + throw ssle; + } + // Remember underlying socket so we can restore it later + oldSocket = server; + server = s; + try { + out = new PrintStream(new BufferedOutputStream(server.getOutputStream()), + true, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding + "encoding not found", e); + } + in = new BufferedInputStream(server.getInputStream()); + + issueCommandCheck("PBSZ 0"); + issueCommandCheck("PROT P"); + useCrypto = true; + return this; + } + + /** + * Sends a CCC command followed by a PROT C + * command to the server terminating an encrypted session and reverting + * back to a non crypted transmission. + * + * @return true if the operation was successful. + * @throws IOException if an error occurred during transmission. + * @see #startSecureSession() + */ + public sun.net.ftp.FtpClient endSecureSession() throws FtpProtocolException, IOException { + if (!useCrypto) { + return this; + } + + issueCommandCheck("CCC"); + issueCommandCheck("PROT C"); + useCrypto = false; + // Restore previous socket and streams + server = oldSocket; + oldSocket = null; + try { + out = new PrintStream(new BufferedOutputStream(server.getOutputStream()), + true, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding + "encoding not found", e); + } + in = new BufferedInputStream(server.getInputStream()); + + return this; + } + + /** + * Sends the "Allocate" (ALLO) command to the server telling it to + * pre-allocate the specified number of bytes for the next transfer. + * + * @param size The number of bytes to allocate. + * @return true if the operation was successful. + * @throws IOException if an error occurred during the transmission. + */ + public sun.net.ftp.FtpClient allocate(long size) throws FtpProtocolException, IOException { + issueCommandCheck("ALLO " + size); + return this; + } + + /** + * Sends the "Structure Mount" (SMNT) command to the server. This let the + * user mount a different file system data structure without altering his + * login or accounting information. + * + * @param struct a String containing the name of the + * structure to mount. + * @return true if the operation was successful. + * @throws IOException if an error occurred during the transmission. + */ + public sun.net.ftp.FtpClient structureMount(String struct) throws FtpProtocolException, IOException { + issueCommandCheck("SMNT " + struct); + return this; + } + + /** + * Sends a SYST (System) command to the server and returns the String + * sent back by the server describing the operating system at the + * server. + * + * @return a String describing the OS, or null + * if the operation was not successful. + * @throws IOException if an error occurred during the transmission. + */ + public String getSystem() throws FtpProtocolException, IOException { + issueCommandCheck("SYST"); + /* + * 215 UNIX Type: L8 Version: SUNOS + */ + String resp = getResponseString(); + // Get rid of the leading code and blank + return resp.substring(4); + } + + /** + * Sends the HELP command to the server, with an optional command, like + * SITE, and returns the text sent back by the server. + * + * @param cmd the command for which the help is requested or + * null for the general help + * @return a String containing the text sent back by the + * server, or null if the command failed. + * @throws IOException if an error occurred during transmission + */ + public String getHelp(String cmd) throws FtpProtocolException, IOException { + issueCommandCheck("HELP " + cmd); + /** + * + * HELP + * 214-The following commands are implemented. + * USER EPRT STRU ALLO DELE SYST RMD MDTM ADAT + * PASS EPSV MODE REST CWD STAT PWD PROT + * QUIT LPRT RETR RNFR LIST HELP CDUP PBSZ + * PORT LPSV STOR RNTO NLST NOOP STOU AUTH + * PASV TYPE APPE ABOR SITE MKD SIZE CCC + * 214 Direct comments to ftp-bugs@jsn. + * + * HELP SITE + * 214-The following SITE commands are implemented. + * UMASK HELP GROUPS + * IDLE ALIAS CHECKMETHOD + * CHMOD CDPATH CHECKSUM + * 214 Direct comments to ftp-bugs@jsn. + */ + Vector resp = getResponseStrings(); + if (resp.size() == 1) { + // Single line response + return resp.get(0).substring(4); + } + // on multiple lines answers, like the ones above, remove 1st and last + // line, concat the the others. + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < resp.size() - 1; i++) { + sb.append(resp.get(i).substring(3)); + } + return sb.toString(); + } + + /** + * Sends the SITE command to the server. This is used by the server + * to provide services specific to his system that are essential + * to file transfer. + * + * @param cmd the command to be sent. + * @return true if the command was successful. + * @throws IOException if an error occurred during transmission + */ + public sun.net.ftp.FtpClient siteCmd(String cmd) throws FtpProtocolException, IOException { + issueCommandCheck("SITE " + cmd); + return this; + } +} diff --git a/src/sun/net/httpserver/AuthFilter.java b/src/sun/net/httpserver/AuthFilter.java new file mode 100644 index 00000000..5b270833 --- /dev/null +++ b/src/sun/net/httpserver/AuthFilter.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import com.sun.net.httpserver.*; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import sun.net.www.MessageHeader; +import java.util.*; +import javax.security.auth.*; +import javax.security.auth.callback.*; +import javax.security.auth.login.*; + +public class AuthFilter extends Filter { + + private Authenticator authenticator; + + public AuthFilter (Authenticator authenticator) { + this.authenticator = authenticator; + } + + public String description () { + return "Authentication filter"; + } + + public void setAuthenticator (Authenticator a) { + authenticator = a; + } + + public void consumeInput (HttpExchange t) throws IOException { + InputStream i = t.getRequestBody(); + byte[] b = new byte [4096]; + while (i.read (b) != -1); + i.close (); + } + + /** + * The filter's implementation, which is invoked by the server + */ + public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException + { + if (authenticator != null) { + Authenticator.Result r = authenticator.authenticate (t); + if (r instanceof Authenticator.Success) { + Authenticator.Success s = (Authenticator.Success)r; + ExchangeImpl e = ExchangeImpl.get (t); + e.setPrincipal (s.getPrincipal()); + chain.doFilter (t); + } else if (r instanceof Authenticator.Retry) { + Authenticator.Retry ry = (Authenticator.Retry)r; + consumeInput (t); + t.sendResponseHeaders (ry.getResponseCode(), -1); + } else if (r instanceof Authenticator.Failure) { + Authenticator.Failure f = (Authenticator.Failure)r; + consumeInput (t); + t.sendResponseHeaders (f.getResponseCode(), -1); + } + } else { + chain.doFilter (t); + } + } +} diff --git a/src/sun/net/httpserver/ChunkedInputStream.java b/src/sun/net/httpserver/ChunkedInputStream.java new file mode 100644 index 00000000..38e2d84d --- /dev/null +++ b/src/sun/net/httpserver/ChunkedInputStream.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.net.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +class ChunkedInputStream extends LeftOverInputStream { + ChunkedInputStream (ExchangeImpl t, InputStream src) { + super (t, src); + } + + private int remaining; + + /* true when a chunk header needs to be read */ + + private boolean needToReadHeader = true; + + final static char CR = '\r'; + final static char LF = '\n'; + /* + * Maximum chunk header size of 2KB + 2 bytes for CRLF + */ + private final static int MAX_CHUNK_HEADER_SIZE = 2050; + + private int numeric (char[] arr, int nchars) throws IOException { + assert arr.length >= nchars; + int len = 0; + for (int i=0; i='0' && c <='9') { + val = c - '0'; + } else if (c>='a' && c<= 'f') { + val = c - 'a' + 10; + } else if (c>='A' && c<= 'F') { + val = c - 'A' + 10; + } else { + throw new IOException ("invalid chunk length"); + } + len = len * 16 + val; + } + return len; + } + + /* read the chunk header line and return the chunk length + * any chunk extensions are ignored + */ + private int readChunkHeader () throws IOException { + boolean gotCR = false; + int c; + char[] len_arr = new char [16]; + int len_size = 0; + boolean end_of_len = false; + int read = 0; + + while ((c=in.read())!= -1) { + char ch = (char) c; + read++; + if ((len_size == len_arr.length -1) || + (read > MAX_CHUNK_HEADER_SIZE)) + { + throw new IOException ("invalid chunk header"); + } + if (gotCR) { + if (ch == LF) { + int l = numeric (len_arr, len_size); + return l; + } else { + gotCR = false; + } + if (!end_of_len) { + len_arr[len_size++] = ch; + } + } else { + if (ch == CR) { + gotCR = true; + } else if (ch == ';') { + end_of_len = true; + } else if (!end_of_len) { + len_arr[len_size++] = ch; + } + } + } + throw new IOException ("end of stream reading chunk header"); + } + + protected int readImpl (byte[]b, int off, int len) throws IOException { + if (eof) { + return -1; + } + if (needToReadHeader) { + remaining = readChunkHeader(); + if (remaining == 0) { + eof = true; + consumeCRLF(); + t.getServerImpl().requestCompleted (t.getConnection()); + return -1; + } + needToReadHeader = false; + } + if (len > remaining) { + len = remaining; + } + int n = in.read(b, off, len); + if (n > -1) { + remaining -= n; + } + if (remaining == 0) { + needToReadHeader = true; + consumeCRLF(); + } + return n; + } + + private void consumeCRLF () throws IOException { + char c; + c = (char)in.read(); /* CR */ + if (c != CR) { + throw new IOException ("invalid chunk end"); + } + c = (char)in.read(); /* LF */ + if (c != LF) { + throw new IOException ("invalid chunk end"); + } + } + + /** + * returns the number of bytes available to read in the current chunk + * which may be less than the real amount, but we'll live with that + * limitation for the moment. It only affects potential efficiency + * rather than correctness. + */ + public int available () throws IOException { + if (eof || closed) { + return 0; + } + int n = in.available(); + return n > remaining? remaining: n; + } + + /* called after the stream is closed to see if bytes + * have been read from the underlying channel + * and buffered internally + */ + public boolean isDataBuffered () throws IOException { + assert eof; + return in.available() > 0; + } + + public boolean markSupported () {return false;} + + public void mark (int l) { + } + + public void reset () throws IOException { + throw new IOException ("mark/reset not supported"); + } +} diff --git a/src/sun/net/httpserver/ChunkedOutputStream.java b/src/sun/net/httpserver/ChunkedOutputStream.java new file mode 100644 index 00000000..b79dc75b --- /dev/null +++ b/src/sun/net/httpserver/ChunkedOutputStream.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.net.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +/** + * a class which allows the caller to write an arbitrary + * number of bytes to an underlying stream. + * normal close() does not close the underlying stream + * + * This class is buffered. + * + * Each chunk is written in one go as :- + * abcd\r\nxxxxxxxxxxxxxx\r\n + * + * abcd is the chunk-size, and xxx is the chunk data + * If the length is less than 4 chars (in size) then the buffer + * is written with an offset. + * Final chunk is: + * 0\r\n\r\n + */ + +class ChunkedOutputStream extends FilterOutputStream +{ + private boolean closed = false; + /* max. amount of user data per chunk */ + final static int CHUNK_SIZE = 4096; + /* allow 4 bytes for chunk-size plus 4 for CRLFs */ + final static int OFFSET = 6; /* initial <=4 bytes for len + CRLF */ + private int pos = OFFSET; + private int count = 0; + private byte[] buf = new byte [CHUNK_SIZE+OFFSET+2]; + ExchangeImpl t; + + ChunkedOutputStream (ExchangeImpl t, OutputStream src) { + super (src); + this.t = t; + } + + public void write (int b) throws IOException { + if (closed) { + throw new StreamClosedException (); + } + buf [pos++] = (byte)b; + count ++; + if (count == CHUNK_SIZE) { + writeChunk(); + } + assert count < CHUNK_SIZE; + } + + public void write (byte[]b, int off, int len) throws IOException { + if (closed) { + throw new StreamClosedException (); + } + int remain = CHUNK_SIZE - count; + if (len > remain) { + System.arraycopy (b,off,buf,pos,remain); + count = CHUNK_SIZE; + writeChunk(); + len -= remain; + off += remain; + while (len >= CHUNK_SIZE) { + System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE); + len -= CHUNK_SIZE; + off += CHUNK_SIZE; + count = CHUNK_SIZE; + writeChunk(); + } + } + if (len > 0) { + System.arraycopy (b,off,buf,pos,len); + count += len; + pos += len; + } + if (count == CHUNK_SIZE) { + writeChunk(); + } + } + + /** + * write out a chunk , and reset the pointers + * chunk does not have to be CHUNK_SIZE bytes + * count must == number of user bytes (<= CHUNK_SIZE) + */ + private void writeChunk () throws IOException { + char[] c = Integer.toHexString (count).toCharArray(); + int clen = c.length; + int startByte = 4 - clen; + int i; + for (i=0; i 0) { + writeChunk(); + } + out.flush(); + } +} diff --git a/src/sun/net/httpserver/Code.java b/src/sun/net/httpserver/Code.java new file mode 100644 index 00000000..032dbb79 --- /dev/null +++ b/src/sun/net/httpserver/Code.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +class Code { + + public static final int HTTP_CONTINUE = 100; + public static final int HTTP_OK = 200; + public static final int HTTP_CREATED = 201; + public static final int HTTP_ACCEPTED = 202; + public static final int HTTP_NOT_AUTHORITATIVE = 203; + public static final int HTTP_NO_CONTENT = 204; + public static final int HTTP_RESET = 205; + public static final int HTTP_PARTIAL = 206; + public static final int HTTP_MULT_CHOICE = 300; + public static final int HTTP_MOVED_PERM = 301; + public static final int HTTP_MOVED_TEMP = 302; + public static final int HTTP_SEE_OTHER = 303; + public static final int HTTP_NOT_MODIFIED = 304; + public static final int HTTP_USE_PROXY = 305; + public static final int HTTP_BAD_REQUEST = 400; + public static final int HTTP_UNAUTHORIZED = 401; + public static final int HTTP_PAYMENT_REQUIRED = 402; + public static final int HTTP_FORBIDDEN = 403; + public static final int HTTP_NOT_FOUND = 404; + public static final int HTTP_BAD_METHOD = 405; + public static final int HTTP_NOT_ACCEPTABLE = 406; + public static final int HTTP_PROXY_AUTH = 407; + public static final int HTTP_CLIENT_TIMEOUT = 408; + public static final int HTTP_CONFLICT = 409; + public static final int HTTP_GONE = 410; + public static final int HTTP_LENGTH_REQUIRED = 411; + public static final int HTTP_PRECON_FAILED = 412; + public static final int HTTP_ENTITY_TOO_LARGE = 413; + public static final int HTTP_REQ_TOO_LONG = 414; + public static final int HTTP_UNSUPPORTED_TYPE = 415; + public static final int HTTP_INTERNAL_ERROR = 500; + public static final int HTTP_NOT_IMPLEMENTED = 501; + public static final int HTTP_BAD_GATEWAY = 502; + public static final int HTTP_UNAVAILABLE = 503; + public static final int HTTP_GATEWAY_TIMEOUT = 504; + public static final int HTTP_VERSION = 505; + + static String msg (int code) { + + switch (code) { + case HTTP_OK: return " OK"; + case HTTP_CONTINUE: return " Continue"; + case HTTP_CREATED: return " Created"; + case HTTP_ACCEPTED: return " Accepted"; + case HTTP_NOT_AUTHORITATIVE: return " Non-Authoritative Information"; + case HTTP_NO_CONTENT: return " No Content"; + case HTTP_RESET: return " Reset Content"; + case HTTP_PARTIAL: return " Partial Content"; + case HTTP_MULT_CHOICE: return " Multiple Choices"; + case HTTP_MOVED_PERM: return " Moved Permanently"; + case HTTP_MOVED_TEMP: return " Temporary Redirect"; + case HTTP_SEE_OTHER: return " See Other"; + case HTTP_NOT_MODIFIED: return " Not Modified"; + case HTTP_USE_PROXY: return " Use Proxy"; + case HTTP_BAD_REQUEST: return " Bad Request"; + case HTTP_UNAUTHORIZED: return " Unauthorized" ; + case HTTP_PAYMENT_REQUIRED: return " Payment Required"; + case HTTP_FORBIDDEN: return " Forbidden"; + case HTTP_NOT_FOUND: return " Not Found"; + case HTTP_BAD_METHOD: return " Method Not Allowed"; + case HTTP_NOT_ACCEPTABLE: return " Not Acceptable"; + case HTTP_PROXY_AUTH: return " Proxy Authentication Required"; + case HTTP_CLIENT_TIMEOUT: return " Request Time-Out"; + case HTTP_CONFLICT: return " Conflict"; + case HTTP_GONE: return " Gone"; + case HTTP_LENGTH_REQUIRED: return " Length Required"; + case HTTP_PRECON_FAILED: return " Precondition Failed"; + case HTTP_ENTITY_TOO_LARGE: return " Request Entity Too Large"; + case HTTP_REQ_TOO_LONG: return " Request-URI Too Large"; + case HTTP_UNSUPPORTED_TYPE: return " Unsupported Media Type"; + case HTTP_INTERNAL_ERROR: return " Internal Server Error"; + case HTTP_NOT_IMPLEMENTED: return " Not Implemented"; + case HTTP_BAD_GATEWAY: return " Bad Gateway"; + case HTTP_UNAVAILABLE: return " Service Unavailable"; + case HTTP_GATEWAY_TIMEOUT: return " Gateway Timeout"; + case HTTP_VERSION: return " HTTP Version Not Supported"; + default: return ""; + } + } +} diff --git a/src/sun/net/httpserver/ContextList.java b/src/sun/net/httpserver/ContextList.java new file mode 100644 index 00000000..929e7bb4 --- /dev/null +++ b/src/sun/net/httpserver/ContextList.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.util.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +class ContextList { + + final static int MAX_CONTEXTS = 50; + + LinkedList list = new LinkedList(); + + public synchronized void add (HttpContextImpl ctx) { + assert ctx.getPath() != null; + list.add (ctx); + } + + public synchronized int size () { + return list.size(); + } + + /* initially contexts are located only by protocol:path. + * Context with longest prefix matches (currently case-sensitive) + */ + synchronized HttpContextImpl findContext (String protocol, String path) { + return findContext (protocol, path, false); + } + + synchronized HttpContextImpl findContext (String protocol, String path, boolean exact) { + protocol = protocol.toLowerCase(); + String longest = ""; + HttpContextImpl lc = null; + for (HttpContextImpl ctx: list) { + if (!ctx.getProtocol().equals(protocol)) { + continue; + } + String cpath = ctx.getPath(); + if (exact && !cpath.equals (path)) { + continue; + } else if (!exact && !path.startsWith(cpath)) { + continue; + } + if (cpath.length() > longest.length()) { + longest = cpath; + lc = ctx; + } + } + return lc; + } + + public synchronized void remove (String protocol, String path) + throws IllegalArgumentException + { + HttpContextImpl ctx = findContext (protocol, path, true); + if (ctx == null) { + throw new IllegalArgumentException ("cannot remove element from list"); + } + list.remove (ctx); + } + + public synchronized void remove (HttpContextImpl context) + throws IllegalArgumentException + { + for (HttpContextImpl ctx: list) { + if (ctx.equals (context)) { + list.remove (ctx); + return; + } + } + throw new IllegalArgumentException ("no such context in list"); + } +} diff --git a/src/sun/net/httpserver/DefaultHttpServerProvider.java b/src/sun/net/httpserver/DefaultHttpServerProvider.java new file mode 100644 index 00000000..fe746bef --- /dev/null +++ b/src/sun/net/httpserver/DefaultHttpServerProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.net.*; +import java.io.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +public class DefaultHttpServerProvider extends HttpServerProvider { + public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException { + return new HttpServerImpl (addr, backlog); + } + + public HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException { + return new HttpsServerImpl (addr, backlog); + } +} diff --git a/src/sun/net/httpserver/Event.java b/src/sun/net/httpserver/Event.java new file mode 100644 index 00000000..6577d5fb --- /dev/null +++ b/src/sun/net/httpserver/Event.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +class Event { + + ExchangeImpl exchange; + + protected Event (ExchangeImpl t) { + this.exchange = t; + } +} diff --git a/src/sun/net/httpserver/ExchangeImpl.java b/src/sun/net/httpserver/ExchangeImpl.java new file mode 100644 index 00000000..fba5189b --- /dev/null +++ b/src/sun/net/httpserver/ExchangeImpl.java @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.net.*; +import javax.net.ssl.*; +import java.util.*; +import java.util.logging.Logger; +import java.text.*; +import com.sun.net.httpserver.*; + +class ExchangeImpl { + + Headers reqHdrs, rspHdrs; + Request req; + String method; + boolean writefinished; + URI uri; + HttpConnection connection; + long reqContentLen; + long rspContentLen; + /* raw streams which access the socket directly */ + InputStream ris; + OutputStream ros; + Thread thread; + /* close the underlying connection when this exchange finished */ + boolean close; + boolean closed; + boolean http10 = false; + + /* for formatting the Date: header */ + private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"; + private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT"); + private static final ThreadLocal dateFormat = + new ThreadLocal() { + @Override protected DateFormat initialValue() { + DateFormat df = new SimpleDateFormat(pattern, Locale.US); + df.setTimeZone(gmtTZ); + return df; + } + }; + + private static final String HEAD = "HEAD"; + + /* streams which take care of the HTTP protocol framing + * and are passed up to higher layers + */ + InputStream uis; + OutputStream uos; + LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper + PlaceholderOutputStream uos_orig; + + boolean sentHeaders; /* true after response headers sent */ + Map attributes; + int rcode = -1; + HttpPrincipal principal; + ServerImpl server; + + ExchangeImpl ( + String m, URI u, Request req, long len, HttpConnection connection + ) throws IOException { + this.req = req; + this.reqHdrs = req.headers(); + this.rspHdrs = new Headers(); + this.method = m; + this.uri = u; + this.connection = connection; + this.reqContentLen = len; + /* ros only used for headers, body written directly to stream */ + this.ros = req.outputStream(); + this.ris = req.inputStream(); + server = getServerImpl(); + server.startExchange(); + } + + public Headers getRequestHeaders () { + return new UnmodifiableHeaders (reqHdrs); + } + + public Headers getResponseHeaders () { + return rspHdrs; + } + + public URI getRequestURI () { + return uri; + } + + public String getRequestMethod (){ + return method; + } + + public HttpContextImpl getHttpContext (){ + return connection.getHttpContext(); + } + + private boolean isHeadRequest() { + return HEAD.equals(getRequestMethod()); + } + + public void close () { + if (closed) { + return; + } + closed = true; + + /* close the underlying connection if, + * a) the streams not set up yet, no response can be sent, or + * b) if the wrapper output stream is not set up, or + * c) if the close of the input/outpu stream fails + */ + try { + if (uis_orig == null || uos == null) { + connection.close(); + return; + } + if (!uos_orig.isWrapped()) { + connection.close(); + return; + } + if (!uis_orig.isClosed()) { + uis_orig.close(); + } + uos.close(); + } catch (IOException e) { + connection.close(); + } + } + + public InputStream getRequestBody () { + if (uis != null) { + return uis; + } + if (reqContentLen == -1L) { + uis_orig = new ChunkedInputStream (this, ris); + uis = uis_orig; + } else { + uis_orig = new FixedLengthInputStream (this, ris, reqContentLen); + uis = uis_orig; + } + return uis; + } + + LeftOverInputStream getOriginalInputStream () { + return uis_orig; + } + + public int getResponseCode () { + return rcode; + } + + public OutputStream getResponseBody () { + /* TODO. Change spec to remove restriction below. Filters + * cannot work with this restriction + * + * if (!sentHeaders) { + * throw new IllegalStateException ("headers not sent"); + * } + */ + if (uos == null) { + uos_orig = new PlaceholderOutputStream (null); + uos = uos_orig; + } + return uos; + } + + + /* returns the place holder stream, which is the stream + * returned from the 1st call to getResponseBody() + * The "real" ouputstream is then placed inside this + */ + PlaceholderOutputStream getPlaceholderResponseBody () { + getResponseBody(); + return uos_orig; + } + + public void sendResponseHeaders (int rCode, long contentLen) + throws IOException + { + if (sentHeaders) { + throw new IOException ("headers already sent"); + } + this.rcode = rCode; + String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n"; + OutputStream tmpout = new BufferedOutputStream (ros); + PlaceholderOutputStream o = getPlaceholderResponseBody(); + tmpout.write (bytes(statusLine, 0), 0, statusLine.length()); + boolean noContentToSend = false; // assume there is content + rspHdrs.set ("Date", dateFormat.get().format (new Date())); + + /* check for response type that is not allowed to send a body */ + + if ((rCode>=100 && rCode <200) /* informational */ + ||(rCode == 204) /* no content */ + ||(rCode == 304)) /* not modified */ + { + if (contentLen != -1) { + Logger logger = server.getLogger(); + String msg = "sendResponseHeaders: rCode = "+ rCode + + ": forcing contentLen = -1"; + logger.warning (msg); + } + contentLen = -1; + } + + if (isHeadRequest()) { + /* HEAD requests should not set a content length by passing it + * through this API, but should instead manually set the required + * headers.*/ + if (contentLen >= 0) { + final Logger logger = server.getLogger(); + String msg = + "sendResponseHeaders: being invoked with a content length for a HEAD request"; + logger.warning (msg); + } + noContentToSend = true; + contentLen = 0; + } else { /* not a HEAD request */ + if (contentLen == 0) { + if (http10) { + o.setWrappedStream (new UndefLengthOutputStream (this, ros)); + close = true; + } else { + rspHdrs.set ("Transfer-encoding", "chunked"); + o.setWrappedStream (new ChunkedOutputStream (this, ros)); + } + } else { + if (contentLen == -1) { + noContentToSend = true; + contentLen = 0; + } + rspHdrs.set("Content-length", Long.toString(contentLen)); + o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen)); + } + } + write (rspHdrs, tmpout); + this.rspContentLen = contentLen; + tmpout.flush() ; + tmpout = null; + sentHeaders = true; + if (noContentToSend) { + WriteFinishedEvent e = new WriteFinishedEvent (this); + server.addEvent (e); + closed = true; + } + server.logReply (rCode, req.requestLine(), null); + } + + void write (Headers map, OutputStream os) throws IOException { + Set>> entries = map.entrySet(); + for (Map.Entry> entry : entries) { + String key = entry.getKey(); + byte[] buf; + List values = entry.getValue(); + for (String val : values) { + int i = key.length(); + buf = bytes (key, 2); + buf[i++] = ':'; + buf[i++] = ' '; + os.write (buf, 0, i); + buf = bytes (val, 2); + i = val.length(); + buf[i++] = '\r'; + buf[i++] = '\n'; + os.write (buf, 0, i); + } + } + os.write ('\r'); + os.write ('\n'); + } + + private byte[] rspbuf = new byte [128]; // used by bytes() + + /** + * convert string to byte[], using rspbuf + * Make sure that at least "extra" bytes are free at end + * of rspbuf. Reallocate rspbuf if not big enough. + * caller must check return value to see if rspbuf moved + */ + private byte[] bytes (String s, int extra) { + int slen = s.length(); + if (slen+extra > rspbuf.length) { + int diff = slen + extra - rspbuf.length; + rspbuf = new byte [2* (rspbuf.length + diff)]; + } + char c[] = s.toCharArray(); + for (int i=0; i remaining) { + len = (int)remaining; + } + int n = in.read(b, off, len); + if (n > -1) { + remaining -= n; + if (remaining == 0) { + t.getServerImpl().requestCompleted (t.getConnection()); + } + } + return n; + } + + public int available () throws IOException { + if (eof) { + return 0; + } + int n = in.available(); + return n < remaining? n: (int)remaining; + } + + public boolean markSupported () {return false;} + + public void mark (int l) { + } + + public void reset () throws IOException { + throw new IOException ("mark/reset not supported"); + } +} diff --git a/src/sun/net/httpserver/FixedLengthOutputStream.java b/src/sun/net/httpserver/FixedLengthOutputStream.java new file mode 100644 index 00000000..8b431645 --- /dev/null +++ b/src/sun/net/httpserver/FixedLengthOutputStream.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.net.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +/** + * a class which allows the caller to write up to a defined + * number of bytes to an underlying stream. The caller *must* + * write the pre-defined number or else an exception will be thrown + * and the whole request aborted. + * normal close() does not close the underlying stream + */ + +class FixedLengthOutputStream extends FilterOutputStream +{ + private long remaining; + private boolean eof = false; + private boolean closed = false; + ExchangeImpl t; + + FixedLengthOutputStream (ExchangeImpl t, OutputStream src, long len) { + super (src); + this.t = t; + this.remaining = len; + } + + public void write (int b) throws IOException { + if (closed) { + throw new IOException ("stream closed"); + } + eof = (remaining == 0); + if (eof) { + throw new StreamClosedException(); + } + out.write(b); + remaining --; + } + + public void write (byte[]b, int off, int len) throws IOException { + if (closed) { + throw new IOException ("stream closed"); + } + eof = (remaining == 0); + if (eof) { + throw new StreamClosedException(); + } + if (len > remaining) { + // stream is still open, caller can retry + throw new IOException ("too many bytes to write to stream"); + } + out.write(b, off, len); + remaining -= len; + } + + public void close () throws IOException { + if (closed) { + return; + } + closed = true; + if (remaining > 0) { + t.close(); + throw new IOException ("insufficient bytes written to stream"); + } + flush(); + eof = true; + LeftOverInputStream is = t.getOriginalInputStream(); + if (!is.isClosed()) { + try { + is.close(); + } catch (IOException e) {} + } + WriteFinishedEvent e = new WriteFinishedEvent (t); + t.getHttpContext().getServerImpl().addEvent (e); + } + + // flush is a pass-through +} diff --git a/src/sun/net/httpserver/HttpConnection.java b/src/sun/net/httpserver/HttpConnection.java new file mode 100644 index 00000000..c9f31efb --- /dev/null +++ b/src/sun/net/httpserver/HttpConnection.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import javax.net.ssl.*; +import java.nio.channels.*; +import java.util.logging.Logger; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +/** + * encapsulates all the connection specific state for a HTTP/S connection + * one of these is hung from the selector attachment and is used to locate + * everything from that. + */ +class HttpConnection { + + HttpContextImpl context; + SSLEngine engine; + SSLContext sslContext; + SSLStreams sslStreams; + + /* high level streams returned to application */ + InputStream i; + + /* low level stream that sits directly over channel */ + InputStream raw; + OutputStream rawout; + + SocketChannel chan; + SelectionKey selectionKey; + String protocol; + long time; + volatile long creationTime; // time this connection was created + volatile long rspStartedTime; // time we started writing the response + int remaining; + boolean closed = false; + Logger logger; + + public enum State {IDLE, REQUEST, RESPONSE}; + volatile State state; + + public String toString() { + String s = null; + if (chan != null) { + s = chan.toString(); + } + return s; + } + + HttpConnection () { + } + + void setChannel (SocketChannel c) { + chan = c; + } + + void setContext (HttpContextImpl ctx) { + context = ctx; + } + + State getState() { + return state; + } + + void setState (State s) { + state = s; + } + + void setParameters ( + InputStream in, OutputStream rawout, SocketChannel chan, + SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol, + HttpContextImpl context, InputStream raw + ) + { + this.context = context; + this.i = in; + this.rawout = rawout; + this.raw = raw; + this.protocol = protocol; + this.engine = engine; + this.chan = chan; + this.sslContext = sslContext; + this.sslStreams = sslStreams; + this.logger = context.getLogger(); + } + + SocketChannel getChannel () { + return chan; + } + + synchronized void close () { + if (closed) { + return; + } + closed = true; + if (logger != null && chan != null) { + logger.finest ("Closing connection: " + chan.toString()); + } + + if (!chan.isOpen()) { + ServerImpl.dprint ("Channel already closed"); + return; + } + try { + /* need to ensure temporary selectors are closed */ + if (raw != null) { + raw.close(); + } + } catch (IOException e) { + ServerImpl.dprint (e); + } + try { + if (rawout != null) { + rawout.close(); + } + } catch (IOException e) { + ServerImpl.dprint (e); + } + try { + if (sslStreams != null) { + sslStreams.close(); + } + } catch (IOException e) { + ServerImpl.dprint (e); + } + try { + chan.close(); + } catch (IOException e) { + ServerImpl.dprint (e); + } + } + + /* remaining is the number of bytes left on the lowest level inputstream + * after the exchange is finished + */ + void setRemaining (int r) { + remaining = r; + } + + int getRemaining () { + return remaining; + } + + SelectionKey getSelectionKey () { + return selectionKey; + } + + InputStream getInputStream () { + return i; + } + + OutputStream getRawOutputStream () { + return rawout; + } + + String getProtocol () { + return protocol; + } + + SSLEngine getSSLEngine () { + return engine; + } + + SSLContext getSSLContext () { + return sslContext; + } + + HttpContextImpl getHttpContext () { + return context; + } +} diff --git a/src/sun/net/httpserver/HttpContextImpl.java b/src/sun/net/httpserver/HttpContextImpl.java new file mode 100644 index 00000000..c7e7095b --- /dev/null +++ b/src/sun/net/httpserver/HttpContextImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; +import java.io.*; +import java.util.*; +import java.util.logging.Logger; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +/** + * HttpContext represents a mapping between a protocol (http or https) together with a root URI path + * to a {@link HttpHandler} which is invoked to handle requests destined + * for the protocol/path on the associated HttpServer. + *

    + * HttpContext instances are created by {@link HttpServer#createContext(String,String,HttpHandler,Object)} + *

    + */ +class HttpContextImpl extends HttpContext { + + private String path; + private String protocol; + private HttpHandler handler; + private Map attributes = new HashMap(); + private ServerImpl server; + /* system filters, not visible to applications */ + private LinkedList sfilters = new LinkedList(); + /* user filters, set by applications */ + private LinkedList ufilters = new LinkedList(); + private Authenticator authenticator; + private AuthFilter authfilter; + + /** + * constructor is package private. + */ + HttpContextImpl (String protocol, String path, HttpHandler cb, ServerImpl server) { + if (path == null || protocol == null || path.length() < 1 || path.charAt(0) != '/') { + throw new IllegalArgumentException ("Illegal value for path or protocol"); + } + this.protocol = protocol.toLowerCase(); + this.path = path; + if (!this.protocol.equals ("http") && !this.protocol.equals ("https")) { + throw new IllegalArgumentException ("Illegal value for protocol"); + } + this.handler = cb; + this.server = server; + authfilter = new AuthFilter(null); + sfilters.add (authfilter); + } + + /** + * returns the handler for this context + * @return the HttpHandler for this context + */ + public HttpHandler getHandler () { + return handler; + } + + public void setHandler (HttpHandler h) { + if (h == null) { + throw new NullPointerException ("Null handler parameter"); + } + if (handler != null) { + throw new IllegalArgumentException ("handler already set"); + } + handler = h; + } + + /** + * returns the path this context was created with + * @return this context's path + */ + public String getPath() { + return path; + } + + /** + * returns the server this context was created with + * @return this context's server + */ + public HttpServer getServer () { + return server.getWrapper(); + } + + ServerImpl getServerImpl () { + return server; + } + + /** + * returns the protocol this context was created with + * @return this context's path + */ + public String getProtocol() { + return protocol; + } + + /** + * returns a mutable Map, which can be used to pass + * configuration and other data to Filter modules + * and to the context's exchange handler. + *

    + * Every attribute stored in this Map will be visible to + * every HttpExchange processed by this context + */ + public Map getAttributes() { + return attributes; + } + + public List getFilters () { + return ufilters; + } + + List getSystemFilters () { + return sfilters; + } + + public Authenticator setAuthenticator (Authenticator auth) { + Authenticator old = authenticator; + authenticator = auth; + authfilter.setAuthenticator (auth); + return old; + } + + public Authenticator getAuthenticator () { + return authenticator; + } + Logger getLogger () { + return server.getLogger(); + } +} diff --git a/src/sun/net/httpserver/HttpError.java b/src/sun/net/httpserver/HttpError.java new file mode 100644 index 00000000..de9dfba8 --- /dev/null +++ b/src/sun/net/httpserver/HttpError.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +/** + * A Http error + */ +class HttpError extends RuntimeException { + private static final long serialVersionUID = 8769596371344178179L; + + public HttpError (String msg) { + super (msg); + } +} diff --git a/src/sun/net/httpserver/HttpExchangeImpl.java b/src/sun/net/httpserver/HttpExchangeImpl.java new file mode 100644 index 00000000..daeaea89 --- /dev/null +++ b/src/sun/net/httpserver/HttpExchangeImpl.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import javax.net.ssl.*; +import java.util.*; +import sun.net.www.MessageHeader; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +class HttpExchangeImpl extends HttpExchange { + + ExchangeImpl impl; + + HttpExchangeImpl (ExchangeImpl impl) { + this.impl = impl; + } + + public Headers getRequestHeaders () { + return impl.getRequestHeaders(); + } + + public Headers getResponseHeaders () { + return impl.getResponseHeaders(); + } + + public URI getRequestURI () { + return impl.getRequestURI(); + } + + public String getRequestMethod (){ + return impl.getRequestMethod(); + } + + public HttpContextImpl getHttpContext (){ + return impl.getHttpContext(); + } + + public void close () { + impl.close(); + } + + public InputStream getRequestBody () { + return impl.getRequestBody(); + } + + public int getResponseCode () { + return impl.getResponseCode(); + } + + public OutputStream getResponseBody () { + return impl.getResponseBody(); + } + + + public void sendResponseHeaders (int rCode, long contentLen) + throws IOException + { + impl.sendResponseHeaders (rCode, contentLen); + } + + public InetSocketAddress getRemoteAddress (){ + return impl.getRemoteAddress(); + } + + public InetSocketAddress getLocalAddress (){ + return impl.getLocalAddress(); + } + + public String getProtocol (){ + return impl.getProtocol(); + } + + public Object getAttribute (String name) { + return impl.getAttribute (name); + } + + public void setAttribute (String name, Object value) { + impl.setAttribute (name, value); + } + + public void setStreams (InputStream i, OutputStream o) { + impl.setStreams (i, o); + } + + public HttpPrincipal getPrincipal () { + return impl.getPrincipal(); + } + + ExchangeImpl getExchangeImpl () { + return impl; + } +} diff --git a/src/sun/net/httpserver/HttpServerImpl.java b/src/sun/net/httpserver/HttpServerImpl.java new file mode 100644 index 00000000..e6e0ad85 --- /dev/null +++ b/src/sun/net/httpserver/HttpServerImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.net.*; +import java.io.*; +import java.nio.*; +import java.security.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import javax.net.ssl.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +public class HttpServerImpl extends HttpServer { + + ServerImpl server; + + HttpServerImpl () throws IOException { + this (new InetSocketAddress(80), 0); + } + + HttpServerImpl ( + InetSocketAddress addr, int backlog + ) throws IOException { + server = new ServerImpl (this, "http", addr, backlog); + } + + public void bind (InetSocketAddress addr, int backlog) throws IOException { + server.bind (addr, backlog); + } + + public void start () { + server.start(); + } + + public void setExecutor (Executor executor) { + server.setExecutor(executor); + } + + public Executor getExecutor () { + return server.getExecutor(); + } + + public void stop (int delay) { + server.stop (delay); + } + + public HttpContextImpl createContext (String path, HttpHandler handler) { + return server.createContext (path, handler); + } + + public HttpContextImpl createContext (String path) { + return server.createContext (path); + } + + public void removeContext (String path) throws IllegalArgumentException { + server.removeContext (path); + } + + public void removeContext (HttpContext context) throws IllegalArgumentException { + server.removeContext (context); + } + + public InetSocketAddress getAddress() { + return server.getAddress(); + } +} diff --git a/src/sun/net/httpserver/HttpsExchangeImpl.java b/src/sun/net/httpserver/HttpsExchangeImpl.java new file mode 100644 index 00000000..7b38de53 --- /dev/null +++ b/src/sun/net/httpserver/HttpsExchangeImpl.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.net.*; +import javax.net.ssl.*; +import java.util.*; +import sun.net.www.MessageHeader; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +class HttpsExchangeImpl extends HttpsExchange { + + ExchangeImpl impl; + + HttpsExchangeImpl (ExchangeImpl impl) throws IOException { + this.impl = impl; + } + + public Headers getRequestHeaders () { + return impl.getRequestHeaders(); + } + + public Headers getResponseHeaders () { + return impl.getResponseHeaders(); + } + + public URI getRequestURI () { + return impl.getRequestURI(); + } + + public String getRequestMethod (){ + return impl.getRequestMethod(); + } + + public HttpContextImpl getHttpContext (){ + return impl.getHttpContext(); + } + + public void close () { + impl.close(); + } + + public InputStream getRequestBody () { + return impl.getRequestBody(); + } + + public int getResponseCode () { + return impl.getResponseCode(); + } + + public OutputStream getResponseBody () { + return impl.getResponseBody(); + } + + + public void sendResponseHeaders (int rCode, long contentLen) + throws IOException + { + impl.sendResponseHeaders (rCode, contentLen); + } + + public InetSocketAddress getRemoteAddress (){ + return impl.getRemoteAddress(); + } + + public InetSocketAddress getLocalAddress (){ + return impl.getLocalAddress(); + } + + public String getProtocol (){ + return impl.getProtocol(); + } + + public SSLSession getSSLSession () { + return impl.getSSLSession (); + } + + public Object getAttribute (String name) { + return impl.getAttribute (name); + } + + public void setAttribute (String name, Object value) { + impl.setAttribute (name, value); + } + + public void setStreams (InputStream i, OutputStream o) { + impl.setStreams (i, o); + } + + public HttpPrincipal getPrincipal () { + return impl.getPrincipal(); + } + + ExchangeImpl getExchangeImpl () { + return impl; + } +} diff --git a/src/sun/net/httpserver/HttpsServerImpl.java b/src/sun/net/httpserver/HttpsServerImpl.java new file mode 100644 index 00000000..f6b94db7 --- /dev/null +++ b/src/sun/net/httpserver/HttpsServerImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.net.*; +import java.io.*; +import java.nio.*; +import java.security.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import javax.net.ssl.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +public class HttpsServerImpl extends HttpsServer { + + ServerImpl server; + + HttpsServerImpl () throws IOException { + this (new InetSocketAddress(443), 0); + } + + HttpsServerImpl ( + InetSocketAddress addr, int backlog + ) throws IOException { + server = new ServerImpl (this, "https", addr, backlog); + } + + public void setHttpsConfigurator (HttpsConfigurator config) { + server.setHttpsConfigurator (config); + } + + public HttpsConfigurator getHttpsConfigurator () { + return server.getHttpsConfigurator(); + } + + public void bind (InetSocketAddress addr, int backlog) throws IOException { + server.bind (addr, backlog); + } + + public void start () { + server.start(); + } + + public void setExecutor (Executor executor) { + server.setExecutor(executor); + } + + public Executor getExecutor () { + return server.getExecutor(); + } + + public void stop (int delay) { + server.stop (delay); + } + + public HttpContextImpl createContext (String path, HttpHandler handler) { + return server.createContext (path, handler); + } + + public HttpContextImpl createContext (String path) { + return server.createContext (path); + } + + public void removeContext (String path) throws IllegalArgumentException { + server.removeContext (path); + } + + public void removeContext (HttpContext context) throws IllegalArgumentException { + server.removeContext (context); + } + + public InetSocketAddress getAddress() { + return server.getAddress(); + } +} diff --git a/src/sun/net/httpserver/LeftOverInputStream.java b/src/sun/net/httpserver/LeftOverInputStream.java new file mode 100644 index 00000000..c715d72a --- /dev/null +++ b/src/sun/net/httpserver/LeftOverInputStream.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.io.*; +import com.sun.net.httpserver.*; +import com.sun.net.httpserver.spi.*; + +/** + * a (filter) input stream which can tell us if bytes are "left over" + * on the underlying stream which can be read (without blocking) + * on another instance of this class. + * + * The class can also report if all bytes "expected" to be read + * were read, by the time close() was called. In that case, + * bytes may be drained to consume them (by calling drain() ). + * + * isEOF() returns true, when all expected bytes have been read + */ +abstract class LeftOverInputStream extends FilterInputStream { + ExchangeImpl t; + ServerImpl server; + protected boolean closed = false; + protected boolean eof = false; + byte[] one = new byte [1]; + + public LeftOverInputStream (ExchangeImpl t, InputStream src) { + super (src); + this.t = t; + this.server = t.getServerImpl(); + } + /** + * if bytes are left over buffered on *the UNDERLYING* stream + */ + public boolean isDataBuffered () throws IOException { + assert eof; + return super.available() > 0; + } + + public void close () throws IOException { + if (closed) { + return; + } + closed = true; + if (!eof) { + eof = drain (ServerConfig.getDrainAmount()); + } + } + + public boolean isClosed () { + return closed; + } + + public boolean isEOF () { + return eof; + } + + protected abstract int readImpl (byte[]b, int off, int len) throws IOException; + + public synchronized int read () throws IOException { + if (closed) { + throw new IOException ("Stream is closed"); + } + int c = readImpl (one, 0, 1); + if (c == -1 || c == 0) { + return c; + } else { + return one[0] & 0xFF; + } + } + + public synchronized int read (byte[]b, int off, int len) throws IOException { + if (closed) { + throw new IOException ("Stream is closed"); + } + return readImpl (b, off, len); + } + + /** + * read and discard up to l bytes or "eof" occurs, + * (whichever is first). Then return true if the stream + * is at eof (ie. all bytes were read) or false if not + * (still bytes to be read) + */ + public boolean drain (long l) throws IOException { + int bufSize = 2048; + byte[] db = new byte [bufSize]; + while (l > 0) { + long len = readImpl (db, 0, bufSize); + if (len == -1) { + eof = true; + return true; + } else { + l = l - len; + } + } + return false; + } +} diff --git a/src/sun/net/httpserver/Request.java b/src/sun/net/httpserver/Request.java new file mode 100644 index 00000000..2a147ada --- /dev/null +++ b/src/sun/net/httpserver/Request.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.nio.*; +import java.io.*; +import java.nio.channels.*; +import com.sun.net.httpserver.*; + +/** + */ +class Request { + + final static int BUF_LEN = 2048; + final static byte CR = 13; + final static byte LF = 10; + + private String startLine; + private SocketChannel chan; + private InputStream is; + private OutputStream os; + + Request (InputStream rawInputStream, OutputStream rawout) throws IOException { + is = rawInputStream; + os = rawout; + do { + startLine = readLine(); + if (startLine == null) { + return; + } + /* skip blank lines */ + } while (startLine == null ? false : startLine.equals ("")); + } + + + char[] buf = new char [BUF_LEN]; + int pos; + StringBuffer lineBuf; + + public InputStream inputStream () { + return is; + } + + public OutputStream outputStream () { + return os; + } + + /** + * read a line from the stream returning as a String. + * Not used for reading headers. + */ + + public String readLine () throws IOException { + boolean gotCR = false, gotLF = false; + pos = 0; lineBuf = new StringBuffer(); + while (!gotLF) { + int c = is.read(); + if (c == -1) { + return null; + } + if (gotCR) { + if (c == LF) { + gotLF = true; + } else { + gotCR = false; + consume (CR); + consume (c); + } + } else { + if (c == CR) { + gotCR = true; + } else { + consume (c); + } + } + } + lineBuf.append (buf, 0, pos); + return new String (lineBuf); + } + + private void consume (int c) { + if (pos == BUF_LEN) { + lineBuf.append (buf); + pos = 0; + } + buf[pos++] = (char)c; + } + + /** + * returns the request line (first line of a request) + */ + public String requestLine () { + return startLine; + } + + Headers hdrs = null; + @SuppressWarnings("fallthrough") + Headers headers () throws IOException { + if (hdrs != null) { + return hdrs; + } + hdrs = new Headers(); + + char s[] = new char[10]; + int len = 0; + + int firstc = is.read(); + + // check for empty headers + if (firstc == CR || firstc == LF) { + int c = is.read(); + if (c == CR || c == LF) { + return hdrs; + } + s[0] = (char)firstc; + len = 1; + firstc = c; + } + + while (firstc != LF && firstc != CR && firstc >= 0) { + int keyend = -1; + int c; + boolean inKey = firstc > ' '; + s[len++] = (char) firstc; + parseloop:{ + while ((c = is.read()) >= 0) { + switch (c) { + /*fallthrough*/ + case ':': + if (inKey && len > 0) + keyend = len; + inKey = false; + break; + case '\t': + c = ' '; + case ' ': + inKey = false; + break; + case CR: + case LF: + firstc = is.read(); + if (c == CR && firstc == LF) { + firstc = is.read(); + if (firstc == CR) + firstc = is.read(); + } + if (firstc == LF || firstc == CR || firstc > ' ') + break parseloop; + /* continuation */ + c = ' '; + break; + } + if (len >= s.length) { + char ns[] = new char[s.length * 2]; + System.arraycopy(s, 0, ns, 0, len); + s = ns; + } + s[len++] = (char) c; + } + firstc = -1; + } + while (len > 0 && s[len - 1] <= ' ') + len--; + String k; + if (keyend <= 0) { + k = null; + keyend = 0; + } else { + k = String.copyValueOf(s, 0, keyend); + if (keyend < len && s[keyend] == ':') + keyend++; + while (keyend < len && s[keyend] <= ' ') + keyend++; + } + String v; + if (keyend >= len) + v = new String(); + else + v = String.copyValueOf(s, keyend, len - keyend); + + if (hdrs.size() >= ServerConfig.getMaxReqHeaders()) { + throw new IOException("Maximum number of request headers (" + + "sun.net.httpserver.maxReqHeaders) exceeded, " + + ServerConfig.getMaxReqHeaders() + "."); + } + + hdrs.add (k,v); + len = 0; + } + return hdrs; + } + + /** + * Implements blocking reading semantics on top of a non-blocking channel + */ + + static class ReadStream extends InputStream { + SocketChannel channel; + ByteBuffer chanbuf; + byte[] one; + private boolean closed = false, eof = false; + ByteBuffer markBuf; /* reads may be satisfied from this buffer */ + boolean marked; + boolean reset; + int readlimit; + static long readTimeout; + ServerImpl server; + final static int BUFSIZE = 8 * 1024; + + public ReadStream (ServerImpl server, SocketChannel chan) throws IOException { + this.channel = chan; + this.server = server; + chanbuf = ByteBuffer.allocate (BUFSIZE); + chanbuf.clear(); + one = new byte[1]; + closed = marked = reset = false; + } + + public synchronized int read (byte[] b) throws IOException { + return read (b, 0, b.length); + } + + public synchronized int read () throws IOException { + int result = read (one, 0, 1); + if (result == 1) { + return one[0] & 0xFF; + } else { + return -1; + } + } + + public synchronized int read (byte[] b, int off, int srclen) throws IOException { + + int canreturn, willreturn; + + if (closed) + throw new IOException ("Stream closed"); + + if (eof) { + return -1; + } + + assert channel.isBlocking(); + + if (off < 0 || srclen < 0|| srclen > (b.length-off)) { + throw new IndexOutOfBoundsException (); + } + + if (reset) { /* satisfy from markBuf */ + canreturn = markBuf.remaining (); + willreturn = canreturn>srclen ? srclen : canreturn; + markBuf.get(b, off, willreturn); + if (canreturn == willreturn) { + reset = false; + } + } else { /* satisfy from channel */ + chanbuf.clear (); + if (srclen < BUFSIZE) { + chanbuf.limit (srclen); + } + do { + willreturn = channel.read (chanbuf); + } while (willreturn == 0); + if (willreturn == -1) { + eof = true; + return -1; + } + chanbuf.flip (); + chanbuf.get(b, off, willreturn); + + if (marked) { /* copy into markBuf */ + try { + markBuf.put (b, off, willreturn); + } catch (BufferOverflowException e) { + marked = false; + } + } + } + return willreturn; + } + + public boolean markSupported () { + return true; + } + + /* Does not query the OS socket */ + public synchronized int available () throws IOException { + if (closed) + throw new IOException ("Stream is closed"); + + if (eof) + return -1; + + if (reset) + return markBuf.remaining(); + + return chanbuf.remaining(); + } + + public void close () throws IOException { + if (closed) { + return; + } + channel.close (); + closed = true; + } + + public synchronized void mark (int readlimit) { + if (closed) + return; + this.readlimit = readlimit; + markBuf = ByteBuffer.allocate (readlimit); + marked = true; + reset = false; + } + + public synchronized void reset () throws IOException { + if (closed ) + return; + if (!marked) + throw new IOException ("Stream not marked"); + marked = false; + reset = true; + markBuf.flip (); + } + } + + static class WriteStream extends OutputStream { + SocketChannel channel; + ByteBuffer buf; + SelectionKey key; + boolean closed; + byte[] one; + ServerImpl server; + + public WriteStream (ServerImpl server, SocketChannel channel) throws IOException { + this.channel = channel; + this.server = server; + assert channel.isBlocking(); + closed = false; + one = new byte [1]; + buf = ByteBuffer.allocate (4096); + } + + public synchronized void write (int b) throws IOException { + one[0] = (byte)b; + write (one, 0, 1); + } + + public synchronized void write (byte[] b) throws IOException { + write (b, 0, b.length); + } + + public synchronized void write (byte[] b, int off, int len) throws IOException { + int l = len; + if (closed) + throw new IOException ("stream is closed"); + + int cap = buf.capacity(); + if (cap < len) { + int diff = len - cap; + buf = ByteBuffer.allocate (2*(cap+diff)); + } + buf.clear(); + buf.put (b, off, len); + buf.flip (); + int n; + while ((n = channel.write (buf)) < l) { + l -= n; + if (l == 0) + return; + } + } + + public void close () throws IOException { + if (closed) + return; + //server.logStackTrace ("Request.OS.close: isOpen="+channel.isOpen()); + channel.close (); + closed = true; + } + } +} diff --git a/src/sun/net/httpserver/SSLStreams.java b/src/sun/net/httpserver/SSLStreams.java new file mode 100644 index 00000000..6e362d07 --- /dev/null +++ b/src/sun/net/httpserver/SSLStreams.java @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.net.*; +import java.nio.*; +import java.io.*; +import java.nio.channels.*; +import java.util.concurrent.locks.*; +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.*; +import com.sun.net.httpserver.*; + +/** + * given a non-blocking SocketChannel, it produces + * (blocking) streams which encrypt/decrypt the SSL content + * and handle the SSL handshaking automatically. + */ + +class SSLStreams { + + SSLContext sslctx; + SocketChannel chan; + TimeSource time; + ServerImpl server; + SSLEngine engine; + EngineWrapper wrapper; + OutputStream os; + InputStream is; + + /* held by thread doing the hand-shake on this connection */ + Lock handshaking = new ReentrantLock(); + + SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException { + this.server = server; + this.time= (TimeSource)server; + this.sslctx= sslctx; + this.chan= chan; + InetSocketAddress addr = + (InetSocketAddress)chan.socket().getRemoteSocketAddress(); + engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort()); + engine.setUseClientMode (false); + HttpsConfigurator cfg = server.getHttpsConfigurator(); + configureEngine (cfg, addr); + wrapper = new EngineWrapper (chan, engine); + } + + private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){ + if (cfg != null) { + Parameters params = new Parameters (cfg, addr); +//BEGIN_TIGER_EXCLUDE + cfg.configure (params); + SSLParameters sslParams = params.getSSLParameters(); + if (sslParams != null) { + engine.setSSLParameters (sslParams); + } else +//END_TIGER_EXCLUDE + { + /* tiger compatibility */ + if (params.getCipherSuites() != null) { + try { + engine.setEnabledCipherSuites ( + params.getCipherSuites() + ); + } catch (IllegalArgumentException e) { /* LOG */} + } + engine.setNeedClientAuth (params.getNeedClientAuth()); + engine.setWantClientAuth (params.getWantClientAuth()); + if (params.getProtocols() != null) { + try { + engine.setEnabledProtocols ( + params.getProtocols() + ); + } catch (IllegalArgumentException e) { /* LOG */} + } + } + } + } + + class Parameters extends HttpsParameters { + InetSocketAddress addr; + HttpsConfigurator cfg; + + Parameters (HttpsConfigurator cfg, InetSocketAddress addr) { + this.addr = addr; + this.cfg = cfg; + } + public InetSocketAddress getClientAddress () { + return addr; + } + public HttpsConfigurator getHttpsConfigurator() { + return cfg; + } +//BEGIN_TIGER_EXCLUDE + SSLParameters params; + public void setSSLParameters (SSLParameters p) { + params = p; + } + SSLParameters getSSLParameters () { + return params; + } +//END_TIGER_EXCLUDE + } + + /** + * cleanup resources allocated inside this object + */ + void close () throws IOException { + wrapper.close(); + } + + /** + * return the SSL InputStream + */ + InputStream getInputStream () throws IOException { + if (is == null) { + is = new InputStream(); + } + return is; + } + + /** + * return the SSL OutputStream + */ + OutputStream getOutputStream () throws IOException { + if (os == null) { + os = new OutputStream(); + } + return os; + } + + SSLEngine getSSLEngine () { + return engine; + } + + /** + * request the engine to repeat the handshake on this session + * the handshake must be driven by reads/writes on the streams + * Normally, not necessary to call this. + */ + void beginHandshake() throws SSLException { + engine.beginHandshake(); + } + + class WrapperResult { + SSLEngineResult result; + + /* if passed in buffer was not big enough then the + * a reallocated buffer is returned here + */ + ByteBuffer buf; + } + + int app_buf_size; + int packet_buf_size; + + enum BufType { + PACKET, APPLICATION + }; + + private ByteBuffer allocate (BufType type) { + return allocate (type, -1); + } + + private ByteBuffer allocate (BufType type, int len) { + assert engine != null; + synchronized (this) { + int size; + if (type == BufType.PACKET) { + if (packet_buf_size == 0) { + SSLSession sess = engine.getSession(); + packet_buf_size = sess.getPacketBufferSize(); + } + if (len > packet_buf_size) { + packet_buf_size = len; + } + size = packet_buf_size; + } else { + if (app_buf_size == 0) { + SSLSession sess = engine.getSession(); + app_buf_size = sess.getApplicationBufferSize(); + } + if (len > app_buf_size) { + app_buf_size = len; + } + size = app_buf_size; + } + return ByteBuffer.allocate (size); + } + } + + /* reallocates the buffer by :- + * 1. creating a new buffer double the size of the old one + * 2. putting the contents of the old buffer into the new one + * 3. set xx_buf_size to the new size if it was smaller than new size + * + * flip is set to true if the old buffer needs to be flipped + * before it is copied. + */ + private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) { + synchronized (this) { + int nsize = 2 * b.capacity(); + ByteBuffer n = allocate (type, nsize); + if (flip) { + b.flip(); + } + n.put(b); + b = n; + } + return b; + } + /** + * This is a thin wrapper over SSLEngine and the SocketChannel, + * which guarantees the ordering of wraps/unwraps with respect to the underlying + * channel read/writes. It handles the UNDER/OVERFLOW status codes + * It does not handle the handshaking status codes, or the CLOSED status code + * though once the engine is closed, any attempt to read/write to it + * will get an exception. The overall result is returned. + * It functions synchronously/blocking + */ + class EngineWrapper { + + SocketChannel chan; + SSLEngine engine; + Object wrapLock, unwrapLock; + ByteBuffer unwrap_src, wrap_dst; + boolean closed = false; + int u_remaining; // the number of bytes left in unwrap_src after an unwrap() + + EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException { + this.chan = chan; + this.engine = engine; + wrapLock = new Object(); + unwrapLock = new Object(); + unwrap_src = allocate(BufType.PACKET); + wrap_dst = allocate(BufType.PACKET); + } + + void close () throws IOException { + } + + /* try to wrap and send the data in src. Handles OVERFLOW. + * Might block if there is an outbound blockage or if another + * thread is calling wrap(). Also, might not send any data + * if an unwrap is needed. + */ + WrapperResult wrapAndSend(ByteBuffer src) throws IOException { + return wrapAndSendX(src, false); + } + + WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException { + if (closed && !ignoreClose) { + throw new IOException ("Engine is closed"); + } + Status status; + WrapperResult r = new WrapperResult(); + synchronized (wrapLock) { + wrap_dst.clear(); + do { + r.result = engine.wrap (src, wrap_dst); + status = r.result.getStatus(); + if (status == Status.BUFFER_OVERFLOW) { + wrap_dst = realloc (wrap_dst, true, BufType.PACKET); + } + } while (status == Status.BUFFER_OVERFLOW); + if (status == Status.CLOSED && !ignoreClose) { + closed = true; + return r; + } + if (r.result.bytesProduced() > 0) { + wrap_dst.flip(); + int l = wrap_dst.remaining(); + assert l == r.result.bytesProduced(); + while (l>0) { + l -= chan.write (wrap_dst); + } + } + } + return r; + } + + /* block until a complete message is available and return it + * in dst, together with the Result. dst may have been re-allocated + * so caller should check the returned value in Result + * If handshaking is in progress then, possibly no data is returned + */ + WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException { + Status status = Status.OK; + WrapperResult r = new WrapperResult(); + r.buf = dst; + if (closed) { + throw new IOException ("Engine is closed"); + } + boolean needData; + if (u_remaining > 0) { + unwrap_src.compact(); + unwrap_src.flip(); + needData = false; + } else { + unwrap_src.clear(); + needData = true; + } + synchronized (unwrapLock) { + int x; + do { + if (needData) { + do { + x = chan.read (unwrap_src); + } while (x == 0); + if (x == -1) { + throw new IOException ("connection closed for reading"); + } + unwrap_src.flip(); + } + r.result = engine.unwrap (unwrap_src, r.buf); + status = r.result.getStatus(); + if (status == Status.BUFFER_UNDERFLOW) { + if (unwrap_src.limit() == unwrap_src.capacity()) { + /* buffer not big enough */ + unwrap_src = realloc ( + unwrap_src, false, BufType.PACKET + ); + } else { + /* Buffer not full, just need to read more + * data off the channel. Reset pointers + * for reading off SocketChannel + */ + unwrap_src.position (unwrap_src.limit()); + unwrap_src.limit (unwrap_src.capacity()); + } + needData = true; + } else if (status == Status.BUFFER_OVERFLOW) { + r.buf = realloc (r.buf, true, BufType.APPLICATION); + needData = false; + } else if (status == Status.CLOSED) { + closed = true; + r.buf.flip(); + return r; + } + } while (status != Status.OK); + } + u_remaining = unwrap_src.remaining(); + return r; + } + } + + /** + * send the data in the given ByteBuffer. If a handshake is needed + * then this is handled within this method. When this call returns, + * all of the given user data has been sent and any handshake has been + * completed. Caller should check if engine has been closed. + */ + public WrapperResult sendData (ByteBuffer src) throws IOException { + WrapperResult r=null; + while (src.remaining() > 0) { + r = wrapper.wrapAndSend(src); + Status status = r.result.getStatus(); + if (status == Status.CLOSED) { + doClosure (); + return r; + } + HandshakeStatus hs_status = r.result.getHandshakeStatus(); + if (hs_status != HandshakeStatus.FINISHED && + hs_status != HandshakeStatus.NOT_HANDSHAKING) + { + doHandshake(hs_status); + } + } + return r; + } + + /** + * read data thru the engine into the given ByteBuffer. If the + * given buffer was not large enough, a new one is allocated + * and returned. This call handles handshaking automatically. + * Caller should check if engine has been closed. + */ + public WrapperResult recvData (ByteBuffer dst) throws IOException { + /* we wait until some user data arrives */ + WrapperResult r = null; + assert dst.position() == 0; + while (dst.position() == 0) { + r = wrapper.recvAndUnwrap (dst); + dst = (r.buf != dst) ? r.buf: dst; + Status status = r.result.getStatus(); + if (status == Status.CLOSED) { + doClosure (); + return r; + } + + HandshakeStatus hs_status = r.result.getHandshakeStatus(); + if (hs_status != HandshakeStatus.FINISHED && + hs_status != HandshakeStatus.NOT_HANDSHAKING) + { + doHandshake (hs_status); + } + } + dst.flip(); + return r; + } + + /* we've received a close notify. Need to call wrap to send + * the response + */ + void doClosure () throws IOException { + try { + handshaking.lock(); + ByteBuffer tmp = allocate(BufType.APPLICATION); + WrapperResult r; + do { + tmp.clear(); + tmp.flip (); + r = wrapper.wrapAndSendX (tmp, true); + } while (r.result.getStatus() != Status.CLOSED); + } finally { + handshaking.unlock(); + } + } + + /* do the (complete) handshake after acquiring the handshake lock. + * If two threads call this at the same time, then we depend + * on the wrapper methods being idempotent. eg. if wrapAndSend() + * is called with no data to send then there must be no problem + */ + @SuppressWarnings("fallthrough") + void doHandshake (HandshakeStatus hs_status) throws IOException { + try { + handshaking.lock(); + ByteBuffer tmp = allocate(BufType.APPLICATION); + while (hs_status != HandshakeStatus.FINISHED && + hs_status != HandshakeStatus.NOT_HANDSHAKING) + { + WrapperResult r = null; + switch (hs_status) { + case NEED_TASK: + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + /* run in current thread, because we are already + * running an external Executor + */ + task.run(); + } + /* fall thru - call wrap again */ + case NEED_WRAP: + tmp.clear(); + tmp.flip(); + r = wrapper.wrapAndSend(tmp); + break; + + case NEED_UNWRAP: + tmp.clear(); + r = wrapper.recvAndUnwrap (tmp); + if (r.buf != tmp) { + tmp = r.buf; + } + assert tmp.position() == 0; + break; + } + hs_status = r.result.getHandshakeStatus(); + } + } finally { + handshaking.unlock(); + } + } + + /** + * represents an SSL input stream. Multiple https requests can + * be sent over one stream. closing this stream causes an SSL close + * input. + */ + class InputStream extends java.io.InputStream { + + ByteBuffer bbuf; + boolean closed = false; + + /* this stream eof */ + boolean eof = false; + + boolean needData = true; + + InputStream () { + bbuf = allocate (BufType.APPLICATION); + } + + public int read (byte[] buf, int off, int len) throws IOException { + if (closed) { + throw new IOException ("SSL stream is closed"); + } + if (eof) { + return 0; + } + int available=0; + if (!needData) { + available = bbuf.remaining(); + needData = (available==0); + } + if (needData) { + bbuf.clear(); + WrapperResult r = recvData (bbuf); + bbuf = r.buf== bbuf? bbuf: r.buf; + if ((available=bbuf.remaining()) == 0) { + eof = true; + return 0; + } else { + needData = false; + } + } + /* copy as much as possible from buf into users buf */ + if (len > available) { + len = available; + } + bbuf.get (buf, off, len); + return len; + } + + public int available () throws IOException { + return bbuf.remaining(); + } + + public boolean markSupported () { + return false; /* not possible with SSLEngine */ + } + + public void reset () throws IOException { + throw new IOException ("mark/reset not supported"); + } + + public long skip (long s) throws IOException { + int n = (int)s; + if (closed) { + throw new IOException ("SSL stream is closed"); + } + if (eof) { + return 0; + } + int ret = n; + while (n > 0) { + if (bbuf.remaining() >= n) { + bbuf.position (bbuf.position()+n); + return ret; + } else { + n -= bbuf.remaining(); + bbuf.clear(); + WrapperResult r = recvData (bbuf); + bbuf = r.buf==bbuf? bbuf: r.buf; + } + } + return ret; /* not reached */ + } + + /** + * close the SSL connection. All data must have been consumed + * before this is called. Otherwise an exception will be thrown. + * [Note. May need to revisit this. not quite the normal close() symantics + */ + public void close () throws IOException { + eof = true; + engine.closeInbound (); + } + + public int read (byte[] buf) throws IOException { + return read (buf, 0, buf.length); + } + + byte single[] = new byte [1]; + + public int read () throws IOException { + int n = read (single, 0, 1); + if (n == 0) { + return -1; + } else { + return single[0] & 0xFF; + } + } + } + + /** + * represents an SSL output stream. plain text data written to this stream + * is encrypted by the stream. Multiple HTTPS responses can be sent on + * one stream. closing this stream initiates an SSL closure + */ + class OutputStream extends java.io.OutputStream { + ByteBuffer buf; + boolean closed = false; + byte single[] = new byte[1]; + + OutputStream() { + buf = allocate(BufType.APPLICATION); + } + + public void write(int b) throws IOException { + single[0] = (byte)b; + write (single, 0, 1); + } + + public void write(byte b[]) throws IOException { + write (b, 0, b.length); + } + public void write(byte b[], int off, int len) throws IOException { + if (closed) { + throw new IOException ("output stream is closed"); + } + while (len > 0) { + int l = len > buf.capacity() ? buf.capacity() : len; + buf.clear(); + buf.put (b, off, l); + len -= l; + off += l; + buf.flip(); + WrapperResult r = sendData (buf); + if (r.result.getStatus() == Status.CLOSED) { + closed = true; + if (len > 0) { + throw new IOException ("output stream is closed"); + } + } + } + } + + public void flush() throws IOException { + /* no-op */ + } + + public void close() throws IOException { + WrapperResult r=null; + engine.closeOutbound(); + closed = true; + HandshakeStatus stat = HandshakeStatus.NEED_WRAP; + buf.clear(); + while (stat == HandshakeStatus.NEED_WRAP) { + r = wrapper.wrapAndSend (buf); + stat = r.result.getHandshakeStatus(); + } + assert r.result.getStatus() == Status.CLOSED; + } + } +} diff --git a/src/sun/net/httpserver/ServerConfig.java b/src/sun/net/httpserver/ServerConfig.java new file mode 100644 index 00000000..a6b74278 --- /dev/null +++ b/src/sun/net/httpserver/ServerConfig.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.util.logging.Logger; +import java.security.PrivilegedAction; + +/** + * Parameters that users will not likely need to set + * but are useful for debugging + */ + +class ServerConfig { + + private static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec. + + /* These values must be a reasonable multiple of clockTick */ + private static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min + private static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ; + + private static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever + private static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever + private static final long DEFAULT_TIMER_MILLIS = 1000; + private static final int DEFAULT_MAX_REQ_HEADERS = 200; + private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024; + + private static int clockTick; + private static long idleInterval; + // The maximum number of bytes to drain from an inputstream + private static long drainAmount; + private static int maxIdleConnections; + // The maximum number of request headers allowable + private static int maxReqHeaders; + // max time a request or response is allowed to take + private static long maxReqTime; + private static long maxRspTime; + private static long timerMillis; + private static boolean debug; + + // the value of the TCP_NODELAY socket-level option + private static boolean noDelay; + + static { + java.security.AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Void run () { + idleInterval = Long.getLong("sun.net.httpserver.idleInterval", + DEFAULT_IDLE_INTERVAL) * 1000; + + clockTick = Integer.getInteger("sun.net.httpserver.clockTick", + DEFAULT_CLOCK_TICK); + + maxIdleConnections = Integer.getInteger( + "sun.net.httpserver.maxIdleConnections", + DEFAULT_MAX_IDLE_CONNECTIONS); + + drainAmount = Long.getLong("sun.net.httpserver.drainAmount", + DEFAULT_DRAIN_AMOUNT); + + maxReqHeaders = Integer.getInteger( + "sun.net.httpserver.maxReqHeaders", + DEFAULT_MAX_REQ_HEADERS); + + maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime", + DEFAULT_MAX_REQ_TIME); + + maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", + DEFAULT_MAX_RSP_TIME); + + timerMillis = Long.getLong("sun.net.httpserver.timerMillis", + DEFAULT_TIMER_MILLIS); + + debug = Boolean.getBoolean("sun.net.httpserver.debug"); + + noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay"); + + return null; + } + }); + + } + + static void checkLegacyProperties(final Logger logger) { + + // legacy properties that are no longer used + // print a warning to logger if they are set. + + java.security.AccessController.doPrivileged( + new PrivilegedAction() { + public Void run () { + if (System.getProperty("sun.net.httpserver.readTimeout") + !=null) + { + logger.warning ("sun.net.httpserver.readTimeout "+ + "property is no longer used. "+ + "Use sun.net.httpserver.maxReqTime instead." + ); + } + if (System.getProperty("sun.net.httpserver.writeTimeout") + !=null) + { + logger.warning ("sun.net.httpserver.writeTimeout "+ + "property is no longer used. Use "+ + "sun.net.httpserver.maxRspTime instead." + ); + } + if (System.getProperty("sun.net.httpserver.selCacheTimeout") + !=null) + { + logger.warning ("sun.net.httpserver.selCacheTimeout "+ + "property is no longer used." + ); + } + return null; + } + } + ); + } + + static boolean debugEnabled() { + return debug; + } + + static long getIdleInterval() { + return idleInterval; + } + + static int getClockTick() { + return clockTick; + } + + static int getMaxIdleConnections() { + return maxIdleConnections; + } + + static long getDrainAmount() { + return drainAmount; + } + + static int getMaxReqHeaders() { + return maxReqHeaders; + } + + static long getMaxReqTime() { + return maxReqTime; + } + + static long getMaxRspTime() { + return maxRspTime; + } + + static long getTimerMillis() { + return timerMillis; + } + + static boolean noDelay() { + return noDelay; + } +} diff --git a/src/sun/net/httpserver/ServerImpl.java b/src/sun/net/httpserver/ServerImpl.java new file mode 100644 index 00000000..5b424fb9 --- /dev/null +++ b/src/sun/net/httpserver/ServerImpl.java @@ -0,0 +1,880 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.Logger; +import java.util.logging.Level; +import javax.net.ssl.*; +import com.sun.net.httpserver.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.httpserver.HttpConnection.State; + +/** + * Provides implementation for both HTTP and HTTPS + */ +class ServerImpl implements TimeSource { + + private String protocol; + private boolean https; + private Executor executor; + private HttpsConfigurator httpsConfig; + private SSLContext sslContext; + private ContextList contexts; + private InetSocketAddress address; + private ServerSocketChannel schan; + private Selector selector; + private SelectionKey listenerKey; + private Set idleConnections; + private Set allConnections; + /* following two are used to keep track of the times + * when a connection/request is first received + * and when we start to send the response + */ + private Set reqConnections; + private Set rspConnections; + private List events; + private Object lolock = new Object(); + private volatile boolean finished = false; + private volatile boolean terminating = false; + private boolean bound = false; + private boolean started = false; + private volatile long time; /* current time */ + private volatile long subticks = 0; + private volatile long ticks; /* number of clock ticks since server started */ + private HttpServer wrapper; + + final static int CLOCK_TICK = ServerConfig.getClockTick(); + final static long IDLE_INTERVAL = ServerConfig.getIdleInterval(); + final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); + final static long TIMER_MILLIS = ServerConfig.getTimerMillis (); + final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime()); + final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime()); + final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; + + private Timer timer, timer1; + private Logger logger; + + ServerImpl ( + HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog + ) throws IOException { + + this.protocol = protocol; + this.wrapper = wrapper; + this.logger = Logger.getLogger ("com.sun.net.httpserver"); + ServerConfig.checkLegacyProperties (logger); + https = protocol.equalsIgnoreCase ("https"); + this.address = addr; + contexts = new ContextList(); + schan = ServerSocketChannel.open(); + if (addr != null) { + ServerSocket socket = schan.socket(); + socket.bind (addr, backlog); + bound = true; + } + selector = Selector.open (); + schan.configureBlocking (false); + listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT); + dispatcher = new Dispatcher(); + idleConnections = Collections.synchronizedSet (new HashSet()); + allConnections = Collections.synchronizedSet (new HashSet()); + reqConnections = Collections.synchronizedSet (new HashSet()); + rspConnections = Collections.synchronizedSet (new HashSet()); + time = System.currentTimeMillis(); + timer = new Timer ("server-timer", true); + timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK); + if (timer1Enabled) { + timer1 = new Timer ("server-timer1", true); + timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS); + logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS); + logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME); + logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME); + } + events = new LinkedList(); + logger.config ("HttpServer created "+protocol+" "+ addr); + } + + public void bind (InetSocketAddress addr, int backlog) throws IOException { + if (bound) { + throw new BindException ("HttpServer already bound"); + } + if (addr == null) { + throw new NullPointerException ("null address"); + } + ServerSocket socket = schan.socket(); + socket.bind (addr, backlog); + bound = true; + } + + public void start () { + if (!bound || started || finished) { + throw new IllegalStateException ("server in wrong state"); + } + if (executor == null) { + executor = new DefaultExecutor(); + } + Thread t = new Thread (dispatcher); + started = true; + t.start(); + } + + public void setExecutor (Executor executor) { + if (started) { + throw new IllegalStateException ("server already started"); + } + this.executor = executor; + } + + private static class DefaultExecutor implements Executor { + public void execute (Runnable task) { + task.run(); + } + } + + public Executor getExecutor () { + return executor; + } + + public void setHttpsConfigurator (HttpsConfigurator config) { + if (config == null) { + throw new NullPointerException ("null HttpsConfigurator"); + } + if (started) { + throw new IllegalStateException ("server already started"); + } + this.httpsConfig = config; + sslContext = config.getSSLContext(); + } + + public HttpsConfigurator getHttpsConfigurator () { + return httpsConfig; + } + + public void stop (int delay) { + if (delay < 0) { + throw new IllegalArgumentException ("negative delay parameter"); + } + terminating = true; + try { schan.close(); } catch (IOException e) {} + selector.wakeup(); + long latest = System.currentTimeMillis() + delay * 1000; + while (System.currentTimeMillis() < latest) { + delay(); + if (finished) { + break; + } + } + finished = true; + selector.wakeup(); + synchronized (allConnections) { + for (HttpConnection c : allConnections) { + c.close(); + } + } + allConnections.clear(); + idleConnections.clear(); + timer.cancel(); + if (timer1Enabled) { + timer1.cancel(); + } + } + + Dispatcher dispatcher; + + public synchronized HttpContextImpl createContext (String path, HttpHandler handler) { + if (handler == null || path == null) { + throw new NullPointerException ("null handler, or path parameter"); + } + HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this); + contexts.add (context); + logger.config ("context created: " + path); + return context; + } + + public synchronized HttpContextImpl createContext (String path) { + if (path == null) { + throw new NullPointerException ("null path parameter"); + } + HttpContextImpl context = new HttpContextImpl (protocol, path, null, this); + contexts.add (context); + logger.config ("context created: " + path); + return context; + } + + public synchronized void removeContext (String path) throws IllegalArgumentException { + if (path == null) { + throw new NullPointerException ("null path parameter"); + } + contexts.remove (protocol, path); + logger.config ("context removed: " + path); + } + + public synchronized void removeContext (HttpContext context) throws IllegalArgumentException { + if (!(context instanceof HttpContextImpl)) { + throw new IllegalArgumentException ("wrong HttpContext type"); + } + contexts.remove ((HttpContextImpl)context); + logger.config ("context removed: " + context.getPath()); + } + + public InetSocketAddress getAddress() { + return AccessController.doPrivileged( + new PrivilegedAction() { + public InetSocketAddress run() { + return + (InetSocketAddress)schan.socket() + .getLocalSocketAddress(); + } + }); + } + + Selector getSelector () { + return selector; + } + + void addEvent (Event r) { + synchronized (lolock) { + events.add (r); + selector.wakeup(); + } + } + + /* main server listener task */ + + class Dispatcher implements Runnable { + + private void handleEvent (Event r) { + ExchangeImpl t = r.exchange; + HttpConnection c = t.getConnection(); + try { + if (r instanceof WriteFinishedEvent) { + + int exchanges = endExchange(); + if (terminating && exchanges == 0) { + finished = true; + } + responseCompleted (c); + LeftOverInputStream is = t.getOriginalInputStream(); + if (!is.isEOF()) { + t.close = true; + } + if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) { + c.close(); + allConnections.remove (c); + } else { + if (is.isDataBuffered()) { + /* don't re-enable the interestops, just handle it */ + requestStarted (c); + handle (c.getChannel(), c); + } else { + connsToRegister.add (c); + } + } + } + } catch (IOException e) { + logger.log ( + Level.FINER, "Dispatcher (1)", e + ); + c.close(); + } + } + + final LinkedList connsToRegister = + new LinkedList(); + + void reRegister (HttpConnection c) { + /* re-register with selector */ + try { + SocketChannel chan = c.getChannel(); + chan.configureBlocking (false); + SelectionKey key = chan.register (selector, SelectionKey.OP_READ); + key.attach (c); + c.selectionKey = key; + c.time = getTime() + IDLE_INTERVAL; + idleConnections.add (c); + } catch (IOException e) { + dprint(e); + logger.log(Level.FINER, "Dispatcher(8)", e); + c.close(); + } + } + + public void run() { + while (!finished) { + try { + List list = null; + synchronized (lolock) { + if (events.size() > 0) { + list = events; + events = new LinkedList(); + } + } + + if (list != null) { + for (Event r: list) { + handleEvent (r); + } + } + + for (HttpConnection c : connsToRegister) { + reRegister(c); + } + connsToRegister.clear(); + + selector.select(1000); + + /* process the selected list now */ + Set selected = selector.selectedKeys(); + Iterator iter = selected.iterator(); + while (iter.hasNext()) { + SelectionKey key = iter.next(); + iter.remove (); + if (key.equals (listenerKey)) { + if (terminating) { + continue; + } + SocketChannel chan = schan.accept(); + + // Set TCP_NODELAY, if appropriate + if (ServerConfig.noDelay()) { + chan.socket().setTcpNoDelay(true); + } + + if (chan == null) { + continue; /* cancel something ? */ + } + chan.configureBlocking (false); + SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ); + HttpConnection c = new HttpConnection (); + c.selectionKey = newkey; + c.setChannel (chan); + newkey.attach (c); + requestStarted (c); + allConnections.add (c); + } else { + try { + if (key.isReadable()) { + boolean closed; + SocketChannel chan = (SocketChannel)key.channel(); + HttpConnection conn = (HttpConnection)key.attachment(); + + key.cancel(); + chan.configureBlocking (true); + if (idleConnections.remove(conn)) { + // was an idle connection so add it + // to reqConnections set. + requestStarted (conn); + } + handle (chan, conn); + } else { + assert false; + } + } catch (CancelledKeyException e) { + handleException(key, null); + } catch (IOException e) { + handleException(key, e); + } + } + } + // call the selector just to process the cancelled keys + selector.selectNow(); + } catch (IOException e) { + logger.log (Level.FINER, "Dispatcher (4)", e); + } catch (Exception e) { + logger.log (Level.FINER, "Dispatcher (7)", e); + } + } + try {selector.close(); } catch (Exception e) {} + } + + private void handleException (SelectionKey key, Exception e) { + HttpConnection conn = (HttpConnection)key.attachment(); + if (e != null) { + logger.log (Level.FINER, "Dispatcher (2)", e); + } + closeConnection(conn); + } + + public void handle (SocketChannel chan, HttpConnection conn) + throws IOException + { + try { + Exchange t = new Exchange (chan, protocol, conn); + executor.execute (t); + } catch (HttpError e1) { + logger.log (Level.FINER, "Dispatcher (4)", e1); + closeConnection(conn); + } catch (IOException e) { + logger.log (Level.FINER, "Dispatcher (5)", e); + closeConnection(conn); + } + } + } + + static boolean debug = ServerConfig.debugEnabled (); + + static synchronized void dprint (String s) { + if (debug) { + System.out.println (s); + } + } + + static synchronized void dprint (Exception e) { + if (debug) { + System.out.println (e); + e.printStackTrace(); + } + } + + Logger getLogger () { + return logger; + } + + private void closeConnection(HttpConnection conn) { + conn.close(); + allConnections.remove(conn); + switch (conn.getState()) { + case REQUEST: + reqConnections.remove(conn); + break; + case RESPONSE: + rspConnections.remove(conn); + break; + case IDLE: + idleConnections.remove(conn); + break; + } + assert !reqConnections.remove(conn); + assert !rspConnections.remove(conn); + assert !idleConnections.remove(conn); + } + + /* per exchange task */ + + class Exchange implements Runnable { + SocketChannel chan; + HttpConnection connection; + HttpContextImpl context; + InputStream rawin; + OutputStream rawout; + String protocol; + ExchangeImpl tx; + HttpContextImpl ctx; + boolean rejected = false; + + Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException { + this.chan = chan; + this.connection = conn; + this.protocol = protocol; + } + + public void run () { + /* context will be null for new connections */ + context = connection.getHttpContext(); + boolean newconnection; + SSLEngine engine = null; + String requestLine = null; + SSLStreams sslStreams = null; + try { + if (context != null ) { + this.rawin = connection.getInputStream(); + this.rawout = connection.getRawOutputStream(); + newconnection = false; + } else { + /* figure out what kind of connection this is */ + newconnection = true; + if (https) { + if (sslContext == null) { + logger.warning ("SSL connection received. No https contxt created"); + throw new HttpError ("No SSL context established"); + } + sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan); + rawin = sslStreams.getInputStream(); + rawout = sslStreams.getOutputStream(); + engine = sslStreams.getSSLEngine(); + connection.sslStreams = sslStreams; + } else { + rawin = new BufferedInputStream( + new Request.ReadStream ( + ServerImpl.this, chan + )); + rawout = new Request.WriteStream ( + ServerImpl.this, chan + ); + } + connection.raw = rawin; + connection.rawout = rawout; + } + Request req = new Request (rawin, rawout); + requestLine = req.requestLine(); + if (requestLine == null) { + /* connection closed */ + closeConnection(connection); + return; + } + int space = requestLine.indexOf (' '); + if (space == -1) { + reject (Code.HTTP_BAD_REQUEST, + requestLine, "Bad request line"); + return; + } + String method = requestLine.substring (0, space); + int start = space+1; + space = requestLine.indexOf(' ', start); + if (space == -1) { + reject (Code.HTTP_BAD_REQUEST, + requestLine, "Bad request line"); + return; + } + String uriStr = requestLine.substring (start, space); + URI uri = new URI (uriStr); + start = space+1; + String version = requestLine.substring (start); + Headers headers = req.headers(); + String s = headers.getFirst ("Transfer-encoding"); + long clen = 0L; + if (s !=null && s.equalsIgnoreCase ("chunked")) { + clen = -1L; + } else { + s = headers.getFirst ("Content-Length"); + if (s != null) { + clen = Long.parseLong(s); + } + if (clen == 0) { + requestCompleted (connection); + } + } + ctx = contexts.findContext (protocol, uri.getPath()); + if (ctx == null) { + reject (Code.HTTP_NOT_FOUND, + requestLine, "No context found for request"); + return; + } + connection.setContext (ctx); + if (ctx.getHandler() == null) { + reject (Code.HTTP_INTERNAL_ERROR, + requestLine, "No handler for context"); + return; + } + tx = new ExchangeImpl ( + method, uri, req, clen, connection + ); + String chdr = headers.getFirst("Connection"); + Headers rheaders = tx.getResponseHeaders(); + + if (chdr != null && chdr.equalsIgnoreCase ("close")) { + tx.close = true; + } + if (version.equalsIgnoreCase ("http/1.0")) { + tx.http10 = true; + if (chdr == null) { + tx.close = true; + rheaders.set ("Connection", "close"); + } else if (chdr.equalsIgnoreCase ("keep-alive")) { + rheaders.set ("Connection", "keep-alive"); + int idle=(int)(ServerConfig.getIdleInterval()/1000); + int max=ServerConfig.getMaxIdleConnections(); + String val = "timeout="+idle+", max="+max; + rheaders.set ("Keep-Alive", val); + } + } + + if (newconnection) { + connection.setParameters ( + rawin, rawout, chan, engine, sslStreams, + sslContext, protocol, ctx, rawin + ); + } + /* check if client sent an Expect 100 Continue. + * In that case, need to send an interim response. + * In future API may be modified to allow app to + * be involved in this process. + */ + String exp = headers.getFirst("Expect"); + if (exp != null && exp.equalsIgnoreCase ("100-continue")) { + logReply (100, requestLine, null); + sendReply ( + Code.HTTP_CONTINUE, false, null + ); + } + /* uf is the list of filters seen/set by the user. + * sf is the list of filters established internally + * and which are not visible to the user. uc and sc + * are the corresponding Filter.Chains. + * They are linked together by a LinkHandler + * so that they can both be invoked in one call. + */ + List sf = ctx.getSystemFilters(); + List uf = ctx.getFilters(); + + Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler()); + Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc)); + + /* set up the two stream references */ + tx.getRequestBody(); + tx.getResponseBody(); + if (https) { + uc.doFilter (new HttpsExchangeImpl (tx)); + } else { + uc.doFilter (new HttpExchangeImpl (tx)); + } + + } catch (IOException e1) { + logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1); + closeConnection(connection); + } catch (NumberFormatException e3) { + reject (Code.HTTP_BAD_REQUEST, + requestLine, "NumberFormatException thrown"); + } catch (URISyntaxException e) { + reject (Code.HTTP_BAD_REQUEST, + requestLine, "URISyntaxException thrown"); + } catch (Exception e4) { + logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4); + closeConnection(connection); + } + } + + /* used to link to 2 or more Filter.Chains together */ + + class LinkHandler implements HttpHandler { + Filter.Chain nextChain; + + LinkHandler (Filter.Chain nextChain) { + this.nextChain = nextChain; + } + + public void handle (HttpExchange exchange) throws IOException { + nextChain.doFilter (exchange); + } + } + + void reject (int code, String requestStr, String message) { + rejected = true; + logReply (code, requestStr, message); + sendReply ( + code, false, "

    "+code+Code.msg(code)+"

    "+message + ); + closeConnection(connection); + } + + void sendReply ( + int code, boolean closeNow, String text) + { + try { + StringBuilder builder = new StringBuilder (512); + builder.append ("HTTP/1.1 ") + .append (code).append (Code.msg(code)).append ("\r\n"); + + if (text != null && text.length() != 0) { + builder.append ("Content-Length: ") + .append (text.length()).append ("\r\n") + .append ("Content-Type: text/html\r\n"); + } else { + builder.append ("Content-Length: 0\r\n"); + text = ""; + } + if (closeNow) { + builder.append ("Connection: close\r\n"); + } + builder.append ("\r\n").append (text); + String s = builder.toString(); + byte[] b = s.getBytes("ISO8859_1"); + rawout.write (b); + rawout.flush(); + if (closeNow) { + closeConnection(connection); + } + } catch (IOException e) { + logger.log (Level.FINER, "ServerImpl.sendReply", e); + closeConnection(connection); + } + } + + } + + void logReply (int code, String requestStr, String text) { + if (!logger.isLoggable(Level.FINE)) { + return; + } + if (text == null) { + text = ""; + } + String r; + if (requestStr.length() > 80) { + r = requestStr.substring (0, 80) + ""; + } else { + r = requestStr; + } + String message = r + " [" + code + " " + + Code.msg(code) + "] ("+text+")"; + logger.fine (message); + } + + long getTicks() { + return ticks; + } + + public long getTime() { + return time; + } + + void delay () { + Thread.yield(); + try { + Thread.sleep (200); + } catch (InterruptedException e) {} + } + + private int exchangeCount = 0; + + synchronized void startExchange () { + exchangeCount ++; + } + + synchronized int endExchange () { + exchangeCount --; + assert exchangeCount >= 0; + return exchangeCount; + } + + HttpServer getWrapper () { + return wrapper; + } + + void requestStarted (HttpConnection c) { + c.creationTime = getTime(); + c.setState (State.REQUEST); + reqConnections.add (c); + } + + // called after a request has been completely read + // by the server. This stops the timer which would + // close the connection if the request doesn't arrive + // quickly enough. It then starts the timer + // that ensures the client reads the response in a timely + // fashion. + + void requestCompleted (HttpConnection c) { + assert c.getState() == State.REQUEST; + reqConnections.remove (c); + c.rspStartedTime = getTime(); + rspConnections.add (c); + c.setState (State.RESPONSE); + } + + // called after response has been sent + void responseCompleted (HttpConnection c) { + assert c.getState() == State.RESPONSE; + rspConnections.remove (c); + c.setState (State.IDLE); + } + + /** + * TimerTask run every CLOCK_TICK ms + */ + class ServerTimerTask extends TimerTask { + public void run () { + LinkedList toClose = new LinkedList(); + time = System.currentTimeMillis(); + ticks ++; + synchronized (idleConnections) { + for (HttpConnection c : idleConnections) { + if (c.time <= time) { + toClose.add (c); + } + } + for (HttpConnection c : toClose) { + idleConnections.remove (c); + allConnections.remove (c); + c.close(); + } + } + } + } + + class ServerTimerTask1 extends TimerTask { + + // runs every TIMER_MILLIS + public void run () { + LinkedList toClose = new LinkedList(); + time = System.currentTimeMillis(); + synchronized (reqConnections) { + if (MAX_REQ_TIME != -1) { + for (HttpConnection c : reqConnections) { + if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) { + toClose.add (c); + } + } + for (HttpConnection c : toClose) { + logger.log (Level.FINE, "closing: no request: " + c); + reqConnections.remove (c); + allConnections.remove (c); + c.close(); + } + } + } + toClose = new LinkedList(); + synchronized (rspConnections) { + if (MAX_RSP_TIME != -1) { + for (HttpConnection c : rspConnections) { + if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) { + toClose.add (c); + } + } + for (HttpConnection c : toClose) { + logger.log (Level.FINE, "closing: no response: " + c); + rspConnections.remove (c); + allConnections.remove (c); + c.close(); + } + } + } + } + } + + void logStackTrace (String s) { + logger.finest (s); + StringBuilder b = new StringBuilder (); + StackTraceElement[] e = Thread.currentThread().getStackTrace(); + for (int i=0; i get(Object key) { + return map.get(key); + } + + public String getFirst (String key) { + return map.getFirst(key); + } + + + public List put(String key, List value) { + return map.put (key, value); + } + + public void add (String key, String value) { + throw new UnsupportedOperationException ("unsupported operation"); + } + + public void set (String key, String value) { + throw new UnsupportedOperationException ("unsupported operation"); + } + + public List remove(Object key) { + throw new UnsupportedOperationException ("unsupported operation"); + } + + public void putAll(Map> t) { + throw new UnsupportedOperationException ("unsupported operation"); + } + + public void clear() { + throw new UnsupportedOperationException ("unsupported operation"); + } + + public Set keySet() { + return Collections.unmodifiableSet (map.keySet()); + } + + public Collection> values() { + return Collections.unmodifiableCollection(map.values()); + } + + /* TODO check that contents of set are not modifable : security */ + + public Set>> entrySet() { + return Collections.unmodifiableSet (map.entrySet()); + } + + public boolean equals(Object o) {return map.equals(o);} + + public int hashCode() {return map.hashCode();} + } diff --git a/src/sun/net/httpserver/WriteFinishedEvent.java b/src/sun/net/httpserver/WriteFinishedEvent.java new file mode 100644 index 00000000..6a04129d --- /dev/null +++ b/src/sun/net/httpserver/WriteFinishedEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.httpserver; + +class WriteFinishedEvent extends Event { + WriteFinishedEvent (ExchangeImpl t) { + super (t); + assert !t.writefinished; + t.writefinished = true; + } +} diff --git a/src/sun/net/idn/Punycode.java b/src/sun/net/idn/Punycode.java new file mode 100644 index 00000000..00279131 --- /dev/null +++ b/src/sun/net/idn/Punycode.java @@ -0,0 +1,511 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* + ******************************************************************************* + * Copyright (C) 2003-2004, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +// +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/text/Punycode.java +// - move from package com.ibm.icu.text to package sun.net.idn +// - use ParseException instead of StringPrepParseException +// 2007-08-14 Martin Buchholz +// - remove redundant casts +// +package sun.net.idn; + +import java.text.ParseException; +import sun.text.normalizer.UCharacter; +import sun.text.normalizer.UTF16; + +/** + * Ported code from ICU punycode.c + * @author ram + */ + +/* Package Private class */ +public final class Punycode { + + /* Punycode parameters for Bootstring */ + private static final int BASE = 36; + private static final int TMIN = 1; + private static final int TMAX = 26; + private static final int SKEW = 38; + private static final int DAMP = 700; + private static final int INITIAL_BIAS = 72; + private static final int INITIAL_N = 0x80; + + /* "Basic" Unicode/ASCII code points */ + private static final int HYPHEN = 0x2d; + private static final int DELIMITER = HYPHEN; + + private static final int ZERO = 0x30; + private static final int NINE = 0x39; + + private static final int SMALL_A = 0x61; + private static final int SMALL_Z = 0x7a; + + private static final int CAPITAL_A = 0x41; + private static final int CAPITAL_Z = 0x5a; + + // TODO: eliminate the 256 limitation + private static final int MAX_CP_COUNT = 256; + + private static final int UINT_MAGIC = 0x80000000; + private static final long ULONG_MAGIC = 0x8000000000000000L; + + private static int adaptBias(int delta, int length, boolean firstTime){ + if(firstTime){ + delta /=DAMP; + }else{ + delta /= 2; + } + delta += delta/length; + + int count=0; + for(; delta>((BASE-TMIN)*TMAX)/2; count+=BASE) { + delta/=(BASE-TMIN); + } + + return count+(((BASE-TMIN+1)*delta)/(delta+SKEW)); + } + + /** + * basicToDigit[] contains the numeric value of a basic code + * point (for use in representing integers) in the range 0 to + * BASE-1, or -1 if b is does not represent a value. + */ + static final int[] basicToDigit= new int[]{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + private static char asciiCaseMap(char b, boolean uppercase) { + if(uppercase) { + if(SMALL_A<=b && b<=SMALL_Z) { + b-=(SMALL_A-CAPITAL_A); + } + } else { + if(CAPITAL_A<=b && b<=CAPITAL_Z) { + b+=(SMALL_A-CAPITAL_A); + } + } + return b; + } + + /** + * digitToBasic() returns the basic code point whose value + * (when used for representing integers) is d, which must be in the + * range 0 to BASE-1. The lowercase form is used unless the uppercase flag is + * nonzero, in which case the uppercase form is used. + */ + private static char digitToBasic(int digit, boolean uppercase) { + /* 0..25 map to ASCII a..z or A..Z */ + /* 26..35 map to ASCII 0..9 */ + if(digit<26) { + if(uppercase) { + return (char)(CAPITAL_A+digit); + } else { + return (char)(SMALL_A+digit); + } + } else { + return (char)((ZERO-26)+digit); + } + } + /** + * Converts Unicode to Punycode. + * The input string must not contain single, unpaired surrogates. + * The output will be represented as an array of ASCII code points. + * + * @param src + * @param caseFlags + * @return + * @throws ParseException + */ + public static StringBuffer encode(StringBuffer src, boolean[] caseFlags) throws ParseException{ + + int[] cpBuffer = new int[MAX_CP_COUNT]; + int n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; + char c, c2; + int srcLength = src.length(); + int destCapacity = MAX_CP_COUNT; + char[] dest = new char[destCapacity]; + StringBuffer result = new StringBuffer(); + /* + * Handle the basic code points and + * convert extended ones to UTF-32 in cpBuffer (caseFlag in sign bit): + */ + srcCPCount=destLength=0; + + for(j=0; j0) { + if(destLength state to , but guard against overflow: + */ + if(m-n>(0x7fffffff-MAX_CP_COUNT-delta)/(handledCPCount+1)) { + throw new RuntimeException("Internal program error"); + } + delta+=(m-n)*(handledCPCount+1); + n=m; + + /* Encode a sequence of same code points n */ + for(j=0; jTMAX) { + t=TMAX; + } + */ + + t=k-bias; + if(t=(bias+TMAX)) { + t=TMAX; + } + + if(q0;) { + if(src.charAt(--j)==DELIMITER) { + break; + } + } + destLength=basicLength=destCPCount=j; + + while(j>0) { + b=src.charAt(--j); + if(!isBasic(b)) { + throw new ParseException("Illegal char found", -1); + } + + if(j0 ? basicLength+1 : 0; in=srcLength) { + throw new ParseException("Illegal char found", -1); + } + + digit=basicToDigit[(byte)src.charAt(in++)]; + if(digit<0) { + throw new ParseException("Invalid char found", -1); + } + if(digit>(0x7fffffff-i)/w) { + /* integer overflow */ + throw new ParseException("Illegal char found", -1); + } + + i+=digit*w; + t=k-bias; + if(t=(bias+TMAX)) { + t=TMAX; + } + if(digit0x7fffffff/(BASE-t)) { + /* integer overflow */ + throw new ParseException("Illegal char found", -1); + } + w*=BASE-t; + } + + /* + * Modification from sample code: + * Increments destCPCount here, + * where needed instead of in for() loop tail. + */ + ++destCPCount; + bias=adaptBias(i-oldi, destCPCount, (oldi==0)); + + /* + * i was supposed to wrap around from (incremented) destCPCount to 0, + * incrementing n each time, so we'll fix that now: + */ + if(i/destCPCount>(0x7fffffff-n)) { + /* integer overflow */ + throw new ParseException("Illegal char found", -1); + } + + n+=i/destCPCount; + i%=destCPCount; + /* not needed for Punycode: */ + /* if (decode_digit(n) <= BASE) return punycode_invalid_input; */ + + if(n>0x10ffff || isSurrogate(n)) { + /* Unicode code point overflow */ + throw new ParseException("Illegal char found", -1); + } + + /* Insert n at position i of the output: */ + cpLength=UTF16.getCharCount(n); + if((destLength+cpLength)1) { + firstSupplementaryIndex=codeUnitIndex; + } else { + ++firstSupplementaryIndex; + } + } else { + codeUnitIndex=firstSupplementaryIndex; + codeUnitIndex=UTF16.moveCodePointOffset(dest, 0, destLength, codeUnitIndex, i-codeUnitIndex); + } + + /* use the UChar index codeUnitIndex instead of the code point index i */ + if(codeUnitIndexRFC 3454. + * StringPrep prepares Unicode strings for use in network protocols. + * Profiles of StingPrep are set of rules and data according to which the + * Unicode Strings are prepared. Each profiles contains tables which describe + * how a code point should be treated. The tables are broadly classied into + *
      + *
    • Unassigned Table: Contains code points that are unassigned + * in the Unicode Version supported by StringPrep. Currently + * RFC 3454 supports Unicode 3.2.
    • + *
    • Prohibited Table: Contains code points that are prohibted from + * the output of the StringPrep processing function.
    • + *
    • Mapping Table: Contains code ponts that are deleted from the output or case mapped.
    • + *
    + * + * The procedure for preparing Unicode strings: + *
      + *
    1. Map: For each character in the input, check if it has a mapping + * and, if so, replace it with its mapping.
    2. + *
    3. Normalize: Possibly normalize the result of step 1 using Unicode + * normalization.
    4. + *
    5. Prohibit: Check for any characters that are not allowed in the + * output. If any are found, return an error.
    6. + *
    7. Check bidi: Possibly check for right-to-left characters, and if + * any are found, make sure that the whole string satisfies the + * requirements for bidirectional strings. If the string does not + * satisfy the requirements for bidirectional strings, return an + * error.
    8. + *
    + * @author Ram Viswanadha + * @draft ICU 2.8 + */ +public final class StringPrep { + /** + * Option to prohibit processing of unassigned code points in the input + * + * @see #prepare + * @draft ICU 2.8 + */ + public static final int DEFAULT = 0x0000; + + /** + * Option to allow processing of unassigned code points in the input + * + * @see #prepare + * @draft ICU 2.8 + */ + public static final int ALLOW_UNASSIGNED = 0x0001; + + private static final int UNASSIGNED = 0x0000; + private static final int MAP = 0x0001; + private static final int PROHIBITED = 0x0002; + private static final int DELETE = 0x0003; + private static final int TYPE_LIMIT = 0x0004; + + private static final int NORMALIZATION_ON = 0x0001; + private static final int CHECK_BIDI_ON = 0x0002; + + private static final int TYPE_THRESHOLD = 0xFFF0; + private static final int MAX_INDEX_VALUE = 0x3FBF; /*16139*/ + private static final int MAX_INDEX_TOP_LENGTH = 0x0003; + + /* indexes[] value names */ + private static final int INDEX_TRIE_SIZE = 0; /* number of bytes in normalization trie */ + private static final int INDEX_MAPPING_DATA_SIZE = 1; /* The array that contains the mapping */ + private static final int NORM_CORRECTNS_LAST_UNI_VERSION = 2; /* The index of Unicode version of last entry in NormalizationCorrections.txt */ + private static final int ONE_UCHAR_MAPPING_INDEX_START = 3; /* The starting index of 1 UChar mapping index in the mapping data array */ + private static final int TWO_UCHARS_MAPPING_INDEX_START = 4; /* The starting index of 2 UChars mapping index in the mapping data array */ + private static final int THREE_UCHARS_MAPPING_INDEX_START = 5; + private static final int FOUR_UCHARS_MAPPING_INDEX_START = 6; + private static final int OPTIONS = 7; /* Bit set of options to turn on in the profile */ + private static final int INDEX_TOP = 16; /* changing this requires a new formatVersion */ + + + /** + * Default buffer size of datafile + */ + private static final int DATA_BUFFER_SIZE = 25000; + + /* Wrappers for Trie implementations */ + private static final class StringPrepTrieImpl implements Trie.DataManipulate{ + private CharTrie sprepTrie = null; + /** + * Called by com.ibm.icu.util.Trie to extract from a lead surrogate's + * data the index array offset of the indexes for that lead surrogate. + * @param property data value for a surrogate from the trie, including + * the folding offset + * @return data offset or 0 if there is no data for the lead surrogate + */ + public int getFoldingOffset(int value){ + return value; + } + } + + // CharTrie implementation for reading the trie data + private StringPrepTrieImpl sprepTrieImpl; + // Indexes read from the data file + private int[] indexes; + // mapping data read from the data file + private char[] mappingData; + // format version of the data file + private byte[] formatVersion; + // the version of Unicode supported by the data file + private VersionInfo sprepUniVer; + // the Unicode version of last entry in the + // NormalizationCorrections.txt file if normalization + // is turned on + private VersionInfo normCorrVer; + // Option to turn on Normalization + private boolean doNFKC; + // Option to turn on checking for BiDi rules + private boolean checkBiDi; + + + private char getCodePointValue(int ch){ + return sprepTrieImpl.sprepTrie.getCodePointValue(ch); + } + + private static VersionInfo getVersionInfo(int comp){ + int micro = comp & 0xFF; + int milli =(comp >> 8) & 0xFF; + int minor =(comp >> 16) & 0xFF; + int major =(comp >> 24) & 0xFF; + return VersionInfo.getInstance(major,minor,milli,micro); + } + private static VersionInfo getVersionInfo(byte[] version){ + if(version.length != 4){ + return null; + } + return VersionInfo.getInstance((int)version[0],(int) version[1],(int) version[2],(int) version[3]); + } + /** + * Creates an StringPrep object after reading the input stream. + * The object does not hold a reference to the input steam, so the stream can be + * closed after the method returns. + * + * @param inputStream The stream for reading the StringPrep profile binarySun + * @throws IOException + * @draft ICU 2.8 + */ + public StringPrep(InputStream inputStream) throws IOException{ + + BufferedInputStream b = new BufferedInputStream(inputStream,DATA_BUFFER_SIZE); + + StringPrepDataReader reader = new StringPrepDataReader(b); + + // read the indexes + indexes = reader.readIndexes(INDEX_TOP); + + byte[] sprepBytes = new byte[indexes[INDEX_TRIE_SIZE]]; + + + //indexes[INDEX_MAPPING_DATA_SIZE] store the size of mappingData in bytes + mappingData = new char[indexes[INDEX_MAPPING_DATA_SIZE]/2]; + // load the rest of the data data and initialize the data members + reader.read(sprepBytes,mappingData); + + sprepTrieImpl = new StringPrepTrieImpl(); + sprepTrieImpl.sprepTrie = new CharTrie( new ByteArrayInputStream(sprepBytes),sprepTrieImpl ); + + // get the data format version + formatVersion = reader.getDataFormatVersion(); + + // get the options + doNFKC = ((indexes[OPTIONS] & NORMALIZATION_ON) > 0); + checkBiDi = ((indexes[OPTIONS] & CHECK_BIDI_ON) > 0); + sprepUniVer = getVersionInfo(reader.getUnicodeVersion()); + normCorrVer = getVersionInfo(indexes[NORM_CORRECTNS_LAST_UNI_VERSION]); + VersionInfo normUniVer = NormalizerImpl.getUnicodeVersion(); + if(normUniVer.compareTo(sprepUniVer) < 0 && /* the Unicode version of SPREP file must be less than the Unicode Vesion of the normalization data */ + normUniVer.compareTo(normCorrVer) < 0 && /* the Unicode version of the NormalizationCorrections.txt file should be less than the Unicode Vesion of the normalization data */ + ((indexes[OPTIONS] & NORMALIZATION_ON) > 0) /* normalization turned on*/ + ){ + throw new IOException("Normalization Correction version not supported"); + } + b.close(); + } + + private static final class Values{ + boolean isIndex; + int value; + int type; + public void reset(){ + isIndex = false; + value = 0; + type = -1; + } + } + + private static final void getValues(char trieWord,Values values){ + values.reset(); + if(trieWord == 0){ + /* + * Initial value stored in the mapping table + * just return TYPE_LIMIT .. so that + * the source codepoint is copied to the destination + */ + values.type = TYPE_LIMIT; + }else if(trieWord >= TYPE_THRESHOLD){ + values.type = (trieWord - TYPE_THRESHOLD); + }else{ + /* get the type */ + values.type = MAP; + /* ascertain if the value is index or delta */ + if((trieWord & 0x02)>0){ + values.isIndex = true; + values.value = trieWord >> 2; //mask off the lower 2 bits and shift + + }else{ + values.isIndex = false; + values.value = (trieWord<<16)>>16; + values.value = (values.value >> 2); + + } + + if((trieWord>>2) == MAX_INDEX_VALUE){ + values.type = DELETE; + values.isIndex = false; + values.value = 0; + } + } + } + + + + private StringBuffer map( UCharacterIterator iter, int options) + throws ParseException { + + Values val = new Values(); + char result = 0; + int ch = UCharacterIterator.DONE; + StringBuffer dest = new StringBuffer(); + boolean allowUnassigned = ((options & ALLOW_UNASSIGNED)>0); + + while((ch=iter.nextCodePoint())!= UCharacterIterator.DONE){ + + result = getCodePointValue(ch); + getValues(result,val); + + // check if the source codepoint is unassigned + if(val.type == UNASSIGNED && allowUnassigned == false){ + throw new ParseException("An unassigned code point was found in the input " + + iter.getText(), iter.getIndex()); + }else if((val.type == MAP)){ + int index, length; + + if(val.isIndex){ + index = val.value; + if(index >= indexes[ONE_UCHAR_MAPPING_INDEX_START] && + index < indexes[TWO_UCHARS_MAPPING_INDEX_START]){ + length = 1; + }else if(index >= indexes[TWO_UCHARS_MAPPING_INDEX_START] && + index < indexes[THREE_UCHARS_MAPPING_INDEX_START]){ + length = 2; + }else if(index >= indexes[THREE_UCHARS_MAPPING_INDEX_START] && + index < indexes[FOUR_UCHARS_MAPPING_INDEX_START]){ + length = 3; + }else{ + length = mappingData[index++]; + } + /* copy mapping to destination */ + dest.append(mappingData,index,length); + continue; + + }else{ + ch -= val.value; + } + }else if(val.type == DELETE){ + // just consume the codepoint and contine + continue; + } + //copy the source into destination + UTF16.append(dest,ch); + } + + return dest; + } + + + private StringBuffer normalize(StringBuffer src){ + /* + * Option UNORM_BEFORE_PRI_29: + * + * IDNA as interpreted by IETF members (see unicode mailing list 2004H1) + * requires strict adherence to Unicode 3.2 normalization, + * including buggy composition from before fixing Public Review Issue #29. + * Note that this results in some valid but nonsensical text to be + * either corrupted or rejected, depending on the text. + * See http://www.unicode.org/review/resolved-pri.html#pri29 + * See unorm.cpp and cnormtst.c + */ + return new StringBuffer( + Normalizer.normalize( + src.toString(), + java.text.Normalizer.Form.NFKC, + Normalizer.UNICODE_3_2|NormalizerImpl.BEFORE_PRI_29)); + } + /* + boolean isLabelSeparator(int ch){ + int result = getCodePointValue(ch); + if( (result & 0x07) == LABEL_SEPARATOR){ + return true; + } + return false; + } + */ + /* + 1) Map -- For each character in the input, check if it has a mapping + and, if so, replace it with its mapping. + + 2) Normalize -- Possibly normalize the result of step 1 using Unicode + normalization. + + 3) Prohibit -- Check for any characters that are not allowed in the + output. If any are found, return an error. + + 4) Check bidi -- Possibly check for right-to-left characters, and if + any are found, make sure that the whole string satisfies the + requirements for bidirectional strings. If the string does not + satisfy the requirements for bidirectional strings, return an + error. + [Unicode3.2] defines several bidirectional categories; each character + has one bidirectional category assigned to it. For the purposes of + the requirements below, an "RandALCat character" is a character that + has Unicode bidirectional categories "R" or "AL"; an "LCat character" + is a character that has Unicode bidirectional category "L". Note + + + that there are many characters which fall in neither of the above + definitions; Latin digits ( through ) are examples of + this because they have bidirectional category "EN". + + In any profile that specifies bidirectional character handling, all + three of the following requirements MUST be met: + + 1) The characters in section 5.8 MUST be prohibited. + + 2) If a string contains any RandALCat character, the string MUST NOT + contain any LCat character. + + 3) If a string contains any RandALCat character, a RandALCat + character MUST be the first character of the string, and a + RandALCat character MUST be the last character of the string. + */ + /** + * Prepare the input buffer for use in applications with the given profile. This operation maps, normalizes(NFKC), + * checks for prohited and BiDi characters in the order defined by RFC 3454 + * depending on the options specified in the profile. + * + * @param src A UCharacterIterator object containing the source string + * @param options A bit set of options: + * + * - StringPrep.NONE Prohibit processing of unassigned code points in the input + * + * - StringPrep.ALLOW_UNASSIGNED Treat the unassigned code points are in the input + * as normal Unicode code points. + * + * @return StringBuffer A StringBuffer containing the output + * @throws ParseException + * @draft ICU 2.8 + */ + public StringBuffer prepare(UCharacterIterator src, int options) + throws ParseException{ + + // map + StringBuffer mapOut = map(src,options); + StringBuffer normOut = mapOut;// initialize + + if(doNFKC){ + // normalize + normOut = normalize(mapOut); + } + + int ch; + char result; + UCharacterIterator iter = UCharacterIterator.getInstance(normOut); + Values val = new Values(); + int direction=UCharacterDirection.CHAR_DIRECTION_COUNT, + firstCharDir=UCharacterDirection.CHAR_DIRECTION_COUNT; + int rtlPos=-1, ltrPos=-1; + boolean rightToLeft=false, leftToRight=false; + + while((ch=iter.nextCodePoint())!= UCharacterIterator.DONE){ + result = getCodePointValue(ch); + getValues(result,val); + + if(val.type == PROHIBITED ){ + throw new ParseException("A prohibited code point was found in the input" + + iter.getText(), val.value); + } + + direction = UCharacter.getDirection(ch); + if(firstCharDir == UCharacterDirection.CHAR_DIRECTION_COUNT){ + firstCharDir = direction; + } + if(direction == UCharacterDirection.LEFT_TO_RIGHT){ + leftToRight = true; + ltrPos = iter.getIndex()-1; + } + if(direction == UCharacterDirection.RIGHT_TO_LEFT || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC){ + rightToLeft = true; + rtlPos = iter.getIndex()-1; + } + } + if(checkBiDi == true){ + // satisfy 2 + if( leftToRight == true && rightToLeft == true){ + throw new ParseException("The input does not conform to the rules for BiDi code points." + + iter.getText(), + (rtlPos>ltrPos) ? rtlPos : ltrPos); + } + + //satisfy 3 + if( rightToLeft == true && + !((firstCharDir == UCharacterDirection.RIGHT_TO_LEFT || firstCharDir == UCharacterDirection.RIGHT_TO_LEFT_ARABIC) && + (direction == UCharacterDirection.RIGHT_TO_LEFT || direction == UCharacterDirection.RIGHT_TO_LEFT_ARABIC)) + ){ + throw new ParseException("The input does not conform to the rules for BiDi code points." + + iter.getText(), + (rtlPos>ltrPos) ? rtlPos : ltrPos); + } + } + return normOut; + + } +} diff --git a/src/sun/net/idn/StringPrepDataReader.java b/src/sun/net/idn/StringPrepDataReader.java new file mode 100644 index 00000000..7719e61c --- /dev/null +++ b/src/sun/net/idn/StringPrepDataReader.java @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/* + ****************************************************************************** + * Copyright (C) 2003, International Business Machines Corporation and * + * others. All Rights Reserved. * + ****************************************************************************** + * + * Created on May 2, 2003 + * + * To change the template for this generated file go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/impl/StringPrepDataReader.java +// - move from package com.ibm.icu.impl to package sun.net.idn +// +package sun.net.idn; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +import sun.text.normalizer.ICUBinary; + + +/** + * @author ram + * + * To change the template for this generated type comment go to + * Window>Preferences>Java>Code Generation>Code and Comments + */ +final class StringPrepDataReader implements ICUBinary.Authenticate { + + /** + *

    private constructor.

    + * @param inputStream ICU uprop.dat file input stream + * @exception IOException throw if data file fails authentication + * @draft 2.1 + */ + public StringPrepDataReader(InputStream inputStream) + throws IOException{ + + unicodeVersion = ICUBinary.readHeader(inputStream, DATA_FORMAT_ID, this); + + + dataInputStream = new DataInputStream(inputStream); + + } + + public void read(byte[] idnaBytes, + char[] mappingTable) + throws IOException{ + + //Read the bytes that make up the idnaTrie + dataInputStream.read(idnaBytes); + + //Read the extra data + for(int i=0;iUCharacter + *

    + * This class is not subclassable + *

    + * @author Syn Wee Quek + * @stable ICU 2.1 + */ + +final class UCharacterDirection implements UCharacterEnums.ECharacterDirection { + + // private constructor ========================================= + ///CLOVER:OFF + /** + * Private constructor to prevent initialisation + */ + private UCharacterDirection() + { + } + ///CLOVER:ON + + /** + * Gets the name of the argument direction + * @param dir direction type to retrieve name + * @return directional name + * @stable ICU 2.1 + */ + public static String toString(int dir) { + switch(dir) + { + case LEFT_TO_RIGHT : + return "Left-to-Right"; + case RIGHT_TO_LEFT : + return "Right-to-Left"; + case EUROPEAN_NUMBER : + return "European Number"; + case EUROPEAN_NUMBER_SEPARATOR : + return "European Number Separator"; + case EUROPEAN_NUMBER_TERMINATOR : + return "European Number Terminator"; + case ARABIC_NUMBER : + return "Arabic Number"; + case COMMON_NUMBER_SEPARATOR : + return "Common Number Separator"; + case BLOCK_SEPARATOR : + return "Paragraph Separator"; + case SEGMENT_SEPARATOR : + return "Segment Separator"; + case WHITE_SPACE_NEUTRAL : + return "Whitespace"; + case OTHER_NEUTRAL : + return "Other Neutrals"; + case LEFT_TO_RIGHT_EMBEDDING : + return "Left-to-Right Embedding"; + case LEFT_TO_RIGHT_OVERRIDE : + return "Left-to-Right Override"; + case RIGHT_TO_LEFT_ARABIC : + return "Right-to-Left Arabic"; + case RIGHT_TO_LEFT_EMBEDDING : + return "Right-to-Left Embedding"; + case RIGHT_TO_LEFT_OVERRIDE : + return "Right-to-Left Override"; + case POP_DIRECTIONAL_FORMAT : + return "Pop Directional Format"; + case DIR_NON_SPACING_MARK : + return "Non-Spacing Mark"; + case BOUNDARY_NEUTRAL : + return "Boundary Neutral"; + } + return "Unassigned"; + } +} diff --git a/src/sun/net/idn/UCharacterEnums.java b/src/sun/net/idn/UCharacterEnums.java new file mode 100644 index 00000000..eef2fdda --- /dev/null +++ b/src/sun/net/idn/UCharacterEnums.java @@ -0,0 +1,587 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/* +/** + ******************************************************************************* + * Copyright (C) 2004, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ +// CHANGELOG +// 2005-05-19 Edward Wang +// - copy this file from icu4jsrc_3_2/src/com/ibm/icu/lang/UCharacterEnums.java +// - move from package com.ibm.icu.lang to package sun.net.idn +// +// 2011-09-06 Kurchi Subhra Hazra +// - Added @Deprecated tag to the following: +// - class UCharacterEnums +// - interfaces ECharacterCategory, ECharacterDirection +// - fields INITIAL_QUOTE_PUNCTUATION, FINAL_QUOTE_PUNCTUATION, +// DIRECTIONALITY_LEFT_TO_RIGHT, DIRECTIONALITY_RIGHT_TO_LEFT, +// DIRECTIONALITY_EUROPEAN_NUMBER, DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR +// DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, DIRECTIONALITY_ARABIC_NUMBER, +// DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, DIRECTIONALITY_PARAGRAPH_SEPARATOR, +// DIRECTIONALITY_SEGMENT_SEPARATOR, DIRECTIONALITY_WHITESPACE, +// DIRECTIONALITY_OTHER_NEUTRALS, DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, +// DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, +// DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, +// DIRECTIONALITY_POP_DIRECTIONAL_FORMAT, DIRECTIONALITY_NON_SPACING_MARK, +// DIRECTIONALITY_BOUNDARY_NEUTRAL, DIRECTIONALITY_UNDEFINED +// + +package sun.net.idn; + +/** + * A container for the different 'enumerated types' used by UCharacter. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + +@Deprecated +class UCharacterEnums { + + /** This is just a namespace, it is not instantiatable. */ + private UCharacterEnums() {}; + + /** + * 'Enum' for the CharacterCategory constants. These constants are + * compatible in name but not in value with those defined in + * java.lang.Character. + * @see UCharacterCategory + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static interface ECharacterCategory { + /** + * Unassigned character type + * @stable ICU 2.1 + */ + public static final int UNASSIGNED = 0; + + /** + * Character type Cn + * Not Assigned (no characters in [UnicodeData.txt] have this property) + * @stable ICU 2.6 + */ + public static final int GENERAL_OTHER_TYPES = 0; + + /** + * Character type Lu + * @stable ICU 2.1 + */ + public static final int UPPERCASE_LETTER = 1; + + /** + * Character type Ll + * @stable ICU 2.1 + */ + public static final int LOWERCASE_LETTER = 2; + + /** + * Character type Lt + * @stable ICU 2.1 + */ + + public static final int TITLECASE_LETTER = 3; + + /** + * Character type Lm + * @stable ICU 2.1 + */ + public static final int MODIFIER_LETTER = 4; + + /** + * Character type Lo + * @stable ICU 2.1 + */ + public static final int OTHER_LETTER = 5; + + /** + * Character type Mn + * @stable ICU 2.1 + */ + public static final int NON_SPACING_MARK = 6; + + /** + * Character type Me + * @stable ICU 2.1 + */ + public static final int ENCLOSING_MARK = 7; + + /** + * Character type Mc + * @stable ICU 2.1 + */ + public static final int COMBINING_SPACING_MARK = 8; + + /** + * Character type Nd + * @stable ICU 2.1 + */ + public static final int DECIMAL_DIGIT_NUMBER = 9; + + /** + * Character type Nl + * @stable ICU 2.1 + */ + public static final int LETTER_NUMBER = 10; + + /** + * Character type No + * @stable ICU 2.1 + */ + public static final int OTHER_NUMBER = 11; + + /** + * Character type Zs + * @stable ICU 2.1 + */ + public static final int SPACE_SEPARATOR = 12; + + /** + * Character type Zl + * @stable ICU 2.1 + */ + public static final int LINE_SEPARATOR = 13; + + /** + * Character type Zp + * @stable ICU 2.1 + */ + public static final int PARAGRAPH_SEPARATOR = 14; + + /** + * Character type Cc + * @stable ICU 2.1 + */ + public static final int CONTROL = 15; + + /** + * Character type Cf + * @stable ICU 2.1 + */ + public static final int FORMAT = 16; + + /** + * Character type Co + * @stable ICU 2.1 + */ + public static final int PRIVATE_USE = 17; + + /** + * Character type Cs + * @stable ICU 2.1 + */ + public static final int SURROGATE = 18; + + /** + * Character type Pd + * @stable ICU 2.1 + */ + public static final int DASH_PUNCTUATION = 19; + + /** + * Character type Ps + * @stable ICU 2.1 + */ + public static final int START_PUNCTUATION = 20; + + /** + * Character type Pe + * @stable ICU 2.1 + */ + public static final int END_PUNCTUATION = 21; + + /** + * Character type Pc + * @stable ICU 2.1 + */ + public static final int CONNECTOR_PUNCTUATION = 22; + + /** + * Character type Po + * @stable ICU 2.1 + */ + public static final int OTHER_PUNCTUATION = 23; + + /** + * Character type Sm + * @stable ICU 2.1 + */ + public static final int MATH_SYMBOL = 24; + + /** + * Character type Sc + * @stable ICU 2.1 + */ + public static final int CURRENCY_SYMBOL = 25; + + /** + * Character type Sk + * @stable ICU 2.1 + */ + public static final int MODIFIER_SYMBOL = 26; + + /** + * Character type So + * @stable ICU 2.1 + */ + public static final int OTHER_SYMBOL = 27; + + /** + * Character type Pi + * @see #INITIAL_QUOTE_PUNCTUATION + * @stable ICU 2.1 + */ + public static final int INITIAL_PUNCTUATION = 28; + + /** + * Character type Pi + * This name is compatible with java.lang.Character's name for this type. + * @see #INITIAL_PUNCTUATION + * @draft ICU 2.8 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final int INITIAL_QUOTE_PUNCTUATION = 28; + + /** + * Character type Pf + * @see #FINAL_QUOTE_PUNCTUATION + * @stable ICU 2.1 + */ + public static final int FINAL_PUNCTUATION = 29; + + /** + * Character type Pf + * This name is compatible with java.lang.Character's name for this type. + * @see #FINAL_PUNCTUATION + * @draft ICU 2.8 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final int FINAL_QUOTE_PUNCTUATION = 29; + + /** + * Character type count + * @stable ICU 2.1 + */ + public static final int CHAR_CATEGORY_COUNT = 30; + } + + /** + * 'Enum' for the CharacterDirection constants. There are two sets + * of names, those used in ICU, and those used in the JDK. The + * JDK constants are compatible in name but not in value + * with those defined in java.lang.Character. + * @see UCharacterDirection + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + + @Deprecated + public static interface ECharacterDirection { + /** + * Directional type L + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT = 0; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = (byte)LEFT_TO_RIGHT; + + /** + * Directional type R + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT = 1; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = (byte)RIGHT_TO_LEFT; + + /** + * Directional type EN + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER = 2; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = (byte)EUROPEAN_NUMBER; + + /** + * Directional type ES + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER_SEPARATOR = 3; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER_SEPARATOR. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = (byte)EUROPEAN_NUMBER_SEPARATOR; + + /** + * Directional type ET + * @stable ICU 2.1 + */ + public static final int EUROPEAN_NUMBER_TERMINATOR = 4; + + /** + * JDK-compatible synonum for EUROPEAN_NUMBER_TERMINATOR. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = (byte)EUROPEAN_NUMBER_TERMINATOR; + + /** + * Directional type AN + * @stable ICU 2.1 + */ + public static final int ARABIC_NUMBER = 5; + + /** + * JDK-compatible synonum for ARABIC_NUMBER. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_ARABIC_NUMBER = (byte)ARABIC_NUMBER; + + /** + * Directional type CS + * @stable ICU 2.1 + */ + public static final int COMMON_NUMBER_SEPARATOR = 6; + + /** + * JDK-compatible synonum for COMMON_NUMBER_SEPARATOR. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = (byte)COMMON_NUMBER_SEPARATOR; + + /** + * Directional type B + * @stable ICU 2.1 + */ + public static final int BLOCK_SEPARATOR = 7; + + /** + * JDK-compatible synonum for BLOCK_SEPARATOR. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = (byte)BLOCK_SEPARATOR; + + /** + * Directional type S + * @stable ICU 2.1 + */ + public static final int SEGMENT_SEPARATOR = 8; + + /** + * JDK-compatible synonum for SEGMENT_SEPARATOR. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = (byte)SEGMENT_SEPARATOR; + + /** + * Directional type WS + * @stable ICU 2.1 + */ + public static final int WHITE_SPACE_NEUTRAL = 9; + + /** + * JDK-compatible synonum for WHITE_SPACE_NEUTRAL. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_WHITESPACE = (byte)WHITE_SPACE_NEUTRAL; + + /** + * Directional type ON + * @stable ICU 2.1 + */ + public static final int OTHER_NEUTRAL = 10; + + /** + * JDK-compatible synonum for OTHER_NEUTRAL. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_OTHER_NEUTRALS = (byte)OTHER_NEUTRAL; + + /** + * Directional type LRE + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT_EMBEDDING = 11; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT_EMBEDDING. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = (byte)LEFT_TO_RIGHT_EMBEDDING; + + /** + * Directional type LRO + * @stable ICU 2.1 + */ + public static final int LEFT_TO_RIGHT_OVERRIDE = 12; + + /** + * JDK-compatible synonum for LEFT_TO_RIGHT_OVERRIDE. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = (byte)LEFT_TO_RIGHT_OVERRIDE; + + /** + * Directional type AL + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_ARABIC = 13; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_ARABIC. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = (byte)RIGHT_TO_LEFT_ARABIC; + + /** + * Directional type RLE + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_EMBEDDING = 14; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_EMBEDDING. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = (byte)RIGHT_TO_LEFT_EMBEDDING; + + /** + * Directional type RLO + * @stable ICU 2.1 + */ + public static final int RIGHT_TO_LEFT_OVERRIDE = 15; + + /** + * JDK-compatible synonum for RIGHT_TO_LEFT_OVERRIDE. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = (byte)RIGHT_TO_LEFT_OVERRIDE; + + /** + * Directional type PDF + * @stable ICU 2.1 + */ + public static final int POP_DIRECTIONAL_FORMAT = 16; + + /** + * JDK-compatible synonum for POP_DIRECTIONAL_FORMAT. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = (byte)POP_DIRECTIONAL_FORMAT; + + /** + * Directional type NSM + * @stable ICU 2.1 + */ + public static final int DIR_NON_SPACING_MARK = 17; + + /** + * JDK-compatible synonum for DIR_NON_SPACING_MARK. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_NON_SPACING_MARK = (byte)DIR_NON_SPACING_MARK; + + /** + * Directional type BN + * @stable ICU 2.1 + */ + public static final int BOUNDARY_NEUTRAL = 18; + + /** + * JDK-compatible synonum for BOUNDARY_NEUTRAL. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = (byte)BOUNDARY_NEUTRAL; + + /** + * Number of directional types + * @stable ICU 2.1 + */ + public static final int CHAR_DIRECTION_COUNT = 19; + + /** + * Undefined bidirectional character type. Undefined char + * values have undefined directionality in the Unicode specification. + * @draft ICU 3.0 + * @deprecated This is a draft API and might change in a future release of ICU. + */ + @Deprecated + public static final byte DIRECTIONALITY_UNDEFINED = -1; + } +} diff --git a/src/sun/net/idn/uidna.spp b/src/sun/net/idn/uidna.spp new file mode 100644 index 00000000..5d886969 Binary files /dev/null and b/src/sun/net/idn/uidna.spp differ diff --git a/src/sun/net/sdp/SdpSupport.java b/src/sun/net/sdp/SdpSupport.java new file mode 100644 index 00000000..72146837 --- /dev/null +++ b/src/sun/net/sdp/SdpSupport.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.sdp; + +import java.io.IOException; +import java.io.FileDescriptor; +import java.security.AccessController; + +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + + +/** + * This class defines methods for creating SDP sockets or "converting" existing + * file descriptors, referencing (unbound) TCP sockets, to SDP. + */ + +public final class SdpSupport { + private static final String os = AccessController + .doPrivileged(new sun.security.action.GetPropertyAction("os.name")); + private static final boolean isSupported = (os.equals("SunOS") || (os.equals("Linux"))); + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + private SdpSupport() { } + + /** + * Creates a SDP socket, returning file descriptor referencing the socket. + */ + public static FileDescriptor createSocket() throws IOException { + if (!isSupported) + throw new UnsupportedOperationException("SDP not supported on this platform"); + int fdVal = create0(); + FileDescriptor fd = new FileDescriptor(); + fdAccess.set(fd, fdVal); + return fd; + } + + /** + * Converts an existing file descriptor, that references an unbound TCP socket, + * to SDP. + */ + public static void convertSocket(FileDescriptor fd) throws IOException { + if (!isSupported) + throw new UnsupportedOperationException("SDP not supported on this platform"); + int fdVal = fdAccess.get(fd); + convert0(fdVal); + } + + private static native int create0() throws IOException; + + private static native void convert0(int fd) throws IOException; + + static { + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + } +} diff --git a/src/sun/net/smtp/SmtpClient.java b/src/sun/net/smtp/SmtpClient.java new file mode 100644 index 00000000..8f6e0973 --- /dev/null +++ b/src/sun/net/smtp/SmtpClient.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.smtp; + +import java.util.StringTokenizer; +import java.io.*; +import java.net.*; +import sun.net.TransferProtocolClient; + +/** + * This class implements the SMTP client. + * You can send a piece of mail by creating a new SmtpClient, calling + * the "to" method to add destinations, calling "from" to name the + * sender, calling startMessage to return a stream to which you write + * the message (with RFC733 headers) and then you finally close the Smtp + * Client. + * + * @author James Gosling + */ + +public class SmtpClient extends TransferProtocolClient { + + String mailhost; + SmtpPrintStream message; + + /** + * issue the QUIT command to the SMTP server and close the connection. + */ + public void closeServer() throws IOException { + if (serverIsOpen()) { + closeMessage(); + issueCommand("QUIT\r\n", 221); + super.closeServer(); + } + } + + void issueCommand(String cmd, int expect) throws IOException { + sendServer(cmd); + int reply; + while ((reply = readServerResponse()) != expect) + if (reply != 220) { + throw new SmtpProtocolException(getResponseString()); + } + } + + private void toCanonical(String s) throws IOException { + if (s.startsWith("<")) + issueCommand("rcpt to: " + s + "\r\n", 250); + else + issueCommand("rcpt to: <" + s + ">\r\n", 250); + } + + public void to(String s) throws IOException { + int st = 0; + int limit = s.length(); + int pos = 0; + int lastnonsp = 0; + int parendepth = 0; + boolean ignore = false; + while (pos < limit) { + int c = s.charAt(pos); + if (parendepth > 0) { + if (c == '(') + parendepth++; + else if (c == ')') + parendepth--; + if (parendepth == 0) + if (lastnonsp > st) + ignore = true; + else + st = pos + 1; + } else if (c == '(') + parendepth++; + else if (c == '<') + st = lastnonsp = pos + 1; + else if (c == '>') + ignore = true; + else if (c == ',') { + if (lastnonsp > st) + toCanonical(s.substring(st, lastnonsp)); + st = pos + 1; + ignore = false; + } else { + if (c > ' ' && !ignore) + lastnonsp = pos + 1; + else if (st == pos) + st++; + } + pos++; + } + if (lastnonsp > st) + toCanonical(s.substring(st, lastnonsp)); + } + + public void from(String s) throws IOException { + if (s.startsWith("<")) + issueCommand("mail from: " + s + "\r\n", 250); + else + issueCommand("mail from: <" + s + ">\r\n", 250); + } + + /** open a SMTP connection to host host. */ + private void openServer(String host) throws IOException { + mailhost = host; + openServer(mailhost, 25); + issueCommand("helo "+InetAddress.getLocalHost().getHostName()+"\r\n", 250); + } + + public PrintStream startMessage() throws IOException { + issueCommand("data\r\n", 354); + try { + message = new SmtpPrintStream(serverOutput, this); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding+" encoding not found", e); + } + return message; + } + + void closeMessage() throws IOException { + if (message != null) + message.close(); + } + + /** New SMTP client connected to host host. */ + public SmtpClient (String host) throws IOException { + super(); + if (host != null) { + try { + openServer(host); + mailhost = host; + return; + } catch(Exception e) { + } + } + try { + String s; + mailhost = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("mail.host")); + if (mailhost != null) { + openServer(mailhost); + return; + } + } catch(Exception e) { + } + try { + mailhost = "localhost"; + openServer(mailhost); + } catch(Exception e) { + mailhost = "mailhost"; + openServer(mailhost); + } + } + + /** Create an uninitialized SMTP client. */ + public SmtpClient () throws IOException { + this(null); + } + + public SmtpClient(int to) throws IOException { + super(); + setConnectTimeout(to); + try { + String s; + mailhost = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("mail.host")); + if (mailhost != null) { + openServer(mailhost); + return; + } + } catch(Exception e) { + } + try { + mailhost = "localhost"; + openServer(mailhost); + } catch(Exception e) { + mailhost = "mailhost"; + openServer(mailhost); + } + } + + public String getMailHost() { + return mailhost; + } + + String getEncoding () { + return encoding; + } +} + +class SmtpPrintStream extends PrintStream { + private SmtpClient target; + private int lastc = '\n'; + + SmtpPrintStream (OutputStream fos, SmtpClient cl) throws UnsupportedEncodingException { + super(fos, false, cl.getEncoding()); + target = cl; + } + + public void close() { + if (target == null) + return; + if (lastc != '\n') { + write('\n'); + } + try { + target.issueCommand(".\r\n", 250); + target.message = null; + out = null; + target = null; + } catch (IOException e) { + } + } + + public void write(int b) { + try { + // quote a dot at the beginning of a line + if (lastc == '\n' && b == '.') { + out.write('.'); + } + + // translate NL to CRLF + if (b == '\n' && lastc != '\r') { + out.write('\r'); + } + out.write(b); + lastc = b; + } catch (IOException e) { + } + } + + public void write(byte b[], int off, int len) { + try { + int lc = lastc; + while (--len >= 0) { + int c = b[off++]; + + // quote a dot at the beginning of a line + if (lc == '\n' && c == '.') + out.write('.'); + + // translate NL to CRLF + if (c == '\n' && lc != '\r') { + out.write('\r'); + } + out.write(c); + lc = c; + } + lastc = lc; + } catch (IOException e) { + } + } + public void print(String s) { + int len = s.length(); + for (int i = 0; i < len; i++) { + write(s.charAt(i)); + } + } +} diff --git a/src/sun/net/smtp/SmtpProtocolException.java b/src/sun/net/smtp/SmtpProtocolException.java new file mode 100644 index 00000000..ea205a2b --- /dev/null +++ b/src/sun/net/smtp/SmtpProtocolException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.smtp; + +import java.io.IOException; + +/** + * This exception is thrown when unexpected results are returned during + * an SMTP session. + */ +public class SmtpProtocolException extends IOException { + private static final long serialVersionUID = -7547136771133814908L; + + SmtpProtocolException(String s) { + super(s); + } +} diff --git a/src/sun/net/spi/DefaultProxySelector.java b/src/sun/net/spi/DefaultProxySelector.java new file mode 100644 index 00000000..373ed0e9 --- /dev/null +++ b/src/sun/net/spi/DefaultProxySelector.java @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.spi; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; +import java.io.IOException; +import sun.misc.RegexpPool; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.net.NetProperties; +import sun.net.SocksProxy; + +/** + * Supports proxy settings using system properties This proxy selector + * provides backward compatibility with the old http protocol handler + * as far as how proxy is set + * + * Most of the implementation copied from the old http protocol handler + * + * Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort, + * proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks. + * NOTE: need to do gopher as well + */ +public class DefaultProxySelector extends ProxySelector { + + /** + * This is where we define all the valid System Properties we have to + * support for each given protocol. + * The format of this 2 dimensional array is : + * - 1 row per protocol (http, ftp, ...) + * - 1st element of each row is the protocol name + * - subsequent elements are prefixes for Host & Port properties + * listed in order of priority. + * Example: + * {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, + * means for FTP we try in that oder: + * + ftp.proxyHost & ftp.proxyPort + * + ftpProxyHost & ftpProxyPort + * + proxyHost & proxyPort + * + socksProxyHost & socksProxyPort + * + * Note that the socksProxy should *always* be the last on the list + */ + final static String[][] props = { + /* + * protocol, Property prefix 1, Property prefix 2, ... + */ + {"http", "http.proxy", "proxy", "socksProxy"}, + {"https", "https.proxy", "proxy", "socksProxy"}, + {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, + {"gopher", "gopherProxy", "socksProxy"}, + {"socket", "socksProxy"} + }; + + private static final String SOCKS_PROXY_VERSION = "socksProxyVersion"; + + private static boolean hasSystemProxies = false; + + static { + final String key = "java.net.useSystemProxies"; + Boolean b = AccessController.doPrivileged( + new PrivilegedAction() { + public Boolean run() { + return NetProperties.getBoolean(key); + }}); + if (b != null && b.booleanValue()) { + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + System.loadLibrary("net"); + return null; + } + }); + hasSystemProxies = init(); + } + } + + /** + * How to deal with "non proxy hosts": + * since we do have to generate a RegexpPool we don't want to do that if + * it's not necessary. Therefore we do cache the result, on a per-protocol + * basis, and change it only when the "source", i.e. the system property, + * did change. + */ + + static class NonProxyInfo { + // Default value for nonProxyHosts, this provides backward compatibility + // by excluding localhost and its litteral notations. + static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]"; + + String hostsSource; + RegexpPool hostsPool; + final String property; + final String defaultVal; + static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal); + static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal); + static NonProxyInfo socksNonProxyInfo = new NonProxyInfo("socksNonProxyHosts", null, null, defStringVal); + + NonProxyInfo(String p, String s, RegexpPool pool, String d) { + property = p; + hostsSource = s; + hostsPool = pool; + defaultVal = d; + } + } + + + /** + * select() method. Where all the hard work is done. + * Build a list of proxies depending on URI. + * Since we're only providing compatibility with the system properties + * from previous releases (see list above), that list will always + * contain 1 single proxy, default being NO_PROXY. + */ + public List select(URI uri) { + if (uri == null) { + throw new IllegalArgumentException("URI can't be null."); + } + String protocol = uri.getScheme(); + String host = uri.getHost(); + + if (host == null) { + // This is a hack to ensure backward compatibility in two + // cases: 1. hostnames contain non-ascii characters, + // internationalized domain names. in which case, URI will + // return null, see BugID 4957669; 2. Some hostnames can + // contain '_' chars even though it's not supposed to be + // legal, in which case URI will return null for getHost, + // but not for getAuthority() See BugID 4913253 + String auth = uri.getAuthority(); + if (auth != null) { + int i; + i = auth.indexOf('@'); + if (i >= 0) { + auth = auth.substring(i+1); + } + i = auth.lastIndexOf(':'); + if (i >= 0) { + auth = auth.substring(0,i); + } + host = auth; + } + } + + if (protocol == null || host == null) { + throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); + } + List proxyl = new ArrayList(1); + + NonProxyInfo pinfo = null; + + if ("http".equalsIgnoreCase(protocol)) { + pinfo = NonProxyInfo.httpNonProxyInfo; + } else if ("https".equalsIgnoreCase(protocol)) { + // HTTPS uses the same property as HTTP, for backward + // compatibility + pinfo = NonProxyInfo.httpNonProxyInfo; + } else if ("ftp".equalsIgnoreCase(protocol)) { + pinfo = NonProxyInfo.ftpNonProxyInfo; + } else if ("socket".equalsIgnoreCase(protocol)) { + pinfo = NonProxyInfo.socksNonProxyInfo; + } + + /** + * Let's check the System properties for that protocol + */ + final String proto = protocol; + final NonProxyInfo nprop = pinfo; + final String urlhost = host.toLowerCase(); + + /** + * This is one big doPrivileged call, but we're trying to optimize + * the code as much as possible. Since we're checking quite a few + * System properties it does help having only 1 call to doPrivileged. + * Be mindful what you do in here though! + */ + Proxy p = AccessController.doPrivileged( + new PrivilegedAction() { + public Proxy run() { + int i, j; + String phost = null; + int pport = 0; + String nphosts = null; + InetSocketAddress saddr = null; + + // Then let's walk the list of protocols in our array + for (i=0; i domainList = null; + + // JNDI-DNS URL for name servers specified via property + private String nameProviderUrl = null; + + // Per-thread soft cache of the last temporary context + private static ThreadLocal> contextRef = + new ThreadLocal<>(); + + // Simple class to encapsulate the temporary context + private static class ThreadContext { + private DirContext dirCtxt; + private List nsList; + + public ThreadContext(DirContext dirCtxt, List nsList) { + this.dirCtxt = dirCtxt; + this.nsList = nsList; + } + + public DirContext dirContext() { + return dirCtxt; + } + + public List nameservers() { + return nsList; + } + } + + // Returns a per-thread DirContext + private DirContext getTemporaryContext() throws NamingException { + SoftReference ref = contextRef.get(); + ThreadContext thrCtxt = null; + List nsList = null; + + // if no property specified we need to obtain the list of servers + // + if (nameProviderUrl == null) + nsList = ResolverConfiguration.open().nameservers(); + + // if soft reference hasn't been gc'ed no property has been + // specified then we need to check if the DNS configuration + // has changed. + // + if ((ref != null) && ((thrCtxt = ref.get()) != null)) { + if (nameProviderUrl == null) { + if (!thrCtxt.nameservers().equals(nsList)) { + // DNS configuration has changed + thrCtxt = null; + } + } + } + + // new thread context needs to be created + if (thrCtxt == null) { + final Hashtable env = new Hashtable<>(); + env.put("java.naming.factory.initial", + "com.sun.jndi.dns.DnsContextFactory"); + + // If no nameservers property specified we create provider URL + // based on system configured name servers + // + String provUrl = nameProviderUrl; + if (provUrl == null) { + provUrl = createProviderURL(nsList); + if (provUrl.length() == 0) { + throw new RuntimeException("bad nameserver configuration"); + } + } + env.put("java.naming.provider.url", provUrl); + + // Need to create directory context in privileged block + // as JNDI-DNS needs to resolve the name servers. + // + DirContext dirCtxt; + try { + dirCtxt = AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + public DirContext run() throws NamingException { + // Create the DNS context using NamingManager rather than using + // the initial context constructor. This avoids having the initial + // context constructor call itself. + Context ctx = NamingManager.getInitialContext(env); + if (!(ctx instanceof DirContext)) { + return null; // cannot create a DNS context + } + return (DirContext)ctx; + } + }); + } catch (java.security.PrivilegedActionException pae) { + throw (NamingException)pae.getException(); + } + + // create new soft reference to our thread context + // + thrCtxt = new ThreadContext(dirCtxt, nsList); + contextRef.set(new SoftReference(thrCtxt)); + } + + return thrCtxt.dirContext(); + } + + /** + * Resolves the specified entry in DNS. + * + * Canonical name records are recursively resolved (to a maximum + * of 5 to avoid performance hit and potential CNAME loops). + * + * @param ctx JNDI directory context + * @param name name to resolve + * @param ids record types to search + * @param depth call depth - pass as 0. + * + * @return array list with results (will have at least on entry) + * + * @throws UnknownHostException if lookup fails or other error. + */ + private ArrayList resolve(final DirContext ctx, final String name, + final String[] ids, int depth) + throws UnknownHostException + { + ArrayList results = new ArrayList<>(); + Attributes attrs; + + // do the query + try { + attrs = AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + public Attributes run() throws NamingException { + return ctx.getAttributes(name, ids); + } + }); + } catch (java.security.PrivilegedActionException pae) { + throw new UnknownHostException(pae.getException().getMessage()); + } + + // non-requested type returned so enumeration is empty + NamingEnumeration ne = attrs.getAll(); + if (!ne.hasMoreElements()) { + throw new UnknownHostException("DNS record not found"); + } + + // iterate through the returned attributes + UnknownHostException uhe = null; + try { + while (ne.hasMoreElements()) { + Attribute attr = ne.next(); + String attrID = attr.getID(); + + for (NamingEnumeration e = attr.getAll(); e.hasMoreElements();) { + String addr = (String)e.next(); + + // for canoncical name records do recursive lookup + // - also check for CNAME loops to avoid stack overflow + + if (attrID.equals("CNAME")) { + if (depth > 4) { + throw new UnknownHostException(name + ": possible CNAME loop"); + } + try { + results.addAll(resolve(ctx, addr, ids, depth+1)); + } catch (UnknownHostException x) { + // canonical name can't be resolved. + if (uhe == null) + uhe = x; + } + } else { + results.add(addr); + } + } + } + } catch (NamingException nx) { + throw new UnknownHostException(nx.getMessage()); + } + + // pending exception as canonical name could not be resolved. + if (results.isEmpty() && uhe != null) { + throw uhe; + } + + return results; + } + + public DNSNameService() throws Exception { + + // default domain + String domain = AccessController.doPrivileged( + new GetPropertyAction("sun.net.spi.nameservice.domain")); + if (domain != null && domain.length() > 0) { + domainList = new LinkedList(); + domainList.add(domain); + } + + // name servers + String nameservers = AccessController.doPrivileged( + new GetPropertyAction("sun.net.spi.nameservice.nameservers")); + if (nameservers != null && nameservers.length() > 0) { + nameProviderUrl = createProviderURL(nameservers); + if (nameProviderUrl.length() == 0) { + throw new RuntimeException("malformed nameservers property"); + } + + } else { + + // no property specified so check host DNS resolver configured + // with at least one nameserver in dotted notation. + // + List nsList = ResolverConfiguration.open().nameservers(); + if (nsList.isEmpty()) { + throw new RuntimeException("no nameservers provided"); + } + boolean found = false; + for (String addr: nsList) { + if (IPAddressUtil.isIPv4LiteralAddress(addr) || + IPAddressUtil.isIPv6LiteralAddress(addr)) { + found = true; + break; + } + } + if (!found) { + throw new RuntimeException("bad nameserver configuration"); + } + } + } + + public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException { + + // DNS records that we search for + String[] ids = {"A", "AAAA", "CNAME"}; + + // first get directory context + DirContext ctx; + try { + ctx = getTemporaryContext(); + } catch (NamingException nx) { + throw new Error(nx); + } + + ArrayList results = null; + UnknownHostException uhe = null; + + // If host already contains a domain name then just look it up + if (host.indexOf('.') >= 0) { + try { + results = resolve(ctx, host, ids, 0); + } catch (UnknownHostException x) { + uhe = x; + } + } + + // Here we try to resolve the host using the domain suffix or + // the domain suffix search list. If the host cannot be resolved + // using the domain suffix then we attempt devolution of + // the suffix - eg: if we are searching for "foo" and our + // domain suffix is "eng.sun.com" we will try to resolve + // "foo.eng.sun.com" and "foo.sun.com". + // It's not normal to attempt devolation with domains on the + // domain suffix search list - however as ResolverConfiguration + // doesn't distinguish domain or search list in the list it + // returns we approximate by doing devolution on the domain + // suffix if the list has one entry. + + if (results == null) { + List searchList = null; + Iterator i; + boolean usingSearchList = false; + + if (domainList != null) { + i = domainList.iterator(); + } else { + searchList = ResolverConfiguration.open().searchlist(); + if (searchList.size() > 1) { + usingSearchList = true; + } + i = searchList.iterator(); + } + + // iterator through each domain suffix + while (i.hasNext()) { + String parentDomain = i.next(); + int start = 0; + while ((start = parentDomain.indexOf(".")) != -1 + && start < parentDomain.length() -1) { + try { + results = resolve(ctx, host+"."+parentDomain, ids, 0); + break; + } catch (UnknownHostException x) { + uhe = x; + if (usingSearchList) { + break; + } + + // devolve + parentDomain = parentDomain.substring(start+1); + } + } + if (results != null) { + break; + } + } + } + + // finally try the host if it doesn't have a domain name + if (results == null && (host.indexOf('.') < 0)) { + results = resolve(ctx, host, ids, 0); + } + + // if not found then throw the (last) exception thrown. + if (results == null) { + assert uhe != null; + throw uhe; + } + + /** + * Convert the array list into a byte aray list - this + * filters out any invalid IPv4/IPv6 addresses. + */ + assert results.size() > 0; + InetAddress[] addrs = new InetAddress[results.size()]; + int count = 0; + for (int i=0; i results = null; + try { + ctx = getTemporaryContext(); + } catch (NamingException nx) { + throw new Error(nx); + } + if (addr.length == 4) { // IPv4 Address + for (int i = addr.length-1; i >= 0; i--) { + literalip += (addr[i] & 0xff) +"."; + } + literalip += "IN-ADDR.ARPA."; + + results = resolve(ctx, literalip, ids, 0); + host = results.get(0); + } else if (addr.length == 16) { // IPv6 Address + /** + * Because RFC 3152 changed the root domain name for reverse + * lookups from IP6.INT. to IP6.ARPA., we need to check + * both. I.E. first the new one, IP6.ARPA, then if it fails + * the older one, IP6.INT + */ + + for (int i = addr.length-1; i >= 0; i--) { + literalip += Integer.toHexString((addr[i] & 0x0f)) +"." + +Integer.toHexString((addr[i] & 0xf0) >> 4) +"."; + } + String ip6lit = literalip + "IP6.ARPA."; + + try { + results = resolve(ctx, ip6lit, ids, 0); + host = results.get(0); + } catch (UnknownHostException e) { + host = null; + } + if (host == null) { + // IP6.ARPA lookup failed, let's try the older IP6.INT + ip6lit = literalip + "IP6.INT."; + results = resolve(ctx, ip6lit, ids, 0); + host = results.get(0); + } + } + } catch (Exception e) { + throw new UnknownHostException(e.getMessage()); + } + // Either we couldn't find it or the address was neither IPv4 or IPv6 + if (host == null) + throw new UnknownHostException(); + // remove trailing dot + if (host.endsWith(".")) { + host = host.substring(0, host.length() - 1); + } + return host; + } + + + // --------- + + private static void appendIfLiteralAddress(String addr, StringBuffer sb) { + if (IPAddressUtil.isIPv4LiteralAddress(addr)) { + sb.append("dns://" + addr + " "); + } else { + if (IPAddressUtil.isIPv6LiteralAddress(addr)) { + sb.append("dns://[" + addr + "] "); + } + } + } + + /* + * @return String containing the JNDI-DNS provider URL + * corresponding to the supplied List of nameservers. + */ + private static String createProviderURL(List nsList) { + StringBuffer sb = new StringBuffer(); + for (String s: nsList) { + appendIfLiteralAddress(s, sb); + } + return sb.toString(); + } + + /* + * @return String containing the JNDI-DNS provider URL + * corresponding to the list of nameservers + * contained in the provided str. + */ + private static String createProviderURL(String str) { + StringBuffer sb = new StringBuffer(); + StringTokenizer st = new StringTokenizer(str, ","); + while (st.hasMoreTokens()) { + appendIfLiteralAddress(st.nextToken(), sb); + } + return sb.toString(); + } +} diff --git a/src/sun/net/spi/nameservice/dns/DNSNameServiceDescriptor.java b/src/sun/net/spi/nameservice/dns/DNSNameServiceDescriptor.java new file mode 100644 index 00000000..b18e4b1b --- /dev/null +++ b/src/sun/net/spi/nameservice/dns/DNSNameServiceDescriptor.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.spi.nameservice.dns; + +import sun.net.spi.nameservice.*; + +public final class DNSNameServiceDescriptor implements NameServiceDescriptor { + /** + * Create a new instance of the corresponding name service. + */ + public NameService createNameService() throws Exception { + return new DNSNameService(); + } + + /** + * Returns this service provider's name + * + */ + public String getProviderName() { + return "sun"; + } + + /** + * Returns this name service type + * "dns" "nis" etc + */ + public String getType() { + return "dns"; + } +} diff --git a/src/sun/net/spi/nameservice/dns/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor b/src/sun/net/spi/nameservice/dns/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor new file mode 100644 index 00000000..52ff4b1f --- /dev/null +++ b/src/sun/net/spi/nameservice/dns/META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor @@ -0,0 +1,2 @@ +# dns service provider descriptor +sun.net.spi.nameservice.dns.DNSNameServiceDescriptor diff --git a/src/sun/net/util/IPAddressUtil.java b/src/sun/net/util/IPAddressUtil.java new file mode 100644 index 00000000..4dafa8a1 --- /dev/null +++ b/src/sun/net/util/IPAddressUtil.java @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.util; + +public class IPAddressUtil { + private final static int INADDR4SZ = 4; + private final static int INADDR16SZ = 16; + private final static int INT16SZ = 2; + + /* + * Converts IPv4 address in its textual presentation form + * into its numeric binary form. + * + * @param src a String representing an IPv4 address in standard format + * @return a byte array representing the IPv4 numeric address + */ + @SuppressWarnings("fallthrough") + public static byte[] textToNumericFormatV4(String src) + { + byte[] res = new byte[INADDR4SZ]; + + long tmpValue = 0; + int currByte = 0; + + int len = src.length(); + if (len == 0 || len > 15) { + return null; + } + /* + * When only one part is given, the value is stored directly in + * the network address without any byte rearrangement. + * + * When a two part address is supplied, the last part is + * interpreted as a 24-bit quantity and placed in the right + * most three bytes of the network address. This makes the + * two part address format convenient for specifying Class A + * network addresses as net.host. + * + * When a three part address is specified, the last part is + * interpreted as a 16-bit quantity and placed in the right + * most two bytes of the network address. This makes the + * three part address format convenient for specifying + * Class B net- work addresses as 128.net.host. + * + * When four parts are specified, each is interpreted as a + * byte of data and assigned, from left to right, to the + * four bytes of an IPv4 address. + * + * We determine and parse the leading parts, if any, as single + * byte values in one pass directly into the resulting byte[], + * then the remainder is treated as a 8-to-32-bit entity and + * translated into the remaining bytes in the array. + */ + for (int i = 0; i < len; i++) { + char c = src.charAt(i); + if (c == '.') { + if (tmpValue < 0 || tmpValue > 0xff || currByte == 3) { + return null; + } + res[currByte++] = (byte) (tmpValue & 0xff); + tmpValue = 0; + } else { + int digit = Character.digit(c, 10); + if (digit < 0) { + return null; + } + tmpValue *= 10; + tmpValue += digit; + } + } + if (tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) { + return null; + } + switch (currByte) { + case 0: + res[0] = (byte) ((tmpValue >> 24) & 0xff); + case 1: + res[1] = (byte) ((tmpValue >> 16) & 0xff); + case 2: + res[2] = (byte) ((tmpValue >> 8) & 0xff); + case 3: + res[3] = (byte) ((tmpValue >> 0) & 0xff); + } + return res; + } + + /* + * Convert IPv6 presentation level address to network order binary form. + * credit: + * Converted from C code from Solaris 8 (inet_pton) + * + * Any component of the string following a per-cent % is ignored. + * + * @param src a String representing an IPv6 address in textual format + * @return a byte array representing the IPv6 numeric address + */ + public static byte[] textToNumericFormatV6(String src) + { + // Shortest valid string is "::", hence at least 2 chars + if (src.length() < 2) { + return null; + } + + int colonp; + char ch; + boolean saw_xdigit; + int val; + char[] srcb = src.toCharArray(); + byte[] dst = new byte[INADDR16SZ]; + + int srcb_length = srcb.length; + int pc = src.indexOf ("%"); + if (pc == srcb_length -1) { + return null; + } + + if (pc != -1) { + srcb_length = pc; + } + + colonp = -1; + int i = 0, j = 0; + /* Leading :: requires some special handling. */ + if (srcb[i] == ':') + if (srcb[++i] != ':') + return null; + int curtok = i; + saw_xdigit = false; + val = 0; + while (i < srcb_length) { + ch = srcb[i++]; + int chval = Character.digit(ch, 16); + if (chval != -1) { + val <<= 4; + val |= chval; + if (val > 0xffff) + return null; + saw_xdigit = true; + continue; + } + if (ch == ':') { + curtok = i; + if (!saw_xdigit) { + if (colonp != -1) + return null; + colonp = j; + continue; + } else if (i == srcb_length) { + return null; + } + if (j + INT16SZ > INADDR16SZ) + return null; + dst[j++] = (byte) ((val >> 8) & 0xff); + dst[j++] = (byte) (val & 0xff); + saw_xdigit = false; + val = 0; + continue; + } + if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) { + String ia4 = src.substring(curtok, srcb_length); + /* check this IPv4 address has 3 dots, ie. A.B.C.D */ + int dot_count = 0, index=0; + while ((index = ia4.indexOf ('.', index)) != -1) { + dot_count ++; + index ++; + } + if (dot_count != 3) { + return null; + } + byte[] v4addr = textToNumericFormatV4(ia4); + if (v4addr == null) { + return null; + } + for (int k = 0; k < INADDR4SZ; k++) { + dst[j++] = v4addr[k]; + } + saw_xdigit = false; + break; /* '\0' was seen by inet_pton4(). */ + } + return null; + } + if (saw_xdigit) { + if (j + INT16SZ > INADDR16SZ) + return null; + dst[j++] = (byte) ((val >> 8) & 0xff); + dst[j++] = (byte) (val & 0xff); + } + + if (colonp != -1) { + int n = j - colonp; + + if (j == INADDR16SZ) + return null; + for (i = 1; i <= n; i++) { + dst[INADDR16SZ - i] = dst[colonp + n - i]; + dst[colonp + n - i] = 0; + } + j = INADDR16SZ; + } + if (j != INADDR16SZ) + return null; + byte[] newdst = convertFromIPv4MappedAddress(dst); + if (newdst != null) { + return newdst; + } else { + return dst; + } + } + + /** + * @param src a String representing an IPv4 address in textual format + * @return a boolean indicating whether src is an IPv4 literal address + */ + public static boolean isIPv4LiteralAddress(String src) { + return textToNumericFormatV4(src) != null; + } + + /** + * @param src a String representing an IPv6 address in textual format + * @return a boolean indicating whether src is an IPv6 literal address + */ + public static boolean isIPv6LiteralAddress(String src) { + return textToNumericFormatV6(src) != null; + } + + /* + * Convert IPv4-Mapped address to IPv4 address. Both input and + * returned value are in network order binary form. + * + * @param src a String representing an IPv4-Mapped address in textual format + * @return a byte array representing the IPv4 numeric address + */ + public static byte[] convertFromIPv4MappedAddress(byte[] addr) { + if (isIPv4MappedAddress(addr)) { + byte[] newAddr = new byte[INADDR4SZ]; + System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ); + return newAddr; + } + return null; + } + + /** + * Utility routine to check if the InetAddress is an + * IPv4 mapped IPv6 address. + * + * @return a boolean indicating if the InetAddress is + * an IPv4 mapped IPv6 address; or false if address is IPv4 address. + */ + private static boolean isIPv4MappedAddress(byte[] addr) { + if (addr.length < INADDR16SZ) { + return false; + } + if ((addr[0] == 0x00) && (addr[1] == 0x00) && + (addr[2] == 0x00) && (addr[3] == 0x00) && + (addr[4] == 0x00) && (addr[5] == 0x00) && + (addr[6] == 0x00) && (addr[7] == 0x00) && + (addr[8] == 0x00) && (addr[9] == 0x00) && + (addr[10] == (byte)0xff) && + (addr[11] == (byte)0xff)) { + return true; + } + return false; + } +} diff --git a/src/sun/net/util/URLUtil.java b/src/sun/net/util/URLUtil.java new file mode 100644 index 00000000..7840d717 --- /dev/null +++ b/src/sun/net/util/URLUtil.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.util; + +import java.io.IOException; +import java.net.URL; +import java.net.URLPermission; +import java.security.Permission; + +/** + * URL Utility class. + */ +public class URLUtil { + /** + * Returns a string form of the url suitable for use as a key in HashMap/Sets. + * + * The string form should be behave in the same manner as the URL when + * compared for equality in a HashMap/Set, except that no nameservice + * lookup is done on the hostname (only string comparison), and the fragment + * is not considered. + * + * @see java.net.URLStreamHandler.sameFile( URL) + */ + public static String urlNoFragString(URL url) { + StringBuilder strForm = new StringBuilder(); + + String protocol = url.getProtocol(); + if (protocol != null) { + /* protocol is compared case-insensitive, so convert to lowercase */ + protocol = protocol.toLowerCase(); + strForm.append(protocol); + strForm.append("://"); + } + + String host = url.getHost(); + if (host != null) { + /* host is compared case-insensitive, so convert to lowercase */ + host = host.toLowerCase(); + strForm.append(host); + + int port = url.getPort(); + if (port == -1) { + /* if no port is specificed then use the protocols + * default, if there is one */ + port = url.getDefaultPort(); + } + if (port != -1) { + strForm.append(":").append(port); + } + } + + String file = url.getFile(); + if (file != null) { + strForm.append(file); + } + + return strForm.toString(); + } + + public static Permission getConnectPermission(URL url) throws IOException { + String urlStringLowerCase = url.toString().toLowerCase(); + if (urlStringLowerCase.startsWith("http:") || urlStringLowerCase.startsWith("https:")) { + return getURLConnectPermission(url); + } else if (urlStringLowerCase.startsWith("jar:http:") || urlStringLowerCase.startsWith("jar:https:")) { + String urlString = url.toString(); + int bangPos = urlString.indexOf("!/"); + urlString = urlString.substring(4, bangPos > -1 ? bangPos : urlString.length()); + URL u = new URL(urlString); + return getURLConnectPermission(u); + // If protocol is HTTP or HTTPS than use URLPermission object + } else { + return url.openConnection().getPermission(); + } + } + + private static Permission getURLConnectPermission(URL url) { + String urlString = url.getProtocol() + "://" + url.getAuthority() + url.getPath(); + return new URLPermission(urlString); + } +} + diff --git a/src/sun/net/www/ApplicationLaunchException.java b/src/sun/net/www/ApplicationLaunchException.java new file mode 100644 index 00000000..3394cd3e --- /dev/null +++ b/src/sun/net/www/ApplicationLaunchException.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; + +/** + * An exception thrown by the MimeLauncher when it is unable to launch + * an external content viewer. + * + * @author Sunita Mani + */ + +public class ApplicationLaunchException extends Exception { + private static final long serialVersionUID = -4782286141289536883L; + + public ApplicationLaunchException(String reason) { + super(reason); + } +} diff --git a/src/sun/net/www/HeaderParser.java b/src/sun/net/www/HeaderParser.java new file mode 100644 index 00000000..3f1629fa --- /dev/null +++ b/src/sun/net/www/HeaderParser.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; + +import java.util.Iterator; + +/* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers + * sensibly: + * From a String like: 'timeout=15, max=5' + * create an array of Strings: + * { {"timeout", "15"}, + * {"max", "5"} + * } + * From one like: 'Basic Realm="FuzzFace" Foo="Biz Bar Baz"' + * create one like (no quotes in literal): + * { {"basic", null}, + * {"realm", "FuzzFace"} + * {"foo", "Biz Bar Baz"} + * } + * keys are converted to lower case, vals are left as is.... + * + * @author Dave Brown + */ + + +public class HeaderParser { + + /* table of key/val pairs */ + String raw; + String[][] tab; + int nkeys; + int asize = 10; // initial size of array is 10 + + public HeaderParser(String raw) { + this.raw = raw; + tab = new String[asize][2]; + parse(); + } + + private HeaderParser () { + } + + /** + * create a new HeaderParser from this, whose keys (and corresponding values) + * range from "start" to "end-1" + */ + public HeaderParser subsequence (int start, int end) { + if (start == 0 && end == nkeys) { + return this; + } + if (start < 0 || start >= end || end > nkeys) + throw new IllegalArgumentException ("invalid start or end"); + HeaderParser n = new HeaderParser (); + n.tab = new String [asize][2]; + n.asize = asize; + System.arraycopy (tab, start, n.tab, 0, (end-start)); + n.nkeys= (end-start); + return n; + } + + private void parse() { + + if (raw != null) { + raw = raw.trim(); + char[] ca = raw.toCharArray(); + int beg = 0, end = 0, i = 0; + boolean inKey = true; + boolean inQuote = false; + int len = ca.length; + while (end < len) { + char c = ca[end]; + if ((c == '=') && !inQuote) { // end of a key + tab[i][0] = new String(ca, beg, end-beg).toLowerCase(); + inKey = false; + end++; + beg = end; + } else if (c == '\"') { + if (inQuote) { + tab[i++][1]= new String(ca, beg, end-beg); + inQuote=false; + do { + end++; + } while (end < len && (ca[end] == ' ' || ca[end] == ',')); + inKey=true; + beg=end; + } else { + inQuote=true; + end++; + beg=end; + } + } else if (c == ' ' || c == ',') { // end key/val, of whatever we're in + if (inQuote) { + end++; + continue; + } else if (inKey) { + tab[i++][0] = (new String(ca, beg, end-beg)).toLowerCase(); + } else { + tab[i++][1] = (new String(ca, beg, end-beg)); + } + while (end < len && (ca[end] == ' ' || ca[end] == ',')) { + end++; + } + inKey = true; + beg = end; + } else { + end++; + } + if (i == asize) { + asize = asize * 2; + String[][] ntab = new String[asize][2]; + System.arraycopy (tab, 0, ntab, 0, tab.length); + tab = ntab; + } + } + // get last key/val, if any + if (--end > beg) { + if (!inKey) { + if (ca[end] == '\"') { + tab[i++][1] = (new String(ca, beg, end-beg)); + } else { + tab[i++][1] = (new String(ca, beg, end-beg+1)); + } + } else { + tab[i++][0] = (new String(ca, beg, end-beg+1)).toLowerCase(); + } + } else if (end == beg) { + if (!inKey) { + if (ca[end] == '\"') { + tab[i++][1] = String.valueOf(ca[end-1]); + } else { + tab[i++][1] = String.valueOf(ca[end]); + } + } else { + tab[i++][0] = String.valueOf(ca[end]).toLowerCase(); + } + } + nkeys=i; + } + + } + + public String findKey(int i) { + if (i < 0 || i > asize) + return null; + return tab[i][0]; + } + + public String findValue(int i) { + if (i < 0 || i > asize) + return null; + return tab[i][1]; + } + + public String findValue(String key) { + return findValue(key, null); + } + + public String findValue(String k, String Default) { + if (k == null) + return Default; + k = k.toLowerCase(); + for (int i = 0; i < asize; ++i) { + if (tab[i][0] == null) { + return Default; + } else if (k.equals(tab[i][0])) { + return tab[i][1]; + } + } + return Default; + } + + class ParserIterator implements Iterator { + int index; + boolean returnsValue; // or key + + ParserIterator (boolean returnValue) { + returnsValue = returnValue; + } + public boolean hasNext () { + return index keys () { + return new ParserIterator (false); + } + + public Iterator values () { + return new ParserIterator (true); + } + + public String toString () { + Iterator k = keys(); + StringBuffer sbuf = new StringBuffer(); + sbuf.append ("{size="+asize+" nkeys="+nkeys+" "); + for (int i=0; k.hasNext(); i++) { + String key = k.next(); + String val = findValue (i); + if (val != null && "".equals (val)) { + val = null; + } + sbuf.append (" {"+key+(val==null?"":","+val)+"}"); + if (k.hasNext()) { + sbuf.append (","); + } + } + sbuf.append (" }"); + return new String (sbuf); + } + + public int findInt(String k, int Default) { + try { + return Integer.parseInt(findValue(k, String.valueOf(Default))); + } catch (Throwable t) { + return Default; + } + } + /* + public static void main(String[] a) throws Exception { + System.out.print("enter line to parse> "); + System.out.flush(); + DataInputStream dis = new DataInputStream(System.in); + String line = dis.readLine(); + HeaderParser p = new HeaderParser(line); + for (int i = 0; i < asize; ++i) { + if (p.findKey(i) == null) break; + String v = p.findValue(i); + System.out.println(i + ") " +p.findKey(i) + "="+v); + } + System.out.println("Done!"); + + } + */ +} diff --git a/src/sun/net/www/MessageHeader.java b/src/sun/net/www/MessageHeader.java new file mode 100644 index 00000000..df8b1dbe --- /dev/null +++ b/src/sun/net/www/MessageHeader.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * news stream opener + */ + +package sun.net.www; + +import java.io.*; +import java.util.Collections; +import java.util.*; + +/** An RFC 844 or MIME message header. Includes methods + for parsing headers from incoming streams, fetching + values, setting values, and printing headers. + Key values of null are legal: they indicate lines in + the header that don't have a valid key, but do have + a value (this isn't legal according to the standard, + but lines like this are everywhere). */ +public +class MessageHeader { + private String keys[]; + private String values[]; + private int nkeys; + + public MessageHeader () { + grow(); + } + + public MessageHeader (InputStream is) throws IOException { + parseHeader(is); + } + + /** + * Returns list of header names in a comma separated list + */ + public synchronized String getHeaderNamesInList() { + StringJoiner joiner = new StringJoiner(","); + for (int i=0; i= 0;) + if (keys[i] == null) + return values[i]; + } else + for (int i = nkeys; --i >= 0;) { + if (k.equalsIgnoreCase(keys[i])) + return values[i]; + } + return null; + } + + // return the location of the key + public synchronized int getKey(String k) { + for (int i = nkeys; --i >= 0;) + if ((keys[i] == k) || + (k != null && k.equalsIgnoreCase(keys[i]))) + return i; + return -1; + } + + public synchronized String getKey(int n) { + if (n < 0 || n >= nkeys) return null; + return keys[n]; + } + + public synchronized String getValue(int n) { + if (n < 0 || n >= nkeys) return null; + return values[n]; + } + + /** Deprecated: Use multiValueIterator() instead. + * + * Find the next value that corresponds to this key. + * It finds the first value that follows v. To iterate + * over all the values of a key use: + *
    +     *          for(String v=h.findValue(k); v!=null; v=h.findNextValue(k, v)) {
    +     *              ...
    +     *          }
    +     *  
    + */ + public synchronized String findNextValue(String k, String v) { + boolean foundV = false; + if (k == null) { + for (int i = nkeys; --i >= 0;) + if (keys[i] == null) + if (foundV) + return values[i]; + else if (values[i] == v) + foundV = true; + } else + for (int i = nkeys; --i >= 0;) + if (k.equalsIgnoreCase(keys[i])) + if (foundV) + return values[i]; + else if (values[i] == v) + foundV = true; + return null; + } + + /** + * Removes bare Negotiate and Kerberos headers when an "NTLM ..." + * appears. All Performed on headers with key being k. + * @return true if there is a change + */ + public boolean filterNTLMResponses(String k) { + boolean found = false; + for (int i=0; i 5 + && values[i].substring(0, 5).equalsIgnoreCase("NTLM ")) { + found = true; + break; + } + } + if (found) { + int j = 0; + for (int i=0; i { + int index = 0; + int next = -1; + String key; + boolean haveNext = false; + Object lock; + + public HeaderIterator (String k, Object lock) { + key = k; + this.lock = lock; + } + public boolean hasNext () { + synchronized (lock) { + if (haveNext) { + return true; + } + while (index < nkeys) { + if (key.equalsIgnoreCase (keys[index])) { + haveNext = true; + next = index++; + return true; + } + index ++; + } + return false; + } + } + public String next() { + synchronized (lock) { + if (haveNext) { + haveNext = false; + return values [next]; + } + if (hasNext()) { + return next(); + } else { + throw new NoSuchElementException ("No more elements"); + } + } + } + public void remove () { + throw new UnsupportedOperationException ("remove not allowed"); + } + } + + /** + * return an Iterator that returns all values of a particular + * key in sequence + */ + public Iterator multiValueIterator (String k) { + return new HeaderIterator (k, this); + } + + public synchronized Map> getHeaders() { + return getHeaders(null); + } + + public synchronized Map> getHeaders(String[] excludeList) { + return filterAndAddHeaders(excludeList, null); + } + + public synchronized Map> filterAndAddHeaders( + String[] excludeList, Map> include) { + boolean skipIt = false; + Map> m = new HashMap>(); + for (int i = nkeys; --i >= 0;) { + if (excludeList != null) { + // check if the key is in the excludeList. + // if so, don't include it in the Map. + for (int j = 0; j < excludeList.length; j++) { + if ((excludeList[j] != null) && + (excludeList[j].equalsIgnoreCase(keys[i]))) { + skipIt = true; + break; + } + } + } + if (!skipIt) { + List l = m.get(keys[i]); + if (l == null) { + l = new ArrayList(); + m.put(keys[i], l); + } + l.add(values[i]); + } else { + // reset the flag + skipIt = false; + } + } + + if (include != null) { + for (Map.Entry> entry: include.entrySet()) { + List l = m.get(entry.getKey()); + if (l == null) { + l = new ArrayList(); + m.put(entry.getKey(), l); + } + l.addAll(entry.getValue()); + } + } + + for (String key : m.keySet()) { + m.put(key, Collections.unmodifiableList(m.get(key))); + } + + return Collections.unmodifiableMap(m); + } + + /** Prints the key-value pairs represented by this + header. Also prints the RFC required blank line + at the end. Omits pairs with a null key. */ + public synchronized void print(PrintStream p) { + for (int i = 0; i < nkeys; i++) + if (keys[i] != null) { + p.print(keys[i] + + (values[i] != null ? ": "+values[i]: "") + "\r\n"); + } + p.print("\r\n"); + p.flush(); + } + + /** Adds a key value pair to the end of the + header. Duplicates are allowed */ + public synchronized void add(String k, String v) { + grow(); + keys[nkeys] = k; + values[nkeys] = v; + nkeys++; + } + + /** Prepends a key value pair to the beginning of the + header. Duplicates are allowed */ + public synchronized void prepend(String k, String v) { + grow(); + for (int i = nkeys; i > 0; i--) { + keys[i] = keys[i-1]; + values[i] = values[i-1]; + } + keys[0] = k; + values[0] = v; + nkeys++; + } + + /** Overwrite the previous key/val pair at location 'i' + * with the new k/v. If the index didn't exist before + * the key/val is simply tacked onto the end. + */ + + public synchronized void set(int i, String k, String v) { + grow(); + if (i < 0) { + return; + } else if (i >= nkeys) { + add(k, v); + } else { + keys[i] = k; + values[i] = v; + } + } + + + /** grow the key/value arrays as needed */ + + private void grow() { + if (keys == null || nkeys >= keys.length) { + String[] nk = new String[nkeys + 4]; + String[] nv = new String[nkeys + 4]; + if (keys != null) + System.arraycopy(keys, 0, nk, 0, nkeys); + if (values != null) + System.arraycopy(values, 0, nv, 0, nkeys); + keys = nk; + values = nv; + } + } + + /** + * Remove the key from the header. If there are multiple values under + * the same key, they are all removed. + * Nothing is done if the key doesn't exist. + * After a remove, the other pairs' order are not changed. + * @param k the key to remove + */ + public synchronized void remove(String k) { + if(k == null) { + for (int i = 0; i < nkeys; i++) { + while (keys[i] == null && i < nkeys) { + for(int j=i; j= 0;) + if (k.equalsIgnoreCase(keys[i])) { + values[i] = v; + return; + } + add(k, v); + } + + /** Set's the value of a key only if there is no + * key with that value already. + */ + + public synchronized void setIfNotSet(String k, String v) { + if (findValue(k) == null) { + add(k, v); + } + } + + /** Convert a message-id string to canonical form (strips off + leading and trailing <>s) */ + public static String canonicalID(String id) { + if (id == null) + return ""; + int st = 0; + int len = id.length(); + boolean substr = false; + int c; + while (st < len && ((c = id.charAt(st)) == '<' || + c <= ' ')) { + st++; + substr = true; + } + while (st < len && ((c = id.charAt(len - 1)) == '>' || + c <= ' ')) { + len--; + substr = true; + } + return substr ? id.substring(st, len) : id; + } + + /** Parse a MIME header from an input stream. */ + public void parseHeader(InputStream is) throws IOException { + synchronized (this) { + nkeys = 0; + } + mergeHeader(is); + } + + /** Parse and merge a MIME header from an input stream. */ + @SuppressWarnings("fallthrough") + public void mergeHeader(InputStream is) throws IOException { + if (is == null) + return; + char s[] = new char[10]; + int firstc = is.read(); + while (firstc != '\n' && firstc != '\r' && firstc >= 0) { + int len = 0; + int keyend = -1; + int c; + boolean inKey = firstc > ' '; + s[len++] = (char) firstc; + parseloop:{ + while ((c = is.read()) >= 0) { + switch (c) { + case ':': + if (inKey && len > 0) + keyend = len; + inKey = false; + break; + case '\t': + c = ' '; + /*fall through*/ + case ' ': + inKey = false; + break; + case '\r': + case '\n': + firstc = is.read(); + if (c == '\r' && firstc == '\n') { + firstc = is.read(); + if (firstc == '\r') + firstc = is.read(); + } + if (firstc == '\n' || firstc == '\r' || firstc > ' ') + break parseloop; + /* continuation */ + c = ' '; + break; + } + if (len >= s.length) { + char ns[] = new char[s.length * 2]; + System.arraycopy(s, 0, ns, 0, len); + s = ns; + } + s[len++] = (char) c; + } + firstc = -1; + } + while (len > 0 && s[len - 1] <= ' ') + len--; + String k; + if (keyend <= 0) { + k = null; + keyend = 0; + } else { + k = String.copyValueOf(s, 0, keyend); + if (keyend < len && s[keyend] == ':') + keyend++; + while (keyend < len && s[keyend] <= ' ') + keyend++; + } + String v; + if (keyend >= len) + v = new String(); + else + v = String.copyValueOf(s, keyend, len - keyend); + add(k, v); + } + } + + public synchronized String toString() { + String result = super.toString() + nkeys + " pairs: "; + for (int i = 0; i < keys.length && i < nkeys; i++) { + result += "{"+keys[i]+": "+values[i]+"}"; + } + return result; + } +} diff --git a/src/sun/net/www/MeteredStream.java b/src/sun/net/www/MeteredStream.java new file mode 100644 index 00000000..4ed01841 --- /dev/null +++ b/src/sun/net/www/MeteredStream.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; + +import java.net.URL; +import java.util.*; +import java.io.*; +import sun.net.ProgressSource; +import sun.net.www.http.ChunkedInputStream; + + +public class MeteredStream extends FilterInputStream { + + // Instance variables. + /* if expected != -1, after we've read >= expected, we're "closed" and return -1 + * from subsequest read() 's + */ + protected boolean closed = false; + protected long expected; + protected long count = 0; + protected long markedCount = 0; + protected int markLimit = -1; + protected ProgressSource pi; + + public MeteredStream(InputStream is, ProgressSource pi, long expected) + { + super(is); + + this.pi = pi; + this.expected = expected; + + if (pi != null) { + pi.updateProgress(0, expected); + } + } + + private final void justRead(long n) throws IOException { + if (n == -1) { + + /* + * don't close automatically when mark is set and is valid; + * cannot reset() after close() + */ + if (!isMarked()) { + close(); + } + return; + } + + count += n; + + /** + * If read beyond the markLimit, invalidate the mark + */ + if (count - markedCount > markLimit) { + markLimit = -1; + } + + if (pi != null) + pi.updateProgress(count, expected); + + if (isMarked()) { + return; + } + + // if expected length is known, we could determine if + // read overrun. + if (expected > 0) { + if (count >= expected) { + close(); + } + } + } + + /** + * Returns true if the mark is valid, false otherwise + */ + private boolean isMarked() { + + if (markLimit < 0) { + return false; + } + + // mark is set, but is not valid anymore + if (count - markedCount > markLimit) { + return false; + } + + // mark still holds + return true; + } + + public synchronized int read() throws IOException { + if (closed) { + return -1; + } + int c = in.read(); + if (c != -1) { + justRead(1); + } else { + justRead(c); + } + return c; + } + + public synchronized int read(byte b[], int off, int len) + throws IOException { + if (closed) { + return -1; + } + int n = in.read(b, off, len); + justRead(n); + return n; + } + + public synchronized long skip(long n) throws IOException { + + // REMIND: what does skip do on EOF???? + if (closed) { + return 0; + } + + if (in instanceof ChunkedInputStream) { + n = in.skip(n); + } + else { + // just skip min(n, num_bytes_left) + long min = (n > expected - count) ? expected - count: n; + n = in.skip(min); + } + justRead(n); + return n; + } + + public void close() throws IOException { + if (closed) { + return; + } + if (pi != null) + pi.finishTracking(); + + closed = true; + in.close(); + } + + public synchronized int available() throws IOException { + return closed ? 0: in.available(); + } + + public synchronized void mark(int readLimit) { + if (closed) { + return; + } + super.mark(readLimit); + + /* + * mark the count to restore upon reset + */ + markedCount = count; + markLimit = readLimit; + } + + public synchronized void reset() throws IOException { + if (closed) { + return; + } + + if (!isMarked()) { + throw new IOException ("Resetting to an invalid mark"); + } + + count = markedCount; + super.reset(); + } + + public boolean markSupported() { + if (closed) { + return false; + } + return super.markSupported(); + } + + protected void finalize() throws Throwable { + try { + close(); + if (pi != null) + pi.close(); + } + finally { + // Call super class + super.finalize(); + } + } +} diff --git a/src/sun/net/www/MimeEntry.java b/src/sun/net/www/MimeEntry.java new file mode 100644 index 00000000..005d9606 --- /dev/null +++ b/src/sun/net/www/MimeEntry.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 1994, 2002, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; +import java.net.URL; +import java.io.*; +import java.util.StringTokenizer; + +public class MimeEntry implements Cloneable { + private String typeName; // of the form: "type/subtype" + private String tempFileNameTemplate; + + private int action; + private String command; + private String description; + private String imageFileName; + private String fileExtensions[]; + + boolean starred; + + // Actions + public static final int UNKNOWN = 0; + public static final int LOAD_INTO_BROWSER = 1; + public static final int SAVE_TO_FILE = 2; + public static final int LAUNCH_APPLICATION = 3; + + static final String[] actionKeywords = { + "unknown", + "browser", + "save", + "application", + }; + + /** + * Construct an empty entry of the given type and subtype. + */ + public MimeEntry(String type) { + // Default action is UNKNOWN so clients can decide what the default + // should be, typically save to file or ask user. + this(type, UNKNOWN, null, null, null); + } + + // + // The next two constructors are used only by the deprecated + // PlatformMimeTable classes or, in last case, is called by the public + // constructor. They are kept here anticipating putting support for + // mailcap formatted config files back in (so BOTH the properties format + // and the mailcap formats are supported). + // + MimeEntry(String type, String imageFileName, String extensionString) { + typeName = type.toLowerCase(); + action = UNKNOWN; + command = null; + this.imageFileName = imageFileName; + setExtensions(extensionString); + starred = isStarred(typeName); + } + + // For use with MimeTable::parseMailCap + MimeEntry(String typeName, int action, String command, + String tempFileNameTemplate) { + this.typeName = typeName.toLowerCase(); + this.action = action; + this.command = command; + this.imageFileName = null; + this.fileExtensions = null; + + this.tempFileNameTemplate = tempFileNameTemplate; + } + + // This is the one called by the public constructor. + MimeEntry(String typeName, int action, String command, + String imageFileName, String fileExtensions[]) { + + this.typeName = typeName.toLowerCase(); + this.action = action; + this.command = command; + this.imageFileName = imageFileName; + this.fileExtensions = fileExtensions; + + starred = isStarred(typeName); + + } + + public synchronized String getType() { + return typeName; + } + + public synchronized void setType(String type) { + typeName = type.toLowerCase(); + } + + public synchronized int getAction() { + return action; + } + + public synchronized void setAction(int action, String command) { + this.action = action; + this.command = command; + } + + public synchronized void setAction(int action) { + this.action = action; + } + + public synchronized String getLaunchString() { + return command; + } + + public synchronized void setCommand(String command) { + this.command = command; + } + + public synchronized String getDescription() { + return (description != null ? description : typeName); + } + + public synchronized void setDescription(String description) { + this.description = description; + } + + // ??? what to return for the image -- the file name or should this return + // something more advanced like an image source or something? + // returning the name has the least policy associated with it. + // pro tempore, we'll use the name + public String getImageFileName() { + return imageFileName; + } + + public synchronized void setImageFileName(String filename) { + File file = new File(filename); + if (file.getParent() == null) { + imageFileName = System.getProperty( + "java.net.ftp.imagepath."+filename); + } + else { + imageFileName = filename; + } + + if (filename.lastIndexOf('.') < 0) { + imageFileName = imageFileName + ".gif"; + } + } + + public String getTempFileTemplate() { + return tempFileNameTemplate; + } + + public synchronized String[] getExtensions() { + return fileExtensions; + } + + public synchronized String getExtensionsAsList() { + String extensionsAsString = ""; + if (fileExtensions != null) { + for (int i = 0; i < fileExtensions.length; i++) { + extensionsAsString += fileExtensions[i]; + if (i < (fileExtensions.length - 1)) { + extensionsAsString += ","; + } + } + } + + return extensionsAsString; + } + + public synchronized void setExtensions(String extensionString) { + StringTokenizer extTokens = new StringTokenizer(extensionString, ","); + int numExts = extTokens.countTokens(); + String extensionStrings[] = new String[numExts]; + + for (int i = 0; i < numExts; i++) { + String ext = (String)extTokens.nextElement(); + extensionStrings[i] = ext.trim(); + } + + fileExtensions = extensionStrings; + } + + private boolean isStarred(String typeName) { + return (typeName != null) + && (typeName.length() > 0) + && (typeName.endsWith("/*")); + } + + /** + * Invoke the MIME type specific behavior for this MIME type. + * Returned value can be one of several types: + *
      + *
    1. A thread -- the caller can choose when to launch this thread. + *
    2. A string -- the string is loaded into the browser directly. + *
    3. An input stream -- the caller can read from this byte stream and + * will typically store the results in a file. + *
    4. A document (?) -- + *
    + */ + public Object launch(java.net.URLConnection urlc, InputStream is, MimeTable mt) throws ApplicationLaunchException { + switch (action) { + case SAVE_TO_FILE: + // REMIND: is this really the right thing to do? + try { + return is; + } catch(Exception e) { + // I18N + return "Load to file failed:\n" + e; + } + + case LOAD_INTO_BROWSER: + // REMIND: invoke the content handler? + // may be the right thing to do, may not be -- short term + // where docs are not loaded asynch, loading and returning + // the content is the right thing to do. + try { + return urlc.getContent(); + } catch (Exception e) { + return null; + } + + case LAUNCH_APPLICATION: + { + String threadName = command; + int fst = threadName.indexOf(' '); + if (fst > 0) { + threadName = threadName.substring(0, fst); + } + + return new MimeLauncher(this, urlc, is, + mt.getTempFileTemplate(), threadName); + } + + case UNKNOWN: + // REMIND: What do do here? + return null; + } + + return null; + } + + public boolean matches(String type) { + if (starred) { + // REMIND: is this the right thing or not? + return type.startsWith(typeName); + } else { + return type.equals(typeName); + } + } + + public Object clone() { + // return a shallow copy of this. + MimeEntry theClone = new MimeEntry(typeName); + theClone.action = action; + theClone.command = command; + theClone.description = description; + theClone.imageFileName = imageFileName; + theClone.tempFileNameTemplate = tempFileNameTemplate; + theClone.fileExtensions = fileExtensions; + + return theClone; + } + + public synchronized String toProperty() { + StringBuffer buf = new StringBuffer(); + + String separator = "; "; + boolean needSeparator = false; + + int action = getAction(); + if (action != MimeEntry.UNKNOWN) { + buf.append("action=" + actionKeywords[action]); + needSeparator = true; + } + + String command = getLaunchString(); + if (command != null && command.length() > 0) { + if (needSeparator) { + buf.append(separator); + } + buf.append("application=" + command); + needSeparator = true; + } + + if (getImageFileName() != null) { + if (needSeparator) { + buf.append(separator); + } + buf.append("icon=" + getImageFileName()); + needSeparator = true; + } + + String extensions = getExtensionsAsList(); + if (extensions.length() > 0) { + if (needSeparator) { + buf.append(separator); + } + buf.append("file_extensions=" + extensions); + needSeparator = true; + } + + String description = getDescription(); + if (description != null && !description.equals(getType())) { + if (needSeparator) { + buf.append(separator); + } + buf.append("description=" + description); + } + + return buf.toString(); + } + + public String toString() { + return "MimeEntry[contentType=" + typeName + + ", image=" + imageFileName + + ", action=" + action + + ", command=" + command + + ", extensions=" + getExtensionsAsList() + + "]"; + } +} diff --git a/src/sun/net/www/MimeLauncher.java b/src/sun/net/www/MimeLauncher.java new file mode 100644 index 00000000..ee4fb40e --- /dev/null +++ b/src/sun/net/www/MimeLauncher.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; +import java.net.URL; +import java.io.*; +import java.util.StringTokenizer; + +class MimeLauncher extends Thread { + java.net.URLConnection uc; + MimeEntry m; + String genericTempFileTemplate; + InputStream is; + String execPath; + + MimeLauncher (MimeEntry M, java.net.URLConnection uc, + InputStream is, String tempFileTemplate, String threadName) throws ApplicationLaunchException { + super(threadName); + m = M; + this.uc = uc; + this.is = is; + genericTempFileTemplate = tempFileTemplate; + + /* get the application to launch */ + String launchString = m.getLaunchString(); + + /* get a valid path to launch application - sets + the execPath instance variable with the correct path. + */ + if (!findExecutablePath(launchString)) { + /* strip off parameters i.e %s */ + String appName; + int index = launchString.indexOf(' '); + if (index != -1) { + appName = launchString.substring(0, index); + } + else { + appName = launchString; + } + throw new ApplicationLaunchException(appName); + } + } + + protected String getTempFileName(URL url, String template) { + String tempFilename = template; + + // Replace all but last occurrance of "%s" with timestamp to insure + // uniqueness. There's a subtle behavior here: if there is anything + // _after_ the last "%s" we need to append it so that unusual launch + // strings that have the datafile in the middle can still be used. + int wildcard = tempFilename.lastIndexOf("%s"); + String prefix = tempFilename.substring(0, wildcard); + + String suffix = ""; + if (wildcard < tempFilename.length() - 2) { + suffix = tempFilename.substring(wildcard + 2); + } + + long timestamp = System.currentTimeMillis()/1000; + int argIndex = 0; + while ((argIndex = prefix.indexOf("%s")) >= 0) { + prefix = prefix.substring(0, argIndex) + + timestamp + + prefix.substring(argIndex + 2); + } + + // Add a file name and file-extension if known + String filename = url.getFile(); + + String extension = ""; + int dot = filename.lastIndexOf('.'); + + // BugId 4084826: Temp MIME file names not always valid. + // Fix: don't allow slashes in the file name or extension. + if (dot >= 0 && dot > filename.lastIndexOf('/')) { + extension = filename.substring(dot); + } + + filename = "HJ" + url.hashCode(); + + tempFilename = prefix + filename + timestamp + extension + suffix; + + return tempFilename; + } + + public void run() { + try { + String ofn = m.getTempFileTemplate(); + if (ofn == null) { + ofn = genericTempFileTemplate; + } + + ofn = getTempFileName(uc.getURL(), ofn); + try { + OutputStream os = new FileOutputStream(ofn); + byte buf[] = new byte[2048]; + int i = 0; + try { + while ((i = is.read(buf)) >= 0) { + os.write(buf, 0, i); + } + } catch(IOException e) { + //System.err.println("Exception in write loop " + i); + //e.printStackTrace(); + } finally { + os.close(); + is.close(); + } + } catch(IOException e) { + //System.err.println("Exception in input or output stream"); + //e.printStackTrace(); + } + + int inx = 0; + String c = execPath; + while ((inx = c.indexOf("%t")) >= 0) { + c = c.substring(0, inx) + uc.getContentType() + + c.substring(inx + 2); + } + + boolean substituted = false; + while ((inx = c.indexOf("%s")) >= 0) { + c = c.substring(0, inx) + ofn + c.substring(inx + 2); + substituted = true; + } + if (!substituted) + c = c + " <" + ofn; + + // System.out.println("Execing " +c); + + Runtime.getRuntime().exec(c); + } catch(IOException e) { + } + } + + /* This method determines the path for the launcher application + and sets the execPath instance variable. It uses the exec.path + property to obtain a list of paths that is in turn used to + location the application. If a valid path is not found, it + returns false else true. */ + private boolean findExecutablePath(String str) { + if (str == null || str.length() == 0) { + return false; + } + + String command; + int index = str.indexOf(' '); + if (index != -1) { + command = str.substring(0, index); + } + else { + command = str; + } + + File f = new File(command); + if (f.isFile()) { + // Already executable as it is + execPath = str; + return true; + } + + String execPathList; + execPathList = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("exec.path")); + if (execPathList == null) { + // exec.path property not set + return false; + } + + StringTokenizer iter = new StringTokenizer(execPathList, "|"); + while (iter.hasMoreElements()) { + String prefix = (String)iter.nextElement(); + String fullCmd = prefix + File.separator + command; + f = new File(fullCmd); + if (f.isFile()) { + execPath = prefix + File.separator + str; + return true; + } + } + + return false; // application not found in exec.path + } +} diff --git a/src/sun/net/www/MimeTable.java b/src/sun/net/www/MimeTable.java new file mode 100644 index 00000000..cab244c7 --- /dev/null +++ b/src/sun/net/www/MimeTable.java @@ -0,0 +1,459 @@ +/* + * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; +import java.io.*; +import java.net.FileNameMap; +import java.util.Hashtable; +import java.util.Enumeration; +import java.util.Properties; +import java.util.StringTokenizer; + +public class MimeTable implements FileNameMap { + /** Keyed by content type, returns MimeEntries */ + private Hashtable entries + = new Hashtable(); + + /** Keyed by file extension (with the .), returns MimeEntries */ + private Hashtable extensionMap + = new Hashtable(); + + // Will be reset if in the platform-specific data file + private static String tempFileTemplate; + + static { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + tempFileTemplate = + System.getProperty("content.types.temp.file.template", + "/tmp/%s"); + + mailcapLocations = new String[] { + System.getProperty("user.mailcap"), + System.getProperty("user.home") + "/.mailcap", + "/etc/mailcap", + "/usr/etc/mailcap", + "/usr/local/etc/mailcap", + System.getProperty("hotjava.home", + "/usr/local/hotjava") + + "/lib/mailcap", + }; + return null; + } + }); + } + + + private static final String filePreamble = "sun.net.www MIME content-types table"; + private static final String fileMagic = "#" + filePreamble; + + MimeTable() { + load(); + } + + private static class DefaultInstanceHolder { + static final MimeTable defaultInstance = getDefaultInstance(); + + static MimeTable getDefaultInstance() { + return java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public MimeTable run() { + MimeTable instance = new MimeTable(); + URLConnection.setFileNameMap(instance); + return instance; + } + }); + } + } + + /** + * Get the single instance of this class. First use will load the + * table from a data file. + */ + public static MimeTable getDefaultTable() { + return DefaultInstanceHolder.defaultInstance; + } + + /** + * + */ + public static FileNameMap loadTable() { + MimeTable mt = getDefaultTable(); + return (FileNameMap)mt; + } + + public synchronized int getSize() { + return entries.size(); + } + + public synchronized String getContentTypeFor(String fileName) { + MimeEntry entry = findByFileName(fileName); + if (entry != null) { + return entry.getType(); + } else { + return null; + } + } + + public synchronized void add(MimeEntry m) { + entries.put(m.getType(), m); + + String exts[] = m.getExtensions(); + if (exts == null) { + return; + } + + for (int i = 0; i < exts.length; i++) { + extensionMap.put(exts[i], m); + } + } + + public synchronized MimeEntry remove(String type) { + MimeEntry entry = entries.get(type); + return remove(entry); + } + + public synchronized MimeEntry remove(MimeEntry entry) { + String[] extensionKeys = entry.getExtensions(); + if (extensionKeys != null) { + for (int i = 0; i < extensionKeys.length; i++) { + extensionMap.remove(extensionKeys[i]); + } + } + + return entries.remove(entry.getType()); + } + + public synchronized MimeEntry find(String type) { + MimeEntry entry = entries.get(type); + if (entry == null) { + // try a wildcard lookup + Enumeration e = entries.elements(); + while (e.hasMoreElements()) { + MimeEntry wild = e.nextElement(); + if (wild.matches(type)) { + return wild; + } + } + } + + return entry; + } + + /** + * Locate a MimeEntry by the file extension that has been associated + * with it. Parses general file names, and URLs. + */ + public MimeEntry findByFileName(String fname) { + String ext = ""; + + int i = fname.lastIndexOf('#'); + + if (i > 0) { + fname = fname.substring(0, i - 1); + } + + i = fname.lastIndexOf('.'); + // REMIND: OS specific delimters appear here + i = Math.max(i, fname.lastIndexOf('/')); + i = Math.max(i, fname.lastIndexOf('?')); + + if (i != -1 && fname.charAt(i) == '.') { + ext = fname.substring(i).toLowerCase(); + } + + return findByExt(ext); + } + + /** + * Locate a MimeEntry by the file extension that has been associated + * with it. + */ + public synchronized MimeEntry findByExt(String fileExtension) { + return extensionMap.get(fileExtension); + } + + public synchronized MimeEntry findByDescription(String description) { + Enumeration e = elements(); + while (e.hasMoreElements()) { + MimeEntry entry = e.nextElement(); + if (description.equals(entry.getDescription())) { + return entry; + } + } + + // We failed, now try treating description as type + return find(description); + } + + String getTempFileTemplate() { + return tempFileTemplate; + } + + public synchronized Enumeration elements() { + return entries.elements(); + } + + // For backward compatibility -- mailcap format files + // This is not currently used, but may in the future when we add ability + // to read BOTH the properties format and the mailcap format. + protected static String[] mailcapLocations; + + public synchronized void load() { + Properties entries = new Properties(); + File file = null; + try { + InputStream is; + // First try to load the user-specific table, if it exists + String userTablePath = + System.getProperty("content.types.user.table"); + if (userTablePath != null) { + file = new File(userTablePath); + if (!file.exists()) { + // No user-table, try to load the default built-in table. + file = new File(System.getProperty("java.home") + + File.separator + + "lib" + + File.separator + + "content-types.properties"); + } + } + else { + // No user table, try to load the default built-in table. + file = new File(System.getProperty("java.home") + + File.separator + + "lib" + + File.separator + + "content-types.properties"); + } + + is = new BufferedInputStream(new FileInputStream(file)); + entries.load(is); + is.close(); + } + catch (IOException e) { + System.err.println("Warning: default mime table not found: " + + file.getPath()); + return; + } + parse(entries); + } + + void parse(Properties entries) { + // first, strip out the platform-specific temp file template + String tempFileTemplate = (String)entries.get("temp.file.template"); + if (tempFileTemplate != null) { + entries.remove("temp.file.template"); + MimeTable.tempFileTemplate = tempFileTemplate; + } + + // now, parse the mime-type spec's + Enumeration types = entries.propertyNames(); + while (types.hasMoreElements()) { + String type = (String)types.nextElement(); + String attrs = entries.getProperty(type); + parse(type, attrs); + } + } + + // + // Table format: + // + // ::= | + // + // ::= | + // + // ::= '=' + // + // ::= '/' + // + // ::= [ ';' ]* + // | [ ]+ + // + // ::= '=' + // + // ::= 'description' | 'action' | 'application' + // | 'file_extensions' | 'icon' + // + // ::= * + // + // Embedded ';' in an are quoted with leading '\' . + // + // Interpretation of depends on the it is + // associated with. + // + + void parse(String type, String attrs) { + MimeEntry newEntry = new MimeEntry(type); + + // REMIND handle embedded ';' and '|' and literal '"' + StringTokenizer tokenizer = new StringTokenizer(attrs, ";"); + while (tokenizer.hasMoreTokens()) { + String pair = tokenizer.nextToken(); + parse(pair, newEntry); + } + + add(newEntry); + } + + void parse(String pair, MimeEntry entry) { + // REMIND add exception handling... + String name = null; + String value = null; + + boolean gotName = false; + StringTokenizer tokenizer = new StringTokenizer(pair, "="); + while (tokenizer.hasMoreTokens()) { + if (gotName) { + value = tokenizer.nextToken().trim(); + } + else { + name = tokenizer.nextToken().trim(); + gotName = true; + } + } + + fill(entry, name, value); + } + + void fill(MimeEntry entry, String name, String value) { + if ("description".equalsIgnoreCase(name)) { + entry.setDescription(value); + } + else if ("action".equalsIgnoreCase(name)) { + entry.setAction(getActionCode(value)); + } + else if ("application".equalsIgnoreCase(name)) { + entry.setCommand(value); + } + else if ("icon".equalsIgnoreCase(name)) { + entry.setImageFileName(value); + } + else if ("file_extensions".equalsIgnoreCase(name)) { + entry.setExtensions(value); + } + + // else illegal name exception + } + + String[] getExtensions(String list) { + StringTokenizer tokenizer = new StringTokenizer(list, ","); + int n = tokenizer.countTokens(); + String[] extensions = new String[n]; + for (int i = 0; i < n; i++) { + extensions[i] = tokenizer.nextToken(); + } + + return extensions; + } + + int getActionCode(String action) { + for (int i = 0; i < MimeEntry.actionKeywords.length; i++) { + if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) { + return i; + } + } + + return MimeEntry.UNKNOWN; + } + + public synchronized boolean save(String filename) { + if (filename == null) { + filename = System.getProperty("user.home" + + File.separator + + "lib" + + File.separator + + "content-types.properties"); + } + + return saveAsProperties(new File(filename)); + } + + public Properties getAsProperties() { + Properties properties = new Properties(); + Enumeration e = elements(); + while (e.hasMoreElements()) { + MimeEntry entry = e.nextElement(); + properties.put(entry.getType(), entry.toProperty()); + } + + return properties; + } + + protected boolean saveAsProperties(File file) { + FileOutputStream os = null; + try { + os = new FileOutputStream(file); + Properties properties = getAsProperties(); + properties.put("temp.file.template", tempFileTemplate); + String tag; + String user = System.getProperty("user.name"); + if (user != null) { + tag = "; customized for " + user; + properties.store(os, filePreamble + tag); + } + else { + properties.store(os, filePreamble); + } + } + catch (IOException e) { + e.printStackTrace(); + return false; + } + finally { + if (os != null) { + try { os.close(); } catch (IOException e) {} + } + } + + return true; + } + /* + * Debugging utilities + * + public void list(PrintStream out) { + Enumeration keys = entries.keys(); + while (keys.hasMoreElements()) { + String key = (String)keys.nextElement(); + MimeEntry entry = (MimeEntry)entries.get(key); + out.println(key + ": " + entry); + } + } + + public static void main(String[] args) { + MimeTable testTable = MimeTable.getDefaultTable(); + + Enumeration e = testTable.elements(); + while (e.hasMoreElements()) { + MimeEntry entry = (MimeEntry)e.nextElement(); + System.out.println(entry); + } + + testTable.save(File.separator + "tmp" + + File.separator + "mime_table.save"); + } + */ +} diff --git a/src/sun/net/www/ParseUtil.java b/src/sun/net/www/ParseUtil.java new file mode 100644 index 00000000..a1926b78 --- /dev/null +++ b/src/sun/net/www/ParseUtil.java @@ -0,0 +1,680 @@ +/* + * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; + +import java.util.BitSet; +import java.io.UnsupportedEncodingException; +import java.io.File; +import java.net.URL; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import sun.nio.cs.ThreadLocalCoders; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/** + * A class that contains useful routines common to sun.net.www + * @author Mike McCloskey + */ + +public class ParseUtil { + static BitSet encodedInPath; + + static { + encodedInPath = new BitSet(256); + + // Set the bits corresponding to characters that are encoded in the + // path component of a URI. + + // These characters are reserved in the path segment as described in + // RFC2396 section 3.3. + encodedInPath.set('='); + encodedInPath.set(';'); + encodedInPath.set('?'); + encodedInPath.set('/'); + + // These characters are defined as excluded in RFC2396 section 2.4.3 + // and must be escaped if they occur in the data part of a URI. + encodedInPath.set('#'); + encodedInPath.set(' '); + encodedInPath.set('<'); + encodedInPath.set('>'); + encodedInPath.set('%'); + encodedInPath.set('"'); + encodedInPath.set('{'); + encodedInPath.set('}'); + encodedInPath.set('|'); + encodedInPath.set('\\'); + encodedInPath.set('^'); + encodedInPath.set('['); + encodedInPath.set(']'); + encodedInPath.set('`'); + + // US ASCII control characters 00-1F and 7F. + for (int i=0; i<32; i++) + encodedInPath.set(i); + encodedInPath.set(127); + } + + /** + * Constructs an encoded version of the specified path string suitable + * for use in the construction of a URL. + * + * A path separator is replaced by a forward slash. The string is UTF8 + * encoded. The % escape sequence is used for characters that are above + * 0x7F or those defined in RFC2396 as reserved or excluded in the path + * component of a URL. + */ + public static String encodePath(String path) { + return encodePath(path, true); + } + /* + * flag indicates whether path uses platform dependent + * File.separatorChar or not. True indicates path uses platform + * dependent File.separatorChar. + */ + public static String encodePath(String path, boolean flag) { + char[] retCC = new char[path.length() * 2 + 16]; + int retLen = 0; + char[] pathCC = path.toCharArray(); + + int n = path.length(); + for (int i=0; i= 'a' && c <= 'z' || + c >= 'A' && c <= 'Z' || + c >= '0' && c <= '9') { + retCC[retLen++] = c; + } else + if (encodedInPath.get(c)) + retLen = escape(retCC, c, retLen); + else + retCC[retLen++] = c; + } else if (c > 0x07FF) { + retLen = escape(retCC, (char)(0xE0 | ((c >> 12) & 0x0F)), retLen); + retLen = escape(retCC, (char)(0x80 | ((c >> 6) & 0x3F)), retLen); + retLen = escape(retCC, (char)(0x80 | ((c >> 0) & 0x3F)), retLen); + } else { + retLen = escape(retCC, (char)(0xC0 | ((c >> 6) & 0x1F)), retLen); + retLen = escape(retCC, (char)(0x80 | ((c >> 0) & 0x3F)), retLen); + } + } + //worst case scenario for character [0x7ff-] every single + //character will be encoded into 9 characters. + if (retLen + 9 > retCC.length) { + int newLen = retCC.length * 2 + 16; + if (newLen < 0) { + newLen = Integer.MAX_VALUE; + } + char[] buf = new char[newLen]; + System.arraycopy(retCC, 0, buf, 0, retLen); + retCC = buf; + } + } + return new String(retCC, 0, retLen); + } + + /** + * Appends the URL escape sequence for the specified char to the + * specified StringBuffer. + */ + private static int escape(char[] cc, char c, int index) { + cc[index++] = '%'; + cc[index++] = Character.forDigit((c >> 4) & 0xF, 16); + cc[index++] = Character.forDigit(c & 0xF, 16); + return index; + } + + /** + * Un-escape and return the character at position i in string s. + */ + private static byte unescape(String s, int i) { + return (byte) Integer.parseInt(s.substring(i+1,i+3),16); + } + + + /** + * Returns a new String constructed from the specified String by replacing + * the URL escape sequences and UTF8 encoding with the characters they + * represent. + */ + public static String decode(String s) { + int n = s.length(); + if ((n == 0) || (s.indexOf('%') < 0)) + return s; + + StringBuilder sb = new StringBuilder(n); + ByteBuffer bb = ByteBuffer.allocate(n); + CharBuffer cb = CharBuffer.allocate(n); + CharsetDecoder dec = ThreadLocalCoders.decoderFor("UTF-8") + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT); + + char c = s.charAt(0); + for (int i = 0; i < n;) { + assert c == s.charAt(i); + if (c != '%') { + sb.append(c); + if (++i >= n) + break; + c = s.charAt(i); + continue; + } + bb.clear(); + int ui = i; + for (;;) { + assert (n - i >= 2); + try { + bb.put(unescape(s, i)); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(); + } + i += 3; + if (i >= n) + break; + c = s.charAt(i); + if (c != '%') + break; + } + bb.flip(); + cb.clear(); + dec.reset(); + CoderResult cr = dec.decode(bb, cb, true); + if (cr.isError()) + throw new IllegalArgumentException("Error decoding percent encoded characters"); + cr = dec.flush(cb); + if (cr.isError()) + throw new IllegalArgumentException("Error decoding percent encoded characters"); + sb.append(cb.flip().toString()); + } + + return sb.toString(); + } + + /** + * Returns a canonical version of the specified string. + */ + public String canonizeString(String file) { + int i = 0; + int lim = file.length(); + + // Remove embedded /../ + while ((i = file.indexOf("/../")) >= 0) { + if ((lim = file.lastIndexOf('/', i - 1)) >= 0) { + file = file.substring(0, lim) + file.substring(i + 3); + } else { + file = file.substring(i + 3); + } + } + // Remove embedded /./ + while ((i = file.indexOf("/./")) >= 0) { + file = file.substring(0, i) + file.substring(i + 2); + } + // Remove trailing .. + while (file.endsWith("/..")) { + i = file.indexOf("/.."); + if ((lim = file.lastIndexOf('/', i - 1)) >= 0) { + file = file.substring(0, lim+1); + } else { + file = file.substring(0, i); + } + } + // Remove trailing . + if (file.endsWith("/.")) + file = file.substring(0, file.length() -1); + + return file; + } + + public static URL fileToEncodedURL(File file) + throws MalformedURLException + { + String path = file.getAbsolutePath(); + path = ParseUtil.encodePath(path); + if (!path.startsWith("/")) { + path = "/" + path; + } + if (!path.endsWith("/") && file.isDirectory()) { + path = path + "/"; + } + return new URL("file", "", path); + } + + public static URI toURI(URL url) { + String protocol = url.getProtocol(); + String auth = url.getAuthority(); + String path = url.getPath(); + String query = url.getQuery(); + String ref = url.getRef(); + if (path != null && !(path.startsWith("/"))) + path = "/" + path; + + // + // In java.net.URI class, a port number of -1 implies the default + // port number. So get it stripped off before creating URI instance. + // + if (auth != null && auth.endsWith(":-1")) + auth = auth.substring(0, auth.length() - 3); + + URI uri; + try { + uri = createURI(protocol, auth, path, query, ref); + } catch (URISyntaxException e) { + uri = null; + } + return uri; + } + + // + // createURI() and its auxiliary code are cloned from java.net.URI. + // Most of the code are just copy and paste, except that quote() + // has been modified to avoid double-escape. + // + // Usually it is unacceptable, but we're forced to do it because + // otherwise we need to change public API, namely java.net.URI's + // multi-argument constructors. It turns out that the changes cause + // incompatibilities so can't be done. + // + private static URI createURI(String scheme, + String authority, + String path, + String query, + String fragment) throws URISyntaxException + { + String s = toString(scheme, null, + authority, null, null, -1, + path, query, fragment); + checkPath(s, scheme, path); + return new URI(s); + } + + private static String toString(String scheme, + String opaquePart, + String authority, + String userInfo, + String host, + int port, + String path, + String query, + String fragment) + { + StringBuffer sb = new StringBuffer(); + if (scheme != null) { + sb.append(scheme); + sb.append(':'); + } + appendSchemeSpecificPart(sb, opaquePart, + authority, userInfo, host, port, + path, query); + appendFragment(sb, fragment); + return sb.toString(); + } + + private static void appendSchemeSpecificPart(StringBuffer sb, + String opaquePart, + String authority, + String userInfo, + String host, + int port, + String path, + String query) + { + if (opaquePart != null) { + /* check if SSP begins with an IPv6 address + * because we must not quote a literal IPv6 address + */ + if (opaquePart.startsWith("//[")) { + int end = opaquePart.indexOf("]"); + if (end != -1 && opaquePart.indexOf(":")!=-1) { + String doquote, dontquote; + if (end == opaquePart.length()) { + dontquote = opaquePart; + doquote = ""; + } else { + dontquote = opaquePart.substring(0,end+1); + doquote = opaquePart.substring(end+1); + } + sb.append (dontquote); + sb.append(quote(doquote, L_URIC, H_URIC)); + } + } else { + sb.append(quote(opaquePart, L_URIC, H_URIC)); + } + } else { + appendAuthority(sb, authority, userInfo, host, port); + if (path != null) + sb.append(quote(path, L_PATH, H_PATH)); + if (query != null) { + sb.append('?'); + sb.append(quote(query, L_URIC, H_URIC)); + } + } + } + + private static void appendAuthority(StringBuffer sb, + String authority, + String userInfo, + String host, + int port) + { + if (host != null) { + sb.append("//"); + if (userInfo != null) { + sb.append(quote(userInfo, L_USERINFO, H_USERINFO)); + sb.append('@'); + } + boolean needBrackets = ((host.indexOf(':') >= 0) + && !host.startsWith("[") + && !host.endsWith("]")); + if (needBrackets) sb.append('['); + sb.append(host); + if (needBrackets) sb.append(']'); + if (port != -1) { + sb.append(':'); + sb.append(port); + } + } else if (authority != null) { + sb.append("//"); + if (authority.startsWith("[")) { + int end = authority.indexOf("]"); + if (end != -1 && authority.indexOf(":")!=-1) { + String doquote, dontquote; + if (end == authority.length()) { + dontquote = authority; + doquote = ""; + } else { + dontquote = authority.substring(0,end+1); + doquote = authority.substring(end+1); + } + sb.append (dontquote); + sb.append(quote(doquote, + L_REG_NAME | L_SERVER, + H_REG_NAME | H_SERVER)); + } + } else { + sb.append(quote(authority, + L_REG_NAME | L_SERVER, + H_REG_NAME | H_SERVER)); + } + } + } + + private static void appendFragment(StringBuffer sb, String fragment) { + if (fragment != null) { + sb.append('#'); + sb.append(quote(fragment, L_URIC, H_URIC)); + } + } + + // Quote any characters in s that are not permitted + // by the given mask pair + // + private static String quote(String s, long lowMask, long highMask) { + int n = s.length(); + StringBuffer sb = null; + boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c < '\u0080') { + if (!match(c, lowMask, highMask) && !isEscaped(s, i)) { + if (sb == null) { + sb = new StringBuffer(); + sb.append(s.substring(0, i)); + } + appendEscape(sb, (byte)c); + } else { + if (sb != null) + sb.append(c); + } + } else if (allowNonASCII + && (Character.isSpaceChar(c) + || Character.isISOControl(c))) { + if (sb == null) { + sb = new StringBuffer(); + sb.append(s.substring(0, i)); + } + appendEncoded(sb, c); + } else { + if (sb != null) + sb.append(c); + } + } + return (sb == null) ? s : sb.toString(); + } + + // + // To check if the given string has an escaped triplet + // at the given position + // + private static boolean isEscaped(String s, int pos) { + if (s == null || (s.length() <= (pos + 2))) + return false; + + return s.charAt(pos) == '%' + && match(s.charAt(pos + 1), L_HEX, H_HEX) + && match(s.charAt(pos + 2), L_HEX, H_HEX); + } + + private static void appendEncoded(StringBuffer sb, char c) { + ByteBuffer bb = null; + try { + bb = ThreadLocalCoders.encoderFor("UTF-8") + .encode(CharBuffer.wrap("" + c)); + } catch (CharacterCodingException x) { + assert false; + } + while (bb.hasRemaining()) { + int b = bb.get() & 0xff; + if (b >= 0x80) + appendEscape(sb, (byte)b); + else + sb.append((char)b); + } + } + + private final static char[] hexDigits = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private static void appendEscape(StringBuffer sb, byte b) { + sb.append('%'); + sb.append(hexDigits[(b >> 4) & 0x0f]); + sb.append(hexDigits[(b >> 0) & 0x0f]); + } + + // Tell whether the given character is permitted by the given mask pair + private static boolean match(char c, long lowMask, long highMask) { + if (c < 64) + return ((1L << c) & lowMask) != 0; + if (c < 128) + return ((1L << (c - 64)) & highMask) != 0; + return false; + } + + // If a scheme is given then the path, if given, must be absolute + // + private static void checkPath(String s, String scheme, String path) + throws URISyntaxException + { + if (scheme != null) { + if ((path != null) + && ((path.length() > 0) && (path.charAt(0) != '/'))) + throw new URISyntaxException(s, + "Relative path in absolute URI"); + } + } + + + // -- Character classes for parsing -- + + // Compute a low-order mask for the characters + // between first and last, inclusive + private static long lowMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 63), 0); + int l = Math.max(Math.min(last, 63), 0); + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Compute the low-order mask for the characters in the given string + private static long lowMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if (c < 64) + m |= (1L << c); + } + return m; + } + + // Compute a high-order mask for the characters + // between first and last, inclusive + private static long highMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 127), 64) - 64; + int l = Math.max(Math.min(last, 127), 64) - 64; + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Compute the high-order mask for the characters in the given string + private static long highMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if ((c >= 64) && (c < 128)) + m |= (1L << (c - 64)); + } + return m; + } + + + // Character-class masks + + // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + // "8" | "9" + private static final long L_DIGIT = lowMask('0', '9'); + private static final long H_DIGIT = 0L; + + // hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + // "a" | "b" | "c" | "d" | "e" | "f" + private static final long L_HEX = L_DIGIT; + private static final long H_HEX = highMask('A', 'F') | highMask('a', 'f'); + + // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + private static final long L_UPALPHA = 0L; + private static final long H_UPALPHA = highMask('A', 'Z'); + + // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | + // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | + // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" + private static final long L_LOWALPHA = 0L; + private static final long H_LOWALPHA = highMask('a', 'z'); + + // alpha = lowalpha | upalpha + private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA; + private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA; + + // alphanum = alpha | digit + private static final long L_ALPHANUM = L_DIGIT | L_ALPHA; + private static final long H_ALPHANUM = H_DIGIT | H_ALPHA; + + // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | + // "(" | ")" + private static final long L_MARK = lowMask("-_.!~*'()"); + private static final long H_MARK = highMask("-_.!~*'()"); + + // unreserved = alphanum | mark + private static final long L_UNRESERVED = L_ALPHANUM | L_MARK; + private static final long H_UNRESERVED = H_ALPHANUM | H_MARK; + + // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | + // "$" | "," | "[" | "]" + // Added per RFC2732: "[", "]" + private static final long L_RESERVED = lowMask(";/?:@&=+$,[]"); + private static final long H_RESERVED = highMask(";/?:@&=+$,[]"); + + // The zero'th bit is used to indicate that escape pairs and non-US-ASCII + // characters are allowed; this is handled by the scanEscape method below. + private static final long L_ESCAPED = 1L; + private static final long H_ESCAPED = 0L; + + // Dash, for use in domainlabel and toplabel + private static final long L_DASH = lowMask("-"); + private static final long H_DASH = highMask("-"); + + // uric = reserved | unreserved | escaped + private static final long L_URIC = L_RESERVED | L_UNRESERVED | L_ESCAPED; + private static final long H_URIC = H_RESERVED | H_UNRESERVED | H_ESCAPED; + + // pchar = unreserved | escaped | + // ":" | "@" | "&" | "=" | "+" | "$" | "," + private static final long L_PCHAR + = L_UNRESERVED | L_ESCAPED | lowMask(":@&=+$,"); + private static final long H_PCHAR + = H_UNRESERVED | H_ESCAPED | highMask(":@&=+$,"); + + // All valid path characters + private static final long L_PATH = L_PCHAR | lowMask(";/"); + private static final long H_PATH = H_PCHAR | highMask(";/"); + + // userinfo = *( unreserved | escaped | + // ";" | ":" | "&" | "=" | "+" | "$" | "," ) + private static final long L_USERINFO + = L_UNRESERVED | L_ESCAPED | lowMask(";:&=+$,"); + private static final long H_USERINFO + = H_UNRESERVED | H_ESCAPED | highMask(";:&=+$,"); + + // reg_name = 1*( unreserved | escaped | "$" | "," | + // ";" | ":" | "@" | "&" | "=" | "+" ) + private static final long L_REG_NAME + = L_UNRESERVED | L_ESCAPED | lowMask("$,;:@&=+"); + private static final long H_REG_NAME + = H_UNRESERVED | H_ESCAPED | highMask("$,;:@&=+"); + + // All valid characters for server-based authorities + private static final long L_SERVER + = L_USERINFO | L_ALPHANUM | L_DASH | lowMask(".:@[]"); + private static final long H_SERVER + = H_USERINFO | H_ALPHANUM | H_DASH | highMask(".:@[]"); +} diff --git a/src/sun/net/www/URLConnection.java b/src/sun/net/www/URLConnection.java new file mode 100644 index 00000000..c4959285 --- /dev/null +++ b/src/sun/net/www/URLConnection.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www; + +import java.net.URL; +import java.util.*; + +/** + * A class to represent an active connection to an object + * represented by a URL. + * @author James Gosling + */ + +abstract public class URLConnection extends java.net.URLConnection { + + /** The URL that it is connected to */ + + private String contentType; + private int contentLength = -1; + + protected MessageHeader properties; + + /** Create a URLConnection object. These should not be created directly: + instead they should be created by protocol handers in response to + URL.openConnection. + @param u The URL that this connects to. + */ + public URLConnection (URL u) { + super(u); + properties = new MessageHeader(); + } + + /** Call this routine to get the property list for this object. + * Properties (like content-type) that have explicit getXX() methods + * associated with them should be accessed using those methods. */ + public MessageHeader getProperties() { + return properties; + } + + /** Call this routine to set the property list for this object. */ + public void setProperties(MessageHeader properties) { + this.properties = properties; + } + + public void setRequestProperty(String key, String value) { + if(connected) + throw new IllegalAccessError("Already connected"); + if (key == null) + throw new NullPointerException ("key cannot be null"); + properties.set(key, value); + } + + /** + * The following three methods addRequestProperty, getRequestProperty, + * and getRequestProperties were copied from the superclass implementation + * before it was changed by CR:6230836, to maintain backward compatibility. + */ + public void addRequestProperty(String key, String value) { + if (connected) + throw new IllegalStateException("Already connected"); + if (key == null) + throw new NullPointerException ("key is null"); + } + + public String getRequestProperty(String key) { + if (connected) + throw new IllegalStateException("Already connected"); + return null; + } + + public Map> getRequestProperties() { + if (connected) + throw new IllegalStateException("Already connected"); + return Collections.emptyMap(); + } + + public String getHeaderField(String name) { + try { + getInputStream(); + } catch (Exception e) { + return null; + } + return properties == null ? null : properties.findValue(name); + } + + /** + * Return the key for the nth header field. Returns null if + * there are fewer than n fields. This can be used to iterate + * through all the headers in the message. + */ + public String getHeaderFieldKey(int n) { + try { + getInputStream(); + } catch (Exception e) { + return null; + } + MessageHeader props = properties; + return props == null ? null : props.getKey(n); + } + + /** + * Return the value for the nth header field. Returns null if + * there are fewer than n fields. This can be used in conjunction + * with getHeaderFieldKey to iterate through all the headers in the message. + */ + public String getHeaderField(int n) { + try { + getInputStream(); + } catch (Exception e) { + return null; + } + MessageHeader props = properties; + return props == null ? null : props.getValue(n); + } + + /** Call this routine to get the content-type associated with this + * object. + */ + public String getContentType() { + if (contentType == null) + contentType = getHeaderField("content-type"); + if (contentType == null) { + String ct = null; + try { + ct = guessContentTypeFromStream(getInputStream()); + } catch(java.io.IOException e) { + } + String ce = properties.findValue("content-encoding"); + if (ct == null) { + ct = properties.findValue("content-type"); + + if (ct == null) + if (url.getFile().endsWith("/")) + ct = "text/html"; + else + ct = guessContentTypeFromName(url.getFile()); + } + + /* + * If the Mime header had a Content-encoding field and its value + * was not one of the values that essentially indicate no + * encoding, we force the content type to be unknown. This will + * cause a save dialog to be presented to the user. It is not + * ideal but is better than what we were previously doing, namely + * bringing up an image tool for compressed tar files. + */ + + if (ct == null || ce != null && + !(ce.equalsIgnoreCase("7bit") + || ce.equalsIgnoreCase("8bit") + || ce.equalsIgnoreCase("binary"))) + ct = "content/unknown"; + setContentType(ct); + } + return contentType; + } + + /** + * Set the content type of this URL to a specific value. + * @param type The content type to use. One of the + * content_* static variables in this + * class should be used. + * eg. setType(URL.content_html); + */ + public void setContentType(String type) { + contentType = type; + properties.set("content-type", type); + } + + /** Call this routine to get the content-length associated with this + * object. + */ + public int getContentLength() { + try { + getInputStream(); + } catch (Exception e) { + return -1; + } + int l = contentLength; + if (l < 0) { + try { + l = Integer.parseInt(properties.findValue("content-length")); + setContentLength(l); + } catch(Exception e) { + } + } + return l; + } + + /** Call this routine to set the content-length associated with this + * object. + */ + protected void setContentLength(int length) { + contentLength = length; + properties.set("content-length", String.valueOf(length)); + } + + /** + * Returns true if the data associated with this URL can be cached. + */ + public boolean canCache() { + return url.getFile().indexOf('?') < 0 /* && url.postData == null + REMIND */ ; + } + + /** + * Call this to close the connection and flush any remaining data. + * Overriders must remember to call super.close() + */ + public void close() { + url = null; + } + + private static HashMap proxiedHosts = new HashMap<>(); + + public synchronized static void setProxiedHost(String host) { + proxiedHosts.put(host.toLowerCase(), null); + } + + public synchronized static boolean isProxiedHost(String host) { + return proxiedHosts.containsKey(host.toLowerCase()); + } +} diff --git a/src/sun/net/www/content/audio/aiff.java b/src/sun/net/www/content/audio/aiff.java new file mode 100644 index 00000000..802942d0 --- /dev/null +++ b/src/sun/net/www/content/audio/aiff.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Basic .aiff audio handler. + * @author Jeff Nisewanger + */ +package sun.net.www.content.audio; + +import java.net.*; +import java.io.IOException; +import sun.applet.AppletAudioClip; + +/** + * Returns an AppletAudioClip object. + */ +public class aiff extends ContentHandler { + public Object getContent(URLConnection uc) throws IOException { + return new AppletAudioClip(uc); + } +} diff --git a/src/sun/net/www/content/audio/basic.java b/src/sun/net/www/content/audio/basic.java new file mode 100644 index 00000000..0389a3a3 --- /dev/null +++ b/src/sun/net/www/content/audio/basic.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Basic .au and .snd audio handler. + * @author Jeff Nisewanger + */ +package sun.net.www.content.audio; + +import java.net.*; +import java.io.IOException; +import sun.applet.AppletAudioClip; + +/** + * Returns an AppletAudioClip object. + * This provides backwards compatibility with the behavior + * of ClassLoader.getResource().getContent() on JDK1.1. + */ +public class basic extends ContentHandler { + public Object getContent(URLConnection uc) throws IOException { + return new AppletAudioClip(uc); + } +} diff --git a/src/sun/net/www/content/audio/wav.java b/src/sun/net/www/content/audio/wav.java new file mode 100644 index 00000000..9d76ee13 --- /dev/null +++ b/src/sun/net/www/content/audio/wav.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Basic .wav audio handler. + * @author Jeff Nisewanger + */ +package sun.net.www.content.audio; + +import java.net.*; +import java.io.IOException; +import sun.applet.AppletAudioClip; + +/** + * Returns an AppletAudioClip object. + */ +public class wav extends ContentHandler { + public Object getContent(URLConnection uc) throws IOException { + return new AppletAudioClip(uc); + } +} diff --git a/src/sun/net/www/content/audio/x_aiff.java b/src/sun/net/www/content/audio/x_aiff.java new file mode 100644 index 00000000..1c7d94b0 --- /dev/null +++ b/src/sun/net/www/content/audio/x_aiff.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Basic .aiff audio handler. + * @author Jeff Nisewanger + */ +package sun.net.www.content.audio; + +import java.net.*; +import java.io.IOException; +import sun.applet.AppletAudioClip; + +/** + * Returns an AppletAudioClip object. + */ +public class x_aiff extends ContentHandler { + public Object getContent(URLConnection uc) throws IOException { + return new AppletAudioClip(uc); + } +} diff --git a/src/sun/net/www/content/audio/x_wav.java b/src/sun/net/www/content/audio/x_wav.java new file mode 100644 index 00000000..4aa765d3 --- /dev/null +++ b/src/sun/net/www/content/audio/x_wav.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Basic .wav audio handler. + * @author Jeff Nisewanger + */ +package sun.net.www.content.audio; + +import java.net.*; +import java.io.IOException; +import sun.applet.AppletAudioClip; + +/** + * Returns an AppletAudioClip object. + */ +public class x_wav extends ContentHandler { + public Object getContent(URLConnection uc) throws IOException { + return new AppletAudioClip(uc); + } +} diff --git a/src/sun/net/www/content/image/gif.java b/src/sun/net/www/content/image/gif.java new file mode 100644 index 00000000..7106d15c --- /dev/null +++ b/src/sun/net/www/content/image/gif.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.image; + +import java.net.*; +import sun.awt.image.*; +import java.io.IOException; +import java.awt.Image; +import java.awt.Toolkit; + + +public class gif extends ContentHandler { + public Object getContent(URLConnection urlc) throws IOException { + return new URLImageSource(urlc); + } + + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws IOException { + Class[] cls = classes; + for (int i = 0; i < cls.length; i++) { + if (cls[i].isAssignableFrom(URLImageSource.class)) { + return new URLImageSource(urlc); + } + if (cls[i].isAssignableFrom(Image.class)) { + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(new URLImageSource(urlc)); + } + } + return null; + } +} diff --git a/src/sun/net/www/content/image/jpeg.java b/src/sun/net/www/content/image/jpeg.java new file mode 100644 index 00000000..20333517 --- /dev/null +++ b/src/sun/net/www/content/image/jpeg.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.image; + +import java.net.*; +import sun.awt.image.*; +import java.io.IOException; +import java.awt.Image; +import java.awt.Toolkit; + +public class jpeg extends ContentHandler { + public Object getContent(URLConnection urlc) throws IOException { + return new URLImageSource(urlc); + } + + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws IOException { + Class[] cls = classes; + for (int i = 0; i < cls.length; i++) { + if (cls[i].isAssignableFrom(URLImageSource.class)) { + return new URLImageSource(urlc); + } + if (cls[i].isAssignableFrom(Image.class)) { + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(new URLImageSource(urlc)); + } + } + return null; + } +} diff --git a/src/sun/net/www/content/image/png.java b/src/sun/net/www/content/image/png.java new file mode 100644 index 00000000..74eeab61 --- /dev/null +++ b/src/sun/net/www/content/image/png.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.image; + +import java.net.*; +import java.io.IOException; +import sun.awt.image.*; +import java.awt.Image; +import java.awt.Toolkit; + +public class png extends ContentHandler { + public Object getContent(URLConnection urlc) throws IOException { + return new URLImageSource(urlc); + } + + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws IOException { + Class[] cls = classes; + for (int i = 0; i < cls.length; i++) { + if (cls[i].isAssignableFrom(URLImageSource.class)) { + return new URLImageSource(urlc); + } + if (cls[i].isAssignableFrom(Image.class)) { + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(new URLImageSource(urlc)); + } + } + return null; + } +} diff --git a/src/sun/net/www/content/image/x_xbitmap.java b/src/sun/net/www/content/image/x_xbitmap.java new file mode 100644 index 00000000..839b902e --- /dev/null +++ b/src/sun/net/www/content/image/x_xbitmap.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.image; + +import java.net.*; +import sun.awt.image.*; +import java.awt.Image; +import java.awt.Toolkit; + +public class x_xbitmap extends ContentHandler { + public Object getContent(URLConnection urlc) throws java.io.IOException { + return new URLImageSource(urlc); + } + + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws java.io.IOException { + Class[] cls = classes; + for (int i = 0; i < cls.length; i++) { + if (cls[i].isAssignableFrom(URLImageSource.class)) { + return new URLImageSource(urlc); + } + if (cls[i].isAssignableFrom(Image.class)) { + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(new URLImageSource(urlc)); + } + } + return null; + } +} diff --git a/src/sun/net/www/content/image/x_xpixmap.java b/src/sun/net/www/content/image/x_xpixmap.java new file mode 100644 index 00000000..d2208449 --- /dev/null +++ b/src/sun/net/www/content/image/x_xpixmap.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.image; + +import java.net.*; +import sun.awt.image.*; +import java.awt.Image; +import java.awt.Toolkit; + +public class x_xpixmap extends ContentHandler { + public Object getContent(URLConnection urlc) throws java.io.IOException { + return new URLImageSource(urlc); + } + + @SuppressWarnings("rawtypes") + public Object getContent(URLConnection urlc, Class[] classes) throws java.io.IOException { + Class[] cls = classes; + for (int i = 0; i < cls.length; i++) { + if (cls[i].isAssignableFrom(URLImageSource.class)) { + return new URLImageSource(urlc); + } + if (cls[i].isAssignableFrom(Image.class)) { + Toolkit tk = Toolkit.getDefaultToolkit(); + return tk.createImage(new URLImageSource(urlc)); + } + } + return null; + } +} diff --git a/src/sun/net/www/content/text/Generic.java b/src/sun/net/www/content/text/Generic.java new file mode 100644 index 00000000..800c5c72 --- /dev/null +++ b/src/sun/net/www/content/text/Generic.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1994, 1995, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Generic text file handler + */ +package sun.net.www.content.text; + +public class Generic extends plain { + /* nothing to do since Generic is identical to plain! */ +} diff --git a/src/sun/net/www/content/text/PlainTextInputStream.java b/src/sun/net/www/content/text/PlainTextInputStream.java new file mode 100644 index 00000000..6291cfb6 --- /dev/null +++ b/src/sun/net/www/content/text/PlainTextInputStream.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.content.text; +import java.io.InputStream; +import java.io.FilterInputStream; + +/** + * PlainTextInputStream class extends the FilterInputStream class. + * Currently all calls to the PlainTextInputStream object will call + * the corresponding methods in the FilterInputStream class. Hence + * for now its use is more semantic. + * + * @author Sunita Mani + */ +public class PlainTextInputStream extends FilterInputStream { + + /** + * Calls FilterInputStream's constructor. + * @param an InputStream + */ + PlainTextInputStream(InputStream is) { + super(is); + } +} diff --git a/src/sun/net/www/content/text/plain.java b/src/sun/net/www/content/text/plain.java new file mode 100644 index 00000000..32836559 --- /dev/null +++ b/src/sun/net/www/content/text/plain.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1994, 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Plain text file handler. + * @author Steven B. Byrne + */ +package sun.net.www.content.text; +import java.net.*; +import java.io.InputStream; +import java.io.IOException; + +public class plain extends ContentHandler { + /** + * Returns a PlainTextInputStream object from which data + * can be read. + */ + public Object getContent(URLConnection uc) { + try { + InputStream is = uc.getInputStream(); + return new PlainTextInputStream(uc.getInputStream()); + } catch (IOException e) { + return "Error reading document:\n" + e.toString(); + } + } +} diff --git a/src/sun/net/www/http/ChunkedInputStream.java b/src/sun/net/www/http/ChunkedInputStream.java new file mode 100644 index 00000000..786a6c24 --- /dev/null +++ b/src/sun/net/www/http/ChunkedInputStream.java @@ -0,0 +1,780 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.www.http; + +import java.io.*; +import java.util.*; + +import sun.net.*; +import sun.net.www.*; + +/** + * A ChunkedInputStream provides a stream for reading a body of + * a http message that can be sent as a series of chunks, each with its own + * size indicator. Optionally the last chunk can be followed by trailers + * containing entity-header fields. + *

    + * A ChunkedInputStream is also Hurryable so it + * can be hurried to the end of the stream if the bytes are available on + * the underlying stream. + */ +public +class ChunkedInputStream extends InputStream implements Hurryable { + + /** + * The underlying stream + */ + private InputStream in; + + /** + * The HttpClient that should be notified when the chunked stream has + * completed. + */ + private HttpClient hc; + + /** + * The MessageHeader that is populated with any optional trailer + * that appear after the last chunk. + */ + private MessageHeader responses; + + /** + * The size, in bytes, of the chunk that is currently being read. + * This size is only valid if the current position in the underlying + * input stream is inside a chunk (ie: state == STATE_READING_CHUNK). + */ + private int chunkSize; + + /** + * The number of bytes read from the underlying stream for the current + * chunk. This value is always in the range 0 through to + * chunkSize + */ + private int chunkRead; + + /** + * The internal buffer array where chunk data is available for the + * application to read. + */ + private byte chunkData[] = new byte[4096]; + + /** + * The current position in the buffer. It contains the index + * of the next byte to read from chunkData + */ + private int chunkPos; + + /** + * The index one greater than the index of the last valid byte in the + * buffer. This value is always in the range 0 through + * chunkData.length. + */ + private int chunkCount; + + /** + * The internal buffer where bytes from the underlying stream can be + * read. It may contain bytes representing chunk-size, chunk-data, or + * trailer fields. + */ + private byte rawData[] = new byte[32]; + + /** + * The current position in the buffer. It contains the index + * of the next byte to read from rawData + */ + private int rawPos; + + /** + * The index one greater than the index of the last valid byte in the + * buffer. This value is always in the range 0 through + * rawData.length. + */ + private int rawCount; + + /** + * Indicates if an error was encountered when processing the chunked + * stream. + */ + private boolean error; + + /** + * Indicates if the chunked stream has been closed using the + * close method. + */ + private boolean closed; + + /* + * Maximum chunk header size of 2KB + 2 bytes for CRLF + */ + private final static int MAX_CHUNK_HEADER_SIZE = 2050; + + /** + * State to indicate that next field should be :- + * chunk-size [ chunk-extension ] CRLF + */ + static final int STATE_AWAITING_CHUNK_HEADER = 1; + + /** + * State to indicate that we are currently reading the chunk-data. + */ + static final int STATE_READING_CHUNK = 2; + + /** + * Indicates that a chunk has been completely read and the next + * fields to be examine should be CRLF + */ + static final int STATE_AWAITING_CHUNK_EOL = 3; + + /** + * Indicates that all chunks have been read and the next field + * should be optional trailers or an indication that the chunked + * stream is complete. + */ + static final int STATE_AWAITING_TRAILERS = 4; + + /** + * State to indicate that the chunked stream is complete and + * no further bytes should be read from the underlying stream. + */ + static final int STATE_DONE = 5; + + /** + * Indicates the current state. + */ + private int state; + + + /** + * Check to make sure that this stream has not been closed. + */ + private void ensureOpen() throws IOException { + if (closed) { + throw new IOException("stream is closed"); + } + } + + + /** + * Ensures there is size bytes available in + * rawData. This requires that we either + * shift the bytes in use to the begining of the buffer + * or allocate a large buffer with sufficient space available. + */ + private void ensureRawAvailable(int size) { + if (rawCount + size > rawData.length) { + int used = rawCount - rawPos; + if (used + size > rawData.length) { + byte tmp[] = new byte[used + size]; + if (used > 0) { + System.arraycopy(rawData, rawPos, tmp, 0, used); + } + rawData = tmp; + } else { + if (used > 0) { + System.arraycopy(rawData, rawPos, rawData, 0, used); + } + } + rawCount = used; + rawPos = 0; + } + } + + + /** + * Close the underlying input stream by either returning it to the + * keep alive cache or closing the stream. + *

    + * As a chunked stream is inheritly persistent (see HTTP 1.1 RFC) the + * underlying stream can be returned to the keep alive cache if the + * stream can be completely read without error. + */ + private void closeUnderlying() throws IOException { + if (in == null) { + return; + } + + if (!error && state == STATE_DONE) { + hc.finished(); + } else { + if (!hurry()) { + hc.closeServer(); + } + } + + in = null; + } + + /** + * Attempt to read the remainder of a chunk directly into the + * caller's buffer. + *

    + * Return the number of bytes read. + */ + private int fastRead(byte[] b, int off, int len) throws IOException { + + // assert state == STATE_READING_CHUNKS; + + int remaining = chunkSize - chunkRead; + int cnt = (remaining < len) ? remaining : len; + if (cnt > 0) { + int nread; + try { + nread = in.read(b, off, cnt); + } catch (IOException e) { + error = true; + throw e; + } + if (nread > 0) { + chunkRead += nread; + if (chunkRead >= chunkSize) { + state = STATE_AWAITING_CHUNK_EOL; + } + return nread; + } + error = true; + throw new IOException("Premature EOF"); + } else { + return 0; + } + } + + /** + * Process any outstanding bytes that have already been read into + * rawData. + *

    + * The parsing of the chunked stream is performed as a state machine with + * state representing the current state of the processing. + *

    + * Returns when either all the outstanding bytes in rawData have been + * processed or there is insufficient bytes available to continue + * processing. When the latter occurs rawPos will not have + * been updated and thus the processing can be restarted once further + * bytes have been read into rawData. + */ + private void processRaw() throws IOException { + int pos; + int i; + + while (state != STATE_DONE) { + + switch (state) { + + /** + * We are awaiting a line with a chunk header + */ + case STATE_AWAITING_CHUNK_HEADER: + /* + * Find \n to indicate end of chunk header. If not found when there is + * insufficient bytes in the raw buffer to parse a chunk header. + */ + pos = rawPos; + while (pos < rawCount) { + if (rawData[pos] == '\n') { + break; + } + pos++; + if ((pos - rawPos) >= MAX_CHUNK_HEADER_SIZE) { + error = true; + throw new IOException("Chunk header too long"); + } + } + if (pos >= rawCount) { + return; + } + + /* + * Extract the chunk size from the header (ignoring extensions). + */ + String header = new String(rawData, rawPos, pos-rawPos+1, "US-ASCII"); + for (i=0; i < header.length(); i++) { + if (Character.digit(header.charAt(i), 16) == -1) + break; + } + try { + chunkSize = Integer.parseInt(header.substring(0, i), 16); + } catch (NumberFormatException e) { + error = true; + throw new IOException("Bogus chunk size"); + } + + /* + * Chunk has been parsed so move rawPos to first byte of chunk + * data. + */ + rawPos = pos + 1; + chunkRead = 0; + + /* + * A chunk size of 0 means EOF. + */ + if (chunkSize > 0) { + state = STATE_READING_CHUNK; + } else { + state = STATE_AWAITING_TRAILERS; + } + break; + + + /** + * We are awaiting raw entity data (some may have already been + * read). chunkSize is the size of the chunk; chunkRead is the + * total read from the underlying stream to date. + */ + case STATE_READING_CHUNK : + /* no data available yet */ + if (rawPos >= rawCount) { + return; + } + + /* + * Compute the number of bytes of chunk data available in the + * raw buffer. + */ + int copyLen = Math.min( chunkSize-chunkRead, rawCount-rawPos ); + + /* + * Expand or compact chunkData if needed. + */ + if (chunkData.length < chunkCount + copyLen) { + int cnt = chunkCount - chunkPos; + if (chunkData.length < cnt + copyLen) { + byte tmp[] = new byte[cnt + copyLen]; + System.arraycopy(chunkData, chunkPos, tmp, 0, cnt); + chunkData = tmp; + } else { + System.arraycopy(chunkData, chunkPos, chunkData, 0, cnt); + } + chunkPos = 0; + chunkCount = cnt; + } + + /* + * Copy the chunk data into chunkData so that it's available + * to the read methods. + */ + System.arraycopy(rawData, rawPos, chunkData, chunkCount, copyLen); + rawPos += copyLen; + chunkCount += copyLen; + chunkRead += copyLen; + + /* + * If all the chunk has been copied into chunkData then the next + * token should be CRLF. + */ + if (chunkSize - chunkRead <= 0) { + state = STATE_AWAITING_CHUNK_EOL; + } else { + return; + } + break; + + + /** + * Awaiting CRLF after the chunk + */ + case STATE_AWAITING_CHUNK_EOL: + /* not available yet */ + if (rawPos + 1 >= rawCount) { + return; + } + + if (rawData[rawPos] != '\r') { + error = true; + throw new IOException("missing CR"); + } + if (rawData[rawPos+1] != '\n') { + error = true; + throw new IOException("missing LF"); + } + rawPos += 2; + + /* + * Move onto the next chunk + */ + state = STATE_AWAITING_CHUNK_HEADER; + break; + + + /** + * Last chunk has been read so not we're waiting for optional + * trailers. + */ + case STATE_AWAITING_TRAILERS: + + /* + * Do we have an entire line in the raw buffer? + */ + pos = rawPos; + while (pos < rawCount) { + if (rawData[pos] == '\n') { + break; + } + pos++; + } + if (pos >= rawCount) { + return; + } + + if (pos == rawPos) { + error = true; + throw new IOException("LF should be proceeded by CR"); + } + if (rawData[pos-1] != '\r') { + error = true; + throw new IOException("LF should be proceeded by CR"); + } + + /* + * Stream done so close underlying stream. + */ + if (pos == (rawPos + 1)) { + + state = STATE_DONE; + closeUnderlying(); + + return; + } + + /* + * Extract any tailers and append them to the message + * headers. + */ + String trailer = new String(rawData, rawPos, pos-rawPos, "US-ASCII"); + i = trailer.indexOf(':'); + if (i == -1) { + throw new IOException("Malformed tailer - format should be key:value"); + } + String key = (trailer.substring(0, i)).trim(); + String value = (trailer.substring(i+1, trailer.length())).trim(); + + responses.add(key, value); + + /* + * Move onto the next trailer. + */ + rawPos = pos+1; + break; + + } /* switch */ + } + } + + + /** + * Reads any available bytes from the underlying stream into + * rawData and returns the number of bytes of + * chunk data available in chunkData that the + * application can read. + */ + private int readAheadNonBlocking() throws IOException { + + /* + * If there's anything available on the underlying stream then we read + * it into the raw buffer and process it. Processing ensures that any + * available chunk data is made available in chunkData. + */ + int avail = in.available(); + if (avail > 0) { + + /* ensure that there is space in rawData to read the available */ + ensureRawAvailable(avail); + + int nread; + try { + nread = in.read(rawData, rawCount, avail); + } catch (IOException e) { + error = true; + throw e; + } + if (nread < 0) { + error = true; /* premature EOF ? */ + return -1; + } + rawCount += nread; + + /* + * Process the raw bytes that have been read. + */ + processRaw(); + } + + /* + * Return the number of chunked bytes available to read + */ + return chunkCount - chunkPos; + } + + /** + * Reads from the underlying stream until there is chunk data + * available in chunkData for the application to + * read. + */ + private int readAheadBlocking() throws IOException { + + do { + /* + * All of chunked response has been read to return EOF. + */ + if (state == STATE_DONE) { + return -1; + } + + /* + * We must read into the raw buffer so make sure there is space + * available. We use a size of 32 to avoid too much chunk data + * being read into the raw buffer. + */ + ensureRawAvailable(32); + int nread; + try { + nread = in.read(rawData, rawCount, rawData.length-rawCount); + } catch (IOException e) { + error = true; + throw e; + } + + /** + * If we hit EOF it means there's a problem as we should never + * attempt to read once the last chunk and trailers have been + * received. + */ + if (nread < 0) { + error = true; + throw new IOException("Premature EOF"); + } + + /** + * Process the bytes from the underlying stream + */ + rawCount += nread; + processRaw(); + + } while (chunkCount <= 0); + + /* + * Return the number of chunked bytes available to read + */ + return chunkCount - chunkPos; + } + + /** + * Read ahead in either blocking or non-blocking mode. This method + * is typically used when we run out of available bytes in + * chunkData or we need to determine how many bytes + * are available on the input stream. + */ + private int readAhead(boolean allowBlocking) throws IOException { + + /* + * Last chunk already received - return EOF + */ + if (state == STATE_DONE) { + return -1; + } + + /* + * Reset position/count if data in chunkData is exhausted. + */ + if (chunkPos >= chunkCount) { + chunkCount = 0; + chunkPos = 0; + } + + /* + * Read ahead blocking or non-blocking + */ + if (allowBlocking) { + return readAheadBlocking(); + } else { + return readAheadNonBlocking(); + } + } + + /** + * Creates a ChunkedInputStream and saves its arguments, for + * later use. + * + * @param in the underlying input stream. + * @param hc the HttpClient + * @param responses the MessageHeader that should be populated with optional + * trailers. + */ + public ChunkedInputStream(InputStream in, HttpClient hc, MessageHeader responses) throws IOException { + + /* save arguments */ + this.in = in; + this.responses = responses; + this.hc = hc; + + /* + * Set our initial state to indicate that we are first starting to + * look for a chunk header. + */ + state = STATE_AWAITING_CHUNK_HEADER; + } + + /** + * See + * the general contract of the read + * method of InputStream. + * + * @return the next byte of data, or -1 if the end of the + * stream is reached. + * @exception IOException if an I/O error occurs. + * @see FilterInputStream#in + */ + public synchronized int read() throws IOException { + ensureOpen(); + if (chunkPos >= chunkCount) { + if (readAhead(true) <= 0) { + return -1; + } + } + return chunkData[chunkPos++] & 0xff; + } + + + /** + * Reads bytes from this stream into the specified byte array, starting at + * the given offset. + * + * @param b destination buffer. + * @param off offset at which to start storing bytes. + * @param len maximum number of bytes to read. + * @return the number of bytes read, or -1 if the end of + * the stream has been reached. + * @exception IOException if an I/O error occurs. + */ + public synchronized int read(byte b[], int off, int len) + throws IOException + { + ensureOpen(); + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int avail = chunkCount - chunkPos; + if (avail <= 0) { + /* + * Optimization: if we're in the middle of the chunk read + * directly from the underlying stream into the caller's + * buffer + */ + if (state == STATE_READING_CHUNK) { + return fastRead( b, off, len ); + } + + /* + * We're not in the middle of a chunk so we must read ahead + * until there is some chunk data available. + */ + avail = readAhead(true); + if (avail < 0) { + return -1; /* EOF */ + } + } + int cnt = (avail < len) ? avail : len; + System.arraycopy(chunkData, chunkPos, b, off, cnt); + chunkPos += cnt; + + return cnt; + } + + /** + * Returns the number of bytes that can be read from this input + * stream without blocking. + * + * @return the number of bytes that can be read from this input + * stream without blocking. + * @exception IOException if an I/O error occurs. + * @see FilterInputStream#in + */ + public synchronized int available() throws IOException { + ensureOpen(); + + int avail = chunkCount - chunkPos; + if(avail > 0) { + return avail; + } + + avail = readAhead(false); + + if (avail < 0) { + return 0; + } else { + return avail; + } + } + + /** + * Close the stream by either returning the connection to the + * keep alive cache or closing the underlying stream. + *

    + * If the chunked response hasn't been completely read we + * try to "hurry" to the end of the response. If this is + * possible (without blocking) then the connection can be + * returned to the keep alive cache. + * + * @exception IOException if an I/O error occurs. + */ + public synchronized void close() throws IOException { + if (closed) { + return; + } + closeUnderlying(); + closed = true; + } + + /** + * Hurry the input stream by reading everything from the underlying + * stream. If the last chunk (and optional trailers) can be read without + * blocking then the stream is considered hurried. + *

    + * Note that if an error has occurred or we can't get to last chunk + * without blocking then this stream can't be hurried and should be + * closed. + */ + public synchronized boolean hurry() { + if (in == null || error) { + return false; + } + + try { + readAhead(false); + } catch (Exception e) { + return false; + } + + if (error) { + return false; + } + + return (state == STATE_DONE); + } + +} diff --git a/src/sun/net/www/http/ChunkedOutputStream.java b/src/sun/net/www/http/ChunkedOutputStream.java new file mode 100644 index 00000000..68f089b9 --- /dev/null +++ b/src/sun/net/www/http/ChunkedOutputStream.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.www.http; + +import java.io.*; + +/** + * OutputStream that sends the output to the underlying stream using chunked + * encoding as specified in RFC 2068. + */ +public class ChunkedOutputStream extends PrintStream { + + /* Default chunk size (including chunk header) if not specified */ + static final int DEFAULT_CHUNK_SIZE = 4096; + private static final byte[] CRLF = {'\r', '\n'}; + private static final int CRLF_SIZE = CRLF.length; + private static final byte[] FOOTER = CRLF; + private static final int FOOTER_SIZE = CRLF_SIZE; + private static final byte[] EMPTY_CHUNK_HEADER = getHeader(0); + private static final int EMPTY_CHUNK_HEADER_SIZE = getHeaderSize(0); + + /* internal buffer */ + private byte buf[]; + /* size of data (excluding footers and headers) already stored in buf */ + private int size; + /* current index in buf (i.e. buf[count] */ + private int count; + /* number of bytes to be filled up to complete a data chunk + * currently being built */ + private int spaceInCurrentChunk; + + /* underlying stream */ + private PrintStream out; + + /* the chunk size we use */ + private int preferredChunkDataSize; + private int preferedHeaderSize; + private int preferredChunkGrossSize; + /* header for a complete Chunk */ + private byte[] completeHeader; + + /* return the size of the header for a particular chunk size */ + private static int getHeaderSize(int size) { + return (Integer.toHexString(size)).length() + CRLF_SIZE; + } + + /* return a header for a particular chunk size */ + private static byte[] getHeader(int size){ + try { + String hexStr = Integer.toHexString(size); + byte[] hexBytes = hexStr.getBytes("US-ASCII"); + byte[] header = new byte[getHeaderSize(size)]; + for (int i=0; i 0) { + int adjusted_size = size - getHeaderSize(size) - FOOTER_SIZE; + if (getHeaderSize(adjusted_size+1) < getHeaderSize(size)){ + adjusted_size++; + } + size = adjusted_size; + } + + if (size > 0) { + preferredChunkDataSize = size; + } else { + preferredChunkDataSize = DEFAULT_CHUNK_SIZE - + getHeaderSize(DEFAULT_CHUNK_SIZE) - FOOTER_SIZE; + } + + preferedHeaderSize = getHeaderSize(preferredChunkDataSize); + preferredChunkGrossSize = preferedHeaderSize + preferredChunkDataSize + + FOOTER_SIZE; + completeHeader = getHeader(preferredChunkDataSize); + + /* start with an initial buffer */ + buf = new byte[preferredChunkGrossSize]; + reset(); + } + + /* + * Flush a buffered, completed chunk to an underlying stream. If the data in + * the buffer is insufficient to build up a chunk of "preferredChunkSize" + * then the data do not get flushed unless flushAll is true. If flushAll is + * true then the remaining data builds up a last chunk which size is smaller + * than preferredChunkSize, and then the last chunk gets flushed to + * underlying stream. If flushAll is true and there is no data in a buffer + * at all then an empty chunk (containing a header only) gets flushed to + * underlying stream. + */ + private void flush(boolean flushAll) { + if (spaceInCurrentChunk == 0) { + /* flush a completed chunk to underlying stream */ + out.write(buf, 0, preferredChunkGrossSize); + out.flush(); + reset(); + } else if (flushAll){ + /* complete the last chunk and flush it to underlying stream */ + if (size > 0){ + /* adjust a header start index in case the header of the last + * chunk is shorter then preferedHeaderSize */ + + int adjustedHeaderStartIndex = preferedHeaderSize - + getHeaderSize(size); + + /* write header */ + System.arraycopy(getHeader(size), 0, buf, + adjustedHeaderStartIndex, getHeaderSize(size)); + + /* write footer */ + buf[count++] = FOOTER[0]; + buf[count++] = FOOTER[1]; + + //send the last chunk to underlying stream + out.write(buf, adjustedHeaderStartIndex, count - adjustedHeaderStartIndex); + } else { + //send an empty chunk (containing just a header) to underlying stream + out.write(EMPTY_CHUNK_HEADER, 0, EMPTY_CHUNK_HEADER_SIZE); + } + + out.flush(); + reset(); + } + } + + @Override + public boolean checkError() { + return out.checkError(); + } + + /* Check that the output stream is still open */ + private void ensureOpen() { + if (out == null) + setError(); + } + + /* + * Writes data from b[] to an internal buffer and stores the data as data + * chunks of a following format: {Data length in Hex}{CRLF}{data}{CRLF} + * The size of the data is preferredChunkSize. As soon as a completed chunk + * is read from b[] a process of reading from b[] suspends, the chunk gets + * flushed to the underlying stream and then the reading process from b[] + * continues. When there is no more sufficient data in b[] to build up a + * chunk of preferredChunkSize size the data get stored as an incomplete + * chunk of a following format: {space for data length}{CRLF}{data} + * The size of the data is of course smaller than preferredChunkSize. + */ + @Override + public synchronized void write(byte b[], int off, int len) { + ensureOpen(); + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + + /* if b[] contains enough data then one loop cycle creates one complete + * data chunk with a header, body and a footer, and then flushes the + * chunk to the underlying stream. Otherwise, the last loop cycle + * creates incomplete data chunk with empty header and with no footer + * and stores this incomplete chunk in an internal buffer buf[] + */ + int bytesToWrite = len; + int inputIndex = off; /* the index of the byte[] currently being written */ + + do { + /* enough data to complete a chunk */ + if (bytesToWrite >= spaceInCurrentChunk) { + + /* header */ + for (int i=0; i 0); + } + + @Override + public synchronized void write(int _b) { + byte b[] = {(byte)_b}; + write(b, 0, 1); + } + + public synchronized void reset() { + count = preferedHeaderSize; + size = 0; + spaceInCurrentChunk = preferredChunkDataSize; + } + + public int size() { + return size; + } + + @Override + public synchronized void close() { + ensureOpen(); + + /* if we have buffer a chunked send it */ + if (size > 0) { + flush(true); + } + + /* send a zero length chunk */ + flush(true); + + /* don't close the underlying stream */ + out = null; + } + + @Override + public synchronized void flush() { + ensureOpen(); + if (size > 0) { + flush(true); + } + } +} diff --git a/src/sun/net/www/http/HttpCapture.java b/src/sun/net/www/http/HttpCapture.java new file mode 100644 index 00000000..7035295a --- /dev/null +++ b/src/sun/net/www/http/HttpCapture.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.*; +import java.util.ArrayList; +import java.util.regex.*; +import sun.net.NetProperties; +import sun.util.logging.PlatformLogger; + +/** + * Main class of the HTTP traffic capture tool. + * Captures are triggered by the sun.net.http.captureRules system property. + * If set, it should point to a file containing the capture rules. + * Format for the file is simple: + * - 1 rule per line + * - Lines starting with a # are considered comments and ignored + * - a rule is a pair of a regular expression and file pattern, separated by a comma + * - The regular expression is applied to URLs, if it matches, the traffic for + * that URL will be captured in the associated file. + * - if the file name contains a '%d', then that sequence will be replaced by a + * unique random number for each URL. This allow for multi-threaded captures + * of URLs matching the same pattern. + * - Rules are checked in sequence, in the same order as in the file, until a + * match is found or the end of the list is reached. + * + * Examples of rules: + * www\.sun\.com , sun%d.log + * yahoo\.com\/.*asf , yahoo.log + * + * @author jccollet + */ +public class HttpCapture { + private File file = null; + private boolean incoming = true; + private BufferedWriter out = null; + private static boolean initialized = false; + private static volatile ArrayList patterns = null; + private static volatile ArrayList capFiles = null; + + private static synchronized void init() { + initialized = true; + String rulesFile = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + return NetProperties.get("sun.net.http.captureRules"); + } + }); + if (rulesFile != null && !rulesFile.isEmpty()) { + BufferedReader in; + try { + in = new BufferedReader(new FileReader(rulesFile)); + } catch (FileNotFoundException ex) { + return; + } + try { + String line = in.readLine(); + while (line != null) { + line = line.trim(); + if (!line.startsWith("#")) { + // skip line if it's a comment + String[] s = line.split(","); + if (s.length == 2) { + if (patterns == null) { + patterns = new ArrayList(); + capFiles = new ArrayList(); + } + patterns.add(Pattern.compile(s[0].trim())); + capFiles.add(s[1].trim()); + } + } + line = in.readLine(); + } + } catch (IOException ioe) { + + } finally { + try { + in.close(); + } catch (IOException ex) { + } + } + } + } + + private static synchronized boolean isInitialized() { + return initialized; + } + + private HttpCapture(File f, java.net.URL url) { + file = f; + try { + out = new BufferedWriter(new FileWriter(file, true)); + out.write("URL: " + url + "\n"); + } catch (IOException ex) { + PlatformLogger.getLogger(HttpCapture.class.getName()).severe(null, ex); + } + } + + public synchronized void sent(int c) throws IOException { + if (incoming) { + out.write("\n------>\n"); + incoming = false; + out.flush(); + } + out.write(c); + } + + public synchronized void received(int c) throws IOException { + if (!incoming) { + out.write("\n<------\n"); + incoming = true; + out.flush(); + } + out.write(c); + } + + public synchronized void flush() throws IOException { + out.flush(); + } + + public static HttpCapture getCapture(java.net.URL url) { + if (!isInitialized()) { + init(); + } + if (patterns == null || patterns.isEmpty()) { + return null; + } + String s = url.toString(); + for (int i = 0; i < patterns.size(); i++) { + Pattern p = patterns.get(i); + if (p.matcher(s).find()) { + String f = capFiles.get(i); + File fi; + if (f.indexOf("%d") >= 0) { + java.util.Random rand = new java.util.Random(); + do { + String f2 = f.replace("%d", Integer.toString(rand.nextInt())); + fi = new File(f2); + } while (fi.exists()); + } else { + fi = new File(f); + } + return new HttpCapture(fi, url); + } + } + return null; + } +} diff --git a/src/sun/net/www/http/HttpCaptureInputStream.java b/src/sun/net/www/http/HttpCaptureInputStream.java new file mode 100644 index 00000000..ff0edf54 --- /dev/null +++ b/src/sun/net/www/http/HttpCaptureInputStream.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; +import java.io.*; + +/** + * A Simple FilterInputStream subclass to capture HTTP traffic. + * Every byte read is also passed to the HttpCapture class. + * + * @author jccollet + */ +public class HttpCaptureInputStream extends FilterInputStream { + private HttpCapture capture = null; + + public HttpCaptureInputStream(InputStream in, HttpCapture cap) { + super(in); + capture = cap; + } + + @Override + public int read() throws IOException { + int i = super.read(); + capture.received(i); + return i; + } + + @Override + public void close() throws IOException { + try { + capture.flush(); + } catch (IOException iOException) { + } + super.close(); + } + + @Override + public int read(byte[] b) throws IOException { + int ret = super.read(b); + for (int i = 0; i < ret; i++) { + capture.received(b[i]); + } + return ret; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int ret = super.read(b, off, len); + for (int i = 0; i < ret; i++) { + capture.received(b[off+i]); + } + return ret; + } +} diff --git a/src/sun/net/www/http/HttpCaptureOutputStream.java b/src/sun/net/www/http/HttpCaptureOutputStream.java new file mode 100644 index 00000000..e402c1c2 --- /dev/null +++ b/src/sun/net/www/http/HttpCaptureOutputStream.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; +import java.io.*; + +/** + * A Simple FilterOutputStream subclass to capture HTTP traffic. + * Every byte written is also passed to the HttpCapture class. + * + * @author jccollet + */ +public class HttpCaptureOutputStream extends FilterOutputStream { + private HttpCapture capture = null; + + public HttpCaptureOutputStream(OutputStream out, HttpCapture cap) { + super(out); + capture = cap; + } + + @Override + public void write(int b) throws IOException { + capture.sent(b); + out.write(b); + } + + @Override + public void write(byte[] ba) throws IOException { + for (byte b : ba) { + capture.sent(b); + } + out.write(ba); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + for (int i = off; i < len; i++) { + capture.sent(b[i]); + } + out.write(b, off, len); + } + + @Override + public void flush() throws IOException { + try { + capture.flush(); + } catch (IOException iOException) { + } + super.flush(); + } +} diff --git a/src/sun/net/www/http/HttpClient.java b/src/sun/net/www/http/HttpClient.java new file mode 100644 index 00000000..287efa6d --- /dev/null +++ b/src/sun/net/www/http/HttpClient.java @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.*; +import java.net.*; +import java.util.Locale; +import sun.net.NetworkClient; +import sun.net.ProgressSource; +import sun.net.www.MessageHeader; +import sun.net.www.HeaderParser; +import sun.net.www.MeteredStream; +import sun.net.www.ParseUtil; +import sun.net.www.protocol.http.HttpURLConnection; +import sun.util.logging.PlatformLogger; +import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*; + +/** + * @author Herb Jellinek + * @author Dave Brown + */ +public class HttpClient extends NetworkClient { + // whether this httpclient comes from the cache + protected boolean cachedHttpClient = false; + + protected boolean inCache; + + // Http requests we send + MessageHeader requests; + + // Http data we send with the headers + PosterOutputStream poster = null; + + // true if we are in streaming mode (fixed length or chunked) + boolean streaming; + + // if we've had one io error + boolean failedOnce = false; + + /** Response code for CONTINUE */ + private boolean ignoreContinue = true; + private static final int HTTP_CONTINUE = 100; + + /** Default port number for http daemons. REMIND: make these private */ + static final int httpPortNumber = 80; + + /** return default port number (subclasses may override) */ + protected int getDefaultPort () { return httpPortNumber; } + + static private int getDefaultPort(String proto) { + if ("http".equalsIgnoreCase(proto)) + return 80; + if ("https".equalsIgnoreCase(proto)) + return 443; + return -1; + } + + /* All proxying (generic as well as instance-specific) may be + * disabled through use of this flag + */ + protected boolean proxyDisabled; + + // are we using proxy in this instance? + public boolean usingProxy = false; + // target host, port for the URL + protected String host; + protected int port; + + /* where we cache currently open, persistent connections */ + protected static KeepAliveCache kac = new KeepAliveCache(); + + private static boolean keepAliveProp = true; + + // retryPostProp is true by default so as to preserve behavior + // from previous releases. + private static boolean retryPostProp = true; + + volatile boolean keepingAlive = false; /* this is a keep-alive connection */ + int keepAliveConnections = -1; /* number of keep-alives left */ + + /**Idle timeout value, in milliseconds. Zero means infinity, + * iff keepingAlive=true. + * Unfortunately, we can't always believe this one. If I'm connected + * through a Netscape proxy to a server that sent me a keep-alive + * time of 15 sec, the proxy unilaterally terminates my connection + * after 5 sec. So we have to hard code our effective timeout to + * 4 sec for the case where we're using a proxy. *SIGH* + */ + int keepAliveTimeout = 0; + + /** whether the response is to be cached */ + private CacheRequest cacheRequest = null; + + /** Url being fetched. */ + protected URL url; + + /* if set, the client will be reused and must not be put in cache */ + public boolean reuse = false; + + // Traffic capture tool, if configured. See HttpCapture class for info + private HttpCapture capture = null; + + private static final PlatformLogger logger = HttpURLConnection.getHttpLogger(); + private static void logFinest(String msg) { + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest(msg); + } + } + + /** + * A NOP method kept for backwards binary compatibility + * @deprecated -- system properties are no longer cached. + */ + @Deprecated + public static synchronized void resetProperties() { + } + + int getKeepAliveTimeout() { + return keepAliveTimeout; + } + + static { + String keepAlive = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("http.keepAlive")); + + String retryPost = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("sun.net.http.retryPost")); + + if (keepAlive != null) { + keepAliveProp = Boolean.valueOf(keepAlive).booleanValue(); + } else { + keepAliveProp = true; + } + + if (retryPost != null) { + retryPostProp = Boolean.valueOf(retryPost).booleanValue(); + } else + retryPostProp = true; + + } + + /** + * @return true iff http keep alive is set (i.e. enabled). Defaults + * to true if the system property http.keepAlive isn't set. + */ + public boolean getHttpKeepAliveSet() { + return keepAliveProp; + } + + + protected HttpClient() { + } + + private HttpClient(URL url) + throws IOException { + this(url, (String)null, -1, false); + } + + protected HttpClient(URL url, + boolean proxyDisabled) throws IOException { + this(url, null, -1, proxyDisabled); + } + + /* This package-only CTOR should only be used for FTP piggy-backed on HTTP + * HTTP URL's that use this won't take advantage of keep-alive. + * Additionally, this constructor may be used as a last resort when the + * first HttpClient gotten through New() failed (probably b/c of a + * Keep-Alive mismatch). + * + * XXX That documentation is wrong ... it's not package-private any more + */ + public HttpClient(URL url, String proxyHost, int proxyPort) + throws IOException { + this(url, proxyHost, proxyPort, false); + } + + protected HttpClient(URL url, Proxy p, int to) throws IOException { + proxy = (p == null) ? Proxy.NO_PROXY : p; + this.host = url.getHost(); + this.url = url; + port = url.getPort(); + if (port == -1) { + port = getDefaultPort(); + } + setConnectTimeout(to); + + capture = HttpCapture.getCapture(url); + openServer(); + } + + static protected Proxy newHttpProxy(String proxyHost, int proxyPort, + String proto) { + if (proxyHost == null || proto == null) + return Proxy.NO_PROXY; + int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort; + InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport); + return new Proxy(Proxy.Type.HTTP, saddr); + } + + /* + * This constructor gives "ultimate" flexibility, including the ability + * to bypass implicit proxying. Sometimes we need to be using tunneling + * (transport or network level) instead of proxying (application level), + * for example when we don't want the application level data to become + * visible to third parties. + * + * @param url the URL to which we're connecting + * @param proxy proxy to use for this URL (e.g. forwarding) + * @param proxyPort proxy port to use for this URL + * @param proxyDisabled true to disable default proxying + */ + private HttpClient(URL url, String proxyHost, int proxyPort, + boolean proxyDisabled) + throws IOException { + this(url, proxyDisabled ? Proxy.NO_PROXY : + newHttpProxy(proxyHost, proxyPort, "http"), -1); + } + + public HttpClient(URL url, String proxyHost, int proxyPort, + boolean proxyDisabled, int to) + throws IOException { + this(url, proxyDisabled ? Proxy.NO_PROXY : + newHttpProxy(proxyHost, proxyPort, "http"), to); + } + + /* This class has no public constructor for HTTP. This method is used to + * get an HttpClient to the specified URL. If there's currently an + * active HttpClient to that server/port, you'll get that one. + */ + public static HttpClient New(URL url) + throws IOException { + return HttpClient.New(url, Proxy.NO_PROXY, -1, true, null); + } + + public static HttpClient New(URL url, boolean useCache) + throws IOException { + return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache, null); + } + + public static HttpClient New(URL url, Proxy p, int to, boolean useCache, + HttpURLConnection httpuc) throws IOException + { + if (p == null) { + p = Proxy.NO_PROXY; + } + HttpClient ret = null; + /* see if one's already around */ + if (useCache) { + ret = kac.get(url, null); + if (ret != null && httpuc != null && + httpuc.streaming() && + httpuc.getRequestMethod() == "POST") { + if (!ret.available()) { + ret.inCache = false; + ret.closeServer(); + ret = null; + } + } + + if (ret != null) { + if ((ret.proxy != null && ret.proxy.equals(p)) || + (ret.proxy == null && p == null)) { + synchronized (ret) { + ret.cachedHttpClient = true; + assert ret.inCache; + ret.inCache = false; + if (httpuc != null && ret.needsTunneling()) + httpuc.setTunnelState(TUNNELING); + logFinest("KeepAlive stream retrieved from the cache, " + ret); + } + } else { + // We cannot return this connection to the cache as it's + // KeepAliveTimeout will get reset. We simply close the connection. + // This should be fine as it is very rare that a connection + // to the same host will not use the same proxy. + synchronized(ret) { + ret.inCache = false; + ret.closeServer(); + } + ret = null; + } + } + } + if (ret == null) { + ret = new HttpClient(url, p, to); + } else { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) { + security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort()); + } else { + security.checkConnect(url.getHost(), url.getPort()); + } + } + ret.url = url; + } + return ret; + } + + public static HttpClient New(URL url, Proxy p, int to, + HttpURLConnection httpuc) throws IOException + { + return New(url, p, to, true, httpuc); + } + + public static HttpClient New(URL url, String proxyHost, int proxyPort, + boolean useCache) + throws IOException { + return New(url, newHttpProxy(proxyHost, proxyPort, "http"), + -1, useCache, null); + } + + public static HttpClient New(URL url, String proxyHost, int proxyPort, + boolean useCache, int to, + HttpURLConnection httpuc) + throws IOException { + return New(url, newHttpProxy(proxyHost, proxyPort, "http"), + to, useCache, httpuc); + } + + /* return it to the cache as still usable, if: + * 1) It's keeping alive, AND + * 2) It still has some connections left, AND + * 3) It hasn't had a error (PrintStream.checkError()) + * 4) It hasn't timed out + * + * If this client is not keepingAlive, it should have been + * removed from the cache in the parseHeaders() method. + */ + + public void finished() { + if (reuse) /* will be reused */ + return; + keepAliveConnections--; + poster = null; + if (keepAliveConnections > 0 && isKeepingAlive() && + !(serverOutput.checkError())) { + /* This connection is keepingAlive && still valid. + * Return it to the cache. + */ + putInKeepAliveCache(); + } else { + closeServer(); + } + } + + protected synchronized boolean available() { + boolean available = true; + int old = -1; + + try { + try { + old = serverSocket.getSoTimeout(); + serverSocket.setSoTimeout(1); + BufferedInputStream tmpbuf = + new BufferedInputStream(serverSocket.getInputStream()); + int r = tmpbuf.read(); + if (r == -1) { + logFinest("HttpClient.available(): " + + "read returned -1: not available"); + available = false; + } + } catch (SocketTimeoutException e) { + logFinest("HttpClient.available(): " + + "SocketTimeout: its available"); + } finally { + if (old != -1) + serverSocket.setSoTimeout(old); + } + } catch (IOException e) { + logFinest("HttpClient.available(): " + + "SocketException: not available"); + available = false; + } + return available; + } + + protected synchronized void putInKeepAliveCache() { + if (inCache) { + assert false : "Duplicate put to keep alive cache"; + return; + } + inCache = true; + kac.put(url, null, this); + } + + protected synchronized boolean isInKeepAliveCache() { + return inCache; + } + + /* + * Close an idle connection to this URL (if it exists in the + * cache). + */ + public void closeIdleConnection() { + HttpClient http = kac.get(url, null); + if (http != null) { + http.closeServer(); + } + } + + /* We're very particular here about what our InputStream to the server + * looks like for reasons that are apparent if you can decipher the + * method parseHTTP(). That's why this method is overidden from the + * superclass. + */ + @Override + public void openServer(String server, int port) throws IOException { + serverSocket = doConnect(server, port); + try { + OutputStream out = serverSocket.getOutputStream(); + if (capture != null) { + out = new HttpCaptureOutputStream(out, capture); + } + serverOutput = new PrintStream( + new BufferedOutputStream(out), + false, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding+" encoding not found", e); + } + serverSocket.setTcpNoDelay(true); + } + + /* + * Returns true if the http request should be tunneled through proxy. + * An example where this is the case is Https. + */ + public boolean needsTunneling() { + return false; + } + + /* + * Returns true if this httpclient is from cache + */ + public synchronized boolean isCachedConnection() { + return cachedHttpClient; + } + + /* + * Finish any work left after the socket connection is + * established. In the normal http case, it's a NO-OP. Subclass + * may need to override this. An example is Https, where for + * direct connection to the origin server, ssl handshake needs to + * be done; for proxy tunneling, the socket needs to be converted + * into an SSL socket before ssl handshake can take place. + */ + public void afterConnect() throws IOException, UnknownHostException { + // NO-OP. Needs to be overwritten by HttpsClient + } + + /* + * call openServer in a privileged block + */ + private synchronized void privilegedOpenServer(final InetSocketAddress server) + throws IOException + { + try { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedExceptionAction() { + public Void run() throws IOException { + openServer(server.getHostString(), server.getPort()); + return null; + } + }); + } catch (java.security.PrivilegedActionException pae) { + throw (IOException) pae.getException(); + } + } + + /* + * call super.openServer + */ + private void superOpenServer(final String proxyHost, + final int proxyPort) + throws IOException, UnknownHostException + { + super.openServer(proxyHost, proxyPort); + } + + /* + */ + protected synchronized void openServer() throws IOException { + + SecurityManager security = System.getSecurityManager(); + + if (security != null) { + security.checkConnect(host, port); + } + + if (keepingAlive) { // already opened + return; + } + + if (url.getProtocol().equals("http") || + url.getProtocol().equals("https") ) { + + if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) { + sun.net.www.URLConnection.setProxiedHost(host); + privilegedOpenServer((InetSocketAddress) proxy.address()); + usingProxy = true; + return; + } else { + // make direct connection + openServer(host, port); + usingProxy = false; + return; + } + + } else { + /* we're opening some other kind of url, most likely an + * ftp url. + */ + if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) { + sun.net.www.URLConnection.setProxiedHost(host); + privilegedOpenServer((InetSocketAddress) proxy.address()); + usingProxy = true; + return; + } else { + // make direct connection + super.openServer(host, port); + usingProxy = false; + return; + } + } + } + + public String getURLFile() throws IOException { + + String fileName; + + /** + * proxyDisabled is set by subclass HttpsClient! + */ + if (usingProxy && !proxyDisabled) { + // Do not use URLStreamHandler.toExternalForm as the fragment + // should not be part of the RequestURI. It should be an + // absolute URI which does not have a fragment part. + StringBuffer result = new StringBuffer(128); + result.append(url.getProtocol()); + result.append(":"); + if (url.getAuthority() != null && url.getAuthority().length() > 0) { + result.append("//"); + result.append(url.getAuthority()); + } + if (url.getPath() != null) { + result.append(url.getPath()); + } + if (url.getQuery() != null) { + result.append('?'); + result.append(url.getQuery()); + } + + fileName = result.toString(); + } else { + fileName = url.getFile(); + + if ((fileName == null) || (fileName.length() == 0)) { + fileName = "/"; + } else if (fileName.charAt(0) == '?') { + /* HTTP/1.1 spec says in 5.1.2. about Request-URI: + * "Note that the absolute path cannot be empty; if + * none is present in the original URI, it MUST be + * given as "/" (the server root)." So if the file + * name here has only a query string, the path is + * empty and we also have to add a "/". + */ + fileName = "/" + fileName; + } + } + + if (fileName.indexOf('\n') == -1) + return fileName; + else + throw new MalformedURLException("Illegal character in URL"); + } + + /** + * @deprecated + */ + @Deprecated + public void writeRequests(MessageHeader head) { + requests = head; + requests.print(serverOutput); + serverOutput.flush(); + } + + public void writeRequests(MessageHeader head, + PosterOutputStream pos) throws IOException { + requests = head; + requests.print(serverOutput); + poster = pos; + if (poster != null) + poster.writeTo(serverOutput); + serverOutput.flush(); + } + + public void writeRequests(MessageHeader head, + PosterOutputStream pos, + boolean streaming) throws IOException { + this.streaming = streaming; + writeRequests(head, pos); + } + + /** Parse the first line of the HTTP request. It usually looks + something like: "HTTP/1.0 comment\r\n". */ + + public boolean parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc) + throws IOException { + /* If "HTTP/*" is found in the beginning, return true. Let + * HttpURLConnection parse the mime header itself. + * + * If this isn't valid HTTP, then we don't try to parse a header + * out of the beginning of the response into the responses, + * and instead just queue up the output stream to it's very beginning. + * This seems most reasonable, and is what the NN browser does. + */ + + try { + serverInput = serverSocket.getInputStream(); + if (capture != null) { + serverInput = new HttpCaptureInputStream(serverInput, capture); + } + serverInput = new BufferedInputStream(serverInput); + return (parseHTTPHeader(responses, pi, httpuc)); + } catch (SocketTimeoutException stex) { + // We don't want to retry the request when the app. sets a timeout + // but don't close the server if timeout while waiting for 100-continue + if (ignoreContinue) { + closeServer(); + } + throw stex; + } catch (IOException e) { + closeServer(); + cachedHttpClient = false; + if (!failedOnce && requests != null) { + failedOnce = true; + if (getRequestMethod().equals("CONNECT") + || streaming + || (httpuc.getRequestMethod().equals("POST") + && !retryPostProp)) { + // do not retry the request + } else { + // try once more + openServer(); + if (needsTunneling()) { + MessageHeader origRequests = requests; + httpuc.doTunneling(); + requests = origRequests; + } + afterConnect(); + writeRequests(requests, poster); + return parseHTTP(responses, pi, httpuc); + } + } + throw e; + } + + } + + private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc) + throws IOException { + /* If "HTTP/*" is found in the beginning, return true. Let + * HttpURLConnection parse the mime header itself. + * + * If this isn't valid HTTP, then we don't try to parse a header + * out of the beginning of the response into the responses, + * and instead just queue up the output stream to it's very beginning. + * This seems most reasonable, and is what the NN browser does. + */ + + keepAliveConnections = -1; + keepAliveTimeout = 0; + + boolean ret = false; + byte[] b = new byte[8]; + + try { + int nread = 0; + serverInput.mark(10); + while (nread < 8) { + int r = serverInput.read(b, nread, 8 - nread); + if (r < 0) { + break; + } + nread += r; + } + String keep=null; + ret = b[0] == 'H' && b[1] == 'T' + && b[2] == 'T' && b[3] == 'P' && b[4] == '/' && + b[5] == '1' && b[6] == '.'; + serverInput.reset(); + if (ret) { // is valid HTTP - response started w/ "HTTP/1." + responses.parseHeader(serverInput); + + // we've finished parsing http headers + // check if there are any applicable cookies to set (in cache) + CookieHandler cookieHandler = httpuc.getCookieHandler(); + if (cookieHandler != null) { + URI uri = ParseUtil.toURI(url); + // NOTE: That cast from Map shouldn't be necessary but + // a bug in javac is triggered under certain circumstances + // So we do put the cast in as a workaround until + // it is resolved. + if (uri != null) + cookieHandler.put(uri, responses.getHeaders()); + } + + /* decide if we're keeping alive: + * This is a bit tricky. There's a spec, but most current + * servers (10/1/96) that support this differ in dialects. + * If the server/client misunderstand each other, the + * protocol should fall back onto HTTP/1.0, no keep-alive. + */ + if (usingProxy) { // not likely a proxy will return this + keep = responses.findValue("Proxy-Connection"); + } + if (keep == null) { + keep = responses.findValue("Connection"); + } + if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) { + /* some servers, notably Apache1.1, send something like: + * "Keep-Alive: timeout=15, max=1" which we should respect. + */ + HeaderParser p = new HeaderParser( + responses.findValue("Keep-Alive")); + if (p != null) { + /* default should be larger in case of proxy */ + keepAliveConnections = p.findInt("max", usingProxy?50:5); + keepAliveTimeout = p.findInt("timeout", usingProxy?60:5); + } + } else if (b[7] != '0') { + /* + * We're talking 1.1 or later. Keep persistent until + * the server says to close. + */ + if (keep != null) { + /* + * The only Connection token we understand is close. + * Paranoia: if there is any Connection header then + * treat as non-persistent. + */ + keepAliveConnections = 1; + } else { + keepAliveConnections = 5; + } + } + } else if (nread != 8) { + if (!failedOnce && requests != null) { + failedOnce = true; + if (getRequestMethod().equals("CONNECT") + || streaming + || (httpuc.getRequestMethod().equals("POST") + && !retryPostProp)) { + // do not retry the request + } else { + closeServer(); + cachedHttpClient = false; + openServer(); + if (needsTunneling()) { + MessageHeader origRequests = requests; + httpuc.doTunneling(); + requests = origRequests; + } + afterConnect(); + writeRequests(requests, poster); + return parseHTTP(responses, pi, httpuc); + } + } + throw new SocketException("Unexpected end of file from server"); + } else { + // we can't vouche for what this is.... + responses.set("Content-type", "unknown/unknown"); + } + } catch (IOException e) { + throw e; + } + + int code = -1; + try { + String resp; + resp = responses.getValue(0); + /* should have no leading/trailing LWS + * expedite the typical case by assuming it has + * form "HTTP/1.x 2XX " + */ + int ind; + ind = resp.indexOf(' '); + while(resp.charAt(ind) == ' ') + ind++; + code = Integer.parseInt(resp.substring(ind, ind + 3)); + } catch (Exception e) {} + + if (code == HTTP_CONTINUE && ignoreContinue) { + responses.reset(); + return parseHTTPHeader(responses, pi, httpuc); + } + + long cl = -1; + + /* + * Set things up to parse the entity body of the reply. + * We should be smarter about avoid pointless work when + * the HTTP method and response code indicate there will be + * no entity body to parse. + */ + String te = responses.findValue("Transfer-Encoding"); + if (te != null && te.equalsIgnoreCase("chunked")) { + serverInput = new ChunkedInputStream(serverInput, this, responses); + + /* + * If keep alive not specified then close after the stream + * has completed. + */ + if (keepAliveConnections <= 1) { + keepAliveConnections = 1; + keepingAlive = false; + } else { + keepingAlive = true; + } + failedOnce = false; + } else { + + /* + * If it's a keep alive connection then we will keep + * (alive if :- + * 1. content-length is specified, or + * 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that + * 204 or 304 response must not include a message body. + */ + String cls = responses.findValue("content-length"); + if (cls != null) { + try { + cl = Long.parseLong(cls); + } catch (NumberFormatException e) { + cl = -1; + } + } + String requestLine = requests.getKey(0); + + if ((requestLine != null && + (requestLine.startsWith("HEAD"))) || + code == HttpURLConnection.HTTP_NOT_MODIFIED || + code == HttpURLConnection.HTTP_NO_CONTENT) { + cl = 0; + } + + if (keepAliveConnections > 1 && + (cl >= 0 || + code == HttpURLConnection.HTTP_NOT_MODIFIED || + code == HttpURLConnection.HTTP_NO_CONTENT)) { + keepingAlive = true; + failedOnce = false; + } else if (keepingAlive) { + /* Previously we were keeping alive, and now we're not. Remove + * this from the cache (but only here, once) - otherwise we get + * multiple removes and the cache count gets messed up. + */ + keepingAlive=false; + } + } + + /* wrap a KeepAliveStream/MeteredStream around it if appropriate */ + + if (cl > 0) { + // In this case, content length is well known, so it is okay + // to wrap the input stream with KeepAliveStream/MeteredStream. + + if (pi != null) { + // Progress monitor is enabled + pi.setContentType(responses.findValue("content-type")); + } + + if (isKeepingAlive()) { + // Wrap KeepAliveStream if keep alive is enabled. + logFinest("KeepAlive stream used: " + url); + serverInput = new KeepAliveStream(serverInput, pi, cl, this); + failedOnce = false; + } + else { + serverInput = new MeteredStream(serverInput, pi, cl); + } + } + else if (cl == -1) { + // In this case, content length is unknown - the input + // stream would simply be a regular InputStream or + // ChunkedInputStream. + + if (pi != null) { + // Progress monitoring is enabled. + + pi.setContentType(responses.findValue("content-type")); + + // Wrap MeteredStream for tracking indeterministic + // progress, even if the input stream is ChunkedInputStream. + serverInput = new MeteredStream(serverInput, pi, cl); + } + else { + // Progress monitoring is disabled, and there is no + // need to wrap an unknown length input stream. + + // ** This is an no-op ** + } + } + else { + if (pi != null) + pi.finishTracking(); + } + + return ret; + } + + public synchronized InputStream getInputStream() { + return serverInput; + } + + public OutputStream getOutputStream() { + return serverOutput; + } + + @Override + public String toString() { + return getClass().getName()+"("+url+")"; + } + + public final boolean isKeepingAlive() { + return getHttpKeepAliveSet() && keepingAlive; + } + + public void setCacheRequest(CacheRequest cacheRequest) { + this.cacheRequest = cacheRequest; + } + + CacheRequest getCacheRequest() { + return cacheRequest; + } + + String getRequestMethod() { + if (requests != null) { + String requestLine = requests.getKey(0); + if (requestLine != null) { + return requestLine.split("\\s+")[0]; + } + } + return ""; + } + + @Override + protected void finalize() throws Throwable { + // This should do nothing. The stream finalizer will + // close the fd. + } + + public void setDoNotRetry(boolean value) { + // failedOnce is used to determine if a request should be retried. + failedOnce = value; + } + + public void setIgnoreContinue(boolean value) { + ignoreContinue = value; + } + + /* Use only on connections in error. */ + @Override + public void closeServer() { + try { + keepingAlive = false; + serverSocket.close(); + } catch (Exception e) {} + } + + /** + * @return the proxy host being used for this client, or null + * if we're not going through a proxy + */ + public String getProxyHostUsed() { + if (!usingProxy) { + return null; + } else { + return ((InetSocketAddress)proxy.address()).getHostString(); + } + } + + /** + * @return the proxy port being used for this client. Meaningless + * if getProxyHostUsed() gives null. + */ + public int getProxyPortUsed() { + if (usingProxy) + return ((InetSocketAddress)proxy.address()).getPort(); + return -1; + } +} diff --git a/src/sun/net/www/http/Hurryable.java b/src/sun/net/www/http/Hurryable.java new file mode 100644 index 00000000..49a74fc6 --- /dev/null +++ b/src/sun/net/www/http/Hurryable.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.www.http; + +/** + * A Hurryable is a class that has been instructed to complete + * its input processing so as to make resource associated with that input + * available to others. + */ +public interface Hurryable { + + /** + * @return a boolean indicating if the stream has been + * hurried or not. + */ + boolean hurry(); + +} diff --git a/src/sun/net/www/http/KeepAliveCache.java b/src/sun/net/www/http/KeepAliveCache.java new file mode 100644 index 00000000..16f02d7c --- /dev/null +++ b/src/sun/net/www/http/KeepAliveCache.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.util.ArrayList; +import java.util.HashMap; +import java.net.URL; + +/** + * A class that implements a cache of idle Http connections for keep-alive + * + * @author Stephen R. Pietrowicz (NCSA) + * @author Dave Brown + */ +public class KeepAliveCache + extends HashMap + implements Runnable { + private static final long serialVersionUID = -2937172892064557949L; + + /* maximum # keep-alive connections to maintain at once + * This should be 2 by the HTTP spec, but because we don't support pipe-lining + * a larger value is more appropriate. So we now set a default of 5, and the value + * refers to the number of idle connections per destination (in the cache) only. + * It can be reset by setting system property "http.maxConnections". + */ + static final int MAX_CONNECTIONS = 5; + static int result = -1; + static int getMaxConnections() { + if (result == -1) { + result = java.security.AccessController.doPrivileged( + new sun.security.action.GetIntegerAction("http.maxConnections", + MAX_CONNECTIONS)) + .intValue(); + if (result <= 0) + result = MAX_CONNECTIONS; + } + return result; + } + + static final int LIFETIME = 5000; + + private Thread keepAliveTimer = null; + + /** + * Constructor + */ + public KeepAliveCache() {} + + /** + * Register this URL and HttpClient (that supports keep-alive) with the cache + * @param url The URL contains info about the host and port + * @param http The HttpClient to be cached + */ + public synchronized void put(final URL url, Object obj, HttpClient http) { + boolean startThread = (keepAliveTimer == null); + if (!startThread) { + if (!keepAliveTimer.isAlive()) { + startThread = true; + } + } + if (startThread) { + clear(); + /* Unfortunately, we can't always believe the keep-alive timeout we got + * back from the server. If I'm connected through a Netscape proxy + * to a server that sent me a keep-alive + * time of 15 sec, the proxy unilaterally terminates my connection + * The robustness to get around this is in HttpClient.parseHTTP() + */ + final KeepAliveCache cache = this; + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + // We want to create the Keep-Alive-Timer in the + // system threadgroup + ThreadGroup grp = Thread.currentThread().getThreadGroup(); + ThreadGroup parent = null; + while ((parent = grp.getParent()) != null) { + grp = parent; + } + + keepAliveTimer = new Thread(grp, cache, "Keep-Alive-Timer"); + keepAliveTimer.setDaemon(true); + keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); + // Set the context class loader to null in order to avoid + // keeping a strong reference to an application classloader. + keepAliveTimer.setContextClassLoader(null); + keepAliveTimer.start(); + return null; + } + }); + } + + KeepAliveKey key = new KeepAliveKey(url, obj); + ClientVector v = super.get(key); + + if (v == null) { + int keepAliveTimeout = http.getKeepAliveTimeout(); + v = new ClientVector(keepAliveTimeout > 0? + keepAliveTimeout*1000 : LIFETIME); + v.put(http); + super.put(key, v); + } else { + v.put(http); + } + } + + /* remove an obsolete HttpClient from its VectorCache */ + public synchronized void remove (HttpClient h, Object obj) { + KeepAliveKey key = new KeepAliveKey(h.url, obj); + ClientVector v = super.get(key); + if (v != null) { + v.remove(h); + if (v.empty()) { + removeVector(key); + } + } + } + + /* called by a clientVector thread when all its connections have timed out + * and that vector of connections should be removed. + */ + synchronized void removeVector(KeepAliveKey k) { + super.remove(k); + } + + /** + * Check to see if this URL has a cached HttpClient + */ + public synchronized HttpClient get(URL url, Object obj) { + + KeepAliveKey key = new KeepAliveKey(url, obj); + ClientVector v = super.get(key); + if (v == null) { // nothing in cache yet + return null; + } + return v.get(); + } + + /* Sleeps for an alloted timeout, then checks for timed out connections. + * Errs on the side of caution (leave connections idle for a relatively + * short time). + */ + @Override + public void run() { + do { + try { + Thread.sleep(LIFETIME); + } catch (InterruptedException e) {} + synchronized (this) { + /* Remove all unused HttpClients. Starting from the + * bottom of the stack (the least-recently used first). + * REMIND: It'd be nice to not remove *all* connections + * that aren't presently in use. One could have been added + * a second ago that's still perfectly valid, and we're + * needlessly axing it. But it's not clear how to do this + * cleanly, and doing it right may be more trouble than it's + * worth. + */ + + long currentTime = System.currentTimeMillis(); + + ArrayList keysToRemove + = new ArrayList(); + + for (KeepAliveKey key : keySet()) { + ClientVector v = get(key); + synchronized (v) { + int i; + + for (i = 0; i < v.size(); i++) { + KeepAliveEntry e = v.elementAt(i); + if ((currentTime - e.idleStartTime) > v.nap) { + HttpClient h = e.hc; + h.closeServer(); + } else { + break; + } + } + v.subList(0, i).clear(); + + if (v.size() == 0) { + keysToRemove.add(key); + } + } + } + + for (KeepAliveKey key : keysToRemove) { + removeVector(key); + } + } + } while (size() > 0); + + return; + } + + /* + * Do not serialize this class! + */ + private void writeObject(java.io.ObjectOutputStream stream) + throws IOException { + throw new NotSerializableException(); + } + + private void readObject(java.io.ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new NotSerializableException(); + } +} + +/* FILO order for recycling HttpClients, should run in a thread + * to time them out. If > maxConns are in use, block. + */ + + +class ClientVector extends java.util.Stack { + private static final long serialVersionUID = -8680532108106489459L; + + // sleep time in milliseconds, before cache clear + int nap; + + + + ClientVector (int nap) { + this.nap = nap; + } + + synchronized HttpClient get() { + if (empty()) { + return null; + } else { + // Loop until we find a connection that has not timed out + HttpClient hc = null; + long currentTime = System.currentTimeMillis(); + do { + KeepAliveEntry e = pop(); + if ((currentTime - e.idleStartTime) > nap) { + e.hc.closeServer(); + } else { + hc = e.hc; + } + } while ((hc== null) && (!empty())); + return hc; + } + } + + /* return a still valid, unused HttpClient */ + synchronized void put(HttpClient h) { + if (size() >= KeepAliveCache.getMaxConnections()) { + h.closeServer(); // otherwise the connection remains in limbo + } else { + push(new KeepAliveEntry(h, System.currentTimeMillis())); + } + } + + /* + * Do not serialize this class! + */ + private void writeObject(java.io.ObjectOutputStream stream) + throws IOException { + throw new NotSerializableException(); + } + + private void readObject(java.io.ObjectInputStream stream) + throws IOException, ClassNotFoundException { + throw new NotSerializableException(); + } +} + + +class KeepAliveKey { + private String protocol = null; + private String host = null; + private int port = 0; + private Object obj = null; // additional key, such as socketfactory + + /** + * Constructor + * + * @param url the URL containing the protocol, host and port information + */ + public KeepAliveKey(URL url, Object obj) { + this.protocol = url.getProtocol(); + this.host = url.getHost(); + this.port = url.getPort(); + this.obj = obj; + } + + /** + * Determine whether or not two objects of this type are equal + */ + @Override + public boolean equals(Object obj) { + if ((obj instanceof KeepAliveKey) == false) + return false; + KeepAliveKey kae = (KeepAliveKey)obj; + return host.equals(kae.host) + && (port == kae.port) + && protocol.equals(kae.protocol) + && this.obj == kae.obj; + } + + /** + * The hashCode() for this object is the string hashCode() of + * concatenation of the protocol, host name and port. + */ + @Override + public int hashCode() { + String str = protocol+host+port; + return this.obj == null? str.hashCode() : + str.hashCode() + this.obj.hashCode(); + } +} + +class KeepAliveEntry { + HttpClient hc; + long idleStartTime; + + KeepAliveEntry(HttpClient hc, long idleStartTime) { + this.hc = hc; + this.idleStartTime = idleStartTime; + } +} diff --git a/src/sun/net/www/http/KeepAliveCleanerEntry.java b/src/sun/net/www/http/KeepAliveCleanerEntry.java new file mode 100644 index 00000000..521366c2 --- /dev/null +++ b/src/sun/net/www/http/KeepAliveCleanerEntry.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.*; + +class KeepAliveCleanerEntry +{ + KeepAliveStream kas; + HttpClient hc; + + public KeepAliveCleanerEntry(KeepAliveStream kas, HttpClient hc) { + this.kas = kas; + this.hc = hc; + } + + protected KeepAliveStream getKeepAliveStream() { + return kas; + } + + protected HttpClient getHttpClient() { + return hc; + } + + protected void setQueuedForCleanup() { + kas.queuedForCleanup = true; + } + + protected boolean getQueuedForCleanup() { + return kas.queuedForCleanup; + } + +} diff --git a/src/sun/net/www/http/KeepAliveStream.java b/src/sun/net/www/http/KeepAliveStream.java new file mode 100644 index 00000000..9155c4ae --- /dev/null +++ b/src/sun/net/www/http/KeepAliveStream.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.*; +import sun.net.ProgressSource; +import sun.net.www.MeteredStream; + +/** + * A stream that has the property of being able to be kept alive for + * multiple downloads from the same server. + * + * @author Stephen R. Pietrowicz (NCSA) + * @author Dave Brown + */ +public +class KeepAliveStream extends MeteredStream implements Hurryable { + + // instance variables + HttpClient hc; + + boolean hurried; + + // has this KeepAliveStream been put on the queue for asynchronous cleanup. + protected boolean queuedForCleanup = false; + + private static final KeepAliveStreamCleaner queue = new KeepAliveStreamCleaner(); + private static Thread cleanerThread; // null + + /** + * Constructor + */ + public KeepAliveStream(InputStream is, ProgressSource pi, long expected, HttpClient hc) { + super(is, pi, expected); + this.hc = hc; + } + + /** + * Attempt to cache this connection + */ + public void close() throws IOException { + // If the inputstream is closed already, just return. + if (closed) { + return; + } + + // If this stream has already been queued for cleanup. + if (queuedForCleanup) { + return; + } + + // Skip past the data that's left in the Inputstream because + // some sort of error may have occurred. + // Do this ONLY if the skip won't block. The stream may have + // been closed at the beginning of a big file and we don't want + // to hang around for nothing. So if we can't skip without blocking + // we just close the socket and, therefore, terminate the keepAlive + // NOTE: Don't close super class + try { + if (expected > count) { + long nskip = expected - count; + if (nskip <= available()) { + do {} while ((nskip = (expected - count)) > 0L + && skip(Math.min(nskip, available())) > 0L); + } else if (expected <= KeepAliveStreamCleaner.MAX_DATA_REMAINING && !hurried) { + //put this KeepAliveStream on the queue so that the data remaining + //on the socket can be cleanup asyncronously. + queueForCleanup(new KeepAliveCleanerEntry(this, hc)); + } else { + hc.closeServer(); + } + } + if (!closed && !hurried && !queuedForCleanup) { + hc.finished(); + } + } finally { + if (pi != null) + pi.finishTracking(); + + if (!queuedForCleanup) { + // nulling out the underlying inputstream as well as + // httpClient to let gc collect the memories faster + in = null; + hc = null; + closed = true; + } + } + } + + /* we explicitly do not support mark/reset */ + + public boolean markSupported() { + return false; + } + + public void mark(int limit) {} + + public void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + public synchronized boolean hurry() { + try { + /* CASE 0: we're actually already done */ + if (closed || count >= expected) { + return false; + } else if (in.available() < (expected - count)) { + /* CASE I: can't meet the demand */ + return false; + } else { + /* CASE II: fill our internal buffer + * Remind: possibly check memory here + */ + int size = (int) (expected - count); + byte[] buf = new byte[size]; + DataInputStream dis = new DataInputStream(in); + dis.readFully(buf); + in = new ByteArrayInputStream(buf); + hurried = true; + return true; + } + } catch (IOException e) { + // e.printStackTrace(); + return false; + } + } + + private static void queueForCleanup(KeepAliveCleanerEntry kace) { + synchronized(queue) { + if(!kace.getQueuedForCleanup()) { + if (!queue.offer(kace)) { + kace.getHttpClient().closeServer(); + return; + } + + kace.setQueuedForCleanup(); + queue.notifyAll(); + } + + boolean startCleanupThread = (cleanerThread == null); + if (!startCleanupThread) { + if (!cleanerThread.isAlive()) { + startCleanupThread = true; + } + } + + if (startCleanupThread) { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + // We want to create the Keep-Alive-SocketCleaner in the + // system threadgroup + ThreadGroup grp = Thread.currentThread().getThreadGroup(); + ThreadGroup parent = null; + while ((parent = grp.getParent()) != null) { + grp = parent; + } + + cleanerThread = new Thread(grp, queue, "Keep-Alive-SocketCleaner"); + cleanerThread.setDaemon(true); + cleanerThread.setPriority(Thread.MAX_PRIORITY - 2); + // Set the context class loader to null in order to avoid + // keeping a strong reference to an application classloader. + cleanerThread.setContextClassLoader(null); + cleanerThread.start(); + return null; + } + }); + } + } // queue + } + + protected long remainingToRead() { + return expected - count; + } + + protected void setClosed() { + in = null; + hc = null; + closed = true; + } +} diff --git a/src/sun/net/www/http/KeepAliveStreamCleaner.java b/src/sun/net/www/http/KeepAliveStreamCleaner.java new file mode 100644 index 00000000..87f0d09e --- /dev/null +++ b/src/sun/net/www/http/KeepAliveStreamCleaner.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.IOException; +import java.util.LinkedList; +import sun.net.NetProperties; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class is used to cleanup any remaining data that may be on a KeepAliveStream + * so that the connection can be cached in the KeepAliveCache. + * Instances of this class can be used as a FIFO queue for KeepAliveCleanerEntry objects. + * Executing this Runnable removes each KeepAliveCleanerEntry from the Queue, reads + * the reamining bytes on its KeepAliveStream, and if successful puts the connection in + * the KeepAliveCache. + * + * @author Chris Hegarty + */ + +@SuppressWarnings("serial") // never serialized +class KeepAliveStreamCleaner + extends LinkedList + implements Runnable +{ + // maximum amount of remaining data that we will try to cleanup + protected static int MAX_DATA_REMAINING = 512; + + // maximum amount of KeepAliveStreams to be queued + protected static int MAX_CAPACITY = 10; + + // timeout for both socket and poll on the queue + protected static final int TIMEOUT = 5000; + + // max retries for skipping data + private static final int MAX_RETRIES = 5; + + static { + final String maxDataKey = "http.KeepAlive.remainingData"; + int maxData = AccessController.doPrivileged( + new PrivilegedAction() { + public Integer run() { + return NetProperties.getInteger(maxDataKey, MAX_DATA_REMAINING); + }}).intValue() * 1024; + MAX_DATA_REMAINING = maxData; + + final String maxCapacityKey = "http.KeepAlive.queuedConnections"; + int maxCapacity = AccessController.doPrivileged( + new PrivilegedAction() { + public Integer run() { + return NetProperties.getInteger(maxCapacityKey, MAX_CAPACITY); + }}).intValue(); + MAX_CAPACITY = maxCapacity; + + } + + + @Override + public boolean offer(KeepAliveCleanerEntry e) { + if (size() >= MAX_CAPACITY) + return false; + + return super.offer(e); + } + + @Override + public void run() + { + KeepAliveCleanerEntry kace = null; + + do { + try { + synchronized(this) { + long before = System.currentTimeMillis(); + long timeout = TIMEOUT; + while ((kace = poll()) == null) { + this.wait(timeout); + + long after = System.currentTimeMillis(); + long elapsed = after - before; + if (elapsed > timeout) { + /* one last try */ + kace = poll(); + break; + } + before = after; + timeout -= elapsed; + } + } + + if(kace == null) + break; + + KeepAliveStream kas = kace.getKeepAliveStream(); + + if (kas != null) { + synchronized(kas) { + HttpClient hc = kace.getHttpClient(); + try { + if (hc != null && !hc.isInKeepAliveCache()) { + int oldTimeout = hc.getReadTimeout(); + hc.setReadTimeout(TIMEOUT); + long remainingToRead = kas.remainingToRead(); + if (remainingToRead > 0) { + long n = 0; + int retries = 0; + while (n < remainingToRead && retries < MAX_RETRIES) { + remainingToRead = remainingToRead - n; + n = kas.skip(remainingToRead); + if (n == 0) + retries++; + } + remainingToRead = remainingToRead - n; + } + if (remainingToRead == 0) { + hc.setReadTimeout(oldTimeout); + hc.finished(); + } else + hc.closeServer(); + } + } catch (IOException ioe) { + hc.closeServer(); + } finally { + kas.setClosed(); + } + } + } + } catch (InterruptedException ie) { } + } while (kace != null); + } +} diff --git a/src/sun/net/www/http/PosterOutputStream.java b/src/sun/net/www/http/PosterOutputStream.java new file mode 100644 index 00000000..905d574b --- /dev/null +++ b/src/sun/net/www/http/PosterOutputStream.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.http; + +import java.io.*; +import java.net.*; + +/** + * Instances of this class are returned to applications for the purpose of + * sending user data for a HTTP request (excluding TRACE). This class is used + * when the content-length will be specified in the header of the request. + * The semantics of ByteArrayOutputStream are extended so that + * when close() is called, it is no longer possible to write + * additional data to the stream. From this point the content length of + * the request is fixed and cannot change. + * + * @author Michael McMahon + */ + +public class PosterOutputStream extends ByteArrayOutputStream { + + private boolean closed; + + /** + * Creates a new output stream for POST user data + */ + public PosterOutputStream () { + super (256); + } + + /** + * Writes the specified byte to this output stream. + * + * @param b the byte to be written. + */ + public synchronized void write(int b) { + if (closed) { + return; + } + super.write (b); + } + + /** + * Writes len bytes from the specified byte array + * starting at offset off to this output stream. + * + * @param b the data. + * @param off the start offset in the data. + * @param len the number of bytes to write. + */ + public synchronized void write(byte b[], int off, int len) { + if (closed) { + return; + } + super.write (b, off, len); + } + + /** + * Resets the count field of this output + * stream to zero, so that all currently accumulated output in the + * output stream is discarded. The output stream can be used again, + * reusing the already allocated buffer space. If the output stream + * has been closed, then this method has no effect. + * + * @see ByteArrayInputStream#count + */ + public synchronized void reset() { + if (closed) { + return; + } + super.reset (); + } + + /** + * After close() has been called, it is no longer possible to write + * to this stream. Further calls to write will have no effect. + */ + public synchronized void close() throws IOException { + closed = true; + super.close (); + } +} diff --git a/src/sun/net/www/protocol/file/FileURLConnection.java b/src/sun/net/www/protocol/file/FileURLConnection.java new file mode 100644 index 00000000..1997cb08 --- /dev/null +++ b/src/sun/net/www/protocol/file/FileURLConnection.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Open an file input stream given a URL. + * @author James Gosling + * @author Steven B. Byrne + */ + +package sun.net.www.protocol.file; + +import java.net.URL; +import java.net.FileNameMap; +import java.io.*; +import java.text.Collator; +import java.security.Permission; +import sun.net.*; +import sun.net.www.*; +import java.util.*; +import java.text.SimpleDateFormat; + +import sun.security.action.GetPropertyAction; +import sun.security.action.GetIntegerAction; +import sun.security.action.GetBooleanAction; + +public class FileURLConnection extends URLConnection { + + static String CONTENT_LENGTH = "content-length"; + static String CONTENT_TYPE = "content-type"; + static String TEXT_PLAIN = "text/plain"; + static String LAST_MODIFIED = "last-modified"; + + String contentType; + InputStream is; + + File file; + String filename; + boolean isDirectory = false; + boolean exists = false; + List files; + + long length = -1; + long lastModified = 0; + + protected FileURLConnection(URL u, File file) { + super(u); + this.file = file; + } + + /* + * Note: the semantics of FileURLConnection object is that the + * results of the various URLConnection calls, such as + * getContentType, getInputStream or getContentLength reflect + * whatever was true when connect was called. + */ + public void connect() throws IOException { + if (!connected) { + try { + filename = file.toString(); + isDirectory = file.isDirectory(); + if (isDirectory) { + String[] fileList = file.list(); + if (fileList == null) + throw new FileNotFoundException(filename + " exists, but is not accessible"); + files = Arrays.asList(fileList); + } else { + + is = new BufferedInputStream(new FileInputStream(filename)); + + // Check if URL should be metered + boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET"); + if (meteredInput) { + ProgressSource pi = new ProgressSource(url, "GET", file.length()); + is = new MeteredStream(is, pi, file.length()); + } + } + } catch (IOException e) { + throw e; + } + connected = true; + } + } + + private boolean initializedHeaders = false; + + private void initializeHeaders() { + try { + connect(); + exists = file.exists(); + } catch (IOException e) { + } + if (!initializedHeaders || !exists) { + length = file.length(); + lastModified = file.lastModified(); + + if (!isDirectory) { + FileNameMap map = java.net.URLConnection.getFileNameMap(); + contentType = map.getContentTypeFor(filename); + if (contentType != null) { + properties.add(CONTENT_TYPE, contentType); + } + properties.add(CONTENT_LENGTH, String.valueOf(length)); + + /* + * Format the last-modified field into the preferred + * Internet standard - ie: fixed-length subset of that + * defined by RFC 1123 + */ + if (lastModified != 0) { + Date date = new Date(lastModified); + SimpleDateFormat fo = + new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + fo.setTimeZone(TimeZone.getTimeZone("GMT")); + properties.add(LAST_MODIFIED, fo.format(date)); + } + } else { + properties.add(CONTENT_TYPE, TEXT_PLAIN); + } + initializedHeaders = true; + } + } + + public String getHeaderField(String name) { + initializeHeaders(); + return super.getHeaderField(name); + } + + public String getHeaderField(int n) { + initializeHeaders(); + return super.getHeaderField(n); + } + + public int getContentLength() { + initializeHeaders(); + if (length > Integer.MAX_VALUE) + return -1; + return (int) length; + } + + public long getContentLengthLong() { + initializeHeaders(); + return length; + } + + public String getHeaderFieldKey(int n) { + initializeHeaders(); + return super.getHeaderFieldKey(n); + } + + public MessageHeader getProperties() { + initializeHeaders(); + return super.getProperties(); + } + + public long getLastModified() { + initializeHeaders(); + return lastModified; + } + + public synchronized InputStream getInputStream() + throws IOException { + + int iconHeight; + int iconWidth; + + connect(); + + if (is == null) { + if (isDirectory) { + FileNameMap map = java.net.URLConnection.getFileNameMap(); + + StringBuffer buf = new StringBuffer(); + + if (files == null) { + throw new FileNotFoundException(filename); + } + + Collections.sort(files, Collator.getInstance()); + + for (int i = 0 ; i < files.size() ; i++) { + String fileName = files.get(i); + buf.append(fileName); + buf.append("\n"); + } + // Put it into a (default) locale-specific byte-stream. + is = new ByteArrayInputStream(buf.toString().getBytes()); + } else { + throw new FileNotFoundException(filename); + } + } + return is; + } + + Permission permission; + + /* since getOutputStream isn't supported, only read permission is + * relevant + */ + public Permission getPermission() throws IOException { + if (permission == null) { + String decodedPath = ParseUtil.decode(url.getPath()); + if (File.separatorChar == '/') { + permission = new FilePermission(decodedPath, "read"); + } else { + permission = new FilePermission( + decodedPath.replace('/',File.separatorChar), "read"); + } + } + return permission; + } +} diff --git a/src/sun/net/www/protocol/ftp/FtpURLConnection.java b/src/sun/net/www/protocol/ftp/FtpURLConnection.java new file mode 100644 index 00000000..285f1a76 --- /dev/null +++ b/src/sun/net/www/protocol/ftp/FtpURLConnection.java @@ -0,0 +1,643 @@ +/* + * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * FTP stream opener. + */ + +package sun.net.www.protocol.ftp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.BufferedInputStream; +import java.io.FilterInputStream; +import java.io.FilterOutputStream; +import java.io.FileNotFoundException; +import java.net.URL; +import java.net.SocketPermission; +import java.net.UnknownHostException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.Proxy; +import java.net.ProxySelector; +import java.util.StringTokenizer; +import java.util.Iterator; +import java.security.Permission; +import sun.net.NetworkClient; +import sun.net.www.MessageHeader; +import sun.net.www.MeteredStream; +import sun.net.www.URLConnection; +import sun.net.www.protocol.http.HttpURLConnection; +import sun.net.ftp.FtpClient; +import sun.net.ftp.FtpProtocolException; +import sun.net.ProgressSource; +import sun.net.ProgressMonitor; +import sun.net.www.ParseUtil; +import sun.security.action.GetPropertyAction; + + +/** + * This class Opens an FTP input (or output) stream given a URL. + * It works as a one shot FTP transfer : + *

      + *
    • Login
    • + *
    • Get (or Put) the file
    • + *
    • Disconnect
    • + *
    + * You should not have to use it directly in most cases because all will be handled + * in a abstract layer. Here is an example of how to use the class : + *

    + * URL url = new URL("ftp://ftp.sun.com/pub/test.txt");

    + * UrlConnection con = url.openConnection();

    + * InputStream is = con.getInputStream();

    + * ...

    + * is.close(); + * + * @see FtpClient + */ +public class FtpURLConnection extends URLConnection { + + // In case we have to use proxies, we use HttpURLConnection + HttpURLConnection http = null; + private Proxy instProxy; + + InputStream is = null; + OutputStream os = null; + + FtpClient ftp = null; + Permission permission; + + String password; + String user; + + String host; + String pathname; + String filename; + String fullpath; + int port; + static final int NONE = 0; + static final int ASCII = 1; + static final int BIN = 2; + static final int DIR = 3; + int type = NONE; + /* Redefine timeouts from java.net.URLConnection as we need -1 to mean + * not set. This is to ensure backward compatibility. + */ + private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;; + private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;; + + /** + * For FTP URLs we need to have a special InputStream because we + * need to close 2 sockets after we're done with it : + * - The Data socket (for the file). + * - The command socket (FtpClient). + * Since that's the only class that needs to see that, it is an inner class. + */ + protected class FtpInputStream extends FilterInputStream { + FtpClient ftp; + FtpInputStream(FtpClient cl, InputStream fd) { + super(new BufferedInputStream(fd)); + ftp = cl; + } + + @Override + public void close() throws IOException { + super.close(); + if (ftp != null) { + ftp.close(); + } + } + } + + /** + * For FTP URLs we need to have a special OutputStream because we + * need to close 2 sockets after we're done with it : + * - The Data socket (for the file). + * - The command socket (FtpClient). + * Since that's the only class that needs to see that, it is an inner class. + */ + protected class FtpOutputStream extends FilterOutputStream { + FtpClient ftp; + FtpOutputStream(FtpClient cl, OutputStream fd) { + super(fd); + ftp = cl; + } + + @Override + public void close() throws IOException { + super.close(); + if (ftp != null) { + ftp.close(); + } + } + } + + /** + * Creates an FtpURLConnection from a URL. + * + * @param url The URL to retrieve or store. + */ + public FtpURLConnection(URL url) { + this(url, null); + } + + /** + * Same as FtpURLconnection(URL) with a per connection proxy specified + */ + FtpURLConnection(URL url, Proxy p) { + super(url); + instProxy = p; + host = url.getHost(); + port = url.getPort(); + String userInfo = url.getUserInfo(); + + if (userInfo != null) { // get the user and password + int delimiter = userInfo.indexOf(':'); + if (delimiter == -1) { + user = ParseUtil.decode(userInfo); + password = null; + } else { + user = ParseUtil.decode(userInfo.substring(0, delimiter++)); + password = ParseUtil.decode(userInfo.substring(delimiter)); + } + } + } + + private void setTimeouts() { + if (ftp != null) { + if (connectTimeout >= 0) { + ftp.setConnectTimeout(connectTimeout); + } + if (readTimeout >= 0) { + ftp.setReadTimeout(readTimeout); + } + } + } + + /** + * Connects to the FTP server and logs in. + * + * @throws FtpLoginException if the login is unsuccessful + * @throws FtpProtocolException if an error occurs + * @throws UnknownHostException if trying to connect to an unknown host + */ + + public synchronized void connect() throws IOException { + if (connected) { + return; + } + + Proxy p = null; + if (instProxy == null) { // no per connection proxy specified + /** + * Do we have to use a proxy? + */ + ProxySelector sel = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public ProxySelector run() { + return ProxySelector.getDefault(); + } + }); + if (sel != null) { + URI uri = ParseUtil.toURI(url); + Iterator it = sel.select(uri).iterator(); + while (it.hasNext()) { + p = it.next(); + if (p == null || p == Proxy.NO_PROXY || + p.type() == Proxy.Type.SOCKS) { + break; + } + if (p.type() != Proxy.Type.HTTP || + !(p.address() instanceof InetSocketAddress)) { + sel.connectFailed(uri, p.address(), new IOException("Wrong proxy type")); + continue; + } + // OK, we have an http proxy + InetSocketAddress paddr = (InetSocketAddress) p.address(); + try { + http = new HttpURLConnection(url, p); + http.setDoInput(getDoInput()); + http.setDoOutput(getDoOutput()); + if (connectTimeout >= 0) { + http.setConnectTimeout(connectTimeout); + } + if (readTimeout >= 0) { + http.setReadTimeout(readTimeout); + } + http.connect(); + connected = true; + return; + } catch (IOException ioe) { + sel.connectFailed(uri, paddr, ioe); + http = null; + } + } + } + } else { // per connection proxy specified + p = instProxy; + if (p.type() == Proxy.Type.HTTP) { + http = new HttpURLConnection(url, instProxy); + http.setDoInput(getDoInput()); + http.setDoOutput(getDoOutput()); + if (connectTimeout >= 0) { + http.setConnectTimeout(connectTimeout); + } + if (readTimeout >= 0) { + http.setReadTimeout(readTimeout); + } + http.connect(); + connected = true; + return; + } + } + + if (user == null) { + user = "anonymous"; + String vers = java.security.AccessController.doPrivileged( + new GetPropertyAction("java.version")); + password = java.security.AccessController.doPrivileged( + new GetPropertyAction("ftp.protocol.user", + "Java" + vers + "@")); + } + try { + ftp = FtpClient.create(); + if (p != null) { + ftp.setProxy(p); + } + setTimeouts(); + if (port != -1) { + ftp.connect(new InetSocketAddress(host, port)); + } else { + ftp.connect(new InetSocketAddress(host, FtpClient.defaultPort())); + } + } catch (UnknownHostException e) { + // Maybe do something smart here, like use a proxy like iftp. + // Just keep throwing for now. + throw e; + } catch (FtpProtocolException fe) { + throw new IOException(fe); + } + try { + ftp.login(user, password == null ? null : password.toCharArray()); + } catch (FtpProtocolException e) { + ftp.close(); + // Backward compatibility + throw new sun.net.ftp.FtpLoginException("Invalid username/password"); + } + connected = true; + } + + + /* + * Decodes the path as per the RFC-1738 specifications. + */ + private void decodePath(String path) { + int i = path.indexOf(";type="); + if (i >= 0) { + String s1 = path.substring(i + 6, path.length()); + if ("i".equalsIgnoreCase(s1)) { + type = BIN; + } + if ("a".equalsIgnoreCase(s1)) { + type = ASCII; + } + if ("d".equalsIgnoreCase(s1)) { + type = DIR; + } + path = path.substring(0, i); + } + if (path != null && path.length() > 1 && + path.charAt(0) == '/') { + path = path.substring(1); + } + if (path == null || path.length() == 0) { + path = "./"; + } + if (!path.endsWith("/")) { + i = path.lastIndexOf('/'); + if (i > 0) { + filename = path.substring(i + 1, path.length()); + filename = ParseUtil.decode(filename); + pathname = path.substring(0, i); + } else { + filename = ParseUtil.decode(path); + pathname = null; + } + } else { + pathname = path.substring(0, path.length() - 1); + filename = null; + } + if (pathname != null) { + fullpath = pathname + "/" + (filename != null ? filename : ""); + } else { + fullpath = filename; + } + } + + /* + * As part of RFC-1738 it is specified that the path should be + * interpreted as a series of FTP CWD commands. + * This is because, '/' is not necessarly the directory delimiter + * on every systems. + */ + private void cd(String path) throws FtpProtocolException, IOException { + if (path == null || path.isEmpty()) { + return; + } + if (path.indexOf('/') == -1) { + ftp.changeDirectory(ParseUtil.decode(path)); + return; + } + + StringTokenizer token = new StringTokenizer(path, "/"); + while (token.hasMoreTokens()) { + ftp.changeDirectory(ParseUtil.decode(token.nextToken())); + } + } + + /** + * Get the InputStream to retreive the remote file. It will issue the + * "get" (or "dir") command to the ftp server. + * + * @return the InputStream to the connection. + * + * @throws IOException if already opened for output + * @throws FtpProtocolException if errors occur during the transfert. + */ + @Override + public InputStream getInputStream() throws IOException { + if (!connected) { + connect(); + } + + if (http != null) { + return http.getInputStream(); + } + + if (os != null) { + throw new IOException("Already opened for output"); + } + + if (is != null) { + return is; + } + + MessageHeader msgh = new MessageHeader(); + + boolean isAdir = false; + try { + decodePath(url.getPath()); + if (filename == null || type == DIR) { + ftp.setAsciiType(); + cd(pathname); + if (filename == null) { + is = new FtpInputStream(ftp, ftp.list(null)); + } else { + is = new FtpInputStream(ftp, ftp.nameList(filename)); + } + } else { + if (type == ASCII) { + ftp.setAsciiType(); + } else { + ftp.setBinaryType(); + } + cd(pathname); + is = new FtpInputStream(ftp, ftp.getFileStream(filename)); + } + + /* Try to get the size of the file in bytes. If that is + successful, then create a MeteredStream. */ + try { + long l = ftp.getLastTransferSize(); + msgh.add("content-length", Long.toString(l)); + if (l > 0) { + + // Wrap input stream with MeteredStream to ensure read() will always return -1 + // at expected length. + + // Check if URL should be metered + boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, "GET"); + ProgressSource pi = null; + + if (meteredInput) { + pi = new ProgressSource(url, "GET", l); + pi.beginTracking(); + } + + is = new MeteredStream(is, pi, l); + } + } catch (Exception e) { + e.printStackTrace(); + /* do nothing, since all we were doing was trying to + get the size in bytes of the file */ + } + + if (isAdir) { + msgh.add("content-type", "text/plain"); + msgh.add("access-type", "directory"); + } else { + msgh.add("access-type", "file"); + String ftype = guessContentTypeFromName(fullpath); + if (ftype == null && is.markSupported()) { + ftype = guessContentTypeFromStream(is); + } + if (ftype != null) { + msgh.add("content-type", ftype); + } + } + } catch (FileNotFoundException e) { + try { + cd(fullpath); + /* if that worked, then make a directory listing + and build an html stream with all the files in + the directory */ + ftp.setAsciiType(); + + is = new FtpInputStream(ftp, ftp.list(null)); + msgh.add("content-type", "text/plain"); + msgh.add("access-type", "directory"); + } catch (IOException ex) { + throw new FileNotFoundException(fullpath); + } catch (FtpProtocolException ex2) { + throw new FileNotFoundException(fullpath); + } + } catch (FtpProtocolException ftpe) { + throw new IOException(ftpe); + } + setProperties(msgh); + return is; + } + + /** + * Get the OutputStream to store the remote file. It will issue the + * "put" command to the ftp server. + * + * @return the OutputStream to the connection. + * + * @throws IOException if already opened for input or the URL + * points to a directory + * @throws FtpProtocolException if errors occur during the transfert. + */ + @Override + public OutputStream getOutputStream() throws IOException { + if (!connected) { + connect(); + } + + if (http != null) { + OutputStream out = http.getOutputStream(); + // getInputStream() is neccessary to force a writeRequests() + // on the http client. + http.getInputStream(); + return out; + } + + if (is != null) { + throw new IOException("Already opened for input"); + } + + if (os != null) { + return os; + } + + decodePath(url.getPath()); + if (filename == null || filename.length() == 0) { + throw new IOException("illegal filename for a PUT"); + } + try { + if (pathname != null) { + cd(pathname); + } + if (type == ASCII) { + ftp.setAsciiType(); + } else { + ftp.setBinaryType(); + } + os = new FtpOutputStream(ftp, ftp.putFileStream(filename, false)); + } catch (FtpProtocolException e) { + throw new IOException(e); + } + return os; + } + + String guessContentTypeFromFilename(String fname) { + return guessContentTypeFromName(fname); + } + + /** + * Gets the Permission associated with the host & port. + * + * @return The Permission object. + */ + @Override + public Permission getPermission() { + if (permission == null) { + int urlport = url.getPort(); + urlport = urlport < 0 ? FtpClient.defaultPort() : urlport; + String urlhost = this.host + ":" + urlport; + permission = new SocketPermission(urlhost, "connect"); + } + return permission; + } + + /** + * Sets the general request property. If a property with the key already + * exists, overwrite its value with the new value. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + * @throws IllegalStateException if already connected + * @see #getRequestProperty(String) + */ + @Override + public void setRequestProperty(String key, String value) { + super.setRequestProperty(key, value); + if ("type".equals(key)) { + if ("i".equalsIgnoreCase(value)) { + type = BIN; + } else if ("a".equalsIgnoreCase(value)) { + type = ASCII; + } else if ("d".equalsIgnoreCase(value)) { + type = DIR; + } else { + throw new IllegalArgumentException( + "Value of '" + key + + "' request property was '" + value + + "' when it must be either 'i', 'a' or 'd'"); + } + } + } + + /** + * Returns the value of the named general request property for this + * connection. + * + * @param key the keyword by which the request is known (e.g., "accept"). + * @return the value of the named general request property for this + * connection. + * @throws IllegalStateException if already connected + * @see #setRequestProperty(String, String) + */ + @Override + public String getRequestProperty(String key) { + String value = super.getRequestProperty(key); + + if (value == null) { + if ("type".equals(key)) { + value = (type == ASCII ? "a" : type == DIR ? "d" : "i"); + } + } + + return value; + } + + @Override + public void setConnectTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeouts can't be negative"); + } + connectTimeout = timeout; + } + + @Override + public int getConnectTimeout() { + return (connectTimeout < 0 ? 0 : connectTimeout); + } + + @Override + public void setReadTimeout(int timeout) { + if (timeout < 0) { + throw new IllegalArgumentException("timeouts can't be negative"); + } + readTimeout = timeout; + } + + @Override + public int getReadTimeout() { + return readTimeout < 0 ? 0 : readTimeout; + } +} diff --git a/src/sun/net/www/protocol/ftp/Handler.java b/src/sun/net/www/protocol/ftp/Handler.java new file mode 100644 index 00000000..f5caad65 --- /dev/null +++ b/src/sun/net/www/protocol/ftp/Handler.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * FTP stream opener + */ + +package sun.net.www.protocol.ftp; + +import java.io.IOException; +import java.net.URL; +import java.net.Proxy; +import java.util.Map; +import java.util.HashMap; +import sun.net.ftp.FtpClient; +import sun.net.www.protocol.http.HttpURLConnection; + +/** open an ftp connection given a URL */ +public class Handler extends java.net.URLStreamHandler { + + protected int getDefaultPort() { + return 21; + } + + protected boolean equals(URL u1, URL u2) { + String userInfo1 = u1.getUserInfo(); + String userInfo2 = u2.getUserInfo(); + return super.equals(u1, u2) && + (userInfo1 == null? userInfo2 == null: userInfo1.equals(userInfo2)); + } + + protected java.net.URLConnection openConnection(URL u) + throws IOException { + return openConnection(u, null); + } + + protected java.net.URLConnection openConnection(URL u, Proxy p) + throws IOException { + return new FtpURLConnection(u, p); + } +} diff --git a/src/sun/net/www/protocol/http/AuthCache.java b/src/sun/net/www/protocol/http/AuthCache.java new file mode 100644 index 00000000..6af6dd6e --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthCache.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +/** + * @author Michael McMahon + * + * Interface provided by internal http authentication cache. + * NB. This API will be replaced in a future release, and should + * not be made public. + */ + +public interface AuthCache { + + /** + * Put an entry in the cache. pkey is a string specified as follows: + * + * A:[B:]C:D:E[:F] Between 4 and 6 fields separated by ":" + * where the fields have the following meaning: + * A is "s" or "p" for server or proxy authentication respectively + * B is optional and is the {@link AuthScheme}, e.g. BASIC, DIGEST, NTLM, etc + * C is either "http" or "https" + * D is the hostname + * E is the port number + * F is optional and if present is the realm + * + * Generally, two entries are created for each AuthCacheValue, + * one including the realm and one without the realm. + * Also, for some schemes (digest) multiple entries may be created + * with the same pkey, but with a different path value in + * the AuthCacheValue. + */ + public void put (String pkey, AuthCacheValue value); + + /** + * Get an entry from the cache based on pkey as described above, but also + * using a pathname (skey) and the cache must return an entry + * if skey is a sub-path of the AuthCacheValue.path field. + */ + public AuthCacheValue get (String pkey, String skey); + + /** + * remove the entry from the cache whose pkey is specified and + * whose path is equal to entry.path. If entry is null then + * all entries with the same pkey should be removed. + */ + public void remove (String pkey, AuthCacheValue entry); +} diff --git a/src/sun/net/www/protocol/http/AuthCacheImpl.java b/src/sun/net/www/protocol/http/AuthCacheImpl.java new file mode 100644 index 00000000..45a5a0ea --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthCacheImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.HashMap; + +/** + * @author Michael McMahon + */ + +public class AuthCacheImpl implements AuthCache { + HashMap> hashtable; + + public AuthCacheImpl () { + hashtable = new HashMap>(); + } + + public void setMap (HashMap> map) { + hashtable = map; + } + + // put a value in map according to primary key + secondary key which + // is the path field of AuthenticationInfo + + public synchronized void put (String pkey, AuthCacheValue value) { + LinkedList list = hashtable.get (pkey); + String skey = value.getPath(); + if (list == null) { + list = new LinkedList(); + hashtable.put(pkey, list); + } + // Check if the path already exists or a super-set of it exists + ListIterator iter = list.listIterator(); + while (iter.hasNext()) { + AuthenticationInfo inf = (AuthenticationInfo)iter.next(); + if (inf.path == null || inf.path.startsWith (skey)) { + iter.remove (); + } + } + iter.add(value); + } + + // get a value from map checking both primary + // and secondary (urlpath) key + + public synchronized AuthCacheValue get (String pkey, String skey) { + AuthenticationInfo result = null; + LinkedList list = hashtable.get (pkey); + if (list == null || list.size() == 0) { + return null; + } + if (skey == null) { + // list should contain only one element + return (AuthenticationInfo)list.get (0); + } + ListIterator iter = list.listIterator(); + while (iter.hasNext()) { + AuthenticationInfo inf = (AuthenticationInfo)iter.next(); + if (skey.startsWith (inf.path)) { + return inf; + } + } + return null; + } + + public synchronized void remove (String pkey, AuthCacheValue entry) { + LinkedList list = hashtable.get (pkey); + if (list == null) { + return; + } + if (entry == null) { + list.clear(); + return; + } + ListIterator iter = list.listIterator (); + while (iter.hasNext()) { + AuthenticationInfo inf = (AuthenticationInfo)iter.next(); + if (entry.equals(inf)) { + iter.remove (); + } + } + } +} diff --git a/src/sun/net/www/protocol/http/AuthCacheValue.java b/src/sun/net/www/protocol/http/AuthCacheValue.java new file mode 100644 index 00000000..a8f66fd1 --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthCacheValue.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.io.Serializable; +import java.net.PasswordAuthentication; + +/** + * AuthCacheValue: interface to minimize exposure to authentication cache + * for external users (ie. plugin) + * + * @author Michael McMahon + */ + +public abstract class AuthCacheValue implements Serializable { + + static final long serialVersionUID = 735249334068211611L; + + public enum Type { + Proxy, + Server + }; + + /** + * Caches authentication info entered by user. See cacheKey() + */ + static protected AuthCache cache = new AuthCacheImpl(); + + public static void setAuthCache (AuthCache map) { + cache = map; + } + + /* Package private ctor to prevent extension outside package */ + + AuthCacheValue() {} + + /** + * Proxy or Server + */ + abstract Type getAuthType (); + + /** + * Authentication scheme + */ + abstract AuthScheme getAuthScheme(); + + /** + * name of server/proxy + */ + abstract String getHost (); + + /** + * portnumber of server/proxy + */ + abstract int getPort(); + + /** + * realm of authentication if known + */ + abstract String getRealm(); + + /** + * root path of realm or the request path if the root + * is not known yet. + */ + abstract String getPath(); + + /** + * returns http or https + */ + abstract String getProtocolScheme(); + + /** + * the credentials associated with this authentication + */ + abstract PasswordAuthentication credentials(); +} diff --git a/src/sun/net/www/protocol/http/AuthScheme.java b/src/sun/net/www/protocol/http/AuthScheme.java new file mode 100644 index 00000000..2c56632f --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthScheme.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.www.protocol.http; + +/* Authentication schemes supported by the http implementation. New schemes, if + * supported, should be defined here. + */ +public enum AuthScheme { + BASIC, + DIGEST, + NTLM, + NEGOTIATE, + KERBEROS, + UNKNOWN; +} + diff --git a/src/sun/net/www/protocol/http/AuthenticationHeader.java b/src/sun/net/www/protocol/http/AuthenticationHeader.java new file mode 100644 index 00000000..89b4d991 --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthenticationHeader.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import sun.net.www.*; +import java.util.Iterator; +import java.util.HashMap; + +/** + * This class is used to parse the information in WWW-Authenticate: and Proxy-Authenticate: + * headers. It searches among multiple header lines and within each header line + * for the best currently supported scheme. It can also return a HeaderParser + * containing the challenge data for that particular scheme. + * + * Some examples: + * + * WWW-Authenticate: Basic realm="foo" Digest realm="bar" NTLM + * Note the realm parameter must be associated with the particular scheme. + * + * or + * + * WWW-Authenticate: Basic realm="foo" + * WWW-Authenticate: Digest realm="foo",qop="auth",nonce="thisisanunlikelynonce" + * WWW-Authenticate: NTLM + * + * or + * + * WWW-Authenticate: Basic realm="foo" + * WWW-Authenticate: NTLM ASKAJK9893289889QWQIOIONMNMN + * + * The last example shows how NTLM breaks the rules of rfc2617 for the structure of + * the authentication header. This is the reason why the raw header field is used for ntlm. + * + * At present, the class chooses schemes in following order : + * 1. Negotiate (if supported) + * 2. Kerberos (if supported) + * 3. Digest + * 4. NTLM (if supported) + * 5. Basic + * + * This choice can be modified by setting a system property: + * + * -Dhttp.auth.preference="scheme" + * + * which in this case, specifies that "scheme" should be used as the auth scheme when offered + * disregarding the default prioritisation. If scheme is not offered then the default priority + * is used. + * + * Attention: when http.auth.preference is set as SPNEGO or Kerberos, it's actually "Negotiate + * with SPNEGO" or "Negotiate with Kerberos", which means the user will prefer the Negotiate + * scheme with GSS/SPNEGO or GSS/Kerberos mechanism. + * + * This also means that the real "Kerberos" scheme can never be set as a preference. + */ + +public class AuthenticationHeader { + + MessageHeader rsp; // the response to be parsed + HeaderParser preferred; + String preferred_r; // raw Strings + private final HttpCallerInfo hci; // un-schemed, need check + + // When set true, do not use Negotiate even if the response + // headers suggest so. + boolean dontUseNegotiate = false; + static String authPref=null; + + public String toString() { + return "AuthenticationHeader: prefer " + preferred_r; + } + + static { + authPref = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("http.auth.preference")); + + // http.auth.preference can be set to SPNEGO or Kerberos. + // In fact they means "Negotiate with SPNEGO" and "Negotiate with + // Kerberos" separately, so here they are all translated into + // Negotiate. Read NegotiateAuthentication.java to see how they + // were used later. + + if (authPref != null) { + authPref = authPref.toLowerCase(); + if(authPref.equals("spnego") || authPref.equals("kerberos")) { + authPref = "negotiate"; + } + } + } + + String hdrname; // Name of the header to look for + + /** + * parse a set of authentication headers and choose the preferred scheme + * that we support for a given host + */ + public AuthenticationHeader (String hdrname, MessageHeader response, + HttpCallerInfo hci, boolean dontUseNegotiate) { + this.hci = hci; + this.dontUseNegotiate = dontUseNegotiate; + rsp = response; + this.hdrname = hdrname; + schemes = new HashMap(); + parse(); + } + + public HttpCallerInfo getHttpCallerInfo() { + return hci; + } + /* we build up a map of scheme names mapped to SchemeMapValue objects */ + static class SchemeMapValue { + SchemeMapValue (HeaderParser h, String r) {raw=r; parser=h;} + String raw; + HeaderParser parser; + } + + HashMap schemes; + + /* Iterate through each header line, and then within each line. + * If multiple entries exist for a particular scheme (unlikely) + * then the last one will be used. The + * preferred scheme that we support will be used. + */ + private void parse () { + Iterator iter = rsp.multiValueIterator(hdrname); + while (iter.hasNext()) { + String raw = iter.next(); + HeaderParser hp = new HeaderParser(raw); + Iterator keys = hp.keys(); + int i, lastSchemeIndex; + for (i=0, lastSchemeIndex = -1; keys.hasNext(); i++) { + keys.next(); + if (hp.findValue(i) == null) { /* found a scheme name */ + if (lastSchemeIndex != -1) { + HeaderParser hpn = hp.subsequence (lastSchemeIndex, i); + String scheme = hpn.findKey(0); + schemes.put (scheme, new SchemeMapValue (hpn, raw)); + } + lastSchemeIndex = i; + } + } + if (i > lastSchemeIndex) { + HeaderParser hpn = hp.subsequence (lastSchemeIndex, i); + String scheme = hpn.findKey(0); + schemes.put(scheme, new SchemeMapValue (hpn, raw)); + } + } + + /* choose the best of them, the order is + * negotiate -> kerberos -> digest -> ntlm -> basic + */ + SchemeMapValue v = null; + if (authPref == null || (v=schemes.get (authPref)) == null) { + + if(v == null && !dontUseNegotiate) { + SchemeMapValue tmp = schemes.get("negotiate"); + if(tmp != null) { + if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Negotiate"))) { + tmp = null; + } + v = tmp; + } + } + + if(v == null && !dontUseNegotiate) { + SchemeMapValue tmp = schemes.get("kerberos"); + if(tmp != null) { + // the Kerberos scheme is only observed in MS ISA Server. In + // fact i think it's a Kerberos-mechnism-only Negotiate. + // Since the Kerberos scheme is always accompanied with the + // Negotiate scheme, so it seems impossible to reach this + // line. Even if the user explicitly set http.auth.preference + // as Kerberos, it means Negotiate with Kerberos, and the code + // will still tried to use Negotiate at first. + // + // The only chance this line get executed is that the server + // only suggest the Kerberos scheme. + if(hci == null || !NegotiateAuthentication.isSupported(new HttpCallerInfo(hci, "Kerberos"))) { + tmp = null; + } + v = tmp; + } + } + + if(v == null) { + if ((v=schemes.get ("digest")) == null) { + if (!NTLMAuthenticationProxy.supported + || ((v=schemes.get("ntlm"))==null)) { + v = schemes.get ("basic"); + } + } + } + } else { // authPref != null && it's found in reponses' + if (dontUseNegotiate && authPref.equals("negotiate")) { + v = null; + } + } + + if (v != null) { + preferred = v.parser; + preferred_r = v.raw; + } + } + + /** + * return a header parser containing the preferred authentication scheme (only). + * The preferred scheme is the strongest of the schemes proposed by the server. + * The returned HeaderParser will contain the relevant parameters for that scheme + */ + public HeaderParser headerParser() { + return preferred; + } + + /** + * return the name of the preferred scheme + */ + public String scheme() { + if (preferred != null) { + return preferred.findKey(0); + } else { + return null; + } + } + + /* return the raw header field for the preferred/chosen scheme */ + + public String raw () { + return preferred_r; + } + + /** + * returns true is the header exists and contains a recognised scheme + */ + public boolean isPresent () { + return preferred != null; + } +} diff --git a/src/sun/net/www/protocol/http/AuthenticationInfo.java b/src/sun/net/www/protocol/http/AuthenticationInfo.java new file mode 100644 index 00000000..c1317e1d --- /dev/null +++ b/src/sun/net/www/protocol/http/AuthenticationInfo.java @@ -0,0 +1,452 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.util.HashMap; + +import sun.net.www.HeaderParser; + + +/** + * AuthenticationInfo: Encapsulate the information needed to + * authenticate a user to a server. + * + * @author Jon Payne + * @author Herb Jellinek + * @author Bill Foote + */ +// REMIND: It would be nice if this class understood about partial matching. +// If you're authorized for foo.com, chances are high you're also +// authorized for baz.foo.com. +// NB: When this gets implemented, be careful about the uncaching +// policy in HttpURLConnection. A failure on baz.foo.com shouldn't +// uncache foo.com! + +public abstract class AuthenticationInfo extends AuthCacheValue implements Cloneable { + + static final long serialVersionUID = -2588378268010453259L; + + // Constants saying what kind of authroization this is. This determines + // the namespace in the hash table lookup. + public static final char SERVER_AUTHENTICATION = 's'; + public static final char PROXY_AUTHENTICATION = 'p'; + + /** + * If true, then simultaneous authentication requests to the same realm/proxy + * are serialized, in order to avoid a user having to type the same username/passwords + * repeatedly, via the Authenticator. Default is false, which means that this + * behavior is switched off. + */ + static boolean serializeAuth; + + static { + serializeAuth = java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "http.auth.serializeRequests")).booleanValue(); + } + + /* AuthCacheValue: */ + + transient protected PasswordAuthentication pw; + + public PasswordAuthentication credentials() { + return pw; + } + + public Type getAuthType() { + return type == SERVER_AUTHENTICATION ? + Type.Server: + Type.Proxy; + } + + AuthScheme getAuthScheme() { + return authScheme; + } + + public String getHost() { + return host; + } + public int getPort() { + return port; + } + public String getRealm() { + return realm; + } + public String getPath() { + return path; + } + public String getProtocolScheme() { + return protocol; + } + + /** + * requests is used to ensure that interaction with the + * Authenticator for a particular realm is single threaded. + * ie. if multiple threads need to get credentials from the user + * at the same time, then all but the first will block until + * the first completes its authentication. + */ + static private HashMap requests = new HashMap<>(); + + /* check if a request for this destination is in progress + * return false immediately if not. Otherwise block until + * request is finished and return true + */ + static private boolean requestIsInProgress (String key) { + if (!serializeAuth) { + /* behavior is disabled. Revert to concurrent requests */ + return false; + } + synchronized (requests) { + Thread t, c; + c = Thread.currentThread(); + if ((t = requests.get(key)) == null) { + requests.put (key, c); + return false; + } + if (t == c) { + return false; + } + while (requests.containsKey(key)) { + try { + requests.wait (); + } catch (InterruptedException e) {} + } + } + /* entry may be in cache now. */ + return true; + } + + /* signal completion of an authentication (whether it succeeded or not) + * so that other threads can continue. + */ + static private void requestCompleted (String key) { + synchronized (requests) { + Thread thread = requests.get(key); + if (thread != null && thread == Thread.currentThread()) { + boolean waspresent = requests.remove(key) != null; + assert waspresent; + } + requests.notifyAll(); + } + } + + //public String toString () { + //return ("{"+type+":"+authScheme+":"+protocol+":"+host+":"+port+":"+realm+":"+path+"}"); + //} + + // REMIND: This cache just grows forever. We should put in a bounded + // cache, or maybe something using WeakRef's. + + /** The type (server/proxy) of authentication this is. Used for key lookup */ + char type; + + /** The authentication scheme (basic/digest). Also used for key lookup */ + AuthScheme authScheme; + + /** The protocol/scheme (i.e. http or https ). Need to keep the caches + * logically separate for the two protocols. This field is only used + * when constructed with a URL (the normal case for server authentication) + * For proxy authentication the protocol is not relevant. + */ + String protocol; + + /** The host we're authenticating against. */ + String host; + + /** The port on the host we're authenticating against. */ + int port; + + /** The realm we're authenticating against. */ + String realm; + + /** The shortest path from the URL we authenticated against. */ + String path; + + /** Use this constructor only for proxy entries */ + public AuthenticationInfo(char type, AuthScheme authScheme, String host, int port, String realm) { + this.type = type; + this.authScheme = authScheme; + this.protocol = ""; + this.host = host.toLowerCase(); + this.port = port; + this.realm = realm; + this.path = null; + } + + public Object clone() { + try { + return super.clone (); + } catch (CloneNotSupportedException e) { + // Cannot happen because Cloneable implemented by AuthenticationInfo + return null; + } + } + + /* + * Constructor used to limit the authorization to the path within + * the URL. Use this constructor for origin server entries. + */ + public AuthenticationInfo(char type, AuthScheme authScheme, URL url, String realm) { + this.type = type; + this.authScheme = authScheme; + this.protocol = url.getProtocol().toLowerCase(); + this.host = url.getHost().toLowerCase(); + this.port = url.getPort(); + if (this.port == -1) { + this.port = url.getDefaultPort(); + } + this.realm = realm; + + String urlPath = url.getPath(); + if (urlPath.length() == 0) + this.path = urlPath; + else { + this.path = reducePath (urlPath); + } + + } + + /* + * reduce the path to the root of where we think the + * authorization begins. This could get shorter as + * the url is traversed up following a successful challenge. + */ + static String reducePath (String urlPath) { + int sepIndex = urlPath.lastIndexOf('/'); + int targetSuffixIndex = urlPath.lastIndexOf('.'); + if (sepIndex != -1) + if (sepIndex < targetSuffixIndex) + return urlPath.substring(0, sepIndex+1); + else + return urlPath; + else + return urlPath; + } + + /** + * Returns info for the URL, for an HTTP server auth. Used when we + * don't yet know the realm + * (i.e. when we're preemptively setting the auth). + */ + static AuthenticationInfo getServerAuth(URL url) { + int port = url.getPort(); + if (port == -1) { + port = url.getDefaultPort(); + } + String key = SERVER_AUTHENTICATION + ":" + url.getProtocol().toLowerCase() + + ":" + url.getHost().toLowerCase() + ":" + port; + return getAuth(key, url); + } + + /** + * Returns info for the URL, for an HTTP server auth. Used when we + * do know the realm (i.e. when we're responding to a challenge). + * In this case we do not use the path because the protection space + * is identified by the host:port:realm only + */ + static String getServerAuthKey(URL url, String realm, AuthScheme scheme) { + int port = url.getPort(); + if (port == -1) { + port = url.getDefaultPort(); + } + String key = SERVER_AUTHENTICATION + ":" + scheme + ":" + url.getProtocol().toLowerCase() + + ":" + url.getHost().toLowerCase() + ":" + port + ":" + realm; + return key; + } + + static AuthenticationInfo getServerAuth(String key) { + AuthenticationInfo cached = getAuth(key, null); + if ((cached == null) && requestIsInProgress (key)) { + /* check the cache again, it might contain an entry */ + cached = getAuth(key, null); + } + return cached; + } + + + /** + * Return the AuthenticationInfo object from the cache if it's path is + * a substring of the supplied URLs path. + */ + static AuthenticationInfo getAuth(String key, URL url) { + if (url == null) { + return (AuthenticationInfo)cache.get (key, null); + } else { + return (AuthenticationInfo)cache.get (key, url.getPath()); + } + } + + /** + * Returns a firewall authentication, for the given host/port. Used + * for preemptive header-setting. Note, the protocol field is always + * blank for proxies. + */ + static AuthenticationInfo getProxyAuth(String host, int port) { + String key = PROXY_AUTHENTICATION + "::" + host.toLowerCase() + ":" + port; + AuthenticationInfo result = (AuthenticationInfo) cache.get(key, null); + return result; + } + + /** + * Returns a firewall authentication, for the given host/port and realm. + * Used in response to a challenge. Note, the protocol field is always + * blank for proxies. + */ + static String getProxyAuthKey(String host, int port, String realm, AuthScheme scheme) { + String key = PROXY_AUTHENTICATION + ":" + scheme + "::" + host.toLowerCase() + + ":" + port + ":" + realm; + return key; + } + + static AuthenticationInfo getProxyAuth(String key) { + AuthenticationInfo cached = (AuthenticationInfo) cache.get(key, null); + if ((cached == null) && requestIsInProgress (key)) { + /* check the cache again, it might contain an entry */ + cached = (AuthenticationInfo) cache.get(key, null); + } + return cached; + } + + + /** + * Add this authentication to the cache + */ + void addToCache() { + String key = cacheKey(true); + cache.put(key, this); + if (supportsPreemptiveAuthorization()) { + cache.put(cacheKey(false), this); + } + endAuthRequest(key); + } + + static void endAuthRequest (String key) { + if (!serializeAuth) { + return; + } + synchronized (requests) { + requestCompleted(key); + } + } + + /** + * Remove this authentication from the cache + */ + void removeFromCache() { + cache.remove(cacheKey(true), this); + if (supportsPreemptiveAuthorization()) { + cache.remove(cacheKey(false), this); + } + } + + /** + * @return true if this authentication supports preemptive authorization + */ + public abstract boolean supportsPreemptiveAuthorization(); + + /** + * @return the name of the HTTP header this authentication wants set. + * This is used for preemptive authorization. + */ + public String getHeaderName() { + if (type == SERVER_AUTHENTICATION) { + return "Authorization"; + } else { + return "Proxy-authorization"; + } + } + + /** + * Calculates and returns the authentication header value based + * on the stored authentication parameters. If the calculation does not depend + * on the URL or the request method then these parameters are ignored. + * @param url The URL + * @param method The request method + * @return the value of the HTTP header this authentication wants set. + * Used for preemptive authorization. + */ + public abstract String getHeaderValue(URL url, String method); + + /** + * Set header(s) on the given connection. Subclasses must override + * This will only be called for + * definitive (i.e. non-preemptive) authorization. + * @param conn The connection to apply the header(s) to + * @param p A source of header values for this connection, if needed. + * @param raw The raw header field (if needed) + * @return true if all goes well, false if no headers were set. + */ + public abstract boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw); + + /** + * Check if the header indicates that the current auth. parameters are stale. + * If so, then replace the relevant field with the new value + * and return true. Otherwise return false. + * returning true means the request can be retried with the same userid/password + * returning false means we have to go back to the user to ask for a new + * username password. + */ + public abstract boolean isAuthorizationStale (String header); + + /** + * Give a key for hash table lookups. + * @param includeRealm if you want the realm considered. Preemptively + * setting an authorization is done before the realm is known. + */ + String cacheKey(boolean includeRealm) { + // This must be kept in sync with the getXXXAuth() methods in this + // class. + if (includeRealm) { + return type + ":" + authScheme + ":" + protocol + ":" + + host + ":" + port + ":" + realm; + } else { + return type + ":" + protocol + ":" + host + ":" + port; + } + } + + String s1, s2; /* used for serialization of pw */ + + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject (); + pw = new PasswordAuthentication (s1, s2.toCharArray()); + s1 = null; s2= null; + } + + private synchronized void writeObject(java.io.ObjectOutputStream s) + throws IOException + { + s1 = pw.getUserName(); + s2 = new String (pw.getPassword()); + s.defaultWriteObject (); + } +} diff --git a/src/sun/net/www/protocol/http/BasicAuthentication.java b/src/sun/net/www/protocol/http/BasicAuthentication.java new file mode 100644 index 00000000..e77da615 --- /dev/null +++ b/src/sun/net/www/protocol/http/BasicAuthentication.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.PasswordAuthentication; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Base64; +import sun.net.www.HeaderParser; + +/** + * BasicAuthentication: Encapsulate an http server authentication using + * the "basic" scheme. + * + * @author Bill Foote + */ + + +class BasicAuthentication extends AuthenticationInfo { + + private static final long serialVersionUID = 100L; + + /** The authentication string for this host, port, and realm. This is + a simple BASE64 encoding of "login:password". */ + String auth; + + /** + * Create a BasicAuthentication + */ + public BasicAuthentication(boolean isProxy, String host, int port, + String realm, PasswordAuthentication pw) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.BASIC, host, port, realm); + String plain = pw.getUserName() + ":"; + byte[] nameBytes = null; + try { + nameBytes = plain.getBytes("ISO-8859-1"); + } catch (java.io.UnsupportedEncodingException uee) { + assert false; + } + + // get password bytes + char[] passwd = pw.getPassword(); + byte[] passwdBytes = new byte[passwd.length]; + for (int i=0; i() { + public Boolean run() { + return NetProperties.getBoolean(compatPropName); + } + } + ); + delimCompatFlag = (b == null) ? false : b.booleanValue(); + } + + // Authentication parameters defined in RFC2617. + // One instance of these may be shared among several DigestAuthentication + // instances as a result of a single authorization (for multiple domains) + + static class Parameters implements Serializable { + private static final long serialVersionUID = -3584543755194526252L; + + private boolean serverQop; // server proposed qop=auth + private String opaque; + private String cnonce; + private String nonce; + private String algorithm; + private int NCcount=0; + + // The H(A1) string used for MD5-sess + private String cachedHA1; + + // Force the HA1 value to be recalculated because the nonce has changed + private boolean redoCachedHA1 = true; + + private static final int cnonceRepeat = 5; + + private static final int cnoncelen = 40; /* number of characters in cnonce */ + + private static Random random; + + static { + random = new Random(); + } + + Parameters () { + serverQop = false; + opaque = null; + algorithm = null; + cachedHA1 = null; + nonce = null; + setNewCnonce(); + } + + boolean authQop () { + return serverQop; + } + synchronized void incrementNC() { + NCcount ++; + } + synchronized int getNCCount () { + return NCcount; + } + + int cnonce_count = 0; + + /* each call increments the counter */ + synchronized String getCnonce () { + if (cnonce_count >= cnonceRepeat) { + setNewCnonce(); + } + cnonce_count++; + return cnonce; + } + synchronized void setNewCnonce () { + byte bb[] = new byte [cnoncelen/2]; + char cc[] = new char [cnoncelen]; + random.nextBytes (bb); + for (int i=0; i<(cnoncelen/2); i++) { + int x = bb[i] + 128; + cc[i*2]= (char) ('A'+ x/16); + cc[i*2+1]= (char) ('A'+ x%16); + } + cnonce = new String (cc, 0, cnoncelen); + cnonce_count = 0; + redoCachedHA1 = true; + } + + synchronized void setQop (String qop) { + if (qop != null) { + StringTokenizer st = new StringTokenizer (qop, " "); + while (st.hasMoreTokens()) { + if (st.nextToken().equalsIgnoreCase ("auth")) { + serverQop = true; + return; + } + } + } + serverQop = false; + } + + synchronized String getOpaque () { return opaque;} + synchronized void setOpaque (String s) { opaque=s;} + + synchronized String getNonce () { return nonce;} + + synchronized void setNonce (String s) { + if (!s.equals(nonce)) { + nonce=s; + NCcount = 0; + redoCachedHA1 = true; + } + } + + synchronized String getCachedHA1 () { + if (redoCachedHA1) { + return null; + } else { + return cachedHA1; + } + } + + synchronized void setCachedHA1 (String s) { + cachedHA1=s; + redoCachedHA1=false; + } + + synchronized String getAlgorithm () { return algorithm;} + synchronized void setAlgorithm (String s) { algorithm=s;} + } + + Parameters params; + + /** + * Create a DigestAuthentication + */ + public DigestAuthentication(boolean isProxy, URL url, String realm, + String authMethod, PasswordAuthentication pw, + Parameters params) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.DIGEST, + url, + realm); + this.authMethod = authMethod; + this.pw = pw; + this.params = params; + } + + public DigestAuthentication(boolean isProxy, String host, int port, String realm, + String authMethod, PasswordAuthentication pw, + Parameters params) { + super(isProxy ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + AuthScheme.DIGEST, + host, + port, + realm); + this.authMethod = authMethod; + this.pw = pw; + this.params = params; + } + + /** + * @return true if this authentication supports preemptive authorization + */ + @Override + public boolean supportsPreemptiveAuthorization() { + return true; + } + + /** + * Reclaculates the request-digest and returns it. + * + *

    Used in the common case where the requestURI is simply the + * abs_path. + * + * @param url + * the URL + * + * @param method + * the HTTP method + * + * @return the value of the HTTP header this authentication wants set + */ + @Override + public String getHeaderValue(URL url, String method) { + return getHeaderValueImpl(url.getFile(), method); + } + + /** + * Reclaculates the request-digest and returns it. + * + *

    Used when the requestURI is not the abs_path. The exact + * requestURI can be passed as a String. + * + * @param requestURI + * the Request-URI from the HTTP request line + * + * @param method + * the HTTP method + * + * @return the value of the HTTP header this authentication wants set + */ + String getHeaderValue(String requestURI, String method) { + return getHeaderValueImpl(requestURI, method); + } + + /** + * Check if the header indicates that the current auth. parameters are stale. + * If so, then replace the relevant field with the new value + * and return true. Otherwise return false. + * returning true means the request can be retried with the same userid/password + * returning false means we have to go back to the user to ask for a new + * username password. + */ + @Override + public boolean isAuthorizationStale (String header) { + HeaderParser p = new HeaderParser (header); + String s = p.findValue ("stale"); + if (s == null || !s.equals("true")) + return false; + String newNonce = p.findValue ("nonce"); + if (newNonce == null || "".equals(newNonce)) { + return false; + } + params.setNonce (newNonce); + return true; + } + + /** + * Set header(s) on the given connection. + * @param conn The connection to apply the header(s) to + * @param p A source of header values for this connection, if needed. + * @param raw Raw header values for this connection, if needed. + * @return true if all goes well, false if no headers were set. + */ + @Override + public boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + params.setNonce (p.findValue("nonce")); + params.setOpaque (p.findValue("opaque")); + params.setQop (p.findValue("qop")); + + String uri=""; + String method; + if (type == PROXY_AUTHENTICATION && + conn.tunnelState() == HttpURLConnection.TunnelState.SETUP) { + uri = HttpURLConnection.connectRequestURI(conn.getURL()); + method = HTTP_CONNECT; + } else { + try { + uri = conn.getRequestURI(); + } catch (IOException e) {} + method = conn.getMethod(); + } + + if (params.nonce == null || authMethod == null || pw == null || realm == null) { + return false; + } + if (authMethod.length() >= 1) { + // Method seems to get converted to all lower case elsewhere. + // It really does need to start with an upper case letter + // here. + authMethod = Character.toUpperCase(authMethod.charAt(0)) + + authMethod.substring(1).toLowerCase(); + } + String algorithm = p.findValue("algorithm"); + if (algorithm == null || "".equals(algorithm)) { + algorithm = "MD5"; // The default, accoriding to rfc2069 + } + params.setAlgorithm (algorithm); + + // If authQop is true, then the server is doing RFC2617 and + // has offered qop=auth. We do not support any other modes + // and if auth is not offered we fallback to the RFC2069 behavior + + if (params.authQop()) { + params.setNewCnonce(); + } + + String value = getHeaderValueImpl (uri, method); + if (value != null) { + conn.setAuthenticationProperty(getHeaderName(), value); + return true; + } else { + return false; + } + } + + /* Calculate the Authorization header field given the request URI + * and based on the authorization information in params + */ + private String getHeaderValueImpl (String uri, String method) { + String response; + char[] passwd = pw.getPassword(); + boolean qop = params.authQop(); + String opaque = params.getOpaque(); + String cnonce = params.getCnonce (); + String nonce = params.getNonce (); + String algorithm = params.getAlgorithm (); + params.incrementNC (); + int nccount = params.getNCCount (); + String ncstring=null; + + if (nccount != -1) { + ncstring = Integer.toHexString (nccount).toLowerCase(); + int len = ncstring.length(); + if (len < 8) + ncstring = zeroPad [len] + ncstring; + } + + try { + response = computeDigest(true, pw.getUserName(),passwd,realm, + method, uri, nonce, cnonce, ncstring); + } catch (NoSuchAlgorithmException ex) { + return null; + } + + String ncfield = "\""; + if (qop) { + ncfield = "\", nc=" + ncstring; + } + + String algoS, qopS; + + if (delimCompatFlag) { + // Put quotes around these String value parameters + algoS = ", algorithm=\"" + algorithm + "\""; + qopS = ", qop=\"auth\""; + } else { + // Don't put quotes around them, per the RFC + algoS = ", algorithm=" + algorithm; + qopS = ", qop=auth"; + } + + String value = authMethod + + " username=\"" + pw.getUserName() + + "\", realm=\"" + realm + + "\", nonce=\"" + nonce + + ncfield + + ", uri=\"" + uri + + "\", response=\"" + response + "\"" + + algoS; + if (opaque != null) { + value += ", opaque=\"" + opaque + "\""; + } + if (cnonce != null) { + value += ", cnonce=\"" + cnonce + "\""; + } + if (qop) { + value += qopS; + } + return value; + } + + public void checkResponse (String header, String method, URL url) + throws IOException { + checkResponse (header, method, url.getFile()); + } + + public void checkResponse (String header, String method, String uri) + throws IOException { + char[] passwd = pw.getPassword(); + String username = pw.getUserName(); + boolean qop = params.authQop(); + String opaque = params.getOpaque(); + String cnonce = params.cnonce; + String nonce = params.getNonce (); + String algorithm = params.getAlgorithm (); + int nccount = params.getNCCount (); + String ncstring=null; + + if (header == null) { + throw new ProtocolException ("No authentication information in response"); + } + + if (nccount != -1) { + ncstring = Integer.toHexString (nccount).toUpperCase(); + int len = ncstring.length(); + if (len < 8) + ncstring = zeroPad [len] + ncstring; + } + try { + String expected = computeDigest(false, username,passwd,realm, + method, uri, nonce, cnonce, ncstring); + HeaderParser p = new HeaderParser (header); + String rspauth = p.findValue ("rspauth"); + if (rspauth == null) { + throw new ProtocolException ("No digest in response"); + } + if (!rspauth.equals (expected)) { + throw new ProtocolException ("Response digest invalid"); + } + /* Check if there is a nextnonce field */ + String nextnonce = p.findValue ("nextnonce"); + if (nextnonce != null && ! "".equals(nextnonce)) { + params.setNonce (nextnonce); + } + + } catch (NoSuchAlgorithmException ex) { + throw new ProtocolException ("Unsupported algorithm in response"); + } + } + + private String computeDigest( + boolean isRequest, String userName, char[] password, + String realm, String connMethod, + String requestURI, String nonceString, + String cnonce, String ncValue + ) throws NoSuchAlgorithmException + { + + String A1, HashA1; + String algorithm = params.getAlgorithm (); + boolean md5sess = algorithm.equalsIgnoreCase ("MD5-sess"); + + MessageDigest md = MessageDigest.getInstance(md5sess?"MD5":algorithm); + + if (md5sess) { + if ((HashA1 = params.getCachedHA1 ()) == null) { + String s = userName + ":" + realm + ":"; + String s1 = encode (s, password, md); + A1 = s1 + ":" + nonceString + ":" + cnonce; + HashA1 = encode(A1, null, md); + params.setCachedHA1 (HashA1); + } + } else { + A1 = userName + ":" + realm + ":"; + HashA1 = encode(A1, password, md); + } + + String A2; + if (isRequest) { + A2 = connMethod + ":" + requestURI; + } else { + A2 = ":" + requestURI; + } + String HashA2 = encode(A2, null, md); + String combo, finalHash; + + if (params.authQop()) { /* RRC2617 when qop=auth */ + combo = HashA1+ ":" + nonceString + ":" + ncValue + ":" + + cnonce + ":auth:" +HashA2; + + } else { /* for compatibility with RFC2069 */ + combo = HashA1 + ":" + + nonceString + ":" + + HashA2; + } + finalHash = encode(combo, null, md); + return finalHash; + } + + private final static char charArray[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private final static String zeroPad[] = { + // 0 1 2 3 4 5 6 7 + "00000000", "0000000", "000000", "00000", "0000", "000", "00", "0" + }; + + private String encode(String src, char[] passwd, MessageDigest md) { + try { + md.update(src.getBytes("ISO-8859-1")); + } catch (UnsupportedEncodingException uee) { + assert false; + } + if (passwd != null) { + byte[] passwdBytes = new byte[passwd.length]; + for (int i=0; i>> 4) & 0xf); + res.append(charArray[hashchar]); + hashchar = (digest[i] & 0xf); + res.append(charArray[hashchar]); + } + return res.toString(); + } +} diff --git a/src/sun/net/www/protocol/http/Handler.java b/src/sun/net/www/protocol/http/Handler.java new file mode 100644 index 00000000..0decc5ca --- /dev/null +++ b/src/sun/net/www/protocol/http/Handler.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * HTTP stream opener + */ + +package sun.net.www.protocol.http; + +import java.io.IOException; +import java.net.URL; +import java.net.Proxy; + +/** open an http input stream given a URL */ +public class Handler extends java.net.URLStreamHandler { + protected String proxy; + protected int proxyPort; + + protected int getDefaultPort() { + return 80; + } + + public Handler () { + proxy = null; + proxyPort = -1; + } + + public Handler (String proxy, int port) { + this.proxy = proxy; + this.proxyPort = port; + } + + protected java.net.URLConnection openConnection(URL u) + throws IOException { + return openConnection(u, (Proxy)null); + } + + protected java.net.URLConnection openConnection(URL u, Proxy p) + throws IOException { + return new HttpURLConnection(u, p, this); + } +} diff --git a/src/sun/net/www/protocol/http/HttpAuthenticator.java b/src/sun/net/www/protocol/http/HttpAuthenticator.java new file mode 100644 index 00000000..29902025 --- /dev/null +++ b/src/sun/net/www/protocol/http/HttpAuthenticator.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.net.URL; + +/** + * An interface for all objects that implement HTTP authentication. + * See the HTTP spec for details on how this works in general. + * A single class or object can implement an arbitrary number of + * authentication schemes. + * + * @author David Brown + * + * @deprecated -- use java.net.Authenticator instead + * @see java.net.Authenticator + */ +// +// REMIND: Unless compatibility with sun.* API's from 1.2 to 2.0 is +// a goal, there's no reason to carry this forward into JDK 2.0. +@Deprecated +public interface HttpAuthenticator { + + + /** + * Indicate whether the specified authentication scheme is + * supported. In accordance with HTTP specifications, the + * scheme name should be checked in a case-insensitive fashion. + */ + + boolean schemeSupported (String scheme); + + /** + * Returns the String that should be included in the HTTP + * Authorization field. Return null if no info was + * supplied or could be found. + *

    + * Example: + * --> GET http://www.authorization-required.com/ HTTP/1.0 + * <-- HTTP/1.0 403 Unauthorized + * <-- WWW-Authenticate: Basic realm="WallyWorld" + * call schemeSupported("Basic"); (return true) + * call authString(u, "Basic", "WallyWorld", null); + * return "QWadhgWERghghWERfdfQ==" + * --> GET http://www.authorization-required.com/ HTTP/1.0 + * --> Authorization: Basic QWadhgWERghghWERfdfQ== + * <-- HTTP/1.0 200 OK + * YAY!!! + */ + + public String authString (URL u, String scheme, String realm); + +} diff --git a/src/sun/net/www/protocol/http/HttpCallerInfo.java b/src/sun/net/www/protocol/http/HttpCallerInfo.java new file mode 100644 index 00000000..fcb3ac82 --- /dev/null +++ b/src/sun/net/www/protocol/http/HttpCallerInfo.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.net.Authenticator.RequestorType; +import java.net.InetAddress; +import java.net.URL; + +/** + * Used in HTTP/Negotiate, to feed HTTP request info into JGSS as a HttpCaller, + * so that special actions can be taken, including special callback handler, + * special useSubjectCredsOnly value. + * + * This is an immutable class. It can be instantiated in two styles; + * + * 1. Un-schemed: Create at the beginning before the preferred scheme is + * determined. This object can be fed into AuthenticationHeader to check + * for the preference. + * + * 2. Schemed: With the scheme field filled, can be used in JGSS-API calls. + */ +final public class HttpCallerInfo { + // All info that an Authenticator needs. + final public URL url; + final public String host, protocol, prompt, scheme; + final public int port; + final public InetAddress addr; + final public RequestorType authType; + + /** + * Create a schemed object based on an un-schemed one. + */ + public HttpCallerInfo(HttpCallerInfo old, String scheme) { + this.url = old.url; + this.host = old.host; + this.protocol = old.protocol; + this.prompt = old.prompt; + this.port = old.port; + this.addr = old.addr; + this.authType = old.authType; + this.scheme = scheme; + } + + /** + * Constructor an un-schemed object for site access. + */ + public HttpCallerInfo(URL url) { + this.url= url; + prompt = ""; + host = url.getHost(); + + int p = url.getPort(); + if (p == -1) { + port = url.getDefaultPort(); + } else { + port = p; + } + + InetAddress ia; + try { + ia = InetAddress.getByName(url.getHost()); + } catch (Exception e) { + ia = null; + } + addr = ia; + + protocol = url.getProtocol(); + authType = RequestorType.SERVER; + scheme = ""; + } + + /** + * Constructor an un-schemed object for proxy access. + */ + public HttpCallerInfo(URL url, String host, int port) { + this.url= url; + this.host = host; + this.port = port; + prompt = ""; + addr = null; + protocol = url.getProtocol(); + authType = RequestorType.PROXY; + scheme = ""; + } +} diff --git a/src/sun/net/www/protocol/http/HttpURLConnection.java b/src/sun/net/www/protocol/http/HttpURLConnection.java new file mode 100644 index 00000000..90edb985 --- /dev/null +++ b/src/sun/net/www/protocol/http/HttpURLConnection.java @@ -0,0 +1,3671 @@ +/* + * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.util.Arrays; +import java.net.URL; +import java.net.URLConnection; +import java.net.ProtocolException; +import java.net.HttpRetryException; +import java.net.PasswordAuthentication; +import java.net.Authenticator; +import java.net.HttpCookie; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.net.SocketTimeoutException; +import java.net.SocketPermission; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.InetSocketAddress; +import java.net.CookieHandler; +import java.net.ResponseCache; +import java.net.CacheResponse; +import java.net.SecureCacheResponse; +import java.net.CacheRequest; +import java.net.URLPermission; +import java.net.Authenticator.RequestorType; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.List; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.Iterator; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Set; +import sun.net.*; +import sun.net.www.*; +import sun.net.www.http.HttpClient; +import sun.net.www.http.PosterOutputStream; +import sun.net.www.http.ChunkedInputStream; +import sun.net.www.http.ChunkedOutputStream; +import sun.util.logging.PlatformLogger; +import java.text.SimpleDateFormat; +import java.util.TimeZone; +import java.net.MalformedURLException; +import java.nio.ByteBuffer; +import static sun.net.www.protocol.http.AuthScheme.BASIC; +import static sun.net.www.protocol.http.AuthScheme.DIGEST; +import static sun.net.www.protocol.http.AuthScheme.NTLM; +import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; +import static sun.net.www.protocol.http.AuthScheme.KERBEROS; +import static sun.net.www.protocol.http.AuthScheme.UNKNOWN; + +/** + * A class to represent an HTTP connection to a remote object. + */ + + +public class HttpURLConnection extends java.net.HttpURLConnection { + + static String HTTP_CONNECT = "CONNECT"; + + static final String version; + public static final String userAgent; + + /* max # of allowed re-directs */ + static final int defaultmaxRedirects = 20; + static final int maxRedirects; + + /* Not all servers support the (Proxy)-Authentication-Info headers. + * By default, we don't require them to be sent + */ + static final boolean validateProxy; + static final boolean validateServer; + + private StreamingOutputStream strOutputStream; + private final static String RETRY_MSG1 = + "cannot retry due to proxy authentication, in streaming mode"; + private final static String RETRY_MSG2 = + "cannot retry due to server authentication, in streaming mode"; + private final static String RETRY_MSG3 = + "cannot retry due to redirection, in streaming mode"; + + /* + * System properties related to error stream handling: + * + * sun.net.http.errorstream.enableBuffering = + * + * With the above system property set to true (default is false), + * when the response code is >=400, the HTTP handler will try to + * buffer the response body (up to a certain amount and within a + * time limit). Thus freeing up the underlying socket connection + * for reuse. The rationale behind this is that usually when the + * server responds with a >=400 error (client error or server + * error, such as 404 file not found), the server will send a + * small response body to explain who to contact and what to do to + * recover. With this property set to true, even if the + * application doesn't call getErrorStream(), read the response + * body, and then call close(), the underlying socket connection + * can still be kept-alive and reused. The following two system + * properties provide further control to the error stream + * buffering behaviour. + * + * sun.net.http.errorstream.timeout = + * the timeout (in millisec) waiting the error stream + * to be buffered; default is 300 ms + * + * sun.net.http.errorstream.bufferSize = + * the size (in bytes) to use for the buffering the error stream; + * default is 4k + */ + + + /* Should we enable buffering of error streams? */ + private static boolean enableESBuffer = false; + + /* timeout waiting for read for buffered error stream; + */ + private static int timeout4ESBuffer = 0; + + /* buffer size for buffered error stream; + */ + private static int bufSize4ES = 0; + + /* + * Restrict setting of request headers through the public api + * consistent with JavaScript XMLHttpRequest2 with a few + * exceptions. Disallowed headers are silently ignored for + * backwards compatibility reasons rather than throwing a + * SecurityException. For example, some applets set the + * Host header since old JREs did not implement HTTP 1.1. + * Additionally, any header starting with Sec- is + * disallowed. + * + * The following headers are allowed for historical reasons: + * + * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date, + * Referer, TE, User-Agent, headers beginning with Proxy-. + * + * The following headers are allowed in a limited form: + * + * Connection: close + * + * See http://www.w3.org/TR/XMLHttpRequest2. + */ + private static final boolean allowRestrictedHeaders; + private static final Set restrictedHeaderSet; + private static final String[] restrictedHeaders = { + /* Restricted by XMLHttpRequest2 */ + //"Accept-Charset", + //"Accept-Encoding", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Connection", /* close is allowed */ + "Content-Length", + //"Cookie", + //"Cookie2", + "Content-Transfer-Encoding", + //"Date", + //"Expect", + "Host", + "Keep-Alive", + "Origin", + // "Referer", + // "TE", + "Trailer", + "Transfer-Encoding", + "Upgrade", + //"User-Agent", + "Via" + }; + + static { + maxRedirects = AccessController.doPrivileged( + new sun.security.action.GetIntegerAction( + "http.maxRedirects", defaultmaxRedirects)).intValue(); + version = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("java.version")); + String agent = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("http.agent")); + if (agent == null) { + agent = "Java/"+version; + } else { + agent = agent + " Java/"+version; + } + userAgent = agent; + validateProxy = AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "http.auth.digest.validateProxy")).booleanValue(); + validateServer = AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "http.auth.digest.validateServer")).booleanValue(); + + enableESBuffer = AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "sun.net.http.errorstream.enableBuffering")).booleanValue(); + timeout4ESBuffer = AccessController.doPrivileged( + new sun.security.action.GetIntegerAction( + "sun.net.http.errorstream.timeout", 300)).intValue(); + if (timeout4ESBuffer <= 0) { + timeout4ESBuffer = 300; // use the default + } + + bufSize4ES = AccessController.doPrivileged( + new sun.security.action.GetIntegerAction( + "sun.net.http.errorstream.bufferSize", 4096)).intValue(); + if (bufSize4ES <= 0) { + bufSize4ES = 4096; // use the default + } + + allowRestrictedHeaders = AccessController.doPrivileged( + new sun.security.action.GetBooleanAction( + "sun.net.http.allowRestrictedHeaders")).booleanValue(); + if (!allowRestrictedHeaders) { + restrictedHeaderSet = new HashSet(restrictedHeaders.length); + for (int i=0; i < restrictedHeaders.length; i++) { + restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase()); + } + } else { + restrictedHeaderSet = null; + } + } + + static final String httpVersion = "HTTP/1.1"; + static final String acceptString = + "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"; + + // the following http request headers should NOT have their values + // returned for security reasons. + private static final String[] EXCLUDE_HEADERS = { + "Proxy-Authorization", + "Authorization" + }; + + // also exclude system cookies when any might be set + private static final String[] EXCLUDE_HEADERS2= { + "Proxy-Authorization", + "Authorization", + "Cookie", + "Cookie2" + }; + + protected HttpClient http; + protected Handler handler; + protected Proxy instProxy; + + private CookieHandler cookieHandler; + private final ResponseCache cacheHandler; + + // the cached response, and cached response headers and body + protected CacheResponse cachedResponse; + private MessageHeader cachedHeaders; + private InputStream cachedInputStream; + + /* output stream to server */ + protected PrintStream ps = null; + + + /* buffered error stream */ + private InputStream errorStream = null; + + /* User set Cookies */ + private boolean setUserCookies = true; + private String userCookies = null; + private String userCookies2 = null; + + /* We only have a single static authenticator for now. + * REMIND: backwards compatibility with JDK 1.1. Should be + * eliminated for JDK 2.0. + */ + @Deprecated + private static HttpAuthenticator defaultAuth; + + /* all the headers we send + * NOTE: do *NOT* dump out the content of 'requests' in the + * output or stacktrace since it may contain security-sensitive + * headers such as those defined in EXCLUDE_HEADERS. + */ + private MessageHeader requests; + + /* The headers actually set by the user are recorded here also + */ + private MessageHeader userHeaders; + + /* Headers and request method cannot be changed + * once this flag is set in :- + * - getOutputStream() + * - getInputStream()) + * - connect() + * Access synchronized on this. + */ + private boolean connecting = false; + + /* The following two fields are only used with Digest Authentication */ + String domain; /* The list of authentication domains */ + DigestAuthentication.Parameters digestparams; + + /* Current credentials in use */ + AuthenticationInfo currentProxyCredentials = null; + AuthenticationInfo currentServerCredentials = null; + boolean needToCheck = true; + private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */ + private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */ + + /* try auth without calling Authenticator. Used for transparent NTLM authentication */ + private boolean tryTransparentNTLMServer = true; + private boolean tryTransparentNTLMProxy = true; + private boolean useProxyResponseCode = false; + + /* Used by Windows specific code */ + private Object authObj; + + /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */ + boolean isUserServerAuth; + boolean isUserProxyAuth; + + String serverAuthKey, proxyAuthKey; + + /* Progress source */ + protected ProgressSource pi; + + /* all the response headers we get back */ + private MessageHeader responses; + /* the stream _from_ the server */ + private InputStream inputStream = null; + /* post stream _to_ the server, if any */ + private PosterOutputStream poster = null; + + /* Indicates if the std. request headers have been set in requests. */ + private boolean setRequests=false; + + /* Indicates whether a request has already failed or not */ + private boolean failedOnce=false; + + /* Remembered Exception, we will throw it again if somebody + calls getInputStream after disconnect */ + private Exception rememberedException = null; + + /* If we decide we want to reuse a client, we put it here */ + private HttpClient reuseClient = null; + + /* Tunnel states */ + public enum TunnelState { + /* No tunnel */ + NONE, + + /* Setting up a tunnel */ + SETUP, + + /* Tunnel has been successfully setup */ + TUNNELING + } + + private TunnelState tunnelState = TunnelState.NONE; + + /* Redefine timeouts from java.net.URLConnection as we need -1 to mean + * not set. This is to ensure backward compatibility. + */ + private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT; + private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT; + + /* A permission converted from a URLPermission */ + private SocketPermission socketPermission; + + /* Logging support */ + private static final PlatformLogger logger = + PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection"); + + /* + * privileged request password authentication + * + */ + private static PasswordAuthentication + privilegedRequestPasswordAuthentication( + final String host, + final InetAddress addr, + final int port, + final String protocol, + final String prompt, + final String scheme, + final URL url, + final RequestorType authType) { + return AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public PasswordAuthentication run() { + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Requesting Authentication: host =" + host + " url = " + url); + } + PasswordAuthentication pass = Authenticator.requestPasswordAuthentication( + host, addr, port, protocol, + prompt, scheme, url, authType); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null")); + } + return pass; + } + }); + } + + private boolean isRestrictedHeader(String key, String value) { + if (allowRestrictedHeaders) { + return false; + } + + key = key.toLowerCase(); + if (restrictedHeaderSet.contains(key)) { + /* + * Exceptions to restricted headers: + * + * Allow "Connection: close". + */ + if (key.equals("connection") && value.equalsIgnoreCase("close")) { + return false; + } + return true; + } else if (key.startsWith("sec-")) { + return true; + } + return false; + } + + /* + * Checks the validity of http message header and whether the header + * is restricted and throws IllegalArgumentException if invalid or + * restricted. + */ + private boolean isExternalMessageHeaderAllowed(String key, String value) { + checkMessageHeader(key, value); + if (!isRestrictedHeader(key, value)) { + return true; + } + return false; + } + + /* Logging support */ + public static PlatformLogger getHttpLogger() { + return logger; + } + + /* Used for Windows NTLM implementation */ + public Object authObj() { + return authObj; + } + + public void authObj(Object authObj) { + this.authObj = authObj; + } + + /* + * checks the validity of http message header and throws + * IllegalArgumentException if invalid. + */ + private void checkMessageHeader(String key, String value) { + char LF = '\n'; + int index = key.indexOf(LF); + if (index != -1) { + throw new IllegalArgumentException( + "Illegal character(s) in message header field: " + key); + } + else { + if (value == null) { + return; + } + + index = value.indexOf(LF); + while (index != -1) { + index++; + if (index < value.length()) { + char c = value.charAt(index); + if ((c==' ') || (c=='\t')) { + // ok, check the next occurrence + index = value.indexOf(LF, index); + continue; + } + } + throw new IllegalArgumentException( + "Illegal character(s) in message header value: " + value); + } + } + } + + public synchronized void setRequestMethod(String method) + throws ProtocolException { + if (connecting) { + throw new IllegalStateException("connect in progress"); + } + super.setRequestMethod(method); + } + + /* adds the standard key/val pairs to reqests if necessary & write to + * given PrintStream + */ + private void writeRequests() throws IOException { + /* print all message headers in the MessageHeader + * onto the wire - all the ones we've set and any + * others that have been set + */ + // send any pre-emptive authentication + if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) { + setPreemptiveProxyAuthentication(requests); + } + if (!setRequests) { + + /* We're very particular about the order in which we + * set the request headers here. The order should not + * matter, but some careless CGI programs have been + * written to expect a very particular order of the + * standard headers. To name names, the order in which + * Navigator3.0 sends them. In particular, we make *sure* + * to send Content-type: <> and Content-length:<> second + * to last and last, respectively, in the case of a POST + * request. + */ + if (!failedOnce) { + checkURLFile(); + requests.prepend(method + " " + getRequestURI()+" " + + httpVersion, null); + } + if (!getUseCaches()) { + requests.setIfNotSet ("Cache-Control", "no-cache"); + requests.setIfNotSet ("Pragma", "no-cache"); + } + requests.setIfNotSet("User-Agent", userAgent); + int port = url.getPort(); + String host = url.getHost(); + if (port != -1 && port != url.getDefaultPort()) { + host += ":" + String.valueOf(port); + } + String reqHost = requests.findValue("Host"); + if (reqHost == null || + (!reqHost.equalsIgnoreCase(host) && !checkSetHost())) + { + requests.set("Host", host); + } + requests.setIfNotSet("Accept", acceptString); + + /* + * For HTTP/1.1 the default behavior is to keep connections alive. + * However, we may be talking to a 1.0 server so we should set + * keep-alive just in case, except if we have encountered an error + * or if keep alive is disabled via a system property + */ + + // Try keep-alive only on first attempt + if (!failedOnce && http.getHttpKeepAliveSet()) { + if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) { + requests.setIfNotSet("Proxy-Connection", "keep-alive"); + } else { + requests.setIfNotSet("Connection", "keep-alive"); + } + } else { + /* + * RFC 2616 HTTP/1.1 section 14.10 says: + * HTTP/1.1 applications that do not support persistent + * connections MUST include the "close" connection option + * in every message + */ + requests.setIfNotSet("Connection", "close"); + } + // Set modified since if necessary + long modTime = getIfModifiedSince(); + if (modTime != 0 ) { + Date date = new Date(modTime); + //use the preferred date format according to RFC 2068(HTTP1.1), + // RFC 822 and RFC 1123 + SimpleDateFormat fo = + new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); + fo.setTimeZone(TimeZone.getTimeZone("GMT")); + requests.setIfNotSet("If-Modified-Since", fo.format(date)); + } + // check for preemptive authorization + AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url); + if (sauth != null && sauth.supportsPreemptiveAuthorization() ) { + // Sets "Authorization" + requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method)); + currentServerCredentials = sauth; + } + + if (!method.equals("PUT") && (poster != null || streaming())) { + requests.setIfNotSet ("Content-type", + "application/x-www-form-urlencoded"); + } + + boolean chunked = false; + + if (streaming()) { + if (chunkLength != -1) { + requests.set ("Transfer-Encoding", "chunked"); + chunked = true; + } else { /* fixed content length */ + if (fixedContentLengthLong != -1) { + requests.set ("Content-Length", + String.valueOf(fixedContentLengthLong)); + } else if (fixedContentLength != -1) { + requests.set ("Content-Length", + String.valueOf(fixedContentLength)); + } + } + } else if (poster != null) { + /* add Content-Length & POST/PUT data */ + synchronized (poster) { + /* close it, so no more data can be added */ + poster.close(); + requests.set("Content-Length", + String.valueOf(poster.size())); + } + } + + if (!chunked) { + if (requests.findValue("Transfer-Encoding") != null) { + requests.remove("Transfer-Encoding"); + if (logger.isLoggable(PlatformLogger.Level.WARNING)) { + logger.warning( + "use streaming mode for chunked encoding"); + } + } + } + + // get applicable cookies based on the uri and request headers + // add them to the existing request headers + setCookieHeader(); + + setRequests=true; + } + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(requests.toString()); + } + http.writeRequests(requests, poster, streaming()); + if (ps.checkError()) { + String proxyHost = http.getProxyHostUsed(); + int proxyPort = http.getProxyPortUsed(); + disconnectInternal(); + if (failedOnce) { + throw new IOException("Error writing to server"); + } else { // try once more + failedOnce=true; + if (proxyHost != null) { + setProxiedClient(url, proxyHost, proxyPort); + } else { + setNewClient (url); + } + ps = (PrintStream) http.getOutputStream(); + connected=true; + responses = new MessageHeader(); + setRequests=false; + writeRequests(); + } + } + } + + private boolean checkSetHost() { + SecurityManager s = System.getSecurityManager(); + if (s != null) { + String name = s.getClass().getName(); + if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") || + name.equals("sun.plugin2.applet.FXAppletSecurityManager") || + name.equals("com.sun.javaws.security.JavaWebStartSecurity") || + name.equals("sun.plugin.security.ActivatorSecurityManager")) + { + int CHECK_SET_HOST = -2; + try { + s.checkConnect(url.toExternalForm(), CHECK_SET_HOST); + } catch (SecurityException ex) { + return false; + } + } + } + return true; + } + + private void checkURLFile() { + SecurityManager s = System.getSecurityManager(); + if (s != null) { + String name = s.getClass().getName(); + if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") || + name.equals("sun.plugin2.applet.FXAppletSecurityManager") || + name.equals("com.sun.javaws.security.JavaWebStartSecurity") || + name.equals("sun.plugin.security.ActivatorSecurityManager")) + { + int CHECK_SUBPATH = -3; + try { + s.checkConnect(url.toExternalForm(), CHECK_SUBPATH); + } catch (SecurityException ex) { + throw new SecurityException("denied access outside a permitted URL subpath", ex); + } + } + } + } + + /** + * Create a new HttpClient object, bypassing the cache of + * HTTP client objects/connections. + * + * @param url the URL being accessed + */ + protected void setNewClient (URL url) + throws IOException { + setNewClient(url, false); + } + + /** + * Obtain a HttpsClient object. Use the cached copy if specified. + * + * @param url the URL being accessed + * @param useCache whether the cached connection should be used + * if present + */ + protected void setNewClient (URL url, boolean useCache) + throws IOException { + http = HttpClient.New(url, null, -1, useCache, connectTimeout, this); + http.setReadTimeout(readTimeout); + } + + + /** + * Create a new HttpClient object, set up so that it uses + * per-instance proxying to the given HTTP proxy. This + * bypasses the cache of HTTP client objects/connections. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + */ + protected void setProxiedClient (URL url, String proxyHost, int proxyPort) + throws IOException { + setProxiedClient(url, proxyHost, proxyPort, false); + } + + /** + * Obtain a HttpClient object, set up so that it uses per-instance + * proxying to the given HTTP proxy. Use the cached copy of HTTP + * client objects/connections if specified. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + * @param useCache whether the cached connection should be used + * if present + */ + protected void setProxiedClient (URL url, + String proxyHost, int proxyPort, + boolean useCache) + throws IOException { + proxiedConnect(url, proxyHost, proxyPort, useCache); + } + + protected void proxiedConnect(URL url, + String proxyHost, int proxyPort, + boolean useCache) + throws IOException { + http = HttpClient.New (url, proxyHost, proxyPort, useCache, + connectTimeout, this); + http.setReadTimeout(readTimeout); + } + + protected HttpURLConnection(URL u, Handler handler) + throws IOException { + // we set proxy == null to distinguish this case with the case + // when per connection proxy is set + this(u, null, handler); + } + + public HttpURLConnection(URL u, String host, int port) { + this(u, new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port))); + } + + /** this constructor is used by other protocol handlers such as ftp + that want to use http to fetch urls on their behalf.*/ + public HttpURLConnection(URL u, Proxy p) { + this(u, p, new Handler()); + } + + protected HttpURLConnection(URL u, Proxy p, Handler handler) { + super(u); + requests = new MessageHeader(); + responses = new MessageHeader(); + userHeaders = new MessageHeader(); + this.handler = handler; + instProxy = p; + if (instProxy instanceof ApplicationProxy) { + /* Application set Proxies should not have access to cookies + * in a secure environment unless explicitly allowed. */ + try { + cookieHandler = CookieHandler.getDefault(); + } catch (SecurityException se) { /* swallow exception */ } + } else { + cookieHandler = AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public CookieHandler run() { + return CookieHandler.getDefault(); + } + }); + } + cacheHandler = AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public ResponseCache run() { + return ResponseCache.getDefault(); + } + }); + } + + /** + * @deprecated. Use java.net.Authenticator.setDefault() instead. + */ + @Deprecated + public static void setDefaultAuthenticator(HttpAuthenticator a) { + defaultAuth = a; + } + + /** + * opens a stream allowing redirects only to the same host. + */ + public static InputStream openConnectionCheckRedirects(URLConnection c) + throws IOException + { + boolean redir; + int redirects = 0; + InputStream in; + + do { + if (c instanceof HttpURLConnection) { + ((HttpURLConnection) c).setInstanceFollowRedirects(false); + } + + // We want to open the input stream before + // getting headers, because getHeaderField() + // et al swallow IOExceptions. + in = c.getInputStream(); + redir = false; + + if (c instanceof HttpURLConnection) { + HttpURLConnection http = (HttpURLConnection) c; + int stat = http.getResponseCode(); + if (stat >= 300 && stat <= 307 && stat != 306 && + stat != HttpURLConnection.HTTP_NOT_MODIFIED) { + URL base = http.getURL(); + String loc = http.getHeaderField("Location"); + URL target = null; + if (loc != null) { + target = new URL(base, loc); + } + http.disconnect(); + if (target == null + || !base.getProtocol().equals(target.getProtocol()) + || base.getPort() != target.getPort() + || !hostsEqual(base, target) + || redirects >= 5) + { + throw new SecurityException("illegal URL redirect"); + } + redir = true; + c = target.openConnection(); + redirects++; + } + } + } while (redir); + return in; + } + + + // + // Same as java.net.URL.hostsEqual + // + private static boolean hostsEqual(URL u1, URL u2) { + final String h1 = u1.getHost(); + final String h2 = u2.getHost(); + + if (h1 == null) { + return h2 == null; + } else if (h2 == null) { + return false; + } else if (h1.equalsIgnoreCase(h2)) { + return true; + } + // Have to resolve addresses before comparing, otherwise + // names like tachyon and tachyon.eng would compare different + final boolean result[] = {false}; + + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + try { + InetAddress a1 = InetAddress.getByName(h1); + InetAddress a2 = InetAddress.getByName(h2); + result[0] = a1.equals(a2); + } catch(UnknownHostException | SecurityException e) { + } + return null; + } + }); + + return result[0]; + } + + // overridden in HTTPS subclass + + public void connect() throws IOException { + synchronized (this) { + connecting = true; + } + plainConnect(); + } + + private boolean checkReuseConnection () { + if (connected) { + return true; + } + if (reuseClient != null) { + http = reuseClient; + http.setReadTimeout(getReadTimeout()); + http.reuse = false; + reuseClient = null; + connected = true; + return true; + } + return false; + } + + private String getHostAndPort(URL url) { + String host = url.getHost(); + final String hostarg = host; + try { + // lookup hostname and use IP address if available + host = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public String run() throws IOException { + InetAddress addr = InetAddress.getByName(hostarg); + return addr.getHostAddress(); + } + } + ); + } catch (PrivilegedActionException e) {} + int port = url.getPort(); + if (port == -1) { + String scheme = url.getProtocol(); + if ("http".equals(scheme)) { + return host + ":80"; + } else { // scheme must be https + return host + ":443"; + } + } + return host + ":" + Integer.toString(port); + } + + protected void plainConnect() throws IOException { + synchronized (this) { + if (connected) { + return; + } + } + SocketPermission p = URLtoSocketPermission(this.url); + if (p != null) { + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Void run() throws IOException { + plainConnect0(); + return null; + } + }, null, p + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + // run without additional permission + plainConnect0(); + } + } + + /** + * if the caller has a URLPermission for connecting to the + * given URL, then return a SocketPermission which permits + * access to that destination. Return null otherwise. The permission + * is cached in a field (which can only be changed by redirects) + */ + SocketPermission URLtoSocketPermission(URL url) throws IOException { + + if (socketPermission != null) { + return socketPermission; + } + + SecurityManager sm = System.getSecurityManager(); + + if (sm == null) { + return null; + } + + // the permission, which we might grant + + SocketPermission newPerm = new SocketPermission( + getHostAndPort(url), "connect" + ); + + String actions = getRequestMethod()+":" + + getUserSetHeaders().getHeaderNamesInList(); + + String urlstring = url.getProtocol() + "://" + url.getAuthority() + + url.getPath(); + + URLPermission p = new URLPermission(urlstring, actions); + try { + sm.checkPermission(p); + socketPermission = newPerm; + return socketPermission; + } catch (SecurityException e) { + // fall thru + } + return null; + } + + protected void plainConnect0() throws IOException { + // try to see if request can be served from local cache + if (cacheHandler != null && getUseCaches()) { + try { + URI uri = ParseUtil.toURI(url); + if (uri != null) { + cachedResponse = cacheHandler.get(uri, getRequestMethod(), getUserSetHeaders().getHeaders()); + if ("https".equalsIgnoreCase(uri.getScheme()) + && !(cachedResponse instanceof SecureCacheResponse)) { + cachedResponse = null; + } + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Cache Request for " + uri + " / " + getRequestMethod()); + logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null")); + } + if (cachedResponse != null) { + cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders()); + cachedInputStream = cachedResponse.getBody(); + } + } + } catch (IOException ioex) { + // ignore and commence normal connection + } + if (cachedHeaders != null && cachedInputStream != null) { + connected = true; + return; + } else { + cachedResponse = null; + } + } + try { + /* Try to open connections using the following scheme, + * return on the first one that's successful: + * 1) if (instProxy != null) + * connect to instProxy; raise exception if failed + * 2) else use system default ProxySelector + * 3) is 2) fails, make direct connection + */ + + if (instProxy == null) { // no instance Proxy is set + /** + * Do we have to use a proxy? + */ + ProxySelector sel = + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public ProxySelector run() { + return ProxySelector.getDefault(); + } + }); + if (sel != null) { + URI uri = ParseUtil.toURI(url); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("ProxySelector Request for " + uri); + } + Iterator it = sel.select(uri).iterator(); + Proxy p; + while (it.hasNext()) { + p = it.next(); + try { + if (!failedOnce) { + http = getNewHttpClient(url, p, connectTimeout); + http.setReadTimeout(readTimeout); + } else { + // make sure to construct new connection if first + // attempt failed + http = getNewHttpClient(url, p, connectTimeout, false); + http.setReadTimeout(readTimeout); + } + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + if (p != null) { + logger.finest("Proxy used: " + p.toString()); + } + } + break; + } catch (IOException ioex) { + if (p != Proxy.NO_PROXY) { + sel.connectFailed(uri, p.address(), ioex); + if (!it.hasNext()) { + // fallback to direct connection + http = getNewHttpClient(url, null, connectTimeout, false); + http.setReadTimeout(readTimeout); + break; + } + } else { + throw ioex; + } + continue; + } + } + } else { + // No proxy selector, create http client with no proxy + if (!failedOnce) { + http = getNewHttpClient(url, null, connectTimeout); + http.setReadTimeout(readTimeout); + } else { + // make sure to construct new connection if first + // attempt failed + http = getNewHttpClient(url, null, connectTimeout, false); + http.setReadTimeout(readTimeout); + } + } + } else { + if (!failedOnce) { + http = getNewHttpClient(url, instProxy, connectTimeout); + http.setReadTimeout(readTimeout); + } else { + // make sure to construct new connection if first + // attempt failed + http = getNewHttpClient(url, instProxy, connectTimeout, false); + http.setReadTimeout(readTimeout); + } + } + + ps = (PrintStream)http.getOutputStream(); + } catch (IOException e) { + throw e; + } + // constructor to HTTP client calls openserver + connected = true; + } + + // subclass HttpsClient will overwrite & return an instance of HttpsClient + protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout) + throws IOException { + return HttpClient.New(url, p, connectTimeout, this); + } + + // subclass HttpsClient will overwrite & return an instance of HttpsClient + protected HttpClient getNewHttpClient(URL url, Proxy p, + int connectTimeout, boolean useCache) + throws IOException { + return HttpClient.New(url, p, connectTimeout, useCache, this); + } + + private void expect100Continue() throws IOException { + // Expect: 100-Continue was set, so check the return code for + // Acceptance + int oldTimeout = http.getReadTimeout(); + boolean enforceTimeOut = false; + boolean timedOut = false; + if (oldTimeout <= 0) { + // 5s read timeout in case the server doesn't understand + // Expect: 100-Continue + http.setReadTimeout(5000); + enforceTimeOut = true; + } + + try { + http.parseHTTP(responses, pi, this); + } catch (SocketTimeoutException se) { + if (!enforceTimeOut) { + throw se; + } + timedOut = true; + http.setIgnoreContinue(true); + } + if (!timedOut) { + // Can't use getResponseCode() yet + String resp = responses.getValue(0); + // Parse the response which is of the form: + // HTTP/1.1 417 Expectation Failed + // HTTP/1.1 100 Continue + if (resp != null && resp.startsWith("HTTP/")) { + String[] sa = resp.split("\\s+"); + responseCode = -1; + try { + // Response code is 2nd token on the line + if (sa.length > 1) + responseCode = Integer.parseInt(sa[1]); + } catch (NumberFormatException numberFormatException) { + } + } + if (responseCode != 100) { + throw new ProtocolException("Server rejected operation"); + } + } + + http.setReadTimeout(oldTimeout); + + responseCode = -1; + responses.reset(); + // Proceed + } + + /* + * Allowable input/output sequences: + * [interpreted as request entity] + * - get output, [write output,] get input, [read input] + * - get output, [write output] + * [interpreted as GET] + * - get input, [read input] + * Disallowed: + * - get input, [read input,] get output, [write output] + */ + + @Override + public synchronized OutputStream getOutputStream() throws IOException { + connecting = true; + SocketPermission p = URLtoSocketPermission(this.url); + + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return getOutputStream0(); + } + }, null, p + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + return getOutputStream0(); + } + } + + private synchronized OutputStream getOutputStream0() throws IOException { + try { + if (!doOutput) { + throw new ProtocolException("cannot write to a URLConnection" + + " if doOutput=false - call setDoOutput(true)"); + } + + if (method.equals("GET")) { + method = "POST"; // Backward compatibility + } + if ("TRACE".equals(method) && "http".equals(url.getProtocol())) { + throw new ProtocolException("HTTP method TRACE" + + " doesn't support output"); + } + + // if there's already an input stream open, throw an exception + if (inputStream != null) { + throw new ProtocolException("Cannot write output after reading input."); + } + + if (!checkReuseConnection()) + connect(); + + boolean expectContinue = false; + String expects = requests.findValue("Expect"); + if ("100-Continue".equalsIgnoreCase(expects) && streaming()) { + http.setIgnoreContinue(false); + expectContinue = true; + } + + if (streaming() && strOutputStream == null) { + writeRequests(); + } + + if (expectContinue) { + expect100Continue(); + } + ps = (PrintStream)http.getOutputStream(); + if (streaming()) { + if (strOutputStream == null) { + if (chunkLength != -1) { /* chunked */ + strOutputStream = new StreamingOutputStream( + new ChunkedOutputStream(ps, chunkLength), -1L); + } else { /* must be fixed content length */ + long length = 0L; + if (fixedContentLengthLong != -1) { + length = fixedContentLengthLong; + } else if (fixedContentLength != -1) { + length = fixedContentLength; + } + strOutputStream = new StreamingOutputStream(ps, length); + } + } + return strOutputStream; + } else { + if (poster == null) { + poster = new PosterOutputStream(); + } + return poster; + } + } catch (RuntimeException e) { + disconnectInternal(); + throw e; + } catch (ProtocolException e) { + // Save the response code which may have been set while enforcing + // the 100-continue. disconnectInternal() forces it to -1 + int i = responseCode; + disconnectInternal(); + responseCode = i; + throw e; + } catch (IOException e) { + disconnectInternal(); + throw e; + } + } + + public boolean streaming () { + return (fixedContentLength != -1) || (fixedContentLengthLong != -1) || + (chunkLength != -1); + } + + /* + * get applicable cookies based on the uri and request headers + * add them to the existing request headers + */ + private void setCookieHeader() throws IOException { + if (cookieHandler != null) { + // we only want to capture the user defined Cookies once, as + // they cannot be changed by user code after we are connected, + // only internally. + synchronized (this) { + if (setUserCookies) { + int k = requests.getKey("Cookie"); + if (k != -1) + userCookies = requests.getValue(k); + k = requests.getKey("Cookie2"); + if (k != -1) + userCookies2 = requests.getValue(k); + setUserCookies = false; + } + } + + // remove old Cookie header before setting new one. + requests.remove("Cookie"); + requests.remove("Cookie2"); + + URI uri = ParseUtil.toURI(url); + if (uri != null) { + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("CookieHandler request for " + uri); + } + Map> cookies + = cookieHandler.get( + uri, requests.getHeaders(EXCLUDE_HEADERS)); + if (!cookies.isEmpty()) { + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Cookies retrieved: " + cookies.toString()); + } + for (Map.Entry> entry : + cookies.entrySet()) { + String key = entry.getKey(); + // ignore all entries that don't have "Cookie" + // or "Cookie2" as keys + if (!"Cookie".equalsIgnoreCase(key) && + !"Cookie2".equalsIgnoreCase(key)) { + continue; + } + List l = entry.getValue(); + if (l != null && !l.isEmpty()) { + StringBuilder cookieValue = new StringBuilder(); + for (String value : l) { + cookieValue.append(value).append("; "); + } + // strip off the trailing '; ' + try { + requests.add(key, cookieValue.substring(0, cookieValue.length() - 2)); + } catch (StringIndexOutOfBoundsException ignored) { + // no-op + } + } + } + } + } + if (userCookies != null) { + int k; + if ((k = requests.getKey("Cookie")) != -1) + requests.set("Cookie", requests.getValue(k) + ";" + userCookies); + else + requests.set("Cookie", userCookies); + } + if (userCookies2 != null) { + int k; + if ((k = requests.getKey("Cookie2")) != -1) + requests.set("Cookie2", requests.getValue(k) + ";" + userCookies2); + else + requests.set("Cookie2", userCookies2); + } + + } // end of getting cookies + } + + @Override + public synchronized InputStream getInputStream() throws IOException { + connecting = true; + SocketPermission p = URLtoSocketPermission(this.url); + + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return getInputStream0(); + } + }, null, p + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + return getInputStream0(); + } + } + + @SuppressWarnings("empty-statement") + private synchronized InputStream getInputStream0() throws IOException { + + if (!doInput) { + throw new ProtocolException("Cannot read from URLConnection" + + " if doInput=false (call setDoInput(true))"); + } + + if (rememberedException != null) { + if (rememberedException instanceof RuntimeException) + throw new RuntimeException(rememberedException); + else { + throw getChainedException((IOException)rememberedException); + } + } + + if (inputStream != null) { + return inputStream; + } + + if (streaming() ) { + if (strOutputStream == null) { + getOutputStream(); + } + /* make sure stream is closed */ + strOutputStream.close (); + if (!strOutputStream.writtenOK()) { + throw new IOException ("Incomplete output stream"); + } + } + + int redirects = 0; + int respCode = 0; + long cl = -1; + AuthenticationInfo serverAuthentication = null; + AuthenticationInfo proxyAuthentication = null; + AuthenticationHeader srvHdr = null; + + /** + * Failed Negotiate + * + * In some cases, the Negotiate auth is supported for the + * remote host but the negotiate process still fails (For + * example, if the web page is located on a backend server + * and delegation is needed but fails). The authentication + * process will start again, and we need to detect this + * kind of failure and do proper fallback (say, to NTLM). + * + * In order to achieve this, the inNegotiate flag is set + * when the first negotiate challenge is met (and reset + * if authentication is finished). If a fresh new negotiate + * challenge (no parameter) is found while inNegotiate is + * set, we know there's a failed auth attempt recently. + * Here we'll ignore the header line so that fallback + * can be practiced. + * + * inNegotiateProxy is for proxy authentication. + */ + boolean inNegotiate = false; + boolean inNegotiateProxy = false; + + // If the user has set either of these headers then do not remove them + isUserServerAuth = requests.getKey("Authorization") != -1; + isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1; + + try { + do { + if (!checkReuseConnection()) + connect(); + + if (cachedInputStream != null) { + return cachedInputStream; + } + + // Check if URL should be metered + boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, method); + + if (meteredInput) { + pi = new ProgressSource(url, method); + pi.beginTracking(); + } + + /* REMIND: This exists to fix the HttpsURLConnection subclass. + * Hotjava needs to run on JDK1.1FCS. Do proper fix once a + * proper solution for SSL can be found. + */ + ps = (PrintStream)http.getOutputStream(); + + if (!streaming()) { + writeRequests(); + } + http.parseHTTP(responses, pi, this); + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(responses.toString()); + } + + boolean b1 = responses.filterNTLMResponses("WWW-Authenticate"); + boolean b2 = responses.filterNTLMResponses("Proxy-Authenticate"); + if (b1 || b2) { + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(">>>> Headers are filtered"); + logger.fine(responses.toString()); + } + } + + inputStream = http.getInputStream(); + + respCode = getResponseCode(); + if (respCode == -1) { + disconnectInternal(); + throw new IOException ("Invalid Http response"); + } + if (respCode == HTTP_PROXY_AUTH) { + if (streaming()) { + disconnectInternal(); + throw new HttpRetryException ( + RETRY_MSG1, HTTP_PROXY_AUTH); + } + + // Read comments labeled "Failed Negotiate" for details. + boolean dontUseNegotiate = false; + Iterator iter = responses.multiValueIterator("Proxy-Authenticate"); + while (iter.hasNext()) { + String value = iter.next().trim(); + if (value.equalsIgnoreCase("Negotiate") || + value.equalsIgnoreCase("Kerberos")) { + if (!inNegotiateProxy) { + inNegotiateProxy = true; + } else { + dontUseNegotiate = true; + doingNTLMp2ndStage = false; + proxyAuthentication = null; + } + break; + } + } + + // changes: add a 3rd parameter to the constructor of + // AuthenticationHeader, so that NegotiateAuthentication. + // isSupported can be tested. + // The other 2 appearances of "new AuthenticationHeader" is + // altered in similar ways. + + AuthenticationHeader authhdr = new AuthenticationHeader ( + "Proxy-Authenticate", responses, + new HttpCallerInfo(url, http.getProxyHostUsed(), + http.getProxyPortUsed()), + dontUseNegotiate + ); + + if (!doingNTLMp2ndStage) { + proxyAuthentication = + resetProxyAuthentication(proxyAuthentication, authhdr); + if (proxyAuthentication != null) { + redirects++; + disconnectInternal(); + continue; + } + } else { + /* in this case, only one header field will be present */ + String raw = responses.findValue ("Proxy-Authenticate"); + reset (); + if (!proxyAuthentication.setHeaders(this, + authhdr.headerParser(), raw)) { + disconnectInternal(); + throw new IOException ("Authentication failure"); + } + if (serverAuthentication != null && srvHdr != null && + !serverAuthentication.setHeaders(this, + srvHdr.headerParser(), raw)) { + disconnectInternal (); + throw new IOException ("Authentication failure"); + } + authObj = null; + doingNTLMp2ndStage = false; + continue; + } + } else { + inNegotiateProxy = false; + doingNTLMp2ndStage = false; + if (!isUserProxyAuth) + requests.remove("Proxy-Authorization"); + } + + // cache proxy authentication info + if (proxyAuthentication != null) { + // cache auth info on success, domain header not relevant. + proxyAuthentication.addToCache(); + } + + if (respCode == HTTP_UNAUTHORIZED) { + if (streaming()) { + disconnectInternal(); + throw new HttpRetryException ( + RETRY_MSG2, HTTP_UNAUTHORIZED); + } + + // Read comments labeled "Failed Negotiate" for details. + boolean dontUseNegotiate = false; + Iterator iter = responses.multiValueIterator("WWW-Authenticate"); + while (iter.hasNext()) { + String value = iter.next().trim(); + if (value.equalsIgnoreCase("Negotiate") || + value.equalsIgnoreCase("Kerberos")) { + if (!inNegotiate) { + inNegotiate = true; + } else { + dontUseNegotiate = true; + doingNTLM2ndStage = false; + serverAuthentication = null; + } + break; + } + } + + srvHdr = new AuthenticationHeader ( + "WWW-Authenticate", responses, + new HttpCallerInfo(url), + dontUseNegotiate + ); + + String raw = srvHdr.raw(); + if (!doingNTLM2ndStage) { + if ((serverAuthentication != null)&& + serverAuthentication.getAuthScheme() != NTLM) { + if (serverAuthentication.isAuthorizationStale (raw)) { + /* we can retry with the current credentials */ + disconnectWeb(); + redirects++; + requests.set(serverAuthentication.getHeaderName(), + serverAuthentication.getHeaderValue(url, method)); + currentServerCredentials = serverAuthentication; + setCookieHeader(); + continue; + } else { + serverAuthentication.removeFromCache(); + } + } + serverAuthentication = getServerAuthentication(srvHdr); + currentServerCredentials = serverAuthentication; + + if (serverAuthentication != null) { + disconnectWeb(); + redirects++; // don't let things loop ad nauseum + setCookieHeader(); + continue; + } + } else { + reset (); + /* header not used for ntlm */ + if (!serverAuthentication.setHeaders(this, null, raw)) { + disconnectWeb(); + throw new IOException ("Authentication failure"); + } + doingNTLM2ndStage = false; + authObj = null; + setCookieHeader(); + continue; + } + } + // cache server authentication info + if (serverAuthentication != null) { + // cache auth info on success + if (!(serverAuthentication instanceof DigestAuthentication) || + (domain == null)) { + if (serverAuthentication instanceof BasicAuthentication) { + // check if the path is shorter than the existing entry + String npath = AuthenticationInfo.reducePath (url.getPath()); + String opath = serverAuthentication.path; + if (!opath.startsWith (npath) || npath.length() >= opath.length()) { + /* npath is longer, there must be a common root */ + npath = BasicAuthentication.getRootPath (opath, npath); + } + // remove the entry and create a new one + BasicAuthentication a = + (BasicAuthentication) serverAuthentication.clone(); + serverAuthentication.removeFromCache(); + a.path = npath; + serverAuthentication = a; + } + serverAuthentication.addToCache(); + } else { + // what we cache is based on the domain list in the request + DigestAuthentication srv = (DigestAuthentication) + serverAuthentication; + StringTokenizer tok = new StringTokenizer (domain," "); + String realm = srv.realm; + PasswordAuthentication pw = srv.pw; + digestparams = srv.params; + while (tok.hasMoreTokens()) { + String path = tok.nextToken(); + try { + /* path could be an abs_path or a complete URI */ + URL u = new URL (url, path); + DigestAuthentication d = new DigestAuthentication ( + false, u, realm, "Digest", pw, digestparams); + d.addToCache (); + } catch (Exception e) {} + } + } + } + + // some flags should be reset to its initialized form so that + // even after a redirect the necessary checks can still be + // preformed. + inNegotiate = false; + inNegotiateProxy = false; + + //serverAuthentication = null; + doingNTLMp2ndStage = false; + doingNTLM2ndStage = false; + if (!isUserServerAuth) + requests.remove("Authorization"); + if (!isUserProxyAuth) + requests.remove("Proxy-Authorization"); + + if (respCode == HTTP_OK) { + checkResponseCredentials (false); + } else { + needToCheck = false; + } + + // a flag need to clean + needToCheck = true; + + if (followRedirect()) { + /* if we should follow a redirect, then the followRedirects() + * method will disconnect() and re-connect us to the new + * location + */ + redirects++; + + // redirecting HTTP response may have set cookie, so + // need to re-generate request header + setCookieHeader(); + + continue; + } + + try { + cl = Long.parseLong(responses.findValue("content-length")); + } catch (Exception exc) { }; + + if (method.equals("HEAD") || cl == 0 || + respCode == HTTP_NOT_MODIFIED || + respCode == HTTP_NO_CONTENT) { + + if (pi != null) { + pi.finishTracking(); + pi = null; + } + http.finished(); + http = null; + inputStream = new EmptyInputStream(); + connected = false; + } + + if (respCode == 200 || respCode == 203 || respCode == 206 || + respCode == 300 || respCode == 301 || respCode == 410) { + if (cacheHandler != null && getUseCaches()) { + // give cache a chance to save response in cache + URI uri = ParseUtil.toURI(url); + if (uri != null) { + URLConnection uconn = this; + if ("https".equalsIgnoreCase(uri.getScheme())) { + try { + // use reflection to get to the public + // HttpsURLConnection instance saved in + // DelegateHttpsURLConnection + uconn = (URLConnection)this.getClass().getField("httpsURLConnection").get(this); + } catch (IllegalAccessException | + NoSuchFieldException e) { + // ignored; use 'this' + } + } + CacheRequest cacheRequest = + cacheHandler.put(uri, uconn); + if (cacheRequest != null && http != null) { + http.setCacheRequest(cacheRequest); + inputStream = new HttpInputStream(inputStream, cacheRequest); + } + } + } + } + + if (!(inputStream instanceof HttpInputStream)) { + inputStream = new HttpInputStream(inputStream); + } + + if (respCode >= 400) { + if (respCode == 404 || respCode == 410) { + throw new FileNotFoundException(url.toString()); + } else { + throw new IOException("Server returned HTTP" + + " response code: " + respCode + " for URL: " + + url.toString()); + } + } + poster = null; + strOutputStream = null; + return inputStream; + } while (redirects < maxRedirects); + + throw new ProtocolException("Server redirected too many " + + " times ("+ redirects + ")"); + } catch (RuntimeException e) { + disconnectInternal(); + rememberedException = e; + throw e; + } catch (IOException e) { + rememberedException = e; + + // buffer the error stream if bytes < 4k + // and it can be buffered within 1 second + String te = responses.findValue("Transfer-Encoding"); + if (http != null && http.isKeepingAlive() && enableESBuffer && + (cl > 0 || (te != null && te.equalsIgnoreCase("chunked")))) { + errorStream = ErrorStream.getErrorStream(inputStream, cl, http); + } + throw e; + } finally { + if (proxyAuthKey != null) { + AuthenticationInfo.endAuthRequest(proxyAuthKey); + } + if (serverAuthKey != null) { + AuthenticationInfo.endAuthRequest(serverAuthKey); + } + } + } + + /* + * Creates a chained exception that has the same type as + * original exception and with the same message. Right now, + * there is no convenient APIs for doing so. + */ + private IOException getChainedException(final IOException rememberedException) { + try { + final Object[] args = { rememberedException.getMessage() }; + IOException chainedException = + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public IOException run() throws Exception { + return (IOException) + rememberedException.getClass() + .getConstructor(new Class[] { String.class }) + .newInstance(args); + } + }); + chainedException.initCause(rememberedException); + return chainedException; + } catch (Exception ignored) { + return rememberedException; + } + } + + @Override + public InputStream getErrorStream() { + if (connected && responseCode >= 400) { + // Client Error 4xx and Server Error 5xx + if (errorStream != null) { + return errorStream; + } else if (inputStream != null) { + return inputStream; + } + } + return null; + } + + /** + * set or reset proxy authentication info in request headers + * after receiving a 407 error. In the case of NTLM however, + * receiving a 407 is normal and we just skip the stale check + * because ntlm does not support this feature. + */ + private AuthenticationInfo + resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException { + if ((proxyAuthentication != null )&& + proxyAuthentication.getAuthScheme() != NTLM) { + String raw = auth.raw(); + if (proxyAuthentication.isAuthorizationStale (raw)) { + /* we can retry with the current credentials */ + String value; + if (proxyAuthentication instanceof DigestAuthentication) { + DigestAuthentication digestProxy = (DigestAuthentication) + proxyAuthentication; + if (tunnelState() == TunnelState.SETUP) { + value = digestProxy.getHeaderValue(connectRequestURI(url), HTTP_CONNECT); + } else { + value = digestProxy.getHeaderValue(getRequestURI(), method); + } + } else { + value = proxyAuthentication.getHeaderValue(url, method); + } + requests.set(proxyAuthentication.getHeaderName(), value); + currentProxyCredentials = proxyAuthentication; + return proxyAuthentication; + } else { + proxyAuthentication.removeFromCache(); + } + } + proxyAuthentication = getHttpProxyAuthentication(auth); + currentProxyCredentials = proxyAuthentication; + return proxyAuthentication; + } + + /** + * Returns the tunnel state. + * + * @return the state + */ + TunnelState tunnelState() { + return tunnelState; + } + + /** + * Set the tunneling status. + * + * @param the state + */ + public void setTunnelState(TunnelState tunnelState) { + this.tunnelState = tunnelState; + } + + /** + * establish a tunnel through proxy server + */ + public synchronized void doTunneling() throws IOException { + int retryTunnel = 0; + String statusLine = ""; + int respCode = 0; + AuthenticationInfo proxyAuthentication = null; + String proxyHost = null; + int proxyPort = -1; + + // save current requests so that they can be restored after tunnel is setup. + MessageHeader savedRequests = requests; + requests = new MessageHeader(); + + // Read comments labeled "Failed Negotiate" for details. + boolean inNegotiateProxy = false; + + try { + /* Actively setting up a tunnel */ + setTunnelState(TunnelState.SETUP); + + do { + if (!checkReuseConnection()) { + proxiedConnect(url, proxyHost, proxyPort, false); + } + // send the "CONNECT" request to establish a tunnel + // through proxy server + sendCONNECTRequest(); + responses.reset(); + + // There is no need to track progress in HTTP Tunneling, + // so ProgressSource is null. + http.parseHTTP(responses, null, this); + + /* Log the response to the CONNECT */ + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(responses.toString()); + } + + if (responses.filterNTLMResponses("Proxy-Authenticate")) { + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(">>>> Headers are filtered"); + logger.fine(responses.toString()); + } + } + + statusLine = responses.getValue(0); + StringTokenizer st = new StringTokenizer(statusLine); + st.nextToken(); + respCode = Integer.parseInt(st.nextToken().trim()); + if (respCode == HTTP_PROXY_AUTH) { + // Read comments labeled "Failed Negotiate" for details. + boolean dontUseNegotiate = false; + Iterator iter = responses.multiValueIterator("Proxy-Authenticate"); + while (iter.hasNext()) { + String value = iter.next().trim(); + if (value.equalsIgnoreCase("Negotiate") || + value.equalsIgnoreCase("Kerberos")) { + if (!inNegotiateProxy) { + inNegotiateProxy = true; + } else { + dontUseNegotiate = true; + doingNTLMp2ndStage = false; + proxyAuthentication = null; + } + break; + } + } + + AuthenticationHeader authhdr = new AuthenticationHeader ( + "Proxy-Authenticate", responses, + new HttpCallerInfo(url, http.getProxyHostUsed(), + http.getProxyPortUsed()), + dontUseNegotiate + ); + if (!doingNTLMp2ndStage) { + proxyAuthentication = + resetProxyAuthentication(proxyAuthentication, authhdr); + if (proxyAuthentication != null) { + proxyHost = http.getProxyHostUsed(); + proxyPort = http.getProxyPortUsed(); + disconnectInternal(); + retryTunnel++; + continue; + } + } else { + String raw = responses.findValue ("Proxy-Authenticate"); + reset (); + if (!proxyAuthentication.setHeaders(this, + authhdr.headerParser(), raw)) { + disconnectInternal(); + throw new IOException ("Authentication failure"); + } + authObj = null; + doingNTLMp2ndStage = false; + continue; + } + } + // cache proxy authentication info + if (proxyAuthentication != null) { + // cache auth info on success, domain header not relevant. + proxyAuthentication.addToCache(); + } + + if (respCode == HTTP_OK) { + setTunnelState(TunnelState.TUNNELING); + break; + } + // we don't know how to deal with other response code + // so disconnect and report error + disconnectInternal(); + setTunnelState(TunnelState.NONE); + break; + } while (retryTunnel < maxRedirects); + + if (retryTunnel >= maxRedirects || (respCode != HTTP_OK)) { + throw new IOException("Unable to tunnel through proxy."+ + " Proxy returns \"" + + statusLine + "\""); + } + } finally { + if (proxyAuthKey != null) { + AuthenticationInfo.endAuthRequest(proxyAuthKey); + } + } + + // restore original request headers + requests = savedRequests; + + // reset responses + responses.reset(); + } + + static String connectRequestURI(URL url) { + String host = url.getHost(); + int port = url.getPort(); + port = port != -1 ? port : url.getDefaultPort(); + + return host + ":" + port; + } + + /** + * send a CONNECT request for establishing a tunnel to proxy server + */ + private void sendCONNECTRequest() throws IOException { + int port = url.getPort(); + + requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url) + + " " + httpVersion, null); + requests.setIfNotSet("User-Agent", userAgent); + + String host = url.getHost(); + if (port != -1 && port != url.getDefaultPort()) { + host += ":" + String.valueOf(port); + } + requests.setIfNotSet("Host", host); + + // Not really necessary for a tunnel, but can't hurt + requests.setIfNotSet("Accept", acceptString); + + if (http.getHttpKeepAliveSet()) { + requests.setIfNotSet("Proxy-Connection", "keep-alive"); + } + + setPreemptiveProxyAuthentication(requests); + + /* Log the CONNECT request */ + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine(requests.toString()); + } + + http.writeRequests(requests, null); + } + + /** + * Sets pre-emptive proxy authentication in header + */ + private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOException { + AuthenticationInfo pauth + = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(), + http.getProxyPortUsed()); + if (pauth != null && pauth.supportsPreemptiveAuthorization()) { + String value; + if (pauth instanceof DigestAuthentication) { + DigestAuthentication digestProxy = (DigestAuthentication) pauth; + if (tunnelState() == TunnelState.SETUP) { + value = digestProxy + .getHeaderValue(connectRequestURI(url), HTTP_CONNECT); + } else { + value = digestProxy.getHeaderValue(getRequestURI(), method); + } + } else { + value = pauth.getHeaderValue(url, method); + } + + // Sets "Proxy-authorization" + requests.set(pauth.getHeaderName(), value); + currentProxyCredentials = pauth; + } + } + + /** + * Gets the authentication for an HTTP proxy, and applies it to + * the connection. + */ + @SuppressWarnings("fallthrough") + private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) { + /* get authorization from authenticator */ + AuthenticationInfo ret = null; + String raw = authhdr.raw(); + String host = http.getProxyHostUsed(); + int port = http.getProxyPortUsed(); + if (host != null && authhdr.isPresent()) { + HeaderParser p = authhdr.headerParser(); + String realm = p.findValue("realm"); + String scheme = authhdr.scheme(); + AuthScheme authScheme = UNKNOWN; + if ("basic".equalsIgnoreCase(scheme)) { + authScheme = BASIC; + } else if ("digest".equalsIgnoreCase(scheme)) { + authScheme = DIGEST; + } else if ("ntlm".equalsIgnoreCase(scheme)) { + authScheme = NTLM; + doingNTLMp2ndStage = true; + } else if ("Kerberos".equalsIgnoreCase(scheme)) { + authScheme = KERBEROS; + doingNTLMp2ndStage = true; + } else if ("Negotiate".equalsIgnoreCase(scheme)) { + authScheme = NEGOTIATE; + doingNTLMp2ndStage = true; + } + + if (realm == null) + realm = ""; + proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm, authScheme); + ret = AuthenticationInfo.getProxyAuth(proxyAuthKey); + if (ret == null) { + switch (authScheme) { + case BASIC: + InetAddress addr = null; + try { + final String finalHost = host; + addr = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InetAddress run() + throws UnknownHostException { + return InetAddress.getByName(finalHost); + } + }); + } catch (PrivilegedActionException ignored) { + // User will have an unknown host. + } + PasswordAuthentication a = + privilegedRequestPasswordAuthentication( + host, addr, port, "http", + realm, scheme, url, RequestorType.PROXY); + if (a != null) { + ret = new BasicAuthentication(true, host, port, realm, a); + } + break; + case DIGEST: + a = privilegedRequestPasswordAuthentication( + host, null, port, url.getProtocol(), + realm, scheme, url, RequestorType.PROXY); + if (a != null) { + DigestAuthentication.Parameters params = + new DigestAuthentication.Parameters(); + ret = new DigestAuthentication(true, host, port, realm, + scheme, a, params); + } + break; + case NTLM: + if (NTLMAuthenticationProxy.supported) { + /* tryTransparentNTLMProxy will always be true the first + * time around, but verify that the platform supports it + * otherwise don't try. */ + if (tryTransparentNTLMProxy) { + tryTransparentNTLMProxy = + NTLMAuthenticationProxy.supportsTransparentAuth; + /* If the platform supports transparent authentication + * then normally it's ok to do transparent auth to a proxy + * because we generally trust proxies (chosen by the user) + * But not in the case of 305 response where the server + * chose it. */ + if (tryTransparentNTLMProxy && useProxyResponseCode) { + tryTransparentNTLMProxy = false; + } + } + a = null; + if (tryTransparentNTLMProxy) { + logger.finest("Trying Transparent NTLM authentication"); + } else { + a = privilegedRequestPasswordAuthentication( + host, null, port, url.getProtocol(), + "", scheme, url, RequestorType.PROXY); + } + /* If we are not trying transparent authentication then + * we need to have a PasswordAuthentication instance. For + * transparent authentication (Windows only) the username + * and password will be picked up from the current logged + * on users credentials. + */ + if (tryTransparentNTLMProxy || + (!tryTransparentNTLMProxy && a != null)) { + ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a); + } + + /* set to false so that we do not try again */ + tryTransparentNTLMProxy = false; + } + break; + case NEGOTIATE: + ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate")); + break; + case KERBEROS: + ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos")); + break; + case UNKNOWN: + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Unknown/Unsupported authentication scheme: " + scheme); + } + /*fall through*/ + default: + throw new AssertionError("should not reach here"); + } + } + // For backwards compatibility, we also try defaultAuth + // REMIND: Get rid of this for JDK2.0. + + if (ret == null && defaultAuth != null + && defaultAuth.schemeSupported(scheme)) { + try { + URL u = new URL("http", host, port, "/"); + String a = defaultAuth.authString(u, scheme, realm); + if (a != null) { + ret = new BasicAuthentication (true, host, port, realm, a); + // not in cache by default - cache on success + } + } catch (MalformedURLException ignored) { + } + } + if (ret != null) { + if (!ret.setHeaders(this, p, raw)) { + ret = null; + } + } + } + if (logger.isLoggable(PlatformLogger.Level.FINER)) { + logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null")); + } + return ret; + } + + /** + * Gets the authentication for an HTTP server, and applies it to + * the connection. + * @param authHdr the AuthenticationHeader which tells what auth scheme is + * preferred. + */ + @SuppressWarnings("fallthrough") + private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) { + /* get authorization from authenticator */ + AuthenticationInfo ret = null; + String raw = authhdr.raw(); + /* When we get an NTLM auth from cache, don't set any special headers */ + if (authhdr.isPresent()) { + HeaderParser p = authhdr.headerParser(); + String realm = p.findValue("realm"); + String scheme = authhdr.scheme(); + AuthScheme authScheme = UNKNOWN; + if ("basic".equalsIgnoreCase(scheme)) { + authScheme = BASIC; + } else if ("digest".equalsIgnoreCase(scheme)) { + authScheme = DIGEST; + } else if ("ntlm".equalsIgnoreCase(scheme)) { + authScheme = NTLM; + doingNTLM2ndStage = true; + } else if ("Kerberos".equalsIgnoreCase(scheme)) { + authScheme = KERBEROS; + doingNTLM2ndStage = true; + } else if ("Negotiate".equalsIgnoreCase(scheme)) { + authScheme = NEGOTIATE; + doingNTLM2ndStage = true; + } + + domain = p.findValue ("domain"); + if (realm == null) + realm = ""; + serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme); + ret = AuthenticationInfo.getServerAuth(serverAuthKey); + InetAddress addr = null; + if (ret == null) { + try { + addr = InetAddress.getByName(url.getHost()); + } catch (UnknownHostException ignored) { + // User will have addr = null + } + } + // replacing -1 with default port for a protocol + int port = url.getPort(); + if (port == -1) { + port = url.getDefaultPort(); + } + if (ret == null) { + switch(authScheme) { + case KERBEROS: + ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos")); + break; + case NEGOTIATE: + ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate")); + break; + case BASIC: + PasswordAuthentication a = + privilegedRequestPasswordAuthentication( + url.getHost(), addr, port, url.getProtocol(), + realm, scheme, url, RequestorType.SERVER); + if (a != null) { + ret = new BasicAuthentication(false, url, realm, a); + } + break; + case DIGEST: + a = privilegedRequestPasswordAuthentication( + url.getHost(), addr, port, url.getProtocol(), + realm, scheme, url, RequestorType.SERVER); + if (a != null) { + digestparams = new DigestAuthentication.Parameters(); + ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams); + } + break; + case NTLM: + if (NTLMAuthenticationProxy.supported) { + URL url1; + try { + url1 = new URL (url, "/"); /* truncate the path */ + } catch (Exception e) { + url1 = url; + } + + /* tryTransparentNTLMServer will always be true the first + * time around, but verify that the platform supports it + * otherwise don't try. */ + if (tryTransparentNTLMServer) { + tryTransparentNTLMServer = + NTLMAuthenticationProxy.supportsTransparentAuth; + /* If the platform supports transparent authentication + * then check if we are in a secure environment + * whether, or not, we should try transparent authentication.*/ + if (tryTransparentNTLMServer) { + tryTransparentNTLMServer = + NTLMAuthenticationProxy.isTrustedSite(url); + } + } + a = null; + if (tryTransparentNTLMServer) { + logger.finest("Trying Transparent NTLM authentication"); + } else { + a = privilegedRequestPasswordAuthentication( + url.getHost(), addr, port, url.getProtocol(), + "", scheme, url, RequestorType.SERVER); + } + + /* If we are not trying transparent authentication then + * we need to have a PasswordAuthentication instance. For + * transparent authentication (Windows only) the username + * and password will be picked up from the current logged + * on users credentials. + */ + if (tryTransparentNTLMServer || + (!tryTransparentNTLMServer && a != null)) { + ret = NTLMAuthenticationProxy.proxy.create(false, url1, a); + } + + /* set to false so that we do not try again */ + tryTransparentNTLMServer = false; + } + break; + case UNKNOWN: + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("Unknown/Unsupported authentication scheme: " + scheme); + } + /*fall through*/ + default: + throw new AssertionError("should not reach here"); + } + } + + // For backwards compatibility, we also try defaultAuth + // REMIND: Get rid of this for JDK2.0. + + if (ret == null && defaultAuth != null + && defaultAuth.schemeSupported(scheme)) { + String a = defaultAuth.authString(url, scheme, realm); + if (a != null) { + ret = new BasicAuthentication (false, url, realm, a); + // not in cache by default - cache on success + } + } + + if (ret != null ) { + if (!ret.setHeaders(this, p, raw)) { + ret = null; + } + } + } + if (logger.isLoggable(PlatformLogger.Level.FINER)) { + logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null")); + } + return ret; + } + + /* inclose will be true if called from close(), in which case we + * force the call to check because this is the last chance to do so. + * If not in close(), then the authentication info could arrive in a trailer + * field, which we have not read yet. + */ + private void checkResponseCredentials (boolean inClose) throws IOException { + try { + if (!needToCheck) + return; + if ((validateProxy && currentProxyCredentials != null) && + (currentProxyCredentials instanceof DigestAuthentication)) { + String raw = responses.findValue ("Proxy-Authentication-Info"); + if (inClose || (raw != null)) { + DigestAuthentication da = (DigestAuthentication) + currentProxyCredentials; + da.checkResponse (raw, method, getRequestURI()); + currentProxyCredentials = null; + } + } + if ((validateServer && currentServerCredentials != null) && + (currentServerCredentials instanceof DigestAuthentication)) { + String raw = responses.findValue ("Authentication-Info"); + if (inClose || (raw != null)) { + DigestAuthentication da = (DigestAuthentication) + currentServerCredentials; + da.checkResponse (raw, method, url); + currentServerCredentials = null; + } + } + if ((currentServerCredentials==null) && (currentProxyCredentials == null)) { + needToCheck = false; + } + } catch (IOException e) { + disconnectInternal(); + connected = false; + throw e; + } + } + + /* The request URI used in the request line for this request. + * Also, needed for digest authentication + */ + + String requestURI = null; + + String getRequestURI() throws IOException { + if (requestURI == null) { + requestURI = http.getURLFile(); + } + return requestURI; + } + + /* Tells us whether to follow a redirect. If so, it + * closes the connection (break any keep-alive) and + * resets the url, re-connects, and resets the request + * property. + */ + private boolean followRedirect() throws IOException { + if (!getInstanceFollowRedirects()) { + return false; + } + + final int stat = getResponseCode(); + if (stat < 300 || stat > 307 || stat == 306 + || stat == HTTP_NOT_MODIFIED) { + return false; + } + final String loc = getHeaderField("Location"); + if (loc == null) { + /* this should be present - if not, we have no choice + * but to go forward w/ the response we got + */ + return false; + } + + URL locUrl; + try { + locUrl = new URL(loc); + if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) { + return false; + } + + } catch (MalformedURLException mue) { + // treat loc as a relative URI to conform to popular browsers + locUrl = new URL(url, loc); + } + + final URL locUrl0 = locUrl; + socketPermission = null; // force recalculation + SocketPermission p = URLtoSocketPermission(locUrl); + + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Boolean run() throws IOException { + return followRedirect0(loc, stat, locUrl0); + } + }, null, p + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + // run without additional permission + return followRedirect0(loc, stat, locUrl); + } + } + + /* Tells us whether to follow a redirect. If so, it + * closes the connection (break any keep-alive) and + * resets the url, re-connects, and resets the request + * property. + */ + private boolean followRedirect0(String loc, int stat, URL locUrl) + throws IOException + { + disconnectInternal(); + if (streaming()) { + throw new HttpRetryException (RETRY_MSG3, stat, loc); + } + if (logger.isLoggable(PlatformLogger.Level.FINE)) { + logger.fine("Redirected from " + url + " to " + locUrl); + } + + // clear out old response headers!!!! + responses = new MessageHeader(); + if (stat == HTTP_USE_PROXY) { + /* This means we must re-request the resource through the + * proxy denoted in the "Location:" field of the response. + * Judging by the spec, the string in the Location header + * _should_ denote a URL - let's hope for "http://my.proxy.org" + * Make a new HttpClient to the proxy, using HttpClient's + * Instance-specific proxy fields, but note we're still fetching + * the same URL. + */ + String proxyHost = locUrl.getHost(); + int proxyPort = locUrl.getPort(); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkConnect(proxyHost, proxyPort); + } + + setProxiedClient (url, proxyHost, proxyPort); + requests.set(0, method + " " + getRequestURI()+" " + + httpVersion, null); + connected = true; + // need to remember this in case NTLM proxy authentication gets + // used. We can't use transparent authentication when user + // doesn't know about proxy. + useProxyResponseCode = true; + } else { + // maintain previous headers, just change the name + // of the file we're getting + url = locUrl; + requestURI = null; // force it to be recalculated + if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) { + /* The HTTP/1.1 spec says that a redirect from a POST + * *should not* be immediately turned into a GET, and + * that some HTTP/1.0 clients incorrectly did this. + * Correct behavior redirects a POST to another POST. + * Unfortunately, since most browsers have this incorrect + * behavior, the web works this way now. Typical usage + * seems to be: + * POST a login code or passwd to a web page. + * after validation, the server redirects to another + * (welcome) page + * The second request is (erroneously) expected to be GET + * + * We will do the incorrect thing (POST-->GET) by default. + * We will provide the capability to do the "right" thing + * (POST-->POST) by a system property, "http.strictPostRedirect=true" + */ + + requests = new MessageHeader(); + setRequests = false; + super.setRequestMethod("GET"); // avoid the connecting check + poster = null; + if (!checkReuseConnection()) + connect(); + } else { + if (!checkReuseConnection()) + connect(); + /* Even after a connect() call, http variable still can be + * null, if a ResponseCache has been installed and it returns + * a non-null CacheResponse instance. So check nullity before using it. + * + * And further, if http is null, there's no need to do anything + * about request headers because successive http session will use + * cachedInputStream/cachedHeaders anyway, which is returned by + * CacheResponse. + */ + if (http != null) { + requests.set(0, method + " " + getRequestURI()+" " + + httpVersion, null); + int port = url.getPort(); + String host = url.getHost(); + if (port != -1 && port != url.getDefaultPort()) { + host += ":" + String.valueOf(port); + } + requests.set("Host", host); + } + } + } + return true; + } + + /* dummy byte buffer for reading off socket prior to closing */ + byte[] cdata = new byte [128]; + + /** + * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance + */ + private void reset() throws IOException { + http.reuse = true; + /* must save before calling close */ + reuseClient = http; + InputStream is = http.getInputStream(); + if (!method.equals("HEAD")) { + try { + /* we want to read the rest of the response without using the + * hurry mechanism, because that would close the connection + * if everything is not available immediately + */ + if ((is instanceof ChunkedInputStream) || + (is instanceof MeteredStream)) { + /* reading until eof will not block */ + while (is.read (cdata) > 0) {} + } else { + /* raw stream, which will block on read, so only read + * the expected number of bytes, probably 0 + */ + long cl = 0; + int n = 0; + String cls = responses.findValue ("Content-Length"); + if (cls != null) { + try { + cl = Long.parseLong (cls); + } catch (NumberFormatException e) { + cl = 0; + } + } + for (long i=0; i cookies = access.parse(value); + boolean multipleCookies = false; + for (HttpCookie cookie : cookies) { + // skip HttpOnly cookies + if (cookie.isHttpOnly()) + continue; + if (multipleCookies) + retValue.append(','); // RFC 2965, comma separated + retValue.append(access.header(cookie)); + multipleCookies = true; + } + + return retValue.length() == 0 ? "" : retValue.toString(); + } + + return value; + } + + // Cache the filtered response headers so that they don't need + // to be generated for every getHeaderFields() call. + private Map> filteredHeaders; // null + + private Map> getFilteredHeaderFields() { + if (filteredHeaders != null) + return filteredHeaders; + + Map> headers, tmpMap = new HashMap<>(); + + if (cachedHeaders != null) + headers = cachedHeaders.getHeaders(); + else + headers = responses.getHeaders(); + + for (Map.Entry> e: headers.entrySet()) { + String key = e.getKey(); + List values = e.getValue(), filteredVals = new ArrayList<>(); + for (String value : values) { + String fVal = filterHeaderField(key, value); + if (fVal != null) + filteredVals.add(fVal); + } + if (!filteredVals.isEmpty()) + tmpMap.put(key, Collections.unmodifiableList(filteredVals)); + } + + return filteredHeaders = Collections.unmodifiableMap(tmpMap); + } + + /** + * Gets a header field by name. Returns null if not known. + * @param name the name of the header field + */ + @Override + public String getHeaderField(String name) { + try { + getInputStream(); + } catch (IOException e) {} + + if (cachedHeaders != null) { + return filterHeaderField(name, cachedHeaders.findValue(name)); + } + + return filterHeaderField(name, responses.findValue(name)); + } + + /** + * Returns an unmodifiable Map of the header fields. + * The Map keys are Strings that represent the + * response-header field names. Each Map value is an + * unmodifiable List of Strings that represents + * the corresponding field values. + * + * @return a Map of header fields + * @since 1.4 + */ + @Override + public Map> getHeaderFields() { + try { + getInputStream(); + } catch (IOException e) {} + + return getFilteredHeaderFields(); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + @Override + public String getHeaderField(int n) { + try { + getInputStream(); + } catch (IOException e) {} + + if (cachedHeaders != null) { + return filterHeaderField(cachedHeaders.getKey(n), + cachedHeaders.getValue(n)); + } + return filterHeaderField(responses.getKey(n), responses.getValue(n)); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + @Override + public String getHeaderFieldKey(int n) { + try { + getInputStream(); + } catch (IOException e) {} + + if (cachedHeaders != null) { + return cachedHeaders.getKey(n); + } + + return responses.getKey(n); + } + + /** + * Sets request property. If a property with the key already + * exists, overwrite its value with the new value. + * @param value the value to be set + */ + @Override + public synchronized void setRequestProperty(String key, String value) { + if (connected || connecting) + throw new IllegalStateException("Already connected"); + if (key == null) + throw new NullPointerException ("key is null"); + + if (isExternalMessageHeaderAllowed(key, value)) { + requests.set(key, value); + if (!key.equalsIgnoreCase("Content-Type")) { + userHeaders.set(key, value); + } + } + } + + MessageHeader getUserSetHeaders() { + return userHeaders; + } + + /** + * Adds a general request property specified by a + * key-value pair. This method will not overwrite + * existing values associated with the same key. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + * @see #getRequestProperties(String) + * @since 1.4 + */ + @Override + public synchronized void addRequestProperty(String key, String value) { + if (connected || connecting) + throw new IllegalStateException("Already connected"); + if (key == null) + throw new NullPointerException ("key is null"); + + if (isExternalMessageHeaderAllowed(key, value)) { + requests.add(key, value); + if (!key.equalsIgnoreCase("Content-Type")) { + userHeaders.add(key, value); + } + } + } + + // + // Set a property for authentication. This can safely disregard + // the connected test. + // + public void setAuthenticationProperty(String key, String value) { + checkMessageHeader(key, value); + requests.set(key, value); + } + + @Override + public synchronized String getRequestProperty (String key) { + if (key == null) { + return null; + } + + // don't return headers containing security sensitive information + for (int i=0; i < EXCLUDE_HEADERS.length; i++) { + if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) { + return null; + } + } + if (!setUserCookies) { + if (key.equalsIgnoreCase("Cookie")) { + return userCookies; + } + if (key.equalsIgnoreCase("Cookie2")) { + return userCookies2; + } + } + return requests.findValue(key); + } + + /** + * Returns an unmodifiable Map of general request + * properties for this connection. The Map keys + * are Strings that represent the request-header + * field names. Each Map value is a unmodifiable List + * of Strings that represents the corresponding + * field values. + * + * @return a Map of the general request properties for this connection. + * @throws IllegalStateException if already connected + * @since 1.4 + */ + @Override + public synchronized Map> getRequestProperties() { + if (connected) + throw new IllegalStateException("Already connected"); + + // exclude headers containing security-sensitive info + if (setUserCookies) { + return requests.getHeaders(EXCLUDE_HEADERS); + } + /* + * The cookies in the requests message headers may have + * been modified. Use the saved user cookies instead. + */ + Map> userCookiesMap = null; + if (userCookies != null || userCookies2 != null) { + userCookiesMap = new HashMap<>(); + if (userCookies != null) { + userCookiesMap.put("Cookie", Arrays.asList(userCookies)); + } + if (userCookies2 != null) { + userCookiesMap.put("Cookie2", Arrays.asList(userCookies2)); + } + } + return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap); + } + + @Override + public void setConnectTimeout(int timeout) { + if (timeout < 0) + throw new IllegalArgumentException("timeouts can't be negative"); + connectTimeout = timeout; + } + + + /** + * Returns setting for connect timeout. + *

    + * 0 return implies that the option is disabled + * (i.e., timeout of infinity). + * + * @return an int that indicates the connect timeout + * value in milliseconds + * @see URLConnection#setConnectTimeout(int) + * @see URLConnection#connect() + * @since 1.5 + */ + @Override + public int getConnectTimeout() { + return (connectTimeout < 0 ? 0 : connectTimeout); + } + + /** + * Sets the read timeout to a specified timeout, in + * milliseconds. A non-zero value specifies the timeout when + * reading from Input stream when a connection is established to a + * resource. If the timeout expires before there is data available + * for read, a java.net.SocketTimeoutException is raised. A + * timeout of zero is interpreted as an infinite timeout. + * + *

    Some non-standard implementation of this method ignores the + * specified timeout. To see the read timeout set, please call + * getReadTimeout(). + * + * @param timeout an int that specifies the timeout + * value to be used in milliseconds + * @throws IllegalArgumentException if the timeout parameter is negative + * + * @see java.net.URLConnectiongetReadTimeout() + * @see InputStream#read() + * @since 1.5 + */ + @Override + public void setReadTimeout(int timeout) { + if (timeout < 0) + throw new IllegalArgumentException("timeouts can't be negative"); + readTimeout = timeout; + } + + /** + * Returns setting for read timeout. 0 return implies that the + * option is disabled (i.e., timeout of infinity). + * + * @return an int that indicates the read timeout + * value in milliseconds + * + * @see URLConnection#setReadTimeout(int) + * @see InputStream#read() + * @since 1.5 + */ + @Override + public int getReadTimeout() { + return readTimeout < 0 ? 0 : readTimeout; + } + + public CookieHandler getCookieHandler() { + return cookieHandler; + } + + String getMethod() { + return method; + } + + private MessageHeader mapToMessageHeader(Map> map) { + MessageHeader headers = new MessageHeader(); + if (map == null || map.isEmpty()) { + return headers; + } + for (Map.Entry> entry : map.entrySet()) { + String key = entry.getKey(); + List values = entry.getValue(); + for (String value : values) { + if (key == null) { + headers.prepend(key, value); + } else { + headers.add(key, value); + } + } + } + return headers; + } + + /* The purpose of this wrapper is just to capture the close() call + * so we can check authentication information that may have + * arrived in a Trailer field + */ + class HttpInputStream extends FilterInputStream { + private CacheRequest cacheRequest; + private OutputStream outputStream; + private boolean marked = false; + private int inCache = 0; + private int markCount = 0; + private boolean closed; // false + + public HttpInputStream (InputStream is) { + super (is); + this.cacheRequest = null; + this.outputStream = null; + } + + public HttpInputStream (InputStream is, CacheRequest cacheRequest) { + super (is); + this.cacheRequest = cacheRequest; + try { + this.outputStream = cacheRequest.getBody(); + } catch (IOException ioex) { + this.cacheRequest.abort(); + this.cacheRequest = null; + this.outputStream = null; + } + } + + /** + * Marks the current position in this input stream. A subsequent + * call to the reset method repositions this stream at + * the last marked position so that subsequent reads re-read the same + * bytes. + *

    + * The readlimit argument tells this input stream to + * allow that many bytes to be read before the mark position gets + * invalidated. + *

    + * This method simply performs in.mark(readlimit). + * + * @param readlimit the maximum limit of bytes that can be read before + * the mark position becomes invalid. + * @see FilterInputStream#in + * @see FilterInputStream#reset() + */ + @Override + public synchronized void mark(int readlimit) { + super.mark(readlimit); + if (cacheRequest != null) { + marked = true; + markCount = 0; + } + } + + /** + * Repositions this stream to the position at the time the + * mark method was last called on this input stream. + *

    + * This method + * simply performs in.reset(). + *

    + * Stream marks are intended to be used in + * situations where you need to read ahead a little to see what's in + * the stream. Often this is most easily done by invoking some + * general parser. If the stream is of the type handled by the + * parse, it just chugs along happily. If the stream is not of + * that type, the parser should toss an exception when it fails. + * If this happens within readlimit bytes, it allows the outer + * code to reset the stream and try another parser. + * + * @exception IOException if the stream has not been marked or if the + * mark has been invalidated. + * @see FilterInputStream#in + * @see FilterInputStream#mark(int) + */ + @Override + public synchronized void reset() throws IOException { + super.reset(); + if (cacheRequest != null) { + marked = false; + inCache += markCount; + } + } + + private void ensureOpen() throws IOException { + if (closed) + throw new IOException("stream is closed"); + } + + @Override + public int read() throws IOException { + ensureOpen(); + try { + byte[] b = new byte[1]; + int ret = read(b); + return (ret == -1? ret : (b[0] & 0x00FF)); + } catch (IOException ioex) { + if (cacheRequest != null) { + cacheRequest.abort(); + } + throw ioex; + } + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + ensureOpen(); + try { + int newLen = super.read(b, off, len); + int nWrite; + // write to cache + if (inCache > 0) { + if (inCache >= newLen) { + inCache -= newLen; + nWrite = 0; + } else { + nWrite = newLen - inCache; + inCache = 0; + } + } else { + nWrite = newLen; + } + if (nWrite > 0 && outputStream != null) + outputStream.write(b, off + (newLen-nWrite), nWrite); + if (marked) { + markCount += newLen; + } + return newLen; + } catch (IOException ioex) { + if (cacheRequest != null) { + cacheRequest.abort(); + } + throw ioex; + } + } + + /* skip() calls read() in order to ensure that entire response gets + * cached. same implementation as InputStream.skip */ + + private byte[] skipBuffer; + private static final int SKIP_BUFFER_SIZE = 8096; + + @Override + public long skip (long n) throws IOException { + ensureOpen(); + long remaining = n; + int nr; + if (skipBuffer == null) + skipBuffer = new byte[SKIP_BUFFER_SIZE]; + + byte[] localSkipBuffer = skipBuffer; + + if (n <= 0) { + return 0; + } + + while (remaining > 0) { + nr = read(localSkipBuffer, 0, + (int) Math.min(SKIP_BUFFER_SIZE, remaining)); + if (nr < 0) { + break; + } + remaining -= nr; + } + + return n - remaining; + } + + @Override + public void close () throws IOException { + if (closed) + return; + + try { + if (outputStream != null) { + if (read() != -1) { + cacheRequest.abort(); + } else { + outputStream.close(); + } + } + super.close (); + } catch (IOException ioex) { + if (cacheRequest != null) { + cacheRequest.abort(); + } + throw ioex; + } finally { + closed = true; + HttpURLConnection.this.http = null; + checkResponseCredentials (true); + } + } + } + + class StreamingOutputStream extends FilterOutputStream { + + long expected; + long written; + boolean closed; + boolean error; + IOException errorExcp; + + /** + * expectedLength == -1 if the stream is chunked + * expectedLength > 0 if the stream is fixed content-length + * In the 2nd case, we make sure the expected number of + * of bytes are actually written + */ + StreamingOutputStream (OutputStream os, long expectedLength) { + super (os); + expected = expectedLength; + written = 0L; + closed = false; + error = false; + } + + @Override + public void write (int b) throws IOException { + checkError(); + written ++; + if (expected != -1L && written > expected) { + throw new IOException ("too many bytes written"); + } + out.write (b); + } + + @Override + public void write (byte[] b) throws IOException { + write (b, 0, b.length); + } + + @Override + public void write (byte[] b, int off, int len) throws IOException { + checkError(); + written += len; + if (expected != -1L && written > expected) { + out.close (); + throw new IOException ("too many bytes written"); + } + out.write (b, off, len); + } + + void checkError () throws IOException { + if (closed) { + throw new IOException ("Stream is closed"); + } + if (error) { + throw errorExcp; + } + if (((PrintStream)out).checkError()) { + throw new IOException("Error writing request body to server"); + } + } + + /* this is called to check that all the bytes + * that were supposed to be written were written + * and that the stream is now closed(). + */ + boolean writtenOK () { + return closed && ! error; + } + + @Override + public void close () throws IOException { + if (closed) { + return; + } + closed = true; + if (expected != -1L) { + /* not chunked */ + if (written != expected) { + error = true; + errorExcp = new IOException ("insufficient data written"); + out.close (); + throw errorExcp; + } + super.flush(); /* can't close the socket */ + } else { + /* chunked */ + super.close (); /* force final chunk to be written */ + /* trailing \r\n */ + OutputStream o = http.getOutputStream(); + o.write ('\r'); + o.write ('\n'); + o.flush(); + } + } + } + + + static class ErrorStream extends InputStream { + ByteBuffer buffer; + InputStream is; + + private ErrorStream(ByteBuffer buf) { + buffer = buf; + is = null; + } + + private ErrorStream(ByteBuffer buf, InputStream is) { + buffer = buf; + this.is = is; + } + + // when this method is called, it's either the case that cl > 0, or + // if chunk-encoded, cl = -1; in other words, cl can't be 0 + public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) { + + // cl can't be 0; this following is here for extra precaution + if (cl == 0) { + return null; + } + + try { + // set SO_TIMEOUT to 1/5th of the total timeout + // remember the old timeout value so that we can restore it + int oldTimeout = http.getReadTimeout(); + http.setReadTimeout(timeout4ESBuffer/5); + + long expected = 0; + boolean isChunked = false; + // the chunked case + if (cl < 0) { + expected = bufSize4ES; + isChunked = true; + } else { + expected = cl; + } + if (expected <= bufSize4ES) { + int exp = (int) expected; + byte[] buffer = new byte[exp]; + int count = 0, time = 0, len = 0; + do { + try { + len = is.read(buffer, count, + buffer.length - count); + if (len < 0) { + if (isChunked) { + // chunked ended + // if chunked ended prematurely, + // an IOException would be thrown + break; + } + // the server sends less than cl bytes of data + throw new IOException("the server closes"+ + " before sending "+cl+ + " bytes of data"); + } + count += len; + } catch (SocketTimeoutException ex) { + time += timeout4ESBuffer/5; + } + } while (count < exp && time < timeout4ESBuffer); + + // reset SO_TIMEOUT to old value + http.setReadTimeout(oldTimeout); + + // if count < cl at this point, we will not try to reuse + // the connection + if (count == 0) { + // since we haven't read anything, + // we will return the underlying + // inputstream back to the application + return null; + } else if ((count == expected && !(isChunked)) || (isChunked && len <0)) { + // put the connection into keep-alive cache + // the inputstream will try to do the right thing + is.close(); + return new ErrorStream(ByteBuffer.wrap(buffer, 0, count)); + } else { + // we read part of the response body + return new ErrorStream( + ByteBuffer.wrap(buffer, 0, count), is); + } + } + return null; + } catch (IOException ioex) { + // ioex.printStackTrace(); + return null; + } + } + + @Override + public int available() throws IOException { + if (is == null) { + return buffer.remaining(); + } else { + return buffer.remaining()+is.available(); + } + } + + public int read() throws IOException { + byte[] b = new byte[1]; + int ret = read(b); + return (ret == -1? ret : (b[0] & 0x00FF)); + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int rem = buffer.remaining(); + if (rem > 0) { + int ret = rem < len? rem : len; + buffer.get(b, off, ret); + return ret; + } else { + if (is == null) { + return -1; + } else { + return is.read(b, off, len); + } + } + } + + @Override + public void close() throws IOException { + buffer = null; + if (is != null) { + is.close(); + } + } + } +} + +/** An input stream that just returns EOF. This is for + * HTTP URLConnections that are KeepAlive && use the + * HEAD method - i.e., stream not dead, but nothing to be read. + */ + +class EmptyInputStream extends InputStream { + + @Override + public int available() { + return 0; + } + + public int read() { + return -1; + } +} diff --git a/src/sun/net/www/protocol/http/NTLMAuthenticationProxy.java b/src/sun/net/www/protocol/http/NTLMAuthenticationProxy.java new file mode 100644 index 00000000..61c351a1 --- /dev/null +++ b/src/sun/net/www/protocol/http/NTLMAuthenticationProxy.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.net.www.protocol.http; + +import java.net.URL; +import java.net.PasswordAuthentication; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import sun.util.logging.PlatformLogger; + +/** + * Proxy class for loading NTLMAuthentication, so as to remove static + * dependancy. + */ +class NTLMAuthenticationProxy { + private static Method supportsTA; + private static Method isTrustedSite; + private static final String clazzStr = "sun.net.www.protocol.http.ntlm.NTLMAuthentication"; + private static final String supportsTAStr = "supportsTransparentAuth"; + private static final String isTrustedSiteStr = "isTrustedSite"; + + static final NTLMAuthenticationProxy proxy = tryLoadNTLMAuthentication(); + static final boolean supported = proxy != null ? true : false; + static final boolean supportsTransparentAuth = supported ? supportsTransparentAuth() : false; + + private final Constructor threeArgCtr; + private final Constructor fiveArgCtr; + + private NTLMAuthenticationProxy(Constructor threeArgCtr, + Constructor fiveArgCtr) { + this.threeArgCtr = threeArgCtr; + this.fiveArgCtr = fiveArgCtr; + } + + + AuthenticationInfo create(boolean isProxy, + URL url, + PasswordAuthentication pw) { + try { + return threeArgCtr.newInstance(isProxy, url, pw); + } catch (ReflectiveOperationException roe) { + finest(roe); + } + + return null; + } + + AuthenticationInfo create(boolean isProxy, + String host, + int port, + PasswordAuthentication pw) { + try { + return fiveArgCtr.newInstance(isProxy, host, port, pw); + } catch (ReflectiveOperationException roe) { + finest(roe); + } + + return null; + } + + /* Returns true if the NTLM implementation supports transparent + * authentication (try with the current users credentials before + * prompting for username and password, etc). + */ + private static boolean supportsTransparentAuth() { + try { + return (Boolean)supportsTA.invoke(null); + } catch (ReflectiveOperationException roe) { + finest(roe); + } + + return false; + } + + /* Transparent authentication should only be tried with a trusted + * site ( when running in a secure environment ). + */ + public static boolean isTrustedSite(URL url) { + try { + return (Boolean)isTrustedSite.invoke(null, url); + } catch (ReflectiveOperationException roe) { + finest(roe); + } + + return false; + } + + /** + * Loads the NTLM authentiation implementation through reflection. If + * the class is present, then it must have the required constructors and + * method. Otherwise, it is considered an error. + */ + @SuppressWarnings("unchecked") + private static NTLMAuthenticationProxy tryLoadNTLMAuthentication() { + Class cl; + Constructor threeArg, fiveArg; + try { + cl = (Class)Class.forName(clazzStr, true, null); + if (cl != null) { + threeArg = cl.getConstructor(boolean.class, + URL.class, + PasswordAuthentication.class); + fiveArg = cl.getConstructor(boolean.class, + String.class, + int.class, + PasswordAuthentication.class); + supportsTA = cl.getDeclaredMethod(supportsTAStr); + isTrustedSite = cl.getDeclaredMethod(isTrustedSiteStr, URL.class); + return new NTLMAuthenticationProxy(threeArg, + fiveArg); + } + } catch (ClassNotFoundException cnfe) { + finest(cnfe); + } catch (ReflectiveOperationException roe) { + throw new AssertionError(roe); + } + + return null; + } + + static void finest(Exception e) { + PlatformLogger logger = HttpURLConnection.getHttpLogger(); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("NTLMAuthenticationProxy: " + e); + } + } +} diff --git a/src/sun/net/www/protocol/http/NegotiateAuthentication.java b/src/sun/net/www/protocol/http/NegotiateAuthentication.java new file mode 100644 index 00000000..9e2a49a0 --- /dev/null +++ b/src/sun/net/www/protocol/http/NegotiateAuthentication.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.net.URL; +import java.io.IOException; +import java.net.Authenticator.RequestorType; +import java.util.Base64; +import java.util.HashMap; +import sun.net.www.HeaderParser; +import sun.util.logging.PlatformLogger; +import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; +import static sun.net.www.protocol.http.AuthScheme.KERBEROS; + +/** + * NegotiateAuthentication: + * + * @author weijun.wang@sun.com + * @since 1.6 + */ + +class NegotiateAuthentication extends AuthenticationInfo { + + private static final long serialVersionUID = 100L; + private static final PlatformLogger logger = HttpURLConnection.getHttpLogger(); + + final private HttpCallerInfo hci; + + // These maps are used to manage the GSS availability for diffrent + // hosts. The key for both maps is the host name. + // supported is set when isSupported is checked, + // if it's true, a cached Negotiator is put into cache. + // the cache can be used only once, so after the first use, it's cleaned. + static HashMap supported = null; + static HashMap cache = null; + + // The HTTP Negotiate Helper + private Negotiator negotiator = null; + + /** + * Constructor used for both WWW and proxy entries. + * @param hci a schemed object. + */ + public NegotiateAuthentication(HttpCallerInfo hci) { + super(RequestorType.PROXY==hci.authType ? PROXY_AUTHENTICATION : SERVER_AUTHENTICATION, + hci.scheme.equalsIgnoreCase("Negotiate") ? NEGOTIATE : KERBEROS, + hci.url, + ""); + this.hci = hci; + } + + /** + * @return true if this authentication supports preemptive authorization + */ + @Override + public boolean supportsPreemptiveAuthorization() { + return false; + } + + /** + * Find out if the HttpCallerInfo supports Negotiate protocol. + * @return true if supported + */ + public static boolean isSupported(HttpCallerInfo hci) { + ClassLoader loader = null; + try { + loader = Thread.currentThread().getContextClassLoader(); + } catch (SecurityException se) { + if (logger.isLoggable(PlatformLogger.Level.FINER)) { + logger.finer("NegotiateAuthentication: " + + "Attempt to get the context class loader failed - " + se); + } + } + + if (loader != null) { + // Lock on the class loader instance to avoid the deadlock engaging + // the lock in "ClassLoader.loadClass(String, boolean)" method. + synchronized (loader) { + return isSupportedImpl(hci); + } + } + return isSupportedImpl(hci); + } + + /** + * Find out if the HttpCallerInfo supports Negotiate protocol. In order to + * find out yes or no, an initialization of a Negotiator object against it + * is tried. The generated object will be cached under the name of ths + * hostname at a success try.
    + * + * If this method is called for the second time on an HttpCallerInfo with + * the same hostname, the answer is retrieved from cache. + * + * @return true if supported + */ + private static synchronized boolean isSupportedImpl(HttpCallerInfo hci) { + if (supported == null) { + supported = new HashMap (); + cache = new HashMap (); + } + String hostname = hci.host; + hostname = hostname.toLowerCase(); + if (supported.containsKey(hostname)) { + return supported.get(hostname); + } + + Negotiator neg = Negotiator.getNegotiator(hci); + if (neg != null) { + supported.put(hostname, true); + // the only place cache.put is called. here we can make sure + // the object is valid and the oneToken inside is not null + cache.put(hostname, neg); + return true; + } else { + supported.put(hostname, false); + return false; + } + } + + /** + * Not supported. Must use the setHeaders() method + */ + @Override + public String getHeaderValue(URL url, String method) { + throw new RuntimeException ("getHeaderValue not supported"); + } + + /** + * Check if the header indicates that the current auth. parameters are stale. + * If so, then replace the relevant field with the new value + * and return true. Otherwise return false. + * returning true means the request can be retried with the same userid/password + * returning false means we have to go back to the user to ask for a new + * username password. + */ + @Override + public boolean isAuthorizationStale (String header) { + return false; /* should not be called for Negotiate */ + } + + /** + * Set header(s) on the given connection. + * @param conn The connection to apply the header(s) to + * @param p A source of header values for this connection, not used because + * HeaderParser converts the fields to lower case, use raw instead + * @param raw The raw header field. + * @return true if all goes well, false if no headers were set. + */ + @Override + public synchronized boolean setHeaders(HttpURLConnection conn, HeaderParser p, String raw) { + + try { + String response; + byte[] incoming = null; + String[] parts = raw.split("\\s+"); + if (parts.length > 1) { + incoming = Base64.getDecoder().decode(parts[1]); + } + response = hci.scheme + " " + Base64.getEncoder().encodeToString( + incoming==null?firstToken():nextToken(incoming)); + + conn.setAuthenticationProperty(getHeaderName(), response); + return true; + } catch (IOException e) { + return false; + } + } + + /** + * return the first token. + * @returns the token + * @throws IOException if Negotiator.getNegotiator() or + * Negotiator.firstToken() failed. + */ + private byte[] firstToken() throws IOException { + negotiator = null; + if (cache != null) { + synchronized(cache) { + negotiator = cache.get(getHost()); + if (negotiator != null) { + cache.remove(getHost()); // so that it is only used once + } + } + } + if (negotiator == null) { + negotiator = Negotiator.getNegotiator(hci); + if (negotiator == null) { + IOException ioe = new IOException("Cannot initialize Negotiator"); + throw ioe; + } + } + + return negotiator.firstToken(); + } + + /** + * return more tokens + * @param token the token to be fed into negotiator.nextToken() + * @returns the token + * @throws IOException if negotiator.nextToken() throws Exception. + * May happen if the input token is invalid. + */ + private byte[] nextToken(byte[] token) throws IOException { + return negotiator.nextToken(token); + } + + // MS will send a final WWW-Authenticate even if the status is already + // 200 OK. The token can be fed into initSecContext() again to determine + // if the server can be trusted. This is not the same concept as Digest's + // Authentication-Info header. + // + // Currently we ignore this header. + +} diff --git a/src/sun/net/www/protocol/http/Negotiator.java b/src/sun/net/www/protocol/http/Negotiator.java new file mode 100644 index 00000000..856efe16 --- /dev/null +++ b/src/sun/net/www/protocol/http/Negotiator.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import sun.util.logging.PlatformLogger; + +/** + * This abstract class is a bridge to connect NegotiteAuthentication and + * NegotiatorImpl, so that JAAS and JGSS calls can be made + */ +public abstract class Negotiator { + static Negotiator getNegotiator(HttpCallerInfo hci) { + + // These lines are equivalent to + // return new NegotiatorImpl(hci); + // The current implementation will make sure NegotiatorImpl is not + // directly referenced when compiling, thus smooth the way of building + // the J2SE platform where HttpURLConnection is a bootstrap class. + // + // Makes NegotiatorImpl, and the security classes it references, a + // runtime dependency rather than a static one. + + Class clazz; + Constructor c; + try { + clazz = Class.forName("sun.net.www.protocol.http.spnego.NegotiatorImpl", true, null); + c = clazz.getConstructor(HttpCallerInfo.class); + } catch (ClassNotFoundException cnfe) { + finest(cnfe); + return null; + } catch (ReflectiveOperationException roe) { + // if the class is there then something seriously wrong if + // the constructor is not. + throw new AssertionError(roe); + } + + try { + return (Negotiator) (c.newInstance(hci)); + } catch (ReflectiveOperationException roe) { + finest(roe); + Throwable t = roe.getCause(); + if (t != null && t instanceof Exception) + finest((Exception)t); + return null; + } + } + + public abstract byte[] firstToken() throws IOException; + + public abstract byte[] nextToken(byte[] in) throws IOException; + + private static void finest(Exception e) { + PlatformLogger logger = HttpURLConnection.getHttpLogger(); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("NegotiateAuthentication: " + e); + } + } +} + diff --git a/src/sun/net/www/protocol/http/logging/HttpLogFormatter.java b/src/sun/net/www/protocol/http/logging/HttpLogFormatter.java new file mode 100644 index 00000000..be2869c8 --- /dev/null +++ b/src/sun/net/www/protocol/http/logging/HttpLogFormatter.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http.logging; + +import java.util.logging.LogRecord; +import java.util.regex.*; + +/** + * A Formatter to make the HTTP logs a bit more palatable to the developer + * looking at them. The idea is to present the HTTP events in such a way that + * commands and headers are easily spotted (i.e. on separate lines). + * @author jccollet + */ +public class HttpLogFormatter extends java.util.logging.SimpleFormatter { + // Pattern for MessageHeader data. Mostly pairs within curly brackets + private static volatile Pattern pattern = null; + // Pattern for Cookies + private static volatile Pattern cpattern = null; + + public HttpLogFormatter() { + if (pattern == null) { + pattern = Pattern.compile("\\{[^\\}]*\\}"); + cpattern = Pattern.compile("[^,\\] ]{2,}"); + } + } + + @Override + public String format(LogRecord record) { + String sourceClassName = record.getSourceClassName(); + if (sourceClassName == null || + !(sourceClassName.startsWith("sun.net.www.protocol.http") || + sourceClassName.startsWith("sun.net.www.http"))) { + return super.format(record); + } + String src = record.getMessage(); + StringBuilder buf = new StringBuilder("HTTP: "); + if (src.startsWith("sun.net.www.MessageHeader@")) { + // MessageHeader logs are composed of pairs within curly brackets + // Let's extract them to make it more readable. That way we get one + // header pair (name, value) per line. A lot easier to read. + Matcher match = pattern.matcher(src); + while (match.find()) { + int i = match.start(); + int j = match.end(); + String s = src.substring(i + 1, j - 1); + if (s.startsWith("null: ")) { + s = s.substring(6); + } + if (s.endsWith(": null")) { + s = s.substring(0, s.length() - 6); + } + buf.append("\t").append(s).append("\n"); + } + } else if (src.startsWith("Cookies retrieved: {")) { + // This comes from the Cookie handler, let's clean up the format a bit + String s = src.substring(20); + buf.append("Cookies from handler:\n"); + while (s.length() >= 7) { + if (s.startsWith("Cookie=[")) { + String s2 = s.substring(8); + int c = s2.indexOf("Cookie2=["); + if (c > 0) { + s2 = s2.substring(0, c-1); + s = s2.substring(c); + } else { + s = ""; + } + if (s2.length() < 4) { + continue; + } + Matcher m = cpattern.matcher(s2); + while (m.find()) { + int i = m.start(); + int j = m.end(); + if (i >= 0) { + String cookie = s2.substring(i + 1, j > 0 ? j - 1 : s2.length() - 1); + buf.append("\t").append(cookie).append("\n"); + } + } + } + if (s.startsWith("Cookie2=[")) { + String s2 = s.substring(9); + int c = s2.indexOf("Cookie=["); + if (c > 0) { + s2 = s2.substring(0, c-1); + s = s2.substring(c); + } else { + s = ""; + } + Matcher m = cpattern.matcher(s2); + while (m.find()) { + int i = m.start(); + int j = m.end(); + if (i >= 0) { + String cookie = s2.substring(i+1, j > 0 ? j-1 : s2.length() - 1); + buf.append("\t").append(cookie).append("\n"); + } + } + } + } + } else { + // Anything else we let as is. + buf.append(src).append("\n"); + } + return buf.toString(); + } + +} diff --git a/src/sun/net/www/protocol/http/ntlm/NTLMAuthenticationCallback.java b/src/sun/net/www/protocol/http/ntlm/NTLMAuthenticationCallback.java new file mode 100644 index 00000000..92886311 --- /dev/null +++ b/src/sun/net/www/protocol/http/ntlm/NTLMAuthenticationCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http.ntlm; + +import java.net.URL; + +/** + * This class is used to call back to deployment to determine if a given + * URL is trusted. Transparent authentication (try with logged in users + * credentials without prompting) should only be tried with trusted sites. + */ +public abstract class NTLMAuthenticationCallback { + private static volatile NTLMAuthenticationCallback callback = + new DefaultNTLMAuthenticationCallback(); + + public static void setNTLMAuthenticationCallback( + NTLMAuthenticationCallback callback) { + NTLMAuthenticationCallback.callback = callback; + } + + public static NTLMAuthenticationCallback getNTLMAuthenticationCallback() { + return callback; + } + + /** + * Returns true if the given site is trusted, i.e. we can try + * transparent Authentication. + */ + public abstract boolean isTrustedSite(URL url); + + static class DefaultNTLMAuthenticationCallback extends NTLMAuthenticationCallback { + @Override + public boolean isTrustedSite(URL url) { return true; } + } +} + diff --git a/src/sun/net/www/protocol/http/spnego/NegotiateCallbackHandler.java b/src/sun/net/www/protocol/http/spnego/NegotiateCallbackHandler.java new file mode 100644 index 00000000..db03ab58 --- /dev/null +++ b/src/sun/net/www/protocol/http/spnego/NegotiateCallbackHandler.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.http.spnego; + +import java.io.IOException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.util.Arrays; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import sun.net.www.protocol.http.HttpCallerInfo; + +/** + * @since 1.6 + * Special callback handler used in JGSS for the HttpCaller. + */ +public class NegotiateCallbackHandler implements CallbackHandler { + + private String username; + private char[] password; + + /** + * Authenticator asks for username and password in a single prompt, + * but CallbackHandler checks one by one. So, no matter which callback + * gets handled first, make sure Authenticator is only called once. + */ + private boolean answered; + + private final HttpCallerInfo hci; + + public NegotiateCallbackHandler(HttpCallerInfo hci) { + this.hci = hci; + } + + private void getAnswer() { + if (!answered) { + answered = true; + PasswordAuthentication passAuth = + Authenticator.requestPasswordAuthentication( + hci.host, hci.addr, hci.port, hci.protocol, + hci.prompt, hci.scheme, hci.url, hci.authType); + /** + * To be compatible with existing callback handler implementations, + * when the underlying Authenticator is canceled, username and + * password are assigned null. No exception is thrown. + */ + if (passAuth != null) { + username = passAuth.getUserName(); + password = passAuth.getPassword(); + } + } + } + + public void handle(Callback[] callbacks) throws + UnsupportedCallbackException, IOException { + for (int i=0; i + *

  • Find out what GSS mechanism to use from the system property + * http.negotiate.mechanism.oid, defaults SPNEGO + *
  • Creating the GSSName for the target host, "HTTP/"+hostname + *
  • Creating GSSContext + *
  • A first call to initSecContext + */ + private void init(HttpCallerInfo hci) throws GSSException { + final Oid oid; + + if (hci.scheme.equalsIgnoreCase("Kerberos")) { + // we can only use Kerberos mech when the scheme is kerberos + oid = GSSUtil.GSS_KRB5_MECH_OID; + } else { + String pref = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + return System.getProperty( + "http.auth.preference", + "spnego"); + } + }); + if (pref.equalsIgnoreCase("kerberos")) { + oid = GSSUtil.GSS_KRB5_MECH_OID; + } else { + // currently there is no 3rd mech we can use + oid = GSSUtil.GSS_SPNEGO_MECH_OID; + } + } + + GSSManagerImpl manager = new GSSManagerImpl( + new HttpCaller(hci)); + + // RFC 4559 4.1 uses uppercase service name "HTTP". + // RFC 4120 6.2.1 demands the host be lowercase + String peerName = "HTTP@" + hci.host.toLowerCase(); + + GSSName serverName = manager.createName(peerName, + GSSName.NT_HOSTBASED_SERVICE); + context = manager.createContext(serverName, + oid, + null, + GSSContext.DEFAULT_LIFETIME); + + // Always respect delegation policy in HTTP/SPNEGO. + if (context instanceof ExtendedGSSContext) { + ((ExtendedGSSContext)context).requestDelegPolicy(true); + } + oneToken = context.initSecContext(new byte[0], 0, 0); + } + + /** + * Constructor + * @throws IOException If negotiator cannot be constructed + */ + public NegotiatorImpl(HttpCallerInfo hci) throws IOException { + try { + init(hci); + } catch (GSSException e) { + if (DEBUG) { + System.out.println("Negotiate support not initiated, will " + + "fallback to other scheme if allowed. Reason:"); + e.printStackTrace(); + } + IOException ioe = new IOException("Negotiate support not initiated"); + ioe.initCause(e); + throw ioe; + } + } + + /** + * Return the first token of GSS, in SPNEGO, it's called NegTokenInit + * @return the first token + */ + @Override + public byte[] firstToken() { + return oneToken; + } + + /** + * Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg + * @param token the token received from server + * @return the next token + * @throws IOException if the token cannot be created successfully + */ + @Override + public byte[] nextToken(byte[] token) throws IOException { + try { + return context.initSecContext(token, 0, token.length); + } catch (GSSException e) { + if (DEBUG) { + System.out.println("Negotiate support cannot continue. Reason:"); + e.printStackTrace(); + } + IOException ioe = new IOException("Negotiate support cannot continue"); + ioe.initCause(e); + throw ioe; + } + } +} diff --git a/src/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java b/src/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java new file mode 100644 index 00000000..3b8ff0c0 --- /dev/null +++ b/src/sun/net/www/protocol/https/AbstractDelegateHttpsURLConnection.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.https; + +import java.net.URL; +import java.net.Proxy; +import java.net.SecureCacheResponse; +import java.security.Principal; +import java.io.IOException; +import java.util.List; +import javax.net.ssl.SSLPeerUnverifiedException; +import sun.net.www.http.*; +import sun.net.www.protocol.http.HttpURLConnection; + +/** + * HTTPS URL connection support. + * We need this delegate because HttpsURLConnection is a subclass of + * java.net.HttpURLConnection. We will avoid copying over the code from + * sun.net.www.protocol.http.HttpURLConnection by having this class + * + */ +public abstract class AbstractDelegateHttpsURLConnection extends + HttpURLConnection { + + protected AbstractDelegateHttpsURLConnection(URL url, + sun.net.www.protocol.http.Handler handler) throws IOException { + this(url, null, handler); + } + + protected AbstractDelegateHttpsURLConnection(URL url, Proxy p, + sun.net.www.protocol.http.Handler handler) throws IOException { + super(url, p, handler); + } + + protected abstract javax.net.ssl.SSLSocketFactory getSSLSocketFactory(); + + protected abstract javax.net.ssl.HostnameVerifier getHostnameVerifier(); + + /** + * No user application is able to call these routines, as no one + * should ever get access to an instance of + * DelegateHttpsURLConnection (sun.* or com.*) + */ + + /** + * Create a new HttpClient object, bypassing the cache of + * HTTP client objects/connections. + * + * Note: this method is changed from protected to public because + * the com.sun.ssl.internal.www.protocol.https handler reuses this + * class for its actual implemantation + * + * @param url the URL being accessed + */ + public void setNewClient (URL url) + throws IOException { + setNewClient (url, false); + } + + /** + * Obtain a HttpClient object. Use the cached copy if specified. + * + * Note: this method is changed from protected to public because + * the com.sun.ssl.internal.www.protocol.https handler reuses this + * class for its actual implemantation + * + * @param url the URL being accessed + * @param useCache whether the cached connection should be used + * if present + */ + public void setNewClient (URL url, boolean useCache) + throws IOException { + http = HttpsClient.New (getSSLSocketFactory(), + url, + getHostnameVerifier(), + useCache, this); + ((HttpsClient)http).afterConnect(); + } + + /** + * Create a new HttpClient object, set up so that it uses + * per-instance proxying to the given HTTP proxy. This + * bypasses the cache of HTTP client objects/connections. + * + * Note: this method is changed from protected to public because + * the com.sun.ssl.internal.www.protocol.https handler reuses this + * class for its actual implemantation + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + */ + public void setProxiedClient (URL url, String proxyHost, int proxyPort) + throws IOException { + setProxiedClient(url, proxyHost, proxyPort, false); + } + + /** + * Obtain a HttpClient object, set up so that it uses per-instance + * proxying to the given HTTP proxy. Use the cached copy of HTTP + * client objects/connections if specified. + * + * Note: this method is changed from protected to public because + * the com.sun.ssl.internal.www.protocol.https handler reuses this + * class for its actual implemantation + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + * @param useCache whether the cached connection should be used + * if present + */ + public void setProxiedClient (URL url, String proxyHost, int proxyPort, + boolean useCache) throws IOException { + proxiedConnect(url, proxyHost, proxyPort, useCache); + if (!http.isCachedConnection()) { + doTunneling(); + } + ((HttpsClient)http).afterConnect(); + } + + protected void proxiedConnect(URL url, String proxyHost, int proxyPort, + boolean useCache) throws IOException { + if (connected) + return; + http = HttpsClient.New (getSSLSocketFactory(), + url, + getHostnameVerifier(), + proxyHost, proxyPort, useCache, this); + connected = true; + } + + /** + * Used by subclass to access "connected" variable. + */ + public boolean isConnected() { + return connected; + } + + /** + * Used by subclass to access "connected" variable. + */ + public void setConnected(boolean conn) { + connected = conn; + } + + /** + * Implements the HTTP protocol handler's "connect" method, + * establishing an SSL connection to the server as necessary. + */ + public void connect() throws IOException { + if (connected) + return; + plainConnect(); + if (cachedResponse != null) { + // using cached response + return; + } + if (!http.isCachedConnection() && http.needsTunneling()) { + doTunneling(); + } + ((HttpsClient)http).afterConnect(); + } + + // will try to use cached HttpsClient + protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout) + throws IOException { + return HttpsClient.New(getSSLSocketFactory(), url, + getHostnameVerifier(), p, true, connectTimeout, + this); + } + + // will open new connection + protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout, + boolean useCache) + throws IOException { + return HttpsClient.New(getSSLSocketFactory(), url, + getHostnameVerifier(), p, + useCache, connectTimeout, this); + } + + /** + * Returns the cipher suite in use on this connection. + */ + public String getCipherSuite () { + if (cachedResponse != null) { + return ((SecureCacheResponse)cachedResponse).getCipherSuite(); + } + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return ((HttpsClient)http).getCipherSuite (); + } + } + + /** + * Returns the certificate chain the client sent to the + * server, or null if the client did not authenticate. + */ + public java.security.cert.Certificate[] getLocalCertificates() { + if (cachedResponse != null) { + List l = ((SecureCacheResponse)cachedResponse).getLocalCertificateChain(); + if (l == null) { + return null; + } else { + return l.toArray(new java.security.cert.Certificate[0]); + } + } + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return (((HttpsClient)http).getLocalCertificates ()); + } + } + + /** + * Returns the server's certificate chain, or throws + * SSLPeerUnverified Exception if + * the server did not authenticate. + */ + public java.security.cert.Certificate[] getServerCertificates() + throws SSLPeerUnverifiedException { + if (cachedResponse != null) { + List l = ((SecureCacheResponse)cachedResponse).getServerCertificateChain(); + if (l == null) { + return null; + } else { + return l.toArray(new java.security.cert.Certificate[0]); + } + } + + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return (((HttpsClient)http).getServerCertificates ()); + } + } + + /** + * Returns the server's X.509 certificate chain, or null if + * the server did not authenticate. + */ + public javax.security.cert.X509Certificate[] getServerCertificateChain() + throws SSLPeerUnverifiedException { + if (cachedResponse != null) { + throw new UnsupportedOperationException("this method is not supported when using cache"); + } + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return ((HttpsClient)http).getServerCertificateChain (); + } + } + + /** + * Returns the server's principal, or throws SSLPeerUnverifiedException + * if the server did not authenticate. + */ + Principal getPeerPrincipal() + throws SSLPeerUnverifiedException + { + if (cachedResponse != null) { + return ((SecureCacheResponse)cachedResponse).getPeerPrincipal(); + } + + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return (((HttpsClient)http).getPeerPrincipal()); + } + } + + /** + * Returns the principal the client sent to the + * server, or null if the client did not authenticate. + */ + Principal getLocalPrincipal() + { + if (cachedResponse != null) { + return ((SecureCacheResponse)cachedResponse).getLocalPrincipal(); + } + + if (http == null) { + throw new IllegalStateException("connection not yet open"); + } else { + return (((HttpsClient)http).getLocalPrincipal()); + } + } + +} diff --git a/src/sun/net/www/protocol/https/DefaultHostnameVerifier.java b/src/sun/net/www/protocol/https/DefaultHostnameVerifier.java new file mode 100644 index 00000000..e1998583 --- /dev/null +++ b/src/sun/net/www/protocol/https/DefaultHostnameVerifier.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.https; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +/** + * HostnameVerifier provides a callback mechanism so that + * implementers of this interface can supply a policy for + * handling the case where the host to connect to and + * the server name from the certificate mismatch. + * + * The default implementation will deny such connections. + * + * @author Xuelei Fan + */ +final public class DefaultHostnameVerifier implements HostnameVerifier { + public boolean verify(String hostname, SSLSession session) { + return false; + } +} diff --git a/src/sun/net/www/protocol/https/DelegateHttpsURLConnection.java b/src/sun/net/www/protocol/https/DelegateHttpsURLConnection.java new file mode 100644 index 00000000..b6ff7b41 --- /dev/null +++ b/src/sun/net/www/protocol/https/DelegateHttpsURLConnection.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.https; + +import java.net.URL; +import java.net.Proxy; +import java.io.IOException; + +/** + * This class was introduced to provide an additional level of + * abstraction between javax.net.ssl.HttpURLConnection and + * com.sun.net.ssl.HttpURLConnection objects.

    + * + * javax.net.ssl.HttpURLConnection is used in the new sun.net version + * of protocol implementation (this one) + * com.sun.net.ssl.HttpURLConnection is used in the com.sun version. + * + */ +public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection { + + // we need a reference to the HttpsURLConnection to get + // the properties set there + // we also need it to be public so that it can be referenced + // from sun.net.www.protocol.http.HttpURLConnection + // this is for ResponseCache.put(URI, URLConnection) + // second parameter needs to be cast to javax.net.ssl.HttpsURLConnection + // instead of AbstractDelegateHttpsURLConnection + public javax.net.ssl.HttpsURLConnection httpsURLConnection; + + DelegateHttpsURLConnection(URL url, + sun.net.www.protocol.http.Handler handler, + javax.net.ssl.HttpsURLConnection httpsURLConnection) + throws IOException { + this(url, null, handler, httpsURLConnection); + } + + DelegateHttpsURLConnection(URL url, Proxy p, + sun.net.www.protocol.http.Handler handler, + javax.net.ssl.HttpsURLConnection httpsURLConnection) + throws IOException { + super(url, p, handler); + this.httpsURLConnection = httpsURLConnection; + } + + protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() { + return httpsURLConnection.getSSLSocketFactory(); + } + + protected javax.net.ssl.HostnameVerifier getHostnameVerifier() { + return httpsURLConnection.getHostnameVerifier(); + } + + /* + * Called by layered delegator's finalize() method to handle closing + * the underlying object. + */ + protected void dispose() throws Throwable { + super.finalize(); + } +} diff --git a/src/sun/net/www/protocol/https/Handler.java b/src/sun/net/www/protocol/https/Handler.java new file mode 100644 index 00000000..d2be7fdf --- /dev/null +++ b/src/sun/net/www/protocol/https/Handler.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * HTTP stream opener + */ + +package sun.net.www.protocol.https; + +import java.io.IOException; +import java.net.URL; +import java.net.Proxy; + +/** open an http input stream given a URL */ +public class Handler extends sun.net.www.protocol.http.Handler { + protected String proxy; + protected int proxyPort; + + protected int getDefaultPort() { + return 443; + } + + public Handler () { + proxy = null; + proxyPort = -1; + } + + public Handler (String proxy, int port) { + this.proxy = proxy; + this.proxyPort = port; + } + + protected java.net.URLConnection openConnection(URL u) + throws IOException { + return openConnection(u, (Proxy)null); + } + + protected java.net.URLConnection openConnection(URL u, Proxy p) + throws IOException { + return new HttpsURLConnectionImpl(u, p, this); + } +} diff --git a/src/sun/net/www/protocol/https/HttpsClient.java b/src/sun/net/www/protocol/https/HttpsClient.java new file mode 100644 index 00000000..d49eb01f --- /dev/null +++ b/src/sun/net/www/protocol/https/HttpsClient.java @@ -0,0 +1,792 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.net.www.protocol.https; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.io.PrintStream; +import java.io.BufferedOutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketException; +import java.net.URL; +import java.net.UnknownHostException; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.security.Principal; +import java.security.cert.*; +import java.util.StringTokenizer; +import java.util.Vector; +import java.security.AccessController; + +import javax.security.auth.x500.X500Principal; + +import javax.net.ssl.*; +import sun.net.www.http.HttpClient; +import sun.net.www.protocol.http.HttpURLConnection; +import sun.security.action.*; + +import sun.security.util.HostnameChecker; +import sun.security.ssl.SSLSocketImpl; + +import sun.util.logging.PlatformLogger; +import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*; + + +/** + * This class provides HTTPS client URL support, building on the standard + * "sun.net.www" HTTP protocol handler. HTTPS is the same protocol as HTTP, + * but differs in the transport layer which it uses:

      + * + *
    • There's a Secure Sockets Layer between TCP + * and the HTTP protocol code. + * + *
    • It uses a different default TCP port. + * + *
    • It doesn't use application level proxies, which can see and + * manipulate HTTP user level data, compromising privacy. It uses + * low level tunneling instead, which hides HTTP protocol and data + * from all third parties. (Traffic analysis is still possible). + * + *
    • It does basic server authentication, to protect + * against "URL spoofing" attacks. This involves deciding + * whether the X.509 certificate chain identifying the server + * is trusted, and verifying that the name of the server is + * found in the certificate. (The application may enable an + * anonymous SSL cipher suite, and such checks are not done + * for anonymous ciphers.) + * + *
    • It exposes key SSL session attributes, specifically the + * cipher suite in use and the server's X509 certificates, to + * application software which knows about this protocol handler. + * + *
    + * + *

    System properties used include:

      + * + *
    • https.proxyHost ... the host supporting SSL + * tunneling using the conventional CONNECT syntax + * + *
    • https.proxyPort ... port to use on proxyHost + * + *
    • https.cipherSuites ... comma separated list of + * SSL cipher suite names to enable. + * + *
    • http.nonProxyHosts ... + * + *
    + * + * @author David Brownell + * @author Bill Foote + */ + +// final for export control reasons (access to APIs); remove with care +final class HttpsClient extends HttpClient + implements HandshakeCompletedListener +{ + // STATIC STATE and ACCESSORS THERETO + + // HTTPS uses a different default port number than HTTP. + private static final int httpsPortNumber = 443; + + // default HostnameVerifier class canonical name + private static final String defaultHVCanonicalName = + "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier"; + + /** Returns the default HTTPS port (443) */ + @Override + protected int getDefaultPort() { return httpsPortNumber; } + + private HostnameVerifier hv; + private SSLSocketFactory sslSocketFactory; + + // HttpClient.proxyDisabled will always be false, because we don't + // use an application-level HTTP proxy. We might tunnel through + // our http proxy, though. + + + // INSTANCE DATA + + // last negotiated SSL session + private SSLSession session; + + private String [] getCipherSuites() { + // + // If ciphers are assigned, sort them into an array. + // + String ciphers []; + String cipherString = AccessController.doPrivileged( + new GetPropertyAction("https.cipherSuites")); + + if (cipherString == null || "".equals(cipherString)) { + ciphers = null; + } else { + StringTokenizer tokenizer; + Vector v = new Vector(); + + tokenizer = new StringTokenizer(cipherString, ","); + while (tokenizer.hasMoreTokens()) + v.addElement(tokenizer.nextToken()); + ciphers = new String [v.size()]; + for (int i = 0; i < ciphers.length; i++) + ciphers [i] = v.elementAt(i); + } + return ciphers; + } + + private String [] getProtocols() { + // + // If protocols are assigned, sort them into an array. + // + String protocols []; + String protocolString = AccessController.doPrivileged( + new GetPropertyAction("https.protocols")); + + if (protocolString == null || "".equals(protocolString)) { + protocols = null; + } else { + StringTokenizer tokenizer; + Vector v = new Vector(); + + tokenizer = new StringTokenizer(protocolString, ","); + while (tokenizer.hasMoreTokens()) + v.addElement(tokenizer.nextToken()); + protocols = new String [v.size()]; + for (int i = 0; i < protocols.length; i++) { + protocols [i] = v.elementAt(i); + } + } + return protocols; + } + + private String getUserAgent() { + String userAgent = AccessController.doPrivileged( + new GetPropertyAction("https.agent")); + if (userAgent == null || userAgent.length() == 0) { + userAgent = "JSSE"; + } + return userAgent; + } + + // should remove once HttpClient.newHttpProxy is putback + private static Proxy newHttpProxy(String proxyHost, int proxyPort) { + InetSocketAddress saddr = null; + final String phost = proxyHost; + final int pport = proxyPort < 0 ? httpsPortNumber : proxyPort; + try { + saddr = AccessController.doPrivileged(new + java.security.PrivilegedExceptionAction() { + public InetSocketAddress run() { + return new InetSocketAddress(phost, pport); + }}); + } catch (java.security.PrivilegedActionException pae) { + } + return new Proxy(Proxy.Type.HTTP, saddr); + } + + // CONSTRUCTOR, FACTORY + + + /** + * Create an HTTPS client URL. Traffic will be tunneled through any + * intermediate nodes rather than proxied, so that confidentiality + * of data exchanged can be preserved. However, note that all the + * anonymous SSL flavors are subject to "person-in-the-middle" + * attacks against confidentiality. If you enable use of those + * flavors, you may be giving up the protection you get through + * SSL tunneling. + * + * Use New to get new HttpsClient. This constructor is meant to be + * used only by New method. New properly checks for URL spoofing. + * + * @param URL https URL with which a connection must be established + */ + private HttpsClient(SSLSocketFactory sf, URL url) + throws IOException + { + // HttpClient-level proxying is always disabled, + // because we override doConnect to do tunneling instead. + this(sf, url, (String)null, -1); + } + + /** + * Create an HTTPS client URL. Traffic will be tunneled through + * the specified proxy server. + */ + HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort) + throws IOException { + this(sf, url, proxyHost, proxyPort, -1); + } + + /** + * Create an HTTPS client URL. Traffic will be tunneled through + * the specified proxy server, with a connect timeout + */ + HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort, + int connectTimeout) + throws IOException { + this(sf, url, + (proxyHost == null? null: + HttpsClient.newHttpProxy(proxyHost, proxyPort)), + connectTimeout); + } + + /** + * Same as previous constructor except using a Proxy + */ + HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy, + int connectTimeout) + throws IOException { + this.proxy = proxy; + setSSLSocketFactory(sf); + this.proxyDisabled = true; + + this.host = url.getHost(); + this.url = url; + port = url.getPort(); + if (port == -1) { + port = getDefaultPort(); + } + setConnectTimeout(connectTimeout); + openServer(); + } + + + // This code largely ripped off from HttpClient.New, and + // it uses the same keepalive cache. + + static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, + HttpURLConnection httpuc) + throws IOException { + return HttpsClient.New(sf, url, hv, true, httpuc); + } + + /** See HttpClient for the model for this method. */ + static HttpClient New(SSLSocketFactory sf, URL url, + HostnameVerifier hv, boolean useCache, + HttpURLConnection httpuc) throws IOException { + return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc); + } + + /** + * Get a HTTPS client to the URL. Traffic will be tunneled through + * the specified proxy server. + */ + static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, + String proxyHost, int proxyPort, + HttpURLConnection httpuc) throws IOException { + return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc); + } + + static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, + String proxyHost, int proxyPort, boolean useCache, + HttpURLConnection httpuc) + throws IOException { + return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1, + httpuc); + } + + static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, + String proxyHost, int proxyPort, boolean useCache, + int connectTimeout, HttpURLConnection httpuc) + throws IOException { + + return HttpsClient.New(sf, url, hv, + (proxyHost == null? null : + HttpsClient.newHttpProxy(proxyHost, proxyPort)), + useCache, connectTimeout, httpuc); + } + + static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv, + Proxy p, boolean useCache, + int connectTimeout, HttpURLConnection httpuc) + throws IOException + { + if (p == null) { + p = Proxy.NO_PROXY; + } + HttpsClient ret = null; + if (useCache) { + /* see if one's already around */ + ret = (HttpsClient) kac.get(url, sf); + if (ret != null && httpuc != null && + httpuc.streaming() && + httpuc.getRequestMethod() == "POST") { + if (!ret.available()) + ret = null; + } + + if (ret != null) { + if ((ret.proxy != null && ret.proxy.equals(p)) || + (ret.proxy == null && p == null)) { + synchronized (ret) { + ret.cachedHttpClient = true; + assert ret.inCache; + ret.inCache = false; + if (httpuc != null && ret.needsTunneling()) + httpuc.setTunnelState(TUNNELING); + PlatformLogger logger = HttpURLConnection.getHttpLogger(); + if (logger.isLoggable(PlatformLogger.Level.FINEST)) { + logger.finest("KeepAlive stream retrieved from the cache, " + ret); + } + } + } else { + // We cannot return this connection to the cache as it's + // KeepAliveTimeout will get reset. We simply close the connection. + // This should be fine as it is very rare that a connection + // to the same host will not use the same proxy. + synchronized(ret) { + ret.inCache = false; + ret.closeServer(); + } + ret = null; + } + } + } + if (ret == null) { + ret = new HttpsClient(sf, url, p, connectTimeout); + } else { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) { + security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort()); + } else { + security.checkConnect(url.getHost(), url.getPort()); + } + } + ret.url = url; + } + ret.setHostnameVerifier(hv); + + return ret; + } + + // METHODS + void setHostnameVerifier(HostnameVerifier hv) { + this.hv = hv; + } + + void setSSLSocketFactory(SSLSocketFactory sf) { + sslSocketFactory = sf; + } + + SSLSocketFactory getSSLSocketFactory() { + return sslSocketFactory; + } + + /** + * The following method, createSocket, is defined in NetworkClient + * and overridden here so that the socket facroty is used to create + * new sockets. + */ + @Override + protected Socket createSocket() throws IOException { + try { + return sslSocketFactory.createSocket(); + } catch (SocketException se) { + // + // bug 6771432 + // javax.net.SocketFactory throws a SocketException with an + // UnsupportedOperationException as its cause to indicate that + // unconnected sockets have not been implemented. + // + Throwable t = se.getCause(); + if (t != null && t instanceof UnsupportedOperationException) { + return super.createSocket(); + } else { + throw se; + } + } + } + + + @Override + public boolean needsTunneling() { + return (proxy != null && proxy.type() != Proxy.Type.DIRECT + && proxy.type() != Proxy.Type.SOCKS); + } + + @Override + public void afterConnect() throws IOException, UnknownHostException { + if (!isCachedConnection()) { + SSLSocket s = null; + SSLSocketFactory factory = sslSocketFactory; + try { + if (!(serverSocket instanceof SSLSocket)) { + s = (SSLSocket)factory.createSocket(serverSocket, + host, port, true); + } else { + s = (SSLSocket)serverSocket; + if (s instanceof SSLSocketImpl) { + ((SSLSocketImpl)s).setHost(host); + } + } + } catch (IOException ex) { + // If we fail to connect through the tunnel, try it + // locally, as a last resort. If this doesn't work, + // throw the original exception. + try { + s = (SSLSocket)factory.createSocket(host, port); + } catch (IOException ignored) { + throw ex; + } + } + + // + // Force handshaking, so that we get any authentication. + // Register a handshake callback so our session state tracks any + // later session renegotiations. + // + String [] protocols = getProtocols(); + String [] ciphers = getCipherSuites(); + if (protocols != null) { + s.setEnabledProtocols(protocols); + } + if (ciphers != null) { + s.setEnabledCipherSuites(ciphers); + } + s.addHandshakeCompletedListener(this); + + // We have two hostname verification approaches. One is in + // SSL/TLS socket layer, where the algorithm is configured with + // SSLParameters.setEndpointIdentificationAlgorithm(), and the + // hostname verification is done by X509ExtendedTrustManager when + // the algorithm is "HTTPS". The other one is in HTTPS layer, + // where the algorithm is customized by + // HttpsURLConnection.setHostnameVerifier(), and the hostname + // verification is done by HostnameVerifier when the default + // rules for hostname verification fail. + // + // The relationship between two hostname verification approaches + // likes the following: + // + // | EIA algorithm + // +---------------------------------------------- + // | null | HTTPS | LDAP/other | + // ------------------------------------------------------------- + // | |1 |2 |3 | + // HNV | default | Set HTTPS EIA | use EIA | HTTPS | + // |-------------------------------------------------------- + // | non - |4 |5 |6 | + // | default | HTTPS/HNV | use EIA | HTTPS/HNV | + // ------------------------------------------------------------- + // + // Abbreviation: + // EIA: the endpoint identification algorithm in SSL/TLS + // socket layer + // HNV: the hostname verification object in HTTPS layer + // Notes: + // case 1. default HNV and EIA is null + // Set EIA as HTTPS, hostname check done in SSL/TLS + // layer. + // case 2. default HNV and EIA is HTTPS + // Use existing EIA, hostname check done in SSL/TLS + // layer. + // case 3. default HNV and EIA is other than HTTPS + // Use existing EIA, EIA check done in SSL/TLS + // layer, then do HTTPS check in HTTPS layer. + // case 4. non-default HNV and EIA is null + // No EIA, no EIA check done in SSL/TLS layer, then do + // HTTPS check in HTTPS layer using HNV as override. + // case 5. non-default HNV and EIA is HTTPS + // Use existing EIA, hostname check done in SSL/TLS + // layer. No HNV override possible. We will review this + // decision and may update the architecture for JDK 7. + // case 6. non-default HNV and EIA is other than HTTPS + // Use existing EIA, EIA check done in SSL/TLS layer, + // then do HTTPS check in HTTPS layer as override. + boolean needToCheckSpoofing = true; + String identification = + s.getSSLParameters().getEndpointIdentificationAlgorithm(); + if (identification != null && identification.length() != 0) { + if (identification.equalsIgnoreCase("HTTPS")) { + // Do not check server identity again out of SSLSocket, + // the endpoint will be identified during TLS handshaking + // in SSLSocket. + needToCheckSpoofing = false; + } // else, we don't understand the identification algorithm, + // need to check URL spoofing here. + } else { + boolean isDefaultHostnameVerifier = false; + + // We prefer to let the SSLSocket do the spoof checks, but if + // the application has specified a HostnameVerifier (HNV), + // we will always use that. + if (hv != null) { + String canonicalName = hv.getClass().getCanonicalName(); + if (canonicalName != null && + canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) { + isDefaultHostnameVerifier = true; + } + } else { + // Unlikely to happen! As the behavior is the same as the + // default hostname verifier, so we prefer to let the + // SSLSocket do the spoof checks. + isDefaultHostnameVerifier = true; + } + + if (isDefaultHostnameVerifier) { + // If the HNV is the default from HttpsURLConnection, we + // will do the spoof checks in SSLSocket. + SSLParameters paramaters = s.getSSLParameters(); + paramaters.setEndpointIdentificationAlgorithm("HTTPS"); + s.setSSLParameters(paramaters); + + needToCheckSpoofing = false; + } + } + + s.startHandshake(); + session = s.getSession(); + // change the serverSocket and serverOutput + serverSocket = s; + try { + serverOutput = new PrintStream( + new BufferedOutputStream(serverSocket.getOutputStream()), + false, encoding); + } catch (UnsupportedEncodingException e) { + throw new InternalError(encoding+" encoding not found"); + } + + // check URL spoofing if it has not been checked under handshaking + if (needToCheckSpoofing) { + checkURLSpoofing(hv); + } + } else { + // if we are reusing a cached https session, + // we don't need to do handshaking etc. But we do need to + // set the ssl session + session = ((SSLSocket)serverSocket).getSession(); + } + } + + // Server identity checking is done according to RFC 2818: HTTP over TLS + // Section 3.1 Server Identity + private void checkURLSpoofing(HostnameVerifier hostnameVerifier) + throws IOException { + // + // Get authenticated server name, if any + // + String host = url.getHost(); + + // if IPv6 strip off the "[]" + if (host != null && host.startsWith("[") && host.endsWith("]")) { + host = host.substring(1, host.length()-1); + } + + Certificate[] peerCerts = null; + String cipher = session.getCipherSuite(); + try { + HostnameChecker checker = HostnameChecker.getInstance( + HostnameChecker.TYPE_TLS); + + // Use ciphersuite to determine whether Kerberos is present. + if (cipher.startsWith("TLS_KRB5")) { + if (!HostnameChecker.match(host, getPeerPrincipal())) { + throw new SSLPeerUnverifiedException("Hostname checker" + + " failed for Kerberos"); + } + } else { // X.509 + + // get the subject's certificate + peerCerts = session.getPeerCertificates(); + + X509Certificate peerCert; + if (peerCerts[0] instanceof + X509Certificate) { + peerCert = (X509Certificate)peerCerts[0]; + } else { + throw new SSLPeerUnverifiedException(""); + } + checker.match(host, peerCert); + } + + // if it doesn't throw an exception, we passed. Return. + return; + + } catch (SSLPeerUnverifiedException e) { + + // + // client explicitly changed default policy and enabled + // anonymous ciphers; we can't check the standard policy + // + // ignore + } catch (CertificateException cpe) { + // ignore + } + + if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) { + return; + } else if ((hostnameVerifier != null) && + (hostnameVerifier.verify(host, session))) { + return; + } + + serverSocket.close(); + session.invalidate(); + + throw new IOException("HTTPS hostname wrong: should be <" + + url.getHost() + ">"); + } + + @Override + protected void putInKeepAliveCache() { + if (inCache) { + assert false : "Duplicate put to keep alive cache"; + return; + } + inCache = true; + kac.put(url, sslSocketFactory, this); + } + + /* + * Close an idle connection to this URL (if it exists in the cache). + */ + @Override + public void closeIdleConnection() { + HttpClient http = kac.get(url, sslSocketFactory); + if (http != null) { + http.closeServer(); + } + } + + /** + * Returns the cipher suite in use on this connection. + */ + String getCipherSuite() { + return session.getCipherSuite(); + } + + /** + * Returns the certificate chain the client sent to the + * server, or null if the client did not authenticate. + */ + public Certificate [] getLocalCertificates() { + return session.getLocalCertificates(); + } + + /** + * Returns the certificate chain with which the server + * authenticated itself, or throw a SSLPeerUnverifiedException + * if the server did not authenticate. + */ + Certificate [] getServerCertificates() + throws SSLPeerUnverifiedException + { + return session.getPeerCertificates(); + } + + /** + * Returns the X.509 certificate chain with which the server + * authenticated itself, or null if the server did not authenticate. + */ + javax.security.cert.X509Certificate [] getServerCertificateChain() + throws SSLPeerUnverifiedException + { + return session.getPeerCertificateChain(); + } + + /** + * Returns the principal with which the server authenticated + * itself, or throw a SSLPeerUnverifiedException if the + * server did not authenticate. + */ + Principal getPeerPrincipal() + throws SSLPeerUnverifiedException + { + Principal principal; + try { + principal = session.getPeerPrincipal(); + } catch (AbstractMethodError e) { + // if the provider does not support it, fallback to peer certs. + // return the X500Principal of the end-entity cert. + Certificate[] certs = + session.getPeerCertificates(); + principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); + } + return principal; + } + + /** + * Returns the principal the client sent to the + * server, or null if the client did not authenticate. + */ + Principal getLocalPrincipal() + { + Principal principal; + try { + principal = session.getLocalPrincipal(); + } catch (AbstractMethodError e) { + principal = null; + // if the provider does not support it, fallback to local certs. + // return the X500Principal of the end-entity cert. + Certificate[] certs = + session.getLocalCertificates(); + if (certs != null) { + principal = ((X509Certificate)certs[0]).getSubjectX500Principal(); + } + } + return principal; + } + + /** + * This method implements the SSL HandshakeCompleted callback, + * remembering the resulting session so that it may be queried + * for the current cipher suite and peer certificates. Servers + * sometimes re-initiate handshaking, so the session in use on + * a given connection may change. When sessions change, so may + * peer identities and cipher suites. + */ + public void handshakeCompleted(HandshakeCompletedEvent event) + { + session = event.getSession(); + } + + /** + * @return the proxy host being used for this client, or null + * if we're not going through a proxy + */ + @Override + public String getProxyHostUsed() { + if (!needsTunneling()) { + return null; + } else { + return super.getProxyHostUsed(); + } + } + + /** + * @return the proxy port being used for this client. Meaningless + * if getProxyHostUsed() gives null. + */ + @Override + public int getProxyPortUsed() { + return (proxy == null || proxy.type() == Proxy.Type.DIRECT || + proxy.type() == Proxy.Type.SOCKS)? -1: + ((InetSocketAddress)proxy.address()).getPort(); + } +} diff --git a/src/sun/net/www/protocol/https/HttpsURLConnectionImpl.java b/src/sun/net/www/protocol/https/HttpsURLConnectionImpl.java new file mode 100644 index 00000000..750c0a97 --- /dev/null +++ b/src/sun/net/www/protocol/https/HttpsURLConnectionImpl.java @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * NOTE: This class lives in the package sun.net.www.protocol.https. + * There is a copy in com.sun.net.ssl.internal.www.protocol.https for JSSE + * 1.0.2 compatibility. It is 100% identical except the package and extends + * lines. Any changes should be made to be class in sun.net.* and then copied + * to com.sun.net.*. + */ + +// For both copies of the file, uncomment one line and comment the other +package sun.net.www.protocol.https; +// package com.sun.net.ssl.internal.www.protocol.https; + +import java.net.URL; +import java.net.Proxy; +import java.net.ProtocolException; +import java.io.*; +import javax.net.ssl.*; +import java.security.Permission; +import java.security.Principal; +import java.util.Map; +import java.util.List; +import sun.net.www.http.HttpClient; + +/** + * A class to represent an HTTP connection to a remote object. + * + * Ideally, this class should subclass and inherit the http handler + * implementation, but it can't do so because that class have the + * wrong Java Type. Thus it uses the delegate (aka, the + * Adapter/Wrapper design pattern) to reuse code from the http + * handler. + * + * Since it would use a delegate to access + * sun.net.www.protocol.http.HttpURLConnection functionalities, it + * needs to implement all public methods in it's super class and all + * the way to Object. + * + */ + +// For both copies of the file, uncomment one line and comment the +// other. The differences between the two copies are introduced for +// plugin, and it is marked as such. +public class HttpsURLConnectionImpl + extends javax.net.ssl.HttpsURLConnection { +// public class HttpsURLConnectionOldImpl +// extends com.sun.net.ssl.HttpsURLConnection { + + // NOTE: made protected for plugin so that subclass can set it. + protected DelegateHttpsURLConnection delegate; + +// For both copies of the file, uncomment one line and comment the other + HttpsURLConnectionImpl(URL u, Handler handler) throws IOException { +// HttpsURLConnectionOldImpl(URL u, Handler handler) throws IOException { + this(u, null, handler); + } + +// For both copies of the file, uncomment one line and comment the other + HttpsURLConnectionImpl(URL u, Proxy p, Handler handler) throws IOException { +// HttpsURLConnectionOldImpl(URL u, Proxy p, Handler handler) throws IOException { + super(u); + delegate = new DelegateHttpsURLConnection(url, p, handler, this); + } + + // NOTE: introduced for plugin + // subclass needs to overwrite this to set delegate to + // the appropriate delegatee + protected HttpsURLConnectionImpl(URL u) throws IOException { + super(u); + } + + /** + * Create a new HttpClient object, bypassing the cache of + * HTTP client objects/connections. + * + * @param url the URL being accessed + */ + protected void setNewClient(URL url) throws IOException { + delegate.setNewClient(url, false); + } + + /** + * Obtain a HttpClient object. Use the cached copy if specified. + * + * @param url the URL being accessed + * @param useCache whether the cached connection should be used + * if present + */ + protected void setNewClient(URL url, boolean useCache) + throws IOException { + delegate.setNewClient(url, useCache); + } + + /** + * Create a new HttpClient object, set up so that it uses + * per-instance proxying to the given HTTP proxy. This + * bypasses the cache of HTTP client objects/connections. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + */ + protected void setProxiedClient(URL url, String proxyHost, int proxyPort) + throws IOException { + delegate.setProxiedClient(url, proxyHost, proxyPort); + } + + /** + * Obtain a HttpClient object, set up so that it uses per-instance + * proxying to the given HTTP proxy. Use the cached copy of HTTP + * client objects/connections if specified. + * + * @param url the URL being accessed + * @param proxyHost the proxy host to use + * @param proxyPort the proxy port to use + * @param useCache whether the cached connection should be used + * if present + */ + protected void setProxiedClient(URL url, String proxyHost, int proxyPort, + boolean useCache) throws IOException { + delegate.setProxiedClient(url, proxyHost, proxyPort, useCache); + } + + /** + * Implements the HTTP protocol handler's "connect" method, + * establishing an SSL connection to the server as necessary. + */ + public void connect() throws IOException { + delegate.connect(); + } + + /** + * Used by subclass to access "connected" variable. Since we are + * delegating the actual implementation to "delegate", we need to + * delegate the access of "connected" as well. + */ + protected boolean isConnected() { + return delegate.isConnected(); + } + + /** + * Used by subclass to access "connected" variable. Since we are + * delegating the actual implementation to "delegate", we need to + * delegate the access of "connected" as well. + */ + protected void setConnected(boolean conn) { + delegate.setConnected(conn); + } + + /** + * Returns the cipher suite in use on this connection. + */ + public String getCipherSuite() { + return delegate.getCipherSuite(); + } + + /** + * Returns the certificate chain the client sent to the + * server, or null if the client did not authenticate. + */ + public java.security.cert.Certificate [] + getLocalCertificates() { + return delegate.getLocalCertificates(); + } + + /** + * Returns the server's certificate chain, or throws + * SSLPeerUnverified Exception if + * the server did not authenticate. + */ + public java.security.cert.Certificate [] + getServerCertificates() throws SSLPeerUnverifiedException { + return delegate.getServerCertificates(); + } + + /** + * Returns the server's X.509 certificate chain, or null if + * the server did not authenticate. + * + * NOTE: This method is not necessary for the version of this class + * implementing javax.net.ssl.HttpsURLConnection, but provided for + * compatibility with the com.sun.net.ssl.HttpsURLConnection version. + */ + public javax.security.cert.X509Certificate[] getServerCertificateChain() { + try { + return delegate.getServerCertificateChain(); + } catch (SSLPeerUnverifiedException e) { + // this method does not throw an exception as declared in + // com.sun.net.ssl.HttpsURLConnection. + // Return null for compatibility. + return null; + } + } + + /** + * Returns the principal with which the server authenticated itself, + * or throw a SSLPeerUnverifiedException if the server did not authenticate. + */ + public Principal getPeerPrincipal() + throws SSLPeerUnverifiedException + { + return delegate.getPeerPrincipal(); + } + + /** + * Returns the principal the client sent to the + * server, or null if the client did not authenticate. + */ + public Principal getLocalPrincipal() + { + return delegate.getLocalPrincipal(); + } + + /* + * Allowable input/output sequences: + * [interpreted as POST/PUT] + * - get output, [write output,] get input, [read input] + * - get output, [write output] + * [interpreted as GET] + * - get input, [read input] + * Disallowed: + * - get input, [read input,] get output, [write output] + */ + + public synchronized OutputStream getOutputStream() throws IOException { + return delegate.getOutputStream(); + } + + public synchronized InputStream getInputStream() throws IOException { + return delegate.getInputStream(); + } + + public InputStream getErrorStream() { + return delegate.getErrorStream(); + } + + /** + * Disconnect from the server. + */ + public void disconnect() { + delegate.disconnect(); + } + + public boolean usingProxy() { + return delegate.usingProxy(); + } + + /** + * Returns an unmodifiable Map of the header fields. + * The Map keys are Strings that represent the + * response-header field names. Each Map value is an + * unmodifiable List of Strings that represents + * the corresponding field values. + * + * @return a Map of header fields + * @since 1.4 + */ + public Map> getHeaderFields() { + return delegate.getHeaderFields(); + } + + /** + * Gets a header field by name. Returns null if not known. + * @param name the name of the header field + */ + public String getHeaderField(String name) { + return delegate.getHeaderField(name); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + public String getHeaderField(int n) { + return delegate.getHeaderField(n); + } + + /** + * Gets a header field by index. Returns null if not known. + * @param n the index of the header field + */ + public String getHeaderFieldKey(int n) { + return delegate.getHeaderFieldKey(n); + } + + /** + * Sets request property. If a property with the key already + * exists, overwrite its value with the new value. + * @param value the value to be set + */ + public void setRequestProperty(String key, String value) { + delegate.setRequestProperty(key, value); + } + + /** + * Adds a general request property specified by a + * key-value pair. This method will not overwrite + * existing values associated with the same key. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + * @see #getRequestProperties(String) + * @since 1.4 + */ + public void addRequestProperty(String key, String value) { + delegate.addRequestProperty(key, value); + } + + /** + * Overwrite super class method + */ + public int getResponseCode() throws IOException { + return delegate.getResponseCode(); + } + + public String getRequestProperty(String key) { + return delegate.getRequestProperty(key); + } + + /** + * Returns an unmodifiable Map of general request + * properties for this connection. The Map keys + * are Strings that represent the request-header + * field names. Each Map value is a unmodifiable List + * of Strings that represents the corresponding + * field values. + * + * @return a Map of the general request properties for this connection. + * @throws IllegalStateException if already connected + * @since 1.4 + */ + public Map> getRequestProperties() { + return delegate.getRequestProperties(); + } + + /* + * We support JDK 1.2.x so we can't count on these from JDK 1.3. + * We override and supply our own version. + */ + public void setInstanceFollowRedirects(boolean shouldFollow) { + delegate.setInstanceFollowRedirects(shouldFollow); + } + + public boolean getInstanceFollowRedirects() { + return delegate.getInstanceFollowRedirects(); + } + + public void setRequestMethod(String method) throws ProtocolException { + delegate.setRequestMethod(method); + } + + public String getRequestMethod() { + return delegate.getRequestMethod(); + } + + public String getResponseMessage() throws IOException { + return delegate.getResponseMessage(); + } + + public long getHeaderFieldDate(String name, long Default) { + return delegate.getHeaderFieldDate(name, Default); + } + + public Permission getPermission() throws IOException { + return delegate.getPermission(); + } + + public URL getURL() { + return delegate.getURL(); + } + + public int getContentLength() { + return delegate.getContentLength(); + } + + public long getContentLengthLong() { + return delegate.getContentLengthLong(); + } + + public String getContentType() { + return delegate.getContentType(); + } + + public String getContentEncoding() { + return delegate.getContentEncoding(); + } + + public long getExpiration() { + return delegate.getExpiration(); + } + + public long getDate() { + return delegate.getDate(); + } + + public long getLastModified() { + return delegate.getLastModified(); + } + + public int getHeaderFieldInt(String name, int Default) { + return delegate.getHeaderFieldInt(name, Default); + } + + public long getHeaderFieldLong(String name, long Default) { + return delegate.getHeaderFieldLong(name, Default); + } + + public Object getContent() throws IOException { + return delegate.getContent(); + } + + @SuppressWarnings("rawtypes") + public Object getContent(Class[] classes) throws IOException { + return delegate.getContent(classes); + } + + public String toString() { + return delegate.toString(); + } + + public void setDoInput(boolean doinput) { + delegate.setDoInput(doinput); + } + + public boolean getDoInput() { + return delegate.getDoInput(); + } + + public void setDoOutput(boolean dooutput) { + delegate.setDoOutput(dooutput); + } + + public boolean getDoOutput() { + return delegate.getDoOutput(); + } + + public void setAllowUserInteraction(boolean allowuserinteraction) { + delegate.setAllowUserInteraction(allowuserinteraction); + } + + public boolean getAllowUserInteraction() { + return delegate.getAllowUserInteraction(); + } + + public void setUseCaches(boolean usecaches) { + delegate.setUseCaches(usecaches); + } + + public boolean getUseCaches() { + return delegate.getUseCaches(); + } + + public void setIfModifiedSince(long ifmodifiedsince) { + delegate.setIfModifiedSince(ifmodifiedsince); + } + + public long getIfModifiedSince() { + return delegate.getIfModifiedSince(); + } + + public boolean getDefaultUseCaches() { + return delegate.getDefaultUseCaches(); + } + + public void setDefaultUseCaches(boolean defaultusecaches) { + delegate.setDefaultUseCaches(defaultusecaches); + } + + /* + * finalize (dispose) the delegated object. Otherwise + * sun.net.www.protocol.http.HttpURLConnection's finalize() + * would have to be made public. + */ + protected void finalize() throws Throwable { + delegate.dispose(); + } + + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + public int hashCode() { + return delegate.hashCode(); + } + + public void setConnectTimeout(int timeout) { + delegate.setConnectTimeout(timeout); + } + + public int getConnectTimeout() { + return delegate.getConnectTimeout(); + } + + public void setReadTimeout(int timeout) { + delegate.setReadTimeout(timeout); + } + + public int getReadTimeout() { + return delegate.getReadTimeout(); + } + + public void setFixedLengthStreamingMode (int contentLength) { + delegate.setFixedLengthStreamingMode(contentLength); + } + + public void setFixedLengthStreamingMode(long contentLength) { + delegate.setFixedLengthStreamingMode(contentLength); + } + + public void setChunkedStreamingMode (int chunklen) { + delegate.setChunkedStreamingMode(chunklen); + } +} diff --git a/src/sun/net/www/protocol/jar/Handler.java b/src/sun/net/www/protocol/jar/Handler.java new file mode 100644 index 00000000..427790a9 --- /dev/null +++ b/src/sun/net/www/protocol/jar/Handler.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.jar; + +import java.io.IOException; +import java.net.*; +import sun.net.www.ParseUtil; + +/* + * Jar URL Handler + */ +public class Handler extends URLStreamHandler { + + private static final String separator = "!/"; + + protected URLConnection openConnection(URL u) + throws IOException { + return new JarURLConnection(u, this); + } + + private static int indexOfBangSlash(String spec) { + int indexOfBang = spec.length(); + while((indexOfBang = spec.lastIndexOf('!', indexOfBang)) != -1) { + if ((indexOfBang != (spec.length() - 1)) && + (spec.charAt(indexOfBang + 1) == '/')) { + return indexOfBang + 1; + } else { + indexOfBang--; + } + } + return -1; + } + + /** + * Compare two jar URLs + */ + @Override + protected boolean sameFile(URL u1, URL u2) { + if (!u1.getProtocol().equals("jar") || !u2.getProtocol().equals("jar")) + return false; + + String file1 = u1.getFile(); + String file2 = u2.getFile(); + int sep1 = file1.indexOf(separator); + int sep2 = file2.indexOf(separator); + + if (sep1 == -1 || sep2 == -1) { + return super.sameFile(u1, u2); + } + + String entry1 = file1.substring(sep1 + 2); + String entry2 = file2.substring(sep2 + 2); + + if (!entry1.equals(entry2)) + return false; + + URL enclosedURL1 = null, enclosedURL2 = null; + try { + enclosedURL1 = new URL(file1.substring(0, sep1)); + enclosedURL2 = new URL(file2.substring(0, sep2)); + } catch (MalformedURLException unused) { + return super.sameFile(u1, u2); + } + + if (!super.sameFile(enclosedURL1, enclosedURL2)) { + return false; + } + + return true; + } + + @Override + protected int hashCode(URL u) { + int h = 0; + + String protocol = u.getProtocol(); + if (protocol != null) + h += protocol.hashCode(); + + String file = u.getFile(); + int sep = file.indexOf(separator); + + if (sep == -1) + return h + file.hashCode(); + + URL enclosedURL = null; + String fileWithoutEntry = file.substring(0, sep); + try { + enclosedURL = new URL(fileWithoutEntry); + h += enclosedURL.hashCode(); + } catch (MalformedURLException unused) { + h += fileWithoutEntry.hashCode(); + } + + String entry = file.substring(sep + 2); + h += entry.hashCode(); + + return h; + } + + + @Override + @SuppressWarnings("deprecation") + protected void parseURL(URL url, String spec, + int start, int limit) { + String file = null; + String ref = null; + // first figure out if there is an anchor + int refPos = spec.indexOf('#', limit); + boolean refOnly = refPos == start; + if (refPos > -1) { + ref = spec.substring(refPos + 1, spec.length()); + if (refOnly) { + file = url.getFile(); + } + } + // then figure out if the spec is + // 1. absolute (jar:) + // 2. relative (i.e. url + foo/bar/baz.ext) + // 3. anchor-only (i.e. url + #foo), which we already did (refOnly) + boolean absoluteSpec = false; + if (spec.length() >= 4) { + absoluteSpec = spec.substring(0, 4).equalsIgnoreCase("jar:"); + } + spec = spec.substring(start, limit); + + if (absoluteSpec) { + file = parseAbsoluteSpec(spec); + } else if (!refOnly) { + file = parseContextSpec(url, spec); + + // Canonize the result after the bangslash + int bangSlash = indexOfBangSlash(file); + String toBangSlash = file.substring(0, bangSlash); + String afterBangSlash = file.substring(bangSlash); + ParseUtil canonizer = new ParseUtil(); + afterBangSlash = canonizer.canonizeString(afterBangSlash); + file = toBangSlash + afterBangSlash; + } + setURL(url, "jar", "", -1, file, ref); + } + + private String parseAbsoluteSpec(String spec) { + URL url = null; + int index = -1; + // check for !/ + if ((index = indexOfBangSlash(spec)) == -1) { + throw new NullPointerException("no !/ in spec"); + } + // test the inner URL + try { + String innerSpec = spec.substring(0, index - 1); + url = new URL(innerSpec); + } catch (MalformedURLException e) { + throw new NullPointerException("invalid url: " + + spec + " (" + e + ")"); + } + return spec; + } + + private String parseContextSpec(URL url, String spec) { + String ctxFile = url.getFile(); + // if the spec begins with /, chop up the jar back !/ + if (spec.startsWith("/")) { + int bangSlash = indexOfBangSlash(ctxFile); + if (bangSlash == -1) { + throw new NullPointerException("malformed " + + "context url:" + + url + + ": no !/"); + } + ctxFile = ctxFile.substring(0, bangSlash); + } + if (!ctxFile.endsWith("/") && (!spec.startsWith("/"))){ + // chop up the last component + int lastSlash = ctxFile.lastIndexOf('/'); + if (lastSlash == -1) { + throw new NullPointerException("malformed " + + "context url:" + + url); + } + ctxFile = ctxFile.substring(0, lastSlash + 1); + } + return (ctxFile + spec); + } +} diff --git a/src/sun/net/www/protocol/jar/JarURLConnection.java b/src/sun/net/www/protocol/jar/JarURLConnection.java new file mode 100644 index 00000000..c6779a0b --- /dev/null +++ b/src/sun/net/www/protocol/jar/JarURLConnection.java @@ -0,0 +1,374 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.jar; + +import java.io.InputStream; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.BufferedInputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import java.net.UnknownServiceException; +import java.util.Enumeration; +import java.util.Map; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.security.Permission; + +/** + * @author Benjamin Renaud + * @since 1.2 + */ +public class JarURLConnection extends java.net.JarURLConnection { + + private static final boolean debug = false; + + /* the Jar file factory. It handles both retrieval and caching. + */ + private static final JarFileFactory factory = JarFileFactory.getInstance(); + + /* the url for the Jar file */ + private URL jarFileURL; + + /* the permission to get this JAR file. This is the actual, ultimate, + * permission, returned by the jar file factory. + */ + private Permission permission; + + /* the url connection for the JAR file */ + private URLConnection jarFileURLConnection; + + /* the entry name, if any */ + private String entryName; + + /* the JarEntry */ + private JarEntry jarEntry; + + /* the jar file corresponding to this connection */ + private JarFile jarFile; + + /* the content type for this connection */ + private String contentType; + + public JarURLConnection(URL url, Handler handler) + throws MalformedURLException, IOException { + super(url); + + jarFileURL = getJarFileURL(); + jarFileURLConnection = jarFileURL.openConnection(); + entryName = getEntryName(); + } + + public JarFile getJarFile() throws IOException { + connect(); + return jarFile; + } + + public JarEntry getJarEntry() throws IOException { + connect(); + return jarEntry; + } + + public Permission getPermission() throws IOException { + return jarFileURLConnection.getPermission(); + } + + class JarURLInputStream extends java.io.FilterInputStream { + JarURLInputStream (InputStream src) { + super (src); + } + public void close () throws IOException { + try { + super.close(); + } finally { + if (!getUseCaches()) { + jarFile.close(); + } + } + } + } + + + + public void connect() throws IOException { + if (!connected) { + /* the factory call will do the security checks */ + jarFile = factory.get(getJarFileURL(), getUseCaches()); + + /* we also ask the factory the permission that was required + * to get the jarFile, and set it as our permission. + */ + if (getUseCaches()) { + jarFileURLConnection = factory.getConnection(jarFile); + } + + if ((entryName != null)) { + jarEntry = (JarEntry)jarFile.getEntry(entryName); + if (jarEntry == null) { + try { + if (!getUseCaches()) { + jarFile.close(); + } + } catch (Exception e) { + } + throw new FileNotFoundException("JAR entry " + entryName + + " not found in " + + jarFile.getName()); + } + } + connected = true; + } + } + + public InputStream getInputStream() throws IOException { + connect(); + + InputStream result = null; + + if (entryName == null) { + throw new IOException("no entry name specified"); + } else { + if (jarEntry == null) { + throw new FileNotFoundException("JAR entry " + entryName + + " not found in " + + jarFile.getName()); + } + result = new JarURLInputStream (jarFile.getInputStream(jarEntry)); + } + return result; + } + + public int getContentLength() { + long result = getContentLengthLong(); + if (result > Integer.MAX_VALUE) + return -1; + return (int) result; + } + + public long getContentLengthLong() { + long result = -1; + try { + connect(); + if (jarEntry == null) { + /* if the URL referes to an archive */ + result = jarFileURLConnection.getContentLengthLong(); + } else { + /* if the URL referes to an archive entry */ + result = getJarEntry().getSize(); + } + } catch (IOException e) { + } + return result; + } + + public Object getContent() throws IOException { + Object result = null; + + connect(); + if (entryName == null) { + result = jarFile; + } else { + result = super.getContent(); + } + return result; + } + + public String getContentType() { + if (contentType == null) { + if (entryName == null) { + contentType = "x-java/jar"; + } else { + try { + connect(); + InputStream in = jarFile.getInputStream(jarEntry); + contentType = guessContentTypeFromStream( + new BufferedInputStream(in)); + in.close(); + } catch (IOException e) { + // don't do anything + } + } + if (contentType == null) { + contentType = guessContentTypeFromName(entryName); + } + if (contentType == null) { + contentType = "content/unknown"; + } + } + return contentType; + } + + public String getHeaderField(String name) { + return jarFileURLConnection.getHeaderField(name); + } + + /** + * Sets the general request property. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + */ + public void setRequestProperty(String key, String value) { + jarFileURLConnection.setRequestProperty(key, value); + } + + /** + * Returns the value of the named general request property for this + * connection. + * + * @return the value of the named general request property for this + * connection. + */ + public String getRequestProperty(String key) { + return jarFileURLConnection.getRequestProperty(key); + } + + /** + * Adds a general request property specified by a + * key-value pair. This method will not overwrite + * existing values associated with the same key. + * + * @param key the keyword by which the request is known + * (e.g., "accept"). + * @param value the value associated with it. + */ + public void addRequestProperty(String key, String value) { + jarFileURLConnection.addRequestProperty(key, value); + } + + /** + * Returns an unmodifiable Map of general request + * properties for this connection. The Map keys + * are Strings that represent the request-header + * field names. Each Map value is a unmodifiable List + * of Strings that represents the corresponding + * field values. + * + * @return a Map of the general request properties for this connection. + */ + public Map> getRequestProperties() { + return jarFileURLConnection.getRequestProperties(); + } + + /** + * Set the value of the allowUserInteraction field of + * this URLConnection. + * + * @param allowuserinteraction the new value. + * @see URLConnection#allowUserInteraction + */ + public void setAllowUserInteraction(boolean allowuserinteraction) { + jarFileURLConnection.setAllowUserInteraction(allowuserinteraction); + } + + /** + * Returns the value of the allowUserInteraction field for + * this object. + * + * @return the value of the allowUserInteraction field for + * this object. + * @see URLConnection#allowUserInteraction + */ + public boolean getAllowUserInteraction() { + return jarFileURLConnection.getAllowUserInteraction(); + } + + /* + * cache control + */ + + /** + * Sets the value of the useCaches field of this + * URLConnection to the specified value. + *

    + * Some protocols do caching of documents. Occasionally, it is important + * to be able to "tunnel through" and ignore the caches (e.g., the + * "reload" button in a browser). If the UseCaches flag on a connection + * is true, the connection is allowed to use whatever caches it can. + * If false, caches are to be ignored. + * The default value comes from DefaultUseCaches, which defaults to + * true. + * + * @see URLConnection#useCaches + */ + public void setUseCaches(boolean usecaches) { + jarFileURLConnection.setUseCaches(usecaches); + } + + /** + * Returns the value of this URLConnection's + * useCaches field. + * + * @return the value of this URLConnection's + * useCaches field. + * @see URLConnection#useCaches + */ + public boolean getUseCaches() { + return jarFileURLConnection.getUseCaches(); + } + + /** + * Sets the value of the ifModifiedSince field of + * this URLConnection to the specified value. + * + * @param value the new value. + * @see URLConnection#ifModifiedSince + */ + public void setIfModifiedSince(long ifmodifiedsince) { + jarFileURLConnection.setIfModifiedSince(ifmodifiedsince); + } + + /** + * Sets the default value of the useCaches field to the + * specified value. + * + * @param defaultusecaches the new value. + * @see URLConnection#useCaches + */ + public void setDefaultUseCaches(boolean defaultusecaches) { + jarFileURLConnection.setDefaultUseCaches(defaultusecaches); + } + + /** + * Returns the default value of a URLConnection's + * useCaches flag. + *

    + * Ths default is "sticky", being a part of the static state of all + * URLConnections. This flag applies to the next, and all following + * URLConnections that are created. + * + * @return the default value of a URLConnection's + * useCaches flag. + * @see URLConnection#useCaches + */ + public boolean getDefaultUseCaches() { + return jarFileURLConnection.getDefaultUseCaches(); + } +} diff --git a/src/sun/net/www/protocol/jar/URLJarFile.java b/src/sun/net/www/protocol/jar/URLJarFile.java new file mode 100644 index 00000000..1656ef7b --- /dev/null +++ b/src/sun/net/www/protocol/jar/URLJarFile.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.jar; + +import java.io.*; +import java.net.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.jar.*; +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; +import java.security.CodeSigner; +import java.security.cert.Certificate; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import sun.net.www.ParseUtil; + +/* URL jar file is a common JarFile subtype used for JarURLConnection */ +public class URLJarFile extends JarFile { + + /* + * Interface to be able to call retrieve() in plugin if + * this variable is set. + */ + private static URLJarFileCallBack callback = null; + + /* Controller of the Jar File's closing */ + private URLJarFileCloseController closeController = null; + + private static int BUF_SIZE = 2048; + + private Manifest superMan; + private Attributes superAttr; + private Map superEntries; + + static JarFile getJarFile(URL url) throws IOException { + return getJarFile(url, null); + } + + static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException { + if (isFileURL(url)) + return new URLJarFile(url, closeController); + else { + return retrieve(url, closeController); + } + } + + /* + * Changed modifier from private to public in order to be able + * to instantiate URLJarFile from sun.plugin package. + */ + public URLJarFile(File file) throws IOException { + this(file, null); + } + + /* + * Changed modifier from private to public in order to be able + * to instantiate URLJarFile from sun.plugin package. + */ + public URLJarFile(File file, URLJarFileCloseController closeController) throws IOException { + super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); + this.closeController = closeController; + } + + private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException { + super(ParseUtil.decode(url.getFile())); + this.closeController = closeController; + } + + private static boolean isFileURL(URL url) { + if (url.getProtocol().equalsIgnoreCase("file")) { + /* + * Consider this a 'file' only if it's a LOCAL file, because + * 'file:' URLs can be accessible through ftp. + */ + String host = url.getHost(); + if (host == null || host.equals("") || host.equals("~") || + host.equalsIgnoreCase("localhost")) + return true; + } + return false; + } + + /* + * close the jar file. + */ + protected void finalize() throws IOException { + close(); + } + + /** + * Returns the ZipEntry for the given entry name or + * null if not found. + * + * @param name the JAR file entry name + * @return the ZipEntry for the given entry name or + * null if not found + * @see ZipEntry + */ + public ZipEntry getEntry(String name) { + ZipEntry ze = super.getEntry(name); + if (ze != null) { + if (ze instanceof JarEntry) + return new URLJarFileEntry((JarEntry)ze); + else + throw new InternalError(super.getClass() + + " returned unexpected entry type " + + ze.getClass()); + } + return null; + } + + public Manifest getManifest() throws IOException { + + if (!isSuperMan()) { + return null; + } + + Manifest man = new Manifest(); + Attributes attr = man.getMainAttributes(); + attr.putAll((Map)superAttr.clone()); + + // now deep copy the manifest entries + if (superEntries != null) { + Map entries = man.getEntries(); + for (String key : superEntries.keySet()) { + Attributes at = superEntries.get(key); + entries.put(key, (Attributes) at.clone()); + } + } + + return man; + } + + /* If close controller is set the notify the controller about the pending close */ + public void close() throws IOException { + if (closeController != null) { + closeController.close(this); + } + super.close(); + } + + // optimal side-effects + private synchronized boolean isSuperMan() throws IOException { + + if (superMan == null) { + superMan = super.getManifest(); + } + + if (superMan != null) { + superAttr = superMan.getMainAttributes(); + superEntries = superMan.getEntries(); + return true; + } else + return false; + } + + /** + * Given a URL, retrieves a JAR file, caches it to disk, and creates a + * cached JAR file object. + */ + private static JarFile retrieve(final URL url) throws IOException { + return retrieve(url, null); + } + + /** + * Given a URL, retrieves a JAR file, caches it to disk, and creates a + * cached JAR file object. + */ + private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException { + /* + * See if interface is set, then call retrieve function of the class + * that implements URLJarFileCallBack interface (sun.plugin - to + * handle the cache failure for JARJAR file.) + */ + if (callback != null) + { + return callback.retrieve(url); + } + + else + { + + JarFile result = null; + + /* get the stream before asserting privileges */ + try (final InputStream in = url.openConnection().getInputStream()) { + result = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public JarFile run() throws IOException { + Path tmpFile = Files.createTempFile("jar_cache", null); + try { + Files.copy(in, tmpFile, StandardCopyOption.REPLACE_EXISTING); + JarFile jarFile = new URLJarFile(tmpFile.toFile(), closeController); + tmpFile.toFile().deleteOnExit(); + return jarFile; + } catch (Throwable thr) { + try { + Files.delete(tmpFile); + } catch (IOException ioe) { + thr.addSuppressed(ioe); + } + throw thr; + } + } + }); + } catch (PrivilegedActionException pae) { + throw (IOException) pae.getException(); + } + + return result; + } + } + + /* + * Set the call back interface to call retrive function in sun.plugin + * package if plugin is running. + */ + public static void setCallBack(URLJarFileCallBack cb) + { + callback = cb; + } + + + private class URLJarFileEntry extends JarEntry { + private JarEntry je; + + URLJarFileEntry(JarEntry je) { + super(je); + this.je=je; + } + + public Attributes getAttributes() throws IOException { + if (URLJarFile.this.isSuperMan()) { + Map e = URLJarFile.this.superEntries; + if (e != null) { + Attributes a = e.get(getName()); + if (a != null) + return (Attributes)a.clone(); + } + } + return null; + } + + public Certificate[] getCertificates() { + Certificate[] certs = je.getCertificates(); + return certs == null? null: certs.clone(); + } + + public CodeSigner[] getCodeSigners() { + CodeSigner[] csg = je.getCodeSigners(); + return csg == null? null: csg.clone(); + } + } + + public interface URLJarFileCloseController { + public void close(JarFile jarFile); + } +} diff --git a/src/sun/net/www/protocol/jar/URLJarFileCallBack.java b/src/sun/net/www/protocol/jar/URLJarFileCallBack.java new file mode 100644 index 00000000..d55aeafd --- /dev/null +++ b/src/sun/net/www/protocol/jar/URLJarFileCallBack.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.jar; + +import java.io.*; +import java.net.*; +import java.util.jar.*; + + +/* + * This interface is used to call back to sun.plugin package. + */ +public interface URLJarFileCallBack +{ + public JarFile retrieve (URL url) throws IOException; +} diff --git a/src/sun/net/www/protocol/mailto/Handler.java b/src/sun/net/www/protocol/mailto/Handler.java new file mode 100644 index 00000000..7207d5d0 --- /dev/null +++ b/src/sun/net/www/protocol/mailto/Handler.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * mailto stream opener + */ + +package sun.net.www.protocol.mailto; + +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.io.*; +import sun.net.www.*; +//import sun.net.www.protocol.news.ArticlePoster; +import sun.net.smtp.SmtpClient; + +/** open an nntp input stream given a URL */ +public class Handler extends URLStreamHandler { + +/* +// private String decodePercent(String s) { +// if (s==null || s.indexOf('%') < 0) +// return s; +// int limit = s.length(); +// char d[] = new char[limit]; +// int dp = 0; +// for (int sp = 0; sp < limit; sp++) { +// int c = s.charAt(sp); +// if (c == '%' && sp + 2 < limit) { +// int s1 = s.charAt(sp + 1); +// int s2 = s.charAt(sp + 2); +// if ('0' <= s1 && s1 <= '9') +// s1 = s1 - '0'; +// else if ('a' <= s1 && s1 <= 'f') +// s1 = s1 - 'a' + 10; +// else if ('A' <= s1 && s1 <= 'F') +// s1 = s1 - 'A' + 10; +// else +// s1 = -1; +// if ('0' <= s2 && s2 <= '9') +// s2 = s2 - '0'; +// else if ('a' <= s2 && s2 <= 'f') +// s2 = s2 - 'a' + 10; +// else if ('A' <= s2 && s2 <= 'F') +// s2 = s2 - 'A' + 10; +// else +// s2 = -1; +// if (s1 >= 0 && s2 >= 0) { +// c = (s1 << 4) | s2; +// sp += 2; +// } +// } +// d[dp++] = (char) c; +// } +// return new String(d, 0, dp); +// } + +// public InputStream openStream(URL u) { +// String dest = u.file; +// String subj = ""; +// int lastsl = dest.lastIndexOf('/'); +// if (lastsl >= 0) { +// int st = dest.charAt(0) == '/' ? 1 : 0; +// if (lastsl > st) +// subj = dest.substring(st, lastsl); +// dest = dest.substring(lastsl + 1); +// } +// if (u.postData != null) { +// ArticlePoster.MailTo("Posted form", +// decodePercent(dest), +// u.postData); +// } +// else +// ArticlePoster.MailTo(decodePercent(subj), decodePercent(dest)); +// return null; +// } + */ + + public synchronized URLConnection openConnection(URL u) { + return new MailToURLConnection(u); + } + + /** + * This method is called to parse the string spec into URL u for a + * mailto protocol. + * + * @param u the URL to receive the result of parsing the spec + * @param spec the URL string to parse + * @param start the character position to start parsing at. This is + * just past the ':'. + * @param limit the character position to stop parsing at. + */ + public void parseURL(URL u, String spec, int start, int limit) { + + String protocol = u.getProtocol(); + String host = ""; + int port = u.getPort(); + String file = ""; + + if (start < limit) { + file = spec.substring(start, limit); + } + /* + * Let's just make sure we DO have an Email address in the URL. + */ + boolean nogood = false; + if (file == null || file.equals("")) + nogood = true; + else { + boolean allwhites = true; + for (int i = 0; i < file.length(); i++) + if (!Character.isWhitespace(file.charAt(i))) + allwhites = false; + if (allwhites) + nogood = true; + } + if (nogood) + throw new RuntimeException("No email address"); + setURLHandler(u, protocol, host, port, file, null); + } + + /** + * This method is used to suppress the deprecated warning + * + * @param u the URL to receive the result of parsing the spec + * @param spec the URL string to parse + * @param start the character position to start parsing at. This is + * just past the ':'. + * @param limit the character position to stop parsing at. + */ + @SuppressWarnings("deprecation") + private void setURLHandler(URL u, String protocol, String host, int port, String file, String ref) { + setURL(u,protocol,host,port,file,null); + } +} diff --git a/src/sun/net/www/protocol/mailto/MailToURLConnection.java b/src/sun/net/www/protocol/mailto/MailToURLConnection.java new file mode 100644 index 00000000..df4d3306 --- /dev/null +++ b/src/sun/net/www/protocol/mailto/MailToURLConnection.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.net.www.protocol.mailto; + +import java.net.URL; +import java.net.InetAddress; +import java.net.SocketPermission; +import java.io.*; +import java.security.Permission; +import sun.net.www.*; +import sun.net.smtp.SmtpClient; +import sun.net.www.ParseUtil; + + +/** + * Handle mailto URLs. To send mail using a mailto URLConnection, + * call getOutputStream, write the message to the output + * stream, and close it. + * + */ +public class MailToURLConnection extends URLConnection { + InputStream is = null; + OutputStream os = null; + + SmtpClient client; + Permission permission; + private int connectTimeout = -1; + private int readTimeout = -1; + + MailToURLConnection(URL u) { + super(u); + + MessageHeader props = new MessageHeader(); + props.add("content-type", "text/html"); + setProperties(props); + } + + /** + * Get the user's full email address - stolen from + * HotJavaApplet.getMailAddress(). + */ + String getFromAddress() { + String str = System.getProperty("user.fromaddr"); + if (str == null) { + str = System.getProperty("user.name"); + if (str != null) { + String host = System.getProperty("mail.host"); + if (host == null) { + try { + host = InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException e) { + } + } + str += "@" + host; + } else { + str = ""; + } + } + return str; + } + + public void connect() throws IOException { + client = new SmtpClient(connectTimeout); + client.setReadTimeout(readTimeout); + } + + @Override + public synchronized OutputStream getOutputStream() throws IOException { + if (os != null) { + return os; + } else if (is != null) { + throw new IOException("Cannot write output after reading input."); + } + connect(); + + String to = ParseUtil.decode(url.getPath()); + client.from(getFromAddress()); + client.to(to); + + os = client.startMessage(); + return os; + } + + @Override + public Permission getPermission() throws IOException { + if (permission == null) { + connect(); + String host = client.getMailHost() + ":" + 25; + permission = new SocketPermission(host, "connect"); + } + return permission; + } + + @Override + public void setConnectTimeout(int timeout) { + if (timeout < 0) + throw new IllegalArgumentException("timeouts can't be negative"); + connectTimeout = timeout; + } + + @Override + public int getConnectTimeout() { + return (connectTimeout < 0 ? 0 : connectTimeout); + } + + @Override + public void setReadTimeout(int timeout) { + if (timeout < 0) + throw new IllegalArgumentException("timeouts can't be negative"); + readTimeout = timeout; + } + + @Override + public int getReadTimeout() { + return readTimeout < 0 ? 0 : readTimeout; + } +} diff --git a/src/sun/net/www/protocol/netdoc/Handler.java b/src/sun/net/www/protocol/netdoc/Handler.java new file mode 100644 index 00000000..fbcddcb8 --- /dev/null +++ b/src/sun/net/www/protocol/netdoc/Handler.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/*- + * netdoc urls point either into the local filesystem or externally + * through an http url, with network documents being preferred. Useful for + * FAQs & other documents which are likely to be changing over time at the + * central site, and where the user will want the most recent edition. + * + * @author Steven B. Byrne + */ + +package sun.net.www.protocol.netdoc; + +import java.net.URL; +import java.net.URLConnection; +import java.net.MalformedURLException; +import java.net.URLStreamHandler; +import java.io.InputStream; +import java.io.IOException; + +public class Handler extends URLStreamHandler { + static URL base; + + /* + * Attempt to find a load the given url using the default (network) + * documentation location. If that fails, use the local copy + */ + public synchronized URLConnection openConnection(URL u) + throws IOException + { + URLConnection uc = null; + URL ru; + + Boolean tmp = java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction("newdoc.localonly")); + boolean localonly = tmp.booleanValue(); + + String docurl = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("doc.url")); + + String file = u.getFile(); + if (!localonly) { + try { + if (base == null) { + base = new URL(docurl); + } + ru = new URL(base, file); + } catch (MalformedURLException e) { + ru = null; + } + if (ru != null) { + uc = ru.openConnection(); + } + } + + if (uc == null) { + try { + ru = new URL("file", "~", file); + + uc = ru.openConnection(); + InputStream is = uc.getInputStream(); // Check for success. + } catch (MalformedURLException e) { + uc = null; + } catch (IOException e) { + uc = null; + } + } + + if (uc == null) { + throw new IOException("Can't find file for URL: " + +u.toExternalForm()); + } + return uc; + } +} diff --git a/src/sun/nio/ByteBuffered.java b/src/sun/nio/ByteBuffered.java new file mode 100644 index 00000000..e294194c --- /dev/null +++ b/src/sun/nio/ByteBuffered.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** This is an interface to adapt existing APIs to use {@link ByteBuffer + * ByteBuffers} as the underlying + * data format. Only the initial producer and final consumer have to be changed.

    + * + * For example, the Zip/Jar code supports {@link java.io.InputStream InputStreams}. + * To make the Zip code use {@link java.nio.MappedByteBuffer MappedByteBuffers} as + * the underlying data structure, it can create a class of InputStream that wraps the ByteBuffer, + * and implements the ByteBuffered interface. A co-operating class several layers + * away can ask the InputStream if it is an instance of ByteBuffered, then + * call the {@link #getByteBuffer()} method. + */ +public interface ByteBuffered { + + /** + * Returns the ByteBuffer behind this object, if this particular + * instance has one. An implementation of getByteBuffer() is allowed + * to return null for any reason. + * + * @return The ByteBuffer, if this particular instance has one, + * or null otherwise. + * + * @throws IOException + * If the ByteBuffer is no longer valid. + * + * @since 1.5 + */ + public ByteBuffer getByteBuffer() throws IOException; +} diff --git a/src/sun/nio/ch/AbstractPollArrayWrapper.java b/src/sun/nio/ch/AbstractPollArrayWrapper.java new file mode 100644 index 00000000..c25ec92b --- /dev/null +++ b/src/sun/nio/ch/AbstractPollArrayWrapper.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + + +/** + * Manipulates a native array of pollfd structs. + * + * @author Mike McCloskey + * @since 1.4 + */ + +public abstract class AbstractPollArrayWrapper { + + // Miscellaneous constants + static final short SIZE_POLLFD = 8; + static final short FD_OFFSET = 0; + static final short EVENT_OFFSET = 4; + static final short REVENT_OFFSET = 6; + + // The poll fd array + protected AllocatedNativeObject pollArray; + + // Number of valid entries in the pollArray + protected int totalChannels = 0; + + // Base address of the native pollArray + protected long pollArrayAddress; + + // Access methods for fd structures + int getEventOps(int i) { + int offset = SIZE_POLLFD * i + EVENT_OFFSET; + return pollArray.getShort(offset); + } + + int getReventOps(int i) { + int offset = SIZE_POLLFD * i + REVENT_OFFSET; + return pollArray.getShort(offset); + } + + int getDescriptor(int i) { + int offset = SIZE_POLLFD * i + FD_OFFSET; + return pollArray.getInt(offset); + } + + void putEventOps(int i, int event) { + int offset = SIZE_POLLFD * i + EVENT_OFFSET; + pollArray.putShort(offset, (short)event); + } + + void putReventOps(int i, int revent) { + int offset = SIZE_POLLFD * i + REVENT_OFFSET; + pollArray.putShort(offset, (short)revent); + } + + void putDescriptor(int i, int fd) { + int offset = SIZE_POLLFD * i + FD_OFFSET; + pollArray.putInt(offset, fd); + } + +} diff --git a/src/sun/nio/ch/AbstractPollSelectorImpl.java b/src/sun/nio/ch/AbstractPollSelectorImpl.java new file mode 100644 index 00000000..d9a46d81 --- /dev/null +++ b/src/sun/nio/ch/AbstractPollSelectorImpl.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectionKey; +import java.nio.channels.spi.SelectorProvider; + + +/** + * An abstract selector impl. + */ + +abstract class AbstractPollSelectorImpl + extends SelectorImpl +{ + + // The poll fd array + PollArrayWrapper pollWrapper; + + // Initial capacity of the pollfd array + protected final int INIT_CAP = 10; + + // The list of SelectableChannels serviced by this Selector + protected SelectionKeyImpl[] channelArray; + + // In some impls the first entry of channelArray is bogus + protected int channelOffset = 0; + + // The number of valid channels in this Selector's poll array + protected int totalChannels; + + // True if this Selector has been closed + private boolean closed = false; + + // Lock for close and cleanup + private Object closeLock = new Object(); + + AbstractPollSelectorImpl(SelectorProvider sp, int channels, int offset) { + super(sp); + this.totalChannels = channels; + this.channelOffset = offset; + } + + public void putEventOps(SelectionKeyImpl sk, int ops) { + synchronized (closeLock) { + if (closed) + throw new ClosedSelectorException(); + pollWrapper.putEventOps(sk.getIndex(), ops); + } + } + + public Selector wakeup() { + pollWrapper.interrupt(); + return this; + } + + protected abstract int doSelect(long timeout) throws IOException; + + protected void implClose() throws IOException { + synchronized (closeLock) { + if (closed) + return; + closed = true; + // Deregister channels + for(int i=channelOffset; i= 0); + if (i != totalChannels - 1) { + // Copy end one over it + SelectionKeyImpl endChannel = channelArray[totalChannels-1]; + channelArray[i] = endChannel; + endChannel.setIndex(i); + pollWrapper.release(i); + PollArrayWrapper.replaceEntry(pollWrapper, totalChannels - 1, + pollWrapper, i); + } else { + pollWrapper.release(i); + } + // Destroy the last one + channelArray[totalChannels-1] = null; + totalChannels--; + pollWrapper.totalChannels--; + ski.setIndex(-1); + // Remove the key from keys and selectedKeys + keys.remove(ski); + selectedKeys.remove(ski); + deregister((AbstractSelectionKey)ski); + SelectableChannel selch = ski.channel(); + if (!selch.isOpen() && !selch.isRegistered()) + ((SelChImpl)selch).kill(); + } +} diff --git a/src/sun/nio/ch/AllocatedNativeObject.java b/src/sun/nio/ch/AllocatedNativeObject.java new file mode 100644 index 00000000..2406cb8f --- /dev/null +++ b/src/sun/nio/ch/AllocatedNativeObject.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.ch; // Formerly in sun.misc + + +// ## In the fullness of time, this class will be eliminated + +class AllocatedNativeObject // package-private + extends NativeObject +{ + + /** + * Allocates a memory area of at least size bytes outside of the + * Java heap and creates a native object for that area. + * + * @param size + * Number of bytes to allocate + * + * @param pageAligned + * If true then the area will be aligned on a hardware + * page boundary + * + * @throws OutOfMemoryError + * If the request cannot be satisfied + */ + AllocatedNativeObject(int size, boolean pageAligned) { + super(size, pageAligned); + } + + /** + * Frees the native memory area associated with this object. + */ + synchronized void free() { + if (allocationAddress != 0) { + unsafe.freeMemory(allocationAddress); + allocationAddress = 0; + } + } + +} diff --git a/src/sun/nio/ch/AsynchronousChannelGroupImpl.java b/src/sun/nio/ch/AsynchronousChannelGroupImpl.java new file mode 100644 index 00000000..ef284ca7 --- /dev/null +++ b/src/sun/nio/ch/AsynchronousChannelGroupImpl.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.Channel; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import sun.security.action.GetIntegerAction; + +/** + * Base implementation of AsynchronousChannelGroup + */ + +abstract class AsynchronousChannelGroupImpl + extends AsynchronousChannelGroup implements Executor +{ + // number of internal threads handling I/O events when using an unbounded + // thread pool. Internal threads do not dispatch to completion handlers. + private static final int internalThreadCount = AccessController.doPrivileged( + new GetIntegerAction("sun.nio.ch.internalThreadPoolSize", 1)); + + // associated thread pool + private final ThreadPool pool; + + // number of tasks running (including internal) + private final AtomicInteger threadCount = new AtomicInteger(); + + // associated Executor for timeouts + private ScheduledThreadPoolExecutor timeoutExecutor; + + // task queue for when using a fixed thread pool. In that case, thread + // waiting on I/O events must be awokon to poll tasks from this queue. + private final Queue taskQueue; + + // group shutdown + private final AtomicBoolean shutdown = new AtomicBoolean(); + private final Object shutdownNowLock = new Object(); + private volatile boolean terminateInitiated; + + AsynchronousChannelGroupImpl(AsynchronousChannelProvider provider, + ThreadPool pool) + { + super(provider); + this.pool = pool; + + if (pool.isFixedThreadPool()) { + taskQueue = new ConcurrentLinkedQueue(); + } else { + taskQueue = null; // not used + } + + // use default thread factory as thread should not be visible to + // application (it doesn't execute completion handlers). + this.timeoutExecutor = (ScheduledThreadPoolExecutor) + Executors.newScheduledThreadPool(1, ThreadPool.defaultThreadFactory()); + this.timeoutExecutor.setRemoveOnCancelPolicy(true); + } + + final ExecutorService executor() { + return pool.executor(); + } + + final boolean isFixedThreadPool() { + return pool.isFixedThreadPool(); + } + + final int fixedThreadCount() { + if (isFixedThreadPool()) { + return pool.poolSize(); + } else { + return pool.poolSize() + internalThreadCount; + } + } + + private Runnable bindToGroup(final Runnable task) { + final AsynchronousChannelGroupImpl thisGroup = this; + return new Runnable() { + public void run() { + Invoker.bindToGroup(thisGroup); + task.run(); + } + }; + } + + private void startInternalThread(final Runnable task) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + // internal threads should not be visible to application so + // cannot use user-supplied thread factory + ThreadPool.defaultThreadFactory().newThread(task).start(); + return null; + } + }); + } + + protected final void startThreads(Runnable task) { + if (!isFixedThreadPool()) { + for (int i=0; i 0) { + task = bindToGroup(task); + try { + for (int i=0; i schedule(Runnable task, long timeout, TimeUnit unit) { + try { + return timeoutExecutor.schedule(task, timeout, unit); + } catch (RejectedExecutionException rej) { + if (terminateInitiated) { + // no timeout scheduled as group is terminating + return null; + } + throw new AssertionError(rej); + } + } + + @Override + public final boolean isShutdown() { + return shutdown.get(); + } + + @Override + public final boolean isTerminated() { + return pool.executor().isTerminated(); + } + + /** + * Returns true if there are no channels in the group + */ + abstract boolean isEmpty(); + + /** + * Attaches a foreign channel to this group. + */ + abstract Object attachForeignChannel(Channel channel, FileDescriptor fdo) + throws IOException; + + /** + * Detaches a foreign channel from this group. + */ + abstract void detachForeignChannel(Object key); + + /** + * Closes all channels in the group + */ + abstract void closeAllChannels() throws IOException; + + /** + * Shutdown all tasks waiting for I/O events. + */ + abstract void shutdownHandlerTasks(); + + private void shutdownExecutors() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + pool.executor().shutdown(); + timeoutExecutor.shutdown(); + return null; + } + }); + } + + @Override + public final void shutdown() { + if (shutdown.getAndSet(true)) { + // already shutdown + return; + } + // if there are channels in the group then shutdown will continue + // when the last channel is closed + if (!isEmpty()) { + return; + } + // initiate termination (acquire shutdownNowLock to ensure that other + // threads invoking shutdownNow will block). + synchronized (shutdownNowLock) { + if (!terminateInitiated) { + terminateInitiated = true; + shutdownHandlerTasks(); + shutdownExecutors(); + } + } + } + + @Override + public final void shutdownNow() throws IOException { + shutdown.set(true); + synchronized (shutdownNowLock) { + if (!terminateInitiated) { + terminateInitiated = true; + closeAllChannels(); + shutdownHandlerTasks(); + shutdownExecutors(); + } + } + } + + /** + * For use by AsynchronousFileChannel to release resources without shutting + * down the thread pool. + */ + final void detachFromThreadPool() { + if (shutdown.getAndSet(true)) + throw new AssertionError("Already shutdown"); + if (!isEmpty()) + throw new AssertionError("Group not empty"); + shutdownHandlerTasks(); + } + + @Override + public final boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException + { + return pool.executor().awaitTermination(timeout, unit); + } + + /** + * Executes the given command on one of the channel group's pooled threads. + */ + @Override + public final void execute(Runnable task) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // when a security manager is installed then the user's task + // must be run with the current calling context + final AccessControlContext acc = AccessController.getContext(); + final Runnable delegate = task; + task = new Runnable() { + @Override + public void run() { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + delegate.run(); + return null; + } + }, acc); + } + }; + } + executeOnPooledThread(task); + } +} diff --git a/src/sun/nio/ch/AsynchronousFileChannelImpl.java b/src/sun/nio/ch/AsynchronousFileChannelImpl.java new file mode 100644 index 00000000..3e5d9bc3 --- /dev/null +++ b/src/sun/nio/ch/AsynchronousFileChannelImpl.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.nio.channels.FileLock; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Base implementation of AsynchronousFileChannel. + */ + +abstract class AsynchronousFileChannelImpl + extends AsynchronousFileChannel +{ + // close support + protected final ReadWriteLock closeLock = new ReentrantReadWriteLock(); + protected volatile boolean closed; + + // file descriptor + protected final FileDescriptor fdObj; + + // indicates if open for reading/writing + protected final boolean reading; + protected final boolean writing; + + // associated Executor + protected final ExecutorService executor; + + protected AsynchronousFileChannelImpl(FileDescriptor fdObj, + boolean reading, + boolean writing, + ExecutorService executor) + { + this.fdObj = fdObj; + this.reading = reading; + this.writing = writing; + this.executor = executor; + } + + final ExecutorService executor() { + return executor; + } + + @Override + public final boolean isOpen() { + return !closed; + } + + /** + * Marks the beginning of an I/O operation. + * + * @throws ClosedChannelException If channel is closed + */ + protected final void begin() throws IOException { + closeLock.readLock().lock(); + if (closed) + throw new ClosedChannelException(); + } + + /** + * Marks the end of an I/O operation. + */ + protected final void end() { + closeLock.readLock().unlock(); + } + + /** + * Marks end of I/O operation + */ + protected final void end(boolean completed) throws IOException { + end(); + if (!completed && !isOpen()) + throw new AsynchronousCloseException(); + } + + // -- file locking -- + + abstract Future implLock(long position, + long size, + boolean shared, + A attachment, + CompletionHandler handler); + + @Override + public final Future lock(long position, + long size, + boolean shared) + + { + return implLock(position, size, shared, null, null); + } + + @Override + public final void lock(long position, + long size, + boolean shared, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + implLock(position, size, shared, attachment, handler); + } + + private volatile FileLockTable fileLockTable; + + final void ensureFileLockTableInitialized() throws IOException { + if (fileLockTable == null) { + synchronized (this) { + if (fileLockTable == null) { + fileLockTable = FileLockTable.newSharedFileLockTable(this, fdObj); + } + } + } + } + + final void invalidateAllLocks() throws IOException { + if (fileLockTable != null) { + for (FileLock fl: fileLockTable.removeAll()) { + synchronized (fl) { + if (fl.isValid()) { + FileLockImpl fli = (FileLockImpl)fl; + implRelease(fli); + fli.invalidate(); + } + } + } + } + } + + /** + * Adds region to lock table + */ + protected final FileLockImpl addToFileLockTable(long position, long size, boolean shared) { + final FileLockImpl fli; + try { + // like begin() but returns null instead of exception + closeLock.readLock().lock(); + if (closed) + return null; + + try { + ensureFileLockTableInitialized(); + } catch (IOException x) { + // should not happen + throw new AssertionError(x); + } + fli = new FileLockImpl(this, position, size, shared); + // may throw OverlappedFileLockException + fileLockTable.add(fli); + } finally { + end(); + } + return fli; + } + + protected final void removeFromFileLockTable(FileLockImpl fli) { + fileLockTable.remove(fli); + } + + /** + * Releases the given file lock. + */ + protected abstract void implRelease(FileLockImpl fli) throws IOException; + + /** + * Invoked by FileLockImpl to release the given file lock and remove it + * from the lock table. + */ + final void release(FileLockImpl fli) throws IOException { + try { + begin(); + implRelease(fli); + removeFromFileLockTable(fli); + } finally { + end(); + } + } + + + // -- reading and writing -- + + abstract Future implRead(ByteBuffer dst, + long position, + A attachment, + CompletionHandler handler); + + @Override + public final Future read(ByteBuffer dst, long position) { + return implRead(dst, position, null, null); + } + + @Override + public final void read(ByteBuffer dst, + long position, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + implRead(dst, position, attachment, handler); + } + + abstract Future implWrite(ByteBuffer src, + long position, + A attachment, + CompletionHandler handler); + + + @Override + public final Future write(ByteBuffer src, long position) { + return implWrite(src, position, null, null); + } + + @Override + public final void write(ByteBuffer src, + long position, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + implWrite(src, position, attachment, handler); + } +} diff --git a/src/sun/nio/ch/AsynchronousServerSocketChannelImpl.java b/src/sun/nio/ch/AsynchronousServerSocketChannelImpl.java new file mode 100644 index 00000000..f5ef4050 --- /dev/null +++ b/src/sun/nio/ch/AsynchronousServerSocketChannelImpl.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import sun.net.NetHooks; + +/** + * Base implementation of AsynchronousServerSocketChannel. + */ + +abstract class AsynchronousServerSocketChannelImpl + extends AsynchronousServerSocketChannel + implements Cancellable, Groupable +{ + protected final FileDescriptor fd; + + // the local address to which the channel's socket is bound + protected volatile InetSocketAddress localAddress = null; + + // need this lock to set local address + private final Object stateLock = new Object(); + + // close support + private ReadWriteLock closeLock = new ReentrantReadWriteLock(); + private volatile boolean open = true; + + // set true when accept operation is cancelled + private volatile boolean acceptKilled; + + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + + AsynchronousServerSocketChannelImpl(AsynchronousChannelGroupImpl group) { + super(group.provider()); + this.fd = Net.serverSocket(true); + } + + @Override + public final boolean isOpen() { + return open; + } + + /** + * Marks beginning of access to file descriptor/handle + */ + final void begin() throws IOException { + closeLock.readLock().lock(); + if (!isOpen()) + throw new ClosedChannelException(); + } + + /** + * Marks end of access to file descriptor/handle + */ + final void end() { + closeLock.readLock().unlock(); + } + + /** + * Invoked to close file descriptor/handle. + */ + abstract void implClose() throws IOException; + + @Override + public final void close() throws IOException { + // synchronize with any threads using file descriptor/handle + closeLock.writeLock().lock(); + try { + if (!open) + return; // already closed + open = false; + } finally { + closeLock.writeLock().unlock(); + } + implClose(); + } + + /** + * Invoked by accept to accept connection + */ + abstract Future + implAccept(Object attachment, + CompletionHandler handler); + + + @Override + public final Future accept() { + return implAccept(null, null); + } + + @Override + @SuppressWarnings("unchecked") + public final void accept(A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + implAccept(attachment, (CompletionHandler)handler); + } + + final boolean isAcceptKilled() { + return acceptKilled; + } + + @Override + public final void onCancel(PendingFuture task) { + acceptKilled = true; + } + + @Override + public final AsynchronousServerSocketChannel bind(SocketAddress local, int backlog) + throws IOException + { + InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) : + Net.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + + try { + begin(); + synchronized (stateLock) { + if (localAddress != null) + throw new AlreadyBoundException(); + NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + Net.listen(fd, backlog < 1 ? 50 : backlog); + localAddress = Net.localAddress(fd); + } + } finally { + end(); + } + return this; + } + + @Override + public final SocketAddress getLocalAddress() throws IOException { + if (!isOpen()) + throw new ClosedChannelException(); + return Net.getRevealedLocalAddress(localAddress); + } + + @Override + public final AsynchronousServerSocketChannel setOption(SocketOption name, + T value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + try { + begin(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } + return this; + } finally { + end(); + } + } + + @Override + @SuppressWarnings("unchecked") + public final T getOption(SocketOption name) throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + try { + begin(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } finally { + end(); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(2); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()); + sb.append('['); + if (!isOpen()) + sb.append("closed"); + else { + if (localAddress == null) { + sb.append("unbound"); + } else { + sb.append(Net.getRevealedLocalAddressAsString(localAddress)); + } + } + sb.append(']'); + return sb.toString(); + } +} diff --git a/src/sun/nio/ch/AsynchronousSocketChannelImpl.java b/src/sun/nio/ch/AsynchronousSocketChannelImpl.java new file mode 100644 index 00000000..835b3c22 --- /dev/null +++ b/src/sun/nio/ch/AsynchronousSocketChannelImpl.java @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.ReadPendingException; +import java.nio.channels.WritePendingException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import sun.net.ExtendedOptionsImpl; +import sun.net.NetHooks; + +/** + * Base implementation of AsynchronousSocketChannel + */ + +abstract class AsynchronousSocketChannelImpl + extends AsynchronousSocketChannel + implements Cancellable, Groupable +{ + protected final FileDescriptor fd; + + // protects state, localAddress, and remoteAddress + protected final Object stateLock = new Object(); + + protected volatile InetSocketAddress localAddress = null; + protected volatile InetSocketAddress remoteAddress = null; + + // State, increases monotonically + static final int ST_UNINITIALIZED = -1; + static final int ST_UNCONNECTED = 0; + static final int ST_PENDING = 1; + static final int ST_CONNECTED = 2; + protected volatile int state = ST_UNINITIALIZED; + + // reading state + private final Object readLock = new Object(); + private boolean reading; + private boolean readShutdown; + private boolean readKilled; // further reading disallowed due to timeout + + // writing state + private final Object writeLock = new Object(); + private boolean writing; + private boolean writeShutdown; + private boolean writeKilled; // further writing disallowed due to timeout + + // close support + private final ReadWriteLock closeLock = new ReentrantReadWriteLock(); + private volatile boolean open = true; + + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + + AsynchronousSocketChannelImpl(AsynchronousChannelGroupImpl group) + throws IOException + { + super(group.provider()); + this.fd = Net.socket(true); + this.state = ST_UNCONNECTED; + } + + // Constructor for sockets obtained from AsynchronousServerSocketChannelImpl + AsynchronousSocketChannelImpl(AsynchronousChannelGroupImpl group, + FileDescriptor fd, + InetSocketAddress remote) + throws IOException + { + super(group.provider()); + this.fd = fd; + this.state = ST_CONNECTED; + this.localAddress = Net.localAddress(fd); + this.remoteAddress = remote; + } + + @Override + public final boolean isOpen() { + return open; + } + + /** + * Marks beginning of access to file descriptor/handle + */ + final void begin() throws IOException { + closeLock.readLock().lock(); + if (!isOpen()) + throw new ClosedChannelException(); + } + + /** + * Marks end of access to file descriptor/handle + */ + final void end() { + closeLock.readLock().unlock(); + } + + /** + * Invoked to close socket and release other resources. + */ + abstract void implClose() throws IOException; + + @Override + public final void close() throws IOException { + // synchronize with any threads initiating asynchronous operations + closeLock.writeLock().lock(); + try { + if (!open) + return; // already closed + open = false; + } finally { + closeLock.writeLock().unlock(); + } + implClose(); + } + + final void enableReading(boolean killed) { + synchronized (readLock) { + reading = false; + if (killed) + readKilled = true; + } + } + + final void enableReading() { + enableReading(false); + } + + final void enableWriting(boolean killed) { + synchronized (writeLock) { + writing = false; + if (killed) + writeKilled = true; + } + } + + final void enableWriting() { + enableWriting(false); + } + + final void killReading() { + synchronized (readLock) { + readKilled = true; + } + } + + final void killWriting() { + synchronized (writeLock) { + writeKilled = true; + } + } + + final void killConnect() { + // when a connect is cancelled then the connection may have been + // established so prevent reading or writing. + killReading(); + killWriting(); + } + + /** + * Invoked by connect to initiate the connect operation. + */ + abstract Future implConnect(SocketAddress remote, + A attachment, + CompletionHandler handler); + + @Override + public final Future connect(SocketAddress remote) { + return implConnect(remote, null, null); + } + + @Override + public final void connect(SocketAddress remote, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + implConnect(remote, attachment, handler); + } + + /** + * Invoked by read to initiate the I/O operation. + */ + abstract Future implRead(boolean isScatteringRead, + ByteBuffer dst, + ByteBuffer[] dsts, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + @SuppressWarnings("unchecked") + private Future read(boolean isScatteringRead, + ByteBuffer dst, + ByteBuffer[] dsts, + long timeout, + TimeUnit unit, + A att, + CompletionHandler handler) + { + if (!isOpen()) { + Throwable e = new ClosedChannelException(); + if (handler == null) + return CompletedFuture.withFailure(e); + Invoker.invoke(this, handler, att, null, e); + return null; + } + + if (remoteAddress == null) + throw new NotYetConnectedException(); + + boolean hasSpaceToRead = isScatteringRead || dst.hasRemaining(); + boolean shutdown = false; + + // check and update state + synchronized (readLock) { + if (readKilled) + throw new IllegalStateException("Reading not allowed due to timeout or cancellation"); + if (reading) + throw new ReadPendingException(); + if (readShutdown) { + shutdown = true; + } else { + if (hasSpaceToRead) { + reading = true; + } + } + } + + // immediately complete with -1 if shutdown for read + // immediately complete with 0 if no space remaining + if (shutdown || !hasSpaceToRead) { + Number result; + if (isScatteringRead) { + result = (shutdown) ? Long.valueOf(-1L) : Long.valueOf(0L); + } else { + result = (shutdown) ? -1 : 0; + } + if (handler == null) + return CompletedFuture.withResult((V)result); + Invoker.invoke(this, handler, att, (V)result, null); + return null; + } + + return implRead(isScatteringRead, dst, dsts, timeout, unit, att, handler); + } + + @Override + public final Future read(ByteBuffer dst) { + if (dst.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + return read(false, dst, null, 0L, TimeUnit.MILLISECONDS, null, null); + } + + @Override + public final void read(ByteBuffer dst, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + if (dst.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + read(false, dst, null, timeout, unit, attachment, handler); + } + + @Override + public final void read(ByteBuffer[] dsts, + int offset, + int length, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); + ByteBuffer[] bufs = Util.subsequence(dsts, offset, length); + for (int i=0; i Future implWrite(boolean isGatheringWrite, + ByteBuffer src, + ByteBuffer[] srcs, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + @SuppressWarnings("unchecked") + private Future write(boolean isGatheringWrite, + ByteBuffer src, + ByteBuffer[] srcs, + long timeout, + TimeUnit unit, + A att, + CompletionHandler handler) + { + boolean hasDataToWrite = isGatheringWrite || src.hasRemaining(); + + boolean closed = false; + if (isOpen()) { + if (remoteAddress == null) + throw new NotYetConnectedException(); + // check and update state + synchronized (writeLock) { + if (writeKilled) + throw new IllegalStateException("Writing not allowed due to timeout or cancellation"); + if (writing) + throw new WritePendingException(); + if (writeShutdown) { + closed = true; + } else { + if (hasDataToWrite) + writing = true; + } + } + } else { + closed = true; + } + + // channel is closed or shutdown for write + if (closed) { + Throwable e = new ClosedChannelException(); + if (handler == null) + return CompletedFuture.withFailure(e); + Invoker.invoke(this, handler, att, null, e); + return null; + } + + // nothing to write so complete immediately + if (!hasDataToWrite) { + Number result = (isGatheringWrite) ? (Number)0L : (Number)0; + if (handler == null) + return CompletedFuture.withResult((V)result); + Invoker.invoke(this, handler, att, (V)result, null); + return null; + } + + return implWrite(isGatheringWrite, src, srcs, timeout, unit, att, handler); + } + + @Override + public final Future write(ByteBuffer src) { + return write(false, src, null, 0L, TimeUnit.MILLISECONDS, null, null); + } + + @Override + public final void write(ByteBuffer src, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + write(false, src, null, timeout, unit, attachment, handler); + } + + @Override + public final void write(ByteBuffer[] srcs, + int offset, + int length, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + if (handler == null) + throw new NullPointerException("'handler' is null"); + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); + srcs = Util.subsequence(srcs, offset, length); + write(true, null, srcs, timeout, unit, attachment, handler); + } + + @Override + public final AsynchronousSocketChannel bind(SocketAddress local) + throws IOException + { + try { + begin(); + synchronized (stateLock) { + if (state == ST_PENDING) + throw new ConnectionPendingException(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkListen(isa.getPort()); + } + NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + localAddress = Net.localAddress(fd); + } + } finally { + end(); + } + return this; + } + + @Override + public final SocketAddress getLocalAddress() throws IOException { + if (!isOpen()) + throw new ClosedChannelException(); + return Net.getRevealedLocalAddress(localAddress); + } + + @Override + public final AsynchronousSocketChannel setOption(SocketOption name, T value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + try { + begin(); + if (writeShutdown) + throw new IOException("Connection has been shutdown for writing"); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } + return this; + } finally { + end(); + } + } + + @Override + @SuppressWarnings("unchecked") + public final T getOption(SocketOption name) throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + try { + begin(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } finally { + end(); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(5); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_KEEPALIVE); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.TCP_NODELAY); + if (ExtendedOptionsImpl.flowSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); + } + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public final SocketAddress getRemoteAddress() throws IOException { + if (!isOpen()) + throw new ClosedChannelException(); + return remoteAddress; + } + + @Override + public final AsynchronousSocketChannel shutdownInput() throws IOException { + try { + begin(); + if (remoteAddress == null) + throw new NotYetConnectedException(); + synchronized (readLock) { + if (!readShutdown) { + Net.shutdown(fd, Net.SHUT_RD); + readShutdown = true; + } + } + } finally { + end(); + } + return this; + } + + @Override + public final AsynchronousSocketChannel shutdownOutput() throws IOException { + try { + begin(); + if (remoteAddress == null) + throw new NotYetConnectedException(); + synchronized (writeLock) { + if (!writeShutdown) { + Net.shutdown(fd, Net.SHUT_WR); + writeShutdown = true; + } + } + } finally { + end(); + } + return this; + } + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()); + sb.append('['); + synchronized (stateLock) { + if (!isOpen()) { + sb.append("closed"); + } else { + switch (state) { + case ST_UNCONNECTED: + sb.append("unconnected"); + break; + case ST_PENDING: + sb.append("connection-pending"); + break; + case ST_CONNECTED: + sb.append("connected"); + if (readShutdown) + sb.append(" ishut"); + if (writeShutdown) + sb.append(" oshut"); + break; + } + if (localAddress != null) { + sb.append(" local="); + sb.append( + Net.getRevealedLocalAddressAsString(localAddress)); + } + if (remoteAddress != null) { + sb.append(" remote="); + sb.append(remoteAddress.toString()); + } + } + } + sb.append(']'); + return sb.toString(); + } +} diff --git a/src/sun/nio/ch/Cancellable.java b/src/sun/nio/ch/Cancellable.java new file mode 100644 index 00000000..9a84a63a --- /dev/null +++ b/src/sun/nio/ch/Cancellable.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +/** + * Implemented by asynchronous channels that require notification when an I/O + * operation is cancelled. + */ + +interface Cancellable { + /** + * Invoked to notify channel that cancel has been invoked while holding + * the Future's lock. + */ + void onCancel(PendingFuture task); +} diff --git a/src/sun/nio/ch/ChannelInputStream.java b/src/sun/nio/ch/ChannelInputStream.java new file mode 100644 index 00000000..95b750e7 --- /dev/null +++ b/src/sun/nio/ch/ChannelInputStream.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.channels.SelectableChannel; + + +/** + * This class is defined here rather than in java.nio.channels.Channels + * so that code can be shared with SocketAdaptor. + * + * @author Mike McCloskey + * @author Mark Reinhold + * @since 1.4 + */ + +public class ChannelInputStream + extends InputStream +{ + + public static int read(ReadableByteChannel ch, ByteBuffer bb, + boolean block) + throws IOException + { + if (ch instanceof SelectableChannel) { + SelectableChannel sc = (SelectableChannel)ch; + synchronized (sc.blockingLock()) { + boolean bm = sc.isBlocking(); + if (!bm) + throw new IllegalBlockingModeException(); + if (bm != block) + sc.configureBlocking(block); + int n = ch.read(bb); + if (bm != block) + sc.configureBlocking(bm); + return n; + } + } else { + return ch.read(bb); + } + } + + protected final ReadableByteChannel ch; + private ByteBuffer bb = null; + private byte[] bs = null; // Invoker's previous array + private byte[] b1 = null; + + public ChannelInputStream(ReadableByteChannel ch) { + this.ch = ch; + } + + public synchronized int read() throws IOException { + if (b1 == null) + b1 = new byte[1]; + int n = this.read(b1); + if (n == 1) + return b1[0] & 0xff; + return -1; + } + + public synchronized int read(byte[] bs, int off, int len) + throws IOException + { + if ((off < 0) || (off > bs.length) || (len < 0) || + ((off + len) > bs.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) + return 0; + + ByteBuffer bb = ((this.bs == bs) + ? this.bb + : ByteBuffer.wrap(bs)); + bb.limit(Math.min(off + len, bb.capacity())); + bb.position(off); + this.bb = bb; + this.bs = bs; + return read(bb); + } + + protected int read(ByteBuffer bb) + throws IOException + { + return ChannelInputStream.read(ch, bb, true); + } + + public int available() throws IOException { + // special case where the channel is to a file + if (ch instanceof SeekableByteChannel) { + SeekableByteChannel sbc = (SeekableByteChannel)ch; + long rem = Math.max(0, sbc.size() - sbc.position()); + return (rem > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)rem; + } + return 0; + } + + public void close() throws IOException { + ch.close(); + } + +} diff --git a/src/sun/nio/ch/CompletedFuture.java b/src/sun/nio/ch/CompletedFuture.java new file mode 100644 index 00000000..75edf8ce --- /dev/null +++ b/src/sun/nio/ch/CompletedFuture.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * A Future representing the result of an I/O operation that has already + * completed. + */ + +final class CompletedFuture implements Future { + private final V result; + private final Throwable exc; + + private CompletedFuture(V result, Throwable exc) { + this.result = result; + this.exc = exc; + } + + static CompletedFuture withResult(V result) { + return new CompletedFuture(result, null); + } + + static CompletedFuture withFailure(Throwable exc) { + // exception must be IOException or SecurityException + if (!(exc instanceof IOException) && !(exc instanceof SecurityException)) + exc = new IOException(exc); + return new CompletedFuture(null, exc); + } + + static CompletedFuture withResult(V result, Throwable exc) { + if (exc == null) { + return withResult(result); + } else { + return withFailure(exc); + } + } + + @Override + public V get() throws ExecutionException { + if (exc != null) + throw new ExecutionException(exc); + return result; + } + + @Override + public V get(long timeout, TimeUnit unit) throws ExecutionException { + if (unit == null) + throw new NullPointerException(); + if (exc != null) + throw new ExecutionException(exc); + return result; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } +} diff --git a/src/sun/nio/ch/DatagramChannelImpl.java b/src/sun/nio/ch/DatagramChannelImpl.java new file mode 100644 index 00000000..0c414344 --- /dev/null +++ b/src/sun/nio/ch/DatagramChannelImpl.java @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.PortUnreachableException; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.MembershipKey; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectionKey; +import java.nio.channels.UnsupportedAddressTypeException; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import sun.net.ExtendedOptionsImpl; +import sun.net.ResourceManager; + +/** + * An implementation of DatagramChannels. + */ + +class DatagramChannelImpl + extends DatagramChannel + implements SelChImpl +{ + + // Used to make native read and write calls + private static NativeDispatcher nd = new DatagramDispatcher(); + + // Our file descriptor + private final FileDescriptor fd; + + // fd value needed for dev/poll. This value will remain valid + // even after the value in the file descriptor object has been set to -1 + private final int fdVal; + + // The protocol family of the socket + private final ProtocolFamily family; + + // IDs of native threads doing reads and writes, for signalling + private volatile long readerThread = 0; + private volatile long writerThread = 0; + + // Cached InetAddress and port for unconnected DatagramChannels + // used by receive0 + private InetAddress cachedSenderInetAddress; + private int cachedSenderPort; + + // Lock held by current reading or connecting thread + private final Object readLock = new Object(); + + // Lock held by current writing or connecting thread + private final Object writeLock = new Object(); + + // Lock held by any thread that modifies the state fields declared below + // DO NOT invoke a blocking I/O operation while holding this lock! + private final Object stateLock = new Object(); + + // -- The following fields are protected by stateLock + + // State (does not necessarily increase monotonically) + private static final int ST_UNINITIALIZED = -1; + private static final int ST_UNCONNECTED = 0; + private static final int ST_CONNECTED = 1; + private static final int ST_KILLED = 2; + private int state = ST_UNINITIALIZED; + + // Binding + private InetSocketAddress localAddress; + private InetSocketAddress remoteAddress; + + // Our socket adaptor, if any + private DatagramSocket socket; + + // Multicast support + private MembershipRegistry registry; + + // set true when socket is bound and SO_REUSEADDRESS is emulated + private boolean reuseAddressEmulated; + + // set true/false when socket is already bound and SO_REUSEADDR is emulated + private boolean isReuseAddress; + + // -- End of fields protected by stateLock + + + public DatagramChannelImpl(SelectorProvider sp) + throws IOException + { + super(sp); + ResourceManager.beforeUdpCreate(); + try { + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + this.fd = Net.socket(family, false); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + } catch (IOException ioe) { + ResourceManager.afterUdpClose(); + throw ioe; + } + } + + public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) + throws IOException + { + super(sp); + if ((family != StandardProtocolFamily.INET) && + (family != StandardProtocolFamily.INET6)) + { + if (family == null) + throw new NullPointerException("'family' is null"); + else + throw new UnsupportedOperationException("Protocol family not supported"); + } + if (family == StandardProtocolFamily.INET6) { + if (!Net.isIPv6Available()) { + throw new UnsupportedOperationException("IPv6 not available"); + } + } + this.family = family; + this.fd = Net.socket(family, false); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + } + + public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) + throws IOException + { + super(sp); + this.family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + this.localAddress = Net.localAddress(fd); + } + + public DatagramSocket socket() { + synchronized (stateLock) { + if (socket == null) + socket = DatagramSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + // Perform security check before returning address + return Net.getRevealedLocalAddress(localAddress); + } + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + return remoteAddress; + } + } + + @Override + public DatagramChannel setOption(SocketOption name, T value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.IP_TOS || + name == StandardSocketOptions.IP_MULTICAST_TTL || + name == StandardSocketOptions.IP_MULTICAST_LOOP) + { + // options are protocol dependent + Net.setSocketOption(fd, family, name, value); + return this; + } + + if (name == StandardSocketOptions.IP_MULTICAST_IF) { + if (value == null) + throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); + NetworkInterface interf = (NetworkInterface)value; + if (family == StandardProtocolFamily.INET6) { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + Net.setInterface6(fd, index); + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + int targetAddress = Net.inet4AsInt(target); + Net.setInterface4(fd, targetAddress); + } + return this; + } + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind() && localAddress != null) + { + reuseAddressEmulated = true; + this.isReuseAddress = (Boolean)value; + } + + // remaining options don't need any special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + ensureOpen(); + + if (name == StandardSocketOptions.IP_TOS || + name == StandardSocketOptions.IP_MULTICAST_TTL || + name == StandardSocketOptions.IP_MULTICAST_LOOP) + { + return (T) Net.getSocketOption(fd, family, name); + } + + if (name == StandardSocketOptions.IP_MULTICAST_IF) { + if (family == StandardProtocolFamily.INET) { + int address = Net.getInterface4(fd); + if (address == 0) + return null; // default interface + + InetAddress ia = Net.inet4FromInt(address); + NetworkInterface ni = NetworkInterface.getByInetAddress(ia); + if (ni == null) + throw new IOException("Unable to map address to interface"); + return (T) ni; + } else { + int index = Net.getInterface6(fd); + if (index == 0) + return null; // default interface + + NetworkInterface ni = NetworkInterface.getByIndex(index); + if (ni == null) + throw new IOException("Unable to map index to interface"); + return (T) ni; + } + } + + if (name == StandardSocketOptions.SO_REUSEADDR && + reuseAddressEmulated) + { + return (T)Boolean.valueOf(isReuseAddress); + } + + // no special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_BROADCAST); + set.add(StandardSocketOptions.IP_TOS); + set.add(StandardSocketOptions.IP_MULTICAST_IF); + set.add(StandardSocketOptions.IP_MULTICAST_TTL); + set.add(StandardSocketOptions.IP_MULTICAST_LOOP); + if (ExtendedOptionsImpl.flowSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); + } + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + private void ensureOpen() throws ClosedChannelException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + private SocketAddress sender; // Set by receive0 (## ugh) + + public SocketAddress receive(ByteBuffer dst) throws IOException { + if (dst.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + if (dst == null) + throw new NullPointerException(); + synchronized (readLock) { + ensureOpen(); + // Socket was not bound before attempting receive + if (localAddress() == null) + bind(null); + int n = 0; + ByteBuffer bb = null; + try { + begin(); + if (!isOpen()) + return null; + SecurityManager security = System.getSecurityManager(); + readerThread = NativeThread.current(); + if (isConnected() || (security == null)) { + do { + n = receive(fd, dst); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + if (n == IOStatus.UNAVAILABLE) + return null; + } else { + bb = Util.getTemporaryDirectBuffer(dst.remaining()); + for (;;) { + do { + n = receive(fd, bb); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + if (n == IOStatus.UNAVAILABLE) + return null; + InetSocketAddress isa = (InetSocketAddress)sender; + try { + security.checkAccept( + isa.getAddress().getHostAddress(), + isa.getPort()); + } catch (SecurityException se) { + // Ignore packet + bb.clear(); + n = 0; + continue; + } + bb.flip(); + dst.put(bb); + break; + } + } + return sender; + } finally { + if (bb != null) + Util.releaseTemporaryDirectBuffer(bb); + readerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + private int receive(FileDescriptor fd, ByteBuffer dst) + throws IOException + { + int pos = dst.position(); + int lim = dst.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + if (dst instanceof DirectBuffer && rem > 0) + return receiveIntoNativeBuffer(fd, dst, rem, pos); + + // Substitute a native buffer. If the supplied buffer is empty + // we must instead use a nonempty buffer, otherwise the call + // will not block waiting for a datagram on some platforms. + int newSize = Math.max(rem, 1); + ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize); + try { + int n = receiveIntoNativeBuffer(fd, bb, newSize, 0); + bb.flip(); + if (n > 0 && rem > 0) + dst.put(bb); + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb, + int rem, int pos) + throws IOException + { + int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem, + isConnected()); + if (n > 0) + bb.position(pos + n); + return n; + } + + public int send(ByteBuffer src, SocketAddress target) + throws IOException + { + if (src == null) + throw new NullPointerException(); + + synchronized (writeLock) { + ensureOpen(); + InetSocketAddress isa = Net.checkAddress(target); + InetAddress ia = isa.getAddress(); + if (ia == null) + throw new IOException("Target address not resolved"); + synchronized (stateLock) { + if (!isConnected()) { + if (target == null) + throw new NullPointerException(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (ia.isMulticastAddress()) { + sm.checkMulticast(ia); + } else { + sm.checkConnect(ia.getHostAddress(), + isa.getPort()); + } + } + } else { // Connected case; Check address then write + if (!target.equals(remoteAddress)) { + throw new IllegalArgumentException( + "Connected address not equal to target address"); + } + return write(src); + } + } + + int n = 0; + try { + begin(); + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + do { + n = send(fd, src, isa); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + synchronized (stateLock) { + if (isOpen() && (localAddress == null)) { + localAddress = Net.localAddress(fd); + } + } + return IOStatus.normalize(n); + } finally { + writerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target) + throws IOException + { + if (src instanceof DirectBuffer) + return sendFromNativeBuffer(fd, src, target); + + // Substitute a native buffer + int pos = src.position(); + int lim = src.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + ByteBuffer bb = Util.getTemporaryDirectBuffer(rem); + try { + bb.put(src); + bb.flip(); + // Do not update src until we see how many bytes were written + src.position(pos); + + int n = sendFromNativeBuffer(fd, bb, target); + if (n > 0) { + // now update src + src.position(pos + n); + } + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int sendFromNativeBuffer(FileDescriptor fd, ByteBuffer bb, + InetSocketAddress target) + throws IOException + { + int pos = bb.position(); + int lim = bb.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + boolean preferIPv6 = (family != StandardProtocolFamily.INET); + int written; + try { + written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos, + rem, target.getAddress(), target.getPort()); + } catch (PortUnreachableException pue) { + if (isConnected()) + throw pue; + written = rem; + } + if (written > 0) + bb.position(pos + written); + return written; + } + + public int read(ByteBuffer buf) throws IOException { + if (buf == null) + throw new NullPointerException(); + synchronized (readLock) { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + int n = 0; + try { + begin(); + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + do { + n = IOUtil.read(fd, buf, -1, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + readerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); + synchronized (readLock) { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + long n = 0; + try { + begin(); + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + do { + n = IOUtil.read(fd, dsts, offset, length, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + readerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + public int write(ByteBuffer buf) throws IOException { + if (buf == null) + throw new NullPointerException(); + synchronized (writeLock) { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + int n = 0; + try { + begin(); + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + do { + n = IOUtil.write(fd, buf, -1, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + writerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); + synchronized (writeLock) { + synchronized (stateLock) { + ensureOpen(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + long n = 0; + try { + begin(); + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + do { + n = IOUtil.write(fd, srcs, offset, length, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + writerThread = 0; + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + public SocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + public SocketAddress remoteAddress() { + synchronized (stateLock) { + return remoteAddress; + } + } + + @Override + public DatagramChannel bind(SocketAddress local) throws IOException { + synchronized (readLock) { + synchronized (writeLock) { + synchronized (stateLock) { + ensureOpen(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa; + if (local == null) { + // only Inet4Address allowed with IPv4 socket + if (family == StandardProtocolFamily.INET) { + isa = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0); + } else { + isa = new InetSocketAddress(0); + } + } else { + isa = Net.checkAddress(local); + + // only Inet4Address allowed with IPv4 socket + if (family == StandardProtocolFamily.INET) { + InetAddress addr = isa.getAddress(); + if (!(addr instanceof Inet4Address)) + throw new UnsupportedAddressTypeException(); + } + } + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkListen(isa.getPort()); + } + Net.bind(family, fd, isa.getAddress(), isa.getPort()); + localAddress = Net.localAddress(fd); + } + } + } + return this; + } + + public boolean isConnected() { + synchronized (stateLock) { + return (state == ST_CONNECTED); + } + } + + void ensureOpenAndUnconnected() throws IOException { // package-private + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (state != ST_UNCONNECTED) + throw new IllegalStateException("Connect already invoked"); + } + } + + @Override + public DatagramChannel connect(SocketAddress sa) throws IOException { + int localPort = 0; + + synchronized(readLock) { + synchronized(writeLock) { + synchronized (stateLock) { + ensureOpenAndUnconnected(); + InetSocketAddress isa = Net.checkAddress(sa); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), + isa.getPort()); + int n = Net.connect(family, + fd, + isa.getAddress(), + isa.getPort()); + if (n <= 0) + throw new Error(); // Can't happen + + // Connection succeeded; disallow further invocation + state = ST_CONNECTED; + remoteAddress = isa; + sender = isa; + cachedSenderInetAddress = isa.getAddress(); + cachedSenderPort = isa.getPort(); + + // set or refresh local address + localAddress = Net.localAddress(fd); + + // flush any packets already received. + boolean blocking = false; + synchronized (blockingLock()) { + try { + blocking = isBlocking(); + // remainder of each packet thrown away + ByteBuffer tmpBuf = ByteBuffer.allocate(1); + if (blocking) { + configureBlocking(false); + } + do { + tmpBuf.clear(); + } while (receive(tmpBuf) != null); + } finally { + if (blocking) { + configureBlocking(true); + } + } + } + } + } + } + return this; + } + + public DatagramChannel disconnect() throws IOException { + synchronized(readLock) { + synchronized(writeLock) { + synchronized (stateLock) { + if (!isConnected() || !isOpen()) + return this; + InetSocketAddress isa = remoteAddress; + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), + isa.getPort()); + boolean isIPv6 = (family == StandardProtocolFamily.INET6); + disconnect0(fd, isIPv6); + remoteAddress = null; + state = ST_UNCONNECTED; + + // refresh local address + localAddress = Net.localAddress(fd); + } + } + } + return this; + } + + /** + * Joins channel's socket to the given group/interface and + * optional source address. + */ + private MembershipKey innerJoin(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (!group.isMulticastAddress()) + throw new IllegalArgumentException("Group not a multicast address"); + + // check multicast address is compatible with this socket + if (group instanceof Inet4Address) { + if (family == StandardProtocolFamily.INET6 && !Net.canIPv6SocketJoinIPv4Group()) + throw new IllegalArgumentException("IPv6 socket cannot join IPv4 multicast group"); + } else if (group instanceof Inet6Address) { + if (family != StandardProtocolFamily.INET6) + throw new IllegalArgumentException("Only IPv6 sockets can join IPv6 multicast group"); + } else { + throw new IllegalArgumentException("Address type not supported"); + } + + // check source address + if (source != null) { + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != group.getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkMulticast(group); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + // check the registry to see if we are already a member of the group + if (registry == null) { + registry = new MembershipRegistry(); + } else { + // return existing membership key + MembershipKey key = registry.checkMembership(group, interf, source); + if (key != null) + return key; + } + + MembershipKeyImpl key; + if ((family == StandardProtocolFamily.INET6) && + ((group instanceof Inet6Address) || Net.canJoin6WithIPv4Group())) + { + int index = interf.getIndex(); + if (index == -1) + throw new IOException("Network interface cannot be identified"); + + // need multicast and source address as byte arrays + byte[] groupAddress = Net.inet6AsByteArray(group); + byte[] sourceAddress = (source == null) ? null : + Net.inet6AsByteArray(source); + + // join the group + int n = Net.join6(fd, groupAddress, index, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type6(this, group, interf, source, + groupAddress, index, sourceAddress); + + } else { + // need IPv4 address to identify interface + Inet4Address target = Net.anyInet4Address(interf); + if (target == null) + throw new IOException("Network interface not configured for IPv4"); + + int groupAddress = Net.inet4AsInt(group); + int targetAddress = Net.inet4AsInt(target); + int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source); + + // join the group + int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress); + if (n == IOStatus.UNAVAILABLE) + throw new UnsupportedOperationException(); + + key = new MembershipKeyImpl.Type4(this, group, interf, source, + groupAddress, targetAddress, sourceAddress); + } + + registry.add(key); + return key; + } + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf) + throws IOException + { + return innerJoin(group, interf, null); + } + + @Override + public MembershipKey join(InetAddress group, + NetworkInterface interf, + InetAddress source) + throws IOException + { + if (source == null) + throw new NullPointerException("source address is null"); + return innerJoin(group, interf, source); + } + + // package-private + void drop(MembershipKeyImpl key) { + assert key.channel() == this; + + synchronized (stateLock) { + if (!key.isValid()) + return; + + try { + if (key instanceof MembershipKeyImpl.Type6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.drop6(fd, key6.groupAddress(), key6.index(), key6.source()); + } else { + MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4)key; + Net.drop4(fd, key4.groupAddress(), key4.interfaceAddress(), + key4.source()); + } + } catch (IOException ioe) { + // should not happen + throw new AssertionError(ioe); + } + + key.invalidate(); + registry.remove(key); + } + } + + /** + * Block datagrams from given source if a memory to receive all + * datagrams. + */ + void block(MembershipKeyImpl key, InetAddress source) + throws IOException + { + assert key.channel() == this; + assert key.sourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + if (source.isAnyLocalAddress()) + throw new IllegalArgumentException("Source address is a wildcard address"); + if (source.isMulticastAddress()) + throw new IllegalArgumentException("Source address is multicast address"); + if (source.getClass() != key.group().getClass()) + throw new IllegalArgumentException("Source address is different type to group"); + + int n; + if (key instanceof MembershipKeyImpl.Type6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + n = Net.block6(fd, key6.groupAddress(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + n = Net.block4(fd, key4.groupAddress(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + if (n == IOStatus.UNAVAILABLE) { + // ancient kernel + throw new UnsupportedOperationException(); + } + } + } + + /** + * Unblock given source. + */ + void unblock(MembershipKeyImpl key, InetAddress source) { + assert key.channel() == this; + assert key.sourceAddress() == null; + + synchronized (stateLock) { + if (!key.isValid()) + throw new IllegalStateException("key is no longer valid"); + + try { + if (key instanceof MembershipKeyImpl.Type6) { + MembershipKeyImpl.Type6 key6 = + (MembershipKeyImpl.Type6)key; + Net.unblock6(fd, key6.groupAddress(), key6.index(), + Net.inet6AsByteArray(source)); + } else { + MembershipKeyImpl.Type4 key4 = + (MembershipKeyImpl.Type4)key; + Net.unblock4(fd, key4.groupAddress(), key4.interfaceAddress(), + Net.inet4AsInt(source)); + } + } catch (IOException ioe) { + // should not happen + throw new AssertionError(ioe); + } + } + } + + protected void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + if (state != ST_KILLED) + nd.preClose(fd); + ResourceManager.afterUdpClose(); + + // if member of mulitcast group then invalidate all keys + if (registry != null) + registry.invalidateAll(); + + long th; + if ((th = readerThread) != 0) + NativeThread.signal(th); + if ((th = writerThread) != 0) + NativeThread.signal(th); + if (!isRegistered()) + kill(); + } + } + + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLED) + return; + if (state == ST_UNINITIALIZED) { + state = ST_KILLED; + return; + } + assert !isOpen() && !isRegistered(); + nd.close(fd); + state = ST_KILLED; + } + } + + protected void finalize() throws IOException { + // fd is null if constructor threw exception + if (fd != null) + close(); + } + + /** + * Translates native poll revent set into a ready operation set + */ + public boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) { + // This should only happen if this channel is pre-closed while a + // selection operation is in progress + // ## Throw an error if this channel has not been pre-closed + return false; + } + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0)) + newOps |= SelectionKey.OP_READ; + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0)) + newOps |= SelectionKey.OP_WRITE; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (readLock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + readerThread = 0; + end(n > 0); + } + return n; + } + } + + /** + * Translates an interest operation set into a native poll event set + */ + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= Net.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= Net.POLLOUT; + if ((ops & SelectionKey.OP_CONNECT) != 0) + newOps |= Net.POLLIN; + sk.selector.putEventOps(sk, newOps); + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + + // -- Native methods -- + + private static native void initIDs(); + + private static native void disconnect0(FileDescriptor fd, boolean isIPv6) + throws IOException; + + private native int receive0(FileDescriptor fd, long address, int len, + boolean connected) + throws IOException; + + private native int send0(boolean preferIPv6, FileDescriptor fd, long address, + int len, InetAddress addr, int port) + throws IOException; + + static { + IOUtil.load(); + initIDs(); + } + +} diff --git a/src/sun/nio/ch/DatagramSocketAdaptor.java b/src/sun/nio/ch/DatagramSocketAdaptor.java new file mode 100644 index 00000000..8b4bf251 --- /dev/null +++ b/src/sun/nio/ch/DatagramSocketAdaptor.java @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.DatagramSocketImpl; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOption; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.IllegalBlockingModeException; + + +// Make a datagram-socket channel look like a datagram socket. +// +// The methods in this class are defined in exactly the same order as in +// java.net.DatagramSocket so as to simplify tracking future changes to that +// class. +// + +public class DatagramSocketAdaptor + extends DatagramSocket +{ + + // The channel being adapted + private final DatagramChannelImpl dc; + + // Timeout "option" value for receives + private volatile int timeout = 0; + + // ## super will create a useless impl + private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException { + // Invoke the DatagramSocketAdaptor(SocketAddress) constructor, + // passing a dummy DatagramSocketImpl object to aovid any native + // resource allocation in super class and invoking our bind method + // before the dc field is initialized. + super(dummyDatagramSocket); + this.dc = dc; + } + + public static DatagramSocket create(DatagramChannelImpl dc) { + try { + return new DatagramSocketAdaptor(dc); + } catch (IOException x) { + throw new Error(x); + } + } + + private void connectInternal(SocketAddress remote) + throws SocketException + { + InetSocketAddress isa = Net.asInetSocketAddress(remote); + int port = isa.getPort(); + if (port < 0 || port > 0xFFFF) + throw new IllegalArgumentException("connect: " + port); + if (remote == null) + throw new IllegalArgumentException("connect: null address"); + if (isClosed()) + return; + try { + dc.connect(remote); + } catch (Exception x) { + Net.translateToSocketException(x); + } + } + + public void bind(SocketAddress local) throws SocketException { + try { + if (local == null) + local = new InetSocketAddress(0); + dc.bind(local); + } catch (Exception x) { + Net.translateToSocketException(x); + } + } + + public void connect(InetAddress address, int port) { + try { + connectInternal(new InetSocketAddress(address, port)); + } catch (SocketException x) { + // Yes, j.n.DatagramSocket really does this + } + } + + public void connect(SocketAddress remote) throws SocketException { + if (remote == null) + throw new IllegalArgumentException("Address can't be null"); + connectInternal(remote); + } + + public void disconnect() { + try { + dc.disconnect(); + } catch (IOException x) { + throw new Error(x); + } + } + + public boolean isBound() { + return dc.localAddress() != null; + } + + public boolean isConnected() { + return dc.remoteAddress() != null; + } + + public InetAddress getInetAddress() { + return (isConnected() + ? Net.asInetSocketAddress(dc.remoteAddress()).getAddress() + : null); + } + + public int getPort() { + return (isConnected() + ? Net.asInetSocketAddress(dc.remoteAddress()).getPort() + : -1); + } + + public void send(DatagramPacket p) throws IOException { + synchronized (dc.blockingLock()) { + if (!dc.isBlocking()) + throw new IllegalBlockingModeException(); + try { + synchronized (p) { + ByteBuffer bb = ByteBuffer.wrap(p.getData(), + p.getOffset(), + p.getLength()); + if (dc.isConnected()) { + if (p.getAddress() == null) { + // Legacy DatagramSocket will send in this case + // and set address and port of the packet + InetSocketAddress isa = (InetSocketAddress) + dc.remoteAddress(); + p.setPort(isa.getPort()); + p.setAddress(isa.getAddress()); + dc.write(bb); + } else { + // Target address may not match connected address + dc.send(bb, p.getSocketAddress()); + } + } else { + // Not connected so address must be valid or throw + dc.send(bb, p.getSocketAddress()); + } + } + } catch (IOException x) { + Net.translateException(x); + } + } + } + + // Must hold dc.blockingLock() + // + private SocketAddress receive(ByteBuffer bb) throws IOException { + if (timeout == 0) { + return dc.receive(bb); + } + + dc.configureBlocking(false); + try { + int n; + SocketAddress sender; + if ((sender = dc.receive(bb)) != null) + return sender; + long to = timeout; + for (;;) { + if (!dc.isOpen()) + throw new ClosedChannelException(); + long st = System.currentTimeMillis(); + int result = dc.poll(Net.POLLIN, to); + if (result > 0 && + ((result & Net.POLLIN) != 0)) { + if ((sender = dc.receive(bb)) != null) + return sender; + } + to -= System.currentTimeMillis() - st; + if (to <= 0) + throw new SocketTimeoutException(); + + } + } finally { + if (dc.isOpen()) + dc.configureBlocking(true); + } + } + + public void receive(DatagramPacket p) throws IOException { + synchronized (dc.blockingLock()) { + if (!dc.isBlocking()) + throw new IllegalBlockingModeException(); + try { + synchronized (p) { + ByteBuffer bb = ByteBuffer.wrap(p.getData(), + p.getOffset(), + p.getLength()); + SocketAddress sender = receive(bb); + p.setSocketAddress(sender); + p.setLength(bb.position() - p.getOffset()); + } + } catch (IOException x) { + Net.translateException(x); + } + } + } + + public InetAddress getLocalAddress() { + if (isClosed()) + return null; + SocketAddress local = dc.localAddress(); + if (local == null) + local = new InetSocketAddress(0); + InetAddress result = ((InetSocketAddress)local).getAddress(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkConnect(result.getHostAddress(), -1); + } catch (SecurityException x) { + return new InetSocketAddress(0).getAddress(); + } + } + return result; + } + + public int getLocalPort() { + if (isClosed()) + return -1; + try { + SocketAddress local = dc.getLocalAddress(); + if (local != null) { + return ((InetSocketAddress)local).getPort(); + } + } catch (Exception x) { + } + return 0; + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + dc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return dc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setSendBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + + } + + public void setBroadcast(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_BROADCAST, on); + } + + public boolean getBroadcast() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_BROADCAST); + } + + public void setTrafficClass(int tc) throws SocketException { + setIntOption(StandardSocketOptions.IP_TOS, tc); + } + + public int getTrafficClass() throws SocketException { + return getIntOption(StandardSocketOptions.IP_TOS); + } + + public void close() { + try { + dc.close(); + } catch (IOException x) { + throw new Error(x); + } + } + + public boolean isClosed() { + return !dc.isOpen(); + } + + public DatagramChannel getChannel() { + return dc; + } + + /* + * A dummy implementation of DatagramSocketImpl that can be passed to the + * DatagramSocket constructor so that no native resources are allocated in + * super class. + */ + private static final DatagramSocketImpl dummyDatagramSocket + = new DatagramSocketImpl() + { + protected void create() throws SocketException {} + + protected void bind(int lport, InetAddress laddr) throws SocketException {} + + protected void send(DatagramPacket p) throws IOException {} + + protected int peek(InetAddress i) throws IOException { return 0; } + + protected int peekData(DatagramPacket p) throws IOException { return 0; } + + protected void receive(DatagramPacket p) throws IOException {} + + @Deprecated + protected void setTTL(byte ttl) throws IOException {} + + @Deprecated + protected byte getTTL() throws IOException { return 0; } + + protected void setTimeToLive(int ttl) throws IOException {} + + protected int getTimeToLive() throws IOException { return 0;} + + protected void join(InetAddress inetaddr) throws IOException {} + + protected void leave(InetAddress inetaddr) throws IOException {} + + protected void joinGroup(SocketAddress mcastaddr, + NetworkInterface netIf) throws IOException {} + + protected void leaveGroup(SocketAddress mcastaddr, + NetworkInterface netIf) throws IOException {} + + protected void close() {} + + public Object getOption(int optID) throws SocketException { return null;} + + public void setOption(int optID, Object value) throws SocketException {} + }; +} diff --git a/src/sun/nio/ch/DirectBuffer.java b/src/sun/nio/ch/DirectBuffer.java new file mode 100644 index 00000000..577ed7b1 --- /dev/null +++ b/src/sun/nio/ch/DirectBuffer.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import sun.misc.Cleaner; + + +public interface DirectBuffer { + + public long address(); + + public Object attachment(); + + public Cleaner cleaner(); + +} diff --git a/src/sun/nio/ch/ExtendedSocketOption.java b/src/sun/nio/ch/ExtendedSocketOption.java new file mode 100644 index 00000000..630b7dcd --- /dev/null +++ b/src/sun/nio/ch/ExtendedSocketOption.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.net.SocketOption; + +/** + * Defines socket options that are supported by the implementation + * but not defined in StandardSocketOptions. + */ + +class ExtendedSocketOption { + private ExtendedSocketOption() { } + + static final SocketOption SO_OOBINLINE = + new SocketOption() { + public String name() { return "SO_OOBINLINE"; } + public Class type() { return Boolean.class; } + public String toString() { return name(); } + }; +} diff --git a/src/sun/nio/ch/FileChannelImpl.java b/src/sun/nio/ch/FileChannelImpl.java new file mode 100644 index 00000000..bb127e8a --- /dev/null +++ b/src/sun/nio/ch/FileChannelImpl.java @@ -0,0 +1,1194 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.FileLockInterruptionException; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.OverlappingFileLockException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.List; + +import sun.misc.Cleaner; +import sun.security.action.GetPropertyAction; + +public class FileChannelImpl + extends FileChannel +{ + // Memory allocation size for mapping buffers + private static final long allocationGranularity; + + // Used to make native read and write calls + private final FileDispatcher nd; + + // File descriptor + private final FileDescriptor fd; + + // File access mode (immutable) + private final boolean writable; + private final boolean readable; + private final boolean append; + + // Required to prevent finalization of creating stream (immutable) + private final Object parent; + + // The path of the referenced file + // (null if the parent stream is created with a file descriptor) + private final String path; + + // Thread-safe set of IDs of native threads, for signalling + private final NativeThreadSet threads = new NativeThreadSet(2); + + // Lock for operations involving position and size + private final Object positionLock = new Object(); + + private FileChannelImpl(FileDescriptor fd, String path, boolean readable, + boolean writable, boolean append, Object parent) + { + this.fd = fd; + this.readable = readable; + this.writable = writable; + this.append = append; + this.parent = parent; + this.path = path; + this.nd = new FileDispatcherImpl(append); + } + + // Used by FileInputStream.getChannel() and RandomAccessFile.getChannel() + public static FileChannel open(FileDescriptor fd, String path, + boolean readable, boolean writable, + Object parent) + { + return new FileChannelImpl(fd, path, readable, writable, false, parent); + } + + // Used by FileOutputStream.getChannel + public static FileChannel open(FileDescriptor fd, String path, + boolean readable, boolean writable, + boolean append, Object parent) + { + return new FileChannelImpl(fd, path, readable, writable, append, parent); + } + + private void ensureOpen() throws IOException { + if (!isOpen()) + throw new ClosedChannelException(); + } + + + // -- Standard channel operations -- + + protected void implCloseChannel() throws IOException { + // Release and invalidate any locks that we still hold + if (fileLockTable != null) { + for (FileLock fl: fileLockTable.removeAll()) { + synchronized (fl) { + if (fl.isValid()) { + nd.release(fd, fl.position(), fl.size()); + ((FileLockImpl)fl).invalidate(); + } + } + } + } + + // signal any threads blocked on this channel + threads.signalAndWait(); + + if (parent != null) { + + // Close the fd via the parent stream's close method. The parent + // will reinvoke our close method, which is defined in the + // superclass AbstractInterruptibleChannel, but the isOpen logic in + // that method will prevent this method from being reinvoked. + // + ((java.io.Closeable)parent).close(); + } else { + nd.close(fd); + } + + } + + public int read(ByteBuffer dst) throws IOException { + ensureOpen(); + if (!readable) + throw new NonReadableChannelException(); + synchronized (positionLock) { + int n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return 0; + do { + n = IOUtil.read(fd, dst, -1, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + } + + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); + ensureOpen(); + if (!readable) + throw new NonReadableChannelException(); + synchronized (positionLock) { + long n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return 0; + do { + n = IOUtil.read(fd, dsts, offset, length, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + } + + public int write(ByteBuffer src) throws IOException { + ensureOpen(); + if (!writable) + throw new NonWritableChannelException(); + synchronized (positionLock) { + int n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return 0; + do { + n = IOUtil.write(fd, src, -1, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + } + + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); + ensureOpen(); + if (!writable) + throw new NonWritableChannelException(); + synchronized (positionLock) { + long n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return 0; + do { + n = IOUtil.write(fd, srcs, offset, length, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + } + + // -- Other operations -- + + public long position() throws IOException { + ensureOpen(); + synchronized (positionLock) { + long p = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return 0; + do { + // in append-mode then position is advanced to end before writing + p = (append) ? nd.size(fd) : position0(fd, -1); + } while ((p == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(p); + } finally { + threads.remove(ti); + end(p > -1); + assert IOStatus.check(p); + } + } + } + + public FileChannel position(long newPosition) throws IOException { + ensureOpen(); + if (newPosition < 0) + throw new IllegalArgumentException(); + synchronized (positionLock) { + long p = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return null; + do { + p = position0(fd, newPosition); + } while ((p == IOStatus.INTERRUPTED) && isOpen()); + return this; + } finally { + threads.remove(ti); + end(p > -1); + assert IOStatus.check(p); + } + } + } + + public long size() throws IOException { + ensureOpen(); + synchronized (positionLock) { + long s = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return -1; + do { + s = nd.size(fd); + } while ((s == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(s); + } finally { + threads.remove(ti); + end(s > -1); + assert IOStatus.check(s); + } + } + } + + public FileChannel truncate(long newSize) throws IOException { + ensureOpen(); + if (newSize < 0) + throw new IllegalArgumentException("Negative size"); + if (!writable) + throw new NonWritableChannelException(); + synchronized (positionLock) { + int rv = -1; + long p = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return null; + + // get current size + long size; + do { + size = nd.size(fd); + } while ((size == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + + // get current position + do { + p = position0(fd, -1); + } while ((p == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + assert p >= 0; + + // truncate file if given size is less than the current size + if (newSize < size) { + do { + rv = nd.truncate(fd, newSize); + } while ((rv == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + } + + // if position is beyond new size then adjust it + if (p > newSize) + p = newSize; + do { + rv = (int)position0(fd, p); + } while ((rv == IOStatus.INTERRUPTED) && isOpen()); + return this; + } finally { + threads.remove(ti); + end(rv > -1); + assert IOStatus.check(rv); + } + } + } + + public void force(boolean metaData) throws IOException { + ensureOpen(); + int rv = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return; + do { + rv = nd.force(fd, metaData); + } while ((rv == IOStatus.INTERRUPTED) && isOpen()); + } finally { + threads.remove(ti); + end(rv > -1); + assert IOStatus.check(rv); + } + } + + // Assume at first that the underlying kernel supports sendfile(); + // set this to false if we find out later that it doesn't + // + private static volatile boolean transferSupported = true; + + // Assume that the underlying kernel sendfile() will work if the target + // fd is a pipe; set this to false if we find out later that it doesn't + // + private static volatile boolean pipeSupported = true; + + // Assume that the underlying kernel sendfile() will work if the target + // fd is a file; set this to false if we find out later that it doesn't + // + private static volatile boolean fileSupported = true; + + private long transferToDirectly(long position, int icount, + WritableByteChannel target) + throws IOException + { + if (!transferSupported) + return IOStatus.UNSUPPORTED; + + FileDescriptor targetFD = null; + if (target instanceof FileChannelImpl) { + if (!fileSupported) + return IOStatus.UNSUPPORTED_CASE; + targetFD = ((FileChannelImpl)target).fd; + } else if (target instanceof SelChImpl) { + // Direct transfer to pipe causes EINVAL on some configurations + if ((target instanceof SinkChannelImpl) && !pipeSupported) + return IOStatus.UNSUPPORTED_CASE; + targetFD = ((SelChImpl)target).getFD(); + } + if (targetFD == null) + return IOStatus.UNSUPPORTED; + int thisFDVal = IOUtil.fdVal(fd); + int targetFDVal = IOUtil.fdVal(targetFD); + if (thisFDVal == targetFDVal) // Not supported on some configurations + return IOStatus.UNSUPPORTED; + + long n = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return -1; + do { + n = transferTo0(thisFDVal, position, icount, targetFDVal); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + if (n == IOStatus.UNSUPPORTED_CASE) { + if (target instanceof SinkChannelImpl) + pipeSupported = false; + if (target instanceof FileChannelImpl) + fileSupported = false; + return IOStatus.UNSUPPORTED_CASE; + } + if (n == IOStatus.UNSUPPORTED) { + // Don't bother trying again + transferSupported = false; + return IOStatus.UNSUPPORTED; + } + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end (n > -1); + } + } + + // Maximum size to map when using a mapped buffer + private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L; + + private long transferToTrustedChannel(long position, long count, + WritableByteChannel target) + throws IOException + { + boolean isSelChImpl = (target instanceof SelChImpl); + if (!((target instanceof FileChannelImpl) || isSelChImpl)) + return IOStatus.UNSUPPORTED; + + // Trusted target: Use a mapped buffer + long remaining = count; + while (remaining > 0L) { + long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); + try { + MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size); + try { + // ## Bug: Closing this channel will not terminate the write + int n = target.write(dbb); + assert n >= 0; + remaining -= n; + if (isSelChImpl) { + // one attempt to write to selectable channel + break; + } + assert n > 0; + position += n; + } finally { + unmap(dbb); + } + } catch (ClosedByInterruptException e) { + // target closed by interrupt as ClosedByInterruptException needs + // to be thrown after closing this channel. + assert !target.isOpen(); + try { + close(); + } catch (Throwable suppressed) { + e.addSuppressed(suppressed); + } + throw e; + } catch (IOException ioe) { + // Only throw exception if no bytes have been written + if (remaining == count) + throw ioe; + break; + } + } + return count - remaining; + } + + private long transferToArbitraryChannel(long position, int icount, + WritableByteChannel target) + throws IOException + { + // Untrusted target: Use a newly-erased buffer + int c = Math.min(icount, TRANSFER_SIZE); + ByteBuffer bb = Util.getTemporaryDirectBuffer(c); + long tw = 0; // Total bytes written + long pos = position; + try { + Util.erase(bb); + while (tw < icount) { + bb.limit(Math.min((int)(icount - tw), TRANSFER_SIZE)); + int nr = read(bb, pos); + if (nr <= 0) + break; + bb.flip(); + // ## Bug: Will block writing target if this channel + // ## is asynchronously closed + int nw = target.write(bb); + tw += nw; + if (nw != nr) + break; + pos += nw; + bb.clear(); + } + return tw; + } catch (IOException x) { + if (tw > 0) + return tw; + throw x; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + public long transferTo(long position, long count, + WritableByteChannel target) + throws IOException + { + ensureOpen(); + if (!target.isOpen()) + throw new ClosedChannelException(); + if (!readable) + throw new NonReadableChannelException(); + if (target instanceof FileChannelImpl && + !((FileChannelImpl)target).writable) + throw new NonWritableChannelException(); + if ((position < 0) || (count < 0)) + throw new IllegalArgumentException(); + long sz = size(); + if (position > sz) + return 0; + int icount = (int)Math.min(count, Integer.MAX_VALUE); + if ((sz - position) < icount) + icount = (int)(sz - position); + + long n; + + // Attempt a direct transfer, if the kernel supports it + if ((n = transferToDirectly(position, icount, target)) >= 0) + return n; + + // Attempt a mapped transfer, but only to trusted channel types + if ((n = transferToTrustedChannel(position, icount, target)) >= 0) + return n; + + // Slow path for untrusted targets + return transferToArbitraryChannel(position, icount, target); + } + + private long transferFromFileChannel(FileChannelImpl src, + long position, long count) + throws IOException + { + if (!src.readable) + throw new NonReadableChannelException(); + synchronized (src.positionLock) { + long pos = src.position(); + long max = Math.min(count, src.size() - pos); + + long remaining = max; + long p = pos; + while (remaining > 0L) { + long size = Math.min(remaining, MAPPED_TRANSFER_SIZE); + // ## Bug: Closing this channel will not terminate the write + MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size); + try { + long n = write(bb, position); + assert n > 0; + p += n; + position += n; + remaining -= n; + } catch (IOException ioe) { + // Only throw exception if no bytes have been written + if (remaining == max) + throw ioe; + break; + } finally { + unmap(bb); + } + } + long nwritten = max - remaining; + src.position(pos + nwritten); + return nwritten; + } + } + + private static final int TRANSFER_SIZE = 8192; + + private long transferFromArbitraryChannel(ReadableByteChannel src, + long position, long count) + throws IOException + { + // Untrusted target: Use a newly-erased buffer + int c = (int)Math.min(count, TRANSFER_SIZE); + ByteBuffer bb = Util.getTemporaryDirectBuffer(c); + long tw = 0; // Total bytes written + long pos = position; + try { + Util.erase(bb); + while (tw < count) { + bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE)); + // ## Bug: Will block reading src if this channel + // ## is asynchronously closed + int nr = src.read(bb); + if (nr <= 0) + break; + bb.flip(); + int nw = write(bb, pos); + tw += nw; + if (nw != nr) + break; + pos += nw; + bb.clear(); + } + return tw; + } catch (IOException x) { + if (tw > 0) + return tw; + throw x; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + public long transferFrom(ReadableByteChannel src, + long position, long count) + throws IOException + { + ensureOpen(); + if (!src.isOpen()) + throw new ClosedChannelException(); + if (!writable) + throw new NonWritableChannelException(); + if ((position < 0) || (count < 0)) + throw new IllegalArgumentException(); + if (position > size()) + return 0; + if (src instanceof FileChannelImpl) + return transferFromFileChannel((FileChannelImpl)src, + position, count); + + return transferFromArbitraryChannel(src, position, count); + } + + public int read(ByteBuffer dst, long position) throws IOException { + if (dst == null) + throw new NullPointerException(); + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (!readable) + throw new NonReadableChannelException(); + ensureOpen(); + if (nd.needsPositionLock()) { + synchronized (positionLock) { + return readInternal(dst, position); + } + } else { + return readInternal(dst, position); + } + } + + private int readInternal(ByteBuffer dst, long position) throws IOException { + assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); + int n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return -1; + do { + n = IOUtil.read(fd, dst, position, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + + public int write(ByteBuffer src, long position) throws IOException { + if (src == null) + throw new NullPointerException(); + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (!writable) + throw new NonWritableChannelException(); + ensureOpen(); + if (nd.needsPositionLock()) { + synchronized (positionLock) { + return writeInternal(src, position); + } + } else { + return writeInternal(src, position); + } + } + + private int writeInternal(ByteBuffer src, long position) throws IOException { + assert !nd.needsPositionLock() || Thread.holdsLock(positionLock); + int n = 0; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return -1; + do { + n = IOUtil.write(fd, src, position, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return IOStatus.normalize(n); + } finally { + threads.remove(ti); + end(n > 0); + assert IOStatus.check(n); + } + } + + + // -- Memory-mapped buffers -- + + private static class Unmapper + implements Runnable + { + // may be required to close file + private static final NativeDispatcher nd = new FileDispatcherImpl(); + + // keep track of mapped buffer usage + static volatile int count; + static volatile long totalSize; + static volatile long totalCapacity; + + private volatile long address; + private final long size; + private final int cap; + private final FileDescriptor fd; + + private Unmapper(long address, long size, int cap, + FileDescriptor fd) + { + assert (address != 0); + this.address = address; + this.size = size; + this.cap = cap; + this.fd = fd; + + synchronized (Unmapper.class) { + count++; + totalSize += size; + totalCapacity += cap; + } + } + + public void run() { + if (address == 0) + return; + unmap0(address, size); + address = 0; + + // if this mapping has a valid file descriptor then we close it + if (fd.valid()) { + try { + nd.close(fd); + } catch (IOException ignore) { + // nothing we can do + } + } + + synchronized (Unmapper.class) { + count--; + totalSize -= size; + totalCapacity -= cap; + } + } + } + + private static void unmap(MappedByteBuffer bb) { + Cleaner cl = ((DirectBuffer)bb).cleaner(); + if (cl != null) + cl.clean(); + } + + private static final int MAP_RO = 0; + private static final int MAP_RW = 1; + private static final int MAP_PV = 2; + + public MappedByteBuffer map(MapMode mode, long position, long size) + throws IOException + { + ensureOpen(); + if (mode == null) + throw new NullPointerException("Mode is null"); + if (position < 0L) + throw new IllegalArgumentException("Negative position"); + if (size < 0L) + throw new IllegalArgumentException("Negative size"); + if (position + size < 0) + throw new IllegalArgumentException("Position + size overflow"); + if (size > Integer.MAX_VALUE) + throw new IllegalArgumentException("Size exceeds Integer.MAX_VALUE"); + + int imode = -1; + if (mode == MapMode.READ_ONLY) + imode = MAP_RO; + else if (mode == MapMode.READ_WRITE) + imode = MAP_RW; + else if (mode == MapMode.PRIVATE) + imode = MAP_PV; + assert (imode >= 0); + if ((mode != MapMode.READ_ONLY) && !writable) + throw new NonWritableChannelException(); + if (!readable) + throw new NonReadableChannelException(); + + long addr = -1; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return null; + + long filesize; + do { + filesize = nd.size(fd); + } while ((filesize == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + + if (filesize < position + size) { // Extend file size + if (!writable) { + throw new IOException("Channel not open for writing " + + "- cannot extend file to required size"); + } + int rv; + do { + rv = nd.truncate(fd, position + size); + } while ((rv == IOStatus.INTERRUPTED) && isOpen()); + if (!isOpen()) + return null; + } + if (size == 0) { + addr = 0; + // a valid file descriptor is not required + FileDescriptor dummy = new FileDescriptor(); + if ((!writable) || (imode == MAP_RO)) + return Util.newMappedByteBufferR(0, 0, dummy, null); + else + return Util.newMappedByteBuffer(0, 0, dummy, null); + } + + int pagePosition = (int)(position % allocationGranularity); + long mapPosition = position - pagePosition; + long mapSize = size + pagePosition; + try { + // If no exception was thrown from map0, the address is valid + addr = map0(imode, mapPosition, mapSize); + } catch (OutOfMemoryError x) { + // An OutOfMemoryError may indicate that we've exhausted memory + // so force gc and re-attempt map + System.gc(); + try { + Thread.sleep(100); + } catch (InterruptedException y) { + Thread.currentThread().interrupt(); + } + try { + addr = map0(imode, mapPosition, mapSize); + } catch (OutOfMemoryError y) { + // After a second OOME, fail + throw new IOException("Map failed", y); + } + } + + // On Windows, and potentially other platforms, we need an open + // file descriptor for some mapping operations. + FileDescriptor mfd; + try { + mfd = nd.duplicateForMapping(fd); + } catch (IOException ioe) { + unmap0(addr, mapSize); + throw ioe; + } + + assert (IOStatus.checkAll(addr)); + assert (addr % allocationGranularity == 0); + int isize = (int)size; + Unmapper um = new Unmapper(addr, mapSize, isize, mfd); + if ((!writable) || (imode == MAP_RO)) { + return Util.newMappedByteBufferR(isize, + addr + pagePosition, + mfd, + um); + } else { + return Util.newMappedByteBuffer(isize, + addr + pagePosition, + mfd, + um); + } + } finally { + threads.remove(ti); + end(IOStatus.checkAll(addr)); + } + } + + /** + * Invoked by sun.management.ManagementFactoryHelper to create the management + * interface for mapped buffers. + */ + public static sun.misc.JavaNioAccess.BufferPool getMappedBufferPool() { + return new sun.misc.JavaNioAccess.BufferPool() { + @Override + public String getName() { + return "mapped"; + } + @Override + public long getCount() { + return Unmapper.count; + } + @Override + public long getTotalCapacity() { + return Unmapper.totalCapacity; + } + @Override + public long getMemoryUsed() { + return Unmapper.totalSize; + } + }; + } + + // -- Locks -- + + + + // keeps track of locks on this file + private volatile FileLockTable fileLockTable; + + // indicates if file locks are maintained system-wide (as per spec) + private static boolean isSharedFileLockTable; + + // indicates if the disableSystemWideOverlappingFileLockCheck property + // has been checked + private static volatile boolean propertyChecked; + + // The lock list in J2SE 1.4/5.0 was local to each FileChannel instance so + // the overlap check wasn't system wide when there were multiple channels to + // the same file. This property is used to get 1.4/5.0 behavior if desired. + private static boolean isSharedFileLockTable() { + if (!propertyChecked) { + synchronized (FileChannelImpl.class) { + if (!propertyChecked) { + String value = AccessController.doPrivileged( + new GetPropertyAction( + "sun.nio.ch.disableSystemWideOverlappingFileLockCheck")); + isSharedFileLockTable = ((value == null) || value.equals("false")); + propertyChecked = true; + } + } + } + return isSharedFileLockTable; + } + + private FileLockTable fileLockTable() throws IOException { + if (fileLockTable == null) { + synchronized (this) { + if (fileLockTable == null) { + if (isSharedFileLockTable()) { + int ti = threads.add(); + try { + ensureOpen(); + fileLockTable = FileLockTable.newSharedFileLockTable(this, fd); + } finally { + threads.remove(ti); + } + } else { + fileLockTable = new SimpleFileLockTable(); + } + } + } + } + return fileLockTable; + } + + public FileLock lock(long position, long size, boolean shared) + throws IOException + { + ensureOpen(); + if (shared && !readable) + throw new NonReadableChannelException(); + if (!shared && !writable) + throw new NonWritableChannelException(); + FileLockImpl fli = new FileLockImpl(this, position, size, shared); + FileLockTable flt = fileLockTable(); + flt.add(fli); + boolean completed = false; + int ti = -1; + try { + begin(); + ti = threads.add(); + if (!isOpen()) + return null; + int n; + do { + n = nd.lock(fd, true, position, size, shared); + } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); + if (isOpen()) { + if (n == FileDispatcher.RET_EX_LOCK) { + assert shared; + FileLockImpl fli2 = new FileLockImpl(this, position, size, + false); + flt.replace(fli, fli2); + fli = fli2; + } + completed = true; + } + } finally { + if (!completed) + flt.remove(fli); + threads.remove(ti); + try { + end(completed); + } catch (ClosedByInterruptException e) { + throw new FileLockInterruptionException(); + } + } + return fli; + } + + public FileLock tryLock(long position, long size, boolean shared) + throws IOException + { + ensureOpen(); + if (shared && !readable) + throw new NonReadableChannelException(); + if (!shared && !writable) + throw new NonWritableChannelException(); + FileLockImpl fli = new FileLockImpl(this, position, size, shared); + FileLockTable flt = fileLockTable(); + flt.add(fli); + int result; + + int ti = threads.add(); + try { + try { + ensureOpen(); + result = nd.lock(fd, false, position, size, shared); + } catch (IOException e) { + flt.remove(fli); + throw e; + } + if (result == FileDispatcher.NO_LOCK) { + flt.remove(fli); + return null; + } + if (result == FileDispatcher.RET_EX_LOCK) { + assert shared; + FileLockImpl fli2 = new FileLockImpl(this, position, size, + false); + flt.replace(fli, fli2); + return fli2; + } + return fli; + } finally { + threads.remove(ti); + } + } + + void release(FileLockImpl fli) throws IOException { + int ti = threads.add(); + try { + ensureOpen(); + nd.release(fd, fli.position(), fli.size()); + } finally { + threads.remove(ti); + } + assert fileLockTable != null; + fileLockTable.remove(fli); + } + + // -- File lock support -- + + /** + * A simple file lock table that maintains a list of FileLocks obtained by a + * FileChannel. Use to get 1.4/5.0 behaviour. + */ + private static class SimpleFileLockTable extends FileLockTable { + // synchronize on list for access + private final List lockList = new ArrayList(2); + + public SimpleFileLockTable() { + } + + private void checkList(long position, long size) + throws OverlappingFileLockException + { + assert Thread.holdsLock(lockList); + for (FileLock fl: lockList) { + if (fl.overlaps(position, size)) { + throw new OverlappingFileLockException(); + } + } + } + + public void add(FileLock fl) throws OverlappingFileLockException { + synchronized (lockList) { + checkList(fl.position(), fl.size()); + lockList.add(fl); + } + } + + public void remove(FileLock fl) { + synchronized (lockList) { + lockList.remove(fl); + } + } + + public List removeAll() { + synchronized(lockList) { + List result = new ArrayList(lockList); + lockList.clear(); + return result; + } + } + + public void replace(FileLock fl1, FileLock fl2) { + synchronized (lockList) { + lockList.remove(fl1); + lockList.add(fl2); + } + } + } + + // -- Native methods -- + + // Creates a new mapping + private native long map0(int prot, long position, long length) + throws IOException; + + // Removes an existing mapping + private static native int unmap0(long address, long length); + + // Transfers from src to dst, or returns -2 if kernel can't do that + private native long transferTo0(int src, long position, long count, int dst); + + // Sets or reports this file's position + // If offset is -1, the current position is returned + // otherwise the position is set to offset + private native long position0(FileDescriptor fd, long offset); + + // Caches fieldIDs + private static native long initIDs(); + + static { + IOUtil.load(); + allocationGranularity = initIDs(); + } + +} diff --git a/src/sun/nio/ch/FileDispatcher.java b/src/sun/nio/ch/FileDispatcher.java new file mode 100644 index 00000000..367a8ee6 --- /dev/null +++ b/src/sun/nio/ch/FileDispatcher.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; + +abstract class FileDispatcher extends NativeDispatcher { + + public static final int NO_LOCK = -1; // Failed to lock + public static final int LOCKED = 0; // Obtained requested lock + public static final int RET_EX_LOCK = 1; // Obtained exclusive lock + public static final int INTERRUPTED = 2; // Request interrupted + + abstract int force(FileDescriptor fd, boolean metaData) throws IOException; + + abstract int truncate(FileDescriptor fd, long size) throws IOException; + + abstract long size(FileDescriptor fd) throws IOException; + + abstract int lock(FileDescriptor fd, boolean blocking, long pos, long size, + boolean shared) throws IOException; + + abstract void release(FileDescriptor fd, long pos, long size) + throws IOException; + + /** + * Returns a dup of fd if a file descriptor is required for + * memory-mapping operations, otherwise returns an invalid + * FileDescriptor (meaning a newly allocated FileDescriptor) + */ + abstract FileDescriptor duplicateForMapping(FileDescriptor fd) + throws IOException; +} diff --git a/src/sun/nio/ch/FileLockImpl.java b/src/sun/nio/ch/FileLockImpl.java new file mode 100644 index 00000000..5e94b035 --- /dev/null +++ b/src/sun/nio/ch/FileLockImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.Channel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; + +public class FileLockImpl + extends FileLock +{ + private volatile boolean valid = true; + + FileLockImpl(FileChannel channel, long position, long size, boolean shared) + { + super(channel, position, size, shared); + } + + FileLockImpl(AsynchronousFileChannel channel, long position, long size, boolean shared) + { + super(channel, position, size, shared); + } + + public boolean isValid() { + return valid; + } + + void invalidate() { + assert Thread.holdsLock(this); + valid = false; + } + + public synchronized void release() throws IOException { + Channel ch = acquiredBy(); + if (!ch.isOpen()) + throw new ClosedChannelException(); + if (valid) { + if (ch instanceof FileChannelImpl) + ((FileChannelImpl)ch).release(this); + else if (ch instanceof AsynchronousFileChannelImpl) + ((AsynchronousFileChannelImpl)ch).release(this); + else throw new AssertionError(); + valid = false; + } + } +} diff --git a/src/sun/nio/ch/FileLockTable.java b/src/sun/nio/ch/FileLockTable.java new file mode 100644 index 00000000..90722f97 --- /dev/null +++ b/src/sun/nio/ch/FileLockTable.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.nio.channels.Channel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +abstract class FileLockTable { + protected FileLockTable() { + } + + /** + * Creates and returns a file lock table for a channel that is connected to + * the a system-wide map of all file locks for the Java virtual machine. + */ + public static FileLockTable newSharedFileLockTable(Channel channel, + FileDescriptor fd) + throws IOException + { + return new SharedFileLockTable(channel, fd); + } + + /** + * Adds a file lock to the table. + * + * @throws OverlappingFileLockException if the file lock overlaps + * with an existing file lock in the table + */ + public abstract void add(FileLock fl) throws OverlappingFileLockException; + + /** + * Remove an existing file lock from the table. + */ + public abstract void remove(FileLock fl); + + /** + * Removes all file locks from the table. + * + * @return The list of file locks removed + */ + public abstract List removeAll(); + + /** + * Replaces an existing file lock in the table. + */ + public abstract void replace(FileLock fl1, FileLock fl2); +} + + +/** + * A file lock table that is over a system-wide map of all file locks. + */ +class SharedFileLockTable extends FileLockTable { + + /** + * A weak reference to a FileLock. + *

    + * SharedFileLockTable uses a list of file lock references to avoid keeping the + * FileLock (and FileChannel) alive. + */ + private static class FileLockReference extends WeakReference { + private FileKey fileKey; + + FileLockReference(FileLock referent, + ReferenceQueue queue, + FileKey key) { + super(referent, queue); + this.fileKey = key; + } + + FileKey fileKey() { + return fileKey; + } + } + + // The system-wide map is a ConcurrentHashMap that is keyed on the FileKey. + // The map value is a list of file locks represented by FileLockReferences. + // All access to the list must be synchronized on the list. + private static ConcurrentHashMap> lockMap = + new ConcurrentHashMap>(); + + // reference queue for cleared refs + private static ReferenceQueue queue = new ReferenceQueue(); + + // The connection to which this table is connected + private final Channel channel; + + // File key for the file that this channel is connected to + private final FileKey fileKey; + + SharedFileLockTable(Channel channel, FileDescriptor fd) throws IOException { + this.channel = channel; + this.fileKey = FileKey.create(fd); + } + + @Override + public void add(FileLock fl) throws OverlappingFileLockException { + List list = lockMap.get(fileKey); + + for (;;) { + + // The key isn't in the map so we try to create it atomically + if (list == null) { + list = new ArrayList(2); + List prev; + synchronized (list) { + prev = lockMap.putIfAbsent(fileKey, list); + if (prev == null) { + // we successfully created the key so we add the file lock + list.add(new FileLockReference(fl, queue, fileKey)); + break; + } + } + // someone else got there first + list = prev; + } + + // There is already a key. It is possible that some other thread + // is removing it so we re-fetch the value from the map. If it + // hasn't changed then we check the list for overlapping locks + // and add the new lock to the list. + synchronized (list) { + List current = lockMap.get(fileKey); + if (list == current) { + checkList(list, fl.position(), fl.size()); + list.add(new FileLockReference(fl, queue, fileKey)); + break; + } + list = current; + } + + } + + // process any stale entries pending in the reference queue + removeStaleEntries(); + } + + private void removeKeyIfEmpty(FileKey fk, List list) { + assert Thread.holdsLock(list); + assert lockMap.get(fk) == list; + if (list.isEmpty()) { + lockMap.remove(fk); + } + } + + @Override + public void remove(FileLock fl) { + assert fl != null; + + // the lock must exist so the list of locks must be present + List list = lockMap.get(fileKey); + if (list == null) return; + + synchronized (list) { + int index = 0; + while (index < list.size()) { + FileLockReference ref = list.get(index); + FileLock lock = ref.get(); + if (lock == fl) { + assert (lock != null) && (lock.acquiredBy() == channel); + ref.clear(); + list.remove(index); + break; + } + index++; + } + } + } + + @Override + public List removeAll() { + List result = new ArrayList(); + List list = lockMap.get(fileKey); + if (list != null) { + synchronized (list) { + int index = 0; + while (index < list.size()) { + FileLockReference ref = list.get(index); + FileLock lock = ref.get(); + + // remove locks obtained by this channel + if (lock != null && lock.acquiredBy() == channel) { + // remove the lock from the list + ref.clear(); + list.remove(index); + + // add to result + result.add(lock); + } else { + index++; + } + } + + // once the lock list is empty we remove it from the map + removeKeyIfEmpty(fileKey, list); + } + } + return result; + } + + @Override + public void replace(FileLock fromLock, FileLock toLock) { + // the lock must exist so there must be a list + List list = lockMap.get(fileKey); + assert list != null; + + synchronized (list) { + for (int index=0; index list, long position, long size) + throws OverlappingFileLockException + { + assert Thread.holdsLock(list); + for (FileLockReference ref: list) { + FileLock fl = ref.get(); + if (fl != null && fl.overlaps(position, size)) + throw new OverlappingFileLockException(); + } + } + + // Process the reference queue + private void removeStaleEntries() { + FileLockReference ref; + while ((ref = (FileLockReference)queue.poll()) != null) { + FileKey fk = ref.fileKey(); + List list = lockMap.get(fk); + if (list != null) { + synchronized (list) { + list.remove(ref); + removeKeyIfEmpty(fk, list); + } + } + } + } +} diff --git a/src/sun/nio/ch/Groupable.java b/src/sun/nio/ch/Groupable.java new file mode 100644 index 00000000..64d8958e --- /dev/null +++ b/src/sun/nio/ch/Groupable.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +/** + * Implemented by asynchronous channels that can be associated with an + * asynchronous channel group. + */ + +interface Groupable { + AsynchronousChannelGroupImpl group(); +} diff --git a/src/sun/nio/ch/IOStatus.java b/src/sun/nio/ch/IOStatus.java new file mode 100644 index 00000000..968e1348 --- /dev/null +++ b/src/sun/nio/ch/IOStatus.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.lang.annotation.Native; + +// Constants for reporting I/O status + +public final class IOStatus { + + private IOStatus() { } + + @Native public static final int EOF = -1; // End of file + @Native public static final int UNAVAILABLE = -2; // Nothing available (non-blocking) + @Native public static final int INTERRUPTED = -3; // System call interrupted + @Native public static final int UNSUPPORTED = -4; // Operation not supported + @Native public static final int THROWN = -5; // Exception thrown in JNI code + @Native public static final int UNSUPPORTED_CASE = -6; // This case not supported + + // The following two methods are for use in try/finally blocks where a + // status value needs to be normalized before being returned to the invoker + // but also checked for illegal negative values before the return + // completes, like so: + // + // int n = 0; + // try { + // begin(); + // n = op(fd, buf, ...); + // return IOStatus.normalize(n); // Converts UNAVAILABLE to zero + // } finally { + // end(n > 0); + // assert IOStatus.check(n); // Checks other negative values + // } + // + + public static int normalize(int n) { + if (n == UNAVAILABLE) + return 0; + return n; + } + + public static boolean check(int n) { + return (n >= UNAVAILABLE); + } + + public static long normalize(long n) { + if (n == UNAVAILABLE) + return 0; + return n; + } + + public static boolean check(long n) { + return (n >= UNAVAILABLE); + } + + // Return true iff n is not one of the IOStatus values + public static boolean checkAll(long n) { + return ((n > EOF) || (n < UNSUPPORTED_CASE)); + } + +} diff --git a/src/sun/nio/ch/IOUtil.java b/src/sun/nio/ch/IOUtil.java new file mode 100644 index 00000000..91980b16 --- /dev/null +++ b/src/sun/nio/ch/IOUtil.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; + + +/** + * File-descriptor based I/O utilities that are shared by NIO classes. + */ + +public class IOUtil { + + /** + * Max number of iovec structures that readv/writev supports + */ + static final int IOV_MAX; + + private IOUtil() { } // No instantiation + + static int write(FileDescriptor fd, ByteBuffer src, long position, + NativeDispatcher nd) + throws IOException + { + if (src instanceof DirectBuffer) + return writeFromNativeBuffer(fd, src, position, nd); + + // Substitute a native buffer + int pos = src.position(); + int lim = src.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + ByteBuffer bb = Util.getTemporaryDirectBuffer(rem); + try { + bb.put(src); + bb.flip(); + // Do not update src until we see how many bytes were written + src.position(pos); + + int n = writeFromNativeBuffer(fd, bb, position, nd); + if (n > 0) { + // now update src + src.position(pos + n); + } + return n; + } finally { + Util.offerFirstTemporaryDirectBuffer(bb); + } + } + + private static int writeFromNativeBuffer(FileDescriptor fd, ByteBuffer bb, + long position, NativeDispatcher nd) + throws IOException + { + int pos = bb.position(); + int lim = bb.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + int written = 0; + if (rem == 0) + return 0; + if (position != -1) { + written = nd.pwrite(fd, + ((DirectBuffer)bb).address() + pos, + rem, position); + } else { + written = nd.write(fd, ((DirectBuffer)bb).address() + pos, rem); + } + if (written > 0) + bb.position(pos + written); + return written; + } + + static long write(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd) + throws IOException + { + return write(fd, bufs, 0, bufs.length, nd); + } + + static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + NativeDispatcher nd) + throws IOException + { + IOVecWrapper vec = IOVecWrapper.get(length); + + boolean completed = false; + int iov_len = 0; + try { + + // Iterate over buffers to populate native iovec array. + int count = offset + length; + int i = offset; + while (i < count && iov_len < IOV_MAX) { + ByteBuffer buf = bufs[i]; + int pos = buf.position(); + int lim = buf.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + if (rem > 0) { + vec.setBuffer(iov_len, buf, pos, rem); + + // allocate shadow buffer to ensure I/O is done with direct buffer + if (!(buf instanceof DirectBuffer)) { + ByteBuffer shadow = Util.getTemporaryDirectBuffer(rem); + shadow.put(buf); + shadow.flip(); + vec.setShadow(iov_len, shadow); + buf.position(pos); // temporarily restore position in user buffer + buf = shadow; + pos = shadow.position(); + } + + vec.putBase(iov_len, ((DirectBuffer)buf).address() + pos); + vec.putLen(iov_len, rem); + iov_len++; + } + i++; + } + if (iov_len == 0) + return 0L; + + long bytesWritten = nd.writev(fd, vec.address, iov_len); + + // Notify the buffers how many bytes were taken + long left = bytesWritten; + for (int j=0; j 0) { + ByteBuffer buf = vec.getBuffer(j); + int pos = vec.getPosition(j); + int rem = vec.getRemaining(j); + int n = (left > rem) ? rem : (int)left; + buf.position(pos + n); + left -= n; + } + // return shadow buffers to buffer pool + ByteBuffer shadow = vec.getShadow(j); + if (shadow != null) + Util.offerLastTemporaryDirectBuffer(shadow); + vec.clearRefs(j); + } + + completed = true; + return bytesWritten; + + } finally { + // if an error occurred then clear refs to buffers and return any shadow + // buffers to cache + if (!completed) { + for (int j=0; j 0) + dst.put(bb); + return n; + } finally { + Util.offerFirstTemporaryDirectBuffer(bb); + } + } + + private static int readIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb, + long position, NativeDispatcher nd) + throws IOException + { + int pos = bb.position(); + int lim = bb.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + if (rem == 0) + return 0; + int n = 0; + if (position != -1) { + n = nd.pread(fd, ((DirectBuffer)bb).address() + pos, + rem, position); + } else { + n = nd.read(fd, ((DirectBuffer)bb).address() + pos, rem); + } + if (n > 0) + bb.position(pos + n); + return n; + } + + static long read(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd) + throws IOException + { + return read(fd, bufs, 0, bufs.length, nd); + } + + static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + NativeDispatcher nd) + throws IOException + { + IOVecWrapper vec = IOVecWrapper.get(length); + + boolean completed = false; + int iov_len = 0; + try { + + // Iterate over buffers to populate native iovec array. + int count = offset + length; + int i = offset; + while (i < count && iov_len < IOV_MAX) { + ByteBuffer buf = bufs[i]; + if (buf.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + int pos = buf.position(); + int lim = buf.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + if (rem > 0) { + vec.setBuffer(iov_len, buf, pos, rem); + + // allocate shadow buffer to ensure I/O is done with direct buffer + if (!(buf instanceof DirectBuffer)) { + ByteBuffer shadow = Util.getTemporaryDirectBuffer(rem); + vec.setShadow(iov_len, shadow); + buf = shadow; + pos = shadow.position(); + } + + vec.putBase(iov_len, ((DirectBuffer)buf).address() + pos); + vec.putLen(iov_len, rem); + iov_len++; + } + i++; + } + if (iov_len == 0) + return 0L; + + long bytesRead = nd.readv(fd, vec.address, iov_len); + + // Notify the buffers how many bytes were read + long left = bytesRead; + for (int j=0; j 0) { + ByteBuffer buf = vec.getBuffer(j); + int rem = vec.getRemaining(j); + int n = (left > rem) ? rem : (int)left; + if (shadow == null) { + int pos = vec.getPosition(j); + buf.position(pos + n); + } else { + shadow.limit(shadow.position() + n); + buf.put(shadow); + } + left -= n; + } + if (shadow != null) + Util.offerLastTemporaryDirectBuffer(shadow); + vec.clearRefs(j); + } + + completed = true; + return bytesRead; + + } finally { + // if an error occurred then clear refs to buffers and return any shadow + // buffers to cache + if (!completed) { + for (int j=0; j() { + public Void run() { + System.loadLibrary("net"); + System.loadLibrary("nio"); + return null; + } + }); + + initIDs(); + + IOV_MAX = iovMax(); + } + +} diff --git a/src/sun/nio/ch/IOVecWrapper.java b/src/sun/nio/ch/IOVecWrapper.java new file mode 100644 index 00000000..1a1e6688 --- /dev/null +++ b/src/sun/nio/ch/IOVecWrapper.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.nio.ByteBuffer; + +import sun.misc.Cleaner; + + +/** + * Manipulates a native array of iovec structs on Solaris: + * + * typedef struct iovec { + * caddr_t iov_base; + int iov_len; + * } iovec_t; + * + * @author Mike McCloskey + * @since 1.4 + */ + +class IOVecWrapper { + + // Miscellaneous constants + private static final int BASE_OFFSET = 0; + private static final int LEN_OFFSET; + private static final int SIZE_IOVEC; + + // The iovec array + private final AllocatedNativeObject vecArray; + + // Number of elements in iovec array + private final int size; + + // Buffers and position/remaining corresponding to elements in iovec array + private final ByteBuffer[] buf; + private final int[] position; + private final int[] remaining; + + // Shadow buffers for cases when original buffer is substituted + private final ByteBuffer[] shadow; + + // Base address of this array + final long address; + + // Address size in bytes + static int addressSize; + + private static class Deallocator implements Runnable { + private final AllocatedNativeObject obj; + Deallocator(AllocatedNativeObject obj) { + this.obj = obj; + } + public void run() { + obj.free(); + } + } + + // per thread IOVecWrapper + private static final ThreadLocal cached = + new ThreadLocal(); + + private IOVecWrapper(int size) { + this.size = size; + this.buf = new ByteBuffer[size]; + this.position = new int[size]; + this.remaining = new int[size]; + this.shadow = new ByteBuffer[size]; + this.vecArray = new AllocatedNativeObject(size * SIZE_IOVEC, false); + this.address = vecArray.address(); + } + + static IOVecWrapper get(int size) { + IOVecWrapper wrapper = cached.get(); + if (wrapper != null && wrapper.size < size) { + // not big enough; eagerly release memory + wrapper.vecArray.free(); + wrapper = null; + } + if (wrapper == null) { + wrapper = new IOVecWrapper(size); + Cleaner.create(wrapper, new Deallocator(wrapper.vecArray)); + cached.set(wrapper); + } + return wrapper; + } + + void setBuffer(int i, ByteBuffer buf, int pos, int rem) { + this.buf[i] = buf; + this.position[i] = pos; + this.remaining[i] = rem; + } + + void setShadow(int i, ByteBuffer buf) { + shadow[i] = buf; + } + + ByteBuffer getBuffer(int i) { + return buf[i]; + } + + int getPosition(int i) { + return position[i]; + } + + int getRemaining(int i) { + return remaining[i]; + } + + ByteBuffer getShadow(int i) { + return shadow[i]; + } + + void clearRefs(int i) { + buf[i] = null; + shadow[i] = null; + } + + void putBase(int i, long base) { + int offset = SIZE_IOVEC * i + BASE_OFFSET; + if (addressSize == 4) + vecArray.putInt(offset, (int)base); + else + vecArray.putLong(offset, base); + } + + void putLen(int i, long len) { + int offset = SIZE_IOVEC * i + LEN_OFFSET; + if (addressSize == 4) + vecArray.putInt(offset, (int)len); + else + vecArray.putLong(offset, len); + } + + static { + addressSize = Util.unsafe().addressSize(); + LEN_OFFSET = addressSize; + SIZE_IOVEC = (short) (addressSize * 2); + } +} diff --git a/src/sun/nio/ch/Interruptible.java b/src/sun/nio/ch/Interruptible.java new file mode 100644 index 00000000..8445e35f --- /dev/null +++ b/src/sun/nio/ch/Interruptible.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * An object that interrupts a thread blocked in an I/O operation. + */ + +package sun.nio.ch; + +public interface Interruptible { + + public void interrupt(Thread t); + +} diff --git a/src/sun/nio/ch/Invoker.java b/src/sun/nio/ch/Invoker.java new file mode 100644 index 00000000..5aea412d --- /dev/null +++ b/src/sun/nio/ch/Invoker.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.nio.channels.AsynchronousChannel; +import java.nio.channels.CompletionHandler; +import java.nio.channels.ShutdownChannelGroupException; +import java.security.AccessController; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +import sun.security.action.GetIntegerAction; + +/** + * Defines static methods to invoke a completion handler or arbitrary task. + */ + +class Invoker { + private Invoker() { } + + // maximum number of completion handlers that may be invoked on the current + // thread before it re-directs invocations to the thread pool. This helps + // avoid stack overflow and lessens the risk of starvation. + private static final int maxHandlerInvokeCount = AccessController.doPrivileged( + new GetIntegerAction("sun.nio.ch.maxCompletionHandlersOnStack", 16)); + + // Per-thread object with reference to channel group and a counter for + // the number of completion handlers invoked. This should be reset to 0 + // when all completion handlers have completed. + static class GroupAndInvokeCount { + private final AsynchronousChannelGroupImpl group; + private int handlerInvokeCount; + GroupAndInvokeCount(AsynchronousChannelGroupImpl group) { + this.group = group; + } + AsynchronousChannelGroupImpl group() { + return group; + } + int invokeCount() { + return handlerInvokeCount; + } + void setInvokeCount(int value) { + handlerInvokeCount = value; + } + void resetInvokeCount() { + handlerInvokeCount = 0; + } + void incrementInvokeCount() { + handlerInvokeCount++; + } + } + private static final ThreadLocal myGroupAndInvokeCount = + new ThreadLocal() { + @Override protected GroupAndInvokeCount initialValue() { + return null; + } + }; + + /** + * Binds this thread to the given group + */ + static void bindToGroup(AsynchronousChannelGroupImpl group) { + myGroupAndInvokeCount.set(new GroupAndInvokeCount(group)); + } + + /** + * Returns the GroupAndInvokeCount object for this thread. + */ + static GroupAndInvokeCount getGroupAndInvokeCount() { + return myGroupAndInvokeCount.get(); + } + + /** + * Returns true if the current thread is in a channel group's thread pool + */ + static boolean isBoundToAnyGroup() { + return myGroupAndInvokeCount.get() != null; + } + + /** + * Returns true if the current thread is in the given channel's thread + * pool and we haven't exceeded the maximum number of handler frames on + * the stack. + */ + static boolean mayInvokeDirect(GroupAndInvokeCount myGroupAndInvokeCount, + AsynchronousChannelGroupImpl group) + { + if ((myGroupAndInvokeCount != null) && + (myGroupAndInvokeCount.group() == group) && + (myGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount)) + { + return true; + } + return false; + } + + /** + * Invoke handler without checking the thread identity or number of handlers + * on the thread stack. + */ + static void invokeUnchecked(CompletionHandler handler, + A attachment, + V value, + Throwable exc) + { + if (exc == null) { + handler.completed(value, attachment); + } else { + handler.failed(exc, attachment); + } + + // clear interrupt + Thread.interrupted(); + + // clear thread locals when in default thread pool + if (System.getSecurityManager() != null) { + Thread me = Thread.currentThread(); + if (me instanceof sun.misc.InnocuousThread) { + GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get(); + ((sun.misc.InnocuousThread)me).eraseThreadLocals(); + if (thisGroupAndInvokeCount != null) { + myGroupAndInvokeCount.set(thisGroupAndInvokeCount); + } + } + } + } + + /** + * Invoke handler assuming thread identity already checked + */ + static void invokeDirect(GroupAndInvokeCount myGroupAndInvokeCount, + CompletionHandler handler, + A attachment, + V result, + Throwable exc) + { + myGroupAndInvokeCount.incrementInvokeCount(); + Invoker.invokeUnchecked(handler, attachment, result, exc); + } + + /** + * Invokes the handler. If the current thread is in the channel group's + * thread pool then the handler is invoked directly, otherwise it is + * invoked indirectly. + */ + static void invoke(AsynchronousChannel channel, + CompletionHandler handler, + A attachment, + V result, + Throwable exc) + { + boolean invokeDirect = false; + boolean identityOkay = false; + GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get(); + if (thisGroupAndInvokeCount != null) { + if ((thisGroupAndInvokeCount.group() == ((Groupable)channel).group())) + identityOkay = true; + if (identityOkay && + (thisGroupAndInvokeCount.invokeCount() < maxHandlerInvokeCount)) + { + // group match + invokeDirect = true; + } + } + if (invokeDirect) { + invokeDirect(thisGroupAndInvokeCount, handler, attachment, result, exc); + } else { + try { + invokeIndirectly(channel, handler, attachment, result, exc); + } catch (RejectedExecutionException ree) { + // channel group shutdown; fallback to invoking directly + // if the current thread has the right identity. + if (identityOkay) { + invokeDirect(thisGroupAndInvokeCount, + handler, attachment, result, exc); + } else { + throw new ShutdownChannelGroupException(); + } + } + } + } + + /** + * Invokes the handler indirectly via the channel group's thread pool. + */ + static void invokeIndirectly(AsynchronousChannel channel, + final CompletionHandler handler, + final A attachment, + final V result, + final Throwable exc) + { + try { + ((Groupable)channel).group().executeOnPooledThread(new Runnable() { + public void run() { + GroupAndInvokeCount thisGroupAndInvokeCount = + myGroupAndInvokeCount.get(); + if (thisGroupAndInvokeCount != null) + thisGroupAndInvokeCount.setInvokeCount(1); + invokeUnchecked(handler, attachment, result, exc); + } + }); + } catch (RejectedExecutionException ree) { + throw new ShutdownChannelGroupException(); + } + } + + /** + * Invokes the handler "indirectly" in the given Executor + */ + static void invokeIndirectly(final CompletionHandler handler, + final A attachment, + final V value, + final Throwable exc, + Executor executor) + { + try { + executor.execute(new Runnable() { + public void run() { + invokeUnchecked(handler, attachment, value, exc); + } + }); + } catch (RejectedExecutionException ree) { + throw new ShutdownChannelGroupException(); + } + } + + /** + * Invokes the given task on the thread pool associated with the given + * channel. If the current thread is in the thread pool then the task is + * invoked directly. + */ + static void invokeOnThreadInThreadPool(Groupable channel, + Runnable task) + { + boolean invokeDirect; + GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get(); + AsynchronousChannelGroupImpl targetGroup = channel.group(); + if (thisGroupAndInvokeCount == null) { + invokeDirect = false; + } else { + invokeDirect = (thisGroupAndInvokeCount.group == targetGroup); + } + try { + if (invokeDirect) { + task.run(); + } else { + targetGroup.executeOnPooledThread(task); + } + } catch (RejectedExecutionException ree) { + throw new ShutdownChannelGroupException(); + } + } + + /** + * Invoke handler with completed result. This method does not check the + * thread identity or the number of handlers on the thread stack. + */ + static void invokeUnchecked(PendingFuture future) { + assert future.isDone(); + CompletionHandler handler = future.handler(); + if (handler != null) { + invokeUnchecked(handler, + future.attachment(), + future.value(), + future.exception()); + } + } + + /** + * Invoke handler with completed result. If the current thread is in the + * channel group's thread pool then the handler is invoked directly, + * otherwise it is invoked indirectly. + */ + static void invoke(PendingFuture future) { + assert future.isDone(); + CompletionHandler handler = future.handler(); + if (handler != null) { + invoke(future.channel(), + handler, + future.attachment(), + future.value(), + future.exception()); + } + } + + /** + * Invoke handler with completed result. The handler is invoked indirectly, + * via the channel group's thread pool. + */ + static void invokeIndirectly(PendingFuture future) { + assert future.isDone(); + CompletionHandler handler = future.handler(); + if (handler != null) { + invokeIndirectly(future.channel(), + handler, + future.attachment(), + future.value(), + future.exception()); + } + } +} diff --git a/src/sun/nio/ch/MembershipKeyImpl.java b/src/sun/nio/ch/MembershipKeyImpl.java new file mode 100644 index 00000000..cdbeed51 --- /dev/null +++ b/src/sun/nio/ch/MembershipKeyImpl.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.nio.channels.MembershipKey; +import java.nio.channels.MulticastChannel; +import java.util.HashSet; + +/** + * MembershipKey implementation. + */ + +class MembershipKeyImpl + extends MembershipKey +{ + private final MulticastChannel ch; + private final InetAddress group; + private final NetworkInterface interf; + private final InetAddress source; + + // true when key is valid + private volatile boolean valid = true; + + // lock used when creating or accessing blockedSet + private Object stateLock = new Object(); + + // set of source addresses that are blocked + private HashSet blockedSet; + + private MembershipKeyImpl(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source) + { + this.ch = ch; + this.group = group; + this.interf = interf; + this.source = source; + } + + /** + * MembershipKey will additional context for IPv4 membership + */ + static class Type4 extends MembershipKeyImpl { + private final int groupAddress; + private final int interfAddress; + private final int sourceAddress; + + Type4(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + int groupAddress, + int interfAddress, + int sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.interfAddress = interfAddress; + this.sourceAddress = sourceAddress; + } + + int groupAddress() { + return groupAddress; + } + + int interfaceAddress() { + return interfAddress; + } + + int source() { + return sourceAddress; + } + } + + /** + * MembershipKey will additional context for IPv6 membership + */ + static class Type6 extends MembershipKeyImpl { + private final byte[] groupAddress; + private final int index; + private final byte[] sourceAddress; + + Type6(MulticastChannel ch, + InetAddress group, + NetworkInterface interf, + InetAddress source, + byte[] groupAddress, + int index, + byte[] sourceAddress) + { + super(ch, group, interf, source); + this.groupAddress = groupAddress; + this.index = index; + this.sourceAddress = sourceAddress; + } + + byte[] groupAddress() { + return groupAddress; + } + + int index() { + return index; + } + + byte[] source() { + return sourceAddress; + } + } + + public boolean isValid() { + return valid; + } + + // package-private + void invalidate() { + valid = false; + } + + public void drop() { + // delegate to channel + ((DatagramChannelImpl)ch).drop(this); + } + + @Override + public MulticastChannel channel() { + return ch; + } + + @Override + public InetAddress group() { + return group; + } + + @Override + public NetworkInterface networkInterface() { + return interf; + } + + @Override + public InetAddress sourceAddress() { + return source; + } + + @Override + public MembershipKey block(InetAddress toBlock) + throws IOException + { + if (source != null) + throw new IllegalStateException("key is source-specific"); + + synchronized (stateLock) { + if ((blockedSet != null) && blockedSet.contains(toBlock)) { + // already blocked, nothing to do + return this; + } + + ((DatagramChannelImpl)ch).block(this, toBlock); + + // created blocked set if required and add source address + if (blockedSet == null) + blockedSet = new HashSet(); + blockedSet.add(toBlock); + } + return this; + } + + @Override + public MembershipKey unblock(InetAddress toUnblock) { + synchronized (stateLock) { + if ((blockedSet == null) || !blockedSet.contains(toUnblock)) + throw new IllegalStateException("not blocked"); + + ((DatagramChannelImpl)ch).unblock(this, toUnblock); + + blockedSet.remove(toUnblock); + } + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(64); + sb.append('<'); + sb.append(group.getHostAddress()); + sb.append(','); + sb.append(interf.getName()); + if (source != null) { + sb.append(','); + sb.append(source.getHostAddress()); + } + sb.append('>'); + return sb.toString(); + } +} diff --git a/src/sun/nio/ch/MembershipRegistry.java b/src/sun/nio/ch/MembershipRegistry.java new file mode 100644 index 00000000..7f0311c6 --- /dev/null +++ b/src/sun/nio/ch/MembershipRegistry.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.nio.channels.MembershipKey; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Simple registry of membership keys for a MulticastChannel. + * + * Instances of this object are not safe by multiple concurrent threads. + */ + +class MembershipRegistry { + + // map multicast group to keys + private Map> groups = null; + + MembershipRegistry() { + } + + /** + * Checks registry for membership of the group on the given + * network interface. + */ + MembershipKey checkMembership(InetAddress group, NetworkInterface interf, + InetAddress source) + { + if (groups != null) { + List keys = groups.get(group); + if (keys != null) { + for (MembershipKeyImpl key: keys) { + if (key.networkInterface().equals(interf)) { + // already a member to receive all packets so return + // existing key or detect conflict + if (source == null) { + if (key.sourceAddress() == null) + return key; + throw new IllegalStateException("Already a member to receive all packets"); + } + + // already have source-specific membership so return key + // or detect conflict + if (key.sourceAddress() == null) + throw new IllegalStateException("Already have source-specific membership"); + if (source.equals(key.sourceAddress())) + return key; + } + } + } + } + return null; + } + + /** + * Add membership to the registry, returning a new membership key. + */ + void add(MembershipKeyImpl key) { + InetAddress group = key.group(); + List keys; + if (groups == null) { + groups = new HashMap>(); + keys = null; + } else { + keys = groups.get(group); + } + if (keys == null) { + keys = new LinkedList(); + groups.put(group, keys); + } + keys.add(key); + } + + /** + * Remove a key from the registry + */ + void remove(MembershipKeyImpl key) { + InetAddress group = key.group(); + List keys = groups.get(group); + if (keys != null) { + Iterator i = keys.iterator(); + while (i.hasNext()) { + if (i.next() == key) { + i.remove(); + break; + } + } + if (keys.isEmpty()) { + groups.remove(group); + } + } + } + + /** + * Invalidate all keys in the registry + */ + void invalidateAll() { + if (groups != null) { + for (InetAddress group: groups.keySet()) { + for (MembershipKeyImpl key: groups.get(group)) { + key.invalidate(); + } + } + } + } +} diff --git a/src/sun/nio/ch/NativeDispatcher.java b/src/sun/nio/ch/NativeDispatcher.java new file mode 100644 index 00000000..502a3e01 --- /dev/null +++ b/src/sun/nio/ch/NativeDispatcher.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * Allows different platforms to call different native methods + * for read and write operations. + */ + +abstract class NativeDispatcher +{ + + abstract int read(FileDescriptor fd, long address, int len) + throws IOException; + + /** + * Returns {@code true} if pread/pwrite needs to be synchronized with + * position sensitive methods. + */ + boolean needsPositionLock() { + return false; + } + + int pread(FileDescriptor fd, long address, int len, long position) + throws IOException + { + throw new IOException("Operation Unsupported"); + } + + abstract long readv(FileDescriptor fd, long address, int len) + throws IOException; + + abstract int write(FileDescriptor fd, long address, int len) + throws IOException; + + int pwrite(FileDescriptor fd, long address, int len, long position) + throws IOException + { + throw new IOException("Operation Unsupported"); + } + + abstract long writev(FileDescriptor fd, long address, int len) + throws IOException; + + abstract void close(FileDescriptor fd) throws IOException; + + // Prepare the given fd for closing by duping it to a known internal fd + // that's already closed. This is necessary on some operating systems + // (Solaris and Linux) to prevent fd recycling. + // + void preClose(FileDescriptor fd) throws IOException { + // Do nothing by default; this is only needed on Unix + } + +} diff --git a/src/sun/nio/ch/NativeObject.java b/src/sun/nio/ch/NativeObject.java new file mode 100644 index 00000000..59fd0606 --- /dev/null +++ b/src/sun/nio/ch/NativeObject.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.ch; // Formerly in sun.misc + +import java.nio.ByteOrder; + +import sun.misc.Unsafe; + + +// ## In the fullness of time, this class will be eliminated + +/** + * Proxies for objects that reside in native memory. + */ + +class NativeObject { // package-private + + protected static final Unsafe unsafe = Unsafe.getUnsafe(); + + // Native allocation address; + // may be smaller than the base address due to page-size rounding + // + protected long allocationAddress; + + // Native base address + // + private final long address; + + /** + * Creates a new native object that is based at the given native address. + */ + NativeObject(long address) { + this.allocationAddress = address; + this.address = address; + } + + /** + * Creates a new native object allocated at the given native address but + * whose base is at the additional offset. + */ + NativeObject(long address, long offset) { + this.allocationAddress = address; + this.address = address + offset; + } + + // Invoked only by AllocatedNativeObject + // + protected NativeObject(int size, boolean pageAligned) { + if (!pageAligned) { + this.allocationAddress = unsafe.allocateMemory(size); + this.address = this.allocationAddress; + } else { + int ps = pageSize(); + long a = unsafe.allocateMemory(size + ps); + this.allocationAddress = a; + this.address = a + ps - (a & (ps - 1)); + } + } + + /** + * Returns the native base address of this native object. + * + * @return The native base address + */ + long address() { + return address; + } + + long allocationAddress() { + return allocationAddress; + } + + /** + * Creates a new native object starting at the given offset from the base + * of this native object. + * + * @param offset + * The offset from the base of this native object that is to be + * the base of the new native object + * + * @return The newly created native object + */ + NativeObject subObject(int offset) { + return new NativeObject(offset + address); + } + + /** + * Reads an address from this native object at the given offset and + * constructs a native object using that address. + * + * @param offset + * The offset of the address to be read. Note that the size of an + * address is implementation-dependent. + * + * @return The native object created using the address read from the + * given offset + */ + NativeObject getObject(int offset) { + long newAddress = 0L; + switch (addressSize()) { + case 8: + newAddress = unsafe.getLong(offset + address); + break; + case 4: + newAddress = unsafe.getInt(offset + address) & 0x00000000FFFFFFFF; + break; + default: + throw new InternalError("Address size not supported"); + } + + return new NativeObject(newAddress); + } + + /** + * Writes the base address of the given native object at the given offset + * of this native object. + * + * @param offset + * The offset at which the address is to be written. Note that the + * size of an address is implementation-dependent. + * + * @param ob + * The native object whose address is to be written + */ + void putObject(int offset, NativeObject ob) { + switch (addressSize()) { + case 8: + putLong(offset, ob.address); + break; + case 4: + putInt(offset, (int)(ob.address & 0x00000000FFFFFFFF)); + break; + default: + throw new InternalError("Address size not supported"); + } + } + + + /* -- Value accessors: No range checking! -- */ + + /** + * Reads a byte starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the byte + * + * @return The byte value read + */ + final byte getByte(int offset) { + return unsafe.getByte(offset + address); + } + + /** + * Writes a byte at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the byte + * + * @param value + * The byte value to be written + */ + final void putByte(int offset, byte value) { + unsafe.putByte(offset + address, value); + } + + /** + * Reads a short starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the short + * + * @return The short value read + */ + final short getShort(int offset) { + return unsafe.getShort(offset + address); + } + + /** + * Writes a short at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the short + * + * @param value + * The short value to be written + */ + final void putShort(int offset, short value) { + unsafe.putShort(offset + address, value); + } + + /** + * Reads a char starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the char + * + * @return The char value read + */ + final char getChar(int offset) { + return unsafe.getChar(offset + address); + } + + /** + * Writes a char at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the char + * + * @param value + * The char value to be written + */ + final void putChar(int offset, char value) { + unsafe.putChar(offset + address, value); + } + + /** + * Reads an int starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the int + * + * @return The int value read + */ + final int getInt(int offset) { + return unsafe.getInt(offset + address); + } + + /** + * Writes an int at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the int + * + * @param value + * The int value to be written + */ + final void putInt(int offset, int value) { + unsafe.putInt(offset + address, value); + } + + /** + * Reads a long starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the long + * + * @return The long value read + */ + final long getLong(int offset) { + return unsafe.getLong(offset + address); + } + + /** + * Writes a long at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the long + * + * @param value + * The long value to be written + */ + final void putLong(int offset, long value) { + unsafe.putLong(offset + address, value); + } + + /** + * Reads a float starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the float + * + * @return The float value read + */ + final float getFloat(int offset) { + return unsafe.getFloat(offset + address); + } + + /** + * Writes a float at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the float + * + * @param value + * The float value to be written + */ + final void putFloat(int offset, float value) { + unsafe.putFloat(offset + address, value); + } + + /** + * Reads a double starting at the given offset from base of this native + * object. + * + * @param offset + * The offset at which to read the double + * + * @return The double value read + */ + final double getDouble(int offset) { + return unsafe.getDouble(offset + address); + } + + /** + * Writes a double at the specified offset from this native object's + * base address. + * + * @param offset + * The offset at which to write the double + * + * @param value + * The double value to be written + */ + final void putDouble(int offset, double value) { + unsafe.putDouble(offset + address, value); + } + + /** + * Returns the native architecture's address size in bytes. + * + * @return The address size of the native architecture + */ + static int addressSize() { + return unsafe.addressSize(); + } + + // Cache for byte order + private static ByteOrder byteOrder = null; + + /** + * Returns the byte order of the underlying hardware. + * + * @return An instance of {@link ByteOrder} + */ + static ByteOrder byteOrder() { + if (byteOrder != null) + return byteOrder; + long a = unsafe.allocateMemory(8); + try { + unsafe.putLong(a, 0x0102030405060708L); + byte b = unsafe.getByte(a); + switch (b) { + case 0x01: byteOrder = ByteOrder.BIG_ENDIAN; break; + case 0x08: byteOrder = ByteOrder.LITTLE_ENDIAN; break; + default: + assert false; + } + } finally { + unsafe.freeMemory(a); + } + return byteOrder; + } + + // Cache for page size + private static int pageSize = -1; + + /** + * Returns the page size of the underlying hardware. + * + * @return The page size, in bytes + */ + static int pageSize() { + if (pageSize == -1) + pageSize = unsafe.pageSize(); + return pageSize; + } + +} diff --git a/src/sun/nio/ch/NativeThreadSet.java b/src/sun/nio/ch/NativeThreadSet.java new file mode 100644 index 00000000..5eb90af8 --- /dev/null +++ b/src/sun/nio/ch/NativeThreadSet.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + + +// Special-purpose data structure for sets of native threads + + +class NativeThreadSet { + + private long[] elts; + private int used = 0; + private boolean waitingToEmpty; + + NativeThreadSet(int n) { + elts = new long[n]; + } + + // Adds the current native thread to this set, returning its index so that + // it can efficiently be removed later. + // + int add() { + long th = NativeThread.current(); + // 0 and -1 are treated as placeholders, not real thread handles + if (th == 0) + th = -1; + synchronized (this) { + int start = 0; + if (used >= elts.length) { + int on = elts.length; + int nn = on * 2; + long[] nelts = new long[nn]; + System.arraycopy(elts, 0, nelts, 0, on); + elts = nelts; + start = on; + } + for (int i = start; i < elts.length; i++) { + if (elts[i] == 0) { + elts[i] = th; + used++; + return i; + } + } + assert false; + return -1; + } + } + + // Removes the thread at the given index. + // + void remove(int i) { + synchronized (this) { + elts[i] = 0; + used--; + if (used == 0 && waitingToEmpty) + notifyAll(); + } + } + + // Signals all threads in this set. + // + synchronized void signalAndWait() { + boolean interrupted = false; + while (used > 0) { + int u = used; + int n = elts.length; + for (int i = 0; i < n; i++) { + long th = elts[i]; + if (th == 0) + continue; + if (th != -1) + NativeThread.signal(th); + if (--u == 0) + break; + } + waitingToEmpty = true; + try { + wait(50); + } catch (InterruptedException e) { + interrupted = true; + } finally { + waitingToEmpty = false; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } +} diff --git a/src/sun/nio/ch/Net.java b/src/sun/nio/ch/Net.java new file mode 100644 index 00000000..6516809a --- /dev/null +++ b/src/sun/nio/ch/Net.java @@ -0,0 +1,655 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.net.UnknownHostException; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.UnsupportedAddressTypeException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Enumeration; + +import sun.net.ExtendedOptionsImpl; + +import jdk.net.*; + + +public class Net { + + private Net() { } + + // unspecified protocol family + static final ProtocolFamily UNSPEC = new ProtocolFamily() { + public String name() { + return "UNSPEC"; + } + }; + + // set to true if exclusive binding is on for Windows + private static final boolean exclusiveBind; + + static { + int availLevel = isExclusiveBindAvailable(); + if (availLevel >= 0) { + String exclBindProp = + AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public String run() { + return System.getProperty( + "sun.net.useExclusiveBind"); + } + }); + if (exclBindProp != null) { + exclusiveBind = exclBindProp.length() == 0 ? + true : Boolean.parseBoolean(exclBindProp); + } else if (availLevel == 1) { + exclusiveBind = true; + } else { + exclusiveBind = false; + } + } else { + exclusiveBind = false; + } + } + + // -- Miscellaneous utilities -- + + private static volatile boolean checkedIPv6 = false; + private static volatile boolean isIPv6Available; + + /** + * Tells whether dual-IPv4/IPv6 sockets should be used. + */ + static boolean isIPv6Available() { + if (!checkedIPv6) { + isIPv6Available = isIPv6Available0(); + checkedIPv6 = true; + } + return isIPv6Available; + } + + /** + * Returns true if exclusive binding is on + */ + static boolean useExclusiveBind() { + return exclusiveBind; + } + + /** + * Tells whether IPv6 sockets can join IPv4 multicast groups + */ + static boolean canIPv6SocketJoinIPv4Group() { + return canIPv6SocketJoinIPv4Group0(); + } + + /** + * Tells whether {@link #join6} can be used to join an IPv4 + * multicast group (IPv4 group as IPv4-mapped IPv6 address) + */ + static boolean canJoin6WithIPv4Group() { + return canJoin6WithIPv4Group0(); + } + + public static InetSocketAddress checkAddress(SocketAddress sa) { + if (sa == null) + throw new NullPointerException(); + if (!(sa instanceof InetSocketAddress)) + throw new UnsupportedAddressTypeException(); // ## needs arg + InetSocketAddress isa = (InetSocketAddress)sa; + if (isa.isUnresolved()) + throw new UnresolvedAddressException(); // ## needs arg + InetAddress addr = isa.getAddress(); + if (!(addr instanceof Inet4Address || addr instanceof Inet6Address)) + throw new IllegalArgumentException("Invalid address type"); + return isa; + } + + static InetSocketAddress asInetSocketAddress(SocketAddress sa) { + if (!(sa instanceof InetSocketAddress)) + throw new UnsupportedAddressTypeException(); + return (InetSocketAddress)sa; + } + + static void translateToSocketException(Exception x) + throws SocketException + { + if (x instanceof SocketException) + throw (SocketException)x; + Exception nx = x; + if (x instanceof ClosedChannelException) + nx = new SocketException("Socket is closed"); + else if (x instanceof NotYetConnectedException) + nx = new SocketException("Socket is not connected"); + else if (x instanceof AlreadyBoundException) + nx = new SocketException("Already bound"); + else if (x instanceof NotYetBoundException) + nx = new SocketException("Socket is not bound yet"); + else if (x instanceof UnsupportedAddressTypeException) + nx = new SocketException("Unsupported address type"); + else if (x instanceof UnresolvedAddressException) { + nx = new SocketException("Unresolved address"); + } + if (nx != x) + nx.initCause(x); + + if (nx instanceof SocketException) + throw (SocketException)nx; + else if (nx instanceof RuntimeException) + throw (RuntimeException)nx; + else + throw new Error("Untranslated exception", nx); + } + + static void translateException(Exception x, + boolean unknownHostForUnresolved) + throws IOException + { + if (x instanceof IOException) + throw (IOException)x; + // Throw UnknownHostException from here since it cannot + // be thrown as a SocketException + if (unknownHostForUnresolved && + (x instanceof UnresolvedAddressException)) + { + throw new UnknownHostException(); + } + translateToSocketException(x); + } + + static void translateException(Exception x) + throws IOException + { + translateException(x, false); + } + + /** + * Returns the local address after performing a SecurityManager#checkConnect. + */ + static InetSocketAddress getRevealedLocalAddress(InetSocketAddress addr) { + SecurityManager sm = System.getSecurityManager(); + if (addr == null || sm == null) + return addr; + + try{ + sm.checkConnect(addr.getAddress().getHostAddress(), -1); + // Security check passed + } catch (SecurityException e) { + // Return loopback address only if security check fails + addr = getLoopbackAddress(addr.getPort()); + } + return addr; + } + + static String getRevealedLocalAddressAsString(InetSocketAddress addr) { + return System.getSecurityManager() == null ? addr.toString() : + getLoopbackAddress(addr.getPort()).toString(); + } + + private static InetSocketAddress getLoopbackAddress(int port) { + return new InetSocketAddress(InetAddress.getLoopbackAddress(), + port); + } + + /** + * Returns any IPv4 address of the given network interface, or + * null if the interface does not have any IPv4 addresses. + */ + static Inet4Address anyInet4Address(final NetworkInterface interf) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Inet4Address run() { + Enumeration addrs = interf.getInetAddresses(); + while (addrs.hasMoreElements()) { + InetAddress addr = addrs.nextElement(); + if (addr instanceof Inet4Address) { + return (Inet4Address)addr; + } + } + return null; + } + }); + } + + /** + * Returns an IPv4 address as an int. + */ + static int inet4AsInt(InetAddress ia) { + if (ia instanceof Inet4Address) { + byte[] addr = ia.getAddress(); + int address = addr[3] & 0xFF; + address |= ((addr[2] << 8) & 0xFF00); + address |= ((addr[1] << 16) & 0xFF0000); + address |= ((addr[0] << 24) & 0xFF000000); + return address; + } + throw new AssertionError("Should not reach here"); + } + + /** + * Returns an InetAddress from the given IPv4 address + * represented as an int. + */ + static InetAddress inet4FromInt(int address) { + byte[] addr = new byte[4]; + addr[0] = (byte) ((address >>> 24) & 0xFF); + addr[1] = (byte) ((address >>> 16) & 0xFF); + addr[2] = (byte) ((address >>> 8) & 0xFF); + addr[3] = (byte) (address & 0xFF); + try { + return InetAddress.getByAddress(addr); + } catch (UnknownHostException uhe) { + throw new AssertionError("Should not reach here"); + } + } + + /** + * Returns an IPv6 address as a byte array + */ + static byte[] inet6AsByteArray(InetAddress ia) { + if (ia instanceof Inet6Address) { + return ia.getAddress(); + } + + // need to construct IPv4-mapped address + if (ia instanceof Inet4Address) { + byte[] ip4address = ia.getAddress(); + byte[] address = new byte[16]; + address[10] = (byte)0xff; + address[11] = (byte)0xff; + address[12] = ip4address[0]; + address[13] = ip4address[1]; + address[14] = ip4address[2]; + address[15] = ip4address[3]; + return address; + } + + throw new AssertionError("Should not reach here"); + } + + // -- Socket options + + static void setSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name, Object value) + throws IOException + { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + // only simple values supported by this method + Class type = name.type(); + + if (type == SocketFlow.class) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new NetworkPermission("setOption.SO_FLOW_SLA")); + } + ExtendedOptionsImpl.setFlowOption(fd, (SocketFlow)value); + return; + } + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // special handling + if (name == StandardSocketOptions.SO_RCVBUF || + name == StandardSocketOptions.SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException("Invalid send/receive buffer size"); + } + if (name == StandardSocketOptions.SO_LINGER) { + int i = ((Integer)value).intValue(); + if (i < 0) + value = Integer.valueOf(-1); + if (i > 65535) + value = Integer.valueOf(65535); + } + if (name == StandardSocketOptions.IP_TOS) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid IP_TOS value"); + } + if (name == StandardSocketOptions.IP_MULTICAST_TTL) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 255) + throw new IllegalArgumentException("Invalid TTL/hop value"); + } + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + int arg; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + boolean mayNeedConversion = (family == UNSPEC); + boolean isIPv6 = (family == StandardProtocolFamily.INET6); + setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6); + } + + static Object getSocketOption(FileDescriptor fd, ProtocolFamily family, + SocketOption name) + throws IOException + { + Class type = name.type(); + + if (type == SocketFlow.class) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new NetworkPermission("getOption.SO_FLOW_SLA")); + } + SocketFlow flow = SocketFlow.create(); + ExtendedOptionsImpl.getFlowOption(fd, flow); + return flow; + } + + // only simple values supported by this method + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + // map option name to platform level/name + OptionKey key = SocketOptionRegistry.findOption(name, family); + if (key == null) + throw new AssertionError("Option not found"); + + boolean mayNeedConversion = (family == UNSPEC); + int value = getIntOption0(fd, mayNeedConversion, key.level(), key.name()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } + + // -- Socket operations -- + + private static native boolean isIPv6Available0(); + + /* + * Returns 1 for Windows versions that support exclusive binding by default, 0 + * for those that do not, and -1 for Solaris/Linux/Mac OS + */ + private static native int isExclusiveBindAvailable(); + + private static native boolean canIPv6SocketJoinIPv4Group0(); + + private static native boolean canJoin6WithIPv4Group0(); + + static FileDescriptor socket(boolean stream) throws IOException { + return socket(UNSPEC, stream); + } + + static FileDescriptor socket(ProtocolFamily family, boolean stream) + throws IOException { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return IOUtil.newFD(socket0(preferIPv6, stream, false)); + } + + static FileDescriptor serverSocket(boolean stream) { + return IOUtil.newFD(socket0(isIPv6Available(), stream, true)); + } + + // Due to oddities SO_REUSEADDR on windows reuse is ignored + private static native int socket0(boolean preferIPv6, boolean stream, boolean reuse); + + public static void bind(FileDescriptor fd, InetAddress addr, int port) + throws IOException + { + bind(UNSPEC, fd, addr, port); + } + + static void bind(ProtocolFamily family, FileDescriptor fd, + InetAddress addr, int port) throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + bind0(fd, preferIPv6, exclusiveBind, addr, port); + } + + private static native void bind0(FileDescriptor fd, boolean preferIPv6, + boolean useExclBind, InetAddress addr, + int port) + throws IOException; + + static native void listen(FileDescriptor fd, int backlog) throws IOException; + + static int connect(FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + return connect(UNSPEC, fd, remote, remotePort); + } + + static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) + throws IOException + { + boolean preferIPv6 = isIPv6Available() && + (family != StandardProtocolFamily.INET); + return connect0(preferIPv6, fd, remote, remotePort); + } + + private static native int connect0(boolean preferIPv6, + FileDescriptor fd, + InetAddress remote, + int remotePort) + throws IOException; + + + public final static int SHUT_RD = 0; + public final static int SHUT_WR = 1; + public final static int SHUT_RDWR = 2; + + static native void shutdown(FileDescriptor fd, int how) throws IOException; + + private static native int localPort(FileDescriptor fd) + throws IOException; + + private static native InetAddress localInetAddress(FileDescriptor fd) + throws IOException; + + public static InetSocketAddress localAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(localInetAddress(fd), localPort(fd)); + } + + private static native int remotePort(FileDescriptor fd) + throws IOException; + + private static native InetAddress remoteInetAddress(FileDescriptor fd) + throws IOException; + + static InetSocketAddress remoteAddress(FileDescriptor fd) + throws IOException + { + return new InetSocketAddress(remoteInetAddress(fd), remotePort(fd)); + } + + private static native int getIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt) + throws IOException; + + private static native void setIntOption0(FileDescriptor fd, boolean mayNeedConversion, + int level, int opt, int arg, boolean isIPv6) + throws IOException; + + static native int poll(FileDescriptor fd, int events, long timeout) + throws IOException; + + // -- Multicast support -- + + + /** + * Join IPv4 multicast group + */ + static int join4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return joinOrDrop4(true, fd, group, interf, source); + } + + /** + * Drop membership of IPv4 multicast group + */ + static void drop4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + joinOrDrop4(false, fd, group, interf, source); + } + + private static native int joinOrDrop4(boolean join, FileDescriptor fd, int group, int interf, int source) + throws IOException; + + /** + * Block IPv4 source + */ + static int block4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + return blockOrUnblock4(true, fd, group, interf, source); + } + + /** + * Unblock IPv6 source + */ + static void unblock4(FileDescriptor fd, int group, int interf, int source) + throws IOException + { + blockOrUnblock4(false, fd, group, interf, source); + } + + private static native int blockOrUnblock4(boolean block, FileDescriptor fd, int group, + int interf, int source) + throws IOException; + + /** + * Join IPv6 multicast group + */ + static int join6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + return joinOrDrop6(true, fd, group, index, source); + } + + /** + * Drop membership of IPv6 multicast group + */ + static void drop6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + joinOrDrop6(false, fd, group, index, source); + } + + private static native int joinOrDrop6(boolean join, FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException; + + /** + * Block IPv6 source + */ + static int block6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + return blockOrUnblock6(true, fd, group, index, source); + } + + /** + * Unblock IPv6 source + */ + static void unblock6(FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException + { + blockOrUnblock6(false, fd, group, index, source); + } + + static native int blockOrUnblock6(boolean block, FileDescriptor fd, byte[] group, int index, byte[] source) + throws IOException; + + static native void setInterface4(FileDescriptor fd, int interf) throws IOException; + + static native int getInterface4(FileDescriptor fd) throws IOException; + + static native void setInterface6(FileDescriptor fd, int index) throws IOException; + + static native int getInterface6(FileDescriptor fd) throws IOException; + + private static native void initIDs(); + + /** + * Event masks for the various poll system calls. + * They will be set platform dependant in the static initializer below. + */ + public static final short POLLIN; + public static final short POLLOUT; + public static final short POLLERR; + public static final short POLLHUP; + public static final short POLLNVAL; + public static final short POLLCONN; + + static native short pollinValue(); + static native short polloutValue(); + static native short pollerrValue(); + static native short pollhupValue(); + static native short pollnvalValue(); + static native short pollconnValue(); + + static { + IOUtil.load(); + initIDs(); + + POLLIN = pollinValue(); + POLLOUT = polloutValue(); + POLLERR = pollerrValue(); + POLLHUP = pollhupValue(); + POLLNVAL = pollnvalValue(); + POLLCONN = pollconnValue(); + } + +} diff --git a/src/sun/nio/ch/OptionKey.java b/src/sun/nio/ch/OptionKey.java new file mode 100644 index 00000000..ebb272ae --- /dev/null +++ b/src/sun/nio/ch/OptionKey.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +/** + * Represents the level/name of a socket option + */ + +class OptionKey { + private int level; + private int name; + + OptionKey(int level, int name) { + this.level = level; + this.name = name; + } + + int level() { + return level; + } + + int name() { + return name; + } +} diff --git a/src/sun/nio/ch/PendingFuture.java b/src/sun/nio/ch/PendingFuture.java new file mode 100644 index 00000000..62b4c116 --- /dev/null +++ b/src/sun/nio/ch/PendingFuture.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.AsynchronousChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A Future for a pending I/O operation. A PendingFuture allows for the + * attachment of an additional arbitrary context object and a timer task. + */ + +final class PendingFuture implements Future { + private static final CancellationException CANCELLED = + new CancellationException(); + + private final AsynchronousChannel channel; + private final CompletionHandler handler; + private final A attachment; + + // true if result (or exception) is available + private volatile boolean haveResult; + private volatile V result; + private volatile Throwable exc; + + // latch for waiting (created lazily if needed) + private CountDownLatch latch; + + // optional timer task that is cancelled when result becomes available + private Future timeoutTask; + + // optional context object + private volatile Object context; + + PendingFuture(AsynchronousChannel channel, + CompletionHandler handler, + A attachment, + Object context) + { + this.channel = channel; + this.handler = handler; + this.attachment = attachment; + this.context = context; + } + + PendingFuture(AsynchronousChannel channel, + CompletionHandler handler, + A attachment) + { + this.channel = channel; + this.handler = handler; + this.attachment = attachment; + } + + PendingFuture(AsynchronousChannel channel) { + this(channel, null, null); + } + + PendingFuture(AsynchronousChannel channel, Object context) { + this(channel, null, null, context); + } + + AsynchronousChannel channel() { + return channel; + } + + CompletionHandler handler() { + return handler; + } + + A attachment() { + return attachment; + } + + void setContext(Object context) { + this.context = context; + } + + Object getContext() { + return context; + } + + void setTimeoutTask(Future task) { + synchronized (this) { + if (haveResult) { + task.cancel(false); + } else { + this.timeoutTask = task; + } + } + } + + // creates latch if required; return true if caller needs to wait + private boolean prepareForWait() { + synchronized (this) { + if (haveResult) { + return false; + } else { + if (latch == null) + latch = new CountDownLatch(1); + return true; + } + } + } + + /** + * Sets the result, or a no-op if the result or exception is already set. + */ + void setResult(V res) { + synchronized (this) { + if (haveResult) + return; + result = res; + haveResult = true; + if (timeoutTask != null) + timeoutTask.cancel(false); + if (latch != null) + latch.countDown(); + } + } + + /** + * Sets the result, or a no-op if the result or exception is already set. + */ + void setFailure(Throwable x) { + if (!(x instanceof IOException) && !(x instanceof SecurityException)) + x = new IOException(x); + synchronized (this) { + if (haveResult) + return; + exc = x; + haveResult = true; + if (timeoutTask != null) + timeoutTask.cancel(false); + if (latch != null) + latch.countDown(); + } + } + + /** + * Sets the result + */ + void setResult(V res, Throwable x) { + if (x == null) { + setResult(res); + } else { + setFailure(x); + } + } + + @Override + public V get() throws ExecutionException, InterruptedException { + if (!haveResult) { + boolean needToWait = prepareForWait(); + if (needToWait) + latch.await(); + } + if (exc != null) { + if (exc == CANCELLED) + throw new CancellationException(); + throw new ExecutionException(exc); + } + return result; + } + + @Override + public V get(long timeout, TimeUnit unit) + throws ExecutionException, InterruptedException, TimeoutException + { + if (!haveResult) { + boolean needToWait = prepareForWait(); + if (needToWait) + if (!latch.await(timeout, unit)) throw new TimeoutException(); + } + if (exc != null) { + if (exc == CANCELLED) + throw new CancellationException(); + throw new ExecutionException(exc); + } + return result; + } + + Throwable exception() { + return (exc != CANCELLED) ? exc : null; + } + + V value() { + return result; + } + + @Override + public boolean isCancelled() { + return (exc == CANCELLED); + } + + @Override + public boolean isDone() { + return haveResult; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + synchronized (this) { + if (haveResult) + return false; // already completed + + // notify channel + if (channel() instanceof Cancellable) + ((Cancellable)channel()).onCancel(this); + + // set result and cancel timer + exc = CANCELLED; + haveResult = true; + if (timeoutTask != null) + timeoutTask.cancel(false); + } + + // close channel if forceful cancel + if (mayInterruptIfRunning) { + try { + channel().close(); + } catch (IOException ignore) { } + } + + // release waiters + if (latch != null) + latch.countDown(); + return true; + } +} diff --git a/src/sun/nio/ch/PollSelectorProvider.java b/src/sun/nio/ch/PollSelectorProvider.java new file mode 100644 index 00000000..93cfb321 --- /dev/null +++ b/src/sun/nio/ch/PollSelectorProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.nio.channels.Channel; +import java.nio.channels.spi.AbstractSelector; + +public class PollSelectorProvider + extends SelectorProviderImpl +{ + public AbstractSelector openSelector() throws IOException { + return new PollSelectorImpl(this); + } + + public Channel inheritedChannel() throws IOException { + return InheritedChannel.getChannel(); + } +} diff --git a/src/sun/nio/ch/Reflect.java b/src/sun/nio/ch/Reflect.java new file mode 100644 index 00000000..1b60658f --- /dev/null +++ b/src/sun/nio/ch/Reflect.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + + +class Reflect { // package-private + + private Reflect() { } + + private static class ReflectionError extends Error { + private static final long serialVersionUID = -8659519328078164097L; + ReflectionError(Throwable x) { + super(x); + } + } + + private static void setAccessible(final AccessibleObject ao) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + ao.setAccessible(true); + return null; + }}); + } + + static Constructor lookupConstructor(String className, + Class[] paramTypes) + { + try { + Class cl = Class.forName(className); + Constructor c = cl.getDeclaredConstructor(paramTypes); + setAccessible(c); + return c; + } catch (ClassNotFoundException | NoSuchMethodException x) { + throw new ReflectionError(x); + } + } + + static Object invoke(Constructor c, Object[] args) { + try { + return c.newInstance(args); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException x) { + throw new ReflectionError(x); + } + } + + static Method lookupMethod(String className, + String methodName, + Class... paramTypes) + { + try { + Class cl = Class.forName(className); + Method m = cl.getDeclaredMethod(methodName, paramTypes); + setAccessible(m); + return m; + } catch (ClassNotFoundException | NoSuchMethodException x) { + throw new ReflectionError(x); + } + } + + static Object invoke(Method m, Object ob, Object[] args) { + try { + return m.invoke(ob, args); + } catch (IllegalAccessException | InvocationTargetException x) { + throw new ReflectionError(x); + } + } + + static Object invokeIO(Method m, Object ob, Object[] args) + throws IOException + { + try { + return m.invoke(ob, args); + } catch (IllegalAccessException x) { + throw new ReflectionError(x); + } catch (InvocationTargetException x) { + if (IOException.class.isInstance(x.getCause())) + throw (IOException)x.getCause(); + throw new ReflectionError(x); + } + } + + static Field lookupField(String className, String fieldName) { + try { + Class cl = Class.forName(className); + Field f = cl.getDeclaredField(fieldName); + setAccessible(f); + return f; + } catch (ClassNotFoundException | NoSuchFieldException x) { + throw new ReflectionError(x); + } + } + + static Object get(Object ob, Field f) { + try { + return f.get(ob); + } catch (IllegalAccessException x) { + throw new ReflectionError(x); + } + } + + static Object get(Field f) { + return get(null, f); + } + + static void set(Object ob, Field f, Object val) { + try { + f.set(ob, val); + } catch (IllegalAccessException x) { + throw new ReflectionError(x); + } + } + + static void setInt(Object ob, Field f, int val) { + try { + f.setInt(ob, val); + } catch (IllegalAccessException x) { + throw new ReflectionError(x); + } + } + + static void setBoolean(Object ob, Field f, boolean val) { + try { + f.setBoolean(ob, val); + } catch (IllegalAccessException x) { + throw new ReflectionError(x); + } + } + +} diff --git a/src/sun/nio/ch/Secrets.java b/src/sun/nio/ch/Secrets.java new file mode 100644 index 00000000..ef7b7314 --- /dev/null +++ b/src/sun/nio/ch/Secrets.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; + +/** + * Provides access to implementation private constructors and methods. + */ + +public final class Secrets { + private Secrets() { } + + private static SelectorProvider provider() { + SelectorProvider p = SelectorProvider.provider(); + if (!(p instanceof SelectorProviderImpl)) + throw new UnsupportedOperationException(); + return p; + } + + public static SocketChannel newSocketChannel(FileDescriptor fd) { + try { + return new SocketChannelImpl(provider(), fd, false); + } catch (IOException ioe) { + throw new AssertionError(ioe); + } + } + + public static ServerSocketChannel newServerSocketChannel(FileDescriptor fd) { + try { + return new ServerSocketChannelImpl(provider(), fd, false); + } catch (IOException ioe) { + throw new AssertionError(ioe); + } + } +} diff --git a/src/sun/nio/ch/SelChImpl.java b/src/sun/nio/ch/SelChImpl.java new file mode 100644 index 00000000..8a8da332 --- /dev/null +++ b/src/sun/nio/ch/SelChImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.channels.Channel; + + +/** + * An interface that allows translation (and more!). + * + * @since 1.4 + */ + +public interface SelChImpl extends Channel { + + FileDescriptor getFD(); + + int getFDVal(); + + /** + * Adds the specified ops if present in interestOps. The specified + * ops are turned on without affecting the other ops. + * + * @return true iff the new value of sk.readyOps() set by this method + * contains at least one bit that the previous value did not + * contain + */ + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk); + + /** + * Sets the specified ops if present in interestOps. The specified + * ops are turned on, and all other ops are turned off. + * + * @return true iff the new value of sk.readyOps() set by this method + * contains at least one bit that the previous value did not + * contain + */ + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk); + + void translateAndSetInterestOps(int ops, SelectionKeyImpl sk); + + int validOps(); + + void kill() throws IOException; + +} diff --git a/src/sun/nio/ch/SelectionKeyImpl.java b/src/sun/nio/ch/SelectionKeyImpl.java new file mode 100644 index 00000000..da431610 --- /dev/null +++ b/src/sun/nio/ch/SelectionKeyImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.nio.channels.CancelledKeyException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectionKey; + + +/** + * An implementation of SelectionKey for Solaris. + */ + +public class SelectionKeyImpl + extends AbstractSelectionKey +{ + + final SelChImpl channel; // package-private + public final SelectorImpl selector; + + // Index for a pollfd array in Selector that this key is registered with + private int index; + + private volatile int interestOps; + private int readyOps; + + SelectionKeyImpl(SelChImpl ch, SelectorImpl sel) { + channel = ch; + selector = sel; + } + + public SelectableChannel channel() { + return (SelectableChannel)channel; + } + + public Selector selector() { + return selector; + } + + int getIndex() { // package-private + return index; + } + + void setIndex(int i) { // package-private + index = i; + } + + private void ensureValid() { + if (!isValid()) + throw new CancelledKeyException(); + } + + public int interestOps() { + ensureValid(); + return interestOps; + } + + public SelectionKey interestOps(int ops) { + ensureValid(); + return nioInterestOps(ops); + } + + public int readyOps() { + ensureValid(); + return readyOps; + } + + // The nio versions of these operations do not care if a key + // has been invalidated. They are for internal use by nio code. + + public void nioReadyOps(int ops) { + readyOps = ops; + } + + public int nioReadyOps() { + return readyOps; + } + + public SelectionKey nioInterestOps(int ops) { + if ((ops & ~channel().validOps()) != 0) + throw new IllegalArgumentException(); + channel.translateAndSetInterestOps(ops, this); + interestOps = ops; + return this; + } + + public int nioInterestOps() { + return interestOps; + } + +} diff --git a/src/sun/nio/ch/SelectorImpl.java b/src/sun/nio/ch/SelectorImpl.java new file mode 100644 index 00000000..f97b2812 --- /dev/null +++ b/src/sun/nio/ch/SelectorImpl.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.SocketException; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.IllegalSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + + +/** + * Base Selector implementation class. + */ + +public abstract class SelectorImpl + extends AbstractSelector +{ + + // The set of keys with data ready for an operation + protected Set selectedKeys; + + // The set of keys registered with this Selector + protected HashSet keys; + + // Public views of the key sets + private Set publicKeys; // Immutable + private Set publicSelectedKeys; // Removal allowed, but not addition + + protected SelectorImpl(SelectorProvider sp) { + super(sp); + keys = new HashSet(); + selectedKeys = new HashSet(); + if (Util.atBugLevel("1.4")) { + publicKeys = keys; + publicSelectedKeys = selectedKeys; + } else { + publicKeys = Collections.unmodifiableSet(keys); + publicSelectedKeys = Util.ungrowableSet(selectedKeys); + } + } + + public Set keys() { + if (!isOpen() && !Util.atBugLevel("1.4")) + throw new ClosedSelectorException(); + return publicKeys; + } + + public Set selectedKeys() { + if (!isOpen() && !Util.atBugLevel("1.4")) + throw new ClosedSelectorException(); + return publicSelectedKeys; + } + + protected abstract int doSelect(long timeout) throws IOException; + + private int lockAndDoSelect(long timeout) throws IOException { + synchronized (this) { + if (!isOpen()) + throw new ClosedSelectorException(); + synchronized (publicKeys) { + synchronized (publicSelectedKeys) { + return doSelect(timeout); + } + } + } + } + + public int select(long timeout) + throws IOException + { + if (timeout < 0) + throw new IllegalArgumentException("Negative timeout"); + return lockAndDoSelect((timeout == 0) ? -1 : timeout); + } + + public int select() throws IOException { + return select(0); + } + + public int selectNow() throws IOException { + return lockAndDoSelect(0); + } + + public void implCloseSelector() throws IOException { + wakeup(); + synchronized (this) { + synchronized (publicKeys) { + synchronized (publicSelectedKeys) { + implClose(); + } + } + } + } + + protected abstract void implClose() throws IOException; + + public void putEventOps(SelectionKeyImpl sk, int ops) { } + + protected final SelectionKey register(AbstractSelectableChannel ch, + int ops, + Object attachment) + { + if (!(ch instanceof SelChImpl)) + throw new IllegalSelectorException(); + SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this); + k.attach(attachment); + synchronized (publicKeys) { + implRegister(k); + } + k.interestOps(ops); + return k; + } + + protected abstract void implRegister(SelectionKeyImpl ski); + + void processDeregisterQueue() throws IOException { + // Precondition: Synchronized on this, keys, and selectedKeys + Set cks = cancelledKeys(); + synchronized (cks) { + if (!cks.isEmpty()) { + Iterator i = cks.iterator(); + while (i.hasNext()) { + SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); + try { + implDereg(ski); + } catch (SocketException se) { + throw new IOException("Error deregistering key", se); + } finally { + i.remove(); + } + } + } + } + } + + protected abstract void implDereg(SelectionKeyImpl ski) throws IOException; + + abstract public Selector wakeup(); + +} diff --git a/src/sun/nio/ch/SelectorProviderImpl.java b/src/sun/nio/ch/SelectorProviderImpl.java new file mode 100644 index 00000000..ef0a4014 --- /dev/null +++ b/src/sun/nio/ch/SelectorProviderImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.ProtocolFamily; +import java.nio.channels.DatagramChannel; +import java.nio.channels.Pipe; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.AbstractSelector; +import java.nio.channels.spi.SelectorProvider; + + +public abstract class SelectorProviderImpl + extends SelectorProvider +{ + + public DatagramChannel openDatagramChannel() throws IOException { + return new DatagramChannelImpl(this); + } + + public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { + return new DatagramChannelImpl(this, family); + } + + public Pipe openPipe() throws IOException { + return new PipeImpl(this); + } + + public abstract AbstractSelector openSelector() throws IOException; + + public ServerSocketChannel openServerSocketChannel() throws IOException { + return new ServerSocketChannelImpl(this); + } + + public SocketChannel openSocketChannel() throws IOException { + return new SocketChannelImpl(this); + } +} diff --git a/src/sun/nio/ch/ServerSocketAdaptor.java b/src/sun/nio/ch/ServerSocketAdaptor.java new file mode 100644 index 00000000..b21ab3ad --- /dev/null +++ b/src/sun/nio/ch/ServerSocketAdaptor.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + + +// Make a server-socket channel look like a server socket. +// +// The methods in this class are defined in exactly the same order as in +// java.net.ServerSocket so as to simplify tracking future changes to that +// class. +// + +public class ServerSocketAdaptor // package-private + extends ServerSocket +{ + + // The channel being adapted + private final ServerSocketChannelImpl ssc; + + // Timeout "option" value for accepts + private volatile int timeout = 0; + + public static ServerSocket create(ServerSocketChannelImpl ssc) { + try { + return new ServerSocketAdaptor(ssc); + } catch (IOException x) { + throw new Error(x); + } + } + + // ## super will create a useless impl + private ServerSocketAdaptor(ServerSocketChannelImpl ssc) + throws IOException + { + this.ssc = ssc; + } + + + public void bind(SocketAddress local) throws IOException { + bind(local, 50); + } + + public void bind(SocketAddress local, int backlog) throws IOException { + if (local == null) + local = new InetSocketAddress(0); + try { + ssc.bind(local, backlog); + } catch (Exception x) { + Net.translateException(x); + } + } + + public InetAddress getInetAddress() { + if (!ssc.isBound()) + return null; + return Net.getRevealedLocalAddress(ssc.localAddress()).getAddress(); + + } + + public int getLocalPort() { + if (!ssc.isBound()) + return -1; + return Net.asInetSocketAddress(ssc.localAddress()).getPort(); + } + + + public Socket accept() throws IOException { + synchronized (ssc.blockingLock()) { + if (!ssc.isBound()) + throw new IllegalBlockingModeException(); + try { + if (timeout == 0) { + SocketChannel sc = ssc.accept(); + if (sc == null && !ssc.isBlocking()) + throw new IllegalBlockingModeException(); + return sc.socket(); + } + + ssc.configureBlocking(false); + try { + SocketChannel sc; + if ((sc = ssc.accept()) != null) + return sc.socket(); + long to = timeout; + for (;;) { + if (!ssc.isOpen()) + throw new ClosedChannelException(); + long st = System.currentTimeMillis(); + int result = ssc.poll(Net.POLLIN, to); + if (result > 0 && ((sc = ssc.accept()) != null)) + return sc.socket(); + to -= System.currentTimeMillis() - st; + if (to <= 0) + throw new SocketTimeoutException(); + } + } finally { + if (ssc.isOpen()) + ssc.configureBlocking(true); + } + + } catch (Exception x) { + Net.translateException(x); + assert false; + return null; // Never happens + } + } + } + + public void close() throws IOException { + ssc.close(); + } + + public ServerSocketChannel getChannel() { + return ssc; + } + + public boolean isBound() { + return ssc.isBound(); + } + + public boolean isClosed() { + return !ssc.isOpen(); + } + + public void setSoTimeout(int timeout) throws SocketException { + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setReuseAddress(boolean on) throws SocketException { + try { + ssc.setOption(StandardSocketOptions.SO_REUSEADDR, on); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + public boolean getReuseAddress() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_REUSEADDR).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // Never happens + } + } + + public String toString() { + if (!isBound()) + return "ServerSocket[unbound]"; + return "ServerSocket[addr=" + getInetAddress() + + // ",port=" + getPort() + + ",localport=" + getLocalPort() + "]"; + } + + public void setReceiveBufferSize(int size) throws SocketException { + // size 0 valid for ServerSocketChannel, invalid for ServerSocket + if (size <= 0) + throw new IllegalArgumentException("size cannot be 0 or negative"); + try { + ssc.setOption(StandardSocketOptions.SO_RCVBUF, size); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + public int getReceiveBufferSize() throws SocketException { + try { + return ssc.getOption(StandardSocketOptions.SO_RCVBUF).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // Never happens + } + } + +} diff --git a/src/sun/nio/ch/ServerSocketChannelImpl.java b/src/sun/nio/ch/ServerSocketChannelImpl.java new file mode 100644 index 00000000..7aeb7242 --- /dev/null +++ b/src/sun/nio/ch/ServerSocketChannelImpl.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.ServerSocket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.SelectionKey; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import sun.net.NetHooks; + + +/** + * An implementation of ServerSocketChannels + */ + +class ServerSocketChannelImpl + extends ServerSocketChannel + implements SelChImpl +{ + + // Used to make native close and configure calls + private static NativeDispatcher nd; + + // Our file descriptor + private final FileDescriptor fd; + + // fd value needed for dev/poll. This value will remain valid + // even after the value in the file descriptor object has been set to -1 + private int fdVal; + + // ID of native thread currently blocked in this channel, for signalling + private volatile long thread = 0; + + // Lock held by thread currently blocked in this channel + private final Object lock = new Object(); + + // Lock held by any thread that modifies the state fields declared below + // DO NOT invoke a blocking I/O operation while holding this lock! + private final Object stateLock = new Object(); + + // -- The following fields are protected by stateLock + + // Channel state, increases monotonically + private static final int ST_UNINITIALIZED = -1; + private static final int ST_INUSE = 0; + private static final int ST_KILLED = 1; + private int state = ST_UNINITIALIZED; + + // Binding + private InetSocketAddress localAddress; // null => unbound + + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + + // Our socket adaptor, if any + ServerSocket socket; + + // -- End of fields protected by stateLock + + + ServerSocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = Net.serverSocket(true); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_INUSE; + } + + ServerSocketChannelImpl(SelectorProvider sp, + FileDescriptor fd, + boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_INUSE; + if (bound) + localAddress = Net.localAddress(fd); + } + + public ServerSocket socket() { + synchronized (stateLock) { + if (socket == null) + socket = ServerSocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + return localAddress == null ? localAddress + : Net.getRevealedLocalAddress( + Net.asInetSocketAddress(localAddress)); + } + } + + @Override + public ServerSocketChannel setOption(SocketOption name, T value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + if (name == StandardSocketOptions.IP_TOS) { + ProtocolFamily family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + Net.setSocketOption(fd, family, name, value); + return this; + } + + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + } else { + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + } + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(2); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.IP_TOS); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + public boolean isBound() { + synchronized (stateLock) { + return localAddress != null; + } + } + + public InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + @Override + public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException { + synchronized (lock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (isBound()) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) : + Net.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + Net.listen(fd, backlog < 1 ? 50 : backlog); + synchronized (stateLock) { + localAddress = Net.localAddress(fd); + } + } + return this; + } + + public SocketChannel accept() throws IOException { + synchronized (lock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + SocketChannel sc = null; + + int n = 0; + FileDescriptor newfd = new FileDescriptor(); + InetSocketAddress[] isaa = new InetSocketAddress[1]; + + try { + begin(); + if (!isOpen()) + return null; + thread = NativeThread.current(); + for (;;) { + n = accept(this.fd, newfd, isaa); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + break; + } + } finally { + thread = 0; + end(n > 0); + assert IOStatus.check(n); + } + + if (n < 1) + return null; + + IOUtil.configureBlocking(newfd, true); + InetSocketAddress isa = isaa[0]; + sc = new SocketChannelImpl(provider(), newfd, isa); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkAccept(isa.getAddress().getHostAddress(), + isa.getPort()); + } catch (SecurityException x) { + sc.close(); + throw x; + } + } + return sc; + + } + } + + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + protected void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + if (state != ST_KILLED) + nd.preClose(fd); + long th = thread; + if (th != 0) + NativeThread.signal(th); + if (!isRegistered()) + kill(); + } + } + + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLED) + return; + if (state == ST_UNINITIALIZED) { + state = ST_KILLED; + return; + } + assert !isOpen() && !isRegistered(); + nd.close(fd); + state = ST_KILLED; + } + } + + /** + * Translates native poll revent set into a ready operation set + */ + public boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) { + // This should only happen if this channel is pre-closed while a + // selection operation is in progress + // ## Throw an error if this channel has not been pre-closed + return false; + } + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_ACCEPT) != 0)) + newOps |= SelectionKey.OP_ACCEPT; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (lock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + thread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + thread = 0; + end(n > 0); + } + return n; + } + } + + /** + * Translates an interest operation set into a native poll event set + */ + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + + // Translate ops + if ((ops & SelectionKey.OP_ACCEPT) != 0) + newOps |= Net.POLLIN; + // Place ops into pollfd array + sk.selector.putEventOps(sk, newOps); + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(this.getClass().getName()); + sb.append('['); + if (!isOpen()) { + sb.append("closed"); + } else { + synchronized (stateLock) { + InetSocketAddress addr = localAddress(); + if (addr == null) { + sb.append("unbound"); + } else { + sb.append(Net.getRevealedLocalAddressAsString(addr)); + } + } + } + sb.append(']'); + return sb.toString(); + } + + /** + * Accept a connection on a socket. + * + * @implNote Wrap native call to allow instrumentation. + */ + private int accept(FileDescriptor ssfd, FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException + { + return accept0(ssfd, newfd, isaa); + } + + // -- Native methods -- + + // Accepts a new connection, setting the given file descriptor to refer to + // the new socket and setting isaa[0] to the socket's remote address. + // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no + // connections are pending) or IOStatus.INTERRUPTED. + // + private native int accept0(FileDescriptor ssfd, FileDescriptor newfd, + InetSocketAddress[] isaa) + throws IOException; + + private static native void initIDs(); + + static { + IOUtil.load(); + initIDs(); + nd = new SocketDispatcher(); + } + +} diff --git a/src/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java b/src/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java new file mode 100644 index 00000000..9b586c6b --- /dev/null +++ b/src/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.AsynchronousFileChannel; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.CompletionHandler; +import java.nio.channels.FileLock; +import java.nio.channels.NonReadableChannelException; +import java.nio.channels.NonWritableChannelException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +/** + * "Portable" implementation of AsynchronousFileChannel for use on operating + * systems that don't support asynchronous file I/O. + */ + +public class SimpleAsynchronousFileChannelImpl + extends AsynchronousFileChannelImpl +{ + // lazy initialization of default thread pool for file I/O + private static class DefaultExecutorHolder { + static final ExecutorService defaultExecutor = + ThreadPool.createDefault().executor(); + } + + // Used to make native read and write calls + private static final FileDispatcher nd = new FileDispatcherImpl(); + + // Thread-safe set of IDs of native threads, for signalling + private final NativeThreadSet threads = new NativeThreadSet(2); + + + SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj, + boolean reading, + boolean writing, + ExecutorService executor) + { + super(fdObj, reading, writing, executor); + } + + public static AsynchronousFileChannel open(FileDescriptor fdo, + boolean reading, + boolean writing, + ThreadPool pool) + { + // Executor is either default or based on pool parameters + ExecutorService executor = (pool == null) ? + DefaultExecutorHolder.defaultExecutor : pool.executor(); + return new SimpleAsynchronousFileChannelImpl(fdo, reading, writing, executor); + } + + @Override + public void close() throws IOException { + // mark channel as closed + synchronized (fdObj) { + if (closed) + return; // already closed + closed = true; + // from this point on, if another thread invokes the begin() method + // then it will throw ClosedChannelException + } + + // Invalidate and release any locks that we still hold + invalidateAllLocks(); + + // signal any threads blocked on this channel + threads.signalAndWait(); + + // wait until all async I/O operations have completely gracefully + closeLock.writeLock().lock(); + try { + // do nothing + } finally { + closeLock.writeLock().unlock(); + } + + // close file + nd.close(fdObj); + } + + @Override + public long size() throws IOException { + int ti = threads.add(); + try { + long n = 0L; + try { + begin(); + do { + n = nd.size(fdObj); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + return n; + } finally { + end(n >= 0L); + } + } finally { + threads.remove(ti); + } + } + + @Override + public AsynchronousFileChannel truncate(long size) throws IOException { + if (size < 0L) + throw new IllegalArgumentException("Negative size"); + if (!writing) + throw new NonWritableChannelException(); + int ti = threads.add(); + try { + long n = 0L; + try { + begin(); + do { + n = nd.size(fdObj); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + // truncate file if 'size' less than current size + if (size < n && isOpen()) { + do { + n = nd.truncate(fdObj, size); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + } + return this; + } finally { + end(n > 0); + } + } finally { + threads.remove(ti); + } + } + + @Override + public void force(boolean metaData) throws IOException { + int ti = threads.add(); + try { + int n = 0; + try { + begin(); + do { + n = nd.force(fdObj, metaData); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + } finally { + end(n >= 0); + } + } finally { + threads.remove(ti); + } + } + + @Override + Future implLock(final long position, + final long size, + final boolean shared, + final A attachment, + final CompletionHandler handler) + { + if (shared && !reading) + throw new NonReadableChannelException(); + if (!shared && !writing) + throw new NonWritableChannelException(); + + // add to lock table + final FileLockImpl fli = addToFileLockTable(position, size, shared); + if (fli == null) { + Throwable exc = new ClosedChannelException(); + if (handler == null) + return CompletedFuture.withFailure(exc); + Invoker.invokeIndirectly(handler, attachment, null, exc, executor); + return null; + } + + final PendingFuture result = (handler == null) ? + new PendingFuture(this) : null; + Runnable task = new Runnable() { + public void run() { + Throwable exc = null; + + int ti = threads.add(); + try { + int n; + try { + begin(); + do { + n = nd.lock(fdObj, true, position, size, shared); + } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); + if (n != FileDispatcher.LOCKED || !isOpen()) { + throw new AsynchronousCloseException(); + } + } catch (IOException x) { + removeFromFileLockTable(fli); + if (!isOpen()) + x = new AsynchronousCloseException(); + exc = x; + } finally { + end(); + } + } finally { + threads.remove(ti); + } + if (handler == null) { + result.setResult(fli, exc); + } else { + Invoker.invokeUnchecked(handler, attachment, fli, exc); + } + } + }; + boolean executed = false; + try { + executor.execute(task); + executed = true; + } finally { + if (!executed) { + // rollback + removeFromFileLockTable(fli); + } + } + return result; + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) + throws IOException + { + if (shared && !reading) + throw new NonReadableChannelException(); + if (!shared && !writing) + throw new NonWritableChannelException(); + + // add to lock table + FileLockImpl fli = addToFileLockTable(position, size, shared); + if (fli == null) + throw new ClosedChannelException(); + + int ti = threads.add(); + boolean gotLock = false; + try { + begin(); + int n; + do { + n = nd.lock(fdObj, false, position, size, shared); + } while ((n == FileDispatcher.INTERRUPTED) && isOpen()); + if (n == FileDispatcher.LOCKED && isOpen()) { + gotLock = true; + return fli; // lock acquired + } + if (n == FileDispatcher.NO_LOCK) + return null; // locked by someone else + if (n == FileDispatcher.INTERRUPTED) + throw new AsynchronousCloseException(); + // should not get here + throw new AssertionError(); + } finally { + if (!gotLock) + removeFromFileLockTable(fli); + end(); + threads.remove(ti); + } + } + + @Override + protected void implRelease(FileLockImpl fli) throws IOException { + nd.release(fdObj, fli.position(), fli.size()); + } + + @Override + Future implRead(final ByteBuffer dst, + final long position, + final A attachment, + final CompletionHandler handler) + { + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (!reading) + throw new NonReadableChannelException(); + if (dst.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + + // complete immediately if channel closed or no space remaining + if (!isOpen() || (dst.remaining() == 0)) { + Throwable exc = (isOpen()) ? null : new ClosedChannelException(); + if (handler == null) + return CompletedFuture.withResult(0, exc); + Invoker.invokeIndirectly(handler, attachment, 0, exc, executor); + return null; + } + + final PendingFuture result = (handler == null) ? + new PendingFuture(this) : null; + Runnable task = new Runnable() { + public void run() { + int n = 0; + Throwable exc = null; + + int ti = threads.add(); + try { + begin(); + do { + n = IOUtil.read(fdObj, dst, position, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + if (n < 0 && !isOpen()) + throw new AsynchronousCloseException(); + } catch (IOException x) { + if (!isOpen()) + x = new AsynchronousCloseException(); + exc = x; + } finally { + end(); + threads.remove(ti); + } + if (handler == null) { + result.setResult(n, exc); + } else { + Invoker.invokeUnchecked(handler, attachment, n, exc); + } + } + }; + executor.execute(task); + return result; + } + + @Override + Future implWrite(final ByteBuffer src, + final long position, + final A attachment, + final CompletionHandler handler) + { + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (!writing) + throw new NonWritableChannelException(); + + // complete immediately if channel is closed or no bytes remaining + if (!isOpen() || (src.remaining() == 0)) { + Throwable exc = (isOpen()) ? null : new ClosedChannelException(); + if (handler == null) + return CompletedFuture.withResult(0, exc); + Invoker.invokeIndirectly(handler, attachment, 0, exc, executor); + return null; + } + + final PendingFuture result = (handler == null) ? + new PendingFuture(this) : null; + Runnable task = new Runnable() { + public void run() { + int n = 0; + Throwable exc = null; + + int ti = threads.add(); + try { + begin(); + do { + n = IOUtil.write(fdObj, src, position, nd); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + if (n < 0 && !isOpen()) + throw new AsynchronousCloseException(); + } catch (IOException x) { + if (!isOpen()) + x = new AsynchronousCloseException(); + exc = x; + } finally { + end(); + threads.remove(ti); + } + if (handler == null) { + result.setResult(n, exc); + } else { + Invoker.invokeUnchecked(handler, attachment, n, exc); + } + } + }; + executor.execute(task); + return result; + } +} diff --git a/src/sun/nio/ch/SocketAdaptor.java b/src/sun/nio/ch/SocketAdaptor.java new file mode 100644 index 00000000..482ffd34 --- /dev/null +++ b/src/sun/nio/ch/SocketAdaptor.java @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketImpl; +import java.net.SocketOption; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.IllegalBlockingModeException; +import java.nio.channels.SocketChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + + +// Make a socket channel look like a socket. +// +// The only aspects of java.net.Socket-hood that we don't attempt to emulate +// here are the interrupted-I/O exceptions (which our Solaris implementations +// attempt to support) and the sending of urgent data. Otherwise an adapted +// socket should look enough like a real java.net.Socket to fool most of the +// developers most of the time, right down to the exception message strings. +// +// The methods in this class are defined in exactly the same order as in +// java.net.Socket so as to simplify tracking future changes to that class. +// + +public class SocketAdaptor + extends Socket +{ + + // The channel being adapted + private final SocketChannelImpl sc; + + // Timeout "option" value for reads + private volatile int timeout = 0; + + private SocketAdaptor(SocketChannelImpl sc) throws SocketException { + super((SocketImpl) null); + this.sc = sc; + } + + public static Socket create(SocketChannelImpl sc) { + try { + return new SocketAdaptor(sc); + } catch (SocketException e) { + throw new InternalError("Should not reach here"); + } + } + + public SocketChannel getChannel() { + return sc; + } + + // Override this method just to protect against changes in the superclass + // + public void connect(SocketAddress remote) throws IOException { + connect(remote, 0); + } + + public void connect(SocketAddress remote, int timeout) throws IOException { + if (remote == null) + throw new IllegalArgumentException("connect: The address can't be null"); + if (timeout < 0) + throw new IllegalArgumentException("connect: timeout can't be negative"); + + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + + try { + + if (timeout == 0) { + sc.connect(remote); + return; + } + + sc.configureBlocking(false); + try { + if (sc.connect(remote)) + return; + long to = timeout; + for (;;) { + if (!sc.isOpen()) + throw new ClosedChannelException(); + long st = System.currentTimeMillis(); + + int result = sc.poll(Net.POLLCONN, to); + if (result > 0 && sc.finishConnect()) + break; + to -= System.currentTimeMillis() - st; + if (to <= 0) { + try { + sc.close(); + } catch (IOException x) { } + throw new SocketTimeoutException(); + } + } + } finally { + if (sc.isOpen()) + sc.configureBlocking(true); + } + + } catch (Exception x) { + Net.translateException(x, true); + } + } + + } + + public void bind(SocketAddress local) throws IOException { + try { + sc.bind(local); + } catch (Exception x) { + Net.translateException(x); + } + } + + public InetAddress getInetAddress() { + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return null; + } else { + return ((InetSocketAddress)remote).getAddress(); + } + } + + public InetAddress getLocalAddress() { + if (sc.isOpen()) { + InetSocketAddress local = sc.localAddress(); + if (local != null) { + return Net.getRevealedLocalAddress(local).getAddress(); + } + } + return new InetSocketAddress(0).getAddress(); + } + + public int getPort() { + SocketAddress remote = sc.remoteAddress(); + if (remote == null) { + return 0; + } else { + return ((InetSocketAddress)remote).getPort(); + } + } + + public int getLocalPort() { + SocketAddress local = sc.localAddress(); + if (local == null) { + return -1; + } else { + return ((InetSocketAddress)local).getPort(); + } + } + + private class SocketInputStream + extends ChannelInputStream + { + private SocketInputStream() { + super(sc); + } + + protected int read(ByteBuffer bb) + throws IOException + { + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + if (timeout == 0) + return sc.read(bb); + sc.configureBlocking(false); + + try { + int n; + if ((n = sc.read(bb)) != 0) + return n; + long to = timeout; + for (;;) { + if (!sc.isOpen()) + throw new ClosedChannelException(); + long st = System.currentTimeMillis(); + int result = sc.poll(Net.POLLIN, to); + if (result > 0) { + if ((n = sc.read(bb)) != 0) + return n; + } + to -= System.currentTimeMillis() - st; + if (to <= 0) + throw new SocketTimeoutException(); + } + } finally { + if (sc.isOpen()) + sc.configureBlocking(true); + } + + } + } + } + + private InputStream socketInputStream = null; + + public InputStream getInputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isInputOpen()) + throw new SocketException("Socket input is shutdown"); + if (socketInputStream == null) { + try { + socketInputStream = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return new SocketInputStream(); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + } + return socketInputStream; + } + + public OutputStream getOutputStream() throws IOException { + if (!sc.isOpen()) + throw new SocketException("Socket is closed"); + if (!sc.isConnected()) + throw new SocketException("Socket is not connected"); + if (!sc.isOutputOpen()) + throw new SocketException("Socket output is shutdown"); + OutputStream os = null; + try { + os = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return Channels.newOutputStream(sc); + } + }); + } catch (java.security.PrivilegedActionException e) { + throw (IOException)e.getException(); + } + return os; + } + + private void setBooleanOption(SocketOption name, boolean value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private void setIntOption(SocketOption name, int value) + throws SocketException + { + try { + sc.setOption(name, value); + } catch (IOException x) { + Net.translateToSocketException(x); + } + } + + private boolean getBooleanOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).booleanValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return false; // keep compiler happy + } + } + + private int getIntOption(SocketOption name) throws SocketException { + try { + return sc.getOption(name).intValue(); + } catch (IOException x) { + Net.translateToSocketException(x); + return -1; // keep compiler happy + } + } + + public void setTcpNoDelay(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.TCP_NODELAY, on); + } + + public boolean getTcpNoDelay() throws SocketException { + return getBooleanOption(StandardSocketOptions.TCP_NODELAY); + } + + public void setSoLinger(boolean on, int linger) throws SocketException { + if (!on) + linger = -1; + setIntOption(StandardSocketOptions.SO_LINGER, linger); + } + + public int getSoLinger() throws SocketException { + return getIntOption(StandardSocketOptions.SO_LINGER); + } + + public void sendUrgentData(int data) throws IOException { + synchronized (sc.blockingLock()) { + if (!sc.isBlocking()) + throw new IllegalBlockingModeException(); + int n = sc.sendOutOfBandData((byte)data); + assert n == 1; + } + } + + public void setOOBInline(boolean on) throws SocketException { + setBooleanOption(ExtendedSocketOption.SO_OOBINLINE, on); + } + + public boolean getOOBInline() throws SocketException { + return getBooleanOption(ExtendedSocketOption.SO_OOBINLINE); + } + + public void setSoTimeout(int timeout) throws SocketException { + if (timeout < 0) + throw new IllegalArgumentException("timeout can't be negative"); + this.timeout = timeout; + } + + public int getSoTimeout() throws SocketException { + return timeout; + } + + public void setSendBufferSize(int size) throws SocketException { + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid send size"); + setIntOption(StandardSocketOptions.SO_SNDBUF, size); + } + + public int getSendBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_SNDBUF); + } + + public void setReceiveBufferSize(int size) throws SocketException { + // size 0 valid for SocketChannel, invalid for Socket + if (size <= 0) + throw new IllegalArgumentException("Invalid receive size"); + setIntOption(StandardSocketOptions.SO_RCVBUF, size); + } + + public int getReceiveBufferSize() throws SocketException { + return getIntOption(StandardSocketOptions.SO_RCVBUF); + } + + public void setKeepAlive(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_KEEPALIVE, on); + } + + public boolean getKeepAlive() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_KEEPALIVE); + } + + public void setTrafficClass(int tc) throws SocketException { + setIntOption(StandardSocketOptions.IP_TOS, tc); + } + + public int getTrafficClass() throws SocketException { + return getIntOption(StandardSocketOptions.IP_TOS); + } + + public void setReuseAddress(boolean on) throws SocketException { + setBooleanOption(StandardSocketOptions.SO_REUSEADDR, on); + } + + public boolean getReuseAddress() throws SocketException { + return getBooleanOption(StandardSocketOptions.SO_REUSEADDR); + } + + public void close() throws IOException { + sc.close(); + } + + public void shutdownInput() throws IOException { + try { + sc.shutdownInput(); + } catch (Exception x) { + Net.translateException(x); + } + } + + public void shutdownOutput() throws IOException { + try { + sc.shutdownOutput(); + } catch (Exception x) { + Net.translateException(x); + } + } + + public String toString() { + if (sc.isConnected()) + return "Socket[addr=" + getInetAddress() + + ",port=" + getPort() + + ",localport=" + getLocalPort() + "]"; + return "Socket[unconnected]"; + } + + public boolean isConnected() { + return sc.isConnected(); + } + + public boolean isBound() { + return sc.localAddress() != null; + } + + public boolean isClosed() { + return !sc.isOpen(); + } + + public boolean isInputShutdown() { + return !sc.isInputOpen(); + } + + public boolean isOutputShutdown() { + return !sc.isOutputOpen(); + } + +} diff --git a/src/sun/nio/ch/SocketChannelImpl.java b/src/sun/nio/ch/SocketChannelImpl.java new file mode 100644 index 00000000..1e548c3e --- /dev/null +++ b/src/sun/nio/ch/SocketChannelImpl.java @@ -0,0 +1,1057 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ProtocolFamily; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.net.StandardProtocolFamily; +import java.net.StandardSocketOptions; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NoConnectionPendingException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import sun.net.ExtendedOptionsImpl; +import sun.net.NetHooks; + + +/** + * An implementation of SocketChannels + */ + +class SocketChannelImpl + extends SocketChannel + implements SelChImpl +{ + + // Used to make native read and write calls + private static NativeDispatcher nd; + + // Our file descriptor object + private final FileDescriptor fd; + + // fd value needed for dev/poll. This value will remain valid + // even after the value in the file descriptor object has been set to -1 + private final int fdVal; + + // IDs of native threads doing reads and writes, for signalling + private volatile long readerThread = 0; + private volatile long writerThread = 0; + + // Lock held by current reading or connecting thread + private final Object readLock = new Object(); + + // Lock held by current writing or connecting thread + private final Object writeLock = new Object(); + + // Lock held by any thread that modifies the state fields declared below + // DO NOT invoke a blocking I/O operation while holding this lock! + private final Object stateLock = new Object(); + + // -- The following fields are protected by stateLock + + // set true when exclusive binding is on and SO_REUSEADDR is emulated + private boolean isReuseAddress; + + // State, increases monotonically + private static final int ST_UNINITIALIZED = -1; + private static final int ST_UNCONNECTED = 0; + private static final int ST_PENDING = 1; + private static final int ST_CONNECTED = 2; + private static final int ST_KILLPENDING = 3; + private static final int ST_KILLED = 4; + private int state = ST_UNINITIALIZED; + + // Binding + private InetSocketAddress localAddress; + private InetSocketAddress remoteAddress; + + // Input/Output open + private boolean isInputOpen = true; + private boolean isOutputOpen = true; + private boolean readyToConnect = false; + + // Socket adaptor, created on demand + private Socket socket; + + // -- End of fields protected by stateLock + + + // Constructor for normal connecting sockets + // + SocketChannelImpl(SelectorProvider sp) throws IOException { + super(sp); + this.fd = Net.socket(true); + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + } + + SocketChannelImpl(SelectorProvider sp, + FileDescriptor fd, + boolean bound) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_UNCONNECTED; + if (bound) + this.localAddress = Net.localAddress(fd); + } + + // Constructor for sockets obtained from server sockets + // + SocketChannelImpl(SelectorProvider sp, + FileDescriptor fd, InetSocketAddress remote) + throws IOException + { + super(sp); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.state = ST_CONNECTED; + this.localAddress = Net.localAddress(fd); + this.remoteAddress = remote; + } + + public Socket socket() { + synchronized (stateLock) { + if (socket == null) + socket = SocketAdaptor.create(this); + return socket; + } + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + return Net.getRevealedLocalAddress(localAddress); + } + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + return remoteAddress; + } + } + + @Override + public SocketChannel setOption(SocketOption name, T value) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + if (name == StandardSocketOptions.IP_TOS) { + ProtocolFamily family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + Net.setSocketOption(fd, family, name, value); + return this; + } + + if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) { + // SO_REUSEADDR emulated when using exclusive bind + isReuseAddress = (Boolean)value; + return this; + } + + // no options that require special handling + Net.setSocketOption(fd, Net.UNSPEC, name, value); + return this; + } + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SocketOption name) + throws IOException + { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + if (name == StandardSocketOptions.SO_REUSEADDR && + Net.useExclusiveBind()) + { + // SO_REUSEADDR emulated when using exclusive bind + return (T)Boolean.valueOf(isReuseAddress); + } + + // special handling for IP_TOS: always return 0 when IPv6 + if (name == StandardSocketOptions.IP_TOS) { + ProtocolFamily family = Net.isIPv6Available() ? + StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + return (T) Net.getSocketOption(fd, family, name); + } + + // no options that require special handling + return (T) Net.getSocketOption(fd, Net.UNSPEC, name); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(8); + set.add(StandardSocketOptions.SO_SNDBUF); + set.add(StandardSocketOptions.SO_RCVBUF); + set.add(StandardSocketOptions.SO_KEEPALIVE); + set.add(StandardSocketOptions.SO_REUSEADDR); + set.add(StandardSocketOptions.SO_LINGER); + set.add(StandardSocketOptions.TCP_NODELAY); + // additional options required by socket adaptor + set.add(StandardSocketOptions.IP_TOS); + set.add(ExtendedSocketOption.SO_OOBINLINE); + if (ExtendedOptionsImpl.flowSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); + } + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + private boolean ensureReadOpen() throws ClosedChannelException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (!isInputOpen) + return false; + else + return true; + } + } + + private void ensureWriteOpen() throws ClosedChannelException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isOutputOpen) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + } + + private void readerCleanup() throws IOException { + synchronized (stateLock) { + readerThread = 0; + if (state == ST_KILLPENDING) + kill(); + } + } + + private void writerCleanup() throws IOException { + synchronized (stateLock) { + writerThread = 0; + if (state == ST_KILLPENDING) + kill(); + } + } + + public int read(ByteBuffer buf) throws IOException { + + if (buf == null) + throw new NullPointerException(); + + synchronized (readLock) { + if (!ensureReadOpen()) + return -1; + int n = 0; + try { + + // Set up the interruption machinery; see + // AbstractInterruptibleChannel for details + // + begin(); + + synchronized (stateLock) { + if (!isOpen()) { + // Either the current thread is already interrupted, so + // begin() closed the channel, or another thread closed the + // channel since we checked it a few bytecodes ago. In + // either case the value returned here is irrelevant since + // the invocation of end() in the finally block will throw + // an appropriate exception. + // + return 0; + + } + + // Save this thread so that it can be signalled on those + // platforms that require it + // + readerThread = NativeThread.current(); + } + + // Between the previous test of isOpen() and the return of the + // IOUtil.read invocation below, this channel might be closed + // or this thread might be interrupted. We rely upon the + // implicit synchronization point in the kernel read() call to + // make sure that the right thing happens. In either case the + // implCloseSelectableChannel method is ultimately invoked in + // some other thread, so there are three possibilities: + // + // - implCloseSelectableChannel() invokes nd.preClose() + // before this thread invokes read(), in which case the + // read returns immediately with either EOF or an error, + // the latter of which will cause an IOException to be + // thrown. + // + // - implCloseSelectableChannel() invokes nd.preClose() after + // this thread is blocked in read(). On some operating + // systems (e.g., Solaris and Windows) this causes the read + // to return immediately with either EOF or an error + // indication. + // + // - implCloseSelectableChannel() invokes nd.preClose() after + // this thread is blocked in read() but the operating + // system (e.g., Linux) doesn't support preemptive close, + // so implCloseSelectableChannel() proceeds to signal this + // thread, thereby causing the read to return immediately + // with IOStatus.INTERRUPTED. + // + // In all three cases the invocation of end() in the finally + // clause will notice that the channel has been closed and + // throw an appropriate exception (AsynchronousCloseException + // or ClosedByInterruptException) if necessary. + // + // *There is A fourth possibility. implCloseSelectableChannel() + // invokes nd.preClose(), signals reader/writer thred and quickly + // moves on to nd.close() in kill(), which does a real close. + // Then a third thread accepts a new connection, opens file or + // whatever that causes the released "fd" to be recycled. All + // above happens just between our last isOpen() check and the + // next kernel read reached, with the recycled "fd". The solution + // is to postpone the real kill() if there is a reader or/and + // writer thread(s) over there "waiting", leave the cleanup/kill + // to the reader or writer thread. (the preClose() still happens + // so the connection gets cut off as usual). + // + // For socket channels there is the additional wrinkle that + // asynchronous shutdown works much like asynchronous close, + // except that the channel is shutdown rather than completely + // closed. This is analogous to the first two cases above, + // except that the shutdown operation plays the role of + // nd.preClose(). + for (;;) { + n = IOUtil.read(fd, buf, -1, nd); + if ((n == IOStatus.INTERRUPTED) && isOpen()) { + // The system call was interrupted but the channel + // is still open, so retry + continue; + } + return IOStatus.normalize(n); + } + + } finally { + readerCleanup(); // Clear reader thread + // The end method, which is defined in our superclass + // AbstractInterruptibleChannel, resets the interruption + // machinery. If its argument is true then it returns + // normally; otherwise it checks the interrupt and open state + // of this channel and throws an appropriate exception if + // necessary. + // + // So, if we actually managed to do any I/O in the above try + // block then we pass true to the end method. We also pass + // true if the channel was in non-blocking mode when the I/O + // operation was initiated but no data could be transferred; + // this prevents spurious exceptions from being thrown in the + // rare event that a channel is closed or a thread is + // interrupted at the exact moment that a non-blocking I/O + // request is made. + // + end(n > 0 || (n == IOStatus.UNAVAILABLE)); + + // Extra case for socket channels: Asynchronous shutdown + // + synchronized (stateLock) { + if ((n <= 0) && (!isInputOpen)) + return IOStatus.EOF; + } + + assert IOStatus.check(n); + + } + } + } + + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); + synchronized (readLock) { + if (!ensureReadOpen()) + return -1; + long n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + } + + for (;;) { + n = IOUtil.read(fd, dsts, offset, length, nd); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + return IOStatus.normalize(n); + } + } finally { + readerCleanup(); + end(n > 0 || (n == IOStatus.UNAVAILABLE)); + synchronized (stateLock) { + if ((n <= 0) && (!isInputOpen)) + return IOStatus.EOF; + } + assert IOStatus.check(n); + } + } + } + + public int write(ByteBuffer buf) throws IOException { + if (buf == null) + throw new NullPointerException(); + synchronized (writeLock) { + ensureWriteOpen(); + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + } + for (;;) { + n = IOUtil.write(fd, buf, -1, nd); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + return IOStatus.normalize(n); + } + } finally { + writerCleanup(); + end(n > 0 || (n == IOStatus.UNAVAILABLE)); + synchronized (stateLock) { + if ((n <= 0) && (!isOutputOpen)) + throw new AsynchronousCloseException(); + } + assert IOStatus.check(n); + } + } + } + + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); + synchronized (writeLock) { + ensureWriteOpen(); + long n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + } + for (;;) { + n = IOUtil.write(fd, srcs, offset, length, nd); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + return IOStatus.normalize(n); + } + } finally { + writerCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + synchronized (stateLock) { + if ((n <= 0) && (!isOutputOpen)) + throw new AsynchronousCloseException(); + } + assert IOStatus.check(n); + } + } + } + + // package-private + int sendOutOfBandData(byte b) throws IOException { + synchronized (writeLock) { + ensureWriteOpen(); + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + writerThread = NativeThread.current(); + } + for (;;) { + n = sendOutOfBandData(fd, b); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + return IOStatus.normalize(n); + } + } finally { + writerCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + synchronized (stateLock) { + if ((n <= 0) && (!isOutputOpen)) + throw new AsynchronousCloseException(); + } + assert IOStatus.check(n); + } + } + } + + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + public InetSocketAddress localAddress() { + synchronized (stateLock) { + return localAddress; + } + } + + public SocketAddress remoteAddress() { + synchronized (stateLock) { + return remoteAddress; + } + } + + @Override + public SocketChannel bind(SocketAddress local) throws IOException { + synchronized (readLock) { + synchronized (writeLock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (state == ST_PENDING) + throw new ConnectionPendingException(); + if (localAddress != null) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkListen(isa.getPort()); + } + NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + localAddress = Net.localAddress(fd); + } + } + } + return this; + } + + public boolean isConnected() { + synchronized (stateLock) { + return (state == ST_CONNECTED); + } + } + + public boolean isConnectionPending() { + synchronized (stateLock) { + return (state == ST_PENDING); + } + } + + void ensureOpenAndUnconnected() throws IOException { // package-private + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (state == ST_CONNECTED) + throw new AlreadyConnectedException(); + if (state == ST_PENDING) + throw new ConnectionPendingException(); + } + } + + public boolean connect(SocketAddress sa) throws IOException { + int localPort = 0; + + synchronized (readLock) { + synchronized (writeLock) { + ensureOpenAndUnconnected(); + InetSocketAddress isa = Net.checkAddress(sa); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), + isa.getPort()); + synchronized (blockingLock()) { + int n = 0; + try { + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) { + return false; + } + // notify hook only if unbound + if (localAddress == null) { + NetHooks.beforeTcpConnect(fd, + isa.getAddress(), + isa.getPort()); + } + readerThread = NativeThread.current(); + } + for (;;) { + InetAddress ia = isa.getAddress(); + if (ia.isAnyLocalAddress()) + ia = InetAddress.getLocalHost(); + n = Net.connect(fd, + ia, + isa.getPort()); + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + + } finally { + readerCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } catch (IOException x) { + // If an exception was thrown, close the channel after + // invoking end() so as to avoid bogus + // AsynchronousCloseExceptions + close(); + throw x; + } + synchronized (stateLock) { + remoteAddress = isa; + if (n > 0) { + + // Connection succeeded; disallow further + // invocation + state = ST_CONNECTED; + if (isOpen()) + localAddress = Net.localAddress(fd); + return true; + } + // If nonblocking and no exception then connection + // pending; disallow another invocation + if (!isBlocking()) + state = ST_PENDING; + else + assert false; + } + } + return false; + } + } + } + + public boolean finishConnect() throws IOException { + synchronized (readLock) { + synchronized (writeLock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (state == ST_CONNECTED) + return true; + if (state != ST_PENDING) + throw new NoConnectionPendingException(); + } + int n = 0; + try { + try { + begin(); + synchronized (blockingLock()) { + synchronized (stateLock) { + if (!isOpen()) { + return false; + } + readerThread = NativeThread.current(); + } + if (!isBlocking()) { + for (;;) { + n = checkConnect(fd, false, + readyToConnect); + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + } else { + for (;;) { + n = checkConnect(fd, true, + readyToConnect); + if (n == 0) { + // Loop in case of + // spurious notifications + continue; + } + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + } + } + } finally { + synchronized (stateLock) { + readerThread = 0; + if (state == ST_KILLPENDING) { + kill(); + // poll()/getsockopt() does not report + // error (throws exception, with n = 0) + // on Linux platform after dup2 and + // signal-wakeup. Force n to 0 so the + // end() can throw appropriate exception + n = 0; + } + } + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } catch (IOException x) { + // If an exception was thrown, close the channel after + // invoking end() so as to avoid bogus + // AsynchronousCloseExceptions + close(); + throw x; + } + if (n > 0) { + synchronized (stateLock) { + state = ST_CONNECTED; + if (isOpen()) + localAddress = Net.localAddress(fd); + } + return true; + } + return false; + } + } + } + + @Override + public SocketChannel shutdownInput() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isInputOpen) { + Net.shutdown(fd, Net.SHUT_RD); + if (readerThread != 0) + NativeThread.signal(readerThread); + isInputOpen = false; + } + return this; + } + } + + @Override + public SocketChannel shutdownOutput() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + if (isOutputOpen) { + Net.shutdown(fd, Net.SHUT_WR); + if (writerThread != 0) + NativeThread.signal(writerThread); + isOutputOpen = false; + } + return this; + } + } + + public boolean isInputOpen() { + synchronized (stateLock) { + return isInputOpen; + } + } + + public boolean isOutputOpen() { + synchronized (stateLock) { + return isOutputOpen; + } + } + + // AbstractInterruptibleChannel synchronizes invocations of this method + // using AbstractInterruptibleChannel.closeLock, and also ensures that this + // method is only ever invoked once. Before we get to this method, isOpen + // (which is volatile) will have been set to false. + // + protected void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + isInputOpen = false; + isOutputOpen = false; + + // Close the underlying file descriptor and dup it to a known fd + // that's already closed. This prevents other operations on this + // channel from using the old fd, which might be recycled in the + // meantime and allocated to an entirely different channel. + // + if (state != ST_KILLED) + nd.preClose(fd); + + // Signal native threads, if needed. If a target thread is not + // currently blocked in an I/O operation then no harm is done since + // the signal handler doesn't actually do anything. + // + if (readerThread != 0) + NativeThread.signal(readerThread); + + if (writerThread != 0) + NativeThread.signal(writerThread); + + // If this channel is not registered then it's safe to close the fd + // immediately since we know at this point that no thread is + // blocked in an I/O operation upon the channel and, since the + // channel is marked closed, no thread will start another such + // operation. If this channel is registered then we don't close + // the fd since it might be in use by a selector. In that case + // closing this channel caused its keys to be cancelled, so the + // last selector to deregister a key for this channel will invoke + // kill() to close the fd. + // + if (!isRegistered()) + kill(); + } + } + + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ST_KILLED) + return; + if (state == ST_UNINITIALIZED) { + state = ST_KILLED; + return; + } + assert !isOpen() && !isRegistered(); + + // Postpone the kill if there is a waiting reader + // or writer thread. See the comments in read() for + // more detailed explanation. + if (readerThread == 0 && writerThread == 0) { + nd.close(fd); + state = ST_KILLED; + } else { + state = ST_KILLPENDING; + } + } + } + + /** + * Translates native poll revent ops into a ready operation ops + */ + public boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & Net.POLLNVAL) != 0) { + // This should only happen if this channel is pre-closed while a + // selection operation is in progress + // ## Throw an error if this channel has not been pre-closed + return false; + } + + if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + // No need to poll again in checkConnect, + // the error will be detected there + readyToConnect = true; + return (newOps & ~oldOps) != 0; + } + + if (((ops & Net.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0) && + (state == ST_CONNECTED)) + newOps |= SelectionKey.OP_READ; + + if (((ops & Net.POLLCONN) != 0) && + ((intOps & SelectionKey.OP_CONNECT) != 0) && + ((state == ST_UNCONNECTED) || (state == ST_PENDING))) { + newOps |= SelectionKey.OP_CONNECT; + readyToConnect = true; + } + + if (((ops & Net.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0) && + (state == ST_CONNECTED)) + newOps |= SelectionKey.OP_WRITE; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (readLock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + readerCleanup(); + end(n > 0); + } + return n; + } + } + + /** + * Translates an interest operation set into a native poll event set + */ + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= Net.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= Net.POLLOUT; + if ((ops & SelectionKey.OP_CONNECT) != 0) + newOps |= Net.POLLCONN; + sk.selector.putEventOps(sk, newOps); + } + + public FileDescriptor getFD() { + return fd; + } + + public int getFDVal() { + return fdVal; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(this.getClass().getSuperclass().getName()); + sb.append('['); + if (!isOpen()) + sb.append("closed"); + else { + synchronized (stateLock) { + switch (state) { + case ST_UNCONNECTED: + sb.append("unconnected"); + break; + case ST_PENDING: + sb.append("connection-pending"); + break; + case ST_CONNECTED: + sb.append("connected"); + if (!isInputOpen) + sb.append(" ishut"); + if (!isOutputOpen) + sb.append(" oshut"); + break; + } + InetSocketAddress addr = localAddress(); + if (addr != null) { + sb.append(" local="); + sb.append(Net.getRevealedLocalAddressAsString(addr)); + } + if (remoteAddress() != null) { + sb.append(" remote="); + sb.append(remoteAddress().toString()); + } + } + } + sb.append(']'); + return sb.toString(); + } + + + // -- Native methods -- + + private static native int checkConnect(FileDescriptor fd, + boolean block, boolean ready) + throws IOException; + + private static native int sendOutOfBandData(FileDescriptor fd, byte data) + throws IOException; + + static { + IOUtil.load(); + nd = new SocketDispatcher(); + } + +} diff --git a/src/sun/nio/ch/ThreadPool.java b/src/sun/nio/ch/ThreadPool.java new file mode 100644 index 00000000..dc45cc36 --- /dev/null +++ b/src/sun/nio/ch/ThreadPool.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; + +import sun.security.action.GetPropertyAction; + +/** + * Encapsulates a thread pool associated with a channel group. + */ + +public class ThreadPool { + private static final String DEFAULT_THREAD_POOL_THREAD_FACTORY = + "java.nio.channels.DefaultThreadPool.threadFactory"; + private static final String DEFAULT_THREAD_POOL_INITIAL_SIZE = + "java.nio.channels.DefaultThreadPool.initialSize"; + + private final ExecutorService executor; + + // indicates if thread pool is fixed size + private final boolean isFixed; + + // indicates the pool size (for a fixed thread pool configuratin this is + // the maximum pool size; for other thread pools it is the initial size) + private final int poolSize; + + private ThreadPool(ExecutorService executor, + boolean isFixed, + int poolSize) + { + this.executor = executor; + this.isFixed = isFixed; + this.poolSize = poolSize; + } + + ExecutorService executor() { + return executor; + } + + boolean isFixedThreadPool() { + return isFixed; + } + + int poolSize() { + return poolSize; + } + + static ThreadFactory defaultThreadFactory() { + if (System.getSecurityManager() == null) { + return (Runnable r) -> { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + }; + } else { + return (Runnable r) -> { + PrivilegedAction action = () -> { + Thread t = new sun.misc.InnocuousThread(r); + t.setDaemon(true); + return t; + }; + return AccessController.doPrivileged(action); + }; + } + } + + private static class DefaultThreadPoolHolder { + final static ThreadPool defaultThreadPool = createDefault(); + } + + // return the default (system-wide) thread pool + static ThreadPool getDefault() { + return DefaultThreadPoolHolder.defaultThreadPool; + } + + // create thread using default settings (configured by system properties) + static ThreadPool createDefault() { + // default the number of fixed threads to the hardware core count + int initialSize = getDefaultThreadPoolInitialSize(); + if (initialSize < 0) + initialSize = Runtime.getRuntime().availableProcessors(); + // default to thread factory that creates daemon threads + ThreadFactory threadFactory = getDefaultThreadPoolThreadFactory(); + if (threadFactory == null) + threadFactory = defaultThreadFactory(); + // create thread pool + ExecutorService executor = Executors.newCachedThreadPool(threadFactory); + return new ThreadPool(executor, false, initialSize); + } + + // create using given parameters + static ThreadPool create(int nThreads, ThreadFactory factory) { + if (nThreads <= 0) + throw new IllegalArgumentException("'nThreads' must be > 0"); + ExecutorService executor = Executors.newFixedThreadPool(nThreads, factory); + return new ThreadPool(executor, true, nThreads); + } + + // wrap a user-supplied executor + public static ThreadPool wrap(ExecutorService executor, int initialSize) { + if (executor == null) + throw new NullPointerException("'executor' is null"); + // attempt to check if cached thread pool + if (executor instanceof ThreadPoolExecutor) { + int max = ((ThreadPoolExecutor)executor).getMaximumPoolSize(); + if (max == Integer.MAX_VALUE) { + if (initialSize < 0) { + initialSize = Runtime.getRuntime().availableProcessors(); + } else { + // not a cached thread pool so ignore initial size + initialSize = 0; + } + } + } else { + // some other type of thread pool + if (initialSize < 0) + initialSize = 0; + } + return new ThreadPool(executor, false, initialSize); + } + + private static int getDefaultThreadPoolInitialSize() { + String propValue = AccessController.doPrivileged(new + GetPropertyAction(DEFAULT_THREAD_POOL_INITIAL_SIZE)); + if (propValue != null) { + try { + return Integer.parseInt(propValue); + } catch (NumberFormatException x) { + throw new Error("Value of property '" + DEFAULT_THREAD_POOL_INITIAL_SIZE + + "' is invalid: " + x); + } + } + return -1; + } + + private static ThreadFactory getDefaultThreadPoolThreadFactory() { + String propValue = AccessController.doPrivileged(new + GetPropertyAction(DEFAULT_THREAD_POOL_THREAD_FACTORY)); + if (propValue != null) { + try { + Class c = Class + .forName(propValue, true, ClassLoader.getSystemClassLoader()); + return ((ThreadFactory)c.newInstance()); + } catch (ClassNotFoundException x) { + throw new Error(x); + } catch (InstantiationException x) { + throw new Error(x); + } catch (IllegalAccessException x) { + throw new Error(x); + } + } + return null; + } +} diff --git a/src/sun/nio/ch/Util.java b/src/sun/nio/ch/Util.java new file mode 100644 index 00000000..814eab66 --- /dev/null +++ b/src/sun/nio/ch/Util.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import sun.misc.Unsafe; +import sun.security.action.GetPropertyAction; + + +public class Util { + + // -- Caches -- + + // The number of temp buffers in our pool + private static final int TEMP_BUF_POOL_SIZE = IOUtil.IOV_MAX; + + // Per-thread cache of temporary direct buffers + private static ThreadLocal bufferCache = + new ThreadLocal() + { + @Override + protected BufferCache initialValue() { + return new BufferCache(); + } + }; + + /** + * A simple cache of direct buffers. + */ + private static class BufferCache { + // the array of buffers + private ByteBuffer[] buffers; + + // the number of buffers in the cache + private int count; + + // the index of the first valid buffer (undefined if count == 0) + private int start; + + private int next(int i) { + return (i + 1) % TEMP_BUF_POOL_SIZE; + } + + BufferCache() { + buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE]; + } + + /** + * Removes and returns a buffer from the cache of at least the given + * size (or null if no suitable buffer is found). + */ + ByteBuffer get(int size) { + if (count == 0) + return null; // cache is empty + + ByteBuffer[] buffers = this.buffers; + + // search for suitable buffer (often the first buffer will do) + ByteBuffer buf = buffers[start]; + if (buf.capacity() < size) { + buf = null; + int i = start; + while ((i = next(i)) != start) { + ByteBuffer bb = buffers[i]; + if (bb == null) + break; + if (bb.capacity() >= size) { + buf = bb; + break; + } + } + if (buf == null) + return null; + // move first element to here to avoid re-packing + buffers[i] = buffers[start]; + } + + // remove first element + buffers[start] = null; + start = next(start); + count--; + + // prepare the buffer and return it + buf.rewind(); + buf.limit(size); + return buf; + } + + boolean offerFirst(ByteBuffer buf) { + if (count >= TEMP_BUF_POOL_SIZE) { + return false; + } else { + start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE; + buffers[start] = buf; + count++; + return true; + } + } + + boolean offerLast(ByteBuffer buf) { + if (count >= TEMP_BUF_POOL_SIZE) { + return false; + } else { + int next = (start + count) % TEMP_BUF_POOL_SIZE; + buffers[next] = buf; + count++; + return true; + } + } + + boolean isEmpty() { + return count == 0; + } + + ByteBuffer removeFirst() { + assert count > 0; + ByteBuffer buf = buffers[start]; + buffers[start] = null; + start = next(start); + count--; + return buf; + } + } + + /** + * Returns a temporary buffer of at least the given size + */ + public static ByteBuffer getTemporaryDirectBuffer(int size) { + BufferCache cache = bufferCache.get(); + ByteBuffer buf = cache.get(size); + if (buf != null) { + return buf; + } else { + // No suitable buffer in the cache so we need to allocate a new + // one. To avoid the cache growing then we remove the first + // buffer from the cache and free it. + if (!cache.isEmpty()) { + buf = cache.removeFirst(); + free(buf); + } + return ByteBuffer.allocateDirect(size); + } + } + + /** + * Releases a temporary buffer by returning to the cache or freeing it. + */ + public static void releaseTemporaryDirectBuffer(ByteBuffer buf) { + offerFirstTemporaryDirectBuffer(buf); + } + + /** + * Releases a temporary buffer by returning to the cache or freeing it. If + * returning to the cache then insert it at the start so that it is + * likely to be returned by a subsequent call to getTemporaryDirectBuffer. + */ + static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { + assert buf != null; + BufferCache cache = bufferCache.get(); + if (!cache.offerFirst(buf)) { + // cache is full + free(buf); + } + } + + /** + * Releases a temporary buffer by returning to the cache or freeing it. If + * returning to the cache then insert it at the end. This makes it + * suitable for scatter/gather operations where the buffers are returned to + * cache in same order that they were obtained. + */ + static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { + assert buf != null; + BufferCache cache = bufferCache.get(); + if (!cache.offerLast(buf)) { + // cache is full + free(buf); + } + } + + /** + * Frees the memory for the given direct buffer + */ + private static void free(ByteBuffer buf) { + ((DirectBuffer)buf).cleaner().clean(); + } + + + // -- Random stuff -- + + static ByteBuffer[] subsequence(ByteBuffer[] bs, int offset, int length) { + if ((offset == 0) && (length == bs.length)) + return bs; + int n = length; + ByteBuffer[] bs2 = new ByteBuffer[n]; + for (int i = 0; i < n; i++) + bs2[i] = bs[offset + i]; + return bs2; + } + + static Set ungrowableSet(final Set s) { + return new Set() { + + public int size() { return s.size(); } + public boolean isEmpty() { return s.isEmpty(); } + public boolean contains(Object o) { return s.contains(o); } + public Object[] toArray() { return s.toArray(); } + public T[] toArray(T[] a) { return s.toArray(a); } + public String toString() { return s.toString(); } + public Iterator iterator() { return s.iterator(); } + public boolean equals(Object o) { return s.equals(o); } + public int hashCode() { return s.hashCode(); } + public void clear() { s.clear(); } + public boolean remove(Object o) { return s.remove(o); } + + public boolean containsAll(Collection coll) { + return s.containsAll(coll); + } + public boolean removeAll(Collection coll) { + return s.removeAll(coll); + } + public boolean retainAll(Collection coll) { + return s.retainAll(coll); + } + + public boolean add(E o){ + throw new UnsupportedOperationException(); + } + public boolean addAll(Collection coll) { + throw new UnsupportedOperationException(); + } + + }; + } + + + // -- Unsafe access -- + + private static Unsafe unsafe = Unsafe.getUnsafe(); + + private static byte _get(long a) { + return unsafe.getByte(a); + } + + private static void _put(long a, byte b) { + unsafe.putByte(a, b); + } + + static void erase(ByteBuffer bb) { + unsafe.setMemory(((DirectBuffer)bb).address(), bb.capacity(), (byte)0); + } + + static Unsafe unsafe() { + return unsafe; + } + + private static int pageSize = -1; + + static int pageSize() { + if (pageSize == -1) + pageSize = unsafe().pageSize(); + return pageSize; + } + + private static volatile Constructor directByteBufferConstructor = null; + + private static void initDBBConstructor() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + try { + Class cl = Class.forName("java.nio.DirectByteBuffer"); + Constructor ctor = cl.getDeclaredConstructor( + new Class[] { int.class, + long.class, + FileDescriptor.class, + Runnable.class }); + ctor.setAccessible(true); + directByteBufferConstructor = ctor; + } catch (ClassNotFoundException | + NoSuchMethodException | + IllegalArgumentException | + ClassCastException x) { + throw new InternalError(x); + } + return null; + }}); + } + + static MappedByteBuffer newMappedByteBuffer(int size, long addr, + FileDescriptor fd, + Runnable unmapper) + { + MappedByteBuffer dbb; + if (directByteBufferConstructor == null) + initDBBConstructor(); + try { + dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance( + new Object[] { new Integer(size), + new Long(addr), + fd, + unmapper }); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException e) { + throw new InternalError(e); + } + return dbb; + } + + private static volatile Constructor directByteBufferRConstructor = null; + + private static void initDBBRConstructor() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + try { + Class cl = Class.forName("java.nio.DirectByteBufferR"); + Constructor ctor = cl.getDeclaredConstructor( + new Class[] { int.class, + long.class, + FileDescriptor.class, + Runnable.class }); + ctor.setAccessible(true); + directByteBufferRConstructor = ctor; + } catch (ClassNotFoundException | + NoSuchMethodException | + IllegalArgumentException | + ClassCastException x) { + throw new InternalError(x); + } + return null; + }}); + } + + static MappedByteBuffer newMappedByteBufferR(int size, long addr, + FileDescriptor fd, + Runnable unmapper) + { + MappedByteBuffer dbb; + if (directByteBufferRConstructor == null) + initDBBRConstructor(); + try { + dbb = (MappedByteBuffer)directByteBufferRConstructor.newInstance( + new Object[] { new Integer(size), + new Long(addr), + fd, + unmapper }); + } catch (InstantiationException | + IllegalAccessException | + InvocationTargetException e) { + throw new InternalError(e); + } + return dbb; + } + + + // -- Bug compatibility -- + + private static volatile String bugLevel = null; + + static boolean atBugLevel(String bl) { // package-private + if (bugLevel == null) { + if (!sun.misc.VM.isBooted()) + return false; + String value = AccessController.doPrivileged( + new GetPropertyAction("sun.nio.ch.bugLevel")); + bugLevel = (value != null) ? value : ""; + } + return bugLevel.equals(bl); + } + +} diff --git a/src/sun/nio/ch/sctp/MessageInfoImpl.java b/src/sun/nio/ch/sctp/MessageInfoImpl.java new file mode 100644 index 00000000..545816ec --- /dev/null +++ b/src/sun/nio/ch/sctp/MessageInfoImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.nio.ch.sctp; + +import java.net.SocketAddress; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.MessageInfo; + +/** + * An implementation of a MessageInfo. + */ +public class MessageInfoImpl extends MessageInfo { + private final SocketAddress address; + private final int bytes; /* 0 */ + + private Association association; + private int assocId; + private int streamNumber; + private boolean complete = true; + private boolean unordered; /* false */ + private long timeToLive; /* 0L */ + private int ppid; /* 0 */ + + public MessageInfoImpl(Association association, + SocketAddress address, + int streamNumber) { + this.association = association; + this.address = address; + this.streamNumber = streamNumber; + bytes = 0; + } + + /* Invoked from native */ + private MessageInfoImpl(int assocId, + SocketAddress address, + int bytes, + int streamNumber, + boolean complete, + boolean unordered, + int ppid) { + this.assocId = assocId; + this.address = address; + this.bytes = bytes; + this.streamNumber = streamNumber; + this.complete = complete; + this.unordered = unordered; + this.ppid = ppid; + } + + @Override + public Association association() { + return association; + } + + /** + * MessageInfoImpl instances created from native will need to have their + * association set from the channel. + */ + void setAssociation(Association association) { + this.association = association; + } + + int associationID() { + return assocId; + } + + @Override + public SocketAddress address() { + return address; + } + + @Override + public int bytes() { + return bytes; + } + + @Override + public int streamNumber() { + return streamNumber; + } + + @Override + public MessageInfo streamNumber(int streamNumber) { + if (streamNumber < 0 || streamNumber > 65536) + throw new IllegalArgumentException("Invalid stream number"); + + this.streamNumber = streamNumber; + return this; + } + + @Override + public int payloadProtocolID() { + return ppid; + } + + @Override + public MessageInfo payloadProtocolID(int ppid) { + this.ppid = ppid; + return this; + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public MessageInfo complete(boolean complete) { + this.complete = complete; + return this; + } + + @Override + public boolean isUnordered() { + return unordered; + } + + @Override + public MessageInfo unordered(boolean unordered) { + this.unordered = unordered; + return this; + } + + @Override + public long timeToLive() { + return timeToLive; + } + + @Override + public MessageInfo timeToLive(long millis) { + timeToLive = millis; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(super.toString()); + sb.append( "[Address: ").append(address) + .append(", Association: ").append(association) + .append(", Assoc ID: ").append(assocId) + .append(", Bytes: ").append(bytes) + .append(", Stream Number: ").append(streamNumber) + .append(", Complete: ").append(complete) + .append(", isUnordered: ").append(unordered) + .append("]"); + return sb.toString(); + } +} diff --git a/src/sun/nio/ch/sctp/SctpStdSocketOption.java b/src/sun/nio/ch/sctp/SctpStdSocketOption.java new file mode 100644 index 00000000..05da15bd --- /dev/null +++ b/src/sun/nio/ch/sctp/SctpStdSocketOption.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.nio.ch.sctp; + +import java.lang.annotation.Native; + +import com.sun.nio.sctp.SctpSocketOption; + +public class SctpStdSocketOption + implements SctpSocketOption +{ + /* for native mapping of int options */ + @Native public static final int SCTP_DISABLE_FRAGMENTS = 1; + @Native public static final int SCTP_EXPLICIT_COMPLETE = 2; + @Native public static final int SCTP_FRAGMENT_INTERLEAVE = 3; + @Native public static final int SCTP_NODELAY = 4; + @Native public static final int SO_SNDBUF = 5; + @Native public static final int SO_RCVBUF = 6; + @Native public static final int SO_LINGER = 7; + + private final String name; + private final Class type; + + /* for native mapping of int options */ + private int constValue; + + public SctpStdSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + + public SctpStdSocketOption(String name, Class type, int constValue) { + this.name = name; + this.type = type; + this.constValue = constValue; + } + + @Override + public String name() { + return name; + } + + @Override + public Class type() { + return type; + } + + @Override + public String toString() { + return name; + } + + int constValue() { + return constValue; + } +} diff --git a/src/sun/nio/cs/AbstractCharsetProvider.java b/src/sun/nio/cs/AbstractCharsetProvider.java new file mode 100644 index 00000000..7dd3666c --- /dev/null +++ b/src/sun/nio/cs/AbstractCharsetProvider.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.lang.ref.SoftReference; +import java.nio.charset.Charset; +import java.nio.charset.spi.CharsetProvider; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +import sun.misc.ASCIICaseInsensitiveComparator; + + +/** + * Abstract base class for charset providers. + * + * @author Mark Reinhold + */ + +public class AbstractCharsetProvider + extends CharsetProvider +{ + + /* Maps canonical names to class names + */ + private Map classMap + = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER); + + /* Maps alias names to canonical names + */ + private Map aliasMap + = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER); + + /* Maps canonical names to alias-name arrays + */ + private Map aliasNameMap + = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER); + + /* Maps canonical names to soft references that hold cached instances + */ + private Map> cache + = new TreeMap<>(ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER); + + private String packagePrefix; + + protected AbstractCharsetProvider() { + packagePrefix = "sun.nio.cs"; + } + + protected AbstractCharsetProvider(String pkgPrefixName) { + packagePrefix = pkgPrefixName; + } + + /* Add an entry to the given map, but only if no mapping yet exists + * for the given name. + */ + private static void put(Map m, K name, V value) { + if (!m.containsKey(name)) + m.put(name, value); + } + + private static void remove(Map m, K name) { + V x = m.remove(name); + assert (x != null); + } + + /* Declare support for the given charset + */ + protected void charset(String name, String className, String[] aliases) { + synchronized (this) { + put(classMap, name, className); + for (int i = 0; i < aliases.length; i++) + put(aliasMap, aliases[i], name); + put(aliasNameMap, name, aliases); + cache.clear(); + } + } + + protected void deleteCharset(String name, String[] aliases) { + synchronized (this) { + remove(classMap, name); + for (int i = 0; i < aliases.length; i++) + remove(aliasMap, aliases[i]); + remove(aliasNameMap, name); + cache.clear(); + } + } + + /* Late initialization hook, needed by some providers + */ + protected void init() { } + + private String canonicalize(String charsetName) { + String acn = aliasMap.get(charsetName); + return (acn != null) ? acn : charsetName; + } + + private Charset lookup(String csn) { + + // Check cache first + SoftReference sr = cache.get(csn); + if (sr != null) { + Charset cs = sr.get(); + if (cs != null) + return cs; + } + + // Do we even support this charset? + String cln = classMap.get(csn); + + if (cln == null) + return null; + + // Instantiate the charset and cache it + try { + + Class c = Class.forName(packagePrefix + "." + cln, + true, + this.getClass().getClassLoader()); + + Charset cs = (Charset)c.newInstance(); + cache.put(csn, new SoftReference(cs)); + return cs; + } catch (ClassNotFoundException x) { + return null; + } catch (IllegalAccessException x) { + return null; + } catch (InstantiationException x) { + return null; + } + } + + public final Charset charsetForName(String charsetName) { + synchronized (this) { + init(); + return lookup(canonicalize(charsetName)); + } + } + + public final Iterator charsets() { + + final ArrayList ks; + synchronized (this) { + init(); + ks = new ArrayList<>(classMap.keySet()); + } + + return new Iterator() { + Iterator i = ks.iterator(); + + public boolean hasNext() { + return i.hasNext(); + } + + public Charset next() { + String csn = i.next(); + synchronized (AbstractCharsetProvider.this) { + return lookup(csn); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public final String[] aliases(String charsetName) { + synchronized (this) { + init(); + return aliasNameMap.get(charsetName); + } + } + +} diff --git a/src/sun/nio/cs/ArrayDecoder.java b/src/sun/nio/cs/ArrayDecoder.java new file mode 100644 index 00000000..6b0abe98 --- /dev/null +++ b/src/sun/nio/cs/ArrayDecoder.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +/* + * FastPath byte[]->char[] decoder, REPLACE on malformed or + * unmappable input. + */ + +public interface ArrayDecoder { + int decode(byte[] src, int off, int len, char[] dst); +} diff --git a/src/sun/nio/cs/ArrayEncoder.java b/src/sun/nio/cs/ArrayEncoder.java new file mode 100644 index 00000000..2ef46e7f --- /dev/null +++ b/src/sun/nio/cs/ArrayEncoder.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +/* + * FastPath char[]->byte[] encoder, REPLACE on malformed input or + * unmappable input. + */ + +public interface ArrayEncoder { + int encode(char[] src, int off, int len, byte[] dst); +} diff --git a/src/sun/nio/cs/CESU_8.java b/src/sun/nio/cs/CESU_8.java new file mode 100644 index 00000000..40711f83 --- /dev/null +++ b/src/sun/nio/cs/CESU_8.java @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/* Legal CESU-8 Byte Sequences + * + * # Code Points Bits Bit/Byte pattern + * 1 7 0xxxxxxx + * U+0000..U+007F 00..7F + * + * 2 11 110xxxxx 10xxxxxx + * U+0080..U+07FF C2..DF 80..BF + * + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+FFFF E1..EF 80..BF 80..BF + * + */ + +class CESU_8 extends Unicode +{ + public CESU_8() { + super("CESU-8", StandardCharsets.aliases_CESU_8); + } + + public String historicalName() { + return "CESU8"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static final void updatePositions(Buffer src, int sp, + Buffer dst, int dp) { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + + private static class Decoder extends CharsetDecoder + implements ArrayDecoder { + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private static boolean isNotContinuation(int b) { + return (b & 0xc0) != 0x80; + } + + // [E0] [A0..BF] [80..BF] + // [E1..EF] [80..BF] [80..BF] + private static boolean isMalformed3(int b1, int b2, int b3) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80; + } + + // only used when there is only one byte left in src buffer + private static boolean isMalformed3_2(int b1, int b2) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80; + } + + + // [F0] [90..BF] [80..BF] [80..BF] + // [F1..F3] [80..BF] [80..BF] [80..BF] + // [F4] [80..8F] [80..BF] [80..BF] + // only check 80-be range here, the [0xf0,0x80...] and [0xf4,0x90-...] + // will be checked by Character.isSupplementaryCodePoint(uc) + private static boolean isMalformed4(int b2, int b3, int b4) { + return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (b4 & 0xc0) != 0x80; + } + + // only used when there is less than 4 bytes left in src buffer + private static boolean isMalformed4_2(int b1, int b2) { + return (b1 == 0xf0 && b2 == 0x90) || + (b2 & 0xc0) != 0x80; + } + + private static boolean isMalformed4_3(int b3) { + return (b3 & 0xc0) != 0x80; + } + + private static CoderResult malformedN(ByteBuffer src, int nb) { + switch (nb) { + case 1: + case 2: // always 1 + return CoderResult.malformedForLength(1); + case 3: + int b1 = src.get(); + int b2 = src.get(); // no need to lookup b3 + return CoderResult.malformedForLength( + ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + isNotContinuation(b2)) ? 1 : 2); + case 4: // we don't care the speed here + b1 = src.get() & 0xff; + b2 = src.get() & 0xff; + if (b1 > 0xf4 || + (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + isNotContinuation(b2)) + return CoderResult.malformedForLength(1); + if (isNotContinuation(src.get())) + return CoderResult.malformedForLength(2); + return CoderResult.malformedForLength(3); + default: + assert false; + return null; + } + } + + private static CoderResult malformed(ByteBuffer src, int sp, + CharBuffer dst, int dp, + int nb) + { + src.position(sp - src.arrayOffset()); + CoderResult cr = malformedN(src, nb); + updatePositions(src, sp, dst, dp); + return cr; + } + + + private static CoderResult malformed(ByteBuffer src, + int mark, int nb) + { + src.position(mark); + CoderResult cr = malformedN(src, nb); + src.position(mark); + return cr; + } + + private static CoderResult malformedForLength(ByteBuffer src, + int sp, + CharBuffer dst, + int dp, + int malformedNB) + { + updatePositions(src, sp, dst, dp); + return CoderResult.malformedForLength(malformedNB); + } + + private static CoderResult malformedForLength(ByteBuffer src, + int mark, + int malformedNB) + { + src.position(mark); + return CoderResult.malformedForLength(malformedNB); + } + + + private static CoderResult xflow(Buffer src, int sp, int sl, + Buffer dst, int dp, int nb) { + updatePositions(src, sp, dst, dp); + return (nb == 0 || sl - sp < nb) + ? CoderResult.UNDERFLOW : CoderResult.OVERFLOW; + } + + private static CoderResult xflow(Buffer src, int mark, int nb) { + src.position(mark); + return (nb == 0 || src.remaining() < nb) + ? CoderResult.UNDERFLOW : CoderResult.OVERFLOW; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + // This method is optimized for ASCII input. + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + // ASCII only loop + while (dp < dlASCII && sa[sp] >= 0) + da[dp++] = (char) sa[sp++]; + while (sp < sl) { + int b1 = sa[sp]; + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dp >= dl) + return xflow(src, sp, sl, dst, dp, 1); + da[dp++] = (char) b1; + sp++; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (sl - sp < 2 || dp >= dl) + return xflow(src, sp, sl, dst, dp, 2); + int b2 = sa[sp + 1]; + if (isNotContinuation(b2)) + return malformedForLength(src, sp, dst, dp, 1); + da[dp++] = (char) (((b1 << 6) ^ b2) + ^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + sp += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + int srcRemaining = sl - sp; + if (srcRemaining < 3 || dp >= dl) { + if (srcRemaining > 1 && isMalformed3_2(b1, sa[sp + 1])) + return malformedForLength(src, sp, dst, dp, 1); + return xflow(src, sp, sl, dst, dp, 3); + } + int b2 = sa[sp + 1]; + int b3 = sa[sp + 2]; + if (isMalformed3(b1, b2, b3)) + return malformed(src, sp, dst, dp, 3); + da[dp++] = (char) + ((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + sp += 3; + } else { + return malformed(src, sp, dst, dp, 1); + } + } + return xflow(src, sp, sl, dst, dp, 0); + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + int limit = src.limit(); + while (mark < limit) { + int b1 = src.get(); + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dst.remaining() < 1) + return xflow(src, mark, 1); // overflow + dst.put((char) b1); + mark++; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (limit - mark < 2|| dst.remaining() < 1) + return xflow(src, mark, 2); + int b2 = src.get(); + if (isNotContinuation(b2)) + return malformedForLength(src, mark, 1); + dst.put((char) (((b1 << 6) ^ b2) + ^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0)))); + mark += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + int srcRemaining = limit - mark; + if (srcRemaining < 3 || dst.remaining() < 1) { + if (srcRemaining > 1 && isMalformed3_2(b1, src.get())) + return malformedForLength(src, mark, 1); + return xflow(src, mark, 3); + } + int b2 = src.get(); + int b3 = src.get(); + if (isMalformed3(b1, b2, b3)) + return malformed(src, mark, 3); + dst.put((char) + ((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0))))); + mark += 3; + } else { + return malformed(src, mark, 1); + } + } + return xflow(src, mark, 0); + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + private static ByteBuffer getByteBuffer(ByteBuffer bb, byte[] ba, int sp) + { + if (bb == null) + bb = ByteBuffer.wrap(ba); + bb.position(sp); + return bb; + } + + // returns -1 if there is/are malformed byte(s) and the + // "action" for malformed input is not REPLACE. + public int decode(byte[] sa, int sp, int len, char[] da) { + final int sl = sp + len; + int dp = 0; + int dlASCII = Math.min(len, da.length); + ByteBuffer bb = null; // only necessary if malformed + + // ASCII only optimized loop + while (dp < dlASCII && sa[sp] >= 0) + da[dp++] = (char) sa[sp++]; + + while (sp < sl) { + int b1 = sa[sp++]; + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + da[dp++] = (char) b1; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (sp < sl) { + int b2 = sa[sp++]; + if (isNotContinuation(b2)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + sp--; // malformedN(bb, 2) always returns 1 + } else { + da[dp++] = (char) (((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + } + continue; + } + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + return dp; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + if (sp + 1 < sl) { + int b2 = sa[sp++]; + int b3 = sa[sp++]; + if (isMalformed3(b1, b2, b3)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + sp -=3; + bb = getByteBuffer(bb, sa, sp); + sp += malformedN(bb, 3).length(); + } else { + da[dp++] = (char)((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + } + continue; + } + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + if (sp < sl && isMalformed3_2(b1, sa[sp])) { + da[dp++] = replacement().charAt(0); + continue; + + } + da[dp++] = replacement().charAt(0); + return dp; + } else { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + } + } + return dp; + } + } + + private static class Encoder extends CharsetEncoder + implements ArrayEncoder { + + private Encoder(Charset cs) { + super(cs, 1.1f, 3.0f); + } + + public boolean canEncode(char c) { + return !Character.isSurrogate(c); + } + + public boolean isLegalReplacement(byte[] repl) { + return ((repl.length == 1 && repl[0] >= 0) || + super.isLegalReplacement(repl)); + } + + private static CoderResult overflow(CharBuffer src, int sp, + ByteBuffer dst, int dp) { + updatePositions(src, sp, dst, dp); + return CoderResult.OVERFLOW; + } + + private static CoderResult overflow(CharBuffer src, int mark) { + src.position(mark); + return CoderResult.OVERFLOW; + } + + private static void to3Bytes(byte[] da, int dp, char c) { + da[dp] = (byte)(0xe0 | ((c >> 12))); + da[dp + 1] = (byte)(0x80 | ((c >> 6) & 0x3f)); + da[dp + 2] = (byte)(0x80 | (c & 0x3f)); + } + + private static void to3Bytes(ByteBuffer dst, char c) { + dst.put((byte)(0xe0 | ((c >> 12)))); + dst.put((byte)(0x80 | ((c >> 6) & 0x3f))); + dst.put((byte)(0x80 | (c & 0x3f))); + } + + private Surrogate.Parser sgp; + private char[] c2; + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + // ASCII only loop + while (dp < dlASCII && sa[sp] < '\u0080') + da[dp++] = (byte) sa[sp++]; + while (sp < sl) { + char c = sa[sp]; + if (c < 0x80) { + // Have at most seven bits + if (dp >= dl) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)c; + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dl - dp < 2) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xc0 | (c >> 6)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + // Have a surrogate pair + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, sa, sp, sl); + if (uc < 0) { + updatePositions(src, sp, dst, dp); + return sgp.error(); + } + if (dl - dp < 6) + return overflow(src, sp, dst, dp); + to3Bytes(da, dp, Character.highSurrogate(uc)); + dp += 3; + to3Bytes(da, dp, Character.lowSurrogate(uc)); + dp += 3; + sp++; // 2 chars + } else { + // 3 bytes, 16 bits + if (dl - dp < 3) + return overflow(src, sp, dst, dp); + to3Bytes(da, dp, c); + dp += 3; + } + sp++; + } + updatePositions(src, sp, dst, dp); + return CoderResult.UNDERFLOW; + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + while (src.hasRemaining()) { + char c = src.get(); + if (c < 0x80) { + // Have at most seven bits + if (!dst.hasRemaining()) + return overflow(src, mark); + dst.put((byte)c); + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dst.remaining() < 2) + return overflow(src, mark); + dst.put((byte)(0xc0 | (c >> 6))); + dst.put((byte)(0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { + // Have a surrogate pair + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, src); + if (uc < 0) { + src.position(mark); + return sgp.error(); + } + if (dst.remaining() < 6) + return overflow(src, mark); + to3Bytes(dst, Character.highSurrogate(uc)); + to3Bytes(dst, Character.lowSurrogate(uc)); + mark++; // 2 chars + } else { + // 3 bytes, 16 bits + if (dst.remaining() < 3) + return overflow(src, mark); + to3Bytes(dst, c); + } + mark++; + } + src.position(mark); + return CoderResult.UNDERFLOW; + } + + protected final CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + // returns -1 if there is malformed char(s) and the + // "action" for malformed input is not REPLACE. + public int encode(char[] sa, int sp, int len, byte[] da) { + int sl = sp + len; + int dp = 0; + int dlASCII = dp + Math.min(len, da.length); + + // ASCII only optimized loop + while (dp < dlASCII && sa[sp] < '\u0080') + da[dp++] = (byte) sa[sp++]; + + while (sp < sl) { + char c = sa[sp++]; + if (c < 0x80) { + // Have at most seven bits + da[dp++] = (byte)c; + } else if (c < 0x800) { + // 2 bytes, 11 bits + da[dp++] = (byte)(0xc0 | (c >> 6)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, sa, sp - 1, sl); + if (uc < 0) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement()[0]; + } else { + to3Bytes(da, dp, Character.highSurrogate(uc)); + dp += 3; + to3Bytes(da, dp, Character.lowSurrogate(uc)); + dp += 3; + sp++; // 2 chars + } + } else { + // 3 bytes, 16 bits + to3Bytes(da, dp, c); + dp += 3; + } + } + return dp; + } + } +} diff --git a/src/sun/nio/cs/CharsetMapping.java b/src/sun/nio/cs/CharsetMapping.java new file mode 100644 index 00000000..f23d62db --- /dev/null +++ b/src/sun/nio/cs/CharsetMapping.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Comparator; + +public class CharsetMapping { + public final static char UNMAPPABLE_DECODING = '\uFFFD'; + public final static int UNMAPPABLE_ENCODING = 0xFFFD; + + char[] b2cSB; //singlebyte b->c + char[] b2cDB1; //dobulebyte b->c /db1 + char[] b2cDB2; //dobulebyte b->c /db2 + + int b2Min, b2Max; //min/max(start/end) value of 2nd byte + int b1MinDB1, b1MaxDB1; //min/Max(start/end) value of 1st byte/db1 + int b1MinDB2, b1MaxDB2; //min/Max(start/end) value of 1st byte/db2 + int dbSegSize; + + char[] c2b; + char[] c2bIndex; + + // Supplementary + char[] b2cSupp; + char[] c2bSupp; + + // Composite + Entry[] b2cComp; + Entry[] c2bComp; + + public char decodeSingle(int b) { + return b2cSB[b]; + } + + public char decodeDouble(int b1, int b2) { + if (b2 >= b2Min && b2 < b2Max) { + b2 -= b2Min; + if (b1 >= b1MinDB1 && b1 <= b1MaxDB1) { + b1 -= b1MinDB1; + return b2cDB1[b1 * dbSegSize + b2]; + } + if (b1 >= b1MinDB2 && b1 <= b1MaxDB2) { + b1 -= b1MinDB2; + return b2cDB2[b1 * dbSegSize + b2]; + } + } + return UNMAPPABLE_DECODING; + } + + // for jis0213 all supplementary characters are in 0x2xxxx range, + // so only the xxxx part is now stored, should actually store the + // codepoint value instead. + public char[] decodeSurrogate(int db, char[] cc) { + int end = b2cSupp.length / 2; + int i = Arrays.binarySearch(b2cSupp, 0, end, (char)db); + if (i >= 0) { + Character.toChars(b2cSupp[end + i] + 0x20000, cc, 0); + return cc; + } + return null; + } + + public char[] decodeComposite(Entry comp, char[] cc) { + int i = findBytes(b2cComp, comp); + if (i >= 0) { + cc[0] = (char)b2cComp[i].cp; + cc[1] = (char)b2cComp[i].cp2; + return cc; + } + return null; + } + + public int encodeChar(char ch) { + int index = c2bIndex[ch >> 8]; + if (index == 0xffff) + return UNMAPPABLE_ENCODING; + return c2b[index + (ch & 0xff)]; + } + + public int encodeSurrogate(char hi, char lo) { + int cp = Character.toCodePoint(hi, lo); + if (cp < 0x20000 || cp >= 0x30000) + return UNMAPPABLE_ENCODING; + int end = c2bSupp.length / 2; + int i = Arrays.binarySearch(c2bSupp, 0, end, (char)cp); + if (i >= 0) + return c2bSupp[end + i]; + return UNMAPPABLE_ENCODING; + } + + public boolean isCompositeBase(Entry comp) { + if (comp.cp <= 0x31f7 && comp.cp >= 0xe6) { + return (findCP(c2bComp, comp) >= 0); + } + return false; + } + + public int encodeComposite(Entry comp) { + int i = findComp(c2bComp, comp); + if (i >= 0) + return c2bComp[i].bs; + return UNMAPPABLE_ENCODING; + } + + // init the CharsetMapping object from the .dat binary file + public static CharsetMapping get(final InputStream is) { + return AccessController.doPrivileged(new PrivilegedAction() { + public CharsetMapping run() { + return new CharsetMapping().load(is); + } + }); + } + + public static class Entry { + public int bs; //byte sequence reps + public int cp; //Unicode codepoint + public int cp2; //CC of composite + } + + static Comparator comparatorBytes = + new Comparator() { + public int compare(Entry m1, Entry m2) { + return m1.bs - m2.bs; + } + public boolean equals(Object obj) { + return this == obj; + } + }; + + static Comparator comparatorCP = + new Comparator() { + public int compare(Entry m1, Entry m2) { + return m1.cp - m2.cp; + } + public boolean equals(Object obj) { + return this == obj; + } + }; + + static Comparator comparatorComp = + new Comparator() { + public int compare(Entry m1, Entry m2) { + int v = m1.cp - m2.cp; + if (v == 0) + v = m1.cp2 - m2.cp2; + return v; + } + public boolean equals(Object obj) { + return this == obj; + } + }; + + static int findBytes(Entry[] a, Entry k) { + return Arrays.binarySearch(a, 0, a.length, k, comparatorBytes); + } + + static int findCP(Entry[] a, Entry k) { + return Arrays.binarySearch(a, 0, a.length, k, comparatorCP); + } + + static int findComp(Entry[] a, Entry k) { + return Arrays.binarySearch(a, 0, a.length, k, comparatorComp); + } + + /*****************************************************************************/ + // tags of different charset mapping tables + private final static int MAP_SINGLEBYTE = 0x1; // 0..256 : c + private final static int MAP_DOUBLEBYTE1 = 0x2; // min..max: c + private final static int MAP_DOUBLEBYTE2 = 0x3; // min..max: c [DB2] + private final static int MAP_SUPPLEMENT = 0x5; // db,c + private final static int MAP_SUPPLEMENT_C2B = 0x6; // c,db + private final static int MAP_COMPOSITE = 0x7; // db,base,cc + private final static int MAP_INDEXC2B = 0x8; // index table of c->bb + + private static final boolean readNBytes(InputStream in, byte[] bb, int N) + throws IOException + { + int off = 0; + while (N > 0) { + int n = in.read(bb, off, N); + if (n == -1) + return false; + N = N - n; + off += n; + } + return true; + } + + int off = 0; + byte[] bb; + private char[] readCharArray() { + // first 2 bytes are the number of "chars" stored in this table + int size = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + char [] cc = new char[size]; + for (int i = 0; i < size; i++) { + cc[i] = (char)(((bb[off++]&0xff)<<8) | (bb[off++]&0xff)); + } + return cc; + } + + void readSINGLEBYTE() { + char[] map = readCharArray(); + for (int i = 0; i < map.length; i++) { + char c = map[i]; + if (c != UNMAPPABLE_DECODING) { + c2b[c2bIndex[c >> 8] + (c&0xff)] = (char)i; + } + } + b2cSB = map; + } + + void readINDEXC2B() { + char[] map = readCharArray(); + for (int i = map.length - 1; i >= 0; i--) { + if (c2b == null && map[i] != -1) { + c2b = new char[map[i] + 256]; + Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING); + break; + } + } + c2bIndex = map; + } + + char[] readDB(int b1Min, int b2Min, int segSize) { + char[] map = readCharArray(); + for (int i = 0; i < map.length; i++) { + char c = map[i]; + if (c != UNMAPPABLE_DECODING) { + int b1 = i / segSize; + int b2 = i % segSize; + int b = (b1 + b1Min)* 256 + (b2 + b2Min); + //System.out.printf(" DB %x\t%x%n", b, c & 0xffff); + c2b[c2bIndex[c >> 8] + (c&0xff)] = (char)(b); + } + } + return map; + } + + void readDOUBLEBYTE1() { + b1MinDB1 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b1MaxDB1 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b2Min = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b2Max = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + dbSegSize = b2Max - b2Min + 1; + b2cDB1 = readDB(b1MinDB1, b2Min, dbSegSize); + } + + void readDOUBLEBYTE2() { + b1MinDB2 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b1MaxDB2 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b2Min = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + b2Max = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + dbSegSize = b2Max - b2Min + 1; + b2cDB2 = readDB(b1MinDB2, b2Min, dbSegSize); + } + + void readCOMPOSITE() { + char[] map = readCharArray(); + int mLen = map.length/3; + b2cComp = new Entry[mLen]; + c2bComp = new Entry[mLen]; + for (int i = 0, j= 0; i < mLen; i++) { + Entry m = new Entry(); + m.bs = map[j++]; + m.cp = map[j++]; + m.cp2 = map[j++]; + b2cComp[i] = m; + c2bComp[i] = m; + } + Arrays.sort(c2bComp, 0, c2bComp.length, comparatorComp); + } + + CharsetMapping load(InputStream in) { + try { + // The first 4 bytes are the size of the total data followed in + // this .dat file. + int len = ((in.read()&0xff) << 24) | ((in.read()&0xff) << 16) | + ((in.read()&0xff) << 8) | (in.read()&0xff); + bb = new byte[len]; + off = 0; + //System.out.printf("In : Total=%d%n", len); + // Read in all bytes + if (!readNBytes(in, bb, len)) + throw new RuntimeException("Corrupted data file"); + in.close(); + + while (off < len) { + int type = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff); + switch(type) { + case MAP_INDEXC2B: + readINDEXC2B(); + break; + case MAP_SINGLEBYTE: + readSINGLEBYTE(); + break; + case MAP_DOUBLEBYTE1: + readDOUBLEBYTE1(); + break; + case MAP_DOUBLEBYTE2: + readDOUBLEBYTE2(); + break; + case MAP_SUPPLEMENT: + b2cSupp = readCharArray(); + break; + case MAP_SUPPLEMENT_C2B: + c2bSupp = readCharArray(); + break; + case MAP_COMPOSITE: + readCOMPOSITE(); + break; + default: + throw new RuntimeException("Corrupted data file"); + } + } + bb = null; + return this; + } catch (IOException x) { + x.printStackTrace(); + return null; + } + } +} diff --git a/src/sun/nio/cs/FastCharsetProvider.java b/src/sun/nio/cs/FastCharsetProvider.java new file mode 100644 index 00000000..c9fceab6 --- /dev/null +++ b/src/sun/nio/cs/FastCharsetProvider.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.spi.CharsetProvider; +import java.util.Iterator; +import java.util.Map; + + +/** + * Abstract base class for fast charset providers. + * + * @author Mark Reinhold + */ + +public class FastCharsetProvider + extends CharsetProvider +{ + + // Maps canonical names to class names + private Map classMap; + + // Maps alias names to canonical names + private Map aliasMap; + + // Maps canonical names to cached instances + private Map cache; + + private String packagePrefix; + + protected FastCharsetProvider(String pp, + Map am, + Map cm, + Map c) + { + packagePrefix = pp; + aliasMap = am; + classMap = cm; + cache = c; + } + + private String canonicalize(String csn) { + String acn = aliasMap.get(csn); + return (acn != null) ? acn : csn; + } + + // Private ASCII-only version, optimized for interpretation during startup + // + private static String toLower(String s) { + int n = s.length(); + boolean allLower = true; + for (int i = 0; i < n; i++) { + int c = s.charAt(i); + if (((c - 'A') | ('Z' - c)) >= 0) { + allLower = false; + break; + } + } + if (allLower) + return s; + char[] ca = new char[n]; + for (int i = 0; i < n; i++) { + int c = s.charAt(i); + if (((c - 'A') | ('Z' - c)) >= 0) + ca[i] = (char)(c + 0x20); + else + ca[i] = (char)c; + } + return new String(ca); + } + + private Charset lookup(String charsetName) { + + String csn = canonicalize(toLower(charsetName)); + + // Check cache first + Charset cs = cache.get(csn); + if (cs != null) + return cs; + + // Do we even support this charset? + String cln = classMap.get(csn); + if (cln == null) + return null; + + if (cln.equals("US_ASCII")) { + cs = new US_ASCII(); + cache.put(csn, cs); + return cs; + } + + // Instantiate the charset and cache it + try { + Class c = Class.forName(packagePrefix + "." + cln, + true, + this.getClass().getClassLoader()); + cs = (Charset)c.newInstance(); + cache.put(csn, cs); + return cs; + } catch (ClassNotFoundException | + IllegalAccessException | + InstantiationException x) { + return null; + } + } + + public final Charset charsetForName(String charsetName) { + synchronized (this) { + return lookup(canonicalize(charsetName)); + } + } + + public final Iterator charsets() { + + return new Iterator() { + + Iterator i = classMap.keySet().iterator(); + + public boolean hasNext() { + return i.hasNext(); + } + + public Charset next() { + String csn = i.next(); + return lookup(csn); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + + } + +} diff --git a/src/sun/nio/cs/HistoricallyNamedCharset.java b/src/sun/nio/cs/HistoricallyNamedCharset.java new file mode 100644 index 00000000..c8c6c20b --- /dev/null +++ b/src/sun/nio/cs/HistoricallyNamedCharset.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + + +public interface HistoricallyNamedCharset { + + public String historicalName(); + +} diff --git a/src/sun/nio/cs/ISO_8859_1.java b/src/sun/nio/cs/ISO_8859_1.java new file mode 100644 index 00000000..cf8103ba --- /dev/null +++ b/src/sun/nio/cs/ISO_8859_1.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +class ISO_8859_1 + extends Charset + implements HistoricallyNamedCharset +{ + + public ISO_8859_1() { + super("ISO-8859-1", StandardCharsets.aliases_ISO_8859_1); + } + + public String historicalName() { + return "ISO8859_1"; + } + + public boolean contains(Charset cs) { + return ((cs instanceof US_ASCII) + || (cs instanceof ISO_8859_1)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends CharsetDecoder + implements ArrayDecoder { + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + byte b = sa[sp]; + if (dp >= dl) + return CoderResult.OVERFLOW; + da[dp++] = (char)(b & 0xff); + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + byte b = src.get(); + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((char)(b & 0xff)); + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + if (len > dst.length) + len = dst.length; + int dp = 0; + while (dp < len) + dst[dp++] = (char)(src[sp++] & 0xff); + return dp; + } + } + + private static class Encoder extends CharsetEncoder + implements ArrayEncoder { + private Encoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + public boolean canEncode(char c) { + return c <= '\u00FF'; + } + + public boolean isLegalReplacement(byte[] repl) { + return true; // we accept any byte value + } + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + // JVM may replace this method with intrinsic code. + private static int encodeISOArray(char[] sa, int sp, + byte[] da, int dp, int len) + { + int i = 0; + for (; i < len; i++) { + char c = sa[sp++]; + if (c > '\u00FF') + break; + da[dp++] = (byte)c; + } + return i; + } + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int soff = src.arrayOffset(); + int sp = soff + src.position(); + int sl = soff + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int doff = dst.arrayOffset(); + int dp = doff + dst.position(); + int dl = doff + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + int dlen = dl - dp; + int slen = sl - sp; + int len = (dlen < slen) ? dlen : slen; + try { + int ret = encodeISOArray(sa, sp, da, dp, len); + sp = sp + ret; + dp = dp + ret; + if (ret != len) { + if (sgp.parse(sa[sp], sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + if (len < slen) + return CoderResult.OVERFLOW; + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - soff); + dst.position(dp - doff); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + if (c <= '\u00FF') { + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((byte)c); + mark++; + continue; + } + if (sgp.parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + private byte repl = (byte)'?'; + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement[0]; + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int slen = Math.min(len, dst.length); + int sl = sp + slen; + while (sp < sl) { + int ret = encodeISOArray(src, sp, dst, dp, slen); + sp = sp + ret; + dp = dp + ret; + if (ret != slen) { + char c = src[sp++]; + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + if (len > dst.length) { + sl++; + len--; + } + sp++; + } + dst[dp++] = repl; + slen = Math.min((sl - sp), (dst.length - dp)); + } + } + return dp; + } + } +} diff --git a/src/sun/nio/cs/SingleByte.java b/src/sun/nio/cs/SingleByte.java new file mode 100644 index 00000000..bbf978ff --- /dev/null +++ b/src/sun/nio/cs/SingleByte.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +public class SingleByte +{ + private static final CoderResult withResult(CoderResult cr, + Buffer src, int sp, + Buffer dst, int dp) + { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + return cr; + } + + final public static class Decoder extends CharsetDecoder + implements ArrayDecoder { + private final char[] b2c; + + public Decoder(Charset cs, char[] b2c) { + super(cs, 1.0f, 1.0f); + this.b2c = b2c; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + CoderResult cr = CoderResult.UNDERFLOW; + if ((dl - dp) < (sl - sp)) { + sl = sp + (dl - dp); + cr = CoderResult.OVERFLOW; + } + + while (sp < sl) { + char c = decode(sa[sp]); + if (c == UNMAPPABLE_DECODING) { + return withResult(CoderResult.unmappableForLength(1), + src, sp, dst, dp); + } + da[dp++] = c; + sp++; + } + return withResult(cr, src, sp, dst, dp); + } + + private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = decode(src.get()); + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(1); + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put(c); + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + public final char decode(int b) { + return b2c[b + 128]; + } + + private char repl = '\uFFFD'; + protected void implReplaceWith(String newReplacement) { + repl = newReplacement.charAt(0); + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + if (len > dst.length) + len = dst.length; + int dp = 0; + while (dp < len) { + dst[dp] = decode(src[sp++]); + if (dst[dp] == UNMAPPABLE_DECODING) { + dst[dp] = repl; + } + dp++; + } + return dp; + } + } + + final public static class Encoder extends CharsetEncoder + implements ArrayEncoder { + private Surrogate.Parser sgp; + private final char[] c2b; + private final char[] c2bIndex; + + public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + super(cs, 1.0f, 1.0f); + this.c2b = c2b; + this.c2bIndex = c2bIndex; + } + + public boolean canEncode(char c) { + return encode(c) != UNMAPPABLE_ENCODING; + } + + public boolean isLegalReplacement(byte[] repl) { + return ((repl.length == 1 && repl[0] == (byte)'?') || + super.isLegalReplacement(repl)); + } + + private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + CoderResult cr = CoderResult.UNDERFLOW; + if ((dl - dp) < (sl - sp)) { + sl = sp + (dl - dp); + cr = CoderResult.OVERFLOW; + } + + while (sp < sl) { + char c = sa[sp]; + int b = encode(c); + if (b == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp == null) + sgp = new Surrogate.Parser(); + if (sgp.parse(c, sa, sp, sl) < 0) + return withResult(sgp.error(), src, sp, dst, dp); + return withResult(sgp.unmappableResult(), src, sp, dst, dp); + } + return withResult(CoderResult.unmappableForLength(1), + src, sp, dst, dp); + } + da[dp++] = (byte)b; + sp++; + } + return withResult(cr, src, sp, dst, dp); + } + + private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + int b = encode(c); + if (b == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp == null) + sgp = new Surrogate.Parser(); + if (sgp.parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.unmappableForLength(1); + } + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((byte)b); + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + public final int encode(char ch) { + char index = c2bIndex[ch >> 8]; + if (index == UNMAPPABLE_ENCODING) + return UNMAPPABLE_ENCODING; + return c2b[index + (ch & 0xff)]; + } + + private byte repl = (byte)'?'; + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement[0]; + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = src[sp++]; + int b = encode(c); + if (b != UNMAPPABLE_ENCODING) { + dst[dp++] = (byte)b; + continue; + } + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + if (len > dst.length) { + sl++; + len--; + } + sp++; + } + dst[dp++] = repl; + } + return dp; + } + } + + // init the c2b and c2bIndex tables from b2c. + public static void initC2B(char[] b2c, char[] c2bNR, + char[] c2b, char[] c2bIndex) { + for (int i = 0; i < c2bIndex.length; i++) + c2bIndex[i] = UNMAPPABLE_ENCODING; + for (int i = 0; i < c2b.length; i++) + c2b[i] = UNMAPPABLE_ENCODING; + int off = 0; + for (int i = 0; i < b2c.length; i++) { + char c = b2c[i]; + if (c == UNMAPPABLE_DECODING) + continue; + int index = (c >> 8); + if (c2bIndex[index] == UNMAPPABLE_ENCODING) { + c2bIndex[index] = (char)off; + off += 0x100; + } + index = c2bIndex[index] + (c & 0xff); + c2b[index] = (char)((i>=0x80)?(i-0x80):(i+0x80)); + } + if (c2bNR != null) { + // c-->b nr entries + int i = 0; + while (i < c2bNR.length) { + char b = c2bNR[i++]; + char c = c2bNR[i++]; + int index = (c >> 8); + if (c2bIndex[index] == UNMAPPABLE_ENCODING) { + c2bIndex[index] = (char)off; + off += 0x100; + } + index = c2bIndex[index] + (c & 0xff); + c2b[index] = b; + } + } + } +} diff --git a/src/sun/nio/cs/StreamDecoder.java b/src/sun/nio/cs/StreamDecoder.java new file mode 100644 index 00000000..b4c5d25a --- /dev/null +++ b/src/sun/nio/cs/StreamDecoder.java @@ -0,0 +1,391 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.IllegalCharsetNameException; + +public class StreamDecoder extends Reader +{ + + private static final int MIN_BYTE_BUFFER_SIZE = 32; + private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; + + private volatile boolean isOpen = true; + + private void ensureOpen() throws IOException { + if (!isOpen) + throw new IOException("Stream closed"); + } + + // In order to handle surrogates properly we must never try to produce + // fewer than two characters at a time. If we're only asked to return one + // character then the other is saved here to be returned later. + // + private boolean haveLeftoverChar = false; + private char leftoverChar; + + + // Factories for java.io.InputStreamReader + + public static StreamDecoder forInputStreamReader(InputStream in, + Object lock, + String charsetName) + throws UnsupportedEncodingException + { + String csn = charsetName; + if (csn == null) + csn = Charset.defaultCharset().name(); + try { + if (Charset.isSupported(csn)) + return new StreamDecoder(in, lock, Charset.forName(csn)); + } catch (IllegalCharsetNameException x) { } + throw new UnsupportedEncodingException (csn); + } + + public static StreamDecoder forInputStreamReader(InputStream in, + Object lock, + Charset cs) + { + return new StreamDecoder(in, lock, cs); + } + + public static StreamDecoder forInputStreamReader(InputStream in, + Object lock, + CharsetDecoder dec) + { + return new StreamDecoder(in, lock, dec); + } + + + // Factory for java.nio.channels.Channels.newReader + + public static StreamDecoder forDecoder(ReadableByteChannel ch, + CharsetDecoder dec, + int minBufferCap) + { + return new StreamDecoder(ch, dec, minBufferCap); + } + + + // -- Public methods corresponding to those in InputStreamReader -- + + // All synchronization and state/argument checking is done in these public + // methods; the concrete stream-decoder subclasses defined below need not + // do any such checking. + + public String getEncoding() { + if (isOpen()) + return encodingName(); + return null; + } + + public int read() throws IOException { + return read0(); + } + + @SuppressWarnings("fallthrough") + private int read0() throws IOException { + synchronized (lock) { + + // Return the leftover char, if there is one + if (haveLeftoverChar) { + haveLeftoverChar = false; + return leftoverChar; + } + + // Convert more bytes + char cb[] = new char[2]; + int n = read(cb, 0, 2); + switch (n) { + case -1: + return -1; + case 2: + leftoverChar = cb[1]; + haveLeftoverChar = true; + // FALL THROUGH + case 1: + return cb[0]; + default: + assert false : n; + return -1; + } + } + } + + public int read(char cbuf[], int offset, int length) throws IOException { + int off = offset; + int len = length; + synchronized (lock) { + ensureOpen(); + if ((off < 0) || (off > cbuf.length) || (len < 0) || + ((off + len) > cbuf.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } + if (len == 0) + return 0; + + int n = 0; + + if (haveLeftoverChar) { + // Copy the leftover char into the buffer + cbuf[off] = leftoverChar; + off++; len--; + haveLeftoverChar = false; + n = 1; + if ((len == 0) || !implReady()) + // Return now if this is all we can produce w/o blocking + return n; + } + + if (len == 1) { + // Treat single-character array reads just like read() + int c = read0(); + if (c == -1) + return (n == 0) ? -1 : n; + cbuf[off] = (char)c; + return n + 1; + } + + return n + implRead(cbuf, off, off + len); + } + } + + public boolean ready() throws IOException { + synchronized (lock) { + ensureOpen(); + return haveLeftoverChar || implReady(); + } + } + + public void close() throws IOException { + synchronized (lock) { + if (!isOpen) + return; + implClose(); + isOpen = false; + } + } + + private boolean isOpen() { + return isOpen; + } + + + // -- Charset-based stream decoder impl -- + + // In the early stages of the build we haven't yet built the NIO native + // code, so guard against that by catching the first UnsatisfiedLinkError + // and setting this flag so that later attempts fail quickly. + // + private static volatile boolean channelsAvailable = true; + + private static FileChannel getChannel(FileInputStream in) { + if (!channelsAvailable) + return null; + try { + return in.getChannel(); + } catch (UnsatisfiedLinkError x) { + channelsAvailable = false; + return null; + } + } + + private Charset cs; + private CharsetDecoder decoder; + private ByteBuffer bb; + + // Exactly one of these is non-null + private InputStream in; + private ReadableByteChannel ch; + + StreamDecoder(InputStream in, Object lock, Charset cs) { + this(in, lock, + cs.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE)); + } + + StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) { + super(lock); + this.cs = dec.charset(); + this.decoder = dec; + + // This path disabled until direct buffers are faster + if (false && in instanceof FileInputStream) { + ch = getChannel((FileInputStream)in); + if (ch != null) + bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE); + } + if (ch == null) { + this.in = in; + this.ch = null; + bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE); + } + bb.flip(); // So that bb is initially empty + } + + StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) { + this.in = null; + this.ch = ch; + this.decoder = dec; + this.cs = dec.charset(); + this.bb = ByteBuffer.allocate(mbc < 0 + ? DEFAULT_BYTE_BUFFER_SIZE + : (mbc < MIN_BYTE_BUFFER_SIZE + ? MIN_BYTE_BUFFER_SIZE + : mbc)); + bb.flip(); + } + + private int readBytes() throws IOException { + bb.compact(); + try { + if (ch != null) { + // Read from the channel + int n = ch.read(bb); + if (n < 0) + return n; + } else { + // Read from the input stream, and then update the buffer + int lim = bb.limit(); + int pos = bb.position(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + assert rem > 0; + int n = in.read(bb.array(), bb.arrayOffset() + pos, rem); + if (n < 0) + return n; + if (n == 0) + throw new IOException("Underlying input stream returned zero bytes"); + assert (n <= rem) : "n = " + n + ", rem = " + rem; + bb.position(pos + n); + } + } finally { + // Flip even when an IOException is thrown, + // otherwise the stream will stutter + bb.flip(); + } + + int rem = bb.remaining(); + assert (rem != 0) : rem; + return rem; + } + + int implRead(char[] cbuf, int off, int end) throws IOException { + + // In order to handle surrogate pairs, this method requires that + // the invoker attempt to read at least two characters. Saving the + // extra character, if any, at a higher level is easier than trying + // to deal with it here. + assert (end - off > 1); + + CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off); + if (cb.position() != 0) + // Ensure that cb[0] == cbuf[off] + cb = cb.slice(); + + boolean eof = false; + for (;;) { + CoderResult cr = decoder.decode(bb, cb, eof); + if (cr.isUnderflow()) { + if (eof) + break; + if (!cb.hasRemaining()) + break; + if ((cb.position() > 0) && !inReady()) + break; // Block at most once + int n = readBytes(); + if (n < 0) { + eof = true; + if ((cb.position() == 0) && (!bb.hasRemaining())) + break; + decoder.reset(); + } + continue; + } + if (cr.isOverflow()) { + assert cb.position() > 0; + break; + } + cr.throwException(); + } + + if (eof) { + // ## Need to flush decoder + decoder.reset(); + } + + if (cb.position() == 0) { + if (eof) + return -1; + assert false; + } + return cb.position(); + } + + String encodingName() { + return ((cs instanceof HistoricallyNamedCharset) + ? ((HistoricallyNamedCharset)cs).historicalName() + : cs.name()); + } + + private boolean inReady() { + try { + return (((in != null) && (in.available() > 0)) + || (ch instanceof FileChannel)); // ## RBC.available()? + } catch (IOException x) { + return false; + } + } + + boolean implReady() { + return bb.hasRemaining() || inReady(); + } + + void implClose() throws IOException { + if (ch != null) + ch.close(); + else + in.close(); + } + +} diff --git a/src/sun/nio/cs/StreamEncoder.java b/src/sun/nio/cs/StreamEncoder.java new file mode 100644 index 00000000..3beb75c1 --- /dev/null +++ b/src/sun/nio/cs/StreamEncoder.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.channels.WritableByteChannel; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.IllegalCharsetNameException; + +public class StreamEncoder extends Writer +{ + + private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192; + + private volatile boolean isOpen = true; + + private void ensureOpen() throws IOException { + if (!isOpen) + throw new IOException("Stream closed"); + } + + // Factories for java.io.OutputStreamWriter + public static StreamEncoder forOutputStreamWriter(OutputStream out, + Object lock, + String charsetName) + throws UnsupportedEncodingException + { + String csn = charsetName; + if (csn == null) + csn = Charset.defaultCharset().name(); + try { + if (Charset.isSupported(csn)) + return new StreamEncoder(out, lock, Charset.forName(csn)); + } catch (IllegalCharsetNameException x) { } + throw new UnsupportedEncodingException (csn); + } + + public static StreamEncoder forOutputStreamWriter(OutputStream out, + Object lock, + Charset cs) + { + return new StreamEncoder(out, lock, cs); + } + + public static StreamEncoder forOutputStreamWriter(OutputStream out, + Object lock, + CharsetEncoder enc) + { + return new StreamEncoder(out, lock, enc); + } + + + // Factory for java.nio.channels.Channels.newWriter + + public static StreamEncoder forEncoder(WritableByteChannel ch, + CharsetEncoder enc, + int minBufferCap) + { + return new StreamEncoder(ch, enc, minBufferCap); + } + + + // -- Public methods corresponding to those in OutputStreamWriter -- + + // All synchronization and state/argument checking is done in these public + // methods; the concrete stream-encoder subclasses defined below need not + // do any such checking. + + public String getEncoding() { + if (isOpen()) + return encodingName(); + return null; + } + + public void flushBuffer() throws IOException { + synchronized (lock) { + if (isOpen()) + implFlushBuffer(); + else + throw new IOException("Stream closed"); + } + } + + public void write(int c) throws IOException { + char cbuf[] = new char[1]; + cbuf[0] = (char) c; + write(cbuf, 0, 1); + } + + public void write(char cbuf[], int off, int len) throws IOException { + synchronized (lock) { + ensureOpen(); + if ((off < 0) || (off > cbuf.length) || (len < 0) || + ((off + len) > cbuf.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + implWrite(cbuf, off, len); + } + } + + public void write(String str, int off, int len) throws IOException { + /* Check the len before creating a char buffer */ + if (len < 0) + throw new IndexOutOfBoundsException(); + char cbuf[] = new char[len]; + str.getChars(off, off + len, cbuf, 0); + write(cbuf, 0, len); + } + + public void flush() throws IOException { + synchronized (lock) { + ensureOpen(); + implFlush(); + } + } + + public void close() throws IOException { + synchronized (lock) { + if (!isOpen) + return; + implClose(); + isOpen = false; + } + } + + private boolean isOpen() { + return isOpen; + } + + + // -- Charset-based stream encoder impl -- + + private Charset cs; + private CharsetEncoder encoder; + private ByteBuffer bb; + + // Exactly one of these is non-null + private final OutputStream out; + private WritableByteChannel ch; + + // Leftover first char in a surrogate pair + private boolean haveLeftoverChar = false; + private char leftoverChar; + private CharBuffer lcb = null; + + private StreamEncoder(OutputStream out, Object lock, Charset cs) { + this(out, lock, + cs.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE)); + } + + private StreamEncoder(OutputStream out, Object lock, CharsetEncoder enc) { + super(lock); + this.out = out; + this.ch = null; + this.cs = enc.charset(); + this.encoder = enc; + + // This path disabled until direct buffers are faster + if (false && out instanceof FileOutputStream) { + ch = ((FileOutputStream)out).getChannel(); + if (ch != null) + bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE); + } + if (ch == null) { + bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE); + } + } + + private StreamEncoder(WritableByteChannel ch, CharsetEncoder enc, int mbc) { + this.out = null; + this.ch = ch; + this.cs = enc.charset(); + this.encoder = enc; + this.bb = ByteBuffer.allocate(mbc < 0 + ? DEFAULT_BYTE_BUFFER_SIZE + : mbc); + } + + private void writeBytes() throws IOException { + bb.flip(); + int lim = bb.limit(); + int pos = bb.position(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + if (rem > 0) { + if (ch != null) { + if (ch.write(bb) != rem) + assert false : rem; + } else { + out.write(bb.array(), bb.arrayOffset() + pos, rem); + } + } + bb.clear(); + } + + private void flushLeftoverChar(CharBuffer cb, boolean endOfInput) + throws IOException + { + if (!haveLeftoverChar && !endOfInput) + return; + if (lcb == null) + lcb = CharBuffer.allocate(2); + else + lcb.clear(); + if (haveLeftoverChar) + lcb.put(leftoverChar); + if ((cb != null) && cb.hasRemaining()) + lcb.put(cb.get()); + lcb.flip(); + while (lcb.hasRemaining() || endOfInput) { + CoderResult cr = encoder.encode(lcb, bb, endOfInput); + if (cr.isUnderflow()) { + if (lcb.hasRemaining()) { + leftoverChar = lcb.get(); + if (cb != null && cb.hasRemaining()) + flushLeftoverChar(cb, endOfInput); + return; + } + break; + } + if (cr.isOverflow()) { + assert bb.position() > 0; + writeBytes(); + continue; + } + cr.throwException(); + } + haveLeftoverChar = false; + } + + void implWrite(char cbuf[], int off, int len) + throws IOException + { + CharBuffer cb = CharBuffer.wrap(cbuf, off, len); + + if (haveLeftoverChar) + flushLeftoverChar(cb, false); + + while (cb.hasRemaining()) { + CoderResult cr = encoder.encode(cb, bb, false); + if (cr.isUnderflow()) { + assert (cb.remaining() <= 1) : cb.remaining(); + if (cb.remaining() == 1) { + haveLeftoverChar = true; + leftoverChar = cb.get(); + } + break; + } + if (cr.isOverflow()) { + assert bb.position() > 0; + writeBytes(); + continue; + } + cr.throwException(); + } + } + + void implFlushBuffer() throws IOException { + if (bb.position() > 0) + writeBytes(); + } + + void implFlush() throws IOException { + implFlushBuffer(); + if (out != null) + out.flush(); + } + + void implClose() throws IOException { + flushLeftoverChar(null, true); + try { + for (;;) { + CoderResult cr = encoder.flush(bb); + if (cr.isUnderflow()) + break; + if (cr.isOverflow()) { + assert bb.position() > 0; + writeBytes(); + continue; + } + cr.throwException(); + } + + if (bb.position() > 0) + writeBytes(); + if (ch != null) + ch.close(); + else + out.close(); + } catch (IOException x) { + encoder.reset(); + throw x; + } + } + + String encodingName() { + return ((cs instanceof HistoricallyNamedCharset) + ? ((HistoricallyNamedCharset)cs).historicalName() + : cs.name()); + } +} diff --git a/src/sun/nio/cs/Surrogate.java b/src/sun/nio/cs/Surrogate.java new file mode 100644 index 00000000..15cae6bb --- /dev/null +++ b/src/sun/nio/cs/Surrogate.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.CharBuffer; +import java.nio.charset.CoderResult; + +/** + * Utility class for dealing with surrogates. + * + * @author Mark Reinhold + * @author Martin Buchholz + * @author Ulf Zibis + */ +public class Surrogate { + + private Surrogate() { } + + // TODO: Deprecate/remove the following redundant definitions + public static final char MIN_HIGH = Character.MIN_HIGH_SURROGATE; + public static final char MAX_HIGH = Character.MAX_HIGH_SURROGATE; + public static final char MIN_LOW = Character.MIN_LOW_SURROGATE; + public static final char MAX_LOW = Character.MAX_LOW_SURROGATE; + public static final char MIN = Character.MIN_SURROGATE; + public static final char MAX = Character.MAX_SURROGATE; + public static final int UCS4_MIN = Character.MIN_SUPPLEMENTARY_CODE_POINT; + public static final int UCS4_MAX = Character.MAX_CODE_POINT; + + /** + * Tells whether or not the given value is in the high surrogate range. + * Use of {@link Character#isHighSurrogate} is generally preferred. + */ + public static boolean isHigh(int c) { + return (MIN_HIGH <= c) && (c <= MAX_HIGH); + } + + /** + * Tells whether or not the given value is in the low surrogate range. + * Use of {@link Character#isLowSurrogate} is generally preferred. + */ + public static boolean isLow(int c) { + return (MIN_LOW <= c) && (c <= MAX_LOW); + } + + /** + * Tells whether or not the given value is in the surrogate range. + * Use of {@link Character#isSurrogate} is generally preferred. + */ + public static boolean is(int c) { + return (MIN <= c) && (c <= MAX); + } + + /** + * Tells whether or not the given UCS-4 character must be represented as a + * surrogate pair in UTF-16. + * Use of {@link Character#isSupplementaryCodePoint} is generally preferred. + */ + public static boolean neededFor(int uc) { + return Character.isSupplementaryCodePoint(uc); + } + + /** + * Returns the high UTF-16 surrogate for the given supplementary UCS-4 character. + * Use of {@link Character#highSurrogate} is generally preferred. + */ + public static char high(int uc) { + assert Character.isSupplementaryCodePoint(uc); + return Character.highSurrogate(uc); + } + + /** + * Returns the low UTF-16 surrogate for the given supplementary UCS-4 character. + * Use of {@link Character#lowSurrogate} is generally preferred. + */ + public static char low(int uc) { + assert Character.isSupplementaryCodePoint(uc); + return Character.lowSurrogate(uc); + } + + /** + * Converts the given surrogate pair into a 32-bit UCS-4 character. + * Use of {@link Character#toCodePoint} is generally preferred. + */ + public static int toUCS4(char c, char d) { + assert Character.isHighSurrogate(c) && Character.isLowSurrogate(d); + return Character.toCodePoint(c, d); + } + + /** + * Surrogate parsing support. Charset implementations may use instances of + * this class to handle the details of parsing UTF-16 surrogate pairs. + */ + public static class Parser { + + public Parser() { } + + private int character; // UCS-4 + private CoderResult error = CoderResult.UNDERFLOW; + private boolean isPair; + + /** + * Returns the UCS-4 character previously parsed. + */ + public int character() { + assert (error == null); + return character; + } + + /** + * Tells whether or not the previously-parsed UCS-4 character was + * originally represented by a surrogate pair. + */ + public boolean isPair() { + assert (error == null); + return isPair; + } + + /** + * Returns the number of UTF-16 characters consumed by the previous + * parse. + */ + public int increment() { + assert (error == null); + return isPair ? 2 : 1; + } + + /** + * If the previous parse operation detected an error, return the object + * describing that error. + */ + public CoderResult error() { + assert (error != null); + return error; + } + + /** + * Returns an unmappable-input result object, with the appropriate + * input length, for the previously-parsed character. + */ + public CoderResult unmappableResult() { + assert (error == null); + return CoderResult.unmappableForLength(isPair ? 2 : 1); + } + + /** + * Parses a UCS-4 character from the given source buffer, handling + * surrogates. + * + * @param c The first character + * @param in The source buffer, from which one more character + * will be consumed if c is a high surrogate + * + * @returns Either a parsed UCS-4 character, in which case the isPair() + * and increment() methods will return meaningful values, or + * -1, in which case error() will return a descriptive result + * object + */ + public int parse(char c, CharBuffer in) { + if (Character.isHighSurrogate(c)) { + if (!in.hasRemaining()) { + error = CoderResult.UNDERFLOW; + return -1; + } + char d = in.get(); + if (Character.isLowSurrogate(d)) { + character = Character.toCodePoint(c, d); + isPair = true; + error = null; + return character; + } + error = CoderResult.malformedForLength(1); + return -1; + } + if (Character.isLowSurrogate(c)) { + error = CoderResult.malformedForLength(1); + return -1; + } + character = c; + isPair = false; + error = null; + return character; + } + + /** + * Parses a UCS-4 character from the given source buffer, handling + * surrogates. + * + * @param c The first character + * @param ia The input array, from which one more character + * will be consumed if c is a high surrogate + * @param ip The input index + * @param il The input limit + * + * @returns Either a parsed UCS-4 character, in which case the isPair() + * and increment() methods will return meaningful values, or + * -1, in which case error() will return a descriptive result + * object + */ + public int parse(char c, char[] ia, int ip, int il) { + assert (ia[ip] == c); + if (Character.isHighSurrogate(c)) { + if (il - ip < 2) { + error = CoderResult.UNDERFLOW; + return -1; + } + char d = ia[ip + 1]; + if (Character.isLowSurrogate(d)) { + character = Character.toCodePoint(c, d); + isPair = true; + error = null; + return character; + } + error = CoderResult.malformedForLength(1); + return -1; + } + if (Character.isLowSurrogate(c)) { + error = CoderResult.malformedForLength(1); + return -1; + } + character = c; + isPair = false; + error = null; + return character; + } + + } + + /** + * Surrogate generation support. Charset implementations may use instances + * of this class to handle the details of generating UTF-16 surrogate + * pairs. + */ + public static class Generator { + + public Generator() { } + + private CoderResult error = CoderResult.OVERFLOW; + + /** + * If the previous generation operation detected an error, return the + * object describing that error. + */ + public CoderResult error() { + assert error != null; + return error; + } + + /** + * Generates one or two UTF-16 characters to represent the given UCS-4 + * character. + * + * @param uc The UCS-4 character + * @param len The number of input bytes from which the UCS-4 value + * was constructed (used when creating result objects) + * @param dst The destination buffer, to which one or two UTF-16 + * characters will be written + * + * @returns Either a positive count of the number of UTF-16 characters + * written to the destination buffer, or -1, in which case + * error() will return a descriptive result object + */ + public int generate(int uc, int len, CharBuffer dst) { + if (Character.isBmpCodePoint(uc)) { + char c = (char) uc; + if (Character.isSurrogate(c)) { + error = CoderResult.malformedForLength(len); + return -1; + } + if (dst.remaining() < 1) { + error = CoderResult.OVERFLOW; + return -1; + } + dst.put(c); + error = null; + return 1; + } else if (Character.isValidCodePoint(uc)) { + if (dst.remaining() < 2) { + error = CoderResult.OVERFLOW; + return -1; + } + dst.put(Character.highSurrogate(uc)); + dst.put(Character.lowSurrogate(uc)); + error = null; + return 2; + } else { + error = CoderResult.unmappableForLength(len); + return -1; + } + } + + /** + * Generates one or two UTF-16 characters to represent the given UCS-4 + * character. + * + * @param uc The UCS-4 character + * @param len The number of input bytes from which the UCS-4 value + * was constructed (used when creating result objects) + * @param da The destination array, to which one or two UTF-16 + * characters will be written + * @param dp The destination position + * @param dl The destination limit + * + * @returns Either a positive count of the number of UTF-16 characters + * written to the destination buffer, or -1, in which case + * error() will return a descriptive result object + */ + public int generate(int uc, int len, char[] da, int dp, int dl) { + if (Character.isBmpCodePoint(uc)) { + char c = (char) uc; + if (Character.isSurrogate(c)) { + error = CoderResult.malformedForLength(len); + return -1; + } + if (dl - dp < 1) { + error = CoderResult.OVERFLOW; + return -1; + } + da[dp] = c; + error = null; + return 1; + } else if (Character.isValidCodePoint(uc)) { + if (dl - dp < 2) { + error = CoderResult.OVERFLOW; + return -1; + } + da[dp] = Character.highSurrogate(uc); + da[dp + 1] = Character.lowSurrogate(uc); + error = null; + return 2; + } else { + error = CoderResult.unmappableForLength(len); + return -1; + } + } + } + +} diff --git a/src/sun/nio/cs/ThreadLocalCoders.java b/src/sun/nio/cs/ThreadLocalCoders.java new file mode 100644 index 00000000..d05f2cc3 --- /dev/null +++ b/src/sun/nio/cs/ThreadLocalCoders.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + + +/** + * Utility class for caching per-thread decoders and encoders. + */ + +public class ThreadLocalCoders { + + private static final int CACHE_SIZE = 3; + + private static abstract class Cache { + + // Thread-local reference to array of cached objects, in LRU order + private ThreadLocal cache = new ThreadLocal<>(); + private final int size; + + Cache(int size) { + this.size = size; + } + + abstract Object create(Object name); + + private void moveToFront(Object[] oa, int i) { + Object ob = oa[i]; + for (int j = i; j > 0; j--) + oa[j] = oa[j - 1]; + oa[0] = ob; + } + + abstract boolean hasName(Object ob, Object name); + + Object forName(Object name) { + Object[] oa = cache.get(); + if (oa == null) { + oa = new Object[size]; + cache.set(oa); + } else { + for (int i = 0; i < oa.length; i++) { + Object ob = oa[i]; + if (ob == null) + continue; + if (hasName(ob, name)) { + if (i > 0) + moveToFront(oa, i); + return ob; + } + } + } + + // Create a new object + Object ob = create(name); + oa[oa.length - 1] = ob; + moveToFront(oa, oa.length - 1); + return ob; + } + + } + + private static Cache decoderCache = new Cache(CACHE_SIZE) { + boolean hasName(Object ob, Object name) { + if (name instanceof String) + return (((CharsetDecoder)ob).charset().name().equals(name)); + if (name instanceof Charset) + return ((CharsetDecoder)ob).charset().equals(name); + return false; + } + Object create(Object name) { + if (name instanceof String) + return Charset.forName((String)name).newDecoder(); + if (name instanceof Charset) + return ((Charset)name).newDecoder(); + assert false; + return null; + } + }; + + public static CharsetDecoder decoderFor(Object name) { + CharsetDecoder cd = (CharsetDecoder)decoderCache.forName(name); + cd.reset(); + return cd; + } + + private static Cache encoderCache = new Cache(CACHE_SIZE) { + boolean hasName(Object ob, Object name) { + if (name instanceof String) + return (((CharsetEncoder)ob).charset().name().equals(name)); + if (name instanceof Charset) + return ((CharsetEncoder)ob).charset().equals(name); + return false; + } + Object create(Object name) { + if (name instanceof String) + return Charset.forName((String)name).newEncoder(); + if (name instanceof Charset) + return ((Charset)name).newEncoder(); + assert false; + return null; + } + }; + + public static CharsetEncoder encoderFor(Object name) { + CharsetEncoder ce = (CharsetEncoder)encoderCache.forName(name); + ce.reset(); + return ce; + } + +} diff --git a/src/sun/nio/cs/US_ASCII.java b/src/sun/nio/cs/US_ASCII.java new file mode 100644 index 00000000..aa954283 --- /dev/null +++ b/src/sun/nio/cs/US_ASCII.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +public class US_ASCII + extends Charset + implements HistoricallyNamedCharset +{ + + public US_ASCII() { + super("US-ASCII", StandardCharsets.aliases_US_ASCII); + } + + public String historicalName() { + return "ASCII"; + } + + public boolean contains(Charset cs) { + return (cs instanceof US_ASCII); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends CharsetDecoder + implements ArrayDecoder { + + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + byte b = sa[sp]; + if (b >= 0) { + if (dp >= dl) + return CoderResult.OVERFLOW; + da[dp++] = (char)b; + sp++; + continue; + } + return CoderResult.malformedForLength(1); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + byte b = src.get(); + if (b >= 0) { + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((char)b); + mark++; + continue; + } + return CoderResult.malformedForLength(1); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + private char repl = '\uFFFD'; + protected void implReplaceWith(String newReplacement) { + repl = newReplacement.charAt(0); + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + int dp = 0; + len = Math.min(len, dst.length); + while (dp < len) { + byte b = src[sp++]; + if (b >= 0) + dst[dp++] = (char)b; + else + dst[dp++] = repl; + } + return dp; + } + } + + private static class Encoder extends CharsetEncoder + implements ArrayEncoder { + + private Encoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + public boolean canEncode(char c) { + return c < 0x80; + } + + public boolean isLegalReplacement(byte[] repl) { + return (repl.length == 1 && repl[0] >= 0) || + super.isLegalReplacement(repl); + } + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + char c = sa[sp]; + if (c < 0x80) { + if (dp >= dl) + return CoderResult.OVERFLOW; + da[dp] = (byte)c; + sp++; dp++; + continue; + } + if (sgp.parse(c, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + if (c < 0x80) { + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((byte)c); + mark++; + continue; + } + if (sgp.parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + private byte repl = (byte)'?'; + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement[0]; + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = src[sp++]; + if (c < 0x80) { + dst[dp++] = (byte)c; + continue; + } + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + if (len > dst.length) { + sl++; + len--; + } + sp++; + } + dst[dp++] = repl; + } + return dp; + } + } + +} diff --git a/src/sun/nio/cs/UTF_16.java b/src/sun/nio/cs/UTF_16.java new file mode 100644 index 00000000..a5d72ff0 --- /dev/null +++ b/src/sun/nio/cs/UTF_16.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +class UTF_16 extends Unicode +{ + + public UTF_16() { + super("UTF-16", StandardCharsets.aliases_UTF_16); + } + + public String historicalName() { + return "UTF-16"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends UnicodeDecoder { + + public Decoder(Charset cs) { + super(cs, NONE); + } + } + + private static class Encoder extends UnicodeEncoder { + + public Encoder(Charset cs) { + super(cs, BIG, true); + } + } + +} diff --git a/src/sun/nio/cs/UTF_16BE.java b/src/sun/nio/cs/UTF_16BE.java new file mode 100644 index 00000000..a7c89c6b --- /dev/null +++ b/src/sun/nio/cs/UTF_16BE.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +class UTF_16BE extends Unicode +{ + + public UTF_16BE() { + super("UTF-16BE", StandardCharsets.aliases_UTF_16BE); + } + + public String historicalName() { + return "UnicodeBigUnmarked"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends UnicodeDecoder { + + public Decoder(Charset cs) { + super(cs, BIG); + } + } + + private static class Encoder extends UnicodeEncoder { + + public Encoder(Charset cs) { + super(cs, BIG, false); + } + } + +} diff --git a/src/sun/nio/cs/UTF_16LE.java b/src/sun/nio/cs/UTF_16LE.java new file mode 100644 index 00000000..c24c1f55 --- /dev/null +++ b/src/sun/nio/cs/UTF_16LE.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +class UTF_16LE extends Unicode +{ + + public UTF_16LE() { + super("UTF-16LE", StandardCharsets.aliases_UTF_16LE); + } + + public String historicalName() { + return "UnicodeLittleUnmarked"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends UnicodeDecoder { + + public Decoder(Charset cs) { + super(cs, LITTLE); + } + } + + private static class Encoder extends UnicodeEncoder { + + public Encoder(Charset cs) { + super(cs, LITTLE, false); + } + } + +} diff --git a/src/sun/nio/cs/UTF_16LE_BOM.java b/src/sun/nio/cs/UTF_16LE_BOM.java new file mode 100644 index 00000000..86ac2a52 --- /dev/null +++ b/src/sun/nio/cs/UTF_16LE_BOM.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +class UTF_16LE_BOM extends Unicode +{ + + public UTF_16LE_BOM() { + super("x-UTF-16LE-BOM", StandardCharsets.aliases_UTF_16LE_BOM); + } + + public String historicalName() { + return "UnicodeLittle"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends UnicodeDecoder { + + public Decoder(Charset cs) { + super(cs, NONE, LITTLE); + } + } + + private static class Encoder extends UnicodeEncoder { + + public Encoder(Charset cs) { + super(cs, LITTLE, true); + } + } + +} diff --git a/src/sun/nio/cs/UTF_32.java b/src/sun/nio/cs/UTF_32.java new file mode 100644 index 00000000..3165f758 --- /dev/null +++ b/src/sun/nio/cs/UTF_32.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class UTF_32 extends Unicode +{ + public UTF_32() { + super("UTF-32", StandardCharsets.aliases_UTF_32); + } + + public String historicalName() { + return "UTF-32"; + } + + public CharsetDecoder newDecoder() { + return new UTF_32Coder.Decoder(this, UTF_32Coder.NONE); + } + + public CharsetEncoder newEncoder() { + return new UTF_32Coder.Encoder(this, UTF_32Coder.BIG, false); + } +} diff --git a/src/sun/nio/cs/UTF_32BE.java b/src/sun/nio/cs/UTF_32BE.java new file mode 100644 index 00000000..cf025f1f --- /dev/null +++ b/src/sun/nio/cs/UTF_32BE.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class UTF_32BE extends Unicode +{ + public UTF_32BE() { + super("UTF-32BE", StandardCharsets.aliases_UTF_32BE); + } + + public String historicalName() { + return "UTF-32BE"; + } + + public CharsetDecoder newDecoder() { + return new UTF_32Coder.Decoder(this, UTF_32Coder.BIG); + } + + public CharsetEncoder newEncoder() { + return new UTF_32Coder.Encoder(this, UTF_32Coder.BIG, false); + } +} diff --git a/src/sun/nio/cs/UTF_32BE_BOM.java b/src/sun/nio/cs/UTF_32BE_BOM.java new file mode 100644 index 00000000..3de22bdd --- /dev/null +++ b/src/sun/nio/cs/UTF_32BE_BOM.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class UTF_32BE_BOM extends Unicode +{ + public UTF_32BE_BOM() { + super("X-UTF-32BE-BOM", StandardCharsets.aliases_UTF_32BE_BOM); + } + + public String historicalName() { + return "X-UTF-32BE-BOM"; + } + + public CharsetDecoder newDecoder() { + return new UTF_32Coder.Decoder(this, UTF_32Coder.BIG); + } + + public CharsetEncoder newEncoder() { + return new UTF_32Coder.Encoder(this, UTF_32Coder.BIG, true); + } +} diff --git a/src/sun/nio/cs/UTF_32Coder.java b/src/sun/nio/cs/UTF_32Coder.java new file mode 100644 index 00000000..27df65cb --- /dev/null +++ b/src/sun/nio/cs/UTF_32Coder.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +class UTF_32Coder { + protected static final int BOM_BIG = 0xFEFF; + protected static final int BOM_LITTLE = 0xFFFE0000; + protected static final int NONE = 0; + protected static final int BIG = 1; + protected static final int LITTLE = 2; + + protected static class Decoder extends CharsetDecoder { + private int currentBO; + private int expectedBO; + + protected Decoder(Charset cs, int bo) { + super(cs, 0.25f, 1.0f); + this.expectedBO = bo; + this.currentBO = NONE; + } + + private int getCP(ByteBuffer src) { + return (currentBO==BIG) + ?(((src.get() & 0xff) << 24) | + ((src.get() & 0xff) << 16) | + ((src.get() & 0xff) << 8) | + (src.get() & 0xff)) + :((src.get() & 0xff) | + ((src.get() & 0xff) << 8) | + ((src.get() & 0xff) << 16) | + ((src.get() & 0xff) << 24)); + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.remaining() < 4) + return CoderResult.UNDERFLOW; + int mark = src.position(); + int cp; + try { + if (currentBO == NONE) { + cp = ((src.get() & 0xff) << 24) | + ((src.get() & 0xff) << 16) | + ((src.get() & 0xff) << 8) | + (src.get() & 0xff); + if (cp == BOM_BIG && expectedBO != LITTLE) { + currentBO = BIG; + mark += 4; + } else if (cp == BOM_LITTLE && expectedBO != BIG) { + currentBO = LITTLE; + mark += 4; + } else { + if (expectedBO == NONE) + currentBO = BIG; + else + currentBO = expectedBO; + src.position(mark); + } + } + while (src.remaining() >= 4) { + cp = getCP(src); + if (Character.isBmpCodePoint(cp)) { + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + mark += 4; + dst.put((char) cp); + } else if (Character.isValidCodePoint(cp)) { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + mark += 4; + dst.put(Character.highSurrogate(cp)); + dst.put(Character.lowSurrogate(cp)); + } else { + return CoderResult.malformedForLength(4); + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + protected void implReset() { + currentBO = NONE; + } + } + + protected static class Encoder extends CharsetEncoder { + private boolean doBOM = false; + private boolean doneBOM = true; + private int byteOrder; + + protected void put(int cp, ByteBuffer dst) { + if (byteOrder==BIG) { + dst.put((byte)(cp >> 24)); + dst.put((byte)(cp >> 16)); + dst.put((byte)(cp >> 8)); + dst.put((byte)cp); + } else { + dst.put((byte)cp); + dst.put((byte)(cp >> 8)); + dst.put((byte)(cp >> 16)); + dst.put((byte)(cp >> 24)); + } + } + + protected Encoder(Charset cs, int byteOrder, boolean doBOM) { + super(cs, 4.0f, + doBOM?8.0f:4.0f, + (byteOrder==BIG)?new byte[]{(byte)0, (byte)0, (byte)0xff, (byte)0xfd} + :new byte[]{(byte)0xfd, (byte)0xff, (byte)0, (byte)0}); + this.byteOrder = byteOrder; + this.doBOM = doBOM; + this.doneBOM = !doBOM; + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + if (!doneBOM && src.hasRemaining()) { + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + put(BOM_BIG, dst); + doneBOM = true; + } + try { + while (src.hasRemaining()) { + char c = src.get(); + if (!Character.isSurrogate(c)) { + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + mark++; + put(c, dst); + } else if (Character.isHighSurrogate(c)) { + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + char low = src.get(); + if (Character.isLowSurrogate(low)) { + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + mark += 2; + put(Character.toCodePoint(c, low), dst); + } else { + return CoderResult.malformedForLength(1); + } + } else { + // assert Character.isLowSurrogate(c); + return CoderResult.malformedForLength(1); + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected void implReset() { + doneBOM = !doBOM; + } + + } +} diff --git a/src/sun/nio/cs/UTF_32LE.java b/src/sun/nio/cs/UTF_32LE.java new file mode 100644 index 00000000..c8974c8e --- /dev/null +++ b/src/sun/nio/cs/UTF_32LE.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class UTF_32LE extends Unicode +{ + public UTF_32LE() { + super("UTF-32LE", StandardCharsets.aliases_UTF_32LE); + } + + public String historicalName() { + return "UTF-32LE"; + } + + public CharsetDecoder newDecoder() { + return new UTF_32Coder.Decoder(this, UTF_32Coder.LITTLE); + } + + public CharsetEncoder newEncoder() { + return new UTF_32Coder.Encoder(this, UTF_32Coder.LITTLE, false); + } +} diff --git a/src/sun/nio/cs/UTF_32LE_BOM.java b/src/sun/nio/cs/UTF_32LE_BOM.java new file mode 100644 index 00000000..9db63f93 --- /dev/null +++ b/src/sun/nio/cs/UTF_32LE_BOM.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class UTF_32LE_BOM extends Unicode +{ + public UTF_32LE_BOM() { + super("X-UTF-32LE-BOM", StandardCharsets.aliases_UTF_32LE_BOM); + } + + public String historicalName() { + return "X-UTF-32LE-BOM"; + } + + public CharsetDecoder newDecoder() { + return new UTF_32Coder.Decoder(this, UTF_32Coder.LITTLE); + } + + public CharsetEncoder newEncoder() { + return new UTF_32Coder.Encoder(this, UTF_32Coder.LITTLE, true); + } +} diff --git a/src/sun/nio/cs/UTF_8.java b/src/sun/nio/cs/UTF_8.java new file mode 100644 index 00000000..3ee2341f --- /dev/null +++ b/src/sun/nio/cs/UTF_8.java @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/* Legal UTF-8 Byte Sequences + * + * # Code Points Bits Bit/Byte pattern + * 1 7 0xxxxxxx + * U+0000..U+007F 00..7F + * + * 2 11 110xxxxx 10xxxxxx + * U+0080..U+07FF C2..DF 80..BF + * + * 3 16 1110xxxx 10xxxxxx 10xxxxxx + * U+0800..U+0FFF E0 A0..BF 80..BF + * U+1000..U+FFFF E1..EF 80..BF 80..BF + * + * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + * U+100000..U10FFFF F4 80..8F 80..BF 80..BF + * + */ + +class UTF_8 extends Unicode +{ + public UTF_8() { + super("UTF-8", StandardCharsets.aliases_UTF_8); + } + + public String historicalName() { + return "UTF8"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static final void updatePositions(Buffer src, int sp, + Buffer dst, int dp) { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + + private static class Decoder extends CharsetDecoder + implements ArrayDecoder { + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private static boolean isNotContinuation(int b) { + return (b & 0xc0) != 0x80; + } + + // [E0] [A0..BF] [80..BF] + // [E1..EF] [80..BF] [80..BF] + private static boolean isMalformed3(int b1, int b2, int b3) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80; + } + + // only used when there is only one byte left in src buffer + private static boolean isMalformed3_2(int b1, int b2) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80; + } + + // [F0] [90..BF] [80..BF] [80..BF] + // [F1..F3] [80..BF] [80..BF] [80..BF] + // [F4] [80..8F] [80..BF] [80..BF] + // only check 80-be range here, the [0xf0,0x80...] and [0xf4,0x90-...] + // will be checked by Character.isSupplementaryCodePoint(uc) + private static boolean isMalformed4(int b2, int b3, int b4) { + return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (b4 & 0xc0) != 0x80; + } + + // only used when there is less than 4 bytes left in src buffer. + // both b1 and b2 should be "& 0xff" before passed in. + private static boolean isMalformed4_2(int b1, int b2) { + return (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + (b2 & 0xc0) != 0x80; + } + + // tests if b1 and b2 are malformed as the first 2 bytes of a + // legal`4-byte utf-8 byte sequence. + // only used when there is less than 4 bytes left in src buffer, + // after isMalformed4_2 has been invoked. + private static boolean isMalformed4_3(int b3) { + return (b3 & 0xc0) != 0x80; + } + + private static CoderResult lookupN(ByteBuffer src, int n) + { + for (int i = 1; i < n; i++) { + if (isNotContinuation(src.get())) + return CoderResult.malformedForLength(i); + } + return CoderResult.malformedForLength(n); + } + + private static CoderResult malformedN(ByteBuffer src, int nb) { + switch (nb) { + case 1: + case 2: // always 1 + return CoderResult.malformedForLength(1); + case 3: + int b1 = src.get(); + int b2 = src.get(); // no need to lookup b3 + return CoderResult.malformedForLength( + ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + isNotContinuation(b2)) ? 1 : 2); + case 4: // we don't care the speed here + b1 = src.get() & 0xff; + b2 = src.get() & 0xff; + if (b1 > 0xf4 || + (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + isNotContinuation(b2)) + return CoderResult.malformedForLength(1); + if (isNotContinuation(src.get())) + return CoderResult.malformedForLength(2); + return CoderResult.malformedForLength(3); + default: + assert false; + return null; + } + } + + private static CoderResult malformed(ByteBuffer src, int sp, + CharBuffer dst, int dp, + int nb) + { + src.position(sp - src.arrayOffset()); + CoderResult cr = malformedN(src, nb); + updatePositions(src, sp, dst, dp); + return cr; + } + + + private static CoderResult malformed(ByteBuffer src, + int mark, int nb) + { + src.position(mark); + CoderResult cr = malformedN(src, nb); + src.position(mark); + return cr; + } + + private static CoderResult malformedForLength(ByteBuffer src, + int sp, + CharBuffer dst, + int dp, + int malformedNB) + { + updatePositions(src, sp, dst, dp); + return CoderResult.malformedForLength(malformedNB); + } + + private static CoderResult malformedForLength(ByteBuffer src, + int mark, + int malformedNB) + { + src.position(mark); + return CoderResult.malformedForLength(malformedNB); + } + + + private static CoderResult xflow(Buffer src, int sp, int sl, + Buffer dst, int dp, int nb) { + updatePositions(src, sp, dst, dp); + return (nb == 0 || sl - sp < nb) + ? CoderResult.UNDERFLOW : CoderResult.OVERFLOW; + } + + private static CoderResult xflow(Buffer src, int mark, int nb) { + src.position(mark); + return (nb == 0 || src.remaining() < nb) + ? CoderResult.UNDERFLOW : CoderResult.OVERFLOW; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + // This method is optimized for ASCII input. + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + // ASCII only loop + while (dp < dlASCII && sa[sp] >= 0) + da[dp++] = (char) sa[sp++]; + while (sp < sl) { + int b1 = sa[sp]; + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dp >= dl) + return xflow(src, sp, sl, dst, dp, 1); + da[dp++] = (char) b1; + sp++; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + // [C2..DF] [80..BF] + if (sl - sp < 2 || dp >= dl) + return xflow(src, sp, sl, dst, dp, 2); + int b2 = sa[sp + 1]; + // Now we check the first byte of 2-byte sequence as + // if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) + // no longer need to check b1 against c1 & c0 for + // malformed as we did in previous version + // (b1 & 0x1e) == 0x0 || (b2 & 0xc0) != 0x80; + // only need to check the second byte b2. + if (isNotContinuation(b2)) + return malformedForLength(src, sp, dst, dp, 1); + da[dp++] = (char) (((b1 << 6) ^ b2) + ^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + sp += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + int srcRemaining = sl - sp; + if (srcRemaining < 3 || dp >= dl) { + if (srcRemaining > 1 && isMalformed3_2(b1, sa[sp + 1])) + return malformedForLength(src, sp, dst, dp, 1); + return xflow(src, sp, sl, dst, dp, 3); + } + int b2 = sa[sp + 1]; + int b3 = sa[sp + 2]; + if (isMalformed3(b1, b2, b3)) + return malformed(src, sp, dst, dp, 3); + char c = (char) + ((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (Character.isSurrogate(c)) + return malformedForLength(src, sp, dst, dp, 3); + da[dp++] = c; + sp += 3; + } else if ((b1 >> 3) == -2) { + // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int srcRemaining = sl - sp; + if (srcRemaining < 4 || dl - dp < 2) { + b1 &= 0xff; + if (b1 > 0xf4 || + srcRemaining > 1 && isMalformed4_2(b1, sa[sp + 1] & 0xff)) + return malformedForLength(src, sp, dst, dp, 1); + if (srcRemaining > 2 && isMalformed4_3(sa[sp + 2])) + return malformedForLength(src, sp, dst, dp, 2); + return xflow(src, sp, sl, dst, dp, 4); + } + int b2 = sa[sp + 1]; + int b3 = sa[sp + 2]; + int b4 = sa[sp + 3]; + int uc = ((b1 << 18) ^ + (b2 << 12) ^ + (b3 << 6) ^ + (b4 ^ + (((byte) 0xF0 << 18) ^ + ((byte) 0x80 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (isMalformed4(b2, b3, b4) || + // shortest form check + !Character.isSupplementaryCodePoint(uc)) { + return malformed(src, sp, dst, dp, 4); + } + da[dp++] = Character.highSurrogate(uc); + da[dp++] = Character.lowSurrogate(uc); + sp += 4; + } else + return malformed(src, sp, dst, dp, 1); + } + return xflow(src, sp, sl, dst, dp, 0); + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + int limit = src.limit(); + while (mark < limit) { + int b1 = src.get(); + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + if (dst.remaining() < 1) + return xflow(src, mark, 1); // overflow + dst.put((char) b1); + mark++; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (limit - mark < 2|| dst.remaining() < 1) + return xflow(src, mark, 2); + int b2 = src.get(); + if (isNotContinuation(b2)) + return malformedForLength(src, mark, 1); + dst.put((char) (((b1 << 6) ^ b2) + ^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0)))); + mark += 2; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + int srcRemaining = limit - mark; + if (srcRemaining < 3 || dst.remaining() < 1) { + if (srcRemaining > 1 && isMalformed3_2(b1, src.get())) + return malformedForLength(src, mark, 1); + return xflow(src, mark, 3); + } + int b2 = src.get(); + int b3 = src.get(); + if (isMalformed3(b1, b2, b3)) + return malformed(src, mark, 3); + char c = (char) + ((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (Character.isSurrogate(c)) + return malformedForLength(src, mark, 3); + dst.put(c); + mark += 3; + } else if ((b1 >> 3) == -2) { + // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int srcRemaining = limit - mark; + if (srcRemaining < 4 || dst.remaining() < 2) { + b1 &= 0xff; + if (b1 > 0xf4 || + srcRemaining > 1 && isMalformed4_2(b1, src.get() & 0xff)) + return malformedForLength(src, mark, 1); + if (srcRemaining > 2 && isMalformed4_3(src.get())) + return malformedForLength(src, mark, 2); + return xflow(src, mark, 4); + } + int b2 = src.get(); + int b3 = src.get(); + int b4 = src.get(); + int uc = ((b1 << 18) ^ + (b2 << 12) ^ + (b3 << 6) ^ + (b4 ^ + (((byte) 0xF0 << 18) ^ + ((byte) 0x80 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (isMalformed4(b2, b3, b4) || + // shortest form check + !Character.isSupplementaryCodePoint(uc)) { + return malformed(src, mark, 4); + } + dst.put(Character.highSurrogate(uc)); + dst.put(Character.lowSurrogate(uc)); + mark += 4; + } else { + return malformed(src, mark, 1); + } + } + return xflow(src, mark, 0); + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + private static ByteBuffer getByteBuffer(ByteBuffer bb, byte[] ba, int sp) + { + if (bb == null) + bb = ByteBuffer.wrap(ba); + bb.position(sp); + return bb; + } + + // returns -1 if there is/are malformed byte(s) and the + // "action" for malformed input is not REPLACE. + public int decode(byte[] sa, int sp, int len, char[] da) { + final int sl = sp + len; + int dp = 0; + int dlASCII = Math.min(len, da.length); + ByteBuffer bb = null; // only necessary if malformed + + // ASCII only optimized loop + while (dp < dlASCII && sa[sp] >= 0) + da[dp++] = (char) sa[sp++]; + + while (sp < sl) { + int b1 = sa[sp++]; + if (b1 >= 0) { + // 1 byte, 7 bits: 0xxxxxxx + da[dp++] = (char) b1; + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + if (sp < sl) { + int b2 = sa[sp++]; + if (isNotContinuation(b2)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + sp--; // malformedN(bb, 2) always returns 1 + } else { + da[dp++] = (char) (((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + } + continue; + } + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + return dp; + } else if ((b1 >> 4) == -2) { + // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + if (sp + 1 < sl) { + int b2 = sa[sp++]; + int b3 = sa[sp++]; + if (isMalformed3(b1, b2, b3)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + sp -= 3; + bb = getByteBuffer(bb, sa, sp); + sp += malformedN(bb, 3).length(); + } else { + char c = (char)((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (Character.isSurrogate(c)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + } else { + da[dp++] = c; + } + } + continue; + } + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + if (sp < sl && isMalformed3_2(b1, sa[sp])) { + da[dp++] = replacement().charAt(0); + continue; + + } + da[dp++] = replacement().charAt(0); + return dp; + } else if ((b1 >> 3) == -2) { + // 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + if (sp + 2 < sl) { + int b2 = sa[sp++]; + int b3 = sa[sp++]; + int b4 = sa[sp++]; + int uc = ((b1 << 18) ^ + (b2 << 12) ^ + (b3 << 6) ^ + (b4 ^ + (((byte) 0xF0 << 18) ^ + ((byte) 0x80 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (isMalformed4(b2, b3, b4) || + // shortest form check + !Character.isSupplementaryCodePoint(uc)) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + sp -= 4; + bb = getByteBuffer(bb, sa, sp); + sp += malformedN(bb, 4).length(); + } else { + da[dp++] = Character.highSurrogate(uc); + da[dp++] = Character.lowSurrogate(uc); + } + continue; + } + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + b1 &= 0xff; + if (b1 > 0xf4 || + sp < sl && isMalformed4_2(b1, sa[sp] & 0xff)) { + da[dp++] = replacement().charAt(0); + continue; + } + sp++; + if (sp < sl && isMalformed4_3(sa[sp])) { + da[dp++] = replacement().charAt(0); + continue; + } + da[dp++] = replacement().charAt(0); + return dp; + } else { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = replacement().charAt(0); + } + } + return dp; + } + } + + private static final class Encoder extends CharsetEncoder + implements ArrayEncoder { + + private Encoder(Charset cs) { + super(cs, 1.1f, 3.0f); + } + + public boolean canEncode(char c) { + return !Character.isSurrogate(c); + } + + public boolean isLegalReplacement(byte[] repl) { + return ((repl.length == 1 && repl[0] >= 0) || + super.isLegalReplacement(repl)); + } + + private static CoderResult overflow(CharBuffer src, int sp, + ByteBuffer dst, int dp) { + updatePositions(src, sp, dst, dp); + return CoderResult.OVERFLOW; + } + + private static CoderResult overflow(CharBuffer src, int mark) { + src.position(mark); + return CoderResult.OVERFLOW; + } + + private Surrogate.Parser sgp; + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + int dlASCII = dp + Math.min(sl - sp, dl - dp); + + // ASCII only loop + while (dp < dlASCII && sa[sp] < '\u0080') + da[dp++] = (byte) sa[sp++]; + while (sp < sl) { + char c = sa[sp]; + if (c < 0x80) { + // Have at most seven bits + if (dp >= dl) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)c; + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dl - dp < 2) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xc0 | (c >> 6)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + // Have a surrogate pair + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, sa, sp, sl); + if (uc < 0) { + updatePositions(src, sp, dst, dp); + return sgp.error(); + } + if (dl - dp < 4) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xf0 | ((uc >> 18))); + da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); + da[dp++] = (byte)(0x80 | ((uc >> 6) & 0x3f)); + da[dp++] = (byte)(0x80 | (uc & 0x3f)); + sp++; // 2 chars + } else { + // 3 bytes, 16 bits + if (dl - dp < 3) + return overflow(src, sp, dst, dp); + da[dp++] = (byte)(0xe0 | ((c >> 12))); + da[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } + sp++; + } + updatePositions(src, sp, dst, dp); + return CoderResult.UNDERFLOW; + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + while (src.hasRemaining()) { + char c = src.get(); + if (c < 0x80) { + // Have at most seven bits + if (!dst.hasRemaining()) + return overflow(src, mark); + dst.put((byte)c); + } else if (c < 0x800) { + // 2 bytes, 11 bits + if (dst.remaining() < 2) + return overflow(src, mark); + dst.put((byte)(0xc0 | (c >> 6))); + dst.put((byte)(0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { + // Have a surrogate pair + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, src); + if (uc < 0) { + src.position(mark); + return sgp.error(); + } + if (dst.remaining() < 4) + return overflow(src, mark); + dst.put((byte)(0xf0 | ((uc >> 18)))); + dst.put((byte)(0x80 | ((uc >> 12) & 0x3f))); + dst.put((byte)(0x80 | ((uc >> 6) & 0x3f))); + dst.put((byte)(0x80 | (uc & 0x3f))); + mark++; // 2 chars + } else { + // 3 bytes, 16 bits + if (dst.remaining() < 3) + return overflow(src, mark); + dst.put((byte)(0xe0 | ((c >> 12)))); + dst.put((byte)(0x80 | ((c >> 6) & 0x3f))); + dst.put((byte)(0x80 | (c & 0x3f))); + } + mark++; + } + src.position(mark); + return CoderResult.UNDERFLOW; + } + + protected final CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + private byte repl = (byte)'?'; + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement[0]; + } + + // returns -1 if there is malformed char(s) and the + // "action" for malformed input is not REPLACE. + public int encode(char[] sa, int sp, int len, byte[] da) { + int sl = sp + len; + int dp = 0; + int dlASCII = dp + Math.min(len, da.length); + + // ASCII only optimized loop + while (dp < dlASCII && sa[sp] < '\u0080') + da[dp++] = (byte) sa[sp++]; + + while (sp < sl) { + char c = sa[sp++]; + if (c < 0x80) { + // Have at most seven bits + da[dp++] = (byte)c; + } else if (c < 0x800) { + // 2 bytes, 11 bits + da[dp++] = (byte)(0xc0 | (c >> 6)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + if (sgp == null) + sgp = new Surrogate.Parser(); + int uc = sgp.parse(c, sa, sp - 1, sl); + if (uc < 0) { + if (malformedInputAction() != CodingErrorAction.REPLACE) + return -1; + da[dp++] = repl; + } else { + da[dp++] = (byte)(0xf0 | ((uc >> 18))); + da[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); + da[dp++] = (byte)(0x80 | ((uc >> 6) & 0x3f)); + da[dp++] = (byte)(0x80 | (uc & 0x3f)); + sp++; // 2 chars + } + } else { + // 3 bytes, 16 bits + da[dp++] = (byte)(0xe0 | ((c >> 12))); + da[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + da[dp++] = (byte)(0x80 | (c & 0x3f)); + } + } + return dp; + } + } +} diff --git a/src/sun/nio/cs/Unicode.java b/src/sun/nio/cs/Unicode.java new file mode 100644 index 00000000..ed8d6770 --- /dev/null +++ b/src/sun/nio/cs/Unicode.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.charset.Charset; + +abstract class Unicode extends Charset + implements HistoricallyNamedCharset +{ + public Unicode(String name, String[] aliases) { + super(name, aliases); + } + + public boolean contains(Charset cs) { + return ((cs instanceof US_ASCII) + || (cs instanceof ISO_8859_1) + || (cs instanceof ISO_8859_15) + || (cs instanceof MS1252) + || (cs instanceof UTF_8) + || (cs instanceof UTF_16) + || (cs instanceof UTF_16BE) + || (cs instanceof UTF_16LE) + || (cs instanceof UTF_16LE_BOM) + || (cs.name().equals("GBK")) + || (cs.name().equals("GB18030")) + || (cs.name().equals("ISO-8859-2")) + || (cs.name().equals("ISO-8859-3")) + || (cs.name().equals("ISO-8859-4")) + || (cs.name().equals("ISO-8859-5")) + || (cs.name().equals("ISO-8859-6")) + || (cs.name().equals("ISO-8859-7")) + || (cs.name().equals("ISO-8859-8")) + || (cs.name().equals("ISO-8859-9")) + || (cs.name().equals("ISO-8859-13")) + || (cs.name().equals("JIS_X0201")) + || (cs.name().equals("x-JIS0208")) + || (cs.name().equals("JIS_X0212-1990")) + || (cs.name().equals("GB2312")) + || (cs.name().equals("EUC-KR")) + || (cs.name().equals("x-EUC-TW")) + || (cs.name().equals("EUC-JP")) + || (cs.name().equals("x-euc-jp-linux")) + || (cs.name().equals("KOI8-R")) + || (cs.name().equals("TIS-620")) + || (cs.name().equals("x-ISCII91")) + || (cs.name().equals("windows-1251")) + || (cs.name().equals("windows-1253")) + || (cs.name().equals("windows-1254")) + || (cs.name().equals("windows-1255")) + || (cs.name().equals("windows-1256")) + || (cs.name().equals("windows-1257")) + || (cs.name().equals("windows-1258")) + || (cs.name().equals("windows-932")) + || (cs.name().equals("x-mswin-936")) + || (cs.name().equals("x-windows-949")) + || (cs.name().equals("x-windows-950")) + || (cs.name().equals("windows-31j")) + || (cs.name().equals("Big5")) + || (cs.name().equals("Big5-HKSCS")) + || (cs.name().equals("x-MS950-HKSCS")) + || (cs.name().equals("ISO-2022-JP")) + || (cs.name().equals("ISO-2022-KR")) + || (cs.name().equals("x-ISO-2022-CN-CNS")) + || (cs.name().equals("x-ISO-2022-CN-GB")) + || (cs.name().equals("Big5-HKSCS")) + || (cs.name().equals("x-Johab")) + || (cs.name().equals("Shift_JIS"))); + } +} diff --git a/src/sun/nio/cs/UnicodeDecoder.java b/src/sun/nio/cs/UnicodeDecoder.java new file mode 100644 index 00000000..de26e95c --- /dev/null +++ b/src/sun/nio/cs/UnicodeDecoder.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; + + +abstract class UnicodeDecoder extends CharsetDecoder { + + protected static final char BYTE_ORDER_MARK = (char) 0xfeff; + protected static final char REVERSED_MARK = (char) 0xfffe; + + protected static final int NONE = 0; + protected static final int BIG = 1; + protected static final int LITTLE = 2; + + private final int expectedByteOrder; + private int currentByteOrder; + private int defaultByteOrder = BIG; + + public UnicodeDecoder(Charset cs, int bo) { + super(cs, 0.5f, 1.0f); + expectedByteOrder = currentByteOrder = bo; + } + + public UnicodeDecoder(Charset cs, int bo, int defaultBO) { + this(cs, bo); + defaultByteOrder = defaultBO; + } + + private char decode(int b1, int b2) { + if (currentByteOrder == BIG) + return (char)((b1 << 8) | b2); + else + return (char)((b2 << 8) | b1); + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + + try { + while (src.remaining() > 1) { + int b1 = src.get() & 0xff; + int b2 = src.get() & 0xff; + + // Byte Order Mark interpretation + if (currentByteOrder == NONE) { + char c = (char)((b1 << 8) | b2); + if (c == BYTE_ORDER_MARK) { + currentByteOrder = BIG; + mark += 2; + continue; + } else if (c == REVERSED_MARK) { + currentByteOrder = LITTLE; + mark += 2; + continue; + } else { + currentByteOrder = defaultByteOrder; + // FALL THROUGH to process b1, b2 normally + } + } + + char c = decode(b1, b2); + + if (c == REVERSED_MARK) { + // A reversed BOM cannot occur within middle of stream + return CoderResult.malformedForLength(2); + } + + // Surrogates + if (Character.isSurrogate(c)) { + if (Character.isHighSurrogate(c)) { + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + char c2 = decode(src.get() & 0xff, src.get() & 0xff); + if (!Character.isLowSurrogate(c2)) + return CoderResult.malformedForLength(4); + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + mark += 4; + dst.put(c); + dst.put(c2); + continue; + } + // Unpaired low surrogate + return CoderResult.malformedForLength(2); + } + + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + mark += 2; + dst.put(c); + + } + return CoderResult.UNDERFLOW; + + } finally { + src.position(mark); + } + } + + protected void implReset() { + currentByteOrder = expectedByteOrder; + } + +} diff --git a/src/sun/nio/cs/UnicodeEncoder.java b/src/sun/nio/cs/UnicodeEncoder.java new file mode 100644 index 00000000..4f4c359b --- /dev/null +++ b/src/sun/nio/cs/UnicodeEncoder.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +/** + * Base class for different flavors of UTF-16 encoders + */ +public abstract class UnicodeEncoder extends CharsetEncoder { + + protected static final char BYTE_ORDER_MARK = '\uFEFF'; + protected static final char REVERSED_MARK = '\uFFFE'; + + protected static final int BIG = 0; + protected static final int LITTLE = 1; + + private int byteOrder; /* Byte order in use */ + private boolean usesMark; /* Write an initial BOM */ + private boolean needsMark; + + protected UnicodeEncoder(Charset cs, int bo, boolean m) { + super(cs, 2.0f, + // Four bytes max if you need a BOM + m ? 4.0f : 2.0f, + // Replacement depends upon byte order + ((bo == BIG) + ? new byte[] { (byte)0xff, (byte)0xfd } + : new byte[] { (byte)0xfd, (byte)0xff })); + usesMark = needsMark = m; + byteOrder = bo; + } + + private void put(char c, ByteBuffer dst) { + if (byteOrder == BIG) { + dst.put((byte)(c >> 8)); + dst.put((byte)(c & 0xff)); + } else { + dst.put((byte)(c & 0xff)); + dst.put((byte)(c >> 8)); + } + } + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + + if (needsMark && src.hasRemaining()) { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + put(BYTE_ORDER_MARK, dst); + needsMark = false; + } + try { + while (src.hasRemaining()) { + char c = src.get(); + if (!Character.isSurrogate(c)) { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + mark++; + put(c, dst); + continue; + } + int d = sgp.parse(c, src); + if (d < 0) + return sgp.error(); + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + mark += 2; + put(Character.highSurrogate(d), dst); + put(Character.lowSurrogate(d), dst); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected void implReset() { + needsMark = usesMark; + } + + public boolean canEncode(char c) { + return ! Character.isSurrogate(c); + } +} diff --git a/src/sun/nio/cs/ext/Big5_HKSCS.java b/src/sun/nio/cs/ext/Big5_HKSCS.java new file mode 100644 index 00000000..29cfe89b --- /dev/null +++ b/src/sun/nio/cs/ext/Big5_HKSCS.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class Big5_HKSCS extends Charset implements HistoricallyNamedCharset +{ + public Big5_HKSCS() { + super("Big5-HKSCS", ExtendedCharsets.aliasesFor("Big5-HKSCS")); + } + + public String historicalName() { + return "Big5_HKSCS"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof Big5) + || (cs instanceof Big5_HKSCS)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + static class Decoder extends HKSCS.Decoder { + private static DoubleByte.Decoder big5 = + (DoubleByte.Decoder)new Big5().newDecoder(); + + private static char[][] b2cBmp = new char[0x100][]; + private static char[][] b2cSupp = new char[0x100][]; + static { + initb2c(b2cBmp, HKSCSMapping.b2cBmpStr); + initb2c(b2cSupp, HKSCSMapping.b2cSuppStr); + } + + private Decoder(Charset cs) { + super(cs, big5, b2cBmp, b2cSupp); + } + } + + static class Encoder extends HKSCS.Encoder { + private static DoubleByte.Encoder big5 = + (DoubleByte.Encoder)new Big5().newEncoder(); + + static char[][] c2bBmp = new char[0x100][]; + static char[][] c2bSupp = new char[0x100][]; + static { + initc2b(c2bBmp, HKSCSMapping.b2cBmpStr, HKSCSMapping.pua); + initc2b(c2bSupp, HKSCSMapping.b2cSuppStr, null); + } + + private Encoder(Charset cs) { + super(cs, big5, c2bBmp, c2bSupp); + } + } +} diff --git a/src/sun/nio/cs/ext/Big5_HKSCS_2001.java b/src/sun/nio/cs/ext/Big5_HKSCS_2001.java new file mode 100644 index 00000000..ad327d9b --- /dev/null +++ b/src/sun/nio/cs/ext/Big5_HKSCS_2001.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class Big5_HKSCS_2001 extends Charset +{ + public Big5_HKSCS_2001() { + super("x-Big5-HKSCS-2001", ExtendedCharsets.aliasesFor("x-Big5-HKSCS-2001")); + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof Big5) + || (cs instanceof Big5_HKSCS_2001)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends HKSCS.Decoder { + private static DoubleByte.Decoder big5 = + (DoubleByte.Decoder)new Big5().newDecoder(); + + private static char[][] b2cBmp = new char[0x100][]; + private static char[][] b2cSupp = new char[0x100][]; + static { + initb2c(b2cBmp, HKSCS2001Mapping.b2cBmpStr); + initb2c(b2cSupp, HKSCS2001Mapping.b2cSuppStr); + } + + private Decoder(Charset cs) { + super(cs, big5, b2cBmp, b2cSupp); + } + } + + private static class Encoder extends HKSCS.Encoder { + private static DoubleByte.Encoder big5 = + (DoubleByte.Encoder)new Big5().newEncoder(); + + static char[][] c2bBmp = new char[0x100][]; + static char[][] c2bSupp = new char[0x100][]; + static { + initc2b(c2bBmp, HKSCS2001Mapping.b2cBmpStr, + HKSCS2001Mapping.pua); + initc2b(c2bSupp, HKSCS2001Mapping.b2cSuppStr, null); + } + + private Encoder(Charset cs) { + super(cs, big5, c2bBmp, c2bSupp); + } + } +} diff --git a/src/sun/nio/cs/ext/Big5_Solaris.java b/src/sun/nio/cs/ext/Big5_Solaris.java new file mode 100644 index 00000000..657e9186 --- /dev/null +++ b/src/sun/nio/cs/ext/Big5_Solaris.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class Big5_Solaris extends Charset implements HistoricallyNamedCharset +{ + public Big5_Solaris() { + super("x-Big5-Solaris", ExtendedCharsets.aliasesFor("x-Big5-Solaris")); + } + + public String historicalName() { + return "Big5_Solaris"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof Big5) + || (cs instanceof Big5_Solaris)); + } + + public CharsetDecoder newDecoder() { + initb2c(); + return new DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe); + } + + public CharsetEncoder newEncoder() { + initc2b(); + return new DoubleByte.Encoder(this, c2b, c2bIndex); + } + + static char[][] b2c; + static char[] b2cSB; + private static volatile boolean b2cInitialized = false; + + static void initb2c() { + if (b2cInitialized) + return; + synchronized (Big5_Solaris.class) { + if (b2cInitialized) + return; + Big5.initb2c(); + b2c = Big5.b2c.clone(); + // Big5 Solaris implementation has 7 additional mappings + int[] sol = new int[] { + 0xF9D6, 0x7881, + 0xF9D7, 0x92B9, + 0xF9D8, 0x88CF, + 0xF9D9, 0x58BB, + 0xF9DA, 0x6052, + 0xF9DB, 0x7CA7, + 0xF9DC, 0x5AFA }; + if (b2c[0xf9] == DoubleByte.B2C_UNMAPPABLE) { + b2c[0xf9] = new char[0xfe - 0x40 + 1]; + Arrays.fill(b2c[0xf9], UNMAPPABLE_DECODING); + } + + for (int i = 0; i < sol.length;) { + b2c[0xf9][sol[i++] & 0xff - 0x40] = (char)sol[i++]; + } + b2cSB = Big5.b2cSB; + b2cInitialized = true; + } + } + + static char[] c2b; + static char[] c2bIndex; + private static volatile boolean c2bInitialized = false; + + static void initc2b() { + if (c2bInitialized) + return; + synchronized (Big5_Solaris.class) { + if (c2bInitialized) + return; + Big5.initc2b(); + c2b = Big5.c2b.clone(); + c2bIndex = Big5.c2bIndex.clone(); + int[] sol = new int[] { + 0x7881, 0xF9D6, + 0x92B9, 0xF9D7, + 0x88CF, 0xF9D8, + 0x58BB, 0xF9D9, + 0x6052, 0xF9DA, + 0x7CA7, 0xF9DB, + 0x5AFA, 0xF9DC }; + + for (int i = 0; i < sol.length;) { + int c = sol[i++]; + // no need to check c2bIndex[c >>8], we know it points + // to the appropriate place. + c2b[c2bIndex[c >> 8] + (c & 0xff)] = (char)sol[i++]; + } + c2bInitialized = true; + } + } +} diff --git a/src/sun/nio/cs/ext/DelegatableDecoder.java b/src/sun/nio/cs/ext/DelegatableDecoder.java new file mode 100644 index 00000000..13b49755 --- /dev/null +++ b/src/sun/nio/cs/ext/DelegatableDecoder.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CoderResult; + +/** + * A decoder that can be delegated to by another decoder + * when normal inheritance cannot be used. + * Used by autodecting decoders. + */ +interface DelegatableDecoder { + CoderResult decodeLoop(ByteBuffer src, CharBuffer dst); + void implReset(); + CoderResult implFlush(CharBuffer out); +} diff --git a/src/sun/nio/cs/ext/DoubleByte.java b/src/sun/nio/cs/ext/DoubleByte.java new file mode 100644 index 00000000..2ab951b7 --- /dev/null +++ b/src/sun/nio/cs/ext/DoubleByte.java @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.util.Arrays; + +import sun.nio.cs.ArrayDecoder; +import sun.nio.cs.ArrayEncoder; +import sun.nio.cs.Surrogate; + +/* + * Four types of "DoubleByte" charsets are implemented in this class + * (1)DoubleByte + * The "mostly widely used" multibyte charset, a combination of + * a singlebyte character set (usually the ASCII charset) and a + * doublebyte character set. The codepoint values of singlebyte + * and doublebyte don't overlap. Microsoft's multibyte charsets + * and IBM's "DBCS_ASCII" charsets, such as IBM1381, 942, 943, + * 948, 949 and 950 are such charsets. + * + * (2)DoubleByte_EBCDIC + * IBM EBCDIC Mix multibyte charset. Use SO and SI to shift (switch) + * in and out between the singlebyte character set and doublebyte + * character set. + * + * (3)DoubleByte_SIMPLE_EUC + * It's a "simple" form of EUC encoding scheme, only have the + * singlebyte character set G0 and one doublebyte character set + * G1 are defined, G2 (with SS2) and G3 (with SS3) are not used. + * So it is actually the same as the "typical" type (1) mentioned + * above, except it return "malformed" for the SS2 and SS3 when + * decoding. + * + * (4)DoubleByte ONLY + * A "pure" doublebyte only character set. From implementation + * point of view, this is the type (1) with "decodeSingle" always + * returns unmappable. + * + * For simplicity, all implementations share the same decoding and + * encoding data structure. + * + * Decoding: + * + * char[][] b2c; + * char[] b2cSB; + * int b2Min, b2Max + * + * public char decodeSingle(int b) { + * return b2cSB.[b]; + * } + * + * public char decodeDouble(int b1, int b2) { + * if (b2 < b2Min || b2 > b2Max) + * return UNMAPPABLE_DECODING; + * return b2c[b1][b2 - b2Min]; + * } + * + * (1)b2Min, b2Max are the corresponding min and max value of the + * low-half of the double-byte. + * (2)The high 8-bit/b1 of the double-byte are used to indexed into + * b2c array. + * + * Encoding: + * + * char[] c2b; + * char[] c2bIndex; + * + * public int encodeChar(char ch) { + * return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; + * } + * + */ + +public class DoubleByte { + + public final static char[] B2C_UNMAPPABLE; + static { + B2C_UNMAPPABLE = new char[0x100]; + Arrays.fill(B2C_UNMAPPABLE, UNMAPPABLE_DECODING); + } + + public static class Decoder extends CharsetDecoder + implements DelegatableDecoder, ArrayDecoder + { + final char[][] b2c; + final char[] b2cSB; + final int b2Min; + final int b2Max; + + // for SimpleEUC override + protected CoderResult crMalformedOrUnderFlow(int b) { + return CoderResult.UNDERFLOW; + } + + protected CoderResult crMalformedOrUnmappable(int b1, int b2) { + if (b2c[b1] == B2C_UNMAPPABLE || // isNotLeadingByte(b1) + b2c[b2] != B2C_UNMAPPABLE || // isLeadingByte(b2) + decodeSingle(b2) != UNMAPPABLE_DECODING) { // isSingle(b2) + return CoderResult.malformedForLength(1); + } + return CoderResult.unmappableForLength(2); + } + + Decoder(Charset cs, float avgcpb, float maxcpb, + char[][] b2c, char[] b2cSB, + int b2Min, int b2Max) { + super(cs, avgcpb, maxcpb); + this.b2c = b2c; + this.b2cSB = b2cSB; + this.b2Min = b2Min; + this.b2Max = b2Max; + } + + Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max); + } + + protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl && dp < dl) { + // inline the decodeSingle/Double() for better performance + int inSize = 1; + int b1 = sa[sp] & 0xff; + char c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) { + if (sl - sp < 2) + return crMalformedOrUnderFlow(b1); + int b2 = sa[sp + 1] & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + return crMalformedOrUnmappable(b1, b2); + } + inSize++; + } + da[dp++] = c; + sp += inSize; + } + return (sp >= sl) ? CoderResult.UNDERFLOW + : CoderResult.OVERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + + while (src.hasRemaining() && dst.hasRemaining()) { + int b1 = src.get() & 0xff; + char c = b2cSB[b1]; + int inSize = 1; + if (c == UNMAPPABLE_DECODING) { + if (src.remaining() < 1) + return crMalformedOrUnderFlow(b1); + int b2 = src.get() & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) + return crMalformedOrUnmappable(b1, b2); + inSize++; + } + dst.put(c); + mark += inSize; + } + return src.hasRemaining()? CoderResult.OVERFLOW + : CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + // Make some protected methods public for use by JISAutoDetect + public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + int dp = 0; + int sl = sp + len; + char repl = replacement().charAt(0); + while (sp < sl) { + int b1 = src[sp++] & 0xff; + char c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) { + if (sp < sl) { + int b2 = src[sp++] & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + if (b2c[b1] == B2C_UNMAPPABLE || // isNotLeadingByte + b2c[b2] != B2C_UNMAPPABLE || // isLeadingByte + decodeSingle(b2) != UNMAPPABLE_DECODING) { + sp--; + } + } + } + if (c == UNMAPPABLE_DECODING) { + c = repl; + } + } + dst[dp++] = c; + } + return dp; + } + + public void implReset() { + super.implReset(); + } + + public CoderResult implFlush(CharBuffer out) { + return super.implFlush(out); + } + + // decode loops are not using decodeSingle/Double() for performance + // reason. + public char decodeSingle(int b) { + return b2cSB[b]; + } + + public char decodeDouble(int b1, int b2) { + if (b1 < 0 || b1 > b2c.length || + b2 < b2Min || b2 > b2Max) + return UNMAPPABLE_DECODING; + return b2c[b1][b2 - b2Min]; + } + } + + // IBM_EBCDIC_DBCS + public static class Decoder_EBCDIC extends Decoder { + private static final int SBCS = 0; + private static final int DBCS = 1; + private static final int SO = 0x0e; + private static final int SI = 0x0f; + private int currentState; + + Decoder_EBCDIC(Charset cs, + char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + super(cs, b2c, b2cSB, b2Min, b2Max); + } + + public void implReset() { + currentState = SBCS; + } + + // Check validity of dbcs ebcdic byte pair values + // + // First byte : 0x41 -- 0xFE + // Second byte: 0x41 -- 0xFE + // Doublebyte blank: 0x4040 + // + // The validation implementation in "old" DBCS_IBM_EBCDIC and sun.io + // as + // if ((b1 != 0x40 || b2 != 0x40) && + // (b2 < 0x41 || b2 > 0xfe)) {...} + // is not correct/complete (range check for b1) + // + private static boolean isDoubleByte(int b1, int b2) { + return (0x41 <= b1 && b1 <= 0xfe && 0x41 <= b2 && b2 <= 0xfe) + || (b1 == 0x40 && b2 == 0x40); // DBCS-HOST SPACE + } + + protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + // don't check dp/dl together here, it's possible to + // decdoe a SO/SI without space in output buffer. + while (sp < sl) { + int b1 = sa[sp] & 0xff; + int inSize = 1; + if (b1 == SO) { // Shift out + if (currentState != SBCS) + return CoderResult.malformedForLength(1); + else + currentState = DBCS; + } else if (b1 == SI) { + if (currentState != DBCS) + return CoderResult.malformedForLength(1); + else + currentState = SBCS; + } else { + char c = UNMAPPABLE_DECODING; + if (currentState == SBCS) { + c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(1); + } else { + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + int b2 = sa[sp + 1] & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + if (!isDoubleByte(b1, b2)) + return CoderResult.malformedForLength(2); + return CoderResult.unmappableForLength(2); + } + inSize++; + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + + da[dp++] = c; + } + sp += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int b1 = src.get() & 0xff; + int inSize = 1; + if (b1 == SO) { // Shift out + if (currentState != SBCS) + return CoderResult.malformedForLength(1); + else + currentState = DBCS; + } else if (b1 == SI) { + if (currentState != DBCS) + return CoderResult.malformedForLength(1); + else + currentState = SBCS; + } else { + char c = UNMAPPABLE_DECODING; + if (currentState == SBCS) { + c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(1); + } else { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + int b2 = src.get()&0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + if (!isDoubleByte(b1, b2)) + return CoderResult.malformedForLength(2); + return CoderResult.unmappableForLength(2); + } + inSize++; + } + + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + + dst.put(c); + } + mark += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + int dp = 0; + int sl = sp + len; + currentState = SBCS; + char repl = replacement().charAt(0); + while (sp < sl) { + int b1 = src[sp++] & 0xff; + if (b1 == SO) { // Shift out + if (currentState != SBCS) + dst[dp++] = repl; + else + currentState = DBCS; + } else if (b1 == SI) { + if (currentState != DBCS) + dst[dp++] = repl; + else + currentState = SBCS; + } else { + char c = UNMAPPABLE_DECODING; + if (currentState == SBCS) { + c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) + c = repl; + } else { + if (sl == sp) { + c = repl; + } else { + int b2 = src[sp++] & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + c = repl; + } + } + } + dst[dp++] = c; + } + } + return dp; + } + } + + // DBCS_ONLY + public static class Decoder_DBCSONLY extends Decoder { + static final char[] b2cSB_UNMAPPABLE; + static { + b2cSB_UNMAPPABLE = new char[0x100]; + Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING); + } + Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max); + } + } + + // EUC_SIMPLE + // The only thing we need to "override" is to check SS2/SS3 and + // return "malformed" if found + public static class Decoder_EUC_SIM extends Decoder { + private final int SS2 = 0x8E; + private final int SS3 = 0x8F; + + Decoder_EUC_SIM(Charset cs, + char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + super(cs, b2c, b2cSB, b2Min, b2Max); + } + + // No support provided for G2/G3 for SimpleEUC + protected CoderResult crMalformedOrUnderFlow(int b) { + if (b == SS2 || b == SS3 ) + return CoderResult.malformedForLength(1); + return CoderResult.UNDERFLOW; + } + + protected CoderResult crMalformedOrUnmappable(int b1, int b2) { + if (b1 == SS2 || b1 == SS3 ) + return CoderResult.malformedForLength(1); + return CoderResult.unmappableForLength(2); + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + int dp = 0; + int sl = sp + len; + char repl = replacement().charAt(0); + while (sp < sl) { + int b1 = src[sp++] & 0xff; + char c = b2cSB[b1]; + if (c == UNMAPPABLE_DECODING) { + if (sp < sl) { + int b2 = src[sp++] & 0xff; + if (b2 < b2Min || b2 > b2Max || + (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) { + if (b1 == SS2 || b1 == SS3) { + sp--; + } + c = repl; + } + } else { + c = repl; + } + } + dst[dp++] = c; + } + return dp; + } + } + + public static class Encoder extends CharsetEncoder + implements ArrayEncoder + { + final int MAX_SINGLEBYTE = 0xff; + private final char[] c2b; + private final char[] c2bIndex; + Surrogate.Parser sgp; + + protected Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + super(cs, 2.0f, 2.0f); + this.c2b = c2b; + this.c2bIndex = c2bIndex; + } + + Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) { + super(cs, avg, max, repl); + this.c2b = c2b; + this.c2bIndex = c2bIndex; + } + + public boolean canEncode(char c) { + return encodeChar(c) != UNMAPPABLE_ENCODING; + } + + Surrogate.Parser sgp() { + if (sgp == null) + sgp = new Surrogate.Parser(); + return sgp; + } + + protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + char c = sa[sp]; + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp().parse(c, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.unmappableForLength(1); + } + + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(bb >> 8); + da[dp++] = (byte)bb; + } else { // SingleByte + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)bb; + } + + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp().parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.unmappableForLength(1); + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(bb >> 8)); + dst.put((byte)(bb)); + } else { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)bb); + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + protected byte[] repl = replacement(); + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement; + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + int dl = dst.length; + while (sp < sl) { + char c = src[sp++]; + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + + } + return dp; + } + + public int encodeChar(char ch) { + return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; + } + + // init the c2b and c2bIndex tables from b2c. + static void initC2B(String[] b2c, String b2cSB, String b2cNR, String c2bNR, + int b2Min, int b2Max, + char[] c2b, char[] c2bIndex) + { + Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING); + int off = 0x100; + + char[][] b2c_ca = new char[b2c.length][]; + char[] b2cSB_ca = null; + if (b2cSB != null) + b2cSB_ca = b2cSB.toCharArray(); + + for (int i = 0; i < b2c.length; i++) { + if (b2c[i] == null) + continue; + b2c_ca[i] = b2c[i].toCharArray(); + } + + if (b2cNR != null) { + int j = 0; + while (j < b2cNR.length()) { + char b = b2cNR.charAt(j++); + char c = b2cNR.charAt(j++); + if (b < 0x100 && b2cSB_ca != null) { + if (b2cSB_ca[b] == c) + b2cSB_ca[b] = UNMAPPABLE_DECODING; + } else { + if (b2c_ca[b >> 8][(b & 0xff) - b2Min] == c) + b2c_ca[b >> 8][(b & 0xff) - b2Min] = UNMAPPABLE_DECODING; + } + } + } + + if (b2cSB_ca != null) { // SingleByte + for (int b = 0; b < b2cSB_ca.length; b++) { + char c = b2cSB_ca[b]; + if (c == UNMAPPABLE_DECODING) + continue; + int index = c2bIndex[c >> 8]; + if (index == 0) { + index = off; + off += 0x100; + c2bIndex[c >> 8] = (char)index; + } + c2b[index + (c & 0xff)] = (char)b; + } + } + + for (int b1 = 0; b1 < b2c.length; b1++) { // DoubleByte + char[] db = b2c_ca[b1]; + if (db == null) + continue; + for (int b2 = b2Min; b2 <= b2Max; b2++) { + char c = db[b2 - b2Min]; + if (c == UNMAPPABLE_DECODING) + continue; + int index = c2bIndex[c >> 8]; + if (index == 0) { + index = off; + off += 0x100; + c2bIndex[c >> 8] = (char)index; + } + c2b[index + (c & 0xff)] = (char)((b1 << 8) | b2); + } + } + + if (c2bNR != null) { + // add c->b only nr entries + for (int i = 0; i < c2bNR.length(); i += 2) { + char b = c2bNR.charAt(i); + char c = c2bNR.charAt(i + 1); + int index = (c >> 8); + if (c2bIndex[index] == 0) { + c2bIndex[index] = (char)off; + off += 0x100; + } + index = c2bIndex[index] + (c & 0xff); + c2b[index] = b; + } + } + } + } + + public static class Encoder_DBCSONLY extends Encoder { + Encoder_DBCSONLY(Charset cs, byte[] repl, + char[] c2b, char[] c2bIndex) { + super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex); + } + + public int encodeChar(char ch) { + int bb = super.encodeChar(ch); + if (bb <= MAX_SINGLEBYTE) + return UNMAPPABLE_ENCODING; + return bb; + } + } + + + + public static class Encoder_EBCDIC extends Encoder { + static final int SBCS = 0; + static final int DBCS = 1; + static final byte SO = 0x0e; + static final byte SI = 0x0f; + + protected int currentState = SBCS; + + Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) { + super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex); + } + + protected void implReset() { + currentState = SBCS; + } + + protected CoderResult implFlush(ByteBuffer out) { + if (currentState == DBCS) { + if (out.remaining() < 1) + return CoderResult.OVERFLOW; + out.put(SI); + } + implReset(); + return CoderResult.UNDERFLOW; + } + + protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + char c = sa[sp]; + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp().parse(c, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.unmappableForLength(1); + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + currentState = DBCS; + da[dp++] = SO; + } + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(bb >> 8); + da[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + currentState = SBCS; + da[dp++] = SI; + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)bb; + + } + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + if (sgp().parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + return CoderResult.unmappableForLength(1); + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + currentState = DBCS; + dst.put(SO); + } + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(bb >> 8)); + dst.put((byte)(bb)); + } else { // Single-byte + if (currentState == DBCS) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + currentState = SBCS; + dst.put(SI); + } + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)bb); + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = src[sp++]; + int bb = encodeChar(c); + + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + currentState = DBCS; + dst[dp++] = SO; + } + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + dst[dp++] = (byte)bb; + } + } + + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + return dp; + } + } + + // EUC_SIMPLE + public static class Encoder_EUC_SIM extends Encoder { + Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) { + super(cs, c2b, c2bIndex); + } + } + +} diff --git a/src/sun/nio/cs/ext/DoubleByteEncoder.java b/src/sun/nio/cs/ext/DoubleByteEncoder.java new file mode 100644 index 00000000..54eecd15 --- /dev/null +++ b/src/sun/nio/cs/ext/DoubleByteEncoder.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.Surrogate; + +public abstract class DoubleByteEncoder + extends CharsetEncoder +{ + + private short index1[]; + private String index2[]; + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + protected DoubleByteEncoder(Charset cs, + short[] index1, String[] index2) + { + super(cs, 2.0f, 2.0f); + this.index1 = index1; + this.index2 = index2; + } + + protected DoubleByteEncoder(Charset cs, + short[] index1, String[] index2, + float avg, float max) + { + super(cs, avg, max); + this.index1 = index1; + this.index2 = index2; + } + + protected DoubleByteEncoder(Charset cs, + short[] index1, String[] index2, byte[] repl) + { + super(cs, 2.0f, 2.0f, repl); + this.index1 = index1; + this.index2 = index2; + } + + + protected DoubleByteEncoder(Charset cs, + short[] index1, String[] index2, + byte[] repl, float avg, float max) + { + super(cs, avg, max,repl); + this.index1 = index1; + this.index2 = index2; + } + + public boolean canEncode(char c) { + return (encodeSingle(c) != -1 || + encodeDouble(c) != 0); + } + + private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + char c = sa[sp]; + if (Character.isSurrogate(c)) { + if (sgp.parse(c, sa, sp, sl) < 0) + return sgp.error(); + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + char c2 = sa[sp + 1]; + + byte[] outputBytes = new byte[2]; + outputBytes = encodeSurrogate(c, c2); + + if (outputBytes == null) { + return sgp.unmappableResult(); + } + else { + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = outputBytes[0]; + da[dp++] = outputBytes[1]; + sp += 2; + continue; + } + } + if (c >= '\uFFFE') + return CoderResult.unmappableForLength(1); + + int b = encodeSingle(c); + if (b != -1) { // Single Byte + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)b; + sp++; + continue; + } + + int ncode = encodeDouble(c); + if (ncode != 0 && c != '\u0000' ) { + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte) ((ncode & 0xff00) >> 8); + da[dp++] = (byte) (ncode & 0xff); + sp++; + continue; + } + return CoderResult.unmappableForLength(1); + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + + try { + while (src.hasRemaining()) { + char c = src.get(); + if (Character.isSurrogate(c)) { + int surr; + if ((surr = sgp.parse(c, src)) < 0) + return sgp.error(); + char c2 = Surrogate.low(surr); + byte[] outputBytes = new byte[2]; + outputBytes = encodeSurrogate(c, c2); + + if (outputBytes == null) { + return sgp.unmappableResult(); + } else { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + mark += 2; + dst.put(outputBytes[0]); + dst.put(outputBytes[1]); + continue; + } + } + if (c >= '\uFFFE') + return CoderResult.unmappableForLength(1); + int b = encodeSingle(c); + + if (b != -1) { // Single-byte character + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + mark++; + dst.put((byte)b); + continue; + } + // Double Byte character + + int ncode = encodeDouble(c); + if (ncode != 0 && c != '\u0000') { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + mark++; + dst.put((byte) ((ncode & 0xff00) >> 8)); + dst.put((byte) ncode); + continue; + } + return CoderResult.unmappableForLength(1); + } + + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (true && src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + /* + * Can be changed by subclass + */ + protected int encodeDouble(char ch) { + int offset = index1[((ch & 0xff00) >> 8 )] << 8; + return index2[offset >> 12].charAt((offset & 0xfff) + (ch & 0xff)); + } + + /* + * Can be changed by subclass + */ + protected int encodeSingle(char inputChar) { + if (inputChar < 0x80) + return (byte)inputChar; + else + return -1; + } + + /** + * Protected method which should be overridden by concrete DBCS + * CharsetEncoder classes which included supplementary characters + * within their mapping coverage. + * null return value indicates surrogate values could not be + * handled or encoded. + */ + protected byte[] encodeSurrogate(char highSurrogate, char lowSurrogate) { + return null; + } +} diff --git a/src/sun/nio/cs/ext/EUC_JP.java b/src/sun/nio/cs/ext/EUC_JP.java new file mode 100644 index 00000000..3e36df3f --- /dev/null +++ b/src/sun/nio/cs/ext/EUC_JP.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.nio.cs.SingleByte; +import sun.nio.cs.Surrogate; + +public class EUC_JP + extends Charset + implements HistoricallyNamedCharset +{ + public EUC_JP() { + super("EUC-JP", ExtendedCharsets.aliasesFor("EUC-JP")); + } + + public String historicalName() { + return "EUC_JP"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof JIS_X_0201) + || (cs instanceof JIS_X_0208) + || (cs instanceof JIS_X_0212) + || (cs instanceof EUC_JP)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + static class Decoder extends CharsetDecoder + implements DelegatableDecoder { + + final static SingleByte.Decoder DEC0201 = + (SingleByte.Decoder)new JIS_X_0201().newDecoder(); + + final static DoubleByte.Decoder DEC0208 = + (DoubleByte.Decoder)new JIS_X_0208().newDecoder(); + + final static DoubleByte.Decoder DEC0212 = + (DoubleByte.Decoder)new JIS_X_0212().newDecoder(); + + private final SingleByte.Decoder dec0201; + private final DoubleByte.Decoder dec0208; + private final DoubleByte.Decoder dec0212; + + protected Decoder(Charset cs) { + this(cs, 0.5f, 1.0f, DEC0201, DEC0208, DEC0212); + } + + protected Decoder(Charset cs, float avgCpb, float maxCpb, + SingleByte.Decoder dec0201, + DoubleByte.Decoder dec0208, + DoubleByte.Decoder dec0212) { + super(cs, avgCpb, maxCpb); + this.dec0201 = dec0201; + this.dec0208 = dec0208; + this.dec0212 = dec0212; + } + + + protected char decodeDouble(int byte1, int byte2) { + if (byte1 == 0x8e) { + if (byte2 < 0x80) + return UNMAPPABLE_DECODING; + return dec0201.decode((byte)byte2); + } + return dec0208.decodeDouble(byte1 - 0x80, byte2 - 0x80); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int b1 = 0, b2 = 0; + int inputSize = 0; + char outputChar = UNMAPPABLE_DECODING; + try { + while (sp < sl) { + b1 = sa[sp] & 0xff; + inputSize = 1; + + if ((b1 & 0x80) == 0) { + outputChar = (char)b1; + } else { // Multibyte char + if (b1 == 0x8f) { // JIS0212 + if (sp + 3 > sl) + return CoderResult.UNDERFLOW; + b1 = sa[sp + 1] & 0xff; + b2 = sa[sp + 2] & 0xff; + inputSize += 2; + if (dec0212 == null) // JIS02012 not supported + return CoderResult.unmappableForLength(inputSize); + outputChar = dec0212.decodeDouble(b1-0x80, b2-0x80); + } else { // JIS0201, JIS0208 + if (sp + 2 > sl) + return CoderResult.UNDERFLOW; + b2 = sa[sp + 1] & 0xff; + inputSize++; + outputChar = decodeDouble(b1, b2); + } + } + if (outputChar == UNMAPPABLE_DECODING) { // can't be decoded + return CoderResult.unmappableForLength(inputSize); + } + if (dp + 1 > dl) + return CoderResult.OVERFLOW; + da[dp++] = outputChar; + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + int b1 = 0, b2 = 0; + int inputSize = 0; + char outputChar = UNMAPPABLE_DECODING; + + try { + while (src.hasRemaining()) { + b1 = src.get() & 0xff; + inputSize = 1; + if ((b1 & 0x80) == 0) { + outputChar = (char)b1; + } else { // Multibyte char + if (b1 == 0x8f) { // JIS0212 + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + b1 = src.get() & 0xff; + b2 = src.get() & 0xff; + inputSize += 2; + if (dec0212 == null) // JIS02012 not supported + return CoderResult.unmappableForLength(inputSize); + outputChar = dec0212.decodeDouble(b1-0x80, b2-0x80); + } else { // JIS0201 JIS0208 + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b2 = src.get() & 0xff; + inputSize++; + outputChar = decodeDouble(b1, b2); + } + } + if (outputChar == UNMAPPABLE_DECODING) { + return CoderResult.unmappableForLength(inputSize); + } + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(outputChar); + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + // Make some protected methods public for use by JISAutoDetect + public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + public void implReset() { + super.implReset(); + } + public CoderResult implFlush(CharBuffer out) { + return super.implFlush(out); + } + } + + + static class Encoder extends CharsetEncoder { + + final static SingleByte.Encoder ENC0201 = + (SingleByte.Encoder)new JIS_X_0201().newEncoder(); + + final static DoubleByte.Encoder ENC0208 = + (DoubleByte.Encoder)new JIS_X_0208().newEncoder(); + + final static DoubleByte.Encoder ENC0212 = + (DoubleByte.Encoder)new JIS_X_0212().newEncoder(); + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + + private final SingleByte.Encoder enc0201; + private final DoubleByte.Encoder enc0208; + private final DoubleByte.Encoder enc0212; + + protected Encoder(Charset cs) { + this(cs, 3.0f, 3.0f, ENC0201, ENC0208, ENC0212); + } + + protected Encoder(Charset cs, float avgBpc, float maxBpc, + SingleByte.Encoder enc0201, + DoubleByte.Encoder enc0208, + DoubleByte.Encoder enc0212) { + super(cs, avgBpc, maxBpc); + this.enc0201 = enc0201; + this.enc0208 = enc0208; + this.enc0212 = enc0212; + } + + public boolean canEncode(char c) { + byte[] encodedBytes = new byte[3]; + return encodeSingle(c, encodedBytes) != 0 || + encodeDouble(c) != UNMAPPABLE_ENCODING; + } + + protected int encodeSingle(char inputChar, byte[] outputByte) { + int b = enc0201.encode(inputChar); + if (b == UNMAPPABLE_ENCODING) + return 0; + if (b >= 0 && b < 128) { + outputByte[0] = (byte)b; + return 1; + } + outputByte[0] = (byte)0x8e; + outputByte[1] = (byte)b; + return 2; + } + + protected int encodeDouble(char ch) { + int b = enc0208.encodeChar(ch); + if (b != UNMAPPABLE_ENCODING) + return b + 0x8080; + if (enc0212 != null) { + b = enc0212.encodeChar(ch); + if (b != UNMAPPABLE_ENCODING) + b += 0x8F8080; + } + return b; + } + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int outputSize = 0; + byte[] outputByte; + int inputSize = 0; // Size of input + byte[] tmpBuf = new byte[3]; + + try { + while (sp < sl) { + outputByte = tmpBuf; + char c = sa[sp]; + if (Character.isSurrogate(c)) { + if (sgp.parse(c, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + outputSize = encodeSingle(c, outputByte); + if (outputSize == 0) { // DoubleByte + int ncode = encodeDouble(c); + if (ncode != UNMAPPABLE_ENCODING) { + if ((ncode & 0xFF0000) == 0) { + outputByte[0] = (byte) ((ncode & 0xff00) >> 8); + outputByte[1] = (byte) (ncode & 0xff); + outputSize = 2; + } else { + outputByte[0] = (byte) 0x8f; + outputByte[1] = (byte) ((ncode & 0xff00) >> 8); + outputByte[2] = (byte) (ncode & 0xff); + outputSize = 3; + } + } else { + return CoderResult.unmappableForLength(1); + } + } + if (dl - dp < outputSize) + return CoderResult.OVERFLOW; + // Put the byte in the output buffer + for (int i = 0; i < outputSize; i++) { + da[dp++] = outputByte[i]; + } + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int outputSize = 0; + byte[] outputByte; + int inputSize = 0; // Size of input + byte[] tmpBuf = new byte[3]; + + int mark = src.position(); + + try { + while (src.hasRemaining()) { + outputByte = tmpBuf; + char c = src.get(); + if (Character.isSurrogate(c)) { + if (sgp.parse(c, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + outputSize = encodeSingle(c, outputByte); + if (outputSize == 0) { // DoubleByte + int ncode = encodeDouble(c); + if (ncode != UNMAPPABLE_ENCODING) { + if ((ncode & 0xFF0000) == 0) { + outputByte[0] = (byte) ((ncode & 0xff00) >> 8); + outputByte[1] = (byte) (ncode & 0xff); + outputSize = 2; + } else { + outputByte[0] = (byte) 0x8f; + outputByte[1] = (byte) ((ncode & 0xff00) >> 8); + outputByte[2] = (byte) (ncode & 0xff); + outputSize = 3; + } + } else { + return CoderResult.unmappableForLength(1); + } + } + if (dst.remaining() < outputSize) + return CoderResult.OVERFLOW; + // Put the byte in the output buffer + for (int i = 0; i < outputSize; i++) { + dst.put(outputByte[i]); + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/EUC_JP_LINUX.java b/src/sun/nio/cs/ext/EUC_JP_LINUX.java new file mode 100644 index 00000000..753f636b --- /dev/null +++ b/src/sun/nio/cs/ext/EUC_JP_LINUX.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class EUC_JP_LINUX + extends Charset + implements HistoricallyNamedCharset +{ + public EUC_JP_LINUX() { + super("x-euc-jp-linux", ExtendedCharsets.aliasesFor("x-euc-jp-linux")); + } + + public String historicalName() { + return "EUC_JP_LINUX"; + } + + public boolean contains(Charset cs) { + return ((cs instanceof JIS_X_0201) + || (cs.name().equals("US-ASCII")) + || (cs instanceof EUC_JP_LINUX)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends EUC_JP.Decoder { + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f, DEC0201, DEC0208, null); + } + } + + private static class Encoder extends EUC_JP.Encoder { + private Encoder(Charset cs) { + super(cs, 2.0f, 2.0f, ENC0201, ENC0208, null); + } + } +} diff --git a/src/sun/nio/cs/ext/EUC_JP_Open.java b/src/sun/nio/cs/ext/EUC_JP_Open.java new file mode 100644 index 00000000..4cd9419f --- /dev/null +++ b/src/sun/nio/cs/ext/EUC_JP_Open.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class EUC_JP_Open + extends Charset + implements HistoricallyNamedCharset +{ + public EUC_JP_Open() { + super("x-eucJP-Open", ExtendedCharsets.aliasesFor("x-eucJP-Open")); + } + + public String historicalName() { + return "EUC_JP_Solaris"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof JIS_X_0201) + || (cs instanceof EUC_JP)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends EUC_JP.Decoder { + private static DoubleByte.Decoder DEC0208_Solaris = + (DoubleByte.Decoder)new JIS_X_0208_Solaris().newDecoder(); + private static DoubleByte.Decoder DEC0212_Solaris = + (DoubleByte.Decoder)new JIS_X_0212_Solaris().newDecoder(); + + private Decoder(Charset cs) { + // JIS_X_0208_Solaris only has the "extra" mappings, it + // does not have the JIS_X_0208 entries + super(cs, 0.5f, 1.0f, DEC0201, DEC0208, DEC0212_Solaris); + } + + protected char decodeDouble(int byte1, int byte2) { + char c = super.decodeDouble(byte1, byte2); + if (c == UNMAPPABLE_DECODING) + return DEC0208_Solaris.decodeDouble(byte1 - 0x80, byte2 - 0x80); + return c; + } + } + + private static class Encoder extends EUC_JP.Encoder { + private static DoubleByte.Encoder ENC0208_Solaris = + (DoubleByte.Encoder)new JIS_X_0208_Solaris().newEncoder(); + + private static DoubleByte.Encoder ENC0212_Solaris = + (DoubleByte.Encoder)new JIS_X_0212_Solaris().newEncoder(); + + private Encoder(Charset cs) { + // The EUC_JP_Open has some interesting tweak for the + // encoding, so can't just pass the euc0208_solaris to + // the euc_jp. Have to override the encodeDouble() as + // showed below (mapping testing catches this). + // super(cs, 3.0f, 3.0f, ENC0201, ENC0208_Solaris, ENC0212_Solaris); + super(cs); + } + + protected int encodeDouble(char ch) { + int b = super.encodeDouble(ch); + if (b != UNMAPPABLE_ENCODING) + return b; + b = ENC0208_Solaris.encodeChar(ch); + if (b != UNMAPPABLE_ENCODING && b > 0x7500) { + return 0x8F8080 + ENC0212_Solaris.encodeChar(ch); + } + return b == UNMAPPABLE_ENCODING ? b : b + 0x8080; + + } + } +} diff --git a/src/sun/nio/cs/ext/EUC_TW.java b/src/sun/nio/cs/ext/EUC_TW.java new file mode 100644 index 00000000..c4e54802 --- /dev/null +++ b/src/sun/nio/cs/ext/EUC_TW.java @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.util.Arrays; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class EUC_TW extends Charset implements HistoricallyNamedCharset +{ + private static final int SS2 = 0x8E; + + /* + (1) EUC_TW + Second byte of EUC_TW for cs2 is in range of + 0xA1-0xB0 for plane 1-16. According to CJKV /163, + plane1 is coded in both cs1 and cs2. This impl + however does not decode the codepoints of plane1 + in cs2, so only p2-p7 and p15 are supported in cs2. + + Plane2 0xA2; + Plane3 0xA3; + Plane4 0xA4; + Plane5 0xA5; + Plane6 0xA6; + Plane7 0xA7; + Plane15 0xAF; + + (2) Mapping + The fact that all supplementary characters encoded in EUC_TW are + in 0x2xxxx range gives us the room to optimize the data tables. + + Decoding: + (1) save the lower 16-bit value of all codepoints of b->c mapping + in a String array table String[plane] b2c. + (2) save "codepoint is supplementary" info (one bit) in a + byte[] b2cIsSupp, so 8 codepoints (same codepoint value, different + plane No) share one byte. + + Encoding: + (1)c->b mappings are stored in + char[]c2b/char[]c2bIndex + char[]c2bSupp/char[]c2bIndexsupp (indexed by lower 16-bit + (2)byte[] c2bPlane stores the "plane info" of each euc-tw codepoints, + BMP and Supp share the low/high 4 bits of one byte. + + Mapping tables are stored separated in EUC_TWMapping, which + is generated by tool. + */ + + public EUC_TW() { + super("x-EUC-TW", ExtendedCharsets.aliasesFor("x-EUC-TW")); + } + + public String historicalName() { + return "EUC_TW"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof EUC_TW)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + public static class Decoder extends CharsetDecoder { + public Decoder(Charset cs) { + super(cs, 2.0f, 2.0f); + } + + char[] c1 = new char[1]; + char[] c2 = new char[2]; + public char[] toUnicode(int b1, int b2, int p) { + return decode(b1, b2, p, c1, c2); + } + + static final String[] b2c = EUC_TWMapping.b2c; + static final int b1Min = EUC_TWMapping.b1Min; + static final int b1Max = EUC_TWMapping.b1Max; + static final int b2Min = EUC_TWMapping.b2Min; + static final int b2Max = EUC_TWMapping.b2Max; + static final int dbSegSize = b2Max - b2Min + 1; + static final byte[] b2cIsSupp; + + // adjust from cns planeNo to the plane index of b2c + static final byte[] cnspToIndex = new byte[0x100]; + static { + Arrays.fill(cnspToIndex, (byte)-1); + cnspToIndex[0xa2] = 1; cnspToIndex[0xa3] = 2; cnspToIndex[0xa4] = 3; + cnspToIndex[0xa5] = 4; cnspToIndex[0xa6] = 5; cnspToIndex[0xa7] = 6; + cnspToIndex[0xaf] = 7; + } + + //static final BitSet b2cIsSupp; + static { + String b2cIsSuppStr = EUC_TWMapping.b2cIsSuppStr; + // work on a local copy is much faster than operate + // directly on b2cIsSupp + byte[] flag = new byte[b2cIsSuppStr.length() << 1]; + int off = 0; + for (int i = 0; i < b2cIsSuppStr.length(); i++) { + char c = b2cIsSuppStr.charAt(i); + flag[off++] = (byte)(c >> 8); + flag[off++] = (byte)(c & 0xff); + } + b2cIsSupp = flag; + } + + static boolean isLegalDB(int b) { + return b >= b1Min && b <= b1Max; + } + + static char[] decode(int b1, int b2, int p, char[] c1, char[] c2) + { + if (b1 < b1Min || b1 > b1Max || b2 < b2Min || b2 > b2Max) + return null; + int index = (b1 - b1Min) * dbSegSize + b2 - b2Min; + char c = b2c[p].charAt(index); + if (c == UNMAPPABLE_DECODING) + return null; + if ((b2cIsSupp[index] & (1 << p)) == 0) { + c1[0] = c; + return c1; + } else { + c2[0] = Character.highSurrogate(0x20000 + c); + c2[1] = Character.lowSurrogate(0x20000 + c); + return c2; + } + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + try { + while (sp < sl) { + int byte1 = sa[sp] & 0xff; + if (byte1 == SS2) { // Codeset 2 G2 + if ( sl - sp < 4) + return CoderResult.UNDERFLOW; + int cnsPlane = cnspToIndex[sa[sp + 1] & 0xff]; + if (cnsPlane < 0) + return CoderResult.malformedForLength(2); + byte1 = sa[sp + 2] & 0xff; + int byte2 = sa[sp + 3] & 0xff; + char[] cc = toUnicode(byte1, byte2, cnsPlane); + if (cc == null) { + if (!isLegalDB(byte1) || !isLegalDB(byte2)) + return CoderResult.malformedForLength(4); + return CoderResult.unmappableForLength(4); + } + if (dl - dp < cc.length) + return CoderResult.OVERFLOW; + if (cc.length == 1) { + da[dp++] = cc[0]; + } else { + da[dp++] = cc[0]; + da[dp++] = cc[1]; + } + sp += 4; + } else if (byte1 < 0x80) { // ASCII G0 + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (char) byte1; + sp++; + } else { // Codeset 1 G1 + if ( sl - sp < 2) + return CoderResult.UNDERFLOW; + int byte2 = sa[sp + 1] & 0xff; + char[] cc = toUnicode(byte1, byte2, 0); + if (cc == null) { + if (!isLegalDB(byte1) || !isLegalDB(byte2)) + return CoderResult.malformedForLength(1); + return CoderResult.unmappableForLength(2); + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = cc[0]; + sp += 2; + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int byte1 = src.get() & 0xff; + if (byte1 == SS2) { // Codeset 2 G2 + if ( src.remaining() < 3) + return CoderResult.UNDERFLOW; + int cnsPlane = cnspToIndex[src.get() & 0xff]; + if (cnsPlane < 0) + return CoderResult.malformedForLength(2); + byte1 = src.get() & 0xff; + int byte2 = src.get() & 0xff; + char[] cc = toUnicode(byte1, byte2, cnsPlane); + if (cc == null) { + if (!isLegalDB(byte1) || !isLegalDB(byte2)) + return CoderResult.malformedForLength(4); + return CoderResult.unmappableForLength(4); + } + if (dst.remaining() < cc.length) + return CoderResult.OVERFLOW; + if (cc.length == 1) { + dst.put(cc[0]); + } else { + dst.put(cc[0]); + dst.put(cc[1]); + } + mark += 4; + } else if (byte1 < 0x80) { // ASCII G0 + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put((char) byte1); + mark++; + } else { // Codeset 1 G1 + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + int byte2 = src.get() & 0xff; + char[] cc = toUnicode(byte1, byte2, 0); + if (cc == null) { + if (!isLegalDB(byte1) || !isLegalDB(byte2)) + return CoderResult.malformedForLength(1); + return CoderResult.unmappableForLength(2); + } + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put(cc[0]); + mark +=2; + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + } + + public static class Encoder extends CharsetEncoder { + private byte[] bb = new byte[4]; + + public Encoder(Charset cs) { + super(cs, 4.0f, 4.0f); + } + + public boolean canEncode(char c) { + return (c <= '\u007f' || toEUC(c, bb) != -1); + } + + public boolean canEncode(CharSequence cs) { + int i = 0; + while (i < cs.length()) { + char c = cs.charAt(i++); + if (Character.isHighSurrogate(c)) { + if (i == cs.length()) + return false; + char low = cs.charAt(i++); + if (!Character.isLowSurrogate(low) || toEUC(c, low, bb) == -1) + return false; + } else if (!canEncode(c)) { + return false; + } + } + return true; + } + + public int toEUC(char hi, char low, byte[] bb) { + return encode(hi, low, bb); + } + + public int toEUC(char c, byte[] bb) { + return encode(c, bb); + } + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + int inSize; + int outSize; + + try { + while (sp < sl) { + char c = sa[sp]; + inSize = 1; + if (c < 0x80) { // ASCII + bb[0] = (byte)c; + outSize = 1; + } else { + outSize = toEUC(c, bb); + if (outSize == -1) { + // to check surrogates only after BMP failed + // has the benefit of improving the BMP encoding + // 10% faster, with the price of the slowdown of + // supplementary character encoding. given the use + // of supplementary characters is really rare, this + // is something worth doing. + if (Character.isHighSurrogate(c)) { + if ((sp + 1) == sl) + return CoderResult.UNDERFLOW; + if (!Character.isLowSurrogate(sa[sp + 1])) + return CoderResult.malformedForLength(1); + outSize = toEUC(c, sa[sp+1], bb); + inSize = 2; + } else if (Character.isLowSurrogate(c)) { + return CoderResult.malformedForLength(1); + } + } + } + if (outSize == -1) + return CoderResult.unmappableForLength(inSize); + if ( dl - dp < outSize) + return CoderResult.OVERFLOW; + for (int i = 0; i < outSize; i++) + da[dp++] = bb[i]; + sp += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int outSize; + int inSize; + int mark = src.position(); + + try { + while (src.hasRemaining()) { + inSize = 1; + char c = src.get(); + if (c < 0x80) { // ASCII + outSize = 1; + bb[0] = (byte)c; + } else { + outSize = toEUC(c, bb); + if (outSize == -1) { + if (Character.isHighSurrogate(c)) { + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + char c2 = src.get(); + if (!Character.isLowSurrogate(c2)) + return CoderResult.malformedForLength(1); + outSize = toEUC(c, c2, bb); + inSize = 2; + } else if (Character.isLowSurrogate(c)) { + return CoderResult.malformedForLength(1); + } + } + } + if (outSize == -1) + return CoderResult.unmappableForLength(inSize); + if (dst.remaining() < outSize) + return CoderResult.OVERFLOW; + for (int i = 0; i < outSize; i++) + dst.put(bb[i]); + mark += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + static int encode(char hi, char low, byte[] bb) { + int c = Character.toCodePoint(hi, low); + if ((c & 0xf0000) != 0x20000) + return -1; + c -= 0x20000; + int index = c2bSuppIndex[c >> 8]; + if (index == UNMAPPABLE_ENCODING) + return -1; + index = index + (c & 0xff); + int db = c2bSupp[index]; + if (db == UNMAPPABLE_ENCODING) + return -1; + int p = (c2bPlane[index] >> 4) & 0xf; + bb[0] = (byte)SS2; + bb[1] = (byte)(0xa0 | p); + bb[2] = (byte)(db >> 8); + bb[3] = (byte)db; + return 4; + } + + static int encode(char c, byte[] bb) { + int index = c2bIndex[c >> 8]; + if (index == UNMAPPABLE_ENCODING) + return -1; + index = index + (c & 0xff); + int db = c2b[index]; + if (db == UNMAPPABLE_ENCODING) + return -1; + int p = c2bPlane[index] & 0xf; + if (p == 0) { + bb[0] = (byte)(db >> 8); + bb[1] = (byte)db; + return 2; + } else { + bb[0] = (byte)SS2; + bb[1] = (byte)(0xa0 | p); + bb[2] = (byte)(db >> 8); + bb[3] = (byte)db; + return 4; + } + } + + static final char[] c2b; + static final char[] c2bIndex; + static final char[] c2bSupp; + static final char[] c2bSuppIndex; + static final byte[] c2bPlane; + static { + int b1Min = Decoder.b1Min; + int b1Max = Decoder.b1Max; + int b2Min = Decoder.b2Min; + int b2Max = Decoder.b2Max; + int dbSegSize = Decoder.dbSegSize; + String[] b2c = Decoder.b2c; + byte[] b2cIsSupp = Decoder.b2cIsSupp; + + c2bIndex = EUC_TWMapping.c2bIndex; + c2bSuppIndex = EUC_TWMapping.c2bSuppIndex; + char[] c2b0 = new char[EUC_TWMapping.C2BSIZE]; + char[] c2bSupp0 = new char[EUC_TWMapping.C2BSUPPSIZE]; + byte[] c2bPlane0 = new byte[Math.max(EUC_TWMapping.C2BSIZE, + EUC_TWMapping.C2BSUPPSIZE)]; + + Arrays.fill(c2b0, (char)UNMAPPABLE_ENCODING); + Arrays.fill(c2bSupp0, (char)UNMAPPABLE_ENCODING); + + for (int p = 0; p < b2c.length; p++) { + String db = b2c[p]; + /* + adjust the "plane" from 0..7 to 0, 2, 3, 4, 5, 6, 7, 0xf, + which helps balance between footprint (to save the plane + info in 4 bits) and runtime performance (to require only + one operation "0xa0 | plane" to encode the plane byte) + */ + int plane = p; + if (plane == 7) + plane = 0xf; + else if (plane != 0) + plane = p + 1; + + int off = 0; + for (int b1 = b1Min; b1 <= b1Max; b1++) { + for (int b2 = b2Min; b2 <= b2Max; b2++) { + char c = db.charAt(off); + if (c != UNMAPPABLE_DECODING) { + if ((b2cIsSupp[off] & (1 << p)) != 0) { + int index = c2bSuppIndex[c >> 8] + (c&0xff); + c2bSupp0[index] = (char)((b1 << 8) + b2); + c2bPlane0[index] |= (byte)(plane << 4); + } else { + int index = c2bIndex[c >> 8] + (c&0xff); + c2b0[index] = (char)((b1 << 8) + b2); + c2bPlane0[index] |= (byte)plane; + } + } + off++; + } + } + } + c2b = c2b0; + c2bSupp = c2bSupp0; + c2bPlane = c2bPlane0; + } + } +} diff --git a/src/sun/nio/cs/ext/ExtendedCharsets.java b/src/sun/nio/cs/ext/ExtendedCharsets.java new file mode 100644 index 00000000..29bbc310 --- /dev/null +++ b/src/sun/nio/cs/ext/ExtendedCharsets.java @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.security.AccessController; + +import sun.nio.cs.AbstractCharsetProvider; +import sun.security.action.GetPropertyAction; + + +/** + * Provider for extended charsets. + */ + +public class ExtendedCharsets + extends AbstractCharsetProvider +{ + + static volatile ExtendedCharsets instance = null; + + public ExtendedCharsets() { + + super("sun.nio.cs.ext"); // identify provider pkg name. + + // Traditional Chinese + + charset("Big5", "Big5", + new String[] { + // IANA aliases + "csBig5" + }); + + charset("x-MS950-HKSCS-XP", "MS950_HKSCS_XP", + new String[] { + "MS950_HKSCS_XP" // JDK historical; + }); + + charset("x-MS950-HKSCS", "MS950_HKSCS", + new String[] { + // IANA aliases + "MS950_HKSCS" // JDK historical; + }); + + charset("x-windows-950", "MS950", + new String[] { + "ms950", // JDK historical + "windows-950" + }); + + charset("x-windows-874", "MS874", + new String[] { + "ms874", // JDK historical + "ms-874", + "windows-874" }); + + charset("x-EUC-TW", "EUC_TW", + new String[] { + "euc_tw", // JDK historical + "euctw", + "cns11643", + "EUC-TW" + }); + + charset("Big5-HKSCS", "Big5_HKSCS", + new String[] { + "Big5_HKSCS", // JDK historical + "big5hk", + "big5-hkscs", + "big5hkscs" // Linux alias + }); + + charset("x-Big5-HKSCS-2001", "Big5_HKSCS_2001", + new String[] { + "Big5_HKSCS_2001", + "big5hk-2001", + "big5-hkscs-2001", + "big5-hkscs:unicode3.0", + "big5hkscs-2001", + }); + + charset("x-Big5-Solaris", "Big5_Solaris", + new String[] { + "Big5_Solaris", // JDK historical + }); + + // Simplified Chinese + charset("GBK", "GBK", + new String[] { + "windows-936", + "CP936" + }); + + charset("GB18030", "GB18030", + new String[] { + "gb18030-2000" + }); + + charset("GB2312", "EUC_CN", + new String[] { + // IANA aliases + "gb2312", + "gb2312-80", + "gb2312-1980", + "euc-cn", + "euccn", + "x-EUC-CN", // 1.4 compatibility + "EUC_CN" //JDK historical + }); + + charset("x-mswin-936", "MS936", + new String[] { + "ms936", // historical + // IANA aliases + "ms_936" + }); + + // The definition of this charset may be overridden by the init method, + // below, if the sun.nio.cs.map property is defined. + // + charset("Shift_JIS", "SJIS", + new String[] { + // IANA aliases + "sjis", // historical + "shift_jis", + "shift-jis", + "ms_kanji", + "x-sjis", + "csShiftJIS" + }); + + // The definition of this charset may be overridden by the init method, + // below, if the sun.nio.cs.map property is defined. + // + charset("windows-31j", "MS932", + new String[] { + "MS932", // JDK historical + "windows-932", + "csWindows31J" + }); + + charset("JIS_X0201", "JIS_X_0201", + new String[] { + "JIS0201", // JDK historical + // IANA aliases + "JIS_X0201", + "X0201", + "csHalfWidthKatakana" + }); + + charset("x-JIS0208", "JIS_X_0208", + new String[] { + "JIS0208", // JDK historical + // IANA aliases + "JIS_C6226-1983", + "iso-ir-87", + "x0208", + "JIS_X0208-1983", + "csISO87JISX0208" + }); + + charset("JIS_X0212-1990", "JIS_X_0212", + new String[] { + "JIS0212", // JDK historical + // IANA aliases + "jis_x0212-1990", + "x0212", + "iso-ir-159", + "csISO159JISX02121990" + }); + + charset("x-SJIS_0213", "SJIS_0213", + new String[] { + "sjis-0213", + "sjis_0213", + "sjis:2004", + "sjis_0213:2004", + "shift_jis_0213:2004", + "shift_jis:2004" + }); + + charset("x-MS932_0213", "MS932_0213", + new String[] { + "MS932-0213", + "MS932_0213", + "MS932:2004", + "windows-932-0213", + "windows-932:2004" + }); + + charset("EUC-JP", "EUC_JP", + new String[] { + "euc_jp", // JDK historical + // IANA aliases + "eucjis", + "eucjp", + "Extended_UNIX_Code_Packed_Format_for_Japanese", + "csEUCPkdFmtjapanese", + "x-euc-jp", + "x-eucjp" + }); + + charset("x-euc-jp-linux", "EUC_JP_LINUX", + new String[] { + "euc_jp_linux", // JDK historical + "euc-jp-linux" + }); + + charset("x-eucjp-open", "EUC_JP_Open", + new String[] { + "EUC_JP_Solaris", // JDK historical + "eucJP-open" + }); + + charset("x-PCK", "PCK", + new String[] { + // IANA aliases + "pck" // historical + }); + + charset("ISO-2022-JP", "ISO2022_JP", + new String[] { + // IANA aliases + "iso2022jp", // historical + "jis", + "csISO2022JP", + "jis_encoding", + "csjisencoding" + }); + + charset("ISO-2022-JP-2", "ISO2022_JP_2", + new String[] { + // IANA aliases + "csISO2022JP2", + "iso2022jp2" + }); + + charset("x-windows-50221", "MS50221", + new String[] { + "ms50221", // historical + "cp50221", + }); + + charset("x-windows-50220", "MS50220", + new String[] { + "ms50220", // historical + "cp50220", + }); + + charset("x-windows-iso2022jp", "MSISO2022JP", + new String[] { + "windows-iso2022jp", // historical + }); + + charset("x-JISAutoDetect", "JISAutoDetect", + new String[] { + "JISAutoDetect" // historical + }); + + // Korean + charset("EUC-KR", "EUC_KR", + new String[] { + "euc_kr", // JDK historical + // IANA aliases + "ksc5601", + "euckr", + "ks_c_5601-1987", + "ksc5601-1987", + "ksc5601_1987", + "ksc_5601", + "csEUCKR", + "5601" + }); + + charset("x-windows-949", "MS949", + new String[] { + "ms949", // JDK historical + "windows949", + "windows-949", + // IANA aliases + "ms_949" + }); + + charset("x-Johab", "Johab", + new String[] { + "ksc5601-1992", + "ksc5601_1992", + "ms1361", + "johab" // JDK historical + }); + + charset("ISO-2022-KR", "ISO2022_KR", + new String[] { + "ISO2022KR", // JDK historical + "csISO2022KR" + }); + + charset("ISO-2022-CN", "ISO2022_CN", + new String[] { + "ISO2022CN", // JDK historical + "csISO2022CN" + }); + + charset("x-ISO-2022-CN-CNS", "ISO2022_CN_CNS", + new String[] { + "ISO2022CN_CNS", // JDK historical + "ISO-2022-CN-CNS" + }); + + charset("x-ISO-2022-CN-GB", "ISO2022_CN_GB", + new String[] { + "ISO2022CN_GB", // JDK historical + "ISO-2022-CN-GB" + }); + + charset("x-ISCII91", "ISCII91", + new String[] { + "iscii", + "ST_SEV_358-88", + "iso-ir-153", + "csISO153GOST1976874", + "ISCII91" // JDK historical + }); + + charset("ISO-8859-3", "ISO_8859_3", + new String[] { + "iso8859_3", // JDK historical + "8859_3", + "ISO_8859-3:1988", + "iso-ir-109", + "ISO_8859-3", + "ISO8859-3", + "latin3", + "l3", + "ibm913", + "ibm-913", + "cp913", + "913", + "csISOLatin3" + }); + + charset("ISO-8859-6", "ISO_8859_6", + new String[] { + "iso8859_6", // JDK historical + "8859_6", + "iso-ir-127", + "ISO_8859-6", + "ISO_8859-6:1987", + "ISO8859-6", + "ECMA-114", + "ASMO-708", + "arabic", + "ibm1089", + "ibm-1089", + "cp1089", + "1089", + "csISOLatinArabic" + }); + + charset("ISO-8859-8", "ISO_8859_8", + new String[] { + "iso8859_8", // JDK historical + "8859_8", + "iso-ir-138", + "ISO_8859-8", + "ISO_8859-8:1988", + "ISO8859-8", + "cp916", + "916", + "ibm916", + "ibm-916", + "hebrew", + "csISOLatinHebrew" + }); + + charset("x-ISO-8859-11", "ISO_8859_11", + new String[] { + "iso-8859-11", + "iso8859_11" + }); + + charset("TIS-620", "TIS_620", + new String[] { + "tis620", // JDK historical + "tis620.2533" + }); + + // Various Microsoft Windows international codepages + + charset("windows-1255", "MS1255", + new String[] { + "cp1255" // JDK historical + }); + + charset("windows-1256", "MS1256", + new String[] { + "cp1256" // JDK historical + }); + + charset("windows-1258", "MS1258", + new String[] { + "cp1258" // JDK historical + }); + + // IBM & PC/MSDOS encodings + + charset("x-IBM942", "IBM942", + new String[] { + "cp942", // JDK historical + "ibm942", + "ibm-942", + "942" + }); + + charset("x-IBM942C", "IBM942C", + new String[] { + "cp942C", // JDK historical + "ibm942C", + "ibm-942C", + "942C" + }); + + charset("x-IBM943", "IBM943", + new String[] { + "cp943", // JDK historical + "ibm943", + "ibm-943", + "943" + }); + + charset("x-IBM943C", "IBM943C", + new String[] { + "cp943C", // JDK historical + "ibm943C", + "ibm-943C", + "943C" + }); + + charset("x-IBM948", "IBM948", + new String[] { + "cp948", // JDK historical + "ibm948", + "ibm-948", + "948" + }); + + charset("x-IBM950", "IBM950", + new String[] { + "cp950", // JDK historical + "ibm950", + "ibm-950", + "950" + }); + + charset("x-IBM930", "IBM930", + new String[] { + "cp930", // JDK historical + "ibm930", + "ibm-930", + "930" + }); + + charset("x-IBM935", "IBM935", + new String[] { + "cp935", // JDK historical + "ibm935", + "ibm-935", + "935" + }); + + charset("x-IBM937", "IBM937", + new String[] { + "cp937", // JDK historical + "ibm937", + "ibm-937", + "937" + }); + + charset("x-IBM856", "IBM856", + new String[] { + "cp856", // JDK historical + "ibm-856", + "ibm856", + "856" + }); + + charset("IBM860", "IBM860", + new String[] { + "cp860", // JDK historical + "ibm860", + "ibm-860", + "860", + "csIBM860" + }); + charset("IBM861", "IBM861", + new String[] { + "cp861", // JDK historical + "ibm861", + "ibm-861", + "861", + "csIBM861", + "cp-is" + }); + + charset("IBM863", "IBM863", + new String[] { + "cp863", // JDK historical + "ibm863", + "ibm-863", + "863", + "csIBM863" + }); + + charset("IBM864", "IBM864", + new String[] { + "cp864", // JDK historical + "ibm864", + "ibm-864", + "864", + "csIBM864" + }); + + charset("IBM865", "IBM865", + new String[] { + "cp865", // JDK historical + "ibm865", + "ibm-865", + "865", + "csIBM865" + }); + + charset("IBM868", "IBM868", + new String[] { + "cp868", // JDK historical + "ibm868", + "ibm-868", + "868", + "cp-ar", + "csIBM868" + }); + + charset("IBM869", "IBM869", + new String[] { + "cp869", // JDK historical + "ibm869", + "ibm-869", + "869", + "cp-gr", + "csIBM869" + }); + + charset("x-IBM921", "IBM921", + new String[] { + "cp921", // JDK historical + "ibm921", + "ibm-921", + "921" + }); + + charset("x-IBM1006", "IBM1006", + new String[] { + "cp1006", // JDK historical + "ibm1006", + "ibm-1006", + "1006" + }); + + charset("x-IBM1046", "IBM1046", + new String[] { + "cp1046", // JDK historical + "ibm1046", + "ibm-1046", + "1046" + }); + + charset("IBM1047", "IBM1047", + new String[] { + "cp1047", // JDK historical + "ibm-1047", + "1047" + }); + + charset("x-IBM1098", "IBM1098", + new String[] { + "cp1098", // JDK historical + "ibm1098", + "ibm-1098", + "1098", + }); + + charset("IBM037", "IBM037", + new String[] { + "cp037", // JDK historical + "ibm037", + "ebcdic-cp-us", + "ebcdic-cp-ca", + "ebcdic-cp-wt", + "ebcdic-cp-nl", + "csIBM037", + "cs-ebcdic-cp-us", + "cs-ebcdic-cp-ca", + "cs-ebcdic-cp-wt", + "cs-ebcdic-cp-nl", + "ibm-037", + "ibm-37", + "cpibm37", + "037" + }); + + charset("x-IBM1025", "IBM1025", + new String[] { + "cp1025", // JDK historical + "ibm1025", + "ibm-1025", + "1025" + }); + + charset("IBM1026", "IBM1026", + new String[] { + "cp1026", // JDK historical + "ibm1026", + "ibm-1026", + "1026" + }); + + charset("x-IBM1112", "IBM1112", + new String[] { + "cp1112", // JDK historical + "ibm1112", + "ibm-1112", + "1112" + }); + + charset("x-IBM1122", "IBM1122", + new String[] { + "cp1122", // JDK historical + "ibm1122", + "ibm-1122", + "1122" + }); + + charset("x-IBM1123", "IBM1123", + new String[] { + "cp1123", // JDK historical + "ibm1123", + "ibm-1123", + "1123" + }); + + charset("x-IBM1124", "IBM1124", + new String[] { + "cp1124", // JDK historical + "ibm1124", + "ibm-1124", + "1124" + }); + + charset("x-IBM1364", "IBM1364", + new String[] { + "cp1364", + "ibm1364", + "ibm-1364", + "1364" + }); + + charset("IBM273", "IBM273", + new String[] { + "cp273", // JDK historical + "ibm273", + "ibm-273", + "273" + }); + + charset("IBM277", "IBM277", + new String[] { + "cp277", // JDK historical + "ibm277", + "ibm-277", + "277" + }); + + charset("IBM278", "IBM278", + new String[] { + "cp278", // JDK historical + "ibm278", + "ibm-278", + "278", + "ebcdic-sv", + "ebcdic-cp-se", + "csIBM278" + }); + + charset("IBM280", "IBM280", + new String[] { + "cp280", // JDK historical + "ibm280", + "ibm-280", + "280" + }); + + charset("IBM284", "IBM284", + new String[] { + "cp284", // JDK historical + "ibm284", + "ibm-284", + "284", + "csIBM284", + "cpibm284" + }); + + charset("IBM285", "IBM285", + new String[] { + "cp285", // JDK historical + "ibm285", + "ibm-285", + "285", + "ebcdic-cp-gb", + "ebcdic-gb", + "csIBM285", + "cpibm285" + }); + + charset("IBM297", "IBM297", + new String[] { + "cp297", // JDK historical + "ibm297", + "ibm-297", + "297", + "ebcdic-cp-fr", + "cpibm297", + "csIBM297", + }); + + charset("IBM420", "IBM420", + new String[] { + "cp420", // JDK historical + "ibm420", + "ibm-420", + "ebcdic-cp-ar1", + "420", + "csIBM420" + }); + + charset("IBM424", "IBM424", + new String[] { + "cp424", // JDK historical + "ibm424", + "ibm-424", + "424", + "ebcdic-cp-he", + "csIBM424" + }); + + charset("IBM500", "IBM500", + new String[] { + "cp500", // JDK historical + "ibm500", + "ibm-500", + "500", + "ebcdic-cp-ch", + "ebcdic-cp-bh", + "csIBM500" + }); + + charset("x-IBM833", "IBM833", + new String[] { + "cp833", + "ibm833", + "ibm-833" + }); + + //EBCDIC DBCS-only Korean + charset("x-IBM834", "IBM834", + new String[] { + "cp834", + "ibm834", + "834", + "ibm-834" + }); + + + charset("IBM-Thai", "IBM838", + new String[] { + "cp838", // JDK historical + "ibm838", + "ibm-838", + "838" + }); + + charset("IBM870", "IBM870", + new String[] { + "cp870", // JDK historical + "ibm870", + "ibm-870", + "870", + "ebcdic-cp-roece", + "ebcdic-cp-yu", + "csIBM870" + }); + + charset("IBM871", "IBM871", + new String[] { + "cp871", // JDK historical + "ibm871", + "ibm-871", + "871", + "ebcdic-cp-is", + "csIBM871" + }); + + charset("x-IBM875", "IBM875", + new String[] { + "cp875", // JDK historical + "ibm875", + "ibm-875", + "875" + }); + + charset("IBM918", "IBM918", + new String[] { + "cp918", // JDK historical + "ibm-918", + "918", + "ebcdic-cp-ar2" + }); + + charset("x-IBM922", "IBM922", + new String[] { + "cp922", // JDK historical + "ibm922", + "ibm-922", + "922" + }); + + charset("x-IBM1097", "IBM1097", + new String[] { + "cp1097", // JDK historical + "ibm1097", + "ibm-1097", + "1097" + }); + + charset("x-IBM949", "IBM949", + new String[] { + "cp949", // JDK historical + "ibm949", + "ibm-949", + "949" + }); + + charset("x-IBM949C", "IBM949C", + new String[] { + "cp949C", // JDK historical + "ibm949C", + "ibm-949C", + "949C" + }); + + charset("x-IBM939", "IBM939", + new String[] { + "cp939", // JDK historical + "ibm939", + "ibm-939", + "939" + }); + + charset("x-IBM933", "IBM933", + new String[] { + "cp933", // JDK historical + "ibm933", + "ibm-933", + "933" + }); + + charset("x-IBM1381", "IBM1381", + new String[] { + "cp1381", // JDK historical + "ibm1381", + "ibm-1381", + "1381" + }); + + charset("x-IBM1383", "IBM1383", + new String[] { + "cp1383", // JDK historical + "ibm1383", + "ibm-1383", + "1383" + }); + + charset("x-IBM970", "IBM970", + new String[] { + "cp970", // JDK historical + "ibm970", + "ibm-970", + "ibm-eucKR", + "970" + }); + + charset("x-IBM964", "IBM964", + new String[] { + "cp964", // JDK historical + "ibm964", + "ibm-964", + "964" + }); + + charset("x-IBM33722", "IBM33722", + new String[] { + "cp33722", // JDK historical + "ibm33722", + "ibm-33722", + "ibm-5050", // from IBM alias list + "ibm-33722_vascii_vpua", // from IBM alias list + "33722" + }); + + charset("IBM01140", "IBM1140", + new String[] { + "cp1140", // JDK historical + "ccsid01140", + "cp01140", + "1140", + "ebcdic-us-037+euro" + }); + + charset("IBM01141", "IBM1141", + new String[] { + "cp1141", // JDK historical + "ccsid01141", + "cp01141", + "1141", + "ebcdic-de-273+euro" + }); + + charset("IBM01142", "IBM1142", + new String[] { + "cp1142", // JDK historical + "ccsid01142", + "cp01142", + "1142", + "ebcdic-no-277+euro", + "ebcdic-dk-277+euro" + }); + + charset("IBM01143", "IBM1143", + new String[] { + "cp1143", // JDK historical + "ccsid01143", + "cp01143", + "1143", + "ebcdic-fi-278+euro", + "ebcdic-se-278+euro" + }); + + charset("IBM01144", "IBM1144", + new String[] { + "cp1144", // JDK historical + "ccsid01144", + "cp01144", + "1144", + "ebcdic-it-280+euro" + }); + + charset("IBM01145", "IBM1145", + new String[] { + "cp1145", // JDK historical + "ccsid01145", + "cp01145", + "1145", + "ebcdic-es-284+euro" + }); + + charset("IBM01146", "IBM1146", + new String[] { + "cp1146", // JDK historical + "ccsid01146", + "cp01146", + "1146", + "ebcdic-gb-285+euro" + }); + + charset("IBM01147", "IBM1147", + new String[] { + "cp1147", // JDK historical + "ccsid01147", + "cp01147", + "1147", + "ebcdic-fr-277+euro" + }); + + charset("IBM01148", "IBM1148", + new String[] { + "cp1148", // JDK historical + "ccsid01148", + "cp01148", + "1148", + "ebcdic-international-500+euro" + }); + + charset("IBM01149", "IBM1149", + new String[] { + "cp1149", // JDK historical + "ccsid01149", + "cp01149", + "1149", + "ebcdic-s-871+euro" + }); + + charset("IBM290", "IBM290", + new String[] { + "cp290", + "ibm290", + "ibm-290", + "csIBM290", + "EBCDIC-JP-kana", + "290" + }); + + charset("x-IBM300", "IBM300", + new String[] { + "cp300", + "ibm300", + "ibm-300", + "300" + }); + + // Macintosh MacOS/Apple char encodingd + + + charset("x-MacRoman", "MacRoman", + new String[] { + "MacRoman" // JDK historical + }); + + charset("x-MacCentralEurope", "MacCentralEurope", + new String[] { + "MacCentralEurope" // JDK historical + }); + + charset("x-MacCroatian", "MacCroatian", + new String[] { + "MacCroatian" // JDK historical + }); + + + charset("x-MacGreek", "MacGreek", + new String[] { + "MacGreek" // JDK historical + }); + + charset("x-MacCyrillic", "MacCyrillic", + new String[] { + "MacCyrillic" // JDK historical + }); + + charset("x-MacUkraine", "MacUkraine", + new String[] { + "MacUkraine" // JDK historical + }); + + charset("x-MacTurkish", "MacTurkish", + new String[] { + "MacTurkish" // JDK historical + }); + + charset("x-MacArabic", "MacArabic", + new String[] { + "MacArabic" // JDK historical + }); + + charset("x-MacHebrew", "MacHebrew", + new String[] { + "MacHebrew" // JDK historical + }); + + charset("x-MacIceland", "MacIceland", + new String[] { + "MacIceland" // JDK historical + }); + + charset("x-MacRomania", "MacRomania", + new String[] { + "MacRomania" // JDK historical + }); + + charset("x-MacThai", "MacThai", + new String[] { + "MacThai" // JDK historical + }); + + charset("x-MacSymbol", "MacSymbol", + new String[] { + "MacSymbol" // JDK historical + }); + + charset("x-MacDingbat", "MacDingbat", + new String[] { + "MacDingbat" // JDK historical + }); + + instance = this; + + } + + private boolean initialized = false; + + // If the sun.nio.cs.map property is defined on the command line we won't + // see it in the system-properties table until after the charset subsystem + // has been initialized. We therefore delay the effect of this property + // until after the JRE has completely booted. + // + // At the moment following values for this property are supported, property + // value string is case insensitive. + // + // (1)"Windows-31J/Shift_JIS" + // In 1.4.1 we added a correct implementation of the Shift_JIS charset + // but in previous releases this charset name had been treated as an alias + // for Windows-31J, aka MS932. Users who have existing code that depends + // upon this alias can restore the previous behavior by defining this + // property to have this value. + // + // (2)"x-windows-50221/ISO-2022-JP" + // "x-windows-50220/ISO-2022-JP" + // "x-windows-iso2022jp/ISO-2022-JP" + // The charset ISO-2022-JP is a "standard based" implementation by default, + // which supports ASCII, JIS_X_0201 and JIS_X_0208 mappings based encoding + // and decoding only. + // There are three Microsoft iso-2022-jp variants, namely x-windows-50220, + // x-windows-50221 and x-windows-iso2022jp which behaves "slightly" differently + // compared to the "standard based" implementation. See ISO2022_JP.java for + // detailed description. Users who prefer the behavior of MS iso-2022-jp + // variants should use these names explicitly instead of using "ISO-2022-JP" + // and its aliases. However for those who need the ISO-2022-JP charset behaves + // exactly the same as MS variants do, above properties can be defined to + // switch. + // + // If we need to define other charset-alias mappings in the future then + // this property could be further extended, the general idea being that its + // value should be of the form + // + // new-charset-1/old-charset-1,new-charset-2/old-charset-2,... + // + // where each charset named to the left of a slash is intended to replace + // (most) uses of the charset named to the right of the slash. + // + protected void init() { + if (initialized) + return; + if (!sun.misc.VM.isBooted()) + return; + + String map = AccessController.doPrivileged( + new GetPropertyAction("sun.nio.cs.map")); + boolean sjisIsMS932 = false; + boolean iso2022jpIsMS50221 = false; + boolean iso2022jpIsMS50220 = false; + boolean iso2022jpIsMSISO2022JP = false; + if (map != null) { + String[] maps = map.split(","); + for (int i = 0; i < maps.length; i++) { + if (maps[i].equalsIgnoreCase("Windows-31J/Shift_JIS")) { + sjisIsMS932 = true; + } else if (maps[i].equalsIgnoreCase("x-windows-50221/ISO-2022-JP")) { + iso2022jpIsMS50221 = true; + } else if (maps[i].equalsIgnoreCase("x-windows-50220/ISO-2022-JP")) { + iso2022jpIsMS50220 = true; + } else if (maps[i].equalsIgnoreCase("x-windows-iso2022jp/ISO-2022-JP")) { + iso2022jpIsMSISO2022JP = true; + } + } + } + if (sjisIsMS932) { + deleteCharset("Shift_JIS", + new String[] { + // IANA aliases + "sjis", // historical + "shift_jis", + "shift-jis", + "ms_kanji", + "x-sjis", + "csShiftJIS" + }); + deleteCharset("windows-31j", + new String[] { + "MS932", // JDK historical + "windows-932", + "csWindows31J" + }); + charset("Shift_JIS", "SJIS", + new String[] { + // IANA aliases + "sjis" // JDK historical + }); + charset("windows-31j", "MS932", + new String[] { + "MS932", // JDK historical + "windows-932", + "csWindows31J", + "shift-jis", + "ms_kanji", + "x-sjis", + "csShiftJIS", + // This alias takes precedence over the actual + // Shift_JIS charset itself since aliases are always + // resolved first, before looking up canonical names. + "shift_jis" + }); + } + if (iso2022jpIsMS50221 || + iso2022jpIsMS50220 || + iso2022jpIsMSISO2022JP) { + deleteCharset("ISO-2022-JP", + new String[] { + "iso2022jp", + "jis", + "csISO2022JP", + "jis_encoding", + "csjisencoding" + }); + if (iso2022jpIsMS50221) { + deleteCharset("x-windows-50221", + new String[] { + "cp50221", + "ms50221" + }); + charset("x-windows-50221", "MS50221", + new String[] { + "cp50221", + "ms50221", + "iso-2022-jp", + "iso2022jp", + "jis", + "csISO2022JP", + "jis_encoding", + "csjisencoding" + }); + } else if (iso2022jpIsMS50220) { + deleteCharset("x-windows-50220", + new String[] { + "cp50220", + "ms50220" + }); + charset("x-windows-50220", "MS50220", + new String[] { + "cp50220", + "ms50220", + "iso-2022-jp", + "iso2022jp", + "jis", + "csISO2022JP", + "jis_encoding", + "csjisencoding" + }); + } else { + deleteCharset("x-windows-iso2022jp", + new String[] { + "windows-iso2022jp" + }); + charset("x-windows-iso2022jp", "MSISO2022JP", + new String[] { + "windows-iso2022jp", + "iso-2022-jp", + "iso2022jp", + "jis", + "csISO2022JP", + "jis_encoding", + "csjisencoding" + }); + + + } + } + String osName = AccessController.doPrivileged( + new GetPropertyAction("os.name")); + if ("SunOS".equals(osName) || "Linux".equals(osName) || "AIX".equals(osName) + || osName.contains("OS X")) { + charset("x-COMPOUND_TEXT", "COMPOUND_TEXT", + new String[] { + "COMPOUND_TEXT", // JDK historical + "x11-compound_text", + "x-compound-text" + }); + } + initialized = true; + } + + public static String[] aliasesFor(String charsetName) { + if (instance == null) + return null; + return instance.aliases(charsetName); + } +} diff --git a/src/sun/nio/cs/ext/GB18030.java b/src/sun/nio/cs/ext/GB18030.java new file mode 100644 index 00000000..87d4ef75 --- /dev/null +++ b/src/sun/nio/cs/ext/GB18030.java @@ -0,0 +1,12779 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.Surrogate; + +public class GB18030 + extends Charset +{ + private static final int GB18030_SINGLE_BYTE = 1; + private static final int GB18030_DOUBLE_BYTE = 2; + private static final int GB18030_FOUR_BYTE = 3; + + public GB18030() { + super("GB18030", ExtendedCharsets.aliasesFor("GB18030")); + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs.name().equals("GBK")) + || (cs.name().equals("ISO-8859-1")) + || (cs.name().equals("ISO-8859-2")) + || (cs.name().equals("ISO-8859-3")) + || (cs.name().equals("ISO-8859-4")) + || (cs.name().equals("ISO-8859-5")) + || (cs.name().equals("ISO-8859-6")) + || (cs.name().equals("ISO-8859-7")) + || (cs.name().equals("ISO-8859-8")) + || (cs.name().equals("ISO-8859-9")) + || (cs.name().equals("ISO-8859-13")) + || (cs.name().equals("UTF-8")) + || (cs.name().equals("UTF-16")) + || (cs.name().equals("UTF-16LE")) + || (cs.name().equals("UTF-16BE")) + || (cs.name().equals("ISO-8859-15")) + || (cs.name().equals("windows-1251")) + || (cs.name().equals("windows-1252")) + || (cs.name().equals("windows-1253")) + || (cs.name().equals("windows-1254")) + || (cs.name().equals("windows-1255")) + || (cs.name().equals("windows-1256")) + || (cs.name().equals("windows-1257")) + || (cs.name().equals("windows-1258")) + || (cs.name().equals("windows-932")) + || (cs.name().equals("x-mswin-936")) + || (cs.name().equals("x-windows-949")) + || (cs.name().equals("x-windows-950")) + || (cs.name().equals("windows-31j")) + || (cs.name().equals("JIS_X0201")) + || (cs.name().equals("JIS_X0208-1990")) + || (cs.name().equals("JIS_X0212")) + || (cs.name().equals("Shift_JIS")) + || (cs.name().equals("GB2312")) + || (cs.name().equals("EUC-KR")) + || (cs.name().equals("x-EUC-TW")) + || (cs.name().equals("EUC-JP")) + || (cs.name().equals("euc-jp-linux")) + || (cs.name().equals("KOI8-R")) + || (cs.name().equals("TIS-620")) + || (cs.name().equals("x-ISCII91")) + || (cs.name().equals("Big5")) + || (cs.name().equals("Big5-HKSCS")) + || (cs.name().equals("x-MS950-HKSCS")) + || (cs.name().equals("ISO-2022-JP")) + || (cs.name().equals("ISO-2022-KR")) + || (cs.name().equals("x-ISO-2022-CN-CNS")) + || (cs.name().equals("x-ISO-2022-CN-GB")) + || (cs.name().equals("x-Johab")) + || (cs instanceof GB18030)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private final static String innerDecoderIndex0= + "\u0080"+ + "\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088"+ + "\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090"+ + "\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098"+ + "\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0"+ + "\u00A1\u00A2\u00A3\u00A5\u00A6\u00A9\u00AA\u00AB"+ + "\u00AC\u00AD\u00AE\u00AF\u00B2\u00B3\u00B4\u00B5"+ + "\u00B6\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE"+ + "\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6"+ + "\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE"+ + "\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6"+ + "\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF"+ + "\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00EB\u00EE"+ + "\u00EF\u00F0\u00F1\u00F4\u00F5\u00F6\u00F8\u00FB"+ + "\u00FD\u00FE\u00FF\u0100\u0102\u0103\u0104\u0105"+ + "\u0106\u0107\u0108\u0109\u010A\u010B\u010C\u010D"+ + "\u010E\u010F\u0110\u0111\u0112\u0114\u0115\u0116"+ + "\u0117\u0118\u0119\u011A\u011C\u011D\u011E\u011F"+ + "\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127"+ + "\u0128\u0129\u012A\u012C\u012D\u012E\u012F\u0130"+ + "\u0131\u0132\u0133\u0134\u0135\u0136\u0137\u0138"+ + "\u0139\u013A\u013B\u013C\u013D\u013E\u013F\u0140"+ + "\u0141\u0142\u0143\u0145\u0146\u0147\u0149\u014A"+ + "\u014B\u014C\u014E\u014F\u0150\u0151\u0152\u0153"+ + "\u0154\u0155\u0156\u0157\u0158\u0159\u015A\u015B"+ + "\u015C\u015D\u015E\u015F\u0160\u0161\u0162\u0163"+ + "\u0164\u0165\u0166\u0167\u0168\u0169\u016A\u016C"+ + "\u016D\u016E\u016F\u0170\u0171\u0172\u0173\u0174"+ + "\u0175\u0176\u0177\u0178\u0179\u017A\u017B\u017C"+ + "\u017D\u017E\u017F\u0180\u0181\u0182\u0183\u0184"+ + "\u0185\u0186\u0187\u0188\u0189\u018A\u018B\u018C"+ + "\u018D\u018E\u018F\u0190\u0191\u0192\u0193\u0194"+ + "\u0195\u0196\u0197\u0198\u0199\u019A\u019B"+ + "\u019C\u019D\u019E\u019F\u01A0\u01A1\u01A2\u01A3"+ + "\u01A4\u01A5\u01A6\u01A7\u01A8\u01A9\u01AA\u01AB"+ + "\u01AC\u01AD\u01AE\u01AF\u01B0\u01B1\u01B2\u01B3"+ + "\u01B4\u01B5\u01B6\u01B7\u01B8\u01B9\u01BA\u01BB"+ + "\u01BC\u01BD\u01BE\u01BF\u01C0\u01C1\u01C2\u01C3"+ + "\u01C4\u01C5\u01C6\u01C7\u01C8\u01C9\u01CA\u01CB"+ + "\u01CC\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9"+ + "\u01DB\u01DD\u01DE\u01DF\u01E0\u01E1\u01E2\u01E3"+ + "\u01E4\u01E5\u01E6\u01E7\u01E8\u01E9\u01EA\u01EB"+ + "\u01EC\u01ED\u01EE\u01EF\u01F0\u01F1\u01F2\u01F3"+ + "\u01F4\u01F5\u01F6\u01F7\u01F8\u01FA\u01FB\u01FC"+ + "\u01FD\u01FE\u01FF\u0200\u0201\u0202\u0203\u0204"+ + "\u0205\u0206\u0207\u0208\u0209\u020A\u020B\u020C"+ + "\u020D\u020E\u020F\u0210\u0211\u0212\u0213\u0214"+ + "\u0215\u0216\u0217\u0218\u0219\u021A\u021B\u021C"+ + "\u021D\u021E\u021F\u0220\u0221\u0222\u0223\u0224"+ + "\u0225\u0226\u0227\u0228\u0229\u022A\u022B\u022C"+ + "\u022D\u022E\u022F\u0230\u0231\u0232\u0233\u0234"+ + "\u0235\u0236\u0237\u0238\u0239\u023A\u023B\u023C"+ + "\u023D\u023E\u023F\u0240\u0241\u0242\u0243\u0244"+ + "\u0245\u0246\u0247\u0248\u0249\u024A\u024B\u024C"+ + "\u024D\u024E\u024F\u0250\u0252\u0253\u0254\u0255"+ + "\u0256\u0257\u0258\u0259\u025A\u025B\u025C\u025D"+ + "\u025E\u025F\u0260\u0262\u0263\u0264\u0265\u0266"+ + "\u0267\u0268\u0269\u026A\u026B\u026C\u026D\u026E"+ + "\u026F\u0270\u0271\u0272\u0273\u0274\u0275\u0276"+ + "\u0277\u0278\u0279\u027A\u027B\u027C\u027D\u027E"+ + "\u027F\u0280\u0281\u0282\u0283\u0284\u0285\u0286"+ + "\u0287\u0288\u0289\u028A\u028B\u028C\u028D\u028E"+ + "\u028F\u0290\u0291\u0292\u0293\u0294\u0295\u0296"+ + "\u0297\u0298\u0299\u029A\u029B\u029C\u029D\u029E"+ + "\u029F\u02A0\u02A1\u02A2\u02A3\u02A4\u02A5\u02A6"+ + "\u02A7\u02A8\u02A9\u02AA\u02AB\u02AC\u02AD\u02AE"+ + "\u02AF\u02B0\u02B1\u02B2\u02B3\u02B4\u02B5\u02B6"+ + "\u02B7\u02B8\u02B9\u02BA\u02BB\u02BC\u02BD\u02BE"+ + "\u02BF\u02C0\u02C1\u02C2\u02C3\u02C4\u02C5\u02C6"+ + "\u02C8\u02CC\u02CD\u02CE\u02CF\u02D0\u02D1\u02D2"+ + "\u02D3\u02D4\u02D5\u02D6\u02D7\u02D8\u02DA\u02DB"+ + "\u02DC\u02DD\u02DE\u02DF\u02E0\u02E1\u02E2\u02E3"+ + "\u02E4\u02E5\u02E6\u02E7\u02E8\u02E9\u02EA\u02EB"+ + "\u02EC\u02ED\u02EE\u02EF\u02F0\u02F1\u02F2\u02F3"+ + "\u02F4\u02F5\u02F6\u02F7\u02F8\u02F9\u02FA\u02FB"+ + "\u02FC\u02FD\u02FE\u02FF\u0300\u0301\u0302\u0303"+ + "\u0304\u0305\u0306\u0307\u0308\u0309\u030A\u030B"+ + "\u030C\u030D\u030E\u030F\u0310\u0311\u0312\u0313"+ + "\u0314\u0315\u0316\u0317\u0318\u0319\u031A\u031B"+ + "\u031C\u031D\u031E\u031F\u0320\u0321\u0322\u0323"+ + "\u0324\u0325\u0326\u0327\u0328\u0329\u032A\u032B"+ + "\u032C\u032D\u032E\u032F\u0330\u0331\u0332\u0333"+ + "\u0334\u0335\u0336\u0337\u0338\u0339\u033A\u033B"+ + "\u033C\u033D\u033E\u033F\u0340\u0341\u0342\u0343"+ + "\u0344\u0345\u0346\u0347\u0348\u0349\u034A\u034B"+ + "\u034C\u034D\u034E\u034F\u0350\u0351\u0352\u0353"+ + "\u0354\u0355\u0356\u0357\u0358\u0359\u035A\u035B"+ + "\u035C\u035D\u035E\u035F\u0360\u0361\u0362\u0363"+ + "\u0364\u0365\u0366\u0367\u0368\u0369\u036A\u036B"+ + "\u036C\u036D\u036E\u036F\u0370\u0371\u0372\u0373"+ + "\u0374\u0375\u0376\u0377\u0378\u0379\u037A\u037B"+ + "\u037C\u037D\u037E\u037F\u0380\u0381\u0382\u0383"+ + "\u0384\u0385\u0386\u0387\u0388\u0389\u038A\u038B"+ + "\u038C\u038D\u038E\u038F\u0390\u03A2\u03AA\u03AB"+ + "\u03AC\u03AD\u03AE\u03AF\u03B0\u03C2\u03CA\u03CB"+ + "\u03CC\u03CD\u03CE\u03CF\u03D0\u03D1\u03D2\u03D3"+ + "\u03D4\u03D5\u03D6\u03D7\u03D8\u03D9\u03DA\u03DB"+ + "\u03DC\u03DD\u03DE\u03DF\u03E0\u03E1\u03E2\u03E3"+ + "\u03E4\u03E5\u03E6\u03E7\u03E8\u03E9\u03EA\u03EB"+ + "\u03EC\u03ED\u03EE\u03EF\u03F0\u03F1\u03F2\u03F3"+ + "\u03F4\u03F5\u03F6\u03F7\u03F8\u03F9\u03FA\u03FB"+ + "\u03FC\u03FD\u03FE\u03FF\u0400\u0402\u0403\u0404"+ + "\u0405\u0406\u0407\u0408\u0409\u040A\u040B\u040C"+ + "\u040D\u040E\u040F\u0450\u0452\u0453\u0454\u0455"+ + "\u0456\u0457\u0458\u0459\u045A\u045B\u045C\u045D"+ + "\u045E\u045F\u0460\u0461\u0462\u0463\u0464\u0465"+ + "\u0466\u0467\u0468\u0469\u046A\u046B\u046C\u046D"+ + "\u046E\u046F\u0470\u0471\u0472\u0473\u0474\u0475"+ + "\u0476\u0477\u0478\u0479\u047A\u047B\u047C\u047D"+ + "\u047E\u047F\u0480\u0481\u0482\u0483\u0484\u0485"+ + "\u0486\u0487\u0488\u0489\u048A\u048B\u048C\u048D"+ + "\u048E\u048F\u0490\u0491\u0492\u0493\u0494\u0495"+ + "\u0496\u0497\u0498\u0499\u049A\u049B\u049C\u049D"+ + "\u049E\u049F\u04A0\u04A1\u04A2\u04A3\u04A4\u04A5"+ + "\u04A6\u04A7\u04A8\u04A9\u04AA\u04AB\u04AC\u04AD"+ + "\u04AE\u04AF\u04B0\u04B1\u04B2\u04B3\u04B4\u04B5"+ + "\u04B6\u04B7\u04B8\u04B9\u04BA\u04BB\u04BC\u04BD"+ + "\u04BE\u04BF\u04C0\u04C1\u04C2\u04C3\u04C4\u04C5"+ + "\u04C6\u04C7\u04C8\u04C9\u04CA\u04CB\u04CC\u04CD"+ + "\u04CE\u04CF\u04D0\u04D1\u04D2\u04D3\u04D4\u04D5"+ + "\u04D6\u04D7\u04D8\u04D9\u04DA\u04DB\u04DC\u04DD"+ + "\u04DE\u04DF\u04E0\u04E1\u04E2\u04E3\u04E4\u04E5"+ + "\u04E6\u04E7\u04E8\u04E9\u04EA\u04EB\u04EC\u04ED"+ + "\u04EE\u04EF\u04F0\u04F1\u04F2\u04F3\u04F4\u04F5"+ + "\u04F6\u04F7\u04F8\u04F9\u04FA\u04FB\u04FC\u04FD"+ + "\u04FE\u04FF\u0500\u0501\u0502\u0503\u0504\u0505"+ + "\u0506\u0507\u0508\u0509\u050A\u050B\u050C\u050D"+ + "\u050E\u050F\u0510\u0511\u0512\u0513\u0514\u0515"+ + "\u0516\u0517\u0518\u0519\u051A\u051B\u051C\u051D"+ + "\u051E\u051F\u0520\u0521\u0522\u0523\u0524\u0525"+ + "\u0526\u0527\u0528\u0529\u052A\u052B\u052C\u052D"+ + "\u052E\u052F\u0530\u0531\u0532\u0533\u0534\u0535"+ + "\u0536\u0537\u0538\u0539\u053A\u053B\u053C\u053D"+ + "\u053E\u053F\u0540\u0541\u0542\u0543\u0544\u0545"+ + "\u0546\u0547\u0548\u0549\u054A\u054B\u054C\u054D"+ + "\u054E\u054F\u0550\u0551\u0552\u0553\u0554\u0555"+ + "\u0556\u0557\u0558\u0559\u055A\u055B\u055C\u055D"+ + "\u055E\u055F\u0560\u0561\u0562\u0563\u0564\u0565"+ + "\u0566\u0567\u0568\u0569\u056A\u056B\u056C\u056D"+ + "\u056E\u056F\u0570\u0571\u0572\u0573\u0574\u0575"+ + "\u0576\u0577\u0578\u0579\u057A\u057B\u057C\u057D"+ + "\u057E\u057F\u0580\u0581\u0582\u0583\u0584\u0585"+ + "\u0586\u0587\u0588\u0589\u058A\u058B\u058C\u058D"+ + "\u058E\u058F\u0590\u0591\u0592\u0593\u0594\u0595"+ + "\u0596\u0597\u0598\u0599\u059A\u059B\u059C\u059D"+ + "\u059E\u059F\u05A0\u05A1\u05A2\u05A3\u05A4\u05A5"+ + "\u05A6\u05A7\u05A8\u05A9\u05AA\u05AB\u05AC\u05AD"+ + "\u05AE\u05AF\u05B0\u05B1\u05B2\u05B3\u05B4\u05B5"+ + "\u05B6\u05B7\u05B8\u05B9\u05BA\u05BB\u05BC\u05BD"+ + "\u05BE\u05BF\u05C0\u05C1\u05C2\u05C3\u05C4\u05C5"+ + "\u05C6\u05C7\u05C8\u05C9\u05CA\u05CB\u05CC\u05CD"+ + "\u05CE\u05CF\u05D0\u05D1\u05D2\u05D3\u05D4\u05D5"+ + "\u05D6\u05D7\u05D8\u05D9\u05DA\u05DB\u05DC\u05DD"+ + "\u05DE\u05DF\u05E0\u05E1\u05E2\u05E3\u05E4\u05E5"+ + "\u05E6\u05E7\u05E8\u05E9\u05EA\u05EB\u05EC\u05ED"+ + "\u05EE\u05EF\u05F0\u05F1\u05F2\u05F3\u05F4\u05F5"+ + "\u05F6\u05F7\u05F8\u05F9\u05FA\u05FB\u05FC\u05FD"+ + "\u05FE\u05FF\u0600\u0601\u0602\u0603\u0604\u0605"+ + "\u0606\u0607\u0608\u0609\u060A\u060B\u060C\u060D"+ + "\u060E\u060F\u0610\u0611\u0612\u0613\u0614\u0615"+ + "\u0616\u0617\u0618\u0619\u061A\u061B\u061C\u061D"+ + "\u061E\u061F\u0620\u0621\u0622\u0623\u0624\u0625"+ + "\u0626\u0627\u0628\u0629\u062A\u062B\u062C\u062D"+ + "\u062E\u062F\u0630\u0631\u0632\u0633\u0634\u0635"+ + "\u0636\u0637\u0638\u0639\u063A\u063B\u063C\u063D"+ + "\u063E\u063F\u0640\u0641\u0642\u0643\u0644\u0645"+ + "\u0646\u0647\u0648\u0649\u064A\u064B\u064C\u064D"+ + "\u064E\u064F\u0650\u0651\u0652\u0653\u0654\u0655"+ + "\u0656\u0657\u0658\u0659\u065A\u065B\u065C\u065D"+ + "\u065E\u065F\u0660\u0661\u0662\u0663\u0664\u0665"+ + "\u0666\u0667\u0668\u0669\u066A\u066B\u066C\u066D"+ + "\u066E\u066F\u0670\u0671\u0672\u0673\u0674\u0675"+ + "\u0676\u0677\u0678\u0679\u067A\u067B\u067C\u067D"+ + "\u067E\u067F\u0680\u0681\u0682\u0683\u0684\u0685"+ + "\u0686\u0687\u0688\u0689\u068A\u068B\u068C\u068D"+ + "\u068E\u068F\u0690\u0691\u0692\u0693\u0694\u0695"+ + "\u0696\u0697\u0698\u0699\u069A\u069B\u069C\u069D"+ + "\u069E\u069F\u06A0\u06A1\u06A2\u06A3\u06A4\u06A5"+ + "\u06A6\u06A7\u06A8\u06A9\u06AA\u06AB\u06AC\u06AD"+ + "\u06AE\u06AF\u06B0\u06B1\u06B2\u06B3\u06B4\u06B5"+ + "\u06B6\u06B7\u06B8\u06B9\u06BA\u06BB\u06BC\u06BD"+ + "\u06BE\u06BF\u06C0\u06C1\u06C2\u06C3\u06C4\u06C5"+ + "\u06C6\u06C7\u06C8\u06C9\u06CA\u06CB\u06CC\u06CD"+ + "\u06CE\u06CF\u06D0\u06D1\u06D2\u06D3\u06D4\u06D5"+ + "\u06D6\u06D7\u06D8\u06D9\u06DA\u06DB\u06DC\u06DD"+ + "\u06DE\u06DF\u06E0\u06E1\u06E2\u06E3\u06E4\u06E5"+ + "\u06E6\u06E7\u06E8\u06E9\u06EA\u06EB\u06EC\u06ED"+ + "\u06EE\u06EF\u06F0\u06F1\u06F2\u06F3\u06F4\u06F5"+ + "\u06F6\u06F7\u06F8\u06F9\u06FA\u06FB\u06FC\u06FD"+ + "\u06FE\u06FF\u0700\u0701\u0702\u0703\u0704\u0705"+ + "\u0706\u0707\u0708\u0709\u070A\u070B\u070C\u070D"+ + "\u070E\u070F\u0710\u0711\u0712\u0713\u0714\u0715"+ + "\u0716\u0717\u0718\u0719\u071A\u071B\u071C\u071D"+ + "\u071E\u071F\u0720\u0721\u0722\u0723\u0724\u0725"+ + "\u0726\u0727\u0728\u0729\u072A\u072B\u072C\u072D"+ + "\u072E\u072F\u0730\u0731\u0732\u0733\u0734\u0735"+ + "\u0736\u0737\u0738\u0739\u073A\u073B\u073C\u073D"+ + "\u073E\u073F\u0740\u0741\u0742\u0743\u0744\u0745"+ + "\u0746\u0747\u0748\u0749\u074A\u074B\u074C\u074D"+ + "\u074E\u074F\u0750\u0751\u0752\u0753\u0754\u0755"+ + "\u0756\u0757\u0758\u0759\u075A\u075B\u075C\u075D"+ + "\u075E\u075F\u0760\u0761\u0762\u0763\u0764\u0765"+ + "\u0766\u0767\u0768\u0769\u076A\u076B\u076C\u076D"+ + "\u076E\u076F\u0770\u0771\u0772\u0773\u0774\u0775"+ + "\u0776\u0777\u0778\u0779\u077A\u077B\u077C\u077D"+ + "\u077E\u077F\u0780\u0781\u0782\u0783\u0784\u0785"+ + "\u0786\u0787\u0788\u0789\u078A\u078B\u078C\u078D"+ + "\u078E\u078F\u0790\u0791\u0792\u0793\u0794\u0795"+ + "\u0796\u0797\u0798\u0799\u079A\u079B\u079C\u079D"+ + "\u079E\u079F\u07A0\u07A1\u07A2\u07A3\u07A4\u07A5"+ + "\u07A6\u07A7\u07A8\u07A9\u07AA\u07AB\u07AC\u07AD"+ + "\u07AE\u07AF\u07B0\u07B1\u07B2\u07B3\u07B4\u07B5"+ + "\u07B6\u07B7\u07B8\u07B9\u07BA\u07BB\u07BC\u07BD"+ + "\u07BE\u07BF\u07C0\u07C1\u07C2\u07C3\u07C4\u07C5"+ + "\u07C6\u07C7\u07C8\u07C9\u07CA\u07CB\u07CC\u07CD"+ + "\u07CE\u07CF\u07D0\u07D1\u07D2\u07D3\u07D4\u07D5"+ + "\u07D6\u07D7\u07D8\u07D9\u07DA\u07DB\u07DC\u07DD"+ + "\u07DE\u07DF\u07E0\u07E1\u07E2\u07E3\u07E4\u07E5"+ + "\u07E6\u07E7\u07E8\u07E9\u07EA\u07EB\u07EC\u07ED"+ + "\u07EE\u07EF\u07F0\u07F1\u07F2\u07F3\u07F4\u07F5"+ + "\u07F6\u07F7\u07F8\u07F9\u07FA\u07FB\u07FC\u07FD"+ + "\u07FE\u07FF\u0800\u0801\u0802\u0803\u0804\u0805"+ + "\u0806\u0807\u0808\u0809\u080A\u080B\u080C\u080D"+ + "\u080E\u080F\u0810\u0811\u0812\u0813\u0814\u0815"+ + "\u0816\u0817\u0818\u0819\u081A\u081B\u081C\u081D"+ + "\u081E\u081F\u0820\u0821\u0822\u0823\u0824\u0825"+ + "\u0826\u0827\u0828\u0829\u082A\u082B\u082C\u082D"+ + "\u082E\u082F\u0830\u0831\u0832\u0833\u0834\u0835"+ + "\u0836\u0837\u0838\u0839\u083A\u083B\u083C\u083D"+ + "\u083E\u083F\u0840\u0841\u0842\u0843\u0844\u0845"+ + "\u0846\u0847\u0848\u0849\u084A\u084B\u084C\u084D"+ + "\u084E\u084F\u0850\u0851\u0852\u0853\u0854\u0855"+ + "\u0856\u0857\u0858\u0859\u085A\u085B\u085C\u085D"+ + "\u085E\u085F\u0860\u0861\u0862\u0863\u0864\u0865"+ + "\u0866\u0867\u0868\u0869\u086A\u086B\u086C\u086D"+ + "\u086E\u086F\u0870\u0871\u0872\u0873\u0874\u0875"+ + "\u0876\u0877\u0878\u0879\u087A\u087B\u087C\u087D"+ + "\u087E\u087F\u0880\u0881\u0882\u0883\u0884\u0885"+ + "\u0886\u0887\u0888\u0889\u088A\u088B\u088C\u088D"+ + "\u088E\u088F\u0890\u0891\u0892\u0893\u0894\u0895"+ + "\u0896\u0897\u0898\u0899\u089A\u089B\u089C\u089D"+ + "\u089E\u089F\u08A0\u08A1\u08A2\u08A3\u08A4\u08A5"+ + "\u08A6\u08A7\u08A8\u08A9\u08AA\u08AB\u08AC\u08AD"+ + "\u08AE\u08AF\u08B0\u08B1\u08B2\u08B3\u08B4\u08B5"+ + "\u08B6\u08B7\u08B8\u08B9\u08BA\u08BB\u08BC\u08BD"+ + "\u08BE\u08BF\u08C0\u08C1\u08C2\u08C3\u08C4\u08C5"+ + "\u08C6\u08C7\u08C8\u08C9\u08CA\u08CB\u08CC\u08CD"+ + "\u08CE\u08CF\u08D0\u08D1\u08D2\u08D3\u08D4\u08D5"+ + "\u08D6\u08D7\u08D8\u08D9\u08DA\u08DB\u08DC\u08DD"+ + "\u08DE\u08DF\u08E0\u08E1\u08E2\u08E3\u08E4\u08E5"+ + "\u08E6\u08E7\u08E8\u08E9\u08EA\u08EB\u08EC\u08ED"+ + "\u08EE\u08EF\u08F0\u08F1\u08F2\u08F3\u08F4\u08F5"+ + "\u08F6\u08F7\u08F8\u08F9\u08FA\u08FB\u08FC\u08FD"+ + "\u08FE\u08FF\u0900\u0901\u0902\u0903\u0904\u0905"+ + "\u0906\u0907\u0908\u0909\u090A\u090B\u090C\u090D"+ + "\u090E\u090F\u0910\u0911\u0912\u0913\u0914\u0915"+ + "\u0916\u0917\u0918\u0919\u091A\u091B\u091C\u091D"+ + "\u091E\u091F\u0920\u0921\u0922\u0923\u0924\u0925"+ + "\u0926\u0927\u0928\u0929\u092A\u092B\u092C\u092D"+ + "\u092E\u092F\u0930\u0931\u0932\u0933\u0934\u0935"+ + "\u0936\u0937\u0938\u0939\u093A\u093B\u093C\u093D"+ + "\u093E\u093F\u0940\u0941\u0942\u0943\u0944\u0945"+ + "\u0946\u0947\u0948\u0949\u094A\u094B\u094C\u094D"+ + "\u094E\u094F\u0950\u0951\u0952\u0953\u0954\u0955"+ + "\u0956\u0957\u0958\u0959\u095A\u095B\u095C\u095D"+ + "\u095E\u095F\u0960\u0961\u0962\u0963\u0964\u0965"+ + "\u0966\u0967\u0968\u0969\u096A\u096B\u096C\u096D"+ + "\u096E\u096F\u0970\u0971\u0972\u0973\u0974\u0975"+ + "\u0976\u0977\u0978\u0979\u097A\u097B\u097C\u097D"+ + "\u097E\u097F\u0980\u0981\u0982\u0983\u0984\u0985"+ + "\u0986\u0987\u0988\u0989\u098A\u098B\u098C\u098D"+ + "\u098E\u098F\u0990\u0991\u0992\u0993\u0994\u0995"+ + "\u0996\u0997\u0998\u0999\u099A\u099B\u099C\u099D"+ + "\u099E\u099F\u09A0\u09A1\u09A2\u09A3\u09A4\u09A5"+ + "\u09A6\u09A7\u09A8\u09A9\u09AA\u09AB\u09AC\u09AD"+ + "\u09AE\u09AF\u09B0\u09B1\u09B2\u09B3\u09B4\u09B5"+ + "\u09B6\u09B7\u09B8\u09B9\u09BA\u09BB\u09BC\u09BD"+ + "\u09BE\u09BF\u09C0\u09C1\u09C2\u09C3\u09C4\u09C5"+ + "\u09C6\u09C7\u09C8\u09C9\u09CA\u09CB\u09CC\u09CD"+ + "\u09CE\u09CF\u09D0\u09D1\u09D2\u09D3\u09D4\u09D5"+ + "\u09D6\u09D7\u09D8\u09D9\u09DA\u09DB\u09DC\u09DD"+ + "\u09DE\u09DF\u09E0\u09E1\u09E2\u09E3\u09E4\u09E5"+ + "\u09E6\u09E7\u09E8\u09E9\u09EA\u09EB\u09EC\u09ED"+ + "\u09EE\u09EF\u09F0\u09F1\u09F2\u09F3\u09F4\u09F5"+ + "\u09F6\u09F7\u09F8\u09F9\u09FA\u09FB\u09FC\u09FD"+ + "\u09FE\u09FF\u0A00\u0A01\u0A02\u0A03\u0A04\u0A05"+ + "\u0A06\u0A07\u0A08\u0A09\u0A0A\u0A0B\u0A0C\u0A0D"+ + "\u0A0E\u0A0F\u0A10\u0A11\u0A12\u0A13\u0A14\u0A15"+ + "\u0A16\u0A17\u0A18\u0A19\u0A1A\u0A1B\u0A1C\u0A1D"+ + "\u0A1E\u0A1F\u0A20\u0A21\u0A22\u0A23\u0A24\u0A25"+ + "\u0A26\u0A27\u0A28\u0A29\u0A2A\u0A2B\u0A2C\u0A2D"+ + "\u0A2E\u0A2F\u0A30\u0A31\u0A32\u0A33\u0A34\u0A35"+ + "\u0A36\u0A37\u0A38\u0A39\u0A3A\u0A3B\u0A3C\u0A3D"+ + "\u0A3E\u0A3F\u0A40\u0A41\u0A42\u0A43\u0A44\u0A45"+ + "\u0A46\u0A47\u0A48\u0A49\u0A4A\u0A4B\u0A4C\u0A4D"+ + "\u0A4E\u0A4F\u0A50\u0A51\u0A52\u0A53\u0A54\u0A55"+ + "\u0A56\u0A57\u0A58\u0A59\u0A5A\u0A5B\u0A5C\u0A5D"+ + "\u0A5E\u0A5F\u0A60\u0A61\u0A62\u0A63\u0A64\u0A65"+ + "\u0A66\u0A67\u0A68\u0A69\u0A6A\u0A6B\u0A6C\u0A6D"+ + "\u0A6E\u0A6F\u0A70\u0A71\u0A72\u0A73\u0A74\u0A75"+ + "\u0A76\u0A77\u0A78\u0A79\u0A7A\u0A7B\u0A7C\u0A7D"+ + "\u0A7E\u0A7F\u0A80\u0A81\u0A82\u0A83\u0A84\u0A85"+ + "\u0A86\u0A87\u0A88\u0A89\u0A8A\u0A8B\u0A8C\u0A8D"+ + "\u0A8E\u0A8F\u0A90\u0A91\u0A92\u0A93\u0A94\u0A95"+ + "\u0A96\u0A97\u0A98\u0A99\u0A9A\u0A9B\u0A9C\u0A9D"+ + "\u0A9E\u0A9F\u0AA0\u0AA1\u0AA2\u0AA3\u0AA4\u0AA5"+ + "\u0AA6\u0AA7\u0AA8\u0AA9\u0AAA\u0AAB\u0AAC\u0AAD"+ + "\u0AAE\u0AAF\u0AB0\u0AB1\u0AB2\u0AB3\u0AB4\u0AB5"+ + "\u0AB6\u0AB7\u0AB8\u0AB9\u0ABA\u0ABB\u0ABC\u0ABD"+ + "\u0ABE\u0ABF\u0AC0\u0AC1\u0AC2\u0AC3\u0AC4\u0AC5"+ + "\u0AC6\u0AC7\u0AC8\u0AC9\u0ACA\u0ACB\u0ACC\u0ACD"+ + "\u0ACE\u0ACF\u0AD0\u0AD1\u0AD2\u0AD3\u0AD4\u0AD5"+ + "\u0AD6\u0AD7\u0AD8\u0AD9\u0ADA\u0ADB\u0ADC\u0ADD"+ + "\u0ADE\u0ADF\u0AE0\u0AE1\u0AE2\u0AE3\u0AE4\u0AE5"+ + "\u0AE6\u0AE7\u0AE8\u0AE9\u0AEA\u0AEB\u0AEC\u0AED"+ + "\u0AEE\u0AEF\u0AF0\u0AF1\u0AF2\u0AF3\u0AF4\u0AF5"+ + "\u0AF6\u0AF7\u0AF8\u0AF9\u0AFA\u0AFB\u0AFC\u0AFD"+ + "\u0AFE\u0AFF\u0B00\u0B01\u0B02\u0B03\u0B04\u0B05"+ + "\u0B06\u0B07\u0B08\u0B09\u0B0A\u0B0B\u0B0C\u0B0D"+ + "\u0B0E\u0B0F\u0B10\u0B11\u0B12\u0B13\u0B14\u0B15"+ + "\u0B16\u0B17\u0B18\u0B19\u0B1A\u0B1B\u0B1C\u0B1D"+ + "\u0B1E\u0B1F\u0B20\u0B21\u0B22\u0B23\u0B24\u0B25"+ + "\u0B26\u0B27\u0B28\u0B29\u0B2A\u0B2B\u0B2C\u0B2D"+ + "\u0B2E\u0B2F\u0B30\u0B31\u0B32\u0B33\u0B34\u0B35"+ + "\u0B36\u0B37\u0B38\u0B39\u0B3A\u0B3B\u0B3C\u0B3D"+ + "\u0B3E\u0B3F\u0B40\u0B41\u0B42\u0B43\u0B44\u0B45"+ + "\u0B46\u0B47\u0B48\u0B49\u0B4A\u0B4B\u0B4C\u0B4D"+ + "\u0B4E\u0B4F\u0B50\u0B51\u0B52\u0B53\u0B54\u0B55"+ + "\u0B56\u0B57\u0B58\u0B59\u0B5A\u0B5B\u0B5C\u0B5D"+ + "\u0B5E\u0B5F\u0B60\u0B61\u0B62\u0B63\u0B64\u0B65"+ + "\u0B66\u0B67\u0B68\u0B69\u0B6A\u0B6B\u0B6C\u0B6D"+ + "\u0B6E\u0B6F\u0B70\u0B71\u0B72\u0B73\u0B74\u0B75"+ + "\u0B76\u0B77\u0B78\u0B79\u0B7A\u0B7B\u0B7C\u0B7D"+ + "\u0B7E\u0B7F\u0B80\u0B81\u0B82\u0B83\u0B84\u0B85"+ + "\u0B86\u0B87\u0B88\u0B89\u0B8A\u0B8B\u0B8C\u0B8D"+ + "\u0B8E\u0B8F\u0B90\u0B91\u0B92\u0B93\u0B94\u0B95"+ + "\u0B96\u0B97\u0B98\u0B99\u0B9A\u0B9B\u0B9C\u0B9D"+ + "\u0B9E\u0B9F\u0BA0\u0BA1\u0BA2\u0BA3\u0BA4\u0BA5"+ + "\u0BA6\u0BA7\u0BA8\u0BA9\u0BAA\u0BAB\u0BAC\u0BAD"+ + "\u0BAE\u0BAF\u0BB0\u0BB1\u0BB2\u0BB3\u0BB4\u0BB5"+ + "\u0BB6\u0BB7\u0BB8\u0BB9\u0BBA\u0BBB\u0BBC\u0BBD"+ + "\u0BBE\u0BBF\u0BC0\u0BC1\u0BC2\u0BC3\u0BC4\u0BC5"+ + "\u0BC6\u0BC7\u0BC8\u0BC9\u0BCA\u0BCB\u0BCC\u0BCD"+ + "\u0BCE\u0BCF\u0BD0\u0BD1\u0BD2\u0BD3\u0BD4\u0BD5"+ + "\u0BD6\u0BD7\u0BD8\u0BD9\u0BDA\u0BDB\u0BDC\u0BDD"+ + "\u0BDE\u0BDF\u0BE0\u0BE1\u0BE2\u0BE3\u0BE4\u0BE5"+ + "\u0BE6\u0BE7\u0BE8\u0BE9\u0BEA\u0BEB\u0BEC\u0BED"+ + "\u0BEE\u0BEF\u0BF0\u0BF1\u0BF2\u0BF3\u0BF4\u0BF5"+ + "\u0BF6\u0BF7\u0BF8\u0BF9\u0BFA\u0BFB\u0BFC\u0BFD"+ + "\u0BFE\u0BFF\u0C00\u0C01\u0C02\u0C03\u0C04\u0C05"+ + "\u0C06\u0C07\u0C08\u0C09\u0C0A\u0C0B\u0C0C\u0C0D"+ + "\u0C0E\u0C0F\u0C10\u0C11\u0C12\u0C13\u0C14\u0C15"+ + "\u0C16\u0C17\u0C18\u0C19\u0C1A\u0C1B\u0C1C\u0C1D"+ + "\u0C1E\u0C1F\u0C20\u0C21\u0C22\u0C23\u0C24\u0C25"+ + "\u0C26\u0C27\u0C28\u0C29\u0C2A\u0C2B\u0C2C\u0C2D"+ + "\u0C2E\u0C2F\u0C30\u0C31\u0C32\u0C33\u0C34\u0C35"+ + "\u0C36\u0C37\u0C38\u0C39\u0C3A\u0C3B\u0C3C\u0C3D"+ + "\u0C3E\u0C3F\u0C40\u0C41\u0C42\u0C43\u0C44\u0C45"+ + "\u0C46\u0C47\u0C48\u0C49\u0C4A\u0C4B\u0C4C\u0C4D"+ + "\u0C4E\u0C4F\u0C50\u0C51\u0C52\u0C53\u0C54\u0C55"+ + "\u0C56\u0C57\u0C58\u0C59\u0C5A\u0C5B\u0C5C\u0C5D"+ + "\u0C5E\u0C5F\u0C60\u0C61\u0C62\u0C63\u0C64\u0C65"+ + "\u0C66\u0C67\u0C68\u0C69\u0C6A\u0C6B\u0C6C\u0C6D"+ + "\u0C6E\u0C6F\u0C70\u0C71\u0C72\u0C73\u0C74\u0C75"+ + "\u0C76\u0C77\u0C78\u0C79\u0C7A\u0C7B\u0C7C\u0C7D"+ + "\u0C7E\u0C7F\u0C80\u0C81\u0C82\u0C83\u0C84\u0C85"+ + "\u0C86\u0C87\u0C88\u0C89\u0C8A\u0C8B\u0C8C\u0C8D"+ + "\u0C8E\u0C8F\u0C90\u0C91\u0C92\u0C93\u0C94\u0C95"+ + "\u0C96\u0C97\u0C98\u0C99\u0C9A\u0C9B\u0C9C\u0C9D"+ + "\u0C9E\u0C9F\u0CA0\u0CA1\u0CA2\u0CA3\u0CA4\u0CA5"+ + "\u0CA6\u0CA7\u0CA8\u0CA9\u0CAA\u0CAB\u0CAC\u0CAD"+ + "\u0CAE\u0CAF\u0CB0\u0CB1\u0CB2\u0CB3\u0CB4\u0CB5"+ + "\u0CB6\u0CB7\u0CB8\u0CB9\u0CBA\u0CBB\u0CBC\u0CBD"+ + "\u0CBE\u0CBF\u0CC0\u0CC1\u0CC2\u0CC3\u0CC4\u0CC5"+ + "\u0CC6\u0CC7\u0CC8\u0CC9\u0CCA\u0CCB\u0CCC\u0CCD"+ + "\u0CCE\u0CCF\u0CD0\u0CD1\u0CD2\u0CD3\u0CD4\u0CD5"+ + "\u0CD6\u0CD7\u0CD8\u0CD9\u0CDA\u0CDB\u0CDC\u0CDD"+ + "\u0CDE\u0CDF\u0CE0\u0CE1\u0CE2\u0CE3\u0CE4\u0CE5"+ + "\u0CE6\u0CE7\u0CE8\u0CE9\u0CEA\u0CEB\u0CEC\u0CED"+ + "\u0CEE\u0CEF\u0CF0\u0CF1\u0CF2\u0CF3\u0CF4\u0CF5"+ + "\u0CF6\u0CF7\u0CF8\u0CF9\u0CFA\u0CFB\u0CFC\u0CFD"+ + "\u0CFE\u0CFF\u0D00\u0D01\u0D02\u0D03\u0D04\u0D05"+ + "\u0D06\u0D07\u0D08\u0D09\u0D0A\u0D0B\u0D0C\u0D0D"+ + "\u0D0E\u0D0F\u0D10\u0D11\u0D12\u0D13\u0D14\u0D15"+ + "\u0D16\u0D17\u0D18\u0D19\u0D1A\u0D1B\u0D1C\u0D1D"+ + "\u0D1E\u0D1F\u0D20\u0D21\u0D22\u0D23\u0D24\u0D25"+ + "\u0D26\u0D27\u0D28\u0D29\u0D2A\u0D2B\u0D2C\u0D2D"+ + "\u0D2E\u0D2F\u0D30\u0D31\u0D32\u0D33\u0D34\u0D35"+ + "\u0D36\u0D37\u0D38\u0D39\u0D3A\u0D3B\u0D3C\u0D3D"+ + "\u0D3E\u0D3F\u0D40\u0D41\u0D42\u0D43\u0D44\u0D45"+ + "\u0D46\u0D47\u0D48\u0D49\u0D4A\u0D4B\u0D4C\u0D4D"+ + "\u0D4E\u0D4F\u0D50\u0D51\u0D52\u0D53\u0D54\u0D55"+ + "\u0D56\u0D57\u0D58\u0D59\u0D5A\u0D5B\u0D5C\u0D5D"+ + "\u0D5E\u0D5F\u0D60\u0D61\u0D62\u0D63\u0D64\u0D65"+ + "\u0D66\u0D67\u0D68\u0D69\u0D6A\u0D6B\u0D6C\u0D6D"+ + "\u0D6E\u0D6F\u0D70\u0D71\u0D72\u0D73\u0D74\u0D75"+ + "\u0D76\u0D77\u0D78\u0D79\u0D7A\u0D7B\u0D7C\u0D7D"+ + "\u0D7E\u0D7F\u0D80\u0D81\u0D82\u0D83\u0D84\u0D85"+ + "\u0D86\u0D87\u0D88\u0D89\u0D8A\u0D8B\u0D8C\u0D8D"+ + "\u0D8E\u0D8F\u0D90\u0D91\u0D92\u0D93\u0D94\u0D95"+ + "\u0D96\u0D97\u0D98\u0D99\u0D9A\u0D9B\u0D9C\u0D9D"+ + "\u0D9E\u0D9F\u0DA0\u0DA1\u0DA2\u0DA3\u0DA4\u0DA5"+ + "\u0DA6\u0DA7\u0DA8\u0DA9\u0DAA\u0DAB\u0DAC\u0DAD"+ + "\u0DAE\u0DAF\u0DB0\u0DB1\u0DB2\u0DB3\u0DB4\u0DB5"+ + "\u0DB6\u0DB7\u0DB8\u0DB9\u0DBA\u0DBB\u0DBC\u0DBD"+ + "\u0DBE\u0DBF\u0DC0\u0DC1\u0DC2\u0DC3\u0DC4\u0DC5"+ + "\u0DC6\u0DC7\u0DC8\u0DC9\u0DCA\u0DCB\u0DCC\u0DCD"+ + "\u0DCE\u0DCF\u0DD0\u0DD1\u0DD2\u0DD3\u0DD4\u0DD5"+ + "\u0DD6\u0DD7\u0DD8\u0DD9\u0DDA\u0DDB\u0DDC\u0DDD"+ + "\u0DDE\u0DDF\u0DE0\u0DE1\u0DE2\u0DE3\u0DE4\u0DE5"+ + "\u0DE6\u0DE7\u0DE8\u0DE9\u0DEA\u0DEB\u0DEC\u0DED"+ + "\u0DEE\u0DEF\u0DF0\u0DF1\u0DF2\u0DF3\u0DF4\u0DF5"+ + "\u0DF6\u0DF7\u0DF8\u0DF9\u0DFA\u0DFB\u0DFC\u0DFD"+ + "\u0DFE\u0DFF\u0E00\u0E01\u0E02\u0E03\u0E04\u0E05"+ + "\u0E06\u0E07\u0E08\u0E09\u0E0A\u0E0B\u0E0C\u0E0D"+ + "\u0E0E\u0E0F\u0E10\u0E11\u0E12\u0E13\u0E14\u0E15"+ + "\u0E16\u0E17\u0E18\u0E19\u0E1A\u0E1B\u0E1C\u0E1D"+ + "\u0E1E\u0E1F\u0E20\u0E21\u0E22\u0E23\u0E24\u0E25"+ + "\u0E26\u0E27\u0E28\u0E29\u0E2A\u0E2B\u0E2C\u0E2D"+ + "\u0E2E\u0E2F\u0E30\u0E31\u0E32\u0E33\u0E34\u0E35"+ + "\u0E36\u0E37\u0E38\u0E39\u0E3A\u0E3B\u0E3C\u0E3D"+ + "\u0E3E\u0E3F\u0E40\u0E41\u0E42\u0E43\u0E44\u0E45"+ + "\u0E46\u0E47\u0E48\u0E49\u0E4A\u0E4B\u0E4C\u0E4D"+ + "\u0E4E\u0E4F\u0E50\u0E51\u0E52\u0E53\u0E54\u0E55"+ + "\u0E56\u0E57\u0E58\u0E59\u0E5A\u0E5B\u0E5C\u0E5D"+ + "\u0E5E\u0E5F\u0E60\u0E61\u0E62\u0E63\u0E64\u0E65"+ + "\u0E66\u0E67\u0E68\u0E69\u0E6A\u0E6B\u0E6C\u0E6D"+ + "\u0E6E\u0E6F\u0E70\u0E71\u0E72\u0E73\u0E74\u0E75"+ + "\u0E76\u0E77\u0E78\u0E79\u0E7A\u0E7B\u0E7C\u0E7D"+ + "\u0E7E\u0E7F\u0E80\u0E81\u0E82\u0E83\u0E84\u0E85"+ + "\u0E86\u0E87\u0E88\u0E89\u0E8A\u0E8B\u0E8C\u0E8D"+ + "\u0E8E\u0E8F\u0E90\u0E91\u0E92\u0E93\u0E94\u0E95"+ + "\u0E96\u0E97\u0E98\u0E99\u0E9A\u0E9B\u0E9C\u0E9D"+ + "\u0E9E\u0E9F\u0EA0\u0EA1\u0EA2\u0EA3\u0EA4\u0EA5"+ + "\u0EA6\u0EA7\u0EA8\u0EA9\u0EAA\u0EAB\u0EAC\u0EAD"+ + "\u0EAE\u0EAF\u0EB0\u0EB1\u0EB2\u0EB3\u0EB4\u0EB5"+ + "\u0EB6\u0EB7\u0EB8\u0EB9\u0EBA\u0EBB\u0EBC\u0EBD"+ + "\u0EBE\u0EBF\u0EC0\u0EC1\u0EC2\u0EC3\u0EC4\u0EC5"+ + "\u0EC6\u0EC7\u0EC8\u0EC9\u0ECA\u0ECB\u0ECC\u0ECD"+ + "\u0ECE\u0ECF\u0ED0\u0ED1\u0ED2\u0ED3\u0ED4\u0ED5"+ + "\u0ED6\u0ED7\u0ED8\u0ED9\u0EDA\u0EDB\u0EDC\u0EDD"+ + "\u0EDE\u0EDF\u0EE0\u0EE1\u0EE2\u0EE3\u0EE4\u0EE5"+ + "\u0EE6\u0EE7\u0EE8\u0EE9\u0EEA\u0EEB\u0EEC\u0EED"+ + "\u0EEE\u0EEF\u0EF0\u0EF1\u0EF2\u0EF3\u0EF4\u0EF5"+ + "\u0EF6\u0EF7\u0EF8\u0EF9\u0EFA\u0EFB\u0EFC\u0EFD"+ + "\u0EFE\u0EFF\u0F00\u0F01\u0F02\u0F03\u0F04\u0F05"+ + "\u0F06\u0F07\u0F08\u0F09\u0F0A\u0F0B\u0F0C\u0F0D"+ + "\u0F0E\u0F0F\u0F10\u0F11\u0F12\u0F13\u0F14\u0F15"+ + "\u0F16\u0F17\u0F18\u0F19\u0F1A\u0F1B\u0F1C\u0F1D"+ + "\u0F1E\u0F1F\u0F20\u0F21\u0F22\u0F23\u0F24\u0F25"+ + "\u0F26\u0F27\u0F28\u0F29\u0F2A\u0F2B\u0F2C\u0F2D"+ + "\u0F2E\u0F2F\u0F30\u0F31\u0F32\u0F33\u0F34\u0F35"+ + "\u0F36\u0F37\u0F38\u0F39\u0F3A\u0F3B\u0F3C\u0F3D"+ + "\u0F3E\u0F3F\u0F40\u0F41\u0F42\u0F43\u0F44\u0F45"+ + "\u0F46\u0F47\u0F48\u0F49\u0F4A\u0F4B\u0F4C\u0F4D"+ + "\u0F4E\u0F4F\u0F50\u0F51\u0F52\u0F53\u0F54\u0F55"+ + "\u0F56\u0F57\u0F58\u0F59\u0F5A\u0F5B\u0F5C\u0F5D"+ + "\u0F5E\u0F5F\u0F60\u0F61\u0F62\u0F63\u0F64\u0F65"+ + "\u0F66\u0F67\u0F68\u0F69\u0F6A\u0F6B\u0F6C\u0F6D"+ + "\u0F6E\u0F6F\u0F70\u0F71\u0F72\u0F73\u0F74\u0F75"+ + "\u0F76\u0F77\u0F78\u0F79\u0F7A\u0F7B\u0F7C\u0F7D"+ + "\u0F7E\u0F7F\u0F80\u0F81\u0F82\u0F83\u0F84\u0F85"+ + "\u0F86\u0F87\u0F88\u0F89\u0F8A\u0F8B\u0F8C\u0F8D"+ + "\u0F8E\u0F8F\u0F90\u0F91\u0F92\u0F93\u0F94\u0F95"+ + "\u0F96\u0F97\u0F98\u0F99\u0F9A\u0F9B\u0F9C\u0F9D"+ + "\u0F9E\u0F9F\u0FA0\u0FA1\u0FA2\u0FA3\u0FA4\u0FA5"+ + "\u0FA6\u0FA7\u0FA8\u0FA9\u0FAA\u0FAB\u0FAC\u0FAD"+ + "\u0FAE\u0FAF\u0FB0\u0FB1\u0FB2\u0FB3\u0FB4\u0FB5"+ + "\u0FB6\u0FB7\u0FB8\u0FB9\u0FBA\u0FBB\u0FBC\u0FBD"+ + "\u0FBE\u0FBF\u0FC0\u0FC1\u0FC2\u0FC3\u0FC4\u0FC5"+ + "\u0FC6\u0FC7\u0FC8\u0FC9\u0FCA\u0FCB\u0FCC\u0FCD"+ + "\u0FCE\u0FCF\u0FD0\u0FD1\u0FD2\u0FD3\u0FD4\u0FD5"+ + "\u0FD6\u0FD7\u0FD8\u0FD9\u0FDA\u0FDB\u0FDC\u0FDD"+ + "\u0FDE\u0FDF\u0FE0\u0FE1\u0FE2\u0FE3\u0FE4\u0FE5"+ + "\u0FE6\u0FE7\u0FE8\u0FE9\u0FEA\u0FEB\u0FEC\u0FED"+ + "\u0FEE\u0FEF\u0FF0\u0FF1\u0FF2\u0FF3\u0FF4\u0FF5"+ + "\u0FF6\u0FF7\u0FF8\u0FF9\u0FFA\u0FFB\u0FFC\u0FFD"+ + "\u0FFE\u0FFF\u1000\u1001\u1002\u1003\u1004\u1005"+ + "\u1006\u1007\u1008\u1009\u100A\u100B\u100C\u100D"+ + "\u100E\u100F\u1010\u1011\u1012\u1013\u1014\u1015"+ + "\u1016\u1017\u1018\u1019\u101A\u101B\u101C\u101D"+ + "\u101E\u101F\u1020\u1021\u1022\u1023\u1024\u1025"+ + "\u1026\u1027\u1028\u1029\u102A\u102B\u102C\u102D"+ + "\u102E\u102F\u1030\u1031\u1032\u1033\u1034\u1035"+ + "\u1036\u1037\u1038\u1039\u103A\u103B\u103C\u103D"+ + "\u103E\u103F\u1040\u1041\u1042\u1043\u1044\u1045"+ + "\u1046\u1047\u1048\u1049\u104A\u104B\u104C\u104D"+ + "\u104E\u104F\u1050\u1051\u1052\u1053\u1054\u1055"+ + "\u1056\u1057\u1058\u1059\u105A\u105B\u105C\u105D"+ + "\u105E\u105F\u1060\u1061\u1062\u1063\u1064\u1065"+ + "\u1066\u1067\u1068\u1069\u106A\u106B\u106C\u106D"+ + "\u106E\u106F\u1070\u1071\u1072\u1073\u1074\u1075"+ + "\u1076\u1077\u1078\u1079\u107A\u107B\u107C\u107D"+ + "\u107E\u107F\u1080\u1081\u1082\u1083\u1084\u1085"+ + "\u1086\u1087\u1088\u1089\u108A\u108B\u108C\u108D"+ + "\u108E\u108F\u1090\u1091\u1092\u1093\u1094\u1095"+ + "\u1096\u1097\u1098\u1099\u109A\u109B\u109C\u109D"+ + "\u109E\u109F\u10A0\u10A1\u10A2\u10A3\u10A4\u10A5"+ + "\u10A6\u10A7\u10A8\u10A9\u10AA\u10AB\u10AC\u10AD"+ + "\u10AE\u10AF\u10B0\u10B1\u10B2\u10B3\u10B4\u10B5"+ + "\u10B6\u10B7\u10B8\u10B9\u10BA\u10BB\u10BC\u10BD"+ + "\u10BE\u10BF\u10C0\u10C1\u10C2\u10C3\u10C4\u10C5"+ + "\u10C6\u10C7\u10C8\u10C9\u10CA\u10CB\u10CC\u10CD"+ + "\u10CE\u10CF\u10D0\u10D1\u10D2\u10D3\u10D4\u10D5"+ + "\u10D6\u10D7\u10D8\u10D9\u10DA\u10DB\u10DC\u10DD"+ + "\u10DE\u10DF\u10E0\u10E1\u10E2\u10E3\u10E4\u10E5"+ + "\u10E6\u10E7\u10E8\u10E9\u10EA\u10EB\u10EC\u10ED"+ + "\u10EE\u10EF\u10F0\u10F1\u10F2\u10F3\u10F4\u10F5"+ + "\u10F6\u10F7\u10F8\u10F9\u10FA\u10FB\u10FC\u10FD"+ + "\u10FE\u10FF\u1100\u1101\u1102\u1103\u1104\u1105"+ + "\u1106\u1107\u1108\u1109\u110A\u110B\u110C\u110D"+ + "\u110E\u110F\u1110\u1111\u1112\u1113\u1114\u1115"+ + "\u1116\u1117\u1118\u1119\u111A\u111B\u111C\u111D"; + + private final static String innerDecoderIndex1= + "\u111E\u111F\u1120\u1121\u1122\u1123\u1124\u1125"+ + "\u1126\u1127\u1128\u1129\u112A\u112B\u112C\u112D"+ + "\u112E\u112F\u1130\u1131\u1132\u1133\u1134\u1135"+ + "\u1136\u1137\u1138\u1139\u113A\u113B\u113C\u113D"+ + "\u113E\u113F\u1140\u1141\u1142\u1143\u1144\u1145"+ + "\u1146\u1147\u1148\u1149\u114A\u114B\u114C\u114D"+ + "\u114E\u114F\u1150\u1151\u1152\u1153\u1154\u1155"+ + "\u1156\u1157\u1158\u1159\u115A\u115B\u115C\u115D"+ + "\u115E\u115F\u1160\u1161\u1162\u1163\u1164\u1165"+ + "\u1166\u1167\u1168\u1169\u116A\u116B\u116C\u116D"+ + "\u116E\u116F\u1170\u1171\u1172\u1173\u1174\u1175"+ + "\u1176\u1177\u1178\u1179\u117A\u117B\u117C\u117D"+ + "\u117E\u117F\u1180\u1181\u1182\u1183\u1184\u1185"+ + "\u1186\u1187\u1188\u1189\u118A\u118B\u118C\u118D"+ + "\u118E\u118F\u1190\u1191\u1192\u1193\u1194\u1195"+ + "\u1196\u1197\u1198\u1199\u119A\u119B\u119C\u119D"+ + "\u119E\u119F\u11A0\u11A1\u11A2\u11A3\u11A4\u11A5"+ + "\u11A6\u11A7\u11A8\u11A9\u11AA\u11AB\u11AC\u11AD"+ + "\u11AE\u11AF\u11B0\u11B1\u11B2\u11B3\u11B4\u11B5"+ + "\u11B6\u11B7\u11B8\u11B9\u11BA\u11BB\u11BC\u11BD"+ + "\u11BE\u11BF\u11C0\u11C1\u11C2\u11C3\u11C4\u11C5"+ + "\u11C6\u11C7\u11C8\u11C9\u11CA\u11CB\u11CC\u11CD"+ + "\u11CE\u11CF\u11D0\u11D1\u11D2\u11D3\u11D4\u11D5"+ + "\u11D6\u11D7\u11D8\u11D9\u11DA\u11DB\u11DC\u11DD"+ + "\u11DE\u11DF\u11E0\u11E1\u11E2\u11E3\u11E4\u11E5"+ + "\u11E6\u11E7\u11E8\u11E9\u11EA\u11EB\u11EC\u11ED"+ + "\u11EE\u11EF\u11F0\u11F1\u11F2\u11F3\u11F4\u11F5"+ + "\u11F6\u11F7\u11F8\u11F9\u11FA\u11FB\u11FC\u11FD"+ + "\u11FE\u11FF\u1200\u1201\u1202\u1203\u1204\u1205"+ + "\u1206\u1207\u1208\u1209\u120A\u120B\u120C\u120D"+ + "\u120E\u120F\u1210\u1211\u1212\u1213\u1214\u1215"+ + "\u1216\u1217\u1218\u1219\u121A\u121B\u121C\u121D"+ + "\u121E\u121F\u1220\u1221\u1222\u1223\u1224\u1225"+ + "\u1226\u1227\u1228\u1229\u122A\u122B\u122C\u122D"+ + "\u122E\u122F\u1230\u1231\u1232\u1233\u1234\u1235"+ + "\u1236\u1237\u1238\u1239\u123A\u123B\u123C\u123D"+ + "\u123E\u123F\u1240\u1241\u1242\u1243\u1244\u1245"+ + "\u1246\u1247\u1248\u1249\u124A\u124B\u124C\u124D"+ + "\u124E\u124F\u1250\u1251\u1252\u1253\u1254\u1255"+ + "\u1256\u1257\u1258\u1259\u125A\u125B\u125C\u125D"+ + "\u125E\u125F\u1260\u1261\u1262\u1263\u1264\u1265"+ + "\u1266\u1267\u1268\u1269\u126A\u126B\u126C\u126D"+ + "\u126E\u126F\u1270\u1271\u1272\u1273\u1274\u1275"+ + "\u1276\u1277\u1278\u1279\u127A\u127B\u127C\u127D"+ + "\u127E\u127F\u1280\u1281\u1282\u1283\u1284\u1285"+ + "\u1286\u1287\u1288\u1289\u128A\u128B\u128C\u128D"+ + "\u128E\u128F\u1290\u1291\u1292\u1293\u1294\u1295"+ + "\u1296\u1297\u1298\u1299\u129A\u129B\u129C\u129D"+ + "\u129E\u129F\u12A0\u12A1\u12A2\u12A3\u12A4\u12A5"+ + "\u12A6\u12A7\u12A8\u12A9\u12AA\u12AB\u12AC\u12AD"+ + "\u12AE\u12AF\u12B0\u12B1\u12B2\u12B3\u12B4\u12B5"+ + "\u12B6\u12B7\u12B8\u12B9\u12BA\u12BB\u12BC\u12BD"+ + "\u12BE\u12BF\u12C0\u12C1\u12C2\u12C3\u12C4\u12C5"+ + "\u12C6\u12C7\u12C8\u12C9\u12CA\u12CB\u12CC\u12CD"+ + "\u12CE\u12CF\u12D0\u12D1\u12D2\u12D3\u12D4\u12D5"+ + "\u12D6\u12D7\u12D8\u12D9\u12DA\u12DB\u12DC\u12DD"+ + "\u12DE\u12DF\u12E0\u12E1\u12E2\u12E3\u12E4\u12E5"+ + "\u12E6\u12E7\u12E8\u12E9\u12EA\u12EB\u12EC\u12ED"+ + "\u12EE\u12EF\u12F0\u12F1\u12F2\u12F3\u12F4\u12F5"+ + "\u12F6\u12F7\u12F8\u12F9\u12FA\u12FB\u12FC\u12FD"+ + "\u12FE\u12FF\u1300\u1301\u1302\u1303\u1304\u1305"+ + "\u1306\u1307\u1308\u1309\u130A\u130B\u130C\u130D"+ + "\u130E\u130F\u1310\u1311\u1312\u1313\u1314\u1315"+ + "\u1316\u1317\u1318\u1319\u131A\u131B\u131C\u131D"+ + "\u131E\u131F\u1320\u1321\u1322\u1323\u1324\u1325"+ + "\u1326\u1327\u1328\u1329\u132A\u132B\u132C\u132D"+ + "\u132E\u132F\u1330\u1331\u1332\u1333\u1334\u1335"+ + "\u1336\u1337\u1338\u1339\u133A\u133B\u133C\u133D"+ + "\u133E\u133F\u1340\u1341\u1342\u1343\u1344\u1345"+ + "\u1346\u1347\u1348\u1349\u134A\u134B\u134C\u134D"+ + "\u134E\u134F\u1350\u1351\u1352\u1353\u1354\u1355"+ + "\u1356\u1357\u1358\u1359\u135A\u135B\u135C\u135D"+ + "\u135E\u135F\u1360\u1361\u1362\u1363\u1364\u1365"+ + "\u1366\u1367\u1368\u1369\u136A\u136B\u136C\u136D"+ + "\u136E\u136F\u1370\u1371\u1372\u1373\u1374\u1375"+ + "\u1376\u1377\u1378\u1379\u137A\u137B\u137C\u137D"+ + "\u137E\u137F\u1380\u1381\u1382\u1383\u1384\u1385"+ + "\u1386\u1387\u1388\u1389\u138A\u138B\u138C\u138D"+ + "\u138E\u138F\u1390\u1391\u1392\u1393\u1394\u1395"+ + "\u1396\u1397\u1398\u1399\u139A\u139B\u139C\u139D"+ + "\u139E\u139F\u13A0\u13A1\u13A2\u13A3\u13A4\u13A5"+ + "\u13A6\u13A7\u13A8\u13A9\u13AA\u13AB\u13AC\u13AD"+ + "\u13AE\u13AF\u13B0\u13B1\u13B2\u13B3\u13B4\u13B5"+ + "\u13B6\u13B7\u13B8\u13B9\u13BA\u13BB\u13BC\u13BD"+ + "\u13BE\u13BF\u13C0\u13C1\u13C2\u13C3\u13C4\u13C5"+ + "\u13C6\u13C7\u13C8\u13C9\u13CA\u13CB\u13CC\u13CD"+ + "\u13CE\u13CF\u13D0\u13D1\u13D2\u13D3\u13D4\u13D5"+ + "\u13D6\u13D7\u13D8\u13D9\u13DA\u13DB\u13DC\u13DD"+ + "\u13DE\u13DF\u13E0\u13E1\u13E2\u13E3\u13E4\u13E5"+ + "\u13E6\u13E7\u13E8\u13E9\u13EA\u13EB\u13EC\u13ED"+ + "\u13EE\u13EF\u13F0\u13F1\u13F2\u13F3\u13F4\u13F5"+ + "\u13F6\u13F7\u13F8\u13F9\u13FA\u13FB\u13FC\u13FD"+ + "\u13FE\u13FF\u1400\u1401\u1402\u1403\u1404\u1405"+ + "\u1406\u1407\u1408\u1409\u140A\u140B\u140C\u140D"+ + "\u140E\u140F\u1410\u1411\u1412\u1413\u1414\u1415"+ + "\u1416\u1417\u1418\u1419\u141A\u141B\u141C\u141D"+ + "\u141E\u141F\u1420\u1421\u1422\u1423\u1424\u1425"+ + "\u1426\u1427\u1428\u1429\u142A\u142B\u142C\u142D"+ + "\u142E\u142F\u1430\u1431\u1432\u1433\u1434\u1435"+ + "\u1436\u1437\u1438\u1439\u143A\u143B\u143C\u143D"+ + "\u143E\u143F\u1440\u1441\u1442\u1443\u1444\u1445"+ + "\u1446\u1447\u1448\u1449\u144A\u144B\u144C\u144D"+ + "\u144E\u144F\u1450\u1451\u1452\u1453\u1454\u1455"+ + "\u1456\u1457\u1458\u1459\u145A\u145B\u145C\u145D"+ + "\u145E\u145F\u1460\u1461\u1462\u1463\u1464\u1465"+ + "\u1466\u1467\u1468\u1469\u146A\u146B\u146C\u146D"+ + "\u146E\u146F\u1470\u1471\u1472\u1473\u1474\u1475"+ + "\u1476\u1477\u1478\u1479\u147A\u147B\u147C\u147D"+ + "\u147E\u147F\u1480\u1481\u1482\u1483\u1484\u1485"+ + "\u1486\u1487\u1488\u1489\u148A\u148B\u148C\u148D"+ + "\u148E\u148F\u1490\u1491\u1492\u1493\u1494\u1495"+ + "\u1496\u1497\u1498\u1499\u149A\u149B\u149C\u149D"+ + "\u149E\u149F\u14A0\u14A1\u14A2\u14A3\u14A4\u14A5"+ + "\u14A6\u14A7\u14A8\u14A9\u14AA\u14AB\u14AC\u14AD"+ + "\u14AE\u14AF\u14B0\u14B1\u14B2\u14B3\u14B4\u14B5"+ + "\u14B6\u14B7\u14B8\u14B9\u14BA\u14BB\u14BC\u14BD"+ + "\u14BE\u14BF\u14C0\u14C1\u14C2\u14C3\u14C4\u14C5"+ + "\u14C6\u14C7\u14C8\u14C9\u14CA\u14CB\u14CC\u14CD"+ + "\u14CE\u14CF\u14D0\u14D1\u14D2\u14D3\u14D4\u14D5"+ + "\u14D6\u14D7\u14D8\u14D9\u14DA\u14DB\u14DC\u14DD"+ + "\u14DE\u14DF\u14E0\u14E1\u14E2\u14E3\u14E4\u14E5"+ + "\u14E6\u14E7\u14E8\u14E9\u14EA\u14EB\u14EC\u14ED"+ + "\u14EE\u14EF\u14F0\u14F1\u14F2\u14F3\u14F4\u14F5"+ + "\u14F6\u14F7\u14F8\u14F9\u14FA\u14FB\u14FC\u14FD"+ + "\u14FE\u14FF\u1500\u1501\u1502\u1503\u1504\u1505"+ + "\u1506\u1507\u1508\u1509\u150A\u150B\u150C\u150D"+ + "\u150E\u150F\u1510\u1511\u1512\u1513\u1514\u1515"+ + "\u1516\u1517\u1518\u1519\u151A\u151B\u151C\u151D"+ + "\u151E\u151F\u1520\u1521\u1522\u1523\u1524\u1525"+ + "\u1526\u1527\u1528\u1529\u152A\u152B\u152C\u152D"+ + "\u152E\u152F\u1530\u1531\u1532\u1533\u1534\u1535"+ + "\u1536\u1537\u1538\u1539\u153A\u153B\u153C\u153D"+ + "\u153E\u153F\u1540\u1541\u1542\u1543\u1544\u1545"+ + "\u1546\u1547\u1548\u1549\u154A\u154B\u154C\u154D"+ + "\u154E\u154F\u1550\u1551\u1552\u1553\u1554\u1555"+ + "\u1556\u1557\u1558\u1559\u155A\u155B\u155C\u155D"+ + "\u155E\u155F\u1560\u1561\u1562\u1563\u1564\u1565"+ + "\u1566\u1567\u1568\u1569\u156A\u156B\u156C\u156D"+ + "\u156E\u156F\u1570\u1571\u1572\u1573\u1574\u1575"+ + "\u1576\u1577\u1578\u1579\u157A\u157B\u157C\u157D"+ + "\u157E\u157F\u1580\u1581\u1582\u1583\u1584\u1585"+ + "\u1586\u1587\u1588\u1589\u158A\u158B\u158C\u158D"+ + "\u158E\u158F\u1590\u1591\u1592\u1593\u1594\u1595"+ + "\u1596\u1597\u1598\u1599\u159A\u159B\u159C\u159D"+ + "\u159E\u159F\u15A0\u15A1\u15A2\u15A3\u15A4\u15A5"+ + "\u15A6\u15A7\u15A8\u15A9\u15AA\u15AB\u15AC\u15AD"+ + "\u15AE\u15AF\u15B0\u15B1\u15B2\u15B3\u15B4\u15B5"+ + "\u15B6\u15B7\u15B8\u15B9\u15BA\u15BB\u15BC\u15BD"+ + "\u15BE\u15BF\u15C0\u15C1\u15C2\u15C3\u15C4\u15C5"+ + "\u15C6\u15C7\u15C8\u15C9\u15CA\u15CB\u15CC\u15CD"+ + "\u15CE\u15CF\u15D0\u15D1\u15D2\u15D3\u15D4\u15D5"+ + "\u15D6\u15D7\u15D8\u15D9\u15DA\u15DB\u15DC\u15DD"+ + "\u15DE\u15DF\u15E0\u15E1\u15E2\u15E3\u15E4\u15E5"+ + "\u15E6\u15E7\u15E8\u15E9\u15EA\u15EB\u15EC\u15ED"+ + "\u15EE\u15EF\u15F0\u15F1\u15F2\u15F3\u15F4\u15F5"+ + "\u15F6\u15F7\u15F8\u15F9\u15FA\u15FB\u15FC\u15FD"+ + "\u15FE\u15FF\u1600\u1601\u1602\u1603\u1604\u1605"+ + "\u1606\u1607\u1608\u1609\u160A\u160B\u160C\u160D"+ + "\u160E\u160F\u1610\u1611\u1612\u1613\u1614\u1615"+ + "\u1616\u1617\u1618\u1619\u161A\u161B\u161C\u161D"+ + "\u161E\u161F\u1620\u1621\u1622\u1623\u1624\u1625"+ + "\u1626\u1627\u1628\u1629\u162A\u162B\u162C\u162D"+ + "\u162E\u162F\u1630\u1631\u1632\u1633\u1634\u1635"+ + "\u1636\u1637\u1638\u1639\u163A\u163B\u163C\u163D"+ + "\u163E\u163F\u1640\u1641\u1642\u1643\u1644\u1645"+ + "\u1646\u1647\u1648\u1649\u164A\u164B\u164C\u164D"+ + "\u164E\u164F\u1650\u1651\u1652\u1653\u1654\u1655"+ + "\u1656\u1657\u1658\u1659\u165A\u165B\u165C\u165D"+ + "\u165E\u165F\u1660\u1661\u1662\u1663\u1664\u1665"+ + "\u1666\u1667\u1668\u1669\u166A\u166B\u166C\u166D"+ + "\u166E\u166F\u1670\u1671\u1672\u1673\u1674\u1675"+ + "\u1676\u1677\u1678\u1679\u167A\u167B\u167C\u167D"+ + "\u167E\u167F\u1680\u1681\u1682\u1683\u1684\u1685"+ + "\u1686\u1687\u1688\u1689\u168A\u168B\u168C\u168D"+ + "\u168E\u168F\u1690\u1691\u1692\u1693\u1694\u1695"+ + "\u1696\u1697\u1698\u1699\u169A\u169B\u169C\u169D"+ + "\u169E\u169F\u16A0\u16A1\u16A2\u16A3\u16A4\u16A5"+ + "\u16A6\u16A7\u16A8\u16A9\u16AA\u16AB\u16AC\u16AD"+ + "\u16AE\u16AF\u16B0\u16B1\u16B2\u16B3\u16B4\u16B5"+ + "\u16B6\u16B7\u16B8\u16B9\u16BA\u16BB\u16BC\u16BD"+ + "\u16BE\u16BF\u16C0\u16C1\u16C2\u16C3\u16C4\u16C5"+ + "\u16C6\u16C7\u16C8\u16C9\u16CA\u16CB\u16CC\u16CD"+ + "\u16CE\u16CF\u16D0\u16D1\u16D2\u16D3\u16D4\u16D5"+ + "\u16D6\u16D7\u16D8\u16D9\u16DA\u16DB\u16DC\u16DD"+ + "\u16DE\u16DF\u16E0\u16E1\u16E2\u16E3\u16E4\u16E5"+ + "\u16E6\u16E7\u16E8\u16E9\u16EA\u16EB\u16EC\u16ED"+ + "\u16EE\u16EF\u16F0\u16F1\u16F2\u16F3\u16F4\u16F5"+ + "\u16F6\u16F7\u16F8\u16F9\u16FA\u16FB\u16FC\u16FD"+ + "\u16FE\u16FF\u1700\u1701\u1702\u1703\u1704\u1705"+ + "\u1706\u1707\u1708\u1709\u170A\u170B\u170C\u170D"+ + "\u170E\u170F\u1710\u1711\u1712\u1713\u1714\u1715"+ + "\u1716\u1717\u1718\u1719\u171A\u171B\u171C\u171D"+ + "\u171E\u171F\u1720\u1721\u1722\u1723\u1724\u1725"+ + "\u1726\u1727\u1728\u1729\u172A\u172B\u172C\u172D"+ + "\u172E\u172F\u1730\u1731\u1732\u1733\u1734\u1735"+ + "\u1736\u1737\u1738\u1739\u173A\u173B\u173C\u173D"+ + "\u173E\u173F\u1740\u1741\u1742\u1743\u1744\u1745"+ + "\u1746\u1747\u1748\u1749\u174A\u174B\u174C\u174D"+ + "\u174E\u174F\u1750\u1751\u1752\u1753\u1754\u1755"+ + "\u1756\u1757\u1758\u1759\u175A\u175B\u175C\u175D"+ + "\u175E\u175F\u1760\u1761\u1762\u1763\u1764\u1765"+ + "\u1766\u1767\u1768\u1769\u176A\u176B\u176C\u176D"+ + "\u176E\u176F\u1770\u1771\u1772\u1773\u1774\u1775"+ + "\u1776\u1777\u1778\u1779\u177A\u177B\u177C\u177D"+ + "\u177E\u177F\u1780\u1781\u1782\u1783\u1784\u1785"+ + "\u1786\u1787\u1788\u1789\u178A\u178B\u178C\u178D"+ + "\u178E\u178F\u1790\u1791\u1792\u1793\u1794\u1795"+ + "\u1796\u1797\u1798\u1799\u179A\u179B\u179C\u179D"+ + "\u179E\u179F\u17A0\u17A1\u17A2\u17A3\u17A4\u17A5"+ + "\u17A6\u17A7\u17A8\u17A9\u17AA\u17AB\u17AC\u17AD"+ + "\u17AE\u17AF\u17B0\u17B1\u17B2\u17B3\u17B4\u17B5"+ + "\u17B6\u17B7\u17B8\u17B9\u17BA\u17BB\u17BC\u17BD"+ + "\u17BE\u17BF\u17C0\u17C1\u17C2\u17C3\u17C4\u17C5"+ + "\u17C6\u17C7\u17C8\u17C9\u17CA\u17CB\u17CC\u17CD"+ + "\u17CE\u17CF\u17D0\u17D1\u17D2\u17D3\u17D4\u17D5"+ + "\u17D6\u17D7\u17D8\u17D9\u17DA\u17DB\u17DC\u17DD"+ + "\u17DE\u17DF\u17E0\u17E1\u17E2\u17E3\u17E4\u17E5"+ + "\u17E6\u17E7\u17E8\u17E9\u17EA\u17EB\u17EC\u17ED"+ + "\u17EE\u17EF\u17F0\u17F1\u17F2\u17F3\u17F4\u17F5"+ + "\u17F6\u17F7\u17F8\u17F9\u17FA\u17FB\u17FC\u17FD"+ + "\u17FE\u17FF\u1800\u1801\u1802\u1803\u1804\u1805"+ + "\u1806\u1807\u1808\u1809\u180A\u180B\u180C\u180D"+ + "\u180E\u180F\u1810\u1811\u1812\u1813\u1814\u1815"+ + "\u1816\u1817\u1818\u1819\u181A\u181B\u181C\u181D"+ + "\u181E\u181F\u1820\u1821\u1822\u1823\u1824\u1825"+ + "\u1826\u1827\u1828\u1829\u182A\u182B\u182C\u182D"+ + "\u182E\u182F\u1830\u1831\u1832\u1833\u1834\u1835"+ + "\u1836\u1837\u1838\u1839\u183A\u183B\u183C\u183D"+ + "\u183E\u183F\u1840\u1841\u1842\u1843\u1844\u1845"+ + "\u1846\u1847\u1848\u1849\u184A\u184B\u184C\u184D"+ + "\u184E\u184F\u1850\u1851\u1852\u1853\u1854\u1855"+ + "\u1856\u1857\u1858\u1859\u185A\u185B\u185C\u185D"+ + "\u185E\u185F\u1860\u1861\u1862\u1863\u1864\u1865"+ + "\u1866\u1867\u1868\u1869\u186A\u186B\u186C\u186D"+ + "\u186E\u186F\u1870\u1871\u1872\u1873\u1874\u1875"+ + "\u1876\u1877\u1878\u1879\u187A\u187B\u187C\u187D"+ + "\u187E\u187F\u1880\u1881\u1882\u1883\u1884\u1885"+ + "\u1886\u1887\u1888\u1889\u188A\u188B\u188C\u188D"+ + "\u188E\u188F\u1890\u1891\u1892\u1893\u1894\u1895"+ + "\u1896\u1897\u1898\u1899\u189A\u189B\u189C\u189D"+ + "\u189E\u189F\u18A0\u18A1\u18A2\u18A3\u18A4\u18A5"+ + "\u18A6\u18A7\u18A8\u18A9\u18AA\u18AB\u18AC\u18AD"+ + "\u18AE\u18AF\u18B0\u18B1\u18B2\u18B3\u18B4\u18B5"+ + "\u18B6\u18B7\u18B8\u18B9\u18BA\u18BB\u18BC\u18BD"+ + "\u18BE\u18BF\u18C0\u18C1\u18C2\u18C3\u18C4\u18C5"+ + "\u18C6\u18C7\u18C8\u18C9\u18CA\u18CB\u18CC\u18CD"+ + "\u18CE\u18CF\u18D0\u18D1\u18D2\u18D3\u18D4\u18D5"+ + "\u18D6\u18D7\u18D8\u18D9\u18DA\u18DB\u18DC\u18DD"+ + "\u18DE\u18DF\u18E0\u18E1\u18E2\u18E3\u18E4\u18E5"+ + "\u18E6\u18E7\u18E8\u18E9\u18EA\u18EB\u18EC\u18ED"+ + "\u18EE\u18EF\u18F0\u18F1\u18F2\u18F3\u18F4\u18F5"+ + "\u18F6\u18F7\u18F8\u18F9\u18FA\u18FB\u18FC\u18FD"+ + "\u18FE\u18FF\u1900\u1901\u1902\u1903\u1904\u1905"+ + "\u1906\u1907\u1908\u1909\u190A\u190B\u190C\u190D"+ + "\u190E\u190F\u1910\u1911\u1912\u1913\u1914\u1915"+ + "\u1916\u1917\u1918\u1919\u191A\u191B\u191C\u191D"+ + "\u191E\u191F\u1920\u1921\u1922\u1923\u1924\u1925"+ + "\u1926\u1927\u1928\u1929\u192A\u192B\u192C\u192D"+ + "\u192E\u192F\u1930\u1931\u1932\u1933\u1934\u1935"+ + "\u1936\u1937\u1938\u1939\u193A\u193B\u193C\u193D"+ + "\u193E\u193F\u1940\u1941\u1942\u1943\u1944\u1945"+ + "\u1946\u1947\u1948\u1949\u194A\u194B\u194C\u194D"+ + "\u194E\u194F\u1950\u1951\u1952\u1953\u1954\u1955"+ + "\u1956\u1957\u1958\u1959\u195A\u195B\u195C\u195D"+ + "\u195E\u195F\u1960\u1961\u1962\u1963\u1964\u1965"+ + "\u1966\u1967\u1968\u1969\u196A\u196B\u196C\u196D"+ + "\u196E\u196F\u1970\u1971\u1972\u1973\u1974\u1975"+ + "\u1976\u1977\u1978\u1979\u197A\u197B\u197C\u197D"+ + "\u197E\u197F\u1980\u1981\u1982\u1983\u1984\u1985"+ + "\u1986\u1987\u1988\u1989\u198A\u198B\u198C\u198D"+ + "\u198E\u198F\u1990\u1991\u1992\u1993\u1994\u1995"+ + "\u1996\u1997\u1998\u1999\u199A\u199B\u199C\u199D"+ + "\u199E\u199F\u19A0\u19A1\u19A2\u19A3\u19A4\u19A5"+ + "\u19A6\u19A7\u19A8\u19A9\u19AA\u19AB\u19AC\u19AD"+ + "\u19AE\u19AF\u19B0\u19B1\u19B2\u19B3\u19B4\u19B5"+ + "\u19B6\u19B7\u19B8\u19B9\u19BA\u19BB\u19BC\u19BD"+ + "\u19BE\u19BF\u19C0\u19C1\u19C2\u19C3\u19C4\u19C5"+ + "\u19C6\u19C7\u19C8\u19C9\u19CA\u19CB\u19CC\u19CD"+ + "\u19CE\u19CF\u19D0\u19D1\u19D2\u19D3\u19D4\u19D5"+ + "\u19D6\u19D7\u19D8\u19D9\u19DA\u19DB\u19DC\u19DD"+ + "\u19DE\u19DF\u19E0\u19E1\u19E2\u19E3\u19E4\u19E5"+ + "\u19E6\u19E7\u19E8\u19E9\u19EA\u19EB\u19EC\u19ED"+ + "\u19EE\u19EF\u19F0\u19F1\u19F2\u19F3\u19F4\u19F5"+ + "\u19F6\u19F7\u19F8\u19F9\u19FA\u19FB\u19FC\u19FD"+ + "\u19FE\u19FF\u1A00\u1A01\u1A02\u1A03\u1A04\u1A05"+ + "\u1A06\u1A07\u1A08\u1A09\u1A0A\u1A0B\u1A0C\u1A0D"+ + "\u1A0E\u1A0F\u1A10\u1A11\u1A12\u1A13\u1A14\u1A15"+ + "\u1A16\u1A17\u1A18\u1A19\u1A1A\u1A1B\u1A1C\u1A1D"+ + "\u1A1E\u1A1F\u1A20\u1A21\u1A22\u1A23\u1A24\u1A25"+ + "\u1A26\u1A27\u1A28\u1A29\u1A2A\u1A2B\u1A2C\u1A2D"+ + "\u1A2E\u1A2F\u1A30\u1A31\u1A32\u1A33\u1A34\u1A35"+ + "\u1A36\u1A37\u1A38\u1A39\u1A3A\u1A3B\u1A3C\u1A3D"+ + "\u1A3E\u1A3F\u1A40\u1A41\u1A42\u1A43\u1A44\u1A45"+ + "\u1A46\u1A47\u1A48\u1A49\u1A4A\u1A4B\u1A4C\u1A4D"+ + "\u1A4E\u1A4F\u1A50\u1A51\u1A52\u1A53\u1A54\u1A55"+ + "\u1A56\u1A57\u1A58\u1A59\u1A5A\u1A5B\u1A5C\u1A5D"+ + "\u1A5E\u1A5F\u1A60\u1A61\u1A62\u1A63\u1A64\u1A65"+ + "\u1A66\u1A67\u1A68\u1A69\u1A6A\u1A6B\u1A6C\u1A6D"+ + "\u1A6E\u1A6F\u1A70\u1A71\u1A72\u1A73\u1A74\u1A75"+ + "\u1A76\u1A77\u1A78\u1A79\u1A7A\u1A7B\u1A7C\u1A7D"+ + "\u1A7E\u1A7F\u1A80\u1A81\u1A82\u1A83\u1A84\u1A85"+ + "\u1A86\u1A87\u1A88\u1A89\u1A8A\u1A8B\u1A8C\u1A8D"+ + "\u1A8E\u1A8F\u1A90\u1A91\u1A92\u1A93\u1A94\u1A95"+ + "\u1A96\u1A97\u1A98\u1A99\u1A9A\u1A9B\u1A9C\u1A9D"+ + "\u1A9E\u1A9F\u1AA0\u1AA1\u1AA2\u1AA3\u1AA4\u1AA5"+ + "\u1AA6\u1AA7\u1AA8\u1AA9\u1AAA\u1AAB\u1AAC\u1AAD"+ + "\u1AAE\u1AAF\u1AB0\u1AB1\u1AB2\u1AB3\u1AB4\u1AB5"+ + "\u1AB6\u1AB7\u1AB8\u1AB9\u1ABA\u1ABB\u1ABC\u1ABD"+ + "\u1ABE\u1ABF\u1AC0\u1AC1\u1AC2\u1AC3\u1AC4\u1AC5"+ + "\u1AC6\u1AC7\u1AC8\u1AC9\u1ACA\u1ACB\u1ACC\u1ACD"+ + "\u1ACE\u1ACF\u1AD0\u1AD1\u1AD2\u1AD3\u1AD4\u1AD5"+ + "\u1AD6\u1AD7\u1AD8\u1AD9\u1ADA\u1ADB\u1ADC\u1ADD"+ + "\u1ADE\u1ADF\u1AE0\u1AE1\u1AE2\u1AE3\u1AE4\u1AE5"+ + "\u1AE6\u1AE7\u1AE8\u1AE9\u1AEA\u1AEB\u1AEC\u1AED"+ + "\u1AEE\u1AEF\u1AF0\u1AF1\u1AF2\u1AF3\u1AF4\u1AF5"+ + "\u1AF6\u1AF7\u1AF8\u1AF9\u1AFA\u1AFB\u1AFC\u1AFD"+ + "\u1AFE\u1AFF\u1B00\u1B01\u1B02\u1B03\u1B04\u1B05"+ + "\u1B06\u1B07\u1B08\u1B09\u1B0A\u1B0B\u1B0C\u1B0D"+ + "\u1B0E\u1B0F\u1B10\u1B11\u1B12\u1B13\u1B14\u1B15"+ + "\u1B16\u1B17\u1B18\u1B19\u1B1A\u1B1B\u1B1C\u1B1D"+ + "\u1B1E\u1B1F\u1B20\u1B21\u1B22\u1B23\u1B24\u1B25"+ + "\u1B26\u1B27\u1B28\u1B29\u1B2A\u1B2B\u1B2C\u1B2D"+ + "\u1B2E\u1B2F\u1B30\u1B31\u1B32\u1B33\u1B34\u1B35"+ + "\u1B36\u1B37\u1B38\u1B39\u1B3A\u1B3B\u1B3C\u1B3D"+ + "\u1B3E\u1B3F\u1B40\u1B41\u1B42\u1B43\u1B44\u1B45"+ + "\u1B46\u1B47\u1B48\u1B49\u1B4A\u1B4B\u1B4C\u1B4D"+ + "\u1B4E\u1B4F\u1B50\u1B51\u1B52\u1B53\u1B54\u1B55"+ + "\u1B56\u1B57\u1B58\u1B59\u1B5A\u1B5B\u1B5C\u1B5D"+ + "\u1B5E\u1B5F\u1B60\u1B61\u1B62\u1B63\u1B64\u1B65"+ + "\u1B66\u1B67\u1B68\u1B69\u1B6A\u1B6B\u1B6C\u1B6D"+ + "\u1B6E\u1B6F\u1B70\u1B71\u1B72\u1B73\u1B74\u1B75"+ + "\u1B76\u1B77\u1B78\u1B79\u1B7A\u1B7B\u1B7C\u1B7D"+ + "\u1B7E\u1B7F\u1B80\u1B81\u1B82\u1B83\u1B84\u1B85"+ + "\u1B86\u1B87\u1B88\u1B89\u1B8A\u1B8B\u1B8C\u1B8D"+ + "\u1B8E\u1B8F\u1B90\u1B91\u1B92\u1B93\u1B94\u1B95"+ + "\u1B96\u1B97\u1B98\u1B99\u1B9A\u1B9B\u1B9C\u1B9D"+ + "\u1B9E\u1B9F\u1BA0\u1BA1\u1BA2\u1BA3\u1BA4\u1BA5"+ + "\u1BA6\u1BA7\u1BA8\u1BA9\u1BAA\u1BAB\u1BAC\u1BAD"+ + "\u1BAE\u1BAF\u1BB0\u1BB1\u1BB2\u1BB3\u1BB4\u1BB5"+ + "\u1BB6\u1BB7\u1BB8\u1BB9\u1BBA\u1BBB\u1BBC\u1BBD"+ + "\u1BBE\u1BBF\u1BC0\u1BC1\u1BC2\u1BC3\u1BC4\u1BC5"+ + "\u1BC6\u1BC7\u1BC8\u1BC9\u1BCA\u1BCB\u1BCC\u1BCD"+ + "\u1BCE\u1BCF\u1BD0\u1BD1\u1BD2\u1BD3\u1BD4\u1BD5"+ + "\u1BD6\u1BD7\u1BD8\u1BD9\u1BDA\u1BDB\u1BDC\u1BDD"+ + "\u1BDE\u1BDF\u1BE0\u1BE1\u1BE2\u1BE3\u1BE4\u1BE5"+ + "\u1BE6\u1BE7\u1BE8\u1BE9\u1BEA\u1BEB\u1BEC\u1BED"+ + "\u1BEE\u1BEF\u1BF0\u1BF1\u1BF2\u1BF3\u1BF4\u1BF5"+ + "\u1BF6\u1BF7\u1BF8\u1BF9\u1BFA\u1BFB\u1BFC\u1BFD"+ + "\u1BFE\u1BFF\u1C00\u1C01\u1C02\u1C03\u1C04\u1C05"+ + "\u1C06\u1C07\u1C08\u1C09\u1C0A\u1C0B\u1C0C\u1C0D"+ + "\u1C0E\u1C0F\u1C10\u1C11\u1C12\u1C13\u1C14\u1C15"+ + "\u1C16\u1C17\u1C18\u1C19\u1C1A\u1C1B\u1C1C\u1C1D"+ + "\u1C1E\u1C1F\u1C20\u1C21\u1C22\u1C23\u1C24\u1C25"+ + "\u1C26\u1C27\u1C28\u1C29\u1C2A\u1C2B\u1C2C\u1C2D"+ + "\u1C2E\u1C2F\u1C30\u1C31\u1C32\u1C33\u1C34\u1C35"+ + "\u1C36\u1C37\u1C38\u1C39\u1C3A\u1C3B\u1C3C\u1C3D"+ + "\u1C3E\u1C3F\u1C40\u1C41\u1C42\u1C43\u1C44\u1C45"+ + "\u1C46\u1C47\u1C48\u1C49\u1C4A\u1C4B\u1C4C\u1C4D"+ + "\u1C4E\u1C4F\u1C50\u1C51\u1C52\u1C53\u1C54\u1C55"+ + "\u1C56\u1C57\u1C58\u1C59\u1C5A\u1C5B\u1C5C\u1C5D"+ + "\u1C5E\u1C5F\u1C60\u1C61\u1C62\u1C63\u1C64\u1C65"+ + "\u1C66\u1C67\u1C68\u1C69\u1C6A\u1C6B\u1C6C\u1C6D"+ + "\u1C6E\u1C6F\u1C70\u1C71\u1C72\u1C73\u1C74\u1C75"+ + "\u1C76\u1C77\u1C78\u1C79\u1C7A\u1C7B\u1C7C\u1C7D"+ + "\u1C7E\u1C7F\u1C80\u1C81\u1C82\u1C83\u1C84\u1C85"+ + "\u1C86\u1C87\u1C88\u1C89\u1C8A\u1C8B\u1C8C\u1C8D"+ + "\u1C8E\u1C8F\u1C90\u1C91\u1C92\u1C93\u1C94\u1C95"+ + "\u1C96\u1C97\u1C98\u1C99\u1C9A\u1C9B\u1C9C\u1C9D"+ + "\u1C9E\u1C9F\u1CA0\u1CA1\u1CA2\u1CA3\u1CA4\u1CA5"+ + "\u1CA6\u1CA7\u1CA8\u1CA9\u1CAA\u1CAB\u1CAC\u1CAD"+ + "\u1CAE\u1CAF\u1CB0\u1CB1\u1CB2\u1CB3\u1CB4\u1CB5"+ + "\u1CB6\u1CB7\u1CB8\u1CB9\u1CBA\u1CBB\u1CBC\u1CBD"+ + "\u1CBE\u1CBF\u1CC0\u1CC1\u1CC2\u1CC3\u1CC4\u1CC5"+ + "\u1CC6\u1CC7\u1CC8\u1CC9\u1CCA\u1CCB\u1CCC\u1CCD"+ + "\u1CCE\u1CCF\u1CD0\u1CD1\u1CD2\u1CD3\u1CD4\u1CD5"+ + "\u1CD6\u1CD7\u1CD8\u1CD9\u1CDA\u1CDB\u1CDC\u1CDD"+ + "\u1CDE\u1CDF\u1CE0\u1CE1\u1CE2\u1CE3\u1CE4\u1CE5"+ + "\u1CE6\u1CE7\u1CE8\u1CE9\u1CEA\u1CEB\u1CEC\u1CED"+ + "\u1CEE\u1CEF\u1CF0\u1CF1\u1CF2\u1CF3\u1CF4\u1CF5"+ + "\u1CF6\u1CF7\u1CF8\u1CF9\u1CFA\u1CFB\u1CFC\u1CFD"+ + "\u1CFE\u1CFF\u1D00\u1D01\u1D02\u1D03\u1D04\u1D05"+ + "\u1D06\u1D07\u1D08\u1D09\u1D0A\u1D0B\u1D0C\u1D0D"+ + "\u1D0E\u1D0F\u1D10\u1D11\u1D12\u1D13\u1D14\u1D15"+ + "\u1D16\u1D17\u1D18\u1D19\u1D1A\u1D1B\u1D1C\u1D1D"+ + "\u1D1E\u1D1F\u1D20\u1D21\u1D22\u1D23\u1D24\u1D25"+ + "\u1D26\u1D27\u1D28\u1D29\u1D2A\u1D2B\u1D2C\u1D2D"+ + "\u1D2E\u1D2F\u1D30\u1D31\u1D32\u1D33\u1D34\u1D35"+ + "\u1D36\u1D37\u1D38\u1D39\u1D3A\u1D3B\u1D3C\u1D3D"+ + "\u1D3E\u1D3F\u1D40\u1D41\u1D42\u1D43\u1D44\u1D45"+ + "\u1D46\u1D47\u1D48\u1D49\u1D4A\u1D4B\u1D4C\u1D4D"+ + "\u1D4E\u1D4F\u1D50\u1D51\u1D52\u1D53\u1D54\u1D55"+ + "\u1D56\u1D57\u1D58\u1D59\u1D5A\u1D5B\u1D5C\u1D5D"+ + "\u1D5E\u1D5F\u1D60\u1D61\u1D62\u1D63\u1D64\u1D65"+ + "\u1D66\u1D67\u1D68\u1D69\u1D6A\u1D6B\u1D6C\u1D6D"+ + "\u1D6E\u1D6F\u1D70\u1D71\u1D72\u1D73\u1D74\u1D75"+ + "\u1D76\u1D77\u1D78\u1D79\u1D7A\u1D7B\u1D7C\u1D7D"+ + "\u1D7E\u1D7F\u1D80\u1D81\u1D82\u1D83\u1D84\u1D85"+ + "\u1D86\u1D87\u1D88\u1D89\u1D8A\u1D8B\u1D8C\u1D8D"+ + "\u1D8E\u1D8F\u1D90\u1D91\u1D92\u1D93\u1D94\u1D95"+ + "\u1D96\u1D97\u1D98\u1D99\u1D9A\u1D9B\u1D9C\u1D9D"+ + "\u1D9E\u1D9F\u1DA0\u1DA1\u1DA2\u1DA3\u1DA4\u1DA5"+ + "\u1DA6\u1DA7\u1DA8\u1DA9\u1DAA\u1DAB\u1DAC\u1DAD"+ + "\u1DAE\u1DAF\u1DB0\u1DB1\u1DB2\u1DB3\u1DB4\u1DB5"+ + "\u1DB6\u1DB7\u1DB8\u1DB9\u1DBA\u1DBB\u1DBC\u1DBD"+ + "\u1DBE\u1DBF\u1DC0\u1DC1\u1DC2\u1DC3\u1DC4\u1DC5"+ + "\u1DC6\u1DC7\u1DC8\u1DC9\u1DCA\u1DCB\u1DCC\u1DCD"+ + "\u1DCE\u1DCF\u1DD0\u1DD1\u1DD2\u1DD3\u1DD4\u1DD5"+ + "\u1DD6\u1DD7\u1DD8\u1DD9\u1DDA\u1DDB\u1DDC\u1DDD"+ + "\u1DDE\u1DDF\u1DE0\u1DE1\u1DE2\u1DE3\u1DE4\u1DE5"+ + "\u1DE6\u1DE7\u1DE8\u1DE9\u1DEA\u1DEB\u1DEC\u1DED"+ + "\u1DEE\u1DEF\u1DF0\u1DF1\u1DF2\u1DF3\u1DF4\u1DF5"+ + "\u1DF6\u1DF7\u1DF8\u1DF9\u1DFA\u1DFB\u1DFC\u1DFD"+ + "\u1DFE\u1DFF\u1E00\u1E01\u1E02\u1E03\u1E04\u1E05"+ + "\u1E06\u1E07\u1E08\u1E09\u1E0A\u1E0B\u1E0C\u1E0D"+ + "\u1E0E\u1E0F\u1E10\u1E11\u1E12\u1E13\u1E14\u1E15"+ + "\u1E16\u1E17\u1E18\u1E19\u1E1A\u1E1B\u1E1C\u1E1D"+ + "\u1E1E\u1E1F\u1E20\u1E21\u1E22\u1E23\u1E24\u1E25"+ + "\u1E26\u1E27\u1E28\u1E29\u1E2A\u1E2B\u1E2C\u1E2D"+ + "\u1E2E\u1E2F\u1E30\u1E31\u1E32\u1E33\u1E34\u1E35"+ + "\u1E36\u1E37\u1E38\u1E39\u1E3A\u1E3B\u1E3C\u1E3D"+ + "\u1E3E\u1E3F\u1E40\u1E41\u1E42\u1E43\u1E44\u1E45"+ + "\u1E46\u1E47\u1E48\u1E49\u1E4A\u1E4B\u1E4C\u1E4D"+ + "\u1E4E\u1E4F\u1E50\u1E51\u1E52\u1E53\u1E54\u1E55"+ + "\u1E56\u1E57\u1E58\u1E59\u1E5A\u1E5B\u1E5C\u1E5D"+ + "\u1E5E\u1E5F\u1E60\u1E61\u1E62\u1E63\u1E64\u1E65"+ + "\u1E66\u1E67\u1E68\u1E69\u1E6A\u1E6B\u1E6C\u1E6D"+ + "\u1E6E\u1E6F\u1E70\u1E71\u1E72\u1E73\u1E74\u1E75"+ + "\u1E76\u1E77\u1E78\u1E79\u1E7A\u1E7B\u1E7C\u1E7D"+ + "\u1E7E\u1E7F\u1E80\u1E81\u1E82\u1E83\u1E84\u1E85"+ + "\u1E86\u1E87\u1E88\u1E89\u1E8A\u1E8B\u1E8C\u1E8D"+ + "\u1E8E\u1E8F\u1E90\u1E91\u1E92\u1E93\u1E94\u1E95"+ + "\u1E96\u1E97\u1E98\u1E99\u1E9A\u1E9B\u1E9C\u1E9D"+ + "\u1E9E\u1E9F\u1EA0\u1EA1\u1EA2\u1EA3\u1EA4\u1EA5"+ + "\u1EA6\u1EA7\u1EA8\u1EA9\u1EAA\u1EAB\u1EAC\u1EAD"+ + "\u1EAE\u1EAF\u1EB0\u1EB1\u1EB2\u1EB3\u1EB4\u1EB5"+ + "\u1EB6\u1EB7\u1EB8\u1EB9\u1EBA\u1EBB\u1EBC\u1EBD"+ + "\u1EBE\u1EBF\u1EC0\u1EC1\u1EC2\u1EC3\u1EC4\u1EC5"+ + "\u1EC6\u1EC7\u1EC8\u1EC9\u1ECA\u1ECB\u1ECC\u1ECD"+ + "\u1ECE\u1ECF\u1ED0\u1ED1\u1ED2\u1ED3\u1ED4\u1ED5"+ + "\u1ED6\u1ED7\u1ED8\u1ED9\u1EDA\u1EDB\u1EDC\u1EDD"+ + "\u1EDE\u1EDF\u1EE0\u1EE1\u1EE2\u1EE3\u1EE4\u1EE5"+ + "\u1EE6\u1EE7\u1EE8\u1EE9\u1EEA\u1EEB\u1EEC\u1EED"+ + "\u1EEE\u1EEF\u1EF0\u1EF1\u1EF2\u1EF3\u1EF4\u1EF5"+ + "\u1EF6\u1EF7\u1EF8\u1EF9\u1EFA\u1EFB\u1EFC\u1EFD"+ + "\u1EFE\u1EFF\u1F00\u1F01\u1F02\u1F03\u1F04\u1F05"+ + "\u1F06\u1F07\u1F08\u1F09\u1F0A\u1F0B\u1F0C\u1F0D"+ + "\u1F0E\u1F0F\u1F10\u1F11\u1F12\u1F13\u1F14\u1F15"+ + "\u1F16\u1F17\u1F18\u1F19\u1F1A\u1F1B\u1F1C\u1F1D"+ + "\u1F1E\u1F1F\u1F20\u1F21\u1F22\u1F23\u1F24\u1F25"+ + "\u1F26\u1F27\u1F28\u1F29\u1F2A\u1F2B\u1F2C\u1F2D"+ + "\u1F2E\u1F2F\u1F30\u1F31\u1F32\u1F33\u1F34\u1F35"+ + "\u1F36\u1F37\u1F38\u1F39\u1F3A\u1F3B\u1F3C\u1F3D"+ + "\u1F3E\u1F3F\u1F40\u1F41\u1F42\u1F43\u1F44\u1F45"+ + "\u1F46\u1F47\u1F48\u1F49\u1F4A\u1F4B\u1F4C\u1F4D"+ + "\u1F4E\u1F4F\u1F50\u1F51\u1F52\u1F53\u1F54\u1F55"+ + "\u1F56\u1F57\u1F58\u1F59\u1F5A\u1F5B\u1F5C\u1F5D"+ + "\u1F5E\u1F5F\u1F60\u1F61\u1F62\u1F63\u1F64\u1F65"+ + "\u1F66\u1F67\u1F68\u1F69\u1F6A\u1F6B\u1F6C\u1F6D"+ + "\u1F6E\u1F6F\u1F70\u1F71\u1F72\u1F73\u1F74\u1F75"+ + "\u1F76\u1F77\u1F78\u1F79\u1F7A\u1F7B\u1F7C\u1F7D"+ + "\u1F7E\u1F7F\u1F80\u1F81\u1F82\u1F83\u1F84\u1F85"+ + "\u1F86\u1F87\u1F88\u1F89\u1F8A\u1F8B\u1F8C\u1F8D"+ + "\u1F8E\u1F8F\u1F90\u1F91\u1F92\u1F93\u1F94\u1F95"+ + "\u1F96\u1F97\u1F98\u1F99\u1F9A\u1F9B\u1F9C\u1F9D"+ + "\u1F9E\u1F9F\u1FA0\u1FA1\u1FA2\u1FA3\u1FA4\u1FA5"+ + "\u1FA6\u1FA7\u1FA8\u1FA9\u1FAA\u1FAB\u1FAC\u1FAD"+ + "\u1FAE\u1FAF\u1FB0\u1FB1\u1FB2\u1FB3\u1FB4\u1FB5"+ + "\u1FB6\u1FB7\u1FB8\u1FB9\u1FBA\u1FBB\u1FBC\u1FBD"+ + "\u1FBE\u1FBF\u1FC0\u1FC1\u1FC2\u1FC3\u1FC4\u1FC5"+ + "\u1FC6\u1FC7\u1FC8\u1FC9\u1FCA\u1FCB\u1FCC\u1FCD"+ + "\u1FCE\u1FCF\u1FD0\u1FD1\u1FD2\u1FD3\u1FD4\u1FD5"+ + "\u1FD6\u1FD7\u1FD8\u1FD9\u1FDA\u1FDB\u1FDC\u1FDD"+ + "\u1FDE\u1FDF\u1FE0\u1FE1\u1FE2\u1FE3\u1FE4\u1FE5"+ + "\u1FE6\u1FE7\u1FE8\u1FE9\u1FEA\u1FEB\u1FEC\u1FED"+ + "\u1FEE\u1FEF\u1FF0\u1FF1\u1FF2\u1FF3\u1FF4\u1FF5"+ + "\u1FF6\u1FF7\u1FF8\u1FF9\u1FFA\u1FFB\u1FFC\u1FFD"+ + "\u1FFE\u1FFF\u2000\u2001\u2002\u2003\u2004\u2005"+ + "\u2006\u2007\u2008\u2009\u200A\u200B\u200C\u200D"+ + "\u200E\u200F\u2011\u2012\u2017\u201A\u201B\u201E"+ + "\u201F\u2020\u2021\u2022\u2023\u2024\u2027\u2028"+ + "\u2029\u202A\u202B\u202C\u202D\u202E\u202F\u2031"+ + "\u2034\u2036\u2037\u2038\u2039\u203A\u203C\u203D"+ + "\u203E\u203F\u2040\u2041\u2042\u2043\u2044\u2045"+ + "\u2046\u2047\u2048\u2049\u204A\u204B\u204C\u204D"+ + "\u204E\u204F\u2050\u2051\u2052\u2053\u2054\u2055"+ + "\u2056\u2057\u2058\u2059\u205A\u205B\u205C\u205D"+ + "\u205E\u205F\u2060\u2061\u2062\u2063\u2064\u2065"+ + "\u2066\u2067\u2068\u2069\u206A\u206B\u206C\u206D"+ + "\u206E\u206F\u2070\u2071\u2072\u2073\u2074\u2075"+ + "\u2076\u2077\u2078\u2079\u207A\u207B\u207C\u207D"+ + "\u207E\u207F\u2080\u2081\u2082\u2083\u2084\u2085"+ + "\u2086\u2087\u2088\u2089\u208A\u208B\u208C\u208D"+ + "\u208E\u208F\u2090\u2091\u2092\u2093\u2094\u2095"+ + "\u2096\u2097\u2098\u2099\u209A\u209B\u209C\u209D"+ + "\u209E\u209F\u20A0\u20A1\u20A2\u20A3\u20A4\u20A5"+ + "\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB\u20AD\u20AE"+ + "\u20AF\u20B0\u20B1\u20B2\u20B3\u20B4\u20B5\u20B6"+ + "\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC\u20BD\u20BE"+ + "\u20BF\u20C0\u20C1\u20C2\u20C3\u20C4\u20C5\u20C6"+ + "\u20C7\u20C8\u20C9\u20CA\u20CB\u20CC\u20CD\u20CE"+ + "\u20CF\u20D0\u20D1\u20D2\u20D3\u20D4\u20D5\u20D6"+ + "\u20D7\u20D8\u20D9\u20DA\u20DB\u20DC\u20DD\u20DE"+ + "\u20DF\u20E0\u20E1\u20E2\u20E3\u20E4\u20E5\u20E6"+ + "\u20E7\u20E8\u20E9\u20EA\u20EB\u20EC\u20ED\u20EE"+ + "\u20EF\u20F0\u20F1\u20F2\u20F3\u20F4\u20F5\u20F6"+ + "\u20F7\u20F8\u20F9\u20FA\u20FB\u20FC\u20FD\u20FE"+ + "\u20FF\u2100\u2101\u2102\u2104\u2106\u2107\u2108"+ + "\u210A\u210B\u210C\u210D\u210E\u210F\u2110\u2111"+ + "\u2112\u2113\u2114\u2115\u2117\u2118\u2119\u211A"+ + "\u211B\u211C\u211D\u211E\u211F\u2120\u2122\u2123"+ + "\u2124\u2125\u2126\u2127\u2128\u2129\u212A\u212B"+ + "\u212C\u212D\u212E\u212F\u2130\u2131\u2132\u2133"; + + private final static String innerDecoderIndex2= + "\u2134\u2135\u2136\u2137\u2138\u2139\u213A\u213B"+ + "\u213C\u213D\u213E\u213F\u2140\u2141\u2142\u2143"+ + "\u2144\u2145\u2146\u2147\u2148\u2149\u214A\u214B"+ + "\u214C\u214D\u214E\u214F\u2150\u2151\u2152\u2153"+ + "\u2154\u2155\u2156\u2157\u2158\u2159\u215A\u215B"+ + "\u215C\u215D\u215E\u215F\u216C\u216D\u216E\u216F"+ + "\u217A\u217B\u217C\u217D\u217E\u217F\u2180\u2181"+ + "\u2182\u2183\u2184\u2185\u2186\u2187\u2188\u2189"+ + "\u218A\u218B\u218C\u218D\u218E\u218F\u2194\u2195"+ + "\u219A\u219B\u219C\u219D\u219E\u219F\u21A0\u21A1"+ + "\u21A2\u21A3\u21A4\u21A5\u21A6\u21A7\u21A8\u21A9"+ + "\u21AA\u21AB\u21AC\u21AD\u21AE\u21AF\u21B0\u21B1"+ + "\u21B2\u21B3\u21B4\u21B5\u21B6\u21B7\u21B8\u21B9"+ + "\u21BA\u21BB\u21BC\u21BD\u21BE\u21BF\u21C0\u21C1"+ + "\u21C2\u21C3\u21C4\u21C5\u21C6\u21C7\u21C8\u21C9"+ + "\u21CA\u21CB\u21CC\u21CD\u21CE\u21CF\u21D0\u21D1"+ + "\u21D2\u21D3\u21D4\u21D5\u21D6\u21D7\u21D8\u21D9"+ + "\u21DA\u21DB\u21DC\u21DD\u21DE\u21DF\u21E0\u21E1"+ + "\u21E2\u21E3\u21E4\u21E5\u21E6\u21E7\u21E8\u21E9"+ + "\u21EA\u21EB\u21EC\u21ED\u21EE\u21EF\u21F0\u21F1"+ + "\u21F2\u21F3\u21F4\u21F5\u21F6\u21F7\u21F8\u21F9"+ + "\u21FA\u21FB\u21FC\u21FD\u21FE\u21FF\u2200\u2201"+ + "\u2202\u2203\u2204\u2205\u2206\u2207\u2209\u220A"+ + "\u220B\u220C\u220D\u220E\u2210\u2212\u2213\u2214"+ + "\u2216\u2217\u2218\u2219\u221B\u221C\u2221\u2222"+ + "\u2224\u2226\u222C\u222D\u222F\u2230\u2231\u2232"+ + "\u2233\u2238\u2239\u223A\u223B\u223C\u223E\u223F"+ + "\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247"+ + "\u2249\u224A\u224B\u224D\u224E\u224F\u2250\u2251"+ + "\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225A"+ + "\u225B\u225C\u225D\u225E\u225F\u2262\u2263\u2268"+ + "\u2269\u226A\u226B\u226C\u226D\u2270\u2271\u2272"+ + "\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227A"+ + "\u227B\u227C\u227D\u227E\u227F\u2280\u2281\u2282"+ + "\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228A"+ + "\u228B\u228C\u228D\u228E\u228F\u2290\u2291\u2292"+ + "\u2293\u2294\u2296\u2297\u2298\u229A\u229B\u229C"+ + "\u229D\u229E\u229F\u22A0\u22A1\u22A2\u22A3\u22A4"+ + "\u22A6\u22A7\u22A8\u22A9\u22AA\u22AB\u22AC\u22AD"+ + "\u22AE\u22AF\u22B0\u22B1\u22B2\u22B3\u22B4\u22B5"+ + "\u22B6\u22B7\u22B8\u22B9\u22BA\u22BB\u22BC\u22BD"+ + "\u22BE\u22C0\u22C1\u22C2\u22C3\u22C4\u22C5\u22C6"+ + "\u22C7\u22C8\u22C9\u22CA\u22CB\u22CC\u22CD\u22CE"+ + "\u22CF\u22D0\u22D1\u22D2\u22D3\u22D4\u22D5\u22D6"+ + "\u22D7\u22D8\u22D9\u22DA\u22DB\u22DC\u22DD\u22DE"+ + "\u22DF\u22E0\u22E1\u22E2\u22E3\u22E4\u22E5\u22E6"+ + "\u22E7\u22E8\u22E9\u22EA\u22EB\u22EC\u22ED\u22EE"+ + "\u22EF\u22F0\u22F1\u22F2\u22F3\u22F4\u22F5\u22F6"+ + "\u22F7\u22F8\u22F9\u22FA\u22FB\u22FC\u22FD\u22FE"+ + "\u22FF\u2300\u2301\u2302\u2303\u2304\u2305\u2306"+ + "\u2307\u2308\u2309\u230A\u230B\u230C\u230D\u230E"+ + "\u230F\u2310\u2311\u2313\u2314\u2315\u2316\u2317"+ + "\u2318\u2319\u231A\u231B\u231C\u231D\u231E\u231F"+ + "\u2320\u2321\u2322\u2323\u2324\u2325\u2326\u2327"+ + "\u2328\u2329\u232A\u232B\u232C\u232D\u232E\u232F"+ + "\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337"+ + "\u2338\u2339\u233A\u233B\u233C\u233D\u233E\u233F"+ + "\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347"+ + "\u2348\u2349\u234A\u234B\u234C\u234D\u234E\u234F"+ + "\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357"+ + "\u2358\u2359\u235A\u235B\u235C\u235D\u235E\u235F"+ + "\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367"+ + "\u2368\u2369\u236A\u236B\u236C\u236D\u236E\u236F"+ + "\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377"+ + "\u2378\u2379\u237A\u237B\u237C\u237D\u237E\u237F"+ + "\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387"+ + "\u2388\u2389\u238A\u238B\u238C\u238D\u238E\u238F"+ + "\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397"+ + "\u2398\u2399\u239A\u239B\u239C\u239D\u239E\u239F"+ + "\u23A0\u23A1\u23A2\u23A3\u23A4\u23A5\u23A6\u23A7"+ + "\u23A8\u23A9\u23AA\u23AB\u23AC\u23AD\u23AE\u23AF"+ + "\u23B0\u23B1\u23B2\u23B3\u23B4\u23B5\u23B6\u23B7"+ + "\u23B8\u23B9\u23BA\u23BB\u23BC\u23BD\u23BE\u23BF"+ + "\u23C0\u23C1\u23C2\u23C3\u23C4\u23C5\u23C6\u23C7"+ + "\u23C8\u23C9\u23CA\u23CB\u23CC\u23CD\u23CE\u23CF"+ + "\u23D0\u23D1\u23D2\u23D3\u23D4\u23D5\u23D6\u23D7"+ + "\u23D8\u23D9\u23DA\u23DB\u23DC\u23DD\u23DE\u23DF"+ + "\u23E0\u23E1\u23E2\u23E3\u23E4\u23E5\u23E6\u23E7"+ + "\u23E8\u23E9\u23EA\u23EB\u23EC\u23ED\u23EE\u23EF"+ + "\u23F0\u23F1\u23F2\u23F3\u23F4\u23F5\u23F6\u23F7"+ + "\u23F8\u23F9\u23FA\u23FB\u23FC\u23FD\u23FE\u23FF"+ + "\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407"+ + "\u2408\u2409\u240A\u240B\u240C\u240D\u240E\u240F"+ + "\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417"+ + "\u2418\u2419\u241A\u241B\u241C\u241D\u241E\u241F"+ + "\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2427"+ + "\u2428\u2429\u242A\u242B\u242C\u242D\u242E\u242F"+ + "\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437"+ + "\u2438\u2439\u243A\u243B\u243C\u243D\u243E\u243F"+ + "\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447"+ + "\u2448\u2449\u244A\u244B\u244C\u244D\u244E\u244F"+ + "\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457"+ + "\u2458\u2459\u245A\u245B\u245C\u245D\u245E\u245F"+ + "\u246A\u246B\u246C\u246D\u246E\u246F\u2470\u2471"+ + "\u2472\u2473\u249C\u249D\u249E\u249F\u24A0\u24A1"+ + "\u24A2\u24A3\u24A4\u24A5\u24A6\u24A7\u24A8\u24A9"+ + "\u24AA\u24AB\u24AC\u24AD\u24AE\u24AF\u24B0\u24B1"+ + "\u24B2\u24B3\u24B4\u24B5\u24B6\u24B7\u24B8\u24B9"+ + "\u24BA\u24BB\u24BC\u24BD\u24BE\u24BF\u24C0\u24C1"+ + "\u24C2\u24C3\u24C4\u24C5\u24C6\u24C7\u24C8\u24C9"+ + "\u24CA\u24CB\u24CC\u24CD\u24CE\u24CF\u24D0\u24D1"+ + "\u24D2\u24D3\u24D4\u24D5\u24D6\u24D7\u24D8\u24D9"+ + "\u24DA\u24DB\u24DC\u24DD\u24DE\u24DF\u24E0\u24E1"+ + "\u24E2\u24E3\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9"+ + "\u24EA\u24EB\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1"+ + "\u24F2\u24F3\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9"+ + "\u24FA\u24FB\u24FC\u24FD\u24FE\u24FF\u254C\u254D"+ + "\u254E\u254F\u2574\u2575\u2576\u2577\u2578\u2579"+ + "\u257A\u257B\u257C\u257D\u257E\u257F\u2580\u2590"+ + "\u2591\u2592\u2596\u2597\u2598\u2599\u259A\u259B"+ + "\u259C\u259D\u259E\u259F\u25A2\u25A3\u25A4\u25A5"+ + "\u25A6\u25A7\u25A8\u25A9\u25AA\u25AB\u25AC\u25AD"+ + "\u25AE\u25AF\u25B0\u25B1\u25B4\u25B5\u25B6\u25B7"+ + "\u25B8\u25B9\u25BA\u25BB\u25BE\u25BF\u25C0\u25C1"+ + "\u25C2\u25C3\u25C4\u25C5\u25C8\u25C9\u25CA\u25CC"+ + "\u25CD\u25D0\u25D1\u25D2\u25D3\u25D4\u25D5\u25D6"+ + "\u25D7\u25D8\u25D9\u25DA\u25DB\u25DC\u25DD\u25DE"+ + "\u25DF\u25E0\u25E1\u25E6\u25E7\u25E8\u25E9\u25EA"+ + "\u25EB\u25EC\u25ED\u25EE\u25EF\u25F0\u25F1\u25F2"+ + "\u25F3\u25F4\u25F5\u25F6\u25F7\u25F8\u25F9\u25FA"+ + "\u25FB\u25FC\u25FD\u25FE\u25FF\u2600\u2601\u2602"+ + "\u2603\u2604\u2607\u2608\u260A\u260B\u260C\u260D"+ + "\u260E\u260F\u2610\u2611\u2612\u2613\u2614\u2615"+ + "\u2616\u2617\u2618\u2619\u261A\u261B\u261C\u261D"+ + "\u261E\u261F\u2620\u2621\u2622\u2623\u2624\u2625"+ + "\u2626\u2627\u2628\u2629\u262A\u262B\u262C\u262D"+ + "\u262E\u262F\u2630\u2631\u2632\u2633\u2634\u2635"+ + "\u2636\u2637\u2638\u2639\u263A\u263B\u263C\u263D"+ + "\u263E\u263F\u2641\u2643\u2644\u2645\u2646\u2647"+ + "\u2648\u2649\u264A\u264B\u264C\u264D\u264E\u264F"+ + "\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657"+ + "\u2658\u2659\u265A\u265B\u265C\u265D\u265E\u265F"+ + "\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667"+ + "\u2668\u2669\u266A\u266B\u266C\u266D\u266E\u266F"+ + "\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677"+ + "\u2678\u2679\u267A\u267B\u267C\u267D\u267E\u267F"+ + "\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687"+ + "\u2688\u2689\u268A\u268B\u268C\u268D\u268E\u268F"+ + "\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697"+ + "\u2698\u2699\u269A\u269B\u269C\u269D\u269E\u269F"+ + "\u26A0\u26A1\u26A2\u26A3\u26A4\u26A5\u26A6\u26A7"+ + "\u26A8\u26A9\u26AA\u26AB\u26AC\u26AD\u26AE\u26AF"+ + "\u26B0\u26B1\u26B2\u26B3\u26B4\u26B5\u26B6\u26B7"+ + "\u26B8\u26B9\u26BA\u26BB\u26BC\u26BD\u26BE\u26BF"+ + "\u26C0\u26C1\u26C2\u26C3\u26C4\u26C5\u26C6\u26C7"+ + "\u26C8\u26C9\u26CA\u26CB\u26CC\u26CD\u26CE\u26CF"+ + "\u26D0\u26D1\u26D2\u26D3\u26D4\u26D5\u26D6\u26D7"+ + "\u26D8\u26D9\u26DA\u26DB\u26DC\u26DD\u26DE\u26DF"+ + "\u26E0\u26E1\u26E2\u26E3\u26E4\u26E5\u26E6\u26E7"+ + "\u26E8\u26E9\u26EA\u26EB\u26EC\u26ED\u26EE\u26EF"+ + "\u26F0\u26F1\u26F2\u26F3\u26F4\u26F5\u26F6\u26F7"+ + "\u26F8\u26F9\u26FA\u26FB\u26FC\u26FD\u26FE\u26FF"+ + "\u2700\u2701\u2702\u2703\u2704\u2705\u2706\u2707"+ + "\u2708\u2709\u270A\u270B\u270C\u270D\u270E\u270F"+ + "\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717"+ + "\u2718\u2719\u271A\u271B\u271C\u271D\u271E\u271F"+ + "\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727"+ + "\u2728\u2729\u272A\u272B\u272C\u272D\u272E\u272F"+ + "\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737"+ + "\u2738\u2739\u273A\u273B\u273C\u273D\u273E\u273F"+ + "\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747"+ + "\u2748\u2749\u274A\u274B\u274C\u274D\u274E\u274F"+ + "\u2750\u2751\u2752\u2753\u2754\u2755\u2756\u2757"+ + "\u2758\u2759\u275A\u275B\u275C\u275D\u275E\u275F"+ + "\u2760\u2761\u2762\u2763\u2764\u2765\u2766\u2767"+ + "\u2768\u2769\u276A\u276B\u276C\u276D\u276E\u276F"+ + "\u2770\u2771\u2772\u2773\u2774\u2775\u2776\u2777"+ + "\u2778\u2779\u277A\u277B\u277C\u277D\u277E\u277F"+ + "\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787"+ + "\u2788\u2789\u278A\u278B\u278C\u278D\u278E\u278F"+ + "\u2790\u2791\u2792\u2793\u2794\u2795\u2796\u2797"+ + "\u2798\u2799\u279A\u279B\u279C\u279D\u279E\u279F"+ + "\u27A0\u27A1\u27A2\u27A3\u27A4\u27A5\u27A6\u27A7"+ + "\u27A8\u27A9\u27AA\u27AB\u27AC\u27AD\u27AE\u27AF"+ + "\u27B0\u27B1\u27B2\u27B3\u27B4\u27B5\u27B6\u27B7"+ + "\u27B8\u27B9\u27BA\u27BB\u27BC\u27BD\u27BE\u27BF"+ + "\u27C0\u27C1\u27C2\u27C3\u27C4\u27C5\u27C6\u27C7"+ + "\u27C8\u27C9\u27CA\u27CB\u27CC\u27CD\u27CE\u27CF"+ + "\u27D0\u27D1\u27D2\u27D3\u27D4\u27D5\u27D6\u27D7"+ + "\u27D8\u27D9\u27DA\u27DB\u27DC\u27DD\u27DE\u27DF"+ + "\u27E0\u27E1\u27E2\u27E3\u27E4\u27E5\u27E6\u27E7"+ + "\u27E8\u27E9\u27EA\u27EB\u27EC\u27ED\u27EE\u27EF"+ + "\u27F0\u27F1\u27F2\u27F3\u27F4\u27F5\u27F6\u27F7"+ + "\u27F8\u27F9\u27FA\u27FB\u27FC\u27FD\u27FE\u27FF"+ + "\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807"+ + "\u2808\u2809\u280A\u280B\u280C\u280D\u280E\u280F"+ + "\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817"+ + "\u2818\u2819\u281A\u281B\u281C\u281D\u281E\u281F"+ + "\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827"+ + "\u2828\u2829\u282A\u282B\u282C\u282D\u282E\u282F"+ + "\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837"+ + "\u2838\u2839\u283A\u283B\u283C\u283D\u283E\u283F"+ + "\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847"+ + "\u2848\u2849\u284A\u284B\u284C\u284D\u284E\u284F"+ + "\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857"+ + "\u2858\u2859\u285A\u285B\u285C\u285D\u285E\u285F"+ + "\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867"+ + "\u2868\u2869\u286A\u286B\u286C\u286D\u286E\u286F"+ + "\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877"+ + "\u2878\u2879\u287A\u287B\u287C\u287D\u287E\u287F"+ + "\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887"+ + "\u2888\u2889\u288A\u288B\u288C\u288D\u288E\u288F"+ + "\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897"+ + "\u2898\u2899\u289A\u289B\u289C\u289D\u289E\u289F"+ + "\u28A0\u28A1\u28A2\u28A3\u28A4\u28A5\u28A6\u28A7"+ + "\u28A8\u28A9\u28AA\u28AB\u28AC\u28AD\u28AE\u28AF"+ + "\u28B0\u28B1\u28B2\u28B3\u28B4\u28B5\u28B6\u28B7"+ + "\u28B8\u28B9\u28BA\u28BB\u28BC\u28BD\u28BE\u28BF"+ + "\u28C0\u28C1\u28C2\u28C3\u28C4\u28C5\u28C6\u28C7"+ + "\u28C8\u28C9\u28CA\u28CB\u28CC\u28CD\u28CE\u28CF"+ + "\u28D0\u28D1\u28D2\u28D3\u28D4\u28D5\u28D6\u28D7"+ + "\u28D8\u28D9\u28DA\u28DB\u28DC\u28DD\u28DE\u28DF"+ + "\u28E0\u28E1\u28E2\u28E3\u28E4\u28E5\u28E6\u28E7"+ + "\u28E8\u28E9\u28EA\u28EB\u28EC\u28ED\u28EE\u28EF"+ + "\u28F0\u28F1\u28F2\u28F3\u28F4\u28F5\u28F6\u28F7"+ + "\u28F8\u28F9\u28FA\u28FB\u28FC\u28FD\u28FE\u28FF"+ + "\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907"+ + "\u2908\u2909\u290A\u290B\u290C\u290D\u290E\u290F"+ + "\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917"+ + "\u2918\u2919\u291A\u291B\u291C\u291D\u291E\u291F"+ + "\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927"+ + "\u2928\u2929\u292A\u292B\u292C\u292D\u292E\u292F"+ + "\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937"+ + "\u2938\u2939\u293A\u293B\u293C\u293D\u293E\u293F"+ + "\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947"+ + "\u2948\u2949\u294A\u294B\u294C\u294D\u294E\u294F"+ + "\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957"+ + "\u2958\u2959\u295A\u295B\u295C\u295D\u295E\u295F"+ + "\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967"+ + "\u2968\u2969\u296A\u296B\u296C\u296D\u296E\u296F"+ + "\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977"+ + "\u2978\u2979\u297A\u297B\u297C\u297D\u297E\u297F"+ + "\u2980\u2981\u2982\u2983\u2984\u2985\u2986\u2987"+ + "\u2988\u2989\u298A\u298B\u298C\u298D\u298E\u298F"+ + "\u2990\u2991\u2992\u2993\u2994\u2995\u2996\u2997"+ + "\u2998\u2999\u299A\u299B\u299C\u299D\u299E\u299F"+ + "\u29A0\u29A1\u29A2\u29A3\u29A4\u29A5\u29A6\u29A7"+ + "\u29A8\u29A9\u29AA\u29AB\u29AC\u29AD\u29AE\u29AF"+ + "\u29B0\u29B1\u29B2\u29B3\u29B4\u29B5\u29B6\u29B7"+ + "\u29B8\u29B9\u29BA\u29BB\u29BC\u29BD\u29BE\u29BF"+ + "\u29C0\u29C1\u29C2\u29C3\u29C4\u29C5\u29C6\u29C7"+ + "\u29C8\u29C9\u29CA\u29CB\u29CC\u29CD\u29CE\u29CF"+ + "\u29D0\u29D1\u29D2\u29D3\u29D4\u29D5\u29D6\u29D7"+ + "\u29D8\u29D9\u29DA\u29DB\u29DC\u29DD\u29DE\u29DF"+ + "\u29E0\u29E1\u29E2\u29E3\u29E4\u29E5\u29E6\u29E7"+ + "\u29E8\u29E9\u29EA\u29EB\u29EC\u29ED\u29EE\u29EF"+ + "\u29F0\u29F1\u29F2\u29F3\u29F4\u29F5\u29F6\u29F7"+ + "\u29F8\u29F9\u29FA\u29FB\u29FC\u29FD\u29FE\u29FF"+ + "\u2A00\u2A01\u2A02\u2A03\u2A04\u2A05\u2A06\u2A07"+ + "\u2A08\u2A09\u2A0A\u2A0B\u2A0C\u2A0D\u2A0E\u2A0F"+ + "\u2A10\u2A11\u2A12\u2A13\u2A14\u2A15\u2A16\u2A17"+ + "\u2A18\u2A19\u2A1A\u2A1B\u2A1C\u2A1D\u2A1E\u2A1F"+ + "\u2A20\u2A21\u2A22\u2A23\u2A24\u2A25\u2A26\u2A27"+ + "\u2A28\u2A29\u2A2A\u2A2B\u2A2C\u2A2D\u2A2E\u2A2F"+ + "\u2A30\u2A31\u2A32\u2A33\u2A34\u2A35\u2A36\u2A37"+ + "\u2A38\u2A39\u2A3A\u2A3B\u2A3C\u2A3D\u2A3E\u2A3F"+ + "\u2A40\u2A41\u2A42\u2A43\u2A44\u2A45\u2A46\u2A47"+ + "\u2A48\u2A49\u2A4A\u2A4B\u2A4C\u2A4D\u2A4E\u2A4F"+ + "\u2A50\u2A51\u2A52\u2A53\u2A54\u2A55\u2A56\u2A57"+ + "\u2A58\u2A59\u2A5A\u2A5B\u2A5C\u2A5D\u2A5E\u2A5F"+ + "\u2A60\u2A61\u2A62\u2A63\u2A64\u2A65\u2A66\u2A67"+ + "\u2A68\u2A69\u2A6A\u2A6B\u2A6C\u2A6D\u2A6E\u2A6F"+ + "\u2A70\u2A71\u2A72\u2A73\u2A74\u2A75\u2A76\u2A77"+ + "\u2A78\u2A79\u2A7A\u2A7B\u2A7C\u2A7D\u2A7E\u2A7F"+ + "\u2A80\u2A81\u2A82\u2A83\u2A84\u2A85\u2A86\u2A87"+ + "\u2A88\u2A89\u2A8A\u2A8B\u2A8C\u2A8D\u2A8E\u2A8F"+ + "\u2A90\u2A91\u2A92\u2A93\u2A94\u2A95\u2A96\u2A97"+ + "\u2A98\u2A99\u2A9A\u2A9B\u2A9C\u2A9D\u2A9E\u2A9F"+ + "\u2AA0\u2AA1\u2AA2\u2AA3\u2AA4\u2AA5\u2AA6\u2AA7"+ + "\u2AA8\u2AA9\u2AAA\u2AAB\u2AAC\u2AAD\u2AAE\u2AAF"+ + "\u2AB0\u2AB1\u2AB2\u2AB3\u2AB4\u2AB5\u2AB6\u2AB7"+ + "\u2AB8\u2AB9\u2ABA\u2ABB\u2ABC\u2ABD\u2ABE\u2ABF"+ + "\u2AC0\u2AC1\u2AC2\u2AC3\u2AC4\u2AC5\u2AC6\u2AC7"+ + "\u2AC8\u2AC9\u2ACA\u2ACB\u2ACC\u2ACD\u2ACE\u2ACF"+ + "\u2AD0\u2AD1\u2AD2\u2AD3\u2AD4\u2AD5\u2AD6\u2AD7"+ + "\u2AD8\u2AD9\u2ADA\u2ADB\u2ADC\u2ADD\u2ADE\u2ADF"+ + "\u2AE0\u2AE1\u2AE2\u2AE3\u2AE4\u2AE5\u2AE6\u2AE7"+ + "\u2AE8\u2AE9\u2AEA\u2AEB\u2AEC\u2AED\u2AEE\u2AEF"+ + "\u2AF0\u2AF1\u2AF2\u2AF3\u2AF4\u2AF5\u2AF6\u2AF7"+ + "\u2AF8\u2AF9\u2AFA\u2AFB\u2AFC\u2AFD\u2AFE\u2AFF"+ + "\u2B00\u2B01\u2B02\u2B03\u2B04\u2B05\u2B06\u2B07"+ + "\u2B08\u2B09\u2B0A\u2B0B\u2B0C\u2B0D\u2B0E\u2B0F"+ + "\u2B10\u2B11\u2B12\u2B13\u2B14\u2B15\u2B16\u2B17"+ + "\u2B18\u2B19\u2B1A\u2B1B\u2B1C\u2B1D\u2B1E\u2B1F"+ + "\u2B20\u2B21\u2B22\u2B23\u2B24\u2B25\u2B26\u2B27"+ + "\u2B28\u2B29\u2B2A\u2B2B\u2B2C\u2B2D\u2B2E\u2B2F"+ + "\u2B30\u2B31\u2B32\u2B33\u2B34\u2B35\u2B36\u2B37"+ + "\u2B38\u2B39\u2B3A\u2B3B\u2B3C\u2B3D\u2B3E\u2B3F"+ + "\u2B40\u2B41\u2B42\u2B43\u2B44\u2B45\u2B46\u2B47"+ + "\u2B48\u2B49\u2B4A\u2B4B\u2B4C\u2B4D\u2B4E\u2B4F"+ + "\u2B50\u2B51\u2B52\u2B53\u2B54\u2B55\u2B56\u2B57"+ + "\u2B58\u2B59\u2B5A\u2B5B\u2B5C\u2B5D\u2B5E\u2B5F"+ + "\u2B60\u2B61\u2B62\u2B63\u2B64\u2B65\u2B66\u2B67"+ + "\u2B68\u2B69\u2B6A\u2B6B\u2B6C\u2B6D\u2B6E\u2B6F"+ + "\u2B70\u2B71\u2B72\u2B73\u2B74\u2B75\u2B76\u2B77"+ + "\u2B78\u2B79\u2B7A\u2B7B\u2B7C\u2B7D\u2B7E\u2B7F"+ + "\u2B80\u2B81\u2B82\u2B83\u2B84\u2B85\u2B86\u2B87"+ + "\u2B88\u2B89\u2B8A\u2B8B\u2B8C\u2B8D\u2B8E\u2B8F"+ + "\u2B90\u2B91\u2B92\u2B93\u2B94\u2B95\u2B96\u2B97"+ + "\u2B98\u2B99\u2B9A\u2B9B\u2B9C\u2B9D\u2B9E\u2B9F"+ + "\u2BA0\u2BA1\u2BA2\u2BA3\u2BA4\u2BA5\u2BA6\u2BA7"+ + "\u2BA8\u2BA9\u2BAA\u2BAB\u2BAC\u2BAD\u2BAE\u2BAF"+ + "\u2BB0\u2BB1\u2BB2\u2BB3\u2BB4\u2BB5\u2BB6\u2BB7"+ + "\u2BB8\u2BB9\u2BBA\u2BBB\u2BBC\u2BBD\u2BBE\u2BBF"+ + "\u2BC0\u2BC1\u2BC2\u2BC3\u2BC4\u2BC5\u2BC6\u2BC7"+ + "\u2BC8\u2BC9\u2BCA\u2BCB\u2BCC\u2BCD\u2BCE\u2BCF"+ + "\u2BD0\u2BD1\u2BD2\u2BD3\u2BD4\u2BD5\u2BD6\u2BD7"+ + "\u2BD8\u2BD9\u2BDA\u2BDB\u2BDC\u2BDD\u2BDE\u2BDF"+ + "\u2BE0\u2BE1\u2BE2\u2BE3\u2BE4\u2BE5\u2BE6\u2BE7"+ + "\u2BE8\u2BE9\u2BEA\u2BEB\u2BEC\u2BED\u2BEE\u2BEF"+ + "\u2BF0\u2BF1\u2BF2\u2BF3\u2BF4\u2BF5\u2BF6\u2BF7"+ + "\u2BF8\u2BF9\u2BFA\u2BFB\u2BFC\u2BFD\u2BFE\u2BFF"+ + "\u2C00\u2C01\u2C02\u2C03\u2C04\u2C05\u2C06\u2C07"+ + "\u2C08\u2C09\u2C0A\u2C0B\u2C0C\u2C0D\u2C0E\u2C0F"+ + "\u2C10\u2C11\u2C12\u2C13\u2C14\u2C15\u2C16\u2C17"+ + "\u2C18\u2C19\u2C1A\u2C1B\u2C1C\u2C1D\u2C1E\u2C1F"+ + "\u2C20\u2C21\u2C22\u2C23\u2C24\u2C25\u2C26\u2C27"+ + "\u2C28\u2C29\u2C2A\u2C2B\u2C2C\u2C2D\u2C2E\u2C2F"+ + "\u2C30\u2C31\u2C32\u2C33\u2C34\u2C35\u2C36\u2C37"+ + "\u2C38\u2C39\u2C3A\u2C3B\u2C3C\u2C3D\u2C3E\u2C3F"+ + "\u2C40\u2C41\u2C42\u2C43\u2C44\u2C45\u2C46\u2C47"+ + "\u2C48\u2C49\u2C4A\u2C4B\u2C4C\u2C4D\u2C4E\u2C4F"+ + "\u2C50\u2C51\u2C52\u2C53\u2C54\u2C55\u2C56\u2C57"+ + "\u2C58\u2C59\u2C5A\u2C5B\u2C5C\u2C5D\u2C5E\u2C5F"+ + "\u2C60\u2C61\u2C62\u2C63\u2C64\u2C65\u2C66\u2C67"+ + "\u2C68\u2C69\u2C6A\u2C6B\u2C6C\u2C6D\u2C6E\u2C6F"+ + "\u2C70\u2C71\u2C72\u2C73\u2C74\u2C75\u2C76\u2C77"+ + "\u2C78\u2C79\u2C7A\u2C7B\u2C7C\u2C7D\u2C7E\u2C7F"+ + "\u2C80\u2C81\u2C82\u2C83\u2C84\u2C85\u2C86\u2C87"+ + "\u2C88\u2C89\u2C8A\u2C8B\u2C8C\u2C8D\u2C8E\u2C8F"+ + "\u2C90\u2C91\u2C92\u2C93\u2C94\u2C95\u2C96\u2C97"+ + "\u2C98\u2C99\u2C9A\u2C9B\u2C9C\u2C9D\u2C9E\u2C9F"+ + "\u2CA0\u2CA1\u2CA2\u2CA3\u2CA4\u2CA5\u2CA6\u2CA7"+ + "\u2CA8\u2CA9\u2CAA\u2CAB\u2CAC\u2CAD\u2CAE\u2CAF"+ + "\u2CB0\u2CB1\u2CB2\u2CB3\u2CB4\u2CB5\u2CB6\u2CB7"+ + "\u2CB8\u2CB9\u2CBA\u2CBB\u2CBC\u2CBD\u2CBE\u2CBF"+ + "\u2CC0\u2CC1\u2CC2\u2CC3\u2CC4\u2CC5\u2CC6\u2CC7"+ + "\u2CC8\u2CC9\u2CCA\u2CCB\u2CCC\u2CCD\u2CCE\u2CCF"+ + "\u2CD0\u2CD1\u2CD2\u2CD3\u2CD4\u2CD5\u2CD6\u2CD7"+ + "\u2CD8\u2CD9\u2CDA\u2CDB\u2CDC\u2CDD\u2CDE\u2CDF"+ + "\u2CE0\u2CE1\u2CE2\u2CE3\u2CE4\u2CE5\u2CE6\u2CE7"+ + "\u2CE8\u2CE9\u2CEA\u2CEB\u2CEC\u2CED\u2CEE\u2CEF"+ + "\u2CF0\u2CF1\u2CF2\u2CF3\u2CF4\u2CF5\u2CF6\u2CF7"+ + "\u2CF8\u2CF9\u2CFA\u2CFB\u2CFC\u2CFD\u2CFE\u2CFF"+ + "\u2D00\u2D01\u2D02\u2D03\u2D04\u2D05\u2D06\u2D07"+ + "\u2D08\u2D09\u2D0A\u2D0B\u2D0C\u2D0D\u2D0E\u2D0F"+ + "\u2D10\u2D11\u2D12\u2D13\u2D14\u2D15\u2D16\u2D17"+ + "\u2D18\u2D19\u2D1A\u2D1B\u2D1C\u2D1D\u2D1E\u2D1F"+ + "\u2D20\u2D21\u2D22\u2D23\u2D24\u2D25\u2D26\u2D27"+ + "\u2D28\u2D29\u2D2A\u2D2B\u2D2C\u2D2D\u2D2E\u2D2F"+ + "\u2D30\u2D31\u2D32\u2D33\u2D34\u2D35\u2D36\u2D37"+ + "\u2D38\u2D39\u2D3A\u2D3B\u2D3C\u2D3D\u2D3E\u2D3F"+ + "\u2D40\u2D41\u2D42\u2D43\u2D44\u2D45\u2D46\u2D47"+ + "\u2D48\u2D49\u2D4A\u2D4B\u2D4C\u2D4D\u2D4E\u2D4F"+ + "\u2D50\u2D51\u2D52\u2D53\u2D54\u2D55\u2D56\u2D57"+ + "\u2D58\u2D59\u2D5A\u2D5B\u2D5C\u2D5D\u2D5E\u2D5F"+ + "\u2D60\u2D61\u2D62\u2D63\u2D64\u2D65\u2D66\u2D67"+ + "\u2D68\u2D69\u2D6A\u2D6B\u2D6C\u2D6D\u2D6E\u2D6F"+ + "\u2D70\u2D71\u2D72\u2D73\u2D74\u2D75\u2D76\u2D77"+ + "\u2D78\u2D79\u2D7A\u2D7B\u2D7C\u2D7D\u2D7E\u2D7F"+ + "\u2D80\u2D81\u2D82\u2D83\u2D84\u2D85\u2D86\u2D87"+ + "\u2D88\u2D89\u2D8A\u2D8B\u2D8C\u2D8D\u2D8E\u2D8F"+ + "\u2D90\u2D91\u2D92\u2D93\u2D94\u2D95\u2D96\u2D97"+ + "\u2D98\u2D99\u2D9A\u2D9B\u2D9C\u2D9D\u2D9E\u2D9F"+ + "\u2DA0\u2DA1\u2DA2\u2DA3\u2DA4\u2DA5\u2DA6\u2DA7"+ + "\u2DA8\u2DA9\u2DAA\u2DAB\u2DAC\u2DAD\u2DAE\u2DAF"+ + "\u2DB0\u2DB1\u2DB2\u2DB3\u2DB4\u2DB5\u2DB6\u2DB7"+ + "\u2DB8\u2DB9\u2DBA\u2DBB\u2DBC\u2DBD\u2DBE\u2DBF"+ + "\u2DC0\u2DC1\u2DC2\u2DC3\u2DC4\u2DC5\u2DC6\u2DC7"+ + "\u2DC8\u2DC9\u2DCA\u2DCB\u2DCC\u2DCD\u2DCE\u2DCF"+ + "\u2DD0\u2DD1\u2DD2\u2DD3\u2DD4\u2DD5\u2DD6\u2DD7"+ + "\u2DD8\u2DD9\u2DDA\u2DDB\u2DDC\u2DDD\u2DDE\u2DDF"+ + "\u2DE0\u2DE1\u2DE2\u2DE3\u2DE4\u2DE5\u2DE6\u2DE7"+ + "\u2DE8\u2DE9\u2DEA\u2DEB\u2DEC\u2DED\u2DEE\u2DEF"+ + "\u2DF0\u2DF1\u2DF2\u2DF3\u2DF4\u2DF5\u2DF6\u2DF7"+ + "\u2DF8\u2DF9\u2DFA\u2DFB\u2DFC\u2DFD\u2DFE\u2DFF"+ + "\u2E00\u2E01\u2E02\u2E03\u2E04\u2E05\u2E06\u2E07"+ + "\u2E08\u2E09\u2E0A\u2E0B\u2E0C\u2E0D\u2E0E\u2E0F"+ + "\u2E10\u2E11\u2E12\u2E13\u2E14\u2E15\u2E16\u2E17"+ + "\u2E18\u2E19\u2E1A\u2E1B\u2E1C\u2E1D\u2E1E\u2E1F"+ + "\u2E20\u2E21\u2E22\u2E23\u2E24\u2E25\u2E26\u2E27"+ + "\u2E28\u2E29\u2E2A\u2E2B\u2E2C\u2E2D\u2E2E\u2E2F"+ + "\u2E30\u2E31\u2E32\u2E33\u2E34\u2E35\u2E36\u2E37"+ + "\u2E38\u2E39\u2E3A\u2E3B\u2E3C\u2E3D\u2E3E\u2E3F"+ + "\u2E40\u2E41\u2E42\u2E43\u2E44\u2E45\u2E46\u2E47"+ + "\u2E48\u2E49\u2E4A\u2E4B\u2E4C\u2E4D\u2E4E\u2E4F"+ + "\u2E50\u2E51\u2E52\u2E53\u2E54\u2E55\u2E56\u2E57"+ + "\u2E58\u2E59\u2E5A\u2E5B\u2E5C\u2E5D\u2E5E\u2E5F"+ + "\u2E60\u2E61\u2E62\u2E63\u2E64\u2E65\u2E66\u2E67"+ + "\u2E68\u2E69\u2E6A\u2E6B\u2E6C\u2E6D\u2E6E\u2E6F"+ + "\u2E70\u2E71\u2E72\u2E73\u2E74\u2E75\u2E76\u2E77"+ + "\u2E78\u2E79\u2E7A\u2E7B\u2E7C\u2E7D\u2E7E\u2E7F"+ + "\u2E80\u2E82\u2E83\u2E85\u2E86\u2E87\u2E89\u2E8A"+ + "\u2E8D\u2E8E\u2E8F\u2E90\u2E91\u2E92\u2E93\u2E94"+ + "\u2E95\u2E96\u2E98\u2E99\u2E9A\u2E9B\u2E9C\u2E9D"+ + "\u2E9E\u2E9F\u2EA0\u2EA1\u2EA2\u2EA3\u2EA4\u2EA5"+ + "\u2EA6\u2EA8\u2EA9\u2EAB\u2EAC\u2EAD\u2EAF\u2EB0"+ + "\u2EB1\u2EB2\u2EB4\u2EB5\u2EB8\u2EB9\u2EBA\u2EBC"+ + "\u2EBD\u2EBE\u2EBF\u2EC0\u2EC1\u2EC2\u2EC3\u2EC4"+ + "\u2EC5\u2EC6\u2EC7\u2EC8\u2EC9\u2ECB\u2ECC\u2ECD"+ + "\u2ECE\u2ECF\u2ED0\u2ED1\u2ED2\u2ED3\u2ED4\u2ED5"+ + "\u2ED6\u2ED7\u2ED8\u2ED9\u2EDA\u2EDB\u2EDC\u2EDD"+ + "\u2EDE\u2EDF\u2EE0\u2EE1\u2EE2\u2EE3\u2EE4\u2EE5"+ + "\u2EE6\u2EE7\u2EE8\u2EE9\u2EEA\u2EEB\u2EEC\u2EED"+ + "\u2EEE\u2EEF\u2EF0\u2EF1\u2EF2\u2EF3\u2EF4\u2EF5"+ + "\u2EF6\u2EF7\u2EF8\u2EF9\u2EFA\u2EFB\u2EFC\u2EFD"+ + "\u2EFE\u2EFF\u2F00\u2F01\u2F02\u2F03\u2F04\u2F05"+ + "\u2F06\u2F07\u2F08\u2F09\u2F0A\u2F0B\u2F0C\u2F0D"+ + "\u2F0E\u2F0F\u2F10\u2F11\u2F12\u2F13\u2F14\u2F15"+ + "\u2F16\u2F17\u2F18\u2F19\u2F1A\u2F1B\u2F1C\u2F1D"+ + "\u2F1E\u2F1F\u2F20\u2F21\u2F22\u2F23\u2F24\u2F25"+ + "\u2F26\u2F27\u2F28\u2F29\u2F2A\u2F2B\u2F2C\u2F2D"+ + "\u2F2E\u2F2F\u2F30\u2F31\u2F32\u2F33\u2F34\u2F35"+ + "\u2F36\u2F37\u2F38\u2F39\u2F3A\u2F3B\u2F3C\u2F3D"+ + "\u2F3E\u2F3F\u2F40\u2F41\u2F42\u2F43\u2F44\u2F45"+ + "\u2F46\u2F47\u2F48\u2F49\u2F4A\u2F4B\u2F4C\u2F4D"+ + "\u2F4E\u2F4F\u2F50\u2F51\u2F52\u2F53\u2F54\u2F55"+ + "\u2F56\u2F57\u2F58\u2F59\u2F5A\u2F5B\u2F5C\u2F5D"+ + "\u2F5E\u2F5F\u2F60\u2F61\u2F62\u2F63\u2F64\u2F65"+ + "\u2F66\u2F67\u2F68\u2F69\u2F6A\u2F6B\u2F6C\u2F6D"+ + "\u2F6E\u2F6F\u2F70\u2F71\u2F72\u2F73\u2F74\u2F75"+ + "\u2F76\u2F77\u2F78\u2F79\u2F7A\u2F7B\u2F7C\u2F7D"+ + "\u2F7E\u2F7F\u2F80\u2F81\u2F82\u2F83\u2F84\u2F85"+ + "\u2F86\u2F87\u2F88\u2F89\u2F8A\u2F8B\u2F8C\u2F8D"+ + "\u2F8E\u2F8F\u2F90\u2F91\u2F92\u2F93\u2F94\u2F95"+ + "\u2F96\u2F97\u2F98\u2F99\u2F9A\u2F9B\u2F9C\u2F9D"+ + "\u2F9E\u2F9F\u2FA0\u2FA1\u2FA2\u2FA3\u2FA4\u2FA5"+ + "\u2FA6\u2FA7\u2FA8\u2FA9\u2FAA\u2FAB\u2FAC\u2FAD"+ + "\u2FAE\u2FAF\u2FB0\u2FB1\u2FB2\u2FB3\u2FB4\u2FB5"+ + "\u2FB6\u2FB7\u2FB8\u2FB9\u2FBA\u2FBB\u2FBC\u2FBD"+ + "\u2FBE\u2FBF\u2FC0\u2FC1\u2FC2\u2FC3\u2FC4\u2FC5"+ + "\u2FC6\u2FC7\u2FC8\u2FC9\u2FCA\u2FCB\u2FCC\u2FCD"+ + "\u2FCE\u2FCF\u2FD0\u2FD1\u2FD2\u2FD3\u2FD4\u2FD5"+ + "\u2FD6\u2FD7\u2FD8\u2FD9\u2FDA\u2FDB\u2FDC\u2FDD"+ + "\u2FDE\u2FDF\u2FE0\u2FE1\u2FE2\u2FE3\u2FE4\u2FE5"+ + "\u2FE6\u2FE7\u2FE8\u2FE9\u2FEA\u2FEB\u2FEC\u2FED"+ + "\u2FEE\u2FEF\u2FFC\u2FFD\u2FFE\u2FFF\u3004\u3018"+ + "\u3019\u301A\u301B\u301C\u301F\u3020\u302A\u302B"+ + "\u302C\u302D\u302E\u302F\u3030\u3031\u3032\u3033"+ + "\u3034\u3035\u3036\u3037\u3038\u3039\u303A\u303B"+ + "\u303C\u303D\u303F\u3040\u3094\u3095\u3096\u3097"+ + "\u3098\u3099\u309A\u309F\u30A0\u30F7\u30F8\u30F9"+ + "\u30FA\u30FB\u30FF\u3100\u3101\u3102\u3103\u3104"+ + "\u312A\u312B\u312C\u312D\u312E\u312F\u3130\u3131"+ + "\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139"+ + "\u313A\u313B\u313C\u313D\u313E\u313F\u3140\u3141"+ + "\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149"+ + "\u314A\u314B\u314C\u314D\u314E\u314F\u3150\u3151"+ + "\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159"+ + "\u315A\u315B\u315C\u315D\u315E\u315F\u3160\u3161"+ + "\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169"+ + "\u316A\u316B\u316C\u316D\u316E\u316F\u3170\u3171"+ + "\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179"+ + "\u317A\u317B\u317C\u317D\u317E\u317F\u3180\u3181"+ + "\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189"+ + "\u318A\u318B\u318C\u318D\u318E\u318F\u3190\u3191"+ + "\u3192\u3193\u3194\u3195\u3196\u3197\u3198\u3199"+ + "\u319A\u319B\u319C\u319D\u319E\u319F\u31A0\u31A1"+ + "\u31A2\u31A3\u31A4\u31A5\u31A6\u31A7\u31A8\u31A9"+ + "\u31AA\u31AB\u31AC\u31AD\u31AE\u31AF\u31B0\u31B1"+ + "\u31B2\u31B3\u31B4\u31B5\u31B6\u31B7\u31B8\u31B9"+ + "\u31BA\u31BB\u31BC\u31BD\u31BE\u31BF\u31C0\u31C1"+ + "\u31C2\u31C3\u31C4\u31C5\u31C6\u31C7\u31C8\u31C9"+ + "\u31CA\u31CB\u31CC\u31CD\u31CE\u31CF\u31D0\u31D1"+ + "\u31D2\u31D3\u31D4\u31D5\u31D6\u31D7\u31D8\u31D9"+ + "\u31DA\u31DB\u31DC\u31DD\u31DE\u31DF\u31E0\u31E1"+ + "\u31E2\u31E3\u31E4\u31E5\u31E6\u31E7\u31E8\u31E9"+ + "\u31EA\u31EB\u31EC\u31ED\u31EE\u31EF\u31F0\u31F1"+ + "\u31F2\u31F3\u31F4\u31F5\u31F6\u31F7\u31F8\u31F9"+ + "\u31FA\u31FB\u31FC\u31FD\u31FE\u31FF\u3200\u3201"+ + "\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209"+ + "\u320A\u320B\u320C\u320D\u320E\u320F\u3210\u3211"+ + "\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219"+ + "\u321A\u321B\u321C\u321D\u321E\u321F\u322A\u322B"+ + "\u322C\u322D\u322E\u322F\u3230\u3232\u3233\u3234"+ + "\u3235\u3236\u3237\u3238\u3239\u323A\u323B\u323C"+ + "\u323D\u323E\u323F\u3240\u3241\u3242\u3243\u3244"+ + "\u3245\u3246\u3247\u3248\u3249\u324A\u324B\u324C"+ + "\u324D\u324E\u324F\u3250\u3251\u3252\u3253\u3254"+ + "\u3255\u3256\u3257\u3258\u3259\u325A\u325B\u325C"+ + "\u325D\u325E\u325F\u3260\u3261\u3262\u3263\u3264"+ + "\u3265\u3266\u3267\u3268\u3269\u326A\u326B\u326C"+ + "\u326D\u326E\u326F\u3270\u3271\u3272\u3273\u3274"+ + "\u3275\u3276\u3277\u3278\u3279\u327A\u327B\u327C"+ + "\u327D\u327E\u327F\u3280\u3281\u3282\u3283\u3284"+ + "\u3285\u3286\u3287\u3288\u3289\u328A\u328B\u328C"+ + "\u328D\u328E\u328F\u3290\u3291\u3292\u3293\u3294"+ + "\u3295\u3296\u3297\u3298\u3299\u329A\u329B\u329C"+ + "\u329D\u329E\u329F\u32A0\u32A1\u32A2\u32A4\u32A5"+ + "\u32A6\u32A7\u32A8\u32A9\u32AA\u32AB\u32AC\u32AD"+ + "\u32AE\u32AF\u32B0\u32B1\u32B2\u32B3\u32B4\u32B5"+ + "\u32B6\u32B7\u32B8\u32B9\u32BA\u32BB\u32BC\u32BD"+ + "\u32BE\u32BF\u32C0\u32C1\u32C2\u32C3\u32C4\u32C5"+ + "\u32C6\u32C7\u32C8\u32C9\u32CA\u32CB\u32CC\u32CD"+ + "\u32CE\u32CF\u32D0\u32D1\u32D2\u32D3\u32D4\u32D5"+ + "\u32D6\u32D7\u32D8\u32D9\u32DA\u32DB\u32DC\u32DD"+ + "\u32DE\u32DF\u32E0\u32E1\u32E2\u32E3\u32E4\u32E5"+ + "\u32E6\u32E7\u32E8\u32E9\u32EA\u32EB\u32EC\u32ED"+ + "\u32EE\u32EF\u32F0\u32F1\u32F2\u32F3\u32F4\u32F5"+ + "\u32F6\u32F7\u32F8\u32F9\u32FA\u32FB\u32FC\u32FD"+ + "\u32FE\u32FF\u3300\u3301\u3302\u3303\u3304\u3305"+ + "\u3306\u3307\u3308\u3309\u330A\u330B\u330C\u330D"+ + "\u330E\u330F\u3310\u3311\u3312\u3313\u3314\u3315"+ + "\u3316\u3317\u3318\u3319\u331A\u331B\u331C\u331D"+ + "\u331E\u331F\u3320\u3321\u3322\u3323\u3324\u3325"+ + "\u3326\u3327\u3328\u3329\u332A\u332B\u332C\u332D"+ + "\u332E\u332F\u3330\u3331\u3332\u3333\u3334\u3335"+ + "\u3336\u3337\u3338\u3339\u333A\u333B\u333C\u333D"+ + "\u333E\u333F\u3340\u3341\u3342\u3343\u3344\u3345"+ + "\u3346\u3347\u3348\u3349\u334A\u334B\u334C\u334D"+ + "\u334E\u334F\u3350\u3351\u3352\u3353\u3354\u3355"+ + "\u3356\u3357\u3358\u3359\u335A\u335B\u335C\u335D"; + + private final static String innerDecoderIndex3= + "\u335E\u335F\u3360\u3361\u3362\u3363\u3364\u3365"+ + "\u3366\u3367\u3368\u3369\u336A\u336B\u336C\u336D"+ + "\u336E\u336F\u3370\u3371\u3372\u3373\u3374\u3375"+ + "\u3376\u3377\u3378\u3379\u337A\u337B\u337C\u337D"+ + "\u337E\u337F\u3380\u3381\u3382\u3383\u3384\u3385"+ + "\u3386\u3387\u3388\u3389\u338A\u338B\u338C\u338D"+ + "\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397"+ + "\u3398\u3399\u339A\u339B\u339F\u33A0\u33A2\u33A3"+ + "\u33A4\u33A5\u33A6\u33A7\u33A8\u33A9\u33AA\u33AB"+ + "\u33AC\u33AD\u33AE\u33AF\u33B0\u33B1\u33B2\u33B3"+ + "\u33B4\u33B5\u33B6\u33B7\u33B8\u33B9\u33BA\u33BB"+ + "\u33BC\u33BD\u33BE\u33BF\u33C0\u33C1\u33C2\u33C3"+ + "\u33C5\u33C6\u33C7\u33C8\u33C9\u33CA\u33CB\u33CC"+ + "\u33CD\u33CF\u33D0\u33D3\u33D4\u33D6\u33D7\u33D8"+ + "\u33D9\u33DA\u33DB\u33DC\u33DD\u33DE\u33DF\u33E0"+ + "\u33E1\u33E2\u33E3\u33E4\u33E5\u33E6\u33E7\u33E8"+ + "\u33E9\u33EA\u33EB\u33EC\u33ED\u33EE\u33EF\u33F0"+ + "\u33F1\u33F2\u33F3\u33F4\u33F5\u33F6\u33F7\u33F8"+ + "\u33F9\u33FA\u33FB\u33FC\u33FD\u33FE\u33FF\u3400"+ + "\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408"+ + "\u3409\u340A\u340B\u340C\u340D\u340E\u340F\u3410"+ + "\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418"+ + "\u3419\u341A\u341B\u341C\u341D\u341E\u341F\u3420"+ + "\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428"+ + "\u3429\u342A\u342B\u342C\u342D\u342E\u342F\u3430"+ + "\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438"+ + "\u3439\u343A\u343B\u343C\u343D\u343E\u343F\u3440"+ + "\u3441\u3442\u3443\u3444\u3445\u3446\u3448\u3449"+ + "\u344A\u344B\u344C\u344D\u344E\u344F\u3450\u3451"+ + "\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459"+ + "\u345A\u345B\u345C\u345D\u345E\u345F\u3460\u3461"+ + "\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469"+ + "\u346A\u346B\u346C\u346D\u346E\u346F\u3470\u3471"+ + "\u3472\u3474\u3475\u3476\u3477\u3478\u3479\u347A"+ + "\u347B\u347C\u347D\u347E\u347F\u3480\u3481\u3482"+ + "\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348A"+ + "\u348B\u348C\u348D\u348E\u348F\u3490\u3491\u3492"+ + "\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349A"+ + "\u349B\u349C\u349D\u349E\u349F\u34A0\u34A1\u34A2"+ + "\u34A3\u34A4\u34A5\u34A6\u34A7\u34A8\u34A9\u34AA"+ + "\u34AB\u34AC\u34AD\u34AE\u34AF\u34B0\u34B1\u34B2"+ + "\u34B3\u34B4\u34B5\u34B6\u34B7\u34B8\u34B9\u34BA"+ + "\u34BB\u34BC\u34BD\u34BE\u34BF\u34C0\u34C1\u34C2"+ + "\u34C3\u34C4\u34C5\u34C6\u34C7\u34C8\u34C9\u34CA"+ + "\u34CB\u34CC\u34CD\u34CE\u34CF\u34D0\u34D1\u34D2"+ + "\u34D3\u34D4\u34D5\u34D6\u34D7\u34D8\u34D9\u34DA"+ + "\u34DB\u34DC\u34DD\u34DE\u34DF\u34E0\u34E1\u34E2"+ + "\u34E3\u34E4\u34E5\u34E6\u34E7\u34E8\u34E9\u34EA"+ + "\u34EB\u34EC\u34ED\u34EE\u34EF\u34F0\u34F1\u34F2"+ + "\u34F3\u34F4\u34F5\u34F6\u34F7\u34F8\u34F9\u34FA"+ + "\u34FB\u34FC\u34FD\u34FE\u34FF\u3500\u3501\u3502"+ + "\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350A"+ + "\u350B\u350C\u350D\u350E\u350F\u3510\u3511\u3512"+ + "\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351A"+ + "\u351B\u351C\u351D\u351E\u351F\u3520\u3521\u3522"+ + "\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352A"+ + "\u352B\u352C\u352D\u352E\u352F\u3530\u3531\u3532"+ + "\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353A"+ + "\u353B\u353C\u353D\u353E\u353F\u3540\u3541\u3542"+ + "\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354A"+ + "\u354B\u354C\u354D\u354E\u354F\u3550\u3551\u3552"+ + "\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355A"+ + "\u355B\u355C\u355D\u355E\u355F\u3560\u3561\u3562"+ + "\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356A"+ + "\u356B\u356C\u356D\u356E\u356F\u3570\u3571\u3572"+ + "\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357A"+ + "\u357B\u357C\u357D\u357E\u357F\u3580\u3581\u3582"+ + "\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358A"+ + "\u358B\u358C\u358D\u358E\u358F\u3590\u3591\u3592"+ + "\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359A"+ + "\u359B\u359C\u359D\u359F\u35A0\u35A1\u35A2\u35A3"+ + "\u35A4\u35A5\u35A6\u35A7\u35A8\u35A9\u35AA\u35AB"+ + "\u35AC\u35AD\u35AE\u35AF\u35B0\u35B1\u35B2\u35B3"+ + "\u35B4\u35B5\u35B6\u35B7\u35B8\u35B9\u35BA\u35BB"+ + "\u35BC\u35BD\u35BE\u35BF\u35C0\u35C1\u35C2\u35C3"+ + "\u35C4\u35C5\u35C6\u35C7\u35C8\u35C9\u35CA\u35CB"+ + "\u35CC\u35CD\u35CE\u35CF\u35D0\u35D1\u35D2\u35D3"+ + "\u35D4\u35D5\u35D6\u35D7\u35D8\u35D9\u35DA\u35DB"+ + "\u35DC\u35DD\u35DE\u35DF\u35E0\u35E1\u35E2\u35E3"+ + "\u35E4\u35E5\u35E6\u35E7\u35E8\u35E9\u35EA\u35EB"+ + "\u35EC\u35ED\u35EE\u35EF\u35F0\u35F1\u35F2\u35F3"+ + "\u35F4\u35F5\u35F6\u35F7\u35F8\u35F9\u35FA\u35FB"+ + "\u35FC\u35FD\u35FE\u35FF\u3600\u3601\u3602\u3603"+ + "\u3604\u3605\u3606\u3607\u3608\u3609\u360A\u360B"+ + "\u360C\u360D\u360F\u3610\u3611\u3612\u3613\u3614"+ + "\u3615\u3616\u3617\u3618\u3619\u361B\u361C\u361D"+ + "\u361E\u361F\u3620\u3621\u3622\u3623\u3624\u3625"+ + "\u3626\u3627\u3628\u3629\u362A\u362B\u362C\u362D"+ + "\u362E\u362F\u3630\u3631\u3632\u3633\u3634\u3635"+ + "\u3636\u3637\u3638\u3639\u363A\u363B\u363C\u363D"+ + "\u363E\u363F\u3640\u3641\u3642\u3643\u3644\u3645"+ + "\u3646\u3647\u3648\u3649\u364A\u364B\u364C\u364D"+ + "\u364E\u364F\u3650\u3651\u3652\u3653\u3654\u3655"+ + "\u3656\u3657\u3658\u3659\u365A\u365B\u365C\u365D"+ + "\u365E\u365F\u3660\u3661\u3662\u3663\u3664\u3665"+ + "\u3666\u3667\u3668\u3669\u366A\u366B\u366C\u366D"+ + "\u366E\u366F\u3670\u3671\u3672\u3673\u3674\u3675"+ + "\u3676\u3677\u3678\u3679\u367A\u367B\u367C\u367D"+ + "\u367E\u367F\u3680\u3681\u3682\u3683\u3684\u3685"+ + "\u3686\u3687\u3688\u3689\u368A\u368B\u368C\u368D"+ + "\u368E\u368F\u3690\u3691\u3692\u3693\u3694\u3695"+ + "\u3696\u3697\u3698\u3699\u369A\u369B\u369C\u369D"+ + "\u369E\u369F\u36A0\u36A1\u36A2\u36A3\u36A4\u36A5"+ + "\u36A6\u36A7\u36A8\u36A9\u36AA\u36AB\u36AC\u36AD"+ + "\u36AE\u36AF\u36B0\u36B1\u36B2\u36B3\u36B4\u36B5"+ + "\u36B6\u36B7\u36B8\u36B9\u36BA\u36BB\u36BC\u36BD"+ + "\u36BE\u36BF\u36C0\u36C1\u36C2\u36C3\u36C4\u36C5"+ + "\u36C6\u36C7\u36C8\u36C9\u36CA\u36CB\u36CC\u36CD"+ + "\u36CE\u36CF\u36D0\u36D1\u36D2\u36D3\u36D4\u36D5"+ + "\u36D6\u36D7\u36D8\u36D9\u36DA\u36DB\u36DC\u36DD"+ + "\u36DE\u36DF\u36E0\u36E1\u36E2\u36E3\u36E4\u36E5"+ + "\u36E6\u36E7\u36E8\u36E9\u36EA\u36EB\u36EC\u36ED"+ + "\u36EE\u36EF\u36F0\u36F1\u36F2\u36F3\u36F4\u36F5"+ + "\u36F6\u36F7\u36F8\u36F9\u36FA\u36FB\u36FC\u36FD"+ + "\u36FE\u36FF\u3700\u3701\u3702\u3703\u3704\u3705"+ + "\u3706\u3707\u3708\u3709\u370A\u370B\u370C\u370D"+ + "\u370E\u370F\u3710\u3711\u3712\u3713\u3714\u3715"+ + "\u3716\u3717\u3718\u3719\u371A\u371B\u371C\u371D"+ + "\u371E\u371F\u3720\u3721\u3722\u3723\u3724\u3725"+ + "\u3726\u3727\u3728\u3729\u372A\u372B\u372C\u372D"+ + "\u372E\u372F\u3730\u3731\u3732\u3733\u3734\u3735"+ + "\u3736\u3737\u3738\u3739\u373A\u373B\u373C\u373D"+ + "\u373E\u373F\u3740\u3741\u3742\u3743\u3744\u3745"+ + "\u3746\u3747\u3748\u3749\u374A\u374B\u374C\u374D"+ + "\u374E\u374F\u3750\u3751\u3752\u3753\u3754\u3755"+ + "\u3756\u3757\u3758\u3759\u375A\u375B\u375C\u375D"+ + "\u375E\u375F\u3760\u3761\u3762\u3763\u3764\u3765"+ + "\u3766\u3767\u3768\u3769\u376A\u376B\u376C\u376D"+ + "\u376E\u376F\u3770\u3771\u3772\u3773\u3774\u3775"+ + "\u3776\u3777\u3778\u3779\u377A\u377B\u377C\u377D"+ + "\u377E\u377F\u3780\u3781\u3782\u3783\u3784\u3785"+ + "\u3786\u3787\u3788\u3789\u378A\u378B\u378C\u378D"+ + "\u378E\u378F\u3790\u3791\u3792\u3793\u3794\u3795"+ + "\u3796\u3797\u3798\u3799\u379A\u379B\u379C\u379D"+ + "\u379E\u379F\u37A0\u37A1\u37A2\u37A3\u37A4\u37A5"+ + "\u37A6\u37A7\u37A8\u37A9\u37AA\u37AB\u37AC\u37AD"+ + "\u37AE\u37AF\u37B0\u37B1\u37B2\u37B3\u37B4\u37B5"+ + "\u37B6\u37B7\u37B8\u37B9\u37BA\u37BB\u37BC\u37BD"+ + "\u37BE\u37BF\u37C0\u37C1\u37C2\u37C3\u37C4\u37C5"+ + "\u37C6\u37C7\u37C8\u37C9\u37CA\u37CB\u37CC\u37CD"+ + "\u37CE\u37CF\u37D0\u37D1\u37D2\u37D3\u37D4\u37D5"+ + "\u37D6\u37D7\u37D8\u37D9\u37DA\u37DB\u37DC\u37DD"+ + "\u37DE\u37DF\u37E0\u37E1\u37E2\u37E3\u37E4\u37E5"+ + "\u37E6\u37E7\u37E8\u37E9\u37EA\u37EB\u37EC\u37ED"+ + "\u37EE\u37EF\u37F0\u37F1\u37F2\u37F3\u37F4\u37F5"+ + "\u37F6\u37F7\u37F8\u37F9\u37FA\u37FB\u37FC\u37FD"+ + "\u37FE\u37FF\u3800\u3801\u3802\u3803\u3804\u3805"+ + "\u3806\u3807\u3808\u3809\u380A\u380B\u380C\u380D"+ + "\u380E\u380F\u3810\u3811\u3812\u3813\u3814\u3815"+ + "\u3816\u3817\u3818\u3819\u381A\u381B\u381C\u381D"+ + "\u381E\u381F\u3820\u3821\u3822\u3823\u3824\u3825"+ + "\u3826\u3827\u3828\u3829\u382A\u382B\u382C\u382D"+ + "\u382E\u382F\u3830\u3831\u3832\u3833\u3834\u3835"+ + "\u3836\u3837\u3838\u3839\u383A\u383B\u383C\u383D"+ + "\u383E\u383F\u3840\u3841\u3842\u3843\u3844\u3845"+ + "\u3846\u3847\u3848\u3849\u384A\u384B\u384C\u384D"+ + "\u384E\u384F\u3850\u3851\u3852\u3853\u3854\u3855"+ + "\u3856\u3857\u3858\u3859\u385A\u385B\u385C\u385D"+ + "\u385E\u385F\u3860\u3861\u3862\u3863\u3864\u3865"+ + "\u3866\u3867\u3868\u3869\u386A\u386B\u386C\u386D"+ + "\u386E\u386F\u3870\u3871\u3872\u3873\u3874\u3875"+ + "\u3876\u3877\u3878\u3879\u387A\u387B\u387C\u387D"+ + "\u387E\u387F\u3880\u3881\u3882\u3883\u3884\u3885"+ + "\u3886\u3887\u3888\u3889\u388A\u388B\u388C\u388D"+ + "\u388E\u388F\u3890\u3891\u3892\u3893\u3894\u3895"+ + "\u3896\u3897\u3898\u3899\u389A\u389B\u389C\u389D"+ + "\u389E\u389F\u38A0\u38A1\u38A2\u38A3\u38A4\u38A5"+ + "\u38A6\u38A7\u38A8\u38A9\u38AA\u38AB\u38AC\u38AD"+ + "\u38AE\u38AF\u38B0\u38B1\u38B2\u38B3\u38B4\u38B5"+ + "\u38B6\u38B7\u38B8\u38B9\u38BA\u38BB\u38BC\u38BD"+ + "\u38BE\u38BF\u38C0\u38C1\u38C2\u38C3\u38C4\u38C5"+ + "\u38C6\u38C7\u38C8\u38C9\u38CA\u38CB\u38CC\u38CD"+ + "\u38CE\u38CF\u38D0\u38D1\u38D2\u38D3\u38D4\u38D5"+ + "\u38D6\u38D7\u38D8\u38D9\u38DA\u38DB\u38DC\u38DD"+ + "\u38DE\u38DF\u38E0\u38E1\u38E2\u38E3\u38E4\u38E5"+ + "\u38E6\u38E7\u38E8\u38E9\u38EA\u38EB\u38EC\u38ED"+ + "\u38EE\u38EF\u38F0\u38F1\u38F2\u38F3\u38F4\u38F5"+ + "\u38F6\u38F7\u38F8\u38F9\u38FA\u38FB\u38FC\u38FD"+ + "\u38FE\u38FF\u3900\u3901\u3902\u3903\u3904\u3905"+ + "\u3906\u3907\u3908\u3909\u390A\u390B\u390C\u390D"+ + "\u390E\u390F\u3910\u3911\u3912\u3913\u3914\u3915"+ + "\u3916\u3917\u3919\u391A\u391B\u391C\u391D\u391E"+ + "\u391F\u3920\u3921\u3922\u3923\u3924\u3925\u3926"+ + "\u3927\u3928\u3929\u392A\u392B\u392C\u392D\u392E"+ + "\u392F\u3930\u3931\u3932\u3933\u3934\u3935\u3936"+ + "\u3937\u3938\u3939\u393A\u393B\u393C\u393D\u393E"+ + "\u393F\u3940\u3941\u3942\u3943\u3944\u3945\u3946"+ + "\u3947\u3948\u3949\u394A\u394B\u394C\u394D\u394E"+ + "\u394F\u3950\u3951\u3952\u3953\u3954\u3955\u3956"+ + "\u3957\u3958\u3959\u395A\u395B\u395C\u395D\u395E"+ + "\u395F\u3960\u3961\u3962\u3963\u3964\u3965\u3966"+ + "\u3967\u3968\u3969\u396A\u396B\u396C\u396D\u396F"+ + "\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977"+ + "\u3978\u3979\u397A\u397B\u397C\u397D\u397E\u397F"+ + "\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987"+ + "\u3988\u3989\u398A\u398B\u398C\u398D\u398E\u398F"+ + "\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997"+ + "\u3998\u3999\u399A\u399B\u399C\u399D\u399E\u399F"+ + "\u39A0\u39A1\u39A2\u39A3\u39A4\u39A5\u39A6\u39A7"+ + "\u39A8\u39A9\u39AA\u39AB\u39AC\u39AD\u39AE\u39AF"+ + "\u39B0\u39B1\u39B2\u39B3\u39B4\u39B5\u39B6\u39B7"+ + "\u39B8\u39B9\u39BA\u39BB\u39BC\u39BD\u39BE\u39BF"+ + "\u39C0\u39C1\u39C2\u39C3\u39C4\u39C5\u39C6\u39C7"+ + "\u39C8\u39C9\u39CA\u39CB\u39CC\u39CD\u39CE\u39D1"+ + "\u39D2\u39D3\u39D4\u39D5\u39D6\u39D7\u39D8\u39D9"+ + "\u39DA\u39DB\u39DC\u39DD\u39DE\u39E0\u39E1\u39E2"+ + "\u39E3\u39E4\u39E5\u39E6\u39E7\u39E8\u39E9\u39EA"+ + "\u39EB\u39EC\u39ED\u39EE\u39EF\u39F0\u39F1\u39F2"+ + "\u39F3\u39F4\u39F5\u39F6\u39F7\u39F8\u39F9\u39FA"+ + "\u39FB\u39FC\u39FD\u39FE\u39FF\u3A00\u3A01\u3A02"+ + "\u3A03\u3A04\u3A05\u3A06\u3A07\u3A08\u3A09\u3A0A"+ + "\u3A0B\u3A0C\u3A0D\u3A0E\u3A0F\u3A10\u3A11\u3A12"+ + "\u3A13\u3A14\u3A15\u3A16\u3A17\u3A18\u3A19\u3A1A"+ + "\u3A1B\u3A1C\u3A1D\u3A1E\u3A1F\u3A20\u3A21\u3A22"+ + "\u3A23\u3A24\u3A25\u3A26\u3A27\u3A28\u3A29\u3A2A"+ + "\u3A2B\u3A2C\u3A2D\u3A2E\u3A2F\u3A30\u3A31\u3A32"+ + "\u3A33\u3A34\u3A35\u3A36\u3A37\u3A38\u3A39\u3A3A"+ + "\u3A3B\u3A3C\u3A3D\u3A3E\u3A3F\u3A40\u3A41\u3A42"+ + "\u3A43\u3A44\u3A45\u3A46\u3A47\u3A48\u3A49\u3A4A"+ + "\u3A4B\u3A4C\u3A4D\u3A4E\u3A4F\u3A50\u3A51\u3A52"+ + "\u3A53\u3A54\u3A55\u3A56\u3A57\u3A58\u3A59\u3A5A"+ + "\u3A5B\u3A5C\u3A5D\u3A5E\u3A5F\u3A60\u3A61\u3A62"+ + "\u3A63\u3A64\u3A65\u3A66\u3A67\u3A68\u3A69\u3A6A"+ + "\u3A6B\u3A6C\u3A6D\u3A6E\u3A6F\u3A70\u3A71\u3A72"+ + "\u3A74\u3A75\u3A76\u3A77\u3A78\u3A79\u3A7A\u3A7B"+ + "\u3A7C\u3A7D\u3A7E\u3A7F\u3A80\u3A81\u3A82\u3A83"+ + "\u3A84\u3A85\u3A86\u3A87\u3A88\u3A89\u3A8A\u3A8B"+ + "\u3A8C\u3A8D\u3A8E\u3A8F\u3A90\u3A91\u3A92\u3A93"+ + "\u3A94\u3A95\u3A96\u3A97\u3A98\u3A99\u3A9A\u3A9B"+ + "\u3A9C\u3A9D\u3A9E\u3A9F\u3AA0\u3AA1\u3AA2\u3AA3"+ + "\u3AA4\u3AA5\u3AA6\u3AA7\u3AA8\u3AA9\u3AAA\u3AAB"+ + "\u3AAC\u3AAD\u3AAE\u3AAF\u3AB0\u3AB1\u3AB2\u3AB3"+ + "\u3AB4\u3AB5\u3AB6\u3AB7\u3AB8\u3AB9\u3ABA\u3ABB"+ + "\u3ABC\u3ABD\u3ABE\u3ABF\u3AC0\u3AC1\u3AC2\u3AC3"+ + "\u3AC4\u3AC5\u3AC6\u3AC7\u3AC8\u3AC9\u3ACA\u3ACB"+ + "\u3ACC\u3ACD\u3ACE\u3ACF\u3AD0\u3AD1\u3AD2\u3AD3"+ + "\u3AD4\u3AD5\u3AD6\u3AD7\u3AD8\u3AD9\u3ADA\u3ADB"+ + "\u3ADC\u3ADD\u3ADE\u3ADF\u3AE0\u3AE1\u3AE2\u3AE3"+ + "\u3AE4\u3AE5\u3AE6\u3AE7\u3AE8\u3AE9\u3AEA\u3AEB"+ + "\u3AEC\u3AED\u3AEE\u3AEF\u3AF0\u3AF1\u3AF2\u3AF3"+ + "\u3AF4\u3AF5\u3AF6\u3AF7\u3AF8\u3AF9\u3AFA\u3AFB"+ + "\u3AFC\u3AFD\u3AFE\u3AFF\u3B00\u3B01\u3B02\u3B03"+ + "\u3B04\u3B05\u3B06\u3B07\u3B08\u3B09\u3B0A\u3B0B"+ + "\u3B0C\u3B0D\u3B0E\u3B0F\u3B10\u3B11\u3B12\u3B13"+ + "\u3B14\u3B15\u3B16\u3B17\u3B18\u3B19\u3B1A\u3B1B"+ + "\u3B1C\u3B1D\u3B1E\u3B1F\u3B20\u3B21\u3B22\u3B23"+ + "\u3B24\u3B25\u3B26\u3B27\u3B28\u3B29\u3B2A\u3B2B"+ + "\u3B2C\u3B2D\u3B2E\u3B2F\u3B30\u3B31\u3B32\u3B33"+ + "\u3B34\u3B35\u3B36\u3B37\u3B38\u3B39\u3B3A\u3B3B"+ + "\u3B3C\u3B3D\u3B3E\u3B3F\u3B40\u3B41\u3B42\u3B43"+ + "\u3B44\u3B45\u3B46\u3B47\u3B48\u3B49\u3B4A\u3B4B"+ + "\u3B4C\u3B4D\u3B4F\u3B50\u3B51\u3B52\u3B53\u3B54"+ + "\u3B55\u3B56\u3B57\u3B58\u3B59\u3B5A\u3B5B\u3B5C"+ + "\u3B5D\u3B5E\u3B5F\u3B60\u3B61\u3B62\u3B63\u3B64"+ + "\u3B65\u3B66\u3B67\u3B68\u3B69\u3B6A\u3B6B\u3B6C"+ + "\u3B6D\u3B6E\u3B6F\u3B70\u3B71\u3B72\u3B73\u3B74"+ + "\u3B75\u3B76\u3B77\u3B78\u3B79\u3B7A\u3B7B\u3B7C"+ + "\u3B7D\u3B7E\u3B7F\u3B80\u3B81\u3B82\u3B83\u3B84"+ + "\u3B85\u3B86\u3B87\u3B88\u3B89\u3B8A\u3B8B\u3B8C"+ + "\u3B8D\u3B8E\u3B8F\u3B90\u3B91\u3B92\u3B93\u3B94"+ + "\u3B95\u3B96\u3B97\u3B98\u3B99\u3B9A\u3B9B\u3B9C"+ + "\u3B9D\u3B9E\u3B9F\u3BA0\u3BA1\u3BA2\u3BA3\u3BA4"+ + "\u3BA5\u3BA6\u3BA7\u3BA8\u3BA9\u3BAA\u3BAB\u3BAC"+ + "\u3BAD\u3BAE\u3BAF\u3BB0\u3BB1\u3BB2\u3BB3\u3BB4"+ + "\u3BB5\u3BB6\u3BB7\u3BB8\u3BB9\u3BBA\u3BBB\u3BBC"+ + "\u3BBD\u3BBE\u3BBF\u3BC0\u3BC1\u3BC2\u3BC3\u3BC4"+ + "\u3BC5\u3BC6\u3BC7\u3BC8\u3BC9\u3BCA\u3BCB\u3BCC"+ + "\u3BCD\u3BCE\u3BCF\u3BD0\u3BD1\u3BD2\u3BD3\u3BD4"+ + "\u3BD5\u3BD6\u3BD7\u3BD8\u3BD9\u3BDA\u3BDB\u3BDC"+ + "\u3BDD\u3BDE\u3BDF\u3BE0\u3BE1\u3BE2\u3BE3\u3BE4"+ + "\u3BE5\u3BE6\u3BE7\u3BE8\u3BE9\u3BEA\u3BEB\u3BEC"+ + "\u3BED\u3BEE\u3BEF\u3BF0\u3BF1\u3BF2\u3BF3\u3BF4"+ + "\u3BF5\u3BF6\u3BF7\u3BF8\u3BF9\u3BFA\u3BFB\u3BFC"+ + "\u3BFD\u3BFE\u3BFF\u3C00\u3C01\u3C02\u3C03\u3C04"+ + "\u3C05\u3C06\u3C07\u3C08\u3C09\u3C0A\u3C0B\u3C0C"+ + "\u3C0D\u3C0E\u3C0F\u3C10\u3C11\u3C12\u3C13\u3C14"+ + "\u3C15\u3C16\u3C17\u3C18\u3C19\u3C1A\u3C1B\u3C1C"+ + "\u3C1D\u3C1E\u3C1F\u3C20\u3C21\u3C22\u3C23\u3C24"+ + "\u3C25\u3C26\u3C27\u3C28\u3C29\u3C2A\u3C2B\u3C2C"+ + "\u3C2D\u3C2E\u3C2F\u3C30\u3C31\u3C32\u3C33\u3C34"+ + "\u3C35\u3C36\u3C37\u3C38\u3C39\u3C3A\u3C3B\u3C3C"+ + "\u3C3D\u3C3E\u3C3F\u3C40\u3C41\u3C42\u3C43\u3C44"+ + "\u3C45\u3C46\u3C47\u3C48\u3C49\u3C4A\u3C4B\u3C4C"+ + "\u3C4D\u3C4E\u3C4F\u3C50\u3C51\u3C52\u3C53\u3C54"+ + "\u3C55\u3C56\u3C57\u3C58\u3C59\u3C5A\u3C5B\u3C5C"+ + "\u3C5D\u3C5E\u3C5F\u3C60\u3C61\u3C62\u3C63\u3C64"+ + "\u3C65\u3C66\u3C67\u3C68\u3C69\u3C6A\u3C6B\u3C6C"+ + "\u3C6D\u3C6F\u3C70\u3C71\u3C72\u3C73\u3C74\u3C75"+ + "\u3C76\u3C77\u3C78\u3C79\u3C7A\u3C7B\u3C7C\u3C7D"+ + "\u3C7E\u3C7F\u3C80\u3C81\u3C82\u3C83\u3C84\u3C85"+ + "\u3C86\u3C87\u3C88\u3C89\u3C8A\u3C8B\u3C8C\u3C8D"+ + "\u3C8E\u3C8F\u3C90\u3C91\u3C92\u3C93\u3C94\u3C95"+ + "\u3C96\u3C97\u3C98\u3C99\u3C9A\u3C9B\u3C9C\u3C9D"+ + "\u3C9E\u3C9F\u3CA0\u3CA1\u3CA2\u3CA3\u3CA4\u3CA5"+ + "\u3CA6\u3CA7\u3CA8\u3CA9\u3CAA\u3CAB\u3CAC\u3CAD"+ + "\u3CAE\u3CAF\u3CB0\u3CB1\u3CB2\u3CB3\u3CB4\u3CB5"+ + "\u3CB6\u3CB7\u3CB8\u3CB9\u3CBA\u3CBB\u3CBC\u3CBD"+ + "\u3CBE\u3CBF\u3CC0\u3CC1\u3CC2\u3CC3\u3CC4\u3CC5"+ + "\u3CC6\u3CC7\u3CC8\u3CC9\u3CCA\u3CCB\u3CCC\u3CCD"+ + "\u3CCE\u3CCF\u3CD0\u3CD1\u3CD2\u3CD3\u3CD4\u3CD5"+ + "\u3CD6\u3CD7\u3CD8\u3CD9\u3CDA\u3CDB\u3CDC\u3CDD"+ + "\u3CDE\u3CDF\u3CE1\u3CE2\u3CE3\u3CE4\u3CE5\u3CE6"+ + "\u3CE7\u3CE8\u3CE9\u3CEA\u3CEB\u3CEC\u3CED\u3CEE"+ + "\u3CEF\u3CF0\u3CF1\u3CF2\u3CF3\u3CF4\u3CF5\u3CF6"+ + "\u3CF7\u3CF8\u3CF9\u3CFA\u3CFB\u3CFC\u3CFD\u3CFE"+ + "\u3CFF\u3D00\u3D01\u3D02\u3D03\u3D04\u3D05\u3D06"+ + "\u3D07\u3D08\u3D09\u3D0A\u3D0B\u3D0C\u3D0D\u3D0E"+ + "\u3D0F\u3D10\u3D11\u3D12\u3D13\u3D14\u3D15\u3D16"+ + "\u3D17\u3D18\u3D19\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E"+ + "\u3D1F\u3D20\u3D21\u3D22\u3D23\u3D24\u3D25\u3D26"+ + "\u3D27\u3D28\u3D29\u3D2A\u3D2B\u3D2C\u3D2D\u3D2E"+ + "\u3D2F\u3D30\u3D31\u3D32\u3D33\u3D34\u3D35\u3D36"+ + "\u3D37\u3D38\u3D39\u3D3A\u3D3B\u3D3C\u3D3D\u3D3E"+ + "\u3D3F\u3D40\u3D41\u3D42\u3D43\u3D44\u3D45\u3D46"+ + "\u3D47\u3D48\u3D49\u3D4A\u3D4B\u3D4C\u3D4D\u3D4E"+ + "\u3D4F\u3D50\u3D51\u3D52\u3D53\u3D54\u3D55\u3D56"+ + "\u3D57\u3D58\u3D59\u3D5A\u3D5B\u3D5C\u3D5D\u3D5E"+ + "\u3D5F\u3D60\u3D61\u3D62\u3D63\u3D64\u3D65\u3D66"+ + "\u3D67\u3D68\u3D69\u3D6A\u3D6B\u3D6C\u3D6D\u3D6E"+ + "\u3D6F\u3D70\u3D71\u3D72\u3D73\u3D74\u3D75\u3D76"+ + "\u3D77\u3D78\u3D79\u3D7A\u3D7B\u3D7C\u3D7D\u3D7E"+ + "\u3D7F\u3D80\u3D81\u3D82\u3D83\u3D84\u3D85\u3D86"+ + "\u3D87\u3D88\u3D89\u3D8A\u3D8B\u3D8C\u3D8D\u3D8E"+ + "\u3D8F\u3D90\u3D91\u3D92\u3D93\u3D94\u3D95\u3D96"+ + "\u3D97\u3D98\u3D99\u3D9A\u3D9B\u3D9C\u3D9D\u3D9E"+ + "\u3D9F\u3DA0\u3DA1\u3DA2\u3DA3\u3DA4\u3DA5\u3DA6"+ + "\u3DA7\u3DA8\u3DA9\u3DAA\u3DAB\u3DAC\u3DAD\u3DAE"+ + "\u3DAF\u3DB0\u3DB1\u3DB2\u3DB3\u3DB4\u3DB5\u3DB6"+ + "\u3DB7\u3DB8\u3DB9\u3DBA\u3DBB\u3DBC\u3DBD\u3DBE"+ + "\u3DBF\u3DC0\u3DC1\u3DC2\u3DC3\u3DC4\u3DC5\u3DC6"+ + "\u3DC7\u3DC8\u3DC9\u3DCA\u3DCB\u3DCC\u3DCD\u3DCE"+ + "\u3DCF\u3DD0\u3DD1\u3DD2\u3DD3\u3DD4\u3DD5\u3DD6"+ + "\u3DD7\u3DD8\u3DD9\u3DDA\u3DDB\u3DDC\u3DDD\u3DDE"+ + "\u3DDF\u3DE0\u3DE1\u3DE2\u3DE3\u3DE4\u3DE5\u3DE6"+ + "\u3DE7\u3DE8\u3DE9\u3DEA\u3DEB\u3DEC\u3DED\u3DEE"+ + "\u3DEF\u3DF0\u3DF1\u3DF2\u3DF3\u3DF4\u3DF5\u3DF6"+ + "\u3DF7\u3DF8\u3DF9\u3DFA\u3DFB\u3DFC\u3DFD\u3DFE"+ + "\u3DFF\u3E00\u3E01\u3E02\u3E03\u3E04\u3E05\u3E06"+ + "\u3E07\u3E08\u3E09\u3E0A\u3E0B\u3E0C\u3E0D\u3E0E"+ + "\u3E0F\u3E10\u3E11\u3E12\u3E13\u3E14\u3E15\u3E16"+ + "\u3E17\u3E18\u3E19\u3E1A\u3E1B\u3E1C\u3E1D\u3E1E"+ + "\u3E1F\u3E20\u3E21\u3E22\u3E23\u3E24\u3E25\u3E26"+ + "\u3E27\u3E28\u3E29\u3E2A\u3E2B\u3E2C\u3E2D\u3E2E"+ + "\u3E2F\u3E30\u3E31\u3E32\u3E33\u3E34\u3E35\u3E36"+ + "\u3E37\u3E38\u3E39\u3E3A\u3E3B\u3E3C\u3E3D\u3E3E"+ + "\u3E3F\u3E40\u3E41\u3E42\u3E43\u3E44\u3E45\u3E46"+ + "\u3E47\u3E48\u3E49\u3E4A\u3E4B\u3E4C\u3E4D\u3E4E"+ + "\u3E4F\u3E50\u3E51\u3E52\u3E53\u3E54\u3E55\u3E56"+ + "\u3E57\u3E58\u3E59\u3E5A\u3E5B\u3E5C\u3E5D\u3E5E"+ + "\u3E5F\u3E60\u3E61\u3E62\u3E63\u3E64\u3E65\u3E66"+ + "\u3E67\u3E68\u3E69\u3E6A\u3E6B\u3E6C\u3E6D\u3E6E"+ + "\u3E6F\u3E70\u3E71\u3E72\u3E73\u3E74\u3E75\u3E76"+ + "\u3E77\u3E78\u3E79\u3E7A\u3E7B\u3E7C\u3E7D\u3E7E"+ + "\u3E7F\u3E80\u3E81\u3E82\u3E83\u3E84\u3E85\u3E86"+ + "\u3E87\u3E88\u3E89\u3E8A\u3E8B\u3E8C\u3E8D\u3E8E"+ + "\u3E8F\u3E90\u3E91\u3E92\u3E93\u3E94\u3E95\u3E96"+ + "\u3E97\u3E98\u3E99\u3E9A\u3E9B\u3E9C\u3E9D\u3E9E"+ + "\u3E9F\u3EA0\u3EA1\u3EA2\u3EA3\u3EA4\u3EA5\u3EA6"+ + "\u3EA7\u3EA8\u3EA9\u3EAA\u3EAB\u3EAC\u3EAD\u3EAE"+ + "\u3EAF\u3EB0\u3EB1\u3EB2\u3EB3\u3EB4\u3EB5\u3EB6"+ + "\u3EB7\u3EB8\u3EB9\u3EBA\u3EBB\u3EBC\u3EBD\u3EBE"+ + "\u3EBF\u3EC0\u3EC1\u3EC2\u3EC3\u3EC4\u3EC5\u3EC6"+ + "\u3EC7\u3EC8\u3EC9\u3ECA\u3ECB\u3ECC\u3ECD\u3ECE"+ + "\u3ECF\u3ED0\u3ED1\u3ED2\u3ED3\u3ED4\u3ED5\u3ED6"+ + "\u3ED7\u3ED8\u3ED9\u3EDA\u3EDB\u3EDC\u3EDD\u3EDE"+ + "\u3EDF\u3EE0\u3EE1\u3EE2\u3EE3\u3EE4\u3EE5\u3EE6"+ + "\u3EE7\u3EE8\u3EE9\u3EEA\u3EEB\u3EEC\u3EED\u3EEE"+ + "\u3EEF\u3EF0\u3EF1\u3EF2\u3EF3\u3EF4\u3EF5\u3EF6"+ + "\u3EF7\u3EF8\u3EF9\u3EFA\u3EFB\u3EFC\u3EFD\u3EFE"+ + "\u3EFF\u3F00\u3F01\u3F02\u3F03\u3F04\u3F05\u3F06"+ + "\u3F07\u3F08\u3F09\u3F0A\u3F0B\u3F0C\u3F0D\u3F0E"+ + "\u3F0F\u3F10\u3F11\u3F12\u3F13\u3F14\u3F15\u3F16"+ + "\u3F17\u3F18\u3F19\u3F1A\u3F1B\u3F1C\u3F1D\u3F1E"+ + "\u3F1F\u3F20\u3F21\u3F22\u3F23\u3F24\u3F25\u3F26"+ + "\u3F27\u3F28\u3F29\u3F2A\u3F2B\u3F2C\u3F2D\u3F2E"+ + "\u3F2F\u3F30\u3F31\u3F32\u3F33\u3F34\u3F35\u3F36"+ + "\u3F37\u3F38\u3F39\u3F3A\u3F3B\u3F3C\u3F3D\u3F3E"+ + "\u3F3F\u3F40\u3F41\u3F42\u3F43\u3F44\u3F45\u3F46"+ + "\u3F47\u3F48\u3F49\u3F4A\u3F4B\u3F4C\u3F4D\u3F4E"+ + "\u3F4F\u3F50\u3F51\u3F52\u3F53\u3F54\u3F55\u3F56"+ + "\u3F57\u3F58\u3F59\u3F5A\u3F5B\u3F5C\u3F5D\u3F5E"+ + "\u3F5F\u3F60\u3F61\u3F62\u3F63\u3F64\u3F65\u3F66"+ + "\u3F67\u3F68\u3F69\u3F6A\u3F6B\u3F6C\u3F6D\u3F6E"+ + "\u3F6F\u3F70\u3F71\u3F72\u3F73\u3F74\u3F75\u3F76"+ + "\u3F77\u3F78\u3F79\u3F7A\u3F7B\u3F7C\u3F7D\u3F7E"+ + "\u3F7F\u3F80\u3F81\u3F82\u3F83\u3F84\u3F85\u3F86"+ + "\u3F87\u3F88\u3F89\u3F8A\u3F8B\u3F8C\u3F8D\u3F8E"+ + "\u3F8F\u3F90\u3F91\u3F92\u3F93\u3F94\u3F95\u3F96"+ + "\u3F97\u3F98\u3F99\u3F9A\u3F9B\u3F9C\u3F9D\u3F9E"+ + "\u3F9F\u3FA0\u3FA1\u3FA2\u3FA3\u3FA4\u3FA5\u3FA6"+ + "\u3FA7\u3FA8\u3FA9\u3FAA\u3FAB\u3FAC\u3FAD\u3FAE"+ + "\u3FAF\u3FB0\u3FB1\u3FB2\u3FB3\u3FB4\u3FB5\u3FB6"+ + "\u3FB7\u3FB8\u3FB9\u3FBA\u3FBB\u3FBC\u3FBD\u3FBE"+ + "\u3FBF\u3FC0\u3FC1\u3FC2\u3FC3\u3FC4\u3FC5\u3FC6"+ + "\u3FC7\u3FC8\u3FC9\u3FCA\u3FCB\u3FCC\u3FCD\u3FCE"+ + "\u3FCF\u3FD0\u3FD1\u3FD2\u3FD3\u3FD4\u3FD5\u3FD6"+ + "\u3FD7\u3FD8\u3FD9\u3FDA\u3FDB\u3FDC\u3FDD\u3FDE"+ + "\u3FDF\u3FE0\u3FE1\u3FE2\u3FE3\u3FE4\u3FE5\u3FE6"+ + "\u3FE7\u3FE8\u3FE9\u3FEA\u3FEB\u3FEC\u3FED\u3FEE"+ + "\u3FEF\u3FF0\u3FF1\u3FF2\u3FF3\u3FF4\u3FF5\u3FF6"+ + "\u3FF7\u3FF8\u3FF9\u3FFA\u3FFB\u3FFC\u3FFD\u3FFE"+ + "\u3FFF\u4000\u4001\u4002\u4003\u4004\u4005\u4006"+ + "\u4007\u4008\u4009\u400A\u400B\u400C\u400D\u400E"+ + "\u400F\u4010\u4011\u4012\u4013\u4014\u4015\u4016"+ + "\u4017\u4018\u4019\u401A\u401B\u401C\u401D\u401E"+ + "\u401F\u4020\u4021\u4022\u4023\u4024\u4025\u4026"+ + "\u4027\u4028\u4029\u402A\u402B\u402C\u402D\u402E"+ + "\u402F\u4030\u4031\u4032\u4033\u4034\u4035\u4036"+ + "\u4037\u4038\u4039\u403A\u403B\u403C\u403D\u403E"+ + "\u403F\u4040\u4041\u4042\u4043\u4044\u4045\u4046"+ + "\u4047\u4048\u4049\u404A\u404B\u404C\u404D\u404E"+ + "\u404F\u4050\u4051\u4052\u4053\u4054\u4055\u4057"+ + "\u4058\u4059\u405A\u405B\u405C\u405D\u405E\u405F"+ + "\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067"+ + "\u4068\u4069\u406A\u406B\u406C\u406D\u406E\u406F"+ + "\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077"+ + "\u4078\u4079\u407A\u407B\u407C\u407D\u407E\u407F"+ + "\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087"+ + "\u4088\u4089\u408A\u408B\u408C\u408D\u408E\u408F"+ + "\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097"+ + "\u4098\u4099\u409A\u409B\u409C\u409D\u409E\u409F"+ + "\u40A0\u40A1\u40A2\u40A3\u40A4\u40A5\u40A6\u40A7"+ + "\u40A8\u40A9\u40AA\u40AB\u40AC\u40AD\u40AE\u40AF"+ + "\u40B0\u40B1\u40B2\u40B3\u40B4\u40B5\u40B6\u40B7"+ + "\u40B8\u40B9\u40BA\u40BB\u40BC\u40BD\u40BE\u40BF"+ + "\u40C0\u40C1\u40C2\u40C3\u40C4\u40C5\u40C6\u40C7"+ + "\u40C8\u40C9\u40CA\u40CB\u40CC\u40CD\u40CE\u40CF"+ + "\u40D0\u40D1\u40D2\u40D3\u40D4\u40D5\u40D6\u40D7"+ + "\u40D8\u40D9\u40DA\u40DB\u40DC\u40DD\u40DE\u40DF"+ + "\u40E0\u40E1\u40E2\u40E3\u40E4\u40E5\u40E6\u40E7"+ + "\u40E8\u40E9\u40EA\u40EB\u40EC\u40ED\u40EE\u40EF"+ + "\u40F0\u40F1\u40F2\u40F3\u40F4\u40F5\u40F6\u40F7"+ + "\u40F8\u40F9\u40FA\u40FB\u40FC\u40FD\u40FE\u40FF"+ + "\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107"+ + "\u4108\u4109\u410A\u410B\u410C\u410D\u410E\u410F"+ + "\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117"+ + "\u4118\u4119\u411A\u411B\u411C\u411D\u411E\u411F"+ + "\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127"+ + "\u4128\u4129\u412A\u412B\u412C\u412D\u412E\u412F"+ + "\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137"+ + "\u4138\u4139\u413A\u413B\u413C\u413D\u413E\u413F"+ + "\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147"+ + "\u4148\u4149\u414A\u414B\u414C\u414D\u414E\u414F"+ + "\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157"+ + "\u4158\u4159\u415A\u415B\u415C\u415D\u415E\u4160"+ + "\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168"+ + "\u4169\u416A\u416B\u416C\u416D\u416E\u416F\u4170"+ + "\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178"+ + "\u4179\u417A\u417B\u417C\u417D\u417E\u417F\u4180"+ + "\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188"+ + "\u4189\u418A\u418B\u418C\u418D\u418E\u418F\u4190"+ + "\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198"+ + "\u4199\u419A\u419B\u419C\u419D\u419E\u419F\u41A0"+ + "\u41A1\u41A2\u41A3\u41A4\u41A5\u41A6\u41A7\u41A8"+ + "\u41A9\u41AA\u41AB\u41AC\u41AD\u41AE\u41AF\u41B0"+ + "\u41B1\u41B2\u41B3\u41B4\u41B5\u41B6\u41B7\u41B8"+ + "\u41B9\u41BA\u41BB\u41BC\u41BD\u41BE\u41BF\u41C0"+ + "\u41C1\u41C2\u41C3\u41C4\u41C5\u41C6\u41C7\u41C8"+ + "\u41C9\u41CA\u41CB\u41CC\u41CD\u41CE\u41CF\u41D0"+ + "\u41D1\u41D2\u41D3\u41D4\u41D5\u41D6\u41D7\u41D8"+ + "\u41D9\u41DA\u41DB\u41DC\u41DD\u41DE\u41DF\u41E0"+ + "\u41E1\u41E2\u41E3\u41E4\u41E5\u41E6\u41E7\u41E8"+ + "\u41E9\u41EA\u41EB\u41EC\u41ED\u41EE\u41EF\u41F0"+ + "\u41F1\u41F2\u41F3\u41F4\u41F5\u41F6\u41F7\u41F8"+ + "\u41F9\u41FA\u41FB\u41FC\u41FD\u41FE\u41FF\u4200"+ + "\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208"+ + "\u4209\u420A\u420B\u420C\u420D\u420E\u420F\u4210"+ + "\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218"+ + "\u4219\u421A\u421B\u421C\u421D\u421E\u421F\u4220"+ + "\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228"+ + "\u4229\u422A\u422B\u422C\u422D\u422E\u422F\u4230"+ + "\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238"+ + "\u4239\u423A\u423B\u423C\u423D\u423E\u423F\u4240"+ + "\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248"+ + "\u4249\u424A\u424B\u424C\u424D\u424E\u424F\u4250"+ + "\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258"+ + "\u4259\u425A\u425B\u425C\u425D\u425E\u425F\u4260"+ + "\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268"+ + "\u4269\u426A\u426B\u426C\u426D\u426E\u426F\u4270"+ + "\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278"+ + "\u4279\u427A\u427B\u427C\u427D\u427E\u427F\u4280"+ + "\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288"+ + "\u4289\u428A\u428B\u428C\u428D\u428E\u428F\u4290"+ + "\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298"+ + "\u4299\u429A\u429B\u429C\u429D\u429E\u429F\u42A0"+ + "\u42A1\u42A2\u42A3\u42A4\u42A5\u42A6\u42A7\u42A8"+ + "\u42A9\u42AA\u42AB\u42AC\u42AD\u42AE\u42AF\u42B0"+ + "\u42B1\u42B2\u42B3\u42B4\u42B5\u42B6\u42B7\u42B8"+ + "\u42B9\u42BA\u42BB\u42BC\u42BD\u42BE\u42BF\u42C0"+ + "\u42C1\u42C2\u42C3\u42C4\u42C5\u42C6\u42C7\u42C8"+ + "\u42C9\u42CA\u42CB\u42CC\u42CD\u42CE\u42CF\u42D0"+ + "\u42D1\u42D2\u42D3\u42D4\u42D5\u42D6\u42D7\u42D8"+ + "\u42D9\u42DA\u42DB\u42DC\u42DD\u42DE\u42DF\u42E0"+ + "\u42E1\u42E2\u42E3\u42E4\u42E5\u42E6\u42E7\u42E8"+ + "\u42E9\u42EA\u42EB\u42EC\u42ED\u42EE\u42EF\u42F0"+ + "\u42F1\u42F2\u42F3\u42F4\u42F5\u42F6\u42F7\u42F8"+ + "\u42F9\u42FA\u42FB\u42FC\u42FD\u42FE\u42FF\u4300"+ + "\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308"+ + "\u4309\u430A\u430B\u430C\u430D\u430E\u430F\u4310"+ + "\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318"+ + "\u4319\u431A\u431B\u431C\u431D\u431E\u431F\u4320"+ + "\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328"+ + "\u4329\u432A\u432B\u432C\u432D\u432E\u432F\u4330"+ + "\u4331\u4332\u4333\u4334\u4335\u4336\u4338\u4339"+ + "\u433A\u433B\u433C\u433D\u433E\u433F\u4340\u4341"+ + "\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349"+ + "\u434A\u434B\u434C\u434D\u434E\u434F\u4350\u4351"+ + "\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359"+ + "\u435A\u435B\u435C\u435D\u435E\u435F\u4360\u4361"+ + "\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369"+ + "\u436A\u436B\u436C\u436D\u436E\u436F\u4370\u4371"+ + "\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379"; + + private final static String innerDecoderIndex4= + "\u437A\u437B\u437C\u437D\u437E\u437F\u4380\u4381"+ + "\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389"+ + "\u438A\u438B\u438C\u438D\u438E\u438F\u4390\u4391"+ + "\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399"+ + "\u439A\u439B\u439C\u439D\u439E\u439F\u43A0\u43A1"+ + "\u43A2\u43A3\u43A4\u43A5\u43A6\u43A7\u43A8\u43A9"+ + "\u43AA\u43AB\u43AD\u43AE\u43AF\u43B0\u43B2\u43B3"+ + "\u43B4\u43B5\u43B6\u43B7\u43B8\u43B9\u43BA\u43BB"+ + "\u43BC\u43BD\u43BE\u43BF\u43C0\u43C1\u43C2\u43C3"+ + "\u43C4\u43C5\u43C6\u43C7\u43C8\u43C9\u43CA\u43CB"+ + "\u43CC\u43CD\u43CE\u43CF\u43D0\u43D1\u43D2\u43D3"+ + "\u43D4\u43D5\u43D6\u43D7\u43D8\u43D9\u43DA\u43DB"+ + "\u43DC\u43DE\u43DF\u43E0\u43E1\u43E2\u43E3\u43E4"+ + "\u43E5\u43E6\u43E7\u43E8\u43E9\u43EA\u43EB\u43EC"+ + "\u43ED\u43EE\u43EF\u43F0\u43F1\u43F2\u43F3\u43F4"+ + "\u43F5\u43F6\u43F7\u43F8\u43F9\u43FA\u43FB\u43FC"+ + "\u43FD\u43FE\u43FF\u4400\u4401\u4402\u4403\u4404"+ + "\u4405\u4406\u4407\u4408\u4409\u440A\u440B\u440C"+ + "\u440D\u440E\u440F\u4410\u4411\u4412\u4413\u4414"+ + "\u4415\u4416\u4417\u4418\u4419\u441A\u441B\u441C"+ + "\u441D\u441E\u441F\u4420\u4421\u4422\u4423\u4424"+ + "\u4425\u4426\u4427\u4428\u4429\u442A\u442B\u442C"+ + "\u442D\u442E\u442F\u4430\u4431\u4432\u4433\u4434"+ + "\u4435\u4436\u4437\u4438\u4439\u443A\u443B\u443C"+ + "\u443D\u443E\u443F\u4440\u4441\u4442\u4443\u4444"+ + "\u4445\u4446\u4447\u4448\u4449\u444A\u444B\u444C"+ + "\u444D\u444E\u444F\u4450\u4451\u4452\u4453\u4454"+ + "\u4455\u4456\u4457\u4458\u4459\u445A\u445B\u445C"+ + "\u445D\u445E\u445F\u4460\u4461\u4462\u4463\u4464"+ + "\u4465\u4466\u4467\u4468\u4469\u446A\u446B\u446C"+ + "\u446D\u446E\u446F\u4470\u4471\u4472\u4473\u4474"+ + "\u4475\u4476\u4477\u4478\u4479\u447A\u447B\u447C"+ + "\u447D\u447E\u447F\u4480\u4481\u4482\u4483\u4484"+ + "\u4485\u4486\u4487\u4488\u4489\u448A\u448B\u448C"+ + "\u448D\u448E\u448F\u4490\u4491\u4492\u4493\u4494"+ + "\u4495\u4496\u4497\u4498\u4499\u449A\u449B\u449C"+ + "\u449D\u449E\u449F\u44A0\u44A1\u44A2\u44A3\u44A4"+ + "\u44A5\u44A6\u44A7\u44A8\u44A9\u44AA\u44AB\u44AC"+ + "\u44AD\u44AE\u44AF\u44B0\u44B1\u44B2\u44B3\u44B4"+ + "\u44B5\u44B6\u44B7\u44B8\u44B9\u44BA\u44BB\u44BC"+ + "\u44BD\u44BE\u44BF\u44C0\u44C1\u44C2\u44C3\u44C4"+ + "\u44C5\u44C6\u44C7\u44C8\u44C9\u44CA\u44CB\u44CC"+ + "\u44CD\u44CE\u44CF\u44D0\u44D1\u44D2\u44D3\u44D4"+ + "\u44D5\u44D7\u44D8\u44D9\u44DA\u44DB\u44DC\u44DD"+ + "\u44DE\u44DF\u44E0\u44E1\u44E2\u44E3\u44E4\u44E5"+ + "\u44E6\u44E7\u44E8\u44E9\u44EA\u44EB\u44EC\u44ED"+ + "\u44EE\u44EF\u44F0\u44F1\u44F2\u44F3\u44F4\u44F5"+ + "\u44F6\u44F7\u44F8\u44F9\u44FA\u44FB\u44FC\u44FD"+ + "\u44FE\u44FF\u4500\u4501\u4502\u4503\u4504\u4505"+ + "\u4506\u4507\u4508\u4509\u450A\u450B\u450C\u450D"+ + "\u450E\u450F\u4510\u4511\u4512\u4513\u4514\u4515"+ + "\u4516\u4517\u4518\u4519\u451A\u451B\u451C\u451D"+ + "\u451E\u451F\u4520\u4521\u4522\u4523\u4524\u4525"+ + "\u4526\u4527\u4528\u4529\u452A\u452B\u452C\u452D"+ + "\u452E\u452F\u4530\u4531\u4532\u4533\u4534\u4535"+ + "\u4536\u4537\u4538\u4539\u453A\u453B\u453C\u453D"+ + "\u453E\u453F\u4540\u4541\u4542\u4543\u4544\u4545"+ + "\u4546\u4547\u4548\u4549\u454A\u454B\u454C\u454D"+ + "\u454E\u454F\u4550\u4551\u4552\u4553\u4554\u4555"+ + "\u4556\u4557\u4558\u4559\u455A\u455B\u455C\u455D"+ + "\u455E\u455F\u4560\u4561\u4562\u4563\u4564\u4565"+ + "\u4566\u4567\u4568\u4569\u456A\u456B\u456C\u456D"+ + "\u456E\u456F\u4570\u4571\u4572\u4573\u4574\u4575"+ + "\u4576\u4577\u4578\u4579\u457A\u457B\u457C\u457D"+ + "\u457E\u457F\u4580\u4581\u4582\u4583\u4584\u4585"+ + "\u4586\u4587\u4588\u4589\u458A\u458B\u458C\u458D"+ + "\u458E\u458F\u4590\u4591\u4592\u4593\u4594\u4595"+ + "\u4596\u4597\u4598\u4599\u459A\u459B\u459C\u459D"+ + "\u459E\u459F\u45A0\u45A1\u45A2\u45A3\u45A4\u45A5"+ + "\u45A6\u45A7\u45A8\u45A9\u45AA\u45AB\u45AC\u45AD"+ + "\u45AE\u45AF\u45B0\u45B1\u45B2\u45B3\u45B4\u45B5"+ + "\u45B6\u45B7\u45B8\u45B9\u45BA\u45BB\u45BC\u45BD"+ + "\u45BE\u45BF\u45C0\u45C1\u45C2\u45C3\u45C4\u45C5"+ + "\u45C6\u45C7\u45C8\u45C9\u45CA\u45CB\u45CC\u45CD"+ + "\u45CE\u45CF\u45D0\u45D1\u45D2\u45D3\u45D4\u45D5"+ + "\u45D6\u45D7\u45D8\u45D9\u45DA\u45DB\u45DC\u45DD"+ + "\u45DE\u45DF\u45E0\u45E1\u45E2\u45E3\u45E4\u45E5"+ + "\u45E6\u45E7\u45E8\u45E9\u45EA\u45EB\u45EC\u45ED"+ + "\u45EE\u45EF\u45F0\u45F1\u45F2\u45F3\u45F4\u45F5"+ + "\u45F6\u45F7\u45F8\u45F9\u45FA\u45FB\u45FC\u45FD"+ + "\u45FE\u45FF\u4600\u4601\u4602\u4603\u4604\u4605"+ + "\u4606\u4607\u4608\u4609\u460A\u460B\u460C\u460D"+ + "\u460E\u460F\u4610\u4611\u4612\u4613\u4614\u4615"+ + "\u4616\u4617\u4618\u4619\u461A\u461B\u461C\u461D"+ + "\u461E\u461F\u4620\u4621\u4622\u4623\u4624\u4625"+ + "\u4626\u4627\u4628\u4629\u462A\u462B\u462C\u462D"+ + "\u462E\u462F\u4630\u4631\u4632\u4633\u4634\u4635"+ + "\u4636\u4637\u4638\u4639\u463A\u463B\u463C\u463D"+ + "\u463E\u463F\u4640\u4641\u4642\u4643\u4644\u4645"+ + "\u4646\u4647\u4648\u4649\u464A\u464B\u464D\u464E"+ + "\u464F\u4650\u4651\u4652\u4653\u4654\u4655\u4656"+ + "\u4657\u4658\u4659\u465A\u465B\u465C\u465D\u465E"+ + "\u465F\u4660\u4662\u4663\u4664\u4665\u4666\u4667"+ + "\u4668\u4669\u466A\u466B\u466C\u466D\u466E\u466F"+ + "\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677"+ + "\u4678\u4679\u467A\u467B\u467C\u467D\u467E\u467F"+ + "\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687"+ + "\u4688\u4689\u468A\u468B\u468C\u468D\u468E\u468F"+ + "\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697"+ + "\u4698\u4699\u469A\u469B\u469C\u469D\u469E\u469F"+ + "\u46A0\u46A1\u46A2\u46A3\u46A4\u46A5\u46A6\u46A7"+ + "\u46A8\u46A9\u46AA\u46AB\u46AC\u46AD\u46AE\u46AF"+ + "\u46B0\u46B1\u46B2\u46B3\u46B4\u46B5\u46B6\u46B7"+ + "\u46B8\u46B9\u46BA\u46BB\u46BC\u46BD\u46BE\u46BF"+ + "\u46C0\u46C1\u46C2\u46C3\u46C4\u46C5\u46C6\u46C7"+ + "\u46C8\u46C9\u46CA\u46CB\u46CC\u46CD\u46CE\u46CF"+ + "\u46D0\u46D1\u46D2\u46D3\u46D4\u46D5\u46D6\u46D7"+ + "\u46D8\u46D9\u46DA\u46DB\u46DC\u46DD\u46DE\u46DF"+ + "\u46E0\u46E1\u46E2\u46E3\u46E4\u46E5\u46E6\u46E7"+ + "\u46E8\u46E9\u46EA\u46EB\u46EC\u46ED\u46EE\u46EF"+ + "\u46F0\u46F1\u46F2\u46F3\u46F4\u46F5\u46F6\u46F7"+ + "\u46F8\u46F9\u46FA\u46FB\u46FC\u46FD\u46FE\u46FF"+ + "\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707"+ + "\u4708\u4709\u470A\u470B\u470C\u470D\u470E\u470F"+ + "\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717"+ + "\u4718\u4719\u471A\u471B\u471C\u471D\u471E\u471F"+ + "\u4720\u4721\u4722\u4724\u4725\u4726\u4727\u4728"+ + "\u472A\u472B\u472C\u472D\u472E\u472F\u4730\u4731"+ + "\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739"+ + "\u473A\u473B\u473C\u473D\u473E\u473F\u4740\u4741"+ + "\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749"+ + "\u474A\u474B\u474C\u474D\u474E\u474F\u4750\u4751"+ + "\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759"+ + "\u475A\u475B\u475C\u475D\u475E\u475F\u4760\u4761"+ + "\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769"+ + "\u476A\u476B\u476C\u476D\u476E\u476F\u4770\u4771"+ + "\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779"+ + "\u477A\u477B\u477D\u477E\u477F\u4780\u4781\u4782"+ + "\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478A"+ + "\u478B\u478C\u478E\u478F\u4790\u4791\u4792\u4793"+ + "\u4794\u4795\u4796\u4797\u4798\u4799\u479A\u479B"+ + "\u479C\u479D\u479E\u479F\u47A0\u47A1\u47A2\u47A3"+ + "\u47A4\u47A5\u47A6\u47A7\u47A8\u47A9\u47AA\u47AB"+ + "\u47AC\u47AD\u47AE\u47AF\u47B0\u47B1\u47B2\u47B3"+ + "\u47B4\u47B5\u47B6\u47B7\u47B8\u47B9\u47BA\u47BB"+ + "\u47BC\u47BD\u47BE\u47BF\u47C0\u47C1\u47C2\u47C3"+ + "\u47C4\u47C5\u47C6\u47C7\u47C8\u47C9\u47CA\u47CB"+ + "\u47CC\u47CD\u47CE\u47CF\u47D0\u47D1\u47D2\u47D3"+ + "\u47D4\u47D5\u47D6\u47D7\u47D8\u47D9\u47DA\u47DB"+ + "\u47DC\u47DD\u47DE\u47DF\u47E0\u47E1\u47E2\u47E3"+ + "\u47E4\u47E5\u47E6\u47E7\u47E8\u47E9\u47EA\u47EB"+ + "\u47EC\u47ED\u47EE\u47EF\u47F0\u47F1\u47F2\u47F3"+ + "\u47F4\u47F5\u47F6\u47F7\u47F8\u47F9\u47FA\u47FB"+ + "\u47FC\u47FD\u47FE\u47FF\u4800\u4801\u4802\u4803"+ + "\u4804\u4805\u4806\u4807\u4808\u4809\u480A\u480B"+ + "\u480C\u480D\u480E\u480F\u4810\u4811\u4812\u4813"+ + "\u4814\u4815\u4816\u4817\u4818\u4819\u481A\u481B"+ + "\u481C\u481D\u481E\u481F\u4820\u4821\u4822\u4823"+ + "\u4824\u4825\u4826\u4827\u4828\u4829\u482A\u482B"+ + "\u482C\u482D\u482E\u482F\u4830\u4831\u4832\u4833"+ + "\u4834\u4835\u4836\u4837\u4838\u4839\u483A\u483B"+ + "\u483C\u483D\u483E\u483F\u4840\u4841\u4842\u4843"+ + "\u4844\u4845\u4846\u4847\u4848\u4849\u484A\u484B"+ + "\u484C\u484D\u484E\u484F\u4850\u4851\u4852\u4853"+ + "\u4854\u4855\u4856\u4857\u4858\u4859\u485A\u485B"+ + "\u485C\u485D\u485E\u485F\u4860\u4861\u4862\u4863"+ + "\u4864\u4865\u4866\u4867\u4868\u4869\u486A\u486B"+ + "\u486C\u486D\u486E\u486F\u4870\u4871\u4872\u4873"+ + "\u4874\u4875\u4876\u4877\u4878\u4879\u487A\u487B"+ + "\u487C\u487D\u487E\u487F\u4880\u4881\u4882\u4883"+ + "\u4884\u4885\u4886\u4887\u4888\u4889\u488A\u488B"+ + "\u488C\u488D\u488E\u488F\u4890\u4891\u4892\u4893"+ + "\u4894\u4895\u4896\u4897\u4898\u4899\u489A\u489B"+ + "\u489C\u489D\u489E\u489F\u48A0\u48A1\u48A2\u48A3"+ + "\u48A4\u48A5\u48A6\u48A7\u48A8\u48A9\u48AA\u48AB"+ + "\u48AC\u48AD\u48AE\u48AF\u48B0\u48B1\u48B2\u48B3"+ + "\u48B4\u48B5\u48B6\u48B7\u48B8\u48B9\u48BA\u48BB"+ + "\u48BC\u48BD\u48BE\u48BF\u48C0\u48C1\u48C2\u48C3"+ + "\u48C4\u48C5\u48C6\u48C7\u48C8\u48C9\u48CA\u48CB"+ + "\u48CC\u48CD\u48CE\u48CF\u48D0\u48D1\u48D2\u48D3"+ + "\u48D4\u48D5\u48D6\u48D7\u48D8\u48D9\u48DA\u48DB"+ + "\u48DC\u48DD\u48DE\u48DF\u48E0\u48E1\u48E2\u48E3"+ + "\u48E4\u48E5\u48E6\u48E7\u48E8\u48E9\u48EA\u48EB"+ + "\u48EC\u48ED\u48EE\u48EF\u48F0\u48F1\u48F2\u48F3"+ + "\u48F4\u48F5\u48F6\u48F7\u48F8\u48F9\u48FA\u48FB"+ + "\u48FC\u48FD\u48FE\u48FF\u4900\u4901\u4902\u4903"+ + "\u4904\u4905\u4906\u4907\u4908\u4909\u490A\u490B"+ + "\u490C\u490D\u490E\u490F\u4910\u4911\u4912\u4913"+ + "\u4914\u4915\u4916\u4917\u4918\u4919\u491A\u491B"+ + "\u491C\u491D\u491E\u491F\u4920\u4921\u4922\u4923"+ + "\u4924\u4925\u4926\u4927\u4928\u4929\u492A\u492B"+ + "\u492C\u492D\u492E\u492F\u4930\u4931\u4932\u4933"+ + "\u4934\u4935\u4936\u4937\u4938\u4939\u493A\u493B"+ + "\u493C\u493D\u493E\u493F\u4940\u4941\u4942\u4943"+ + "\u4944\u4945\u4946\u4948\u4949\u494A\u494B\u494C"+ + "\u494D\u494E\u494F\u4950\u4951\u4952\u4953\u4954"+ + "\u4955\u4956\u4957\u4958\u4959\u495A\u495B\u495C"+ + "\u495D\u495E\u495F\u4960\u4961\u4962\u4963\u4964"+ + "\u4965\u4966\u4967\u4968\u4969\u496A\u496B\u496C"+ + "\u496D\u496E\u496F\u4970\u4971\u4972\u4973\u4974"+ + "\u4975\u4976\u4977\u4978\u4979\u497B\u497C\u497E"+ + "\u497F\u4980\u4981\u4984\u4987\u4988\u4989\u498A"+ + "\u498B\u498C\u498D\u498E\u498F\u4990\u4991\u4992"+ + "\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499A"+ + "\u499C\u499D\u499E\u49A0\u49A1\u49A2\u49A3\u49A4"+ + "\u49A5\u49A6\u49A7\u49A8\u49A9\u49AA\u49AB\u49AC"+ + "\u49AD\u49AE\u49AF\u49B0\u49B1\u49B2\u49B3\u49B4"+ + "\u49B5\u49B8\u49B9\u49BA\u49BB\u49BC\u49BD\u49BE"+ + "\u49BF\u49C0\u49C1\u49C2\u49C3\u49C4\u49C5\u49C6"+ + "\u49C7\u49C8\u49C9\u49CA\u49CB\u49CC\u49CD\u49CE"+ + "\u49CF\u49D0\u49D1\u49D2\u49D3\u49D4\u49D5\u49D6"+ + "\u49D7\u49D8\u49D9\u49DA\u49DB\u49DC\u49DD\u49DE"+ + "\u49DF\u49E0\u49E1\u49E2\u49E3\u49E4\u49E5\u49E6"+ + "\u49E7\u49E8\u49E9\u49EA\u49EB\u49EC\u49ED\u49EE"+ + "\u49EF\u49F0\u49F1\u49F2\u49F3\u49F4\u49F5\u49F6"+ + "\u49F7\u49F8\u49F9\u49FA\u49FB\u49FC\u49FD\u49FE"+ + "\u49FF\u4A00\u4A01\u4A02\u4A03\u4A04\u4A05\u4A06"+ + "\u4A07\u4A08\u4A09\u4A0A\u4A0B\u4A0C\u4A0D\u4A0E"+ + "\u4A0F\u4A10\u4A11\u4A12\u4A13\u4A14\u4A15\u4A16"+ + "\u4A17\u4A18\u4A19\u4A1A\u4A1B\u4A1C\u4A1D\u4A1E"+ + "\u4A1F\u4A20\u4A21\u4A22\u4A23\u4A24\u4A25\u4A26"+ + "\u4A27\u4A28\u4A29\u4A2A\u4A2B\u4A2C\u4A2D\u4A2E"+ + "\u4A2F\u4A30\u4A31\u4A32\u4A33\u4A34\u4A35\u4A36"+ + "\u4A37\u4A38\u4A39\u4A3A\u4A3B\u4A3C\u4A3D\u4A3E"+ + "\u4A3F\u4A40\u4A41\u4A42\u4A43\u4A44\u4A45\u4A46"+ + "\u4A47\u4A48\u4A49\u4A4A\u4A4B\u4A4C\u4A4D\u4A4E"+ + "\u4A4F\u4A50\u4A51\u4A52\u4A53\u4A54\u4A55\u4A56"+ + "\u4A57\u4A58\u4A59\u4A5A\u4A5B\u4A5C\u4A5D\u4A5E"+ + "\u4A5F\u4A60\u4A61\u4A62\u4A63\u4A64\u4A65\u4A66"+ + "\u4A67\u4A68\u4A69\u4A6A\u4A6B\u4A6C\u4A6D\u4A6E"+ + "\u4A6F\u4A70\u4A71\u4A72\u4A73\u4A74\u4A75\u4A76"+ + "\u4A77\u4A78\u4A79\u4A7A\u4A7B\u4A7C\u4A7D\u4A7E"+ + "\u4A7F\u4A80\u4A81\u4A82\u4A83\u4A84\u4A85\u4A86"+ + "\u4A87\u4A88\u4A89\u4A8A\u4A8B\u4A8C\u4A8D\u4A8E"+ + "\u4A8F\u4A90\u4A91\u4A92\u4A93\u4A94\u4A95\u4A96"+ + "\u4A97\u4A98\u4A99\u4A9A\u4A9B\u4A9C\u4A9D\u4A9E"+ + "\u4A9F\u4AA0\u4AA1\u4AA2\u4AA3\u4AA4\u4AA5\u4AA6"+ + "\u4AA7\u4AA8\u4AA9\u4AAA\u4AAB\u4AAC\u4AAD\u4AAE"+ + "\u4AAF\u4AB0\u4AB1\u4AB2\u4AB3\u4AB4\u4AB5\u4AB6"+ + "\u4AB7\u4AB8\u4AB9\u4ABA\u4ABB\u4ABC\u4ABD\u4ABE"+ + "\u4ABF\u4AC0\u4AC1\u4AC2\u4AC3\u4AC4\u4AC5\u4AC6"+ + "\u4AC7\u4AC8\u4AC9\u4ACA\u4ACB\u4ACC\u4ACD\u4ACE"+ + "\u4ACF\u4AD0\u4AD1\u4AD2\u4AD3\u4AD4\u4AD5\u4AD6"+ + "\u4AD7\u4AD8\u4AD9\u4ADA\u4ADB\u4ADC\u4ADD\u4ADE"+ + "\u4ADF\u4AE0\u4AE1\u4AE2\u4AE3\u4AE4\u4AE5\u4AE6"+ + "\u4AE7\u4AE8\u4AE9\u4AEA\u4AEB\u4AEC\u4AED\u4AEE"+ + "\u4AEF\u4AF0\u4AF1\u4AF2\u4AF3\u4AF4\u4AF5\u4AF6"+ + "\u4AF7\u4AF8\u4AF9\u4AFA\u4AFB\u4AFC\u4AFD\u4AFE"+ + "\u4AFF\u4B00\u4B01\u4B02\u4B03\u4B04\u4B05\u4B06"+ + "\u4B07\u4B08\u4B09\u4B0A\u4B0B\u4B0C\u4B0D\u4B0E"+ + "\u4B0F\u4B10\u4B11\u4B12\u4B13\u4B14\u4B15\u4B16"+ + "\u4B17\u4B18\u4B19\u4B1A\u4B1B\u4B1C\u4B1D\u4B1E"+ + "\u4B1F\u4B20\u4B21\u4B22\u4B23\u4B24\u4B25\u4B26"+ + "\u4B27\u4B28\u4B29\u4B2A\u4B2B\u4B2C\u4B2D\u4B2E"+ + "\u4B2F\u4B30\u4B31\u4B32\u4B33\u4B34\u4B35\u4B36"+ + "\u4B37\u4B38\u4B39\u4B3A\u4B3B\u4B3C\u4B3D\u4B3E"+ + "\u4B3F\u4B40\u4B41\u4B42\u4B43\u4B44\u4B45\u4B46"+ + "\u4B47\u4B48\u4B49\u4B4A\u4B4B\u4B4C\u4B4D\u4B4E"+ + "\u4B4F\u4B50\u4B51\u4B52\u4B53\u4B54\u4B55\u4B56"+ + "\u4B57\u4B58\u4B59\u4B5A\u4B5B\u4B5C\u4B5D\u4B5E"+ + "\u4B5F\u4B60\u4B61\u4B62\u4B63\u4B64\u4B65\u4B66"+ + "\u4B67\u4B68\u4B69\u4B6A\u4B6B\u4B6C\u4B6D\u4B6E"+ + "\u4B6F\u4B70\u4B71\u4B72\u4B73\u4B74\u4B75\u4B76"+ + "\u4B77\u4B78\u4B79\u4B7A\u4B7B\u4B7C\u4B7D\u4B7E"+ + "\u4B7F\u4B80\u4B81\u4B82\u4B83\u4B84\u4B85\u4B86"+ + "\u4B87\u4B88\u4B89\u4B8A\u4B8B\u4B8C\u4B8D\u4B8E"+ + "\u4B8F\u4B90\u4B91\u4B92\u4B93\u4B94\u4B95\u4B96"+ + "\u4B97\u4B98\u4B99\u4B9A\u4B9B\u4B9C\u4B9D\u4B9E"+ + "\u4B9F\u4BA0\u4BA1\u4BA2\u4BA3\u4BA4\u4BA5\u4BA6"+ + "\u4BA7\u4BA8\u4BA9\u4BAA\u4BAB\u4BAC\u4BAD\u4BAE"+ + "\u4BAF\u4BB0\u4BB1\u4BB2\u4BB3\u4BB4\u4BB5\u4BB6"+ + "\u4BB7\u4BB8\u4BB9\u4BBA\u4BBB\u4BBC\u4BBD\u4BBE"+ + "\u4BBF\u4BC0\u4BC1\u4BC2\u4BC3\u4BC4\u4BC5\u4BC6"+ + "\u4BC7\u4BC8\u4BC9\u4BCA\u4BCB\u4BCC\u4BCD\u4BCE"+ + "\u4BCF\u4BD0\u4BD1\u4BD2\u4BD3\u4BD4\u4BD5\u4BD6"+ + "\u4BD7\u4BD8\u4BD9\u4BDA\u4BDB\u4BDC\u4BDD\u4BDE"+ + "\u4BDF\u4BE0\u4BE1\u4BE2\u4BE3\u4BE4\u4BE5\u4BE6"+ + "\u4BE7\u4BE8\u4BE9\u4BEA\u4BEB\u4BEC\u4BED\u4BEE"+ + "\u4BEF\u4BF0\u4BF1\u4BF2\u4BF3\u4BF4\u4BF5\u4BF6"+ + "\u4BF7\u4BF8\u4BF9\u4BFA\u4BFB\u4BFC\u4BFD\u4BFE"+ + "\u4BFF\u4C00\u4C01\u4C02\u4C03\u4C04\u4C05\u4C06"+ + "\u4C07\u4C08\u4C09\u4C0A\u4C0B\u4C0C\u4C0D\u4C0E"+ + "\u4C0F\u4C10\u4C11\u4C12\u4C13\u4C14\u4C15\u4C16"+ + "\u4C17\u4C18\u4C19\u4C1A\u4C1B\u4C1C\u4C1D\u4C1E"+ + "\u4C1F\u4C20\u4C21\u4C22\u4C23\u4C24\u4C25\u4C26"+ + "\u4C27\u4C28\u4C29\u4C2A\u4C2B\u4C2C\u4C2D\u4C2E"+ + "\u4C2F\u4C30\u4C31\u4C32\u4C33\u4C34\u4C35\u4C36"+ + "\u4C37\u4C38\u4C39\u4C3A\u4C3B\u4C3C\u4C3D\u4C3E"+ + "\u4C3F\u4C40\u4C41\u4C42\u4C43\u4C44\u4C45\u4C46"+ + "\u4C47\u4C48\u4C49\u4C4A\u4C4B\u4C4C\u4C4D\u4C4E"+ + "\u4C4F\u4C50\u4C51\u4C52\u4C53\u4C54\u4C55\u4C56"+ + "\u4C57\u4C58\u4C59\u4C5A\u4C5B\u4C5C\u4C5D\u4C5E"+ + "\u4C5F\u4C60\u4C61\u4C62\u4C63\u4C64\u4C65\u4C66"+ + "\u4C67\u4C68\u4C69\u4C6A\u4C6B\u4C6C\u4C6D\u4C6E"+ + "\u4C6F\u4C70\u4C71\u4C72\u4C73\u4C74\u4C75\u4C76"+ + "\u4C78\u4C79\u4C7A\u4C7B\u4C7C\u4C7D\u4C7E\u4C7F"+ + "\u4C80\u4C81\u4C82\u4C83\u4C84\u4C85\u4C86\u4C87"+ + "\u4C88\u4C89\u4C8A\u4C8B\u4C8C\u4C8D\u4C8E\u4C8F"+ + "\u4C90\u4C91\u4C92\u4C93\u4C94\u4C95\u4C96\u4C97"+ + "\u4C98\u4C99\u4C9A\u4C9B\u4C9C\u4C9D\u4C9E\u4CA4"+ + "\u4CA5\u4CA6\u4CA7\u4CA8\u4CA9\u4CAA\u4CAB\u4CAC"+ + "\u4CAD\u4CAE\u4CAF\u4CB0\u4CB1\u4CB2\u4CB3\u4CB4"+ + "\u4CB5\u4CB6\u4CB7\u4CB8\u4CB9\u4CBA\u4CBB\u4CBC"+ + "\u4CBD\u4CBE\u4CBF\u4CC0\u4CC1\u4CC2\u4CC3\u4CC4"+ + "\u4CC5\u4CC6\u4CC7\u4CC8\u4CC9\u4CCA\u4CCB\u4CCC"+ + "\u4CCD\u4CCE\u4CCF\u4CD0\u4CD1\u4CD2\u4CD3\u4CD4"+ + "\u4CD5\u4CD6\u4CD7\u4CD8\u4CD9\u4CDA\u4CDB\u4CDC"+ + "\u4CDD\u4CDE\u4CDF\u4CE0\u4CE1\u4CE2\u4CE3\u4CE4"+ + "\u4CE5\u4CE6\u4CE7\u4CE8\u4CE9\u4CEA\u4CEB\u4CEC"+ + "\u4CED\u4CEE\u4CEF\u4CF0\u4CF1\u4CF2\u4CF3\u4CF4"+ + "\u4CF5\u4CF6\u4CF7\u4CF8\u4CF9\u4CFA\u4CFB\u4CFC"+ + "\u4CFD\u4CFE\u4CFF\u4D00\u4D01\u4D02\u4D03\u4D04"+ + "\u4D05\u4D06\u4D07\u4D08\u4D09\u4D0A\u4D0B\u4D0C"+ + "\u4D0D\u4D0E\u4D0F\u4D10\u4D11\u4D12\u4D1A\u4D1B"+ + "\u4D1C\u4D1D\u4D1E\u4D1F\u4D20\u4D21\u4D22\u4D23"+ + "\u4D24\u4D25\u4D26\u4D27\u4D28\u4D29\u4D2A\u4D2B"+ + "\u4D2C\u4D2D\u4D2E\u4D2F\u4D30\u4D31\u4D32\u4D33"+ + "\u4D34\u4D35\u4D36\u4D37\u4D38\u4D39\u4D3A\u4D3B"+ + "\u4D3C\u4D3D\u4D3E\u4D3F\u4D40\u4D41\u4D42\u4D43"+ + "\u4D44\u4D45\u4D46\u4D47\u4D48\u4D49\u4D4A\u4D4B"+ + "\u4D4C\u4D4D\u4D4E\u4D4F\u4D50\u4D51\u4D52\u4D53"+ + "\u4D54\u4D55\u4D56\u4D57\u4D58\u4D59\u4D5A\u4D5B"+ + "\u4D5C\u4D5D\u4D5E\u4D5F\u4D60\u4D61\u4D62\u4D63"+ + "\u4D64\u4D65\u4D66\u4D67\u4D68\u4D69\u4D6A\u4D6B"+ + "\u4D6C\u4D6D\u4D6E\u4D6F\u4D70\u4D71\u4D72\u4D73"+ + "\u4D74\u4D75\u4D76\u4D77\u4D78\u4D79\u4D7A\u4D7B"+ + "\u4D7C\u4D7D\u4D7E\u4D7F\u4D80\u4D81\u4D82\u4D83"+ + "\u4D84\u4D85\u4D86\u4D87\u4D88\u4D89\u4D8A\u4D8B"+ + "\u4D8C\u4D8D\u4D8E\u4D8F\u4D90\u4D91\u4D92\u4D93"+ + "\u4D94\u4D95\u4D96\u4D97\u4D98\u4D99\u4D9A\u4D9B"+ + "\u4D9C\u4D9D\u4D9E\u4D9F\u4DA0\u4DA1\u4DA2\u4DA3"+ + "\u4DA4\u4DA5\u4DA6\u4DA7\u4DA8\u4DA9\u4DAA\u4DAB"+ + "\u4DAC\u4DAD\u4DAF\u4DB0\u4DB1\u4DB2\u4DB3\u4DB4"+ + "\u4DB5\u4DB6\u4DB7\u4DB8\u4DB9\u4DBA\u4DBB\u4DBC"+ + "\u4DBD\u4DBE\u4DBF\u4DC0\u4DC1\u4DC2\u4DC3\u4DC4"+ + "\u4DC5\u4DC6\u4DC7\u4DC8\u4DC9\u4DCA\u4DCB\u4DCC"+ + "\u4DCD\u4DCE\u4DCF\u4DD0\u4DD1\u4DD2\u4DD3\u4DD4"+ + "\u4DD5\u4DD6\u4DD7\u4DD8\u4DD9\u4DDA\u4DDB\u4DDC"+ + "\u4DDD\u4DDE\u4DDF\u4DE0\u4DE1\u4DE2\u4DE3\u4DE4"+ + "\u4DE5\u4DE6\u4DE7\u4DE8\u4DE9\u4DEA\u4DEB\u4DEC"+ + "\u4DED\u4DEE\u4DEF\u4DF0\u4DF1\u4DF2\u4DF3\u4DF4"+ + "\u4DF5\u4DF6\u4DF7\u4DF8\u4DF9\u4DFA\u4DFB\u4DFC"+ + "\u4DFD\u4DFE\u4DFF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uE76C\uE7C8\uE7E7"+ + "\uE7E8\uE7E9\uE7EA\uE7EB\uE7EC\uE7ED\uE7EE\uE7EF"+ + "\uE7F0\uE7F1\uE7F2\uE7F3\uE815\uE819\uE81A\uE81B"+ + "\uE81C\uE81D\uE81F\uE820\uE821\uE822\uE823\uE824"+ + "\uE825\uE827\uE828\uE829\uE82A\uE82D\uE82E\uE82F"+ + "\uE830\uE833\uE834\uE835\uE836\uE837\uE838\uE839"+ + "\uE83A\uE83C\uE83D\uE83E\uE83F\uE840\uE841\uE842"+ + "\uE844\uE845\uE846\uE847\uE848\uE849\uE84A\uE84B"+ + "\uE84C\uE84D\uE84E\uE84F\uE850\uE851\uE852\uE853"+ + "\uE856\uE857\uE858\uE859\uE85A\uE85B\uE85C\uE85D"+ + "\uE85E\uE85F\uE860\uE861\uE862\uE863\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uF900\uF901\uF902\uF903\uF904\uF905\uF906"+ + "\uF907\uF908\uF909\uF90A\uF90B\uF90C\uF90D\uF90E"+ + "\uF90F\uF910\uF911\uF912\uF913\uF914\uF915\uF916"+ + "\uF917\uF918\uF919\uF91A\uF91B\uF91C\uF91D\uF91E"+ + "\uF91F\uF920\uF921\uF922\uF923\uF924\uF925\uF926"+ + "\uF927\uF928\uF929\uF92A\uF92B\uF92D\uF92E\uF92F"+ + "\uF930\uF931\uF932\uF933\uF934\uF935\uF936\uF937"+ + "\uF938\uF939\uF93A\uF93B\uF93C\uF93D\uF93E\uF93F"+ + "\uF940\uF941\uF942\uF943\uF944\uF945\uF946\uF947"+ + "\uF948\uF949\uF94A\uF94B\uF94C\uF94D\uF94E\uF94F"+ + "\uF950\uF951\uF952\uF953\uF954\uF955\uF956\uF957"+ + "\uF958\uF959\uF95A\uF95B\uF95C\uF95D\uF95E\uF95F"+ + "\uF960\uF961\uF962\uF963\uF964\uF965\uF966\uF967"+ + "\uF968\uF969\uF96A\uF96B\uF96C\uF96D\uF96E\uF96F"+ + "\uF970\uF971\uF972\uF973\uF974\uF975\uF976\uF977"+ + "\uF978\uF97A\uF97B\uF97C\uF97D\uF97E\uF97F\uF980"+ + "\uF981\uF982\uF983\uF984\uF985\uF986\uF987\uF988"+ + "\uF989\uF98A\uF98B\uF98C\uF98D\uF98E\uF98F\uF990"+ + "\uF991\uF992\uF993\uF994\uF996\uF997\uF998\uF999"+ + "\uF99A\uF99B\uF99C\uF99D\uF99E\uF99F\uF9A0\uF9A1"+ + "\uF9A2\uF9A3\uF9A4\uF9A5\uF9A6\uF9A7\uF9A8\uF9A9"+ + "\uF9AA\uF9AB\uF9AC\uF9AD\uF9AE\uF9AF\uF9B0\uF9B1"+ + "\uF9B2\uF9B3\uF9B4\uF9B5\uF9B6\uF9B7\uF9B8\uF9B9"+ + "\uF9BA\uF9BB\uF9BC\uF9BD\uF9BE\uF9BF\uF9C0\uF9C1"+ + "\uF9C2\uF9C3\uF9C4\uF9C5\uF9C6\uF9C7\uF9C8\uF9C9"+ + "\uF9CA\uF9CB\uF9CC\uF9CD\uF9CE\uF9CF\uF9D0\uF9D1"+ + "\uF9D2\uF9D3\uF9D4\uF9D5\uF9D6\uF9D7\uF9D8\uF9D9"+ + "\uF9DA\uF9DB\uF9DC\uF9DD\uF9DE\uF9DF\uF9E0\uF9E1"+ + "\uF9E2\uF9E3\uF9E4\uF9E5\uF9E6\uF9E8\uF9E9\uF9EA"+ + "\uF9EB\uF9EC\uF9ED\uF9EE\uF9EF\uF9F0\uF9F2\uF9F3"+ + "\uF9F4\uF9F5\uF9F6\uF9F7\uF9F8\uF9F9\uF9FA\uF9FB"+ + "\uF9FC\uF9FD\uF9FE\uF9FF\uFA00\uFA01\uFA02\uFA03"+ + "\uFA04\uFA05\uFA06\uFA07\uFA08\uFA09\uFA0A\uFA0B"+ + "\uFA10\uFA12\uFA15\uFA16\uFA17\uFA19\uFA1A\uFA1B"+ + "\uFA1C\uFA1D\uFA1E\uFA22\uFA25\uFA26\uFA2A\uFA2B"+ + "\uFA2C\uFA2D\uFA2E\uFA2F\uFA30\uFA31\uFA32\uFA33"+ + "\uFA34\uFA35\uFA36\uFA37\uFA38\uFA39\uFA3A\uFA3B"+ + "\uFA3C\uFA3D\uFA3E\uFA3F\uFA40\uFA41\uFA42\uFA43"+ + "\uFA44\uFA45\uFA46\uFA47\uFA48\uFA49\uFA4A\uFA4B"+ + "\uFA4C\uFA4D\uFA4E\uFA4F\uFA50\uFA51\uFA52\uFA53"+ + "\uFA54\uFA55\uFA56\uFA57\uFA58\uFA59\uFA5A\uFA5B"+ + "\uFA5C\uFA5D\uFA5E\uFA5F\uFA60\uFA61\uFA62\uFA63"+ + "\uFA64\uFA65\uFA66\uFA67\uFA68\uFA69\uFA6A\uFA6B"+ + "\uFA6C\uFA6D\uFA6E\uFA6F\uFA70\uFA71\uFA72\uFA73"+ + "\uFA74\uFA75\uFA76\uFA77\uFA78\uFA79\uFA7A\uFA7B"+ + "\uFA7C\uFA7D\uFA7E\uFA7F\uFA80\uFA81\uFA82\uFA83"+ + "\uFA84\uFA85\uFA86\uFA87\uFA88\uFA89\uFA8A\uFA8B"+ + "\uFA8C\uFA8D\uFA8E\uFA8F\uFA90\uFA91\uFA92\uFA93"+ + "\uFA94\uFA95\uFA96\uFA97\uFA98\uFA99\uFA9A\uFA9B"+ + "\uFA9C\uFA9D\uFA9E\uFA9F\uFAA0\uFAA1\uFAA2\uFAA3"+ + "\uFAA4\uFAA5\uFAA6\uFAA7\uFAA8\uFAA9\uFAAA\uFAAB"+ + "\uFAAC\uFAAD\uFAAE\uFAAF\uFAB0\uFAB1\uFAB2\uFAB3"+ + "\uFAB4\uFAB5\uFAB6\uFAB7\uFAB8\uFAB9\uFABA\uFABB"+ + "\uFABC\uFABD\uFABE\uFABF\uFAC0\uFAC1\uFAC2\uFAC3"+ + "\uFAC4\uFAC5\uFAC6\uFAC7\uFAC8\uFAC9\uFACA\uFACB"+ + "\uFACC\uFACD\uFACE\uFACF\uFAD0\uFAD1\uFAD2\uFAD3"+ + "\uFAD4\uFAD5\uFAD6\uFAD7\uFAD8\uFAD9\uFADA\uFADB"+ + "\uFADC\uFADD\uFADE\uFADF\uFAE0\uFAE1\uFAE2\uFAE3"+ + "\uFAE4\uFAE5\uFAE6\uFAE7\uFAE8\uFAE9\uFAEA\uFAEB"+ + "\uFAEC\uFAED\uFAEE\uFAEF\uFAF0\uFAF1\uFAF2\uFAF3"+ + "\uFAF4\uFAF5\uFAF6\uFAF7\uFAF8\uFAF9\uFAFA\uFAFB"+ + "\uFAFC\uFAFD\uFAFE\uFAFF\uFB00\uFB01\uFB02\uFB03"+ + "\uFB04\uFB05\uFB06\uFB07\uFB08\uFB09\uFB0A\uFB0B"+ + "\uFB0C\uFB0D\uFB0E\uFB0F\uFB10\uFB11\uFB12\uFB13"+ + "\uFB14\uFB15\uFB16\uFB17\uFB18\uFB19\uFB1A\uFB1B"+ + "\uFB1C\uFB1D\uFB1E\uFB1F\uFB20\uFB21\uFB22\uFB23"+ + "\uFB24\uFB25\uFB26\uFB27\uFB28\uFB29\uFB2A\uFB2B"+ + "\uFB2C\uFB2D\uFB2E\uFB2F\uFB30\uFB31\uFB32\uFB33"+ + "\uFB34\uFB35\uFB36\uFB37\uFB38\uFB39\uFB3A\uFB3B"+ + "\uFB3C\uFB3D\uFB3E\uFB3F\uFB40\uFB41\uFB42\uFB43"+ + "\uFB44\uFB45\uFB46\uFB47\uFB48\uFB49\uFB4A\uFB4B"+ + "\uFB4C\uFB4D\uFB4E\uFB4F\uFB50\uFB51\uFB52\uFB53"+ + "\uFB54\uFB55\uFB56\uFB57\uFB58\uFB59\uFB5A\uFB5B"+ + "\uFB5C\uFB5D\uFB5E\uFB5F\uFB60\uFB61\uFB62\uFB63"+ + "\uFB64\uFB65\uFB66\uFB67\uFB68\uFB69\uFB6A\uFB6B"; + + private final static String innerDecoderIndex5= + "\uFB6C\uFB6D\uFB6E\uFB6F\uFB70\uFB71\uFB72\uFB73"+ + "\uFB74\uFB75\uFB76\uFB77\uFB78\uFB79\uFB7A\uFB7B"+ + "\uFB7C\uFB7D\uFB7E\uFB7F\uFB80\uFB81\uFB82\uFB83"+ + "\uFB84\uFB85\uFB86\uFB87\uFB88\uFB89\uFB8A\uFB8B"+ + "\uFB8C\uFB8D\uFB8E\uFB8F\uFB90\uFB91\uFB92\uFB93"+ + "\uFB94\uFB95\uFB96\uFB97\uFB98\uFB99\uFB9A\uFB9B"+ + "\uFB9C\uFB9D\uFB9E\uFB9F\uFBA0\uFBA1\uFBA2\uFBA3"+ + "\uFBA4\uFBA5\uFBA6\uFBA7\uFBA8\uFBA9\uFBAA\uFBAB"+ + "\uFBAC\uFBAD\uFBAE\uFBAF\uFBB0\uFBB1\uFBB2\uFBB3"+ + "\uFBB4\uFBB5\uFBB6\uFBB7\uFBB8\uFBB9\uFBBA\uFBBB"+ + "\uFBBC\uFBBD\uFBBE\uFBBF\uFBC0\uFBC1\uFBC2\uFBC3"+ + "\uFBC4\uFBC5\uFBC6\uFBC7\uFBC8\uFBC9\uFBCA\uFBCB"+ + "\uFBCC\uFBCD\uFBCE\uFBCF\uFBD0\uFBD1\uFBD2\uFBD3"+ + "\uFBD4\uFBD5\uFBD6\uFBD7\uFBD8\uFBD9\uFBDA\uFBDB"+ + "\uFBDC\uFBDD\uFBDE\uFBDF\uFBE0\uFBE1\uFBE2\uFBE3"+ + "\uFBE4\uFBE5\uFBE6\uFBE7\uFBE8\uFBE9\uFBEA\uFBEB"+ + "\uFBEC\uFBED\uFBEE\uFBEF\uFBF0\uFBF1\uFBF2\uFBF3"+ + "\uFBF4\uFBF5\uFBF6\uFBF7\uFBF8\uFBF9\uFBFA\uFBFB"+ + "\uFBFC\uFBFD\uFBFE\uFBFF\uFC00\uFC01\uFC02\uFC03"+ + "\uFC04\uFC05\uFC06\uFC07\uFC08\uFC09\uFC0A\uFC0B"+ + "\uFC0C\uFC0D\uFC0E\uFC0F\uFC10\uFC11\uFC12\uFC13"+ + "\uFC14\uFC15\uFC16\uFC17\uFC18\uFC19\uFC1A\uFC1B"+ + "\uFC1C\uFC1D\uFC1E\uFC1F\uFC20\uFC21\uFC22\uFC23"+ + "\uFC24\uFC25\uFC26\uFC27\uFC28\uFC29\uFC2A\uFC2B"+ + "\uFC2C\uFC2D\uFC2E\uFC2F\uFC30\uFC31\uFC32\uFC33"+ + "\uFC34\uFC35\uFC36\uFC37\uFC38\uFC39\uFC3A\uFC3B"+ + "\uFC3C\uFC3D\uFC3E\uFC3F\uFC40\uFC41\uFC42\uFC43"+ + "\uFC44\uFC45\uFC46\uFC47\uFC48\uFC49\uFC4A\uFC4B"+ + "\uFC4C\uFC4D\uFC4E\uFC4F\uFC50\uFC51\uFC52\uFC53"+ + "\uFC54\uFC55\uFC56\uFC57\uFC58\uFC59\uFC5A\uFC5B"+ + "\uFC5C\uFC5D\uFC5E\uFC5F\uFC60\uFC61\uFC62\uFC63"+ + "\uFC64\uFC65\uFC66\uFC67\uFC68\uFC69\uFC6A\uFC6B"+ + "\uFC6C\uFC6D\uFC6E\uFC6F\uFC70\uFC71\uFC72\uFC73"+ + "\uFC74\uFC75\uFC76\uFC77\uFC78\uFC79\uFC7A\uFC7B"+ + "\uFC7C\uFC7D\uFC7E\uFC7F\uFC80\uFC81\uFC82\uFC83"+ + "\uFC84\uFC85\uFC86\uFC87\uFC88\uFC89\uFC8A\uFC8B"+ + "\uFC8C\uFC8D\uFC8E\uFC8F\uFC90\uFC91\uFC92\uFC93"+ + "\uFC94\uFC95\uFC96\uFC97\uFC98\uFC99\uFC9A\uFC9B"+ + "\uFC9C\uFC9D\uFC9E\uFC9F\uFCA0\uFCA1\uFCA2\uFCA3"+ + "\uFCA4\uFCA5\uFCA6\uFCA7\uFCA8\uFCA9\uFCAA\uFCAB"+ + "\uFCAC\uFCAD\uFCAE\uFCAF\uFCB0\uFCB1\uFCB2\uFCB3"+ + "\uFCB4\uFCB5\uFCB6\uFCB7\uFCB8\uFCB9\uFCBA\uFCBB"+ + "\uFCBC\uFCBD\uFCBE\uFCBF\uFCC0\uFCC1\uFCC2\uFCC3"+ + "\uFCC4\uFCC5\uFCC6\uFCC7\uFCC8\uFCC9\uFCCA\uFCCB"+ + "\uFCCC\uFCCD\uFCCE\uFCCF\uFCD0\uFCD1\uFCD2\uFCD3"+ + "\uFCD4\uFCD5\uFCD6\uFCD7\uFCD8\uFCD9\uFCDA\uFCDB"+ + "\uFCDC\uFCDD\uFCDE\uFCDF\uFCE0\uFCE1\uFCE2\uFCE3"+ + "\uFCE4\uFCE5\uFCE6\uFCE7\uFCE8\uFCE9\uFCEA\uFCEB"+ + "\uFCEC\uFCED\uFCEE\uFCEF\uFCF0\uFCF1\uFCF2\uFCF3"+ + "\uFCF4\uFCF5\uFCF6\uFCF7\uFCF8\uFCF9\uFCFA\uFCFB"+ + "\uFCFC\uFCFD\uFCFE\uFCFF\uFD00\uFD01\uFD02\uFD03"+ + "\uFD04\uFD05\uFD06\uFD07\uFD08\uFD09\uFD0A\uFD0B"+ + "\uFD0C\uFD0D\uFD0E\uFD0F\uFD10\uFD11\uFD12\uFD13"+ + "\uFD14\uFD15\uFD16\uFD17\uFD18\uFD19\uFD1A\uFD1B"+ + "\uFD1C\uFD1D\uFD1E\uFD1F\uFD20\uFD21\uFD22\uFD23"+ + "\uFD24\uFD25\uFD26\uFD27\uFD28\uFD29\uFD2A\uFD2B"+ + "\uFD2C\uFD2D\uFD2E\uFD2F\uFD30\uFD31\uFD32\uFD33"+ + "\uFD34\uFD35\uFD36\uFD37\uFD38\uFD39\uFD3A\uFD3B"+ + "\uFD3C\uFD3D\uFD3E\uFD3F\uFD40\uFD41\uFD42\uFD43"+ + "\uFD44\uFD45\uFD46\uFD47\uFD48\uFD49\uFD4A\uFD4B"+ + "\uFD4C\uFD4D\uFD4E\uFD4F\uFD50\uFD51\uFD52\uFD53"+ + "\uFD54\uFD55\uFD56\uFD57\uFD58\uFD59\uFD5A\uFD5B"+ + "\uFD5C\uFD5D\uFD5E\uFD5F\uFD60\uFD61\uFD62\uFD63"+ + "\uFD64\uFD65\uFD66\uFD67\uFD68\uFD69\uFD6A\uFD6B"+ + "\uFD6C\uFD6D\uFD6E\uFD6F\uFD70\uFD71\uFD72\uFD73"+ + "\uFD74\uFD75\uFD76\uFD77\uFD78\uFD79\uFD7A\uFD7B"+ + "\uFD7C\uFD7D\uFD7E\uFD7F\uFD80\uFD81\uFD82\uFD83"+ + "\uFD84\uFD85\uFD86\uFD87\uFD88\uFD89\uFD8A\uFD8B"+ + "\uFD8C\uFD8D\uFD8E\uFD8F\uFD90\uFD91\uFD92\uFD93"+ + "\uFD94\uFD95\uFD96\uFD97\uFD98\uFD99\uFD9A\uFD9B"+ + "\uFD9C\uFD9D\uFD9E\uFD9F\uFDA0\uFDA1\uFDA2\uFDA3"+ + "\uFDA4\uFDA5\uFDA6\uFDA7\uFDA8\uFDA9\uFDAA\uFDAB"+ + "\uFDAC\uFDAD\uFDAE\uFDAF\uFDB0\uFDB1\uFDB2\uFDB3"+ + "\uFDB4\uFDB5\uFDB6\uFDB7\uFDB8\uFDB9\uFDBA\uFDBB"+ + "\uFDBC\uFDBD\uFDBE\uFDBF\uFDC0\uFDC1\uFDC2\uFDC3"+ + "\uFDC4\uFDC5\uFDC6\uFDC7\uFDC8\uFDC9\uFDCA\uFDCB"+ + "\uFDCC\uFDCD\uFDCE\uFDCF\uFDD0\uFDD1\uFDD2\uFDD3"+ + "\uFDD4\uFDD5\uFDD6\uFDD7\uFDD8\uFDD9\uFDDA\uFDDB"+ + "\uFDDC\uFDDD\uFDDE\uFDDF\uFDE0\uFDE1\uFDE2\uFDE3"+ + "\uFDE4\uFDE5\uFDE6\uFDE7\uFDE8\uFDE9\uFDEA\uFDEB"+ + "\uFDEC\uFDED\uFDEE\uFDEF\uFDF0\uFDF1\uFDF2\uFDF3"+ + "\uFDF4\uFDF5\uFDF6\uFDF7\uFDF8\uFDF9\uFDFA\uFDFB"+ + "\uFDFC\uFDFD\uFDFE\uFDFF\uFE00\uFE01\uFE02\uFE03"+ + "\uFE04\uFE05\uFE06\uFE07\uFE08\uFE09\uFE0A\uFE0B"+ + "\uFE0C\uFE0D\uFE0E\uFE0F\uFE10\uFE11\uFE12\uFE13"+ + "\uFE14\uFE15\uFE16\uFE17\uFE18\uFE19\uFE1A\uFE1B"+ + "\uFE1C\uFE1D\uFE1E\uFE1F\uFE20\uFE21\uFE22\uFE23"+ + "\uFE24\uFE25\uFE26\uFE27\uFE28\uFE29\uFE2A\uFE2B"+ + "\uFE2C\uFE2D\uFE2E\uFE2F\uFE32\uFE45\uFE46\uFE47"+ + "\uFE48\uFE53\uFE58\uFE67\uFE6C\uFE6D\uFE6E\uFE6F"+ + "\uFE70\uFE71\uFE72\uFE73\uFE74\uFE75\uFE76\uFE77"+ + "\uFE78\uFE79\uFE7A\uFE7B\uFE7C\uFE7D\uFE7E\uFE7F"+ + "\uFE80\uFE81\uFE82\uFE83\uFE84\uFE85\uFE86\uFE87"+ + "\uFE88\uFE89\uFE8A\uFE8B\uFE8C\uFE8D\uFE8E\uFE8F"+ + "\uFE90\uFE91\uFE92\uFE93\uFE94\uFE95\uFE96\uFE97"+ + "\uFE98\uFE99\uFE9A\uFE9B\uFE9C\uFE9D\uFE9E\uFE9F"+ + "\uFEA0\uFEA1\uFEA2\uFEA3\uFEA4\uFEA5\uFEA6\uFEA7"+ + "\uFEA8\uFEA9\uFEAA\uFEAB\uFEAC\uFEAD\uFEAE\uFEAF"+ + "\uFEB0\uFEB1\uFEB2\uFEB3\uFEB4\uFEB5\uFEB6\uFEB7"+ + "\uFEB8\uFEB9\uFEBA\uFEBB\uFEBC\uFEBD\uFEBE\uFEBF"+ + "\uFEC0\uFEC1\uFEC2\uFEC3\uFEC4\uFEC5\uFEC6\uFEC7"+ + "\uFEC8\uFEC9\uFECA\uFECB\uFECC\uFECD\uFECE\uFECF"+ + "\uFED0\uFED1\uFED2\uFED3\uFED4\uFED5\uFED6\uFED7"+ + "\uFED8\uFED9\uFEDA\uFEDB\uFEDC\uFEDD\uFEDE\uFEDF"+ + "\uFEE0\uFEE1\uFEE2\uFEE3\uFEE4\uFEE5\uFEE6\uFEE7"+ + "\uFEE8\uFEE9\uFEEA\uFEEB\uFEEC\uFEED\uFEEE\uFEEF"+ + "\uFEF0\uFEF1\uFEF2\uFEF3\uFEF4\uFEF5\uFEF6\uFEF7"+ + "\uFEF8\uFEF9\uFEFA\uFEFB\uFEFC\uFEFD\uFEFE\uFEFF"+ + "\uFF00\uFF5F\uFF60\uFF61\uFF62\uFF63\uFF64\uFF65"+ + "\uFF66\uFF67\uFF68\uFF69\uFF6A\uFF6B\uFF6C\uFF6D"+ + "\uFF6E\uFF6F\uFF70\uFF71\uFF72\uFF73\uFF74\uFF75"+ + "\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D"+ + "\uFF7E\uFF7F\uFF80\uFF81\uFF82\uFF83\uFF84\uFF85"+ + "\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C\uFF8D"+ + "\uFF8E\uFF8F\uFF90\uFF91\uFF92\uFF93\uFF94\uFF95"+ + "\uFF96\uFF97\uFF98\uFF99\uFF9A\uFF9B\uFF9C\uFF9D"+ + "\uFF9E\uFF9F\uFFA0\uFFA1\uFFA2\uFFA3\uFFA4\uFFA5"+ + "\uFFA6\uFFA7\uFFA8\uFFA9\uFFAA\uFFAB\uFFAC\uFFAD"+ + "\uFFAE\uFFAF\uFFB0\uFFB1\uFFB2\uFFB3\uFFB4\uFFB5"+ + "\uFFB6\uFFB7\uFFB8\uFFB9\uFFBA\uFFBB\uFFBC\uFFBD"+ + "\uFFBE\uFFBF\uFFC0\uFFC1\uFFC2\uFFC3\uFFC4\uFFC5"+ + "\uFFC6\uFFC7\uFFC8\uFFC9\uFFCA\uFFCB\uFFCC\uFFCD"+ + "\uFFCE\uFFCF\uFFD0\uFFD1\uFFD2\uFFD3\uFFD4\uFFD5"+ + "\uFFD6\uFFD7\uFFD8\uFFD9\uFFDA\uFFDB\uFFDC\uFFDD"+ + "\uFFDE\uFFDF\uFFE6\uFFE7\uFFE8\uFFE9\uFFEA\uFFEB"+ + "\uFFEC\uFFED\uFFEE\uFFEF\uFFF0\uFFF1\uFFF2\uFFF3"+ + "\uFFF4\uFFF5\uFFF6\uFFF7\uFFF8\uFFF9\uFFFA\uFFFB"+ + "\uFFFC\uFFFD\uFFFE\uFFFF\uFFFD\uFFFD\uFFFD\uFFFD"; + + private final static short decoderIndex1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 75, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 77, 78, 79, 80, 81, 82, 83, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + static String decoderIndex2[] = { + innerDecoderIndex0, + innerDecoderIndex1, + innerDecoderIndex2, + innerDecoderIndex3, + innerDecoderIndex4, + innerDecoderIndex5 + }; + +/* + * + */ + private final static String innerIndex0= + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u4E02"+ + "\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20"+ + "\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33"+ + "\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46"+ + "\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63"+ + "\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D"+ + "\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78"+ + "\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81"+ + "\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A\uFFFD\u4E90"+ + "\u4E96\u4E97\u4E99\u4E9C\u4E9D\u4E9E\u4EA3\u4EAA"+ + "\u4EAF\u4EB0\u4EB1\u4EB4\u4EB6\u4EB7\u4EB8\u4EB9"+ + "\u4EBC\u4EBD\u4EBE\u4EC8\u4ECC\u4ECF\u4ED0\u4ED2"+ + "\u4EDA\u4EDB\u4EDC\u4EE0\u4EE2\u4EE6\u4EE7\u4EE9"+ + "\u4EED\u4EEE\u4EEF\u4EF1\u4EF4\u4EF8\u4EF9\u4EFA"+ + "\u4EFC\u4EFE\u4F00\u4F02\u4F03\u4F04\u4F05\u4F06"+ + "\u4F07\u4F08\u4F0B\u4F0C\u4F12\u4F13\u4F14\u4F15"+ + "\u4F16\u4F1C\u4F1D\u4F21\u4F23\u4F28\u4F29\u4F2C"+ + "\u4F2D\u4F2E\u4F31\u4F33\u4F35\u4F37\u4F39\u4F3B"+ + "\u4F3E\u4F3F\u4F40\u4F41\u4F42\u4F44\u4F45\u4F47"+ + "\u4F48\u4F49\u4F4A\u4F4B\u4F4C\u4F52\u4F54\u4F56"+ + "\u4F61\u4F62\u4F66\u4F68\u4F6A\u4F6B\u4F6D\u4F6E"+ + "\u4F71\u4F72\u4F75\u4F77\u4F78\u4F79\u4F7A\u4F7D"+ + "\u4F80\u4F81\u4F82\u4F85\u4F86\u4F87\u4F8A\u4F8C"+ + "\u4F8E\u4F90\u4F92\u4F93\u4F95\u4F96\u4F98\u4F99"+ + "\u4F9A\u4F9C\u4F9E\u4F9F\u4FA1\u4FA2\u4FA4\u4FAB"+ + "\u4FAD\u4FB0\u4FB1\u4FB2\u4FB3\u4FB4\u4FB6\u4FB7"+ + "\u4FB8\u4FB9\u4FBA\u4FBB\u4FBC\u4FBD\u4FBE\u4FC0"+ + "\u4FC1\u4FC2\u4FC6\u4FC7\u4FC8\u4FC9\u4FCB\u4FCC"+ + "\u4FCD\u4FD2\u4FD3\u4FD4\u4FD5\u4FD6\u4FD9\u4FDB"+ + "\u4FE0\u4FE2\u4FE4\u4FE5\u4FE7\u4FEB\u4FEC\u4FF0"+ + "\u4FF2\u4FF4\u4FF5\u4FF6\u4FF7\u4FF9\u4FFB\u4FFC"+ + "\u4FFD\u4FFF\u5000\u5001\u5002\u5003\u5004\u5005"+ + "\u5006\u5007\u5008\u5009\u500A\uFFFD\u500B\u500E"+ + "\u5010\u5011\u5013\u5015\u5016\u5017\u501B\u501D"+ + "\u501E\u5020\u5022\u5023\u5024\u5027\u502B\u502F"+ + "\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037"+ + "\u5038\u5039\u503B\u503D\u503F\u5040\u5041\u5042"+ + "\u5044\u5045\u5046\u5049\u504A\u504B\u504D\u5050"+ + "\u5051\u5052\u5053\u5054\u5056\u5057\u5058\u5059"+ + "\u505B\u505D\u505E\u505F\u5060\u5061\u5062\u5063"+ + "\u5064\u5066\u5067\u5068\u5069\u506A\u506B\u506D"+ + "\u506E\u506F\u5070\u5071\u5072\u5073\u5074\u5075"+ + "\u5078\u5079\u507A\u507C\u507D\u5081\u5082\u5083"+ + "\u5084\u5086\u5087\u5089\u508A\u508B\u508C\u508E"+ + "\u508F\u5090\u5091\u5092\u5093\u5094\u5095\u5096"+ + "\u5097\u5098\u5099\u509A\u509B\u509C\u509D\u509E"+ + "\u509F\u50A0\u50A1\u50A2\u50A4\u50A6\u50AA\u50AB"+ + "\u50AD\u50AE\u50AF\u50B0\u50B1\u50B3\u50B4\u50B5"+ + "\u50B6\u50B7\u50B8\u50B9\u50BC\u50BD\u50BE\u50BF"+ + "\u50C0\u50C1\u50C2\u50C3\u50C4\u50C5\u50C6\u50C7"+ + "\u50C8\u50C9\u50CA\u50CB\u50CC\u50CD\u50CE\u50D0"+ + "\u50D1\u50D2\u50D3\u50D4\u50D5\u50D7\u50D8\u50D9"+ + "\u50DB\u50DC\u50DD\u50DE\u50DF\u50E0\u50E1\u50E2"+ + "\u50E3\u50E4\u50E5\u50E8\u50E9\u50EA\u50EB\u50EF"+ + "\u50F0\u50F1\u50F2\u50F4\u50F6\u50F7\u50F8\u50F9"+ + "\u50FA\u50FC\u50FD\u50FE\u50FF\u5100\u5101\u5102"+ + "\u5103\u5104\u5105\u5108\uFFFD\u5109\u510A\u510C"+ + "\u510D\u510E\u510F\u5110\u5111\u5113\u5114\u5115"+ + "\u5116\u5117\u5118\u5119\u511A\u511B\u511C\u511D"+ + "\u511E\u511F\u5120\u5122\u5123\u5124\u5125\u5126"+ + "\u5127\u5128\u5129\u512A\u512B\u512C\u512D\u512E"+ + "\u512F\u5130\u5131\u5132\u5133\u5134\u5135\u5136"+ + "\u5137\u5138\u5139\u513A\u513B\u513C\u513D\u513E"+ + "\u5142\u5147\u514A\u514C\u514E\u514F\u5150\u5152"+ + "\u5153\u5157\u5158\u5159\u515B\u515D\u515E\u515F"+ + "\u5160\u5161\u5163\u5164\u5166\u5167\u5169\u516A"+ + "\u516F\u5172\u517A\u517E\u517F\u5183\u5184\u5186"+ + "\u5187\u518A\u518B\u518E\u518F\u5190\u5191\u5193"+ + "\u5194\u5198\u519A\u519D\u519E\u519F\u51A1\u51A3"+ + "\u51A6\u51A7\u51A8\u51A9\u51AA\u51AD\u51AE\u51B4"+ + "\u51B8\u51B9\u51BA\u51BE\u51BF\u51C1\u51C2\u51C3"+ + "\u51C5\u51C8\u51CA\u51CD\u51CE\u51D0\u51D2\u51D3"+ + "\u51D4\u51D5\u51D6\u51D7\u51D8\u51D9\u51DA\u51DC"+ + "\u51DE\u51DF\u51E2\u51E3\u51E5\u51E6\u51E7\u51E8"+ + "\u51E9\u51EA\u51EC\u51EE\u51F1\u51F2\u51F4\u51F7"+ + "\u51FE\u5204\u5205\u5209\u520B\u520C\u520F\u5210"+ + "\u5213\u5214\u5215\u521C\u521E\u521F\u5221\u5222"+ + "\u5223\u5225\u5226\u5227\u522A\u522C\u522F\u5231"+ + "\u5232\u5234\u5235\u523C\u523E\u5244\u5245\u5246"+ + "\u5247\u5248\u5249\u524B\u524E\u524F\u5252\u5253"+ + "\u5255\u5257\u5258\uFFFD\u5259\u525A\u525B\u525D"+ + "\u525F\u5260\u5262\u5263\u5264\u5266\u5268\u526B"+ + "\u526C\u526D\u526E\u5270\u5271\u5273\u5274\u5275"+ + "\u5276\u5277\u5278\u5279\u527A\u527B\u527C\u527E"+ + "\u5280\u5283\u5284\u5285\u5286\u5287\u5289\u528A"+ + "\u528B\u528C\u528D\u528E\u528F\u5291\u5292\u5294"+ + "\u5295\u5296\u5297\u5298\u5299\u529A\u529C\u52A4"+ + "\u52A5\u52A6\u52A7\u52AE\u52AF\u52B0\u52B4\u52B5"+ + "\u52B6\u52B7\u52B8\u52B9\u52BA\u52BB\u52BC\u52BD"+ + "\u52C0\u52C1\u52C2\u52C4\u52C5\u52C6\u52C8\u52CA"+ + "\u52CC\u52CD\u52CE\u52CF\u52D1\u52D3\u52D4\u52D5"+ + "\u52D7\u52D9\u52DA\u52DB\u52DC\u52DD\u52DE\u52E0"+ + "\u52E1\u52E2\u52E3\u52E5\u52E6\u52E7\u52E8\u52E9"+ + "\u52EA\u52EB\u52EC\u52ED\u52EE\u52EF\u52F1\u52F2"+ + "\u52F3\u52F4\u52F5\u52F6\u52F7\u52F8\u52FB\u52FC"+ + "\u52FD\u5301\u5302\u5303\u5304\u5307\u5309\u530A"+ + "\u530B\u530C\u530E\u5311\u5312\u5313\u5314\u5318"+ + "\u531B\u531C\u531E\u531F\u5322\u5324\u5325\u5327"+ + "\u5328\u5329\u532B\u532C\u532D\u532F\u5330\u5331"+ + "\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u533C"+ + "\u533D\u5340\u5342\u5344\u5346\u534B\u534C\u534D"+ + "\u5350\u5354\u5358\u5359\u535B\u535D\u5365\u5368"+ + "\u536A\u536C\u536D\u5372\u5376\u5379\u537B\u537C"+ + "\u537D\u537E\u5380\u5381\u5383\u5387\u5388\u538A"+ + "\u538E\u538F\uFFFD\u5390\u5391\u5392\u5393\u5394"+ + "\u5396\u5397\u5399\u539B\u539C\u539E\u53A0\u53A1"+ + "\u53A4\u53A7\u53AA\u53AB\u53AC\u53AD\u53AF\u53B0"+ + "\u53B1\u53B2\u53B3\u53B4\u53B5\u53B7\u53B8\u53B9"+ + "\u53BA\u53BC\u53BD\u53BE\u53C0\u53C3\u53C4\u53C5"+ + "\u53C6\u53C7\u53CE\u53CF\u53D0\u53D2\u53D3\u53D5"+ + "\u53DA\u53DC\u53DD\u53DE\u53E1\u53E2\u53E7\u53F4"+ + "\u53FA\u53FE\u53FF\u5400\u5402\u5405\u5407\u540B"+ + "\u5414\u5418\u5419\u541A\u541C\u5422\u5424\u5425"+ + "\u542A\u5430\u5433\u5436\u5437\u543A\u543D\u543F"+ + "\u5441\u5442\u5444\u5445\u5447\u5449\u544C\u544D"+ + "\u544E\u544F\u5451\u545A\u545D\u545E\u545F\u5460"+ + "\u5461\u5463\u5465\u5467\u5469\u546A\u546B\u546C"+ + "\u546D\u546E\u546F\u5470\u5474\u5479\u547A\u547E"+ + "\u547F\u5481\u5483\u5485\u5487\u5488\u5489\u548A"+ + "\u548D\u5491\u5493\u5497\u5498\u549C\u549E\u549F"+ + "\u54A0\u54A1\u54A2\u54A5\u54AE\u54B0\u54B2\u54B5"+ + "\u54B6\u54B7\u54B9\u54BA\u54BC\u54BE\u54C3\u54C5"+ + "\u54CA\u54CB\u54D6\u54D8\u54DB\u54E0\u54E1\u54E2"+ + "\u54E3\u54E4\u54EB\u54EC\u54EF\u54F0\u54F1\u54F4"+ + "\u54F5\u54F6\u54F7\u54F8\u54F9\u54FB\u54FE\u5500"+ + "\u5502\u5503\u5504\u5505\u5508\u550A\u550B\u550C"+ + "\u550D\u550E\u5512\u5513\u5515\u5516\u5517\u5518"+ + "\u5519\u551A\u551C\u551D\u551E\u551F\u5521\u5525"+ + "\u5526\uFFFD\u5528\u5529\u552B\u552D\u5532\u5534"+ + "\u5535\u5536\u5538\u5539\u553A\u553B\u553D\u5540"+ + "\u5542\u5545\u5547\u5548\u554B\u554C\u554D\u554E"+ + "\u554F\u5551\u5552\u5553\u5554\u5557\u5558\u5559"+ + "\u555A\u555B\u555D\u555E\u555F\u5560\u5562\u5563"+ + "\u5568\u5569\u556B\u556F\u5570\u5571\u5572\u5573"+ + "\u5574\u5579\u557A\u557D\u557F\u5585\u5586\u558C"+ + "\u558D\u558E\u5590\u5592\u5593\u5595\u5596\u5597"+ + "\u559A\u559B\u559E\u55A0\u55A1\u55A2\u55A3\u55A4"+ + "\u55A5\u55A6\u55A8\u55A9\u55AA\u55AB\u55AC\u55AD"+ + "\u55AE\u55AF\u55B0\u55B2\u55B4\u55B6\u55B8\u55BA"+ + "\u55BC\u55BF\u55C0\u55C1\u55C2\u55C3\u55C6\u55C7"+ + "\u55C8\u55CA\u55CB\u55CE\u55CF\u55D0\u55D5\u55D7"+ + "\u55D8\u55D9\u55DA\u55DB\u55DE\u55E0\u55E2\u55E7"+ + "\u55E9\u55ED\u55EE\u55F0\u55F1\u55F4\u55F6\u55F8"+ + "\u55F9\u55FA\u55FB\u55FC\u55FF\u5602\u5603\u5604"+ + "\u5605\u5606\u5607\u560A\u560B\u560D\u5610\u5611"+ + "\u5612\u5613\u5614\u5615\u5616\u5617\u5619\u561A"+ + "\u561C\u561D\u5620\u5621\u5622\u5625\u5626\u5628"+ + "\u5629\u562A\u562B\u562E\u562F\u5630\u5633\u5635"+ + "\u5637\u5638\u563A\u563C\u563D\u563E\u5640\u5641"+ + "\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649"+ + "\u564A\u564B\u564F\u5650\u5651\u5652\u5653\u5655"+ + "\u5656\u565A\u565B\u565D\u565E\u565F\u5660\u5661"+ + "\uFFFD\u5663\u5665\u5666\u5667\u566D\u566E\u566F"+ + "\u5670\u5672\u5673\u5674\u5675\u5677\u5678\u5679"+ + "\u567A\u567D\u567E\u567F\u5680\u5681\u5682\u5683"+ + "\u5684\u5687\u5688\u5689\u568A\u568B\u568C\u568D"+ + "\u5690\u5691\u5692\u5694\u5695\u5696\u5697\u5698"+ + "\u5699\u569A\u569B\u569C\u569D\u569E\u569F\u56A0"+ + "\u56A1\u56A2\u56A4\u56A5\u56A6\u56A7\u56A8\u56A9"+ + "\u56AA\u56AB\u56AC\u56AD\u56AE\u56B0\u56B1\u56B2"+ + "\u56B3\u56B4\u56B5\u56B6\u56B8\u56B9\u56BA\u56BB"+ + "\u56BD\u56BE\u56BF\u56C0\u56C1\u56C2\u56C3\u56C4"+ + "\u56C5\u56C6\u56C7\u56C8\u56C9\u56CB\u56CC\u56CD"+ + "\u56CE\u56CF\u56D0\u56D1\u56D2\u56D3\u56D5\u56D6"+ + "\u56D8\u56D9\u56DC\u56E3\u56E5\u56E6\u56E7\u56E8"+ + "\u56E9\u56EA\u56EC\u56EE\u56EF\u56F2\u56F3\u56F6"+ + "\u56F7\u56F8\u56FB\u56FC\u5700\u5701\u5702\u5705"+ + "\u5707\u570B\u570C\u570D\u570E\u570F\u5710\u5711"+ + "\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719"+ + "\u571A\u571B\u571D\u571E\u5720\u5721\u5722\u5724"+ + "\u5725\u5726\u5727\u572B\u5731\u5732\u5734\u5735"+ + "\u5736\u5737\u5738\u573C\u573D\u573F\u5741\u5743"+ + "\u5744\u5745\u5746\u5748\u5749\u574B\u5752\u5753"+ + "\u5754\u5755\u5756\u5758\u5759\u5762\u5763\u5765"+ + "\u5767\u576C\u576E\u5770\u5771\u5772\u5774\u5775"+ + "\u5778\u5779\u577A\u577D\u577E\u577F\u5780\uFFFD"+ + "\u5781\u5787\u5788\u5789\u578A\u578D\u578E\u578F"+ + "\u5790\u5791\u5794\u5795\u5796\u5797\u5798\u5799"+ + "\u579A\u579C\u579D\u579E\u579F\u57A5\u57A8\u57AA"+ + "\u57AC\u57AF\u57B0\u57B1\u57B3\u57B5\u57B6\u57B7"+ + "\u57B9\u57BA\u57BB\u57BC\u57BD\u57BE\u57BF\u57C0"+ + "\u57C1\u57C4\u57C5\u57C6\u57C7\u57C8\u57C9\u57CA"+ + "\u57CC\u57CD\u57D0\u57D1\u57D3\u57D6\u57D7\u57DB"+ + "\u57DC\u57DE\u57E1\u57E2\u57E3\u57E5\u57E6\u57E7"+ + "\u57E8\u57E9\u57EA\u57EB\u57EC\u57EE\u57F0\u57F1"+ + "\u57F2\u57F3\u57F5\u57F6\u57F7\u57FB\u57FC\u57FE"+ + "\u57FF\u5801\u5803\u5804\u5805\u5808\u5809\u580A"+ + "\u580C\u580E\u580F\u5810\u5812\u5813\u5814\u5816"+ + "\u5817\u5818\u581A\u581B\u581C\u581D\u581F\u5822"+ + "\u5823\u5825\u5826\u5827\u5828\u5829\u582B\u582C"+ + "\u582D\u582E\u582F\u5831\u5832\u5833\u5834\u5836"+ + "\u5837\u5838\u5839\u583A\u583B\u583C\u583D\u583E"+ + "\u583F\u5840\u5841\u5842\u5843\u5845\u5846\u5847"+ + "\u5848\u5849\u584A\u584B\u584E\u584F\u5850\u5852"+ + "\u5853\u5855\u5856\u5857\u5859\u585A\u585B\u585C"+ + "\u585D\u585F\u5860\u5861\u5862\u5863\u5864\u5866"+ + "\u5867\u5868\u5869\u586A\u586D\u586E\u586F\u5870"+ + "\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878"+ + "\u5879\u587A\u587B\u587C\u587D\u587F\u5882\u5884"+ + "\u5886\u5887\u5888\u588A\u588B\u588C\uFFFD\u588D"+ + "\u588E\u588F\u5890\u5891\u5894\u5895\u5896\u5897"+ + "\u5898\u589B\u589C\u589D\u58A0\u58A1\u58A2\u58A3"+ + "\u58A4\u58A5\u58A6\u58A7\u58AA\u58AB\u58AC\u58AD"+ + "\u58AE\u58AF\u58B0\u58B1\u58B2\u58B3\u58B4\u58B5"+ + "\u58B6\u58B7\u58B8\u58B9\u58BA\u58BB\u58BD\u58BE"+ + "\u58BF\u58C0\u58C2\u58C3\u58C4\u58C6\u58C7\u58C8"+ + "\u58C9\u58CA\u58CB\u58CC\u58CD\u58CE\u58CF\u58D0"+ + "\u58D2\u58D3\u58D4\u58D6\u58D7\u58D8\u58D9\u58DA"+ + "\u58DB\u58DC\u58DD\u58DE\u58DF\u58E0\u58E1\u58E2"+ + "\u58E3\u58E5\u58E6\u58E7\u58E8\u58E9\u58EA\u58ED"+ + "\u58EF\u58F1\u58F2\u58F4\u58F5\u58F7\u58F8\u58FA"+ + "\u58FB\u58FC\u58FD\u58FE\u58FF\u5900\u5901\u5903"+ + "\u5905\u5906\u5908\u5909\u590A\u590B\u590C\u590E"+ + "\u5910\u5911\u5912\u5913\u5917\u5918\u591B\u591D"+ + "\u591E\u5920\u5921\u5922\u5923\u5926\u5928\u592C"+ + "\u5930\u5932\u5933\u5935\u5936\u593B\u593D\u593E"+ + "\u593F\u5940\u5943\u5945\u5946\u594A\u594C\u594D"+ + "\u5950\u5952\u5953\u5959\u595B\u595C\u595D\u595E"+ + "\u595F\u5961\u5963\u5964\u5966\u5967\u5968\u5969"+ + "\u596A\u596B\u596C\u596D\u596E\u596F\u5970\u5971"+ + "\u5972\u5975\u5977\u597A\u597B\u597C\u597E\u597F"+ + "\u5980\u5985\u5989\u598B\u598C\u598E\u598F\u5990"+ + "\u5991\u5994\u5995\u5998\u599A\u599B\u599C\u599D"+ + "\u599F\u59A0\u59A1\u59A2\u59A6\uFFFD\u59A7\u59AC"+ + "\u59AD\u59B0\u59B1\u59B3\u59B4\u59B5\u59B6\u59B7"+ + "\u59B8\u59BA\u59BC\u59BD\u59BF\u59C0\u59C1\u59C2"+ + "\u59C3\u59C4\u59C5\u59C7\u59C8\u59C9\u59CC\u59CD"+ + "\u59CE\u59CF\u59D5\u59D6\u59D9\u59DB\u59DE\u59DF"+ + "\u59E0\u59E1\u59E2\u59E4\u59E6\u59E7\u59E9\u59EA"+ + "\u59EB\u59ED\u59EE\u59EF\u59F0\u59F1\u59F2\u59F3"+ + "\u59F4\u59F5\u59F6\u59F7\u59F8\u59FA\u59FC\u59FD"+ + "\u59FE\u5A00\u5A02\u5A0A\u5A0B\u5A0D\u5A0E\u5A0F"+ + "\u5A10\u5A12\u5A14\u5A15\u5A16\u5A17\u5A19\u5A1A"+ + "\u5A1B\u5A1D\u5A1E\u5A21\u5A22\u5A24\u5A26\u5A27"+ + "\u5A28\u5A2A\u5A2B\u5A2C\u5A2D\u5A2E\u5A2F\u5A30"+ + "\u5A33\u5A35\u5A37\u5A38\u5A39\u5A3A\u5A3B\u5A3D"+ + "\u5A3E\u5A3F\u5A41\u5A42\u5A43\u5A44\u5A45\u5A47"+ + "\u5A48\u5A4B\u5A4C\u5A4D\u5A4E\u5A4F\u5A50\u5A51"+ + "\u5A52\u5A53\u5A54\u5A56\u5A57\u5A58\u5A59\u5A5B"+ + "\u5A5C\u5A5D\u5A5E\u5A5F\u5A60\u5A61\u5A63\u5A64"+ + "\u5A65\u5A66\u5A68\u5A69\u5A6B\u5A6C\u5A6D\u5A6E"+ + "\u5A6F\u5A70\u5A71\u5A72\u5A73\u5A78\u5A79\u5A7B"+ + "\u5A7C\u5A7D\u5A7E\u5A80\u5A81\u5A82\u5A83\u5A84"+ + "\u5A85\u5A86\u5A87\u5A88\u5A89\u5A8A\u5A8B\u5A8C"+ + "\u5A8D\u5A8E\u5A8F\u5A90\u5A91\u5A93\u5A94\u5A95"+ + "\u5A96\u5A97\u5A98\u5A99\u5A9C\u5A9D\u5A9E\u5A9F"+ + "\u5AA0\u5AA1\u5AA2\u5AA3\u5AA4\u5AA5\u5AA6\u5AA7"+ + "\u5AA8\u5AA9\u5AAB\u5AAC\uFFFD\u5AAD\u5AAE\u5AAF"+ + "\u5AB0\u5AB1\u5AB4\u5AB6\u5AB7\u5AB9\u5ABA\u5ABB"+ + "\u5ABC\u5ABD\u5ABF\u5AC0\u5AC3\u5AC4\u5AC5\u5AC6"+ + "\u5AC7\u5AC8\u5ACA\u5ACB\u5ACD\u5ACE\u5ACF\u5AD0"+ + "\u5AD1\u5AD3\u5AD5\u5AD7\u5AD9\u5ADA\u5ADB\u5ADD"+ + "\u5ADE\u5ADF\u5AE2\u5AE4\u5AE5\u5AE7\u5AE8\u5AEA"+ + "\u5AEC\u5AED\u5AEE\u5AEF\u5AF0\u5AF2\u5AF3\u5AF4"+ + "\u5AF5\u5AF6\u5AF7\u5AF8\u5AF9\u5AFA\u5AFB\u5AFC"+ + "\u5AFD\u5AFE\u5AFF\u5B00\u5B01\u5B02\u5B03\u5B04"+ + "\u5B05\u5B06\u5B07\u5B08\u5B0A\u5B0B\u5B0C\u5B0D"+ + "\u5B0E\u5B0F\u5B10\u5B11\u5B12\u5B13\u5B14\u5B15"+ + "\u5B18\u5B19\u5B1A\u5B1B\u5B1C\u5B1D\u5B1E\u5B1F"+ + "\u5B20\u5B21\u5B22\u5B23\u5B24\u5B25\u5B26\u5B27"+ + "\u5B28\u5B29\u5B2A\u5B2B\u5B2C\u5B2D\u5B2E\u5B2F"+ + "\u5B30\u5B31\u5B33\u5B35\u5B36\u5B38\u5B39\u5B3A"+ + "\u5B3B\u5B3C\u5B3D\u5B3E\u5B3F\u5B41\u5B42\u5B43"+ + "\u5B44\u5B45\u5B46\u5B47\u5B48\u5B49\u5B4A\u5B4B"+ + "\u5B4C\u5B4D\u5B4E\u5B4F\u5B52\u5B56\u5B5E\u5B60"+ + "\u5B61\u5B67\u5B68\u5B6B\u5B6D\u5B6E\u5B6F\u5B72"+ + "\u5B74\u5B76\u5B77\u5B78\u5B79\u5B7B\u5B7C\u5B7E"+ + "\u5B7F\u5B82\u5B86\u5B8A\u5B8D\u5B8E\u5B90\u5B91"+ + "\u5B92\u5B94\u5B96\u5B9F\u5BA7\u5BA8\u5BA9\u5BAC"+ + "\u5BAD\u5BAE\u5BAF\u5BB1\u5BB2\u5BB7\u5BBA\u5BBB"+ + "\u5BBC\u5BC0\u5BC1\u5BC3\u5BC8\u5BC9\u5BCA\u5BCB"+ + "\u5BCD\u5BCE\u5BCF\uFFFD\u5BD1\u5BD4\u5BD5\u5BD6"+ + "\u5BD7\u5BD8\u5BD9\u5BDA\u5BDB\u5BDC\u5BE0\u5BE2"+ + "\u5BE3\u5BE6\u5BE7\u5BE9\u5BEA\u5BEB\u5BEC\u5BED"+ + "\u5BEF\u5BF1\u5BF2\u5BF3\u5BF4\u5BF5\u5BF6\u5BF7"+ + "\u5BFD\u5BFE\u5C00\u5C02\u5C03\u5C05\u5C07\u5C08"+ + "\u5C0B\u5C0C\u5C0D\u5C0E\u5C10\u5C12\u5C13\u5C17"+ + "\u5C19\u5C1B\u5C1E\u5C1F\u5C20\u5C21\u5C23\u5C26"+ + "\u5C28\u5C29\u5C2A\u5C2B\u5C2D\u5C2E\u5C2F\u5C30"+ + "\u5C32\u5C33\u5C35\u5C36\u5C37\u5C43\u5C44\u5C46"+ + "\u5C47\u5C4C\u5C4D\u5C52\u5C53\u5C54\u5C56\u5C57"+ + "\u5C58\u5C5A\u5C5B\u5C5C\u5C5D\u5C5F\u5C62\u5C64"+ + "\u5C67\u5C68\u5C69\u5C6A\u5C6B\u5C6C\u5C6D\u5C70"+ + "\u5C72\u5C73\u5C74\u5C75\u5C76\u5C77\u5C78\u5C7B"+ + "\u5C7C\u5C7D\u5C7E\u5C80\u5C83\u5C84\u5C85\u5C86"+ + "\u5C87\u5C89\u5C8A\u5C8B\u5C8E\u5C8F\u5C92\u5C93"+ + "\u5C95\u5C9D\u5C9E\u5C9F\u5CA0\u5CA1\u5CA4\u5CA5"+ + "\u5CA6\u5CA7\u5CA8\u5CAA\u5CAE\u5CAF\u5CB0\u5CB2"+ + "\u5CB4\u5CB6\u5CB9\u5CBA\u5CBB\u5CBC\u5CBE\u5CC0"+ + "\u5CC2\u5CC3\u5CC5\u5CC6\u5CC7\u5CC8\u5CC9\u5CCA"+ + "\u5CCC\u5CCD\u5CCE\u5CCF\u5CD0\u5CD1\u5CD3\u5CD4"+ + "\u5CD5\u5CD6\u5CD7\u5CD8\u5CDA\u5CDB\u5CDC\u5CDD"+ + "\u5CDE\u5CDF\u5CE0\u5CE2\u5CE3\u5CE7\u5CE9\u5CEB"+ + "\u5CEC\u5CEE\u5CEF\u5CF1\u5CF2\u5CF3\u5CF4\u5CF5"+ + "\u5CF6\u5CF7\u5CF8\u5CF9\u5CFA\u5CFC\u5CFD\u5CFE"+ + "\u5CFF\u5D00\uFFFD\u5D01\u5D04\u5D05\u5D08\u5D09"+ + "\u5D0A\u5D0B\u5D0C\u5D0D\u5D0F\u5D10\u5D11\u5D12"+ + "\u5D13\u5D15\u5D17\u5D18\u5D19\u5D1A\u5D1C\u5D1D"+ + "\u5D1F\u5D20\u5D21\u5D22\u5D23\u5D25\u5D28\u5D2A"+ + "\u5D2B\u5D2C\u5D2F\u5D30\u5D31\u5D32\u5D33\u5D35"+ + "\u5D36\u5D37\u5D38\u5D39\u5D3A\u5D3B\u5D3C\u5D3F"+ + "\u5D40\u5D41\u5D42\u5D43\u5D44\u5D45\u5D46\u5D48"+ + "\u5D49\u5D4D\u5D4E\u5D4F\u5D50\u5D51\u5D52\u5D53"+ + "\u5D54\u5D55\u5D56\u5D57\u5D59\u5D5A\u5D5C\u5D5E"+ + "\u5D5F\u5D60\u5D61\u5D62\u5D63\u5D64\u5D65\u5D66"+ + "\u5D67\u5D68\u5D6A\u5D6D\u5D6E\u5D70\u5D71\u5D72"+ + "\u5D73\u5D75\u5D76\u5D77\u5D78\u5D79\u5D7A\u5D7B"+ + "\u5D7C\u5D7D\u5D7E\u5D7F\u5D80\u5D81\u5D83\u5D84"+ + "\u5D85\u5D86\u5D87\u5D88\u5D89\u5D8A\u5D8B\u5D8C"+ + "\u5D8D\u5D8E\u5D8F\u5D90\u5D91\u5D92\u5D93\u5D94"+ + "\u5D95\u5D96\u5D97\u5D98\u5D9A\u5D9B\u5D9C\u5D9E"+ + "\u5D9F\u5DA0\u5DA1\u5DA2\u5DA3\u5DA4\u5DA5\u5DA6"+ + "\u5DA7\u5DA8\u5DA9\u5DAA\u5DAB\u5DAC\u5DAD\u5DAE"+ + "\u5DAF\u5DB0\u5DB1\u5DB2\u5DB3\u5DB4\u5DB5\u5DB6"+ + "\u5DB8\u5DB9\u5DBA\u5DBB\u5DBC\u5DBD\u5DBE\u5DBF"+ + "\u5DC0\u5DC1\u5DC2\u5DC3\u5DC4\u5DC6\u5DC7\u5DC8"+ + "\u5DC9\u5DCA\u5DCB\u5DCC\u5DCE\u5DCF\u5DD0\u5DD1"+ + "\u5DD2\u5DD3\u5DD4\u5DD5\u5DD6\u5DD7\u5DD8\u5DD9"+ + "\u5DDA\u5DDC\u5DDF\u5DE0\u5DE3\u5DE4\u5DEA\u5DEC"+ + "\u5DED\uFFFD\u5DF0\u5DF5\u5DF6\u5DF8\u5DF9\u5DFA"+ + "\u5DFB\u5DFC\u5DFF\u5E00\u5E04\u5E07\u5E09\u5E0A"+ + "\u5E0B\u5E0D\u5E0E\u5E12\u5E13\u5E17\u5E1E\u5E1F"+ + "\u5E20\u5E21\u5E22\u5E23\u5E24\u5E25\u5E28\u5E29"+ + "\u5E2A\u5E2B\u5E2C\u5E2F\u5E30\u5E32\u5E33\u5E34"+ + "\u5E35\u5E36\u5E39\u5E3A\u5E3E\u5E3F\u5E40\u5E41"+ + "\u5E43\u5E46\u5E47\u5E48\u5E49\u5E4A\u5E4B\u5E4D"+ + "\u5E4E\u5E4F\u5E50\u5E51\u5E52\u5E53\u5E56\u5E57"+ + "\u5E58\u5E59\u5E5A\u5E5C\u5E5D\u5E5F\u5E60\u5E63"+ + "\u5E64\u5E65\u5E66\u5E67\u5E68\u5E69\u5E6A\u5E6B"+ + "\u5E6C\u5E6D\u5E6E\u5E6F\u5E70\u5E71\u5E75\u5E77"+ + "\u5E79\u5E7E\u5E81\u5E82\u5E83\u5E85\u5E88\u5E89"+ + "\u5E8C\u5E8D\u5E8E\u5E92\u5E98\u5E9B\u5E9D\u5EA1"+ + "\u5EA2\u5EA3\u5EA4\u5EA8\u5EA9\u5EAA\u5EAB\u5EAC"+ + "\u5EAE\u5EAF\u5EB0\u5EB1\u5EB2\u5EB4\u5EBA\u5EBB"+ + "\u5EBC\u5EBD\u5EBF\u5EC0\u5EC1\u5EC2\u5EC3\u5EC4"+ + "\u5EC5\u5EC6\u5EC7\u5EC8\u5ECB\u5ECC\u5ECD\u5ECE"+ + "\u5ECF\u5ED0\u5ED4\u5ED5\u5ED7\u5ED8\u5ED9\u5EDA"+ + "\u5EDC\u5EDD\u5EDE\u5EDF\u5EE0\u5EE1\u5EE2\u5EE3"+ + "\u5EE4\u5EE5\u5EE6\u5EE7\u5EE9\u5EEB\u5EEC\u5EED"+ + "\u5EEE\u5EEF\u5EF0\u5EF1\u5EF2\u5EF3\u5EF5\u5EF8"+ + "\u5EF9\u5EFB\u5EFC\u5EFD\u5F05\u5F06\u5F07\u5F09"+ + "\u5F0C\u5F0D\u5F0E\u5F10\u5F12\u5F14\u5F16\u5F19"+ + "\u5F1A\u5F1C\u5F1D\u5F1E\u5F21\u5F22\u5F23\u5F24"+ + "\uFFFD\u5F28\u5F2B\u5F2C\u5F2E\u5F30\u5F32\u5F33"+ + "\u5F34\u5F35\u5F36\u5F37\u5F38\u5F3B\u5F3D\u5F3E"+ + "\u5F3F\u5F41\u5F42\u5F43\u5F44\u5F45\u5F46\u5F47"+ + "\u5F48\u5F49\u5F4A\u5F4B\u5F4C\u5F4D\u5F4E\u5F4F"+ + "\u5F51\u5F54\u5F59\u5F5A\u5F5B\u5F5C\u5F5E\u5F5F"+ + "\u5F60\u5F63\u5F65\u5F67\u5F68\u5F6B\u5F6E\u5F6F"+ + "\u5F72\u5F74\u5F75\u5F76\u5F78\u5F7A\u5F7D\u5F7E"+ + "\u5F7F\u5F83\u5F86\u5F8D\u5F8E\u5F8F\u5F91\u5F93"+ + "\u5F94\u5F96\u5F9A\u5F9B\u5F9D\u5F9E\u5F9F\u5FA0"+ + "\u5FA2\u5FA3\u5FA4\u5FA5\u5FA6\u5FA7\u5FA9\u5FAB"+ + "\u5FAC\u5FAF\u5FB0\u5FB1\u5FB2\u5FB3\u5FB4\u5FB6"+ + "\u5FB8\u5FB9\u5FBA\u5FBB\u5FBE\u5FBF\u5FC0\u5FC1"+ + "\u5FC2\u5FC7\u5FC8\u5FCA\u5FCB\u5FCE\u5FD3\u5FD4"+ + "\u5FD5\u5FDA\u5FDB\u5FDC\u5FDE\u5FDF\u5FE2\u5FE3"+ + "\u5FE5\u5FE6\u5FE8\u5FE9\u5FEC\u5FEF\u5FF0\u5FF2"+ + "\u5FF3\u5FF4\u5FF6\u5FF7\u5FF9\u5FFA\u5FFC\u6007"; + + private final static String innerIndex1= + "\u6008\u6009\u600B\u600C\u6010\u6011\u6013\u6017"+ + "\u6018\u601A\u601E\u601F\u6022\u6023\u6024\u602C"+ + "\u602D\u602E\u6030\u6031\u6032\u6033\u6034\u6036"+ + "\u6037\u6038\u6039\u603A\u603D\u603E\u6040\u6044"+ + "\u6045\u6046\u6047\u6048\u6049\u604A\u604C\u604E"+ + "\u604F\u6051\u6053\u6054\u6056\u6057\u6058\u605B"+ + "\u605C\u605E\u605F\u6060\u6061\u6065\u6066\u606E"+ + "\u6071\u6072\u6074\u6075\u6077\u607E\u6080\uFFFD"+ + "\u6081\u6082\u6085\u6086\u6087\u6088\u608A\u608B"+ + "\u608E\u608F\u6090\u6091\u6093\u6095\u6097\u6098"+ + "\u6099\u609C\u609E\u60A1\u60A2\u60A4\u60A5\u60A7"+ + "\u60A9\u60AA\u60AE\u60B0\u60B3\u60B5\u60B6\u60B7"+ + "\u60B9\u60BA\u60BD\u60BE\u60BF\u60C0\u60C1\u60C2"+ + "\u60C3\u60C4\u60C7\u60C8\u60C9\u60CC\u60CD\u60CE"+ + "\u60CF\u60D0\u60D2\u60D3\u60D4\u60D6\u60D7\u60D9"+ + "\u60DB\u60DE\u60E1\u60E2\u60E3\u60E4\u60E5\u60EA"+ + "\u60F1\u60F2\u60F5\u60F7\u60F8\u60FB\u60FC\u60FD"+ + "\u60FE\u60FF\u6102\u6103\u6104\u6105\u6107\u610A"+ + "\u610B\u610C\u6110\u6111\u6112\u6113\u6114\u6116"+ + "\u6117\u6118\u6119\u611B\u611C\u611D\u611E\u6121"+ + "\u6122\u6125\u6128\u6129\u612A\u612C\u612D\u612E"+ + "\u612F\u6130\u6131\u6132\u6133\u6134\u6135\u6136"+ + "\u6137\u6138\u6139\u613A\u613B\u613C\u613D\u613E"+ + "\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147"+ + "\u6149\u614B\u614D\u614F\u6150\u6152\u6153\u6154"+ + "\u6156\u6157\u6158\u6159\u615A\u615B\u615C\u615E"+ + "\u615F\u6160\u6161\u6163\u6164\u6165\u6166\u6169"+ + "\u616A\u616B\u616C\u616D\u616E\u616F\u6171\u6172"+ + "\u6173\u6174\u6176\u6178\u6179\u617A\u617B\u617C"+ + "\u617D\u617E\u617F\u6180\u6181\u6182\u6183\u6184"+ + "\u6185\u6186\u6187\u6188\u6189\u618A\u618C\u618D"+ + "\u618F\u6190\u6191\u6192\u6193\u6195\uFFFD\u6196"+ + "\u6197\u6198\u6199\u619A\u619B\u619C\u619E\u619F"+ + "\u61A0\u61A1\u61A2\u61A3\u61A4\u61A5\u61A6\u61AA"+ + "\u61AB\u61AD\u61AE\u61AF\u61B0\u61B1\u61B2\u61B3"+ + "\u61B4\u61B5\u61B6\u61B8\u61B9\u61BA\u61BB\u61BC"+ + "\u61BD\u61BF\u61C0\u61C1\u61C3\u61C4\u61C5\u61C6"+ + "\u61C7\u61C9\u61CC\u61CD\u61CE\u61CF\u61D0\u61D3"+ + "\u61D5\u61D6\u61D7\u61D8\u61D9\u61DA\u61DB\u61DC"+ + "\u61DD\u61DE\u61DF\u61E0\u61E1\u61E2\u61E3\u61E4"+ + "\u61E5\u61E7\u61E8\u61E9\u61EA\u61EB\u61EC\u61ED"+ + "\u61EE\u61EF\u61F0\u61F1\u61F2\u61F3\u61F4\u61F6"+ + "\u61F7\u61F8\u61F9\u61FA\u61FB\u61FC\u61FD\u61FE"+ + "\u6200\u6201\u6202\u6203\u6204\u6205\u6207\u6209"+ + "\u6213\u6214\u6219\u621C\u621D\u621E\u6220\u6223"+ + "\u6226\u6227\u6228\u6229\u622B\u622D\u622F\u6230"+ + "\u6231\u6232\u6235\u6236\u6238\u6239\u623A\u623B"+ + "\u623C\u6242\u6244\u6245\u6246\u624A\u624F\u6250"+ + "\u6255\u6256\u6257\u6259\u625A\u625C\u625D\u625E"+ + "\u625F\u6260\u6261\u6262\u6264\u6265\u6268\u6271"+ + "\u6272\u6274\u6275\u6277\u6278\u627A\u627B\u627D"+ + "\u6281\u6282\u6283\u6285\u6286\u6287\u6288\u628B"+ + "\u628C\u628D\u628E\u628F\u6290\u6294\u6299\u629C"+ + "\u629D\u629E\u62A3\u62A6\u62A7\u62A9\u62AA\u62AD"+ + "\u62AE\u62AF\u62B0\u62B2\u62B3\u62B4\u62B6\u62B7"+ + "\u62B8\u62BA\u62BE\u62C0\u62C1\uFFFD\u62C3\u62CB"+ + "\u62CF\u62D1\u62D5\u62DD\u62DE\u62E0\u62E1\u62E4"+ + "\u62EA\u62EB\u62F0\u62F2\u62F5\u62F8\u62F9\u62FA"+ + "\u62FB\u6300\u6303\u6304\u6305\u6306\u630A\u630B"+ + "\u630C\u630D\u630F\u6310\u6312\u6313\u6314\u6315"+ + "\u6317\u6318\u6319\u631C\u6326\u6327\u6329\u632C"+ + "\u632D\u632E\u6330\u6331\u6333\u6334\u6335\u6336"+ + "\u6337\u6338\u633B\u633C\u633E\u633F\u6340\u6341"+ + "\u6344\u6347\u6348\u634A\u6351\u6352\u6353\u6354"+ + "\u6356\u6357\u6358\u6359\u635A\u635B\u635C\u635D"+ + "\u6360\u6364\u6365\u6366\u6368\u636A\u636B\u636C"+ + "\u636F\u6370\u6372\u6373\u6374\u6375\u6378\u6379"+ + "\u637C\u637D\u637E\u637F\u6381\u6383\u6384\u6385"+ + "\u6386\u638B\u638D\u6391\u6393\u6394\u6395\u6397"+ + "\u6399\u639A\u639B\u639C\u639D\u639E\u639F\u63A1"+ + "\u63A4\u63A6\u63AB\u63AF\u63B1\u63B2\u63B5\u63B6"+ + "\u63B9\u63BB\u63BD\u63BF\u63C0\u63C1\u63C2\u63C3"+ + "\u63C5\u63C7\u63C8\u63CA\u63CB\u63CC\u63D1\u63D3"+ + "\u63D4\u63D5\u63D7\u63D8\u63D9\u63DA\u63DB\u63DC"+ + "\u63DD\u63DF\u63E2\u63E4\u63E5\u63E6\u63E7\u63E8"+ + "\u63EB\u63EC\u63EE\u63EF\u63F0\u63F1\u63F3\u63F5"+ + "\u63F7\u63F9\u63FA\u63FB\u63FC\u63FE\u6403\u6404"+ + "\u6406\u6407\u6408\u6409\u640A\u640D\u640E\u6411"+ + "\u6412\u6415\u6416\u6417\u6418\u6419\u641A\u641D"+ + "\u641F\u6422\u6423\u6424\uFFFD\u6425\u6427\u6428"+ + "\u6429\u642B\u642E\u642F\u6430\u6431\u6432\u6433"+ + "\u6435\u6436\u6437\u6438\u6439\u643B\u643C\u643E"+ + "\u6440\u6442\u6443\u6449\u644B\u644C\u644D\u644E"+ + "\u644F\u6450\u6451\u6453\u6455\u6456\u6457\u6459"+ + "\u645A\u645B\u645C\u645D\u645F\u6460\u6461\u6462"+ + "\u6463\u6464\u6465\u6466\u6468\u646A\u646B\u646C"+ + "\u646E\u646F\u6470\u6471\u6472\u6473\u6474\u6475"+ + "\u6476\u6477\u647B\u647C\u647D\u647E\u647F\u6480"+ + "\u6481\u6483\u6486\u6488\u6489\u648A\u648B\u648C"+ + "\u648D\u648E\u648F\u6490\u6493\u6494\u6497\u6498"+ + "\u649A\u649B\u649C\u649D\u649F\u64A0\u64A1\u64A2"+ + "\u64A3\u64A5\u64A6\u64A7\u64A8\u64AA\u64AB\u64AF"+ + "\u64B1\u64B2\u64B3\u64B4\u64B6\u64B9\u64BB\u64BD"+ + "\u64BE\u64BF\u64C1\u64C3\u64C4\u64C6\u64C7\u64C8"+ + "\u64C9\u64CA\u64CB\u64CC\u64CF\u64D1\u64D3\u64D4"+ + "\u64D5\u64D6\u64D9\u64DA\u64DB\u64DC\u64DD\u64DF"+ + "\u64E0\u64E1\u64E3\u64E5\u64E7\u64E8\u64E9\u64EA"+ + "\u64EB\u64EC\u64ED\u64EE\u64EF\u64F0\u64F1\u64F2"+ + "\u64F3\u64F4\u64F5\u64F6\u64F7\u64F8\u64F9\u64FA"+ + "\u64FB\u64FC\u64FD\u64FE\u64FF\u6501\u6502\u6503"+ + "\u6504\u6505\u6506\u6507\u6508\u650A\u650B\u650C"+ + "\u650D\u650E\u650F\u6510\u6511\u6513\u6514\u6515"+ + "\u6516\u6517\u6519\u651A\u651B\u651C\u651D\u651E"+ + "\u651F\u6520\u6521\uFFFD\u6522\u6523\u6524\u6526"+ + "\u6527\u6528\u6529\u652A\u652C\u652D\u6530\u6531"+ + "\u6532\u6533\u6537\u653A\u653C\u653D\u6540\u6541"+ + "\u6542\u6543\u6544\u6546\u6547\u654A\u654B\u654D"+ + "\u654E\u6550\u6552\u6553\u6554\u6557\u6558\u655A"+ + "\u655C\u655F\u6560\u6561\u6564\u6565\u6567\u6568"+ + "\u6569\u656A\u656D\u656E\u656F\u6571\u6573\u6575"+ + "\u6576\u6578\u6579\u657A\u657B\u657C\u657D\u657E"+ + "\u657F\u6580\u6581\u6582\u6583\u6584\u6585\u6586"+ + "\u6588\u6589\u658A\u658D\u658E\u658F\u6592\u6594"+ + "\u6595\u6596\u6598\u659A\u659D\u659E\u65A0\u65A2"+ + "\u65A3\u65A6\u65A8\u65AA\u65AC\u65AE\u65B1\u65B2"+ + "\u65B3\u65B4\u65B5\u65B6\u65B7\u65B8\u65BA\u65BB"+ + "\u65BE\u65BF\u65C0\u65C2\u65C7\u65C8\u65C9\u65CA"+ + "\u65CD\u65D0\u65D1\u65D3\u65D4\u65D5\u65D8\u65D9"+ + "\u65DA\u65DB\u65DC\u65DD\u65DE\u65DF\u65E1\u65E3"+ + "\u65E4\u65EA\u65EB\u65F2\u65F3\u65F4\u65F5\u65F8"+ + "\u65F9\u65FB\u65FC\u65FD\u65FE\u65FF\u6601\u6604"+ + "\u6605\u6607\u6608\u6609\u660B\u660D\u6610\u6611"+ + "\u6612\u6616\u6617\u6618\u661A\u661B\u661C\u661E"+ + "\u6621\u6622\u6623\u6624\u6626\u6629\u662A\u662B"+ + "\u662C\u662E\u6630\u6632\u6633\u6637\u6638\u6639"+ + "\u663A\u663B\u663D\u663F\u6640\u6642\u6644\u6645"+ + "\u6646\u6647\u6648\u6649\u664A\u664D\u664E\u6650"+ + "\u6651\u6658\uFFFD\u6659\u665B\u665C\u665D\u665E"+ + "\u6660\u6662\u6663\u6665\u6667\u6669\u666A\u666B"+ + "\u666C\u666D\u6671\u6672\u6673\u6675\u6678\u6679"+ + "\u667B\u667C\u667D\u667F\u6680\u6681\u6683\u6685"+ + "\u6686\u6688\u6689\u668A\u668B\u668D\u668E\u668F"+ + "\u6690\u6692\u6693\u6694\u6695\u6698\u6699\u669A"+ + "\u669B\u669C\u669E\u669F\u66A0\u66A1\u66A2\u66A3"+ + "\u66A4\u66A5\u66A6\u66A9\u66AA\u66AB\u66AC\u66AD"+ + "\u66AF\u66B0\u66B1\u66B2\u66B3\u66B5\u66B6\u66B7"+ + "\u66B8\u66BA\u66BB\u66BC\u66BD\u66BF\u66C0\u66C1"+ + "\u66C2\u66C3\u66C4\u66C5\u66C6\u66C7\u66C8\u66C9"+ + "\u66CA\u66CB\u66CC\u66CD\u66CE\u66CF\u66D0\u66D1"+ + "\u66D2\u66D3\u66D4\u66D5\u66D6\u66D7\u66D8\u66DA"+ + "\u66DE\u66DF\u66E0\u66E1\u66E2\u66E3\u66E4\u66E5"+ + "\u66E7\u66E8\u66EA\u66EB\u66EC\u66ED\u66EE\u66EF"+ + "\u66F1\u66F5\u66F6\u66F8\u66FA\u66FB\u66FD\u6701"+ + "\u6702\u6703\u6704\u6705\u6706\u6707\u670C\u670E"+ + "\u670F\u6711\u6712\u6713\u6716\u6718\u6719\u671A"+ + "\u671C\u671E\u6720\u6721\u6722\u6723\u6724\u6725"+ + "\u6727\u6729\u672E\u6730\u6732\u6733\u6736\u6737"+ + "\u6738\u6739\u673B\u673C\u673E\u673F\u6741\u6744"+ + "\u6745\u6747\u674A\u674B\u674D\u6752\u6754\u6755"+ + "\u6757\u6758\u6759\u675A\u675B\u675D\u6762\u6763"+ + "\u6764\u6766\u6767\u676B\u676C\u676E\u6771\u6774"+ + "\u6776\uFFFD\u6778\u6779\u677A\u677B\u677D\u6780"+ + "\u6782\u6783\u6785\u6786\u6788\u678A\u678C\u678D"+ + "\u678E\u678F\u6791\u6792\u6793\u6794\u6796\u6799"+ + "\u679B\u679F\u67A0\u67A1\u67A4\u67A6\u67A9\u67AC"+ + "\u67AE\u67B1\u67B2\u67B4\u67B9\u67BA\u67BB\u67BC"+ + "\u67BD\u67BE\u67BF\u67C0\u67C2\u67C5\u67C6\u67C7"+ + "\u67C8\u67C9\u67CA\u67CB\u67CC\u67CD\u67CE\u67D5"+ + "\u67D6\u67D7\u67DB\u67DF\u67E1\u67E3\u67E4\u67E6"+ + "\u67E7\u67E8\u67EA\u67EB\u67ED\u67EE\u67F2\u67F5"+ + "\u67F6\u67F7\u67F8\u67F9\u67FA\u67FB\u67FC\u67FE"+ + "\u6801\u6802\u6803\u6804\u6806\u680D\u6810\u6812"+ + "\u6814\u6815\u6818\u6819\u681A\u681B\u681C\u681E"+ + "\u681F\u6820\u6822\u6823\u6824\u6825\u6826\u6827"+ + "\u6828\u682B\u682C\u682D\u682E\u682F\u6830\u6831"+ + "\u6834\u6835\u6836\u683A\u683B\u683F\u6847\u684B"+ + "\u684D\u684F\u6852\u6856\u6857\u6858\u6859\u685A"+ + "\u685B\u685C\u685D\u685E\u685F\u686A\u686C\u686D"+ + "\u686E\u686F\u6870\u6871\u6872\u6873\u6875\u6878"+ + "\u6879\u687A\u687B\u687C\u687D\u687E\u687F\u6880"+ + "\u6882\u6884\u6887\u6888\u6889\u688A\u688B\u688C"+ + "\u688D\u688E\u6890\u6891\u6892\u6894\u6895\u6896"+ + "\u6898\u6899\u689A\u689B\u689C\u689D\u689E\u689F"+ + "\u68A0\u68A1\u68A3\u68A4\u68A5\u68A9\u68AA\u68AB"+ + "\u68AC\u68AE\u68B1\u68B2\u68B4\u68B6\u68B7\u68B8"+ + "\uFFFD\u68B9\u68BA\u68BB\u68BC\u68BD\u68BE\u68BF"+ + "\u68C1\u68C3\u68C4\u68C5\u68C6\u68C7\u68C8\u68CA"+ + "\u68CC\u68CE\u68CF\u68D0\u68D1\u68D3\u68D4\u68D6"+ + "\u68D7\u68D9\u68DB\u68DC\u68DD\u68DE\u68DF\u68E1"+ + "\u68E2\u68E4\u68E5\u68E6\u68E7\u68E8\u68E9\u68EA"+ + "\u68EB\u68EC\u68ED\u68EF\u68F2\u68F3\u68F4\u68F6"+ + "\u68F7\u68F8\u68FB\u68FD\u68FE\u68FF\u6900\u6902"+ + "\u6903\u6904\u6906\u6907\u6908\u6909\u690A\u690C"+ + "\u690F\u6911\u6913\u6914\u6915\u6916\u6917\u6918"+ + "\u6919\u691A\u691B\u691C\u691D\u691E\u6921\u6922"+ + "\u6923\u6925\u6926\u6927\u6928\u6929\u692A\u692B"+ + "\u692C\u692E\u692F\u6931\u6932\u6933\u6935\u6936"+ + "\u6937\u6938\u693A\u693B\u693C\u693E\u6940\u6941"+ + "\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694A"+ + "\u694B\u694C\u694D\u694E\u694F\u6950\u6951\u6952"+ + "\u6953\u6955\u6956\u6958\u6959\u695B\u695C\u695F"+ + "\u6961\u6962\u6964\u6965\u6967\u6968\u6969\u696A"+ + "\u696C\u696D\u696F\u6970\u6972\u6973\u6974\u6975"+ + "\u6976\u697A\u697B\u697D\u697E\u697F\u6981\u6983"+ + "\u6985\u698A\u698B\u698C\u698E\u698F\u6990\u6991"+ + "\u6992\u6993\u6996\u6997\u6999\u699A\u699D\u699E"+ + "\u699F\u69A0\u69A1\u69A2\u69A3\u69A4\u69A5\u69A6"+ + "\u69A9\u69AA\u69AC\u69AE\u69AF\u69B0\u69B2\u69B3"+ + "\u69B5\u69B6\u69B8\u69B9\u69BA\u69BC\u69BD\uFFFD"+ + "\u69BE\u69BF\u69C0\u69C2\u69C3\u69C4\u69C5\u69C6"+ + "\u69C7\u69C8\u69C9\u69CB\u69CD\u69CF\u69D1\u69D2"+ + "\u69D3\u69D5\u69D6\u69D7\u69D8\u69D9\u69DA\u69DC"+ + "\u69DD\u69DE\u69E1\u69E2\u69E3\u69E4\u69E5\u69E6"+ + "\u69E7\u69E8\u69E9\u69EA\u69EB\u69EC\u69EE\u69EF"+ + "\u69F0\u69F1\u69F3\u69F4\u69F5\u69F6\u69F7\u69F8"+ + "\u69F9\u69FA\u69FB\u69FC\u69FE\u6A00\u6A01\u6A02"+ + "\u6A03\u6A04\u6A05\u6A06\u6A07\u6A08\u6A09\u6A0B"+ + "\u6A0C\u6A0D\u6A0E\u6A0F\u6A10\u6A11\u6A12\u6A13"+ + "\u6A14\u6A15\u6A16\u6A19\u6A1A\u6A1B\u6A1C\u6A1D"+ + "\u6A1E\u6A20\u6A22\u6A23\u6A24\u6A25\u6A26\u6A27"+ + "\u6A29\u6A2B\u6A2C\u6A2D\u6A2E\u6A30\u6A32\u6A33"+ + "\u6A34\u6A36\u6A37\u6A38\u6A39\u6A3A\u6A3B\u6A3C"+ + "\u6A3F\u6A40\u6A41\u6A42\u6A43\u6A45\u6A46\u6A48"+ + "\u6A49\u6A4A\u6A4B\u6A4C\u6A4D\u6A4E\u6A4F\u6A51"+ + "\u6A52\u6A53\u6A54\u6A55\u6A56\u6A57\u6A5A\u6A5C"+ + "\u6A5D\u6A5E\u6A5F\u6A60\u6A62\u6A63\u6A64\u6A66"+ + "\u6A67\u6A68\u6A69\u6A6A\u6A6B\u6A6C\u6A6D\u6A6E"+ + "\u6A6F\u6A70\u6A72\u6A73\u6A74\u6A75\u6A76\u6A77"+ + "\u6A78\u6A7A\u6A7B\u6A7D\u6A7E\u6A7F\u6A81\u6A82"+ + "\u6A83\u6A85\u6A86\u6A87\u6A88\u6A89\u6A8A\u6A8B"+ + "\u6A8C\u6A8D\u6A8F\u6A92\u6A93\u6A94\u6A95\u6A96"+ + "\u6A98\u6A99\u6A9A\u6A9B\u6A9C\u6A9D\u6A9E\u6A9F"+ + "\u6AA1\u6AA2\u6AA3\u6AA4\u6AA5\u6AA6\uFFFD\u6AA7"+ + "\u6AA8\u6AAA\u6AAD\u6AAE\u6AAF\u6AB0\u6AB1\u6AB2"+ + "\u6AB3\u6AB4\u6AB5\u6AB6\u6AB7\u6AB8\u6AB9\u6ABA"+ + "\u6ABB\u6ABC\u6ABD\u6ABE\u6ABF\u6AC0\u6AC1\u6AC2"+ + "\u6AC3\u6AC4\u6AC5\u6AC6\u6AC7\u6AC8\u6AC9\u6ACA"+ + "\u6ACB\u6ACC\u6ACD\u6ACE\u6ACF\u6AD0\u6AD1\u6AD2"+ + "\u6AD3\u6AD4\u6AD5\u6AD6\u6AD7\u6AD8\u6AD9\u6ADA"+ + "\u6ADB\u6ADC\u6ADD\u6ADE\u6ADF\u6AE0\u6AE1\u6AE2"+ + "\u6AE3\u6AE4\u6AE5\u6AE6\u6AE7\u6AE8\u6AE9\u6AEA"+ + "\u6AEB\u6AEC\u6AED\u6AEE\u6AEF\u6AF0\u6AF1\u6AF2"+ + "\u6AF3\u6AF4\u6AF5\u6AF6\u6AF7\u6AF8\u6AF9\u6AFA"+ + "\u6AFB\u6AFC\u6AFD\u6AFE\u6AFF\u6B00\u6B01\u6B02"+ + "\u6B03\u6B04\u6B05\u6B06\u6B07\u6B08\u6B09\u6B0A"+ + "\u6B0B\u6B0C\u6B0D\u6B0E\u6B0F\u6B10\u6B11\u6B12"+ + "\u6B13\u6B14\u6B15\u6B16\u6B17\u6B18\u6B19\u6B1A"+ + "\u6B1B\u6B1C\u6B1D\u6B1E\u6B1F\u6B25\u6B26\u6B28"+ + "\u6B29\u6B2A\u6B2B\u6B2C\u6B2D\u6B2E\u6B2F\u6B30"+ + "\u6B31\u6B33\u6B34\u6B35\u6B36\u6B38\u6B3B\u6B3C"+ + "\u6B3D\u6B3F\u6B40\u6B41\u6B42\u6B44\u6B45\u6B48"+ + "\u6B4A\u6B4B\u6B4D\u6B4E\u6B4F\u6B50\u6B51\u6B52"+ + "\u6B53\u6B54\u6B55\u6B56\u6B57\u6B58\u6B5A\u6B5B"+ + "\u6B5C\u6B5D\u6B5E\u6B5F\u6B60\u6B61\u6B68\u6B69"+ + "\u6B6B\u6B6C\u6B6D\u6B6E\u6B6F\u6B70\u6B71\u6B72"+ + "\u6B73\u6B74\u6B75\u6B76\u6B77\u6B78\u6B7A\u6B7D"+ + "\u6B7E\u6B7F\u6B80\u6B85\u6B88\uFFFD\u6B8C\u6B8E"+ + "\u6B8F\u6B90\u6B91\u6B94\u6B95\u6B97\u6B98\u6B99"+ + "\u6B9C\u6B9D\u6B9E\u6B9F\u6BA0\u6BA2\u6BA3\u6BA4"+ + "\u6BA5\u6BA6\u6BA7\u6BA8\u6BA9\u6BAB\u6BAC\u6BAD"+ + "\u6BAE\u6BAF\u6BB0\u6BB1\u6BB2\u6BB6\u6BB8\u6BB9"+ + "\u6BBA\u6BBB\u6BBC\u6BBD\u6BBE\u6BC0\u6BC3\u6BC4"+ + "\u6BC6\u6BC7\u6BC8\u6BC9\u6BCA\u6BCC\u6BCE\u6BD0"+ + "\u6BD1\u6BD8\u6BDA\u6BDC\u6BDD\u6BDE\u6BDF\u6BE0"+ + "\u6BE2\u6BE3\u6BE4\u6BE5\u6BE6\u6BE7\u6BE8\u6BE9"+ + "\u6BEC\u6BED\u6BEE\u6BF0\u6BF1\u6BF2\u6BF4\u6BF6"+ + "\u6BF7\u6BF8\u6BFA\u6BFB\u6BFC\u6BFE\u6BFF\u6C00"+ + "\u6C01\u6C02\u6C03\u6C04\u6C08\u6C09\u6C0A\u6C0B"+ + "\u6C0C\u6C0E\u6C12\u6C17\u6C1C\u6C1D\u6C1E\u6C20"+ + "\u6C23\u6C25\u6C2B\u6C2C\u6C2D\u6C31\u6C33\u6C36"+ + "\u6C37\u6C39\u6C3A\u6C3B\u6C3C\u6C3E\u6C3F\u6C43"+ + "\u6C44\u6C45\u6C48\u6C4B\u6C4C\u6C4D\u6C4E\u6C4F"+ + "\u6C51\u6C52\u6C53\u6C56\u6C58\u6C59\u6C5A\u6C62"+ + "\u6C63\u6C65\u6C66\u6C67\u6C6B\u6C6C\u6C6D\u6C6E"+ + "\u6C6F\u6C71\u6C73\u6C75\u6C77\u6C78\u6C7A\u6C7B"+ + "\u6C7C\u6C7F\u6C80\u6C84\u6C87\u6C8A\u6C8B\u6C8D"+ + "\u6C8E\u6C91\u6C92\u6C95\u6C96\u6C97\u6C98\u6C9A"+ + "\u6C9C\u6C9D\u6C9E\u6CA0\u6CA2\u6CA8\u6CAC\u6CAF"+ + "\u6CB0\u6CB4\u6CB5\u6CB6\u6CB7\u6CBA\u6CC0\u6CC1"+ + "\u6CC2\u6CC3\u6CC6\u6CC7\u6CC8\u6CCB\u6CCD\u6CCE"+ + "\u6CCF\u6CD1\u6CD2\u6CD8\uFFFD\u6CD9\u6CDA\u6CDC"+ + "\u6CDD\u6CDF\u6CE4\u6CE6\u6CE7\u6CE9\u6CEC\u6CED"+ + "\u6CF2\u6CF4\u6CF9\u6CFF\u6D00\u6D02\u6D03\u6D05"+ + "\u6D06\u6D08\u6D09\u6D0A\u6D0D\u6D0F\u6D10\u6D11"+ + "\u6D13\u6D14\u6D15\u6D16\u6D18\u6D1C\u6D1D\u6D1F"+ + "\u6D20\u6D21\u6D22\u6D23\u6D24\u6D26\u6D28\u6D29"+ + "\u6D2C\u6D2D\u6D2F\u6D30\u6D34\u6D36\u6D37\u6D38"+ + "\u6D3A\u6D3F\u6D40\u6D42\u6D44\u6D49\u6D4C\u6D50"+ + "\u6D55\u6D56\u6D57\u6D58\u6D5B\u6D5D\u6D5F\u6D61"+ + "\u6D62\u6D64\u6D65\u6D67\u6D68\u6D6B\u6D6C\u6D6D"+ + "\u6D70\u6D71\u6D72\u6D73\u6D75\u6D76\u6D79\u6D7A"+ + "\u6D7B\u6D7D\u6D7E\u6D7F\u6D80\u6D81\u6D83\u6D84"+ + "\u6D86\u6D87\u6D8A\u6D8B\u6D8D\u6D8F\u6D90\u6D92"+ + "\u6D96\u6D97\u6D98\u6D99\u6D9A\u6D9C\u6DA2\u6DA5"+ + "\u6DAC\u6DAD\u6DB0\u6DB1\u6DB3\u6DB4\u6DB6\u6DB7"+ + "\u6DB9\u6DBA\u6DBB\u6DBC\u6DBD\u6DBE\u6DC1\u6DC2"+ + "\u6DC3\u6DC8\u6DC9\u6DCA\u6DCD\u6DCE\u6DCF\u6DD0"+ + "\u6DD2\u6DD3\u6DD4\u6DD5\u6DD7\u6DDA\u6DDB\u6DDC"+ + "\u6DDF\u6DE2\u6DE3\u6DE5\u6DE7\u6DE8\u6DE9\u6DEA"+ + "\u6DED\u6DEF\u6DF0\u6DF2\u6DF4\u6DF5\u6DF6\u6DF8"+ + "\u6DFA\u6DFD\u6DFE\u6DFF\u6E00\u6E01\u6E02\u6E03"+ + "\u6E04\u6E06\u6E07\u6E08\u6E09\u6E0B\u6E0F\u6E12"+ + "\u6E13\u6E15\u6E18\u6E19\u6E1B\u6E1C\u6E1E\u6E1F"+ + "\u6E22\u6E26\u6E27\u6E28\u6E2A\u6E2C\u6E2E\u6E30"+ + "\u6E31\u6E33\u6E35\uFFFD\u6E36\u6E37\u6E39\u6E3B"+ + "\u6E3C\u6E3D\u6E3E\u6E3F\u6E40\u6E41\u6E42\u6E45"+ + "\u6E46\u6E47\u6E48\u6E49\u6E4A\u6E4B\u6E4C\u6E4F"+ + "\u6E50\u6E51\u6E52\u6E55\u6E57\u6E59\u6E5A\u6E5C"+ + "\u6E5D\u6E5E\u6E60\u6E61\u6E62\u6E63\u6E64\u6E65"+ + "\u6E66\u6E67\u6E68\u6E69\u6E6A\u6E6C\u6E6D\u6E6F"+ + "\u6E70\u6E71\u6E72\u6E73\u6E74\u6E75\u6E76\u6E77"+ + "\u6E78\u6E79\u6E7A\u6E7B\u6E7C\u6E7D\u6E80\u6E81"+ + "\u6E82\u6E84\u6E87\u6E88\u6E8A\u6E8B\u6E8C\u6E8D"+ + "\u6E8E\u6E91\u6E92\u6E93\u6E94\u6E95\u6E96\u6E97"+ + "\u6E99\u6E9A\u6E9B\u6E9D\u6E9E\u6EA0\u6EA1\u6EA3"+ + "\u6EA4\u6EA6\u6EA8\u6EA9\u6EAB\u6EAC\u6EAD\u6EAE"+ + "\u6EB0\u6EB3\u6EB5\u6EB8\u6EB9\u6EBC\u6EBE\u6EBF"+ + "\u6EC0\u6EC3\u6EC4\u6EC5\u6EC6\u6EC8\u6EC9\u6ECA"+ + "\u6ECC\u6ECD\u6ECE\u6ED0\u6ED2\u6ED6\u6ED8\u6ED9"+ + "\u6EDB\u6EDC\u6EDD\u6EE3\u6EE7\u6EEA\u6EEB\u6EEC"+ + "\u6EED\u6EEE\u6EEF\u6EF0\u6EF1\u6EF2\u6EF3\u6EF5"+ + "\u6EF6\u6EF7\u6EF8\u6EFA\u6EFB\u6EFC\u6EFD\u6EFE"+ + "\u6EFF\u6F00\u6F01\u6F03\u6F04\u6F05\u6F07\u6F08"+ + "\u6F0A\u6F0B\u6F0C\u6F0D\u6F0E\u6F10\u6F11\u6F12"+ + "\u6F16\u6F17\u6F18\u6F19\u6F1A\u6F1B\u6F1C\u6F1D"+ + "\u6F1E\u6F1F\u6F21\u6F22\u6F23\u6F25\u6F26\u6F27"+ + "\u6F28\u6F2C\u6F2E\u6F30\u6F32\u6F34\u6F35\u6F37"+ + "\u6F38\u6F39\u6F3A\u6F3B\u6F3C\u6F3D\u6F3F\u6F40"+ + "\u6F41\u6F42\uFFFD\u6F43\u6F44\u6F45\u6F48\u6F49"+ + "\u6F4A\u6F4C\u6F4E\u6F4F\u6F50\u6F51\u6F52\u6F53"+ + "\u6F54\u6F55\u6F56\u6F57\u6F59\u6F5A\u6F5B\u6F5D"+ + "\u6F5F\u6F60\u6F61\u6F63\u6F64\u6F65\u6F67\u6F68"+ + "\u6F69\u6F6A\u6F6B\u6F6C\u6F6F\u6F70\u6F71\u6F73"+ + "\u6F75\u6F76\u6F77\u6F79\u6F7B\u6F7D\u6F7E\u6F7F"+ + "\u6F80\u6F81\u6F82\u6F83\u6F85\u6F86\u6F87\u6F8A"+ + "\u6F8B\u6F8F\u6F90\u6F91\u6F92\u6F93\u6F94\u6F95"+ + "\u6F96\u6F97\u6F98\u6F99\u6F9A\u6F9B\u6F9D\u6F9E"+ + "\u6F9F\u6FA0\u6FA2\u6FA3\u6FA4\u6FA5\u6FA6\u6FA8"+ + "\u6FA9\u6FAA\u6FAB\u6FAC\u6FAD\u6FAE\u6FAF\u6FB0"+ + "\u6FB1\u6FB2\u6FB4\u6FB5\u6FB7\u6FB8\u6FBA\u6FBB"+ + "\u6FBC\u6FBD\u6FBE\u6FBF\u6FC1\u6FC3\u6FC4\u6FC5"+ + "\u6FC6\u6FC7\u6FC8\u6FCA\u6FCB\u6FCC\u6FCD\u6FCE"+ + "\u6FCF\u6FD0\u6FD3\u6FD4\u6FD5\u6FD6\u6FD7\u6FD8"+ + "\u6FD9\u6FDA\u6FDB\u6FDC\u6FDD\u6FDF\u6FE2\u6FE3"+ + "\u6FE4\u6FE5\u6FE6\u6FE7\u6FE8\u6FE9\u6FEA\u6FEB"+ + "\u6FEC\u6FED\u6FF0\u6FF1\u6FF2\u6FF3\u6FF4\u6FF5"+ + "\u6FF6\u6FF7\u6FF8\u6FF9\u6FFA\u6FFB\u6FFC\u6FFD"+ + "\u6FFE\u6FFF\u7000\u7001\u7002\u7003\u7004\u7005"+ + "\u7006\u7007\u7008\u7009\u700A\u700B\u700C\u700D"+ + "\u700E\u700F\u7010\u7012\u7013\u7014\u7015\u7016"+ + "\u7017\u7018\u7019\u701C\u701D\u701E\u701F\u7020"+ + "\u7021\u7022\u7024\u7025\u7026\u7027\u7028\u7029"+ + "\u702A\uFFFD\u702B\u702C\u702D\u702E\u702F\u7030"+ + "\u7031\u7032\u7033\u7034\u7036\u7037\u7038\u703A"+ + "\u703B\u703C\u703D\u703E\u703F\u7040\u7041\u7042"+ + "\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704A"+ + "\u704B\u704D\u704E\u7050\u7051\u7052\u7053\u7054"+ + "\u7055\u7056\u7057\u7058\u7059\u705A\u705B\u705C"+ + "\u705D\u705F\u7060\u7061\u7062\u7063\u7064\u7065"+ + "\u7066\u7067\u7068\u7069\u706A\u706E\u7071\u7072"+ + "\u7073\u7074\u7077\u7079\u707A\u707B\u707D\u7081"+ + "\u7082\u7083\u7084\u7086\u7087\u7088\u708B\u708C"+ + "\u708D\u708F\u7090\u7091\u7093\u7097\u7098\u709A"+ + "\u709B\u709E\u709F\u70A0\u70A1\u70A2\u70A3\u70A4"+ + "\u70A5\u70A6\u70A7\u70A8\u70A9\u70AA\u70B0\u70B2"+ + "\u70B4\u70B5\u70B6\u70BA\u70BE\u70BF\u70C4\u70C5"+ + "\u70C6\u70C7\u70C9\u70CB\u70CC\u70CD\u70CE\u70CF"+ + "\u70D0\u70D1\u70D2\u70D3\u70D4\u70D5\u70D6\u70D7"+ + "\u70DA\u70DC\u70DD\u70DE\u70E0\u70E1\u70E2\u70E3"+ + "\u70E5\u70EA\u70EE\u70F0\u70F1\u70F2\u70F3\u70F4"+ + "\u70F5\u70F6\u70F8\u70FA\u70FB\u70FC\u70FE\u70FF"+ + "\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107"+ + "\u7108\u710B\u710C\u710D\u710E\u710F\u7111\u7112"+ + "\u7114\u7117\u711B\u711C\u711D\u711E\u711F\u7120"+ + "\u7121\u7122\u7123\u7124\u7125\u7127\u7128\u7129"+ + "\u712A\u712B\u712C\u712D\u712E\u7132\u7133\u7134"+ + "\uFFFD\u7135\u7137\u7138\u7139\u713A\u713B\u713C"+ + "\u713D\u713E\u713F\u7140\u7141\u7142\u7143\u7144"+ + "\u7146\u7147\u7148\u7149\u714B\u714D\u714F\u7150"+ + "\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158"+ + "\u7159\u715A\u715B\u715D\u715F\u7160\u7161\u7162"+ + "\u7163\u7165\u7169\u716A\u716B\u716C\u716D\u716F"+ + "\u7170\u7171\u7174\u7175\u7176\u7177\u7179\u717B"+ + "\u717C\u717E\u717F\u7180\u7181\u7182\u7183\u7185"+ + "\u7186\u7187\u7188\u7189\u718B\u718C\u718D\u718E"+ + "\u7190\u7191\u7192\u7193\u7195\u7196\u7197\u719A"+ + "\u719B\u719C\u719D\u719E\u71A1\u71A2\u71A3\u71A4"+ + "\u71A5\u71A6\u71A7\u71A9\u71AA\u71AB\u71AD\u71AE"+ + "\u71AF\u71B0\u71B1\u71B2\u71B4\u71B6\u71B7\u71B8"+ + "\u71BA\u71BB\u71BC\u71BD\u71BE\u71BF\u71C0\u71C1"+ + "\u71C2\u71C4\u71C5\u71C6\u71C7\u71C8\u71C9\u71CA"+ + "\u71CB\u71CC\u71CD\u71CF\u71D0\u71D1\u71D2\u71D3"; + + private final static String innerIndex2= + "\u71D6\u71D7\u71D8\u71D9\u71DA\u71DB\u71DC\u71DD"+ + "\u71DE\u71DF\u71E1\u71E2\u71E3\u71E4\u71E6\u71E8"+ + "\u71E9\u71EA\u71EB\u71EC\u71ED\u71EF\u71F0\u71F1"+ + "\u71F2\u71F3\u71F4\u71F5\u71F6\u71F7\u71F8\u71FA"+ + "\u71FB\u71FC\u71FD\u71FE\u71FF\u7200\u7201\u7202"+ + "\u7203\u7204\u7205\u7207\u7208\u7209\u720A\u720B"+ + "\u720C\u720D\u720E\u720F\u7210\u7211\u7212\u7213"+ + "\u7214\u7215\u7216\u7217\u7218\u7219\u721A\uFFFD"+ + "\u721B\u721C\u721E\u721F\u7220\u7221\u7222\u7223"+ + "\u7224\u7225\u7226\u7227\u7229\u722B\u722D\u722E"+ + "\u722F\u7232\u7233\u7234\u723A\u723C\u723E\u7240"+ + "\u7241\u7242\u7243\u7244\u7245\u7246\u7249\u724A"+ + "\u724B\u724E\u724F\u7250\u7251\u7253\u7254\u7255"+ + "\u7257\u7258\u725A\u725C\u725E\u7260\u7263\u7264"+ + "\u7265\u7268\u726A\u726B\u726C\u726D\u7270\u7271"+ + "\u7273\u7274\u7276\u7277\u7278\u727B\u727C\u727D"+ + "\u7282\u7283\u7285\u7286\u7287\u7288\u7289\u728C"+ + "\u728E\u7290\u7291\u7293\u7294\u7295\u7296\u7297"+ + "\u7298\u7299\u729A\u729B\u729C\u729D\u729E\u72A0"+ + "\u72A1\u72A2\u72A3\u72A4\u72A5\u72A6\u72A7\u72A8"+ + "\u72A9\u72AA\u72AB\u72AE\u72B1\u72B2\u72B3\u72B5"+ + "\u72BA\u72BB\u72BC\u72BD\u72BE\u72BF\u72C0\u72C5"+ + "\u72C6\u72C7\u72C9\u72CA\u72CB\u72CC\u72CF\u72D1"+ + "\u72D3\u72D4\u72D5\u72D6\u72D8\u72DA\u72DB\uE4C6"+ + "\uE4C7\uE4C8\uE4C9\uE4CA\uE4CB\uE4CC\uE4CD\uE4CE"+ + "\uE4CF\uE4D0\uE4D1\uE4D2\uE4D3\uE4D4\uE4D5\uE4D6"+ + "\uE4D7\uE4D8\uE4D9\uE4DA\uE4DB\uE4DC\uE4DD\uE4DE"+ + "\uE4DF\uE4E0\uE4E1\uE4E2\uE4E3\uE4E4\uE4E5\uE4E6"+ + "\uE4E7\uE4E8\uE4E9\uE4EA\uE4EB\uE4EC\uE4ED\uE4EE"+ + "\uE4EF\uE4F0\uE4F1\uE4F2\uE4F3\uE4F4\uE4F5\uE4F6"+ + "\uE4F7\uE4F8\uE4F9\uE4FA\uE4FB\uE4FC\uE4FD\uE4FE"+ + "\uE4FF\uE500\uE501\uE502\uE503\uE504\uFFFD\uE505"+ + "\uE506\uE507\uE508\uE509\uE50A\uE50B\uE50C\uE50D"+ + "\uE50E\uE50F\uE510\uE511\uE512\uE513\uE514\uE515"+ + "\uE516\uE517\uE518\uE519\uE51A\uE51B\uE51C\uE51D"+ + "\uE51E\uE51F\uE520\uE521\uE522\uE523\uE524\uE525"+ + "\u3000\u3001\u3002\u00B7\u02C9\u02C7\u00A8\u3003"+ + "\u3005\u2014\uFF5E\u2016\u2026\u2018\u2019\u201C"+ + "\u201D\u3014\u3015\u3008\u3009\u300A\u300B\u300C"+ + "\u300D\u300E\u300F\u3016\u3017\u3010\u3011\u00B1"+ + "\u00D7\u00F7\u2236\u2227\u2228\u2211\u220F\u222A"+ + "\u2229\u2208\u2237\u221A\u22A5\u2225\u2220\u2312"+ + "\u2299\u222B\u222E\u2261\u224C\u2248\u223D\u221D"+ + "\u2260\u226E\u226F\u2264\u2265\u221E\u2235\u2234"+ + "\u2642\u2640\u00B0\u2032\u2033\u2103\uFF04\u00A4"+ + "\uFFE0\uFFE1\u2030\u00A7\u2116\u2606\u2605\u25CB"+ + "\u25CF\u25CE\u25C7\u25C6\u25A1\u25A0\u25B3\u25B2"+ + "\u203B\u2192\u2190\u2191\u2193\u3013\uE526\uE527"+ + "\uE528\uE529\uE52A\uE52B\uE52C\uE52D\uE52E\uE52F"+ + "\uE530\uE531\uE532\uE533\uE534\uE535\uE536\uE537"+ + "\uE538\uE539\uE53A\uE53B\uE53C\uE53D\uE53E\uE53F"+ + "\uE540\uE541\uE542\uE543\uE544\uE545\uE546\uE547"+ + "\uE548\uE549\uE54A\uE54B\uE54C\uE54D\uE54E\uE54F"+ + "\uE550\uE551\uE552\uE553\uE554\uE555\uE556\uE557"+ + "\uE558\uE559\uE55A\uE55B\uE55C\uE55D\uE55E\uE55F"+ + "\uE560\uE561\uE562\uE563\uE564\uFFFD\uE565\uE566"+ + "\uE567\uE568\uE569\uE56A\uE56B\uE56C\uE56D\uE56E"+ + "\uE56F\uE570\uE571\uE572\uE573\uE574\uE575\uE576"+ + "\uE577\uE578\uE579\uE57A\uE57B\uE57C\uE57D\uE57E"+ + "\uE57F\uE580\uE581\uE582\uE583\uE584\uE585\u2170"+ + "\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178"+ + "\u2179\uE766\uE767\uE768\uE769\uE76A\uE76B\u2488"+ + "\u2489\u248A\u248B\u248C\u248D\u248E\u248F\u2490"+ + "\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498"+ + "\u2499\u249A\u249B\u2474\u2475\u2476\u2477\u2478"+ + "\u2479\u247A\u247B\u247C\u247D\u247E\u247F\u2480"+ + "\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2460"+ + "\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468"+ + "\u2469\u20AC\uE76D\u3220\u3221\u3222\u3223\u3224"+ + "\u3225\u3226\u3227\u3228\u3229\uE76E\uE76F\u2160"+ + "\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168"+ + "\u2169\u216A\u216B\uE770\uE771\uE586\uE587\uE588"+ + "\uE589\uE58A\uE58B\uE58C\uE58D\uE58E\uE58F\uE590"+ + "\uE591\uE592\uE593\uE594\uE595\uE596\uE597\uE598"+ + "\uE599\uE59A\uE59B\uE59C\uE59D\uE59E\uE59F\uE5A0"+ + "\uE5A1\uE5A2\uE5A3\uE5A4\uE5A5\uE5A6\uE5A7\uE5A8"+ + "\uE5A9\uE5AA\uE5AB\uE5AC\uE5AD\uE5AE\uE5AF\uE5B0"+ + "\uE5B1\uE5B2\uE5B3\uE5B4\uE5B5\uE5B6\uE5B7\uE5B8"+ + "\uE5B9\uE5BA\uE5BB\uE5BC\uE5BD\uE5BE\uE5BF\uE5C0"+ + "\uE5C1\uE5C2\uE5C3\uE5C4\uFFFD\uE5C5\uE5C6\uE5C7"+ + "\uE5C8\uE5C9\uE5CA\uE5CB\uE5CC\uE5CD\uE5CE\uE5CF"+ + "\uE5D0\uE5D1\uE5D2\uE5D3\uE5D4\uE5D5\uE5D6\uE5D7"+ + "\uE5D8\uE5D9\uE5DA\uE5DB\uE5DC\uE5DD\uE5DE\uE5DF"+ + "\uE5E0\uE5E1\uE5E2\uE5E3\uE5E4\uE5E5\uFF01\uFF02"+ + "\uFF03\uFFE5\uFF05\uFF06\uFF07\uFF08\uFF09\uFF0A"+ + "\uFF0B\uFF0C\uFF0D\uFF0E\uFF0F\uFF10\uFF11\uFF12"+ + "\uFF13\uFF14\uFF15\uFF16\uFF17\uFF18\uFF19\uFF1A"+ + "\uFF1B\uFF1C\uFF1D\uFF1E\uFF1F\uFF20\uFF21\uFF22"+ + "\uFF23\uFF24\uFF25\uFF26\uFF27\uFF28\uFF29\uFF2A"+ + "\uFF2B\uFF2C\uFF2D\uFF2E\uFF2F\uFF30\uFF31\uFF32"+ + "\uFF33\uFF34\uFF35\uFF36\uFF37\uFF38\uFF39\uFF3A"+ + "\uFF3B\uFF3C\uFF3D\uFF3E\uFF3F\uFF40\uFF41\uFF42"+ + "\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48\uFF49\uFF4A"+ + "\uFF4B\uFF4C\uFF4D\uFF4E\uFF4F\uFF50\uFF51\uFF52"+ + "\uFF53\uFF54\uFF55\uFF56\uFF57\uFF58\uFF59\uFF5A"+ + "\uFF5B\uFF5C\uFF5D\uFFE3\uE5E6\uE5E7\uE5E8\uE5E9"+ + "\uE5EA\uE5EB\uE5EC\uE5ED\uE5EE\uE5EF\uE5F0\uE5F1"+ + "\uE5F2\uE5F3\uE5F4\uE5F5\uE5F6\uE5F7\uE5F8\uE5F9"+ + "\uE5FA\uE5FB\uE5FC\uE5FD\uE5FE\uE5FF\uE600\uE601"+ + "\uE602\uE603\uE604\uE605\uE606\uE607\uE608\uE609"+ + "\uE60A\uE60B\uE60C\uE60D\uE60E\uE60F\uE610\uE611"+ + "\uE612\uE613\uE614\uE615\uE616\uE617\uE618\uE619"+ + "\uE61A\uE61B\uE61C\uE61D\uE61E\uE61F\uE620\uE621"+ + "\uE622\uE623\uE624\uFFFD\uE625\uE626\uE627\uE628"+ + "\uE629\uE62A\uE62B\uE62C\uE62D\uE62E\uE62F\uE630"+ + "\uE631\uE632\uE633\uE634\uE635\uE636\uE637\uE638"+ + "\uE639\uE63A\uE63B\uE63C\uE63D\uE63E\uE63F\uE640"+ + "\uE641\uE642\uE643\uE644\uE645\u3041\u3042\u3043"+ + "\u3044\u3045\u3046\u3047\u3048\u3049\u304A\u304B"+ + "\u304C\u304D\u304E\u304F\u3050\u3051\u3052\u3053"+ + "\u3054\u3055\u3056\u3057\u3058\u3059\u305A\u305B"+ + "\u305C\u305D\u305E\u305F\u3060\u3061\u3062\u3063"+ + "\u3064\u3065\u3066\u3067\u3068\u3069\u306A\u306B"+ + "\u306C\u306D\u306E\u306F\u3070\u3071\u3072\u3073"+ + "\u3074\u3075\u3076\u3077\u3078\u3079\u307A\u307B"+ + "\u307C\u307D\u307E\u307F\u3080\u3081\u3082\u3083"+ + "\u3084\u3085\u3086\u3087\u3088\u3089\u308A\u308B"+ + "\u308C\u308D\u308E\u308F\u3090\u3091\u3092\u3093"+ + "\uE772\uE773\uE774\uE775\uE776\uE777\uE778\uE779"+ + "\uE77A\uE77B\uE77C\uE646\uE647\uE648\uE649\uE64A"+ + "\uE64B\uE64C\uE64D\uE64E\uE64F\uE650\uE651\uE652"+ + "\uE653\uE654\uE655\uE656\uE657\uE658\uE659\uE65A"+ + "\uE65B\uE65C\uE65D\uE65E\uE65F\uE660\uE661\uE662"+ + "\uE663\uE664\uE665\uE666\uE667\uE668\uE669\uE66A"+ + "\uE66B\uE66C\uE66D\uE66E\uE66F\uE670\uE671\uE672"+ + "\uE673\uE674\uE675\uE676\uE677\uE678\uE679\uE67A"+ + "\uE67B\uE67C\uE67D\uE67E\uE67F\uE680\uE681\uE682"+ + "\uE683\uE684\uFFFD\uE685\uE686\uE687\uE688\uE689"+ + "\uE68A\uE68B\uE68C\uE68D\uE68E\uE68F\uE690\uE691"+ + "\uE692\uE693\uE694\uE695\uE696\uE697\uE698\uE699"+ + "\uE69A\uE69B\uE69C\uE69D\uE69E\uE69F\uE6A0\uE6A1"+ + "\uE6A2\uE6A3\uE6A4\uE6A5\u30A1\u30A2\u30A3\u30A4"+ + "\u30A5\u30A6\u30A7\u30A8\u30A9\u30AA\u30AB\u30AC"+ + "\u30AD\u30AE\u30AF\u30B0\u30B1\u30B2\u30B3\u30B4"+ + "\u30B5\u30B6\u30B7\u30B8\u30B9\u30BA\u30BB\u30BC"+ + "\u30BD\u30BE\u30BF\u30C0\u30C1\u30C2\u30C3\u30C4"+ + "\u30C5\u30C6\u30C7\u30C8\u30C9\u30CA\u30CB\u30CC"+ + "\u30CD\u30CE\u30CF\u30D0\u30D1\u30D2\u30D3\u30D4"+ + "\u30D5\u30D6\u30D7\u30D8\u30D9\u30DA\u30DB\u30DC"+ + "\u30DD\u30DE\u30DF\u30E0\u30E1\u30E2\u30E3\u30E4"+ + "\u30E5\u30E6\u30E7\u30E8\u30E9\u30EA\u30EB\u30EC"+ + "\u30ED\u30EE\u30EF\u30F0\u30F1\u30F2\u30F3\u30F4"+ + "\u30F5\u30F6\uE77D\uE77E\uE77F\uE780\uE781\uE782"+ + "\uE783\uE784\uE6A6\uE6A7\uE6A8\uE6A9\uE6AA\uE6AB"+ + "\uE6AC\uE6AD\uE6AE\uE6AF\uE6B0\uE6B1\uE6B2\uE6B3"+ + "\uE6B4\uE6B5\uE6B6\uE6B7\uE6B8\uE6B9\uE6BA\uE6BB"+ + "\uE6BC\uE6BD\uE6BE\uE6BF\uE6C0\uE6C1\uE6C2\uE6C3"+ + "\uE6C4\uE6C5\uE6C6\uE6C7\uE6C8\uE6C9\uE6CA\uE6CB"+ + "\uE6CC\uE6CD\uE6CE\uE6CF\uE6D0\uE6D1\uE6D2\uE6D3"+ + "\uE6D4\uE6D5\uE6D6\uE6D7\uE6D8\uE6D9\uE6DA\uE6DB"+ + "\uE6DC\uE6DD\uE6DE\uE6DF\uE6E0\uE6E1\uE6E2\uE6E3"+ + "\uE6E4\uFFFD\uE6E5\uE6E6\uE6E7\uE6E8\uE6E9\uE6EA"+ + "\uE6EB\uE6EC\uE6ED\uE6EE\uE6EF\uE6F0\uE6F1\uE6F2"+ + "\uE6F3\uE6F4\uE6F5\uE6F6\uE6F7\uE6F8\uE6F9\uE6FA"+ + "\uE6FB\uE6FC\uE6FD\uE6FE\uE6FF\uE700\uE701\uE702"+ + "\uE703\uE704\uE705\u0391\u0392\u0393\u0394\u0395"+ + "\u0396\u0397\u0398\u0399\u039A\u039B\u039C\u039D"+ + "\u039E\u039F\u03A0\u03A1\u03A3\u03A4\u03A5\u03A6"+ + "\u03A7\u03A8\u03A9\uE785\uE786\uE787\uE788\uE789"+ + "\uE78A\uE78B\uE78C\u03B1\u03B2\u03B3\u03B4\u03B5"+ + "\u03B6\u03B7\u03B8\u03B9\u03BA\u03BB\u03BC\u03BD"+ + "\u03BE\u03BF\u03C0\u03C1\u03C3\u03C4\u03C5\u03C6"+ + "\u03C7\u03C8\u03C9\uE78D\uE78E\uE78F\uE790\uE791"+ + "\uE792\uE793\uFE35\uFE36\uFE39\uFE3A\uFE3F\uFE40"+ + "\uFE3D\uFE3E\uFE41\uFE42\uFE43\uFE44\uE794\uE795"+ + "\uFE3B\uFE3C\uFE37\uFE38\uFE31\uE796\uFE33\uFE34"+ + "\uE797\uE798\uE799\uE79A\uE79B\uE79C\uE79D\uE79E"+ + "\uE79F\uE706\uE707\uE708\uE709\uE70A\uE70B\uE70C"+ + "\uE70D\uE70E\uE70F\uE710\uE711\uE712\uE713\uE714"+ + "\uE715\uE716\uE717\uE718\uE719\uE71A\uE71B\uE71C"+ + "\uE71D\uE71E\uE71F\uE720\uE721\uE722\uE723\uE724"+ + "\uE725\uE726\uE727\uE728\uE729\uE72A\uE72B\uE72C"+ + "\uE72D\uE72E\uE72F\uE730\uE731\uE732\uE733\uE734"+ + "\uE735\uE736\uE737\uE738\uE739\uE73A\uE73B\uE73C"+ + "\uE73D\uE73E\uE73F\uE740\uE741\uE742\uE743\uE744"+ + "\uFFFD\uE745\uE746\uE747\uE748\uE749\uE74A\uE74B"+ + "\uE74C\uE74D\uE74E\uE74F\uE750\uE751\uE752\uE753"+ + "\uE754\uE755\uE756\uE757\uE758\uE759\uE75A\uE75B"+ + "\uE75C\uE75D\uE75E\uE75F\uE760\uE761\uE762\uE763"+ + "\uE764\uE765\u0410\u0411\u0412\u0413\u0414\u0415"+ + "\u0401\u0416\u0417\u0418\u0419\u041A\u041B\u041C"+ + "\u041D\u041E\u041F\u0420\u0421\u0422\u0423\u0424"+ + "\u0425\u0426\u0427\u0428\u0429\u042A\u042B\u042C"+ + "\u042D\u042E\u042F\uE7A0\uE7A1\uE7A2\uE7A3\uE7A4"+ + "\uE7A5\uE7A6\uE7A7\uE7A8\uE7A9\uE7AA\uE7AB\uE7AC"+ + "\uE7AD\uE7AE\u0430\u0431\u0432\u0433\u0434\u0435"+ + "\u0451\u0436\u0437\u0438\u0439\u043A\u043B\u043C"+ + "\u043D\u043E\u043F\u0440\u0441\u0442\u0443\u0444"+ + "\u0445\u0446\u0447\u0448\u0449\u044A\u044B\u044C"+ + "\u044D\u044E\u044F\uE7AF\uE7B0\uE7B1\uE7B2\uE7B3"+ + "\uE7B4\uE7B5\uE7B6\uE7B7\uE7B8\uE7B9\uE7BA\uE7BB"+ + "\u02CA\u02CB\u02D9\u2013\u2015\u2025\u2035\u2105"+ + "\u2109\u2196\u2197\u2198\u2199\u2215\u221F\u2223"+ + "\u2252\u2266\u2267\u22BF\u2550\u2551\u2552\u2553"+ + "\u2554\u2555\u2556\u2557\u2558\u2559\u255A\u255B"+ + "\u255C\u255D\u255E\u255F\u2560\u2561\u2562\u2563"+ + "\u2564\u2565\u2566\u2567\u2568\u2569\u256A\u256B"+ + "\u256C\u256D\u256E\u256F\u2570\u2571\u2572\u2573"+ + "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\uFFFD"+ + "\u2588\u2589\u258A\u258B\u258C\u258D\u258E\u258F"+ + "\u2593\u2594\u2595\u25BC\u25BD\u25E2\u25E3\u25E4"+ + "\u25E5\u2609\u2295\u3012\u301D\u301E\uE7BC\uE7BD"+ + "\uE7BE\uE7BF\uE7C0\uE7C1\uE7C2\uE7C3\uE7C4\uE7C5"+ + "\uE7C6\u0101\u00E1\u01CE\u00E0\u0113\u00E9\u011B"+ + "\u00E8\u012B\u00ED\u01D0\u00EC\u014D\u00F3\u01D2"+ + "\u00F2\u016B\u00FA\u01D4\u00F9\u01D6\u01D8\u01DA"+ + "\u01DC\u00FC\u00EA\u0251\uE7C7\u0144\u0148\u01F9"+ + "\u0261\uE7C9\uE7CA\uE7CB\uE7CC\u3105\u3106\u3107"+ + "\u3108\u3109\u310A\u310B\u310C\u310D\u310E\u310F"+ + "\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117"+ + "\u3118\u3119\u311A\u311B\u311C\u311D\u311E\u311F"+ + "\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127"+ + "\u3128\u3129\uE7CD\uE7CE\uE7CF\uE7D0\uE7D1\uE7D2"+ + "\uE7D3\uE7D4\uE7D5\uE7D6\uE7D7\uE7D8\uE7D9\uE7DA"+ + "\uE7DB\uE7DC\uE7DD\uE7DE\uE7DF\uE7E0\uE7E1\u3021"+ + "\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029"+ + "\u32A3\u338E\u338F\u339C\u339D\u339E\u33A1\u33C4"+ + "\u33CE\u33D1\u33D2\u33D5\uFE30\uFFE2\uFFE4\uE7E2"+ + "\u2121\u3231\uE7E3\u2010\uE7E4\uE7E5\uE7E6\u30FC"+ + "\u309B\u309C\u30FD\u30FE\u3006\u309D\u309E\uFE49"+ + "\uFE4A\uFE4B\uFE4C\uFE4D\uFE4E\uFE4F\uFE50\uFE51"+ + "\uFE52\uFE54\uFE55\uFE56\uFE57\uFE59\uFE5A\uFE5B"+ + "\uFE5C\uFE5D\uFE5E\uFE5F\uFE60\uFE61\uFFFD\uFE62"+ + "\uFE63\uFE64\uFE65\uFE66\uFE68\uFE69\uFE6A\uFE6B"+ + "\u303E\u2FF0\u2FF1\u2FF2\u2FF3\u2FF4\u2FF5\u2FF6"+ + "\u2FF7\u2FF8\u2FF9\u2FFA\u2FFB\u3007\uE7F4\uE7F5"+ + "\uE7F6\uE7F7\uE7F8\uE7F9\uE7FA\uE7FB\uE7FC\uE7FD"+ + "\uE7FE\uE7FF\uE800\u2500\u2501\u2502\u2503\u2504"+ + "\u2505\u2506\u2507\u2508\u2509\u250A\u250B\u250C"+ + "\u250D\u250E\u250F\u2510\u2511\u2512\u2513\u2514"+ + "\u2515\u2516\u2517\u2518\u2519\u251A\u251B\u251C"+ + "\u251D\u251E\u251F\u2520\u2521\u2522\u2523\u2524"+ + "\u2525\u2526\u2527\u2528\u2529\u252A\u252B\u252C"+ + "\u252D\u252E\u252F\u2530\u2531\u2532\u2533\u2534"+ + "\u2535\u2536\u2537\u2538\u2539\u253A\u253B\u253C"+ + "\u253D\u253E\u253F\u2540\u2541\u2542\u2543\u2544"+ + "\u2545\u2546\u2547\u2548\u2549\u254A\u254B\uE801"+ + "\uE802\uE803\uE804\uE805\uE806\uE807\uE808\uE809"+ + "\uE80A\uE80B\uE80C\uE80D\uE80E\uE80F\u72DC\u72DD"+ + "\u72DF\u72E2\u72E3\u72E4\u72E5\u72E6\u72E7\u72EA"+ + "\u72EB\u72F5\u72F6\u72F9\u72FD\u72FE\u72FF\u7300"+ + "\u7302\u7304\u7305\u7306\u7307\u7308\u7309\u730B"+ + "\u730C\u730D\u730F\u7310\u7311\u7312\u7314\u7318"+ + "\u7319\u731A\u731F\u7320\u7323\u7324\u7326\u7327"+ + "\u7328\u732D\u732F\u7330\u7332\u7333\u7335\u7336"+ + "\u733A\u733B\u733C\u733D\u7340\u7341\u7342\u7343"+ + "\u7344\u7345\u7346\u7347\u7348\uFFFD\u7349\u734A"+ + "\u734B\u734C\u734E\u734F\u7351\u7353\u7354\u7355"+ + "\u7356\u7358\u7359\u735A\u735B\u735C\u735D\u735E"+ + "\u735F\u7361\u7362\u7363\u7364\u7365\u7366\u7367"+ + "\u7368\u7369\u736A\u736B\u736E\u7370\u7371\uE000"+ + "\uE001\uE002\uE003\uE004\uE005\uE006\uE007\uE008"+ + "\uE009\uE00A\uE00B\uE00C\uE00D\uE00E\uE00F\uE010"+ + "\uE011\uE012\uE013\uE014\uE015\uE016\uE017\uE018"+ + "\uE019\uE01A\uE01B\uE01C\uE01D\uE01E\uE01F\uE020"+ + "\uE021\uE022\uE023\uE024\uE025\uE026\uE027\uE028"+ + "\uE029\uE02A\uE02B\uE02C\uE02D\uE02E\uE02F\uE030"+ + "\uE031\uE032\uE033\uE034\uE035\uE036\uE037\uE038"+ + "\uE039\uE03A\uE03B\uE03C\uE03D\uE03E\uE03F\uE040"+ + "\uE041\uE042\uE043\uE044\uE045\uE046\uE047\uE048"+ + "\uE049\uE04A\uE04B\uE04C\uE04D\uE04E\uE04F\uE050"+ + "\uE051\uE052\uE053\uE054\uE055\uE056\uE057\uE058"+ + "\uE059\uE05A\uE05B\uE05C\uE05D\u7372\u7373\u7374"+ + "\u7375\u7376\u7377\u7378\u7379\u737A\u737B\u737C"+ + "\u737D\u737F\u7380\u7381\u7382\u7383\u7385\u7386"+ + "\u7388\u738A\u738C\u738D\u738F\u7390\u7392\u7393"+ + "\u7394\u7395\u7397\u7398\u7399\u739A\u739C\u739D"+ + "\u739E\u73A0\u73A1\u73A3\u73A4\u73A5\u73A6\u73A7"+ + "\u73A8\u73AA\u73AC\u73AD\u73B1\u73B4\u73B5\u73B6"+ + "\u73B8\u73B9\u73BC\u73BD\u73BE\u73BF\u73C1\u73C3"+ + "\u73C4\u73C5\u73C6\u73C7\uFFFD\u73CB\u73CC\u73CE"+ + "\u73D2\u73D3\u73D4\u73D5\u73D6\u73D7\u73D8\u73DA"+ + "\u73DB\u73DC\u73DD\u73DF\u73E1\u73E2\u73E3\u73E4"+ + "\u73E6\u73E8\u73EA\u73EB\u73EC\u73EE\u73EF\u73F0"+ + "\u73F1\u73F3\u73F4\u73F5\u73F6\u73F7\uE05E\uE05F"+ + "\uE060\uE061\uE062\uE063\uE064\uE065\uE066\uE067"+ + "\uE068\uE069\uE06A\uE06B\uE06C\uE06D\uE06E\uE06F"+ + "\uE070\uE071\uE072\uE073\uE074\uE075\uE076\uE077"+ + "\uE078\uE079\uE07A\uE07B\uE07C\uE07D\uE07E\uE07F"+ + "\uE080\uE081\uE082\uE083\uE084\uE085\uE086\uE087"+ + "\uE088\uE089\uE08A\uE08B\uE08C\uE08D\uE08E\uE08F"+ + "\uE090\uE091\uE092\uE093\uE094\uE095\uE096\uE097"+ + "\uE098\uE099\uE09A\uE09B\uE09C\uE09D\uE09E\uE09F"+ + "\uE0A0\uE0A1\uE0A2\uE0A3\uE0A4\uE0A5\uE0A6\uE0A7"+ + "\uE0A8\uE0A9\uE0AA\uE0AB\uE0AC\uE0AD\uE0AE\uE0AF"+ + "\uE0B0\uE0B1\uE0B2\uE0B3\uE0B4\uE0B5\uE0B6\uE0B7"+ + "\uE0B8\uE0B9\uE0BA\uE0BB\u73F8\u73F9\u73FA\u73FB"+ + "\u73FC\u73FD\u73FE\u73FF\u7400\u7401\u7402\u7404"+ + "\u7407\u7408\u740B\u740C\u740D\u740E\u7411\u7412"+ + "\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741C"+ + "\u741D\u741E\u741F\u7420\u7421\u7423\u7424\u7427"+ + "\u7429\u742B\u742D\u742F\u7431\u7432\u7437\u7438"+ + "\u7439\u743A\u743B\u743D\u743E\u743F\u7440\u7442"+ + "\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744A"+ + "\u744B\u744C\u744D\uFFFD\u744E\u744F\u7450\u7451"+ + "\u7452\u7453\u7454\u7456\u7458\u745D\u7460\u7461"+ + "\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469"+ + "\u746A\u746B\u746C\u746E\u746F\u7471\u7472\u7473"+ + "\u7474\u7475\u7478\u7479\u747A\uE0BC\uE0BD\uE0BE"+ + "\uE0BF\uE0C0\uE0C1\uE0C2\uE0C3\uE0C4\uE0C5\uE0C6"+ + "\uE0C7\uE0C8\uE0C9\uE0CA\uE0CB\uE0CC\uE0CD\uE0CE"+ + "\uE0CF\uE0D0\uE0D1\uE0D2\uE0D3\uE0D4\uE0D5\uE0D6"+ + "\uE0D7\uE0D8\uE0D9\uE0DA\uE0DB\uE0DC\uE0DD\uE0DE"+ + "\uE0DF\uE0E0\uE0E1\uE0E2\uE0E3\uE0E4\uE0E5\uE0E6"+ + "\uE0E7\uE0E8\uE0E9\uE0EA\uE0EB\uE0EC\uE0ED\uE0EE"+ + "\uE0EF\uE0F0\uE0F1\uE0F2\uE0F3\uE0F4\uE0F5\uE0F6"+ + "\uE0F7\uE0F8\uE0F9\uE0FA\uE0FB\uE0FC\uE0FD\uE0FE"+ + "\uE0FF\uE100\uE101\uE102\uE103\uE104\uE105\uE106"+ + "\uE107\uE108\uE109\uE10A\uE10B\uE10C\uE10D\uE10E"+ + "\uE10F\uE110\uE111\uE112\uE113\uE114\uE115\uE116"+ + "\uE117\uE118\uE119\u747B\u747C\u747D\u747F\u7482"+ + "\u7484\u7485\u7486\u7488\u7489\u748A\u748C\u748D"+ + "\u748F\u7491\u7492\u7493\u7494\u7495\u7496\u7497"+ + "\u7498\u7499\u749A\u749B\u749D\u749F\u74A0\u74A1"+ + "\u74A2\u74A3\u74A4\u74A5\u74A6\u74AA\u74AB\u74AC"+ + "\u74AD\u74AE\u74AF\u74B0\u74B1\u74B2\u74B3\u74B4"+ + "\u74B5\u74B6\u74B7\u74B8\u74B9\u74BB\u74BC\u74BD"+ + "\u74BE\u74BF\u74C0\u74C1\u74C2\u74C3\u74C4\u74C5"+ + "\u74C6\u74C7\uFFFD\u74C8\u74C9\u74CA\u74CB\u74CC"+ + "\u74CD\u74CE\u74CF\u74D0\u74D1\u74D3\u74D4\u74D5"+ + "\u74D6\u74D7\u74D8\u74D9\u74DA\u74DB\u74DD\u74DF"+ + "\u74E1\u74E5\u74E7\u74E8\u74E9\u74EA\u74EB\u74EC"+ + "\u74ED\u74F0\u74F1\u74F2\uE11A\uE11B\uE11C\uE11D"+ + "\uE11E\uE11F\uE120\uE121\uE122\uE123\uE124\uE125"+ + "\uE126\uE127\uE128\uE129\uE12A\uE12B\uE12C\uE12D"+ + "\uE12E\uE12F\uE130\uE131\uE132\uE133\uE134\uE135"+ + "\uE136\uE137\uE138\uE139\uE13A\uE13B\uE13C\uE13D"+ + "\uE13E\uE13F\uE140\uE141\uE142\uE143\uE144\uE145"+ + "\uE146\uE147\uE148\uE149\uE14A\uE14B\uE14C\uE14D"+ + "\uE14E\uE14F\uE150\uE151\uE152\uE153\uE154\uE155"+ + "\uE156\uE157\uE158\uE159\uE15A\uE15B\uE15C\uE15D"+ + "\uE15E\uE15F\uE160\uE161\uE162\uE163\uE164\uE165"+ + "\uE166\uE167\uE168\uE169\uE16A\uE16B\uE16C\uE16D"+ + "\uE16E\uE16F\uE170\uE171\uE172\uE173\uE174\uE175"+ + "\uE176\uE177\u74F3\u74F5\u74F8\u74F9\u74FA\u74FB"+ + "\u74FC\u74FD\u74FE\u7500\u7501\u7502\u7503\u7505"+ + "\u7506\u7507\u7508\u7509\u750A\u750B\u750C\u750E"+ + "\u7510\u7512\u7514\u7515\u7516\u7517\u751B\u751D"+ + "\u751E\u7520\u7521\u7522\u7523\u7524\u7526\u7527"+ + "\u752A\u752E\u7534\u7536\u7539\u753C\u753D\u753F"+ + "\u7541\u7542\u7543\u7544\u7546\u7547\u7549\u754A"+ + "\u754D\u7550\u7551\u7552\u7553\u7555\u7556\u7557"+ + "\u7558\uFFFD\u755D\u755E\u755F\u7560\u7561\u7562"+ + "\u7563\u7564\u7567\u7568\u7569\u756B\u756C\u756D"+ + "\u756E\u756F\u7570\u7571\u7573\u7575\u7576\u7577"+ + "\u757A\u757B\u757C\u757D\u757E\u7580\u7581\u7582"+ + "\u7584\u7585\u7587\uE178\uE179\uE17A\uE17B\uE17C"+ + "\uE17D\uE17E\uE17F\uE180\uE181\uE182\uE183\uE184"+ + "\uE185\uE186\uE187\uE188\uE189\uE18A\uE18B\uE18C"+ + "\uE18D\uE18E\uE18F\uE190\uE191\uE192\uE193\uE194"+ + "\uE195\uE196\uE197\uE198\uE199\uE19A\uE19B\uE19C"+ + "\uE19D\uE19E\uE19F\uE1A0\uE1A1\uE1A2\uE1A3\uE1A4"+ + "\uE1A5\uE1A6\uE1A7\uE1A8\uE1A9\uE1AA\uE1AB\uE1AC"+ + "\uE1AD\uE1AE\uE1AF\uE1B0\uE1B1\uE1B2\uE1B3\uE1B4"+ + "\uE1B5\uE1B6\uE1B7\uE1B8\uE1B9\uE1BA\uE1BB\uE1BC"+ + "\uE1BD\uE1BE\uE1BF\uE1C0\uE1C1\uE1C2\uE1C3\uE1C4"+ + "\uE1C5\uE1C6\uE1C7\uE1C8\uE1C9\uE1CA\uE1CB\uE1CC"+ + "\uE1CD\uE1CE\uE1CF\uE1D0\uE1D1\uE1D2\uE1D3\uE1D4"+ + "\uE1D5\u7588\u7589\u758A\u758C\u758D\u758E\u7590"+ + "\u7593\u7595\u7598\u759B\u759C\u759E\u75A2\u75A6"+ + "\u75A7\u75A8\u75A9\u75AA\u75AD\u75B6\u75B7\u75BA"+ + "\u75BB\u75BF\u75C0\u75C1\u75C6\u75CB\u75CC\u75CE"+ + "\u75CF\u75D0\u75D1\u75D3\u75D7\u75D9\u75DA\u75DC"+ + "\u75DD\u75DF\u75E0\u75E1\u75E5\u75E9\u75EC\u75ED"+ + "\u75EE\u75EF\u75F2\u75F3\u75F5\u75F6\u75F7\u75F8"+ + "\u75FA\u75FB\u75FD\u75FE\u7602\u7604\u7606\u7607"+ + "\uFFFD\u7608\u7609\u760B\u760D\u760E\u760F\u7611"+ + "\u7612\u7613\u7614\u7616\u761A\u761C\u761D\u761E"+ + "\u7621\u7623\u7627\u7628\u762C\u762E\u762F\u7631"+ + "\u7632\u7636\u7637\u7639\u763A\u763B\u763D\u7641"+ + "\u7642\u7644\uE1D6\uE1D7\uE1D8\uE1D9\uE1DA\uE1DB"+ + "\uE1DC\uE1DD\uE1DE\uE1DF\uE1E0\uE1E1\uE1E2\uE1E3"+ + "\uE1E4\uE1E5\uE1E6\uE1E7\uE1E8\uE1E9\uE1EA\uE1EB"+ + "\uE1EC\uE1ED\uE1EE\uE1EF\uE1F0\uE1F1\uE1F2\uE1F3"+ + "\uE1F4\uE1F5\uE1F6\uE1F7\uE1F8\uE1F9\uE1FA\uE1FB"+ + "\uE1FC\uE1FD\uE1FE\uE1FF\uE200\uE201\uE202\uE203"+ + "\uE204\uE205\uE206\uE207\uE208\uE209\uE20A\uE20B"+ + "\uE20C\uE20D\uE20E\uE20F\uE210\uE211\uE212\uE213"+ + "\uE214\uE215\uE216\uE217\uE218\uE219\uE21A\uE21B"+ + "\uE21C\uE21D\uE21E\uE21F\uE220\uE221\uE222\uE223"+ + "\uE224\uE225\uE226\uE227\uE228\uE229\uE22A\uE22B"+ + "\uE22C\uE22D\uE22E\uE22F\uE230\uE231\uE232\uE233"; + + private final static String innerIndex3= + "\u7645\u7646\u7647\u7648\u7649\u764A\u764B\u764E"+ + "\u764F\u7650\u7651\u7652\u7653\u7655\u7657\u7658"+ + "\u7659\u765A\u765B\u765D\u765F\u7660\u7661\u7662"+ + "\u7664\u7665\u7666\u7667\u7668\u7669\u766A\u766C"+ + "\u766D\u766E\u7670\u7671\u7672\u7673\u7674\u7675"+ + "\u7676\u7677\u7679\u767A\u767C\u767F\u7680\u7681"+ + "\u7683\u7685\u7689\u768A\u768C\u768D\u768F\u7690"+ + "\u7692\u7694\u7695\u7697\u7698\u769A\u769B\uFFFD"+ + "\u769C\u769D\u769E\u769F\u76A0\u76A1\u76A2\u76A3"+ + "\u76A5\u76A6\u76A7\u76A8\u76A9\u76AA\u76AB\u76AC"+ + "\u76AD\u76AF\u76B0\u76B3\u76B5\u76B6\u76B7\u76B8"+ + "\u76B9\u76BA\u76BB\u76BC\u76BD\u76BE\u76C0\u76C1"+ + "\u76C3\u554A\u963F\u57C3\u6328\u54CE\u5509\u54C0"+ + "\u7691\u764C\u853C\u77EE\u827E\u788D\u7231\u9698"+ + "\u978D\u6C28\u5B89\u4FFA\u6309\u6697\u5CB8\u80FA"+ + "\u6848\u80AE\u6602\u76CE\u51F9\u6556\u71AC\u7FF1"+ + "\u8884\u50B2\u5965\u61CA\u6FB3\u82AD\u634C\u6252"+ + "\u53ED\u5427\u7B06\u516B\u75A4\u5DF4\u62D4\u8DCB"+ + "\u9776\u628A\u8019\u575D\u9738\u7F62\u7238\u767D"+ + "\u67CF\u767E\u6446\u4F70\u8D25\u62DC\u7A17\u6591"+ + "\u73ED\u642C\u6273\u822C\u9881\u677F\u7248\u626E"+ + "\u62CC\u4F34\u74E3\u534A\u529E\u7ECA\u90A6\u5E2E"+ + "\u6886\u699C\u8180\u7ED1\u68D2\u78C5\u868C\u9551"+ + "\u508D\u8C24\u82DE\u80DE\u5305\u8912\u5265\u76C4"+ + "\u76C7\u76C9\u76CB\u76CC\u76D3\u76D5\u76D9\u76DA"+ + "\u76DC\u76DD\u76DE\u76E0\u76E1\u76E2\u76E3\u76E4"+ + "\u76E6\u76E7\u76E8\u76E9\u76EA\u76EB\u76EC\u76ED"+ + "\u76F0\u76F3\u76F5\u76F6\u76F7\u76FA\u76FB\u76FD"+ + "\u76FF\u7700\u7702\u7703\u7705\u7706\u770A\u770C"+ + "\u770E\u770F\u7710\u7711\u7712\u7713\u7714\u7715"+ + "\u7716\u7717\u7718\u771B\u771C\u771D\u771E\u7721"+ + "\u7723\u7724\u7725\u7727\u772A\u772B\uFFFD\u772C"+ + "\u772E\u7730\u7731\u7732\u7733\u7734\u7739\u773B"+ + "\u773D\u773E\u773F\u7742\u7744\u7745\u7746\u7748"+ + "\u7749\u774A\u774B\u774C\u774D\u774E\u774F\u7752"+ + "\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775C"+ + "\u8584\u96F9\u4FDD\u5821\u9971\u5B9D\u62B1\u62A5"+ + "\u66B4\u8C79\u9C8D\u7206\u676F\u7891\u60B2\u5351"+ + "\u5317\u8F88\u80CC\u8D1D\u94A1\u500D\u72C8\u5907"+ + "\u60EB\u7119\u88AB\u5954\u82EF\u672C\u7B28\u5D29"+ + "\u7EF7\u752D\u6CF5\u8E66\u8FF8\u903C\u9F3B\u6BD4"+ + "\u9119\u7B14\u5F7C\u78A7\u84D6\u853D\u6BD5\u6BD9"+ + "\u6BD6\u5E01\u5E87\u75F9\u95ED\u655D\u5F0A\u5FC5"+ + "\u8F9F\u58C1\u81C2\u907F\u965B\u97AD\u8FB9\u7F16"+ + "\u8D2C\u6241\u4FBF\u53D8\u535E\u8FA8\u8FA9\u8FAB"+ + "\u904D\u6807\u5F6A\u8198\u8868\u9CD6\u618B\u522B"+ + "\u762A\u5F6C\u658C\u6FD2\u6EE8\u5BBE\u6448\u5175"+ + "\u51B0\u67C4\u4E19\u79C9\u997C\u70B3\u775D\u775E"+ + "\u775F\u7760\u7764\u7767\u7769\u776A\u776D\u776E"+ + "\u776F\u7770\u7771\u7772\u7773\u7774\u7775\u7776"+ + "\u7777\u7778\u777A\u777B\u777C\u7781\u7782\u7783"+ + "\u7786\u7787\u7788\u7789\u778A\u778B\u778F\u7790"+ + "\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779A"+ + "\u779B\u779C\u779D\u779E\u77A1\u77A3\u77A4\u77A6"+ + "\u77A8\u77AB\u77AD\u77AE\u77AF\u77B1\u77B2\u77B4"+ + "\u77B6\u77B7\u77B8\u77B9\u77BA\uFFFD\u77BC\u77BE"+ + "\u77C0\u77C1\u77C2\u77C3\u77C4\u77C5\u77C6\u77C7"+ + "\u77C8\u77C9\u77CA\u77CB\u77CC\u77CE\u77CF\u77D0"+ + "\u77D1\u77D2\u77D3\u77D4\u77D5\u77D6\u77D8\u77D9"+ + "\u77DA\u77DD\u77DE\u77DF\u77E0\u77E1\u77E4\u75C5"+ + "\u5E76\u73BB\u83E0\u64AD\u62E8\u94B5\u6CE2\u535A"+ + "\u52C3\u640F\u94C2\u7B94\u4F2F\u5E1B\u8236\u8116"+ + "\u818A\u6E24\u6CCA\u9A73\u6355\u535C\u54FA\u8865"+ + "\u57E0\u4E0D\u5E03\u6B65\u7C3F\u90E8\u6016\u64E6"+ + "\u731C\u88C1\u6750\u624D\u8D22\u776C\u8E29\u91C7"+ + "\u5F69\u83DC\u8521\u9910\u53C2\u8695\u6B8B\u60ED"+ + "\u60E8\u707F\u82CD\u8231\u4ED3\u6CA7\u85CF\u64CD"+ + "\u7CD9\u69FD\u66F9\u8349\u5395\u7B56\u4FA7\u518C"+ + "\u6D4B\u5C42\u8E6D\u63D2\u53C9\u832C\u8336\u67E5"+ + "\u78B4\u643D\u5BDF\u5C94\u5DEE\u8BE7\u62C6\u67F4"+ + "\u8C7A\u6400\u63BA\u8749\u998B\u8C17\u7F20\u94F2"+ + "\u4EA7\u9610\u98A4\u660C\u7316\u77E6\u77E8\u77EA"+ + "\u77EF\u77F0\u77F1\u77F2\u77F4\u77F5\u77F7\u77F9"+ + "\u77FA\u77FB\u77FC\u7803\u7804\u7805\u7806\u7807"+ + "\u7808\u780A\u780B\u780E\u780F\u7810\u7813\u7815"+ + "\u7819\u781B\u781E\u7820\u7821\u7822\u7824\u7828"+ + "\u782A\u782B\u782E\u782F\u7831\u7832\u7833\u7835"+ + "\u7836\u783D\u783F\u7841\u7842\u7843\u7844\u7846"+ + "\u7848\u7849\u784A\u784B\u784D\u784F\u7851\u7853"+ + "\u7854\u7858\u7859\u785A\uFFFD\u785B\u785C\u785E"+ + "\u785F\u7860\u7861\u7862\u7863\u7864\u7865\u7866"+ + "\u7867\u7868\u7869\u786F\u7870\u7871\u7872\u7873"+ + "\u7874\u7875\u7876\u7878\u7879\u787A\u787B\u787D"+ + "\u787E\u787F\u7880\u7881\u7882\u7883\u573A\u5C1D"+ + "\u5E38\u957F\u507F\u80A0\u5382\u655E\u7545\u5531"+ + "\u5021\u8D85\u6284\u949E\u671D\u5632\u6F6E\u5DE2"+ + "\u5435\u7092\u8F66\u626F\u64A4\u63A3\u5F7B\u6F88"+ + "\u90F4\u81E3\u8FB0\u5C18\u6668\u5FF1\u6C89\u9648"+ + "\u8D81\u886C\u6491\u79F0\u57CE\u6A59\u6210\u5448"+ + "\u4E58\u7A0B\u60E9\u6F84\u8BDA\u627F\u901E\u9A8B"+ + "\u79E4\u5403\u75F4\u6301\u5319\u6C60\u8FDF\u5F1B"+ + "\u9A70\u803B\u9F7F\u4F88\u5C3A\u8D64\u7FC5\u65A5"+ + "\u70BD\u5145\u51B2\u866B\u5D07\u5BA0\u62BD\u916C"+ + "\u7574\u8E0C\u7A20\u6101\u7B79\u4EC7\u7EF8\u7785"+ + "\u4E11\u81ED\u521D\u51FA\u6A71\u53A8\u8E87\u9504"+ + "\u96CF\u6EC1\u9664\u695A\u7884\u7885\u7886\u7888"+ + "\u788A\u788B\u788F\u7890\u7892\u7894\u7895\u7896"+ + "\u7899\u789D\u789E\u78A0\u78A2\u78A4\u78A6\u78A8"+ + "\u78A9\u78AA\u78AB\u78AC\u78AD\u78AE\u78AF\u78B5"+ + "\u78B6\u78B7\u78B8\u78BA\u78BB\u78BC\u78BD\u78BF"+ + "\u78C0\u78C2\u78C3\u78C4\u78C6\u78C7\u78C8\u78CC"+ + "\u78CD\u78CE\u78CF\u78D1\u78D2\u78D3\u78D6\u78D7"+ + "\u78D8\u78DA\u78DB\u78DC\u78DD\u78DE\u78DF\u78E0"+ + "\u78E1\u78E2\u78E3\uFFFD\u78E4\u78E5\u78E6\u78E7"+ + "\u78E9\u78EA\u78EB\u78ED\u78EE\u78EF\u78F0\u78F1"+ + "\u78F3\u78F5\u78F6\u78F8\u78F9\u78FB\u78FC\u78FD"+ + "\u78FE\u78FF\u7900\u7902\u7903\u7904\u7906\u7907"+ + "\u7908\u7909\u790A\u790B\u790C\u7840\u50A8\u77D7"+ + "\u6410\u89E6\u5904\u63E3\u5DDD\u7A7F\u693D\u4F20"+ + "\u8239\u5598\u4E32\u75AE\u7A97\u5E62\u5E8A\u95EF"+ + "\u521B\u5439\u708A\u6376\u9524\u5782\u6625\u693F"+ + "\u9187\u5507\u6DF3\u7EAF\u8822\u6233\u7EF0\u75B5"+ + "\u8328\u78C1\u96CC\u8F9E\u6148\u74F7\u8BCD\u6B64"+ + "\u523A\u8D50\u6B21\u806A\u8471\u56F1\u5306\u4ECE"+ + "\u4E1B\u51D1\u7C97\u918B\u7C07\u4FC3\u8E7F\u7BE1"+ + "\u7A9C\u6467\u5D14\u50AC\u8106\u7601\u7CB9\u6DEC"+ + "\u7FE0\u6751\u5B58\u5BF8\u78CB\u64AE\u6413\u63AA"+ + "\u632B\u9519\u642D\u8FBE\u7B54\u7629\u6253\u5927"+ + "\u5446\u6B79\u50A3\u6234\u5E26\u6B86\u4EE3\u8D37"+ + "\u888B\u5F85\u902E\u790D\u790E\u790F\u7910\u7911"+ + "\u7912\u7914\u7915\u7916\u7917\u7918\u7919\u791A"+ + "\u791B\u791C\u791D\u791F\u7920\u7921\u7922\u7923"+ + "\u7925\u7926\u7927\u7928\u7929\u792A\u792B\u792C"+ + "\u792D\u792E\u792F\u7930\u7931\u7932\u7933\u7935"+ + "\u7936\u7937\u7938\u7939\u793D\u793F\u7942\u7943"+ + "\u7944\u7945\u7947\u794A\u794B\u794C\u794D\u794E"+ + "\u794F\u7950\u7951\u7952\u7954\u7955\u7958\u7959"+ + "\u7961\u7963\uFFFD\u7964\u7966\u7969\u796A\u796B"+ + "\u796C\u796E\u7970\u7971\u7972\u7973\u7974\u7975"+ + "\u7976\u7979\u797B\u797C\u797D\u797E\u797F\u7982"+ + "\u7983\u7986\u7987\u7988\u7989\u798B\u798C\u798D"+ + "\u798E\u7990\u7991\u7992\u6020\u803D\u62C5\u4E39"+ + "\u5355\u90F8\u63B8\u80C6\u65E6\u6C2E\u4F46\u60EE"+ + "\u6DE1\u8BDE\u5F39\u86CB\u5F53\u6321\u515A\u8361"+ + "\u6863\u5200\u6363\u8E48\u5012\u5C9B\u7977\u5BFC"+ + "\u5230\u7A3B\u60BC\u9053\u76D7\u5FB7\u5F97\u7684"+ + "\u8E6C\u706F\u767B\u7B49\u77AA\u51F3\u9093\u5824"+ + "\u4F4E\u6EF4\u8FEA\u654C\u7B1B\u72C4\u6DA4\u7FDF"+ + "\u5AE1\u62B5\u5E95\u5730\u8482\u7B2C\u5E1D\u5F1F"+ + "\u9012\u7F14\u98A0\u6382\u6EC7\u7898\u70B9\u5178"+ + "\u975B\u57AB\u7535\u4F43\u7538\u5E97\u60E6\u5960"+ + "\u6DC0\u6BBF\u7889\u53FC\u96D5\u51CB\u5201\u6389"+ + "\u540A\u9493\u8C03\u8DCC\u7239\u789F\u8776\u8FED"+ + "\u8C0D\u53E0\u7993\u7994\u7995\u7996\u7997\u7998"+ + "\u7999\u799B\u799C\u799D\u799E\u799F\u79A0\u79A1"+ + "\u79A2\u79A3\u79A4\u79A5\u79A6\u79A8\u79A9\u79AA"+ + "\u79AB\u79AC\u79AD\u79AE\u79AF\u79B0\u79B1\u79B2"+ + "\u79B4\u79B5\u79B6\u79B7\u79B8\u79BC\u79BF\u79C2"+ + "\u79C4\u79C5\u79C7\u79C8\u79CA\u79CC\u79CE\u79CF"+ + "\u79D0\u79D3\u79D4\u79D6\u79D7\u79D9\u79DA\u79DB"+ + "\u79DC\u79DD\u79DE\u79E0\u79E1\u79E2\u79E5\u79E8"+ + "\u79EA\uFFFD\u79EC\u79EE\u79F1\u79F2\u79F3\u79F4"+ + "\u79F5\u79F6\u79F7\u79F9\u79FA\u79FC\u79FE\u79FF"+ + "\u7A01\u7A04\u7A05\u7A07\u7A08\u7A09\u7A0A\u7A0C"+ + "\u7A0F\u7A10\u7A11\u7A12\u7A13\u7A15\u7A16\u7A18"+ + "\u7A19\u7A1B\u7A1C\u4E01\u76EF\u53EE\u9489\u9876"+ + "\u9F0E\u952D\u5B9A\u8BA2\u4E22\u4E1C\u51AC\u8463"+ + "\u61C2\u52A8\u680B\u4F97\u606B\u51BB\u6D1E\u515C"+ + "\u6296\u6597\u9661\u8C46\u9017\u75D8\u90FD\u7763"+ + "\u6BD2\u728A\u72EC\u8BFB\u5835\u7779\u8D4C\u675C"+ + "\u9540\u809A\u5EA6\u6E21\u5992\u7AEF\u77ED\u953B"+ + "\u6BB5\u65AD\u7F0E\u5806\u5151\u961F\u5BF9\u58A9"+ + "\u5428\u8E72\u6566\u987F\u56E4\u949D\u76FE\u9041"+ + "\u6387\u54C6\u591A\u593A\u579B\u8EB2\u6735\u8DFA"+ + "\u8235\u5241\u60F0\u5815\u86FE\u5CE8\u9E45\u4FC4"+ + "\u989D\u8BB9\u5A25\u6076\u5384\u627C\u904F\u9102"+ + "\u997F\u6069\u800C\u513F\u8033\u5C14\u9975\u6D31"+ + "\u4E8C\u7A1D\u7A1F\u7A21\u7A22\u7A24\u7A25\u7A26"+ + "\u7A27\u7A28\u7A29\u7A2A\u7A2B\u7A2C\u7A2D\u7A2E"+ + "\u7A2F\u7A30\u7A31\u7A32\u7A34\u7A35\u7A36\u7A38"+ + "\u7A3A\u7A3E\u7A40\u7A41\u7A42\u7A43\u7A44\u7A45"+ + "\u7A47\u7A48\u7A49\u7A4A\u7A4B\u7A4C\u7A4D\u7A4E"+ + "\u7A4F\u7A50\u7A52\u7A53\u7A54\u7A55\u7A56\u7A58"+ + "\u7A59\u7A5A\u7A5B\u7A5C\u7A5D\u7A5E\u7A5F\u7A60"+ + "\u7A61\u7A62\u7A63\u7A64\u7A65\u7A66\u7A67\u7A68"+ + "\uFFFD\u7A69\u7A6A\u7A6B\u7A6C\u7A6D\u7A6E\u7A6F"+ + "\u7A71\u7A72\u7A73\u7A75\u7A7B\u7A7C\u7A7D\u7A7E"+ + "\u7A82\u7A85\u7A87\u7A89\u7A8A\u7A8B\u7A8C\u7A8E"+ + "\u7A8F\u7A90\u7A93\u7A94\u7A99\u7A9A\u7A9B\u7A9E"+ + "\u7AA1\u7AA2\u8D30\u53D1\u7F5A\u7B4F\u4F10\u4E4F"+ + "\u9600\u6CD5\u73D0\u85E9\u5E06\u756A\u7FFB\u6A0A"+ + "\u77FE\u9492\u7E41\u51E1\u70E6\u53CD\u8FD4\u8303"+ + "\u8D29\u72AF\u996D\u6CDB\u574A\u82B3\u65B9\u80AA"+ + "\u623F\u9632\u59A8\u4EFF\u8BBF\u7EBA\u653E\u83F2"+ + "\u975E\u5561\u98DE\u80A5\u532A\u8BFD\u5420\u80BA"+ + "\u5E9F\u6CB8\u8D39\u82AC\u915A\u5429\u6C1B\u5206"+ + "\u7EB7\u575F\u711A\u6C7E\u7C89\u594B\u4EFD\u5FFF"+ + "\u6124\u7CAA\u4E30\u5C01\u67AB\u8702\u5CF0\u950B"+ + "\u98CE\u75AF\u70FD\u9022\u51AF\u7F1D\u8BBD\u5949"+ + "\u51E4\u4F5B\u5426\u592B\u6577\u80A4\u5B75\u6276"+ + "\u62C2\u8F90\u5E45\u6C1F\u7B26\u4F0F\u4FD8\u670D"+ + "\u7AA3\u7AA4\u7AA7\u7AA9\u7AAA\u7AAB\u7AAE\u7AAF"+ + "\u7AB0\u7AB1\u7AB2\u7AB4\u7AB5\u7AB6\u7AB7\u7AB8"+ + "\u7AB9\u7ABA\u7ABB\u7ABC\u7ABD\u7ABE\u7AC0\u7AC1"+ + "\u7AC2\u7AC3\u7AC4\u7AC5\u7AC6\u7AC7\u7AC8\u7AC9"+ + "\u7ACA\u7ACC\u7ACD\u7ACE\u7ACF\u7AD0\u7AD1\u7AD2"+ + "\u7AD3\u7AD4\u7AD5\u7AD7\u7AD8\u7ADA\u7ADB\u7ADC"+ + "\u7ADD\u7AE1\u7AE2\u7AE4\u7AE7\u7AE8\u7AE9\u7AEA"+ + "\u7AEB\u7AEC\u7AEE\u7AF0\u7AF1\u7AF2\u7AF3\uFFFD"+ + "\u7AF4\u7AF5\u7AF6\u7AF7\u7AF8\u7AFB\u7AFC\u7AFE"+ + "\u7B00\u7B01\u7B02\u7B05\u7B07\u7B09\u7B0C\u7B0D"+ + "\u7B0E\u7B10\u7B12\u7B13\u7B16\u7B17\u7B18\u7B1A"+ + "\u7B1C\u7B1D\u7B1F\u7B21\u7B22\u7B23\u7B27\u7B29"+ + "\u7B2D\u6D6E\u6DAA\u798F\u88B1\u5F17\u752B\u629A"+ + "\u8F85\u4FEF\u91DC\u65A7\u812F\u8151\u5E9C\u8150"+ + "\u8D74\u526F\u8986\u8D4B\u590D\u5085\u4ED8\u961C"+ + "\u7236\u8179\u8D1F\u5BCC\u8BA3\u9644\u5987\u7F1A"+ + "\u5490\u5676\u560E\u8BE5\u6539\u6982\u9499\u76D6"+ + "\u6E89\u5E72\u7518\u6746\u67D1\u7AFF\u809D\u8D76"+ + "\u611F\u79C6\u6562\u8D63\u5188\u521A\u94A2\u7F38"+ + "\u809B\u7EB2\u5C97\u6E2F\u6760\u7BD9\u768B\u9AD8"+ + "\u818F\u7F94\u7CD5\u641E\u9550\u7A3F\u544A\u54E5"+ + "\u6B4C\u6401\u6208\u9E3D\u80F3\u7599\u5272\u9769"+ + "\u845B\u683C\u86E4\u9601\u9694\u94EC\u4E2A\u5404"+ + "\u7ED9\u6839\u8DDF\u8015\u66F4\u5E9A\u7FB9\u7B2F"+ + "\u7B30\u7B32\u7B34\u7B35\u7B36\u7B37\u7B39\u7B3B"+ + "\u7B3D\u7B3F\u7B40\u7B41\u7B42\u7B43\u7B44\u7B46"+ + "\u7B48\u7B4A\u7B4D\u7B4E\u7B53\u7B55\u7B57\u7B59"+ + "\u7B5C\u7B5E\u7B5F\u7B61\u7B63\u7B64\u7B65\u7B66"+ + "\u7B67\u7B68\u7B69\u7B6A\u7B6B\u7B6C\u7B6D\u7B6F"+ + "\u7B70\u7B73\u7B74\u7B76\u7B78\u7B7A\u7B7C\u7B7D"+ + "\u7B7F\u7B81\u7B82\u7B83\u7B84\u7B86\u7B87\u7B88"+ + "\u7B89\u7B8A\u7B8B\u7B8C\u7B8E\u7B8F\uFFFD\u7B91"+ + "\u7B92\u7B93\u7B96\u7B98\u7B99\u7B9A\u7B9B\u7B9E"+ + "\u7B9F\u7BA0\u7BA3\u7BA4\u7BA5\u7BAE\u7BAF\u7BB0"+ + "\u7BB2\u7BB3\u7BB5\u7BB6\u7BB7\u7BB9\u7BBA\u7BBB"+ + "\u7BBC\u7BBD\u7BBE\u7BBF\u7BC0\u7BC2\u7BC3\u7BC4"+ + "\u57C2\u803F\u6897\u5DE5\u653B\u529F\u606D\u9F9A"+ + "\u4F9B\u8EAC\u516C\u5BAB\u5F13\u5DE9\u6C5E\u62F1"+ + "\u8D21\u5171\u94A9\u52FE\u6C9F\u82DF\u72D7\u57A2"+ + "\u6784\u8D2D\u591F\u8F9C\u83C7\u5495\u7B8D\u4F30"+ + "\u6CBD\u5B64\u59D1\u9F13\u53E4\u86CA\u9AA8\u8C37"+ + "\u80A1\u6545\u987E\u56FA\u96C7\u522E\u74DC\u5250"+ + "\u5BE1\u6302\u8902\u4E56\u62D0\u602A\u68FA\u5173"+ + "\u5B98\u51A0\u89C2\u7BA1\u9986\u7F50\u60EF\u704C"+ + "\u8D2F\u5149\u5E7F\u901B\u7470\u89C4\u572D\u7845"+ + "\u5F52\u9F9F\u95FA\u8F68\u9B3C\u8BE1\u7678\u6842"+ + "\u67DC\u8DEA\u8D35\u523D\u8F8A\u6EDA\u68CD\u9505"+ + "\u90ED\u56FD\u679C\u88F9\u8FC7\u54C8\u7BC5\u7BC8"+ + "\u7BC9\u7BCA\u7BCB\u7BCD\u7BCE\u7BCF\u7BD0\u7BD2"+ + "\u7BD4\u7BD5\u7BD6\u7BD7\u7BD8\u7BDB\u7BDC\u7BDE"+ + "\u7BDF\u7BE0\u7BE2\u7BE3\u7BE4\u7BE7\u7BE8\u7BE9"+ + "\u7BEB\u7BEC\u7BED\u7BEF\u7BF0\u7BF2\u7BF3\u7BF4"+ + "\u7BF5\u7BF6\u7BF8\u7BF9\u7BFA\u7BFB\u7BFD\u7BFF"+ + "\u7C00\u7C01\u7C02\u7C03\u7C04\u7C05\u7C06\u7C08"+ + "\u7C09\u7C0A\u7C0D\u7C0E\u7C10\u7C11\u7C12\u7C13"+ + "\u7C14\u7C15\u7C17\u7C18\u7C19\uFFFD\u7C1A\u7C1B"+ + "\u7C1C\u7C1D\u7C1E\u7C20\u7C21\u7C22\u7C23\u7C24"+ + "\u7C25\u7C28\u7C29\u7C2B\u7C2C\u7C2D\u7C2E\u7C2F"+ + "\u7C30\u7C31\u7C32\u7C33\u7C34\u7C35\u7C36\u7C37"+ + "\u7C39\u7C3A\u7C3B\u7C3C\u7C3D\u7C3E\u7C42\u9AB8"+ + "\u5B69\u6D77\u6C26\u4EA5\u5BB3\u9A87\u9163\u61A8"+ + "\u90AF\u97E9\u542B\u6DB5\u5BD2\u51FD\u558A\u7F55"+ + "\u7FF0\u64BC\u634D\u65F1\u61BE\u608D\u710A\u6C57"+ + "\u6C49\u592F\u676D\u822A\u58D5\u568E\u8C6A\u6BEB"+ + "\u90DD\u597D\u8017\u53F7\u6D69\u5475\u559D\u8377"+ + "\u83CF\u6838\u79BE\u548C\u4F55\u5408\u76D2\u8C89"+ + "\u9602\u6CB3\u6DB8\u8D6B\u8910\u9E64\u8D3A\u563F"+ + "\u9ED1\u75D5\u5F88\u72E0\u6068\u54FC\u4EA8\u6A2A"+ + "\u8861\u6052\u8F70\u54C4\u70D8\u8679\u9E3F\u6D2A"+ + "\u5B8F\u5F18\u7EA2\u5589\u4FAF\u7334\u543C\u539A"+ + "\u5019\u540E\u547C\u4E4E\u5FFD\u745A\u58F6\u846B"+ + "\u80E1\u8774\u72D0\u7CCA\u6E56\u7C43\u7C44\u7C45"+ + "\u7C46\u7C47\u7C48\u7C49\u7C4A\u7C4B\u7C4C\u7C4E"+ + "\u7C4F\u7C50\u7C51\u7C52\u7C53\u7C54\u7C55\u7C56"+ + "\u7C57\u7C58\u7C59\u7C5A\u7C5B\u7C5C\u7C5D\u7C5E"+ + "\u7C5F\u7C60\u7C61\u7C62\u7C63\u7C64\u7C65\u7C66"+ + "\u7C67\u7C68\u7C69\u7C6A\u7C6B\u7C6C\u7C6D\u7C6E"+ + "\u7C6F\u7C70\u7C71\u7C72\u7C75\u7C76\u7C77\u7C78"+ + "\u7C79\u7C7A\u7C7E\u7C7F\u7C80\u7C81\u7C82\u7C83"+ + "\u7C84\u7C85\u7C86\u7C87\uFFFD\u7C88\u7C8A\u7C8B"+ + "\u7C8C\u7C8D\u7C8E\u7C8F\u7C90\u7C93\u7C94\u7C96"+ + "\u7C99\u7C9A\u7C9B\u7CA0\u7CA1\u7CA3\u7CA6\u7CA7"+ + "\u7CA8\u7CA9\u7CAB\u7CAC\u7CAD\u7CAF\u7CB0\u7CB4"+ + "\u7CB5\u7CB6\u7CB7\u7CB8\u7CBA\u7CBB\u5F27\u864E"+ + "\u552C\u62A4\u4E92\u6CAA\u6237\u82B1\u54D7\u534E"+ + "\u733E\u6ED1\u753B\u5212\u5316\u8BDD\u69D0\u5F8A"+ + "\u6000\u6DEE\u574F\u6B22\u73AF\u6853\u8FD8\u7F13"+ + "\u6362\u60A3\u5524\u75EA\u8C62\u7115\u6DA3\u5BA6"+ + "\u5E7B\u8352\u614C\u9EC4\u78FA\u8757\u7C27\u7687"+ + "\u51F0\u60F6\u714C\u6643\u5E4C\u604D\u8C0E\u7070"+ + "\u6325\u8F89\u5FBD\u6062\u86D4\u56DE\u6BC1\u6094"+ + "\u6167\u5349\u60E0\u6666\u8D3F\u79FD\u4F1A\u70E9"+ + "\u6C47\u8BB3\u8BF2\u7ED8\u8364\u660F\u5A5A\u9B42"+ + "\u6D51\u6DF7\u8C41\u6D3B\u4F19\u706B\u83B7\u6216"+ + "\u60D1\u970D\u8D27\u7978\u51FB\u573E\u57FA\u673A"+ + "\u7578\u7A3D\u79EF\u7B95\u7CBF\u7CC0\u7CC2\u7CC3"+ + "\u7CC4\u7CC6\u7CC9\u7CCB\u7CCE\u7CCF\u7CD0\u7CD1"+ + "\u7CD2\u7CD3\u7CD4\u7CD8\u7CDA\u7CDB\u7CDD\u7CDE"+ + "\u7CE1\u7CE2\u7CE3\u7CE4\u7CE5\u7CE6\u7CE7\u7CE9"+ + "\u7CEA\u7CEB\u7CEC\u7CED\u7CEE\u7CF0\u7CF1\u7CF2"+ + "\u7CF3\u7CF4\u7CF5\u7CF6\u7CF7\u7CF9\u7CFA\u7CFC"+ + "\u7CFD\u7CFE\u7CFF\u7D00\u7D01\u7D02\u7D03\u7D04"+ + "\u7D05\u7D06\u7D07\u7D08\u7D09\u7D0B\u7D0C\u7D0D"+ + "\u7D0E\u7D0F\u7D10\uFFFD\u7D11\u7D12\u7D13\u7D14"+ + "\u7D15\u7D16\u7D17\u7D18\u7D19\u7D1A\u7D1B\u7D1C"+ + "\u7D1D\u7D1E\u7D1F\u7D21\u7D23\u7D24\u7D25\u7D26"+ + "\u7D28\u7D29\u7D2A\u7D2C\u7D2D\u7D2E\u7D30\u7D31"+ + "\u7D32\u7D33\u7D34\u7D35\u7D36\u808C\u9965\u8FF9"+ + "\u6FC0\u8BA5\u9E21\u59EC\u7EE9\u7F09\u5409\u6781"+ + "\u68D8\u8F91\u7C4D\u96C6\u53CA\u6025\u75BE\u6C72"+ + "\u5373\u5AC9\u7EA7\u6324\u51E0\u810A\u5DF1\u84DF"+ + "\u6280\u5180\u5B63\u4F0E\u796D\u5242\u60B8\u6D4E"+ + "\u5BC4\u5BC2\u8BA1\u8BB0\u65E2\u5FCC\u9645\u5993"+ + "\u7EE7\u7EAA\u5609\u67B7\u5939\u4F73\u5BB6\u52A0"+ + "\u835A\u988A\u8D3E\u7532\u94BE\u5047\u7A3C\u4EF7"+ + "\u67B6\u9A7E\u5AC1\u6B7C\u76D1\u575A\u5C16\u7B3A"+ + "\u95F4\u714E\u517C\u80A9\u8270\u5978\u7F04\u8327"+ + "\u68C0\u67EC\u78B1\u7877\u62E3\u6361\u7B80\u4FED"+ + "\u526A\u51CF\u8350\u69DB\u9274\u8DF5\u8D31\u89C1"+ + "\u952E\u7BAD\u4EF6\u7D37\u7D38\u7D39\u7D3A\u7D3B"+ + "\u7D3C\u7D3D\u7D3E\u7D3F\u7D40\u7D41\u7D42\u7D43"+ + "\u7D44\u7D45\u7D46\u7D47\u7D48\u7D49\u7D4A\u7D4B"+ + "\u7D4C\u7D4D\u7D4E\u7D4F\u7D50\u7D51\u7D52\u7D53"+ + "\u7D54\u7D55\u7D56\u7D57\u7D58\u7D59\u7D5A\u7D5B"+ + "\u7D5C\u7D5D\u7D5E\u7D5F\u7D60\u7D61\u7D62\u7D63"+ + "\u7D64\u7D65\u7D66\u7D67\u7D68\u7D69\u7D6A\u7D6B"+ + "\u7D6C\u7D6D\u7D6F\u7D70\u7D71\u7D72\u7D73\u7D74"+ + "\u7D75\u7D76\uFFFD\u7D78\u7D79\u7D7A\u7D7B\u7D7C"+ + "\u7D7D\u7D7E\u7D7F\u7D80\u7D81\u7D82\u7D83\u7D84"+ + "\u7D85\u7D86\u7D87\u7D88\u7D89\u7D8A\u7D8B\u7D8C"+ + "\u7D8D\u7D8E\u7D8F\u7D90\u7D91\u7D92\u7D93\u7D94"+ + "\u7D95\u7D96\u7D97\u7D98\u5065\u8230\u5251\u996F"+ + "\u6E10\u6E85\u6DA7\u5EFA\u50F5\u59DC\u5C06\u6D46"+ + "\u6C5F\u7586\u848B\u6868\u5956\u8BB2\u5320\u9171"+ + "\u964D\u8549\u6912\u7901\u7126\u80F6\u4EA4\u90CA"+ + "\u6D47\u9A84\u5A07\u56BC\u6405\u94F0\u77EB\u4FA5"+ + "\u811A\u72E1\u89D2\u997A\u7F34\u7EDE\u527F\u6559"+ + "\u9175\u8F7F\u8F83\u53EB\u7A96\u63ED\u63A5\u7686"+ + "\u79F8\u8857\u9636\u622A\u52AB\u8282\u6854\u6770"+ + "\u6377\u776B\u7AED\u6D01\u7ED3\u89E3\u59D0\u6212"+ + "\u85C9\u82A5\u754C\u501F\u4ECB\u75A5\u8BEB\u5C4A"+ + "\u5DFE\u7B4B\u65A4\u91D1\u4ECA\u6D25\u895F\u7D27"+ + "\u9526\u4EC5\u8C28\u8FDB\u9773\u664B\u7981\u8FD1"+ + "\u70EC\u6D78\u7D99\u7D9A\u7D9B\u7D9C\u7D9D\u7D9E"+ + "\u7D9F\u7DA0\u7DA1\u7DA2\u7DA3\u7DA4\u7DA5\u7DA7"+ + "\u7DA8\u7DA9\u7DAA\u7DAB\u7DAC\u7DAD\u7DAF\u7DB0"+ + "\u7DB1\u7DB2\u7DB3\u7DB4\u7DB5\u7DB6\u7DB7\u7DB8"+ + "\u7DB9\u7DBA\u7DBB\u7DBC\u7DBD\u7DBE\u7DBF\u7DC0"+ + "\u7DC1\u7DC2\u7DC3\u7DC4\u7DC5\u7DC6\u7DC7\u7DC8"+ + "\u7DC9\u7DCA\u7DCB\u7DCC\u7DCD\u7DCE\u7DCF\u7DD0"+ + "\u7DD1\u7DD2\u7DD3\u7DD4\u7DD5\u7DD6\u7DD7\u7DD8"+ + "\u7DD9\uFFFD\u7DDA\u7DDB\u7DDC\u7DDD\u7DDE\u7DDF"+ + "\u7DE0\u7DE1\u7DE2\u7DE3\u7DE4\u7DE5\u7DE6\u7DE7"+ + "\u7DE8\u7DE9\u7DEA\u7DEB\u7DEC\u7DED\u7DEE\u7DEF"+ + "\u7DF0\u7DF1\u7DF2\u7DF3\u7DF4\u7DF5\u7DF6\u7DF7"+ + "\u7DF8\u7DF9\u7DFA\u5C3D\u52B2\u8346\u5162\u830E"+ + "\u775B\u6676\u9CB8\u4EAC\u60CA\u7CBE\u7CB3\u7ECF"+ + "\u4E95\u8B66\u666F\u9888\u9759\u5883\u656C\u955C"+ + "\u5F84\u75C9\u9756\u7ADF\u7ADE\u51C0\u70AF\u7A98"+ + "\u63EA\u7A76\u7EA0\u7396\u97ED\u4E45\u7078\u4E5D"+ + "\u9152\u53A9\u6551\u65E7\u81FC\u8205\u548E\u5C31"+ + "\u759A\u97A0\u62D8\u72D9\u75BD\u5C45\u9A79\u83CA"+ + "\u5C40\u5480\u77E9\u4E3E\u6CAE\u805A\u62D2\u636E"+ + "\u5DE8\u5177\u8DDD\u8E1E\u952F\u4FF1\u53E5\u60E7"+ + "\u70AC\u5267\u6350\u9E43\u5A1F\u5026\u7737\u5377"+ + "\u7EE2\u6485\u652B\u6289\u6398\u5014\u7235\u89C9"+ + "\u51B3\u8BC0\u7EDD\u5747\u83CC\u94A7\u519B\u541B"+ + "\u5CFB\u7DFB\u7DFC\u7DFD\u7DFE\u7DFF\u7E00\u7E01"+ + "\u7E02\u7E03\u7E04\u7E05\u7E06\u7E07\u7E08\u7E09"+ + "\u7E0A\u7E0B\u7E0C\u7E0D\u7E0E\u7E0F\u7E10\u7E11"+ + "\u7E12\u7E13\u7E14\u7E15\u7E16\u7E17\u7E18\u7E19"+ + "\u7E1A\u7E1B\u7E1C\u7E1D\u7E1E\u7E1F\u7E20\u7E21"+ + "\u7E22\u7E23\u7E24\u7E25\u7E26\u7E27\u7E28\u7E29"+ + "\u7E2A\u7E2B\u7E2C\u7E2D\u7E2E\u7E2F\u7E30\u7E31"+ + "\u7E32\u7E33\u7E34\u7E35\u7E36\u7E37\u7E38\u7E39"+ + "\uFFFD\u7E3A\u7E3C\u7E3D\u7E3E\u7E3F\u7E40\u7E42"+ + "\u7E43\u7E44\u7E45\u7E46\u7E48\u7E49\u7E4A\u7E4B"+ + "\u7E4C\u7E4D\u7E4E\u7E4F\u7E50\u7E51\u7E52\u7E53"+ + "\u7E54\u7E55\u7E56\u7E57\u7E58\u7E59\u7E5A\u7E5B"+ + "\u7E5C\u7E5D\u4FCA\u7AE3\u6D5A\u90E1\u9A8F\u5580"+ + "\u5496\u5361\u54AF\u5F00\u63E9\u6977\u51EF\u6168"+ + "\u520A\u582A\u52D8\u574E\u780D\u770B\u5EB7\u6177"+ + "\u7CE0\u625B\u6297\u4EA2\u7095\u8003\u62F7\u70E4"+ + "\u9760\u5777\u82DB\u67EF\u68F5\u78D5\u9897\u79D1"+ + "\u58F3\u54B3\u53EF\u6E34\u514B\u523B\u5BA2\u8BFE"+ + "\u80AF\u5543\u57A6\u6073\u5751\u542D\u7A7A\u6050"+ + "\u5B54\u63A7\u62A0\u53E3\u6263\u5BC7\u67AF\u54ED"+ + "\u7A9F\u82E6\u9177\u5E93\u88E4\u5938\u57AE\u630E"+ + "\u8DE8\u80EF\u5757\u7B77\u4FA9\u5FEB\u5BBD\u6B3E"+ + "\u5321\u7B50\u72C2\u6846\u77FF\u7736\u65F7\u51B5"+ + "\u4E8F\u76D4\u5CBF\u7AA5\u8475\u594E\u9B41\u5080"; + + private final static String innerIndex4= + "\u7E5E\u7E5F\u7E60\u7E61\u7E62\u7E63\u7E64\u7E65"+ + "\u7E66\u7E67\u7E68\u7E69\u7E6A\u7E6B\u7E6C\u7E6D"+ + "\u7E6E\u7E6F\u7E70\u7E71\u7E72\u7E73\u7E74\u7E75"+ + "\u7E76\u7E77\u7E78\u7E79\u7E7A\u7E7B\u7E7C\u7E7D"+ + "\u7E7E\u7E7F\u7E80\u7E81\u7E83\u7E84\u7E85\u7E86"+ + "\u7E87\u7E88\u7E89\u7E8A\u7E8B\u7E8C\u7E8D\u7E8E"+ + "\u7E8F\u7E90\u7E91\u7E92\u7E93\u7E94\u7E95\u7E96"+ + "\u7E97\u7E98\u7E99\u7E9A\u7E9C\u7E9D\u7E9E\uFFFD"+ + "\u7EAE\u7EB4\u7EBB\u7EBC\u7ED6\u7EE4\u7EEC\u7EF9"+ + "\u7F0A\u7F10\u7F1E\u7F37\u7F39\u7F3B\u7F3C\u7F3D"+ + "\u7F3E\u7F3F\u7F40\u7F41\u7F43\u7F46\u7F47\u7F48"+ + "\u7F49\u7F4A\u7F4B\u7F4C\u7F4D\u7F4E\u7F4F\u7F52"+ + "\u7F53\u9988\u6127\u6E83\u5764\u6606\u6346\u56F0"+ + "\u62EC\u6269\u5ED3\u9614\u5783\u62C9\u5587\u8721"+ + "\u814A\u8FA3\u5566\u83B1\u6765\u8D56\u84DD\u5A6A"+ + "\u680F\u62E6\u7BEE\u9611\u5170\u6F9C\u8C30\u63FD"+ + "\u89C8\u61D2\u7F06\u70C2\u6EE5\u7405\u6994\u72FC"+ + "\u5ECA\u90CE\u6717\u6D6A\u635E\u52B3\u7262\u8001"+ + "\u4F6C\u59E5\u916A\u70D9\u6D9D\u52D2\u4E50\u96F7"+ + "\u956D\u857E\u78CA\u7D2F\u5121\u5792\u64C2\u808B"+ + "\u7C7B\u6CEA\u68F1\u695E\u51B7\u5398\u68A8\u7281"+ + "\u9ECE\u7BF1\u72F8\u79BB\u6F13\u7406\u674E\u91CC"+ + "\u9CA4\u793C\u8389\u8354\u540F\u6817\u4E3D\u5389"+ + "\u52B1\u783E\u5386\u5229\u5088\u4F8B\u4FD0\u7F56"+ + "\u7F59\u7F5B\u7F5C\u7F5D\u7F5E\u7F60\u7F63\u7F64"+ + "\u7F65\u7F66\u7F67\u7F6B\u7F6C\u7F6D\u7F6F\u7F70"+ + "\u7F73\u7F75\u7F76\u7F77\u7F78\u7F7A\u7F7B\u7F7C"+ + "\u7F7D\u7F7F\u7F80\u7F82\u7F83\u7F84\u7F85\u7F86"+ + "\u7F87\u7F88\u7F89\u7F8B\u7F8D\u7F8F\u7F90\u7F91"+ + "\u7F92\u7F93\u7F95\u7F96\u7F97\u7F98\u7F99\u7F9B"+ + "\u7F9C\u7FA0\u7FA2\u7FA3\u7FA5\u7FA6\u7FA8\u7FA9"+ + "\u7FAA\u7FAB\u7FAC\u7FAD\u7FAE\u7FB1\uFFFD\u7FB3"+ + "\u7FB4\u7FB5\u7FB6\u7FB7\u7FBA\u7FBB\u7FBE\u7FC0"+ + "\u7FC2\u7FC3\u7FC4\u7FC6\u7FC7\u7FC8\u7FC9\u7FCB"+ + "\u7FCD\u7FCF\u7FD0\u7FD1\u7FD2\u7FD3\u7FD6\u7FD7"+ + "\u7FD9\u7FDA\u7FDB\u7FDC\u7FDD\u7FDE\u7FE2\u7FE3"+ + "\u75E2\u7ACB\u7C92\u6CA5\u96B6\u529B\u7483\u54E9"+ + "\u4FE9\u8054\u83B2\u8FDE\u9570\u5EC9\u601C\u6D9F"+ + "\u5E18\u655B\u8138\u94FE\u604B\u70BC\u7EC3\u7CAE"+ + "\u51C9\u6881\u7CB1\u826F\u4E24\u8F86\u91CF\u667E"+ + "\u4EAE\u8C05\u64A9\u804A\u50DA\u7597\u71CE\u5BE5"+ + "\u8FBD\u6F66\u4E86\u6482\u9563\u5ED6\u6599\u5217"+ + "\u88C2\u70C8\u52A3\u730E\u7433\u6797\u78F7\u9716"+ + "\u4E34\u90BB\u9CDE\u6DCB\u51DB\u8D41\u541D\u62CE"+ + "\u73B2\u83F1\u96F6\u9F84\u94C3\u4F36\u7F9A\u51CC"+ + "\u7075\u9675\u5CAD\u9886\u53E6\u4EE4\u6E9C\u7409"+ + "\u69B4\u786B\u998F\u7559\u5218\u7624\u6D41\u67F3"+ + "\u516D\u9F99\u804B\u5499\u7B3C\u7ABF\u7FE4\u7FE7"+ + "\u7FE8\u7FEA\u7FEB\u7FEC\u7FED\u7FEF\u7FF2\u7FF4"+ + "\u7FF5\u7FF6\u7FF7\u7FF8\u7FF9\u7FFA\u7FFD\u7FFE"+ + "\u7FFF\u8002\u8007\u8008\u8009\u800A\u800E\u800F"+ + "\u8011\u8013\u801A\u801B\u801D\u801E\u801F\u8021"+ + "\u8023\u8024\u802B\u802C\u802D\u802E\u802F\u8030"+ + "\u8032\u8034\u8039\u803A\u803C\u803E\u8040\u8041"+ + "\u8044\u8045\u8047\u8048\u8049\u804E\u804F\u8050"+ + "\u8051\u8053\u8055\u8056\u8057\uFFFD\u8059\u805B"+ + "\u805C\u805D\u805E\u805F\u8060\u8061\u8062\u8063"+ + "\u8064\u8065\u8066\u8067\u8068\u806B\u806C\u806D"+ + "\u806E\u806F\u8070\u8072\u8073\u8074\u8075\u8076"+ + "\u8077\u8078\u8079\u807A\u807B\u807C\u807D\u9686"+ + "\u5784\u62E2\u9647\u697C\u5A04\u6402\u7BD3\u6F0F"+ + "\u964B\u82A6\u5362\u9885\u5E90\u7089\u63B3\u5364"+ + "\u864F\u9C81\u9E93\u788C\u9732\u8DEF\u8D42\u9E7F"+ + "\u6F5E\u7984\u5F55\u9646\u622E\u9A74\u5415\u94DD"+ + "\u4FA3\u65C5\u5C65\u5C61\u7F15\u8651\u6C2F\u5F8B"+ + "\u7387\u6EE4\u7EFF\u5CE6\u631B\u5B6A\u6EE6\u5375"+ + "\u4E71\u63A0\u7565\u62A1\u8F6E\u4F26\u4ED1\u6CA6"+ + "\u7EB6\u8BBA\u841D\u87BA\u7F57\u903B\u9523\u7BA9"+ + "\u9AA1\u88F8\u843D\u6D1B\u9A86\u7EDC\u5988\u9EBB"+ + "\u739B\u7801\u8682\u9A6C\u9A82\u561B\u5417\u57CB"+ + "\u4E70\u9EA6\u5356\u8FC8\u8109\u7792\u9992\u86EE"+ + "\u6EE1\u8513\u66FC\u6162\u6F2B\u807E\u8081\u8082"+ + "\u8085\u8088\u808A\u808D\u808E\u808F\u8090\u8091"+ + "\u8092\u8094\u8095\u8097\u8099\u809E\u80A3\u80A6"+ + "\u80A7\u80A8\u80AC\u80B0\u80B3\u80B5\u80B6\u80B8"+ + "\u80B9\u80BB\u80C5\u80C7\u80C8\u80C9\u80CA\u80CB"+ + "\u80CF\u80D0\u80D1\u80D2\u80D3\u80D4\u80D5\u80D8"+ + "\u80DF\u80E0\u80E2\u80E3\u80E6\u80EE\u80F5\u80F7"+ + "\u80F9\u80FB\u80FE\u80FF\u8100\u8101\u8103\u8104"+ + "\u8105\u8107\u8108\u810B\uFFFD\u810C\u8115\u8117"+ + "\u8119\u811B\u811C\u811D\u811F\u8120\u8121\u8122"+ + "\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812A"+ + "\u812B\u812D\u812E\u8130\u8133\u8134\u8135\u8137"+ + "\u8139\u813A\u813B\u813C\u813D\u813F\u8C29\u8292"+ + "\u832B\u76F2\u6C13\u5FD9\u83BD\u732B\u8305\u951A"+ + "\u6BDB\u77DB\u94C6\u536F\u8302\u5192\u5E3D\u8C8C"+ + "\u8D38\u4E48\u73AB\u679A\u6885\u9176\u9709\u7164"+ + "\u6CA1\u7709\u5A92\u9541\u6BCF\u7F8E\u6627\u5BD0"+ + "\u59B9\u5A9A\u95E8\u95F7\u4EEC\u840C\u8499\u6AAC"+ + "\u76DF\u9530\u731B\u68A6\u5B5F\u772F\u919A\u9761"+ + "\u7CDC\u8FF7\u8C1C\u5F25\u7C73\u79D8\u89C5\u6CCC"+ + "\u871C\u5BC6\u5E42\u68C9\u7720\u7EF5\u5195\u514D"+ + "\u52C9\u5A29\u7F05\u9762\u82D7\u63CF\u7784\u85D0"+ + "\u79D2\u6E3A\u5E99\u5999\u8511\u706D\u6C11\u62BF"+ + "\u76BF\u654F\u60AF\u95FD\u660E\u879F\u9E23\u94ED"+ + "\u540D\u547D\u8C2C\u6478\u8140\u8141\u8142\u8143"+ + "\u8144\u8145\u8147\u8149\u814D\u814E\u814F\u8152"+ + "\u8156\u8157\u8158\u815B\u815C\u815D\u815E\u815F"+ + "\u8161\u8162\u8163\u8164\u8166\u8168\u816A\u816B"+ + "\u816C\u816F\u8172\u8173\u8175\u8176\u8177\u8178"+ + "\u8181\u8183\u8184\u8185\u8186\u8187\u8189\u818B"+ + "\u818C\u818D\u818E\u8190\u8192\u8193\u8194\u8195"+ + "\u8196\u8197\u8199\u819A\u819E\u819F\u81A0\u81A1"+ + "\u81A2\u81A4\u81A5\uFFFD\u81A7\u81A9\u81AB\u81AC"+ + "\u81AD\u81AE\u81AF\u81B0\u81B1\u81B2\u81B4\u81B5"+ + "\u81B6\u81B7\u81B8\u81B9\u81BC\u81BD\u81BE\u81BF"+ + "\u81C4\u81C5\u81C7\u81C8\u81C9\u81CB\u81CD\u81CE"+ + "\u81CF\u81D0\u81D1\u81D2\u81D3\u6479\u8611\u6A21"+ + "\u819C\u78E8\u6469\u9B54\u62B9\u672B\u83AB\u58A8"+ + "\u9ED8\u6CAB\u6F20\u5BDE\u964C\u8C0B\u725F\u67D0"+ + "\u62C7\u7261\u4EA9\u59C6\u6BCD\u5893\u66AE\u5E55"+ + "\u52DF\u6155\u6728\u76EE\u7766\u7267\u7A46\u62FF"+ + "\u54EA\u5450\u94A0\u90A3\u5A1C\u7EB3\u6C16\u4E43"+ + "\u5976\u8010\u5948\u5357\u7537\u96BE\u56CA\u6320"+ + "\u8111\u607C\u95F9\u6DD6\u5462\u9981\u5185\u5AE9"+ + "\u80FD\u59AE\u9713\u502A\u6CE5\u5C3C\u62DF\u4F60"+ + "\u533F\u817B\u9006\u6EBA\u852B\u62C8\u5E74\u78BE"+ + "\u64B5\u637B\u5FF5\u5A18\u917F\u9E1F\u5C3F\u634F"+ + "\u8042\u5B7D\u556E\u954A\u954D\u6D85\u60A8\u67E0"+ + "\u72DE\u51DD\u5B81\u81D4\u81D5\u81D6\u81D7\u81D8"+ + "\u81D9\u81DA\u81DB\u81DC\u81DD\u81DE\u81DF\u81E0"+ + "\u81E1\u81E2\u81E4\u81E5\u81E6\u81E8\u81E9\u81EB"+ + "\u81EE\u81EF\u81F0\u81F1\u81F2\u81F5\u81F6\u81F7"+ + "\u81F8\u81F9\u81FA\u81FD\u81FF\u8203\u8207\u8208"+ + "\u8209\u820A\u820B\u820E\u820F\u8211\u8213\u8215"+ + "\u8216\u8217\u8218\u8219\u821A\u821D\u8220\u8224"+ + "\u8225\u8226\u8227\u8229\u822E\u8232\u823A\u823C"+ + "\u823D\u823F\uFFFD\u8240\u8241\u8242\u8243\u8245"+ + "\u8246\u8248\u824A\u824C\u824D\u824E\u8250\u8251"+ + "\u8252\u8253\u8254\u8255\u8256\u8257\u8259\u825B"+ + "\u825C\u825D\u825E\u8260\u8261\u8262\u8263\u8264"+ + "\u8265\u8266\u8267\u8269\u62E7\u6CDE\u725B\u626D"+ + "\u94AE\u7EBD\u8113\u6D53\u519C\u5F04\u5974\u52AA"+ + "\u6012\u5973\u6696\u8650\u759F\u632A\u61E6\u7CEF"+ + "\u8BFA\u54E6\u6B27\u9E25\u6BB4\u85D5\u5455\u5076"+ + "\u6CA4\u556A\u8DB4\u722C\u5E15\u6015\u7436\u62CD"+ + "\u6392\u724C\u5F98\u6E43\u6D3E\u6500\u6F58\u76D8"+ + "\u78D0\u76FC\u7554\u5224\u53DB\u4E53\u5E9E\u65C1"+ + "\u802A\u80D6\u629B\u5486\u5228\u70AE\u888D\u8DD1"+ + "\u6CE1\u5478\u80DA\u57F9\u88F4\u8D54\u966A\u914D"+ + "\u4F69\u6C9B\u55B7\u76C6\u7830\u62A8\u70F9\u6F8E"+ + "\u5F6D\u84EC\u68DA\u787C\u7BF7\u81A8\u670B\u9E4F"+ + "\u6367\u78B0\u576F\u7812\u9739\u6279\u62AB\u5288"+ + "\u7435\u6BD7\u826A\u826B\u826C\u826D\u8271\u8275"+ + "\u8276\u8277\u8278\u827B\u827C\u8280\u8281\u8283"+ + "\u8285\u8286\u8287\u8289\u828C\u8290\u8293\u8294"+ + "\u8295\u8296\u829A\u829B\u829E\u82A0\u82A2\u82A3"+ + "\u82A7\u82B2\u82B5\u82B6\u82BA\u82BB\u82BC\u82BF"+ + "\u82C0\u82C2\u82C3\u82C5\u82C6\u82C9\u82D0\u82D6"+ + "\u82D9\u82DA\u82DD\u82E2\u82E7\u82E8\u82E9\u82EA"+ + "\u82EC\u82ED\u82EE\u82F0\u82F2\u82F3\u82F5\u82F6"+ + "\u82F8\uFFFD\u82FA\u82FC\u82FD\u82FE\u82FF\u8300"+ + "\u830A\u830B\u830D\u8310\u8312\u8313\u8316\u8318"+ + "\u8319\u831D\u831E\u831F\u8320\u8321\u8322\u8323"+ + "\u8324\u8325\u8326\u8329\u832A\u832E\u8330\u8332"+ + "\u8337\u833B\u833D\u5564\u813E\u75B2\u76AE\u5339"+ + "\u75DE\u50FB\u5C41\u8B6C\u7BC7\u504F\u7247\u9A97"+ + "\u98D8\u6F02\u74E2\u7968\u6487\u77A5\u62FC\u9891"+ + "\u8D2B\u54C1\u8058\u4E52\u576A\u82F9\u840D\u5E73"+ + "\u51ED\u74F6\u8BC4\u5C4F\u5761\u6CFC\u9887\u5A46"+ + "\u7834\u9B44\u8FEB\u7C95\u5256\u6251\u94FA\u4EC6"+ + "\u8386\u8461\u83E9\u84B2\u57D4\u6734\u5703\u666E"+ + "\u6D66\u8C31\u66DD\u7011\u671F\u6B3A\u6816\u621A"+ + "\u59BB\u4E03\u51C4\u6F06\u67D2\u6C8F\u5176\u68CB"+ + "\u5947\u6B67\u7566\u5D0E\u8110\u9F50\u65D7\u7948"+ + "\u7941\u9A91\u8D77\u5C82\u4E5E\u4F01\u542F\u5951"+ + "\u780C\u5668\u6C14\u8FC4\u5F03\u6C7D\u6CE3\u8BAB"+ + "\u6390\u833E\u833F\u8341\u8342\u8344\u8345\u8348"+ + "\u834A\u834B\u834C\u834D\u834E\u8353\u8355\u8356"+ + "\u8357\u8358\u8359\u835D\u8362\u8370\u8371\u8372"+ + "\u8373\u8374\u8375\u8376\u8379\u837A\u837E\u837F"+ + "\u8380\u8381\u8382\u8383\u8384\u8387\u8388\u838A"+ + "\u838B\u838C\u838D\u838F\u8390\u8391\u8394\u8395"+ + "\u8396\u8397\u8399\u839A\u839D\u839F\u83A1\u83A2"+ + "\u83A3\u83A4\u83A5\u83A6\u83A7\u83AC\u83AD\u83AE"+ + "\uFFFD\u83AF\u83B5\u83BB\u83BE\u83BF\u83C2\u83C3"+ + "\u83C4\u83C6\u83C8\u83C9\u83CB\u83CD\u83CE\u83D0"+ + "\u83D1\u83D2\u83D3\u83D5\u83D7\u83D9\u83DA\u83DB"+ + "\u83DE\u83E2\u83E3\u83E4\u83E6\u83E7\u83E8\u83EB"+ + "\u83EC\u83ED\u6070\u6D3D\u7275\u6266\u948E\u94C5"+ + "\u5343\u8FC1\u7B7E\u4EDF\u8C26\u4E7E\u9ED4\u94B1"+ + "\u94B3\u524D\u6F5C\u9063\u6D45\u8C34\u5811\u5D4C"+ + "\u6B20\u6B49\u67AA\u545B\u8154\u7F8C\u5899\u8537"+ + "\u5F3A\u62A2\u6A47\u9539\u6572\u6084\u6865\u77A7"+ + "\u4E54\u4FA8\u5DE7\u9798\u64AC\u7FD8\u5CED\u4FCF"+ + "\u7A8D\u5207\u8304\u4E14\u602F\u7A83\u94A6\u4FB5"+ + "\u4EB2\u79E6\u7434\u52E4\u82B9\u64D2\u79BD\u5BDD"+ + "\u6C81\u9752\u8F7B\u6C22\u503E\u537F\u6E05\u64CE"+ + "\u6674\u6C30\u60C5\u9877\u8BF7\u5E86\u743C\u7A77"+ + "\u79CB\u4E18\u90B1\u7403\u6C42\u56DA\u914B\u6CC5"+ + "\u8D8B\u533A\u86C6\u66F2\u8EAF\u5C48\u9A71\u6E20"+ + "\u83EE\u83EF\u83F3\u83F4\u83F5\u83F6\u83F7\u83FA"+ + "\u83FB\u83FC\u83FE\u83FF\u8400\u8402\u8405\u8407"+ + "\u8408\u8409\u840A\u8410\u8412\u8413\u8414\u8415"+ + "\u8416\u8417\u8419\u841A\u841B\u841E\u841F\u8420"+ + "\u8421\u8422\u8423\u8429\u842A\u842B\u842C\u842D"+ + "\u842E\u842F\u8430\u8432\u8433\u8434\u8435\u8436"+ + "\u8437\u8439\u843A\u843B\u843E\u843F\u8440\u8441"+ + "\u8442\u8443\u8444\u8445\u8447\u8448\u8449\uFFFD"+ + "\u844A\u844B\u844C\u844D\u844E\u844F\u8450\u8452"+ + "\u8453\u8454\u8455\u8456\u8458\u845D\u845E\u845F"+ + "\u8460\u8462\u8464\u8465\u8466\u8467\u8468\u846A"+ + "\u846E\u846F\u8470\u8472\u8474\u8477\u8479\u847B"+ + "\u847C\u53D6\u5A36\u9F8B\u8DA3\u53BB\u5708\u98A7"+ + "\u6743\u919B\u6CC9\u5168\u75CA\u62F3\u72AC\u5238"+ + "\u529D\u7F3A\u7094\u7638\u5374\u9E4A\u69B7\u786E"+ + "\u96C0\u88D9\u7FA4\u7136\u71C3\u5189\u67D3\u74E4"+ + "\u58E4\u6518\u56B7\u8BA9\u9976\u6270\u7ED5\u60F9"+ + "\u70ED\u58EC\u4EC1\u4EBA\u5FCD\u97E7\u4EFB\u8BA4"+ + "\u5203\u598A\u7EAB\u6254\u4ECD\u65E5\u620E\u8338"+ + "\u84C9\u8363\u878D\u7194\u6EB6\u5BB9\u7ED2\u5197"+ + "\u63C9\u67D4\u8089\u8339\u8815\u5112\u5B7A\u5982"+ + "\u8FB1\u4E73\u6C5D\u5165\u8925\u8F6F\u962E\u854A"+ + "\u745E\u9510\u95F0\u6DA6\u82E5\u5F31\u6492\u6D12"+ + "\u8428\u816E\u9CC3\u585E\u8D5B\u4E09\u53C1\u847D"+ + "\u847E\u847F\u8480\u8481\u8483\u8484\u8485\u8486"+ + "\u848A\u848D\u848F\u8490\u8491\u8492\u8493\u8494"+ + "\u8495\u8496\u8498\u849A\u849B\u849D\u849E\u849F"+ + "\u84A0\u84A2\u84A3\u84A4\u84A5\u84A6\u84A7\u84A8"+ + "\u84A9\u84AA\u84AB\u84AC\u84AD\u84AE\u84B0\u84B1"+ + "\u84B3\u84B5\u84B6\u84B7\u84BB\u84BC\u84BE\u84C0"+ + "\u84C2\u84C3\u84C5\u84C6\u84C7\u84C8\u84CB\u84CC"+ + "\u84CE\u84CF\u84D2\u84D4\u84D5\u84D7\uFFFD\u84D8"+ + "\u84D9\u84DA\u84DB\u84DC\u84DE\u84E1\u84E2\u84E4"+ + "\u84E7\u84E8\u84E9\u84EA\u84EB\u84ED\u84EE\u84EF"+ + "\u84F1\u84F2\u84F3\u84F4\u84F5\u84F6\u84F7\u84F8"+ + "\u84F9\u84FA\u84FB\u84FD\u84FE\u8500\u8501\u8502"+ + "\u4F1E\u6563\u6851\u55D3\u4E27\u6414\u9A9A\u626B"+ + "\u5AC2\u745F\u8272\u6DA9\u68EE\u50E7\u838E\u7802"+ + "\u6740\u5239\u6C99\u7EB1\u50BB\u5565\u715E\u7B5B"+ + "\u6652\u73CA\u82EB\u6749\u5C71\u5220\u717D\u886B"+ + "\u95EA\u9655\u64C5\u8D61\u81B3\u5584\u6C55\u6247"+ + "\u7F2E\u5892\u4F24\u5546\u8D4F\u664C\u4E0A\u5C1A"+ + "\u88F3\u68A2\u634E\u7A0D\u70E7\u828D\u52FA\u97F6"+ + "\u5C11\u54E8\u90B5\u7ECD\u5962\u8D4A\u86C7\u820C"+ + "\u820D\u8D66\u6444\u5C04\u6151\u6D89\u793E\u8BBE"+ + "\u7837\u7533\u547B\u4F38\u8EAB\u6DF1\u5A20\u7EC5"+ + "\u795E\u6C88\u5BA1\u5A76\u751A\u80BE\u614E\u6E17"+ + "\u58F0\u751F\u7525\u7272\u5347\u7EF3\u8503\u8504"+ + "\u8505\u8506\u8507\u8508\u8509\u850A\u850B\u850D"+ + "\u850E\u850F\u8510\u8512\u8514\u8515\u8516\u8518"+ + "\u8519\u851B\u851C\u851D\u851E\u8520\u8522\u8523"+ + "\u8524\u8525\u8526\u8527\u8528\u8529\u852A\u852D"+ + "\u852E\u852F\u8530\u8531\u8532\u8533\u8534\u8535"+ + "\u8536\u853E\u853F\u8540\u8541\u8542\u8544\u8545"+ + "\u8546\u8547\u854B\u854C\u854D\u854E\u854F\u8550"+ + "\u8551\u8552\u8553\u8554\u8555\uFFFD\u8557\u8558"+ + "\u855A\u855B\u855C\u855D\u855F\u8560\u8561\u8562"+ + "\u8563\u8565\u8566\u8567\u8569\u856A\u856B\u856C"+ + "\u856D\u856E\u856F\u8570\u8571\u8573\u8575\u8576"+ + "\u8577\u8578\u857C\u857D\u857F\u8580\u8581\u7701"+ + "\u76DB\u5269\u80DC\u5723\u5E08\u5931\u72EE\u65BD"+ + "\u6E7F\u8BD7\u5C38\u8671\u5341\u77F3\u62FE\u65F6"+ + "\u4EC0\u98DF\u8680\u5B9E\u8BC6\u53F2\u77E2\u4F7F"+ + "\u5C4E\u9A76\u59CB\u5F0F\u793A\u58EB\u4E16\u67FF"+ + "\u4E8B\u62ED\u8A93\u901D\u52BF\u662F\u55DC\u566C"+ + "\u9002\u4ED5\u4F8D\u91CA\u9970\u6C0F\u5E02\u6043"+ + "\u5BA4\u89C6\u8BD5\u6536\u624B\u9996\u5B88\u5BFF"+ + "\u6388\u552E\u53D7\u7626\u517D\u852C\u67A2\u68B3"+ + "\u6B8A\u6292\u8F93\u53D4\u8212\u6DD1\u758F\u4E66"+ + "\u8D4E\u5B70\u719F\u85AF\u6691\u66D9\u7F72\u8700"+ + "\u9ECD\u9F20\u5C5E\u672F\u8FF0\u6811\u675F\u620D"+ + "\u7AD6\u5885\u5EB6\u6570\u6F31\u8582\u8583\u8586"+ + "\u8588\u8589\u858A\u858B\u858C\u858D\u858E\u8590"+ + "\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598"+ + "\u8599\u859A\u859D\u859E\u859F\u85A0\u85A1\u85A2"+ + "\u85A3\u85A5\u85A6\u85A7\u85A9\u85AB\u85AC\u85AD"+ + "\u85B1\u85B2\u85B3\u85B4\u85B5\u85B6\u85B8\u85BA"+ + "\u85BB\u85BC\u85BD\u85BE\u85BF\u85C0\u85C2\u85C3"+ + "\u85C4\u85C5\u85C6\u85C7\u85C8\u85CA\u85CB\u85CC"+ + "\u85CD\u85CE\u85D1\u85D2\uFFFD\u85D4\u85D6\u85D7"+ + "\u85D8\u85D9\u85DA\u85DB\u85DD\u85DE\u85DF\u85E0"+ + "\u85E1\u85E2\u85E3\u85E5\u85E6\u85E7\u85E8\u85EA"+ + "\u85EB\u85EC\u85ED\u85EE\u85EF\u85F0\u85F1\u85F2"+ + "\u85F3\u85F4\u85F5\u85F6\u85F7\u85F8\u6055\u5237"+ + "\u800D\u6454\u8870\u7529\u5E05\u6813\u62F4\u971C"+ + "\u53CC\u723D\u8C01\u6C34\u7761\u7A0E\u542E\u77AC"+ + "\u987A\u821C\u8BF4\u7855\u6714\u70C1\u65AF\u6495"+ + "\u5636\u601D\u79C1\u53F8\u4E1D\u6B7B\u8086\u5BFA"+ + "\u55E3\u56DB\u4F3A\u4F3C\u9972\u5DF3\u677E\u8038"+ + "\u6002\u9882\u9001\u5B8B\u8BBC\u8BF5\u641C\u8258"+ + "\u64DE\u55FD\u82CF\u9165\u4FD7\u7D20\u901F\u7C9F"+ + "\u50F3\u5851\u6EAF\u5BBF\u8BC9\u8083\u9178\u849C"+ + "\u7B97\u867D\u968B\u968F\u7EE5\u9AD3\u788E\u5C81"+ + "\u7A57\u9042\u96A7\u795F\u5B59\u635F\u7B0B\u84D1"+ + "\u68AD\u5506\u7F29\u7410\u7D22\u9501\u6240\u584C"+ + "\u4ED6\u5B83\u5979\u5854\u85F9\u85FA\u85FC\u85FD"+ + "\u85FE\u8600\u8601\u8602\u8603\u8604\u8606\u8607"+ + "\u8608\u8609\u860A\u860B\u860C\u860D\u860E\u860F"+ + "\u8610\u8612\u8613\u8614\u8615\u8617\u8618\u8619"+ + "\u861A\u861B\u861C\u861D\u861E\u861F\u8620\u8621"+ + "\u8622\u8623\u8624\u8625\u8626\u8628\u862A\u862B"+ + "\u862C\u862D\u862E\u862F\u8630\u8631\u8632\u8633"+ + "\u8634\u8635\u8636\u8637\u8639\u863A\u863B\u863D"+ + "\u863E\u863F\u8640\uFFFD\u8641\u8642\u8643\u8644"+ + "\u8645\u8646\u8647\u8648\u8649\u864A\u864B\u864C"+ + "\u8652\u8653\u8655\u8656\u8657\u8658\u8659\u865B"+ + "\u865C\u865D\u865F\u8660\u8661\u8663\u8664\u8665"+ + "\u8666\u8667\u8668\u8669\u866A\u736D\u631E\u8E4B"+ + "\u8E0F\u80CE\u82D4\u62AC\u53F0\u6CF0\u915E\u592A"+ + "\u6001\u6C70\u574D\u644A\u8D2A\u762B\u6EE9\u575B"+ + "\u6A80\u75F0\u6F6D\u8C2D\u8C08\u5766\u6BEF\u8892"+ + "\u78B3\u63A2\u53F9\u70AD\u6C64\u5858\u642A\u5802"+ + "\u68E0\u819B\u5510\u7CD6\u5018\u8EBA\u6DCC\u8D9F"+ + "\u70EB\u638F\u6D9B\u6ED4\u7EE6\u8404\u6843\u9003"+ + "\u6DD8\u9676\u8BA8\u5957\u7279\u85E4\u817E\u75BC"+ + "\u8A8A\u68AF\u5254\u8E22\u9511\u63D0\u9898\u8E44"+ + "\u557C\u4F53\u66FF\u568F\u60D5\u6D95\u5243\u5C49"+ + "\u5929\u6DFB\u586B\u7530\u751C\u606C\u8214\u8146"+ + "\u6311\u6761\u8FE2\u773A\u8DF3\u8D34\u94C1\u5E16"+ + "\u5385\u542C\u70C3\u866D\u866F\u8670\u8672\u8673"+ + "\u8674\u8675\u8676\u8677\u8678\u8683\u8684\u8685"+ + "\u8686\u8687\u8688\u8689\u868E\u868F\u8690\u8691"+ + "\u8692\u8694\u8696\u8697\u8698\u8699\u869A\u869B"+ + "\u869E\u869F\u86A0\u86A1\u86A2\u86A5\u86A6\u86AB"+ + "\u86AD\u86AE\u86B2\u86B3\u86B7\u86B8\u86B9\u86BB"+ + "\u86BC\u86BD\u86BE\u86BF\u86C1\u86C2\u86C3\u86C5"+ + "\u86C8\u86CC\u86CD\u86D2\u86D3\u86D5\u86D6\u86D7"+ + "\u86DA\u86DC\uFFFD\u86DD\u86E0\u86E1\u86E2\u86E3"+ + "\u86E5\u86E6\u86E7\u86E8\u86EA\u86EB\u86EC\u86EF"+ + "\u86F5\u86F6\u86F7\u86FA\u86FB\u86FC\u86FD\u86FF"+ + "\u8701\u8704\u8705\u8706\u870B\u870C\u870E\u870F"+ + "\u8710\u8711\u8714\u8716\u6C40\u5EF7\u505C\u4EAD"+ + "\u5EAD\u633A\u8247\u901A\u6850\u916E\u77B3\u540C"+ + "\u94DC\u5F64\u7AE5\u6876\u6345\u7B52\u7EDF\u75DB"+ + "\u5077\u6295\u5934\u900F\u51F8\u79C3\u7A81\u56FE"+ + "\u5F92\u9014\u6D82\u5C60\u571F\u5410\u5154\u6E4D"+ + "\u56E2\u63A8\u9893\u817F\u8715\u892A\u9000\u541E"+ + "\u5C6F\u81C0\u62D6\u6258\u8131\u9E35\u9640\u9A6E"+ + "\u9A7C\u692D\u59A5\u62D3\u553E\u6316\u54C7\u86D9"+ + "\u6D3C\u5A03\u74E6\u889C\u6B6A\u5916\u8C4C\u5F2F"+ + "\u6E7E\u73A9\u987D\u4E38\u70F7\u5B8C\u7897\u633D"+ + "\u665A\u7696\u60CB\u5B9B\u5A49\u4E07\u8155\u6C6A"+ + "\u738B\u4EA1\u6789\u7F51\u5F80\u65FA\u671B\u5FD8"+ + "\u5984\u5A01\u8719\u871B\u871D\u871F\u8720\u8724"+ + "\u8726\u8727\u8728\u872A\u872B\u872C\u872D\u872F"+ + "\u8730\u8732\u8733\u8735\u8736\u8738\u8739\u873A"+ + "\u873C\u873D\u8740\u8741\u8742\u8743\u8744\u8745"+ + "\u8746\u874A\u874B\u874D\u874F\u8750\u8751\u8752"+ + "\u8754\u8755\u8756\u8758\u875A\u875B\u875C\u875D"+ + "\u875E\u875F\u8761\u8762\u8766\u8767\u8768\u8769"+ + "\u876A\u876B\u876C\u876D\u876F\u8771\u8772\u8773"+ + "\u8775\uFFFD\u8777\u8778\u8779\u877A\u877F\u8780"+ + "\u8781\u8784\u8786\u8787\u8789\u878A\u878C\u878E"+ + "\u878F\u8790\u8791\u8792\u8794\u8795\u8796\u8798"+ + "\u8799\u879A\u879B\u879C\u879D\u879E\u87A0\u87A1"+ + "\u87A2\u87A3\u87A4\u5DCD\u5FAE\u5371\u97E6\u8FDD"+ + "\u6845\u56F4\u552F\u60DF\u4E3A\u6F4D\u7EF4\u82C7"+ + "\u840E\u59D4\u4F1F\u4F2A\u5C3E\u7EAC\u672A\u851A"+ + "\u5473\u754F\u80C3\u5582\u9B4F\u4F4D\u6E2D\u8C13"+ + "\u5C09\u6170\u536B\u761F\u6E29\u868A\u6587\u95FB"+ + "\u7EB9\u543B\u7A33\u7D0A\u95EE\u55E1\u7FC1\u74EE"+ + "\u631D\u8717\u6DA1\u7A9D\u6211\u65A1\u5367\u63E1"+ + "\u6C83\u5DEB\u545C\u94A8\u4E4C\u6C61\u8BEC\u5C4B"+ + "\u65E0\u829C\u68A7\u543E\u5434\u6BCB\u6B66\u4E94"+ + "\u6342\u5348\u821E\u4F0D\u4FAE\u575E\u620A\u96FE"+ + "\u6664\u7269\u52FF\u52A1\u609F\u8BEF\u6614\u7199"+ + "\u6790\u897F\u7852\u77FD\u6670\u563B\u5438\u9521"+ + "\u727A\u87A5\u87A6\u87A7\u87A9\u87AA\u87AE\u87B0"+ + "\u87B1\u87B2\u87B4\u87B6\u87B7\u87B8\u87B9\u87BB"+ + "\u87BC\u87BE\u87BF\u87C1\u87C2\u87C3\u87C4\u87C5"+ + "\u87C7\u87C8\u87C9\u87CC\u87CD\u87CE\u87CF\u87D0"+ + "\u87D4\u87D5\u87D6\u87D7\u87D8\u87D9\u87DA\u87DC"+ + "\u87DD\u87DE\u87DF\u87E1\u87E2\u87E3\u87E4\u87E6"+ + "\u87E7\u87E8\u87E9\u87EB\u87EC\u87ED\u87EF\u87F0"+ + "\u87F1\u87F2\u87F3\u87F4\u87F5\u87F6\u87F7\u87F8"+ + "\uFFFD\u87FA\u87FB\u87FC\u87FD\u87FF\u8800\u8801"+ + "\u8802\u8804\u8805\u8806\u8807\u8808\u8809\u880B"+ + "\u880C\u880D\u880E\u880F\u8810\u8811\u8812\u8814"+ + "\u8817\u8818\u8819\u881A\u881C\u881D\u881E\u881F"+ + "\u8820\u8823\u7A00\u606F\u5E0C\u6089\u819D\u5915"+ + "\u60DC\u7184\u70EF\u6EAA\u6C50\u7280\u6A84\u88AD"+ + "\u5E2D\u4E60\u5AB3\u559C\u94E3\u6D17\u7CFB\u9699"+ + "\u620F\u7EC6\u778E\u867E\u5323\u971E\u8F96\u6687"+ + "\u5CE1\u4FA0\u72ED\u4E0B\u53A6\u590F\u5413\u6380"+ + "\u9528\u5148\u4ED9\u9C9C\u7EA4\u54B8\u8D24\u8854"+ + "\u8237\u95F2\u6D8E\u5F26\u5ACC\u663E\u9669\u73B0"+ + "\u732E\u53BF\u817A\u9985\u7FA1\u5BAA\u9677\u9650"+ + "\u7EBF\u76F8\u53A2\u9576\u9999\u7BB1\u8944\u6E58"+ + "\u4E61\u7FD4\u7965\u8BE6\u60F3\u54CD\u4EAB\u9879"+ + "\u5DF7\u6A61\u50CF\u5411\u8C61\u8427\u785D\u9704"+ + "\u524A\u54EE\u56A3\u9500\u6D88\u5BB5\u6DC6\u6653"; + + private final static String innerIndex5= + "\u8824\u8825\u8826\u8827\u8828\u8829\u882A\u882B"+ + "\u882C\u882D\u882E\u882F\u8830\u8831\u8833\u8834"+ + "\u8835\u8836\u8837\u8838\u883A\u883B\u883D\u883E"+ + "\u883F\u8841\u8842\u8843\u8846\u8847\u8848\u8849"+ + "\u884A\u884B\u884E\u884F\u8850\u8851\u8852\u8853"+ + "\u8855\u8856\u8858\u885A\u885B\u885C\u885D\u885E"+ + "\u885F\u8860\u8866\u8867\u886A\u886D\u886F\u8871"+ + "\u8873\u8874\u8875\u8876\u8878\u8879\u887A\uFFFD"+ + "\u887B\u887C\u8880\u8883\u8886\u8887\u8889\u888A"+ + "\u888C\u888E\u888F\u8890\u8891\u8893\u8894\u8895"+ + "\u8897\u8898\u8899\u889A\u889B\u889D\u889E\u889F"+ + "\u88A0\u88A1\u88A3\u88A5\u88A6\u88A7\u88A8\u88A9"+ + "\u88AA\u5C0F\u5B5D\u6821\u8096\u5578\u7B11\u6548"+ + "\u6954\u4E9B\u6B47\u874E\u978B\u534F\u631F\u643A"+ + "\u90AA\u659C\u80C1\u8C10\u5199\u68B0\u5378\u87F9"+ + "\u61C8\u6CC4\u6CFB\u8C22\u5C51\u85AA\u82AF\u950C"+ + "\u6B23\u8F9B\u65B0\u5FFB\u5FC3\u4FE1\u8845\u661F"+ + "\u8165\u7329\u60FA\u5174\u5211\u578B\u5F62\u90A2"+ + "\u884C\u9192\u5E78\u674F\u6027\u59D3\u5144\u51F6"+ + "\u80F8\u5308\u6C79\u96C4\u718A\u4F11\u4FEE\u7F9E"+ + "\u673D\u55C5\u9508\u79C0\u8896\u7EE3\u589F\u620C"+ + "\u9700\u865A\u5618\u987B\u5F90\u8BB8\u84C4\u9157"+ + "\u53D9\u65ED\u5E8F\u755C\u6064\u7D6E\u5A7F\u7EEA"+ + "\u7EED\u8F69\u55A7\u5BA3\u60AC\u65CB\u7384\u88AC"+ + "\u88AE\u88AF\u88B0\u88B2\u88B3\u88B4\u88B5\u88B6"+ + "\u88B8\u88B9\u88BA\u88BB\u88BD\u88BE\u88BF\u88C0"+ + "\u88C3\u88C4\u88C7\u88C8\u88CA\u88CB\u88CC\u88CD"+ + "\u88CF\u88D0\u88D1\u88D3\u88D6\u88D7\u88DA\u88DB"+ + "\u88DC\u88DD\u88DE\u88E0\u88E1\u88E6\u88E7\u88E9"+ + "\u88EA\u88EB\u88EC\u88ED\u88EE\u88EF\u88F2\u88F5"+ + "\u88F6\u88F7\u88FA\u88FB\u88FD\u88FF\u8900\u8901"+ + "\u8903\u8904\u8905\u8906\u8907\u8908\uFFFD\u8909"+ + "\u890B\u890C\u890D\u890E\u890F\u8911\u8914\u8915"+ + "\u8916\u8917\u8918\u891C\u891D\u891E\u891F\u8920"+ + "\u8922\u8923\u8924\u8926\u8927\u8928\u8929\u892C"+ + "\u892D\u892E\u892F\u8931\u8932\u8933\u8935\u8937"+ + "\u9009\u7663\u7729\u7EDA\u9774\u859B\u5B66\u7A74"+ + "\u96EA\u8840\u52CB\u718F\u5FAA\u65EC\u8BE2\u5BFB"+ + "\u9A6F\u5DE1\u6B89\u6C5B\u8BAD\u8BAF\u900A\u8FC5"+ + "\u538B\u62BC\u9E26\u9E2D\u5440\u4E2B\u82BD\u7259"+ + "\u869C\u5D16\u8859\u6DAF\u96C5\u54D1\u4E9A\u8BB6"+ + "\u7109\u54BD\u9609\u70DF\u6DF9\u76D0\u4E25\u7814"+ + "\u8712\u5CA9\u5EF6\u8A00\u989C\u960E\u708E\u6CBF"+ + "\u5944\u63A9\u773C\u884D\u6F14\u8273\u5830\u71D5"+ + "\u538C\u781A\u96C1\u5501\u5F66\u7130\u5BB4\u8C1A"+ + "\u9A8C\u6B83\u592E\u9E2F\u79E7\u6768\u626C\u4F6F"+ + "\u75A1\u7F8A\u6D0B\u9633\u6C27\u4EF0\u75D2\u517B"+ + "\u6837\u6F3E\u9080\u8170\u5996\u7476\u8938\u8939"+ + "\u893A\u893B\u893C\u893D\u893E\u893F\u8940\u8942"+ + "\u8943\u8945\u8946\u8947\u8948\u8949\u894A\u894B"+ + "\u894C\u894D\u894E\u894F\u8950\u8951\u8952\u8953"+ + "\u8954\u8955\u8956\u8957\u8958\u8959\u895A\u895B"+ + "\u895C\u895D\u8960\u8961\u8962\u8963\u8964\u8965"+ + "\u8967\u8968\u8969\u896A\u896B\u896C\u896D\u896E"+ + "\u896F\u8970\u8971\u8972\u8973\u8974\u8975\u8976"+ + "\u8977\u8978\u8979\u897A\u897C\uFFFD\u897D\u897E"+ + "\u8980\u8982\u8984\u8985\u8987\u8988\u8989\u898A"+ + "\u898B\u898C\u898D\u898E\u898F\u8990\u8991\u8992"+ + "\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899A"+ + "\u899B\u899C\u899D\u899E\u899F\u89A0\u89A1\u6447"+ + "\u5C27\u9065\u7A91\u8C23\u59DA\u54AC\u8200\u836F"+ + "\u8981\u8000\u6930\u564E\u8036\u7237\u91CE\u51B6"+ + "\u4E5F\u9875\u6396\u4E1A\u53F6\u66F3\u814B\u591C"+ + "\u6DB2\u4E00\u58F9\u533B\u63D6\u94F1\u4F9D\u4F0A"+ + "\u8863\u9890\u5937\u9057\u79FB\u4EEA\u80F0\u7591"+ + "\u6C82\u5B9C\u59E8\u5F5D\u6905\u8681\u501A\u5DF2"+ + "\u4E59\u77E3\u4EE5\u827A\u6291\u6613\u9091\u5C79"+ + "\u4EBF\u5F79\u81C6\u9038\u8084\u75AB\u4EA6\u88D4"+ + "\u610F\u6BC5\u5FC6\u4E49\u76CA\u6EA2\u8BE3\u8BAE"+ + "\u8C0A\u8BD1\u5F02\u7FFC\u7FCC\u7ECE\u8335\u836B"+ + "\u56E0\u6BB7\u97F3\u9634\u59FB\u541F\u94F6\u6DEB"+ + "\u5BC5\u996E\u5C39\u5F15\u9690\u89A2\u89A3\u89A4"+ + "\u89A5\u89A6\u89A7\u89A8\u89A9\u89AA\u89AB\u89AC"+ + "\u89AD\u89AE\u89AF\u89B0\u89B1\u89B2\u89B3\u89B4"+ + "\u89B5\u89B6\u89B7\u89B8\u89B9\u89BA\u89BB\u89BC"+ + "\u89BD\u89BE\u89BF\u89C0\u89C3\u89CD\u89D3\u89D4"+ + "\u89D5\u89D7\u89D8\u89D9\u89DB\u89DD\u89DF\u89E0"+ + "\u89E1\u89E2\u89E4\u89E7\u89E8\u89E9\u89EA\u89EC"+ + "\u89ED\u89EE\u89F0\u89F1\u89F2\u89F4\u89F5\u89F6"+ + "\u89F7\u89F8\u89F9\u89FA\uFFFD\u89FB\u89FC\u89FD"+ + "\u89FE\u89FF\u8A01\u8A02\u8A03\u8A04\u8A05\u8A06"+ + "\u8A08\u8A09\u8A0A\u8A0B\u8A0C\u8A0D\u8A0E\u8A0F"+ + "\u8A10\u8A11\u8A12\u8A13\u8A14\u8A15\u8A16\u8A17"+ + "\u8A18\u8A19\u8A1A\u8A1B\u8A1C\u8A1D\u5370\u82F1"+ + "\u6A31\u5A74\u9E70\u5E94\u7F28\u83B9\u8424\u8425"+ + "\u8367\u8747\u8FCE\u8D62\u76C8\u5F71\u9896\u786C"+ + "\u6620\u54DF\u62E5\u4F63\u81C3\u75C8\u5EB8\u96CD"+ + "\u8E0A\u86F9\u548F\u6CF3\u6D8C\u6C38\u607F\u52C7"+ + "\u7528\u5E7D\u4F18\u60A0\u5FE7\u5C24\u7531\u90AE"+ + "\u94C0\u72B9\u6CB9\u6E38\u9149\u6709\u53CB\u53F3"+ + "\u4F51\u91C9\u8BF1\u53C8\u5E7C\u8FC2\u6DE4\u4E8E"+ + "\u76C2\u6986\u865E\u611A\u8206\u4F59\u4FDE\u903E"+ + "\u9C7C\u6109\u6E1D\u6E14\u9685\u4E88\u5A31\u96E8"+ + "\u4E0E\u5C7F\u79B9\u5B87\u8BED\u7FBD\u7389\u57DF"+ + "\u828B\u90C1\u5401\u9047\u55BB\u5CEA\u5FA1\u6108"+ + "\u6B32\u72F1\u80B2\u8A89\u8A1E\u8A1F\u8A20\u8A21"+ + "\u8A22\u8A23\u8A24\u8A25\u8A26\u8A27\u8A28\u8A29"+ + "\u8A2A\u8A2B\u8A2C\u8A2D\u8A2E\u8A2F\u8A30\u8A31"+ + "\u8A32\u8A33\u8A34\u8A35\u8A36\u8A37\u8A38\u8A39"+ + "\u8A3A\u8A3B\u8A3C\u8A3D\u8A3F\u8A40\u8A41\u8A42"+ + "\u8A43\u8A44\u8A45\u8A46\u8A47\u8A49\u8A4A\u8A4B"+ + "\u8A4C\u8A4D\u8A4E\u8A4F\u8A50\u8A51\u8A52\u8A53"+ + "\u8A54\u8A55\u8A56\u8A57\u8A58\u8A59\u8A5A\u8A5B"+ + "\u8A5C\u8A5D\u8A5E\uFFFD\u8A5F\u8A60\u8A61\u8A62"+ + "\u8A63\u8A64\u8A65\u8A66\u8A67\u8A68\u8A69\u8A6A"+ + "\u8A6B\u8A6C\u8A6D\u8A6E\u8A6F\u8A70\u8A71\u8A72"+ + "\u8A73\u8A74\u8A75\u8A76\u8A77\u8A78\u8A7A\u8A7B"+ + "\u8A7C\u8A7D\u8A7E\u8A7F\u8A80\u6D74\u5BD3\u88D5"+ + "\u9884\u8C6B\u9A6D\u9E33\u6E0A\u51A4\u5143\u57A3"+ + "\u8881\u539F\u63F4\u8F95\u56ED\u5458\u5706\u733F"+ + "\u6E90\u7F18\u8FDC\u82D1\u613F\u6028\u9662\u66F0"+ + "\u7EA6\u8D8A\u8DC3\u94A5\u5CB3\u7CA4\u6708\u60A6"+ + "\u9605\u8018\u4E91\u90E7\u5300\u9668\u5141\u8FD0"+ + "\u8574\u915D\u6655\u97F5\u5B55\u531D\u7838\u6742"+ + "\u683D\u54C9\u707E\u5BB0\u8F7D\u518D\u5728\u54B1"+ + "\u6512\u6682\u8D5E\u8D43\u810F\u846C\u906D\u7CDF"+ + "\u51FF\u85FB\u67A3\u65E9\u6FA1\u86A4\u8E81\u566A"+ + "\u9020\u7682\u7076\u71E5\u8D23\u62E9\u5219\u6CFD"+ + "\u8D3C\u600E\u589E\u618E\u66FE\u8D60\u624E\u55B3"+ + "\u6E23\u672D\u8F67\u8A81\u8A82\u8A83\u8A84\u8A85"+ + "\u8A86\u8A87\u8A88\u8A8B\u8A8C\u8A8D\u8A8E\u8A8F"+ + "\u8A90\u8A91\u8A92\u8A94\u8A95\u8A96\u8A97\u8A98"+ + "\u8A99\u8A9A\u8A9B\u8A9C\u8A9D\u8A9E\u8A9F\u8AA0"+ + "\u8AA1\u8AA2\u8AA3\u8AA4\u8AA5\u8AA6\u8AA7\u8AA8"+ + "\u8AA9\u8AAA\u8AAB\u8AAC\u8AAD\u8AAE\u8AAF\u8AB0"+ + "\u8AB1\u8AB2\u8AB3\u8AB4\u8AB5\u8AB6\u8AB7\u8AB8"+ + "\u8AB9\u8ABA\u8ABB\u8ABC\u8ABD\u8ABE\u8ABF\u8AC0"+ + "\u8AC1\u8AC2\uFFFD\u8AC3\u8AC4\u8AC5\u8AC6\u8AC7"+ + "\u8AC8\u8AC9\u8ACA\u8ACB\u8ACC\u8ACD\u8ACE\u8ACF"+ + "\u8AD0\u8AD1\u8AD2\u8AD3\u8AD4\u8AD5\u8AD6\u8AD7"+ + "\u8AD8\u8AD9\u8ADA\u8ADB\u8ADC\u8ADD\u8ADE\u8ADF"+ + "\u8AE0\u8AE1\u8AE2\u8AE3\u94E1\u95F8\u7728\u6805"+ + "\u69A8\u548B\u4E4D\u70B8\u8BC8\u6458\u658B\u5B85"+ + "\u7A84\u503A\u5BE8\u77BB\u6BE1\u8A79\u7C98\u6CBE"+ + "\u76CF\u65A9\u8F97\u5D2D\u5C55\u8638\u6808\u5360"+ + "\u6218\u7AD9\u6E5B\u7EFD\u6A1F\u7AE0\u5F70\u6F33"+ + "\u5F20\u638C\u6DA8\u6756\u4E08\u5E10\u8D26\u4ED7"+ + "\u80C0\u7634\u969C\u62DB\u662D\u627E\u6CBC\u8D75"+ + "\u7167\u7F69\u5146\u8087\u53EC\u906E\u6298\u54F2"+ + "\u86F0\u8F99\u8005\u9517\u8517\u8FD9\u6D59\u73CD"+ + "\u659F\u771F\u7504\u7827\u81FB\u8D1E\u9488\u4FA6"+ + "\u6795\u75B9\u8BCA\u9707\u632F\u9547\u9635\u84B8"+ + "\u6323\u7741\u5F81\u72F0\u4E89\u6014\u6574\u62EF"+ + "\u6B63\u653F\u8AE4\u8AE5\u8AE6\u8AE7\u8AE8\u8AE9"+ + "\u8AEA\u8AEB\u8AEC\u8AED\u8AEE\u8AEF\u8AF0\u8AF1"+ + "\u8AF2\u8AF3\u8AF4\u8AF5\u8AF6\u8AF7\u8AF8\u8AF9"+ + "\u8AFA\u8AFB\u8AFC\u8AFD\u8AFE\u8AFF\u8B00\u8B01"+ + "\u8B02\u8B03\u8B04\u8B05\u8B06\u8B08\u8B09\u8B0A"+ + "\u8B0B\u8B0C\u8B0D\u8B0E\u8B0F\u8B10\u8B11\u8B12"+ + "\u8B13\u8B14\u8B15\u8B16\u8B17\u8B18\u8B19\u8B1A"+ + "\u8B1B\u8B1C\u8B1D\u8B1E\u8B1F\u8B20\u8B21\u8B22"+ + "\u8B23\uFFFD\u8B24\u8B25\u8B27\u8B28\u8B29\u8B2A"+ + "\u8B2B\u8B2C\u8B2D\u8B2E\u8B2F\u8B30\u8B31\u8B32"+ + "\u8B33\u8B34\u8B35\u8B36\u8B37\u8B38\u8B39\u8B3A"+ + "\u8B3B\u8B3C\u8B3D\u8B3E\u8B3F\u8B40\u8B41\u8B42"+ + "\u8B43\u8B44\u8B45\u5E27\u75C7\u90D1\u8BC1\u829D"+ + "\u679D\u652F\u5431\u8718\u77E5\u80A2\u8102\u6C41"+ + "\u4E4B\u7EC7\u804C\u76F4\u690D\u6B96\u6267\u503C"+ + "\u4F84\u5740\u6307\u6B62\u8DBE\u53EA\u65E8\u7EB8"+ + "\u5FD7\u631A\u63B7\u81F3\u81F4\u7F6E\u5E1C\u5CD9"+ + "\u5236\u667A\u79E9\u7A1A\u8D28\u7099\u75D4\u6EDE"+ + "\u6CBB\u7A92\u4E2D\u76C5\u5FE0\u949F\u8877\u7EC8"+ + "\u79CD\u80BF\u91CD\u4EF2\u4F17\u821F\u5468\u5DDE"+ + "\u6D32\u8BCC\u7CA5\u8F74\u8098\u5E1A\u5492\u76B1"+ + "\u5B99\u663C\u9AA4\u73E0\u682A\u86DB\u6731\u732A"+ + "\u8BF8\u8BDB\u9010\u7AF9\u70DB\u716E\u62C4\u77A9"+ + "\u5631\u4E3B\u8457\u67F1\u52A9\u86C0\u8D2E\u94F8"+ + "\u7B51\u8B46\u8B47\u8B48\u8B49\u8B4A\u8B4B\u8B4C"+ + "\u8B4D\u8B4E\u8B4F\u8B50\u8B51\u8B52\u8B53\u8B54"+ + "\u8B55\u8B56\u8B57\u8B58\u8B59\u8B5A\u8B5B\u8B5C"+ + "\u8B5D\u8B5E\u8B5F\u8B60\u8B61\u8B62\u8B63\u8B64"+ + "\u8B65\u8B67\u8B68\u8B69\u8B6A\u8B6B\u8B6D\u8B6E"+ + "\u8B6F\u8B70\u8B71\u8B72\u8B73\u8B74\u8B75\u8B76"+ + "\u8B77\u8B78\u8B79\u8B7A\u8B7B\u8B7C\u8B7D\u8B7E"+ + "\u8B7F\u8B80\u8B81\u8B82\u8B83\u8B84\u8B85\u8B86"+ + "\uFFFD\u8B87\u8B88\u8B89\u8B8A\u8B8B\u8B8C\u8B8D"+ + "\u8B8E\u8B8F\u8B90\u8B91\u8B92\u8B93\u8B94\u8B95"+ + "\u8B96\u8B97\u8B98\u8B99\u8B9A\u8B9B\u8B9C\u8B9D"+ + "\u8B9E\u8B9F\u8BAC\u8BB1\u8BBB\u8BC7\u8BD0\u8BEA"+ + "\u8C09\u8C1E\u4F4F\u6CE8\u795D\u9A7B\u6293\u722A"+ + "\u62FD\u4E13\u7816\u8F6C\u64B0\u8D5A\u7BC6\u6869"+ + "\u5E84\u88C5\u5986\u649E\u58EE\u72B6\u690E\u9525"+ + "\u8FFD\u8D58\u5760\u7F00\u8C06\u51C6\u6349\u62D9"+ + "\u5353\u684C\u7422\u8301\u914C\u5544\u7740\u707C"+ + "\u6D4A\u5179\u54A8\u8D44\u59FF\u6ECB\u6DC4\u5B5C"+ + "\u7D2B\u4ED4\u7C7D\u6ED3\u5B50\u81EA\u6E0D\u5B57"+ + "\u9B03\u68D5\u8E2A\u5B97\u7EFC\u603B\u7EB5\u90B9"+ + "\u8D70\u594F\u63CD\u79DF\u8DB3\u5352\u65CF\u7956"+ + "\u8BC5\u963B\u7EC4\u94BB\u7E82\u5634\u9189\u6700"+ + "\u7F6A\u5C0A\u9075\u6628\u5DE6\u4F50\u67DE\u505A"+ + "\u4F5C\u5750\u5EA7\uE810\uE811\uE812\uE813\uE814"+ + "\u8C38\u8C39\u8C3A\u8C3B\u8C3C\u8C3D\u8C3E\u8C3F"+ + "\u8C40\u8C42\u8C43\u8C44\u8C45\u8C48\u8C4A\u8C4B"+ + "\u8C4D\u8C4E\u8C4F\u8C50\u8C51\u8C52\u8C53\u8C54"+ + "\u8C56\u8C57\u8C58\u8C59\u8C5B\u8C5C\u8C5D\u8C5E"+ + "\u8C5F\u8C60\u8C63\u8C64\u8C65\u8C66\u8C67\u8C68"+ + "\u8C69\u8C6C\u8C6D\u8C6E\u8C6F\u8C70\u8C71\u8C72"+ + "\u8C74\u8C75\u8C76\u8C77\u8C7B\u8C7C\u8C7D\u8C7E"+ + "\u8C7F\u8C80\u8C81\u8C83\u8C84\u8C86\u8C87\uFFFD"+ + "\u8C88\u8C8B\u8C8D\u8C8E\u8C8F\u8C90\u8C91\u8C92"+ + "\u8C93\u8C95\u8C96\u8C97\u8C99\u8C9A\u8C9B\u8C9C"+ + "\u8C9D\u8C9E\u8C9F\u8CA0\u8CA1\u8CA2\u8CA3\u8CA4"+ + "\u8CA5\u8CA6\u8CA7\u8CA8\u8CA9\u8CAA\u8CAB\u8CAC"+ + "\u8CAD\u4E8D\u4E0C\u5140\u4E10\u5EFF\u5345\u4E15"+ + "\u4E98\u4E1E\u9B32\u5B6C\u5669\u4E28\u79BA\u4E3F"+ + "\u5315\u4E47\u592D\u723B\u536E\u6C10\u56DF\u80E4"+ + "\u9997\u6BD3\u777E\u9F17\u4E36\u4E9F\u9F10\u4E5C"+ + "\u4E69\u4E93\u8288\u5B5B\u556C\u560F\u4EC4\u538D"+ + "\u539D\u53A3\u53A5\u53AE\u9765\u8D5D\u531A\u53F5"+ + "\u5326\u532E\u533E\u8D5C\u5366\u5363\u5202\u5208"+ + "\u520E\u522D\u5233\u523F\u5240\u524C\u525E\u5261"+ + "\u525C\u84AF\u527D\u5282\u5281\u5290\u5293\u5182"+ + "\u7F54\u4EBB\u4EC3\u4EC9\u4EC2\u4EE8\u4EE1\u4EEB"+ + "\u4EDE\u4F1B\u4EF3\u4F22\u4F64\u4EF5\u4F25\u4F27"+ + "\u4F09\u4F2B\u4F5E\u4F67\u6538\u4F5A\u4F5D\u8CAE"+ + "\u8CAF\u8CB0\u8CB1\u8CB2\u8CB3\u8CB4\u8CB5\u8CB6"+ + "\u8CB7\u8CB8\u8CB9\u8CBA\u8CBB\u8CBC\u8CBD\u8CBE"+ + "\u8CBF\u8CC0\u8CC1\u8CC2\u8CC3\u8CC4\u8CC5\u8CC6"+ + "\u8CC7\u8CC8\u8CC9\u8CCA\u8CCB\u8CCC\u8CCD\u8CCE"+ + "\u8CCF\u8CD0\u8CD1\u8CD2\u8CD3\u8CD4\u8CD5\u8CD6"+ + "\u8CD7\u8CD8\u8CD9\u8CDA\u8CDB\u8CDC\u8CDD\u8CDE"+ + "\u8CDF\u8CE0\u8CE1\u8CE2\u8CE3\u8CE4\u8CE5\u8CE6"+ + "\u8CE7\u8CE8\u8CE9\u8CEA\u8CEB\u8CEC\uFFFD\u8CED"+ + "\u8CEE\u8CEF\u8CF0\u8CF1\u8CF2\u8CF3\u8CF4\u8CF5"+ + "\u8CF6\u8CF7\u8CF8\u8CF9\u8CFA\u8CFB\u8CFC\u8CFD"+ + "\u8CFE\u8CFF\u8D00\u8D01\u8D02\u8D03\u8D04\u8D05"+ + "\u8D06\u8D07\u8D08\u8D09\u8D0A\u8D0B\u8D0C\u8D0D"+ + "\u4F5F\u4F57\u4F32\u4F3D\u4F76\u4F74\u4F91\u4F89"+ + "\u4F83\u4F8F\u4F7E\u4F7B\u4FAA\u4F7C\u4FAC\u4F94"+ + "\u4FE6\u4FE8\u4FEA\u4FC5\u4FDA\u4FE3\u4FDC\u4FD1"+ + "\u4FDF\u4FF8\u5029\u504C\u4FF3\u502C\u500F\u502E"+ + "\u502D\u4FFE\u501C\u500C\u5025\u5028\u507E\u5043"+ + "\u5055\u5048\u504E\u506C\u507B\u50A5\u50A7\u50A9"+ + "\u50BA\u50D6\u5106\u50ED\u50EC\u50E6\u50EE\u5107"+ + "\u510B\u4EDD\u6C3D\u4F58\u4F65\u4FCE\u9FA0\u6C46"+ + "\u7C74\u516E\u5DFD\u9EC9\u9998\u5181\u5914\u52F9"+ + "\u530D\u8A07\u5310\u51EB\u5919\u5155\u4EA0\u5156"+ + "\u4EB3\u886E\u88A4\u4EB5\u8114\u88D2\u7980\u5B34"+ + "\u8803\u7FB8\u51AB\u51B1\u51BD\u51BC\u8D0E\u8D0F"+ + "\u8D10\u8D11\u8D12\u8D13\u8D14\u8D15\u8D16\u8D17"+ + "\u8D18\u8D19\u8D1A\u8D1B\u8D1C\u8D20\u8D51\u8D52"+ + "\u8D57\u8D5F\u8D65\u8D68\u8D69\u8D6A\u8D6C\u8D6E"+ + "\u8D6F\u8D71\u8D72\u8D78\u8D79\u8D7A\u8D7B\u8D7C"+ + "\u8D7D\u8D7E\u8D7F\u8D80\u8D82\u8D83\u8D86\u8D87"+ + "\u8D88\u8D89\u8D8C\u8D8D\u8D8E\u8D8F\u8D90\u8D92"+ + "\u8D93\u8D95\u8D96\u8D97\u8D98\u8D99\u8D9A\u8D9B"+ + "\u8D9C\u8D9D\u8D9E\u8DA0\u8DA1\uFFFD\u8DA2\u8DA4"+ + "\u8DA5\u8DA6\u8DA7\u8DA8\u8DA9\u8DAA\u8DAB\u8DAC"+ + "\u8DAD\u8DAE\u8DAF\u8DB0\u8DB2\u8DB6\u8DB7\u8DB9"+ + "\u8DBB\u8DBD\u8DC0\u8DC1\u8DC2\u8DC5\u8DC7\u8DC8"+ + "\u8DC9\u8DCA\u8DCD\u8DD0\u8DD2\u8DD3\u8DD4\u51C7"+ + "\u5196\u51A2\u51A5\u8BA0\u8BA6\u8BA7\u8BAA\u8BB4"+ + "\u8BB5\u8BB7\u8BC2\u8BC3\u8BCB\u8BCF\u8BCE\u8BD2"+ + "\u8BD3\u8BD4\u8BD6\u8BD8\u8BD9\u8BDC\u8BDF\u8BE0"+ + "\u8BE4\u8BE8\u8BE9\u8BEE\u8BF0\u8BF3\u8BF6\u8BF9"+ + "\u8BFC\u8BFF\u8C00\u8C02\u8C04\u8C07\u8C0C\u8C0F"+ + "\u8C11\u8C12\u8C14\u8C15\u8C16\u8C19\u8C1B\u8C18"+ + "\u8C1D\u8C1F\u8C20\u8C21\u8C25\u8C27\u8C2A\u8C2B"+ + "\u8C2E\u8C2F\u8C32\u8C33\u8C35\u8C36\u5369\u537A"+ + "\u961D\u9622\u9621\u9631\u962A\u963D\u963C\u9642"+ + "\u9649\u9654\u965F\u9667\u966C\u9672\u9674\u9688"+ + "\u968D\u9697\u96B0\u9097\u909B\u909D\u9099\u90AC"+ + "\u90A1\u90B4\u90B3\u90B6\u90BA\u8DD5\u8DD8\u8DD9"+ + "\u8DDC\u8DE0\u8DE1\u8DE2\u8DE5\u8DE6\u8DE7\u8DE9"+ + "\u8DED\u8DEE\u8DF0\u8DF1\u8DF2\u8DF4\u8DF6\u8DFC"+ + "\u8DFE\u8DFF\u8E00\u8E01\u8E02\u8E03\u8E04\u8E06"+ + "\u8E07\u8E08\u8E0B\u8E0D\u8E0E\u8E10\u8E11\u8E12"+ + "\u8E13\u8E15\u8E16\u8E17\u8E18\u8E19\u8E1A\u8E1B"+ + "\u8E1C\u8E20\u8E21\u8E24\u8E25\u8E26\u8E27\u8E28"+ + "\u8E2B\u8E2D\u8E30\u8E32\u8E33\u8E34\u8E36\u8E37"+ + "\u8E38\u8E3B\u8E3C\u8E3E\uFFFD\u8E3F\u8E43\u8E45"+ + "\u8E46\u8E4C\u8E4D\u8E4E\u8E4F\u8E50\u8E53\u8E54"+ + "\u8E55\u8E56\u8E57\u8E58\u8E5A\u8E5B\u8E5C\u8E5D"+ + "\u8E5E\u8E5F\u8E60\u8E61\u8E62\u8E63\u8E64\u8E65"+ + "\u8E67\u8E68\u8E6A\u8E6B\u8E6E\u8E71\u90B8\u90B0"+ + "\u90CF\u90C5\u90BE\u90D0\u90C4\u90C7\u90D3\u90E6"+ + "\u90E2\u90DC\u90D7\u90DB\u90EB\u90EF\u90FE\u9104"+ + "\u9122\u911E\u9123\u9131\u912F\u9139\u9143\u9146"+ + "\u520D\u5942\u52A2\u52AC\u52AD\u52BE\u54FF\u52D0"+ + "\u52D6\u52F0\u53DF\u71EE\u77CD\u5EF4\u51F5\u51FC"+ + "\u9B2F\u53B6\u5F01\u755A\u5DEF\u574C\u57A9\u57A1"+ + "\u587E\u58BC\u58C5\u58D1\u5729\u572C\u572A\u5733"+ + "\u5739\u572E\u572F\u575C\u573B\u5742\u5769\u5785"+ + "\u576B\u5786\u577C\u577B\u5768\u576D\u5776\u5773"+ + "\u57AD\u57A4\u578C\u57B2\u57CF\u57A7\u57B4\u5793"+ + "\u57A0\u57D5\u57D8\u57DA\u57D9\u57D2\u57B8\u57F4"+ + "\u57EF\u57F8\u57E4\u57DD\u8E73\u8E75\u8E77\u8E78"+ + "\u8E79\u8E7A\u8E7B\u8E7D\u8E7E\u8E80\u8E82\u8E83"+ + "\u8E84\u8E86\u8E88\u8E89\u8E8A\u8E8B\u8E8C\u8E8D"+ + "\u8E8E\u8E91\u8E92\u8E93\u8E95\u8E96\u8E97\u8E98"+ + "\u8E99\u8E9A\u8E9B\u8E9D\u8E9F\u8EA0\u8EA1\u8EA2"+ + "\u8EA3\u8EA4\u8EA5\u8EA6\u8EA7\u8EA8\u8EA9\u8EAA"+ + "\u8EAD\u8EAE\u8EB0\u8EB1\u8EB3\u8EB4\u8EB5\u8EB6"+ + "\u8EB7\u8EB8\u8EB9\u8EBB\u8EBC\u8EBD\u8EBE\u8EBF"+ + "\u8EC0\u8EC1\u8EC2\uFFFD\u8EC3\u8EC4\u8EC5\u8EC6"+ + "\u8EC7\u8EC8\u8EC9\u8ECA\u8ECB\u8ECC\u8ECD\u8ECF"+ + "\u8ED0\u8ED1\u8ED2\u8ED3\u8ED4\u8ED5\u8ED6\u8ED7"+ + "\u8ED8\u8ED9\u8EDA\u8EDB\u8EDC\u8EDD\u8EDE\u8EDF"+ + "\u8EE0\u8EE1\u8EE2\u8EE3\u8EE4\u580B\u580D\u57FD"+ + "\u57ED\u5800\u581E\u5819\u5844\u5820\u5865\u586C"+ + "\u5881\u5889\u589A\u5880\u99A8\u9F19\u61FF\u8279"+ + "\u827D\u827F\u828F\u828A\u82A8\u8284\u828E\u8291"+ + "\u8297\u8299\u82AB\u82B8\u82BE\u82B0\u82C8\u82CA"+ + "\u82E3\u8298\u82B7\u82AE\u82CB\u82CC\u82C1\u82A9"+ + "\u82B4\u82A1\u82AA\u829F\u82C4\u82CE\u82A4\u82E1"+ + "\u8309\u82F7\u82E4\u830F\u8307\u82DC\u82F4\u82D2"+ + "\u82D8\u830C\u82FB\u82D3\u8311\u831A\u8306\u8314"+ + "\u8315\u82E0\u82D5\u831C\u8351\u835B\u835C\u8308"+ + "\u8392\u833C\u8334\u8331\u839B\u835E\u832F\u834F"+ + "\u8347\u8343\u835F\u8340\u8317\u8360\u832D\u833A"+ + "\u8333\u8366\u8365\u8EE5\u8EE6\u8EE7\u8EE8\u8EE9"+ + "\u8EEA\u8EEB\u8EEC\u8EED\u8EEE\u8EEF\u8EF0\u8EF1"+ + "\u8EF2\u8EF3\u8EF4\u8EF5\u8EF6\u8EF7\u8EF8\u8EF9"+ + "\u8EFA\u8EFB\u8EFC\u8EFD\u8EFE\u8EFF\u8F00\u8F01"+ + "\u8F02\u8F03\u8F04\u8F05\u8F06\u8F07\u8F08\u8F09"+ + "\u8F0A\u8F0B\u8F0C\u8F0D\u8F0E\u8F0F\u8F10\u8F11"+ + "\u8F12\u8F13\u8F14\u8F15\u8F16\u8F17\u8F18\u8F19"+ + "\u8F1A\u8F1B\u8F1C\u8F1D\u8F1E\u8F1F\u8F20\u8F21"+ + "\u8F22\u8F23\uFFFD\u8F24\u8F25\u8F26\u8F27\u8F28"+ + "\u8F29\u8F2A\u8F2B\u8F2C\u8F2D\u8F2E\u8F2F\u8F30"+ + "\u8F31\u8F32\u8F33\u8F34\u8F35\u8F36\u8F37\u8F38"+ + "\u8F39\u8F3A\u8F3B\u8F3C\u8F3D\u8F3E\u8F3F\u8F40"+ + "\u8F41\u8F42\u8F43\u8F44\u8368\u831B\u8369\u836C"+ + "\u836A\u836D\u836E\u83B0\u8378\u83B3\u83B4\u83A0"+ + "\u83AA\u8393\u839C\u8385\u837C\u83B6\u83A9\u837D"+ + "\u83B8\u837B\u8398\u839E\u83A8\u83BA\u83BC\u83C1"+ + "\u8401\u83E5\u83D8\u5807\u8418\u840B\u83DD\u83FD"+ + "\u83D6\u841C\u8438\u8411\u8406\u83D4\u83DF\u840F"+ + "\u8403\u83F8\u83F9\u83EA\u83C5\u83C0\u8426\u83F0"+ + "\u83E1\u845C\u8451\u845A\u8459\u8473\u8487\u8488"+ + "\u847A\u8489\u8478\u843C\u8446\u8469\u8476\u848C"+ + "\u848E\u8431\u846D\u84C1\u84CD\u84D0\u84E6\u84BD"+ + "\u84D3\u84CA\u84BF\u84BA\u84E0\u84A1\u84B9\u84B4"+ + "\u8497\u84E5\u84E3\u850C\u750D\u8538\u84F0\u8539"+ + "\u851F\u853A\u8F45\u8F46\u8F47\u8F48\u8F49\u8F4A"+ + "\u8F4B\u8F4C\u8F4D\u8F4E\u8F4F\u8F50\u8F51\u8F52"+ + "\u8F53\u8F54\u8F55\u8F56\u8F57\u8F58\u8F59\u8F5A"+ + "\u8F5B\u8F5C\u8F5D\u8F5E\u8F5F\u8F60\u8F61\u8F62"+ + "\u8F63\u8F64\u8F65\u8F6A\u8F80\u8F8C\u8F92\u8F9D"+ + "\u8FA0\u8FA1\u8FA2\u8FA4\u8FA5\u8FA6\u8FA7\u8FAA"+ + "\u8FAC\u8FAD\u8FAE\u8FAF\u8FB2\u8FB3\u8FB4\u8FB5"+ + "\u8FB7\u8FB8\u8FBA\u8FBB\u8FBC\u8FBF\u8FC0\u8FC3"+ + "\u8FC6\uFFFD\u8FC9\u8FCA\u8FCB\u8FCC\u8FCD\u8FCF"+ + "\u8FD2\u8FD6\u8FD7\u8FDA\u8FE0\u8FE1\u8FE3\u8FE7"+ + "\u8FEC\u8FEF\u8FF1\u8FF2\u8FF4\u8FF5\u8FF6\u8FFA"+ + "\u8FFB\u8FFC\u8FFE\u8FFF\u9007\u9008\u900C\u900E"+ + "\u9013\u9015\u9018\u8556\u853B\u84FF\u84FC\u8559"+ + "\u8548\u8568\u8564\u855E\u857A\u77A2\u8543\u8572"+ + "\u857B\u85A4\u85A8\u8587\u858F\u8579\u85AE\u859C"+ + "\u8585\u85B9\u85B7\u85B0\u85D3\u85C1\u85DC\u85FF"+ + "\u8627\u8605\u8629\u8616\u863C\u5EFE\u5F08\u593C"+ + "\u5941\u8037\u5955\u595A\u5958\u530F\u5C22\u5C25"+ + "\u5C2C\u5C34\u624C\u626A\u629F\u62BB\u62CA\u62DA"+ + "\u62D7\u62EE\u6322\u62F6\u6339\u634B\u6343\u63AD"+ + "\u63F6\u6371\u637A\u638E\u63B4\u636D\u63AC\u638A"+ + "\u6369\u63AE\u63BC\u63F2\u63F8\u63E0\u63FF\u63C4"+ + "\u63DE\u63CE\u6452\u63C6\u63BE\u6445\u6441\u640B"+ + "\u641B\u6420\u640C\u6426\u6421\u645E\u6484\u646D"+ + "\u6496\u9019\u901C\u9023\u9024\u9025\u9027\u9028"+ + "\u9029\u902A\u902B\u902C\u9030\u9031\u9032\u9033"+ + "\u9034\u9037\u9039\u903A\u903D\u903F\u9040\u9043"+ + "\u9045\u9046\u9048\u9049\u904A\u904B\u904C\u904E"+ + "\u9054\u9055\u9056\u9059\u905A\u905C\u905D\u905E"+ + "\u905F\u9060\u9061\u9064\u9066\u9067\u9069\u906A"+ + "\u906B\u906C\u906F\u9070\u9071\u9072\u9073\u9076"+ + "\u9077\u9078\u9079\u907A\u907B\u907C\u907E\u9081"+ + "\uFFFD\u9084\u9085\u9086\u9087\u9089\u908A\u908C"+ + "\u908D\u908E\u908F\u9090\u9092\u9094\u9096\u9098"+ + "\u909A\u909C\u909E\u909F\u90A0\u90A4\u90A5\u90A7"+ + "\u90A8\u90A9\u90AB\u90AD\u90B2\u90B7\u90BC\u90BD"+ + "\u90BF\u90C0\u647A\u64B7\u64B8\u6499\u64BA\u64C0"+ + "\u64D0\u64D7\u64E4\u64E2\u6509\u6525\u652E\u5F0B"+ + "\u5FD2\u7519\u5F11\u535F\u53F1\u53FD\u53E9\u53E8"+ + "\u53FB\u5412\u5416\u5406\u544B\u5452\u5453\u5454"+ + "\u5456\u5443\u5421\u5457\u5459\u5423\u5432\u5482"+ + "\u5494\u5477\u5471\u5464\u549A\u549B\u5484\u5476"+ + "\u5466\u549D\u54D0\u54AD\u54C2\u54B4\u54D2\u54A7"+ + "\u54A6\u54D3\u54D4\u5472\u54A3\u54D5\u54BB\u54BF"+ + "\u54CC\u54D9\u54DA\u54DC\u54A9\u54AA\u54A4\u54DD"+ + "\u54CF\u54DE\u551B\u54E7\u5520\u54FD\u5514\u54F3"+ + "\u5522\u5523\u550F\u5511\u5527\u552A\u5567\u558F"+ + "\u55B5\u5549\u556D\u5541\u5555\u553F\u5550\u553C"; + + private final static String innerIndex6= + "\u90C2\u90C3\u90C6\u90C8\u90C9\u90CB\u90CC\u90CD"+ + "\u90D2\u90D4\u90D5\u90D6\u90D8\u90D9\u90DA\u90DE"+ + "\u90DF\u90E0\u90E3\u90E4\u90E5\u90E9\u90EA\u90EC"+ + "\u90EE\u90F0\u90F1\u90F2\u90F3\u90F5\u90F6\u90F7"+ + "\u90F9\u90FA\u90FB\u90FC\u90FF\u9100\u9101\u9103"+ + "\u9105\u9106\u9107\u9108\u9109\u910A\u910B\u910C"+ + "\u910D\u910E\u910F\u9110\u9111\u9112\u9113\u9114"+ + "\u9115\u9116\u9117\u9118\u911A\u911B\u911C\uFFFD"+ + "\u911D\u911F\u9120\u9121\u9124\u9125\u9126\u9127"+ + "\u9128\u9129\u912A\u912B\u912C\u912D\u912E\u9130"+ + "\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u913A"+ + "\u913B\u913C\u913D\u913E\u913F\u9140\u9141\u9142"+ + "\u9144\u5537\u5556\u5575\u5576\u5577\u5533\u5530"+ + "\u555C\u558B\u55D2\u5583\u55B1\u55B9\u5588\u5581"+ + "\u559F\u557E\u55D6\u5591\u557B\u55DF\u55BD\u55BE"+ + "\u5594\u5599\u55EA\u55F7\u55C9\u561F\u55D1\u55EB"+ + "\u55EC\u55D4\u55E6\u55DD\u55C4\u55EF\u55E5\u55F2"+ + "\u55F3\u55CC\u55CD\u55E8\u55F5\u55E4\u8F94\u561E"+ + "\u5608\u560C\u5601\u5624\u5623\u55FE\u5600\u5627"+ + "\u562D\u5658\u5639\u5657\u562C\u564D\u5662\u5659"+ + "\u565C\u564C\u5654\u5686\u5664\u5671\u566B\u567B"+ + "\u567C\u5685\u5693\u56AF\u56D4\u56D7\u56DD\u56E1"+ + "\u56F5\u56EB\u56F9\u56FF\u5704\u570A\u5709\u571C"+ + "\u5E0F\u5E19\u5E14\u5E11\u5E31\u5E3B\u5E3C\u9145"+ + "\u9147\u9148\u9151\u9153\u9154\u9155\u9156\u9158"+ + "\u9159\u915B\u915C\u915F\u9160\u9166\u9167\u9168"+ + "\u916B\u916D\u9173\u917A\u917B\u917C\u9180\u9181"+ + "\u9182\u9183\u9184\u9186\u9188\u918A\u918E\u918F"+ + "\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919C"+ + "\u919D\u919E\u919F\u91A0\u91A1\u91A4\u91A5\u91A6"+ + "\u91A7\u91A8\u91A9\u91AB\u91AC\u91B0\u91B1\u91B2"+ + "\u91B3\u91B6\u91B7\u91B8\u91B9\u91BB\uFFFD\u91BC"+ + "\u91BD\u91BE\u91BF\u91C0\u91C1\u91C2\u91C3\u91C4"+ + "\u91C5\u91C6\u91C8\u91CB\u91D0\u91D2\u91D3\u91D4"+ + "\u91D5\u91D6\u91D7\u91D8\u91D9\u91DA\u91DB\u91DD"+ + "\u91DE\u91DF\u91E0\u91E1\u91E2\u91E3\u91E4\u91E5"+ + "\u5E37\u5E44\u5E54\u5E5B\u5E5E\u5E61\u5C8C\u5C7A"+ + "\u5C8D\u5C90\u5C96\u5C88\u5C98\u5C99\u5C91\u5C9A"+ + "\u5C9C\u5CB5\u5CA2\u5CBD\u5CAC\u5CAB\u5CB1\u5CA3"+ + "\u5CC1\u5CB7\u5CC4\u5CD2\u5CE4\u5CCB\u5CE5\u5D02"+ + "\u5D03\u5D27\u5D26\u5D2E\u5D24\u5D1E\u5D06\u5D1B"+ + "\u5D58\u5D3E\u5D34\u5D3D\u5D6C\u5D5B\u5D6F\u5D5D"+ + "\u5D6B\u5D4B\u5D4A\u5D69\u5D74\u5D82\u5D99\u5D9D"+ + "\u8C73\u5DB7\u5DC5\u5F73\u5F77\u5F82\u5F87\u5F89"+ + "\u5F8C\u5F95\u5F99\u5F9C\u5FA8\u5FAD\u5FB5\u5FBC"+ + "\u8862\u5F61\u72AD\u72B0\u72B4\u72B7\u72B8\u72C3"+ + "\u72C1\u72CE\u72CD\u72D2\u72E8\u72EF\u72E9\u72F2"+ + "\u72F4\u72F7\u7301\u72F3\u7303\u72FA\u91E6\u91E7"+ + "\u91E8\u91E9\u91EA\u91EB\u91EC\u91ED\u91EE\u91EF"+ + "\u91F0\u91F1\u91F2\u91F3\u91F4\u91F5\u91F6\u91F7"+ + "\u91F8\u91F9\u91FA\u91FB\u91FC\u91FD\u91FE\u91FF"+ + "\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207"+ + "\u9208\u9209\u920A\u920B\u920C\u920D\u920E\u920F"+ + "\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217"+ + "\u9218\u9219\u921A\u921B\u921C\u921D\u921E\u921F"+ + "\u9220\u9221\u9222\u9223\u9224\uFFFD\u9225\u9226"+ + "\u9227\u9228\u9229\u922A\u922B\u922C\u922D\u922E"+ + "\u922F\u9230\u9231\u9232\u9233\u9234\u9235\u9236"+ + "\u9237\u9238\u9239\u923A\u923B\u923C\u923D\u923E"+ + "\u923F\u9240\u9241\u9242\u9243\u9244\u9245\u72FB"+ + "\u7317\u7313\u7321\u730A\u731E\u731D\u7315\u7322"+ + "\u7339\u7325\u732C\u7338\u7331\u7350\u734D\u7357"+ + "\u7360\u736C\u736F\u737E\u821B\u5925\u98E7\u5924"+ + "\u5902\u9963\u9967\u9968\u9969\u996A\u996B\u996C"+ + "\u9974\u9977\u997D\u9980\u9984\u9987\u998A\u998D"+ + "\u9990\u9991\u9993\u9994\u9995\u5E80\u5E91\u5E8B"+ + "\u5E96\u5EA5\u5EA0\u5EB9\u5EB5\u5EBE\u5EB3\u8D53"+ + "\u5ED2\u5ED1\u5EDB\u5EE8\u5EEA\u81BA\u5FC4\u5FC9"+ + "\u5FD6\u5FCF\u6003\u5FEE\u6004\u5FE1\u5FE4\u5FFE"+ + "\u6005\u6006\u5FEA\u5FED\u5FF8\u6019\u6035\u6026"+ + "\u601B\u600F\u600D\u6029\u602B\u600A\u603F\u6021"+ + "\u6078\u6079\u607B\u607A\u6042\u9246\u9247\u9248"+ + "\u9249\u924A\u924B\u924C\u924D\u924E\u924F\u9250"+ + "\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258"+ + "\u9259\u925A\u925B\u925C\u925D\u925E\u925F\u9260"+ + "\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268"+ + "\u9269\u926A\u926B\u926C\u926D\u926E\u926F\u9270"+ + "\u9271\u9272\u9273\u9275\u9276\u9277\u9278\u9279"+ + "\u927A\u927B\u927C\u927D\u927E\u927F\u9280\u9281"+ + "\u9282\u9283\u9284\u9285\uFFFD\u9286\u9287\u9288"+ + "\u9289\u928A\u928B\u928C\u928D\u928F\u9290\u9291"+ + "\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299"+ + "\u929A\u929B\u929C\u929D\u929E\u929F\u92A0\u92A1"+ + "\u92A2\u92A3\u92A4\u92A5\u92A6\u92A7\u606A\u607D"+ + "\u6096\u609A\u60AD\u609D\u6083\u6092\u608C\u609B"+ + "\u60EC\u60BB\u60B1\u60DD\u60D8\u60C6\u60DA\u60B4"+ + "\u6120\u6126\u6115\u6123\u60F4\u6100\u610E\u612B"+ + "\u614A\u6175\u61AC\u6194\u61A7\u61B7\u61D4\u61F5"+ + "\u5FDD\u96B3\u95E9\u95EB\u95F1\u95F3\u95F5\u95F6"+ + "\u95FC\u95FE\u9603\u9604\u9606\u9608\u960A\u960B"+ + "\u960C\u960D\u960F\u9612\u9615\u9616\u9617\u9619"+ + "\u961A\u4E2C\u723F\u6215\u6C35\u6C54\u6C5C\u6C4A"+ + "\u6CA3\u6C85\u6C90\u6C94\u6C8C\u6C68\u6C69\u6C74"+ + "\u6C76\u6C86\u6CA9\u6CD0\u6CD4\u6CAD\u6CF7\u6CF8"+ + "\u6CF1\u6CD7\u6CB2\u6CE0\u6CD6\u6CFA\u6CEB\u6CEE"+ + "\u6CB1\u6CD3\u6CEF\u6CFE\u92A8\u92A9\u92AA\u92AB"+ + "\u92AC\u92AD\u92AF\u92B0\u92B1\u92B2\u92B3\u92B4"+ + "\u92B5\u92B6\u92B7\u92B8\u92B9\u92BA\u92BB\u92BC"+ + "\u92BD\u92BE\u92BF\u92C0\u92C1\u92C2\u92C3\u92C4"+ + "\u92C5\u92C6\u92C7\u92C9\u92CA\u92CB\u92CC\u92CD"+ + "\u92CE\u92CF\u92D0\u92D1\u92D2\u92D3\u92D4\u92D5"+ + "\u92D6\u92D7\u92D8\u92D9\u92DA\u92DB\u92DC\u92DD"+ + "\u92DE\u92DF\u92E0\u92E1\u92E2\u92E3\u92E4\u92E5"+ + "\u92E6\u92E7\u92E8\uFFFD\u92E9\u92EA\u92EB\u92EC"+ + "\u92ED\u92EE\u92EF\u92F0\u92F1\u92F2\u92F3\u92F4"+ + "\u92F5\u92F6\u92F7\u92F8\u92F9\u92FA\u92FB\u92FC"+ + "\u92FD\u92FE\u92FF\u9300\u9301\u9302\u9303\u9304"+ + "\u9305\u9306\u9307\u9308\u9309\u6D39\u6D27\u6D0C"+ + "\u6D43\u6D48\u6D07\u6D04\u6D19\u6D0E\u6D2B\u6D4D"+ + "\u6D2E\u6D35\u6D1A\u6D4F\u6D52\u6D54\u6D33\u6D91"+ + "\u6D6F\u6D9E\u6DA0\u6D5E\u6D93\u6D94\u6D5C\u6D60"+ + "\u6D7C\u6D63\u6E1A\u6DC7\u6DC5\u6DDE\u6E0E\u6DBF"+ + "\u6DE0\u6E11\u6DE6\u6DDD\u6DD9\u6E16\u6DAB\u6E0C"+ + "\u6DAE\u6E2B\u6E6E\u6E4E\u6E6B\u6EB2\u6E5F\u6E86"+ + "\u6E53\u6E54\u6E32\u6E25\u6E44\u6EDF\u6EB1\u6E98"+ + "\u6EE0\u6F2D\u6EE2\u6EA5\u6EA7\u6EBD\u6EBB\u6EB7"+ + "\u6ED7\u6EB4\u6ECF\u6E8F\u6EC2\u6E9F\u6F62\u6F46"+ + "\u6F47\u6F24\u6F15\u6EF9\u6F2F\u6F36\u6F4B\u6F74"+ + "\u6F2A\u6F09\u6F29\u6F89\u6F8D\u6F8C\u6F78\u6F72"+ + "\u6F7C\u6F7A\u6FD1\u930A\u930B\u930C\u930D\u930E"+ + "\u930F\u9310\u9311\u9312\u9313\u9314\u9315\u9316"+ + "\u9317\u9318\u9319\u931A\u931B\u931C\u931D\u931E"+ + "\u931F\u9320\u9321\u9322\u9323\u9324\u9325\u9326"+ + "\u9327\u9328\u9329\u932A\u932B\u932C\u932D\u932E"+ + "\u932F\u9330\u9331\u9332\u9333\u9334\u9335\u9336"+ + "\u9337\u9338\u9339\u933A\u933B\u933C\u933D\u933F"+ + "\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347"+ + "\u9348\u9349\uFFFD\u934A\u934B\u934C\u934D\u934E"+ + "\u934F\u9350\u9351\u9352\u9353\u9354\u9355\u9356"+ + "\u9357\u9358\u9359\u935A\u935B\u935C\u935D\u935E"+ + "\u935F\u9360\u9361\u9362\u9363\u9364\u9365\u9366"+ + "\u9367\u9368\u9369\u936B\u6FC9\u6FA7\u6FB9\u6FB6"+ + "\u6FC2\u6FE1\u6FEE\u6FDE\u6FE0\u6FEF\u701A\u7023"+ + "\u701B\u7039\u7035\u704F\u705E\u5B80\u5B84\u5B95"+ + "\u5B93\u5BA5\u5BB8\u752F\u9A9E\u6434\u5BE4\u5BEE"+ + "\u8930\u5BF0\u8E47\u8B07\u8FB6\u8FD3\u8FD5\u8FE5"+ + "\u8FEE\u8FE4\u8FE9\u8FE6\u8FF3\u8FE8\u9005\u9004"+ + "\u900B\u9026\u9011\u900D\u9016\u9021\u9035\u9036"+ + "\u902D\u902F\u9044\u9051\u9052\u9050\u9068\u9058"+ + "\u9062\u905B\u66B9\u9074\u907D\u9082\u9088\u9083"+ + "\u908B\u5F50\u5F57\u5F56\u5F58\u5C3B\u54AB\u5C50"+ + "\u5C59\u5B71\u5C63\u5C66\u7FBC\u5F2A\u5F29\u5F2D"+ + "\u8274\u5F3C\u9B3B\u5C6E\u5981\u5983\u598D\u59A9"+ + "\u59AA\u59A3\u936C\u936D\u936E\u936F\u9370\u9371"+ + "\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379"+ + "\u937A\u937B\u937C\u937D\u937E\u937F\u9380\u9381"+ + "\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389"+ + "\u938A\u938B\u938C\u938D\u938E\u9390\u9391\u9392"+ + "\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939A"+ + "\u939B\u939C\u939D\u939E\u939F\u93A0\u93A1\u93A2"+ + "\u93A3\u93A4\u93A5\u93A6\u93A7\u93A8\u93A9\u93AA"+ + "\u93AB\uFFFD\u93AC\u93AD\u93AE\u93AF\u93B0\u93B1"+ + "\u93B2\u93B3\u93B4\u93B5\u93B6\u93B7\u93B8\u93B9"+ + "\u93BA\u93BB\u93BC\u93BD\u93BE\u93BF\u93C0\u93C1"+ + "\u93C2\u93C3\u93C4\u93C5\u93C6\u93C7\u93C8\u93C9"+ + "\u93CB\u93CC\u93CD\u5997\u59CA\u59AB\u599E\u59A4"+ + "\u59D2\u59B2\u59AF\u59D7\u59BE\u5A05\u5A06\u59DD"+ + "\u5A08\u59E3\u59D8\u59F9\u5A0C\u5A09\u5A32\u5A34"+ + "\u5A11\u5A23\u5A13\u5A40\u5A67\u5A4A\u5A55\u5A3C"+ + "\u5A62\u5A75\u80EC\u5AAA\u5A9B\u5A77\u5A7A\u5ABE"+ + "\u5AEB\u5AB2\u5AD2\u5AD4\u5AB8\u5AE0\u5AE3\u5AF1"+ + "\u5AD6\u5AE6\u5AD8\u5ADC\u5B09\u5B17\u5B16\u5B32"+ + "\u5B37\u5B40\u5C15\u5C1C\u5B5A\u5B65\u5B73\u5B51"+ + "\u5B53\u5B62\u9A75\u9A77\u9A78\u9A7A\u9A7F\u9A7D"+ + "\u9A80\u9A81\u9A85\u9A88\u9A8A\u9A90\u9A92\u9A93"+ + "\u9A96\u9A98\u9A9B\u9A9C\u9A9D\u9A9F\u9AA0\u9AA2"+ + "\u9AA3\u9AA5\u9AA7\u7E9F\u7EA1\u7EA3\u7EA5\u7EA8"+ + "\u7EA9\u93CE\u93CF\u93D0\u93D1\u93D2\u93D3\u93D4"+ + "\u93D5\u93D7\u93D8\u93D9\u93DA\u93DB\u93DC\u93DD"+ + "\u93DE\u93DF\u93E0\u93E1\u93E2\u93E3\u93E4\u93E5"+ + "\u93E6\u93E7\u93E8\u93E9\u93EA\u93EB\u93EC\u93ED"+ + "\u93EE\u93EF\u93F0\u93F1\u93F2\u93F3\u93F4\u93F5"+ + "\u93F6\u93F7\u93F8\u93F9\u93FA\u93FB\u93FC\u93FD"+ + "\u93FE\u93FF\u9400\u9401\u9402\u9403\u9404\u9405"+ + "\u9406\u9407\u9408\u9409\u940A\u940B\u940C\u940D"+ + "\uFFFD\u940E\u940F\u9410\u9411\u9412\u9413\u9414"+ + "\u9415\u9416\u9417\u9418\u9419\u941A\u941B\u941C"+ + "\u941D\u941E\u941F\u9420\u9421\u9422\u9423\u9424"+ + "\u9425\u9426\u9427\u9428\u9429\u942A\u942B\u942C"+ + "\u942D\u942E\u7EAD\u7EB0\u7EBE\u7EC0\u7EC1\u7EC2"+ + "\u7EC9\u7ECB\u7ECC\u7ED0\u7ED4\u7ED7\u7EDB\u7EE0"+ + "\u7EE1\u7EE8\u7EEB\u7EEE\u7EEF\u7EF1\u7EF2\u7F0D"+ + "\u7EF6\u7EFA\u7EFB\u7EFE\u7F01\u7F02\u7F03\u7F07"+ + "\u7F08\u7F0B\u7F0C\u7F0F\u7F11\u7F12\u7F17\u7F19"+ + "\u7F1C\u7F1B\u7F1F\u7F21\u7F22\u7F23\u7F24\u7F25"+ + "\u7F26\u7F27\u7F2A\u7F2B\u7F2C\u7F2D\u7F2F\u7F30"+ + "\u7F31\u7F32\u7F33\u7F35\u5E7A\u757F\u5DDB\u753E"+ + "\u9095\u738E\u7391\u73AE\u73A2\u739F\u73CF\u73C2"+ + "\u73D1\u73B7\u73B3\u73C0\u73C9\u73C8\u73E5\u73D9"+ + "\u987C\u740A\u73E9\u73E7\u73DE\u73BA\u73F2\u740F"+ + "\u742A\u745B\u7426\u7425\u7428\u7430\u742E\u742C"+ + "\u942F\u9430\u9431\u9432\u9433\u9434\u9435\u9436"+ + "\u9437\u9438\u9439\u943A\u943B\u943C\u943D\u943F"+ + "\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447"+ + "\u9448\u9449\u944A\u944B\u944C\u944D\u944E\u944F"+ + "\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457"+ + "\u9458\u9459\u945A\u945B\u945C\u945D\u945E\u945F"+ + "\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467"+ + "\u9468\u9469\u946A\u946C\u946D\u946E\u946F\uFFFD"+ + "\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477"+ + "\u9478\u9479\u947A\u947B\u947C\u947D\u947E\u947F"+ + "\u9480\u9481\u9482\u9483\u9484\u9491\u9496\u9498"+ + "\u94C7\u94CF\u94D3\u94D4\u94DA\u94E6\u94FB\u951C"+ + "\u9520\u741B\u741A\u7441\u745C\u7457\u7455\u7459"+ + "\u7477\u746D\u747E\u749C\u748E\u7480\u7481\u7487"+ + "\u748B\u749E\u74A8\u74A9\u7490\u74A7\u74D2\u74BA"+ + "\u97EA\u97EB\u97EC\u674C\u6753\u675E\u6748\u6769"+ + "\u67A5\u6787\u676A\u6773\u6798\u67A7\u6775\u67A8"+ + "\u679E\u67AD\u678B\u6777\u677C\u67F0\u6809\u67D8"+ + "\u680A\u67E9\u67B0\u680C\u67D9\u67B5\u67DA\u67B3"+ + "\u67DD\u6800\u67C3\u67B8\u67E2\u680E\u67C1\u67FD"+ + "\u6832\u6833\u6860\u6861\u684E\u6862\u6844\u6864"+ + "\u6883\u681D\u6855\u6866\u6841\u6867\u6840\u683E"+ + "\u684A\u6849\u6829\u68B5\u688F\u6874\u6877\u6893"+ + "\u686B\u68C2\u696E\u68FC\u691F\u6920\u68F9\u9527"+ + "\u9533\u953D\u9543\u9548\u954B\u9555\u955A\u9560"+ + "\u956E\u9574\u9575\u9577\u9578\u9579\u957A\u957B"+ + "\u957C\u957D\u957E\u9580\u9581\u9582\u9583\u9584"+ + "\u9585\u9586\u9587\u9588\u9589\u958A\u958B\u958C"+ + "\u958D\u958E\u958F\u9590\u9591\u9592\u9593\u9594"+ + "\u9595\u9596\u9597\u9598\u9599\u959A\u959B\u959C"+ + "\u959D\u959E\u959F\u95A0\u95A1\u95A2\u95A3\u95A4"+ + "\u95A5\u95A6\u95A7\u95A8\u95A9\u95AA\uFFFD\u95AB"+ + "\u95AC\u95AD\u95AE\u95AF\u95B0\u95B1\u95B2\u95B3"+ + "\u95B4\u95B5\u95B6\u95B7\u95B8\u95B9\u95BA\u95BB"+ + "\u95BC\u95BD\u95BE\u95BF\u95C0\u95C1\u95C2\u95C3"+ + "\u95C4\u95C5\u95C6\u95C7\u95C8\u95C9\u95CA\u95CB"+ + "\u6924\u68F0\u690B\u6901\u6957\u68E3\u6910\u6971"+ + "\u6939\u6960\u6942\u695D\u6984\u696B\u6980\u6998"+ + "\u6978\u6934\u69CC\u6987\u6988\u69CE\u6989\u6966"+ + "\u6963\u6979\u699B\u69A7\u69BB\u69AB\u69AD\u69D4"+ + "\u69B1\u69C1\u69CA\u69DF\u6995\u69E0\u698D\u69FF"+ + "\u6A2F\u69ED\u6A17\u6A18\u6A65\u69F2\u6A44\u6A3E"+ + "\u6AA0\u6A50\u6A5B\u6A35\u6A8E\u6A79\u6A3D\u6A28"+ + "\u6A58\u6A7C\u6A91\u6A90\u6AA9\u6A97\u6AAB\u7337"+ + "\u7352\u6B81\u6B82\u6B87\u6B84\u6B92\u6B93\u6B8D"+ + "\u6B9A\u6B9B\u6BA1\u6BAA\u8F6B\u8F6D\u8F71\u8F72"+ + "\u8F73\u8F75\u8F76\u8F78\u8F77\u8F79\u8F7A\u8F7C"+ + "\u8F7E\u8F81\u8F82\u8F84\u8F87\u8F8B\u95CC\u95CD"+ + "\u95CE\u95CF\u95D0\u95D1\u95D2\u95D3\u95D4\u95D5"+ + "\u95D6\u95D7\u95D8\u95D9\u95DA\u95DB\u95DC\u95DD"+ + "\u95DE\u95DF\u95E0\u95E1\u95E2\u95E3\u95E4\u95E5"+ + "\u95E6\u95E7\u95EC\u95FF\u9607\u9613\u9618\u961B"+ + "\u961E\u9620\u9623\u9624\u9625\u9626\u9627\u9628"+ + "\u9629\u962B\u962C\u962D\u962F\u9630\u9637\u9638"+ + "\u9639\u963A\u963E\u9641\u9643\u964A\u964E\u964F"+ + "\u9651\u9652\u9653\u9656\u9657\uFFFD\u9658\u9659"+ + "\u965A\u965C\u965D\u965E\u9660\u9663\u9665\u9666"+ + "\u966B\u966D\u966E\u966F\u9670\u9671\u9673\u9678"+ + "\u9679\u967A\u967B\u967C\u967D\u967E\u967F\u9680"+ + "\u9681\u9682\u9683\u9684\u9687\u9689\u968A\u8F8D"+ + "\u8F8E\u8F8F\u8F98\u8F9A\u8ECE\u620B\u6217\u621B"+ + "\u621F\u6222\u6221\u6225\u6224\u622C\u81E7\u74EF"+ + "\u74F4\u74FF\u750F\u7511\u7513\u6534\u65EE\u65EF"+ + "\u65F0\u660A\u6619\u6772\u6603\u6615\u6600\u7085"+ + "\u66F7\u661D\u6634\u6631\u6636\u6635\u8006\u665F"+ + "\u6654\u6641\u664F\u6656\u6661\u6657\u6677\u6684"+ + "\u668C\u66A7\u669D\u66BE\u66DB\u66DC\u66E6\u66E9"+ + "\u8D32\u8D33\u8D36\u8D3B\u8D3D\u8D40\u8D45\u8D46"+ + "\u8D48\u8D49\u8D47\u8D4D\u8D55\u8D59\u89C7\u89CA"+ + "\u89CB\u89CC\u89CE\u89CF\u89D0\u89D1\u726E\u729F"+ + "\u725D\u7266\u726F\u727E\u727F\u7284\u728B\u728D"+ + "\u728F\u7292\u6308\u6332\u63B0\u968C\u968E\u9691"+ + "\u9692\u9693\u9695\u9696\u969A\u969B\u969D\u969E"+ + "\u969F\u96A0\u96A1\u96A2\u96A3\u96A4\u96A5\u96A6"+ + "\u96A8\u96A9\u96AA\u96AB\u96AC\u96AD\u96AE\u96AF"+ + "\u96B1\u96B2\u96B4\u96B5\u96B7\u96B8\u96BA\u96BB"+ + "\u96BF\u96C2\u96C3\u96C8\u96CA\u96CB\u96D0\u96D1"+ + "\u96D3\u96D4\u96D6\u96D7\u96D8\u96D9\u96DA\u96DB"+ + "\u96DC\u96DD\u96DE\u96DF\u96E1\u96E2\u96E3\u96E4"+ + "\u96E5\u96E6\u96E7\u96EB\uFFFD\u96EC\u96ED\u96EE"+ + "\u96F0\u96F1\u96F2\u96F4\u96F5\u96F8\u96FA\u96FB"+ + "\u96FC\u96FD\u96FF\u9702\u9703\u9705\u970A\u970B"+ + "\u970C\u9710\u9711\u9712\u9714\u9715\u9717\u9718"+ + "\u9719\u971A\u971B\u971D\u971F\u9720\u643F\u64D8"+ + "\u8004\u6BEA\u6BF3\u6BFD\u6BF5\u6BF9\u6C05\u6C07"+ + "\u6C06\u6C0D\u6C15\u6C18\u6C19\u6C1A\u6C21\u6C29"+ + "\u6C24\u6C2A\u6C32\u6535\u6555\u656B\u724D\u7252"+ + "\u7256\u7230\u8662\u5216\u809F\u809C\u8093\u80BC"+ + "\u670A\u80BD\u80B1\u80AB\u80AD\u80B4\u80B7\u80E7"+ + "\u80E8\u80E9\u80EA\u80DB\u80C2\u80C4\u80D9\u80CD"+ + "\u80D7\u6710\u80DD\u80EB\u80F1\u80F4\u80ED\u810D"+ + "\u810E\u80F2\u80FC\u6715\u8112\u8C5A\u8136\u811E"+ + "\u812C\u8118\u8132\u8148\u814C\u8153\u8174\u8159"+ + "\u815A\u8171\u8160\u8169\u817C\u817D\u816D\u8167"+ + "\u584D\u5AB5\u8188\u8182\u8191\u6ED5\u81A3\u81AA"+ + "\u81CC\u6726\u81CA\u81BB\u9721\u9722\u9723\u9724"+ + "\u9725\u9726\u9727\u9728\u9729\u972B\u972C\u972E"+ + "\u972F\u9731\u9733\u9734\u9735\u9736\u9737\u973A"+ + "\u973B\u973C\u973D\u973F\u9740\u9741\u9742\u9743"+ + "\u9744\u9745\u9746\u9747\u9748\u9749\u974A\u974B"+ + "\u974C\u974D\u974E\u974F\u9750\u9751\u9754\u9755"+ + "\u9757\u9758\u975A\u975C\u975D\u975F\u9763\u9764"+ + "\u9766\u9767\u9768\u976A\u976B\u976C\u976D\u976E"+ + "\u976F\u9770\u9771\uFFFD\u9772\u9775\u9777\u9778"+ + "\u9779\u977A\u977B\u977D\u977E\u977F\u9780\u9781"+ + "\u9782\u9783\u9784\u9786\u9787\u9788\u9789\u978A"+ + "\u978C\u978E\u978F\u9790\u9793\u9795\u9796\u9797"+ + "\u9799\u979A\u979B\u979C\u979D\u81C1\u81A6\u6B24"+ + "\u6B37\u6B39\u6B43\u6B46\u6B59\u98D1\u98D2\u98D3"+ + "\u98D5\u98D9\u98DA\u6BB3\u5F40\u6BC2\u89F3\u6590"+ + "\u9F51\u6593\u65BC\u65C6\u65C4\u65C3\u65CC\u65CE"+ + "\u65D2\u65D6\u7080\u709C\u7096\u709D\u70BB\u70C0"+ + "\u70B7\u70AB\u70B1\u70E8\u70CA\u7110\u7113\u7116"+ + "\u712F\u7131\u7173\u715C\u7168\u7145\u7172\u714A"+ + "\u7178\u717A\u7198\u71B3\u71B5\u71A8\u71A0\u71E0"+ + "\u71D4\u71E7\u71F9\u721D\u7228\u706C\u7118\u7166"+ + "\u71B9\u623E\u623D\u6243\u6248\u6249\u793B\u7940"+ + "\u7946\u7949\u795B\u795C\u7953\u795A\u7962\u7957"+ + "\u7960\u796F\u7967\u797A\u7985\u798A\u799A\u79A7"+ + "\u79B3\u5FD1\u5FD0\u979E\u979F\u97A1\u97A2\u97A4"+ + "\u97A5\u97A6\u97A7\u97A8\u97A9\u97AA\u97AC\u97AE"+ + "\u97B0\u97B1\u97B3\u97B5\u97B6\u97B7\u97B8\u97B9"+ + "\u97BA\u97BB\u97BC\u97BD\u97BE\u97BF\u97C0\u97C1"+ + "\u97C2\u97C3\u97C4\u97C5\u97C6\u97C7\u97C8\u97C9"+ + "\u97CA\u97CB\u97CC\u97CD\u97CE\u97CF\u97D0\u97D1"+ + "\u97D2\u97D3\u97D4\u97D5\u97D6\u97D7\u97D8\u97D9"+ + "\u97DA\u97DB\u97DC\u97DD\u97DE\u97DF\u97E0\u97E1"+ + "\u97E2\u97E3\uFFFD\u97E4\u97E5\u97E8\u97EE\u97EF"+ + "\u97F0\u97F1\u97F2\u97F4\u97F7\u97F8\u97F9\u97FA"+ + "\u97FB\u97FC\u97FD\u97FE\u97FF\u9800\u9801\u9802"+ + "\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980A"+ + "\u980B\u980C\u980D\u980E\u603C\u605D\u605A\u6067"+ + "\u6041\u6059\u6063\u60AB\u6106\u610D\u615D\u61A9"+ + "\u619D\u61CB\u61D1\u6206\u8080\u807F\u6C93\u6CF6"+ + "\u6DFC\u77F6\u77F8\u7800\u7809\u7817\u7818\u7811"+ + "\u65AB\u782D\u781C\u781D\u7839\u783A\u783B\u781F"+ + "\u783C\u7825\u782C\u7823\u7829\u784E\u786D\u7856"+ + "\u7857\u7826\u7850\u7847\u784C\u786A\u789B\u7893"+ + "\u789A\u7887\u789C\u78A1\u78A3\u78B2\u78B9\u78A5"+ + "\u78D4\u78D9\u78C9\u78EC\u78F2\u7905\u78F4\u7913"+ + "\u7924\u791E\u7934\u9F9B\u9EF9\u9EFB\u9EFC\u76F1"+ + "\u7704\u770D\u76F9\u7707\u7708\u771A\u7722\u7719"+ + "\u772D\u7726\u7735\u7738\u7750\u7751\u7747\u7743"+ + "\u775A\u7768\u980F\u9810\u9811\u9812\u9813\u9814"+ + "\u9815\u9816\u9817\u9818\u9819\u981A\u981B\u981C"+ + "\u981D\u981E\u981F\u9820\u9821\u9822\u9823\u9824"+ + "\u9825\u9826\u9827\u9828\u9829\u982A\u982B\u982C"+ + "\u982D\u982E\u982F\u9830\u9831\u9832\u9833\u9834"+ + "\u9835\u9836\u9837\u9838\u9839\u983A\u983B\u983C"+ + "\u983D\u983E\u983F\u9840\u9841\u9842\u9843\u9844"+ + "\u9845\u9846\u9847\u9848\u9849\u984A\u984B\u984C"+ + "\u984D\uFFFD\u984E\u984F\u9850\u9851\u9852\u9853"+ + "\u9854\u9855\u9856\u9857\u9858\u9859\u985A\u985B"+ + "\u985C\u985D\u985E\u985F\u9860\u9861\u9862\u9863"+ + "\u9864\u9865\u9866\u9867\u9868\u9869\u986A\u986B"+ + "\u986C\u986D\u986E\u7762\u7765\u777F\u778D\u777D"+ + "\u7780\u778C\u7791\u779F\u77A0\u77B0\u77B5\u77BD"+ + "\u753A\u7540\u754E\u754B\u7548\u755B\u7572\u7579"+ + "\u7583\u7F58\u7F61\u7F5F\u8A48\u7F68\u7F74\u7F71"+ + "\u7F79\u7F81\u7F7E\u76CD\u76E5\u8832\u9485\u9486"+ + "\u9487\u948B\u948A\u948C\u948D\u948F\u9490\u9494"+ + "\u9497\u9495\u949A\u949B\u949C\u94A3\u94A4\u94AB"+ + "\u94AA\u94AD\u94AC\u94AF\u94B0\u94B2\u94B4\u94B6"+ + "\u94B7\u94B8\u94B9\u94BA\u94BC\u94BD\u94BF\u94C4"+ + "\u94C8\u94C9\u94CA\u94CB\u94CC\u94CD\u94CE\u94D0"+ + "\u94D1\u94D2\u94D5\u94D6\u94D7\u94D9\u94D8\u94DB"+ + "\u94DE\u94DF\u94E0\u94E2\u94E4\u94E5\u94E7\u94E8"+ + "\u94EA\u986F\u9870\u9871\u9872\u9873\u9874\u988B"+ + "\u988E\u9892\u9895\u9899\u98A3\u98A8\u98A9\u98AA"+ + "\u98AB\u98AC\u98AD\u98AE\u98AF\u98B0\u98B1\u98B2"+ + "\u98B3\u98B4\u98B5\u98B6\u98B7\u98B8\u98B9\u98BA"+ + "\u98BB\u98BC\u98BD\u98BE\u98BF\u98C0\u98C1\u98C2"+ + "\u98C3\u98C4\u98C5\u98C6\u98C7\u98C8\u98C9\u98CA"+ + "\u98CB\u98CC\u98CD\u98CF\u98D0\u98D4\u98D6\u98D7"+ + "\u98DB\u98DC\u98DD\u98E0\u98E1\u98E2\u98E3\u98E4"+ + "\uFFFD\u98E5\u98E6\u98E9\u98EA\u98EB\u98EC\u98ED"+ + "\u98EE\u98EF\u98F0\u98F1\u98F2\u98F3\u98F4\u98F5"+ + "\u98F6\u98F7\u98F8\u98F9\u98FA\u98FB\u98FC\u98FD"+ + "\u98FE\u98FF\u9900\u9901\u9902\u9903\u9904\u9905"+ + "\u9906\u9907\u94E9\u94EB\u94EE\u94EF\u94F3\u94F4"+ + "\u94F5\u94F7\u94F9\u94FC\u94FD\u94FF\u9503\u9502"+ + "\u9506\u9507\u9509\u950A\u950D\u950E\u950F\u9512"+ + "\u9513\u9514\u9515\u9516\u9518\u951B\u951D\u951E"+ + "\u951F\u9522\u952A\u952B\u9529\u952C\u9531\u9532"+ + "\u9534\u9536\u9537\u9538\u953C\u953E\u953F\u9542"+ + "\u9535\u9544\u9545\u9546\u9549\u954C\u954E\u954F"+ + "\u9552\u9553\u9554\u9556\u9557\u9558\u9559\u955B"+ + "\u955E\u955F\u955D\u9561\u9562\u9564\u9565\u9566"+ + "\u9567\u9568\u9569\u956A\u956B\u956C\u956F\u9571"+ + "\u9572\u9573\u953A\u77E7\u77EC\u96C9\u79D5\u79ED"+ + "\u79E3\u79EB\u7A06\u5D47\u7A03\u7A02\u7A1E\u7A14"; + + private final static String innerIndex7= + "\u9908\u9909\u990A\u990B\u990C\u990E\u990F\u9911"+ + "\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919"+ + "\u991A\u991B\u991C\u991D\u991E\u991F\u9920\u9921"+ + "\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929"+ + "\u992A\u992B\u992C\u992D\u992F\u9930\u9931\u9932"+ + "\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993A"+ + "\u993B\u993C\u993D\u993E\u993F\u9940\u9941\u9942"+ + "\u9943\u9944\u9945\u9946\u9947\u9948\u9949\uFFFD"+ + "\u994A\u994B\u994C\u994D\u994E\u994F\u9950\u9951"+ + "\u9952\u9953\u9956\u9957\u9958\u9959\u995A\u995B"+ + "\u995C\u995D\u995E\u995F\u9960\u9961\u9962\u9964"+ + "\u9966\u9973\u9978\u9979\u997B\u997E\u9982\u9983"+ + "\u9989\u7A39\u7A37\u7A51\u9ECF\u99A5\u7A70\u7688"+ + "\u768E\u7693\u7699\u76A4\u74DE\u74E0\u752C\u9E20"+ + "\u9E22\u9E28\u9E29\u9E2A\u9E2B\u9E2C\u9E32\u9E31"+ + "\u9E36\u9E38\u9E37\u9E39\u9E3A\u9E3E\u9E41\u9E42"+ + "\u9E44\u9E46\u9E47\u9E48\u9E49\u9E4B\u9E4C\u9E4E"+ + "\u9E51\u9E55\u9E57\u9E5A\u9E5B\u9E5C\u9E5E\u9E63"+ + "\u9E66\u9E67\u9E68\u9E69\u9E6A\u9E6B\u9E6C\u9E71"+ + "\u9E6D\u9E73\u7592\u7594\u7596\u75A0\u759D\u75AC"+ + "\u75A3\u75B3\u75B4\u75B8\u75C4\u75B1\u75B0\u75C3"+ + "\u75C2\u75D6\u75CD\u75E3\u75E8\u75E6\u75E4\u75EB"+ + "\u75E7\u7603\u75F1\u75FC\u75FF\u7610\u7600\u7605"+ + "\u760C\u7617\u760A\u7625\u7618\u7615\u7619\u998C"+ + "\u998E\u999A\u999B\u999C\u999D\u999E\u999F\u99A0"+ + "\u99A1\u99A2\u99A3\u99A4\u99A6\u99A7\u99A9\u99AA"+ + "\u99AB\u99AC\u99AD\u99AE\u99AF\u99B0\u99B1\u99B2"+ + "\u99B3\u99B4\u99B5\u99B6\u99B7\u99B8\u99B9\u99BA"+ + "\u99BB\u99BC\u99BD\u99BE\u99BF\u99C0\u99C1\u99C2"+ + "\u99C3\u99C4\u99C5\u99C6\u99C7\u99C8\u99C9\u99CA"+ + "\u99CB\u99CC\u99CD\u99CE\u99CF\u99D0\u99D1\u99D2"+ + "\u99D3\u99D4\u99D5\u99D6\u99D7\u99D8\uFFFD\u99D9"+ + "\u99DA\u99DB\u99DC\u99DD\u99DE\u99DF\u99E0\u99E1"+ + "\u99E2\u99E3\u99E4\u99E5\u99E6\u99E7\u99E8\u99E9"+ + "\u99EA\u99EB\u99EC\u99ED\u99EE\u99EF\u99F0\u99F1"+ + "\u99F2\u99F3\u99F4\u99F5\u99F6\u99F7\u99F8\u99F9"+ + "\u761B\u763C\u7622\u7620\u7640\u762D\u7630\u763F"+ + "\u7635\u7643\u763E\u7633\u764D\u765E\u7654\u765C"+ + "\u7656\u766B\u766F\u7FCA\u7AE6\u7A78\u7A79\u7A80"+ + "\u7A86\u7A88\u7A95\u7AA6\u7AA0\u7AAC\u7AA8\u7AAD"+ + "\u7AB3\u8864\u8869\u8872\u887D\u887F\u8882\u88A2"+ + "\u88C6\u88B7\u88BC\u88C9\u88E2\u88CE\u88E3\u88E5"+ + "\u88F1\u891A\u88FC\u88E8\u88FE\u88F0\u8921\u8919"+ + "\u8913\u891B\u890A\u8934\u892B\u8936\u8941\u8966"+ + "\u897B\u758B\u80E5\u76B2\u76B4\u77DC\u8012\u8014"+ + "\u8016\u801C\u8020\u8022\u8025\u8026\u8027\u8029"+ + "\u8028\u8031\u800B\u8035\u8043\u8046\u804D\u8052"+ + "\u8069\u8071\u8983\u9878\u9880\u9883\u99FA\u99FB"+ + "\u99FC\u99FD\u99FE\u99FF\u9A00\u9A01\u9A02\u9A03"+ + "\u9A04\u9A05\u9A06\u9A07\u9A08\u9A09\u9A0A\u9A0B"+ + "\u9A0C\u9A0D\u9A0E\u9A0F\u9A10\u9A11\u9A12\u9A13"+ + "\u9A14\u9A15\u9A16\u9A17\u9A18\u9A19\u9A1A\u9A1B"+ + "\u9A1C\u9A1D\u9A1E\u9A1F\u9A20\u9A21\u9A22\u9A23"+ + "\u9A24\u9A25\u9A26\u9A27\u9A28\u9A29\u9A2A\u9A2B"+ + "\u9A2C\u9A2D\u9A2E\u9A2F\u9A30\u9A31\u9A32\u9A33"+ + "\u9A34\u9A35\u9A36\u9A37\u9A38\uFFFD\u9A39\u9A3A"+ + "\u9A3B\u9A3C\u9A3D\u9A3E\u9A3F\u9A40\u9A41\u9A42"+ + "\u9A43\u9A44\u9A45\u9A46\u9A47\u9A48\u9A49\u9A4A"+ + "\u9A4B\u9A4C\u9A4D\u9A4E\u9A4F\u9A50\u9A51\u9A52"+ + "\u9A53\u9A54\u9A55\u9A56\u9A57\u9A58\u9A59\u9889"+ + "\u988C\u988D\u988F\u9894\u989A\u989B\u989E\u989F"+ + "\u98A1\u98A2\u98A5\u98A6\u864D\u8654\u866C\u866E"+ + "\u867F\u867A\u867C\u867B\u86A8\u868D\u868B\u86AC"+ + "\u869D\u86A7\u86A3\u86AA\u8693\u86A9\u86B6\u86C4"+ + "\u86B5\u86CE\u86B0\u86BA\u86B1\u86AF\u86C9\u86CF"+ + "\u86B4\u86E9\u86F1\u86F2\u86ED\u86F3\u86D0\u8713"+ + "\u86DE\u86F4\u86DF\u86D8\u86D1\u8703\u8707\u86F8"+ + "\u8708\u870A\u870D\u8709\u8723\u873B\u871E\u8725"+ + "\u872E\u871A\u873E\u8748\u8734\u8731\u8729\u8737"+ + "\u873F\u8782\u8722\u877D\u877E\u877B\u8760\u8770"+ + "\u874C\u876E\u878B\u8753\u8763\u877C\u8764\u8759"+ + "\u8765\u8793\u87AF\u87A8\u87D2\u9A5A\u9A5B\u9A5C"+ + "\u9A5D\u9A5E\u9A5F\u9A60\u9A61\u9A62\u9A63\u9A64"+ + "\u9A65\u9A66\u9A67\u9A68\u9A69\u9A6A\u9A6B\u9A72"+ + "\u9A83\u9A89\u9A8D\u9A8E\u9A94\u9A95\u9A99\u9AA6"+ + "\u9AA9\u9AAA\u9AAB\u9AAC\u9AAD\u9AAE\u9AAF\u9AB2"+ + "\u9AB3\u9AB4\u9AB5\u9AB9\u9ABB\u9ABD\u9ABE\u9ABF"+ + "\u9AC3\u9AC4\u9AC6\u9AC7\u9AC8\u9AC9\u9ACA\u9ACD"+ + "\u9ACE\u9ACF\u9AD0\u9AD2\u9AD4\u9AD5\u9AD6\u9AD7"+ + "\u9AD9\u9ADA\u9ADB\u9ADC\uFFFD\u9ADD\u9ADE\u9AE0"+ + "\u9AE2\u9AE3\u9AE4\u9AE5\u9AE7\u9AE8\u9AE9\u9AEA"+ + "\u9AEC\u9AEE\u9AF0\u9AF1\u9AF2\u9AF3\u9AF4\u9AF5"+ + "\u9AF6\u9AF7\u9AF8\u9AFA\u9AFC\u9AFD\u9AFE\u9AFF"+ + "\u9B00\u9B01\u9B02\u9B04\u9B05\u9B06\u87C6\u8788"+ + "\u8785\u87AD\u8797\u8783\u87AB\u87E5\u87AC\u87B5"+ + "\u87B3\u87CB\u87D3\u87BD\u87D1\u87C0\u87CA\u87DB"+ + "\u87EA\u87E0\u87EE\u8816\u8813\u87FE\u880A\u881B"+ + "\u8821\u8839\u883C\u7F36\u7F42\u7F44\u7F45\u8210"+ + "\u7AFA\u7AFD\u7B08\u7B03\u7B04\u7B15\u7B0A\u7B2B"+ + "\u7B0F\u7B47\u7B38\u7B2A\u7B19\u7B2E\u7B31\u7B20"+ + "\u7B25\u7B24\u7B33\u7B3E\u7B1E\u7B58\u7B5A\u7B45"+ + "\u7B75\u7B4C\u7B5D\u7B60\u7B6E\u7B7B\u7B62\u7B72"+ + "\u7B71\u7B90\u7BA6\u7BA7\u7BB8\u7BAC\u7B9D\u7BA8"+ + "\u7B85\u7BAA\u7B9C\u7BA2\u7BAB\u7BB4\u7BD1\u7BC1"+ + "\u7BCC\u7BDD\u7BDA\u7BE5\u7BE6\u7BEA\u7C0C\u7BFE"+ + "\u7BFC\u7C0F\u7C16\u7C0B\u9B07\u9B09\u9B0A\u9B0B"+ + "\u9B0C\u9B0D\u9B0E\u9B10\u9B11\u9B12\u9B14\u9B15"+ + "\u9B16\u9B17\u9B18\u9B19\u9B1A\u9B1B\u9B1C\u9B1D"+ + "\u9B1E\u9B20\u9B21\u9B22\u9B24\u9B25\u9B26\u9B27"+ + "\u9B28\u9B29\u9B2A\u9B2B\u9B2C\u9B2D\u9B2E\u9B30"+ + "\u9B31\u9B33\u9B34\u9B35\u9B36\u9B37\u9B38\u9B39"+ + "\u9B3A\u9B3D\u9B3E\u9B3F\u9B40\u9B46\u9B4A\u9B4B"+ + "\u9B4C\u9B4E\u9B50\u9B52\u9B53\u9B55\u9B56\u9B57"+ + "\u9B58\u9B59\u9B5A\uFFFD\u9B5B\u9B5C\u9B5D\u9B5E"+ + "\u9B5F\u9B60\u9B61\u9B62\u9B63\u9B64\u9B65\u9B66"+ + "\u9B67\u9B68\u9B69\u9B6A\u9B6B\u9B6C\u9B6D\u9B6E"+ + "\u9B6F\u9B70\u9B71\u9B72\u9B73\u9B74\u9B75\u9B76"+ + "\u9B77\u9B78\u9B79\u9B7A\u9B7B\u7C1F\u7C2A\u7C26"+ + "\u7C38\u7C41\u7C40\u81FE\u8201\u8202\u8204\u81EC"+ + "\u8844\u8221\u8222\u8223\u822D\u822F\u8228\u822B"+ + "\u8238\u823B\u8233\u8234\u823E\u8244\u8249\u824B"+ + "\u824F\u825A\u825F\u8268\u887E\u8885\u8888\u88D8"+ + "\u88DF\u895E\u7F9D\u7F9F\u7FA7\u7FAF\u7FB0\u7FB2"+ + "\u7C7C\u6549\u7C91\u7C9D\u7C9C\u7C9E\u7CA2\u7CB2"+ + "\u7CBC\u7CBD\u7CC1\u7CC7\u7CCC\u7CCD\u7CC8\u7CC5"+ + "\u7CD7\u7CE8\u826E\u66A8\u7FBF\u7FCE\u7FD5\u7FE5"+ + "\u7FE1\u7FE6\u7FE9\u7FEE\u7FF3\u7CF8\u7D77\u7DA6"+ + "\u7DAE\u7E47\u7E9B\u9EB8\u9EB4\u8D73\u8D84\u8D94"+ + "\u8D91\u8DB1\u8D67\u8D6D\u8C47\u8C49\u914A\u9150"+ + "\u914E\u914F\u9164\u9B7C\u9B7D\u9B7E\u9B7F\u9B80"+ + "\u9B81\u9B82\u9B83\u9B84\u9B85\u9B86\u9B87\u9B88"+ + "\u9B89\u9B8A\u9B8B\u9B8C\u9B8D\u9B8E\u9B8F\u9B90"+ + "\u9B91\u9B92\u9B93\u9B94\u9B95\u9B96\u9B97\u9B98"+ + "\u9B99\u9B9A\u9B9B\u9B9C\u9B9D\u9B9E\u9B9F\u9BA0"+ + "\u9BA1\u9BA2\u9BA3\u9BA4\u9BA5\u9BA6\u9BA7\u9BA8"+ + "\u9BA9\u9BAA\u9BAB\u9BAC\u9BAD\u9BAE\u9BAF\u9BB0"+ + "\u9BB1\u9BB2\u9BB3\u9BB4\u9BB5\u9BB6\u9BB7\u9BB8"+ + "\u9BB9\u9BBA\uFFFD\u9BBB\u9BBC\u9BBD\u9BBE\u9BBF"+ + "\u9BC0\u9BC1\u9BC2\u9BC3\u9BC4\u9BC5\u9BC6\u9BC7"+ + "\u9BC8\u9BC9\u9BCA\u9BCB\u9BCC\u9BCD\u9BCE\u9BCF"+ + "\u9BD0\u9BD1\u9BD2\u9BD3\u9BD4\u9BD5\u9BD6\u9BD7"+ + "\u9BD8\u9BD9\u9BDA\u9BDB\u9162\u9161\u9170\u9169"+ + "\u916F\u917D\u917E\u9172\u9174\u9179\u918C\u9185"+ + "\u9190\u918D\u9191\u91A2\u91A3\u91AA\u91AD\u91AE"+ + "\u91AF\u91B5\u91B4\u91BA\u8C55\u9E7E\u8DB8\u8DEB"+ + "\u8E05\u8E59\u8E69\u8DB5\u8DBF\u8DBC\u8DBA\u8DC4"+ + "\u8DD6\u8DD7\u8DDA\u8DDE\u8DCE\u8DCF\u8DDB\u8DC6"+ + "\u8DEC\u8DF7\u8DF8\u8DE3\u8DF9\u8DFB\u8DE4\u8E09"+ + "\u8DFD\u8E14\u8E1D\u8E1F\u8E2C\u8E2E\u8E23\u8E2F"+ + "\u8E3A\u8E40\u8E39\u8E35\u8E3D\u8E31\u8E49\u8E41"+ + "\u8E42\u8E51\u8E52\u8E4A\u8E70\u8E76\u8E7C\u8E6F"+ + "\u8E74\u8E85\u8E8F\u8E94\u8E90\u8E9C\u8E9E\u8C78"+ + "\u8C82\u8C8A\u8C85\u8C98\u8C94\u659B\u89D6\u89DE"+ + "\u89DA\u89DC\u9BDC\u9BDD\u9BDE\u9BDF\u9BE0\u9BE1"+ + "\u9BE2\u9BE3\u9BE4\u9BE5\u9BE6\u9BE7\u9BE8\u9BE9"+ + "\u9BEA\u9BEB\u9BEC\u9BED\u9BEE\u9BEF\u9BF0\u9BF1"+ + "\u9BF2\u9BF3\u9BF4\u9BF5\u9BF6\u9BF7\u9BF8\u9BF9"+ + "\u9BFA\u9BFB\u9BFC\u9BFD\u9BFE\u9BFF\u9C00\u9C01"+ + "\u9C02\u9C03\u9C04\u9C05\u9C06\u9C07\u9C08\u9C09"+ + "\u9C0A\u9C0B\u9C0C\u9C0D\u9C0E\u9C0F\u9C10\u9C11"+ + "\u9C12\u9C13\u9C14\u9C15\u9C16\u9C17\u9C18\u9C19"+ + "\u9C1A\uFFFD\u9C1B\u9C1C\u9C1D\u9C1E\u9C1F\u9C20"+ + "\u9C21\u9C22\u9C23\u9C24\u9C25\u9C26\u9C27\u9C28"+ + "\u9C29\u9C2A\u9C2B\u9C2C\u9C2D\u9C2E\u9C2F\u9C30"+ + "\u9C31\u9C32\u9C33\u9C34\u9C35\u9C36\u9C37\u9C38"+ + "\u9C39\u9C3A\u9C3B\u89E5\u89EB\u89EF\u8A3E\u8B26"+ + "\u9753\u96E9\u96F3\u96EF\u9706\u9701\u9708\u970F"+ + "\u970E\u972A\u972D\u9730\u973E\u9F80\u9F83\u9F85"+ + "\u9F86\u9F87\u9F88\u9F89\u9F8A\u9F8C\u9EFE\u9F0B"+ + "\u9F0D\u96B9\u96BC\u96BD\u96CE\u96D2\u77BF\u96E0"+ + "\u928E\u92AE\u92C8\u933E\u936A\u93CA\u938F\u943E"+ + "\u946B\u9C7F\u9C82\u9C85\u9C86\u9C87\u9C88\u7A23"+ + "\u9C8B\u9C8E\u9C90\u9C91\u9C92\u9C94\u9C95\u9C9A"+ + "\u9C9B\u9C9E\u9C9F\u9CA0\u9CA1\u9CA2\u9CA3\u9CA5"+ + "\u9CA6\u9CA7\u9CA8\u9CA9\u9CAB\u9CAD\u9CAE\u9CB0"+ + "\u9CB1\u9CB2\u9CB3\u9CB4\u9CB5\u9CB6\u9CB7\u9CBA"+ + "\u9CBB\u9CBC\u9CBD\u9CC4\u9CC5\u9CC6\u9CC7\u9CCA"+ + "\u9CCB\u9C3C\u9C3D\u9C3E\u9C3F\u9C40\u9C41\u9C42"+ + "\u9C43\u9C44\u9C45\u9C46\u9C47\u9C48\u9C49\u9C4A"+ + "\u9C4B\u9C4C\u9C4D\u9C4E\u9C4F\u9C50\u9C51\u9C52"+ + "\u9C53\u9C54\u9C55\u9C56\u9C57\u9C58\u9C59\u9C5A"+ + "\u9C5B\u9C5C\u9C5D\u9C5E\u9C5F\u9C60\u9C61\u9C62"+ + "\u9C63\u9C64\u9C65\u9C66\u9C67\u9C68\u9C69\u9C6A"+ + "\u9C6B\u9C6C\u9C6D\u9C6E\u9C6F\u9C70\u9C71\u9C72"+ + "\u9C73\u9C74\u9C75\u9C76\u9C77\u9C78\u9C79\u9C7A"+ + "\uFFFD\u9C7B\u9C7D\u9C7E\u9C80\u9C83\u9C84\u9C89"+ + "\u9C8A\u9C8C\u9C8F\u9C93\u9C96\u9C97\u9C98\u9C99"+ + "\u9C9D\u9CAA\u9CAC\u9CAF\u9CB9\u9CBE\u9CBF\u9CC0"+ + "\u9CC1\u9CC2\u9CC8\u9CC9\u9CD1\u9CD2\u9CDA\u9CDB"+ + "\u9CE0\u9CE1\u9CCC\u9CCD\u9CCE\u9CCF\u9CD0\u9CD3"+ + "\u9CD4\u9CD5\u9CD7\u9CD8\u9CD9\u9CDC\u9CDD\u9CDF"+ + "\u9CE2\u977C\u9785\u9791\u9792\u9794\u97AF\u97AB"+ + "\u97A3\u97B2\u97B4\u9AB1\u9AB0\u9AB7\u9E58\u9AB6"+ + "\u9ABA\u9ABC\u9AC1\u9AC0\u9AC5\u9AC2\u9ACB\u9ACC"+ + "\u9AD1\u9B45\u9B43\u9B47\u9B49\u9B48\u9B4D\u9B51"+ + "\u98E8\u990D\u992E\u9955\u9954\u9ADF\u9AE1\u9AE6"+ + "\u9AEF\u9AEB\u9AFB\u9AED\u9AF9\u9B08\u9B0F\u9B13"+ + "\u9B1F\u9B23\u9EBD\u9EBE\u7E3B\u9E82\u9E87\u9E88"+ + "\u9E8B\u9E92\u93D6\u9E9D\u9E9F\u9EDB\u9EDC\u9EDD"+ + "\u9EE0\u9EDF\u9EE2\u9EE9\u9EE7\u9EE5\u9EEA\u9EEF"+ + "\u9F22\u9F2C\u9F2F\u9F39\u9F37\u9F3D\u9F3E\u9F44"+ + "\u9CE3\u9CE4\u9CE5\u9CE6\u9CE7\u9CE8\u9CE9\u9CEA"+ + "\u9CEB\u9CEC\u9CED\u9CEE\u9CEF\u9CF0\u9CF1\u9CF2"+ + "\u9CF3\u9CF4\u9CF5\u9CF6\u9CF7\u9CF8\u9CF9\u9CFA"+ + "\u9CFB\u9CFC\u9CFD\u9CFE\u9CFF\u9D00\u9D01\u9D02"+ + "\u9D03\u9D04\u9D05\u9D06\u9D07\u9D08\u9D09\u9D0A"+ + "\u9D0B\u9D0C\u9D0D\u9D0E\u9D0F\u9D10\u9D11\u9D12"+ + "\u9D13\u9D14\u9D15\u9D16\u9D17\u9D18\u9D19\u9D1A"+ + "\u9D1B\u9D1C\u9D1D\u9D1E\u9D1F\u9D20\u9D21\uFFFD"+ + "\u9D22\u9D23\u9D24\u9D25\u9D26\u9D27\u9D28\u9D29"+ + "\u9D2A\u9D2B\u9D2C\u9D2D\u9D2E\u9D2F\u9D30\u9D31"+ + "\u9D32\u9D33\u9D34\u9D35\u9D36\u9D37\u9D38\u9D39"+ + "\u9D3A\u9D3B\u9D3C\u9D3D\u9D3E\u9D3F\u9D40\u9D41"+ + "\u9D42\uE234\uE235\uE236\uE237\uE238\uE239\uE23A"+ + "\uE23B\uE23C\uE23D\uE23E\uE23F\uE240\uE241\uE242"+ + "\uE243\uE244\uE245\uE246\uE247\uE248\uE249\uE24A"+ + "\uE24B\uE24C\uE24D\uE24E\uE24F\uE250\uE251\uE252"+ + "\uE253\uE254\uE255\uE256\uE257\uE258\uE259\uE25A"+ + "\uE25B\uE25C\uE25D\uE25E\uE25F\uE260\uE261\uE262"+ + "\uE263\uE264\uE265\uE266\uE267\uE268\uE269\uE26A"+ + "\uE26B\uE26C\uE26D\uE26E\uE26F\uE270\uE271\uE272"+ + "\uE273\uE274\uE275\uE276\uE277\uE278\uE279\uE27A"+ + "\uE27B\uE27C\uE27D\uE27E\uE27F\uE280\uE281\uE282"+ + "\uE283\uE284\uE285\uE286\uE287\uE288\uE289\uE28A"+ + "\uE28B\uE28C\uE28D\uE28E\uE28F\uE290\uE291\u9D43"+ + "\u9D44\u9D45\u9D46\u9D47\u9D48\u9D49\u9D4A\u9D4B"+ + "\u9D4C\u9D4D\u9D4E\u9D4F\u9D50\u9D51\u9D52\u9D53"+ + "\u9D54\u9D55\u9D56\u9D57\u9D58\u9D59\u9D5A\u9D5B"+ + "\u9D5C\u9D5D\u9D5E\u9D5F\u9D60\u9D61\u9D62\u9D63"+ + "\u9D64\u9D65\u9D66\u9D67\u9D68\u9D69\u9D6A\u9D6B"+ + "\u9D6C\u9D6D\u9D6E\u9D6F\u9D70\u9D71\u9D72\u9D73"+ + "\u9D74\u9D75\u9D76\u9D77\u9D78\u9D79\u9D7A\u9D7B"+ + "\u9D7C\u9D7D\u9D7E\u9D7F\u9D80\u9D81\uFFFD\u9D82"+ + "\u9D83\u9D84\u9D85\u9D86\u9D87\u9D88\u9D89\u9D8A"+ + "\u9D8B\u9D8C\u9D8D\u9D8E\u9D8F\u9D90\u9D91\u9D92"+ + "\u9D93\u9D94\u9D95\u9D96\u9D97\u9D98\u9D99\u9D9A"+ + "\u9D9B\u9D9C\u9D9D\u9D9E\u9D9F\u9DA0\u9DA1\u9DA2"+ + "\uE292\uE293\uE294\uE295\uE296\uE297\uE298\uE299"+ + "\uE29A\uE29B\uE29C\uE29D\uE29E\uE29F\uE2A0\uE2A1"+ + "\uE2A2\uE2A3\uE2A4\uE2A5\uE2A6\uE2A7\uE2A8\uE2A9"+ + "\uE2AA\uE2AB\uE2AC\uE2AD\uE2AE\uE2AF\uE2B0\uE2B1"+ + "\uE2B2\uE2B3\uE2B4\uE2B5\uE2B6\uE2B7\uE2B8\uE2B9"+ + "\uE2BA\uE2BB\uE2BC\uE2BD\uE2BE\uE2BF\uE2C0\uE2C1"+ + "\uE2C2\uE2C3\uE2C4\uE2C5\uE2C6\uE2C7\uE2C8\uE2C9"+ + "\uE2CA\uE2CB\uE2CC\uE2CD\uE2CE\uE2CF\uE2D0\uE2D1"+ + "\uE2D2\uE2D3\uE2D4\uE2D5\uE2D6\uE2D7\uE2D8\uE2D9"+ + "\uE2DA\uE2DB\uE2DC\uE2DD\uE2DE\uE2DF\uE2E0\uE2E1"+ + "\uE2E2\uE2E3\uE2E4\uE2E5\uE2E6\uE2E7\uE2E8\uE2E9"+ + "\uE2EA\uE2EB\uE2EC\uE2ED\uE2EE\uE2EF\u9DA3\u9DA4"+ + "\u9DA5\u9DA6\u9DA7\u9DA8\u9DA9\u9DAA\u9DAB\u9DAC"+ + "\u9DAD\u9DAE\u9DAF\u9DB0\u9DB1\u9DB2\u9DB3\u9DB4"+ + "\u9DB5\u9DB6\u9DB7\u9DB8\u9DB9\u9DBA\u9DBB\u9DBC"+ + "\u9DBD\u9DBE\u9DBF\u9DC0\u9DC1\u9DC2\u9DC3\u9DC4"+ + "\u9DC5\u9DC6\u9DC7\u9DC8\u9DC9\u9DCA\u9DCB\u9DCC"+ + "\u9DCD\u9DCE\u9DCF\u9DD0\u9DD1\u9DD2\u9DD3\u9DD4"+ + "\u9DD5\u9DD6\u9DD7\u9DD8\u9DD9\u9DDA\u9DDB\u9DDC"+ + "\u9DDD\u9DDE\u9DDF\u9DE0\u9DE1\uFFFD\u9DE2\u9DE3"+ + "\u9DE4\u9DE5\u9DE6\u9DE7\u9DE8\u9DE9\u9DEA\u9DEB"+ + "\u9DEC\u9DED\u9DEE\u9DEF\u9DF0\u9DF1\u9DF2\u9DF3"+ + "\u9DF4\u9DF5\u9DF6\u9DF7\u9DF8\u9DF9\u9DFA\u9DFB"+ + "\u9DFC\u9DFD\u9DFE\u9DFF\u9E00\u9E01\u9E02\uE2F0"+ + "\uE2F1\uE2F2\uE2F3\uE2F4\uE2F5\uE2F6\uE2F7\uE2F8"+ + "\uE2F9\uE2FA\uE2FB\uE2FC\uE2FD\uE2FE\uE2FF\uE300"+ + "\uE301\uE302\uE303\uE304\uE305\uE306\uE307\uE308"+ + "\uE309\uE30A\uE30B\uE30C\uE30D\uE30E\uE30F\uE310"+ + "\uE311\uE312\uE313\uE314\uE315\uE316\uE317\uE318"+ + "\uE319\uE31A\uE31B\uE31C\uE31D\uE31E\uE31F\uE320"+ + "\uE321\uE322\uE323\uE324\uE325\uE326\uE327\uE328"+ + "\uE329\uE32A\uE32B\uE32C\uE32D\uE32E\uE32F\uE330"+ + "\uE331\uE332\uE333\uE334\uE335\uE336\uE337\uE338"+ + "\uE339\uE33A\uE33B\uE33C\uE33D\uE33E\uE33F\uE340"+ + "\uE341\uE342\uE343\uE344\uE345\uE346\uE347\uE348"+ + "\uE349\uE34A\uE34B\uE34C\uE34D\u9E03\u9E04\u9E05"+ + "\u9E06\u9E07\u9E08\u9E09\u9E0A\u9E0B\u9E0C\u9E0D"+ + "\u9E0E\u9E0F\u9E10\u9E11\u9E12\u9E13\u9E14\u9E15"+ + "\u9E16\u9E17\u9E18\u9E19\u9E1A\u9E1B\u9E1C\u9E1D"+ + "\u9E1E\u9E24\u9E27\u9E2E\u9E30\u9E34\u9E3B\u9E3C"+ + "\u9E40\u9E4D\u9E50\u9E52\u9E53\u9E54\u9E56\u9E59"+ + "\u9E5D\u9E5F\u9E60\u9E61\u9E62\u9E65\u9E6E\u9E6F"+ + "\u9E72\u9E74\u9E75\u9E76\u9E77\u9E78\u9E79\u9E7A"+ + "\u9E7B\u9E7C\u9E7D\u9E80\uFFFD\u9E81\u9E83\u9E84"+ + "\u9E85\u9E86\u9E89\u9E8A\u9E8C\u9E8D\u9E8E\u9E8F"+ + "\u9E90\u9E91\u9E94\u9E95\u9E96\u9E97\u9E98\u9E99"+ + "\u9E9A\u9E9B\u9E9C\u9E9E\u9EA0\u9EA1\u9EA2\u9EA3"+ + "\u9EA4\u9EA5\u9EA7\u9EA8\u9EA9\u9EAA\uE34E\uE34F"+ + "\uE350\uE351\uE352\uE353\uE354\uE355\uE356\uE357"+ + "\uE358\uE359\uE35A\uE35B\uE35C\uE35D\uE35E\uE35F"+ + "\uE360\uE361\uE362\uE363\uE364\uE365\uE366\uE367"+ + "\uE368\uE369\uE36A\uE36B\uE36C\uE36D\uE36E\uE36F"+ + "\uE370\uE371\uE372\uE373\uE374\uE375\uE376\uE377"+ + "\uE378\uE379\uE37A\uE37B\uE37C\uE37D\uE37E\uE37F"+ + "\uE380\uE381\uE382\uE383\uE384\uE385\uE386\uE387"+ + "\uE388\uE389\uE38A\uE38B\uE38C\uE38D\uE38E\uE38F"+ + "\uE390\uE391\uE392\uE393\uE394\uE395\uE396\uE397"+ + "\uE398\uE399\uE39A\uE39B\uE39C\uE39D\uE39E\uE39F"+ + "\uE3A0\uE3A1\uE3A2\uE3A3\uE3A4\uE3A5\uE3A6\uE3A7"+ + "\uE3A8\uE3A9\uE3AA\uE3AB\u9EAB\u9EAC\u9EAD\u9EAE"+ + "\u9EAF\u9EB0\u9EB1\u9EB2\u9EB3\u9EB5\u9EB6\u9EB7"+ + "\u9EB9\u9EBA\u9EBC\u9EBF\u9EC0\u9EC1\u9EC2\u9EC3"+ + "\u9EC5\u9EC6\u9EC7\u9EC8\u9ECA\u9ECB\u9ECC\u9ED0"+ + "\u9ED2\u9ED3\u9ED5\u9ED6\u9ED7\u9ED9\u9EDA\u9EDE"+ + "\u9EE1\u9EE3\u9EE4\u9EE6\u9EE8\u9EEB\u9EEC\u9EED"+ + "\u9EEE\u9EF0\u9EF1\u9EF2\u9EF3\u9EF4\u9EF5\u9EF6"+ + "\u9EF7\u9EF8\u9EFA\u9EFD\u9EFF\u9F00\u9F01\u9F02"+ + "\u9F03\u9F04\u9F05\uFFFD\u9F06\u9F07\u9F08\u9F09"+ + "\u9F0A\u9F0C\u9F0F\u9F11\u9F12\u9F14\u9F15\u9F16"+ + "\u9F18\u9F1A\u9F1B\u9F1C\u9F1D\u9F1E\u9F1F\u9F21"+ + "\u9F23\u9F24\u9F25\u9F26\u9F27\u9F28\u9F29\u9F2A"+ + "\u9F2B\u9F2D\u9F2E\u9F30\u9F31\uE3AC\uE3AD\uE3AE"+ + "\uE3AF\uE3B0\uE3B1\uE3B2\uE3B3\uE3B4\uE3B5\uE3B6"+ + "\uE3B7\uE3B8\uE3B9\uE3BA\uE3BB\uE3BC\uE3BD\uE3BE"+ + "\uE3BF\uE3C0\uE3C1\uE3C2\uE3C3\uE3C4\uE3C5\uE3C6"+ + "\uE3C7\uE3C8\uE3C9\uE3CA\uE3CB\uE3CC\uE3CD\uE3CE"+ + "\uE3CF\uE3D0\uE3D1\uE3D2\uE3D3\uE3D4\uE3D5\uE3D6"+ + "\uE3D7\uE3D8\uE3D9\uE3DA\uE3DB\uE3DC\uE3DD\uE3DE"+ + "\uE3DF\uE3E0\uE3E1\uE3E2\uE3E3\uE3E4\uE3E5\uE3E6"+ + "\uE3E7\uE3E8\uE3E9\uE3EA\uE3EB\uE3EC\uE3ED\uE3EE"+ + "\uE3EF\uE3F0\uE3F1\uE3F2\uE3F3\uE3F4\uE3F5\uE3F6"+ + "\uE3F7\uE3F8\uE3F9\uE3FA\uE3FB\uE3FC\uE3FD\uE3FE"+ + "\uE3FF\uE400\uE401\uE402\uE403\uE404\uE405\uE406"+ + "\uE407\uE408\uE409\u9F32\u9F33\u9F34\u9F35\u9F36"+ + "\u9F38\u9F3A\u9F3C\u9F3F\u9F40\u9F41\u9F42\u9F43"+ + "\u9F45\u9F46\u9F47\u9F48\u9F49\u9F4A\u9F4B\u9F4C"+ + "\u9F4D\u9F4E\u9F4F\u9F52\u9F53\u9F54\u9F55\u9F56"+ + "\u9F57\u9F58\u9F59\u9F5A\u9F5B\u9F5C\u9F5D\u9F5E"+ + "\u9F5F\u9F60\u9F61\u9F62\u9F63\u9F64\u9F65\u9F66"+ + "\u9F67\u9F68\u9F69\u9F6A\u9F6B\u9F6C\u9F6D\u9F6E"+ + "\u9F6F\u9F70\u9F71\u9F72\u9F73\u9F74\u9F75\u9F76"+ + "\u9F77\u9F78\uFFFD\u9F79\u9F7A\u9F7B\u9F7C\u9F7D"+ + "\u9F7E\u9F81\u9F82\u9F8D\u9F8E\u9F8F\u9F90\u9F91"+ + "\u9F92\u9F93\u9F94\u9F95\u9F96\u9F97\u9F98\u9F9C"+ + "\u9F9D\u9F9E\u9FA1\u9FA2\u9FA3\u9FA4\u9FA5\uF92C"+ + "\uF979\uF995\uF9E7\uF9F1\uE40A\uE40B\uE40C\uE40D"+ + "\uE40E\uE40F\uE410\uE411\uE412\uE413\uE414\uE415"+ + "\uE416\uE417\uE418\uE419\uE41A\uE41B\uE41C\uE41D"+ + "\uE41E\uE41F\uE420\uE421\uE422\uE423\uE424\uE425"+ + "\uE426\uE427\uE428\uE429\uE42A\uE42B\uE42C\uE42D"+ + "\uE42E\uE42F\uE430\uE431\uE432\uE433\uE434\uE435"+ + "\uE436\uE437\uE438\uE439\uE43A\uE43B\uE43C\uE43D"+ + "\uE43E\uE43F\uE440\uE441\uE442\uE443\uE444\uE445"+ + "\uE446\uE447\uE448\uE449\uE44A\uE44B\uE44C\uE44D"+ + "\uE44E\uE44F\uE450\uE451\uE452\uE453\uE454\uE455"+ + "\uE456\uE457\uE458\uE459\uE45A\uE45B\uE45C\uE45D"+ + "\uE45E\uE45F\uE460\uE461\uE462\uE463\uE464\uE465"+ + "\uE466\uE467\uFA0C\uFA0D\uFA0E\uFA0F\uFA11\uFA13"+ + "\uFA14\uFA18\uFA1F\uFA20\uFA21\uFA23\uFA24\uFA27"+ + "\uFA28\uFA29\u2E81\uE816\uE817\uE818\u2E84\u3473"+ + "\u3447\u2E88\u2E8B\uE81E\u359E\u361A\u360E\u2E8C"+ + "\u2E97\u396E\u3918\uE826\u39CF\u39DF\u3A73\u39D0"+ + "\uE82B\uE82C\u3B4E\u3C6E\u3CE0\u2EA7\uE831\uE832"+ + "\u2EAA\u4056\u415F\u2EAE\u4337\u2EB3\u2EB6\u2EB7"+ + "\uE83B\u43B1\u43AC\u2EBB\u43DD\u44D6\u4661\u464C"+ + "\uE843\uFFFD\u4723\u4729\u477C\u478D\u2ECA\u4947"+ + "\u497A\u497D\u4982\u4983\u4985\u4986\u499F\u499B"+ + "\u49B7\u49B6\uE854\uE855\u4CA3\u4C9F\u4CA0\u4CA1"+ + "\u4C77\u4CA2\u4D13\u4D14\u4D15\u4D16\u4D17\u4D18"+ + "\u4D19\u4DAE\uE864\uE468\uE469\uE46A\uE46B\uE46C"+ + "\uE46D\uE46E\uE46F\uE470\uE471\uE472\uE473\uE474"+ + "\uE475\uE476\uE477\uE478\uE479\uE47A\uE47B\uE47C"+ + "\uE47D\uE47E\uE47F\uE480\uE481\uE482\uE483\uE484"+ + "\uE485\uE486\uE487\uE488\uE489\uE48A\uE48B\uE48C"+ + "\uE48D\uE48E\uE48F\uE490\uE491\uE492\uE493\uE494"+ + "\uE495\uE496\uE497\uE498\uE499\uE49A\uE49B\uE49C"+ + "\uE49D\uE49E\uE49F\uE4A0\uE4A1\uE4A2\uE4A3\uE4A4"+ + "\uE4A5\uE4A6\uE4A7\uE4A8\uE4A9\uE4AA\uE4AB\uE4AC"+ + "\uE4AD\uE4AE\uE4AF\uE4B0\uE4B1\uE4B2\uE4B3\uE4B4"+ + "\uE4B5\uE4B6\uE4B7\uE4B8\uE4B9\uE4BA\uE4BB\uE4BC"+ + "\uE4BD\uE4BE\uE4BF\uE4C0\uE4C1\uE4C2\uE4C3\uE4C4"+ + "\uE4C5"; + + final static short index1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0 + }; + + static String index2[] = { + innerIndex0, + innerIndex1, + innerIndex2, + innerIndex3, + innerIndex4, + innerIndex5, + innerIndex6, + innerIndex7 + }; + + + + + + private final static String innerEncoderIndex0= + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007"+ + "\u2008\u2009\u200A\u200B\u200C\u200D\u200E\u200F"+ + "\u2010\u2011\u2012\u2013\u2014\u2015\u2016\u2017"+ + "\u2018\u2019\u201A\u201B\u201C\u201D\u201E\u201F"+ + "\u2020\u2021\u2022\u2023\uA1E8\u2024\u2025\uA1EC"+ + "\uA1A7\u2026\u2027\u2028\u2029\u202A\u202B\u202C"+ + "\uA1E3\uA1C0\u202D\u202E\u202F\u2030\u2031\uA1A4"+ + "\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u2039"+ + "\u203A\u203B\u203C\u203D\u203E\u203F\u2040\u2041"+ + "\u2042\u2043\u2044\u2045\u2046\u2047\u2048\u2049"+ + "\u204A\u204B\u204C\u204D\u204E\u204F\u2050\uA1C1"+ + "\u2051\u2052\u2053\u2054\u2055\u2056\u2057\u2058"+ + "\uA8A4\uA8A2\u2059\u205A\u205B\u205C\u205D\u205E"+ + "\uA8A8\uA8A6\uA8BA\u205F\uA8AC\uA8AA\u2060\u2061"+ + "\u2062\u2063\uA8B0\uA8AE\u2064\u2065\u2066\uA1C2"+ + "\u2067\uA8B4\uA8B2\u2068\uA8B9\u2069\u206A\u206B"+ + "\u206C\uA8A1\u206D\u206E\u206F\u2070\u2071\u2072"+ + "\u2073\u2074\u2075\u2076\u2077\u2078\u2079\u207A"+ + "\u207B\u207C\u207D\uA8A5\u207E\u207F\u2080\u2081"+ + "\u2082\u2083\u2084\uA8A7\u2085\u2086\u2087\u2088"+ + "\u2089\u208A\u208B\u208C\u208D\u208E\u208F\u2090"+ + "\u2091\u2092\u2093\uA8A9\u2094\u2095\u2096\u2097"+ + "\u2098\u2099\u209A\u209B\u209C\u209D\u209E\u209F"+ + "\u20A0\u20A1\u20A2\u20A3\u20A4\u20A5\u20A6\u20A7"+ + "\u20A8\u20A9\u20AA\u20AB\uA8BD\u20AC\u20AD\u20AE"+ + "\uA8BE\u20AF\u20B0\u20B1\u20B2\uA8AD\u20B3\u20B4"+ + "\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC"+ + "\u20BD\u20BE\u20BF\u20C0\u20C1\u20C2\u20C3\u20C4"+ + "\u20C5\u20C6\u20C7\u20C8\u20C9\u20CA\u20CB\u20CC"+ + "\u20CD\u20CE\u20CF\uA8B1\u20D0\u20D1\u20D2\u20D3"+ + "\u20D4\u20D5\u20D6\u20D7\u20D8\u20D9\u20DA\u20DB"+ + "\u20DC\u20DD\u20DE\u20DF\u20E0\u20E1\u20E2\u20E3"+ + "\u20E4\u20E5\u20E6\u20E7\u20E8\u20E9\u20EA\u20EB"+ + "\u20EC\u20ED\u20EE\u20EF\u20F0\u20F1\u20F2\u20F3"+ + "\u20F4\u20F5\u20F6\u20F7\u20F8\u20F9\u20FA\u20FB"+ + "\u20FC\u20FD\u20FE\u20FF\u2100\u2101\u2102\u2103"+ + "\u2104\u2105\u2106\u2107\u2108\u2109\u210A\u210B"+ + "\u210C\u210D\u210E\u210F\u2110\u2111\u2112\u2113"+ + "\u2114\u2115\u2116\u2117\u2118\u2119\u211A\u211B"+ + "\u211C\u211D\u211E\u211F\u2120\u2121\u2122\u2123"+ + "\u2124\u2125\u2126\u2127\u2128\u2129\u212A\u212B"+ + "\u212C\u212D\u212E\u212F\u2130\u2131\uA8A3\u2132"+ + "\uA8AB\u2133\uA8AF\u2134\uA8B3\u2135\uA8B5\u2136"+ + "\uA8B6\u2137\uA8B7\u2138\uA8B8\u2139\u213A\u213B"+ + "\u213C\u213D\u213E\u213F\u2140\u2141\u2142\u2143"+ + "\u2144\u2145\u2146\u2147\u2148\u2149\u214A\u214B"+ + "\u214C\u214D\u214E\u214F\u2150\u2151\u2152\u2153"+ + "\u2154\uA8BF\u2155\u2156\u2157\u2158\u2159\u215A"+ + "\u215B\u215C\u215D\u215E\u215F\u2160\u2161\u2162"+ + "\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216A"+ + "\u216B\u216C\u216D\u216E\u216F\u2170\u2171\u2172"+ + "\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217A"+ + "\u217B\u217C\u217D\u217E\u217F\u2180\u2181\u2182"+ + "\u2183\u2184\u2185\u2186\u2187\u2188\u2189\u218A"+ + "\u218B\u218C\u218D\u218E\u218F\u2190\u2191\u2192"+ + "\u2193\u2194\u2195\u2196\u2197\u2198\u2199\u219A"+ + "\u219B\u219C\u219D\u219E\u219F\u21A0\u21A1\u21A2"+ + "\u21A3\u21A4\u21A5\u21A6\u21A7\u21A8\u21A9\u21AA"+ + "\u21AB\uA8BB\u21AC\u21AD\u21AE\u21AF\u21B0\u21B1"+ + "\u21B2\u21B3\u21B4\u21B5\u21B6\u21B7\u21B8\u21B9"+ + "\u21BA\uA8C0\u21BB\u21BC\u21BD\u21BE\u21BF\u21C0"+ + "\u21C1\u21C2\u21C3\u21C4\u21C5\u21C6\u21C7\u21C8"+ + "\u21C9\u21CA\u21CB\u21CC\u21CD\u21CE\u21CF\u21D0"+ + "\u21D1\u21D2\u21D3\u21D4\u21D5\u21D6\u21D7\u21D8"+ + "\u21D9\u21DA\u21DB\u21DC\u21DD\u21DE\u21DF\u21E0"+ + "\u21E1\u21E2\u21E3\u21E4\u21E5\u21E6\u21E7\u21E8"+ + "\u21E9\u21EA\u21EB\u21EC\u21ED\u21EE\u21EF\u21F0"+ + "\u21F1\u21F2\u21F3\u21F4\u21F5\u21F6\u21F7\u21F8"+ + "\u21F9\u21FA\u21FB\u21FC\u21FD\u21FE\u21FF\u2200"+ + "\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208"+ + "\u2209\u220A\u220B\u220C\u220D\u220E\u220F\u2210"+ + "\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218"+ + "\u2219\u221A\u221B\u221C\u221D\u221E\u221F\uA1A6"+ + "\u2220\uA1A5\uA840\uA841\u2221\u2222\u2223\u2224"+ + "\u2225\u2226\u2227\u2228\u2229\u222A\u222B\u222C"+ + "\u222D\uA842\u222E\u222F\u2230\u2231\u2232\u2233"+ + "\u2234\u2235\u2236\u2237\u2238\u2239\u223A\u223B"+ + "\u223C\u223D\u223E\u223F\u2240\u2241\u2242\u2243"+ + "\u2244\u2245\u2246\u2247\u2248\u2249\u224A\u224B"+ + "\u224C\u224D\u224E\u224F\u2250\u2251\u2252\u2253"+ + "\u2254\u2255\u2256\u2257\u2258\u2259\u225A\u225B"+ + "\u225C\u225D\u225E\u225F\u2260\u2261\u2262\u2263"+ + "\u2264\u2265\u2266\u2267\u2268\u2269\u226A\u226B"+ + "\u226C\u226D\u226E\u226F\u2270\u2271\u2272\u2273"+ + "\u2274\u2275\u2276\u2277\u2278\u2279\u227A\u227B"+ + "\u227C\u227D\u227E\u227F\u2280\u2281\u2282\u2283"+ + "\u2284\u2285\u2286\u2287\u2288\u2289\u228A\u228B"+ + "\u228C\u228D\u228E\u228F\u2290\u2291\u2292\u2293"+ + "\u2294\u2295\u2296\u2297\u2298\u2299\u229A\u229B"+ + "\u229C\u229D\u229E\u229F\u22A0\u22A1\u22A2\u22A3"+ + "\u22A4\u22A5\u22A6\u22A7\u22A8\u22A9\u22AA\u22AB"+ + "\u22AC\u22AD\u22AE\u22AF\u22B0\u22B1\u22B2\u22B3"+ + "\u22B4\u22B5\u22B6\u22B7\u22B8\u22B9\u22BA\u22BB"+ + "\u22BC\u22BD\u22BE\u22BF\u22C0\u22C1\u22C2\u22C3"+ + "\u22C4\u22C5\u22C6\u22C7\u22C8\u22C9\u22CA\u22CB"+ + "\u22CC\u22CD\u22CE\u22CF\u22D0\u22D1\u22D2\u22D3"+ + "\u22D4\u22D5\u22D6\u22D7\u22D8\u22D9\u22DA\u22DB"+ + "\u22DC\u22DD\u22DE\u22DF\u22E0\u22E1\u22E2\u22E3"+ + "\u22E4\uA6A1\uA6A2\uA6A3\uA6A4\uA6A5\uA6A6\uA6A7"+ + "\uA6A8\uA6A9\uA6AA\uA6AB\uA6AC\uA6AD\uA6AE\uA6AF"+ + "\uA6B0\uA6B1\u22E5\uA6B2\uA6B3\uA6B4\uA6B5\uA6B6"+ + "\uA6B7\uA6B8\u22E6\u22E7\u22E8\u22E9\u22EA\u22EB"+ + "\u22EC\uA6C1\uA6C2\uA6C3\uA6C4\uA6C5\uA6C6\uA6C7"+ + "\uA6C8\uA6C9\uA6CA\uA6CB\uA6CC\uA6CD\uA6CE\uA6CF"+ + "\uA6D0\uA6D1\u22ED\uA6D2\uA6D3\uA6D4\uA6D5\uA6D6"+ + "\uA6D7\uA6D8\u22EE\u22EF\u22F0\u22F1\u22F2\u22F3"+ + "\u22F4\u22F5\u22F6\u22F7\u22F8\u22F9\u22FA\u22FB"+ + "\u22FC\u22FD\u22FE\u22FF\u2300\u2301\u2302\u2303"+ + "\u2304\u2305\u2306\u2307\u2308\u2309\u230A\u230B"+ + "\u230C\u230D\u230E\u230F\u2310\u2311\u2312\u2313"+ + "\u2314\u2315\u2316\u2317\u2318\u2319\u231A\u231B"+ + "\u231C\u231D\u231E\u231F\u2320\u2321\u2322\u2323"+ + "\u2324\uA7A7\u2325\u2326\u2327\u2328\u2329\u232A"+ + "\u232B\u232C\u232D\u232E\u232F\u2330\u2331\u2332"+ + "\uA7A1\uA7A2\uA7A3\uA7A4\uA7A5\uA7A6\uA7A8\uA7A9"+ + "\uA7AA\uA7AB\uA7AC\uA7AD\uA7AE\uA7AF\uA7B0\uA7B1"+ + "\uA7B2\uA7B3\uA7B4\uA7B5\uA7B6\uA7B7\uA7B8\uA7B9"+ + "\uA7BA\uA7BB\uA7BC\uA7BD\uA7BE\uA7BF\uA7C0\uA7C1"+ + "\uA7D1\uA7D2\uA7D3\uA7D4\uA7D5\uA7D6\uA7D8\uA7D9"+ + "\uA7DA\uA7DB\uA7DC\uA7DD\uA7DE\uA7DF\uA7E0\uA7E1"+ + "\uA7E2\uA7E3\uA7E4\uA7E5\uA7E6\uA7E7\uA7E8\uA7E9"+ + "\uA7EA\uA7EB\uA7EC\uA7ED\uA7EE\uA7EF\uA7F0\uA7F1"+ + "\u2333\uA7D7\u2334\u2335\u2336\u2337\u2338\u2339"+ + "\u233A\u233B\u233C\u233D\u233E\u233F\u2340\u2341"+ + "\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349"+ + "\u234A\u234B\u234C\u234D\u234E\u234F\u2350\u2351"+ + "\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359"+ + "\u235A\u235B\u235C\u235D\u235E\u235F\u2360\u2361"+ + "\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369"+ + "\u236A\u236B\u236C\u236D\u236E\u236F\u2370\u2371"+ + "\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379"+ + "\u237A\u237B\u237C\u237D\u237E\u237F\u2380\u2381"+ + "\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389"+ + "\u238A\u238B\u238C\u238D\u238E\u238F\u2390\u2391"+ + "\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399"+ + "\u239A\u239B\u239C\u239D\u239E\u239F\u23A0\u23A1"+ + "\u23A2\u23A3\u23A4\u23A5\u23A6\u23A7\u23A8\u23A9"+ + "\u23AA\u23AB\u23AC\u23AD\u23AE\u23AF\u23B0\u23B1"+ + "\u23B2\u23B3\u23B4\u23B5\u23B6\u23B7\u23B8\u23B9"+ + "\u23BA\u23BB\u23BC\u23BD\u23BE\u23BF\u23C0\u23C1"+ + "\u23C2\u23C3\u23C4\u23C5\u23C6\u23C7\u23C8\u23C9"+ + "\u23CA\u23CB\u23CC\u23CD\u23CE\u23CF\u23D0\u23D1"+ + "\u23D2\u23D3\u23D4\u23D5\u23D6\u23D7\u23D8\u23D9"+ + "\u23DA\u23DB\u23DC\u23DD\u23DE\u23DF\u23E0\u23E1"+ + "\u23E2\u23E3\u23E4\u23E5\u23E6\u23E7\u23E8\u23E9"+ + "\u23EA\u23EB\u23EC\u23ED\u23EE\u23EF\u23F0\u23F1"+ + "\u23F2\u23F3\u23F4\u23F5\u23F6\u23F7\u23F8\u23F9"+ + "\u23FA\u23FB\u23FC\u23FD\u23FE\u23FF\u2400\u2401"+ + "\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409"+ + "\u240A\u240B\u240C\u240D\u240E\u240F\u2410\u2411"+ + "\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419"+ + "\u241A\u241B\u241C\u241D\u241E\u241F\u2420\u2421"+ + "\u2422\u2423\u2424\u2425\u2426\u2427\u2428\u2429"+ + "\u242A\u242B\u242C\u242D\u242E\u242F\u2430\u2431"+ + "\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439"+ + "\u243A\u243B\u243C\u243D\u243E\u243F\u2440\u2441"+ + "\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449"+ + "\u244A\u244B\u244C\u244D\u244E\u244F\u2450\u2451"+ + "\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459"+ + "\u245A\u245B\u245C\u245D\u245E\u245F\u2460\u2461"+ + "\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469"+ + "\u246A\u246B\u246C\u246D\u246E\u246F\u2470\u2471"+ + "\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479"+ + "\u247A\u247B\u247C\u247D\u247E\u247F\u2480\u2481"+ + "\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489"+ + "\u248A\u248B\u248C\u248D\u248E\u248F\u2490\u2491"+ + "\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499"+ + "\u249A\u249B\u249C\u249D\u249E\u249F\u24A0\u24A1"+ + "\u24A2\u24A3\u24A4\u24A5\u24A6\u24A7\u24A8\u24A9"+ + "\u24AA\u24AB\u24AC\u24AD\u24AE\u24AF\u24B0\u24B1"+ + "\u24B2\u24B3\u24B4\u24B5\u24B6\u24B7\u24B8\u24B9"+ + "\u24BA\u24BB\u24BC\u24BD\u24BE\u24BF\u24C0\u24C1"+ + "\u24C2\u24C3\u24C4\u24C5\u24C6\u24C7\u24C8\u24C9"+ + "\u24CA\u24CB\u24CC\u24CD\u24CE\u24CF\u24D0\u24D1"+ + "\u24D2\u24D3\u24D4\u24D5\u24D6\u24D7\u24D8\u24D9"+ + "\u24DA\u24DB\u24DC\u24DD\u24DE\u24DF\u24E0\u24E1"+ + "\u24E2\u24E3\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9"+ + "\u24EA\u24EB\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1"+ + "\u24F2\u24F3\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9"+ + "\u24FA\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501"+ + "\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509"+ + "\u250A\u250B\u250C\u250D\u250E\u250F\u2510\u2511"+ + "\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519"+ + "\u251A\u251B\u251C\u251D\u251E\u251F\u2520\u2521"+ + "\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529"+ + "\u252A\u252B\u252C\u252D\u252E\u252F\u2530\u2531"+ + "\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539"+ + "\u253A\u253B\u253C\u253D\u253E\u253F\u2540\u2541"+ + "\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549"+ + "\u254A\u254B\u254C\u254D\u254E\u254F\u2550\u2551"+ + "\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559"+ + "\u255A\u255B\u255C\u255D\u255E\u255F\u2560\u2561"+ + "\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569"+ + "\u256A\u256B\u256C\u256D\u256E\u256F\u2570\u2571"+ + "\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579"+ + "\u257A\u257B\u257C\u257D\u257E\u257F\u2580\u2581"+ + "\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589"+ + "\u258A\u258B\u258C\u258D\u258E\u258F\u2590\u2591"+ + "\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599"+ + "\u259A\u259B\u259C\u259D\u259E\u259F\u25A0\u25A1"+ + "\u25A2\u25A3\u25A4\u25A5\u25A6\u25A7\u25A8\u25A9"+ + "\u25AA\u25AB\u25AC\u25AD\u25AE\u25AF\u25B0\u25B1"+ + "\u25B2\u25B3\u25B4\u25B5\u25B6\u25B7\u25B8\u25B9"+ + "\u25BA\u25BB\u25BC\u25BD\u25BE\u25BF\u25C0\u25C1"+ + "\u25C2\u25C3\u25C4\u25C5\u25C6\u25C7\u25C8\u25C9"+ + "\u25CA\u25CB\u25CC\u25CD\u25CE\u25CF\u25D0\u25D1"+ + "\u25D2\u25D3\u25D4\u25D5\u25D6\u25D7\u25D8\u25D9"+ + "\u25DA\u25DB\u25DC\u25DD\u25DE\u25DF\u25E0\u25E1"+ + "\u25E2\u25E3\u25E4\u25E5\u25E6\u25E7\u25E8\u25E9"+ + "\u25EA\u25EB\u25EC\u25ED\u25EE\u25EF\u25F0\u25F1"+ + "\u25F2\u25F3\u25F4\u25F5\u25F6\u25F7\u25F8\u25F9"+ + "\u25FA\u25FB\u25FC\u25FD\u25FE\u25FF\u2600\u2601"+ + "\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609"+ + "\u260A\u260B\u260C\u260D\u260E\u260F\u2610\u2611"+ + "\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619"+ + "\u261A\u261B\u261C\u261D\u261E\u261F\u2620\u2621"+ + "\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629"+ + "\u262A\u262B\u262C\u262D\u262E\u262F\u2630\u2631"+ + "\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639"+ + "\u263A\u263B\u263C\u263D\u263E\u263F\u2640\u2641"+ + "\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649"+ + "\u264A\u264B\u264C\u264D\u264E\u264F\u2650\u2651"+ + "\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659"+ + "\u265A\u265B\u265C\u265D\u265E\u265F\u2660\u2661"+ + "\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669"+ + "\u266A\u266B\u266C\u266D\u266E\u266F\u2670\u2671"+ + "\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679"+ + "\u267A\u267B\u267C\u267D\u267E\u267F\u2680\u2681"+ + "\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689"+ + "\u268A\u268B\u268C\u268D\u268E\u268F\u2690\u2691"+ + "\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699"+ + "\u269A\u269B\u269C\u269D\u269E\u269F\u26A0\u26A1"+ + "\u26A2\u26A3\u26A4\u26A5\u26A6\u26A7\u26A8\u26A9"+ + "\u26AA\u26AB\u26AC\u26AD\u26AE\u26AF\u26B0\u26B1"+ + "\u26B2\u26B3\u26B4\u26B5\u26B6\u26B7\u26B8\u26B9"+ + "\u26BA\u26BB\u26BC\u26BD\u26BE\u26BF\u26C0\u26C1"+ + "\u26C2\u26C3\u26C4\u26C5\u26C6\u26C7\u26C8\u26C9"+ + "\u26CA\u26CB\u26CC\u26CD\u26CE\u26CF\u26D0\u26D1"+ + "\u26D2\u26D3\u26D4\u26D5\u26D6\u26D7\u26D8\u26D9"+ + "\u26DA\u26DB\u26DC\u26DD\u26DE\u26DF\u26E0\u26E1"+ + "\u26E2\u26E3\u26E4\u26E5\u26E6\u26E7\u26E8\u26E9"+ + "\u26EA\u26EB\u26EC\u26ED\u26EE\u26EF\u26F0\u26F1"+ + "\u26F2\u26F3\u26F4\u26F5\u26F6\u26F7\u26F8\u26F9"+ + "\u26FA\u26FB\u26FC\u26FD\u26FE\u26FF\u2700\u2701"+ + "\u2702\u2703\u2704\u2705\u2706\u2707\u2708\u2709"+ + "\u270A\u270B\u270C\u270D\u270E\u270F\u2710\u2711"+ + "\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719"+ + "\u271A\u271B\u271C\u271D\u271E\u271F\u2720\u2721"+ + "\u2722\u2723\u2724\u2725\u2726\u2727\u2728\u2729"+ + "\u272A\u272B\u272C\u272D\u272E\u272F\u2730\u2731"+ + "\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739"+ + "\u273A\u273B\u273C\u273D\u273E\u273F\u2740\u2741"+ + "\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749"+ + "\u274A\u274B\u274C\u274D\u274E\u274F\u2750\u2751"+ + "\u2752\u2753\u2754\u2755\u2756\u2757\u2758\u2759"+ + "\u275A\u275B\u275C\u275D\u275E\u275F\u2760\u2761"+ + "\u2762\u2763\u2764\u2765\u2766\u2767\u2768\u2769"+ + "\u276A\u276B\u276C\u276D\u276E\u276F\u2770\u2771"+ + "\u2772\u2773\u2774\u2775\u2776\u2777\u2778\u2779"+ + "\u277A\u277B\u277C\u277D\u277E\u277F\u2780\u2781"+ + "\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789"+ + "\u278A\u278B\u278C\u278D\u278E\u278F\u2790\u2791"+ + "\u2792\u2793\u2794\u2795\u2796\u2797\u2798\u2799"+ + "\u279A\u279B\u279C\u279D\u279E\u279F\u27A0\u27A1"+ + "\u27A2\u27A3\u27A4\u27A5\u27A6\u27A7\u27A8\u27A9"+ + "\u27AA\u27AB\u27AC\u27AD\u27AE\u27AF\u27B0\u27B1"+ + "\u27B2\u27B3\u27B4\u27B5\u27B6\u27B7\u27B8\u27B9"+ + "\u27BA\u27BB\u27BC\u27BD\u27BE\u27BF\u27C0\u27C1"+ + "\u27C2\u27C3\u27C4\u27C5\u27C6\u27C7\u27C8\u27C9"+ + "\u27CA\u27CB\u27CC\u27CD\u27CE\u27CF\u27D0\u27D1"+ + "\u27D2\u27D3\u27D4\u27D5\u27D6\u27D7\u27D8\u27D9"+ + "\u27DA\u27DB\u27DC\u27DD\u27DE\u27DF\u27E0\u27E1"+ + "\u27E2\u27E3\u27E4\u27E5\u27E6\u27E7\u27E8\u27E9"+ + "\u27EA\u27EB\u27EC\u27ED\u27EE\u27EF\u27F0\u27F1"+ + "\u27F2\u27F3\u27F4\u27F5\u27F6\u27F7\u27F8\u27F9"+ + "\u27FA\u27FB\u27FC\u27FD\u27FE\u27FF\u2800\u2801"+ + "\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809"+ + "\u280A\u280B\u280C\u280D\u280E\u280F\u2810\u2811"+ + "\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819"+ + "\u281A\u281B\u281C\u281D\u281E\u281F\u2820\u2821"+ + "\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829"+ + "\u282A\u282B\u282C\u282D\u282E\u282F\u2830\u2831"+ + "\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839"+ + "\u283A\u283B\u283C\u283D\u283E\u283F\u2840\u2841"+ + "\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849"+ + "\u284A\u284B\u284C\u284D\u284E\u284F\u2850\u2851"+ + "\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859"+ + "\u285A\u285B\u285C\u285D\u285E\u285F\u2860\u2861"+ + "\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869"+ + "\u286A\u286B\u286C\u286D\u286E\u286F\u2870\u2871"+ + "\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879"+ + "\u287A\u287B\u287C\u287D\u287E\u287F\u2880\u2881"+ + "\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889"+ + "\u288A\u288B\u288C\u288D\u288E\u288F\u2890\u2891"+ + "\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899"+ + "\u289A\u289B\u289C\u289D\u289E\u289F\u28A0\u28A1"+ + "\u28A2\u28A3\u28A4\u28A5\u28A6\u28A7\u28A8\u28A9"+ + "\u28AA\u28AB\u28AC\u28AD\u28AE\u28AF\u28B0\u28B1"+ + "\u28B2\u28B3\u28B4\u28B5\u28B6\u28B7\u28B8\u28B9"+ + "\u28BA\u28BB\u28BC\u28BD\u28BE\u28BF\u28C0\u28C1"+ + "\u28C2\u28C3\u28C4\u28C5\u28C6\u28C7\u28C8\u28C9"+ + "\u28CA\u28CB\u28CC\u28CD\u28CE\u28CF\u28D0\u28D1"+ + "\u28D2\u28D3\u28D4\u28D5\u28D6\u28D7\u28D8\u28D9"+ + "\u28DA\u28DB\u28DC\u28DD\u28DE\u28DF\u28E0\u28E1"+ + "\u28E2\u28E3\u28E4\u28E5\u28E6\u28E7\u28E8\u28E9"+ + "\u28EA\u28EB\u28EC\u28ED\u28EE\u28EF\u28F0\u28F1"+ + "\u28F2\u28F3\u28F4\u28F5\u28F6\u28F7\u28F8\u28F9"+ + "\u28FA\u28FB\u28FC\u28FD\u28FE\u28FF\u2900\u2901"+ + "\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909"+ + "\u290A\u290B\u290C\u290D\u290E\u290F\u2910\u2911"+ + "\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919"+ + "\u291A\u291B\u291C\u291D\u291E\u291F\u2920\u2921"+ + "\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929"+ + "\u292A\u292B\u292C\u292D\u292E\u292F\u2930\u2931"+ + "\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939"+ + "\u293A\u293B\u293C\u293D\u293E\u293F\u2940\u2941"+ + "\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949"+ + "\u294A\u294B\u294C\u294D\u294E\u294F\u2950\u2951"+ + "\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959"+ + "\u295A\u295B\u295C\u295D\u295E\u295F\u2960\u2961"+ + "\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969"+ + "\u296A\u296B\u296C\u296D\u296E\u296F\u2970\u2971"+ + "\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979"+ + "\u297A\u297B\u297C\u297D\u297E\u297F\u2980\u2981"+ + "\u2982\u2983\u2984\u2985\u2986\u2987\u2988\u2989"+ + "\u298A\u298B\u298C\u298D\u298E\u298F\u2990\u2991"+ + "\u2992\u2993\u2994\u2995\u2996\u2997\u2998\u2999"+ + "\u299A\u299B\u299C\u299D\u299E\u299F\u29A0\u29A1"+ + "\u29A2\u29A3\u29A4\u29A5\u29A6\u29A7\u29A8\u29A9"+ + "\u29AA\u29AB\u29AC\u29AD\u29AE\u29AF\u29B0\u29B1"+ + "\u29B2\u29B3\u29B4\u29B5\u29B6\u29B7\u29B8\u29B9"+ + "\u29BA\u29BB\u29BC\u29BD\u29BE\u29BF\u29C0\u29C1"+ + "\u29C2\u29C3\u29C4\u29C5\u29C6\u29C7\u29C8\u29C9"+ + "\u29CA\u29CB\u29CC\u29CD\u29CE\u29CF\u29D0\u29D1"+ + "\u29D2\u29D3\u29D4\u29D5\u29D6\u29D7\u29D8\u29D9"+ + "\u29DA\u29DB\u29DC\u29DD\u29DE\u29DF\u29E0\u29E1"+ + "\u29E2\u29E3\u29E4\u29E5\u29E6\u29E7\u29E8\u29E9"+ + "\u29EA\u29EB\u29EC\u29ED\u29EE\u29EF\u29F0\u29F1"+ + "\u29F2\u29F3\u29F4\u29F5\u29F6\u29F7\u29F8\u29F9"+ + "\u29FA\u29FB\u29FC\u29FD\u29FE\u29FF\u2A00\u2A01"+ + "\u2A02\u2A03\u2A04\u2A05\u2A06\u2A07\u2A08\u2A09"+ + "\u2A0A\u2A0B\u2A0C\u2A0D\u2A0E\u2A0F\u2A10\u2A11"+ + "\u2A12\u2A13\u2A14\u2A15\u2A16\u2A17\u2A18\u2A19"+ + "\u2A1A\u2A1B\u2A1C\u2A1D\u2A1E\u2A1F\u2A20\u2A21"+ + "\u2A22\u2A23\u2A24\u2A25\u2A26\u2A27\u2A28\u2A29"+ + "\u2A2A\u2A2B\u2A2C\u2A2D\u2A2E\u2A2F\u2A30\u2A31"+ + "\u2A32\u2A33\u2A34\u2A35\u2A36\u2A37\u2A38\u2A39"+ + "\u2A3A\u2A3B\u2A3C\u2A3D\u2A3E\u2A3F\u2A40\u2A41"+ + "\u2A42\u2A43\u2A44\u2A45\u2A46\u2A47\u2A48\u2A49"+ + "\u2A4A\u2A4B\u2A4C\u2A4D\u2A4E\u2A4F\u2A50\u2A51"+ + "\u2A52\u2A53\u2A54\u2A55\u2A56\u2A57\u2A58\u2A59"+ + "\u2A5A\u2A5B\u2A5C\u2A5D\u2A5E\u2A5F\u2A60\u2A61"+ + "\u2A62\u2A63\u2A64\u2A65\u2A66\u2A67\u2A68\u2A69"+ + "\u2A6A\u2A6B\u2A6C\u2A6D\u2A6E\u2A6F\u2A70\u2A71"+ + "\u2A72\u2A73\u2A74\u2A75\u2A76\u2A77\u2A78\u2A79"+ + "\u2A7A\u2A7B\u2A7C\u2A7D\u2A7E\u2A7F\u2A80\u2A81"+ + "\u2A82\u2A83\u2A84\u2A85\u2A86\u2A87\u2A88\u2A89"+ + "\u2A8A\u2A8B\u2A8C\u2A8D\u2A8E\u2A8F\u2A90\u2A91"+ + "\u2A92\u2A93\u2A94\u2A95\u2A96\u2A97\u2A98\u2A99"+ + "\u2A9A\u2A9B\u2A9C\u2A9D\u2A9E\u2A9F\u2AA0\u2AA1"+ + "\u2AA2\u2AA3\u2AA4\u2AA5\u2AA6\u2AA7\u2AA8\u2AA9"+ + "\u2AAA\u2AAB\u2AAC\u2AAD\u2AAE\u2AAF\u2AB0\u2AB1"+ + "\u2AB2\u2AB3\u2AB4\u2AB5\u2AB6\u2AB7\u2AB8\u2AB9"+ + "\u2ABA\u2ABB\u2ABC\u2ABD\u2ABE\u2ABF\u2AC0\u2AC1"+ + "\u2AC2\u2AC3\u2AC4\u2AC5\u2AC6\u2AC7\u2AC8\u2AC9"+ + "\u2ACA\u2ACB\u2ACC\u2ACD\u2ACE\u2ACF\u2AD0\u2AD1"+ + "\u2AD2\u2AD3\u2AD4\u2AD5\u2AD6\u2AD7\u2AD8\u2AD9"+ + "\u2ADA\u2ADB\u2ADC\u2ADD\u2ADE\u2ADF\u2AE0\u2AE1"+ + "\u2AE2\u2AE3\u2AE4\u2AE5\u2AE6\u2AE7\u2AE8\u2AE9"+ + "\u2AEA\u2AEB\u2AEC\u2AED\u2AEE\u2AEF\u2AF0\u2AF1"+ + "\u2AF2\u2AF3\u2AF4\u2AF5\u2AF6\u2AF7\u2AF8\u2AF9"+ + "\u2AFA\u2AFB\u2AFC\u2AFD\u2AFE\u2AFF\u2B00\u2B01"+ + "\u2B02\u2B03\u2B04\u2B05\u2B06\u2B07\u2B08\u2B09"+ + "\u2B0A\u2B0B\u2B0C\u2B0D\u2B0E\u2B0F\u2B10\u2B11"+ + "\u2B12\u2B13\u2B14\u2B15\u2B16\u2B17\u2B18\u2B19"+ + "\u2B1A\u2B1B\u2B1C\u2B1D\u2B1E\u2B1F\u2B20\u2B21"+ + "\u2B22\u2B23\u2B24\u2B25\u2B26\u2B27\u2B28\u2B29"+ + "\u2B2A\u2B2B\u2B2C\u2B2D\u2B2E\u2B2F\u2B30\u2B31"+ + "\u2B32\u2B33\u2B34\u2B35\u2B36\u2B37\u2B38\u2B39"+ + "\u2B3A\u2B3B\u2B3C\u2B3D\u2B3E\u2B3F\u2B40\u2B41"+ + "\u2B42\u2B43\u2B44\u2B45\u2B46\u2B47\u2B48\u2B49"+ + "\u2B4A\u2B4B\u2B4C\u2B4D\u2B4E\u2B4F\u2B50\u2B51"+ + "\u2B52\u2B53\u2B54\u2B55\u2B56\u2B57\u2B58\u2B59"+ + "\u2B5A\u2B5B\u2B5C\u2B5D\u2B5E\u2B5F\u2B60\u2B61"+ + "\u2B62\u2B63\u2B64\u2B65\u2B66\u2B67\u2B68\u2B69"+ + "\u2B6A\u2B6B\u2B6C\u2B6D\u2B6E\u2B6F\u2B70\u2B71"+ + "\u2B72\u2B73\u2B74\u2B75\u2B76\u2B77\u2B78\u2B79"+ + "\u2B7A\u2B7B\u2B7C\u2B7D\u2B7E\u2B7F\u2B80\u2B81"+ + "\u2B82\u2B83\u2B84\u2B85\u2B86\u2B87\u2B88\u2B89"+ + "\u2B8A\u2B8B\u2B8C\u2B8D\u2B8E\u2B8F\u2B90\u2B91"+ + "\u2B92\u2B93\u2B94\u2B95\u2B96\u2B97\u2B98\u2B99"+ + "\u2B9A\u2B9B\u2B9C\u2B9D\u2B9E\u2B9F\u2BA0\u2BA1"+ + "\u2BA2\u2BA3\u2BA4\u2BA5\u2BA6\u2BA7\u2BA8\u2BA9"+ + "\u2BAA\u2BAB\u2BAC\u2BAD\u2BAE\u2BAF\u2BB0\u2BB1"+ + "\u2BB2\u2BB3\u2BB4\u2BB5\u2BB6\u2BB7\u2BB8\u2BB9"+ + "\u2BBA\u2BBB\u2BBC\u2BBD\u2BBE\u2BBF\u2BC0\u2BC1"+ + "\u2BC2\u2BC3\u2BC4\u2BC5\u2BC6\u2BC7\u2BC8\u2BC9"+ + "\u2BCA\u2BCB\u2BCC\u2BCD\u2BCE\u2BCF\u2BD0\u2BD1"+ + "\u2BD2\u2BD3\u2BD4\u2BD5\u2BD6\u2BD7\u2BD8\u2BD9"+ + "\u2BDA\u2BDB\u2BDC\u2BDD\u2BDE\u2BDF\u2BE0\u2BE1"+ + "\u2BE2\u2BE3\u2BE4\u2BE5\u2BE6\u2BE7\u2BE8\u2BE9"+ + "\u2BEA\u2BEB\u2BEC\u2BED\u2BEE\u2BEF\u2BF0\u2BF1"+ + "\u2BF2\u2BF3\u2BF4\u2BF5\u2BF6\u2BF7\u2BF8\u2BF9"+ + "\u2BFA\u2BFB\u2BFC\u2BFD\u2BFE\u2BFF\u2C00\u2C01"+ + "\u2C02\u2C03\u2C04\u2C05\u2C06\u2C07\u2C08\u2C09"+ + "\u2C0A\u2C0B\u2C0C\u2C0D\u2C0E\u2C0F\u2C10\u2C11"+ + "\u2C12\u2C13\u2C14\u2C15\u2C16\u2C17\u2C18\u2C19"+ + "\u2C1A\u2C1B\u2C1C\u2C1D\u2C1E\u2C1F\u2C20\u2C21"+ + "\u2C22\u2C23\u2C24\u2C25\u2C26\u2C27\u2C28\u2C29"+ + "\u2C2A\u2C2B\u2C2C\u2C2D\u2C2E\u2C2F\u2C30\u2C31"+ + "\u2C32\u2C33\u2C34\u2C35\u2C36\u2C37\u2C38\u2C39"+ + "\u2C3A\u2C3B\u2C3C\u2C3D\u2C3E\u2C3F\u2C40\u2C41"+ + "\u2C42\u2C43\u2C44\u2C45\u2C46\u2C47\u2C48\u2C49"+ + "\u2C4A\u2C4B\u2C4C\u2C4D\u2C4E\u2C4F\u2C50\u2C51"+ + "\u2C52\u2C53\u2C54\u2C55\u2C56\u2C57\u2C58\u2C59"+ + "\u2C5A\u2C5B\u2C5C\u2C5D\u2C5E\u2C5F\u2C60\u2C61"+ + "\u2C62\u2C63\u2C64\u2C65\u2C66\u2C67\u2C68\u2C69"+ + "\u2C6A\u2C6B\u2C6C\u2C6D\u2C6E\u2C6F\u2C70\u2C71"+ + "\u2C72\u2C73\u2C74\u2C75\u2C76\u2C77\u2C78\u2C79"+ + "\u2C7A\u2C7B\u2C7C\u2C7D\u2C7E\u2C7F\u2C80\u2C81"+ + "\u2C82\u2C83\u2C84\u2C85\u2C86\u2C87\u2C88\u2C89"+ + "\u2C8A\u2C8B\u2C8C\u2C8D\u2C8E\u2C8F\u2C90\u2C91"+ + "\u2C92\u2C93\u2C94\u2C95\u2C96\u2C97\u2C98\u2C99"+ + "\u2C9A\u2C9B\u2C9C\u2C9D\u2C9E\u2C9F\u2CA0\u2CA1"+ + "\u2CA2\u2CA3\u2CA4\u2CA5\u2CA6\u2CA7\u2CA8\u2CA9"+ + "\u2CAA\u2CAB\u2CAC\u2CAD\u2CAE\u2CAF\u2CB0\u2CB1"+ + "\u2CB2\u2CB3\u2CB4\u2CB5\u2CB6\u2CB7\u2CB8\u2CB9"+ + "\u2CBA\u2CBB\u2CBC\u2CBD\u2CBE\u2CBF\u2CC0\u2CC1"+ + "\u2CC2\u2CC3\u2CC4\u2CC5\u2CC6\u2CC7\u2CC8\u2CC9"+ + "\u2CCA\u2CCB\u2CCC\u2CCD\u2CCE\u2CCF\u2CD0\u2CD1"+ + "\u2CD2\u2CD3\u2CD4\u2CD5\u2CD6\u2CD7\u2CD8\u2CD9"+ + "\u2CDA\u2CDB\u2CDC\u2CDD\u2CDE\u2CDF\u2CE0\u2CE1"+ + "\u2CE2\u2CE3\u2CE4\u2CE5\u2CE6\u2CE7\u2CE8\u2CE9"+ + "\u2CEA\u2CEB\u2CEC\u2CED\u2CEE\u2CEF\u2CF0\u2CF1"+ + "\u2CF2\u2CF3\u2CF4\u2CF5\u2CF6\u2CF7\u2CF8\u2CF9"+ + "\u2CFA\u2CFB\u2CFC\u2CFD\u2CFE\u2CFF\u2D00\u2D01"+ + "\u2D02\u2D03\u2D04\u2D05\u2D06\u2D07\u2D08\u2D09"+ + "\u2D0A\u2D0B\u2D0C\u2D0D\u2D0E\u2D0F\u2D10\u2D11"+ + "\u2D12\u2D13\u2D14\u2D15\u2D16\u2D17\u2D18\u2D19"+ + "\u2D1A\u2D1B\u2D1C\u2D1D\u2D1E\u2D1F\u2D20\u2D21"+ + "\u2D22\u2D23\u2D24\u2D25\u2D26\u2D27\u2D28\u2D29"+ + "\u2D2A\u2D2B\u2D2C\u2D2D\u2D2E\u2D2F\u2D30\u2D31"+ + "\u2D32\u2D33\u2D34\u2D35\u2D36\u2D37\u2D38\u2D39"+ + "\u2D3A\u2D3B\u2D3C\u2D3D\u2D3E\u2D3F\u2D40\u2D41"+ + "\u2D42\u2D43\u2D44\u2D45\u2D46\u2D47\u2D48\u2D49"+ + "\u2D4A\u2D4B\u2D4C\u2D4D\u2D4E\u2D4F\u2D50\u2D51"+ + "\u2D52\u2D53\u2D54\u2D55\u2D56\u2D57\u2D58\u2D59"+ + "\u2D5A\u2D5B\u2D5C\u2D5D\u2D5E\u2D5F\u2D60\u2D61"+ + "\u2D62\u2D63\u2D64\u2D65\u2D66\u2D67\u2D68\u2D69"+ + "\u2D6A\u2D6B\u2D6C\u2D6D\u2D6E\u2D6F\u2D70\u2D71"+ + "\u2D72\u2D73\u2D74\u2D75\u2D76\u2D77\u2D78\u2D79"+ + "\u2D7A\u2D7B\u2D7C\u2D7D\u2D7E\u2D7F\u2D80\u2D81"+ + "\u2D82\u2D83\u2D84\u2D85\u2D86\u2D87\u2D88\u2D89"+ + "\u2D8A\u2D8B\u2D8C\u2D8D\u2D8E\u2D8F\u2D90\u2D91"+ + "\u2D92\u2D93\u2D94\u2D95\u2D96\u2D97\u2D98\u2D99"+ + "\u2D9A\u2D9B\u2D9C\u2D9D\u2D9E\u2D9F\u2DA0\u2DA1"+ + "\u2DA2\u2DA3\u2DA4\u2DA5\u2DA6\u2DA7\u2DA8\u2DA9"+ + "\u2DAA\u2DAB\u2DAC\u2DAD\u2DAE\u2DAF\u2DB0\u2DB1"+ + "\u2DB2\u2DB3\u2DB4\u2DB5\u2DB6\u2DB7\u2DB8\u2DB9"+ + "\u2DBA\u2DBB\u2DBC\u2DBD\u2DBE\u2DBF\u2DC0\u2DC1"+ + "\u2DC2\u2DC3\u2DC4\u2DC5\u2DC6\u2DC7\u2DC8\u2DC9"+ + "\u2DCA\u2DCB\u2DCC\u2DCD\u2DCE\u2DCF\u2DD0\u2DD1"+ + "\u2DD2\u2DD3\u2DD4\u2DD5\u2DD6\u2DD7\u2DD8\u2DD9"+ + "\u2DDA\u2DDB\u2DDC\u2DDD\u2DDE\u2DDF\u2DE0\u2DE1"+ + "\u2DE2\u2DE3\u2DE4\u2DE5\u2DE6\u2DE7\u2DE8\u2DE9"+ + "\u2DEA\u2DEB\u2DEC\u2DED\u2DEE\u2DEF\u2DF0\u2DF1"+ + "\u2DF2\u2DF3\u2DF4\u2DF5\u2DF6\u2DF7\u2DF8\u2DF9"+ + "\u2DFA\u2DFB\u2DFC\u2DFD\u2DFE\u2DFF\u2E00\u2E01"+ + "\u2E02\u2E03\u2E04\u2E05\u2E06\u2E07\u2E08\u2E09"+ + "\u2E0A\u2E0B\u2E0C\u2E0D\u2E0E\u2E0F\u2E10\u2E11"+ + "\u2E12\u2E13\u2E14\u2E15\u2E16\u2E17\u2E18\u2E19"+ + "\u2E1A\u2E1B\u2E1C\u2E1D\u2E1E\u2E1F\u2E20\u2E21"+ + "\u2E22\u2E23\u2E24\u2E25\u2E26\u2E27\u2E28\u2E29"+ + "\u2E2A\u2E2B\u2E2C\u2E2D\u2E2E\u2E2F\u2E30\u2E31"+ + "\u2E32\u2E33\u2E34\u2E35\u2E36\u2E37\u2E38\u2E39"+ + "\u2E3A\u2E3B\u2E3C\u2E3D\u2E3E\u2E3F\u2E40\u2E41"+ + "\u2E42\u2E43\u2E44\u2E45\u2E46\u2E47\u2E48\u2E49"+ + "\u2E4A\u2E4B\u2E4C\u2E4D\u2E4E\u2E4F\u2E50\u2E51"+ + "\u2E52\u2E53\u2E54\u2E55\u2E56\u2E57\u2E58\u2E59"+ + "\u2E5A\u2E5B\u2E5C\u2E5D\u2E5E\u2E5F\u2E60\u2E61"+ + "\u2E62\u2E63\u2E64\u2E65\u2E66\u2E67\u2E68\u2E69"+ + "\u2E6A\u2E6B\u2E6C\u2E6D\u2E6E\u2E6F\u2E70\u2E71"+ + "\u2E72\u2E73\u2E74\u2E75\u2E76\u2E77\u2E78\u2E79"+ + "\u2E7A\u2E7B\u2E7C\u2E7D\u2E7E\u2E7F\u2E80\u2E81"+ + "\u2E82\u2E83\u2E84\u2E85\u2E86\u2E87\u2E88\u2E89"+ + "\u2E8A\u2E8B\u2E8C\u2E8D\u2E8E\u2E8F\u2E90\u2E91"+ + "\u2E92\u2E93\u2E94\u2E95\u2E96\u2E97\u2E98\u2E99"+ + "\u2E9A\u2E9B\u2E9C\u2E9D\u2E9E\u2E9F\u2EA0\u2EA1"+ + "\u2EA2\u2EA3\u2EA4\u2EA5\u2EA6\u2EA7\u2EA8\u2EA9"+ + "\u2EAA\u2EAB\u2EAC\u2EAD\u2EAE\u2EAF\u2EB0\u2EB1"+ + "\u2EB2\u2EB3\u2EB4\u2EB5\u2EB6\u2EB7\u2EB8\u2EB9"+ + "\u2EBA\u2EBB\u2EBC\u2EBD\u2EBE\u2EBF\u2EC0\u2EC1"+ + "\u2EC2\u2EC3\u2EC4\u2EC5\u2EC6\u2EC7\u2EC8\u2EC9"+ + "\u2ECA\u2ECB\u2ECC\u2ECD\u2ECE\u2ECF\u2ED0\u2ED1"+ + "\u2ED2\u2ED3\u2ED4\u2ED5\u2ED6\u2ED7\u2ED8\u2ED9"+ + "\u2EDA\u2EDB\u2EDC\u2EDD\u2EDE\u2EDF\u2EE0\u2EE1"; + + private final static String innerEncoderIndex1= + "\u2EE2\u2EE3\u2EE4\u2EE5\u2EE6\u2EE7\u2EE8\u2EE9"+ + "\u2EEA\u2EEB\u2EEC\u2EED\u2EEE\u2EEF\u2EF0\u2EF1"+ + "\u2EF2\u2EF3\u2EF4\u2EF5\u2EF6\u2EF7\u2EF8\u2EF9"+ + "\u2EFA\u2EFB\u2EFC\u2EFD\u2EFE\u2EFF\u2F00\u2F01"+ + "\u2F02\u2F03\u2F04\u2F05\u2F06\u2F07\u2F08\u2F09"+ + "\u2F0A\u2F0B\u2F0C\u2F0D\u2F0E\u2F0F\u2F10\u2F11"+ + "\u2F12\u2F13\u2F14\u2F15\u2F16\u2F17\u2F18\u2F19"+ + "\u2F1A\u2F1B\u2F1C\u2F1D\u2F1E\u2F1F\u2F20\u2F21"+ + "\u2F22\u2F23\u2F24\u2F25\u2F26\u2F27\u2F28\u2F29"+ + "\u2F2A\u2F2B\u2F2C\u2F2D\u2F2E\u2F2F\u2F30\u2F31"+ + "\u2F32\u2F33\u2F34\u2F35\u2F36\u2F37\u2F38\u2F39"+ + "\u2F3A\u2F3B\u2F3C\u2F3D\u2F3E\u2F3F\u2F40\u2F41"+ + "\u2F42\u2F43\u2F44\u2F45\u2F46\u2F47\u2F48\u2F49"+ + "\u2F4A\u2F4B\u2F4C\u2F4D\u2F4E\u2F4F\u2F50\u2F51"+ + "\u2F52\u2F53\u2F54\u2F55\u2F56\u2F57\u2F58\u2F59"+ + "\u2F5A\u2F5B\u2F5C\u2F5D\u2F5E\u2F5F\u2F60\u2F61"+ + "\u2F62\u2F63\u2F64\u2F65\u2F66\u2F67\u2F68\u2F69"+ + "\u2F6A\u2F6B\u2F6C\u2F6D\u2F6E\u2F6F\u2F70\u2F71"+ + "\u2F72\u2F73\u2F74\u2F75\u2F76\u2F77\u2F78\u2F79"+ + "\u2F7A\u2F7B\u2F7C\u2F7D\u2F7E\u2F7F\u2F80\u2F81"+ + "\u2F82\u2F83\u2F84\u2F85\u2F86\u2F87\u2F88\u2F89"+ + "\u2F8A\u2F8B\u2F8C\u2F8D\u2F8E\u2F8F\u2F90\u2F91"+ + "\u2F92\u2F93\u2F94\u2F95\u2F96\u2F97\u2F98\u2F99"+ + "\u2F9A\u2F9B\u2F9C\u2F9D\u2F9E\u2F9F\u2FA0\u2FA1"+ + "\u2FA2\u2FA3\u2FA4\u2FA5\u2FA6\u2FA7\u2FA8\u2FA9"+ + "\u2FAA\u2FAB\u2FAC\u2FAD\u2FAE\u2FAF\u2FB0\u2FB1"+ + "\u2FB2\u2FB3\u2FB4\u2FB5\u2FB6\u2FB7\u2FB8\u2FB9"+ + "\u2FBA\u2FBB\u2FBC\u2FBD\u2FBE\u2FBF\u2FC0\u2FC1"+ + "\u2FC2\u2FC3\u2FC4\u2FC5\u2FC6\u2FC7\u2FC8\u2FC9"+ + "\u2FCA\u2FCB\u2FCC\u2FCD\u2FCE\u2FCF\u2FD0\u2FD1"+ + "\u2FD2\u2FD3\u2FD4\u2FD5\u2FD6\u2FD7\u2FD8\u2FD9"+ + "\u2FDA\u2FDB\u2FDC\u2FDD\u2FDE\u2FDF\u2FE0\u2FE1"+ + "\u2FE2\u2FE3\u2FE4\u2FE5\u2FE6\u2FE7\u2FE8\u2FE9"+ + "\u2FEA\u2FEB\u2FEC\u2FED\u2FEE\u2FEF\u2FF0\u2FF1"+ + "\u2FF2\u2FF3\u2FF4\u2FF5\u2FF6\u2FF7\u2FF8\u2FF9"+ + "\u2FFA\u2FFB\u2FFC\u2FFD\u2FFE\u2FFF\u3000\u3001"+ + "\u3002\u3003\u3004\u3005\u3006\u3007\u3008\u3009"+ + "\u300A\u300B\u300C\u300D\u300E\u300F\u3010\u3011"+ + "\u3012\u3013\u3014\u3015\u3016\u3017\u3018\u3019"+ + "\u301A\u301B\u301C\u301D\u301E\u301F\u3020\u3021"+ + "\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029"+ + "\u302A\u302B\u302C\u302D\u302E\u302F\u3030\u3031"+ + "\u3032\u3033\u3034\u3035\u3036\u3037\u3038\u3039"+ + "\u303A\u303B\u303C\u303D\u303E\u303F\u3040\u3041"+ + "\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049"+ + "\u304A\u304B\u304C\u304D\u304E\u304F\u3050\u3051"+ + "\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059"+ + "\u305A\u305B\u305C\u305D\u305E\u305F\u3060\u3061"+ + "\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069"+ + "\u306A\u306B\u306C\u306D\u306E\u306F\u3070\u3071"+ + "\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079"+ + "\u307A\u307B\u307C\u307D\u307E\u307F\u3080\u3081"+ + "\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089"+ + "\u308A\u308B\u308C\u308D\u308E\u308F\u3090\u3091"+ + "\u3092\u3093\u3094\u3095\u3096\u3097\u3098\u3099"+ + "\u309A\u309B\u309C\u309D\u309E\u309F\u30A0\u30A1"+ + "\u30A2\u30A3\u30A4\u30A5\u30A6\u30A7\u30A8\u30A9"+ + "\u30AA\u30AB\u30AC\u30AD\u30AE\u30AF\u30B0\u30B1"+ + "\u30B2\u30B3\u30B4\u30B5\u30B6\u30B7\u30B8\u30B9"+ + "\u30BA\u30BB\u30BC\u30BD\u30BE\u30BF\u30C0\u30C1"+ + "\u30C2\u30C3\u30C4\u30C5\u30C6\u30C7\u30C8\u30C9"+ + "\u30CA\u30CB\u30CC\u30CD\u30CE\u30CF\u30D0\u30D1"+ + "\u30D2\u30D3\u30D4\u30D5\u30D6\u30D7\u30D8\u30D9"+ + "\u30DA\u30DB\u30DC\u30DD\u30DE\u30DF\u30E0\u30E1"+ + "\u30E2\u30E3\u30E4\u30E5\u30E6\u30E7\u30E8\u30E9"+ + "\u30EA\u30EB\u30EC\u30ED\u30EE\u30EF\u30F0\u30F1"+ + "\u30F2\u30F3\u30F4\u30F5\u30F6\u30F7\u30F8\u30F9"+ + "\u30FA\u30FB\u30FC\u30FD\u30FE\u30FF\u3100\u3101"+ + "\u3102\u3103\u3104\u3105\u3106\u3107\u3108\u3109"+ + "\u310A\u310B\u310C\u310D\u310E\u310F\u3110\u3111"+ + "\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119"+ + "\u311A\u311B\u311C\u311D\u311E\u311F\u3120\u3121"+ + "\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129"+ + "\u312A\u312B\u312C\u312D\u312E\u312F\u3130\u3131"+ + "\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139"+ + "\u313A\u313B\u313C\u313D\u313E\u313F\u3140\u3141"+ + "\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149"+ + "\u314A\u314B\u314C\u314D\u314E\u314F\u3150\u3151"+ + "\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159"+ + "\u315A\u315B\u315C\u315D\u315E\u315F\u3160\u3161"+ + "\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169"+ + "\u316A\u316B\u316C\u316D\u316E\u316F\u3170\u3171"+ + "\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179"+ + "\u317A\u317B\u317C\u317D\u317E\u317F\u3180\u3181"+ + "\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189"+ + "\u318A\u318B\u318C\u318D\u318E\u318F\u3190\u3191"+ + "\u3192\u3193\u3194\u3195\u3196\u3197\u3198\u3199"+ + "\u319A\u319B\u319C\u319D\u319E\u319F\u31A0\u31A1"+ + "\u31A2\u31A3\u31A4\u31A5\u31A6\u31A7\u31A8\u31A9"+ + "\u31AA\u31AB\u31AC\u31AD\u31AE\u31AF\u31B0\u31B1"+ + "\u31B2\u31B3\u31B4\u31B5\u31B6\u31B7\u31B8\u31B9"+ + "\u31BA\u31BB\u31BC\u31BD\u31BE\u31BF\u31C0\u31C1"+ + "\u31C2\u31C3\u31C4\u31C5\u31C6\u31C7\u31C8\u31C9"+ + "\u31CA\u31CB\u31CC\u31CD\u31CE\u31CF\u31D0\u31D1"+ + "\u31D2\u31D3\u31D4\u31D5\u31D6\u31D7\u31D8\u31D9"+ + "\u31DA\u31DB\u31DC\u31DD\u31DE\u31DF\u31E0\u31E1"+ + "\u31E2\u31E3\u31E4\u31E5\u31E6\u31E7\u31E8\u31E9"+ + "\u31EA\u31EB\u31EC\u31ED\u31EE\u31EF\u31F0\u31F1"+ + "\u31F2\u31F3\u31F4\u31F5\u31F6\u31F7\u31F8\u31F9"+ + "\u31FA\u31FB\u31FC\u31FD\u31FE\u31FF\u3200\u3201"+ + "\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209"+ + "\u320A\u320B\u320C\u320D\u320E\u320F\u3210\u3211"+ + "\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219"+ + "\u321A\u321B\u321C\u321D\u321E\u321F\u3220\u3221"+ + "\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229"+ + "\u322A\u322B\u322C\u322D\u322E\u322F\u3230\u3231"+ + "\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239"+ + "\u323A\u323B\u323C\u323D\u323E\u323F\u3240\u3241"+ + "\u3242\u3243\u3244\u3245\u3246\u3247\u3248\u3249"+ + "\u324A\u324B\u324C\u324D\u324E\u324F\u3250\u3251"+ + "\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259"+ + "\u325A\u325B\u325C\u325D\u325E\u325F\u3260\u3261"+ + "\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269"+ + "\u326A\u326B\u326C\u326D\u326E\u326F\u3270\u3271"+ + "\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279"+ + "\u327A\u327B\u327C\u327D\u327E\u327F\u3280\u3281"+ + "\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289"+ + "\u328A\u328B\u328C\u328D\u328E\u328F\u3290\u3291"+ + "\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299"+ + "\u329A\u329B\u329C\u329D\u329E\u329F\u32A0\u32A1"+ + "\u32A2\u32A3\u32A4\u32A5\u32A6\u32A7\u32A8\u32A9"+ + "\u32AA\u32AB\u32AC\u32AD\u32AE\u32AF\u32B0\u32B1"+ + "\u32B2\u32B3\u32B4\u32B5\u32B6\u32B7\u32B8\u32B9"+ + "\u32BA\u32BB\u32BC\u32BD\u32BE\u32BF\u32C0\u32C1"+ + "\u32C2\u32C3\u32C4\u32C5\u32C6\u32C7\u32C8\u32C9"+ + "\u32CA\u32CB\u32CC\u32CD\u32CE\u32CF\u32D0\u32D1"+ + "\u32D2\u32D3\u32D4\u32D5\u32D6\u32D7\u32D8\u32D9"+ + "\u32DA\u32DB\u32DC\u32DD\u32DE\u32DF\u32E0\u32E1"+ + "\u32E2\u32E3\u32E4\u32E5\u32E6\u32E7\u32E8\u32E9"+ + "\u32EA\u32EB\u32EC\u32ED\u32EE\u32EF\u32F0\u32F1"+ + "\u32F2\u32F3\u32F4\u32F5\u32F6\u32F7\u32F8\u32F9"+ + "\u32FA\u32FB\u32FC\u32FD\u32FE\u32FF\u3300\u3301"+ + "\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309"+ + "\u330A\u330B\u330C\u330D\u330E\u330F\u3310\u3311"+ + "\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319"+ + "\u331A\u331B\u331C\u331D\u331E\u331F\u3320\u3321"+ + "\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329"+ + "\u332A\u332B\u332C\u332D\u332E\u332F\u3330\u3331"+ + "\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339"+ + "\u333A\u333B\u333C\u333D\u333E\u333F\u3340\u3341"+ + "\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349"+ + "\u334A\u334B\u334C\u334D\u334E\u334F\u3350\u3351"+ + "\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359"+ + "\u335A\u335B\u335C\u335D\u335E\u335F\u3360\u3361"+ + "\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369"+ + "\u336A\u336B\u336C\u336D\u336E\u336F\u3370\u3371"+ + "\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379"+ + "\u337A\u337B\u337C\u337D\u337E\u337F\u3380\u3381"+ + "\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389"+ + "\u338A\u338B\u338C\u338D\u338E\u338F\u3390\u3391"+ + "\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399"+ + "\u339A\u339B\u339C\u339D\u339E\u339F\u33A0\u33A1"+ + "\u33A2\u33A3\u33A4\u33A5\u33A6\u33A7\u33A8\u33A9"+ + "\u33AA\u33AB\u33AC\u33AD\u33AE\u33AF\u33B0\u33B1"+ + "\u33B2\u33B3\u33B4\u33B5\u33B6\u33B7\u33B8\u33B9"+ + "\u33BA\u33BB\u33BC\u33BD\u33BE\u33BF\u33C0\u33C1"+ + "\u33C2\u33C3\u33C4\u33C5\u33C6\u33C7\u33C8\u33C9"+ + "\u33CA\u33CB\u33CC\u33CD\u33CE\u33CF\u33D0\u33D1"+ + "\u33D2\u33D3\u33D4\u33D5\u33D6\u33D7\u33D8\u33D9"+ + "\u33DA\u33DB\u33DC\u33DD\u33DE\u33DF\u33E0\u33E1"+ + "\u33E2\u33E3\u33E4\u33E5\u33E6\u33E7\u33E8\u33E9"+ + "\u33EA\u33EB\u33EC\u33ED\u33EE\u33EF\u33F0\u33F1"+ + "\u33F2\u33F3\u33F4\u33F5\u33F6\u33F7\u33F8\u33F9"+ + "\u33FA\u33FB\u33FC\u33FD\u33FE\u33FF\u3400\u3401"+ + "\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409"+ + "\u340A\u340B\u340C\u340D\u340E\u340F\u3410\u3411"+ + "\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419"+ + "\u341A\u341B\u341C\u341D\u341E\u341F\u3420\u3421"+ + "\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429"+ + "\u342A\u342B\u342C\u342D\u342E\u342F\u3430\u3431"+ + "\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439"+ + "\u343A\u343B\u343C\u343D\u343E\u343F\u3440\u3441"+ + "\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449"+ + "\u344A\u344B\u344C\u344D\u344E\u344F\u3450\u3451"+ + "\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459"+ + "\u345A\u345B\u345C\u345D\u345E\u345F\u3460\u3461"+ + "\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469"+ + "\u346A\u346B\u346C\u346D\u346E\u346F\u3470\u3471"+ + "\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479"+ + "\u347A\u347B\u347C\u347D\u347E\u347F\u3480\u3481"+ + "\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489"+ + "\u348A\u348B\u348C\u348D\u348E\u348F\u3490\u3491"+ + "\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499"+ + "\u349A\u349B\u349C\u349D\u349E\u349F\u34A0\u34A1"+ + "\u34A2\u34A3\u34A4\u34A5\u34A6\u34A7\u34A8\u34A9"+ + "\u34AA\u34AB\u34AC\u34AD\u34AE\u34AF\u34B0\u34B1"+ + "\u34B2\u34B3\u34B4\u34B5\u34B6\u34B7\u34B8\u34B9"+ + "\u34BA\u34BB\u34BC\u34BD\u34BE\u34BF\u34C0\u34C1"+ + "\u34C2\u34C3\u34C4\u34C5\u34C6\u34C7\u34C8\u34C9"+ + "\u34CA\u34CB\u34CC\u34CD\u34CE\u34CF\u34D0\u34D1"+ + "\u34D2\u34D3\u34D4\u34D5\u34D6\u34D7\u34D8\u34D9"+ + "\u34DA\u34DB\u34DC\u34DD\u34DE\u34DF\u34E0\u34E1"+ + "\u34E2\u34E3\u34E4\u34E5\u34E6\u34E7\u34E8\u34E9"+ + "\u34EA\u34EB\u34EC\u34ED\u34EE\u34EF\u34F0\u34F1"+ + "\u34F2\u34F3\u34F4\u34F5\u34F6\u34F7\u34F8\u34F9"+ + "\u34FA\u34FB\u34FC\u34FD\u34FE\u34FF\u3500\u3501"+ + "\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509"+ + "\u350A\u350B\u350C\u350D\u350E\u350F\u3510\u3511"+ + "\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519"+ + "\u351A\u351B\u351C\u351D\u351E\u351F\u3520\u3521"+ + "\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529"+ + "\u352A\u352B\u352C\u352D\u352E\u352F\u3530\u3531"+ + "\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539"+ + "\u353A\u353B\u353C\u353D\u353E\u353F\u3540\u3541"+ + "\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549"+ + "\u354A\u354B\u354C\u354D\u354E\u354F\u3550\u3551"+ + "\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559"+ + "\u355A\u355B\u355C\u355D\u355E\u355F\u3560\u3561"+ + "\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569"+ + "\u356A\u356B\u356C\u356D\u356E\u356F\u3570\u3571"+ + "\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579"+ + "\u357A\u357B\u357C\u357D\u357E\u357F\u3580\u3581"+ + "\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589"+ + "\u358A\u358B\u358C\u358D\u358E\u358F\u3590\u3591"+ + "\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599"+ + "\u359A\u359B\u359C\u359D\u359E\u359F\u35A0\u35A1"+ + "\u35A2\u35A3\u35A4\u35A5\u35A6\u35A7\u35A8\u35A9"+ + "\u35AA\u35AB\u35AC\u35AD\u35AE\u35AF\u35B0\u35B1"+ + "\u35B2\u35B3\u35B4\u35B5\u35B6\u35B7\u35B8\u35B9"+ + "\u35BA\u35BB\u35BC\u35BD\u35BE\u35BF\u35C0\u35C1"+ + "\u35C2\u35C3\u35C4\u35C5\u35C6\u35C7\u35C8\u35C9"+ + "\u35CA\u35CB\u35CC\u35CD\u35CE\u35CF\u35D0\u35D1"+ + "\u35D2\u35D3\u35D4\u35D5\u35D6\u35D7\u35D8\u35D9"+ + "\u35DA\u35DB\u35DC\u35DD\u35DE\u35DF\u35E0\u35E1"+ + "\u35E2\u35E3\u35E4\u35E5\u35E6\u35E7\u35E8\u35E9"+ + "\u35EA\u35EB\u35EC\u35ED\u35EE\u35EF\u35F0\u35F1"+ + "\u35F2\u35F3\u35F4\u35F5\u35F6\u35F7\u35F8\u35F9"+ + "\u35FA\u35FB\u35FC\u35FD\u35FE\u35FF\u3600\u3601"+ + "\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609"+ + "\u360A\u360B\u360C\u360D\u360E\u360F\u3610\u3611"+ + "\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619"+ + "\u361A\u361B\u361C\u361D\u361E\u361F\u3620\u3621"+ + "\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629"+ + "\u362A\u362B\u362C\u362D\u362E\u362F\u3630\u3631"+ + "\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639"+ + "\u363A\u363B\u363C\u363D\u363E\u363F\u3640\u3641"+ + "\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649"+ + "\u364A\u364B\u364C\u364D\u364E\u364F\u3650\u3651"+ + "\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659"+ + "\u365A\u365B\u365C\u365D\u365E\u365F\u3660\u3661"+ + "\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669"+ + "\u366A\u366B\u366C\u366D\u366E\u366F\u3670\u3671"+ + "\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679"+ + "\u367A\u367B\u367C\u367D\u367E\u367F\u3680\u3681"+ + "\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689"+ + "\u368A\u368B\u368C\u368D\u368E\u368F\u3690\u3691"+ + "\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699"+ + "\u369A\u369B\u369C\u369D\u369E\u369F\u36A0\u36A1"+ + "\u36A2\u36A3\u36A4\u36A5\u36A6\u36A7\u36A8\u36A9"+ + "\u36AA\u36AB\u36AC\u36AD\u36AE\u36AF\u36B0\u36B1"+ + "\u36B2\u36B3\u36B4\u36B5\u36B6\u36B7\u36B8\u36B9"+ + "\u36BA\u36BB\u36BC\u36BD\u36BE\u36BF\u36C0\u36C1"+ + "\u36C2\u36C3\u36C4\u36C5\u36C6\u36C7\u36C8\u36C9"+ + "\u36CA\u36CB\u36CC\u36CD\u36CE\u36CF\u36D0\u36D1"+ + "\u36D2\u36D3\u36D4\u36D5\u36D6\u36D7\u36D8\u36D9"+ + "\u36DA\u36DB\u36DC\u36DD\u36DE\u36DF\u36E0\u36E1"+ + "\u36E2\u36E3\u36E4\u36E5\u36E6\u36E7\u36E8\u36E9"+ + "\u36EA\u36EB\u36EC\u36ED\u36EE\u36EF\u36F0\u36F1"+ + "\u36F2\u36F3\u36F4\u36F5\u36F6\u36F7\u36F8\u36F9"+ + "\u36FA\u36FB\u36FC\u36FD\u36FE\u36FF\u3700\u3701"+ + "\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709"+ + "\u370A\u370B\u370C\u370D\u370E\u370F\u3710\u3711"+ + "\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719"+ + "\u371A\u371B\u371C\u371D\u371E\u371F\u3720\u3721"+ + "\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729"+ + "\u372A\u372B\u372C\u372D\u372E\u372F\u3730\u3731"+ + "\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739"+ + "\u373A\u373B\u373C\u373D\u373E\u373F\u3740\u3741"+ + "\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749"+ + "\u374A\u374B\u374C\u374D\u374E\u374F\u3750\u3751"+ + "\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759"+ + "\u375A\u375B\u375C\u375D\u375E\u375F\u3760\u3761"+ + "\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769"+ + "\u376A\u376B\u376C\u376D\u376E\u376F\u3770\u3771"+ + "\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779"+ + "\u377A\u377B\u377C\u377D\u377E\u377F\u3780\u3781"+ + "\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789"+ + "\u378A\u378B\u378C\u378D\u378E\u378F\u3790\u3791"+ + "\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799"+ + "\u379A\u379B\u379C\u379D\u379E\u379F\u37A0\u37A1"+ + "\u37A2\u37A3\u37A4\u37A5\u37A6\u37A7\u37A8\u37A9"+ + "\u37AA\u37AB\u37AC\u37AD\u37AE\u37AF\u37B0\u37B1"+ + "\u37B2\u37B3\u37B4\u37B5\u37B6\u37B7\u37B8\u37B9"+ + "\u37BA\u37BB\u37BC\u37BD\u37BE\u37BF\u37C0\u37C1"+ + "\u37C2\u37C3\u37C4\u37C5\u37C6\u37C7\u37C8\u37C9"+ + "\u37CA\u37CB\u37CC\u37CD\u37CE\u37CF\u37D0\u37D1"+ + "\u37D2\u37D3\u37D4\u37D5\u37D6\u37D7\u37D8\u37D9"+ + "\u37DA\u37DB\u37DC\u37DD\u37DE\u37DF\u37E0\u37E1"+ + "\u37E2\u37E3\u37E4\u37E5\u37E6\u37E7\u37E8\u37E9"+ + "\u37EA\u37EB\u37EC\u37ED\u37EE\u37EF\u37F0\u37F1"+ + "\u37F2\u37F3\u37F4\u37F5\u37F6\u37F7\u37F8\u37F9"+ + "\u37FA\u37FB\u37FC\u37FD\u37FE\u37FF\u3800\u3801"+ + "\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809"+ + "\u380A\u380B\u380C\u380D\u380E\u380F\u3810\u3811"+ + "\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819"+ + "\u381A\u381B\u381C\u381D\u381E\u381F\u3820\u3821"+ + "\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829"+ + "\u382A\u382B\u382C\u382D\u382E\u382F\u3830\u3831"+ + "\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839"+ + "\u383A\u383B\u383C\u383D\u383E\u383F\u3840\u3841"+ + "\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849"+ + "\u384A\u384B\u384C\u384D\u384E\u384F\u3850\u3851"+ + "\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859"+ + "\u385A\u385B\u385C\u385D\u385E\u385F\u3860\u3861"+ + "\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869"+ + "\u386A\u386B\u386C\u386D\u386E\u386F\u3870\u3871"+ + "\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879"+ + "\u387A\u387B\u387C\u387D\u387E\u387F\u3880\u3881"+ + "\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889"+ + "\u388A\u388B\u388C\u388D\u388E\u388F\u3890\u3891"+ + "\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899"+ + "\u389A\u389B\u389C\u389D\u389E\u389F\u38A0\u38A1"+ + "\u38A2\u38A3\u38A4\u38A5\u38A6\u38A7\u38A8\u38A9"+ + "\u38AA\u38AB\u38AC\u38AD\u38AE\u38AF\u38B0\u38B1"+ + "\u38B2\u38B3\u38B4\u38B5\u38B6\u38B7\u38B8\u38B9"+ + "\u38BA\u38BB\u38BC\u38BD\u38BE\u38BF\u38C0\u38C1"+ + "\u38C2\u38C3\u38C4\u38C5\u38C6\u38C7\u38C8\u38C9"+ + "\u38CA\u38CB\u38CC\u38CD\u38CE\u38CF\u38D0\u38D1"+ + "\u38D2\u38D3\u38D4\u38D5\u38D6\u38D7\u38D8\u38D9"+ + "\u38DA\u38DB\u38DC\u38DD\u38DE\u38DF\u38E0\u38E1"+ + "\u38E2\u38E3\u38E4\u38E5\u38E6\u38E7\u38E8\u38E9"+ + "\u38EA\u38EB\u38EC\u38ED\u38EE\u38EF\u38F0\u38F1"+ + "\u38F2\u38F3\u38F4\u38F5\u38F6\u38F7\u38F8\u38F9"+ + "\u38FA\u38FB\u38FC\u38FD\u38FE\u38FF\u3900\u3901"+ + "\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909"+ + "\u390A\u390B\u390C\u390D\u390E\u390F\u3910\u3911"+ + "\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919"+ + "\u391A\u391B\u391C\u391D\u391E\u391F\u3920\u3921"+ + "\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929"+ + "\u392A\u392B\u392C\u392D\u392E\u392F\u3930\u3931"+ + "\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939"+ + "\u393A\u393B\u393C\u393D\u393E\u393F\u3940\u3941"+ + "\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949"+ + "\u394A\u394B\u394C\u394D\u394E\u394F\u3950\u3951"+ + "\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959"+ + "\u395A\u395B\u395C\u395D\u395E\u395F\u3960\u3961"+ + "\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969"+ + "\u396A\u396B\u396C\u396D\u396E\u396F\u3970\u3971"+ + "\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979"+ + "\u397A\u397B\u397C\u397D\u397E\u397F\u3980\u3981"+ + "\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989"+ + "\u398A\u398B\u398C\u398D\u398E\u398F\u3990\u3991"+ + "\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999"+ + "\u399A\u399B\u399C\u399D\u399E\u399F\u39A0\u39A1"+ + "\u39A2\u39A3\u39A4\u39A5\u39A6\u39A7\u39A8\u39A9"+ + "\u39AA\u39AB\u39AC\u39AD\u39AE\u39AF\u39B0\u39B1"+ + "\u39B2\u39B3\u39B4\u39B5\u39B6\u39B7\u39B8\u39B9"+ + "\u39BA\u39BB\u39BC\u39BD\u39BE\u39BF\u39C0\u39C1"+ + "\u39C2\u39C3\u39C4\u39C5\u39C6\u39C7\u39C8\u39C9"+ + "\u39CA\u39CB\u39CC\u39CD\u39CE\u39CF\u39D0\u39D1"+ + "\u39D2\u39D3\u39D4\u39D5\u39D6\u39D7\u39D8\u39D9"+ + "\u39DA\u39DB\u39DC\u39DD\u39DE\u39DF\u39E0\u39E1"+ + "\u39E2\u39E3\u39E4\u39E5\u39E6\u39E7\u39E8\u39E9"+ + "\u39EA\u39EB\u39EC\u39ED\u39EE\u39EF\u39F0\u39F1"+ + "\u39F2\u39F3\u39F4\u39F5\u39F6\u39F7\u39F8\u39F9"+ + "\u39FA\u39FB\u39FC\u39FD\u39FE\u39FF\u3A00\u3A01"+ + "\u3A02\u3A03\u3A04\u3A05\u3A06\u3A07\u3A08\u3A09"+ + "\u3A0A\u3A0B\u3A0C\u3A0D\u3A0E\u3A0F\u3A10\u3A11"+ + "\u3A12\u3A13\u3A14\u3A15\u3A16\u3A17\u3A18\u3A19"+ + "\u3A1A\u3A1B\u3A1C\u3A1D\u3A1E\u3A1F\u3A20\u3A21"+ + "\u3A22\u3A23\u3A24\u3A25\u3A26\u3A27\u3A28\u3A29"+ + "\u3A2A\u3A2B\u3A2C\u3A2D\u3A2E\u3A2F\u3A30\u3A31"+ + "\u3A32\u3A33\u3A34\u3A35\u3A36\u3A37\u3A38\u3A39"+ + "\u3A3A\u3A3B\u3A3C\u3A3D\u3A3E\u3A3F\u3A40\u3A41"+ + "\u3A42\u3A43\u3A44\u3A45\u3A46\u3A47\u3A48\u3A49"+ + "\u3A4A\u3A4B\u3A4C\u3A4D\u3A4E\u3A4F\u3A50\u3A51"+ + "\u3A52\u3A53\u3A54\u3A55\u3A56\u3A57\u3A58\u3A59"+ + "\u3A5A\u3A5B\u3A5C\u3A5D\u3A5E\u3A5F\u3A60\u3A61"+ + "\u3A62\u3A63\u3A64\u3A65\u3A66\u3A67\u3A68\u3A69"+ + "\u3A6A\u3A6B\u3A6C\u3A6D\u3A6E\u3A6F\u3A70\u3A71"+ + "\u3A72\u3A73\u3A74\u3A75\u3A76\u3A77\u3A78\u3A79"+ + "\u3A7A\u3A7B\u3A7C\u3A7D\u3A7E\u3A7F\u3A80\u3A81"+ + "\u3A82\u3A83\u3A84\u3A85\u3A86\u3A87\u3A88\u3A89"+ + "\u3A8A\u3A8B\u3A8C\u3A8D\u3A8E\u3A8F\u3A90\u3A91"+ + "\u3A92\u3A93\u3A94\u3A95\u3A96\u3A97\u3A98\u3A99"+ + "\u3A9A\u3A9B\u3A9C\u3A9D\u3A9E\u3A9F\u3AA0\u3AA1"+ + "\u3AA2\u3AA3\u3AA4\u3AA5\u3AA6\u3AA7\u3AA8\u3AA9"+ + "\u3AAA\u3AAB\u3AAC\u3AAD\u3AAE\u3AAF\u3AB0\u3AB1"+ + "\u3AB2\u3AB3\u3AB4\u3AB5\u3AB6\u3AB7\u3AB8\u3AB9"+ + "\u3ABA\u3ABB\u3ABC\u3ABD\u3ABE\u3ABF\u3AC0\u3AC1"+ + "\u3AC2\u3AC3\u3AC4\u3AC5\u3AC6\u3AC7\u3AC8\u3AC9"+ + "\u3ACA\u3ACB\u3ACC\u3ACD\u3ACE\u3ACF\u3AD0\u3AD1"+ + "\u3AD2\u3AD3\u3AD4\u3AD5\u3AD6\u3AD7\u3AD8\u3AD9"+ + "\u3ADA\u3ADB\u3ADC\u3ADD\u3ADE\u3ADF\u3AE0\u3AE1"+ + "\u3AE2\u3AE3\u3AE4\u3AE5\u3AE6\u3AE7\u3AE8\u3AE9"+ + "\u3AEA\u3AEB\u3AEC\u3AED\u3AEE\u3AEF\u3AF0\u3AF1"+ + "\u3AF2\u3AF3\u3AF4\u3AF5\u3AF6\u3AF7\u3AF8\u3AF9"+ + "\u3AFA\u3AFB\u3AFC\u3AFD\u3AFE\u3AFF\u3B00\u3B01"+ + "\u3B02\u3B03\u3B04\u3B05\u3B06\u3B07\u3B08\u3B09"+ + "\u3B0A\u3B0B\u3B0C\u3B0D\u3B0E\u3B0F\u3B10\u3B11"+ + "\u3B12\u3B13\u3B14\u3B15\u3B16\u3B17\u3B18\u3B19"+ + "\u3B1A\u3B1B\u3B1C\u3B1D\u3B1E\u3B1F\u3B20\u3B21"+ + "\u3B22\u3B23\u3B24\u3B25\u3B26\u3B27\u3B28\u3B29"+ + "\u3B2A\u3B2B\u3B2C\u3B2D\u3B2E\u3B2F\u3B30\u3B31"+ + "\u3B32\u3B33\u3B34\u3B35\u3B36\u3B37\u3B38\u3B39"+ + "\u3B3A\u3B3B\u3B3C\u3B3D\u3B3E\u3B3F\u3B40\u3B41"+ + "\u3B42\u3B43\u3B44\u3B45\u3B46\u3B47\u3B48\u3B49"+ + "\u3B4A\u3B4B\u3B4C\u3B4D\u3B4E\u3B4F\u3B50\u3B51"+ + "\u3B52\u3B53\u3B54\u3B55\u3B56\u3B57\u3B58\u3B59"+ + "\u3B5A\u3B5B\u3B5C\u3B5D\u3B5E\u3B5F\u3B60\u3B61"+ + "\u3B62\u3B63\u3B64\u3B65\u3B66\u3B67\u3B68\u3B69"+ + "\u3B6A\u3B6B\u3B6C\u3B6D\u3B6E\u3B6F\u3B70\u3B71"+ + "\u3B72\u3B73\u3B74\u3B75\u3B76\u3B77\u3B78\u3B79"+ + "\u3B7A\u3B7B\u3B7C\u3B7D\u3B7E\u3B7F\u3B80\u3B81"+ + "\u3B82\u3B83\u3B84\u3B85\u3B86\u3B87\u3B88\u3B89"+ + "\u3B8A\u3B8B\u3B8C\u3B8D\u3B8E\u3B8F\u3B90\u3B91"+ + "\u3B92\u3B93\u3B94\u3B95\u3B96\u3B97\u3B98\u3B99"+ + "\u3B9A\u3B9B\u3B9C\u3B9D\u3B9E\u3B9F\u3BA0\u3BA1"+ + "\u3BA2\u3BA3\u3BA4\u3BA5\u3BA6\u3BA7\u3BA8\u3BA9"+ + "\u3BAA\u3BAB\u3BAC\u3BAD\u3BAE\u3BAF\u3BB0\u3BB1"+ + "\u3BB2\u3BB3\u3BB4\u3BB5\u3BB6\u3BB7\u3BB8\u3BB9"+ + "\u3BBA\u3BBB\u3BBC\u3BBD\u3BBE\u3BBF\u3BC0\u3BC1"+ + "\u3BC2\u3BC3\u3BC4\u3BC5\u3BC6\u3BC7\u3BC8\u3BC9"+ + "\u3BCA\u3BCB\u3BCC\u3BCD\u3BCE\u3BCF\u3BD0\u3BD1"+ + "\u3BD2\u3BD3\u3BD4\u3BD5\u3BD6\u3BD7\u3BD8\u3BD9"+ + "\u3BDA\u3BDB\u3BDC\u3BDD\u3BDE\u3BDF\u3BE0\u3BE1"+ + "\u3BE2\u3BE3\u3BE4\u3BE5\u3BE6\u3BE7\u3BE8\u3BE9"+ + "\u3BEA\u3BEB\u3BEC\u3BED\u3BEE\u3BEF\u3BF0\u3BF1"+ + "\u3BF2\u3BF3\u3BF4\u3BF5\u3BF6\u3BF7\u3BF8\u3BF9"+ + "\u3BFA\u3BFB\u3BFC\u3BFD\u3BFE\u3BFF\u3C00\u3C01"+ + "\u3C02\u3C03\u3C04\u3C05\u3C06\u3C07\u3C08\u3C09"+ + "\u3C0A\u3C0B\u3C0C\u3C0D\u3C0E\u3C0F\u3C10\u3C11"+ + "\u3C12\u3C13\u3C14\u3C15\u3C16\u3C17\u3C18\u3C19"+ + "\u3C1A\u3C1B\u3C1C\u3C1D\u3C1E\u3C1F\u3C20\u3C21"+ + "\u3C22\u3C23\u3C24\u3C25\u3C26\u3C27\u3C28\u3C29"+ + "\u3C2A\u3C2B\u3C2C\u3C2D\u3C2E\u3C2F\u3C30\u3C31"+ + "\u3C32\u3C33\u3C34\u3C35\u3C36\u3C37\u3C38\u3C39"+ + "\u3C3A\u3C3B\u3C3C\u3C3D\u3C3E\u3C3F\u3C40\u3C41"+ + "\u3C42\u3C43\u3C44\u3C45\u3C46\u3C47\u3C48\u3C49"+ + "\u3C4A\u3C4B\u3C4C\u3C4D\u3C4E\u3C4F\u3C50\u3C51"+ + "\u3C52\u3C53\u3C54\u3C55\u3C56\u3C57\u3C58\u3C59"+ + "\u3C5A\u3C5B\u3C5C\u3C5D\u3C5E\u3C5F\u3C60\u3C61"+ + "\u3C62\u3C63\u3C64\u3C65\u3C66\u3C67\u3C68\u3C69"+ + "\u3C6A\u3C6B\u3C6C\u3C6D\u3C6E\u3C6F\u3C70\u3C71"+ + "\u3C72\u3C73\u3C74\u3C75\u3C76\u3C77\u3C78\u3C79"+ + "\u3C7A\u3C7B\u3C7C\u3C7D\u3C7E\u3C7F\u3C80\u3C81"+ + "\u3C82\u3C83\u3C84\u3C85\u3C86\u3C87\u3C88\u3C89"+ + "\u3C8A\u3C8B\u3C8C\u3C8D\u3C8E\u3C8F\u3C90\u3C91"+ + "\u3C92\u3C93\u3C94\u3C95\u3C96\u3C97\u3C98\u3C99"+ + "\u3C9A\u3C9B\u3C9C\u3C9D\u3C9E\u3C9F\u3CA0\u3CA1"+ + "\u3CA2\u3CA3\u3CA4\u3CA5\u3CA6\u3CA7\u3CA8\u3CA9"+ + "\u3CAA\u3CAB\u3CAC\u3CAD\u3CAE\u3CAF\u3CB0\u3CB1"+ + "\u3CB2\u3CB3\u3CB4\u3CB5\u3CB6\u3CB7\u3CB8\u3CB9"+ + "\u3CBA\u3CBB\u3CBC\u3CBD\u3CBE\u3CBF\u3CC0\u3CC1"+ + "\u3CC2\u3CC3\u3CC4\u3CC5\u3CC6\u3CC7\u3CC8\u3CC9"+ + "\u3CCA\u3CCB\u3CCC\u3CCD\u3CCE\u3CCF\u3CD0\u3CD1"+ + "\u3CD2\u3CD3\u3CD4\u3CD5\u3CD6\u3CD7\u3CD8\u3CD9"+ + "\u3CDA\u3CDB\u3CDC\u3CDD\u3CDE\u3CDF\u3CE0\u3CE1"+ + "\u3CE2\u3CE3\u3CE4\u3CE5\u3CE6\u3CE7\u3CE8\u3CE9"+ + "\u3CEA\u3CEB\u3CEC\u3CED\u3CEE\u3CEF\u3CF0\u3CF1"+ + "\u3CF2\u3CF3\u3CF4\u3CF5\u3CF6\u3CF7\u3CF8\u3CF9"+ + "\u3CFA\u3CFB\u3CFC\u3CFD\u3CFE\u3CFF\u3D00\u3D01"+ + "\u3D02\u3D03\u3D04\u3D05\u3D06\u3D07\u3D08\u3D09"+ + "\u3D0A\u3D0B\u3D0C\u3D0D\u3D0E\u3D0F\u3D10\u3D11"+ + "\u3D12\u3D13\u3D14\u3D15\u3D16\u3D17\u3D18\u3D19"+ + "\u3D1A\u3D1B\u3D1C\u3D1D\u3D1E\u3D1F\u3D20\u3D21"+ + "\u3D22\u3D23\u3D24\u3D25\u3D26\u3D27\u3D28\u3D29"+ + "\u3D2A\u3D2B\u3D2C\u3D2D\u3D2E\u3D2F\u3D30\u3D31"+ + "\u3D32\u3D33\u3D34\u3D35\u3D36\u3D37\u3D38\u3D39"+ + "\u3D3A\u3D3B\u3D3C\u3D3D\u3D3E\u3D3F\u3D40\u3D41"+ + "\u3D42\u3D43\u3D44\u3D45\u3D46\u3D47\u3D48\u3D49"+ + "\u3D4A\u3D4B\u3D4C\u3D4D\u3D4E\u3D4F\u3D50\u3D51"+ + "\u3D52\u3D53\u3D54\u3D55\u3D56\u3D57\u3D58\u3D59"+ + "\u3D5A\u3D5B\u3D5C\u3D5D\u3D5E\u3D5F\u3D60\u3D61"+ + "\u3D62\u3D63\u3D64\u3D65\u3D66\u3D67\u3D68\u3D69"+ + "\u3D6A\u3D6B\u3D6C\u3D6D\u3D6E\u3D6F\u3D70\u3D71"+ + "\u3D72\u3D73\u3D74\u3D75\u3D76\u3D77\u3D78\u3D79"+ + "\u3D7A\u3D7B\u3D7C\u3D7D\u3D7E\u3D7F\u3D80\u3D81"+ + "\u3D82\u3D83\u3D84\u3D85\u3D86\u3D87\u3D88\u3D89"+ + "\u3D8A\u3D8B\u3D8C\u3D8D\u3D8E\u3D8F\u3D90\u3D91"+ + "\u3D92\u3D93\u3D94\u3D95\u3D96\u3D97\u3D98\u3D99"+ + "\u3D9A\u3D9B\u3D9C\u3D9D\u3D9E\u3D9F\u3DA0\u3DA1"+ + "\u3DA2\u3DA3\u3DA4\u3DA5\u3DA6\u3DA7\u3DA8\u3DA9"+ + "\u3DAA\u3DAB\u3DAC\u3DAD\u3DAE\u3DAF\u3DB0\u3DB1"+ + "\u3DB2\u3DB3\u3DB4\u3DB5\u3DB6\u3DB7\u3DB8\u3DB9"+ + "\u3DBA\u3DBB\u3DBC\u3DBD\u3DBE\u3DBF\u3DC0\u3DC1"+ + "\u3DC2\u3DC3\u3DC4\u3DC5\u3DC6\u3DC7\u3DC8\u3DC9"+ + "\u3DCA\u3DCB\u3DCC\u3DCD\u3DCE\u3DCF\u3DD0\u3DD1"+ + "\u3DD2\u3DD3\u3DD4\u3DD5\u3DD6\u3DD7\u3DD8\u3DD9"+ + "\u3DDA\u3DDB\u3DDC\u3DDD\u3DDE\u3DDF\u3DE0\u3DE1"+ + "\u3DE2\u3DE3\u3DE4\u3DE5\u3DE6\u3DE7\u3DE8\u3DE9"+ + "\u3DEA\u3DEB\u3DEC\u3DED\u3DEE\u3DEF\u3DF0\u3DF1"+ + "\u3DF2\u3DF3\u3DF4\u3DF5\u3DF6\u3DF7\u3DF8\u3DF9"+ + "\u3DFA\u3DFB\u3DFC\u3DFD\u3DFE\u3DFF\u3E00\u3E01"+ + "\u3E02\u3E03\u3E04\u3E05\u3E06\u3E07\u3E08\u3E09"+ + "\u3E0A\u3E0B\u3E0C\u3E0D\u3E0E\u3E0F\u3E10\u3E11"+ + "\u3E12\u3E13\u3E14\u3E15\u3E16\u3E17\u3E18\u3E19"+ + "\u3E1A\u3E1B\u3E1C\u3E1D\u3E1E\u3E1F\u3E20\u3E21"+ + "\u3E22\u3E23\u3E24\u3E25\u3E26\u3E27\u3E28\u3E29"+ + "\u3E2A\u3E2B\u3E2C\u3E2D\u3E2E\u3E2F\u3E30\u3E31"+ + "\u3E32\u3E33\u3E34\u3E35\u3E36\u3E37\u3E38\u3E39"+ + "\u3E3A\u3E3B\u3E3C\u3E3D\u3E3E\u3E3F\u3E40\u3E41"+ + "\u3E42\u3E43\u3E44\u3E45\u3E46\u3E47\u3E48\u3E49"+ + "\u3E4A\u3E4B\u3E4C\u3E4D\u3E4E\u3E4F\u3E50\u3E51"+ + "\u3E52\u3E53\u3E54\u3E55\u3E56\u3E57\u3E58\u3E59"+ + "\u3E5A\u3E5B\u3E5C\u3E5D\u3E5E\u3E5F\u3E60\u3E61"+ + "\u3E62\u3E63\u3E64\u3E65\u3E66\u3E67\u3E68\u3E69"+ + "\u3E6A\u3E6B\u3E6C\u3E6D\u3E6E\u3E6F\u3E70\u3E71"+ + "\u3E72\u3E73\u3E74\u3E75\u3E76\u3E77\u3E78\u3E79"+ + "\u3E7A\u3E7B\u3E7C\u3E7D\u3E7E\u3E7F\u3E80\u3E81"+ + "\u3E82\u3E83\u3E84\u3E85\u3E86\u3E87\u3E88\u3E89"+ + "\u3E8A\u3E8B\u3E8C\u3E8D\u3E8E\u3E8F\u3E90\u3E91"+ + "\u3E92\u3E93\u3E94\u3E95\u3E96\u3E97\u3E98\u3E99"+ + "\u3E9A\u3E9B\u3E9C\u3E9D\u3E9E\u3E9F\u3EA0\u3EA1"+ + "\u3EA2\u3EA3\u3EA4\u3EA5\u3EA6\u3EA7\u3EA8\u3EA9"+ + "\u3EAA\u3EAB\u3EAC\u3EAD\u3EAE\u3EAF\u3EB0\u3EB1"+ + "\u3EB2\u3EB3\u3EB4\u3EB5\u3EB6\u3EB7\u3EB8\u3EB9"+ + "\u3EBA\u3EBB\u3EBC\u3EBD\u3EBE\u3EBF\u3EC0\u3EC1"+ + "\u3EC2\u3EC3\u3EC4\u3EC5\u3EC6\u3EC7\u3EC8\u3EC9"+ + "\u3ECA\u3ECB\u3ECC\u3ECD\u3ECE\u3ECF\u3ED0\u3ED1"+ + "\u3ED2\u3ED3\u3ED4\u3ED5\u3ED6\u3ED7\u3ED8\u3ED9"+ + "\u3EDA\u3EDB\u3EDC\u3EDD\u3EDE\u3EDF\u3EE0\u3EE1"; + + private final static String innerEncoderIndex2= + "\u3EE2\u3EE3\u3EE4\u3EE5\u3EE6\u3EE7\u3EE8\u3EE9"+ + "\u3EEA\u3EEB\u3EEC\u3EED\u3EEE\u3EEF\u3EF0\u3EF1"+ + "\uA95C\u3EF2\u3EF3\uA843\uA1AA\uA844\uA1AC\u3EF4"+ + "\uA1AE\uA1AF\u3EF5\u3EF6\uA1B0\uA1B1\u3EF7\u3EF8"+ + "\u3EF9\u3EFA\u3EFB\u3EFC\u3EFD\uA845\uA1AD\u3EFE"+ + "\u3EFF\u3F00\u3F01\u3F02\u3F03\u3F04\u3F05\u3F06"+ + "\uA1EB\u3F07\uA1E4\uA1E5\u3F08\uA846\u3F09\u3F0A"+ + "\u3F0B\u3F0C\u3F0D\uA1F9\u3F0E\u3F0F\u3F10\u3F11"+ + "\u3F12\u3F13\u3F14\u3F15\u3F16\u3F17\u3F18\u3F19"+ + "\u3F1A\u3F1B\u3F1C\u3F1D\u3F1E\u3F1F\u3F20\u3F21"+ + "\u3F22\u3F23\u3F24\u3F25\u3F26\u3F27\u3F28\u3F29"+ + "\u3F2A\u3F2B\u3F2C\u3F2D\u3F2E\u3F2F\u3F30\u3F31"+ + "\u3F32\u3F33\u3F34\u3F35\u3F36\u3F37\u3F38\u3F39"+ + "\u3F3A\u3F3B\u3F3C\u3F3D\u3F3E\u3F3F\u3F40\u3F41"+ + "\u3F42\u3F43\u3F44\u3F45\u3F46\u3F47\u3F48\u3F49"+ + "\u3F4A\u3F4B\u3F4C\u3F4D\u3F4E\u3F4F\u3F50\u3F51"+ + "\u3F52\u3F53\u3F54\u3F55\u3F56\u3F57\u3F58\u3F59"+ + "\u3F5A\u3F5B\u3F5C\u3F5D\u3F5E\u3F5F\u3F60\u3F61"+ + "\u3F62\u3F63\u3F64\u3F65\u3F66\u3F67\u3F68\u3F69"+ + "\u3F6A\u3F6B\u3F6C\u3F6D\u3F6E\u3F6F\u3F70\u3F71"+ + "\u3F72\u3F73\u3F74\u3F75\u3F76\u3F77\u3F78\u3F79"+ + "\u3F7A\u3F7B\u3F7C\u3F7D\uA2E3\u3F7E\u3F7F\u3F80"+ + "\u3F81\u3F82\u3F83\u3F84\u3F85\u3F86\u3F87\u3F88"+ + "\u3F89\u3F8A\u3F8B\u3F8C\u3F8D\u3F8E\u3F8F\u3F90"+ + "\u3F91\u3F92\u3F93\u3F94\u3F95\u3F96\u3F97\u3F98"+ + "\u3F99\u3F9A\u3F9B\u3F9C\u3F9D\u3F9E\u3F9F\u3FA0"+ + "\u3FA1\u3FA2\u3FA3\u3FA4\u3FA5\u3FA6\u3FA7\u3FA8"+ + "\u3FA9\u3FAA\u3FAB\u3FAC\u3FAD\u3FAE\u3FAF\u3FB0"+ + "\u3FB1\u3FB2\u3FB3\u3FB4\u3FB5\u3FB6\u3FB7\u3FB8"+ + "\u3FB9\u3FBA\u3FBB\u3FBC\u3FBD\u3FBE\u3FBF\u3FC0"+ + "\u3FC1\u3FC2\u3FC3\u3FC4\u3FC5\u3FC6\u3FC7\u3FC8"+ + "\u3FC9\u3FCA\u3FCB\u3FCC\u3FCD\u3FCE\u3FCF\u3FD0"+ + "\u3FD1\u3FD2\u3FD3\uA1E6\u3FD4\uA847\u3FD5\u3FD6"+ + "\u3FD7\uA848\u3FD8\u3FD9\u3FDA\u3FDB\u3FDC\u3FDD"+ + "\u3FDE\u3FDF\u3FE0\u3FE1\u3FE2\u3FE3\uA1ED\u3FE4"+ + "\u3FE5\u3FE6\u3FE7\u3FE8\u3FE9\u3FEA\u3FEB\u3FEC"+ + "\u3FED\uA959\u3FEE\u3FEF\u3FF0\u3FF1\u3FF2\u3FF3"+ + "\u3FF4\u3FF5\u3FF6\u3FF7\u3FF8\u3FF9\u3FFA\u3FFB"+ + "\u3FFC\u3FFD\u3FFE\u3FFF\u4000\u4001\u4002\u4003"+ + "\u4004\u4005\u4006\u4007\u4008\u4009\u400A\u400B"+ + "\u400C\u400D\u400E\u400F\u4010\u4011\u4012\u4013"+ + "\u4014\u4015\u4016\u4017\u4018\u4019\u401A\u401B"+ + "\u401C\u401D\u401E\u401F\u4020\u4021\u4022\u4023"+ + "\u4024\u4025\u4026\u4027\u4028\u4029\u402A\u402B"+ + "\uA2F1\uA2F2\uA2F3\uA2F4\uA2F5\uA2F6\uA2F7\uA2F8"+ + "\uA2F9\uA2FA\uA2FB\uA2FC\u402C\u402D\u402E\u402F"+ + "\uA2A1\uA2A2\uA2A3\uA2A4\uA2A5\uA2A6\uA2A7\uA2A8"+ + "\uA2A9\uA2AA\u4030\u4031\u4032\u4033\u4034\u4035"+ + "\u4036\u4037\u4038\u4039\u403A\u403B\u403C\u403D"+ + "\u403E\u403F\u4040\u4041\u4042\u4043\u4044\u4045"+ + "\uA1FB\uA1FC\uA1FA\uA1FD\u4046\u4047\uA849\uA84A"+ + "\uA84B\uA84C\u4048\u4049\u404A\u404B\u404C\u404D"+ + "\u404E\u404F\u4050\u4051\u4052\u4053\u4054\u4055"+ + "\u4056\u4057\u4058\u4059\u405A\u405B\u405C\u405D"+ + "\u405E\u405F\u4060\u4061\u4062\u4063\u4064\u4065"+ + "\u4066\u4067\u4068\u4069\u406A\u406B\u406C\u406D"+ + "\u406E\u406F\u4070\u4071\u4072\u4073\u4074\u4075"+ + "\u4076\u4077\u4078\u4079\u407A\u407B\u407C\u407D"+ + "\u407E\u407F\u4080\u4081\u4082\u4083\u4084\u4085"+ + "\u4086\u4087\u4088\u4089\u408A\u408B\u408C\u408D"+ + "\u408E\u408F\u4090\u4091\u4092\u4093\u4094\u4095"+ + "\u4096\u4097\u4098\u4099\u409A\u409B\u409C\u409D"+ + "\u409E\u409F\u40A0\u40A1\u40A2\u40A3\u40A4\u40A5"+ + "\u40A6\u40A7\u40A8\u40A9\u40AA\u40AB\u40AC\u40AD"+ + "\u40AE\u40AF\u40B0\u40B1\u40B2\u40B3\u40B4\u40B5"+ + "\uA1CA\u40B6\u40B7\u40B8\u40B9\u40BA\u40BB\uA1C7"+ + "\u40BC\uA1C6\u40BD\u40BE\u40BF\uA84D\u40C0\u40C1"+ + "\u40C2\u40C3\uA1CC\u40C4\u40C5\uA1D8\uA1DE\uA84E"+ + "\uA1CF\u40C6\u40C7\uA84F\u40C8\uA1CE\u40C9\uA1C4"+ + "\uA1C5\uA1C9\uA1C8\uA1D2\u40CA\u40CB\uA1D3\u40CC"+ + "\u40CD\u40CE\u40CF\u40D0\uA1E0\uA1DF\uA1C3\uA1CB"+ + "\u40D1\u40D2\u40D3\u40D4\u40D5\uA1D7\u40D6\u40D7"+ + "\u40D8\u40D9\u40DA\u40DB\u40DC\u40DD\u40DE\u40DF"+ + "\uA1D6\u40E0\u40E1\u40E2\uA1D5\u40E3\u40E4\u40E5"+ + "\u40E6\u40E7\uA850\u40E8\u40E9\u40EA\u40EB\u40EC"+ + "\u40ED\u40EE\u40EF\u40F0\u40F1\u40F2\u40F3\u40F4"+ + "\uA1D9\uA1D4\u40F5\u40F6\uA1DC\uA1DD\uA851\uA852"+ + "\u40F7\u40F8\u40F9\u40FA\u40FB\u40FC\uA1DA\uA1DB"+ + "\u40FD\u40FE\u40FF\u4100\u4101\u4102\u4103\u4104"+ + "\u4105\u4106\u4107\u4108\u4109\u410A\u410B\u410C"+ + "\u410D\u410E\u410F\u4110\u4111\u4112\u4113\u4114"+ + "\u4115\u4116\u4117\u4118\u4119\u411A\u411B\u411C"+ + "\u411D\u411E\u411F\u4120\u4121\uA892\u4122\u4123"+ + "\u4124\uA1D1\u4125\u4126\u4127\u4128\u4129\u412A"+ + "\u412B\u412C\u412D\u412E\u412F\uA1CD\u4130\u4131"+ + "\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139"+ + "\u413A\u413B\u413C\u413D\u413E\u413F\u4140\u4141"+ + "\u4142\u4143\u4144\u4145\u4146\u4147\u4148\uA853"+ + "\u4149\u414A\u414B\u414C\u414D\u414E\u414F\u4150"+ + "\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158"+ + "\u4159\u415A\u415B\u415C\u415D\u415E\u415F\u4160"+ + "\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168"+ + "\u4169\u416A\u416B\u416C\u416D\u416E\u416F\u4170"+ + "\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178"+ + "\u4179\u417A\u417B\u417C\u417D\u417E\u417F\u4180"+ + "\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188"+ + "\u4189\u418A\u418B\u418C\u418D\u418E\u418F\u4190"+ + "\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198"+ + "\u4199\u419A\uA1D0\u419B\u419C\u419D\u419E\u419F"+ + "\u41A0\u41A1\u41A2\u41A3\u41A4\u41A5\u41A6\u41A7"+ + "\u41A8\u41A9\u41AA\u41AB\u41AC\u41AD\u41AE\u41AF"+ + "\u41B0\u41B1\u41B2\u41B3\u41B4\u41B5\u41B6\u41B7"+ + "\u41B8\u41B9\u41BA\u41BB\u41BC\u41BD\u41BE\u41BF"+ + "\u41C0\u41C1\u41C2\u41C3\u41C4\u41C5\u41C6\u41C7"+ + "\u41C8\u41C9\u41CA\u41CB\u41CC\u41CD\u41CE\u41CF"+ + "\u41D0\u41D1\u41D2\u41D3\u41D4\u41D5\u41D6\u41D7"+ + "\u41D8\u41D9\u41DA\u41DB\u41DC\u41DD\u41DE\u41DF"+ + "\u41E0\u41E1\u41E2\u41E3\u41E4\u41E5\u41E6\u41E7"+ + "\u41E8\u41E9\u41EA\u41EB\u41EC\u41ED\u41EE\u41EF"+ + "\u41F0\u41F1\u41F2\u41F3\u41F4\u41F5\u41F6\u41F7"+ + "\u41F8\u41F9\u41FA\u41FB\u41FC\u41FD\u41FE\u41FF"+ + "\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207"+ + "\u4208\u4209\u420A\u420B\u420C\u420D\u420E\u420F"+ + "\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217"+ + "\u4218\u4219\u421A\u421B\u421C\u421D\u421E\u421F"+ + "\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227"+ + "\u4228\u4229\u422A\u422B\u422C\u422D\u422E\u422F"+ + "\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237"+ + "\u4238\u4239\u423A\u423B\u423C\u423D\u423E\u423F"+ + "\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247"+ + "\u4248\u4249\u424A\u424B\u424C\u424D\u424E\u424F"+ + "\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257"+ + "\u4258\u4259\u425A\u425B\u425C\u425D\u425E\u425F"+ + "\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267"+ + "\u4268\u4269\u426A\u426B\u426C\u426D\u426E\u426F"+ + "\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277"+ + "\u4278\u4279\u427A\u427B\u427C\u427D\u427E\u427F"+ + "\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287"+ + "\u4288\u4289\u428A\u428B\u428C\u428D\u428E\u428F"+ + "\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297"+ + "\u4298\u4299\u429A\u429B\u429C\u429D\u429E\u429F"+ + "\u42A0\u42A1\u42A2\u42A3\u42A4\u42A5\u42A6\u42A7"+ + "\u42A8\u42A9\u42AA\u42AB\u42AC\u42AD\u42AE\u42AF"+ + "\u42B0\u42B1\u42B2\u42B3\u42B4\u42B5\u42B6\u42B7"+ + "\u42B8\u42B9\u42BA\u42BB\u42BC\u42BD\u42BE\u42BF"+ + "\u42C0\u42C1\u42C2\u42C3\u42C4\u42C5\u42C6\u42C7"+ + "\u42C8\u42C9\u42CA\u42CB\u42CC\u42CD\u42CE\u42CF"+ + "\u42D0\u42D1\u42D2\u42D3\u42D4\u42D5\u42D6\u42D7"+ + "\u42D8\u42D9\u42DA\u42DB\u42DC\u42DD\u42DE\u42DF"+ + "\u42E0\u42E1\u42E2\u42E3\u42E4\u42E5\u42E6\u42E7"+ + "\uA2D9\uA2DA\uA2DB\uA2DC\uA2DD\uA2DE\uA2DF\uA2E0"+ + "\uA2E1\uA2E2\u42E8\u42E9\u42EA\u42EB\u42EC\u42ED"+ + "\u42EE\u42EF\u42F0\u42F1\uA2C5\uA2C6\uA2C7\uA2C8"+ + "\uA2C9\uA2CA\uA2CB\uA2CC\uA2CD\uA2CE\uA2CF\uA2D0"+ + "\uA2D1\uA2D2\uA2D3\uA2D4\uA2D5\uA2D6\uA2D7\uA2D8"+ + "\uA2B1\uA2B2\uA2B3\uA2B4\uA2B5\uA2B6\uA2B7\uA2B8"+ + "\uA2B9\uA2BA\uA2BB\uA2BC\uA2BD\uA2BE\uA2BF\uA2C0"+ + "\uA2C1\uA2C2\uA2C3\uA2C4\u42F2\u42F3\u42F4\u42F5"+ + "\u42F6\u42F7\u42F8\u42F9\u42FA\u42FB\u42FC\u42FD"+ + "\u42FE\u42FF\u4300\u4301\u4302\u4303\u4304\u4305"+ + "\u4306\u4307\u4308\u4309\u430A\u430B\u430C\u430D"+ + "\u430E\u430F\u4310\u4311\u4312\u4313\u4314\u4315"+ + "\u4316\u4317\u4318\u4319\u431A\u431B\u431C\u431D"+ + "\u431E\u431F\u4320\u4321\u4322\u4323\u4324\u4325"+ + "\u4326\u4327\u4328\u4329\u432A\u432B\u432C\u432D"+ + "\u432E\u432F\u4330\u4331\u4332\u4333\u4334\u4335"+ + "\u4336\u4337\u4338\u4339\u433A\u433B\u433C\u433D"+ + "\u433E\u433F\u4340\u4341\u4342\u4343\u4344\u4345"+ + "\u4346\u4347\u4348\u4349\u434A\u434B\u434C\u434D"+ + "\u434E\u434F\u4350\u4351\u4352\u4353\u4354\u4355"+ + "\uA9A4\uA9A5\uA9A6\uA9A7\uA9A8\uA9A9\uA9AA\uA9AB"+ + "\uA9AC\uA9AD\uA9AE\uA9AF\uA9B0\uA9B1\uA9B2\uA9B3"+ + "\uA9B4\uA9B5\uA9B6\uA9B7\uA9B8\uA9B9\uA9BA\uA9BB"+ + "\uA9BC\uA9BD\uA9BE\uA9BF\uA9C0\uA9C1\uA9C2\uA9C3"+ + "\uA9C4\uA9C5\uA9C6\uA9C7\uA9C8\uA9C9\uA9CA\uA9CB"+ + "\uA9CC\uA9CD\uA9CE\uA9CF\uA9D0\uA9D1\uA9D2\uA9D3"+ + "\uA9D4\uA9D5\uA9D6\uA9D7\uA9D8\uA9D9\uA9DA\uA9DB"+ + "\uA9DC\uA9DD\uA9DE\uA9DF\uA9E0\uA9E1\uA9E2\uA9E3"+ + "\uA9E4\uA9E5\uA9E6\uA9E7\uA9E8\uA9E9\uA9EA\uA9EB"+ + "\uA9EC\uA9ED\uA9EE\uA9EF\u4356\u4357\u4358\u4359"+ + "\uA854\uA855\uA856\uA857\uA858\uA859\uA85A\uA85B"+ + "\uA85C\uA85D\uA85E\uA85F\uA860\uA861\uA862\uA863"+ + "\uA864\uA865\uA866\uA867\uA868\uA869\uA86A\uA86B"+ + "\uA86C\uA86D\uA86E\uA86F\uA870\uA871\uA872\uA873"+ + "\uA874\uA875\uA876\uA877\u435A\u435B\u435C\u435D"+ + "\u435E\u435F\u4360\u4361\u4362\u4363\u4364\u4365"+ + "\u4366\uA878\uA879\uA87A\uA87B\uA87C\uA87D\uA87E"+ + "\uA880\uA881\uA882\uA883\uA884\uA885\uA886\uA887"+ + "\u4367\u4368\u4369\uA888\uA889\uA88A\u436A\u436B"+ + "\u436C\u436D\u436E\u436F\u4370\u4371\u4372\u4373"+ + "\uA1F6\uA1F5\u4374\u4375\u4376\u4377\u4378\u4379"+ + "\u437A\u437B\u437C\u437D\u437E\u437F\u4380\u4381"+ + "\u4382\u4383\uA1F8\uA1F7\u4384\u4385\u4386\u4387"+ + "\u4388\u4389\u438A\u438B\uA88B\uA88C\u438C\u438D"+ + "\u438E\u438F\u4390\u4391\u4392\u4393\uA1F4\uA1F3"+ + "\u4394\u4395\u4396\uA1F0\u4397\u4398\uA1F2\uA1F1"+ + "\u4399\u439A\u439B\u439C\u439D\u439E\u439F\u43A0"+ + "\u43A1\u43A2\u43A3\u43A4\u43A5\u43A6\u43A7\u43A8"+ + "\u43A9\u43AA\uA88D\uA88E\uA88F\uA890\u43AB\u43AC"+ + "\u43AD\u43AE\u43AF\u43B0\u43B1\u43B2\u43B3\u43B4"+ + "\u43B5\u43B6\u43B7\u43B8\u43B9\u43BA\u43BB\u43BC"+ + "\u43BD\u43BE\u43BF\u43C0\u43C1\u43C2\u43C3\u43C4"+ + "\u43C5\u43C6\u43C7\u43C8\u43C9\uA1EF\uA1EE\u43CA"+ + "\u43CB\uA891\u43CC\u43CD\u43CE\u43CF\u43D0\u43D1"+ + "\u43D2\u43D3\u43D4\u43D5\u43D6\u43D7\u43D8\u43D9"+ + "\u43DA\u43DB\u43DC\u43DD\u43DE\u43DF\u43E0\u43E1"+ + "\u43E2\u43E3\u43E4\u43E5\u43E6\u43E7\u43E8\u43E9"+ + "\u43EA\u43EB\u43EC\u43ED\u43EE\u43EF\u43F0\u43F1"+ + "\u43F2\u43F3\u43F4\u43F5\u43F6\u43F7\u43F8\u43F9"+ + "\u43FA\u43FB\u43FC\u43FD\u43FE\u43FF\u4400\u4401"+ + "\uA1E2\u4402\uA1E1\u4403\u4404\u4405\u4406\u4407"+ + "\u4408\u4409\u440A\u440B\u440C\u440D\u440E\u440F"+ + "\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417"+ + "\u4418\u4419\u441A\u441B\u441C\u441D\u441E\u441F"+ + "\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427"+ + "\u4428\u4429\u442A\u442B\u442C\u442D\u442E\u442F"+ + "\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437"+ + "\u4438\u4439\u443A\u443B\u443C\u443D\u443E\u443F"+ + "\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447"+ + "\u4448\u4449\u444A\u444B\u444C\u444D\u444E\u444F"+ + "\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457"+ + "\u4458\u4459\u445A\u445B\u445C\u445D\u445E\u445F"+ + "\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467"+ + "\u4468\u4469\u446A\u446B\u446C\u446D\u446E\u446F"+ + "\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477"+ + "\u4478\u4479\u447A\u447B\u447C\u447D\u447E\u447F"+ + "\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487"+ + "\u4488\u4489\u448A\u448B\u448C\u448D\u448E\u448F"+ + "\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497"+ + "\u4498\u4499\u449A\u449B\u449C\u449D\u449E\u449F"+ + "\u44A0\u44A1\u44A2\u44A3\u44A4\u44A5\u44A6\u44A7"+ + "\u44A8\u44A9\u44AA\u44AB\u44AC\u44AD\u44AE\u44AF"+ + "\u44B0\u44B1\u44B2\u44B3\u44B4\u44B5\u44B6\u44B7"+ + "\u44B8\u44B9\u44BA\u44BB\u44BC\u44BD\u44BE\u44BF"+ + "\u44C0\u44C1\u44C2\u44C3\u44C4\u44C5\u44C6\u44C7"+ + "\u44C8\u44C9\u44CA\u44CB\u44CC\u44CD\u44CE\u44CF"+ + "\u44D0\u44D1\u44D2\u44D3\u44D4\u44D5\u44D6\u44D7"+ + "\u44D8\u44D9\u44DA\u44DB\u44DC\u44DD\u44DE\u44DF"+ + "\u44E0\u44E1\u44E2\u44E3\u44E4\u44E5\u44E6\u44E7"+ + "\u44E8\u44E9\u44EA\u44EB\u44EC\u44ED\u44EE\u44EF"+ + "\u44F0\u44F1\u44F2\u44F3\u44F4\u44F5\u44F6\u44F7"+ + "\u44F8\u44F9\u44FA\u44FB\u44FC\u44FD\u44FE\u44FF"+ + "\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507"+ + "\u4508\u4509\u450A\u450B\u450C\u450D\u450E\u450F"+ + "\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517"+ + "\u4518\u4519\u451A\u451B\u451C\u451D\u451E\u451F"+ + "\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527"+ + "\u4528\u4529\u452A\u452B\u452C\u452D\u452E\u452F"+ + "\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537"+ + "\u4538\u4539\u453A\u453B\u453C\u453D\u453E\u453F"+ + "\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547"+ + "\u4548\u4549\u454A\u454B\u454C\u454D\u454E\u454F"+ + "\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557"+ + "\u4558\u4559\u455A\u455B\u455C\u455D\u455E\u455F"+ + "\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567"+ + "\u4568\u4569\u456A\u456B\u456C\u456D\u456E\u456F"+ + "\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577"+ + "\u4578\u4579\u457A\u457B\u457C\u457D\u457E\u457F"+ + "\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587"+ + "\u4588\u4589\u458A\u458B\u458C\u458D\u458E\u458F"+ + "\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597"+ + "\u4598\u4599\u459A\u459B\u459C\u459D\u459E\u459F"+ + "\u45A0\u45A1\u45A2\u45A3\u45A4\u45A5\u45A6\u45A7"+ + "\u45A8\u45A9\u45AA\u45AB\u45AC\u45AD\u45AE\u45AF"+ + "\u45B0\u45B1\u45B2\u45B3\u45B4\u45B5\u45B6\u45B7"+ + "\u45B8\u45B9\u45BA\u45BB\u45BC\u45BD\u45BE\u45BF"+ + "\u45C0\u45C1\u45C2\u45C3\u45C4\u45C5\u45C6\u45C7"+ + "\u45C8\u45C9\u45CA\u45CB\u45CC\u45CD\u45CE\u45CF"+ + "\u45D0\u45D1\u45D2\u45D3\u45D4\u45D5\u45D6\u45D7"+ + "\u45D8\u45D9\u45DA\u45DB\u45DC\u45DD\u45DE\u45DF"+ + "\u45E0\u45E1\u45E2\u45E3\u45E4\u45E5\u45E6\u45E7"+ + "\u45E8\u45E9\u45EA\u45EB\u45EC\u45ED\u45EE\u45EF"+ + "\u45F0\u45F1\u45F2\u45F3\u45F4\u45F5\u45F6\u45F7"+ + "\u45F8\u45F9\u45FA\u45FB\u45FC\u45FD\u45FE\u45FF"+ + "\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607"+ + "\u4608\u4609\u460A\u460B\u460C\u460D\u460E\u460F"+ + "\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617"+ + "\u4618\u4619\u461A\u461B\u461C\u461D\u461E\u461F"+ + "\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627"+ + "\u4628\u4629\u462A\u462B\u462C\u462D\u462E\u462F"+ + "\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637"+ + "\u4638\u4639\u463A\u463B\u463C\u463D\u463E\u463F"+ + "\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647"+ + "\u4648\u4649\u464A\u464B\u464C\u464D\u464E\u464F"+ + "\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657"+ + "\u4658\u4659\u465A\u465B\u465C\u465D\u465E\u465F"+ + "\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667"+ + "\u4668\u4669\u466A\u466B\u466C\u466D\u466E\u466F"+ + "\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677"+ + "\u4678\u4679\u467A\u467B\u467C\u467D\u467E\u467F"+ + "\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687"+ + "\u4688\u4689\u468A\u468B\u468C\u468D\u468E\u468F"+ + "\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697"+ + "\u4698\u4699\u469A\u469B\u469C\u469D\u469E\u469F"+ + "\u46A0\u46A1\u46A2\u46A3\u46A4\u46A5\u46A6\u46A7"+ + "\u46A8\u46A9\u46AA\u46AB\u46AC\u46AD\u46AE\u46AF"+ + "\u46B0\u46B1\u46B2\u46B3\u46B4\u46B5\u46B6\u46B7"+ + "\u46B8\u46B9\u46BA\u46BB\u46BC\u46BD\u46BE\u46BF"+ + "\u46C0\u46C1\u46C2\u46C3\u46C4\u46C5\u46C6\u46C7"+ + "\u46C8\u46C9\u46CA\u46CB\u46CC\u46CD\u46CE\u46CF"+ + "\u46D0\u46D1\u46D2\u46D3\u46D4\u46D5\u46D6\u46D7"+ + "\u46D8\u46D9\u46DA\u46DB\u46DC\u46DD\u46DE\u46DF"+ + "\u46E0\u46E1\u46E2\u46E3\u46E4\u46E5\u46E6\u46E7"+ + "\u46E8\u46E9\u46EA\u46EB\u46EC\u46ED\u46EE\u46EF"+ + "\u46F0\u46F1\u46F2\u46F3\u46F4\u46F5\u46F6\u46F7"+ + "\u46F8\u46F9\u46FA\u46FB\u46FC\u46FD\u46FE\u46FF"+ + "\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707"+ + "\u4708\u4709\u470A\u470B\u470C\u470D\u470E\u470F"+ + "\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717"+ + "\u4718\u4719\u471A\u471B\u471C\u471D\u471E\u471F"+ + "\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727"+ + "\u4728\u4729\u472A\u472B\u472C\u472D\u472E\u472F"+ + "\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737"+ + "\u4738\u4739\u473A\u473B\u473C\u473D\u473E\u473F"+ + "\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747"+ + "\u4748\u4749\u474A\u474B\u474C\u474D\u474E\u474F"+ + "\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757"+ + "\u4758\u4759\u475A\u475B\u475C\u475D\u475E\u475F"+ + "\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767"+ + "\u4768\u4769\u476A\u476B\u476C\u476D\u476E\u476F"+ + "\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777"+ + "\u4778\u4779\u477A\u477B\u477C\u477D\u477E\u477F"+ + "\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787"+ + "\u4788\u4789\u478A\u478B\u478C\u478D\u478E\u478F"+ + "\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797"+ + "\u4798\u4799\u479A\u479B\u479C\u479D\u479E\u479F"+ + "\u47A0\u47A1\u47A2\u47A3\u47A4\u47A5\u47A6\u47A7"+ + "\u47A8\u47A9\u47AA\u47AB\u47AC\u47AD\u47AE\u47AF"+ + "\u47B0\u47B1\u47B2\u47B3\u47B4\u47B5\u47B6\u47B7"+ + "\u47B8\u47B9\u47BA\u47BB\u47BC\u47BD\u47BE\u47BF"+ + "\u47C0\u47C1\u47C2\u47C3\u47C4\u47C5\u47C6\u47C7"+ + "\u47C8\u47C9\u47CA\u47CB\u47CC\u47CD\u47CE\u47CF"+ + "\u47D0\u47D1\u47D2\u47D3\u47D4\u47D5\u47D6\u47D7"+ + "\u47D8\u47D9\u47DA\u47DB\u47DC\u47DD\u47DE\u47DF"+ + "\u47E0\u47E1\u47E2\u47E3\u47E4\u47E5\u47E6\u47E7"+ + "\u47E8\u47E9\u47EA\u47EB\u47EC\u47ED\u47EE\u47EF"+ + "\u47F0\u47F1\u47F2\u47F3\u47F4\u47F5\u47F6\u47F7"+ + "\u47F8\u47F9\u47FA\u47FB\u47FC\u47FD\u47FE\u47FF"+ + "\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807"+ + "\u4808\u4809\u480A\u480B\u480C\u480D\u480E\u480F"+ + "\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817"+ + "\u4818\u4819\u481A\u481B\u481C\u481D\u481E\u481F"+ + "\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827"+ + "\u4828\u4829\u482A\u482B\u482C\u482D\u482E\u482F"+ + "\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837"+ + "\u4838\u4839\u483A\u483B\u483C\u483D\u483E\u483F"+ + "\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847"+ + "\u4848\u4849\u484A\u484B\u484C\u484D\u484E\u484F"+ + "\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857"+ + "\u4858\u4859\u485A\u485B\u485C\u485D\u485E\u485F"+ + "\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867"+ + "\u4868\u4869\u486A\u486B\u486C\u486D\u486E\u486F"+ + "\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877"+ + "\u4878\u4879\u487A\u487B\u487C\u487D\u487E\u487F"+ + "\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887"+ + "\u4888\u4889\u488A\u488B\u488C\u488D\u488E\u488F"+ + "\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897"+ + "\u4898\u4899\u489A\u489B\u489C\u489D\u489E\u489F"+ + "\u48A0\u48A1\u48A2\u48A3\u48A4\u48A5\u48A6\u48A7"+ + "\u48A8\u48A9\u48AA\u48AB\u48AC\u48AD\u48AE\u48AF"+ + "\u48B0\u48B1\u48B2\u48B3\u48B4\u48B5\u48B6\u48B7"+ + "\u48B8\u48B9\u48BA\u48BB\u48BC\u48BD\u48BE\u48BF"+ + "\u48C0\u48C1\u48C2\u48C3\u48C4\u48C5\u48C6\u48C7"+ + "\u48C8\u48C9\u48CA\u48CB\u48CC\u48CD\u48CE\u48CF"+ + "\u48D0\u48D1\u48D2\u48D3\u48D4\u48D5\u48D6\u48D7"+ + "\u48D8\u48D9\u48DA\u48DB\u48DC\u48DD\u48DE\u48DF"+ + "\u48E0\u48E1\u48E2\u48E3\u48E4\u48E5\u48E6\u48E7"+ + "\u48E8\u48E9\u48EA\u48EB\u48EC\u48ED\u48EE\u48EF"+ + "\u48F0\u48F1\u48F2\u48F3\u48F4\u48F5\u48F6\u48F7"+ + "\u48F8\u48F9\u48FA\u48FB\u48FC\u48FD\u48FE\u48FF"+ + "\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907"+ + "\u4908\u4909\u490A\u490B\u490C\u490D\u490E\u490F"+ + "\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917"+ + "\u4918\u4919\u491A\u491B\u491C\u491D\u491E\u491F"+ + "\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927"+ + "\u4928\u4929\u492A\u492B\u492C\u492D\u492E\u492F"+ + "\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937"+ + "\u4938\u4939\u493A\u493B\u493C\u493D\u493E\u493F"+ + "\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947"+ + "\u4948\u4949\u494A\u494B\u494C\u494D\u494E\u494F"+ + "\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957"+ + "\u4958\u4959\u495A\u495B\u495C\u495D\u495E\u495F"+ + "\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967"+ + "\u4968\u4969\u496A\u496B\u496C\u496D\u496E\u496F"+ + "\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977"+ + "\u4978\u4979\u497A\u497B\u497C\u497D\u497E\u497F"+ + "\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987"+ + "\u4988\u4989\u498A\u498B\u498C\u498D\u498E\u498F"+ + "\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997"+ + "\u4998\u4999\u499A\u499B\u499C\u499D\u499E\u499F"+ + "\u49A0\u49A1\u49A2\u49A3\u49A4\u49A5\u49A6\u49A7"+ + "\u49A8\u49A9\u49AA\u49AB\u49AC\u49AD\u49AE\u49AF"+ + "\u49B0\u49B1\u49B2\u49B3\u49B4\u49B5\u49B6\u49B7"+ + "\u49B8\u49B9\u49BA\u49BB\u49BC\u49BD\u49BE\u49BF"+ + "\u49C0\u49C1\u49C2\u49C3\u49C4\u49C5\u49C6\u49C7"+ + "\u49C8\u49C9\u49CA\u49CB\u49CC\u49CD\u49CE\u49CF"+ + "\u49D0\u49D1\u49D2\u49D3\u49D4\u49D5\u49D6\u49D7"+ + "\u49D8\u49D9\u49DA\u49DB\u49DC\u49DD\u49DE\u49DF"+ + "\u49E0\u49E1\u49E2\u49E3\u49E4\u49E5\u49E6\u49E7"+ + "\u49E8\u49E9\u49EA\u49EB\u49EC\u49ED\u49EE\u49EF"+ + "\u49F0\u49F1\u49F2\u49F3\u49F4\u49F5\u49F6\u49F7"+ + "\u49F8\u49F9\u49FA\u49FB\u49FC\u49FD\u49FE\u49FF"+ + "\u4A00\u4A01\u4A02\u4A03\u4A04\u4A05\u4A06\u4A07"+ + "\u4A08\u4A09\u4A0A\u4A0B\u4A0C\u4A0D\u4A0E\u4A0F"+ + "\u4A10\u4A11\u4A12\u4A13\u4A14\u4A15\u4A16\u4A17"+ + "\u4A18\u4A19\u4A1A\u4A1B\u4A1C\u4A1D\u4A1E\u4A1F"+ + "\u4A20\u4A21\u4A22\u4A23\u4A24\u4A25\u4A26\u4A27"+ + "\u4A28\u4A29\u4A2A\u4A2B\u4A2C\u4A2D\u4A2E\u4A2F"+ + "\u4A30\u4A31\u4A32\u4A33\u4A34\u4A35\u4A36\u4A37"+ + "\u4A38\u4A39\u4A3A\u4A3B\u4A3C\u4A3D\u4A3E\u4A3F"+ + "\u4A40\u4A41\u4A42\u4A43\u4A44\u4A45\u4A46\u4A47"+ + "\u4A48\u4A49\u4A4A\u4A4B\u4A4C\u4A4D\u4A4E\u4A4F"+ + "\u4A50\u4A51\u4A52\u4A53\u4A54\u4A55\u4A56\u4A57"+ + "\u4A58\u4A59\u4A5A\u4A5B\u4A5C\u4A5D\u4A5E\u4A5F"+ + "\u4A60\u4A61\u4A62\u4A63\u4A64\u4A65\u4A66\u4A67"+ + "\u4A68\u4A69\u4A6A\u4A6B\u4A6C\u4A6D\u4A6E\u4A6F"+ + "\u4A70\u4A71\u4A72\u4A73\u4A74\u4A75\u4A76\u4A77"+ + "\u4A78\u4A79\u4A7A\u4A7B\u4A7C\u4A7D\u4A7E\u4A7F"+ + "\u4A80\u4A81\u4A82\u4A83\u4A84\u4A85\u4A86\u4A87"+ + "\u4A88\u4A89\u4A8A\u4A8B\u4A8C\u4A8D\u4A8E\u4A8F"+ + "\u4A90\u4A91\u4A92\u4A93\u4A94\u4A95\u4A96\u4A97"+ + "\u4A98\u4A99\u4A9A\u4A9B\u4A9C\u4A9D\u4A9E\u4A9F"+ + "\u4AA0\u4AA1\u4AA2\u4AA3\u4AA4\u4AA5\u4AA6\u4AA7"+ + "\u4AA8\u4AA9\u4AAA\u4AAB\u4AAC\u4AAD\u4AAE\u4AAF"+ + "\u4AB0\u4AB1\u4AB2\u4AB3\u4AB4\u4AB5\u4AB6\u4AB7"+ + "\u4AB8\u4AB9\u4ABA\u4ABB\u4ABC\u4ABD\u4ABE\u4ABF"+ + "\u4AC0\u4AC1\u4AC2\u4AC3\u4AC4\u4AC5\u4AC6\u4AC7"+ + "\u4AC8\u4AC9\u4ACA\u4ACB\u4ACC\u4ACD\u4ACE\u4ACF"+ + "\u4AD0\u4AD1\u4AD2\u4AD3\u4AD4\u4AD5\u4AD6\u4AD7"+ + "\u4AD8\u4AD9\u4ADA\u4ADB\u4ADC\u4ADD\u4ADE\u4ADF"+ + "\u4AE0\u4AE1\u4AE2\u4AE3\u4AE4\u4AE5\u4AE6\u4AE7"+ + "\u4AE8\u4AE9\u4AEA\u4AEB\u4AEC\u4AED\u4AEE\u4AEF"+ + "\u4AF0\u4AF1\u4AF2\u4AF3\u4AF4\u4AF5\u4AF6\u4AF7"+ + "\u4AF8\u4AF9\u4AFA\u4AFB\u4AFC\u4AFD\u4AFE\u4AFF"+ + "\u4B00\u4B01\u4B02\u4B03\u4B04\u4B05\u4B06\u4B07"+ + "\u4B08\u4B09\u4B0A\u4B0B\u4B0C\u4B0D\u4B0E\u4B0F"+ + "\u4B10\u4B11\u4B12\u4B13\u4B14\u4B15\u4B16\u4B17"+ + "\u4B18\u4B19\u4B1A\u4B1B\u4B1C\u4B1D\u4B1E\u4B1F"+ + "\u4B20\u4B21\u4B22\u4B23\u4B24\u4B25\u4B26\u4B27"+ + "\u4B28\u4B29\u4B2A\u4B2B\u4B2C\u4B2D\u4B2E\u4B2F"+ + "\u4B30\u4B31\u4B32\u4B33\u4B34\u4B35\u4B36\u4B37"+ + "\u4B38\u4B39\u4B3A\u4B3B\u4B3C\u4B3D\u4B3E\u4B3F"+ + "\u4B40\u4B41\u4B42\u4B43\u4B44\u4B45\u4B46\u4B47"+ + "\u4B48\u4B49\u4B4A\u4B4B\u4B4C\u4B4D\u4B4E\u4B4F"+ + "\u4B50\u4B51\u4B52\u4B53\u4B54\u4B55\u4B56\u4B57"+ + "\u4B58\u4B59\u4B5A\u4B5B\u4B5C\u4B5D\u4B5E\u4B5F"+ + "\u4B60\u4B61\u4B62\u4B63\u4B64\u4B65\u4B66\u4B67"+ + "\u4B68\u4B69\u4B6A\u4B6B\u4B6C\u4B6D\u4B6E\u4B6F"+ + "\u4B70\u4B71\u4B72\u4B73\u4B74\u4B75\u4B76\u4B77"+ + "\u4B78\u4B79\u4B7A\u4B7B\u4B7C\u4B7D\u4B7E\u4B7F"+ + "\u4B80\u4B81\u4B82\u4B83\u4B84\u4B85\u4B86\u4B87"+ + "\u4B88\u4B89\u4B8A\u4B8B\u4B8C\u4B8D\u4B8E\u4B8F"+ + "\u4B90\u4B91\u4B92\u4B93\u4B94\u4B95\u4B96\u4B97"+ + "\u4B98\u4B99\u4B9A\u4B9B\u4B9C\u4B9D\u4B9E\u4B9F"+ + "\u4BA0\u4BA1\u4BA2\u4BA3\u4BA4\u4BA5\u4BA6\u4BA7"+ + "\u4BA8\u4BA9\u4BAA\u4BAB\u4BAC\u4BAD\u4BAE\u4BAF"+ + "\u4BB0\u4BB1\u4BB2\u4BB3\u4BB4\u4BB5\u4BB6\u4BB7"+ + "\u4BB8\u4BB9\u4BBA\u4BBB\u4BBC\u4BBD\u4BBE\u4BBF"+ + "\u4BC0\u4BC1\u4BC2\u4BC3\u4BC4\u4BC5\u4BC6\u4BC7"+ + "\u4BC8\u4BC9\u4BCA\u4BCB\u4BCC\u4BCD\u4BCE\u4BCF"+ + "\u4BD0\u4BD1\u4BD2\u4BD3\u4BD4\u4BD5\u4BD6\u4BD7"+ + "\u4BD8\u4BD9\u4BDA\u4BDB\u4BDC\u4BDD\u4BDE\u4BDF"+ + "\u4BE0\u4BE1\u4BE2\u4BE3\u4BE4\u4BE5\u4BE6\u4BE7"+ + "\u4BE8\u4BE9\u4BEA\u4BEB\u4BEC\u4BED\u4BEE\u4BEF"+ + "\u4BF0\u4BF1\u4BF2\u4BF3\u4BF4\u4BF5\u4BF6\u4BF7"+ + "\u4BF8\u4BF9\u4BFA\u4BFB\u4BFC\u4BFD\u4BFE\u4BFF"+ + "\u4C00\u4C01\u4C02\u4C03\u4C04\u4C05\u4C06\u4C07"+ + "\u4C08\u4C09\u4C0A\u4C0B\u4C0C\u4C0D\u4C0E\u4C0F"+ + "\u4C10\u4C11\u4C12\u4C13\u4C14\u4C15\u4C16\u4C17"+ + "\u4C18\u4C19\u4C1A\u4C1B\u4C1C\u4C1D\u4C1E\u4C1F"+ + "\u4C20\u4C21\u4C22\u4C23\u4C24\u4C25\u4C26\u4C27"+ + "\u4C28\u4C29\u4C2A\u4C2B\u4C2C\u4C2D\u4C2E\u4C2F"+ + "\u4C30\u4C31\u4C32\u4C33\u4C34\u4C35\u4C36\u4C37"+ + "\u4C38\u4C39\u4C3A\u4C3B\u4C3C\u4C3D\u4C3E\u4C3F"+ + "\u4C40\uFE50\u4C41\u4C42\uFE54\u4C43\u4C44\u4C45"+ + "\uFE57\u4C46\u4C47\uFE58\uFE5D\u4C48\u4C49\u4C4A"+ + "\u4C4B\u4C4C\u4C4D\u4C4E\u4C4F\u4C50\u4C51\uFE5E"+ + "\u4C52\u4C53\u4C54\u4C55\u4C56\u4C57\u4C58\u4C59"+ + "\u4C5A\u4C5B\u4C5C\u4C5D\u4C5E\u4C5F\u4C60\uFE6B"+ + "\u4C61\u4C62\uFE6E\u4C63\u4C64\u4C65\uFE71\u4C66"+ + "\u4C67\u4C68\u4C69\uFE73\u4C6A\u4C6B\uFE74\uFE75"+ + "\u4C6C\u4C6D\u4C6E\uFE79\u4C6F\u4C70\u4C71\u4C72"+ + "\u4C73\u4C74\u4C75\u4C76\u4C77\u4C78\u4C79\u4C7A"+ + "\u4C7B\u4C7C\uFE84\u4C7D\u4C7E\u4C7F\u4C80\u4C81"+ + "\u4C82\u4C83\u4C84\u4C85\u4C86\u4C87\u4C88\u4C89"+ + "\u4C8A\u4C8B\u4C8C\u4C8D\u4C8E\u4C8F\u4C90\u4C91"+ + "\u4C92\u4C93\u4C94\u4C95\u4C96\u4C97\u4C98\u4C99"+ + "\u4C9A\u4C9B\u4C9C\u4C9D\u4C9E\u4C9F\u4CA0\u4CA1"+ + "\u4CA2\u4CA3\u4CA4\u4CA5\u4CA6\u4CA7\u4CA8\u4CA9"+ + "\u4CAA\u4CAB\u4CAC\u4CAD\u4CAE\u4CAF\u4CB0\u4CB1"+ + "\u4CB2\u4CB3\u4CB4\u4CB5\u4CB6\u4CB7\u4CB8\u4CB9"+ + "\u4CBA\u4CBB\u4CBC\u4CBD\u4CBE\u4CBF\u4CC0\u4CC1"+ + "\u4CC2\u4CC3\u4CC4\u4CC5\u4CC6\u4CC7\u4CC8\u4CC9"+ + "\u4CCA\u4CCB\u4CCC\u4CCD\u4CCE\u4CCF\u4CD0\u4CD1"+ + "\u4CD2\u4CD3\u4CD4\u4CD5\u4CD6\u4CD7\u4CD8\u4CD9"+ + "\u4CDA\u4CDB\u4CDC\u4CDD\u4CDE\u4CDF\u4CE0\u4CE1"+ + "\u4CE2\u4CE3\u4CE4\u4CE5\u4CE6\u4CE7\u4CE8\u4CE9"+ + "\u4CEA\u4CEB\u4CEC\u4CED\u4CEE\u4CEF\u4CF0\u4CF1"+ + "\u4CF2\u4CF3\u4CF4\u4CF5\u4CF6\u4CF7\u4CF8\u4CF9"+ + "\u4CFA\u4CFB\u4CFC\u4CFD\u4CFE\u4CFF\u4D00\u4D01"+ + "\u4D02\u4D03\u4D04\u4D05\u4D06\u4D07\u4D08\u4D09"+ + "\u4D0A\u4D0B\u4D0C\u4D0D\u4D0E\u4D0F\u4D10\u4D11"+ + "\u4D12\u4D13\u4D14\u4D15\u4D16\u4D17\u4D18\u4D19"+ + "\u4D1A\u4D1B\u4D1C\u4D1D\u4D1E\u4D1F\u4D20\u4D21"+ + "\u4D22\u4D23\u4D24\u4D25\u4D26\u4D27\u4D28\u4D29"+ + "\u4D2A\u4D2B\u4D2C\u4D2D\u4D2E\u4D2F\u4D30\u4D31"+ + "\u4D32\u4D33\u4D34\u4D35\u4D36\u4D37\u4D38\u4D39"+ + "\u4D3A\u4D3B\u4D3C\u4D3D\u4D3E\u4D3F\u4D40\u4D41"+ + "\u4D42\u4D43\u4D44\u4D45\u4D46\u4D47\u4D48\u4D49"+ + "\u4D4A\u4D4B\u4D4C\u4D4D\u4D4E\u4D4F\u4D50\u4D51"+ + "\u4D52\u4D53\u4D54\u4D55\u4D56\u4D57\u4D58\u4D59"+ + "\u4D5A\u4D5B\u4D5C\u4D5D\u4D5E\u4D5F\u4D60\u4D61"+ + "\u4D62\u4D63\u4D64\u4D65\u4D66\u4D67\u4D68\u4D69"+ + "\u4D6A\u4D6B\u4D6C\u4D6D\u4D6E\u4D6F\u4D70\u4D71"+ + "\u4D72\u4D73\u4D74\u4D75\u4D76\u4D77\u4D78\u4D79"+ + "\u4D7A\u4D7B\u4D7C\u4D7D\u4D7E\u4D7F\u4D80\u4D81"+ + "\u4D82\u4D83\u4D84\u4D85\u4D86\u4D87\u4D88\u4D89"+ + "\u4D8A\u4D8B\u4D8C\u4D8D\u4D8E\u4D8F\u4D90\u4D91"+ + "\u4D92\u4D93\u4D94\u4D95\u4D96\u4D97\u4D98\u4D99"+ + "\u4D9A\u4D9B\u4D9C\u4D9D\u4D9E\u4D9F\u4DA0\u4DA1"+ + "\uA98A\uA98B\uA98C\uA98D\uA98E\uA98F\uA990\uA991"+ + "\uA992\uA993\uA994\uA995\u4DA2\u4DA3\u4DA4\u4DA5"; + + private final static String innerEncoderIndex3= + "\uA1A1\uA1A2\uA1A3\uA1A8\u4DA6\uA1A9\uA965\uA996"+ + "\uA1B4\uA1B5\uA1B6\uA1B7\uA1B8\uA1B9\uA1BA\uA1BB"+ + "\uA1BE\uA1BF\uA893\uA1FE\uA1B2\uA1B3\uA1BC\uA1BD"+ + "\u4DA7\u4DA8\u4DA9\u4DAA\u4DAB\uA894\uA895\u4DAC"+ + "\u4DAD\uA940\uA941\uA942\uA943\uA944\uA945\uA946"+ + "\uA947\uA948\u4DAE\u4DAF\u4DB0\u4DB1\u4DB2\u4DB3"+ + "\u4DB4\u4DB5\u4DB6\u4DB7\u4DB8\u4DB9\u4DBA\u4DBB"+ + "\u4DBC\u4DBD\u4DBE\u4DBF\u4DC0\u4DC1\uA989\u4DC2"+ + "\u4DC3\uA4A1\uA4A2\uA4A3\uA4A4\uA4A5\uA4A6\uA4A7"+ + "\uA4A8\uA4A9\uA4AA\uA4AB\uA4AC\uA4AD\uA4AE\uA4AF"+ + "\uA4B0\uA4B1\uA4B2\uA4B3\uA4B4\uA4B5\uA4B6\uA4B7"+ + "\uA4B8\uA4B9\uA4BA\uA4BB\uA4BC\uA4BD\uA4BE\uA4BF"+ + "\uA4C0\uA4C1\uA4C2\uA4C3\uA4C4\uA4C5\uA4C6\uA4C7"+ + "\uA4C8\uA4C9\uA4CA\uA4CB\uA4CC\uA4CD\uA4CE\uA4CF"+ + "\uA4D0\uA4D1\uA4D2\uA4D3\uA4D4\uA4D5\uA4D6\uA4D7"+ + "\uA4D8\uA4D9\uA4DA\uA4DB\uA4DC\uA4DD\uA4DE\uA4DF"+ + "\uA4E0\uA4E1\uA4E2\uA4E3\uA4E4\uA4E5\uA4E6\uA4E7"+ + "\uA4E8\uA4E9\uA4EA\uA4EB\uA4EC\uA4ED\uA4EE\uA4EF"+ + "\uA4F0\uA4F1\uA4F2\uA4F3\u4DC4\u4DC5\u4DC6\u4DC7"+ + "\u4DC8\u4DC9\u4DCA\uA961\uA962\uA966\uA967\u4DCB"+ + "\u4DCC\uA5A1\uA5A2\uA5A3\uA5A4\uA5A5\uA5A6\uA5A7"+ + "\uA5A8\uA5A9\uA5AA\uA5AB\uA5AC\uA5AD\uA5AE\uA5AF"+ + "\uA5B0\uA5B1\uA5B2\uA5B3\uA5B4\uA5B5\uA5B6\uA5B7"+ + "\uA5B8\uA5B9\uA5BA\uA5BB\uA5BC\uA5BD\uA5BE\uA5BF"+ + "\uA5C0\uA5C1\uA5C2\uA5C3\uA5C4\uA5C5\uA5C6\uA5C7"+ + "\uA5C8\uA5C9\uA5CA\uA5CB\uA5CC\uA5CD\uA5CE\uA5CF"+ + "\uA5D0\uA5D1\uA5D2\uA5D3\uA5D4\uA5D5\uA5D6\uA5D7"+ + "\uA5D8\uA5D9\uA5DA\uA5DB\uA5DC\uA5DD\uA5DE\uA5DF"+ + "\uA5E0\uA5E1\uA5E2\uA5E3\uA5E4\uA5E5\uA5E6\uA5E7"+ + "\uA5E8\uA5E9\uA5EA\uA5EB\uA5EC\uA5ED\uA5EE\uA5EF"+ + "\uA5F0\uA5F1\uA5F2\uA5F3\uA5F4\uA5F5\uA5F6\u4DCD"+ + "\u4DCE\u4DCF\u4DD0\u4DD1\uA960\uA963\uA964\u4DD2"+ + "\u4DD3\u4DD4\u4DD5\u4DD6\u4DD7\uA8C5\uA8C6\uA8C7"+ + "\uA8C8\uA8C9\uA8CA\uA8CB\uA8CC\uA8CD\uA8CE\uA8CF"+ + "\uA8D0\uA8D1\uA8D2\uA8D3\uA8D4\uA8D5\uA8D6\uA8D7"+ + "\uA8D8\uA8D9\uA8DA\uA8DB\uA8DC\uA8DD\uA8DE\uA8DF"+ + "\uA8E0\uA8E1\uA8E2\uA8E3\uA8E4\uA8E5\uA8E6\uA8E7"+ + "\uA8E8\uA8E9\u4DD8\u4DD9\u4DDA\u4DDB\u4DDC\u4DDD"+ + "\u4DDE\u4DDF\u4DE0\u4DE1\u4DE2\u4DE3\u4DE4\u4DE5"+ + "\u4DE6\u4DE7\u4DE8\u4DE9\u4DEA\u4DEB\u4DEC\u4DED"+ + "\u4DEE\u4DEF\u4DF0\u4DF1\u4DF2\u4DF3\u4DF4\u4DF5"+ + "\u4DF6\u4DF7\u4DF8\u4DF9\u4DFA\u4DFB\u4DFC\u4DFD"+ + "\u4DFE\u4DFF\u4E00\u4E01\u4E02\u4E03\u4E04\u4E05"+ + "\u4E06\u4E07\u4E08\u4E09\u4E0A\u4E0B\u4E0C\u4E0D"+ + "\u4E0E\u4E0F\u4E10\u4E11\u4E12\u4E13\u4E14\u4E15"+ + "\u4E16\u4E17\u4E18\u4E19\u4E1A\u4E1B\u4E1C\u4E1D"+ + "\u4E1E\u4E1F\u4E20\u4E21\u4E22\u4E23\u4E24\u4E25"+ + "\u4E26\u4E27\u4E28\u4E29\u4E2A\u4E2B\u4E2C\u4E2D"+ + "\u4E2E\u4E2F\u4E30\u4E31\u4E32\u4E33\u4E34\u4E35"+ + "\u4E36\u4E37\u4E38\u4E39\u4E3A\u4E3B\u4E3C\u4E3D"+ + "\u4E3E\u4E3F\u4E40\u4E41\u4E42\u4E43\u4E44\u4E45"+ + "\u4E46\u4E47\u4E48\u4E49\u4E4A\u4E4B\u4E4C\u4E4D"+ + "\u4E4E\u4E4F\u4E50\u4E51\u4E52\u4E53\u4E54\u4E55"+ + "\u4E56\u4E57\u4E58\u4E59\u4E5A\u4E5B\u4E5C\u4E5D"+ + "\u4E5E\u4E5F\u4E60\u4E61\u4E62\u4E63\u4E64\u4E65"+ + "\u4E66\u4E67\u4E68\u4E69\u4E6A\u4E6B\u4E6C\u4E6D"+ + "\u4E6E\u4E6F\u4E70\u4E71\u4E72\u4E73\u4E74\u4E75"+ + "\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D"+ + "\u4E7E\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85"+ + "\u4E86\u4E87\u4E88\u4E89\u4E8A\u4E8B\u4E8C\u4E8D"+ + "\u4E8E\u4E8F\u4E90\u4E91\u4E92\u4E93\u4E94\u4E95"+ + "\u4E96\u4E97\u4E98\u4E99\u4E9A\u4E9B\u4E9C\u4E9D"+ + "\u4E9E\u4E9F\u4EA0\u4EA1\u4EA2\u4EA3\u4EA4\u4EA5"+ + "\u4EA6\u4EA7\u4EA8\u4EA9\u4EAA\u4EAB\u4EAC\u4EAD"+ + "\u4EAE\u4EAF\u4EB0\u4EB1\u4EB2\u4EB3\u4EB4\u4EB5"+ + "\u4EB6\u4EB7\u4EB8\u4EB9\u4EBA\u4EBB\u4EBC\u4EBD"+ + "\u4EBE\u4EBF\u4EC0\u4EC1\u4EC2\u4EC3\u4EC4\u4EC5"+ + "\u4EC6\u4EC7\u4EC8\u4EC9\u4ECA\u4ECB\u4ECC\u4ECD"+ + "\uA2E5\uA2E6\uA2E7\uA2E8\uA2E9\uA2EA\uA2EB\uA2EC"+ + "\uA2ED\uA2EE\u4ECE\u4ECF\u4ED0\u4ED1\u4ED2\u4ED3"+ + "\u4ED4\uA95A\u4ED5\u4ED6\u4ED7\u4ED8\u4ED9\u4EDA"+ + "\u4EDB\u4EDC\u4EDD\u4EDE\u4EDF\u4EE0\u4EE1\u4EE2"+ + "\u4EE3\u4EE4\u4EE5\u4EE6\u4EE7\u4EE8\u4EE9\u4EEA"+ + "\u4EEB\u4EEC\u4EED\u4EEE\u4EEF\u4EF0\u4EF1\u4EF2"+ + "\u4EF3\u4EF4\u4EF5\u4EF6\u4EF7\u4EF8\u4EF9\u4EFA"+ + "\u4EFB\u4EFC\u4EFD\u4EFE\u4EFF\u4F00\u4F01\u4F02"+ + "\u4F03\u4F04\u4F05\u4F06\u4F07\u4F08\u4F09\u4F0A"+ + "\u4F0B\u4F0C\u4F0D\u4F0E\u4F0F\u4F10\u4F11\u4F12"+ + "\u4F13\u4F14\u4F15\u4F16\u4F17\u4F18\u4F19\u4F1A"+ + "\u4F1B\u4F1C\u4F1D\u4F1E\u4F1F\u4F20\u4F21\u4F22"+ + "\u4F23\u4F24\u4F25\u4F26\u4F27\u4F28\u4F29\u4F2A"+ + "\u4F2B\u4F2C\u4F2D\u4F2E\u4F2F\u4F30\u4F31\u4F32"+ + "\u4F33\u4F34\u4F35\u4F36\u4F37\u4F38\u4F39\u4F3A"+ + "\u4F3B\u4F3C\u4F3D\u4F3E\u4F3F\u4F40\u4F41\u4F42"+ + "\u4F43\u4F44\u4F45\uA949\u4F46\u4F47\u4F48\u4F49"+ + "\u4F4A\u4F4B\u4F4C\u4F4D\u4F4E\u4F4F\u4F50\u4F51"+ + "\u4F52\u4F53\u4F54\u4F55\u4F56\u4F57\u4F58\u4F59"+ + "\u4F5A\u4F5B\u4F5C\u4F5D\u4F5E\u4F5F\u4F60\u4F61"+ + "\u4F62\u4F63\u4F64\u4F65\u4F66\u4F67\u4F68\u4F69"+ + "\u4F6A\u4F6B\u4F6C\u4F6D\u4F6E\u4F6F\u4F70\u4F71"+ + "\u4F72\u4F73\u4F74\u4F75\u4F76\u4F77\u4F78\u4F79"+ + "\u4F7A\u4F7B\u4F7C\u4F7D\u4F7E\u4F7F\u4F80\u4F81"+ + "\u4F82\u4F83\u4F84\u4F85\u4F86\u4F87\u4F88\u4F89"+ + "\u4F8A\u4F8B\u4F8C\u4F8D\u4F8E\u4F8F\u4F90\u4F91"+ + "\u4F92\u4F93\u4F94\u4F95\u4F96\u4F97\u4F98\u4F99"+ + "\u4F9A\u4F9B\u4F9C\u4F9D\u4F9E\u4F9F\u4FA0\u4FA1"+ + "\u4FA2\u4FA3\u4FA4\u4FA5\u4FA6\u4FA7\u4FA8\u4FA9"+ + "\u4FAA\u4FAB\u4FAC\u4FAD\u4FAE\u4FAF\u4FB0\u4FB1"+ + "\u4FB2\u4FB3\u4FB4\u4FB5\u4FB6\u4FB7\u4FB8\u4FB9"+ + "\u4FBA\u4FBB\u4FBC\u4FBD\u4FBE\u4FBF\u4FC0\u4FC1"+ + "\u4FC2\u4FC3\u4FC4\u4FC5\u4FC6\u4FC7\u4FC8\u4FC9"+ + "\u4FCA\u4FCB\u4FCC\u4FCD\u4FCE\u4FCF\u4FD0\u4FD1"+ + "\u4FD2\u4FD3\u4FD4\u4FD5\u4FD6\u4FD7\u4FD8\u4FD9"+ + "\u4FDA\u4FDB\u4FDC\u4FDD\u4FDE\u4FDF\u4FE0\u4FE1"+ + "\u4FE2\u4FE3\u4FE4\u4FE5\u4FE6\u4FE7\u4FE8\u4FE9"+ + "\u4FEA\u4FEB\u4FEC\u4FED\u4FEE\u4FEF\u4FF0\u4FF1"+ + "\u4FF2\u4FF3\u4FF4\u4FF5\u4FF6\u4FF7\u4FF8\u4FF9"+ + "\u4FFA\u4FFB\u4FFC\u4FFD\u4FFE\u4FFF\u5000\u5001"+ + "\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009"+ + "\u500A\u500B\u500C\u500D\u500E\u500F\u5010\u5011"+ + "\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019"+ + "\u501A\u501B\u501C\u501D\u501E\u501F\u5020\u5021"+ + "\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029"+ + "\u502A\u502B\u502C\u502D\u502E\u502F\uA94A\uA94B"+ + "\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037"+ + "\u5038\u5039\u503A\u503B\uA94C\uA94D\uA94E\u503C"+ + "\u503D\uA94F\u503E\u503F\u5040\u5041\u5042\u5043"+ + "\u5044\u5045\u5046\u5047\u5048\u5049\u504A\u504B"+ + "\u504C\u504D\u504E\u504F\u5050\u5051\u5052\u5053"+ + "\u5054\u5055\u5056\u5057\u5058\u5059\u505A\u505B"+ + "\u505C\u505D\u505E\u505F\uA950\u5060\u5061\u5062"+ + "\u5063\u5064\u5065\u5066\u5067\u5068\uA951\u5069"+ + "\u506A\uA952\uA953\u506B\u506C\uA954\u506D\u506E"+ + "\u506F\u5070\u5071\u5072\u5073\u5074\u5075\u5076"+ + "\u5077\u5078\u5079\u507A\u507B\u507C\u507D\u507E"+ + "\u507F\u5080\u5081\u5082\u5083\u5084\u5085\u5086"+ + "\u5087\u5088\u5089\u508A\u508B\u508C\u508D\u508E"+ + "\u508F\u5090\u5091\u5092\u5093\u5094\u5095\u5096"+ + "\u5097\u5098\u5099\u509A\u509B\u509C\u509D\u509E"+ + "\u509F\u50A0\u50A1\u50A2\u50A3\u50A4\u50A5\u50A6"+ + "\u50A7\u50A8\u50A9\u50AA\u50AB\u50AC\u50AD\u50AE"+ + "\u50AF\u50B0\u50B1\u50B2\u50B3\u50B4\u50B5\u50B6"+ + "\u50B7\u50B8\u50B9\u50BA\u50BB\u50BC\u50BD\u50BE"+ + "\u50BF\u50C0\u50C1\u50C2\u50C3\u50C4\u50C5\u50C6"+ + "\u50C7\u50C8\u50C9\u50CA\u50CB\u50CC\u50CD\u50CE"+ + "\u50CF\u50D0\u50D1\u50D2\u50D3\u50D4\u50D5\u50D6"+ + "\u50D7\u50D8\u50D9\u50DA\u50DB\u50DC\u50DD\uFE56"+ + "\u50DE\u50DF\u50E0\u50E1\u50E2\u50E3\u50E4\u50E5"+ + "\u50E6\u50E7\u50E8\u50E9\u50EA\u50EB\u50EC\u50ED"+ + "\u50EE\u50EF\u50F0\u50F1\u50F2\u50F3\u50F4\u50F5"+ + "\u50F6\u50F7\u50F8\u50F9\u50FA\u50FB\u50FC\u50FD"+ + "\u50FE\u50FF\u5100\u5101\u5102\u5103\u5104\u5105"+ + "\u5106\u5107\u5108\uFE55\u5109\u510A\u510B\u510C"+ + "\u510D\u510E\u510F\u5110\u5111\u5112\u5113\u5114"+ + "\u5115\u5116\u5117\u5118\u5119\u511A\u511B\u511C"+ + "\u511D\u511E\u511F\u5120\u5121\u5122\u5123\u5124"+ + "\u5125\u5126\u5127\u5128\u5129\u512A\u512B\u512C"+ + "\u512D\u512E\u512F\u5130\u5131\u5132\u5133\u5134"+ + "\u5135\u5136\u5137\u5138\u5139\u513A\u513B\u513C"+ + "\u513D\u513E\u513F\u5140\u5141\u5142\u5143\u5144"+ + "\u5145\u5146\u5147\u5148\u5149\u514A\u514B\u514C"+ + "\u514D\u514E\u514F\u5150\u5151\u5152\u5153\u5154"+ + "\u5155\u5156\u5157\u5158\u5159\u515A\u515B\u515C"+ + "\u515D\u515E\u515F\u5160\u5161\u5162\u5163\u5164"+ + "\u5165\u5166\u5167\u5168\u5169\u516A\u516B\u516C"+ + "\u516D\u516E\u516F\u5170\u5171\u5172\u5173\u5174"+ + "\u5175\u5176\u5177\u5178\u5179\u517A\u517B\u517C"+ + "\u517D\u517E\u517F\u5180\u5181\u5182\u5183\u5184"+ + "\u5185\u5186\u5187\u5188\u5189\u518A\u518B\u518C"+ + "\u518D\u518E\u518F\u5190\u5191\u5192\u5193\u5194"+ + "\u5195\u5196\u5197\u5198\u5199\u519A\u519B\u519C"+ + "\u519D\u519E\u519F\u51A0\u51A1\u51A2\u51A3\u51A4"+ + "\u51A5\u51A6\u51A7\u51A8\u51A9\u51AA\u51AB\u51AC"+ + "\u51AD\u51AE\u51AF\u51B0\u51B1\u51B2\u51B3\u51B4"+ + "\u51B5\u51B6\u51B7\u51B8\u51B9\u51BA\u51BB\u51BC"+ + "\u51BD\u51BE\u51BF\u51C0\u51C1\u51C2\u51C3\u51C4"+ + "\u51C5\u51C6\u51C7\u51C8\u51C9\u51CA\u51CB\u51CC"+ + "\u51CD\u51CE\u51CF\u51D0\u51D1\u51D2\u51D3\u51D4"+ + "\u51D5\u51D6\u51D7\u51D8\u51D9\u51DA\u51DB\u51DC"+ + "\u51DD\u51DE\u51DF\u51E0\u51E1\u51E2\u51E3\u51E4"+ + "\u51E5\u51E6\u51E7\u51E8\u51E9\u51EA\u51EB\u51EC"+ + "\u51ED\u51EE\u51EF\u51F0\u51F1\u51F2\u51F3\u51F4"+ + "\u51F5\u51F6\u51F7\u51F8\u51F9\u51FA\u51FB\u51FC"+ + "\u51FD\u51FE\u51FF\u5200\u5201\u5202\u5203\u5204"+ + "\u5205\u5206\u5207\u5208\u5209\u520A\u520B\u520C"+ + "\u520D\u520E\u520F\u5210\u5211\u5212\u5213\u5214"+ + "\u5215\u5216\u5217\u5218\u5219\u521A\u521B\u521C"+ + "\u521D\u521E\u521F\u5220\u5221\u5222\u5223\u5224"+ + "\u5225\u5226\u5227\u5228\u5229\u522A\u522B\u522C"+ + "\u522D\u522E\u522F\u5230\u5231\u5232\uFE5A\u5233"+ + "\u5234\u5235\u5236\u5237\u5238\u5239\u523A\u523B"+ + "\u523C\u523D\u523E\u523F\u5240\u5241\u5242\u5243"+ + "\u5244\u5245\u5246\u5247\u5248\u5249\u524A\u524B"+ + "\u524C\u524D\u524E\u524F\u5250\u5251\u5252\u5253"+ + "\u5254\u5255\u5256\u5257\u5258\u5259\u525A\u525B"+ + "\u525C\u525D\u525E\u525F\u5260\u5261\u5262\u5263"+ + "\u5264\u5265\u5266\u5267\u5268\u5269\u526A\u526B"+ + "\u526C\u526D\u526E\u526F\u5270\u5271\u5272\u5273"+ + "\u5274\u5275\u5276\u5277\u5278\u5279\u527A\u527B"+ + "\u527C\u527D\u527E\u527F\u5280\u5281\u5282\u5283"+ + "\u5284\u5285\u5286\u5287\u5288\u5289\u528A\u528B"+ + "\u528C\u528D\u528E\u528F\u5290\u5291\u5292\u5293"+ + "\u5294\u5295\u5296\u5297\u5298\u5299\u529A\u529B"+ + "\u529C\u529D\u529E\u529F\u52A0\u52A1\uFE5C\u52A2"+ + "\u52A3\u52A4\u52A5\u52A6\u52A7\u52A8\u52A9\u52AA"+ + "\u52AB\u52AC\uFE5B\u52AD\u52AE\u52AF\u52B0\u52B1"+ + "\u52B2\u52B3\u52B4\u52B5\u52B6\u52B7\u52B8\u52B9"+ + "\u52BA\u52BB\u52BC\u52BD\u52BE\u52BF\u52C0\u52C1"+ + "\u52C2\u52C3\u52C4\u52C5\u52C6\u52C7\u52C8\u52C9"+ + "\u52CA\u52CB\u52CC\u52CD\u52CE\u52CF\u52D0\u52D1"+ + "\u52D2\u52D3\u52D4\u52D5\u52D6\u52D7\u52D8\u52D9"+ + "\u52DA\u52DB\u52DC\u52DD\u52DE\u52DF\u52E0\u52E1"+ + "\u52E2\u52E3\u52E4\u52E5\u52E6\u52E7\u52E8\u52E9"+ + "\u52EA\u52EB\u52EC\u52ED\u52EE\u52EF\u52F0\u52F1"+ + "\u52F2\u52F3\u52F4\u52F5\u52F6\u52F7\u52F8\u52F9"+ + "\u52FA\u52FB\u52FC\u52FD\u52FE\u52FF\u5300\u5301"+ + "\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309"+ + "\u530A\u530B\u530C\u530D\u530E\u530F\u5310\u5311"+ + "\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319"+ + "\u531A\u531B\u531C\u531D\u531E\u531F\u5320\u5321"+ + "\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329"+ + "\u532A\u532B\u532C\u532D\u532E\u532F\u5330\u5331"+ + "\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339"+ + "\u533A\u533B\u533C\u533D\u533E\u533F\u5340\u5341"+ + "\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349"+ + "\u534A\u534B\u534C\u534D\u534E\u534F\u5350\u5351"+ + "\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359"+ + "\u535A\u535B\u535C\u535D\u535E\u535F\u5360\u5361"+ + "\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369"+ + "\u536A\u536B\u536C\u536D\u536E\u536F\u5370\u5371"+ + "\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379"+ + "\u537A\u537B\u537C\u537D\u537E\u537F\u5380\u5381"+ + "\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389"+ + "\u538A\u538B\u538C\u538D\u538E\u538F\u5390\u5391"+ + "\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399"+ + "\u539A\u539B\u539C\u539D\u539E\u539F\u53A0\u53A1"+ + "\u53A2\u53A3\u53A4\u53A5\u53A6\u53A7\u53A8\u53A9"+ + "\u53AA\u53AB\u53AC\u53AD\u53AE\u53AF\u53B0\u53B1"+ + "\u53B2\u53B3\u53B4\u53B5\u53B6\u53B7\u53B8\u53B9"+ + "\u53BA\u53BB\u53BC\u53BD\u53BE\u53BF\u53C0\u53C1"+ + "\u53C2\u53C3\u53C4\u53C5\u53C6\u53C7\u53C8\u53C9"+ + "\u53CA\u53CB\u53CC\u53CD\u53CE\u53CF\u53D0\u53D1"+ + "\u53D2\u53D3\u53D4\u53D5\u53D6\u53D7\u53D8\u53D9"+ + "\u53DA\u53DB\u53DC\u53DD\u53DE\u53DF\u53E0\u53E1"+ + "\u53E2\u53E3\u53E4\u53E5\u53E6\u53E7\u53E8\u53E9"+ + "\u53EA\u53EB\u53EC\u53ED\u53EE\u53EF\u53F0\u53F1"+ + "\u53F2\u53F3\u53F4\u53F5\u53F6\u53F7\u53F8\u53F9"+ + "\u53FA\u53FB\u53FC\u53FD\u53FE\u53FF\u5400\u5401"+ + "\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409"+ + "\u540A\u540B\u540C\u540D\u540E\u540F\u5410\u5411"+ + "\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419"+ + "\u541A\u541B\u541C\u541D\u541E\u541F\u5420\u5421"+ + "\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429"+ + "\u542A\u542B\u542C\u542D\u542E\u542F\u5430\u5431"+ + "\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439"+ + "\u543A\u543B\u543C\u543D\u543E\u543F\u5440\u5441"+ + "\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449"+ + "\u544A\u544B\u544C\u544D\u544E\u544F\u5450\u5451"+ + "\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459"+ + "\u545A\u545B\u545C\u545D\u545E\u545F\u5460\u5461"+ + "\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469"+ + "\u546A\u546B\u546C\u546D\u546E\u546F\u5470\u5471"+ + "\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479"+ + "\u547A\u547B\u547C\u547D\u547E\u547F\u5480\u5481"+ + "\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489"+ + "\u548A\u548B\u548C\u548D\u548E\u548F\u5490\u5491"+ + "\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499"+ + "\u549A\u549B\u549C\u549D\u549E\u549F\u54A0\u54A1"+ + "\u54A2\u54A3\u54A4\u54A5\u54A6\u54A7\u54A8\u54A9"+ + "\u54AA\u54AB\u54AC\u54AD\u54AE\u54AF\u54B0\u54B1"+ + "\u54B2\u54B3\u54B4\u54B5\u54B6\u54B7\u54B8\u54B9"+ + "\u54BA\u54BB\u54BC\u54BD\u54BE\u54BF\u54C0\u54C1"+ + "\u54C2\u54C3\u54C4\u54C5\u54C6\u54C7\u54C8\u54C9"+ + "\u54CA\u54CB\u54CC\u54CD\u54CE\u54CF\u54D0\u54D1"+ + "\u54D2\u54D3\u54D4\u54D5\u54D6\u54D7\u54D8\u54D9"+ + "\u54DA\u54DB\u54DC\u54DD\u54DE\u54DF\u54E0\u54E1"+ + "\u54E2\u54E3\u54E4\u54E5\u54E6\u54E7\u54E8\u54E9"+ + "\u54EA\u54EB\u54EC\u54ED\u54EE\u54EF\u54F0\u54F1"+ + "\u54F2\u54F3\u54F4\u54F5\u54F6\u54F7\u54F8\u54F9"+ + "\u54FA\u54FB\u54FC\u54FD\u54FE\u54FF\u5500\u5501"+ + "\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509"+ + "\u550A\u550B\u550C\u550D\u550E\u550F\u5510\u5511"+ + "\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519"+ + "\u551A\u551B\u551C\u551D\u551E\u551F\u5520\u5521"+ + "\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529"+ + "\u552A\u552B\u552C\u552D\u552E\u552F\u5530\u5531"+ + "\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539"+ + "\u553A\u553B\u553C\u553D\u553E\u553F\u5540\u5541"+ + "\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549"+ + "\u554A\u554B\u554C\u554D\u554E\u554F\u5550\u5551"+ + "\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559"+ + "\u555A\u555B\u555C\u555D\u555E\u555F\u5560\u5561"+ + "\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569"+ + "\u556A\u556B\u556C\u556D\u556E\u556F\u5570\u5571"+ + "\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579"+ + "\u557A\u557B\u557C\u557D\u557E\u557F\u5580\u5581"+ + "\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589"+ + "\u558A\u558B\u558C\u558D\u558E\u558F\u5590\u5591"+ + "\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599"+ + "\u559A\u559B\u559C\u559D\u559E\u559F\u55A0\u55A1"+ + "\u55A2\u55A3\u55A4\u55A5\u55A6\u55A7\u55A8\u55A9"+ + "\uFE60\u55AA\u55AB\u55AC\u55AD\u55AE\u55AF\u55B0"+ + "\u55B1\u55B2\u55B3\u55B4\u55B5\u55B6\u55B7\u55B8"+ + "\u55B9\u55BA\u55BB\u55BC\u55BD\u55BE\u55BF\u55C0"+ + "\u55C1\u55C2\u55C3\u55C4\u55C5\u55C6\u55C7\u55C8"+ + "\u55C9\u55CA\u55CB\u55CC\u55CD\u55CE\u55CF\u55D0"+ + "\u55D1\u55D2\u55D3\u55D4\u55D5\u55D6\u55D7\u55D8"+ + "\u55D9\u55DA\u55DB\u55DC\u55DD\u55DE\u55DF\u55E0"+ + "\u55E1\u55E2\u55E3\u55E4\u55E5\u55E6\u55E7\u55E8"+ + "\u55E9\u55EA\u55EB\u55EC\u55ED\u55EE\u55EF\u55F0"+ + "\u55F1\u55F2\u55F3\u55F4\u55F5\u55F6\u55F7\u55F8"+ + "\u55F9\u55FA\u55FB\u55FC\u55FD\u55FE\uFE5F\u55FF"+ + "\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607"+ + "\u5608\u5609\u560A\u560B\u560C\u560D\u560E\u560F"+ + "\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617"+ + "\u5618\u5619\u561A\u561B\u561C\u561D\u561E\u561F"+ + "\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627"+ + "\u5628\u5629\u562A\u562B\u562C\u562D\u562E\u562F"+ + "\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637"+ + "\u5638\u5639\u563A\u563B\u563C\u563D\u563E\u563F"+ + "\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647"+ + "\u5648\u5649\u564A\u564B\u564C\u564D\u564E\u564F"+ + "\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657"+ + "\u5658\u5659\u565A\u565B\u565C\u565D\u565E\uFE62"+ + "\uFE65\u565F\u5660\u5661\u5662\u5663\u5664\u5665"+ + "\u5666\u5667\u5668\u5669\u566A\u566B\u566C\uFE63"+ + "\u566D\u566E\u566F\u5670\u5671\u5672\u5673\u5674"+ + "\u5675\u5676\u5677\u5678\u5679\u567A\u567B\u567C"+ + "\u567D\u567E\u567F\u5680\u5681\u5682\u5683\u5684"+ + "\u5685\u5686\u5687\u5688\u5689\u568A\u568B\u568C"+ + "\u568D\u568E\u568F\u5690\u5691\u5692\u5693\u5694"+ + "\u5695\u5696\u5697\u5698\u5699\u569A\u569B\u569C"+ + "\u569D\u569E\u569F\u56A0\u56A1\u56A2\u56A3\u56A4"+ + "\u56A5\u56A6\u56A7\u56A8\u56A9\u56AA\u56AB\u56AC"+ + "\u56AD\u56AE\u56AF\u56B0\u56B1\u56B2\u56B3\u56B4"+ + "\u56B5\u56B6\u56B7\u56B8\u56B9\u56BA\u56BB\u56BC"+ + "\u56BD\u56BE\u56BF\u56C0\u56C1\u56C2\u56C3\u56C4"+ + "\u56C5\u56C6\u56C7\u56C8\u56C9\u56CA\u56CB\u56CC"+ + "\u56CD\u56CE\u56CF\u56D0\u56D1\u56D2\u56D3\u56D4"+ + "\u56D5\u56D6\u56D7\u56D8\u56D9\u56DA\u56DB\u56DC"+ + "\u56DD\u56DE\u56DF\u56E0\u56E1\u56E2\u56E3\u56E4"+ + "\u56E5\u56E6\u56E7\u56E8\u56E9\u56EA\u56EB\u56EC"+ + "\u56ED\u56EE\u56EF\u56F0\u56F1\u56F2\u56F3\u56F4"+ + "\u56F5\u56F6\u56F7\u56F8\u56F9\u56FA\u56FB\u56FC"+ + "\u56FD\u56FE\u56FF\uFE64\u5700\u5701\u5702\u5703"+ + "\u5704\u5705\u5706\u5707\u5708\u5709\u570A\u570B"+ + "\u570C\u570D\u570E\u570F\u5710\u5711\u5712\u5713"+ + "\u5714\u5715\u5716\u5717\u5718\u5719\u571A\u571B"+ + "\u571C\u571D\u571E\u571F\u5720\u5721\u5722\u5723"+ + "\u5724\u5725\u5726\u5727\u5728\u5729\u572A\u572B"+ + "\u572C\u572D\u572E\u572F\u5730\u5731\u5732\u5733"+ + "\u5734\u5735\u5736\u5737\u5738\u5739\u573A\u573B"+ + "\u573C\u573D\u573E\u573F\u5740\u5741\u5742\u5743"+ + "\u5744\u5745\u5746\u5747\u5748\u5749\u574A\u574B"+ + "\u574C\u574D\u574E\u574F\u5750\u5751\u5752\u5753"+ + "\u5754\u5755\u5756\u5757\u5758\u5759\u575A\u575B"+ + "\u575C\u575D\u575E\u575F\u5760\u5761\u5762\u5763"+ + "\u5764\u5765\u5766\u5767\u5768\u5769\u576A\u576B"+ + "\u576C\u576D\u576E\u576F\u5770\u5771\u5772\u5773"+ + "\u5774\u5775\u5776\u5777\u5778\u5779\u577A\u577B"+ + "\u577C\u577D\u577E\u577F\u5780\u5781\u5782\u5783"+ + "\u5784\u5785\u5786\u5787\u5788\u5789\u578A\u578B"+ + "\u578C\u578D\u578E\u578F\u5790\u5791\u5792\u5793"+ + "\u5794\u5795\u5796\u5797\u5798\u5799\u579A\u579B"+ + "\u579C\u579D\u579E\u579F\u57A0\u57A1\u57A2\u57A3"+ + "\u57A4\u57A5\u57A6\u57A7\u57A8\u57A9\u57AA\u57AB"+ + "\u57AC\u57AD\u57AE\u57AF\u57B0\u57B1\u57B2\u57B3"+ + "\u57B4\u57B5\u57B6\u57B7\u57B8\u57B9\u57BA\u57BB"+ + "\u57BC\u57BD\u57BE\u57BF\u57C0\u57C1\u57C2\u57C3"+ + "\u57C4\u57C5\u57C6\u57C7\u57C8\u57C9\u57CA\u57CB"+ + "\u57CC\u57CD\u57CE\u57CF\u57D0\u57D1\u57D2\u57D3"+ + "\u57D4\u57D5\u57D6\u57D7\u57D8\u57D9\uFE68\u57DA"+ + "\u57DB\u57DC\u57DD\u57DE\u57DF\u57E0\u57E1\u57E2"+ + "\u57E3\u57E4\u57E5\u57E6\u57E7\u57E8\u57E9\u57EA"+ + "\u57EB\u57EC\u57ED\u57EE\u57EF\u57F0\u57F1\u57F2"+ + "\u57F3\u57F4\u57F5\u57F6\u57F7\u57F8\u57F9\u57FA"+ + "\u57FB\u57FC\u57FD\u57FE\u57FF\u5800\u5801\u5802"+ + "\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580A"+ + "\u580B\u580C\u580D\u580E\u580F\u5810\u5811\u5812"+ + "\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581A"+ + "\u581B\u581C\u581D\u581E\u581F\u5820\u5821\u5822"+ + "\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582A"+ + "\u582B\u582C\u582D\u582E\u582F\u5830\u5831\u5832"+ + "\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583A"+ + "\u583B\u583C\u583D\u583E\u583F\u5840\u5841\u5842"+ + "\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584A"+ + "\u584B\u584C\u584D\u584E\u584F\u5850\u5851\u5852"+ + "\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585A"+ + "\u585B\u585C\u585D\u585E\u585F\u5860\u5861\u5862"+ + "\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586A"+ + "\u586B\u586C\u586D\u586E\u586F\u5870\u5871\u5872"+ + "\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587A"+ + "\u587B\u587C\u587D\u587E\u587F\u5880\u5881\u5882"+ + "\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588A"+ + "\u588B\u588C\u588D\u588E\u588F\u5890\u5891\u5892"+ + "\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589A"+ + "\u589B\u589C\u589D\u589E\u589F\u58A0\u58A1\u58A2"+ + "\u58A3\u58A4\u58A5\u58A6\u58A7\u58A8\u58A9\u58AA"+ + "\u58AB\u58AC\u58AD\u58AE\u58AF\u58B0\u58B1\u58B2"+ + "\u58B3\u58B4\u58B5\u58B6\u58B7\u58B8\u58B9\u58BA"+ + "\u58BB\u58BC\u58BD\u58BE\u58BF\u58C0\u58C1\u58C2"+ + "\u58C3\u58C4\u58C5\u58C6\u58C7\u58C8\u58C9\u58CA"+ + "\u58CB\u58CC\u58CD\u58CE\u58CF\u58D0\u58D1\u58D2"+ + "\u58D3\u58D4\u58D5\u58D6\u58D7\u58D8\u58D9\u58DA"+ + "\u58DB\u58DC\u58DD\u58DE\u58DF\u58E0\u58E1\u58E2"+ + "\u58E3\u58E4\u58E5\u58E6\u58E7\u58E8\u58E9\u58EA"+ + "\u58EB\u58EC\u58ED\u58EE\u58EF\u58F0\u58F1\u58F2"+ + "\u58F3\u58F4\u58F5\u58F6\u58F7\u58F8\uFE69\u58F9"+ + "\u58FA\u58FB\u58FC\u58FD\u58FE\u58FF\u5900\u5901"+ + "\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909"+ + "\u590A\u590B\u590C\u590D\u590E\u590F\u5910\u5911"+ + "\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919"+ + "\u591A\u591B\u591C\u591D\u591E\u591F\u5920\u5921"+ + "\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929"+ + "\u592A\u592B\u592C\u592D\u592E\u592F\u5930\u5931"+ + "\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939"+ + "\u593A\u593B\u593C\u593D\u593E\u593F\u5940\u5941"+ + "\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949"+ + "\u594A\u594B\u594C\u594D\u594E\u594F\u5950\u5951"+ + "\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959"+ + "\u595A\u595B\u595C\u595D\u595E\u595F\u5960\u5961"+ + "\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969"+ + "\uFE6A\u596A\u596B\u596C\u596D\u596E\u596F\u5970"+ + "\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978"+ + "\u5979\u597A\u597B\u597C\u597D\u597E\u597F\u5980"+ + "\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988"+ + "\u5989\u598A\u598B\u598C\u598D\u598E\u598F\u5990"+ + "\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998"+ + "\u5999\u599A\u599B\u599C\u599D\u599E\u599F\u59A0"+ + "\u59A1\u59A2\u59A3\u59A4\u59A5\u59A6\u59A7\u59A8"+ + "\u59A9\u59AA\u59AB\u59AC\u59AD\u59AE\u59AF\u59B0"+ + "\u59B1\u59B2\u59B3\u59B4\u59B5\u59B6\u59B7\u59B8"+ + "\u59B9\u59BA\u59BB\u59BC\u59BD\u59BE\u59BF\u59C0"+ + "\u59C1\u59C2\u59C3\u59C4\u59C5\u59C6\u59C7\u59C8"+ + "\u59C9\u59CA\u59CB\u59CC\u59CD\u59CE\u59CF\u59D0"+ + "\u59D1\u59D2\u59D3\u59D4\u59D5\u59D6\u59D7\u59D8"+ + "\u59D9\u59DA\u59DB\u59DC\u59DD\u59DE\u59DF\u59E0"+ + "\u59E1\u59E2\u59E3\u59E4\u59E5\u59E6\u59E7\u59E8"+ + "\u59E9\u59EA\u59EB\u59EC\u59ED\u59EE\u59EF\u59F0"+ + "\u59F1\u59F2\u59F3\u59F4\u59F5\u59F6\u59F7\u59F8"+ + "\u59F9\u59FA\u59FB\u59FC\u59FD\u59FE\u59FF\u5A00"+ + "\u5A01\u5A02\u5A03\u5A04\u5A05\u5A06\u5A07\u5A08"+ + "\u5A09\u5A0A\u5A0B\u5A0C\u5A0D\u5A0E\u5A0F\u5A10"+ + "\u5A11\u5A12\u5A13\u5A14\u5A15\u5A16\u5A17\u5A18"+ + "\u5A19\u5A1A\u5A1B\u5A1C\u5A1D\u5A1E\u5A1F\u5A20"+ + "\u5A21\u5A22\u5A23\u5A24\u5A25\u5A26\u5A27\u5A28"+ + "\u5A29\u5A2A\u5A2B\u5A2C\u5A2D\u5A2E\u5A2F\u5A30"+ + "\u5A31\u5A32\u5A33\u5A34\u5A35\u5A36\u5A37\u5A38"+ + "\u5A39\u5A3A\u5A3B\u5A3C\u5A3D\u5A3E\u5A3F\u5A40"+ + "\u5A41\u5A42\u5A43\u5A44\u5A45\u5A46\u5A47\u5A48"+ + "\u5A49\u5A4A\u5A4B\u5A4C\u5A4D\u5A4E\u5A4F\u5A50"+ + "\u5A51\u5A52\u5A53\u5A54\u5A55\u5A56\u5A57\u5A58"+ + "\u5A59\u5A5A\u5A5B\u5A5C\u5A5D\u5A5E\u5A5F\u5A60"+ + "\u5A61\u5A62\u5A63\u5A64\u5A65\u5A66\u5A67\u5A68"+ + "\u5A69\u5A6A\u5A6B\u5A6C\u5A6D\u5A6E\u5A6F\u5A70"+ + "\u5A71\u5A72\u5A73\u5A74\u5A75\u5A76\u5A77\u5A78"+ + "\u5A79\u5A7A\u5A7B\u5A7C\u5A7D\u5A7E\u5A7F\u5A80"+ + "\u5A81\u5A82\u5A83\u5A84\u5A85\u5A86\u5A87\u5A88"+ + "\u5A89\u5A8A\u5A8B\u5A8C\u5A8D\u5A8E\u5A8F\u5A90"+ + "\u5A91\u5A92\u5A93\u5A94\u5A95\u5A96\u5A97\u5A98"+ + "\u5A99\u5A9A\u5A9B\u5A9C\u5A9D\u5A9E\u5A9F\u5AA0"+ + "\u5AA1\u5AA2\u5AA3\u5AA4\u5AA5\u5AA6\u5AA7\u5AA8"+ + "\u5AA9\u5AAA\u5AAB\u5AAC\u5AAD\u5AAE\u5AAF\u5AB0"+ + "\u5AB1\u5AB2\u5AB3\u5AB4\u5AB5\u5AB6\u5AB7\u5AB8"+ + "\u5AB9\u5ABA\u5ABB\u5ABC\u5ABD\u5ABE\u5ABF\u5AC0"+ + "\u5AC1\u5AC2\u5AC3\u5AC4\u5AC5\u5AC6\u5AC7\u5AC8"+ + "\u5AC9\u5ACA\u5ACB\u5ACC\u5ACD\u5ACE\u5ACF\u5AD0"+ + "\u5AD1\u5AD2\u5AD3\u5AD4\u5AD5\u5AD6\u5AD7\u5AD8"+ + "\u5AD9\u5ADA\u5ADB\u5ADC\u5ADD\u5ADE\u5ADF\u5AE0"+ + "\u5AE1\u5AE2\u5AE3\u5AE4\u5AE5\u5AE6\u5AE7\u5AE8"+ + "\u5AE9\u5AEA\u5AEB\u5AEC\u5AED\u5AEE\u5AEF\u5AF0"+ + "\u5AF1\u5AF2\u5AF3\u5AF4\u5AF5\u5AF6\u5AF7\u5AF8"+ + "\u5AF9\u5AFA\u5AFB\u5AFC\u5AFD\u5AFE\u5AFF\u5B00"+ + "\u5B01\u5B02\u5B03\u5B04\u5B05\u5B06\u5B07\u5B08"+ + "\u5B09\u5B0A\u5B0B\u5B0C\u5B0D\u5B0E\u5B0F\u5B10"+ + "\u5B11\u5B12\u5B13\u5B14\u5B15\u5B16\u5B17\u5B18"+ + "\u5B19\u5B1A\u5B1B\u5B1C\u5B1D\u5B1E\u5B1F\u5B20"+ + "\u5B21\u5B22\u5B23\u5B24\u5B25\u5B26\u5B27\u5B28"+ + "\u5B29\u5B2A\u5B2B\u5B2C\u5B2D\u5B2E\u5B2F\u5B30"+ + "\u5B31\u5B32\u5B33\u5B34\u5B35\u5B36\u5B37\u5B38"+ + "\u5B39\u5B3A\u5B3B\u5B3C\u5B3D\u5B3E\u5B3F\u5B40"+ + "\u5B41\u5B42\u5B43\u5B44\u5B45\u5B46\u5B47\u5B48"+ + "\u5B49\u5B4A\u5B4B\u5B4C\u5B4D\u5B4E\u5B4F\u5B50"+ + "\u5B51\u5B52\u5B53\u5B54\u5B55\u5B56\u5B57\u5B58"+ + "\u5B59\u5B5A\u5B5B\u5B5C\u5B5D\u5B5E\u5B5F\u5B60"+ + "\u5B61\u5B62\u5B63\u5B64\u5B65\u5B66\u5B67\u5B68"+ + "\u5B69\u5B6A\u5B6B\u5B6C\u5B6D\u5B6E\u5B6F\u5B70"+ + "\u5B71\u5B72\u5B73\u5B74\u5B75\u5B76\u5B77\u5B78"+ + "\u5B79\u5B7A\u5B7B\u5B7C\u5B7D\u5B7E\u5B7F\u5B80"+ + "\u5B81\u5B82\u5B83\u5B84\u5B85\u5B86\u5B87\u5B88"+ + "\u5B89\u5B8A\u5B8B\u5B8C\u5B8D\u5B8E\u5B8F\u5B90"+ + "\u5B91\u5B92\u5B93\u5B94\u5B95\u5B96\u5B97\u5B98"+ + "\u5B99\u5B9A\u5B9B\u5B9C\u5B9D\u5B9E\u5B9F\u5BA0"+ + "\u5BA1\u5BA2\u5BA3\u5BA4\u5BA5\u5BA6\u5BA7\u5BA8"+ + "\u5BA9\u5BAA\u5BAB\u5BAC\u5BAD\u5BAE\u5BAF\u5BB0"+ + "\u5BB1\u5BB2\u5BB3\u5BB4\u5BB5\u5BB6\u5BB7\u5BB8"+ + "\u5BB9\u5BBA\u5BBB\u5BBC\u5BBD\u5BBE\u5BBF\u5BC0"+ + "\u5BC1\u5BC2\u5BC3\u5BC4\u5BC5\u5BC6\u5BC7\u5BC8"+ + "\u5BC9\u5BCA\u5BCB\u5BCC\u5BCD\u5BCE\u5BCF\u5BD0"+ + "\u5BD1\u5BD2\u5BD3\u5BD4\u5BD5\u5BD6\u5BD7\u5BD8"+ + "\u5BD9\u5BDA\u5BDB\u5BDC\u5BDD\u5BDE\u5BDF\u5BE0"+ + "\u5BE1\u5BE2\u5BE3\u5BE4\u5BE5\u5BE6\u5BE7\u5BE8"+ + "\u5BE9\u5BEA\u5BEB\u5BEC\u5BED\u5BEE\u5BEF\u5BF0"+ + "\u5BF1\u5BF2\u5BF3\u5BF4\u5BF5\u5BF6\u5BF7\u5BF8"+ + "\u5BF9\u5BFA\u5BFB\u5BFC\u5BFD\u5BFE\u5BFF\u5C00"+ + "\u5C01\u5C02\u5C03\u5C04\u5C05\u5C06\u5C07\u5C08"+ + "\u5C09\u5C0A\u5C0B\u5C0C\u5C0D\u5C0E\u5C0F\u5C10"+ + "\u5C11\u5C12\u5C13\u5C14\u5C15\u5C16\u5C17\u5C18"+ + "\u5C19\u5C1A\u5C1B\u5C1C\u5C1D\u5C1E\u5C1F\u5C20"+ + "\u5C21\u5C22\u5C23\u5C24\u5C25\u5C26\u5C27\u5C28"+ + "\u5C29\u5C2A\u5C2B\u5C2C\u5C2D\u5C2E\u5C2F\u5C30"+ + "\u5C31\u5C32\u5C33\u5C34\u5C35\u5C36\u5C37\u5C38"+ + "\u5C39\u5C3A\u5C3B\u5C3C\u5C3D\u5C3E\u5C3F\u5C40"+ + "\u5C41\u5C42\u5C43\u5C44\u5C45\u5C46\u5C47\u5C48"+ + "\u5C49\u5C4A\u5C4B\u5C4C\u5C4D\u5C4E\u5C4F\u5C50"+ + "\u5C51\u5C52\u5C53\u5C54\u5C55\u5C56\u5C57\u5C58"+ + "\u5C59\u5C5A\u5C5B\u5C5C\u5C5D\u5C5E\u5C5F\u5C60"+ + "\u5C61\u5C62\u5C63\u5C64\u5C65\u5C66\u5C67\u5C68"+ + "\u5C69\u5C6A\u5C6B\u5C6C\u5C6D\u5C6E\u5C6F\u5C70"+ + "\u5C71\u5C72\u5C73\u5C74\u5C75\u5C76\u5C77\u5C78"+ + "\u5C79\u5C7A\u5C7B\u5C7C\u5C7D\u5C7E\u5C7F\u5C80"+ + "\u5C81\u5C82\u5C83\u5C84\u5C85\u5C86\u5C87\u5C88"; + + private final static String innerEncoderIndex4= + "\u5C89\u5C8A\u5C8B\u5C8C\u5C8D\u5C8E\u5C8F\u5C90"+ + "\u5C91\u5C92\u5C93\u5C94\u5C95\u5C96\u5C97\u5C98"+ + "\u5C99\u5C9A\u5C9B\u5C9C\u5C9D\u5C9E\u5C9F\u5CA0"+ + "\u5CA1\u5CA2\u5CA3\u5CA4\u5CA5\u5CA6\u5CA7\u5CA8"+ + "\u5CA9\u5CAA\u5CAB\u5CAC\u5CAD\u5CAE\u5CAF\u5CB0"+ + "\u5CB1\u5CB2\u5CB3\u5CB4\u5CB5\u5CB6\u5CB7\u5CB8"+ + "\u5CB9\u5CBA\u5CBB\u5CBC\u5CBD\u5CBE\u5CBF\u5CC0"+ + "\u5CC1\u5CC2\u5CC3\u5CC4\u5CC5\u5CC6\u5CC7\u5CC8"+ + "\u5CC9\u5CCA\u5CCB\u5CCC\u5CCD\u5CCE\u5CCF\u5CD0"+ + "\u5CD1\u5CD2\u5CD3\u5CD4\u5CD5\u5CD6\u5CD7\u5CD8"+ + "\u5CD9\u5CDA\u5CDB\u5CDC\u5CDD\u5CDE\uFE6F\u5CDF"+ + "\u5CE0\u5CE1\u5CE2\u5CE3\u5CE4\u5CE5\u5CE6\u5CE7"+ + "\u5CE8\u5CE9\u5CEA\u5CEB\u5CEC\u5CED\u5CEE\u5CEF"+ + "\u5CF0\u5CF1\u5CF2\u5CF3\u5CF4\u5CF5\u5CF6\u5CF7"+ + "\u5CF8\u5CF9\u5CFA\u5CFB\u5CFC\u5CFD\u5CFE\u5CFF"+ + "\u5D00\u5D01\u5D02\u5D03\u5D04\u5D05\u5D06\u5D07"+ + "\u5D08\u5D09\u5D0A\u5D0B\u5D0C\u5D0D\u5D0E\u5D0F"+ + "\u5D10\u5D11\u5D12\u5D13\u5D14\u5D15\u5D16\u5D17"+ + "\u5D18\u5D19\u5D1A\u5D1B\u5D1C\u5D1D\u5D1E\u5D1F"+ + "\u5D20\u5D21\u5D22\u5D23\u5D24\u5D25\u5D26\u5D27"+ + "\u5D28\u5D29\u5D2A\u5D2B\u5D2C\u5D2D\u5D2E\u5D2F"+ + "\u5D30\u5D31\u5D32\u5D33\u5D34\u5D35\u5D36\u5D37"+ + "\u5D38\u5D39\u5D3A\u5D3B\u5D3C\u5D3D\u5D3E\u5D3F"+ + "\u5D40\u5D41\u5D42\u5D43\u5D44\u5D45\u5D46\u5D47"+ + "\u5D48\u5D49\u5D4A\u5D4B\u5D4C\u5D4D\u5D4E\u5D4F"+ + "\u5D50\u5D51\u5D52\u5D53\u5D54\u5D55\u5D56\u5D57"+ + "\u5D58\u5D59\u5D5A\u5D5B\u5D5C\u5D5D\u5D5E\u5D5F"+ + "\u5D60\u5D61\u5D62\u5D63\u5D64\u5D65\u5D66\u5D67"+ + "\u5D68\u5D69\u5D6A\u5D6B\u5D6C\u5D6D\u5D6E\u5D6F"+ + "\u5D70\u5D71\u5D72\u5D73\u5D74\u5D75\u5D76\u5D77"+ + "\u5D78\u5D79\u5D7A\u5D7B\u5D7C\u5D7D\u5D7E\u5D7F"+ + "\u5D80\u5D81\u5D82\u5D83\u5D84\u5D85\u5D86\u5D87"+ + "\u5D88\u5D89\u5D8A\u5D8B\u5D8C\u5D8D\u5D8E\u5D8F"+ + "\u5D90\u5D91\u5D92\u5D93\u5D94\u5D95\u5D96\u5D97"+ + "\u5D98\u5D99\u5D9A\u5D9B\u5D9C\u5D9D\u5D9E\u5D9F"+ + "\u5DA0\u5DA1\u5DA2\u5DA3\u5DA4\u5DA5\u5DA6\u5DA7"+ + "\u5DA8\u5DA9\u5DAA\u5DAB\u5DAC\u5DAD\u5DAE\u5DAF"+ + "\u5DB0\u5DB1\u5DB2\u5DB3\u5DB4\u5DB5\u5DB6\u5DB7"+ + "\u5DB8\u5DB9\u5DBA\u5DBB\u5DBC\u5DBD\u5DBE\u5DBF"+ + "\u5DC0\u5DC1\u5DC2\u5DC3\u5DC4\u5DC5\u5DC6\u5DC7"+ + "\u5DC8\u5DC9\u5DCA\u5DCB\u5DCC\u5DCD\u5DCE\u5DCF"+ + "\u5DD0\u5DD1\u5DD2\u5DD3\u5DD4\u5DD5\u5DD6\u5DD7"+ + "\u5DD8\u5DD9\u5DDA\u5DDB\u5DDC\u5DDD\u5DDE\u5DDF"+ + "\u5DE0\u5DE1\u5DE2\u5DE3\u5DE4\u5DE5\u5DE6\uFE70"+ + "\u5DE7\u5DE8\u5DE9\u5DEA\u5DEB\u5DEC\u5DED\u5DEE"+ + "\u5DEF\u5DF0\u5DF1\u5DF2\u5DF3\u5DF4\u5DF5\u5DF6"+ + "\u5DF7\u5DF8\u5DF9\u5DFA\u5DFB\u5DFC\u5DFD\u5DFE"+ + "\u5DFF\u5E00\u5E01\u5E02\u5E03\u5E04\u5E05\u5E06"+ + "\u5E07\u5E08\u5E09\u5E0A\u5E0B\u5E0C\u5E0D\u5E0E"+ + "\u5E0F\u5E10\u5E11\u5E12\u5E13\u5E14\u5E15\u5E16"+ + "\u5E17\u5E18\u5E19\u5E1A\u5E1B\u5E1C\u5E1D\u5E1E"+ + "\u5E1F\u5E20\u5E21\u5E22\u5E23\u5E24\u5E25\u5E26"+ + "\u5E27\u5E28\u5E29\u5E2A\u5E2B\u5E2C\u5E2D\u5E2E"+ + "\u5E2F\u5E30\u5E31\u5E32\u5E33\u5E34\u5E35\u5E36"+ + "\u5E37\u5E38\u5E39\u5E3A\u5E3B\u5E3C\u5E3D\u5E3E"+ + "\u5E3F\u5E40\u5E41\u5E42\u5E43\u5E44\u5E45\u5E46"+ + "\u5E47\u5E48\u5E49\u5E4A\u5E4B\u5E4C\u5E4D\u5E4E"+ + "\u5E4F\u5E50\u5E51\u5E52\u5E53\u5E54\u5E55\u5E56"+ + "\u5E57\u5E58\u5E59\u5E5A\u5E5B\u5E5C\u5E5D\u5E5E"+ + "\u5E5F\u5E60\u5E61\u5E62\u5E63\u5E64\u5E65\u5E66"+ + "\u5E67\u5E68\u5E69\u5E6A\u5E6B\u5E6C\u5E6D\u5E6E"+ + "\u5E6F\u5E70\u5E71\u5E72\u5E73\u5E74\u5E75\u5E76"+ + "\u5E77\u5E78\u5E79\u5E7A\u5E7B\u5E7C\u5E7D\u5E7E"+ + "\u5E7F\u5E80\u5E81\u5E82\u5E83\u5E84\u5E85\u5E86"+ + "\u5E87\u5E88\u5E89\u5E8A\u5E8B\u5E8C\u5E8D\u5E8E"+ + "\u5E8F\u5E90\u5E91\u5E92\u5E93\u5E94\u5E95\u5E96"+ + "\u5E97\u5E98\u5E99\u5E9A\u5E9B\u5E9C\u5E9D\u5E9E"+ + "\u5E9F\u5EA0\u5EA1\u5EA2\u5EA3\u5EA4\u5EA5\u5EA6"+ + "\u5EA7\u5EA8\u5EA9\u5EAA\u5EAB\u5EAC\u5EAD\u5EAE"+ + "\u5EAF\u5EB0\u5EB1\u5EB2\u5EB3\u5EB4\u5EB5\u5EB6"+ + "\u5EB7\u5EB8\u5EB9\u5EBA\u5EBB\u5EBC\u5EBD\u5EBE"+ + "\u5EBF\u5EC0\u5EC1\u5EC2\u5EC3\u5EC4\u5EC5\u5EC6"+ + "\u5EC7\u5EC8\u5EC9\u5ECA\u5ECB\u5ECC\u5ECD\u5ECE"+ + "\u5ECF\u5ED0\u5ED1\u5ED2\u5ED3\u5ED4\u5ED5\u5ED6"+ + "\u5ED7\u5ED8\u5ED9\u5EDA\u5EDB\u5EDC\u5EDD\u5EDE"+ + "\u5EDF\u5EE0\u5EE1\u5EE2\u5EE3\u5EE4\u5EE5\u5EE6"+ + "\u5EE7\u5EE8\u5EE9\u5EEA\u5EEB\u5EEC\u5EED\u5EEE"+ + "\u5EEF\u5EF0\u5EF1\u5EF2\u5EF3\u5EF4\u5EF5\u5EF6"+ + "\u5EF7\u5EF8\u5EF9\u5EFA\u5EFB\u5EFC\u5EFD\u5EFE"+ + "\u5EFF\u5F00\u5F01\u5F02\u5F03\u5F04\u5F05\u5F06"+ + "\u5F07\u5F08\u5F09\u5F0A\u5F0B\u5F0C\u5F0D\u5F0E"+ + "\u5F0F\u5F10\u5F11\u5F12\u5F13\u5F14\u5F15\u5F16"+ + "\u5F17\u5F18\u5F19\u5F1A\u5F1B\u5F1C\u5F1D\u5F1E"+ + "\u5F1F\u5F20\u5F21\u5F22\u5F23\u5F24\u5F25\u5F26"+ + "\u5F27\u5F28\u5F29\u5F2A\u5F2B\u5F2C\u5F2D\u5F2E"+ + "\u5F2F\u5F30\u5F31\u5F32\u5F33\u5F34\u5F35\u5F36"+ + "\u5F37\u5F38\u5F39\u5F3A\u5F3B\u5F3C\u5F3D\u5F3E"+ + "\u5F3F\u5F40\u5F41\u5F42\u5F43\u5F44\u5F45\u5F46"+ + "\u5F47\u5F48\u5F49\u5F4A\u5F4B\u5F4C\u5F4D\u5F4E"+ + "\u5F4F\u5F50\u5F51\u5F52\u5F53\u5F54\u5F55\u5F56"+ + "\u5F57\u5F58\u5F59\u5F5A\u5F5B\u5F5C\u5F5D\u5F5E"+ + "\u5F5F\u5F60\u5F61\u5F62\u5F63\u5F64\u5F65\u5F66"+ + "\u5F67\u5F68\u5F69\u5F6A\u5F6B\u5F6C\u5F6D\u5F6E"+ + "\u5F6F\u5F70\u5F71\u5F72\u5F73\u5F74\u5F75\u5F76"+ + "\u5F77\u5F78\u5F79\u5F7A\u5F7B\u5F7C\u5F7D\u5F7E"+ + "\u5F7F\u5F80\u5F81\u5F82\u5F83\u5F84\u5F85\u5F86"+ + "\u5F87\u5F88\u5F89\u5F8A\u5F8B\u5F8C\u5F8D\u5F8E"+ + "\u5F8F\u5F90\u5F91\u5F92\u5F93\u5F94\u5F95\u5F96"+ + "\u5F97\u5F98\u5F99\u5F9A\u5F9B\u5F9C\u5F9D\u5F9E"+ + "\u5F9F\u5FA0\u5FA1\u5FA2\u5FA3\u5FA4\u5FA5\u5FA6"+ + "\u5FA7\u5FA8\u5FA9\u5FAA\u5FAB\u5FAC\u5FAD\u5FAE"+ + "\u5FAF\u5FB0\u5FB1\u5FB2\u5FB3\u5FB4\u5FB5\u5FB6"+ + "\u5FB7\u5FB8\u5FB9\u5FBA\u5FBB\u5FBC\u5FBD\uFE72"+ + "\u5FBE\u5FBF\u5FC0\u5FC1\u5FC2\u5FC3\u5FC4\u5FC5"+ + "\u5FC6\u5FC7\u5FC8\u5FC9\u5FCA\u5FCB\u5FCC\u5FCD"+ + "\u5FCE\u5FCF\u5FD0\u5FD1\u5FD2\u5FD3\u5FD4\u5FD5"+ + "\u5FD6\u5FD7\u5FD8\u5FD9\u5FDA\u5FDB\u5FDC\u5FDD"+ + "\u5FDE\u5FDF\u5FE0\u5FE1\u5FE2\u5FE3\u5FE4\u5FE5"+ + "\u5FE6\u5FE7\u5FE8\u5FE9\u5FEA\u5FEB\u5FEC\u5FED"+ + "\u5FEE\u5FEF\u5FF0\u5FF1\u5FF2\u5FF3\u5FF4\u5FF5"+ + "\u5FF6\u5FF7\u5FF8\u5FF9\u5FFA\u5FFB\u5FFC\u5FFD"+ + "\u5FFE\u5FFF\u6000\u6001\u6002\u6003\u6004\u6005"+ + "\u6006\u6007\u6008\u6009\u600A\u600B\u600C\u600D"+ + "\u600E\u600F\u6010\u6011\u6012\u6013\u6014\u6015"+ + "\u6016\u6017\u6018\u6019\u601A\u601B\u601C\u601D"+ + "\u601E\u601F\u6020\u6021\u6022\u6023\u6024\u6025"+ + "\u6026\u6027\u6028\u6029\u602A\u602B\u602C\u602D"+ + "\u602E\u602F\u6030\u6031\uFE78\u6032\u6033\u6034"+ + "\u6035\uFE77\u6036\u6037\u6038\u6039\u603A\u603B"+ + "\u603C\u603D\u603E\u603F\u6040\u6041\u6042\u6043"+ + "\u6044\u6045\u6046\u6047\u6048\u6049\u604A\u604B"+ + "\u604C\u604D\u604E\u604F\u6050\u6051\u6052\u6053"+ + "\u6054\u6055\u6056\u6057\u6058\u6059\u605A\u605B"+ + "\u605C\u605D\u605E\u605F\u6060\uFE7A\u6061\u6062"+ + "\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606A"+ + "\u606B\u606C\u606D\u606E\u606F\u6070\u6071\u6072"+ + "\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607A"+ + "\u607B\u607C\u607D\u607E\u607F\u6080\u6081\u6082"+ + "\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608A"+ + "\u608B\u608C\u608D\u608E\u608F\u6090\u6091\u6092"+ + "\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609A"+ + "\u609B\u609C\u609D\u609E\u609F\u60A0\u60A1\u60A2"+ + "\u60A3\u60A4\u60A5\u60A6\u60A7\u60A8\u60A9\u60AA"+ + "\u60AB\u60AC\u60AD\u60AE\u60AF\u60B0\u60B1\u60B2"+ + "\u60B3\u60B4\u60B5\u60B6\u60B7\u60B8\u60B9\u60BA"+ + "\u60BB\u60BC\u60BD\u60BE\u60BF\u60C0\u60C1\u60C2"+ + "\u60C3\u60C4\u60C5\u60C6\u60C7\u60C8\u60C9\u60CA"+ + "\u60CB\u60CC\u60CD\u60CE\u60CF\u60D0\u60D1\u60D2"+ + "\u60D3\u60D4\u60D5\u60D6\u60D7\u60D8\u60D9\u60DA"+ + "\u60DB\u60DC\u60DD\u60DE\u60DF\u60E0\u60E1\u60E2"+ + "\u60E3\u60E4\u60E5\u60E6\u60E7\u60E8\u60E9\u60EA"+ + "\u60EB\u60EC\u60ED\u60EE\u60EF\u60F0\u60F1\u60F2"+ + "\u60F3\u60F4\u60F5\u60F6\u60F7\u60F8\u60F9\u60FA"+ + "\u60FB\u60FC\u60FD\u60FE\u60FF\u6100\u6101\u6102"+ + "\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610A"+ + "\u610B\u610C\u610D\u610E\u610F\u6110\u6111\u6112"+ + "\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611A"+ + "\u611B\u611C\u611D\u611E\u611F\u6120\u6121\u6122"+ + "\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612A"+ + "\u612B\u612C\u612D\u612E\u612F\u6130\u6131\u6132"+ + "\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613A"+ + "\u613B\u613C\u613D\u613E\u613F\u6140\u6141\u6142"+ + "\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614A"+ + "\u614B\u614C\u614D\u614E\u614F\u6150\u6151\u6152"+ + "\u6153\u6154\u6155\u6156\u6157\u6158\uFE7B\u6159"+ + "\u615A\u615B\u615C\u615D\u615E\u615F\u6160\u6161"+ + "\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169"+ + "\u616A\u616B\u616C\u616D\u616E\u616F\u6170\u6171"+ + "\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179"+ + "\u617A\u617B\u617C\u617D\u617E\u617F\u6180\u6181"+ + "\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189"+ + "\u618A\u618B\u618C\u618D\u618E\u618F\u6190\u6191"+ + "\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199"+ + "\u619A\u619B\u619C\u619D\u619E\u619F\u61A0\u61A1"+ + "\u61A2\u61A3\u61A4\u61A5\u61A6\u61A7\u61A8\u61A9"+ + "\u61AA\u61AB\u61AC\u61AD\u61AE\u61AF\u61B0\u61B1"+ + "\u61B2\u61B3\u61B4\u61B5\u61B6\u61B7\u61B8\u61B9"+ + "\u61BA\u61BB\u61BC\u61BD\u61BE\u61BF\u61C0\u61C1"+ + "\u61C2\u61C3\u61C4\u61C5\u61C6\u61C7\u61C8\u61C9"+ + "\u61CA\u61CB\u61CC\u61CD\u61CE\u61CF\u61D0\u61D1"+ + "\u61D2\u61D3\u61D4\u61D5\u61D6\u61D7\u61D8\u61D9"+ + "\u61DA\u61DB\u61DC\u61DD\u61DE\u61DF\u61E0\u61E1"+ + "\u61E2\u61E3\u61E4\u61E5\u61E6\u61E7\u61E8\u61E9"+ + "\u61EA\u61EB\u61EC\u61ED\u61EE\u61EF\u61F0\u61F1"+ + "\u61F2\u61F3\u61F4\u61F5\u61F6\u61F7\u61F8\u61F9"+ + "\u61FA\u61FB\u61FC\u61FD\u61FE\u61FF\u6200\u6201"+ + "\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209"+ + "\u620A\u620B\u620C\u620D\u620E\u620F\u6210\u6211"+ + "\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219"+ + "\u621A\u621B\u621C\u621D\u621E\u621F\u6220\u6221"+ + "\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229"+ + "\u622A\u622B\u622C\u622D\u622E\u622F\u6230\u6231"+ + "\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239"+ + "\u623A\u623B\u623C\u623D\u623E\u623F\u6240\u6241"+ + "\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249"+ + "\u624A\u624B\u624C\u624D\u624E\u624F\u6250\u6251"+ + "\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259"+ + "\u625A\u625B\u625C\u625D\u625E\u625F\u6260\u6261"+ + "\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269"+ + "\u626A\u626B\u626C\u626D\u626E\u626F\u6270\u6271"+ + "\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279"+ + "\u627A\u627B\u627C\u627D\u627E\u627F\u6280\u6281"+ + "\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289"+ + "\u628A\u628B\u628C\u628D\u628E\u628F\u6290\u6291"+ + "\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299"+ + "\u629A\u629B\u629C\u629D\u629E\u629F\u62A0\u62A1"+ + "\u62A2\u62A3\u62A4\u62A5\u62A6\u62A7\u62A8\u62A9"+ + "\u62AA\u62AB\u62AC\u62AD\u62AE\u62AF\u62B0\u62B1"+ + "\u62B2\u62B3\u62B4\u62B5\u62B6\u62B7\u62B8\u62B9"+ + "\u62BA\u62BB\u62BC\u62BD\u62BE\u62BF\u62C0\u62C1"+ + "\u62C2\u62C3\u62C4\u62C5\u62C6\u62C7\u62C8\u62C9"+ + "\u62CA\u62CB\u62CC\u62CD\uFE7D\u62CE\u62CF\u62D0"+ + "\u62D1\u62D2\u62D3\u62D4\u62D5\u62D6\u62D7\u62D8"+ + "\u62D9\u62DA\u62DB\u62DC\u62DD\u62DE\u62DF\u62E0"+ + "\u62E1\uFE7C\u62E2\u62E3\u62E4\u62E5\u62E6\u62E7"+ + "\u62E8\u62E9\u62EA\u62EB\u62EC\u62ED\u62EE\u62EF"+ + "\u62F0\u62F1\u62F2\u62F3\u62F4\u62F5\u62F6\u62F7"+ + "\u62F8\u62F9\u62FA\u62FB\u62FC\u62FD\u62FE\u62FF"+ + "\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307"+ + "\u6308\u6309\u630A\u630B\u630C\u630D\u630E\u630F"+ + "\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317"+ + "\u6318\u6319\u631A\u631B\u631C\u631D\u631E\u631F"+ + "\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327"+ + "\u6328\u6329\u632A\u632B\u632C\u632D\u632E\u632F"+ + "\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337"+ + "\u6338\u6339\u633A\u633B\u633C\u633D\u633E\u633F"+ + "\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347"+ + "\u6348\u6349\u634A\u634B\u634C\u634D\u634E\u634F"+ + "\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357"+ + "\u6358\u6359\u635A\u635B\u635C\u635D\u635E\u635F"+ + "\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367"+ + "\u6368\u6369\u636A\u636B\u636C\u636D\u636E\u636F"+ + "\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377"+ + "\u6378\u6379\u637A\u637B\u637C\u637D\u637E\u637F"+ + "\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387"+ + "\u6388\u6389\u638A\u638B\u638C\u638D\u638E\u638F"+ + "\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397"+ + "\u6398\u6399\u639A\u639B\u639C\u639D\u639E\u639F"+ + "\u63A0\u63A1\u63A2\uFE80\u63A3\u63A4\u63A5\u63A6"+ + "\u63A7\uFE81\u63A8\u63A9\u63AA\u63AB\u63AC\u63AD"+ + "\u63AE\u63AF\u63B0\u63B1\u63B2\u63B3\u63B4\u63B5"+ + "\u63B6\u63B7\u63B8\u63B9\u63BA\u63BB\u63BC\u63BD"+ + "\u63BE\u63BF\u63C0\u63C1\u63C2\u63C3\u63C4\u63C5"+ + "\u63C6\u63C7\u63C8\u63C9\u63CA\u63CB\u63CC\u63CD"+ + "\u63CE\u63CF\u63D0\u63D1\u63D2\u63D3\u63D4\u63D5"+ + "\u63D6\u63D7\u63D8\u63D9\u63DA\u63DB\u63DC\u63DD"+ + "\u63DE\u63DF\u63E0\u63E1\u63E2\u63E3\u63E4\u63E5"+ + "\u63E6\u63E7\u63E8\u63E9\u63EA\u63EB\u63EC\u63ED"+ + "\u63EE\u63EF\u63F0\u63F1\u63F2\u63F3\u63F4\u63F5"+ + "\u63F6\u63F7\u63F8\u63F9\uFE82\u63FA\u63FB\u63FC"+ + "\u63FD\u63FE\u63FF\u6400\u6401\u6402\u6403\u6404"+ + "\u6405\u6406\u6407\u6408\u6409\uFE83\u640A\u640B"+ + "\u640C\u640D\u640E\u640F\u6410\u6411\u6412\u6413"+ + "\u6414\u6415\u6416\u6417\u6418\u6419\u641A\u641B"+ + "\u641C\u641D\u641E\u641F\u6420\u6421\u6422\u6423"+ + "\u6424\u6425\u6426\u6427\u6428\u6429\u642A\u642B"+ + "\u642C\u642D\u642E\u642F\u6430\u6431\u6432\u6433"+ + "\u6434\u6435\u6436\u6437\u6438\u6439\u643A\u643B"+ + "\u643C\u643D\u643E\u643F\u6440\u6441\u6442\u6443"+ + "\u6444\u6445\u6446\u6447\u6448\u6449\u644A\u644B"+ + "\u644C\u644D\u644E\u644F\u6450\u6451\u6452\u6453"+ + "\u6454\u6455\u6456\u6457\u6458\u6459\u645A\u645B"+ + "\u645C\u645D\u645E\u645F\u6460\u6461\u6462\u6463"+ + "\u6464\u6465\u6466\u6467\u6468\u6469\u646A\u646B"+ + "\u646C\u646D\u646E\u646F\u6470\u6471\u6472\u6473"+ + "\u6474\u6475\u6476\u6477\u6478\u6479\u647A\u647B"+ + "\u647C\u647D\u647E\u647F\u6480\u6481\u6482\u6483"+ + "\u6484\u6485\u6486\u6487\u6488\u6489\u648A\u648B"+ + "\u648C\u648D\u648E\u648F\u6490\u6491\u6492\u6493"+ + "\u6494\u6495\u6496\u6497\u6498\u6499\u649A\u649B"+ + "\u649C\u649D\u649E\u649F\u64A0\u64A1\u64A2\u64A3"+ + "\u64A4\u64A5\u64A6\u64A7\u64A8\u64A9\u64AA\u64AB"+ + "\u64AC\u64AD\u64AE\u64AF\u64B0\u64B1\u64B2\u64B3"+ + "\u64B4\u64B5\u64B6\u64B7\u64B8\u64B9\u64BA\u64BB"+ + "\u64BC\u64BD\u64BE\u64BF\u64C0\u64C1\u64C2\u64C3"+ + "\u64C4\u64C5\u64C6\u64C7\u64C8\u64C9\u64CA\u64CB"+ + "\u64CC\u64CD\u64CE\u64CF\u64D0\u64D1\u64D2\u64D3"+ + "\u64D4\u64D5\u64D6\u64D7\u64D8\u64D9\u64DA\u64DB"+ + "\u64DC\u64DD\u64DE\u64DF\u64E0\u64E1\u64E2\u64E3"+ + "\u64E4\u64E5\u64E6\u64E7\u64E8\u64E9\u64EA\u64EB"+ + "\u64EC\u64ED\u64EE\u64EF\u64F0\u64F1\u64F2\u64F3"+ + "\u64F4\u64F5\u64F6\u64F7\u64F8\u64F9\u64FA\u64FB"+ + "\u64FC\u64FD\u64FE\u64FF\u6500\u6501\u6502\u6503"+ + "\u6504\u6505\u6506\u6507\u6508\u6509\u650A\u650B"+ + "\u650C\u650D\u650E\u650F\u6510\u6511\u6512\u6513"+ + "\u6514\u6515\u6516\u6517\u6518\u6519\u651A\u651B"+ + "\u651C\u651D\u651E\u651F\u6520\u6521\u6522\u6523"+ + "\u6524\u6525\u6526\u6527\u6528\u6529\u652A\u652B"+ + "\u652C\u652D\u652E\u652F\u6530\u6531\u6532\u6533"+ + "\u6534\u6535\u6536\u6537\u6538\u6539\u653A\u653B"+ + "\u653C\u653D\u653E\u653F\u6540\u6541\u6542\u6543"+ + "\u6544\u6545\u6546\u6547\u6548\u6549\u654A\u654B"+ + "\u654C\u654D\u654E\u654F\u6550\u6551\u6552\u6553"+ + "\u6554\u6555\u6556\u6557\u6558\u6559\u655A\u655B"+ + "\u655C\u655D\u655E\u655F\u6560\u6561\u6562\u6563"+ + "\u6564\u6565\u6566\u6567\u6568\u6569\u656A\u656B"+ + "\u656C\u656D\u656E\u656F\u6570\u6571\u6572\u6573"+ + "\u6574\u6575\u6576\u6577\u6578\u6579\u657A\u657B"+ + "\u657C\u657D\u657E\u657F\u6580\u6581\u6582\u6583"+ + "\u6584\u6585\u6586\u6587\u6588\u6589\u658A\u658B"+ + "\u658C\u658D\u658E\u658F\u6590\u6591\u6592\u6593"+ + "\u6594\u6595\u6596\u6597\u6598\u6599\u659A\u659B"+ + "\u659C\u659D\u659E\u659F\u65A0\u65A1\u65A2\u65A3"+ + "\u65A4\u65A5\u65A6\u65A7\u65A8\u65A9\u65AA\u65AB"+ + "\u65AC\u65AD\u65AE\u65AF\u65B0\u65B1\u65B2\u65B3"+ + "\u65B4\u65B5\u65B6\u65B7\u65B8\u65B9\u65BA\u65BB"+ + "\u65BC\u65BD\u65BE\u65BF\u65C0\u65C1\u65C2\uFE85"+ + "\u65C3\u65C4\u65C5\u65C6\u65C7\u65C8\u65C9\u65CA"+ + "\u65CB\u65CC\u65CD\u65CE\u65CF\u65D0\u65D1\u65D2"+ + "\u65D3\u65D4\u65D5\u65D6\u65D7\u65D8\u65D9\u65DA"+ + "\u65DB\u65DC\u65DD\u65DE\u65DF\u65E0\u65E1\u65E2"+ + "\u65E3\u65E4\u65E5\u65E6\u65E7\u65E8\u65E9\u65EA"+ + "\u65EB\u65EC\u65ED\u65EE\u65EF\u65F0\u65F1\u65F2"+ + "\u65F3\u65F4\uFE86\u65F5\u65F6\uFE87\u65F7\u65F8"+ + "\u65F9\u65FA\uFE88\uFE89\u65FB\uFE8A\uFE8B\u65FC"+ + "\u65FD\u65FE\u65FF\u6600\u6601\u6602\u6603\u6604"+ + "\u6605\u6606\u6607\u6608\u6609\u660A\u660B\u660C"+ + "\u660D\u660E\u660F\uFE8D\u6610\u6611\u6612\uFE8C"+ + "\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661A"+ + "\u661B\u661C\u661D\u661E\u661F\u6620\u6621\u6622"+ + "\u6623\u6624\u6625\u6626\u6627\u6628\uFE8F\uFE8E"+ + "\u6629\u662A\u662B\u662C\u662D\u662E\u662F\u6630"+ + "\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638"+ + "\u6639\u663A\u663B\u663C\u663D\u663E\u663F\u6640"+ + "\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648"+ + "\u6649\u664A\u664B\u664C\u664D\u664E\u664F\u6650"+ + "\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658"+ + "\u6659\u665A\u665B\u665C\u665D\u665E\u665F\u6660"+ + "\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668"+ + "\u6669\u666A\u666B\u666C\u666D\u666E\u666F\u6670"+ + "\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678"+ + "\u6679\u667A\u667B\u667C\u667D\u667E\u667F\u6680"+ + "\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688"+ + "\u6689\u668A\u668B\u668C\u668D\u668E\u668F\u6690"+ + "\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698"+ + "\u6699\u669A\u669B\u669C\u669D\u669E\u669F\u66A0"+ + "\u66A1\u66A2\u66A3\u66A4\u66A5\u66A6\u66A7\u66A8"+ + "\u66A9\u66AA\u66AB\u66AC\u66AD\u66AE\u66AF\u66B0"+ + "\u66B1\u66B2\u66B3\u66B4\u66B5\u66B6\u66B7\u66B8"+ + "\u66B9\u66BA\u66BB\u66BC\u66BD\u66BE\u66BF\u66C0"+ + "\u66C1\u66C2\u66C3\u66C4\u66C5\u66C6\u66C7\u66C8"+ + "\u66C9\u66CA\u66CB\u66CC\u66CD\u66CE\u66CF\u66D0"+ + "\u66D1\u66D2\u66D3\u66D4\u66D5\u66D6\u66D7\u66D8"+ + "\u66D9\u66DA\u66DB\u66DC\u66DD\u66DE\u66DF\u66E0"+ + "\u66E1\u66E2\u66E3\u66E4\u66E5\u66E6\u66E7\u66E8"+ + "\u66E9\u66EA\u66EB\u66EC\u66ED\u66EE\u66EF\u66F0"+ + "\u66F1\u66F2\u66F3\u66F4\u66F5\u66F6\u66F7\u66F8"+ + "\u66F9\u66FA\u66FB\u66FC\u66FD\u66FE\u66FF\u6700"+ + "\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708"+ + "\u6709\u670A\u670B\u670C\u670D\u670E\u670F\u6710"+ + "\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718"+ + "\u6719\u671A\u671B\u671C\u671D\u671E\u671F\u6720"+ + "\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728"+ + "\u6729\u672A\u672B\u672C\u672D\u672E\u672F\u6730"+ + "\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738"+ + "\u6739\u673A\u673B\u673C\u673D\u673E\u673F\u6740"+ + "\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748"+ + "\u6749\u674A\u674B\u674C\u674D\u674E\u674F\u6750"+ + "\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758"+ + "\u6759\u675A\u675B\u675C\u675D\u675E\u675F\u6760"+ + "\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768"+ + "\u6769\u676A\u676B\u676C\u676D\u676E\u676F\u6770"+ + "\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778"+ + "\u6779\u677A\u677B\u677C\u677D\u677E\u677F\u6780"+ + "\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788"+ + "\u6789\u678A\u678B\u678C\u678D\u678E\u678F\u6790"+ + "\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798"+ + "\u6799\u679A\u679B\u679C\u679D\u679E\u679F\u67A0"+ + "\u67A1\u67A2\u67A3\u67A4\u67A5\u67A6\u67A7\u67A8"+ + "\u67A9\u67AA\u67AB\u67AC\u67AD\u67AE\u67AF\u67B0"+ + "\u67B1\u67B2\u67B3\u67B4\u67B5\u67B6\u67B7\u67B8"+ + "\u67B9\u67BA\u67BB\u67BC\u67BD\u67BE\u67BF\u67C0"+ + "\u67C1\u67C2\u67C3\u67C4\u67C5\u67C6\u67C7\u67C8"+ + "\u67C9\u67CA\u67CB\u67CC\u67CD\u67CE\u67CF\u67D0"+ + "\u67D1\u67D2\u67D3\u67D4\u67D5\u67D6\u67D7\u67D8"+ + "\u67D9\u67DA\u67DB\u67DC\u67DD\u67DE\u67DF\u67E0"+ + "\u67E1\u67E2\u67E3\u67E4\u67E5\u67E6\u67E7\u67E8"+ + "\u67E9\u67EA\u67EB\u67EC\u67ED\u67EE\u67EF\u67F0"+ + "\u67F1\u67F2\u67F3\u67F4\u67F5\u67F6\u67F7\u67F8"+ + "\u67F9\u67FA\u67FB\u67FC\u67FD\u67FE\u67FF\u6800"+ + "\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808"+ + "\u6809\u680A\u680B\u680C\u680D\u680E\u680F\u6810"+ + "\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818"+ + "\u6819\u681A\u681B\u681C\u681D\u681E\u681F\u6820"+ + "\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828"+ + "\u6829\u682A\u682B\u682C\u682D\u682E\u682F\u6830"+ + "\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838"+ + "\u6839\u683A\u683B\u683C\u683D\u683E\u683F\u6840"+ + "\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848"+ + "\u6849\u684A\u684B\u684C\u684D\u684E\u684F\u6850"+ + "\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858"+ + "\u6859\u685A\u685B\u685C\u685D\u685E\u685F\u6860"+ + "\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868"+ + "\u6869\u686A\u686B\u686C\u686D\u686E\u686F\u6870"+ + "\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878"+ + "\u6879\u687A\u687B\u687C\u687D\u687E\u687F\u6880"+ + "\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888"+ + "\u6889\u688A\u688B\u688C\u688D\u688E\u688F\u6890"+ + "\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898"+ + "\u6899\u689A\u689B\u689C\u689D\u689E\u689F\u68A0"+ + "\u68A1\u68A2\u68A3\u68A4\u68A5\u68A6\u68A7\u68A8"+ + "\u68A9\u68AA\u68AB\u68AC\u68AD\u68AE\u68AF\u68B0"+ + "\u68B1\u68B2\u68B3\u68B4\u68B5\u68B6\u68B7\u68B8"+ + "\u68B9\u68BA\u68BB\u68BC\u68BD\u68BE\u68BF\u68C0"+ + "\u68C1\u68C2\u68C3\u68C4\u68C5\u68C6\u68C7\u68C8"+ + "\u68C9\u68CA\u68CB\u68CC\u68CD\u68CE\u68CF\u68D0"+ + "\u68D1\u68D2\u68D3\u68D4\u68D5\u68D6\u68D7\u68D8"+ + "\u68D9\u68DA\u68DB\u68DC\u68DD\u68DE\u68DF\u68E0"+ + "\u68E1\u68E2\u68E3\u68E4\u68E5\u68E6\u68E7\uFE96"+ + "\u68E8\u68E9\u68EA\u68EB\u68EC\u68ED\u68EE\u68EF"+ + "\u68F0\u68F1\u68F2\u68F3\u68F4\u68F5\u68F6\u68F7"+ + "\u68F8\u68F9\u68FA\u68FB\u68FC\u68FD\u68FE\u68FF"+ + "\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907"+ + "\u6908\u6909\u690A\u690B\u690C\u690D\u690E\uFE93"+ + "\uFE94\uFE95\uFE97\uFE92\u690F\u6910\u6911\u6912"+ + "\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691A"+ + "\u691B\u691C\u691D\u691E\u691F\u6920\u6921\u6922"+ + "\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692A"+ + "\u692B\u692C\u692D\u692E\u692F\u6930\u6931\u6932"+ + "\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693A"+ + "\u693B\u693C\u693D\u693E\u693F\u6940\u6941\u6942"+ + "\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694A"+ + "\u694B\u694C\u694D\u694E\u694F\u6950\u6951\u6952"+ + "\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695A"+ + "\u695B\u695C\u695D\u695E\u695F\u6960\u6961\u6962"+ + "\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696A"+ + "\u696B\u696C\u696D\u696E\u696F\u6970\u6971\u6972"+ + "\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697A"+ + "\u697B\u697C\u697D\uFE98\uFE99\uFE9A\uFE9B\uFE9C"+ + "\uFE9D\uFE9E\u697E\u697F\u6980\u6981\u6982\u6983"+ + "\u6984\u6985\u6986\u6987\u6988\u6989\u698A\u698B"+ + "\u698C\u698D\u698E\u698F\u6990\u6991\u6992\u6993"+ + "\u6994\u6995\u6996\u6997\u6998\u6999\u699A\u699B"+ + "\u699C\u699D\u699E\u699F\u69A0\u69A1\u69A2\u69A3"+ + "\u69A4\u69A5\u69A6\u69A7\u69A8\u69A9\u69AA\u69AB"+ + "\u69AC\u69AD\u69AE\u69AF\u69B0\u69B1\u69B2\u69B3"+ + "\u69B4\u69B5\u69B6\u69B7\u69B8\u69B9\u69BA\u69BB"+ + "\u69BC\u69BD\u69BE\u69BF\u69C0\u69C1\u69C2\u69C3"+ + "\u69C4\u69C5\u69C6\u69C7\u69C8\u69C9\u69CA\u69CB"+ + "\u69CC\u69CD\u69CE\u69CF\u69D0\u69D1\u69D2\u69D3"+ + "\u69D4\u69D5\u69D6\u69D7\u69D8\u69D9\u69DA\u69DB"+ + "\u69DC\u69DD\u69DE\u69DF\u69E0\u69E1\u69E2\u69E3"+ + "\u69E4\u69E5\u69E6\u69E7\u69E8\u69E9\u69EA\u69EB"+ + "\u69EC\u69ED\u69EE\u69EF\u69F0\u69F1\u69F2\u69F3"+ + "\u69F4\u69F5\u69F6\u69F7\u69F8\u69F9\u69FA\u69FB"+ + "\u69FC\u69FD\u69FE\u69FF\u6A00\u6A01\u6A02\u6A03"+ + "\u6A04\u6A05\u6A06\u6A07\u6A08\u6A09\u6A0A\u6A0B"+ + "\u6A0C\u6A0D\u6A0E\u6A0F\u6A10\u6A11\uFE9F\u6A12"+ + "\u6A13\u6A14\u6A15\u6A16\u6A17\u6A18\u6A19\u6A1A"+ + "\u6A1B\u6A1C\u6A1D\u6A1E\u6A1F\u6A20\u6A21\u6A22"+ + "\u6A23\u6A24\u6A25\u6A26\u6A27\u6A28\u6A29\u6A2A"+ + "\u6A2B\u6A2C\u6A2D\u6A2E\u6A2F\u6A30\u6A31\u6A32"+ + "\u6A33\u6A34\u6A35\u6A36\u6A37\u6A38\u6A39\u6A3A"+ + "\u6A3B\u6A3C\u6A3D\u6A3E\u6A3F\u6A40\u6A41\u6A42"+ + "\u6A43\u6A44\u6A45\u6A46\u6A47\u6A48\u6A49\u6A4A"+ + "\u6A4B\u6A4C\u6A4D\u6A4E\u6A4F\u6A50\u6A51\u6A52"+ + "\u6A53\u6A54\u6A55\u6A56\u6A57\u6A58\u6A59\u6A5A"+ + "\u6A5B\u6A5C\u6A5D\u6A5E\u6A5F\u6A60\u6A61\u6A62"+ + "\uD2BB\uB6A1\u8140\uC6DF\u8141\u8142\u8143\uCDF2"+ + "\uD5C9\uC8FD\uC9CF\uCFC2\uD8A2\uB2BB\uD3EB\u8144"+ + "\uD8A4\uB3F3\u8145\uD7A8\uC7D2\uD8A7\uCAC0\u8146"+ + "\uC7F0\uB1FB\uD2B5\uB4D4\uB6AB\uCBBF\uD8A9\u8147"+ + "\u8148\u8149\uB6AA\u814A\uC1BD\uD1CF\u814B\uC9A5"+ + "\uD8AD\u814C\uB8F6\uD1BE\uE3DC\uD6D0\u814D\u814E"+ + "\uB7E1\u814F\uB4AE\u8150\uC1D9\u8151\uD8BC\u8152"+ + "\uCDE8\uB5A4\uCEAA\uD6F7\u8153\uC0F6\uBED9\uD8AF"+ + "\u8154\u8155\u8156\uC4CB\u8157\uBEC3\u8158\uD8B1"+ + "\uC3B4\uD2E5\u8159\uD6AE\uCEDA\uD5A7\uBAF5\uB7A6"+ + "\uC0D6\u815A\uC6B9\uC5D2\uC7C7\u815B\uB9D4\u815C"+ + "\uB3CB\uD2D2\u815D\u815E\uD8BF\uBEC5\uC6F2\uD2B2"+ + "\uCFB0\uCFE7\u815F\u8160\u8161\u8162\uCAE9\u8163"+ + "\u8164\uD8C0\u8165\u8166\u8167\u8168\u8169\u816A"+ + "\uC2F2\uC2D2\u816B\uC8E9\u816C\u816D\u816E\u816F"+ + "\u8170\u8171\u8172\u8173\u8174\u8175\uC7AC\u8176"+ + "\u8177\u8178\u8179\u817A\u817B\u817C\uC1CB\u817D"+ + "\uD3E8\uD5F9\u817E\uCAC2\uB6FE\uD8A1\uD3DA\uBFF7"+ + "\u8180\uD4C6\uBBA5\uD8C1\uCEE5\uBEAE\u8181\u8182"+ + "\uD8A8\u8183\uD1C7\uD0A9\u8184\u8185\u8186\uD8BD"+ + "\uD9EF\uCDF6\uBFBA\u8187\uBDBB\uBAA5\uD2E0\uB2FA"+ + "\uBAE0\uC4B6\u8188\uCFED\uBEA9\uCDA4\uC1C1\u8189"+ + "\u818A\u818B\uC7D7\uD9F1\u818C\uD9F4\u818D\u818E"+ + "\u818F\u8190\uC8CB\uD8E9\u8191\u8192\u8193\uD2DA"+ + "\uCAB2\uC8CA\uD8EC\uD8EA\uD8C6\uBDF6\uC6CD\uB3F0"+ + "\u8194\uD8EB\uBDF1\uBDE9\u8195\uC8D4\uB4D3\u8196"+ + "\u8197\uC2D8\u8198\uB2D6\uD7D0\uCACB\uCBFB\uD5CC"+ + "\uB8B6\uCFC9\u8199\u819A\u819B\uD9DA\uD8F0\uC7AA"+ + "\u819C\uD8EE\u819D\uB4FA\uC1EE\uD2D4\u819E\u819F"+ + "\uD8ED\u81A0\uD2C7\uD8EF\uC3C7\u81A1\u81A2\u81A3"+ + "\uD1F6\u81A4\uD6D9\uD8F2\u81A5\uD8F5\uBCFE\uBCDB"+ + "\u81A6\u81A7\u81A8\uC8CE\u81A9\uB7DD\u81AA\uB7C2"+ + "\u81AB\uC6F3\u81AC\u81AD\u81AE\u81AF\u81B0\u81B1"+ + "\u81B2\uD8F8\uD2C1\u81B3\u81B4\uCEE9\uBCBF\uB7FC"+ + "\uB7A5\uD0DD\u81B5\u81B6\u81B7\u81B8\u81B9\uD6DA"+ + "\uD3C5\uBBEF\uBBE1\uD8F1\u81BA\u81BB\uC9A1\uCEB0"+ + "\uB4AB\u81BC\uD8F3\u81BD\uC9CB\uD8F6\uC2D7\uD8F7"+ + "\u81BE\u81BF\uCEB1\uD8F9\u81C0\u81C1\u81C2\uB2AE"+ + "\uB9C0\u81C3\uD9A3\u81C4\uB0E9\u81C5\uC1E6\u81C6"+ + "\uC9EC\u81C7\uCBC5\u81C8\uCBC6\uD9A4\u81C9\u81CA"+ + "\u81CB\u81CC\u81CD\uB5E8\u81CE\u81CF\uB5AB\u81D0"+ + "\u81D1\u81D2\u81D3\u81D4\u81D5\uCEBB\uB5CD\uD7A1"+ + "\uD7F4\uD3D3\u81D6\uCCE5\u81D7\uBACE\u81D8\uD9A2"+ + "\uD9DC\uD3E0\uD8FD\uB7F0\uD7F7\uD8FE\uD8FA\uD9A1"+ + "\uC4E3\u81D9\u81DA\uD3B6\uD8F4\uD9DD\u81DB\uD8FB"+ + "\u81DC\uC5E5\u81DD\u81DE\uC0D0\u81DF\u81E0\uD1F0"+ + "\uB0DB\u81E1\u81E2\uBCD1\uD9A6\u81E3\uD9A5\u81E4"+ + "\u81E5\u81E6\u81E7\uD9AC\uD9AE\u81E8\uD9AB\uCAB9"+ + "\u81E9\u81EA\u81EB\uD9A9\uD6B6\u81EC\u81ED\u81EE"+ + "\uB3DE\uD9A8\u81EF\uC0FD\u81F0\uCACC\u81F1\uD9AA"+ + "\u81F2\uD9A7\u81F3\u81F4\uD9B0\u81F5\u81F6\uB6B1"+ + "\u81F7\u81F8\u81F9\uB9A9\u81FA\uD2C0\u81FB\u81FC"+ + "\uCFC0\u81FD\u81FE\uC2C2\u8240\uBDC4\uD5EC\uB2E0"+ + "\uC7C8\uBFEB\uD9AD\u8241\uD9AF\u8242\uCEEA\uBAEE"+ + "\u8243\u8244\u8245\u8246\u8247\uC7D6\u8248\u8249"+ + "\u824A\u824B\u824C\u824D\u824E\u824F\u8250\uB1E3"+ + "\u8251\u8252\u8253\uB4D9\uB6ED\uD9B4\u8254\u8255"+ + "\u8256\u8257\uBFA1\u8258\u8259\u825A\uD9DE\uC7CE"+ + "\uC0FE\uD9B8\u825B\u825C\u825D\u825E\u825F\uCBD7"+ + "\uB7FD\u8260\uD9B5\u8261\uD9B7\uB1A3\uD3E1\uD9B9"+ + "\u8262\uD0C5\u8263\uD9B6\u8264\u8265\uD9B1\u8266"+ + "\uD9B2\uC1A9\uD9B3\u8267\u8268\uBCF3\uD0DE\uB8A9"+ + "\u8269\uBEE3\u826A\uD9BD\u826B\u826C\u826D\u826E"+ + "\uD9BA\u826F\uB0B3\u8270\u8271\u8272\uD9C2\u8273"; + + private final static String innerEncoderIndex5= + "\u8274\u8275\u8276\u8277\u8278\u8279\u827A\u827B"+ + "\u827C\u827D\u827E\u8280\uD9C4\uB1B6\u8281\uD9BF"+ + "\u8282\u8283\uB5B9\u8284\uBEF3\u8285\u8286\u8287"+ + "\uCCC8\uBAF2\uD2D0\u8288\uD9C3\u8289\u828A\uBDE8"+ + "\u828B\uB3AB\u828C\u828D\u828E\uD9C5\uBEEB\u828F"+ + "\uD9C6\uD9BB\uC4DF\u8290\uD9BE\uD9C1\uD9C0\u8291"+ + "\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299"+ + "\u829A\u829B\uD5AE\u829C\uD6B5\u829D\uC7E3\u829E"+ + "\u829F\u82A0\u82A1\uD9C8\u82A2\u82A3\u82A4\uBCD9"+ + "\uD9CA\u82A5\u82A6\u82A7\uD9BC\u82A8\uD9CB\uC6AB"+ + "\u82A9\u82AA\u82AB\u82AC\u82AD\uD9C9\u82AE\u82AF"+ + "\u82B0\u82B1\uD7F6\u82B2\uCDA3\u82B3\u82B4\u82B5"+ + "\u82B6\u82B7\u82B8\u82B9\u82BA\uBDA1\u82BB\u82BC"+ + "\u82BD\u82BE\u82BF\u82C0\uD9CC\u82C1\u82C2\u82C3"+ + "\u82C4\u82C5\u82C6\u82C7\u82C8\u82C9\uC5BC\uCDB5"+ + "\u82CA\u82CB\u82CC\uD9CD\u82CD\u82CE\uD9C7\uB3A5"+ + "\uBFFE\u82CF\u82D0\u82D1\u82D2\uB8B5\u82D3\u82D4"+ + "\uC0FC\u82D5\u82D6\u82D7\u82D8\uB0F8\u82D9\u82DA"+ + "\u82DB\u82DC\u82DD\u82DE\u82DF\u82E0\u82E1\u82E2"+ + "\u82E3\u82E4\u82E5\u82E6\u82E7\u82E8\u82E9\u82EA"+ + "\u82EB\u82EC\u82ED\uB4F6\u82EE\uD9CE\u82EF\uD9CF"+ + "\uB4A2\uD9D0\u82F0\u82F1\uB4DF\u82F2\u82F3\u82F4"+ + "\u82F5\u82F6\uB0C1\u82F7\u82F8\u82F9\u82FA\u82FB"+ + "\u82FC\u82FD\uD9D1\uC9B5\u82FE\u8340\u8341\u8342"+ + "\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834A"+ + "\u834B\u834C\u834D\u834E\u834F\u8350\u8351\uCFF1"+ + "\u8352\u8353\u8354\u8355\u8356\u8357\uD9D2\u8358"+ + "\u8359\u835A\uC1C5\u835B\u835C\u835D\u835E\u835F"+ + "\u8360\u8361\u8362\u8363\u8364\u8365\uD9D6\uC9AE"+ + "\u8366\u8367\u8368\u8369\uD9D5\uD9D4\uD9D7\u836A"+ + "\u836B\u836C\u836D\uCBDB\u836E\uBDA9\u836F\u8370"+ + "\u8371\u8372\u8373\uC6A7\u8374\u8375\u8376\u8377"+ + "\u8378\u8379\u837A\u837B\u837C\u837D\uD9D3\uD9D8"+ + "\u837E\u8380\u8381\uD9D9\u8382\u8383\u8384\u8385"+ + "\u8386\u8387\uC8E5\u8388\u8389\u838A\u838B\u838C"+ + "\u838D\u838E\u838F\u8390\u8391\u8392\u8393\u8394"+ + "\u8395\uC0DC\u8396\u8397\u8398\u8399\u839A\u839B"+ + "\u839C\u839D\u839E\u839F\u83A0\u83A1\u83A2\u83A3"+ + "\u83A4\u83A5\u83A6\u83A7\u83A8\u83A9\u83AA\u83AB"+ + "\u83AC\u83AD\u83AE\u83AF\u83B0\u83B1\u83B2\uB6F9"+ + "\uD8A3\uD4CA\u83B3\uD4AA\uD0D6\uB3E4\uD5D7\u83B4"+ + "\uCFC8\uB9E2\u83B5\uBFCB\u83B6\uC3E2\u83B7\u83B8"+ + "\u83B9\uB6D2\u83BA\u83BB\uCDC3\uD9EE\uD9F0\u83BC"+ + "\u83BD\u83BE\uB5B3\u83BF\uB6B5\u83C0\u83C1\u83C2"+ + "\u83C3\u83C4\uBEA4\u83C5\u83C6\uC8EB\u83C7\u83C8"+ + "\uC8AB\u83C9\u83CA\uB0CB\uB9AB\uC1F9\uD9E2\u83CB"+ + "\uC0BC\uB9B2\u83CC\uB9D8\uD0CB\uB1F8\uC6E4\uBEDF"+ + "\uB5E4\uD7C8\u83CD\uD1F8\uBCE6\uCADE\u83CE\u83CF"+ + "\uBCBD\uD9E6\uD8E7\u83D0\u83D1\uC4DA\u83D2\u83D3"+ + "\uB8D4\uC8BD\u83D4\u83D5\uB2E1\uD4D9\u83D6\u83D7"+ + "\u83D8\u83D9\uC3B0\u83DA\u83DB\uC3E1\uDAA2\uC8DF"+ + "\u83DC\uD0B4\u83DD\uBEFC\uC5A9\u83DE\u83DF\u83E0"+ + "\uB9DA\u83E1\uDAA3\u83E2\uD4A9\uDAA4\u83E3\u83E4"+ + "\u83E5\u83E6\u83E7\uD9FB\uB6AC\u83E8\u83E9\uB7EB"+ + "\uB1F9\uD9FC\uB3E5\uBEF6\u83EA\uBFF6\uD2B1\uC0E4"+ + "\u83EB\u83EC\u83ED\uB6B3\uD9FE\uD9FD\u83EE\u83EF"+ + "\uBEBB\u83F0\u83F1\u83F2\uC6E0\u83F3\uD7BC\uDAA1"+ + "\u83F4\uC1B9\u83F5\uB5F2\uC1E8\u83F6\u83F7\uBCF5"+ + "\u83F8\uB4D5\u83F9\u83FA\u83FB\u83FC\u83FD\u83FE"+ + "\u8440\u8441\u8442\uC1DD\u8443\uC4FD\u8444\u8445"+ + "\uBCB8\uB7B2\u8446\u8447\uB7EF\u8448\u8449\u844A"+ + "\u844B\u844C\u844D\uD9EC\u844E\uC6BE\u844F\uBFAD"+ + "\uBBCB\u8450\u8451\uB5CA\u8452\uDBC9\uD0D7\u8453"+ + "\uCDB9\uB0BC\uB3F6\uBBF7\uDBCA\uBAAF\u8454\uD4E4"+ + "\uB5B6\uB5F3\uD8D6\uC8D0\u8455\u8456\uB7D6\uC7D0"+ + "\uD8D7\u8457\uBFAF\u8458\u8459\uDBBB\uD8D8\u845A"+ + "\u845B\uD0CC\uBBAE\u845C\u845D\u845E\uEBBE\uC1D0"+ + "\uC1F5\uD4F2\uB8D5\uB4B4\u845F\uB3F5\u8460\u8461"+ + "\uC9BE\u8462\u8463\u8464\uC5D0\u8465\u8466\u8467"+ + "\uC5D9\uC0FB\u8468\uB1F0\u8469\uD8D9\uB9CE\u846A"+ + "\uB5BD\u846B\u846C\uD8DA\u846D\u846E\uD6C6\uCBA2"+ + "\uC8AF\uC9B2\uB4CC\uBFCC\u846F\uB9F4\u8470\uD8DB"+ + "\uD8DC\uB6E7\uBCC1\uCCEA\u8471\u8472\u8473\u8474"+ + "\u8475\u8476\uCFF7\u8477\uD8DD\uC7B0\u8478\u8479"+ + "\uB9D0\uBDA3\u847A\u847B\uCCDE\u847C\uC6CA\u847D"+ + "\u847E\u8480\u8481\u8482\uD8E0\u8483\uD8DE\u8484"+ + "\u8485\uD8DF\u8486\u8487\u8488\uB0FE\u8489\uBEE7"+ + "\u848A\uCAA3\uBCF4\u848B\u848C\u848D\u848E\uB8B1"+ + "\u848F\u8490\uB8EE\u8491\u8492\u8493\u8494\u8495"+ + "\u8496\u8497\u8498\u8499\u849A\uD8E2\u849B\uBDCB"+ + "\u849C\uD8E4\uD8E3\u849D\u849E\u849F\u84A0\u84A1"+ + "\uC5FC\u84A2\u84A3\u84A4\u84A5\u84A6\u84A7\u84A8"+ + "\uD8E5\u84A9\u84AA\uD8E6\u84AB\u84AC\u84AD\u84AE"+ + "\u84AF\u84B0\u84B1\uC1A6\u84B2\uC8B0\uB0EC\uB9A6"+ + "\uBCD3\uCEF1\uDBBD\uC1D3\u84B3\u84B4\u84B5\u84B6"+ + "\uB6AF\uD6FA\uC5AC\uBDD9\uDBBE\uDBBF\u84B7\u84B8"+ + "\u84B9\uC0F8\uBEA2\uC0CD\u84BA\u84BB\u84BC\u84BD"+ + "\u84BE\u84BF\u84C0\u84C1\u84C2\u84C3\uDBC0\uCAC6"+ + "\u84C4\u84C5\u84C6\uB2AA\u84C7\u84C8\u84C9\uD3C2"+ + "\u84CA\uC3E3\u84CB\uD1AB\u84CC\u84CD\u84CE\u84CF"+ + "\uDBC2\u84D0\uC0D5\u84D1\u84D2\u84D3\uDBC3\u84D4"+ + "\uBFB1\u84D5\u84D6\u84D7\u84D8\u84D9\u84DA\uC4BC"+ + "\u84DB\u84DC\u84DD\u84DE\uC7DA\u84DF\u84E0\u84E1"+ + "\u84E2\u84E3\u84E4\u84E5\u84E6\u84E7\u84E8\u84E9"+ + "\uDBC4\u84EA\u84EB\u84EC\u84ED\u84EE\u84EF\u84F0"+ + "\u84F1\uD9E8\uC9D7\u84F2\u84F3\u84F4\uB9B4\uCEF0"+ + "\uD4C8\u84F5\u84F6\u84F7\u84F8\uB0FC\uB4D2\u84F9"+ + "\uD0D9\u84FA\u84FB\u84FC\u84FD\uD9E9\u84FE\uDECB"+ + "\uD9EB\u8540\u8541\u8542\u8543\uD8B0\uBBAF\uB1B1"+ + "\u8544\uB3D7\uD8CE\u8545\u8546\uD4D1\u8547\u8548"+ + "\uBDB3\uBFEF\u8549\uCFBB\u854A\u854B\uD8D0\u854C"+ + "\u854D\u854E\uB7CB\u854F\u8550\u8551\uD8D1\u8552"+ + "\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855A"+ + "\u855B\uC6A5\uC7F8\uD2BD\u855C\u855D\uD8D2\uC4E4"+ + "\u855E\uCAAE\u855F\uC7A7\u8560\uD8A6\u8561\uC9FD"+ + "\uCEE7\uBBDC\uB0EB\u8562\u8563\u8564\uBBAA\uD0AD"+ + "\u8565\uB1B0\uD7E4\uD7BF\u8566\uB5A5\uC2F4\uC4CF"+ + "\u8567\u8568\uB2A9\u8569\uB2B7\u856A\uB1E5\uDFB2"+ + "\uD5BC\uBFA8\uC2AC\uD8D5\uC2B1\u856B\uD8D4\uCED4"+ + "\u856C\uDAE0\u856D\uCEC0\u856E\u856F\uD8B4\uC3AE"+ + "\uD3A1\uCEA3\u8570\uBCB4\uC8B4\uC2D1\u8571\uBEED"+ + "\uD0B6\u8572\uDAE1\u8573\u8574\u8575\u8576\uC7E4"+ + "\u8577\u8578\uB3A7\u8579\uB6F2\uCCFC\uC0FA\u857A"+ + "\u857B\uC0F7\u857C\uD1B9\uD1E1\uD8C7\u857D\u857E"+ + "\u8580\u8581\u8582\u8583\u8584\uB2DE\u8585\u8586"+ + "\uC0E5\u8587\uBAF1\u8588\u8589\uD8C8\u858A\uD4AD"+ + "\u858B\u858C\uCFE1\uD8C9\u858D\uD8CA\uCFC3\u858E"+ + "\uB3F8\uBEC7\u858F\u8590\u8591\u8592\uD8CB\u8593"+ + "\u8594\u8595\u8596\u8597\u8598\u8599\uDBCC\u859A"+ + "\u859B\u859C\u859D\uC8A5\u859E\u859F\u85A0\uCFD8"+ + "\u85A1\uC8FE\uB2CE\u85A2\u85A3\u85A4\u85A5\u85A6"+ + "\uD3D6\uB2E6\uBCB0\uD3D1\uCBAB\uB7B4\u85A7\u85A8"+ + "\u85A9\uB7A2\u85AA\u85AB\uCAE5\u85AC\uC8A1\uCADC"+ + "\uB1E4\uD0F0\u85AD\uC5D1\u85AE\u85AF\u85B0\uDBC5"+ + "\uB5FE\u85B1\u85B2\uBFDA\uB9C5\uBEE4\uC1ED\u85B3"+ + "\uDFB6\uDFB5\uD6BB\uBDD0\uD5D9\uB0C8\uB6A3\uBFC9"+ + "\uCCA8\uDFB3\uCAB7\uD3D2\u85B4\uD8CF\uD2B6\uBAC5"+ + "\uCBBE\uCCBE\u85B5\uDFB7\uB5F0\uDFB4\u85B6\u85B7"+ + "\u85B8\uD3F5\u85B9\uB3D4\uB8F7\u85BA\uDFBA\u85BB"+ + "\uBACF\uBCAA\uB5F5\u85BC\uCDAC\uC3FB\uBAF3\uC0F4"+ + "\uCDC2\uCFF2\uDFB8\uCFC5\u85BD\uC2C0\uDFB9\uC2F0"+ + "\u85BE\u85BF\u85C0\uBEFD\u85C1\uC1DF\uCDCC\uD2F7"+ + "\uB7CD\uDFC1\u85C2\uDFC4\u85C3\u85C4\uB7F1\uB0C9"+ + "\uB6D6\uB7D4\u85C5\uBAAC\uCCFD\uBFD4\uCBB1\uC6F4"+ + "\u85C6\uD6A8\uDFC5\u85C7\uCEE2\uB3B3\u85C8\u85C9"+ + "\uCEFC\uB4B5\u85CA\uCEC7\uBAF0\u85CB\uCEE1\u85CC"+ + "\uD1BD\u85CD\u85CE\uDFC0\u85CF\u85D0\uB4F4\u85D1"+ + "\uB3CA\u85D2\uB8E6\uDFBB\u85D3\u85D4\u85D5\u85D6"+ + "\uC4C5\u85D7\uDFBC\uDFBD\uDFBE\uC5BB\uDFBF\uDFC2"+ + "\uD4B1\uDFC3\u85D8\uC7BA\uCED8\u85D9\u85DA\u85DB"+ + "\u85DC\u85DD\uC4D8\u85DE\uDFCA\u85DF\uDFCF\u85E0"+ + "\uD6DC\u85E1\u85E2\u85E3\u85E4\u85E5\u85E6\u85E7"+ + "\u85E8\uDFC9\uDFDA\uCEB6\u85E9\uBAC7\uDFCE\uDFC8"+ + "\uC5DE\u85EA\u85EB\uC9EB\uBAF4\uC3FC\u85EC\u85ED"+ + "\uBED7\u85EE\uDFC6\u85EF\uDFCD\u85F0\uC5D8\u85F1"+ + "\u85F2\u85F3\u85F4\uD5A6\uBACD\u85F5\uBECC\uD3BD"+ + "\uB8C0\u85F6\uD6E4\u85F7\uDFC7\uB9BE\uBFA7\u85F8"+ + "\u85F9\uC1FC\uDFCB\uDFCC\u85FA\uDFD0\u85FB\u85FC"+ + "\u85FD\u85FE\u8640\uDFDB\uDFE5\u8641\uDFD7\uDFD6"+ + "\uD7C9\uDFE3\uDFE4\uE5EB\uD2A7\uDFD2\u8642\uBFA9"+ + "\u8643\uD4DB\u8644\uBFC8\uDFD4\u8645\u8646\u8647"+ + "\uCFCC\u8648\u8649\uDFDD\u864A\uD1CA\u864B\uDFDE"+ + "\uB0A7\uC6B7\uDFD3\u864C\uBAE5\u864D\uB6DF\uCDDB"+ + "\uB9FE\uD4D5\u864E\u864F\uDFDF\uCFEC\uB0A5\uDFE7"+ + "\uDFD1\uD1C6\uDFD5\uDFD8\uDFD9\uDFDC\u8650\uBBA9"+ + "\u8651\uDFE0\uDFE1\u8652\uDFE2\uDFE6\uDFE8\uD3B4"+ + "\u8653\u8654\u8655\u8656\u8657\uB8E7\uC5B6\uDFEA"+ + "\uC9DA\uC1A8\uC4C4\u8658\u8659\uBFDE\uCFF8\u865A"+ + "\u865B\u865C\uD5DC\uDFEE\u865D\u865E\u865F\u8660"+ + "\u8661\u8662\uB2B8\u8663\uBADF\uDFEC\u8664\uDBC1"+ + "\u8665\uD1E4\u8666\u8667\u8668\u8669\uCBF4\uB4BD"+ + "\u866A\uB0A6\u866B\u866C\u866D\u866E\u866F\uDFF1"+ + "\uCCC6\uDFF2\u8670\u8671\uDFED\u8672\u8673\u8674"+ + "\u8675\u8676\u8677\uDFE9\u8678\u8679\u867A\u867B"+ + "\uDFEB\u867C\uDFEF\uDFF0\uBBBD\u867D\u867E\uDFF3"+ + "\u8680\u8681\uDFF4\u8682\uBBA3\u8683\uCADB\uCEA8"+ + "\uE0A7\uB3AA\u8684\uE0A6\u8685\u8686\u8687\uE0A1"+ + "\u8688\u8689\u868A\u868B\uDFFE\u868C\uCDD9\uDFFC"+ + "\u868D\uDFFA\u868E\uBFD0\uD7C4\u868F\uC9CC\u8690"+ + "\u8691\uDFF8\uB0A1\u8692\u8693\u8694\u8695\u8696"+ + "\uDFFD\u8697\u8698\u8699\u869A\uDFFB\uE0A2\u869B"+ + "\u869C\u869D\u869E\u869F\uE0A8\u86A0\u86A1\u86A2"+ + "\u86A3\uB7C8\u86A4\u86A5\uC6A1\uC9B6\uC0B2\uDFF5"+ + "\u86A6\u86A7\uC5BE\u86A8\uD8C4\uDFF9\uC4F6\u86A9"+ + "\u86AA\u86AB\u86AC\u86AD\u86AE\uE0A3\uE0A4\uE0A5"+ + "\uD0A5\u86AF\u86B0\uE0B4\uCCE4\u86B1\uE0B1\u86B2"+ + "\uBFA6\uE0AF\uCEB9\uE0AB\uC9C6\u86B3\u86B4\uC0AE"+ + "\uE0AE\uBAED\uBAB0\uE0A9\u86B5\u86B6\u86B7\uDFF6"+ + "\u86B8\uE0B3\u86B9\u86BA\uE0B8\u86BB\u86BC\u86BD"+ + "\uB4AD\uE0B9\u86BE\u86BF\uCFB2\uBAC8\u86C0\uE0B0"+ + "\u86C1\u86C2\u86C3\u86C4\u86C5\u86C6\u86C7\uD0FA"+ + "\u86C8\u86C9\u86CA\u86CB\u86CC\u86CD\u86CE\u86CF"+ + "\u86D0\uE0AC\u86D1\uD4FB\u86D2\uDFF7\u86D3\uC5E7"+ + "\u86D4\uE0AD\u86D5\uD3F7\u86D6\uE0B6\uE0B7\u86D7"+ + "\u86D8\u86D9\u86DA\u86DB\uE0C4\uD0E1\u86DC\u86DD"+ + "\u86DE\uE0BC\u86DF\u86E0\uE0C9\uE0CA\u86E1\u86E2"+ + "\u86E3\uE0BE\uE0AA\uC9A4\uE0C1\u86E4\uE0B2\u86E5"+ + "\u86E6\u86E7\u86E8\u86E9\uCAC8\uE0C3\u86EA\uE0B5"+ + "\u86EB\uCECB\u86EC\uCBC3\uE0CD\uE0C6\uE0C2\u86ED"+ + "\uE0CB\u86EE\uE0BA\uE0BF\uE0C0\u86EF\u86F0\uE0C5"+ + "\u86F1\u86F2\uE0C7\uE0C8\u86F3\uE0CC\u86F4\uE0BB"+ + "\u86F5\u86F6\u86F7\u86F8\u86F9\uCBD4\uE0D5\u86FA"+ + "\uE0D6\uE0D2\u86FB\u86FC\u86FD\u86FE\u8740\u8741"+ + "\uE0D0\uBCCE\u8742\u8743\uE0D1\u8744\uB8C2\uD8C5"+ + "\u8745\u8746\u8747\u8748\u8749\u874A\u874B\u874C"+ + "\uD0EA\u874D\u874E\uC2EF\u874F\u8750\uE0CF\uE0BD"+ + "\u8751\u8752\u8753\uE0D4\uE0D3\u8754\u8755\uE0D7"+ + "\u8756\u8757\u8758\u8759\uE0DC\uE0D8\u875A\u875B"+ + "\u875C\uD6F6\uB3B0\u875D\uD7EC\u875E\uCBBB\u875F"+ + "\u8760\uE0DA\u8761\uCEFB\u8762\u8763\u8764\uBAD9"+ + "\u8765\u8766\u8767\u8768\u8769\u876A\u876B\u876C"+ + "\u876D\u876E\u876F\u8770\uE0E1\uE0DD\uD2AD\u8771"+ + "\u8772\u8773\u8774\u8775\uE0E2\u8776\u8777\uE0DB"+ + "\uE0D9\uE0DF\u8778\u8779\uE0E0\u877A\u877B\u877C"+ + "\u877D\u877E\uE0DE\u8780\uE0E4\u8781\u8782\u8783"+ + "\uC6F7\uD8AC\uD4EB\uE0E6\uCAC9\u8784\u8785\u8786"+ + "\u8787\uE0E5\u8788\u8789\u878A\u878B\uB8C1\u878C"+ + "\u878D\u878E\u878F\uE0E7\uE0E8\u8790\u8791\u8792"+ + "\u8793\u8794\u8795\u8796\u8797\uE0E9\uE0E3\u8798"+ + "\u8799\u879A\u879B\u879C\u879D\u879E\uBABF\uCCE7"+ + "\u879F\u87A0\u87A1\uE0EA\u87A2\u87A3\u87A4\u87A5"+ + "\u87A6\u87A7\u87A8\u87A9\u87AA\u87AB\u87AC\u87AD"+ + "\u87AE\u87AF\u87B0\uCFF9\u87B1\u87B2\u87B3\u87B4"+ + "\u87B5\u87B6\u87B7\u87B8\u87B9\u87BA\u87BB\uE0EB"+ + "\u87BC\u87BD\u87BE\u87BF\u87C0\u87C1\u87C2\uC8C2"+ + "\u87C3\u87C4\u87C5\u87C6\uBDC0\u87C7\u87C8\u87C9"+ + "\u87CA\u87CB\u87CC\u87CD\u87CE\u87CF\u87D0\u87D1"+ + "\u87D2\u87D3\uC4D2\u87D4\u87D5\u87D6\u87D7\u87D8"+ + "\u87D9\u87DA\u87DB\u87DC\uE0EC\u87DD\u87DE\uE0ED"+ + "\u87DF\u87E0\uC7F4\uCBC4\u87E1\uE0EE\uBBD8\uD8B6"+ + "\uD2F2\uE0EF\uCDC5\u87E2\uB6DA\u87E3\u87E4\u87E5"+ + "\u87E6\u87E7\u87E8\uE0F1\u87E9\uD4B0\u87EA\u87EB"+ + "\uC0A7\uB4D1\u87EC\u87ED\uCEA7\uE0F0\u87EE\u87EF"+ + "\u87F0\uE0F2\uB9CC\u87F1\u87F2\uB9FA\uCDBC\uE0F3"+ + "\u87F3\u87F4\u87F5\uC6D4\uE0F4\u87F6\uD4B2\u87F7"+ + "\uC8A6\uE0F6\uE0F5\u87F8\u87F9\u87FA\u87FB\u87FC"+ + "\u87FD\u87FE\u8840\u8841\u8842\u8843\u8844\u8845"+ + "\u8846\u8847\u8848\u8849\uE0F7\u884A\u884B\uCDC1"+ + "\u884C\u884D\u884E\uCAA5\u884F\u8850\u8851\u8852"+ + "\uD4DA\uDBD7\uDBD9\u8853\uDBD8\uB9E7\uDBDC\uDBDD"+ + "\uB5D8\u8854\u8855\uDBDA\u8856\u8857\u8858\u8859"+ + "\u885A\uDBDB\uB3A1\uDBDF\u885B\u885C\uBBF8\u885D"+ + "\uD6B7\u885E\uDBE0\u885F\u8860\u8861\u8862\uBEF9"+ + "\u8863\u8864\uB7BB\u8865\uDBD0\uCCAE\uBFB2\uBBB5"+ + "\uD7F8\uBFD3\u8866\u8867\u8868\u8869\u886A\uBFE9"+ + "\u886B\u886C\uBCE1\uCCB3\uDBDE\uB0D3\uCEEB\uB7D8"+ + "\uD7B9\uC6C2\u886D\u886E\uC0A4\u886F\uCCB9\u8870"+ + "\uDBE7\uDBE1\uC6BA\uDBE3\u8871\uDBE8\u8872\uC5F7"+ + "\u8873\u8874\u8875\uDBEA\u8876\u8877\uDBE9\uBFC0"+ + "\u8878\u8879\u887A\uDBE6\uDBE5\u887B\u887C\u887D"+ + "\u887E\u8880\uB4B9\uC0AC\uC2A2\uDBE2\uDBE4\u8881"+ + "\u8882\u8883\u8884\uD0CD\uDBED\u8885\u8886\u8887"+ + "\u8888\u8889\uC0DD\uDBF2\u888A\u888B\u888C\u888D"+ + "\u888E\u888F\u8890\uB6E2\u8891\u8892\u8893\u8894"+ + "\uDBF3\uDBD2\uB9B8\uD4AB\uDBEC\u8895\uBFD1\uDBF0"+ + "\u8896\uDBD1\u8897\uB5E6\u8898\uDBEB\uBFE5\u8899"+ + "\u889A\u889B\uDBEE\u889C\uDBF1\u889D\u889E\u889F"+ + "\uDBF9\u88A0\u88A1\u88A2\u88A3\u88A4\u88A5\u88A6"+ + "\u88A7\u88A8\uB9A1\uB0A3\u88A9\u88AA\u88AB\u88AC"+ + "\u88AD\u88AE\u88AF\uC2F1\u88B0\u88B1\uB3C7\uDBEF"+ + "\u88B2\u88B3\uDBF8\u88B4\uC6D2\uDBF4\u88B5\u88B6"+ + "\uDBF5\uDBF7\uDBF6\u88B7\u88B8\uDBFE\u88B9\uD3F2"+ + "\uB2BA\u88BA\u88BB\u88BC\uDBFD\u88BD\u88BE\u88BF"+ + "\u88C0\u88C1\u88C2\u88C3\u88C4\uDCA4\u88C5\uDBFB"+ + "\u88C6\u88C7\u88C8\u88C9\uDBFA\u88CA\u88CB\u88CC"+ + "\uDBFC\uC5E0\uBBF9\u88CD\u88CE\uDCA3\u88CF\u88D0"+ + "\uDCA5\u88D1\uCCC3\u88D2\u88D3\u88D4\uB6D1\uDDC0"+ + "\u88D5\u88D6\u88D7\uDCA1\u88D8\uDCA2\u88D9\u88DA"+ + "\u88DB\uC7B5\u88DC\u88DD\u88DE\uB6E9\u88DF\u88E0"+ + "\u88E1\uDCA7\u88E2\u88E3\u88E4\u88E5\uDCA6\u88E6"+ + "\uDCA9\uB1A4\u88E7\u88E8\uB5CC\u88E9\u88EA\u88EB"+ + "\u88EC\u88ED\uBFB0\u88EE\u88EF\u88F0\u88F1\u88F2"+ + "\uD1DF\u88F3\u88F4\u88F5\u88F6\uB6C2\u88F7\u88F8"+ + "\u88F9\u88FA\u88FB\u88FC\u88FD\u88FE\u8940\u8941"+ + "\u8942\u8943\u8944\u8945\uDCA8\u8946\u8947\u8948"+ + "\u8949\u894A\u894B\u894C\uCBFA\uEBF3\u894D\u894E"+ + "\u894F\uCBDC\u8950\u8951\uCBFE\u8952\u8953\u8954"+ + "\uCCC1\u8955\u8956\u8957\u8958\u8959\uC8FB\u895A"+ + "\u895B\u895C\u895D\u895E\u895F\uDCAA\u8960\u8961"+ + "\u8962\u8963\u8964\uCCEE\uDCAB\u8965\u8966\u8967"+ + "\u8968\u8969\u896A\u896B\u896C\u896D\u896E\u896F"+ + "\u8970\u8971\u8972\u8973\u8974\u8975\uDBD3\u8976"+ + "\uDCAF\uDCAC\u8977\uBEB3\u8978\uCAFB\u8979\u897A"+ + "\u897B\uDCAD\u897C\u897D\u897E\u8980\u8981\u8982"+ + "\u8983\u8984\uC9CA\uC4B9\u8985\u8986\u8987\u8988"+ + "\u8989\uC7BD\uDCAE\u898A\u898B\u898C\uD4F6\uD0E6"+ + "\u898D\u898E\u898F\u8990\u8991\u8992\u8993\u8994"+ + "\uC4AB\uB6D5\u8995\u8996\u8997\u8998\u8999\u899A"+ + "\u899B\u899C\u899D\u899E\u899F\u89A0\u89A1\u89A2"+ + "\u89A3\u89A4\u89A5\u89A6\uDBD4\u89A7\u89A8\u89A9"+ + "\u89AA\uB1DA\u89AB\u89AC\u89AD\uDBD5\u89AE\u89AF"+ + "\u89B0\u89B1\u89B2\u89B3\u89B4\u89B5\u89B6\u89B7"+ + "\u89B8\uDBD6\u89B9\u89BA\u89BB\uBABE\u89BC\u89BD"+ + "\u89BE\u89BF\u89C0\u89C1\u89C2\u89C3\u89C4\u89C5"+ + "\u89C6\u89C7\u89C8\u89C9\uC8C0\u89CA\u89CB\u89CC"+ + "\u89CD\u89CE\u89CF\uCABF\uC8C9\u89D0\uD7B3\u89D1"+ + "\uC9F9\u89D2\u89D3\uBFC7\u89D4\u89D5\uBAF8\u89D6"+ + "\u89D7\uD2BC\u89D8\u89D9\u89DA\u89DB\u89DC\u89DD"+ + "\u89DE\u89DF\uE2BA\u89E0\uB4A6\u89E1\u89E2\uB1B8"+ + "\u89E3\u89E4\u89E5\u89E6\u89E7\uB8B4\u89E8\uCFC4"+ + "\u89E9\u89EA\u89EB\u89EC\uD9E7\uCFA6\uCDE2\u89ED"+ + "\u89EE\uD9ED\uB6E0\u89EF\uD2B9\u89F0\u89F1\uB9BB"+ + "\u89F2\u89F3\u89F4\u89F5\uE2B9\uE2B7\u89F6\uB4F3"+ + "\u89F7\uCCEC\uCCAB\uB7F2\u89F8\uD8B2\uD1EB\uBABB"+ + "\u89F9\uCAA7\u89FA\u89FB\uCDB7\u89FC\u89FD\uD2C4"+ + "\uBFE4\uBCD0\uB6E1\u89FE\uDEC5\u8A40\u8A41\u8A42"+ + "\u8A43\uDEC6\uDBBC\u8A44\uD1D9\u8A45\u8A46\uC6E6"+ + "\uC4CE\uB7EE\u8A47\uB7DC\u8A48\u8A49\uBFFC\uD7E0"+ + "\u8A4A\uC6F5\u8A4B\u8A4C\uB1BC\uDEC8\uBDB1\uCCD7"+ + "\uDECA\u8A4D\uDEC9\u8A4E\u8A4F\u8A50\u8A51\u8A52"+ + "\uB5EC\u8A53\uC9DD\u8A54\u8A55\uB0C2\u8A56\u8A57"+ + "\u8A58\u8A59\u8A5A\u8A5B\u8A5C\u8A5D\u8A5E\u8A5F"+ + "\u8A60\u8A61\u8A62\uC5AE\uC5AB\u8A63\uC4CC\u8A64"+ + "\uBCE9\uCBFD\u8A65\u8A66\u8A67\uBAC3\u8A68\u8A69"+ + "\u8A6A\uE5F9\uC8E7\uE5FA\uCDFD\u8A6B\uD7B1\uB8BE"+ + "\uC2E8\u8A6C\uC8D1\u8A6D\u8A6E\uE5FB\u8A6F\u8A70"+ + "\u8A71\u8A72\uB6CA\uBCCB\u8A73\u8A74\uD1FD\uE6A1"+ + "\u8A75\uC3EE\u8A76\u8A77\u8A78\u8A79\uE6A4\u8A7A"+ + "\u8A7B\u8A7C\u8A7D\uE5FE\uE6A5\uCDD7\u8A7E\u8A80"+ + "\uB7C1\uE5FC\uE5FD\uE6A3\u8A81\u8A82\uC4DD\uE6A8"+ + "\u8A83\u8A84\uE6A7\u8A85\u8A86\u8A87\u8A88\u8A89"+ + "\u8A8A\uC3C3\u8A8B\uC6DE\u8A8C\u8A8D\uE6AA\u8A8E"+ + "\u8A8F\u8A90\u8A91\u8A92\u8A93\u8A94\uC4B7\u8A95"+ + "\u8A96\u8A97\uE6A2\uCABC\u8A98\u8A99\u8A9A\u8A9B"+ + "\uBDE3\uB9C3\uE6A6\uD0D5\uCEAF\u8A9C\u8A9D\uE6A9"+ + "\uE6B0\u8A9E\uD2A6\u8A9F\uBDAA\uE6AD\u8AA0\u8AA1"+ + "\u8AA2\u8AA3\u8AA4\uE6AF\u8AA5\uC0D1\u8AA6\u8AA7"+ + "\uD2CC\u8AA8\u8AA9\u8AAA\uBCA7\u8AAB\u8AAC\u8AAD"+ + "\u8AAE\u8AAF\u8AB0\u8AB1\u8AB2\u8AB3\u8AB4\u8AB5"+ + "\u8AB6\uE6B1\u8AB7\uD2F6\u8AB8\u8AB9\u8ABA\uD7CB"+ + "\u8ABB\uCDFE\u8ABC\uCDDE\uC2A6\uE6AB\uE6AC\uBDBF"+ + "\uE6AE\uE6B3\u8ABD\u8ABE\uE6B2\u8ABF\u8AC0\u8AC1"+ + "\u8AC2\uE6B6\u8AC3\uE6B8\u8AC4\u8AC5\u8AC6\u8AC7"+ + "\uC4EF\u8AC8\u8AC9\u8ACA\uC4C8\u8ACB\u8ACC\uBEEA"+ + "\uC9EF\u8ACD\u8ACE\uE6B7\u8ACF\uB6F0\u8AD0\u8AD1"+ + "\u8AD2\uC3E4\u8AD3\u8AD4\u8AD5\u8AD6\u8AD7\u8AD8"+ + "\u8AD9\uD3E9\uE6B4\u8ADA\uE6B5\u8ADB\uC8A2\u8ADC"+ + "\u8ADD\u8ADE\u8ADF\u8AE0\uE6BD\u8AE1\u8AE2\u8AE3"+ + "\uE6B9\u8AE4\u8AE5\u8AE6\u8AE7\u8AE8\uC6C5\u8AE9"+ + "\u8AEA\uCDF1\uE6BB\u8AEB\u8AEC\u8AED\u8AEE\u8AEF"+ + "\u8AF0\u8AF1\u8AF2\u8AF3\u8AF4\uE6BC\u8AF5\u8AF6"+ + "\u8AF7\u8AF8\uBBE9\u8AF9\u8AFA\u8AFB\u8AFC\u8AFD"+ + "\u8AFE\u8B40\uE6BE\u8B41\u8B42\u8B43\u8B44\uE6BA"+ + "\u8B45\u8B46\uC0B7\u8B47\u8B48\u8B49\u8B4A\u8B4B"+ + "\u8B4C\u8B4D\u8B4E\u8B4F\uD3A4\uE6BF\uC9F4\uE6C3"+ + "\u8B50\u8B51\uE6C4\u8B52\u8B53\u8B54\u8B55\uD0F6"+ + "\u8B56\u8B57\u8B58\u8B59\u8B5A\u8B5B\u8B5C\u8B5D"+ + "\u8B5E\u8B5F\u8B60\u8B61\u8B62\u8B63\u8B64\u8B65"+ + "\u8B66\u8B67\uC3BD\u8B68\u8B69\u8B6A\u8B6B\u8B6C"+ + "\u8B6D\u8B6E\uC3C4\uE6C2\u8B6F\u8B70\u8B71\u8B72"+ + "\u8B73\u8B74\u8B75\u8B76\u8B77\u8B78\u8B79\u8B7A"+ + "\u8B7B\u8B7C\uE6C1\u8B7D\u8B7E\u8B80\u8B81\u8B82"+ + "\u8B83\u8B84\uE6C7\uCFB1\u8B85\uEBF4\u8B86\u8B87"+ + "\uE6CA\u8B88\u8B89\u8B8A\u8B8B\u8B8C\uE6C5\u8B8D"+ + "\u8B8E\uBCDE\uC9A9\u8B8F\u8B90\u8B91\u8B92\u8B93"+ + "\u8B94\uBCB5\u8B95\u8B96\uCFD3\u8B97\u8B98\u8B99"+ + "\u8B9A\u8B9B\uE6C8\u8B9C\uE6C9\u8B9D\uE6CE\u8B9E"+ + "\uE6D0\u8B9F\u8BA0\u8BA1\uE6D1\u8BA2\u8BA3\u8BA4"+ + "\uE6CB\uB5D5\u8BA5\uE6CC\u8BA6\u8BA7\uE6CF\u8BA8"+ + "\u8BA9\uC4DB\u8BAA\uE6C6\u8BAB\u8BAC\u8BAD\u8BAE"+ + "\u8BAF\uE6CD\u8BB0\u8BB1\u8BB2\u8BB3\u8BB4\u8BB5"+ + "\u8BB6\u8BB7\u8BB8\u8BB9\u8BBA\u8BBB\u8BBC\u8BBD"+ + "\u8BBE\u8BBF\u8BC0\u8BC1\u8BC2\u8BC3\u8BC4\u8BC5"+ + "\u8BC6\uE6D2\u8BC7\u8BC8\u8BC9\u8BCA\u8BCB\u8BCC"+ + "\u8BCD\u8BCE\u8BCF\u8BD0\u8BD1\u8BD2\uE6D4\uE6D3"+ + "\u8BD3\u8BD4\u8BD5\u8BD6\u8BD7\u8BD8\u8BD9\u8BDA"+ + "\u8BDB\u8BDC\u8BDD\u8BDE\u8BDF\u8BE0\u8BE1\u8BE2"+ + "\u8BE3\u8BE4\u8BE5\u8BE6\u8BE7\u8BE8\u8BE9\u8BEA"+ + "\u8BEB\u8BEC\uE6D5\u8BED\uD9F8\u8BEE\u8BEF\uE6D6"+ + "\u8BF0\u8BF1\u8BF2\u8BF3\u8BF4\u8BF5\u8BF6\u8BF7"+ + "\uE6D7\u8BF8\u8BF9\u8BFA\u8BFB\u8BFC\u8BFD\u8BFE"+ + "\u8C40\u8C41\u8C42\u8C43\u8C44\u8C45\u8C46\u8C47"+ + "\uD7D3\uE6DD\u8C48\uE6DE\uBFD7\uD4D0\u8C49\uD7D6"+ + "\uB4E6\uCBEF\uE6DA\uD8C3\uD7CE\uD0A2\u8C4A\uC3CF"+ + "\u8C4B\u8C4C\uE6DF\uBCBE\uB9C2\uE6DB\uD1A7\u8C4D"+ + "\u8C4E\uBAA2\uC2CF\u8C4F\uD8AB\u8C50\u8C51\u8C52"+ + "\uCAEB\uE5EE\u8C53\uE6DC\u8C54\uB7F5\u8C55\u8C56"+ + "\u8C57\u8C58\uC8E6\u8C59\u8C5A\uC4F5\u8C5B\u8C5C"+ + "\uE5B2\uC4FE\u8C5D\uCBFC\uE5B3\uD5AC\u8C5E\uD3EE"+ + "\uCAD8\uB0B2\u8C5F\uCBCE\uCDEA\u8C60\u8C61\uBAEA"+ + "\u8C62\u8C63\u8C64\uE5B5\u8C65\uE5B4\u8C66\uD7DA"+ + "\uB9D9\uD6E6\uB6A8\uCDF0\uD2CB\uB1A6\uCAB5\u8C67"+ + "\uB3E8\uC9F3\uBFCD\uD0FB\uCAD2\uE5B6\uBBC2\u8C68"+ + "\u8C69\u8C6A\uCFDC\uB9AC\u8C6B\u8C6C\u8C6D\u8C6E"+ + "\uD4D7\u8C6F\u8C70\uBAA6\uD1E7\uCFFC\uBCD2\u8C71"+ + "\uE5B7\uC8DD\u8C72\u8C73\u8C74\uBFED\uB1F6\uCBDE"+ + "\u8C75\u8C76\uBCC5\u8C77\uBCC4\uD2FA\uC3DC\uBFDC"+ + "\u8C78\u8C79\u8C7A\u8C7B\uB8BB\u8C7C\u8C7D\u8C7E"+ + "\uC3C2\u8C80\uBAAE\uD4A2\u8C81\u8C82\u8C83\u8C84"+ + "\u8C85\u8C86\u8C87\u8C88\u8C89\uC7DE\uC4AF\uB2EC"+ + "\u8C8A\uB9D1\u8C8B\u8C8C\uE5BB\uC1C8\u8C8D\u8C8E"+ + "\uD5AF\u8C8F\u8C90\u8C91\u8C92\u8C93\uE5BC\u8C94"+ + "\uE5BE\u8C95\u8C96\u8C97\u8C98\u8C99\u8C9A\u8C9B"+ + "\uB4E7\uB6D4\uCBC2\uD1B0\uB5BC\u8C9C\u8C9D\uCAD9"+ + "\u8C9E\uB7E2\u8C9F\u8CA0\uC9E4\u8CA1\uBDAB\u8CA2"+ + "\u8CA3\uCEBE\uD7F0\u8CA4\u8CA5\u8CA6\u8CA7\uD0A1"+ + "\u8CA8\uC9D9\u8CA9\u8CAA\uB6FB\uE6D8\uBCE2\u8CAB"+ + "\uB3BE\u8CAC\uC9D0\u8CAD\uE6D9\uB3A2\u8CAE\u8CAF"+ + "\u8CB0\u8CB1\uDECC\u8CB2\uD3C8\uDECD\u8CB3\uD2A2"+ + "\u8CB4\u8CB5\u8CB6\u8CB7\uDECE\u8CB8\u8CB9\u8CBA"+ + "\u8CBB\uBECD\u8CBC\u8CBD\uDECF\u8CBE\u8CBF\u8CC0"+ + "\uCAAC\uD2FC\uB3DF\uE5EA\uC4E1\uBEA1\uCEB2\uC4F2"+ + "\uBED6\uC6A8\uB2E3\u8CC1\u8CC2\uBED3\u8CC3\u8CC4"+ + "\uC7FC\uCCEB\uBDEC\uCEDD\u8CC5\u8CC6\uCABA\uC6C1"+ + "\uE5EC\uD0BC\u8CC7\u8CC8\u8CC9\uD5B9\u8CCA\u8CCB"+ + "\u8CCC\uE5ED\u8CCD\u8CCE\u8CCF\u8CD0\uCAF4\u8CD1"+ + "\uCDC0\uC2C5\u8CD2\uE5EF\u8CD3\uC2C4\uE5F0\u8CD4"+ + "\u8CD5\u8CD6\u8CD7\u8CD8\u8CD9\u8CDA\uE5F8\uCDCD"+ + "\u8CDB\uC9BD\u8CDC\u8CDD\u8CDE\u8CDF\u8CE0\u8CE1"+ + "\u8CE2\uD2D9\uE1A8\u8CE3\u8CE4\u8CE5\u8CE6\uD3EC"+ + "\u8CE7\uCBEA\uC6F1\u8CE8\u8CE9\u8CEA\u8CEB\u8CEC"+ + "\uE1AC\u8CED\u8CEE\u8CEF\uE1A7\uE1A9\u8CF0\u8CF1"+ + "\uE1AA\uE1AF\u8CF2\u8CF3\uB2ED\u8CF4\uE1AB\uB8DA"+ + "\uE1AD\uE1AE\uE1B0\uB5BA\uE1B1\u8CF5\u8CF6\u8CF7"+ + "\u8CF8\u8CF9\uE1B3\uE1B8\u8CFA\u8CFB\u8CFC\u8CFD"+ + "\u8CFE\uD1D2\u8D40\uE1B6\uE1B5\uC1EB\u8D41\u8D42"+ + "\u8D43\uE1B7\u8D44\uD4C0\u8D45\uE1B2\u8D46\uE1BA"+ + "\uB0B6\u8D47\u8D48\u8D49\u8D4A\uE1B4\u8D4B\uBFF9"+ + "\u8D4C\uE1B9\u8D4D\u8D4E\uE1BB\u8D4F\u8D50\u8D51"+ + "\u8D52\u8D53\u8D54\uE1BE\u8D55\u8D56\u8D57\u8D58"+ + "\u8D59\u8D5A\uE1BC\u8D5B\u8D5C\u8D5D\u8D5E\u8D5F"+ + "\u8D60\uD6C5\u8D61\u8D62\u8D63\u8D64\u8D65\u8D66"+ + "\u8D67\uCFBF\u8D68\u8D69\uE1BD\uE1BF\uC2CD\u8D6A"+ + "\uB6EB\u8D6B\uD3F8\u8D6C\u8D6D\uC7CD\u8D6E\u8D6F"+ + "\uB7E5\u8D70\u8D71\u8D72\u8D73\u8D74\u8D75\u8D76"+ + "\u8D77\u8D78\u8D79\uBEFE\u8D7A\u8D7B\u8D7C\u8D7D"+ + "\u8D7E\u8D80\uE1C0\uE1C1\u8D81\u8D82\uE1C7\uB3E7"+ + "\u8D83\u8D84\u8D85\u8D86\u8D87\u8D88\uC6E9\u8D89"+ + "\u8D8A\u8D8B\u8D8C\u8D8D\uB4DE\u8D8E\uD1C2\u8D8F"+ + "\u8D90\u8D91\u8D92\uE1C8\u8D93\u8D94\uE1C6\u8D95"+ + "\u8D96\u8D97\u8D98\u8D99\uE1C5\u8D9A\uE1C3\uE1C2"+ + "\u8D9B\uB1C0\u8D9C\u8D9D\u8D9E\uD5B8\uE1C4\u8D9F"+ + "\u8DA0\u8DA1\u8DA2\u8DA3\uE1CB\u8DA4\u8DA5\u8DA6"+ + "\u8DA7\u8DA8\u8DA9\u8DAA\u8DAB\uE1CC\uE1CA\u8DAC"+ + "\u8DAD\u8DAE\u8DAF\u8DB0\u8DB1\u8DB2\u8DB3\uEFFA"+ + "\u8DB4\u8DB5\uE1D3\uE1D2\uC7B6\u8DB6\u8DB7\u8DB8"+ + "\u8DB9\u8DBA\u8DBB\u8DBC\u8DBD\u8DBE\u8DBF\u8DC0"+ + "\uE1C9\u8DC1\u8DC2\uE1CE\u8DC3\uE1D0\u8DC4\u8DC5"+ + "\u8DC6\u8DC7\u8DC8\u8DC9\u8DCA\u8DCB\u8DCC\u8DCD"+ + "\u8DCE\uE1D4\u8DCF\uE1D1\uE1CD\u8DD0\u8DD1\uE1CF"+ + "\u8DD2\u8DD3\u8DD4\u8DD5\uE1D5\u8DD6\u8DD7\u8DD8"+ + "\u8DD9\u8DDA\u8DDB\u8DDC\u8DDD\u8DDE\u8DDF\u8DE0"+ + "\u8DE1\u8DE2\uE1D6\u8DE3\u8DE4\u8DE5\u8DE6\u8DE7"+ + "\u8DE8\u8DE9\u8DEA\u8DEB\u8DEC\u8DED\u8DEE\u8DEF"+ + "\u8DF0\u8DF1\u8DF2\u8DF3\u8DF4\u8DF5\u8DF6\u8DF7"+ + "\u8DF8\uE1D7\u8DF9\u8DFA\u8DFB\uE1D8\u8DFC\u8DFD"+ + "\u8DFE\u8E40\u8E41\u8E42\u8E43\u8E44\u8E45\u8E46"+ + "\u8E47\u8E48\u8E49\u8E4A\u8E4B\u8E4C\u8E4D\u8E4E"+ + "\u8E4F\u8E50\u8E51\u8E52\u8E53\u8E54\u8E55\uE1DA"+ + "\u8E56\u8E57\u8E58\u8E59\u8E5A\u8E5B\u8E5C\u8E5D"+ + "\u8E5E\u8E5F\u8E60\u8E61\u8E62\uE1DB\u8E63\u8E64"+ + "\u8E65\u8E66\u8E67\u8E68\u8E69\uCEA1\u8E6A\u8E6B"+ + "\u8E6C\u8E6D\u8E6E\u8E6F\u8E70\u8E71\u8E72\u8E73"+ + "\u8E74\u8E75\u8E76\uE7DD\u8E77\uB4A8\uD6DD\u8E78"+ + "\u8E79\uD1B2\uB3B2\u8E7A\u8E7B\uB9A4\uD7F3\uC7C9"+ + "\uBEDE\uB9AE\u8E7C\uCED7\u8E7D\u8E7E\uB2EE\uDBCF"+ + "\u8E80\uBCBA\uD2D1\uCBC8\uB0CD\u8E81\u8E82\uCFEF"+ + "\u8E83\u8E84\u8E85\u8E86\u8E87\uD9E3\uBDED\u8E88"+ + "\u8E89\uB1D2\uCAD0\uB2BC\u8E8A\uCBA7\uB7AB\u8E8B"+ + "\uCAA6\u8E8C\u8E8D\u8E8E\uCFA3\u8E8F\u8E90\uE0F8"+ + "\uD5CA\uE0FB\u8E91\u8E92\uE0FA\uC5C1\uCCFB\u8E93"+ + "\uC1B1\uE0F9\uD6E3\uB2AF\uD6C4\uB5DB\u8E94\u8E95"+ + "\u8E96\u8E97\u8E98\u8E99\u8E9A\u8E9B\uB4F8\uD6A1"+ + "\u8E9C\u8E9D\u8E9E\u8E9F\u8EA0\uCFAF\uB0EF\u8EA1"+ + "\u8EA2\uE0FC\u8EA3\u8EA4\u8EA5\u8EA6\u8EA7\uE1A1"+ + "\uB3A3\u8EA8\u8EA9\uE0FD\uE0FE\uC3B1\u8EAA\u8EAB"+ + "\u8EAC\u8EAD\uC3DD\u8EAE\uE1A2\uB7F9\u8EAF\u8EB0"+ + "\u8EB1\u8EB2\u8EB3\u8EB4\uBBCF\u8EB5\u8EB6\u8EB7"+ + "\u8EB8\u8EB9\u8EBA\u8EBB\uE1A3\uC4BB\u8EBC\u8EBD"+ + "\u8EBE\u8EBF\u8EC0\uE1A4\u8EC1\u8EC2\uE1A5\u8EC3"+ + "\u8EC4\uE1A6\uB4B1\u8EC5\u8EC6\u8EC7\u8EC8\u8EC9"+ + "\u8ECA\u8ECB\u8ECC\u8ECD\u8ECE\u8ECF\u8ED0\u8ED1"+ + "\u8ED2\u8ED3\uB8C9\uC6BD\uC4EA\u8ED4\uB2A2\u8ED5"+ + "\uD0D2\u8ED6\uE7DB\uBBC3\uD3D7\uD3C4\u8ED7\uB9E3"+ + "\uE2CF\u8ED8\u8ED9\u8EDA\uD7AF\u8EDB\uC7EC\uB1D3"+ + "\u8EDC\u8EDD\uB4B2\uE2D1\u8EDE\u8EDF\u8EE0\uD0F2"+ + "\uC2AE\uE2D0\u8EE1\uBFE2\uD3A6\uB5D7\uE2D2\uB5EA"+ + "\u8EE2\uC3ED\uB8FD\u8EE3\uB8AE\u8EE4\uC5D3\uB7CF"+ + "\uE2D4\u8EE5\u8EE6\u8EE7\u8EE8\uE2D3\uB6C8\uD7F9"+ + "\u8EE9\u8EEA\u8EEB\u8EEC\u8EED\uCDA5\u8EEE\u8EEF"+ + "\u8EF0\u8EF1\u8EF2\uE2D8\u8EF3\uE2D6\uCAFC\uBFB5"+ + "\uD3B9\uE2D5\u8EF4\u8EF5\u8EF6\u8EF7\uE2D7\u8EF8"+ + "\u8EF9\u8EFA\u8EFB\u8EFC\u8EFD\u8EFE\u8F40\u8F41"+ + "\u8F42\uC1AE\uC0C8\u8F43\u8F44\u8F45\u8F46\u8F47"+ + "\u8F48\uE2DB\uE2DA\uC0AA\u8F49\u8F4A\uC1CE\u8F4B"+ + "\u8F4C\u8F4D\u8F4E\uE2DC\u8F4F\u8F50\u8F51\u8F52"+ + "\u8F53\u8F54\u8F55\u8F56\u8F57\u8F58\u8F59\u8F5A"+ + "\uE2DD\u8F5B\uE2DE\u8F5C\u8F5D\u8F5E\u8F5F\u8F60"+ + "\u8F61\u8F62\u8F63\u8F64\uDBC8\u8F65\uD1D3\uCDA2"+ + "\u8F66\u8F67\uBDA8\u8F68\u8F69\u8F6A\uDEC3\uD8A5"+ + "\uBFAA\uDBCD\uD2EC\uC6FA\uC5AA\u8F6B\u8F6C\u8F6D"+ + "\uDEC4\u8F6E\uB1D7\uDFAE\u8F6F\u8F70\u8F71\uCABD"+ + "\u8F72\uDFB1\u8F73\uB9AD\u8F74\uD2FD\u8F75\uB8A5"+ + "\uBAEB\u8F76\u8F77\uB3DA\u8F78\u8F79\u8F7A\uB5DC"+ + "\uD5C5\u8F7B\u8F7C\u8F7D\u8F7E\uC3D6\uCFD2\uBBA1"+ + "\u8F80\uE5F3\uE5F2\u8F81\u8F82\uE5F4\u8F83\uCDE4"+ + "\u8F84\uC8F5\u8F85\u8F86\u8F87\u8F88\u8F89\u8F8A"+ + "\u8F8B\uB5AF\uC7BF\u8F8C\uE5F6\u8F8D\u8F8E\u8F8F"+ + "\uECB0\u8F90\u8F91\u8F92\u8F93\u8F94\u8F95\u8F96"+ + "\u8F97\u8F98\u8F99\u8F9A\u8F9B\u8F9C\u8F9D\u8F9E"+ + "\uE5E6\u8F9F\uB9E9\uB5B1\u8FA0\uC2BC\uE5E8\uE5E7"+ + "\uE5E9\u8FA1\u8FA2\u8FA3\u8FA4\uD2CD\u8FA5\u8FA6"+ + "\u8FA7\uE1EA\uD0CE\u8FA8\uCDAE\u8FA9\uD1E5\u8FAA"+ + "\u8FAB\uB2CA\uB1EB\u8FAC\uB1F2\uC5ED\u8FAD\u8FAE"+ + "\uD5C3\uD3B0\u8FAF\uE1DC\u8FB0\u8FB1\u8FB2\uE1DD"+ + "\u8FB3\uD2DB\u8FB4\uB3B9\uB1CB\u8FB5\u8FB6\u8FB7"+ + "\uCDF9\uD5F7\uE1DE\u8FB8\uBEB6\uB4FD\u8FB9\uE1DF"+ + "\uBADC\uE1E0\uBBB2\uC2C9\uE1E1\u8FBA\u8FBB\u8FBC"+ + "\uD0EC\u8FBD\uCDBD\u8FBE\u8FBF\uE1E2\u8FC0\uB5C3"+ + "\uC5C7\uE1E3\u8FC1\u8FC2\uE1E4\u8FC3\u8FC4\u8FC5"+ + "\u8FC6\uD3F9\u8FC7\u8FC8\u8FC9\u8FCA\u8FCB\u8FCC"+ + "\uE1E5\u8FCD\uD1AD\u8FCE\u8FCF\uE1E6\uCEA2\u8FD0"+ + "\u8FD1\u8FD2\u8FD3\u8FD4\u8FD5\uE1E7\u8FD6\uB5C2"+ + "\u8FD7\u8FD8\u8FD9\u8FDA\uE1E8\uBBD5\u8FDB\u8FDC"+ + "\u8FDD\u8FDE\u8FDF\uD0C4\uE2E0\uB1D8\uD2E4\u8FE0"+ + "\u8FE1\uE2E1\u8FE2\u8FE3\uBCC9\uC8CC\u8FE4\uE2E3"+ + "\uECFE\uECFD\uDFAF\u8FE5\u8FE6\u8FE7\uE2E2\uD6BE"+ + "\uCDFC\uC3A6\u8FE8\u8FE9\u8FEA\uE3C3\u8FEB\u8FEC"+ + "\uD6D2\uE2E7\u8FED\u8FEE\uE2E8\u8FEF\u8FF0\uD3C7"+ + "\u8FF1\u8FF2\uE2EC\uBFEC\u8FF3\uE2ED\uE2E5\u8FF4"+ + "\u8FF5\uB3C0\u8FF6\u8FF7\u8FF8\uC4EE\u8FF9\u8FFA"+ + "\uE2EE\u8FFB\u8FFC\uD0C3\u8FFD\uBAF6\uE2E9\uB7DE"; + + private final static String innerEncoderIndex6= + "\uBBB3\uCCAC\uCBCB\uE2E4\uE2E6\uE2EA\uE2EB\u8FFE"+ + "\u9040\u9041\uE2F7\u9042\u9043\uE2F4\uD4F5\uE2F3"+ + "\u9044\u9045\uC5AD\u9046\uD5FA\uC5C2\uB2C0\u9047"+ + "\u9048\uE2EF\u9049\uE2F2\uC1AF\uCBBC\u904A\u904B"+ + "\uB5A1\uE2F9\u904C\u904D\u904E\uBCB1\uE2F1\uD0D4"+ + "\uD4B9\uE2F5\uB9D6\uE2F6\u904F\u9050\u9051\uC7D3"+ + "\u9052\u9053\u9054\u9055\u9056\uE2F0\u9057\u9058"+ + "\u9059\u905A\u905B\uD7DC\uEDA1\u905C\u905D\uE2F8"+ + "\u905E\uEDA5\uE2FE\uCAD1\u905F\u9060\u9061\u9062"+ + "\u9063\u9064\u9065\uC1B5\u9066\uBBD0\u9067\u9068"+ + "\uBFD6\u9069\uBAE3\u906A\u906B\uCBA1\u906C\u906D"+ + "\u906E\uEDA6\uEDA3\u906F\u9070\uEDA2\u9071\u9072"+ + "\u9073\u9074\uBBD6\uEDA7\uD0F4\u9075\u9076\uEDA4"+ + "\uBADE\uB6F7\uE3A1\uB6B2\uCCF1\uB9A7\u9077\uCFA2"+ + "\uC7A1\u9078\u9079\uBFD2\u907A\u907B\uB6F1\u907C"+ + "\uE2FA\uE2FB\uE2FD\uE2FC\uC4D5\uE3A2\u907D\uD3C1"+ + "\u907E\u9080\u9081\uE3A7\uC7C4\u9082\u9083\u9084"+ + "\u9085\uCFA4\u9086\u9087\uE3A9\uBAB7\u9088\u9089"+ + "\u908A\u908B\uE3A8\u908C\uBBDA\u908D\uE3A3\u908E"+ + "\u908F\u9090\uE3A4\uE3AA\u9091\uE3A6\u9092\uCEF2"+ + "\uD3C6\u9093\u9094\uBBBC\u9095\u9096\uD4C3\u9097"+ + "\uC4FA\u9098\u9099\uEDA8\uD0FC\uE3A5\u909A\uC3F5"+ + "\u909B\uE3AD\uB1AF\u909C\uE3B2\u909D\u909E\u909F"+ + "\uBCC2\u90A0\u90A1\uE3AC\uB5BF\u90A2\u90A3\u90A4"+ + "\u90A5\u90A6\u90A7\u90A8\u90A9\uC7E9\uE3B0\u90AA"+ + "\u90AB\u90AC\uBEAA\uCDEF\u90AD\u90AE\u90AF\u90B0"+ + "\u90B1\uBBF3\u90B2\u90B3\u90B4\uCCE8\u90B5\u90B6"+ + "\uE3AF\u90B7\uE3B1\u90B8\uCFA7\uE3AE\u90B9\uCEA9"+ + "\uBBDD\u90BA\u90BB\u90BC\u90BD\u90BE\uB5EB\uBEE5"+ + "\uB2D2\uB3CD\u90BF\uB1B9\uE3AB\uB2D1\uB5AC\uB9DF"+ + "\uB6E8\u90C0\u90C1\uCFEB\uE3B7\u90C2\uBBCC\u90C3"+ + "\u90C4\uC8C7\uD0CA\u90C5\u90C6\u90C7\u90C8\u90C9"+ + "\uE3B8\uB3EE\u90CA\u90CB\u90CC\u90CD\uEDA9\u90CE"+ + "\uD3FA\uD3E4\u90CF\u90D0\u90D1\uEDAA\uE3B9\uD2E2"+ + "\u90D2\u90D3\u90D4\u90D5\u90D6\uE3B5\u90D7\u90D8"+ + "\u90D9\u90DA\uD3DE\u90DB\u90DC\u90DD\u90DE\uB8D0"+ + "\uE3B3\u90DF\u90E0\uE3B6\uB7DF\u90E1\uE3B4\uC0A2"+ + "\u90E2\u90E3\u90E4\uE3BA\u90E5\u90E6\u90E7\u90E8"+ + "\u90E9\u90EA\u90EB\u90EC\u90ED\u90EE\u90EF\u90F0"+ + "\u90F1\u90F2\u90F3\u90F4\u90F5\u90F6\u90F7\uD4B8"+ + "\u90F8\u90F9\u90FA\u90FB\u90FC\u90FD\u90FE\u9140"+ + "\uB4C8\u9141\uE3BB\u9142\uBBC5\u9143\uC9F7\u9144"+ + "\u9145\uC9E5\u9146\u9147\u9148\uC4BD\u9149\u914A"+ + "\u914B\u914C\u914D\u914E\u914F\uEDAB\u9150\u9151"+ + "\u9152\u9153\uC2FD\u9154\u9155\u9156\u9157\uBBDB"+ + "\uBFAE\u9158\u9159\u915A\u915B\u915C\u915D\u915E"+ + "\uCEBF\u915F\u9160\u9161\u9162\uE3BC\u9163\uBFB6"+ + "\u9164\u9165\u9166\u9167\u9168\u9169\u916A\u916B"+ + "\u916C\u916D\u916E\u916F\u9170\u9171\u9172\u9173"+ + "\u9174\u9175\u9176\uB1EF\u9177\u9178\uD4F7\u9179"+ + "\u917A\u917B\u917C\u917D\uE3BE\u917E\u9180\u9181"+ + "\u9182\u9183\u9184\u9185\u9186\uEDAD\u9187\u9188"+ + "\u9189\u918A\u918B\u918C\u918D\u918E\u918F\uE3BF"+ + "\uBAA9\uEDAC\u9190\u9191\uE3BD\u9192\u9193\u9194"+ + "\u9195\u9196\u9197\u9198\u9199\u919A\u919B\uE3C0"+ + "\u919C\u919D\u919E\u919F\u91A0\u91A1\uBAB6\u91A2"+ + "\u91A3\u91A4\uB6AE\u91A5\u91A6\u91A7\u91A8\u91A9"+ + "\uD0B8\u91AA\uB0C3\uEDAE\u91AB\u91AC\u91AD\u91AE"+ + "\u91AF\uEDAF\uC0C1\u91B0\uE3C1\u91B1\u91B2\u91B3"+ + "\u91B4\u91B5\u91B6\u91B7\u91B8\u91B9\u91BA\u91BB"+ + "\u91BC\u91BD\u91BE\u91BF\u91C0\u91C1\uC5B3\u91C2"+ + "\u91C3\u91C4\u91C5\u91C6\u91C7\u91C8\u91C9\u91CA"+ + "\u91CB\u91CC\u91CD\u91CE\u91CF\uE3C2\u91D0\u91D1"+ + "\u91D2\u91D3\u91D4\u91D5\u91D6\u91D7\u91D8\uDCB2"+ + "\u91D9\u91DA\u91DB\u91DC\u91DD\u91DE\uEDB0\u91DF"+ + "\uB8EA\u91E0\uCEEC\uEAA7\uD0E7\uCAF9\uC8D6\uCFB7"+ + "\uB3C9\uCED2\uBDE4\u91E1\u91E2\uE3DE\uBBF2\uEAA8"+ + "\uD5BD\u91E3\uC6DD\uEAA9\u91E4\u91E5\u91E6\uEAAA"+ + "\u91E7\uEAAC\uEAAB\u91E8\uEAAE\uEAAD\u91E9\u91EA"+ + "\u91EB\u91EC\uBDD8\u91ED\uEAAF\u91EE\uC2BE\u91EF"+ + "\u91F0\u91F1\u91F2\uB4C1\uB4F7\u91F3\u91F4\uBBA7"+ + "\u91F5\u91F6\u91F7\u91F8\u91F9\uECE6\uECE5\uB7BF"+ + "\uCBF9\uB1E2\u91FA\uECE7\u91FB\u91FC\u91FD\uC9C8"+ + "\uECE8\uECE9\u91FE\uCAD6\uDED0\uB2C5\uD4FA\u9240"+ + "\u9241\uC6CB\uB0C7\uB4F2\uC8D3\u9242\u9243\u9244"+ + "\uCDD0\u9245\u9246\uBFB8\u9247\u9248\u9249\u924A"+ + "\u924B\u924C\u924D\uBFDB\u924E\u924F\uC7A4\uD6B4"+ + "\u9250\uC0A9\uDED1\uC9A8\uD1EF\uC5A4\uB0E7\uB3B6"+ + "\uC8C5\u9251\u9252\uB0E2\u9253\u9254\uB7F6\u9255"+ + "\u9256\uC5FA\u9257\u9258\uB6F3\u9259\uD5D2\uB3D0"+ + "\uBCBC\u925A\u925B\u925C\uB3AD\u925D\u925E\u925F"+ + "\u9260\uBEF1\uB0D1\u9261\u9262\u9263\u9264\u9265"+ + "\u9266\uD2D6\uCAE3\uD7A5\u9267\uCDB6\uB6B6\uBFB9"+ + "\uD5DB\u9268\uB8A7\uC5D7\u9269\u926A\u926B\uDED2"+ + "\uBFD9\uC2D5\uC7C0\u926C\uBBA4\uB1A8\u926D\u926E"+ + "\uC5EA\u926F\u9270\uC5FB\uCCA7\u9271\u9272\u9273"+ + "\u9274\uB1A7\u9275\u9276\u9277\uB5D6\u9278\u9279"+ + "\u927A\uC4A8\u927B\uDED3\uD1BA\uB3E9\u927C\uC3F2"+ + "\u927D\u927E\uB7F7\u9280\uD6F4\uB5A3\uB2F0\uC4B4"+ + "\uC4E9\uC0AD\uDED4\u9281\uB0E8\uC5C4\uC1E0\u9282"+ + "\uB9D5\u9283\uBEDC\uCDD8\uB0CE\u9284\uCDCF\uDED6"+ + "\uBED0\uD7BE\uDED5\uD5D0\uB0DD\u9285\u9286\uC4E2"+ + "\u9287\u9288\uC2A3\uBCF0\u9289\uD3B5\uC0B9\uC5A1"+ + "\uB2A6\uD4F1\u928A\u928B\uC0A8\uCAC3\uDED7\uD5FC"+ + "\u928C\uB9B0\u928D\uC8AD\uCBA9\u928E\uDED9\uBFBD"+ + "\u928F\u9290\u9291\u9292\uC6B4\uD7A7\uCAB0\uC4C3"+ + "\u9293\uB3D6\uB9D2\u9294\u9295\u9296\u9297\uD6B8"+ + "\uEAFC\uB0B4\u9298\u9299\u929A\u929B\uBFE6\u929C"+ + "\u929D\uCCF4\u929E\u929F\u92A0\u92A1\uCDDA\u92A2"+ + "\u92A3\u92A4\uD6BF\uC2CE\u92A5\uCECE\uCCA2\uD0AE"+ + "\uC4D3\uB5B2\uDED8\uD5F5\uBCB7\uBBD3\u92A6\u92A7"+ + "\uB0A4\u92A8\uC5B2\uB4EC\u92A9\u92AA\u92AB\uD5F1"+ + "\u92AC\u92AD\uEAFD\u92AE\u92AF\u92B0\u92B1\u92B2"+ + "\u92B3\uDEDA\uCDA6\u92B4\u92B5\uCDEC\u92B6\u92B7"+ + "\u92B8\u92B9\uCEE6\uDEDC\u92BA\uCDB1\uC0A6\u92BB"+ + "\u92BC\uD7BD\u92BD\uDEDB\uB0C6\uBAB4\uC9D3\uC4F3"+ + "\uBEE8\u92BE\u92BF\u92C0\u92C1\uB2B6\u92C2\u92C3"+ + "\u92C4\u92C5\u92C6\u92C7\u92C8\u92C9\uC0CC\uCBF0"+ + "\u92CA\uBCF1\uBBBB\uB5B7\u92CB\u92CC\u92CD\uC5F5"+ + "\u92CE\uDEE6\u92CF\u92D0\u92D1\uDEE3\uBEDD\u92D2"+ + "\u92D3\uDEDF\u92D4\u92D5\u92D6\u92D7\uB4B7\uBDDD"+ + "\u92D8\u92D9\uDEE0\uC4ED\u92DA\u92DB\u92DC\u92DD"+ + "\uCFC6\u92DE\uB5E0\u92DF\u92E0\u92E1\u92E2\uB6DE"+ + "\uCADA\uB5F4\uDEE5\u92E3\uD5C6\u92E4\uDEE1\uCCCD"+ + "\uC6FE\u92E5\uC5C5\u92E6\u92E7\u92E8\uD2B4\u92E9"+ + "\uBEF2\u92EA\u92EB\u92EC\u92ED\u92EE\u92EF\u92F0"+ + "\uC2D3\u92F1\uCCBD\uB3B8\u92F2\uBDD3\u92F3\uBFD8"+ + "\uCDC6\uD1DA\uB4EB\u92F4\uDEE4\uDEDD\uDEE7\u92F5"+ + "\uEAFE\u92F6\u92F7\uC2B0\uDEE2\u92F8\u92F9\uD6C0"+ + "\uB5A7\u92FA\uB2F4\u92FB\uDEE8\u92FC\uDEF2\u92FD"+ + "\u92FE\u9340\u9341\u9342\uDEED\u9343\uDEF1\u9344"+ + "\u9345\uC8E0\u9346\u9347\u9348\uD7E1\uDEEF\uC3E8"+ + "\uCCE1\u9349\uB2E5\u934A\u934B\u934C\uD2BE\u934D"+ + "\u934E\u934F\u9350\u9351\u9352\u9353\uDEEE\u9354"+ + "\uDEEB\uCED5\u9355\uB4A7\u9356\u9357\u9358\u9359"+ + "\u935A\uBFAB\uBEBE\u935B\u935C\uBDD2\u935D\u935E"+ + "\u935F\u9360\uDEE9\u9361\uD4AE\u9362\uDEDE\u9363"+ + "\uDEEA\u9364\u9365\u9366\u9367\uC0BF\u9368\uDEEC"+ + "\uB2F3\uB8E9\uC2A7\u9369\u936A\uBDC1\u936B\u936C"+ + "\u936D\u936E\u936F\uDEF5\uDEF8\u9370\u9371\uB2AB"+ + "\uB4A4\u9372\u9373\uB4EA\uC9A6\u9374\u9375\u9376"+ + "\u9377\u9378\u9379\uDEF6\uCBD1\u937A\uB8E3\u937B"+ + "\uDEF7\uDEFA\u937C\u937D\u937E\u9380\uDEF9\u9381"+ + "\u9382\u9383\uCCC2\u9384\uB0E1\uB4EE\u9385\u9386"+ + "\u9387\u9388\u9389\u938A\uE5BA\u938B\u938C\u938D"+ + "\u938E\u938F\uD0AF\u9390\u9391\uB2EB\u9392\uEBA1"+ + "\u9393\uDEF4\u9394\u9395\uC9E3\uDEF3\uB0DA\uD2A1"+ + "\uB1F7\u9396\uCCAF\u9397\u9398\u9399\u939A\u939B"+ + "\u939C\u939D\uDEF0\u939E\uCBA4\u939F\u93A0\u93A1"+ + "\uD5AA\u93A2\u93A3\u93A4\u93A5\u93A6\uDEFB\u93A7"+ + "\u93A8\u93A9\u93AA\u93AB\u93AC\u93AD\u93AE\uB4DD"+ + "\u93AF\uC4A6\u93B0\u93B1\u93B2\uDEFD\u93B3\u93B4"+ + "\u93B5\u93B6\u93B7\u93B8\u93B9\u93BA\u93BB\u93BC"+ + "\uC3FE\uC4A1\uDFA1\u93BD\u93BE\u93BF\u93C0\u93C1"+ + "\u93C2\u93C3\uC1CC\u93C4\uDEFC\uBEEF\u93C5\uC6B2"+ + "\u93C6\u93C7\u93C8\u93C9\u93CA\u93CB\u93CC\u93CD"+ + "\u93CE\uB3C5\uC8F6\u93CF\u93D0\uCBBA\uDEFE\u93D1"+ + "\u93D2\uDFA4\u93D3\u93D4\u93D5\u93D6\uD7B2\u93D7"+ + "\u93D8\u93D9\u93DA\u93DB\uB3B7\u93DC\u93DD\u93DE"+ + "\u93DF\uC1C3\u93E0\u93E1\uC7CB\uB2A5\uB4E9\u93E2"+ + "\uD7AB\u93E3\u93E4\u93E5\u93E6\uC4EC\u93E7\uDFA2"+ + "\uDFA3\u93E8\uDFA5\u93E9\uBAB3\u93EA\u93EB\u93EC"+ + "\uDFA6\u93ED\uC0DE\u93EE\u93EF\uC9C3\u93F0\u93F1"+ + "\u93F2\u93F3\u93F4\u93F5\u93F6\uB2D9\uC7E6\u93F7"+ + "\uDFA7\u93F8\uC7DC\u93F9\u93FA\u93FB\u93FC\uDFA8"+ + "\uEBA2\u93FD\u93FE\u9440\u9441\u9442\uCBD3\u9443"+ + "\u9444\u9445\uDFAA\u9446\uDFA9\u9447\uB2C1\u9448"+ + "\u9449\u944A\u944B\u944C\u944D\u944E\u944F\u9450"+ + "\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458"+ + "\u9459\u945A\u945B\u945C\u945D\u945E\u945F\u9460"+ + "\uC5CA\u9461\u9462\u9463\u9464\u9465\u9466\u9467"+ + "\u9468\uDFAB\u9469\u946A\u946B\u946C\u946D\u946E"+ + "\u946F\u9470\uD4DC\u9471\u9472\u9473\u9474\u9475"+ + "\uC8C1\u9476\u9477\u9478\u9479\u947A\u947B\u947C"+ + "\u947D\u947E\u9480\u9481\u9482\uDFAC\u9483\u9484"+ + "\u9485\u9486\u9487\uBEF0\u9488\u9489\uDFAD\uD6A7"+ + "\u948A\u948B\u948C\u948D\uEAB7\uEBB6\uCAD5\u948E"+ + "\uD8FC\uB8C4\u948F\uB9A5\u9490\u9491\uB7C5\uD5FE"+ + "\u9492\u9493\u9494\u9495\u9496\uB9CA\u9497\u9498"+ + "\uD0A7\uF4CD\u9499\u949A\uB5D0\u949B\u949C\uC3F4"+ + "\u949D\uBEC8\u949E\u949F\u94A0\uEBB7\uB0BD\u94A1"+ + "\u94A2\uBDCC\u94A3\uC1B2\u94A4\uB1D6\uB3A8\u94A5"+ + "\u94A6\u94A7\uB8D2\uC9A2\u94A8\u94A9\uB6D8\u94AA"+ + "\u94AB\u94AC\u94AD\uEBB8\uBEB4\u94AE\u94AF\u94B0"+ + "\uCAFD\u94B1\uC7C3\u94B2\uD5FB\u94B3\u94B4\uB7F3"+ + "\u94B5\u94B6\u94B7\u94B8\u94B9\u94BA\u94BB\u94BC"+ + "\u94BD\u94BE\u94BF\u94C0\u94C1\u94C2\u94C3\uCEC4"+ + "\u94C4\u94C5\u94C6\uD5AB\uB1F3\u94C7\u94C8\u94C9"+ + "\uECB3\uB0DF\u94CA\uECB5\u94CB\u94CC\u94CD\uB6B7"+ + "\u94CE\uC1CF\u94CF\uF5FA\uD0B1\u94D0\u94D1\uD5E5"+ + "\u94D2\uCED3\u94D3\u94D4\uBDEF\uB3E2\u94D5\uB8AB"+ + "\u94D6\uD5B6\u94D7\uEDBD\u94D8\uB6CF\u94D9\uCBB9"+ + "\uD0C2\u94DA\u94DB\u94DC\u94DD\u94DE\u94DF\u94E0"+ + "\u94E1\uB7BD\u94E2\u94E3\uECB6\uCAA9\u94E4\u94E5"+ + "\u94E6\uC5D4\u94E7\uECB9\uECB8\uC2C3\uECB7\u94E8"+ + "\u94E9\u94EA\u94EB\uD0FD\uECBA\u94EC\uECBB\uD7E5"+ + "\u94ED\u94EE\uECBC\u94EF\u94F0\u94F1\uECBD\uC6EC"+ + "\u94F2\u94F3\u94F4\u94F5\u94F6\u94F7\u94F8\u94F9"+ + "\uCEDE\u94FA\uBCC8\u94FB\u94FC\uC8D5\uB5A9\uBEC9"+ + "\uD6BC\uD4E7\u94FD\u94FE\uD1AE\uD0F1\uEAB8\uEAB9"+ + "\uEABA\uBAB5\u9540\u9541\u9542\u9543\uCAB1\uBFF5"+ + "\u9544\u9545\uCDFA\u9546\u9547\u9548\u9549\u954A"+ + "\uEAC0\u954B\uB0BA\uEABE\u954C\u954D\uC0A5\u954E"+ + "\u954F\u9550\uEABB\u9551\uB2FD\u9552\uC3F7\uBBE8"+ + "\u9553\u9554\u9555\uD2D7\uCEF4\uEABF\u9556\u9557"+ + "\u9558\uEABC\u9559\u955A\u955B\uEAC3\u955C\uD0C7"+ + "\uD3B3\u955D\u955E\u955F\u9560\uB4BA\u9561\uC3C1"+ + "\uD7F2\u9562\u9563\u9564\u9565\uD5D1\u9566\uCAC7"+ + "\u9567\uEAC5\u9568\u9569\uEAC4\uEAC7\uEAC6\u956A"+ + "\u956B\u956C\u956D\u956E\uD6E7\u956F\uCFD4\u9570"+ + "\u9571\uEACB\u9572\uBBCE\u9573\u9574\u9575\u9576"+ + "\u9577\u9578\u9579\uBDFA\uC9CE\u957A\u957B\uEACC"+ + "\u957C\u957D\uC9B9\uCFFE\uEACA\uD4CE\uEACD\uEACF"+ + "\u957E\u9580\uCDED\u9581\u9582\u9583\u9584\uEAC9"+ + "\u9585\uEACE\u9586\u9587\uCEEE\u9588\uBBDE\u9589"+ + "\uB3BF\u958A\u958B\u958C\u958D\u958E\uC6D5\uBEB0"+ + "\uCEFA\u958F\u9590\u9591\uC7E7\u9592\uBEA7\uEAD0"+ + "\u9593\u9594\uD6C7\u9595\u9596\u9597\uC1C0\u9598"+ + "\u9599\u959A\uD4DD\u959B\uEAD1\u959C\u959D\uCFBE"+ + "\u959E\u959F\u95A0\u95A1\uEAD2\u95A2\u95A3\u95A4"+ + "\u95A5\uCAEE\u95A6\u95A7\u95A8\u95A9\uC5AF\uB0B5"+ + "\u95AA\u95AB\u95AC\u95AD\u95AE\uEAD4\u95AF\u95B0"+ + "\u95B1\u95B2\u95B3\u95B4\u95B5\u95B6\u95B7\uEAD3"+ + "\uF4DF\u95B8\u95B9\u95BA\u95BB\u95BC\uC4BA\u95BD"+ + "\u95BE\u95BF\u95C0\u95C1\uB1A9\u95C2\u95C3\u95C4"+ + "\u95C5\uE5DF\u95C6\u95C7\u95C8\u95C9\uEAD5\u95CA"+ + "\u95CB\u95CC\u95CD\u95CE\u95CF\u95D0\u95D1\u95D2"+ + "\u95D3\u95D4\u95D5\u95D6\u95D7\u95D8\u95D9\u95DA"+ + "\u95DB\u95DC\u95DD\u95DE\u95DF\u95E0\u95E1\u95E2"+ + "\u95E3\uCAEF\u95E4\uEAD6\uEAD7\uC6D8\u95E5\u95E6"+ + "\u95E7\u95E8\u95E9\u95EA\u95EB\u95EC\uEAD8\u95ED"+ + "\u95EE\uEAD9\u95EF\u95F0\u95F1\u95F2\u95F3\u95F4"+ + "\uD4BB\u95F5\uC7FA\uD2B7\uB8FC\u95F6\u95F7\uEAC2"+ + "\u95F8\uB2DC\u95F9\u95FA\uC2FC\u95FB\uD4F8\uCCE6"+ + "\uD7EE\u95FC\u95FD\u95FE\u9640\u9641\u9642\u9643"+ + "\uD4C2\uD3D0\uEBC3\uC5F3\u9644\uB7FE\u9645\u9646"+ + "\uEBD4\u9647\u9648\u9649\uCBB7\uEBDE\u964A\uC0CA"+ + "\u964B\u964C\u964D\uCDFB\u964E\uB3AF\u964F\uC6DA"+ + "\u9650\u9651\u9652\u9653\u9654\u9655\uEBFC\u9656"+ + "\uC4BE\u9657\uCEB4\uC4A9\uB1BE\uD4FD\u9658\uCAF5"+ + "\u9659\uD6EC\u965A\u965B\uC6D3\uB6E4\u965C\u965D"+ + "\u965E\u965F\uBBFA\u9660\u9661\uD0E0\u9662\u9663"+ + "\uC9B1\u9664\uD4D3\uC8A8\u9665\u9666\uB8CB\u9667"+ + "\uE8BE\uC9BC\u9668\u9669\uE8BB\u966A\uC0EE\uD0D3"+ + "\uB2C4\uB4E5\u966B\uE8BC\u966C\u966D\uD5C8\u966E"+ + "\u966F\u9670\u9671\u9672\uB6C5\u9673\uE8BD\uCAF8"+ + "\uB8DC\uCCF5\u9674\u9675\u9676\uC0B4\u9677\u9678"+ + "\uD1EE\uE8BF\uE8C2\u9679\u967A\uBABC\u967B\uB1AD"+ + "\uBDDC\u967C\uEABD\uE8C3\u967D\uE8C6\u967E\uE8CB"+ + "\u9680\u9681\u9682\u9683\uE8CC\u9684\uCBC9\uB0E5"+ + "\u9685\uBCAB\u9686\u9687\uB9B9\u9688\u9689\uE8C1"+ + "\u968A\uCDF7\u968B\uE8CA\u968C\u968D\u968E\u968F"+ + "\uCEF6\u9690\u9691\u9692\u9693\uD5ED\u9694\uC1D6"+ + "\uE8C4\u9695\uC3B6\u9696\uB9FB\uD6A6\uE8C8\u9697"+ + "\u9698\u9699\uCAE0\uD4E6\u969A\uE8C0\u969B\uE8C5"+ + "\uE8C7\u969C\uC7B9\uB7E3\u969D\uE8C9\u969E\uBFDD"+ + "\uE8D2\u969F\u96A0\uE8D7\u96A1\uE8D5\uBCDC\uBCCF"+ + "\uE8DB\u96A2\u96A3\u96A4\u96A5\u96A6\u96A7\u96A8"+ + "\u96A9\uE8DE\u96AA\uE8DA\uB1FA\u96AB\u96AC\u96AD"+ + "\u96AE\u96AF\u96B0\u96B1\u96B2\u96B3\u96B4\uB0D8"+ + "\uC4B3\uB8CC\uC6E2\uC8BE\uC8E1\u96B5\u96B6\u96B7"+ + "\uE8CF\uE8D4\uE8D6\u96B8\uB9F1\uE8D8\uD7F5\u96B9"+ + "\uC4FB\u96BA\uE8DC\u96BB\u96BC\uB2E9\u96BD\u96BE"+ + "\u96BF\uE8D1\u96C0\u96C1\uBCED\u96C2\u96C3\uBFC2"+ + "\uE8CD\uD6F9\u96C4\uC1F8\uB2F1\u96C5\u96C6\u96C7"+ + "\u96C8\u96C9\u96CA\u96CB\u96CC\uE8DF\u96CD\uCAC1"+ + "\uE8D9\u96CE\u96CF\u96D0\u96D1\uD5A4\u96D2\uB1EA"+ + "\uD5BB\uE8CE\uE8D0\uB6B0\uE8D3\u96D3\uE8DD\uC0B8"+ + "\u96D4\uCAF7\u96D5\uCBA8\u96D6\u96D7\uC6DC\uC0F5"+ + "\u96D8\u96D9\u96DA\u96DB\u96DC\uE8E9\u96DD\u96DE"+ + "\u96DF\uD0A3\u96E0\u96E1\u96E2\u96E3\u96E4\u96E5"+ + "\u96E6\uE8F2\uD6EA\u96E7\u96E8\u96E9\u96EA\u96EB"+ + "\u96EC\u96ED\uE8E0\uE8E1\u96EE\u96EF\u96F0\uD1F9"+ + "\uBACB\uB8F9\u96F1\u96F2\uB8F1\uD4D4\uE8EF\u96F3"+ + "\uE8EE\uE8EC\uB9F0\uCCD2\uE8E6\uCEA6\uBFF2\u96F4"+ + "\uB0B8\uE8F1\uE8F0\u96F5\uD7C0\u96F6\uE8E4\u96F7"+ + "\uCDA9\uC9A3\u96F8\uBBB8\uBDDB\uE8EA\u96F9\u96FA"+ + "\u96FB\u96FC\u96FD\u96FE\u9740\u9741\u9742\u9743"+ + "\uE8E2\uE8E3\uE8E5\uB5B5\uE8E7\uC7C5\uE8EB\uE8ED"+ + "\uBDB0\uD7AE\u9744\uE8F8\u9745\u9746\u9747\u9748"+ + "\u9749\u974A\u974B\u974C\uE8F5\u974D\uCDB0\uE8F6"+ + "\u974E\u974F\u9750\u9751\u9752\u9753\u9754\u9755"+ + "\u9756\uC1BA\u9757\uE8E8\u9758\uC3B7\uB0F0\u9759"+ + "\u975A\u975B\u975C\u975D\u975E\u975F\u9760\uE8F4"+ + "\u9761\u9762\u9763\uE8F7\u9764\u9765\u9766\uB9A3"+ + "\u9767\u9768\u9769\u976A\u976B\u976C\u976D\u976E"+ + "\u976F\u9770\uC9D2\u9771\u9772\u9773\uC3CE\uCEE0"+ + "\uC0E6\u9774\u9775\u9776\u9777\uCBF3\u9778\uCCDD"+ + "\uD0B5\u9779\u977A\uCAE1\u977B\uE8F3\u977C\u977D"+ + "\u977E\u9780\u9781\u9782\u9783\u9784\u9785\u9786"+ + "\uBCEC\u9787\uE8F9\u9788\u9789\u978A\u978B\u978C"+ + "\u978D\uC3DE\u978E\uC6E5\u978F\uB9F7\u9790\u9791"+ + "\u9792\u9793\uB0F4\u9794\u9795\uD7D8\u9796\u9797"+ + "\uBCAC\u9798\uC5EF\u9799\u979A\u979B\u979C\u979D"+ + "\uCCC4\u979E\u979F\uE9A6\u97A0\u97A1\u97A2\u97A3"+ + "\u97A4\u97A5\u97A6\u97A7\u97A8\u97A9\uC9AD\u97AA"+ + "\uE9A2\uC0E2\u97AB\u97AC\u97AD\uBFC3\u97AE\u97AF"+ + "\u97B0\uE8FE\uB9D7\u97B1\uE8FB\u97B2\u97B3\u97B4"+ + "\u97B5\uE9A4\u97B6\u97B7\u97B8\uD2CE\u97B9\u97BA"+ + "\u97BB\u97BC\u97BD\uE9A3\u97BE\uD6B2\uD7B5\u97BF"+ + "\uE9A7\u97C0\uBDB7\u97C1\u97C2\u97C3\u97C4\u97C5"+ + "\u97C6\u97C7\u97C8\u97C9\u97CA\u97CB\u97CC\uE8FC"+ + "\uE8FD\u97CD\u97CE\u97CF\uE9A1\u97D0\u97D1\u97D2"+ + "\u97D3\u97D4\u97D5\u97D6\u97D7\uCDD6\u97D8\u97D9"+ + "\uD2AC\u97DA\u97DB\u97DC\uE9B2\u97DD\u97DE\u97DF"+ + "\u97E0\uE9A9\u97E1\u97E2\u97E3\uB4AA\u97E4\uB4BB"+ + "\u97E5\u97E6\uE9AB\u97E7\u97E8\u97E9\u97EA\u97EB"+ + "\u97EC\u97ED\u97EE\u97EF\u97F0\u97F1\u97F2\u97F3"+ + "\u97F4\u97F5\u97F6\u97F7\uD0A8\u97F8\u97F9\uE9A5"+ + "\u97FA\u97FB\uB3FE\u97FC\u97FD\uE9AC\uC0E3\u97FE"+ + "\uE9AA\u9840\u9841\uE9B9\u9842\u9843\uE9B8\u9844"+ + "\u9845\u9846\u9847\uE9AE\u9848\u9849\uE8FA\u984A"+ + "\u984B\uE9A8\u984C\u984D\u984E\u984F\u9850\uBFAC"+ + "\uE9B1\uE9BA\u9851\u9852\uC2A5\u9853\u9854\u9855"+ + "\uE9AF\u9856\uB8C5\u9857\uE9AD\u9858\uD3DC\uE9B4"+ + "\uE9B5\uE9B7\u9859\u985A\u985B\uE9C7\u985C\u985D"+ + "\u985E\u985F\u9860\u9861\uC0C6\uE9C5\u9862\u9863"+ + "\uE9B0\u9864\u9865\uE9BB\uB0F1\u9866\u9867\u9868"+ + "\u9869\u986A\u986B\u986C\u986D\u986E\u986F\uE9BC"+ + "\uD5A5\u9870\u9871\uE9BE\u9872\uE9BF\u9873\u9874"+ + "\u9875\uE9C1\u9876\u9877\uC1F1\u9878\u9879\uC8B6"+ + "\u987A\u987B\u987C\uE9BD\u987D\u987E\u9880\u9881"+ + "\u9882\uE9C2\u9883\u9884\u9885\u9886\u9887\u9888"+ + "\u9889\u988A\uE9C3\u988B\uE9B3\u988C\uE9B6\u988D"+ + "\uBBB1\u988E\u988F\u9890\uE9C0\u9891\u9892\u9893"+ + "\u9894\u9895\u9896\uBCF7\u9897\u9898\u9899\uE9C4"+ + "\uE9C6\u989A\u989B\u989C\u989D\u989E\u989F\u98A0"+ + "\u98A1\u98A2\u98A3\u98A4\u98A5\uE9CA\u98A6\u98A7"+ + "\u98A8\u98A9\uE9CE\u98AA\u98AB\u98AC\u98AD\u98AE"+ + "\u98AF\u98B0\u98B1\u98B2\u98B3\uB2DB\u98B4\uE9C8"+ + "\u98B5\u98B6\u98B7\u98B8\u98B9\u98BA\u98BB\u98BC"+ + "\u98BD\u98BE\uB7AE\u98BF\u98C0\u98C1\u98C2\u98C3"+ + "\u98C4\u98C5\u98C6\u98C7\u98C8\u98C9\u98CA\uE9CB"+ + "\uE9CC\u98CB\u98CC\u98CD\u98CE\u98CF\u98D0\uD5C1"+ + "\u98D1\uC4A3\u98D2\u98D3\u98D4\u98D5\u98D6\u98D7"+ + "\uE9D8\u98D8\uBAE1\u98D9\u98DA\u98DB\u98DC\uE9C9"+ + "\u98DD\uD3A3\u98DE\u98DF\u98E0\uE9D4\u98E1\u98E2"+ + "\u98E3\u98E4\u98E5\u98E6\u98E7\uE9D7\uE9D0\u98E8"+ + "\u98E9\u98EA\u98EB\u98EC\uE9CF\u98ED\u98EE\uC7C1"+ + "\u98EF\u98F0\u98F1\u98F2\u98F3\u98F4\u98F5\u98F6"+ + "\uE9D2\u98F7\u98F8\u98F9\u98FA\u98FB\u98FC\u98FD"+ + "\uE9D9\uB3C8\u98FE\uE9D3\u9940\u9941\u9942\u9943"+ + "\u9944\uCFF0\u9945\u9946\u9947\uE9CD\u9948\u9949"+ + "\u994A\u994B\u994C\u994D\u994E\u994F\u9950\u9951"+ + "\u9952\uB3F7\u9953\u9954\u9955\u9956\u9957\u9958"+ + "\u9959\uE9D6\u995A\u995B\uE9DA\u995C\u995D\u995E"+ + "\uCCB4\u995F\u9960\u9961\uCFAD\u9962\u9963\u9964"+ + "\u9965\u9966\u9967\u9968\u9969\u996A\uE9D5\u996B"+ + "\uE9DC\uE9DB\u996C\u996D\u996E\u996F\u9970\uE9DE"+ + "\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978"+ + "\uE9D1\u9979\u997A\u997B\u997C\u997D\u997E\u9980"+ + "\u9981\uE9DD\u9982\uE9DF\uC3CA\u9983\u9984\u9985"+ + "\u9986\u9987\u9988\u9989\u998A\u998B\u998C\u998D"+ + "\u998E\u998F\u9990\u9991\u9992\u9993\u9994\u9995"+ + "\u9996\u9997\u9998\u9999\u999A\u999B\u999C\u999D"+ + "\u999E\u999F\u99A0\u99A1\u99A2\u99A3\u99A4\u99A5"+ + "\u99A6\u99A7\u99A8\u99A9\u99AA\u99AB\u99AC\u99AD"+ + "\u99AE\u99AF\u99B0\u99B1\u99B2\u99B3\u99B4\u99B5"+ + "\u99B6\u99B7\u99B8\u99B9\u99BA\u99BB\u99BC\u99BD"+ + "\u99BE\u99BF\u99C0\u99C1\u99C2\u99C3\u99C4\u99C5"+ + "\u99C6\u99C7\u99C8\u99C9\u99CA\u99CB\u99CC\u99CD"+ + "\u99CE\u99CF\u99D0\u99D1\u99D2\u99D3\u99D4\u99D5"+ + "\u99D6\u99D7\u99D8\u99D9\u99DA\u99DB\u99DC\u99DD"+ + "\u99DE\u99DF\u99E0\u99E1\u99E2\u99E3\u99E4\u99E5"+ + "\u99E6\u99E7\u99E8\u99E9\u99EA\u99EB\u99EC\u99ED"+ + "\u99EE\u99EF\u99F0\u99F1\u99F2\u99F3\u99F4\u99F5"+ + "\uC7B7\uB4CE\uBBB6\uD0C0\uECA3\u99F6\u99F7\uC5B7"+ + "\u99F8\u99F9\u99FA\u99FB\u99FC\u99FD\u99FE\u9A40"+ + "\u9A41\u9A42\uD3FB\u9A43\u9A44\u9A45\u9A46\uECA4"+ + "\u9A47\uECA5\uC6DB\u9A48\u9A49\u9A4A\uBFEE\u9A4B"+ + "\u9A4C\u9A4D\u9A4E\uECA6\u9A4F\u9A50\uECA7\uD0AA"+ + "\u9A51\uC7B8\u9A52\u9A53\uB8E8\u9A54\u9A55\u9A56"+ + "\u9A57\u9A58\u9A59\u9A5A\u9A5B\u9A5C\u9A5D\u9A5E"+ + "\u9A5F\uECA8\u9A60\u9A61\u9A62\u9A63\u9A64\u9A65"+ + "\u9A66\u9A67\uD6B9\uD5FD\uB4CB\uB2BD\uCEE4\uC6E7"+ + "\u9A68\u9A69\uCDE1\u9A6A\u9A6B\u9A6C\u9A6D\u9A6E"+ + "\u9A6F\u9A70\u9A71\u9A72\u9A73\u9A74\u9A75\u9A76"+ + "\u9A77\uB4F5\u9A78\uCBC0\uBCDF\u9A79\u9A7A\u9A7B"+ + "\u9A7C\uE9E2\uE9E3\uD1EA\uE9E5\u9A7D\uB4F9\uE9E4"+ + "\u9A7E\uD1B3\uCAE2\uB2D0\u9A80\uE9E8\u9A81\u9A82"+ + "\u9A83\u9A84\uE9E6\uE9E7\u9A85\u9A86\uD6B3\u9A87"+ + "\u9A88\u9A89\uE9E9\uE9EA\u9A8A\u9A8B\u9A8C\u9A8D"+ + "\u9A8E\uE9EB\u9A8F\u9A90\u9A91\u9A92\u9A93\u9A94"+ + "\u9A95\u9A96\uE9EC\u9A97\u9A98\u9A99\u9A9A\u9A9B"+ + "\u9A9C\u9A9D\u9A9E\uECAF\uC5B9\uB6CE\u9A9F\uD2F3"+ + "\u9AA0\u9AA1\u9AA2\u9AA3\u9AA4\u9AA5\u9AA6\uB5EE"+ + "\u9AA7\uBBD9\uECB1\u9AA8\u9AA9\uD2E3\u9AAA\u9AAB"+ + "\u9AAC\u9AAD\u9AAE\uCEE3\u9AAF\uC4B8\u9AB0\uC3BF"+ + "\u9AB1\u9AB2\uB6BE\uD8B9\uB1C8\uB1CF\uB1D1\uC5FE"+ + "\u9AB3\uB1D0\u9AB4\uC3AB\u9AB5\u9AB6\u9AB7\u9AB8"+ + "\u9AB9\uD5B1\u9ABA\u9ABB\u9ABC\u9ABD\u9ABE\u9ABF"+ + "\u9AC0\u9AC1\uEBA4\uBAC1\u9AC2\u9AC3\u9AC4\uCCBA"+ + "\u9AC5\u9AC6\u9AC7\uEBA5\u9AC8\uEBA7\u9AC9\u9ACA"+ + "\u9ACB\uEBA8\u9ACC\u9ACD\u9ACE\uEBA6\u9ACF\u9AD0"+ + "\u9AD1\u9AD2\u9AD3\u9AD4\u9AD5\uEBA9\uEBAB\uEBAA"+ + "\u9AD6\u9AD7\u9AD8\u9AD9\u9ADA\uEBAC\u9ADB\uCACF"+ + "\uD8B5\uC3F1\u9ADC\uC3A5\uC6F8\uEBAD\uC4CA\u9ADD"+ + "\uEBAE\uEBAF\uEBB0\uB7D5\u9ADE\u9ADF\u9AE0\uB7FA"+ + "\u9AE1\uEBB1\uC7E2\u9AE2\uEBB3\u9AE3\uBAA4\uD1F5"+ + "\uB0B1\uEBB2\uEBB4\u9AE4\u9AE5\u9AE6\uB5AA\uC2C8"+ + "\uC7E8\u9AE7\uEBB5\u9AE8\uCBAE\uE3DF\u9AE9\u9AEA"+ + "\uD3C0\u9AEB\u9AEC\u9AED\u9AEE\uD9DB\u9AEF\u9AF0"+ + "\uCDA1\uD6AD\uC7F3\u9AF1\u9AF2\u9AF3\uD9E0\uBBE3"+ + "\u9AF4\uBABA\uE3E2\u9AF5\u9AF6\u9AF7\u9AF8\u9AF9"+ + "\uCFAB\u9AFA\u9AFB\u9AFC\uE3E0\uC9C7\u9AFD\uBAB9"+ + "\u9AFE\u9B40\u9B41\uD1B4\uE3E1\uC8EA\uB9AF\uBDAD"+ + "\uB3D8\uCEDB\u9B42\u9B43\uCCC0\u9B44\u9B45\u9B46"+ + "\uE3E8\uE3E9\uCDF4\u9B47\u9B48\u9B49\u9B4A\u9B4B"+ + "\uCCAD\u9B4C\uBCB3\u9B4D\uE3EA\u9B4E\uE3EB\u9B4F"+ + "\u9B50\uD0DA\u9B51\u9B52\u9B53\uC6FB\uB7DA\u9B54"+ + "\u9B55\uC7DF\uD2CA\uCED6\u9B56\uE3E4\uE3EC\u9B57"+ + "\uC9F2\uB3C1\u9B58\u9B59\uE3E7\u9B5A\u9B5B\uC6E3"+ + "\uE3E5\u9B5C\u9B5D\uEDB3\uE3E6\u9B5E\u9B5F\u9B60"+ + "\u9B61\uC9B3\u9B62\uC5E6\u9B63\u9B64\u9B65\uB9B5"+ + "\u9B66\uC3BB\u9B67\uE3E3\uC5BD\uC1A4\uC2D9\uB2D7"+ + "\u9B68\uE3ED\uBBA6\uC4AD\u9B69\uE3F0\uBEDA\u9B6A"+ + "\u9B6B\uE3FB\uE3F5\uBAD3\u9B6C\u9B6D\u9B6E\u9B6F"+ + "\uB7D0\uD3CD\u9B70\uD6CE\uD5D3\uB9C1\uD5B4\uD1D8"+ + "\u9B71\u9B72\u9B73\u9B74\uD0B9\uC7F6\u9B75\u9B76"+ + "\u9B77\uC8AA\uB2B4\u9B78\uC3DA\u9B79\u9B7A\u9B7B"+ + "\uE3EE\u9B7C\u9B7D\uE3FC\uE3EF\uB7A8\uE3F7\uE3F4"+ + "\u9B7E\u9B80\u9B81\uB7BA\u9B82\u9B83\uC5A2\u9B84"+ + "\uE3F6\uC5DD\uB2A8\uC6FC\u9B85\uC4E0\u9B86\u9B87"+ + "\uD7A2\u9B88\uC0E1\uE3F9\u9B89\u9B8A\uE3FA\uE3FD"+ + "\uCCA9\uE3F3\u9B8B\uD3BE\u9B8C\uB1C3\uEDB4\uE3F1"+ + "\uE3F2\u9B8D\uE3F8\uD0BA\uC6C3\uD4F3\uE3FE\u9B8E"+ + "\u9B8F\uBDE0\u9B90\u9B91\uE4A7\u9B92\u9B93\uE4A6"+ + "\u9B94\u9B95\u9B96\uD1F3\uE4A3\u9B97\uE4A9\u9B98"+ + "\u9B99\u9B9A\uC8F7\u9B9B\u9B9C\u9B9D\u9B9E\uCFB4"+ + "\u9B9F\uE4A8\uE4AE\uC2E5\u9BA0\u9BA1\uB6B4\u9BA2"+ + "\u9BA3\u9BA4\u9BA5\u9BA6\u9BA7\uBDF2\u9BA8\uE4A2"+ + "\u9BA9\u9BAA\uBAE9\uE4AA\u9BAB\u9BAC\uE4AC\u9BAD"+ + "\u9BAE\uB6FD\uD6DE\uE4B2\u9BAF\uE4AD\u9BB0\u9BB1"+ + "\u9BB2\uE4A1\u9BB3\uBBEE\uCDDD\uC7A2\uC5C9\u9BB4"+ + "\u9BB5\uC1F7\u9BB6\uE4A4\u9BB7\uC7B3\uBDAC\uBDBD"+ + "\uE4A5\u9BB8\uD7C7\uB2E2\u9BB9\uE4AB\uBCC3\uE4AF"+ + "\u9BBA\uBBEB\uE4B0\uC5A8\uE4B1\u9BBB\u9BBC\u9BBD"+ + "\u9BBE\uD5E3\uBFA3\u9BBF\uE4BA\u9BC0\uE4B7\u9BC1"+ + "\uE4BB\u9BC2\u9BC3\uE4BD\u9BC4\u9BC5\uC6D6\u9BC6"+ + "\u9BC7\uBAC6\uC0CB\u9BC8\u9BC9\u9BCA\uB8A1\uE4B4"+ + "\u9BCB\u9BCC\u9BCD\u9BCE\uD4A1\u9BCF\u9BD0\uBAA3"+ + "\uBDFE\u9BD1\u9BD2\u9BD3\uE4BC\u9BD4\u9BD5\u9BD6"+ + "\u9BD7\u9BD8\uCDBF\u9BD9\u9BDA\uC4F9\u9BDB\u9BDC"+ + "\uCFFB\uC9E6\u9BDD\u9BDE\uD3BF\u9BDF\uCFD1\u9BE0"+ + "\u9BE1\uE4B3\u9BE2\uE4B8\uE4B9\uCCE9\u9BE3\u9BE4"+ + "\u9BE5\u9BE6\u9BE7\uCCCE\u9BE8\uC0D4\uE4B5\uC1B0"+ + "\uE4B6\uCED0\u9BE9\uBBC1\uB5D3\u9BEA\uC8F3\uBDA7"+ + "\uD5C7\uC9AC\uB8A2\uE4CA\u9BEB\u9BEC\uE4CC\uD1C4"+ + "\u9BED\u9BEE\uD2BA\u9BEF\u9BF0\uBAAD\u9BF1\u9BF2"+ + "\uBAD4\u9BF3\u9BF4\u9BF5\u9BF6\u9BF7\u9BF8\uE4C3"+ + "\uB5ED\u9BF9\u9BFA\u9BFB\uD7CD\uE4C0\uCFFD\uE4BF"+ + "\u9BFC\u9BFD\u9BFE\uC1DC\uCCCA\u9C40\u9C41\u9C42"+ + "\u9C43\uCAE7\u9C44\u9C45\u9C46\u9C47\uC4D7\u9C48"+ + "\uCCD4\uE4C8\u9C49\u9C4A\u9C4B\uE4C7\uE4C1\u9C4C"+ + "\uE4C4\uB5AD\u9C4D\u9C4E\uD3D9\u9C4F\uE4C6\u9C50"+ + "\u9C51\u9C52\u9C53\uD2F9\uB4E3\u9C54\uBBB4\u9C55"+ + "\u9C56\uC9EE\u9C57\uB4BE\u9C58\u9C59\u9C5A\uBBEC"+ + "\u9C5B\uD1CD\u9C5C\uCCED\uEDB5\u9C5D\u9C5E\u9C5F"+ + "\u9C60\u9C61\u9C62\u9C63\u9C64\uC7E5\u9C65\u9C66"+ + "\u9C67\u9C68\uD4A8\u9C69\uE4CB\uD7D5\uE4C2\u9C6A"+ + "\uBDA5\uE4C5\u9C6B\u9C6C\uD3E6\u9C6D\uE4C9\uC9F8"+ + "\u9C6E\u9C6F\uE4BE\u9C70\u9C71\uD3E5\u9C72\u9C73"+ + "\uC7FE\uB6C9\u9C74\uD4FC\uB2B3\uE4D7\u9C75\u9C76"+ + "\u9C77\uCEC2\u9C78\uE4CD\u9C79\uCEBC\u9C7A\uB8DB"+ + "\u9C7B\u9C7C\uE4D6\u9C7D\uBFCA\u9C7E\u9C80\u9C81"+ + "\uD3CE\u9C82\uC3EC\u9C83\u9C84\u9C85\u9C86\u9C87"+ + "\u9C88\u9C89\u9C8A\uC5C8\uE4D8\u9C8B\u9C8C\u9C8D"+ + "\u9C8E\u9C8F\u9C90\u9C91\u9C92\uCDC4\uE4CF\u9C93"+ + "\u9C94\u9C95\u9C96\uE4D4\uE4D5\u9C97\uBAFE\u9C98"+ + "\uCFE6\u9C99\u9C9A\uD5BF\u9C9B\u9C9C\u9C9D\uE4D2"+ + "\u9C9E\u9C9F\u9CA0\u9CA1\u9CA2\u9CA3\u9CA4\u9CA5"+ + "\u9CA6\u9CA7\u9CA8\uE4D0\u9CA9\u9CAA\uE4CE\u9CAB"+ + "\u9CAC\u9CAD\u9CAE\u9CAF\u9CB0\u9CB1\u9CB2\u9CB3"+ + "\u9CB4\u9CB5\u9CB6\u9CB7\u9CB8\u9CB9\uCDE5\uCAAA"+ + "\u9CBA\u9CBB\u9CBC\uC0A3\u9CBD\uBDA6\uE4D3\u9CBE"+ + "\u9CBF\uB8C8\u9CC0\u9CC1\u9CC2\u9CC3\u9CC4\uE4E7"+ + "\uD4B4\u9CC5\u9CC6\u9CC7\u9CC8\u9CC9\u9CCA\u9CCB"+ + "\uE4DB\u9CCC\u9CCD\u9CCE\uC1EF\u9CCF\u9CD0\uE4E9"+ + "\u9CD1\u9CD2\uD2E7\u9CD3\u9CD4\uE4DF\u9CD5\uE4E0"+ + "\u9CD6\u9CD7\uCFAA\u9CD8\u9CD9\u9CDA\u9CDB\uCBDD"+ + "\u9CDC\uE4DA\uE4D1\u9CDD\uE4E5\u9CDE\uC8DC\uE4E3"+ + "\u9CDF\u9CE0\uC4E7\uE4E2\u9CE1\uE4E1\u9CE2\u9CE3"+ + "\u9CE4\uB3FC\uE4E8\u9CE5\u9CE6\u9CE7\u9CE8\uB5E1"+ + "\u9CE9\u9CEA\u9CEB\uD7CC\u9CEC\u9CED\u9CEE\uE4E6"+ + "\u9CEF\uBBAC\u9CF0\uD7D2\uCCCF\uEBF8\u9CF1\uE4E4"+ + "\u9CF2\u9CF3\uB9F6\u9CF4\u9CF5\u9CF6\uD6CD\uE4D9"+ + "\uE4DC\uC2FA\uE4DE\u9CF7\uC2CB\uC0C4\uC2D0\u9CF8"+ + "\uB1F5\uCCB2\u9CF9\u9CFA\u9CFB\u9CFC\u9CFD\u9CFE"+ + "\u9D40\u9D41\u9D42\u9D43\uB5CE\u9D44\u9D45\u9D46"+ + "\u9D47\uE4EF\u9D48\u9D49\u9D4A\u9D4B\u9D4C\u9D4D"+ + "\u9D4E\u9D4F\uC6AF\u9D50\u9D51\u9D52\uC6E1\u9D53"+ + "\u9D54\uE4F5\u9D55\u9D56\u9D57\u9D58\u9D59\uC2A9"+ + "\u9D5A\u9D5B\u9D5C\uC0EC\uD1DD\uE4EE\u9D5D\u9D5E"+ + "\u9D5F\u9D60\u9D61\u9D62\u9D63\u9D64\u9D65\u9D66"+ + "\uC4AE\u9D67\u9D68\u9D69\uE4ED\u9D6A\u9D6B\u9D6C"+ + "\u9D6D\uE4F6\uE4F4\uC2FE\u9D6E\uE4DD\u9D6F\uE4F0"+ + "\u9D70\uCAFE\u9D71\uD5C4\u9D72\u9D73\uE4F1\u9D74"+ + "\u9D75\u9D76\u9D77\u9D78\u9D79\u9D7A\uD1FA\u9D7B"+ + "\u9D7C\u9D7D\u9D7E\u9D80\u9D81\u9D82\uE4EB\uE4EC"+ + "\u9D83\u9D84\u9D85\uE4F2\u9D86\uCEAB\u9D87\u9D88"+ + "\u9D89\u9D8A\u9D8B\u9D8C\u9D8D\u9D8E\u9D8F\u9D90"+ + "\uC5CB\u9D91\u9D92\u9D93\uC7B1\u9D94\uC2BA\u9D95"+ + "\u9D96\u9D97\uE4EA\u9D98\u9D99\u9D9A\uC1CA\u9D9B"+ + "\u9D9C\u9D9D\u9D9E\u9D9F\u9DA0\uCCB6\uB3B1\u9DA1"+ + "\u9DA2\u9DA3\uE4FB\u9DA4\uE4F3\u9DA5\u9DA6\u9DA7"+ + "\uE4FA\u9DA8\uE4FD\u9DA9\uE4FC\u9DAA\u9DAB\u9DAC"+ + "\u9DAD\u9DAE\u9DAF\u9DB0\uB3CE\u9DB1\u9DB2\u9DB3"+ + "\uB3BA\uE4F7\u9DB4\u9DB5\uE4F9\uE4F8\uC5EC\u9DB6"+ + "\u9DB7\u9DB8\u9DB9\u9DBA\u9DBB\u9DBC\u9DBD\u9DBE"+ + "\u9DBF\u9DC0\u9DC1\u9DC2\uC0BD\u9DC3\u9DC4\u9DC5"+ + "\u9DC6\uD4E8\u9DC7\u9DC8\u9DC9\u9DCA\u9DCB\uE5A2"+ + "\u9DCC\u9DCD\u9DCE\u9DCF\u9DD0\u9DD1\u9DD2\u9DD3"+ + "\u9DD4\u9DD5\u9DD6\uB0C4\u9DD7\u9DD8\uE5A4\u9DD9"+ + "\u9DDA\uE5A3\u9DDB\u9DDC\u9DDD\u9DDE\u9DDF\u9DE0"+ + "\uBCA4\u9DE1\uE5A5\u9DE2\u9DE3\u9DE4\u9DE5\u9DE6"+ + "\u9DE7\uE5A1\u9DE8\u9DE9\u9DEA\u9DEB\u9DEC\u9DED"+ + "\u9DEE\uE4FE\uB1F4\u9DEF\u9DF0\u9DF1\u9DF2\u9DF3"+ + "\u9DF4\u9DF5\u9DF6\u9DF7\u9DF8\u9DF9\uE5A8\u9DFA"+ + "\uE5A9\uE5A6\u9DFB\u9DFC\u9DFD\u9DFE\u9E40\u9E41"+ + "\u9E42\u9E43\u9E44\u9E45\u9E46\u9E47\uE5A7\uE5AA"+ + "\u9E48\u9E49\u9E4A\u9E4B\u9E4C\u9E4D\u9E4E\u9E4F"+ + "\u9E50\u9E51\u9E52\u9E53\u9E54\u9E55\u9E56\u9E57"; + + private final static String innerEncoderIndex7= + "\u9E58\u9E59\u9E5A\u9E5B\u9E5C\u9E5D\u9E5E\u9E5F"+ + "\u9E60\u9E61\u9E62\u9E63\u9E64\u9E65\u9E66\u9E67"+ + "\u9E68\uC6D9\u9E69\u9E6A\u9E6B\u9E6C\u9E6D\u9E6E"+ + "\u9E6F\u9E70\uE5AB\uE5AD\u9E71\u9E72\u9E73\u9E74"+ + "\u9E75\u9E76\u9E77\uE5AC\u9E78\u9E79\u9E7A\u9E7B"+ + "\u9E7C\u9E7D\u9E7E\u9E80\u9E81\u9E82\u9E83\u9E84"+ + "\u9E85\u9E86\u9E87\u9E88\u9E89\uE5AF\u9E8A\u9E8B"+ + "\u9E8C\uE5AE\u9E8D\u9E8E\u9E8F\u9E90\u9E91\u9E92"+ + "\u9E93\u9E94\u9E95\u9E96\u9E97\u9E98\u9E99\u9E9A"+ + "\u9E9B\u9E9C\u9E9D\u9E9E\uB9E0\u9E9F\u9EA0\uE5B0"+ + "\u9EA1\u9EA2\u9EA3\u9EA4\u9EA5\u9EA6\u9EA7\u9EA8"+ + "\u9EA9\u9EAA\u9EAB\u9EAC\u9EAD\u9EAE\uE5B1\u9EAF"+ + "\u9EB0\u9EB1\u9EB2\u9EB3\u9EB4\u9EB5\u9EB6\u9EB7"+ + "\u9EB8\u9EB9\u9EBA\uBBF0\uECE1\uC3F0\u9EBB\uB5C6"+ + "\uBBD2\u9EBC\u9EBD\u9EBE\u9EBF\uC1E9\uD4EE\u9EC0"+ + "\uBEC4\u9EC1\u9EC2\u9EC3\uD7C6\u9EC4\uD4D6\uB2D3"+ + "\uECBE\u9EC5\u9EC6\u9EC7\u9EC8\uEAC1\u9EC9\u9ECA"+ + "\u9ECB\uC2AF\uB4B6\u9ECC\u9ECD\u9ECE\uD1D7\u9ECF"+ + "\u9ED0\u9ED1\uB3B4\u9ED2\uC8B2\uBFBB\uECC0\u9ED3"+ + "\u9ED4\uD6CB\u9ED5\u9ED6\uECBF\uECC1\u9ED7\u9ED8"+ + "\u9ED9\u9EDA\u9EDB\u9EDC\u9EDD\u9EDE\u9EDF\u9EE0"+ + "\u9EE1\u9EE2\u9EE3\uECC5\uBEE6\uCCBF\uC5DA\uBEBC"+ + "\u9EE4\uECC6\u9EE5\uB1FE\u9EE6\u9EE7\u9EE8\uECC4"+ + "\uD5A8\uB5E3\u9EE9\uECC2\uC1B6\uB3E3\u9EEA\u9EEB"+ + "\uECC3\uCBB8\uC0C3\uCCFE\u9EEC\u9EED\u9EEE\u9EEF"+ + "\uC1D2\u9EF0\uECC8\u9EF1\u9EF2\u9EF3\u9EF4\u9EF5"+ + "\u9EF6\u9EF7\u9EF8\u9EF9\u9EFA\u9EFB\u9EFC\u9EFD"+ + "\uBAE6\uC0D3\u9EFE\uD6F2\u9F40\u9F41\u9F42\uD1CC"+ + "\u9F43\u9F44\u9F45\u9F46\uBFBE\u9F47\uB7B3\uC9D5"+ + "\uECC7\uBBE2\u9F48\uCCCC\uBDFD\uC8C8\u9F49\uCFA9"+ + "\u9F4A\u9F4B\u9F4C\u9F4D\u9F4E\u9F4F\u9F50\uCDE9"+ + "\u9F51\uC5EB\u9F52\u9F53\u9F54\uB7E9\u9F55\u9F56"+ + "\u9F57\u9F58\u9F59\u9F5A\u9F5B\u9F5C\u9F5D\u9F5E"+ + "\u9F5F\uD1C9\uBAB8\u9F60\u9F61\u9F62\u9F63\u9F64"+ + "\uECC9\u9F65\u9F66\uECCA\u9F67\uBBC0\uECCB\u9F68"+ + "\uECE2\uB1BA\uB7D9\u9F69\u9F6A\u9F6B\u9F6C\u9F6D"+ + "\u9F6E\u9F6F\u9F70\u9F71\u9F72\u9F73\uBDB9\u9F74"+ + "\u9F75\u9F76\u9F77\u9F78\u9F79\u9F7A\u9F7B\uECCC"+ + "\uD1E6\uECCD\u9F7C\u9F7D\u9F7E\u9F80\uC8BB\u9F81"+ + "\u9F82\u9F83\u9F84\u9F85\u9F86\u9F87\u9F88\u9F89"+ + "\u9F8A\u9F8B\u9F8C\u9F8D\u9F8E\uECD1\u9F8F\u9F90"+ + "\u9F91\u9F92\uECD3\u9F93\uBBCD\u9F94\uBCE5\u9F95"+ + "\u9F96\u9F97\u9F98\u9F99\u9F9A\u9F9B\u9F9C\u9F9D"+ + "\u9F9E\u9F9F\u9FA0\u9FA1\uECCF\u9FA2\uC9B7\u9FA3"+ + "\u9FA4\u9FA5\u9FA6\u9FA7\uC3BA\u9FA8\uECE3\uD5D5"+ + "\uECD0\u9FA9\u9FAA\u9FAB\u9FAC\u9FAD\uD6F3\u9FAE"+ + "\u9FAF\u9FB0\uECD2\uECCE\u9FB1\u9FB2\u9FB3\u9FB4"+ + "\uECD4\u9FB5\uECD5\u9FB6\u9FB7\uC9BF\u9FB8\u9FB9"+ + "\u9FBA\u9FBB\u9FBC\u9FBD\uCFA8\u9FBE\u9FBF\u9FC0"+ + "\u9FC1\u9FC2\uD0DC\u9FC3\u9FC4\u9FC5\u9FC6\uD1AC"+ + "\u9FC7\u9FC8\u9FC9\u9FCA\uC8DB\u9FCB\u9FCC\u9FCD"+ + "\uECD6\uCEF5\u9FCE\u9FCF\u9FD0\u9FD1\u9FD2\uCAEC"+ + "\uECDA\u9FD3\u9FD4\u9FD5\u9FD6\u9FD7\u9FD8\u9FD9"+ + "\uECD9\u9FDA\u9FDB\u9FDC\uB0BE\u9FDD\u9FDE\u9FDF"+ + "\u9FE0\u9FE1\u9FE2\uECD7\u9FE3\uECD8\u9FE4\u9FE5"+ + "\u9FE6\uECE4\u9FE7\u9FE8\u9FE9\u9FEA\u9FEB\u9FEC"+ + "\u9FED\u9FEE\u9FEF\uC8BC\u9FF0\u9FF1\u9FF2\u9FF3"+ + "\u9FF4\u9FF5\u9FF6\u9FF7\u9FF8\u9FF9\uC1C7\u9FFA"+ + "\u9FFB\u9FFC\u9FFD\u9FFE\uECDC\uD1E0\uA040\uA041"+ + "\uA042\uA043\uA044\uA045\uA046\uA047\uA048\uA049"+ + "\uECDB\uA04A\uA04B\uA04C\uA04D\uD4EF\uA04E\uECDD"+ + "\uA04F\uA050\uA051\uA052\uA053\uA054\uDBC6\uA055"+ + "\uA056\uA057\uA058\uA059\uA05A\uA05B\uA05C\uA05D"+ + "\uA05E\uECDE\uA05F\uA060\uA061\uA062\uA063\uA064"+ + "\uA065\uA066\uA067\uA068\uA069\uA06A\uB1AC\uA06B"+ + "\uA06C\uA06D\uA06E\uA06F\uA070\uA071\uA072\uA073"+ + "\uA074\uA075\uA076\uA077\uA078\uA079\uA07A\uA07B"+ + "\uA07C\uA07D\uA07E\uA080\uA081\uECDF\uA082\uA083"+ + "\uA084\uA085\uA086\uA087\uA088\uA089\uA08A\uA08B"+ + "\uECE0\uA08C\uD7A6\uA08D\uC5C0\uA08E\uA08F\uA090"+ + "\uEBBC\uB0AE\uA091\uA092\uA093\uBEF4\uB8B8\uD2AF"+ + "\uB0D6\uB5F9\uA094\uD8B3\uA095\uCBAC\uA096\uE3DD"+ + "\uA097\uA098\uA099\uA09A\uA09B\uA09C\uA09D\uC6AC"+ + "\uB0E6\uA09E\uA09F\uA0A0\uC5C6\uEBB9\uA0A1\uA0A2"+ + "\uA0A3\uA0A4\uEBBA\uA0A5\uA0A6\uA0A7\uEBBB\uA0A8"+ + "\uA0A9\uD1C0\uA0AA\uC5A3\uA0AB\uEAF2\uA0AC\uC4B2"+ + "\uA0AD\uC4B5\uC0CE\uA0AE\uA0AF\uA0B0\uEAF3\uC4C1"+ + "\uA0B1\uCEEF\uA0B2\uA0B3\uA0B4\uA0B5\uEAF0\uEAF4"+ + "\uA0B6\uA0B7\uC9FC\uA0B8\uA0B9\uC7A3\uA0BA\uA0BB"+ + "\uA0BC\uCCD8\uCEFE\uA0BD\uA0BE\uA0BF\uEAF5\uEAF6"+ + "\uCFAC\uC0E7\uA0C0\uA0C1\uEAF7\uA0C2\uA0C3\uA0C4"+ + "\uA0C5\uA0C6\uB6BF\uEAF8\uA0C7\uEAF9\uA0C8\uEAFA"+ + "\uA0C9\uA0CA\uEAFB\uA0CB\uA0CC\uA0CD\uA0CE\uA0CF"+ + "\uA0D0\uA0D1\uA0D2\uA0D3\uA0D4\uA0D5\uA0D6\uEAF1"+ + "\uA0D7\uA0D8\uA0D9\uA0DA\uA0DB\uA0DC\uA0DD\uA0DE"+ + "\uA0DF\uA0E0\uA0E1\uA0E2\uC8AE\uE1EB\uA0E3\uB7B8"+ + "\uE1EC\uA0E4\uA0E5\uA0E6\uE1ED\uA0E7\uD7B4\uE1EE"+ + "\uE1EF\uD3CC\uA0E8\uA0E9\uA0EA\uA0EB\uA0EC\uA0ED"+ + "\uA0EE\uE1F1\uBFF1\uE1F0\uB5D2\uA0EF\uA0F0\uA0F1"+ + "\uB1B7\uA0F2\uA0F3\uA0F4\uA0F5\uE1F3\uE1F2\uA0F6"+ + "\uBAFC\uA0F7\uE1F4\uA0F8\uA0F9\uA0FA\uA0FB\uB9B7"+ + "\uA0FC\uBED1\uA0FD\uA0FE\uAA40\uAA41\uC4FC\uAA42"+ + "\uBADD\uBDC6\uAA43\uAA44\uAA45\uAA46\uAA47\uAA48"+ + "\uE1F5\uE1F7\uAA49\uAA4A\uB6C0\uCFC1\uCAA8\uE1F6"+ + "\uD5F8\uD3FC\uE1F8\uE1FC\uE1F9\uAA4B\uAA4C\uE1FA"+ + "\uC0EA\uAA4D\uE1FE\uE2A1\uC0C7\uAA4E\uAA4F\uAA50"+ + "\uAA51\uE1FB\uAA52\uE1FD\uAA53\uAA54\uAA55\uAA56"+ + "\uAA57\uAA58\uE2A5\uAA59\uAA5A\uAA5B\uC1D4\uAA5C"+ + "\uAA5D\uAA5E\uAA5F\uE2A3\uAA60\uE2A8\uB2FE\uE2A2"+ + "\uAA61\uAA62\uAA63\uC3CD\uB2C2\uE2A7\uE2A6\uAA64"+ + "\uAA65\uE2A4\uE2A9\uAA66\uAA67\uE2AB\uAA68\uAA69"+ + "\uAA6A\uD0C9\uD6ED\uC3A8\uE2AC\uAA6B\uCFD7\uAA6C"+ + "\uAA6D\uE2AE\uAA6E\uAA6F\uBAEF\uAA70\uAA71\uE9E0"+ + "\uE2AD\uE2AA\uAA72\uAA73\uAA74\uAA75\uBBAB\uD4B3"+ + "\uAA76\uAA77\uAA78\uAA79\uAA7A\uAA7B\uAA7C\uAA7D"+ + "\uAA7E\uAA80\uAA81\uAA82\uAA83\uE2B0\uAA84\uAA85"+ + "\uE2AF\uAA86\uE9E1\uAA87\uAA88\uAA89\uAA8A\uE2B1"+ + "\uAA8B\uAA8C\uAA8D\uAA8E\uAA8F\uAA90\uAA91\uAA92"+ + "\uE2B2\uAA93\uAA94\uAA95\uAA96\uAA97\uAA98\uAA99"+ + "\uAA9A\uAA9B\uAA9C\uAA9D\uE2B3\uCCA1\uAA9E\uE2B4"+ + "\uAA9F\uAAA0\uAB40\uAB41\uAB42\uAB43\uAB44\uAB45"+ + "\uAB46\uAB47\uAB48\uAB49\uAB4A\uAB4B\uE2B5\uAB4C"+ + "\uAB4D\uAB4E\uAB4F\uAB50\uD0FE\uAB51\uAB52\uC2CA"+ + "\uAB53\uD3F1\uAB54\uCDF5\uAB55\uAB56\uE7E0\uAB57"+ + "\uAB58\uE7E1\uAB59\uAB5A\uAB5B\uAB5C\uBEC1\uAB5D"+ + "\uAB5E\uAB5F\uAB60\uC2EA\uAB61\uAB62\uAB63\uE7E4"+ + "\uAB64\uAB65\uE7E3\uAB66\uAB67\uAB68\uAB69\uAB6A"+ + "\uAB6B\uCDE6\uAB6C\uC3B5\uAB6D\uAB6E\uE7E2\uBBB7"+ + "\uCFD6\uAB6F\uC1E1\uE7E9\uAB70\uAB71\uAB72\uE7E8"+ + "\uAB73\uAB74\uE7F4\uB2A3\uAB75\uAB76\uAB77\uAB78"+ + "\uE7EA\uAB79\uE7E6\uAB7A\uAB7B\uAB7C\uAB7D\uAB7E"+ + "\uE7EC\uE7EB\uC9BA\uAB80\uAB81\uD5E4\uAB82\uE7E5"+ + "\uB7A9\uE7E7\uAB83\uAB84\uAB85\uAB86\uAB87\uAB88"+ + "\uAB89\uE7EE\uAB8A\uAB8B\uAB8C\uAB8D\uE7F3\uAB8E"+ + "\uD6E9\uAB8F\uAB90\uAB91\uAB92\uE7ED\uAB93\uE7F2"+ + "\uAB94\uE7F1\uAB95\uAB96\uAB97\uB0E0\uAB98\uAB99"+ + "\uAB9A\uAB9B\uE7F5\uAB9C\uAB9D\uAB9E\uAB9F\uABA0"+ + "\uAC40\uAC41\uAC42\uAC43\uAC44\uAC45\uAC46\uAC47"+ + "\uAC48\uAC49\uAC4A\uC7F2\uAC4B\uC0C5\uC0ED\uAC4C"+ + "\uAC4D\uC1F0\uE7F0\uAC4E\uAC4F\uAC50\uAC51\uE7F6"+ + "\uCBF6\uAC52\uAC53\uAC54\uAC55\uAC56\uAC57\uAC58"+ + "\uAC59\uAC5A\uE8A2\uE8A1\uAC5B\uAC5C\uAC5D\uAC5E"+ + "\uAC5F\uAC60\uD7C1\uAC61\uAC62\uE7FA\uE7F9\uAC63"+ + "\uE7FB\uAC64\uE7F7\uAC65\uE7FE\uAC66\uE7FD\uAC67"+ + "\uE7FC\uAC68\uAC69\uC1D5\uC7D9\uC5FD\uC5C3\uAC6A"+ + "\uAC6B\uAC6C\uAC6D\uAC6E\uC7ED\uAC6F\uAC70\uAC71"+ + "\uAC72\uE8A3\uAC73\uAC74\uAC75\uAC76\uAC77\uAC78"+ + "\uAC79\uAC7A\uAC7B\uAC7C\uAC7D\uAC7E\uAC80\uAC81"+ + "\uAC82\uAC83\uAC84\uAC85\uAC86\uE8A6\uAC87\uE8A5"+ + "\uAC88\uE8A7\uBAF7\uE7F8\uE8A4\uAC89\uC8F0\uC9AA"+ + "\uAC8A\uAC8B\uAC8C\uAC8D\uAC8E\uAC8F\uAC90\uAC91"+ + "\uAC92\uAC93\uAC94\uAC95\uAC96\uE8A9\uAC97\uAC98"+ + "\uB9E5\uAC99\uAC9A\uAC9B\uAC9C\uAC9D\uD1FE\uE8A8"+ + "\uAC9E\uAC9F\uACA0\uAD40\uAD41\uAD42\uE8AA\uAD43"+ + "\uE8AD\uE8AE\uAD44\uC1A7\uAD45\uAD46\uAD47\uE8AF"+ + "\uAD48\uAD49\uAD4A\uE8B0\uAD4B\uAD4C\uE8AC\uAD4D"+ + "\uE8B4\uAD4E\uAD4F\uAD50\uAD51\uAD52\uAD53\uAD54"+ + "\uAD55\uAD56\uAD57\uAD58\uE8AB\uAD59\uE8B1\uAD5A"+ + "\uAD5B\uAD5C\uAD5D\uAD5E\uAD5F\uAD60\uAD61\uE8B5"+ + "\uE8B2\uE8B3\uAD62\uAD63\uAD64\uAD65\uAD66\uAD67"+ + "\uAD68\uAD69\uAD6A\uAD6B\uAD6C\uAD6D\uAD6E\uAD6F"+ + "\uAD70\uAD71\uE8B7\uAD72\uAD73\uAD74\uAD75\uAD76"+ + "\uAD77\uAD78\uAD79\uAD7A\uAD7B\uAD7C\uAD7D\uAD7E"+ + "\uAD80\uAD81\uAD82\uAD83\uAD84\uAD85\uAD86\uAD87"+ + "\uAD88\uAD89\uE8B6\uAD8A\uAD8B\uAD8C\uAD8D\uAD8E"+ + "\uAD8F\uAD90\uAD91\uAD92\uB9CF\uAD93\uF0AC\uAD94"+ + "\uF0AD\uAD95\uC6B0\uB0EA\uC8BF\uAD96\uCDDF\uAD97"+ + "\uAD98\uAD99\uAD9A\uAD9B\uAD9C\uAD9D\uCECD\uEAB1"+ + "\uAD9E\uAD9F\uADA0\uAE40\uEAB2\uAE41\uC6BF\uB4C9"+ + "\uAE42\uAE43\uAE44\uAE45\uAE46\uAE47\uAE48\uEAB3"+ + "\uAE49\uAE4A\uAE4B\uAE4C\uD5E7\uAE4D\uAE4E\uAE4F"+ + "\uAE50\uAE51\uAE52\uAE53\uAE54\uDDF9\uAE55\uEAB4"+ + "\uAE56\uEAB5\uAE57\uEAB6\uAE58\uAE59\uAE5A\uAE5B"+ + "\uB8CA\uDFB0\uC9F5\uAE5C\uCCF0\uAE5D\uAE5E\uC9FA"+ + "\uAE5F\uAE60\uAE61\uAE62\uAE63\uC9FB\uAE64\uAE65"+ + "\uD3C3\uCBA6\uAE66\uB8A6\uF0AE\uB1C2\uAE67\uE5B8"+ + "\uCCEF\uD3C9\uBCD7\uC9EA\uAE68\uB5E7\uAE69\uC4D0"+ + "\uB5E9\uAE6A\uEEAE\uBBAD\uAE6B\uAE6C\uE7DE\uAE6D"+ + "\uEEAF\uAE6E\uAE6F\uAE70\uAE71\uB3A9\uAE72\uAE73"+ + "\uEEB2\uAE74\uAE75\uEEB1\uBDE7\uAE76\uEEB0\uCEB7"+ + "\uAE77\uAE78\uAE79\uAE7A\uC5CF\uAE7B\uAE7C\uAE7D"+ + "\uAE7E\uC1F4\uDBCE\uEEB3\uD0F3\uAE80\uAE81\uAE82"+ + "\uAE83\uAE84\uAE85\uAE86\uAE87\uC2D4\uC6E8\uAE88"+ + "\uAE89\uAE8A\uB7AC\uAE8B\uAE8C\uAE8D\uAE8E\uAE8F"+ + "\uAE90\uAE91\uEEB4\uAE92\uB3EB\uAE93\uAE94\uAE95"+ + "\uBBFB\uEEB5\uAE96\uAE97\uAE98\uAE99\uAE9A\uE7DC"+ + "\uAE9B\uAE9C\uAE9D\uEEB6\uAE9E\uAE9F\uBDAE\uAEA0"+ + "\uAF40\uAF41\uAF42\uF1E2\uAF43\uAF44\uAF45\uCAE8"+ + "\uAF46\uD2C9\uF0DA\uAF47\uF0DB\uAF48\uF0DC\uC1C6"+ + "\uAF49\uB8ED\uBECE\uAF4A\uAF4B\uF0DE\uAF4C\uC5B1"+ + "\uF0DD\uD1F1\uAF4D\uF0E0\uB0CC\uBDEA\uAF4E\uAF4F"+ + "\uAF50\uAF51\uAF52\uD2DF\uF0DF\uAF53\uB4AF\uB7E8"+ + "\uF0E6\uF0E5\uC6A3\uF0E1\uF0E2\uB4C3\uAF54\uAF55"+ + "\uF0E3\uD5EE\uAF56\uAF57\uCCDB\uBED2\uBCB2\uAF58"+ + "\uAF59\uAF5A\uF0E8\uF0E7\uF0E4\uB2A1\uAF5B\uD6A2"+ + "\uD3B8\uBEB7\uC8AC\uAF5C\uAF5D\uF0EA\uAF5E\uAF5F"+ + "\uAF60\uAF61\uD1F7\uAF62\uD6CC\uBADB\uF0E9\uAF63"+ + "\uB6BB\uAF64\uAF65\uCDB4\uAF66\uAF67\uC6A6\uAF68"+ + "\uAF69\uAF6A\uC1A1\uF0EB\uF0EE\uAF6B\uF0ED\uF0F0"+ + "\uF0EC\uAF6C\uBBBE\uF0EF\uAF6D\uAF6E\uAF6F\uAF70"+ + "\uCCB5\uF0F2\uAF71\uAF72\uB3D5\uAF73\uAF74\uAF75"+ + "\uAF76\uB1D4\uAF77\uAF78\uF0F3\uAF79\uAF7A\uF0F4"+ + "\uF0F6\uB4E1\uAF7B\uF0F1\uAF7C\uF0F7\uAF7D\uAF7E"+ + "\uAF80\uAF81\uF0FA\uAF82\uF0F8\uAF83\uAF84\uAF85"+ + "\uF0F5\uAF86\uAF87\uAF88\uAF89\uF0FD\uAF8A\uF0F9"+ + "\uF0FC\uF0FE\uAF8B\uF1A1\uAF8C\uAF8D\uAF8E\uCEC1"+ + "\uF1A4\uAF8F\uF1A3\uAF90\uC1F6\uF0FB\uCADD\uAF91"+ + "\uAF92\uB4F1\uB1F1\uCCB1\uAF93\uF1A6\uAF94\uAF95"+ + "\uF1A7\uAF96\uAF97\uF1AC\uD5CE\uF1A9\uAF98\uAF99"+ + "\uC8B3\uAF9A\uAF9B\uAF9C\uF1A2\uAF9D\uF1AB\uF1A8"+ + "\uF1A5\uAF9E\uAF9F\uF1AA\uAFA0\uB040\uB041\uB042"+ + "\uB043\uB044\uB045\uB046\uB0A9\uF1AD\uB047\uB048"+ + "\uB049\uB04A\uB04B\uB04C\uF1AF\uB04D\uF1B1\uB04E"+ + "\uB04F\uB050\uB051\uB052\uF1B0\uB053\uF1AE\uB054"+ + "\uB055\uB056\uB057\uD1A2\uB058\uB059\uB05A\uB05B"+ + "\uB05C\uB05D\uB05E\uF1B2\uB05F\uB060\uB061\uF1B3"+ + "\uB062\uB063\uB064\uB065\uB066\uB067\uB068\uB069"+ + "\uB9EF\uB06A\uB06B\uB5C7\uB06C\uB0D7\uB0D9\uB06D"+ + "\uB06E\uB06F\uD4ED\uB070\uB5C4\uB071\uBDD4\uBBCA"+ + "\uF0A7\uB072\uB073\uB8DE\uB074\uB075\uF0A8\uB076"+ + "\uB077\uB0A8\uB078\uF0A9\uB079\uB07A\uCDEE\uB07B"+ + "\uB07C\uF0AA\uB07D\uB07E\uB080\uB081\uB082\uB083"+ + "\uB084\uB085\uB086\uB087\uF0AB\uB088\uB089\uB08A"+ + "\uB08B\uB08C\uB08D\uB08E\uB08F\uB090\uC6A4\uB091"+ + "\uB092\uD6E5\uF1E4\uB093\uF1E5\uB094\uB095\uB096"+ + "\uB097\uB098\uB099\uB09A\uB09B\uB09C\uB09D\uC3F3"+ + "\uB09E\uB09F\uD3DB\uB0A0\uB140\uD6D1\uC5E8\uB141"+ + "\uD3AF\uB142\uD2E6\uB143\uB144\uEEC1\uB0BB\uD5B5"+ + "\uD1CE\uBCE0\uBAD0\uB145\uBFF8\uB146\uB8C7\uB5C1"+ + "\uC5CC\uB147\uB148\uCAA2\uB149\uB14A\uB14B\uC3CB"+ + "\uB14C\uB14D\uB14E\uB14F\uB150\uEEC2\uB151\uB152"+ + "\uB153\uB154\uB155\uB156\uB157\uB158\uC4BF\uB6A2"+ + "\uB159\uEDEC\uC3A4\uB15A\uD6B1\uB15B\uB15C\uB15D"+ + "\uCFE0\uEDEF\uB15E\uB15F\uC5CE\uB160\uB6DC\uB161"+ + "\uB162\uCAA1\uB163\uB164\uEDED\uB165\uB166\uEDF0"+ + "\uEDF1\uC3BC\uB167\uBFB4\uB168\uEDEE\uB169\uB16A"+ + "\uB16B\uB16C\uB16D\uB16E\uB16F\uB170\uB171\uB172"+ + "\uB173\uEDF4\uEDF2\uB174\uB175\uB176\uB177\uD5E6"+ + "\uC3DF\uB178\uEDF3\uB179\uB17A\uB17B\uEDF6\uB17C"+ + "\uD5A3\uD1A3\uB17D\uB17E\uB180\uEDF5\uB181\uC3D0"+ + "\uB182\uB183\uB184\uB185\uB186\uEDF7\uBFF4\uBEEC"+ + "\uEDF8\uB187\uCCF7\uB188\uD1DB\uB189\uB18A\uB18B"+ + "\uD7C5\uD5F6\uB18C\uEDFC\uB18D\uB18E\uB18F\uEDFB"+ + "\uB190\uB191\uB192\uB193\uB194\uB195\uB196\uB197"+ + "\uEDF9\uEDFA\uB198\uB199\uB19A\uB19B\uB19C\uB19D"+ + "\uB19E\uB19F\uEDFD\uBEA6\uB1A0\uB240\uB241\uB242"+ + "\uB243\uCBAF\uEEA1\uB6BD\uB244\uEEA2\uC4C0\uB245"+ + "\uEDFE\uB246\uB247\uBDDE\uB2C7\uB248\uB249\uB24A"+ + "\uB24B\uB24C\uB24D\uB24E\uB24F\uB250\uB251\uB252"+ + "\uB253\uB6C3\uB254\uB255\uB256\uEEA5\uD8BA\uEEA3"+ + "\uEEA6\uB257\uB258\uB259\uC3E9\uB3F2\uB25A\uB25B"+ + "\uB25C\uB25D\uB25E\uB25F\uEEA7\uEEA4\uCFB9\uB260"+ + "\uB261\uEEA8\uC2F7\uB262\uB263\uB264\uB265\uB266"+ + "\uB267\uB268\uB269\uB26A\uB26B\uB26C\uB26D\uEEA9"+ + "\uEEAA\uB26E\uDEAB\uB26F\uB270\uC6B3\uB271\uC7C6"+ + "\uB272\uD6F5\uB5C9\uB273\uCBB2\uB274\uB275\uB276"+ + "\uEEAB\uB277\uB278\uCDAB\uB279\uEEAC\uB27A\uB27B"+ + "\uB27C\uB27D\uB27E\uD5B0\uB280\uEEAD\uB281\uF6C4"+ + "\uB282\uB283\uB284\uB285\uB286\uB287\uB288\uB289"+ + "\uB28A\uB28B\uB28C\uB28D\uB28E\uDBC7\uB28F\uB290"+ + "\uB291\uB292\uB293\uB294\uB295\uB296\uB297\uB4A3"+ + "\uB298\uB299\uB29A\uC3AC\uF1E6\uB29B\uB29C\uB29D"+ + "\uB29E\uB29F\uCAB8\uD2D3\uB2A0\uD6AA\uB340\uEFF2"+ + "\uB341\uBED8\uB342\uBDC3\uEFF3\uB6CC\uB0AB\uB343"+ + "\uB344\uB345\uB346\uCAAF\uB347\uB348\uEDB6\uB349"+ + "\uEDB7\uB34A\uB34B\uB34C\uB34D\uCEF9\uB7AF\uBFF3"+ + "\uEDB8\uC2EB\uC9B0\uB34E\uB34F\uB350\uB351\uB352"+ + "\uB353\uEDB9\uB354\uB355\uC6F6\uBFB3\uB356\uB357"+ + "\uB358\uEDBC\uC5F8\uB359\uD1D0\uB35A\uD7A9\uEDBA"+ + "\uEDBB\uB35B\uD1E2\uB35C\uEDBF\uEDC0\uB35D\uEDC4"+ + "\uB35E\uB35F\uB360\uEDC8\uB361\uEDC6\uEDCE\uD5E8"+ + "\uB362\uEDC9\uB363\uB364\uEDC7\uEDBE\uB365\uB366"+ + "\uC5E9\uB367\uB368\uB369\uC6C6\uB36A\uB36B\uC9E9"+ + "\uD4D2\uEDC1\uEDC2\uEDC3\uEDC5\uB36C\uC0F9\uB36D"+ + "\uB4A1\uB36E\uB36F\uB370\uB371\uB9E8\uB372\uEDD0"+ + "\uB373\uB374\uB375\uB376\uEDD1\uB377\uEDCA\uB378"+ + "\uEDCF\uB379\uCEF8\uB37A\uB37B\uCBB6\uEDCC\uEDCD"+ + "\uB37C\uB37D\uB37E\uB380\uB381\uCFF5\uB382\uB383"+ + "\uB384\uB385\uB386\uB387\uB388\uB389\uB38A\uB38B"+ + "\uB38C\uB38D\uEDD2\uC1F2\uD3B2\uEDCB\uC8B7\uB38E"+ + "\uB38F\uB390\uB391\uB392\uB393\uB394\uB395\uBCEF"+ + "\uB396\uB397\uB398\uB399\uC5F0\uB39A\uB39B\uB39C"+ + "\uB39D\uB39E\uB39F\uB3A0\uB440\uB441\uB442\uEDD6"+ + "\uB443\uB5EF\uB444\uB445\uC2B5\uB0AD\uCBE9\uB446"+ + "\uB447\uB1AE\uB448\uEDD4\uB449\uB44A\uB44B\uCDEB"+ + "\uB5E2\uB44C\uEDD5\uEDD3\uEDD7\uB44D\uB44E\uB5FA"+ + "\uB44F\uEDD8\uB450\uEDD9\uB451\uEDDC\uB452\uB1CC"+ + "\uB453\uB454\uB455\uB456\uB457\uB458\uB459\uB45A"+ + "\uC5F6\uBCEE\uEDDA\uCCBC\uB2EA\uB45B\uB45C\uB45D"+ + "\uB45E\uEDDB\uB45F\uB460\uB461\uB462\uC4EB\uB463"+ + "\uB464\uB4C5\uB465\uB466\uB467\uB0F5\uB468\uB469"+ + "\uB46A\uEDDF\uC0DA\uB4E8\uB46B\uB46C\uB46D\uB46E"+ + "\uC5CD\uB46F\uB470\uB471\uEDDD\uBFC4\uB472\uB473"+ + "\uB474\uEDDE\uB475\uB476\uB477\uB478\uB479\uB47A"+ + "\uB47B\uB47C\uB47D\uB47E\uB480\uB481\uB482\uB483"+ + "\uC4A5\uB484\uB485\uB486\uEDE0\uB487\uB488\uB489"+ + "\uB48A\uB48B\uEDE1\uB48C\uEDE3\uB48D\uB48E\uC1D7"+ + "\uB48F\uB490\uBBC7\uB491\uB492\uB493\uB494\uB495"+ + "\uB496\uBDB8\uB497\uB498\uB499\uEDE2\uB49A\uB49B"+ + "\uB49C\uB49D\uB49E\uB49F\uB4A0\uB540\uB541\uB542"+ + "\uB543\uB544\uB545\uEDE4\uB546\uB547\uB548\uB549"+ + "\uB54A\uB54B\uB54C\uB54D\uB54E\uB54F\uEDE6\uB550"+ + "\uB551\uB552\uB553\uB554\uEDE5\uB555\uB556\uB557"+ + "\uB558\uB559\uB55A\uB55B\uB55C\uB55D\uB55E\uB55F"+ + "\uB560\uB561\uB562\uB563\uEDE7\uB564\uB565\uB566"+ + "\uB567\uB568\uCABE\uECEA\uC0F1\uB569\uC9E7\uB56A"+ + "\uECEB\uC6EE\uB56B\uB56C\uB56D\uB56E\uECEC\uB56F"+ + "\uC6ED\uECED\uB570\uB571\uB572\uB573\uB574\uB575"+ + "\uB576\uB577\uB578\uECF0\uB579\uB57A\uD7E6\uECF3"+ + "\uB57B\uB57C\uECF1\uECEE\uECEF\uD7A3\uC9F1\uCBEE"+ + "\uECF4\uB57D\uECF2\uB57E\uB580\uCFE9\uB581\uECF6"+ + "\uC6B1\uB582\uB583\uB584\uB585\uBCC0\uB586\uECF5"+ + "\uB587\uB588\uB589\uB58A\uB58B\uB58C\uB58D\uB5BB"+ + "\uBBF6\uB58E\uECF7\uB58F\uB590\uB591\uB592\uB593"+ + "\uD9F7\uBDFB\uB594\uB595\uC2BB\uECF8\uB596\uB597"+ + "\uB598\uB599\uECF9\uB59A\uB59B\uB59C\uB59D\uB8A3"+ + "\uB59E\uB59F\uB5A0\uB640\uB641\uB642\uB643\uB644"+ + "\uB645\uB646\uECFA\uB647\uB648\uB649\uB64A\uB64B"+ + "\uB64C\uB64D\uB64E\uB64F\uB650\uB651\uB652\uECFB"+ + "\uB653\uB654\uB655\uB656\uB657\uB658\uB659\uB65A"+ + "\uB65B\uB65C\uB65D\uECFC\uB65E\uB65F\uB660\uB661"+ + "\uB662\uD3ED\uD8AE\uC0EB\uB663\uC7DD\uBACC\uB664"+ + "\uD0E3\uCBBD\uB665\uCDBA\uB666\uB667\uB8D1\uB668"+ + "\uB669\uB1FC\uB66A\uC7EF\uB66B\uD6D6\uB66C\uB66D"+ + "\uB66E\uBFC6\uC3EB\uB66F\uB670\uEFF5\uB671\uB672"+ + "\uC3D8\uB673\uB674\uB675\uB676\uB677\uB678\uD7E2"+ + "\uB679\uB67A\uB67B\uEFF7\uB3D3\uB67C\uC7D8\uD1ED"+ + "\uB67D\uD6C8\uB67E\uEFF8\uB680\uEFF6\uB681\uBBFD"+ + "\uB3C6\uB682\uB683\uB684\uB685\uB686\uB687\uB688"+ + "\uBDD5\uB689\uB68A\uD2C6\uB68B\uBBE0\uB68C\uB68D"+ + "\uCFA1\uB68E\uEFFC\uEFFB\uB68F\uB690\uEFF9\uB691"+ + "\uB692\uB693\uB694\uB3CC\uB695\uC9D4\uCBB0\uB696"+ + "\uB697\uB698\uB699\uB69A\uEFFE\uB69B\uB69C\uB0DE"+ + "\uB69D\uB69E\uD6C9\uB69F\uB6A0\uB740\uEFFD\uB741"+ + "\uB3ED\uB742\uB743\uF6D5\uB744\uB745\uB746\uB747"+ + "\uB748\uB749\uB74A\uB74B\uB74C\uB74D\uB74E\uB74F"+ + "\uB750\uB751\uB752\uCEC8\uB753\uB754\uB755\uF0A2"+ + "\uB756\uF0A1\uB757\uB5BE\uBCDA\uBBFC\uB758\uB8E5"+ + "\uB759\uB75A\uB75B\uB75C\uB75D\uB75E\uC4C2\uB75F"+ + "\uB760\uB761\uB762\uB763\uB764\uB765\uB766\uB767"+ + "\uB768\uF0A3\uB769\uB76A\uB76B\uB76C\uB76D\uCBEB"+ + "\uB76E\uB76F\uB770\uB771\uB772\uB773\uB774\uB775"+ + "\uB776\uB777\uB778\uB779\uB77A\uB77B\uB77C\uB77D"+ + "\uB77E\uB780\uB781\uB782\uB783\uB784\uB785\uB786"+ + "\uF0A6\uB787\uB788\uB789\uD1A8\uB78A\uBEBF\uC7EE"+ + "\uF1B6\uF1B7\uBFD5\uB78B\uB78C\uB78D\uB78E\uB4A9"+ + "\uF1B8\uCDBB\uB78F\uC7D4\uD5AD\uB790\uF1B9\uB791"+ + "\uF1BA\uB792\uB793\uB794\uB795\uC7CF\uB796\uB797"+ + "\uB798\uD2A4\uD6CF\uB799\uB79A\uF1BB\uBDD1\uB4B0"+ + "\uBEBD\uB79B\uB79C\uB79D\uB4DC\uCED1\uB79E\uBFDF"+ + "\uF1BD\uB79F\uB7A0\uB840\uB841\uBFFA\uF1BC\uB842"+ + "\uF1BF\uB843\uB844\uB845\uF1BE\uF1C0\uB846\uB847"+ + "\uB848\uB849\uB84A\uF1C1\uB84B\uB84C\uB84D\uB84E"+ + "\uB84F\uB850\uB851\uB852\uB853\uB854\uB855\uC1FE"+ + "\uB856\uB857\uB858\uB859\uB85A\uB85B\uB85C\uB85D"+ + "\uB85E\uB85F\uB860\uC1A2\uB861\uB862\uB863\uB864"+ + "\uB865\uB866\uB867\uB868\uB869\uB86A\uCAFA\uB86B"+ + "\uB86C\uD5BE\uB86D\uB86E\uB86F\uB870\uBEBA\uBEB9"+ + "\uD5C2\uB871\uB872\uBFA2\uB873\uCDAF\uF1B5\uB874"+ + "\uB875\uB876\uB877\uB878\uB879\uBDDF\uB87A\uB6CB"+ + "\uB87B\uB87C\uB87D\uB87E\uB880\uB881\uB882\uB883"+ + "\uB884\uD6F1\uF3C3\uB885\uB886\uF3C4\uB887\uB8CD"+ + "\uB888\uB889\uB88A\uF3C6\uF3C7\uB88B\uB0CA\uB88C"+ + "\uF3C5\uB88D\uF3C9\uCBF1\uB88E\uB88F\uB890\uF3CB"+ + "\uB891\uD0A6\uB892\uB893\uB1CA\uF3C8\uB894\uB895"+ + "\uB896\uF3CF\uB897\uB5D1\uB898\uB899\uF3D7\uB89A"+ + "\uF3D2\uB89B\uB89C\uB89D\uF3D4\uF3D3\uB7FB\uB89E"+ + "\uB1BF\uB89F\uF3CE\uF3CA\uB5DA\uB8A0\uF3D0\uB940"+ + "\uB941\uF3D1\uB942\uF3D5\uB943\uB944\uB945\uB946"+ + "\uF3CD\uB947\uBCE3\uB948\uC1FD\uB949\uF3D6\uB94A"+ + "\uB94B\uB94C\uB94D\uB94E\uB94F\uF3DA\uB950\uF3CC"+ + "\uB951\uB5C8\uB952\uBDEE\uF3DC\uB953\uB954\uB7A4"+ + "\uBFF0\uD6FE\uCDB2\uB955\uB4F0\uB956\uB2DF\uB957"+ + "\uF3D8\uB958\uF3D9\uC9B8\uB959\uF3DD\uB95A\uB95B"+ + "\uF3DE\uB95C\uF3E1\uB95D\uB95E\uB95F\uB960\uB961"+ + "\uB962\uB963\uB964\uB965\uB966\uB967\uF3DF\uB968"+ + "\uB969\uF3E3\uF3E2\uB96A\uB96B\uF3DB\uB96C\uBFEA"+ + "\uB96D\uB3EF\uB96E\uF3E0\uB96F\uB970\uC7A9\uB971"+ + "\uBCF2\uB972\uB973\uB974\uB975\uF3EB\uB976\uB977"+ + "\uB978\uB979\uB97A\uB97B\uB97C\uB9BF\uB97D\uB97E"+ + "\uF3E4\uB980\uB981\uB982\uB2AD\uBBFE\uB983\uCBE3"+ + "\uB984\uB985\uB986\uB987\uF3ED\uF3E9\uB988\uB989"+ + "\uB98A\uB9DC\uF3EE\uB98B\uB98C\uB98D\uF3E5\uF3E6"+ + "\uF3EA\uC2E1\uF3EC\uF3EF\uF3E8\uBCFD\uB98E\uB98F"+ + "\uB990\uCFE4\uB991\uB992\uF3F0\uB993\uB994\uB995"+ + "\uF3E7\uB996\uB997\uB998\uB999\uB99A\uB99B\uB99C"+ + "\uB99D\uF3F2\uB99E\uB99F\uB9A0\uBA40\uD7AD\uC6AA"+ + "\uBA41\uBA42\uBA43\uBA44\uF3F3\uBA45\uBA46\uBA47"+ + "\uBA48\uF3F1\uBA49\uC2A8\uBA4A\uBA4B\uBA4C\uBA4D"+ + "\uBA4E\uB8DD\uF3F5\uBA4F\uBA50\uF3F4\uBA51\uBA52"+ + "\uBA53\uB4DB\uBA54\uBA55\uBA56\uF3F6\uF3F7\uBA57"+ + "\uBA58\uBA59\uF3F8\uBA5A\uBA5B\uBA5C\uC0BA\uBA5D"+ + "\uBA5E\uC0E9\uBA5F\uBA60\uBA61\uBA62\uBA63\uC5F1"+ + "\uBA64\uBA65\uBA66\uBA67\uF3FB\uBA68\uF3FA\uBA69"+ + "\uBA6A\uBA6B\uBA6C\uBA6D\uBA6E\uBA6F\uBA70\uB4D8"+ + "\uBA71\uBA72\uBA73\uF3FE\uF3F9\uBA74\uBA75\uF3FC"+ + "\uBA76\uBA77\uBA78\uBA79\uBA7A\uBA7B\uF3FD\uBA7C"+ + "\uBA7D\uBA7E\uBA80\uBA81\uBA82\uBA83\uBA84\uF4A1"+ + "\uBA85\uBA86\uBA87\uBA88\uBA89\uBA8A\uF4A3\uBBC9"+ + "\uBA8B\uBA8C\uF4A2\uBA8D\uBA8E\uBA8F\uBA90\uBA91"+ + "\uBA92\uBA93\uBA94\uBA95\uBA96\uBA97\uBA98\uBA99"+ + "\uF4A4\uBA9A\uBA9B\uBA9C\uBA9D\uBA9E\uBA9F\uB2BE"+ + "\uF4A6\uF4A5\uBAA0\uBB40\uBB41\uBB42\uBB43\uBB44"+ + "\uBB45\uBB46\uBB47\uBB48\uBB49\uBCAE\uBB4A\uBB4B"+ + "\uBB4C\uBB4D\uBB4E\uBB4F\uBB50\uBB51\uBB52\uBB53"+ + "\uBB54\uBB55\uBB56\uBB57\uBB58\uBB59\uBB5A\uBB5B"+ + "\uBB5C\uBB5D\uBB5E\uBB5F\uBB60\uBB61\uBB62\uBB63"+ + "\uBB64\uBB65\uBB66\uBB67\uBB68\uBB69\uBB6A\uBB6B"+ + "\uBB6C\uBB6D\uBB6E\uC3D7\uD9E1\uBB6F\uBB70\uBB71"+ + "\uBB72\uBB73\uBB74\uC0E0\uF4CC\uD7D1\uBB75\uBB76"+ + "\uBB77\uBB78\uBB79\uBB7A\uBB7B\uBB7C\uBB7D\uBB7E"+ + "\uBB80\uB7DB\uBB81\uBB82\uBB83\uBB84\uBB85\uBB86"+ + "\uBB87\uF4CE\uC1A3\uBB88\uBB89\uC6C9\uBB8A\uB4D6"+ + "\uD5B3\uBB8B\uBB8C\uBB8D\uF4D0\uF4CF\uF4D1\uCBDA"+ + "\uBB8E\uBB8F\uF4D2\uBB90\uD4C1\uD6E0\uBB91\uBB92"+ + "\uBB93\uBB94\uB7E0\uBB95\uBB96\uBB97\uC1B8\uBB98"+ + "\uBB99\uC1BB\uF4D3\uBEAC\uBB9A\uBB9B\uBB9C\uBB9D"+ + "\uBB9E\uB4E2\uBB9F\uBBA0\uF4D4\uF4D5\uBEAB\uBC40"+ + "\uBC41\uF4D6\uBC42\uBC43\uBC44\uF4DB\uBC45\uF4D7"+ + "\uF4DA\uBC46\uBAFD\uBC47\uF4D8\uF4D9\uBC48\uBC49"+ + "\uBC4A\uBC4B\uBC4C\uBC4D\uBC4E\uB8E2\uCCC7\uF4DC"+ + "\uBC4F\uB2DA\uBC50\uBC51\uC3D3\uBC52\uBC53\uD4E3"+ + "\uBFB7\uBC54\uBC55\uBC56\uBC57\uBC58\uBC59\uBC5A"+ + "\uF4DD\uBC5B\uBC5C\uBC5D\uBC5E\uBC5F\uBC60\uC5B4"+ + "\uBC61\uBC62\uBC63\uBC64\uBC65\uBC66\uBC67\uBC68"+ + "\uF4E9\uBC69\uBC6A\uCFB5\uBC6B\uBC6C\uBC6D\uBC6E"+ + "\uBC6F\uBC70\uBC71\uBC72\uBC73\uBC74\uBC75\uBC76"+ + "\uBC77\uBC78\uCEC9\uBC79\uBC7A\uBC7B\uBC7C\uBC7D"+ + "\uBC7E\uBC80\uBC81\uBC82\uBC83\uBC84\uBC85\uBC86"+ + "\uBC87\uBC88\uBC89\uBC8A\uBC8B\uBC8C\uBC8D\uBC8E"+ + "\uCBD8\uBC8F\uCBF7\uBC90\uBC91\uBC92\uBC93\uBDF4"+ + "\uBC94\uBC95\uBC96\uD7CF\uBC97\uBC98\uBC99\uC0DB"+ + "\uBC9A\uBC9B\uBC9C\uBC9D\uBC9E\uBC9F\uBCA0\uBD40"+ + "\uBD41\uBD42\uBD43\uBD44\uBD45\uBD46\uBD47\uBD48"+ + "\uBD49\uBD4A\uBD4B\uBD4C\uBD4D\uBD4E\uBD4F\uBD50"+ + "\uBD51\uBD52\uBD53\uBD54\uBD55\uBD56\uBD57\uBD58"+ + "\uBD59\uBD5A\uBD5B\uBD5C\uBD5D\uBD5E\uBD5F\uBD60"+ + "\uBD61\uBD62\uBD63\uBD64\uBD65\uBD66\uBD67\uBD68"+ + "\uBD69\uBD6A\uBD6B\uBD6C\uBD6D\uBD6E\uBD6F\uBD70"+ + "\uBD71\uBD72\uBD73\uBD74\uBD75\uBD76\uD0F5\uBD77"+ + "\uBD78\uBD79\uBD7A\uBD7B\uBD7C\uBD7D\uBD7E\uF4EA"+ + "\uBD80\uBD81\uBD82\uBD83\uBD84\uBD85\uBD86\uBD87"+ + "\uBD88\uBD89\uBD8A\uBD8B\uBD8C\uBD8D\uBD8E\uBD8F"+ + "\uBD90\uBD91\uBD92\uBD93\uBD94\uBD95\uBD96\uBD97"+ + "\uBD98\uBD99\uBD9A\uBD9B\uBD9C\uBD9D\uBD9E\uBD9F"+ + "\uBDA0\uBE40\uBE41\uBE42\uBE43\uBE44\uBE45\uBE46"+ + "\uBE47\uBE48\uBE49\uBE4A\uBE4B\uBE4C\uF4EB\uBE4D"+ + "\uBE4E\uBE4F\uBE50\uBE51\uBE52\uBE53\uF4EC\uBE54"+ + "\uBE55\uBE56\uBE57\uBE58\uBE59\uBE5A\uBE5B\uBE5C"+ + "\uBE5D\uBE5E\uBE5F\uBE60\uBE61\uBE62\uBE63\uBE64"+ + "\uBE65\uBE66\uBE67\uBE68\uBE69\uBE6A\uBE6B\uBE6C"+ + "\uBE6D\uBE6E\uBE6F\uBE70\uBE71\uBE72\uBE73\uBE74"+ + "\uBE75\uBE76\uBE77\uBE78\uBE79\uBE7A\uBE7B\uBE7C"+ + "\uBE7D\uBE7E\uBE80\uBE81\uBE82\uBE83\uBE84\uBE85"+ + "\uBE86\uBE87\uBE88\uBE89\uBE8A\uBE8B\uBE8C\uBE8D"+ + "\uBE8E\uBE8F\uBE90\uBE91\uBE92\uBE93\uBE94\uBE95"+ + "\uBE96\uBE97\uBE98\uBE99\uBE9A\uBE9B\uBE9C\uBE9D"+ + "\uBE9E\uBE9F\uBEA0\uBF40\uBF41\uBF42\uBF43\uBF44"+ + "\uBF45\uBF46\uBF47\uBF48\uBF49\uBF4A\uBF4B\uBF4C"+ + "\uBF4D\uBF4E\uBF4F\uBF50\uBF51\uBF52\uBF53\uBF54"+ + "\uBF55\uBF56\uBF57\uBF58\uBF59\uBF5A\uBF5B\uBF5C"+ + "\uBF5D\uBF5E\uBF5F\uBF60\uBF61\uBF62\uBF63\uBF64"+ + "\uBF65\uBF66\uBF67\uBF68\uBF69\uBF6A\uBF6B\uBF6C"+ + "\uBF6D\uBF6E\uBF6F\uBF70\uBF71\uBF72\uBF73\uBF74"+ + "\uBF75\uBF76\uBF77\uBF78\uBF79\uBF7A\uBF7B\uBF7C"+ + "\uBF7D\uBF7E\uBF80\uF7E3\uBF81\uBF82\uBF83\uBF84"+ + "\uBF85\uB7B1\uBF86\uBF87\uBF88\uBF89\uBF8A\uF4ED"+ + "\uBF8B\uBF8C\uBF8D\uBF8E\uBF8F\uBF90\uBF91\uBF92"+ + "\uBF93\uBF94\uBF95\uBF96\uBF97\uBF98\uBF99\uBF9A"+ + "\uBF9B\uBF9C\uBF9D\uBF9E\uBF9F\uBFA0\uC040\uC041"+ + "\uC042\uC043\uC044\uC045\uC046\uC047\uC048\uC049"+ + "\uC04A\uC04B\uC04C\uC04D\uC04E\uC04F\uC050\uC051"+ + "\uC052\uC053\uC054\uC055\uC056\uC057\uC058\uC059"+ + "\uC05A\uC05B\uC05C\uC05D\uC05E\uC05F\uC060\uC061"+ + "\uC062\uC063\uD7EB\uC064\uC065\uC066\uC067\uC068"+ + "\uC069\uC06A\uC06B\uC06C\uC06D\uC06E\uC06F\uC070"+ + "\uC071\uC072\uC073\uC074\uC075\uC076\uC077\uC078"+ + "\uC079\uC07A\uC07B\uF4EE\uC07C\uC07D\uC07E\uE6F9"+ + "\uBEC0\uE6FA\uBAEC\uE6FB\uCFCB\uE6FC\uD4BC\uBCB6"+ + "\uE6FD\uE6FE\uBCCD\uC8D2\uCEB3\uE7A1\uC080\uB4BF"+ + "\uE7A2\uC9B4\uB8D9\uC4C9\uC081\uD7DD\uC2DA\uB7D7"+ + "\uD6BD\uCEC6\uB7C4\uC082\uC083\uC5A6\uE7A3\uCFDF"+ + "\uE7A4\uE7A5\uE7A6\uC1B7\uD7E9\uC9F0\uCFB8\uD6AF"+ + "\uD6D5\uE7A7\uB0ED\uE7A8\uE7A9\uC9DC\uD2EF\uBEAD"+ + "\uE7AA\uB0F3\uC8DE\uBDE1\uE7AB\uC8C6\uC084\uE7AC"+ + "\uBBE6\uB8F8\uD1A4\uE7AD\uC2E7\uBEF8\uBDCA\uCDB3"+ + "\uE7AE\uE7AF\uBEEE\uD0E5\uC085\uCBE7\uCCD0\uBCCC"+ + "\uE7B0\uBCA8\uD0F7\uE7B1\uC086\uD0F8\uE7B2\uE7B3"+ + "\uB4C2\uE7B4\uE7B5\uC9FE\uCEAC\uC3E0\uE7B7\uB1C1"+ + "\uB3F1\uC087\uE7B8\uE7B9\uD7DB\uD5C0\uE7BA\uC2CC"+ + "\uD7BA\uE7BB\uE7BC\uE7BD\uBCEA\uC3E5\uC0C2\uE7BE"+ + "\uE7BF\uBCA9\uC088\uE7C0\uE7C1\uE7B6\uB6D0\uE7C2"+ + "\uC089\uE7C3\uE7C4\uBBBA\uB5DE\uC2C6\uB1E0\uE7C5"+ + "\uD4B5\uE7C6\uB8BF\uE7C8\uE7C7\uB7EC\uC08A\uE7C9"+ + "\uB2F8\uE7CA\uE7CB\uE7CC\uE7CD\uE7CE\uE7CF\uE7D0"+ + "\uD3A7\uCBF5\uE7D1\uE7D2\uE7D3\uE7D4\uC9C9\uE7D5"+ + "\uE7D6\uE7D7\uE7D8\uE7D9\uBDC9\uE7DA\uF3BE\uC08B"+ + "\uB8D7\uC08C\uC8B1\uC08D\uC08E\uC08F\uC090\uC091"+ + "\uC092\uC093\uF3BF\uC094\uF3C0\uF3C1\uC095\uC096"+ + "\uC097\uC098\uC099\uC09A\uC09B\uC09C\uC09D\uC09E"+ + "\uB9DE\uCDF8\uC09F\uC0A0\uD8E8\uBAB1\uC140\uC2DE"+ + "\uEEB7\uC141\uB7A3\uC142\uC143\uC144\uC145\uEEB9"+ + "\uC146\uEEB8\uB0D5\uC147\uC148\uC149\uC14A\uC14B"+ + "\uEEBB\uD5D6\uD7EF\uC14C\uC14D\uC14E\uD6C3\uC14F"+ + "\uC150\uEEBD\uCAF0\uC151\uEEBC\uC152\uC153\uC154"+ + "\uC155\uEEBE\uC156\uC157\uC158\uC159\uEEC0\uC15A"+ + "\uC15B\uEEBF\uC15C\uC15D\uC15E\uC15F\uC160\uC161"+ + "\uC162\uC163\uD1F2\uC164\uC7BC\uC165\uC3C0\uC166"+ + "\uC167\uC168\uC169\uC16A\uB8E1\uC16B\uC16C\uC16D"+ + "\uC16E\uC16F\uC1E7\uC170\uC171\uF4C6\uD0DF\uF4C7"+ + "\uC172\uCFDB\uC173\uC174\uC8BA\uC175\uC176\uF4C8"+ + "\uC177\uC178\uC179\uC17A\uC17B\uC17C\uC17D\uF4C9"+ + "\uF4CA\uC17E\uF4CB\uC180\uC181\uC182\uC183\uC184"+ + "\uD9FA\uB8FE\uC185\uC186\uE5F1\uD3F0\uC187\uF4E0"+ + "\uC188\uCECC\uC189\uC18A\uC18B\uB3E1\uC18C\uC18D"+ + "\uC18E\uC18F\uF1B4\uC190\uD2EE\uC191\uF4E1\uC192"+ + "\uC193\uC194\uC195\uC196\uCFE8\uF4E2\uC197\uC198"+ + "\uC7CC\uC199\uC19A\uC19B\uC19C\uC19D\uC19E\uB5D4"+ + "\uB4E4\uF4E4\uC19F\uC1A0\uC240\uF4E3\uF4E5\uC241"+ + "\uC242\uF4E6\uC243\uC244\uC245\uC246\uF4E7\uC247"+ + "\uBAB2\uB0BF\uC248\uF4E8\uC249\uC24A\uC24B\uC24C"+ + "\uC24D\uC24E\uC24F\uB7AD\uD2ED\uC250\uC251\uC252"; + + private final static String innerEncoderIndex8= + "\uD2AB\uC0CF\uC253\uBFBC\uEBA3\uD5DF\uEAC8\uC254"+ + "\uC255\uC256\uC257\uF1F3\uB6F8\uCBA3\uC258\uC259"+ + "\uC4CD\uC25A\uF1E7\uC25B\uF1E8\uB8FB\uF1E9\uBAC4"+ + "\uD4C5\uB0D2\uC25C\uC25D\uF1EA\uC25E\uC25F\uC260"+ + "\uF1EB\uC261\uF1EC\uC262\uC263\uF1ED\uF1EE\uF1EF"+ + "\uF1F1\uF1F0\uC5D5\uC264\uC265\uC266\uC267\uC268"+ + "\uC269\uF1F2\uC26A\uB6FA\uC26B\uF1F4\uD2AE\uDEC7"+ + "\uCBCA\uC26C\uC26D\uB3DC\uC26E\uB5A2\uC26F\uB9A2"+ + "\uC270\uC271\uC4F4\uF1F5\uC272\uC273\uF1F6\uC274"+ + "\uC275\uC276\uC1C4\uC1FB\uD6B0\uF1F7\uC277\uC278"+ + "\uC279\uC27A\uF1F8\uC27B\uC1AA\uC27C\uC27D\uC27E"+ + "\uC6B8\uC280\uBEDB\uC281\uC282\uC283\uC284\uC285"+ + "\uC286\uC287\uC288\uC289\uC28A\uC28B\uC28C\uC28D"+ + "\uC28E\uF1F9\uB4CF\uC28F\uC290\uC291\uC292\uC293"+ + "\uC294\uF1FA\uC295\uC296\uC297\uC298\uC299\uC29A"+ + "\uC29B\uC29C\uC29D\uC29E\uC29F\uC2A0\uC340\uEDB2"+ + "\uEDB1\uC341\uC342\uCBE0\uD2DE\uC343\uCBC1\uD5D8"+ + "\uC344\uC8E2\uC345\uC0DF\uBCA1\uC346\uC347\uC348"+ + "\uC349\uC34A\uC34B\uEBC1\uC34C\uC34D\uD0A4\uC34E"+ + "\uD6E2\uC34F\uB6C7\uB8D8\uEBC0\uB8CE\uC350\uEBBF"+ + "\uB3A6\uB9C9\uD6AB\uC351\uB7F4\uB7CA\uC352\uC353"+ + "\uC354\uBCE7\uB7BE\uEBC6\uC355\uEBC7\uB0B9\uBFCF"+ + "\uC356\uEBC5\uD3FD\uC357\uEBC8\uC358\uC359\uEBC9"+ + "\uC35A\uC35B\uB7CE\uC35C\uEBC2\uEBC4\uC9F6\uD6D7"+ + "\uD5CD\uD0B2\uEBCF\uCEB8\uEBD0\uC35D\uB5A8\uC35E"+ + "\uC35F\uC360\uC361\uC362\uB1B3\uEBD2\uCCA5\uC363"+ + "\uC364\uC365\uC366\uC367\uC368\uC369\uC5D6\uEBD3"+ + "\uC36A\uEBD1\uC5DF\uEBCE\uCAA4\uEBD5\uB0FB\uC36B"+ + "\uC36C\uBAFA\uC36D\uC36E\uD8B7\uF1E3\uC36F\uEBCA"+ + "\uEBCB\uEBCC\uEBCD\uEBD6\uE6C0\uEBD9\uC370\uBFE8"+ + "\uD2C8\uEBD7\uEBDC\uB8EC\uEBD8\uC371\uBDBA\uC372"+ + "\uD0D8\uC373\uB0B7\uC374\uEBDD\uC4DC\uC375\uC376"+ + "\uC377\uC378\uD6AC\uC379\uC37A\uC37B\uB4E0\uC37C"+ + "\uC37D\uC2F6\uBCB9\uC37E\uC380\uEBDA\uEBDB\uD4E0"+ + "\uC6EA\uC4D4\uEBDF\uC5A7\uD9F5\uC381\uB2B1\uC382"+ + "\uEBE4\uC383\uBDC5\uC384\uC385\uC386\uEBE2\uC387"+ + "\uC388\uC389\uC38A\uC38B\uC38C\uC38D\uC38E\uC38F"+ + "\uC390\uC391\uC392\uC393\uEBE3\uC394\uC395\uB8AC"+ + "\uC396\uCDD1\uEBE5\uC397\uC398\uC399\uEBE1\uC39A"+ + "\uC1B3\uC39B\uC39C\uC39D\uC39E\uC39F\uC6A2\uC3A0"+ + "\uC440\uC441\uC442\uC443\uC444\uC445\uCCF3\uC446"+ + "\uEBE6\uC447\uC0B0\uD2B8\uEBE7\uC448\uC449\uC44A"+ + "\uB8AF\uB8AD\uC44B\uEBE8\uC7BB\uCDF3\uC44C\uC44D"+ + "\uC44E\uEBEA\uEBEB\uC44F\uC450\uC451\uC452\uC453"+ + "\uEBED\uC454\uC455\uC456\uC457\uD0C8\uC458\uEBF2"+ + "\uC459\uEBEE\uC45A\uC45B\uC45C\uEBF1\uC8F9\uC45D"+ + "\uD1FC\uEBEC\uC45E\uC45F\uEBE9\uC460\uC461\uC462"+ + "\uC463\uB8B9\uCFD9\uC4E5\uEBEF\uEBF0\uCCDA\uCDC8"+ + "\uB0F2\uC464\uEBF6\uC465\uC466\uC467\uC468\uC469"+ + "\uEBF5\uC46A\uB2B2\uC46B\uC46C\uC46D\uC46E\uB8E0"+ + "\uC46F\uEBF7\uC470\uC471\uC472\uC473\uC474\uC475"+ + "\uB1EC\uC476\uC477\uCCC5\uC4A4\uCFA5\uC478\uC479"+ + "\uC47A\uC47B\uC47C\uEBF9\uC47D\uC47E\uECA2\uC480"+ + "\uC5F2\uC481\uEBFA\uC482\uC483\uC484\uC485\uC486"+ + "\uC487\uC488\uC489\uC9C5\uC48A\uC48B\uC48C\uC48D"+ + "\uC48E\uC48F\uE2DF\uEBFE\uC490\uC491\uC492\uC493"+ + "\uCDCE\uECA1\uB1DB\uD3B7\uC494\uC495\uD2DC\uC496"+ + "\uC497\uC498\uEBFD\uC499\uEBFB\uC49A\uC49B\uC49C"+ + "\uC49D\uC49E\uC49F\uC4A0\uC540\uC541\uC542\uC543"+ + "\uC544\uC545\uC546\uC547\uC548\uC549\uC54A\uC54B"+ + "\uC54C\uC54D\uC54E\uB3BC\uC54F\uC550\uC551\uEAB0"+ + "\uC552\uC553\uD7D4\uC554\uF4AB\uB3F4\uC555\uC556"+ + "\uC557\uC558\uC559\uD6C1\uD6C2\uC55A\uC55B\uC55C"+ + "\uC55D\uC55E\uC55F\uD5E9\uBECA\uC560\uF4A7\uC561"+ + "\uD2A8\uF4A8\uF4A9\uC562\uF4AA\uBECB\uD3DF\uC563"+ + "\uC564\uC565\uC566\uC567\uC9E0\uC9E1\uC568\uC569"+ + "\uF3C2\uC56A\uCAE6\uC56B\uCCF2\uC56C\uC56D\uC56E"+ + "\uC56F\uC570\uC571\uE2B6\uCBB4\uC572\uCEE8\uD6DB"+ + "\uC573\uF4AD\uF4AE\uF4AF\uC574\uC575\uC576\uC577"+ + "\uF4B2\uC578\uBABD\uF4B3\uB0E3\uF4B0\uC579\uF4B1"+ + "\uBDA2\uB2D5\uC57A\uF4B6\uF4B7\uB6E6\uB2B0\uCFCF"+ + "\uF4B4\uB4AC\uC57B\uF4B5\uC57C\uC57D\uF4B8\uC57E"+ + "\uC580\uC581\uC582\uC583\uF4B9\uC584\uC585\uCDA7"+ + "\uC586\uF4BA\uC587\uF4BB\uC588\uC589\uC58A\uF4BC"+ + "\uC58B\uC58C\uC58D\uC58E\uC58F\uC590\uC591\uC592"+ + "\uCBD2\uC593\uF4BD\uC594\uC595\uC596\uC597\uF4BE"+ + "\uC598\uC599\uC59A\uC59B\uC59C\uC59D\uC59E\uC59F"+ + "\uF4BF\uC5A0\uC640\uC641\uC642\uC643\uF4DE\uC1BC"+ + "\uBCE8\uC644\uC9AB\uD1DE\uE5F5\uC645\uC646\uC647"+ + "\uC648\uDCB3\uD2D5\uC649\uC64A\uDCB4\uB0AC\uDCB5"+ + "\uC64B\uC64C\uBDDA\uC64D\uDCB9\uC64E\uC64F\uC650"+ + "\uD8C2\uC651\uDCB7\uD3F3\uC652\uC9D6\uDCBA\uDCB6"+ + "\uC653\uDCBB\uC3A2\uC654\uC655\uC656\uC657\uDCBC"+ + "\uDCC5\uDCBD\uC658\uC659\uCEDF\uD6A5\uC65A\uDCCF"+ + "\uC65B\uDCCD\uC65C\uC65D\uDCD2\uBDE6\uC2AB\uC65E"+ + "\uDCB8\uDCCB\uDCCE\uDCBE\uB7D2\uB0C5\uDCC7\uD0BE"+ + "\uDCC1\uBBA8\uC65F\uB7BC\uDCCC\uC660\uC661\uDCC6"+ + "\uDCBF\uC7DB\uC662\uC663\uC664\uD1BF\uDCC0\uC665"+ + "\uC666\uDCCA\uC667\uC668\uDCD0\uC669\uC66A\uCEAD"+ + "\uDCC2\uC66B\uDCC3\uDCC8\uDCC9\uB2D4\uDCD1\uCBD5"+ + "\uC66C\uD4B7\uDCDB\uDCDF\uCCA6\uDCE6\uC66D\uC3E7"+ + "\uDCDC\uC66E\uC66F\uBFC1\uDCD9\uC670\uB0FA\uB9B6"+ + "\uDCE5\uDCD3\uC671\uDCC4\uDCD6\uC8F4\uBFE0\uC672"+ + "\uC673\uC674\uC675\uC9BB\uC676\uC677\uC678\uB1BD"+ + "\uC679\uD3A2\uC67A\uC67B\uDCDA\uC67C\uC67D\uDCD5"+ + "\uC67E\uC6BB\uC680\uDCDE\uC681\uC682\uC683\uC684"+ + "\uC685\uD7C2\uC3AF\uB7B6\uC7D1\uC3A9\uDCE2\uDCD8"+ + "\uDCEB\uDCD4\uC686\uC687\uDCDD\uC688\uBEA5\uDCD7"+ + "\uC689\uDCE0\uC68A\uC68B\uDCE3\uDCE4\uC68C\uDCF8"+ + "\uC68D\uC68E\uDCE1\uDDA2\uDCE7\uC68F\uC690\uC691"+ + "\uC692\uC693\uC694\uC695\uC696\uC697\uC698\uBCEB"+ + "\uB4C4\uC699\uC69A\uC3A3\uB2E7\uDCFA\uC69B\uDCF2"+ + "\uC69C\uDCEF\uC69D\uDCFC\uDCEE\uD2F0\uB2E8\uC69E"+ + "\uC8D7\uC8E3\uDCFB\uC69F\uDCED\uC6A0\uC740\uC741"+ + "\uDCF7\uC742\uC743\uDCF5\uC744\uC745\uBEA3\uDCF4"+ + "\uC746\uB2DD\uC747\uC748\uC749\uC74A\uC74B\uDCF3"+ + "\uBCF6\uDCE8\uBBC4\uC74C\uC0F3\uC74D\uC74E\uC74F"+ + "\uC750\uC751\uBCD4\uDCE9\uDCEA\uC752\uDCF1\uDCF6"+ + "\uDCF9\uB5B4\uC753\uC8D9\uBBE7\uDCFE\uDCFD\uD3AB"+ + "\uDDA1\uDDA3\uDDA5\uD2F1\uDDA4\uDDA6\uDDA7\uD2A9"+ + "\uC754\uC755\uC756\uC757\uC758\uC759\uC75A\uBAC9"+ + "\uDDA9\uC75B\uC75C\uDDB6\uDDB1\uDDB4\uC75D\uC75E"+ + "\uC75F\uC760\uC761\uC762\uC763\uDDB0\uC6CE\uC764"+ + "\uC765\uC0F2\uC766\uC767\uC768\uC769\uC9AF\uC76A"+ + "\uC76B\uC76C\uDCEC\uDDAE\uC76D\uC76E\uC76F\uC770"+ + "\uDDB7\uC771\uC772\uDCF0\uDDAF\uC773\uDDB8\uC774"+ + "\uDDAC\uC775\uC776\uC777\uC778\uC779\uC77A\uC77B"+ + "\uDDB9\uDDB3\uDDAD\uC4AA\uC77C\uC77D\uC77E\uC780"+ + "\uDDA8\uC0B3\uC1AB\uDDAA\uDDAB\uC781\uDDB2\uBBF1"+ + "\uDDB5\uD3A8\uDDBA\uC782\uDDBB\uC3A7\uC783\uC784"+ + "\uDDD2\uDDBC\uC785\uC786\uC787\uDDD1\uC788\uB9BD"+ + "\uC789\uC78A\uBED5\uC78B\uBEFA\uC78C\uC78D\uBACA"+ + "\uC78E\uC78F\uC790\uC791\uDDCA\uC792\uDDC5\uC793"+ + "\uDDBF\uC794\uC795\uC796\uB2CB\uDDC3\uC797\uDDCB"+ + "\uB2A4\uDDD5\uC798\uC799\uC79A\uDDBE\uC79B\uC79C"+ + "\uC79D\uC6D0\uDDD0\uC79E\uC79F\uC7A0\uC840\uC841"+ + "\uDDD4\uC1E2\uB7C6\uC842\uC843\uC844\uC845\uC846"+ + "\uDDCE\uDDCF\uC847\uC848\uC849\uDDC4\uC84A\uC84B"+ + "\uC84C\uDDBD\uC84D\uDDCD\uCCD1\uC84E\uDDC9\uC84F"+ + "\uC850\uC851\uC852\uDDC2\uC3C8\uC6BC\uCEAE\uDDCC"+ + "\uC853\uDDC8\uC854\uC855\uC856\uC857\uC858\uC859"+ + "\uDDC1\uC85A\uC85B\uC85C\uDDC6\uC2DC\uC85D\uC85E"+ + "\uC85F\uC860\uC861\uC862\uD3A9\uD3AA\uDDD3\uCFF4"+ + "\uC8F8\uC863\uC864\uC865\uC866\uC867\uC868\uC869"+ + "\uC86A\uDDE6\uC86B\uC86C\uC86D\uC86E\uC86F\uC870"+ + "\uDDC7\uC871\uC872\uC873\uDDE0\uC2E4\uC874\uC875"+ + "\uC876\uC877\uC878\uC879\uC87A\uC87B\uDDE1\uC87C"+ + "\uC87D\uC87E\uC880\uC881\uC882\uC883\uC884\uC885"+ + "\uC886\uDDD7\uC887\uC888\uC889\uC88A\uC88B\uD6F8"+ + "\uC88C\uDDD9\uDDD8\uB8F0\uDDD6\uC88D\uC88E\uC88F"+ + "\uC890\uC6CF\uC891\uB6AD\uC892\uC893\uC894\uC895"+ + "\uC896\uDDE2\uC897\uBAF9\uD4E1\uDDE7\uC898\uC899"+ + "\uC89A\uB4D0\uC89B\uDDDA\uC89C\uBFFB\uDDE3\uC89D"+ + "\uDDDF\uC89E\uDDDD\uC89F\uC8A0\uC940\uC941\uC942"+ + "\uC943\uC944\uB5D9\uC945\uC946\uC947\uC948\uDDDB"+ + "\uDDDC\uDDDE\uC949\uBDAF\uDDE4\uC94A\uDDE5\uC94B"+ + "\uC94C\uC94D\uC94E\uC94F\uC950\uC951\uC952\uDDF5"+ + "\uC953\uC3C9\uC954\uC955\uCBE2\uC956\uC957\uC958"+ + "\uC959\uDDF2\uC95A\uC95B\uC95C\uC95D\uC95E\uC95F"+ + "\uC960\uC961\uC962\uC963\uC964\uC965\uC966\uD8E1"+ + "\uC967\uC968\uC6D1\uC969\uDDF4\uC96A\uC96B\uC96C"+ + "\uD5F4\uDDF3\uDDF0\uC96D\uC96E\uDDEC\uC96F\uDDEF"+ + "\uC970\uDDE8\uC971\uC972\uD0EE\uC973\uC974\uC975"+ + "\uC976\uC8D8\uDDEE\uC977\uC978\uDDE9\uC979\uC97A"+ + "\uDDEA\uCBF2\uC97B\uDDED\uC97C\uC97D\uB1CD\uC97E"+ + "\uC980\uC981\uC982\uC983\uC984\uC0B6\uC985\uBCBB"+ + "\uDDF1\uC986\uC987\uDDF7\uC988\uDDF6\uDDEB\uC989"+ + "\uC98A\uC98B\uC98C\uC98D\uC5EE\uC98E\uC98F\uC990"+ + "\uDDFB\uC991\uC992\uC993\uC994\uC995\uC996\uC997"+ + "\uC998\uC999\uC99A\uC99B\uDEA4\uC99C\uC99D\uDEA3"+ + "\uC99E\uC99F\uC9A0\uCA40\uCA41\uCA42\uCA43\uCA44"+ + "\uCA45\uCA46\uCA47\uCA48\uDDF8\uCA49\uCA4A\uCA4B"+ + "\uCA4C\uC3EF\uCA4D\uC2FB\uCA4E\uCA4F\uCA50\uD5E1"+ + "\uCA51\uCA52\uCEB5\uCA53\uCA54\uCA55\uCA56\uDDFD"+ + "\uCA57\uB2CC\uCA58\uCA59\uCA5A\uCA5B\uCA5C\uCA5D"+ + "\uCA5E\uCA5F\uCA60\uC4E8\uCADF\uCA61\uCA62\uCA63"+ + "\uCA64\uCA65\uCA66\uCA67\uCA68\uCA69\uCA6A\uC7BE"+ + "\uDDFA\uDDFC\uDDFE\uDEA2\uB0AA\uB1CE\uCA6B\uCA6C"+ + "\uCA6D\uCA6E\uCA6F\uDEAC\uCA70\uCA71\uCA72\uCA73"+ + "\uDEA6\uBDB6\uC8EF\uCA74\uCA75\uCA76\uCA77\uCA78"+ + "\uCA79\uCA7A\uCA7B\uCA7C\uCA7D\uCA7E\uDEA1\uCA80"+ + "\uCA81\uDEA5\uCA82\uCA83\uCA84\uCA85\uDEA9\uCA86"+ + "\uCA87\uCA88\uCA89\uCA8A\uDEA8\uCA8B\uCA8C\uCA8D"+ + "\uDEA7\uCA8E\uCA8F\uCA90\uCA91\uCA92\uCA93\uCA94"+ + "\uCA95\uCA96\uDEAD\uCA97\uD4CC\uCA98\uCA99\uCA9A"+ + "\uCA9B\uDEB3\uDEAA\uDEAE\uCA9C\uCA9D\uC0D9\uCA9E"+ + "\uCA9F\uCAA0\uCB40\uCB41\uB1A1\uDEB6\uCB42\uDEB1"+ + "\uCB43\uCB44\uCB45\uCB46\uCB47\uCB48\uCB49\uDEB2"+ + "\uCB4A\uCB4B\uCB4C\uCB4D\uCB4E\uCB4F\uCB50\uCB51"+ + "\uCB52\uCB53\uCB54\uD1A6\uDEB5\uCB55\uCB56\uCB57"+ + "\uCB58\uCB59\uCB5A\uCB5B\uDEAF\uCB5C\uCB5D\uCB5E"+ + "\uDEB0\uCB5F\uD0BD\uCB60\uCB61\uCB62\uDEB4\uCAED"+ + "\uDEB9\uCB63\uCB64\uCB65\uCB66\uCB67\uCB68\uDEB8"+ + "\uCB69\uDEB7\uCB6A\uCB6B\uCB6C\uCB6D\uCB6E\uCB6F"+ + "\uCB70\uDEBB\uCB71\uCB72\uCB73\uCB74\uCB75\uCB76"+ + "\uCB77\uBDE5\uCB78\uCB79\uCB7A\uCB7B\uCB7C\uB2D8"+ + "\uC3EA\uCB7D\uCB7E\uDEBA\uCB80\uC5BA\uCB81\uCB82"+ + "\uCB83\uCB84\uCB85\uCB86\uDEBC\uCB87\uCB88\uCB89"+ + "\uCB8A\uCB8B\uCB8C\uCB8D\uCCD9\uCB8E\uCB8F\uCB90"+ + "\uCB91\uB7AA\uCB92\uCB93\uCB94\uCB95\uCB96\uCB97"+ + "\uCB98\uCB99\uCB9A\uCB9B\uCB9C\uCB9D\uCB9E\uCB9F"+ + "\uCBA0\uCC40\uCC41\uD4E5\uCC42\uCC43\uCC44\uDEBD"+ + "\uCC45\uCC46\uCC47\uCC48\uCC49\uDEBF\uCC4A\uCC4B"+ + "\uCC4C\uCC4D\uCC4E\uCC4F\uCC50\uCC51\uCC52\uCC53"+ + "\uCC54\uC4A2\uCC55\uCC56\uCC57\uCC58\uDEC1\uCC59"+ + "\uCC5A\uCC5B\uCC5C\uCC5D\uCC5E\uCC5F\uCC60\uCC61"+ + "\uCC62\uCC63\uCC64\uCC65\uCC66\uCC67\uCC68\uDEBE"+ + "\uCC69\uDEC0\uCC6A\uCC6B\uCC6C\uCC6D\uCC6E\uCC6F"+ + "\uCC70\uCC71\uCC72\uCC73\uCC74\uCC75\uCC76\uCC77"+ + "\uD5BA\uCC78\uCC79\uCC7A\uDEC2\uCC7B\uCC7C\uCC7D"+ + "\uCC7E\uCC80\uCC81\uCC82\uCC83\uCC84\uCC85\uCC86"+ + "\uCC87\uCC88\uCC89\uCC8A\uCC8B\uF2AE\uBBA2\uC2B2"+ + "\uC5B0\uC2C7\uCC8C\uCC8D\uF2AF\uCC8E\uCC8F\uCC90"+ + "\uCC91\uCC92\uD0E9\uCC93\uCC94\uCC95\uD3DD\uCC96"+ + "\uCC97\uCC98\uEBBD\uCC99\uCC9A\uCC9B\uCC9C\uCC9D"+ + "\uCC9E\uCC9F\uCCA0\uB3E6\uF2B0\uCD40\uF2B1\uCD41"+ + "\uCD42\uCAAD\uCD43\uCD44\uCD45\uCD46\uCD47\uCD48"+ + "\uCD49\uBAE7\uF2B3\uF2B5\uF2B4\uCBE4\uCFBA\uF2B2"+ + "\uCAB4\uD2CF\uC2EC\uCD4A\uCD4B\uCD4C\uCD4D\uCD4E"+ + "\uCD4F\uCD50\uCEC3\uF2B8\uB0F6\uF2B7\uCD51\uCD52"+ + "\uCD53\uCD54\uCD55\uF2BE\uCD56\uB2CF\uCD57\uCD58"+ + "\uCD59\uCD5A\uCD5B\uCD5C\uD1C1\uF2BA\uCD5D\uCD5E"+ + "\uCD5F\uCD60\uCD61\uF2BC\uD4E9\uCD62\uCD63\uF2BB"+ + "\uF2B6\uF2BF\uF2BD\uCD64\uF2B9\uCD65\uCD66\uF2C7"+ + "\uF2C4\uF2C6\uCD67\uCD68\uF2CA\uF2C2\uF2C0\uCD69"+ + "\uCD6A\uCD6B\uF2C5\uCD6C\uCD6D\uCD6E\uCD6F\uCD70"+ + "\uD6FB\uCD71\uCD72\uCD73\uF2C1\uCD74\uC7F9\uC9DF"+ + "\uCD75\uF2C8\uB9C6\uB5B0\uCD76\uCD77\uF2C3\uF2C9"+ + "\uF2D0\uF2D6\uCD78\uCD79\uBBD7\uCD7A\uCD7B\uCD7C"+ + "\uF2D5\uCDDC\uCD7D\uD6EB\uCD7E\uCD80\uF2D2\uF2D4"+ + "\uCD81\uCD82\uCD83\uCD84\uB8F2\uCD85\uCD86\uCD87"+ + "\uCD88\uF2CB\uCD89\uCD8A\uCD8B\uF2CE\uC2F9\uCD8C"+ + "\uD5DD\uF2CC\uF2CD\uF2CF\uF2D3\uCD8D\uCD8E\uCD8F"+ + "\uF2D9\uD3BC\uCD90\uCD91\uCD92\uCD93\uB6EA\uCD94"+ + "\uCAF1\uCD95\uB7E4\uF2D7\uCD96\uCD97\uCD98\uF2D8"+ + "\uF2DA\uF2DD\uF2DB\uCD99\uCD9A\uF2DC\uCD9B\uCD9C"+ + "\uCD9D\uCD9E\uD1D1\uF2D1\uCD9F\uCDC9\uCDA0\uCECF"+ + "\uD6A9\uCE40\uF2E3\uCE41\uC3DB\uCE42\uF2E0\uCE43"+ + "\uCE44\uC0AF\uF2EC\uF2DE\uCE45\uF2E1\uCE46\uCE47"+ + "\uCE48\uF2E8\uCE49\uCE4A\uCE4B\uCE4C\uF2E2\uCE4D"+ + "\uCE4E\uF2E7\uCE4F\uCE50\uF2E6\uCE51\uCE52\uF2E9"+ + "\uCE53\uCE54\uCE55\uF2DF\uCE56\uCE57\uF2E4\uF2EA"+ + "\uCE58\uCE59\uCE5A\uCE5B\uCE5C\uCE5D\uCE5E\uD3AC"+ + "\uF2E5\uB2F5\uCE5F\uCE60\uF2F2\uCE61\uD0AB\uCE62"+ + "\uCE63\uCE64\uCE65\uF2F5\uCE66\uCE67\uCE68\uBBC8"+ + "\uCE69\uF2F9\uCE6A\uCE6B\uCE6C\uCE6D\uCE6E\uCE6F"+ + "\uF2F0\uCE70\uCE71\uF2F6\uF2F8\uF2FA\uCE72\uCE73"+ + "\uCE74\uCE75\uCE76\uCE77\uCE78\uCE79\uF2F3\uCE7A"+ + "\uF2F1\uCE7B\uCE7C\uCE7D\uBAFB\uCE7E\uB5FB\uCE80"+ + "\uCE81\uCE82\uCE83\uF2EF\uF2F7\uF2ED\uF2EE\uCE84"+ + "\uCE85\uCE86\uF2EB\uF3A6\uCE87\uF3A3\uCE88\uCE89"+ + "\uF3A2\uCE8A\uCE8B\uF2F4\uCE8C\uC8DA\uCE8D\uCE8E"+ + "\uCE8F\uCE90\uCE91\uF2FB\uCE92\uCE93\uCE94\uF3A5"+ + "\uCE95\uCE96\uCE97\uCE98\uCE99\uCE9A\uCE9B\uC3F8"+ + "\uCE9C\uCE9D\uCE9E\uCE9F\uCEA0\uCF40\uCF41\uCF42"+ + "\uF2FD\uCF43\uCF44\uF3A7\uF3A9\uF3A4\uCF45\uF2FC"+ + "\uCF46\uCF47\uCF48\uF3AB\uCF49\uF3AA\uCF4A\uCF4B"+ + "\uCF4C\uCF4D\uC2DD\uCF4E\uCF4F\uF3AE\uCF50\uCF51"+ + "\uF3B0\uCF52\uCF53\uCF54\uCF55\uCF56\uF3A1\uCF57"+ + "\uCF58\uCF59\uF3B1\uF3AC\uCF5A\uCF5B\uCF5C\uCF5D"+ + "\uCF5E\uF3AF\uF2FE\uF3AD\uCF5F\uCF60\uCF61\uCF62"+ + "\uCF63\uCF64\uCF65\uF3B2\uCF66\uCF67\uCF68\uCF69"+ + "\uF3B4\uCF6A\uCF6B\uCF6C\uCF6D\uF3A8\uCF6E\uCF6F"+ + "\uCF70\uCF71\uF3B3\uCF72\uCF73\uCF74\uF3B5\uCF75"+ + "\uCF76\uCF77\uCF78\uCF79\uCF7A\uCF7B\uCF7C\uCF7D"+ + "\uCF7E\uD0B7\uCF80\uCF81\uCF82\uCF83\uF3B8\uCF84"+ + "\uCF85\uCF86\uCF87\uD9F9\uCF88\uCF89\uCF8A\uCF8B"+ + "\uCF8C\uCF8D\uF3B9\uCF8E\uCF8F\uCF90\uCF91\uCF92"+ + "\uCF93\uCF94\uCF95\uF3B7\uCF96\uC8E4\uF3B6\uCF97"+ + "\uCF98\uCF99\uCF9A\uF3BA\uCF9B\uCF9C\uCF9D\uCF9E"+ + "\uCF9F\uF3BB\uB4C0\uCFA0\uD040\uD041\uD042\uD043"+ + "\uD044\uD045\uD046\uD047\uD048\uD049\uD04A\uD04B"+ + "\uD04C\uD04D\uEEC3\uD04E\uD04F\uD050\uD051\uD052"+ + "\uD053\uF3BC\uD054\uD055\uF3BD\uD056\uD057\uD058"+ + "\uD1AA\uD059\uD05A\uD05B\uF4AC\uD0C6\uD05C\uD05D"+ + "\uD05E\uD05F\uD060\uD061\uD0D0\uD1DC\uD062\uD063"+ + "\uD064\uD065\uD066\uD067\uCFCE\uD068\uD069\uBDD6"+ + "\uD06A\uD1C3\uD06B\uD06C\uD06D\uD06E\uD06F\uD070"+ + "\uD071\uBAE2\uE1E9\uD2C2\uF1C2\uB2B9\uD072\uD073"+ + "\uB1ED\uF1C3\uD074\uC9C0\uB3C4\uD075\uD9F2\uD076"+ + "\uCBA5\uD077\uF1C4\uD078\uD079\uD07A\uD07B\uD6D4"+ + "\uD07C\uD07D\uD07E\uD080\uD081\uF1C5\uF4C0\uF1C6"+ + "\uD082\uD4AC\uF1C7\uD083\uB0C0\uF4C1\uD084\uD085"+ + "\uF4C2\uD086\uD087\uB4FC\uD088\uC5DB\uD089\uD08A"+ + "\uD08B\uD08C\uCCBB\uD08D\uD08E\uD08F\uD0E4\uD090"+ + "\uD091\uD092\uD093\uD094\uCDE0\uD095\uD096\uD097"+ + "\uD098\uD099\uF1C8\uD09A\uD9F3\uD09B\uD09C\uD09D"+ + "\uD09E\uD09F\uD0A0\uB1BB\uD140\uCFAE\uD141\uD142"+ + "\uD143\uB8A4\uD144\uD145\uD146\uD147\uD148\uF1CA"+ + "\uD149\uD14A\uD14B\uD14C\uF1CB\uD14D\uD14E\uD14F"+ + "\uD150\uB2C3\uC1D1\uD151\uD152\uD7B0\uF1C9\uD153"+ + "\uD154\uF1CC\uD155\uD156\uD157\uD158\uF1CE\uD159"+ + "\uD15A\uD15B\uD9F6\uD15C\uD2E1\uD4A3\uD15D\uD15E"+ + "\uF4C3\uC8B9\uD15F\uD160\uD161\uD162\uD163\uF4C4"+ + "\uD164\uD165\uF1CD\uF1CF\uBFE3\uF1D0\uD166\uD167"+ + "\uF1D4\uD168\uD169\uD16A\uD16B\uD16C\uD16D\uD16E"+ + "\uF1D6\uF1D1\uD16F\uC9D1\uC5E1\uD170\uD171\uD172"+ + "\uC2E3\uB9FC\uD173\uD174\uF1D3\uD175\uF1D5\uD176"+ + "\uD177\uD178\uB9D3\uD179\uD17A\uD17B\uD17C\uD17D"+ + "\uD17E\uD180\uF1DB\uD181\uD182\uD183\uD184\uD185"+ + "\uBAD6\uD186\uB0FD\uF1D9\uD187\uD188\uD189\uD18A"+ + "\uD18B\uF1D8\uF1D2\uF1DA\uD18C\uD18D\uD18E\uD18F"+ + "\uD190\uF1D7\uD191\uD192\uD193\uC8EC\uD194\uD195"+ + "\uD196\uD197\uCDCA\uF1DD\uD198\uD199\uD19A\uD19B"+ + "\uE5BD\uD19C\uD19D\uD19E\uF1DC\uD19F\uF1DE\uD1A0"+ + "\uD240\uD241\uD242\uD243\uD244\uD245\uD246\uD247"+ + "\uD248\uF1DF\uD249\uD24A\uCFE5\uD24B\uD24C\uD24D"+ + "\uD24E\uD24F\uD250\uD251\uD252\uD253\uD254\uD255"+ + "\uD256\uD257\uD258\uD259\uD25A\uD25B\uD25C\uD25D"+ + "\uD25E\uD25F\uD260\uD261\uD262\uD263\uF4C5\uBDF3"+ + "\uD264\uD265\uD266\uD267\uD268\uD269\uF1E0\uD26A"+ + "\uD26B\uD26C\uD26D\uD26E\uD26F\uD270\uD271\uD272"+ + "\uD273\uD274\uD275\uD276\uD277\uD278\uD279\uD27A"+ + "\uD27B\uD27C\uD27D\uF1E1\uD27E\uD280\uD281\uCEF7"+ + "\uD282\uD2AA\uD283\uF1FB\uD284\uD285\uB8B2\uD286"+ + "\uD287\uD288\uD289\uD28A\uD28B\uD28C\uD28D\uD28E"+ + "\uD28F\uD290\uD291\uD292\uD293\uD294\uD295\uD296"+ + "\uD297\uD298\uD299\uD29A\uD29B\uD29C\uD29D\uD29E"+ + "\uD29F\uD2A0\uD340\uD341\uD342\uD343\uD344\uD345"+ + "\uD346\uD347\uD348\uD349\uD34A\uD34B\uD34C\uD34D"+ + "\uD34E\uD34F\uD350\uD351\uD352\uD353\uD354\uD355"+ + "\uD356\uD357\uD358\uD359\uD35A\uD35B\uD35C\uD35D"+ + "\uD35E\uBCFB\uB9DB\uD35F\uB9E6\uC3D9\uCAD3\uEAE8"+ + "\uC0C0\uBEF5\uEAE9\uEAEA\uEAEB\uD360\uEAEC\uEAED"+ + "\uEAEE\uEAEF\uBDC7\uD361\uD362\uD363\uF5FB\uD364"+ + "\uD365\uD366\uF5FD\uD367\uF5FE\uD368\uF5FC\uD369"+ + "\uD36A\uD36B\uD36C\uBDE2\uD36D\uF6A1\uB4A5\uD36E"+ + "\uD36F\uD370\uD371\uF6A2\uD372\uD373\uD374\uF6A3"+ + "\uD375\uD376\uD377\uECB2\uD378\uD379\uD37A\uD37B"+ + "\uD37C\uD37D\uD37E\uD380\uD381\uD382\uD383\uD384"+ + "\uD1D4\uD385\uD386\uD387\uD388\uD389\uD38A\uD9EA"+ + "\uD38B\uD38C\uD38D\uD38E\uD38F\uD390\uD391\uD392"+ + "\uD393\uD394\uD395\uD396\uD397\uD398\uD399\uD39A"+ + "\uD39B\uD39C\uD39D\uD39E\uD39F\uD3A0\uD440\uD441"+ + "\uD442\uD443\uD444\uD445\uD446\uD447\uD448\uD449"+ + "\uD44A\uD44B\uD44C\uD44D\uD44E\uD44F\uD450\uD451"+ + "\uD452\uD453\uD454\uD455\uD456\uD457\uD458\uD459"+ + "\uD45A\uD45B\uD45C\uD45D\uD45E\uD45F\uF6A4\uD460"+ + "\uD461\uD462\uD463\uD464\uD465\uD466\uD467\uD468"+ + "\uEEBA\uD469\uD46A\uD46B\uD46C\uD46D\uD46E\uD46F"+ + "\uD470\uD471\uD472\uD473\uD474\uD475\uD476\uD477"+ + "\uD478\uD479\uD47A\uD47B\uD47C\uD47D\uD47E\uD480"+ + "\uD481\uD482\uD483\uD484\uD485\uD486\uD487\uD488"+ + "\uD489\uD48A\uD48B\uD48C\uD48D\uD48E\uD48F\uD490"+ + "\uD491\uD492\uD493\uD494\uD495\uD496\uD497\uD498"+ + "\uD499\uD5B2\uD49A\uD49B\uD49C\uD49D\uD49E\uD49F"+ + "\uD4A0\uD540\uD541\uD542\uD543\uD544\uD545\uD546"+ + "\uD547\uD3FE\uCCDC\uD548\uD549\uD54A\uD54B\uD54C"+ + "\uD54D\uD54E\uD54F\uCAC4\uD550\uD551\uD552\uD553"+ + "\uD554\uD555\uD556\uD557\uD558\uD559\uD55A\uD55B"+ + "\uD55C\uD55D\uD55E\uD55F\uD560\uD561\uD562\uD563"+ + "\uD564\uD565\uD566\uD567\uD568\uD569\uD56A\uD56B"+ + "\uD56C\uD56D\uD56E\uD56F\uD570\uD571\uD572\uD573"+ + "\uD574\uD575\uD576\uD577\uD578\uD579\uD57A\uD57B"+ + "\uD57C\uD57D\uD57E\uD580\uD581\uD582\uD583\uD584"+ + "\uD585\uD586\uD587\uD588\uD589\uD58A\uD58B\uD58C"+ + "\uD58D\uD58E\uD58F\uD590\uD591\uD592\uD593\uD594"+ + "\uD595\uD596\uD597\uD598\uD599\uD59A\uD59B\uD59C"+ + "\uD59D\uD59E\uD59F\uD5A0\uD640\uD641\uD642\uD643"+ + "\uD644\uD645\uD646\uD647\uD648\uD649\uD64A\uD64B"+ + "\uD64C\uD64D\uD64E\uD64F\uD650\uD651\uD652\uD653"+ + "\uD654\uD655\uD656\uD657\uD658\uD659\uD65A\uD65B"+ + "\uD65C\uD65D\uD65E\uD65F\uD660\uD661\uD662\uE5C0"+ + "\uD663\uD664\uD665\uD666\uD667\uD668\uD669\uD66A"+ + "\uD66B\uD66C\uD66D\uD66E\uD66F\uD670\uD671\uD672"+ + "\uD673\uD674\uD675\uD676\uD677\uD678\uD679\uD67A"+ + "\uD67B\uD67C\uD67D\uD67E\uD680\uD681\uF6A5\uD682"+ + "\uD683\uD684\uD685\uD686\uD687\uD688\uD689\uD68A"+ + "\uD68B\uD68C\uD68D\uD68E\uD68F\uD690\uD691\uD692"+ + "\uD693\uD694\uD695\uD696\uD697\uD698\uD699\uD69A"+ + "\uD69B\uD69C\uD69D\uD69E\uD69F\uD6A0\uD740\uD741"+ + "\uD742\uD743\uD744\uD745\uD746\uD747\uD748\uD749"+ + "\uD74A\uD74B\uD74C\uD74D\uD74E\uD74F\uD750\uD751"+ + "\uD752\uD753\uD754\uD755\uD756\uD757\uD758\uD759"+ + "\uD75A\uD75B\uD75C\uD75D\uD75E\uD75F\uBEAF\uD760"+ + "\uD761\uD762\uD763\uD764\uC6A9\uD765\uD766\uD767"+ + "\uD768\uD769\uD76A\uD76B\uD76C\uD76D\uD76E\uD76F"+ + "\uD770\uD771\uD772\uD773\uD774\uD775\uD776\uD777"+ + "\uD778\uD779\uD77A\uD77B\uD77C\uD77D\uD77E\uD780"+ + "\uD781\uD782\uD783\uD784\uD785\uD786\uD787\uD788"+ + "\uD789\uD78A\uD78B\uD78C\uD78D\uD78E\uD78F\uD790"+ + "\uD791\uD792\uD793\uD794\uD795\uD796\uD797\uD798"+ + "\uDAA5\uBCC6\uB6A9\uB8BC\uC8CF\uBCA5\uDAA6\uDAA7"+ + "\uCCD6\uC8C3\uDAA8\uC6FD\uD799\uD1B5\uD2E9\uD1B6"+ + "\uBCC7\uD79A\uBDB2\uBBE4\uDAA9\uDAAA\uD1C8\uDAAB"+ + "\uD0ED\uB6EF\uC2DB\uD79B\uCBCF\uB7ED\uC9E8\uB7C3"+ + "\uBEF7\uD6A4\uDAAC\uDAAD\uC6C0\uD7E7\uCAB6\uD79C"+ + "\uD5A9\uCBDF\uD5EF\uDAAE\uD6DF\uB4CA\uDAB0\uDAAF"+ + "\uD79D\uD2EB\uDAB1\uDAB2\uDAB3\uCAD4\uDAB4\uCAAB"+ + "\uDAB5\uDAB6\uB3CF\uD6EF\uDAB7\uBBB0\uB5AE\uDAB8"+ + "\uDAB9\uB9EE\uD1AF\uD2E8\uDABA\uB8C3\uCFEA\uB2EF"+ + "\uDABB\uDABC\uD79E\uBDEB\uCEDC\uD3EF\uDABD\uCEF3"+ + "\uDABE\uD3D5\uBBE5\uDABF\uCBB5\uCBD0\uDAC0\uC7EB"+ + "\uD6EE\uDAC1\uC5B5\uB6C1\uDAC2\uB7CC\uBFCE\uDAC3"+ + "\uDAC4\uCBAD\uDAC5\uB5F7\uDAC6\uC1C2\uD7BB\uDAC7"+ + "\uCCB8\uD79F\uD2EA\uC4B1\uDAC8\uB5FD\uBBD1\uDAC9"+ + "\uD0B3\uDACA\uDACB\uCEBD\uDACC\uDACD\uDACE\uB2F7"+ + "\uDAD1\uDACF\uD1E8\uDAD0\uC3D5\uDAD2\uD7A0\uDAD3"+ + "\uDAD4\uDAD5\uD0BB\uD2A5\uB0F9\uDAD6\uC7AB\uDAD7"+ + "\uBDF7\uC3A1\uDAD8\uDAD9\uC3FD\uCCB7\uDADA\uDADB"+ + "\uC0BE\uC6D7\uDADC\uDADD\uC7B4\uDADE\uDADF\uB9C8"+ + "\uD840\uD841\uD842\uD843\uD844\uD845\uD846\uD847"+ + "\uD848\uBBED\uD849\uD84A\uD84B\uD84C\uB6B9\uF4F8"+ + "\uD84D\uF4F9\uD84E\uD84F\uCDE3\uD850\uD851\uD852"+ + "\uD853\uD854\uD855\uD856\uD857\uF5B9\uD858\uD859"+ + "\uD85A\uD85B\uEBE0\uD85C\uD85D\uD85E\uD85F\uD860"+ + "\uD861\uCFF3\uBBBF\uD862\uD863\uD864\uD865\uD866"+ + "\uD867\uD868\uBAC0\uD4A5\uD869\uD86A\uD86B\uD86C"+ + "\uD86D\uD86E\uD86F\uE1D9\uD870\uD871\uD872\uD873"+ + "\uF5F4\uB1AA\uB2F2\uD874\uD875\uD876\uD877\uD878"+ + "\uD879\uD87A\uF5F5\uD87B\uD87C\uF5F7\uD87D\uD87E"+ + "\uD880\uBAD1\uF5F6\uD881\uC3B2\uD882\uD883\uD884"+ + "\uD885\uD886\uD887\uD888\uF5F9\uD889\uD88A\uD88B"+ + "\uF5F8\uD88C\uD88D\uD88E\uD88F\uD890\uD891\uD892"+ + "\uD893\uD894\uD895\uD896\uD897\uD898\uD899\uD89A"+ + "\uD89B\uD89C\uD89D\uD89E\uD89F\uD8A0\uD940\uD941"+ + "\uD942\uD943\uD944\uD945\uD946\uD947\uD948\uD949"+ + "\uD94A\uD94B\uD94C\uD94D\uD94E\uD94F\uD950\uD951"+ + "\uD952\uD953\uD954\uD955\uD956\uD957\uD958\uD959"+ + "\uD95A\uD95B\uD95C\uD95D\uD95E\uD95F\uD960\uD961"+ + "\uD962\uD963\uD964\uD965\uD966\uD967\uD968\uD969"+ + "\uD96A\uD96B\uD96C\uD96D\uD96E\uD96F\uD970\uD971"+ + "\uD972\uD973\uD974\uD975\uD976\uD977\uD978\uD979"+ + "\uD97A\uD97B\uD97C\uD97D\uD97E\uD980\uD981\uD982"+ + "\uD983\uD984\uD985\uD986\uD987\uD988\uD989\uD98A"+ + "\uD98B\uD98C\uD98D\uD98E\uD98F\uD990\uD991\uD992"+ + "\uD993\uD994\uD995\uD996\uD997\uD998\uD999\uD99A"+ + "\uD99B\uD99C\uD99D\uD99E\uD99F\uD9A0\uDA40\uDA41"+ + "\uDA42\uDA43\uDA44\uDA45\uDA46\uDA47\uDA48\uDA49"+ + "\uDA4A\uDA4B\uDA4C\uDA4D\uDA4E\uB1B4\uD5EA\uB8BA"+ + "\uDA4F\uB9B1\uB2C6\uD4F0\uCFCD\uB0DC\uD5CB\uBBF5"+ + "\uD6CA\uB7B7\uCCB0\uC6B6\uB1E1\uB9BA\uD6FC\uB9E1"+ + "\uB7A1\uBCFA\uEADA\uEADB\uCCF9\uB9F3\uEADC\uB4FB"+ + "\uC3B3\uB7D1\uBAD8\uEADD\uD4F4\uEADE\uBCD6\uBBDF"+ + "\uEADF\uC1DE\uC2B8\uD4DF\uD7CA\uEAE0\uEAE1\uEAE4"+ + "\uEAE2\uEAE3\uC9DE\uB8B3\uB6C4\uEAE5\uCAEA\uC9CD"+ + "\uB4CD\uDA50\uDA51\uE2D9\uC5E2\uEAE6\uC0B5\uDA52"+ + "\uD7B8\uEAE7\uD7AC\uC8FC\uD8D3\uD8CD\uD4DE\uDA53"+ + "\uD4F9\uC9C4\uD3AE\uB8D3\uB3E0\uDA54\uC9E2\uF4F6"+ + "\uDA55\uDA56\uDA57\uBAD5\uDA58\uF4F7\uDA59\uDA5A"+ + "\uD7DF\uDA5B\uDA5C\uF4F1\uB8B0\uD5D4\uB8CF\uC6F0"+ + "\uDA5D\uDA5E\uDA5F\uDA60\uDA61\uDA62\uDA63\uDA64"+ + "\uDA65\uB3C3\uDA66\uDA67\uF4F2\uB3AC\uDA68\uDA69"+ + "\uDA6A\uDA6B\uD4BD\uC7F7\uDA6C\uDA6D\uDA6E\uDA6F"+ + "\uDA70\uF4F4\uDA71\uDA72\uF4F3\uDA73\uDA74\uDA75"+ + "\uDA76\uDA77\uDA78\uDA79\uDA7A\uDA7B\uDA7C\uCCCB"+ + "\uDA7D\uDA7E\uDA80\uC8A4\uDA81\uDA82\uDA83\uDA84"+ + "\uDA85\uDA86\uDA87\uDA88\uDA89\uDA8A\uDA8B\uDA8C"+ + "\uDA8D\uF4F5\uDA8E\uD7E3\uC5BF\uF5C0\uDA8F\uDA90"+ + "\uF5BB\uDA91\uF5C3\uDA92\uF5C2\uDA93\uD6BA\uF5C1"+ + "\uDA94\uDA95\uDA96\uD4BE\uF5C4\uDA97\uF5CC\uDA98"+ + "\uDA99\uDA9A\uDA9B\uB0CF\uB5F8\uDA9C\uF5C9\uF5CA"+ + "\uDA9D\uC5DC\uDA9E\uDA9F\uDAA0\uDB40\uF5C5\uF5C6"+ + "\uDB41\uDB42\uF5C7\uF5CB\uDB43\uBEE0\uF5C8\uB8FA"+ + "\uDB44\uDB45\uDB46\uF5D0\uF5D3\uDB47\uDB48\uDB49"+ + "\uBFE7\uDB4A\uB9F2\uF5BC\uF5CD\uDB4B\uDB4C\uC2B7"+ + "\uDB4D\uDB4E\uDB4F\uCCF8\uDB50\uBCF9\uDB51\uF5CE"+ + "\uF5CF\uF5D1\uB6E5\uF5D2\uDB52\uF5D5\uDB53\uDB54"+ + "\uDB55\uDB56\uDB57\uDB58\uDB59\uF5BD\uDB5A\uDB5B"+ + "\uDB5C\uF5D4\uD3BB\uDB5D\uB3EC\uDB5E\uDB5F\uCCA4"+ + "\uDB60\uDB61\uDB62\uDB63\uF5D6\uDB64\uDB65\uDB66"+ + "\uDB67\uDB68\uDB69\uDB6A\uDB6B\uF5D7\uBEE1\uF5D8"+ + "\uDB6C\uDB6D\uCCDF\uF5DB\uDB6E\uDB6F\uDB70\uDB71"+ + "\uDB72\uB2C8\uD7D9\uDB73\uF5D9\uDB74\uF5DA\uF5DC"+ + "\uDB75\uF5E2\uDB76\uDB77\uDB78\uF5E0\uDB79\uDB7A"+ + "\uDB7B\uF5DF\uF5DD\uDB7C\uDB7D\uF5E1\uDB7E\uDB80"+ + "\uF5DE\uF5E4\uF5E5\uDB81\uCCE3\uDB82\uDB83\uE5BF"+ + "\uB5B8\uF5E3\uF5E8\uCCA3\uDB84\uDB85\uDB86\uDB87"+ + "\uDB88\uF5E6\uF5E7\uDB89\uDB8A\uDB8B\uDB8C\uDB8D"+ + "\uDB8E\uF5BE\uDB8F\uDB90\uDB91\uDB92\uDB93\uDB94"+ + "\uDB95\uDB96\uDB97\uDB98\uDB99\uDB9A\uB1C4\uDB9B"+ + "\uDB9C\uF5BF\uDB9D\uDB9E\uB5C5\uB2E4\uDB9F\uF5EC"+ + "\uF5E9\uDBA0\uB6D7\uDC40\uF5ED\uDC41\uF5EA\uDC42"+ + "\uDC43\uDC44\uDC45\uDC46\uF5EB\uDC47\uDC48\uB4DA"+ + "\uDC49\uD4EA\uDC4A\uDC4B\uDC4C\uF5EE\uDC4D\uB3F9"+ + "\uDC4E\uDC4F\uDC50\uDC51\uDC52\uDC53\uDC54\uF5EF"+ + "\uF5F1\uDC55\uDC56\uDC57\uF5F0\uDC58\uDC59\uDC5A"+ + "\uDC5B\uDC5C\uDC5D\uDC5E\uF5F2\uDC5F\uF5F3\uDC60"+ + "\uDC61\uDC62\uDC63\uDC64\uDC65\uDC66\uDC67\uDC68"+ + "\uDC69\uDC6A\uDC6B\uC9ED\uB9AA\uDC6C\uDC6D\uC7FB"+ + "\uDC6E\uDC6F\uB6E3\uDC70\uDC71\uDC72\uDC73\uDC74"+ + "\uDC75\uDC76\uCCC9\uDC77\uDC78\uDC79\uDC7A\uDC7B"+ + "\uDC7C\uDC7D\uDC7E\uDC80\uDC81\uDC82\uDC83\uDC84"+ + "\uDC85\uDC86\uDC87\uDC88\uDC89\uDC8A\uEAA6\uDC8B"+ + "\uDC8C\uDC8D\uDC8E\uDC8F\uDC90\uDC91\uDC92\uDC93"+ + "\uDC94\uDC95\uDC96\uDC97\uDC98\uDC99\uDC9A\uDC9B"+ + "\uDC9C\uDC9D\uDC9E\uDC9F\uDCA0\uDD40\uDD41\uDD42"+ + "\uDD43\uDD44\uDD45\uDD46\uDD47\uDD48\uDD49\uDD4A"+ + "\uDD4B\uDD4C\uDD4D\uDD4E\uDD4F\uDD50\uDD51\uDD52"+ + "\uDD53\uDD54\uDD55\uDD56\uDD57\uDD58\uDD59\uDD5A"+ + "\uDD5B\uDD5C\uDD5D\uDD5E\uDD5F\uDD60\uDD61\uDD62"+ + "\uDD63\uDD64\uDD65\uDD66\uDD67\uDD68\uDD69\uDD6A"+ + "\uDD6B\uDD6C\uDD6D\uDD6E\uDD6F\uDD70\uDD71\uDD72"+ + "\uDD73\uDD74\uDD75\uDD76\uDD77\uDD78\uDD79\uDD7A"+ + "\uDD7B\uDD7C\uDD7D\uDD7E\uDD80\uDD81\uDD82\uDD83"+ + "\uDD84\uDD85\uDD86\uDD87\uDD88\uDD89\uDD8A\uDD8B"+ + "\uDD8C\uDD8D\uDD8E\uDD8F\uDD90\uDD91\uDD92\uDD93"+ + "\uDD94\uDD95\uDD96\uDD97\uDD98\uDD99\uDD9A\uDD9B"+ + "\uDD9C\uDD9D\uDD9E\uDD9F\uDDA0\uDE40\uDE41\uDE42"+ + "\uDE43\uDE44\uDE45\uDE46\uDE47\uDE48\uDE49\uDE4A"+ + "\uDE4B\uDE4C\uDE4D\uDE4E\uDE4F\uDE50\uDE51\uDE52"+ + "\uDE53\uDE54\uDE55\uDE56\uDE57\uDE58\uDE59\uDE5A"+ + "\uDE5B\uDE5C\uDE5D\uDE5E\uDE5F\uDE60\uB3B5\uD4FE"+ + "\uB9EC\uD0F9\uDE61\uE9ED\uD7AA\uE9EE\uC2D6\uC8ED"+ + "\uBAE4\uE9EF\uE9F0\uE9F1\uD6E1\uE9F2\uE9F3\uE9F5"+ + "\uE9F4\uE9F6\uE9F7\uC7E1\uE9F8\uD4D8\uE9F9\uBDCE"+ + "\uDE62\uE9FA\uE9FB\uBDCF\uE9FC\uB8A8\uC1BE\uE9FD"+ + "\uB1B2\uBBD4\uB9F5\uE9FE\uDE63\uEAA1\uEAA2\uEAA3"+ + "\uB7F8\uBCAD\uDE64\uCAE4\uE0CE\uD4AF\uCFBD\uD5B7"+ + "\uEAA4\uD5DE\uEAA5\uD0C1\uB9BC\uDE65\uB4C7\uB1D9"+ + "\uDE66\uDE67\uDE68\uC0B1\uDE69\uDE6A\uDE6B\uDE6C"+ + "\uB1E6\uB1E7\uDE6D\uB1E8\uDE6E\uDE6F\uDE70\uDE71"+ + "\uB3BD\uC8E8\uDE72\uDE73\uDE74\uDE75\uE5C1\uDE76"+ + "\uDE77\uB1DF\uDE78\uDE79\uDE7A\uC1C9\uB4EF\uDE7B"+ + "\uDE7C\uC7A8\uD3D8\uDE7D\uC6F9\uD1B8\uDE7E\uB9FD"+ + "\uC2F5\uDE80\uDE81\uDE82\uDE83\uDE84\uD3AD\uDE85"+ + "\uD4CB\uBDFC\uDE86\uE5C2\uB7B5\uE5C3\uDE87\uDE88"+ + "\uBBB9\uD5E2\uDE89\uBDF8\uD4B6\uCEA5\uC1AC\uB3D9"+ + "\uDE8A\uDE8B\uCCF6\uDE8C\uE5C6\uE5C4\uE5C8\uDE8D"+ + "\uE5CA\uE5C7\uB5CF\uC6C8\uDE8E\uB5FC\uE5C5\uDE8F"+ + "\uCAF6\uDE90\uDE91\uE5C9\uDE92\uDE93\uDE94\uC3D4"+ + "\uB1C5\uBCA3\uDE95\uDE96\uDE97\uD7B7\uDE98\uDE99"; + + private final static String innerEncoderIndex9= + "\uCDCB\uCBCD\uCACA\uCCD3\uE5CC\uE5CB\uC4E6\uDE9A"+ + "\uDE9B\uD1A1\uD1B7\uE5CD\uDE9C\uE5D0\uDE9D\uCDB8"+ + "\uD6F0\uE5CF\uB5DD\uDE9E\uCDBE\uDE9F\uE5D1\uB6BA"+ + "\uDEA0\uDF40\uCDA8\uB9E4\uDF41\uCAC5\uB3D1\uCBD9"+ + "\uD4EC\uE5D2\uB7EA\uDF42\uDF43\uDF44\uE5CE\uDF45"+ + "\uDF46\uDF47\uDF48\uDF49\uDF4A\uE5D5\uB4FE\uE5D6"+ + "\uDF4B\uDF4C\uDF4D\uDF4E\uDF4F\uE5D3\uE5D4\uDF50"+ + "\uD2DD\uDF51\uDF52\uC2DF\uB1C6\uDF53\uD3E2\uDF54"+ + "\uDF55\uB6DD\uCBEC\uDF56\uE5D7\uDF57\uDF58\uD3F6"+ + "\uDF59\uDF5A\uDF5B\uDF5C\uDF5D\uB1E9\uDF5E\uB6F4"+ + "\uE5DA\uE5D8\uE5D9\uB5C0\uDF5F\uDF60\uDF61\uD2C5"+ + "\uE5DC\uDF62\uDF63\uE5DE\uDF64\uDF65\uDF66\uDF67"+ + "\uDF68\uDF69\uE5DD\uC7B2\uDF6A\uD2A3\uDF6B\uDF6C"+ + "\uE5DB\uDF6D\uDF6E\uDF6F\uDF70\uD4E2\uD5DA\uDF71"+ + "\uDF72\uDF73\uDF74\uDF75\uE5E0\uD7F1\uDF76\uDF77"+ + "\uDF78\uDF79\uDF7A\uDF7B\uDF7C\uE5E1\uDF7D\uB1DC"+ + "\uD1FB\uDF7E\uE5E2\uE5E4\uDF80\uDF81\uDF82\uDF83"+ + "\uE5E3\uDF84\uDF85\uE5E5\uDF86\uDF87\uDF88\uDF89"+ + "\uDF8A\uD2D8\uDF8B\uB5CB\uDF8C\uE7DF\uDF8D\uDAF5"+ + "\uDF8E\uDAF8\uDF8F\uDAF6\uDF90\uDAF7\uDF91\uDF92"+ + "\uDF93\uDAFA\uD0CF\uC4C7\uDF94\uDF95\uB0EE\uDF96"+ + "\uDF97\uDF98\uD0B0\uDF99\uDAF9\uDF9A\uD3CA\uBAAA"+ + "\uDBA2\uC7F1\uDF9B\uDAFC\uDAFB\uC9DB\uDAFD\uDF9C"+ + "\uDBA1\uD7DE\uDAFE\uC1DA\uDF9D\uDF9E\uDBA5\uDF9F"+ + "\uDFA0\uD3F4\uE040\uE041\uDBA7\uDBA4\uE042\uDBA8"+ + "\uE043\uE044\uBDBC\uE045\uE046\uE047\uC0C9\uDBA3"+ + "\uDBA6\uD6A3\uE048\uDBA9\uE049\uE04A\uE04B\uDBAD"+ + "\uE04C\uE04D\uE04E\uDBAE\uDBAC\uBAC2\uE04F\uE050"+ + "\uE051\uBFA4\uDBAB\uE052\uE053\uE054\uDBAA\uD4C7"+ + "\uB2BF\uE055\uE056\uDBAF\uE057\uB9F9\uE058\uDBB0"+ + "\uE059\uE05A\uE05B\uE05C\uB3BB\uE05D\uE05E\uE05F"+ + "\uB5A6\uE060\uE061\uE062\uE063\uB6BC\uDBB1\uE064"+ + "\uE065\uE066\uB6F5\uE067\uDBB2\uE068\uE069\uE06A"+ + "\uE06B\uE06C\uE06D\uE06E\uE06F\uE070\uE071\uE072"+ + "\uE073\uE074\uE075\uE076\uE077\uE078\uE079\uE07A"+ + "\uE07B\uB1C9\uE07C\uE07D\uE07E\uE080\uDBB4\uE081"+ + "\uE082\uE083\uDBB3\uDBB5\uE084\uE085\uE086\uE087"+ + "\uE088\uE089\uE08A\uE08B\uE08C\uE08D\uE08E\uDBB7"+ + "\uE08F\uDBB6\uE090\uE091\uE092\uE093\uE094\uE095"+ + "\uE096\uDBB8\uE097\uE098\uE099\uE09A\uE09B\uE09C"+ + "\uE09D\uE09E\uE09F\uDBB9\uE0A0\uE140\uDBBA\uE141"+ + "\uE142\uD3CF\uF4FA\uC7F5\uD7C3\uC5E4\uF4FC\uF4FD"+ + "\uF4FB\uE143\uBEC6\uE144\uE145\uE146\uE147\uD0EF"+ + "\uE148\uE149\uB7D3\uE14A\uE14B\uD4CD\uCCAA\uE14C"+ + "\uE14D\uF5A2\uF5A1\uBAA8\uF4FE\uCBD6\uE14E\uE14F"+ + "\uE150\uF5A4\uC0D2\uE151\uB3EA\uE152\uCDAA\uF5A5"+ + "\uF5A3\uBDB4\uF5A8\uE153\uF5A9\uBDCD\uC3B8\uBFE1"+ + "\uCBE1\uF5AA\uE154\uE155\uE156\uF5A6\uF5A7\uC4F0"+ + "\uE157\uE158\uE159\uE15A\uE15B\uF5AC\uE15C\uB4BC"+ + "\uE15D\uD7ED\uE15E\uB4D7\uF5AB\uF5AE\uE15F\uE160"+ + "\uF5AD\uF5AF\uD0D1\uE161\uE162\uE163\uE164\uE165"+ + "\uE166\uE167\uC3D1\uC8A9\uE168\uE169\uE16A\uE16B"+ + "\uE16C\uE16D\uF5B0\uF5B1\uE16E\uE16F\uE170\uE171"+ + "\uE172\uE173\uF5B2\uE174\uE175\uF5B3\uF5B4\uF5B5"+ + "\uE176\uE177\uE178\uE179\uF5B7\uF5B6\uE17A\uE17B"+ + "\uE17C\uE17D\uF5B8\uE17E\uE180\uE181\uE182\uE183"+ + "\uE184\uE185\uE186\uE187\uE188\uE189\uE18A\uB2C9"+ + "\uE18B\uD3D4\uCACD\uE18C\uC0EF\uD6D8\uD2B0\uC1BF"+ + "\uE18D\uBDF0\uE18E\uE18F\uE190\uE191\uE192\uE193"+ + "\uE194\uE195\uE196\uE197\uB8AA\uE198\uE199\uE19A"+ + "\uE19B\uE19C\uE19D\uE19E\uE19F\uE1A0\uE240\uE241"+ + "\uE242\uE243\uE244\uE245\uE246\uE247\uE248\uE249"+ + "\uE24A\uE24B\uE24C\uE24D\uE24E\uE24F\uE250\uE251"+ + "\uE252\uE253\uE254\uE255\uE256\uE257\uE258\uE259"+ + "\uE25A\uE25B\uE25C\uE25D\uE25E\uE25F\uE260\uE261"+ + "\uE262\uE263\uE264\uE265\uE266\uE267\uE268\uE269"+ + "\uE26A\uE26B\uE26C\uE26D\uE26E\uE26F\uE270\uE271"+ + "\uE272\uE273\uE274\uE275\uE276\uE277\uE278\uE279"+ + "\uE27A\uE27B\uE27C\uE27D\uE27E\uE280\uE281\uE282"+ + "\uE283\uE284\uE285\uE286\uE287\uE288\uE289\uE28A"+ + "\uE28B\uE28C\uE28D\uE28E\uE28F\uE290\uE291\uE292"+ + "\uE293\uE294\uE295\uE296\uE297\uE298\uE299\uE29A"+ + "\uE29B\uE29C\uE29D\uE29E\uE29F\uE2A0\uE340\uE341"+ + "\uE342\uE343\uE344\uE345\uE346\uE347\uE348\uE349"+ + "\uE34A\uE34B\uE34C\uE34D\uE34E\uE34F\uE350\uE351"+ + "\uE352\uE353\uE354\uE355\uE356\uE357\uE358\uE359"+ + "\uE35A\uE35B\uE35C\uE35D\uE35E\uE35F\uE360\uE361"+ + "\uE362\uE363\uE364\uE365\uE366\uE367\uE368\uE369"+ + "\uE36A\uE36B\uE36C\uE36D\uBCF8\uE36E\uE36F\uE370"+ + "\uE371\uE372\uE373\uE374\uE375\uE376\uE377\uE378"+ + "\uE379\uE37A\uE37B\uE37C\uE37D\uE37E\uE380\uE381"+ + "\uE382\uE383\uE384\uE385\uE386\uE387\uF6C6\uE388"+ + "\uE389\uE38A\uE38B\uE38C\uE38D\uE38E\uE38F\uE390"+ + "\uE391\uE392\uE393\uE394\uE395\uE396\uE397\uE398"+ + "\uE399\uE39A\uE39B\uE39C\uE39D\uE39E\uE39F\uE3A0"+ + "\uE440\uE441\uE442\uE443\uE444\uE445\uF6C7\uE446"+ + "\uE447\uE448\uE449\uE44A\uE44B\uE44C\uE44D\uE44E"+ + "\uE44F\uE450\uE451\uE452\uE453\uE454\uE455\uE456"+ + "\uE457\uE458\uE459\uE45A\uE45B\uE45C\uE45D\uE45E"+ + "\uF6C8\uE45F\uE460\uE461\uE462\uE463\uE464\uE465"+ + "\uE466\uE467\uE468\uE469\uE46A\uE46B\uE46C\uE46D"+ + "\uE46E\uE46F\uE470\uE471\uE472\uE473\uE474\uE475"+ + "\uE476\uE477\uE478\uE479\uE47A\uE47B\uE47C\uE47D"+ + "\uE47E\uE480\uE481\uE482\uE483\uE484\uE485\uE486"+ + "\uE487\uE488\uE489\uE48A\uE48B\uE48C\uE48D\uE48E"+ + "\uE48F\uE490\uE491\uE492\uE493\uE494\uE495\uE496"+ + "\uE497\uE498\uE499\uE49A\uE49B\uE49C\uE49D\uE49E"+ + "\uE49F\uE4A0\uE540\uE541\uE542\uE543\uE544\uE545"+ + "\uE546\uE547\uE548\uE549\uE54A\uE54B\uE54C\uE54D"+ + "\uE54E\uE54F\uE550\uE551\uE552\uE553\uE554\uE555"+ + "\uE556\uE557\uE558\uE559\uE55A\uE55B\uE55C\uE55D"+ + "\uE55E\uE55F\uE560\uE561\uE562\uE563\uE564\uE565"+ + "\uE566\uE567\uE568\uE569\uE56A\uE56B\uE56C\uE56D"+ + "\uE56E\uE56F\uE570\uE571\uE572\uE573\uF6C9\uE574"+ + "\uE575\uE576\uE577\uE578\uE579\uE57A\uE57B\uE57C"+ + "\uE57D\uE57E\uE580\uE581\uE582\uE583\uE584\uE585"+ + "\uE586\uE587\uE588\uE589\uE58A\uE58B\uE58C\uE58D"+ + "\uE58E\uE58F\uE590\uE591\uE592\uE593\uE594\uE595"+ + "\uE596\uE597\uE598\uE599\uE59A\uE59B\uE59C\uE59D"+ + "\uE59E\uE59F\uF6CA\uE5A0\uE640\uE641\uE642\uE643"+ + "\uE644\uE645\uE646\uE647\uE648\uE649\uE64A\uE64B"+ + "\uE64C\uE64D\uE64E\uE64F\uE650\uE651\uE652\uE653"+ + "\uE654\uE655\uE656\uE657\uE658\uE659\uE65A\uE65B"+ + "\uE65C\uE65D\uE65E\uE65F\uE660\uE661\uE662\uF6CC"+ + "\uE663\uE664\uE665\uE666\uE667\uE668\uE669\uE66A"+ + "\uE66B\uE66C\uE66D\uE66E\uE66F\uE670\uE671\uE672"+ + "\uE673\uE674\uE675\uE676\uE677\uE678\uE679\uE67A"+ + "\uE67B\uE67C\uE67D\uE67E\uE680\uE681\uE682\uE683"+ + "\uE684\uE685\uE686\uE687\uE688\uE689\uE68A\uE68B"+ + "\uE68C\uE68D\uE68E\uE68F\uE690\uE691\uE692\uE693"+ + "\uE694\uE695\uE696\uE697\uE698\uE699\uE69A\uE69B"+ + "\uE69C\uE69D\uF6CB\uE69E\uE69F\uE6A0\uE740\uE741"+ + "\uE742\uE743\uE744\uE745\uE746\uE747\uF7E9\uE748"+ + "\uE749\uE74A\uE74B\uE74C\uE74D\uE74E\uE74F\uE750"+ + "\uE751\uE752\uE753\uE754\uE755\uE756\uE757\uE758"+ + "\uE759\uE75A\uE75B\uE75C\uE75D\uE75E\uE75F\uE760"+ + "\uE761\uE762\uE763\uE764\uE765\uE766\uE767\uE768"+ + "\uE769\uE76A\uE76B\uE76C\uE76D\uE76E\uE76F\uE770"+ + "\uE771\uE772\uE773\uE774\uE775\uE776\uE777\uE778"+ + "\uE779\uE77A\uE77B\uE77C\uE77D\uE77E\uE780\uE781"+ + "\uE782\uE783\uE784\uE785\uE786\uE787\uE788\uE789"+ + "\uE78A\uE78B\uE78C\uE78D\uE78E\uE78F\uE790\uE791"+ + "\uE792\uE793\uE794\uE795\uE796\uE797\uE798\uE799"+ + "\uE79A\uE79B\uE79C\uE79D\uE79E\uE79F\uE7A0\uE840"+ + "\uE841\uE842\uE843\uE844\uE845\uE846\uE847\uE848"+ + "\uE849\uE84A\uE84B\uE84C\uE84D\uE84E\uF6CD\uE84F"+ + "\uE850\uE851\uE852\uE853\uE854\uE855\uE856\uE857"+ + "\uE858\uE859\uE85A\uE85B\uE85C\uE85D\uE85E\uE85F"+ + "\uE860\uE861\uE862\uE863\uE864\uE865\uE866\uE867"+ + "\uE868\uE869\uE86A\uE86B\uE86C\uE86D\uE86E\uE86F"+ + "\uE870\uE871\uE872\uE873\uE874\uE875\uE876\uE877"+ + "\uE878\uE879\uE87A\uF6CE\uE87B\uE87C\uE87D\uE87E"+ + "\uE880\uE881\uE882\uE883\uE884\uE885\uE886\uE887"+ + "\uE888\uE889\uE88A\uE88B\uE88C\uE88D\uE88E\uE88F"+ + "\uE890\uE891\uE892\uE893\uE894\uEEC4\uEEC5\uEEC6"+ + "\uD5EB\uB6A4\uEEC8\uEEC7\uEEC9\uEECA\uC7A5\uEECB"+ + "\uEECC\uE895\uB7B0\uB5F6\uEECD\uEECF\uE896\uEECE"+ + "\uE897\uB8C6\uEED0\uEED1\uEED2\uB6DB\uB3AE\uD6D3"+ + "\uC4C6\uB1B5\uB8D6\uEED3\uEED4\uD4BF\uC7D5\uBEFB"+ + "\uCED9\uB9B3\uEED6\uEED5\uEED8\uEED7\uC5A5\uEED9"+ + "\uEEDA\uC7AE\uEEDB\uC7AF\uEEDC\uB2A7\uEEDD\uEEDE"+ + "\uEEDF\uEEE0\uEEE1\uD7EA\uEEE2\uEEE3\uBCD8\uEEE4"+ + "\uD3CB\uCCFA\uB2AC\uC1E5\uEEE5\uC7A6\uC3AD\uE898"+ + "\uEEE6\uEEE7\uEEE8\uEEE9\uEEEA\uEEEB\uEEEC\uE899"+ + "\uEEED\uEEEE\uEEEF\uE89A\uE89B\uEEF0\uEEF1\uEEF2"+ + "\uEEF4\uEEF3\uE89C\uEEF5\uCDAD\uC2C1\uEEF6\uEEF7"+ + "\uEEF8\uD5A1\uEEF9\uCFB3\uEEFA\uEEFB\uE89D\uEEFC"+ + "\uEEFD\uEFA1\uEEFE\uEFA2\uB8F5\uC3FA\uEFA3\uEFA4"+ + "\uBDC2\uD2BF\uB2F9\uEFA5\uEFA6\uEFA7\uD2F8\uEFA8"+ + "\uD6FD\uEFA9\uC6CC\uE89E\uEFAA\uEFAB\uC1B4\uEFAC"+ + "\uCFFA\uCBF8\uEFAE\uEFAD\uB3FA\uB9F8\uEFAF\uEFB0"+ + "\uD0E2\uEFB1\uEFB2\uB7E6\uD0BF\uEFB3\uEFB4\uEFB5"+ + "\uC8F1\uCCE0\uEFB6\uEFB7\uEFB8\uEFB9\uEFBA\uD5E0"+ + "\uEFBB\uB4ED\uC3AA\uEFBC\uE89F\uEFBD\uEFBE\uEFBF"+ + "\uE8A0\uCEFD\uEFC0\uC2E0\uB4B8\uD7B6\uBDF5\uE940"+ + "\uCFC7\uEFC3\uEFC1\uEFC2\uEFC4\uB6A7\uBCFC\uBEE2"+ + "\uC3CC\uEFC5\uEFC6\uE941\uEFC7\uEFCF\uEFC8\uEFC9"+ + "\uEFCA\uC7C2\uEFF1\uB6CD\uEFCB\uE942\uEFCC\uEFCD"+ + "\uB6C6\uC3BE\uEFCE\uE943\uEFD0\uEFD1\uEFD2\uD5F2"+ + "\uE944\uEFD3\uC4F7\uE945\uEFD4\uC4F8\uEFD5\uEFD6"+ + "\uB8E4\uB0F7\uEFD7\uEFD8\uEFD9\uE946\uEFDA\uEFDB"+ + "\uEFDC\uEFDD\uE947\uEFDE\uBEB5\uEFE1\uEFDF\uEFE0"+ + "\uE948\uEFE2\uEFE3\uC1CD\uEFE4\uEFE5\uEFE6\uEFE7"+ + "\uEFE8\uEFE9\uEFEA\uEFEB\uEFEC\uC0D8\uE949\uEFED"+ + "\uC1AD\uEFEE\uEFEF\uEFF0\uE94A\uE94B\uCFE2\uE94C"+ + "\uE94D\uE94E\uE94F\uE950\uE951\uE952\uE953\uB3A4"+ + "\uE954\uE955\uE956\uE957\uE958\uE959\uE95A\uE95B"+ + "\uE95C\uE95D\uE95E\uE95F\uE960\uE961\uE962\uE963"+ + "\uE964\uE965\uE966\uE967\uE968\uE969\uE96A\uE96B"+ + "\uE96C\uE96D\uE96E\uE96F\uE970\uE971\uE972\uE973"+ + "\uE974\uE975\uE976\uE977\uE978\uE979\uE97A\uE97B"+ + "\uE97C\uE97D\uE97E\uE980\uE981\uE982\uE983\uE984"+ + "\uE985\uE986\uE987\uE988\uE989\uE98A\uE98B\uE98C"+ + "\uE98D\uE98E\uE98F\uE990\uE991\uE992\uE993\uE994"+ + "\uE995\uE996\uE997\uE998\uE999\uE99A\uE99B\uE99C"+ + "\uE99D\uE99E\uE99F\uE9A0\uEA40\uEA41\uEA42\uEA43"+ + "\uEA44\uEA45\uEA46\uEA47\uEA48\uEA49\uEA4A\uEA4B"+ + "\uEA4C\uEA4D\uEA4E\uEA4F\uEA50\uEA51\uEA52\uEA53"+ + "\uEA54\uEA55\uEA56\uEA57\uEA58\uEA59\uEA5A\uEA5B"+ + "\uC3C5\uE3C5\uC9C1\uE3C6\uEA5C\uB1D5\uCECA\uB4B3"+ + "\uC8F2\uE3C7\uCFD0\uE3C8\uBCE4\uE3C9\uE3CA\uC3C6"+ + "\uD5A2\uC4D6\uB9EB\uCEC5\uE3CB\uC3F6\uE3CC\uEA5D"+ + "\uB7A7\uB8F3\uBAD2\uE3CD\uE3CE\uD4C4\uE3CF\uEA5E"+ + "\uE3D0\uD1CB\uE3D1\uE3D2\uE3D3\uE3D4\uD1D6\uE3D5"+ + "\uB2FB\uC0BB\uE3D6\uEA5F\uC0AB\uE3D7\uE3D8\uE3D9"+ + "\uEA60\uE3DA\uE3DB\uEA61\uB8B7\uDAE2\uEA62\uB6D3"+ + "\uEA63\uDAE4\uDAE3\uEA64\uEA65\uEA66\uEA67\uEA68"+ + "\uEA69\uEA6A\uDAE6\uEA6B\uEA6C\uEA6D\uC8EE\uEA6E"+ + "\uEA6F\uDAE5\uB7C0\uD1F4\uD2F5\uD5F3\uBDD7\uEA70"+ + "\uEA71\uEA72\uEA73\uD7E8\uDAE8\uDAE7\uEA74\uB0A2"+ + "\uCDD3\uEA75\uDAE9\uEA76\uB8BD\uBCCA\uC2BD\uC2A4"+ + "\uB3C2\uDAEA\uEA77\uC2AA\uC4B0\uBDB5\uEA78\uEA79"+ + "\uCFDE\uEA7A\uEA7B\uEA7C\uDAEB\uC9C2\uEA7D\uEA7E"+ + "\uEA80\uEA81\uEA82\uB1DD\uEA83\uEA84\uEA85\uDAEC"+ + "\uEA86\uB6B8\uD4BA\uEA87\uB3FD\uEA88\uEA89\uDAED"+ + "\uD4C9\uCFD5\uC5E3\uEA8A\uDAEE\uEA8B\uEA8C\uEA8D"+ + "\uEA8E\uEA8F\uDAEF\uEA90\uDAF0\uC1EA\uCCD5\uCFDD"+ + "\uEA91\uEA92\uEA93\uEA94\uEA95\uEA96\uEA97\uEA98"+ + "\uEA99\uEA9A\uEA9B\uEA9C\uEA9D\uD3E7\uC2A1\uEA9E"+ + "\uDAF1\uEA9F\uEAA0\uCBE5\uEB40\uDAF2\uEB41\uCBE6"+ + "\uD2FE\uEB42\uEB43\uEB44\uB8F4\uEB45\uEB46\uDAF3"+ + "\uB0AF\uCFB6\uEB47\uEB48\uD5CF\uEB49\uEB4A\uEB4B"+ + "\uEB4C\uEB4D\uEB4E\uEB4F\uEB50\uEB51\uEB52\uCBED"+ + "\uEB53\uEB54\uEB55\uEB56\uEB57\uEB58\uEB59\uEB5A"+ + "\uDAF4\uEB5B\uEB5C\uE3C4\uEB5D\uEB5E\uC1A5\uEB5F"+ + "\uEB60\uF6BF\uEB61\uEB62\uF6C0\uF6C1\uC4D1\uEB63"+ + "\uC8B8\uD1E3\uEB64\uEB65\uD0DB\uD1C5\uBCAF\uB9CD"+ + "\uEB66\uEFF4\uEB67\uEB68\uB4C6\uD3BA\uF6C2\uB3FB"+ + "\uEB69\uEB6A\uF6C3\uEB6B\uEB6C\uB5F1\uEB6D\uEB6E"+ + "\uEB6F\uEB70\uEB71\uEB72\uEB73\uEB74\uEB75\uEB76"+ + "\uF6C5\uEB77\uEB78\uEB79\uEB7A\uEB7B\uEB7C\uEB7D"+ + "\uD3EA\uF6A7\uD1A9\uEB7E\uEB80\uEB81\uEB82\uF6A9"+ + "\uEB83\uEB84\uEB85\uF6A8\uEB86\uEB87\uC1E3\uC0D7"+ + "\uEB88\uB1A2\uEB89\uEB8A\uEB8B\uEB8C\uCEED\uEB8D"+ + "\uD0E8\uF6AB\uEB8E\uEB8F\uCFF6\uEB90\uF6AA\uD5F0"+ + "\uF6AC\uC3B9\uEB91\uEB92\uEB93\uBBF4\uF6AE\uF6AD"+ + "\uEB94\uEB95\uEB96\uC4DE\uEB97\uEB98\uC1D8\uEB99"+ + "\uEB9A\uEB9B\uEB9C\uEB9D\uCBAA\uEB9E\uCFBC\uEB9F"+ + "\uEBA0\uEC40\uEC41\uEC42\uEC43\uEC44\uEC45\uEC46"+ + "\uEC47\uEC48\uF6AF\uEC49\uEC4A\uF6B0\uEC4B\uEC4C"+ + "\uF6B1\uEC4D\uC2B6\uEC4E\uEC4F\uEC50\uEC51\uEC52"+ + "\uB0D4\uC5F9\uEC53\uEC54\uEC55\uEC56\uF6B2\uEC57"+ + "\uEC58\uEC59\uEC5A\uEC5B\uEC5C\uEC5D\uEC5E\uEC5F"+ + "\uEC60\uEC61\uEC62\uEC63\uEC64\uEC65\uEC66\uEC67"+ + "\uEC68\uEC69\uC7E0\uF6A6\uEC6A\uEC6B\uBEB8\uEC6C"+ + "\uEC6D\uBEB2\uEC6E\uB5E5\uEC6F\uEC70\uB7C7\uEC71"+ + "\uBFBF\uC3D2\uC3E6\uEC72\uEC73\uD8CC\uEC74\uEC75"+ + "\uEC76\uB8EF\uEC77\uEC78\uEC79\uEC7A\uEC7B\uEC7C"+ + "\uEC7D\uEC7E\uEC80\uBDF9\uD1A5\uEC81\uB0D0\uEC82"+ + "\uEC83\uEC84\uEC85\uEC86\uF7B0\uEC87\uEC88\uEC89"+ + "\uEC8A\uEC8B\uEC8C\uEC8D\uEC8E\uF7B1\uEC8F\uEC90"+ + "\uEC91\uEC92\uEC93\uD0AC\uEC94\uB0B0\uEC95\uEC96"+ + "\uEC97\uF7B2\uF7B3\uEC98\uF7B4\uEC99\uEC9A\uEC9B"+ + "\uC7CA\uEC9C\uEC9D\uEC9E\uEC9F\uECA0\uED40\uED41"+ + "\uBECF\uED42\uED43\uF7B7\uED44\uED45\uED46\uED47"+ + "\uED48\uED49\uED4A\uF7B6\uED4B\uB1DE\uED4C\uF7B5"+ + "\uED4D\uED4E\uF7B8\uED4F\uF7B9\uED50\uED51\uED52"+ + "\uED53\uED54\uED55\uED56\uED57\uED58\uED59\uED5A"+ + "\uED5B\uED5C\uED5D\uED5E\uED5F\uED60\uED61\uED62"+ + "\uED63\uED64\uED65\uED66\uED67\uED68\uED69\uED6A"+ + "\uED6B\uED6C\uED6D\uED6E\uED6F\uED70\uED71\uED72"+ + "\uED73\uED74\uED75\uED76\uED77\uED78\uED79\uED7A"+ + "\uED7B\uED7C\uED7D\uED7E\uED80\uED81\uCEA4\uC8CD"+ + "\uED82\uBAAB\uE8B8\uE8B9\uE8BA\uBEC2\uED83\uED84"+ + "\uED85\uED86\uED87\uD2F4\uED88\uD4CF\uC9D8\uED89"+ + "\uED8A\uED8B\uED8C\uED8D\uED8E\uED8F\uED90\uED91"+ + "\uED92\uED93\uED94\uED95\uED96\uED97\uED98\uED99"+ + "\uED9A\uED9B\uED9C\uED9D\uED9E\uED9F\uEDA0\uEE40"+ + "\uEE41\uEE42\uEE43\uEE44\uEE45\uEE46\uEE47\uEE48"+ + "\uEE49\uEE4A\uEE4B\uEE4C\uEE4D\uEE4E\uEE4F\uEE50"+ + "\uEE51\uEE52\uEE53\uEE54\uEE55\uEE56\uEE57\uEE58"+ + "\uEE59\uEE5A\uEE5B\uEE5C\uEE5D\uEE5E\uEE5F\uEE60"+ + "\uEE61\uEE62\uEE63\uEE64\uEE65\uEE66\uEE67\uEE68"+ + "\uEE69\uEE6A\uEE6B\uEE6C\uEE6D\uEE6E\uEE6F\uEE70"+ + "\uEE71\uEE72\uEE73\uEE74\uEE75\uEE76\uEE77\uEE78"+ + "\uEE79\uEE7A\uEE7B\uEE7C\uEE7D\uEE7E\uEE80\uEE81"+ + "\uEE82\uEE83\uEE84\uEE85\uEE86\uEE87\uEE88\uEE89"+ + "\uEE8A\uEE8B\uEE8C\uEE8D\uEE8E\uEE8F\uEE90\uEE91"+ + "\uEE92\uEE93\uEE94\uEE95\uEE96\uEE97\uEE98\uEE99"+ + "\uEE9A\uEE9B\uEE9C\uEE9D\uEE9E\uEE9F\uEEA0\uEF40"+ + "\uEF41\uEF42\uEF43\uEF44\uEF45\uD2B3\uB6A5\uC7EA"+ + "\uF1FC\uCFEE\uCBB3\uD0EB\uE7EF\uCDE7\uB9CB\uB6D9"+ + "\uF1FD\uB0E4\uCBCC\uF1FE\uD4A4\uC2AD\uC1EC\uC6C4"+ + "\uBEB1\uF2A1\uBCD5\uEF46\uF2A2\uF2A3\uEF47\uF2A4"+ + "\uD2C3\uC6B5\uEF48\uCDC7\uF2A5\uEF49\uD3B1\uBFC5"+ + "\uCCE2\uEF4A\uF2A6\uF2A7\uD1D5\uB6EE\uF2A8\uF2A9"+ + "\uB5DF\uF2AA\uF2AB\uEF4B\uB2FC\uF2AC\uF2AD\uC8A7"+ + "\uEF4C\uEF4D\uEF4E\uEF4F\uEF50\uEF51\uEF52\uEF53"+ + "\uEF54\uEF55\uEF56\uEF57\uEF58\uEF59\uEF5A\uEF5B"+ + "\uEF5C\uEF5D\uEF5E\uEF5F\uEF60\uEF61\uEF62\uEF63"+ + "\uEF64\uEF65\uEF66\uEF67\uEF68\uEF69\uEF6A\uEF6B"+ + "\uEF6C\uEF6D\uEF6E\uEF6F\uEF70\uEF71\uB7E7\uEF72"+ + "\uEF73\uECA9\uECAA\uECAB\uEF74\uECAC\uEF75\uEF76"+ + "\uC6AE\uECAD\uECAE\uEF77\uEF78\uEF79\uB7C9\uCAB3"+ + "\uEF7A\uEF7B\uEF7C\uEF7D\uEF7E\uEF80\uEF81\uE2B8"+ + "\uF7CF\uEF82\uEF83\uEF84\uEF85\uEF86\uEF87\uEF88"+ + "\uEF89\uEF8A\uEF8B\uEF8C\uEF8D\uEF8E\uEF8F\uEF90"+ + "\uEF91\uEF92\uEF93\uEF94\uEF95\uEF96\uEF97\uEF98"+ + "\uEF99\uEF9A\uEF9B\uEF9C\uEF9D\uEF9E\uEF9F\uEFA0"+ + "\uF040\uF041\uF042\uF043\uF044\uF7D0\uF045\uF046"+ + "\uB2CD\uF047\uF048\uF049\uF04A\uF04B\uF04C\uF04D"+ + "\uF04E\uF04F\uF050\uF051\uF052\uF053\uF054\uF055"+ + "\uF056\uF057\uF058\uF059\uF05A\uF05B\uF05C\uF05D"+ + "\uF05E\uF05F\uF060\uF061\uF062\uF063\uF7D1\uF064"+ + "\uF065\uF066\uF067\uF068\uF069\uF06A\uF06B\uF06C"+ + "\uF06D\uF06E\uF06F\uF070\uF071\uF072\uF073\uF074"+ + "\uF075\uF076\uF077\uF078\uF079\uF07A\uF07B\uF07C"+ + "\uF07D\uF07E\uF080\uF081\uF082\uF083\uF084\uF085"+ + "\uF086\uF087\uF088\uF089\uF7D3\uF7D2\uF08A\uF08B"+ + "\uF08C\uF08D\uF08E\uF08F\uF090\uF091\uF092\uF093"+ + "\uF094\uF095\uF096\uE2BB\uF097\uBCA2\uF098\uE2BC"+ + "\uE2BD\uE2BE\uE2BF\uE2C0\uE2C1\uB7B9\uD2FB\uBDA4"+ + "\uCACE\uB1A5\uCBC7\uF099\uE2C2\uB6FC\uC8C4\uE2C3"+ + "\uF09A\uF09B\uBDC8\uF09C\uB1FD\uE2C4\uF09D\uB6F6"+ + "\uE2C5\uC4D9\uF09E\uF09F\uE2C6\uCFDA\uB9DD\uE2C7"+ + "\uC0A1\uF0A0\uE2C8\uB2F6\uF140\uE2C9\uF141\uC1F3"+ + "\uE2CA\uE2CB\uC2F8\uE2CC\uE2CD\uE2CE\uCAD7\uD8B8"+ + "\uD9E5\uCFE3\uF142\uF143\uF144\uF145\uF146\uF147"+ + "\uF148\uF149\uF14A\uF14B\uF14C\uF0A5\uF14D\uF14E"+ + "\uDCB0\uF14F\uF150\uF151\uF152\uF153\uF154\uF155"+ + "\uF156\uF157\uF158\uF159\uF15A\uF15B\uF15C\uF15D"+ + "\uF15E\uF15F\uF160\uF161\uF162\uF163\uF164\uF165"+ + "\uF166\uF167\uF168\uF169\uF16A\uF16B\uF16C\uF16D"+ + "\uF16E\uF16F\uF170\uF171\uF172\uF173\uF174\uF175"+ + "\uF176\uF177\uF178\uF179\uF17A\uF17B\uF17C\uF17D"+ + "\uF17E\uF180\uF181\uF182\uF183\uF184\uF185\uF186"+ + "\uF187\uF188\uF189\uF18A\uF18B\uF18C\uF18D\uF18E"+ + "\uF18F\uF190\uF191\uF192\uF193\uF194\uF195\uF196"+ + "\uF197\uF198\uF199\uF19A\uF19B\uF19C\uF19D\uF19E"+ + "\uF19F\uF1A0\uF240\uF241\uF242\uF243\uF244\uF245"+ + "\uF246\uF247\uF248\uF249\uF24A\uF24B\uF24C\uF24D"+ + "\uF24E\uF24F\uF250\uF251\uF252\uF253\uF254\uF255"+ + "\uF256\uF257\uF258\uF259\uF25A\uF25B\uF25C\uF25D"+ + "\uF25E\uF25F\uF260\uF261\uF262\uF263\uF264\uF265"+ + "\uF266\uF267\uF268\uF269\uF26A\uF26B\uF26C\uF26D"+ + "\uF26E\uF26F\uF270\uF271\uF272\uF273\uF274\uF275"+ + "\uF276\uF277\uF278\uF279\uF27A\uF27B\uF27C\uF27D"+ + "\uF27E\uF280\uF281\uF282\uF283\uF284\uF285\uF286"+ + "\uF287\uF288\uF289\uF28A\uF28B\uF28C\uF28D\uF28E"+ + "\uF28F\uF290\uF291\uF292\uF293\uF294\uF295\uF296"+ + "\uF297\uF298\uF299\uF29A\uF29B\uF29C\uF29D\uF29E"+ + "\uF29F\uF2A0\uF340\uF341\uF342\uF343\uF344\uF345"+ + "\uF346\uF347\uF348\uF349\uF34A\uF34B\uF34C\uF34D"+ + "\uF34E\uF34F\uF350\uF351\uC2ED\uD4A6\uCDD4\uD1B1"+ + "\uB3DB\uC7FD\uF352\uB2B5\uC2BF\uE6E0\uCABB\uE6E1"+ + "\uE6E2\uBED4\uE6E3\uD7A4\uCDD5\uE6E5\uBCDD\uE6E4"+ + "\uE6E6\uE6E7\uC2EE\uF353\uBDBE\uE6E8\uC2E6\uBAA7"+ + "\uE6E9\uF354\uE6EA\uB3D2\uD1E9\uF355\uF356\uBFA5"+ + "\uE6EB\uC6EF\uE6EC\uE6ED\uF357\uF358\uE6EE\uC6AD"+ + "\uE6EF\uF359\uC9A7\uE6F0\uE6F1\uE6F2\uE5B9\uE6F3"+ + "\uE6F4\uC2E2\uE6F5\uE6F6\uD6E8\uE6F7\uF35A\uE6F8"+ + "\uB9C7\uF35B\uF35C\uF35D\uF35E\uF35F\uF360\uF361"+ + "\uF7BB\uF7BA\uF362\uF363\uF364\uF365\uF7BE\uF7BC"+ + "\uBAA1\uF366\uF7BF\uF367\uF7C0\uF368\uF369\uF36A"+ + "\uF7C2\uF7C1\uF7C4\uF36B\uF36C\uF7C3\uF36D\uF36E"+ + "\uF36F\uF370\uF371\uF7C5\uF7C6\uF372\uF373\uF374"+ + "\uF375\uF7C7\uF376\uCBE8\uF377\uF378\uF379\uF37A"+ + "\uB8DF\uF37B\uF37C\uF37D\uF37E\uF380\uF381\uF7D4"+ + "\uF382\uF7D5\uF383\uF384\uF385\uF386\uF7D6\uF387"+ + "\uF388\uF389\uF38A\uF7D8\uF38B\uF7DA\uF38C\uF7D7"+ + "\uF38D\uF38E\uF38F\uF390\uF391\uF392\uF393\uF394"+ + "\uF395\uF7DB\uF396\uF7D9\uF397\uF398\uF399\uF39A"+ + "\uF39B\uF39C\uF39D\uD7D7\uF39E\uF39F\uF3A0\uF440"+ + "\uF7DC\uF441\uF442\uF443\uF444\uF445\uF446\uF7DD"+ + "\uF447\uF448\uF449\uF7DE\uF44A\uF44B\uF44C\uF44D"+ + "\uF44E\uF44F\uF450\uF451\uF452\uF453\uF454\uF7DF"+ + "\uF455\uF456\uF457\uF7E0\uF458\uF459\uF45A\uF45B"+ + "\uF45C\uF45D\uF45E\uF45F\uF460\uF461\uF462\uDBCB"+ + "\uF463\uF464\uD8AA\uF465\uF466\uF467\uF468\uF469"+ + "\uF46A\uF46B\uF46C\uE5F7\uB9ED\uF46D\uF46E\uF46F"+ + "\uF470\uBFFD\uBBEA\uF7C9\uC6C7\uF7C8\uF471\uF7CA"+ + "\uF7CC\uF7CB\uF472\uF473\uF474\uF7CD\uF475\uCEBA"+ + "\uF476\uF7CE\uF477\uF478\uC4A7\uF479\uF47A\uF47B"+ + "\uF47C\uF47D\uF47E\uF480\uF481\uF482\uF483\uF484"+ + "\uF485\uF486\uF487\uF488\uF489\uF48A\uF48B\uF48C"+ + "\uF48D\uF48E\uF48F\uF490\uF491\uF492\uF493\uF494"+ + "\uF495\uF496\uF497\uF498\uF499\uF49A\uF49B\uF49C"+ + "\uF49D\uF49E\uF49F\uF4A0\uF540\uF541\uF542\uF543"+ + "\uF544\uF545\uF546\uF547\uF548\uF549\uF54A\uF54B"+ + "\uF54C\uF54D\uF54E\uF54F\uF550\uF551\uF552\uF553"+ + "\uF554\uF555\uF556\uF557\uF558\uF559\uF55A\uF55B"+ + "\uF55C\uF55D\uF55E\uF55F\uF560\uF561\uF562\uF563"+ + "\uF564\uF565\uF566\uF567\uF568\uF569\uF56A\uF56B"+ + "\uF56C\uF56D\uF56E\uF56F\uF570\uF571\uF572\uF573"+ + "\uF574\uF575\uF576\uF577\uF578\uF579\uF57A\uF57B"+ + "\uF57C\uF57D\uF57E\uF580\uF581\uF582\uF583\uF584"+ + "\uF585\uF586\uF587\uF588\uF589\uF58A\uF58B\uF58C"+ + "\uF58D\uF58E\uF58F\uF590\uF591\uF592\uF593\uF594"+ + "\uF595\uF596\uF597\uF598\uF599\uF59A\uF59B\uF59C"+ + "\uF59D\uF59E\uF59F\uF5A0\uF640\uF641\uF642\uF643"+ + "\uF644\uF645\uF646\uF647\uF648\uF649\uF64A\uF64B"+ + "\uF64C\uF64D\uF64E\uF64F\uF650\uF651\uF652\uF653"+ + "\uF654\uF655\uF656\uF657\uF658\uF659\uF65A\uF65B"+ + "\uF65C\uF65D\uF65E\uF65F\uF660\uF661\uF662\uF663"+ + "\uF664\uF665\uF666\uF667\uF668\uF669\uF66A\uF66B"+ + "\uF66C\uF66D\uF66E\uF66F\uF670\uF671\uF672\uF673"+ + "\uF674\uF675\uF676\uF677\uF678\uF679\uF67A\uF67B"+ + "\uF67C\uF67D\uF67E\uF680\uF681\uF682\uF683\uF684"+ + "\uF685\uF686\uF687\uF688\uF689\uF68A\uF68B\uF68C"+ + "\uF68D\uF68E\uF68F\uF690\uF691\uF692\uF693\uF694"+ + "\uF695\uF696\uF697\uF698\uF699\uF69A\uF69B\uF69C"+ + "\uF69D\uF69E\uF69F\uF6A0\uF740\uF741\uF742\uF743"+ + "\uF744\uF745\uF746\uF747\uF748\uF749\uF74A\uF74B"+ + "\uF74C\uF74D\uF74E\uF74F\uF750\uF751\uF752\uF753"+ + "\uF754\uF755\uF756\uF757\uF758\uF759\uF75A\uF75B"+ + "\uF75C\uF75D\uF75E\uF75F\uF760\uF761\uF762\uF763"+ + "\uF764\uF765\uF766\uF767\uF768\uF769\uF76A\uF76B"+ + "\uF76C\uF76D\uF76E\uF76F\uF770\uF771\uF772\uF773"+ + "\uF774\uF775\uF776\uF777\uF778\uF779\uF77A\uF77B"+ + "\uF77C\uF77D\uF77E\uF780\uD3E3\uF781\uF782\uF6CF"+ + "\uF783\uC2B3\uF6D0\uF784\uF785\uF6D1\uF6D2\uF6D3"+ + "\uF6D4\uF786\uF787\uF6D6\uF788\uB1AB\uF6D7\uF789"+ + "\uF6D8\uF6D9\uF6DA\uF78A\uF6DB\uF6DC\uF78B\uF78C"+ + "\uF78D\uF78E\uF6DD\uF6DE\uCFCA\uF78F\uF6DF\uF6E0"+ + "\uF6E1\uF6E2\uF6E3\uF6E4\uC0F0\uF6E5\uF6E6\uF6E7"+ + "\uF6E8\uF6E9\uF790\uF6EA\uF791\uF6EB\uF6EC\uF792"+ + "\uF6ED\uF6EE\uF6EF\uF6F0\uF6F1\uF6F2\uF6F3\uF6F4"+ + "\uBEA8\uF793\uF6F5\uF6F6\uF6F7\uF6F8\uF794\uF795"+ + "\uF796\uF797\uF798\uC8FA\uF6F9\uF6FA\uF6FB\uF6FC"+ + "\uF799\uF79A\uF6FD\uF6FE\uF7A1\uF7A2\uF7A3\uF7A4"+ + "\uF7A5\uF79B\uF79C\uF7A6\uF7A7\uF7A8\uB1EE\uF7A9"+ + "\uF7AA\uF7AB\uF79D\uF79E\uF7AC\uF7AD\uC1DB\uF7AE"+ + "\uF79F\uF7A0\uF7AF\uF840\uF841\uF842\uF843\uF844"+ + "\uF845\uF846\uF847\uF848\uF849\uF84A\uF84B\uF84C"+ + "\uF84D\uF84E\uF84F\uF850\uF851\uF852\uF853\uF854"+ + "\uF855\uF856\uF857\uF858\uF859\uF85A\uF85B\uF85C"+ + "\uF85D\uF85E\uF85F\uF860\uF861\uF862\uF863\uF864"+ + "\uF865\uF866\uF867\uF868\uF869\uF86A\uF86B\uF86C"+ + "\uF86D\uF86E\uF86F\uF870\uF871\uF872\uF873\uF874"+ + "\uF875\uF876\uF877\uF878\uF879\uF87A\uF87B\uF87C"+ + "\uF87D\uF87E\uF880\uF881\uF882\uF883\uF884\uF885"+ + "\uF886\uF887\uF888\uF889\uF88A\uF88B\uF88C\uF88D"+ + "\uF88E\uF88F\uF890\uF891\uF892\uF893\uF894\uF895"+ + "\uF896\uF897\uF898\uF899\uF89A\uF89B\uF89C\uF89D"+ + "\uF89E\uF89F\uF8A0\uF940\uF941\uF942\uF943\uF944"+ + "\uF945\uF946\uF947\uF948\uF949\uF94A\uF94B\uF94C"+ + "\uF94D\uF94E\uF94F\uF950\uF951\uF952\uF953\uF954"+ + "\uF955\uF956\uF957\uF958\uF959\uF95A\uF95B\uF95C"+ + "\uF95D\uF95E\uF95F\uF960\uF961\uF962\uF963\uF964"+ + "\uF965\uF966\uF967\uF968\uF969\uF96A\uF96B\uF96C"+ + "\uF96D\uF96E\uF96F\uF970\uF971\uF972\uF973\uF974"+ + "\uF975\uF976\uF977\uF978\uF979\uF97A\uF97B\uF97C"+ + "\uF97D\uF97E\uF980\uF981\uF982\uF983\uF984\uF985"+ + "\uF986\uF987\uF988\uF989\uF98A\uF98B\uF98C\uF98D"+ + "\uF98E\uF98F\uF990\uF991\uF992\uF993\uF994\uF995"+ + "\uF996\uF997\uF998\uF999\uF99A\uF99B\uF99C\uF99D"+ + "\uF99E\uF99F\uF9A0\uFA40\uFA41\uFA42\uFA43\uFA44"+ + "\uFA45\uFA46\uFA47\uFA48\uFA49\uFA4A\uFA4B\uFA4C"+ + "\uFA4D\uFA4E\uFA4F\uFA50\uFA51\uFA52\uFA53\uFA54"+ + "\uFA55\uFA56\uFA57\uFA58\uFA59\uFA5A\uFA5B\uFA5C"+ + "\uFA5D\uFA5E\uFA5F\uFA60\uFA61\uFA62\uFA63\uFA64"+ + "\uFA65\uFA66\uFA67\uFA68\uFA69\uFA6A\uFA6B\uFA6C"+ + "\uFA6D\uFA6E\uFA6F\uFA70\uFA71\uFA72\uFA73\uFA74"+ + "\uFA75\uFA76\uFA77\uFA78\uFA79\uFA7A\uFA7B\uFA7C"+ + "\uFA7D\uFA7E\uFA80\uFA81\uFA82\uFA83\uFA84\uFA85"+ + "\uFA86\uFA87\uFA88\uFA89\uFA8A\uFA8B\uFA8C\uFA8D"+ + "\uFA8E\uFA8F\uFA90\uFA91\uFA92\uFA93\uFA94\uFA95"+ + "\uFA96\uFA97\uFA98\uFA99\uFA9A\uFA9B\uFA9C\uFA9D"+ + "\uFA9E\uFA9F\uFAA0\uFB40\uFB41\uFB42\uFB43\uFB44"+ + "\uFB45\uFB46\uFB47\uFB48\uFB49\uFB4A\uFB4B\uFB4C"+ + "\uFB4D\uFB4E\uFB4F\uFB50\uFB51\uFB52\uFB53\uFB54"+ + "\uFB55\uFB56\uFB57\uFB58\uFB59\uFB5A\uFB5B\uC4F1"+ + "\uF0AF\uBCA6\uF0B0\uC3F9\uFB5C\uC5B8\uD1BB\uFB5D"+ + "\uF0B1\uF0B2\uF0B3\uF0B4\uF0B5\uD1BC\uFB5E\uD1EC"+ + "\uFB5F\uF0B7\uF0B6\uD4A7\uFB60\uCDD2\uF0B8\uF0BA"+ + "\uF0B9\uF0BB\uF0BC\uFB61\uFB62\uB8EB\uF0BD\uBAE8"+ + "\uFB63\uF0BE\uF0BF\uBEE9\uF0C0\uB6EC\uF0C1\uF0C2"+ + "\uF0C3\uF0C4\uC8B5\uF0C5\uF0C6\uFB64\uF0C7\uC5F4"+ + "\uFB65\uF0C8\uFB66\uFB67\uFB68\uF0C9\uFB69\uF0CA"+ + "\uF7BD\uFB6A\uF0CB\uF0CC\uF0CD\uFB6B\uF0CE\uFB6C"+ + "\uFB6D\uFB6E\uFB6F\uF0CF\uBAD7\uFB70\uF0D0\uF0D1"+ + "\uF0D2\uF0D3\uF0D4\uF0D5\uF0D6\uF0D8\uFB71\uFB72"+ + "\uD3A5\uF0D7\uFB73\uF0D9\uFB74\uFB75\uFB76\uFB77"+ + "\uFB78\uFB79\uFB7A\uFB7B\uFB7C\uFB7D\uF5BA\uC2B9"+ + "\uFB7E\uFB80\uF7E4\uFB81\uFB82\uFB83\uFB84\uF7E5"+ + "\uF7E6\uFB85\uFB86\uF7E7\uFB87\uFB88\uFB89\uFB8A"+ + "\uFB8B\uFB8C\uF7E8\uC2B4\uFB8D\uFB8E\uFB8F\uFB90"+ + "\uFB91\uFB92\uFB93\uFB94\uFB95\uF7EA\uFB96\uF7EB"+ + "\uFB97\uFB98\uFB99\uFB9A\uFB9B\uFB9C\uC2F3\uFB9D"+ + "\uFB9E\uFB9F\uFBA0\uFC40\uFC41\uFC42\uFC43\uFC44"+ + "\uFC45\uFC46\uFC47\uFC48\uF4F0\uFC49\uFC4A\uFC4B"+ + "\uF4EF\uFC4C\uFC4D\uC2E9\uFC4E\uF7E1\uF7E2\uFC4F"+ + "\uFC50\uFC51\uFC52\uFC53\uBBC6\uFC54\uFC55\uFC56"+ + "\uFC57\uD9E4\uFC58\uFC59\uFC5A\uCAF2\uC0E8\uF0A4"+ + "\uFC5B\uBADA\uFC5C\uFC5D\uC7AD\uFC5E\uFC5F\uFC60"+ + "\uC4AC\uFC61\uFC62\uF7EC\uF7ED\uF7EE\uFC63\uF7F0"+ + "\uF7EF\uFC64\uF7F1\uFC65\uFC66\uF7F4\uFC67\uF7F3"+ + "\uFC68\uF7F2\uF7F5\uFC69\uFC6A\uFC6B\uFC6C\uF7F6"+ + "\uFC6D\uFC6E\uFC6F\uFC70\uFC71\uFC72\uFC73\uFC74"+ + "\uFC75\uEDE9\uFC76\uEDEA\uEDEB\uFC77\uF6BC\uFC78"+ + "\uFC79\uFC7A\uFC7B\uFC7C\uFC7D\uFC7E\uFC80\uFC81"+ + "\uFC82\uFC83\uFC84\uF6BD\uFC85\uF6BE\uB6A6\uFC86"+ + "\uD8BE\uFC87\uFC88\uB9C4\uFC89\uFC8A\uFC8B\uD8BB"+ + "\uFC8C\uDCB1\uFC8D\uFC8E\uFC8F\uFC90\uFC91\uFC92"+ + "\uCAF3\uFC93\uF7F7\uFC94\uFC95\uFC96\uFC97\uFC98"+ + "\uFC99\uFC9A\uFC9B\uFC9C\uF7F8\uFC9D\uFC9E\uF7F9"+ + "\uFC9F\uFCA0\uFD40\uFD41\uFD42\uFD43\uFD44\uF7FB"+ + "\uFD45\uF7FA\uFD46\uB1C7\uFD47\uF7FC\uF7FD\uFD48"+ + "\uFD49\uFD4A\uFD4B\uFD4C\uF7FE\uFD4D\uFD4E\uFD4F"+ + "\uFD50\uFD51\uFD52\uFD53\uFD54\uFD55\uFD56\uFD57"+ + "\uC6EB\uECB4\uFD58\uFD59\uFD5A\uFD5B\uFD5C\uFD5D"+ + "\uFD5E\uFD5F\uFD60\uFD61\uFD62\uFD63\uFD64\uFD65"+ + "\uFD66\uFD67\uFD68\uFD69\uFD6A\uFD6B\uFD6C\uFD6D"+ + "\uFD6E\uFD6F\uFD70\uFD71\uFD72\uFD73\uFD74\uFD75"+ + "\uFD76\uFD77\uFD78\uFD79\uFD7A\uFD7B\uFD7C\uFD7D"+ + "\uFD7E\uFD80\uFD81\uFD82\uFD83\uFD84\uFD85\uB3DD"+ + "\uF6B3\uFD86\uFD87\uF6B4\uC1E4\uF6B5\uF6B6\uF6B7"+ + "\uF6B8\uF6B9\uF6BA\uC8A3\uF6BB\uFD88\uFD89\uFD8A"+ + "\uFD8B\uFD8C\uFD8D\uFD8E\uFD8F\uFD90\uFD91\uFD92"+ + "\uFD93\uC1FA\uB9A8\uEDE8\uFD94\uFD95\uFD96\uB9EA"+ + "\uD9DF\uFD97\uFD98\uFD99\uFD9A\uFD9B\u6A63\u6A64"+ + "\u6A65\u6A66\u6A67\u6A68\u6A69\u6A6A\u6A6B\u6A6C"+ + "\u6A6D\u6A6E\u6A6F\u6A70\u6A71\u6A72\u6A73\u6A74"+ + "\u6A75\u6A76\u6A77\u6A78\u6A79\u6A7A\u6A7B\u6A7C"+ + "\u6A7D\u6A7E\u6A7F\u6A80\u6A81\u6A82\u6A83\u6A84"+ + "\u6A85\u6A86\u6A87\u6A88\u6A89\u6A8A\u6A8B\u6A8C"+ + "\u6A8D\u6A8E\u6A8F\u6A90\u6A91\u6A92\u6A93\u6A94"+ + "\u6A95\u6A96\u6A97\u6A98\u6A99\u6A9A\u6A9B\u6A9C"+ + "\u6A9D\u6A9E\u6A9F\u6AA0\u6AA1\u6AA2\u6AA3\u6AA4"+ + "\u6AA5\u6AA6\u6AA7\u6AA8\u6AA9\u6AAA\u6AAB\u6AAC"+ + "\u6AAD\u6AAE\u6AAF\u6AB0\u6AB1\u6AB2\u6AB3\u6AB4"+ + "\u6AB5\u6AB6\u6AB7\u6AB8\u6AB9\u6ABA\u6ABB\u6ABC"; + + private final static String innerEncoderIndex10= + "\u6ABD\u6ABE\u6ABF\u6AC0\u6AC1\u6AC2\u6AC3\u6AC4"+ + "\u6AC5\u6AC6\u6AC7\u6AC8\u6AC9\u6ACA\u6ACB\u6ACC"+ + "\u6ACD\u6ACE\u6ACF\u6AD0\u6AD1\u6AD2\u6AD3\u6AD4"+ + "\u6AD5\u6AD6\u6AD7\u6AD8\u6AD9\u6ADA\u6ADB\u6ADC"+ + "\u6ADD\u6ADE\u6ADF\u6AE0\u6AE1\u6AE2\u6AE3\u6AE4"+ + "\u6AE5\u6AE6\u6AE7\u6AE8\u6AE9\u6AEA\u6AEB\u6AEC"+ + "\u6AED\u6AEE\u6AEF\u6AF0\u6AF1\u6AF2\u6AF3\u6AF4"+ + "\u6AF5\u6AF6\u6AF7\u6AF8\u6AF9\u6AFA\u6AFB\u6AFC"+ + "\u6AFD\u6AFE\u6AFF\u6B00\u6B01\u6B02\u6B03\u6B04"+ + "\u6B05\u6B06\u6B07\u6B08\u6B09\u6B0A\u6B0B\u6B0C"+ + "\u6B0D\u6B0E\u6B0F\u6B10\u6B11\u6B12\u6B13\u6B14"+ + "\u6B15\u6B16\u6B17\u6B18\u6B19\u6B1A\u6B1B\u6B1C"+ + "\u6B1D\u6B1E\u6B1F\u6B20\u6B21\u6B22\u6B23\u6B24"+ + "\u6B25\u6B26\u6B27\u6B28\u6B29\u6B2A\u6B2B\u6B2C"+ + "\u6B2D\u6B2E\u6B2F\u6B30\u6B31\u6B32\u6B33\u6B34"+ + "\u6B35\u6B36\u6B37\u6B38\u6B39\u6B3A\u6B3B\u6B3C"+ + "\u6B3D\u6B3E\u6B3F\u6B40\u6B41\u6B42\u6B43\u6B44"+ + "\u6B45\u6B46\u6B47\u6B48\u6B49\u6B4A\u6B4B\u6B4C"+ + "\u6B4D\u6B4E\u6B4F\u6B50\u6B51\u6B52\u6B53\u6B54"+ + "\u6B55\u6B56\u6B57\u6B58\u6B59\u6B5A\u6B5B\u6B5C"+ + "\u6B5D\u6B5E\u6B5F\u6B60\u6B61\u6B62\u6B63\u6B64"+ + "\u6B65\u6B66\u6B67\u6B68\u6B69\u6B6A\u6B6B\u6B6C"+ + "\u6B6D\u6B6E\u6B6F\u6B70\u6B71\u6B72\u6B73\u6B74"+ + "\u6B75\u6B76\u6B77\u6B78\u6B79\u6B7A\u6B7B\u6B7C"+ + "\u6B7D\u6B7E\u6B7F\u6B80\u6B81\u6B82\u6B83\u6B84"+ + "\u6B85\u6B86\u6B87\u6B88\u6B89\u6B8A\u6B8B\u6B8C"+ + "\u6B8D\u6B8E\u6B8F\u6B90\u6B91\u6B92\u6B93\u6B94"+ + "\u6B95\u6B96\u6B97\u6B98\u6B99\u6B9A\u6B9B\u6B9C"+ + "\u6B9D\u6B9E\u6B9F\u6BA0\u6BA1\u6BA2\u6BA3\u6BA4"+ + "\u6BA5\u6BA6\u6BA7\u6BA8\u6BA9\u6BAA\u6BAB\u6BAC"+ + "\u6BAD\u6BAE\u6BAF\u6BB0\u6BB1\u6BB2\u6BB3\u6BB4"+ + "\u6BB5\u6BB6\u6BB7\u6BB8\u6BB9\u6BBA\u6BBB\u6BBC"+ + "\u6BBD\u6BBE\u6BBF\u6BC0\u6BC1\u6BC2\u6BC3\u6BC4"+ + "\u6BC5\u6BC6\u6BC7\u6BC8\u6BC9\u6BCA\u6BCB\u6BCC"+ + "\u6BCD\u6BCE\u6BCF\u6BD0\u6BD1\u6BD2\u6BD3\u6BD4"+ + "\u6BD5\u6BD6\u6BD7\u6BD8\u6BD9\u6BDA\u6BDB\u6BDC"+ + "\u6BDD\u6BDE\u6BDF\u6BE0\u6BE1\u6BE2\u6BE3\u6BE4"+ + "\u6BE5\u6BE6\u6BE7\u6BE8\u6BE9\u6BEA\u6BEB\u6BEC"+ + "\u6BED\u6BEE\u6BEF\u6BF0\u6BF1\u6BF2\u6BF3\u6BF4"+ + "\u6BF5\u6BF6\u6BF7\u6BF8\u6BF9\u6BFA\u6BFB\u6BFC"+ + "\u6BFD\u6BFE\u6BFF\u6C00\u6C01\u6C02\u6C03\u6C04"+ + "\u6C05\u6C06\u6C07\u6C08\u6C09\u6C0A\u6C0B\u6C0C"+ + "\u6C0D\u6C0E\u6C0F\u6C10\u6C11\u6C12\u6C13\u6C14"+ + "\u6C15\u6C16\u6C17\u6C18\u6C19\u6C1A\u6C1B\u6C1C"+ + "\u6C1D\u6C1E\u6C1F\u6C20\u6C21\u6C22\u6C23\u6C24"+ + "\u6C25\u6C26\u6C27\u6C28\u6C29\u6C2A\u6C2B\u6C2C"+ + "\u6C2D\u6C2E\u6C2F\u6C30\u6C31\u6C32\u6C33\u6C34"+ + "\u6C35\u6C36\u6C37\u6C38\u6C39\u6C3A\u6C3B\u6C3C"+ + "\u6C3D\u6C3E\u6C3F\u6C40\u6C41\u6C42\u6C43\u6C44"+ + "\u6C45\u6C46\u6C47\u6C48\u6C49\u6C4A\u6C4B\u6C4C"+ + "\u6C4D\u6C4E\u6C4F\u6C50\u6C51\u6C52\u6C53\u6C54"+ + "\u6C55\u6C56\u6C57\u6C58\u6C59\u6C5A\u6C5B\u6C5C"+ + "\u6C5D\u6C5E\u6C5F\u6C60\u6C61\u6C62\u6C63\u6C64"+ + "\u6C65\u6C66\u6C67\u6C68\u6C69\u6C6A\u6C6B\u6C6C"+ + "\u6C6D\u6C6E\u6C6F\u6C70\u6C71\u6C72\u6C73\u6C74"+ + "\u6C75\u6C76\u6C77\u6C78\u6C79\u6C7A\u6C7B\u6C7C"+ + "\u6C7D\u6C7E\u6C7F\u6C80\u6C81\u6C82\u6C83\u6C84"+ + "\u6C85\u6C86\u6C87\u6C88\u6C89\u6C8A\u6C8B\u6C8C"+ + "\u6C8D\u6C8E\u6C8F\u6C90\u6C91\u6C92\u6C93\u6C94"+ + "\u6C95\u6C96\u6C97\u6C98\u6C99\u6C9A\u6C9B\u6C9C"+ + "\u6C9D\u6C9E\u6C9F\u6CA0\u6CA1\u6CA2\u6CA3\u6CA4"+ + "\u6CA5\u6CA6\u6CA7\u6CA8\u6CA9\u6CAA\u6CAB\u6CAC"+ + "\u6CAD\u6CAE\u6CAF\u6CB0\u6CB1\u6CB2\u6CB3\u6CB4"+ + "\u6CB5\u6CB6\u6CB7\u6CB8\u6CB9\u6CBA\u6CBB\u6CBC"+ + "\u6CBD\u6CBE\u6CBF\u6CC0\u6CC1\u6CC2\u6CC3\u6CC4"+ + "\u6CC5\u6CC6\u6CC7\u6CC8\u6CC9\u6CCA\u6CCB\u6CCC"+ + "\u6CCD\u6CCE\u6CCF\u6CD0\u6CD1\u6CD2\u6CD3\u6CD4"+ + "\u6CD5\u6CD6\u6CD7\u6CD8\u6CD9\u6CDA\u6CDB\u6CDC"+ + "\u6CDD\u6CDE\u6CDF\u6CE0\u6CE1\u6CE2\u6CE3\u6CE4"+ + "\u6CE5\u6CE6\u6CE7\u6CE8\u6CE9\u6CEA\u6CEB\u6CEC"+ + "\u6CED\u6CEE\u6CEF\u6CF0\u6CF1\u6CF2\u6CF3\u6CF4"+ + "\u6CF5\u6CF6\u6CF7\u6CF8\u6CF9\u6CFA\u6CFB\u6CFC"+ + "\u6CFD\u6CFE\u6CFF\u6D00\u6D01\u6D02\u6D03\u6D04"+ + "\u6D05\u6D06\u6D07\u6D08\u6D09\u6D0A\u6D0B\u6D0C"+ + "\u6D0D\u6D0E\u6D0F\u6D10\u6D11\u6D12\u6D13\u6D14"+ + "\u6D15\u6D16\u6D17\u6D18\u6D19\u6D1A\u6D1B\u6D1C"+ + "\u6D1D\u6D1E\u6D1F\u6D20\u6D21\u6D22\u6D23\u6D24"+ + "\u6D25\u6D26\u6D27\u6D28\u6D29\u6D2A\u6D2B\u6D2C"+ + "\u6D2D\u6D2E\u6D2F\u6D30\u6D31\u6D32\u6D33\u6D34"+ + "\u6D35\u6D36\u6D37\u6D38\u6D39\u6D3A\u6D3B\u6D3C"+ + "\u6D3D\u6D3E\u6D3F\u6D40\u6D41\u6D42\u6D43\u6D44"+ + "\u6D45\u6D46\u6D47\u6D48\u6D49\u6D4A\u6D4B\u6D4C"+ + "\u6D4D\u6D4E\u6D4F\u6D50\u6D51\u6D52\u6D53\u6D54"+ + "\u6D55\u6D56\u6D57\u6D58\u6D59\u6D5A\u6D5B\u6D5C"+ + "\u6D5D\u6D5E\u6D5F\u6D60\u6D61\u6D62\u6D63\u6D64"+ + "\u6D65\u6D66\u6D67\u6D68\u6D69\u6D6A\u6D6B\u6D6C"+ + "\u6D6D\u6D6E\u6D6F\u6D70\u6D71\u6D72\u6D73\u6D74"+ + "\u6D75\u6D76\u6D77\u6D78\u6D79\u6D7A\u6D7B\u6D7C"+ + "\u6D7D\u6D7E\u6D7F\u6D80\u6D81\u6D82\u6D83\u6D84"+ + "\u6D85\u6D86\u6D87\u6D88\u6D89\u6D8A\u6D8B\u6D8C"+ + "\u6D8D\u6D8E\u6D8F\u6D90\u6D91\u6D92\u6D93\u6D94"+ + "\u6D95\u6D96\u6D97\u6D98\u6D99\u6D9A\u6D9B\u6D9C"+ + "\u6D9D\u6D9E\u6D9F\u6DA0\u6DA1\u6DA2\u6DA3\u6DA4"+ + "\u6DA5\u6DA6\u6DA7\u6DA8\u6DA9\u6DAA\u6DAB\u6DAC"+ + "\u6DAD\u6DAE\u6DAF\u6DB0\u6DB1\u6DB2\u6DB3\u6DB4"+ + "\u6DB5\u6DB6\u6DB7\u6DB8\u6DB9\u6DBA\u6DBB\u6DBC"+ + "\u6DBD\u6DBE\u6DBF\u6DC0\u6DC1\u6DC2\u6DC3\u6DC4"+ + "\u6DC5\u6DC6\u6DC7\u6DC8\u6DC9\u6DCA\u6DCB\u6DCC"+ + "\u6DCD\u6DCE\u6DCF\u6DD0\u6DD1\u6DD2\u6DD3\u6DD4"+ + "\u6DD5\u6DD6\u6DD7\u6DD8\u6DD9\u6DDA\u6DDB\u6DDC"+ + "\u6DDD\u6DDE\u6DDF\u6DE0\u6DE1\u6DE2\u6DE3\u6DE4"+ + "\u6DE5\u6DE6\u6DE7\u6DE8\u6DE9\u6DEA\u6DEB\u6DEC"+ + "\u6DED\u6DEE\u6DEF\u6DF0\u6DF1\u6DF2\u6DF3\u6DF4"+ + "\u6DF5\u6DF6\u6DF7\u6DF8\u6DF9\u6DFA\u6DFB\u6DFC"+ + "\u6DFD\u6DFE\u6DFF\u6E00\u6E01\u6E02\u6E03\u6E04"+ + "\u6E05\u6E06\u6E07\u6E08\u6E09\u6E0A\u6E0B\u6E0C"+ + "\u6E0D\u6E0E\u6E0F\u6E10\u6E11\u6E12\u6E13\u6E14"+ + "\u6E15\u6E16\u6E17\u6E18\u6E19\u6E1A\u6E1B\u6E1C"+ + "\u6E1D\u6E1E\u6E1F\u6E20\u6E21\u6E22\u6E23\u6E24"+ + "\u6E25\u6E26\u6E27\u6E28\u6E29\u6E2A\u6E2B\u6E2C"+ + "\u6E2D\u6E2E\u6E2F\u6E30\u6E31\u6E32\u6E33\u6E34"+ + "\u6E35\u6E36\u6E37\u6E38\u6E39\u6E3A\u6E3B\u6E3C"+ + "\u6E3D\u6E3E\u6E3F\u6E40\u6E41\u6E42\u6E43\u6E44"+ + "\u6E45\u6E46\u6E47\u6E48\u6E49\u6E4A\u6E4B\u6E4C"+ + "\u6E4D\u6E4E\u6E4F\u6E50\u6E51\u6E52\u6E53\u6E54"+ + "\u6E55\u6E56\u6E57\u6E58\u6E59\u6E5A\u6E5B\u6E5C"+ + "\u6E5D\u6E5E\u6E5F\u6E60\u6E61\u6E62\u6E63\u6E64"+ + "\u6E65\u6E66\u6E67\u6E68\u6E69\u6E6A\u6E6B\u6E6C"+ + "\u6E6D\u6E6E\u6E6F\u6E70\u6E71\u6E72\u6E73\u6E74"+ + "\u6E75\u6E76\u6E77\u6E78\u6E79\u6E7A\u6E7B\u6E7C"+ + "\u6E7D\u6E7E\u6E7F\u6E80\u6E81\u6E82\u6E83\u6E84"+ + "\u6E85\u6E86\u6E87\u6E88\u6E89\u6E8A\u6E8B\u6E8C"+ + "\u6E8D\u6E8E\u6E8F\u6E90\u6E91\u6E92\u6E93\u6E94"+ + "\u6E95\u6E96\u6E97\u6E98\u6E99\u6E9A\u6E9B\u6E9C"+ + "\u6E9D\u6E9E\u6E9F\u6EA0\u6EA1\u6EA2\u6EA3\u6EA4"+ + "\u6EA5\u6EA6\u6EA7\u6EA8\u6EA9\u6EAA\u6EAB\u6EAC"+ + "\u6EAD\u6EAE\u6EAF\u6EB0\u6EB1\u6EB2\u6EB3\u6EB4"+ + "\u6EB5\u6EB6\u6EB7\u6EB8\u6EB9\u6EBA\u6EBB\u6EBC"+ + "\u6EBD\u6EBE\u6EBF\u6EC0\u6EC1\u6EC2\u6EC3\u6EC4"+ + "\u6EC5\u6EC6\u6EC7\u6EC8\u6EC9\u6ECA\u6ECB\u6ECC"+ + "\u6ECD\u6ECE\u6ECF\u6ED0\u6ED1\u6ED2\u6ED3\u6ED4"+ + "\u6ED5\u6ED6\u6ED7\u6ED8\u6ED9\u6EDA\u6EDB\u6EDC"+ + "\u6EDD\u6EDE\u6EDF\u6EE0\u6EE1\u6EE2\u6EE3\u6EE4"+ + "\u6EE5\u6EE6\u6EE7\u6EE8\u6EE9\u6EEA\u6EEB\u6EEC"+ + "\u6EED\u6EEE\u6EEF\u6EF0\u6EF1\u6EF2\u6EF3\u6EF4"+ + "\u6EF5\u6EF6\u6EF7\u6EF8\u6EF9\u6EFA\u6EFB\u6EFC"+ + "\u6EFD\u6EFE\u6EFF\u6F00\u6F01\u6F02\u6F03\u6F04"+ + "\u6F05\u6F06\u6F07\u6F08\u6F09\u6F0A\u6F0B\u6F0C"+ + "\u6F0D\u6F0E\u6F0F\u6F10\u6F11\u6F12\u6F13\u6F14"+ + "\u6F15\u6F16\u6F17\u6F18\u6F19\u6F1A\u6F1B\u6F1C"+ + "\u6F1D\u6F1E\u6F1F\u6F20\u6F21\u6F22\u6F23\u6F24"+ + "\u6F25\u6F26\u6F27\u6F28\u6F29\u6F2A\u6F2B\u6F2C"+ + "\u6F2D\u6F2E\u6F2F\u6F30\u6F31\u6F32\u6F33\u6F34"+ + "\u6F35\u6F36\u6F37\u6F38\u6F39\u6F3A\u6F3B\u6F3C"+ + "\u6F3D\u6F3E\u6F3F\u6F40\u6F41\u6F42\u6F43\u6F44"+ + "\u6F45\u6F46\u6F47\u6F48\u6F49\u6F4A\u6F4B\u6F4C"+ + "\u6F4D\u6F4E\u6F4F\u6F50\u6F51\u6F52\u6F53\u6F54"+ + "\u6F55\u6F56\u6F57\u6F58\u6F59\u6F5A\u6F5B\u6F5C"+ + "\u6F5D\u6F5E\u6F5F\u6F60\u6F61\u6F62\u6F63\u6F64"+ + "\u6F65\u6F66\u6F67\u6F68\u6F69\u6F6A\u6F6B\u6F6C"+ + "\u6F6D\u6F6E\u6F6F\u6F70\u6F71\u6F72\u6F73\u6F74"+ + "\u6F75\u6F76\u6F77\u6F78\u6F79\u6F7A\u6F7B\u6F7C"+ + "\u6F7D\u6F7E\u6F7F\u6F80\u6F81\u6F82\u6F83\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD"+ + "\uAAA1\uAAA2\uAAA3\uAAA4\uAAA5\uAAA6\uAAA7\uAAA8"+ + "\uAAA9\uAAAA\uAAAB\uAAAC\uAAAD\uAAAE\uAAAF\uAAB0"+ + "\uAAB1\uAAB2\uAAB3\uAAB4\uAAB5\uAAB6\uAAB7\uAAB8"+ + "\uAAB9\uAABA\uAABB\uAABC\uAABD\uAABE\uAABF\uAAC0"+ + "\uAAC1\uAAC2\uAAC3\uAAC4\uAAC5\uAAC6\uAAC7\uAAC8"+ + "\uAAC9\uAACA\uAACB\uAACC\uAACD\uAACE\uAACF\uAAD0"+ + "\uAAD1\uAAD2\uAAD3\uAAD4\uAAD5\uAAD6\uAAD7\uAAD8"+ + "\uAAD9\uAADA\uAADB\uAADC\uAADD\uAADE\uAADF\uAAE0"+ + "\uAAE1\uAAE2\uAAE3\uAAE4\uAAE5\uAAE6\uAAE7\uAAE8"+ + "\uAAE9\uAAEA\uAAEB\uAAEC\uAAED\uAAEE\uAAEF\uAAF0"+ + "\uAAF1\uAAF2\uAAF3\uAAF4\uAAF5\uAAF6\uAAF7\uAAF8"+ + "\uAAF9\uAAFA\uAAFB\uAAFC\uAAFD\uAAFE\uABA1\uABA2"+ + "\uABA3\uABA4\uABA5\uABA6\uABA7\uABA8\uABA9\uABAA"+ + "\uABAB\uABAC\uABAD\uABAE\uABAF\uABB0\uABB1\uABB2"+ + "\uABB3\uABB4\uABB5\uABB6\uABB7\uABB8\uABB9\uABBA"+ + "\uABBB\uABBC\uABBD\uABBE\uABBF\uABC0\uABC1\uABC2"+ + "\uABC3\uABC4\uABC5\uABC6\uABC7\uABC8\uABC9\uABCA"+ + "\uABCB\uABCC\uABCD\uABCE\uABCF\uABD0\uABD1\uABD2"+ + "\uABD3\uABD4\uABD5\uABD6\uABD7\uABD8\uABD9\uABDA"+ + "\uABDB\uABDC\uABDD\uABDE\uABDF\uABE0\uABE1\uABE2"+ + "\uABE3\uABE4\uABE5\uABE6\uABE7\uABE8\uABE9\uABEA"+ + "\uABEB\uABEC\uABED\uABEE\uABEF\uABF0\uABF1\uABF2"+ + "\uABF3\uABF4\uABF5\uABF6\uABF7\uABF8\uABF9\uABFA"+ + "\uABFB\uABFC\uABFD\uABFE\uACA1\uACA2\uACA3\uACA4"+ + "\uACA5\uACA6\uACA7\uACA8\uACA9\uACAA\uACAB\uACAC"+ + "\uACAD\uACAE\uACAF\uACB0\uACB1\uACB2\uACB3\uACB4"+ + "\uACB5\uACB6\uACB7\uACB8\uACB9\uACBA\uACBB\uACBC"+ + "\uACBD\uACBE\uACBF\uACC0\uACC1\uACC2\uACC3\uACC4"+ + "\uACC5\uACC6\uACC7\uACC8\uACC9\uACCA\uACCB\uACCC"+ + "\uACCD\uACCE\uACCF\uACD0\uACD1\uACD2\uACD3\uACD4"+ + "\uACD5\uACD6\uACD7\uACD8\uACD9\uACDA\uACDB\uACDC"+ + "\uACDD\uACDE\uACDF\uACE0\uACE1\uACE2\uACE3\uACE4"+ + "\uACE5\uACE6\uACE7\uACE8\uACE9\uACEA\uACEB\uACEC"+ + "\uACED\uACEE\uACEF\uACF0\uACF1\uACF2\uACF3\uACF4"+ + "\uACF5\uACF6\uACF7\uACF8\uACF9\uACFA\uACFB\uACFC"+ + "\uACFD\uACFE\uADA1\uADA2\uADA3\uADA4\uADA5\uADA6"+ + "\uADA7\uADA8\uADA9\uADAA\uADAB\uADAC\uADAD\uADAE"+ + "\uADAF\uADB0\uADB1\uADB2\uADB3\uADB4\uADB5\uADB6"+ + "\uADB7\uADB8\uADB9\uADBA\uADBB\uADBC\uADBD\uADBE"+ + "\uADBF\uADC0\uADC1\uADC2\uADC3\uADC4\uADC5\uADC6"+ + "\uADC7\uADC8\uADC9\uADCA\uADCB\uADCC\uADCD\uADCE"+ + "\uADCF\uADD0\uADD1\uADD2\uADD3\uADD4\uADD5\uADD6"+ + "\uADD7\uADD8\uADD9\uADDA\uADDB\uADDC\uADDD\uADDE"+ + "\uADDF\uADE0\uADE1\uADE2\uADE3\uADE4\uADE5\uADE6"+ + "\uADE7\uADE8\uADE9\uADEA\uADEB\uADEC\uADED\uADEE"+ + "\uADEF\uADF0\uADF1\uADF2\uADF3\uADF4\uADF5\uADF6"+ + "\uADF7\uADF8\uADF9\uADFA\uADFB\uADFC\uADFD\uADFE"+ + "\uAEA1\uAEA2\uAEA3\uAEA4\uAEA5\uAEA6\uAEA7\uAEA8"+ + "\uAEA9\uAEAA\uAEAB\uAEAC\uAEAD\uAEAE\uAEAF\uAEB0"+ + "\uAEB1\uAEB2\uAEB3\uAEB4\uAEB5\uAEB6\uAEB7\uAEB8"+ + "\uAEB9\uAEBA\uAEBB\uAEBC\uAEBD\uAEBE\uAEBF\uAEC0"+ + "\uAEC1\uAEC2\uAEC3\uAEC4\uAEC5\uAEC6\uAEC7\uAEC8"+ + "\uAEC9\uAECA\uAECB\uAECC\uAECD\uAECE\uAECF\uAED0"+ + "\uAED1\uAED2\uAED3\uAED4\uAED5\uAED6\uAED7\uAED8"+ + "\uAED9\uAEDA\uAEDB\uAEDC\uAEDD\uAEDE\uAEDF\uAEE0"+ + "\uAEE1\uAEE2\uAEE3\uAEE4\uAEE5\uAEE6\uAEE7\uAEE8"+ + "\uAEE9\uAEEA\uAEEB\uAEEC\uAEED\uAEEE\uAEEF\uAEF0"+ + "\uAEF1\uAEF2\uAEF3\uAEF4\uAEF5\uAEF6\uAEF7\uAEF8"+ + "\uAEF9\uAEFA\uAEFB\uAEFC\uAEFD\uAEFE\uAFA1\uAFA2"+ + "\uAFA3\uAFA4\uAFA5\uAFA6\uAFA7\uAFA8\uAFA9\uAFAA"+ + "\uAFAB\uAFAC\uAFAD\uAFAE\uAFAF\uAFB0\uAFB1\uAFB2"+ + "\uAFB3\uAFB4\uAFB5\uAFB6\uAFB7\uAFB8\uAFB9\uAFBA"+ + "\uAFBB\uAFBC\uAFBD\uAFBE\uAFBF\uAFC0\uAFC1\uAFC2"+ + "\uAFC3\uAFC4\uAFC5\uAFC6\uAFC7\uAFC8\uAFC9\uAFCA"+ + "\uAFCB\uAFCC\uAFCD\uAFCE\uAFCF\uAFD0\uAFD1\uAFD2"+ + "\uAFD3\uAFD4\uAFD5\uAFD6\uAFD7\uAFD8\uAFD9\uAFDA"+ + "\uAFDB\uAFDC\uAFDD\uAFDE\uAFDF\uAFE0\uAFE1\uAFE2"+ + "\uAFE3\uAFE4\uAFE5\uAFE6\uAFE7\uAFE8\uAFE9\uAFEA"+ + "\uAFEB\uAFEC\uAFED\uAFEE\uAFEF\uAFF0\uAFF1\uAFF2"+ + "\uAFF3\uAFF4\uAFF5\uAFF6\uAFF7\uAFF8\uAFF9\uAFFA"+ + "\uAFFB\uAFFC\uAFFD\uAFFE\uF8A1\uF8A2\uF8A3\uF8A4"+ + "\uF8A5\uF8A6\uF8A7\uF8A8\uF8A9\uF8AA\uF8AB\uF8AC"+ + "\uF8AD\uF8AE\uF8AF\uF8B0\uF8B1\uF8B2\uF8B3\uF8B4"+ + "\uF8B5\uF8B6\uF8B7\uF8B8\uF8B9\uF8BA\uF8BB\uF8BC"+ + "\uF8BD\uF8BE\uF8BF\uF8C0\uF8C1\uF8C2\uF8C3\uF8C4"+ + "\uF8C5\uF8C6\uF8C7\uF8C8\uF8C9\uF8CA\uF8CB\uF8CC"+ + "\uF8CD\uF8CE\uF8CF\uF8D0\uF8D1\uF8D2\uF8D3\uF8D4"+ + "\uF8D5\uF8D6\uF8D7\uF8D8\uF8D9\uF8DA\uF8DB\uF8DC"+ + "\uF8DD\uF8DE\uF8DF\uF8E0\uF8E1\uF8E2\uF8E3\uF8E4"+ + "\uF8E5\uF8E6\uF8E7\uF8E8\uF8E9\uF8EA\uF8EB\uF8EC"+ + "\uF8ED\uF8EE\uF8EF\uF8F0\uF8F1\uF8F2\uF8F3\uF8F4"+ + "\uF8F5\uF8F6\uF8F7\uF8F8\uF8F9\uF8FA\uF8FB\uF8FC"+ + "\uF8FD\uF8FE\uF9A1\uF9A2\uF9A3\uF9A4\uF9A5\uF9A6"+ + "\uF9A7\uF9A8\uF9A9\uF9AA\uF9AB\uF9AC\uF9AD\uF9AE"+ + "\uF9AF\uF9B0\uF9B1\uF9B2\uF9B3\uF9B4\uF9B5\uF9B6"+ + "\uF9B7\uF9B8\uF9B9\uF9BA\uF9BB\uF9BC\uF9BD\uF9BE"+ + "\uF9BF\uF9C0\uF9C1\uF9C2\uF9C3\uF9C4\uF9C5\uF9C6"+ + "\uF9C7\uF9C8\uF9C9\uF9CA\uF9CB\uF9CC\uF9CD\uF9CE"+ + "\uF9CF\uF9D0\uF9D1\uF9D2\uF9D3\uF9D4\uF9D5\uF9D6"+ + "\uF9D7\uF9D8\uF9D9\uF9DA\uF9DB\uF9DC\uF9DD\uF9DE"+ + "\uF9DF\uF9E0\uF9E1\uF9E2\uF9E3\uF9E4\uF9E5\uF9E6"+ + "\uF9E7\uF9E8\uF9E9\uF9EA\uF9EB\uF9EC\uF9ED\uF9EE"+ + "\uF9EF\uF9F0\uF9F1\uF9F2\uF9F3\uF9F4\uF9F5\uF9F6"+ + "\uF9F7\uF9F8\uF9F9\uF9FA\uF9FB\uF9FC\uF9FD\uF9FE"+ + "\uFAA1\uFAA2\uFAA3\uFAA4\uFAA5\uFAA6\uFAA7\uFAA8"+ + "\uFAA9\uFAAA\uFAAB\uFAAC\uFAAD\uFAAE\uFAAF\uFAB0"+ + "\uFAB1\uFAB2\uFAB3\uFAB4\uFAB5\uFAB6\uFAB7\uFAB8"+ + "\uFAB9\uFABA\uFABB\uFABC\uFABD\uFABE\uFABF\uFAC0"+ + "\uFAC1\uFAC2\uFAC3\uFAC4\uFAC5\uFAC6\uFAC7\uFAC8"+ + "\uFAC9\uFACA\uFACB\uFACC\uFACD\uFACE\uFACF\uFAD0"+ + "\uFAD1\uFAD2\uFAD3\uFAD4\uFAD5\uFAD6\uFAD7\uFAD8"+ + "\uFAD9\uFADA\uFADB\uFADC\uFADD\uFADE\uFADF\uFAE0"+ + "\uFAE1\uFAE2\uFAE3\uFAE4\uFAE5\uFAE6\uFAE7\uFAE8"+ + "\uFAE9\uFAEA\uFAEB\uFAEC\uFAED\uFAEE\uFAEF\uFAF0"+ + "\uFAF1\uFAF2\uFAF3\uFAF4\uFAF5\uFAF6\uFAF7\uFAF8"+ + "\uFAF9\uFAFA\uFAFB\uFAFC\uFAFD\uFAFE\uFBA1\uFBA2"+ + "\uFBA3\uFBA4\uFBA5\uFBA6\uFBA7\uFBA8\uFBA9\uFBAA"+ + "\uFBAB\uFBAC\uFBAD\uFBAE\uFBAF\uFBB0\uFBB1\uFBB2"+ + "\uFBB3\uFBB4\uFBB5\uFBB6\uFBB7\uFBB8\uFBB9\uFBBA"+ + "\uFBBB\uFBBC\uFBBD\uFBBE\uFBBF\uFBC0\uFBC1\uFBC2"+ + "\uFBC3\uFBC4\uFBC5\uFBC6\uFBC7\uFBC8\uFBC9\uFBCA"+ + "\uFBCB\uFBCC\uFBCD\uFBCE\uFBCF\uFBD0\uFBD1\uFBD2"+ + "\uFBD3\uFBD4\uFBD5\uFBD6\uFBD7\uFBD8\uFBD9\uFBDA"+ + "\uFBDB\uFBDC\uFBDD\uFBDE\uFBDF\uFBE0\uFBE1\uFBE2"+ + "\uFBE3\uFBE4\uFBE5\uFBE6\uFBE7\uFBE8\uFBE9\uFBEA"+ + "\uFBEB\uFBEC\uFBED\uFBEE\uFBEF\uFBF0\uFBF1\uFBF2"+ + "\uFBF3\uFBF4\uFBF5\uFBF6\uFBF7\uFBF8\uFBF9\uFBFA"+ + "\uFBFB\uFBFC\uFBFD\uFBFE\uFCA1\uFCA2\uFCA3\uFCA4"+ + "\uFCA5\uFCA6\uFCA7\uFCA8\uFCA9\uFCAA\uFCAB\uFCAC"+ + "\uFCAD\uFCAE\uFCAF\uFCB0\uFCB1\uFCB2\uFCB3\uFCB4"+ + "\uFCB5\uFCB6\uFCB7\uFCB8\uFCB9\uFCBA\uFCBB\uFCBC"+ + "\uFCBD\uFCBE\uFCBF\uFCC0\uFCC1\uFCC2\uFCC3\uFCC4"+ + "\uFCC5\uFCC6\uFCC7\uFCC8\uFCC9\uFCCA\uFCCB\uFCCC"+ + "\uFCCD\uFCCE\uFCCF\uFCD0\uFCD1\uFCD2\uFCD3\uFCD4"+ + "\uFCD5\uFCD6\uFCD7\uFCD8\uFCD9\uFCDA\uFCDB\uFCDC"+ + "\uFCDD\uFCDE\uFCDF\uFCE0\uFCE1\uFCE2\uFCE3\uFCE4"+ + "\uFCE5\uFCE6\uFCE7\uFCE8\uFCE9\uFCEA\uFCEB\uFCEC"+ + "\uFCED\uFCEE\uFCEF\uFCF0\uFCF1\uFCF2\uFCF3\uFCF4"+ + "\uFCF5\uFCF6\uFCF7\uFCF8\uFCF9\uFCFA\uFCFB\uFCFC"+ + "\uFCFD\uFCFE\uFDA1\uFDA2\uFDA3\uFDA4\uFDA5\uFDA6"+ + "\uFDA7\uFDA8\uFDA9\uFDAA\uFDAB\uFDAC\uFDAD\uFDAE"+ + "\uFDAF\uFDB0\uFDB1\uFDB2\uFDB3\uFDB4\uFDB5\uFDB6"+ + "\uFDB7\uFDB8\uFDB9\uFDBA\uFDBB\uFDBC\uFDBD\uFDBE"+ + "\uFDBF\uFDC0\uFDC1\uFDC2\uFDC3\uFDC4\uFDC5\uFDC6"+ + "\uFDC7\uFDC8\uFDC9\uFDCA\uFDCB\uFDCC\uFDCD\uFDCE"+ + "\uFDCF\uFDD0\uFDD1\uFDD2\uFDD3\uFDD4\uFDD5\uFDD6"+ + "\uFDD7\uFDD8\uFDD9\uFDDA\uFDDB\uFDDC\uFDDD\uFDDE"+ + "\uFDDF\uFDE0\uFDE1\uFDE2\uFDE3\uFDE4\uFDE5\uFDE6"+ + "\uFDE7\uFDE8\uFDE9\uFDEA\uFDEB\uFDEC\uFDED\uFDEE"+ + "\uFDEF\uFDF0\uFDF1\uFDF2\uFDF3\uFDF4\uFDF5\uFDF6"+ + "\uFDF7\uFDF8\uFDF9\uFDFA\uFDFB\uFDFC\uFDFD\uFDFE"+ + "\uFEA1\uFEA2\uFEA3\uFEA4\uFEA5\uFEA6\uFEA7\uFEA8"+ + "\uFEA9\uFEAA\uFEAB\uFEAC\uFEAD\uFEAE\uFEAF\uFEB0"+ + "\uFEB1\uFEB2\uFEB3\uFEB4\uFEB5\uFEB6\uFEB7\uFEB8"+ + "\uFEB9\uFEBA\uFEBB\uFEBC\uFEBD\uFEBE\uFEBF\uFEC0"+ + "\uFEC1\uFEC2\uFEC3\uFEC4\uFEC5\uFEC6\uFEC7\uFEC8"+ + "\uFEC9\uFECA\uFECB\uFECC\uFECD\uFECE\uFECF\uFED0"+ + "\uFED1\uFED2\uFED3\uFED4\uFED5\uFED6\uFED7\uFED8"+ + "\uFED9\uFEDA\uFEDB\uFEDC\uFEDD\uFEDE\uFEDF\uFEE0"+ + "\uFEE1\uFEE2\uFEE3\uFEE4\uFEE5\uFEE6\uFEE7\uFEE8"+ + "\uFEE9\uFEEA\uFEEB\uFEEC\uFEED\uFEEE\uFEEF\uFEF0"+ + "\uFEF1\uFEF2\uFEF3\uFEF4\uFEF5\uFEF6\uFEF7\uFEF8"+ + "\uFEF9\uFEFA\uFEFB\uFEFC\uFEFD\uFEFE\uA140\uA141"+ + "\uA142\uA143\uA144\uA145\uA146\uA147\uA148\uA149"+ + "\uA14A\uA14B\uA14C\uA14D\uA14E\uA14F\uA150\uA151"+ + "\uA152\uA153\uA154\uA155\uA156\uA157\uA158\uA159"+ + "\uA15A\uA15B\uA15C\uA15D\uA15E\uA15F\uA160\uA161"+ + "\uA162\uA163\uA164\uA165\uA166\uA167\uA168\uA169"+ + "\uA16A\uA16B\uA16C\uA16D\uA16E\uA16F\uA170\uA171"+ + "\uA172\uA173\uA174\uA175\uA176\uA177\uA178\uA179"+ + "\uA17A\uA17B\uA17C\uA17D\uA17E\uA180\uA181\uA182"+ + "\uA183\uA184\uA185\uA186\uA187\uA188\uA189\uA18A"+ + "\uA18B\uA18C\uA18D\uA18E\uA18F\uA190\uA191\uA192"+ + "\uA193\uA194\uA195\uA196\uA197\uA198\uA199\uA19A"+ + "\uA19B\uA19C\uA19D\uA19E\uA19F\uA1A0\uA240\uA241"+ + "\uA242\uA243\uA244\uA245\uA246\uA247\uA248\uA249"+ + "\uA24A\uA24B\uA24C\uA24D\uA24E\uA24F\uA250\uA251"+ + "\uA252\uA253\uA254\uA255\uA256\uA257\uA258\uA259"+ + "\uA25A\uA25B\uA25C\uA25D\uA25E\uA25F\uA260\uA261"+ + "\uA262\uA263\uA264\uA265\uA266\uA267\uA268\uA269"+ + "\uA26A\uA26B\uA26C\uA26D\uA26E\uA26F\uA270\uA271"+ + "\uA272\uA273\uA274\uA275\uA276\uA277\uA278\uA279"+ + "\uA27A\uA27B\uA27C\uA27D\uA27E\uA280\uA281\uA282"+ + "\uA283\uA284\uA285\uA286\uA287\uA288\uA289\uA28A"+ + "\uA28B\uA28C\uA28D\uA28E\uA28F\uA290\uA291\uA292"+ + "\uA293\uA294\uA295\uA296\uA297\uA298\uA299\uA29A"+ + "\uA29B\uA29C\uA29D\uA29E\uA29F\uA2A0\uA340\uA341"+ + "\uA342\uA343\uA344\uA345\uA346\uA347\uA348\uA349"+ + "\uA34A\uA34B\uA34C\uA34D\uA34E\uA34F\uA350\uA351"+ + "\uA352\uA353\uA354\uA355\uA356\uA357\uA358\uA359"+ + "\uA35A\uA35B\uA35C\uA35D\uA35E\uA35F\uA360\uA361"+ + "\uA362\uA363\uA364\uA365\uA366\uA367\uA368\uA369"+ + "\uA36A\uA36B\uA36C\uA36D\uA36E\uA36F\uA370\uA371"+ + "\uA372\uA373\uA374\uA375\uA376\uA377\uA378\uA379"+ + "\uA37A\uA37B\uA37C\uA37D\uA37E\uA380\uA381\uA382"+ + "\uA383\uA384\uA385\uA386\uA387\uA388\uA389\uA38A"+ + "\uA38B\uA38C\uA38D\uA38E\uA38F\uA390\uA391\uA392"+ + "\uA393\uA394\uA395\uA396\uA397\uA398\uA399\uA39A"+ + "\uA39B\uA39C\uA39D\uA39E\uA39F\uA3A0\uA440\uA441"+ + "\uA442\uA443\uA444\uA445\uA446\uA447\uA448\uA449"+ + "\uA44A\uA44B\uA44C\uA44D\uA44E\uA44F\uA450\uA451"+ + "\uA452\uA453\uA454\uA455\uA456\uA457\uA458\uA459"+ + "\uA45A\uA45B\uA45C\uA45D\uA45E\uA45F\uA460\uA461"+ + "\uA462\uA463\uA464\uA465\uA466\uA467\uA468\uA469"+ + "\uA46A\uA46B\uA46C\uA46D\uA46E\uA46F\uA470\uA471"+ + "\uA472\uA473\uA474\uA475\uA476\uA477\uA478\uA479"+ + "\uA47A\uA47B\uA47C\uA47D\uA47E\uA480\uA481\uA482"+ + "\uA483\uA484\uA485\uA486\uA487\uA488\uA489\uA48A"+ + "\uA48B\uA48C\uA48D\uA48E\uA48F\uA490\uA491\uA492"+ + "\uA493\uA494\uA495\uA496\uA497\uA498\uA499\uA49A"+ + "\uA49B\uA49C\uA49D\uA49E\uA49F\uA4A0\uA540\uA541"+ + "\uA542\uA543\uA544\uA545\uA546\uA547\uA548\uA549"+ + "\uA54A\uA54B\uA54C\uA54D\uA54E\uA54F\uA550\uA551"+ + "\uA552\uA553\uA554\uA555\uA556\uA557\uA558\uA559"+ + "\uA55A\uA55B\uA55C\uA55D\uA55E\uA55F\uA560\uA561"+ + "\uA562\uA563\uA564\uA565\uA566\uA567\uA568\uA569"+ + "\uA56A\uA56B\uA56C\uA56D\uA56E\uA56F\uA570\uA571"+ + "\uA572\uA573\uA574\uA575\uA576\uA577\uA578\uA579"+ + "\uA57A\uA57B\uA57C\uA57D\uA57E\uA580\uA581\uA582"+ + "\uA583\uA584\uA585\uA586\uA587\uA588\uA589\uA58A"+ + "\uA58B\uA58C\uA58D\uA58E\uA58F\uA590\uA591\uA592"+ + "\uA593\uA594\uA595\uA596\uA597\uA598\uA599\uA59A"+ + "\uA59B\uA59C\uA59D\uA59E\uA59F\uA5A0\uA640\uA641"+ + "\uA642\uA643\uA644\uA645\uA646\uA647\uA648\uA649"+ + "\uA64A\uA64B\uA64C\uA64D\uA64E\uA64F\uA650\uA651"+ + "\uA652\uA653\uA654\uA655\uA656\uA657\uA658\uA659"+ + "\uA65A\uA65B\uA65C\uA65D\uA65E\uA65F\uA660\uA661"+ + "\uA662\uA663\uA664\uA665\uA666\uA667\uA668\uA669"+ + "\uA66A\uA66B\uA66C\uA66D\uA66E\uA66F\uA670\uA671"+ + "\uA672\uA673\uA674\uA675\uA676\uA677\uA678\uA679"+ + "\uA67A\uA67B\uA67C\uA67D\uA67E\uA680\uA681\uA682"+ + "\uA683\uA684\uA685\uA686\uA687\uA688\uA689\uA68A"+ + "\uA68B\uA68C\uA68D\uA68E\uA68F\uA690\uA691\uA692"+ + "\uA693\uA694\uA695\uA696\uA697\uA698\uA699\uA69A"+ + "\uA69B\uA69C\uA69D\uA69E\uA69F\uA6A0\uA740\uA741"+ + "\uA742\uA743\uA744\uA745\uA746\uA747\uA748\uA749"+ + "\uA74A\uA74B\uA74C\uA74D\uA74E\uA74F\uA750\uA751"+ + "\uA752\uA753\uA754\uA755\uA756\uA757\uA758\uA759"+ + "\uA75A\uA75B\uA75C\uA75D\uA75E\uA75F\uA760\uA761"+ + "\uA762\uA763\uA764\uA765\uA766\uA767\uA768\uA769"+ + "\uA76A\uA76B\uA76C\uA76D\uA76E\uA76F\uA770\uA771"+ + "\uA772\uA773\uA774\uA775\uA776\uA777\uA778\uA779"+ + "\uA77A\uA77B\uA77C\uA77D\uA77E\uA780\uA781\uA782"+ + "\uA783\uA784\uA785\uA786\uA787\uA788\uA789\uA78A"+ + "\uA78B\uA78C\uA78D\uA78E\uA78F\uA790\uA791\uA792"+ + "\uA793\uA794\uA795\uA796\uA797\uA798\uA799\uA79A"+ + "\uA79B\uA79C\uA79D\uA79E\uA79F\uA7A0\uA2AB\uA2AC"+ + "\uA2AD\uA2AE\uA2AF\uA2B0\u2000\uA2E4\uA2EF\uA2F0"+ + "\uA2FD\uA2FE\uA4F4\uA4F5\uA4F6\uA4F7\uA4F8\uA4F9"+ + "\uA4FA\uA4FB\uA4FC\uA4FD\uA4FE\uA5F7\uA5F8\uA5F9"+ + "\uA5FA\uA5FB\uA5FC\uA5FD\uA5FE\uA6B9\uA6BA\uA6BB"+ + "\uA6BC\uA6BD\uA6BE\uA6BF\uA6C0\uA6D9\uA6DA\uA6DB"+ + "\uA6DC\uA6DD\uA6DE\uA6DF\uA6EC\uA6ED\uA6F3\uA6F6"+ + "\uA6F7\uA6F8\uA6F9\uA6FA\uA6FB\uA6FC\uA6FD\uA6FE"+ + "\uA7C2\uA7C3\uA7C4\uA7C5\uA7C6\uA7C7\uA7C8\uA7C9"+ + "\uA7CA\uA7CB\uA7CC\uA7CD\uA7CE\uA7CF\uA7D0\uA7F2"+ + "\uA7F3\uA7F4\uA7F5\uA7F6\uA7F7\uA7F8\uA7F9\uA7FA"+ + "\uA7FB\uA7FC\uA7FD\uA7FE\uA896\uA897\uA898\uA899"+ + "\uA89A\uA89B\uA89C\uA89D\uA89E\uA89F\uA8A0\uA8BC"+ + "\u2001\uA8C1\uA8C2\uA8C3\uA8C4\uA8EA\uA8EB\uA8EC"+ + "\uA8ED\uA8EE\uA8EF\uA8F0\uA8F1\uA8F2\uA8F3\uA8F4"+ + "\uA8F5\uA8F6\uA8F7\uA8F8\uA8F9\uA8FA\uA8FB\uA8FC"+ + "\uA8FD\uA8FE\uA958\uA95B\uA95D\uA95E\uA95F\u2002"+ + "\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A"+ + "\u200B\u200C\u200D\u200E\uA997\uA998\uA999\uA99A"+ + "\uA99B\uA99C\uA99D\uA99E\uA99F\uA9A0\uA9A1\uA9A2"+ + "\uA9A3\uA9F0\uA9F1\uA9F2\uA9F3\uA9F4\uA9F5\uA9F6"+ + "\uA9F7\uA9F8\uA9F9\uA9FA\uA9FB\uA9FC\uA9FD\uA9FE"+ + "\uD7FA\uD7FB\uD7FC\uD7FD\uD7FE\u200F\uFE51\uFE52"+ + "\uFE53\u2010\u2011\u2012\u2013\u2014\uFE59\u2015"+ + "\u2016\u2017\u2018\u2019\u201A\u201B\uFE61\u201C"+ + "\u201D\u201E\u201F\uFE66\uFE67\u2020\u2021\u2022"+ + "\u2023\uFE6C\uFE6D\u2024\u2025\u2026\u2027\u2028"+ + "\u2029\u202A\u202B\uFE76\u202C\u202D\u202E\u202F"+ + "\u2030\u2031\u2032\uFE7E\u2033\u2034\u2035\u2036"+ + "\u2037\u2038\u2039\u203A\u203B\u203C\u203D\u203E"+ + "\u203F\u2040\u2041\u2042\uFE90\uFE91\u2043\u2044"+ + "\u2045\u2046\u2047\u2048\u2049\u204A\u204B\u204C"+ + "\u204D\u204E\u204F\u2050\uFEA0\u2051\u2052\u2053"+ + "\u2054\u2055\u2056\u2057\u2058\u2059\u205A\u205B"+ + "\u205C\u205D\u205E\u205F\u2060\u2061\u2062\u2063"+ + "\u2064\u2065\u2066\u2067\u2068\u2069\u206A\u206B"+ + "\u206C\u206D\u206E\u206F\u2070\u2071\u2072\u2073"+ + "\u2074\u2075\u2076\u2077\u2078\u2079\u207A\u207B"+ + "\u207C\u207D\u207E\u207F\u2080\u2081\u2082\u2083"+ + "\u2084\u2085\u2086\u2087\u2088\u2089\u208A\u208B"+ + "\u208C\u208D\u208E\u208F\u2090\u2091\u2092\u2093"+ + "\u2094\u2095\u2096\u2097\u2098\u2099\u209A\u209B"+ + "\u209C\u209D\u209E\u209F\u20A0\u20A1\u20A2\u20A3"+ + "\u20A4\u20A5\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB"+ + "\u20AC\u20AD\u20AE\u20AF\u20B0\u20B1\u20B2\u20B3"+ + "\u20B4\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB"+ + "\u20BC\u20BD\u20BE\u20BF\u20C0\u20C1\u20C2\u20C3"+ + "\u20C4\u20C5\u20C6\u20C7\u20C8\u20C9\u20CA\u20CB"+ + "\u20CC\u20CD\u20CE\u20CF\u20D0\u20D1\u20D2\u20D3"+ + "\u20D4\u20D5\u20D6\u20D7\u20D8\u20D9\u20DA\u20DB"+ + "\u20DC\u20DD\u20DE\u20DF\u20E0\u20E1\u20E2\u20E3"+ + "\u20E4\u20E5\u20E6\u20E7\u20E8\u20E9\u20EA\u20EB"+ + "\u20EC\u20ED\u20EE\u20EF\u20F0\u20F1\u20F2\u20F3"+ + "\u20F4\u20F5\u20F6\u20F7\u20F8\u20F9\u20FA\u20FB"+ + "\u20FC\u20FD\u20FE\u20FF\u2100\u2101\u2102\u2103"+ + "\u2104\u2105\u2106\u2107\u2108\u2109\u210A\u210B"+ + "\u210C\u210D\u210E\u210F\u2110\u2111\u2112\u2113"+ + "\u2114\u2115\u2116\u2117\u2118\u2119\u211A\u211B"+ + "\u211C\u211D\u211E\u211F\u2120\u2121\u2122\u2123"+ + "\u2124\u2125\u2126\u2127\u2128\u2129\u212A\u212B"+ + "\u212C\u212D\u212E\u212F\u2130\u2131\u2132\u2133"+ + "\u2134\u2135\u2136\u2137\u2138\u2139\u213A\u213B"+ + "\u213C\u213D\u213E\u213F\u2140\u2141\u2142\u2143"+ + "\u2144\u2145\u2146\u2147\u2148\u2149\u214A\u214B"+ + "\u214C\u214D\u214E\u214F\u2150\u2151\u2152\u2153"+ + "\u2154\u2155\u2156\u2157\u2158\u2159\u215A\u215B"+ + "\u215C\u215D\u215E\u215F\u2160\u2161\u2162\u2163"+ + "\u2164\u2165\u2166\u2167\u2168\u2169\u216A\u216B"+ + "\u216C\u216D\u216E\u216F\u2170\u2171\u2172\u2173"+ + "\u2174\u2175\u2176\u2177\u2178\u2179\u217A\u217B"+ + "\u217C\u217D\u217E\u217F\u2180\u2181\u2182\u2183"+ + "\u2184\u2185\u2186\u2187\u2188\u2189\u218A\u218B"+ + "\u218C\u218D\u218E\u218F\u2190\u2191\u2192\u2193"+ + "\u2194\u2195\u2196\u2197\u2198\u2199\u219A\u219B"+ + "\u219C\u219D\u219E\u219F\u21A0\u21A1\u21A2\u21A3"+ + "\u21A4\u21A5\u21A6\u21A7\u21A8\u21A9\u21AA\u21AB"+ + "\u21AC\u21AD\u21AE\u21AF\u21B0\u21B1\u21B2\u21B3"+ + "\u21B4\u21B5\u21B6\u21B7\u21B8\u21B9\u21BA\u21BB"+ + "\u21BC\u21BD\u21BE\u21BF\u21C0\u21C1\u21C2\u21C3"+ + "\u21C4\u21C5\u21C6\u21C7\u21C8\u21C9\u21CA\u21CB"+ + "\u21CC\u21CD\u21CE\u21CF\u21D0\u21D1\u21D2\u21D3"+ + "\u21D4\u21D5\u21D6\u21D7\u21D8\u21D9\u21DA\u21DB"+ + "\u21DC\u21DD\u21DE\u21DF\u21E0\u21E1\u21E2\u21E3"+ + "\u21E4\u21E5\u21E6\u21E7\u21E8\u21E9\u21EA\u21EB"+ + "\u21EC\u21ED\u21EE\u21EF\u21F0\u21F1\u21F2\u21F3"+ + "\u21F4\u21F5\u21F6\u21F7\u21F8\u21F9\u21FA\u21FB"+ + "\u21FC\u21FD\u21FE\u21FF\u2200\u2201\u2202\u2203"+ + "\u2204\u2205\u2206\u2207\u2208\u2209\u220A\u220B"+ + "\u220C\u220D\u220E\u220F\u2210\u2211\u2212\u2213"+ + "\u2214\u2215\u2216\u2217\u2218\u2219\u221A\u221B"+ + "\u221C\u221D\u221E\u221F\u2220\u2221\u2222\u2223"+ + "\u2224\u2225\u2226\u2227\u2228\u2229\u222A\u222B"+ + "\u222C\u222D\u222E\u222F\u2230\u2231\u2232\u2233"+ + "\u2234\u2235\u2236\u2237\u2238\u2239\u223A\u223B"+ + "\u223C\u223D\u223E\u223F\u2240\u2241\u2242\u2243"+ + "\u2244\u2245\u2246\u2247\u2248\u2249\u224A\u224B"+ + "\u224C\u224D\u224E\u224F\u2250\u2251\u2252\u2253"+ + "\u2254\u2255\u2256\u2257\u2258\u2259\u225A\u225B"+ + "\u225C\u225D\u225E\u225F\u2260\u2261\u2262\u2263"+ + "\u2264\u2265\u2266\u2267\u2268\u2269\u226A\u226B"+ + "\u226C\u226D\u226E\u226F\u2270\u2271\u2272\u2273"+ + "\u2274\u2275\u2276\u2277\u2278\u2279\u227A\u227B"+ + "\u227C\u227D\u227E\u227F\u2280\u2281\u2282\u2283"+ + "\u2284\u2285\u2286\u2287\u2288\u2289\u228A\u228B"+ + "\u228C\u228D\u228E\u228F\u2290\u2291\u2292\u2293"+ + "\u2294\u2295\u2296\u2297\u2298\u2299\u229A\u229B"+ + "\u229C\u229D\u229E\u229F\u22A0\u22A1\u22A2\u22A3"+ + "\u22A4\u22A5\u22A6\u22A7\u22A8\u22A9\u22AA\u22AB"+ + "\u22AC\u22AD\u22AE\u22AF\u22B0\u22B1\u22B2\u22B3"+ + "\u22B4\u22B5\u22B6\u22B7\u22B8\u22B9\u22BA\u22BB"+ + "\u22BC\u22BD\u22BE\u22BF\u22C0\u22C1\u22C2\u22C3"+ + "\u22C4\u22C5\u22C6\u22C7\u22C8\u22C9\u22CA\u22CB"+ + "\u22CC\u22CD\u22CE\u22CF\u22D0\u22D1\u22D2\u22D3"+ + "\u22D4\u22D5\u22D6\u22D7\u22D8\u22D9\u22DA\u22DB"+ + "\u22DC\u22DD\u22DE\u22DF\u22E0\u22E1\u22E2\u22E3"+ + "\u22E4\u22E5\u22E6\u22E7\u22E8\u22E9\u22EA\u22EB"; + + private final static String innerEncoderIndex11= + "\u22EC\u22ED\u22EE\u22EF\u22F0\u22F1\u22F2\u22F3"+ + "\u22F4\u22F5\u22F6\u22F7\u22F8\u22F9\u22FA\u22FB"+ + "\u22FC\u22FD\u22FE\u22FF\u2300\u2301\u2302\u2303"+ + "\u2304\u2305\u2306\u2307\u2308\u2309\u230A\u230B"+ + "\u230C\u230D\u230E\u230F\u2310\u2311\u2312\u2313"+ + "\u2314\u2315\u2316\u2317\u2318\u2319\u231A\u231B"+ + "\u231C\u231D\u231E\u231F\u2320\u2321\u2322\u2323"+ + "\u2324\u2325\u2326\u2327\u2328\u2329\u232A\u232B"+ + "\u232C\u232D\u232E\u232F\u2330\u2331\u2332\u2333"+ + "\u2334\u2335\u2336\u2337\u2338\u2339\u233A\u233B"+ + "\u233C\u233D\u233E\u233F\u2340\u2341\u2342\u2343"+ + "\u2344\u2345\u2346\u2347\u2348\u2349\u234A\u234B"+ + "\u234C\u234D\u234E\u234F\u2350\u2351\u2352\u2353"+ + "\u2354\u2355\u2356\u2357\u2358\u2359\u235A\u235B"+ + "\u235C\u235D\u235E\u235F\u2360\u2361\u2362\u2363"+ + "\u2364\u2365\u2366\u2367\u2368\u2369\u236A\u236B"+ + "\u236C\u236D\u236E\u236F\u2370\u2371\u2372\u2373"+ + "\u2374\u2375\u2376\u2377\u2378\u2379\u237A\u237B"+ + "\u237C\u237D\u237E\u237F\u2380\u2381\u2382\u2383"+ + "\u2384\u2385\u2386\u2387\u2388\u2389\u238A\u238B"+ + "\u238C\u238D\u238E\u238F\u2390\u2391\u2392\u2393"+ + "\u2394\u2395\u2396\u2397\u2398\u2399\u239A\u239B"+ + "\u239C\u239D\u239E\u239F\u23A0\u23A1\u23A2\u23A3"+ + "\u23A4\u23A5\u23A6\u23A7\u23A8\u23A9\u23AA\u23AB"+ + "\u23AC\u23AD\u23AE\u23AF\u23B0\u23B1\u23B2\u23B3"+ + "\u23B4\u23B5\u23B6\u23B7\u23B8\u23B9\u23BA\u23BB"+ + "\u23BC\u23BD\u23BE\u23BF\u23C0\u23C1\u23C2\u23C3"+ + "\u23C4\u23C5\u23C6\u23C7\u23C8\u23C9\u23CA\u23CB"+ + "\u23CC\u23CD\u23CE\u23CF\u23D0\u23D1\u23D2\u23D3"+ + "\u23D4\u23D5\u23D6\u23D7\u23D8\u23D9\u23DA\u23DB"+ + "\u23DC\u23DD\u23DE\u23DF\u23E0\u23E1\u23E2\u23E3"+ + "\u23E4\u23E5\u23E6\u23E7\u23E8\u23E9\u23EA\u23EB"+ + "\u23EC\u23ED\u23EE\u23EF\u23F0\u23F1\u23F2\u23F3"+ + "\u23F4\u23F5\u23F6\u23F7\u23F8\u23F9\u23FA\u23FB"+ + "\u23FC\u23FD\u23FE\u23FF\u2400\u2401\u2402\u2403"+ + "\u2404\u2405\u2406\u2407\u2408\u2409\u240A\u240B"+ + "\u240C\u240D\u240E\u240F\u2410\u2411\u2412\u2413"+ + "\u2414\u2415\u2416\u2417\u2418\u2419\u241A\u241B"+ + "\u241C\u241D\u241E\u241F\u2420\u2421\u2422\u2423"+ + "\u2424\u2425\u2426\u2427\u2428\u2429\u242A\u242B"+ + "\u242C\u242D\u242E\u242F\u2430\u2431\u2432\u2433"+ + "\u2434\u2435\u2436\u2437\u2438\u2439\u243A\u243B"+ + "\u243C\u243D\u243E\u243F\u2440\u2441\u2442\u2443"+ + "\u2444\u2445\u2446\u2447\u2448\u2449\u244A\u244B"+ + "\u244C\u244D\u244E\u244F\u2450\u2451\u2452\u2453"+ + "\u2454\u2455\u2456\u2457\u2458\u2459\u245A\u245B"+ + "\u245C\u245D\u245E\u245F\u2460\u2461\u2462\u2463"+ + "\u2464\u2465\u2466\u2467\u2468\u2469\u246A\u246B"+ + "\u246C\u246D\u246E\u246F\u2470\u2471\u2472\u2473"+ + "\u2474\u2475\u2476\u2477\u2478\u2479\u247A\u247B"+ + "\u247C\u247D\u247E\u247F\u2480\u2481\u2482\u2483"+ + "\u2484\u2485\u2486\u2487\u2488\u2489\u248A\u248B"+ + "\u248C\u248D\u248E\u248F\u2490\u2491\u2492\u2493"+ + "\u2494\u2495\u2496\u2497\u2498\u2499\u249A\u249B"+ + "\u249C\u249D\u249E\u249F\u24A0\u24A1\u24A2\u24A3"+ + "\u24A4\u24A5\u24A6\u24A7\u24A8\u24A9\u24AA\u24AB"+ + "\u24AC\u24AD\u24AE\u24AF\u24B0\u24B1\u24B2\u24B3"+ + "\u24B4\u24B5\u24B6\u24B7\u24B8\u24B9\u24BA\u24BB"+ + "\u24BC\u24BD\u24BE\u24BF\u24C0\u24C1\u24C2\u24C3"+ + "\u24C4\u24C5\u24C6\u24C7\u24C8\u24C9\u24CA\u24CB"+ + "\u24CC\u24CD\u24CE\u24CF\u24D0\u24D1\u24D2\u24D3"+ + "\u24D4\u24D5\u24D6\u24D7\u24D8\u24D9\u24DA\u24DB"+ + "\u24DC\u24DD\u24DE\u24DF\u24E0\u24E1\u24E2\u24E3"+ + "\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9\u24EA\u24EB"+ + "\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1\u24F2\u24F3"+ + "\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9\u24FA\u24FB"+ + "\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502\u2503"+ + "\u2504\u2505\u2506\u2507\u2508\u2509\u250A\u250B"+ + "\u250C\u250D\u250E\u250F\u2510\u2511\u2512\u2513"+ + "\u2514\u2515\u2516\u2517\u2518\u2519\u251A\u251B"+ + "\u251C\u251D\u251E\u251F\u2520\u2521\u2522\u2523"+ + "\u2524\u2525\u2526\u2527\u2528\u2529\u252A\u252B"+ + "\u252C\u252D\u252E\u252F\u2530\u2531\u2532\u2533"+ + "\u2534\u2535\u2536\u2537\u2538\u2539\u253A\u253B"+ + "\u253C\u253D\u253E\u253F\u2540\u2541\u2542\u2543"+ + "\u2544\u2545\u2546\u2547\u2548\u2549\u254A\u254B"+ + "\u254C\u254D\u254E\u254F\u2550\u2551\u2552\u2553"+ + "\u2554\u2555\u2556\u2557\u2558\u2559\u255A\u255B"+ + "\u255C\u255D\u255E\u255F\u2560\u2561\u2562\u2563"+ + "\u2564\u2565\u2566\u2567\u2568\u2569\u256A\u256B"+ + "\u256C\u256D\u256E\u256F\u2570\u2571\u2572\u2573"+ + "\u2574\u2575\u2576\u2577\u2578\u2579\u257A\u257B"+ + "\u257C\u257D\u257E\u257F\u2580\u2581\u2582\u2583"+ + "\u2584\u2585\u2586\u2587\u2588\u2589\u258A\u258B"+ + "\u258C\u258D\u258E\u258F\u2590\u2591\u2592\u2593"+ + "\u2594\u2595\u2596\u2597\u2598\u2599\u259A\u259B"+ + "\u259C\u259D\u259E\u259F\u25A0\u25A1\u25A2\u25A3"+ + "\u25A4\u25A5\u25A6\u25A7\u25A8\u25A9\u25AA\u25AB"+ + "\u25AC\u25AD\u25AE\u25AF\u25B0\u25B1\u25B2\u25B3"+ + "\u25B4\u25B5\u25B6\u25B7\u25B8\u25B9\u25BA\u25BB"+ + "\u25BC\u25BD\u25BE\u25BF\u25C0\u25C1\u25C2\u25C3"+ + "\u25C4\u25C5\u25C6\u25C7\u25C8\u25C9\u25CA\u25CB"+ + "\u25CC\u25CD\u25CE\u25CF\u25D0\u25D1\u25D2\u25D3"+ + "\u25D4\u25D5\u25D6\u25D7\u25D8\u25D9\u25DA\u25DB"+ + "\u25DC\u25DD\u25DE\u25DF\u25E0\u25E1\u25E2\u25E3"+ + "\u25E4\u25E5\u25E6\u25E7\u25E8\u25E9\u25EA\u25EB"+ + "\u25EC\u25ED\u25EE\u25EF\u25F0\u25F1\u25F2\u25F3"+ + "\u25F4\u25F5\u25F6\u25F7\u25F8\u25F9\u25FA\u25FB"+ + "\u25FC\u25FD\u25FE\u25FF\u2600\u2601\u2602\u2603"+ + "\u2604\u2605\u2606\u2607\u2608\u2609\u260A\u260B"+ + "\u260C\u260D\u260E\u260F\u2610\u2611\u2612\u2613"+ + "\u2614\u2615\u2616\u2617\u2618\u2619\u261A\u261B"+ + "\u261C\u261D\u261E\u261F\u2620\u2621\u2622\u2623"+ + "\u2624\u2625\u2626\u2627\u2628\u2629\u262A\u262B"+ + "\u262C\u262D\u262E\u262F\u2630\u2631\u2632\u2633"+ + "\u2634\u2635\u2636\u2637\u2638\u2639\u263A\u263B"+ + "\u263C\u263D\u263E\u263F\u2640\u2641\u2642\u2643"+ + "\u2644\u2645\u2646\u2647\u2648\u2649\u264A\u264B"+ + "\u264C\u264D\u264E\u264F\u2650\u2651\u2652\u2653"+ + "\u2654\u2655\u2656\u2657\u2658\u2659\u265A\u265B"+ + "\u265C\u265D\u265E\u265F\u2660\u2661\u2662\u2663"+ + "\u2664\u2665\u2666\u2667\u2668\u2669\u266A\u266B"+ + "\u266C\u266D\u266E\u266F\u2670\u2671\u2672\u2673"+ + "\u2674\u2675\u2676\u2677\u2678\u2679\u267A\u267B"+ + "\u267C\u267D\u267E\u267F\u2680\u2681\u2682\u2683"+ + "\u2684\u2685\u2686\u2687\u2688\u2689\u268A\u268B"+ + "\u268C\u268D\u268E\u268F\u2690\u2691\u2692\u2693"+ + "\u2694\u2695\u2696\u2697\u2698\u2699\u269A\u269B"+ + "\u269C\u269D\u269E\u269F\u26A0\u26A1\u26A2\u26A3"+ + "\u26A4\u26A5\u26A6\u26A7\u26A8\u26A9\u26AA\u26AB"+ + "\u26AC\u26AD\u26AE\u26AF\u26B0\u26B1\u26B2\u26B3"+ + "\u26B4\u26B5\u26B6\u26B7\u26B8\u26B9\u26BA\u26BB"+ + "\u26BC\u26BD\u26BE\u26BF\u26C0\u26C1\u26C2\u26C3"+ + "\u26C4\u26C5\u26C6\u26C7\u26C8\u26C9\u26CA\u26CB"+ + "\u26CC\u26CD\u26CE\u26CF\u26D0\u26D1\u26D2\u26D3"+ + "\u26D4\u26D5\u26D6\u26D7\u26D8\u26D9\u26DA\u26DB"+ + "\u26DC\u26DD\u26DE\u26DF\u26E0\u26E1\u26E2\u26E3"+ + "\u26E4\u26E5\u26E6\u26E7\u26E8\u26E9\u26EA\u26EB"+ + "\u26EC\u26ED\u26EE\u26EF\u26F0\u26F1\u26F2\u26F3"+ + "\u26F4\u26F5\u26F6\u26F7\u26F8\u26F9\u26FA\u26FB"+ + "\u26FC\u26FD\u26FE\u26FF\u2700\u2701\u2702\u2703"+ + "\u2704\u2705\u2706\u2707\u2708\u2709\u270A\u270B"+ + "\u270C\u270D\u270E\u270F\u2710\u2711\u2712\u2713"+ + "\u2714\u2715\u2716\u2717\u2718\u2719\u271A\u271B"+ + "\u271C\u271D\u271E\u271F\u2720\u2721\u2722\u2723"+ + "\u2724\u2725\u2726\u2727\u2728\u2729\u272A\u272B"+ + "\u272C\u272D\u272E\u272F\u2730\u2731\u2732\u2733"+ + "\u2734\u2735\u2736\u2737\u2738\u2739\u273A\u273B"+ + "\u273C\u273D\u273E\u273F\u2740\u2741\u2742\u2743"+ + "\u2744\u2745\u2746\u2747\u2748\u2749\u274A\u274B"+ + "\u274C\u274D\u274E\u274F\u2750\u2751\u2752\u2753"+ + "\u2754\u2755\u2756\u2757\u2758\u2759\u275A\u275B"+ + "\u275C\u275D\u275E\u275F\u2760\u2761\u2762\u2763"+ + "\u2764\u2765\u2766\u2767\u2768\u2769\u276A\u276B"+ + "\u276C\u276D\u276E\u276F\u2770\u2771\u2772\u2773"+ + "\u2774\u2775\u2776\u2777\u2778\u2779\u277A\u277B"+ + "\u277C\u277D\u277E\u277F\u2780\u2781\u2782\u2783"+ + "\u2784\u2785\u2786\u2787\u2788\u2789\u278A\u278B"+ + "\u278C\u278D\u278E\u278F\u2790\u2791\u2792\u2793"+ + "\u2794\u2795\u2796\u2797\u2798\u2799\u279A\u279B"+ + "\u279C\u279D\u279E\u279F\u27A0\u27A1\u27A2\u27A3"+ + "\u27A4\u27A5\u27A6\u27A7\u27A8\u27A9\u27AA\u27AB"+ + "\u27AC\u27AD\u27AE\u27AF\u27B0\u27B1\u27B2\u27B3"+ + "\u27B4\u27B5\u27B6\u27B7\u27B8\u27B9\u27BA\u27BB"+ + "\u27BC\u27BD\u27BE\u27BF\u27C0\u27C1\u27C2\u27C3"+ + "\u27C4\u27C5\u27C6\u27C7\u27C8\u27C9\u27CA\u27CB"+ + "\u27CC\u27CD\u27CE\u27CF\u27D0\u27D1\u27D2\u27D3"+ + "\u27D4\u27D5\u27D6\u27D7\u27D8\u27D9\u27DA\u27DB"+ + "\u27DC\u27DD\u27DE\u27DF\u27E0\u27E1\u27E2\u27E3"+ + "\u27E4\u27E5\u27E6\u27E7\u27E8\u27E9\u27EA\u27EB"+ + "\u27EC\u27ED\u27EE\u27EF\u27F0\u27F1\u27F2\u27F3"+ + "\u27F4\u27F5\u27F6\u27F7\u27F8\u27F9\u27FA\u27FB"+ + "\u27FC\u27FD\u27FE\u27FF\u2800\u2801\u2802\u2803"+ + "\u2804\u2805\u2806\u2807\u2808\u2809\u280A\u280B"+ + "\u280C\u280D\u280E\u280F\u2810\u2811\u2812\u2813"+ + "\u2814\u2815\u2816\u2817\u2818\u2819\u281A\u281B"+ + "\u281C\u281D\u281E\u281F\u2820\u2821\u2822\u2823"+ + "\u2824\u2825\u2826\u2827\u2828\u2829\u282A\u282B"+ + "\u282C\u282D\u282E\u282F\u2830\u2831\u2832\u2833"+ + "\u2834\u2835\u2836\u2837\u2838\u2839\u283A\u283B"+ + "\u283C\u283D\u283E\u283F\u2840\u2841\u2842\u2843"+ + "\u2844\u2845\u2846\u2847\u2848\u2849\u284A\u284B"+ + "\u284C\u284D\u284E\u284F\u2850\u2851\u2852\u2853"+ + "\u2854\u2855\u2856\u2857\u2858\u2859\u285A\u285B"+ + "\u285C\u285D\u285E\u285F\u2860\u2861\u2862\u2863"+ + "\u2864\u2865\u2866\u2867\u2868\u2869\u286A\u286B"+ + "\u286C\u286D\u286E\u286F\u2870\u2871\u2872\u2873"+ + "\u2874\u2875\u2876\u2877\u2878\u2879\u287A\u287B"+ + "\u287C\u287D\u287E\u287F\u2880\u2881\u2882\u2883"+ + "\u2884\u2885\u2886\u2887\u2888\u2889\u288A\u288B"+ + "\u288C\u288D\u288E\u288F\u2890\u2891\u2892\u2893"+ + "\u2894\u2895\u2896\u2897\u2898\u2899\u289A\u289B"+ + "\u289C\u289D\u289E\u289F\u28A0\u28A1\u28A2\u28A3"+ + "\u28A4\u28A5\u28A6\u28A7\u28A8\u28A9\u28AA\u28AB"+ + "\u28AC\u28AD\u28AE\u28AF\u28B0\u28B1\u28B2\u28B3"+ + "\u28B4\u28B5\u28B6\u28B7\u28B8\u28B9\u28BA\u28BB"+ + "\u28BC\u28BD\u28BE\u28BF\u28C0\u28C1\u28C2\u28C3"+ + "\u28C4\u28C5\u28C6\u28C7\u28C8\u28C9\u28CA\u28CB"+ + "\u28CC\u28CD\u28CE\u28CF\u28D0\u28D1\u28D2\u28D3"+ + "\u28D4\u28D5\u28D6\u28D7\u28D8\u28D9\u28DA\u28DB"+ + "\u28DC\u28DD\u28DE\u28DF\u28E0\u28E1\u28E2\u28E3"+ + "\u28E4\u28E5\u28E6\u28E7\u28E8\u28E9\u28EA\u28EB"+ + "\u28EC\u28ED\u28EE\u28EF\u28F0\u28F1\u28F2\u28F3"+ + "\u28F4\u28F5\u28F6\u28F7\u28F8\u28F9\u28FA\u28FB"+ + "\u28FC\u28FD\u28FE\u28FF\u2900\u2901\u2902\u2903"+ + "\u2904\u2905\u2906\u2907\u2908\u2909\u290A\u290B"+ + "\u290C\u290D\u290E\u290F\u2910\u2911\u2912\u2913"+ + "\u2914\u2915\u2916\u2917\u2918\u2919\u291A\u291B"+ + "\u291C\u291D\u291E\u291F\u2920\u2921\u2922\u2923"+ + "\u2924\u2925\u2926\u2927\u2928\u2929\u292A\u292B"+ + "\u292C\u292D\u292E\u292F\u2930\u2931\u2932\u2933"+ + "\u2934\u2935\u2936\u2937\u2938\u2939\u293A\u293B"+ + "\u293C\u293D\u293E\u293F\u2940\u2941\u2942\u2943"+ + "\u2944\u2945\u2946\u2947\u2948\u2949\u294A\u294B"+ + "\u294C\u294D\u294E\u294F\u2950\u2951\u2952\u2953"+ + "\u2954\u2955\u2956\u2957\u2958\u2959\u295A\u295B"+ + "\u295C\u295D\u295E\u295F\u2960\u2961\u2962\u2963"+ + "\u2964\u2965\u2966\u2967\u2968\u2969\u296A\u296B"+ + "\u296C\u296D\u296E\u296F\u2970\u2971\u2972\u2973"+ + "\u2974\u2975\u2976\u2977\u2978\u2979\u297A\u297B"+ + "\u297C\u297D\u297E\u297F\u2980\u2981\u2982\u2983"+ + "\u2984\u2985\u2986\u2987\u2988\u2989\u298A\u298B"+ + "\u298C\u298D\u298E\u298F\u2990\u2991\u2992\u2993"+ + "\u2994\u2995\u2996\u2997\u2998\u2999\u299A\u299B"+ + "\u299C\u299D\u299E\u299F\u29A0\u29A1\u29A2\u29A3"+ + "\u29A4\u29A5\u29A6\u29A7\u29A8\u29A9\u29AA\u29AB"+ + "\u29AC\u29AD\u29AE\u29AF\u29B0\u29B1\u29B2\u29B3"+ + "\u29B4\u29B5\u29B6\u29B7\u29B8\u29B9\u29BA\u29BB"+ + "\u29BC\u29BD\u29BE\u29BF\u29C0\u29C1\u29C2\u29C3"+ + "\u29C4\u29C5\u29C6\u29C7\u29C8\u29C9\u29CA\u29CB"+ + "\u29CC\u29CD\u29CE\u29CF\u29D0\u29D1\u29D2\u29D3"+ + "\u29D4\u29D5\u29D6\u29D7\u29D8\u29D9\u29DA\u29DB"+ + "\u29DC\u29DD\u29DE\u29DF\u29E0\u29E1\u29E2\u29E3"+ + "\u29E4\u29E5\u29E6\u29E7\u29E8\u29E9\u29EA\u29EB"+ + "\u29EC\u29ED\u29EE\u29EF\u29F0\u29F1\u29F2\u29F3"+ + "\u29F4\u29F5\u29F6\u29F7\u29F8\u29F9\u29FA\u29FB"+ + "\u29FC\u29FD\u29FE\u29FF\u2A00\u2A01\u2A02\u2A03"+ + "\u2A04\u2A05\u2A06\u2A07\u2A08\u2A09\u2A0A\u2A0B"+ + "\u2A0C\u2A0D\u2A0E\u2A0F\u2A10\u2A11\u2A12\u2A13"+ + "\u2A14\u2A15\u2A16\u2A17\u2A18\u2A19\u2A1A\u2A1B"+ + "\u2A1C\u2A1D\u2A1E\u2A1F\u2A20\u2A21\u2A22\u2A23"+ + "\u2A24\u2A25\u2A26\u2A27\u2A28\u2A29\u2A2A\u2A2B"+ + "\u2A2C\u2A2D\u2A2E\u2A2F\u2A30\u2A31\u2A32\u2A33"+ + "\u2A34\u2A35\u2A36\u2A37\u2A38\u2A39\u2A3A\u2A3B"+ + "\u2A3C\u2A3D\u2A3E\u2A3F\u2A40\u2A41\u2A42\u2A43"+ + "\u2A44\u2A45\u2A46\u2A47\u2A48\u2A49\u2A4A\u2A4B"+ + "\u2A4C\u2A4D\u2A4E\u2A4F\u2A50\u2A51\u2A52\u2A53"+ + "\u2A54\u2A55\u2A56\u2A57\u2A58\u2A59\u2A5A\u2A5B"+ + "\u2A5C\u2A5D\u2A5E\u2A5F\u2A60\u2A61\u2A62\u2A63"+ + "\u2A64\u2A65\u2A66\u2A67\u2A68\u2A69\u2A6A\u2A6B"+ + "\u2A6C\u2A6D\u2A6E\u2A6F\u2A70\u2A71\u2A72\u2A73"+ + "\u2A74\u2A75\u2A76\u2A77\u2A78\u2A79\u2A7A\u2A7B"+ + "\u2A7C\u2A7D\u2A7E\u2A7F\u2A80\u2A81\u2A82\u2A83"+ + "\u2A84\u2A85\u2A86\u2A87\u2A88\u2A89\u2A8A\u2A8B"+ + "\u2A8C\u2A8D\u2A8E\u2A8F\u2A90\u2A91\u2A92\u2A93"+ + "\u2A94\u2A95\u2A96\u2A97\u2A98\u2A99\u2A9A\u2A9B"+ + "\u2A9C\u2A9D\u2A9E\u2A9F\u2AA0\u2AA1\u2AA2\u2AA3"+ + "\u2AA4\u2AA5\u2AA6\u2AA7\u2AA8\u2AA9\u2AAA\u2AAB"+ + "\u2AAC\u2AAD\u2AAE\u2AAF\u2AB0\u2AB1\u2AB2\u2AB3"+ + "\u2AB4\u2AB5\u2AB6\u2AB7\u2AB8\u2AB9\u2ABA\u2ABB"+ + "\u2ABC\u2ABD\u2ABE\u2ABF\u2AC0\u2AC1\u2AC2\u2AC3"+ + "\u2AC4\u2AC5\u2AC6\u2AC7\u2AC8\u2AC9\u2ACA\u2ACB"+ + "\u2ACC\u2ACD\u2ACE\u2ACF\u2AD0\u2AD1\u2AD2\u2AD3"+ + "\u2AD4\u2AD5\u2AD6\u2AD7\u2AD8\u2AD9\u2ADA\u2ADB"+ + "\u2ADC\u2ADD\u2ADE\u2ADF\u2AE0\u2AE1\u2AE2\u2AE3"+ + "\u2AE4\u2AE5\u2AE6\u2AE7\u2AE8\u2AE9\u2AEA\u2AEB"+ + "\u2AEC\u2AED\u2AEE\u2AEF\u2AF0\u2AF1\u2AF2\u2AF3"+ + "\u2AF4\u2AF5\u2AF6\u2AF7\u2AF8\u2AF9\u2AFA\u2AFB"+ + "\u2AFC\u2AFD\u2AFE\u2AFF\u2B00\u2B01\u2B02\u2B03"+ + "\u2B04\u2B05\u2B06\u2B07\u2B08\u2B09\u2B0A\u2B0B"+ + "\u2B0C\u2B0D\u2B0E\u2B0F\u2B10\u2B11\u2B12\u2B13"+ + "\u2B14\u2B15\u2B16\u2B17\u2B18\u2B19\u2B1A\u2B1B"+ + "\u2B1C\u2B1D\u2B1E\u2B1F\u2B20\u2B21\u2B22\u2B23"+ + "\u2B24\u2B25\u2B26\u2B27\u2B28\u2B29\u2B2A\u2B2B"+ + "\u2B2C\u2B2D\u2B2E\u2B2F\u2B30\u2B31\u2B32\u2B33"+ + "\u2B34\u2B35\u2B36\u2B37\u2B38\u2B39\u2B3A\u2B3B"+ + "\u2B3C\u2B3D\u2B3E\u2B3F\u2B40\u2B41\u2B42\u2B43"+ + "\u2B44\u2B45\u2B46\u2B47\u2B48\u2B49\u2B4A\u2B4B"+ + "\u2B4C\u2B4D\u2B4E\u2B4F\u2B50\u2B51\u2B52\u2B53"+ + "\u2B54\u2B55\u2B56\u2B57\u2B58\u2B59\u2B5A\u2B5B"+ + "\u2B5C\u2B5D\u2B5E\u2B5F\u2B60\u2B61\u2B62\u2B63"+ + "\u2B64\u2B65\u2B66\u2B67\u2B68\u2B69\u2B6A\u2B6B"+ + "\u2B6C\u2B6D\u2B6E\u2B6F\u2B70\u2B71\u2B72\u2B73"+ + "\u2B74\u2B75\u2B76\u2B77\u2B78\u2B79\u2B7A\u2B7B"+ + "\u2B7C\u2B7D\u2B7E\u2B7F\u2B80\u2B81\u2B82\u2B83"+ + "\u2B84\u2B85\u2B86\u2B87\u2B88\u2B89\u2B8A\u2B8B"+ + "\u2B8C\u2B8D\u2B8E\u2B8F\u2B90\u2B91\u2B92\u2B93"+ + "\u2B94\u2B95\u2B96\u2B97\u2B98\u2B99\u2B9A\u2B9B"+ + "\u2B9C\u2B9D\u2B9E\u2B9F\u2BA0\u2BA1\u2BA2\u2BA3"+ + "\u2BA4\u2BA5\u2BA6\u2BA7\u2BA8\u2BA9\u2BAA\u2BAB"+ + "\u2BAC\u2BAD\u2BAE\u2BAF\u2BB0\u2BB1\u2BB2\u2BB3"+ + "\u2BB4\u2BB5\u2BB6\u2BB7\u2BB8\u2BB9\u2BBA\u2BBB"+ + "\u2BBC\u2BBD\u2BBE\u2BBF\u2BC0\u2BC1\u2BC2\u2BC3"+ + "\u2BC4\u2BC5\u2BC6\u2BC7\u2BC8\u2BC9\u2BCA\u2BCB"+ + "\u2BCC\u2BCD\u2BCE\u2BCF\u2BD0\u2BD1\u2BD2\u2BD3"+ + "\u2BD4\u2BD5\u2BD6\u2BD7\u2BD8\u2BD9\u2BDA\u2BDB"+ + "\u2BDC\u2BDD\u2BDE\u2BDF\u2BE0\u2BE1\u2BE2\u2BE3"+ + "\u2BE4\u2BE5\u2BE6\u2BE7\u2BE8\u2BE9\u2BEA\u2BEB"+ + "\u2BEC\u2BED\u2BEE\u2BEF\u2BF0\u2BF1\u2BF2\u2BF3"+ + "\u2BF4\u2BF5\u2BF6\u2BF7\u2BF8\u2BF9\u2BFA\u2BFB"+ + "\u2BFC\u2BFD\u2BFE\u2BFF\u2C00\u2C01\u2C02\u2C03"+ + "\u2C04\u2C05\u2C06\u2C07\u2C08\u2C09\u2C0A\u2C0B"+ + "\u2C0C\u2C0D\u2C0E\u2C0F\u2C10\u2C11\u2C12\u2C13"+ + "\u2C14\u2C15\u2C16\u2C17\u2C18\u2C19\u2C1A\u2C1B"+ + "\u2C1C\u2C1D\u2C1E\u2C1F\u2C20\u2C21\u2C22\u2C23"+ + "\u2C24\u2C25\u2C26\u2C27\u2C28\u2C29\u2C2A\u2C2B"+ + "\u2C2C\u2C2D\u2C2E\u2C2F\u2C30\u2C31\u2C32\u2C33"+ + "\u2C34\u2C35\u2C36\u2C37\u2C38\u2C39\u2C3A\u2C3B"+ + "\u2C3C\u2C3D\u2C3E\u2C3F\u2C40\u2C41\u2C42\u2C43"+ + "\u2C44\u2C45\u2C46\u2C47\u2C48\u2C49\u2C4A\u2C4B"+ + "\u2C4C\u2C4D\u2C4E\u2C4F\u2C50\u2C51\u2C52\u2C53"+ + "\u2C54\u2C55\u2C56\u2C57\u2C58\u2C59\u2C5A\u2C5B"+ + "\u2C5C\u2C5D\u2C5E\u2C5F\u2C60\u2C61\u2C62\u2C63"+ + "\u2C64\u2C65\u2C66\u2C67\u2C68\u2C69\u2C6A\u2C6B"+ + "\u2C6C\u2C6D\u2C6E\u2C6F\u2C70\u2C71\u2C72\u2C73"+ + "\u2C74\u2C75\u2C76\u2C77\u2C78\u2C79\u2C7A\u2C7B"+ + "\u2C7C\u2C7D\u2C7E\u2C7F\u2C80\u2C81\u2C82\u2C83"+ + "\u2C84\u2C85\u2C86\u2C87\u2C88\u2C89\u2C8A\u2C8B"+ + "\u2C8C\u2C8D\u2C8E\u2C8F\u2C90\u2C91\u2C92\u2C93"+ + "\u2C94\u2C95\u2C96\u2C97\u2C98\u2C99\u2C9A\u2C9B"+ + "\u2C9C\u2C9D\u2C9E\u2C9F\u2CA0\u2CA1\u2CA2\u2CA3"+ + "\u2CA4\u2CA5\u2CA6\u2CA7\u2CA8\u2CA9\u2CAA\u2CAB"+ + "\u2CAC\u2CAD\u2CAE\u2CAF\u2CB0\u2CB1\u2CB2\u2CB3"+ + "\u2CB4\u2CB5\u2CB6\u2CB7\u2CB8\u2CB9\u2CBA\u2CBB"+ + "\u2CBC\u2CBD\u2CBE\u2CBF\u2CC0\u2CC1\u2CC2\u2CC3"+ + "\u2CC4\u2CC5\u2CC6\u2CC7\u2CC8\u2CC9\u2CCA\u2CCB"+ + "\u2CCC\u2CCD\u2CCE\u2CCF\u2CD0\u2CD1\u2CD2\u2CD3"+ + "\u2CD4\u2CD5\u2CD6\u2CD7\u2CD8\u2CD9\u2CDA\u2CDB"+ + "\u2CDC\u2CDD\u2CDE\u2CDF\u2CE0\u2CE1\u2CE2\u2CE3"+ + "\u2CE4\u2CE5\u2CE6\u2CE7\u2CE8\u2CE9\u2CEA\u2CEB"+ + "\u2CEC\u2CED\u2CEE\u2CEF\u2CF0\u2CF1\u2CF2\u2CF3"+ + "\u2CF4\u2CF5\u2CF6\u2CF7\u2CF8\u2CF9\u2CFA\u2CFB"+ + "\u2CFC\u2CFD\u2CFE\u2CFF\u2D00\u2D01\u2D02\u2D03"+ + "\u2D04\u2D05\u2D06\u2D07\u2D08\u2D09\u2D0A\u2D0B"+ + "\u2D0C\u2D0D\u2D0E\u2D0F\u2D10\u2D11\u2D12\u2D13"+ + "\u2D14\u2D15\u2D16\u2D17\u2D18\u2D19\u2D1A\u2D1B"+ + "\u2D1C\u2D1D\u2D1E\u2D1F\u2D20\u2D21\u2D22\u2D23"+ + "\u2D24\u2D25\u2D26\u2D27\u2D28\u2D29\u2D2A\u2D2B"+ + "\u2D2C\u2D2D\u2D2E\u2D2F\u2D30\u2D31\u2D32\u2D33"+ + "\u2D34\u2D35\u2D36\u2D37\u2D38\u2D39\u2D3A\u2D3B"+ + "\u2D3C\u2D3D\u2D3E\u2D3F\u2D40\u2D41\u2D42\u2D43"+ + "\u2D44\u2D45\u2D46\u2D47\u2D48\u2D49\u2D4A\u2D4B"+ + "\u2D4C\u2D4D\u2D4E\u2D4F\u2D50\u2D51\u2D52\u2D53"+ + "\u2D54\u2D55\u2D56\u2D57\u2D58\u2D59\u2D5A\u2D5B"+ + "\u2D5C\u2D5D\u2D5E\u2D5F\u2D60\u2D61\u2D62\u2D63"+ + "\u2D64\u2D65\u2D66\u2D67\u2D68\u2D69\u2D6A\u2D6B"+ + "\u2D6C\u2D6D\u2D6E\u2D6F\u2D70\u2D71\u2D72\u2D73"+ + "\u2D74\u2D75\u2D76\u2D77\u2D78\u2D79\u2D7A\u2D7B"+ + "\u2D7C\u2D7D\u2D7E\u2D7F\u2D80\u2D81\u2D82\u2D83"+ + "\u2D84\u2D85\u2D86\u2D87\u2D88\u2D89\u2D8A\u2D8B"+ + "\u2D8C\u2D8D\u2D8E\u2D8F\u2D90\u2D91\u2D92\u2D93"+ + "\u2D94\u2D95\u2D96\u2D97\u2D98\u2D99\u2D9A\u2D9B"+ + "\u2D9C\u2D9D\u2D9E\u2D9F\u2DA0\u2DA1\u2DA2\u2DA3"+ + "\u2DA4\u2DA5\u2DA6\u2DA7\u2DA8\u2DA9\u2DAA\u2DAB"+ + "\u2DAC\u2DAD\u2DAE\u2DAF\u2DB0\u2DB1\u2DB2\u2DB3"+ + "\u2DB4\u2DB5\u2DB6\u2DB7\u2DB8\u2DB9\u2DBA\u2DBB"+ + "\u2DBC\u2DBD\u2DBE\u2DBF\u2DC0\u2DC1\u2DC2\u2DC3"+ + "\u2DC4\u2DC5\u2DC6\u2DC7\u2DC8\u2DC9\u2DCA\u2DCB"+ + "\u2DCC\u2DCD\u2DCE\u2DCF\u2DD0\u2DD1\u2DD2\u2DD3"+ + "\u2DD4\u2DD5\u2DD6\u2DD7\u2DD8\u2DD9\u2DDA\u2DDB"+ + "\u2DDC\u2DDD\u2DDE\u2DDF\u2DE0\u2DE1\u2DE2\u2DE3"+ + "\u2DE4\u2DE5\u2DE6\u2DE7\u2DE8\u2DE9\u2DEA\u2DEB"+ + "\u2DEC\u2DED\u2DEE\u2DEF\u2DF0\u2DF1\u2DF2\u2DF3"+ + "\u2DF4\u2DF5\u2DF6\u2DF7\u2DF8\u2DF9\u2DFA\u2DFB"+ + "\u2DFC\u2DFD\u2DFE\u2DFF\u2E00\u2E01\u2E02\u2E03"+ + "\u2E04\u2E05\u2E06\u2E07\u2E08\u2E09\u2E0A\u2E0B"+ + "\u2E0C\u2E0D\u2E0E\u2E0F\u2E10\u2E11\u2E12\u2E13"+ + "\u2E14\u2E15\u2E16\u2E17\u2E18\u2E19\u2E1A\u2E1B"+ + "\u2E1C\u2E1D\u2E1E\u2E1F\u2E20\u2E21\u2E22\u2E23"+ + "\u2E24\u2E25\u2E26\u2E27\u2E28\u2E29\u2E2A\u2E2B"+ + "\u2E2C\u2E2D\u2E2E\u2E2F\u2E30\u2E31\u2E32\u2E33"+ + "\u2E34\u2E35\u2E36\u2E37\u2E38\u2E39\u2E3A\u2E3B"+ + "\u2E3C\u2E3D\u2E3E\u2E3F\u2E40\u2E41\u2E42\u2E43"+ + "\u2E44\u2E45\u2E46\u2E47\u2E48\u2E49\u2E4A\u2E4B"+ + "\u2E4C\u2E4D\u2E4E\u2E4F\u2E50\u2E51\u2E52\u2E53"+ + "\u2E54\u2E55\u2E56\u2E57\u2E58\u2E59\u2E5A\u2E5B"+ + "\u2E5C\u2E5D\u2E5E\u2E5F\u2E60\u2E61\u2E62\u2E63"+ + "\u2E64\u2E65\u2E66\u2E67\u2E68\u2E69\u2E6A\u2E6B"+ + "\u2E6C\u2E6D\u2E6E\u2E6F\u2E70\u2E71\u2E72\u2E73"+ + "\u2E74\u2E75\u2E76\u2E77\u2E78\u2E79\u2E7A\u2E7B"+ + "\u2E7C\u2E7D\u2E7E\u2E7F\u2E80\u2E81\u2E82\u2E83"+ + "\u2E84\u2E85\u2E86\u2E87\u2E88\u2E89\u2E8A\u2E8B"+ + "\u2E8C\u2E8D\u2E8E\u2E8F\u2E90\u2E91\u2E92\u2E93"+ + "\u2E94\u2E95\u2E96\u2E97\u2E98\u2E99\u2E9A\u2E9B"+ + "\u2E9C\u2E9D\u2E9E\u2E9F\u2EA0\u2EA1\u2EA2\u2EA3"+ + "\u2EA4\u2EA5\u2EA6\u2EA7\u2EA8\u2EA9\u2EAA\u2EAB"+ + "\u2EAC\u2EAD\u2EAE\u2EAF\u2EB0\u2EB1\u2EB2\u2EB3"+ + "\u2EB4\u2EB5\u2EB6\u2EB7\u2EB8\u2EB9\u2EBA\u2EBB"+ + "\u2EBC\u2EBD\u2EBE\u2EBF\u2EC0\u2EC1\u2EC2\u2EC3"+ + "\u2EC4\u2EC5\u2EC6\u2EC7\u2EC8\u2EC9\u2ECA\u2ECB"+ + "\u2ECC\u2ECD\u2ECE\u2ECF\u2ED0\u2ED1\u2ED2\u2ED3"+ + "\u2ED4\u2ED5\u2ED6\u2ED7\u2ED8\u2ED9\u2EDA\u2EDB"+ + "\u2EDC\u2EDD\u2EDE\u2EDF\u2EE0\u2EE1\u2EE2\u2EE3"+ + "\u2EE4\u2EE5\u2EE6\u2EE7\u2EE8\u2EE9\u2EEA\u2EEB"+ + "\u2EEC\u2EED\u2EEE\u2EEF\u2EF0\u2EF1\u2EF2\u2EF3"+ + "\u2EF4\u2EF5\u2EF6\u2EF7\u2EF8\u2EF9\u2EFA\u2EFB"+ + "\u2EFC\u2EFD\u2EFE\u2EFF\u2F00\u2F01\u2F02\u2F03"+ + "\u2F04\u2F05\u2F06\u2F07\u2F08\u2F09\u2F0A\u2F0B"+ + "\u2F0C\u2F0D\u2F0E\u2F0F\u2F10\u2F11\u2F12\u2F13"+ + "\u2F14\u2F15\u2F16\u2F17\u2F18\u2F19\u2F1A\u2F1B"+ + "\u2F1C\u2F1D\u2F1E\u2F1F\u2F20\u2F21\u2F22\u2F23"+ + "\u2F24\u2F25\u2F26\u2F27\u2F28\u2F29\u2F2A\u2F2B"+ + "\u2F2C\u2F2D\u2F2E\u2F2F\u2F30\u2F31\u2F32\u2F33"+ + "\u2F34\u2F35\u2F36\u2F37\u2F38\u2F39\u2F3A\u2F3B"+ + "\u2F3C\u2F3D\u2F3E\u2F3F\u2F40\u2F41\u2F42\u2F43"+ + "\u2F44\u2F45\u2F46\u2F47\u2F48\u2F49\u2F4A\u2F4B"+ + "\u2F4C\u2F4D\u2F4E\u2F4F\u2F50\u2F51\u2F52\u2F53"+ + "\u2F54\u2F55\u2F56\u2F57\u2F58\u2F59\u2F5A\u2F5B"+ + "\u2F5C\u2F5D\u2F5E\u2F5F\u2F60\u2F61\u2F62\u2F63"+ + "\u2F64\u2F65\u2F66\u2F67\u2F68\u2F69\u2F6A\u2F6B"+ + "\u2F6C\u2F6D\u2F6E\u2F6F\u2F70\u2F71\u2F72\u2F73"+ + "\u2F74\u2F75\u2F76\u2F77\u2F78\u2F79\u2F7A\u2F7B"+ + "\u2F7C\u2F7D\u2F7E\u2F7F\u2F80\u2F81\u2F82\u2F83"+ + "\u2F84\u2F85\u2F86\u2F87\u2F88\u2F89\u2F8A\u2F8B"+ + "\u2F8C\u2F8D\u2F8E\u2F8F\u2F90\u2F91\u2F92\u2F93"+ + "\u2F94\u2F95\u2F96\u2F97\u2F98\u2F99\u2F9A\u2F9B"+ + "\u2F9C\u2F9D\u2F9E\u2F9F\u2FA0\u2FA1\u2FA2\u2FA3"+ + "\u2FA4\u2FA5\u2FA6\u2FA7\u2FA8\u2FA9\u2FAA\u2FAB"+ + "\u2FAC\u2FAD\u2FAE\u2FAF\u2FB0\u2FB1\u2FB2\u2FB3"+ + "\u2FB4\u2FB5\u2FB6\u2FB7\u2FB8\u2FB9\u2FBA\u2FBB"+ + "\u2FBC\u2FBD\u2FBE\u2FBF\u2FC0\u2FC1\u2FC2\u2FC3"+ + "\u2FC4\u2FC5\u2FC6\u2FC7\u2FC8\u2FC9\u2FCA\u2FCB"+ + "\u2FCC\u2FCD\u2FCE\u2FCF\u2FD0\u2FD1\u2FD2\u2FD3"+ + "\u2FD4\u2FD5\u2FD6\u2FD7\u2FD8\u2FD9\u2FDA\u2FDB"+ + "\u2FDC\u2FDD\u2FDE\u2FDF\u2FE0\u2FE1\u2FE2\u2FE3"+ + "\u2FE4\u2FE5\u2FE6\u2FE7\u2FE8\u2FE9\u2FEA\u2FEB"+ + "\u2FEC\u2FED\u2FEE\u2FEF\u2FF0\u2FF1\u2FF2\u2FF3"+ + "\u2FF4\u2FF5\u2FF6\u2FF7\u2FF8\u2FF9\u2FFA\u2FFB"+ + "\u2FFC\u2FFD\u2FFE\u2FFF\u3000\u3001\u3002\u3003"+ + "\u3004\u3005\u3006\u3007\u3008\u3009\u300A\u300B"+ + "\u300C\u300D\u300E\u300F\u3010\u3011\u3012\u3013"+ + "\u3014\u3015\u3016\u3017\u3018\u3019\u301A\u301B"+ + "\u301C\u301D\u301E\u301F\u3020\u3021\u3022\u3023"+ + "\u3024\u3025\u3026\u3027\u3028\u3029\u302A\u302B"+ + "\u302C\u302D\u302E\u302F\u3030\u3031\u3032\u3033"+ + "\u3034\u3035\u3036\u3037\u3038\u3039\u303A\u303B"+ + "\u303C\u303D\u303E\u303F\u3040\u3041\u3042\u3043"+ + "\u3044\u3045\u3046\u3047\u3048\u3049\u304A\u304B"+ + "\u304C\u304D\u304E\u304F\u3050\u3051\u3052\u3053"+ + "\u3054\u3055\u3056\u3057\u3058\u3059\u305A\u305B"+ + "\u305C\u305D\u305E\u305F\u3060\u3061\u3062\u3063"+ + "\u3064\u3065\u3066\u3067\u3068\u3069\u306A\u306B"+ + "\u306C\u306D\u306E\u306F\u3070\u3071\u3072\u3073"+ + "\u3074\u3075\u3076\u3077\u3078\u3079\u307A\u307B"+ + "\u307C\u307D\u307E\u307F\u3080\u3081\u3082\u3083"+ + "\u3084\u3085\u3086\u3087\u3088\u3089\u308A\u308B"+ + "\u308C\u308D\u308E\u308F\u3090\u3091\u3092\u3093"+ + "\u3094\u3095\u3096\u3097\u3098\u3099\u309A\u309B"+ + "\u309C\u309D\u309E\u309F\u30A0\u30A1\u30A2\u30A3"+ + "\u30A4\u30A5\u30A6\u30A7\u30A8\u30A9\u30AA\u30AB"+ + "\u30AC\u30AD\u30AE\u30AF\u30B0\u30B1\u30B2\u30B3"+ + "\u30B4\u30B5\u30B6\u30B7\u30B8\u30B9\u30BA\u30BB"+ + "\u30BC\u30BD\u30BE\u30BF\u30C0\u30C1\u30C2\u30C3"+ + "\u30C4\u30C5\u30C6\u30C7\u30C8\u30C9\u30CA\u30CB"+ + "\u30CC\u30CD\u30CE\u30CF\u30D0\u30D1\u30D2\u30D3"+ + "\u30D4\u30D5\u30D6\u30D7\u30D8\u30D9\u30DA\u30DB"+ + "\u30DC\u30DD\u30DE\u30DF\u30E0\u30E1\u30E2\u30E3"+ + "\u30E4\u30E5\u30E6\u30E7\u30E8\u30E9\u30EA\u30EB"+ + "\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007"+ + "\u2008\u2009\u200A\u200B\u200C\u200D\u200E\u200F"+ + "\u2010\u2011\u2012\u2013\u2014\u2015\u2016\u2017"+ + "\u2018\u2019\u201A\u201B\u201C\u201D\u201E\u201F"+ + "\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027"+ + "\u2028\u2029\u202A\u202B\uFD9C\u202C\u202D\u202E"+ + "\u202F\u2030\u2031\u2032\u2033\u2034\u2035\u2036"+ + "\u2037\u2038\u2039\u203A\u203B\u203C\u203D\u203E"+ + "\u203F\u2040\u2041\u2042\u2043\u2044\u2045\u2046"+ + "\u2047\u2048\u2049\u204A\u204B\u204C\u204D\u204E"+ + "\u204F\u2050\u2051\u2052\u2053\u2054\u2055\u2056"+ + "\u2057\u2058\u2059\u205A\u205B\u205C\u205D\u205E"+ + "\u205F\u2060\u2061\u2062\u2063\u2064\u2065\u2066"+ + "\u2067\u2068\u2069\u206A\u206B\u206C\u206D\u206E"+ + "\u206F\u2070\u2071\u2072\u2073\u2074\u2075\u2076"+ + "\u2077\uFD9D\u2078\u2079\u207A\u207B\u207C\u207D"+ + "\u207E\u207F\u2080\u2081\u2082\u2083\u2084\u2085"+ + "\u2086\u2087\u2088\u2089\u208A\u208B\u208C\u208D"+ + "\u208E\u208F\u2090\u2091\u2092\uFD9E\u2093\u2094"+ + "\u2095\u2096\u2097\u2098\u2099\u209A\u209B\u209C"+ + "\u209D\u209E\u209F\u20A0\u20A1\u20A2\u20A3\u20A4"+ + "\u20A5\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB\u20AC"+ + "\u20AD\u20AE\u20AF\u20B0\u20B1\u20B2\u20B3\u20B4"+ + "\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC"+ + "\u20BD\u20BE\u20BF\u20C0\u20C1\u20C2\u20C3\u20C4"+ + "\u20C5\u20C6\u20C7\u20C8\u20C9\u20CA\u20CB\u20CC"+ + "\u20CD\u20CE\u20CF\u20D0\u20D1\u20D2\u20D3\u20D4"+ + "\u20D5\u20D6\u20D7\u20D8\u20D9\u20DA\u20DB\u20DC"+ + "\u20DD\u20DE\u20DF\u20E0\u20E1\u20E2\u20E3\uFD9F"+ + "\u20E4\u20E5\u20E6\u20E7\u20E8\u20E9\u20EA\u20EB"+ + "\u20EC\uFDA0\u20ED\u20EE\u20EF\u20F0\u20F1\u20F2"+ + "\u20F3\u20F4\u20F5\u20F6\u20F7\u20F8\u20F9\u20FA"+ + "\u20FB\u20FC\u20FD\u20FE\u20FF\u2100\u2101\u2102"+ + "\u2103\u2104\u2105\u2106\uFE40\uFE41\uFE42\uFE43"+ + "\u2107\uFE44\u2108\uFE45\uFE46\u2109\u210A\u210B"+ + "\uFE47\u210C\u210D\u210E\u210F\u2110\u2111\uFE48"+ + "\uFE49\uFE4A\u2112\uFE4B\uFE4C\u2113\u2114\uFE4D"+ + "\uFE4E\uFE4F\u2115\u2116\u2117\u2118\u2119\u211A"+ + "\u211B\u211C\u211D\u211E\u211F\u2120\u2121\u2122"+ + "\u2123\u2124\u2125\u2126\u2127\u2128\u2129\u212A"+ + "\u212B\u212C\u212D\u212E\u212F\u2130\u2131\u2132"+ + "\u2133\u2134\u2135\u2136\u2137\u2138\u2139\u213A"+ + "\u213B\u213C\u213D\u213E\u213F\u2140\u2141\u2142"+ + "\u2143\u2144\u2145\u2146\u2147\u2148\u2149\u214A"+ + "\u214B\u214C\u214D\u214E\u214F\u2150\u2151\u2152"+ + "\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215A"+ + "\u215B\u215C\u215D\u215E\u215F\u2160\u2161\u2162"+ + "\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216A"+ + "\u216B\u216C\u216D\u216E\u216F\u2170\u2171\u2172"+ + "\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217A"+ + "\u217B\u217C\u217D\u217E\u217F\u2180\u2181\u2182"+ + "\u2183\u2184\u2185\u2186\u2187\u2188\u2189\u218A"+ + "\u218B\u218C\u218D\u218E\u218F\u2190\u2191\u2192"+ + "\u2193\u2194\u2195\u2196\u2197\u2198\u2199\u219A"+ + "\u219B\u219C\u219D\u219E\u219F\u21A0\u21A1\u21A2"+ + "\u21A3\u21A4\u21A5\u21A6\u21A7\u21A8\u21A9\u21AA"+ + "\u21AB\u21AC\u21AD\u21AE\u21AF\u21B0\u21B1\u21B2"+ + "\u21B3\u21B4\u21B5\u21B6\u21B7\u21B8\u21B9\u21BA"+ + "\u21BB\u21BC\u21BD\u21BE\u21BF\u21C0\u21C1\u21C2"+ + "\u21C3\u21C4\u21C5\u21C6\u21C7\u21C8\u21C9\u21CA"+ + "\u21CB\u21CC\u21CD\u21CE\u21CF\u21D0\u21D1\u21D2"+ + "\u21D3\u21D4\u21D5\u21D6\u21D7\u21D8\u21D9\u21DA"+ + "\u21DB\u21DC\u21DD\u21DE\u21DF\u21E0\u21E1\u21E2"+ + "\u21E3\u21E4\u21E5\u21E6\u21E7\u21E8\u21E9\u21EA"; + + private final static String innerEncoderIndex12= + "\u21EB\u21EC\u21ED\u21EE\u21EF\u21F0\u21F1\u21F2"+ + "\u21F3\u21F4\u21F5\u21F6\u21F7\u21F8\u21F9\u21FA"+ + "\u21FB\u21FC\u21FD\u21FE\u21FF\u2200\u2201\u2202"+ + "\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220A"+ + "\u220B\u220C\u220D\u220E\u220F\u2210\u2211\u2212"+ + "\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221A"+ + "\u221B\u221C\u221D\u221E\u221F\u2220\u2221\u2222"+ + "\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222A"+ + "\u222B\u222C\u222D\u222E\u222F\u2230\u2231\u2232"+ + "\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223A"+ + "\u223B\u223C\u223D\u223E\u223F\u2240\u2241\u2242"+ + "\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224A"+ + "\u224B\u224C\u224D\u224E\u224F\u2250\u2251\u2252"+ + "\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225A"+ + "\u225B\u225C\u225D\u225E\u225F\u2260\u2261\u2262"+ + "\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226A"+ + "\u226B\u226C\u226D\u226E\u226F\u2270\u2271\u2272"+ + "\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227A"+ + "\u227B\u227C\u227D\u227E\u227F\u2280\u2281\u2282"+ + "\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228A"+ + "\u228B\u228C\u228D\u228E\u228F\u2290\u2291\u2292"+ + "\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229A"+ + "\u229B\u229C\u229D\u229E\u229F\u22A0\u22A1\u22A2"+ + "\u22A3\u22A4\u22A5\u22A6\u22A7\u22A8\u22A9\u22AA"+ + "\u22AB\u22AC\u22AD\u22AE\u22AF\u22B0\u22B1\u22B2"+ + "\u22B3\u22B4\u22B5\u22B6\u22B7\u22B8\u22B9\u22BA"+ + "\u22BB\u22BC\u22BD\u22BE\u22BF\u22C0\u22C1\u22C2"+ + "\u22C3\u22C4\u22C5\u22C6\u22C7\u22C8\u22C9\u22CA"+ + "\u22CB\u22CC\u22CD\u22CE\u22CF\u22D0\u22D1\u22D2"+ + "\u22D3\u22D4\u22D5\u22D6\u22D7\u22D8\u22D9\u22DA"+ + "\u22DB\u22DC\u22DD\u22DE\u22DF\u22E0\u22E1\u22E2"+ + "\u22E3\u22E4\u22E5\u22E6\u22E7\u22E8\u22E9\u22EA"+ + "\u22EB\u22EC\u22ED\u22EE\u22EF\u22F0\u22F1\u22F2"+ + "\u22F3\u22F4\u22F5\u22F6\u22F7\u22F8\u22F9\u22FA"+ + "\u22FB\u22FC\u22FD\u22FE\u22FF\u2300\u2301\u2302"+ + "\u2303\u2304\u2305\u2306\u2307\u2308\u2309\u230A"+ + "\u230B\u230C\u230D\u230E\u230F\u2310\u2311\u2312"+ + "\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231A"+ + "\u231B\u231C\u231D\u231E\u231F\u2320\u2321\u2322"+ + "\u2323\u2324\u2325\u2326\u2327\u2328\u2329\u232A"+ + "\u232B\u232C\u232D\u232E\u232F\u2330\u2331\u2332"+ + "\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233A"+ + "\u233B\u233C\u233D\u233E\u233F\u2340\u2341\u2342"+ + "\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234A"+ + "\u234B\u234C\u234D\u234E\u234F\u2350\u2351\u2352"+ + "\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235A"+ + "\u235B\u235C\u235D\u235E\u235F\u2360\u2361\u2362"+ + "\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236A"+ + "\u236B\u236C\u236D\u236E\u236F\u2370\u2371\u2372"+ + "\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237A"+ + "\u237B\u237C\u237D\u237E\u237F\u2380\u2381\u2382"+ + "\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238A"+ + "\u238B\u238C\u238D\u238E\u238F\u2390\u2391\u2392"+ + "\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239A"+ + "\u239B\u239C\u239D\u239E\u239F\u23A0\u23A1\u23A2"+ + "\u23A3\u23A4\u23A5\u23A6\u23A7\u23A8\u23A9\u23AA"+ + "\u23AB\u23AC\u23AD\u23AE\u23AF\u23B0\u23B1\u23B2"+ + "\u23B3\u23B4\u23B5\u23B6\u23B7\u23B8\u23B9\u23BA"+ + "\u23BB\u23BC\u23BD\u23BE\u23BF\u23C0\u23C1\u23C2"+ + "\u23C3\u23C4\u23C5\u23C6\u23C7\u23C8\u23C9\u23CA"+ + "\u23CB\u23CC\u23CD\u23CE\u23CF\u23D0\u23D1\u23D2"+ + "\u23D3\u23D4\u23D5\u23D6\u23D7\u23D8\u23D9\u23DA"+ + "\u23DB\u23DC\u23DD\u23DE\u23DF\u23E0\u23E1\u23E2"+ + "\u23E3\u23E4\u23E5\u23E6\u23E7\u23E8\u23E9\u23EA"+ + "\u23EB\u23EC\u23ED\u23EE\u23EF\u23F0\u23F1\u23F2"+ + "\u23F3\u23F4\u23F5\u23F6\u23F7\u23F8\u23F9\u23FA"+ + "\u23FB\u23FC\u23FD\u23FE\u23FF\u2400\u2401\u2402"+ + "\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240A"+ + "\u240B\u240C\u240D\u240E\u240F\u2410\u2411\u2412"+ + "\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241A"+ + "\u241B\u241C\u241D\u241E\u241F\u2420\u2421\u2422"+ + "\u2423\u2424\u2425\u2426\u2427\u2428\u2429\u242A"+ + "\u242B\u242C\u242D\u242E\u242F\u2430\u2431\u2432"+ + "\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243A"+ + "\u243B\u243C\u243D\u243E\u243F\u2440\u2441\u2442"+ + "\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244A"+ + "\u244B\u244C\u244D\u244E\u244F\u2450\u2451\u2452"+ + "\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245A"+ + "\u245B\u245C\u245D\u245E\u245F\u2460\u2461\u2462"+ + "\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246A"+ + "\u246B\u246C\u246D\u246E\u246F\u2470\u2471\u2472"+ + "\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247A"+ + "\u247B\u247C\u247D\u247E\u247F\u2480\u2481\u2482"+ + "\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248A"+ + "\u248B\u248C\u248D\u248E\u248F\u2490\u2491\u2492"+ + "\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249A"+ + "\u249B\u249C\u249D\u249E\u249F\u24A0\u24A1\u24A2"+ + "\u24A3\u24A4\u24A5\u24A6\u24A7\u24A8\u24A9\u24AA"+ + "\u24AB\u24AC\u24AD\u24AE\u24AF\u24B0\u24B1\u24B2"+ + "\u24B3\u24B4\u24B5\u24B6\u24B7\u24B8\u24B9\u24BA"+ + "\u24BB\u24BC\u24BD\u24BE\u24BF\u24C0\u24C1\u24C2"+ + "\u24C3\u24C4\u24C5\u24C6\u24C7\u24C8\u24C9\u24CA"+ + "\u24CB\u24CC\u24CD\u24CE\u24CF\u24D0\u24D1\u24D2"+ + "\u24D3\u24D4\u24D5\u24D6\u24D7\u24D8\u24D9\u24DA"+ + "\u24DB\u24DC\u24DD\u24DE\u24DF\u24E0\u24E1\u24E2"+ + "\u24E3\u24E4\u24E5\u24E6\u24E7\u24E8\u24E9\u24EA"+ + "\u24EB\u24EC\u24ED\u24EE\u24EF\u24F0\u24F1\u24F2"+ + "\u24F3\u24F4\u24F5\u24F6\u24F7\u24F8\u24F9\u24FA"+ + "\u24FB\u24FC\u24FD\u24FE\u24FF\u2500\u2501\u2502"+ + "\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250A"+ + "\u250B\u250C\u250D\u250E\u250F\u2510\u2511\u2512"+ + "\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251A"+ + "\uA955\uA6F2\u251B\uA6F4\uA6F5\uA6E0\uA6E1\uA6F0"+ + "\uA6F1\uA6E2\uA6E3\uA6EE\uA6EF\uA6E6\uA6E7\uA6E4"+ + "\uA6E5\uA6E8\uA6E9\uA6EA\uA6EB\u251C\u251D\u251E"+ + "\u251F\uA968\uA969\uA96A\uA96B\uA96C\uA96D\uA96E"+ + "\uA96F\uA970\uA971\u2520\uA972\uA973\uA974\uA975"+ + "\u2521\uA976\uA977\uA978\uA979\uA97A\uA97B\uA97C"+ + "\uA97D\uA97E\uA980\uA981\uA982\uA983\uA984\u2522"+ + "\uA985\uA986\uA987\uA988\u2523\u2524\u2525\u2526"+ + "\u2527\u2528\u2529\u252A\u252B\u252C\u252D\u252E"+ + "\u252F\u2530\u2531\u2532\u2533\u2534\u2535\u2536"+ + "\u2537\u2538\u2539\u253A\u253B\u253C\u253D\u253E"+ + "\u253F\u2540\u2541\u2542\u2543\u2544\u2545\u2546"+ + "\u2547\u2548\u2549\u254A\u254B\u254C\u254D\u254E"+ + "\u254F\u2550\u2551\u2552\u2553\u2554\u2555\u2556"+ + "\u2557\u2558\u2559\u255A\u255B\u255C\u255D\u255E"+ + "\u255F\u2560\u2561\u2562\u2563\u2564\u2565\u2566"+ + "\u2567\u2568\u2569\u256A\u256B\u256C\u256D\u256E"+ + "\u256F\u2570\u2571\u2572\u2573\u2574\u2575\u2576"+ + "\u2577\u2578\u2579\u257A\u257B\u257C\u257D\u257E"+ + "\u257F\u2580\u2581\u2582\u2583\u2584\u2585\u2586"+ + "\u2587\u2588\u2589\u258A\u258B\u258C\u258D\u258E"+ + "\u258F\u2590\u2591\u2592\u2593\u2594\u2595\u2596"+ + "\u2597\u2598\u2599\u259A\u259B\u259C\u259D\u259E"+ + "\u259F\u25A0\u25A1\u25A2\u25A3\u25A4\u25A5\u25A6"+ + "\u25A7\u25A8\u25A9\u25AA\u25AB\u25AC\u25AD\u25AE"+ + "\u25AF\u25B0\u25B1\u25B2\u25B3\u25B4\u25B5\u25B6"+ + "\u25B7\uA3A1\uA3A2\uA3A3\uA1E7\uA3A5\uA3A6\uA3A7"+ + "\uA3A8\uA3A9\uA3AA\uA3AB\uA3AC\uA3AD\uA3AE\uA3AF"+ + "\uA3B0\uA3B1\uA3B2\uA3B3\uA3B4\uA3B5\uA3B6\uA3B7"+ + "\uA3B8\uA3B9\uA3BA\uA3BB\uA3BC\uA3BD\uA3BE\uA3BF"+ + "\uA3C0\uA3C1\uA3C2\uA3C3\uA3C4\uA3C5\uA3C6\uA3C7"+ + "\uA3C8\uA3C9\uA3CA\uA3CB\uA3CC\uA3CD\uA3CE\uA3CF"+ + "\uA3D0\uA3D1\uA3D2\uA3D3\uA3D4\uA3D5\uA3D6\uA3D7"+ + "\uA3D8\uA3D9\uA3DA\uA3DB\uA3DC\uA3DD\uA3DE\uA3DF"+ + "\uA3E0\uA3E1\uA3E2\uA3E3\uA3E4\uA3E5\uA3E6\uA3E7"+ + "\uA3E8\uA3E9\uA3EA\uA3EB\uA3EC\uA3ED\uA3EE\uA3EF"+ + "\uA3F0\uA3F1\uA3F2\uA3F3\uA3F4\uA3F5\uA3F6\uA3F7"+ + "\uA3F8\uA3F9\uA3FA\uA3FB\uA3FC\uA3FD\uA1AB\u25B8"+ + "\u25B9\u25BA\u25BB\u25BC\u25BD\u25BE\u25BF\u25C0"+ + "\u25C1\u25C2\u25C3\u25C4\u25C5\u25C6\u25C7\u25C8"+ + "\u25C9\u25CA\u25CB\u25CC\u25CD\u25CE\u25CF\u25D0"+ + "\u25D1\u25D2\u25D3\u25D4\u25D5\u25D6\u25D7\u25D8"+ + "\u25D9\u25DA\u25DB\u25DC\u25DD\u25DE\u25DF\u25E0"+ + "\u25E1\u25E2\u25E3\u25E4\u25E5\u25E6\u25E7\u25E8"+ + "\u25E9\u25EA\u25EB\u25EC\u25ED\u25EE\u25EF\u25F0"+ + "\u25F1\u25F2\u25F3\u25F4\u25F5\u25F6\u25F7\u25F8"+ + "\u25F9\u25FA\u25FB\u25FC\u25FD\u25FE\u25FF\u2600"+ + "\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608"+ + "\u2609\u260A\u260B\u260C\u260D\u260E\u260F\u2610"+ + "\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618"+ + "\u2619\u261A\u261B\u261C\u261D\u261E\u261F\u2620"+ + "\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628"+ + "\u2629\u262A\u262B\u262C\u262D\u262E\u262F\u2630"+ + "\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638"+ + "\uA1E9\uA1EA\uA956\uA3FE\uA957\uA3A4\u2639\u263A"+ + "\u263B\u263C\u263D\u263E\u263F\u2640\u2641\u2642"+ + "\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264A"+ + "\u264B\u264C\u264D\u264E\u264F\u2650\u2651\u2652"; + + private final static short encoderIndex1[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196 + }; + + static String encoderIndex2[] = { + innerEncoderIndex0, + innerEncoderIndex1, + innerEncoderIndex2, + innerEncoderIndex3, + innerEncoderIndex4, + innerEncoderIndex5, + innerEncoderIndex6, + innerEncoderIndex7, + innerEncoderIndex8, + innerEncoderIndex9, + innerEncoderIndex10, + innerEncoderIndex11, + innerEncoderIndex12 + }; + private static class Decoder extends CharsetDecoder { + + private static final char REPLACE_CHAR = '\uFFFD'; + private int currentState = GB18030_DOUBLE_BYTE; + + private Decoder(Charset cs) { + super(cs, 1.0f, 2.0f); + } + + private char getChar(int offset) { + int byte1 = (offset >>8) & 0xFF; + int byte2 = (offset & 0xFF); + int start = 0, end = 0xFF; + + if (((byte1 < 0) || (byte1 > decoderIndex1.length)) + || ((byte2 < start) || (byte2 > end))) { + return REPLACE_CHAR; + } + + int n = (decoderIndex1[byte1] & 0xf) * (end - start + 1) + (byte2 - start); + return decoderIndex2[decoderIndex1[byte1] >> 4].charAt(n); + } + + protected char decodeDouble(int byte1, int byte2) { + int start = 0x40, end = 0xFE; + if (((byte1 < 0) || (byte1 > index1.length)) + || ((byte2 < start) || (byte2 > end))) + return '\uFFFD'; + + int n = (index1[byte1] & 0xf) * (end - start + 1) + (byte2 - start); + return index2[index1[byte1] >> 4].charAt(n); + } + + protected void implReset() { + currentState = GB18030_DOUBLE_BYTE; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int inputSize = 1; + + try { + while (sp < sl) { + int byte1 = 0 , byte2 = 0, byte3 = 0, byte4 = 0; + // Get the input byte + byte1 = sa[sp] & 0xFF; + inputSize = 1; + + if ((byte1 & (byte)0x80) == 0){ // US-ASCII range + currentState = GB18030_SINGLE_BYTE; + } + else if (byte1 < 0x81 || byte1 > 0xfe) { + return CoderResult.malformedForLength(1); + } + else { // Either 2 or 4 byte sequence follows + if ( sl - sp < 2 ) + return CoderResult.UNDERFLOW; + byte2 = sa[sp + 1] & 0xFF; + inputSize = 2; + + if (byte2 < 0x30) + return CoderResult.malformedForLength(1); + else if (byte2 >= 0x30 && byte2 <= 0x39) { + currentState = GB18030_FOUR_BYTE; + + if (sl - sp < 4) + return CoderResult.UNDERFLOW; + + byte3 = sa[sp + 2] & 0xFF; + if (byte3 < 0x81 || byte3 > 0xfe) + return CoderResult.malformedForLength(3); + + byte4 = sa[sp + 3] & 0xFF; + inputSize = 4; + + if (byte4 < 0x30 || byte4 > 0x39) + return CoderResult.malformedForLength(4); + } + else if (byte2 == 0x7f || byte2 == 0xff || + (byte2 < 0x40 )) { + return CoderResult.malformedForLength(2); + } + else + currentState = GB18030_DOUBLE_BYTE; + } + + if (dl - dp < 1) + return CoderResult.OVERFLOW; + switch (currentState){ + case GB18030_SINGLE_BYTE: + da[dp++] = (char)byte1; + break; + case GB18030_DOUBLE_BYTE: + da[dp++] = decodeDouble(byte1, byte2); + break; + case GB18030_FOUR_BYTE: + int offset = (((byte1 - 0x81) * 10 + + (byte2 - 0x30)) * 126 + + byte3 - 0x81) * 10 + byte4 - 0x30; + int hiByte = (offset >>8) & 0xFF; + int lowByte = (offset & 0xFF); + + // Mixture of table lookups and algorithmic calculation + // of character values. + + // BMP Ranges + if (offset <= 0x4A62) + da[dp++] = getChar(offset); + else if (offset > 0x4A62 && offset <= 0x82BC) + da[dp++] = (char)(offset + 0x5543); + else if (offset >= 0x82BD && offset <= 0x830D) + da[dp++] = getChar(offset); + else if (offset >= 0x830D && offset <= 0x93A8) + da[dp++] = (char)(offset + 0x6557); + else if (offset >= 0x93A9 && offset <= 0x99FB) + da[dp++] = getChar(offset); + // Supplemental UCS planes handled via surrogates + else if (offset >= 0x2E248 && offset < 0x12E248) { + if (offset >= 0x12E248) + return CoderResult.malformedForLength(4); + offset -= 0x1e248; + if ( dl - dp < 2) + return CoderResult.OVERFLOW; + // emit high + low surrogate + da[dp++] = (char)((offset - 0x10000) / 0x400 + 0xD800); + da[dp++] = (char)((offset - 0x10000) % 0x400 + 0xDC00); + } + else + return CoderResult.malformedForLength(inputSize); + break; + } + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + + try { + while (src.hasRemaining()) { + int byte1 = 0, byte2 = 0, byte3 = 0, byte4 = 0; + byte1 = src.get() & 0xFF; + int inputSize = 1; + + if ((byte1 & (byte)0x80) == 0){ // US-ASCII range + currentState = GB18030_SINGLE_BYTE; + } + else if (byte1 < 0x81 || byte1 > 0xfe) { + return CoderResult.malformedForLength(1); + } + else { // Either 2 or 4 byte sequence follows + if ( src.remaining() < 1 ) + return CoderResult.UNDERFLOW; + byte2 = src.get() & 0xFF; + inputSize = 2; + + if (byte2 < 0x30) + return CoderResult.malformedForLength(1); + else if (byte2 >= 0x30 && byte2 <= 0x39) { + currentState = GB18030_FOUR_BYTE; + + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + + byte3 = src.get() & 0xFF; + if (byte3 < 0x81 || byte3 > 0xfe) + return CoderResult.malformedForLength(3); + + byte4 = src.get() & 0xFF; + inputSize = 4; + + if (byte4 < 0x30 || byte4 > 0x39) + return CoderResult.malformedForLength(4); + } + else if (byte2 == 0x7f || byte2 == 0xff || + (byte2 < 0x40 )) { + return CoderResult.malformedForLength(2); + } + else + currentState = GB18030_DOUBLE_BYTE; + } + + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + switch (currentState){ + case GB18030_SINGLE_BYTE: + dst.put((char)byte1); + break; + case GB18030_DOUBLE_BYTE: + dst.put(decodeDouble(byte1, byte2)); + break; + case GB18030_FOUR_BYTE: + int offset = (((byte1 - 0x81) * 10 + + (byte2 - 0x30)) * 126 + + byte3 - 0x81) * 10 + byte4 - 0x30; + int hiByte = (offset >>8) & 0xFF; + int lowByte = (offset & 0xFF); + + // Mixture of table lookups and algorithmic calculation + // of character values. + + // BMP Ranges + if (offset <= 0x4A62) + dst.put(getChar(offset)); + else if (offset > 0x4A62 && offset <= 0x82BC) + dst.put((char)(offset + 0x5543)); + else if (offset >= 0x82BD && offset <= 0x830D) + dst.put(getChar(offset)); + else if (offset >= 0x830D && offset <= 0x93A8) + dst.put((char)(offset + 0x6557)); + else if (offset >= 0x93A9 && offset <= 0x99F9) + dst.put(getChar(offset)); + // Supplemental UCS planes handled via surrogates + else if (offset >= 0x2E248 && offset < 0x12E248) { + if (offset >= 0x12E248) + return CoderResult.malformedForLength(4); + offset -= 0x1e248; + if ( dst.remaining() < 2) + return CoderResult.OVERFLOW; + // emit high + low surrogate + dst.put((char)((offset - 0x10000) / 0x400 + 0xD800)); + dst.put((char)((offset - 0x10000) % 0x400 + 0xDC00)); + } else { + return CoderResult.malformedForLength(inputSize); + } + } + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + } + + private static class Encoder extends CharsetEncoder { + + private int currentState = GB18030_DOUBLE_BYTE; + + private Encoder(Charset cs) { + super(cs, 4.0f, 4.0f); // max of 4 bytes per char + } + + public boolean canEncode(char c) { + return ! Character.isSurrogate(c); + } + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + private int getGB18030(short[] outerIndex, String[] innerEncoderIndex, + char ch) { + int offset = outerIndex[((ch & 0xff00) >> 8 )] << 8; + return innerEncoderIndex[offset >> 12].charAt((offset & 0xfff) + + (ch & 0xff)); + } + + protected void implReset() { + currentState = GB18030_DOUBLE_BYTE; + } + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int condensedKey = 0; // expands to a four byte sequence + int hiByte = 0, loByte = 0; + currentState = GB18030_DOUBLE_BYTE; + + try { + while (sp < sl) { + int inputSize = 1; + char c = sa[sp]; + + if (Character.isSurrogate(c)) { + if ((condensedKey=sgp.parse(c, sa, sp, sl)) < 0) + return sgp.error(); + // Character.toCodePoint looks like + // (((high & 0x3ff) << 10) | (low & 0x3ff)) + 0x10000; + // so we add (0x2e248 - 0x10000) to get the "key". + condensedKey += 0x1E248; + currentState = GB18030_FOUR_BYTE; + inputSize = sgp.increment(); + } + else if (c >= 0x0000 && c <= 0x007F) { + currentState = GB18030_SINGLE_BYTE; + } + else if (c <= 0xA4C6 || c >= 0xE000) { + int outByteVal = getGB18030(encoderIndex1, + encoderIndex2, + c); + if (outByteVal == 0xFFFD ) + return CoderResult.unmappableForLength(1); + + hiByte = (outByteVal & 0xFF00) >> 8; + loByte = (outByteVal & 0xFF); + + condensedKey = (hiByte - 0x20) * 256 + loByte; + + if (c >= 0xE000 && c < 0xF900) + condensedKey += 0x82BD; + else if (c >= 0xF900) + condensedKey += 0x93A9; + + if (hiByte > 0x80) + currentState = GB18030_DOUBLE_BYTE; + else + currentState = GB18030_FOUR_BYTE; + } + else if (c >= 0xA4C7 && c <= 0xD7FF) { + condensedKey = c - 0x5543; + currentState = GB18030_FOUR_BYTE; + } + + switch(currentState) { + case GB18030_SINGLE_BYTE: + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)c; + break; + + case GB18030_DOUBLE_BYTE: + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)hiByte; + da[dp++] = (byte)loByte; + break; + + case GB18030_FOUR_BYTE: // Four Byte encoding + byte b1, b2, b3, b4; + + if (dl - dp < 4) + return CoderResult.OVERFLOW; + // Decompose the condensed key into its 4 byte equivalent + b4 = (byte)((condensedKey % 10) + 0x30); + condensedKey /= 10; + b3 = (byte)((condensedKey % 126) + 0x81); + condensedKey /= 126; + b2 = (byte)((condensedKey % 10) + 0x30); + b1 = (byte)((condensedKey / 10) + 0x81); + da[dp++] = b1; + da[dp++] = b2; + da[dp++] = b3; + da[dp++] = b4; + break; + default: + assert(false); + } + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int condensedKey = 0; + int hiByte = 0, loByte = 0; + currentState = GB18030_DOUBLE_BYTE; + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + int inputSize = 1; + if (Character.isSurrogate(c)) { + if ((condensedKey = sgp.parse(c, src))<0) + return sgp.error(); + condensedKey += 0x1e248; + currentState = GB18030_FOUR_BYTE; + inputSize = 2; + } + else if (c >= 0x0000 && c <= 0x007F) { + currentState = GB18030_SINGLE_BYTE; + } + else if (c <= 0xA4C6 || c >= 0xE000) { + int outByteVal = getGB18030(encoderIndex1, + encoderIndex2, + c); + if (outByteVal == 0xFFFD ) + return CoderResult.unmappableForLength(1); + + hiByte = (outByteVal & 0xFF00) >> 8; + loByte = (outByteVal & 0xFF); + + condensedKey = (hiByte - 0x20) * 256 + loByte; + + if (c >= 0xE000 && c < 0xF900) + condensedKey += 0x82BD; + else if (c >= 0xF900) + condensedKey += 0x93A9; + + if (hiByte > 0x80) + currentState = GB18030_DOUBLE_BYTE; + else + currentState = GB18030_FOUR_BYTE; + } + else if (c >= 0xA4C7 && c <= 0xD7FF) { + condensedKey = c - 0x5543; + currentState = GB18030_FOUR_BYTE; + } + + if (currentState == GB18030_SINGLE_BYTE) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)c); + } else if (currentState == GB18030_DOUBLE_BYTE) { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)hiByte); + dst.put((byte)loByte); + } + else { // Four Byte encoding + byte b1, b2, b3, b4; + + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + // Decompose the condensed key into its 4 byte equivalent + b4 = (byte)((condensedKey % 10) + 0x30); + condensedKey /= 10; + b3 = (byte)((condensedKey % 126) + 0x81); + condensedKey /= 126; + b2 = (byte)((condensedKey % 10) + 0x30); + b1 = (byte)((condensedKey / 10) + 0x81); + dst.put(b1); + dst.put(b2); + dst.put(b3); + dst.put(b4); + } + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/HKSCS.java b/src/sun/nio/cs/ext/HKSCS.java new file mode 100644 index 00000000..87ebfbec --- /dev/null +++ b/src/sun/nio/cs/ext/HKSCS.java @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CoderResult; +import java.util.Arrays; + +import sun.nio.cs.Surrogate; + +public class HKSCS { + + public static class Decoder extends DoubleByte.Decoder { + static int b2Min = 0x40; + static int b2Max = 0xfe; + + private char[][] b2cBmp; + private char[][] b2cSupp; + private DoubleByte.Decoder big5Dec; + + protected Decoder(Charset cs, + DoubleByte.Decoder big5Dec, + char[][] b2cBmp, char[][] b2cSupp) + { + // super(cs, 0.5f, 1.0f); + // need to extends DoubleByte.Decoder so the + // sun.io can use it. this implementation + super(cs, 0.5f, 1.0f, null, null, 0, 0); + this.big5Dec = big5Dec; + this.b2cBmp = b2cBmp; + this.b2cSupp = b2cSupp; + } + + public char decodeSingle(int b) { + return big5Dec.decodeSingle(b); + } + + public char decodeBig5(int b1, int b2) { + return big5Dec.decodeDouble(b1, b2); + } + + public char decodeDouble(int b1, int b2) { + return b2cBmp[b1][b2 - b2Min]; + } + + public char decodeDoubleEx(int b1, int b2) { + /* if the b2cSupp is null, the subclass need + to override the methold + if (b2cSupp == null) + return UNMAPPABLE_DECODING; + */ + return b2cSupp[b1][b2 - b2Min]; + } + + protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + int b1 = sa[sp] & 0xff; + char c = decodeSingle(b1); + int inSize = 1, outSize = 1; + char[] cc = null; + if (c == UNMAPPABLE_DECODING) { + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + int b2 = sa[sp + 1] & 0xff; + inSize++; + if (b2 < b2Min || b2 > b2Max) + return CoderResult.unmappableForLength(2); + c = decodeDouble(b1, b2); //bmp + if (c == UNMAPPABLE_DECODING) { + c = decodeDoubleEx(b1, b2); //supp + if (c == UNMAPPABLE_DECODING) { + c = decodeBig5(b1, b2); //big5 + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(2); + } else { + // supplementary character in u+2xxxx area + outSize = 2; + } + } + } + if (dl - dp < outSize) + return CoderResult.OVERFLOW; + if (outSize == 2) { + // supplementary characters + da[dp++] = Surrogate.high(0x20000 + c); + da[dp++] = Surrogate.low(0x20000 + c); + } else { + da[dp++] = c; + } + sp += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char[] cc = null; + int b1 = src.get() & 0xff; + int inSize = 1, outSize = 1; + char c = decodeSingle(b1); + if (c == UNMAPPABLE_DECODING) { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + int b2 = src.get() & 0xff; + inSize++; + if (b2 < b2Min || b2 > b2Max) + return CoderResult.unmappableForLength(2); + c = decodeDouble(b1, b2); //bmp + if (c == UNMAPPABLE_DECODING) { + c = decodeDoubleEx(b1, b2); //supp + if (c == UNMAPPABLE_DECODING) { + c = decodeBig5(b1, b2); //big5 + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(2); + } else { + outSize = 2; + } + } + } + if (dst.remaining() < outSize) + return CoderResult.OVERFLOW; + if (outSize == 2) { + dst.put(Surrogate.high(0x20000 + c)); + dst.put(Surrogate.low(0x20000 + c)); + } else { + dst.put(c); + } + mark += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + public int decode(byte[] src, int sp, int len, char[] dst) { + int dp = 0; + int sl = sp + len; + char repl = replacement().charAt(0); + while (sp < sl) { + int b1 = src[sp++] & 0xff; + char c = decodeSingle(b1); + if (c == UNMAPPABLE_DECODING) { + if (sl == sp) { + c = repl; + } else { + int b2 = src[sp++] & 0xff; + if (b2 < b2Min || b2 > b2Max) { + c = repl; + } else if ((c = decodeDouble(b1, b2)) == UNMAPPABLE_DECODING) { + c = decodeDoubleEx(b1, b2); //supp + if (c == UNMAPPABLE_DECODING) { + c = decodeBig5(b1, b2); //big5 + if (c == UNMAPPABLE_DECODING) + c = repl; + } else { + // supplementary character in u+2xxxx area + dst[dp++] = Surrogate.high(0x20000 + c); + dst[dp++] = Surrogate.low(0x20000 + c); + continue; + } + } + } + } + dst[dp++] = c; + } + return dp; + } + + public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + static void initb2c(char[][]b2c, String[] b2cStr) + { + for (int i = 0; i < b2cStr.length; i++) { + if (b2cStr[i] == null) + b2c[i] = DoubleByte.B2C_UNMAPPABLE; + else + b2c[i] = b2cStr[i].toCharArray(); + } + } + + } + + public static class Encoder extends DoubleByte.Encoder { + private DoubleByte.Encoder big5Enc; + private char[][] c2bBmp; + private char[][] c2bSupp; + + protected Encoder(Charset cs, + DoubleByte.Encoder big5Enc, + char[][] c2bBmp, + char[][] c2bSupp) + { + super(cs, null, null); + this.big5Enc = big5Enc; + this.c2bBmp = c2bBmp; + this.c2bSupp = c2bSupp; + } + + public int encodeBig5(char ch) { + return big5Enc.encodeChar(ch); + } + + public int encodeChar(char ch) { + int bb = c2bBmp[ch >> 8][ch & 0xff]; + if (bb == UNMAPPABLE_ENCODING) + return encodeBig5(ch); + return bb; + } + + public int encodeSupp(int cp) { + if ((cp & 0xf0000) != 0x20000) + return UNMAPPABLE_ENCODING; + return c2bSupp[(cp >> 8) & 0xff][cp & 0xff]; + } + + public boolean canEncode(char c) { + return encodeChar(c) != UNMAPPABLE_ENCODING; + } + + protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + char c = sa[sp]; + int inSize = 1; + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + int cp; + if ((cp = sgp().parse(c, sa, sp, sl)) < 0) + return sgp.error(); + bb = encodeSupp(cp); + if (bb == UNMAPPABLE_ENCODING) + return CoderResult.unmappableForLength(2); + inSize = 2; + } else { + return CoderResult.unmappableForLength(1); + } + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(bb >> 8); + da[dp++] = (byte)bb; + } else { // SingleByte + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)bb; + } + sp += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int inSize = 1; + char c = src.get(); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isSurrogate(c)) { + int cp; + if ((cp = sgp().parse(c, src)) < 0) + return sgp.error(); + bb = encodeSupp(cp); + if (bb == UNMAPPABLE_ENCODING) + return CoderResult.unmappableForLength(2); + inSize = 2; + } else { + return CoderResult.unmappableForLength(1); + } + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(bb >> 8)); + dst.put((byte)(bb)); + } else { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)bb); + } + mark += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + private byte[] repl = replacement(); + protected void implReplaceWith(byte[] newReplacement) { + repl = newReplacement; + } + + public int encode(char[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = src[sp++]; + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (!Character.isHighSurrogate(c) || sp == sl || + !Character.isLowSurrogate(src[sp]) || + (bb = encodeSupp(Character.toCodePoint(c, src[sp++]))) + == UNMAPPABLE_ENCODING) { + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } + sp++; + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + } + return dp; + } + + + static char[] C2B_UNMAPPABLE = new char[0x100]; + static { + Arrays.fill(C2B_UNMAPPABLE, (char)UNMAPPABLE_ENCODING); + } + + static void initc2b(char[][] c2b, String[] b2cStr, String pua) { + // init c2b/c2bSupp from b2cStr and supp + int b2Min = 0x40; + Arrays.fill(c2b, C2B_UNMAPPABLE); + for (int b1 = 0; b1 < 0x100; b1++) { + String s = b2cStr[b1]; + if (s == null) + continue; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + int hi = c >> 8; + if (c2b[hi] == C2B_UNMAPPABLE) { + c2b[hi] = new char[0x100]; + Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING); + } + c2b[hi][c & 0xff] = (char)((b1 << 8) | (i + b2Min)); + } + } + if (pua != null) { // add the compatibility pua entries + char c = '\ue000'; //first pua character + for (int i = 0; i < pua.length(); i++) { + char bb = pua.charAt(i); + if (bb != UNMAPPABLE_DECODING) { + int hi = c >> 8; + if (c2b[hi] == C2B_UNMAPPABLE) { + c2b[hi] = new char[0x100]; + Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING); + } + c2b[hi][c & 0xff] = bb; + } + c++; + } + } + } + } +} diff --git a/src/sun/nio/cs/ext/IBM33722.java b/src/sun/nio/cs/ext/IBM33722.java new file mode 100644 index 00000000..f6bc1687 --- /dev/null +++ b/src/sun/nio/cs/ext/IBM33722.java @@ -0,0 +1,7138 @@ + +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class IBM33722 + extends Charset + implements HistoricallyNamedCharset +{ + + public IBM33722() { + super("x-IBM33722", ExtendedCharsets.aliasesFor("x-IBM33722")); + } + + public String historicalName() { + return "Cp33722"; + } + + public boolean contains(Charset cs) { + return (cs instanceof IBM33722); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + protected static class Decoder extends CharsetDecoder { + + private final int G0 = 0; + private final int G1 = 1; + private final int G2 = 2; + private final int G3 = 3; + private final int G4 = 4; + private final int SS2 = 0x8E; + private final int SS3 = 0x8F; + + private int firstByte, state; + + public Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + int byte1, byte2; + int inputSize = 1; + char outputChar = '\uFFFD'; + byte1 = sa[sp] & 0xff; + + if (byte1 == SS2) { + if (sl - sp < 2) { + return CoderResult.UNDERFLOW; + } + byte1 = sa[sp + 1] & 0xff; + inputSize = 2; + if ( byte1 < 0xa1 || byte1 > 0xfe) { //valid first byte for G2 + return CoderResult.malformedForLength(2); + } + outputChar = mappingTableG2.charAt(byte1 - 0xa1); + } else if(byte1 == SS3 ) { //G3 + if (sl - sp < 3) { + return CoderResult.UNDERFLOW; + } + byte1 = sa[sp + 1] & 0xff; + byte2 = sa[sp + 2] & 0xff; + inputSize = 3; + if ( byte1 < 0xa1 || byte1 > 0xfe) { + return CoderResult.malformedForLength(2); + } + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(3); + } + outputChar = mappingTableG3.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } else if ( byte1 <= 0x9f ) { // valid single byte + outputChar = byteToCharTable.charAt(byte1); + } else if (byte1 < 0xa1 || byte1 > 0xfe) { // invalid range? + return CoderResult.malformedForLength(1); + } else { // G1 + if (sl - sp < 2) { + return CoderResult.UNDERFLOW; + } + byte2 = sa[sp + 1] & 0xff; + inputSize = 2; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(2); + } + outputChar = mappingTableG1.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } + if (outputChar == '\uFFFD') + return CoderResult.unmappableForLength(inputSize); + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = outputChar; + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int byte1, byte2; + int inputSize = 1; + char outputChar = '\uFFFD'; + byte1 = src.get() & 0xff; + + if (byte1 == SS2) { + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + byte1 = src.get() & 0xff; + inputSize = 2; + if ( byte1 < 0xa1 || byte1 > 0xfe) { //valid first byte for G2 + return CoderResult.malformedForLength(2); + } + outputChar = mappingTableG2.charAt(byte1 - 0xa1); + } else if (byte1 == SS3 ) { //G3 + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + + byte1 = src.get() & 0xff; + if ( byte1 < 0xa1 || byte1 > 0xfe) { + return CoderResult.malformedForLength(2); + } + byte2 = src.get() & 0xff; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(3); + } + inputSize = 3; + outputChar = mappingTableG3.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } else if ( byte1 <= 0x9f ) { // valid single byte + outputChar = byteToCharTable.charAt(byte1); + } else if (byte1 < 0xa1 || byte1 > 0xfe) { // invalid range? + return CoderResult.malformedForLength(1); + } else { // G1 + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + byte2 = src.get() & 0xff; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(2); + } + inputSize = 2; + outputChar = mappingTableG1.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } + + if (outputChar == '\uFFFD') + return CoderResult.unmappableForLength(inputSize); + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put(outputChar); + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (true && src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + private final static String byteToCharTable; + private final static String mappingTableG1; + private final static String mappingTableG2; + private final static String mappingTableG3; + static { + byteToCharTable = + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\u0008\u0009\n\u000B\u000C\r\u000E\u000F" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F" + + "\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027" + + "\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037" + + "\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F" + + "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047" + + "\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F" + + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057" + + "\u0058\u0059\u005A\u005B\u00A5\u005D\u005E\u005F" + + "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067" + + "\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F" + + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077" + + "\u0078\u0079\u007A\u007B\u007C\u007D\u203E\u007F" + + "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087" + + "\u0088\u0089\u008A\u008B\u008C\u008D\uFFFD\uFFFD" + + "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097" + + "\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F" + ; + mappingTableG1 = + "\u3000\u3001\u3002\uFF0C\uFF0E\u30FB\uFF1A\uFF1B" + + "\uFF1F\uFF01\u309B\u309C\u00B4\uFF40\u00A8\uFF3E" + + "\uFFE3\uFF3F\u30FD\u30FE\u309D\u309E\u3003\u4EDD" + + "\u3005\u3006\u3007\u30FC\u2014\u2010\uFF0F\uFF3C" + + "\u301C\u2016\uFF5C\u2026\u2025\u2018\u2019\u201C" + + "\u201D\uFF08\uFF09\u3014\u3015\uFF3B\uFF3D\uFF5B" + + "\uFF5D\u3008\u3009\u300A\u300B\u300C\u300D\u300E" + + "\u300F\u3010\u3011\uFF0B\u2212\u00B1\u00D7\u00F7" + + "\uFF1D\u2260\uFF1C\uFF1E\u2266\u2267\u221E\u2234" + + "\u2642\u2640\u00B0\u2032\u2033\u2103\uFFE5\uFF04" + + "\uFFE0\uFFE1\uFF05\uFF03\uFF06\uFF0A\uFF20\u00A7" + + "\u2606\u2605\u25CB\u25CF\u25CE\u25C7\u25C6\u25A1" + + "\u25A0\u25B3\u25B2\u25BD\u25BC\u203B\u3012\u2192" + + "\u2190\u2191\u2193\u3013\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2208" + + "\u220B\u2286\u2287\u2282\u2283\u222A\u2229\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2227" + + "\u2228\uFFE2\u21D2\u21D4\u2200\u2203\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u2220\u22A5\u2312\u2202\u2207\u2261\u2252" + + "\u226A\u226B\u221A\u223D\u221D\u2235\u222B\u222C" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u212B" + + "\u2030\u266F\u266D\u266A\u2020\u2021\u00B6\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u25EF\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFF10\uFF11\uFF12\uFF13\uFF14" + + "\uFF15\uFF16\uFF17\uFF18\uFF19\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFF21\uFF22\uFF23\uFF24" + + "\uFF25\uFF26\uFF27\uFF28\uFF29\uFF2A\uFF2B\uFF2C" + + "\uFF2D\uFF2E\uFF2F\uFF30\uFF31\uFF32\uFF33\uFF34" + + "\uFF35\uFF36\uFF37\uFF38\uFF39\uFF3A\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFF41\uFF42\uFF43\uFF44" + + "\uFF45\uFF46\uFF47\uFF48\uFF49\uFF4A\uFF4B\uFF4C" + + "\uFF4D\uFF4E\uFF4F\uFF50\uFF51\uFF52\uFF53\uFF54" + + "\uFF55\uFF56\uFF57\uFF58\uFF59\uFF5A\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u3041\u3042\u3043\u3044\u3045\u3046" + + "\u3047\u3048\u3049\u304A\u304B\u304C\u304D\u304E" + + "\u304F\u3050\u3051\u3052\u3053\u3054\u3055\u3056" + + "\u3057\u3058\u3059\u305A\u305B\u305C\u305D\u305E" + + "\u305F\u3060\u3061\u3062\u3063\u3064\u3065\u3066" + + "\u3067\u3068\u3069\u306A\u306B\u306C\u306D\u306E" + + "\u306F\u3070\u3071\u3072\u3073\u3074\u3075\u3076" + + "\u3077\u3078\u3079\u307A\u307B\u307C\u307D\u307E" + + "\u307F\u3080\u3081\u3082\u3083\u3084\u3085\u3086" + + "\u3087\u3088\u3089\u308A\u308B\u308C\u308D\u308E" + + "\u308F\u3090\u3091\u3092\u3093\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u30A1\u30A2\u30A3\u30A4\u30A5\u30A6\u30A7\u30A8" + + "\u30A9\u30AA\u30AB\u30AC\u30AD\u30AE\u30AF\u30B0" + + "\u30B1\u30B2\u30B3\u30B4\u30B5\u30B6\u30B7\u30B8" + + "\u30B9\u30BA\u30BB\u30BC\u30BD\u30BE\u30BF\u30C0" + + "\u30C1\u30C2\u30C3\u30C4\u30C5\u30C6\u30C7\u30C8" + + "\u30C9\u30CA\u30CB\u30CC\u30CD\u30CE\u30CF\u30D0" + + "\u30D1\u30D2\u30D3\u30D4\u30D5\u30D6\u30D7\u30D8" + + "\u30D9\u30DA\u30DB\u30DC\u30DD\u30DE\u30DF\u30E0" + + "\u30E1\u30E2\u30E3\u30E4\u30E5\u30E6\u30E7\u30E8" + + "\u30E9\u30EA\u30EB\u30EC\u30ED\u30EE\u30EF\u30F0" + + "\u30F1\u30F2\u30F3\u30F4\u30F5\u30F6\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u0391\u0392" + + "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039A" + + "\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\u03A3" + + "\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u03B1\u03B2" + + "\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA" + + "\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C3" + + "\u03C4\u03C5\u03C6\u03C7\u03C8\u03C9\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u0410\u0411\u0412\u0413" + + "\u0414\u0415\u0401\u0416\u0417\u0418\u0419\u041A" + + "\u041B\u041C\u041D\u041E\u041F\u0420\u0421\u0422" + + "\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042A" + + "\u042B\u042C\u042D\u042E\u042F\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u0430\u0431\u0432\u0433" + + "\u0434\u0435\u0451\u0436\u0437\u0438\u0439\u043A" + + "\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442" + + "\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A" + + "\u044B\u044C\u044D\u044E\u044F\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u2500\u2502\u250C\u2510\u2518\u2514" + + "\u251C\u252C\u2524\u2534\u253C\u2501\u2503\u250F" + + "\u2513\u251B\u2517\u2523\u2533\u252B\u253B\u254B" + + "\u2520\u252F\u2528\u2537\u253F\u251D\u2530\u2525" + + "\u2538\u2542\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u4E9C\u5516\u5A03\u963F\u54C0\u611B" + + "\u6328\u59F6\u9022\u8475\u831C\u7A50\u60AA\u63E1" + + "\u6E25\u65ED\u8466\u82A6\u9BF5\u6893\u5727\u65A1" + + "\u6271\u5B9B\u59D0\u867B\u98F4\u7D62\u7DBE\u9B8E" + + "\u6216\u7C9F\u88B7\u5B89\u5EB5\u6309\u6697\u6848" + + "\u95C7\u978D\u674F\u4EE5\u4F0A\u4F4D\u4F9D\u5049" + + "\u56F2\u5937\u59D4\u5A01\u5C09\u60DF\u610F\u6170" + + "\u6613\u6905\u70BA\u754F\u7570\u79FB\u7DAD\u7DEF" + + "\u80C3\u840E\u8863\u8B02\u9055\u907A\u533B\u4E95" + + "\u4EA5\u57DF\u80B2\u90C1\u78EF\u4E00\u58F1\u6EA2" + + "\u9038\u7A32\u8328\u828B\u9C2F\u5141\u5370\u54BD" + + "\u54E1\u56E0\u59FB\u5F15\u98F2\u6DEB\u80E4\u852D" + + "\u9662\u9670\u96A0\u97FB\u540B\u53F3\u5B87\u70CF" + + "\u7FBD\u8FC2\u96E8\u536F\u9D5C\u7ABA\u4E11\u7893" + + "\u81FC\u6E26\u5618\u5504\u6B1D\u851A\u9C3B\u59E5" + + "\u53A9\u6D66\u74DC\u958F\u5642\u4E91\u904B\u96F2" + + "\u834F\u990C\u53E1\u55B6\u5B30\u5F71\u6620\u66F3" + + "\u6804\u6C38\u6CF3\u6D29\u745B\u76C8\u7A4E\u9834" + + "\u82F1\u885B\u8A60\u92ED\u6DB2\u75AB\u76CA\u99C5" + + "\u60A6\u8B01\u8D8A\u95B2\u698E\u53AD\u5186\u5712" + + "\u5830\u5944\u5BB4\u5EF6\u6028\u63A9\u63F4\u6CBF" + + "\u6F14\u708E\u7114\u7159\u71D5\u733F\u7E01\u8276" + + "\u82D1\u8597\u9060\u925B\u9D1B\u5869\u65BC\u6C5A" + + "\u7525\u51F9\u592E\u5965\u5F80\u5FDC\u62BC\u65FA" + + "\u6A2A\u6B27\u6BB4\u738B\u7FC1\u8956\u9D2C\u9D0E" + + "\u9EC4\u5CA1\u6C96\u837B\u5104\u5C4B\u61B6\u81C6" + + "\u6876\u7261\u4E59\u4FFA\u5378\u6069\u6E29\u7A4F" + + "\u97F3\u4E0B\u5316\u4EEE\u4F55\u4F3D\u4FA1\u4F73" + + "\u52A0\u53EF\u5609\u590F\u5AC1\u5BB6\u5BE1\u79D1" + + "\u6687\u679C\u67B6\u6B4C\u6CB3\u706B\u73C2\u798D" + + "\u79BE\u7A3C\u7B87\u82B1\u82DB\u8304\u8377\u83EF" + + "\u83D3\u8766\u8AB2\u5629\u8CA8\u8FE6\u904E\u971E" + + "\u868A\u4FC4\u5CE8\u6211\u7259\u753B\u81E5\u82BD" + + "\u86FE\u8CC0\u96C5\u9913\u99D5\u4ECB\u4F1A\u89E3" + + "\u56DE\u584A\u58CA\u5EFB\u5FEB\u602A\u6094\u6062" + + "\u61D0\u6212\u62D0\u6539\u9B41\u6666\u68B0\u6D77" + + "\u7070\u754C\u7686\u7D75\u82A5\u87F9\u958B\u968E" + + "\u8C9D\u51F1\u52BE\u5916\u54B3\u5BB3\u5D16\u6168" + + "\u6982\u6DAF\u788D\u84CB\u8857\u8A72\u93A7\u9AB8" + + "\u6D6C\u99A8\u86D9\u57A3\u67FF\u86CE\u920E\u5283" + + "\u5687\u5404\u5ED3\u62E1\u64B9\u683C\u6838\u6BBB" + + "\u7372\u78BA\u7A6B\u899A\u89D2\u8D6B\u8F03\u90ED" + + "\u95A3\u9694\u9769\u5B66\u5CB3\u697D\u984D\u984E" + + "\u639B\u7B20\u6A2B\u6A7F\u68B6\u9C0D\u6F5F\u5272" + + "\u559D\u6070\u62EC\u6D3B\u6E07\u6ED1\u845B\u8910" + + "\u8F44\u4E14\u9C39\u53F6\u691B\u6A3A\u9784\u682A" + + "\u515C\u7AC3\u84B2\u91DC\u938C\u565B\u9D28\u6822" + + "\u8305\u8431\u7CA5\u5208\u82C5\u74E6\u4E7E\u4F83" + + "\u51A0\u5BD2\u520A\u52D8\u52E7\u5DFB\u559A\u582A" + + "\u59E6\u5B8C\u5B98\u5BDB\u5E72\u5E79\u60A3\u611F" + + "\u6163\u61BE\u63DB\u6562\u67D1\u6853\u68FA\u6B3E" + + "\u6B53\u6C57\u6F22\u6F97\u6F45\u74B0\u7518\u76E3" + + "\u770B\u7AFF\u7BA1\u7C21\u7DE9\u7F36\u7FF0\u809D" + + "\u8266\u839E\u89B3\u8ACC\u8CAB\u9084\u9451\u9593" + + "\u9591\u95A2\u9665\u97D3\u9928\u8218\u4E38\u542B" + + "\u5CB8\u5DCC\u73A9\u764C\u773C\u5CA9\u7FEB\u8D0B" + + "\u96C1\u9811\u9854\u9858\u4F01\u4F0E\u5371\u559C" + + "\u5668\u57FA\u5947\u5B09\u5BC4\u5C90\u5E0C\u5E7E" + + "\u5FCC\u63EE\u673A\u65D7\u65E2\u671F\u68CB\u68C4" + + "\u6A5F\u5E30\u6BC5\u6C17\u6C7D\u757F\u7948\u5B63" + + "\u7A00\u7D00\u5FBD\u898F\u8A18\u8CB4\u8D77\u8ECC" + + "\u8F1D\u98E2\u9A0E\u9B3C\u4E80\u507D\u5100\u5993" + + "\u5B9C\u622F\u6280\u64EC\u6B3A\u72A0\u7591\u7947" + + "\u7FA9\u87FB\u8ABC\u8B70\u63AC\u83CA\u97A0\u5409" + + "\u5403\u55AB\u6854\u6A58\u8A70\u7827\u6775\u9ECD" + + "\u5374\u5BA2\u811A\u8650\u9006\u4E18\u4E45\u4EC7" + + "\u4F11\u53CA\u5438\u5BAE\u5F13\u6025\u6551\u673D" + + "\u6C42\u6C72\u6CE3\u7078\u7403\u7A76\u7AAE\u7B08" + + "\u7D1A\u7CFE\u7D66\u65E7\u725B\u53BB\u5C45\u5DE8" + + "\u62D2\u62E0\u6319\u6E20\u865A\u8A31\u8DDD\u92F8" + + "\u6F01\u79A6\u9B5A\u4EA8\u4EAB\u4EAC\u4F9B\u4FA0" + + "\u50D1\u5147\u7AF6\u5171\u51F6\u5354\u5321\u537F" + + "\u53EB\u55AC\u5883\u5CE1\u5F37\u5F4A\u602F\u6050" + + "\u606D\u631F\u6559\u6A4B\u6CC1\u72C2\u72ED\u77EF" + + "\u80F8\u8105\u8208\u854E\u90F7\u93E1\u97FF\u9957" + + "\u9A5A\u4EF0\u51DD\u5C2D\u6681\u696D\u5C40\u66F2" + + "\u6975\u7389\u6850\u7C81\u50C5\u52E4\u5747\u5DFE" + + "\u9326\u65A4\u6B23\u6B3D\u7434\u7981\u79BD\u7B4B" + + "\u7DCA\u82B9\u83CC\u887F\u895F\u8B39\u8FD1\u91D1" + + "\u541F\u9280\u4E5D\u5036\u53E5\u533A\u72D7\u7396" + + "\u77E9\u82E6\u8EAF\u99C6\u99C8\u99D2\u5177\u611A" + + "\u865E\u55B0\u7A7A\u5076\u5BD3\u9047\u9685\u4E32" + + "\u6ADB\u91E7\u5C51\u5C48\u6398\u7A9F\u6C93\u9774" + + "\u8F61\u7AAA\u718A\u9688\u7C82\u6817\u7E70\u6851" + + "\u936C\u52F2\u541B\u85AB\u8A13\u7FA4\u8ECD\u90E1" + + "\u5366\u8888\u7941\u4FC2\u50BE\u5211\u5144\u5553" + + "\u572D\u73EA\u578B\u5951\u5F62\u5F84\u6075\u6176" + + "\u6167\u61A9\u63B2\u643A\u656C\u666F\u6842\u6E13" + + "\u7566\u7A3D\u7CFB\u7D4C\u7D99\u7E4B\u7F6B\u830E" + + "\u834A\u86CD\u8A08\u8A63\u8B66\u8EFD\u981A\u9D8F" + + "\u82B8\u8FCE\u9BE8\u5287\u621F\u6483\u6FC0\u9699" + + "\u6841\u5091\u6B20\u6C7A\u6F54\u7A74\u7D50\u8840" + + "\u8A23\u6708\u4EF6\u5039\u5026\u5065\u517C\u5238" + + "\u5263\u55A7\u570F\u5805\u5ACC\u5EFA\u61B2\u61F8" + + "\u62F3\u6372\u691C\u6A29\u727D\u72AC\u732E\u7814" + + "\u786F\u7D79\u770C\u80A9\u898B\u8B19\u8CE2\u8ED2" + + "\u9063\u9375\u967A\u9855\u9A13\u9E78\u5143\u539F" + + "\u53B3\u5E7B\u5F26\u6E1B\u6E90\u7384\u73FE\u7D43" + + "\u8237\u8A00\u8AFA\u9650\u4E4E\u500B\u53E4\u547C" + + "\u56FA\u59D1\u5B64\u5DF1\u5EAB\u5F27\u6238\u6545" + + "\u67AF\u6E56\u72D0\u7CCA\u88B4\u80A1\u80E1\u83F0" + + "\u864E\u8A87\u8DE8\u9237\u96C7\u9867\u9F13\u4E94" + + "\u4E92\u4F0D\u5348\u5449\u543E\u5A2F\u5F8C\u5FA1" + + "\u609F\u68A7\u6A8E\u745A\u7881\u8A9E\u8AA4\u8B77" + + "\u9190\u4E5E\u9BC9\u4EA4\u4F7C\u4FAF\u5019\u5016" + + "\u5149\u516C\u529F\u52B9\u52FE\u539A\u53E3\u5411" + + "\u540E\u5589\u5751\u57A2\u597D\u5B54\u5B5D\u5B8F" + + "\u5DE5\u5DE7\u5DF7\u5E78\u5E83\u5E9A\u5EB7\u5F18" + + "\u6052\u614C\u6297\u62D8\u63A7\u653B\u6602\u6643" + + "\u66F4\u676D\u6821\u6897\u69CB\u6C5F\u6D2A\u6D69" + + "\u6E2F\u6E9D\u7532\u7687\u786C\u7A3F\u7CE0\u7D05" + + "\u7D18\u7D5E\u7DB1\u8015\u8003\u80AF\u80B1\u8154" + + "\u818F\u822A\u8352\u884C\u8861\u8B1B\u8CA2\u8CFC" + + "\u90CA\u9175\u9271\u783F\u92FC\u95A4\u964D\u9805" + + "\u9999\u9AD8\u9D3B\u525B\u52AB\u53F7\u5408\u58D5" + + "\u62F7\u6FE0\u8C6A\u8F5F\u9EB9\u514B\u523B\u544A" + + "\u56FD\u7A40\u9177\u9D60\u9ED2\u7344\u6F09\u8170" + + "\u7511\u5FFD\u60DA\u9AA8\u72DB\u8FBC\u6B64\u9803" + + "\u4ECA\u56F0\u5764\u58BE\u5A5A\u6068\u61C7\u660F" + + "\u6606\u6839\u68B1\u6DF7\u75D5\u7D3A\u826E\u9B42" + + "\u4E9B\u4F50\u53C9\u5506\u5D6F\u5DE6\u5DEE\u67FB" + + "\u6C99\u7473\u7802\u8A50\u9396\u88DF\u5750\u5EA7" + + "\u632B\u50B5\u50AC\u518D\u6700\u54C9\u585E\u59BB" + + "\u5BB0\u5F69\u624D\u63A1\u683D\u6B73\u6E08\u707D" + + "\u91C7\u7280\u7815\u7826\u796D\u658E\u7D30\u83DC" + + "\u88C1\u8F09\u969B\u5264\u5728\u6750\u7F6A\u8CA1" + + "\u51B4\u5742\u962A\u583A\u698A\u80B4\u54B2\u5D0E" + + "\u57FC\u7895\u9DFA\u4F5C\u524A\u548B\u643E\u6628" + + "\u6714\u67F5\u7A84\u7B56\u7D22\u932F\u685C\u9BAD" + + "\u7B39\u5319\u518A\u5237\u5BDF\u62F6\u64AE\u64E6" + + "\u672D\u6BBA\u85A9\u96D1\u7690\u9BD6\u634C\u9306" + + "\u9BAB\u76BF\u6652\u4E09\u5098\u53C2\u5C71\u60E8" + + "\u6492\u6563\u685F\u71E6\u73CA\u7523\u7B97\u7E82" + + "\u8695\u8B83\u8CDB\u9178\u9910\u65AC\u66AB\u6B8B" + + "\u4ED5\u4ED4\u4F3A\u4F7F\u523A\u53F8\u53F2\u55E3" + + "\u56DB\u58EB\u59CB\u59C9\u59FF\u5B50\u5C4D\u5E02" + + "\u5E2B\u5FD7\u601D\u6307\u652F\u5B5C\u65AF\u65BD" + + "\u65E8\u679D\u6B62\u6B7B\u6C0F\u7345\u7949\u79C1" + + "\u7CF8\u7D19\u7D2B\u80A2\u8102\u81F3\u8996\u8A5E" + + "\u8A69\u8A66\u8A8C\u8AEE\u8CC7\u8CDC\u96CC\u98FC" + + "\u6B6F\u4E8B\u4F3C\u4F8D\u5150\u5B57\u5BFA\u6148" + + "\u6301\u6642\u6B21\u6ECB\u6CBB\u723E\u74BD\u75D4" + + "\u78C1\u793A\u800C\u8033\u81EA\u8494\u8F9E\u6C50" + + "\u9E7F\u5F0F\u8B58\u9D2B\u7AFA\u8EF8\u5B8D\u96EB" + + "\u4E03\u53F1\u57F7\u5931\u5AC9\u5BA4\u6089\u6E7F" + + "\u6F06\u75BE\u8CEA\u5B9F\u8500\u7BE0\u5072\u67F4" + + "\u829D\u5C61\u854A\u7E1E\u820E\u5199\u5C04\u6368" + + "\u8D66\u659C\u716E\u793E\u7D17\u8005\u8B1D\u8ECA" + + "\u906E\u86C7\u90AA\u501F\u52FA\u5C3A\u6753\u707C" + + "\u7235\u914C\u91C8\u932B\u82E5\u5BC2\u5F31\u60F9" + + "\u4E3B\u53D6\u5B88\u624B\u6731\u6B8A\u72E9\u73E0" + + "\u7A2E\u816B\u8DA3\u9152\u9996\u5112\u53D7\u546A" + + "\u5BFF\u6388\u6A39\u7DAC\u9700\u56DA\u53CE\u5468" + + "\u5B97\u5C31\u5DDE\u4FEE\u6101\u62FE\u6D32\u79C0" + + "\u79CB\u7D42\u7E4D\u7FD2\u81ED\u821F\u8490\u8846" + + "\u8972\u8B90\u8E74\u8F2F\u9031\u914B\u916C\u96C6" + + "\u919C\u4EC0\u4F4F\u5145\u5341\u5F93\u620E\u67D4" + + "\u6C41\u6E0B\u7363\u7E26\u91CD\u9283\u53D4\u5919" + + "\u5BBF\u6DD1\u795D\u7E2E\u7C9B\u587E\u719F\u51FA" + + "\u8853\u8FF0\u4FCA\u5CFB\u6625\u77AC\u7AE3\u821C" + + "\u99FF\u51C6\u5FAA\u65EC\u696F\u6B89\u6DF3\u6E96" + + "\u6F64\u76FE\u7D14\u5DE1\u9075\u9187\u9806\u51E6" + + "\u521D\u6240\u6691\u66D9\u6E1A\u5EB6\u7DD2\u7F72" + + "\u66F8\u85AF\u85F7\u8AF8\u52A9\u53D9\u5973\u5E8F" + + "\u5F90\u6055\u92E4\u9664\u50B7\u511F\u52DD\u5320" + + "\u5347\u53EC\u54E8\u5546\u5531\u5617\u5968\u59BE" + + "\u5A3C\u5BB5\u5C06\u5C0F\u5C11\u5C1A\u5E84\u5E8A" + + "\u5EE0\u5F70\u627F\u6284\u62DB\u638C\u6377\u6607" + + "\u660C\u662D\u6676\u677E\u68A2\u6A1F\u6A35\u6CBC" + + "\u6D88\u6E09\u6E58\u713C\u7126\u7167\u75C7\u7701" + + "\u785D\u7901\u7965\u79F0\u7AE0\u7B11\u7CA7\u7D39" + + "\u8096\u83D6\u848B\u8549\u885D\u88F3\u8A1F\u8A3C" + + "\u8A54\u8A73\u8C61\u8CDE\u91A4\u9266\u937E\u9418" + + "\u969C\u9798\u4E0A\u4E08\u4E1E\u4E57\u5197\u5270" + + "\u57CE\u5834\u58CC\u5B22\u5E38\u60C5\u64FE\u6761" + + "\u6756\u6D44\u72B6\u7573\u7A63\u84B8\u8B72\u91B8" + + "\u9320\u5631\u57F4\u98FE\u62ED\u690D\u6B96\u71ED" + + "\u7E54\u8077\u8272\u89E6\u98DF\u8755\u8FB1\u5C3B" + + "\u4F38\u4FE1\u4FB5\u5507\u5A20\u5BDD\u5BE9\u5FC3" + + "\u614E\u632F\u65B0\u664B\u68EE\u699B\u6D78\u6DF1" + + "\u7533\u75B9\u771F\u795E\u79E6\u7D33\u81E3\u82AF" + + "\u85AA\u89AA\u8A3A\u8EAB\u8F9B\u9032\u91DD\u9707" + + "\u4EBA\u4EC1\u5203\u5875\u58EC\u5C0B\u751A\u5C3D" + + "\u814E\u8A0A\u8FC5\u9663\u976D\u7B25\u8ACF\u9808" + + "\u9162\u56F3\u53A8\u9017\u5439\u5782\u5E25\u63A8" + + "\u6C34\u708A\u7761\u7C8B\u7FE0\u8870\u9042\u9154" + + "\u9310\u9318\u968F\u745E\u9AC4\u5D07\u5D69\u6570" + + "\u67A2\u8DA8\u96DB\u636E\u6749\u6919\u83C5\u9817" + + "\u96C0\u88FE\u6F84\u647A\u5BF8\u4E16\u702C\u755D" + + "\u662F\u51C4\u5236\u52E2\u59D3\u5F81\u6027\u6210" + + "\u653F\u6574\u661F\u6674\u68F2\u6816\u6B63\u6E05" + + "\u7272\u751F\u76DB\u7CBE\u8056\u58F0\u88FD\u897F" + + "\u8AA0\u8A93\u8ACB\u901D\u9192\u9752\u9759\u6589" + + "\u7A0E\u8106\u96BB\u5E2D\u60DC\u621A\u65A5\u6614" + + "\u6790\u77F3\u7A4D\u7C4D\u7E3E\u810A\u8CAC\u8D64" + + "\u8DE1\u8E5F\u78A9\u5207\u62D9\u63A5\u6442\u6298" + + "\u8A2D\u7A83\u7BC0\u8AAC\u96EA\u7D76\u820C\u8749" + + "\u4ED9\u5148\u5343\u5360\u5BA3\u5C02\u5C16\u5DDD" + + "\u6226\u6247\u64B0\u6813\u6834\u6CC9\u6D45\u6D17" + + "\u67D3\u6F5C\u714E\u717D\u65CB\u7A7F\u7BAD\u7DDA" + + "\u7E4A\u7FA8\u817A\u821B\u8239\u85A6\u8A6E\u8CCE" + + "\u8DF5\u9078\u9077\u92AD\u9291\u9583\u9BAE\u524D" + + "\u5584\u6F38\u7136\u5168\u7985\u7E55\u81B3\u7CCE" + + "\u564C\u5851\u5CA8\u63AA\u66FE\u66FD\u695A\u72D9" + + "\u758F\u758E\u790E\u7956\u79DF\u7C97\u7D20\u7D44" + + "\u8607\u8A34\u963B\u9061\u9F20\u50E7\u5275\u53CC" + + "\u53E2\u5009\u55AA\u58EE\u594F\u723D\u5B8B\u5C64" + + "\u531D\u60E3\u60F3\u635C\u6383\u633F\u63BB\u64CD" + + "\u65E9\u66F9\u5DE3\u69CD\u69FD\u6F15\u71E5\u4E89" + + "\u75E9\u76F8\u7A93\u7CDF\u7DCF\u7D9C\u8061\u8349" + + "\u8358\u846C\u84BC\u85FB\u88C5\u8D70\u9001\u906D" + + "\u9397\u971C\u9A12\u50CF\u5897\u618E\u81D3\u8535" + + "\u8D08\u9020\u4FC3\u5074\u5247\u5373\u606F\u6349" + + "\u675F\u6E2C\u8DB3\u901F\u4FD7\u5C5E\u8CCA\u65CF" + + "\u7D9A\u5352\u8896\u5176\u63C3\u5B58\u5B6B\u5C0A" + + "\u640D\u6751\u905C\u4ED6\u591A\u592A\u6C70\u8A51" + + "\u553E\u5815\u59A5\u60F0\u6253\u67C1\u8235\u6955" + + "\u9640\u99C4\u9A28\u4F53\u5806\u5BFE\u8010\u5CB1" + + "\u5E2F\u5F85\u6020\u614B\u6234\u66FF\u6CF0\u6EDE" + + "\u80CE\u817F\u82D4\u888B\u8CB8\u9000\u902E\u968A" + + "\u9EDB\u9BDB\u4EE3\u53F0\u5927\u7B2C\u918D\u984C" + + "\u9DF9\u6EDD\u7027\u5353\u5544\u5B85\u6258\u629E" + + "\u62D3\u6CA2\u6FEF\u7422\u8A17\u9438\u6FC1\u8AFE" + + "\u8338\u51E7\u86F8\u53EA\u53E9\u4F46\u9054\u8FB0" + + "\u596A\u8131\u5DFD\u7AEA\u8FBF\u68DA\u8C37\u72F8" + + "\u9C48\u6A3D\u8AB0\u4E39\u5358\u5606\u5766\u62C5" + + "\u63A2\u65E6\u6B4E\u6DE1\u6E5B\u70AD\u77ED\u7AEF" + + "\u7BAA\u7DBB\u803D\u80C6\u86CB\u8A95\u935B\u56E3" + + "\u58C7\u5F3E\u65AD\u6696\u6A80\u6BB5\u7537\u8AC7" + + "\u5024\u77E5\u5730\u5F1B\u6065\u667A\u6C60\u75F4" + + "\u7A1A\u7F6E\u81F4\u8718\u9045\u99B3\u7BC9\u755C" + + "\u7AF9\u7B51\u84C4\u9010\u79E9\u7A92\u8336\u5AE1" + + "\u7740\u4E2D\u4EF2\u5B99\u5FE0\u62BD\u663C\u67F1" + + "\u6CE8\u866B\u8877\u8A3B\u914E\u92F3\u99D0\u6A17" + + "\u7026\u732A\u82E7\u8457\u8CAF\u4E01\u5146\u51CB" + + "\u558B\u5BF5\u5E16\u5E33\u5E81\u5F14\u5F35\u5F6B" + + "\u5FB4\u61F2\u6311\u66A2\u671D\u6F6E\u7252\u753A" + + "\u773A\u8074\u8139\u8178\u8776\u8ABF\u8ADC\u8D85" + + "\u8DF3\u929A\u9577\u9802\u9CE5\u52C5\u6357\u76F4" + + "\u6715\u6C88\u73CD\u8CC3\u93AE\u9673\u6D25\u589C" + + "\u690E\u69CC\u8FFD\u939A\u75DB\u901A\u585A\u6802" + + "\u63B4\u69FB\u4F43\u6F2C\u67D8\u8FBB\u8526\u7DB4" + + "\u9354\u693F\u6F70\u576A\u58F7\u5B2C\u7D2C\u722A" + + "\u540A\u91E3\u9DB4\u4EAD\u4F4E\u505C\u5075\u5243" + + "\u8C9E\u5448\u5824\u5B9A\u5E1D\u5E95\u5EAD\u5EF7" + + "\u5F1F\u608C\u62B5\u633A\u63D0\u68AF\u6C40\u7887" + + "\u798E\u7A0B\u7DE0\u8247\u8A02\u8AE6\u8E44\u9013" + + "\u90B8\u912D\u91D8\u9F0E\u6CE5\u6458\u64E2\u6575" + + "\u6EF4\u7684\u7B1B\u9069\u93D1\u6EBA\u54F2\u5FB9" + + "\u64A4\u8F4D\u8FED\u9244\u5178\u586B\u5929\u5C55" + + "\u5E97\u6DFB\u7E8F\u751C\u8CBC\u8EE2\u985B\u70B9" + + "\u4F1D\u6BBF\u6FB1\u7530\u96FB\u514E\u5410\u5835" + + "\u5857\u59AC\u5C60\u5F92\u6597\u675C\u6E21\u767B" + + "\u83DF\u8CED\u9014\u90FD\u934D\u7825\u783A\u52AA" + + "\u5EA6\u571F\u5974\u6012\u5012\u515A\u51AC\u51CD" + + "\u5200\u5510\u5854\u5858\u5957\u5B95\u5CF6\u5D8B" + + "\u60BC\u6295\u642D\u6771\u6843\u68BC\u68DF\u76D7" + + "\u6DD8\u6E6F\u6D9B\u706F\u71C8\u5F53\u75D8\u7977" + + "\u7B49\u7B54\u7B52\u7CD6\u7D71\u5230\u8463\u8569" + + "\u85E4\u8A0E\u8B04\u8C46\u8E0F\u9003\u900F\u9419" + + "\u9676\u982D\u9A30\u95D8\u50CD\u52D5\u540C\u5802" + + "\u5C0E\u61A7\u649E\u6D1E\u77B3\u7AE5\u80F4\u8404" + + "\u9053\u9285\u5CE0\u9D07\u533F\u5F97\u5FB3\u6D9C" + + "\u7279\u7763\u79BF\u7BE4\u6BD2\u72EC\u8AAD\u6803" + + "\u6A61\u51F8\u7A81\u6934\u5C4A\u9CF6\u82EB\u5BC5" + + "\u9149\u701E\u5678\u5C6F\u60C7\u6566\u6C8C\u8C5A" + + "\u9041\u9813\u5451\u66C7\u920D\u5948\u90A3\u5185" + + "\u4E4D\u51EA\u8599\u8B0E\u7058\u637A\u934B\u6962" + + "\u99B4\u7E04\u7577\u5357\u6960\u8EDF\u96E3\u6C5D" + + "\u4E8C\u5C3C\u5F10\u8FE9\u5302\u8CD1\u8089\u8679" + + "\u5EFF\u65E5\u4E73\u5165\u5982\u5C3F\u97EE\u4EFB" + + "\u598A\u5FCD\u8A8D\u6FE1\u79B0\u7962\u5BE7\u8471" + + "\u732B\u71B1\u5E74\u5FF5\u637B\u649A\u71C3\u7C98" + + "\u4E43\u5EFC\u4E4B\u57DC\u56A2\u60A9\u6FC3\u7D0D" + + "\u80FD\u8133\u81BF\u8FB2\u8997\u86A4\u5DF4\u628A" + + "\u64AD\u8987\u6777\u6CE2\u6D3E\u7436\u7834\u5A46" + + "\u7F75\u82AD\u99AC\u4FF3\u5EC3\u62DD\u6392\u6557" + + "\u676F\u76C3\u724C\u80CC\u80BA\u8F29\u914D\u500D" + + "\u57F9\u5A92\u6885\u6973\u7164\u72FD\u8CB7\u58F2" + + "\u8CE0\u966A\u9019\u877F\u79E4\u77E7\u8429\u4F2F" + + "\u5265\u535A\u62CD\u67CF\u6CCA\u767D\u7B94\u7C95" + + "\u8236\u8584\u8FEB\u66DD\u6F20\u7206\u7E1B\u83AB" + + "\u99C1\u9EA6\u51FD\u7BB1\u7872\u7BB8\u8087\u7B48" + + "\u6AE8\u5E61\u808C\u7551\u7560\u516B\u9262\u6E8C" + + "\u767A\u9197\u9AEA\u4F10\u7F70\u629C\u7B4F\u95A5" + + "\u9CE9\u567A\u5859\u86E4\u96BC\u4F34\u5224\u534A" + + "\u53CD\u53DB\u5E06\u642C\u6591\u677F\u6C3E\u6C4E" + + "\u7248\u72AF\u73ED\u7554\u7E41\u822C\u85E9\u8CA9" + + "\u7BC4\u91C6\u7169\u9812\u98EF\u633D\u6669\u756A" + + "\u76E4\u78D0\u8543\u86EE\u532A\u5351\u5426\u5983" + + "\u5E87\u5F7C\u60B2\u6249\u6279\u62AB\u6590\u6BD4" + + "\u6CCC\u75B2\u76AE\u7891\u79D8\u7DCB\u7F77\u80A5" + + "\u88AB\u8AB9\u8CBB\u907F\u975E\u98DB\u6A0B\u7C38" + + "\u5099\u5C3E\u5FAE\u6787\u6BD8\u7435\u7709\u7F8E" + + "\u9F3B\u67CA\u7A17\u5339\u758B\u9AED\u5F66\u819D" + + "\u83F1\u8098\u5F3C\u5FC5\u7562\u7B46\u903C\u6867" + + "\u59EB\u5A9B\u7D10\u767E\u8B2C\u4FF5\u5F6A\u6A19" + + "\u6C37\u6F02\u74E2\u7968\u8868\u8A55\u8C79\u5EDF" + + "\u63CF\u75C5\u79D2\u82D7\u9328\u92F2\u849C\u86ED" + + "\u9C2D\u54C1\u5F6C\u658C\u6D5C\u7015\u8CA7\u8CD3" + + "\u983B\u654F\u74F6\u4E0D\u4ED8\u57E0\u592B\u5A66" + + "\u5BCC\u51A8\u5E03\u5E9C\u6016\u6276\u6577\u65A7" + + "\u666E\u6D6E\u7236\u7B26\u8150\u819A\u8299\u8B5C" + + "\u8CA0\u8CE6\u8D74\u961C\u9644\u4FAE\u64AB\u6B66" + + "\u821E\u8461\u856A\u90E8\u5C01\u6953\u98A8\u847A" + + "\u8557\u4F0F\u526F\u5FA9\u5E45\u670D\u798F\u8179" + + "\u8907\u8986\u6DF5\u5F17\u6255\u6CB8\u4ECF\u7269" + + "\u9B92\u5206\u543B\u5674\u58B3\u61A4\u626E\u711A" + + "\u596E\u7C89\u7CDE\u7D1B\u96F0\u6587\u805E\u4E19" + + "\u4F75\u5175\u5840\u5E63\u5E73\u5F0A\u67C4\u4E26" + + "\u853D\u9589\u965B\u7C73\u9801\u50FB\u58C1\u7656" + + "\u78A7\u5225\u77A5\u8511\u7B86\u504F\u5909\u7247" + + "\u7BC7\u7DE8\u8FBA\u8FD4\u904D\u4FBF\u52C9\u5A29" + + "\u5F01\u97AD\u4FDD\u8217\u92EA\u5703\u6355\u6B69" + + "\u752B\u88DC\u8F14\u7A42\u52DF\u5893\u6155\u620A" + + "\u66AE\u6BCD\u7C3F\u83E9\u5023\u4FF8\u5305\u5446" + + "\u5831\u5949\u5B9D\u5CF0\u5CEF\u5D29\u5E96\u62B1" + + "\u6367\u653E\u65B9\u670B\u6CD5\u6CE1\u70F9\u7832" + + "\u7E2B\u80DE\u82B3\u840C\u84EC\u8702\u8912\u8A2A" + + "\u8C4A\u90A6\u92D2\u98FD\u9CF3\u9D6C\u4E4F\u4EA1" + + "\u508D\u5256\u574A\u59A8\u5E3D\u5FD8\u5FD9\u623F" + + "\u66B4\u671B\u67D0\u68D2\u5192\u7D21\u80AA\u81A8" + + "\u8B00\u8C8C\u8CBF\u927E\u9632\u5420\u982C\u5317" + + "\u50D5\u535C\u58A8\u64B2\u6734\u7267\u7766\u7A46" + + "\u91E6\u52C3\u6CA1\u6B86\u5800\u5E4C\u5954\u672C" + + "\u7FFB\u51E1\u76C6\u6469\u78E8\u9B54\u9EBB\u57CB" + + "\u59B9\u6627\u679A\u6BCE\u54E9\u69D9\u5E55\u819C" + + "\u6795\u9BAA\u67FE\u9C52\u685D\u4EA6\u4FE3\u53C8" + + "\u62B9\u672B\u6CAB\u8FC4\u4FAD\u7E6D\u9EBF\u4E07" + + "\u6162\u6E80\u6F2B\u8513\u5473\u672A\u9B45\u5DF3" + + "\u7B95\u5CAC\u5BC6\u871C\u6E4A\u84D1\u7A14\u8108" + + "\u5999\u7C8D\u6C11\u7720\u52D9\u5922\u7121\u725F" + + "\u77DB\u9727\u9D61\u690B\u5A7F\u5A18\u51A5\u540D" + + "\u547D\u660E\u76DF\u8FF7\u9298\u9CF4\u59EA\u725D" + + "\u6EC5\u514D\u68C9\u7DBF\u7DEC\u9762\u9EBA\u6478" + + "\u6A21\u8302\u5984\u5B5F\u6BDB\u731B\u76F2\u7DB2" + + "\u8017\u8499\u5132\u6728\u9ED9\u76EE\u6762\u52FF" + + "\u9905\u5C24\u623B\u7C7E\u8CB0\u554F\u60B6\u7D0B" + + "\u9580\u5301\u4E5F\u51B6\u591C\u723A\u8036\u91CE" + + "\u5F25\u77E2\u5384\u5F79\u7D04\u85AC\u8A33\u8E8D" + + "\u9756\u67F3\u85AE\u9453\u6109\u6108\u6CB9\u7652" + + "\u8AED\u8F38\u552F\u4F51\u512A\u52C7\u53CB\u5BA5" + + "\u5E7D\u60A0\u6182\u63D6\u6709\u67DA\u6E67\u6D8C" + + "\u7336\u7337\u7531\u7950\u88D5\u8A98\u904A\u9091" + + "\u90F5\u96C4\u878D\u5915\u4E88\u4F59\u4E0E\u8A89" + + "\u8F3F\u9810\u50AD\u5E7C\u5996\u5BB9\u5EB8\u63DA" + + "\u63FA\u64C1\u66DC\u694A\u69D8\u6D0B\u6EB6\u7194" + + "\u7528\u7AAF\u7F8A\u8000\u8449\u84C9\u8981\u8B21" + + "\u8E0A\u9065\u967D\u990A\u617E\u6291\u6B32\u6C83" + + "\u6D74\u7FCC\u7FFC\u6DC0\u7F85\u87BA\u88F8\u6765" + + "\u83B1\u983C\u96F7\u6D1B\u7D61\u843D\u916A\u4E71" + + "\u5375\u5D50\u6B04\u6FEB\u85CD\u862D\u89A7\u5229" + + "\u540F\u5C65\u674E\u68A8\u7406\u7483\u75E2\u88CF" + + "\u88E1\u91CC\u96E2\u9678\u5F8B\u7387\u7ACB\u844E" + + "\u63A0\u7565\u5289\u6D41\u6E9C\u7409\u7559\u786B" + + "\u7C92\u9686\u7ADC\u9F8D\u4FB6\u616E\u65C5\u865C" + + "\u4E86\u4EAE\u50DA\u4E21\u51CC\u5BEE\u6599\u6881" + + "\u6DBC\u731F\u7642\u77AD\u7A1C\u7CE7\u826F\u8AD2" + + "\u907C\u91CF\u9675\u9818\u529B\u7DD1\u502B\u5398" + + "\u6797\u6DCB\u71D0\u7433\u81E8\u8F2A\u96A3\u9C57" + + "\u9E9F\u7460\u5841\u6D99\u7D2F\u985E\u4EE4\u4F36" + + "\u4F8B\u51B7\u52B1\u5DBA\u601C\u73B2\u793C\u82D3" + + "\u9234\u96B7\u96F6\u970A\u9E97\u9F62\u66A6\u6B74" + + "\u5217\u52A3\u70C8\u88C2\u5EC9\u604B\u6190\u6F23" + + "\u7149\u7C3E\u7DF4\u806F\u84EE\u9023\u932C\u5442" + + "\u9B6F\u6AD3\u7089\u8CC2\u8DEF\u9732\u52B4\u5A41" + + "\u5ECA\u5F04\u6717\u697C\u6994\u6D6A\u6F0F\u7262" + + "\u72FC\u7BED\u8001\u807E\u874B\u90CE\u516D\u9E93" + + "\u7984\u808B\u9332\u8AD6\u502D\u548C\u8A71\u6B6A" + + "\u8CC4\u8107\u60D1\u67A0\u9DF2\u4E99\u4E98\u9C10" + + "\u8A6B\u85C1\u8568\u6900\u6E7E\u7897\u8155\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u5F0C\u4E10\u4E15\u4E2A\u4E31\u4E36" + + "\u4E3C\u4E3F\u4E42\u4E56\u4E58\u4E82\u4E85\u8C6B" + + "\u4E8A\u8212\u5F0D\u4E8E\u4E9E\u4E9F\u4EA0\u4EA2" + + "\u4EB0\u4EB3\u4EB6\u4ECE\u4ECD\u4EC4\u4EC6\u4EC2" + + "\u4ED7\u4EDE\u4EED\u4EDF\u4EF7\u4F09\u4F5A\u4F30" + + "\u4F5B\u4F5D\u4F57\u4F47\u4F76\u4F88\u4F8F\u4F98" + + "\u4F7B\u4F69\u4F70\u4F91\u4F6F\u4F86\u4F96\u5118" + + "\u4FD4\u4FDF\u4FCE\u4FD8\u4FDB\u4FD1\u4FDA\u4FD0" + + "\u4FE4\u4FE5\u501A\u5028\u5014\u502A\u5025\u5005" + + "\u4F1C\u4FF6\u5021\u5029\u502C\u4FFE\u4FEF\u5011" + + "\u5006\u5043\u5047\u6703\u5055\u5050\u5048\u505A" + + "\u5056\u506C\u5078\u5080\u509A\u5085\u50B4\u50B2" + + "\u50C9\u50CA\u50B3\u50C2\u50D6\u50DE\u50E5\u50ED" + + "\u50E3\u50EE\u50F9\u50F5\u5109\u5101\u5102\u5116" + + "\u5115\u5114\u511A\u5121\u513A\u5137\u513C\u513B" + + "\u513F\u5140\u5152\u514C\u5154\u5162\u7AF8\u5169" + + "\u516A\u516E\u5180\u5182\u56D8\u518C\u5189\u518F" + + "\u5191\u5193\u5195\u5196\u51A4\u51A6\u51A2\u51A9" + + "\u51AA\u51AB\u51B3\u51B1\u51B2\u51B0\u51B5\u51BD" + + "\u51C5\u51C9\u51DB\u51E0\u8655\u51E9\u51ED\u51F0" + + "\u51F5\u51FE\u5204\u520B\u5214\u520E\u5227\u522A" + + "\u522E\u5233\u5239\u524F\u5244\u524B\u524C\u525E" + + "\u5254\u526A\u5274\u5269\u5273\u527F\u527D\u528D" + + "\u5294\u5292\u5271\u5288\u5291\u8FA8\u8FA7\u52AC" + + "\u52AD\u52BC\u52B5\u52C1\u52CD\u52D7\u52DE\u52E3" + + "\u52E6\u98ED\u52E0\u52F3\u52F5\u52F8\u52F9\u5306" + + "\u5308\u7538\u530D\u5310\u530F\u5315\u531A\u5323" + + "\u532F\u5331\u5333\u5338\u5340\u5346\u5345\u4E17" + + "\u5349\u534D\u51D6\u535E\u5369\u536E\u5918\u537B" + + "\u5377\u5382\u5396\u53A0\u53A6\u53A5\u53AE\u53B0" + + "\u53B6\u53C3\u7C12\u96D9\u53DF\u66FC\u71EE\u53EE" + + "\u53E8\u53ED\u53FA\u5401\u543D\u5440\u542C\u542D" + + "\u543C\u542E\u5436\u5429\u541D\u544E\u548F\u5475" + + "\u548E\u545F\u5471\u5477\u5470\u5492\u547B\u5480" + + "\u5476\u5484\u5490\u5486\u54C7\u54A2\u54B8\u54A5" + + "\u54AC\u54C4\u54C8\u54A8\u54AB\u54C2\u54A4\u54BE" + + "\u54BC\u54D8\u54E5\u54E6\u550F\u5514\u54FD\u54EE" + + "\u54ED\u54FA\u54E2\u5539\u5540\u5563\u554C\u552E" + + "\u555C\u5545\u5556\u5557\u5538\u5533\u555D\u5599" + + "\u5580\u54AF\u558A\u559F\u557B\u557E\u5598\u559E" + + "\u55AE\u557C\u5583\u55A9\u5587\u55A8\u55DA\u55C5" + + "\u55DF\u55C4\u55DC\u55E4\u55D4\u5614\u55F7\u5616" + + "\u55FE\u55FD\u561B\u55F9\u564E\u5650\u71DF\u5634" + + "\u5636\u5632\u5638\u566B\u5664\u562F\u566C\u566A" + + "\u5686\u5680\u568A\u56A0\u5694\u568F\u56A5\u56AE" + + "\u56B6\u56B4\u56C2\u56BC\u56C1\u56C3\u56C0\u56C8" + + "\u56CE\u56D1\u56D3\u56D7\u56EE\u56F9\u5700\u56FF" + + "\u5704\u5709\u5708\u570B\u570D\u5713\u5718\u5716" + + "\u55C7\u571C\u5726\u5737\u5738\u574E\u573B\u5740" + + "\u574F\u5769\u57C0\u5788\u5761\u577F\u5789\u5793" + + "\u57A0\u57B3\u57A4\u57AA\u57B0\u57C3\u57C6\u57D4" + + "\u57D2\u57D3\u580A\u57D6\u57E3\u580B\u5819\u581D" + + "\u5872\u5821\u5862\u584B\u5870\u6BC0\u5852\u583D" + + "\u5879\u5885\u58B9\u589F\u58AB\u58BA\u58DE\u58BB" + + "\u58B8\u58AE\u58C5\u58D3\u58D1\u58D7\u58D9\u58D8" + + "\u58E5\u58DC\u58E4\u58DF\u58EF\u58FA\u58F9\u58FB" + + "\u58FC\u58FD\u5902\u590A\u5910\u591B\u68A6\u5925" + + "\u592C\u592D\u5932\u5938\u593E\u7AD2\u5955\u5950" + + "\u594E\u595A\u5958\u5962\u5960\u5967\u596C\u5969" + + "\u5978\u5981\u599D\u4F5E\u4FAB\u59A3\u59B2\u59C6" + + "\u59E8\u59DC\u598D\u59D9\u59DA\u5A25\u5A1F\u5A11" + + "\u5A1C\u5A09\u5A1A\u5A40\u5A6C\u5A49\u5A35\u5A36" + + "\u5A62\u5A6A\u5A9A\u5ABC\u5ABE\u5ACB\u5AC2\u5ABD" + + "\u5AE3\u5AD7\u5AE6\u5AE9\u5AD6\u5AFA\u5AFB\u5B0C" + + "\u5B0B\u5B16\u5B32\u5AD0\u5B2A\u5B36\u5B3E\u5B43" + + "\u5B45\u5B40\u5B51\u5B55\u5B5A\u5B5B\u5B65\u5B69" + + "\u5B70\u5B73\u5B75\u5B78\u6588\u5B7A\u5B80\u5B83" + + "\u5BA6\u5BB8\u5BC3\u5BC7\u5BC9\u5BD4\u5BD0\u5BE4" + + "\u5BE6\u5BE2\u5BDE\u5BE5\u5BEB\u5BF0\u5BF6\u5BF3" + + "\u5C05\u5C07\u5C08\u5C0D\u5C13\u5C20\u5C22\u5C28" + + "\u5C38\u5C39\u5C41\u5C46\u5C4E\u5C53\u5C50\u5C4F" + + "\u5B71\u5C6C\u5C6E\u4E62\u5C76\u5C79\u5C8C\u5C91" + + "\u5C94\u599B\u5CAB\u5CBB\u5CB6\u5CBC\u5CB7\u5CC5" + + "\u5CBE\u5CC7\u5CD9\u5CE9\u5CFD\u5CFA\u5CED\u5D8C" + + "\u5CEA\u5D0B\u5D15\u5D17\u5D5C\u5D1F\u5D1B\u5D11" + + "\u5D14\u5D22\u5D1A\u5D19\u5D18\u5D4C\u5D52\u5D4E" + + "\u5D4B\u5D6C\u5D73\u5D76\u5D87\u5D84\u5D82\u5DA2" + + "\u5D9D\u5DAC\u5DAE\u5DBD\u5D90\u5DB7\u5DBC\u5DC9" + + "\u5DCD\u5DD3\u5DD2\u5DD6\u5DDB\u5DEB\u5DF2\u5DF5" + + "\u5E0B\u5E1A\u5E19\u5E11\u5E1B\u5E36\u5E37\u5E44" + + "\u5E43\u5E40\u5E4E\u5E57\u5E54\u5E5F\u5E62\u5E64" + + "\u5E47\u5E75\u5E76\u5E7A\u9EBC\u5E7F\u5EA0\u5EC1" + + "\u5EC2\u5EC8\u5ED0\u5ECF\u5ED6\u5EE3\u5EDD\u5EDA" + + "\u5EDB\u5EE2\u5EE1\u5EE8\u5EE9\u5EEC\u5EF1\u5EF3" + + "\u5EF0\u5EF4\u5EF8\u5EFE\u5F03\u5F09\u5F5D\u5F5C" + + "\u5F0B\u5F11\u5F16\u5F29\u5F2D\u5F38\u5F41\u5F48" + + "\u5F4C\u5F4E\u5F2F\u5F51\u5F56\u5F57\u5F59\u5F61" + + "\u5F6D\u5F73\u5F77\u5F83\u5F82\u5F7F\u5F8A\u5F88" + + "\u5F91\u5F87\u5F9E\u5F99\u5F98\u5FA0\u5FA8\u5FAD" + + "\u5FBC\u5FD6\u5FFB\u5FE4\u5FF8\u5FF1\u5FDD\u60B3" + + "\u5FFF\u6021\u6060\u6019\u6010\u6029\u600E\u6031" + + "\u601B\u6015\u602B\u6026\u600F\u603A\u605A\u6041" + + "\u606A\u6077\u605F\u604A\u6046\u604D\u6063\u6043" + + "\u6064\u6042\u606C\u606B\u6059\u6081\u608D\u60E7" + + "\u6083\u609A\u6084\u609B\u6096\u6097\u6092\u60A7" + + "\u608B\u60E1\u60B8\u60E0\u60D3\u60B4\u5FF0\u60BD" + + "\u60C6\u60B5\u60D8\u614D\u6115\u6106\u60F6\u60F7" + + "\u6100\u60F4\u60FA\u6103\u6121\u60FB\u60F1\u610D" + + "\u610E\u6147\u613E\u6128\u6127\u614A\u613F\u613C" + + "\u612C\u6134\u613D\u6142\u6144\u6173\u6177\u6158" + + "\u6159\u615A\u616B\u6174\u616F\u6165\u6171\u615F" + + "\u615D\u6153\u6175\u6199\u6196\u6187\u61AC\u6194" + + "\u619A\u618A\u6191\u61AB\u61AE\u61CC\u61CA\u61C9" + + "\u61F7\u61C8\u61C3\u61C6\u61BA\u61CB\u7F79\u61CD" + + "\u61E6\u61E3\u61F6\u61FA\u61F4\u61FF\u61FD\u61FC" + + "\u61FE\u6200\u6208\u6209\u620D\u620C\u6214\u621B" + + "\u621E\u6221\u622A\u622E\u6230\u6232\u6233\u6241" + + "\u624E\u625E\u6263\u625B\u6260\u6268\u627C\u6282" + + "\u6289\u627E\u6292\u6293\u6296\u62D4\u6283\u6294" + + "\u62D7\u62D1\u62BB\u62CF\u62FF\u62C6\u64D4\u62C8" + + "\u62DC\u62CC\u62CA\u62C2\u62C7\u629B\u62C9\u630C" + + "\u62EE\u62F1\u6327\u6302\u6308\u62EF\u62F5\u6350" + + "\u633E\u634D\u641C\u634F\u6396\u638E\u6380\u63AB" + + "\u6376\u63A3\u638F\u6389\u639F\u63B5\u636B\u6369" + + "\u63BE\u63E9\u63C0\u63C6\u63E3\u63C9\u63D2\u63F6" + + "\u63C4\u6416\u6434\u6406\u6413\u6426\u6436\u651D" + + "\u6417\u6428\u640F\u6467\u646F\u6476\u644E\u652A" + + "\u6495\u6493\u64A5\u64A9\u6488\u64BC\u64DA\u64D2" + + "\u64C5\u64C7\u64BB\u64D8\u64C2\u64F1\u64E7\u8209" + + "\u64E0\u64E1\u62AC\u64E3\u64EF\u652C\u64F6\u64F4" + + "\u64F2\u64FA\u6500\u64FD\u6518\u651C\u6505\u6524" + + "\u6523\u652B\u6534\u6535\u6537\u6536\u6538\u754B" + + "\u6548\u6556\u6555\u654D\u6558\u655E\u655D\u6572" + + "\u6578\u6582\u6583\u8B8A\u659B\u659F\u65AB\u65B7" + + "\u65C3\u65C6\u65C1\u65C4\u65CC\u65D2\u65DB\u65D9" + + "\u65E0\u65E1\u65F1\u6772\u660A\u6603\u65FB\u6773" + + "\u6635\u6636\u6634\u661C\u664F\u6644\u6649\u6641" + + "\u665E\u665D\u6664\u6667\u6668\u665F\u6662\u6670" + + "\u6683\u6688\u668E\u6689\u6684\u6698\u669D\u66C1" + + "\u66B9\u66C9\u66BE\u66BC\u66C4\u66B8\u66D6\u66DA" + + "\u66E0\u663F\u66E6\u66E9\u66F0\u66F5\u66F7\u670F" + + "\u6716\u671E\u6726\u6727\u9738\u672E\u673F\u6736" + + "\u6741\u6738\u6737\u6746\u675E\u6760\u6759\u6763" + + "\u6764\u6789\u6770\u67A9\u677C\u676A\u678C\u678B" + + "\u67A6\u67A1\u6785\u67B7\u67EF\u67B4\u67EC\u67B3" + + "\u67E9\u67B8\u67E4\u67DE\u67DD\u67E2\u67EE\u67B9" + + "\u67CE\u67C6\u67E7\u6A9C\u681E\u6846\u6829\u6840" + + "\u684D\u6832\u684E\u68B3\u682B\u6859\u6863\u6877" + + "\u687F\u689F\u688F\u68AD\u6894\u689D\u689B\u6883" + + "\u6AAE\u68B9\u6874\u68B5\u68A0\u68BA\u690F\u688D" + + "\u687E\u6901\u68CA\u6908\u68D8\u6922\u6926\u68E1" + + "\u690C\u68CD\u68D4\u68E7\u68D5\u6936\u6912\u6904" + + "\u68D7\u68E3\u6925\u68F9\u68E0\u68EF\u6928\u692A" + + "\u691A\u6923\u6921\u68C6\u6979\u6977\u695C\u6978" + + "\u696B\u6954\u697E\u696E\u6939\u6974\u693D\u6959" + + "\u6930\u6961\u695E\u695D\u6981\u696A\u69B2\u69AE" + + "\u69D0\u69BF\u69C1\u69D3\u69BE\u69CE\u5BE8\u69CA" + + "\u69DD\u69BB\u69C3\u69A7\u6A2E\u6991\u69A0\u699C" + + "\u6995\u69B4\u69DE\u69E8\u6A02\u6A1B\u69FF\u6B0A" + + "\u69F9\u69F2\u69E7\u6A05\u69B1\u6A1E\u69ED\u6A14" + + "\u69EB\u6A0A\u6A12\u6AC1\u6A23\u6A13\u6A44\u6A0C" + + "\u6A72\u6A36\u6A78\u6A47\u6A62\u6A59\u6A66\u6A48" + + "\u6A38\u6A22\u6A90\u6A8D\u6AA0\u6A84\u6AA2\u6AA3" + + "\u6A97\u8617\u6ABB\u6AC3\u6AC2\u6AB8\u6AB3\u6AAC" + + "\u6ADE\u6AD1\u6ADF\u6AAA\u6ADA\u6AEA\u6AFB\u6B05" + + "\u8616\u6AFA\u6B12\u6B16\u9B31\u6B1F\u6B38\u6B37" + + "\u76DC\u6B39\u98EE\u6B47\u6B43\u6B49\u6B50\u6B59" + + "\u6B54\u6B5B\u6B5F\u6B61\u6B78\u6B79\u6B7F\u6B80" + + "\u6B84\u6B83\u6B8D\u6B98\u6B95\u6B9E\u6BA4\u6BAA" + + "\u6BAB\u6BAF\u6BB2\u6BB1\u6BB3\u6BB7\u6BBC\u6BC6" + + "\u6BCB\u6BD3\u6BDF\u6BEC\u6BEB\u6BF3\u6BEF\u9EBE" + + "\u6C08\u6C13\u6C14\u6C1B\u6C24\u6C23\u6C5E\u6C55" + + "\u6C62\u6C6A\u6C82\u6C8D\u6C9A\u6C81\u6C9B\u6C7E" + + "\u6C68\u6C73\u6C92\u6C90\u6CC4\u6CF1\u6CD3\u6CBD" + + "\u6CD7\u6CC5\u6CDD\u6CAE\u6CB1\u6CBE\u6CBA\u6CDB" + + "\u6CEF\u6CD9\u6CEA\u6D1F\u884D\u6D36\u6D2B\u6D3D" + + "\u6D38\u6D19\u6D35\u6D33\u6D12\u6D0C\u6D63\u6D93" + + "\u6D64\u6D5A\u6D79\u6D59\u6D8E\u6D95\u6FE4\u6D85" + + "\u6DF9\u6E15\u6E0A\u6DB5\u6DC7\u6DE6\u6DB8\u6DC6" + + "\u6DEC\u6DDE\u6DCC\u6DE8\u6DD2\u6DC5\u6DFA\u6DD9" + + "\u6DE4\u6DD5\u6DEA\u6DEE\u6E2D\u6E6E\u6E2E\u6E19" + + "\u6E72\u6E5F\u6E3E\u6E23\u6E6B\u6E2B\u6E76\u6E4D" + + "\u6E1F\u6E43\u6E3A\u6E4E\u6E24\u6EFF\u6E1D\u6E38" + + "\u6E82\u6EAA\u6E98\u6EC9\u6EB7\u6ED3\u6EBD\u6EAF" + + "\u6EC4\u6EB2\u6ED4\u6ED5\u6E8F\u6EA5\u6EC2\u6E9F" + + "\u6F41\u6F11\u704C\u6EEC\u6EF8\u6EFE\u6F3F\u6EF2" + + "\u6F31\u6EEF\u6F32\u6ECC\u6F3E\u6F13\u6EF7\u6F86" + + "\u6F7A\u6F78\u6F81\u6F80\u6F6F\u6F5B\u6FF3\u6F6D" + + "\u6F82\u6F7C\u6F58\u6F8E\u6F91\u6FC2\u6F66\u6FB3" + + "\u6FA3\u6FA1\u6FA4\u6FB9\u6FC6\u6FAA\u6FDF\u6FD5" + + "\u6FEC\u6FD4\u6FD8\u6FF1\u6FEE\u6FDB\u7009\u700B" + + "\u6FFA\u7011\u7001\u700F\u6FFE\u701B\u701A\u6F74" + + "\u701D\u7018\u701F\u7030\u703E\u7032\u7051\u7063" + + "\u7099\u7092\u70AF\u70F1\u70AC\u70B8\u70B3\u70AE" + + "\u70DF\u70CB\u70DD\u70D9\u7109\u70FD\u711C\u7119" + + "\u7165\u7155\u7188\u7166\u7162\u714C\u7156\u716C" + + "\u718F\u71FB\u7184\u7195\u71A8\u71AC\u71D7\u71B9" + + "\u71BE\u71D2\u71C9\u71D4\u71CE\u71E0\u71EC\u71E7" + + "\u71F5\u71FC\u71F9\u71FF\u720D\u7210\u721B\u7228" + + "\u722D\u722C\u7230\u7232\u723B\u723C\u723F\u7240" + + "\u7246\u724B\u7258\u7274\u727E\u7282\u7281\u7287" + + "\u7292\u7296\u72A2\u72A7\u72B9\u72B2\u72C3\u72C6" + + "\u72C4\u72CE\u72D2\u72E2\u72E0\u72E1\u72F9\u72F7" + + "\u500F\u7317\u730A\u731C\u7316\u731D\u7334\u732F" + + "\u7329\u7325\u733E\u734E\u734F\u9ED8\u7357\u736A" + + "\u7368\u7370\u7378\u7375\u737B\u737A\u73C8\u73B3" + + "\u73CE\u73BB\u73C0\u73E5\u73EE\u73DE\u74A2\u7405" + + "\u746F\u7425\u73F8\u7432\u743A\u7455\u743F\u745F" + + "\u7459\u7441\u745C\u7469\u7470\u7463\u746A\u7476" + + "\u747E\u748B\u749E\u74A7\u74CA\u74CF\u74D4\u73F1" + + "\u74E0\u74E3\u74E7\u74E9\u74EE\u74F2\u74F0\u74F1" + + "\u74F8\u74F7\u7504\u7503\u7505\u750C\u750E\u750D" + + "\u7515\u7513\u751E\u7526\u752C\u753C\u7544\u754D" + + "\u754A\u7549\u755B\u7546\u755A\u7569\u7564\u7567" + + "\u756B\u756D\u7578\u7576\u7586\u7587\u7574\u758A" + + "\u7589\u7582\u7594\u759A\u759D\u75A5\u75A3\u75C2" + + "\u75B3\u75C3\u75B5\u75BD\u75B8\u75BC\u75B1\u75CD" + + "\u75CA\u75D2\u75D9\u75E3\u75DE\u75FE\u75FF\u75FC" + + "\u7601\u75F0\u75FA\u75F2\u75F3\u760B\u760D\u7609" + + "\u761F\u7627\u7620\u7621\u7622\u7624\u7634\u7630" + + "\u763B\u7647\u7648\u7646\u765C\u7658\u7661\u7662" + + "\u7668\u7669\u766A\u7667\u766C\u7670\u7672\u7676" + + "\u7678\u767C\u7680\u7683\u7688\u768B\u768E\u7696" + + "\u7693\u7699\u769A\u76B0\u76B4\u76B8\u76B9\u76BA" + + "\u76C2\u76CD\u76D6\u76D2\u76DE\u76E1\u76E5\u76E7" + + "\u76EA\u862F\u76FB\u7708\u7707\u7704\u7729\u7724" + + "\u771E\u7725\u7726\u771B\u7737\u7738\u7747\u775A" + + "\u7768\u776B\u775B\u7765\u777F\u777E\u7779\u778E" + + "\u778B\u7791\u77A0\u779E\u77B0\u77B6\u77B9\u77BF" + + "\u77BC\u77BD\u77BB\u77C7\u77CD\u77D7\u77DA\u77DC" + + "\u77E3\u77EE\u77FC\u780C\u7812\u7926\u7820\u792A" + + "\u7845\u788E\u7874\u7886\u787C\u789A\u788C\u78A3" + + "\u78B5\u78AA\u78AF\u78D1\u78C6\u78CB\u78D4\u78BE" + + "\u78BC\u78C5\u78CA\u78EC\u78E7\u78DA\u78FD\u78F4" + + "\u7907\u7912\u7911\u7919\u792C\u792B\u7940\u7960" + + "\u7957\u795F\u795A\u7955\u7953\u797A\u797F\u798A" + + "\u799D\u79A7\u9F4B\u79AA\u79AE\u79B3\u79B9\u79BA" + + "\u79C9\u79D5\u79E7\u79EC\u79E1\u79E3\u7A08\u7A0D" + + "\u7A18\u7A19\u7A20\u7A1F\u7980\u7A31\u7A3B\u7A3E" + + "\u7A37\u7A43\u7A57\u7A49\u7A61\u7A62\u7A69\u9F9D" + + "\u7A70\u7A79\u7A7D\u7A88\u7A97\u7A95\u7A98\u7A96" + + "\u7AA9\u7AC8\u7AB0\u7AB6\u7AC5\u7AC4\u7ABF\u9083" + + "\u7AC7\u7ACA\u7ACD\u7ACF\u7AD5\u7AD3\u7AD9\u7ADA" + + "\u7ADD\u7AE1\u7AE2\u7AE6\u7AED\u7AF0\u7B02\u7B0F" + + "\u7B0A\u7B06\u7B33\u7B18\u7B19\u7B1E\u7B35\u7B28" + + "\u7B36\u7B50\u7B7A\u7B04\u7B4D\u7B0B\u7B4C\u7B45" + + "\u7B75\u7B65\u7B74\u7B67\u7B70\u7B71\u7B6C\u7B6E" + + "\u7B9D\u7B98\u7B9F\u7B8D\u7B9C\u7B9A\u7B8B\u7B92" + + "\u7B8F\u7B5D\u7B99\u7BCB\u7BC1\u7BCC\u7BCF\u7BB4" + + "\u7BC6\u7BDD\u7BE9\u7C11\u7C14\u7BE6\u7BE5\u7C60" + + "\u7C00\u7C07\u7C13\u7BF3\u7BF7\u7C17\u7C0D\u7BF6" + + "\u7C23\u7C27\u7C2A\u7C1F\u7C37\u7C2B\u7C3D\u7C4C" + + "\u7C43\u7C54\u7C4F\u7C40\u7C50\u7C58\u7C5F\u7C64" + + "\u7C56\u7C65\u7C6C\u7C75\u7C83\u7C90\u7CA4\u7CAD" + + "\u7CA2\u7CAB\u7CA1\u7CA8\u7CB3\u7CB2\u7CB1\u7CAE" + + "\u7CB9\u7CBD\u7CC0\u7CC5\u7CC2\u7CD8\u7CD2\u7CDC" + + "\u7CE2\u9B3B\u7CEF\u7CF2\u7CF4\u7CF6\u7CFA\u7D06" + + "\u7D02\u7D1C\u7D15\u7D0A\u7D45\u7D4B\u7D2E\u7D32" + + "\u7D3F\u7D35\u7D46\u7D73\u7D56\u7D4E\u7D72\u7D68" + + "\u7D6E\u7D4F\u7D63\u7D93\u7D89\u7D5B\u7D8F\u7D7D" + + "\u7D9B\u7DBA\u7DAE\u7DA3\u7DB5\u7DC7\u7DBD\u7DAB" + + "\u7E3D\u7DA2\u7DAF\u7DDC\u7DB8\u7D9F\u7DB0\u7DD8" + + "\u7DDD\u7DE4\u7DDE\u7DFB\u7DF2\u7DE1\u7E05\u7E0A" + + "\u7E23\u7E21\u7E12\u7E31\u7E1F\u7E09\u7E0B\u7E22" + + "\u7E46\u7E66\u7E3B\u7E35\u7E39\u7E43\u7E37\u7E32" + + "\u7E3A\u7E67\u7E5D\u7E56\u7E5E\u7E59\u7E5A\u7E79" + + "\u7E6A\u7E69\u7E7C\u7E7B\u7E83\u7DD5\u7E7D\u8FAE" + + "\u7E7F\u7E88\u7E89\u7E8C\u7E92\u7E90\u7E93\u7E94" + + "\u7E96\u7E8E\u7E9B\u7E9C\u7F38\u7F3A\u7F45\u7F4C" + + "\u7F4D\u7F4E\u7F50\u7F51\u7F55\u7F54\u7F58\u7F5F" + + "\u7F60\u7F68\u7F69\u7F67\u7F78\u7F82\u7F86\u7F83" + + "\u7F88\u7F87\u7F8C\u7F94\u7F9E\u7F9D\u7F9A\u7FA3" + + "\u7FAF\u7FB2\u7FB9\u7FAE\u7FB6\u7FB8\u8B71\u7FC5" + + "\u7FC6\u7FCA\u7FD5\u7FD4\u7FE1\u7FE6\u7FE9\u7FF3" + + "\u7FF9\u98DC\u8006\u8004\u800B\u8012\u8018\u8019" + + "\u801C\u8021\u8028\u803F\u803B\u804A\u8046\u8052" + + "\u8058\u805A\u805F\u8062\u8068\u8073\u8072\u8070" + + "\u8076\u8079\u807D\u807F\u8084\u8086\u8085\u809B" + + "\u8093\u809A\u80AD\u5190\u80AC\u80DB\u80E5\u80D9" + + "\u80DD\u80C4\u80DA\u80D6\u8109\u80EF\u80F1\u811B" + + "\u8129\u8123\u812F\u814B\u968B\u8146\u813E\u8153" + + "\u8151\u80FC\u8171\u816E\u8165\u8166\u8174\u8183" + + "\u8188\u818A\u8180\u8182\u81A0\u8195\u81A4\u81A3" + + "\u815F\u8193\u81A9\u81B0\u81B5\u81BE\u81B8\u81BD" + + "\u81C0\u81C2\u81BA\u81C9\u81CD\u81D1\u81D9\u81D8" + + "\u81C8\u81DA\u81DF\u81E0\u81E7\u81FA\u81FB\u81FE" + + "\u8201\u8202\u8205\u8207\u820A\u820D\u8210\u8216" + + "\u8229\u822B\u8238\u8233\u8240\u8259\u8258\u825D" + + "\u825A\u825F\u8264\u8262\u8268\u826A\u826B\u822E" + + "\u8271\u8277\u8278\u827E\u828D\u8292\u82AB\u829F" + + "\u82BB\u82AC\u82E1\u82E3\u82DF\u82D2\u82F4\u82F3" + + "\u82FA\u8393\u8303\u82FB\u82F9\u82DE\u8306\u82DC" + + "\u8309\u82D9\u8335\u8334\u8316\u8332\u8331\u8340" + + "\u8339\u8350\u8345\u832F\u832B\u8317\u8318\u8385" + + "\u839A\u83AA\u839F\u83A2\u8396\u8323\u838E\u8387" + + "\u838A\u837C\u83B5\u8373\u8375\u83A0\u8389\u83A8" + + "\u83F4\u8413\u83EB\u83CE\u83FD\u8403\u83D8\u840B" + + "\u83C1\u83F7\u8407\u83E0\u83F2\u840D\u8422\u8420" + + "\u83BD\u8438\u8506\u83FB\u846D\u842A\u843C\u855A" + + "\u8484\u8477\u846B\u84AD\u846E\u8482\u8469\u8446" + + "\u842C\u846F\u8479\u8435\u84CA\u8462\u84B9\u84BF" + + "\u849F\u84D9\u84CD\u84BB\u84DA\u84D0\u84C1\u84C6" + + "\u84D6\u84A1\u8521\u84FF\u84F4\u8517\u8518\u852C" + + "\u851F\u8515\u8514\u84FC\u8540\u8563\u8558\u8548" + + "\u8541\u8602\u854B\u8555\u8580\u85A4\u8588\u8591" + + "\u858A\u85A8\u856D\u8594\u859B\u85EA\u8587\u859C" + + "\u8577\u857E\u8590\u85C9\u85BA\u85CF\u85B9\u85D0" + + "\u85D5\u85DD\u85E5\u85DC\u85F9\u860A\u8613\u860B" + + "\u85FE\u85FA\u8606\u8622\u861A\u8630\u863F\u864D" + + "\u4E55\u8654\u865F\u8667\u8671\u8693\u86A3\u86A9" + + "\u86AA\u868B\u868C\u86B6\u86AF\u86C4\u86C6\u86B0" + + "\u86C9\u8823\u86AB\u86D4\u86DE\u86E9\u86EC\u86DF" + + "\u86DB\u86EF\u8712\u8706\u8708\u8700\u8703\u86FB" + + "\u8711\u8709\u870D\u86F9\u870A\u8734\u873F\u8737" + + "\u873B\u8725\u8729\u871A\u8760\u875F\u8778\u874C" + + "\u874E\u8774\u8757\u8768\u876E\u8759\u8753\u8763" + + "\u876A\u8805\u87A2\u879F\u8782\u87AF\u87CB\u87BD" + + "\u87C0\u87D0\u96D6\u87AB\u87C4\u87B3\u87C7\u87C6" + + "\u87BB\u87EF\u87F2\u87E0\u880F\u880D\u87FE\u87F6" + + "\u87F7\u880E\u87D2\u8811\u8816\u8815\u8822\u8821" + + "\u8831\u8836\u8839\u8827\u883B\u8844\u8842\u8852" + + "\u8859\u885E\u8862\u886B\u8881\u887E\u889E\u8875" + + "\u887D\u88B5\u8872\u8882\u8897\u8892\u88AE\u8899" + + "\u88A2\u888D\u88A4\u88B0\u88BF\u88B1\u88C3\u88C4" + + "\u88D4\u88D8\u88D9\u88DD\u88F9\u8902\u88FC\u88F4" + + "\u88E8\u88F2\u8904\u890C\u890A\u8913\u8943\u891E" + + "\u8925\u892A\u892B\u8941\u8944\u893B\u8936\u8938" + + "\u894C\u891D\u8960\u895E\u8966\u8964\u896D\u896A" + + "\u896F\u8974\u8977\u897E\u8983\u8988\u898A\u8993" + + "\u8998\u89A1\u89A9\u89A6\u89AC\u89AF\u89B2\u89BA" + + "\u89BD\u89BF\u89C0\u89DA\u89DC\u89DD\u89E7\u89F4" + + "\u89F8\u8A03\u8A16\u8A10\u8A0C\u8A1B\u8A1D\u8A25" + + "\u8A36\u8A41\u8A5B\u8A52\u8A46\u8A48\u8A7C\u8A6D" + + "\u8A6C\u8A62\u8A85\u8A82\u8A84\u8AA8\u8AA1\u8A91" + + "\u8AA5\u8AA6\u8A9A\u8AA3\u8AC4\u8ACD\u8AC2\u8ADA" + + "\u8AEB\u8AF3\u8AE7\u8AE4\u8AF1\u8B14\u8AE0\u8AE2" + + "\u8AF7\u8ADE\u8ADB\u8B0C\u8B07\u8B1A\u8AE1\u8B16" + + "\u8B10\u8B17\u8B20\u8B33\u97AB\u8B26\u8B2B\u8B3E" + + "\u8B28\u8B41\u8B4C\u8B4F\u8B4E\u8B49\u8B56\u8B5B" + + "\u8B5A\u8B6B\u8B5F\u8B6C\u8B6F\u8B74\u8B7D\u8B80" + + "\u8B8C\u8B8E\u8B92\u8B93\u8B96\u8B99\u8B9A\u8C3A" + + "\u8C41\u8C3F\u8C48\u8C4C\u8C4E\u8C50\u8C55\u8C62" + + "\u8C6C\u8C78\u8C7A\u8C82\u8C89\u8C85\u8C8A\u8C8D" + + "\u8C8E\u8C94\u8C7C\u8C98\u621D\u8CAD\u8CAA\u8CBD" + + "\u8CB2\u8CB3\u8CAE\u8CB6\u8CC8\u8CC1\u8CE4\u8CE3" + + "\u8CDA\u8CFD\u8CFA\u8CFB\u8D04\u8D05\u8D0A\u8D07" + + "\u8D0F\u8D0D\u8D10\u9F4E\u8D13\u8CCD\u8D14\u8D16" + + "\u8D67\u8D6D\u8D71\u8D73\u8D81\u8D99\u8DC2\u8DBE" + + "\u8DBA\u8DCF\u8DDA\u8DD6\u8DCC\u8DDB\u8DCB\u8DEA" + + "\u8DEB\u8DDF\u8DE3\u8DFC\u8E08\u8E09\u8DFF\u8E1D" + + "\u8E1E\u8E10\u8E1F\u8E42\u8E35\u8E30\u8E34\u8E4A" + + "\u8E47\u8E49\u8E4C\u8E50\u8E48\u8E59\u8E64\u8E60" + + "\u8E2A\u8E63\u8E55\u8E76\u8E72\u8E7C\u8E81\u8E87" + + "\u8E85\u8E84\u8E8B\u8E8A\u8E93\u8E91\u8E94\u8E99" + + "\u8EAA\u8EA1\u8EAC\u8EB0\u8EC6\u8EB1\u8EBE\u8EC5" + + "\u8EC8\u8ECB\u8EDB\u8EE3\u8EFC\u8EFB\u8EEB\u8EFE" + + "\u8F0A\u8F05\u8F15\u8F12\u8F19\u8F13\u8F1C\u8F1F" + + "\u8F1B\u8F0C\u8F26\u8F33\u8F3B\u8F39\u8F45\u8F42" + + "\u8F3E\u8F4C\u8F49\u8F46\u8F4E\u8F57\u8F5C\u8F62" + + "\u8F63\u8F64\u8F9C\u8F9F\u8FA3\u8FAD\u8FAF\u8FB7" + + "\u8FDA\u8FE5\u8FE2\u8FEA\u8FEF\u9087\u8FF4\u9005" + + "\u8FF9\u8FFA\u9011\u9015\u9021\u900D\u901E\u9016" + + "\u900B\u9027\u9036\u9035\u9039\u8FF8\u904F\u9050" + + "\u9051\u9052\u900E\u9049\u903E\u9056\u9058\u905E" + + "\u9068\u906F\u9076\u96A8\u9072\u9082\u907D\u9081" + + "\u9080\u908A\u9089\u908F\u90A8\u90AF\u90B1\u90B5" + + "\u90E2\u90E4\u6248\u90DB\u9102\u9112\u9119\u9132" + + "\u9130\u914A\u9156\u9158\u9163\u9165\u9169\u9173" + + "\u9172\u918B\u9189\u9182\u91A2\u91AB\u91AF\u91AA" + + "\u91B5\u91B4\u91BA\u91C0\u91C1\u91C9\u91CB\u91D0" + + "\u91D6\u91DF\u91E1\u91DB\u91FC\u91F5\u91F6\u921E" + + "\u91FF\u9214\u922C\u9215\u9211\u925E\u9257\u9245" + + "\u9249\u9264\u9248\u9295\u923F\u924B\u9250\u929C" + + "\u9296\u9293\u929B\u925A\u92CF\u92B9\u92B7\u92E9" + + "\u930F\u92FA\u9344\u932E\u9319\u9322\u931A\u9323" + + "\u933A\u9335\u933B\u935C\u9360\u937C\u936E\u9356" + + "\u93B0\u93AC\u93AD\u9394\u93B9\u93D6\u93D7\u93E8" + + "\u93E5\u93D8\u93C3\u93DD\u93D0\u93C8\u93E4\u941A" + + "\u9414\u9413\u9403\u9407\u9410\u9436\u942B\u9435" + + "\u9421\u943A\u9441\u9452\u9444\u945B\u9460\u9462" + + "\u945E\u946A\u9229\u9470\u9475\u9477\u947D\u945A" + + "\u947C\u947E\u9481\u947F\u9582\u9587\u958A\u9594" + + "\u9596\u9598\u9599\u95A0\u95A8\u95A7\u95AD\u95BC" + + "\u95BB\u95B9\u95BE\u95CA\u6FF6\u95C3\u95CD\u95CC" + + "\u95D5\u95D4\u95D6\u95DC\u95E1\u95E5\u95E2\u9621" + + "\u9628\u962E\u962F\u9642\u964C\u964F\u964B\u9677" + + "\u965C\u965E\u965D\u965F\u9666\u9672\u966C\u968D" + + "\u9698\u9695\u9697\u96AA\u96A7\u96B1\u96B2\u96B0" + + "\u96B4\u96B6\u96B8\u96B9\u96CE\u96CB\u96C9\u96CD" + + "\u894D\u96DC\u970D\u96D5\u96F9\u9704\u9706\u9708" + + "\u9713\u970E\u9711\u970F\u9716\u9719\u9724\u972A" + + "\u9730\u9739\u973D\u973E\u9744\u9746\u9748\u9742" + + "\u9749\u975C\u9760\u9764\u9766\u9768\u52D2\u976B" + + "\u9771\u9779\u9785\u977C\u9781\u977A\u9786\u978B" + + "\u978F\u9790\u979C\u97A8\u97A6\u97A3\u97B3\u97B4" + + "\u97C3\u97C6\u97C8\u97CB\u97DC\u97ED\u9F4F\u97F2" + + "\u7ADF\u97F6\u97F5\u980F\u980C\u9838\u9824\u9821" + + "\u9837\u983D\u9846\u984F\u984B\u986B\u986F\u9870" + + "\u9871\u9874\u9873\u98AA\u98AF\u98B1\u98B6\u98C4" + + "\u98C3\u98C6\u98E9\u98EB\u9903\u9909\u9912\u9914" + + "\u9918\u9921\u991D\u991E\u9924\u9920\u992C\u992E" + + "\u993D\u993E\u9942\u9949\u9945\u9950\u994B\u9951" + + "\u9952\u994C\u9955\u9997\u9998\u99A5\u99AD\u99AE" + + "\u99BC\u99DF\u99DB\u99DD\u99D8\u99D1\u99ED\u99EE" + + "\u99F1\u99F2\u99FB\u99F8\u9A01\u9A0F\u9A05\u99E2" + + "\u9A19\u9A2B\u9A37\u9A45\u9A42\u9A40\u9A43\u9A3E" + + "\u9A55\u9A4D\u9A5B\u9A57\u9A5F\u9A62\u9A65\u9A64" + + "\u9A69\u9A6B\u9A6A\u9AAD\u9AB0\u9ABC\u9AC0\u9ACF" + + "\u9AD1\u9AD3\u9AD4\u9ADE\u9ADF\u9AE2\u9AE3\u9AE6" + + "\u9AEF\u9AEB\u9AEE\u9AF4\u9AF1\u9AF7\u9AFB\u9B06" + + "\u9B18\u9B1A\u9B1F\u9B22\u9B23\u9B25\u9B27\u9B28" + + "\u9B29\u9B2A\u9B2E\u9B2F\u9B32\u9B44\u9B43\u9B4F" + + "\u9B4D\u9B4E\u9B51\u9B58\u9B74\u9B93\u9B83\u9B91" + + "\u9B96\u9B97\u9B9F\u9BA0\u9BA8\u9BB4\u9BC0\u9BCA" + + "\u9BB9\u9BC6\u9BCF\u9BD1\u9BD2\u9BE3\u9BE2\u9BE4" + + "\u9BD4\u9BE1\u9C3A\u9BF2\u9BF1\u9BF0\u9C15\u9C14" + + "\u9C09\u9C13\u9C0C\u9C06\u9C08\u9C12\u9C0A\u9C04" + + "\u9C2E\u9C1B\u9C25\u9C24\u9C21\u9C30\u9C47\u9C32" + + "\u9C46\u9C3E\u9C5A\u9C60\u9C67\u9C76\u9C78\u9CE7" + + "\u9CEC\u9CF0\u9D09\u9D08\u9CEB\u9D03\u9D06\u9D2A" + + "\u9D26\u9DAF\u9D23\u9D1F\u9D44\u9D15\u9D12\u9D41" + + "\u9D3F\u9D3E\u9D46\u9D48\u9D5D\u9D5E\u9D64\u9D51" + + "\u9D50\u9D59\u9D72\u9D89\u9D87\u9DAB\u9D6F\u9D7A" + + "\u9D9A\u9DA4\u9DA9\u9DB2\u9DC4\u9DC1\u9DBB\u9DB8" + + "\u9DBA\u9DC6\u9DCF\u9DC2\u9DD9\u9DD3\u9DF8\u9DE6" + + "\u9DED\u9DEF\u9DFD\u9E1A\u9E1B\u9E1E\u9E75\u9E79" + + "\u9E7D\u9E81\u9E88\u9E8B\u9E8C\u9E92\u9E95\u9E91" + + "\u9E9D\u9EA5\u9EA9\u9EB8\u9EAA\u9EAD\u9761\u9ECC" + + "\u9ECE\u9ECF\u9ED0\u9ED4\u9EDC\u9EDE\u9EDD\u9EE0" + + "\u9EE5\u9EE8\u9EEF\u9EF4\u9EF6\u9EF7\u9EF9\u9EFB" + + "\u9EFC\u9EFD\u9F07\u9F08\u76B7\u9F15\u9F21\u9F2C" + + "\u9F3E\u9F4A\u9F52\u9F54\u9F63\u9F5F\u9F60\u9F61" + + "\u9F66\u9F67\u9F6C\u9F6A\u9F77\u9F72\u9F76\u9F95" + + "\u9F9C\u9FA0\u582F\u69C7\u9059\u7464\u51DC\u7199" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uE000\uE001\uE002\uE003\uE004\uE005\uE006\uE007" + + "\uE008\uE009\uE00A\uE00B\uE00C\uE00D\uE00E\uE00F" + + "\uE010\uE011\uE012\uE013\uE014\uE015\uE016\uE017" + + "\uE018\uE019\uE01A\uE01B\uE01C\uE01D\uE01E\uE01F" + + "\uE020\uE021\uE022\uE023\uE024\uE025\uE026\uE027" + + "\uE028\uE029\uE02A\uE02B\uE02C\uE02D\uE02E\uE02F" + + "\uE030\uE031\uE032\uE033\uE034\uE035\uE036\uE037" + + "\uE038\uE039\uE03A\uE03B\uE03C\uE03D\uE03E\uE03F" + + "\uE040\uE041\uE042\uE043\uE044\uE045\uE046\uE047" + + "\uE048\uE049\uE04A\uE04B\uE04C\uE04D\uE04E\uE04F" + + "\uE050\uE051\uE052\uE053\uE054\uE055\uE056\uE057" + + "\uE058\uE059\uE05A\uE05B\uE05C\uE05D\uE05E\uE05F" + + "\uE060\uE061\uE062\uE063\uE064\uE065\uE066\uE067" + + "\uE068\uE069\uE06A\uE06B\uE06C\uE06D\uE06E\uE06F" + + "\uE070\uE071\uE072\uE073\uE074\uE075\uE076\uE077" + + "\uE078\uE079\uE07A\uE07B\uE07C\uE07D\uE07E\uE07F" + + "\uE080\uE081\uE082\uE083\uE084\uE085\uE086\uE087" + + "\uE088\uE089\uE08A\uE08B\uE08C\uE08D\uE08E\uE08F" + + "\uE090\uE091\uE092\uE093\uE094\uE095\uE096\uE097" + + "\uE098\uE099\uE09A\uE09B\uE09C\uE09D\uE09E\uE09F" + + "\uE0A0\uE0A1\uE0A2\uE0A3\uE0A4\uE0A5\uE0A6\uE0A7" + + "\uE0A8\uE0A9\uE0AA\uE0AB\uE0AC\uE0AD\uE0AE\uE0AF" + + "\uE0B0\uE0B1\uE0B2\uE0B3\uE0B4\uE0B5\uE0B6\uE0B7" + + "\uE0B8\uE0B9\uE0BA\uE0BB\uE0BC\uE0BD\uE0BE\uE0BF" + + "\uE0C0\uE0C1\uE0C2\uE0C3\uE0C4\uE0C5\uE0C6\uE0C7" + + "\uE0C8\uE0C9\uE0CA\uE0CB\uE0CC\uE0CD\uE0CE\uE0CF" + + "\uE0D0\uE0D1\uE0D2\uE0D3\uE0D4\uE0D5\uE0D6\uE0D7" + + "\uE0D8\uE0D9\uE0DA\uE0DB\uE0DC\uE0DD\uE0DE\uE0DF" + + "\uE0E0\uE0E1\uE0E2\uE0E3\uE0E4\uE0E5\uE0E6\uE0E7" + + "\uE0E8\uE0E9\uE0EA\uE0EB\uE0EC\uE0ED\uE0EE\uE0EF" + + "\uE0F0\uE0F1\uE0F2\uE0F3\uE0F4\uE0F5\uE0F6\uE0F7" + + "\uE0F8\uE0F9\uE0FA\uE0FB\uE0FC\uE0FD\uE0FE\uE0FF" + + "\uE100\uE101\uE102\uE103\uE104\uE105\uE106\uE107" + + "\uE108\uE109\uE10A\uE10B\uE10C\uE10D\uE10E\uE10F" + + "\uE110\uE111\uE112\uE113\uE114\uE115\uE116\uE117" + + "\uE118\uE119\uE11A\uE11B\uE11C\uE11D\uE11E\uE11F" + + "\uE120\uE121\uE122\uE123\uE124\uE125\uE126\uE127" + + "\uE128\uE129\uE12A\uE12B\uE12C\uE12D\uE12E\uE12F" + + "\uE130\uE131\uE132\uE133\uE134\uE135\uE136\uE137" + + "\uE138\uE139\uE13A\uE13B\uE13C\uE13D\uE13E\uE13F" + + "\uE140\uE141\uE142\uE143\uE144\uE145\uE146\uE147" + + "\uE148\uE149\uE14A\uE14B\uE14C\uE14D\uE14E\uE14F" + + "\uE150\uE151\uE152\uE153\uE154\uE155\uE156\uE157" + + "\uE158\uE159\uE15A\uE15B\uE15C\uE15D\uE15E\uE15F" + + "\uE160\uE161\uE162\uE163\uE164\uE165\uE166\uE167" + + "\uE168\uE169\uE16A\uE16B\uE16C\uE16D\uE16E\uE16F" + + "\uE170\uE171\uE172\uE173\uE174\uE175\uE176\uE177" + + "\uE178\uE179\uE17A\uE17B\uE17C\uE17D\uE17E\uE17F" + + "\uE180\uE181\uE182\uE183\uE184\uE185\uE186\uE187" + + "\uE188\uE189\uE18A\uE18B\uE18C\uE18D\uE18E\uE18F" + + "\uE190\uE191\uE192\uE193\uE194\uE195\uE196\uE197" + + "\uE198\uE199\uE19A\uE19B\uE19C\uE19D\uE19E\uE19F" + + "\uE1A0\uE1A1\uE1A2\uE1A3\uE1A4\uE1A5\uE1A6\uE1A7" + + "\uE1A8\uE1A9\uE1AA\uE1AB\uE1AC\uE1AD\uE1AE\uE1AF" + + "\uE1B0\uE1B1\uE1B2\uE1B3\uE1B4\uE1B5\uE1B6\uE1B7" + + "\uE1B8\uE1B9\uE1BA\uE1BB\uE1BC\uE1BD\uE1BE\uE1BF" + + "\uE1C0\uE1C1\uE1C2\uE1C3\uE1C4\uE1C5\uE1C6\uE1C7" + + "\uE1C8\uE1C9\uE1CA\uE1CB\uE1CC\uE1CD\uE1CE\uE1CF" + + "\uE1D0\uE1D1\uE1D2\uE1D3\uE1D4\uE1D5\uE1D6\uE1D7" + + "\uE1D8\uE1D9\uE1DA\uE1DB\uE1DC\uE1DD\uE1DE\uE1DF" + + "\uE1E0\uE1E1\uE1E2\uE1E3\uE1E4\uE1E5\uE1E6\uE1E7" + + "\uE1E8\uE1E9\uE1EA\uE1EB\uE1EC\uE1ED\uE1EE\uE1EF" + + "\uE1F0\uE1F1\uE1F2\uE1F3\uE1F4\uE1F5\uE1F6\uE1F7" + + "\uE1F8\uE1F9\uE1FA\uE1FB\uE1FC\uE1FD\uE1FE\uE1FF" + + "\uE200\uE201\uE202\uE203\uE204\uE205\uE206\uE207" + + "\uE208\uE209\uE20A\uE20B\uE20C\uE20D\uE20E\uE20F" + + "\uE210\uE211\uE212\uE213\uE214\uE215\uE216\uE217" + + "\uE218\uE219\uE21A\uE21B\uE21C\uE21D\uE21E\uE21F" + + "\uE220\uE221\uE222\uE223\uE224\uE225\uE226\uE227" + + "\uE228\uE229\uE22A\uE22B\uE22C\uE22D\uE22E\uE22F" + + "\uE230\uE231\uE232\uE233\uE234\uE235\uE236\uE237" + + "\uE238\uE239\uE23A\uE23B\uE23C\uE23D\uE23E\uE23F" + + "\uE240\uE241\uE242\uE243\uE244\uE245\uE246\uE247" + + "\uE248\uE249\uE24A\uE24B\uE24C\uE24D\uE24E\uE24F" + + "\uE250\uE251\uE252\uE253\uE254\uE255\uE256\uE257" + + "\uE258\uE259\uE25A\uE25B\uE25C\uE25D\uE25E\uE25F" + + "\uE260\uE261\uE262\uE263\uE264\uE265\uE266\uE267" + + "\uE268\uE269\uE26A\uE26B\uE26C\uE26D\uE26E\uE26F" + + "\uE270\uE271\uE272\uE273\uE274\uE275\uE276\uE277" + + "\uE278\uE279\uE27A\uE27B\uE27C\uE27D\uE27E\uE27F" + + "\uE280\uE281\uE282\uE283\uE284\uE285\uE286\uE287" + + "\uE288\uE289\uE28A\uE28B\uE28C\uE28D\uE28E\uE28F" + + "\uE290\uE291\uE292\uE293\uE294\uE295\uE296\uE297" + + "\uE298\uE299\uE29A\uE29B\uE29C\uE29D\uE29E\uE29F" + + "\uE2A0\uE2A1\uE2A2\uE2A3\uE2A4\uE2A5\uE2A6\uE2A7" + + "\uE2A8\uE2A9\uE2AA\uE2AB\uE2AC\uE2AD\uE2AE\uE2AF" + + "\uE2B0\uE2B1\uE2B2\uE2B3\uE2B4\uE2B5\uE2B6\uE2B7" + + "\uE2B8\uE2B9\uE2BA\uE2BB\uE2BC\uE2BD\uE2BE\uE2BF" + + "\uE2C0\uE2C1\uE2C2\uE2C3\uE2C4\uE2C5\uE2C6\uE2C7" + + "\uE2C8\uE2C9\uE2CA\uE2CB\uE2CC\uE2CD\uE2CE\uE2CF" + + "\uE2D0\uE2D1\uE2D2\uE2D3\uE2D4\uE2D5\uE2D6\uE2D7" + + "\uE2D8\uE2D9\uE2DA\uE2DB\uE2DC\uE2DD\uE2DE\uE2DF" + + "\uE2E0\uE2E1\uE2E2\uE2E3\uE2E4\uE2E5\uE2E6\uE2E7" + + "\uE2E8\uE2E9\uE2EA\uE2EB\uE2EC\uE2ED\uE2EE\uE2EF" + + "\uE2F0\uE2F1\uE2F2\uE2F3\uE2F4\uE2F5\uE2F6\uE2F7" + + "\uE2F8\uE2F9\uE2FA\uE2FB\uE2FC\uE2FD\uE2FE\uE2FF" + + "\uE300\uE301\uE302\uE303\uE304\uE305\uE306\uE307" + + "\uE308\uE309\uE30A\uE30B\uE30C\uE30D\uE30E\uE30F" + + "\uE310\uE311\uE312\uE313\uE314\uE315\uE316\uE317" + + "\uE318\uE319\uE31A\uE31B\uE31C\uE31D\uE31E\uE31F" + + "\uE320\uE321\uE322\uE323\uE324\uE325\uE326\uE327" + + "\uE328\uE329\uE32A\uE32B\uE32C\uE32D\uE32E\uE32F" + + "\uE330\uE331\uE332\uE333\uE334\uE335\uE336\uE337" + + "\uE338\uE339\uE33A\uE33B\uE33C\uE33D\uE33E\uE33F" + + "\uE340\uE341\uE342\uE343\uE344\uE345\uE346\uE347" + + "\uE348\uE349\uE34A\uE34B\uE34C\uE34D\uE34E\uE34F" + + "\uE350\uE351\uE352\uE353\uE354\uE355\uE356\uE357" + + "\uE358\uE359\uE35A\uE35B\uE35C\uE35D\uE35E\uE35F" + + "\uE360\uE361\uE362\uE363\uE364\uE365\uE366\uE367" + + "\uE368\uE369\uE36A\uE36B\uE36C\uE36D\uE36E\uE36F" + + "\uE370\uE371\uE372\uE373\uE374\uE375\uE376\uE377" + + "\uE378\uE379\uE37A\uE37B\uE37C\uE37D\uE37E\uE37F" + + "\uE380\uE381\uE382\uE383\uE384\uE385\uE386\uE387" + + "\uE388\uE389\uE38A\uE38B\uE38C\uE38D\uE38E\uE38F" + + "\uE390\uE391\uE392\uE393\uE394\uE395\uE396\uE397" + + "\uE398\uE399\uE39A\uE39B\uE39C\uE39D\uE39E\uE39F" + + "\uE3A0\uE3A1\uE3A2\uE3A3\uE3A4\uE3A5\uE3A6\uE3A7" + + "\uE3A8\uE3A9\uE3AA\uE3AB" + ; + mappingTableG2 = + "\uFF61\uFF62\uFF63\uFF64\uFF65\uFF66\uFF67\uFF68" + + "\uFF69\uFF6A\uFF6B\uFF6C\uFF6D\uFF6E\uFF6F\uFF70" + + "\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78" + + "\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F\uFF80" + + "\uFF81\uFF82\uFF83\uFF84\uFF85\uFF86\uFF87\uFF88" + + "\uFF89\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E\uFF8F\uFF90" + + "\uFF91\uFF92\uFF93\uFF94\uFF95\uFF96\uFF97\uFF98" + + "\uFF99\uFF9A\uFF9B\uFF9C\uFF9D\uFF9E\uFF9F\u00A2" + + "\u00A3\u00AC\\\u007E\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + ; + mappingTableG3 = + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u00A6\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u4E28\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u4EE1\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u4F00\uFFFD\u4F03\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u4F39\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u4F56" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u4F8A\uFFFD\uFFFD\uFFFD\u4F92\uFFFD" + + "\u4F94\uFFFD\uFFFD\u4F9A\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u4FC9\uFFFD\uFFFD\u4FCD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u4FFF" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u501E\u5022" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5040\uFFFD" + + "\u5042\uFFFD\u5046\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u5070\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u5094\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u50D8\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u514A\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u5164\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u519D\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u51BE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u5215\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u529C\uFFFD\uFFFD\u52A6\uFFFD" + + "\u52AF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u52C0\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u52DB\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5300\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u5372\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u5393\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u53B2\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u53DD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u549C\uFFFD\uFFFD\uFFFD\uFFFD\u54A9\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u54FF\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5586" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5765\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u57AC" + + "\uFFFD\uFFFD\u57C7\u57C8\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u58B2" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u590B\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5953\uFFFD" + + "\u595B\u595D\uFFFD\uFFFD\uFFFD\u5963\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u59A4\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u59BA\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u5B56\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u5BC0\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5BD8\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u5C1E\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5CA6\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u5CBA\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5D27\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u5D42\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u5D6D\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5DB8" + + "\u5DB9\uFFFD\uFFFD\uFFFD\u5DD0\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5F21\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5F34\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5F45\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u5F67" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u5FDE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u605D\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u608A" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u60D5\uFFFD\uFFFD\uFFFD\u60DE\uFFFD\uFFFD" + + "\u60F2\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u6111\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6130\uFFFD" + + "\uFFFD\uFFFD\u6137\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u6198\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u6213\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u62A6\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u63F5\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6460\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u649D\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u64CE\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6600\uFFFD\uFFFD\u6609" + + "\uFFFD\uFFFD\uFFFD\u6615\uFFFD\uFFFD\u661E\uFFFD" + + "\uFFFD\uFFFD\u6624\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u662E\uFFFD\u6631\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6657\uFFFD\u6659" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u66FB\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u6673\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6699" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u66A0\uFFFD\uFFFD\uFFFD" + + "\u66B2\uFFFD\uFFFD\u66BF\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u66FA\uFFFD\uFFFD\u670E\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6766\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u67BB\uFFFD\uFFFD\uFFFD\u67C0\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6852\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u6844\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u68C8" + + "\uFFFD\u68CF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6968\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u6998\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u69E2\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6A30\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6A46\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6A73\u6A7E\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6AE4\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6BD6\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6C3F\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6C5C\uFFFD\uFFFD" + + "\u6C6F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6C86" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6CDA\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6D04\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6D6F\uFFFD" + + "\uFFFD\uFFFD\u6D87\uFFFD\uFFFD\uFFFD\u6D96\uFFFD" + + "\uFFFD\uFFFD\u6DAC\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u6DCF\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u6DFC\uFFFD\uFFFD\uFFFD\uFFFD\u6E27\uFFFD" + + "\uFFFD\u6E39\uFFFD\u6E3C\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6E5C" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u6EBF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6F88\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u6FB5\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u6FF5\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u7005\uFFFD\u7007\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u7085\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u70AB\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u7104\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u710F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u7146\u7147\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u715C\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u71C1\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u71FE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u72B1\uFFFD\u72BE\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u7324\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7377\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u73BD\uFFFD\uFFFD\uFFFD\u73C9\uFFFD\uFFFD" + + "\uFFFD\u73D2\uFFFD\u73D6\uFFFD\uFFFD\uFFFD\u73E3" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u73F5\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7407\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u7426\uFFFD\u7429\u742A" + + "\uFFFD\uFFFD\uFFFD\u742E\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u7462\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u7489\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u749F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u752F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u756F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u769B" + + "\u769C\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u76A6\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7746" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u7821\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u784E\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u7864\uFFFD\uFFFD\uFFFD" + + "\u787A\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7994\uFFFD" + + "\uFFFD\uFFFD\u799B\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7AD1" + + "\uFFFD\uFFFD\uFFFD\u7AEB\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u7B9E\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u7D48\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u7D5C\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u7DB7\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7E52\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u7E8A\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u7F47\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u7FA1\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u8301\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u837F\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u83C7\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u83F6\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u8448\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u84B4\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u84DC\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u8553\uFFFD\u8559" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u856B\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u88F5\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u891C\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u8A12" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u8A37\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u8A79\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u8AA7\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u8ABE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u8ADF\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u8AF6\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u8B53\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u8CF0\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u8D12\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u8ECF\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u9067\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u9127\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u91D7\uFFFD\u91DA\u91DE\u91E4\u91E5\uFFFD" + + "\uFFFD\uFFFD\u91ED\u91EE\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9206" + + "\uFFFD\uFFFD\u920A\uFFFD\u9210\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9239" + + "\u923A\u923C\uFFFD\u9240\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u924E\uFFFD\u9251\uFFFD\u9259\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u9267\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u9277\u9278\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u9288\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u92A7" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u92D0" + + "\u92D3\u92D5\u92D7\uFFFD\u92D9\uFFFD\uFFFD\uFFFD" + + "\u92E0\uFFFD\uFFFD\uFFFD\u92E7\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u92F9\u92FB\u92FF\uFFFD\u9302\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u931D\u931E\uFFFD\u9321" + + "\uFFFD\u9325\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u9348\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u9357\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u9370\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u93A4\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u93C6\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u93DE\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u93F8" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u9431\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\u9445\u9448\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u969D\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u96AF\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9733" + + "\uFFFD\u9743\uFFFD\uFFFD\u974F\u9755\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u9857\uFFFD\uFFFD\uFFFD\uFFFD\u9865" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9927\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u9A4E\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u9ADC\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u9B75\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u9B8F\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\u9BB1\uFFFD\uFFFD\uFFFD\u9BBB" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\u9C00\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9D6B\u9D70\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u9E19" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u2170\u2171\u2172\u2173" + + "\u2174\u2175\u2176\u2177\u2178\u2179\u2160\u2161" + + "\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169" + + "\uFF07\uFF02\u3231\u2116\u2121\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u70BB\u4EFC\u50F4\u51EC\u5307\u5324" + + "\uFA0E\u548A\u5759\uFA0F\uFA10\u589E\uFFFD\u5BEC" + + "\u5CF5\u5D53\uFA11\u5FB7\u6085\u6120\u654E\u663B" + + "\u6665\uFA12\uF929\u6801\uFA13\uFA14\u6A6B\u6AE2" + + "\u6DF8\u6DF2\uFFFD\u7028\uFFFD\uFA15\uFA16\u7501" + + "\u7682\u769E\uFA17\uFFFD\u7930\uFA18\uFA19\uFA1A" + + "\uFA1B\u7AE7\uFA1C\uFFFD\uFA1D\u7DA0\u7DD6\uFA1E" + + "\u8362\uFA1F\u85B0\uFA20\uFA21\u8807\uFFFD\uFA22" + + "\u8B7F\u8CF4\u8D76\uFA23\uFA24\uFA25\u90DE\uFA26" + + "\u9115\uFA27\uFA28\u9592\uF9DC\uFA29\u973B\u974D" + + "\u9751\uFFFD\uFA2A\uFA2B\uFA2C\u999E\u9AD9\u9B72" + + "\uFA2D\u9ED1\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uE3AC\uE3AD\uE3AE\uE3AF\uE3B0\uE3B1\uE3B2\uE3B3" + + "\uE3B4\uE3B5\uE3B6\uE3B7\uE3B8\uE3B9\uE3BA\uE3BB" + + "\uE3BC\uE3BD\uE3BE\uE3BF\uE3C0\uE3C1\uE3C2\uE3C3" + + "\uE3C4\uE3C5\uE3C6\uE3C7\uE3C8\uE3C9\uE3CA\uE3CB" + + "\uE3CC\uE3CD\uE3CE\uE3CF\uE3D0\uE3D1\uE3D2\uE3D3" + + "\uE3D4\uE3D5\uE3D6\uE3D7\uE3D8\uE3D9\uE3DA\uE3DB" + + "\uE3DC\uE3DD\uE3DE\uE3DF\uE3E0\uE3E1\uE3E2\uE3E3" + + "\uE3E4\uE3E5\uE3E6\uE3E7\uE3E8\uE3E9\uE3EA\uE3EB" + + "\uE3EC\uE3ED\uE3EE\uE3EF\uE3F0\uE3F1\uE3F2\uE3F3" + + "\uE3F4\uE3F5\uE3F6\uE3F7\uE3F8\uE3F9\uE3FA\uE3FB" + + "\uE3FC\uE3FD\uE3FE\uE3FF\uE400\uE401\uE402\uE403" + + "\uE404\uE405\uE406\uE407\uE408\uE409\uE40A\uE40B" + + "\uE40C\uE40D\uE40E\uE40F\uE410\uE411\uE412\uE413" + + "\uE414\uE415\uE416\uE417\uE418\uE419\uE41A\uE41B" + + "\uE41C\uE41D\uE41E\uE41F\uE420\uE421\uE422\uE423" + + "\uE424\uE425\uE426\uE427\uE428\uE429\uE42A\uE42B" + + "\uE42C\uE42D\uE42E\uE42F\uE430\uE431\uE432\uE433" + + "\uE434\uE435\uE436\uE437\uE438\uE439\uE43A\uE43B" + + "\uE43C\uE43D\uE43E\uE43F\uE440\uE441\uE442\uE443" + + "\uE444\uE445\uE446\uE447\uE448\uE449\uE44A\uE44B" + + "\uE44C\uE44D\uE44E\uE44F\uE450\uE451\uE452\uE453" + + "\uE454\uE455\uE456\uE457\uE458\uE459\uE45A\uE45B" + + "\uE45C\uE45D\uE45E\uE45F\uE460\uE461\uE462\uE463" + + "\uE464\uE465\uE466\uE467\uE468\uE469\uE46A\uE46B" + + "\uE46C\uE46D\uE46E\uE46F\uE470\uE471\uE472\uE473" + + "\uE474\uE475\uE476\uE477\uE478\uE479\uE47A\uE47B" + + "\uE47C\uE47D\uE47E\uE47F\uE480\uE481\uE482\uE483" + + "\uE484\uE485\uE486\uE487\uE488\uE489\uE48A\uE48B" + + "\uE48C\uE48D\uE48E\uE48F\uE490\uE491\uE492\uE493" + + "\uE494\uE495\uE496\uE497\uE498\uE499\uE49A\uE49B" + + "\uE49C\uE49D\uE49E\uE49F\uE4A0\uE4A1\uE4A2\uE4A3" + + "\uE4A4\uE4A5\uE4A6\uE4A7\uE4A8\uE4A9\uE4AA\uE4AB" + + "\uE4AC\uE4AD\uE4AE\uE4AF\uE4B0\uE4B1\uE4B2\uE4B3" + + "\uE4B4\uE4B5\uE4B6\uE4B7\uE4B8\uE4B9\uE4BA\uE4BB" + + "\uE4BC\uE4BD\uE4BE\uE4BF\uE4C0\uE4C1\uE4C2\uE4C3" + + "\uE4C4\uE4C5\uE4C6\uE4C7\uE4C8\uE4C9\uE4CA\uE4CB" + + "\uE4CC\uE4CD\uE4CE\uE4CF\uE4D0\uE4D1\uE4D2\uE4D3" + + "\uE4D4\uE4D5\uE4D6\uE4D7\uE4D8\uE4D9\uE4DA\uE4DB" + + "\uE4DC\uE4DD\uE4DE\uE4DF\uE4E0\uE4E1\uE4E2\uE4E3" + + "\uE4E4\uE4E5\uE4E6\uE4E7\uE4E8\uE4E9\uE4EA\uE4EB" + + "\uE4EC\uE4ED\uE4EE\uE4EF\uE4F0\uE4F1\uE4F2\uE4F3" + + "\uE4F4\uE4F5\uE4F6\uE4F7\uE4F8\uE4F9\uE4FA\uE4FB" + + "\uE4FC\uE4FD\uE4FE\uE4FF\uE500\uE501\uE502\uE503" + + "\uE504\uE505\uE506\uE507\uE508\uE509\uE50A\uE50B" + + "\uE50C\uE50D\uE50E\uE50F\uE510\uE511\uE512\uE513" + + "\uE514\uE515\uE516\uE517\uE518\uE519\uE51A\uE51B" + + "\uE51C\uE51D\uE51E\uE51F\uE520\uE521\uE522\uE523" + + "\uE524\uE525\uE526\uE527\uE528\uE529\uE52A\uE52B" + + "\uE52C\uE52D\uE52E\uE52F\uE530\uE531\uE532\uE533" + + "\uE534\uE535\uE536\uE537\uE538\uE539\uE53A\uE53B" + + "\uE53C\uE53D\uE53E\uE53F\uE540\uE541\uE542\uE543" + + "\uE544\uE545\uE546\uE547\uE548\uE549\uE54A\uE54B" + + "\uE54C\uE54D\uE54E\uE54F\uE550\uE551\uE552\uE553" + + "\uE554\uE555\uE556\uE557\uE558\uE559\uE55A\uE55B" + + "\uE55C\uE55D\uE55E\uE55F\uE560\uE561\uE562\uE563" + + "\uE564\uE565\uE566\uE567\uE568\uE569\uE56A\uE56B" + + "\uE56C\uE56D\uE56E\uE56F\uE570\uE571\uE572\uE573" + + "\uE574\uE575\uE576\uE577\uE578\uE579\uE57A\uE57B" + + "\uE57C\uE57D\uE57E\uE57F\uE580\uE581\uE582\uE583" + + "\uE584\uE585\uE586\uE587\uE588\uE589\uE58A\uE58B" + + "\uE58C\uE58D\uE58E\uE58F\uE590\uE591\uE592\uE593" + + "\uE594\uE595\uE596\uE597\uE598\uE599\uE59A\uE59B" + + "\uE59C\uE59D\uE59E\uE59F\uE5A0\uE5A1\uE5A2\uE5A3" + + "\uE5A4\uE5A5\uE5A6\uE5A7\uE5A8\uE5A9\uE5AA\uE5AB" + + "\uE5AC\uE5AD\uE5AE\uE5AF\uE5B0\uE5B1\uE5B2\uE5B3" + + "\uE5B4\uE5B5\uE5B6\uE5B7\uE5B8\uE5B9\uE5BA\uE5BB" + + "\uE5BC\uE5BD\uE5BE\uE5BF\uE5C0\uE5C1\uE5C2\uE5C3" + + "\uE5C4\uE5C5\uE5C6\uE5C7\uE5C8\uE5C9\uE5CA\uE5CB" + + "\uE5CC\uE5CD\uE5CE\uE5CF\uE5D0\uE5D1\uE5D2\uE5D3" + + "\uE5D4\uE5D5\uE5D6\uE5D7\uE5D8\uE5D9\uE5DA\uE5DB" + + "\uE5DC\uE5DD\uE5DE\uE5DF\uE5E0\uE5E1\uE5E2\uE5E3" + + "\uE5E4\uE5E5\uE5E6\uE5E7\uE5E8\uE5E9\uE5EA\uE5EB" + + "\uE5EC\uE5ED\uE5EE\uE5EF\uE5F0\uE5F1\uE5F2\uE5F3" + + "\uE5F4\uE5F5\uE5F6\uE5F7\uE5F8\uE5F9\uE5FA\uE5FB" + + "\uE5FC\uE5FD\uE5FE\uE5FF\uE600\uE601\uE602\uE603" + + "\uE604\uE605\uE606\uE607\uE608\uE609\uE60A\uE60B" + + "\uE60C\uE60D\uE60E\uE60F\uE610\uE611\uE612\uE613" + + "\uE614\uE615\uE616\uE617\uE618\uE619\uE61A\uE61B" + + "\uE61C\uE61D\uE61E\uE61F\uE620\uE621\uE622\uE623" + + "\uE624\uE625\uE626\uE627\uE628\uE629\uE62A\uE62B" + + "\uE62C\uE62D\uE62E\uE62F\uE630\uE631\uE632\uE633" + + "\uE634\uE635\uE636\uE637\uE638\uE639\uE63A\uE63B" + + "\uE63C\uE63D\uE63E\uE63F\uE640\uE641\uE642\uE643" + + "\uE644\uE645\uE646\uE647\uE648\uE649\uE64A\uE64B" + + "\uE64C\uE64D\uE64E\uE64F\uE650\uE651\uE652\uE653" + + "\uE654\uE655\uE656\uE657\uE658\uE659\uE65A\uE65B" + + "\uE65C\uE65D\uE65E\uE65F\uE660\uE661\uE662\uE663" + + "\uE664\uE665\uE666\uE667\uE668\uE669\uE66A\uE66B" + + "\uE66C\uE66D\uE66E\uE66F\uE670\uE671\uE672\uE673" + + "\uE674\uE675\uE676\uE677\uE678\uE679\uE67A\uE67B" + + "\uE67C\uE67D\uE67E\uE67F\uE680\uE681\uE682\uE683" + + "\uE684\uE685\uE686\uE687\uE688\uE689\uE68A\uE68B" + + "\uE68C\uE68D\uE68E\uE68F\uE690\uE691\uE692\uE693" + + "\uE694\uE695\uE696\uE697\uE698\uE699\uE69A\uE69B" + + "\uE69C\uE69D\uE69E\uE69F\uE6A0\uE6A1\uE6A2\uE6A3" + + "\uE6A4\uE6A5\uE6A6\uE6A7\uE6A8\uE6A9\uE6AA\uE6AB" + + "\uE6AC\uE6AD\uE6AE\uE6AF\uE6B0\uE6B1\uE6B2\uE6B3" + + "\uE6B4\uE6B5\uE6B6\uE6B7\uE6B8\uE6B9\uE6BA\uE6BB" + + "\uE6BC\uE6BD\uE6BE\uE6BF\uE6C0\uE6C1\uE6C2\uE6C3" + + "\uE6C4\uE6C5\uE6C6\uE6C7\uE6C8\uE6C9\uE6CA\uE6CB" + + "\uE6CC\uE6CD\uE6CE\uE6CF\uE6D0\uE6D1\uE6D2\uE6D3" + + "\uE6D4\uE6D5\uE6D6\uE6D7\uE6D8\uE6D9\uE6DA\uE6DB" + + "\uE6DC\uE6DD\uE6DE\uE6DF\uE6E0\uE6E1\uE6E2\uE6E3" + + "\uE6E4\uE6E5\uE6E6\uE6E7\uE6E8\uE6E9\uE6EA\uE6EB" + + "\uE6EC\uE6ED\uE6EE\uE6EF\uE6F0\uE6F1\uE6F2\uE6F3" + + "\uE6F4\uE6F5\uE6F6\uE6F7\uE6F8\uE6F9\uE6FA\uE6FB" + + "\uE6FC\uE6FD\uE6FE\uE6FF\uE700\uE701\uE702\uE703" + + "\uE704\uE705\uE706\uE707\uE708\uE709\uE70A\uE70B" + + "\uE70C\uE70D\uE70E\uE70F\uE710\uE711\uE712\uE713" + + "\uE714\uE715\uE716\uE717\uE718\uE719\uE71A\uE71B" + + "\uE71C\uE71D\uE71E\uE71F\uE720\uE721\uE722\uE723" + + "\uE724\uE725\uE726\uE727\uE728\uE729\uE72A\uE72B" + + "\uE72C\uE72D\uE72E\uE72F\uE730\uE731\uE732\uE733" + + "\uE734\uE735\uE736\uE737\uE738\uE739\uE73A\uE73B" + + "\uE73C\uE73D\uE73E\uE73F\uE740\uE741\uE742\uE743" + + "\uE744\uE745\uE746\uE747\uE748\uE749\uE74A\uE74B" + + "\uE74C\uE74D\uE74E\uE74F\uE750\uE751\uE752\uE753" + + "\uE754\uE755\uE756\uE757" + ; + } + } + + protected static class Encoder extends SimpleEUCEncoder { + + public Encoder(Charset cs) { + super(cs); + super.mask1 = 0xFFE0; + super.mask2 = 0x001F; + super.shift = 5; + super.index1 = index1; + super.index2 = index2; + super.index2a = index2a; + super.index2b = index2b; + } + + private static final short index1[] = + { + 3780, 20704, 20672, 20640, 20608, 6664, 21617, 3165, // 0000 - 00FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0100 - 01FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0200 - 02FF + 2344, 2344, 2344, 2344, 7094, 20576, 20503, 2344, // 0300 - 03FF + 21069, 20471, 20344, 2344, 2344, 2344, 2344, 2344, // 0400 - 04FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0500 - 05FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0600 - 06FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0700 - 07FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0800 - 08FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0900 - 09FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0A00 - 0AFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0B00 - 0BFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0C00 - 0CFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0D00 - 0DFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0E00 - 0EFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 0F00 - 0FFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1000 - 10FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1100 - 11FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1200 - 12FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1300 - 13FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1400 - 14FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1500 - 15FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1600 - 16FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1700 - 17FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1800 - 18FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1900 - 19FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1A00 - 1AFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1B00 - 1BFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1C00 - 1CFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1D00 - 1DFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1E00 - 1EFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 1F00 - 1FFF + 3749, 20254, 2344, 2344, 2344, 2344, 2344, 2344, // 2000 - 20FF + 3495, 2436, 2344, 20107, 14767, 2344, 3978, 2344, // 2100 - 21FF + 20044, 19924, 14282, 19701, 5959, 21608, 2344, 2344, // 2200 - 22FF + 2450, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2300 - 23FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2400 - 24FF + 19527, 19495, 7079, 2344, 2344, 19433, 3733, 14751, // 2500 - 25FF + 5996, 2344, 19279, 14734, 2344, 2344, 2344, 2344, // 2600 - 26FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2700 - 27FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2800 - 28FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2900 - 29FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2A00 - 2AFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2B00 - 2BFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2C00 - 2CFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2D00 - 2DFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2E00 - 2EFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 2F00 - 2FFF + 19157, 2344, 2765, 19125, 19062, 21728, 19030, 18943, // 3000 - 30FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3100 - 31FF + 2344, 4159, 2344, 2344, 2344, 2344, 2344, 2344, // 3200 - 32FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3300 - 33FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3400 - 34FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3500 - 35FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3600 - 36FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3700 - 37FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3800 - 38FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3900 - 39FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3A00 - 3AFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3B00 - 3BFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3C00 - 3CFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3D00 - 3DFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3E00 - 3EFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 3F00 - 3FFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4000 - 40FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4100 - 41FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4200 - 42FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4300 - 43FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4400 - 44FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4500 - 45FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4600 - 46FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4700 - 47FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4800 - 48FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4900 - 49FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4A00 - 4AFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4B00 - 4BFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4C00 - 4CFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // 4D00 - 4DFF + 18852, 2674, 7017, 6358, 18820, 18761, 18729, 387, // 4E00 - 4EFF + 18642, 20379, 5539, 6077, 4556, 270, 5201, 18610, // 4F00 - 4FFF + 18788, 104, 18549, 18430, 18403, 14787, 5327, 21326, // 5000 - 50FF + 18371, 20544, 18279, 7861, 18217, 18061, 14682, 17938, // 5100 - 51FF + 17876, 21297, 6603, 3582, 4236, 17813, 17781, 17749, // 5200 - 52FF + 17544, 17512, 17449, 17417, 8115, 17357, 7459, 20285, // 5300 - 53FF + 20222, 17294, 17262, 11197, 17201, 4721, 17144, 20075, // 5400 - 54FF + 4322, 5045, 17081, 3125, 17049, 6054, 2911, 3033, // 5500 - 55FF + 2823, 10834, 21384, 20764, 16960, 16816, 16753, 16721, // 5600 - 56FF + 16689, 20133, 16631, 20012, 20970, 16520, 16488, 16372, // 5700 - 57FF + 16224, 19774, 15993, 19370, 2972, 11845, 19401, 2734, // 5800 - 58FF + 3935, 3842, 20161, 15752, 19217, 2852, 21667, 20312, // 5900 - 59FF + 19093, 15691, 15632, 3644, 19715, 2348, 18974, 18883, // 5A00 - 5AFF + 10811, 21238, 15600, 19312, 15568, 21549, 15536, 18339, // 5B00 - 5BFF + 18092, 15504, 15350, 15261, 20990, 18123, 20791, 15095, // 5C00 - 5CFF + 17169, 4616, 20847, 10788, 21697, 356, 17625, 18154, // 5D00 - 5DFF + 20409, 21038, 15063, 18185, 17969, 15006, 18029, 14974, // 5E00 - 5EFF + 17844, 17656, 17687, 17480, 14850, 14595, 19186, 14563, // 5F00 - 5FFF + 4177, 14443, 17325, 14244, 17112, 14153, 15659, 14093, // 6000 - 60FF + 14061, 14029, 20439, 20191, 19954, 19743, 416, 18578, // 6100 - 61FF + 13997, 17017, 13873, 13841, 13778, 130, 19557, 13746, // 6200 - 62FF + 16784, 15031, 12531, 19612, 13714, 13651, 13591, 16285, // 6300 - 63FF + 21427, 16986, 19587, 13532, 18308, 21354, 16024, 13469, // 6400 - 64FF + 13318, 19463, 19339, 18672, 160, 16108, 15873, 13258, // 6500 - 65FF + 13226, 13194, 15961, 18460, 15381, 13162, 15442, 13130, // 6600 - 66FF + 13098, 16657, 15318, 13066, 14470, 13006, 12943, 18517, // 6700 - 67FF + 14881, 14912, 12911, 7890, 14818, 12759, 17385, 12643, // 6800 - 68FF + 12292, 14531, 8683, 12111, 14362, 12079, 13904, 18247, // 6900 - 69FF + 17906, 13935, 18911, 13809, 11934, 11793, 13682, 17574, // 6A00 - 6AFF + 14121, 11730, 17230, 13500, 11638, 13619, 11606, 3999, // 6B00 - 6BFF + 9167, 21456, 11574, 11511, 13379, 12974, 12819, 12434, // 6C00 - 6CFF + 16340, 19981, 12142, 16845, 16312, 16540, 11417, 12231, // 6D00 - 6DFF + 13559, 11354, 16928, 18697, 11322, 16599, 16254, 4019, // 6E00 - 6EFF + 11994, 11290, 12047, 19640, 11173, 11876, 11141, 11016, // 6F00 - 6FFF + 11761, 15287, 12454, 16569, 16051, 3553, 8959, 239, // 7000 - 70FF + 15901, 11669, 20817, 19804, 13286, 10102, 11542, 10984, // 7100 - 71FF + 16871, 3189, 10952, 11448, 10765, 10648, 19834, 10586, // 7200 - 72FF + 21145, 13034, 11821, 16401, 17602, 9218, 10496, 10407, // 7300 - 73FF + 3964, 19892, 11479, 10285, 15720, 15782, 8085, 10197, // 7400 - 74FF + 11385, 20876, 13407, 10165, 15812, 20905, 15842, 19247, // 7500 - 75FF + 11258, 9966, 15125, 11047, 9903, 11902, 17717, 11078, // 7600 - 76FF + 11109, 9719, 9659, 10920, 4838, 9627, 12479, 14625, // 7700 - 77FF + 14712, 9595, 18487, 16899, 10679, 17998, 10527, 10859, // 7800 - 78FF + 10438, 16077, 9563, 9531, 9376, 10464, 9344, 10316, // 7900 - 79FF + 9312, 9280, 9143, 10375, 10253, 2548, 19669, 9111, // 7A00 - 7AFF + 14500, 9053, 12670, 10706, 9079, 10025, 8991, 8935, // 7B00 - 7BFF + 8903, 10133, 8871, 8778, 9934, 9779, 8746, 8630, // 7C00 - 7CFF + 8511, 8479, 14331, 9810, 15229, 8447, 10222, 8327, // 7D00 - 7DFF + 9871, 9437, 9468, 9499, 14183, 2344, 2344, 2344, // 7E00 - 7EFF + 2344, 5969, 10733, 8269, 13348, 8809, 8661, 8208, // 7F00 - 7FFF + 8147, 8714, 8295, 8415, 19862, 7747, 15930, 7630, // 8000 - 80FF + 16138, 12788, 5690, 10554, 7986, 7954, 7922, 7599, // 8100 - 81FF + 7278, 21641, 7567, 15411, 455, 7774, 6809, 5631, // 8200 - 82FF + 6634, 12321, 7491, 15472, 7153, 7399, 6872, 7310, // 8300 - 83FF + 14391, 7217, 12347, 6782, 14942, 6264, 5929, 14411, // 8400 - 84FF + 7185, 5570, 7126, 11963, 7049, 16429, 6514, 9994, // 8500 - 85FF + 12700, 13965, 6923, 10050, 21123, 12508, 9687, 10344, // 8600 - 86FF + 6987, 16456, 4343, 6955, 13437, 12849, 6904, 6841, // 8700 - 87FF + 12727, 3466, 6751, 6389, 6233, 12879, 6170, 5898, // 8800 - 88FF + 11227, 5510, 4869, 6719, 5483, 5358, 6453, 12560, // 8900 - 89FF + 6421, 12171, 3435, 6328, 10889, 6296, 10616, 6202, // 8A00 - 8AFF + 6109, 5867, 4963, 12611, 5754, 2344, 2344, 2344, // 8B00 - 8BFF + 2344, 9, 4790, 5171, 9406, 5722, 5452, 5390, // 8C00 - 8CFF + 9195, 2344, 2344, 10078, 4469, 12200, 9248, 5076, // 8D00 - 8DFF + 5835, 8063, 9021, 5297, 3378, 4932, 5265, 9840, // 8E00 - 8EFF + 12260, 4495, 8839, 4752, 3500, 11698, 7804, 7660, // 8F00 - 8FFF + 5233, 5140, 4438, 5108, 5027, 15154, 3347, 4114, // 9000 - 90FF + 7690, 7516, 6029, 8016, 7429, 7340, 4995, 3873, // 9100 - 91FF + 3404, 18998, 4901, 7247, 4691, 12585, 21401, 4659, // 9200 - 92FF + 5600, 4527, 8355, 4407, 21011, 16166, 9748, 3707, // 9300 - 93FF + 14654, 21269, 3316, 4375, 20936, 2344, 2344, 2344, // 9400 - 94FF + 2344, 2344, 2344, 6005, 4268, 4083, 8540, 3156, // 9500 - 95FF + 20940, 3064, 4051, 6544, 5781, 3905, 3812, 6574, // 9600 - 96FF + 3676, 5659, 6483, 3614, 21208, 3532, 8569, 7535, // 9700 - 97FF + 2883, 21519, 7716, 4817, 2344, 2525, 8598, 6139, // 9800 - 98FF + 8384, 3285, 5811, 2344, 20513, 7831, 2611, 5420, // 9900 - 99FF + 2379, 12016, 3253, 4144, 2344, 326, 3221, 4207, // 9A00 - 9AFF + 16192, 4586, 20736, 433, 14212, 3096, 3004, 21101, // 9B00 - 9BFF + 2943, 21177, 4294, 2797, 2344, 2344, 2344, 15206, // 9C00 - 9CFF + 8237, 8176, 21488, 2706, 15179, 12375, 21581, 2405, // 9D00 - 9DFF + 4759, 2344, 2344, 4627, 21760, 7367, 12403, 2643, // 9E00 - 9EFF + 8041, 2580, 6687, 2501, 14301, 2343, 2344, 2344, // 9F00 - 9FFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A000 - A0FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A100 - A1FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A200 - A2FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A300 - A3FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A400 - A4FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A500 - A5FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A600 - A6FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A700 - A7FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A800 - A8FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // A900 - A9FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AA00 - AAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AB00 - ABFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AC00 - ACFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AD00 - ADFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AE00 - AEFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // AF00 - AFFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B000 - B0FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B100 - B1FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B200 - B2FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B300 - B3FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B400 - B4FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B500 - B5FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B600 - B6FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B700 - B7FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B800 - B8FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // B900 - B9FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BA00 - BAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BB00 - BBFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BC00 - BCFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BD00 - BDFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BE00 - BEFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // BF00 - BFFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C000 - C0FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C100 - C1FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C200 - C2FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C300 - C3FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C400 - C4FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C500 - C5FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C600 - C6FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C700 - C7FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C800 - C8FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // C900 - C9FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CA00 - CAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CB00 - CBFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CC00 - CCFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CD00 - CDFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CE00 - CEFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // CF00 - CFFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D000 - D0FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D100 - D1FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D200 - D2FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D300 - D3FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D400 - D4FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D500 - D5FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D600 - D6FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D700 - D7FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D800 - D8FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // D900 - D9FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DA00 - DAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DB00 - DBFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DC00 - DCFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DD00 - DDFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DE00 - DEFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // DF00 - DFFF + 2311, 2279, 2247, 2215, 2183, 2151, 2119, 2087, // E000 - E0FF + 2055, 2023, 1991, 1959, 1927, 1895, 1863, 1831, // E100 - E1FF + 1799, 1767, 1735, 1703, 1671, 1639, 1607, 1575, // E200 - E2FF + 1543, 1511, 1479, 1447, 1415, 1383, 1351, 1319, // E300 - E3FF + 1287, 1255, 1223, 1191, 1159, 1127, 1095, 1063, // E400 - E4FF + 1031, 999, 967, 935, 903, 871, 839, 807, // E500 - E5FF + 775, 743, 711, 679, 647, 615, 583, 551, // E600 - E6FF + 519, 487, 302, 2344, 2344, 2344, 2344, 2344, // E700 - E7FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // E800 - E8FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // E900 - E9FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // EA00 - EAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // EB00 - EBFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // EC00 - ECFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // ED00 - EDFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // EE00 - EEFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // EF00 - EFFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F000 - F0FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F100 - F1FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F200 - F2FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F300 - F3FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F400 - F4FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F500 - F5FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F600 - F6FF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // F700 - F7FF + 2344, 2344, 2344, 20362, 2344, 2344, 2344, 2344, // F800 - F8FF + 2344, 14268, 2344, 2344, 2344, 2344, 19283, 2344, // F900 - F9FF + 2469, 224, 2344, 2344, 2344, 2344, 2344, 2344, // FA00 - FAFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // FB00 - FBFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // FC00 - FCFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // FD00 - FDFF + 2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344, // FE00 - FEFF + 21792, 192, 73, 21824, 41, 2344, 2344, 0, + }; + + private final static String index2; + private final static String index2a; + private final static String index2b; + static { + index2 = + "\u0000\uA1F1\u0000\uA1F2\u0000\uA2CC\u0000\uA1B1\u0000\uA2C3" + // 0 - 4 + "\u0000\uA1EF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5 - 9 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10 - 14 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15 - 19 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20 - 24 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25 - 29 + "\u0000\u0000\u0000\u0000\u0000\uC3AB\u0000\u0000\u0000\u0000" + // 30 - 34 + "\u0000\uECAE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 35 - 39 + "\u0000\uECB0\u0000\u8EC0\u0000\u8EC1\u0000\u8EC2\u0000\u8EC3" + // 40 - 44 + "\u0000\u8EC4\u0000\u8EC5\u0000\u8EC6\u0000\u8EC7\u0000\u8EC8" + // 45 - 49 + "\u0000\u8EC9\u0000\u8ECA\u0000\u8ECB\u0000\u8ECC\u0000\u8ECD" + // 50 - 54 + "\u0000\u8ECE\u0000\u8ECF\u0000\u8ED0\u0000\u8ED1\u0000\u8ED2" + // 55 - 59 + "\u0000\u8ED3\u0000\u8ED4\u0000\u8ED5\u0000\u8ED6\u0000\u8ED7" + // 60 - 64 + "\u0000\u8ED8\u0000\u8ED9\u0000\u8EDA\u0000\u8EDB\u0000\u8EDC" + // 65 - 69 + "\u0000\u8EDD\u0000\u8EDE\u0000\u8EDF\u0000\uA1AE\u0000\uA3E1" + // 70 - 74 + "\u0000\uA3E2\u0000\uA3E3\u0000\uA3E4\u0000\uA3E5\u0000\uA3E6" + // 75 - 79 + "\u0000\uA3E7\u0000\uA3E8\u0000\uA3E9\u0000\uA3EA\u0000\uA3EB" + // 80 - 84 + "\u0000\uA3EC\u0000\uA3ED\u0000\uA3EE\u0000\uA3EF\u0000\uA3F0" + // 85 - 89 + "\u0000\uA3F1\u0000\uA3F2\u0000\uA3F3\u0000\uA3F4\u0000\uA3F5" + // 90 - 94 + "\u0000\uA3F6\u0000\uA3F7\u0000\uA3F8\u0000\uA3F9\u0000\uA3FA" + // 95 - 99 + "\u0000\uA1D0\u0000\uA1C3\u0000\uA1D1\u0000\uA1C1\u0000\u0000" + // 100 - 104 + "\u0000\uD0E9\u008F\uB1D8\u0000\uCAEF\u0000\uC3CD\u0000\uD0E5" + // 105 - 109 + "\u0000\uB7F1\u0000\u0000\u0000\uD0E2\u0000\uD0EA\u0000\uD0E4" + // 110 - 114 + "\u0000\uCED1\u0000\uD0EB\u0000\uCFC1\u0000\u0000\u0000\u0000" + // 115 - 119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 120 - 124 + "\u0000\u0000\u0000\uB6E6\u0000\u0000\u0000\u0000\u0000\uB7F0" + // 125 - 129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 130 - 134 + "\u0000\u0000\u008F\uBFC9\u0000\u0000\u0000\u0000\u0000\u0000" + // 135 - 139 + "\u0000\u0000\u0000\uC8E4\u0000\uDAAD\u0000\u0000\u0000\u0000" + // 140 - 144 + "\u0000\u0000\u0000\u0000\u0000\uCAFA\u0000\u0000\u0000\u0000" + // 145 - 149 + "\u0000\u0000\u0000\uC4F1\u0000\u0000\u0000\u0000\u0000\u0000" + // 150 - 154 + "\u0000\uCBF5\u0000\u0000\u0000\uD9BB\u0000\uB2A1\u0000\uC3EA" + // 155 - 159 + "\u0000\u0000\u0000\u0000\u0000\uDACC\u0000\uDACD\u0000\u0000" + // 160 - 164 + "\u0000\u0000\u0000\u0000\u0000\uCAB8\u0000\uD5DD\u0000\uC0C6" + // 165 - 169 + "\u0000\u0000\u0000\u0000\u0000\uC9CC\u0000\u0000\u0000\uBAD8" + // 170 - 174 + "\u0000\u0000\u0000\uC8E5\u0000\uC8C3\u0000\u0000\u0000\u0000" + // 175 - 179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5CD\u0000\u0000" + // 180 - 184 + "\u0000\uCEC1\u0000\u0000\u0000\uDACF\u0000\uBCD0\u0000\u0000" + // 185 - 189 + "\u0000\u0000\u0000\uDAD0\u0000\uA1F7\u0000\uA3C1\u0000\uA3C2" + // 190 - 194 + "\u0000\uA3C3\u0000\uA3C4\u0000\uA3C5\u0000\uA3C6\u0000\uA3C7" + // 195 - 199 + "\u0000\uA3C8\u0000\uA3C9\u0000\uA3CA\u0000\uA3CB\u0000\uA3CC" + // 200 - 204 + "\u0000\uA3CD\u0000\uA3CE\u0000\uA3CF\u0000\uA3D0\u0000\uA3D1" + // 205 - 209 + "\u0000\uA3D2\u0000\uA3D3\u0000\uA3D4\u0000\uA3D5\u0000\uA3D6" + // 210 - 214 + "\u0000\uA3D7\u0000\uA3D8\u0000\uA3D9\u0000\uA3DA\u0000\uA1CE" + // 215 - 219 + "\u0000\uA1C0\u0000\uA1CF\u0000\uA1B0\u0000\uA1B2\u008F\uF4DA" + // 220 - 224 + "\u008F\uF4DB\u008F\uF4DE\u008F\uF4E2\u008F\uF4E3\u008F\uF4E4" + // 225 - 229 + "\u008F\uF4E6\u008F\uF4E8\u008F\uF4E9\u008F\uF4EC\u008F\uF4F1" + // 230 - 234 + "\u008F\uF4F2\u008F\uF4F3\u008F\uF4F7\u0000\u0000\u0000\u0000" + // 235 - 239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 240 - 244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 245 - 249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 250 - 254 + "\u0000\u0000\u0000\uDFD8\u0000\u0000\u0000\u0000\u0000\u0000" + // 255 - 259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBA3" + // 260 - 264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFE2\u0000\u0000" + // 265 - 269 + "\u0000\uB6A2\u0000\uB2C1\u0000\u0000\u0000\u0000\u0000\u0000" + // 270 - 274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 275 - 279 + "\u0000\u0000\u0000\uD5A5\u0000\u0000\u0000\uCBF9\u0000\uC9EE" + // 280 - 284 + "\u0000\uB8F4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 285 - 289 + "\u0000\u0000\u0000\uBFAF\u0000\uCEB7\u0000\u0000\u0000\u0000" + // 290 - 294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 295 - 299 + "\u0000\u0000\u0000\uCAD8\u008F\uFEE7\u008F\uFEE8\u008F\uFEE9" + // 300 - 304 + "\u008F\uFEEA\u008F\uFEEB\u008F\uFEEC\u008F\uFEED\u008F\uFEEE" + // 305 - 309 + "\u008F\uFEEF\u008F\uFEF0\u008F\uFEF1\u008F\uFEF2\u008F\uFEF3" + // 310 - 314 + "\u008F\uFEF4\u008F\uFEF5\u008F\uFEF6\u008F\uFEF7\u008F\uFEF8" + // 315 - 319 + "\u008F\uFEF9\u008F\uFEFA\u008F\uFEFB\u008F\uFEFC\u008F\uFEFD" + // 320 - 324 + "\u008F\uFEFE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 325 - 329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB9FC" + // 330 - 334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1EC" + // 335 - 339 + "\u0000\u0000\u0000\u0000\u0000\uF1ED\u0000\u0000\u0000\u0000" + // 340 - 344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 345 - 349 + "\u0000\uB3BC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1EE" + // 350 - 354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6D2\u0000\u0000" + // 355 - 359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 360 - 364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6D4\u0000\u0000" + // 365 - 369 + "\u0000\uD6D5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 370 - 374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6D8" + // 375 - 379 + "\u008F\uBBF4\u008F\uBBF5\u0000\uCEE6\u0000\u0000\u0000\uD6D9" + // 380 - 384 + "\u0000\uD6D6\u0000\u0000\u0000\u0000\u008F\uB0C8\u0000\u0000" + // 385 - 389 + "\u0000\uC2E5\u0000\uCEE1\u0000\uB0CA\u0000\u0000\u0000\u0000" + // 390 - 394 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 395 - 399 + "\u0000\uD0C1\u0000\uB2BE\u0000\u0000\u0000\uB6C4\u0000\u0000" + // 400 - 404 + "\u0000\uC3E7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7EF" + // 405 - 409 + "\u0000\uD0C3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7A4" + // 410 - 414 + "\u008F\uF4A2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8E9" + // 415 - 419 + "\u0000\u0000\u0000\u0000\u0000\uD8EA\u0000\uBAA9\u0000\uD8E8" + // 420 - 424 + "\u0000\uD8E6\u0000\uD8E5\u0000\uD8EC\u0000\uD8E4\u0000\uD8EE" + // 425 - 429 + "\u0000\u0000\u0000\u0000\u0000\uB2FB\u0000\u0000\u0000\u0000" + // 430 - 434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 435 - 439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 440 - 444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFA5\u0000\u0000" + // 445 - 449 + "\u0000\u0000\u008F\uF4F6\u0000\u0000\u0000\uF2B7\u008F\uEACD" + // 450 - 454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 455 - 459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 460 - 464 + "\u0000\u0000\u0000\uB0F2\u0000\u0000\u0000\uE7E9\u0000\u0000" + // 465 - 469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7EA\u0000\u0000" + // 470 - 474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 475 - 479 + "\u0000\uC9E7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCC7" + // 480 - 484 + "\u0000\u0000\u0000\uE7EC\u008F\uFEC7\u008F\uFEC8\u008F\uFEC9" + // 485 - 489 + "\u008F\uFECA\u008F\uFECB\u008F\uFECC\u008F\uFECD\u008F\uFECE" + // 490 - 494 + "\u008F\uFECF\u008F\uFED0\u008F\uFED1\u008F\uFED2\u008F\uFED3" + // 495 - 499 + "\u008F\uFED4\u008F\uFED5\u008F\uFED6\u008F\uFED7\u008F\uFED8" + // 500 - 504 + "\u008F\uFED9\u008F\uFEDA\u008F\uFEDB\u008F\uFEDC\u008F\uFEDD" + // 505 - 509 + "\u008F\uFEDE\u008F\uFEDF\u008F\uFEE0\u008F\uFEE1\u008F\uFEE2" + // 510 - 514 + "\u008F\uFEE3\u008F\uFEE4\u008F\uFEE5\u008F\uFEE6\u008F\uFEA7" + // 515 - 519 + "\u008F\uFEA8\u008F\uFEA9\u008F\uFEAA\u008F\uFEAB\u008F\uFEAC" + // 520 - 524 + "\u008F\uFEAD\u008F\uFEAE\u008F\uFEAF\u008F\uFEB0\u008F\uFEB1" + // 525 - 529 + "\u008F\uFEB2\u008F\uFEB3\u008F\uFEB4\u008F\uFEB5\u008F\uFEB6" + // 530 - 534 + "\u008F\uFEB7\u008F\uFEB8\u008F\uFEB9\u008F\uFEBA\u008F\uFEBB" + // 535 - 539 + "\u008F\uFEBC\u008F\uFEBD\u008F\uFEBE\u008F\uFEBF\u008F\uFEC0" + // 540 - 544 + "\u008F\uFEC1\u008F\uFEC2\u008F\uFEC3\u008F\uFEC4\u008F\uFEC5" + // 545 - 549 + "\u008F\uFEC6\u008F\uFDE5\u008F\uFDE6\u008F\uFDE7\u008F\uFDE8" + // 550 - 554 + "\u008F\uFDE9\u008F\uFDEA\u008F\uFDEB\u008F\uFDEC\u008F\uFDED" + // 555 - 559 + "\u008F\uFDEE\u008F\uFDEF\u008F\uFDF0\u008F\uFDF1\u008F\uFDF2" + // 560 - 564 + "\u008F\uFDF3\u008F\uFDF4\u008F\uFDF5\u008F\uFDF6\u008F\uFDF7" + // 565 - 569 + "\u008F\uFDF8\u008F\uFDF9\u008F\uFDFA\u008F\uFDFB\u008F\uFDFC" + // 570 - 574 + "\u008F\uFDFD\u008F\uFDFE\u008F\uFEA1\u008F\uFEA2\u008F\uFEA3" + // 575 - 579 + "\u008F\uFEA4\u008F\uFEA5\u008F\uFEA6\u008F\uFDC5\u008F\uFDC6" + // 580 - 584 + "\u008F\uFDC7\u008F\uFDC8\u008F\uFDC9\u008F\uFDCA\u008F\uFDCB" + // 585 - 589 + "\u008F\uFDCC\u008F\uFDCD\u008F\uFDCE\u008F\uFDCF\u008F\uFDD0" + // 590 - 594 + "\u008F\uFDD1\u008F\uFDD2\u008F\uFDD3\u008F\uFDD4\u008F\uFDD5" + // 595 - 599 + "\u008F\uFDD6\u008F\uFDD7\u008F\uFDD8\u008F\uFDD9\u008F\uFDDA" + // 600 - 604 + "\u008F\uFDDB\u008F\uFDDC\u008F\uFDDD\u008F\uFDDE\u008F\uFDDF" + // 605 - 609 + "\u008F\uFDE0\u008F\uFDE1\u008F\uFDE2\u008F\uFDE3\u008F\uFDE4" + // 610 - 614 + "\u008F\uFDA5\u008F\uFDA6\u008F\uFDA7\u008F\uFDA8\u008F\uFDA9" + // 615 - 619 + "\u008F\uFDAA\u008F\uFDAB\u008F\uFDAC\u008F\uFDAD\u008F\uFDAE" + // 620 - 624 + "\u008F\uFDAF\u008F\uFDB0\u008F\uFDB1\u008F\uFDB2\u008F\uFDB3" + // 625 - 629 + "\u008F\uFDB4\u008F\uFDB5\u008F\uFDB6\u008F\uFDB7\u008F\uFDB8" + // 630 - 634 + "\u008F\uFDB9\u008F\uFDBA\u008F\uFDBB\u008F\uFDBC\u008F\uFDBD" + // 635 - 639 + "\u008F\uFDBE\u008F\uFDBF\u008F\uFDC0\u008F\uFDC1\u008F\uFDC2" + // 640 - 644 + "\u008F\uFDC3\u008F\uFDC4\u008F\uFCE3\u008F\uFCE4\u008F\uFCE5" + // 645 - 649 + "\u008F\uFCE6\u008F\uFCE7\u008F\uFCE8\u008F\uFCE9\u008F\uFCEA" + // 650 - 654 + "\u008F\uFCEB\u008F\uFCEC\u008F\uFCED\u008F\uFCEE\u008F\uFCEF" + // 655 - 659 + "\u008F\uFCF0\u008F\uFCF1\u008F\uFCF2\u008F\uFCF3\u008F\uFCF4" + // 660 - 664 + "\u008F\uFCF5\u008F\uFCF6\u008F\uFCF7\u008F\uFCF8\u008F\uFCF9" + // 665 - 669 + "\u008F\uFCFA\u008F\uFCFB\u008F\uFCFC\u008F\uFCFD\u008F\uFCFE" + // 670 - 674 + "\u008F\uFDA1\u008F\uFDA2\u008F\uFDA3\u008F\uFDA4\u008F\uFCC3" + // 675 - 679 + "\u008F\uFCC4\u008F\uFCC5\u008F\uFCC6\u008F\uFCC7\u008F\uFCC8" + // 680 - 684 + "\u008F\uFCC9\u008F\uFCCA\u008F\uFCCB\u008F\uFCCC\u008F\uFCCD" + // 685 - 689 + "\u008F\uFCCE\u008F\uFCCF\u008F\uFCD0\u008F\uFCD1\u008F\uFCD2" + // 690 - 694 + "\u008F\uFCD3\u008F\uFCD4\u008F\uFCD5\u008F\uFCD6\u008F\uFCD7" + // 695 - 699 + "\u008F\uFCD8\u008F\uFCD9\u008F\uFCDA\u008F\uFCDB\u008F\uFCDC" + // 700 - 704 + "\u008F\uFCDD\u008F\uFCDE\u008F\uFCDF\u008F\uFCE0\u008F\uFCE1" + // 705 - 709 + "\u008F\uFCE2\u008F\uFCA3\u008F\uFCA4\u008F\uFCA5\u008F\uFCA6" + // 710 - 714 + "\u008F\uFCA7\u008F\uFCA8\u008F\uFCA9\u008F\uFCAA\u008F\uFCAB" + // 715 - 719 + "\u008F\uFCAC\u008F\uFCAD\u008F\uFCAE\u008F\uFCAF\u008F\uFCB0" + // 720 - 724 + "\u008F\uFCB1\u008F\uFCB2\u008F\uFCB3\u008F\uFCB4\u008F\uFCB5" + // 725 - 729 + "\u008F\uFCB6\u008F\uFCB7\u008F\uFCB8\u008F\uFCB9\u008F\uFCBA" + // 730 - 734 + "\u008F\uFCBB\u008F\uFCBC\u008F\uFCBD\u008F\uFCBE\u008F\uFCBF" + // 735 - 739 + "\u008F\uFCC0\u008F\uFCC1\u008F\uFCC2\u008F\uFBE1\u008F\uFBE2" + // 740 - 744 + "\u008F\uFBE3\u008F\uFBE4\u008F\uFBE5\u008F\uFBE6\u008F\uFBE7" + // 745 - 749 + "\u008F\uFBE8\u008F\uFBE9\u008F\uFBEA\u008F\uFBEB\u008F\uFBEC" + // 750 - 754 + "\u008F\uFBED\u008F\uFBEE\u008F\uFBEF\u008F\uFBF0\u008F\uFBF1" + // 755 - 759 + "\u008F\uFBF2\u008F\uFBF3\u008F\uFBF4\u008F\uFBF5\u008F\uFBF6" + // 760 - 764 + "\u008F\uFBF7\u008F\uFBF8\u008F\uFBF9\u008F\uFBFA\u008F\uFBFB" + // 765 - 769 + "\u008F\uFBFC\u008F\uFBFD\u008F\uFBFE\u008F\uFCA1\u008F\uFCA2" + // 770 - 774 + "\u008F\uFBC1\u008F\uFBC2\u008F\uFBC3\u008F\uFBC4\u008F\uFBC5" + // 775 - 779 + "\u008F\uFBC6\u008F\uFBC7\u008F\uFBC8\u008F\uFBC9\u008F\uFBCA" + // 780 - 784 + "\u008F\uFBCB\u008F\uFBCC\u008F\uFBCD\u008F\uFBCE\u008F\uFBCF" + // 785 - 789 + "\u008F\uFBD0\u008F\uFBD1\u008F\uFBD2\u008F\uFBD3\u008F\uFBD4" + // 790 - 794 + "\u008F\uFBD5\u008F\uFBD6\u008F\uFBD7\u008F\uFBD8\u008F\uFBD9" + // 795 - 799 + "\u008F\uFBDA\u008F\uFBDB\u008F\uFBDC\u008F\uFBDD\u008F\uFBDE" + // 800 - 804 + "\u008F\uFBDF\u008F\uFBE0\u008F\uFBA1\u008F\uFBA2\u008F\uFBA3" + // 805 - 809 + "\u008F\uFBA4\u008F\uFBA5\u008F\uFBA6\u008F\uFBA7\u008F\uFBA8" + // 810 - 814 + "\u008F\uFBA9\u008F\uFBAA\u008F\uFBAB\u008F\uFBAC\u008F\uFBAD" + // 815 - 819 + "\u008F\uFBAE\u008F\uFBAF\u008F\uFBB0\u008F\uFBB1\u008F\uFBB2" + // 820 - 824 + "\u008F\uFBB3\u008F\uFBB4\u008F\uFBB5\u008F\uFBB6\u008F\uFBB7" + // 825 - 829 + "\u008F\uFBB8\u008F\uFBB9\u008F\uFBBA\u008F\uFBBB\u008F\uFBBC" + // 830 - 834 + "\u008F\uFBBD\u008F\uFBBE\u008F\uFBBF\u008F\uFBC0\u008F\uFADF" + // 835 - 839 + "\u008F\uFAE0\u008F\uFAE1\u008F\uFAE2\u008F\uFAE3\u008F\uFAE4" + // 840 - 844 + "\u008F\uFAE5\u008F\uFAE6\u008F\uFAE7\u008F\uFAE8\u008F\uFAE9" + // 845 - 849 + "\u008F\uFAEA\u008F\uFAEB\u008F\uFAEC\u008F\uFAED\u008F\uFAEE" + // 850 - 854 + "\u008F\uFAEF\u008F\uFAF0\u008F\uFAF1\u008F\uFAF2\u008F\uFAF3" + // 855 - 859 + "\u008F\uFAF4\u008F\uFAF5\u008F\uFAF6\u008F\uFAF7\u008F\uFAF8" + // 860 - 864 + "\u008F\uFAF9\u008F\uFAFA\u008F\uFAFB\u008F\uFAFC\u008F\uFAFD" + // 865 - 869 + "\u008F\uFAFE\u008F\uFABF\u008F\uFAC0\u008F\uFAC1\u008F\uFAC2" + // 870 - 874 + "\u008F\uFAC3\u008F\uFAC4\u008F\uFAC5\u008F\uFAC6\u008F\uFAC7" + // 875 - 879 + "\u008F\uFAC8\u008F\uFAC9\u008F\uFACA\u008F\uFACB\u008F\uFACC" + // 880 - 884 + "\u008F\uFACD\u008F\uFACE\u008F\uFACF\u008F\uFAD0\u008F\uFAD1" + // 885 - 889 + "\u008F\uFAD2\u008F\uFAD3\u008F\uFAD4\u008F\uFAD5\u008F\uFAD6" + // 890 - 894 + "\u008F\uFAD7\u008F\uFAD8\u008F\uFAD9\u008F\uFADA\u008F\uFADB" + // 895 - 899 + "\u008F\uFADC\u008F\uFADD\u008F\uFADE\u008F\uF9FD\u008F\uF9FE" + // 900 - 904 + "\u008F\uFAA1\u008F\uFAA2\u008F\uFAA3\u008F\uFAA4\u008F\uFAA5" + // 905 - 909 + "\u008F\uFAA6\u008F\uFAA7\u008F\uFAA8\u008F\uFAA9\u008F\uFAAA" + // 910 - 914 + "\u008F\uFAAB\u008F\uFAAC\u008F\uFAAD\u008F\uFAAE\u008F\uFAAF" + // 915 - 919 + "\u008F\uFAB0\u008F\uFAB1\u008F\uFAB2\u008F\uFAB3\u008F\uFAB4" + // 920 - 924 + "\u008F\uFAB5\u008F\uFAB6\u008F\uFAB7\u008F\uFAB8\u008F\uFAB9" + // 925 - 929 + "\u008F\uFABA\u008F\uFABB\u008F\uFABC\u008F\uFABD\u008F\uFABE" + // 930 - 934 + "\u008F\uF9DD\u008F\uF9DE\u008F\uF9DF\u008F\uF9E0\u008F\uF9E1" + // 935 - 939 + "\u008F\uF9E2\u008F\uF9E3\u008F\uF9E4\u008F\uF9E5\u008F\uF9E6" + // 940 - 944 + "\u008F\uF9E7\u008F\uF9E8\u008F\uF9E9\u008F\uF9EA\u008F\uF9EB" + // 945 - 949 + "\u008F\uF9EC\u008F\uF9ED\u008F\uF9EE\u008F\uF9EF\u008F\uF9F0" + // 950 - 954 + "\u008F\uF9F1\u008F\uF9F2\u008F\uF9F3\u008F\uF9F4\u008F\uF9F5" + // 955 - 959 + "\u008F\uF9F6\u008F\uF9F7\u008F\uF9F8\u008F\uF9F9\u008F\uF9FA" + // 960 - 964 + "\u008F\uF9FB\u008F\uF9FC\u008F\uF9BD\u008F\uF9BE\u008F\uF9BF" + // 965 - 969 + "\u008F\uF9C0\u008F\uF9C1\u008F\uF9C2\u008F\uF9C3\u008F\uF9C4" + // 970 - 974 + "\u008F\uF9C5\u008F\uF9C6\u008F\uF9C7\u008F\uF9C8\u008F\uF9C9" + // 975 - 979 + "\u008F\uF9CA\u008F\uF9CB\u008F\uF9CC\u008F\uF9CD\u008F\uF9CE" + // 980 - 984 + "\u008F\uF9CF\u008F\uF9D0\u008F\uF9D1\u008F\uF9D2\u008F\uF9D3" + // 985 - 989 + "\u008F\uF9D4\u008F\uF9D5\u008F\uF9D6\u008F\uF9D7\u008F\uF9D8" + // 990 - 994 + "\u008F\uF9D9\u008F\uF9DA\u008F\uF9DB\u008F\uF9DC\u008F\uF8FB" + // 995 - 999 + "\u008F\uF8FC\u008F\uF8FD\u008F\uF8FE\u008F\uF9A1\u008F\uF9A2" + // 1000 - 1004 + "\u008F\uF9A3\u008F\uF9A4\u008F\uF9A5\u008F\uF9A6\u008F\uF9A7" + // 1005 - 1009 + "\u008F\uF9A8\u008F\uF9A9\u008F\uF9AA\u008F\uF9AB\u008F\uF9AC" + // 1010 - 1014 + "\u008F\uF9AD\u008F\uF9AE\u008F\uF9AF\u008F\uF9B0\u008F\uF9B1" + // 1015 - 1019 + "\u008F\uF9B2\u008F\uF9B3\u008F\uF9B4\u008F\uF9B5\u008F\uF9B6" + // 1020 - 1024 + "\u008F\uF9B7\u008F\uF9B8\u008F\uF9B9\u008F\uF9BA\u008F\uF9BB" + // 1025 - 1029 + "\u008F\uF9BC\u008F\uF8DB\u008F\uF8DC\u008F\uF8DD\u008F\uF8DE" + // 1030 - 1034 + "\u008F\uF8DF\u008F\uF8E0\u008F\uF8E1\u008F\uF8E2\u008F\uF8E3" + // 1035 - 1039 + "\u008F\uF8E4\u008F\uF8E5\u008F\uF8E6\u008F\uF8E7\u008F\uF8E8" + // 1040 - 1044 + "\u008F\uF8E9\u008F\uF8EA\u008F\uF8EB\u008F\uF8EC\u008F\uF8ED" + // 1045 - 1049 + "\u008F\uF8EE\u008F\uF8EF\u008F\uF8F0\u008F\uF8F1\u008F\uF8F2" + // 1050 - 1054 + "\u008F\uF8F3\u008F\uF8F4\u008F\uF8F5\u008F\uF8F6\u008F\uF8F7" + // 1055 - 1059 + "\u008F\uF8F8\u008F\uF8F9\u008F\uF8FA\u008F\uF8BB\u008F\uF8BC" + // 1060 - 1064 + "\u008F\uF8BD\u008F\uF8BE\u008F\uF8BF\u008F\uF8C0\u008F\uF8C1" + // 1065 - 1069 + "\u008F\uF8C2\u008F\uF8C3\u008F\uF8C4\u008F\uF8C5\u008F\uF8C6" + // 1070 - 1074 + "\u008F\uF8C7\u008F\uF8C8\u008F\uF8C9\u008F\uF8CA\u008F\uF8CB" + // 1075 - 1079 + "\u008F\uF8CC\u008F\uF8CD\u008F\uF8CE\u008F\uF8CF\u008F\uF8D0" + // 1080 - 1084 + "\u008F\uF8D1\u008F\uF8D2\u008F\uF8D3\u008F\uF8D4\u008F\uF8D5" + // 1085 - 1089 + "\u008F\uF8D6\u008F\uF8D7\u008F\uF8D8\u008F\uF8D9\u008F\uF8DA" + // 1090 - 1094 + "\u008F\uF7F9\u008F\uF7FA\u008F\uF7FB\u008F\uF7FC\u008F\uF7FD" + // 1095 - 1099 + "\u008F\uF7FE\u008F\uF8A1\u008F\uF8A2\u008F\uF8A3\u008F\uF8A4" + // 1100 - 1104 + "\u008F\uF8A5\u008F\uF8A6\u008F\uF8A7\u008F\uF8A8\u008F\uF8A9" + // 1105 - 1109 + "\u008F\uF8AA\u008F\uF8AB\u008F\uF8AC\u008F\uF8AD\u008F\uF8AE" + // 1110 - 1114 + "\u008F\uF8AF\u008F\uF8B0\u008F\uF8B1\u008F\uF8B2\u008F\uF8B3" + // 1115 - 1119 + "\u008F\uF8B4\u008F\uF8B5\u008F\uF8B6\u008F\uF8B7\u008F\uF8B8" + // 1120 - 1124 + "\u008F\uF8B9\u008F\uF8BA\u008F\uF7D9\u008F\uF7DA\u008F\uF7DB" + // 1125 - 1129 + "\u008F\uF7DC\u008F\uF7DD\u008F\uF7DE\u008F\uF7DF\u008F\uF7E0" + // 1130 - 1134 + "\u008F\uF7E1\u008F\uF7E2\u008F\uF7E3\u008F\uF7E4\u008F\uF7E5" + // 1135 - 1139 + "\u008F\uF7E6\u008F\uF7E7\u008F\uF7E8\u008F\uF7E9\u008F\uF7EA" + // 1140 - 1144 + "\u008F\uF7EB\u008F\uF7EC\u008F\uF7ED\u008F\uF7EE\u008F\uF7EF" + // 1145 - 1149 + "\u008F\uF7F0\u008F\uF7F1\u008F\uF7F2\u008F\uF7F3\u008F\uF7F4" + // 1150 - 1154 + "\u008F\uF7F5\u008F\uF7F6\u008F\uF7F7\u008F\uF7F8\u008F\uF7B9" + // 1155 - 1159 + "\u008F\uF7BA\u008F\uF7BB\u008F\uF7BC\u008F\uF7BD\u008F\uF7BE" + // 1160 - 1164 + "\u008F\uF7BF\u008F\uF7C0\u008F\uF7C1\u008F\uF7C2\u008F\uF7C3" + // 1165 - 1169 + "\u008F\uF7C4\u008F\uF7C5\u008F\uF7C6\u008F\uF7C7\u008F\uF7C8" + // 1170 - 1174 + "\u008F\uF7C9\u008F\uF7CA\u008F\uF7CB\u008F\uF7CC\u008F\uF7CD" + // 1175 - 1179 + "\u008F\uF7CE\u008F\uF7CF\u008F\uF7D0\u008F\uF7D1\u008F\uF7D2" + // 1180 - 1184 + "\u008F\uF7D3\u008F\uF7D4\u008F\uF7D5\u008F\uF7D6\u008F\uF7D7" + // 1185 - 1189 + "\u008F\uF7D8\u008F\uF6F7\u008F\uF6F8\u008F\uF6F9\u008F\uF6FA" + // 1190 - 1194 + "\u008F\uF6FB\u008F\uF6FC\u008F\uF6FD\u008F\uF6FE\u008F\uF7A1" + // 1195 - 1199 + "\u008F\uF7A2\u008F\uF7A3\u008F\uF7A4\u008F\uF7A5\u008F\uF7A6" + // 1200 - 1204 + "\u008F\uF7A7\u008F\uF7A8\u008F\uF7A9\u008F\uF7AA\u008F\uF7AB" + // 1205 - 1209 + "\u008F\uF7AC\u008F\uF7AD\u008F\uF7AE\u008F\uF7AF\u008F\uF7B0" + // 1210 - 1214 + "\u008F\uF7B1\u008F\uF7B2\u008F\uF7B3\u008F\uF7B4\u008F\uF7B5" + // 1215 - 1219 + "\u008F\uF7B6\u008F\uF7B7\u008F\uF7B8\u008F\uF6D7\u008F\uF6D8" + // 1220 - 1224 + "\u008F\uF6D9\u008F\uF6DA\u008F\uF6DB\u008F\uF6DC\u008F\uF6DD" + // 1225 - 1229 + "\u008F\uF6DE\u008F\uF6DF\u008F\uF6E0\u008F\uF6E1\u008F\uF6E2" + // 1230 - 1234 + "\u008F\uF6E3\u008F\uF6E4\u008F\uF6E5\u008F\uF6E6\u008F\uF6E7" + // 1235 - 1239 + "\u008F\uF6E8\u008F\uF6E9\u008F\uF6EA\u008F\uF6EB\u008F\uF6EC" + // 1240 - 1244 + "\u008F\uF6ED\u008F\uF6EE\u008F\uF6EF\u008F\uF6F0\u008F\uF6F1" + // 1245 - 1249 + "\u008F\uF6F2\u008F\uF6F3\u008F\uF6F4\u008F\uF6F5\u008F\uF6F6" + // 1250 - 1254 + "\u008F\uF6B7\u008F\uF6B8\u008F\uF6B9\u008F\uF6BA\u008F\uF6BB" + // 1255 - 1259 + "\u008F\uF6BC\u008F\uF6BD\u008F\uF6BE\u008F\uF6BF\u008F\uF6C0" + // 1260 - 1264 + "\u008F\uF6C1\u008F\uF6C2\u008F\uF6C3\u008F\uF6C4\u008F\uF6C5" + // 1265 - 1269 + "\u008F\uF6C6\u008F\uF6C7\u008F\uF6C8\u008F\uF6C9\u008F\uF6CA" + // 1270 - 1274 + "\u008F\uF6CB\u008F\uF6CC\u008F\uF6CD\u008F\uF6CE\u008F\uF6CF" + // 1275 - 1279 + "\u008F\uF6D0\u008F\uF6D1\u008F\uF6D2\u008F\uF6D3\u008F\uF6D4" + // 1280 - 1284 + "\u008F\uF6D5\u008F\uF6D6\u008F\uF5F5\u008F\uF5F6\u008F\uF5F7" + // 1285 - 1289 + "\u008F\uF5F8\u008F\uF5F9\u008F\uF5FA\u008F\uF5FB\u008F\uF5FC" + // 1290 - 1294 + "\u008F\uF5FD\u008F\uF5FE\u008F\uF6A1\u008F\uF6A2\u008F\uF6A3" + // 1295 - 1299 + "\u008F\uF6A4\u008F\uF6A5\u008F\uF6A6\u008F\uF6A7\u008F\uF6A8" + // 1300 - 1304 + "\u008F\uF6A9\u008F\uF6AA\u008F\uF6AB\u008F\uF6AC\u008F\uF6AD" + // 1305 - 1309 + "\u008F\uF6AE\u008F\uF6AF\u008F\uF6B0\u008F\uF6B1\u008F\uF6B2" + // 1310 - 1314 + "\u008F\uF6B3\u008F\uF6B4\u008F\uF6B5\u008F\uF6B6\u008F\uF5D5" + // 1315 - 1319 + "\u008F\uF5D6\u008F\uF5D7\u008F\uF5D8\u008F\uF5D9\u008F\uF5DA" + // 1320 - 1324 + "\u008F\uF5DB\u008F\uF5DC\u008F\uF5DD\u008F\uF5DE\u008F\uF5DF" + // 1325 - 1329 + "\u008F\uF5E0\u008F\uF5E1\u008F\uF5E2\u008F\uF5E3\u008F\uF5E4" + // 1330 - 1334 + "\u008F\uF5E5\u008F\uF5E6\u008F\uF5E7\u008F\uF5E8\u008F\uF5E9" + // 1335 - 1339 + "\u008F\uF5EA\u008F\uF5EB\u008F\uF5EC\u008F\uF5ED\u008F\uF5EE" + // 1340 - 1344 + "\u008F\uF5EF\u008F\uF5F0\u008F\uF5F1\u008F\uF5F2\u008F\uF5F3" + // 1345 - 1349 + "\u008F\uF5F4\u008F\uF5B5\u008F\uF5B6\u008F\uF5B7\u008F\uF5B8" + // 1350 - 1354 + "\u008F\uF5B9\u008F\uF5BA\u008F\uF5BB\u008F\uF5BC\u008F\uF5BD" + // 1355 - 1359 + "\u008F\uF5BE\u008F\uF5BF\u008F\uF5C0\u008F\uF5C1\u008F\uF5C2" + // 1360 - 1364 + "\u008F\uF5C3\u008F\uF5C4\u008F\uF5C5\u008F\uF5C6\u008F\uF5C7" + // 1365 - 1369 + "\u008F\uF5C8\u008F\uF5C9\u008F\uF5CA\u008F\uF5CB\u008F\uF5CC" + // 1370 - 1374 + "\u008F\uF5CD\u008F\uF5CE\u008F\uF5CF\u008F\uF5D0\u008F\uF5D1" + // 1375 - 1379 + "\u008F\uF5D2\u008F\uF5D3\u008F\uF5D4\u0000\uFEF3\u0000\uFEF4" + // 1380 - 1384 + "\u0000\uFEF5\u0000\uFEF6\u0000\uFEF7\u0000\uFEF8\u0000\uFEF9" + // 1385 - 1389 + "\u0000\uFEFA\u0000\uFEFB\u0000\uFEFC\u0000\uFEFD\u0000\uFEFE" + // 1390 - 1394 + "\u008F\uF5A1\u008F\uF5A2\u008F\uF5A3\u008F\uF5A4\u008F\uF5A5" + // 1395 - 1399 + "\u008F\uF5A6\u008F\uF5A7\u008F\uF5A8\u008F\uF5A9\u008F\uF5AA" + // 1400 - 1404 + "\u008F\uF5AB\u008F\uF5AC\u008F\uF5AD\u008F\uF5AE\u008F\uF5AF" + // 1405 - 1409 + "\u008F\uF5B0\u008F\uF5B1\u008F\uF5B2\u008F\uF5B3\u008F\uF5B4" + // 1410 - 1414 + "\u0000\uFED3\u0000\uFED4\u0000\uFED5\u0000\uFED6\u0000\uFED7" + // 1415 - 1419 + "\u0000\uFED8\u0000\uFED9\u0000\uFEDA\u0000\uFEDB\u0000\uFEDC" + // 1420 - 1424 + "\u0000\uFEDD\u0000\uFEDE\u0000\uFEDF\u0000\uFEE0\u0000\uFEE1" + // 1425 - 1429 + "\u0000\uFEE2\u0000\uFEE3\u0000\uFEE4\u0000\uFEE5\u0000\uFEE6" + // 1430 - 1434 + "\u0000\uFEE7\u0000\uFEE8\u0000\uFEE9\u0000\uFEEA\u0000\uFEEB" + // 1435 - 1439 + "\u0000\uFEEC\u0000\uFEED\u0000\uFEEE\u0000\uFEEF\u0000\uFEF0" + // 1440 - 1444 + "\u0000\uFEF1\u0000\uFEF2\u0000\uFEB3\u0000\uFEB4\u0000\uFEB5" + // 1445 - 1449 + "\u0000\uFEB6\u0000\uFEB7\u0000\uFEB8\u0000\uFEB9\u0000\uFEBA" + // 1450 - 1454 + "\u0000\uFEBB\u0000\uFEBC\u0000\uFEBD\u0000\uFEBE\u0000\uFEBF" + // 1455 - 1459 + "\u0000\uFEC0\u0000\uFEC1\u0000\uFEC2\u0000\uFEC3\u0000\uFEC4" + // 1460 - 1464 + "\u0000\uFEC5\u0000\uFEC6\u0000\uFEC7\u0000\uFEC8\u0000\uFEC9" + // 1465 - 1469 + "\u0000\uFECA\u0000\uFECB\u0000\uFECC\u0000\uFECD\u0000\uFECE" + // 1470 - 1474 + "\u0000\uFECF\u0000\uFED0\u0000\uFED1\u0000\uFED2\u0000\uFDF1" + // 1475 - 1479 + "\u0000\uFDF2\u0000\uFDF3\u0000\uFDF4\u0000\uFDF5\u0000\uFDF6" + // 1480 - 1484 + "\u0000\uFDF7\u0000\uFDF8\u0000\uFDF9\u0000\uFDFA\u0000\uFDFB" + // 1485 - 1489 + "\u0000\uFDFC\u0000\uFDFD\u0000\uFDFE\u0000\uFEA1\u0000\uFEA2" + // 1490 - 1494 + "\u0000\uFEA3\u0000\uFEA4\u0000\uFEA5\u0000\uFEA6\u0000\uFEA7" + // 1495 - 1499 + "\u0000\uFEA8\u0000\uFEA9\u0000\uFEAA\u0000\uFEAB\u0000\uFEAC" + // 1500 - 1504 + "\u0000\uFEAD\u0000\uFEAE\u0000\uFEAF\u0000\uFEB0\u0000\uFEB1" + // 1505 - 1509 + "\u0000\uFEB2\u0000\uFDD1\u0000\uFDD2\u0000\uFDD3\u0000\uFDD4" + // 1510 - 1514 + "\u0000\uFDD5\u0000\uFDD6\u0000\uFDD7\u0000\uFDD8\u0000\uFDD9" + // 1515 - 1519 + "\u0000\uFDDA\u0000\uFDDB\u0000\uFDDC\u0000\uFDDD\u0000\uFDDE" + // 1520 - 1524 + "\u0000\uFDDF\u0000\uFDE0\u0000\uFDE1\u0000\uFDE2\u0000\uFDE3" + // 1525 - 1529 + "\u0000\uFDE4\u0000\uFDE5\u0000\uFDE6\u0000\uFDE7\u0000\uFDE8" + // 1530 - 1534 + "\u0000\uFDE9\u0000\uFDEA\u0000\uFDEB\u0000\uFDEC\u0000\uFDED" + // 1535 - 1539 + "\u0000\uFDEE\u0000\uFDEF\u0000\uFDF0\u0000\uFDB1\u0000\uFDB2" + // 1540 - 1544 + "\u0000\uFDB3\u0000\uFDB4\u0000\uFDB5\u0000\uFDB6\u0000\uFDB7" + // 1545 - 1549 + "\u0000\uFDB8\u0000\uFDB9\u0000\uFDBA\u0000\uFDBB\u0000\uFDBC" + // 1550 - 1554 + "\u0000\uFDBD\u0000\uFDBE\u0000\uFDBF\u0000\uFDC0\u0000\uFDC1" + // 1555 - 1559 + "\u0000\uFDC2\u0000\uFDC3\u0000\uFDC4\u0000\uFDC5\u0000\uFDC6" + // 1560 - 1564 + "\u0000\uFDC7\u0000\uFDC8\u0000\uFDC9\u0000\uFDCA\u0000\uFDCB" + // 1565 - 1569 + "\u0000\uFDCC\u0000\uFDCD\u0000\uFDCE\u0000\uFDCF\u0000\uFDD0" + // 1570 - 1574 + "\u0000\uFCEF\u0000\uFCF0\u0000\uFCF1\u0000\uFCF2\u0000\uFCF3" + // 1575 - 1579 + "\u0000\uFCF4\u0000\uFCF5\u0000\uFCF6\u0000\uFCF7\u0000\uFCF8" + // 1580 - 1584 + "\u0000\uFCF9\u0000\uFCFA\u0000\uFCFB\u0000\uFCFC\u0000\uFCFD" + // 1585 - 1589 + "\u0000\uFCFE\u0000\uFDA1\u0000\uFDA2\u0000\uFDA3\u0000\uFDA4" + // 1590 - 1594 + "\u0000\uFDA5\u0000\uFDA6\u0000\uFDA7\u0000\uFDA8\u0000\uFDA9" + // 1595 - 1599 + "\u0000\uFDAA\u0000\uFDAB\u0000\uFDAC\u0000\uFDAD\u0000\uFDAE" + // 1600 - 1604 + "\u0000\uFDAF\u0000\uFDB0\u0000\uFCCF\u0000\uFCD0\u0000\uFCD1" + // 1605 - 1609 + "\u0000\uFCD2\u0000\uFCD3\u0000\uFCD4\u0000\uFCD5\u0000\uFCD6" + // 1610 - 1614 + "\u0000\uFCD7\u0000\uFCD8\u0000\uFCD9\u0000\uFCDA\u0000\uFCDB" + // 1615 - 1619 + "\u0000\uFCDC\u0000\uFCDD\u0000\uFCDE\u0000\uFCDF\u0000\uFCE0" + // 1620 - 1624 + "\u0000\uFCE1\u0000\uFCE2\u0000\uFCE3\u0000\uFCE4\u0000\uFCE5" + // 1625 - 1629 + "\u0000\uFCE6\u0000\uFCE7\u0000\uFCE8\u0000\uFCE9\u0000\uFCEA" + // 1630 - 1634 + "\u0000\uFCEB\u0000\uFCEC\u0000\uFCED\u0000\uFCEE\u0000\uFCAF" + // 1635 - 1639 + "\u0000\uFCB0\u0000\uFCB1\u0000\uFCB2\u0000\uFCB3\u0000\uFCB4" + // 1640 - 1644 + "\u0000\uFCB5\u0000\uFCB6\u0000\uFCB7\u0000\uFCB8\u0000\uFCB9" + // 1645 - 1649 + "\u0000\uFCBA\u0000\uFCBB\u0000\uFCBC\u0000\uFCBD\u0000\uFCBE" + // 1650 - 1654 + "\u0000\uFCBF\u0000\uFCC0\u0000\uFCC1\u0000\uFCC2\u0000\uFCC3" + // 1655 - 1659 + "\u0000\uFCC4\u0000\uFCC5\u0000\uFCC6\u0000\uFCC7\u0000\uFCC8" + // 1660 - 1664 + "\u0000\uFCC9\u0000\uFCCA\u0000\uFCCB\u0000\uFCCC\u0000\uFCCD" + // 1665 - 1669 + "\u0000\uFCCE\u0000\uFBED\u0000\uFBEE\u0000\uFBEF\u0000\uFBF0" + // 1670 - 1674 + "\u0000\uFBF1\u0000\uFBF2\u0000\uFBF3\u0000\uFBF4\u0000\uFBF5" + // 1675 - 1679 + "\u0000\uFBF6\u0000\uFBF7\u0000\uFBF8\u0000\uFBF9\u0000\uFBFA" + // 1680 - 1684 + "\u0000\uFBFB\u0000\uFBFC\u0000\uFBFD\u0000\uFBFE\u0000\uFCA1" + // 1685 - 1689 + "\u0000\uFCA2\u0000\uFCA3\u0000\uFCA4\u0000\uFCA5\u0000\uFCA6" + // 1690 - 1694 + "\u0000\uFCA7\u0000\uFCA8\u0000\uFCA9\u0000\uFCAA\u0000\uFCAB" + // 1695 - 1699 + "\u0000\uFCAC\u0000\uFCAD\u0000\uFCAE\u0000\uFBCD\u0000\uFBCE" + // 1700 - 1704 + "\u0000\uFBCF\u0000\uFBD0\u0000\uFBD1\u0000\uFBD2\u0000\uFBD3" + // 1705 - 1709 + "\u0000\uFBD4\u0000\uFBD5\u0000\uFBD6\u0000\uFBD7\u0000\uFBD8" + // 1710 - 1714 + "\u0000\uFBD9\u0000\uFBDA\u0000\uFBDB\u0000\uFBDC\u0000\uFBDD" + // 1715 - 1719 + "\u0000\uFBDE\u0000\uFBDF\u0000\uFBE0\u0000\uFBE1\u0000\uFBE2" + // 1720 - 1724 + "\u0000\uFBE3\u0000\uFBE4\u0000\uFBE5\u0000\uFBE6\u0000\uFBE7" + // 1725 - 1729 + "\u0000\uFBE8\u0000\uFBE9\u0000\uFBEA\u0000\uFBEB\u0000\uFBEC" + // 1730 - 1734 + "\u0000\uFBAD\u0000\uFBAE\u0000\uFBAF\u0000\uFBB0\u0000\uFBB1" + // 1735 - 1739 + "\u0000\uFBB2\u0000\uFBB3\u0000\uFBB4\u0000\uFBB5\u0000\uFBB6" + // 1740 - 1744 + "\u0000\uFBB7\u0000\uFBB8\u0000\uFBB9\u0000\uFBBA\u0000\uFBBB" + // 1745 - 1749 + "\u0000\uFBBC\u0000\uFBBD\u0000\uFBBE\u0000\uFBBF\u0000\uFBC0" + // 1750 - 1754 + "\u0000\uFBC1\u0000\uFBC2\u0000\uFBC3\u0000\uFBC4\u0000\uFBC5" + // 1755 - 1759 + "\u0000\uFBC6\u0000\uFBC7\u0000\uFBC8\u0000\uFBC9\u0000\uFBCA" + // 1760 - 1764 + "\u0000\uFBCB\u0000\uFBCC\u0000\uFAEB\u0000\uFAEC\u0000\uFAED" + // 1765 - 1769 + "\u0000\uFAEE\u0000\uFAEF\u0000\uFAF0\u0000\uFAF1\u0000\uFAF2" + // 1770 - 1774 + "\u0000\uFAF3\u0000\uFAF4\u0000\uFAF5\u0000\uFAF6\u0000\uFAF7" + // 1775 - 1779 + "\u0000\uFAF8\u0000\uFAF9\u0000\uFAFA\u0000\uFAFB\u0000\uFAFC" + // 1780 - 1784 + "\u0000\uFAFD\u0000\uFAFE\u0000\uFBA1\u0000\uFBA2\u0000\uFBA3" + // 1785 - 1789 + "\u0000\uFBA4\u0000\uFBA5\u0000\uFBA6\u0000\uFBA7\u0000\uFBA8" + // 1790 - 1794 + "\u0000\uFBA9\u0000\uFBAA\u0000\uFBAB\u0000\uFBAC\u0000\uFACB" + // 1795 - 1799 + "\u0000\uFACC\u0000\uFACD\u0000\uFACE\u0000\uFACF\u0000\uFAD0" + // 1800 - 1804 + "\u0000\uFAD1\u0000\uFAD2\u0000\uFAD3\u0000\uFAD4\u0000\uFAD5" + // 1805 - 1809 + "\u0000\uFAD6\u0000\uFAD7\u0000\uFAD8\u0000\uFAD9\u0000\uFADA" + // 1810 - 1814 + "\u0000\uFADB\u0000\uFADC\u0000\uFADD\u0000\uFADE\u0000\uFADF" + // 1815 - 1819 + "\u0000\uFAE0\u0000\uFAE1\u0000\uFAE2\u0000\uFAE3\u0000\uFAE4" + // 1820 - 1824 + "\u0000\uFAE5\u0000\uFAE6\u0000\uFAE7\u0000\uFAE8\u0000\uFAE9" + // 1825 - 1829 + "\u0000\uFAEA\u0000\uFAAB\u0000\uFAAC\u0000\uFAAD\u0000\uFAAE" + // 1830 - 1834 + "\u0000\uFAAF\u0000\uFAB0\u0000\uFAB1\u0000\uFAB2\u0000\uFAB3" + // 1835 - 1839 + "\u0000\uFAB4\u0000\uFAB5\u0000\uFAB6\u0000\uFAB7\u0000\uFAB8" + // 1840 - 1844 + "\u0000\uFAB9\u0000\uFABA\u0000\uFABB\u0000\uFABC\u0000\uFABD" + // 1845 - 1849 + "\u0000\uFABE\u0000\uFABF\u0000\uFAC0\u0000\uFAC1\u0000\uFAC2" + // 1850 - 1854 + "\u0000\uFAC3\u0000\uFAC4\u0000\uFAC5\u0000\uFAC6\u0000\uFAC7" + // 1855 - 1859 + "\u0000\uFAC8\u0000\uFAC9\u0000\uFACA\u0000\uF9E9\u0000\uF9EA" + // 1860 - 1864 + "\u0000\uF9EB\u0000\uF9EC\u0000\uF9ED\u0000\uF9EE\u0000\uF9EF" + // 1865 - 1869 + "\u0000\uF9F0\u0000\uF9F1\u0000\uF9F2\u0000\uF9F3\u0000\uF9F4" + // 1870 - 1874 + "\u0000\uF9F5\u0000\uF9F6\u0000\uF9F7\u0000\uF9F8\u0000\uF9F9" + // 1875 - 1879 + "\u0000\uF9FA\u0000\uF9FB\u0000\uF9FC\u0000\uF9FD\u0000\uF9FE" + // 1880 - 1884 + "\u0000\uFAA1\u0000\uFAA2\u0000\uFAA3\u0000\uFAA4\u0000\uFAA5" + // 1885 - 1889 + "\u0000\uFAA6\u0000\uFAA7\u0000\uFAA8\u0000\uFAA9\u0000\uFAAA" + // 1890 - 1894 + "\u0000\uF9C9\u0000\uF9CA\u0000\uF9CB\u0000\uF9CC\u0000\uF9CD" + // 1895 - 1899 + "\u0000\uF9CE\u0000\uF9CF\u0000\uF9D0\u0000\uF9D1\u0000\uF9D2" + // 1900 - 1904 + "\u0000\uF9D3\u0000\uF9D4\u0000\uF9D5\u0000\uF9D6\u0000\uF9D7" + // 1905 - 1909 + "\u0000\uF9D8\u0000\uF9D9\u0000\uF9DA\u0000\uF9DB\u0000\uF9DC" + // 1910 - 1914 + "\u0000\uF9DD\u0000\uF9DE\u0000\uF9DF\u0000\uF9E0\u0000\uF9E1" + // 1915 - 1919 + "\u0000\uF9E2\u0000\uF9E3\u0000\uF9E4\u0000\uF9E5\u0000\uF9E6" + // 1920 - 1924 + "\u0000\uF9E7\u0000\uF9E8\u0000\uF9A9\u0000\uF9AA\u0000\uF9AB" + // 1925 - 1929 + "\u0000\uF9AC\u0000\uF9AD\u0000\uF9AE\u0000\uF9AF\u0000\uF9B0" + // 1930 - 1934 + "\u0000\uF9B1\u0000\uF9B2\u0000\uF9B3\u0000\uF9B4\u0000\uF9B5" + // 1935 - 1939 + "\u0000\uF9B6\u0000\uF9B7\u0000\uF9B8\u0000\uF9B9\u0000\uF9BA" + // 1940 - 1944 + "\u0000\uF9BB\u0000\uF9BC\u0000\uF9BD\u0000\uF9BE\u0000\uF9BF" + // 1945 - 1949 + "\u0000\uF9C0\u0000\uF9C1\u0000\uF9C2\u0000\uF9C3\u0000\uF9C4" + // 1950 - 1954 + "\u0000\uF9C5\u0000\uF9C6\u0000\uF9C7\u0000\uF9C8\u0000\uF8E7" + // 1955 - 1959 + "\u0000\uF8E8\u0000\uF8E9\u0000\uF8EA\u0000\uF8EB\u0000\uF8EC" + // 1960 - 1964 + "\u0000\uF8ED\u0000\uF8EE\u0000\uF8EF\u0000\uF8F0\u0000\uF8F1" + // 1965 - 1969 + "\u0000\uF8F2\u0000\uF8F3\u0000\uF8F4\u0000\uF8F5\u0000\uF8F6" + // 1970 - 1974 + "\u0000\uF8F7\u0000\uF8F8\u0000\uF8F9\u0000\uF8FA\u0000\uF8FB" + // 1975 - 1979 + "\u0000\uF8FC\u0000\uF8FD\u0000\uF8FE\u0000\uF9A1\u0000\uF9A2" + // 1980 - 1984 + "\u0000\uF9A3\u0000\uF9A4\u0000\uF9A5\u0000\uF9A6\u0000\uF9A7" + // 1985 - 1989 + "\u0000\uF9A8\u0000\uF8C7\u0000\uF8C8\u0000\uF8C9\u0000\uF8CA" + // 1990 - 1994 + "\u0000\uF8CB\u0000\uF8CC\u0000\uF8CD\u0000\uF8CE\u0000\uF8CF" + // 1995 - 1999 + "\u0000\uF8D0\u0000\uF8D1\u0000\uF8D2\u0000\uF8D3\u0000\uF8D4" + // 2000 - 2004 + "\u0000\uF8D5\u0000\uF8D6\u0000\uF8D7\u0000\uF8D8\u0000\uF8D9" + // 2005 - 2009 + "\u0000\uF8DA\u0000\uF8DB\u0000\uF8DC\u0000\uF8DD\u0000\uF8DE" + // 2010 - 2014 + "\u0000\uF8DF\u0000\uF8E0\u0000\uF8E1\u0000\uF8E2\u0000\uF8E3" + // 2015 - 2019 + "\u0000\uF8E4\u0000\uF8E5\u0000\uF8E6\u0000\uF8A7\u0000\uF8A8" + // 2020 - 2024 + "\u0000\uF8A9\u0000\uF8AA\u0000\uF8AB\u0000\uF8AC\u0000\uF8AD" + // 2025 - 2029 + "\u0000\uF8AE\u0000\uF8AF\u0000\uF8B0\u0000\uF8B1\u0000\uF8B2" + // 2030 - 2034 + "\u0000\uF8B3\u0000\uF8B4\u0000\uF8B5\u0000\uF8B6\u0000\uF8B7" + // 2035 - 2039 + "\u0000\uF8B8\u0000\uF8B9\u0000\uF8BA\u0000\uF8BB\u0000\uF8BC" + // 2040 - 2044 + "\u0000\uF8BD\u0000\uF8BE\u0000\uF8BF\u0000\uF8C0\u0000\uF8C1" + // 2045 - 2049 + "\u0000\uF8C2\u0000\uF8C3\u0000\uF8C4\u0000\uF8C5\u0000\uF8C6" + // 2050 - 2054 + "\u0000\uF7E5\u0000\uF7E6\u0000\uF7E7\u0000\uF7E8\u0000\uF7E9" + // 2055 - 2059 + "\u0000\uF7EA\u0000\uF7EB\u0000\uF7EC\u0000\uF7ED\u0000\uF7EE" + // 2060 - 2064 + "\u0000\uF7EF\u0000\uF7F0\u0000\uF7F1\u0000\uF7F2\u0000\uF7F3" + // 2065 - 2069 + "\u0000\uF7F4\u0000\uF7F5\u0000\uF7F6\u0000\uF7F7\u0000\uF7F8" + // 2070 - 2074 + "\u0000\uF7F9\u0000\uF7FA\u0000\uF7FB\u0000\uF7FC\u0000\uF7FD" + // 2075 - 2079 + "\u0000\uF7FE\u0000\uF8A1\u0000\uF8A2\u0000\uF8A3\u0000\uF8A4" + // 2080 - 2084 + "\u0000\uF8A5\u0000\uF8A6\u0000\uF7C5\u0000\uF7C6\u0000\uF7C7" + // 2085 - 2089 + "\u0000\uF7C8\u0000\uF7C9\u0000\uF7CA\u0000\uF7CB\u0000\uF7CC" + // 2090 - 2094 + "\u0000\uF7CD\u0000\uF7CE\u0000\uF7CF\u0000\uF7D0\u0000\uF7D1" + // 2095 - 2099 + "\u0000\uF7D2\u0000\uF7D3\u0000\uF7D4\u0000\uF7D5\u0000\uF7D6" + // 2100 - 2104 + "\u0000\uF7D7\u0000\uF7D8\u0000\uF7D9\u0000\uF7DA\u0000\uF7DB" + // 2105 - 2109 + "\u0000\uF7DC\u0000\uF7DD\u0000\uF7DE\u0000\uF7DF\u0000\uF7E0" + // 2110 - 2114 + "\u0000\uF7E1\u0000\uF7E2\u0000\uF7E3\u0000\uF7E4\u0000\uF7A5" + // 2115 - 2119 + "\u0000\uF7A6\u0000\uF7A7\u0000\uF7A8\u0000\uF7A9\u0000\uF7AA" + // 2120 - 2124 + "\u0000\uF7AB\u0000\uF7AC\u0000\uF7AD\u0000\uF7AE\u0000\uF7AF" + // 2125 - 2129 + "\u0000\uF7B0\u0000\uF7B1\u0000\uF7B2\u0000\uF7B3\u0000\uF7B4" + // 2130 - 2134 + "\u0000\uF7B5\u0000\uF7B6\u0000\uF7B7\u0000\uF7B8\u0000\uF7B9" + // 2135 - 2139 + "\u0000\uF7BA\u0000\uF7BB\u0000\uF7BC\u0000\uF7BD\u0000\uF7BE" + // 2140 - 2144 + "\u0000\uF7BF\u0000\uF7C0\u0000\uF7C1\u0000\uF7C2\u0000\uF7C3" + // 2145 - 2149 + "\u0000\uF7C4\u0000\uF6E3\u0000\uF6E4\u0000\uF6E5\u0000\uF6E6" + // 2150 - 2154 + "\u0000\uF6E7\u0000\uF6E8\u0000\uF6E9\u0000\uF6EA\u0000\uF6EB" + // 2155 - 2159 + "\u0000\uF6EC\u0000\uF6ED\u0000\uF6EE\u0000\uF6EF\u0000\uF6F0" + // 2160 - 2164 + "\u0000\uF6F1\u0000\uF6F2\u0000\uF6F3\u0000\uF6F4\u0000\uF6F5" + // 2165 - 2169 + "\u0000\uF6F6\u0000\uF6F7\u0000\uF6F8\u0000\uF6F9\u0000\uF6FA" + // 2170 - 2174 + "\u0000\uF6FB\u0000\uF6FC\u0000\uF6FD\u0000\uF6FE\u0000\uF7A1" + // 2175 - 2179 + "\u0000\uF7A2\u0000\uF7A3\u0000\uF7A4\u0000\uF6C3\u0000\uF6C4" + // 2180 - 2184 + "\u0000\uF6C5\u0000\uF6C6\u0000\uF6C7\u0000\uF6C8\u0000\uF6C9" + // 2185 - 2189 + "\u0000\uF6CA\u0000\uF6CB\u0000\uF6CC\u0000\uF6CD\u0000\uF6CE" + // 2190 - 2194 + "\u0000\uF6CF\u0000\uF6D0\u0000\uF6D1\u0000\uF6D2\u0000\uF6D3" + // 2195 - 2199 + "\u0000\uF6D4\u0000\uF6D5\u0000\uF6D6\u0000\uF6D7\u0000\uF6D8" + // 2200 - 2204 + "\u0000\uF6D9\u0000\uF6DA\u0000\uF6DB\u0000\uF6DC\u0000\uF6DD" + // 2205 - 2209 + "\u0000\uF6DE\u0000\uF6DF\u0000\uF6E0\u0000\uF6E1\u0000\uF6E2" + // 2210 - 2214 + "\u0000\uF6A3\u0000\uF6A4\u0000\uF6A5\u0000\uF6A6\u0000\uF6A7" + // 2215 - 2219 + "\u0000\uF6A8\u0000\uF6A9\u0000\uF6AA\u0000\uF6AB\u0000\uF6AC" + // 2220 - 2224 + "\u0000\uF6AD\u0000\uF6AE\u0000\uF6AF\u0000\uF6B0\u0000\uF6B1" + // 2225 - 2229 + "\u0000\uF6B2\u0000\uF6B3\u0000\uF6B4\u0000\uF6B5\u0000\uF6B6" + // 2230 - 2234 + "\u0000\uF6B7\u0000\uF6B8\u0000\uF6B9\u0000\uF6BA\u0000\uF6BB" + // 2235 - 2239 + "\u0000\uF6BC\u0000\uF6BD\u0000\uF6BE\u0000\uF6BF\u0000\uF6C0" + // 2240 - 2244 + "\u0000\uF6C1\u0000\uF6C2\u0000\uF5E1\u0000\uF5E2\u0000\uF5E3" + // 2245 - 2249 + "\u0000\uF5E4\u0000\uF5E5\u0000\uF5E6\u0000\uF5E7\u0000\uF5E8" + // 2250 - 2254 + "\u0000\uF5E9\u0000\uF5EA\u0000\uF5EB\u0000\uF5EC\u0000\uF5ED" + // 2255 - 2259 + "\u0000\uF5EE\u0000\uF5EF\u0000\uF5F0\u0000\uF5F1\u0000\uF5F2" + // 2260 - 2264 + "\u0000\uF5F3\u0000\uF5F4\u0000\uF5F5\u0000\uF5F6\u0000\uF5F7" + // 2265 - 2269 + "\u0000\uF5F8\u0000\uF5F9\u0000\uF5FA\u0000\uF5FB\u0000\uF5FC" + // 2270 - 2274 + "\u0000\uF5FD\u0000\uF5FE\u0000\uF6A1\u0000\uF6A2\u0000\uF5C1" + // 2275 - 2279 + "\u0000\uF5C2\u0000\uF5C3\u0000\uF5C4\u0000\uF5C5\u0000\uF5C6" + // 2280 - 2284 + "\u0000\uF5C7\u0000\uF5C8\u0000\uF5C9\u0000\uF5CA\u0000\uF5CB" + // 2285 - 2289 + "\u0000\uF5CC\u0000\uF5CD\u0000\uF5CE\u0000\uF5CF\u0000\uF5D0" + // 2290 - 2294 + "\u0000\uF5D1\u0000\uF5D2\u0000\uF5D3\u0000\uF5D4\u0000\uF5D5" + // 2295 - 2299 + "\u0000\uF5D6\u0000\uF5D7\u0000\uF5D8\u0000\uF5D9\u0000\uF5DA" + // 2300 - 2304 + "\u0000\uF5DB\u0000\uF5DC\u0000\uF5DD\u0000\uF5DE\u0000\uF5DF" + // 2305 - 2309 + "\u0000\uF5E0\u0000\uF5A1\u0000\uF5A2\u0000\uF5A3\u0000\uF5A4" + // 2310 - 2314 + "\u0000\uF5A5\u0000\uF5A6\u0000\uF5A7\u0000\uF5A8\u0000\uF5A9" + // 2315 - 2319 + "\u0000\uF5AA\u0000\uF5AB\u0000\uF5AC\u0000\uF5AD\u0000\uF5AE" + // 2320 - 2324 + "\u0000\uF5AF\u0000\uF5B0\u0000\uF5B1\u0000\uF5B2\u0000\uF5B3" + // 2325 - 2329 + "\u0000\uF5B4\u0000\uF5B5\u0000\uF5B6\u0000\uF5B7\u0000\uF5B8" + // 2330 - 2334 + "\u0000\uF5B9\u0000\uF5BA\u0000\uF5BB\u0000\uF5BC\u0000\uF5BD" + // 2335 - 2339 + "\u0000\uF5BE\u0000\uF5BF\u0000\uF5C0\u0000\uF3FE\u0000\u0000" + // 2340 - 2344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2345 - 2349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2350 - 2354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2355 - 2359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2360 - 2364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2365 - 2369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2370 - 2374 + "\u0000\u0000\u0000\uD5BC\u0000\uD5C0\u0000\uD5BD\u0000\u0000" + // 2375 - 2379 + "\u0000\uF1D5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1D7" + // 2380 - 2384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2385 - 2389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5B3\u0000\uF1D6" + // 2390 - 2394 + "\u0000\u0000\u0000\u0000\u0000\uC1FB\u0000\uB8B3\u0000\u0000" + // 2395 - 2399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1D9" + // 2400 - 2404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2405 - 2409 + "\u0000\u0000\u0000\uF3BC\u0000\u0000\u0000\u0000\u0000\u0000" + // 2410 - 2414 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3BD\u0000\u0000" + // 2415 - 2419 + "\u0000\uF3BE\u0000\u0000\u0000\u0000\u0000\uCFC9\u0000\u0000" + // 2420 - 2424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3BB" + // 2425 - 2429 + "\u0000\uC2EB\u0000\uBAED\u0000\u0000\u0000\u0000\u0000\uF3BF" + // 2430 - 2434 + "\u0000\u0000\u0000\u0000\u008F\uF3B9\u0000\u0000\u0000\u0000" + // 2435 - 2439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2440 - 2444 + "\u0000\u0000\u0000\u0000\u0000\uA2F2\u0000\u0000\u0000\u0000" + // 2445 - 2449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2450 - 2454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2455 - 2459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2460 - 2464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2DE\u0000\u0000" + // 2465 - 2469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2470 - 2474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2475 - 2479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uF4A7\u008F\uF4AA" + // 2480 - 2484 + "\u008F\uF4AB\u008F\uF4B1\u008F\uF4B8\u008F\uF4BB\u008F\uF4BC" + // 2485 - 2489 + "\u008F\uF4C4\u008F\uF4C5\u008F\uF4C9\u008F\uF4CC\u008F\uF4CD" + // 2490 - 2494 + "\u008F\uF4CE\u008F\uF4CF\u008F\uF4D1\u008F\uF4D3\u008F\uF4D6" + // 2495 - 2499 + "\u008F\uF4D8\u0000\uF3F3\u0000\uF3F4\u0000\uCEF0\u0000\uF3F1" + // 2500 - 2504 + "\u0000\u0000\u0000\u0000\u0000\uF3F5\u0000\uF3F6\u0000\u0000" + // 2505 - 2509 + "\u0000\u0000\u0000\uF3F8\u0000\u0000\u0000\uF3F7\u0000\u0000" + // 2510 - 2514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3FA" + // 2515 - 2519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3FB\u0000\uF3F9" + // 2520 - 2524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2525 - 2529 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9F7\u0000\u0000" + // 2530 - 2534 + "\u0000\uF1A4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2535 - 2539 + "\u0000\uF1A5\u0000\u0000\u0000\uF1A6\u0000\u0000\u0000\u0000" + // 2540 - 2544 + "\u0000\u0000\u0000\u0000\u0000\uF1A7\u0000\u0000\u0000\u0000" + // 2545 - 2549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2550 - 2554 + "\u0000\u0000\u0000\u0000\u0000\uE3DD\u0000\uB7A6\u0000\u0000" + // 2555 - 2559 + "\u0000\u0000\u0000\u0000\u0000\uB5E7\u0000\uCDD2\u0000\uE3DF" + // 2560 - 2564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2565 - 2569 + "\u0000\uE3E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1AE" + // 2570 - 2574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3E3" + // 2575 - 2579 + "\u0000\uC1CD\u0000\uF3EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 2580 - 2584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2585 - 2589 + "\u0000\u0000\u0000\u0000\u0000\uF3EC\u0000\u0000\u0000\u0000" + // 2590 - 2594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2595 - 2599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2600 - 2604 + "\u0000\u0000\u0000\u0000\u0000\uC9A1\u0000\u0000\u0000\u0000" + // 2605 - 2609 + "\u0000\uF3ED\u0000\u0000\u0000\uC7FD\u0000\u0000\u0000\u0000" + // 2610 - 2614 + "\u0000\uC2CC\u0000\uB1D8\u0000\uB6EE\u0000\u0000\u0000\uB6EF" + // 2615 - 2619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2620 - 2624 + "\u0000\u0000\u0000\u0000\u0000\uC3F3\u0000\uF1CE\u0000\uB6F0" + // 2625 - 2629 + "\u0000\u0000\u0000\u0000\u0000\uB2EF\u0000\u0000\u0000\u0000" + // 2630 - 2634 + "\u0000\uF1CD\u0000\u0000\u0000\u0000\u0000\uF1CB\u0000\u0000" + // 2635 - 2639 + "\u0000\uF1CC\u0000\u0000\u0000\uF1CA\u0000\uF3DC\u0000\u0000" + // 2640 - 2644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3DD\u0000\u0000" + // 2645 - 2649 + "\u0000\u0000\u0000\uF3DE\u0000\u0000\u0000\u0000\u0000\u0000" + // 2650 - 2654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3DF\u0000\u0000" + // 2655 - 2659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3E0\u0000\u0000" + // 2660 - 2664 + "\u0000\uF3E1\u0000\uF3E2\u0000\u0000\u0000\uF3E3\u0000\u0000" + // 2665 - 2669 + "\u0000\uF3E4\u0000\uF3E5\u0000\uF3E6\u0000\u0000\u0000\u0000" + // 2670 - 2674 + "\u0000\uCEBE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2675 - 2679 + "\u0000\uCAC2\u0000\u0000\u008F\uB0A9\u0000\u0000\u0000\uD0A4" + // 2680 - 2684 + "\u0000\u0000\u0000\u0000\u0000\uC3E6\u0000\u0000\u0000\u0000" + // 2685 - 2689 + "\u0000\u0000\u0000\uD0A5\u0000\uB6FA\u0000\u0000\u0000\u0000" + // 2690 - 2694 + "\u0000\u0000\u0000\uD0A6\u0000\u0000\u0000\uB4DD\u0000\uC3B0" + // 2695 - 2699 + "\u0000\u0000\u0000\uBCE7\u0000\uD0A7\u0000\u0000\u0000\u0000" + // 2700 - 2704 + "\u0000\uD0A8\u0000\uB9F4\u0000\uCCB9\u0000\u0000\u0000\u0000" + // 2705 - 2709 + "\u0000\uF3A3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2710 - 2714 + "\u0000\u0000\u0000\u0000\u008F\uEBFA\u0000\uCBB2\u0000\u0000" + // 2715 - 2719 + "\u0000\u0000\u0000\uF3AB\u008F\uEBFB\u0000\u0000\u0000\uF3A7" + // 2720 - 2724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2725 - 2729 + "\u0000\u0000\u0000\u0000\u0000\uF3AC\u0000\u0000\u0000\u0000" + // 2730 - 2734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4E1\u0000\uD4DF" + // 2735 - 2739 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2740 - 2744 + "\u0000\uBBCE\u0000\uBFD1\u0000\u0000\u0000\uC1D4\u0000\uD4E3" + // 2745 - 2749 + "\u0000\uC0BC\u0000\uB0ED\u0000\uC7E4\u0000\u0000\u0000\u0000" + // 2750 - 2754 + "\u0000\u0000\u0000\u0000\u0000\uC4DB\u0000\u0000\u0000\uD4E5" + // 2755 - 2759 + "\u0000\uD4E4\u0000\uD4E6\u0000\uD4E7\u0000\uD4E8\u0000\u0000" + // 2760 - 2764 + "\u0000\u0000\u0000\uA4A1\u0000\uA4A2\u0000\uA4A3\u0000\uA4A4" + // 2765 - 2769 + "\u0000\uA4A5\u0000\uA4A6\u0000\uA4A7\u0000\uA4A8\u0000\uA4A9" + // 2770 - 2774 + "\u0000\uA4AA\u0000\uA4AB\u0000\uA4AC\u0000\uA4AD\u0000\uA4AE" + // 2775 - 2779 + "\u0000\uA4AF\u0000\uA4B0\u0000\uA4B1\u0000\uA4B2\u0000\uA4B3" + // 2780 - 2784 + "\u0000\uA4B4\u0000\uA4B5\u0000\uA4B6\u0000\uA4B7\u0000\uA4B8" + // 2785 - 2789 + "\u0000\uA4B9\u0000\uA4BA\u0000\uA4BB\u0000\uA4BC\u0000\uA4BD" + // 2790 - 2794 + "\u0000\uA4BE\u0000\uA4BF\u0000\uF2E6\u0000\u0000\u0000\u0000" + // 2795 - 2799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2E7" + // 2800 - 2804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2805 - 2809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2810 - 2814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2E8" + // 2815 - 2819 + "\u0000\u0000\u0000\uF2E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 2820 - 2824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3B2" + // 2825 - 2829 + "\u0000\u0000\u0000\u0000\u0000\uB2C5\u0000\u0000\u0000\u0000" + // 2830 - 2834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2835 - 2839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3D2\u0000\u0000" + // 2840 - 2844 + "\u0000\uD3D4\u0000\uBEA8\u0000\uB1B3\u0000\u0000\u0000\u0000" + // 2845 - 2849 + "\u0000\uD3D7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2850 - 2854 + "\u0000\uD5A6\u008F\uB9AF\u0000\uC2C5\u0000\u0000\u0000\u0000" + // 2855 - 2859 + "\u0000\uCBB8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5CA" + // 2860 - 2864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2865 - 2869 + "\u0000\uD5A7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2870 - 2874 + "\u0000\u0000\u0000\u0000\u0000\uCBE5\u008F\uB9B7\u0000\uBACA" + // 2875 - 2879 + "\u0000\u0000\u0000\u0000\u0000\uBEAA\u0000\u0000\u0000\uCAC7" + // 2880 - 2884 + "\u0000\uC4BA\u0000\uBAA2\u0000\u0000\u0000\uB9E0\u0000\uBDE7" + // 2885 - 2889 + "\u0000\u0000\u0000\uBFDC\u0000\u0000\u0000\u0000\u0000\u0000" + // 2890 - 2894 + "\u0000\uF0F3\u0000\u0000\u0000\u0000\u0000\uF0F2\u0000\uCDC2" + // 2895 - 2899 + "\u0000\uB4E8\u0000\uC8D2\u0000\uC6DC\u0000\u0000\u0000\u0000" + // 2900 - 2904 + "\u0000\u0000\u0000\uBFFC\u0000\uCECE\u0000\u0000\u0000\uB7DB" + // 2905 - 2909 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2910 - 2914 + "\u0000\uD3CE\u0000\uD3CC\u0000\u0000\u0000\uD4A7\u0000\u0000" + // 2915 - 2919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2920 - 2924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2925 - 2929 + "\u0000\u0000\u0000\uD3D1\u0000\u0000\u0000\u0000\u0000\u0000" + // 2930 - 2934 + "\u0000\u0000\u0000\u0000\u0000\uD3CB\u0000\u0000\u0000\uD3CF" + // 2935 - 2939 + "\u0000\u0000\u0000\u0000\u0000\uD3CD\u008F\uEBA5\u0000\u0000" + // 2940 - 2944 + "\u0000\u0000\u0000\u0000\u0000\uF2DA\u0000\u0000\u0000\uF2D6" + // 2945 - 2949 + "\u0000\u0000\u0000\uF2D7\u0000\uF2D3\u0000\uF2D9\u0000\u0000" + // 2950 - 2954 + "\u0000\uF2D5\u0000\uB3E2\u0000\u0000\u0000\u0000\u0000\uCFCC" + // 2955 - 2959 + "\u0000\u0000\u0000\uF2D8\u0000\uF2D4\u0000\uF2D2\u0000\uF2D1" + // 2960 - 2964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2965 - 2969 + "\u0000\uF2DC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2970 - 2974 + "\u0000\uB6AD\u0000\u0000\u0000\uD4D0\u0000\u0000\u0000\u0000" + // 2975 - 2979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2980 - 2984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2985 - 2989 + "\u0000\u0000\u0000\uCAE8\u0000\u0000\u0000\u0000\u0000\u0000" + // 2990 - 2994 + "\u0000\uC1FD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 2995 - 2999 + "\u0000\uC4C6\u0000\u0000\u008F\uF4AC\u0000\uD4D2\u0000\uF2C1" + // 3000 - 3004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3005 - 3009 + "\u0000\uF2C4\u0000\u0000\u0000\u0000\u0000\uB8F1\u0000\uF2C2" + // 3010 - 3014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2C5" + // 3015 - 3019 + "\u0000\u0000\u0000\uF2C6\u0000\uF2C7\u0000\u0000\u0000\uF2CB" + // 3020 - 3024 + "\u0000\u0000\u0000\uBBAA\u0000\u0000\u0000\u0000\u0000\u0000" + // 3025 - 3029 + "\u0000\u0000\u0000\uC2E4\u0000\u0000\u0000\u0000\u0000\u0000" + // 3030 - 3034 + "\u0000\u0000\u0000\uBBCC\u0000\uD3D0\u0000\u0000\u0000\u0000" + // 3035 - 3039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3040 - 3044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3045 - 3049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3050 - 3054 + "\u0000\u0000\u0000\uD3D3\u0000\u0000\u0000\uD3D8\u0000\u0000" + // 3055 - 3059 + "\u0000\u0000\u0000\u0000\u0000\uD3D6\u0000\uD3D5\u0000\u0000" + // 3060 - 3064 + "\u0000\uEFF4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3065 - 3069 + "\u0000\u0000\u0000\u0000\u0000\uEFF5\u0000\u0000\u0000\uBAE5" + // 3070 - 3074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFF6\u0000\uEFF7" + // 3075 - 3079 + "\u0000\u0000\u0000\u0000\u0000\uCBC9\u0000\u0000\u0000\u0000" + // 3080 - 3084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3085 - 3089 + "\u0000\u0000\u0000\uC1CB\u0000\u0000\u0000\u0000\u0000\u0000" + // 3090 - 3094 + "\u0000\uB0A4\u0000\uF2BE\u0000\u0000\u0000\u0000\u0000\u0000" + // 3095 - 3099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2BF" + // 3100 - 3104 + "\u0000\u0000\u0000\uCBEE\u0000\uBBAD\u0000\u0000\u0000\uBAFA" + // 3105 - 3109 + "\u0000\uC1AF\u0000\u0000\u0000\u0000\u008F\uEAE6\u0000\u0000" + // 3110 - 3114 + "\u0000\u0000\u0000\uF2C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 3115 - 3119 + "\u0000\u0000\u0000\uF2C3\u0000\u0000\u008F\uEAEA\u0000\u0000" + // 3120 - 3124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3B2\u0000\u0000" + // 3125 - 3129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3130 - 3134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3135 - 3139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3140 - 3144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3145 - 3149 + "\u0000\u0000\u0000\u0000\u0000\uD3C1\u0000\uD3C6\u0000\u0000" + // 3150 - 3154 + "\u0000\uD3C2\u0000\u0000\u0000\uEFF1\u0000\uEFF3\u0000\u0000" + // 3155 - 3159 + "\u0000\u0000\u0000\uEFF2\u0000\u0000\u0000\u0000\u0000\u0000" + // 3160 - 3164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3165 - 3169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3170 - 3174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3175 - 3179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3180 - 3184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1E0\u0000\u0000" + // 3185 - 3189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3190 - 3194 + "\u0000\u0000\u0000\u0000\u0000\uE0A6\u0000\u0000\u0000\uC4DE" + // 3195 - 3199 + "\u0000\u0000\u0000\uE0A8\u0000\uE0A7\u0000\u0000\u0000\u0000" + // 3200 - 3204 + "\u0000\uE0A9\u0000\u0000\u0000\uE0AA\u0000\u0000\u0000\u0000" + // 3205 - 3209 + "\u0000\uBCDF\u0000\uC9E3\u0000\u0000\u0000\u0000\u0000\u0000" + // 3210 - 3214 + "\u0000\uCCEC\u0000\uE0AB\u0000\uE0AC\u0000\uC1D6\u0000\uBCA4" + // 3215 - 3219 + "\u0000\uE0AD\u0000\uF1EF\u0000\u0000\u0000\u0000\u0000\u0000" + // 3220 - 3224 + "\u0000\uBFF1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3225 - 3229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3230 - 3234 + "\u0000\u0000\u0000\uF1F0\u0000\u0000\u0000\uF1F1\u0000\u0000" + // 3235 - 3239 + "\u0000\uF1F2\u0000\uF1F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 3240 - 3244 + "\u0000\uB9E2\u008F\uF4F5\u0000\u0000\u0000\u0000\u008F\uE9ED" + // 3245 - 3249 + "\u0000\u0000\u0000\uF1F4\u0000\uF1F5\u0000\uF1DE\u0000\u0000" + // 3250 - 3254 + "\u0000\uF1DD\u0000\uF1DF\u0000\u0000\u0000\uF1DC\u0000\u0000" + // 3255 - 3259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3260 - 3264 + "\u0000\u0000\u0000\uF1E2\u008F\uE9D1\u0000\u0000\u0000\u0000" + // 3265 - 3269 + "\u0000\u0000\u0000\uC2CD\u0000\u0000\u0000\u0000\u0000\uF1E1" + // 3270 - 3274 + "\u0000\u0000\u0000\uF1E4\u0000\u0000\u0000\u0000\u0000\uB6C3" + // 3275 - 3279 + "\u0000\uF1E3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1E5" + // 3280 - 3284 + "\u0000\uF1B6\u0000\uF1B2\u0000\u0000\u0000\u0000\u0000\uF1B5" + // 3285 - 3289 + "\u0000\u0000\u0000\u0000\u008F\uE8DD\u0000\uB4DB\u0000\u0000" + // 3290 - 3294 + "\u0000\u0000\u0000\u0000\u0000\uF1B7\u0000\u0000\u0000\uF1B8" + // 3295 - 3299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3300 - 3304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3305 - 3309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1B9" + // 3310 - 3314 + "\u0000\uF1BA\u0000\u0000\u0000\uEFC7\u0000\u0000\u0000\u0000" + // 3315 - 3319 + "\u0000\uEFC9\u008F\uE5EA\u0000\u0000\u0000\u0000\u008F\uE5EB" + // 3320 - 3324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3325 - 3329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4D5\u0000\uEFC8" + // 3330 - 3334 + "\u0000\uCCFA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3335 - 3339 + "\u0000\u0000\u0000\u0000\u0000\uEFD4\u0000\uEFCA\u0000\u0000" + // 3340 - 3344 + "\u0000\u0000\u0000\uEFCD\u0000\u0000\u0000\uB0EA\u0000\u0000" + // 3345 - 3349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3350 - 3354 + "\u0000\u0000\u0000\u0000\u0000\uB9D9\u0000\u0000\u0000\u0000" + // 3355 - 3359 + "\u0000\u0000\u0000\uCFBA\u0000\u0000\u0000\u0000\u0000\u0000" + // 3360 - 3364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3365 - 3369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEBE" + // 3370 - 3374 + "\u0000\u0000\u0000\u0000\u008F\uF4E5\u0000\u0000\u0000\uEDAF" + // 3375 - 3379 + "\u0000\u0000\u0000\u0000\u0000\uEDB2\u0000\uEDB1\u0000\u0000" + // 3380 - 3384 + "\u0000\uEDB0\u0000\u0000\u0000\u0000\u0000\uEDB4\u0000\uEDB3" + // 3385 - 3389 + "\u0000\u0000\u0000\uCCF6\u0000\u0000\u0000\u0000\u0000\u0000" + // 3390 - 3394 + "\u0000\uEDB6\u0000\u0000\u0000\uEDB5\u0000\uEDB7\u0000\u0000" + // 3395 - 3399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDB8\u0000\u0000" + // 3400 - 3404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3405 - 3409 + "\u008F\uE3BC\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uE3BF" + // 3410 - 3414 + "\u0000\u0000\u0000\u0000\u0000\uC6DF\u0000\uB3C3\u0000\u0000" + // 3415 - 3419 + "\u008F\uE3C1\u0000\uEEE7\u0000\u0000\u0000\u0000\u0000\uEEE4" + // 3420 - 3424 + "\u0000\uEEE6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3425 - 3429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEE2" + // 3430 - 3434 + "\u0000\u0000\u0000\uEBC6\u0000\u0000\u0000\u0000\u0000\u0000" + // 3435 - 3439 + "\u0000\u0000\u0000\uEBC9\u0000\u0000\u0000\uEBCA\u0000\u0000" + // 3440 - 3444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3445 - 3449 + "\u0000\u0000\u0000\uBABE\u0000\uC2C2\u0000\uEBC8\u0000\u0000" + // 3450 - 3454 + "\u0000\uBEDB\u0000\uC9BE\u0000\u0000\u0000\u0000\u0000\u0000" + // 3455 - 3459 + "\u0000\u0000\u0000\u0000\u0000\uEBC7\u0000\u0000\u0000\u0000" + // 3460 - 3464 + "\u0000\uBBEC\u0000\u0000\u0000\uEAC2\u0000\uEAC1\u0000\uE9DA" + // 3465 - 3469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAC6\u0000\u0000" + // 3470 - 3474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3475 - 3479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAC3\u0000\u0000" + // 3480 - 3484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAC4\u0000\u0000" + // 3485 - 3489 + "\u0000\u0000\u0000\uEAC5\u0000\u0000\u0000\uEAC7\u0000\u0000" + // 3490 - 3494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1EE\u0000\u0000" + // 3495 - 3499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3500 - 3504 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3505 - 3509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3510 - 3514 + "\u0000\u0000\u0000\u0000\u008F\uF3B8\u0000\u0000\u0000\u0000" + // 3515 - 3519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3520 - 3524 + "\u0000\u0000\u0000\u0000\u0000\uBFC9\u0000\uEDE3\u0000\u0000" + // 3525 - 3529 + "\u0000\uBCAD\u0000\uEDE4\u0000\uB5C7\u0000\u0000\u0000\u0000" + // 3530 - 3534 + "\u0000\uF0E4\u0000\u0000\u0000\u0000\u0000\uF0E3\u0000\u0000" + // 3535 - 3539 + "\u0000\uF0E2\u0000\u0000\u0000\u0000\u0000\uEBF1\u0000\u0000" + // 3540 - 3544 + "\u0000\uCADC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3545 - 3549 + "\u0000\u0000\u0000\uF0E5\u0000\uF0E6\u0000\u0000\u0000\u0000" + // 3550 - 3554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3555 - 3559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC9D3" + // 3560 - 3564 + "\u0000\uDFD9\u0000\uC3BA\u0000\uDFDC\u0000\uDFD7\u0000\u0000" + // 3565 - 3569 + "\u0000\u0000\u0000\u0000\u0000\uDFDB\u0000\u0000\u0000\u0000" + // 3570 - 3574 + "\u0000\u0000\u0000\u0000\u0000\uDFDA\u0000\uC5C0\u0000\uB0D9" + // 3575 - 3579 + "\u008F\uF4A1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3580 - 3584 + "\u0000\uB7F5\u0000\uBADE\u0000\uC7ED\u0000\u0000\u0000\u0000" + // 3585 - 3589 + "\u0000\u0000\u0000\uD1F4\u0000\uD1F2\u0000\u0000\u0000\u0000" + // 3590 - 3594 + "\u0000\u0000\u0000\u0000\u0000\uC9FB\u0000\uBEEA\u0000\uD1FB" + // 3595 - 3599 + "\u0000\uB3E4\u0000\uD1F5\u0000\uD1F3\u0000\uC1CF\u0000\u0000" + // 3600 - 3604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3605 - 3609 + "\u0000\u0000\u0000\uD1F7\u0000\u0000\u0000\uD1F6\u0000\uF0D1" + // 3610 - 3614 + "\u0000\uF3D3\u0000\uCCCC\u0000\u0000\u0000\uF0D2\u0000\u0000" + // 3615 - 3619 + "\u0000\uF0D3\u0000\u0000\u0000\uF0D4\u0000\uB3D7\u0000\u0000" + // 3620 - 3624 + "\u0000\uF0D6\u0000\u0000\u0000\uBFD9\u0000\u0000\u0000\u0000" + // 3625 - 3629 + "\u0000\u0000\u0000\uF0D7\u0000\u0000\u0000\u0000\u0000\uB7A4" + // 3630 - 3634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0D8" + // 3635 - 3639 + "\u0000\uF0DC\u0000\u0000\u0000\uF0DA\u0000\u0000\u0000\u0000" + // 3640 - 3644 + "\u0000\u0000\u0000\uD5B9\u0000\u0000\u0000\u0000\u0000\u0000" + // 3645 - 3649 + "\u0000\uC9D8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5BA" + // 3650 - 3654 + "\u0000\u0000\u0000\uD5B5\u0000\u0000\u0000\u0000\u0000\u0000" + // 3655 - 3659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3660 - 3664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3665 - 3669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3670 - 3674 + "\u0000\uCCBB\u0000\uBCFB\u0000\u0000\u0000\u0000\u0000\u0000" + // 3675 - 3679 + "\u0000\uF0BC\u0000\u0000\u0000\uF0BD\u0000\uBFCC\u0000\uF0BE" + // 3680 - 3684 + "\u0000\u0000\u0000\uCEEE\u0000\u0000\u0000\u0000\u0000\uF0B9" + // 3685 - 3689 + "\u0000\uF0C0\u0000\uF0C2\u0000\u0000\u0000\uF0C1\u0000\u0000" + // 3690 - 3694 + "\u0000\uF0BF\u0000\u0000\u0000\u0000\u0000\uF0C3\u0000\u0000" + // 3695 - 3699 + "\u0000\u0000\u0000\uF0C4\u0000\u0000\u0000\u0000\u0000\uC1FA" + // 3700 - 3704 + "\u0000\u0000\u0000\uB2E2\u0000\u0000\u0000\uB6C0\u0000\u0000" + // 3705 - 3709 + "\u0000\u0000\u0000\uEFBB\u0000\uEFB5\u0000\u0000\u0000\u0000" + // 3710 - 3714 + "\u0000\uEFB4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3715 - 3719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3720 - 3724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3725 - 3729 + "\u0000\u0000\u008F\uE5D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 3730 - 3734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2A1" + // 3735 - 3739 + "\u0000\uA1FE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1FB" + // 3740 - 3744 + "\u0000\u0000\u0000\u0000\u0000\uA1FD\u0000\uA1FC\u0000\u0000" + // 3745 - 3749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3750 - 3754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3755 - 3759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3760 - 3764 + "\u0000\uA1BE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1BD" + // 3765 - 3769 + "\u0000\uA1BD\u0000\uA1C2\u0000\u0000\u0000\uA1C6\u0000\uA1C7" + // 3770 - 3774 + "\u0000\u0000\u0000\u0000\u0000\uA1C8\u0000\uA1C9\u0000\u0000" + // 3775 - 3779 + "\u0000\u0000\u0000\u0001\u0000\u0002\u0000\u0003\u0000\u0004" + // 3780 - 3784 + "\u0000\u0005\u0000\u0006\u0000\u0007\u0000\u0008\u0000\u0009" + // 3785 - 3789 + "\u0000\n\u0000\u000B\u0000\u000C\u0000\r\u0000\u000E" + // 3790 - 3794 + "\u0000\u000F\u0000\u0010\u0000\u0011\u0000\u0012\u0000\u0013" + // 3795 - 3799 + "\u0000\u0014\u0000\u0015\u0000\u0016\u0000\u0017\u0000\u0018" + // 3800 - 3804 + "\u0000\u0019\u0000\u001A\u0000\u001B\u0000\u001C\u0000\u001D" + // 3805 - 3809 + "\u0000\u001E\u0000\u001F\u0000\uBFFD\u0000\uB4E7\u0000\u0000" + // 3810 - 3814 + "\u0000\u0000\u0000\uCDBA\u0000\uB2ED\u0000\uBDB8\u0000\uB8DB" + // 3815 - 3819 + "\u0000\u0000\u0000\uF0B5\u0000\u0000\u0000\uF0B4\u0000\uBBF3" + // 3820 - 3824 + "\u0000\uF0B6\u0000\uF0B3\u0000\u0000\u0000\u0000\u0000\uBBA8" + // 3825 - 3829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0BA\u0000\uEAAD" + // 3830 - 3834 + "\u0000\u0000\u0000\u0000\u0000\uD2D6\u0000\u0000\u0000\uBFF7" + // 3835 - 3839 + "\u0000\uF0B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCB4" + // 3840 - 3844 + "\u0000\u0000\u0000\u0000\u0000\uD4EE\u0000\u0000\u0000\uC2E7" + // 3845 - 3849 + "\u0000\u0000\u0000\uC5B7\u0000\uC2C0\u0000\uC9D7\u0000\uD4EF" + // 3850 - 3854 + "\u0000\uD4F0\u0000\uB1FB\u0000\u0000\u0000\u0000\u0000\uBCBA" + // 3855 - 3859 + "\u0000\uD4F1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3860 - 3864 + "\u0000\uB0D0\u0000\uD4F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 3865 - 3869 + "\u0000\u0000\u0000\u0000\u0000\uD4F3\u0000\u0000\u0000\uEEDD" + // 3870 - 3874 + "\u0000\u0000\u0000\uC4E0\u008F\uE3AA\u008F\uE3AB\u0000\uCBD5" + // 3875 - 3879 + "\u0000\uB6FC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3880 - 3884 + "\u0000\u0000\u008F\uE3AF\u008F\uE3B0\u0000\u0000\u0000\u0000" + // 3885 - 3889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEE0" + // 3890 - 3894 + "\u0000\uEEE1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3895 - 3899 + "\u0000\u0000\u0000\uEEDF\u0000\u0000\u0000\u0000\u0000\uEEE3" + // 3900 - 3904 + "\u0000\uB1A3\u0000\u0000\u0000\u0000\u0000\uCED9\u0000\u0000" + // 3905 - 3909 + "\u0000\u0000\u0000\u0000\u0000\uF0AB\u0000\uEEAE\u0000\u0000" + // 3910 - 3914 + "\u0000\uF0AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3915 - 3919 + "\u008F\uE6EF\u0000\uF0AE\u0000\uF0AC\u0000\uF0AD\u0000\u0000" + // 3920 - 3924 + "\u0000\uF0AF\u0000\u0000\u0000\uF0B0\u0000\uCEEC\u0000\uF0B1" + // 3925 - 3929 + "\u0000\uF0B2\u0000\u0000\u0000\uC0C9\u0000\uC8BB\u0000\u0000" + // 3930 - 3934 + "\u0000\u0000\u0000\u0000\u0000\uD4E9\u0000\u0000\u0000\u0000" + // 3935 - 3939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAD1" + // 3940 - 3944 + "\u0000\uD4EA\u008F\uB8E1\u0000\u0000\u0000\u0000\u0000\u0000" + // 3945 - 3949 + "\u0000\uB2C6\u0000\uD4EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 3950 - 3954 + "\u0000\u0000\u0000\uCDBC\u0000\uB3B0\u0000\u0000\u0000\uD2C9" + // 3955 - 3959 + "\u0000\uBDC8\u0000\uC2BF\u0000\uD4EC\u0000\uCCEB\u0000\u0000" + // 3960 - 3964 + "\u0000\u0000\u0000\u0000\u0000\uB5E5\u0000\u0000\u0000\uE0E6" + // 3965 - 3969 + "\u0000\uCDFD\u008F\uCCA5\u0000\u0000\u0000\uCEB0\u0000\u0000" + // 3970 - 3974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3975 - 3979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3980 - 3984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3985 - 3989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 3990 - 3994 + "\u0000\u0000\u0000\uA2CD\u0000\u0000\u0000\uA2CE\u0000\u0000" + // 3995 - 3999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4000 - 4004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4005 - 4009 + "\u0000\uDDDD\u0000\uDDDC\u0000\u0000\u0000\u0000\u0000\uDDDF" + // 4010 - 4014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDDE\u0000\u0000" + // 4015 - 4019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4020 - 4024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4025 - 4029 + "\u0000\u0000\u0000\uDEF6\u0000\u0000\u0000\u0000\u0000\uDEFC" + // 4030 - 4034 + "\u0000\u0000\u0000\u0000\u0000\uDEFA\u0000\u0000\u0000\uC5A9" + // 4035 - 4039 + "\u0000\u0000\u0000\u0000\u0000\uDFA3\u0000\uDEF7\u0000\u0000" + // 4040 - 4044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEF8" + // 4045 - 4049 + "\u0000\uDEE0\u0000\uC2CB\u0000\u0000\u0000\uEFF8\u0000\u0000" + // 4050 - 4054 + "\u0000\uC9ED\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4055 - 4059 + "\u0000\u0000\u0000\u0000\u0000\uEFFB\u0000\uEFF9\u0000\uB9DF" + // 4060 - 4064 + "\u0000\u0000\u0000\uEFFA\u0000\uB8C2\u0000\u0000\u0000\u0000" + // 4065 - 4069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4070 - 4074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAC5\u0000\uEFFD" + // 4075 - 4079 + "\u0000\uF0A1\u0000\uEFFE\u0000\uF0A2\u0000\uEFE0\u0000\u0000" + // 4080 - 4084 + "\u0000\uB4D8\u0000\uB3D5\u0000\uB9DE\u0000\uC8B6\u0000\u0000" + // 4085 - 4089 + "\u0000\uEFE2\u0000\uEFE1\u0000\u0000\u0000\u0000\u0000\u0000" + // 4090 - 4094 + "\u0000\u0000\u0000\uEFE3\u0000\u0000\u0000\u0000\u0000\u0000" + // 4095 - 4099 + "\u0000\u0000\u0000\uB1DC\u0000\u0000\u0000\u0000\u0000\u0000" + // 4100 - 4104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFE6\u0000\u0000" + // 4105 - 4109 + "\u0000\uEFE5\u0000\uEFE4\u0000\u0000\u0000\uEFE7\u0000\u0000" + // 4110 - 4114 + "\u0000\uB7B4\u0000\uEEBB\u0000\u0000\u0000\uEEBC\u0000\u0000" + // 4115 - 4119 + "\u0000\u0000\u0000\u0000\u0000\uC9F4\u0000\u0000\u0000\u0000" + // 4120 - 4124 + "\u0000\u0000\u0000\u0000\u0000\uB3D4\u0000\u0000\u0000\u0000" + // 4125 - 4129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4130 - 4134 + "\u0000\uCDB9\u0000\u0000\u0000\uB6BF\u0000\u0000\u0000\u0000" + // 4135 - 4139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5D4\u0000\u0000" + // 4140 - 4144 + "\u0000\u0000\u0000\uF1E6\u0000\u0000\u0000\uF1E8\u0000\uF1E7" + // 4145 - 4149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1E9\u0000\uF1EB" + // 4150 - 4154 + "\u0000\uF1EA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4155 - 4159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4160 - 4164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4165 - 4169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4170 - 4174 + "\u0000\u0000\u008F\uF3B7\u0000\u0000\u0000\u0000\u0000\u0000" + // 4175 - 4179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4180 - 4184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4185 - 4189 + "\u0000\u0000\u0000\uD7E3\u0000\uD7E9\u0000\uD7E1\u0000\u0000" + // 4190 - 4194 + "\u0000\uC5DC\u0000\u0000\u0000\u0000\u0000\uD7E6\u0000\uC9DD" + // 4195 - 4199 + "\u0000\u0000\u0000\u0000\u0000\uD7E0\u0000\u0000\u0000\uD7E5" + // 4200 - 4204 + "\u0000\uCEE7\u0000\uBBD7\u0000\u0000\u0000\u0000\u0000\uF1F6" + // 4205 - 4209 + "\u0000\uF1F7\u0000\u0000\u0000\u0000\u0000\uF1F8\u0000\u0000" + // 4210 - 4214 + "\u0000\u0000\u0000\u0000\u0000\uC8B1\u0000\uF1FA\u0000\u0000" + // 4215 - 4219 + "\u0000\uC9A6\u0000\uF1FB\u0000\uF1F9\u0000\u0000\u0000\uF1FD" + // 4220 - 4224 + "\u0000\u0000\u0000\u0000\u0000\uF1FC\u0000\u0000\u0000\u0000" + // 4225 - 4229 + "\u0000\uF1FE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2A1" + // 4230 - 4234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3C4" + // 4235 - 4239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7E0\u0000\uD1FC" + // 4240 - 4244 + "\u0000\uCEAD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1F8" + // 4245 - 4249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1FD\u0000\uD1FA" + // 4250 - 4254 + "\u0000\u0000\u0000\uD1F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 4255 - 4259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCECF\u008F\uB3D8" + // 4260 - 4264 + "\u0000\u0000\u0000\u0000\u0000\uB8F9\u0000\uCCE7\u0000\u0000" + // 4265 - 4269 + "\u0000\uEFD9\u0000\uC1AE\u0000\u0000\u0000\u0000\u0000\u0000" + // 4270 - 4274 + "\u0000\uEFDA\u0000\u0000\u0000\uCAC4\u0000\uEFDB\u0000\uB3AB" + // 4275 - 4279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1BC\u0000\u0000" + // 4280 - 4284 + "\u0000\uB4D7\u008F\uF4EA\u0000\uB4D6\u0000\uEFDC\u0000\u0000" + // 4285 - 4289 + "\u0000\uEFDD\u0000\u0000\u0000\uEFDE\u0000\uEFDF\u0000\u0000" + // 4290 - 4294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4295 - 4299 + "\u0000\uF2E3\u0000\uF2E1\u0000\uC3AD\u0000\u0000\u0000\u0000" + // 4300 - 4304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4305 - 4309 + "\u0000\u0000\u0000\u0000\u0000\uCBF0\u0000\u0000\u0000\u0000" + // 4310 - 4314 + "\u0000\u0000\u0000\u0000\u0000\uCEDA\u0000\u0000\u0000\u0000" + // 4315 - 4319 + "\u0000\uF2E5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4320 - 4324 + "\u0000\u0000\u0000\uB1B4\u0000\u0000\u0000\uBAB6\u0000\uBFB0" + // 4325 - 4329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4330 - 4334 + "\u0000\u0000\u0000\u0000\u0000\uD3A9\u0000\uC5E2\u0000\u0000" + // 4335 - 4339 + "\u0000\u0000\u0000\u0000\u0000\uD3AA\u0000\u0000\u0000\uB0A2" + // 4340 - 4344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4345 - 4349 + "\u0000\u0000\u0000\u0000\u0000\uC0E6\u0000\u0000\u0000\uCFB9" + // 4350 - 4354 + "\u0000\uE9F8\u0000\u0000\u0000\uE9F9\u0000\u0000\u0000\u0000" + // 4355 - 4359 + "\u0000\u0000\u0000\u0000\u0000\uEAA1\u0000\u0000\u0000\uBFAA" + // 4360 - 4364 + "\u0000\u0000\u0000\uE9FB\u0000\u0000\u0000\uE9FE\u0000\u0000" + // 4365 - 4369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9F6" + // 4370 - 4374 + "\u0000\uEFCB\u0000\u0000\u0000\uEFCC\u0000\u0000\u0000\u0000" + // 4375 - 4379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4380 - 4384 + "\u0000\uEFCE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4385 - 4389 + "\u0000\u0000\u0000\uEFD0\u0000\u0000\u0000\u0000\u0000\u0000" + // 4390 - 4394 + "\u0000\u0000\u0000\uEFD1\u0000\u0000\u0000\uEFD2\u0000\u0000" + // 4395 - 4399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFD5\u0000\uEFD3" + // 4400 - 4404 + "\u0000\uEFD6\u0000\uEFD8\u0000\uEFA9\u0000\u0000\u0000\u0000" + // 4405 - 4409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4410 - 4414 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7AD" + // 4415 - 4419 + "\u0000\u0000\u0000\uEFAB\u0000\u0000\u008F\uE4FA\u0000\u0000" + // 4420 - 4424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8B0\u0000\u0000" + // 4425 - 4429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4430 - 4434 + "\u0000\uEFAA\u0000\u0000\u0000\uBEE1\u0000\u0000\u0000\uC6DB" + // 4435 - 4439 + "\u0000\uBFEB\u0000\u0000\u0000\u0000\u0000\uC3D9\u0000\u0000" + // 4440 - 4444 + "\u0000\uB6F8\u0000\u0000\u0000\uEEA6\u0000\uCDB7\u0000\uB1BF" + // 4445 - 4449 + "\u0000\u0000\u0000\uCAD7\u0000\uB2E1\u0000\uEEA1\u0000\uEEA2" + // 4450 - 4454 + "\u0000\uEEA3\u0000\uEEA4\u0000\uC6BB\u0000\uC3A3\u0000\uB0E3" + // 4455 - 4459 + "\u0000\uEEA8\u0000\u0000\u0000\uEEA9\u0000\uF4A3\u0000\u0000" + // 4460 - 4464 + "\u0000\u0000\u0000\uC2BD\u0000\u0000\u0000\uEEAA\u0000\u0000" + // 4465 - 4469 + "\u0000\uECE3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4B6" + // 4470 - 4474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1DB" + // 4475 - 4479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4480 - 4484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4485 - 4489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECE4" + // 4490 - 4494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4495 - 4499 + "\u0000\u0000\u0000\uEDD3\u0000\u0000\u0000\u0000\u0000\uC7DA" + // 4500 - 4504 + "\u0000\uCED8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4505 - 4509 + "\u0000\uBDB4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDD4" + // 4510 - 4514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDA2" + // 4515 - 4519 + "\u0000\uEDD6\u0000\u0000\u0000\uEDD5\u0000\u0000\u0000\u0000" + // 4520 - 4524 + "\u0000\uEDD9\u0000\uCDC1\u0000\uBEFB\u008F\uE4DE\u0000\uEFA2" + // 4525 - 4529 + "\u0000\uEFA4\u0000\u0000\u008F\uE4E0\u0000\uB6D3\u0000\u0000" + // 4530 - 4534 + "\u0000\uC9C5\u0000\u0000\u0000\u0000\u0000\uBCE2\u0000\uCFA3" + // 4535 - 4539 + "\u0000\u0000\u0000\uEEFE\u0000\uBAF8\u0000\u0000\u0000\u0000" + // 4540 - 4544 + "\u0000\uCFBF\u0000\u0000\u0000\u0000\u0000\uEFA6\u0000\u0000" + // 4545 - 4549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFA5\u0000\uEFA7" + // 4550 - 4554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4A6" + // 4555 - 4559 + "\u0000\u0000\u0000\u0000\u0000\uD0D4\u0000\u0000\u0000\uD0CC" + // 4560 - 4564 + "\u0000\u0000\u008F\uB1A3\u0000\uCEE3\u0000\u0000\u0000\uBBF8" + // 4565 - 4569 + "\u0000\u0000\u0000\uD0CD\u0000\u0000\u0000\uD0D2\u008F\uB1A7" + // 4570 - 4574 + "\u0000\u0000\u008F\uB1A9\u0000\u0000\u0000\uD0D5\u0000\u0000" + // 4575 - 4579 + "\u0000\uD0CE\u0000\u0000\u008F\uB1AC\u0000\uB6A1\u0000\u0000" + // 4580 - 4584 + "\u0000\uB0CD\u0000\u0000\u0000\u0000\u0000\uF2A6\u0000\uF2A7" + // 4585 - 4589 + "\u0000\u0000\u0000\uF2A8\u0000\u0000\u0000\uF2A9\u0000\uF2AA" + // 4590 - 4594 + "\u0000\uF2AB\u0000\uF2AC\u0000\u0000\u0000\u0000\u0000\u0000" + // 4595 - 4599 + "\u0000\uF2AD\u0000\uF2AE\u0000\u0000\u0000\uDDB5\u0000\uF2AF" + // 4600 - 4604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4605 - 4609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4F8\u0000\uB5B4" + // 4610 - 4614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6C4\u0000\u0000" + // 4615 - 4619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uBBCA\u0000\u0000" + // 4620 - 4624 + "\u0000\uCAF8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4625 - 4629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4630 - 4634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4635 - 4639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4640 - 4644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3C3\u0000\u0000" + // 4645 - 4649 + "\u0000\u0000\u0000\uB8B4\u0000\uF3C4\u0000\u0000\u0000\u0000" + // 4650 - 4654 + "\u0000\uB8B4\u0000\uF3C5\u0000\u0000\u0000\uBCAF\u008F\uE4C7" + // 4655 - 4659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDFB\u0000\u0000" + // 4660 - 4664 + "\u0000\u0000\u008F\uE4CB\u0000\u0000\u0000\uEEFA\u0000\uCADF" + // 4665 - 4669 + "\u0000\u0000\u0000\u0000\u0000\uB1D4\u0000\u0000\u0000\u0000" + // 4670 - 4674 + "\u0000\u0000\u0000\u0000\u0000\uC9C6\u0000\uC3F2\u0000\u0000" + // 4675 - 4679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5F8\u008F\uE4D0" + // 4680 - 4684 + "\u0000\uEEFC\u008F\uE4D1\u0000\uB9DD\u0000\u0000\u0000\u0000" + // 4685 - 4689 + "\u008F\uE4D2\u0000\uB6E4\u0000\u0000\u0000\u0000\u0000\uBDC6" + // 4690 - 4694 + "\u0000\u0000\u0000\uC6BC\u0000\u0000\u0000\u0000\u008F\uE3F8" + // 4695 - 4699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4700 - 4704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1AD\u0000\u0000" + // 4705 - 4709 + "\u0000\uEEF4\u0000\u0000\u0000\uEEEE\u0000\uEEF3\u0000\u0000" + // 4710 - 4714 + "\u0000\uCCC3\u0000\u0000\u0000\uC4B8\u0000\uEEF5\u0000\uEEF2" + // 4715 - 4719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2F8\u0000\u0000" + // 4720 - 4724 + "\u0000\uD3A3\u0000\uD2FA\u0000\u0000\u0000\u0000\u0000\uD2FE" + // 4725 - 4729 + "\u008F\uB5AF\u0000\u0000\u0000\uD3A1\u0000\uD2FB\u0000\u0000" + // 4730 - 4734 + "\u0000\u0000\u0000\uD3BE\u0000\u0000\u0000\u0000\u0000\uBAE9" + // 4735 - 4739 + "\u0000\uB3B1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4740 - 4744 + "\u0000\uD2F9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3A5" + // 4745 - 4749 + "\u0000\uB0F6\u0000\uD3A4\u0000\u0000\u0000\uB7A5\u0000\uEDE0" + // 4750 - 4754 + "\u0000\uEDE1\u0000\uEDE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 4755 - 4759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4760 - 4764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4765 - 4769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4770 - 4774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4775 - 4779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uECD6" + // 4780 - 4784 + "\u0000\uF3C0\u0000\uF3C1\u0000\u0000\u0000\u0000\u0000\uF3C2" + // 4785 - 4789 + "\u0000\u0000\u0000\uECAF\u0000\u0000\u0000\u0000\u0000\u0000" + // 4790 - 4794 + "\u0000\u0000\u0000\uC6A6\u0000\u0000\u0000\uECB1\u0000\u0000" + // 4795 - 4799 + "\u0000\uCBAD\u0000\u0000\u0000\uECB2\u0000\u0000\u0000\uECB3" + // 4800 - 4804 + "\u0000\u0000\u0000\uECB4\u0000\u0000\u0000\u0000\u0000\u0000" + // 4805 - 4809 + "\u0000\u0000\u0000\uECB5\u0000\u0000\u0000\u0000\u0000\u0000" + // 4810 - 4814 + "\u0000\u0000\u0000\uC6DA\u0000\u0000\u0000\u0000\u0000\u0000" + // 4815 - 4819 + "\u0000\u0000\u0000\u0000\u008F\uE8B6\u0000\u0000\u0000\uB8DC" + // 4820 - 4824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0FC\u0000\u0000" + // 4825 - 4829 + "\u0000\u0000\u0000\u0000\u0000\uF0FD\u0000\uF0FE\u0000\uF1A1" + // 4830 - 4834 + "\u0000\u0000\u0000\uF1A3\u0000\uF1A2\u0000\u0000\u0000\u0000" + // 4835 - 4839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4840 - 4844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2D3" + // 4845 - 4849 + "\u0000\u0000\u0000\u0000\u0000\uE2D2\u0000\u0000\u0000\u0000" + // 4850 - 4854 + "\u0000\uE2D4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4855 - 4859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4860 - 4864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2D6\u0000\u0000" + // 4865 - 4869 + "\u0000\uEAF6\u0000\u0000\u0000\uEAF1\u0000\uEAF7\u0000\u0000" + // 4870 - 4874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4875 - 4879 + "\u0000\u0000\u0000\uEAFB\u0000\uF0B7\u0000\u0000\u0000\u0000" + // 4880 - 4884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4885 - 4889 + "\u0000\u0000\u0000\uB2A8\u0000\u0000\u0000\u0000\u0000\u0000" + // 4890 - 4894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAFE" + // 4895 - 4899 + "\u0000\uB6DF\u008F\uE3D8\u0000\u0000\u0000\u0000\u0000\u0000" + // 4900 - 4904 + "\u0000\uC5B4\u0000\uEEEA\u0000\u0000\u0000\u0000\u0000\uEEED" + // 4905 - 4909 + "\u0000\uEEEB\u0000\u0000\u0000\uEEF0\u0000\u0000\u0000\u0000" + // 4910 - 4914 + "\u008F\uE3DF\u0000\u0000\u0000\uEEF1\u008F\uE3E1\u0000\u0000" + // 4915 - 4919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEE9" + // 4920 - 4924 + "\u0000\u0000\u008F\uE3E3\u0000\uEEF6\u0000\uB1F4\u0000\u0000" + // 4925 - 4929 + "\u0000\u0000\u0000\uEEE8\u0000\u0000\u0000\uEDBA\u0000\u0000" + // 4930 - 4934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4935 - 4939 + "\u0000\u0000\u0000\u0000\u0000\uEDB9\u0000\uBFC8\u0000\uEDBB" + // 4940 - 4944 + "\u0000\u0000\u0000\u0000\u0000\uB6ED\u0000\uEDBC\u0000\uEDBE" + // 4945 - 4949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4950 - 4954 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4955 - 4959 + "\u0000\u0000\u0000\u0000\u0000\uEDBF\u0000\u0000\u0000\uEBF6" + // 4960 - 4964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 4965 - 4969 + "\u0000\u0000\u0000\u0000\u0000\uEBFA\u0000\u0000\u0000\u0000" + // 4970 - 4974 + "\u0000\uEBF7\u0000\u0000\u0000\uEBF9\u0000\uEBF8\u0000\u0000" + // 4975 - 4979 + "\u0000\u0000\u0000\u0000\u008F\uDECB\u0000\u0000\u0000\u0000" + // 4980 - 4984 + "\u0000\uEBFB\u0000\u0000\u0000\uBCB1\u0000\u0000\u0000\uEBFD" + // 4985 - 4989 + "\u0000\uEBFC\u0000\uC9E8\u0000\u0000\u0000\u0000\u0000\uECA1" + // 4990 - 4994 + "\u0000\uEED6\u0000\uEED7\u0000\u0000\u0000\u0000\u0000\u0000" + // 4995 - 4999 + "\u0000\u0000\u0000\uC8D0\u0000\uBAD3\u0000\uBCE1\u0000\uEED8" + // 5000 - 5004 + "\u0000\u0000\u0000\uEED9\u0000\uCEA4\u0000\uBDC5\u0000\uCCEE" + // 5005 - 5009 + "\u0000\uCECC\u0000\uEEDA\u0000\uB6E2\u0000\u0000\u0000\u0000" + // 5010 - 5014 + "\u0000\u0000\u0000\u0000\u0000\uEEDB\u008F\uE3A6\u0000\uC5A3" + // 5015 - 5019 + "\u0000\u0000\u008F\uE3A8\u0000\uEEDE\u0000\uB3F8\u0000\uBFCB" + // 5020 - 5024 + "\u008F\uE3A9\u0000\uEEDC\u0000\uEEB3\u0000\uEEB2\u0000\uEEB0" + // 5025 - 5029 + "\u0000\uE3E4\u0000\uB4D4\u0000\u0000\u0000\u0000\u0000\uEDEE" + // 5030 - 5034 + "\u0000\u0000\u0000\uEEB5\u0000\uEEB4\u0000\u0000\u0000\u0000" + // 5035 - 5039 + "\u0000\u0000\u0000\u0000\u0000\uEEB6\u0000\u0000\u0000\uCDB8" + // 5040 - 5044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5045 - 5049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5050 - 5054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3B4" + // 5055 - 5059 + "\u0000\uCDA3\u0000\u0000\u0000\uBEA7\u0000\u0000\u0000\uD3BA" + // 5060 - 5064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3B9" + // 5065 - 5069 + "\u0000\uD3B0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5070 - 5074 + "\u0000\uC2C3\u0000\u0000\u0000\uC0D7\u0000\u0000\u0000\uECF1" + // 5075 - 5079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8D9" + // 5080 - 5084 + "\u0000\u0000\u0000\uECEE\u0000\uECEF\u0000\u0000\u0000\u0000" + // 5085 - 5089 + "\u0000\u0000\u0000\uCFA9\u0000\u0000\u0000\u0000\u0000\u0000" + // 5090 - 5094 + "\u0000\uC4B7\u0000\u0000\u0000\uC1A9\u0000\u0000\u0000\u0000" + // 5095 - 5099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECF2" + // 5100 - 5104 + "\u0000\u0000\u0000\u0000\u0000\uECF5\u0000\uB1F3\u0000\uC1CC" + // 5105 - 5109 + "\u0000\u0000\u0000\uB8AF\u0000\u0000\u0000\uCDDA\u0000\u0000" + // 5110 - 5114 + "\u008F\uE1E2\u0000\uEEAB\u0000\uC5AC\u0000\u0000\u0000\u0000" + // 5115 - 5119 + "\u0000\u0000\u0000\uC1F8\u0000\uBCD7\u0000\uEEAC\u0000\u0000" + // 5120 - 5124 + "\u0000\u0000\u0000\uEEAF\u0000\u0000\u0000\u0000\u0000\uBDE5" + // 5125 - 5129 + "\u0000\uEEAD\u0000\uC1AB\u0000\uC1AA\u0000\u0000\u0000\uB0E4" + // 5130 - 5134 + "\u0000\u0000\u0000\uCECB\u0000\uEEB1\u0000\u0000\u0000\uC8F2" + // 5135 - 5139 + "\u0000\uC2A4\u0000\uEDF5\u0000\uB0A9\u0000\uCFA2\u0000\u0000" + // 5140 - 5144 + "\u0000\u0000\u0000\u0000\u0000\uEDFA\u0000\u0000\u0000\u0000" + // 5145 - 5149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2E1" + // 5150 - 5154 + "\u0000\u0000\u0000\u0000\u0000\uBDB5\u0000\uBFCA\u0000\u0000" + // 5155 - 5159 + "\u0000\u0000\u0000\uEDFC\u0000\uEDFB\u0000\u0000\u0000\uB0EF" + // 5160 - 5164 + "\u0000\uEDFD\u0000\u0000\u0000\u0000\u0000\uC9AF\u0000\u0000" + // 5165 - 5169 + "\u0000\uEEA7\u0000\u0000\u0000\uBEDD\u0000\uECB6\u0000\u0000" + // 5170 - 5174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5175 - 5179 + "\u0000\u0000\u0000\uB9EB\u0000\uD0AE\u0000\uECB7\u0000\u0000" + // 5180 - 5184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5185 - 5189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5190 - 5194 + "\u0000\uECB8\u0000\uC9BF\u0000\uECB9\u0000\u0000\u0000\uECC1" + // 5195 - 5199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7B8\u0000\uC2A5" + // 5200 - 5204 + "\u0000\uB2E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5205 - 5209 + "\u008F\uB1BB\u0000\uBDD3\u0000\u0000\u0000\u0000\u008F\uB1BE" + // 5210 - 5214 + "\u0000\uD0D9\u0000\u0000\u0000\uD0DE\u0000\uD0DC\u0000\u0000" + // 5215 - 5219 + "\u0000\u0000\u0000\uD0D7\u0000\u0000\u0000\u0000\u0000\uC2AF" + // 5220 - 5224 + "\u0000\uD0DA\u0000\u0000\u0000\uD0DD\u0000\uD0DB\u0000\u0000" + // 5225 - 5229 + "\u0000\uCADD\u0000\u0000\u0000\uD0D8\u0000\uC2E0\u0000\uC1F7" + // 5230 - 5234 + "\u0000\u0000\u0000\uC6A8\u0000\u0000\u0000\uEDF0\u0000\uB5D5" + // 5235 - 5239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDF9" + // 5240 - 5244 + "\u0000\u0000\u0000\uEDF6\u0000\uEEA5\u0000\uC6A9\u0000\uC3E0" + // 5245 - 5249 + "\u0000\uEDF3\u0000\u0000\u0000\uC4FE\u0000\uC5D3\u0000\uEDF4" + // 5250 - 5254 + "\u0000\uEDF8\u0000\uBFE0\u0000\u0000\u0000\uC7E7\u0000\uC4CC" + // 5255 - 5259 + "\u0000\u0000\u0000\u0000\u0000\uC0C2\u0000\uEDF7\u0000\uC2AE" + // 5260 - 5264 + "\u0000\uB6ED\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5265 - 5269 + "\u0000\uEDC0\u0000\uEDBD\u0000\u0000\u0000\uEDC1\u0000\u0000" + // 5270 - 5274 + "\u0000\uBCD6\u0000\uEDC2\u0000\uB5B0\u0000\uB7B3\u0000\u0000" + // 5275 - 5279 + "\u008F\uE0D9\u0000\u0000\u0000\u0000\u0000\uB8AE\u0000\u0000" + // 5280 - 5284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5285 - 5289 + "\u0000\u0000\u0000\u0000\u0000\uEDC3\u0000\u0000\u0000\u0000" + // 5290 - 5294 + "\u0000\u0000\u0000\uC6F0\u0000\uEDA8\u0000\u0000\u0000\u0000" + // 5295 - 5299 + "\u0000\uEDAA\u0000\uEDA7\u0000\u0000\u0000\u0000\u0000\u0000" + // 5300 - 5304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5305 - 5309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5310 - 5314 + "\u0000\uEDAD\u0000\u0000\u0000\uBDB3\u0000\u0000\u0000\uEDAC" + // 5315 - 5319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5320 - 5324 + "\u0000\uEDAE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1A4" + // 5325 - 5329 + "\u0000\u0000\u0000\u0000\u0000\uB6CF\u0000\u0000\u0000\u0000" + // 5330 - 5334 + "\u0000\u0000\u0000\uD1A1\u0000\uD1A2\u0000\u0000\u0000\u0000" + // 5335 - 5339 + "\u0000\uC6AF\u0000\u0000\u0000\uC1FC\u0000\u0000\u0000\uB6A3" + // 5340 - 5344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBCD\u0000\uD1A5" + // 5345 - 5349 + "\u0000\u0000\u008F\uB2BB\u0000\u0000\u0000\uCEBD\u0000\u0000" + // 5350 - 5354 + "\u0000\u0000\u0000\u0000\u0000\uD1A6\u0000\u0000\u0000\uEBAE" + // 5355 - 5359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBB0" + // 5360 - 5364 + "\u0000\uCDF7\u0000\u0000\u0000\uEBAF\u0000\uBFC6\u0000\u0000" + // 5365 - 5369 + "\u0000\uEBB1\u0000\u0000\u0000\u0000\u0000\uEBB2\u0000\u0000" + // 5370 - 5374 + "\u0000\u0000\u0000\uEBB3\u0000\uB4D1\u0000\u0000\u0000\u0000" + // 5375 - 5379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBB4" + // 5380 - 5384 + "\u0000\u0000\u0000\u0000\u0000\uEBB5\u0000\u0000\u0000\uEBB6" + // 5385 - 5389 + "\u0000\uC7E5\u0000\u0000\u0000\uB8AD\u0000\uECCE\u0000\uECCD" + // 5390 - 5394 + "\u0000\u0000\u0000\uC9EA\u0000\u0000\u0000\u0000\u0000\u0000" + // 5395 - 5399 + "\u0000\uBCC1\u0000\u0000\u0000\u0000\u0000\uC5D2\u0000\u0000" + // 5400 - 5404 + "\u0000\u0000\u008F\uDFB9\u0000\u0000\u0000\u0000\u0000\u0000" + // 5405 - 5409 + "\u008F\uF4E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5410 - 5414 + "\u0000\u0000\u0000\uECD1\u0000\uECD2\u0000\uB9D8\u0000\uECD0" + // 5415 - 5419 + "\u0000\u0000\u0000\u0000\u0000\uF1D8\u0000\u0000\u0000\u0000" + // 5420 - 5424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5425 - 5429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1CF\u0000\uF1D0" + // 5430 - 5434 + "\u0000\u0000\u0000\u0000\u0000\uF1D1\u0000\uF1D2\u0000\u0000" + // 5435 - 5439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1D4" + // 5440 - 5444 + "\u0000\u0000\u0000\u0000\u0000\uF1D3\u0000\u0000\u0000\u0000" + // 5445 - 5449 + "\u0000\u0000\u0000\uBDD9\u0000\uB2EC\u0000\uECCC\u0000\uCFA8" + // 5450 - 5454 + "\u0000\uC4C2\u0000\uCFC5\u0000\u0000\u0000\u0000\u0000\uBBF1" + // 5455 - 5459 + "\u0000\uECCB\u0000\u0000\u0000\uC2B1\u0000\u0000\u0000\u0000" + // 5460 - 5464 + "\u0000\uECDC\u0000\uC1A8\u0000\u0000\u0000\u0000\u0000\uC6F8" + // 5465 - 5469 + "\u0000\u0000\u0000\uC9D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 5470 - 5474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECCF\u0000\uBBBF" + // 5475 - 5479 + "\u0000\uBBF2\u0000\u0000\u0000\uBEDE\u0000\u0000\u0000\uCDD7" + // 5480 - 5484 + "\u0000\u0000\u0000\uEBA9\u0000\u0000\u0000\u0000\u0000\uCAA4" + // 5485 - 5489 + "\u0000\uC7C6\u0000\uEBAA\u0000\u0000\u0000\uEBAB\u0000\uB8AB" + // 5490 - 5494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5AC\u0000\u0000" + // 5495 - 5499 + "\u0000\u0000\u0000\u0000\u0000\uEBAC\u0000\u0000\u0000\u0000" + // 5500 - 5504 + "\u0000\uBBEB\u0000\uC7C1\u0000\uEBAD\u0000\u0000\u0000\uB3D0" + // 5505 - 5509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5510 - 5514 + "\u0000\uEAF3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5515 - 5519 + "\u0000\uEAF4\u0000\uEAF5\u0000\u0000\u0000\u0000\u0000\u0000" + // 5520 - 5524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5525 - 5529 + "\u0000\u0000\u0000\u0000\u0000\uEAF9\u0000\u0000\u0000\uEAFA" + // 5530 - 5534 + "\u0000\u0000\u0000\u0000\u0000\uEAF8\u0000\u0000\u0000\u0000" + // 5535 - 5539 + "\u0000\u0000\u0000\u0000\u0000\uC4D1\u0000\u0000\u0000\u0000" + // 5540 - 5544 + "\u0000\uC3A2\u0000\uD0CA\u0000\u0000\u0000\u0000\u0000\u0000" + // 5545 - 5549 + "\u0000\u0000\u0000\u0000\u0000\uB0CC\u0000\uC4E3\u0000\uBDBB" + // 5550 - 5554 + "\u0000\uBAB4\u0000\uCDA4\u0000\u0000\u0000\uC2CE\u0000\u0000" + // 5555 - 5559 + "\u0000\uB2BF\u008F\uB0EE\u0000\uD0C9\u0000\u0000\u0000\uCDBE" + // 5560 - 5564 + "\u0000\uD0C5\u0000\uD0C7\u0000\uBAEE\u0000\uD0C8\u0000\uD5A4" + // 5565 - 5569 + "\u0000\u0000\u0000\uE8F1\u0000\u0000\u0000\uBED5\u0000\u0000" + // 5570 - 5574 + "\u0000\u0000\u0000\uC4D5\u0000\u0000\u0000\u0000\u0000\u0000" + // 5575 - 5579 + "\u0000\u0000\u0000\u0000\u0000\uE8F6\u0000\uB0FE\u0000\u0000" + // 5580 - 5584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5585 - 5589 + "\u0000\u0000\u0000\uC2A2\u0000\u0000\u0000\u0000\u0000\u0000" + // 5590 - 5594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAC3" + // 5595 - 5599 + "\u0000\u0000\u0000\u0000\u008F\uE4D4\u0000\u0000\u0000\u0000" + // 5600 - 5604 + "\u0000\u0000\u0000\uBBAC\u0000\u0000\u0000\u0000\u0000\u0000" + // 5605 - 5609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5610 - 5614 + "\u0000\uEEFB\u0000\uBFED\u0000\u0000\u0000\u0000\u0000\u0000" + // 5615 - 5619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFEE" + // 5620 - 5624 + "\u0000\uEFA1\u0000\uEFA3\u0000\u0000\u0000\u0000\u008F\uE4DB" + // 5625 - 5629 + "\u008F\uE4DC\u0000\u0000\u0000\uE7EF\u0000\u0000\u0000\uE7F0" + // 5630 - 5634 + "\u0000\u0000\u0000\uBCE3\u0000\uB6EC\u0000\uC3F7\u0000\u0000" + // 5635 - 5639 + "\u0000\u0000\u0000\u0000\u0000\uC6D1\u0000\u0000\u0000\u0000" + // 5640 - 5644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1D1\u0000\u0000" + // 5645 - 5649 + "\u0000\uE7F4\u0000\uE7F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 5650 - 5654 + "\u0000\u0000\u0000\uE7F9\u0000\uE7F5\u0000\uE7F8\u0000\u0000" + // 5655 - 5659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0C5\u0000\u0000" + // 5660 - 5664 + "\u0000\u0000\u0000\uCCB8\u0000\u0000\u0000\u0000\u0000\uF0C6" + // 5665 - 5669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5670 - 5674 + "\u0000\uF0C7\u0000\u0000\u0000\uCFAA\u008F\uE7AC\u0000\u0000" + // 5675 - 5679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBB1\u0000\uF0C8" + // 5680 - 5684 + "\u0000\u0000\u008F\uF4ED\u0000\u0000\u0000\uF0C9\u0000\uF0CA" + // 5685 - 5689 + "\u0000\u0000\u0000\uE7A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 5690 - 5694 + "\u0000\u0000\u0000\uE7A2\u0000\u0000\u0000\u0000\u0000\u0000" + // 5695 - 5699 + "\u0000\u0000\u0000\uE6FE\u0000\u0000\u0000\u0000\u0000\uBFD5" + // 5700 - 5704 + "\u0000\u0000\u0000\uC9E5\u0000\uE7A5\u0000\u0000\u0000\uE7A4" + // 5705 - 5709 + "\u0000\uB9D0\u0000\uCFD3\u0000\u0000\u0000\u0000\u0000\u0000" + // 5710 - 5714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5715 - 5719 + "\u0000\u0000\u0000\uE7B5\u0000\uC9E9\u0000\uBAE2\u0000\uB9D7" + // 5720 - 5724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9CF" + // 5725 - 5729 + "\u0000\uB2DF\u0000\uC8CE\u0000\uECC5\u0000\uB4D3\u0000\uC0D5" + // 5730 - 5734 + "\u0000\uECC4\u0000\uECC9\u0000\uC3F9\u0000\uCCE3\u0000\u0000" + // 5735 - 5739 + "\u0000\uECC7\u0000\uECC8\u0000\uB5AE\u0000\u0000\u0000\uECCA" + // 5740 - 5744 + "\u0000\uC7E3\u0000\uC2DF\u0000\u0000\u0000\u0000\u0000\uC8F1" + // 5745 - 5749 + "\u0000\uC5BD\u0000\uECC6\u0000\u0000\u0000\uCBC7\u0000\uECA6" + // 5750 - 5754 + "\u0000\u0000\u0000\u0000\u0000\uBBBE\u0000\u0000\u0000\u0000" + // 5755 - 5759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDACE" + // 5760 - 5764 + "\u0000\u0000\u0000\uECA7\u0000\u0000\u0000\uECA8\u0000\u0000" + // 5765 - 5769 + "\u0000\uBDB2\u0000\u0000\u0000\uECA9\u0000\uECAA\u0000\u0000" + // 5770 - 5774 + "\u0000\u0000\u0000\uECAB\u0000\u0000\u0000\u0000\u0000\uECAC" + // 5775 - 5779 + "\u0000\uECAD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5780 - 5784 + "\u0000\u0000\u0000\uB6F9\u0000\uCEB4\u0000\u0000\u0000\uB7A8" + // 5785 - 5789 + "\u0000\u0000\u0000\uC2E2\u0000\uE7A1\u0000\u0000\u0000\uF0A6" + // 5790 - 5794 + "\u0000\uB3AC\u0000\uBFEF\u0000\u0000\u0000\u0000\u0000\u0000" + // 5795 - 5799 + "\u0000\u0000\u0000\uB3D6\u0000\uF0A8\u0000\u0000\u0000\uF0A9" + // 5800 - 5804 + "\u0000\uF0A7\u0000\uB7E4\u0000\u0000\u0000\uBADD\u0000\uBEE3" + // 5805 - 5809 + "\u008F\uE6E8\u0000\u0000\u0000\u0000\u0000\uF1BB\u0000\u0000" + // 5810 - 5814 + "\u0000\u0000\u0000\uF1BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 5815 - 5819 + "\u0000\uF1BC\u0000\u0000\u0000\uF1BF\u0000\uF1C2\u0000\u0000" + // 5820 - 5824 + "\u0000\u0000\u0000\u0000\u0000\uF1BE\u0000\uF1C0\u0000\uF1C1" + // 5825 - 5829 + "\u0000\u0000\u0000\u0000\u0000\uF1C3\u0000\u0000\u0000\uB6C2" + // 5830 - 5834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5835 - 5839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECF3\u0000\uECF4" + // 5840 - 5844 + "\u0000\uCDD9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5845 - 5849 + "\u0000\uC6A7\u0000\uECF8\u0000\u0000\u0000\u0000\u0000\u0000" + // 5850 - 5854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5855 - 5859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECF6" + // 5860 - 5864 + "\u0000\uECF7\u0000\uECF9\u0000\uEBEF\u0000\uCDD8\u0000\u0000" + // 5865 - 5869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBF2\u0000\u0000" + // 5870 - 5874 + "\u0000\uEBF5\u0000\u0000\u0000\u0000\u0000\uEBF3\u0000\uC9B5" + // 5875 - 5879 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5880 - 5884 + "\u0000\u0000\u0000\uEBF0\u0000\u0000\u0000\u0000\u0000\u0000" + // 5885 - 5889 + "\u0000\u0000\u0000\u0000\u0000\uB6E0\u0000\u0000\u0000\u0000" + // 5890 - 5894 + "\u0000\u0000\u0000\u0000\u0000\uEBF4\u0000\u0000\u0000\uCEA3" + // 5895 - 5899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5900 - 5904 + "\u0000\u0000\u0000\uEAEB\u0000\u0000\u0000\u0000\u0000\u0000" + // 5905 - 5909 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5910 - 5914 + "\u0000\u0000\u0000\uEAEC\u0000\uBED8\u0000\uEAEA\u008F\uDCD3" + // 5915 - 5919 + "\u0000\u0000\u0000\u0000\u0000\uCDE7\u0000\uEAE7\u0000\u0000" + // 5920 - 5924 + "\u0000\u0000\u0000\uEAE9\u0000\uC0BD\u0000\uBFFE\u0000\u0000" + // 5925 - 5929 + "\u0000\uE8ED\u0000\u0000\u0000\u0000\u0000\uC3DF\u0000\u0000" + // 5930 - 5934 + "\u0000\uE8EE\u0000\u0000\u0000\u0000\u0000\uCDD6\u0000\uE8E3" + // 5935 - 5939 + "\u0000\uB3B8\u0000\u0000\u0000\uE8E9\u0000\u0000\u0000\u0000" + // 5940 - 5944 + "\u0000\uE8EC\u0000\uCCAC\u0000\u0000\u0000\u0000\u0000\u0000" + // 5945 - 5949 + "\u0000\u0000\u0000\uE8EF\u0000\u0000\u0000\u0000\u0000\uE8E8" + // 5950 - 5954 + "\u0000\uE8EB\u0000\u0000\u008F\uD9A1\u0000\u0000\u0000\u0000" + // 5955 - 5959 + "\u0000\u0000\u0000\uA2BE\u0000\uA2BF\u0000\u0000\u0000\u0000" + // 5960 - 5964 + "\u0000\uA2BC\u0000\uA2BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 5965 - 5969 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5970 - 5974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5975 - 5979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5980 - 5984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5985 - 5989 + "\u0000\u0000\u0000\uB4CC\u0000\u0000\u0000\uE5FD\u0000\u0000" + // 5990 - 5994 + "\u0000\uE5FE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 5995 - 5999 + "\u0000\u0000\u0000\uA1FA\u0000\uA1F9\u0000\u0000\u0000\u0000" + // 6000 - 6004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6005 - 6009 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6010 - 6014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6015 - 6019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6020 - 6024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4B9\u0000\u0000" + // 6025 - 6029 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6030 - 6034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6D3\u0000\uEEC4" + // 6035 - 6039 + "\u0000\uBDB6\u0000\uBCE0\u0000\uC7DB\u0000\uC3F1\u0000\u0000" + // 6040 - 6044 + "\u0000\u0000\u0000\u0000\u0000\uBCF2\u0000\u0000\u0000\uBFEC" + // 6045 - 6049 + "\u0000\u0000\u0000\uEEC5\u0000\u0000\u0000\uEEC6\u0000\u0000" + // 6050 - 6054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6055 - 6059 + "\u0000\u0000\u0000\uB7F6\u0000\uD3CA\u0000\uD3C8\u0000\uC1D3" + // 6060 - 6064 + "\u0000\uB5CA\u0000\uB6AC\u0000\u0000\u0000\uD3C5\u0000\u0000" + // 6065 - 6069 + "\u0000\uB6F4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6070 - 6074 + "\u0000\u0000\u0000\uB1C4\u0000\u0000\u0000\u0000\u0000\u0000" + // 6075 - 6079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6080 - 6084 + "\u0000\u0000\u0000\uD0D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 6085 - 6089 + "\u0000\u0000\u0000\u0000\u0000\uD0D3\u0000\uD0D1\u0000\u0000" + // 6090 - 6094 + "\u0000\u0000\u0000\uB2C2\u0000\u0000\u0000\uCABB\u0000\uD0CB" + // 6095 - 6099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0CF" + // 6100 - 6104 + "\u0000\uB8F3\u0000\u0000\u0000\u0000\u0000\uBBC8\u0000\uCBC5" + // 6105 - 6109 + "\u0000\uB1DA\u0000\uB0E2\u0000\u0000\u0000\uC6A5\u0000\u0000" + // 6110 - 6114 + "\u0000\u0000\u0000\uEBE9\u0000\u0000\u0000\u0000\u0000\u0000" + // 6115 - 6119 + "\u0000\u0000\u0000\uEBE8\u0000\u0000\u0000\uC6E6\u0000\u0000" + // 6120 - 6124 + "\u0000\uEBED\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBE2" + // 6125 - 6129 + "\u0000\u0000\u0000\uEBEC\u0000\uEBEE\u0000\u0000\u0000\uB8AC" + // 6130 - 6134 + "\u0000\uEBEA\u0000\uB9D6\u0000\u0000\u0000\uBCD5\u0000\u0000" + // 6135 - 6139 + "\u0000\u0000\u0000\uB5B2\u0000\u0000\u0000\u0000\u0000\u0000" + // 6140 - 6144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1AB\u0000\u0000" + // 6145 - 6149 + "\u0000\uF1AC\u0000\u0000\u0000\uD2AC\u0000\uDDBB\u0000\uC8D3" + // 6150 - 6154 + "\u0000\u0000\u0000\u0000\u0000\uB0FB\u0000\u0000\u0000\uB0BB" + // 6155 - 6159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6160 - 6164 + "\u0000\u0000\u0000\u0000\u0000\uBBF4\u0000\uCBB0\u0000\uBEFE" + // 6165 - 6169 + "\u0000\u0000\u0000\uBADB\u0000\uCEF6\u0000\uEAE1\u0000\uEAE2" + // 6170 - 6174 + "\u0000\uC1F5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6175 - 6179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6180 - 6184 + "\u0000\uCEA2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6185 - 6189 + "\u0000\uEAE3\u0000\uCDB5\u0000\u0000\u0000\u0000\u0000\uEAE4" + // 6190 - 6194 + "\u0000\uEAE5\u0000\u0000\u0000\u0000\u0000\uCAE4\u0000\uEAE6" + // 6195 - 6199 + "\u0000\u0000\u0000\uBAC0\u0000\uEBE3\u0000\uEBEB\u0000\uEBE4" + // 6200 - 6204 + "\u0000\u0000\u0000\uEBE0\u0000\u0000\u0000\uC4FC\u0000\uEBDF" + // 6205 - 6209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBDD\u0000\u0000" + // 6210 - 6214 + "\u0000\uCDA1\u0000\uBBF0\u0000\u0000\u0000\u0000\u0000\uEBE1" + // 6215 - 6219 + "\u0000\u0000\u0000\uEBDE\u0000\u0000\u0000\u0000\u008F\uDEB5" + // 6220 - 6224 + "\u0000\uEBE5\u0000\uBDF4\u0000\u0000\u0000\uB8C1\u0000\u0000" + // 6225 - 6229 + "\u0000\u0000\u0000\u0000\u0000\uC2FA\u0000\u0000\u0000\uEACF" + // 6230 - 6234 + "\u0000\uEAD6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6235 - 6239 + "\u0000\u0000\u0000\uB7B6\u0000\u0000\u0000\u0000\u0000\uC2DE" + // 6240 - 6244 + "\u0000\u0000\u0000\uEADC\u0000\u0000\u0000\u0000\u0000\u0000" + // 6245 - 6249 + "\u0000\u0000\u0000\uEAD8\u0000\u0000\u0000\u0000\u0000\u0000" + // 6250 - 6254 + "\u0000\uC2B5\u0000\uEAD7\u0000\u0000\u0000\uEADA\u0000\u0000" + // 6255 - 6259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAD1\u0000\u0000" + // 6260 - 6264 + "\u0000\uE8F0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6265 - 6269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6270 - 6274 + "\u0000\u0000\u0000\u0000\u0000\uE8DA\u0000\u0000\u0000\u0000" + // 6275 - 6279 + "\u0000\u0000\u0000\u0000\u0000\uB3F7\u0000\u0000\u008F\uD8F4" + // 6280 - 6284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBEF8\u0000\uE8E5" + // 6285 - 6289 + "\u0000\u0000\u0000\uE8EA\u0000\uC1F3\u0000\u0000\u0000\u0000" + // 6290 - 6294 + "\u0000\uE8E6\u0000\uC0BF\u0000\uEBD3\u0000\u0000\u0000\uEBD8" + // 6295 - 6299 + "\u0000\uB8ED\u0000\uEBD5\u0000\uEBD6\u008F\uDDFA\u0000\uEBD2" + // 6300 - 6304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0E2\u0000\uC6C9" + // 6305 - 6309 + "\u0000\u0000\u0000\u0000\u0000\uC3AF\u0000\u0000\u0000\uB2DD" + // 6310 - 6314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6315 - 6319 + "\u0000\u0000\u0000\uC8F0\u0000\u0000\u0000\u0000\u0000\uB5C3" + // 6320 - 6324 + "\u0000\u0000\u008F\uDEA4\u0000\uC4B4\u0000\uB1D3\u0000\u0000" + // 6325 - 6329 + "\u0000\uEBCE\u0000\uB7D8\u0000\u0000\u0000\u0000\u0000\uBBEE" + // 6330 - 6334 + "\u0000\u0000\u0000\u0000\u0000\uBBED\u0000\u0000\u0000\uCFCD" + // 6335 - 6339 + "\u0000\uEBCD\u0000\uEBCC\u0000\uC1A7\u0000\u0000\u0000\uB5CD" + // 6340 - 6344 + "\u0000\uCFC3\u0000\uB3BA\u0000\uBEDC\u0000\u0000\u0000\u0000" + // 6345 - 6349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uDDEA\u0000\u0000" + // 6350 - 6354 + "\u0000\u0000\u0000\uEBCB\u0000\u0000\u0000\u0000\u0000\u0000" + // 6355 - 6359 + "\u0000\uD6A6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6360 - 6364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6365 - 6369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6370 - 6374 + "\u0000\uCDF0\u0000\u0000\u0000\uC6FD\u0000\u0000\u0000\u0000" + // 6375 - 6379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6380 - 6384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4A5\u0000\u0000" + // 6385 - 6389 + "\u0000\uB9D5\u0000\uEACD\u0000\uB0E1\u0000\u0000\u0000\u0000" + // 6390 - 6394 + "\u0000\u0000\u0000\u0000\u0000\uC9BD\u0000\u0000\u0000\u0000" + // 6395 - 6399 + "\u0000\uEACE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6400 - 6404 + "\u0000\uBFEA\u0000\u0000\u0000\uEAD5\u0000\u0000\u0000\u0000" + // 6405 - 6409 + "\u0000\uEAD2\u0000\u0000\u0000\uC3EF\u0000\u0000\u0000\u0000" + // 6410 - 6414 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAD3\u0000\uEAD0" + // 6415 - 6419 + "\u0000\uB6DE\u0000\uB8C0\u0000\u0000\u0000\uC4FB\u0000\uEBBE" + // 6420 - 6424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7D7" + // 6425 - 6429 + "\u0000\u0000\u0000\uBFD6\u0000\u0000\u0000\uEBC1\u0000\u0000" + // 6430 - 6434 + "\u0000\uC6A4\u0000\u0000\u0000\uEBC0\u0000\u0000\u008F\uDDC8" + // 6435 - 6439 + "\u0000\uB7B1\u0000\u0000\u0000\u0000\u0000\uEBBF\u0000\uC2F7" + // 6440 - 6444 + "\u0000\uB5AD\u0000\u0000\u0000\u0000\u0000\uEBC2\u0000\u0000" + // 6445 - 6449 + "\u0000\uEBC3\u0000\u0000\u0000\uBED9\u0000\uEBB7\u0000\u0000" + // 6450 - 6454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6455 - 6459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6460 - 6464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6465 - 6469 + "\u0000\u0000\u0000\uB3D1\u0000\u0000\u0000\u0000\u0000\u0000" + // 6470 - 6474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBB8" + // 6475 - 6479 + "\u0000\u0000\u0000\uEBB9\u0000\uEBBA\u0000\u0000\u0000\u0000" + // 6480 - 6484 + "\u0000\uF0CE\u008F\uE7AE\u0000\uF0CB\u0000\u0000\u0000\uF0CC" + // 6485 - 6489 + "\u0000\u0000\u0000\uF0CD\u0000\uF0CF\u0000\u0000\u0000\u0000" + // 6490 - 6494 + "\u0000\u0000\u008F\uF4EE\u0000\u0000\u008F\uE7B1\u0000\u0000" + // 6495 - 6499 + "\u008F\uF4EF\u0000\uC0C4\u0000\u0000\u0000\u0000\u008F\uE7B2" + // 6500 - 6504 + "\u0000\uCCF7\u0000\u0000\u0000\u0000\u0000\uC0C5\u0000\u0000" + // 6505 - 6509 + "\u0000\u0000\u0000\uF0D0\u0000\u0000\u0000\uC8F3\u0000\u0000" + // 6510 - 6514 + "\u0000\uCFCE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6515 - 6519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9B4\u0000\u0000" + // 6520 - 6524 + "\u0000\u0000\u0000\u0000\u0000\uCDF5\u0000\u0000\u0000\uE9B6" + // 6525 - 6529 + "\u0000\uE9B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6530 - 6534 + "\u0000\uE9B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6535 - 6539 + "\u0000\u0000\u0000\u0000\u0000\uE9BC\u0000\uE9BA\u0000\u0000" + // 6540 - 6544 + "\u0000\u0000\u0000\uB1A1\u0000\uBFD8\u0000\uBDFC\u0000\uB4D9" + // 6545 - 6549 + "\u0000\uF0A3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7E6" + // 6550 - 6554 + "\u0000\u0000\u0000\uF0A5\u0000\u0000\u0000\u0000\u0000\u0000" + // 6555 - 6559 + "\u0000\uB1A2\u0000\u0000\u0000\uF0A4\u0000\uC4C4\u0000\u0000" + // 6560 - 6564 + "\u0000\uCECD\u0000\uC6AB\u0000\uEFFC\u0000\uCEA6\u0000\u0000" + // 6565 - 6569 + "\u0000\uB8B1\u0000\u0000\u0000\u0000\u0000\uCDDB\u0000\u0000" + // 6570 - 6574 + "\u0000\u0000\u0000\uCEA5\u0000\uC6F1\u0000\u0000\u0000\u0000" + // 6575 - 6579 + "\u0000\u0000\u0000\u0000\u0000\uB1AB\u0000\u0000\u0000\uC0E3" + // 6580 - 6584 + "\u0000\uBCB6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6585 - 6589 + "\u0000\uCAB7\u0000\u0000\u0000\uB1C0\u0000\u0000\u0000\u0000" + // 6590 - 6594 + "\u0000\u0000\u0000\uCEED\u0000\uCDEB\u0000\u0000\u0000\uF0BB" + // 6595 - 6599 + "\u0000\u0000\u0000\uC5C5\u0000\u0000\u0000\u0000\u0000\u0000" + // 6600 - 6604 + "\u0000\u0000\u0000\uC4E6\u0000\uD1ED\u0000\u0000\u0000\u0000" + // 6605 - 6609 + "\u0000\uC2A7\u0000\u0000\u0000\u0000\u0000\uBAEF\u0000\uD1EE" + // 6610 - 6614 + "\u0000\uD1EF\u0000\uC1B0\u0000\u0000\u0000\uD1EC\u0000\u0000" + // 6615 - 6619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1F1\u0000\u0000" + // 6620 - 6624 + "\u0000\uCBB6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6625 - 6629 + "\u0000\uB9E4\u0000\u0000\u0000\uC7ED\u0000\uD1F0\u0000\u0000" + // 6630 - 6634 + "\u008F\uD7DE\u0000\uCCD0\u0000\uE7F7\u0000\uB2D8\u0000\uB3FD" + // 6635 - 6639 + "\u0000\uE7FB\u0000\u0000\u0000\u0000\u0000\uE7FD\u0000\u0000" + // 6640 - 6644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7D4\u0000\u0000" + // 6645 - 6649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6650 - 6654 + "\u0000\u0000\u0000\uE8A3\u0000\uE8AC\u0000\uE8AD\u0000\u0000" + // 6655 - 6659 + "\u0000\u0000\u0000\u0000\u0000\uB0AB\u0000\u0000\u0000\u0000" + // 6660 - 6664 + "\u0000\u0000\u0000\u8EE0\u0000\u8EE1\u0000\u0000\u0000\\" + // 6665 - 6669 + "\u008F\uA2C3\u0000\uA1F8\u0000\uA1AF\u0000\u0000\u0000\u0000" + // 6670 - 6674 + "\u0000\u0000\u0000\u8EE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 6675 - 6679 + "\u0000\uA1EB\u0000\uA1DE\u0000\u0000\u0000\u0000\u0000\uA1AD" + // 6680 - 6684 + "\u0000\u0000\u0000\uA2F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 6685 - 6689 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6690 - 6694 + "\u0000\u0000\u0000\u0000\u0000\uF3EE\u0000\uE3B7\u0000\u0000" + // 6695 - 6699 + "\u0000\u0000\u0000\uECDA\u0000\uF0ED\u0000\u0000\u0000\u0000" + // 6700 - 6704 + "\u0000\uF3EF\u0000\u0000\u0000\uF3F0\u0000\u0000\u0000\u0000" + // 6705 - 6709 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6710 - 6714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3F2\u0000\uEAFD" + // 6715 - 6719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBA2\u0000\u0000" + // 6720 - 6724 + "\u0000\uEBA1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBA4" + // 6725 - 6729 + "\u0000\u0000\u0000\u0000\u0000\uEBA3\u0000\u0000\u0000\uEBA5" + // 6730 - 6734 + "\u0000\u0000\u0000\u0000\u0000\uBDB1\u0000\u0000\u0000\uEBA6" + // 6735 - 6739 + "\u0000\u0000\u0000\u0000\u0000\uEBA7\u0000\u0000\u0000\u0000" + // 6740 - 6744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBA8" + // 6745 - 6749 + "\u0000\uC0BE\u0000\uB7EC\u0000\u0000\u0000\uEAC9\u0000\u0000" + // 6750 - 6754 + "\u0000\uEAC8\u0000\u0000\u0000\uBDB0\u0000\u0000\u0000\u0000" + // 6755 - 6759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB9D4\u0000\uDEA7" + // 6760 - 6764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEACA" + // 6765 - 6769 + "\u0000\uBDD1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3B9" + // 6770 - 6774 + "\u0000\u0000\u0000\uEACB\u0000\u0000\u0000\uB1D2\u0000\u0000" + // 6775 - 6779 + "\u0000\uBED7\u0000\uEACC\u0000\u0000\u0000\uC9F2\u0000\uE8E4" + // 6780 - 6784 + "\u0000\uC6A1\u0000\u0000\u0000\u0000\u0000\uB0B1\u0000\u0000" + // 6785 - 6789 + "\u0000\u0000\u0000\uE8DD\u0000\u0000\u0000\uE8D9\u0000\uC1F2" + // 6790 - 6794 + "\u0000\uE8D3\u0000\uE8DB\u0000\uE8E0\u0000\u0000\u0000\uC7AC" + // 6795 - 6799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0AA\u0000\u0000" + // 6800 - 6804 + "\u0000\uE8D8\u0000\u0000\u0000\uE8E1\u0000\uC9F8\u0000\u0000" + // 6805 - 6809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4A3" + // 6810 - 6814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6815 - 6819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6820 - 6824 + "\u0000\u0000\u0000\uB1F1\u0000\uE7F2\u0000\uCEEA\u0000\uC2DD" + // 6825 - 6829 + "\u0000\u0000\u0000\u0000\u0000\uC9C4\u0000\u0000\u0000\uE7FE" + // 6830 - 6834 + "\u0000\u0000\u0000\uB2D7\u0000\uE7FC\u0000\u0000\u0000\uE7FA" + // 6835 - 6839 + "\u0000\uE7F1\u0000\uEAB6\u0000\u0000\u0000\u0000\u0000\u0000" + // 6840 - 6844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6845 - 6849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6850 - 6854 + "\u0000\u0000\u0000\uEAB4\u0000\u0000\u0000\u0000\u0000\uEAB5" + // 6855 - 6859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEABA\u0000\uEABB" + // 6860 - 6864 + "\u0000\u0000\u0000\uB3AA\u0000\u0000\u0000\uB5C2\u0000\u0000" + // 6865 - 6869 + "\u0000\u0000\u0000\uEAB9\u0000\u0000\u0000\uE8C7\u0000\u0000" + // 6870 - 6874 + "\u0000\u0000\u0000\u0000\u0000\uBFFB\u0000\u0000\u008F\uD8B7" + // 6875 - 6879 + "\u0000\u0000\u0000\u0000\u0000\uB5C6\u0000\u0000\u0000\uB6DD" + // 6880 - 6884 + "\u0000\u0000\u0000\uE8C2\u0000\u0000\u0000\u0000\u0000\u0000" + // 6885 - 6889 + "\u0000\u0000\u0000\uB2DB\u0000\u0000\u0000\u0000\u0000\uBED4" + // 6890 - 6894 + "\u0000\u0000\u0000\uE8C5\u0000\u0000\u0000\u0000\u0000\u0000" + // 6895 - 6899 + "\u0000\uBADA\u0000\u0000\u0000\u0000\u0000\uC5D1\u0000\uEAAB" + // 6900 - 6904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAAF\u0000\u0000" + // 6905 - 6909 + "\u0000\uEAB2\u0000\uEAB1\u0000\u0000\u0000\u0000\u0000\u0000" + // 6910 - 6914 + "\u0000\uEAA9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6915 - 6919 + "\u0000\uEAAC\u0000\u0000\u0000\uEABD\u0000\u0000\u0000\u0000" + // 6920 - 6924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6925 - 6929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6930 - 6934 + "\u0000\u0000\u0000\uE9C8\u0000\uB8D7\u0000\u0000\u0000\uB5D4" + // 6935 - 6939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9CA\u0000\uD1DD" + // 6940 - 6944 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5F5" + // 6945 - 6949 + "\u0000\u0000\u0000\uCEBA\u0000\u0000\u0000\uB6F3\u0000\uE9CB" + // 6950 - 6954 + "\u0000\uE9F5\u0000\u0000\u0000\u0000\u0000\uEAA2\u0000\u0000" + // 6955 - 6959 + "\u0000\u0000\u0000\uB2DC\u0000\u0000\u0000\uE9FC\u0000\u0000" + // 6960 - 6964 + "\u0000\uEAA3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9FD" + // 6965 - 6969 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6970 - 6974 + "\u0000\uE9FA\u0000\u0000\u0000\uC4B3\u0000\u0000\u0000\uE9F7" + // 6975 - 6979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6980 - 6984 + "\u0000\u0000\u0000\uC7E8\u0000\uE9E6\u0000\u0000\u0000\uCBAA" + // 6985 - 6989 + "\u0000\uE9E7\u0000\u0000\u0000\u0000\u0000\uE9E4\u0000\u0000" + // 6990 - 6994 + "\u0000\uE9E5\u0000\uE9EA\u0000\uE9ED\u0000\u0000\u0000\u0000" + // 6995 - 6999 + "\u0000\uE9EB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9E9" + // 7000 - 7004 + "\u0000\uE9E3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7005 - 7009 + "\u0000\u0000\u0000\uC3D8\u0000\u0000\u0000\uE9F4\u0000\u0000" + // 7010 - 7014 + "\u0000\uCCAA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0A9" + // 7015 - 7019 + "\u0000\uC7B5\u0000\u0000\u0000\uB5D7\u0000\u0000\u0000\u0000" + // 7020 - 7024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7B7\u0000\u0000" + // 7025 - 7029 + "\u0000\uC6E3\u0000\uB8C3\u0000\uCBB3\u0000\u0000\u0000\u0000" + // 7030 - 7034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9C9\u0000\uD0AA" + // 7035 - 7039 + "\u0000\uBEE8\u0000\uD0AB\u0000\uB2B5\u0000\u0000\u0000\u0000" + // 7040 - 7044 + "\u0000\u0000\u0000\uB6E5\u0000\uB8F0\u0000\uCCE9\u0000\uE9A5" + // 7045 - 7049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7F6\u0000\u0000" + // 7050 - 7054 + "\u0000\u0000\u0000\uE9AF\u0000\uE9A7\u0000\u0000\u0000\uE9A9" + // 7055 - 7059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7060 - 7064 + "\u0000\uE9B3\u0000\uE9A8\u0000\u0000\u0000\u0000\u0000\uE9AC" + // 7065 - 7069 + "\u0000\u0000\u0000\u0000\u0000\uB1F2\u0000\u0000\u0000\uC6E5" + // 7070 - 7074 + "\u0000\u0000\u0000\uE9AD\u0000\uE9B0\u0000\u0000\u0000\u0000" + // 7075 - 7079 + "\u0000\u0000\u0000\uA8C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 7080 - 7084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7085 - 7089 + "\u0000\uA8B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7090 - 7094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7095 - 7099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7100 - 7104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7105 - 7109 + "\u0000\u0000\u0000\uA6A1\u0000\uA6A2\u0000\uA6A3\u0000\uA6A4" + // 7110 - 7114 + "\u0000\uA6A5\u0000\uA6A6\u0000\uA6A7\u0000\uA6A8\u0000\uA6A9" + // 7115 - 7119 + "\u0000\uA6AA\u0000\uA6AB\u0000\uA6AC\u0000\uA6AD\u0000\uA6AE" + // 7120 - 7124 + "\u0000\uA6AF\u0000\uE8FB\u0000\uE9A1\u0000\u0000\u0000\uC8D9" + // 7125 - 7129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8FE" + // 7130 - 7134 + "\u0000\uBED6\u0000\uBCC9\u0000\uE9A3\u0000\u0000\u0000\u0000" + // 7135 - 7139 + "\u0000\uB6BE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7140 - 7144 + "\u008F\uD9C6\u0000\u0000\u0000\uE9A4\u0000\u0000\u0000\uC9F9" + // 7145 - 7149 + "\u0000\uE8FD\u008F\uD9C8\u0000\uE8D6\u0000\u0000\u0000\u0000" + // 7150 - 7154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8AE\u0000\u0000" + // 7155 - 7159 + "\u0000\uE8B6\u0000\u0000\u0000\uE8BD\u0000\uE8B7\u0000\u0000" + // 7160 - 7164 + "\u0000\u0000\u0000\u0000\u0000\uE8B5\u0000\u0000\u0000\u0000" + // 7165 - 7169 + "\u0000\u0000\u0000\u0000\u0000\uE7F6\u0000\u0000\u0000\u0000" + // 7170 - 7174 + "\u0000\uE8B3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8AF" + // 7175 - 7179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4D0\u0000\uE8B1" + // 7180 - 7184 + "\u0000\uBCC3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7185 - 7189 + "\u0000\u0000\u0000\uE8D1\u0000\u0000\u0000\u0000\u0000\u0000" + // 7190 - 7194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7195 - 7199 + "\u0000\u0000\u0000\u0000\u0000\uCACE\u0000\u0000\u0000\uCCA2" + // 7200 - 7204 + "\u0000\uE8F9\u0000\uE8F8\u0000\u0000\u0000\uE8F4\u0000\uE8F5" + // 7205 - 7209 + "\u0000\u0000\u0000\uB1B6\u0000\u0000\u0000\u0000\u0000\u0000" + // 7210 - 7214 + "\u0000\u0000\u0000\uE8F7\u0000\uE8CE\u0000\u0000\u0000\uE8CD" + // 7215 - 7219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7220 - 7224 + "\u0000\u0000\u0000\uC7EB\u0000\uE8D4\u0000\u0000\u0000\uE8DF" + // 7225 - 7229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3FE" + // 7230 - 7234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8E2\u0000\u0000" + // 7235 - 7239 + "\u0000\u0000\u0000\uE8D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 7240 - 7244 + "\u0000\uE8D5\u0000\uCDEE\u0000\u0000\u0000\u0000\u0000\uC8AD" + // 7245 - 7249 + "\u0000\u0000\u0000\uEEEC\u0000\u0000\u0000\uBEE0\u008F\uE3E9" + // 7250 - 7254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7255 - 7259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB9DB" + // 7260 - 7264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7265 - 7269 + "\u008F\uE3F1\u008F\uE3F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 7270 - 7274 + "\u0000\u0000\u0000\u0000\u0000\uCBC8\u0000\u0000\u0000\uE7CD" + // 7275 - 7279 + "\u0000\uE7CE\u0000\u0000\u0000\u0000\u0000\uE7CF\u0000\u0000" + // 7280 - 7284 + "\u0000\uE7D0\u0000\uB6BD\u0000\uDAAA\u0000\uE7D1\u0000\u0000" + // 7285 - 7289 + "\u0000\uC0E5\u0000\uE7D2\u0000\uBCCB\u0000\u0000\u0000\uE7D3" + // 7290 - 7294 + "\u0000\u0000\u0000\uD0B0\u0000\u0000\u0000\u0000\u0000\u0000" + // 7295 - 7299 + "\u0000\uE7D4\u0000\uCADE\u0000\uB4DC\u0000\u0000\u0000\u0000" + // 7300 - 7304 + "\u0000\uC1A4\u0000\uBDD8\u0000\u0000\u0000\uC9F1\u0000\uBDAE" + // 7305 - 7309 + "\u0000\uE8CA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7310 - 7314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAEE" + // 7315 - 7319 + "\u0000\u0000\u0000\uE8C1\u0000\u0000\u0000\u0000\u0000\u0000" + // 7320 - 7324 + "\u0000\uB2DA\u0000\uB8D6\u0000\uC9A9\u0000\uE8CB\u0000\u0000" + // 7325 - 7329 + "\u0000\uE8BF\u0000\u0000\u008F\uD8C1\u0000\uE8C8\u0000\u0000" + // 7330 - 7334 + "\u0000\u0000\u0000\u0000\u0000\uE8D2\u0000\u0000\u0000\uE8C3" + // 7335 - 7339 + "\u0000\u0000\u0000\u0000\u0000\uEECF\u0000\u0000\u0000\uBEDF" + // 7340 - 7344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7345 - 7349 + "\u0000\uEED2\u0000\uEED0\u0000\uBEDF\u0000\u0000\u0000\u0000" + // 7350 - 7354 + "\u0000\uEED1\u0000\u0000\u0000\uC8B0\u0000\u0000\u0000\u0000" + // 7355 - 7359 + "\u0000\uEED4\u0000\uEED3\u0000\u0000\u0000\u0000\u0000\uBEFA" + // 7360 - 7364 + "\u0000\u0000\u0000\uEED5\u0000\u0000\u0000\u0000\u0000\u0000" + // 7365 - 7369 + "\u0000\u0000\u0000\u0000\u0000\uF3CE\u0000\uC7FE\u0000\u0000" + // 7370 - 7374 + "\u0000\u0000\u0000\uF3CF\u0000\uF3D1\u0000\u0000\u0000\u0000" + // 7375 - 7379 + "\u0000\uF3D2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7380 - 7384 + "\u0000\u0000\u0000\u0000\u0000\uB9ED\u0000\uCCCD\u0000\u0000" + // 7385 - 7389 + "\u0000\u0000\u0000\uF3D0\u0000\uB9ED\u0000\uCCCD\u0000\uCBE3" + // 7390 - 7394 + "\u0000\uD6F7\u0000\u0000\u0000\uDDE0\u0000\uCBFB\u0000\uE8BC" + // 7395 - 7399 + "\u0000\u0000\u0000\uE8B2\u0000\u0000\u0000\u0000\u0000\u0000" + // 7400 - 7404 + "\u0000\u0000\u0000\u0000\u0000\uE8BE\u0000\u0000\u0000\uE8B0" + // 7405 - 7409 + "\u0000\uC7FC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7410 - 7414 + "\u0000\u0000\u0000\uCDE9\u0000\u0000\u0000\u0000\u0000\u0000" + // 7415 - 7419 + "\u0000\uE8B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7420 - 7424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8CF\u0000\u0000" + // 7425 - 7429 + "\u0000\u0000\u0000\uEECE\u0000\u0000\u0000\u0000\u0000\u0000" + // 7430 - 7434 + "\u0000\u0000\u0000\uBDE6\u0000\u0000\u0000\uEECD\u0000\u0000" + // 7435 - 7439 + "\u0000\uEECC\u0000\u0000\u0000\uC2E9\u0000\u0000\u0000\u0000" + // 7440 - 7444 + "\u0000\uB8EF\u0000\u0000\u0000\uC0C3\u0000\u0000\u0000\u0000" + // 7445 - 7449 + "\u0000\u0000\u0000\u0000\u0000\uC8B0\u0000\u0000\u0000\u0000" + // 7450 - 7454 + "\u0000\u0000\u0000\u0000\u0000\uBDB9\u0000\u0000\u0000\u0000" + // 7455 - 7459 + "\u0000\u0000\u0000\uBBB2\u0000\uD2D4\u0000\u0000\u0000\u0000" + // 7460 - 7464 + "\u0000\u0000\u0000\u0000\u0000\uCBF4\u0000\uBAB5\u0000\uB5DA" + // 7465 - 7469 + "\u0000\uCDA7\u0000\uC1D0\u0000\uC8BF\u0000\uBCFD\u0000\u0000" + // 7470 - 7474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDC7" + // 7475 - 7479 + "\u0000\u0000\u0000\uBCE8\u0000\uBCF5\u0000\u0000\u0000\uBDF6" + // 7480 - 7484 + "\u0000\u0000\u0000\uC8C0\u0000\u0000\u008F\uB4DE\u0000\u0000" + // 7485 - 7489 + "\u0000\uD2D7\u0000\uE8A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 7490 - 7494 + "\u0000\u0000\u0000\uE8A9\u0000\uB7D5\u0000\u0000\u0000\u0000" // 7495 - 7499 + ; + + index2a = + "\u0000\uC1F0\u0000\uB7D5\u0000\u0000\u0000\u0000\u0000\u0000" + // 7500 - 7504 + "\u0000\u0000\u0000\uB1C1\u0000\uE8A8\u0000\u0000\u0000\uB9D3" + // 7505 - 7509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7510 - 7514 + "\u0000\uC1F1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7515 - 7519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uE2C7\u0000\u0000" + // 7520 - 7524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5A2" + // 7525 - 7529 + "\u0000\u0000\u0000\u0000\u0000\uEEC3\u0000\u0000\u0000\uEEC2" + // 7530 - 7534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7535 - 7539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7540 - 7544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0EC\u0000\uC7A3" + // 7545 - 7549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0EE\u0000\uB2BB" + // 7550 - 7554 + "\u0000\u0000\u0000\uF0F1\u0000\uF0F0\u0000\u0000\u0000\u0000" + // 7555 - 7559 + "\u0000\u0000\u0000\u0000\u0000\uB1A4\u0000\u0000\u0000\u0000" + // 7560 - 7564 + "\u0000\u0000\u0000\uB6C1\u0000\uE7D9\u0000\u0000\u0000\u0000" + // 7565 - 7569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4FA" + // 7570 - 7574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7575 - 7579 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7580 - 7584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7585 - 7589 + "\u0000\u0000\u0000\uE7DB\u0000\uE7DA\u0000\uE7DD\u0000\u0000" + // 7590 - 7594 + "\u0000\u0000\u0000\uE7DC\u0000\u0000\u0000\uE7DE\u0000\uE7C8" + // 7595 - 7599 + "\u0000\u0000\u0000\u0000\u0000\uBFC3\u0000\u0000\u0000\uB2E9" + // 7600 - 7604 + "\u0000\u0000\u0000\uE7C9\u0000\uCED7\u0000\u0000\u0000\uBCAB" + // 7605 - 7609 + "\u0000\u0000\u0000\u0000\u0000\uBDAD\u0000\u0000\u0000\u0000" + // 7610 - 7614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBEA\u0000\uC3D7" + // 7615 - 7619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7620 - 7624 + "\u0000\uE7CA\u0000\uE7CB\u0000\uB1B1\u0000\u0000\u0000\uE7CC" + // 7625 - 7629 + "\u0000\u0000\u0000\uB8D5\u0000\u0000\u0000\u0000\u0000\uB0FD" + // 7630 - 7634 + "\u0000\uE6F1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7635 - 7639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7640 - 7644 + "\u0000\uE6F8\u0000\u0000\u0000\uE6F9\u0000\u0000\u0000\u0000" + // 7645 - 7649 + "\u0000\uC6B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6BB" + // 7650 - 7654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7A6\u0000\uC7BD" + // 7655 - 7659 + "\u0000\u0000\u0000\u0000\u0000\uEDEB\u0000\u0000\u0000\u0000" + // 7660 - 7664 + "\u0000\uEDEA\u0000\uB2E0\u0000\u0000\u0000\u0000\u0000\uC6F6" + // 7665 - 7669 + "\u0000\uEDEC\u0000\uC7F7\u0000\u0000\u0000\uC5B3\u0000\u0000" + // 7670 - 7674 + "\u0000\uEDED\u0000\uBDD2\u0000\u0000\u0000\u0000\u0000\u0000" + // 7675 - 7679 + "\u0000\uEDEF\u0000\u0000\u0000\u0000\u0000\uCCC2\u0000\uEDFE" + // 7680 - 7684 + "\u0000\uEDF1\u0000\uEDF2\u0000\u0000\u0000\u0000\u0000\uC4C9" + // 7685 - 7689 + "\u0000\u0000\u0000\u0000\u0000\uEEBF\u0000\u0000\u0000\u0000" + // 7690 - 7694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7695 - 7699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7700 - 7704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEC0\u0000\u0000" + // 7705 - 7709 + "\u0000\u0000\u008F\uF4E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 7710 - 7714 + "\u0000\uEEC1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7715 - 7719 + "\u0000\u0000\u0000\u0000\u0000\uF0F9\u0000\u0000\u0000\u0000" + // 7720 - 7724 + "\u0000\u0000\u0000\u0000\u0000\uF0FB\u0000\uC2EA\u0000\uB3DB" + // 7725 - 7729 + "\u0000\uB3DC\u0000\uF0FA\u0000\u0000\u0000\u0000\u0000\u0000" + // 7730 - 7734 + "\u0000\u0000\u0000\uB4E9\u0000\uB8B2\u0000\u0000\u008F\uE8B1" + // 7735 - 7739 + "\u0000\uB4EA\u0000\u0000\u0000\uC5BF\u0000\uC5BF\u0000\u0000" + // 7740 - 7744 + "\u0000\u0000\u0000\uCEE0\u0000\u0000\u0000\uB8D4\u0000\uBBE8" + // 7745 - 7749 + "\u0000\u0000\u0000\u0000\u0000\uC8EE\u0000\u0000\u0000\u0000" + // 7750 - 7754 + "\u0000\u0000\u0000\uB8AA\u0000\uCBC3\u0000\u0000\u0000\uE6EF" + // 7755 - 7759 + "\u0000\uE6ED\u0000\u0000\u0000\uB9CE\u0000\u0000\u0000\uB9CF" + // 7760 - 7764 + "\u0000\uB0E9\u0000\u0000\u0000\uBAE8\u0000\u0000\u0000\u0000" + // 7765 - 7769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7D9\u0000\u0000" + // 7770 - 7774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3A9" + // 7775 - 7779 + "\u0000\uB0B2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7780 - 7784 + "\u0000\uE7EB\u0000\uE7EE\u0000\uC7CE\u0000\u0000\u0000\uBFC4" + // 7785 - 7789 + "\u0000\u0000\u0000\uB2D6\u0000\u0000\u0000\uCBA7\u0000\u0000" + // 7790 - 7794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7DD\u0000\uB6DC" + // 7795 - 7799 + "\u0000\u0000\u0000\uE7ED\u0000\u0000\u0000\uB2EA\u0000\u0000" + // 7800 - 7804 + "\u0000\u0000\u0000\uB1AA\u0000\u0000\u0000\uCBF8\u0000\uBFD7" + // 7805 - 7809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7810 - 7814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7DE\u0000\u0000" + // 7815 - 7819 + "\u0000\u0000\u0000\uB6E1\u0000\u0000\u0000\u0000\u0000\uCAD6" + // 7820 - 7824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7825 - 7829 + "\u0000\uEDE9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7830 - 7834 + "\u0000\u0000\u0000\uF1C6\u0000\u0000\u0000\u0000\u0000\uB3BE" + // 7835 - 7839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7CF\u0000\uF1C7" + // 7840 - 7844 + "\u0000\uF1C8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7845 - 7849 + "\u0000\uC3DA\u0000\uC6EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 7850 - 7854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1C9" + // 7855 - 7859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1BE\u0000\u0000" + // 7860 - 7864 + "\u008F\uB2ED\u0000\uC6FE\u0000\u0000\u0000\u0000\u0000\uC1B4" + // 7865 - 7869 + "\u0000\uD1C0\u0000\uD1C1\u0000\uC8AC\u0000\uB8F8\u0000\uCFBB" + // 7870 - 7874 + "\u0000\uD1C2\u0000\u0000\u0000\u0000\u0000\uB6A6\u0000\u0000" + // 7875 - 7879 + "\u0000\u0000\u0000\u0000\u0000\uCABC\u0000\uC2B6\u0000\uB6F1" + // 7880 - 7884 + "\u0000\uC5B5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7F3" + // 7885 - 7889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBE3\u0000\u0000" + // 7890 - 7894 + "\u0000\u0000\u0000\u0000\u0000\uC9B0\u0000\u0000\u0000\u0000" + // 7895 - 7899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7900 - 7904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7905 - 7909 + "\u0000\uDBEF\u0000\u0000\u0000\uB2B3\u0000\uDBE4\u0000\u0000" + // 7910 - 7914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7915 - 7919 + "\u0000\uDBF5\u0000\uDBE5\u0000\uE7BD\u0000\u0000\u0000\uE7BE" + // 7920 - 7924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2B2\u0000\u0000" + // 7925 - 7929 + "\u0000\uE7C5\u0000\uE7C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 7930 - 7934 + "\u0000\uE7C1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7C2" + // 7935 - 7939 + "\u0000\u0000\u0000\uC2A1\u0000\u0000\u0000\u0000\u0000\u0000" + // 7940 - 7944 + "\u0000\u0000\u0000\uE7C4\u0000\uE7C3\u0000\uE7C6\u0000\u0000" + // 7945 - 7949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7C7\u0000\uE7B1" + // 7950 - 7954 + "\u0000\u0000\u0000\u0000\u0000\uE7B4\u0000\uE7B3\u0000\u0000" + // 7955 - 7959 + "\u0000\u0000\u0000\u0000\u0000\uCBC4\u0000\uE7B7\u0000\u0000" + // 7960 - 7964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7965 - 7969 + "\u0000\uE7B8\u0000\u0000\u0000\u0000\u0000\uC1B7\u0000\u0000" + // 7970 - 7974 + "\u0000\uE7B9\u0000\u0000\u0000\u0000\u0000\uE7BB\u0000\u0000" + // 7975 - 7979 + "\u0000\uE7BF\u0000\u0000\u0000\u0000\u0000\uE7BC\u0000\uE7BA" + // 7980 - 7984 + "\u0000\uC7BF\u0000\uE7AF\u0000\u0000\u0000\uE7B0\u0000\uE7AC" + // 7985 - 7989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7AD" + // 7990 - 7994 + "\u0000\u0000\u0000\uE7AE\u0000\u0000\u0000\u0000\u0000\u0000" + // 7995 - 7999 + "\u0000\u0000\u0000\uB9D1\u0000\u0000\u0000\u0000\u0000\u0000" + // 8000 - 8004 + "\u0000\uE7B6\u0000\u0000\u0000\uE7B2\u0000\u0000\u0000\u0000" + // 8005 - 8009 + "\u0000\u0000\u0000\u0000\u0000\uC9E6\u0000\u0000\u0000\uCBEC" + // 8010 - 8014 + "\u0000\uC9A8\u0000\u0000\u0000\u0000\u0000\uBFDD\u0000\uEEC7" + // 8015 - 8019 + "\u0000\u0000\u0000\uEEC8\u0000\u0000\u0000\u0000\u0000\u0000" + // 8020 - 8024 + "\u0000\uEEC9\u0000\uCDEF\u0000\u0000\u0000\uBDB7\u0000\u0000" + // 8025 - 8029 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEECB" + // 8030 - 8034 + "\u0000\uEECA\u0000\u0000\u0000\uB9DA\u0000\u0000\u0000\uB9F3" + // 8035 - 8039 + "\u0000\uBBC0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8040 - 8044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3E7\u0000\uF3E8" + // 8045 - 8049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8050 - 8054 + "\u0000\uC5A4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8055 - 8059 + "\u0000\uB8DD\u0000\u0000\u0000\uF3EA\u0000\u0000\u0000\u0000" + // 8060 - 8064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8065 - 8069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDA9\u0000\u0000" + // 8070 - 8074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECFC" + // 8075 - 8079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECFD\u0000\uECFB" + // 8080 - 8084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8085 - 8089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8090 - 8094 + "\u0000\uE0FB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8095 - 8099 + "\u0000\uE0FC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8100 - 8104 + "\u0000\uE0FD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8105 - 8109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1BB\u0000\u0000" + // 8110 - 8114 + "\u0000\u0000\u0000\u0000\u0000\uD2CC\u0000\u0000\u0000\uCCF1" + // 8115 - 8119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8120 - 8124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8125 - 8129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uB4C7" + // 8130 - 8134 + "\u0000\u0000\u0000\u0000\u0000\uD2CD\u0000\u0000\u0000\uCED2" + // 8135 - 8139 + "\u0000\u0000\u0000\uB8FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 8140 - 8144 + "\u0000\u0000\u0000\uB8B6\u0000\uCDD4\u0000\uCFB7\u0000\u0000" + // 8145 - 8149 + "\u0000\uB9CD\u0000\uE6CE\u0000\uBCD4\u0000\uE6CD\u0000\u0000" + // 8150 - 8154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6CF\u0000\uBCA9" + // 8155 - 8159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2D1\u0000\u0000" + // 8160 - 8164 + "\u0000\uE6D0\u0000\u0000\u0000\u0000\u0000\uB9CC\u0000\u0000" + // 8165 - 8169 + "\u0000\uCCD7\u0000\uE6D1\u0000\uE6D2\u0000\u0000\u0000\u0000" + // 8170 - 8174 + "\u0000\uE6D3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2F5" + // 8175 - 8179 + "\u0000\u0000\u0000\u0000\u0000\uF2F3\u0000\u0000\u0000\uB3FB" + // 8180 - 8184 + "\u0000\u0000\u0000\uF2F2\u0000\uBCB2\u0000\uB2A9\u0000\u0000" + // 8185 - 8189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8190 - 8194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8195 - 8199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB9E3\u0000\u0000" + // 8200 - 8204 + "\u0000\u0000\u0000\uF2FC\u0000\uF2FB\u0000\uBFE9\u0000\uE6C7" + // 8205 - 8209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6C8" + // 8210 - 8214 + "\u0000\u0000\u0000\u0000\u0000\uE6C9\u0000\u0000\u0000\uB4E5" + // 8215 - 8219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4CD" + // 8220 - 8224 + "\u0000\u0000\u0000\u0000\u0000\uE6CA\u0000\u0000\u0000\u0000" + // 8225 - 8229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6CB\u0000\u0000" + // 8230 - 8234 + "\u0000\uCBDD\u0000\uCDE3\u0000\u0000\u0000\u0000\u0000\u0000" + // 8235 - 8239 + "\u0000\uF2F0\u0000\u0000\u0000\u0000\u0000\uF2F1\u0000\uC6BE" + // 8240 - 8244 + "\u0000\uF2EE\u0000\uF2ED\u0000\u0000\u0000\u0000\u0000\u0000" + // 8245 - 8249 + "\u0000\u0000\u0000\uB2AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 8250 - 8254 + "\u0000\uF2F9\u0000\u0000\u0000\u0000\u0000\uF2F8\u0000\u0000" + // 8255 - 8259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1F5" + // 8260 - 8264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2F6\u0000\uE6AB" + // 8265 - 8269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8270 - 8274 + "\u0000\u0000\u0000\uE6AE\u0000\uE6AC\u0000\uE6AD\u0000\uBAE1" + // 8275 - 8279 + "\u0000\uB7D3\u0000\u0000\u0000\u0000\u0000\uC3D6\u0000\u0000" + // 8280 - 8284 + "\u0000\uC8B3\u0000\u0000\u0000\uBDF0\u0000\u0000\u0000\u0000" + // 8285 - 8289 + "\u0000\uC7CD\u0000\u0000\u0000\uC8ED\u0000\uE6AF\u0000\uD8ED" + // 8290 - 8294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8295 - 8299 + "\u0000\u0000\u0000\uE6D9\u0000\u0000\u0000\u0000\u0000\u0000" + // 8300 - 8304 + "\u0000\uE6D8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8305 - 8309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6DA\u0000\u0000" + // 8310 - 8314 + "\u0000\u0000\u0000\u0000\u0000\uC0BB\u0000\u0000\u0000\uE6DB" + // 8315 - 8319 + "\u0000\u0000\u0000\uE6DC\u0000\u0000\u0000\u0000\u0000\u0000" + // 8320 - 8324 + "\u0000\uCAB9\u0000\uE6DD\u0000\uC4F9\u0000\uE5CE\u0000\u0000" + // 8325 - 8329 + "\u0000\u0000\u0000\uE5CA\u0000\u0000\u0000\u0000\u0000\u0000" + // 8330 - 8334 + "\u0000\uCAD4\u0000\uB4CB\u0000\u0000\u0000\u0000\u0000\uCCCB" + // 8335 - 8339 + "\u0000\u0000\u0000\u0000\u0000\uB0DE\u0000\u0000\u0000\u0000" + // 8340 - 8344 + "\u0000\uE5CD\u0000\u0000\u0000\uCEFD\u0000\u0000\u0000\u0000" + // 8345 - 8349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5CC" + // 8350 - 8354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEFD" + // 8355 - 8359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uE4E9\u0000\u0000" + // 8360 - 8364 + "\u0000\u0000\u0000\uC6E9\u0000\u0000\u0000\uC5D5\u0000\u0000" + // 8365 - 8369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8370 - 8374 + "\u0000\uC4D7\u0000\u0000\u0000\uEFAC\u008F\uE4EF\u0000\u0000" + // 8375 - 8379 + "\u0000\u0000\u0000\u0000\u0000\uC3C3\u0000\uEFA8\u0000\u0000" + // 8380 - 8384 + "\u0000\u0000\u0000\u0000\u0000\uF1AD\u0000\u0000\u0000\uCCDF" + // 8385 - 8389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1AE\u0000\uCDDC" + // 8390 - 8394 + "\u0000\u0000\u0000\uB1C2\u0000\u0000\u0000\u0000\u0000\u0000" + // 8395 - 8399 + "\u0000\uBBC1\u0000\u0000\u0000\uF1AF\u0000\uB2EE\u0000\uF1B0" + // 8400 - 8404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1B1\u0000\u0000" + // 8405 - 8409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1B3\u0000\uF1B4" + // 8410 - 8414 + "\u0000\u0000\u0000\uC1EF\u0000\uE6DE\u0000\u0000\u0000\u0000" + // 8415 - 8419 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6DF\u0000\u0000" + // 8420 - 8424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8425 - 8429 + "\u0000\uCEFE\u0000\uE6E2\u0000\u0000\u0000\uE6E1\u0000\uE6E0" + // 8430 - 8434 + "\u0000\uC4B0\u0000\u0000\u0000\uE6E3\u0000\uBFA6\u0000\u0000" + // 8435 - 8439 + "\u0000\uE6E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6E5" + // 8440 - 8444 + "\u0000\uCFB8\u0000\uE6E6\u008F\uF4D4\u0000\u0000\u0000\uE5C2" + // 8445 - 8449 + "\u0000\uE5BC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8450 - 8454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5C0\u0000\uBCFA" + // 8455 - 8459 + "\u0000\uB0DD\u0000\uE5BB\u0000\uE5C3\u0000\uE5C7\u0000\uB9CB" + // 8460 - 8464 + "\u0000\uCCD6\u0000\u0000\u0000\uC4D6\u0000\uE5BD\u0000\u0000" + // 8465 - 8469 + "\u008F\uD4A7\u0000\uE5C5\u0000\u0000\u0000\uE5BA\u0000\uC3BE" + // 8470 - 8474 + "\u0000\u0000\u0000\uE5BF\u0000\uB0BD\u0000\uCCCA\u0000\uC1C7" + // 8475 - 8479 + "\u0000\uCBC2\u0000\uBAF7\u0000\u0000\u0000\u0000\u0000\u0000" + // 8480 - 8484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8485 - 8489 + "\u0000\uBBE7\u0000\uC4DD\u0000\u0000\u0000\uE5A7\u0000\uCEDF" + // 8490 - 8494 + "\u0000\uBAD9\u0000\u0000\u0000\uE5A8\u0000\uBFC2\u0000\u0000" + // 8495 - 8499 + "\u0000\uE5AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBED2" + // 8500 - 8504 + "\u0000\uBAB0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8505 - 8509 + "\u0000\uE5A9\u0000\uB5AA\u0000\u0000\u0000\uE5A1\u0000\u0000" + // 8510 - 8514 + "\u0000\uCCF3\u0000\uB9C8\u0000\uE4FE\u0000\u0000\u0000\u0000" + // 8515 - 8519 + "\u0000\u0000\u0000\uE5A4\u0000\uCCE6\u0000\u0000\u0000\uC7BC" + // 8520 - 8524 + "\u0000\u0000\u0000\u0000\u0000\uC9B3\u0000\u0000\u0000\u0000" + // 8525 - 8529 + "\u0000\u0000\u0000\uBDE3\u0000\uE5A3\u0000\u0000\u0000\uBCD3" + // 8530 - 8534 + "\u0000\uB9C9\u0000\uBBE6\u0000\uB5E9\u0000\uCAB6\u0000\uE5A2" + // 8535 - 8539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFEA\u0000\u0000" + // 8540 - 8544 + "\u0000\u0000\u0000\u0000\u0000\uB0C7\u0000\u0000\u0000\u0000" + // 8545 - 8549 + "\u0000\uEFE8\u0000\u0000\u0000\uEFEC\u0000\uEFEB\u0000\u0000" + // 8550 - 8554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8555 - 8559 + "\u0000\uEFEE\u0000\uEFED\u0000\uEFEF\u0000\u0000\u0000\uC6AE" + // 8560 - 8564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFF0\u0000\u0000" + // 8565 - 8569 + "\u0000\u0000\u0000\u0000\u0000\uF0E7\u0000\u0000\u0000\u0000" + // 8570 - 8574 + "\u0000\uF0E8\u0000\u0000\u0000\uF0E9\u0000\u0000\u0000\u0000" + // 8575 - 8579 + "\u0000\uF0EA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8580 - 8584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4DA\u0000\u0000" + // 8585 - 8589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8590 - 8594 + "\u0000\u0000\u0000\u0000\u0000\uF0EB\u0000\u0000\u0000\u0000" + // 8595 - 8599 + "\u0000\u0000\u0000\uF1A9\u0000\uF1A8\u0000\u0000\u0000\uF1AA" + // 8600 - 8604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8605 - 8609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8610 - 8614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8615 - 8619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8620 - 8624 + "\u0000\uC8F4\u0000\uE6CC\u0000\u0000\u0000\u0000\u0000\uBFA9" + // 8625 - 8629 + "\u0000\uB9C7\u0000\u0000\u0000\uE4F7\u0000\u0000\u0000\u0000" + // 8630 - 8634 + "\u0000\u0000\u0000\u0000\u0000\uCEC8\u0000\u0000\u0000\u0000" + // 8635 - 8639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8640 - 8644 + "\u0000\uE4F9\u0000\u0000\u0000\u0000\u0000\uE4FA\u0000\u0000" + // 8645 - 8649 + "\u0000\uE4FB\u0000\u0000\u0000\uE4FC\u0000\u0000\u0000\uBBE5" + // 8650 - 8654 + "\u0000\u0000\u0000\uE4FD\u0000\uB7CF\u0000\u0000\u0000\u0000" + // 8655 - 8659 + "\u0000\uB5EA\u0000\u0000\u0000\uB2A7\u0000\u0000\u0000\u0000" + // 8660 - 8664 + "\u0000\u0000\u0000\uE6C2\u0000\uE6C3\u0000\u0000\u0000\u0000" + // 8665 - 8669 + "\u0000\u0000\u0000\uE6C4\u0000\u0000\u0000\uCDE2\u0000\u0000" + // 8670 - 8674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDAC" + // 8675 - 8679 + "\u0000\u0000\u0000\uE6C6\u0000\uE6C5\u0000\u0000\u0000\u0000" + // 8680 - 8684 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8685 - 8689 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDCC\u0000\u0000" + // 8690 - 8694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8695 - 8699 + "\u0000\u0000\u0000\u0000\u0000\uC9F6\u0000\uDCB8\u0000\uC2CA" + // 8700 - 8704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCBE\u0000\uC1BF" + // 8705 - 8709 + "\u0000\u0000\u0000\uDCB5\u0000\uDCC2\u0000\uDCC1\u0000\u0000" + // 8710 - 8714 + "\u0000\uE6D4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8715 - 8719 + "\u0000\u0000\u0000\u0000\u0000\uE6D5\u0000\u0000\u0000\u0000" + // 8720 - 8724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8725 - 8729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCAA\u0000\u0000" + // 8730 - 8734 + "\u0000\u0000\u0000\uCCED\u0000\u0000\u0000\u0000\u0000\u0000" + // 8735 - 8739 + "\u0000\u0000\u0000\uE6D7\u0000\u0000\u0000\uC3BF\u0000\u0000" + // 8740 - 8744 + "\u0000\uE6D6\u0000\uE4F1\u0000\u0000\u0000\uE4F3\u0000\u0000" + // 8745 - 8749 + "\u0000\u0000\u0000\uE4F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 8750 - 8754 + "\u0000\u0000\u0000\uB8D2\u0000\u0000\u0000\u0000\u0000\u0000" + // 8755 - 8759 + "\u0000\uC1B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4F5" + // 8760 - 8764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5FC\u0000\u0000" + // 8765 - 8769 + "\u0000\uE4F4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4F6" + // 8770 - 8774 + "\u0000\u0000\u0000\uCAB5\u0000\uC1EC\u0000\uE4C6\u0000\u0000" + // 8775 - 8779 + "\u0000\u0000\u0000\u0000\u0000\uE4DE\u0000\uE4E0\u0000\u0000" + // 8780 - 8784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8785 - 8789 + "\u0000\uE4E1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8790 - 8794 + "\u0000\u0000\u0000\u0000\u0000\uCAC6\u0000\u0000\u0000\uE4E2" + // 8795 - 8799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8800 - 8804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCE2\u0000\u0000" + // 8805 - 8809 + "\u008F\uD5AE\u0000\u0000\u0000\uE6BA\u0000\uB7B2\u0000\u0000" + // 8810 - 8814 + "\u0000\u0000\u0000\u0000\u0000\uC1A2\u0000\uB5C1\u0000\u0000" + // 8815 - 8819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6BE\u0000\uE6BB" + // 8820 - 8824 + "\u0000\u0000\u0000\u0000\u0000\uE6BC\u0000\u0000\u0000\u0000" + // 8825 - 8829 + "\u0000\u0000\u0000\uE6BF\u0000\u0000\u0000\uE6C0\u0000\uE6BD" + // 8830 - 8834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1A9\u0000\u0000" + // 8835 - 8839 + "\u0000\u0000\u0000\uEDD8\u0000\u0000\u0000\uB3ED\u0000\uEDD7" + // 8840 - 8844 + "\u0000\uEDDC\u0000\u0000\u0000\u0000\u0000\uEDDB\u0000\u0000" + // 8845 - 8849 + "\u0000\u0000\u0000\uEDDA\u0000\uC5B2\u0000\uEDDD\u0000\u0000" + // 8850 - 8854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8855 - 8859 + "\u0000\u0000\u0000\u0000\u0000\uEDDE\u0000\u0000\u0000\u0000" + // 8860 - 8864 + "\u0000\u0000\u0000\u0000\u0000\uEDDF\u0000\u0000\u0000\u0000" + // 8865 - 8869 + "\u0000\uB9EC\u0000\uE4DA\u0000\u0000\u0000\u0000\u0000\uE4D7" + // 8870 - 8874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8875 - 8879 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4D6\u0000\uC0D2" + // 8880 - 8884 + "\u0000\u0000\u0000\uE4D9\u0000\uE4DB\u0000\u0000\u0000\u0000" + // 8885 - 8889 + "\u0000\u0000\u0000\uE4D8\u0000\u0000\u0000\uE4DF\u0000\u0000" + // 8890 - 8894 + "\u0000\uE4DC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8895 - 8899 + "\u0000\u0000\u0000\u0000\u0000\uE4DD\u0000\uE4C7\u0000\u0000" + // 8900 - 8904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8905 - 8909 + "\u0000\uE4C8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8910 - 8914 + "\u0000\u0000\u0000\uE4CD\u0000\u0000\u0000\u0000\u0000\u0000" + // 8915 - 8919 + "\u0000\uE4C2\u0000\uD2D5\u0000\uE4C9\u0000\uE4C3\u0000\u0000" + // 8920 - 8924 + "\u0000\u0000\u0000\uE4CC\u0000\u0000\u0000\u0000\u0000\u0000" + // 8925 - 8929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3BD\u0000\uE4D2" + // 8930 - 8934 + "\u0000\uBCC4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6C6" + // 8935 - 8939 + "\u0000\uE4C5\u0000\uE4C4\u0000\u0000\u0000\u0000\u0000\uE4C1" + // 8940 - 8944 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFB6\u0000\u0000" + // 8945 - 8949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4CA" + // 8950 - 8954 + "\u0000\u0000\u0000\u0000\u0000\uE4CE\u0000\uE4CB\u0000\u0000" + // 8955 - 8959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8960 - 8964 + "\u0000\u0000\u0000\u0000\u0000\uCEF5\u0000\u0000\u0000\u0000" + // 8965 - 8969 + "\u0000\uDFDE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1A8" + // 8970 - 8974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8975 - 8979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFE0" + // 8980 - 8984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFDF\u0000\u0000" + // 8985 - 8989 + "\u0000\uDFDD\u0000\uC0E1\u0000\uE4BB\u0000\u0000\u0000\u0000" + // 8990 - 8994 + "\u0000\uC8CF\u0000\u0000\u0000\uE4BF\u0000\uCAD3\u0000\u0000" + // 8995 - 8999 + "\u0000\uC3DB\u0000\u0000\u0000\uE4BA\u0000\uE4BC\u0000\u0000" + // 9000 - 9004 + "\u0000\u0000\u0000\uE4BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 9005 - 9009 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9010 - 9014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9015 - 9019 + "\u0000\uE4C0\u0000\u0000\u0000\u0000\u0000\uECFA\u0000\u0000" + // 9020 - 9024 + "\u0000\uC4FD\u0000\u0000\u0000\u0000\u0000\uEDA1\u0000\uEDA5" + // 9025 - 9029 + "\u0000\uEDA2\u0000\uECFE\u0000\u0000\u0000\uEDA3\u0000\u0000" + // 9030 - 9034 + "\u0000\u0000\u0000\u0000\u0000\uEDA4\u0000\u0000\u0000\u0000" + // 9035 - 9039 + "\u0000\u0000\u0000\u0000\u0000\uEDAB\u0000\u0000\u0000\u0000" + // 9040 - 9044 + "\u0000\u0000\u0000\uEDA6\u0000\u0000\u0000\u0000\u0000\u0000" + // 9045 - 9049 + "\u0000\u0000\u0000\u0000\u0000\uC0D8\u0000\uB3DE\u0000\u0000" + // 9050 - 9054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFDA\u0000\uC9E4" + // 9055 - 9059 + "\u0000\u0000\u0000\uE3FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 9060 - 9064 + "\u0000\uC2E8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9065 - 9069 + "\u0000\u0000\u0000\u0000\u0000\uE3F7\u0000\u0000\u0000\uE3FB" + // 9070 - 9074 + "\u0000\uE3FD\u0000\u0000\u0000\u0000\u0000\uBAFB\u0000\u0000" + // 9075 - 9079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9080 - 9084 + "\u0000\uCACF\u0000\uB2D5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9085 - 9089 + "\u0000\uE4B5\u0000\u0000\u0000\uE4B2\u0000\u0000\u0000\uE4B7" + // 9090 - 9094 + "\u0000\u0000\u0000\u0000\u0000\uE4B6\u0000\u0000\u0000\uC7F3" + // 9095 - 9099 + "\u0000\uCCA7\u0000\u0000\u0000\uBBBB\u0000\uE4B0\u0000\uE4B9" + // 9100 - 9104 + "\u0000\uE4B4\u0000\u0000\u0000\uE4B3\u0000\uE4AF\u008F\uD2BB" + // 9105 - 9109 + "\u0000\uE4B1\u0000\uBECF\u0000\uE3EE\u0000\uE3EF\u0000\uBDD7" + // 9110 - 9114 + "\u0000\u0000\u0000\uC6B8\u0000\uE3F0\u008F\uF4D0\u0000\u0000" + // 9115 - 9119 + "\u0000\u0000\u0000\uC3A8\u008F\uD1EC\u0000\u0000\u0000\uE3F1" + // 9120 - 9124 + "\u0000\u0000\u0000\uC3BC\u0000\uE3F2\u0000\u0000\u0000\u0000" + // 9125 - 9129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6A5\u0000\u0000" + // 9130 - 9134 + "\u0000\uD1BF\u0000\uC3DD\u0000\uBCB3\u0000\u0000\u0000\u0000" + // 9135 - 9139 + "\u0000\u0000\u0000\u0000\u0000\uB4C8\u0000\uB9F2\u0000\u0000" + // 9140 - 9144 + "\u0000\uCAE6\u0000\uE3CE\u0000\u0000\u0000\u0000\u0000\uCBD4" + // 9145 - 9149 + "\u0000\u0000\u0000\u0000\u0000\uE3D0\u0000\u0000\u0000\u0000" + // 9150 - 9154 + "\u0000\u0000\u0000\uC0D1\u0000\uB1CF\u0000\uB2BA\u0000\uB0AC" + // 9155 - 9159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9160 - 9164 + "\u0000\u0000\u0000\uE3CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 9165 - 9169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9170 - 9174 + "\u0000\uDDE1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9175 - 9179 + "\u0000\u0000\u0000\u0000\u0000\uBBE1\u0000\u0000\u0000\uCCB1" + // 9180 - 9184 + "\u0000\u0000\u0000\uDDE2\u0000\uDDE3\u0000\u0000\u0000\u0000" + // 9185 - 9189 + "\u0000\uB5A4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDE4" + // 9190 - 9194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECD3" + // 9195 - 9199 + "\u0000\uECD4\u0000\u0000\u0000\uECD6\u0000\uC2A3\u0000\u0000" + // 9200 - 9204 + "\u0000\uECD5\u0000\uB4E6\u0000\u0000\u0000\uECD8\u0000\u0000" + // 9205 - 9209 + "\u0000\uECD7\u0000\uECD9\u0000\u0000\u008F\uDFC3\u0000\uECDB" + // 9210 - 9214 + "\u0000\uECDD\u0000\u0000\u0000\uECDE\u0000\u0000\u0000\u0000" + // 9215 - 9219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9220 - 9224 + "\u0000\u0000\u0000\u0000\u0000\uB4E1\u0000\u0000\u0000\u0000" + // 9225 - 9229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9230 - 9234 + "\u0000\u0000\u0000\uCEE8\u0000\uE0DE\u0000\u0000\u0000\u0000" + // 9235 - 9239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9240 - 9244 + "\u0000\uE0E0\u0000\u0000\u008F\uCBE6\u0000\u0000\u0000\u0000" + // 9245 - 9249 + "\u0000\uECE5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9250 - 9254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECED" + // 9255 - 9259 + "\u0000\uECEB\u0000\u0000\u0000\u0000\u0000\uECE8\u0000\u0000" + // 9260 - 9264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9265 - 9269 + "\u0000\uECEA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECE9" + // 9270 - 9274 + "\u0000\uECEC\u0000\u0000\u0000\uB5F7\u0000\u0000\u0000\uECF0" + // 9275 - 9279 + "\u0000\uE3C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9280 - 9284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9285 - 9289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCEF" + // 9290 - 9294 + "\u0000\u0000\u0000\u0000\u0000\uE3CA\u0000\uB0F0\u0000\u0000" + // 9295 - 9299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3CD\u0000\u0000" + // 9300 - 9304 + "\u0000\u0000\u0000\u0000\u0000\uE3CB\u0000\uB2D4\u0000\uB7CE" + // 9305 - 9309 + "\u0000\uE3CC\u0000\uB9C6\u0000\uB5A9\u0000\u0000\u0000\u0000" + // 9310 - 9314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9315 - 9319 + "\u0000\uE3C3\u0000\u0000\u0000\u0000\u0000\uC4F8\u0000\u0000" + // 9320 - 9324 + "\u0000\uE3C4\u0000\uC0C7\u0000\u0000\u0000\u0000\u0000\u0000" + // 9325 - 9329 + "\u0000\u0000\u0000\u0000\u0000\uCCAD\u0000\u0000\u0000\u0000" + // 9330 - 9334 + "\u0000\uC9A3\u0000\uE3C5\u0000\uE3C6\u0000\uC3D5\u0000\u0000" + // 9335 - 9339 + "\u0000\uCEC7\u0000\u0000\u0000\u0000\u0000\uE3C8\u0000\uBDA8" + // 9340 - 9344 + "\u0000\uBBE4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9345 - 9349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3BD\u0000\u0000" + // 9350 - 9354 + "\u0000\uBDA9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9355 - 9359 + "\u0000\u0000\u0000\uB2CA\u0000\uC9C3\u0000\u0000\u0000\u0000" + // 9360 - 9364 + "\u0000\uE3BE\u0000\u0000\u0000\u0000\u0000\uC8EB\u0000\u0000" + // 9365 - 9369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9370 - 9374 + "\u0000\uC1C5\u0000\uE3C9\u0000\uB6D8\u0000\u0000\u0000\u0000" + // 9375 - 9379 + "\u0000\uCFBD\u0000\uC1B5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9380 - 9384 + "\u0000\u0000\u0000\uE3B4\u0000\u0000\u0000\u0000\u0000\uB2D2" + // 9385 - 9389 + "\u0000\uC4F7\u0000\uCAA1\u0000\u0000\u0000\u0000\u0000\u0000" + // 9390 - 9394 + "\u0000\u0000\u008F\uD0E5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9395 - 9399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uD0E9\u0000\u0000" + // 9400 - 9404 + "\u0000\uE3B5\u0000\u0000\u0000\u0000\u0000\uECBA\u0000\u0000" + // 9405 - 9409 + "\u0000\u0000\u0000\uECBC\u0000\u0000\u0000\u0000\u0000\u0000" + // 9410 - 9414 + "\u0000\uECBB\u0000\uECBD\u0000\u0000\u0000\uCBC6\u0000\uECBE" + // 9415 - 9419 + "\u0000\uECBF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9420 - 9424 + "\u0000\u0000\u0000\uECC0\u0000\u0000\u0000\u0000\u0000\u0000" + // 9425 - 9429 + "\u0000\uECC2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9430 - 9434 + "\u0000\uB3AD\u0000\uC4E7\u0000\u0000\u0000\uE5D2\u0000\uE5D8" + // 9435 - 9439 + "\u0000\uE5D1\u0000\u0000\u0000\u0000\u0000\uBDC4\u0000\u0000" + // 9440 - 9444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBA5\u0000\u0000" + // 9445 - 9449 + "\u0000\u0000\u0000\uBDCC\u0000\u0000\u0000\u0000\u0000\uE5D4" + // 9450 - 9454 + "\u0000\uE5E0\u0000\u0000\u0000\u0000\u0000\uE5DC\u0000\u0000" + // 9455 - 9459 + "\u0000\uE5DF\u0000\u0000\u0000\uE5DD\u0000\uE5E1\u0000\uE5DB" + // 9460 - 9464 + "\u0000\u0000\u0000\uE5C1\u0000\uC0D3\u0000\u0000\u0000\uC8CB" + // 9465 - 9469 + "\u0000\u0000\u0000\uE5DE\u0000\u0000\u0000\u0000\u0000\uE5D9" + // 9470 - 9474 + "\u0000\u0000\u0000\uE5DA\u0000\u0000\u0000\uC1A1\u0000\uB7D2" + // 9475 - 9479 + "\u0000\u0000\u0000\uBDAB\u0000\u0000\u0000\u0000\u0000\u0000" + // 9480 - 9484 + "\u0000\u0000\u008F\uD4D4\u0000\u0000\u0000\uBFA5\u0000\uC1B6" + // 9485 - 9489 + "\u0000\uE5E4\u0000\u0000\u0000\u0000\u0000\uE5E6\u0000\uE5E7" + // 9490 - 9494 + "\u0000\u0000\u0000\u0000\u0000\uE5E3\u0000\uE5E5\u0000\u0000" + // 9495 - 9499 + "\u0000\uBDAB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9500 - 9504 + "\u0000\uE5DA\u0000\uE5E2\u0000\u0000\u0000\uE5EA\u0000\uE5E9" + // 9505 - 9509 + "\u0000\uB7D2\u0000\u0000\u0000\uCBFA\u0000\u0000\u0000\u0000" + // 9510 - 9514 + "\u0000\uB7AB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9515 - 9519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5E8" + // 9520 - 9524 + "\u0000\u0000\u0000\uE5EC\u0000\uE5EB\u0000\uE5EF\u0000\u0000" + // 9525 - 9529 + "\u0000\uE5F1\u0000\uE3AC\u0000\u0000\u0000\uC7AA\u0000\u0000" + // 9530 - 9534 + "\u0000\u0000\u0000\uBECD\u0000\u0000\u0000\u0000\u0000\uC9BC" + // 9535 - 9539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAD7" + // 9540 - 9544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9545 - 9549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5F8" + // 9550 - 9554 + "\u0000\u0000\u0000\u0000\u0000\uE3B2\u0000\u0000\u0000\u0000" + // 9555 - 9559 + "\u0000\u0000\u0000\u0000\u0000\uE3B3\u0000\uE3AB\u0000\uB7B7" + // 9560 - 9564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9565 - 9569 + "\u0000\uB5C0\u0000\uB5A7\u0000\uBBE3\u0000\u0000\u0000\u0000" + // 9570 - 9574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDB4" + // 9575 - 9579 + "\u0000\u0000\u0000\u0000\u0000\uE3B1\u0000\u0000\u0000\uE3B0" + // 9580 - 9584 + "\u0000\uC1C4\u0000\uE3AD\u0000\u0000\u0000\u0000\u0000\uE3AF" + // 9585 - 9589 + "\u0000\u0000\u0000\u0000\u0000\uBDCB\u0000\uBFC0\u0000\uE3AE" + // 9590 - 9594 + "\u0000\uE2E9\u008F\uCFD5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9595 - 9599 + "\u0000\uC5D6\u0000\uBAD6\u0000\uB5CE\u0000\u0000\u0000\u0000" + // 9600 - 9604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9605 - 9609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBA4\u0000\u0000" + // 9610 - 9614 + "\u0000\uC7CB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9615 - 9619 + "\u0000\u0000\u0000\uC5D7\u0000\u0000\u0000\u0000\u0000\u0000" + // 9620 - 9624 + "\u0000\u0000\u0000\uB9DC\u0000\uE2D5\u0000\u0000\u0000\u0000" + // 9625 - 9629 + "\u0000\u0000\u0000\u0000\u0000\uCACD\u0000\u0000\u0000\u0000" + // 9630 - 9634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDD6" + // 9635 - 9639 + "\u0000\uCEC6\u0000\u0000\u0000\u0000\u0000\uE2D7\u0000\u0000" + // 9640 - 9644 + "\u0000\u0000\u0000\uC6B7\u0000\u0000\u0000\u0000\u0000\uE2D8" + // 9645 - 9649 + "\u0000\u0000\u0000\u0000\u0000\uE2D9\u0000\u0000\u0000\uE2DD" + // 9650 - 9654 + "\u0000\uE2DB\u0000\uE2DC\u0000\u0000\u0000\uE2DA\u0000\uC3E5" + // 9655 - 9659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9660 - 9664 + "\u008F\uCEF2\u0000\uE2C9\u0000\u0000\u0000\u0000\u0000\u0000" + // 9665 - 9669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9670 - 9674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9675 - 9679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9680 - 9684 + "\u0000\uE2CA\u0000\uE2CD\u0000\u0000\u0000\u0000\u0000\u0000" + // 9685 - 9689 + "\u0000\u0000\u0000\uE9D6\u0000\u0000\u0000\uE9D7\u0000\uBCD8" + // 9690 - 9694 + "\u0000\u0000\u0000\uE9D9\u0000\u0000\u0000\uC3C1\u0000\u0000" + // 9695 - 9699 + "\u0000\uB7D6\u0000\uB3C2\u0000\u0000\u0000\u0000\u0000\u0000" + // 9700 - 9704 + "\u0000\u0000\u0000\u0000\u0000\uE9DC\u0000\u0000\u0000\u0000" + // 9705 - 9709 + "\u0000\u0000\u0000\u0000\u0000\uB3BF\u0000\u0000\u0000\uE9E1" + // 9710 - 9714 + "\u0000\u0000\u0000\u0000\u0000\uE9DD\u0000\uE9E0\u0000\uCCB2" + // 9715 - 9719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2C2\u0000\uE2C4" + // 9720 - 9724 + "\u0000\uE2C5\u0000\u0000\u0000\u0000\u0000\uE2C1\u0000\u0000" + // 9725 - 9729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9730 - 9734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9735 - 9739 + "\u0000\u0000\u0000\u0000\u0000\uE2C7\u0000\uE2C8\u0000\u0000" + // 9740 - 9744 + "\u0000\uC4AF\u0000\u0000\u0000\uB4E3\u0000\u0000\u0000\u0000" + // 9745 - 9749 + "\u0000\u0000\u0000\uEFB7\u0000\u0000\u0000\u0000\u008F\uE5BF" + // 9750 - 9754 + "\u0000\u0000\u0000\uEFBA\u0000\u0000\u0000\u0000\u0000\u0000" + // 9755 - 9759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFB9" + // 9760 - 9764 + "\u0000\uC5AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9765 - 9769 + "\u0000\uEFB2\u0000\uEFB3\u0000\uEFB6\u0000\u0000\u0000\u0000" + // 9770 - 9774 + "\u0000\u0000\u0000\u0000\u0000\uEFB8\u008F\uE5C9\u0000\u0000" + // 9775 - 9779 + "\u0000\uE4E9\u0000\uE4E7\u0000\u0000\u0000\uE4E5\u0000\uB4A1" + // 9780 - 9784 + "\u0000\u0000\u0000\uBED1\u0000\uE4EA\u0000\u0000\u0000\u0000" + // 9785 - 9789 + "\u0000\uE4E8\u0000\u0000\u0000\uE4E6\u0000\uE4EE\u0000\u0000" + // 9790 - 9794 + "\u0000\u0000\u0000\uE4ED\u0000\uE4EC\u0000\uE4EB\u0000\u0000" + // 9795 - 9799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4EF" + // 9800 - 9804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4F0\u0000\uC0BA" + // 9805 - 9809 + "\u0000\u0000\u0000\uCDED\u0000\uB0BC\u0000\uE5B3\u0000\u0000" + // 9810 - 9814 + "\u0000\u0000\u0000\uB5EB\u0000\u0000\u0000\uE5B0\u0000\u0000" + // 9815 - 9819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B1" + // 9820 - 9824 + "\u0000\u0000\u0000\u0000\u0000\uC5FD\u0000\uE5AF\u0000\uE5AC" + // 9825 - 9829 + "\u0000\u0000\u0000\uB3A8\u0000\uC0E4\u0000\u0000\u0000\u0000" + // 9830 - 9834 + "\u0000\uB8A8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B8" + // 9835 - 9839 + "\u0000\u0000\u0000\u0000\u0000\uC5BE\u0000\uEDC4\u0000\u0000" + // 9840 - 9844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9845 - 9849 + "\u0000\u0000\u0000\uEDC7\u0000\u0000\u0000\u0000\u0000\u0000" + // 9850 - 9854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9855 - 9859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCB4" + // 9860 - 9864 + "\u0000\u0000\u0000\u0000\u0000\uEDC6\u0000\uEDC5\u0000\uB7DA" + // 9865 - 9869 + "\u0000\uEDC8\u0000\u0000\u0000\uB1EF\u0000\u0000\u0000\u0000" + // 9870 - 9874 + "\u0000\uC6EC\u0000\uE5CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 9875 - 9879 + "\u0000\uE5D6\u0000\uE5D0\u0000\uE5D7\u0000\u0000\u0000\u0000" + // 9880 - 9884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5D3" + // 9885 - 9889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9890 - 9894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7FB\u0000\u0000" + // 9895 - 9899 + "\u0000\u0000\u0000\uBCCA\u0000\uE5D5\u0000\uE2A5\u0000\u0000" + // 9900 - 9904 + "\u008F\uF4C7\u0000\uE2A6\u0000\uC5AA\u0000\u0000\u0000\uB3A7" + // 9905 - 9909 + "\u0000\uB9C4\u0000\uE2A7\u0000\u0000\u0000\u0000\u0000\uE2A8" + // 9910 - 9914 + "\u0000\u0000\u0000\u0000\u0000\uE2A9\u0000\u0000\u0000\uBBA9" + // 9915 - 9919 + "\u0000\u0000\u0000\u0000\u0000\uE2AB\u0000\u0000\u0000\u0000" + // 9920 - 9924 + "\u0000\uE2AA\u0000\u0000\u0000\u0000\u0000\uE2AC\u0000\uE2AD" + // 9925 - 9929 + "\u008F\uCEBA\u008F\uCEBB\u0000\u0000\u008F\uF4C8\u0000\u0000" + // 9930 - 9934 + "\u0000\uB6CE\u0000\uB7A9\u0000\uE4E3\u0000\u0000\u0000\u0000" + // 9935 - 9939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAB4\u0000\u0000" + // 9940 - 9944 + "\u0000\uBFE8\u0000\u0000\u0000\uCCB0\u0000\u0000\u0000\u0000" + // 9945 - 9949 + "\u0000\uE4E4\u0000\u0000\u0000\uCEB3\u0000\u0000\u0000\u0000" + // 9950 - 9954 + "\u0000\uC7F4\u0000\u0000\u0000\uC1C6\u0000\uC7B4\u0000\u0000" + // 9955 - 9959 + "\u0000\u0000\u0000\uBDCD\u0000\u0000\u0000\u0000\u0000\u0000" + // 9960 - 9964 + "\u0000\uB0C0\u0000\uE1EB\u0000\uE1EC\u0000\uE1ED\u0000\u0000" + // 9965 - 9969 + "\u0000\uE1EE\u0000\u0000\u0000\uC1E9\u0000\uE1EA\u0000\u0000" + // 9970 - 9974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9975 - 9979 + "\u0000\u0000\u0000\u0000\u0000\uE1F0\u0000\u0000\u0000\u0000" + // 9980 - 9984 + "\u0000\u0000\u0000\uE1EF\u0000\u0000\u0000\u0000\u0000\u0000" + // 9985 - 9989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1F1\u0000\u0000" + // 9990 - 9994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6A3\u0000\uE9BB" + // 9995 - 9999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8CD\u0000\uE9AE" + // 10000 - 10004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10005 - 10009 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10010 - 10014 + "\u0000\u0000\u0000\u0000\u0000\uBDF3\u0000\u0000\u0000\uE9BD" + // 10015 - 10019 + "\u0000\uE9C2\u0000\uC1F4\u0000\u0000\u0000\u0000\u0000\uE9C1" + // 10020 - 10024 + "\u0000\u0000\u0000\uB4C9\u0000\u0000\u0000\u0000\u0000\u0000" + // 10025 - 10029 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10030 - 10034 + "\u0000\uC3BD\u0000\u0000\u0000\u0000\u0000\uC0FD\u0000\u0000" + // 10035 - 10039 + "\u0000\u0000\u0000\u0000\u0000\uC8A2\u0000\u0000\u0000\u0000" + // 10040 - 10044 + "\u0000\uE4BE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8A4" + // 10045 - 10049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10050 - 10054 + "\u0000\u0000\u0000\u0000\u0000\uE9CC\u0000\u0000\u0000\u0000" + // 10055 - 10059 + "\u0000\u0000\u0000\uC3EE\u0000\u0000\u0000\u0000\u0000\u0000" + // 10060 - 10064 + "\u0000\u0000\u0000\u0000\u0000\uE9CD\u0000\u0000\u0000\u0000" + // 10065 - 10069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10070 - 10074 + "\u0000\uC6FA\u0000\u0000\u0000\uB0BA\u0000\u0000\u0000\u0000" + // 10075 - 10079 + "\u0000\u0000\u0000\u0000\u0000\uC0D6\u0000\u0000\u0000\uBCCF" + // 10080 - 10084 + "\u0000\uECDF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3D2" + // 10085 - 10089 + "\u0000\u0000\u0000\uECE0\u0000\u0000\u0000\u0000\u0000\uC1F6" + // 10090 - 10094 + "\u0000\uECE1\u0000\u0000\u0000\uECE2\u0000\uC9EB\u0000\u0000" + // 10095 - 10099 + "\u008F\uF4E1\u0000\uB5AF\u0000\u0000\u0000\u0000\u0000\u0000" + // 10100 - 10104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10105 - 10109 + "\u0000\uDFF1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFF2" + // 10110 - 10114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7AE" + // 10115 - 10119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10120 - 10124 + "\u0000\u0000\u0000\u0000\u0000\uDFF4\u0000\u0000\u0000\u0000" + // 10125 - 10129 + "\u0000\u0000\u0000\u0000\u0000\uDFF5\u0000\u0000\u0000\uB4CA" + // 10130 - 10134 + "\u0000\u0000\u0000\uE4CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 10135 - 10139 + "\u0000\uE4D0\u0000\u0000\u0000\u0000\u0000\uE4D1\u0000\uE4D4" + // 10140 - 10144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10145 - 10149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10150 - 10154 + "\u0000\u0000\u0000\uE4D3\u0000\uC8F6\u0000\u0000\u0000\u0000" + // 10155 - 10159 + "\u0000\u0000\u0000\u0000\u0000\uE4D5\u0000\uCEFC\u0000\uCAED" + // 10160 - 10164 + "\u0000\uC8AB\u0000\u0000\u0000\uC9AD\u0000\u0000\u0000\uE1BF" + // 10165 - 10169 + "\u0000\uCEAC\u0000\uB7CD\u0000\uE1C0\u0000\u0000\u0000\uE1BE" + // 10170 - 10174 + "\u0000\uC8D6\u0000\uE1C1\u0000\u0000\u0000\uE1C2\u0000\u0000" + // 10175 - 10179 + "\u008F\uCDBB\u0000\uB0DB\u0000\u0000\u0000\u0000\u0000\uBEF6" + // 10180 - 10184 + "\u0000\uE1C7\u0000\u0000\u0000\uE1C4\u0000\uC6ED\u0000\uE1C3" + // 10185 - 10189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10190 - 10194 + "\u0000\u0000\u0000\uB5A6\u0000\uE1A1\u0000\u0000\u0000\uC9BB" + // 10195 - 10199 + "\u0000\uE1A2\u0000\u0000\u0000\u0000\u0000\uB4A4\u0000\uE1A3" + // 10200 - 10204 + "\u0000\u0000\u0000\uE1A4\u0000\u0000\u0000\u0000\u0000\u0000" + // 10205 - 10209 + "\u0000\u0000\u0000\uE1A5\u0000\u0000\u0000\uE1A7\u0000\uE1A8" + // 10210 - 10214 + "\u0000\uE1A6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9D3" + // 10215 - 10219 + "\u0000\uE1AA\u0000\uE1A9\u0000\u0000\u0000\u0000\u0000\u0000" + // 10220 - 10224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5BE" + // 10225 - 10229 + "\u0000\u0000\u0000\u0000\u0000\uB6DB\u0000\uC8EC\u0000\u0000" + // 10230 - 10234 + "\u0000\u0000\u0000\u0000\u0000\uC1ED\u0000\u0000\u0000\uCED0" + // 10235 - 10239 + "\u0000\uBDEF\u0000\u0000\u0000\u0000\u0000\uE5EE\u008F\uF4D5" + // 10240 - 10244 + "\u0000\u0000\u0000\uE5C8\u0000\u0000\u0000\uC0FE\u0000\u0000" + // 10245 - 10249 + "\u0000\uE5C4\u0000\uE5C9\u0000\uE5CB\u0000\u0000\u0000\uC6CD" + // 10250 - 10254 + "\u0000\u0000\u0000\uC0E0\u0000\uBAF5\u0000\u0000\u0000\u0000" + // 10255 - 10259 + "\u0000\u0000\u0000\uE3D8\u0000\u0000\u0000\u0000\u0000\u0000" + // 10260 - 10264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10265 - 10269 + "\u0000\u0000\u0000\uC3E2\u0000\uC1EB\u0000\u0000\u0000\uE3DA" + // 10270 - 10274 + "\u0000\uE3DC\u0000\uE3D9\u0000\uE3DB\u0000\u0000\u0000\u0000" + // 10275 - 10279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7A2" + // 10280 - 10284 + "\u0000\uCEDC\u0000\u0000\u008F\uCCC2\u0000\uE0F4\u0000\uF4A4" + // 10285 - 10289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0F2" + // 10290 - 10294 + "\u0000\uE0F5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10295 - 10299 + "\u0000\uE0E7\u0000\uE0F3\u0000\u0000\u0000\u0000\u0000\uBABC" + // 10300 - 10304 + "\u0000\u0000\u0000\u0000\u0000\uE0F6\u0000\u0000\u0000\u0000" + // 10305 - 10309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10310 - 10314 + "\u0000\uE0F7\u0000\u0000\u0000\uE3C1\u0000\u0000\u0000\uE3C2" + // 10315 - 10319 + "\u0000\uC7E9\u0000\u0000\u0000\uBFC1\u0000\uE3BF\u0000\u0000" + // 10320 - 10324 + "\u0000\uC3E1\u0000\u0000\u0000\u0000\u0000\uE3C0\u0000\u0000" + // 10325 - 10329 + "\u0000\u0000\u0000\u0000\u0000\uBECE\u0000\u0000\u0000\u0000" + // 10330 - 10334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10335 - 10339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0DC\u0000\u0000" + // 10340 - 10344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8BA\u0000\u0000" + // 10345 - 10349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9DE\u0000\u0000" + // 10350 - 10354 + "\u0000\u0000\u0000\uE9DF\u0000\uC9C8\u0000\uC8DA\u0000\uE9E2" + // 10355 - 10359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10360 - 10364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2FD\u0000\uE9EC" + // 10365 - 10369 + "\u0000\u0000\u0000\uE9E8\u0000\u0000\u0000\u0000\u0000\uB2EB" + // 10370 - 10374 + "\u0000\u0000\u0000\uE3D1\u0000\uE3D2\u0000\uBEF7\u0000\u0000" + // 10375 - 10379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3D3" + // 10380 - 10384 + "\u0000\u0000\u0000\uB3CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 10385 - 10389 + "\u0000\u0000\u0000\uE3D5\u0000\u0000\u0000\u0000\u0000\u0000" + // 10390 - 10394 + "\u0000\uB7EA\u0000\u0000\u0000\uB5E6\u0000\u0000\u0000\u0000" + // 10395 - 10399 + "\u0000\uE3D6\u0000\uB6F5\u0000\u0000\u0000\u0000\u0000\uE3D7" + // 10400 - 10404 + "\u0000\u0000\u0000\uC0FC\u0000\uBCEE\u0000\u0000\u0000\u0000" + // 10405 - 10409 + "\u008F\uCBF4\u0000\u0000\u0000\uE0E2\u0000\u0000\u0000\u0000" + // 10410 - 10414 + "\u0000\u0000\u0000\u0000\u0000\uB7BE\u0000\u0000\u0000\u0000" + // 10415 - 10419 + "\u0000\uC8C9\u0000\uE0E3\u0000\u0000\u0000\u0000\u0000\uE0FE" + // 10420 - 10424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCBF9\u0000\u0000" + // 10425 - 10429 + "\u0000\u0000\u0000\uE0E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 10430 - 10434 + "\u0000\u0000\u0000\u0000\u0000\uB8BD\u0000\u0000\u0000\uBECC" + // 10435 - 10439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10440 - 10444 + "\u0000\uE3A5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10445 - 10449 + "\u0000\u0000\u0000\u0000\u0000\uC1C3\u0000\u0000\u0000\u0000" + // 10450 - 10454 + "\u0000\uE3A7\u0000\uE3A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 10455 - 10459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3A8\u0000\u0000" + // 10460 - 10464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10465 - 10469 + "\u0000\uB5FA\u0000\uE3B6\u0000\u0000\u0000\u0000\u0000\uE3B8" + // 10470 - 10474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3B9\u0000\u0000" + // 10475 - 10479 + "\u0000\uC7A9\u0000\uC5F8\u0000\u0000\u0000\uE3BA\u0000\u0000" + // 10480 - 10484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3BB" + // 10485 - 10489 + "\u0000\uE3BC\u0000\u0000\u0000\u0000\u0000\uB6D9\u0000\uB2D3" + // 10490 - 10494 + "\u0000\uC6C5\u0000\uE0E1\u0000\u0000\u0000\uB2D1\u0000\u0000" + // 10495 - 10499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0DD" + // 10500 - 10504 + "\u008F\uCBEA\u0000\uBBB9\u0000\u0000\u0000\u0000\u0000\uC4C1" + // 10505 - 10509 + "\u0000\uE0DF\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCBEE" + // 10510 - 10514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCBF0\u0000\u0000" + // 10515 - 10519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10520 - 10524 + "\u0000\u0000\u0000\uE0E4\u0000\u0000\u0000\uBCA7\u0000\u0000" + // 10525 - 10529 + "\u0000\u0000\u0000\u0000\u0000\uE2FC\u0000\uE2F7\u0000\u0000" + // 10530 - 10534 + "\u0000\u0000\u0000\u0000\u0000\uE2FD\u0000\uE2F8\u0000\u0000" + // 10535 - 10539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8D8\u0000\uE2F6" + // 10540 - 10544 + "\u0000\u0000\u0000\u0000\u0000\uE2F9\u0000\u0000\u0000\u0000" + // 10545 - 10549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3A2\u0000\u0000" + // 10550 - 10554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7A9" + // 10555 - 10559 + "\u0000\uE7AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10560 - 10564 + "\u0000\uBCF0\u0000\u0000\u0000\u0000\u0000\uE7A8\u0000\u0000" + // 10565 - 10569 + "\u0000\uB9F8\u0000\uE7A7\u0000\u0000\u0000\u0000\u0000\uE7AB" + // 10570 - 10574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4B2\u0000\uCAA2" + // 10575 - 10579 + "\u0000\uC1A3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10580 - 10584 + "\u0000\uC2DC\u0000\uE0C3\u0000\uE0C4\u0000\uE0C2\u0000\u0000" + // 10585 - 10589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10590 - 10594 + "\u0000\uBCED\u0000\u0000\u0000\u0000\u0000\uC6C8\u0000\uB6B9" + // 10595 - 10599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10600 - 10604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0C6" + // 10605 - 10609 + "\u0000\uC3AC\u0000\uE0C5\u0000\u0000\u0000\u0000\u0000\uCFB5" + // 10610 - 10614 + "\u0000\uC7E2\u0000\u0000\u0000\u0000\u0000\uEBDB\u0000\u0000" + // 10615 - 10619 + "\u0000\uEBD9\u0000\u0000\u0000\u0000\u0000\uC3CC\u0000\u0000" + // 10620 - 10624 + "\u0000\u0000\u0000\u0000\u0000\uC0C1\u0000\uB4D2\u0000\uEBDA" + // 10625 - 10629 + "\u0000\u0000\u0000\uBFDB\u0000\u0000\u0000\u0000\u0000\uCECA" + // 10630 - 10634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFC0\u0000\u0000" + // 10635 - 10639 + "\u0000\u0000\u0000\u0000\u0000\uEBDC\u0000\uEBE7\u0000\uC4B5" + // 10640 - 10644 + "\u0000\u0000\u0000\uEBE6\u008F\uDEB0\u0000\uB5BE\u0000\u0000" + // 10645 - 10649 + "\u0000\uE0B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10650 - 10654 + "\u0000\uE0BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10655 - 10659 + "\u0000\uB8A4\u0000\u0000\u0000\u0000\u0000\uC8C8\u0000\u0000" + // 10660 - 10664 + "\u008F\uCAEF\u0000\uE0BC\u0000\u0000\u0000\u0000\u0000\u0000" + // 10665 - 10669 + "\u0000\uBEF5\u0000\u0000\u0000\u0000\u0000\uE0BB\u0000\u0000" + // 10670 - 10674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCAF1\u0000\u0000" + // 10675 - 10679 + "\u0000\uB8EB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10680 - 10684 + "\u0000\uE2EE\u0000\uC4F6\u0000\u0000\u0000\u0000\u0000\u0000" + // 10685 - 10689 + "\u0000\u0000\u0000\uE2F1\u0000\uB3B7\u0000\uE2EC\u0000\u0000" + // 10690 - 10694 + "\u0000\u0000\u0000\uC8EA\u0000\u0000\u0000\uB1B0\u0000\u0000" + // 10695 - 10699 + "\u0000\uBAEC\u0000\u0000\u0000\uCFD2\u0000\u0000\u0000\u0000" + // 10700 - 10704 + "\u0000\uE2F0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10705 - 10709 + "\u0000\u0000\u0000\uE4A8\u0000\u0000\u0000\uE4AA\u0000\u0000" + // 10710 - 10714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4AD\u0000\u0000" + // 10715 - 10719 + "\u0000\uE4AE\u0000\u0000\u0000\uE4AB\u0000\uE4AC\u0000\u0000" + // 10720 - 10724 + "\u0000\u0000\u0000\uE4A9\u0000\uE4A7\u0000\u0000\u0000\u0000" + // 10725 - 10729 + "\u0000\u0000\u0000\u0000\u0000\uE4A1\u0000\u0000\u0000\u0000" + // 10730 - 10734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6A1\u0000\u0000" + // 10735 - 10739 + "\u008F\uD4F2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10740 - 10744 + "\u0000\uE6A2\u0000\uE6A3\u0000\uE6A4\u0000\u0000\u0000\uE6A5" + // 10745 - 10749 + "\u0000\uE6A6\u0000\u0000\u0000\u0000\u0000\uE6A8\u0000\uE6A7" + // 10750 - 10754 + "\u0000\u0000\u0000\u0000\u0000\uE6A9\u0000\u0000\u0000\u0000" + // 10755 - 10759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6AA" + // 10760 - 10764 + "\u0000\uBAD4\u0000\uE0B5\u0000\uE0B4\u0000\u0000\u0000\u0000" + // 10765 - 10769 + "\u0000\u0000\u0000\u0000\u0000\uE0B6\u0000\u0000\u0000\u0000" + // 10770 - 10774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10775 - 10779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0B7\u0000\u0000" + // 10780 - 10784 + "\u0000\u0000\u0000\u0000\u0000\uE0B8\u0000\u0000\u0000\u0000" + // 10785 - 10789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10790 - 10794 + "\u0000\u0000\u0000\u0000\u0000\uBFF3\u0000\u0000\u0000\u0000" + // 10795 - 10799 + "\u0000\uD6CC\u008F\uBBDE\u0000\u0000\u0000\uBAB7\u0000\u0000" + // 10800 - 10804 + "\u0000\u0000\u0000\u0000\u0000\uD6CD\u0000\u0000\u0000\u0000" + // 10805 - 10809 + "\u0000\uD6CE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10810 - 10814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10815 - 10819 + "\u0000\uB4F2\u0000\u0000\u0000\uD5C9\u0000\uD5C8\u0000\u0000" + // 10820 - 10824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10825 - 10829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5CA\u0000\u0000" + // 10830 - 10834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10835 - 10839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2DE\u0000\u0000" + // 10840 - 10844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3E2" + // 10845 - 10849 + "\u0000\u0000\u0000\uBEFC\u0000\uD3DE\u0000\u0000\u0000\uD3DC" + // 10850 - 10854 + "\u0000\u0000\u0000\uD3DD\u0000\u0000\u0000\uD3DF\u0000\u0000" + // 10855 - 10859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10860 - 10864 + "\u0000\u0000\u0000\uE3A1\u0000\uCBE1\u0000\u0000\u0000\u0000" + // 10865 - 10869 + "\u0000\u0000\u0000\uE2FE\u0000\u0000\u0000\u0000\u0000\uB0EB" + // 10870 - 10874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3A4" + // 10875 - 10879 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10880 - 10884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3A3\u0000\u0000" + // 10885 - 10889 + "\u0000\u0000\u0000\uEBD0\u0000\u0000\u0000\uEBD1\u0000\uEBCF" + // 10890 - 10894 + "\u0000\u0000\u0000\uB8D8\u0000\u0000\u0000\uCDC0\u0000\u0000" + // 10895 - 10899 + "\u0000\u0000\u0000\uBBEF\u0000\uC7A7\u0000\u0000\u0000\u0000" + // 10900 - 10904 + "\u0000\u0000\u0000\uEBD4\u0000\u0000\u0000\uC0C0\u0000\u0000" + // 10905 - 10909 + "\u0000\uC3C2\u0000\u0000\u0000\u0000\u0000\uCDB6\u0000\u0000" + // 10910 - 10914 + "\u0000\uEBD7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8EC" + // 10915 - 10919 + "\u0000\u0000\u0000\uBFE7\u0000\u0000\u0000\uC6C4\u0000\u0000" + // 10920 - 10924 + "\u0000\uE2CE\u0000\uCBD3\u0000\u0000\u0000\uE2CB\u0000\u0000" + // 10925 - 10929 + "\u0000\u0000\u0000\uE2CC\u0000\u0000\u0000\u0000\u0000\u0000" + // 10930 - 10934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10935 - 10939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10940 - 10944 + "\u0000\uE2D1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10945 - 10949 + "\u0000\uE2D0\u0000\uE2CF\u0000\uE0AE\u0000\u0000\u0000\u0000" + // 10950 - 10954 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0AF\u0000\uCAD2" + // 10955 - 10959 + "\u0000\uC8C7\u0000\u0000\u0000\u0000\u0000\uE0B0\u0000\uC7D7" + // 10960 - 10964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10965 - 10969 + "\u0000\uC4AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10970 - 10974 + "\u0000\u0000\u0000\uE0B1\u0000\uB2E7\u0000\u0000\u0000\uB5ED" + // 10975 - 10979 + "\u0000\u0000\u0000\uCCC6\u0000\u0000\u0000\uCCB6\u0000\uDFFA" + // 10980 - 10984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1E7" + // 10985 - 10989 + "\u0000\uBBB8\u0000\uDFFC\u0000\u0000\u0000\u0000\u0000\u0000" + // 10990 - 10994 + "\u0000\u0000\u0000\uDFFB\u0000\uBFA4\u0000\uD2D9\u0000\u0000" + // 10995 - 10999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11000 - 11004 + "\u0000\uDFFD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0A1" + // 11005 - 11009 + "\u0000\u0000\u0000\uDFEE\u0000\uDFFE\u0000\u0000\u008F\uCABD" + // 11010 - 11014 + "\u0000\uE0A2\u0000\uB9EA\u0000\uC7A8\u0000\u0000\u0000\u0000" + // 11015 - 11019 + "\u0000\uDEB9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11020 - 11024 + "\u0000\u0000\u0000\u0000\u0000\uCDF4\u0000\uDFBD\u0000\u0000" + // 11025 - 11029 + "\u0000\uDFC1\u0000\uC2F5\u0000\u0000\u0000\uDFC0\u0000\u0000" + // 11030 - 11034 + "\u0000\uDFAB\u0000\u0000\u008F\uC9A6\u0000\uEFE9\u0000\u0000" + // 11035 - 11039 + "\u0000\u0000\u0000\u0000\u0000\uDFC5\u0000\u0000\u0000\u0000" + // 11040 - 11044 + "\u0000\u0000\u0000\uDFC9\u0000\u0000\u0000\uE1F7\u0000\uE1F8" + // 11045 - 11049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1FC" + // 11050 - 11054 + "\u0000\uE1F9\u0000\uE1FA\u0000\uE1FB\u0000\u0000\u0000\uE1FD" + // 11055 - 11059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1FE\u0000\u0000" + // 11060 - 11064 + "\u0000\uE2A1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2A2" + // 11065 - 11069 + "\u0000\u0000\u0000\uE2A3\u0000\u0000\u0000\uC8AF\u0000\uC5D0" + // 11070 - 11074 + "\u0000\uE2A4\u0000\uC7F2\u0000\uC9B4\u0000\u0000\u0000\uE2B8" + // 11075 - 11079 + "\u0000\u0000\u0000\uB4C6\u0000\uC8D7\u0000\uE2B9\u0000\u0000" + // 11080 - 11084 + "\u0000\uE2BA\u0000\u0000\u0000\u0000\u0000\uE2BB\u0000\u0000" + // 11085 - 11089 + "\u0000\u0000\u0000\u0000\u0000\uCCDC\u0000\u0000\u0000\u0000" + // 11090 - 11094 + "\u0000\u0000\u0000\uCCD5\u0000\u0000\u0000\uC4BE\u0000\u0000" + // 11095 - 11099 + "\u0000\u0000\u0000\u0000\u0000\uC1EA\u0000\u0000\u0000\u0000" + // 11100 - 11104 + "\u0000\uE2BD\u0000\u0000\u0000\u0000\u0000\uBDE2\u0000\u0000" + // 11105 - 11109 + "\u0000\uBECA\u0000\u0000\u0000\u0000\u0000\uE2C0\u0000\u0000" + // 11110 - 11114 + "\u0000\u0000\u0000\uE2BF\u0000\uE2BE\u0000\uC8FD\u0000\u0000" + // 11115 - 11119 + "\u0000\uB4C7\u0000\uB8A9\u0000\u0000\u0000\u0000\u0000\u0000" + // 11120 - 11124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11125 - 11129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11130 - 11134 + "\u0000\u0000\u0000\uE2C6\u0000\u0000\u0000\u0000\u0000\uE2C3" + // 11135 - 11139 + "\u0000\uBFBF\u0000\uB7E3\u0000\uC2F9\u0000\uDFB2\u0000\uC7BB" + // 11140 - 11144 + "\u0000\u0000\u0000\u0000\u0000\uDFB9\u0000\u0000\u0000\u0000" + // 11145 - 11149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11150 - 11154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11155 - 11159 + "\u0000\u0000\u0000\uDFBE\u0000\uDFBC\u0000\u0000\u0000\u0000" + // 11160 - 11164 + "\u0000\uDFBF\u0000\u0000\u0000\u0000\u0000\uDFC2\u0000\u0000" + // 11165 - 11169 + "\u0000\u0000\u0000\u0000\u0000\uDFBB\u0000\uDFA8\u0000\uDFA7" + // 11170 - 11174 + "\u0000\uDFAD\u0000\u0000\u0000\uC0A1\u0000\u0000\u0000\uDFA4" + // 11175 - 11179 + "\u0000\u0000\u008F\uC8E5\u0000\u0000\u0000\u0000\u0000\u0000" + // 11180 - 11184 + "\u0000\u0000\u0000\u0000\u0000\uDFB0\u0000\u0000\u0000\u0000" + // 11185 - 11189 + "\u0000\uDFB1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11190 - 11194 + "\u0000\u0000\u0000\uB4C2\u0000\u0000\u0000\u0000\u0000\u0000" + // 11195 - 11199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11200 - 11204 + "\u0000\uBCFE\u0000\u0000\u0000\uBCF6\u0000\u0000\u0000\u0000" + // 11205 - 11209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2EF\u0000\uD2ED" + // 11210 - 11214 + "\u0000\u0000\u0000\uCCA3\u0000\u0000\u0000\uD2EA\u0000\uD2F3" + // 11215 - 11219 + "\u0000\uD2EE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2F1" + // 11220 - 11224 + "\u0000\uB8C6\u0000\uCCBF\u0000\u0000\u0000\u0000\u0000\uEAE8" + // 11225 - 11229 + "\u0000\u0000\u0000\uEAED\u0000\u0000\u0000\u0000\u0000\uCAA3" + // 11230 - 11234 + "\u0000\u0000\u0000\u0000\u0000\uEAEF\u0000\u0000\u0000\uEAEE" + // 11235 - 11239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3EC\u0000\u0000" + // 11240 - 11244 + "\u0000\uCBAB\u0000\uEAF0\u0000\u0000\u0000\u0000\u0000\u0000" + // 11245 - 11249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11250 - 11254 + "\u008F\uDCDF\u0000\uEAFC\u0000\uEAF2\u0000\u0000\u0000\uE1E1" + // 11255 - 11259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11260 - 11264 + "\u0000\u0000\u0000\u0000\u0000\uE1E8\u0000\u0000\u0000\uE1E6" + // 11265 - 11269 + "\u0000\u0000\u0000\uE1E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 11270 - 11274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11275 - 11279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11280 - 11284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1E9" + // 11285 - 11289 + "\u0000\uC7F9\u0000\u0000\u0000\uB4C1\u0000\uCEFA\u0000\u0000" + // 11290 - 11294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11295 - 11299 + "\u0000\u0000\u0000\uCCA1\u0000\uC4D2\u0000\u0000\u0000\u0000" + // 11300 - 11304 + "\u0000\u0000\u0000\u0000\u0000\uDEFB\u0000\uDEFD\u0000\u0000" + // 11305 - 11309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1B2" + // 11310 - 11314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11315 - 11319 + "\u0000\uDFA1\u0000\uDEF9\u0000\uCBFE\u0000\u0000\u0000\uDEE3" + // 11320 - 11324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11325 - 11329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8AE" + // 11330 - 11334 + "\u0000\u0000\u0000\u0000\u0000\uDEEF\u0000\uB8BB\u0000\u0000" + // 11335 - 11339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDE0" + // 11340 - 11344 + "\u0000\u0000\u0000\uDEE5\u0000\u0000\u0000\u0000\u0000\u0000" + // 11345 - 11349 + "\u0000\uCEAF\u0000\uB9C2\u0000\u0000\u0000\uDEF2\u0000\uB5F4" + // 11350 - 11354 + "\u0000\uC5CF\u0000\u0000\u0000\uDED6\u0000\uDEDF\u0000\uB0AF" + // 11355 - 11359 + "\u0000\uB1B2\u008F\uC7EB\u0000\u0000\u0000\uB2B9\u0000\u0000" + // 11360 - 11364 + "\u0000\uDED8\u0000\uC2AC\u0000\uDECF\u0000\uDED1\u0000\uB9C1" + // 11365 - 11369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11370 - 11374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEE2\u008F\uC7EE" + // 11375 - 11379 + "\u0000\uDEDD\u0000\u0000\u008F\uC7F0\u0000\u0000\u0000\uDED5" + // 11380 - 11384 + "\u0000\u0000\u008F\uF4C6\u0000\u0000\u0000\uE1AC\u0000\uE1AB" + // 11385 - 11389 + "\u0000\uE1AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11390 - 11394 + "\u0000\u0000\u0000\u0000\u0000\uE1AE\u0000\uE1B0\u0000\uE1AF" + // 11395 - 11399 + "\u0000\u0000\u0000\u0000\u0000\uB9F9\u0000\u0000\u0000\uE1B2" + // 11400 - 11404 + "\u0000\u0000\u0000\uE1B1\u0000\u0000\u0000\u0000\u0000\uB4C5" + // 11405 - 11409 + "\u0000\u0000\u0000\uBFD3\u0000\u0000\u0000\uC5BC\u0000\u0000" + // 11410 - 11414 + "\u0000\uE1B3\u0000\uC0B8\u0000\uCDE4\u0000\u0000\u0000\u0000" + // 11415 - 11419 + "\u0000\u0000\u0000\u0000\u0000\uDEC8\u0000\uDEC2\u0000\uDEBF" + // 11420 - 11424 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCED4\u0000\uDEC5" + // 11425 - 11429 + "\u0000\u0000\u0000\u0000\u008F\uC7D9\u0000\u0000\u0000\uBDCA" + // 11430 - 11434 + "\u0000\uDEC7\u0000\u0000\u0000\u0000\u0000\uDECC\u0000\u0000" + // 11435 - 11439 + "\u0000\u0000\u0000\uC5F1\u0000\uDECA\u0000\u0000\u0000\u0000" + // 11440 - 11444 + "\u0000\u0000\u0000\u0000\u0000\uDEC4\u0000\u0000\u0000\uB2B4" + // 11445 - 11449 + "\u0000\uCFB4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11450 - 11454 + "\u0000\uCBD2\u0000\u0000\u0000\uCAAA\u0000\u0000\u0000\u0000" + // 11455 - 11459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11460 - 11464 + "\u0000\u0000\u0000\uC0B7\u0000\u0000\u0000\uE0B2\u0000\u0000" + // 11465 - 11469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6C3\u0000\u0000" + // 11470 - 11474 + "\u0000\u0000\u0000\u0000\u0000\uB8A3\u0000\uE0B3\u0000\u0000" + // 11475 - 11479 + "\u0000\uE0F0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11480 - 11484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11485 - 11489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11490 - 11494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11495 - 11499 + "\u0000\uE0EC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0EF" + // 11500 - 11504 + "\u0000\uB8EA\u0000\uB1CD\u0000\uE0F1\u0000\u0000\u0000\uBFF0" + // 11505 - 11509 + "\u0000\uE0EE\u0000\uC3D3\u0000\u0000\u0000\uDDE9\u0000\u0000" + // 11510 - 11514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDF1" + // 11515 - 11519 + "\u0000\u0000\u0000\uDDEA\u0000\u0000\u0000\u0000\u0000\u0000" + // 11520 - 11524 + "\u0000\u0000\u008F\uC6E3\u0000\uC2C1\u0000\u0000\u0000\uB5E2" + // 11525 - 11529 + "\u0000\uDDF2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11530 - 11534 + "\u0000\u0000\u0000\u0000\u0000\uB7E8\u0000\u0000\u0000\u0000" + // 11535 - 11539 + "\u0000\uB5A5\u0000\uDDF0\u0000\u0000\u008F\uCAB3\u0000\u0000" + // 11540 - 11544 + "\u0000\uC7B3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11545 - 11549 + "\u0000\uC5F5\u0000\uDFF7\u0000\u0000\u0000\u0000\u0000\u0000" + // 11550 - 11554 + "\u0000\u0000\u0000\uDFF9\u0000\u0000\u0000\uCED5\u0000\u0000" + // 11555 - 11559 + "\u0000\uDFF6\u0000\u0000\u0000\uDFF8\u0000\uB1ED\u0000\u0000" + // 11560 - 11564 + "\u0000\uDFF3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11565 - 11569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3DB\u0000\uC4F5" + // 11570 - 11574 + "\u0000\uBDC1\u0000\uB5E1\u0000\u0000\u0000\u0000\u0000\u0000" + // 11575 - 11579 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11580 - 11584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8C6\u0000\u0000" + // 11585 - 11589 + "\u0000\uBCAE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11590 - 11594 + "\u0000\uDDE8\u0000\u0000\u0000\uB4C0\u0000\u0000\u0000\u0000" + // 11595 - 11599 + "\u0000\uB1F8\u0000\u0000\u008F\uC6E0\u0000\uC6F2\u0000\uDDE7" + // 11600 - 11604 + "\u0000\uB9BE\u0000\uD4CC\u0000\u0000\u0000\u0000\u0000\u0000" + // 11605 - 11609 + "\u0000\u0000\u0000\uB5A3\u0000\uDDD8\u0000\u0000\u0000\u0000" + // 11610 - 11614 + "\u0000\u0000\u0000\u0000\u0000\uDDD9\u0000\u0000\u0000\uCAEC" + // 11615 - 11619 + "\u0000\uCBE8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6C7" + // 11620 - 11624 + "\u0000\uDDDA\u0000\uC8E6\u0000\u0000\u008F\uC6B8\u0000\u0000" + // 11625 - 11629 + "\u0000\uC8FB\u0000\u0000\u0000\u0000\u0000\uCCD3\u0000\u0000" + // 11630 - 11634 + "\u0000\u0000\u0000\u0000\u0000\uDDDB\u0000\uDDC8\u0000\u0000" + // 11635 - 11639 + "\u0000\u0000\u0000\uDDCA\u0000\uDDC9\u0000\u0000\u0000\uCBD8" + // 11640 - 11644 + "\u0000\u0000\u0000\u0000\u0000\uBDDE\u0000\uBCEC\u0000\uBBC4" + // 11645 - 11649 + "\u0000\u0000\u0000\uDDCB\u0000\u0000\u0000\u0000\u0000\u0000" + // 11650 - 11654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDCD" + // 11655 - 11659 + "\u0000\uBFA3\u0000\u0000\u0000\uDDCC\u0000\u0000\u0000\u0000" + // 11660 - 11664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDCE\u0000\u0000" + // 11665 - 11669 + "\u0000\uCCB5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11670 - 11674 + "\u0000\uBEC7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11675 - 11679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11680 - 11684 + "\u0000\uB1EB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11685 - 11689 + "\u0000\u0000\u0000\uC1B3\u0000\u0000\u0000\u0000\u0000\u0000" + // 11690 - 11694 + "\u0000\u0000\u0000\u0000\u0000\uBEC6\u0000\u0000\u0000\u0000" + // 11695 - 11699 + "\u0000\u0000\u0000\uEDE5\u0000\u0000\u0000\u0000\u0000\u0000" + // 11700 - 11704 + "\u0000\uD2A1\u0000\uD1FE\u0000\u0000\u0000\u0000\u0000\u0000" + // 11705 - 11709 + "\u0000\u0000\u0000\uEDE6\u0000\uE5F0\u0000\uEDE7\u0000\uC3A4" + // 11710 - 11714 + "\u0000\uBFAB\u0000\uC7C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 11715 - 11719 + "\u0000\u0000\u0000\uEDE8\u0000\u0000\u0000\u0000\u0000\uCAD5" + // 11720 - 11724 + "\u0000\uC4D4\u0000\uB9FE\u0000\u0000\u0000\u0000\u0000\uC3A9" + // 11725 - 11729 + "\u0000\uB7E7\u0000\uBCA1\u0000\u0000\u0000\uB6D5\u0000\u0000" + // 11730 - 11734 + "\u0000\u0000\u0000\u0000\u0000\uB2A4\u0000\u0000\u0000\u0000" + // 11735 - 11739 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11740 - 11744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDDF\u0000\u0000" + // 11745 - 11749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDB8\u0000\uDDB7" + // 11750 - 11754 + "\u0000\uDDBA\u0000\uB5BD\u0000\u0000\u0000\u0000\u0000\uB6D6" + // 11755 - 11759 + "\u0000\uB4BE\u0000\u0000\u0000\uDFC7\u0000\u0000\u0000\u0000" + // 11760 - 11764 + "\u0000\u0000\u008F\uC9AB\u0000\uC6C2\u008F\uC9AD\u0000\u0000" + // 11765 - 11769 + "\u0000\uDFC3\u0000\u0000\u0000\uDFC4\u0000\u0000\u0000\u0000" + // 11770 - 11774 + "\u0000\u0000\u0000\uDFC8\u0000\u0000\u0000\uDFC6\u0000\u0000" + // 11775 - 11779 + "\u0000\u0000\u0000\u0000\u0000\uC9CE\u0000\u0000\u0000\u0000" + // 11780 - 11784 + "\u0000\uDFCE\u0000\u0000\u0000\uDFCB\u0000\uDFCA\u0000\u0000" + // 11785 - 11789 + "\u0000\uDFCD\u0000\uC6D4\u0000\uDFCF\u0000\uDCFB\u0000\u0000" + // 11790 - 11794 + "\u0000\uDCFD\u0000\uDCFE\u0000\u0000\u0000\u0000\u0000\u0000" + // 11795 - 11799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDAC\u0000\u0000" + // 11800 - 11804 + "\u0000\uDDA8\u0000\u0000\u0000\uDBED\u0000\u0000\u0000\u0000" + // 11805 - 11809 + "\u0000\u0000\u0000\u0000\u0000\uDDA7\u0000\u0000\u0000\u0000" + // 11810 - 11814 + "\u0000\u0000\u0000\u0000\u0000\uDDA6\u0000\u0000\u0000\u0000" + // 11815 - 11819 + "\u0000\uDDA3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11820 - 11824 + "\u0000\uB9F6\u0000\uBBE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 11825 - 11829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11830 - 11834 + "\u0000\uE0D2\u0000\uE0D3\u0000\u0000\u0000\u0000\u0000\u0000" + // 11835 - 11839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0D5" + // 11840 - 11844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11845 - 11849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBCF\u0000\u0000" + // 11850 - 11854 + "\u0000\u0000\u0000\uD4D3\u0000\u0000\u0000\u0000\u0000\uD4D8" + // 11855 - 11859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uB8CE\u0000\uCAAF" + // 11860 - 11864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4D7" + // 11865 - 11869 + "\u0000\uD4D1\u0000\uD4D4\u0000\uD4D6\u0000\u0000\u0000\u0000" + // 11870 - 11874 + "\u0000\uBAA6\u0000\u0000\u0000\uDFB6\u0000\u0000\u0000\uDFB5" + // 11875 - 11879 + "\u0000\uDFB7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11880 - 11884 + "\u0000\u0000\u0000\uDFBA\u0000\u0000\u0000\u0000\u0000\u0000" + // 11885 - 11889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5C3\u0000\u0000" + // 11890 - 11894 + "\u0000\uDFB4\u0000\u0000\u008F\uC8F8\u0000\u0000\u0000\u0000" + // 11895 - 11899 + "\u0000\u0000\u0000\uDFB8\u0000\u0000\u0000\u0000\u0000\u0000" + // 11900 - 11904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCEC3\u0000\u0000" + // 11905 - 11909 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11910 - 11914 + "\u0000\u0000\u0000\uC8E9\u0000\u0000\u0000\uE2AE\u0000\u0000" + // 11915 - 11919 + "\u0000\u0000\u0000\u0000\u0000\uE2AF\u0000\u0000\u0000\u0000" + // 11920 - 11924 + "\u0000\uF3E9\u0000\uE2B0\u0000\uE2B1\u0000\uE2B2\u0000\u0000" + // 11925 - 11929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBAE\u0000\uC3C9" + // 11930 - 11934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCFC\u0000\u0000" + // 11935 - 11939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11940 - 11944 + "\u0000\u0000\u0000\u0000\u0000\uDCFA\u0000\uB8E9\u0000\u0000" + // 11945 - 11949 + "\u0000\uDCF9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11950 - 11954 + "\u0000\u0000\u0000\u0000\u0000\uDDA1\u0000\u0000\u0000\u0000" + // 11955 - 11959 + "\u0000\u0000\u0000\u0000\u0000\uDBD8\u0000\u0000\u0000\u0000" + // 11960 - 11964 + "\u0000\u0000\u0000\uE8FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 11965 - 11969 + "\u0000\u0000\u0000\uCFCF\u0000\uC6A2\u0000\uC9F3\u008F\uD9D1" + // 11970 - 11974 + "\u0000\u0000\u0000\uE9AB\u0000\u0000\u0000\u0000\u0000\u0000" + // 11975 - 11979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11980 - 11984 + "\u0000\u0000\u0000\uE9B1\u0000\u0000\u0000\u0000\u0000\u0000" + // 11985 - 11989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9B2\u0000\u0000" + // 11990 - 11994 + "\u0000\uB5F9\u0000\uC9BA\u0000\u0000\u0000\u0000\u0000\u0000" + // 11995 - 11999 + "\u0000\uBCBF\u0000\u0000\u0000\u0000\u0000\uB9F7\u0000\u0000" + // 12000 - 12004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFB3" + // 12005 - 12009 + "\u0000\u0000\u0000\uDEF4\u0000\u0000\u0000\uDFA2\u0000\uB1E9" + // 12010 - 12014 + "\u0000\uC1E6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12015 - 12019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2CD" + // 12020 - 12024 + "\u0000\u0000\u0000\u0000\u0000\uF1DA\u0000\u0000\u0000\u0000" + // 12025 - 12029 + "\u0000\u0000\u0000\u0000\u0000\uC6AD\u0000\u0000\u0000\u0000" + // 12030 - 12034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1DB" + // 12035 - 12039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12040 - 12044 + "\u0000\u0000\u0000\uF1E0\u0000\u0000\u0000\uDEF3\u0000\u0000" + // 12045 - 12049 + "\u0000\u0000\u0000\u0000\u0000\uB4C3\u0000\u0000\u0000\u0000" + // 12050 - 12054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12055 - 12059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8AE" + // 12060 - 12064 + "\u0000\u0000\u0000\u0000\u0000\uB7E9\u0000\u0000\u0000\u0000" + // 12065 - 12069 + "\u0000\u0000\u0000\uDFAF\u0000\u0000\u0000\u0000\u0000\uDFAA" + // 12070 - 12074 + "\u0000\uC0F8\u0000\u0000\u0000\u0000\u0000\uB3E3\u0000\uDCD5" + // 12075 - 12079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12080 - 12084 + "\u0000\u0000\u0000\uDCD2\u0000\u0000\u0000\u0000\u0000\u0000" + // 12085 - 12089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCC6\u0000\u0000" + // 12090 - 12094 + "\u0000\u0000\u0000\uDCE3\u0000\uDCC5\u0000\u0000\u0000\uDCD8" + // 12095 - 12099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12100 - 12104 + "\u0000\u0000\u0000\uDCD0\u0000\u0000\u0000\u0000\u0000\uDCCB" + // 12105 - 12109 + "\u0000\uDCC8\u0000\uC6EF\u0000\uDCC0\u0000\uC6EA\u0000\u0000" + // 12110 - 12114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC4CC" + // 12115 - 12119 + "\u0000\u0000\u0000\uDCC4\u0000\uDCB7\u0000\u0000\u0000\uB6C8" + // 12120 - 12124 + "\u0000\uDCBA\u0000\uBDDD\u0000\u0000\u0000\u0000\u0000\u0000" + // 12125 - 12129 + "\u0000\uC7E0\u0000\uDCBC\u0000\uB6CB\u0000\u0000\u0000\uDCB4" + // 12130 - 12134 + "\u0000\uDCB6\u0000\uDCB3\u0000\u0000\u0000\u0000\u0000\uCFB0" + // 12135 - 12139 + "\u0000\uB3DA\u0000\uDCB9\u0000\u0000\u0000\uCEAE\u0000\u0000" + // 12140 - 12144 + "\u0000\u0000\u0000\uBEF4\u0000\uC0F5\u0000\u0000\u0000\u0000" + // 12145 - 12149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12150 - 12154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12155 - 12159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12160 - 12164 + "\u0000\u0000\u0000\u0000\u0000\uDEB6\u0000\uDEB4\u0000\u0000" + // 12165 - 12169 + "\u0000\uC9CD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7ED" + // 12170 - 12174 + "\u0000\u0000\u0000\uEBC4\u0000\u0000\u0000\u0000\u0000\u0000" + // 12175 - 12179 + "\u0000\u0000\u0000\uCBAC\u0000\u0000\u0000\u0000\u0000\uC0DF" + // 12180 - 12184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5F6\u0000\u0000" + // 12185 - 12189 + "\u0000\uCCF5\u0000\uC1CA\u0000\u0000\u0000\uEBC5\u008F\uDDD4" + // 12190 - 12194 + "\u0000\u0000\u0000\u0000\u0000\uBFC7\u0000\uC3F0\u0000\uBEDA" + // 12195 - 12199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCF1\u0000\u0000" + // 12200 - 12204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFF6\u0000\u0000" + // 12205 - 12209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12210 - 12214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2AD" + // 12215 - 12219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12220 - 12224 + "\u0000\u0000\u0000\uECE7\u0000\u0000\u0000\u0000\u0000\u0000" + // 12225 - 12229 + "\u0000\uECE6\u0000\u0000\u0000\uC3B8\u0000\u0000\u0000\u0000" + // 12230 - 12234 + "\u0000\uDECB\u0000\u0000\u0000\uDEC0\u0000\u0000\u0000\uDEC6" + // 12235 - 12239 + "\u0000\u0000\u0000\uDECD\u0000\uB0FC\u0000\uDEC3\u0000\u0000" + // 12240 - 12244 + "\u0000\uDECE\u0000\u0000\u0000\u0000\u0000\uBFBC\u008F\uF4C0" + // 12245 - 12249 + "\u0000\uBDDF\u0000\u0000\u0000\uCAA5\u0000\u0000\u0000\uBAAE" + // 12250 - 12254 + "\u008F\uF4BF\u0000\uDEBB\u0000\uDEC9\u0000\uC5BA\u008F\uC7E6" + // 12255 - 12259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3D3\u0000\u0000" + // 12260 - 12264 + "\u0000\uEDCA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBADC" + // 12265 - 12269 + "\u0000\uEDC9\u0000\u0000\u0000\uEDD2\u0000\u0000\u0000\u0000" + // 12270 - 12274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDCC\u0000\uEDCE" + // 12275 - 12279 + "\u0000\uCAE5\u0000\uEDCB\u0000\u0000\u0000\u0000\u0000\u0000" + // 12280 - 12284 + "\u0000\uEDCD\u0000\u0000\u0000\uEDD1\u0000\uEDCF\u0000\uB5B1" + // 12285 - 12289 + "\u0000\u0000\u0000\uEDD0\u0000\uCFD0\u0000\uDBF6\u0000\u0000" + // 12290 - 12294 + "\u0000\u0000\u0000\uDCA6\u0000\uB0D8\u0000\u0000\u0000\u0000" + // 12295 - 12299 + "\u0000\uDBF8\u0000\u0000\u0000\u0000\u0000\uCCBA\u0000\uDBFD" + // 12300 - 12304 + "\u0000\uBFA2\u0000\uC4C7\u0000\uDBF3\u0000\u0000\u0000\u0000" + // 12305 - 12309 + "\u0000\uDCA5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12310 - 12314 + "\u0000\u0000\u0000\u0000\u0000\uBFFA\u0000\uDCAF\u0000\uB3F1" + // 12315 - 12319 + "\u0000\uB8A1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8B4" + // 12320 - 12324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0F1" + // 12325 - 12329 + "\u0000\u0000\u0000\u0000\u0000\uE8AB\u0000\u0000\u0000\u0000" + // 12330 - 12334 + "\u0000\u0000\u0000\uE8AA\u0000\u0000\u0000\uE8A5\u0000\uE8A4" + // 12335 - 12339 + "\u0000\u0000\u0000\uE8A2\u0000\uE8A1\u0000\uC3E3\u0000\u0000" + // 12340 - 12344 + "\u0000\uC2FB\u0000\uE8A7\u0000\u0000\u0000\u0000\u0000\u0000" + // 12345 - 12349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8DE\u0000\u0000" + // 12350 - 12354 + "\u008F\uD8D1\u0000\uCDD5\u0000\u0000\u0000\u0000\u0000\u0000" + // 12355 - 12359 + "\u0000\u0000\u0000\uCEAA\u0000\u0000\u0000\u0000\u0000\u0000" + // 12360 - 12364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12365 - 12369 + "\u0000\uC3F8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3EB" + // 12370 - 12374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3AE" + // 12375 - 12379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3AF" + // 12380 - 12384 + "\u0000\u0000\u0000\uF3AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 12385 - 12389 + "\u0000\uF2F4\u0000\u0000\u0000\u0000\u0000\uF3B0\u0000\u0000" + // 12390 - 12394 + "\u0000\uC4E1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3B4" + // 12395 - 12399 + "\u0000\u0000\u0000\uF3B5\u0000\uF3B3\u0000\u0000\u0000\u0000" + // 12400 - 12404 + "\u0000\u0000\u0000\u0000\u0000\uB2AB\u0000\u0000\u0000\u0000" + // 12405 - 12409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12410 - 12414 + "\u0000\uF3D4\u0000\uB5D0\u0000\uF3D5\u0000\uF3D6\u0000\uF3D7" + // 12415 - 12419 + "\u008F\uF4F8\u0000\uB9F5\u0000\u0000\u0000\uF3D8\u0000\u0000" + // 12420 - 12424 + "\u0000\u0000\u0000\u0000\u0000\uE0D4\u0000\uCCDB\u0000\u0000" + // 12425 - 12429 + "\u0000\uC2E3\u0000\uF3D9\u0000\uF3DB\u0000\uF3DA\u0000\u0000" + // 12430 - 12434 + "\u0000\uCBA2\u0000\uC7C8\u0000\uB5E3\u0000\u0000\u0000\uC5A5" + // 12435 - 12439 + "\u0000\u0000\u0000\u0000\u0000\uC3ED\u0000\u0000\u0000\uDEA5" + // 12440 - 12444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEA3" + // 12445 - 12449 + "\u0000\uC2D9\u0000\uDDF6\u0000\u0000\u0000\uB1CB\u0000\u0000" + // 12450 - 12454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12455 - 12459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12460 - 12464 + "\u0000\u0000\u0000\uDEF5\u0000\u0000\u0000\u0000\u0000\u0000" + // 12465 - 12469 + "\u0000\u0000\u0000\uDFD3\u0000\u0000\u0000\u0000\u0000\u0000" + // 12470 - 12474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6E7\u0000\u0000" + // 12475 - 12479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12480 - 12484 + "\u0000\u0000\u0000\uE2DE\u0000\u0000\u0000\u0000\u0000\u0000" + // 12485 - 12489 + "\u0000\u0000\u0000\u0000\u0000\uE2DF\u0000\u0000\u0000\u0000" + // 12490 - 12494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12495 - 12499 + "\u0000\u0000\u0000\u0000\u0000\uE2E0\u0000\u0000\u0000\u0000" + // 12500 - 12504 + "\u0000\uE2E1\u0000\uCCB7\u0000\uE2E2\u0000\u0000\u0000\u0000" + // 12505 - 12509 + "\u0000\u0000\u0000\uE9CF\u0000\uC7C2\u0000\u0000\u0000\u0000" + // 12510 - 12514 + "\u0000\u0000\u0000\u0000\u0000\uE9D0\u0000\uE9D1\u0000\uE9DB" + // 12515 - 12519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9D5\u0000\uE9D8" + // 12520 - 12524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12525 - 12529 + "\u0000\uE9D4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12530 - 12534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12535 - 12539 + "\u0000\uC2AA\u0000\u0000\u0000\u0000\u0000\uBBAB\u0000\uD9D2" + // 12540 - 12544 + "\u0000\u0000\u0000\uD9D4\u0000\uD9D0\u0000\u0000\u0000\u0000" + // 12545 - 12549 + "\u0000\u0000\u0000\u0000\u0000\uCAE1\u0000\u0000\u0000\uC4BD" + // 12550 - 12554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1DC" + // 12555 - 12559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2F2\u0000\u0000" + // 12560 - 12564 + "\u0000\u0000\u0000\uBFA8\u0000\uEBBB\u0000\u0000\u0000\u0000" + // 12565 - 12569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12570 - 12574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12575 - 12579 + "\u0000\uEBBC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBBD" + // 12580 - 12584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12585 - 12589 + "\u0000\u0000\u0000\u0000\u008F\uE4A6\u0000\u0000\u0000\u0000" + // 12590 - 12594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1AC\u0000\u0000" + // 12595 - 12599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12600 - 12604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEF9\u0000\u0000" + // 12605 - 12609 + "\u0000\uEEF8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12610 - 12614 + "\u0000\u0000\u0000\u0000\u0000\uB7D9\u0000\u0000\u0000\u0000" + // 12615 - 12619 + "\u0000\u0000\u0000\u0000\u0000\uEBFE\u0000\uECA2\u0000\u0000" + // 12620 - 12624 + "\u0000\u0000\u0000\uECA3\u0000\uB5C4\u0000\uE6C1\u0000\uBEF9" + // 12625 - 12629 + "\u0000\u0000\u0000\uECA4\u0000\u0000\u0000\u0000\u0000\uB8EE" + // 12630 - 12634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12635 - 12639 + "\u0000\uECA5\u0000\u0000\u008F\uF4DF\u0000\uDCAB\u0000\uDBFC" + // 12640 - 12644 + "\u0000\u0000\u0000\uDCA8\u0000\u0000\u0000\u0000\u0000\u0000" + // 12645 - 12649 + "\u0000\uDCA2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12650 - 12654 + "\u0000\u0000\u0000\u0000\u0000\uBFB9\u0000\uDCAC\u0000\u0000" + // 12655 - 12659 + "\u0000\u0000\u0000\uC0B3\u0000\u0000\u0000\u0000\u0000\u0000" + // 12660 - 12664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCAA\u0000\uB4BD" + // 12665 - 12669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12670 - 12674 + "\u0000\uE4A6\u0000\uC9AE\u0000\u0000\u0000\uC8A6\u0000\uC5F9" + // 12675 - 12679 + "\u0000\u0000\u0000\uB6DA\u0000\uE4A5\u0000\uE4A3\u0000\u0000" + // 12680 - 12684 + "\u0000\uC8B5\u0000\uE3FE\u0000\uC3DE\u0000\uC5FB\u0000\u0000" + // 12685 - 12689 + "\u0000\uC5FA\u0000\u0000\u0000\uBAF6\u0000\u0000\u0000\u0000" + // 12690 - 12694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4B8" + // 12695 - 12699 + "\u0000\u0000\u0000\u0000\u0000\uE9A2\u0000\u0000\u0000\u0000" + // 12700 - 12704 + "\u0000\u0000\u0000\uE9C3\u0000\uC1C9\u0000\u0000\u0000\u0000" + // 12705 - 12709 + "\u0000\uE9BE\u0000\uE9C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 12710 - 12714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9BF" + // 12715 - 12719 + "\u0000\u0000\u0000\u0000\u0000\uDDB1\u0000\uDDA2\u0000\u0000" + // 12720 - 12724 + "\u0000\u0000\u0000\uE9C5\u0000\u0000\u0000\u0000\u0000\u0000" + // 12725 - 12729 + "\u0000\u0000\u0000\u0000\u0000\uEAA4\u0000\u0000\u008F\uF4DC" + // 12730 - 12734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12735 - 12739 + "\u0000\uEAB8\u0000\uEABC\u0000\uEAB7\u0000\u0000\u0000\uEABE" + // 12740 - 12744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAC0\u0000\uEABF" + // 12745 - 12749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12750 - 12754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFB9\u0000\uDBF1" + // 12755 - 12759 + "\u0000\u0000\u0000\uBEBF\u0000\u0000\u0000\u0000\u0000\u0000" + // 12760 - 12764 + "\u0000\uD4ED\u0000\uB8E8\u0000\uCDFC\u0000\u0000\u0000\u0000" + // 12765 - 12769 + "\u0000\u0000\u0000\u0000\u0000\uDBE8\u0000\u0000\u0000\uC4F4" + // 12770 - 12774 + "\u0000\uB3A3\u0000\uBAAD\u0000\u0000\u0000\uDBE0\u0000\u0000" + // 12775 - 12779 + "\u0000\uDBF0\u0000\uB3E1\u0000\u0000\u0000\u0000\u0000\uDBEE" + // 12780 - 12784 + "\u0000\uDBF2\u0000\u0000\u0000\uC5EE\u0000\u0000\u0000\u0000" + // 12785 - 12789 + "\u0000\u0000\u0000\uE6FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 12790 - 12794 + "\u0000\u0000\u0000\u0000\u0000\uE6FB\u0000\u0000\u0000\u0000" + // 12795 - 12799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6FD\u0000\u0000" + // 12800 - 12804 + "\u0000\uC3A6\u0000\u0000\u0000\uC7BE\u0000\u0000\u0000\u0000" + // 12805 - 12809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4B1\u0000\u0000" + // 12810 - 12814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7A3\u0000\u0000" + // 12815 - 12819 + "\u0000\uB6B7\u0000\u0000\u0000\u0000\u0000\uDDF5\u0000\uDDFA" + // 12820 - 12824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0F4\u0000\uC7F1" + // 12825 - 12829 + "\u0000\u0000\u0000\uC8E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 12830 - 12834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDF7\u0000\u0000" + // 12835 - 12839 + "\u0000\uCBA1\u0000\u0000\u0000\uDDF9\u0000\u0000\u0000\uDEA4" + // 12840 - 12844 + "\u008F\uC7A1\u0000\uDEA2\u0000\u0000\u0000\uDDFB\u0000\u0000" + // 12845 - 12849 + "\u0000\u0000\u0000\uEAA5\u0000\u0000\u0000\u0000\u0000\u0000" + // 12850 - 12854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12855 - 12859 + "\u0000\uEAAE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAA8" + // 12860 - 12864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAB0\u0000\u0000" + // 12865 - 12869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12870 - 12874 + "\u0000\uCDE6\u0000\uEAB3\u0000\u0000\u0000\uEAAA\u0000\u0000" + // 12875 - 12879 + "\u0000\u0000\u0000\uEADB\u0000\u0000\u0000\uEADD\u0000\u0000" + // 12880 - 12884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12885 - 12889 + "\u0000\uC8EF\u0000\u0000\u0000\u0000\u0000\uEAD9\u0000\u0000" + // 12890 - 12894 + "\u0000\uEADE\u0000\uEAE0\u0000\u0000\u0000\u0000\u0000\uB8D3" + // 12895 - 12899 + "\u0000\uEAD4\u0000\u0000\u0000\uB0C1\u0000\u0000\u0000\u0000" + // 12900 - 12904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12905 - 12909 + "\u0000\uEADF\u0000\uDBDC\u0000\uB7E5\u0000\uB7CB\u0000\uC5ED" + // 12910 - 12914 + "\u008F\uC3D8\u0000\u0000\u0000\uDBDA\u0000\u0000\u0000\uB0C6" + // 12915 - 12919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBDD" + // 12920 - 12924 + "\u0000\uDBDF\u0000\u0000\u0000\uB6CD\u0000\uB7AC\u008F\uC3C9" + // 12925 - 12929 + "\u0000\uB4BC\u0000\uB5CB\u0000\u0000\u0000\u0000\u0000\u0000" + // 12930 - 12934 + "\u0000\u0000\u0000\uDBE2\u0000\u0000\u0000\u0000\u0000\uBAF9" + // 12935 - 12939 + "\u0000\uCBF1\u0000\u0000\u0000\uBBB7\u008F\uC3B9\u0000\uC2C8" + // 12940 - 12944 + "\u0000\u0000\u0000\u0000\u0000\uCAC1\u0000\u0000\u0000\uDBD6" + // 12945 - 12949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9A2\u0000\u0000" + // 12950 - 12954 + "\u0000\u0000\u0000\u0000\u0000\uDBD5\u0000\uC7F0\u0000\uCBBF" + // 12955 - 12959 + "\u0000\uB4BB\u0000\u0000\u0000\uC0F7\u0000\uBDC0\u0000\u0000" + // 12960 - 12964 + "\u0000\u0000\u0000\u0000\u0000\uC4D3\u0000\u0000\u0000\uCDAE" + // 12965 - 12969 + "\u0000\u0000\u0000\u0000\u0000\uDBD1\u0000\uDBD0\u0000\u0000" + // 12970 - 12974 + "\u0000\uCBD7\u0000\uC2F4\u0000\u0000\u0000\u0000\u0000\u0000" + // 12975 - 12979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12980 - 12984 + "\u0000\uCBF7\u0000\u0000\u0000\u0000\u0000\uDDFC\u0000\u0000" + // 12985 - 12989 + "\u0000\u0000\u0000\uDDFD\u0000\u0000\u0000\uB2CF\u0000\u0000" + // 12990 - 12994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAA8\u0000\uCCFD" + // 12995 - 12999 + "\u0000\uDEA1\u0000\uBCA3\u0000\uBEC2\u0000\uDDF8\u0000\uDDFE" + // 13000 - 13004 + "\u0000\uB1E8\u0000\uCFC8\u0000\uDBC6\u0000\uBFF5\u0000\u0000" + // 13005 - 13009 + "\u0000\u0000\u0000\u0000\u0000\uDBC5\u0000\u0000\u0000\u0000" + // 13010 - 13014 + "\u0000\uDBC0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13015 - 13019 + "\u0000\u0000\u0000\uB8CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 13020 - 13024 + "\u0000\uDBCC\u0000\uDBCA\u0000\u0000\u0000\uB2CD\u0000\uDBC8" + // 13025 - 13029 + "\u0000\uDBCE\u0000\uDBD4\u0000\u0000\u008F\uC3B5\u0000\u0000" + // 13030 - 13034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCBAE\u0000\uE0D0" + // 13035 - 13039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0CF\u0000\uC3F6" + // 13040 - 13044 + "\u0000\uC7AD\u0000\u0000\u0000\u0000\u0000\uB8A5\u0000\uE0CE" + // 13045 - 13049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0CD" + // 13050 - 13054 + "\u0000\u0000\u0000\uCDB1\u0000\uCDB2\u0000\u0000\u0000\u0000" + // 13055 - 13059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0D1" + // 13060 - 13064 + "\u0000\uB1EE\u0000\uDBBA\u0000\uBEF2\u0000\uCCDD\u0000\uDBBC" + // 13065 - 13069 + "\u0000\uDBBD\u0000\uCDE8\u008F\uC3A1\u0000\u0000\u0000\u0000" + // 13070 - 13074 + "\u0000\u0000\u0000\uDBC2\u0000\u0000\u0000\u0000\u0000\uB9BA" + // 13075 - 13079 + "\u0000\u0000\u0000\uC7D5\u0000\uDBBF\u0000\uC5EC\u0000\uDADE" + // 13080 - 13084 + "\u0000\uDAE2\u0000\u0000\u0000\uB5CF\u0000\u0000\u0000\uC7C7" + // 13085 - 13089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBC1" + // 13090 - 13094 + "\u0000\u0000\u0000\uBEBE\u0000\uC8C4\u0000\uBAC7\u0000\u0000" + // 13095 - 13099 + "\u0000\u0000\u0000\uD0F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 13100 - 13104 + "\u0000\u0000\u0000\uB7EE\u0000\uCDAD\u0000\u0000\u0000\uCAFE" + // 13105 - 13109 + "\u0000\u0000\u0000\uC9FE\u008F\uC2F0\u0000\uDBAC\u0000\u0000" + // 13110 - 13114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAF3\u0000\uC4BF" + // 13115 - 13119 + "\u0000\uDBAD\u0000\uCFAF\u0000\u0000\u0000\u0000\u0000\u0000" + // 13120 - 13124 + "\u0000\uCBBE\u0000\u0000\u0000\uC4AB\u0000\uDBAE\u0000\uB4FC" + // 13125 - 13129 + "\u0000\uDBA5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13130 - 13134 + "\u0000\u0000\u0000\uDBA7\u0000\u0000\u0000\u0000\u0000\uDBA8" + // 13135 - 13139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13140 - 13144 + "\u0000\u0000\u0000\uDBA9\u0000\u0000\u0000\uB6CA\u0000\uB1C8" + // 13145 - 13149 + "\u0000\uB9B9\u0000\uDBAA\u0000\u0000\u0000\uDBAB\u0000\uBDF1" + // 13150 - 13154 + "\u0000\uC1E2\u008F\uC2ED\u008F\uC2BF\u0000\uD2D8\u0000\uC1BE" + // 13155 - 13159 + "\u0000\uC1BD\u0000\uC2D8\u008F\uC2D7\u0000\u0000\u0000\uC4AA" + // 13160 - 13164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEF1\u0000\u0000" + // 13165 - 13169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBC3\u0000\u0000" + // 13170 - 13174 + "\u0000\u0000\u0000\uCAEB\u0000\u0000\u0000\u0000\u0000\u0000" + // 13175 - 13179 + "\u008F\uC2DB\u0000\u0000\u0000\uCBBD\u0000\u0000\u0000\u0000" + // 13180 - 13184 + "\u0000\u0000\u0000\uDBA2\u0000\uDAFB\u0000\u0000\u0000\u0000" + // 13185 - 13189 + "\u0000\uDAFE\u0000\u0000\u0000\uDAFD\u008F\uC2DE\u0000\uB1C7" + // 13190 - 13194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC2A5\u0000\uBDD5" + // 13195 - 13199 + "\u0000\u0000\u0000\uCBE6\u0000\uBAF2\u0000\u0000\u0000\u0000" + // 13200 - 13204 + "\u0000\u0000\u0000\u0000\u0000\uBEBC\u008F\uC2AB\u0000\uC0A7" + // 13205 - 13209 + "\u0000\u0000\u008F\uC2AD\u0000\u0000\u0000\u0000\u0000\uDAE5" + // 13210 - 13214 + "\u0000\uDAE3\u0000\uDAE4\u0000\u0000\u0000\u0000\u0000\u0000" + // 13215 - 13219 + "\u0000\u0000\u008F\uF4B6\u0000\uC3EB\u0000\u0000\u0000\u0000" + // 13220 - 13224 + "\u0000\uDBA6\u008F\uC1F5\u0000\u0000\u0000\uB9B7\u0000\uDAE0" + // 13225 - 13229 + "\u0000\u0000\u0000\u0000\u0000\uBAAB\u0000\uBEBA\u0000\u0000" + // 13230 - 13234 + "\u008F\uC1F8\u0000\uDADF\u0000\u0000\u0000\uBEBB\u0000\u0000" + // 13235 - 13239 + "\u0000\uCCC0\u0000\uBAAA\u0000\u0000\u0000\u0000\u0000\u0000" + // 13240 - 13244 + "\u0000\uB0D7\u0000\uC0CE\u008F\uC1FC\u0000\u0000\u0000\u0000" + // 13245 - 13249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAE6" + // 13250 - 13254 + "\u0000\u0000\u008F\uC2A1\u0000\uC0B1\u0000\uDADB\u0000\uDADC" + // 13255 - 13259 + "\u0000\uB4FB\u0000\u0000\u0000\u0000\u0000\uC6FC\u0000\uC3B6" + // 13260 - 13264 + "\u0000\uB5EC\u0000\uBBDD\u0000\uC1E1\u0000\u0000\u0000\u0000" + // 13265 - 13269 + "\u0000\uBDDC\u0000\uB0B0\u0000\u0000\u0000\u0000\u0000\u0000" + // 13270 - 13274 + "\u0000\uDADD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13275 - 13279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2A2" + // 13280 - 13284 + "\u0000\uDAE1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13285 - 13289 + "\u0000\uDFEF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFE7" + // 13290 - 13294 + "\u0000\u0000\u0000\uB7A7\u0000\u0000\u0000\u0000\u0000\u0000" + // 13295 - 13299 + "\u0000\u0000\u0000\uDFED\u0000\u0000\u0000\u0000\u0000\u0000" + // 13300 - 13304 + "\u0000\u0000\u0000\uCDD0\u0000\uDFF0\u0000\u0000\u0000\u0000" + // 13305 - 13309 + "\u0000\u0000\u0000\uF4A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 13310 - 13314 + "\u0000\u0000\u0000\u0000\u0000\uBDCF\u0000\uDAB5\u0000\u0000" + // 13315 - 13319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAB9\u0000\u0000" + // 13320 - 13324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13325 - 13329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13330 - 13334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13335 - 13339 + "\u0000\u0000\u0000\u0000\u0000\uDAB7\u0000\u0000\u0000\u0000" + // 13340 - 13344 + "\u0000\u0000\u0000\uDAB8\u0000\uD9F0\u0000\u0000\u0000\u0000" + // 13345 - 13349 + "\u0000\uE6B0\u0000\uE6B2\u0000\u0000\u0000\uCDE5\u0000\uE6B1" + // 13350 - 13354 + "\u0000\uE6B4\u0000\uE6B3\u0000\u0000\u0000\uCDD3\u0000\u0000" + // 13355 - 13359 + "\u0000\uE6B5\u0000\u0000\u0000\uC8FE\u0000\u0000\u0000\u0000" + // 13360 - 13364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6B6\u0000\u0000" + // 13365 - 13369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6B9" + // 13370 - 13374 + "\u0000\u0000\u0000\u0000\u0000\uE6B8\u0000\uE6B7\u0000\u0000" + // 13375 - 13379 + "\u0000\uDDEE\u0000\uDDEB\u0000\uCDE0\u0000\u0000\u0000\u0000" + // 13380 - 13384 + "\u008F\uC6EA\u0000\u0000\u0000\uC4C0\u0000\u0000\u0000\u0000" + // 13385 - 13389 + "\u0000\u0000\u0000\uC6D9\u0000\uDDEC\u0000\u0000\u0000\u0000" + // 13390 - 13394 + "\u0000\uDDF4\u0000\u0000\u0000\uDDF3\u0000\uB7A3\u0000\u0000" + // 13395 - 13399 + "\u0000\u0000\u0000\uB2AD\u0000\u0000\u0000\u0000\u0000\uBABB" + // 13400 - 13404 + "\u0000\uDDED\u0000\uDDEF\u0000\u0000\u0000\u0000\u0000\u0000" + // 13405 - 13409 + "\u0000\u0000\u0000\uE1B7\u0000\u0000\u0000\uE1BC\u0000\u0000" + // 13410 - 13414 + "\u0000\u0000\u0000\uE1BA\u0000\uE1B9\u0000\uDAC2\u0000\uB3A6" + // 13415 - 13419 + "\u0000\uE1B8\u0000\u0000\u0000\uB0DA\u0000\u0000\u0000\uC8AA" + // 13420 - 13424 + "\u0000\u0000\u0000\u0000\u0000\uC8CA\u0000\u0000\u0000\u0000" + // 13425 - 13429 + "\u0000\u0000\u0000\u0000\u0000\uCEB1\u0000\uE1BD\u0000\uE1BB" + // 13430 - 13434 + "\u0000\uC3DC\u0000\uC0A6\u0000\u0000\u0000\u0000\u0000\uEAA7" + // 13435 - 13439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13440 - 13444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13445 - 13449 + "\u0000\uCDBB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13450 - 13454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13455 - 13459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13460 - 13464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAA6\u0000\uDAAB" + // 13465 - 13469 + "\u0000\uDAAC\u0000\uC5A7\u0000\uDAAE\u0000\u0000\u0000\u0000" + // 13470 - 13474 + "\u0000\uBBA4\u0000\uDAA9\u0000\u0000\u0000\u0000\u0000\u0000" + // 13475 - 13479 + "\u0000\u0000\u0000\uB5BC\u0000\u0000\u0000\u0000\u0000\uDAAF" + // 13480 - 13484 + "\u0000\u0000\u0000\uDAA8\u0000\uDAB3\u0000\u0000\u0000\uDAB2" + // 13485 - 13489 + "\u0000\u0000\u0000\uDAB1\u0000\u0000\u0000\u0000\u0000\u0000" + // 13490 - 13494 + "\u0000\uDAB4\u0000\u0000\u0000\u0000\u0000\uDAB6\u0000\uBEF1" + // 13495 - 13499 + "\u0000\u0000\u0000\uDDC4\u0000\uBBDF\u0000\uC0B5\u0000\uBAA1" + // 13500 - 13504 + "\u0000\u0000\u0000\uC9F0\u0000\u0000\u0000\u0000\u0000\uCAE2" + // 13505 - 13509 + "\u0000\uCFC4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13510 - 13514 + "\u0000\uBBF5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAD0" + // 13515 - 13519 + "\u0000\uCEF2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDC5" + // 13520 - 13524 + "\u0000\uDDC6\u0000\u0000\u0000\uBBE0\u0000\u0000\u0000\u0000" + // 13525 - 13529 + "\u0000\u0000\u0000\uDDC7\u008F\uC0E4\u0000\u0000\u0000\u0000" + // 13530 - 13534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9F4" + // 13535 - 13539 + "\u0000\u0000\u0000\uCBE0\u0000\u0000\u0000\u0000\u0000\u0000" + // 13540 - 13544 + "\u0000\u0000\u0000\u0000\u0000\uD9F5\u0000\u0000\u0000\u0000" + // 13545 - 13549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9F6" + // 13550 - 13554 + "\u0000\u0000\u0000\uCCCE\u0000\u0000\u0000\uC0A2\u0000\u0000" + // 13555 - 13559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0B6" + // 13560 - 13564 + "\u0000\u0000\u0000\uB3E9\u0000\uBAD1\u0000\uBEC4\u0000\uDEBD" + // 13565 - 13569 + "\u0000\uBDC2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13570 - 13574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7CC\u0000\u0000" + // 13575 - 13579 + "\u0000\uDEBC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDED2" + // 13580 - 13584 + "\u0000\uBDED\u0000\uB8BA\u0000\u0000\u0000\uDEE1\u0000\u0000" + // 13585 - 13589 + "\u0000\uDEDB\u0000\uD9E3\u0000\u0000\u0000\u0000\u0000\uC2B7" + // 13590 - 13594 + "\u0000\uD9E9\u0000\u0000\u0000\uD9E4\u0000\u0000\u0000\u0000" + // 13595 - 13599 + "\u0000\uD9E6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13600 - 13604 + "\u0000\u0000\u0000\uC9C1\u0000\uC4F3\u0000\u0000\u0000\uD9E7" + // 13605 - 13609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDAC\u0000\u0000" + // 13610 - 13614 + "\u0000\u0000\u0000\u0000\u0000\uCDC8\u0000\uB4B9\u0000\u0000" + // 13615 - 13619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDCF\u0000\u0000" + // 13620 - 13624 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDD0" + // 13625 - 13629 + "\u0000\uDDD1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDD2" + // 13630 - 13634 + "\u0000\u0000\u0000\uDDD4\u0000\uDDD3\u0000\uDDD5\u0000\uB2A5" + // 13635 - 13639 + "\u0000\uC3CA\u0000\u0000\u0000\uDDD6\u0000\u0000\u0000\u0000" + // 13640 - 13644 + "\u0000\uBBA6\u0000\uB3CC\u0000\uDDD7\u0000\u0000\u0000\u0000" + // 13645 - 13649 + "\u0000\uC5C2\u0000\uCEAB\u0000\uBACE\u0000\uC3B5\u0000\uD9DA" + // 13650 - 13654 + "\u0000\u0000\u0000\uC0DC\u0000\u0000\u0000\uB9B5\u0000\uBFE4" + // 13655 - 13659 + "\u0000\uB1E6\u0000\uC1BC\u0000\uD9D8\u0000\uB5C5\u0000\u0000" + // 13660 - 13664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7C7" + // 13665 - 13669 + "\u0000\u0000\u0000\uC4CF\u0000\uD9DE\u0000\u0000\u0000\u0000" + // 13670 - 13674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1DF\u0000\u0000" + // 13675 - 13679 + "\u0000\u0000\u0000\uD9E1\u0000\u0000\u0000\uDCEA\u0000\uDDA5" + // 13680 - 13684 + "\u0000\uDDA4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13685 - 13689 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13690 - 13694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDAA" + // 13695 - 13699 + "\u0000\u0000\u0000\uCFA6\u0000\u0000\u0000\u0000\u0000\u0000" + // 13700 - 13704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDAD\u0000\uB6FB" + // 13705 - 13709 + "\u0000\u0000\u0000\u0000\u0000\uDDA9\u0000\uDDAB\u0000\uD9D7" + // 13710 - 13714 + "\u0000\u0000\u0000\u0000\u0000\uC1DD\u0000\u0000\u0000\u0000" + // 13715 - 13719 + "\u0000\u0000\u0000\u0000\u0000\uBCF8\u0000\uD9DC\u0000\u0000" + // 13720 - 13724 + "\u0000\u0000\u0000\uBEB8\u0000\u0000\u0000\uD9D6\u0000\uD9DB" + // 13725 - 13729 + "\u0000\u0000\u0000\u0000\u0000\uC7D3\u0000\u0000\u0000\u0000" + // 13730 - 13734 + "\u0000\u0000\u0000\uD9D5\u0000\u0000\u0000\uB7A1\u0000\u0000" + // 13735 - 13739 + "\u0000\u0000\u0000\uB3DD\u0000\u0000\u0000\u0000\u0000\u0000" + // 13740 - 13744 + "\u0000\uD9DD\u0000\uB5F2\u0000\uB3C8\u0000\u0000\u0000\u0000" + // 13745 - 13749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13750 - 13754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3E7\u0000\uBFA1" + // 13755 - 13759 + "\u0000\uD9C9\u0000\uD9CE\u0000\u0000\u0000\uD9CA\u0000\u0000" + // 13760 - 13764 + "\u0000\uB7FD\u0000\u0000\u0000\uD9CF\u0000\uBBA2\u0000\uB9E9" + // 13765 - 13769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13770 - 13774 + "\u0000\u0000\u0000\uBDA6\u0000\uD9BD\u0000\uB5BB\u0000\u0000" + // 13775 - 13779 + "\u0000\uD9B0\u0000\uD9B7\u0000\uBEB6\u0000\u0000\u0000\u0000" + // 13780 - 13784 + "\u0000\u0000\u0000\u0000\u0000\uD9B1\u0000\uC7C4\u0000\u0000" + // 13785 - 13789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13790 - 13794 + "\u0000\uCDDE\u0000\uD9B3\u0000\uD9B4\u0000\uD9B8\u0000\uC5EA" + // 13795 - 13799 + "\u0000\uD9B5\u0000\uB9B3\u0000\uC0DE\u0000\u0000\u0000\u0000" + // 13800 - 13804 + "\u0000\uD9C6\u0000\uC8B4\u0000\u0000\u0000\uC2F2\u0000\u0000" + // 13805 - 13809 + "\u0000\uC6CB\u0000\uDCF3\u0000\u0000\u0000\u0000\u0000\u0000" + // 13810 - 13814 + "\u0000\uDCF5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13815 - 13819 + "\u008F\uF4BD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13820 - 13824 + "\u0000\u0000\u0000\u0000\u0000\uDCEF\u008F\uC5B5\u0000\u0000" + // 13825 - 13829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCF1\u0000\u0000" + // 13830 - 13834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC5B6" + // 13835 - 13839 + "\u0000\uB3E0\u0000\uD9AD\u0000\u0000\u0000\u0000\u0000\uD9AB" + // 13840 - 13844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9AE" + // 13845 - 13849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13850 - 13854 + "\u0000\uCAB1\u0000\u0000\u0000\u0000\u0000\uB0B7\u0000\u0000" + // 13855 - 13859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9DE\u0000\u0000" + // 13860 - 13864 + "\u0000\u0000\u0000\uC8E3\u0000\u0000\u0000\u0000\u0000\uD9AF" + // 13865 - 13869 + "\u0000\u0000\u0000\uD9B2\u0000\uBEB5\u0000\uBDEA\u0000\uD9A8" + // 13870 - 13874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13875 - 13879 + "\u0000\uC0F0\u0000\uEEBD\u0000\uC8E2\u0000\u0000\u0000\uBCEA" + // 13880 - 13884 + "\u0000\u0000\u0000\uBACD\u0000\uD9A9\u0000\u0000\u0000\u0000" + // 13885 - 13889 + "\u0000\u0000\u0000\u0000\u0000\uC2C7\u0000\u0000\u0000\uCAA7" + // 13890 - 13894 + "\u0000\u0000\u0000\u0000\u0000\uC2F1\u0000\u0000\u0000\u0000" + // 13895 - 13899 + "\u0000\uD9AC\u0000\u0000\u0000\u0000\u0000\uD9AA\u0000\u0000" + // 13900 - 13904 + "\u0000\uDCC9\u0000\u0000\u0000\uDCD1\u0000\u0000\u0000\u0000" + // 13905 - 13909 + "\u0000\u0000\u0000\uF4A2\u0000\u0000\u0000\u0000\u0000\uDCCE" + // 13910 - 13914 + "\u0000\uB9BD\u0000\uC4C8\u0000\uC1E4\u0000\uDCCC\u0000\u0000" + // 13915 - 13919 + "\u0000\uDCC7\u0000\u0000\u0000\u0000\u0000\uDCCA\u0000\u0000" + // 13920 - 13924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDCD\u0000\uCBEA" + // 13925 - 13929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCCF\u0000\uDCD9" + // 13930 - 13934 + "\u0000\u0000\u0000\uCCCF\u0000\uDCF8\u0000\uDCEB\u0000\u0000" + // 13935 - 13939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8A2" + // 13940 - 13944 + "\u0000\uB2A3\u0000\uB3DF\u0000\u0000\u0000\u0000\u0000\uDCD3" + // 13945 - 13949 + "\u0000\u0000\u008F\uC4FD\u0000\u0000\u0000\u0000\u0000\u0000" + // 13950 - 13954 + "\u0000\u0000\u0000\uBEC1\u0000\uDCF0\u0000\u0000\u0000\uDCF7" + // 13955 - 13959 + "\u0000\uBCF9\u0000\uB3F2\u0000\u0000\u0000\u0000\u0000\uC3AE" + // 13960 - 13964 + "\u0000\u0000\u0000\u0000\u0000\uE9C4\u0000\u0000\u0000\u0000" + // 13965 - 13969 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13970 - 13974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDF6\u0000\u0000" + // 13975 - 13979 + "\u0000\uE2BC\u0000\uE9C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 13980 - 13984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13985 - 13989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13990 - 13994 + "\u0000\u0000\u0000\uE9C7\u0000\uD8F8\u0000\u0000\u0000\u0000" + // 13995 - 13999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14000 - 14004 + "\u0000\uD8F9\u0000\uD8FA\u0000\uCAEA\u0000\u0000\u0000\uD8FC" + // 14005 - 14009 + "\u0000\uD8FB\u0000\uBDBF\u0000\u0000\u0000\uC0AE\u0000\uB2E6" + // 14010 - 14014 + "\u0000\uB2FC\u008F\uBFA8\u0000\uD8FD\u0000\u0000\u0000\uB0BF" + // 14015 - 14019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0CC\u0000\uD8FE" + // 14020 - 14024 + "\u0000\u0000\u0000\uECC3\u0000\uD9A1\u0000\uB7E1\u008F\uF4B4" + // 14025 - 14029 + "\u0000\uD8BB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14030 - 14034 + "\u0000\u0000\u0000\uD8C3\u0000\uD8C2\u0000\u0000\u0000\u0000" + // 14035 - 14039 + "\u0000\u0000\u0000\uD8C7\u0000\u0000\u0000\u0000\u0000\u0000" + // 14040 - 14044 + "\u008F\uBEC9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8C8" + // 14045 - 14049 + "\u0000\u0000\u0000\u0000\u008F\uBECD\u0000\u0000\u0000\u0000" + // 14050 - 14054 + "\u0000\u0000\u0000\u0000\u0000\uD8C6\u0000\uD8C9\u0000\uD8C1" + // 14055 - 14059 + "\u0000\uD8C5\u0000\uD8B7\u0000\uBDA5\u0000\u0000\u0000\uD8BA" + // 14060 - 14064 + "\u0000\u0000\u0000\u0000\u0000\uD8B4\u0000\u0000\u0000\uCCFC" + // 14065 - 14069 + "\u0000\uCCFB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8BE" + // 14070 - 14074 + "\u0000\uD8BF\u0000\uB0D5\u0000\u0000\u008F\uBEBD\u0000\u0000" + // 14075 - 14079 + "\u0000\u0000\u0000\u0000\u0000\uD8B3\u0000\u0000\u0000\u0000" + // 14080 - 14084 + "\u0000\u0000\u0000\u0000\u0000\uB6F2\u0000\uB0A6\u0000\u0000" + // 14085 - 14089 + "\u0000\u0000\u0000\u0000\u0000\uB4B6\u0000\uD8AA\u0000\uD8A8" + // 14090 - 14094 + "\u0000\u0000\u0000\uC1DA\u0000\u0000\u0000\u0000\u0000\u0000" + // 14095 - 14099 + "\u0000\uD7FC\u0000\uBBB4\u0000\u0000\u0000\u0000\u0000\u0000" + // 14100 - 14104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2C6" + // 14105 - 14109 + "\u0000\uD8BD\u008F\uBEB3\u0000\uC1DB\u0000\uD8B8\u0000\u0000" + // 14110 - 14114 + "\u0000\uD8B5\u0000\uD8B6\u0000\u0000\u0000\uBCE6\u0000\uD8B9" + // 14115 - 14119 + "\u0000\uD8BC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14120 - 14124 + "\u0000\uCDF3\u0000\uDDB0\u0000\u0000\u0000\u0000\u0000\u0000" + // 14125 - 14129 + "\u0000\u0000\u0000\uDCDE\u0000\u0000\u0000\u0000\u0000\u0000" + // 14130 - 14134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDB3" + // 14135 - 14139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDB4\u0000\u0000" + // 14140 - 14144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14145 - 14149 + "\u0000\uB1B5\u0000\u0000\u0000\uDDB6\u0000\uCDAA\u0000\u0000" + // 14150 - 14154 + "\u0000\u0000\u0000\uB4B5\u0000\u0000\u0000\u0000\u0000\uB1D9" + // 14155 - 14159 + "\u0000\uD8A6\u0000\u0000\u0000\uC7BA\u0000\uB0AD\u0000\u0000" + // 14160 - 14164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14165 - 14169 + "\u0000\u0000\u0000\uC8E1\u0000\uD7DC\u0000\uD8AC\u0000\uD8B0" + // 14170 - 14174 + "\u0000\uCCE5\u0000\u0000\u0000\uD8A9\u0000\u0000\u0000\u0000" + // 14175 - 14179 + "\u0000\u0000\u0000\uC5E9\u0000\uD8AE\u0000\u0000\u0000\u0000" + // 14180 - 14184 + "\u0000\uBBBC\u0000\uE5ED\u0000\u0000\u0000\u0000\u0000\u0000" + // 14185 - 14189 + "\u0000\u0000\u0000\uE5F2\u0000\uE5F3\u008F\uD4E3\u0000\u0000" + // 14190 - 14194 + "\u0000\uE5F4\u0000\u0000\u0000\uE5FA\u0000\uC5BB\u0000\uE5F6" + // 14195 - 14199 + "\u0000\u0000\u0000\uE5F5\u0000\uE5F7\u0000\uE5F8\u0000\u0000" + // 14200 - 14204 + "\u0000\uE5F9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14205 - 14209 + "\u0000\uE5FB\u0000\uE5FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 14210 - 14214 + "\u0000\uF2B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14215 - 14219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14220 - 14224 + "\u0000\u0000\u0000\uB0BE\u008F\uEADB\u0000\u0000\u0000\uF2BA" + // 14225 - 14229 + "\u0000\uCAAB\u0000\uF2B8\u0000\u0000\u0000\u0000\u0000\uF2BB" + // 14230 - 14234 + "\u0000\uF2BC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14235 - 14239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2BD\u0000\uD7DF" + // 14240 - 14244 + "\u0000\u0000\u0000\uB2FA\u0000\uD7F3\u0000\uD7F5\u0000\uC3D1" + // 14245 - 14249 + "\u0000\u0000\u0000\u0000\u0000\uBAA8\u0000\uB2B8\u0000\uD7ED" + // 14250 - 14254 + "\u0000\uD7F8\u0000\uD7F7\u0000\uB6B3\u0000\u0000\u0000\uC2A9" + // 14255 - 14259 + "\u0000\uB3E6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14260 - 14264 + "\u0000\uB7C3\u0000\u0000\u0000\uD7EE\u0000\u0000\u0000\u0000" + // 14265 - 14269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14270 - 14274 + "\u0000\u0000\u0000\u0000\u008F\uF4B9\u0000\u0000\u0000\u0000" + // 14275 - 14279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14280 - 14284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14285 - 14289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14290 - 14294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14295 - 14299 + "\u0000\uA2E2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14300 - 14304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14305 - 14309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEB6" + // 14310 - 14314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14315 - 14319 + "\u0000\u0000\u0000\u0000\u0000\uF3FC\u0000\u0000\u0000\u0000" + // 14320 - 14324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3FD" + // 14325 - 14329 + "\u0000\uE3D4\u0000\u0000\u0000\u0000\u0000\uBDAA\u0000\uB8BE" + // 14330 - 14334 + "\u0000\uC1C8\u0000\uE5A5\u0000\uE5AB\u0000\u0000\u008F\uD3E1" + // 14335 - 14339 + "\u0000\u0000\u0000\u0000\u0000\uE5A6\u0000\uB7D0\u0000\u0000" + // 14340 - 14344 + "\u0000\uE5AE\u0000\uE5B2\u0000\uB7EB\u0000\u0000\u0000\u0000" + // 14345 - 14349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5AD\u0000\u0000" + // 14350 - 14354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B6\u008F\uD3E8" + // 14355 - 14359 + "\u0000\u0000\u0000\uB9CA\u0000\u0000\u0000\uDCC3\u0000\uB3B5" + // 14360 - 14364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14365 - 14369 + "\u0000\u0000\u0000\u0000\u0000\uBAE7\u0000\u0000\u0000\u0000" + // 14370 - 14374 + "\u0000\u0000\u0000\uB1DD\u0000\u0000\u0000\u0000\u0000\uDCD4" + // 14375 - 14379 + "\u0000\u0000\u0000\u0000\u0000\uCFB1\u0000\uDCD7\u0000\u0000" + // 14380 - 14384 + "\u0000\u0000\u008F\uC4D9\u0000\u0000\u0000\u0000\u0000\uBFBA" + // 14385 - 14389 + "\u0000\uDCD6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8C4" + // 14390 - 14394 + "\u0000\uC6BA\u0000\u0000\u0000\u0000\u0000\uE8C9\u0000\u0000" + // 14395 - 14399 + "\u0000\u0000\u0000\uCDE9\u0000\uE8C6\u0000\uCBA8\u0000\uE8CC" + // 14400 - 14404 + "\u0000\uB0E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14405 - 14409 + "\u0000\uE8C0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14410 - 14414 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14415 - 14419 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBA9\u0000\u0000" + // 14420 - 14424 + "\u0000\uCFA1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14425 - 14429 + "\u0000\u0000\u0000\uE8F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 14430 - 14434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8FA" + // 14435 - 14439 + "\u0000\u0000\u0000\u0000\u0000\uE8F2\u0000\uC2D5\u0000\uD7DE" + // 14440 - 14444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5DE\u0000\uD7E8" + // 14445 - 14449 + "\u0000\uC0AD\u0000\uB1E5\u0000\uD7E2\u0000\uB2F8\u0000\uD7E7" + // 14450 - 14454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6B1\u0000\u0000" + // 14455 - 14459 + "\u0000\uD7E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14460 - 14464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7EA" + // 14465 - 14469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14470 - 14474 + "\u0000\uDBC7\u0000\u0000\u0000\uC8FA\u0000\u0000\u0000\uDBBE" + // 14475 - 14479 + "\u0000\u0000\u0000\uDBC4\u0000\uDBC3\u0000\u0000\u0000\u0000" + // 14480 - 14484 + "\u0000\u0000\u0000\uC0CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 14485 - 14489 + "\u0000\u0000\u0000\uCBED\u0000\u0000\u0000\uCED3\u0000\u0000" + // 14490 - 14494 + "\u0000\u0000\u0000\uCBE7\u0000\u0000\u0000\uB2CC\u0000\uBBDE" + // 14495 - 14499 + "\u0000\u0000\u0000\u0000\u0000\uE3F3\u0000\u0000\u0000\uE4A2" + // 14500 - 14504 + "\u0000\u0000\u0000\uE3F6\u0000\u0000\u0000\uB5E8\u0000\u0000" + // 14505 - 14509 + "\u0000\uE3F5\u0000\uE4A4\u0000\u0000\u0000\u0000\u0000\u0000" + // 14510 - 14514 + "\u0000\uE3F4\u0000\u0000\u0000\uBED0\u0000\u0000\u0000\u0000" + // 14515 - 14519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3F8" + // 14520 - 14524 + "\u0000\uE3F9\u0000\u0000\u0000\uC5AB\u0000\u0000\u0000\u0000" + // 14525 - 14529 + "\u0000\uE3FA\u0000\u0000\u0000\uDCB1\u0000\uDBFA\u0000\uDCB0" + // 14530 - 14534 + "\u0000\u0000\u0000\uDCA9\u0000\uDBFB\u0000\u0000\u0000\uDCAD" + // 14535 - 14539 + "\u0000\u0000\u0000\uDCAE\u0000\u0000\u0000\u0000\u0000\u0000" + // 14540 - 14544 + "\u0000\u0000\u0000\u0000\u0000\uDCBF\u0000\u0000\u0000\u0000" + // 14545 - 14549 + "\u0000\u0000\u0000\uC6CE\u0000\u0000\u0000\uDCA4\u0000\u0000" + // 14550 - 14554 + "\u0000\u0000\u0000\uDCBB\u0000\u0000\u0000\u0000\u0000\u0000" + // 14555 - 14559 + "\u0000\uDCBD\u0000\u0000\u0000\uC4D8\u0000\uC3E9\u0000\u0000" + // 14560 - 14564 + "\u0000\u0000\u0000\u0000\u0000\uD7D8\u0000\u0000\u0000\u0000" + // 14565 - 14569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2F7" + // 14570 - 14574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8AD" + // 14575 - 14579 + "\u0000\uD7DA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7B0" + // 14580 - 14584 + "\u0000\u0000\u0000\u0000\u0000\uD7D9\u0000\u0000\u0000\u0000" + // 14585 - 14589 + "\u0000\uD7D7\u0000\u0000\u0000\uB9FA\u0000\u0000\u0000\uD7DD" + // 14590 - 14594 + "\u0000\uD7D2\u0000\uB8E6\u0000\u0000\u0000\u0000\u0000\u0000" + // 14595 - 14599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7D3\u0000\uC9FC" + // 14600 - 14604 + "\u0000\uBDDB\u0000\u0000\u0000\u0000\u0000\uD7D4\u0000\uC8F9" + // 14605 - 14609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6C1" + // 14610 - 14614 + "\u0000\uC4A7\u0000\u0000\u0000\u0000\u008F\uF4B2\u0000\u0000" + // 14615 - 14619 + "\u0000\uC5B0\u0000\u0000\u0000\u0000\u0000\uD7D5\u0000\uB5AB" + // 14620 - 14624 + "\u0000\u0000\u0000\u0000\u0000\uCCF0\u0000\uE2E3\u0000\u0000" + // 14625 - 14629 + "\u0000\uC3CE\u0000\u0000\u0000\uC7EA\u0000\u0000\u0000\uB6EB" + // 14630 - 14634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3BB\u0000\uE2E4" + // 14635 - 14639 + "\u0000\uB6BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0D0" + // 14640 - 14644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14645 - 14649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2E5\u0000\u0000" + // 14650 - 14654 + "\u0000\u0000\u0000\u0000\u0000\uEFBF\u0000\u0000\u0000\u0000" + // 14655 - 14659 + "\u0000\u0000\u0000\uEFC0\u0000\u0000\u0000\u0000\u0000\u0000" + // 14660 - 14664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14665 - 14669 + "\u0000\uEFC1\u0000\u0000\u0000\u0000\u0000\uEFBE\u0000\uEFBD" + // 14670 - 14674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBEE2\u0000\uC6AA" + // 14675 - 14679 + "\u0000\uEFBC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14680 - 14684 + "\u0000\u0000\u0000\uC0A8\u0000\uD1D9\u0000\uBDDA\u0000\u0000" + // 14685 - 14689 + "\u0000\u0000\u0000\uD1DA\u0000\u0000\u0000\uC3FC\u0000\uCEBF" + // 14690 - 14694 + "\u0000\uC5E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14695 - 14699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2C5" + // 14700 - 14704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1DB" + // 14705 - 14709 + "\u0000\uF4A5\u0000\uB6C5\u0000\u0000\u0000\u0000\u0000\uBABD" + // 14710 - 14714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14715 - 14719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2E6" + // 14720 - 14724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14725 - 14729 + "\u0000\uE2E7\u0000\u0000\u0000\uB8A6\u0000\uBAD5\u0000\u0000" + // 14730 - 14734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14735 - 14739 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2F6" + // 14740 - 14744 + "\u0000\u0000\u0000\u0000\u0000\uA2F5\u0000\u0000\u0000\uA2F4" + // 14745 - 14749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14750 - 14754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14755 - 14759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14760 - 14764 + "\u0000\u0000\u0000\uA2FE\u0000\u0000\u0000\u0000\u0000\u0000" + // 14765 - 14769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14770 - 14774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14775 - 14779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2AB\u0000\uA2AC" + // 14780 - 14784 + "\u0000\uA2AA\u0000\uA2AD\u0000\u0000\u0000\u0000\u0000\u0000" + // 14785 - 14789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14790 - 14794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAC5" + // 14795 - 14799 + "\u0000\uCDC3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14800 - 14804 + "\u0000\uD0FE\u0000\uD1A3\u0000\uD0FD\u0000\uBAC4\u0000\u0000" + // 14805 - 14809 + "\u0000\uBDFD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14810 - 14814 + "\u0000\u0000\u0000\u0000\u0000\uB7B9\u0000\u0000\u0000\uCEC2" + // 14815 - 14819 + "\u0000\u0000\u0000\uDBEC\u0000\u0000\u0000\uC7DF\u0000\u0000" + // 14820 - 14824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14825 - 14829 + "\u0000\u0000\u0000\uDBF4\u0000\uDBF4\u0000\uDBE7\u0000\u0000" + // 14830 - 14834 + "\u0000\u0000\u0000\u0000\u0000\uB0B4\u0000\uDBE9\u0000\u0000" + // 14835 - 14839 + "\u0000\u0000\u0000\uB9BC\u0000\u0000\u0000\u0000\u0000\u0000" + // 14840 - 14844 + "\u0000\uDBEB\u0000\u0000\u0000\uDBEA\u0000\u0000\u0000\uDBE6" + // 14845 - 14849 + "\u0000\uB1FD\u0000\uC0AC\u0000\uD7C9\u0000\uD7C8\u0000\uB7C2" + // 14850 - 14854 + "\u0000\uC2D4\u0000\u0000\u0000\uD7CE\u0000\uD7CC\u0000\u0000" + // 14855 - 14859 + "\u0000\uD7CB\u0000\uCEA7\u0000\uB8E5\u0000\u0000\u0000\u0000" + // 14860 - 14864 + "\u0000\u0000\u0000\uBDF9\u0000\uD7CD\u0000\uC5CC\u0000\uBDBE" + // 14865 - 14869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6C0\u0000\uD7D1" + // 14870 - 14874 + "\u0000\uD7D0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14875 - 14879 + "\u0000\uD7CF\u0000\u0000\u008F\uF4BA\u0000\uC4CE\u0000\uC6CA" + // 14880 - 14884 + "\u0000\uB1C9\u0000\uBAF4\u0000\u0000\u0000\u0000\u0000\u0000" + // 14885 - 14889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14890 - 14894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14895 - 14899 + "\u0000\uC0F2\u0000\u0000\u0000\u0000\u0000\uC0B4\u0000\uB7AA" + // 14900 - 14904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14905 - 14909 + "\u0000\u0000\u0000\uDBD9\u0000\u0000\u0000\uB9BB\u0000\uB3FC" + // 14910 - 14914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14915 - 14919 + "\u0000\u0000\u0000\uDBDB\u0000\uB3F4\u0000\uDBE1\u0000\u0000" + // 14920 - 14924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14925 - 14929 + "\u0000\uDBDE\u0000\u0000\u0000\uC0F3\u0000\u0000\u0000\u0000" + // 14930 - 14934 + "\u0000\u0000\u0000\uB3CB\u0000\uBAAC\u0000\u0000\u0000\u0000" + // 14935 - 14939 + "\u0000\uB3CA\u0000\uBACF\u0000\u0000\u0000\u0000\u0000\uE8DC" + // 14940 - 14944 + "\u0000\u0000\u0000\uE8D7\u0000\u0000\u0000\u0000\u0000\u0000" + // 14945 - 14949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBED5\u0000\u0000" + // 14950 - 14954 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDAF\u0000\u0000" + // 14955 - 14959 + "\u0000\u0000\u0000\u0000\u0000\uBCAC\u0000\u0000\u0000\u0000" + // 14960 - 14964 + "\u0000\u0000\u0000\u0000\u0000\uCCD8\u0000\u0000\u0000\u0000" + // 14965 - 14969 + "\u0000\uC9C7\u0000\u0000\u0000\u0000\u0000\uE8E7\u0000\uBEB3" + // 14970 - 14974 + "\u0000\uD7A7\u0000\uD7A6\u0000\uD7A2\u0000\u0000\u0000\u0000" + // 14975 - 14979 + "\u0000\u0000\u0000\u0000\u0000\uD7A8\u0000\uD7A9\u0000\u0000" + // 14980 - 14984 + "\u0000\u0000\u0000\uD7AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 14985 - 14989 + "\u0000\uD7AD\u0000\uD7AB\u0000\u0000\u0000\uD7AC\u0000\uD7AE" + // 14990 - 14994 + "\u0000\u0000\u0000\uB1E4\u0000\uC4EE\u0000\uD7AF\u0000\u0000" // 14995 - 14999 + ; + + index2b = + "\u0000\uB7FA\u0000\uB2F6\u0000\uC7B6\u0000\u0000\u0000\uD7B0" + // 15000 - 15004 + "\u0000\uC6FB\u0000\uD6F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 15005 - 15009 + "\u0000\u0000\u0000\u0000\u0000\uC5D9\u0000\uBAC2\u0000\u0000" + // 15010 - 15014 + "\u0000\u0000\u0000\u0000\u0000\uB8CB\u0000\u0000\u0000\uC4ED" + // 15015 - 15019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15020 - 15024 + "\u0000\u0000\u0000\u0000\u0000\uB0C3\u0000\uBDEE\u0000\uB9AF" + // 15025 - 15029 + "\u0000\uCDC7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15030 - 15034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9CB\u0000\uB0A7" + // 15035 - 15039 + "\u0000\u0000\u0000\u0000\u0000\uBAC3\u0000\u0000\u0000\u0000" + // 15040 - 15044 + "\u0000\u0000\u0000\uBFB6\u0000\u0000\u0000\u0000\u0000\u0000" + // 15045 - 15049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15050 - 15054 + "\u0000\u0000\u0000\u0000\u0000\uC4F2\u0000\u0000\u0000\u0000" + // 15055 - 15059 + "\u0000\uC8D4\u0000\uD9D1\u0000\uC1DE\u0000\uD6EC\u0000\u0000" + // 15060 - 15064 + "\u0000\u0000\u0000\uD6EB\u0000\uD6EA\u0000\uC9FD\u0000\u0000" + // 15065 - 15069 + "\u0000\uD6F3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15070 - 15074 + "\u0000\uCBDA\u0000\u0000\u0000\uD6ED\u0000\u0000\u0000\u0000" + // 15075 - 15079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6EF\u0000\uCBEB" + // 15080 - 15084 + "\u0000\u0000\u0000\uD6EE\u0000\u0000\u0000\u0000\u0000\u0000" + // 15085 - 15089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6F0" + // 15090 - 15094 + "\u0000\uC6BD\u0000\uB6AE\u0000\u0000\u0000\u0000\u0000\u0000" + // 15095 - 15099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2E5\u0000\uD6B6" + // 15100 - 15104 + "\u0000\uD6BB\u0000\u0000\u0000\u0000\u0000\uD6B9\u0000\u0000" + // 15105 - 15109 + "\u0000\uCAF7\u0000\uCAF6\u0000\u0000\u0000\u0000\u0000\u0000" + // 15110 - 15114 + "\u0000\u0000\u008F\uF4AF\u0000\uC5E7\u0000\u0000\u0000\u0000" + // 15115 - 15119 + "\u0000\u0000\u0000\uD6B8\u0000\uBDD4\u0000\u0000\u0000\uD6B7" + // 15120 - 15124 + "\u0000\u0000\u0000\u0000\u0000\uCEC5\u0000\u0000\u0000\u0000" + // 15125 - 15129 + "\u0000\u0000\u0000\uE1F4\u0000\uE1F2\u0000\uE1F3\u0000\u0000" + // 15130 - 15134 + "\u0000\u0000\u0000\u0000\u0000\uB4E2\u0000\u0000\u0000\u0000" + // 15135 - 15139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCFE\u0000\u0000" + // 15140 - 15144 + "\u0000\u0000\u0000\u0000\u0000\uCACA\u0000\u0000\u0000\uE1F6" + // 15145 - 15149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1F5\u0000\u0000" + // 15150 - 15154 + "\u0000\u0000\u0000\u0000\u0000\uC6E1\u0000\u0000\u0000\u0000" + // 15155 - 15159 + "\u0000\uCBAE\u0000\u0000\u0000\uEEB7\u0000\u0000\u0000\uBCD9" + // 15160 - 15164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEB8" + // 15165 - 15169 + "\u0000\u0000\u0000\uEEB9\u0000\u0000\u0000\u0000\u0000\u0000" + // 15170 - 15174 + "\u0000\uEEBA\u0000\u0000\u0000\u0000\u0000\uC5A1\u0000\u0000" + // 15175 - 15179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15180 - 15184 + "\u0000\u0000\u0000\uF3A9\u0000\u0000\u0000\uF3A8\u0000\u0000" + // 15185 - 15189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7DC" + // 15190 - 15194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15195 - 15199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15200 - 15204 + "\u0000\uF3AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15205 - 15209 + "\u0000\u0000\u0000\uC4BB\u0000\u0000\u0000\uF2EA\u0000\u0000" + // 15210 - 15214 + "\u0000\uC8B7\u0000\u0000\u0000\uF2EF\u0000\uF2EB\u0000\u0000" + // 15215 - 15219 + "\u0000\u0000\u0000\u0000\u0000\uF2EC\u0000\u0000\u0000\u0000" + // 15220 - 15224 + "\u0000\uCBB1\u0000\uCCC4\u0000\u0000\u0000\uC6D0\u0000\u0000" + // 15225 - 15229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15230 - 15234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B5\u0000\u0000" + // 15235 - 15239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B7" + // 15240 - 15244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE5B4\u0000\u0000" + // 15245 - 15249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7D1" + // 15250 - 15254 + "\u0000\uC2B3\u0000\uE5B9\u0000\uC1EE\u0000\u0000\u0000\u0000" + // 15255 - 15259 + "\u0000\uE5C6\u0000\uC5CB\u0000\uBCC8\u0000\uBCC8\u0000\u0000" + // 15260 - 15264 + "\u0000\uC1D8\u0000\uCDFA\u0000\u0000\u0000\u0000\u0000\u0000" + // 15265 - 15269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6A4\u0000\u0000" + // 15270 - 15274 + "\u0000\uD6A5\u0000\uC6D6\u0000\u0000\u0000\uBBB3\u0000\u0000" + // 15275 - 15279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6A7\u0000\u0000" + // 15280 - 15284 + "\u0000\u0000\u0000\uD6A8\u0000\u0000\u0000\u0000\u0000\u0000" + // 15285 - 15289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3F5\u0000\uC2ED" + // 15290 - 15294 + "\u008F\uF4C2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0A5" + // 15295 - 15299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFD0\u0000\u0000" + // 15300 - 15304 + "\u0000\uDFD2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15305 - 15309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15310 - 15314 + "\u0000\u0000\u0000\u0000\u0000\uDFD1\u0000\u0000\u0000\uDBB5" + // 15315 - 15319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBB8" + // 15320 - 15324 + "\u0000\u0000\u0000\u0000\u0000\uBFF9\u0000\u0000\u0000\u0000" + // 15325 - 15329 + "\u0000\u0000\u0000\u0000\u0000\uCDFB\u0000\uB0C9\u0000\uBAE0" + // 15330 - 15334 + "\u0000\uC2BC\u0000\u0000\u0000\uBCDD\u0000\u0000\u0000\u0000" + // 15335 - 15339 + "\u0000\uBEF3\u0000\u0000\u0000\u0000\u0000\uDBBB\u0000\u0000" + // 15340 - 15344 + "\u0000\u0000\u0000\uC5CE\u0000\u0000\u0000\uDBB9\u0000\uC2AB" + // 15345 - 15349 + "\u0000\uB6C9\u0000\uD5FB\u0000\u0000\u0000\u0000\u0000\u0000" + // 15350 - 15354 + "\u0000\uB5EF\u0000\uD5FC\u0000\u0000\u0000\uB6FE\u0000\u0000" + // 15355 - 15359 + "\u0000\uC6CF\u0000\uB2B0\u0000\u0000\u0000\uBBD3\u0000\uD5FD" + // 15360 - 15364 + "\u0000\uD6A2\u0000\uD6A1\u0000\uB6FD\u0000\u0000\u0000\uD5FE" + // 15365 - 15369 + "\u0000\u0000\u0000\uC5B8\u0000\u0000\u0000\u0000\u0000\u0000" + // 15370 - 15374 + "\u0000\u0000\u0000\u0000\u0000\uD6A2\u0000\u0000\u0000\u0000" + // 15375 - 15379 + "\u0000\uC2B0\u0000\u0000\u0000\uB6C7\u0000\u0000\u0000\uDAF3" + // 15380 - 15384 + "\u0000\uDAF7\u0000\u0000\u0000\u0000\u0000\uB2CB\u0000\uDAF4" + // 15385 - 15389 + "\u0000\uDAF6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15390 - 15394 + "\u0000\uDAF5\u0000\u0000\u0000\u0000\u0000\uBDEB\u0000\u0000" + // 15395 - 15399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3C8\u0000\uB0C5" + // 15400 - 15404 + "\u0000\uDAF8\u008F\uC2D2\u0000\u0000\u0000\u0000\u0000\u0000" + // 15405 - 15409 + "\u0000\uDAF9\u0000\u0000\u0000\u0000\u0000\uE7E0\u0000\u0000" + // 15410 - 15414 + "\u0000\uE7DF\u0000\u0000\u0000\uB4CF\u0000\u0000\u0000\uE7E1" + // 15415 - 15419 + "\u0000\u0000\u0000\uE7E2\u0000\uE7E3\u0000\u0000\u0000\u0000" + // 15420 - 15424 + "\u0000\uBAB1\u0000\uCEC9\u0000\u0000\u0000\uE7E5\u0000\uBFA7" + // 15425 - 15429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1F0\u0000\uE7E6" + // 15430 - 15434 + "\u0000\uE7E7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15435 - 15439 + "\u0000\u0000\u0000\uE7E8\u0000\u0000\u0000\uDAFA\u0000\u0000" + // 15440 - 15444 + "\u0000\u0000\u0000\uDBA1\u0000\u0000\u0000\u0000\u0000\uC6DE" + // 15445 - 15449 + "\u0000\u0000\u0000\uDAFC\u0000\u0000\u0000\u0000\u0000\u0000" + // 15450 - 15454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15455 - 15459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBA3" + // 15460 - 15464 + "\u0000\u0000\u0000\u0000\u0000\uBDEC\u0000\uDBA4\u0000\u0000" + // 15465 - 15469 + "\u0000\uCDCB\u0000\uC7F8\u0000\u0000\u0000\u0000\u008F\uF4D7" + // 15470 - 15474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15475 - 15479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15480 - 15484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15485 - 15489 + "\u0000\u0000\u0000\uE8BA\u0000\u0000\u0000\uE8BB\u0000\u0000" + // 15490 - 15494 + "\u0000\uB2D9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2AE" + // 15495 - 15499 + "\u0000\uE8B8\u0000\u0000\u0000\u0000\u008F\uD8A2\u0000\uD5F6" + // 15500 - 15504 + "\u0000\u0000\u0000\uD5F7\u0000\u0000\u0000\uCCE0\u0000\u0000" + // 15505 - 15509 + "\u0000\u0000\u0000\u0000\u0000\uD5F8\u0000\u0000\u0000\u0000" + // 15510 - 15514 + "\u0000\u0000\u0000\u0000\u0000\uB6C6\u0000\u0000\u0000\u0000" + // 15515 - 15519 + "\u0000\u0000\u0000\uBDA2\u0000\u0000\u0000\u0000\u0000\u0000" + // 15520 - 15524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5F9\u0000\uD5FA" + // 15525 - 15529 + "\u0000\uBCDC\u0000\uBFAC\u0000\uC6F4\u0000\uBFD4\u0000\uC8F8" + // 15530 - 15534 + "\u0000\uC7A2\u008F\uBADB\u0000\u0000\u0000\uBCE4\u0000\uD5E3" + // 15535 - 15539 + "\u0000\uB4F3\u0000\uC6D2\u0000\uCCA9\u0000\uD5E4\u0000\u0000" + // 15540 - 15544 + "\u0000\uD5E5\u0000\u0000\u0000\u0000\u0000\uC9D9\u0000\u0000" + // 15545 - 15549 + "\u0000\u0000\u0000\u0000\u0000\uD5E7\u0000\u0000\u0000\uB4A8" + // 15550 - 15554 + "\u0000\uB6F7\u0000\uD5E6\u0000\u0000\u0000\u0000\u0000\u0000" + // 15555 - 15559 + "\u008F\uBAE1\u0000\u0000\u0000\u0000\u0000\uB4B2\u0000\u0000" + // 15560 - 15564 + "\u0000\uBFB2\u0000\uD5EB\u0000\uBBA1\u0000\uD5DF\u0000\u0000" + // 15565 - 15569 + "\u0000\u0000\u0000\uD5E0\u0000\u0000\u0000\uC2F0\u0000\u0000" + // 15570 - 15574 + "\u0000\uB1A7\u0000\uBCE9\u0000\uB0C2\u0000\u0000\u0000\uC1D7" + // 15575 - 15579 + "\u0000\uB4B0\u0000\uBCB5\u0000\u0000\u0000\uB9A8\u0000\u0000" + // 15580 - 15584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5E6" + // 15585 - 15589 + "\u0000\u0000\u0000\uBDA1\u0000\uB4B1\u0000\uC3E8\u0000\uC4EA" + // 15590 - 15594 + "\u0000\uB0B8\u0000\uB5B9\u0000\uCAF5\u0000\u0000\u0000\uBCC2" + // 15595 - 15599 + "\u0000\uD5D2\u0000\u0000\u0000\u0000\u0000\uD5D0\u0000\u0000" + // 15600 - 15604 + "\u0000\uD5D1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15605 - 15609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15610 - 15614 + "\u0000\u0000\u0000\uBBD2\u0000\uD5D3\u0000\u0000\u0000\u0000" + // 15615 - 15619 + "\u0000\uB9A6\u0000\uD5D4\u008F\uBABE\u0000\uBBFA\u0000\uC2B8" + // 15620 - 15624 + "\u0000\u0000\u0000\uD5D5\u0000\uD5D6\u0000\uBBDA\u0000\uB9A7" + // 15625 - 15629 + "\u0000\u0000\u0000\uCCD2\u0000\uD5B4\u0000\uCFAC\u0000\u0000" + // 15630 - 15634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7CC\u0000\u0000" + // 15635 - 15639 + "\u0000\u0000\u0000\uD5B6\u0000\u0000\u0000\u0000\u0000\u0000" + // 15640 - 15644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15645 - 15649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15650 - 15654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAA7\u0000\u0000" + // 15655 - 15659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBEF0" + // 15660 - 15664 + "\u0000\uD8AF\u0000\uC6D7\u0000\u0000\u0000\u0000\u0000\u0000" + // 15665 - 15669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15670 - 15674 + "\u0000\u0000\u0000\uCFC7\u0000\u0000\u0000\uD8AB\u0000\u0000" + // 15675 - 15679 + "\u008F\uBEAC\u0000\u0000\u0000\u0000\u0000\uD8B1\u0000\u0000" + // 15680 - 15684 + "\u0000\uB9FB\u0000\u0000\u0000\uC0CB\u0000\u0000\u008F\uBEB0" + // 15685 - 15689 + "\u0000\uB0D4\u0000\uBFB1\u0000\u0000\u0000\u0000\u0000\u0000" + // 15690 - 15694 + "\u0000\u0000\u0000\uD5AE\u0000\u0000\u0000\u0000\u0000\u0000" + // 15695 - 15699 + "\u0000\uCADA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15700 - 15704 + "\u0000\u0000\u0000\uB8E4\u0000\u0000\u0000\u0000\u0000\u0000" + // 15705 - 15709 + "\u0000\u0000\u0000\u0000\u0000\uD5B7\u0000\uD5B8\u0000\u0000" + // 15710 - 15714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBEAB" + // 15715 - 15719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDFE\u0000\u0000" + // 15720 - 15724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCCD0" + // 15725 - 15729 + "\u0000\u0000\u0000\uE0F8\u0000\u0000\u0000\u0000\u0000\u0000" + // 15730 - 15734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15735 - 15739 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15740 - 15744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15745 - 15749 + "\u0000\uE0F9\u008F\uCCD9\u0000\uD4FB\u0000\u0000\u0000\uD4FA" + // 15750 - 15754 + "\u008F\uB8FC\u0000\u0000\u0000\uB1FC\u0000\u0000\u0000\uD4FC" + // 15755 - 15759 + "\u0000\uBEA9\u0000\uD4FE\u0000\uC3A5\u0000\u0000\u0000\uD4FD" + // 15760 - 15764 + "\u0000\u0000\u0000\uCAB3\u0000\u0000\u0000\u0000\u0000\u0000" + // 15765 - 15769 + "\u0000\u0000\u0000\uBDF7\u0000\uC5DB\u0000\u0000\u0000\u0000" + // 15770 - 15774 + "\u0000\u0000\u0000\uD5A1\u0000\u0000\u0000\u0000\u0000\u0000" + // 15775 - 15779 + "\u0000\u0000\u0000\uB9A5\u0000\u0000\u0000\u0000\u0000\uE0E5" + // 15780 - 15784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0FA" + // 15785 - 15789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15790 - 15794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4C4\u0000\u0000" + // 15795 - 15799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15800 - 15804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15805 - 15809 + "\u0000\u0000\u0000\uBCA5\u0000\u0000\u0000\u0000\u0000\uE1CA" + // 15810 - 15814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1C5\u0000\uE1C6" + // 15815 - 15819 + "\u0000\u0000\u0000\uE1C9\u0000\uE1C8\u0000\uC9A5\u0000\u0000" + // 15820 - 15824 + "\u0000\u0000\u0000\uC1C2\u0000\uC1C1\u0000\u0000\u0000\uB5BF" + // 15825 - 15829 + "\u0000\u0000\u0000\u0000\u0000\uE1CB\u0000\u0000\u0000\u0000" + // 15830 - 15834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1CC\u0000\u0000" + // 15835 - 15839 + "\u0000\u0000\u0000\uE1CD\u0000\u0000\u0000\u0000\u0000\uE1D0" + // 15840 - 15844 + "\u0000\uE1D2\u0000\u0000\u0000\uC9C2\u0000\u0000\u0000\uBEC9" + // 15845 - 15849 + "\u0000\u0000\u0000\u0000\u0000\uE1D9\u0000\u0000\u0000\u0000" + // 15850 - 15854 + "\u0000\uE1D8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15855 - 15859 + "\u0000\uE1DA\u0000\u0000\u0000\uBCA6\u0000\uBAAF\u0000\u0000" + // 15860 - 15864 + "\u0000\u0000\u0000\uC5F7\u0000\uE1DB\u0000\u0000\u0000\uC4CB" + // 15865 - 15869 + "\u0000\u0000\u0000\u0000\u0000\uE1DD\u0000\u0000\u0000\uDAD5" + // 15870 - 15874 + "\u0000\u0000\u0000\uDAD3\u0000\uDAD6\u0000\uCEB9\u0000\uDAD4" + // 15875 - 15879 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0FB" + // 15880 - 15884 + "\u0000\uDAD7\u0000\u0000\u0000\u0000\u0000\uC2B2\u0000\u0000" + // 15885 - 15889 + "\u0000\u0000\u0000\uDAD8\u0000\u0000\u0000\u0000\u0000\u0000" + // 15890 - 15894 + "\u0000\u0000\u0000\uB4FA\u0000\u0000\u0000\uDADA\u0000\u0000" + // 15895 - 15899 + "\u0000\uDAD9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15900 - 15904 + "\u008F\uC9E3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15905 - 15909 + "\u0000\uDFE1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15910 - 15914 + "\u0000\u0000\u008F\uC9E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 15915 - 15919 + "\u0000\u0000\u0000\uB1EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 15920 - 15924 + "\u0000\u0000\u0000\uDFE4\u0000\uCAB2\u0000\u0000\u0000\uDFE3" + // 15925 - 15929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0DF\u0000\uE6F4" + // 15930 - 15934 + "\u0000\u0000\u0000\uC3C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 15935 - 15939 + "\u0000\u0000\u0000\u0000\u0000\uC7D8\u0000\u0000\u0000\uC2DB" + // 15940 - 15944 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15945 - 15949 + "\u0000\u0000\u0000\u0000\u0000\uE6F6\u0000\u0000\u0000\u0000" + // 15950 - 15954 + "\u0000\uE6F2\u0000\uE6F5\u0000\uE6F0\u0000\u0000\u0000\uE6F3" + // 15955 - 15959 + "\u0000\uCBA6\u0000\u0000\u0000\uDAEA\u0000\uBBFE\u0000\uB9B8" + // 15960 - 15964 + "\u0000\uDAE8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15965 - 15969 + "\u0000\uDAE9\u0000\u0000\u0000\uBFB8\u0000\u0000\u0000\u0000" + // 15970 - 15974 + "\u0000\u0000\u0000\uDAE7\u0000\u0000\u0000\u0000\u0000\uBBAF" + // 15975 - 15979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC2B8" + // 15980 - 15984 + "\u0000\u0000\u008F\uC2BA\u0000\u0000\u0000\u0000\u0000\u0000" + // 15985 - 15989 + "\u0000\uDAEC\u0000\uDAEB\u0000\uDAF0\u0000\uCABD\u0000\uCEDD" + // 15990 - 15994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15995 - 15999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2F4\u0000\uD4CA" + // 16000 - 16004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16005 - 16009 + "\u0000\uC1BA\u0000\uD4CD\u0000\u0000\u0000\uC5E3\u0000\u0000" + // 16010 - 16014 + "\u0000\u0000\u0000\uC5C9\u0000\uC5E4\u0000\uC8B9\u0000\uC4CD" + // 16015 - 16019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBAC9\u0000\u0000" + // 16020 - 16024 + "\u0000\uCDCA\u0000\uDAA7\u0000\u0000\u0000\u0000\u0000\uDAA3" + // 16025 - 16029 + "\u0000\u0000\u0000\uDAA4\u0000\u0000\u0000\u0000\u0000\u0000" + // 16030 - 16034 + "\u0000\u0000\u0000\u0000\u0000\uC1E0\u008F\uC1A6\u0000\u0000" + // 16035 - 16039 + "\u0000\u0000\u0000\u0000\u0000\uDAA2\u0000\u0000\u0000\uD9BF" + // 16040 - 16044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAA6\u0000\u0000" + // 16045 - 16049 + "\u0000\uDAA1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16050 - 16054 + "\u0000\u0000\u008F\uC9CA\u0000\u0000\u0000\u0000\u0000\u0000" + // 16055 - 16059 + "\u0000\uCFA7\u0000\uBFE6\u0000\u0000\u0000\u0000\u0000\u0000" + // 16060 - 16064 + "\u0000\uB1EA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFD6" + // 16065 - 16069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16070 - 16074 + "\u0000\u0000\u0000\uDFD5\u0000\u0000\u0000\u0000\u0000\u0000" + // 16075 - 16079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2E8\u0000\u0000" + // 16080 - 16084 + "\u0000\u0000\u0000\u0000\u0000\uE2EA\u0000\uE3AA\u0000\uE3A9" + // 16085 - 16089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uF4CB\u0000\u0000" + // 16090 - 16094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16095 - 16099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCA8\u0000\u0000" + // 16100 - 16104 + "\u0000\uCEE9\u0000\u0000\u0000\uBCD2\u0000\u0000\u0000\uB0B6" + // 16105 - 16109 + "\u0000\u0000\u0000\u0000\u0000\uB6D4\u0000\uC0CD\u0000\u0000" + // 16110 - 16114 + "\u0000\uC9E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAD1" + // 16115 - 16119 + "\u0000\uBBC2\u0000\uC3C7\u0000\u0000\u0000\uBBDB\u0000\uBFB7" + // 16120 - 16124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16125 - 16129 + "\u0000\u0000\u0000\uDAD2\u0000\u0000\u0000\uCAFD\u0000\u0000" + // 16130 - 16134 + "\u0000\u0000\u0000\uB1F7\u0000\uBBDC\u0000\u0000\u0000\u0000" + // 16135 - 16139 + "\u0000\uBBE9\u0000\u0000\u0000\u0000\u0000\uB6BC\u0000\uC0C8" + // 16140 - 16144 + "\u0000\uCFC6\u0000\uCCAE\u0000\uE6F7\u0000\uC0D4\u0000\u0000" + // 16145 - 16149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16150 - 16154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16155 - 16159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5D3" + // 16160 - 16164 + "\u0000\uE6FA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16165 - 16169 + "\u008F\uE5B3\u0000\u0000\u0000\u0000\u0000\uB3BB\u0000\u0000" + // 16170 - 16174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFAE\u0000\uEFAF" + // 16175 - 16179 + "\u0000\uC4C3\u0000\u0000\u0000\uEFAD\u0000\u0000\u0000\u0000" + // 16180 - 16184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16185 - 16189 + "\u0000\u0000\u0000\uEFB1\u0000\u0000\u0000\u0000\u0000\u0000" + // 16190 - 16194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2A2\u0000\u0000" + // 16195 - 16199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16200 - 16204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16205 - 16209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16210 - 16214 + "\u0000\u0000\u0000\uF2A3\u0000\u0000\u0000\uF2A4\u0000\u0000" + // 16215 - 16219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2A5\u0000\uCBD9" + // 16220 - 16224 + "\u0000\u0000\u0000\uC6B2\u0000\u0000\u0000\u0000\u0000\uB7F8" + // 16225 - 16229 + "\u0000\uC2CF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4C1" + // 16230 - 16234 + "\u0000\uD4C4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16235 - 16239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16240 - 16244 + "\u0000\uC2C4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4C5" + // 16245 - 16249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4C6\u0000\u0000" + // 16250 - 16254 + "\u0000\u0000\u0000\uDEF1\u0000\u0000\u0000\uDEEB\u0000\uCCC7" + // 16255 - 16259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEE6\u0000\u0000" + // 16260 - 16264 + "\u0000\uBCA2\u0000\uDEFE\u0000\u0000\u0000\u0000\u0000\u0000" + // 16265 - 16269 + "\u0000\u0000\u0000\uB3EA\u0000\u0000\u0000\uDEE8\u0000\uDEED" + // 16270 - 16274 + "\u0000\uDEEE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16275 - 16279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2EC\u0000\uC2DA" + // 16280 - 16284 + "\u0000\u0000\u0000\uB0AE\u0000\u0000\u0000\uD9E5\u0000\u0000" + // 16285 - 16289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9E2" + // 16290 - 16294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4F8" + // 16295 - 16299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16300 - 16304 + "\u0000\uB1E7\u008F\uC0C4\u0000\uD9E8\u0000\u0000\u0000\u0000" + // 16305 - 16309 + "\u0000\u0000\u0000\uCDC9\u0000\u0000\u0000\u0000\u0000\u0000" + // 16310 - 16314 + "\u0000\u0000\u0000\u0000\u0000\uDEBA\u0000\u0000\u008F\uC7C7" + // 16315 - 16319 + "\u0000\uBEC3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDB0" + // 16320 - 16324 + "\u0000\u0000\u0000\uDEB7\u0000\u0000\u0000\u0000\u0000\u0000" + // 16325 - 16329 + "\u0000\u0000\u0000\uDEB2\u0000\u0000\u0000\uDEB8\u008F\uC7CB" + // 16330 - 16334 + "\u0000\u0000\u0000\u0000\u0000\uCEDE\u0000\u0000\u0000\uC5F3" + // 16335 - 16339 + "\u0000\uC6C2\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC7AB" + // 16340 - 16344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16345 - 16349 + "\u0000\u0000\u0000\uCDCE\u0000\uDEB0\u0000\u0000\u0000\u0000" + // 16350 - 16354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEAF\u0000\u0000" + // 16355 - 16359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0F6\u0000\u0000" + // 16360 - 16364 + "\u0000\uDEAC\u0000\u0000\u0000\uCDEC\u0000\u0000\u0000\u0000" + // 16365 - 16369 + "\u0000\uC6B6\u0000\uDEA6\u0000\uC9D6\u0000\u0000\u0000\u0000" + // 16370 - 16374 + "\u0000\uD4C3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16375 - 16379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16380 - 16384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16385 - 16389 + "\u0000\u0000\u0000\u0000\u0000\uBEFD\u0000\u0000\u0000\u0000" + // 16390 - 16394 + "\u0000\uBCB9\u0000\u0000\u0000\uC7DD\u0000\uB4F0\u0000\u0000" + // 16395 - 16399 + "\u0000\uBAEB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDC3" + // 16400 - 16404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0D7" + // 16405 - 16409 + "\u0000\u0000\u0000\uE0D6\u0000\u0000\u0000\u0000\u0000\u0000" + // 16410 - 16414 + "\u0000\u0000\u0000\u0000\u0000\uE0D8\u0000\u0000\u0000\uB3CD" + // 16415 - 16419 + "\u0000\u0000\u0000\u0000\u0000\uE0DA\u0000\u0000\u008F\uCBCA" + // 16420 - 16424 + "\u0000\uE0D9\u0000\u0000\u0000\uE0DC\u0000\uE0DB\u0000\u0000" + // 16425 - 16429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9A6\u0000\u0000" + // 16430 - 16434 + "\u0000\uC1A6\u0000\u0000\u0000\uE9AA\u0000\uBBA7\u0000\uBFC5" + // 16435 - 16439 + "\u0000\uB7B0\u0000\uCCF4\u0000\u0000\u0000\uCCF9\u0000\uBDF2" + // 16440 - 16444 + "\u008F\uF4D9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16445 - 16449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9B7" + // 16450 - 16454 + "\u0000\uE9B5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16455 - 16459 + "\u0000\u0000\u0000\uE9F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 16460 - 16464 + "\u0000\uE9F3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16465 - 16469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16470 - 16474 + "\u0000\u0000\u0000\uE9EE\u0000\u0000\u0000\u0000\u0000\uE9F0" + // 16475 - 16479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9F1\u0000\u0000" + // 16480 - 16484 + "\u0000\u0000\u0000\u0000\u0000\uE9EF\u0000\uD4B1\u0000\u0000" + // 16485 - 16489 + "\u0000\u0000\u0000\uD4BC\u0000\u0000\u0000\u0000\u0000\uD4BD" + // 16490 - 16494 + "\u008F\uB7E7\u008F\uB7E8\u0000\u0000\u0000\u0000\u0000\uCBE4" + // 16495 - 16499 + "\u0000\u0000\u0000\u0000\u0000\uBEEB\u0000\u0000\u0000\u0000" + // 16500 - 16504 + "\u0000\u0000\u0000\uD4BF\u0000\uD4C0\u0000\uD4BE\u0000\u0000" + // 16505 - 16509 + "\u0000\uD4C2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16510 - 16514 + "\u0000\u0000\u0000\uC7B8\u0000\u0000\u0000\u0000\u0000\uB0E8" + // 16515 - 16519 + "\u0000\uD4B7\u0000\u0000\u0000\uB9A4\u0000\uB3C0\u0000\uD4B9" + // 16520 - 16524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16525 - 16529 + "\u0000\uD4BA\u0000\u0000\u008F\uB7E4\u0000\u0000\u0000\u0000" + // 16530 - 16534 + "\u0000\u0000\u0000\uD4BB\u0000\u0000\u0000\u0000\u0000\uD4B8" + // 16535 - 16539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16540 - 16544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16545 - 16549 + "\u0000\u0000\u0000\u0000\u008F\uC7CF\u0000\u0000\u0000\u0000" + // 16550 - 16554 + "\u0000\uB3B6\u0000\u0000\u0000\u0000\u0000\uB1D5\u0000\u0000" + // 16555 - 16559 + "\u0000\u0000\u0000\uDEBE\u0000\u0000\u0000\u0000\u0000\uDEC1" + // 16560 - 16564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEC3\u0000\u0000" + // 16565 - 16569 + "\u0000\u0000\u0000\u0000\u0000\uDFD4\u0000\u0000\u0000\u0000" + // 16570 - 16574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16575 - 16579 + "\u0000\uB2D0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5F4" + // 16580 - 16584 + "\u0000\uB3A5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16585 - 16589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5E4\u0000\u0000" + // 16590 - 16594 + "\u0000\u0000\u0000\u0000\u0000\uBCDE\u0000\uBAD2\u0000\u0000" + // 16595 - 16599 + "\u0000\u0000\u0000\uB0EE\u0000\u0000\u0000\u0000\u0000\uDEF0" + // 16600 - 16604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEE4" + // 16605 - 16609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEEA" + // 16610 - 16614 + "\u0000\u0000\u0000\u0000\u0000\uDEEC\u0000\u0000\u0000\u0000" + // 16615 - 16619 + "\u0000\u0000\u0000\uCDCF\u0000\uDEE7\u0000\u0000\u0000\u0000" + // 16620 - 16624 + "\u0000\uC5AE\u0000\u0000\u0000\u0000\u0000\uDEE9\u0000\u0000" + // 16625 - 16629 + "\u008F\uC8B1\u0000\uD4AE\u0000\u0000\u0000\uBAE4\u0000\u0000" + // 16630 - 16634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6D1\u0000\u0000" + // 16635 - 16639 + "\u0000\u0000\u0000\uCBB7\u0000\u0000\u0000\u0000\u0000\u0000" + // 16640 - 16644 + "\u0000\uD4AC\u0000\uD4AF\u0000\uBAC1\u0000\uB9A3\u0000\u0000" + // 16645 - 16649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16650 - 16654 + "\u0000\u0000\u008F\uF4A9\u0000\u0000\u0000\u0000\u0000\u0000" + // 16655 - 16659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBAF\u0000\uDBB0" + // 16660 - 16664 + "\u0000\uCCDA\u0000\u0000\u0000\uCCA4\u0000\uCBF6\u0000\uCBDC" + // 16665 - 16669 + "\u0000\uBBA5\u0000\uDBB2\u0000\u0000\u0000\u0000\u0000\uBCEB" + // 16670 - 16674 + "\u0000\u0000\u0000\u0000\u0000\uCBD1\u0000\u0000\u0000\uDBB4" + // 16675 - 16679 + "\u0000\uDBB7\u0000\uDBB6\u0000\u0000\u0000\uB4F9\u0000\u0000" + // 16680 - 16684 + "\u0000\u0000\u0000\uB5E0\u0000\u0000\u0000\uDBB3\u0000\uD3FB" + // 16685 - 16689 + "\u0000\u0000\u0000\u0000\u0000\uCAE0\u0000\uD3FD\u0000\u0000" + // 16690 - 16694 + "\u0000\u0000\u0000\u0000\u0000\uD4A1\u0000\uD3FE\u0000\u0000" + // 16695 - 16699 + "\u0000\uD4A2\u0000\u0000\u0000\uD4A3\u0000\u0000\u0000\uB7F7" + // 16700 - 16704 + "\u0000\u0000\u0000\u0000\u0000\uB1E0\u0000\uD4A4\u0000\u0000" + // 16705 - 16709 + "\u0000\u0000\u0000\uD4A6\u0000\u0000\u0000\uD4A5\u0000\u0000" + // 16710 - 16714 + "\u0000\u0000\u0000\u0000\u0000\uD4A8\u0000\u0000\u0000\u0000" + // 16715 - 16719 + "\u0000\uC5DA\u0000\uB0F8\u0000\u0000\u0000\u0000\u0000\uC3C4" + // 16720 - 16724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16725 - 16729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16730 - 16734 + "\u0000\uD3F9\u0000\u0000\u0000\uBAA4\u0000\u0000\u0000\uB0CF" + // 16735 - 16739 + "\u0000\uBFDE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16740 - 16744 + "\u0000\u0000\u0000\uD3FA\u0000\uB8C7\u0000\u0000\u0000\u0000" + // 16745 - 16749 + "\u0000\uB9F1\u0000\u0000\u0000\uD3FC\u0000\uD3F3\u0000\uD3F1" + // 16750 - 16754 + "\u0000\uD3EF\u0000\uD3F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 16755 - 16759 + "\u0000\u0000\u0000\uD3F4\u0000\u0000\u0000\uC7B9\u0000\u0000" + // 16760 - 16764 + "\u0000\u0000\u0000\u0000\u0000\uD3F5\u0000\u0000\u0000\u0000" + // 16765 - 16769 + "\u0000\uD3F6\u0000\u0000\u0000\uD3F7\u0000\u0000\u0000\u0000" + // 16770 - 16774 + "\u0000\u0000\u0000\uD3F8\u0000\uD1C5\u0000\u0000\u0000\uBCFC" + // 16775 - 16779 + "\u0000\uBBCD\u0000\u0000\u0000\u0000\u0000\uB2F3\u0000\u0000" + // 16780 - 16784 + "\u0000\uBBFD\u0000\uD9CC\u0000\u0000\u0000\u0000\u0000\u0000" + // 16785 - 16789 + "\u0000\u0000\u0000\uBBD8\u0000\uD9CD\u0000\uB0C4\u0000\u0000" + // 16790 - 16794 + "\u0000\u0000\u0000\uD9C8\u0000\u0000\u0000\u0000\u0000\u0000" + // 16795 - 16799 + "\u0000\u0000\u0000\uC4A9\u0000\u0000\u0000\u0000\u0000\u0000" + // 16800 - 16804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5F3" + // 16805 - 16809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16810 - 16814 + "\u0000\uB6B4\u0000\uD3E8\u0000\u0000\u0000\uC7B9\u0000\u0000" + // 16815 - 16819 + "\u0000\u0000\u0000\uD3EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 16820 - 16824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16825 - 16829 + "\u0000\uD3EC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16830 - 16834 + "\u0000\u0000\u0000\uD3EE\u0000\u0000\u0000\uD3ED\u0000\u0000" + // 16835 - 16839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3F0" + // 16840 - 16844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEB1\u0000\uDEB3" + // 16845 - 16849 + "\u0000\u0000\u0000\uB1BA\u0000\u0000\u0000\u0000\u0000\uB9C0" + // 16850 - 16854 + "\u0000\uCFB2\u0000\u0000\u0000\uB3BD\u0000\u0000\u0000\uC9E2" + // 16855 - 16859 + "\u008F\uC7C3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16860 - 16864 + "\u0000\uCDE1\u0000\u0000\u0000\u0000\u0000\uB3A4\u0000\uBFBB" + // 16865 - 16869 + "\u0000\uDEB5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16870 - 16874 + "\u0000\u0000\u0000\u0000\u0000\uC7FA\u0000\u0000\u0000\u0000" + // 16875 - 16879 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0A3" + // 16880 - 16884 + "\u0000\u0000\u0000\u0000\u0000\uE0A4\u0000\u0000\u0000\u0000" + // 16885 - 16889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16890 - 16894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0A5\u0000\u0000" + // 16895 - 16899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uCFE9\u0000\u0000" + // 16900 - 16904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16905 - 16909 + "\u0000\uCEB2\u0000\uB9C5\u0000\u0000\u0000\u0000\u0000\uB8A7" + // 16910 - 16914 + "\u0000\u0000\u0000\u0000\u0000\uC8A3\u0000\u0000\u0000\uE2ED" + // 16915 - 16919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16920 - 16924 + "\u008F\uCFED\u0000\u0000\u0000\uE2EF\u0000\u0000\u0000\u0000" + // 16925 - 16929 + "\u0000\u0000\u0000\uDEDC\u0000\u0000\u0000\u0000\u0000\u0000" + // 16930 - 16934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCAB\u0000\u0000" + // 16935 - 16939 + "\u0000\u0000\u0000\uDEDA\u0000\uDEDE\u0000\u0000\u0000\u0000" + // 16940 - 16944 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16945 - 16949 + "\u0000\uB8D0\u0000\u0000\u0000\uBEC5\u0000\u0000\u0000\u0000" + // 16950 - 16954 + "\u0000\uC3B9\u008F\uC7FC\u0000\u0000\u0000\u0000\u0000\uDED4" + // 16955 - 16959 + "\u0000\uD3E6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16960 - 16964 + "\u0000\u0000\u0000\uD3E5\u0000\uB3C5\u0000\u0000\u0000\u0000" + // 16965 - 16969 + "\u0000\uD3E7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16970 - 16974 + "\u0000\uD3EA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16975 - 16979 + "\u0000\uD3E9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16980 - 16984 + "\u0000\uB3FA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16985 - 16989 + "\u0000\u0000\u0000\u0000\u0000\uD9EE\u0000\u0000\u0000\uD9F2" + // 16990 - 16994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8C2\u0000\uC5EB" + // 16995 - 16999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17000 - 17004 + "\u0000\u0000\u0000\uD9EB\u0000\u0000\u0000\uD9EF\u0000\u0000" + // 17005 - 17009 + "\u0000\u0000\u0000\u0000\u0000\uB7C8\u0000\u0000\u0000\u0000" + // 17010 - 17014 + "\u0000\u0000\u0000\uBAF1\u0000\u0000\u0000\uD9A2\u0000\u0000" + // 17015 - 17019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0EF\u0000\u0000" + // 17020 - 17024 + "\u0000\u0000\u0000\u0000\u0000\uD9A3\u0000\u0000\u0000\u0000" + // 17025 - 17029 + "\u0000\u0000\u0000\uD9A4\u0000\uB5BA\u0000\uD9A5\u0000\u0000" + // 17030 - 17034 + "\u0000\uD9A6\u0000\uD9A7\u0000\uC2D7\u0000\u0000\u0000\u0000" + // 17035 - 17039 + "\u0000\u0000\u0000\uB8CD\u0000\u0000\u0000\u0000\u0000\uCCE1" + // 17040 - 17044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBBC\u0000\uD3BD" + // 17045 - 17049 + "\u0000\u0000\u0000\u0000\u0000\uD3C7\u0000\uC1B1\u0000\u0000" + // 17050 - 17054 + "\u008F\uB5E8\u0000\uD3C9\u0000\u0000\u0000\uB9A2\u0000\uD3BF" + // 17055 - 17059 + "\u0000\uC3FD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17060 - 17064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17065 - 17069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3C3\u0000\uD3BC" + // 17070 - 17074 + "\u0000\uB4AD\u0000\u0000\u0000\uB4EE\u0000\uB3E5\u0000\uD3C4" + // 17075 - 17079 + "\u0000\uD3C0\u0000\uD3B1\u0000\u0000\u0000\u0000\u0000\u0000" + // 17080 - 17084 + "\u0000\uC2EF\u0000\uD3B6\u0000\uBEA6\u0000\u0000\u0000\u0000" + // 17085 - 17089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3B3\u0000\u0000" + // 17090 - 17094 + "\u0000\u0000\u0000\uCCE4\u0000\u0000\u0000\u0000\u0000\u0000" + // 17095 - 17099 + "\u0000\uB7BC\u0000\u0000\u0000\u0000\u0000\uD3B7\u0000\uD3B8" + // 17100 - 17104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3B5" + // 17105 - 17109 + "\u0000\uD3BB\u0000\uB0A2\u0000\u0000\u0000\uD7FA\u0000\u0000" + // 17110 - 17114 + "\u0000\uD7FD\u0000\uD8A1\u008F\uF4B3\u0000\u0000\u0000\u0000" + // 17115 - 17119 + "\u0000\u0000\u0000\uBCBD\u008F\uBDF0\u0000\uD8A7\u0000\uC4F0" + // 17120 - 17124 + "\u0000\uD7FB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17125 - 17129 + "\u0000\uD8A5\u0000\u0000\u0000\uB2F9\u0000\u0000\u0000\uD8A3" + // 17130 - 17134 + "\u0000\uD8A4\u0000\u0000\u0000\u0000\u0000\uD7FE\u0000\uD8A2" + // 17135 - 17139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8E7\u0000\uB0A5" + // 17140 - 17144 + "\u0000\uC9CA\u0000\uD3A2\u0000\u0000\u0000\uD2FC\u0000\u0000" + // 17145 - 17149 + "\u0000\u0000\u0000\uD2F7\u0000\uD2FD\u0000\uBAC8\u0000\u0000" + // 17150 - 17154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17155 - 17159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17160 - 17164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3A6\u0000\u0000" + // 17165 - 17169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17170 - 17174 + "\u0000\u0000\u0000\uBFF2\u0000\u0000\u0000\u0000\u0000\u0000" + // 17175 - 17179 + "\u0000\uD6BC\u0000\u0000\u0000\u0000\u0000\uBAEA\u0000\u0000" + // 17180 - 17184 + "\u0000\u0000\u0000\uD6C2\u0000\u0000\u0000\u0000\u0000\uD6C3" + // 17185 - 17189 + "\u0000\uD6BD\u0000\uB3B3\u0000\uD6BE\u0000\uD6C7\u0000\uD6C6" + // 17190 - 17194 + "\u0000\uD6C5\u0000\uD6C1\u0000\u0000\u0000\u0000\u0000\u0000" + // 17195 - 17199 + "\u0000\uD6C0\u0000\uD2F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 17200 - 17204 + "\u0000\uD2F4\u0000\u0000\u0000\uD2F6\u0000\u0000\u0000\u0000" + // 17205 - 17209 + "\u0000\u0000\u008F\uF4A8\u0000\uBAF0\u0000\uCFC2\u0000\u0000" + // 17210 - 17214 + "\u0000\uD2EB\u0000\uD2E9\u0000\uD2F5\u0000\u0000\u0000\uD2F0" + // 17215 - 17219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17220 - 17224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uB5AA" + // 17225 - 17229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDBD\u0000\u0000" + // 17230 - 17234 + "\u0000\u0000\u0000\u0000\u0000\uDDBC\u0000\u0000\u0000\uDDBE" + // 17235 - 17239 + "\u0000\u0000\u0000\u0000\u0000\uB2CE\u0000\u0000\u0000\uC3B7" + // 17240 - 17244 + "\u0000\u0000\u0000\uDDBF\u0000\u0000\u0000\u0000\u0000\uB4BF" + // 17245 - 17249 + "\u0000\uDDC1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17250 - 17254 + "\u0000\uDDC0\u0000\u0000\u0000\uDDC2\u0000\u0000\u0000\u0000" + // 17255 - 17259 + "\u0000\u0000\u0000\uDDC3\u0000\uD2E0\u0000\u0000\u0000\uCFA4" + // 17260 - 17264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAF2\u0000\u0000" + // 17265 - 17269 + "\u0000\uC4E8\u0000\uB8E2\u0000\uB9F0\u0000\u0000\u0000\u0000" + // 17270 - 17274 + "\u0000\u0000\u0000\uD2E8\u0000\u0000\u0000\u0000\u0000\uC6DD" + // 17275 - 17279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17280 - 17284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17285 - 17289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2EC\u0000\uCBCA" + // 17290 - 17294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17295 - 17299 + "\u0000\uC8DD\u0000\u0000\u0000\u0000\u0000\uD2E6\u0000\u0000" + // 17300 - 17304 + "\u0000\uB4DE\u0000\uD2E1\u0000\uD2E2\u0000\uD2E4\u0000\u0000" + // 17305 - 17309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17310 - 17314 + "\u0000\u0000\u0000\uD2E5\u0000\u0000\u0000\uB5DB\u0000\uBFE1" + // 17315 - 17319 + "\u0000\u0000\u0000\uCAAD\u0000\uD2E3\u0000\uD2DF\u0000\uB8E3" + // 17320 - 17324 + "\u0000\u0000\u0000\uD7EC\u0000\uD7F6\u0000\uD7F4\u0000\u0000" + // 17325 - 17329 + "\u0000\u0000\u0000\uD7F1\u0000\u0000\u0000\u0000\u0000\u0000" + // 17330 - 17334 + "\u0000\uD7F0\u0000\uCEF8\u0000\u0000\u0000\uD7F2\u0000\u0000" + // 17335 - 17339 + "\u0000\u0000\u0000\uB6B2\u0000\u0000\u0000\uB9B1\u0000\u0000" + // 17340 - 17344 + "\u0000\u0000\u0000\uBDFA\u0000\u0000\u0000\u0000\u0000\u0000" + // 17345 - 17349 + "\u0000\uD7F9\u0000\uD7EB\u0000\u0000\u0000\u0000\u008F\uBDE7" + // 17350 - 17354 + "\u0000\u0000\u0000\uD7EF\u0000\uD2CE\u0000\u0000\u0000\u0000" + // 17355 - 17359 + "\u0000\u0000\u0000\u0000\u0000\uD2D0\u0000\uD2CF\u0000\u0000" + // 17360 - 17364 + "\u0000\uBFDF\u0000\uB1B9\u0000\u0000\u0000\u0000\u0000\u0000" + // 17365 - 17369 + "\u0000\uB1DE\u0000\uD2D1\u0000\u0000\u0000\uD2D2\u0000\u0000" + // 17370 - 17374 + "\u008F\uB4D0\u0000\uB8B7\u0000\u0000\u0000\u0000\u0000\uD2D3" + // 17375 - 17379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5EE" + // 17380 - 17384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4FE" + // 17385 - 17389 + "\u0000\u0000\u0000\uDCB2\u0000\u0000\u008F\uC3FC\u0000\uCCC9" + // 17390 - 17394 + "\u0000\uDBF7\u0000\uB4FD\u0000\u0000\u0000\uDBFE\u0000\u0000" + // 17395 - 17399 + "\u008F\uC3FE\u0000\u0000\u0000\u0000\u0000\uCBC0\u0000\u0000" + // 17400 - 17404 + "\u0000\uDCA1\u0000\uDCA3\u0000\u0000\u0000\uDCA7\u0000\uDBF9" + // 17405 - 17409 + "\u0000\u0000\u0000\uC3AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 17410 - 17414 + "\u0000\u0000\u0000\uC5EF\u0000\uC0EA\u0000\u0000\u0000\u0000" + // 17415 - 17419 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7B5\u0000\u0000" + // 17420 - 17424 + "\u0000\u0000\u0000\uD2C7\u0000\u0000\u0000\u0000\u0000\u0000" + // 17425 - 17429 + "\u0000\u0000\u0000\uD2C8\u0000\uB1AC\u0000\uB0F5\u0000\uB4ED" + // 17430 - 17434 + "\u008F\uB4C0\u0000\uC2A8\u0000\uB5D1\u0000\uCDF1\u0000\u0000" + // 17435 - 17439 + "\u0000\uD2CB\u0000\uB2B7\u0000\u0000\u0000\u0000\u0000\uD2CA" + // 17440 - 17444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6AA\u0000\uD2BF" + // 17445 - 17449 + "\u0000\uBDBD\u0000\u0000\u0000\uC0E9\u0000\u0000\u0000\uD2C1" + // 17450 - 17454 + "\u0000\uD2C0\u0000\uBEA3\u0000\uB8E1\u0000\uD2C3\u0000\uC8BE" + // 17455 - 17459 + "\u0000\u0000\u0000\u0000\u0000\uD2C4\u0000\u0000\u0000\u0000" + // 17460 - 17464 + "\u0000\u0000\u0000\uC8DC\u0000\uC2B4\u0000\uC2EE\u0000\uB6A8" + // 17465 - 17469 + "\u0000\u0000\u0000\u0000\u0000\uC6EE\u0000\uC3B1\u0000\u0000" + // 17470 - 17474 + "\u0000\uC7EE\u0000\u0000\u0000\uCBCE\u0000\u0000\u0000\uD2C6" + // 17475 - 17479 + "\u0000\u0000\u0000\uD7C4\u0000\uB7C1\u0000\u0000\u0000\u0000" + // 17480 - 17484 + "\u0000\u0000\u0000\uC9A7\u008F\uBCFE\u0000\u0000\u0000\uBACC" + // 17485 - 17489 + "\u0000\uC9B7\u0000\uC4A6\u0000\uC9CB\u0000\uD7C5\u0000\u0000" + // 17490 - 17494 + "\u0000\u0000\u0000\uBEB4\u0000\uB1C6\u0000\u0000\u0000\uD7C6" + // 17495 - 17499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7C7\u0000\u0000" + // 17500 - 17504 + "\u0000\uCCF2\u0000\u0000\u0000\u0000\u0000\uC8E0\u0000\u0000" + // 17505 - 17509 + "\u0000\u0000\u0000\uD7CA\u0000\uBEA2\u0000\uB6A9\u0000\u0000" + // 17510 - 17514 + "\u0000\uD2BA\u008F\uF4A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 17515 - 17519 + "\u0000\u0000\u0000\u0000\u0000\uC8DB\u0000\u0000\u0000\u0000" + // 17520 - 17524 + "\u0000\u0000\u0000\u0000\u0000\uD2BB\u0000\u0000\u0000\uD2BC" + // 17525 - 17529 + "\u0000\u0000\u0000\uD2BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 17530 - 17534 + "\u0000\u0000\u0000\uD2BE\u0000\uC9A4\u0000\uB6E8\u0000\uB0E5" + // 17535 - 17539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6BF\u008F\uB3FB" + // 17540 - 17544 + "\u0000\uCCE8\u0000\uC6F7\u0000\u0000\u0000\u0000\u0000\uCAF1" + // 17545 - 17549 + "\u0000\uD2B2\u008F\uF4A5\u0000\uD2B3\u0000\u0000\u0000\u0000" + // 17550 - 17554 + "\u0000\u0000\u0000\u0000\u0000\uD2B5\u0000\u0000\u0000\uD2B7" + // 17555 - 17559 + "\u0000\uD2B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17560 - 17564 + "\u0000\uD2B8\u0000\uB2BD\u0000\uCBCC\u0000\u0000\u0000\uBAFC" + // 17565 - 17569 + "\u0000\uD2B9\u0000\u0000\u0000\u0000\u0000\uC1D9\u0000\u0000" + // 17570 - 17574 + "\u0000\u0000\u008F\uF4BE\u0000\u0000\u008F\uC5D5\u0000\u0000" + // 17575 - 17579 + "\u0000\u0000\u0000\u0000\u0000\uC8A7\u0000\u0000\u0000\uDDAE" + // 17580 - 17584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17585 - 17589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17590 - 17594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17595 - 17599 + "\u0000\uDDB2\u0000\uDDAF\u0000\u0000\u0000\u0000\u0000\u0000" + // 17600 - 17604 + "\u0000\u0000\u0000\uB8BC\u0000\u0000\u0000\u0000\u0000\uCEA8" + // 17605 - 17609 + "\u0000\u0000\u0000\uB6CC\u0000\u0000\u0000\uB2A6\u0000\u0000" + // 17610 - 17614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17615 - 17619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB6EA" + // 17620 - 17624 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17625 - 17629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6DA" + // 17630 - 17634 + "\u0000\u0000\u0000\u0000\u0000\uB4E0\u0000\uD6DB\u0000\u0000" + // 17635 - 17639 + "\u0000\u0000\u008F\uBBF9\u0000\u0000\u0000\uD6DD\u0000\uD6DC" + // 17640 - 17644 + "\u0000\u0000\u0000\u0000\u0000\uD6DE\u0000\u0000\u0000\u0000" + // 17645 - 17649 + "\u0000\u0000\u0000\u0000\u0000\uD6DF\u0000\u0000\u0000\uC0EE" + // 17650 - 17654 + "\u0000\uBDA3\u0000\u0000\u008F\uBCE4\u0000\u0000\u0000\u0000" + // 17655 - 17659 + "\u0000\u0000\u0000\uCCEF\u0000\uB8B9\u0000\uB8CC\u0000\u0000" + // 17660 - 17664 + "\u0000\uD7B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7B9" + // 17665 - 17669 + "\u0000\u0000\u0000\uD7BF\u0000\u0000\u0000\uBCE5\u0000\u0000" + // 17670 - 17674 + "\u0000\u0000\u008F\uBCED\u0000\uC4A5\u0000\u0000\u0000\uB6AF" + // 17675 - 17679 + "\u0000\uD7BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9AB" + // 17680 - 17684 + "\u0000\u0000\u0000\uC3C6\u0000\u0000\u0000\uD7BB\u0000\u0000" + // 17685 - 17689 + "\u0000\u0000\u0000\u0000\u008F\uBCF4\u0000\u0000\u0000\u0000" + // 17690 - 17694 + "\u0000\uD7BC\u0000\u0000\u0000\uB6B0\u0000\u0000\u0000\uD7BD" + // 17695 - 17699 + "\u0000\u0000\u0000\uD7BE\u0000\u0000\u0000\u0000\u0000\uD7C0" + // 17700 - 17704 + "\u0000\u0000\u0000\uC5F6\u0000\u0000\u0000\u0000\u0000\uD7C1" + // 17705 - 17709 + "\u0000\uD7C2\u0000\u0000\u0000\uD7C3\u0000\u0000\u0000\u0000" + // 17710 - 17714 + "\u0000\uD7B4\u0000\uD7B3\u0000\u0000\u0000\u0000\u0000\uE2B3" + // 17715 - 17719 + "\u0000\uC7D6\u0000\u0000\u0000\u0000\u0000\uCBDF\u0000\u0000" + // 17720 - 17724 + "\u0000\uB1CE\u0000\u0000\u0000\uB1D7\u0000\u0000\u0000\u0000" + // 17725 - 17729 + "\u0000\uE2B4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17730 - 17734 + "\u0000\uE2B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2B5" + // 17735 - 17739 + "\u0000\uC5F0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0B9" + // 17740 - 17744 + "\u0000\uDDB9\u0000\u0000\u0000\uE2B7\u0000\uCCC1\u0000\uD2AD" + // 17745 - 17749 + "\u0000\u0000\u0000\uC0AA\u0000\uD2AA\u0000\uB6D0\u0000\u0000" + // 17750 - 17754 + "\u0000\uD2AB\u0000\uB4AB\u0000\u0000\u0000\u0000\u0000\u0000" + // 17755 - 17759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17760 - 17764 + "\u0000\u0000\u0000\u0000\u0000\uB7AE\u0000\uD2AE\u0000\u0000" + // 17765 - 17769 + "\u0000\uD2AF\u0000\u0000\u0000\u0000\u0000\uD2B0\u0000\uD2B1" + // 17770 - 17774 + "\u0000\uBCDB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8FB" + // 17775 - 17779 + "\u0000\uCCDE\u008F\uB3E5\u0000\uD2A6\u0000\u0000\u0000\uCBD6" + // 17780 - 17784 + "\u0000\u0000\u0000\uC4BC\u0000\u0000\u0000\uCDA6\u0000\u0000" + // 17785 - 17789 + "\u0000\uCAD9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2A7" + // 17790 - 17794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0D5" + // 17795 - 17799 + "\u0000\u0000\u0000\u0000\u0000\uC6B0\u0000\u0000\u0000\uD2A8" + // 17800 - 17804 + "\u0000\uB4AA\u0000\uCCB3\u0000\u0000\u008F\uB3EE\u0000\u0000" + // 17805 - 17809 + "\u0000\uBEA1\u0000\uD2A9\u0000\uCAE7\u0000\uB2C3\u0000\u0000" + // 17810 - 17814 + "\u0000\u0000\u0000\uCEF4\u0000\u0000\u0000\u0000\u008F\uB3DB" + // 17815 - 17819 + "\u0000\u0000\u0000\u0000\u0000\uBDF5\u0000\uC5D8\u0000\uB9E5" + // 17820 - 17824 + "\u0000\uD2A2\u0000\uD2A3\u0000\u0000\u008F\uB3DD\u0000\u0000" + // 17825 - 17829 + "\u0000\uCEE5\u0000\u0000\u0000\u0000\u0000\uCFAB\u0000\uD2A5" + // 17830 - 17834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8FA\u0000\u0000" + // 17835 - 17839 + "\u0000\u0000\u0000\uD2A4\u0000\u0000\u0000\uB3AF\u0000\u0000" + // 17840 - 17844 + "\u0000\uCADB\u0000\u0000\u0000\uD7B1\u0000\uCFAE\u0000\u0000" + // 17845 - 17849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7B2\u0000\uCAC0" + // 17850 - 17854 + "\u0000\uD7B5\u0000\uD0A1\u0000\uD0B1\u0000\u0000\u0000\uBCB0" + // 17855 - 17859 + "\u0000\uC6F5\u0000\uD7B6\u0000\u0000\u0000\uB5DD\u0000\uC4A4" + // 17860 - 17864 + "\u0000\uB0FA\u0000\uD7B7\u0000\uCAA6\u0000\uB9B0\u0000\u0000" + // 17865 - 17869 + "\u0000\u0000\u0000\uC3D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 17870 - 17874 + "\u0000\uC4EF\u0000\uC5E1\u0000\u0000\u0000\u0000\u0000\uBFCF" + // 17875 - 17879 + "\u0000\uD1E3\u0000\u0000\u0000\uCAAC\u0000\uC0DA\u0000\uB4A2" + // 17880 - 17884 + "\u0000\u0000\u0000\uB4A9\u0000\uD1E4\u0000\u0000\u0000\u0000" + // 17885 - 17889 + "\u0000\uD1E6\u0000\u0000\u0000\u0000\u0000\uB7BA\u0000\u0000" + // 17890 - 17894 + "\u0000\u0000\u0000\uD1E5\u008F\uB3B5\u0000\u0000\u0000\uCEF3" + // 17895 - 17899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17900 - 17904 + "\u0000\uBDE9\u0000\u0000\u0000\u0000\u0000\uDCDB\u0000\u0000" + // 17905 - 17909 + "\u0000\u0000\u0000\uDCE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 17910 - 17914 + "\u0000\u0000\u0000\uDCE8\u0000\uC8F5\u0000\uDCEE\u0000\u0000" + // 17915 - 17919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCE9" + // 17920 - 17924 + "\u0000\uDCEC\u0000\uDCE6\u0000\u0000\u0000\u0000\u0000\uC3F4" + // 17925 - 17929 + "\u0000\u0000\u0000\uC9B8\u0000\u0000\u0000\uDCDC\u0000\u0000" + // 17930 - 17934 + "\u0000\u0000\u0000\uDCE4\u0000\uBEC0\u0000\uD1DC\u0000\uCBDE" + // 17935 - 17939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDE8" + // 17940 - 17944 + "\u0000\uC2FC\u0000\u0000\u0000\uD1DE\u0000\uC6E4\u0000\u0000" + // 17945 - 17949 + "\u008F\uF4A4\u0000\uD1DF\u0000\u0000\u0000\u0000\u0000\uD1E0" + // 17950 - 17954 + "\u0000\uB3AE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1E1" + // 17955 - 17959 + "\u0000\uB6A7\u0000\u0000\u0000\uC6CC\u0000\uB1FA\u0000\uBDD0" + // 17960 - 17964 + "\u0000\u0000\u0000\u0000\u0000\uC8A1\u0000\uD1E2\u0000\u0000" + // 17965 - 17969 + "\u0000\uC4A3\u0000\u0000\u0000\uB9AD\u0000\uBEB1\u0000\u0000" + // 17970 - 17974 + "\u0000\u0000\u0000\uC8DF\u0000\u0000\u0000\u0000\u0000\uBEB2" + // 17975 - 17979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDF8" + // 17980 - 17984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17985 - 17989 + "\u0000\uC4EC\u0000\uCAF9\u0000\uC5B9\u0000\u0000\u0000\u0000" + // 17990 - 17994 + "\u0000\uB9AE\u0000\u0000\u0000\uC9DC\u0000\u0000\u0000\u0000" + // 17995 - 17999 + "\u0000\u0000\u0000\uE2F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 18000 - 18004 + "\u0000\uCACB\u0000\u0000\u0000\uC0D9\u0000\uE2F4\u0000\u0000" + // 18005 - 18009 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2F5\u0000\u0000" + // 18010 - 18014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2F3" + // 18015 - 18019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3CE" + // 18020 - 18024 + "\u0000\u0000\u0000\uE2FB\u0000\u0000\u0000\uE2FA\u0000\u0000" + // 18025 - 18029 + "\u0000\uD6FA\u0000\uD6FB\u0000\uC7D1\u0000\u0000\u0000\u0000" + // 18030 - 18034 + "\u0000\u0000\u0000\u0000\u0000\uD6FC\u0000\uCEF7\u0000\uCFAD" + // 18035 - 18039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6FE" + // 18040 - 18044 + "\u0000\uD6FD\u0000\u0000\u0000\u0000\u0000\uB3C7\u0000\u0000" + // 18045 - 18049 + "\u0000\u0000\u0000\uD7A1\u0000\u0000\u0000\u0000\u0000\u0000" + // 18050 - 18054 + "\u0000\uD7A4\u0000\uD7A5\u0000\u0000\u0000\uD7A3\u0000\u0000" + // 18055 - 18059 + "\u0000\uC9C0\u0000\uB4A7\u0000\u0000\u0000\uD1CF\u0000\u0000" + // 18060 - 18064 + "\u0000\uD1CD\u0000\uCCBD\u0000\uD1CE\u0000\u0000\u0000\uC9DA" + // 18065 - 18069 + "\u0000\uD1D0\u0000\uD1D1\u0000\uD1D2\u0000\uC5DF\u0000\u0000" + // 18070 - 18074 + "\u0000\u0000\u0000\u0000\u0000\uD1D6\u0000\uD1D4\u0000\uD1D5" + // 18075 - 18079 + "\u0000\uD1D3\u0000\uBAE3\u0000\uD1D7\u0000\uCCEA\u0000\uCEE4" + // 18080 - 18084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18085 - 18089 + "\u0000\uD1D8\u008F\uB2FC\u0000\u0000\u0000\uC9F5\u0000\uC0EC" + // 18090 - 18094 + "\u0000\u0000\u0000\uBCCD\u0000\uD5F1\u0000\uBEAD\u0000\uD5F2" + // 18095 - 18099 + "\u0000\uD5F3\u0000\uB0D3\u0000\uC2BA\u0000\uBFD2\u0000\u0000" + // 18100 - 18104 + "\u0000\uD5F4\u0000\uC6B3\u0000\uBEAE\u0000\u0000\u0000\uBEAF" + // 18105 - 18109 + "\u0000\u0000\u0000\uD5F5\u0000\u0000\u0000\u0000\u0000\uC0ED" + // 18110 - 18114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBEB0\u0000\u0000" + // 18115 - 18119 + "\u0000\u0000\u0000\u0000\u008F\uBAEB\u0000\u0000\u0000\uB2AC" + // 18120 - 18124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uBBB3" + // 18125 - 18129 + "\u0000\u0000\u0000\uC1BB\u0000\uB4E4\u0000\u0000\u0000\uD6AD" + // 18130 - 18134 + "\u0000\uCCA8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18135 - 18139 + "\u0000\uC2D2\u0000\u0000\u0000\uB3D9\u0000\u0000\u0000\u0000" + // 18140 - 18144 + "\u0000\uD6AF\u0000\uD6B1\u0000\uB4DF\u0000\u0000\u008F\uBBB8" + // 18145 - 18149 + "\u0000\uD6AE\u0000\uD6B0\u0000\u0000\u0000\uD6B3\u0000\u0000" + // 18150 - 18154 + "\u0000\uBDE4\u0000\u0000\u0000\uC1E3\u0000\u0000\u0000\uB9A9" + // 18155 - 18159 + "\u0000\uBAB8\u0000\uB9AA\u0000\uB5F0\u0000\u0000\u0000\u0000" + // 18160 - 18164 + "\u0000\uD6E0\u0000\u0000\u0000\u0000\u0000\uBAB9\u0000\u0000" + // 18165 - 18169 + "\u0000\u0000\u0000\uB8CA\u0000\uD6E1\u0000\uCCA6\u0000\uC7C3" + // 18170 - 18174 + "\u0000\uD6E2\u0000\u0000\u0000\uB9AB\u0000\u0000\u0000\u0000" + // 18175 - 18179 + "\u0000\u0000\u0000\uB4AC\u0000\u0000\u0000\uC3A7\u0000\uB6D2" + // 18180 - 18184 + "\u0000\u0000\u0000\uC8A8\u0000\uD6F1\u0000\uCABE\u0000\uD6F2" + // 18185 - 18189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18190 - 18194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18195 - 18199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4B3\u0000\uCABF" + // 18200 - 18204 + "\u0000\uC7AF\u0000\uD6F4\u0000\uD6F5\u0000\u0000\u0000\uB9AC" + // 18205 - 18209 + "\u0000\uB4B4\u0000\uD6F6\u0000\uB8B8\u0000\uCDC4\u0000\uCDA9" + // 18210 - 18214 + "\u0000\uB4F6\u0000\uD6F8\u0000\uD1C3\u0000\u0000\u0000\uD1C4" + // 18215 - 18219 + "\u0000\u0000\u0000\u0000\u0000\uC6E2\u0000\uB1DF\u0000\u0000" + // 18220 - 18224 + "\u0000\u0000\u0000\uD1C7\u0000\uBAFD\u0000\u0000\u0000\uD1C6" + // 18225 - 18229 + "\u0000\uBAC6\u0000\u0000\u0000\uD1C8\u0000\uE6EE\u0000\uD1C9" + // 18230 - 18234 + "\u0000\uCBC1\u0000\uD1CA\u0000\u0000\u0000\uD1CB\u0000\uD1CC" + // 18235 - 18239 + "\u0000\uBEE9\u0000\u0000\u0000\uBCCC\u0000\u0000\u0000\u0000" + // 18240 - 18244 + "\u0000\u0000\u008F\uB2F5\u0000\u0000\u0000\u0000\u008F\uC4EA" + // 18245 - 18249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCE1" + // 18250 - 18254 + "\u0000\uDCDA\u0000\u0000\u0000\u0000\u0000\uDCE7\u0000\u0000" + // 18255 - 18259 + "\u0000\uDCE5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18260 - 18264 + "\u0000\uDCE0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18265 - 18269 + "\u0000\u0000\u0000\u0000\u0000\uDCDF\u0000\u0000\u0000\uC4D0" + // 18270 - 18274 + "\u0000\u0000\u0000\uC1E5\u0000\u0000\u0000\uDCDD\u0000\uD1BA" + // 18275 - 18279 + "\u0000\uB0F4\u0000\u0000\u0000\uB8B5\u0000\uB7BB\u0000\uBDBC" + // 18280 - 18284 + "\u0000\uC3FB\u0000\uB6A4\u0000\uC0E8\u0000\uB8F7\u008F\uB2E6" + // 18285 - 18289 + "\u0000\uB9EE\u0000\uD1BC\u0000\uCCC8\u0000\uC5C6\u0000\u0000" + // 18290 - 18294 + "\u0000\uBBF9\u0000\u0000\u0000\uD1BB\u0000\u0000\u0000\uD1BD" + // 18295 - 18299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18300 - 18304 + "\u0000\uC5DE\u0000\u0000\u0000\uB3F5\u0000\u0000\u0000\u0000" + // 18305 - 18309 + "\u0000\u0000\u0000\uB7E2\u0000\u0000\u0000\u0000\u0000\u0000" + // 18310 - 18314 + "\u0000\u0000\u0000\uD9FD\u0000\u0000\u0000\u0000\u0000\u0000" + // 18315 - 18319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18320 - 18324 + "\u0000\u0000\u0000\uBBB5\u0000\uD9FA\u0000\u0000\u0000\uD9F9" + // 18325 - 18329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7B2" + // 18330 - 18334 + "\u0000\u0000\u0000\u0000\u008F\uC0F4\u0000\uC6B5\u0000\u0000" + // 18335 - 18339 + "\u0000\uB2C9\u0000\uD5EA\u0000\u0000\u0000\uD5E8\u0000\uD5EC" + // 18340 - 18344 + "\u0000\uD5E9\u0000\uC7AB\u0000\uDCCD\u0000\uBFB3\u0000\u0000" + // 18345 - 18349 + "\u0000\uD5ED\u008F\uF4AE\u0000\u0000\u0000\uCEC0\u0000\u0000" + // 18350 - 18354 + "\u0000\uD5EE\u0000\u0000\u0000\u0000\u0000\uD5F0\u0000\u0000" + // 18355 - 18359 + "\u0000\uC3FE\u0000\uD5EF\u0000\u0000\u0000\uC0A3\u0000\u0000" + // 18360 - 18364 + "\u0000\uBBFB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC2D0" + // 18365 - 18369 + "\u0000\uBCF7\u0000\uB5B7\u0000\uD1AE\u0000\uD1AF\u0000\u0000" + // 18370 - 18374 + "\u0000\uB2AF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18375 - 18379 + "\u0000\uD1AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18380 - 18384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCF4" + // 18385 - 18389 + "\u0000\u0000\u0000\uD1B2\u0000\uD1B1\u0000\uD1B0\u0000\u0000" + // 18390 - 18394 + "\u0000\uD0D6\u0000\u0000\u0000\uD1B3\u0000\u0000\u0000\u0000" + // 18395 - 18399 + "\u0000\u0000\u0000\u0000\u0000\uBDFE\u0000\uD0FA\u0000\u0000" + // 18400 - 18404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0FC\u0000\u0000" + // 18405 - 18409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18410 - 18414 + "\u0000\u0000\u0000\uCBB5\u0000\u0000\u0000\u0000\u0000\u0000" + // 18415 - 18419 + "\u0000\uB7E6\u0000\u0000\u0000\u0000\u008F\uB2A3\u0000\u0000" + // 18420 - 18424 + "\u0000\u0000\u0000\u0000\u0000\uBBB1\u0000\uC8F7\u0000\uD0FB" + // 18425 - 18429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18430 - 18434 + "\u0000\uB7F2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18435 - 18439 + "\u0000\u0000\u0000\u0000\u0000\uD0F8\u0000\u0000\u0000\u0000" + // 18440 - 18444 + "\u0000\u0000\u008F\uB1F4\u0000\u0000\u0000\uBCC5\u0000\u0000" + // 18445 - 18449 + "\u0000\uC2A6\u0000\uC4E5\u0000\uB6F6\u0000\u0000\u0000\uD0F9" + // 18450 - 18454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5B6" + // 18455 - 18459 + "\u0000\u0000\u0000\u0000\u0000\uDAF1\u0000\u0000\u0000\uDAED" + // 18460 - 18464 + "\u008F\uF4B7\u0000\uB3A2\u0000\uDAEE\u0000\uDAEF\u0000\uC8D5" + // 18465 - 18469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9E1" + // 18470 - 18474 + "\u0000\uB7CA\u0000\uDAF2\u0000\u0000\u0000\u0000\u008F\uC2C4" + // 18475 - 18479 + "\u0000\uC0B2\u0000\u0000\u0000\uBEBD\u0000\u0000\u0000\u0000" + // 18480 - 18484 + "\u0000\u0000\u0000\uC3D2\u0000\u0000\u0000\u0000\u0000\u0000" + // 18485 - 18489 + "\u0000\u0000\u0000\u0000\u0000\uE2EB\u0000\u0000\u0000\u0000" + // 18490 - 18494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18495 - 18499 + "\u0000\u0000\u008F\uCFE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 18500 - 18504 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18505 - 18509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18510 - 18514 + "\u0000\u0000\u0000\uBECB\u0000\u0000\u0000\u0000\u0000\uDBD2" + // 18515 - 18519 + "\u0000\u0000\u0000\uDBCF\u0000\u0000\u0000\u0000\u0000\uDBD7" + // 18520 - 18524 + "\u0000\u0000\u0000\uDBCD\u0000\u0000\u0000\u0000\u0000\uDBCB" + // 18525 - 18529 + "\u0000\u0000\u0000\uDBD3\u0000\uDBC9\u0000\u0000\u0000\uC3EC" + // 18530 - 18534 + "\u0000\u0000\u0000\uCCF8\u0000\uBCC6\u0000\uBAF4\u0000\u0000" + // 18535 - 18539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBABA" + // 18540 - 18544 + "\u0000\u0000\u0000\u0000\u0000\uCBEF\u0000\uB3C1\u008F\uB1DF" + // 18545 - 18549 + "\u0000\u0000\u008F\uB1E1\u0000\uD0F0\u0000\u0000\u0000\u0000" + // 18550 - 18554 + "\u008F\uB1E3\u0000\uD0F1\u0000\uD0F5\u0000\uB0CE\u0000\u0000" + // 18555 - 18559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAD0" + // 18560 - 18564 + "\u0000\uD0F4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18565 - 18569 + "\u0000\uD0F3\u0000\uD0F7\u0000\u0000\u0000\u0000\u0000\u0000" + // 18570 - 18574 + "\u0000\uD0F6\u0000\u0000\u0000\uC4E4\u0000\u0000\u0000\u0000" + // 18575 - 18579 + "\u0000\u0000\u0000\uD8F0\u0000\u0000\u0000\u0000\u0000\uD8EF" + // 18580 - 18584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18585 - 18589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18590 - 18594 + "\u0000\u0000\u0000\uC4A8\u0000\u0000\u0000\uD8F3\u0000\u0000" + // 18595 - 18599 + "\u0000\uD8F1\u0000\uD8E7\u0000\uB7FC\u0000\u0000\u0000\uD8F2" + // 18600 - 18604 + "\u0000\u0000\u0000\uD8F6\u0000\uD8F5\u0000\uD8F7\u0000\uD8F4" + // 18605 - 18609 + "\u0000\uB6A2\u0000\uBFAE\u0000\u0000\u0000\uCBF3\u0000\uD0DF" + // 18610 - 18614 + "\u0000\uD0E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18615 - 18619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDA4" + // 18620 - 18624 + "\u0000\uD0ED\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7D0" + // 18625 - 18629 + "\u0000\u0000\u0000\uC9B6\u0000\uD0E8\u0000\u0000\u0000\uCAF0" + // 18630 - 18634 + "\u0000\u0000\u0000\uB2B6\u0000\u0000\u0000\u0000\u0000\u0000" + // 18635 - 18639 + "\u0000\uD0EC\u008F\uB1C8\u008F\uB0D2\u0000\uB4EB\u0000\u0000" + // 18640 - 18644 + "\u008F\uB0D4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18645 - 18649 + "\u0000\u0000\u0000\uD0C4\u0000\uB0CB\u0000\u0000\u0000\u0000" + // 18650 - 18654 + "\u0000\uB8E0\u0000\uB4EC\u0000\uC9FA\u0000\uC8B2\u0000\uB5D9" + // 18655 - 18659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18660 - 18664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2F1\u0000\u0000" + // 18665 - 18669 + "\u0000\uD0E7\u0000\uC5C1\u0000\u0000\u0000\u0000\u0000\uB4BA" + // 18670 - 18674 + "\u0000\uBBB6\u0000\u0000\u0000\u0000\u0000\uC6D8\u0000\u0000" + // 18675 - 18679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7C9" + // 18680 - 18684 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFF4\u0000\u0000" + // 18685 - 18689 + "\u0000\uDACA\u0000\u0000\u0000\uC0B0\u0000\uC5A8\u0000\u0000" + // 18690 - 18694 + "\u0000\uC9DF\u0000\uDACB\u0000\u0000\u0000\u0000\u0000\u0000" + // 18695 - 18699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDAF" + // 18700 - 18704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDED7\u0000\u0000" + // 18705 - 18709 + "\u0000\u0000\u0000\uDED0\u0000\uC5F2\u0000\u0000\u0000\u0000" + // 18710 - 18714 + "\u0000\uDED3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDED9" + // 18715 - 18719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18720 - 18724 + "\u0000\u0000\u0000\u0000\u0000\uCFD1\u0000\uBCBE\u0000\uBDBA" + // 18725 - 18729 + "\u0000\uBFCE\u0000\uD0BE\u0000\u0000\u0000\uD0BC\u0000\u0000" + // 18730 - 18734 + "\u0000\uD0BD\u0000\uB5D8\u0000\u0000\u0000\u0000\u0000\uBAA3" + // 18735 - 18739 + "\u0000\uB2F0\u0000\u0000\u0000\uD0BB\u0000\uD0BA\u0000\uCAA9" + // 18740 - 18744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBC6" + // 18745 - 18749 + "\u0000\uBBC5\u0000\uC2BE\u0000\uD0BF\u0000\uC9D5\u0000\uC0E7" + // 18750 - 18754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1B8\u0000\uD0C0" + // 18755 - 18759 + "\u0000\uD0C2\u0000\uD0B5\u0000\uCBB4\u0000\uD0B6\u0000\u0000" + // 18760 - 18764 + "\u0000\uB8F2\u0000\uB0E7\u0000\uCBF2\u0000\u0000\u0000\uB5FC" + // 18765 - 18769 + "\u0000\u0000\u0000\u0000\u0000\uB5FD\u0000\uB5FE\u0000\uC4E2" + // 18770 - 18774 + "\u0000\uCEBC\u0000\u0000\u0000\uD0B7\u0000\u0000\u0000\u0000" + // 18775 - 18779 + "\u0000\uD0B8\u0000\u0000\u0000\u0000\u0000\uD0B9\u0000\u0000" + // 18780 - 18784 + "\u0000\u0000\u0000\u0000\u0000\uBFCD\u0000\u0000\u0000\u0000" + // 18785 - 18789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0E6\u0000\uD0EF" + // 18790 - 18794 + "\u0000\u0000\u0000\u0000\u0000\uC1D2\u0000\u0000\u0000\uB8C4" + // 18795 - 18799 + "\u0000\u0000\u0000\uC7DC\u0000\u0000\u0000\uE0C7\u0000\u0000" + // 18800 - 18804 + "\u0000\uD0EE\u0000\uC5DD\u0000\u0000\u0000\uD0E3\u0000\u0000" + // 18805 - 18809 + "\u0000\uB8F6\u0000\u0000\u0000\u0000\u0000\uB8F5\u0000\uD0E1" + // 18810 - 18814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uB1D7\u0000\uBCDA" + // 18815 - 18819 + "\u0000\uB5B5\u0000\u0000\u0000\uD0AC\u0000\u0000\u0000\u0000" + // 18820 - 18824 + "\u0000\uD0AD\u0000\uCEBB\u0000\u0000\u0000\uCDBD\u0000\uC1E8" + // 18825 - 18829 + "\u0000\uD0AF\u0000\uBBF6\u0000\uC6F3\u0000\u0000\u0000\uD0B2" + // 18830 - 18834 + "\u0000\u0000\u0000\u0000\u0000\uB1BE\u0000\uB8DF\u0000\u0000" + // 18835 - 18839 + "\u0000\uB8DE\u0000\uB0E6\u0000\u0000\u0000\u0000\u0000\uCFCB" + // 18840 - 18844 + "\u0000\uCFCA\u0000\u0000\u0000\uBAB3\u0000\uB0A1\u0000\u0000" + // 18845 - 18849 + "\u0000\uD0B3\u0000\uD0B4\u0000\uB0EC\u0000\uC3FA\u0000\u0000" + // 18850 - 18854 + "\u0000\uBCB7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBFC" + // 18855 - 18859 + "\u0000\uBEE6\u0000\uBBB0\u0000\uBEE5\u0000\uB2BC\u0000\u0000" + // 18860 - 18864 + "\u0000\uC9D4\u0000\uCDBF\u0000\u0000\u0000\uD0A2\u0000\uB1AF" + // 18865 - 18869 + "\u0000\u0000\u0000\u0000\u0000\uB3EE\u0000\uD0A3\u0000\uC0A4" + // 18870 - 18874 + "\u0000\uD2C2\u0000\uB5D6\u0000\uCABA\u0000\u0000\u0000\u0000" + // 18875 - 18879 + "\u0000\u0000\u0000\u0000\u0000\uBEE7\u0000\u0000\u0000\uC3E4" + // 18880 - 18884 + "\u0000\u0000\u0000\uD5C1\u0000\u0000\u0000\u0000\u0000\uD5C3" + // 18885 - 18889 + "\u0000\u0000\u0000\u0000\u0000\uD5C4\u0000\u0000\u0000\u0000" + // 18890 - 18894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18895 - 18899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18900 - 18904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5C6" + // 18905 - 18909 + "\u0000\uD5C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18910 - 18914 + "\u0000\uDCED\u0000\u0000\u008F\uC5A7\u0000\uDCF2\u0000\uDCF6" + // 18915 - 18919 + "\u0000\u0000\u0000\u0000\u0000\uB6B6\u0000\u0000\u0000\u0000" + // 18920 - 18924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18925 - 18929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18930 - 18934 + "\u0000\uB5CC\u0000\uDCF4\u0000\u0000\u0000\u0000\u0000\u0000" + // 18935 - 18939 + "\u0000\u0000\u0000\u0000\u0000\uB5A1\u0000\uA5E0\u0000\uA5E1" + // 18940 - 18944 + "\u0000\uA5E2\u0000\uA5E3\u0000\uA5E4\u0000\uA5E5\u0000\uA5E6" + // 18945 - 18949 + "\u0000\uA5E7\u0000\uA5E8\u0000\uA5E9\u0000\uA5EA\u0000\uA5EB" + // 18950 - 18954 + "\u0000\uA5EC\u0000\uA5ED\u0000\uA5EE\u0000\uA5EF\u0000\uA5F0" + // 18955 - 18959 + "\u0000\uA5F1\u0000\uA5F2\u0000\uA5F3\u0000\uA5F4\u0000\uA5F5" + // 18960 - 18964 + "\u0000\uA5F6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18965 - 18969 + "\u0000\uA1A6\u0000\uA1BC\u0000\uA1B3\u0000\uA1B4\u0000\u0000" + // 18970 - 18974 + "\u0000\uB2C7\u0000\uD5BF\u0000\u0000\u0000\u0000\u0000\u0000" + // 18975 - 18979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBCBB\u0000\u0000" + // 18980 - 18984 + "\u0000\uD5BE\u0000\uB7F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 18985 - 18989 + "\u0000\uD5CC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18990 - 18994 + "\u0000\u0000\u0000\uD5C5\u0000\uD5C2\u0000\u0000\u0000\u0000" + // 18995 - 18999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19000 - 19004 + "\u0000\u0000\u0000\u0000\u0000\uEFCF\u0000\u0000\u0000\u0000" + // 19005 - 19009 + "\u0000\uEEE5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19010 - 19014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEEB\u0000\u0000" + // 19015 - 19019 + "\u0000\u0000\u0000\uB8DA\u0000\u0000\u008F\uE3D4\u008F\uE3D5" + // 19020 - 19024 + "\u0000\u0000\u008F\uE3D6\u0000\u0000\u0000\u0000\u0000\uEEEF" + // 19025 - 19029 + "\u0000\uA5C0\u0000\uA5C1\u0000\uA5C2\u0000\uA5C3\u0000\uA5C4" + // 19030 - 19034 + "\u0000\uA5C5\u0000\uA5C6\u0000\uA5C7\u0000\uA5C8\u0000\uA5C9" + // 19035 - 19039 + "\u0000\uA5CA\u0000\uA5CB\u0000\uA5CC\u0000\uA5CD\u0000\uA5CE" + // 19040 - 19044 + "\u0000\uA5CF\u0000\uA5D0\u0000\uA5D1\u0000\uA5D2\u0000\uA5D3" + // 19045 - 19049 + "\u0000\uA5D4\u0000\uA5D5\u0000\uA5D6\u0000\uA5D7\u0000\uA5D8" + // 19050 - 19054 + "\u0000\uA5D9\u0000\uA5DA\u0000\uA5DB\u0000\uA5DC\u0000\uA5DD" + // 19055 - 19059 + "\u0000\uA5DE\u0000\uA5DF\u0000\uA4E0\u0000\uA4E1\u0000\uA4E2" + // 19060 - 19064 + "\u0000\uA4E3\u0000\uA4E4\u0000\uA4E5\u0000\uA4E6\u0000\uA4E7" + // 19065 - 19069 + "\u0000\uA4E8\u0000\uA4E9\u0000\uA4EA\u0000\uA4EB\u0000\uA4EC" + // 19070 - 19074 + "\u0000\uA4ED\u0000\uA4EE\u0000\uA4EF\u0000\uA4F0\u0000\uA4F1" + // 19075 - 19079 + "\u0000\uA4F2\u0000\uA4F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 19080 - 19084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1AB" + // 19085 - 19089 + "\u0000\uA1AC\u0000\uA1B5\u0000\uA1B6\u0000\u0000\u0000\uB0D2" + // 19090 - 19094 + "\u0000\u0000\u0000\uB0A3\u0000\u0000\u0000\u0000\u0000\u0000" + // 19095 - 19099 + "\u0000\u0000\u0000\u0000\u0000\uD5B2\u0000\u0000\u0000\u0000" + // 19100 - 19104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19105 - 19109 + "\u0000\uD5B0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19110 - 19114 + "\u0000\u0000\u0000\u0000\u0000\uCCBC\u0000\u0000\u0000\uD5B3" + // 19115 - 19119 + "\u0000\u0000\u0000\uD5B1\u0000\u0000\u0000\u0000\u0000\uD5AF" + // 19120 - 19124 + "\u0000\uA4C0\u0000\uA4C1\u0000\uA4C2\u0000\uA4C3\u0000\uA4C4" + // 19125 - 19129 + "\u0000\uA4C5\u0000\uA4C6\u0000\uA4C7\u0000\uA4C8\u0000\uA4C9" + // 19130 - 19134 + "\u0000\uA4CA\u0000\uA4CB\u0000\uA4CC\u0000\uA4CD\u0000\uA4CE" + // 19135 - 19139 + "\u0000\uA4CF\u0000\uA4D0\u0000\uA4D1\u0000\uA4D2\u0000\uA4D3" + // 19140 - 19144 + "\u0000\uA4D4\u0000\uA4D5\u0000\uA4D6\u0000\uA4D7\u0000\uA4D8" + // 19145 - 19149 + "\u0000\uA4D9\u0000\uA4DA\u0000\uA4DB\u0000\uA4DC\u0000\uA4DD" + // 19150 - 19154 + "\u0000\uA4DE\u0000\uA4DF\u0000\uA1A1\u0000\uA1A2\u0000\uA1A3" + // 19155 - 19159 + "\u0000\uA1B7\u0000\u0000\u0000\uA1B9\u0000\uA1BA\u0000\uA1BB" + // 19160 - 19164 + "\u0000\uA1D2\u0000\uA1D3\u0000\uA1D4\u0000\uA1D5\u0000\uA1D6" + // 19165 - 19169 + "\u0000\uA1D7\u0000\uA1D8\u0000\uA1D9\u0000\uA1DA\u0000\uA1DB" + // 19170 - 19174 + "\u0000\uA2A9\u0000\uA2AE\u0000\uA1CC\u0000\uA1CD\u0000\u0000" + // 19175 - 19179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19180 - 19184 + "\u0000\uA1C1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFB4" + // 19185 - 19189 + "\u0000\u0000\u0000\uC9AC\u0000\u0000\u0000\u0000\u0000\u0000" + // 19190 - 19194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4F7\u0000\uC7A6" + // 19195 - 19199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19200 - 19204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD7D6\u0000\uBBD6" + // 19205 - 19209 + "\u0000\uCBBA\u0000\uCBBB\u0000\u0000\u0000\u0000\u0000\uB1FE" + // 19210 - 19214 + "\u0000\uD7DB\u008F\uBDC2\u0000\u0000\u0000\uD5A2\u0000\uC7A1" + // 19215 - 19219 + "\u0000\uC8DE\u0000\uCCD1\u0000\u0000\u0000\u0000\u0000\u0000" + // 19220 - 19224 + "\u0000\u0000\u0000\u0000\u0000\uC7A5\u0000\u0000\u0000\u0000" + // 19225 - 19229 + "\u0000\uD5AB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19230 - 19234 + "\u0000\u0000\u0000\uB5B8\u0000\u0000\u0000\u0000\u0000\uCDC5" + // 19235 - 19239 + "\u0000\u0000\u0000\u0000\u0000\uCCAF\u0000\u0000\u0000\uD6AC" + // 19240 - 19244 + "\u0000\u0000\u0000\uD5A3\u0000\u0000\u0000\u0000\u0000\uCEA1" + // 19245 - 19249 + "\u0000\uE1DC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19250 - 19254 + "\u0000\u0000\u0000\uC1E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 19255 - 19259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1E2\u0000\u0000" + // 19260 - 19264 + "\u0000\uE1E4\u0000\uE1E5\u0000\uC3D4\u0000\u0000\u0000\u0000" + // 19265 - 19269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1E3\u0000\u0000" + // 19270 - 19274 + "\u0000\uE1E0\u0000\u0000\u0000\uE1DE\u0000\uE1DF\u0000\uA1EA" + // 19275 - 19279 + "\u0000\u0000\u0000\uA1E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 19280 - 19284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19285 - 19289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19290 - 19294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19295 - 19299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19300 - 19304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19305 - 19309 + "\u0000\u0000\u008F\uF4EB\u0000\u0000\u0000\u0000\u0000\u0000" + // 19310 - 19314 + "\u0000\uB5A8\u0000\uB8C9\u0000\uD5D7\u0000\uB3D8\u0000\u0000" + // 19315 - 19319 + "\u0000\u0000\u0000\uD5D8\u0000\u0000\u0000\uC2B9\u0000\u0000" + // 19320 - 19324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5D9\u0000\uD6A3" + // 19325 - 19329 + "\u0000\u0000\u0000\uD5DA\u0000\u0000\u0000\uD5DB\u0000\u0000" + // 19330 - 19334 + "\u0000\u0000\u0000\uD5DC\u0000\u0000\u0000\uD5DE\u0000\u0000" + // 19335 - 19339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB8CE" + // 19340 - 19344 + "\u0000\u0000\u0000\u0000\u0000\uDAC3\u0000\u0000\u0000\u0000" + // 19345 - 19349 + "\u0000\u0000\u0000\u0000\u0000\uDAC6\u008F\uF4B5\u0000\uC9D2" + // 19350 - 19354 + "\u0000\u0000\u0000\uB5DF\u0000\u0000\u0000\u0000\u0000\u0000" + // 19355 - 19359 + "\u0000\uDAC5\u0000\uDAC4\u0000\uC7D4\u0000\uDAC7\u0000\uB6B5" + // 19360 - 19364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAC9\u0000\uDAC8" + // 19365 - 19369 + "\u0000\u0000\u0000\uC5B6\u0000\uD4C9\u0000\u0000\u0000\u0000" + // 19370 - 19374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1F6" + // 19375 - 19379 + "\u0000\u0000\u0000\uC5B6\u0000\u0000\u0000\u0000\u0000\u0000" + // 19380 - 19384 + "\u0000\u0000\u0000\uD4CB\u0000\u0000\u0000\uD4C7\u0000\u0000" + // 19385 - 19389 + "\u0000\u0000\u0000\uBFD0\u0000\u0000\u0000\u0000\u0000\u0000" + // 19390 - 19394 + "\u0000\uD4CF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19395 - 19399 + "\u0000\uBDCE\u0000\u0000\u0000\uCAC9\u0000\u0000\u0000\u0000" + // 19400 - 19404 + "\u0000\u0000\u0000\uD4D9\u0000\u0000\u0000\uC3C5\u0000\u0000" + // 19405 - 19409 + "\u0000\u0000\u0000\uB2F5\u0000\u0000\u0000\uBEED\u0000\u0000" + // 19410 - 19414 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4DB\u0000\u0000" + // 19415 - 19419 + "\u0000\uD4DA\u0000\u0000\u0000\uB9E8\u0000\u0000\u0000\uD4DC" + // 19420 - 19424 + "\u0000\uD4DE\u0000\uD4DD\u0000\u0000\u0000\u0000\u0000\uD4E0" + // 19425 - 19429 + "\u0000\u0000\u0000\uD4D5\u0000\uD4E2\u0000\uA2A3\u0000\uA2A2" + // 19430 - 19434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19435 - 19439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19440 - 19444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19445 - 19449 + "\u0000\u0000\u0000\uA2A5\u0000\uA2A4\u0000\u0000\u0000\u0000" + // 19450 - 19454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19455 - 19459 + "\u0000\u0000\u0000\uA2A7\u0000\uA2A6\u0000\u0000\u0000\u0000" + // 19460 - 19464 + "\u0000\uDAB9\u0000\uDABB\u0000\uDABA\u0000\u0000\u0000\u0000" + // 19465 - 19469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9F8\u0000\uDABC" + // 19470 - 19474 + "\u0000\uDAB0\u0000\u0000\u0000\u0000\u0000\uBBD9\u0000\u0000" + // 19475 - 19479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDABD\u0000\uDABE" + // 19480 - 19484 + "\u0000\uDAC0\u0000\uDABF\u0000\uDAC1\u0000\uB2FE\u0000\u0000" + // 19485 - 19489 + "\u0000\uB9B6\u0000\u0000\u0000\u0000\u0000\uCAFC\u0000\uC0AF" + // 19490 - 19494 + "\u0000\uA8B7\u0000\u0000\u0000\u0000\u0000\uA8B2\u0000\uA8A9" + // 19495 - 19499 + "\u0000\uA8BE\u0000\u0000\u0000\u0000\u0000\uA8B9\u0000\u0000" + // 19500 - 19504 + "\u0000\u0000\u0000\uA8B4\u0000\uA8A8\u0000\u0000\u0000\u0000" + // 19505 - 19509 + "\u0000\uA8B8\u0000\uA8BD\u0000\u0000\u0000\u0000\u0000\uA8B3" + // 19510 - 19514 + "\u0000\uA8AA\u0000\u0000\u0000\u0000\u0000\uA8BA\u0000\uA8BF" + // 19515 - 19519 + "\u0000\u0000\u0000\u0000\u0000\uA8B5\u0000\uA8AB\u0000\u0000" + // 19520 - 19524 + "\u0000\u0000\u0000\uA8BB\u0000\uA8A1\u0000\uA8AC\u0000\uA8A2" + // 19525 - 19529 + "\u0000\uA8AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19530 - 19534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA8A3" + // 19535 - 19539 + "\u0000\u0000\u0000\u0000\u0000\uA8AE\u0000\uA8A4\u0000\u0000" + // 19540 - 19544 + "\u0000\u0000\u0000\uA8AF\u0000\uA8A6\u0000\u0000\u0000\u0000" + // 19545 - 19549 + "\u0000\uA8B1\u0000\uA8A5\u0000\u0000\u0000\u0000\u0000\uA8B0" + // 19550 - 19554 + "\u0000\uA8A7\u0000\uA8BC\u0000\u0000\u0000\u0000\u0000\uD9C4" + // 19555 - 19559 + "\u0000\u0000\u0000\u0000\u0000\uC3B4\u0000\uD9BE\u0000\uD9C5" + // 19560 - 19564 + "\u0000\uD9C0\u0000\uD9C7\u0000\uD9C3\u0000\u0000\u0000\uD9C2" + // 19565 - 19569 + "\u0000\uC7EF\u0000\u0000\u0000\uD9BC\u0000\uB2FD\u0000\uD9BA" + // 19570 - 19574 + "\u0000\uB5F1\u0000\uC2F3\u0000\uD9B6\u0000\u0000\u0000\u0000" + // 19575 - 19579 + "\u0000\uD9B9\u0000\uB9B4\u0000\uC0DB\u0000\u0000\u0000\uBEB7" + // 19580 - 19584 + "\u0000\uD9C1\u0000\uC7D2\u0000\u0000\u0000\u0000\u0000\uC0DD" + // 19585 - 19589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19590 - 19594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19595 - 19599 + "\u0000\u0000\u0000\uD9F7\u0000\u0000\u0000\u0000\u0000\uC4CF" + // 19600 - 19604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19605 - 19609 + "\u0000\u0000\u0000\uC5A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 19610 - 19614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAFB" + // 19615 - 19619 + "\u0000\uBCCE\u0000\uD9E0\u0000\u0000\u0000\uD9DF\u0000\u0000" + // 19620 - 19624 + "\u0000\u0000\u0000\uBFF8\u0000\u0000\u0000\u0000\u0000\u0000" + // 19625 - 19629 + "\u0000\uB7FE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9D9" + // 19630 - 19634 + "\u0000\uBEB9\u0000\u0000\u0000\u0000\u0000\uC6E8\u0000\uC7B1" + // 19635 - 19639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDE1" + // 19640 - 19644 + "\u0000\u0000\u0000\uDFB3\u0000\u0000\u0000\u0000\u0000\u0000" + // 19645 - 19649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFAC\u0000\uC4AC" + // 19650 - 19654 + "\u0000\uDFA9\u0000\uC4D9\u0000\u0000\u0000\u0000\u0000\u0000" + // 19655 - 19659 + "\u0000\uDFCC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFA6" + // 19660 - 19664 + "\u0000\u0000\u0000\uDFA5\u0000\u0000\u0000\uDFAE\u0000\u0000" + // 19665 - 19669 + "\u0000\u0000\u0000\u0000\u0000\uB3F6\u0000\uE3E2\u0000\uE3E1" + // 19670 - 19674 + "\u0000\u0000\u0000\uE3E5\u0000\uE3DE\u0000\u0000\u0000\uE3E6" + // 19675 - 19679 + "\u0000\uCEA9\u0000\u0000\u0000\uE3E7\u0000\u0000\u0000\uE3E8" + // 19680 - 19684 + "\u0000\u0000\u008F\uD1E8\u0000\uD4F4\u0000\uE3EA\u0000\u0000" + // 19685 - 19689 + "\u0000\uE3E9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE3EB" + // 19690 - 19694 + "\u0000\uE3EC\u0000\u0000\u0000\uCEB5\u0000\uE3ED\u0000\u0000" + // 19695 - 19699 + "\u0000\uF0EF\u0000\uA1E2\u0000\uA2E1\u0000\u0000\u0000\u0000" + // 19700 - 19704 + "\u0000\u0000\u0000\u0000\u0000\uA1E5\u0000\uA1E6\u0000\u0000" + // 19705 - 19709 + "\u0000\u0000\u0000\uA2E3\u0000\uA2E4\u0000\u0000\u0000\u0000" + // 19710 - 19714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19715 - 19719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19720 - 19724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19725 - 19729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7DE\u0000\u0000" + // 19730 - 19734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19735 - 19739 + "\u0000\u0000\u0000\uD5BB\u0000\uC9B2\u0000\u0000\u0000\u0000" + // 19740 - 19744 + "\u0000\u0000\u0000\u0000\u0000\uCAB0\u0000\u0000\u0000\u0000" + // 19745 - 19749 + "\u0000\uC6B4\u0000\u0000\u0000\uB7C6\u0000\u0000\u0000\uD8E2" + // 19750 - 19754 + "\u0000\uD8DD\u0000\u0000\u0000\uD8E3\u0000\u0000\u0000\u0000" + // 19755 - 19759 + "\u0000\u0000\u0000\uB7FB\u0000\u0000\u0000\u0000\u0000\u0000" + // 19760 - 19764 + "\u0000\uB2B1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8EB" + // 19765 - 19769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4B8\u0000\u0000" + // 19770 - 19774 + "\u0000\uD4C8\u0000\u0000\u0000\u0000\u0000\uC4E9\u0000\u0000" + // 19775 - 19779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB4AE" + // 19780 - 19784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF4A1" + // 19785 - 19789 + "\u0000\uB1E1\u0000\uCAF3\u0000\u0000\u0000\u0000\u0000\uBEEC" + // 19790 - 19794 + "\u0000\uC5C8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19795 - 19799 + "\u0000\uBAE6\u0000\u0000\u0000\u0000\u0000\uD4CE\u0000\u0000" + // 19800 - 19804 + "\u0000\u0000\u0000\uDFE9\u0000\u0000\u0000\uC7E1\u0000\uDFE5" + // 19805 - 19809 + "\u0000\uDFE8\u0000\uBEC8\u0000\u0000\u0000\uC8D1\u0000\u0000" + // 19810 - 19814 + "\u0000\u0000\u0000\uDFEC\u0000\u0000\u0000\uBCD1\u0000\u0000" + // 19815 - 19819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19820 - 19824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19825 - 19829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC0FA\u0000\u0000" + // 19830 - 19834 + "\u0000\u0000\u0000\uB6B8\u0000\uE0BD\u0000\uE0BF\u0000\u0000" + // 19835 - 19839 + "\u0000\uE0BE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19840 - 19844 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0C0\u0000\u0000" + // 19845 - 19849 + "\u0000\uB8D1\u0000\u0000\u0000\uE0C1\u0000\u0000\u0000\u0000" + // 19850 - 19854 + "\u0000\u0000\u0000\u0000\u0000\uB6E9\u0000\u0000\u0000\uC1C0" + // 19855 - 19859 + "\u0000\u0000\u0000\uB9FD\u0000\u0000\u0000\u0000\u0000\u0000" + // 19860 - 19864 + "\u0000\u0000\u0000\uE6E7\u0000\uE6E9\u0000\uE6E8\u0000\uC8A5" + // 19865 - 19869 + "\u0000\u0000\u0000\uC6F9\u0000\u0000\u0000\uCFBE\u0000\uC8A9" + // 19870 - 19874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19875 - 19879 + "\u0000\u0000\u0000\uE6EB\u0000\u0000\u0000\u0000\u0000\uBED3" + // 19880 - 19884 + "\u0000\u0000\u0000\uC9AA\u0000\u0000\u0000\uE6EC\u0000\uE6EA" + // 19885 - 19889 + "\u0000\u0000\u0000\uB4CE\u0000\u0000\u0000\u0000\u0000\uC2F6" + // 19890 - 19894 + "\u0000\u0000\u0000\u0000\u0000\uE0E8\u008F\uCCAB\u0000\u0000" + // 19895 - 19899 + "\u0000\u0000\u008F\uCCAD\u008F\uCCAE\u0000\u0000\u0000\u0000" + // 19900 - 19904 + "\u0000\u0000\u008F\uCCB2\u0000\u0000\u0000\u0000\u0000\u0000" + // 19905 - 19909 + "\u0000\uE0EA\u0000\uCED6\u0000\uB6D7\u0000\uC8FC\u0000\uC7CA" + // 19910 - 19914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0EB\u0000\u0000" + // 19915 - 19919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0ED\u0000\uA2DC" + // 19920 - 19924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1C2" + // 19925 - 19929 + "\u0000\u0000\u0000\uA2CA\u0000\uA2CB\u0000\uA2C1\u0000\uA2C0" + // 19930 - 19934 + "\u0000\uA2E9\u0000\uA2EA\u0000\u0000\u0000\u0000\u0000\u0000" + // 19935 - 19939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1E8" + // 19940 - 19944 + "\u0000\uA2E8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19945 - 19949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2E6\u0000\u0000" + // 19950 - 19954 + "\u0000\u0000\u0000\uCDAB\u0000\u0000\u0000\u0000\u0000\u0000" + // 19955 - 19959 + "\u0000\u0000\u0000\uD8DC\u0000\u0000\u0000\u0000\u0000\uD8E0" + // 19960 - 19964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC1FE\u0000\u0000" + // 19965 - 19969 + "\u0000\uCEF9\u0000\uD8E1\u0000\u0000\u0000\u0000\u0000\uD8DE" + // 19970 - 19974 + "\u0000\u0000\u0000\uD8DB\u0000\u0000\u008F\uBEE4\u0000\uD8DA" + // 19975 - 19979 + "\u0000\uD8DF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19980 - 19984 + "\u0000\u0000\u0000\uC4C5\u0000\u0000\u0000\u0000\u0000\u0000" + // 19985 - 19989 + "\u0000\uB1CC\u0000\uB9BF\u0000\uDEA9\u0000\u0000\u0000\u0000" + // 19990 - 19994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBDA7" + // 19995 - 19999 + "\u0000\uDEAE\u0000\u0000\u0000\uDEAD\u0000\uDEA8\u0000\u0000" + // 20000 - 20004 + "\u0000\uDEAB\u0000\u0000\u0000\u0000\u0000\uB3E8\u0000\u0000" + // 20005 - 20009 + "\u0000\uDEAA\u0000\uC7C9\u0000\u0000\u0000\uD4B3\u0000\u0000" + // 20010 - 20014 + "\u0000\u0000\u0000\uBAA5\u008F\uB7C2\u0000\uC3B3\u0000\u0000" + // 20015 - 20019 + "\u0000\u0000\u0000\uD4B0\u0000\uC4DA\u0000\u0000\u0000\u0000" + // 20020 - 20024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20025 - 20029 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20030 - 20034 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20035 - 20039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4B4\u0000\uA2CF" + // 20040 - 20044 + "\u0000\u0000\u0000\uA2DF\u0000\uA2D0\u0000\u0000\u0000\u0000" + // 20045 - 20049 + "\u0000\u0000\u0000\uA2E0\u0000\uA2BA\u0000\u0000\u0000\u0000" + // 20050 - 20054 + "\u0000\uA2BB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20055 - 20059 + "\u0000\u0000\u0000\u0000\u0000\uA1DD\u0000\u0000\u0000\u0000" + // 20060 - 20064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20065 - 20069 + "\u0000\uA2E5\u0000\u0000\u0000\u0000\u0000\uA2E7\u0000\uA1E7" + // 20070 - 20074 + "\u0000\u0000\u0000\uB0F7\u0000\uD3AF\u0000\u0000\u0000\u0000" + // 20075 - 20079 + "\u0000\uD3A7\u0000\uD3A8\u0000\u0000\u0000\uBEA5\u0000\uCBE9" + // 20080 - 20084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3AD\u0000\uD3AC" + // 20085 - 20089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5AF\u0000\u0000" + // 20090 - 20094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20095 - 20099 + "\u0000\u0000\u0000\uD3AE\u0000\u0000\u0000\u0000\u0000\uD3AB" + // 20100 - 20104 + "\u0000\u0000\u008F\uB5C4\u008F\uF3AB\u008F\uF3AC\u008F\uF3AD" + // 20105 - 20109 + "\u008F\uF3AE\u008F\uF3AF\u008F\uF3B0\u008F\uF3B1\u008F\uF3B2" + // 20110 - 20114 + "\u008F\uF3B3\u008F\uF3B4\u0000\u0000\u0000\u0000\u0000\u0000" + // 20115 - 20119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uF3A1\u008F\uF3A2" + // 20120 - 20124 + "\u008F\uF3A3\u008F\uF3A4\u008F\uF3A5\u008F\uF3A6\u008F\uF3A7" + // 20125 - 20129 + "\u008F\uF3A8\u008F\uF3A9\u008F\uF3AA\u0000\u0000\u0000\u0000" + // 20130 - 20134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4A9" + // 20135 - 20139 + "\u0000\uB0B5\u0000\uBADF\u0000\u0000\u0000\u0000\u0000\u0000" + // 20140 - 20144 + "\u0000\u0000\u0000\uB7BD\u0000\u0000\u0000\u0000\u0000\uC3CF" + // 20145 - 20149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20150 - 20154 + "\u0000\u0000\u0000\uD4AA\u0000\uD4AB\u0000\u0000\u0000\u0000" + // 20155 - 20159 + "\u0000\uD4AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20160 - 20164 + "\u0000\uB1E2\u0000\u0000\u0000\u0000\u0000\uB4F1\u0000\uC6E0" + // 20165 - 20169 + "\u0000\uCAF4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20170 - 20174 + "\u0000\uD4F7\u0000\uC1D5\u0000\uD4F6\u0000\uB7C0\u0000\u0000" + // 20175 - 20179 + "\u008F\uB8F5\u0000\uCBDB\u0000\uD4F5\u0000\u0000\u0000\uC5E5" + // 20180 - 20184 + "\u0000\uD4F9\u0000\u0000\u0000\uD4F8\u008F\uB8F7\u0000\u0000" + // 20185 - 20189 + "\u008F\uB8F8\u0000\u0000\u0000\u0000\u0000\uCBFD\u0000\uB4B7" + // 20190 - 20194 + "\u0000\u0000\u0000\uD8D4\u0000\u0000\u0000\uB7C5\u0000\uB3B4" + // 20195 - 20199 + "\u0000\u0000\u0000\u0000\u0000\uD8D1\u0000\u0000\u0000\u0000" + // 20200 - 20204 + "\u0000\uCEB8\u0000\uD8D3\u0000\uB0D6\u0000\uD8D5\u0000\u0000" + // 20205 - 20209 + "\u0000\uD8CC\u0000\uD8D2\u0000\uD8D9\u0000\uB7C4\u0000\uD8CD" + // 20210 - 20214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20215 - 20219 + "\u0000\u0000\u0000\uCDDD\u0000\u0000\u0000\uD2DE\u0000\u0000" + // 20220 - 20224 + "\u0000\uB5C9\u0000\uB3C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 20225 - 20229 + "\u0000\uB9E7\u0000\uB5C8\u0000\uC4DF\u0000\uB1A5\u0000\uC6B1" + // 20230 - 20234 + "\u0000\uCCBE\u0000\uB9A1\u0000\uCDF9\u0000\uC5C7\u0000\uB8FE" + // 20235 - 20239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20240 - 20244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7AF" + // 20245 - 20249 + "\u0000\u0000\u0000\uD2E7\u0000\u0000\u0000\uB6E3\u0000\uA2F7" + // 20250 - 20254 + "\u0000\uA2F8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1C5" + // 20255 - 20259 + "\u0000\uA1C4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20260 - 20264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20265 - 20269 + "\u0000\uA2F3\u0000\u0000\u0000\uA1EC\u0000\uA1ED\u0000\u0000" + // 20270 - 20274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20275 - 20279 + "\u0000\u0000\u0000\uA2A8\u0000\u0000\u0000\u0000\u0000\u007E" + // 20280 - 20284 + "\u0000\u0000\u0000\uB1C3\u0000\uC1D1\u0000\uB8FD\u0000\uB8C5" + // 20285 - 20289 + "\u0000\uB6E7\u0000\u0000\u0000\u0000\u0000\uD2DB\u0000\uC3A1" + // 20290 - 20294 + "\u0000\uC2FE\u0000\uB6AB\u0000\uBEA4\u0000\uD2DC\u0000\uD2DA" + // 20295 - 20299 + "\u0000\uB2C4\u0000\uC2E6\u0000\uBCB8\u0000\uBBCB\u0000\uB1A6" + // 20300 - 20304 + "\u0000\u0000\u0000\u0000\u0000\uB3F0\u0000\uB9E6\u0000\uBBCA" + // 20305 - 20309 + "\u0000\u0000\u0000\uD2DD\u0000\u0000\u0000\u0000\u0000\u0000" + // 20310 - 20314 + "\u0000\u0000\u0000\u0000\u0000\uB1B8\u0000\uB4AF\u0000\u0000" + // 20315 - 20319 + "\u0000\uD5A9\u0000\u0000\u0000\uCCC5\u0000\uC9B1\u0000\u0000" + // 20320 - 20324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20325 - 20329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0A8" + // 20330 - 20334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0F9" + // 20335 - 20339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBD1\u0000\uA7E2" + // 20340 - 20344 + "\u0000\uA7E3\u0000\uA7E4\u0000\uA7E5\u0000\uA7E6\u0000\uA7E7" + // 20345 - 20349 + "\u0000\uA7E8\u0000\uA7E9\u0000\uA7EA\u0000\uA7EB\u0000\uA7EC" + // 20350 - 20354 + "\u0000\uA7ED\u0000\uA7EE\u0000\uA7EF\u0000\uA7F0\u0000\uA7F1" + // 20355 - 20359 + "\u0000\u0000\u0000\uA7D7\u0000\u0000\u0000\u0000\u0000\u0000" + // 20360 - 20364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20365 - 20369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20370 - 20374 + "\u0000\u0000\u0000\u0000\u008F\uF3B8\u0000\u0000\u0000\u0000" + // 20375 - 20379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20380 - 20384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20385 - 20389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7EC" + // 20390 - 20394 + "\u0000\uD0C6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8BC" + // 20395 - 20399 + "\u0000\u0000\u0000\uCEE2\u0000\u0000\u0000\uBFAD\u008F\uB0E3" + // 20400 - 20404 + "\u0000\uBBC7\u0000\u0000\u0000\uBBF7\u0000\uB2C0\u0000\u0000" + // 20405 - 20409 + "\u0000\u0000\u0000\uBBD4\u0000\uC9DB\u0000\u0000\u0000\u0000" + // 20410 - 20414 + "\u0000\uC8C1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20415 - 20419 + "\u0000\uD6E3\u0000\uB4F5\u0000\u0000\u0000\u0000\u0000\u0000" + // 20420 - 20424 + "\u0000\u0000\u0000\uD6E6\u0000\u0000\u0000\u0000\u0000\u0000" + // 20425 - 20429 + "\u0000\u0000\u0000\uC4A1\u0000\u0000\u0000\u0000\u0000\uD6E5" + // 20430 - 20434 + "\u0000\uD6E4\u0000\uD6E7\u0000\u0000\u0000\uC4EB\u0000\u0000" + // 20435 - 20439 + "\u0000\u0000\u0000\uD8CA\u0000\u0000\u0000\uD8CB\u0000\u0000" + // 20440 - 20444 + "\u0000\u0000\u0000\uD8C0\u0000\uBBFC\u0000\u0000\u0000\uD8C4" + // 20445 - 20449 + "\u0000\uC2D6\u0000\uB9B2\u0000\uD8B2\u0000\uBFB5\u0000\u0000" + // 20450 - 20454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8D8\u0000\u0000" + // 20455 - 20459 + "\u0000\uCAE9\u0000\u0000\u0000\u0000\u0000\uD8CE\u0000\uD8CF" + // 20460 - 20464 + "\u0000\uD8D0\u0000\u0000\u0000\u0000\u0000\uD8D7\u0000\u0000" + // 20465 - 20469 + "\u0000\uD8D6\u0000\uA7B2\u0000\uA7B3\u0000\uA7B4\u0000\uA7B5" + // 20470 - 20474 + "\u0000\uA7B6\u0000\uA7B7\u0000\uA7B8\u0000\uA7B9\u0000\uA7BA" + // 20475 - 20479 + "\u0000\uA7BB\u0000\uA7BC\u0000\uA7BD\u0000\uA7BE\u0000\uA7BF" + // 20480 - 20484 + "\u0000\uA7C0\u0000\uA7C1\u0000\uA7D1\u0000\uA7D2\u0000\uA7D3" + // 20485 - 20489 + "\u0000\uA7D4\u0000\uA7D5\u0000\uA7D6\u0000\uA7D8\u0000\uA7D9" + // 20490 - 20494 + "\u0000\uA7DA\u0000\uA7DB\u0000\uA7DC\u0000\uA7DD\u0000\uA7DE" + // 20495 - 20499 + "\u0000\uA7DF\u0000\uA7E0\u0000\uA7E1\u0000\uA6D0\u0000\uA6D1" + // 20500 - 20504 + "\u0000\u0000\u0000\uA6D2\u0000\uA6D3\u0000\uA6D4\u0000\uA6D5" + // 20505 - 20509 + "\u0000\uA6D6\u0000\uA6D7\u0000\uA6D8\u0000\u0000\u0000\u0000" + // 20510 - 20514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20515 - 20519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20520 - 20524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20525 - 20529 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20530 - 20534 + "\u0000\uBCF3\u0000\uF1C4\u0000\uF1C5\u0000\uB9E1\u0000\u0000" + // 20535 - 20539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uF4F4\u0000\u0000" + // 20540 - 20544 + "\u0000\uD1B4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20545 - 20549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDA5" + // 20550 - 20554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20555 - 20559 + "\u0000\u0000\u0000\u0000\u0000\uCCD9\u0000\u0000\u0000\u0000" + // 20560 - 20564 + "\u0000\u0000\u0000\u0000\u0000\uD1B6\u0000\u0000\u0000\u0000" + // 20565 - 20569 + "\u0000\uD1B5\u0000\uD1B8\u0000\uD1B7\u0000\u0000\u0000\u0000" + // 20570 - 20574 + "\u0000\uD1B9\u0000\uA6B0\u0000\uA6B1\u0000\u0000\u0000\uA6B2" + // 20575 - 20579 + "\u0000\uA6B3\u0000\uA6B4\u0000\uA6B5\u0000\uA6B6\u0000\uA6B7" + // 20580 - 20584 + "\u0000\uA6B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20585 - 20589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA6C1\u0000\uA6C2" + // 20590 - 20594 + "\u0000\uA6C3\u0000\uA6C4\u0000\uA6C5\u0000\uA6C6\u0000\uA6C7" + // 20595 - 20599 + "\u0000\uA6C8\u0000\uA6C9\u0000\uA6CA\u0000\uA6CB\u0000\uA6CC" + // 20600 - 20604 + "\u0000\uA6CD\u0000\uA6CE\u0000\uA6CF\u0000\u0080\u0000\u0081" + // 20605 - 20609 + "\u0000\u0082\u0000\u0083\u0000\u0084\u0000\u0085\u0000\u0086" + // 20610 - 20614 + "\u0000\u0087\u0000\u0088\u0000\u0089\u0000\u008A\u0000\u008B" + // 20615 - 20619 + "\u0000\u008C\u0000\u008D\u0000\u0000\u0000\u0000\u0000\u0090" + // 20620 - 20624 + "\u0000\u0091\u0000\u0092\u0000\u0093\u0000\u0094\u0000\u0095" + // 20625 - 20629 + "\u0000\u0096\u0000\u0097\u0000\u0098\u0000\u0099\u0000\u009A" + // 20630 - 20634 + "\u0000\u009B\u0000\u009C\u0000\u009D\u0000\u009E\u0000\u009F" + // 20635 - 20639 + "\u0000\u0060\u0000\u0061\u0000\u0062\u0000\u0063\u0000\u0064" + // 20640 - 20644 + "\u0000\u0065\u0000\u0066\u0000\u0067\u0000\u0068\u0000\u0069" + // 20645 - 20649 + "\u0000\u006A\u0000\u006B\u0000\u006C\u0000\u006D\u0000\u006E" + // 20650 - 20654 + "\u0000\u006F\u0000\u0070\u0000\u0071\u0000\u0072\u0000\u0073" + // 20655 - 20659 + "\u0000\u0074\u0000\u0075\u0000\u0076\u0000\u0077\u0000\u0078" + // 20660 - 20664 + "\u0000\u0079\u0000\u007A\u0000\u007B\u0000\u007C\u0000\u007D" + // 20665 - 20669 + "\u0000\u8EE4\u0000\u007F\u0000\u0040\u0000\u0041\u0000\u0042" + // 20670 - 20674 + "\u0000\u0043\u0000\u0044\u0000\u0045\u0000\u0046\u0000\u0047" + // 20675 - 20679 + "\u0000\u0048\u0000\u0049\u0000\u004A\u0000\u004B\u0000\u004C" + // 20680 - 20684 + "\u0000\u004D\u0000\u004E\u0000\u004F\u0000\u0050\u0000\u0051" + // 20685 - 20689 + "\u0000\u0052\u0000\u0053\u0000\u0054\u0000\u0055\u0000\u0056" + // 20690 - 20694 + "\u0000\u0057\u0000\u0058\u0000\u0059\u0000\u005A\u0000\u005B" + // 20695 - 20699 + "\u0000\u8EE3\u0000\u005D\u0000\u005E\u0000\u005F\u0000\u0020" + // 20700 - 20704 + "\u0000\u0021\u0000\"\u0000\u0023\u0000\u0024\u0000\u0025" + // 20705 - 20709 + "\u0000\u0026\u0000\u0027\u0000\u0028\u0000\u0029\u0000\u002A" + // 20710 - 20714 + "\u0000\u002B\u0000\u002C\u0000\u002D\u0000\u002E\u0000\u002F" + // 20715 - 20719 + "\u0000\u0030\u0000\u0031\u0000\u0032\u0000\u0033\u0000\u0034" + // 20720 - 20724 + "\u0000\u0035\u0000\u0036\u0000\u0037\u0000\u0038\u0000\u0039" + // 20725 - 20729 + "\u0000\u003A\u0000\u003B\u0000\u003C\u0000\u003D\u0000\u003E" + // 20730 - 20734 + "\u0000\u003F\u0000\u0000\u0000\uB3A1\u0000\uBAB2\u0000\uF2B1" + // 20735 - 20739 + "\u0000\uF2B0\u0000\uCCA5\u0000\u0000\u0000\u0000\u0000\u0000" + // 20740 - 20744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2B3" + // 20745 - 20749 + "\u0000\uF2B4\u0000\uF2B2\u0000\u0000\u0000\uF2B5\u0000\u0000" + // 20750 - 20754 + "\u0000\u0000\u0000\uCBE2\u0000\u0000\u0000\u0000\u0000\u0000" + // 20755 - 20759 + "\u0000\uF2B6\u0000\u0000\u0000\uB5FB\u0000\u0000\u0000\u0000" + // 20760 - 20764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3E1\u0000\u0000" + // 20765 - 20769 + "\u0000\u0000\u0000\u0000\u0000\uB4EF\u0000\u0000\u0000\uD3E4" + // 20770 - 20774 + "\u0000\uD3E0\u0000\uD3E3\u0000\u0000\u0000\u0000\u0000\u0000" + // 20775 - 20779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCAAE" + // 20780 - 20784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6D5\u0000\u0000" + // 20785 - 20789 + "\u0000\uC8B8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20790 - 20794 + "\u0000\u0000\u0000\uD6B2\u0000\u0000\u0000\uD6B4\u0000\u0000" + // 20795 - 20799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20800 - 20804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20805 - 20809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20810 - 20814 + "\u0000\u0000\u0000\uD6B5\u0000\u0000\u0000\u0000\u0000\u0000" + // 20815 - 20819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uC9F4\u008F\uC9F5" + // 20820 - 20824 + "\u0000\u0000\u0000\uCEFB\u0000\u0000\u0000\u0000\u0000\uDFEA" + // 20825 - 20829 + "\u0000\u0000\u0000\uC0F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 20830 - 20834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFE6\u0000\uDFEB" + // 20835 - 20839 + "\u0000\u0000\u0000\u0000\u0000\uB1EC\u0000\u0000\u0000\u0000" + // 20840 - 20844 + "\u008F\uC9FC\u0000\u0000\u0000\u0000\u0000\u0000\u008F\uBBD0" + // 20845 - 20849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20850 - 20854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6CB\u0000\uD6C8" + // 20855 - 20859 + "\u0000\u0000\u0000\uD6CA\u0000\u0000\u0000\uCDF2\u0000\u0000" + // 20860 - 20864 + "\u0000\uD6C9\u008F\uF4B0\u0000\u0000\u0000\u0000\u0000\u0000" + // 20865 - 20869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20870 - 20874 + "\u0000\uD6BF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBBA" + // 20875 - 20879 + "\u0000\u0000\u0000\uB1F9\u0000\uE1B4\u0000\u0000\u0000\uCDD1" + // 20880 - 20884 + "\u0000\u0000\u0000\u0000\u0000\uCAE3\u0000\uE1B5\u0000\u0000" + // 20885 - 20889 + "\u0000\u0000\u008F\uCDAA\u0000\uC5C4\u0000\uCDB3\u0000\uB9C3" + // 20890 - 20894 + "\u0000\uBFBD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC3CB" + // 20895 - 20899 + "\u0000\uD2B4\u0000\u0000\u0000\uC4AE\u0000\uB2E8\u0000\uE1B6" + // 20900 - 20904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1CF\u0000\u0000" + // 20905 - 20909 + "\u0000\uE1CE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20910 - 20914 + "\u0000\u0000\u0000\uB1D6\u0000\u0000\u0000\u0000\u0000\u0000" + // 20915 - 20919 + "\u0000\u0000\u0000\u0000\u0000\uE1D7\u0000\uC8E8\u0000\uE1D1" + // 20920 - 20924 + "\u0000\u0000\u0000\uE1D3\u0000\u0000\u0000\u0000\u0000\uE1D5" + // 20925 - 20929 + "\u0000\uBFBE\u0000\u0000\u0000\u0000\u0000\uE1D6\u0000\uE1D4" + // 20930 - 20934 + "\u0000\uBCC0\u0000\u0000\u0000\uEFD7\u0000\u0000\u0000\u0000" + // 20935 - 20939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20940 - 20944 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20945 - 20949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20950 - 20954 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20955 - 20959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20960 - 20964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9EC\u0000\u0000" + // 20965 - 20969 + "\u0000\u0000\u0000\u0000\u0000\uBFE2\u0000\u0000\u0000\u0000" + // 20970 - 20974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4B2\u0000\uD4B5" + // 20975 - 20979 + "\u0000\u0000\u0000\uB7BF\u0000\u0000\u0000\u0000\u0000\u0000" + // 20980 - 20984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4B6" + // 20985 - 20989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20990 - 20994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20995 - 20999 + "\u0000\u0000\u0000\u0000\u0000\uD6A9\u0000\u0000\u0000\u0000" + // 21000 - 21004 + "\u0000\u0000\u0000\uB4F4\u0000\uD6AA\u0000\u0000\u0000\u0000" + // 21005 - 21009 + "\u0000\uD6AB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21010 - 21014 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21015 - 21019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3F9\u0000\u0000" + // 21020 - 21024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21025 - 21029 + "\u0000\u0000\u0000\uEFB0\u0000\u0000\u0000\uBABF\u0000\uC1F9" + // 21030 - 21034 + "\u0000\u0000\u0000\u0000\u0000\uC4CA\u0000\u0000\u0000\u0000" + // 21035 - 21039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBFE3\u0000\u0000" + // 21040 - 21044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uBBD5" + // 21045 - 21049 + "\u0000\u0000\u0000\uC0CA\u0000\u0000\u0000\uC2D3\u0000\uB5A2" + // 21050 - 21054 + "\u0000\u0000\u0000\u0000\u0000\uC4A2\u0000\u0000\u0000\u0000" + // 21055 - 21059 + "\u0000\uD6E8\u0000\uD6E9\u0000\uBEEF\u0000\u0000\u0000\u0000" + // 21060 - 21064 + "\u0000\u0000\u0000\u0000\u0000\uCBB9\u0000\u0000\u0000\u0000" + // 21065 - 21069 + "\u0000\uA7A7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21070 - 21074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21075 - 21079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21080 - 21084 + "\u0000\uA7A1\u0000\uA7A2\u0000\uA7A3\u0000\uA7A4\u0000\uA7A5" + // 21085 - 21089 + "\u0000\uA7A6\u0000\uA7A8\u0000\uA7A9\u0000\uA7AA\u0000\uA7AB" + // 21090 - 21094 + "\u0000\uA7AC\u0000\uA7AD\u0000\uA7AE\u0000\uA7AF\u0000\uA7B0" + // 21095 - 21099 + "\u0000\uA7B1\u0000\u0000\u0000\uF2CC\u0000\uF2C9\u0000\uF2C8" + // 21100 - 21104 + "\u0000\uF2CA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB7DF" + // 21105 - 21109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21110 - 21114 + "\u0000\u0000\u0000\u0000\u0000\uF2D0\u0000\uF2CF\u0000\uF2CE" + // 21115 - 21119 + "\u0000\u0000\u0000\u0000\u0000\uB0B3\u0000\u0000\u0000\u0000" + // 21120 - 21124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21125 - 21129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2E3\u0000\uE9D2" + // 21130 - 21134 + "\u0000\uE9D3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21135 - 21139 + "\u0000\u0000\u0000\u0000\u0000\uE9CE\u0000\u0000\u0000\uBBBD" + // 21140 - 21144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21145 - 21149 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21150 - 21154 + "\u0000\uE0C9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21155 - 21159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21160 - 21164 + "\u0000\u0000\u0000\u0000\u0000\uE0CB\u0000\uE0C8\u0000\u0000" + // 21165 - 21169 + "\u0000\u0000\u0000\u0000\u0000\uCCD4\u0000\uE0CA\u0000\uE0CC" + // 21170 - 21174 + "\u0000\u0000\u0000\uCEC4\u0000\u0000\u0000\uF2DF\u0000\u0000" + // 21175 - 21179 + "\u0000\u0000\u0000\uF2DE\u0000\uF2DD\u0000\u0000\u0000\u0000" + // 21180 - 21184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21185 - 21189 + "\u0000\uC9C9\u0000\uF2DB\u0000\uB0F3\u0000\uF2E0\u0000\u0000" + // 21190 - 21194 + "\u0000\uF2E2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21195 - 21199 + "\u0000\u0000\u0000\u0000\u0000\uB3EF\u0000\uF2CD\u0000\uB1B7" + // 21200 - 21204 + "\u0000\u0000\u0000\u0000\u0000\uF2E4\u0000\u0000\u0000\uF0DB" + // 21205 - 21209 + "\u0000\u0000\u0000\u0000\u0000\uB3F3\u0000\uF0D9\u0000\uF0DD" + // 21210 - 21214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0DE" + // 21215 - 21219 + "\u0000\u0000\u0000\uB0C8\u0000\u0000\u0000\uF0DF\u0000\uF0E0" + // 21220 - 21224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21225 - 21229 + "\u0000\u0000\u0000\u0000\u0000\uBEE4\u0000\u0000\u0000\u0000" + // 21230 - 21234 + "\u0000\u0000\u0000\uF0E1\u0000\u0000\u0000\u0000\u0000\u0000" + // 21235 - 21239 + "\u0000\uBEEE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21240 - 21244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5CD\u0000\u0000" + // 21245 - 21249 + "\u0000\uC4DC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1C5" + // 21250 - 21254 + "\u0000\u0000\u0000\uD5CB\u0000\u0000\u0000\u0000\u0000\u0000" + // 21255 - 21259 + "\u0000\uD5CE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21260 - 21264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5CF\u0000\u0000" + // 21265 - 21269 + "\u0000\uEFC5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21270 - 21274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21275 - 21279 + "\u0000\uEFC3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21280 - 21284 + "\u0000\u0000\u008F\uE5E2\u0000\u0000\u0000\u0000\u0000\u0000" + // 21285 - 21289 + "\u0000\uEFC4\u0000\uEFC2\u0000\u0000\u0000\uC2F8\u0000\u0000" + // 21290 - 21294 + "\u0000\uEFC6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21295 - 21299 + "\u0000\u0000\u0000\uC8BD\u0000\uCACC\u0000\u0000\u0000\uD1E7" + // 21300 - 21304 + "\u0000\u0000\u0000\uCDF8\u0000\uD1E8\u0000\u0000\u0000\u0000" + // 21305 - 21309 + "\u0000\u0000\u0000\uD1E9\u0000\u0000\u0000\uC5FE\u0000\u0000" + // 21310 - 21314 + "\u0000\u0000\u0000\uD1EA\u0000\u0000\u0000\u0000\u0000\uC0A9" + // 21315 - 21319 + "\u0000\uBAFE\u0000\uB7F4\u0000\uD1EB\u0000\uBBC9\u0000\uB9EF" + // 21320 - 21324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1A9" + // 21325 - 21329 + "\u0000\u0000\u0000\uD1A7\u0000\u0000\u0000\uC1CE\u0000\u0000" + // 21330 - 21334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1A8" + // 21335 - 21339 + "\u0000\uD1AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21340 - 21344 + "\u0000\u0000\u008F\uF4A3\u0000\uD1AC\u0000\u0000\u0000\u0000" + // 21345 - 21349 + "\u0000\u0000\u0000\uD1AB\u0000\u0000\u0000\uCAC8\u0000\u0000" + // 21350 - 21354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5B1\u0000\uD9FB" + // 21355 - 21359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9FC\u0000\u0000" + // 21360 - 21364 + "\u0000\uC9EF\u0000\u0000\u0000\uC7C5\u0000\uBBA3\u0000\u0000" + // 21365 - 21369 + "\u0000\uC0F1\u0000\u0000\u0000\uCBD0\u0000\u0000\u0000\u0000" + // 21370 - 21374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB3C9" + // 21375 - 21379 + "\u0000\u0000\u0000\uDAA5\u0000\uD9FE\u0000\u0000\u0000\u0000" + // 21380 - 21384 + "\u0000\u0000\u0000\uB1BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 21385 - 21389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21390 - 21394 + "\u0000\u0000\u0000\uC1B9\u0000\u0000\u0000\uD3D9\u0000\u0000" + // 21395 - 21399 + "\u0000\uD3DA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21400 - 21404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21405 - 21409 + "\u0000\u0000\u0000\uB3FA\u0000\u0000\u0000\u0000\u0000\u0000" + // 21410 - 21414 + "\u0000\u0000\u0000\uEEF7\u008F\uE4BE\u0000\u0000\u0000\uCBAF" + // 21415 - 21419 + "\u008F\uE4BF\u0000\u0000\u008F\uE4C0\u0000\u0000\u008F\uE4C1" + // 21420 - 21424 + "\u0000\u0000\u008F\uE4C3\u0000\u0000\u0000\u0000\u0000\u0000" + // 21425 - 21429 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9EC\u0000\u0000" + // 21430 - 21434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21435 - 21439 + "\u0000\uC2BB\u0000\u0000\u0000\uD9F3\u0000\u0000\u0000\u0000" + // 21440 - 21444 + "\u0000\u0000\u0000\uD9ED\u0000\uC1DF\u0000\u0000\u0000\uD9EA" + // 21445 - 21449 + "\u0000\uD9F1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21450 - 21454 + "\u0000\uD9D3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDE6" + // 21455 - 21459 + "\u0000\uDDE5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21460 - 21464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21465 - 21469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21470 - 21474 + "\u0000\u0000\u0000\uBFE5\u0000\u0000\u0000\u0000\u0000\uC9B9" + // 21475 - 21479 + "\u0000\uB1CA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21480 - 21484 + "\u0000\u0000\u0000\uC8C5\u008F\uC6D7\u0000\u0000\u0000\uF2FA" + // 21485 - 21489 + "\u0000\u0000\u0000\u0000\u0000\uF2F7\u0000\u0000\u0000\uF2FD" + // 21490 - 21494 + "\u0000\u0000\u0000\uF2FE\u0000\u0000\u0000\u0000\u0000\u0000" + // 21495 - 21499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3A5" + // 21500 - 21504 + "\u0000\uF3A4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21505 - 21509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3A6\u0000\u0000" + // 21510 - 21514 + "\u0000\u0000\u0000\uB1AD\u0000\uF3A1\u0000\uF3A2\u0000\u0000" + // 21515 - 21519 + "\u0000\uF0F6\u0000\u0000\u0000\u0000\u0000\uF0F5\u0000\u0000" + // 21520 - 21524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21525 - 21529 + "\u0000\u0000\u0000\uCBCB\u0000\uC6AC\u0000\u0000\u0000\u0000" + // 21530 - 21534 + "\u0000\uCBCB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB1D0" + // 21535 - 21539 + "\u0000\u0000\u0000\u0000\u0000\uF0F7\u0000\uF0F4\u0000\uF0F8" + // 21540 - 21544 + "\u0000\u0000\u0000\uC9D1\u0000\uCDEA\u0000\uF0F8\u0000\u0000" + // 21545 - 21549 + "\u0000\u0000\u0000\uB5D2\u0000\uC0EB\u0000\uBCBC\u0000\uCDA8" + // 21550 - 21554 + "\u0000\uD5E1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21555 - 21559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB5DC\u0000\u0000" + // 21560 - 21564 + "\u0000\uBACB\u0000\u0000\u0000\u0000\u0000\uB3B2\u0000\uB1E3" + // 21565 - 21569 + "\u0000\uBEAC\u0000\uB2C8\u0000\u0000\u0000\uD5E2\u0000\uCDC6" + // 21570 - 21574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21575 - 21579 + "\u0000\uBDC9\u0000\u0000\u0000\uF3B2\u0000\uF3B8\u0000\u0000" + // 21580 - 21584 + "\u0000\uF3B1\u0000\u0000\u0000\uF3B6\u0000\u0000\u0000\u0000" + // 21585 - 21589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21590 - 21594 + "\u0000\u0000\u0000\uF3B7\u0000\u0000\u0000\u0000\u0000\u0000" + // 21595 - 21599 + "\u0000\uF3BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB2AA" + // 21600 - 21604 + "\u0000\u0000\u0000\uF3B9\u0000\u0000\u0000\u0000\u0000\u0000" + // 21605 - 21609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2DD\u0000\u0000" + // 21610 - 21614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21615 - 21619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21620 - 21624 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21625 - 21629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21630 - 21634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21635 - 21639 + "\u0000\uA1DF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21640 - 21644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21645 - 21649 + "\u0000\uE7D5\u0000\uB9D2\u0000\uE7D6\u0000\uC8CC\u0000\u0000" + // 21650 - 21654 + "\u0000\uE7E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21655 - 21659 + "\u0000\uE7D8\u0000\u0000\u0000\uC2C9\u0000\uC7F5\u0000\uB8BF" + // 21660 - 21664 + "\u0000\uE7D7\u0000\uC1A5\u0000\u0000\u0000\u0000\u0000\u0000" + // 21665 - 21669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD5A8\u0000\u0000" + // 21670 - 21674 + "\u0000\u0000\u0000\uBBD0\u0000\u0000\u0000\uBBCF\u0000\u0000" + // 21675 - 21679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uB0B9\u0000\uB8C8" + // 21680 - 21684 + "\u0000\u0000\u0000\uC0AB\u0000\uB0D1\u0000\u0000\u0000\u0000" + // 21685 - 21689 + "\u0000\u0000\u0000\u0000\u0000\uD5AC\u0000\uD5AD\u0000\u0000" + // 21690 - 21694 + "\u0000\uD5AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6D1" + // 21695 - 21699 + "\u0000\u0000\u0000\uD6D0\u0000\u0000\u0000\u0000\u0000\uD6CF" + // 21700 - 21704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5E8\u0000\uD6BA" + // 21705 - 21709 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6D7\u0000\u0000" + // 21710 - 21714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21715 - 21719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21720 - 21724 + "\u0000\u0000\u0000\uD6D3\u0000\u0000\u0000\u0000\u0000\uA5A1" + // 21725 - 21729 + "\u0000\uA5A2\u0000\uA5A3\u0000\uA5A4\u0000\uA5A5\u0000\uA5A6" + // 21730 - 21734 + "\u0000\uA5A7\u0000\uA5A8\u0000\uA5A9\u0000\uA5AA\u0000\uA5AB" + // 21735 - 21739 + "\u0000\uA5AC\u0000\uA5AD\u0000\uA5AE\u0000\uA5AF\u0000\uA5B0" + // 21740 - 21744 + "\u0000\uA5B1\u0000\uA5B2\u0000\uA5B3\u0000\uA5B4\u0000\uA5B5" + // 21745 - 21749 + "\u0000\uA5B6\u0000\uA5B7\u0000\uA5B8\u0000\uA5B9\u0000\uA5BA" + // 21750 - 21754 + "\u0000\uA5BB\u0000\uA5BC\u0000\uA5BD\u0000\uA5BE\u0000\uA5BF" + // 21755 - 21759 + "\u0000\u0000\u0000\uF3C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 21760 - 21764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3C7\u0000\u0000" + // 21765 - 21769 + "\u0000\u0000\u0000\uF3C8\u0000\uF3C9\u0000\u0000\u0000\u0000" + // 21770 - 21774 + "\u0000\u0000\u0000\u0000\u0000\uF3CC\u0000\uF3CA\u0000\uCFBC" + // 21775 - 21779 + "\u0000\u0000\u0000\uF3CB\u0000\u0000\u0000\uCEEF\u0000\u0000" + // 21780 - 21784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3CD" + // 21785 - 21789 + "\u0000\u0000\u0000\uCEDB\u0000\u0000\u0000\uA1AA\u008F\uF3B6" + // 21790 - 21794 + "\u0000\uA1F4\u0000\uA1F0\u0000\uA1F3\u0000\uA1F5\u008F\uF3B5" + // 21795 - 21799 + "\u0000\uA1CA\u0000\uA1CB\u0000\uA1F6\u0000\uA1DC\u0000\uA1A4" + // 21800 - 21804 + "\u0000\uA1DD\u0000\uA1A5\u0000\uA1BF\u0000\uA3B0\u0000\uA3B1" + // 21805 - 21809 + "\u0000\uA3B2\u0000\uA3B3\u0000\uA3B4\u0000\uA3B5\u0000\uA3B6" + // 21810 - 21814 + "\u0000\uA3B7\u0000\uA3B8\u0000\uA3B9\u0000\uA1A7\u0000\uA1A8" + // 21815 - 21819 + "\u0000\uA1E3\u0000\uA1E1\u0000\uA1E4\u0000\uA1A9\u0000\u0000" + // 21820 - 21824 + "\u0000\u8EA1\u0000\u8EA2\u0000\u8EA3\u0000\u8EA4\u0000\u8EA5" + // 21825 - 21829 + "\u0000\u8EA6\u0000\u8EA7\u0000\u8EA8\u0000\u8EA9\u0000\u8EAA" + // 21830 - 21834 + "\u0000\u8EAB\u0000\u8EAC\u0000\u8EAD\u0000\u8EAE\u0000\u8EAF" + // 21835 - 21839 + "\u0000\u8EB0\u0000\u8EB1\u0000\u8EB2\u0000\u8EB3\u0000\u8EB4" + // 21840 - 21844 + "\u0000\u8EB5\u0000\u8EB6\u0000\u8EB7\u0000\u8EB8\u0000\u8EB9" + // 21845 - 21849 + "\u0000\u8EBA\u0000\u8EBB\u0000\u8EBC\u0000\u8EBD\u0000\u8EBE" + // 21850 - 21854 + "\u0000\u8EBF" + ; + } + } +} diff --git a/src/sun/nio/cs/ext/IBM834.java b/src/sun/nio/cs/ext/IBM834.java new file mode 100644 index 00000000..1ede5cd4 --- /dev/null +++ b/src/sun/nio/cs/ext/IBM834.java @@ -0,0 +1,96 @@ + +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +// EBCDIC DBCS-only Korean +public class IBM834 extends Charset +{ + public IBM834() { + super("x-IBM834", ExtendedCharsets.aliasesFor("x-IBM834")); + } + + public boolean contains(Charset cs) { + return (cs instanceof IBM834); + } + + public CharsetDecoder newDecoder() { + IBM933.initb2c(); + return new DoubleByte.Decoder_DBCSONLY( + this, IBM933.b2c, null, 0x40, 0xfe); // hardcode the b2min/max + } + + public CharsetEncoder newEncoder() { + IBM933.initc2b(); + return new Encoder(this); + } + + protected static class Encoder extends DoubleByte.Encoder_DBCSONLY { + public Encoder(Charset cs) { + super(cs, new byte[] {(byte)0xfe, (byte)0xfe}, + IBM933.c2b, IBM933.c2bIndex); + } + + public int encodeChar(char ch) { + int bb = super.encodeChar(ch); + if (bb == UNMAPPABLE_ENCODING) { + // Cp834 has 6 additional non-roundtrip char->bytes + // mappings, see#6379808 + if (ch == '\u00b7') { + return 0x4143; + } else if (ch == '\u00ad') { + return 0x4148; + } else if (ch == '\u2015') { + return 0x4149; + } else if (ch == '\u223c') { + return 0x42a1; + } else if (ch == '\uff5e') { + return 0x4954; + } else if (ch == '\u2299') { + return 0x496f; + } + } + return bb; + } + + public boolean isLegalReplacement(byte[] repl) { + if (repl.length == 2 && + repl[0] == (byte)0xfe && repl[1] == (byte)0xfe) + return true; + return super.isLegalReplacement(repl); + } + + } +} diff --git a/src/sun/nio/cs/ext/IBM942C.java b/src/sun/nio/cs/ext/IBM942C.java new file mode 100644 index 00000000..987574c4 --- /dev/null +++ b/src/sun/nio/cs/ext/IBM942C.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class IBM942C extends Charset implements HistoricallyNamedCharset +{ + public IBM942C() { + super("x-IBM942C", ExtendedCharsets.aliasesFor("x-IBM942C")); + } + + public String historicalName() { + return "Cp942C"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof IBM942C)); + } + + public CharsetDecoder newDecoder() { + return new DoubleByte.Decoder(this, + IBM942.b2c, + b2cSB, + 0x40, + 0xfc); + } + + public CharsetEncoder newEncoder() { + return new DoubleByte.Encoder(this, c2b, c2bIndex); + } + + final static char[] b2cSB; + final static char[] c2b; + final static char[] c2bIndex; + + static { + IBM942.initb2c(); + + // the mappings need udpate are + // u+001a <-> 0x1a + // u+001c <-> 0x1c + // u+005c <-> 0x5c + // u+007e <-> 0x7e + // u+007f <-> 0x7f + + b2cSB = Arrays.copyOf(IBM942.b2cSB, IBM942.b2cSB.length); + b2cSB[0x1a] = 0x1a; + b2cSB[0x1c] = 0x1c; + b2cSB[0x5c] = 0x5c; + b2cSB[0x7e] = 0x7e; + b2cSB[0x7f] = 0x7f; + + IBM942.initc2b(); + c2b = Arrays.copyOf(IBM942.c2b, IBM942.c2b.length); + c2bIndex = Arrays.copyOf(IBM942.c2bIndex, IBM942.c2bIndex.length); + c2b[c2bIndex[0] + 0x1a] = 0x1a; + c2b[c2bIndex[0] + 0x1c] = 0x1c; + c2b[c2bIndex[0] + 0x5c] = 0x5c; + c2b[c2bIndex[0] + 0x7e] = 0x7e; + c2b[c2bIndex[0] + 0x7f] = 0x7f; + } +} diff --git a/src/sun/nio/cs/ext/IBM943C.java b/src/sun/nio/cs/ext/IBM943C.java new file mode 100644 index 00000000..77a9de52 --- /dev/null +++ b/src/sun/nio/cs/ext/IBM943C.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class IBM943C extends Charset implements HistoricallyNamedCharset +{ + + public IBM943C() { + super("x-IBM943C", ExtendedCharsets.aliasesFor("x-IBM943C")); + } + + public String historicalName() { + return "Cp943C"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof IBM943C)); + } + + public CharsetDecoder newDecoder() { + return new DoubleByte.Decoder(this, + IBM943.b2c, + b2cSB, + 0x40, + 0xfc); + } + + public CharsetEncoder newEncoder() { + return new DoubleByte.Encoder(this, c2b, c2bIndex); + } + + final static char[] b2cSB; + final static char[] c2b; + final static char[] c2bIndex; + + static { + IBM943.initb2c(); + b2cSB = new char[0x100]; + for (int i = 0; i < 0x80; i++) { + b2cSB[i] = (char)i; + } + for (int i = 0x80; i < 0x100; i++) { + b2cSB[i] = IBM943.b2cSB[i]; + } + + IBM943.initc2b(); + c2b = Arrays.copyOf(IBM943.c2b, IBM943.c2b.length); + c2bIndex = Arrays.copyOf(IBM943.c2bIndex, IBM943.c2bIndex.length); + for (char c = '\0'; c < '\u0080'; ++c) { + int index = c2bIndex[c >> 8]; + c2b[index + (c & 0xff)] = c; + } + } +} diff --git a/src/sun/nio/cs/ext/IBM949C.java b/src/sun/nio/cs/ext/IBM949C.java new file mode 100644 index 00000000..0d675e6d --- /dev/null +++ b/src/sun/nio/cs/ext/IBM949C.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Arrays; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class IBM949C extends Charset implements HistoricallyNamedCharset +{ + + public IBM949C() { + super("x-IBM949C", ExtendedCharsets.aliasesFor("x-IBM949C")); + } + + public String historicalName() { + return "Cp949C"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof IBM949C)); + } + + public CharsetDecoder newDecoder() { + return new DoubleByte.Decoder(this, + IBM949.b2c, + b2cSB, + 0xa1, + 0xfe); + } + + public CharsetEncoder newEncoder() { + return new DoubleByte.Encoder(this, c2b, c2bIndex); + } + + final static char[] b2cSB; + final static char[] c2b; + final static char[] c2bIndex; + + static { + IBM949.initb2c(); + b2cSB = new char[0x100]; + for (int i = 0; i < 0x80; i++) { + b2cSB[i] = (char)i; + } + for (int i = 0x80; i < 0x100; i++) { + b2cSB[i] = IBM949.b2cSB[i]; + } + IBM949.initc2b(); + c2b = Arrays.copyOf(IBM949.c2b, IBM949.c2b.length); + c2bIndex = Arrays.copyOf(IBM949.c2bIndex, IBM949.c2bIndex.length); + for (char c = '\0'; c < '\u0080'; ++c) { + int index = c2bIndex[c >> 8]; + c2b[index + (c & 0xff)] = c; + } + } +} diff --git a/src/sun/nio/cs/ext/IBM964.java b/src/sun/nio/cs/ext/IBM964.java new file mode 100644 index 00000000..9db79aec --- /dev/null +++ b/src/sun/nio/cs/ext/IBM964.java @@ -0,0 +1,10533 @@ + +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class IBM964 + extends Charset + implements HistoricallyNamedCharset +{ + + public IBM964() { + super("x-IBM964", ExtendedCharsets.aliasesFor("x-IBM964")); + } + + public String historicalName() { + return "Cp964"; + } + + public boolean contains(Charset cs) { + return (cs instanceof IBM964); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + protected static class Decoder extends CharsetDecoder { + private final int SS2 = 0x8E; + private final int SS3 = 0x8F; + + private String mappingTableG2; + + public Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + int byte1, byte2; + int inputSize = 1; + char outputChar = '\uFFFD'; + byte1 = sa[sp] & 0xff; + + if (byte1 == SS2) { + if (sl - sp < 4) { + return CoderResult.UNDERFLOW; + } + byte1 = sa[sp + 1] & 0xff; + inputSize = 2; + if ( byte1 == 0xa2) + mappingTableG2 = mappingTableG2a2; + else if ( byte1 == 0xac) + mappingTableG2 = mappingTableG2ac; + else if ( byte1 == 0xad) + mappingTableG2 = mappingTableG2ad; + else + return CoderResult.malformedForLength(2); + byte1 = sa[sp + 2] & 0xff; + if ( byte1 < 0xa1 || byte1 > 0xfe) { + return CoderResult.malformedForLength(3); + } + byte2 = sa[sp + 3] & 0xff; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(4); + } + inputSize = 4; + outputChar = mappingTableG2.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } else if(byte1 == SS3 ) { + return CoderResult.malformedForLength(1); + } else if ( byte1 <= 0x9f ) { // valid single byte + outputChar = byteToCharTable.charAt(byte1); + } else if (byte1 < 0xa1 || byte1 > 0xfe) { // invalid range? + return CoderResult.malformedForLength(1); + } else { // G1 + if (sl - sp < 2) { + return CoderResult.UNDERFLOW; + } + byte2 = sa[sp + 1] & 0xff; + inputSize = 2; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(2); + } + outputChar = mappingTableG1.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } + if (outputChar == '\uFFFD') + return CoderResult.unmappableForLength(inputSize); + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = outputChar; + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int byte1, byte2; + int inputSize = 1; + char outputChar = '\uFFFD'; + byte1 = src.get() & 0xff; + + if (byte1 == SS2) { + if (src.remaining() < 3) + return CoderResult.UNDERFLOW; + byte1 = src.get() & 0xff; + inputSize = 2; + if ( byte1 == 0xa2) + mappingTableG2 = mappingTableG2a2; + else if ( byte1 == 0xac) + mappingTableG2 = mappingTableG2ac; + else if ( byte1 == 0xad) + mappingTableG2 = mappingTableG2ad; + else + return CoderResult.malformedForLength(2); + byte1 = src.get() & 0xff; + if ( byte1 < 0xa1 || byte1 > 0xfe) + return CoderResult.malformedForLength(3); + byte2 = src.get() & 0xff; + if ( byte2 < 0xa1 || byte2 > 0xfe) + return CoderResult.malformedForLength(4); + inputSize = 4; + outputChar = mappingTableG2.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } else if (byte1 == SS3 ) { + return CoderResult.malformedForLength(1); + } else if ( byte1 <= 0x9f ) { // valid single byte + outputChar = byteToCharTable.charAt(byte1); + } else if (byte1 < 0xa1 || byte1 > 0xfe) { // invalid range? + return CoderResult.malformedForLength(1); + } else { // G1 + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + byte2 = src.get() & 0xff; + if ( byte2 < 0xa1 || byte2 > 0xfe) { + return CoderResult.malformedForLength(2); + } + inputSize = 2; + outputChar = mappingTableG1.charAt(((byte1 - 0xa1) * 94) + byte2 - 0xa1); + } + + if (outputChar == '\uFFFD') + return CoderResult.unmappableForLength(inputSize); + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + dst.put(outputChar); + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (true && src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + private final static String byteToCharTable; + private final static String mappingTableG1; + private final static String mappingTableG2a2; + private final static String mappingTableG2ac; + private final static String mappingTableG2ad; + + static { + byteToCharTable = + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007" + + "\u0008\u0009\n\u000B\u000C\r\u000E\u000F" + + "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017" + + "\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F" + + "\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027" + + "\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F" + + "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037" + + "\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F" + + "\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047" + + "\u0048\u0049\u004A\u004B\u004C\u004D\u004E\u004F" + + "\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0057" + + "\u0058\u0059\u005A\u005B\\\u005D\u005E\u005F" + + "\u0060\u0061\u0062\u0063\u0064\u0065\u0066\u0067" + + "\u0068\u0069\u006A\u006B\u006C\u006D\u006E\u006F" + + "\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077" + + "\u0078\u0079\u007A\u007B\u007C\u007D\u007E\u007F" + + "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087" + + "\u0088\u0089\u008A\u008B\u008C\u008D\uFFFD\uFFFD" + + "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097" + + "\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F" + ; + mappingTableG1 = + "\u3000\uFF0C\u3001\u3002\uFF0E\u2027\uFF1B\uFF1A" + + "\uFF1F\uFF01\uFE30\u2026\u2025\uFE50\uFE51\uFE52" + + "\u00B7\uFE54\uFE55\uFE56\uFE57\uFE31\u2014\uFE32" + + "\uFE58\uFE33\u2574\uFE34\uFE4F\uFF08\uFF09\uFE35" + + "\uFE36\uFF5B\uFF5D\uFE37\uFE38\u3014\u3015\uFE39" + + "\uFE3A\u3010\u3011\uFE3B\uFE3C\u300A\u300B\uFE3D" + + "\uFE3E\u3008\u3009\uFE3F\uFE40\u300C\u300D\uFE41" + + "\uFE42\u300E\u300F\uFE43\uFE44\uFE59\uFE5A\uFE5B" + + "\uFE5C\uFE5D\uFE5E\u2018\u2019\u201C\u201D\u301D" + + "\u301E\u2035\u2032\uFF03\uFF06\uFF0A\u203B\u00A7" + + "\u3003\u25CB\u25CF\u25B3\u25B2\u25CE\u2606\u2605" + + "\u25C7\u25C6\u25A1\u25A0\u25BD\u25BC\u32A3\u2105" + + "\u203E\uFFE3\uFF3F\u02CD\uFE49\uFE4A\uFE4D\uFE4E" + + "\uFE4B\uFE4C\uFE5F\uFE60\uFE61\uFF0B\uFF0D\u00D7" + + "\u00F7\u00B1\u221A\uFF1C\uFF1E\uFF1D\u2266\u2267" + + "\u2260\u221E\u2252\u2261\uFE62\uFE63\uFE64\uFE65" + + "\uFE66\u223C\u2229\u222A\u22A5\u2220\u221F\u22BF" + + "\u33D2\u33D1\u222B\u222E\u2235\u2234\u2640\u2642" + + "\u2295\u2299\u2191\u2193\u2192\u2190\u2196\u2197" + + "\u2199\u2198\u2225\uFF5C\uFF0F\uFF3C\u2215\uFE68" + + "\uFF04\uFFE5\u3012\uFFE0\uFFE1\uFF05\uFF20\u2103" + + "\u2109\uFE69\uFE6A\uFE6B\u33D5\u339C\u339D\u339E" + + "\u33CE\u33A1\u338E\u338F\u33C4\u00B0\u5159\u515B" + + "\u515E\u515D\u5161\u5163\u55E7\u74E9\u7CCE\u2581" + + "\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u258F" + + "\u258E\u258D\u258C\u258B\u258A\u2589\u253C\u2534" + + "\u252C\u2524\u251C\u2594\u2500\u2502\u2595\u250C" + + "\u2510\u2514\u2518\u256D\u256E\u2570\u256F\u2550" + + "\u255E\u256A\u2561\u25E2\u25E3\u25E5\u25E4\u2571" + + "\u2572\u2573\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFF10\uFF11\uFF12\uFF13\uFF14\uFF15" + + "\uFF16\uFF17\uFF18\uFF19\u2160\u2161\u2162\u2163" + + "\u2164\u2165\u2166\u2167\u2168\u2169\u3021\u3022" + + "\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u5341" + + "\u5344\u5345\uFF21\uFF22\uFF23\uFF24\uFF25\uFF26" + + "\uFF27\uFF28\uFF29\uFF2A\uFF2B\uFF2C\uFF2D\uFF2E" + + "\uFF2F\uFF30\uFF31\uFF32\uFF33\uFF34\uFF35\uFF36" + + "\uFF37\uFF38\uFF39\uFF3A\uFF41\uFF42\uFF43\uFF44" + + "\uFF45\uFF46\uFF47\uFF48\uFF49\uFF4A\uFF4B\uFF4C" + + "\uFF4D\uFF4E\uFF4F\uFF50\uFF51\uFF52\uFF53\uFF54" + + "\uFF55\uFF56\uFF57\uFF58\uFF59\uFF5A\u0391\u0392" + + "\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039A" + + "\u039B\u039C\u039D\u039E\u039F\u03A0\u03A1\u03A3" + + "\u03A4\u03A5\u03A6\u03A7\u03A8\u03A9\u03B1\u03B2" + + "\u03B3\u03B4\u03B5\u03B6\u03B7\u03B8\u03B9\u03BA" + + "\u03BB\u03BC\u03BD\u03BE\u03BF\u03C0\u03C1\u03C3" + + "\u03C4\u03C5\u03C6\u03C7\u03C8\u03C9\u3105\u3106" + + "\u3107\u3108\u3109\u310A\u310B\u310C\u310D\u310E" + + "\u310F\u3110\u3111\u3112\u3113\u3114\u3115\u3116" + + "\u3117\u3118\u3119\u311A\u311B\u311C\u311D\u311E" + + "\u311F\u3120\u3121\u3122\u3123\u3124\u3125\u3126" + + "\u3127\u3128\u3129\u02D9\u02C9\u02CA\u02C7\u02CB" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2460\u2461" + + "\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469" + + "\u2474\u2475\u2476\u2477\u2478\u2479\u247A\u247B" + + "\u247C\u247D\u2170\u2171\u2172\u2173\u2174\u2175" + + "\u2176\u2177\u2178\u2179\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\u4E00\u4E28\u4E36\u4E3F" + + "\u4E59\u4E85\u4E8C\u4EA0\u4EBA\u513F\u5165\u516B" + + "\u5182\u5196\u51AB\u51E0\u51F5\u5200\u529B\u52F9" + + "\u5315\u531A\u5338\u5341\u535C\u5369\u5382\u53B6" + + "\u53C8\u53E3\u56D7\u571F\u58EB\u590A\u5915\u5927" + + "\u5973\u5B50\u5B80\u5BF8\u5C0F\u5C22\u5C38\u5C6E" + + "\u5C71\u5DDB\u5DE5\u5DF1\u5DFE\u5E72\u5E7A\u5E7F" + + "\u5EF4\u5EFE\u5F0B\u5F13\u5F50\u5F61\u5F73\u5FC3" + + "\u6208\u6236\u624B\u652F\u6534\u6587\u6597\u65A4" + + "\u65B9\u65E0\u65E5\u66F0\u6708\u6728\u6B20\u6B62" + + "\u6B79\u6BB3\u6BCB\u6BD4\u6BDB\u6C0F\u6C14\u6C34" + + "\u706B\u722A\u7236\u723B\u723F\u7247\u7259\u725B" + + "\u72AC\u7384\u7389\u74DC\u74E6\u7518\u751F\u7528" + + "\u7530\u758B\u7592\u7676\u767D\u76AE\u76BF\u76EE" + + "\u77DB\u77E2\u77F3\u793A\u79B8\u79BE\u7A74\u7ACB" + + "\u7AF9\u7C73\u7CF8\u7F36\u7F51\u7F8A\u7FBD\u8001" + + "\u800C\u8012\u8033\u807F\u8089\u81E3\u81EA\u81F3" + + "\u81FC\u820C\u821B\u821F\u826E\u8272\u8278\u864D" + + "\u866B\u8840\u884C\u8863\u897E\u898B\u89D2\u8A00" + + "\u8C37\u8C46\u8C55\u8C78\u8C9D\u8D64\u8D70\u8DB3" + + "\u8EAB\u8ECA\u8F9B\u8FB0\u8FB5\u9091\u9149\u91C6" + + "\u91CC\u91D1\u9577\u9580\u961C\u96B6\u96B9\u96E8" + + "\u9752\u975E\u9762\u9769\u97CB\u97ED\u97F3\u9801" + + "\u98A8\u98DB\u98DF\u9996\u9999\u99AC\u9AA8\u9AD8" + + "\u9ADF\u9B25\u9B2F\u9B32\u9B3C\u9B5A\u9CE5\u9E75" + + "\u9E7F\u9EA5\u9EBB\u9EC3\u9ECD\u9ED1\u9EF9\u9EFD" + + "\u9F0E\u9F13\u9F20\u9F3B\u9F4A\u9F52\u9F8D\u9F9C" + + "\u9FA0\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\u2400\u2401" + + "\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409" + + "\u240A\u240B\u240C\u240D\u240E\u240F\u2410\u2411" + + "\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419" + + "\u241A\u241B\u241C\u241D\u241E\u241F\u2421\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\u4E00\u4E59\u4E01\u4E03\u4E43\u4E5D" + + "\u4E86\u4E8C\u4EBA\u513F\u5165\u516B\u51E0\u5200" + + "\u5201\u529B\u5315\u5341\u535C\u53C8\u4E09\u4E0B" + + "\u4E08\u4E0A\u4E2B\u4E38\u51E1\u4E45\u4E48\u4E5F" + + "\u4E5E\u4E8E\u4EA1\u5140\u5203\u52FA\u5343\u53C9" + + "\u53E3\u571F\u58EB\u5915\u5927\u5973\u5B50\u5B51" + + "\u5B53\u5BF8\u5C0F\u5C22\u5C38\u5C71\u5DDD\u5DE5" + + "\u5DF1\u5DF2\u5DF3\u5DFE\u5E72\u5EFE\u5F0B\u5F13" + + "\u624D\u4E11\u4E10\u4E0D\u4E2D\u4E30\u4E39\u4E4B" + + "\u5C39\u4E88\u4E91\u4E95\u4E92\u4E94\u4EA2\u4EC1" + + "\u4EC0\u4EC3\u4EC6\u4EC7\u4ECD\u4ECA\u4ECB\u4EC4" + + "\u5143\u5141\u5167\u516D\u516E\u516C\u5197\u51F6" + + "\u5206\u5207\u5208\u52FB\u52FE\u52FF\u5316\u5339" + + "\u5348\u5347\u5345\u535E\u5384\u53CB\u53CA\u53CD" + + "\u58EC\u5929\u592B\u592A\u592D\u5B54\u5C11\u5C24" + + "\u5C3A\u5C6F\u5DF4\u5E7B\u5EFF\u5F14\u5F15\u5FC3" + + "\u6208\u6236\u624B\u624E\u652F\u6587\u6597\u65A4" + + "\u65B9\u65E5\u66F0\u6708\u6728\u6B20\u6B62\u6B79" + + "\u6BCB\u6BD4\u6BDB\u6C0F\u6C34\u706B\u722A\u7236" + + "\u723B\u7247\u7259\u725B\u72AC\u738B\u4E19\u4E16" + + "\u4E15\u4E14\u4E18\u4E3B\u4E4D\u4E4F\u4E4E\u4EE5" + + "\u4ED8\u4ED4\u4ED5\u4ED6\u4ED7\u4EE3\u4EE4\u4ED9" + + "\u4EDE\u5145\u5144\u5189\u518A\u51AC\u51F9\u51FA" + + "\u51F8\u520A\u52A0\u529F\u5305\u5306\u5317\u531D" + + "\u4EDF\u534A\u5349\u5361\u5360\u536F\u536E\u53BB" + + "\u53EF\u53E4\u53F3\u53EC\u53EE\u53E9\u53E8\u53FC" + + "\u53F8\u53F5\u53EB\u53E6\u53EA\u53F2\u53F1\u53F0" + + "\u53E5\u53ED\u53FB\u56DB\u56DA\u5916\u592E\u5931" + + "\u5974\u5976\u5B55\u5B83\u5C3C\u5DE8\u5DE7\u5DE6" + + "\u5E02\u5E03\u5E73\u5E7C\u5F01\u5F18\u5F17\u5FC5" + + "\u620A\u6253\u6254\u6252\u6251\u65A5\u65E6\u672E" + + "\u672C\u672A\u672B\u672D\u6B63\u6BCD\u6C11\u6C10" + + "\u6C38\u6C41\u6C40\u6C3E\u72AF\u7384\u7389\u74DC" + + "\u74E6\u7518\u751F\u7528\u7529\u7530\u7531\u7532" + + "\u7533\u758B\u767D\u76AE\u76BF\u76EE\u77DB\u77E2" + + "\u77F3\u793A\u79BE\u7A74\u7ACB\u4E1E\u4E1F\u4E52" + + "\u4E53\u4E69\u4E99\u4EA4\u4EA6\u4EA5\u4EFF\u4F09" + + "\u4F19\u4F0A\u4F15\u4F0D\u4F10\u4F11\u4F0F\u4EF2" + + "\u4EF6\u4EFB\u4EF0\u4EF3\u4EFD\u4F01\u4F0B\u5149" + + "\u5147\u5146\u5148\u5168\u5171\u518D\u51B0\u5217" + + "\u5211\u5212\u520E\u5216\u52A3\u5308\u5321\u5320" + + "\u5370\u5371\u5409\u540F\u540C\u540A\u5410\u5401" + + "\u540B\u5404\u5411\u540D\u5408\u5403\u540E\u5406" + + "\u5412\u56E0\u56DE\u56DD\u5733\u5730\u5728\u572D" + + "\u572C\u572F\u5729\u5919\u591A\u5937\u5938\u5984" + + "\u5978\u5983\u597D\u5979\u5982\u5981\u5B57\u5B58" + + "\u5B87\u5B88\u5B85\u5B89\u5BFA\u5C16\u5C79\u5DDE" + + "\u5E06\u5E76\u5E74\u5F0F\u5F1B\u5FD9\u5FD6\u620E" + + "\u620C\u620D\u6210\u6263\u625B\u6258\u6536\u65E9" + + "\u65E8\u65EC\u65ED\u66F2\u66F3\u6709\u673D\u6734" + + "\u6731\u6735\u6B21\u6B64\u6B7B\u6C16\u6C5D\u6C57" + + "\u6C59\u6C5F\u6C60\u6C50\u6C55\u6C61\u6C5B\u6C4D" + + "\u6C4E\u7070\u725F\u725D\u767E\u7AF9\u7C73\u7CF8" + + "\u7F36\u7F8A\u7FBD\u8001\u8003\u800C\u8012\u8033" + + "\u807F\u8089\u808B\u808C\u81E3\u81EA\u81F3\u81FC" + + "\u820C\u821B\u821F\u826E\u8272\u827E\u866B\u8840" + + "\u884C\u8863\u897F\u9621\u4E32\u4EA8\u4F4D\u4F4F" + + "\u4F47\u4F57\u4F5E\u4F34\u4F5B\u4F55\u4F30\u4F50" + + "\u4F51\u4F3D\u4F3A\u4F38\u4F43\u4F54\u4F3C\u4F46" + + "\u4F63\u4F5C\u4F60\u4F2F\u4F4E\u4F36\u4F59\u4F5D" + + "\u4F48\u4F5A\u514C\u514B\u514D\u5175\u51B6\u51B7" + + "\u5225\u5224\u5229\u522A\u5228\u52AB\u52A9\u52AA" + + "\u52AC\u5323\u5373\u5375\u541D\u542D\u541E\u543E" + + "\u5426\u544E\u5427\u5446\u5443\u5433\u5448\u5442" + + "\u541B\u5429\u544A\u5439\u543B\u5438\u542E\u5435" + + "\u5436\u5420\u543C\u5440\u5431\u542B\u541F\u542C" + + "\u56EA\u56F0\u56E4\u56EB\u574A\u5751\u5740\u574D" + + "\u5747\u574E\u573E\u5750\u574F\u573B\u58EF\u593E" + + "\u599D\u5992\u59A8\u599E\u59A3\u5999\u5996\u598D" + + "\u59A4\u5993\u598A\u59A5\u5B5D\u5B5C\u5B5A\u5B5B" + + "\u5B8C\u5B8B\u5B8F\u5C2C\u5C40\u5C41\u5C3F\u5C3E" + + "\u5C90\u5C91\u5C94\u5C8C\u5DEB\u5E0C\u5E8F\u5E87" + + "\u5E8A\u5EF7\u5F04\u5F1F\u5F64\u5F62\u5F77\u5F79" + + "\u5FD8\u5FCC\u5FD7\u5FCD\u5FF1\u5FEB\u5FF8\u5FEA" + + "\u6212\u6211\u6284\u6297\u6296\u6280\u6276\u6289" + + "\u626D\u628A\u627C\u627E\u6279\u6273\u6292\u626F" + + "\u6298\u626E\u6295\u6293\u6291\u6286\u6539\u653B" + + "\u6538\u65F1\u66F4\u675F\u674E\u674F\u6750\u6751" + + "\u675C\u6756\u675E\u6749\u6746\u6760\u6753\u6757" + + "\u6B65\u6BCF\u6C42\u6C5E\u6C99\u6C81\u6C88\u6C89" + + "\u6C85\u6C9B\u6C6A\u6C7A\u6C90\u6C70\u6C8C\u6C68" + + "\u6C96\u6C92\u6C7D\u6C83\u6C72\u6C7E\u6C74\u6C86" + + "\u6C76\u6C8D\u6C94\u6C98\u6C82\u7076\u707C\u707D" + + "\u7078\u7262\u7261\u7260\u72C4\u72C2\u7396\u752C" + + "\u752B\u7537\u7538\u7682\u76EF\u77E3\u79C1\u79C0" + + "\u79BF\u7A76\u7CFB\u7F55\u8096\u8093\u809D\u8098" + + "\u809B\u809A\u80B2\u826F\u8292\u828B\u828D\u898B" + + "\u89D2\u8A00\u8C37\u8C46\u8C55\u8C9D\u8D64\u8D70" + + "\u8DB3\u8EAB\u8ECA\u8F9B\u8FB0\u8FC2\u8FC6\u8FC5" + + "\u8FC4\u5DE1\u9091\u90A2\u90AA\u90A6\u90A3\u9149" + + "\u91C6\u91CC\u9632\u962E\u9631\u962A\u962C\u4E26" + + "\u4E56\u4E73\u4E8B\u4E9B\u4E9E\u4EAB\u4EAC\u4F6F" + + "\u4F9D\u4F8D\u4F73\u4F7F\u4F6C\u4F9B\u4F8B\u4F86" + + "\u4F83\u4F70\u4F75\u4F88\u4F69\u4F7B\u4F96\u4F7E" + + "\u4F8F\u4F91\u4F7A\u5154\u5152\u5155\u5169\u5177" + + "\u5176\u5178\u51BD\u51FD\u523B\u5238\u5237\u523A" + + "\u5230\u522E\u5236\u5241\u52BE\u52BB\u5352\u5354" + + "\u5353\u5351\u5366\u5377\u5378\u5379\u53D6\u53D4" + + "\u53D7\u5473\u5475\u5496\u5478\u5495\u5480\u547B" + + "\u5477\u5484\u5492\u5486\u547C\u5490\u5471\u5476" + + "\u548C\u549A\u5462\u5468\u548B\u547D\u548E\u56FA" + + "\u5783\u5777\u576A\u5769\u5761\u5766\u5764\u577C" + + "\u591C\u5949\u5947\u5948\u5944\u5954\u59BE\u59BB" + + "\u59D4\u59B9\u59AE\u59D1\u59C6\u59D0\u59CD\u59CB" + + "\u59D3\u59CA\u59AF\u59B3\u59D2\u59C5\u5B5F\u5B64" + + "\u5B63\u5B97\u5B9A\u5B98\u5B9C\u5B99\u5B9B\u5C1A" + + "\u5C48\u5C45\u5C46\u5CB7\u5CA1\u5CB8\u5CA9\u5CAB" + + "\u5CB1\u5CB3\u5E18\u5E1A\u5E16\u5E15\u5E1B\u5E11" + + "\u5E78\u5E9A\u5E97\u5E9C\u5E95\u5E96\u5EF6\u5F26" + + "\u5F27\u5F29\u5F80\u5F81\u5F7F\u5F7C\u5FDD\u5FE0" + + "\u5FFD\u5FF5\u5FFF\u600F\u6014\u602F\u6035\u6016" + + "\u602A\u6015\u6021\u6027\u6029\u602B\u601B\u6216" + + "\u6215\u623F\u623E\u6240\u627F\u62C9\u62CC\u62C4" + + "\u62BF\u62C2\u62B9\u62D2\u62DB\u62AB\u62D3\u62D4" + + "\u62CB\u62C8\u62A8\u62BD\u62BC\u62D0\u62D9\u62C7" + + "\u62CD\u62B5\u62DA\u62B1\u62D8\u62D6\u62D7\u62C6" + + "\u62AC\u62CE\u653E\u65A7\u65BC\u65FA\u6614\u6613" + + "\u660C\u6606\u6602\u660E\u6600\u660F\u6615\u660A" + + "\u6607\u670D\u670B\u676D\u678B\u6795\u6771\u679C" + + "\u6773\u6777\u6787\u679D\u6797\u676F\u6770\u677F" + + "\u6789\u677E\u6790\u6775\u679A\u6793\u677C\u676A" + + "\u6772\u6B23\u6B66\u6B67\u6B7F\u6C13\u6C1B\u6CE3" + + "\u6CE8\u6CF3\u6CB1\u6CCC\u6CE5\u6CB3\u6CBD\u6CBE" + + "\u6CBC\u6CE2\u6CAB\u6CD5\u6CD3\u6CB8\u6CC4\u6CB9" + + "\u6CC1\u6CAE\u6CD7\u6CC5\u6CF1\u6CBF\u6CBB\u6CE1" + + "\u6CDB\u6CCA\u6CAC\u6CEF\u6CDC\u6CD6\u6CE0\u7095" + + "\u708E\u7092\u708A\u7099\u722C\u722D\u7238\u7248" + + "\u7267\u7269\u72C0\u72CE\u72D9\u72D7\u72D0\u73A9" + + "\u73A8\u739F\u73AB\u73A5\u753D\u759D\u7599\u759A" + + "\u7684\u76C2\u76F2\u76F4\u77E5\u77FD\u793E\u7940" + + "\u7941\u79C9\u79C8\u7A7A\u7A79\u7AFA\u7CFE\u7F54" + + "\u7F8C\u7F8B\u8005\u80BA\u80A5\u80A2\u80B1\u80A1" + + "\u80AB\u80A9\u80B4\u80AA\u80AF\u81E5\u81FE\u820D" + + "\u82B3\u829D\u8299\u82AD\u82BD\u829F\u82B9\u82B1" + + "\u82AC\u82A5\u82AF\u82B8\u82A3\u82B0\u82BE\u82B7" + + "\u864E\u8671\u521D\u8868\u8ECB\u8FCE\u8FD4\u8FD1" + + "\u90B5\u90B8\u90B1\u90B6\u91C7\u91D1\u9577\u9580" + + "\u961C\u9640\u963F\u963B\u9644\u9642\u96B9\u96E8" + + "\u9752\u975E\u4E9F\u4EAD\u4EAE\u4FE1\u4FB5\u4FAF" + + "\u4FBF\u4FE0\u4FD1\u4FCF\u4FDD\u4FC3\u4FB6\u4FD8" + + "\u4FDF\u4FCA\u4FD7\u4FAE\u4FD0\u4FC4\u4FC2\u4FDA" + + "\u4FCE\u4FDE\u4FB7\u5157\u5192\u5191\u51A0\u524E" + + "\u5243\u524A\u524D\u524C\u524B\u5247\u52C7\u52C9" + + "\u52C3\u52C1\u530D\u5357\u537B\u539A\u53DB\u54AC" + + "\u54C0\u54A8\u54CE\u54C9\u54B8\u54A6\u54B3\u54C7" + + "\u54C2\u54BD\u54AA\u54C1\u54C4\u54C8\u54AF\u54AB" + + "\u54B1\u54BB\u54A9\u54A7\u54BF\u56FF\u5782\u578B" + + "\u57A0\u57A3\u57A2\u57CE\u57AE\u5793\u5955\u5951" + + "\u594F\u594E\u5950\u59DC\u59D8\u59FF\u59E3\u59E8" + + "\u5A03\u59E5\u59EA\u59DA\u59E6\u5A01\u59FB\u5B69" + + "\u5BA3\u5BA6\u5BA4\u5BA2\u5BA5\u5C01\u5C4E\u5C4F" + + "\u5C4D\u5C4B\u5CD9\u5CD2\u5DF7\u5E1D\u5E25\u5E1F" + + "\u5E7D\u5EA0\u5EA6\u5EFA\u5F08\u5F2D\u5F65\u5F88" + + "\u5F85\u5F8A\u5F8B\u5F87\u5F8C\u5F89\u6012\u601D" + + "\u6020\u6025\u600E\u6028\u604D\u6070\u6068\u6062" + + "\u6046\u6043\u606C\u606B\u606A\u6064\u6241\u62DC" + + "\u6316\u6309\u62FC\u62ED\u6301\u62EE\u62FD\u6307" + + "\u62F1\u62F7\u62EF\u62EC\u62FE\u62F4\u6311\u6302" + + "\u653F\u6545\u65AB\u65BD\u65E2\u6625\u662D\u6620" + + "\u6627\u662F\u661F\u6628\u6631\u6624\u66F7\u67FF" + + "\u67D3\u67F1\u67D4\u67D0\u67EC\u67B6\u67AF\u67F5" + + "\u67E9\u67EF\u67C4\u67D1\u67B4\u67DA\u67E5\u67B8" + + "\u67CF\u67DE\u67F3\u67B0\u67D9\u67E2\u67DD\u67D2" + + "\u6B6A\u6B83\u6B86\u6BB5\u6BD2\u6BD7\u6C1F\u6CC9" + + "\u6D0B\u6D32\u6D2A\u6D41\u6D25\u6D0C\u6D31\u6D1E" + + "\u6D17\u6D3B\u6D3D\u6D3E\u6D36\u6D1B\u6CF5\u6D39" + + "\u6D27\u6D38\u6D29\u6D2E\u6D35\u6D0E\u6D2B\u70AB" + + "\u70BA\u70B3\u70AC\u70AF\u70AD\u70B8\u70AE\u70A4" + + "\u7230\u7272\u726F\u7274\u72E9\u72E0\u72E1\u73B7" + + "\u73CA\u73BB\u73B2\u73CD\u73C0\u73B3\u751A\u752D" + + "\u754F\u754C\u754E\u754B\u75AB\u75A4\u75A5\u75A2" + + "\u75A3\u7678\u7686\u7687\u7688\u76C8\u76C6\u76C3" + + "\u76C5\u7701\u76F9\u76F8\u7709\u770B\u76FE\u76FC" + + "\u7707\u77DC\u7802\u7814\u780C\u780D\u7946\u7949" + + "\u7948\u7947\u79B9\u79BA\u79D1\u79D2\u79CB\u7A7F" + + "\u7A81\u7AFF\u7AFD\u7C7D\u7D02\u7D05\u7D00\u7D09" + + "\u7D07\u7D04\u7D06\u7F38\u7F8E\u7FBF\u8010\u800D" + + "\u8011\u8036\u80D6\u80E5\u80DA\u80C3\u80C4\u80CC" + + "\u80E1\u80DB\u80CE\u80DE\u80E4\u80DD\u81F4\u8222" + + "\u82E7\u8303\u8305\u82E3\u82DB\u82E6\u8304\u82E5" + + "\u8302\u8309\u82D2\u82D7\u82F1\u8301\u82DC\u82D4" + + "\u82D1\u82DE\u82D3\u82DF\u82EF\u8306\u8650\u8679" + + "\u867B\u867A\u884D\u886B\u8981\u89D4\u8A08\u8A02" + + "\u8A03\u8C9E\u8CA0\u8D74\u8D73\u8DB4\u8ECD\u8ECC" + + "\u8FF0\u8FE6\u8FE2\u8FEA\u8FE5\u8FED\u8FEB\u8FE4" + + "\u8FE8\u90CA\u90CE\u90C1\u90C3\u914B\u914A\u91CD" + + "\u9582\u9650\u964B\u964C\u964D\u9762\u9769\u97CB" + + "\u97ED\u97F3\u9801\u98A8\u98DB\u98DF\u9996\u9999" + + "\u4E58\u4EB3\u500C\u500D\u5023\u4FEF\u5026\u5025" + + "\u4FF8\u5029\u5016\u5006\u503C\u501F\u501A\u5012" + + "\u5011\u4FFA\u5000\u5014\u5028\u4FF1\u5021\u500B" + + "\u5019\u5018\u4FF3\u4FEE\u502D\u502A\u4FFE\u502B" + + "\u5009\u517C\u51A4\u51A5\u51A2\u51CD\u51CC\u51C6" + + "\u51CB\u5256\u525C\u5254\u525B\u525D\u532A\u537F" + + "\u539F\u539D\u53DF\u54E8\u5510\u5501\u5537\u54FC" + + "\u54E5\u54F2\u5506\u54FA\u5514\u54E9\u54ED\u54E1" + + "\u5509\u54EE\u54EA\u54E6\u5527\u5507\u54FD\u550F" + + "\u5703\u5704\u57C2\u57D4\u57CB\u57C3\u5809\u590F" + + "\u5957\u5958\u595A\u5A11\u5A18\u5A1C\u5A1F\u5A1B" + + "\u5A13\u59EC\u5A20\u5A23\u5A29\u5A25\u5A0C\u5A09" + + "\u5B6B\u5C58\u5BB0\u5BB3\u5BB6\u5BB4\u5BAE\u5BB5" + + "\u5BB9\u5BB8\u5C04\u5C51\u5C55\u5C50\u5CED\u5CFD" + + "\u5CFB\u5CEA\u5CE8\u5CF0\u5CF6\u5D01\u5CF4\u5DEE" + + "\u5E2D\u5E2B\u5EAB\u5EAD\u5EA7\u5F31\u5F92\u5F91" + + "\u5F90\u6059\u6063\u6065\u6050\u6055\u606D\u6069" + + "\u606F\u6084\u609F\u609A\u608D\u6094\u608C\u6085" + + "\u6096\u6247\u62F3\u6308\u62FF\u634E\u633E\u632F" + + "\u6355\u6342\u6346\u634F\u6349\u633A\u6350\u633D" + + "\u632A\u632B\u6328\u634D\u634C\u6548\u6549\u6599" + + "\u65C1\u65C5\u6642\u6649\u664F\u6643\u6652\u664C" + + "\u6645\u6641\u66F8\u6714\u6715\u6717\u6821\u6838" + + "\u6848\u6846\u6853\u6839\u6842\u6854\u6829\u68B3" + + "\u6817\u684C\u6851\u683D\u67F4\u6850\u6840\u683C" + + "\u6843\u682A\u6845\u6813\u6818\u6841\u6B8A\u6B89" + + "\u6BB7\u6C23\u6C27\u6C28\u6C26\u6C24\u6CF0\u6D6A" + + "\u6D95\u6D88\u6D87\u6D66\u6D78\u6D77\u6D59\u6D93" + + "\u6D6C\u6D89\u6D6E\u6D5A\u6D74\u6D69\u6D8C\u6D8A" + + "\u6D79\u6D85\u6D65\u6D94\u70CA\u70D8\u70E4\u70D9" + + "\u70C8\u70CF\u7239\u7279\u72FC\u72F9\u72FD\u72F8" + + "\u72F7\u7386\u73ED\u7409\u73EE\u73E0\u73EA\u73DE" + + "\u7554\u755D\u755C\u755A\u7559\u75BE\u75C5\u75C7" + + "\u75B2\u75B3\u75BD\u75BC\u75B9\u75C2\u75B8\u768B" + + "\u76B0\u76CA\u76CD\u76CE\u7729\u771F\u7720\u7728" + + "\u77E9\u7830\u7827\u7838\u781D\u7834\u7837\u7825" + + "\u782D\u7820\u781F\u7832\u7955\u7950\u7960\u795F" + + "\u7956\u795E\u795D\u7957\u795A\u79E4\u79E3\u79E7" + + "\u79DF\u79E6\u79E9\u79D8\u7A84\u7A88\u7AD9\u7B06" + + "\u7B11\u7C89\u7D21\u7D17\u7D0B\u7D0A\u7D20\u7D22" + + "\u7D14\u7D10\u7D15\u7D1A\u7D1C\u7D0D\u7D19\u7D1B" + + "\u7F3A\u7F5F\u7F94\u7FC5\u7FC1\u8006\u8004\u8018" + + "\u8015\u8019\u8017\u803D\u803F\u80F1\u8102\u80F0" + + "\u8105\u80ED\u80F4\u8106\u80F8\u80F3\u8108\u80FD" + + "\u810A\u80FC\u80EF\u81ED\u81EC\u8200\u8210\u822A" + + "\u822B\u8228\u822C\u82BB\u832B\u8352\u8354\u834A" + + "\u8338\u8350\u8349\u8335\u8334\u834F\u8332\u8339" + + "\u8336\u8317\u8340\u8331\u8328\u8343\u8654\u868A" + + "\u86AA\u8693\u86A4\u86A9\u868C\u86A3\u869C\u8870" + + "\u8877\u8881\u8882\u887D\u8879\u8A18\u8A10\u8A0E" + + "\u8A0C\u8A15\u8A0A\u8A17\u8A13\u8A16\u8A0F\u8A11" + + "\u8C48\u8C7A\u8C79\u8CA1\u8CA2\u8D77\u8EAC\u8ED2" + + "\u8ED4\u8ECF\u8FB1\u9001\u9006\u8FF7\u9000\u8FFA" + + "\u8FF4\u9003\u8FFD\u9005\u8FF8\u9095\u90E1\u90DD" + + "\u90E2\u9152\u914D\u914C\u91D8\u91DD\u91D7\u91DC" + + "\u91D9\u9583\u9662\u9663\u9661\u965B\u965D\u9664" + + "\u9658\u965E\u96BB\u98E2\u99AC\u9AA8\u9AD8\u9B25" + + "\u9B32\u9B3C\u4E7E\u507A\u507D\u505C\u5047\u5043" + + "\u504C\u505A\u5049\u5065\u5076\u504E\u5055\u5075" + + "\u5074\u5077\u504F\u500F\u506F\u506D\u515C\u5195" + + "\u51F0\u526A\u526F\u52D2\u52D9\u52D8\u52D5\u5310" + + "\u530F\u5319\u533F\u5340\u533E\u53C3\u66FC\u5546" + + "\u556A\u5566\u5544\u555E\u5561\u5543\u554A\u5531" + + "\u5556\u554F\u5555\u552F\u5564\u5538\u552E\u555C" + + "\u552C\u5563\u5533\u5541\u5557\u5708\u570B\u5709" + + "\u57DF\u5805\u580A\u5806\u57E0\u57E4\u57FA\u5802" + + "\u5835\u57F7\u57F9\u5920\u5962\u5A36\u5A41\u5A49" + + "\u5A66\u5A6A\u5A40\u5A3C\u5A62\u5A5A\u5A46\u5A4A" + + "\u5B70\u5BC7\u5BC5\u5BC4\u5BC2\u5BBF\u5BC6\u5C09" + + "\u5C08\u5C07\u5C60\u5C5C\u5C5D\u5D07\u5D06\u5D0E" + + "\u5D1B\u5D16\u5D22\u5D11\u5D29\u5D14\u5D19\u5D24" + + "\u5D27\u5D17\u5DE2\u5E38\u5E36\u5E33\u5E37\u5EB7" + + "\u5EB8\u5EB6\u5EB5\u5EBE\u5F35\u5F37\u5F57\u5F6C" + + "\u5F69\u5F6B\u5F97\u5F99\u5F9E\u5F98\u5FA1\u5FA0" + + "\u5F9C\u607F\u60A3\u6089\u60A0\u60A8\u60CB\u60B4" + + "\u60E6\u60BD\u60C5\u60BB\u60B5\u60DC\u60BC\u60D8" + + "\u60D5\u60C6\u60DF\u60B8\u60DA\u60C7\u621A\u621B" + + "\u6248\u63A0\u63A7\u6372\u6396\u63A2\u63A5\u6377" + + "\u6367\u6398\u63AA\u6371\u63A9\u6389\u6383\u639B" + + "\u636B\u63A8\u6384\u6388\u6399\u63A1\u63AC\u6392" + + "\u638F\u6380\u637B\u6369\u6368\u637A\u655D\u6556" + + "\u6551\u6559\u6557\u555F\u654F\u6558\u6555\u6554" + + "\u659C\u659B\u65AC\u65CF\u65CB\u65CC\u65CE\u665D" + + "\u665A\u6664\u6668\u6666\u665E\u66F9\u52D7\u671B" + + "\u6881\u68AF\u68A2\u6893\u68B5\u687F\u6876\u68B1" + + "\u68A7\u6897\u68B0\u6883\u68C4\u68AD\u6886\u6885" + + "\u6894\u689D\u68A8\u689F\u68A1\u6882\u6B32\u6BBA" + + "\u6BEB\u6BEC\u6C2B\u6D8E\u6DBC\u6DF3\u6DD9\u6DB2" + + "\u6DE1\u6DCC\u6DE4\u6DFB\u6DFA\u6E05\u6DC7\u6DCB" + + "\u6DAF\u6DD1\u6DAE\u6DDE\u6DF9\u6DB8\u6DF7\u6DF5" + + "\u6DC5\u6DD2\u6E1A\u6DB5\u6DDA\u6DEB\u6DD8\u6DEA" + + "\u6DF1\u6DEE\u6DE8\u6DC6\u6DC4\u6DAA\u6DEC\u6DBF" + + "\u6DE6\u70F9\u7109\u710A\u70FD\u70EF\u723D\u727D" + + "\u7281\u731C\u731B\u7316\u7313\u7319\u7387\u7405" + + "\u740A\u7403\u7406\u73FE\u740D\u74E0\u74F6\u74F7" + + "\u751C\u7522\u7565\u7566\u7562\u7570\u758F\u75D4" + + "\u75D5\u75B5\u75CA\u75CD\u768E\u76D4\u76D2\u76DB" + + "\u7737\u773E\u773C\u7736\u7738\u773A\u786B\u7843" + + "\u784E\u7965\u7968\u796D\u79FB\u7A92\u7A95\u7B20" + + "\u7B28\u7B1B\u7B2C\u7B26\u7B19\u7B1E\u7B2E\u7C92" + + "\u7C97\u7C95\u7D46\u7D43\u7D71\u7D2E\u7D39\u7D3C" + + "\u7D40\u7D30\u7D33\u7D44\u7D2F\u7D42\u7D32\u7D31" + + "\u7F3D\u7F9E\u7F9A\u7FCC\u7FCE\u7FD2\u801C\u804A" + + "\u8046\u812F\u8116\u8123\u812B\u8129\u8130\u8124" + + "\u8202\u8235\u8237\u8236\u8239\u838E\u839E\u8398" + + "\u8378\u83A2\u8396\u83BD\u83AB\u8392\u838A\u8393" + + "\u8389\u83A0\u8377\u837B\u837C\u8386\u83A7\u8655" + + "\u5F6A\u86C7\u86C0\u86B6\u86C4\u86B5\u86C6\u86CB" + + "\u86B1\u86AF\u86C9\u8853\u889E\u8888\u88AB\u8892" + + "\u8896\u888D\u888B\u8993\u898F\u8A2A\u8A1D\u8A23" + + "\u8A25\u8A31\u8A2D\u8A1F\u8A1B\u8A22\u8C49\u8C5A" + + "\u8CA9\u8CAC\u8CAB\u8CA8\u8CAA\u8CA7\u8D67\u8D66" + + "\u8DBE\u8DBA\u8EDB\u8EDF\u9019\u900D\u901A\u9017" + + "\u9023\u901F\u901D\u9010\u9015\u901E\u9020\u900F" + + "\u9022\u9016\u901B\u9014\u90E8\u90ED\u90FD\u9157" + + "\u91CE\u91F5\u91E6\u91E3\u91E7\u91ED\u91E9\u9589" + + "\u966A\u9675\u9673\u9678\u9670\u9674\u9676\u9677" + + "\u966C\u96C0\u96EA\u96E9\u7AE0\u7ADF\u9802\u9803" + + "\u9B5A\u9CE5\u9E75\u9E7F\u9EA5\u9EBB\u50A2\u508D" + + "\u5085\u5099\u5091\u5080\u5096\u5098\u509A\u6700" + + "\u51F1\u5272\u5274\u5275\u5269\u52DE\u52DD\u52DB" + + "\u535A\u53A5\u557B\u5580\u55A7\u557C\u558A\u559D" + + "\u5598\u5582\u559C\u55AA\u5594\u5587\u558B\u5583" + + "\u55B3\u55AE\u559F\u553E\u55B2\u559A\u55BB\u55AC" + + "\u55B1\u557E\u5589\u55AB\u5599\u570D\u582F\u582A" + + "\u5834\u5824\u5830\u5831\u5821\u581D\u5820\u58F9" + + "\u58FA\u5960\u5A77\u5A9A\u5A7F\u5A92\u5A9B\u5AA7" + + "\u5B73\u5B71\u5BD2\u5BCC\u5BD3\u5BD0\u5C0A\u5C0B" + + "\u5C31\u5D4C\u5D50\u5D34\u5D47\u5DFD\u5E45\u5E3D" + + "\u5E40\u5E43\u5E7E\u5ECA\u5EC1\u5EC2\u5EC4\u5F3C" + + "\u5F6D\u5FA9\u5FAA\u5FA8\u60D1\u60E1\u60B2\u60B6" + + "\u60E0\u611C\u6123\u60FA\u6115\u60F0\u60FB\u60F4" + + "\u6168\u60F1\u610E\u60F6\u6109\u6100\u6112\u621F" + + "\u6249\u63A3\u638C\u63CF\u63C0\u63E9\u63C9\u63C6" + + "\u63CD\u63D2\u63E3\u63D0\u63E1\u63D6\u63ED\u63EE" + + "\u6376\u63F4\u63EA\u63DB\u6452\u63DA\u63F9\u655E" + + "\u6566\u6562\u6563\u6591\u6590\u65AF\u666E\u6670" + + "\u6674\u6676\u666F\u6691\u667A\u667E\u6677\u66FE" + + "\u66FF\u671F\u671D\u68FA\u68D5\u68E0\u68D8\u68D7" + + "\u6905\u68DF\u68F5\u68EE\u68E7\u68F9\u68D2\u68F2" + + "\u68E3\u68CB\u68CD\u690D\u6912\u690E\u68C9\u68DA" + + "\u696E\u68FB\u6B3E\u6B3A\u6B3D\u6B98\u6B96\u6BBC" + + "\u6BEF\u6C2E\u6C2F\u6C2C\u6E2F\u6E38\u6E54\u6E21" + + "\u6E32\u6E67\u6E4A\u6E20\u6E25\u6E23\u6E1B\u6E5B" + + "\u6E58\u6E24\u6E56\u6E6E\u6E2D\u6E26\u6E6F\u6E34" + + "\u6E4D\u6E3A\u6E2C\u6E43\u6E1D\u6E3E\u6ECB\u6E89" + + "\u6E19\u6E4E\u6E63\u6E44\u6E72\u6E69\u6E5F\u7119" + + "\u711A\u7126\u7130\u7121\u7136\u716E\u711C\u724C" + + "\u7284\u7280\u7336\u7325\u7334\u7329\u743A\u742A" + + "\u7433\u7422\u7425\u7435\u7436\u7434\u742F\u741B" + + "\u7426\u7428\u7525\u7526\u756B\u756A\u75E2\u75DB" + + "\u75E3\u75D9\u75D8\u75DE\u75E0\u767B\u767C\u7696" + + "\u7693\u76B4\u76DC\u774F\u77ED\u785D\u786C\u786F" + + "\u7A0D\u7A08\u7A0B\u7A05\u7A00\u7A98\u7A97\u7A96" + + "\u7AE5\u7AE3\u7B49\u7B56\u7B46\u7B50\u7B52\u7B54" + + "\u7B4D\u7B4B\u7B4F\u7B51\u7C9F\u7CA5\u7D5E\u7D50" + + "\u7D68\u7D55\u7D2B\u7D6E\u7D72\u7D61\u7D66\u7D62" + + "\u7D70\u7D73\u5584\u7FD4\u7FD5\u800B\u8052\u8085" + + "\u8155\u8154\u814B\u8151\u814E\u8139\u8146\u813E" + + "\u814C\u8153\u8174\u8212\u821C\u83E9\u8403\u83F8" + + "\u840D\u83E0\u83C5\u840B\u83C1\u83EF\u83F1\u83F4" + + "\u8457\u840A\u83F0\u840C\u83CC\u83FD\u83F2\u83CA" + + "\u8438\u840E\u8404\u83DC\u8407\u83D4\u83DF\u865B" + + "\u86DF\u86D9\u86ED\u86D4\u86DB\u86E4\u86D0\u86DE" + + "\u8857\u88C1\u88C2\u88B1\u8983\u8996\u8A3B\u8A60" + + "\u8A55\u8A5E\u8A3C\u8A41\u8A54\u8A5B\u8A50\u8A46" + + "\u8A34\u8A3A\u8A36\u8A56\u8C61\u8C82\u8CAF\u8CBC" + + "\u8CB3\u8CBD\u8CC1\u8CBB\u8CC0\u8CB4\u8CB7\u8CB6" + + "\u8CBF\u8CB8\u8D8A\u8D85\u8D81\u8DCE\u8DDD\u8DCB" + + "\u8DDA\u8DD1\u8DCC\u8DDB\u8DC6\u8EFB\u8EF8\u8EFC" + + "\u8F9C\u902E\u9035\u9031\u9038\u9032\u9036\u9102" + + "\u90F5\u9109\u90FE\u9163\u9165\u91CF\u9214\u9215" + + "\u9223\u9209\u921E\u920D\u9210\u9207\u9211\u9594" + + "\u958F\u958B\u9591\u9593\u9592\u958E\u968A\u968E" + + "\u968B\u967D\u9685\u9686\u968D\u9672\u9684\u96C1" + + "\u96C5\u96C4\u96C6\u96C7\u96EF\u96F2\u97CC\u9805" + + "\u9806\u9808\u98E7\u98EA\u98EF\u98E9\u98F2\u98ED" + + "\u99AE\u99AD\u9EC3\u9ECD\u9ED1\u4E82\u50AD\u50B5" + + "\u50B2\u50B3\u50C5\u50BE\u50AC\u50B7\u50BB\u50AF" + + "\u50C7\u527F\u5277\u527D\u52DF\u52E6\u52E4\u52E2" + + "\u52E3\u532F\u55DF\u55E8\u55D3\u55E6\u55CE\u55DC" + + "\u55C7\u55D1\u55E3\u55E4\u55EF\u55DA\u55E1\u55C5" + + "\u55C6\u55E5\u55C9\u5712\u5713\u585E\u5851\u5858" + + "\u5857\u585A\u5854\u586B\u584C\u586D\u584A\u5862" + + "\u5852\u584B\u5967\u5AC1\u5AC9\u5ACC\u5ABE\u5ABD" + + "\u5ABC\u5AB3\u5AC2\u5AB2\u5D69\u5D6F\u5E4C\u5E79" + + "\u5EC9\u5EC8\u5F12\u5F59\u5FAC\u5FAE\u611A\u610F" + + "\u6148\u611F\u60F3\u611B\u60F9\u6101\u6108\u614E" + + "\u614C\u6144\u614D\u613E\u6134\u6127\u610D\u6106" + + "\u6137\u6221\u6222\u6413\u643E\u641E\u642A\u642D" + + "\u643D\u642C\u640F\u641C\u6414\u640D\u6436\u6416" + + "\u6417\u6406\u656C\u659F\u65B0\u6697\u6689\u6687" + + "\u6688\u6696\u6684\u6698\u668D\u6703\u6994\u696D" + + "\u695A\u6977\u6960\u6954\u6975\u6930\u6982\u694A" + + "\u6968\u696B\u695E\u6953\u6979\u6986\u695D\u6963" + + "\u695B\u6B47\u6B72\u6BC0\u6BBF\u6BD3\u6BFD\u6EA2" + + "\u6EAF\u6ED3\u6EB6\u6EC2\u6E90\u6E9D\u6EC7\u6EC5" + + "\u6EA5\u6E98\u6EBC\u6EBA\u6EAB\u6ED1\u6E96\u6E9C" + + "\u6EC4\u6ED4\u6EAA\u6EA7\u6EB4\u714E\u7159\u7169" + + "\u7164\u7149\u7167\u715C\u716C\u7166\u714C\u7165" + + "\u715E\u7146\u7168\u7156\u723A\u7252\u7337\u7345" + + "\u733F\u733E\u746F\u745A\u7455\u745F\u745E\u7441" + + "\u743F\u7459\u745B\u745C\u7576\u7578\u7600\u75F0" + + "\u7601\u75F2\u75F1\u75FA\u75FF\u75F4\u75F3\u76DE" + + "\u76DF\u775B\u776B\u7766\u775E\u7763\u7779\u776A" + + "\u776C\u775C\u7765\u7768\u7762\u77EE\u788E\u78B0" + + "\u7897\u7898\u788C\u7889\u787C\u7891\u7893\u787F" + + "\u797A\u797F\u7981\u842C\u79BD\u7A1C\u7A1A\u7A20" + + "\u7A14\u7A1F\u7A1E\u7A9F\u7AA0\u7B77\u7BC0\u7B60" + + "\u7B6E\u7B67\u7CB1\u7CB3\u7CB5\u7D93\u7D79\u7D91" + + "\u7D81\u7D8F\u7D5B\u7F6E\u7F69\u7F6A\u7F72\u7FA9" + + "\u7FA8\u7FA4\u8056\u8058\u8086\u8084\u8171\u8170" + + "\u8178\u8165\u816E\u8173\u816B\u8179\u817A\u8166" + + "\u8205\u8247\u8482\u8477\u843D\u8431\u8475\u8466" + + "\u846B\u8449\u846C\u845B\u843C\u8435\u8461\u8463" + + "\u8469\u846D\u8446\u865E\u865C\u865F\u86F9\u8713" + + "\u8708\u8707\u8700\u86FE\u86FB\u8702\u8703\u8706" + + "\u870A\u8859\u88DF\u88D4\u88D9\u88DC\u88D8\u88DD" + + "\u88E1\u88CA\u88D5\u88D2\u899C\u89E3\u8A6B\u8A72" + + "\u8A73\u8A66\u8A69\u8A70\u8A87\u8A7C\u8A63\u8AA0" + + "\u8A71\u8A85\u8A6D\u8A62\u8A6E\u8A6C\u8A79\u8A7B" + + "\u8A3E\u8A68\u8C62\u8C8A\u8C89\u8CCA\u8CC7\u8CC8" + + "\u8CC4\u8CB2\u8CC3\u8CC2\u8CC5\u8DE1\u8DDF\u8DE8" + + "\u8DEF\u8DF3\u8DFA\u8DEA\u8DE4\u8DE6\u8EB2\u8F03" + + "\u8F09\u8EFE\u8F0A\u8F9F\u8FB2\u904B\u904A\u9053" + + "\u9042\u9054\u903C\u9055\u9050\u9047\u904F\u904E" + + "\u904D\u9051\u903E\u9041\u9112\u9117\u916C\u916A" + + "\u9169\u91C9\u9237\u9257\u9238\u923D\u9240\u923E" + + "\u925B\u924B\u9264\u9251\u9234\u9249\u924D\u9245" + + "\u9239\u923F\u925A\u9598\u9698\u9694\u9695\u96CD" + + "\u96CB\u96C9\u96CA\u96F7\u96FB\u96F9\u96F6\u9756" + + "\u9774\u9776\u9810\u9811\u9813\u980A\u9812\u980C" + + "\u98FC\u98F4\u98FD\u98FE\u99B3\u99B1\u99B4\u9AE1" + + "\u9CE9\u9E82\u9F0E\u9F13\u9F20\u50E7\u50EE\u50E5" + + "\u50D6\u50ED\u50DA\u50D5\u50CF\u50D1\u50F1\u50CE" + + "\u50E9\u5162\u51F3\u5283\u5282\u5331\u53AD\u55FE" + + "\u5600\u561B\u5617\u55FD\u5614\u5606\u5609\u560D" + + "\u560E\u55F7\u5616\u561F\u5608\u5610\u55F6\u5718" + + "\u5716\u5875\u587E\u5883\u5893\u588A\u5879\u5885" + + "\u587D\u58FD\u5925\u5922\u5924\u596A\u5969\u5AE1" + + "\u5AE6\u5AE9\u5AD7\u5AD6\u5AD8\u5AE3\u5B75\u5BDE" + + "\u5BE7\u5BE1\u5BE5\u5BE6\u5BE8\u5BE2\u5BE4\u5BDF" + + "\u5C0D\u5C62\u5D84\u5D87\u5E5B\u5E63\u5E55\u5E57" + + "\u5E54\u5ED3\u5ED6\u5F0A\u5F46\u5F70\u5FB9\u6147" + + "\u613F\u614B\u6177\u6162\u6163\u615F\u615A\u6158" + + "\u6175\u622A\u6487\u6458\u6454\u64A4\u6478\u645F" + + "\u647A\u6451\u6467\u6434\u646D\u647B\u6572\u65A1" + + "\u65D7\u65D6\u66A2\u66A8\u669D\u699C\u69A8\u6995" + + "\u69C1\u69AE\u69D3\u69CB\u699B\u69B7\u69BB\u69AB" + + "\u69B4\u69D0\u69CD\u69AD\u69CC\u69A6\u69C3\u69A3" + + "\u6B49\u6B4C\u6C33\u6F33\u6F14\u6EFE\u6F13\u6EF4" + + "\u6F29\u6F3E\u6F20\u6F2C\u6F0F\u6F02\u6F22\u6EFF" + + "\u6EEF\u6F06\u6F31\u6F38\u6F32\u6F23\u6F15\u6F2B" + + "\u6F2F\u6F88\u6F2A\u6EEC\u6F01\u6EF2\u6ECC\u6EF7" + + "\u7194\u7199\u717D\u718A\u7184\u7192\u723E\u7292" + + "\u7296\u7344\u7350\u7464\u7463\u746A\u7470\u746D" + + "\u7504\u7591\u7627\u760D\u760B\u7609\u7613\u76E1" + + "\u76E3\u7784\u777D\u777F\u7761\u78C1\u789F\u78A7" + + "\u78B3\u78A9\u78A3\u798E\u798F\u798D\u7A2E\u7A31" + + "\u7AAA\u7AA9\u7AED\u7AEF\u7BA1\u7B95\u7B8B\u7B75" + + "\u7B97\u7B9D\u7B94\u7B8F\u7BB8\u7B87\u7B84\u7CB9" + + "\u7CBD\u7CBE\u7DBB\u7DB0\u7D9C\u7DBD\u7DBE\u7DA0" + + "\u7DCA\u7DB4\u7DB2\u7DB1\u7DBA\u7DA2\u7DBF\u7DB5" + + "\u7DB8\u7DAD\u7DD2\u7DC7\u7DAC\u7F70\u7FE0\u7FE1" + + "\u7FDF\u805E\u805A\u8087\u8150\u8180\u818F\u8188" + + "\u818A\u817F\u8182\u81E7\u81FA\u8207\u8214\u821E" + + "\u824B\u84C9\u84BF\u84C6\u84C4\u8499\u849E\u84B2" + + "\u849C\u84CB\u84B8\u84C0\u84D3\u8490\u84BC\u84D1" + + "\u84CA\u873F\u871C\u873B\u8722\u8725\u8734\u8718" + + "\u8755\u8737\u8729\u88F3\u8902\u88F4\u88F9\u88F8" + + "\u88FD\u88E8\u891A\u88EF\u8AA6\u8A8C\u8A9E\u8AA3" + + "\u8A8D\u8AA1\u8A93\u8AA4\u8AAA\u8AA5\u8AA8\u8A98" + + "\u8A91\u8A9A\u8AA7\u8C6A\u8C8D\u8C8C\u8CD3\u8CD1" + + "\u8CD2\u8D6B\u8D99\u8D95\u8DFC\u8F14\u8F12\u8F15" + + "\u8F13\u8FA3\u9060\u9058\u905C\u9063\u9059\u905E" + + "\u9062\u905D\u905B\u9119\u9118\u911E\u9175\u9178" + + "\u9177\u9174\u9278\u92AC\u9280\u9285\u9298\u9296" + + "\u927B\u9293\u929C\u92A8\u927C\u9291\u95A1\u95A8" + + "\u95A9\u95A3\u95A5\u95A4\u9699\u969C\u969B\u96CC" + + "\u96D2\u9700\u977C\u9785\u97F6\u9817\u9818\u98AF" + + "\u98B1\u9903\u9905\u990C\u9909\u99C1\u9AAF\u9AB0" + + "\u9AE6\u9B41\u9B42\u9CF4\u9CF6\u9CF3\u9EBC\u9F3B" + + "\u9F4A\u5104\u5100\u50FB\u50F5\u50F9\u5102\u5108" + + "\u5109\u5105\u51DC\u5287\u5288\u5289\u528D\u528A" + + "\u52F0\u53B2\u562E\u563B\u5639\u5632\u563F\u5634" + + "\u5629\u5653\u564E\u5657\u5674\u5636\u562F\u5630" + + "\u5880\u589F\u589E\u58B3\u589C\u58AE\u58A9\u58A6" + + "\u596D\u5B09\u5AFB\u5B0B\u5AF5\u5B0C\u5B08\u5BEE" + + "\u5BEC\u5BE9\u5BEB\u5C64\u5C65\u5D9D\u5D94\u5E62" + + "\u5E5F\u5E61\u5EE2\u5EDA\u5EDF\u5EDD\u5EE3\u5EE0" + + "\u5F48\u5F71\u5FB7\u5FB5\u6176\u6167\u616E\u615D" + + "\u6155\u6182\u617C\u6170\u616B\u617E\u61A7\u6190" + + "\u61AB\u618E\u61AC\u619A\u61A4\u6194\u61AE\u622E" + + "\u6469\u646F\u6479\u649E\u64B2\u6488\u6490\u64B0" + + "\u64A5\u6493\u6495\u64A9\u6492\u64AE\u64AD\u64AB" + + "\u649A\u64AC\u6499\u64A2\u64B3\u6575\u6577\u6578" + + "\u66AE\u66AB\u66B4\u66B1\u6A23\u6A1F\u69E8\u6A01" + + "\u6A1E\u6A19\u69FD\u6A21\u6A13\u6A0A\u69F3\u6A02" + + "\u6A05\u69ED\u6A11\u6B50\u6B4E\u6BA4\u6BC5\u6BC6" + + "\u6F3F\u6F7C\u6F84\u6F51\u6F66\u6F54\u6F86\u6F6D" + + "\u6F5B\u6F78\u6F6E\u6F8E\u6F7A\u6F70\u6F64\u6F97" + + "\u6F58\u6ED5\u6F6F\u6F60\u6F5F\u719F\u71AC\u71B1" + + "\u71A8\u7256\u729B\u734E\u7357\u7469\u748B\u7483" + + "\u747E\u7480\u757F\u7620\u7629\u761F\u7624\u7626" + + "\u7621\u7622\u769A\u76BA\u76E4\u778E\u7787\u778C" + + "\u7791\u778B\u78CB\u78C5\u78BA\u78CA\u78BE\u78D5" + + "\u78BC\u78D0\u7A3F\u7A3C\u7A40\u7A3D\u7A37\u7A3B" + + "\u7AAF\u7AAE\u7BAD\u7BB1\u7BC4\u7BB4\u7BC6\u7BC7" + + "\u7BC1\u7BA0\u7BCC\u7CCA\u7DE0\u7DF4\u7DEF\u7DFB" + + "\u7DD8\u7DEC\u7DDD\u7DE8\u7DE3\u7DDA\u7DDE\u7DE9" + + "\u7D9E\u7DD9\u7DF2\u7DF9\u7F75\u7F77\u7FAF\u7FE9" + + "\u8026\u819B\u819C\u819D\u81A0\u819A\u8198\u8517" + + "\u853D\u851A\u84EE\u852C\u852D\u8513\u8511\u8523" + + "\u8521\u8514\u84EC\u8525\u84FF\u8506\u8782\u8774" + + "\u8776\u8760\u8766\u8778\u8768\u8759\u8757\u874C" + + "\u8753\u885B\u885D\u8910\u8907\u8912\u8913\u8915" + + "\u890A\u8ABC\u8AD2\u8AC7\u8AC4\u8A95\u8ACB\u8AF8" + + "\u8AB2\u8AC9\u8AC2\u8ABF\u8AB0\u8AD6\u8ACD\u8AB6" + + "\u8AB9\u8ADB\u8C4C\u8C4E\u8C6C\u8CE0\u8CDE\u8CE6" + + "\u8CE4\u8CEC\u8CED\u8CE2\u8CE3\u8CDC\u8CEA\u8CE1" + + "\u8D6D\u8D9F\u8DA3\u8E2B\u8E10\u8E1D\u8E22\u8E0F" + + "\u8E29\u8E1F\u8E21\u8E1E\u8EBA\u8F1D\u8F1B\u8F1F" + + "\u8F29\u8F26\u8F2A\u8F1C\u8F1E\u8F25\u9069\u906E" + + "\u9068\u906D\u9077\u9130\u912D\u9127\u9131\u9187" + + "\u9189\u918B\u9183\u92C5\u92BB\u92B7\u92EA\u92E4" + + "\u92C1\u92B3\u92BC\u92D2\u92C7\u92F0\u92B2\u95AD" + + "\u95B1\u9704\u9706\u9707\u9709\u9760\u978D\u978B" + + "\u978F\u9821\u982B\u981C\u98B3\u990A\u9913\u9912" + + "\u9918\u99DD\u99D0\u99DF\u99DB\u99D1\u99D5\u99D2" + + "\u99D9\u9AB7\u9AEE\u9AEF\u9B27\u9B45\u9B44\u9B77" + + "\u9B6F\u9D06\u9D09\u9D03\u9EA9\u9EBE\u9ECE\u58A8" + + "\u9F52\u5112\u5118\u5114\u5110\u5115\u5180\u51AA" + + "\u51DD\u5291\u5293\u52F3\u5659\u566B\u5679\u5669" + + "\u5664\u5678\u566A\u5668\u5665\u5671\u566F\u566C" + + "\u5662\u5676\u58C1\u58BE\u58C7\u58C5\u596E\u5B1D" + + "\u5B34\u5B78\u5BF0\u5C0E\u5F4A\u61B2\u6191\u61A9" + + "\u618A\u61CD\u61B6\u61BE\u61CA\u61C8\u6230\u64C5" + + "\u64C1\u64CB\u64BB\u64BC\u64DA\u64C4\u64C7\u64C2" + + "\u64CD\u64BF\u64D2\u64D4\u64BE\u6574\u66C6\u66C9" + + "\u66B9\u66C4\u66C7\u66B8\u6A3D\u6A38\u6A3A\u6A59" + + "\u6A6B\u6A58\u6A39\u6A44\u6A62\u6A61\u6A4B\u6A47" + + "\u6A35\u6A5F\u6A48\u6B59\u6B77\u6C05\u6FC2\u6FB1" + + "\u6FA1\u6FC3\u6FA4\u6FC1\u6FA7\u6FB3\u6FC0\u6FB9" + + "\u6FB6\u6FA6\u6FA0\u6FB4\u71BE\u71C9\u71D0\u71D2" + + "\u71C8\u71D5\u71B9\u71CE\u71D9\u71DC\u71C3\u71C4" + + "\u7368\u749C\u74A3\u7498\u749F\u749E\u74E2\u750C" + + "\u750D\u7634\u7638\u763A\u76E7\u76E5\u77A0\u779E" + + "\u779F\u77A5\u78E8\u78DA\u78EC\u78E7\u79A6\u7A4D" + + "\u7A4E\u7A46\u7A4C\u7A4B\u7ABA\u7BD9\u7C11\u7BC9" + + "\u7BE4\u7BDB\u7BE1\u7BE9\u7BE6\u7CD5\u7CD6\u7E0A" + + "\u7E11\u7E08\u7E1B\u7E23\u7E1E\u7E1D\u7E09\u7E10" + + "\u7F79\u7FB2\u7FF0\u7FF1\u7FEE\u8028\u81B3\u81A9" + + "\u81A8\u81FB\u8208\u8258\u8259\u854A\u8559\u8548" + + "\u8568\u8569\u8543\u8549\u856D\u856A\u855E\u8783" + + "\u879F\u879E\u87A2\u878D\u8861\u892A\u8932\u8925" + + "\u892B\u8921\u89AA\u89A6\u8AE6\u8AFA\u8AEB\u8AF1" + + "\u8B00\u8ADC\u8AE7\u8AEE\u8AFE\u8B01\u8B02\u8AF7" + + "\u8AED\u8AF3\u8AF6\u8AFC\u8C6B\u8C6D\u8C93\u8CF4" + + "\u8E44\u8E31\u8E34\u8E42\u8E39\u8E35\u8F3B\u8F2F" + + "\u8F38\u8F33\u8FA8\u8FA6\u9075\u9074\u9078\u9072" + + "\u907C\u907A\u9134\u9192\u9320\u9336\u92F8\u9333" + + "\u932F\u9322\u92FC\u932B\u9304\u931A\u9310\u9326" + + "\u9321\u9315\u932E\u9319\u95BB\u96A7\u96A8\u96AA" + + "\u96D5\u970E\u9711\u9716\u970D\u9713\u970F\u975B" + + "\u975C\u9766\u9798\u9830\u9838\u983B\u9837\u982D" + + "\u9839\u9824\u9910\u9928\u991E\u991B\u9921\u991A" + + "\u99ED\u99E2\u99F1\u9AB8\u9ABC\u9AFB\u9AED\u9B28" + + "\u9B91\u9D15\u9D23\u9D26\u9D28\u9D12\u9D1B\u9ED8" + + "\u9ED4\u9F8D\u9F9C\u512A\u511F\u5121\u5132\u52F5" + + "\u568E\u5680\u5690\u5685\u5687\u568F\u58D5\u58D3" + + "\u58D1\u58CE\u5B30\u5B2A\u5B24\u5B7A\u5C37\u5C68" + + "\u5DBC\u5DBA\u5DBD\u5DB8\u5E6B\u5F4C\u5FBD\u61C9" + + "\u61C2\u61C7\u61E6\u61CB\u6232\u6234\u64CE\u64CA" + + "\u64D8\u64E0\u64F0\u64E6\u64EC\u64F1\u64E2\u64ED" + + "\u6582\u6583\u66D9\u66D6\u6A80\u6A94\u6A84\u6AA2" + + "\u6A9C\u6ADB\u6AA3\u6A7E\u6A97\u6A90\u6AA0\u6B5C" + + "\u6BAE\u6BDA\u6C08\u6FD8\u6FF1\u6FDF\u6FE0\u6FDB" + + "\u6FE4\u6FEB\u6FEF\u6F80\u6FEC\u6FE1\u6FE9\u6FD5" + + "\u6FEE\u6FF0\u71E7\u71DF\u71EE\u71E6\u71E5\u71ED" + + "\u71EC\u71F4\u71E0\u7235\u7246\u7370\u7372\u74A9" + + "\u74B0\u74A6\u74A8\u7646\u7642\u764C\u76EA\u77B3" + + "\u77AA\u77B0\u77AC\u77A7\u77AD\u77EF\u78F7\u78FA" + + "\u78F4\u78EF\u7901\u79A7\u79AA\u7A57\u7ABF\u7C07" + + "\u7C0D\u7BFE\u7BF7\u7C0C\u7BE0\u7CE0\u7CDC\u7CDE" + + "\u7CE2\u7CDF\u7CD9\u7CDD\u7E2E\u7E3E\u7E46\u7E37" + + "\u7E32\u7E43\u7E2B\u7E3D\u7E31\u7E45\u7E41\u7E34" + + "\u7E39\u7E48\u7E35\u7E3F\u7E2F\u7F44\u7FF3\u7FFC" + + "\u8071\u8072\u8070\u806F\u8073\u81C6\u81C3\u81BA" + + "\u81C2\u81C0\u81BF\u81BD\u81C9\u81BE\u81E8\u8209" + + "\u8271\u85AA\u8584\u857E\u859C\u8591\u8594\u85AF" + + "\u859B\u8587\u85A8\u858A\u85A6\u8667\u87C0\u87D1" + + "\u87B3\u87D2\u87C6\u87AB\u87BB\u87BA\u87C8\u87CB" + + "\u893B\u8936\u8944\u8938\u893D\u89AC\u8B0E\u8B17" + + "\u8B19\u8B1B\u8B0A\u8B20\u8B1D\u8B04\u8B10\u8C41" + + "\u8C3F\u8C73\u8CFA\u8CFD\u8CFC\u8CF8\u8CFB\u8DA8" + + "\u8E49\u8E4B\u8E48\u8E4A\u8F44\u8F3E\u8F42\u8F45" + + "\u8F3F\u907F\u907D\u9084\u9081\u9082\u9080\u9139" + + "\u91A3\u919E\u919C\u934D\u9382\u9328\u9375\u934A" + + "\u9365\u934B\u9318\u937E\u936C\u935B\u9370\u935A" + + "\u9354\u95CA\u95CB\u95CC\u95C8\u95C6\u96B1\u96B8" + + "\u96D6\u971C\u971E\u97A0\u97D3\u9846\u98B6\u9935" + + "\u9A01\u99FF\u9BAE\u9BAB\u9BAA\u9BAD\u9D3B\u9D3F" + + "\u9E8B\u9ECF\u9EDE\u9EDC\u9EDD\u9EDB\u9F3E\u9F4B" + + "\u53E2\u5695\u56AE\u58D9\u58D8\u5B38\u5F5E\u61E3" + + "\u6233\u64F4\u64F2\u64FE\u6506\u64FA\u64FB\u64F7" + + "\u65B7\u66DC\u6726\u6AB3\u6AAC\u6AC3\u6ABB\u6AB8" + + "\u6AC2\u6AAE\u6AAF\u6B5F\u6B78\u6BAF\u7009\u700B" + + "\u6FFE\u7006\u6FFA\u7011\u700F\u71FB\u71FC\u71FE" + + "\u71F8\u7377\u7375\u74A7\u74BF\u7515\u7656\u7658" + + "\u7652\u77BD\u77BF\u77BB\u77BC\u790E\u79AE\u7A61" + + "\u7A62\u7A60\u7AC4\u7AC5\u7C2B\u7C27\u7C2A\u7C1E" + + "\u7C23\u7C21\u7CE7\u7E54\u7E55\u7E5E\u7E5A\u7E61" + + "\u7E52\u7E59\u7F48\u7FF9\u7FFB\u8077\u8076\u81CD" + + "\u81CF\u820A\u85CF\u85A9\u85CD\u85D0\u85C9\u85B0" + + "\u85BA\u85B9\u87EF\u87EC\u87F2\u87E0\u8986\u89B2" + + "\u89F4\u8B28\u8B39\u8B2C\u8B2B\u8C50\u8D05\u8E59" + + "\u8E63\u8E66\u8E64\u8E5F\u8E55\u8EC0\u8F49\u8F4D" + + "\u9087\u9083\u9088\u91AB\u91AC\u91D0\u9394\u938A" + + "\u9396\u93A2\u93B3\u93AE\u93AC\u93B0\u9398\u939A" + + "\u9397\u95D4\u95D6\u95D0\u95D5\u96E2\u96DC\u96D9" + + "\u96DB\u96DE\u9724\u97A3\u97A6\u97AD\u97F9\u984D" + + "\u984F\u984C\u984E\u9853\u98BA\u993E\u993F\u993D" + + "\u992E\u99A5\u9A0E\u9AC1\u9B03\u9B06\u9B4F\u9B4E" + + "\u9B4D\u9BCA\u9BC9\u9BFD\u9BC8\u9BC0\u9D51\u9D5D" + + "\u9D60\u9EE0\u9F15\u9F2C\u5133\u56A5\u56A8\u58DE" + + "\u58DF\u58E2\u5BF5\u9F90\u5EEC\u61F2\u61F7\u61F6" + + "\u61F5\u6500\u650F\u66E0\u66DD\u6AE5\u6ADD\u6ADA" + + "\u6AD3\u701B\u701F\u7028\u701A\u701D\u7015\u7018" + + "\u7206\u720D\u7258\u72A2\u7378\u737A\u74BD\u74CA" + + "\u74E3\u7587\u7586\u765F\u7661\u77C7\u7919\u79B1" + + "\u7A6B\u7A69\u7C3E\u7C3F\u7C38\u7C3D\u7C37\u7C40" + + "\u7E6B\u7E6D\u7E79\u7E69\u7E6A\u7E73\u7F85\u7FB6" + + "\u7FB9\u7FB8\u81D8\u85E9\u85DD\u85EA\u85D5\u85E4" + + "\u85E5\u85F7\u87FB\u8805\u880D\u87F9\u87FE\u8960" + + "\u895F\u8956\u895E\u8B41\u8B5C\u8B58\u8B49\u8B5A" + + "\u8B4E\u8B4F\u8B46\u8B59\u8D08\u8D0A\u8E7C\u8E72" + + "\u8E87\u8E76\u8E6C\u8E7A\u8E74\u8F54\u8F4E\u8FAD" + + "\u908A\u908B\u91B1\u91AE\u93E1\u93D1\u93DF\u93C3" + + "\u93C8\u93DC\u93DD\u93D6\u93E2\u93CD\u93D8\u93E4" + + "\u93D7\u93E8\u95DC\u96B4\u96E3\u972A\u9727\u9761" + + "\u97DC\u97FB\u985E\u9858\u985B\u98BC\u9945\u9949" + + "\u9A16\u9A19\u9B0D\u9BE8\u9BE7\u9BD6\u9BDB\u9D89" + + "\u9D61\u9D72\u9D6A\u9D6C\u9E92\u9E97\u9E93\u9EB4" + + "\u52F8\u56B7\u56B6\u56B4\u56BC\u58E4\u5B40\u5B43" + + "\u5B7D\u5BF6\u5DC9\u61F8\u61FA\u6518\u6514\u6519" + + "\u66E6\u6727\u6AEC\u703E\u7030\u7032\u7210\u737B" + + "\u74CF\u7662\u7665\u7926\u792A\u792C\u792B\u7AC7" + + "\u7AF6\u7C4C\u7C43\u7C4D\u7CEF\u7CF0\u8FAE\u7E7D" + + "\u7E7C\u7E82\u7F4C\u8000\u81DA\u8266\u85FB\u85F9" + + "\u8611\u85FA\u8606\u860B\u8607\u860A\u8814\u8815" + + "\u8964\u89BA\u89F8\u8B70\u8B6C\u8B66\u8B6F\u8B5F" + + "\u8B6B\u8D0F\u8D0D\u8E89\u8E81\u8E85\u8E82\u91B4" + + "\u91CB\u9418\u9403\u93FD\u95E1\u9730\u98C4\u9952" + + "\u9951\u99A8\u9A2B\u9A30\u9A37\u9A35\u9C13\u9C0D" + + "\u9E79\u9EB5\u9EE8\u9F2F\u9F5F\u9F63\u9F61\u5137" + + "\u5138\u56C1\u56C0\u56C2\u5914\u5C6C\u5DCD\u61FC" + + "\u61FE\u651D\u651C\u6595\u66E9\u6AFB\u6B04\u6AFA" + + "\u6BB2\u704C\u721B\u72A7\u74D6\u74D4\u7669\u77D3" + + "\u7C50\u7E8F\u7E8C\u7FBC\u8617\u862D\u861A\u8823" + + "\u8822\u8821\u881F\u896A\u896C\u89BD\u8B74\u8B77" + + "\u8B7D\u8D13\u8E8A\u8E8D\u8E8B\u8F5F\u8FAF\u91BA" + + "\u942E\u9433\u9435\u943A\u9438\u9432\u942B\u95E2" + + "\u9738\u9739\u9732\u97FF\u9867\u9865\u9957\u9A45" + + "\u9A43\u9A40\u9A3E\u9ACF\u9B54\u9B51\u9C2D\u9C25" + + "\u9DAF\u9DB4\u9DC2\u9DB8\u9E9D\u9EEF\u9F19\u9F5C" + + "\u9F66\u9F67\u513C\u513B\u56C8\u56CA\u56C9\u5B7F" + + "\u5DD4\u5DD2\u5F4E\u61FF\u6524\u6B0A\u6B61\u7051" + + "\u7058\u7380\u74E4\u758A\u766E\u766C\u79B3\u7C60" + + "\u7C5F\u807E\u807D\u81DF\u8972\u896F\u89FC\u8B80" + + "\u8D16\u8D17\u8E91\u8E93\u8F61\u9148\u9444\u9451" + + "\u9452\u973D\u973E\u97C3\u97C1\u986B\u9955\u9A55" + + "\u9A4D\u9AD2\u9B1A\u9C49\u9C31\u9C3E\u9C3B\u9DD3" + + "\u9DD7\u9F34\u9F6C\u9F6A\u9F94\u56CC\u5DD6\u6200" + + "\u6523\u652B\u652A\u66EC\u6B10\u74DA\u7ACA\u7C64" + + "\u7C63\u7C65\u7E93\u7E96\u7E94\u81E2\u8638\u863F" + + "\u8831\u8B8A\u9090\u908F\u9463\u9460\u9464\u9768" + + "\u986F\u995C\u9A5A\u9A5B\u9A57\u9AD3\u9AD4\u9AD1" + + "\u9C54\u9C57\u9C56\u9DE5\u9E9F\u9EF4\u56D1\u58E9" + + "\u652C\u705E\u7671\u7672\u77D7\u7F50\u7F88\u8836" + + "\u8839\u8862\u8B93\u8B92\u8B96\u8277\u8D1B\u91C0" + + "\u946A\u9742\u9748\u9744\u97C6\u9870\u9A5F\u9B22" + + "\u9B58\u9C5F\u9DF9\u9DFA\u9E7C\u9E7D\u9F07\u9F77" + + "\u9F72\u5EF3\u6B16\u7063\u7C6C\u7C6E\u883B\u89C0" + + "\u8EA1\u91C1\u9472\u9470\u9871\u995E\u9AD6\u9B23" + + "\u9ECC\u7064\u77DA\u8B9A\u9477\u97C9\u9A62\u9A65" + + "\u7E9C\u8B9C\u8EAA\u91C5\u947D\u947E\u947C\u9C77" + + "\u9C78\u9EF7\u8C54\u947F\u9E1A\u7228\u9A6A\u9B31" + + "\u9E1B\u9E1E\u7C72\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD" + ; + mappingTableG2a2 = + "\u4E42\u4E5C\u51F5\u531A\u5382\u4E07\u4E0C\u4E47" + + "\u4E8D\u56D7\u5C6E\u5F73\u4E0F\u5187\u4E0E\u4E2E" + + "\u4E93\u4EC2\u4EC9\u4EC8\u5198\u52FC\u536C\u53B9" + + "\u5720\u5903\u592C\u5C10\u5DFF\u65E1\u6BB3\u6BCC" + + "\u6C14\u723F\u4E31\u4E3C\u4EE8\u4EDC\u4EE9\u4EE1" + + "\u4EDD\u4EDA\u520C\u5209\u531C\u534C\u5722\u5723" + + "\u5917\u592F\u5B81\u5B84\u5C12\u5C3B\u5C74\u5C73" + + "\u5E04\u5E80\u5E82\u5FC9\u6209\u6250\u6C15\u6C36" + + "\u6C43\u6C3F\u6C3B\u72AE\u72B0\u738A\u79B8\u808A" + + "\u961E\u4F0E\u4F18\u4F2C\u4EF5\u4F14\u4EF1\u4F00" + + "\u4EF7\u4F08\u4F1D\u4F02\u4F05\u4F22\u4F13\u4F04" + + "\u4EF4\u4F12\u51B1\u5213\u5210\u52A6\u5322\u531F" + + "\u534D\u538A\u5407\u56E1\u56DF\u572E\u572A\u5734" + + "\u593C\u5980\u597C\u5985\u597B\u597E\u5977\u597F" + + "\u5B56\u5C15\u5C25\u5C7C\u5C7A\u5C7B\u5C7E\u5DDF" + + "\u5E75\u5E84\u5F02\u5F1A\u5F74\u5FD5\u5FD4\u5FCF" + + "\u625C\u625E\u6264\u6261\u6266\u6262\u6259\u6260" + + "\u625A\u6265\u6537\u65EF\u65EE\u673E\u6739\u6738" + + "\u673B\u673A\u673F\u673C\u6733\u6C18\u6C46\u6C52" + + "\u6C5C\u6C4F\u6C4A\u6C54\u6C4B\u6C4C\u7071\u725E" + + "\u72B4\u72B5\u738E\u752A\u767F\u7A75\u7F51\u8278" + + "\u827C\u8280\u827D\u827F\u864D\u897E\u9099\u9097" + + "\u9098\u909B\u9094\u9622\u9624\u9620\u9623\u4F56" + + "\u4F3B\u4F62\u4F49\u4F53\u4F64\u4F3E\u4F67\u4F52" + + "\u4F5F\u4F41\u4F58\u4F2D\u4F33\u4F3F\u4F61\u518F" + + "\u51B9\u521C\u521E\u5221\u52AD\u52AE\u5309\u5363" + + "\u5372\u538E\u538F\u5430\u5437\u542A\u5454\u5445" + + "\u5419\u541C\u5425\u5418\u543D\u544F\u5441\u5428" + + "\u5424\u5447\u56EE\u56E7\u56E5\u5741\u5745\u574C" + + "\u5749\u574B\u5752\u5906\u5940\u59A6\u5998\u59A0" + + "\u5997\u598E\u59A2\u5990\u598F\u59A7\u59A1\u5B8E" + + "\u5B92\u5C28\u5C2A\u5C8D\u5C8F\u5C88\u5C8B\u5C89" + + "\u5C92\u5C8A\u5C86\u5C93\u5C95\u5DE0\u5E0A\u5E0E" + + "\u5E8B\u5E89\u5E8C\u5E88\u5E8D\u5F05\u5F1D\u5F78" + + "\u5F76\u5FD2\u5FD1\u5FD0\u5FED\u5FE8\u5FEE\u5FF3" + + "\u5FE1\u5FE4\u5FE3\u5FFA\u5FEF\u5FF7\u5FFB\u6000" + + "\u5FF4\u623A\u6283\u628C\u628E\u628F\u6294\u6287" + + "\u6271\u627B\u627A\u6270\u6281\u6288\u6277\u627D" + + "\u6272\u6274\u65F0\u65F4\u65F3\u65F2\u65F5\u6745" + + "\u6747\u6759\u6755\u674C\u6748\u675D\u674D\u675A" + + "\u674B\u6BD0\u6C19\u6C1A\u6C78\u6C67\u6C6B\u6C84" + + "\u6C8B\u6C8F\u6C71\u6C6F\u6C69\u6C9A\u6C6D\u6C87" + + "\u6C95\u6C9C\u6C66\u6C73\u6C65\u6C7B\u6C8E\u7074" + + "\u707A\u7263\u72BF\u72BD\u72C3\u72C6\u72C1\u72BA" + + "\u72C5\u7395\u7397\u7393\u7394\u7392\u753A\u7539" + + "\u7594\u7595\u7681\u793D\u8034\u8095\u8099\u8090" + + "\u8092\u809C\u8290\u828F\u8285\u828E\u8291\u8293" + + "\u828A\u8283\u8284\u8C78\u8FC9\u8FBF\u909F\u90A1" + + "\u90A5\u909E\u90A7\u90A0\u9630\u9628\u962F\u962D" + + "\u4E33\u4F98\u4F7C\u4F85\u4F7D\u4F80\u4F87\u4F76" + + "\u4F74\u4F89\u4F84\u4F77\u4F4C\u4F97\u4F6A\u4F9A" + + "\u4F79\u4F81\u4F78\u4F90\u4F9C\u4F94\u4F9E\u4F92" + + "\u4F82\u4F95\u4F6B\u4F6E\u519E\u51BC\u51BE\u5235" + + "\u5232\u5233\u5246\u5231\u52BC\u530A\u530B\u533C" + + "\u5392\u5394\u5487\u547F\u5481\u5491\u5482\u5488" + + "\u546B\u547A\u547E\u5465\u546C\u5474\u5466\u548D" + + "\u546F\u5461\u5460\u5498\u5463\u5467\u5464\u56F7" + + "\u56F9\u576F\u5772\u576D\u576B\u5771\u5770\u5776" + + "\u5780\u5775\u577B\u5773\u5774\u5762\u5768\u577D" + + "\u590C\u5945\u59B5\u59BA\u59CF\u59CE\u59B2\u59CC" + + "\u59C1\u59B6\u59BC\u59C3\u59D6\u59B1\u59BD\u59C0" + + "\u59C8\u59B4\u59C7\u5B62\u5B65\u5B93\u5B95\u5C44" + + "\u5C47\u5CAE\u5CA4\u5CA0\u5CB5\u5CAF\u5CA8\u5CAC" + + "\u5C9F\u5CA3\u5CAD\u5CA2\u5CAA\u5CA7\u5C9D\u5CA5" + + "\u5CB6\u5CB0\u5CA6\u5E17\u5E14\u5E19\u5F28\u5F22" + + "\u5F23\u5F24\u5F54\u5F82\u5F7E\u5F7D\u5FDE\u5FE5" + + "\u602D\u6026\u6019\u6032\u600B\u6034\u600A\u6017" + + "\u6033\u601A\u601E\u602C\u6022\u600D\u6010\u602E" + + "\u6013\u6011\u600C\u6009\u601C\u6214\u623D\u62AD" + + "\u62B4\u62D1\u62BE\u62AA\u62B6\u62CA\u62AE\u62B3" + + "\u62AF\u62BB\u62A9\u62B0\u62B8\u653D\u65A8\u65BB" + + "\u6609\u65FC\u6604\u6612\u6608\u65FB\u6603\u660B" + + "\u660D\u6605\u65FD\u6611\u6610\u66F6\u670A\u6785" + + "\u676C\u678E\u6792\u6776\u677B\u6798\u6786\u6784" + + "\u6774\u678D\u678C\u677A\u679F\u6791\u6799\u6783" + + "\u677D\u6781\u6778\u6779\u6794\u6B25\u6B80\u6B7E" + + "\u6BDE\u6C1D\u6C93\u6CEC\u6CEB\u6CEE\u6CD9\u6CB6" + + "\u6CD4\u6CAD\u6CE7\u6CB7\u6CD0\u6CC2\u6CBA\u6CC3" + + "\u6CC6\u6CED\u6CF2\u6CD2\u6CDD\u6CB4\u6C8A\u6C9D" + + "\u6C80\u6CDE\u6CC0\u6D30\u6CCD\u6CC7\u6CB0\u6CF9" + + "\u6CCF\u6CE9\u6CD1\u7094\u7098\u7085\u7093\u7086" + + "\u7084\u7091\u7096\u7082\u709A\u7083\u726A\u72D6" + + "\u72CB\u72D8\u72C9\u72DC\u72D2\u72D4\u72DA\u72CC" + + "\u72D1\u73A4\u73A1\u73AD\u73A6\u73A2\u73A0\u73AC" + + "\u739D\u74DD\u74E8\u753F\u7540\u753E\u758C\u7598" + + "\u76AF\u76F3\u76F1\u76F0\u76F5\u77F8\u77FC\u77F9" + + "\u77FB\u77FA\u77F7\u7942\u793F\u79C5\u7A78\u7A7B" + + "\u7AFB\u7C75\u7CFD\u8035\u808F\u80AE\u80A3\u80B8" + + "\u80B5\u80AD\u8220\u82A0\u82C0\u82AB\u829A\u8298" + + "\u829B\u82B5\u82A7\u82AE\u82BC\u829E\u82BA\u82B4" + + "\u82A8\u82A1\u82A9\u82C2\u82A4\u82C3\u82B6\u82A2" + + "\u8670\u866F\u866D\u866E\u8C56\u8FD2\u8FCB\u8FD3" + + "\u8FCD\u8FD6\u8FD5\u8FD7\u90B2\u90B4\u90AF\u90B3" + + "\u90B0\u9639\u963D\u963C\u963A\u9643\u4FCD\u4FC5" + + "\u4FD3\u4FB2\u4FC9\u4FCB\u4FC1\u4FD4\u4FDC\u4FD9" + + "\u4FBB\u4FB3\u4FDB\u4FC7\u4FD6\u4FBA\u4FC0\u4FB9" + + "\u4FEC\u5244\u5249\u52C0\u52C2\u533D\u537C\u5397" + + "\u5396\u5399\u5398\u54BA\u54A1\u54AD\u54A5\u54CF" + + "\u54C3\u830D\u54B7\u54AE\u54D6\u54B6\u54C5\u54C6" + + "\u54A0\u5470\u54BC\u54A2\u54BE\u5472\u54DE\u54B0" + + "\u57B5\u579E\u579F\u57A4\u578C\u5797\u579D\u579B" + + "\u5794\u5798\u578F\u5799\u57A5\u579A\u5795\u58F4" + + "\u590D\u5953\u59E1\u59DE\u59EE\u5A00\u59F1\u59DD" + + "\u59FA\u59FD\u59FC\u59F6\u59E4\u59F2\u59F7\u59DB" + + "\u59E9\u59F3\u59F5\u59E0\u59FE\u59F4\u59ED\u5BA8" + + "\u5C4C\u5CD0\u5CD8\u5CCC\u5CD7\u5CCB\u5CDB\u5CDE" + + "\u5CDA\u5CC9\u5CC7\u5CCA\u5CD6\u5CD3\u5CD4\u5CCF" + + "\u5CC8\u5CC6\u5CCE\u5CDF\u5CF8\u5DF9\u5E21\u5E22" + + "\u5E23\u5E20\u5E24\u5EB0\u5EA4\u5EA2\u5E9B\u5EA3" + + "\u5EA5\u5F07\u5F2E\u5F56\u5F86\u6037\u6039\u6054" + + "\u6072\u605E\u6045\u6053\u6047\u6049\u605B\u604C" + + "\u6040\u6042\u605F\u6024\u6044\u6058\u6066\u606E" + + "\u6242\u6243\u62CF\u630D\u630B\u62F5\u630E\u6303" + + "\u62EB\u62F9\u630F\u630C\u62F8\u62F6\u6300\u6313" + + "\u6314\u62FA\u6315\u62FB\u62F0\u6541\u6543\u65AA" + + "\u65BF\u6636\u6621\u6632\u6635\u661C\u6626\u6622" + + "\u6633\u662B\u663A\u661D\u6634\u6639\u662E\u670F" + + "\u6710\u67C1\u67F2\u67C8\u67BA\u67DC\u67BB\u67F8" + + "\u67D8\u67C0\u67B7\u67C5\u67EB\u67E4\u67DF\u67B5" + + "\u67CD\u67B3\u67F7\u67F6\u67EE\u67E3\u67C2\u67B9" + + "\u67CE\u67E7\u67F0\u67B2\u67FC\u67C6\u67ED\u67CC" + + "\u67AE\u67E6\u67DB\u67FA\u67C9\u67CA\u67C3\u67EA" + + "\u67CB\u6B28\u6B82\u6B84\u6BB6\u6BD6\u6BD8\u6BE0" + + "\u6C20\u6C21\u6D28\u6D34\u6D2D\u6D1F\u6D3C\u6D3F" + + "\u6D12\u6D0A\u6CDA\u6D33\u6D04\u6D19\u6D3A\u6D1A" + + "\u6D11\u6D00\u6D1D\u6D42\u6D01\u6D18\u6D37\u6D03" + + "\u6D0F\u6D40\u6D07\u6D20\u6D2C\u6D08\u6D22\u6D09" + + "\u6D10\u70B7\u709F\u70BE\u70B1\u70B0\u70A1\u70B4" + + "\u70B5\u70A9\u7241\u7249\u724A\u726C\u7270\u7273" + + "\u726E\u72CA\u72E4\u72E8\u72EB\u72DF\u72EA\u72E6" + + "\u72E3\u7385\u73CC\u73C2\u73C8\u73C5\u73B9\u73B6" + + "\u73B5\u73B4\u73EB\u73BF\u73C7\u73BE\u73C3\u73C6" + + "\u73B8\u73CB\u74EC\u74EE\u752E\u7547\u7548\u75A7" + + "\u75AA\u7679\u76C4\u7708\u7703\u7704\u7705\u770A" + + "\u76F7\u76FB\u76FA\u77E7\u77E8\u7806\u7811\u7812" + + "\u7805\u7810\u780F\u780E\u7809\u7803\u7813\u794A" + + "\u794C\u794B\u7945\u7944\u79D5\u79CD\u79CF\u79D6" + + "\u79CE\u7A80\u7A7E\u7AD1\u7B00\u7B01\u7C7A\u7C78" + + "\u7C79\u7C7F\u7C80\u7C81\u7D03\u7D08\u7D01\u7F58" + + "\u7F91\u7F8D\u7FBE\u8007\u800E\u800F\u8014\u8037" + + "\u80D8\u80C7\u80E0\u80D1\u80C8\u80C2\u80D0\u80C5" + + "\u80E3\u80D9\u80DC\u80CA\u80D5\u80C9\u80CF\u80D7" + + "\u80E6\u80CD\u81FF\u8221\u8294\u82D9\u82FE\u82F9" + + "\u8307\u82E8\u8300\u82D5\u833A\u82EB\u82D6\u82F4" + + "\u82EC\u82E1\u82F2\u82F5\u830C\u82FB\u82F6\u82F0" + + "\u82EA\u82E4\u82E0\u82FA\u82F3\u82ED\u8677\u8674" + + "\u867C\u8673\u8841\u884E\u8867\u886A\u8869\u89D3" + + "\u8A04\u8A07\u8D72\u8FE3\u8FE1\u8FEE\u8FE0\u90F1" + + "\u90BD\u90BF\u90D5\u90C5\u90BE\u90C7\u90CB\u90C8" + + "\u91D4\u91D3\u9654\u964F\u9651\u9653\u964A\u964E" + + "\u501E\u5005\u5007\u5013\u5022\u5030\u501B\u4FF5" + + "\u4FF4\u5033\u5037\u502C\u4FF6\u4FF7\u5017\u501C" + + "\u5020\u5027\u5035\u502F\u5031\u500E\u515A\u5194" + + "\u5193\u51CA\u51C4\u51C5\u51C8\u51CE\u5261\u525A" + + "\u5252\u525E\u525F\u5255\u5262\u52CD\u530E\u539E" + + "\u5526\u54E2\u5517\u5512\u54E7\u54F3\u54E4\u551A" + + "\u54FF\u5504\u5508\u54EB\u5511\u5505\u54F1\u550A" + + "\u54FB\u54F7\u54F8\u54E0\u550E\u5503\u550B\u5701" + + "\u5702\u57CC\u5832\u57D5\u57D2\u57BA\u57C6\u57BD" + + "\u57BC\u57B8\u57B6\u57BF\u57C7\u57D0\u57B9\u57C1" + + "\u590E\u594A\u5A19\u5A16\u5A2D\u5A2E\u5A15\u5A0F" + + "\u5A17\u5A0A\u5A1E\u5A33\u5B6C\u5BA7\u5BAD\u5BAC" + + "\u5C03\u5C56\u5C54\u5CEC\u5CFF\u5CEE\u5CF1\u5CF7" + + "\u5D00\u5CF9\u5E29\u5E28\u5EA8\u5EAE\u5EAA\u5EAC" + + "\u5F33\u5F30\u5F67\u605D\u605A\u6067\u6041\u60A2" + + "\u6088\u6080\u6092\u6081\u609D\u6083\u6095\u609B" + + "\u6097\u6087\u609C\u608E\u6219\u6246\u62F2\u6310" + + "\u6356\u632C\u6344\u6345\u6336\u6343\u63E4\u6339" + + "\u634B\u634A\u633C\u6329\u6341\u6334\u6358\u6354" + + "\u6359\u632D\u6347\u6333\u635A\u6351\u6338\u6357" + + "\u6340\u6348\u654A\u6546\u65C6\u65C3\u65C4\u65C2" + + "\u664A\u665F\u6647\u6651\u6712\u6713\u681F\u681A" + + "\u6849\u6832\u6833\u683B\u684B\u684F\u6816\u6831" + + "\u681C\u6835\u682B\u682D\u682F\u684E\u6844\u6834" + + "\u681D\u6812\u6814\u6826\u6828\u682E\u684D\u683A" + + "\u6825\u6820\u6B2C\u6B2F\u6B2D\u6B31\u6B34\u6B6D" + + "\u8082\u6B88\u6BE6\u6BE4\u6BE8\u6BE3\u6BE2\u6BE7" + + "\u6C25\u6D7A\u6D63\u6D64\u6D76\u6D0D\u6D61\u6D92" + + "\u6D58\u6D62\u6D6D\u6D6F\u6D91\u6D8D\u6DEF\u6D7F" + + "\u6D86\u6D5E\u6D67\u6D60\u6D97\u6D70\u6D7C\u6D5F" + + "\u6D82\u6D98\u6D2F\u6D68\u6D8B\u6D7E\u6D80\u6D84" + + "\u6D16\u6D83\u6D7B\u6D7D\u6D75\u6D90\u70DC\u70D3" + + "\u70D1\u70DD\u70CB\u7F39\u70E2\u70D7\u70D2\u70DE" + + "\u70E0\u70D4\u70CD\u70C5\u70C6\u70C7\u70DA\u70CE" + + "\u70E1\u7242\u7278\u7277\u7276\u7300\u72FA\u72F4" + + "\u72FE\u72F6\u72F3\u72FB\u7301\u73D3\u73D9\u73E5" + + "\u73D6\u73BC\u73E7\u73E3\u73E9\u73DC\u73D2\u73DB" + + "\u73D4\u73DD\u73DA\u73D7\u73D8\u73E8\u74DE\u74DF" + + "\u74F4\u74F5\u7521\u755B\u755F\u75B0\u75C1\u75BB" + + "\u75C4\u75C0\u75BF\u75B6\u75BA\u768A\u76C9\u771D" + + "\u771B\u7710\u7713\u7712\u7723\u7711\u7715\u7719" + + "\u771A\u7722\u7727\u7823\u782C\u7822\u7835\u782F" + + "\u7828\u782E\u782B\u7821\u7829\u7833\u782A\u7831" + + "\u7954\u795B\u794F\u795C\u7953\u7952\u7951\u79EB" + + "\u79EC\u79E0\u79EE\u79ED\u79EA\u79DC\u79DE\u79DD" + + "\u7A86\u7A89\u7A85\u7A8B\u7A8C\u7A8A\u7A87\u7AD8" + + "\u7B10\u7B04\u7B13\u7B05\u7B0F\u7B08\u7B0A\u7B0E" + + "\u7B09\u7B12\u7C84\u7C91\u7C8A\u7C8C\u7C88\u7C8D" + + "\u7C85\u7D1E\u7D1D\u7D11\u7D0E\u7D18\u7D16\u7D13" + + "\u7D1F\u7D12\u7D0F\u7D0C\u7F5C\u7F61\u7F5E\u7F60" + + "\u7F5D\u7F5B\u7F96\u7F92\u7FC3\u7FC2\u7FC0\u8016" + + "\u803E\u8039\u80FA\u80F2\u80F9\u80F5\u8101\u80FB" + + "\u8100\u8201\u822F\u8225\u8333\u832D\u8344\u8319" + + "\u8351\u8325\u8356\u833F\u8341\u8326\u831C\u8322" + + "\u8342\u834E\u831B\u832A\u8308\u833C\u834D\u8316" + + "\u8324\u8320\u8337\u832F\u8329\u8347\u8345\u834C" + + "\u8353\u831E\u832C\u834B\u8327\u8348\u8653\u8652" + + "\u86A2\u86A8\u8696\u868D\u8691\u869E\u8687\u8697" + + "\u8686\u868B\u869A\u8685\u86A5\u8699\u86A1\u86A7" + + "\u8695\u8698\u868E\u869D\u8690\u8694\u8843\u8844" + + "\u886D\u8875\u8876\u8872\u8880\u8871\u887F\u886F" + + "\u8883\u887E\u8874\u887C\u8A12\u8C47\u8C57\u8C7B" + + "\u8CA4\u8CA3\u8D76\u8D78\u8DB5\u8DB7\u8DB6\u8ED1" + + "\u8ED3\u8FFE\u8FF5\u9002\u8FFF\u8FFB\u9004\u8FFC" + + "\u8FF6\u90D6\u90E0\u90D9\u90DA\u90E3\u90DF\u90E5" + + "\u90D8\u90DB\u90D7\u90DC\u90E4\u9150\u914E\u914F" + + "\u91D5\u91E2\u91DA\u965C\u965F\u96BC\u98E3\u9ADF" + + "\u9B2F\u4E7F\u5070\u506A\u5061\u505E\u5060\u5053" + + "\u504B\u505D\u5072\u5048\u504D\u5041\u505B\u504A" + + "\u5062\u5015\u5045\u505F\u5069\u506B\u5063\u5064" + + "\u5046\u5040\u506E\u5073\u5057\u5051\u51D0\u526B" + + "\u526D\u526C\u526E\u52D6\u52D3\u532D\u539C\u5575" + + "\u5576\u553C\u554D\u5550\u5534\u552A\u5551\u5562" + + "\u5536\u5535\u5530\u5552\u5545\u550C\u5532\u5565" + + "\u554E\u5539\u5548\u552D\u553B\u5540\u554B\u570A" + + "\u5707\u57FB\u5814\u57E2\u57F6\u57DC\u57F4\u5800" + + "\u57ED\u57FD\u5808\u57F8\u580B\u57F3\u57CF\u5807" + + "\u57EE\u57E3\u57F2\u57E5\u57EC\u57E1\u580E\u57FC" + + "\u5810\u57E7\u5801\u580C\u57F1\u57E9\u57F0\u580D" + + "\u5804\u595C\u5A60\u5A58\u5A55\u5A67\u5A5E\u5A38" + + "\u5A35\u5A6D\u5A50\u5A5F\u5A65\u5A6C\u5A53\u5A64" + + "\u5A57\u5A43\u5A5D\u5A52\u5A44\u5A5B\u5A48\u5A8E" + + "\u5A3E\u5A4D\u5A39\u5A4C\u5A70\u5A69\u5A47\u5A51" + + "\u5A56\u5A42\u5A5C\u5B72\u5B6E\u5BC1\u5BC0\u5C59" + + "\u5D1E\u5D0B\u5D1D\u5D1A\u5D20\u5D0C\u5D28\u5D0D" + + "\u5D26\u5D25\u5D0F\u5D30\u5D12\u5D23\u5D1F\u5D2E" + + "\u5E3E\u5E34\u5EB1\u5EB4\u5EB9\u5EB2\u5EB3\u5F36" + + "\u5F38\u5F9B\u5F96\u5F9F\u608A\u6090\u6086\u60BE" + + "\u60B0\u60BA\u60D3\u60D4\u60CF\u60E4\u60D9\u60DD" + + "\u60C8\u60B1\u60DB\u60B7\u60CA\u60BF\u60C3\u60CD" + + "\u60C0\u6332\u6365\u638A\u6382\u637D\u63BD\u639E" + + "\u63AD\u639D\u6397\u63AB\u638E\u636F\u6387\u6390" + + "\u636E\u63AF\u6375\u639C\u636D\u63AE\u637C\u63A4" + + "\u633B\u639F\u6378\u6385\u6381\u6391\u638D\u6370" + + "\u6553\u65CD\u6665\u6661\u665B\u6659\u665C\u6662" + + "\u6718\u6879\u6887\u6890\u689C\u686D\u686E\u68AE" + + "\u68AB\u6956\u686F\u68A3\u68AC\u68A9\u6875\u6874" + + "\u68B2\u688F\u6877\u6892\u687C\u686B\u6872\u68AA" + + "\u6880\u6871\u687E\u689B\u6896\u688B\u68A0\u6889" + + "\u68A4\u6878\u687B\u6891\u688C\u688A\u687D\u6B36" + + "\u6B33\u6B37\u6B38\u6B91\u6B8F\u6B8D\u6B8E\u6B8C" + + "\u6C2A\u6DC0\u6DAB\u6DB4\u6DB3\u6E74\u6DAC\u6DE9" + + "\u6DE2\u6DB7\u6DF6\u6DD4\u6E00\u6DC8\u6DE0\u6DDF" + + "\u6DD6\u6DBE\u6DE5\u6DDC\u6DDD\u6DDB\u6DF4\u6DCA" + + "\u6DBD\u6DED\u6DF0\u6DBA\u6DD5\u6DC2\u6DCF\u6DC9" + + "\u6DD0\u6DF2\u6DD3\u6DFD\u6DD7\u6DCD\u6DE3\u6DBB" + + "\u70FA\u710D\u70F7\u7117\u70F4\u710C\u70F0\u7104" + + "\u70F3\u7110\u70FC\u70FF\u7106\u7113\u7100\u70F8" + + "\u70F6\u710B\u7102\u710E\u727E\u727B\u727C\u727F" + + "\u731D\u7317\u7307\u7311\u7318\u730A\u7308\u72FF" + + "\u730F\u731E\u7388\u73F6\u73F8\u73F5\u7404\u7401" + + "\u73FD\u7407\u7400\u73FA\u73FC\u73FF\u740C\u740B" + + "\u73F4\u7408\u7564\u7563\u75CE\u75D2\u75CF\u75CB" + + "\u75CC\u75D1\u75D0\u768F\u7689\u76D3\u7739\u772F" + + "\u772D\u7731\u7732\u7734\u7733\u773D\u7725\u773B" + + "\u7735\u7848\u7852\u7849\u784D\u784A\u784C\u7826" + + "\u7845\u7850\u7964\u7967\u7969\u796A\u7963\u796B" + + "\u7961\u79BB\u79FA\u79F8\u79F6\u79F7\u7A8F\u7A94" + + "\u7A90\u7B35\u7B3B\u7B34\u7B25\u7B30\u7B22\u7B24" + + "\u7B33\u7B18\u7B2A\u7B1D\u7B31\u7B2B\u7B2D\u7B2F" + + "\u7B32\u7B38\u7B1A\u7B23\u7C94\u7C98\u7C96\u7CA3" + + "\u7D35\u7D3D\u7D38\u7D36\u7D3A\u7D45\u7D2C\u7D29" + + "\u7D41\u7D47\u7D3E\u7D3F\u7D4A\u7D3B\u7D28\u7F63" + + "\u7F95\u7F9C\u7F9D\u7F9B\u7FCA\u7FCB\u7FCD\u7FD0" + + "\u7FD1\u7FC7\u7FCF\u7FC9\u801F\u801E\u801B\u8047" + + "\u8043\u8048\u8118\u8125\u8119\u811B\u812D\u811F" + + "\u812C\u811E\u8121\u8115\u8127\u811D\u8122\u8211" + + "\u8238\u8233\u823A\u8234\u8232\u8274\u8390\u83A3" + + "\u83A8\u838D\u837A\u8373\u83A4\u8374\u838F\u8381" + + "\u8395\u8399\u8375\u8394\u83A9\u837D\u8383\u838C" + + "\u839D\u839B\u83AA\u838B\u837E\u83A5\u83AF\u8388" + + "\u8397\u83B0\u837F\u83A6\u8387\u83AE\u8376\u8659" + + "\u8656\u86BF\u86B7\u86C2\u86C1\u86C5\u86BA\u86B0" + + "\u86C8\u86B9\u86B3\u86B8\u86CC\u86B4\u86BB\u86BC" + + "\u86C3\u86BD\u86BE\u8852\u8889\u8895\u88A8\u88A2" + + "\u88AA\u889A\u8891\u88A1\u889F\u8898\u88A7\u8899" + + "\u889B\u8897\u88A4\u88AC\u888C\u8893\u888E\u8982" + + "\u89D6\u89D9\u89D5\u8A30\u8A27\u8A2C\u8A1E\u8C39" + + "\u8C3B\u8C5C\u8C5D\u8C7D\u8CA5\u8D7D\u8D7B\u8D79" + + "\u8DBC\u8DC2\u8DB9\u8DBF\u8DC1\u8ED8\u8EDE\u8EDD" + + "\u8EDC\u8ED7\u8EE0\u8EE1\u9024\u900B\u9011\u901C" + + "\u900C\u9021\u90EF\u90EA\u90F0\u90F4\u90F2\u90F3" + + "\u90D4\u90EB\u90EC\u90E9\u9156\u9158\u915A\u9153" + + "\u9155\u91EC\u91F4\u91F1\u91F3\u91F8\u91E4\u91F9" + + "\u91EA\u91EB\u91F7\u91E8\u91EE\u957A\u9586\u9588" + + "\u967C\u966D\u966B\u9671\u966F\u96BF\u976A\u9804" + + "\u98E5\u9997\u509B\u5095\u5094\u509E\u508B\u50A3" + + "\u5083\u508C\u508E\u509D\u5068\u509C\u5092\u5082" + + "\u5087\u515F\u51D4\u5312\u5311\u53A4\u53A7\u5591" + + "\u55A8\u55A5\u55AD\u5577\u5645\u55A2\u5593\u5588" + + "\u558F\u55B5\u5581\u55A3\u5592\u55A4\u557D\u558C" + + "\u55A6\u557F\u5595\u55A1\u558E\u570C\u5829\u5837" + + "\u5819\u581E\u5827\u5823\u5828\u57F5\u5848\u5825" + + "\u581C\u581B\u5833\u583F\u5836\u582E\u5839\u5838" + + "\u582D\u582C\u583B\u5961\u5AAF\u5A94\u5A9F\u5A7A" + + "\u5AA2\u5A9E\u5A78\u5AA6\u5A7C\u5AA5\u5AAC\u5A95" + + "\u5AAE\u5A37\u5A84\u5A8A\u5A97\u5A83\u5A8B\u5AA9" + + "\u5A7B\u5A7D\u5A8C\u5A9C\u5A8F\u5A93\u5A9D\u5BEA" + + "\u5BCD\u5BCB\u5BD4\u5BD1\u5BCA\u5BCE\u5C0C\u5C30" + + "\u5D37\u5D43\u5D6B\u5D41\u5D4B\u5D3F\u5D35\u5D51" + + "\u5D4E\u5D55\u5D33\u5D3A\u5D52\u5D3D\u5D31\u5D59" + + "\u5D42\u5D39\u5D49\u5D38\u5D3C\u5D32\u5D36\u5D40" + + "\u5D45\u5E44\u5E41\u5F58\u5FA6\u5FA5\u5FAB\u60C9" + + "\u60B9\u60CC\u60E2\u60CE\u60C4\u6114\u60F2\u610A" + + "\u6116\u6105\u60F5\u6113\u60F8\u60FC\u60FE\u60C1" + + "\u6103\u6118\u611D\u6110\u60FF\u6104\u610B\u624A" + + "\u6394\u63B1\u63B0\u63CE\u63E5\u63E8\u63EF\u63C3" + + "\u649D\u63F3\u63CA\u63E0\u63F6\u63D5\u63F2\u63F5" + + "\u6461\u63DF\u63BE\u63DD\u63DC\u63C4\u63D8\u63D3" + + "\u63C2\u63C7\u63CC\u63CB\u63C8\u63F0\u63D7\u63D9" + + "\u6532\u6567\u656A\u6564\u655C\u6568\u6565\u658C" + + "\u659D\u659E\u65AE\u65D0\u65D2\u667C\u666C\u667B" + + "\u6680\u6671\u6679\u666A\u6672\u6701\u690C\u68D3" + + "\u6904\u68DC\u692A\u68EC\u68EA\u68F1\u690F\u68D6" + + "\u68F7\u68EB\u68E4\u68F6\u6913\u6910\u68F3\u68E1" + + "\u6907\u68CC\u6908\u6970\u68B4\u6911\u68EF\u68C6" + + "\u6914\u68F8\u68D0\u68FD\u68FC\u68E8\u690B\u690A" + + "\u6917\u68CE\u68C8\u68DD\u68DE\u68E6\u68F4\u68D1" + + "\u6906\u68D4\u68E9\u6915\u6925\u68C7\u6B39\u6B3B" + + "\u6B3F\u6B3C\u6B94\u6B97\u6B99\u6B95\u6BBD\u6BF0" + + "\u6BF2\u6BF3\u6C30\u6DFC\u6E46\u6E47\u6E1F\u6E49" + + "\u6E88\u6E3C\u6E3D\u6E45\u6E62\u6E2B\u6E3F\u6E41" + + "\u6E5D\u6E73\u6E1C\u6E33\u6E4B\u6E40\u6E51\u6E3B" + + "\u6E03\u6E2E\u6E5E\u6E68\u6E5C\u6E61\u6E31\u6E28" + + "\u6E60\u6E71\u6E6B\u6E39\u6E22\u6E30\u6E53\u6E65" + + "\u6E27\u6E78\u6E64\u6E77\u6E55\u6E79\u6E52\u6E66" + + "\u6E35\u6E36\u6E5A\u7120\u711E\u712F\u70FB\u712E" + + "\u7131\u7123\u7125\u7122\u7132\u711F\u7128\u713A" + + "\u711B\u724B\u725A\u7288\u7289\u7286\u7285\u728B" + + "\u7312\u730B\u7330\u7322\u7331\u7333\u7327\u7332" + + "\u732D\u7326\u7323\u7335\u730C\u742E\u742C\u7430" + + "\u742B\u7416\u741A\u7421\u742D\u7431\u7424\u7423" + + "\u741D\u7429\u7420\u7432\u74FB\u752F\u756F\u756C" + + "\u75E7\u75DA\u75E1\u75E6\u75DD\u75DF\u75E4\u75D7" + + "\u7695\u7692\u76DA\u7746\u7747\u7744\u774D\u7745" + + "\u774A\u774E\u774B\u774C\u77DE\u77EC\u7860\u7864" + + "\u7865\u785C\u786D\u7871\u786A\u786E\u7870\u7869" + + "\u7868\u785E\u7862\u7974\u7973\u7972\u7970\u7A02" + + "\u7A0A\u7A03\u7A0C\u7A04\u7A99\u7AE6\u7AE4\u7B4A" + + "\u7B47\u7B44\u7B48\u7B4C\u7B4E\u7B40\u7B58\u7B45" + + "\u7CA2\u7C9E\u7CA8\u7CA1\u7D58\u7D6F\u7D63\u7D53" + + "\u7D56\u7D67\u7D6A\u7D4F\u7D6D\u7D5C\u7D6B\u7D52" + + "\u7D54\u7D69\u7D51\u7D5F\u7D4E\u7F3E\u7F3F\u7F65" + + "\u7F66\u7FA2\u7FA0\u7FA1\u7FD7\u8051\u804F\u8050" + + "\u80FE\u80D4\u8143\u814A\u8152\u814F\u8147\u813D" + + "\u814D\u813A\u81E6\u81EE\u81F7\u81F8\u81F9\u8204" + + "\u823C\u823D\u823F\u8275\u833B\u83CF\u83F9\u8423" + + "\u83C0\u83E8\u8412\u83E7\u83E4\u83FC\u83F6\u8410" + + "\u83C6\u83C8\u83EB\u83E3\u83BF\u8401\u83DD\u83E5" + + "\u83D8\u83FF\u83E1\u83CB\u83CE\u83D6\u83F5\u83C9" + + "\u8409\u840F\u83DE\u8411\u8406\u83C2\u83F3\u83D5" + + "\u83FA\u83C7\u83D1\u83EA\u8413\u839A\u83C3\u83EC" + + "\u83EE\u83C4\u83FB\u83D7\u83E2\u841B\u83DB\u83FE" + + "\u86D8\u86E2\u86E6\u86D3\u86E3\u86DA\u86EA\u86DD" + + "\u86EB\u86DC\u86EC\u86E9\u86D7\u86E8\u86D1\u8848" + + "\u8856\u8855\u88BA\u88D7\u88B9\u88B8\u88C0\u88BE" + + "\u88B6\u88BC\u88B7\u88BD\u88B2\u8901\u88C9\u8995" + + "\u8998\u8997\u89DD\u89DA\u89DB\u8A4E\u8A4D\u8A39" + + "\u8A59\u8A40\u8A57\u8A58\u8A44\u8A45\u8A52\u8A48" + + "\u8A51\u8A4A\u8A4C\u8A4F\u8C5F\u8C81\u8C80\u8CBA" + + "\u8CBE\u8CB0\u8CB9\u8CB5\u8D84\u8D80\u8D89\u8DD8" + + "\u8DD3\u8DCD\u8DC7\u8DD6\u8DDC\u8DCF\u8DD5\u8DD9" + + "\u8DC8\u8DD7\u8DC5\u8EEF\u8EF7\u8EFA\u8EF9\u8EE6" + + "\u8EEE\u8EE5\u8EF5\u8EE7\u8EE8\u8EF6\u8EEB\u8EF1" + + "\u8EEC\u8EF4\u8EE9\u902D\u9034\u902F\u9106\u912C" + + "\u9104\u90FF\u90FC\u9108\u90F9\u90FB\u9101\u9100" + + "\u9107\u9105\u9103\u9161\u9164\u915F\u9162\u9160" + + "\u9201\u920A\u9225\u9203\u921A\u9226\u920F\u920C" + + "\u9200\u9212\u91FF\u91FD\u9206\u9204\u9227\u9202" + + "\u921C\u9224\u9219\u9217\u9205\u9216\u957B\u958D" + + "\u958C\u9590\u9687\u967E\u9688\u9689\u9683\u9680" + + "\u96C2\u96C8\u96C3\u96F1\u96F0\u976C\u9770\u976E" + + "\u9807\u98A9\u98EB\u9CE6\u9EF9\u4E83\u4E84\u4EB6" + + "\u50BD\u50BF\u50C6\u50AE\u50C4\u50CA\u50B4\u50C8" + + "\u50C2\u50B0\u50C1\u50BA\u50B1\u50CB\u50C9\u50B6" + + "\u50B8\u51D7\u527A\u5278\u527B\u527C\u55C3\u55DB" + + "\u55CC\u55D0\u55CB\u55CA\u55DD\u55C0\u55D4\u55C4" + + "\u55E9\u55BF\u55D2\u558D\u55CF\u55D5\u55E2\u55D6" + + "\u55C8\u55F2\u55CD\u55D9\u55C2\u5714\u5853\u5868" + + "\u5864\u584F\u584D\u5849\u586F\u5855\u584E\u585D" + + "\u5859\u5865\u585B\u583D\u5863\u5871\u58FC\u5AC7" + + "\u5AC4\u5ACB\u5ABA\u5AB8\u5AB1\u5AB5\u5AB0\u5ABF" + + "\u5AC8\u5ABB\u5AC6\u5AB7\u5AC0\u5ACA\u5AB4\u5AB6" + + "\u5ACD\u5AB9\u5A90\u5BD6\u5BD8\u5BD9\u5C1F\u5C33" + + "\u5D71\u5D63\u5D4A\u5D65\u5D72\u5D6C\u5D5E\u5D68" + + "\u5D67\u5D62\u5DF0\u5E4F\u5E4E\u5E4A\u5E4D\u5E4B" + + "\u5EC5\u5ECC\u5EC6\u5ECB\u5EC7\u5F40\u5FAF\u5FAD" + + "\u60F7\u6149\u614A\u612B\u6145\u6136\u6132\u612E" + + "\u6146\u612F\u614F\u6129\u6140\u6220\u9168\u6223" + + "\u6225\u6224\u63C5\u63F1\u63EB\u6410\u6412\u6409" + + "\u6420\u6424\u6433\u6443\u641F\u6415\u6418\u6439" + + "\u6437\u6422\u6423\u640C\u6426\u6430\u6428\u6441" + + "\u6435\u642F\u640A\u641A\u6440\u6425\u6427\u640B" + + "\u63E7\u641B\u642E\u6421\u640E\u656F\u6592\u65D3" + + "\u6686\u668C\u6695\u6690\u668B\u668A\u6699\u6694" + + "\u6678\u6720\u6966\u695F\u6938\u694E\u6962\u6971" + + "\u693F\u6945\u696A\u6939\u6942\u6957\u6959\u697A" + + "\u6948\u6949\u6935\u696C\u6933\u693D\u6965\u68F0" + + "\u6978\u6934\u6969\u6940\u696F\u6944\u6976\u6958" + + "\u6941\u6974\u694C\u693B\u694B\u6937\u695C\u694F" + + "\u6951\u6932\u6952\u692F\u697B\u693C\u6B46\u6B45" + + "\u6B43\u6B42\u6B48\u6B41\u6B9B\u6BFB\u6BFC\u6BF9" + + "\u6BF7\u6BF8\u6E9B\u6ED6\u6EC8\u6E8F\u6EC0\u6E9F" + + "\u6E93\u6E94\u6EA0\u6EB1\u6EB9\u6EC6\u6ED2\u6EBD" + + "\u6EC1\u6E9E\u6EC9\u6EB7\u6EB0\u6ECD\u6EA6\u6ECF" + + "\u6EB2\u6EBE\u6EC3\u6EDC\u6ED8\u6E99\u6E92\u6E8E" + + "\u6E8D\u6EA4\u6EA1\u6EBF\u6EB3\u6ED0\u6ECA\u6E97" + + "\u6EAE\u6EA3\u7147\u7154\u7152\u7163\u7160\u7141" + + "\u715D\u7162\u7172\u7178\u716A\u7161\u7142\u7158" + + "\u7143\u714B\u7170\u715F\u7150\u7153\u7144\u714D" + + "\u715A\u724F\u728D\u728C\u7291\u7290\u728E\u733C" + + "\u7342\u733B\u733A\u7340\u734A\u7349\u7444\u744A" + + "\u744B\u7452\u7451\u7457\u7440\u744F\u7450\u744E" + + "\u7442\u7446\u744D\u7454\u74E1\u74FF\u74FE\u74FD" + + "\u751D\u7579\u7577\u6983\u75EF\u760F\u7603\u75F7" + + "\u75FE\u75FC\u75F9\u75F8\u7610\u75FB\u75F6\u75ED" + + "\u75F5\u75FD\u7699\u76B5\u76DD\u7755\u775F\u7760" + + "\u7752\u7756\u775A\u7769\u7767\u7754\u7759\u776D" + + "\u77E0\u7887\u789A\u7894\u788F\u7884\u7895\u7885" + + "\u7886\u78A1\u7883\u7879\u7899\u7880\u7896\u787B" + + "\u797C\u7982\u797D\u7979\u7A11\u7A18\u7A19\u7A12" + + "\u7A17\u7A15\u7A22\u7A13\u7A1B\u7A10\u7AA3\u7AA2" + + "\u7A9E\u7AEB\u7B66\u7B64\u7B6D\u7B74\u7B69\u7B72" + + "\u7B65\u7B73\u7B71\u7B70\u7B61\u7B78\u7B76\u7B63" + + "\u7CB2\u7CB4\u7CAF\u7D88\u7D86\u7D80\u7D8D\u7D7F" + + "\u7D85\u7D7A\u7D8E\u7D7B\u7D83\u7D7C\u7D8C\u7D94" + + "\u7D84\u7D7D\u7D92\u7F6D\u7F6B\u7F67\u7F68\u7F6C" + + "\u7FA6\u7FA5\u7FA7\u7FDB\u7FDC\u8021\u8164\u8160" + + "\u8177\u815C\u8169\u815B\u8162\u8172\u6721\u815E" + + "\u8176\u8167\u816F\u8144\u8161\u821D\u8249\u8244" + + "\u8240\u8242\u8245\u84F1\u843F\u8456\u8476\u8479" + + "\u848F\u848D\u8465\u8451\u8440\u8486\u8467\u8430" + + "\u844D\u847D\u845A\u8459\u8474\u8473\u845D\u8507" + + "\u845E\u8437\u843A\u8434\u847A\u8443\u8478\u8432" + + "\u8445\u8429\u83D9\u844B\u842F\u8442\u842D\u845F" + + "\u8470\u8439\u844E\u844C\u8452\u846F\u84C5\u848E" + + "\u843B\u8447\u8436\u8433\u8468\u847E\u8444\u842B" + + "\u8460\u8454\u846E\u8450\u870B\u8704\u86F7\u870C" + + "\u86FA\u86D6\u86F5\u874D\u86F8\u870E\u8709\u8701" + + "\u86F6\u870D\u8705\u88D6\u88CB\u88CD\u88CE\u88DE" + + "\u88DB\u88DA\u88CC\u88D0\u8985\u899B\u89DF\u89E5" + + "\u89E4\u89E1\u89E0\u89E2\u89DC\u89E6\u8A76\u8A86" + + "\u8A7F\u8A61\u8A3F\u8A77\u8A82\u8A84\u8A75\u8A83" + + "\u8A81\u8A74\u8A7A\u8C3C\u8C4B\u8C4A\u8C65\u8C64" + + "\u8C66\u8C86\u8C84\u8C85\u8CCC\u8D68\u8D69\u8D91" + + "\u8D8C\u8D8E\u8D8F\u8D8D\u8D93\u8D94\u8D90\u8D92" + + "\u8DF0\u8DE0\u8DEC\u8DF1\u8DEE\u8DD0\u8DE9\u8DE3" + + "\u8DE2\u8DE7\u8DF2\u8DEB\u8DF4\u8F06\u8EFF\u8F01" + + "\u8F00\u8F05\u8F07\u8F08\u8F02\u8F0B\u9052\u903F" + + "\u9044\u9049\u903D\u9110\u910D\u910F\u9111\u9116" + + "\u9114\u910B\u910E\u916E\u916F\u9248\u9252\u9230" + + "\u923A\u9266\u9233\u9265\u925E\u9283\u922E\u924A" + + "\u9246\u926D\u926C\u924F\u9260\u9267\u926F\u9236" + + "\u9261\u9270\u9231\u9254\u9263\u9250\u9272\u924E" + + "\u9253\u924C\u9256\u9232\u959F\u959C\u959E\u959B" + + "\u9692\u9693\u9691\u9697\u96CE\u96FA\u96FD\u96F8" + + "\u96F5\u9773\u9777\u9778\u9772\u980F\u980D\u980E" + + "\u98AC\u98F6\u98F9\u99AF\u99B2\u99B0\u99B5\u9AAD" + + "\u9AAB\u9B5B\u9CEA\u9CED\u9CE7\u9E80\u9EFD\u50E6" + + "\u50D4\u50D7\u50E8\u50F3\u50DB\u50EA\u50DD\u50E4" + + "\u50D3\u50EC\u50F0\u50EF\u50E3\u50E0\u51D8\u5280" + + "\u5281\u52E9\u52EB\u5330\u53AC\u5627\u5615\u560C" + + "\u5612\u55FC\u560F\u561C\u5601\u5613\u5602\u55FA" + + "\u561D\u5604\u55FF\u55F9\u5889\u587C\u5890\u5898" + + "\u5886\u5881\u587F\u5874\u588B\u587A\u5887\u5891" + + "\u588E\u5876\u5882\u5888\u587B\u5894\u588F\u58FE" + + "\u596B\u5ADC\u5AEE\u5AE5\u5AD5\u5AEA\u5ADA\u5AED" + + "\u5AEB\u5AF3\u5AE2\u5AE0\u5ADB\u5AEC\u5ADE\u5ADD" + + "\u5AD9\u5AE8\u5ADF\u5B77\u5BE0\u5BE3\u5C63\u5D82" + + "\u5D80\u5D7D\u5D86\u5D7A\u5D81\u5D77\u5D8A\u5D89" + + "\u5D88\u5D7E\u5D7C\u5D8D\u5D79\u5D7F\u5E58\u5E59" + + "\u5E53\u5ED8\u5ED1\u5ED7\u5ECE\u5EDC\u5ED5\u5ED9" + + "\u5ED2\u5ED4\u5F44\u5F43\u5F6F\u5FB6\u612C\u6128" + + "\u6141\u615E\u6171\u6173\u6152\u6153\u6172\u616C" + + "\u6180\u6174\u6154\u617A\u615B\u6165\u613B\u616A" + + "\u6161\u6156\u6229\u6227\u622B\u642B\u644D\u645B" + + "\u645D\u6474\u6476\u6472\u6473\u647D\u6475\u6466" + + "\u64A6\u644E\u6482\u645E\u645C\u644B\u6453\u6460" + + "\u6450\u647F\u643F\u646C\u646B\u6459\u6465\u6477" + + "\u6573\u65A0\u66A1\u66A0\u669F\u6705\u6704\u6722" + + "\u69B1\u69B6\u69C9\u69A0\u69CE\u6996\u69B0\u69AC" + + "\u69BC\u6991\u6999\u698E\u69A7\u698D\u69A9\u69BE" + + "\u69AF\u69BF\u69C4\u69BD\u69A4\u69D4\u69B9\u69CA" + + "\u699A\u69CF\u69B3\u6993\u69AA\u69A1\u699E\u69D9" + + "\u6997\u6990\u69C2\u69B5\u69A5\u69C6\u6B4A\u6B4D" + + "\u6B4B\u6B9E\u6B9F\u6BA0\u6BC3\u6BC4\u6BFE\u6ECE" + + "\u6EF5\u6EF1\u6F03\u6F25\u6EF8\u6F37\u6EFB\u6F2E" + + "\u6F09\u6F4E\u6F19\u6F1A\u6F27\u6F18\u6F3B\u6F12" + + "\u6EED\u6F0A\u6F36\u6F73\u6EF9\u6EEE\u6F2D\u6F40" + + "\u6F30\u6F3C\u6F35\u6EEB\u6F07\u6F0E\u6F43\u6F05" + + "\u6EFD\u6EF6\u6F39\u6F1C\u6EFC\u6F3A\u6F1F\u6F0D" + + "\u6F1E\u6F08\u6F21\u7187\u7190\u7189\u7180\u7185" + + "\u7182\u718F\u717B\u7186\u7181\u7197\u7244\u7253" + + "\u7297\u7295\u7293\u7343\u734D\u7351\u734C\u7462" + + "\u7473\u7471\u7475\u7472\u7467\u746E\u7500\u7502" + + "\u7503\u757D\u7590\u7616\u7608\u760C\u7615\u7611" + + "\u760A\u7614\u76B8\u7781\u777C\u7785\u7782\u776E" + + "\u7780\u776F\u777E\u7783\u78B2\u78AA\u78B4\u78AD" + + "\u78A8\u787E\u78AB\u789E\u78A5\u78A0\u78AC\u78A2" + + "\u78A4\u7998\u798A\u798B\u7996\u7995\u7994\u7993" + + "\u7997\u7988\u7992\u7990\u7A2B\u7A4A\u7A30\u7A2F" + + "\u7A28\u7A26\u7AA8\u7AAB\u7AAC\u7AEE\u7B88\u7B9C" + + "\u7B8A\u7B91\u7B90\u7B96\u7B8D\u7B8C\u7B9B\u7B8E" + + "\u7B85\u7B98\u5284\u7B99\u7BA4\u7B82\u7CBB\u7CBF" + + "\u7CBC\u7CBA\u7DA7\u7DB7\u7DC2\u7DA3\u7DAA\u7DC1" + + "\u7DC0\u7DC5\u7D9D\u7DCE\u7DC4\u7DC6\u7DCB\u7DCC" + + "\u7DAF\u7DB9\u7D96\u7DBC\u7D9F\u7DA6\u7DAE\u7DA9" + + "\u7DA1\u7DC9\u7F73\u7FE2\u7FE3\u7FE5\u7FDE\u8024" + + "\u805D\u805C\u8189\u8186\u8183\u8187\u818D\u818C" + + "\u818B\u8215\u8497\u84A4\u84A1\u849F\u84BA\u84CE" + + "\u84C2\u84AC\u84AE\u84AB\u84B9\u84B4\u84C1\u84CD" + + "\u84AA\u849A\u84B1\u84D0\u849D\u84A7\u84BB\u84A2" + + "\u8494\u84C7\u84CC\u849B\u84A9\u84AF\u84A8\u84D6" + + "\u8498\u84B6\u84CF\u84A0\u84D7\u84D4\u84D2\u84DB" + + "\u84B0\u8491\u8661\u8733\u8723\u8728\u876B\u8740" + + "\u872E\u871E\u8721\u8719\u871B\u8743\u872C\u8741" + + "\u873E\u8746\u8720\u8732\u872A\u872D\u873C\u8712" + + "\u873A\u8731\u8735\u8742\u8726\u8727\u8738\u8724" + + "\u871A\u8730\u8711\u88F7\u88E7\u88F1\u88F2\u88FA" + + "\u88FE\u88EE\u88FC\u88F6\u88FB\u88F0\u88EC\u88EB" + + "\u899D\u89A1\u899F\u899E\u89E9\u89EB\u89E8\u8AAB" + + "\u8A99\u8A8B\u8A92\u8A8F\u8A96\u8C3D\u8C68\u8C69" + + "\u8CD5\u8CCF\u8CD7\u8D96\u8E09\u8E02\u8DFF\u8E0D" + + "\u8DFD\u8E0A\u8E03\u8E07\u8E06\u8E05\u8DFE\u8E00" + + "\u8E04\u8F10\u8F11\u8F0E\u8F0D\u9123\u911C\u9120" + + "\u9122\u911F\u911D\u911A\u9124\u9121\u911B\u917A" + + "\u9172\u9179\u9173\u92A5\u92A4\u9276\u929B\u927A" + + "\u92A0\u9294\u92AA\u928D\u92A6\u929A\u92AB\u9279" + + "\u9297\u927F\u92A3\u92EE\u928E\u9282\u9295\u92A2" + + "\u927D\u9288\u92A1\u928A\u9286\u928C\u9299\u92A7" + + "\u927E\u9287\u92A9\u929D\u928B\u922D\u969E\u96A1" + + "\u96FF\u9758\u977D\u977A\u977E\u9783\u9780\u9782" + + "\u977B\u9784\u9781\u977F\u97CE\u97CD\u9816\u98AD" + + "\u98AE\u9902\u9900\u9907\u999D\u999C\u99C3\u99B9" + + "\u99BB\u99BA\u99C2\u99BD\u99C7\u9AB1\u9AE3\u9AE7" + + "\u9B3E\u9B3F\u9B60\u9B61\u9B5F\u9CF1\u9CF2\u9CF5" + + "\u9EA7\u50FF\u5103\u5130\u50F8\u5106\u5107\u50F6" + + "\u50FE\u510B\u510C\u50FD\u510A\u528B\u528C\u52F1" + + "\u52EF\u5648\u5642\u564C\u5635\u5641\u564A\u5649" + + "\u5646\u5658\u565A\u5640\u5633\u563D\u562C\u563E" + + "\u5638\u562A\u563A\u571A\u58AB\u589D\u58B1\u58A0" + + "\u58A3\u58AF\u58AC\u58A5\u58A1\u58FF\u5AFF\u5AF4" + + "\u5AFD\u5AF7\u5AF6\u5B03\u5AF8\u5B02\u5AF9\u5B01" + + "\u5B07\u5B05\u5B0F\u5C67\u5D99\u5D97\u5D9F\u5D92" + + "\u5DA2\u5D93\u5D95\u5DA0\u5D9C\u5DA1\u5D9A\u5D9E" + + "\u5E69\u5E5D\u5E60\u5E5C\u7DF3\u5EDB\u5EDE\u5EE1" + + "\u5F49\u5FB2\u618B\u6183\u6179\u61B1\u61B0\u61A2" + + "\u6189\u619B\u6193\u61AF\u61AD\u619F\u6192\u61AA" + + "\u61A1\u618D\u6166\u61B3\u622D\u646E\u6470\u6496" + + "\u64A0\u6485\u6497\u649C\u648F\u648B\u648A\u648C" + + "\u64A3\u649F\u6468\u64B1\u6498\u6576\u657A\u6579" + + "\u657B\u65B2\u65B3\u66B5\u66B0\u66A9\u66B2\u66B7" + + "\u66AA\u66AF\u6A00\u6A06\u6A17\u69E5\u69F8\u6A15" + + "\u69F1\u69E4\u6A20\u69FF\u69EC\u69E2\u6A1B\u6A1D" + + "\u69FE\u6A27\u69F2\u69EE\u6A14\u69F7\u69E7\u6A40" + + "\u6A08\u69E6\u69FB\u6A0D\u69FC\u69EB\u6A09\u6A04" + + "\u6A18\u6A25\u6A0F\u69F6\u6A26\u6A07\u69F4\u6A16" + + "\u6B51\u6BA5\u6BA3\u6BA2\u6BA6\u6C01\u6C00\u6BFF" + + "\u6C02\u6F41\u6F26\u6F7E\u6F87\u6FC6\u6F92\u6F8D" + + "\u6F89\u6F8C\u6F62\u6F4F\u6F85\u6F5A\u6F96\u6F76" + + "\u6F6C\u6F82\u6F55\u6F72\u6F52\u6F50\u6F57\u6F94" + + "\u6F93\u6F5D\u6F00\u6F61\u6F6B\u6F7D\u6F67\u6F90" + + "\u6F53\u6F8B\u6F69\u6F7F\u6F95\u6F63\u6F77\u6F6A" + + "\u6F7B\u71B2\u71AF\u719B\u71B0\u71A0\u719A\u71A9" + + "\u71B5\u719D\u71A5\u719E\u71A4\u71A1\u71AA\u719C" + + "\u71A7\u71B3\u7298\u729A\u7358\u7352\u735E\u735F" + + "\u7360\u735D\u735B\u7361\u735A\u7359\u7362\u7487" + + "\u7489\u748A\u7486\u7481\u747D\u7485\u7488\u747C" + + "\u7479\u7508\u7507\u757E\u7625\u761E\u7619\u761D" + + "\u761C\u7623\u761A\u7628\u761B\u769C\u769D\u769E" + + "\u769B\u778D\u778F\u7789\u7788\u78CD\u78BB\u78CF" + + "\u78CC\u78D1\u78CE\u78D4\u78C8\u78C3\u78C4\u78C9" + + "\u799A\u79A1\u79A0\u799C\u79A2\u799B\u6B76\u7A39" + + "\u7AB2\u7AB4\u7AB3\u7BB7\u7BCB\u7BBE\u7BAC\u7BCE" + + "\u7BAF\u7BB9\u7BCA\u7BB5\u7CC5\u7CC8\u7CCC\u7CCB" + + "\u7DF7\u7DDB\u7DEA\u7DE7\u7DD7\u7DE1\u7E03\u7DFA" + + "\u7DE6\u7DF6\u7DF1\u7DF0\u7DEE\u7DDF\u7F76\u7FAC" + + "\u7FB0\u7FAD\u7FED\u7FEB\u7FEA\u7FEC\u7FE6\u7FE8" + + "\u8064\u8067\u81A3\u819F\u819E\u8195\u81A2\u8199" + + "\u8197\u8216\u824F\u8253\u8252\u8250\u824E\u8251" + + "\u8524\u853B\u850F\u8500\u8529\u850E\u8509\u850D" + + "\u851F\u850A\u8527\u851C\u84FB\u852B\u84FA\u8508" + + "\u850C\u84F4\u852A\u84F2\u8515\u84F7\u84EB\u84F3" + + "\u84FC\u8512\u84EA\u84E9\u8516\u84FE\u8528\u851D" + + "\u852E\u8502\u84FD\u851E\u84F6\u8531\u8526\u84E7" + + "\u84E8\u84F0\u84EF\u84F9\u8518\u8520\u8530\u850B" + + "\u8519\u852F\u8662\u8756\u8763\u8764\u8777\u87E1" + + "\u8773\u8758\u8754\u875B\u8752\u8761\u875A\u8751" + + "\u875E\u876D\u876A\u8750\u874E\u875F\u875D\u876F" + + "\u876C\u877A\u876E\u875C\u8765\u874F\u877B\u8775" + + "\u8762\u8767\u8769\u885A\u8905\u890C\u8914\u890B" + + "\u8917\u8918\u8919\u8906\u8916\u8911\u890E\u8909" + + "\u89A2\u89A4\u89A3\u89ED\u89F0\u89EC\u8ACF\u8AC6" + + "\u8AB8\u8AD3\u8AD1\u8AD4\u8AD5\u8ABB\u8AD7\u8ABE" + + "\u8AC0\u8AC5\u8AD8\u8AC3\u8ABA\u8ABD\u8AD9\u8C3E" + + "\u8C4D\u8C8F\u8CE5\u8CDF\u8CD9\u8CE8\u8CDA\u8CDD" + + "\u8CE7\u8DA0\u8D9C\u8DA1\u8D9B\u8E20\u8E23\u8E25" + + "\u8E24\u8E2E\u8E15\u8E1B\u8E16\u8E11\u8E19\u8E26" + + "\u8E27\u8E14\u8E12\u8E18\u8E13\u8E1C\u8E17\u8E1A" + + "\u8F2C\u8F24\u8F18\u8F1A\u8F20\u8F23\u8F16\u8F17" + + "\u9073\u9070\u906F\u9067\u906B\u912F\u912B\u9129" + + "\u912A\u9132\u9126\u912E\u9185\u9186\u918A\u9181" + + "\u9182\u9184\u9180\u92D0\u92C3\u92C4\u92C0\u92D9" + + "\u92B6\u92CF\u92F1\u92DF\u92D8\u92E9\u92D7\u92DD" + + "\u92CC\u92EF\u92C2\u92E8\u92CA\u92C8\u92CE\u92E6" + + "\u92CD\u92D5\u92C9\u92E0\u92DE\u92E7\u92D1\u92D3" + + "\u92B5\u92E1\u9325\u92C6\u92B4\u957C\u95AC\u95AB" + + "\u95AE\u95B0\u96A4\u96A2\u96D3\u9705\u9708\u9702" + + "\u975A\u978A\u978E\u9788\u97D0\u97CF\u981E\u981D" + + "\u9826\u9829\u9828\u9820\u981B\u9827\u98B2\u9908" + + "\u98FA\u9911\u9914\u9916\u9917\u9915\u99DC\u99CD" + + "\u99CF\u99D3\u99D4\u99CE\u99C9\u99D6\u99D8\u99CB" + + "\u99D7\u99CC\u9AB3\u9AEC\u9AEB\u9AF3\u9AF2\u9AF1" + + "\u9B46\u9B43\u9B67\u9B74\u9B71\u9B66\u9B76\u9B75" + + "\u9B70\u9B68\u9B64\u9B6C\u9CFC\u9CFA\u9CFD\u9CFF" + + "\u9CF7\u9D07\u9D00\u9CF9\u9CFB\u9D08\u9D05\u9D04" + + "\u9E83\u9ED3\u9F0F\u9F10\u511C\u5113\u5117\u511A" + + "\u5111\u51DE\u5334\u53E1\u5670\u5660\u566E\u5673" + + "\u5666\u5663\u566D\u5672\u565E\u5677\u571C\u571B" + + "\u58C8\u58BD\u58C9\u58BF\u58BA\u58C2\u58BC\u58C6" + + "\u5B17\u5B19\u5B1B\u5B21\u5B14\u5B13\u5B10\u5B16" + + "\u5B28\u5B1A\u5B20\u5B1E\u5BEF\u5DAC\u5DB1\u5DA9" + + "\u5DA7\u5DB5\u5DB0\u5DAE\u5DAA\u5DA8\u5DB2\u5DAD" + + "\u5DAF\u5DB4\u5E67\u5E68\u5E66\u5E6F\u5EE9\u5EE7" + + "\u5EE6\u5EE8\u5EE5\u5F4B\u5FBC\u5FBB\u619D\u61A8" + + "\u6196\u61C5\u61B4\u61C6\u61C1\u61CC\u61BA\u61BF" + + "\u61B8\u618C\u64D7\u64D6\u64D0\u64CF\u64C9\u64BD" + + "\u6489\u64C3\u64DB\u64F3\u64D9\u6533\u657F\u657C" + + "\u65A2\u66C8\u66BE\u66C0\u66CA\u66CB\u66CF\u66BD" + + "\u66BB\u66BA\u66CC\u6723\u6A34\u6A66\u6A49\u6A67" + + "\u6A32\u6A68\u6A3E\u6A5D\u6A6D\u6A76\u6A5B\u6A51" + + "\u6A28\u6A5A\u6A3B\u6A3F\u6A41\u6A6A\u6A64\u6A50" + + "\u6A4F\u6A54\u6A6F\u6A69\u6A60\u6A3C\u6A5E\u6A56" + + "\u6A55\u6A4D\u6A4E\u6A46\u6B55\u6B54\u6B56\u6BA7" + + "\u6BAA\u6BAB\u6BC8\u6BC7\u6C04\u6C03\u6C06\u6FAD" + + "\u6FCB\u6FA3\u6FC7\u6FBC\u6FCE\u6FC8\u6F5E\u6FC4" + + "\u6FBD\u6F9E\u6FCA\u6FA8\u7004\u6FA5\u6FAE\u6FBA" + + "\u6FAC\u6FAA\u6FCF\u6FBF\u6FB8\u6FA2\u6FC9\u6FAB" + + "\u6FCD\u6FAF\u6FB2\u6FB0\u71C5\u71C2\u71BF\u71B8" + + "\u71D6\u71C0\u71C1\u71CB\u71D4\u71CA\u71C7\u71CF" + + "\u71BD\u71D8\u71BC\u71C6\u71DA\u71DB\u729D\u729E" + + "\u7369\u7366\u7367\u736C\u7365\u736B\u736A\u747F" + + "\u749A\u74A0\u7494\u7492\u7495\u74A1\u750B\u7580" + + "\u762F\u762D\u7631\u763D\u7633\u763C\u7635\u7632" + + "\u7630\u76BB\u76E6\u779A\u779D\u77A1\u779C\u779B" + + "\u77A2\u77A3\u7795\u7799\u7797\u78DD\u78E9\u78E5" + + "\u78EA\u78DE\u78E3\u78DB\u78E1\u78E2\u78ED\u78DF" + + "\u78E0\u79A4\u7A44\u7A48\u7A47\u7AB6\u7AB8\u7AB5" + + "\u7AB1\u7AB7\u7BDE\u7BE3\u7BE7\u7BDD\u7BD5\u7BE5" + + "\u7BDA\u7BE8\u7BF9\u7BD4\u7BEA\u7BE2\u7BDC\u7BEB" + + "\u7BD8\u7BDF\u7CD2\u7CD4\u7CD7\u7CD0\u7CD1\u7E12" + + "\u7E21\u7E17\u7E0C\u7E1F\u7E20\u7E13\u7E0E\u7E1C" + + "\u7E15\u7E1A\u7E22\u7E0B\u7E0F\u7E16\u7E0D\u7E14" + + "\u7E25\u7E24\u7F43\u7F7B\u7F7C\u7F7A\u7FB1\u7FEF" + + "\u802A\u8029\u806C\u81B1\u81A6\u81AE\u81B9\u81B5" + + "\u81AB\u81B0\u81AC\u81B4\u81B2\u81B7\u81A7\u81F2" + + "\u8255\u8256\u8257\u8556\u8545\u856B\u854D\u8553" + + "\u8561\u8558\u8540\u8546\u8564\u8541\u8562\u8544" + + "\u8551\u8547\u8563\u853E\u855B\u8571\u854E\u856E" + + "\u8575\u8555\u8567\u8560\u858C\u8566\u855D\u8554" + + "\u8565\u856C\u8663\u8665\u8664\u87A4\u879B\u878F" + + "\u8797\u8793\u8792\u8788\u8781\u8796\u8798\u8779" + + "\u8787\u87A3\u8785\u8790\u8791\u879D\u8784\u8794" + + "\u879C\u879A\u8789\u891E\u8926\u8930\u892D\u892E" + + "\u8927\u8931\u8922\u8929\u8923\u892F\u892C\u891F" + + "\u89F1\u8AE0\u8AE2\u8AF2\u8AF4\u8AF5\u8ADD\u8B14" + + "\u8AE4\u8ADF\u8AF0\u8AC8\u8ADE\u8AE1\u8AE8\u8AFF" + + "\u8AEF\u8AFB\u8C91\u8C92\u8C90\u8CF5\u8CEE\u8CF1" + + "\u8CF0\u8CF3\u8D6C\u8D6E\u8DA5\u8DA7\u8E33\u8E3E" + + "\u8E38\u8E40\u8E45\u8E36\u8E3C\u8E3D\u8E41\u8E30" + + "\u8E3F\u8EBD\u8F36\u8F2E\u8F35\u8F32\u8F39\u8F37" + + "\u8F34\u9076\u9079\u907B\u9086\u90FA\u9133\u9135" + + "\u9136\u9193\u9190\u9191\u918D\u918F\u9327\u931E" + + "\u9308\u931F\u9306\u930F\u937A\u9338\u933C\u931B" + + "\u9323\u9312\u9301\u9346\u932D\u930E\u930D\u92CB" + + "\u931D\u92FA\u9313\u92F9\u92F7\u9334\u9302\u9324" + + "\u92FF\u9329\u9339\u9335\u932A\u9314\u930C\u930B" + + "\u92FE\u9309\u9300\u92FB\u9316\u95BC\u95CD\u95BE" + + "\u95B9\u95BA\u95B6\u95BF\u95B5\u95BD\u96A9\u96D4" + + "\u970B\u9712\u9710\u9799\u9797\u9794\u97F0\u97F8" + + "\u9835\u982F\u9832\u9924\u991F\u9927\u9929\u999E" + + "\u99EE\u99EC\u99E5\u99E4\u99F0\u99E3\u99EA\u99E9" + + "\u99E7\u9AB9\u9ABF\u9AB4\u9ABB\u9AF6\u9AFA\u9AF9" + + "\u9AF7\u9B33\u9B80\u9B85\u9B87\u9B7C\u9B7E\u9B7B" + + "\u9B82\u9B93\u9B92\u9B90\u9B7A\u9B95\u9B7D\u9B88" + + "\u9D25\u9D17\u9D20\u9D1E\u9D14\u9D29\u9D1D\u9D18" + + "\u9D22\u9D10\u9D19\u9D1F\u9E88\u9E86\u9E87\u9EAE" + + "\u9EAD\u9ED5\u9ED6\u9EFA\u9F12\u9F3D\u5126\u5125" + + "\u5122\u5124\u5120\u5129\u52F4\u5693\u568C\u568D" + + "\u5686\u5684\u5683\u567E\u5682\u567F\u5681\u58D6" + + "\u58D4\u58CF\u58D2\u5B2D\u5B25\u5B32\u5B23\u5B2C" + + "\u5B27\u5B26\u5B2F\u5B2E\u5B7B\u5BF1\u5BF2\u5DB7" + + "\u5E6C\u5E6A\u5FBE\u61C3\u61B5\u61BC\u61E7\u61E0" + + "\u61E5\u61E4\u61E8\u61DE\u64EF\u64E9\u64E3\u64EB" + + "\u64E4\u64E8\u6581\u6580\u65B6\u65DA\u66D2\u6A8D" + + "\u6A96\u6A81\u6AA5\u6A89\u6A9F\u6A9B\u6AA1\u6A9E" + + "\u6A87\u6A93\u6A8E\u6A95\u6A83\u6AA8\u6AA4\u6A91" + + "\u6A7F\u6AA6\u6A9A\u6A85\u6A8C\u6A92\u6B5B\u6BAD" + + "\u6C09\u6FCC\u6FA9\u6FF4\u6FD4\u6FE3\u6FDC\u6FED" + + "\u6FE7\u6FE6\u6FDE\u6FF2\u6FDD\u6FE2\u6FE8\u71E1" + + "\u71F1\u71E8\u71F2\u71E4\u71F0\u71E2\u7373\u736E" + + "\u736F\u7497\u74B2\u74AB\u7490\u74AA\u74AD\u74B1" + + "\u74A5\u74AF\u7510\u7511\u7512\u750F\u7584\u7643" + + "\u7648\u7649\u7647\u76A4\u76E9\u77B5\u77AB\u77B2" + + "\u77B7\u77B6\u77B4\u77B1\u77A8\u77F0\u78F3\u78FD" + + "\u7902\u78FB\u78FC\u78FF\u78F2\u7905\u78F9\u78FE" + + "\u7904\u79AB\u79A8\u7A5C\u7A5B\u7A56\u7A58\u7A54" + + "\u7A5A\u7ABE\u7AC0\u7AC1\u7C05\u7C0F\u7BF2\u7C00" + + "\u7BFF\u7BFB\u7C0E\u7BF4\u7C0B\u7BF3\u7C02\u7C09" + + "\u7C03\u7C01\u7BF8\u7BFD\u7C06\u7BF0\u7BF1\u7C10" + + "\u7C0A\u7CE8\u7E2D\u7E3C\u7E42\u7E33\u9848\u7E38" + + "\u7E2A\u7E49\u7E40\u7E47\u7E29\u7E4C\u7E30\u7E3B" + + "\u7E36\u7E44\u7E3A\u7F45\u7F7F\u7F7E\u7F7D\u7FF4" + + "\u7FF2\u802C\u81BB\u81C4\u81CC\u81CA\u81C5\u81C7" + + "\u81BC\u81E9\u825B\u825A\u825C\u8583\u8580\u858F" + + "\u85A7\u8595\u85A0\u858B\u85A3\u857B\u85A4\u859A" + + "\u859E\u8577\u857C\u8589\u85A1\u857A\u8578\u8557" + + "\u858E\u8596\u8586\u858D\u8599\u859D\u8581\u85A2" + + "\u8582\u8588\u8585\u8579\u8576\u8598\u8590\u859F" + + "\u8668\u87BE\u87AA\u87AD\u87C5\u87B0\u87AC\u87B9" + + "\u87B5\u87BC\u87AE\u87C9\u87C3\u87C2\u87CC\u87B7" + + "\u87AF\u87C4\u87CA\u87B4\u87B6\u87BF\u87B8\u87BD" + + "\u87DE\u87B2\u8935\u8933\u893C\u893E\u8941\u8952" + + "\u8937\u8942\u89AD\u89AF\u89AE\u89F2\u89F3\u8B1E" + + "\u8B18\u8B16\u8B11\u8B05\u8B0B\u8B22\u8B0F\u8B12" + + "\u8B15\u8B07\u8B0D\u8B08\u8B06\u8B1C\u8B13\u8B1A" + + "\u8C4F\u8C70\u8C72\u8C71\u8C6F\u8C95\u8C94\u8CF9" + + "\u8D6F\u8E4E\u8E4D\u8E53\u8E50\u8E4C\u8E47\u8F43" + + "\u8F40\u9085\u907E\u9138\u919A\u91A2\u919B\u9199" + + "\u919F\u91A1\u919D\u91A0\u93A1\u9383\u93AF\u9364" + + "\u9356\u9347\u937C\u9358\u935C\u9376\u9349\u9350" + + "\u9351\u9360\u936D\u938F\u934C\u936A\u9379\u9357" + + "\u9355\u9352\u934F\u9371\u9377\u937B\u9361\u935E" + + "\u9363\u9367\u934E\u9359\u95C7\u95C0\u95C9\u95C3" + + "\u95C5\u95B7\u96AE\u96B0\u96AC\u9720\u971F\u9718" + + "\u971D\u9719\u979A\u97A1\u979C\u979E\u979D\u97D5" + + "\u97D4\u97F1\u9841\u9844\u984A\u9849\u9845\u9843" + + "\u9925\u992B\u992C\u992A\u9933\u9932\u992F\u992D" + + "\u9931\u9930\u9998\u99A3\u99A1\u9A02\u99FA\u99F4" + + "\u99F7\u99F9\u99F8\u99F6\u99FB\u99FD\u99FE\u99FC" + + "\u9A03\u9ABE\u9AFE\u9AFD\u9B01\u9AFC\u9B48\u9B9A" + + "\u9BA8\u9B9E\u9B9B\u9BA6\u9BA1\u9BA5\u9BA4\u9B86" + + "\u9BA2\u9BA0\u9BAF\u9D33\u9D41\u9D67\u9D36\u9D2E" + + "\u9D2F\u9D31\u9D38\u9D30\u9D45\u9D42\u9D43\u9D3E" + + "\u9D37\u9D40\u9D3D\u7FF5\u9D2D\u9E8A\u9E89\u9E8D" + + "\u9EB0\u9EC8\u9EDA\u9EFB\u9EFF\u9F24\u9F23\u9F22" + + "\u9F54\u9FA0\u5131\u512D\u512E\u5698\u569C\u5697" + + "\u569A\u569D\u5699\u5970\u5B3C\u5C69\u5C6A\u5DC0" + + "\u5E6D\u5E6E\u61D8\u61DF\u61ED\u61EE\u61F1\u61EA" + + "\u61F0\u61EB\u61D6\u61E9\u64FF\u6504\u64FD\u64F8" + + "\u6501\u6503\u64FC\u6594\u65DB\u66DA\u66DB\u66D8" + + "\u6AC5\u6AB9\u6ABD\u6AE1\u6AC6\u6ABA\u6AB6\u6AB7" + + "\u6AC7\u6AB4\u6AAD\u6B5E\u6BC9\u6C0B\u7007\u700C" + + "\u700D\u7001\u7005\u7014\u700E\u6FFF\u7000\u6FFB" + + "\u7026\u6FFC\u6FF7\u700A\u7201\u71FF\u71F9\u7203" + + "\u71FD\u7376\u74B8\u74C0\u74B5\u74C1\u74BE\u74B6" + + "\u74BB\u74C2\u7514\u7513\u765C\u7664\u7659\u7650" + + "\u7653\u7657\u765A\u76A6\u76BD\u76EC\u77C2\u77BA" + + "\u790C\u7913\u7914\u7909\u7910\u7912\u7911\u79AD" + + "\u79AC\u7A5F\u7C1C\u7C29\u7C19\u7C20\u7C1F\u7C2D" + + "\u7C1D\u7C26\u7C28\u7C22\u7C25\u7C30\u7E5C\u7E50" + + "\u7E56\u7E63\u7E58\u7E62\u7E5F\u7E51\u7E60\u7E57" + + "\u7E53\u7FB5\u7FB3\u7FF7\u7FF8\u8075\u81D1\u81D2" + + "\u81D0\u825F\u825E\u85B4\u85C6\u85C0\u85C3\u85C2" + + "\u85B3\u85B5\u85BD\u85C7\u85C4\u85BF\u85CB\u85CE" + + "\u85C8\u85C5\u85B1\u85B6\u85D2\u8624\u85B8\u85B7" + + "\u85BE\u8669\u87E7\u87E6\u87E2\u87DB\u87EB\u87EA" + + "\u87E5\u87DF\u87F3\u87E4\u87D4\u87DC\u87D3\u87ED" + + "\u87D8\u87E3\u87D7\u87D9\u8801\u87F4\u87E8\u87DD" + + "\u8953\u894B\u894F\u894C\u8946\u8950\u8951\u8949" + + "\u8B2A\u8B27\u8B23\u8B33\u8B30\u8B35\u8B47\u8B2F" + + "\u8B3C\u8B3E\u8B31\u8B25\u8B37\u8B26\u8B36\u8B2E" + + "\u8B24\u8B3B\u8B3D\u8B3A\u8C42\u8C75\u8C99\u8C98" + + "\u8C97\u8CFE\u8D04\u8D02\u8D00\u8E5C\u8E62\u8E60" + + "\u8E57\u8E56\u8E5E\u8E65\u8E67\u8E5B\u8E5A\u8E61" + + "\u8E5D\u8E69\u8E54\u8F46\u8F47\u8F48\u8F4B\u9128" + + "\u913A\u913B\u913E\u91A8\u91A5\u91A7\u91AF\u91AA" + + "\u93B5\u938C\u9392\u93B7\u939B\u939D\u9389\u93A7" + + "\u938E\u93AA\u939E\u93A6\u9395\u9388\u9399\u939F" + + "\u9380\u938D\u93B1\u9391\u93B2\u93A4\u93A8\u93B4" + + "\u93A3\u95D2\u95D3\u95D1\u96B3\u96D7\u96DA\u5DC2" + + "\u96DF\u96D8\u96DD\u9723\u9722\u9725\u97AC\u97AE" + + "\u97A8\u97AB\u97A4\u97AA\u97A2\u97A5\u97D7\u97D9" + + "\u97D6\u97D8\u97FA\u9850\u9851\u9852\u98B8\u9941" + + "\u993C\u993A\u9A0F\u9A0B\u9A09\u9A0D\u9A04\u9A11" + + "\u9A0A\u9A05\u9A07\u9A06\u9AC0\u9ADC\u9B08\u9B04" + + "\u9B05\u9B29\u9B35\u9B4A\u9B4C\u9B4B\u9BC7\u9BC6" + + "\u9BC3\u9BBF\u9BC1\u9BB5\u9BB8\u9BD3\u9BB6\u9BC4" + + "\u9BB9\u9BBD\u9D5C\u9D53\u9D4F\u9D4A\u9D5B\u9D4B" + + "\u9D59\u9D56\u9D4C\u9D57\u9D52\u9D54\u9D5F\u9D58" + + "\u9D5A\u9E8E\u9E8C\u9EDF\u9F01\u9F00\u9F16\u9F25" + + "\u9F2B\u9F2A\u9F29\u9F28\u9F4C\u9F55\u5134\u5135" + + "\u5296\u52F7\u53B4\u56AB\u56AD\u56A6\u56A7\u56AA" + + "\u56AC\u58DA\u58DD\u58DB\u5912\u5B3D\u5B3E\u5B3F" + + "\u5DC3\u5E70\u5FBF\u61FB\u6507\u6510\u650D\u6509" + + "\u650C\u650E\u6584\u65DE\u65DD\u66DE\u6AE7\u6AE0" + + "\u6ACC\u6AD1\u6AD9\u6ACB\u6ADF\u6ADC\u6AD0\u6AEB" + + "\u6ACF\u6ACD\u6ADE\u6B60\u6BB0\u6C0C\u7019\u7027" + + "\u7020\u7016\u702B\u7021\u7022\u7023\u7029\u7017" + + "\u7024\u701C\u720C\u720A\u7207\u7202\u7205\u72A5" + + "\u72A6\u72A4\u72A3\u72A1\u74CB\u74C5\u74B7\u74C3" + + "\u7516\u7660\u77C9\u77CA\u77C4\u77F1\u791D\u791B" + + "\u7921\u791C\u7917\u791E\u79B0\u7A67\u7A68\u7C33" + + "\u7C3C\u7C39\u7C2C\u7C3B\u7CEC\u7CEA\u7E76\u7E75" + + "\u7E78\u7E70\u7E77\u7E6F\u7E7A\u7E72\u7E74\u7E68" + + "\u7F4B\u7F4A\u7F83\u7F86\u7FB7\u7FFD\u7FFE\u8078" + + "\u81D7\u81D5\u820B\u8264\u8261\u8263\u85EB\u85F1" + + "\u85ED\u85D9\u85E1\u85E8\u85DA\u85D7\u85EC\u85F2" + + "\u85F8\u85D8\u85DF\u85E3\u85DC\u85D1\u85F0\u85E6" + + "\u85EF\u85DE\u85E2\u8800\u87FA\u8803\u87F6\u87F7" + + "\u8809\u880C\u880B\u8806\u87FC\u8808\u87FF\u880A" + + "\u8802\u8962\u895A\u895B\u8957\u8961\u895C\u8958" + + "\u895D\u8959\u8988\u89B7\u89B6\u89F6\u8B50\u8B48" + + "\u8B4A\u8B40\u8B53\u8B56\u8B54\u8B4B\u8B55\u8B51" + + "\u8B42\u8B52\u8B57\u8C43\u8C77\u8C76\u8C9A\u8D06" + + "\u8D07\u8D09\u8DAC\u8DAA\u8DAD\u8DAB\u8E6D\u8E78" + + "\u8E73\u8E6A\u8E6F\u8E7B\u8EC2\u8F52\u8F51\u8F4F" + + "\u8F50\u8F53\u8FB4\u9140\u913F\u91B0\u91AD\u93DE" + + "\u93C7\u93CF\u93C2\u93DA\u93D0\u93F9\u93EC\u93CC" + + "\u93D9\u93A9\u93E6\u93CA\u93D4\u93EE\u93E3\u93D5" + + "\u93C4\u93CE\u93C0\u93D2\u93A5\u93E7\u957D\u95DA" + + "\u95DB\u96E1\u9729\u972B\u972C\u9728\u9726\u97B3" + + "\u97B7\u97B6\u97DD\u97DE\u97DF\u985C\u9859\u985D" + + "\u9857\u98BF\u98BD\u98BB\u98BE\u9948\u9947\u9943" + + "\u99A6\u99A7\u9A1A\u9A15\u9A25\u9A1D\u9A24\u9A1B" + + "\u9A22\u9A20\u9A27\u9A23\u9A1E\u9A1C\u9A14\u9AC2" + + "\u9B0B\u9B0A\u9B0E\u9B0C\u9B37\u9BEA\u9BEB\u9BE0" + + "\u9BDE\u9BE4\u9BE6\u9BE2\u9BF0\u9BD4\u9BD7\u9BEC" + + "\u9BDC\u9BD9\u9BE5\u9BD5\u9BE1\u9BDA\u9D77\u9D81" + + "\u9D8A\u9D84\u9D88\u9D71\u9D80\u9D78\u9D86\u9D8B" + + "\u9D8C\u9D7D\u9D6B\u9D74\u9D75\u9D70\u9D69\u9D85" + + "\u9D73\u9D7B\u9D82\u9D6F\u9D79\u9D7F\u9D87\u9D68" + + "\u9E94\u9E91\u9EC0\u9EFC\u9F2D\u9F40\u9F41\u9F4D" + + "\u9F56\u9F57\u9F58\u5337\u56B2\u56B5\u56B3\u58E3" + + "\u5B45\u5DC6\u5DC7\u5EEE\u5EEF\u5FC0\u5FC1\u61F9" + + "\u6517\u6516\u6515\u6513\u65DF\u66E8\u66E3\u66E4" + + "\u6AF3\u6AF0\u6AEA\u6AE8\u6AF9\u6AF1\u6AEE\u6AEF" + + "\u703C\u7035\u702F\u7037\u7034\u7031\u7042\u7038" + + "\u703F\u703A\u7039\u702A\u7040\u703B\u7033\u7041" + + "\u7213\u7214\u72A8\u737D\u737C\u74BA\u76AB\u76AA" + + "\u76BE\u76ED\u77CC\u77CE\u77CF\u77CD\u77F2\u7925" + + "\u7923\u7927\u7928\u7924\u7929\u79B2\u7A6E\u7A6C" + + "\u7A6D\u7AF7\u7C49\u7C48\u7C4A\u7C47\u7C45\u7CEE" + + "\u7E7B\u7E7E\u7E81\u7E80\u7FBA\u7FFF\u8079\u81DB" + + "\u81D9\u8268\u8269\u8622\u85FF\u8601\u85FE\u861B" + + "\u8600\u85F6\u8604\u8609\u8605\u860C\u85FD\u8819" + + "\u8810\u8811\u8817\u8813\u8816\u8963\u8966\u89B9" + + "\u89F7\u8B60\u8B6A\u8B5D\u8B68\u8B63\u8B65\u8B67" + + "\u8B6D\u8DAE\u8E86\u8E88\u8E84\u8F59\u8F56\u8F57" + + "\u8F55\u8F58\u8F5A\u908D\u9143\u9141\u91B7\u91B5" + + "\u91B2\u91B3\u940B\u9413\u93FB\u9420\u940F\u9414" + + "\u93FE\u9415\u9410\u9428\u9419\u940D\u93F5\u9400" + + "\u93F7\u9407\u940E\u9416\u9412\u93FA\u9409\u93F8" + + "\u943C\u940A\u93FF\u93FC\u940C\u93F6\u9411\u9406" + + "\u95DE\u95E0\u95DF\u972E\u972F\u97B9\u97BB\u97FD" + + "\u97FE\u9860\u9862\u9863\u985F\u98C1\u98C2\u9950" + + "\u994E\u9959\u994C\u994B\u9953\u9A32\u9A34\u9A31" + + "\u9A2C\u9A2A\u9A36\u9A29\u9A2E\u9A38\u9A2D\u9AC7" + + "\u9ACA\u9AC6\u9B10\u9B12\u9B11\u9C0B\u9C08\u9BF7" + + "\u9C05\u9C12\u9BF8\u9C40\u9C07\u9C0E\u9C06\u9C17" + + "\u9C14\u9C09\u9D9F\u9D99\u9DA4\u9D9D\u9D92\u9D98" + + "\u9D90\u9D9B\u9DA0\u9D94\u9D9C\u9DAA\u9D97\u9DA1" + + "\u9D9A\u9DA2\u9DA8\u9D9E\u9DA3\u9DBF\u9DA9\u9D96" + + "\u9DA6\u9DA7\u9E99\u9E9B\u9E9A\u9EE5\u9EE4\u9EE7" + + "\u9EE6\u9F30\u9F2E\u9F5B\u9F60\u9F5E\u9F5D\u9F59" + + "\u9F91\u513A\u5139\u5298\u5297\u56C3\u56BD\u56BE" + + "\u5B48\u5B47\u5DCB\u5DCF\u5EF1\u61FD\u651B\u6B02" + + "\u6AFC\u6B03\u6AF8\u6B00\u7043\u7044\u704A\u7048" + + "\u7049\u7045\u7046\u721D\u721A\u7219\u737E\u7517" + + "\u766A\u77D0\u792D\u7931\u792F\u7C54\u7C53\u7CF2" + + "\u7E8A\u7E87\u7E88\u7E8B\u7E86\u7E8D\u7F4D\u7FBB" + + "\u8030\u81DD\u8618\u862A\u8626\u861F\u8623\u861C" + + "\u8619\u8627\u862E\u8621\u8620\u8629\u861E\u8625" + + "\u8829\u881D\u881B\u8820\u8824\u881C\u882B\u884A" + + "\u896D\u8969\u896E\u896B\u89FA\u8B79\u8B78\u8B45" + + "\u8B7A\u8B7B\u8D10\u8D14\u8DAF\u8E8E\u8E8C\u8F5E" + + "\u8F5B\u8F5D\u9146\u9144\u9145\u91B9\u943F\u943B" + + "\u9436\u9429\u943D\u9430\u9439\u942A\u9437\u942C" + + "\u9440\u9431\u95E5\u95E4\u95E3\u9735\u973A\u97BF" + + "\u97E1\u9864\u98C9\u98C6\u98C0\u9958\u9956\u9A39" + + "\u9A3D\u9A46\u9A44\u9A42\u9A41\u9A3A\u9A3F\u9ACD" + + "\u9B15\u9B17\u9B18\u9B16\u9B3A\u9B52\u9C2B\u9C1D" + + "\u9C1C\u9C2C\u9C23\u9C28\u9C29\u9C24\u9C21\u9DB7" + + "\u9DB6\u9DBC\u9DC1\u9DC7\u9DCA\u9DCF\u9DBE\u9DC5" + + "\u9DC3\u9DBB\u9DB5\u9DCE\u9DB9\u9DBA\u9DAC\u9DC8" + + "\u9DB1\u9DAD\u9DCC\u9DB3\u9DCD\u9DB2\u9E7A\u9E9C" + + "\u9EEB\u9EEE\u9EED\u9F1B\u9F18\u9F1A\u9F31\u9F4E" + + "\u9F65\u9F64\u9F92\u4EB9\u56C6\u56C5\u56CB\u5971" + + "\u5B4B\u5B4C\u5DD5\u5DD1\u5EF2\u6521\u6520\u6526" + + "\u6522\u6B0B\u6B08\u6B09\u6C0D\u7055\u7056\u7057" + + "\u7052\u721E\u721F\u72A9\u737F\u74D8\u74D5\u74D9" + + "\u74D7\u766D\u76AD\u7935\u79B4\u7A70\u7A71\u7C57" + + "\u7C5C\u7C59\u7C5B\u7C5A\u7CF4\u7CF1\u7E91\u7F4F" + + "\u7F87\u81DE\u826B\u8634\u8635\u8633\u862C\u8632" + + "\u8636\u882C\u8828\u8826\u882A\u8825\u8971\u89BF" + + "\u89BE\u89FB\u8B7E\u8B84\u8B82\u8B86\u8B85\u8B7F" + + "\u8D15\u8E95\u8E94\u8E9A\u8E92\u8E90\u8E96\u8E97" + + "\u8F60\u8F62\u9147\u944C\u9450\u944A\u944B\u944F" + + "\u9447\u9445\u9448\u9449\u9446\u973F\u97E3\u986A" + + "\u9869\u98CB\u9954\u995B\u9A4E\u9A53\u9A54\u9A4C" + + "\u9A4F\u9A48\u9A4A\u9A49\u9A52\u9A50\u9AD0\u9B19" + + "\u9B2B\u9B3B\u9B56\u9B55\u9C46\u9C48\u9C3F\u9C44" + + "\u9C39\u9C33\u9C41\u9C3C\u9C37\u9C34\u9C32\u9C3D" + + "\u9C36\u9DDB\u9DD2\u9DDE\u9DDA\u9DCB\u9DD0\u9DDC" + + "\u9DD1\u9DDF\u9DE9\u9DD9\u9DD8\u9DD6\u9DF5\u9DD5" + + "\u9DDD\u9EB6\u9EF0\u9F35\u9F33\u9F32\u9F42\u9F6B" + + "\u9F95\u9FA2\u513D\u5299\u58E8\u58E7\u5972\u5B4D" + + "\u5DD8\u882F\u5F4F\u6201\u6203\u6204\u6529\u6525" + + "\u6596\u66EB\u6B11\u6B12\u6B0F\u6BCA\u705B\u705A" + + "\u7222\u7382\u7381\u7383\u7670\u77D4\u7C67\u7C66" + + "\u7E95\u826C\u863A\u8640\u8639\u863C\u8631\u863B" + + "\u863E\u8830\u8832\u882E\u8833\u8976\u8974\u8973" + + "\u89FE\u8B8C\u8B8E\u8B8B\u8B88\u8C45\u8D19\u8E98" + + "\u8F64\u8F63\u91BC\u9462\u9455\u945D\u9457\u945E" + + "\u97C4\u97C5\u9800\u9A56\u9A59\u9B1E\u9B1F\u9B20" + + "\u9C52\u9C58\u9C50\u9C4A\u9C4D\u9C4B\u9C55\u9C59" + + "\u9C4C\u9C4E\u9DFB\u9DF7\u9DEF\u9DE3\u9DEB\u9DF8" + + "\u9DE4\u9DF6\u9DE1\u9DEE\u9DE6\u9DF2\u9DF0\u9DE2" + + "\u9DEC\u9DF4\u9DF3\u9DE8\u9DED\u9EC2\u9ED0\u9EF2" + + "\u9EF3\u9F06\u9F1C\u9F38\u9F37\u9F36\u9F43\u9F4F" + + "\u9F71\u9F70\u9F6E\u9F6F\u56D3\u56CD\u5B4E\u5C6D" + + "\u652D\u66ED\u66EE\u6B13\u705F\u7061\u705D\u7060" + + "\u7223\u74DB\u74E5\u77D5\u7938\u79B7\u79B6\u7C6A" + + "\u7E97\u7F89\u826D\u8643\u8838\u8837\u8835\u884B" + + "\u8B94\u8B95\u8E9E\u8E9F\u8EA0\u8E9D\u91BE\u91BD" + + "\u91C2\u946B\u9468\u9469\u96E5\u9746\u9743\u9747" + + "\u97C7\u97E5\u9A5E\u9AD5\u9B59\u9C63\u9C67\u9C66" + + "\u9C62\u9C5E\u9C60\u9E02\u9DFE\u9E07\u9E03\u9E06" + + "\u9E05\u9E00\u9E01\u9E09\u9DFF\u9DFD\u9E04\u9EA0" + + "\u9F1E\u9F46\u9F74\u9F75\u9F76\u56D4\u652E\u65B8" + + "\u6B18\u6B19\u6B17\u6B1A\u7062\u7226\u72AA\u77D8" + + "\u77D9\u7939\u7C69\u7C6B\u7CF6\u7E9A\u7E98\u7E9B" + + "\u7E99\u81E0\u81E1\u8646\u8647\u8648\u8979\u897A" + + "\u897C\u897B\u89FF\u8B98\u8B99\u8EA5\u8EA4\u8EA3" + + "\u946E\u946D\u946F\u9471\u9473\u9749\u9872\u995F" + + "\u9C68\u9C6E\u9C6D\u9E0B\u9E0D\u9E10\u9E0F\u9E12" + + "\u9E11\u9EA1\u9EF5\u9F09\u9F47\u9F78\u9F7B\u9F7A" + + "\u9F79\u571E\u7066\u7C6F\u883C\u8DB2\u8EA6\u91C3" + + "\u9474\u9478\u9476\u9475\u9A60\u9B2E\u9C74\u9C73" + + "\u9C71\u9C75\u9E14\u9E13\u9EF6\u9F0A\u9FA4\u7068" + + "\u7065\u7CF7\u866A\u883E\u883D\u883F\u8B9E\u8C9C" + + "\u8EA9\u8EC9\u974B\u9873\u9874\u98CC\u9961\u99AB" + + "\u9A64\u9A66\u9A67\u9B24\u9E15\u9E17\u9F48\u6207" + + "\u6B1E\u7227\u864C\u8EA8\u9482\u9480\u9481\u9A69" + + "\u9A68\u9E19\u864B\u8B9F\u9483\u9C79\u9EB7\u7675" + + "\u9A6B\u9C7A\u9E1D\u7069\u706A\u7229\u9EA4\u9F7E" + + "\u9F49\u9F98\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD" + ; + mappingTableG2ac = + "\uE000\uE001\uE002\uE003\uE004\uE005\uE006\uE007" + + "\uE008\uE009\uE00A\uE00B\uE00C\uE00D\uE00E\uE00F" + + "\uE010\uE011\uE012\uE013\uE014\uE015\uE016\uE017" + + "\uE018\uE019\uE01A\uE01B\uE01C\uE01D\uE01E\uE01F" + + "\uE020\uE021\uE022\uE023\uE024\uE025\uE026\uE027" + + "\uE028\uE029\uE02A\uE02B\uE02C\uE02D\uE02E\uE02F" + + "\uE030\uE031\uE032\uE033\uE034\uE035\uE036\uE037" + + "\uE038\uE039\uE03A\uE03B\uE03C\uE03D\uE03E\uE03F" + + "\uE040\uE041\uE042\uE043\uE044\uE045\uE046\uE047" + + "\uE048\uE049\uE04A\uE04B\uE04C\uE04D\uE04E\uE04F" + + "\uE050\uE051\uE052\uE053\uE054\uE055\uE056\uE057" + + "\uE058\uE059\uE05A\uE05B\uE05C\uE05D\uE05E\uE05F" + + "\uE060\uE061\uE062\uE063\uE064\uE065\uE066\uE067" + + "\uE068\uE069\uE06A\uE06B\uE06C\uE06D\uE06E\uE06F" + + "\uE070\uE071\uE072\uE073\uE074\uE075\uE076\uE077" + + "\uE078\uE079\uE07A\uE07B\uE07C\uE07D\uE07E\uE07F" + + "\uE080\uE081\uE082\uE083\uE084\uE085\uE086\uE087" + + "\uE088\uE089\uE08A\uE08B\uE08C\uE08D\uE08E\uE08F" + + "\uE090\uE091\uE092\uE093\uE094\uE095\uE096\uE097" + + "\uE098\uE099\uE09A\uE09B\uE09C\uE09D\uE09E\uE09F" + + "\uE0A0\uE0A1\uE0A2\uE0A3\uE0A4\uE0A5\uE0A6\uE0A7" + + "\uE0A8\uE0A9\uE0AA\uE0AB\uE0AC\uE0AD\uE0AE\uE0AF" + + "\uE0B0\uE0B1\uE0B2\uE0B3\uE0B4\uE0B5\uE0B6\uE0B7" + + "\uE0B8\uE0B9\uE0BA\uE0BB\uE0BC\uE0BD\uE0BE\uE0BF" + + "\uE0C0\uE0C1\uE0C2\uE0C3\uE0C4\uE0C5\uE0C6\uE0C7" + + "\uE0C8\uE0C9\uE0CA\uE0CB\uE0CC\uE0CD\uE0CE\uE0CF" + + "\uE0D0\uE0D1\uE0D2\uE0D3\uE0D4\uE0D5\uE0D6\uE0D7" + + "\uE0D8\uE0D9\uE0DA\uE0DB\uE0DC\uE0DD\uE0DE\uE0DF" + + "\uE0E0\uE0E1\uE0E2\uE0E3\uE0E4\uE0E5\uE0E6\uE0E7" + + "\uE0E8\uE0E9\uE0EA\uE0EB\uE0EC\uE0ED\uE0EE\uE0EF" + + "\uE0F0\uE0F1\uE0F2\uE0F3\uE0F4\uE0F5\uE0F6\uE0F7" + + "\uE0F8\uE0F9\uE0FA\uE0FB\uE0FC\uE0FD\uE0FE\uE0FF" + + "\uE100\uE101\uE102\uE103\uE104\uE105\uE106\uE107" + + "\uE108\uE109\uE10A\uE10B\uE10C\uE10D\uE10E\uE10F" + + "\uE110\uE111\uE112\uE113\uE114\uE115\uE116\uE117" + + "\uE118\uE119\uE11A\uE11B\uE11C\uE11D\uE11E\uE11F" + + "\uE120\uE121\uE122\uE123\uE124\uE125\uE126\uE127" + + "\uE128\uE129\uE12A\uE12B\uE12C\uE12D\uE12E\uE12F" + + "\uE130\uE131\uE132\uE133\uE134\uE135\uE136\uE137" + + "\uE138\uE139\uE13A\uE13B\uE13C\uE13D\uE13E\uE13F" + + "\uE140\uE141\uE142\uE143\uE144\uE145\uE146\uE147" + + "\uE148\uE149\uE14A\uE14B\uE14C\uE14D\uE14E\uE14F" + + "\uE150\uE151\uE152\uE153\uE154\uE155\uE156\uE157" + + "\uE158\uE159\uE15A\uE15B\uE15C\uE15D\uE15E\uE15F" + + "\uE160\uE161\uE162\uE163\uE164\uE165\uE166\uE167" + + "\uE168\uE169\uE16A\uE16B\uE16C\uE16D\uE16E\uE16F" + + "\uE170\uE171\uE172\uE173\uE174\uE175\uE176\uE177" + + "\uE178\uE179\uE17A\uE17B\uE17C\uE17D\uE17E\uE17F" + + "\uE180\uE181\uE182\uE183\uE184\uE185\uE186\uE187" + + "\uE188\uE189\uE18A\uE18B\uE18C\uE18D\uE18E\uE18F" + + "\uE190\uE191\uE192\uE193\uE194\uE195\uE196\uE197" + + "\uE198\uE199\uE19A\uE19B\uE19C\uE19D\uE19E\uE19F" + + "\uE1A0\uE1A1\uE1A2\uE1A3\uE1A4\uE1A5\uE1A6\uE1A7" + + "\uE1A8\uE1A9\uE1AA\uE1AB\uE1AC\uE1AD\uE1AE\uE1AF" + + "\uE1B0\uE1B1\uE1B2\uE1B3\uE1B4\uE1B5\uE1B6\uE1B7" + + "\uE1B8\uE1B9\uE1BA\uE1BB\uE1BC\uE1BD\uE1BE\uE1BF" + + "\uE1C0\uE1C1\uE1C2\uE1C3\uE1C4\uE1C5\uE1C6\uE1C7" + + "\uE1C8\uE1C9\uE1CA\uE1CB\uE1CC\uE1CD\uE1CE\uE1CF" + + "\uE1D0\uE1D1\uE1D2\uE1D3\uE1D4\uE1D5\uE1D6\uE1D7" + + "\uE1D8\uE1D9\uE1DA\uE1DB\uE1DC\uE1DD\uE1DE\uE1DF" + + "\uE1E0\uE1E1\uE1E2\uE1E3\uE1E4\uE1E5\uE1E6\uE1E7" + + "\uE1E8\uE1E9\uE1EA\uE1EB\uE1EC\uE1ED\uE1EE\uE1EF" + + "\uE1F0\uE1F1\uE1F2\uE1F3\uE1F4\uE1F5\uE1F6\uE1F7" + + "\uE1F8\uE1F9\uE1FA\uE1FB\uE1FC\uE1FD\uE1FE\uE1FF" + + "\uE200\uE201\uE202\uE203\uE204\uE205\uE206\uE207" + + "\uE208\uE209\uE20A\uE20B\uE20C\uE20D\uE20E\uE20F" + + "\uE210\uE211\uE212\uE213\uE214\uE215\uE216\uE217" + + "\uE218\uE219\uE21A\uE21B\uE21C\uE21D\uE21E\uE21F" + + "\uE220\uE221\uE222\uE223\uE224\uE225\uE226\uE227" + + "\uE228\uE229\uE22A\uE22B\uE22C\uE22D\uE22E\uE22F" + + "\uE230\uE231\uE232\uE233\uE234\uE235\uE236\uE237" + + "\uE238\uE239\uE23A\uE23B\uE23C\uE23D\uE23E\uE23F" + + "\uE240\uE241\uE242\uE243\uE244\uE245\uE246\uE247" + + "\uE248\uE249\uE24A\uE24B\uE24C\uE24D\uE24E\uE24F" + + "\uE250\uE251\uE252\uE253\uE254\uE255\uE256\uE257" + + "\uE258\uE259\uE25A\uE25B\uE25C\uE25D\uE25E\uE25F" + + "\uE260\uE261\uE262\uE263\uE264\uE265\uE266\uE267" + + "\uE268\uE269\uE26A\uE26B\uE26C\uE26D\uE26E\uE26F" + + "\uE270\uE271\uE272\uE273\uE274\uE275\uE276\uE277" + + "\uE278\uE279\uE27A\uE27B\uE27C\uE27D\uE27E\uE27F" + + "\uE280\uE281\uE282\uE283\uE284\uE285\uE286\uE287" + + "\uE288\uE289\uE28A\uE28B\uE28C\uE28D\uE28E\uE28F" + + "\uE290\uE291\uE292\uE293\uE294\uE295\uE296\uE297" + + "\uE298\uE299\uE29A\uE29B\uE29C\uE29D\uE29E\uE29F" + + "\uE2A0\uE2A1\uE2A2\uE2A3\uE2A4\uE2A5\uE2A6\uE2A7" + + "\uE2A8\uE2A9\uE2AA\uE2AB\uE2AC\uE2AD\uE2AE\uE2AF" + + "\uE2B0\uE2B1\uE2B2\uE2B3\uE2B4\uE2B5\uE2B6\uE2B7" + + "\uE2B8\uE2B9\uE2BA\uE2BB\uE2BC\uE2BD\uE2BE\uE2BF" + + "\uE2C0\uE2C1\uE2C2\uE2C3\uE2C4\uE2C5\uE2C6\uE2C7" + + "\uE2C8\uE2C9\uE2CA\uE2CB\uE2CC\uE2CD\uE2CE\uE2CF" + + "\uE2D0\uE2D1\uE2D2\uE2D3\uE2D4\uE2D5\uE2D6\uE2D7" + + "\uE2D8\uE2D9\uE2DA\uE2DB\uE2DC\uE2DD\uE2DE\uE2DF" + + "\uE2E0\uE2E1\uE2E2\uE2E3\uE2E4\uE2E5\uE2E6\uE2E7" + + "\uE2E8\uE2E9\uE2EA\uE2EB\uE2EC\uE2ED\uE2EE\uE2EF" + + "\uE2F0\uE2F1\uE2F2\uE2F3\uE2F4\uE2F5\uE2F6\uE2F7" + + "\uE2F8\uE2F9\uE2FA\uE2FB\uE2FC\uE2FD\uE2FE\uE2FF" + + "\uE300\uE301\uE302\uE303\uE304\uE305\uE306\uE307" + + "\uE308\uE309\uE30A\uE30B\uE30C\uE30D\uE30E\uE30F" + + "\uE310\uE311\uE312\uE313\uE314\uE315\uE316\uE317" + + "\uE318\uE319\uE31A\uE31B\uE31C\uE31D\uE31E\uE31F" + + "\uE320\uE321\uE322\uE323\uE324\uE325\uE326\uE327" + + "\uE328\uE329\uE32A\uE32B\uE32C\uE32D\uE32E\uE32F" + + "\uE330\uE331\uE332\uE333\uE334\uE335\uE336\uE337" + + "\uE338\uE339\uE33A\uE33B\uE33C\uE33D\uE33E\uE33F" + + "\uE340\uE341\uE342\uE343\uE344\uE345\uE346\uE347" + + "\uE348\uE349\uE34A\uE34B\uE34C\uE34D\uE34E\uE34F" + + "\uE350\uE351\uE352\uE353\uE354\uE355\uE356\uE357" + + "\uE358\uE359\uE35A\uE35B\uE35C\uE35D\uE35E\uE35F" + + "\uE360\uE361\uE362\uE363\uE364\uE365\uE366\uE367" + + "\uE368\uE369\uE36A\uE36B\uE36C\uE36D\uE36E\uE36F" + + "\uE370\uE371\uE372\uE373\uE374\uE375\uE376\uE377" + + "\uE378\uE379\uE37A\uE37B\uE37C\uE37D\uE37E\uE37F" + + "\uE380\uE381\uE382\uE383\uE384\uE385\uE386\uE387" + + "\uE388\uE389\uE38A\uE38B\uE38C\uE38D\uE38E\uE38F" + + "\uE390\uE391\uE392\uE393\uE394\uE395\uE396\uE397" + + "\uE398\uE399\uE39A\uE39B\uE39C\uE39D\uE39E\uE39F" + + "\uE3A0\uE3A1\uE3A2\uE3A3\uE3A4\uE3A5\uE3A6\uE3A7" + + "\uE3A8\uE3A9\uE3AA\uE3AB\uE3AC\uE3AD\uE3AE\uE3AF" + + "\uE3B0\uE3B1\uE3B2\uE3B3\uE3B4\uE3B5\uE3B6\uE3B7" + + "\uE3B8\uE3B9\uE3BA\uE3BB\uE3BC\uE3BD\uE3BE\uE3BF" + + "\uE3C0\uE3C1\uE3C2\uE3C3\uE3C4\uE3C5\uE3C6\uE3C7" + + "\uE3C8\uE3C9\uE3CA\uE3CB\uE3CC\uE3CD\uE3CE\uE3CF" + + "\uE3D0\uE3D1\uE3D2\uE3D3\uE3D4\uE3D5\uE3D6\uE3D7" + + "\uE3D8\uE3D9\uE3DA\uE3DB\uE3DC\uE3DD\uE3DE\uE3DF" + + "\uE3E0\uE3E1\uE3E2\uE3E3\uE3E4\uE3E5\uE3E6\uE3E7" + + "\uE3E8\uE3E9\uE3EA\uE3EB\uE3EC\uE3ED\uE3EE\uE3EF" + + "\uE3F0\uE3F1\uE3F2\uE3F3\uE3F4\uE3F5\uE3F6\uE3F7" + + "\uE3F8\uE3F9\uE3FA\uE3FB\uE3FC\uE3FD\uE3FE\uE3FF" + + "\uE400\uE401\uE402\uE403\uE404\uE405\uE406\uE407" + + "\uE408\uE409\uE40A\uE40B\uE40C\uE40D\uE40E\uE40F" + + "\uE410\uE411\uE412\uE413\uE414\uE415\uE416\uE417" + + "\uE418\uE419\uE41A\uE41B\uE41C\uE41D\uE41E\uE41F" + + "\uE420\uE421\uE422\uE423\uE424\uE425\uE426\uE427" + + "\uE428\uE429\uE42A\uE42B\uE42C\uE42D\uE42E\uE42F" + + "\uE430\uE431\uE432\uE433\uE434\uE435\uE436\uE437" + + "\uE438\uE439\uE43A\uE43B\uE43C\uE43D\uE43E\uE43F" + + "\uE440\uE441\uE442\uE443\uE444\uE445\uE446\uE447" + + "\uE448\uE449\uE44A\uE44B\uE44C\uE44D\uE44E\uE44F" + + "\uE450\uE451\uE452\uE453\uE454\uE455\uE456\uE457" + + "\uE458\uE459\uE45A\uE45B\uE45C\uE45D\uE45E\uE45F" + + "\uE460\uE461\uE462\uE463\uE464\uE465\uE466\uE467" + + "\uE468\uE469\uE46A\uE46B\uE46C\uE46D\uE46E\uE46F" + + "\uE470\uE471\uE472\uE473\uE474\uE475\uE476\uE477" + + "\uE478\uE479\uE47A\uE47B\uE47C\uE47D\uE47E\uE47F" + + "\uE480\uE481\uE482\uE483\uE484\uE485\uE486\uE487" + + "\uE488\uE489\uE48A\uE48B\uE48C\uE48D\uE48E\uE48F" + + "\uE490\uE491\uE492\uE493\uE494\uE495\uE496\uE497" + + "\uE498\uE499\uE49A\uE49B\uE49C\uE49D\uE49E\uE49F" + + "\uE4A0\uE4A1\uE4A2\uE4A3\uE4A4\uE4A5\uE4A6\uE4A7" + + "\uE4A8\uE4A9\uE4AA\uE4AB\uE4AC\uE4AD\uE4AE\uE4AF" + + "\uE4B0\uE4B1\uE4B2\uE4B3\uE4B4\uE4B5\uE4B6\uE4B7" + + "\uE4B8\uE4B9\uE4BA\uE4BB\uE4BC\uE4BD\uE4BE\uE4BF" + + "\uE4C0\uE4C1\uE4C2\uE4C3\uE4C4\uE4C5\uE4C6\uE4C7" + + "\uE4C8\uE4C9\uE4CA\uE4CB\uE4CC\uE4CD\uE4CE\uE4CF" + + "\uE4D0\uE4D1\uE4D2\uE4D3\uE4D4\uE4D5\uE4D6\uE4D7" + + "\uE4D8\uE4D9\uE4DA\uE4DB\uE4DC\uE4DD\uE4DE\uE4DF" + + "\uE4E0\uE4E1\uE4E2\uE4E3\uE4E4\uE4E5\uE4E6\uE4E7" + + "\uE4E8\uE4E9\uE4EA\uE4EB\uE4EC\uE4ED\uE4EE\uE4EF" + + "\uE4F0\uE4F1\uE4F2\uE4F3\uE4F4\uE4F5\uE4F6\uE4F7" + + "\uE4F8\uE4F9\uE4FA\uE4FB\uE4FC\uE4FD\uE4FE\uE4FF" + + "\uE500\uE501\uE502\uE503\uE504\uE505\uE506\uE507" + + "\uE508\uE509\uE50A\uE50B\uE50C\uE50D\uE50E\uE50F" + + "\uE510\uE511\uE512\uE513\uE514\uE515\uE516\uE517" + + "\uE518\uE519\uE51A\uE51B\uE51C\uE51D\uE51E\uE51F" + + "\uE520\uE521\uE522\uE523\uE524\uE525\uE526\uE527" + + "\uE528\uE529\uE52A\uE52B\uE52C\uE52D\uE52E\uE52F" + + "\uE530\uE531\uE532\uE533\uE534\uE535\uE536\uE537" + + "\uE538\uE539\uE53A\uE53B\uE53C\uE53D\uE53E\uE53F" + + "\uE540\uE541\uE542\uE543\uE544\uE545\uE546\uE547" + + "\uE548\uE549\uE54A\uE54B\uE54C\uE54D\uE54E\uE54F" + + "\uE550\uE551\uE552\uE553\uE554\uE555\uE556\uE557" + + "\uE558\uE559\uE55A\uE55B\uE55C\uE55D\uE55E\uE55F" + + "\uE560\uE561\uE562\uE563\uE564\uE565\uE566\uE567" + + "\uE568\uE569\uE56A\uE56B\uE56C\uE56D\uE56E\uE56F" + + "\uE570\uE571\uE572\uE573\uE574\uE575\uE576\uE577" + + "\uE578\uE579\uE57A\uE57B\uE57C\uE57D\uE57E\uE57F" + + "\uE580\uE581\uE582\uE583\uE584\uE585\uE586\uE587" + + "\uE588\uE589\uE58A\uE58B\uE58C\uE58D\uE58E\uE58F" + + "\uE590\uE591\uE592\uE593\uE594\uE595\uE596\uE597" + + "\uE598\uE599\uE59A\uE59B\uE59C\uE59D\uE59E\uE59F" + + "\uE5A0\uE5A1\uE5A2\uE5A3\uE5A4\uE5A5\uE5A6\uE5A7" + + "\uE5A8\uE5A9\uE5AA\uE5AB\uE5AC\uE5AD\uE5AE\uE5AF" + + "\uE5B0\uE5B1\uE5B2\uE5B3\uE5B4\uE5B5\uE5B6\uE5B7" + + "\uE5B8\uE5B9\uE5BA\uE5BB\uE5BC\uE5BD\uE5BE\uE5BF" + + "\uE5C0\uE5C1\uE5C2\uE5C3\uE5C4\uE5C5\uE5C6\uE5C7" + + "\uE5C8\uE5C9\uE5CA\uE5CB\uE5CC\uE5CD\uE5CE\uE5CF" + + "\uE5D0\uE5D1\uE5D2\uE5D3\uE5D4\uE5D5\uE5D6\uE5D7" + + "\uE5D8\uE5D9\uE5DA\uE5DB\uE5DC\uE5DD\uE5DE\uE5DF" + + "\uE5E0\uE5E1\uE5E2\uE5E3\uE5E4\uE5E5\uE5E6\uE5E7" + + "\uE5E8\uE5E9\uE5EA\uE5EB\uE5EC\uE5ED\uE5EE\uE5EF" + + "\uE5F0\uE5F1\uE5F2\uE5F3\uE5F4\uE5F5\uE5F6\uE5F7" + + "\uE5F8\uE5F9\uE5FA\uE5FB\uE5FC\uE5FD\uE5FE\uE5FF" + + "\uE600\uE601\uE602\uE603\uE604\uE605\uE606\uE607" + + "\uE608\uE609\uE60A\uE60B\uE60C\uE60D\uE60E\uE60F" + + "\uE610\uE611\uE612\uE613\uE614\uE615\uE616\uE617" + + "\uE618\uE619\uE61A\uE61B\uE61C\uE61D\uE61E\uE61F" + + "\uE620\uE621\uE622\uE623\uE624\uE625\uE626\uE627" + + "\uE628\uE629\uE62A\uE62B\uE62C\uE62D\uE62E\uE62F" + + "\uE630\uE631\uE632\uE633\uE634\uE635\uE636\uE637" + + "\uE638\uE639\uE63A\uE63B\uE63C\uE63D\uE63E\uE63F" + + "\uE640\uE641\uE642\uE643\uE644\uE645\uE646\uE647" + + "\uE648\uE649\uE64A\uE64B\uE64C\uE64D\uE64E\uE64F" + + "\uE650\uE651\uE652\uE653\uE654\uE655\uE656\uE657" + + "\uE658\uE659\uE65A\uE65B\uE65C\uE65D\uE65E\uE65F" + + "\uE660\uE661\uE662\uE663\uE664\uE665\uE666\uE667" + + "\uE668\uE669\uE66A\uE66B\uE66C\uE66D\uE66E\uE66F" + + "\uE670\uE671\uE672\uE673\uE674\uE675\uE676\uE677" + + "\uE678\uE679\uE67A\uE67B\uE67C\uE67D\uE67E\uE67F" + + "\uE680\uE681\uE682\uE683\uE684\uE685\uE686\uE687" + + "\uE688\uE689\uE68A\uE68B\uE68C\uE68D\uE68E\uE68F" + + "\uE690\uE691\uE692\uE693\uE694\uE695\uE696\uE697" + + "\uE698\uE699\uE69A\uE69B\uE69C\uE69D\uE69E\uE69F" + + "\uE6A0\uE6A1\uE6A2\uE6A3\uE6A4\uE6A5\uE6A6\uE6A7" + + "\uE6A8\uE6A9\uE6AA\uE6AB\uE6AC\uE6AD\uE6AE\uE6AF" + + "\uE6B0\uE6B1\uE6B2\uE6B3\uE6B4\uE6B5\uE6B6\uE6B7" + + "\uE6B8\uE6B9\uE6BA\uE6BB\uE6BC\uE6BD\uE6BE\uE6BF" + + "\uE6C0\uE6C1\uE6C2\uE6C3\uE6C4\uE6C5\uE6C6\uE6C7" + + "\uE6C8\uE6C9\uE6CA\uE6CB\uE6CC\uE6CD\uE6CE\uE6CF" + + "\uE6D0\uE6D1\uE6D2\uE6D3\uE6D4\uE6D5\uE6D6\uE6D7" + + "\uE6D8\uE6D9\uE6DA\uE6DB\uE6DC\uE6DD\uE6DE\uE6DF" + + "\uE6E0\uE6E1\uE6E2\uE6E3\uE6E4\uE6E5\uE6E6\uE6E7" + + "\uE6E8\uE6E9\uE6EA\uE6EB\uE6EC\uE6ED\uE6EE\uE6EF" + + "\uE6F0\uE6F1\uE6F2\uE6F3\uE6F4\uE6F5\uE6F6\uE6F7" + + "\uE6F8\uE6F9\uE6FA\uE6FB\uE6FC\uE6FD\uE6FE\uE6FF" + + "\uE700\uE701\uE702\uE703\uE704\uE705\uE706\uE707" + + "\uE708\uE709\uE70A\uE70B\uE70C\uE70D\uE70E\uE70F" + + "\uE710\uE711\uE712\uE713\uE714\uE715\uE716\uE717" + + "\uE718\uE719\uE71A\uE71B\uE71C\uE71D\uE71E\uE71F" + + "\uE720\uE721\uE722\uE723\uE724\uE725\uE726\uE727" + + "\uE728\uE729\uE72A\uE72B\uE72C\uE72D\uE72E\uE72F" + + "\uE730\uE731\uE732\uE733\uE734\uE735\uE736\uE737" + + "\uE738\uE739\uE73A\uE73B\uE73C\uE73D\uE73E\uE73F" + + "\uE740\uE741\uE742\uE743\uE744\uE745\uE746\uE747" + + "\uE748\uE749\uE74A\uE74B\uE74C\uE74D\uE74E\uE74F" + + "\uE750\uE751\uE752\uE753\uE754\uE755\uE756\uE757" + + "\uE758\uE759\uE75A\uE75B\uE75C\uE75D\uE75E\uE75F" + + "\uE760\uE761\uE762\uE763\uE764\uE765\uE766\uE767" + + "\uE768\uE769\uE76A\uE76B\uE76C\uE76D\uE76E\uE76F" + + "\uE770\uE771\uE772\uE773\uE774\uE775\uE776\uE777" + + "\uE778\uE779\uE77A\uE77B\uE77C\uE77D\uE77E\uE77F" + + "\uE780\uE781\uE782\uE783\uE784\uE785\uE786\uE787" + + "\uE788\uE789\uE78A\uE78B\uE78C\uE78D\uE78E\uE78F" + + "\uE790\uE791\uE792\uE793\uE794\uE795\uE796\uE797" + + "\uE798\uE799\uE79A\uE79B\uE79C\uE79D\uE79E\uE79F" + + "\uE7A0\uE7A1\uE7A2\uE7A3\uE7A4\uE7A5\uE7A6\uE7A7" + + "\uE7A8\uE7A9\uE7AA\uE7AB\uE7AC\uE7AD\uE7AE\uE7AF" + + "\uE7B0\uE7B1\uE7B2\uE7B3\uE7B4\uE7B5\uE7B6\uE7B7" + + "\uE7B8\uE7B9\uE7BA\uE7BB\uE7BC\uE7BD\uE7BE\uE7BF" + + "\uE7C0\uE7C1\uE7C2\uE7C3\uE7C4\uE7C5\uE7C6\uE7C7" + + "\uE7C8\uE7C9\uE7CA\uE7CB\uE7CC\uE7CD\uE7CE\uE7CF" + + "\uE7D0\uE7D1\uE7D2\uE7D3\uE7D4\uE7D5\uE7D6\uE7D7" + + "\uE7D8\uE7D9\uE7DA\uE7DB\uE7DC\uE7DD\uE7DE\uE7DF" + + "\uE7E0\uE7E1\uE7E2\uE7E3\uE7E4\uE7E5\uE7E6\uE7E7" + + "\uE7E8\uE7E9\uE7EA\uE7EB\uE7EC\uE7ED\uE7EE\uE7EF" + + "\uE7F0\uE7F1\uE7F2\uE7F3\uE7F4\uE7F5\uE7F6\uE7F7" + + "\uE7F8\uE7F9\uE7FA\uE7FB\uE7FC\uE7FD\uE7FE\uE7FF" + + "\uE800\uE801\uE802\uE803\uE804\uE805\uE806\uE807" + + "\uE808\uE809\uE80A\uE80B\uE80C\uE80D\uE80E\uE80F" + + "\uE810\uE811\uE812\uE813\uE814\uE815\uE816\uE817" + + "\uE818\uE819\uE81A\uE81B\uE81C\uE81D\uE81E\uE81F" + + "\uE820\uE821\uE822\uE823\uE824\uE825\uE826\uE827" + + "\uE828\uE829\uE82A\uE82B\uE82C\uE82D\uE82E\uE82F" + + "\uE830\uE831\uE832\uE833\uE834\uE835\uE836\uE837" + + "\uE838\uE839\uE83A\uE83B\uE83C\uE83D\uE83E\uE83F" + + "\uE840\uE841\uE842\uE843\uE844\uE845\uE846\uE847" + + "\uE848\uE849\uE84A\uE84B\uE84C\uE84D\uE84E\uE84F" + + "\uE850\uE851\uE852\uE853\uE854\uE855\uE856\uE857" + + "\uE858\uE859\uE85A\uE85B\uE85C\uE85D\uE85E\uE85F" + + "\uE860\uE861\uE862\uE863\uE864\uE865\uE866\uE867" + + "\uE868\uE869\uE86A\uE86B\uE86C\uE86D\uE86E\uE86F" + + "\uE870\uE871\uE872\uE873\uE874\uE875\uE876\uE877" + + "\uE878\uE879\uE87A\uE87B\uE87C\uE87D\uE87E\uE87F" + + "\uE880\uE881\uE882\uE883\uE884\uE885\uE886\uE887" + + "\uE888\uE889\uE88A\uE88B\uE88C\uE88D\uE88E\uE88F" + + "\uE890\uE891\uE892\uE893\uE894\uE895\uE896\uE897" + + "\uE898\uE899\uE89A\uE89B\uE89C\uE89D\uE89E\uE89F" + + "\uE8A0\uE8A1\uE8A2\uE8A3\uE8A4\uE8A5\uE8A6\uE8A7" + + "\uE8A8\uE8A9\uE8AA\uE8AB\uE8AC\uE8AD\uE8AE\uE8AF" + + "\uE8B0\uE8B1\uE8B2\uE8B3\uE8B4\uE8B5\uE8B6\uE8B7" + + "\uE8B8\uE8B9\uE8BA\uE8BB\uE8BC\uE8BD\uE8BE\uE8BF" + + "\uE8C0\uE8C1\uE8C2\uE8C3\uE8C4\uE8C5\uE8C6\uE8C7" + + "\uE8C8\uE8C9\uE8CA\uE8CB\uE8CC\uE8CD\uE8CE\uE8CF" + + "\uE8D0\uE8D1\uE8D2\uE8D3\uE8D4\uE8D5\uE8D6\uE8D7" + + "\uE8D8\uE8D9\uE8DA\uE8DB\uE8DC\uE8DD\uE8DE\uE8DF" + + "\uE8E0\uE8E1\uE8E2\uE8E3\uE8E4\uE8E5\uE8E6\uE8E7" + + "\uE8E8\uE8E9\uE8EA\uE8EB\uE8EC\uE8ED\uE8EE\uE8EF" + + "\uE8F0\uE8F1\uE8F2\uE8F3\uE8F4\uE8F5\uE8F6\uE8F7" + + "\uE8F8\uE8F9\uE8FA\uE8FB\uE8FC\uE8FD\uE8FE\uE8FF" + + "\uE900\uE901\uE902\uE903\uE904\uE905\uE906\uE907" + + "\uE908\uE909\uE90A\uE90B\uE90C\uE90D\uE90E\uE90F" + + "\uE910\uE911\uE912\uE913\uE914\uE915\uE916\uE917" + + "\uE918\uE919\uE91A\uE91B\uE91C\uE91D\uE91E\uE91F" + + "\uE920\uE921\uE922\uE923\uE924\uE925\uE926\uE927" + + "\uE928\uE929\uE92A\uE92B\uE92C\uE92D\uE92E\uE92F" + + "\uE930\uE931\uE932\uE933\uE934\uE935\uE936\uE937" + + "\uE938\uE939\uE93A\uE93B\uE93C\uE93D\uE93E\uE93F" + + "\uE940\uE941\uE942\uE943\uE944\uE945\uE946\uE947" + + "\uE948\uE949\uE94A\uE94B\uE94C\uE94D\uE94E\uE94F" + + "\uE950\uE951\uE952\uE953\uE954\uE955\uE956\uE957" + + "\uE958\uE959\uE95A\uE95B\uE95C\uE95D\uE95E\uE95F" + + "\uE960\uE961\uE962\uE963\uE964\uE965\uE966\uE967" + + "\uE968\uE969\uE96A\uE96B\uE96C\uE96D\uE96E\uE96F" + + "\uE970\uE971\uE972\uE973\uE974\uE975\uE976\uE977" + + "\uE978\uE979\uE97A\uE97B\uE97C\uE97D\uE97E\uE97F" + + "\uE980\uE981\uE982\uE983\uE984\uE985\uE986\uE987" + + "\uE988\uE989\uE98A\uE98B\uE98C\uE98D\uE98E\uE98F" + + "\uE990\uE991\uE992\uE993\uE994\uE995\uE996\uE997" + + "\uE998\uE999\uE99A\uE99B\uE99C\uE99D\uE99E\uE99F" + + "\uE9A0\uE9A1\uE9A2\uE9A3\uE9A4\uE9A5\uE9A6\uE9A7" + + "\uE9A8\uE9A9\uE9AA\uE9AB\uE9AC\uE9AD\uE9AE\uE9AF" + + "\uE9B0\uE9B1\uE9B2\uE9B3\uE9B4\uE9B5\uE9B6\uE9B7" + + "\uE9B8\uE9B9\uE9BA\uE9BB\uE9BC\uE9BD\uE9BE\uE9BF" + + "\uE9C0\uE9C1\uE9C2\uE9C3\uE9C4\uE9C5\uE9C6\uE9C7" + + "\uE9C8\uE9C9\uE9CA\uE9CB\uE9CC\uE9CD\uE9CE\uE9CF" + + "\uE9D0\uE9D1\uE9D2\uE9D3\uE9D4\uE9D5\uE9D6\uE9D7" + + "\uE9D8\uE9D9\uE9DA\uE9DB\uE9DC\uE9DD\uE9DE\uE9DF" + + "\uE9E0\uE9E1\uE9E2\uE9E3\uE9E4\uE9E5\uE9E6\uE9E7" + + "\uE9E8\uE9E9\uE9EA\uE9EB\uE9EC\uE9ED\uE9EE\uE9EF" + + "\uE9F0\uE9F1\uE9F2\uE9F3\uE9F4\uE9F5\uE9F6\uE9F7" + + "\uE9F8\uE9F9\uE9FA\uE9FB\uE9FC\uE9FD\uE9FE\uE9FF" + + "\uEA00\uEA01\uEA02\uEA03\uEA04\uEA05\uEA06\uEA07" + + "\uEA08\uEA09\uEA0A\uEA0B\uEA0C\uEA0D\uEA0E\uEA0F" + + "\uEA10\uEA11\uEA12\uEA13\uEA14\uEA15\uEA16\uEA17" + + "\uEA18\uEA19\uEA1A\uEA1B\uEA1C\uEA1D\uEA1E\uEA1F" + + "\uEA20\uEA21\uEA22\uEA23\uEA24\uEA25\uEA26\uEA27" + + "\uEA28\uEA29\uEA2A\uEA2B\uEA2C\uEA2D\uEA2E\uEA2F" + + "\uEA30\uEA31\uEA32\uEA33\uEA34\uEA35\uEA36\uEA37" + + "\uEA38\uEA39\uEA3A\uEA3B\uEA3C\uEA3D\uEA3E\uEA3F" + + "\uEA40\uEA41\uEA42\uEA43\uEA44\uEA45\uEA46\uEA47" + + "\uEA48\uEA49\uEA4A\uEA4B\uEA4C\uEA4D\uEA4E\uEA4F" + + "\uEA50\uEA51\uEA52\uEA53\uEA54\uEA55\uEA56\uEA57" + + "\uEA58\uEA59\uEA5A\uEA5B\uEA5C\uEA5D\uEA5E\uEA5F" + + "\uEA60\uEA61\uEA62\uEA63\uEA64\uEA65\uEA66\uEA67" + + "\uEA68\uEA69\uEA6A\uEA6B\uEA6C\uEA6D\uEA6E\uEA6F" + + "\uEA70\uEA71\uEA72\uEA73\uEA74\uEA75\uEA76\uEA77" + + "\uEA78\uEA79\uEA7A\uEA7B\uEA7C\uEA7D\uEA7E\uEA7F" + + "\uEA80\uEA81\uEA82\uEA83\uEA84\uEA85\uEA86\uEA87" + + "\uEA88\uEA89\uEA8A\uEA8B\uEA8C\uEA8D\uEA8E\uEA8F" + + "\uEA90\uEA91\uEA92\uEA93\uEA94\uEA95\uEA96\uEA97" + + "\uEA98\uEA99\uEA9A\uEA9B\uEA9C\uEA9D\uEA9E\uEA9F" + + "\uEAA0\uEAA1\uEAA2\uEAA3\uEAA4\uEAA5\uEAA6\uEAA7" + + "\uEAA8\uEAA9\uEAAA\uEAAB\uEAAC\uEAAD\uEAAE\uEAAF" + + "\uEAB0\uEAB1\uEAB2\uEAB3\uEAB4\uEAB5\uEAB6\uEAB7" + + "\uEAB8\uEAB9\uEABA\uEABB\uEABC\uEABD\uEABE\uEABF" + + "\uEAC0\uEAC1\uEAC2\uEAC3\uEAC4\uEAC5\uEAC6\uEAC7" + + "\uEAC8\uEAC9\uEACA\uEACB\uEACC\uEACD\uEACE\uEACF" + + "\uEAD0\uEAD1\uEAD2\uEAD3\uEAD4\uEAD5\uEAD6\uEAD7" + + "\uEAD8\uEAD9\uEADA\uEADB\uEADC\uEADD\uEADE\uEADF" + + "\uEAE0\uEAE1\uEAE2\uEAE3\uEAE4\uEAE5\uEAE6\uEAE7" + + "\uEAE8\uEAE9\uEAEA\uEAEB\uEAEC\uEAED\uEAEE\uEAEF" + + "\uEAF0\uEAF1\uEAF2\uEAF3\uEAF4\uEAF5\uEAF6\uEAF7" + + "\uEAF8\uEAF9\uEAFA\uEAFB\uEAFC\uEAFD\uEAFE\uEAFF" + + "\uEB00\uEB01\uEB02\uEB03\uEB04\uEB05\uEB06\uEB07" + + "\uEB08\uEB09\uEB0A\uEB0B\uEB0C\uEB0D\uEB0E\uEB0F" + + "\uEB10\uEB11\uEB12\uEB13\uEB14\uEB15\uEB16\uEB17" + + "\uEB18\uEB19\uEB1A\uEB1B\uEB1C\uEB1D\uEB1E\uEB1F" + + "\uEB20\uEB21\uEB22\uEB23\uEB24\uEB25\uEB26\uEB27" + + "\uEB28\uEB29\uEB2A\uEB2B\uEB2C\uEB2D\uEB2E\uEB2F" + + "\uEB30\uEB31\uEB32\uEB33\uEB34\uEB35\uEB36\uEB37" + + "\uEB38\uEB39\uEB3A\uEB3B\uEB3C\uEB3D\uEB3E\uEB3F" + + "\uEB40\uEB41\uEB42\uEB43\uEB44\uEB45\uEB46\uEB47" + + "\uEB48\uEB49\uEB4A\uEB4B\uEB4C\uEB4D\uEB4E\uEB4F" + + "\uEB50\uEB51\uEB52\uEB53\uEB54\uEB55\uEB56\uEB57" + + "\uEB58\uEB59\uEB5A\uEB5B\uEB5C\uEB5D\uEB5E\uEB5F" + + "\uEB60\uEB61\uEB62\uEB63\uEB64\uEB65\uEB66\uEB67" + + "\uEB68\uEB69\uEB6A\uEB6B\uEB6C\uEB6D\uEB6E\uEB6F" + + "\uEB70\uEB71\uEB72\uEB73\uEB74\uEB75\uEB76\uEB77" + + "\uEB78\uEB79\uEB7A\uEB7B\uEB7C\uEB7D\uEB7E\uEB7F" + + "\uEB80\uEB81\uEB82\uEB83\uEB84\uEB85\uEB86\uEB87" + + "\uEB88\uEB89\uEB8A\uEB8B\uEB8C\uEB8D\uEB8E\uEB8F" + + "\uEB90\uEB91\uEB92\uEB93\uEB94\uEB95\uEB96\uEB97" + + "\uEB98\uEB99\uEB9A\uEB9B\uEB9C\uEB9D\uEB9E\uEB9F" + + "\uEBA0\uEBA1\uEBA2\uEBA3\uEBA4\uEBA5\uEBA6\uEBA7" + + "\uEBA8\uEBA9\uEBAA\uEBAB\uEBAC\uEBAD\uEBAE\uEBAF" + + "\uEBB0\uEBB1\uEBB2\uEBB3\uEBB4\uEBB5\uEBB6\uEBB7" + + "\uEBB8\uEBB9\uEBBA\uEBBB\uEBBC\uEBBD\uEBBE\uEBBF" + + "\uEBC0\uEBC1\uEBC2\uEBC3\uEBC4\uEBC5\uEBC6\uEBC7" + + "\uEBC8\uEBC9\uEBCA\uEBCB\uEBCC\uEBCD\uEBCE\uEBCF" + + "\uEBD0\uEBD1\uEBD2\uEBD3\uEBD4\uEBD5\uEBD6\uEBD7" + + "\uEBD8\uEBD9\uEBDA\uEBDB\uEBDC\uEBDD\uEBDE\uEBDF" + + "\uEBE0\uEBE1\uEBE2\uEBE3\uEBE4\uEBE5\uEBE6\uEBE7" + + "\uEBE8\uEBE9\uEBEA\uEBEB\uEBEC\uEBED\uEBEE\uEBEF" + + "\uEBF0\uEBF1\uEBF2\uEBF3\uEBF4\uEBF5\uEBF6\uEBF7" + + "\uEBF8\uEBF9\uEBFA\uEBFB\uEBFC\uEBFD\uEBFE\uEBFF" + + "\uEC00\uEC01\uEC02\uEC03\uEC04\uEC05\uEC06\uEC07" + + "\uEC08\uEC09\uEC0A\uEC0B\uEC0C\uEC0D\uEC0E\uEC0F" + + "\uEC10\uEC11\uEC12\uEC13\uEC14\uEC15\uEC16\uEC17" + + "\uEC18\uEC19\uEC1A\uEC1B\uEC1C\uEC1D\uEC1E\uEC1F" + + "\uEC20\uEC21\uEC22\uEC23\uEC24\uEC25\uEC26\uEC27" + + "\uEC28\uEC29\uEC2A\uEC2B\uEC2C\uEC2D\uEC2E\uEC2F" + + "\uEC30\uEC31\uEC32\uEC33\uEC34\uEC35\uEC36\uEC37" + + "\uEC38\uEC39\uEC3A\uEC3B\uEC3C\uEC3D\uEC3E\uEC3F" + + "\uEC40\uEC41\uEC42\uEC43\uEC44\uEC45\uEC46\uEC47" + + "\uEC48\uEC49\uEC4A\uEC4B\uEC4C\uEC4D\uEC4E\uEC4F" + + "\uEC50\uEC51\uEC52\uEC53\uEC54\uEC55\uEC56\uEC57" + + "\uEC58\uEC59\uEC5A\uEC5B\uEC5C\uEC5D\uEC5E\uEC5F" + + "\uEC60\uEC61\uEC62\uEC63\uEC64\uEC65\uEC66\uEC67" + + "\uEC68\uEC69\uEC6A\uEC6B\uEC6C\uEC6D\uEC6E\uEC6F" + + "\uEC70\uEC71\uEC72\uEC73\uEC74\uEC75\uEC76\uEC77" + + "\uEC78\uEC79\uEC7A\uEC7B\uEC7C\uEC7D\uEC7E\uEC7F" + + "\uEC80\uEC81\uEC82\uEC83\uEC84\uEC85\uEC86\uEC87" + + "\uEC88\uEC89\uEC8A\uEC8B\uEC8C\uEC8D\uEC8E\uEC8F" + + "\uEC90\uEC91\uEC92\uEC93\uEC94\uEC95\uEC96\uEC97" + + "\uEC98\uEC99\uEC9A\uEC9B\uEC9C\uEC9D\uEC9E\uEC9F" + + "\uECA0\uECA1\uECA2\uECA3\uECA4\uECA5\uECA6\uECA7" + + "\uECA8\uECA9\uECAA\uECAB\uECAC\uECAD\uECAE\uECAF" + + "\uECB0\uECB1\uECB2\uECB3\uECB4\uECB5\uECB6\uECB7" + + "\uECB8\uECB9\uECBA\uECBB\uECBC\uECBD\uECBE\uECBF" + + "\uECC0\uECC1\uECC2\uECC3\uECC4\uECC5\uECC6\uECC7" + + "\uECC8\uECC9\uECCA\uECCB\uECCC\uECCD\uECCE\uECCF" + + "\uECD0\uECD1\uECD2\uECD3\uECD4\uECD5\uECD6\uECD7" + + "\uECD8\uECD9\uECDA\uECDB\uECDC\uECDD\uECDE\uECDF" + + "\uECE0\uECE1\uECE2\uECE3\uECE4\uECE5\uECE6\uECE7" + + "\uECE8\uECE9\uECEA\uECEB\uECEC\uECED\uECEE\uECEF" + + "\uECF0\uECF1\uECF2\uECF3\uECF4\uECF5\uECF6\uECF7" + + "\uECF8\uECF9\uECFA\uECFB\uECFC\uECFD\uECFE\uECFF" + + "\uED00\uED01\uED02\uED03\uED04\uED05\uED06\uED07" + + "\uED08\uED09\uED0A\uED0B\uED0C\uED0D\uED0E\uED0F" + + "\uED10\uED11\uED12\uED13\uED14\uED15\uED16\uED17" + + "\uED18\uED19\uED1A\uED1B\uED1C\uED1D\uED1E\uED1F" + + "\uED20\uED21\uED22\uED23\uED24\uED25\uED26\uED27" + + "\uED28\uED29\uED2A\uED2B\uED2C\uED2D\uED2E\uED2F" + + "\uED30\uED31\uED32\uED33\uED34\uED35\uED36\uED37" + + "\uED38\uED39\uED3A\uED3B\uED3C\uED3D\uED3E\uED3F" + + "\uED40\uED41\uED42\uED43\uED44\uED45\uED46\uED47" + + "\uED48\uED49\uED4A\uED4B\uED4C\uED4D\uED4E\uED4F" + + "\uED50\uED51\uED52\uED53\uED54\uED55\uED56\uED57" + + "\uED58\uED59\uED5A\uED5B\uED5C\uED5D\uED5E\uED5F" + + "\uED60\uED61\uED62\uED63\uED64\uED65\uED66\uED67" + + "\uED68\uED69\uED6A\uED6B\uED6C\uED6D\uED6E\uED6F" + + "\uED70\uED71\uED72\uED73\uED74\uED75\uED76\uED77" + + "\uED78\uED79\uED7A\uED7B\uED7C\uED7D\uED7E\uED7F" + + "\uED80\uED81\uED82\uED83\uED84\uED85\uED86\uED87" + + "\uED88\uED89\uED8A\uED8B\uED8C\uED8D\uED8E\uED8F" + + "\uED90\uED91\uED92\uED93\uED94\uED95\uED96\uED97" + + "\uED98\uED99\uED9A\uED9B\uED9C\uED9D\uED9E\uED9F" + + "\uEDA0\uEDA1\uEDA2\uEDA3\uEDA4\uEDA5\uEDA6\uEDA7" + + "\uEDA8\uEDA9\uEDAA\uEDAB\uEDAC\uEDAD\uEDAE\uEDAF" + + "\uEDB0\uEDB1\uEDB2\uEDB3\uEDB4\uEDB5\uEDB6\uEDB7" + + "\uEDB8\uEDB9\uEDBA\uEDBB\uEDBC\uEDBD\uEDBE\uEDBF" + + "\uEDC0\uEDC1\uEDC2\uEDC3\uEDC4\uEDC5\uEDC6\uEDC7" + + "\uEDC8\uEDC9\uEDCA\uEDCB\uEDCC\uEDCD\uEDCE\uEDCF" + + "\uEDD0\uEDD1\uEDD2\uEDD3\uEDD4\uEDD5\uEDD6\uEDD7" + + "\uEDD8\uEDD9\uEDDA\uEDDB\uEDDC\uEDDD\uEDDE\uEDDF" + + "\uEDE0\uEDE1\uEDE2\uEDE3\uEDE4\uEDE5\uEDE6\uEDE7" + + "\uEDE8\uEDE9\uEDEA\uEDEB\uEDEC\uEDED\uEDEE\uEDEF" + + "\uEDF0\uEDF1\uEDF2\uEDF3\uEDF4\uEDF5\uEDF6\uEDF7" + + "\uEDF8\uEDF9\uEDFA\uEDFB\uEDFC\uEDFD\uEDFE\uEDFF" + + "\uEE00\uEE01\uEE02\uEE03\uEE04\uEE05\uEE06\uEE07" + + "\uEE08\uEE09\uEE0A\uEE0B\uEE0C\uEE0D\uEE0E\uEE0F" + + "\uEE10\uEE11\uEE12\uEE13\uEE14\uEE15\uEE16\uEE17" + + "\uEE18\uEE19\uEE1A\uEE1B\uEE1C\uEE1D\uEE1E\uEE1F" + + "\uEE20\uEE21\uEE22\uEE23\uEE24\uEE25\uEE26\uEE27" + + "\uEE28\uEE29\uEE2A\uEE2B\uEE2C\uEE2D\uEE2E\uEE2F" + + "\uEE30\uEE31\uEE32\uEE33\uEE34\uEE35\uEE36\uEE37" + + "\uEE38\uEE39\uEE3A\uEE3B\uEE3C\uEE3D\uEE3E\uEE3F" + + "\uEE40\uEE41\uEE42\uEE43\uEE44\uEE45\uEE46\uEE47" + + "\uEE48\uEE49\uEE4A\uEE4B\uEE4C\uEE4D\uEE4E\uEE4F" + + "\uEE50\uEE51\uEE52\uEE53\uEE54\uEE55\uEE56\uEE57" + + "\uEE58\uEE59\uEE5A\uEE5B\uEE5C\uEE5D\uEE5E\uEE5F" + + "\uEE60\uEE61\uEE62\uEE63\uEE64\uEE65\uEE66\uEE67" + + "\uEE68\uEE69\uEE6A\uEE6B\uEE6C\uEE6D\uEE6E\uEE6F" + + "\uEE70\uEE71\uEE72\uEE73\uEE74\uEE75\uEE76\uEE77" + + "\uEE78\uEE79\uEE7A\uEE7B\uEE7C\uEE7D\uEE7E\uEE7F" + + "\uEE80\uEE81\uEE82\uEE83\uEE84\uEE85\uEE86\uEE87" + + "\uEE88\uEE89\uEE8A\uEE8B\uEE8C\uEE8D\uEE8E\uEE8F" + + "\uEE90\uEE91\uEE92\uEE93\uEE94\uEE95\uEE96\uEE97" + + "\uEE98\uEE99\uEE9A\uEE9B\uEE9C\uEE9D\uEE9E\uEE9F" + + "\uEEA0\uEEA1\uEEA2\uEEA3\uEEA4\uEEA5\uEEA6\uEEA7" + + "\uEEA8\uEEA9\uEEAA\uEEAB\uEEAC\uEEAD\uEEAE\uEEAF" + + "\uEEB0\uEEB1\uEEB2\uEEB3\uEEB4\uEEB5\uEEB6\uEEB7" + + "\uEEB8\uEEB9\uEEBA\uEEBB\uEEBC\uEEBD\uEEBE\uEEBF" + + "\uEEC0\uEEC1\uEEC2\uEEC3\uEEC4\uEEC5\uEEC6\uEEC7" + + "\uEEC8\uEEC9\uEECA\uEECB\uEECC\uEECD\uEECE\uEECF" + + "\uEED0\uEED1\uEED2\uEED3\uEED4\uEED5\uEED6\uEED7" + + "\uEED8\uEED9\uEEDA\uEEDB\uEEDC\uEEDD\uEEDE\uEEDF" + + "\uEEE0\uEEE1\uEEE2\uEEE3\uEEE4\uEEE5\uEEE6\uEEE7" + + "\uEEE8\uEEE9\uEEEA\uEEEB\uEEEC\uEEED\uEEEE\uEEEF" + + "\uEEF0\uEEF1\uEEF2\uEEF3\uEEF4\uEEF5\uEEF6\uEEF7" + + "\uEEF8\uEEF9\uEEFA\uEEFB\uEEFC\uEEFD\uEEFE\uEEFF" + + "\uEF00\uEF01\uEF02\uEF03\uEF04\uEF05\uEF06\uEF07" + + "\uEF08\uEF09\uEF0A\uEF0B\uEF0C\uEF0D\uEF0E\uEF0F" + + "\uEF10\uEF11\uEF12\uEF13\uEF14\uEF15\uEF16\uEF17" + + "\uEF18\uEF19\uEF1A\uEF1B\uEF1C\uEF1D\uEF1E\uEF1F" + + "\uEF20\uEF21\uEF22\uEF23\uEF24\uEF25\uEF26\uEF27" + + "\uEF28\uEF29\uEF2A\uEF2B\uEF2C\uEF2D\uEF2E\uEF2F" + + "\uEF30\uEF31\uEF32\uEF33\uEF34\uEF35\uEF36\uEF37" + + "\uEF38\uEF39\uEF3A\uEF3B\uEF3C\uEF3D\uEF3E\uEF3F" + + "\uEF40\uEF41\uEF42\uEF43\uEF44\uEF45\uEF46\uEF47" + + "\uEF48\uEF49\uEF4A\uEF4B\uEF4C\uEF4D\uEF4E\uEF4F" + + "\uEF50\uEF51\uEF52\uEF53\uEF54\uEF55\uEF56\uEF57" + + "\uEF58\uEF59\uEF5A\uEF5B\uEF5C\uEF5D\uEF5E\uEF5F" + + "\uEF60\uEF61\uEF62\uEF63\uEF64\uEF65\uEF66\uEF67" + + "\uEF68\uEF69\uEF6A\uEF6B\uEF6C\uEF6D\uEF6E\uEF6F" + + "\uEF70\uEF71\uEF72\uEF73\uEF74\uEF75\uEF76\uEF77" + + "\uEF78\uEF79\uEF7A\uEF7B\uEF7C\uEF7D\uEF7E\uEF7F" + + "\uEF80\uEF81\uEF82\uEF83\uEF84\uEF85\uEF86\uEF87" + + "\uEF88\uEF89\uEF8A\uEF8B\uEF8C\uEF8D\uEF8E\uEF8F" + + "\uEF90\uEF91\uEF92\uEF93\uEF94\uEF95\uEF96\uEF97" + + "\uEF98\uEF99\uEF9A\uEF9B\uEF9C\uEF9D\uEF9E\uEF9F" + + "\uEFA0\uEFA1\uEFA2\uEFA3\uEFA4\uEFA5\uEFA6\uEFA7" + + "\uEFA8\uEFA9\uEFAA\uEFAB\uEFAC\uEFAD\uEFAE\uEFAF" + + "\uEFB0\uEFB1\uEFB2\uEFB3\uEFB4\uEFB5\uEFB6\uEFB7" + + "\uEFB8\uEFB9\uEFBA\uEFBB\uEFBC\uEFBD\uEFBE\uEFBF" + + "\uEFC0\uEFC1\uEFC2\uEFC3\uEFC4\uEFC5\uEFC6\uEFC7" + + "\uEFC8\uEFC9\uEFCA\uEFCB\uEFCC\uEFCD\uEFCE\uEFCF" + + "\uEFD0\uEFD1\uEFD2\uEFD3\uEFD4\uEFD5\uEFD6\uEFD7" + + "\uEFD8\uEFD9\uEFDA\uEFDB\uEFDC\uEFDD\uEFDE\uEFDF" + + "\uEFE0\uEFE1\uEFE2\uEFE3\uEFE4\uEFE5\uEFE6\uEFE7" + + "\uEFE8\uEFE9\uEFEA\uEFEB\uEFEC\uEFED\uEFEE\uEFEF" + + "\uEFF0\uEFF1\uEFF2\uEFF3\uEFF4\uEFF5\uEFF6\uEFF7" + + "\uEFF8\uEFF9\uEFFA\uEFFB\uEFFC\uEFFD\uEFFE\uEFFF" + + "\uF000\uF001\uF002\uF003\uF004\uF005\uF006\uF007" + + "\uF008\uF009\uF00A\uF00B\uF00C\uF00D\uF00E\uF00F" + + "\uF010\uF011\uF012\uF013\uF014\uF015\uF016\uF017" + + "\uF018\uF019\uF01A\uF01B\uF01C\uF01D\uF01E\uF01F" + + "\uF020\uF021\uF022\uF023\uF024\uF025\uF026\uF027" + + "\uF028\uF029\uF02A\uF02B\uF02C\uF02D\uF02E\uF02F" + + "\uF030\uF031\uF032\uF033\uF034\uF035\uF036\uF037" + + "\uF038\uF039\uF03A\uF03B\uF03C\uF03D\uF03E\uF03F" + + "\uF040\uF041\uF042\uF043\uF044\uF045\uF046\uF047" + + "\uF048\uF049\uF04A\uF04B\uF04C\uF04D\uF04E\uF04F" + + "\uF050\uF051\uF052\uF053\uF054\uF055\uF056\uF057" + + "\uF058\uF059\uF05A\uF05B\uF05C\uF05D\uF05E\uF05F" + + "\uF060\uF061\uF062\uF063\uF064\uF065\uF066\uF067" + + "\uF068\uF069\uF06A\uF06B\uF06C\uF06D\uF06E\uF06F" + + "\uF070\uF071\uF072\uF073\uF074\uF075\uF076\uF077" + + "\uF078\uF079\uF07A\uF07B\uF07C\uF07D\uF07E\uF07F" + + "\uF080\uF081\uF082\uF083\uF084\uF085\uF086\uF087" + + "\uF088\uF089\uF08A\uF08B\uF08C\uF08D\uF08E\uF08F" + + "\uF090\uF091\uF092\uF093\uF094\uF095\uF096\uF097" + + "\uF098\uF099\uF09A\uF09B\uF09C\uF09D\uF09E\uF09F" + + "\uF0A0\uF0A1\uF0A2\uF0A3\uF0A4\uF0A5\uF0A6\uF0A7" + + "\uF0A8\uF0A9\uF0AA\uF0AB\uF0AC\uF0AD\uF0AE\uF0AF" + + "\uF0B0\uF0B1\uF0B2\uF0B3\uF0B4\uF0B5\uF0B6\uF0B7" + + "\uF0B8\uF0B9\uF0BA\uF0BB\uF0BC\uF0BD\uF0BE\uF0BF" + + "\uF0C0\uF0C1\uF0C2\uF0C3\uF0C4\uF0C5\uF0C6\uF0C7" + + "\uF0C8\uF0C9\uF0CA\uF0CB\uF0CC\uF0CD\uF0CE\uF0CF" + + "\uF0D0\uF0D1\uF0D2\uF0D3\uF0D4\uF0D5\uF0D6\uF0D7" + + "\uF0D8\uF0D9\uF0DA\uF0DB\uF0DC\uF0DD\uF0DE\uF0DF" + + "\uF0E0\uF0E1\uF0E2\uF0E3\uF0E4\uF0E5\uF0E6\uF0E7" + + "\uF0E8\uF0E9\uF0EA\uF0EB\uF0EC\uF0ED\uF0EE\uF0EF" + + "\uF0F0\uF0F1\uF0F2\uF0F3\uF0F4\uF0F5\uF0F6\uF0F7" + + "\uF0F8\uF0F9\uF0FA\uF0FB\uF0FC\uF0FD\uF0FE\uF0FF" + + "\uF100\uF101\uF102\uF103\uF104\uF105\uF106\uF107" + + "\uF108\uF109\uF10A\uF10B\uF10C\uF10D\uF10E\uF10F" + + "\uF110\uF111\uF112\uF113\uF114\uF115\uF116\uF117" + + "\uF118\uF119\uF11A\uF11B\uF11C\uF11D\uF11E\uF11F" + + "\uF120\uF121\uF122\uF123\uF124\uF125\uF126\uF127" + + "\uF128\uF129\uF12A\uF12B\uF12C\uF12D\uF12E\uF12F" + + "\uF130\uF131\uF132\uF133\uF134\uF135\uF136\uF137" + + "\uF138\uF139\uF13A\uF13B\uF13C\uF13D\uF13E\uF13F" + + "\uF140\uF141\uF142\uF143\uF144\uF145\uF146\uF147" + + "\uF148\uF149\uF14A\uF14B\uF14C\uF14D\uF14E\uF14F" + + "\uF150\uF151\uF152\uF153\uF154\uF155\uF156\uF157" + + "\uF158\uF159\uF15A\uF15B\uF15C\uF15D\uF15E\uF15F" + + "\uF160\uF161\uF162\uF163\uF164\uF165\uF166\uF167" + + "\uF168\uF169\uF16A\uF16B\uF16C\uF16D\uF16E\uF16F" + + "\uF170\uF171\uF172\uF173\uF174\uF175\uF176\uF177" + + "\uF178\uF179\uF17A\uF17B\uF17C\uF17D\uF17E\uF17F" + + "\uF180\uF181\uF182\uF183\uF184\uF185\uF186\uF187" + + "\uF188\uF189\uF18A\uF18B\uF18C\uF18D\uF18E\uF18F" + + "\uF190\uF191\uF192\uF193\uF194\uF195\uF196\uF197" + + "\uF198\uF199\uF19A\uF19B\uF19C\uF19D\uF19E\uF19F" + + "\uF1A0\uF1A1\uF1A2\uF1A3\uF1A4\uF1A5\uF1A6\uF1A7" + + "\uF1A8\uF1A9\uF1AA\uF1AB\uF1AC\uF1AD\uF1AE\uF1AF" + + "\uF1B0\uF1B1\uF1B2\uF1B3\uF1B4\uF1B5\uF1B6\uF1B7" + + "\uF1B8\uF1B9\uF1BA\uF1BB\uF1BC\uF1BD\uF1BE\uF1BF" + + "\uF1C0\uF1C1\uF1C2\uF1C3\uF1C4\uF1C5\uF1C6\uF1C7" + + "\uF1C8\uF1C9\uF1CA\uF1CB\uF1CC\uF1CD\uF1CE\uF1CF" + + "\uF1D0\uF1D1\uF1D2\uF1D3\uF1D4\uF1D5\uF1D6\uF1D7" + + "\uF1D8\uF1D9\uF1DA\uF1DB\uF1DC\uF1DD\uF1DE\uF1DF" + + "\uF1E0\uF1E1\uF1E2\uF1E3\uF1E4\uF1E5\uF1E6\uF1E7" + + "\uF1E8\uF1E9\uF1EA\uF1EB\uF1EC\uF1ED\uF1EE\uF1EF" + + "\uF1F0\uF1F1\uF1F2\uF1F3\uF1F4\uF1F5\uF1F6\uF1F7" + + "\uF1F8\uF1F9\uF1FA\uF1FB\uF1FC\uF1FD\uF1FE\uF1FF" + + "\uF200\uF201\uF202\uF203\uF204\uF205\uF206\uF207" + + "\uF208\uF209\uF20A\uF20B\uF20C\uF20D\uF20E\uF20F" + + "\uF210\uF211\uF212\uF213\uF214\uF215\uF216\uF217" + + "\uF218\uF219\uF21A\uF21B\uF21C\uF21D\uF21E\uF21F" + + "\uF220\uF221\uF222\uF223\uF224\uF225\uF226\uF227" + + "\uF228\uF229\uF22A\uF22B\uF22C\uF22D\uF22E\uF22F" + + "\uF230\uF231\uF232\uF233\uF234\uF235\uF236\uF237" + + "\uF238\uF239\uF23A\uF23B\uF23C\uF23D\uF23E\uF23F" + + "\uF240\uF241\uF242\uF243\uF244\uF245\uF246\uF247" + + "\uF248\uF249\uF24A\uF24B\uF24C\uF24D\uF24E\uF24F" + + "\uF250\uF251\uF252\uF253\uF254\uF255\uF256\uF257" + + "\uF258\uF259\uF25A\uF25B\uF25C\uF25D\uF25E\uF25F" + + "\uF260\uF261\uF262\uF263\uF264\uF265\uF266\uF267" + + "\uF268\uF269\uF26A\uF26B\uF26C\uF26D\uF26E\uF26F" + + "\uF270\uF271\uF272\uF273\uF274\uF275\uF276\uF277" + + "\uF278\uF279\uF27A\uF27B\uF27C\uF27D\uF27E\uF27F" + + "\uF280\uF281\uF282\uF283\uF284\uF285\uF286\uF287" + + "\uF288\uF289\uF28A\uF28B\uF28C\uF28D\uF28E\uF28F" + + "\uF290\uF291\uF292\uF293\uF294\uF295\uF296\uF297" + + "\uF298\uF299\uF29A\uF29B\uF29C\uF29D\uF29E\uF29F" + + "\uF2A0\uF2A1\uF2A2\uF2A3\uF2A4\uF2A5\uF2A6\uF2A7" + + "\uF2A8\uF2A9\uF2AA\uF2AB\uF2AC\uF2AD\uF2AE\uF2AF" + + "\uF2B0\uF2B1\uF2B2\uF2B3\uF2B4\uF2B5\uF2B6\uF2B7" + + "\uF2B8\uF2B9\uF2BA\uF2BB\uF2BC\uF2BD\uF2BE\uF2BF" + + "\uF2C0\uF2C1\uF2C2\uF2C3\uF2C4\uF2C5\uF2C6\uF2C7" + + "\uF2C8\uF2C9\uF2CA\uF2CB\uF2CC\uF2CD\uF2CE\uF2CF" + + "\uF2D0\uF2D1\uF2D2\uF2D3\uF2D4\uF2D5\uF2D6\uF2D7" + + "\uF2D8\uF2D9\uF2DA\uF2DB\uF2DC\uF2DD\uF2DE\uF2DF" + + "\uF2E0\uF2E1\uF2E2\uF2E3\uF2E4\uF2E5\uF2E6\uF2E7" + + "\uF2E8\uF2E9\uF2EA\uF2EB\uF2EC\uF2ED\uF2EE\uF2EF" + + "\uF2F0\uF2F1\uF2F2\uF2F3\uF2F4\uF2F5\uF2F6\uF2F7" + + "\uF2F8\uF2F9\uF2FA\uF2FB\uF2FC\uF2FD\uF2FE\uF2FF" + + "\uF300\uF301\uF302\uF303\uF304\uF305\uF306\uF307" + + "\uF308\uF309\uF30A\uF30B\uF30C\uF30D\uF30E\uF30F" + + "\uF310\uF311\uF312\uF313\uF314\uF315\uF316\uF317" + + "\uF318\uF319\uF31A\uF31B\uF31C\uF31D\uF31E\uF31F" + + "\uF320\uF321\uF322\uF323\uF324\uF325\uF326\uF327" + + "\uF328\uF329\uF32A\uF32B\uF32C\uF32D\uF32E\uF32F" + + "\uF330\uF331\uF332\uF333\uF334\uF335\uF336\uF337" + + "\uF338\uF339\uF33A\uF33B\uF33C\uF33D\uF33E\uF33F" + + "\uF340\uF341\uF342\uF343\uF344\uF345\uF346\uF347" + + "\uF348\uF349\uF34A\uF34B\uF34C\uF34D\uF34E\uF34F" + + "\uF350\uF351\uF352\uF353\uF354\uF355\uF356\uF357" + + "\uF358\uF359\uF35A\uF35B\uF35C\uF35D\uF35E\uF35F" + + "\uF360\uF361\uF362\uF363\uF364\uF365\uF366\uF367" + + "\uF368\uF369\uF36A\uF36B\uF36C\uF36D\uF36E\uF36F" + + "\uF370\uF371\uF372\uF373\uF374\uF375\uF376\uF377" + + "\uF378\uF379\uF37A\uF37B\uF37C\uF37D\uF37E\uF37F" + + "\uF380\uF381\uF382\uF383\uF384\uF385\uF386\uF387" + + "\uF388\uF389\uF38A\uF38B\uF38C\uF38D\uF38E\uF38F" + + "\uF390\uF391\uF392\uF393\uF394\uF395\uF396\uF397" + + "\uF398\uF399\uF39A\uF39B\uF39C\uF39D\uF39E\uF39F" + + "\uF3A0\uF3A1\uF3A2\uF3A3\uF3A4\uF3A5\uF3A6\uF3A7" + + "\uF3A8\uF3A9\uF3AA\uF3AB\uF3AC\uF3AD\uF3AE\uF3AF" + + "\uF3B0\uF3B1\uF3B2\uF3B3\uF3B4\uF3B5\uF3B6\uF3B7" + + "\uF3B8\uF3B9\uF3BA\uF3BB\uF3BC\uF3BD\uF3BE\uF3BF" + + "\uF3C0\uF3C1\uF3C2\uF3C3\uF3C4\uF3C5\uF3C6\uF3C7" + + "\uF3C8\uF3C9\uF3CA\uF3CB\uF3CC\uF3CD\uF3CE\uF3CF" + + "\uF3D0\uF3D1\uF3D2\uF3D3\uF3D4\uF3D5\uF3D6\uF3D7" + + "\uF3D8\uF3D9\uF3DA\uF3DB\uF3DC\uF3DD\uF3DE\uF3DF" + + "\uF3E0\uF3E1\uF3E2\uF3E3\uF3E4\uF3E5\uF3E6\uF3E7" + + "\uF3E8\uF3E9\uF3EA\uF3EB\uF3EC\uF3ED\uF3EE\uF3EF" + + "\uF3F0\uF3F1\uF3F2\uF3F3\uF3F4\uF3F5\uF3F6\uF3F7" + + "\uF3F8\uF3F9\uF3FA\uF3FB\uF3FC\uF3FD\uF3FE\uF3FF" + + "\uF400\uF401\uF402\uF403\uF404\uF405\uF406\uF407" + + "\uF408\uF409\uF40A\uF40B\uF40C\uF40D\uF40E\uF40F" + + "\uF410\uF411\uF412\uF413\uF414\uF415\uF416\uF417" + + "\uF418\uF419\uF41A\uF41B\uF41C\uF41D\uF41E\uF41F" + + "\uF420\uF421\uF422\uF423\uF424\uF425\uF426\uF427" + + "\uF428\uF429\uF42A\uF42B\uF42C\uF42D\uF42E\uF42F" + + "\uF430\uF431\uF432\uF433\uF434\uF435\uF436\uF437" + + "\uF438\uF439\uF43A\uF43B\uF43C\uF43D\uF43E\uF43F" + + "\uF440\uF441\uF442\uF443\uF444\uF445\uF446\uF447" + + "\uF448\uF449\uF44A\uF44B\uF44C\uF44D\uF44E\uF44F" + + "\uF450\uF451\uF452\uF453\uF454\uF455\uF456\uF457" + + "\uF458\uF459\uF45A\uF45B\uF45C\uF45D\uF45E\uF45F" + + "\uF460\uF461\uF462\uF463\uF464\uF465\uF466\uF467" + + "\uF468\uF469\uF46A\uF46B\uF46C\uF46D\uF46E\uF46F" + + "\uF470\uF471\uF472\uF473\uF474\uF475\uF476\uF477" + + "\uF478\uF479\uF47A\uF47B\uF47C\uF47D\uF47E\uF47F" + + "\uF480\uF481\uF482\uF483\uF484\uF485\uF486\uF487" + + "\uF488\uF489\uF48A\uF48B\uF48C\uF48D\uF48E\uF48F" + + "\uF490\uF491\uF492\uF493\uF494\uF495\uF496\uF497" + + "\uF498\uF499\uF49A\uF49B\uF49C\uF49D\uF49E\uF49F" + + "\uF4A0\uF4A1\uF4A2\uF4A3\uF4A4\uF4A5\uF4A6\uF4A7" + + "\uF4A8\uF4A9\uF4AA\uF4AB\uF4AC\uF4AD\uF4AE\uF4AF" + + "\uF4B0\uF4B1\uF4B2\uF4B3\uF4B4\uF4B5\uF4B6\uF4B7" + + "\uF4B8\uF4B9\uF4BA\uF4BB\uF4BC\uF4BD\uF4BE\uF4BF" + + "\uF4C0\uF4C1\uF4C2\uF4C3\uF4C4\uF4C5\uF4C6\uF4C7" + + "\uF4C8\uF4C9\uF4CA\uF4CB\uF4CC\uF4CD\uF4CE\uF4CF" + + "\uF4D0\uF4D1\uF4D2\uF4D3\uF4D4\uF4D5\uF4D6\uF4D7" + + "\uF4D8\uF4D9\uF4DA\uF4DB\uF4DC\uF4DD\uF4DE\uF4DF" + + "\uF4E0\uF4E1\uF4E2\uF4E3\uF4E4\uF4E5\uF4E6\uF4E7" + + "\uF4E8\uF4E9\uF4EA\uF4EB\uF4EC\uF4ED\uF4EE\uF4EF" + + "\uF4F0\uF4F1\uF4F2\uF4F3\uF4F4\uF4F5\uF4F6\uF4F7" + + "\uF4F8\uF4F9\uF4FA\uF4FB\uF4FC\uF4FD\uF4FE\uF4FF" + + "\uF500\uF501\uF502\uF503\uF504\uF505\uF506\uF507" + + "\uF508\uF509\uF50A\uF50B\uF50C\uF50D\uF50E\uF50F" + + "\uF510\uF511\uF512\uF513\uF514\uF515\uF516\uF517" + + "\uF518\uF519\uF51A\uF51B\uF51C\uF51D\uF51E\uF51F" + + "\uF520\uF521\uF522\uF523\uF524\uF525\uF526\uF527" + + "\uF528\uF529\uF52A\uF52B\uF52C\uF52D\uF52E\uF52F" + + "\uF530\uF531\uF532\uF533\uF534\uF535\uF536\uF537" + + "\uF538\uF539\uF53A\uF53B\uF53C\uF53D\uF53E\uF53F" + + "\uF540\uF541\uF542\uF543\uF544\uF545\uF546\uF547" + + "\uF548\uF549\uF54A\uF54B\uF54C\uF54D\uF54E\uF54F" + + "\uF550\uF551\uF552\uF553\uF554\uF555\uF556\uF557" + + "\uF558\uF559\uF55A\uF55B\uF55C\uF55D\uF55E\uF55F" + + "\uF560\uF561\uF562\uF563\uF564\uF565\uF566\uF567" + + "\uF568\uF569\uF56A\uF56B\uF56C\uF56D\uF56E\uF56F" + + "\uF570\uF571\uF572\uF573\uF574\uF575\uF576\uF577" + + "\uF578\uF579\uF57A\uF57B\uF57C\uF57D\uF57E\uF57F" + + "\uF580\uF581\uF582\uF583\uF584\uF585\uF586\uF587" + + "\uF588\uF589\uF58A\uF58B\uF58C\uF58D\uF58E\uF58F" + + "\uF590\uF591\uF592\uF593\uF594\uF595\uF596\uF597" + + "\uF598\uF599\uF59A\uF59B\uF59C\uF59D\uF59E\uF59F" + + "\uF5A0\uF5A1\uF5A2\uF5A3\uF5A4\uF5A5\uF5A6\uF5A7" + + "\uF5A8\uF5A9\uF5AA\uF5AB\uF5AC\uF5AD\uF5AE\uF5AF" + + "\uF5B0\uF5B1\uF5B2\uF5B3\uF5B4\uF5B5\uF5B6\uF5B7" + + "\uF5B8\uF5B9\uF5BA\uF5BB\uF5BC\uF5BD\uF5BE\uF5BF" + + "\uF5C0\uF5C1\uF5C2\uF5C3\uF5C4\uF5C5\uF5C6\uF5C7" + + "\uF5C8\uF5C9\uF5CA\uF5CB\uF5CC\uF5CD\uF5CE\uF5CF" + + "\uF5D0\uF5D1\uF5D2\uF5D3\uF5D4\uF5D5\uF5D6\uF5D7" + + "\uF5D8\uF5D9\uF5DA\uF5DB\uF5DC\uF5DD\uF5DE\uF5DF" + + "\uF5E0\uF5E1\uF5E2\uF5E3\uF5E4\uF5E5\uF5E6\uF5E7" + + "\uF5E8\uF5E9\uF5EA\uF5EB\uF5EC\uF5ED\uF5EE\uF5EF" + + "\uF5F0\uF5F1\uF5F2\uF5F3\uF5F4\uF5F5\uF5F6\uF5F7" + + "\uF5F8\uF5F9\uF5FA\uF5FB\uF5FC\uF5FD\uF5FE\uF5FF" + + "\uF600\uF601\uF602\uF603\uF604\uF605\uF606\uF607" + + "\uF608\uF609\uF60A\uF60B\uF60C\uF60D\uF60E\uF60F" + + "\uF610\uF611\uF612\uF613\uF614\uF615\uF616\uF617" + + "\uF618\uF619\uF61A\uF61B\uF61C\uF61D\uF61E\uF61F" + + "\uF620\uF621\uF622\uF623\uF624\uF625\uF626\uF627" + + "\uF628\uF629\uF62A\uF62B\uF62C\uF62D\uF62E\uF62F" + + "\uF630\uF631\uF632\uF633\uF634\uF635\uF636\uF637" + + "\uF638\uF639\uF63A\uF63B\uF63C\uF63D\uF63E\uF63F" + + "\uF640\uF641\uF642\uF643\uF644\uF645\uF646\uF647" + + "\uF648\uF649\uF64A\uF64B\uF64C\uF64D\uF64E\uF64F" + + "\uF650\uF651\uF652\uF653\uF654\uF655\uF656\uF657" + + "\uF658\uF659\uF65A\uF65B\uF65C\uF65D\uF65E\uF65F" + + "\uF660\uF661\uF662\uF663\uF664\uF665\uF666\uF667" + + "\uF668\uF669\uF66A\uF66B\uF66C\uF66D\uF66E\uF66F" + + "\uF670\uF671\uF672\uF673\uF674\uF675\uF676\uF677" + + "\uF678\uF679\uF67A\uF67B\uF67C\uF67D\uF67E\uF67F" + + "\uF680\uF681\uF682\uF683\uF684\uF685\uF686\uF687" + + "\uF688\uF689\uF68A\uF68B\uF68C\uF68D\uF68E\uF68F" + + "\uF690\uF691\uF692\uF693\uF694\uF695\uF696\uF697" + + "\uF698\uF699\uF69A\uF69B\uF69C\uF69D\uF69E\uF69F" + + "\uF6A0\uF6A1\uF6A2\uF6A3\uF6A4\uF6A5\uF6A6\uF6A7" + + "\uF6A8\uF6A9\uF6AA\uF6AB\uF6AC\uF6AD\uF6AE\uF6AF" + + "\uF6B0\uF6B1\uF6B2\uF6B3\uF6B4\uF6B5\uF6B6\uF6B7" + + "\uF6B8\uF6B9\uF6BA\uF6BB\uF6BC\uF6BD\uF6BE\uF6BF" + + "\uF6C0\uF6C1\uF6C2\uF6C3\uF6C4\uF6C5\uF6C6\uF6C7" + + "\uF6C8\uF6C9\uF6CA\uF6CB\uF6CC\uF6CD\uF6CE\uF6CF" + + "\uF6D0\uF6D1\uF6D2\uF6D3\uF6D4\uF6D5\uF6D6\uF6D7" + + "\uF6D8\uF6D9\uF6DA\uF6DB\uF6DC\uF6DD\uF6DE\uF6DF" + + "\uF6E0\uF6E1\uF6E2\uF6E3\uF6E4\uF6E5\uF6E6\uF6E7" + + "\uF6E8\uF6E9\uF6EA\uF6EB\uF6EC\uF6ED\uF6EE\uF6EF" + + "\uF6F0\uF6F1\uF6F2\uF6F3\uF6F4\uF6F5\uF6F6\uF6F7" + + "\uF6F8\uF6F9\uF6FA\uF6FB\uF6FC\uF6FD\uF6FE\uF6FF" + + "\uF700\uF701\uF702\uF703\uF704\uF705\uF706\uF707" + + "\uF708\uF709\uF70A\uF70B\uF70C\uF70D\uF70E\uF70F" + + "\uF710\uF711\uF712\uF713\uF714\uF715\uF716\uF717" + + "\uF718\uF719\uF71A\uF71B\uF71C\uF71D\uF71E\uF71F" + + "\uF720\uF721\uF722\uF723\uF724\uF725\uF726\uF727" + + "\uF728\uF729\uF72A\uF72B\uF72C\uF72D\uF72E\uF72F" + + "\uF730\uF731\uF732\uF733\uF734\uF735\uF736\uF737" + + "\uF738\uF739\uF73A\uF73B\uF73C\uF73D\uF73E\uF73F" + + "\uF740\uF741\uF742\uF743\uF744\uF745\uF746\uF747" + + "\uF748\uF749\uF74A\uF74B\uF74C\uF74D\uF74E\uF74F" + + "\uF750\uF751\uF752\uF753\uF754\uF755\uF756\uF757" + + "\uF758\uF759\uF75A\uF75B\uF75C\uF75D\uF75E\uF75F" + + "\uF760\uF761\uF762\uF763\uF764\uF765\uF766\uF767" + + "\uF768\uF769\uF76A\uF76B\uF76C\uF76D\uF76E\uF76F" + + "\uF770\uF771\uF772\uF773\uF774\uF775\uF776\uF777" + + "\uF778\uF779\uF77A\uF77B\uF77C\uF77D\uF77E\uF77F" + + "\uF780\uF781\uF782\uF783\uF784\uF785\uF786\uF787" + + "\uF788\uF789\uF78A\uF78B\uF78C\uF78D\uF78E\uF78F" + + "\uF790\uF791\uF792\uF793\uF794\uF795\uF796\uF797" + + "\uF798\uF799\uF79A\uF79B\uF79C\uF79D\uF79E\uF79F" + + "\uF7A0\uF7A1\uF7A2\uF7A3\uF7A4\uF7A5\uF7A6\uF7A7" + + "\uF7A8\uF7A9\uF7AA\uF7AB\uF7AC\uF7AD\uF7AE\uF7AF" + + "\uF7B0\uF7B1\uF7B2\uF7B3\uF7B4\uF7B5\uF7B6\uF7B7" + + "\uF7B8\uF7B9\uF7BA\uF7BB\uF7BC\uF7BD\uF7BE\uF7BF" + + "\uF7C0\uF7C1\uF7C2\uF7C3\uF7C4\uF7C5\uF7C6\uF7C7" + + "\uF7C8\uF7C9\uF7CA\uF7CB\uF7CC\uF7CD\uF7CE\uF7CF" + + "\uF7D0\uF7D1\uF7D2\uF7D3\uF7D4\uF7D5\uF7D6\uF7D7" + + "\uF7D8\uF7D9\uF7DA\uF7DB\uF7DC\uF7DD\uF7DE\uF7DF" + + "\uF7E0\uF7E1\uF7E2\uF7E3\uF7E4\uF7E5\uF7E6\uF7E7" + + "\uF7E8\uF7E9\uF7EA\uF7EB\uF7EC\uF7ED\uF7EE\uF7EF" + + "\uF7F0\uF7F1\uF7F2\uF7F3\uF7F4\uF7F5\uF7F6\uF7F7" + + "\uF7F8\uF7F9\uF7FA\uF7FB\uF7FC\uF7FD\uF7FE\uF7FF" + + "\uF800\uF801\uF802\uF803\uF804\uF805\uF806\uF807" + + "\uF808\uF809\uF80A\uF80B\uF80C\uF80D\uF80E\uF80F" + + "\uF810\uF811\uF812\uF813\uF814\uF815\uF816\uF817" + + "\uF818\uF819\uF81A\uF81B\uF81C\uF81D\uF81E\uF81F" + + "\uF820\uF821\uF822\uF823\uF824\uF825\uF826\uF827" + + "\uF828\uF829\uF82A\uF82B\uF82C\uF82D\uF82E\uF82F" + + "\uF830\uF831\uF832\uF833\uF834\uF835\uF836\uF837" + + "\uF838\uF839\uF83A\uF83B\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD" + ; + mappingTableG2ad = + "\u309B\u309C\u00A8\uFF3E\u30FD\u30FE\u309D\u309E" + + "\u02BA\uF83E\u3005\u3006\u3007\u30FC\u2010\uFF3B" + + "\uFF3D\u00B4\u2033\u273D\u3013\u2208\u220B\u2286" + + "\u2287\u2282\u2283\u2227\u2228\u21D2\u21D4\u2200" + + "\u2203\u2312\u2202\u2207\u226A\u226B\u223D\u221D" + + "\u222C\u212B\u2030\u266F\u266D\u266A\u2020\u2021" + + "\u00B6\u25EF\u3041\u3042\u3043\u3044\u3045\u3046" + + "\u3047\u3048\u3049\u304A\u304B\u304C\u304D\u304E" + + "\u304F\u3050\u3051\u3052\u3053\u3054\u3055\u3056" + + "\u3057\u3058\u3059\u305A\u305B\u305C\u305D\u305E" + + "\u305F\u3060\u3061\u3062\u3063\u3064\u3065\u3066" + + "\u3067\u3068\u3069\u306A\u306B\u306C\u306D\u306E" + + "\u306F\u3070\u3071\u3072\u3073\u3074\u3075\u3076" + + "\u3077\u3078\u3079\u307A\u307B\u307C\u307D\u307E" + + "\u307F\u3080\u3081\u3082\u3083\u3084\u3085\u3086" + + "\u3087\u3088\u3089\u308A\u308B\u308C\u308D\u308E" + + "\u308F\u3090\u3091\u3092\u3093\u30A1\u30A2\u30A3" + + "\u30A4\u30A5\u30A6\u30A7\u30A8\u30A9\u30AA\u30AB" + + "\u30AC\u30AD\u30AE\u30AF\u30B0\u30B1\u30B2\u30B3" + + "\u30B4\u30B5\u30B6\u30B7\u30B8\u30B9\u30BA\u30BB" + + "\u30BC\u30BD\u30BE\u30BF\u30C0\u30C1\u30C2\u30C3" + + "\u30C4\u30C5\u30C6\u30C7\u30C8\u30C9\u30CA\u30CB" + + "\u30CC\u30CD\u30CE\u30CF\u30D0\u30D1\u30D2\u30D3" + + "\u30D4\u30D5\u30D6\u30D7\u30D8\u30D9\u30DA\u30DB" + + "\u30DC\u30DD\u30DE\u30DF\u30E0\u30E1\u30E2\u30E3" + + "\u30E4\u30E5\u30E6\u30E7\u30E8\u30E9\u30EA\u30EB" + + "\u30EC\u30ED\u30EE\u30EF\u30F0\u30F1\u30F2\u30F3" + + "\u30F4\u30F5\u30F6\u0410\u0411\u0412\u0413\u0414" + + "\u0415\u0401\u0416\u0417\u0418\u0419\u041A\u041B" + + "\u041C\u041D\u041E\u041F\u0420\u0421\u0422\u0423" + + "\u0424\u0425\u0426\u0427\u0428\u0429\u042A\u042B" + + "\u042C\u042D\u042E\u042F\u0430\u0431\u0432\u0433" + + "\u0434\u0435\u0451\u0436\u0437\u0438\u0439\u043A" + + "\u043B\u043C\u043D\u043E\u043F\u0440\u0441\u0442" + + "\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044A" + + "\u044B\u044C\u044D\u044E\u044F\u2501\u2503\u250F" + + "\u2513\u251B\u2517\u2523\u2533\u252B\u253B\u254B" + + "\u2520\u252F\u2528\u2537\u253F\u251D\u2530\u2525" + + "\u2538\u2542\uF83F\uF840\uF841\uF842\u21E7\u21B8" + + "\u21B9\uFFE2\uFFE4\uFF07\uFF02\u3231\u2116\u2121" + + "\u6491\uFA0C\u691E\uFA0D\u6EB8\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD" + + "\uFFFD\uFFFD\uFFFD\uFFFD" + ; + } + } + + protected static class Encoder extends SimpleEUCEncoder { + + public Encoder(Charset cs) { + super(cs); + super.mask1 = 0xFFC0; + super.mask2 = 0x003F; + super.shift = 6; + super.index1 = index1; + super.index2 = index2; + super.index2a = index2a; + super.index2b = index2b; + super.index2c = index2c; + } + + private static final short index1[] = + { + 19535, 13095, 12408, 11748, 223, 223, 223, 223, // 0000 - 01FF + 223, 223, 9457, 14043, 223, 223, 10349, 11067, // 0200 - 03FF + 24969, 10729, 223, 223, 223, 223, 223, 223, // 0400 - 05FF + 223, 223, 223, 223, 223, 223, 223, 223, // 0600 - 07FF + 223, 223, 223, 223, 223, 223, 223, 223, // 0800 - 09FF + 223, 223, 223, 223, 223, 223, 223, 223, // 0A00 - 0BFF + 223, 223, 223, 223, 223, 223, 223, 223, // 0C00 - 0DFF + 223, 223, 223, 223, 223, 223, 223, 223, // 0E00 - 0FFF + 223, 223, 223, 223, 223, 223, 223, 223, // 1000 - 11FF + 223, 223, 223, 223, 223, 223, 223, 223, // 1200 - 13FF + 223, 223, 223, 223, 223, 223, 223, 223, // 1400 - 15FF + 223, 223, 223, 223, 223, 223, 223, 223, // 1600 - 17FF + 223, 223, 223, 223, 223, 223, 223, 223, // 1800 - 19FF + 223, 223, 223, 223, 223, 223, 223, 223, // 1A00 - 1BFF + 223, 223, 223, 223, 223, 223, 223, 223, // 1C00 - 1DFF + 223, 223, 223, 223, 223, 223, 223, 223, // 1E00 - 1FFF + 24401, 223, 223, 223, 24353, 12626, 9241, 8204, // 2000 - 21FF + 10119, 9193, 16486, 223, 8250, 223, 223, 223, // 2200 - 23FF + 9582, 9616, 223, 223, 9083, 13986, 24230, 15436, // 2400 - 25FF + 21619, 8517, 223, 223, 226, 223, 223, 223, // 2600 - 27FF + 223, 223, 223, 223, 223, 223, 223, 223, // 2800 - 29FF + 223, 223, 223, 223, 223, 223, 223, 223, // 2A00 - 2BFF + 223, 223, 223, 223, 223, 223, 223, 223, // 2C00 - 2DFF + 223, 223, 223, 223, 223, 223, 223, 223, // 2E00 - 2FFF + 7839, 22767, 7071, 6753, 10302, 223, 223, 223, // 3000 - 31FF + 8269, 223, 7166, 223, 223, 223, 8124, 9301, // 3200 - 33FF + 223, 223, 223, 223, 223, 223, 223, 223, // 3400 - 35FF + 223, 223, 223, 223, 223, 223, 223, 223, // 3600 - 37FF + 223, 223, 223, 223, 223, 223, 223, 223, // 3800 - 39FF + 223, 223, 223, 223, 223, 223, 223, 223, // 3A00 - 3BFF + 223, 223, 223, 223, 223, 223, 223, 223, // 3C00 - 3DFF + 223, 223, 223, 223, 223, 223, 223, 223, // 3E00 - 3FFF + 223, 223, 223, 223, 223, 223, 223, 223, // 4000 - 41FF + 223, 223, 223, 223, 223, 223, 223, 223, // 4200 - 43FF + 223, 223, 223, 223, 223, 223, 223, 223, // 4400 - 45FF + 223, 223, 223, 223, 223, 223, 223, 223, // 4600 - 47FF + 223, 223, 223, 223, 223, 223, 223, 223, // 4800 - 49FF + 223, 223, 223, 223, 223, 223, 223, 223, // 4A00 - 4BFF + 223, 223, 223, 223, 223, 223, 223, 223, // 4C00 - 4DFF + 6625, 8699, 27828, 28393, 28329, 6879, 28265, 28014, // 4E00 - 4FFF + 27766, 27640, 27576, 21185, 27512, 27325, 27198, 27888, // 5000 - 51FF + 27073, 20233, 26946, 26882, 17057, 26818, 27387, 11327, // 5200 - 53FF + 19346, 26754, 26690, 26626, 26179, 26562, 26498, 26434, // 5400 - 55FF + 26370, 26306, 26053, 25989, 18008, 25863, 25799, 19472, // 5600 - 57FF + 25735, 8573, 25671, 17817, 9518, 25607, 25480, 25416, // 5800 - 59FF + 25289, 25225, 7520, 25161, 7647, 25097, 25033, 24846, // 5A00 - 5BFF + 289, 24656, 21560, 12466, 24592, 24528, 24167, 24103, // 5C00 - 5DFF + 13031, 24039, 23912, 156, 12969, 23848, 23784, 23720, // 5E00 - 5FFF + 23597, 23533, 23469, 23405, 23341, 23214, 23150, 11202, // 6000 - 61FF + 23086, 23022, 22958, 11451, 22831, 22704, 22577, 22454, // 6200 - 63FF + 11806, 22390, 14386, 9741, 22326, 8010, 22262, 6816, // 6400 - 65FF + 22138, 28077, 21948, 21884, 21820, 23656, 28140, 21756, // 6600 - 67FF + 7885, 21692, 21502, 24906, 22198, 21377, 12060, 27261, // 6800 - 69FF + 21313, 21249, 21122, 11389, 21058, 27009, 20994, 20930, // 6A00 - 6BFF + 20866, 20739, 20675, 20489, 20425, 20361, 20297, 20170, // 6C00 - 6DFF + 20106, 19983, 8325, 19919, 19855, 19791, 19727, 19663, // 6E00 - 6FFF + 19599, 19283, 10665, 22513, 19159, 26116, 19095, 19031, // 7000 - 71FF + 26242, 25543, 18967, 18903, 18839, 18775, 18711, 18647, // 7200 - 73FF + 18583, 18519, 18455, 18391, 18327, 18263, 18199, 18135, // 7400 - 75FF + 17945, 10475, 25352, 11139, 24719, 19219, 17881, 10181, // 7600 - 77FF + 10243, 27134, 17691, 24782, 24464, 17627, 23975, 17502, // 7800 - 79FF + 17438, 17374, 17310, 17246, 17121, 16998, 9678, 16934, // 7A00 - 7BFF + 16870, 16806, 16742, 20548, 16678, 16614, 16550, 16424, // 7C00 - 7DFF + 27448, 16297, 16204, 223, 15502, 28201, 21438, 16140, // 7E00 - 7FFF + 16076, 17563, 24292, 7947, 15949, 17182, 15885, 15821, // 8000 - 81FF + 15757, 15693, 15566, 15315, 15251, 15187, 23277, 15123, // 8200 - 83FF + 22894, 14960, 27950, 14896, 14769, 14642, 14578, 14514, // 8400 - 85FF + 14450, 14324, 20042, 14197, 14133, 13924, 22640, 13860, // 8600 - 87FF + 13796, 13732, 13542, 13415, 22011, 22074, 20802, 13351, // 8800 - 89FF + 13287, 13223, 20611, 13159, 12813, 12749, 12594, 223, // 8A00 - 8BFF + 21628, 19409, 12530, 12344, 12252, 12906, 12188, 18071, // 8C00 - 8DFF + 12124, 11998, 17754, 11934, 11870, 11707, 7203, 27702, // 8E00 - 8FFF + 11643, 16360, 11579, 16012, 11515, 11266, 11003, 10939, // 9000 - 91FF + 10875, 10811, 10603, 10539, 10413, 10055, 9997, 9933, // 9200 - 93FF + 9869, 9805, 9451, 223, 223, 11077, 9387, 9147, // 9400 - 95FF + 15059, 9019, 8955, 8891, 8827, 25925, 8763, 15629, // 9600 - 97FF + 8637, 15378, 14069, 8453, 8389, 15023, 93, 14832, // 9800 - 99FF + 14705, 8074, 12280, 7775, 14260, 13605, 7711, 7584, // 9A00 - 9BFF + 12685, 7459, 223, 16233, 7395, 7331, 7267, 13668, // 9C00 - 9DFF + 7135, 10747, 7007, 6943, 6689, 6561, 51, 223, // 9E00 - 9FFF + 223, 223, 223, 223, 223, 223, 223, 223, // A000 - A1FF + 223, 223, 223, 223, 223, 223, 223, 223, // A200 - A3FF + 223, 223, 223, 223, 223, 223, 223, 223, // A400 - A5FF + 223, 223, 223, 223, 223, 223, 223, 223, // A600 - A7FF + 223, 223, 223, 223, 223, 223, 223, 223, // A800 - A9FF + 223, 223, 223, 223, 223, 223, 223, 223, // AA00 - ABFF + 223, 223, 223, 223, 223, 223, 223, 223, // AC00 - ADFF + 223, 223, 223, 223, 223, 223, 223, 223, // AE00 - AFFF + 223, 223, 223, 223, 223, 223, 223, 223, // B000 - B1FF + 223, 223, 223, 223, 223, 223, 223, 223, // B200 - B3FF + 223, 223, 223, 223, 223, 223, 223, 223, // B400 - B5FF + 223, 223, 223, 223, 223, 223, 223, 223, // B600 - B7FF + 223, 223, 223, 223, 223, 223, 223, 223, // B800 - B9FF + 223, 223, 223, 223, 223, 223, 223, 223, // BA00 - BBFF + 223, 223, 223, 223, 223, 223, 223, 223, // BC00 - BDFF + 223, 223, 223, 223, 223, 223, 223, 223, // BE00 - BFFF + 223, 223, 223, 223, 223, 223, 223, 223, // C000 - C1FF + 223, 223, 223, 223, 223, 223, 223, 223, // C200 - C3FF + 223, 223, 223, 223, 223, 223, 223, 223, // C400 - C5FF + 223, 223, 223, 223, 223, 223, 223, 223, // C600 - C7FF + 223, 223, 223, 223, 223, 223, 223, 223, // C800 - C9FF + 223, 223, 223, 223, 223, 223, 223, 223, // CA00 - CBFF + 223, 223, 223, 223, 223, 223, 223, 223, // CC00 - CDFF + 223, 223, 223, 223, 223, 223, 223, 223, // CE00 - CFFF + 223, 223, 223, 223, 223, 223, 223, 223, // D000 - D1FF + 223, 223, 223, 223, 223, 223, 223, 223, // D200 - D3FF + 223, 223, 223, 223, 223, 223, 223, 223, // D400 - D5FF + 223, 223, 223, 223, 223, 223, 223, 223, // D600 - D7FF + 223, 223, 223, 223, 223, 223, 223, 223, // D800 - D9FF + 223, 223, 223, 223, 223, 223, 223, 223, // DA00 - DBFF + 223, 223, 223, 223, 223, 223, 223, 223, // DC00 - DDFF + 223, 223, 223, 223, 223, 223, 223, 223, // DE00 - DFFF + 6497, 6433, 6369, 6305, 6241, 6177, 6113, 6049, // E000 - E1FF + 5985, 5921, 5857, 5793, 5729, 5665, 5601, 5537, // E200 - E3FF + 5473, 5409, 5345, 5281, 5217, 5153, 5089, 5025, // E400 - E5FF + 4961, 4897, 4833, 4769, 4705, 4641, 4577, 4513, // E600 - E7FF + 4449, 4385, 4321, 4257, 4193, 4129, 4065, 4001, // E800 - E9FF + 3937, 3873, 3809, 3745, 3681, 3617, 3553, 3489, // EA00 - EBFF + 3425, 3361, 3297, 3233, 3169, 3105, 3041, 2977, // EC00 - EDFF + 2913, 2849, 2785, 2721, 2657, 2593, 2529, 2465, // EE00 - EFFF + 2401, 2337, 2273, 2209, 2145, 2081, 2017, 1953, // F000 - F1FF + 1889, 1825, 1761, 1697, 1633, 1569, 1505, 1441, // F200 - F3FF + 1377, 1313, 1249, 1185, 1121, 1057, 993, 929, // F400 - F5FF + 865, 801, 737, 673, 609, 545, 481, 417, // F600 - F7FF + 353, 220, 223, 223, 223, 223, 223, 223, // F800 - F9FF + 15488, 223, 223, 223, 223, 223, 223, 223, // FA00 - FBFF + 223, 223, 223, 223, 223, 223, 223, 223, // FC00 - FDFF + 9323, 0, 223, 223, 13478, 12876, 223, 8158, + }; + + private final static String index2; + private final static String index2a; + private final static String index2b; + private final static String index2c; + static { + index2 = + "\u0000\uA1D5\u0000\uA1D8\u0000\uA1D9\u0000\uA1DC\u0000\uA1DD" + // 0 - 4 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2A7" + // 5 - 9 + "\u0000\uA2A8\u0000\uA2AB\u0000\uA2AC\u0000\uA2A9\u0000\uA2AA" + // 10 - 14 + "\u0000\uA1BD\u0000\uA1AE\u0000\uA1AF\u0000\uA1B0\u0000\u0000" + // 15 - 19 + "\u0000\uA1B2\u0000\uA1B3\u0000\uA1B4\u0000\uA1B5\u0000\uA1B9" + // 20 - 24 + "\u0000\uA1DE\u0000\uA1DF\u0000\uA1E0\u0000\uA1E1\u0000\uA1E2" + // 25 - 29 + "\u0000\uA1E3\u0000\uA2AD\u0000\uA2AE\u0000\uA2AF\u0000\uA2BF" + // 30 - 34 + "\u0000\uA2C0\u0000\uA2C1\u0000\uA2C2\u0000\uA2C3\u0000\u0000" + // 35 - 39 + "\u0000\uA2E2\u0000\uA2EC\u0000\uA2ED\u0000\uA2EE\u0000\u0000" + // 40 - 44 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 45 - 49 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 50 - 54 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 55 - 59 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3BE" + // 60 - 64 + "\u0000\u0000\u0000\u0000\u0000\uF8B2\u8EA2\uEBCD\u8EA2\uEDC3" + // 65 - 69 + "\u0000\u0000\u0000\uFCB3\u8EA2\uEEFB\u0000\u0000\u0000\u0000" + // 70 - 74 + "\u8EA2\uF2C4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3BF" + // 75 - 79 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE1C2\u0000\u0000" + // 80 - 84 + "\u8EA2\uEEFC\u0000\u0000\u8EA2\uF1EF\u0000\u0000\u0000\u0000" + // 85 - 89 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 90 - 94 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 95 - 99 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 100 - 104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 105 - 109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 110 - 114 + "\u0000\uD3FB\u8EA2\uBAB4\u8EA2\uE0E1\u0000\uD3FC\u0000\u0000" + // 115 - 119 + "\u0000\u0000\u8EA2\uCFBA\u8EA2\uCFB9\u8EA2\uDBEC\u0000\u0000" + // 120 - 124 + "\u0000\u0000\u8EA2\uE0E3\u0000\u0000\u8EA2\uE0E2\u0000\u0000" + // 125 - 129 + "\u0000\uF7F6\u8EA2\uE7FD\u8EA2\uE7FE\u0000\uFAD4\u0000\u0000" + // 130 - 134 + "\u0000\u0000\u8EA2\uF2A2\u0000\uD8EB\u0000\uE3A6\u0000\uE3A5" + // 135 - 139 + "\u8EA2\uC8EA\u8EA2\uC8EC\u0000\uE7EA\u8EA2\uC8EB\u0000\uE7E9" + // 140 - 144 + "\u0000\uE7EB\u8EA2\uC8ED\u0000\u0000\u0000\u0000\u0000\u0000" + // 145 - 149 + "\u8EA2\uCFBC\u8EA2\uCFBE\u8EA2\uCFBD\u0000\u0000\u8EA2\uCFC0" + // 150 - 154 + "\u0000\u0000\u0000\u0000\u0000\uDFA1\u0000\uDFA2\u0000\u0000" + // 155 - 159 + "\u0000\uDFA3\u8EA2\uC2E3\u8EA2\uC2E5\u8EA2\uC2E7\u0000\uE3EE" + // 160 - 164 + "\u0000\uE3ED\u0000\uDEFE\u8EA2\uC2E6\u8EA2\uC2E4\u0000\u0000" + // 165 - 169 + "\u8EA2\uC9FD\u0000\u0000\u0000\u0000\u8EA2\uC9FB\u8EA2\uCAA3" + // 170 - 174 + "\u0000\uE8E0\u8EA2\uCAA4\u8EA2\uCAA1\u0000\uE8E1\u8EA2\uC9FC" + // 175 - 179 + "\u8EA2\uC9FA\u8EA2\uCAA2\u0000\uECDA\u8EA2\uD0BC\u8EA2\uC9FE" + // 180 - 184 + "\u0000\uECDC\u8EA2\uD0BD\u0000\uECDB\u0000\uECDE\u8EA2\uD0BE" + // 185 - 189 + "\u0000\uECD9\u0000\uECDD\u0000\u0000\u8EA2\uD6FD\u8EA2\uD6FB" + // 190 - 194 + "\u8EA2\uD6FA\u8EA2\uD6FC\u8EA2\uD6F9\u0000\u0000\u0000\u0000" + // 195 - 199 + "\u0000\uF8B3\u0000\u0000\u8EA2\uE8F2\u8EA2\uE8F3\u0000\u0000" + // 200 - 204 + "\u8EA2\uEBD9\u8EA2\uEDCD\u0000\uFDA2\u0000\uA7D5\u0000\u0000" + // 205 - 209 + "\u0000\uCDB7\u0000\uCAAC\u0000\u0000\u0000\u0000\u0000\uD0FA" + // 210 - 214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC4DC\u0000\uC5BD" + // 215 - 219 + "\u8EAD\uA4BA\u8EAD\uA4BB\u8EAD\uA4BC\u0000\u0000\u0000\u0000" + // 220 - 224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 225 - 229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 230 - 234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 235 - 239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 240 - 244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 245 - 249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 250 - 254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 255 - 259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 260 - 264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 265 - 269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 270 - 274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 275 - 279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 280 - 284 + "\u0000\u0000\u0000\u0000\u8EAD\uA1B4\u0000\u0000\u0000\u0000" + // 285 - 289 + "\u0000\uD0EC\u0000\u0000\u8EA2\uAEEB\u0000\uD5AB\u0000\u0000" + // 290 - 294 + "\u0000\u0000\u0000\uD9F2\u0000\uD9F1\u0000\uD9F0\u0000\uDEF1" + // 295 - 299 + "\u0000\uDEF2\u8EA2\uBBBB\u0000\uE8D7\u0000\uF0D2\u0000\uC4D1" + // 300 - 304 + "\u8EA2\uA1BC\u0000\uC5B7\u8EA2\uA1D5\u0000\u0000\u0000\u0000" + // 305 - 309 + "\u8EA2\uA2B4\u0000\uC7FA\u0000\u0000\u0000\u0000\u0000\u0000" + // 310 - 314 + "\u0000\uCCFE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 315 - 319 + "\u8EA2\uC2D1\u0000\u0000\u0000\u0000\u0000\uC4D2\u0000\u0000" + // 320 - 324 + "\u0000\uC5B8\u8EA2\uA2B5\u0000\u0000\u0000\u0000\u8EA2\uA3DE" + // 325 - 329 + "\u0000\u0000\u8EA2\uA3DF\u0000\u0000\u0000\uC9FC\u0000\u0000" + // 330 - 334 + "\u0000\u0000\u0000\u0000\u8EA2\uBBBC\u0000\uDEF3\u0000\u0000" + // 335 - 339 + "\u8EA2\uC2D2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3D3" + // 340 - 344 + "\u0000\uC4D3\u0000\uC4E7\u0000\uC5B9\u8EA2\uA1D6\u0000\uC6C7" + // 345 - 349 + "\u0000\u0000\u0000\uCAA2\u0000\uCAA1\u8EAC\uE2C3\u8EAC\uE2C4" + // 350 - 354 + "\u8EAC\uE2C5\u8EAC\uE2C6\u8EAC\uE2C7\u8EAC\uE2C8\u8EAC\uE2C9" + // 355 - 359 + "\u8EAC\uE2CA\u8EAC\uE2CB\u8EAC\uE2CC\u8EAC\uE2CD\u8EAC\uE2CE" + // 360 - 364 + "\u8EAC\uE2CF\u8EAC\uE2D0\u8EAC\uE2D1\u8EAC\uE2D2\u8EAC\uE2D3" + // 365 - 369 + "\u8EAC\uE2D4\u8EAC\uE2D5\u8EAC\uE2D6\u8EAC\uE2D7\u8EAC\uE2D8" + // 370 - 374 + "\u8EAC\uE2D9\u8EAC\uE2DA\u8EAC\uE2DB\u8EAC\uE2DC\u8EAC\uE2DD" + // 375 - 379 + "\u8EAC\uE2DE\u8EAC\uE2DF\u8EAC\uE2E0\u8EAC\uE2E1\u8EAC\uE2E2" + // 380 - 384 + "\u8EAC\uE2E3\u8EAC\uE2E4\u8EAC\uE2E5\u8EAC\uE2E6\u8EAC\uE2E7" + // 385 - 389 + "\u8EAC\uE2E8\u8EAC\uE2E9\u8EAC\uE2EA\u8EAC\uE2EB\u8EAC\uE2EC" + // 390 - 394 + "\u8EAC\uE2ED\u8EAC\uE2EE\u8EAC\uE2EF\u8EAC\uE2F0\u8EAC\uE2F1" + // 395 - 399 + "\u8EAC\uE2F2\u8EAC\uE2F3\u8EAC\uE2F4\u8EAC\uE2F5\u8EAC\uE2F6" + // 400 - 404 + "\u8EAC\uE2F7\u8EAC\uE2F8\u8EAC\uE2F9\u8EAC\uE2FA\u8EAC\uE2FB" + // 405 - 409 + "\u8EAC\uE2FC\u8EAC\uE2FD\u8EAC\uE2FE\u0000\u0000\u0000\u0000" + // 410 - 414 + "\u8EAD\uA1AA\u8EAD\uA4B9\u8EAC\uE1E1\u8EAC\uE1E2\u8EAC\uE1E3" + // 415 - 419 + "\u8EAC\uE1E4\u8EAC\uE1E5\u8EAC\uE1E6\u8EAC\uE1E7\u8EAC\uE1E8" + // 420 - 424 + "\u8EAC\uE1E9\u8EAC\uE1EA\u8EAC\uE1EB\u8EAC\uE1EC\u8EAC\uE1ED" + // 425 - 429 + "\u8EAC\uE1EE\u8EAC\uE1EF\u8EAC\uE1F0\u8EAC\uE1F1\u8EAC\uE1F2" + // 430 - 434 + "\u8EAC\uE1F3\u8EAC\uE1F4\u8EAC\uE1F5\u8EAC\uE1F6\u8EAC\uE1F7" + // 435 - 439 + "\u8EAC\uE1F8\u8EAC\uE1F9\u8EAC\uE1FA\u8EAC\uE1FB\u8EAC\uE1FC" + // 440 - 444 + "\u8EAC\uE1FD\u8EAC\uE1FE\u8EAC\uE2A1\u8EAC\uE2A2\u8EAC\uE2A3" + // 445 - 449 + "\u8EAC\uE2A4\u8EAC\uE2A5\u8EAC\uE2A6\u8EAC\uE2A7\u8EAC\uE2A8" + // 450 - 454 + "\u8EAC\uE2A9\u8EAC\uE2AA\u8EAC\uE2AB\u8EAC\uE2AC\u8EAC\uE2AD" + // 455 - 459 + "\u8EAC\uE2AE\u8EAC\uE2AF\u8EAC\uE2B0\u8EAC\uE2B1\u8EAC\uE2B2" + // 460 - 464 + "\u8EAC\uE2B3\u8EAC\uE2B4\u8EAC\uE2B5\u8EAC\uE2B6\u8EAC\uE2B7" + // 465 - 469 + "\u8EAC\uE2B8\u8EAC\uE2B9\u8EAC\uE2BA\u8EAC\uE2BB\u8EAC\uE2BC" + // 470 - 474 + "\u8EAC\uE2BD\u8EAC\uE2BE\u8EAC\uE2BF\u8EAC\uE2C0\u8EAC\uE2C1" + // 475 - 479 + "\u8EAC\uE2C2\u8EAC\uE1A1\u8EAC\uE1A2\u8EAC\uE1A3\u8EAC\uE1A4" + // 480 - 484 + "\u8EAC\uE1A5\u8EAC\uE1A6\u8EAC\uE1A7\u8EAC\uE1A8\u8EAC\uE1A9" + // 485 - 489 + "\u8EAC\uE1AA\u8EAC\uE1AB\u8EAC\uE1AC\u8EAC\uE1AD\u8EAC\uE1AE" + // 490 - 494 + "\u8EAC\uE1AF\u8EAC\uE1B0\u8EAC\uE1B1\u8EAC\uE1B2\u8EAC\uE1B3" + // 495 - 499 + "\u8EAC\uE1B4\u8EAC\uE1B5\u8EAC\uE1B6\u8EAC\uE1B7\u8EAC\uE1B8" + // 500 - 504 + "\u8EAC\uE1B9\u8EAC\uE1BA\u8EAC\uE1BB\u8EAC\uE1BC\u8EAC\uE1BD" + // 505 - 509 + "\u8EAC\uE1BE\u8EAC\uE1BF\u8EAC\uE1C0\u8EAC\uE1C1\u8EAC\uE1C2" + // 510 - 514 + "\u8EAC\uE1C3\u8EAC\uE1C4\u8EAC\uE1C5\u8EAC\uE1C6\u8EAC\uE1C7" + // 515 - 519 + "\u8EAC\uE1C8\u8EAC\uE1C9\u8EAC\uE1CA\u8EAC\uE1CB\u8EAC\uE1CC" + // 520 - 524 + "\u8EAC\uE1CD\u8EAC\uE1CE\u8EAC\uE1CF\u8EAC\uE1D0\u8EAC\uE1D1" + // 525 - 529 + "\u8EAC\uE1D2\u8EAC\uE1D3\u8EAC\uE1D4\u8EAC\uE1D5\u8EAC\uE1D6" + // 530 - 534 + "\u8EAC\uE1D7\u8EAC\uE1D8\u8EAC\uE1D9\u8EAC\uE1DA\u8EAC\uE1DB" + // 535 - 539 + "\u8EAC\uE1DC\u8EAC\uE1DD\u8EAC\uE1DE\u8EAC\uE1DF\u8EAC\uE1E0" + // 540 - 544 + "\u8EAC\uE0BF\u8EAC\uE0C0\u8EAC\uE0C1\u8EAC\uE0C2\u8EAC\uE0C3" + // 545 - 549 + "\u8EAC\uE0C4\u8EAC\uE0C5\u8EAC\uE0C6\u8EAC\uE0C7\u8EAC\uE0C8" + // 550 - 554 + "\u8EAC\uE0C9\u8EAC\uE0CA\u8EAC\uE0CB\u8EAC\uE0CC\u8EAC\uE0CD" + // 555 - 559 + "\u8EAC\uE0CE\u8EAC\uE0CF\u8EAC\uE0D0\u8EAC\uE0D1\u8EAC\uE0D2" + // 560 - 564 + "\u8EAC\uE0D3\u8EAC\uE0D4\u8EAC\uE0D5\u8EAC\uE0D6\u8EAC\uE0D7" + // 565 - 569 + "\u8EAC\uE0D8\u8EAC\uE0D9\u8EAC\uE0DA\u8EAC\uE0DB\u8EAC\uE0DC" + // 570 - 574 + "\u8EAC\uE0DD\u8EAC\uE0DE\u8EAC\uE0DF\u8EAC\uE0E0\u8EAC\uE0E1" + // 575 - 579 + "\u8EAC\uE0E2\u8EAC\uE0E3\u8EAC\uE0E4\u8EAC\uE0E5\u8EAC\uE0E6" + // 580 - 584 + "\u8EAC\uE0E7\u8EAC\uE0E8\u8EAC\uE0E9\u8EAC\uE0EA\u8EAC\uE0EB" + // 585 - 589 + "\u8EAC\uE0EC\u8EAC\uE0ED\u8EAC\uE0EE\u8EAC\uE0EF\u8EAC\uE0F0" + // 590 - 594 + "\u8EAC\uE0F1\u8EAC\uE0F2\u8EAC\uE0F3\u8EAC\uE0F4\u8EAC\uE0F5" + // 595 - 599 + "\u8EAC\uE0F6\u8EAC\uE0F7\u8EAC\uE0F8\u8EAC\uE0F9\u8EAC\uE0FA" + // 600 - 604 + "\u8EAC\uE0FB\u8EAC\uE0FC\u8EAC\uE0FD\u8EAC\uE0FE\u8EAC\uDFDD" + // 605 - 609 + "\u8EAC\uDFDE\u8EAC\uDFDF\u8EAC\uDFE0\u8EAC\uDFE1\u8EAC\uDFE2" + // 610 - 614 + "\u8EAC\uDFE3\u8EAC\uDFE4\u8EAC\uDFE5\u8EAC\uDFE6\u8EAC\uDFE7" + // 615 - 619 + "\u8EAC\uDFE8\u8EAC\uDFE9\u8EAC\uDFEA\u8EAC\uDFEB\u8EAC\uDFEC" + // 620 - 624 + "\u8EAC\uDFED\u8EAC\uDFEE\u8EAC\uDFEF\u8EAC\uDFF0\u8EAC\uDFF1" + // 625 - 629 + "\u8EAC\uDFF2\u8EAC\uDFF3\u8EAC\uDFF4\u8EAC\uDFF5\u8EAC\uDFF6" + // 630 - 634 + "\u8EAC\uDFF7\u8EAC\uDFF8\u8EAC\uDFF9\u8EAC\uDFFA\u8EAC\uDFFB" + // 635 - 639 + "\u8EAC\uDFFC\u8EAC\uDFFD\u8EAC\uDFFE\u8EAC\uE0A1\u8EAC\uE0A2" + // 640 - 644 + "\u8EAC\uE0A3\u8EAC\uE0A4\u8EAC\uE0A5\u8EAC\uE0A6\u8EAC\uE0A7" + // 645 - 649 + "\u8EAC\uE0A8\u8EAC\uE0A9\u8EAC\uE0AA\u8EAC\uE0AB\u8EAC\uE0AC" + // 650 - 654 + "\u8EAC\uE0AD\u8EAC\uE0AE\u8EAC\uE0AF\u8EAC\uE0B0\u8EAC\uE0B1" + // 655 - 659 + "\u8EAC\uE0B2\u8EAC\uE0B3\u8EAC\uE0B4\u8EAC\uE0B5\u8EAC\uE0B6" + // 660 - 664 + "\u8EAC\uE0B7\u8EAC\uE0B8\u8EAC\uE0B9\u8EAC\uE0BA\u8EAC\uE0BB" + // 665 - 669 + "\u8EAC\uE0BC\u8EAC\uE0BD\u8EAC\uE0BE\u8EAC\uDEFB\u8EAC\uDEFC" + // 670 - 674 + "\u8EAC\uDEFD\u8EAC\uDEFE\u8EAC\uDFA1\u8EAC\uDFA2\u8EAC\uDFA3" + // 675 - 679 + "\u8EAC\uDFA4\u8EAC\uDFA5\u8EAC\uDFA6\u8EAC\uDFA7\u8EAC\uDFA8" + // 680 - 684 + "\u8EAC\uDFA9\u8EAC\uDFAA\u8EAC\uDFAB\u8EAC\uDFAC\u8EAC\uDFAD" + // 685 - 689 + "\u8EAC\uDFAE\u8EAC\uDFAF\u8EAC\uDFB0\u8EAC\uDFB1\u8EAC\uDFB2" + // 690 - 694 + "\u8EAC\uDFB3\u8EAC\uDFB4\u8EAC\uDFB5\u8EAC\uDFB6\u8EAC\uDFB7" + // 695 - 699 + "\u8EAC\uDFB8\u8EAC\uDFB9\u8EAC\uDFBA\u8EAC\uDFBB\u8EAC\uDFBC" + // 700 - 704 + "\u8EAC\uDFBD\u8EAC\uDFBE\u8EAC\uDFBF\u8EAC\uDFC0\u8EAC\uDFC1" + // 705 - 709 + "\u8EAC\uDFC2\u8EAC\uDFC3\u8EAC\uDFC4\u8EAC\uDFC5\u8EAC\uDFC6" + // 710 - 714 + "\u8EAC\uDFC7\u8EAC\uDFC8\u8EAC\uDFC9\u8EAC\uDFCA\u8EAC\uDFCB" + // 715 - 719 + "\u8EAC\uDFCC\u8EAC\uDFCD\u8EAC\uDFCE\u8EAC\uDFCF\u8EAC\uDFD0" + // 720 - 724 + "\u8EAC\uDFD1\u8EAC\uDFD2\u8EAC\uDFD3\u8EAC\uDFD4\u8EAC\uDFD5" + // 725 - 729 + "\u8EAC\uDFD6\u8EAC\uDFD7\u8EAC\uDFD8\u8EAC\uDFD9\u8EAC\uDFDA" + // 730 - 734 + "\u8EAC\uDFDB\u8EAC\uDFDC\u8EAC\uDEBB\u8EAC\uDEBC\u8EAC\uDEBD" + // 735 - 739 + "\u8EAC\uDEBE\u8EAC\uDEBF\u8EAC\uDEC0\u8EAC\uDEC1\u8EAC\uDEC2" + // 740 - 744 + "\u8EAC\uDEC3\u8EAC\uDEC4\u8EAC\uDEC5\u8EAC\uDEC6\u8EAC\uDEC7" + // 745 - 749 + "\u8EAC\uDEC8\u8EAC\uDEC9\u8EAC\uDECA\u8EAC\uDECB\u8EAC\uDECC" + // 750 - 754 + "\u8EAC\uDECD\u8EAC\uDECE\u8EAC\uDECF\u8EAC\uDED0\u8EAC\uDED1" + // 755 - 759 + "\u8EAC\uDED2\u8EAC\uDED3\u8EAC\uDED4\u8EAC\uDED5\u8EAC\uDED6" + // 760 - 764 + "\u8EAC\uDED7\u8EAC\uDED8\u8EAC\uDED9\u8EAC\uDEDA\u8EAC\uDEDB" + // 765 - 769 + "\u8EAC\uDEDC\u8EAC\uDEDD\u8EAC\uDEDE\u8EAC\uDEDF\u8EAC\uDEE0" + // 770 - 774 + "\u8EAC\uDEE1\u8EAC\uDEE2\u8EAC\uDEE3\u8EAC\uDEE4\u8EAC\uDEE5" + // 775 - 779 + "\u8EAC\uDEE6\u8EAC\uDEE7\u8EAC\uDEE8\u8EAC\uDEE9\u8EAC\uDEEA" + // 780 - 784 + "\u8EAC\uDEEB\u8EAC\uDEEC\u8EAC\uDEED\u8EAC\uDEEE\u8EAC\uDEEF" + // 785 - 789 + "\u8EAC\uDEF0\u8EAC\uDEF1\u8EAC\uDEF2\u8EAC\uDEF3\u8EAC\uDEF4" + // 790 - 794 + "\u8EAC\uDEF5\u8EAC\uDEF6\u8EAC\uDEF7\u8EAC\uDEF8\u8EAC\uDEF9" + // 795 - 799 + "\u8EAC\uDEFA\u8EAC\uDDD9\u8EAC\uDDDA\u8EAC\uDDDB\u8EAC\uDDDC" + // 800 - 804 + "\u8EAC\uDDDD\u8EAC\uDDDE\u8EAC\uDDDF\u8EAC\uDDE0\u8EAC\uDDE1" + // 805 - 809 + "\u8EAC\uDDE2\u8EAC\uDDE3\u8EAC\uDDE4\u8EAC\uDDE5\u8EAC\uDDE6" + // 810 - 814 + "\u8EAC\uDDE7\u8EAC\uDDE8\u8EAC\uDDE9\u8EAC\uDDEA\u8EAC\uDDEB" + // 815 - 819 + "\u8EAC\uDDEC\u8EAC\uDDED\u8EAC\uDDEE\u8EAC\uDDEF\u8EAC\uDDF0" + // 820 - 824 + "\u8EAC\uDDF1\u8EAC\uDDF2\u8EAC\uDDF3\u8EAC\uDDF4\u8EAC\uDDF5" + // 825 - 829 + "\u8EAC\uDDF6\u8EAC\uDDF7\u8EAC\uDDF8\u8EAC\uDDF9\u8EAC\uDDFA" + // 830 - 834 + "\u8EAC\uDDFB\u8EAC\uDDFC\u8EAC\uDDFD\u8EAC\uDDFE\u8EAC\uDEA1" + // 835 - 839 + "\u8EAC\uDEA2\u8EAC\uDEA3\u8EAC\uDEA4\u8EAC\uDEA5\u8EAC\uDEA6" + // 840 - 844 + "\u8EAC\uDEA7\u8EAC\uDEA8\u8EAC\uDEA9\u8EAC\uDEAA\u8EAC\uDEAB" + // 845 - 849 + "\u8EAC\uDEAC\u8EAC\uDEAD\u8EAC\uDEAE\u8EAC\uDEAF\u8EAC\uDEB0" + // 850 - 854 + "\u8EAC\uDEB1\u8EAC\uDEB2\u8EAC\uDEB3\u8EAC\uDEB4\u8EAC\uDEB5" + // 855 - 859 + "\u8EAC\uDEB6\u8EAC\uDEB7\u8EAC\uDEB8\u8EAC\uDEB9\u8EAC\uDEBA" + // 860 - 864 + "\u8EAC\uDCF7\u8EAC\uDCF8\u8EAC\uDCF9\u8EAC\uDCFA\u8EAC\uDCFB" + // 865 - 869 + "\u8EAC\uDCFC\u8EAC\uDCFD\u8EAC\uDCFE\u8EAC\uDDA1\u8EAC\uDDA2" + // 870 - 874 + "\u8EAC\uDDA3\u8EAC\uDDA4\u8EAC\uDDA5\u8EAC\uDDA6\u8EAC\uDDA7" + // 875 - 879 + "\u8EAC\uDDA8\u8EAC\uDDA9\u8EAC\uDDAA\u8EAC\uDDAB\u8EAC\uDDAC" + // 880 - 884 + "\u8EAC\uDDAD\u8EAC\uDDAE\u8EAC\uDDAF\u8EAC\uDDB0\u8EAC\uDDB1" + // 885 - 889 + "\u8EAC\uDDB2\u8EAC\uDDB3\u8EAC\uDDB4\u8EAC\uDDB5\u8EAC\uDDB6" + // 890 - 894 + "\u8EAC\uDDB7\u8EAC\uDDB8\u8EAC\uDDB9\u8EAC\uDDBA\u8EAC\uDDBB" + // 895 - 899 + "\u8EAC\uDDBC\u8EAC\uDDBD\u8EAC\uDDBE\u8EAC\uDDBF\u8EAC\uDDC0" + // 900 - 904 + "\u8EAC\uDDC1\u8EAC\uDDC2\u8EAC\uDDC3\u8EAC\uDDC4\u8EAC\uDDC5" + // 905 - 909 + "\u8EAC\uDDC6\u8EAC\uDDC7\u8EAC\uDDC8\u8EAC\uDDC9\u8EAC\uDDCA" + // 910 - 914 + "\u8EAC\uDDCB\u8EAC\uDDCC\u8EAC\uDDCD\u8EAC\uDDCE\u8EAC\uDDCF" + // 915 - 919 + "\u8EAC\uDDD0\u8EAC\uDDD1\u8EAC\uDDD2\u8EAC\uDDD3\u8EAC\uDDD4" + // 920 - 924 + "\u8EAC\uDDD5\u8EAC\uDDD6\u8EAC\uDDD7\u8EAC\uDDD8\u8EAC\uDCB7" + // 925 - 929 + "\u8EAC\uDCB8\u8EAC\uDCB9\u8EAC\uDCBA\u8EAC\uDCBB\u8EAC\uDCBC" + // 930 - 934 + "\u8EAC\uDCBD\u8EAC\uDCBE\u8EAC\uDCBF\u8EAC\uDCC0\u8EAC\uDCC1" + // 935 - 939 + "\u8EAC\uDCC2\u8EAC\uDCC3\u8EAC\uDCC4\u8EAC\uDCC5\u8EAC\uDCC6" + // 940 - 944 + "\u8EAC\uDCC7\u8EAC\uDCC8\u8EAC\uDCC9\u8EAC\uDCCA\u8EAC\uDCCB" + // 945 - 949 + "\u8EAC\uDCCC\u8EAC\uDCCD\u8EAC\uDCCE\u8EAC\uDCCF\u8EAC\uDCD0" + // 950 - 954 + "\u8EAC\uDCD1\u8EAC\uDCD2\u8EAC\uDCD3\u8EAC\uDCD4\u8EAC\uDCD5" + // 955 - 959 + "\u8EAC\uDCD6\u8EAC\uDCD7\u8EAC\uDCD8\u8EAC\uDCD9\u8EAC\uDCDA" + // 960 - 964 + "\u8EAC\uDCDB\u8EAC\uDCDC\u8EAC\uDCDD\u8EAC\uDCDE\u8EAC\uDCDF" + // 965 - 969 + "\u8EAC\uDCE0\u8EAC\uDCE1\u8EAC\uDCE2\u8EAC\uDCE3\u8EAC\uDCE4" + // 970 - 974 + "\u8EAC\uDCE5\u8EAC\uDCE6\u8EAC\uDCE7\u8EAC\uDCE8\u8EAC\uDCE9" + // 975 - 979 + "\u8EAC\uDCEA\u8EAC\uDCEB\u8EAC\uDCEC\u8EAC\uDCED\u8EAC\uDCEE" + // 980 - 984 + "\u8EAC\uDCEF\u8EAC\uDCF0\u8EAC\uDCF1\u8EAC\uDCF2\u8EAC\uDCF3" + // 985 - 989 + "\u8EAC\uDCF4\u8EAC\uDCF5\u8EAC\uDCF6\u8EAC\uDBD5\u8EAC\uDBD6" + // 990 - 994 + "\u8EAC\uDBD7\u8EAC\uDBD8\u8EAC\uDBD9\u8EAC\uDBDA\u8EAC\uDBDB" + // 995 - 999 + "\u8EAC\uDBDC\u8EAC\uDBDD\u8EAC\uDBDE\u8EAC\uDBDF\u8EAC\uDBE0" + // 1000 - 1004 + "\u8EAC\uDBE1\u8EAC\uDBE2\u8EAC\uDBE3\u8EAC\uDBE4\u8EAC\uDBE5" + // 1005 - 1009 + "\u8EAC\uDBE6\u8EAC\uDBE7\u8EAC\uDBE8\u8EAC\uDBE9\u8EAC\uDBEA" + // 1010 - 1014 + "\u8EAC\uDBEB\u8EAC\uDBEC\u8EAC\uDBED\u8EAC\uDBEE\u8EAC\uDBEF" + // 1015 - 1019 + "\u8EAC\uDBF0\u8EAC\uDBF1\u8EAC\uDBF2\u8EAC\uDBF3\u8EAC\uDBF4" + // 1020 - 1024 + "\u8EAC\uDBF5\u8EAC\uDBF6\u8EAC\uDBF7\u8EAC\uDBF8\u8EAC\uDBF9" + // 1025 - 1029 + "\u8EAC\uDBFA\u8EAC\uDBFB\u8EAC\uDBFC\u8EAC\uDBFD\u8EAC\uDBFE" + // 1030 - 1034 + "\u8EAC\uDCA1\u8EAC\uDCA2\u8EAC\uDCA3\u8EAC\uDCA4\u8EAC\uDCA5" + // 1035 - 1039 + "\u8EAC\uDCA6\u8EAC\uDCA7\u8EAC\uDCA8\u8EAC\uDCA9\u8EAC\uDCAA" + // 1040 - 1044 + "\u8EAC\uDCAB\u8EAC\uDCAC\u8EAC\uDCAD\u8EAC\uDCAE\u8EAC\uDCAF" + // 1045 - 1049 + "\u8EAC\uDCB0\u8EAC\uDCB1\u8EAC\uDCB2\u8EAC\uDCB3\u8EAC\uDCB4" + // 1050 - 1054 + "\u8EAC\uDCB5\u8EAC\uDCB6\u8EAC\uDAF3\u8EAC\uDAF4\u8EAC\uDAF5" + // 1055 - 1059 + "\u8EAC\uDAF6\u8EAC\uDAF7\u8EAC\uDAF8\u8EAC\uDAF9\u8EAC\uDAFA" + // 1060 - 1064 + "\u8EAC\uDAFB\u8EAC\uDAFC\u8EAC\uDAFD\u8EAC\uDAFE\u8EAC\uDBA1" + // 1065 - 1069 + "\u8EAC\uDBA2\u8EAC\uDBA3\u8EAC\uDBA4\u8EAC\uDBA5\u8EAC\uDBA6" + // 1070 - 1074 + "\u8EAC\uDBA7\u8EAC\uDBA8\u8EAC\uDBA9\u8EAC\uDBAA\u8EAC\uDBAB" + // 1075 - 1079 + "\u8EAC\uDBAC\u8EAC\uDBAD\u8EAC\uDBAE\u8EAC\uDBAF\u8EAC\uDBB0" + // 1080 - 1084 + "\u8EAC\uDBB1\u8EAC\uDBB2\u8EAC\uDBB3\u8EAC\uDBB4\u8EAC\uDBB5" + // 1085 - 1089 + "\u8EAC\uDBB6\u8EAC\uDBB7\u8EAC\uDBB8\u8EAC\uDBB9\u8EAC\uDBBA" + // 1090 - 1094 + "\u8EAC\uDBBB\u8EAC\uDBBC\u8EAC\uDBBD\u8EAC\uDBBE\u8EAC\uDBBF" + // 1095 - 1099 + "\u8EAC\uDBC0\u8EAC\uDBC1\u8EAC\uDBC2\u8EAC\uDBC3\u8EAC\uDBC4" + // 1100 - 1104 + "\u8EAC\uDBC5\u8EAC\uDBC6\u8EAC\uDBC7\u8EAC\uDBC8\u8EAC\uDBC9" + // 1105 - 1109 + "\u8EAC\uDBCA\u8EAC\uDBCB\u8EAC\uDBCC\u8EAC\uDBCD\u8EAC\uDBCE" + // 1110 - 1114 + "\u8EAC\uDBCF\u8EAC\uDBD0\u8EAC\uDBD1\u8EAC\uDBD2\u8EAC\uDBD3" + // 1115 - 1119 + "\u8EAC\uDBD4\u8EAC\uDAB3\u8EAC\uDAB4\u8EAC\uDAB5\u8EAC\uDAB6" + // 1120 - 1124 + "\u8EAC\uDAB7\u8EAC\uDAB8\u8EAC\uDAB9\u8EAC\uDABA\u8EAC\uDABB" + // 1125 - 1129 + "\u8EAC\uDABC\u8EAC\uDABD\u8EAC\uDABE\u8EAC\uDABF\u8EAC\uDAC0" + // 1130 - 1134 + "\u8EAC\uDAC1\u8EAC\uDAC2\u8EAC\uDAC3\u8EAC\uDAC4\u8EAC\uDAC5" + // 1135 - 1139 + "\u8EAC\uDAC6\u8EAC\uDAC7\u8EAC\uDAC8\u8EAC\uDAC9\u8EAC\uDACA" + // 1140 - 1144 + "\u8EAC\uDACB\u8EAC\uDACC\u8EAC\uDACD\u8EAC\uDACE\u8EAC\uDACF" + // 1145 - 1149 + "\u8EAC\uDAD0\u8EAC\uDAD1\u8EAC\uDAD2\u8EAC\uDAD3\u8EAC\uDAD4" + // 1150 - 1154 + "\u8EAC\uDAD5\u8EAC\uDAD6\u8EAC\uDAD7\u8EAC\uDAD8\u8EAC\uDAD9" + // 1155 - 1159 + "\u8EAC\uDADA\u8EAC\uDADB\u8EAC\uDADC\u8EAC\uDADD\u8EAC\uDADE" + // 1160 - 1164 + "\u8EAC\uDADF\u8EAC\uDAE0\u8EAC\uDAE1\u8EAC\uDAE2\u8EAC\uDAE3" + // 1165 - 1169 + "\u8EAC\uDAE4\u8EAC\uDAE5\u8EAC\uDAE6\u8EAC\uDAE7\u8EAC\uDAE8" + // 1170 - 1174 + "\u8EAC\uDAE9\u8EAC\uDAEA\u8EAC\uDAEB\u8EAC\uDAEC\u8EAC\uDAED" + // 1175 - 1179 + "\u8EAC\uDAEE\u8EAC\uDAEF\u8EAC\uDAF0\u8EAC\uDAF1\u8EAC\uDAF2" + // 1180 - 1184 + "\u8EAC\uD9D1\u8EAC\uD9D2\u8EAC\uD9D3\u8EAC\uD9D4\u8EAC\uD9D5" + // 1185 - 1189 + "\u8EAC\uD9D6\u8EAC\uD9D7\u8EAC\uD9D8\u8EAC\uD9D9\u8EAC\uD9DA" + // 1190 - 1194 + "\u8EAC\uD9DB\u8EAC\uD9DC\u8EAC\uD9DD\u8EAC\uD9DE\u8EAC\uD9DF" + // 1195 - 1199 + "\u8EAC\uD9E0\u8EAC\uD9E1\u8EAC\uD9E2\u8EAC\uD9E3\u8EAC\uD9E4" + // 1200 - 1204 + "\u8EAC\uD9E5\u8EAC\uD9E6\u8EAC\uD9E7\u8EAC\uD9E8\u8EAC\uD9E9" + // 1205 - 1209 + "\u8EAC\uD9EA\u8EAC\uD9EB\u8EAC\uD9EC\u8EAC\uD9ED\u8EAC\uD9EE" + // 1210 - 1214 + "\u8EAC\uD9EF\u8EAC\uD9F0\u8EAC\uD9F1\u8EAC\uD9F2\u8EAC\uD9F3" + // 1215 - 1219 + "\u8EAC\uD9F4\u8EAC\uD9F5\u8EAC\uD9F6\u8EAC\uD9F7\u8EAC\uD9F8" + // 1220 - 1224 + "\u8EAC\uD9F9\u8EAC\uD9FA\u8EAC\uD9FB\u8EAC\uD9FC\u8EAC\uD9FD" + // 1225 - 1229 + "\u8EAC\uD9FE\u8EAC\uDAA1\u8EAC\uDAA2\u8EAC\uDAA3\u8EAC\uDAA4" + // 1230 - 1234 + "\u8EAC\uDAA5\u8EAC\uDAA6\u8EAC\uDAA7\u8EAC\uDAA8\u8EAC\uDAA9" + // 1235 - 1239 + "\u8EAC\uDAAA\u8EAC\uDAAB\u8EAC\uDAAC\u8EAC\uDAAD\u8EAC\uDAAE" + // 1240 - 1244 + "\u8EAC\uDAAF\u8EAC\uDAB0\u8EAC\uDAB1\u8EAC\uDAB2\u8EAC\uD8EF" + // 1245 - 1249 + "\u8EAC\uD8F0\u8EAC\uD8F1\u8EAC\uD8F2\u8EAC\uD8F3\u8EAC\uD8F4" + // 1250 - 1254 + "\u8EAC\uD8F5\u8EAC\uD8F6\u8EAC\uD8F7\u8EAC\uD8F8\u8EAC\uD8F9" + // 1255 - 1259 + "\u8EAC\uD8FA\u8EAC\uD8FB\u8EAC\uD8FC\u8EAC\uD8FD\u8EAC\uD8FE" + // 1260 - 1264 + "\u8EAC\uD9A1\u8EAC\uD9A2\u8EAC\uD9A3\u8EAC\uD9A4\u8EAC\uD9A5" + // 1265 - 1269 + "\u8EAC\uD9A6\u8EAC\uD9A7\u8EAC\uD9A8\u8EAC\uD9A9\u8EAC\uD9AA" + // 1270 - 1274 + "\u8EAC\uD9AB\u8EAC\uD9AC\u8EAC\uD9AD\u8EAC\uD9AE\u8EAC\uD9AF" + // 1275 - 1279 + "\u8EAC\uD9B0\u8EAC\uD9B1\u8EAC\uD9B2\u8EAC\uD9B3\u8EAC\uD9B4" + // 1280 - 1284 + "\u8EAC\uD9B5\u8EAC\uD9B6\u8EAC\uD9B7\u8EAC\uD9B8\u8EAC\uD9B9" + // 1285 - 1289 + "\u8EAC\uD9BA\u8EAC\uD9BB\u8EAC\uD9BC\u8EAC\uD9BD\u8EAC\uD9BE" + // 1290 - 1294 + "\u8EAC\uD9BF\u8EAC\uD9C0\u8EAC\uD9C1\u8EAC\uD9C2\u8EAC\uD9C3" + // 1295 - 1299 + "\u8EAC\uD9C4\u8EAC\uD9C5\u8EAC\uD9C6\u8EAC\uD9C7\u8EAC\uD9C8" + // 1300 - 1304 + "\u8EAC\uD9C9\u8EAC\uD9CA\u8EAC\uD9CB\u8EAC\uD9CC\u8EAC\uD9CD" + // 1305 - 1309 + "\u8EAC\uD9CE\u8EAC\uD9CF\u8EAC\uD9D0\u8EAC\uD8AF\u8EAC\uD8B0" + // 1310 - 1314 + "\u8EAC\uD8B1\u8EAC\uD8B2\u8EAC\uD8B3\u8EAC\uD8B4\u8EAC\uD8B5" + // 1315 - 1319 + "\u8EAC\uD8B6\u8EAC\uD8B7\u8EAC\uD8B8\u8EAC\uD8B9\u8EAC\uD8BA" + // 1320 - 1324 + "\u8EAC\uD8BB\u8EAC\uD8BC\u8EAC\uD8BD\u8EAC\uD8BE\u8EAC\uD8BF" + // 1325 - 1329 + "\u8EAC\uD8C0\u8EAC\uD8C1\u8EAC\uD8C2\u8EAC\uD8C3\u8EAC\uD8C4" + // 1330 - 1334 + "\u8EAC\uD8C5\u8EAC\uD8C6\u8EAC\uD8C7\u8EAC\uD8C8\u8EAC\uD8C9" + // 1335 - 1339 + "\u8EAC\uD8CA\u8EAC\uD8CB\u8EAC\uD8CC\u8EAC\uD8CD\u8EAC\uD8CE" + // 1340 - 1344 + "\u8EAC\uD8CF\u8EAC\uD8D0\u8EAC\uD8D1\u8EAC\uD8D2\u8EAC\uD8D3" + // 1345 - 1349 + "\u8EAC\uD8D4\u8EAC\uD8D5\u8EAC\uD8D6\u8EAC\uD8D7\u8EAC\uD8D8" + // 1350 - 1354 + "\u8EAC\uD8D9\u8EAC\uD8DA\u8EAC\uD8DB\u8EAC\uD8DC\u8EAC\uD8DD" + // 1355 - 1359 + "\u8EAC\uD8DE\u8EAC\uD8DF\u8EAC\uD8E0\u8EAC\uD8E1\u8EAC\uD8E2" + // 1360 - 1364 + "\u8EAC\uD8E3\u8EAC\uD8E4\u8EAC\uD8E5\u8EAC\uD8E6\u8EAC\uD8E7" + // 1365 - 1369 + "\u8EAC\uD8E8\u8EAC\uD8E9\u8EAC\uD8EA\u8EAC\uD8EB\u8EAC\uD8EC" + // 1370 - 1374 + "\u8EAC\uD8ED\u8EAC\uD8EE\u8EAC\uD7CD\u8EAC\uD7CE\u8EAC\uD7CF" + // 1375 - 1379 + "\u8EAC\uD7D0\u8EAC\uD7D1\u8EAC\uD7D2\u8EAC\uD7D3\u8EAC\uD7D4" + // 1380 - 1384 + "\u8EAC\uD7D5\u8EAC\uD7D6\u8EAC\uD7D7\u8EAC\uD7D8\u8EAC\uD7D9" + // 1385 - 1389 + "\u8EAC\uD7DA\u8EAC\uD7DB\u8EAC\uD7DC\u8EAC\uD7DD\u8EAC\uD7DE" + // 1390 - 1394 + "\u8EAC\uD7DF\u8EAC\uD7E0\u8EAC\uD7E1\u8EAC\uD7E2\u8EAC\uD7E3" + // 1395 - 1399 + "\u8EAC\uD7E4\u8EAC\uD7E5\u8EAC\uD7E6\u8EAC\uD7E7\u8EAC\uD7E8" + // 1400 - 1404 + "\u8EAC\uD7E9\u8EAC\uD7EA\u8EAC\uD7EB\u8EAC\uD7EC\u8EAC\uD7ED" + // 1405 - 1409 + "\u8EAC\uD7EE\u8EAC\uD7EF\u8EAC\uD7F0\u8EAC\uD7F1\u8EAC\uD7F2" + // 1410 - 1414 + "\u8EAC\uD7F3\u8EAC\uD7F4\u8EAC\uD7F5\u8EAC\uD7F6\u8EAC\uD7F7" + // 1415 - 1419 + "\u8EAC\uD7F8\u8EAC\uD7F9\u8EAC\uD7FA\u8EAC\uD7FB\u8EAC\uD7FC" + // 1420 - 1424 + "\u8EAC\uD7FD\u8EAC\uD7FE\u8EAC\uD8A1\u8EAC\uD8A2\u8EAC\uD8A3" + // 1425 - 1429 + "\u8EAC\uD8A4\u8EAC\uD8A5\u8EAC\uD8A6\u8EAC\uD8A7\u8EAC\uD8A8" + // 1430 - 1434 + "\u8EAC\uD8A9\u8EAC\uD8AA\u8EAC\uD8AB\u8EAC\uD8AC\u8EAC\uD8AD" + // 1435 - 1439 + "\u8EAC\uD8AE\u8EAC\uD6EB\u8EAC\uD6EC\u8EAC\uD6ED\u8EAC\uD6EE" + // 1440 - 1444 + "\u8EAC\uD6EF\u8EAC\uD6F0\u8EAC\uD6F1\u8EAC\uD6F2\u8EAC\uD6F3" + // 1445 - 1449 + "\u8EAC\uD6F4\u8EAC\uD6F5\u8EAC\uD6F6\u8EAC\uD6F7\u8EAC\uD6F8" + // 1450 - 1454 + "\u8EAC\uD6F9\u8EAC\uD6FA\u8EAC\uD6FB\u8EAC\uD6FC\u8EAC\uD6FD" + // 1455 - 1459 + "\u8EAC\uD6FE\u8EAC\uD7A1\u8EAC\uD7A2\u8EAC\uD7A3\u8EAC\uD7A4" + // 1460 - 1464 + "\u8EAC\uD7A5\u8EAC\uD7A6\u8EAC\uD7A7\u8EAC\uD7A8\u8EAC\uD7A9" + // 1465 - 1469 + "\u8EAC\uD7AA\u8EAC\uD7AB\u8EAC\uD7AC\u8EAC\uD7AD\u8EAC\uD7AE" + // 1470 - 1474 + "\u8EAC\uD7AF\u8EAC\uD7B0\u8EAC\uD7B1\u8EAC\uD7B2\u8EAC\uD7B3" + // 1475 - 1479 + "\u8EAC\uD7B4\u8EAC\uD7B5\u8EAC\uD7B6\u8EAC\uD7B7\u8EAC\uD7B8" + // 1480 - 1484 + "\u8EAC\uD7B9\u8EAC\uD7BA\u8EAC\uD7BB\u8EAC\uD7BC\u8EAC\uD7BD" + // 1485 - 1489 + "\u8EAC\uD7BE\u8EAC\uD7BF\u8EAC\uD7C0\u8EAC\uD7C1\u8EAC\uD7C2" + // 1490 - 1494 + "\u8EAC\uD7C3\u8EAC\uD7C4\u8EAC\uD7C5\u8EAC\uD7C6\u8EAC\uD7C7" + // 1495 - 1499 + "\u8EAC\uD7C8\u8EAC\uD7C9\u8EAC\uD7CA\u8EAC\uD7CB\u8EAC\uD7CC" + // 1500 - 1504 + "\u8EAC\uD6AB\u8EAC\uD6AC\u8EAC\uD6AD\u8EAC\uD6AE\u8EAC\uD6AF" + // 1505 - 1509 + "\u8EAC\uD6B0\u8EAC\uD6B1\u8EAC\uD6B2\u8EAC\uD6B3\u8EAC\uD6B4" + // 1510 - 1514 + "\u8EAC\uD6B5\u8EAC\uD6B6\u8EAC\uD6B7\u8EAC\uD6B8\u8EAC\uD6B9" + // 1515 - 1519 + "\u8EAC\uD6BA\u8EAC\uD6BB\u8EAC\uD6BC\u8EAC\uD6BD\u8EAC\uD6BE" + // 1520 - 1524 + "\u8EAC\uD6BF\u8EAC\uD6C0\u8EAC\uD6C1\u8EAC\uD6C2\u8EAC\uD6C3" + // 1525 - 1529 + "\u8EAC\uD6C4\u8EAC\uD6C5\u8EAC\uD6C6\u8EAC\uD6C7\u8EAC\uD6C8" + // 1530 - 1534 + "\u8EAC\uD6C9\u8EAC\uD6CA\u8EAC\uD6CB\u8EAC\uD6CC\u8EAC\uD6CD" + // 1535 - 1539 + "\u8EAC\uD6CE\u8EAC\uD6CF\u8EAC\uD6D0\u8EAC\uD6D1\u8EAC\uD6D2" + // 1540 - 1544 + "\u8EAC\uD6D3\u8EAC\uD6D4\u8EAC\uD6D5\u8EAC\uD6D6\u8EAC\uD6D7" + // 1545 - 1549 + "\u8EAC\uD6D8\u8EAC\uD6D9\u8EAC\uD6DA\u8EAC\uD6DB\u8EAC\uD6DC" + // 1550 - 1554 + "\u8EAC\uD6DD\u8EAC\uD6DE\u8EAC\uD6DF\u8EAC\uD6E0\u8EAC\uD6E1" + // 1555 - 1559 + "\u8EAC\uD6E2\u8EAC\uD6E3\u8EAC\uD6E4\u8EAC\uD6E5\u8EAC\uD6E6" + // 1560 - 1564 + "\u8EAC\uD6E7\u8EAC\uD6E8\u8EAC\uD6E9\u8EAC\uD6EA\u8EAC\uD5C9" + // 1565 - 1569 + "\u8EAC\uD5CA\u8EAC\uD5CB\u8EAC\uD5CC\u8EAC\uD5CD\u8EAC\uD5CE" + // 1570 - 1574 + "\u8EAC\uD5CF\u8EAC\uD5D0\u8EAC\uD5D1\u8EAC\uD5D2\u8EAC\uD5D3" + // 1575 - 1579 + "\u8EAC\uD5D4\u8EAC\uD5D5\u8EAC\uD5D6\u8EAC\uD5D7\u8EAC\uD5D8" + // 1580 - 1584 + "\u8EAC\uD5D9\u8EAC\uD5DA\u8EAC\uD5DB\u8EAC\uD5DC\u8EAC\uD5DD" + // 1585 - 1589 + "\u8EAC\uD5DE\u8EAC\uD5DF\u8EAC\uD5E0\u8EAC\uD5E1\u8EAC\uD5E2" + // 1590 - 1594 + "\u8EAC\uD5E3\u8EAC\uD5E4\u8EAC\uD5E5\u8EAC\uD5E6\u8EAC\uD5E7" + // 1595 - 1599 + "\u8EAC\uD5E8\u8EAC\uD5E9\u8EAC\uD5EA\u8EAC\uD5EB\u8EAC\uD5EC" + // 1600 - 1604 + "\u8EAC\uD5ED\u8EAC\uD5EE\u8EAC\uD5EF\u8EAC\uD5F0\u8EAC\uD5F1" + // 1605 - 1609 + "\u8EAC\uD5F2\u8EAC\uD5F3\u8EAC\uD5F4\u8EAC\uD5F5\u8EAC\uD5F6" + // 1610 - 1614 + "\u8EAC\uD5F7\u8EAC\uD5F8\u8EAC\uD5F9\u8EAC\uD5FA\u8EAC\uD5FB" + // 1615 - 1619 + "\u8EAC\uD5FC\u8EAC\uD5FD\u8EAC\uD5FE\u8EAC\uD6A1\u8EAC\uD6A2" + // 1620 - 1624 + "\u8EAC\uD6A3\u8EAC\uD6A4\u8EAC\uD6A5\u8EAC\uD6A6\u8EAC\uD6A7" + // 1625 - 1629 + "\u8EAC\uD6A8\u8EAC\uD6A9\u8EAC\uD6AA\u8EAC\uD4E7\u8EAC\uD4E8" + // 1630 - 1634 + "\u8EAC\uD4E9\u8EAC\uD4EA\u8EAC\uD4EB\u8EAC\uD4EC\u8EAC\uD4ED" + // 1635 - 1639 + "\u8EAC\uD4EE\u8EAC\uD4EF\u8EAC\uD4F0\u8EAC\uD4F1\u8EAC\uD4F2" + // 1640 - 1644 + "\u8EAC\uD4F3\u8EAC\uD4F4\u8EAC\uD4F5\u8EAC\uD4F6\u8EAC\uD4F7" + // 1645 - 1649 + "\u8EAC\uD4F8\u8EAC\uD4F9\u8EAC\uD4FA\u8EAC\uD4FB\u8EAC\uD4FC" + // 1650 - 1654 + "\u8EAC\uD4FD\u8EAC\uD4FE\u8EAC\uD5A1\u8EAC\uD5A2\u8EAC\uD5A3" + // 1655 - 1659 + "\u8EAC\uD5A4\u8EAC\uD5A5\u8EAC\uD5A6\u8EAC\uD5A7\u8EAC\uD5A8" + // 1660 - 1664 + "\u8EAC\uD5A9\u8EAC\uD5AA\u8EAC\uD5AB\u8EAC\uD5AC\u8EAC\uD5AD" + // 1665 - 1669 + "\u8EAC\uD5AE\u8EAC\uD5AF\u8EAC\uD5B0\u8EAC\uD5B1\u8EAC\uD5B2" + // 1670 - 1674 + "\u8EAC\uD5B3\u8EAC\uD5B4\u8EAC\uD5B5\u8EAC\uD5B6\u8EAC\uD5B7" + // 1675 - 1679 + "\u8EAC\uD5B8\u8EAC\uD5B9\u8EAC\uD5BA\u8EAC\uD5BB\u8EAC\uD5BC" + // 1680 - 1684 + "\u8EAC\uD5BD\u8EAC\uD5BE\u8EAC\uD5BF\u8EAC\uD5C0\u8EAC\uD5C1" + // 1685 - 1689 + "\u8EAC\uD5C2\u8EAC\uD5C3\u8EAC\uD5C4\u8EAC\uD5C5\u8EAC\uD5C6" + // 1690 - 1694 + "\u8EAC\uD5C7\u8EAC\uD5C8\u8EAC\uD4A7\u8EAC\uD4A8\u8EAC\uD4A9" + // 1695 - 1699 + "\u8EAC\uD4AA\u8EAC\uD4AB\u8EAC\uD4AC\u8EAC\uD4AD\u8EAC\uD4AE" + // 1700 - 1704 + "\u8EAC\uD4AF\u8EAC\uD4B0\u8EAC\uD4B1\u8EAC\uD4B2\u8EAC\uD4B3" + // 1705 - 1709 + "\u8EAC\uD4B4\u8EAC\uD4B5\u8EAC\uD4B6\u8EAC\uD4B7\u8EAC\uD4B8" + // 1710 - 1714 + "\u8EAC\uD4B9\u8EAC\uD4BA\u8EAC\uD4BB\u8EAC\uD4BC\u8EAC\uD4BD" + // 1715 - 1719 + "\u8EAC\uD4BE\u8EAC\uD4BF\u8EAC\uD4C0\u8EAC\uD4C1\u8EAC\uD4C2" + // 1720 - 1724 + "\u8EAC\uD4C3\u8EAC\uD4C4\u8EAC\uD4C5\u8EAC\uD4C6\u8EAC\uD4C7" + // 1725 - 1729 + "\u8EAC\uD4C8\u8EAC\uD4C9\u8EAC\uD4CA\u8EAC\uD4CB\u8EAC\uD4CC" + // 1730 - 1734 + "\u8EAC\uD4CD\u8EAC\uD4CE\u8EAC\uD4CF\u8EAC\uD4D0\u8EAC\uD4D1" + // 1735 - 1739 + "\u8EAC\uD4D2\u8EAC\uD4D3\u8EAC\uD4D4\u8EAC\uD4D5\u8EAC\uD4D6" + // 1740 - 1744 + "\u8EAC\uD4D7\u8EAC\uD4D8\u8EAC\uD4D9\u8EAC\uD4DA\u8EAC\uD4DB" + // 1745 - 1749 + "\u8EAC\uD4DC\u8EAC\uD4DD\u8EAC\uD4DE\u8EAC\uD4DF\u8EAC\uD4E0" + // 1750 - 1754 + "\u8EAC\uD4E1\u8EAC\uD4E2\u8EAC\uD4E3\u8EAC\uD4E4\u8EAC\uD4E5" + // 1755 - 1759 + "\u8EAC\uD4E6\u8EAC\uD3C5\u8EAC\uD3C6\u8EAC\uD3C7\u8EAC\uD3C8" + // 1760 - 1764 + "\u8EAC\uD3C9\u8EAC\uD3CA\u8EAC\uD3CB\u8EAC\uD3CC\u8EAC\uD3CD" + // 1765 - 1769 + "\u8EAC\uD3CE\u8EAC\uD3CF\u8EAC\uD3D0\u8EAC\uD3D1\u8EAC\uD3D2" + // 1770 - 1774 + "\u8EAC\uD3D3\u8EAC\uD3D4\u8EAC\uD3D5\u8EAC\uD3D6\u8EAC\uD3D7" + // 1775 - 1779 + "\u8EAC\uD3D8\u8EAC\uD3D9\u8EAC\uD3DA\u8EAC\uD3DB\u8EAC\uD3DC" + // 1780 - 1784 + "\u8EAC\uD3DD\u8EAC\uD3DE\u8EAC\uD3DF\u8EAC\uD3E0\u8EAC\uD3E1" + // 1785 - 1789 + "\u8EAC\uD3E2\u8EAC\uD3E3\u8EAC\uD3E4\u8EAC\uD3E5\u8EAC\uD3E6" + // 1790 - 1794 + "\u8EAC\uD3E7\u8EAC\uD3E8\u8EAC\uD3E9\u8EAC\uD3EA\u8EAC\uD3EB" + // 1795 - 1799 + "\u8EAC\uD3EC\u8EAC\uD3ED\u8EAC\uD3EE\u8EAC\uD3EF\u8EAC\uD3F0" + // 1800 - 1804 + "\u8EAC\uD3F1\u8EAC\uD3F2\u8EAC\uD3F3\u8EAC\uD3F4\u8EAC\uD3F5" + // 1805 - 1809 + "\u8EAC\uD3F6\u8EAC\uD3F7\u8EAC\uD3F8\u8EAC\uD3F9\u8EAC\uD3FA" + // 1810 - 1814 + "\u8EAC\uD3FB\u8EAC\uD3FC\u8EAC\uD3FD\u8EAC\uD3FE\u8EAC\uD4A1" + // 1815 - 1819 + "\u8EAC\uD4A2\u8EAC\uD4A3\u8EAC\uD4A4\u8EAC\uD4A5\u8EAC\uD4A6" + // 1820 - 1824 + "\u8EAC\uD2E3\u8EAC\uD2E4\u8EAC\uD2E5\u8EAC\uD2E6\u8EAC\uD2E7" + // 1825 - 1829 + "\u8EAC\uD2E8\u8EAC\uD2E9\u8EAC\uD2EA\u8EAC\uD2EB\u8EAC\uD2EC" + // 1830 - 1834 + "\u8EAC\uD2ED\u8EAC\uD2EE\u8EAC\uD2EF\u8EAC\uD2F0\u8EAC\uD2F1" + // 1835 - 1839 + "\u8EAC\uD2F2\u8EAC\uD2F3\u8EAC\uD2F4\u8EAC\uD2F5\u8EAC\uD2F6" + // 1840 - 1844 + "\u8EAC\uD2F7\u8EAC\uD2F8\u8EAC\uD2F9\u8EAC\uD2FA\u8EAC\uD2FB" + // 1845 - 1849 + "\u8EAC\uD2FC\u8EAC\uD2FD\u8EAC\uD2FE\u8EAC\uD3A1\u8EAC\uD3A2" + // 1850 - 1854 + "\u8EAC\uD3A3\u8EAC\uD3A4\u8EAC\uD3A5\u8EAC\uD3A6\u8EAC\uD3A7" + // 1855 - 1859 + "\u8EAC\uD3A8\u8EAC\uD3A9\u8EAC\uD3AA\u8EAC\uD3AB\u8EAC\uD3AC" + // 1860 - 1864 + "\u8EAC\uD3AD\u8EAC\uD3AE\u8EAC\uD3AF\u8EAC\uD3B0\u8EAC\uD3B1" + // 1865 - 1869 + "\u8EAC\uD3B2\u8EAC\uD3B3\u8EAC\uD3B4\u8EAC\uD3B5\u8EAC\uD3B6" + // 1870 - 1874 + "\u8EAC\uD3B7\u8EAC\uD3B8\u8EAC\uD3B9\u8EAC\uD3BA\u8EAC\uD3BB" + // 1875 - 1879 + "\u8EAC\uD3BC\u8EAC\uD3BD\u8EAC\uD3BE\u8EAC\uD3BF\u8EAC\uD3C0" + // 1880 - 1884 + "\u8EAC\uD3C1\u8EAC\uD3C2\u8EAC\uD3C3\u8EAC\uD3C4\u8EAC\uD2A3" + // 1885 - 1889 + "\u8EAC\uD2A4\u8EAC\uD2A5\u8EAC\uD2A6\u8EAC\uD2A7\u8EAC\uD2A8" + // 1890 - 1894 + "\u8EAC\uD2A9\u8EAC\uD2AA\u8EAC\uD2AB\u8EAC\uD2AC\u8EAC\uD2AD" + // 1895 - 1899 + "\u8EAC\uD2AE\u8EAC\uD2AF\u8EAC\uD2B0\u8EAC\uD2B1\u8EAC\uD2B2" + // 1900 - 1904 + "\u8EAC\uD2B3\u8EAC\uD2B4\u8EAC\uD2B5\u8EAC\uD2B6\u8EAC\uD2B7" + // 1905 - 1909 + "\u8EAC\uD2B8\u8EAC\uD2B9\u8EAC\uD2BA\u8EAC\uD2BB\u8EAC\uD2BC" + // 1910 - 1914 + "\u8EAC\uD2BD\u8EAC\uD2BE\u8EAC\uD2BF\u8EAC\uD2C0\u8EAC\uD2C1" + // 1915 - 1919 + "\u8EAC\uD2C2\u8EAC\uD2C3\u8EAC\uD2C4\u8EAC\uD2C5\u8EAC\uD2C6" + // 1920 - 1924 + "\u8EAC\uD2C7\u8EAC\uD2C8\u8EAC\uD2C9\u8EAC\uD2CA\u8EAC\uD2CB" + // 1925 - 1929 + "\u8EAC\uD2CC\u8EAC\uD2CD\u8EAC\uD2CE\u8EAC\uD2CF\u8EAC\uD2D0" + // 1930 - 1934 + "\u8EAC\uD2D1\u8EAC\uD2D2\u8EAC\uD2D3\u8EAC\uD2D4\u8EAC\uD2D5" + // 1935 - 1939 + "\u8EAC\uD2D6\u8EAC\uD2D7\u8EAC\uD2D8\u8EAC\uD2D9\u8EAC\uD2DA" + // 1940 - 1944 + "\u8EAC\uD2DB\u8EAC\uD2DC\u8EAC\uD2DD\u8EAC\uD2DE\u8EAC\uD2DF" + // 1945 - 1949 + "\u8EAC\uD2E0\u8EAC\uD2E1\u8EAC\uD2E2\u8EAC\uD1C1\u8EAC\uD1C2" + // 1950 - 1954 + "\u8EAC\uD1C3\u8EAC\uD1C4\u8EAC\uD1C5\u8EAC\uD1C6\u8EAC\uD1C7" + // 1955 - 1959 + "\u8EAC\uD1C8\u8EAC\uD1C9\u8EAC\uD1CA\u8EAC\uD1CB\u8EAC\uD1CC" + // 1960 - 1964 + "\u8EAC\uD1CD\u8EAC\uD1CE\u8EAC\uD1CF\u8EAC\uD1D0\u8EAC\uD1D1" + // 1965 - 1969 + "\u8EAC\uD1D2\u8EAC\uD1D3\u8EAC\uD1D4\u8EAC\uD1D5\u8EAC\uD1D6" + // 1970 - 1974 + "\u8EAC\uD1D7\u8EAC\uD1D8\u8EAC\uD1D9\u8EAC\uD1DA\u8EAC\uD1DB" + // 1975 - 1979 + "\u8EAC\uD1DC\u8EAC\uD1DD\u8EAC\uD1DE\u8EAC\uD1DF\u8EAC\uD1E0" + // 1980 - 1984 + "\u8EAC\uD1E1\u8EAC\uD1E2\u8EAC\uD1E3\u8EAC\uD1E4\u8EAC\uD1E5" + // 1985 - 1989 + "\u8EAC\uD1E6\u8EAC\uD1E7\u8EAC\uD1E8\u8EAC\uD1E9\u8EAC\uD1EA" + // 1990 - 1994 + "\u8EAC\uD1EB\u8EAC\uD1EC\u8EAC\uD1ED\u8EAC\uD1EE\u8EAC\uD1EF" + // 1995 - 1999 + "\u8EAC\uD1F0\u8EAC\uD1F1\u8EAC\uD1F2\u8EAC\uD1F3\u8EAC\uD1F4" + // 2000 - 2004 + "\u8EAC\uD1F5\u8EAC\uD1F6\u8EAC\uD1F7\u8EAC\uD1F8\u8EAC\uD1F9" + // 2005 - 2009 + "\u8EAC\uD1FA\u8EAC\uD1FB\u8EAC\uD1FC\u8EAC\uD1FD\u8EAC\uD1FE" + // 2010 - 2014 + "\u8EAC\uD2A1\u8EAC\uD2A2\u8EAC\uD0DF\u8EAC\uD0E0\u8EAC\uD0E1" + // 2015 - 2019 + "\u8EAC\uD0E2\u8EAC\uD0E3\u8EAC\uD0E4\u8EAC\uD0E5\u8EAC\uD0E6" + // 2020 - 2024 + "\u8EAC\uD0E7\u8EAC\uD0E8\u8EAC\uD0E9\u8EAC\uD0EA\u8EAC\uD0EB" + // 2025 - 2029 + "\u8EAC\uD0EC\u8EAC\uD0ED\u8EAC\uD0EE\u8EAC\uD0EF\u8EAC\uD0F0" + // 2030 - 2034 + "\u8EAC\uD0F1\u8EAC\uD0F2\u8EAC\uD0F3\u8EAC\uD0F4\u8EAC\uD0F5" + // 2035 - 2039 + "\u8EAC\uD0F6\u8EAC\uD0F7\u8EAC\uD0F8\u8EAC\uD0F9\u8EAC\uD0FA" + // 2040 - 2044 + "\u8EAC\uD0FB\u8EAC\uD0FC\u8EAC\uD0FD\u8EAC\uD0FE\u8EAC\uD1A1" + // 2045 - 2049 + "\u8EAC\uD1A2\u8EAC\uD1A3\u8EAC\uD1A4\u8EAC\uD1A5\u8EAC\uD1A6" + // 2050 - 2054 + "\u8EAC\uD1A7\u8EAC\uD1A8\u8EAC\uD1A9\u8EAC\uD1AA\u8EAC\uD1AB" + // 2055 - 2059 + "\u8EAC\uD1AC\u8EAC\uD1AD\u8EAC\uD1AE\u8EAC\uD1AF\u8EAC\uD1B0" + // 2060 - 2064 + "\u8EAC\uD1B1\u8EAC\uD1B2\u8EAC\uD1B3\u8EAC\uD1B4\u8EAC\uD1B5" + // 2065 - 2069 + "\u8EAC\uD1B6\u8EAC\uD1B7\u8EAC\uD1B8\u8EAC\uD1B9\u8EAC\uD1BA" + // 2070 - 2074 + "\u8EAC\uD1BB\u8EAC\uD1BC\u8EAC\uD1BD\u8EAC\uD1BE\u8EAC\uD1BF" + // 2075 - 2079 + "\u8EAC\uD1C0\u8EAC\uCFFD\u8EAC\uCFFE\u8EAC\uD0A1\u8EAC\uD0A2" + // 2080 - 2084 + "\u8EAC\uD0A3\u8EAC\uD0A4\u8EAC\uD0A5\u8EAC\uD0A6\u8EAC\uD0A7" + // 2085 - 2089 + "\u8EAC\uD0A8\u8EAC\uD0A9\u8EAC\uD0AA\u8EAC\uD0AB\u8EAC\uD0AC" + // 2090 - 2094 + "\u8EAC\uD0AD\u8EAC\uD0AE\u8EAC\uD0AF\u8EAC\uD0B0\u8EAC\uD0B1" + // 2095 - 2099 + "\u8EAC\uD0B2\u8EAC\uD0B3\u8EAC\uD0B4\u8EAC\uD0B5\u8EAC\uD0B6" + // 2100 - 2104 + "\u8EAC\uD0B7\u8EAC\uD0B8\u8EAC\uD0B9\u8EAC\uD0BA\u8EAC\uD0BB" + // 2105 - 2109 + "\u8EAC\uD0BC\u8EAC\uD0BD\u8EAC\uD0BE\u8EAC\uD0BF\u8EAC\uD0C0" + // 2110 - 2114 + "\u8EAC\uD0C1\u8EAC\uD0C2\u8EAC\uD0C3\u8EAC\uD0C4\u8EAC\uD0C5" + // 2115 - 2119 + "\u8EAC\uD0C6\u8EAC\uD0C7\u8EAC\uD0C8\u8EAC\uD0C9\u8EAC\uD0CA" + // 2120 - 2124 + "\u8EAC\uD0CB\u8EAC\uD0CC\u8EAC\uD0CD\u8EAC\uD0CE\u8EAC\uD0CF" + // 2125 - 2129 + "\u8EAC\uD0D0\u8EAC\uD0D1\u8EAC\uD0D2\u8EAC\uD0D3\u8EAC\uD0D4" + // 2130 - 2134 + "\u8EAC\uD0D5\u8EAC\uD0D6\u8EAC\uD0D7\u8EAC\uD0D8\u8EAC\uD0D9" + // 2135 - 2139 + "\u8EAC\uD0DA\u8EAC\uD0DB\u8EAC\uD0DC\u8EAC\uD0DD\u8EAC\uD0DE" + // 2140 - 2144 + "\u8EAC\uCFBD\u8EAC\uCFBE\u8EAC\uCFBF\u8EAC\uCFC0\u8EAC\uCFC1" + // 2145 - 2149 + "\u8EAC\uCFC2\u8EAC\uCFC3\u8EAC\uCFC4\u8EAC\uCFC5\u8EAC\uCFC6" + // 2150 - 2154 + "\u8EAC\uCFC7\u8EAC\uCFC8\u8EAC\uCFC9\u8EAC\uCFCA\u8EAC\uCFCB" + // 2155 - 2159 + "\u8EAC\uCFCC\u8EAC\uCFCD\u8EAC\uCFCE\u8EAC\uCFCF\u8EAC\uCFD0" + // 2160 - 2164 + "\u8EAC\uCFD1\u8EAC\uCFD2\u8EAC\uCFD3\u8EAC\uCFD4\u8EAC\uCFD5" + // 2165 - 2169 + "\u8EAC\uCFD6\u8EAC\uCFD7\u8EAC\uCFD8\u8EAC\uCFD9\u8EAC\uCFDA" + // 2170 - 2174 + "\u8EAC\uCFDB\u8EAC\uCFDC\u8EAC\uCFDD\u8EAC\uCFDE\u8EAC\uCFDF" + // 2175 - 2179 + "\u8EAC\uCFE0\u8EAC\uCFE1\u8EAC\uCFE2\u8EAC\uCFE3\u8EAC\uCFE4" + // 2180 - 2184 + "\u8EAC\uCFE5\u8EAC\uCFE6\u8EAC\uCFE7\u8EAC\uCFE8\u8EAC\uCFE9" + // 2185 - 2189 + "\u8EAC\uCFEA\u8EAC\uCFEB\u8EAC\uCFEC\u8EAC\uCFED\u8EAC\uCFEE" + // 2190 - 2194 + "\u8EAC\uCFEF\u8EAC\uCFF0\u8EAC\uCFF1\u8EAC\uCFF2\u8EAC\uCFF3" + // 2195 - 2199 + "\u8EAC\uCFF4\u8EAC\uCFF5\u8EAC\uCFF6\u8EAC\uCFF7\u8EAC\uCFF8" + // 2200 - 2204 + "\u8EAC\uCFF9\u8EAC\uCFFA\u8EAC\uCFFB\u8EAC\uCFFC\u8EAC\uCEDB" + // 2205 - 2209 + "\u8EAC\uCEDC\u8EAC\uCEDD\u8EAC\uCEDE\u8EAC\uCEDF\u8EAC\uCEE0" + // 2210 - 2214 + "\u8EAC\uCEE1\u8EAC\uCEE2\u8EAC\uCEE3\u8EAC\uCEE4\u8EAC\uCEE5" + // 2215 - 2219 + "\u8EAC\uCEE6\u8EAC\uCEE7\u8EAC\uCEE8\u8EAC\uCEE9\u8EAC\uCEEA" + // 2220 - 2224 + "\u8EAC\uCEEB\u8EAC\uCEEC\u8EAC\uCEED\u8EAC\uCEEE\u8EAC\uCEEF" + // 2225 - 2229 + "\u8EAC\uCEF0\u8EAC\uCEF1\u8EAC\uCEF2\u8EAC\uCEF3\u8EAC\uCEF4" + // 2230 - 2234 + "\u8EAC\uCEF5\u8EAC\uCEF6\u8EAC\uCEF7\u8EAC\uCEF8\u8EAC\uCEF9" + // 2235 - 2239 + "\u8EAC\uCEFA\u8EAC\uCEFB\u8EAC\uCEFC\u8EAC\uCEFD\u8EAC\uCEFE" + // 2240 - 2244 + "\u8EAC\uCFA1\u8EAC\uCFA2\u8EAC\uCFA3\u8EAC\uCFA4\u8EAC\uCFA5" + // 2245 - 2249 + "\u8EAC\uCFA6\u8EAC\uCFA7\u8EAC\uCFA8\u8EAC\uCFA9\u8EAC\uCFAA" + // 2250 - 2254 + "\u8EAC\uCFAB\u8EAC\uCFAC\u8EAC\uCFAD\u8EAC\uCFAE\u8EAC\uCFAF" + // 2255 - 2259 + "\u8EAC\uCFB0\u8EAC\uCFB1\u8EAC\uCFB2\u8EAC\uCFB3\u8EAC\uCFB4" + // 2260 - 2264 + "\u8EAC\uCFB5\u8EAC\uCFB6\u8EAC\uCFB7\u8EAC\uCFB8\u8EAC\uCFB9" + // 2265 - 2269 + "\u8EAC\uCFBA\u8EAC\uCFBB\u8EAC\uCFBC\u8EAC\uCDF9\u8EAC\uCDFA" + // 2270 - 2274 + "\u8EAC\uCDFB\u8EAC\uCDFC\u8EAC\uCDFD\u8EAC\uCDFE\u8EAC\uCEA1" + // 2275 - 2279 + "\u8EAC\uCEA2\u8EAC\uCEA3\u8EAC\uCEA4\u8EAC\uCEA5\u8EAC\uCEA6" + // 2280 - 2284 + "\u8EAC\uCEA7\u8EAC\uCEA8\u8EAC\uCEA9\u8EAC\uCEAA\u8EAC\uCEAB" + // 2285 - 2289 + "\u8EAC\uCEAC\u8EAC\uCEAD\u8EAC\uCEAE\u8EAC\uCEAF\u8EAC\uCEB0" + // 2290 - 2294 + "\u8EAC\uCEB1\u8EAC\uCEB2\u8EAC\uCEB3\u8EAC\uCEB4\u8EAC\uCEB5" + // 2295 - 2299 + "\u8EAC\uCEB6\u8EAC\uCEB7\u8EAC\uCEB8\u8EAC\uCEB9\u8EAC\uCEBA" + // 2300 - 2304 + "\u8EAC\uCEBB\u8EAC\uCEBC\u8EAC\uCEBD\u8EAC\uCEBE\u8EAC\uCEBF" + // 2305 - 2309 + "\u8EAC\uCEC0\u8EAC\uCEC1\u8EAC\uCEC2\u8EAC\uCEC3\u8EAC\uCEC4" + // 2310 - 2314 + "\u8EAC\uCEC5\u8EAC\uCEC6\u8EAC\uCEC7\u8EAC\uCEC8\u8EAC\uCEC9" + // 2315 - 2319 + "\u8EAC\uCECA\u8EAC\uCECB\u8EAC\uCECC\u8EAC\uCECD\u8EAC\uCECE" + // 2320 - 2324 + "\u8EAC\uCECF\u8EAC\uCED0\u8EAC\uCED1\u8EAC\uCED2\u8EAC\uCED3" + // 2325 - 2329 + "\u8EAC\uCED4\u8EAC\uCED5\u8EAC\uCED6\u8EAC\uCED7\u8EAC\uCED8" + // 2330 - 2334 + "\u8EAC\uCED9\u8EAC\uCEDA\u8EAC\uCDB9\u8EAC\uCDBA\u8EAC\uCDBB" + // 2335 - 2339 + "\u8EAC\uCDBC\u8EAC\uCDBD\u8EAC\uCDBE\u8EAC\uCDBF\u8EAC\uCDC0" + // 2340 - 2344 + "\u8EAC\uCDC1\u8EAC\uCDC2\u8EAC\uCDC3\u8EAC\uCDC4\u8EAC\uCDC5" + // 2345 - 2349 + "\u8EAC\uCDC6\u8EAC\uCDC7\u8EAC\uCDC8\u8EAC\uCDC9\u8EAC\uCDCA" + // 2350 - 2354 + "\u8EAC\uCDCB\u8EAC\uCDCC\u8EAC\uCDCD\u8EAC\uCDCE\u8EAC\uCDCF" + // 2355 - 2359 + "\u8EAC\uCDD0\u8EAC\uCDD1\u8EAC\uCDD2\u8EAC\uCDD3\u8EAC\uCDD4" + // 2360 - 2364 + "\u8EAC\uCDD5\u8EAC\uCDD6\u8EAC\uCDD7\u8EAC\uCDD8\u8EAC\uCDD9" + // 2365 - 2369 + "\u8EAC\uCDDA\u8EAC\uCDDB\u8EAC\uCDDC\u8EAC\uCDDD\u8EAC\uCDDE" + // 2370 - 2374 + "\u8EAC\uCDDF\u8EAC\uCDE0\u8EAC\uCDE1\u8EAC\uCDE2\u8EAC\uCDE3" + // 2375 - 2379 + "\u8EAC\uCDE4\u8EAC\uCDE5\u8EAC\uCDE6\u8EAC\uCDE7\u8EAC\uCDE8" + // 2380 - 2384 + "\u8EAC\uCDE9\u8EAC\uCDEA\u8EAC\uCDEB\u8EAC\uCDEC\u8EAC\uCDED" + // 2385 - 2389 + "\u8EAC\uCDEE\u8EAC\uCDEF\u8EAC\uCDF0\u8EAC\uCDF1\u8EAC\uCDF2" + // 2390 - 2394 + "\u8EAC\uCDF3\u8EAC\uCDF4\u8EAC\uCDF5\u8EAC\uCDF6\u8EAC\uCDF7" + // 2395 - 2399 + "\u8EAC\uCDF8\u8EAC\uCCD7\u8EAC\uCCD8\u8EAC\uCCD9\u8EAC\uCCDA" + // 2400 - 2404 + "\u8EAC\uCCDB\u8EAC\uCCDC\u8EAC\uCCDD\u8EAC\uCCDE\u8EAC\uCCDF" + // 2405 - 2409 + "\u8EAC\uCCE0\u8EAC\uCCE1\u8EAC\uCCE2\u8EAC\uCCE3\u8EAC\uCCE4" + // 2410 - 2414 + "\u8EAC\uCCE5\u8EAC\uCCE6\u8EAC\uCCE7\u8EAC\uCCE8\u8EAC\uCCE9" + // 2415 - 2419 + "\u8EAC\uCCEA\u8EAC\uCCEB\u8EAC\uCCEC\u8EAC\uCCED\u8EAC\uCCEE" + // 2420 - 2424 + "\u8EAC\uCCEF\u8EAC\uCCF0\u8EAC\uCCF1\u8EAC\uCCF2\u8EAC\uCCF3" + // 2425 - 2429 + "\u8EAC\uCCF4\u8EAC\uCCF5\u8EAC\uCCF6\u8EAC\uCCF7\u8EAC\uCCF8" + // 2430 - 2434 + "\u8EAC\uCCF9\u8EAC\uCCFA\u8EAC\uCCFB\u8EAC\uCCFC\u8EAC\uCCFD" + // 2435 - 2439 + "\u8EAC\uCCFE\u8EAC\uCDA1\u8EAC\uCDA2\u8EAC\uCDA3\u8EAC\uCDA4" + // 2440 - 2444 + "\u8EAC\uCDA5\u8EAC\uCDA6\u8EAC\uCDA7\u8EAC\uCDA8\u8EAC\uCDA9" + // 2445 - 2449 + "\u8EAC\uCDAA\u8EAC\uCDAB\u8EAC\uCDAC\u8EAC\uCDAD\u8EAC\uCDAE" + // 2450 - 2454 + "\u8EAC\uCDAF\u8EAC\uCDB0\u8EAC\uCDB1\u8EAC\uCDB2\u8EAC\uCDB3" + // 2455 - 2459 + "\u8EAC\uCDB4\u8EAC\uCDB5\u8EAC\uCDB6\u8EAC\uCDB7\u8EAC\uCDB8" + // 2460 - 2464 + "\u8EAC\uCBF5\u8EAC\uCBF6\u8EAC\uCBF7\u8EAC\uCBF8\u8EAC\uCBF9" + // 2465 - 2469 + "\u8EAC\uCBFA\u8EAC\uCBFB\u8EAC\uCBFC\u8EAC\uCBFD\u8EAC\uCBFE" + // 2470 - 2474 + "\u8EAC\uCCA1\u8EAC\uCCA2\u8EAC\uCCA3\u8EAC\uCCA4\u8EAC\uCCA5" + // 2475 - 2479 + "\u8EAC\uCCA6\u8EAC\uCCA7\u8EAC\uCCA8\u8EAC\uCCA9\u8EAC\uCCAA" + // 2480 - 2484 + "\u8EAC\uCCAB\u8EAC\uCCAC\u8EAC\uCCAD\u8EAC\uCCAE\u8EAC\uCCAF" + // 2485 - 2489 + "\u8EAC\uCCB0\u8EAC\uCCB1\u8EAC\uCCB2\u8EAC\uCCB3\u8EAC\uCCB4" + // 2490 - 2494 + "\u8EAC\uCCB5\u8EAC\uCCB6\u8EAC\uCCB7\u8EAC\uCCB8\u8EAC\uCCB9" + // 2495 - 2499 + "\u8EAC\uCCBA\u8EAC\uCCBB\u8EAC\uCCBC\u8EAC\uCCBD\u8EAC\uCCBE" + // 2500 - 2504 + "\u8EAC\uCCBF\u8EAC\uCCC0\u8EAC\uCCC1\u8EAC\uCCC2\u8EAC\uCCC3" + // 2505 - 2509 + "\u8EAC\uCCC4\u8EAC\uCCC5\u8EAC\uCCC6\u8EAC\uCCC7\u8EAC\uCCC8" + // 2510 - 2514 + "\u8EAC\uCCC9\u8EAC\uCCCA\u8EAC\uCCCB\u8EAC\uCCCC\u8EAC\uCCCD" + // 2515 - 2519 + "\u8EAC\uCCCE\u8EAC\uCCCF\u8EAC\uCCD0\u8EAC\uCCD1\u8EAC\uCCD2" + // 2520 - 2524 + "\u8EAC\uCCD3\u8EAC\uCCD4\u8EAC\uCCD5\u8EAC\uCCD6\u8EAC\uCBB5" + // 2525 - 2529 + "\u8EAC\uCBB6\u8EAC\uCBB7\u8EAC\uCBB8\u8EAC\uCBB9\u8EAC\uCBBA" + // 2530 - 2534 + "\u8EAC\uCBBB\u8EAC\uCBBC\u8EAC\uCBBD\u8EAC\uCBBE\u8EAC\uCBBF" + // 2535 - 2539 + "\u8EAC\uCBC0\u8EAC\uCBC1\u8EAC\uCBC2\u8EAC\uCBC3\u8EAC\uCBC4" + // 2540 - 2544 + "\u8EAC\uCBC5\u8EAC\uCBC6\u8EAC\uCBC7\u8EAC\uCBC8\u8EAC\uCBC9" + // 2545 - 2549 + "\u8EAC\uCBCA\u8EAC\uCBCB\u8EAC\uCBCC\u8EAC\uCBCD\u8EAC\uCBCE" + // 2550 - 2554 + "\u8EAC\uCBCF\u8EAC\uCBD0\u8EAC\uCBD1\u8EAC\uCBD2\u8EAC\uCBD3" + // 2555 - 2559 + "\u8EAC\uCBD4\u8EAC\uCBD5\u8EAC\uCBD6\u8EAC\uCBD7\u8EAC\uCBD8" + // 2560 - 2564 + "\u8EAC\uCBD9\u8EAC\uCBDA\u8EAC\uCBDB\u8EAC\uCBDC\u8EAC\uCBDD" + // 2565 - 2569 + "\u8EAC\uCBDE\u8EAC\uCBDF\u8EAC\uCBE0\u8EAC\uCBE1\u8EAC\uCBE2" + // 2570 - 2574 + "\u8EAC\uCBE3\u8EAC\uCBE4\u8EAC\uCBE5\u8EAC\uCBE6\u8EAC\uCBE7" + // 2575 - 2579 + "\u8EAC\uCBE8\u8EAC\uCBE9\u8EAC\uCBEA\u8EAC\uCBEB\u8EAC\uCBEC" + // 2580 - 2584 + "\u8EAC\uCBED\u8EAC\uCBEE\u8EAC\uCBEF\u8EAC\uCBF0\u8EAC\uCBF1" + // 2585 - 2589 + "\u8EAC\uCBF2\u8EAC\uCBF3\u8EAC\uCBF4\u8EAC\uCAD3\u8EAC\uCAD4" + // 2590 - 2594 + "\u8EAC\uCAD5\u8EAC\uCAD6\u8EAC\uCAD7\u8EAC\uCAD8\u8EAC\uCAD9" + // 2595 - 2599 + "\u8EAC\uCADA\u8EAC\uCADB\u8EAC\uCADC\u8EAC\uCADD\u8EAC\uCADE" + // 2600 - 2604 + "\u8EAC\uCADF\u8EAC\uCAE0\u8EAC\uCAE1\u8EAC\uCAE2\u8EAC\uCAE3" + // 2605 - 2609 + "\u8EAC\uCAE4\u8EAC\uCAE5\u8EAC\uCAE6\u8EAC\uCAE7\u8EAC\uCAE8" + // 2610 - 2614 + "\u8EAC\uCAE9\u8EAC\uCAEA\u8EAC\uCAEB\u8EAC\uCAEC\u8EAC\uCAED" + // 2615 - 2619 + "\u8EAC\uCAEE\u8EAC\uCAEF\u8EAC\uCAF0\u8EAC\uCAF1\u8EAC\uCAF2" + // 2620 - 2624 + "\u8EAC\uCAF3\u8EAC\uCAF4\u8EAC\uCAF5\u8EAC\uCAF6\u8EAC\uCAF7" + // 2625 - 2629 + "\u8EAC\uCAF8\u8EAC\uCAF9\u8EAC\uCAFA\u8EAC\uCAFB\u8EAC\uCAFC" + // 2630 - 2634 + "\u8EAC\uCAFD\u8EAC\uCAFE\u8EAC\uCBA1\u8EAC\uCBA2\u8EAC\uCBA3" + // 2635 - 2639 + "\u8EAC\uCBA4\u8EAC\uCBA5\u8EAC\uCBA6\u8EAC\uCBA7\u8EAC\uCBA8" + // 2640 - 2644 + "\u8EAC\uCBA9\u8EAC\uCBAA\u8EAC\uCBAB\u8EAC\uCBAC\u8EAC\uCBAD" + // 2645 - 2649 + "\u8EAC\uCBAE\u8EAC\uCBAF\u8EAC\uCBB0\u8EAC\uCBB1\u8EAC\uCBB2" + // 2650 - 2654 + "\u8EAC\uCBB3\u8EAC\uCBB4\u8EAC\uC9F1\u8EAC\uC9F2\u8EAC\uC9F3" + // 2655 - 2659 + "\u8EAC\uC9F4\u8EAC\uC9F5\u8EAC\uC9F6\u8EAC\uC9F7\u8EAC\uC9F8" + // 2660 - 2664 + "\u8EAC\uC9F9\u8EAC\uC9FA\u8EAC\uC9FB\u8EAC\uC9FC\u8EAC\uC9FD" + // 2665 - 2669 + "\u8EAC\uC9FE\u8EAC\uCAA1\u8EAC\uCAA2\u8EAC\uCAA3\u8EAC\uCAA4" + // 2670 - 2674 + "\u8EAC\uCAA5\u8EAC\uCAA6\u8EAC\uCAA7\u8EAC\uCAA8\u8EAC\uCAA9" + // 2675 - 2679 + "\u8EAC\uCAAA\u8EAC\uCAAB\u8EAC\uCAAC\u8EAC\uCAAD\u8EAC\uCAAE" + // 2680 - 2684 + "\u8EAC\uCAAF\u8EAC\uCAB0\u8EAC\uCAB1\u8EAC\uCAB2\u8EAC\uCAB3" + // 2685 - 2689 + "\u8EAC\uCAB4\u8EAC\uCAB5\u8EAC\uCAB6\u8EAC\uCAB7\u8EAC\uCAB8" + // 2690 - 2694 + "\u8EAC\uCAB9\u8EAC\uCABA\u8EAC\uCABB\u8EAC\uCABC\u8EAC\uCABD" + // 2695 - 2699 + "\u8EAC\uCABE\u8EAC\uCABF\u8EAC\uCAC0\u8EAC\uCAC1\u8EAC\uCAC2" + // 2700 - 2704 + "\u8EAC\uCAC3\u8EAC\uCAC4\u8EAC\uCAC5\u8EAC\uCAC6\u8EAC\uCAC7" + // 2705 - 2709 + "\u8EAC\uCAC8\u8EAC\uCAC9\u8EAC\uCACA\u8EAC\uCACB\u8EAC\uCACC" + // 2710 - 2714 + "\u8EAC\uCACD\u8EAC\uCACE\u8EAC\uCACF\u8EAC\uCAD0\u8EAC\uCAD1" + // 2715 - 2719 + "\u8EAC\uCAD2\u8EAC\uC9B1\u8EAC\uC9B2\u8EAC\uC9B3\u8EAC\uC9B4" + // 2720 - 2724 + "\u8EAC\uC9B5\u8EAC\uC9B6\u8EAC\uC9B7\u8EAC\uC9B8\u8EAC\uC9B9" + // 2725 - 2729 + "\u8EAC\uC9BA\u8EAC\uC9BB\u8EAC\uC9BC\u8EAC\uC9BD\u8EAC\uC9BE" + // 2730 - 2734 + "\u8EAC\uC9BF\u8EAC\uC9C0\u8EAC\uC9C1\u8EAC\uC9C2\u8EAC\uC9C3" + // 2735 - 2739 + "\u8EAC\uC9C4\u8EAC\uC9C5\u8EAC\uC9C6\u8EAC\uC9C7\u8EAC\uC9C8" + // 2740 - 2744 + "\u8EAC\uC9C9\u8EAC\uC9CA\u8EAC\uC9CB\u8EAC\uC9CC\u8EAC\uC9CD" + // 2745 - 2749 + "\u8EAC\uC9CE\u8EAC\uC9CF\u8EAC\uC9D0\u8EAC\uC9D1\u8EAC\uC9D2" + // 2750 - 2754 + "\u8EAC\uC9D3\u8EAC\uC9D4\u8EAC\uC9D5\u8EAC\uC9D6\u8EAC\uC9D7" + // 2755 - 2759 + "\u8EAC\uC9D8\u8EAC\uC9D9\u8EAC\uC9DA\u8EAC\uC9DB\u8EAC\uC9DC" + // 2760 - 2764 + "\u8EAC\uC9DD\u8EAC\uC9DE\u8EAC\uC9DF\u8EAC\uC9E0\u8EAC\uC9E1" + // 2765 - 2769 + "\u8EAC\uC9E2\u8EAC\uC9E3\u8EAC\uC9E4\u8EAC\uC9E5\u8EAC\uC9E6" + // 2770 - 2774 + "\u8EAC\uC9E7\u8EAC\uC9E8\u8EAC\uC9E9\u8EAC\uC9EA\u8EAC\uC9EB" + // 2775 - 2779 + "\u8EAC\uC9EC\u8EAC\uC9ED\u8EAC\uC9EE\u8EAC\uC9EF\u8EAC\uC9F0" + // 2780 - 2784 + "\u8EAC\uC8CF\u8EAC\uC8D0\u8EAC\uC8D1\u8EAC\uC8D2\u8EAC\uC8D3" + // 2785 - 2789 + "\u8EAC\uC8D4\u8EAC\uC8D5\u8EAC\uC8D6\u8EAC\uC8D7\u8EAC\uC8D8" + // 2790 - 2794 + "\u8EAC\uC8D9\u8EAC\uC8DA\u8EAC\uC8DB\u8EAC\uC8DC\u8EAC\uC8DD" + // 2795 - 2799 + "\u8EAC\uC8DE\u8EAC\uC8DF\u8EAC\uC8E0\u8EAC\uC8E1\u8EAC\uC8E2" + // 2800 - 2804 + "\u8EAC\uC8E3\u8EAC\uC8E4\u8EAC\uC8E5\u8EAC\uC8E6\u8EAC\uC8E7" + // 2805 - 2809 + "\u8EAC\uC8E8\u8EAC\uC8E9\u8EAC\uC8EA\u8EAC\uC8EB\u8EAC\uC8EC" + // 2810 - 2814 + "\u8EAC\uC8ED\u8EAC\uC8EE\u8EAC\uC8EF\u8EAC\uC8F0\u8EAC\uC8F1" + // 2815 - 2819 + "\u8EAC\uC8F2\u8EAC\uC8F3\u8EAC\uC8F4\u8EAC\uC8F5\u8EAC\uC8F6" + // 2820 - 2824 + "\u8EAC\uC8F7\u8EAC\uC8F8\u8EAC\uC8F9\u8EAC\uC8FA\u8EAC\uC8FB" + // 2825 - 2829 + "\u8EAC\uC8FC\u8EAC\uC8FD\u8EAC\uC8FE\u8EAC\uC9A1\u8EAC\uC9A2" + // 2830 - 2834 + "\u8EAC\uC9A3\u8EAC\uC9A4\u8EAC\uC9A5\u8EAC\uC9A6\u8EAC\uC9A7" + // 2835 - 2839 + "\u8EAC\uC9A8\u8EAC\uC9A9\u8EAC\uC9AA\u8EAC\uC9AB\u8EAC\uC9AC" + // 2840 - 2844 + "\u8EAC\uC9AD\u8EAC\uC9AE\u8EAC\uC9AF\u8EAC\uC9B0\u8EAC\uC7ED" + // 2845 - 2849 + "\u8EAC\uC7EE\u8EAC\uC7EF\u8EAC\uC7F0\u8EAC\uC7F1\u8EAC\uC7F2" + // 2850 - 2854 + "\u8EAC\uC7F3\u8EAC\uC7F4\u8EAC\uC7F5\u8EAC\uC7F6\u8EAC\uC7F7" + // 2855 - 2859 + "\u8EAC\uC7F8\u8EAC\uC7F9\u8EAC\uC7FA\u8EAC\uC7FB\u8EAC\uC7FC" + // 2860 - 2864 + "\u8EAC\uC7FD\u8EAC\uC7FE\u8EAC\uC8A1\u8EAC\uC8A2\u8EAC\uC8A3" + // 2865 - 2869 + "\u8EAC\uC8A4\u8EAC\uC8A5\u8EAC\uC8A6\u8EAC\uC8A7\u8EAC\uC8A8" + // 2870 - 2874 + "\u8EAC\uC8A9\u8EAC\uC8AA\u8EAC\uC8AB\u8EAC\uC8AC\u8EAC\uC8AD" + // 2875 - 2879 + "\u8EAC\uC8AE\u8EAC\uC8AF\u8EAC\uC8B0\u8EAC\uC8B1\u8EAC\uC8B2" + // 2880 - 2884 + "\u8EAC\uC8B3\u8EAC\uC8B4\u8EAC\uC8B5\u8EAC\uC8B6\u8EAC\uC8B7" + // 2885 - 2889 + "\u8EAC\uC8B8\u8EAC\uC8B9\u8EAC\uC8BA\u8EAC\uC8BB\u8EAC\uC8BC" + // 2890 - 2894 + "\u8EAC\uC8BD\u8EAC\uC8BE\u8EAC\uC8BF\u8EAC\uC8C0\u8EAC\uC8C1" + // 2895 - 2899 + "\u8EAC\uC8C2\u8EAC\uC8C3\u8EAC\uC8C4\u8EAC\uC8C5\u8EAC\uC8C6" + // 2900 - 2904 + "\u8EAC\uC8C7\u8EAC\uC8C8\u8EAC\uC8C9\u8EAC\uC8CA\u8EAC\uC8CB" + // 2905 - 2909 + "\u8EAC\uC8CC\u8EAC\uC8CD\u8EAC\uC8CE\u8EAC\uC7AD\u8EAC\uC7AE" + // 2910 - 2914 + "\u8EAC\uC7AF\u8EAC\uC7B0\u8EAC\uC7B1\u8EAC\uC7B2\u8EAC\uC7B3" + // 2915 - 2919 + "\u8EAC\uC7B4\u8EAC\uC7B5\u8EAC\uC7B6\u8EAC\uC7B7\u8EAC\uC7B8" + // 2920 - 2924 + "\u8EAC\uC7B9\u8EAC\uC7BA\u8EAC\uC7BB\u8EAC\uC7BC\u8EAC\uC7BD" + // 2925 - 2929 + "\u8EAC\uC7BE\u8EAC\uC7BF\u8EAC\uC7C0\u8EAC\uC7C1\u8EAC\uC7C2" + // 2930 - 2934 + "\u8EAC\uC7C3\u8EAC\uC7C4\u8EAC\uC7C5\u8EAC\uC7C6\u8EAC\uC7C7" + // 2935 - 2939 + "\u8EAC\uC7C8\u8EAC\uC7C9\u8EAC\uC7CA\u8EAC\uC7CB\u8EAC\uC7CC" + // 2940 - 2944 + "\u8EAC\uC7CD\u8EAC\uC7CE\u8EAC\uC7CF\u8EAC\uC7D0\u8EAC\uC7D1" + // 2945 - 2949 + "\u8EAC\uC7D2\u8EAC\uC7D3\u8EAC\uC7D4\u8EAC\uC7D5\u8EAC\uC7D6" + // 2950 - 2954 + "\u8EAC\uC7D7\u8EAC\uC7D8\u8EAC\uC7D9\u8EAC\uC7DA\u8EAC\uC7DB" + // 2955 - 2959 + "\u8EAC\uC7DC\u8EAC\uC7DD\u8EAC\uC7DE\u8EAC\uC7DF\u8EAC\uC7E0" + // 2960 - 2964 + "\u8EAC\uC7E1\u8EAC\uC7E2\u8EAC\uC7E3\u8EAC\uC7E4\u8EAC\uC7E5" + // 2965 - 2969 + "\u8EAC\uC7E6\u8EAC\uC7E7\u8EAC\uC7E8\u8EAC\uC7E9\u8EAC\uC7EA" + // 2970 - 2974 + "\u8EAC\uC7EB\u8EAC\uC7EC\u8EAC\uC6CB\u8EAC\uC6CC\u8EAC\uC6CD" + // 2975 - 2979 + "\u8EAC\uC6CE\u8EAC\uC6CF\u8EAC\uC6D0\u8EAC\uC6D1\u8EAC\uC6D2" + // 2980 - 2984 + "\u8EAC\uC6D3\u8EAC\uC6D4\u8EAC\uC6D5\u8EAC\uC6D6\u8EAC\uC6D7" + // 2985 - 2989 + "\u8EAC\uC6D8\u8EAC\uC6D9\u8EAC\uC6DA\u8EAC\uC6DB\u8EAC\uC6DC" + // 2990 - 2994 + "\u8EAC\uC6DD\u8EAC\uC6DE\u8EAC\uC6DF\u8EAC\uC6E0\u8EAC\uC6E1" + // 2995 - 2999 + "\u8EAC\uC6E2\u8EAC\uC6E3\u8EAC\uC6E4\u8EAC\uC6E5\u8EAC\uC6E6" + // 3000 - 3004 + "\u8EAC\uC6E7\u8EAC\uC6E8\u8EAC\uC6E9\u8EAC\uC6EA\u8EAC\uC6EB" + // 3005 - 3009 + "\u8EAC\uC6EC\u8EAC\uC6ED\u8EAC\uC6EE\u8EAC\uC6EF\u8EAC\uC6F0" + // 3010 - 3014 + "\u8EAC\uC6F1\u8EAC\uC6F2\u8EAC\uC6F3\u8EAC\uC6F4\u8EAC\uC6F5" + // 3015 - 3019 + "\u8EAC\uC6F6\u8EAC\uC6F7\u8EAC\uC6F8\u8EAC\uC6F9\u8EAC\uC6FA" + // 3020 - 3024 + "\u8EAC\uC6FB\u8EAC\uC6FC\u8EAC\uC6FD\u8EAC\uC6FE\u8EAC\uC7A1" + // 3025 - 3029 + "\u8EAC\uC7A2\u8EAC\uC7A3\u8EAC\uC7A4\u8EAC\uC7A5\u8EAC\uC7A6" + // 3030 - 3034 + "\u8EAC\uC7A7\u8EAC\uC7A8\u8EAC\uC7A9\u8EAC\uC7AA\u8EAC\uC7AB" + // 3035 - 3039 + "\u8EAC\uC7AC\u8EAC\uC5E9\u8EAC\uC5EA\u8EAC\uC5EB\u8EAC\uC5EC" + // 3040 - 3044 + "\u8EAC\uC5ED\u8EAC\uC5EE\u8EAC\uC5EF\u8EAC\uC5F0\u8EAC\uC5F1" + // 3045 - 3049 + "\u8EAC\uC5F2\u8EAC\uC5F3\u8EAC\uC5F4\u8EAC\uC5F5\u8EAC\uC5F6" + // 3050 - 3054 + "\u8EAC\uC5F7\u8EAC\uC5F8\u8EAC\uC5F9\u8EAC\uC5FA\u8EAC\uC5FB" + // 3055 - 3059 + "\u8EAC\uC5FC\u8EAC\uC5FD\u8EAC\uC5FE\u8EAC\uC6A1\u8EAC\uC6A2" + // 3060 - 3064 + "\u8EAC\uC6A3\u8EAC\uC6A4\u8EAC\uC6A5\u8EAC\uC6A6\u8EAC\uC6A7" + // 3065 - 3069 + "\u8EAC\uC6A8\u8EAC\uC6A9\u8EAC\uC6AA\u8EAC\uC6AB\u8EAC\uC6AC" + // 3070 - 3074 + "\u8EAC\uC6AD\u8EAC\uC6AE\u8EAC\uC6AF\u8EAC\uC6B0\u8EAC\uC6B1" + // 3075 - 3079 + "\u8EAC\uC6B2\u8EAC\uC6B3\u8EAC\uC6B4\u8EAC\uC6B5\u8EAC\uC6B6" + // 3080 - 3084 + "\u8EAC\uC6B7\u8EAC\uC6B8\u8EAC\uC6B9\u8EAC\uC6BA\u8EAC\uC6BB" + // 3085 - 3089 + "\u8EAC\uC6BC\u8EAC\uC6BD\u8EAC\uC6BE\u8EAC\uC6BF\u8EAC\uC6C0" + // 3090 - 3094 + "\u8EAC\uC6C1\u8EAC\uC6C2\u8EAC\uC6C3\u8EAC\uC6C4\u8EAC\uC6C5" + // 3095 - 3099 + "\u8EAC\uC6C6\u8EAC\uC6C7\u8EAC\uC6C8\u8EAC\uC6C9\u8EAC\uC6CA" + // 3100 - 3104 + "\u8EAC\uC5A9\u8EAC\uC5AA\u8EAC\uC5AB\u8EAC\uC5AC\u8EAC\uC5AD" + // 3105 - 3109 + "\u8EAC\uC5AE\u8EAC\uC5AF\u8EAC\uC5B0\u8EAC\uC5B1\u8EAC\uC5B2" + // 3110 - 3114 + "\u8EAC\uC5B3\u8EAC\uC5B4\u8EAC\uC5B5\u8EAC\uC5B6\u8EAC\uC5B7" + // 3115 - 3119 + "\u8EAC\uC5B8\u8EAC\uC5B9\u8EAC\uC5BA\u8EAC\uC5BB\u8EAC\uC5BC" + // 3120 - 3124 + "\u8EAC\uC5BD\u8EAC\uC5BE\u8EAC\uC5BF\u8EAC\uC5C0\u8EAC\uC5C1" + // 3125 - 3129 + "\u8EAC\uC5C2\u8EAC\uC5C3\u8EAC\uC5C4\u8EAC\uC5C5\u8EAC\uC5C6" + // 3130 - 3134 + "\u8EAC\uC5C7\u8EAC\uC5C8\u8EAC\uC5C9\u8EAC\uC5CA\u8EAC\uC5CB" + // 3135 - 3139 + "\u8EAC\uC5CC\u8EAC\uC5CD\u8EAC\uC5CE\u8EAC\uC5CF\u8EAC\uC5D0" + // 3140 - 3144 + "\u8EAC\uC5D1\u8EAC\uC5D2\u8EAC\uC5D3\u8EAC\uC5D4\u8EAC\uC5D5" + // 3145 - 3149 + "\u8EAC\uC5D6\u8EAC\uC5D7\u8EAC\uC5D8\u8EAC\uC5D9\u8EAC\uC5DA" + // 3150 - 3154 + "\u8EAC\uC5DB\u8EAC\uC5DC\u8EAC\uC5DD\u8EAC\uC5DE\u8EAC\uC5DF" + // 3155 - 3159 + "\u8EAC\uC5E0\u8EAC\uC5E1\u8EAC\uC5E2\u8EAC\uC5E3\u8EAC\uC5E4" + // 3160 - 3164 + "\u8EAC\uC5E5\u8EAC\uC5E6\u8EAC\uC5E7\u8EAC\uC5E8\u8EAC\uC4C7" + // 3165 - 3169 + "\u8EAC\uC4C8\u8EAC\uC4C9\u8EAC\uC4CA\u8EAC\uC4CB\u8EAC\uC4CC" + // 3170 - 3174 + "\u8EAC\uC4CD\u8EAC\uC4CE\u8EAC\uC4CF\u8EAC\uC4D0\u8EAC\uC4D1" + // 3175 - 3179 + "\u8EAC\uC4D2\u8EAC\uC4D3\u8EAC\uC4D4\u8EAC\uC4D5\u8EAC\uC4D6" + // 3180 - 3184 + "\u8EAC\uC4D7\u8EAC\uC4D8\u8EAC\uC4D9\u8EAC\uC4DA\u8EAC\uC4DB" + // 3185 - 3189 + "\u8EAC\uC4DC\u8EAC\uC4DD\u8EAC\uC4DE\u8EAC\uC4DF\u8EAC\uC4E0" + // 3190 - 3194 + "\u8EAC\uC4E1\u8EAC\uC4E2\u8EAC\uC4E3\u8EAC\uC4E4\u8EAC\uC4E5" + // 3195 - 3199 + "\u8EAC\uC4E6\u8EAC\uC4E7\u8EAC\uC4E8\u8EAC\uC4E9\u8EAC\uC4EA" + // 3200 - 3204 + "\u8EAC\uC4EB\u8EAC\uC4EC\u8EAC\uC4ED\u8EAC\uC4EE\u8EAC\uC4EF" + // 3205 - 3209 + "\u8EAC\uC4F0\u8EAC\uC4F1\u8EAC\uC4F2\u8EAC\uC4F3\u8EAC\uC4F4" + // 3210 - 3214 + "\u8EAC\uC4F5\u8EAC\uC4F6\u8EAC\uC4F7\u8EAC\uC4F8\u8EAC\uC4F9" + // 3215 - 3219 + "\u8EAC\uC4FA\u8EAC\uC4FB\u8EAC\uC4FC\u8EAC\uC4FD\u8EAC\uC4FE" + // 3220 - 3224 + "\u8EAC\uC5A1\u8EAC\uC5A2\u8EAC\uC5A3\u8EAC\uC5A4\u8EAC\uC5A5" + // 3225 - 3229 + "\u8EAC\uC5A6\u8EAC\uC5A7\u8EAC\uC5A8\u8EAC\uC3E5\u8EAC\uC3E6" + // 3230 - 3234 + "\u8EAC\uC3E7\u8EAC\uC3E8\u8EAC\uC3E9\u8EAC\uC3EA\u8EAC\uC3EB" + // 3235 - 3239 + "\u8EAC\uC3EC\u8EAC\uC3ED\u8EAC\uC3EE\u8EAC\uC3EF\u8EAC\uC3F0" + // 3240 - 3244 + "\u8EAC\uC3F1\u8EAC\uC3F2\u8EAC\uC3F3\u8EAC\uC3F4\u8EAC\uC3F5" + // 3245 - 3249 + "\u8EAC\uC3F6\u8EAC\uC3F7\u8EAC\uC3F8\u8EAC\uC3F9\u8EAC\uC3FA" + // 3250 - 3254 + "\u8EAC\uC3FB\u8EAC\uC3FC\u8EAC\uC3FD\u8EAC\uC3FE\u8EAC\uC4A1" + // 3255 - 3259 + "\u8EAC\uC4A2\u8EAC\uC4A3\u8EAC\uC4A4\u8EAC\uC4A5\u8EAC\uC4A6" + // 3260 - 3264 + "\u8EAC\uC4A7\u8EAC\uC4A8\u8EAC\uC4A9\u8EAC\uC4AA\u8EAC\uC4AB" + // 3265 - 3269 + "\u8EAC\uC4AC\u8EAC\uC4AD\u8EAC\uC4AE\u8EAC\uC4AF\u8EAC\uC4B0" + // 3270 - 3274 + "\u8EAC\uC4B1\u8EAC\uC4B2\u8EAC\uC4B3\u8EAC\uC4B4\u8EAC\uC4B5" + // 3275 - 3279 + "\u8EAC\uC4B6\u8EAC\uC4B7\u8EAC\uC4B8\u8EAC\uC4B9\u8EAC\uC4BA" + // 3280 - 3284 + "\u8EAC\uC4BB\u8EAC\uC4BC\u8EAC\uC4BD\u8EAC\uC4BE\u8EAC\uC4BF" + // 3285 - 3289 + "\u8EAC\uC4C0\u8EAC\uC4C1\u8EAC\uC4C2\u8EAC\uC4C3\u8EAC\uC4C4" + // 3290 - 3294 + "\u8EAC\uC4C5\u8EAC\uC4C6\u8EAC\uC3A5\u8EAC\uC3A6\u8EAC\uC3A7" + // 3295 - 3299 + "\u8EAC\uC3A8\u8EAC\uC3A9\u8EAC\uC3AA\u8EAC\uC3AB\u8EAC\uC3AC" + // 3300 - 3304 + "\u8EAC\uC3AD\u8EAC\uC3AE\u8EAC\uC3AF\u8EAC\uC3B0\u8EAC\uC3B1" + // 3305 - 3309 + "\u8EAC\uC3B2\u8EAC\uC3B3\u8EAC\uC3B4\u8EAC\uC3B5\u8EAC\uC3B6" + // 3310 - 3314 + "\u8EAC\uC3B7\u8EAC\uC3B8\u8EAC\uC3B9\u8EAC\uC3BA\u8EAC\uC3BB" + // 3315 - 3319 + "\u8EAC\uC3BC\u8EAC\uC3BD\u8EAC\uC3BE\u8EAC\uC3BF\u8EAC\uC3C0" + // 3320 - 3324 + "\u8EAC\uC3C1\u8EAC\uC3C2\u8EAC\uC3C3\u8EAC\uC3C4\u8EAC\uC3C5" + // 3325 - 3329 + "\u8EAC\uC3C6\u8EAC\uC3C7\u8EAC\uC3C8\u8EAC\uC3C9\u8EAC\uC3CA" + // 3330 - 3334 + "\u8EAC\uC3CB\u8EAC\uC3CC\u8EAC\uC3CD\u8EAC\uC3CE\u8EAC\uC3CF" + // 3335 - 3339 + "\u8EAC\uC3D0\u8EAC\uC3D1\u8EAC\uC3D2\u8EAC\uC3D3\u8EAC\uC3D4" + // 3340 - 3344 + "\u8EAC\uC3D5\u8EAC\uC3D6\u8EAC\uC3D7\u8EAC\uC3D8\u8EAC\uC3D9" + // 3345 - 3349 + "\u8EAC\uC3DA\u8EAC\uC3DB\u8EAC\uC3DC\u8EAC\uC3DD\u8EAC\uC3DE" + // 3350 - 3354 + "\u8EAC\uC3DF\u8EAC\uC3E0\u8EAC\uC3E1\u8EAC\uC3E2\u8EAC\uC3E3" + // 3355 - 3359 + "\u8EAC\uC3E4\u8EAC\uC2C3\u8EAC\uC2C4\u8EAC\uC2C5\u8EAC\uC2C6" + // 3360 - 3364 + "\u8EAC\uC2C7\u8EAC\uC2C8\u8EAC\uC2C9\u8EAC\uC2CA\u8EAC\uC2CB" + // 3365 - 3369 + "\u8EAC\uC2CC\u8EAC\uC2CD\u8EAC\uC2CE\u8EAC\uC2CF\u8EAC\uC2D0" + // 3370 - 3374 + "\u8EAC\uC2D1\u8EAC\uC2D2\u8EAC\uC2D3\u8EAC\uC2D4\u8EAC\uC2D5" + // 3375 - 3379 + "\u8EAC\uC2D6\u8EAC\uC2D7\u8EAC\uC2D8\u8EAC\uC2D9\u8EAC\uC2DA" + // 3380 - 3384 + "\u8EAC\uC2DB\u8EAC\uC2DC\u8EAC\uC2DD\u8EAC\uC2DE\u8EAC\uC2DF" + // 3385 - 3389 + "\u8EAC\uC2E0\u8EAC\uC2E1\u8EAC\uC2E2\u8EAC\uC2E3\u8EAC\uC2E4" + // 3390 - 3394 + "\u8EAC\uC2E5\u8EAC\uC2E6\u8EAC\uC2E7\u8EAC\uC2E8\u8EAC\uC2E9" + // 3395 - 3399 + "\u8EAC\uC2EA\u8EAC\uC2EB\u8EAC\uC2EC\u8EAC\uC2ED\u8EAC\uC2EE" + // 3400 - 3404 + "\u8EAC\uC2EF\u8EAC\uC2F0\u8EAC\uC2F1\u8EAC\uC2F2\u8EAC\uC2F3" + // 3405 - 3409 + "\u8EAC\uC2F4\u8EAC\uC2F5\u8EAC\uC2F6\u8EAC\uC2F7\u8EAC\uC2F8" + // 3410 - 3414 + "\u8EAC\uC2F9\u8EAC\uC2FA\u8EAC\uC2FB\u8EAC\uC2FC\u8EAC\uC2FD" + // 3415 - 3419 + "\u8EAC\uC2FE\u8EAC\uC3A1\u8EAC\uC3A2\u8EAC\uC3A3\u8EAC\uC3A4" + // 3420 - 3424 + "\u8EAC\uC1E1\u8EAC\uC1E2\u8EAC\uC1E3\u8EAC\uC1E4\u8EAC\uC1E5" + // 3425 - 3429 + "\u8EAC\uC1E6\u8EAC\uC1E7\u8EAC\uC1E8\u8EAC\uC1E9\u8EAC\uC1EA" + // 3430 - 3434 + "\u8EAC\uC1EB\u8EAC\uC1EC\u8EAC\uC1ED\u8EAC\uC1EE\u8EAC\uC1EF" + // 3435 - 3439 + "\u8EAC\uC1F0\u8EAC\uC1F1\u8EAC\uC1F2\u8EAC\uC1F3\u8EAC\uC1F4" + // 3440 - 3444 + "\u8EAC\uC1F5\u8EAC\uC1F6\u8EAC\uC1F7\u8EAC\uC1F8\u8EAC\uC1F9" + // 3445 - 3449 + "\u8EAC\uC1FA\u8EAC\uC1FB\u8EAC\uC1FC\u8EAC\uC1FD\u8EAC\uC1FE" + // 3450 - 3454 + "\u8EAC\uC2A1\u8EAC\uC2A2\u8EAC\uC2A3\u8EAC\uC2A4\u8EAC\uC2A5" + // 3455 - 3459 + "\u8EAC\uC2A6\u8EAC\uC2A7\u8EAC\uC2A8\u8EAC\uC2A9\u8EAC\uC2AA" + // 3460 - 3464 + "\u8EAC\uC2AB\u8EAC\uC2AC\u8EAC\uC2AD\u8EAC\uC2AE\u8EAC\uC2AF" + // 3465 - 3469 + "\u8EAC\uC2B0\u8EAC\uC2B1\u8EAC\uC2B2\u8EAC\uC2B3\u8EAC\uC2B4" + // 3470 - 3474 + "\u8EAC\uC2B5\u8EAC\uC2B6\u8EAC\uC2B7\u8EAC\uC2B8\u8EAC\uC2B9" + // 3475 - 3479 + "\u8EAC\uC2BA\u8EAC\uC2BB\u8EAC\uC2BC\u8EAC\uC2BD\u8EAC\uC2BE" + // 3480 - 3484 + "\u8EAC\uC2BF\u8EAC\uC2C0\u8EAC\uC2C1\u8EAC\uC2C2\u8EAC\uC1A1" + // 3485 - 3489 + "\u8EAC\uC1A2\u8EAC\uC1A3\u8EAC\uC1A4\u8EAC\uC1A5\u8EAC\uC1A6" + // 3490 - 3494 + "\u8EAC\uC1A7\u8EAC\uC1A8\u8EAC\uC1A9\u8EAC\uC1AA\u8EAC\uC1AB" + // 3495 - 3499 + "\u8EAC\uC1AC\u8EAC\uC1AD\u8EAC\uC1AE\u8EAC\uC1AF\u8EAC\uC1B0" + // 3500 - 3504 + "\u8EAC\uC1B1\u8EAC\uC1B2\u8EAC\uC1B3\u8EAC\uC1B4\u8EAC\uC1B5" + // 3505 - 3509 + "\u8EAC\uC1B6\u8EAC\uC1B7\u8EAC\uC1B8\u8EAC\uC1B9\u8EAC\uC1BA" + // 3510 - 3514 + "\u8EAC\uC1BB\u8EAC\uC1BC\u8EAC\uC1BD\u8EAC\uC1BE\u8EAC\uC1BF" + // 3515 - 3519 + "\u8EAC\uC1C0\u8EAC\uC1C1\u8EAC\uC1C2\u8EAC\uC1C3\u8EAC\uC1C4" + // 3520 - 3524 + "\u8EAC\uC1C5\u8EAC\uC1C6\u8EAC\uC1C7\u8EAC\uC1C8\u8EAC\uC1C9" + // 3525 - 3529 + "\u8EAC\uC1CA\u8EAC\uC1CB\u8EAC\uC1CC\u8EAC\uC1CD\u8EAC\uC1CE" + // 3530 - 3534 + "\u8EAC\uC1CF\u8EAC\uC1D0\u8EAC\uC1D1\u8EAC\uC1D2\u8EAC\uC1D3" + // 3535 - 3539 + "\u8EAC\uC1D4\u8EAC\uC1D5\u8EAC\uC1D6\u8EAC\uC1D7\u8EAC\uC1D8" + // 3540 - 3544 + "\u8EAC\uC1D9\u8EAC\uC1DA\u8EAC\uC1DB\u8EAC\uC1DC\u8EAC\uC1DD" + // 3545 - 3549 + "\u8EAC\uC1DE\u8EAC\uC1DF\u8EAC\uC1E0\u8EAC\uC0BF\u8EAC\uC0C0" + // 3550 - 3554 + "\u8EAC\uC0C1\u8EAC\uC0C2\u8EAC\uC0C3\u8EAC\uC0C4\u8EAC\uC0C5" + // 3555 - 3559 + "\u8EAC\uC0C6\u8EAC\uC0C7\u8EAC\uC0C8\u8EAC\uC0C9\u8EAC\uC0CA" + // 3560 - 3564 + "\u8EAC\uC0CB\u8EAC\uC0CC\u8EAC\uC0CD\u8EAC\uC0CE\u8EAC\uC0CF" + // 3565 - 3569 + "\u8EAC\uC0D0\u8EAC\uC0D1\u8EAC\uC0D2\u8EAC\uC0D3\u8EAC\uC0D4" + // 3570 - 3574 + "\u8EAC\uC0D5\u8EAC\uC0D6\u8EAC\uC0D7\u8EAC\uC0D8\u8EAC\uC0D9" + // 3575 - 3579 + "\u8EAC\uC0DA\u8EAC\uC0DB\u8EAC\uC0DC\u8EAC\uC0DD\u8EAC\uC0DE" + // 3580 - 3584 + "\u8EAC\uC0DF\u8EAC\uC0E0\u8EAC\uC0E1\u8EAC\uC0E2\u8EAC\uC0E3" + // 3585 - 3589 + "\u8EAC\uC0E4\u8EAC\uC0E5\u8EAC\uC0E6\u8EAC\uC0E7\u8EAC\uC0E8" + // 3590 - 3594 + "\u8EAC\uC0E9\u8EAC\uC0EA\u8EAC\uC0EB\u8EAC\uC0EC\u8EAC\uC0ED" + // 3595 - 3599 + "\u8EAC\uC0EE\u8EAC\uC0EF\u8EAC\uC0F0\u8EAC\uC0F1\u8EAC\uC0F2" + // 3600 - 3604 + "\u8EAC\uC0F3\u8EAC\uC0F4\u8EAC\uC0F5\u8EAC\uC0F6\u8EAC\uC0F7" + // 3605 - 3609 + "\u8EAC\uC0F8\u8EAC\uC0F9\u8EAC\uC0FA\u8EAC\uC0FB\u8EAC\uC0FC" + // 3610 - 3614 + "\u8EAC\uC0FD\u8EAC\uC0FE\u8EAC\uBFDD\u8EAC\uBFDE\u8EAC\uBFDF" + // 3615 - 3619 + "\u8EAC\uBFE0\u8EAC\uBFE1\u8EAC\uBFE2\u8EAC\uBFE3\u8EAC\uBFE4" + // 3620 - 3624 + "\u8EAC\uBFE5\u8EAC\uBFE6\u8EAC\uBFE7\u8EAC\uBFE8\u8EAC\uBFE9" + // 3625 - 3629 + "\u8EAC\uBFEA\u8EAC\uBFEB\u8EAC\uBFEC\u8EAC\uBFED\u8EAC\uBFEE" + // 3630 - 3634 + "\u8EAC\uBFEF\u8EAC\uBFF0\u8EAC\uBFF1\u8EAC\uBFF2\u8EAC\uBFF3" + // 3635 - 3639 + "\u8EAC\uBFF4\u8EAC\uBFF5\u8EAC\uBFF6\u8EAC\uBFF7\u8EAC\uBFF8" + // 3640 - 3644 + "\u8EAC\uBFF9\u8EAC\uBFFA\u8EAC\uBFFB\u8EAC\uBFFC\u8EAC\uBFFD" + // 3645 - 3649 + "\u8EAC\uBFFE\u8EAC\uC0A1\u8EAC\uC0A2\u8EAC\uC0A3\u8EAC\uC0A4" + // 3650 - 3654 + "\u8EAC\uC0A5\u8EAC\uC0A6\u8EAC\uC0A7\u8EAC\uC0A8\u8EAC\uC0A9" + // 3655 - 3659 + "\u8EAC\uC0AA\u8EAC\uC0AB\u8EAC\uC0AC\u8EAC\uC0AD\u8EAC\uC0AE" + // 3660 - 3664 + "\u8EAC\uC0AF\u8EAC\uC0B0\u8EAC\uC0B1\u8EAC\uC0B2\u8EAC\uC0B3" + // 3665 - 3669 + "\u8EAC\uC0B4\u8EAC\uC0B5\u8EAC\uC0B6\u8EAC\uC0B7\u8EAC\uC0B8" + // 3670 - 3674 + "\u8EAC\uC0B9\u8EAC\uC0BA\u8EAC\uC0BB\u8EAC\uC0BC\u8EAC\uC0BD" + // 3675 - 3679 + "\u8EAC\uC0BE\u8EAC\uBEFB\u8EAC\uBEFC\u8EAC\uBEFD\u8EAC\uBEFE" + // 3680 - 3684 + "\u8EAC\uBFA1\u8EAC\uBFA2\u8EAC\uBFA3\u8EAC\uBFA4\u8EAC\uBFA5" + // 3685 - 3689 + "\u8EAC\uBFA6\u8EAC\uBFA7\u8EAC\uBFA8\u8EAC\uBFA9\u8EAC\uBFAA" + // 3690 - 3694 + "\u8EAC\uBFAB\u8EAC\uBFAC\u8EAC\uBFAD\u8EAC\uBFAE\u8EAC\uBFAF" + // 3695 - 3699 + "\u8EAC\uBFB0\u8EAC\uBFB1\u8EAC\uBFB2\u8EAC\uBFB3\u8EAC\uBFB4" + // 3700 - 3704 + "\u8EAC\uBFB5\u8EAC\uBFB6\u8EAC\uBFB7\u8EAC\uBFB8\u8EAC\uBFB9" + // 3705 - 3709 + "\u8EAC\uBFBA\u8EAC\uBFBB\u8EAC\uBFBC\u8EAC\uBFBD\u8EAC\uBFBE" + // 3710 - 3714 + "\u8EAC\uBFBF\u8EAC\uBFC0\u8EAC\uBFC1\u8EAC\uBFC2\u8EAC\uBFC3" + // 3715 - 3719 + "\u8EAC\uBFC4\u8EAC\uBFC5\u8EAC\uBFC6\u8EAC\uBFC7\u8EAC\uBFC8" + // 3720 - 3724 + "\u8EAC\uBFC9\u8EAC\uBFCA\u8EAC\uBFCB\u8EAC\uBFCC\u8EAC\uBFCD" + // 3725 - 3729 + "\u8EAC\uBFCE\u8EAC\uBFCF\u8EAC\uBFD0\u8EAC\uBFD1\u8EAC\uBFD2" + // 3730 - 3734 + "\u8EAC\uBFD3\u8EAC\uBFD4\u8EAC\uBFD5\u8EAC\uBFD6\u8EAC\uBFD7" + // 3735 - 3739 + "\u8EAC\uBFD8\u8EAC\uBFD9\u8EAC\uBFDA\u8EAC\uBFDB\u8EAC\uBFDC" + // 3740 - 3744 + "\u8EAC\uBEBB\u8EAC\uBEBC\u8EAC\uBEBD\u8EAC\uBEBE\u8EAC\uBEBF" + // 3745 - 3749 + "\u8EAC\uBEC0\u8EAC\uBEC1\u8EAC\uBEC2\u8EAC\uBEC3\u8EAC\uBEC4" + // 3750 - 3754 + "\u8EAC\uBEC5\u8EAC\uBEC6\u8EAC\uBEC7\u8EAC\uBEC8\u8EAC\uBEC9" + // 3755 - 3759 + "\u8EAC\uBECA\u8EAC\uBECB\u8EAC\uBECC\u8EAC\uBECD\u8EAC\uBECE" + // 3760 - 3764 + "\u8EAC\uBECF\u8EAC\uBED0\u8EAC\uBED1\u8EAC\uBED2\u8EAC\uBED3" + // 3765 - 3769 + "\u8EAC\uBED4\u8EAC\uBED5\u8EAC\uBED6\u8EAC\uBED7\u8EAC\uBED8" + // 3770 - 3774 + "\u8EAC\uBED9\u8EAC\uBEDA\u8EAC\uBEDB\u8EAC\uBEDC\u8EAC\uBEDD" + // 3775 - 3779 + "\u8EAC\uBEDE\u8EAC\uBEDF\u8EAC\uBEE0\u8EAC\uBEE1\u8EAC\uBEE2" + // 3780 - 3784 + "\u8EAC\uBEE3\u8EAC\uBEE4\u8EAC\uBEE5\u8EAC\uBEE6\u8EAC\uBEE7" + // 3785 - 3789 + "\u8EAC\uBEE8\u8EAC\uBEE9\u8EAC\uBEEA\u8EAC\uBEEB\u8EAC\uBEEC" + // 3790 - 3794 + "\u8EAC\uBEED\u8EAC\uBEEE\u8EAC\uBEEF\u8EAC\uBEF0\u8EAC\uBEF1" + // 3795 - 3799 + "\u8EAC\uBEF2\u8EAC\uBEF3\u8EAC\uBEF4\u8EAC\uBEF5\u8EAC\uBEF6" + // 3800 - 3804 + "\u8EAC\uBEF7\u8EAC\uBEF8\u8EAC\uBEF9\u8EAC\uBEFA\u8EAC\uBDD9" + // 3805 - 3809 + "\u8EAC\uBDDA\u8EAC\uBDDB\u8EAC\uBDDC\u8EAC\uBDDD\u8EAC\uBDDE" + // 3810 - 3814 + "\u8EAC\uBDDF\u8EAC\uBDE0\u8EAC\uBDE1\u8EAC\uBDE2\u8EAC\uBDE3" + // 3815 - 3819 + "\u8EAC\uBDE4\u8EAC\uBDE5\u8EAC\uBDE6\u8EAC\uBDE7\u8EAC\uBDE8" + // 3820 - 3824 + "\u8EAC\uBDE9\u8EAC\uBDEA\u8EAC\uBDEB\u8EAC\uBDEC\u8EAC\uBDED" + // 3825 - 3829 + "\u8EAC\uBDEE\u8EAC\uBDEF\u8EAC\uBDF0\u8EAC\uBDF1\u8EAC\uBDF2" + // 3830 - 3834 + "\u8EAC\uBDF3\u8EAC\uBDF4\u8EAC\uBDF5\u8EAC\uBDF6\u8EAC\uBDF7" + // 3835 - 3839 + "\u8EAC\uBDF8\u8EAC\uBDF9\u8EAC\uBDFA\u8EAC\uBDFB\u8EAC\uBDFC" + // 3840 - 3844 + "\u8EAC\uBDFD\u8EAC\uBDFE\u8EAC\uBEA1\u8EAC\uBEA2\u8EAC\uBEA3" + // 3845 - 3849 + "\u8EAC\uBEA4\u8EAC\uBEA5\u8EAC\uBEA6\u8EAC\uBEA7\u8EAC\uBEA8" + // 3850 - 3854 + "\u8EAC\uBEA9\u8EAC\uBEAA\u8EAC\uBEAB\u8EAC\uBEAC\u8EAC\uBEAD" + // 3855 - 3859 + "\u8EAC\uBEAE\u8EAC\uBEAF\u8EAC\uBEB0\u8EAC\uBEB1\u8EAC\uBEB2" + // 3860 - 3864 + "\u8EAC\uBEB3\u8EAC\uBEB4\u8EAC\uBEB5\u8EAC\uBEB6\u8EAC\uBEB7" + // 3865 - 3869 + "\u8EAC\uBEB8\u8EAC\uBEB9\u8EAC\uBEBA\u8EAC\uBCF7\u8EAC\uBCF8" + // 3870 - 3874 + "\u8EAC\uBCF9\u8EAC\uBCFA\u8EAC\uBCFB\u8EAC\uBCFC\u8EAC\uBCFD" + // 3875 - 3879 + "\u8EAC\uBCFE\u8EAC\uBDA1\u8EAC\uBDA2\u8EAC\uBDA3\u8EAC\uBDA4" + // 3880 - 3884 + "\u8EAC\uBDA5\u8EAC\uBDA6\u8EAC\uBDA7\u8EAC\uBDA8\u8EAC\uBDA9" + // 3885 - 3889 + "\u8EAC\uBDAA\u8EAC\uBDAB\u8EAC\uBDAC\u8EAC\uBDAD\u8EAC\uBDAE" + // 3890 - 3894 + "\u8EAC\uBDAF\u8EAC\uBDB0\u8EAC\uBDB1\u8EAC\uBDB2\u8EAC\uBDB3" + // 3895 - 3899 + "\u8EAC\uBDB4\u8EAC\uBDB5\u8EAC\uBDB6\u8EAC\uBDB7\u8EAC\uBDB8" + // 3900 - 3904 + "\u8EAC\uBDB9\u8EAC\uBDBA\u8EAC\uBDBB\u8EAC\uBDBC\u8EAC\uBDBD" + // 3905 - 3909 + "\u8EAC\uBDBE\u8EAC\uBDBF\u8EAC\uBDC0\u8EAC\uBDC1\u8EAC\uBDC2" + // 3910 - 3914 + "\u8EAC\uBDC3\u8EAC\uBDC4\u8EAC\uBDC5\u8EAC\uBDC6\u8EAC\uBDC7" + // 3915 - 3919 + "\u8EAC\uBDC8\u8EAC\uBDC9\u8EAC\uBDCA\u8EAC\uBDCB\u8EAC\uBDCC" + // 3920 - 3924 + "\u8EAC\uBDCD\u8EAC\uBDCE\u8EAC\uBDCF\u8EAC\uBDD0\u8EAC\uBDD1" + // 3925 - 3929 + "\u8EAC\uBDD2\u8EAC\uBDD3\u8EAC\uBDD4\u8EAC\uBDD5\u8EAC\uBDD6" + // 3930 - 3934 + "\u8EAC\uBDD7\u8EAC\uBDD8\u8EAC\uBCB7\u8EAC\uBCB8\u8EAC\uBCB9" + // 3935 - 3939 + "\u8EAC\uBCBA\u8EAC\uBCBB\u8EAC\uBCBC\u8EAC\uBCBD\u8EAC\uBCBE" + // 3940 - 3944 + "\u8EAC\uBCBF\u8EAC\uBCC0\u8EAC\uBCC1\u8EAC\uBCC2\u8EAC\uBCC3" + // 3945 - 3949 + "\u8EAC\uBCC4\u8EAC\uBCC5\u8EAC\uBCC6\u8EAC\uBCC7\u8EAC\uBCC8" + // 3950 - 3954 + "\u8EAC\uBCC9\u8EAC\uBCCA\u8EAC\uBCCB\u8EAC\uBCCC\u8EAC\uBCCD" + // 3955 - 3959 + "\u8EAC\uBCCE\u8EAC\uBCCF\u8EAC\uBCD0\u8EAC\uBCD1\u8EAC\uBCD2" + // 3960 - 3964 + "\u8EAC\uBCD3\u8EAC\uBCD4\u8EAC\uBCD5\u8EAC\uBCD6\u8EAC\uBCD7" + // 3965 - 3969 + "\u8EAC\uBCD8\u8EAC\uBCD9\u8EAC\uBCDA\u8EAC\uBCDB\u8EAC\uBCDC" + // 3970 - 3974 + "\u8EAC\uBCDD\u8EAC\uBCDE\u8EAC\uBCDF\u8EAC\uBCE0\u8EAC\uBCE1" + // 3975 - 3979 + "\u8EAC\uBCE2\u8EAC\uBCE3\u8EAC\uBCE4\u8EAC\uBCE5\u8EAC\uBCE6" + // 3980 - 3984 + "\u8EAC\uBCE7\u8EAC\uBCE8\u8EAC\uBCE9\u8EAC\uBCEA\u8EAC\uBCEB" + // 3985 - 3989 + "\u8EAC\uBCEC\u8EAC\uBCED\u8EAC\uBCEE\u8EAC\uBCEF\u8EAC\uBCF0" + // 3990 - 3994 + "\u8EAC\uBCF1\u8EAC\uBCF2\u8EAC\uBCF3\u8EAC\uBCF4\u8EAC\uBCF5" + // 3995 - 3999 + "\u8EAC\uBCF6\u8EAC\uBBD5\u8EAC\uBBD6\u8EAC\uBBD7\u8EAC\uBBD8" + // 4000 - 4004 + "\u8EAC\uBBD9\u8EAC\uBBDA\u8EAC\uBBDB\u8EAC\uBBDC\u8EAC\uBBDD" + // 4005 - 4009 + "\u8EAC\uBBDE\u8EAC\uBBDF\u8EAC\uBBE0\u8EAC\uBBE1\u8EAC\uBBE2" + // 4010 - 4014 + "\u8EAC\uBBE3\u8EAC\uBBE4\u8EAC\uBBE5\u8EAC\uBBE6\u8EAC\uBBE7" + // 4015 - 4019 + "\u8EAC\uBBE8\u8EAC\uBBE9\u8EAC\uBBEA\u8EAC\uBBEB\u8EAC\uBBEC" + // 4020 - 4024 + "\u8EAC\uBBED\u8EAC\uBBEE\u8EAC\uBBEF\u8EAC\uBBF0\u8EAC\uBBF1" + // 4025 - 4029 + "\u8EAC\uBBF2\u8EAC\uBBF3\u8EAC\uBBF4\u8EAC\uBBF5\u8EAC\uBBF6" + // 4030 - 4034 + "\u8EAC\uBBF7\u8EAC\uBBF8\u8EAC\uBBF9\u8EAC\uBBFA\u8EAC\uBBFB" + // 4035 - 4039 + "\u8EAC\uBBFC\u8EAC\uBBFD\u8EAC\uBBFE\u8EAC\uBCA1\u8EAC\uBCA2" + // 4040 - 4044 + "\u8EAC\uBCA3\u8EAC\uBCA4\u8EAC\uBCA5\u8EAC\uBCA6\u8EAC\uBCA7" + // 4045 - 4049 + "\u8EAC\uBCA8\u8EAC\uBCA9\u8EAC\uBCAA\u8EAC\uBCAB\u8EAC\uBCAC" + // 4050 - 4054 + "\u8EAC\uBCAD\u8EAC\uBCAE\u8EAC\uBCAF\u8EAC\uBCB0\u8EAC\uBCB1" + // 4055 - 4059 + "\u8EAC\uBCB2\u8EAC\uBCB3\u8EAC\uBCB4\u8EAC\uBCB5\u8EAC\uBCB6" + // 4060 - 4064 + "\u8EAC\uBAF3\u8EAC\uBAF4\u8EAC\uBAF5\u8EAC\uBAF6\u8EAC\uBAF7" + // 4065 - 4069 + "\u8EAC\uBAF8\u8EAC\uBAF9\u8EAC\uBAFA\u8EAC\uBAFB\u8EAC\uBAFC" + // 4070 - 4074 + "\u8EAC\uBAFD\u8EAC\uBAFE\u8EAC\uBBA1\u8EAC\uBBA2\u8EAC\uBBA3" + // 4075 - 4079 + "\u8EAC\uBBA4\u8EAC\uBBA5\u8EAC\uBBA6\u8EAC\uBBA7\u8EAC\uBBA8" + // 4080 - 4084 + "\u8EAC\uBBA9\u8EAC\uBBAA\u8EAC\uBBAB\u8EAC\uBBAC\u8EAC\uBBAD" + // 4085 - 4089 + "\u8EAC\uBBAE\u8EAC\uBBAF\u8EAC\uBBB0\u8EAC\uBBB1\u8EAC\uBBB2" + // 4090 - 4094 + "\u8EAC\uBBB3\u8EAC\uBBB4\u8EAC\uBBB5\u8EAC\uBBB6\u8EAC\uBBB7" + // 4095 - 4099 + "\u8EAC\uBBB8\u8EAC\uBBB9\u8EAC\uBBBA\u8EAC\uBBBB\u8EAC\uBBBC" + // 4100 - 4104 + "\u8EAC\uBBBD\u8EAC\uBBBE\u8EAC\uBBBF\u8EAC\uBBC0\u8EAC\uBBC1" + // 4105 - 4109 + "\u8EAC\uBBC2\u8EAC\uBBC3\u8EAC\uBBC4\u8EAC\uBBC5\u8EAC\uBBC6" + // 4110 - 4114 + "\u8EAC\uBBC7\u8EAC\uBBC8\u8EAC\uBBC9\u8EAC\uBBCA\u8EAC\uBBCB" + // 4115 - 4119 + "\u8EAC\uBBCC\u8EAC\uBBCD\u8EAC\uBBCE\u8EAC\uBBCF\u8EAC\uBBD0" + // 4120 - 4124 + "\u8EAC\uBBD1\u8EAC\uBBD2\u8EAC\uBBD3\u8EAC\uBBD4\u8EAC\uBAB3" + // 4125 - 4129 + "\u8EAC\uBAB4\u8EAC\uBAB5\u8EAC\uBAB6\u8EAC\uBAB7\u8EAC\uBAB8" + // 4130 - 4134 + "\u8EAC\uBAB9\u8EAC\uBABA\u8EAC\uBABB\u8EAC\uBABC\u8EAC\uBABD" + // 4135 - 4139 + "\u8EAC\uBABE\u8EAC\uBABF\u8EAC\uBAC0\u8EAC\uBAC1\u8EAC\uBAC2" + // 4140 - 4144 + "\u8EAC\uBAC3\u8EAC\uBAC4\u8EAC\uBAC5\u8EAC\uBAC6\u8EAC\uBAC7" + // 4145 - 4149 + "\u8EAC\uBAC8\u8EAC\uBAC9\u8EAC\uBACA\u8EAC\uBACB\u8EAC\uBACC" + // 4150 - 4154 + "\u8EAC\uBACD\u8EAC\uBACE\u8EAC\uBACF\u8EAC\uBAD0\u8EAC\uBAD1" + // 4155 - 4159 + "\u8EAC\uBAD2\u8EAC\uBAD3\u8EAC\uBAD4\u8EAC\uBAD5\u8EAC\uBAD6" + // 4160 - 4164 + "\u8EAC\uBAD7\u8EAC\uBAD8\u8EAC\uBAD9\u8EAC\uBADA\u8EAC\uBADB" + // 4165 - 4169 + "\u8EAC\uBADC\u8EAC\uBADD\u8EAC\uBADE\u8EAC\uBADF\u8EAC\uBAE0" + // 4170 - 4174 + "\u8EAC\uBAE1\u8EAC\uBAE2\u8EAC\uBAE3\u8EAC\uBAE4\u8EAC\uBAE5" + // 4175 - 4179 + "\u8EAC\uBAE6\u8EAC\uBAE7\u8EAC\uBAE8\u8EAC\uBAE9\u8EAC\uBAEA" + // 4180 - 4184 + "\u8EAC\uBAEB\u8EAC\uBAEC\u8EAC\uBAED\u8EAC\uBAEE\u8EAC\uBAEF" + // 4185 - 4189 + "\u8EAC\uBAF0\u8EAC\uBAF1\u8EAC\uBAF2\u8EAC\uB9D1\u8EAC\uB9D2" + // 4190 - 4194 + "\u8EAC\uB9D3\u8EAC\uB9D4\u8EAC\uB9D5\u8EAC\uB9D6\u8EAC\uB9D7" + // 4195 - 4199 + "\u8EAC\uB9D8\u8EAC\uB9D9\u8EAC\uB9DA\u8EAC\uB9DB\u8EAC\uB9DC" + // 4200 - 4204 + "\u8EAC\uB9DD\u8EAC\uB9DE\u8EAC\uB9DF\u8EAC\uB9E0\u8EAC\uB9E1" + // 4205 - 4209 + "\u8EAC\uB9E2\u8EAC\uB9E3\u8EAC\uB9E4\u8EAC\uB9E5\u8EAC\uB9E6" + // 4210 - 4214 + "\u8EAC\uB9E7\u8EAC\uB9E8\u8EAC\uB9E9\u8EAC\uB9EA\u8EAC\uB9EB" + // 4215 - 4219 + "\u8EAC\uB9EC\u8EAC\uB9ED\u8EAC\uB9EE\u8EAC\uB9EF\u8EAC\uB9F0" + // 4220 - 4224 + "\u8EAC\uB9F1\u8EAC\uB9F2\u8EAC\uB9F3\u8EAC\uB9F4\u8EAC\uB9F5" + // 4225 - 4229 + "\u8EAC\uB9F6\u8EAC\uB9F7\u8EAC\uB9F8\u8EAC\uB9F9\u8EAC\uB9FA" + // 4230 - 4234 + "\u8EAC\uB9FB\u8EAC\uB9FC\u8EAC\uB9FD\u8EAC\uB9FE\u8EAC\uBAA1" + // 4235 - 4239 + "\u8EAC\uBAA2\u8EAC\uBAA3\u8EAC\uBAA4\u8EAC\uBAA5\u8EAC\uBAA6" + // 4240 - 4244 + "\u8EAC\uBAA7\u8EAC\uBAA8\u8EAC\uBAA9\u8EAC\uBAAA\u8EAC\uBAAB" + // 4245 - 4249 + "\u8EAC\uBAAC\u8EAC\uBAAD\u8EAC\uBAAE\u8EAC\uBAAF\u8EAC\uBAB0" + // 4250 - 4254 + "\u8EAC\uBAB1\u8EAC\uBAB2\u8EAC\uB8EF\u8EAC\uB8F0\u8EAC\uB8F1" + // 4255 - 4259 + "\u8EAC\uB8F2\u8EAC\uB8F3\u8EAC\uB8F4\u8EAC\uB8F5\u8EAC\uB8F6" + // 4260 - 4264 + "\u8EAC\uB8F7\u8EAC\uB8F8\u8EAC\uB8F9\u8EAC\uB8FA\u8EAC\uB8FB" + // 4265 - 4269 + "\u8EAC\uB8FC\u8EAC\uB8FD\u8EAC\uB8FE\u8EAC\uB9A1\u8EAC\uB9A2" + // 4270 - 4274 + "\u8EAC\uB9A3\u8EAC\uB9A4\u8EAC\uB9A5\u8EAC\uB9A6\u8EAC\uB9A7" + // 4275 - 4279 + "\u8EAC\uB9A8\u8EAC\uB9A9\u8EAC\uB9AA\u8EAC\uB9AB\u8EAC\uB9AC" + // 4280 - 4284 + "\u8EAC\uB9AD\u8EAC\uB9AE\u8EAC\uB9AF\u8EAC\uB9B0\u8EAC\uB9B1" + // 4285 - 4289 + "\u8EAC\uB9B2\u8EAC\uB9B3\u8EAC\uB9B4\u8EAC\uB9B5\u8EAC\uB9B6" + // 4290 - 4294 + "\u8EAC\uB9B7\u8EAC\uB9B8\u8EAC\uB9B9\u8EAC\uB9BA\u8EAC\uB9BB" + // 4295 - 4299 + "\u8EAC\uB9BC\u8EAC\uB9BD\u8EAC\uB9BE\u8EAC\uB9BF\u8EAC\uB9C0" + // 4300 - 4304 + "\u8EAC\uB9C1\u8EAC\uB9C2\u8EAC\uB9C3\u8EAC\uB9C4\u8EAC\uB9C5" + // 4305 - 4309 + "\u8EAC\uB9C6\u8EAC\uB9C7\u8EAC\uB9C8\u8EAC\uB9C9\u8EAC\uB9CA" + // 4310 - 4314 + "\u8EAC\uB9CB\u8EAC\uB9CC\u8EAC\uB9CD\u8EAC\uB9CE\u8EAC\uB9CF" + // 4315 - 4319 + "\u8EAC\uB9D0\u8EAC\uB8AF\u8EAC\uB8B0\u8EAC\uB8B1\u8EAC\uB8B2" + // 4320 - 4324 + "\u8EAC\uB8B3\u8EAC\uB8B4\u8EAC\uB8B5\u8EAC\uB8B6\u8EAC\uB8B7" + // 4325 - 4329 + "\u8EAC\uB8B8\u8EAC\uB8B9\u8EAC\uB8BA\u8EAC\uB8BB\u8EAC\uB8BC" + // 4330 - 4334 + "\u8EAC\uB8BD\u8EAC\uB8BE\u8EAC\uB8BF\u8EAC\uB8C0\u8EAC\uB8C1" + // 4335 - 4339 + "\u8EAC\uB8C2\u8EAC\uB8C3\u8EAC\uB8C4\u8EAC\uB8C5\u8EAC\uB8C6" + // 4340 - 4344 + "\u8EAC\uB8C7\u8EAC\uB8C8\u8EAC\uB8C9\u8EAC\uB8CA\u8EAC\uB8CB" + // 4345 - 4349 + "\u8EAC\uB8CC\u8EAC\uB8CD\u8EAC\uB8CE\u8EAC\uB8CF\u8EAC\uB8D0" + // 4350 - 4354 + "\u8EAC\uB8D1\u8EAC\uB8D2\u8EAC\uB8D3\u8EAC\uB8D4\u8EAC\uB8D5" + // 4355 - 4359 + "\u8EAC\uB8D6\u8EAC\uB8D7\u8EAC\uB8D8\u8EAC\uB8D9\u8EAC\uB8DA" + // 4360 - 4364 + "\u8EAC\uB8DB\u8EAC\uB8DC\u8EAC\uB8DD\u8EAC\uB8DE\u8EAC\uB8DF" + // 4365 - 4369 + "\u8EAC\uB8E0\u8EAC\uB8E1\u8EAC\uB8E2\u8EAC\uB8E3\u8EAC\uB8E4" + // 4370 - 4374 + "\u8EAC\uB8E5\u8EAC\uB8E6\u8EAC\uB8E7\u8EAC\uB8E8\u8EAC\uB8E9" + // 4375 - 4379 + "\u8EAC\uB8EA\u8EAC\uB8EB\u8EAC\uB8EC\u8EAC\uB8ED\u8EAC\uB8EE" + // 4380 - 4384 + "\u8EAC\uB7CD\u8EAC\uB7CE\u8EAC\uB7CF\u8EAC\uB7D0\u8EAC\uB7D1" + // 4385 - 4389 + "\u8EAC\uB7D2\u8EAC\uB7D3\u8EAC\uB7D4\u8EAC\uB7D5\u8EAC\uB7D6" + // 4390 - 4394 + "\u8EAC\uB7D7\u8EAC\uB7D8\u8EAC\uB7D9\u8EAC\uB7DA\u8EAC\uB7DB" + // 4395 - 4399 + "\u8EAC\uB7DC\u8EAC\uB7DD\u8EAC\uB7DE\u8EAC\uB7DF\u8EAC\uB7E0" + // 4400 - 4404 + "\u8EAC\uB7E1\u8EAC\uB7E2\u8EAC\uB7E3\u8EAC\uB7E4\u8EAC\uB7E5" + // 4405 - 4409 + "\u8EAC\uB7E6\u8EAC\uB7E7\u8EAC\uB7E8\u8EAC\uB7E9\u8EAC\uB7EA" + // 4410 - 4414 + "\u8EAC\uB7EB\u8EAC\uB7EC\u8EAC\uB7ED\u8EAC\uB7EE\u8EAC\uB7EF" + // 4415 - 4419 + "\u8EAC\uB7F0\u8EAC\uB7F1\u8EAC\uB7F2\u8EAC\uB7F3\u8EAC\uB7F4" + // 4420 - 4424 + "\u8EAC\uB7F5\u8EAC\uB7F6\u8EAC\uB7F7\u8EAC\uB7F8\u8EAC\uB7F9" + // 4425 - 4429 + "\u8EAC\uB7FA\u8EAC\uB7FB\u8EAC\uB7FC\u8EAC\uB7FD\u8EAC\uB7FE" + // 4430 - 4434 + "\u8EAC\uB8A1\u8EAC\uB8A2\u8EAC\uB8A3\u8EAC\uB8A4\u8EAC\uB8A5" + // 4435 - 4439 + "\u8EAC\uB8A6\u8EAC\uB8A7\u8EAC\uB8A8\u8EAC\uB8A9\u8EAC\uB8AA" + // 4440 - 4444 + "\u8EAC\uB8AB\u8EAC\uB8AC\u8EAC\uB8AD\u8EAC\uB8AE\u8EAC\uB6EB" + // 4445 - 4449 + "\u8EAC\uB6EC\u8EAC\uB6ED\u8EAC\uB6EE\u8EAC\uB6EF\u8EAC\uB6F0" + // 4450 - 4454 + "\u8EAC\uB6F1\u8EAC\uB6F2\u8EAC\uB6F3\u8EAC\uB6F4\u8EAC\uB6F5" + // 4455 - 4459 + "\u8EAC\uB6F6\u8EAC\uB6F7\u8EAC\uB6F8\u8EAC\uB6F9\u8EAC\uB6FA" + // 4460 - 4464 + "\u8EAC\uB6FB\u8EAC\uB6FC\u8EAC\uB6FD\u8EAC\uB6FE\u8EAC\uB7A1" + // 4465 - 4469 + "\u8EAC\uB7A2\u8EAC\uB7A3\u8EAC\uB7A4\u8EAC\uB7A5\u8EAC\uB7A6" + // 4470 - 4474 + "\u8EAC\uB7A7\u8EAC\uB7A8\u8EAC\uB7A9\u8EAC\uB7AA\u8EAC\uB7AB" + // 4475 - 4479 + "\u8EAC\uB7AC\u8EAC\uB7AD\u8EAC\uB7AE\u8EAC\uB7AF\u8EAC\uB7B0" + // 4480 - 4484 + "\u8EAC\uB7B1\u8EAC\uB7B2\u8EAC\uB7B3\u8EAC\uB7B4\u8EAC\uB7B5" + // 4485 - 4489 + "\u8EAC\uB7B6\u8EAC\uB7B7\u8EAC\uB7B8\u8EAC\uB7B9\u8EAC\uB7BA" + // 4490 - 4494 + "\u8EAC\uB7BB\u8EAC\uB7BC\u8EAC\uB7BD\u8EAC\uB7BE\u8EAC\uB7BF" + // 4495 - 4499 + "\u8EAC\uB7C0\u8EAC\uB7C1\u8EAC\uB7C2\u8EAC\uB7C3\u8EAC\uB7C4" + // 4500 - 4504 + "\u8EAC\uB7C5\u8EAC\uB7C6\u8EAC\uB7C7\u8EAC\uB7C8\u8EAC\uB7C9" + // 4505 - 4509 + "\u8EAC\uB7CA\u8EAC\uB7CB\u8EAC\uB7CC\u8EAC\uB6AB\u8EAC\uB6AC" + // 4510 - 4514 + "\u8EAC\uB6AD\u8EAC\uB6AE\u8EAC\uB6AF\u8EAC\uB6B0\u8EAC\uB6B1" + // 4515 - 4519 + "\u8EAC\uB6B2\u8EAC\uB6B3\u8EAC\uB6B4\u8EAC\uB6B5\u8EAC\uB6B6" + // 4520 - 4524 + "\u8EAC\uB6B7\u8EAC\uB6B8\u8EAC\uB6B9\u8EAC\uB6BA\u8EAC\uB6BB" + // 4525 - 4529 + "\u8EAC\uB6BC\u8EAC\uB6BD\u8EAC\uB6BE\u8EAC\uB6BF\u8EAC\uB6C0" + // 4530 - 4534 + "\u8EAC\uB6C1\u8EAC\uB6C2\u8EAC\uB6C3\u8EAC\uB6C4\u8EAC\uB6C5" + // 4535 - 4539 + "\u8EAC\uB6C6\u8EAC\uB6C7\u8EAC\uB6C8\u8EAC\uB6C9\u8EAC\uB6CA" + // 4540 - 4544 + "\u8EAC\uB6CB\u8EAC\uB6CC\u8EAC\uB6CD\u8EAC\uB6CE\u8EAC\uB6CF" + // 4545 - 4549 + "\u8EAC\uB6D0\u8EAC\uB6D1\u8EAC\uB6D2\u8EAC\uB6D3\u8EAC\uB6D4" + // 4550 - 4554 + "\u8EAC\uB6D5\u8EAC\uB6D6\u8EAC\uB6D7\u8EAC\uB6D8\u8EAC\uB6D9" + // 4555 - 4559 + "\u8EAC\uB6DA\u8EAC\uB6DB\u8EAC\uB6DC\u8EAC\uB6DD\u8EAC\uB6DE" + // 4560 - 4564 + "\u8EAC\uB6DF\u8EAC\uB6E0\u8EAC\uB6E1\u8EAC\uB6E2\u8EAC\uB6E3" + // 4565 - 4569 + "\u8EAC\uB6E4\u8EAC\uB6E5\u8EAC\uB6E6\u8EAC\uB6E7\u8EAC\uB6E8" + // 4570 - 4574 + "\u8EAC\uB6E9\u8EAC\uB6EA\u8EAC\uB5C9\u8EAC\uB5CA\u8EAC\uB5CB" + // 4575 - 4579 + "\u8EAC\uB5CC\u8EAC\uB5CD\u8EAC\uB5CE\u8EAC\uB5CF\u8EAC\uB5D0" + // 4580 - 4584 + "\u8EAC\uB5D1\u8EAC\uB5D2\u8EAC\uB5D3\u8EAC\uB5D4\u8EAC\uB5D5" + // 4585 - 4589 + "\u8EAC\uB5D6\u8EAC\uB5D7\u8EAC\uB5D8\u8EAC\uB5D9\u8EAC\uB5DA" + // 4590 - 4594 + "\u8EAC\uB5DB\u8EAC\uB5DC\u8EAC\uB5DD\u8EAC\uB5DE\u8EAC\uB5DF" + // 4595 - 4599 + "\u8EAC\uB5E0\u8EAC\uB5E1\u8EAC\uB5E2\u8EAC\uB5E3\u8EAC\uB5E4" + // 4600 - 4604 + "\u8EAC\uB5E5\u8EAC\uB5E6\u8EAC\uB5E7\u8EAC\uB5E8\u8EAC\uB5E9" + // 4605 - 4609 + "\u8EAC\uB5EA\u8EAC\uB5EB\u8EAC\uB5EC\u8EAC\uB5ED\u8EAC\uB5EE" + // 4610 - 4614 + "\u8EAC\uB5EF\u8EAC\uB5F0\u8EAC\uB5F1\u8EAC\uB5F2\u8EAC\uB5F3" + // 4615 - 4619 + "\u8EAC\uB5F4\u8EAC\uB5F5\u8EAC\uB5F6\u8EAC\uB5F7\u8EAC\uB5F8" + // 4620 - 4624 + "\u8EAC\uB5F9\u8EAC\uB5FA\u8EAC\uB5FB\u8EAC\uB5FC\u8EAC\uB5FD" + // 4625 - 4629 + "\u8EAC\uB5FE\u8EAC\uB6A1\u8EAC\uB6A2\u8EAC\uB6A3\u8EAC\uB6A4" + // 4630 - 4634 + "\u8EAC\uB6A5\u8EAC\uB6A6\u8EAC\uB6A7\u8EAC\uB6A8\u8EAC\uB6A9" + // 4635 - 4639 + "\u8EAC\uB6AA\u8EAC\uB4E7\u8EAC\uB4E8\u8EAC\uB4E9\u8EAC\uB4EA" + // 4640 - 4644 + "\u8EAC\uB4EB\u8EAC\uB4EC\u8EAC\uB4ED\u8EAC\uB4EE\u8EAC\uB4EF" + // 4645 - 4649 + "\u8EAC\uB4F0\u8EAC\uB4F1\u8EAC\uB4F2\u8EAC\uB4F3\u8EAC\uB4F4" + // 4650 - 4654 + "\u8EAC\uB4F5\u8EAC\uB4F6\u8EAC\uB4F7\u8EAC\uB4F8\u8EAC\uB4F9" + // 4655 - 4659 + "\u8EAC\uB4FA\u8EAC\uB4FB\u8EAC\uB4FC\u8EAC\uB4FD\u8EAC\uB4FE" + // 4660 - 4664 + "\u8EAC\uB5A1\u8EAC\uB5A2\u8EAC\uB5A3\u8EAC\uB5A4\u8EAC\uB5A5" + // 4665 - 4669 + "\u8EAC\uB5A6\u8EAC\uB5A7\u8EAC\uB5A8\u8EAC\uB5A9\u8EAC\uB5AA" + // 4670 - 4674 + "\u8EAC\uB5AB\u8EAC\uB5AC\u8EAC\uB5AD\u8EAC\uB5AE\u8EAC\uB5AF" + // 4675 - 4679 + "\u8EAC\uB5B0\u8EAC\uB5B1\u8EAC\uB5B2\u8EAC\uB5B3\u8EAC\uB5B4" + // 4680 - 4684 + "\u8EAC\uB5B5\u8EAC\uB5B6\u8EAC\uB5B7\u8EAC\uB5B8\u8EAC\uB5B9" + // 4685 - 4689 + "\u8EAC\uB5BA\u8EAC\uB5BB\u8EAC\uB5BC\u8EAC\uB5BD\u8EAC\uB5BE" + // 4690 - 4694 + "\u8EAC\uB5BF\u8EAC\uB5C0\u8EAC\uB5C1\u8EAC\uB5C2\u8EAC\uB5C3" + // 4695 - 4699 + "\u8EAC\uB5C4\u8EAC\uB5C5\u8EAC\uB5C6\u8EAC\uB5C7\u8EAC\uB5C8" + // 4700 - 4704 + "\u8EAC\uB4A7\u8EAC\uB4A8\u8EAC\uB4A9\u8EAC\uB4AA\u8EAC\uB4AB" + // 4705 - 4709 + "\u8EAC\uB4AC\u8EAC\uB4AD\u8EAC\uB4AE\u8EAC\uB4AF\u8EAC\uB4B0" + // 4710 - 4714 + "\u8EAC\uB4B1\u8EAC\uB4B2\u8EAC\uB4B3\u8EAC\uB4B4\u8EAC\uB4B5" + // 4715 - 4719 + "\u8EAC\uB4B6\u8EAC\uB4B7\u8EAC\uB4B8\u8EAC\uB4B9\u8EAC\uB4BA" + // 4720 - 4724 + "\u8EAC\uB4BB\u8EAC\uB4BC\u8EAC\uB4BD\u8EAC\uB4BE\u8EAC\uB4BF" + // 4725 - 4729 + "\u8EAC\uB4C0\u8EAC\uB4C1\u8EAC\uB4C2\u8EAC\uB4C3\u8EAC\uB4C4" + // 4730 - 4734 + "\u8EAC\uB4C5\u8EAC\uB4C6\u8EAC\uB4C7\u8EAC\uB4C8\u8EAC\uB4C9" + // 4735 - 4739 + "\u8EAC\uB4CA\u8EAC\uB4CB\u8EAC\uB4CC\u8EAC\uB4CD\u8EAC\uB4CE" + // 4740 - 4744 + "\u8EAC\uB4CF\u8EAC\uB4D0\u8EAC\uB4D1\u8EAC\uB4D2\u8EAC\uB4D3" + // 4745 - 4749 + "\u8EAC\uB4D4\u8EAC\uB4D5\u8EAC\uB4D6\u8EAC\uB4D7\u8EAC\uB4D8" + // 4750 - 4754 + "\u8EAC\uB4D9\u8EAC\uB4DA\u8EAC\uB4DB\u8EAC\uB4DC\u8EAC\uB4DD" + // 4755 - 4759 + "\u8EAC\uB4DE\u8EAC\uB4DF\u8EAC\uB4E0\u8EAC\uB4E1\u8EAC\uB4E2" + // 4760 - 4764 + "\u8EAC\uB4E3\u8EAC\uB4E4\u8EAC\uB4E5\u8EAC\uB4E6\u8EAC\uB3C5" + // 4765 - 4769 + "\u8EAC\uB3C6\u8EAC\uB3C7\u8EAC\uB3C8\u8EAC\uB3C9\u8EAC\uB3CA" + // 4770 - 4774 + "\u8EAC\uB3CB\u8EAC\uB3CC\u8EAC\uB3CD\u8EAC\uB3CE\u8EAC\uB3CF" + // 4775 - 4779 + "\u8EAC\uB3D0\u8EAC\uB3D1\u8EAC\uB3D2\u8EAC\uB3D3\u8EAC\uB3D4" + // 4780 - 4784 + "\u8EAC\uB3D5\u8EAC\uB3D6\u8EAC\uB3D7\u8EAC\uB3D8\u8EAC\uB3D9" + // 4785 - 4789 + "\u8EAC\uB3DA\u8EAC\uB3DB\u8EAC\uB3DC\u8EAC\uB3DD\u8EAC\uB3DE" + // 4790 - 4794 + "\u8EAC\uB3DF\u8EAC\uB3E0\u8EAC\uB3E1\u8EAC\uB3E2\u8EAC\uB3E3" + // 4795 - 4799 + "\u8EAC\uB3E4\u8EAC\uB3E5\u8EAC\uB3E6\u8EAC\uB3E7\u8EAC\uB3E8" + // 4800 - 4804 + "\u8EAC\uB3E9\u8EAC\uB3EA\u8EAC\uB3EB\u8EAC\uB3EC\u8EAC\uB3ED" + // 4805 - 4809 + "\u8EAC\uB3EE\u8EAC\uB3EF\u8EAC\uB3F0\u8EAC\uB3F1\u8EAC\uB3F2" + // 4810 - 4814 + "\u8EAC\uB3F3\u8EAC\uB3F4\u8EAC\uB3F5\u8EAC\uB3F6\u8EAC\uB3F7" + // 4815 - 4819 + "\u8EAC\uB3F8\u8EAC\uB3F9\u8EAC\uB3FA\u8EAC\uB3FB\u8EAC\uB3FC" + // 4820 - 4824 + "\u8EAC\uB3FD\u8EAC\uB3FE\u8EAC\uB4A1\u8EAC\uB4A2\u8EAC\uB4A3" + // 4825 - 4829 + "\u8EAC\uB4A4\u8EAC\uB4A5\u8EAC\uB4A6\u8EAC\uB2E3\u8EAC\uB2E4" + // 4830 - 4834 + "\u8EAC\uB2E5\u8EAC\uB2E6\u8EAC\uB2E7\u8EAC\uB2E8\u8EAC\uB2E9" + // 4835 - 4839 + "\u8EAC\uB2EA\u8EAC\uB2EB\u8EAC\uB2EC\u8EAC\uB2ED\u8EAC\uB2EE" + // 4840 - 4844 + "\u8EAC\uB2EF\u8EAC\uB2F0\u8EAC\uB2F1\u8EAC\uB2F2\u8EAC\uB2F3" + // 4845 - 4849 + "\u8EAC\uB2F4\u8EAC\uB2F5\u8EAC\uB2F6\u8EAC\uB2F7\u8EAC\uB2F8" + // 4850 - 4854 + "\u8EAC\uB2F9\u8EAC\uB2FA\u8EAC\uB2FB\u8EAC\uB2FC\u8EAC\uB2FD" + // 4855 - 4859 + "\u8EAC\uB2FE\u8EAC\uB3A1\u8EAC\uB3A2\u8EAC\uB3A3\u8EAC\uB3A4" + // 4860 - 4864 + "\u8EAC\uB3A5\u8EAC\uB3A6\u8EAC\uB3A7\u8EAC\uB3A8\u8EAC\uB3A9" + // 4865 - 4869 + "\u8EAC\uB3AA\u8EAC\uB3AB\u8EAC\uB3AC\u8EAC\uB3AD\u8EAC\uB3AE" + // 4870 - 4874 + "\u8EAC\uB3AF\u8EAC\uB3B0\u8EAC\uB3B1\u8EAC\uB3B2\u8EAC\uB3B3" + // 4875 - 4879 + "\u8EAC\uB3B4\u8EAC\uB3B5\u8EAC\uB3B6\u8EAC\uB3B7\u8EAC\uB3B8" + // 4880 - 4884 + "\u8EAC\uB3B9\u8EAC\uB3BA\u8EAC\uB3BB\u8EAC\uB3BC\u8EAC\uB3BD" + // 4885 - 4889 + "\u8EAC\uB3BE\u8EAC\uB3BF\u8EAC\uB3C0\u8EAC\uB3C1\u8EAC\uB3C2" + // 4890 - 4894 + "\u8EAC\uB3C3\u8EAC\uB3C4\u8EAC\uB2A3\u8EAC\uB2A4\u8EAC\uB2A5" + // 4895 - 4899 + "\u8EAC\uB2A6\u8EAC\uB2A7\u8EAC\uB2A8\u8EAC\uB2A9\u8EAC\uB2AA" + // 4900 - 4904 + "\u8EAC\uB2AB\u8EAC\uB2AC\u8EAC\uB2AD\u8EAC\uB2AE\u8EAC\uB2AF" + // 4905 - 4909 + "\u8EAC\uB2B0\u8EAC\uB2B1\u8EAC\uB2B2\u8EAC\uB2B3\u8EAC\uB2B4" + // 4910 - 4914 + "\u8EAC\uB2B5\u8EAC\uB2B6\u8EAC\uB2B7\u8EAC\uB2B8\u8EAC\uB2B9" + // 4915 - 4919 + "\u8EAC\uB2BA\u8EAC\uB2BB\u8EAC\uB2BC\u8EAC\uB2BD\u8EAC\uB2BE" + // 4920 - 4924 + "\u8EAC\uB2BF\u8EAC\uB2C0\u8EAC\uB2C1\u8EAC\uB2C2\u8EAC\uB2C3" + // 4925 - 4929 + "\u8EAC\uB2C4\u8EAC\uB2C5\u8EAC\uB2C6\u8EAC\uB2C7\u8EAC\uB2C8" + // 4930 - 4934 + "\u8EAC\uB2C9\u8EAC\uB2CA\u8EAC\uB2CB\u8EAC\uB2CC\u8EAC\uB2CD" + // 4935 - 4939 + "\u8EAC\uB2CE\u8EAC\uB2CF\u8EAC\uB2D0\u8EAC\uB2D1\u8EAC\uB2D2" + // 4940 - 4944 + "\u8EAC\uB2D3\u8EAC\uB2D4\u8EAC\uB2D5\u8EAC\uB2D6\u8EAC\uB2D7" + // 4945 - 4949 + "\u8EAC\uB2D8\u8EAC\uB2D9\u8EAC\uB2DA\u8EAC\uB2DB\u8EAC\uB2DC" + // 4950 - 4954 + "\u8EAC\uB2DD\u8EAC\uB2DE\u8EAC\uB2DF\u8EAC\uB2E0\u8EAC\uB2E1" + // 4955 - 4959 + "\u8EAC\uB2E2\u8EAC\uB1C1\u8EAC\uB1C2\u8EAC\uB1C3\u8EAC\uB1C4" + // 4960 - 4964 + "\u8EAC\uB1C5\u8EAC\uB1C6\u8EAC\uB1C7\u8EAC\uB1C8\u8EAC\uB1C9" + // 4965 - 4969 + "\u8EAC\uB1CA\u8EAC\uB1CB\u8EAC\uB1CC\u8EAC\uB1CD\u8EAC\uB1CE" + // 4970 - 4974 + "\u8EAC\uB1CF\u8EAC\uB1D0\u8EAC\uB1D1\u8EAC\uB1D2\u8EAC\uB1D3" + // 4975 - 4979 + "\u8EAC\uB1D4\u8EAC\uB1D5\u8EAC\uB1D6\u8EAC\uB1D7\u8EAC\uB1D8" + // 4980 - 4984 + "\u8EAC\uB1D9\u8EAC\uB1DA\u8EAC\uB1DB\u8EAC\uB1DC\u8EAC\uB1DD" + // 4985 - 4989 + "\u8EAC\uB1DE\u8EAC\uB1DF\u8EAC\uB1E0\u8EAC\uB1E1\u8EAC\uB1E2" + // 4990 - 4994 + "\u8EAC\uB1E3\u8EAC\uB1E4\u8EAC\uB1E5\u8EAC\uB1E6\u8EAC\uB1E7" + // 4995 - 4999 + "\u8EAC\uB1E8\u8EAC\uB1E9\u8EAC\uB1EA\u8EAC\uB1EB\u8EAC\uB1EC" + // 5000 - 5004 + "\u8EAC\uB1ED\u8EAC\uB1EE\u8EAC\uB1EF\u8EAC\uB1F0\u8EAC\uB1F1" + // 5005 - 5009 + "\u8EAC\uB1F2\u8EAC\uB1F3\u8EAC\uB1F4\u8EAC\uB1F5\u8EAC\uB1F6" + // 5010 - 5014 + "\u8EAC\uB1F7\u8EAC\uB1F8\u8EAC\uB1F9\u8EAC\uB1FA\u8EAC\uB1FB" + // 5015 - 5019 + "\u8EAC\uB1FC\u8EAC\uB1FD\u8EAC\uB1FE\u8EAC\uB2A1\u8EAC\uB2A2" + // 5020 - 5024 + "\u8EAC\uB0DF\u8EAC\uB0E0\u8EAC\uB0E1\u8EAC\uB0E2\u8EAC\uB0E3" + // 5025 - 5029 + "\u8EAC\uB0E4\u8EAC\uB0E5\u8EAC\uB0E6\u8EAC\uB0E7\u8EAC\uB0E8" + // 5030 - 5034 + "\u8EAC\uB0E9\u8EAC\uB0EA\u8EAC\uB0EB\u8EAC\uB0EC\u8EAC\uB0ED" + // 5035 - 5039 + "\u8EAC\uB0EE\u8EAC\uB0EF\u8EAC\uB0F0\u8EAC\uB0F1\u8EAC\uB0F2" + // 5040 - 5044 + "\u8EAC\uB0F3\u8EAC\uB0F4\u8EAC\uB0F5\u8EAC\uB0F6\u8EAC\uB0F7" + // 5045 - 5049 + "\u8EAC\uB0F8\u8EAC\uB0F9\u8EAC\uB0FA\u8EAC\uB0FB\u8EAC\uB0FC" + // 5050 - 5054 + "\u8EAC\uB0FD\u8EAC\uB0FE\u8EAC\uB1A1\u8EAC\uB1A2\u8EAC\uB1A3" + // 5055 - 5059 + "\u8EAC\uB1A4\u8EAC\uB1A5\u8EAC\uB1A6\u8EAC\uB1A7\u8EAC\uB1A8" + // 5060 - 5064 + "\u8EAC\uB1A9\u8EAC\uB1AA\u8EAC\uB1AB\u8EAC\uB1AC\u8EAC\uB1AD" + // 5065 - 5069 + "\u8EAC\uB1AE\u8EAC\uB1AF\u8EAC\uB1B0\u8EAC\uB1B1\u8EAC\uB1B2" + // 5070 - 5074 + "\u8EAC\uB1B3\u8EAC\uB1B4\u8EAC\uB1B5\u8EAC\uB1B6\u8EAC\uB1B7" + // 5075 - 5079 + "\u8EAC\uB1B8\u8EAC\uB1B9\u8EAC\uB1BA\u8EAC\uB1BB\u8EAC\uB1BC" + // 5080 - 5084 + "\u8EAC\uB1BD\u8EAC\uB1BE\u8EAC\uB1BF\u8EAC\uB1C0\u8EAC\uAFFD" + // 5085 - 5089 + "\u8EAC\uAFFE\u8EAC\uB0A1\u8EAC\uB0A2\u8EAC\uB0A3\u8EAC\uB0A4" + // 5090 - 5094 + "\u8EAC\uB0A5\u8EAC\uB0A6\u8EAC\uB0A7\u8EAC\uB0A8\u8EAC\uB0A9" + // 5095 - 5099 + "\u8EAC\uB0AA\u8EAC\uB0AB\u8EAC\uB0AC\u8EAC\uB0AD\u8EAC\uB0AE" + // 5100 - 5104 + "\u8EAC\uB0AF\u8EAC\uB0B0\u8EAC\uB0B1\u8EAC\uB0B2\u8EAC\uB0B3" + // 5105 - 5109 + "\u8EAC\uB0B4\u8EAC\uB0B5\u8EAC\uB0B6\u8EAC\uB0B7\u8EAC\uB0B8" + // 5110 - 5114 + "\u8EAC\uB0B9\u8EAC\uB0BA\u8EAC\uB0BB\u8EAC\uB0BC\u8EAC\uB0BD" + // 5115 - 5119 + "\u8EAC\uB0BE\u8EAC\uB0BF\u8EAC\uB0C0\u8EAC\uB0C1\u8EAC\uB0C2" + // 5120 - 5124 + "\u8EAC\uB0C3\u8EAC\uB0C4\u8EAC\uB0C5\u8EAC\uB0C6\u8EAC\uB0C7" + // 5125 - 5129 + "\u8EAC\uB0C8\u8EAC\uB0C9\u8EAC\uB0CA\u8EAC\uB0CB\u8EAC\uB0CC" + // 5130 - 5134 + "\u8EAC\uB0CD\u8EAC\uB0CE\u8EAC\uB0CF\u8EAC\uB0D0\u8EAC\uB0D1" + // 5135 - 5139 + "\u8EAC\uB0D2\u8EAC\uB0D3\u8EAC\uB0D4\u8EAC\uB0D5\u8EAC\uB0D6" + // 5140 - 5144 + "\u8EAC\uB0D7\u8EAC\uB0D8\u8EAC\uB0D9\u8EAC\uB0DA\u8EAC\uB0DB" + // 5145 - 5149 + "\u8EAC\uB0DC\u8EAC\uB0DD\u8EAC\uB0DE\u8EAC\uAFBD\u8EAC\uAFBE" + // 5150 - 5154 + "\u8EAC\uAFBF\u8EAC\uAFC0\u8EAC\uAFC1\u8EAC\uAFC2\u8EAC\uAFC3" + // 5155 - 5159 + "\u8EAC\uAFC4\u8EAC\uAFC5\u8EAC\uAFC6\u8EAC\uAFC7\u8EAC\uAFC8" + // 5160 - 5164 + "\u8EAC\uAFC9\u8EAC\uAFCA\u8EAC\uAFCB\u8EAC\uAFCC\u8EAC\uAFCD" + // 5165 - 5169 + "\u8EAC\uAFCE\u8EAC\uAFCF\u8EAC\uAFD0\u8EAC\uAFD1\u8EAC\uAFD2" + // 5170 - 5174 + "\u8EAC\uAFD3\u8EAC\uAFD4\u8EAC\uAFD5\u8EAC\uAFD6\u8EAC\uAFD7" + // 5175 - 5179 + "\u8EAC\uAFD8\u8EAC\uAFD9\u8EAC\uAFDA\u8EAC\uAFDB\u8EAC\uAFDC" + // 5180 - 5184 + "\u8EAC\uAFDD\u8EAC\uAFDE\u8EAC\uAFDF\u8EAC\uAFE0\u8EAC\uAFE1" + // 5185 - 5189 + "\u8EAC\uAFE2\u8EAC\uAFE3\u8EAC\uAFE4\u8EAC\uAFE5\u8EAC\uAFE6" + // 5190 - 5194 + "\u8EAC\uAFE7\u8EAC\uAFE8\u8EAC\uAFE9\u8EAC\uAFEA\u8EAC\uAFEB" + // 5195 - 5199 + "\u8EAC\uAFEC\u8EAC\uAFED\u8EAC\uAFEE\u8EAC\uAFEF\u8EAC\uAFF0" + // 5200 - 5204 + "\u8EAC\uAFF1\u8EAC\uAFF2\u8EAC\uAFF3\u8EAC\uAFF4\u8EAC\uAFF5" + // 5205 - 5209 + "\u8EAC\uAFF6\u8EAC\uAFF7\u8EAC\uAFF8\u8EAC\uAFF9\u8EAC\uAFFA" + // 5210 - 5214 + "\u8EAC\uAFFB\u8EAC\uAFFC\u8EAC\uAEDB\u8EAC\uAEDC\u8EAC\uAEDD" + // 5215 - 5219 + "\u8EAC\uAEDE\u8EAC\uAEDF\u8EAC\uAEE0\u8EAC\uAEE1\u8EAC\uAEE2" + // 5220 - 5224 + "\u8EAC\uAEE3\u8EAC\uAEE4\u8EAC\uAEE5\u8EAC\uAEE6\u8EAC\uAEE7" + // 5225 - 5229 + "\u8EAC\uAEE8\u8EAC\uAEE9\u8EAC\uAEEA\u8EAC\uAEEB\u8EAC\uAEEC" + // 5230 - 5234 + "\u8EAC\uAEED\u8EAC\uAEEE\u8EAC\uAEEF\u8EAC\uAEF0\u8EAC\uAEF1" + // 5235 - 5239 + "\u8EAC\uAEF2\u8EAC\uAEF3\u8EAC\uAEF4\u8EAC\uAEF5\u8EAC\uAEF6" + // 5240 - 5244 + "\u8EAC\uAEF7\u8EAC\uAEF8\u8EAC\uAEF9\u8EAC\uAEFA\u8EAC\uAEFB" + // 5245 - 5249 + "\u8EAC\uAEFC\u8EAC\uAEFD\u8EAC\uAEFE\u8EAC\uAFA1\u8EAC\uAFA2" + // 5250 - 5254 + "\u8EAC\uAFA3\u8EAC\uAFA4\u8EAC\uAFA5\u8EAC\uAFA6\u8EAC\uAFA7" + // 5255 - 5259 + "\u8EAC\uAFA8\u8EAC\uAFA9\u8EAC\uAFAA\u8EAC\uAFAB\u8EAC\uAFAC" + // 5260 - 5264 + "\u8EAC\uAFAD\u8EAC\uAFAE\u8EAC\uAFAF\u8EAC\uAFB0\u8EAC\uAFB1" + // 5265 - 5269 + "\u8EAC\uAFB2\u8EAC\uAFB3\u8EAC\uAFB4\u8EAC\uAFB5\u8EAC\uAFB6" + // 5270 - 5274 + "\u8EAC\uAFB7\u8EAC\uAFB8\u8EAC\uAFB9\u8EAC\uAFBA\u8EAC\uAFBB" + // 5275 - 5279 + "\u8EAC\uAFBC\u8EAC\uADF9\u8EAC\uADFA\u8EAC\uADFB\u8EAC\uADFC" + // 5280 - 5284 + "\u8EAC\uADFD\u8EAC\uADFE\u8EAC\uAEA1\u8EAC\uAEA2\u8EAC\uAEA3" + // 5285 - 5289 + "\u8EAC\uAEA4\u8EAC\uAEA5\u8EAC\uAEA6\u8EAC\uAEA7\u8EAC\uAEA8" + // 5290 - 5294 + "\u8EAC\uAEA9\u8EAC\uAEAA\u8EAC\uAEAB\u8EAC\uAEAC\u8EAC\uAEAD" + // 5295 - 5299 + "\u8EAC\uAEAE\u8EAC\uAEAF\u8EAC\uAEB0\u8EAC\uAEB1\u8EAC\uAEB2" + // 5300 - 5304 + "\u8EAC\uAEB3\u8EAC\uAEB4\u8EAC\uAEB5\u8EAC\uAEB6\u8EAC\uAEB7" + // 5305 - 5309 + "\u8EAC\uAEB8\u8EAC\uAEB9\u8EAC\uAEBA\u8EAC\uAEBB\u8EAC\uAEBC" + // 5310 - 5314 + "\u8EAC\uAEBD\u8EAC\uAEBE\u8EAC\uAEBF\u8EAC\uAEC0\u8EAC\uAEC1" + // 5315 - 5319 + "\u8EAC\uAEC2\u8EAC\uAEC3\u8EAC\uAEC4\u8EAC\uAEC5\u8EAC\uAEC6" + // 5320 - 5324 + "\u8EAC\uAEC7\u8EAC\uAEC8\u8EAC\uAEC9\u8EAC\uAECA\u8EAC\uAECB" + // 5325 - 5329 + "\u8EAC\uAECC\u8EAC\uAECD\u8EAC\uAECE\u8EAC\uAECF\u8EAC\uAED0" + // 5330 - 5334 + "\u8EAC\uAED1\u8EAC\uAED2\u8EAC\uAED3\u8EAC\uAED4\u8EAC\uAED5" + // 5335 - 5339 + "\u8EAC\uAED6\u8EAC\uAED7\u8EAC\uAED8\u8EAC\uAED9\u8EAC\uAEDA" + // 5340 - 5344 + "\u8EAC\uADB9\u8EAC\uADBA\u8EAC\uADBB\u8EAC\uADBC\u8EAC\uADBD" + // 5345 - 5349 + "\u8EAC\uADBE\u8EAC\uADBF\u8EAC\uADC0\u8EAC\uADC1\u8EAC\uADC2" + // 5350 - 5354 + "\u8EAC\uADC3\u8EAC\uADC4\u8EAC\uADC5\u8EAC\uADC6\u8EAC\uADC7" + // 5355 - 5359 + "\u8EAC\uADC8\u8EAC\uADC9\u8EAC\uADCA\u8EAC\uADCB\u8EAC\uADCC" + // 5360 - 5364 + "\u8EAC\uADCD\u8EAC\uADCE\u8EAC\uADCF\u8EAC\uADD0\u8EAC\uADD1" + // 5365 - 5369 + "\u8EAC\uADD2\u8EAC\uADD3\u8EAC\uADD4\u8EAC\uADD5\u8EAC\uADD6" + // 5370 - 5374 + "\u8EAC\uADD7\u8EAC\uADD8\u8EAC\uADD9\u8EAC\uADDA\u8EAC\uADDB" + // 5375 - 5379 + "\u8EAC\uADDC\u8EAC\uADDD\u8EAC\uADDE\u8EAC\uADDF\u8EAC\uADE0" + // 5380 - 5384 + "\u8EAC\uADE1\u8EAC\uADE2\u8EAC\uADE3\u8EAC\uADE4\u8EAC\uADE5" + // 5385 - 5389 + "\u8EAC\uADE6\u8EAC\uADE7\u8EAC\uADE8\u8EAC\uADE9\u8EAC\uADEA" + // 5390 - 5394 + "\u8EAC\uADEB\u8EAC\uADEC\u8EAC\uADED\u8EAC\uADEE\u8EAC\uADEF" + // 5395 - 5399 + "\u8EAC\uADF0\u8EAC\uADF1\u8EAC\uADF2\u8EAC\uADF3\u8EAC\uADF4" + // 5400 - 5404 + "\u8EAC\uADF5\u8EAC\uADF6\u8EAC\uADF7\u8EAC\uADF8\u8EAC\uACD7" + // 5405 - 5409 + "\u8EAC\uACD8\u8EAC\uACD9\u8EAC\uACDA\u8EAC\uACDB\u8EAC\uACDC" + // 5410 - 5414 + "\u8EAC\uACDD\u8EAC\uACDE\u8EAC\uACDF\u8EAC\uACE0\u8EAC\uACE1" + // 5415 - 5419 + "\u8EAC\uACE2\u8EAC\uACE3\u8EAC\uACE4\u8EAC\uACE5\u8EAC\uACE6" + // 5420 - 5424 + "\u8EAC\uACE7\u8EAC\uACE8\u8EAC\uACE9\u8EAC\uACEA\u8EAC\uACEB" + // 5425 - 5429 + "\u8EAC\uACEC\u8EAC\uACED\u8EAC\uACEE\u8EAC\uACEF\u8EAC\uACF0" + // 5430 - 5434 + "\u8EAC\uACF1\u8EAC\uACF2\u8EAC\uACF3\u8EAC\uACF4\u8EAC\uACF5" + // 5435 - 5439 + "\u8EAC\uACF6\u8EAC\uACF7\u8EAC\uACF8\u8EAC\uACF9\u8EAC\uACFA" + // 5440 - 5444 + "\u8EAC\uACFB\u8EAC\uACFC\u8EAC\uACFD\u8EAC\uACFE\u8EAC\uADA1" + // 5445 - 5449 + "\u8EAC\uADA2\u8EAC\uADA3\u8EAC\uADA4\u8EAC\uADA5\u8EAC\uADA6" + // 5450 - 5454 + "\u8EAC\uADA7\u8EAC\uADA8\u8EAC\uADA9\u8EAC\uADAA\u8EAC\uADAB" + // 5455 - 5459 + "\u8EAC\uADAC\u8EAC\uADAD\u8EAC\uADAE\u8EAC\uADAF\u8EAC\uADB0" + // 5460 - 5464 + "\u8EAC\uADB1\u8EAC\uADB2\u8EAC\uADB3\u8EAC\uADB4\u8EAC\uADB5" + // 5465 - 5469 + "\u8EAC\uADB6\u8EAC\uADB7\u8EAC\uADB8\u8EAC\uABF5\u8EAC\uABF6" + // 5470 - 5474 + "\u8EAC\uABF7\u8EAC\uABF8\u8EAC\uABF9\u8EAC\uABFA\u8EAC\uABFB" + // 5475 - 5479 + "\u8EAC\uABFC\u8EAC\uABFD\u8EAC\uABFE\u8EAC\uACA1\u8EAC\uACA2" + // 5480 - 5484 + "\u8EAC\uACA3\u8EAC\uACA4\u8EAC\uACA5\u8EAC\uACA6\u8EAC\uACA7" + // 5485 - 5489 + "\u8EAC\uACA8\u8EAC\uACA9\u8EAC\uACAA\u8EAC\uACAB\u8EAC\uACAC" + // 5490 - 5494 + "\u8EAC\uACAD\u8EAC\uACAE\u8EAC\uACAF\u8EAC\uACB0\u8EAC\uACB1" + // 5495 - 5499 + "\u8EAC\uACB2\u8EAC\uACB3\u8EAC\uACB4\u8EAC\uACB5\u8EAC\uACB6" + // 5500 - 5504 + "\u8EAC\uACB7\u8EAC\uACB8\u8EAC\uACB9\u8EAC\uACBA\u8EAC\uACBB" + // 5505 - 5509 + "\u8EAC\uACBC\u8EAC\uACBD\u8EAC\uACBE\u8EAC\uACBF\u8EAC\uACC0" + // 5510 - 5514 + "\u8EAC\uACC1\u8EAC\uACC2\u8EAC\uACC3\u8EAC\uACC4\u8EAC\uACC5" + // 5515 - 5519 + "\u8EAC\uACC6\u8EAC\uACC7\u8EAC\uACC8\u8EAC\uACC9\u8EAC\uACCA" + // 5520 - 5524 + "\u8EAC\uACCB\u8EAC\uACCC\u8EAC\uACCD\u8EAC\uACCE\u8EAC\uACCF" + // 5525 - 5529 + "\u8EAC\uACD0\u8EAC\uACD1\u8EAC\uACD2\u8EAC\uACD3\u8EAC\uACD4" + // 5530 - 5534 + "\u8EAC\uACD5\u8EAC\uACD6\u8EAC\uABB5\u8EAC\uABB6\u8EAC\uABB7" + // 5535 - 5539 + "\u8EAC\uABB8\u8EAC\uABB9\u8EAC\uABBA\u8EAC\uABBB\u8EAC\uABBC" + // 5540 - 5544 + "\u8EAC\uABBD\u8EAC\uABBE\u8EAC\uABBF\u8EAC\uABC0\u8EAC\uABC1" + // 5545 - 5549 + "\u8EAC\uABC2\u8EAC\uABC3\u8EAC\uABC4\u8EAC\uABC5\u8EAC\uABC6" + // 5550 - 5554 + "\u8EAC\uABC7\u8EAC\uABC8\u8EAC\uABC9\u8EAC\uABCA\u8EAC\uABCB" + // 5555 - 5559 + "\u8EAC\uABCC\u8EAC\uABCD\u8EAC\uABCE\u8EAC\uABCF\u8EAC\uABD0" + // 5560 - 5564 + "\u8EAC\uABD1\u8EAC\uABD2\u8EAC\uABD3\u8EAC\uABD4\u8EAC\uABD5" + // 5565 - 5569 + "\u8EAC\uABD6\u8EAC\uABD7\u8EAC\uABD8\u8EAC\uABD9\u8EAC\uABDA" + // 5570 - 5574 + "\u8EAC\uABDB\u8EAC\uABDC\u8EAC\uABDD\u8EAC\uABDE\u8EAC\uABDF" + // 5575 - 5579 + "\u8EAC\uABE0\u8EAC\uABE1\u8EAC\uABE2\u8EAC\uABE3\u8EAC\uABE4" + // 5580 - 5584 + "\u8EAC\uABE5\u8EAC\uABE6\u8EAC\uABE7\u8EAC\uABE8\u8EAC\uABE9" + // 5585 - 5589 + "\u8EAC\uABEA\u8EAC\uABEB\u8EAC\uABEC\u8EAC\uABED\u8EAC\uABEE" + // 5590 - 5594 + "\u8EAC\uABEF\u8EAC\uABF0\u8EAC\uABF1\u8EAC\uABF2\u8EAC\uABF3" + // 5595 - 5599 + "\u8EAC\uABF4\u8EAC\uAAD3\u8EAC\uAAD4\u8EAC\uAAD5\u8EAC\uAAD6" + // 5600 - 5604 + "\u8EAC\uAAD7\u8EAC\uAAD8\u8EAC\uAAD9\u8EAC\uAADA\u8EAC\uAADB" + // 5605 - 5609 + "\u8EAC\uAADC\u8EAC\uAADD\u8EAC\uAADE\u8EAC\uAADF\u8EAC\uAAE0" + // 5610 - 5614 + "\u8EAC\uAAE1\u8EAC\uAAE2\u8EAC\uAAE3\u8EAC\uAAE4\u8EAC\uAAE5" + // 5615 - 5619 + "\u8EAC\uAAE6\u8EAC\uAAE7\u8EAC\uAAE8\u8EAC\uAAE9\u8EAC\uAAEA" + // 5620 - 5624 + "\u8EAC\uAAEB\u8EAC\uAAEC\u8EAC\uAAED\u8EAC\uAAEE\u8EAC\uAAEF" + // 5625 - 5629 + "\u8EAC\uAAF0\u8EAC\uAAF1\u8EAC\uAAF2\u8EAC\uAAF3\u8EAC\uAAF4" + // 5630 - 5634 + "\u8EAC\uAAF5\u8EAC\uAAF6\u8EAC\uAAF7\u8EAC\uAAF8\u8EAC\uAAF9" + // 5635 - 5639 + "\u8EAC\uAAFA\u8EAC\uAAFB\u8EAC\uAAFC\u8EAC\uAAFD\u8EAC\uAAFE" + // 5640 - 5644 + "\u8EAC\uABA1\u8EAC\uABA2\u8EAC\uABA3\u8EAC\uABA4\u8EAC\uABA5" + // 5645 - 5649 + "\u8EAC\uABA6\u8EAC\uABA7\u8EAC\uABA8\u8EAC\uABA9\u8EAC\uABAA" + // 5650 - 5654 + "\u8EAC\uABAB\u8EAC\uABAC\u8EAC\uABAD\u8EAC\uABAE\u8EAC\uABAF" + // 5655 - 5659 + "\u8EAC\uABB0\u8EAC\uABB1\u8EAC\uABB2\u8EAC\uABB3\u8EAC\uABB4" + // 5660 - 5664 + "\u8EAC\uA9F1\u8EAC\uA9F2\u8EAC\uA9F3\u8EAC\uA9F4\u8EAC\uA9F5" + // 5665 - 5669 + "\u8EAC\uA9F6\u8EAC\uA9F7\u8EAC\uA9F8\u8EAC\uA9F9\u8EAC\uA9FA" + // 5670 - 5674 + "\u8EAC\uA9FB\u8EAC\uA9FC\u8EAC\uA9FD\u8EAC\uA9FE\u8EAC\uAAA1" + // 5675 - 5679 + "\u8EAC\uAAA2\u8EAC\uAAA3\u8EAC\uAAA4\u8EAC\uAAA5\u8EAC\uAAA6" + // 5680 - 5684 + "\u8EAC\uAAA7\u8EAC\uAAA8\u8EAC\uAAA9\u8EAC\uAAAA\u8EAC\uAAAB" + // 5685 - 5689 + "\u8EAC\uAAAC\u8EAC\uAAAD\u8EAC\uAAAE\u8EAC\uAAAF\u8EAC\uAAB0" + // 5690 - 5694 + "\u8EAC\uAAB1\u8EAC\uAAB2\u8EAC\uAAB3\u8EAC\uAAB4\u8EAC\uAAB5" + // 5695 - 5699 + "\u8EAC\uAAB6\u8EAC\uAAB7\u8EAC\uAAB8\u8EAC\uAAB9\u8EAC\uAABA" + // 5700 - 5704 + "\u8EAC\uAABB\u8EAC\uAABC\u8EAC\uAABD\u8EAC\uAABE\u8EAC\uAABF" + // 5705 - 5709 + "\u8EAC\uAAC0\u8EAC\uAAC1\u8EAC\uAAC2\u8EAC\uAAC3\u8EAC\uAAC4" + // 5710 - 5714 + "\u8EAC\uAAC5\u8EAC\uAAC6\u8EAC\uAAC7\u8EAC\uAAC8\u8EAC\uAAC9" + // 5715 - 5719 + "\u8EAC\uAACA\u8EAC\uAACB\u8EAC\uAACC\u8EAC\uAACD\u8EAC\uAACE" + // 5720 - 5724 + "\u8EAC\uAACF\u8EAC\uAAD0\u8EAC\uAAD1\u8EAC\uAAD2\u8EAC\uA9B1" + // 5725 - 5729 + "\u8EAC\uA9B2\u8EAC\uA9B3\u8EAC\uA9B4\u8EAC\uA9B5\u8EAC\uA9B6" + // 5730 - 5734 + "\u8EAC\uA9B7\u8EAC\uA9B8\u8EAC\uA9B9\u8EAC\uA9BA\u8EAC\uA9BB" + // 5735 - 5739 + "\u8EAC\uA9BC\u8EAC\uA9BD\u8EAC\uA9BE\u8EAC\uA9BF\u8EAC\uA9C0" + // 5740 - 5744 + "\u8EAC\uA9C1\u8EAC\uA9C2\u8EAC\uA9C3\u8EAC\uA9C4\u8EAC\uA9C5" + // 5745 - 5749 + "\u8EAC\uA9C6\u8EAC\uA9C7\u8EAC\uA9C8\u8EAC\uA9C9\u8EAC\uA9CA" + // 5750 - 5754 + "\u8EAC\uA9CB\u8EAC\uA9CC\u8EAC\uA9CD\u8EAC\uA9CE\u8EAC\uA9CF" + // 5755 - 5759 + "\u8EAC\uA9D0\u8EAC\uA9D1\u8EAC\uA9D2\u8EAC\uA9D3\u8EAC\uA9D4" + // 5760 - 5764 + "\u8EAC\uA9D5\u8EAC\uA9D6\u8EAC\uA9D7\u8EAC\uA9D8\u8EAC\uA9D9" + // 5765 - 5769 + "\u8EAC\uA9DA\u8EAC\uA9DB\u8EAC\uA9DC\u8EAC\uA9DD\u8EAC\uA9DE" + // 5770 - 5774 + "\u8EAC\uA9DF\u8EAC\uA9E0\u8EAC\uA9E1\u8EAC\uA9E2\u8EAC\uA9E3" + // 5775 - 5779 + "\u8EAC\uA9E4\u8EAC\uA9E5\u8EAC\uA9E6\u8EAC\uA9E7\u8EAC\uA9E8" + // 5780 - 5784 + "\u8EAC\uA9E9\u8EAC\uA9EA\u8EAC\uA9EB\u8EAC\uA9EC\u8EAC\uA9ED" + // 5785 - 5789 + "\u8EAC\uA9EE\u8EAC\uA9EF\u8EAC\uA9F0\u8EAC\uA8CF\u8EAC\uA8D0" + // 5790 - 5794 + "\u8EAC\uA8D1\u8EAC\uA8D2\u8EAC\uA8D3\u8EAC\uA8D4\u8EAC\uA8D5" + // 5795 - 5799 + "\u8EAC\uA8D6\u8EAC\uA8D7\u8EAC\uA8D8\u8EAC\uA8D9\u8EAC\uA8DA" + // 5800 - 5804 + "\u8EAC\uA8DB\u8EAC\uA8DC\u8EAC\uA8DD\u8EAC\uA8DE\u8EAC\uA8DF" + // 5805 - 5809 + "\u8EAC\uA8E0\u8EAC\uA8E1\u8EAC\uA8E2\u8EAC\uA8E3\u8EAC\uA8E4" + // 5810 - 5814 + "\u8EAC\uA8E5\u8EAC\uA8E6\u8EAC\uA8E7\u8EAC\uA8E8\u8EAC\uA8E9" + // 5815 - 5819 + "\u8EAC\uA8EA\u8EAC\uA8EB\u8EAC\uA8EC\u8EAC\uA8ED\u8EAC\uA8EE" + // 5820 - 5824 + "\u8EAC\uA8EF\u8EAC\uA8F0\u8EAC\uA8F1\u8EAC\uA8F2\u8EAC\uA8F3" + // 5825 - 5829 + "\u8EAC\uA8F4\u8EAC\uA8F5\u8EAC\uA8F6\u8EAC\uA8F7\u8EAC\uA8F8" + // 5830 - 5834 + "\u8EAC\uA8F9\u8EAC\uA8FA\u8EAC\uA8FB\u8EAC\uA8FC\u8EAC\uA8FD" + // 5835 - 5839 + "\u8EAC\uA8FE\u8EAC\uA9A1\u8EAC\uA9A2\u8EAC\uA9A3\u8EAC\uA9A4" + // 5840 - 5844 + "\u8EAC\uA9A5\u8EAC\uA9A6\u8EAC\uA9A7\u8EAC\uA9A8\u8EAC\uA9A9" + // 5845 - 5849 + "\u8EAC\uA9AA\u8EAC\uA9AB\u8EAC\uA9AC\u8EAC\uA9AD\u8EAC\uA9AE" + // 5850 - 5854 + "\u8EAC\uA9AF\u8EAC\uA9B0\u8EAC\uA7ED\u8EAC\uA7EE\u8EAC\uA7EF" + // 5855 - 5859 + "\u8EAC\uA7F0\u8EAC\uA7F1\u8EAC\uA7F2\u8EAC\uA7F3\u8EAC\uA7F4" + // 5860 - 5864 + "\u8EAC\uA7F5\u8EAC\uA7F6\u8EAC\uA7F7\u8EAC\uA7F8\u8EAC\uA7F9" + // 5865 - 5869 + "\u8EAC\uA7FA\u8EAC\uA7FB\u8EAC\uA7FC\u8EAC\uA7FD\u8EAC\uA7FE" + // 5870 - 5874 + "\u8EAC\uA8A1\u8EAC\uA8A2\u8EAC\uA8A3\u8EAC\uA8A4\u8EAC\uA8A5" + // 5875 - 5879 + "\u8EAC\uA8A6\u8EAC\uA8A7\u8EAC\uA8A8\u8EAC\uA8A9\u8EAC\uA8AA" + // 5880 - 5884 + "\u8EAC\uA8AB\u8EAC\uA8AC\u8EAC\uA8AD\u8EAC\uA8AE\u8EAC\uA8AF" + // 5885 - 5889 + "\u8EAC\uA8B0\u8EAC\uA8B1\u8EAC\uA8B2\u8EAC\uA8B3\u8EAC\uA8B4" + // 5890 - 5894 + "\u8EAC\uA8B5\u8EAC\uA8B6\u8EAC\uA8B7\u8EAC\uA8B8\u8EAC\uA8B9" + // 5895 - 5899 + "\u8EAC\uA8BA\u8EAC\uA8BB\u8EAC\uA8BC\u8EAC\uA8BD\u8EAC\uA8BE" + // 5900 - 5904 + "\u8EAC\uA8BF\u8EAC\uA8C0\u8EAC\uA8C1\u8EAC\uA8C2\u8EAC\uA8C3" + // 5905 - 5909 + "\u8EAC\uA8C4\u8EAC\uA8C5\u8EAC\uA8C6\u8EAC\uA8C7\u8EAC\uA8C8" + // 5910 - 5914 + "\u8EAC\uA8C9\u8EAC\uA8CA\u8EAC\uA8CB\u8EAC\uA8CC\u8EAC\uA8CD" + // 5915 - 5919 + "\u8EAC\uA8CE\u8EAC\uA7AD\u8EAC\uA7AE\u8EAC\uA7AF\u8EAC\uA7B0" + // 5920 - 5924 + "\u8EAC\uA7B1\u8EAC\uA7B2\u8EAC\uA7B3\u8EAC\uA7B4\u8EAC\uA7B5" + // 5925 - 5929 + "\u8EAC\uA7B6\u8EAC\uA7B7\u8EAC\uA7B8\u8EAC\uA7B9\u8EAC\uA7BA" + // 5930 - 5934 + "\u8EAC\uA7BB\u8EAC\uA7BC\u8EAC\uA7BD\u8EAC\uA7BE\u8EAC\uA7BF" + // 5935 - 5939 + "\u8EAC\uA7C0\u8EAC\uA7C1\u8EAC\uA7C2\u8EAC\uA7C3\u8EAC\uA7C4" + // 5940 - 5944 + "\u8EAC\uA7C5\u8EAC\uA7C6\u8EAC\uA7C7\u8EAC\uA7C8\u8EAC\uA7C9" + // 5945 - 5949 + "\u8EAC\uA7CA\u8EAC\uA7CB\u8EAC\uA7CC\u8EAC\uA7CD\u8EAC\uA7CE" + // 5950 - 5954 + "\u8EAC\uA7CF\u8EAC\uA7D0\u8EAC\uA7D1\u8EAC\uA7D2\u8EAC\uA7D3" + // 5955 - 5959 + "\u8EAC\uA7D4\u8EAC\uA7D5\u8EAC\uA7D6\u8EAC\uA7D7\u8EAC\uA7D8" + // 5960 - 5964 + "\u8EAC\uA7D9\u8EAC\uA7DA\u8EAC\uA7DB\u8EAC\uA7DC\u8EAC\uA7DD" + // 5965 - 5969 + "\u8EAC\uA7DE\u8EAC\uA7DF\u8EAC\uA7E0\u8EAC\uA7E1\u8EAC\uA7E2" + // 5970 - 5974 + "\u8EAC\uA7E3\u8EAC\uA7E4\u8EAC\uA7E5\u8EAC\uA7E6\u8EAC\uA7E7" + // 5975 - 5979 + "\u8EAC\uA7E8\u8EAC\uA7E9\u8EAC\uA7EA\u8EAC\uA7EB\u8EAC\uA7EC" + // 5980 - 5984 + "\u8EAC\uA6CB\u8EAC\uA6CC\u8EAC\uA6CD\u8EAC\uA6CE\u8EAC\uA6CF" + // 5985 - 5989 + "\u8EAC\uA6D0\u8EAC\uA6D1\u8EAC\uA6D2\u8EAC\uA6D3\u8EAC\uA6D4" + // 5990 - 5994 + "\u8EAC\uA6D5\u8EAC\uA6D6\u8EAC\uA6D7\u8EAC\uA6D8\u8EAC\uA6D9" + // 5995 - 5999 + "\u8EAC\uA6DA\u8EAC\uA6DB\u8EAC\uA6DC\u8EAC\uA6DD\u8EAC\uA6DE" + // 6000 - 6004 + "\u8EAC\uA6DF\u8EAC\uA6E0\u8EAC\uA6E1\u8EAC\uA6E2\u8EAC\uA6E3" + // 6005 - 6009 + "\u8EAC\uA6E4\u8EAC\uA6E5\u8EAC\uA6E6\u8EAC\uA6E7\u8EAC\uA6E8" + // 6010 - 6014 + "\u8EAC\uA6E9\u8EAC\uA6EA\u8EAC\uA6EB\u8EAC\uA6EC\u8EAC\uA6ED" + // 6015 - 6019 + "\u8EAC\uA6EE\u8EAC\uA6EF\u8EAC\uA6F0\u8EAC\uA6F1\u8EAC\uA6F2" + // 6020 - 6024 + "\u8EAC\uA6F3\u8EAC\uA6F4\u8EAC\uA6F5\u8EAC\uA6F6\u8EAC\uA6F7" + // 6025 - 6029 + "\u8EAC\uA6F8\u8EAC\uA6F9\u8EAC\uA6FA\u8EAC\uA6FB\u8EAC\uA6FC" + // 6030 - 6034 + "\u8EAC\uA6FD\u8EAC\uA6FE\u8EAC\uA7A1\u8EAC\uA7A2\u8EAC\uA7A3" + // 6035 - 6039 + "\u8EAC\uA7A4\u8EAC\uA7A5\u8EAC\uA7A6\u8EAC\uA7A7\u8EAC\uA7A8" + // 6040 - 6044 + "\u8EAC\uA7A9\u8EAC\uA7AA\u8EAC\uA7AB\u8EAC\uA7AC\u8EAC\uA5E9" + // 6045 - 6049 + "\u8EAC\uA5EA\u8EAC\uA5EB\u8EAC\uA5EC\u8EAC\uA5ED\u8EAC\uA5EE" + // 6050 - 6054 + "\u8EAC\uA5EF\u8EAC\uA5F0\u8EAC\uA5F1\u8EAC\uA5F2\u8EAC\uA5F3" + // 6055 - 6059 + "\u8EAC\uA5F4\u8EAC\uA5F5\u8EAC\uA5F6\u8EAC\uA5F7\u8EAC\uA5F8" + // 6060 - 6064 + "\u8EAC\uA5F9\u8EAC\uA5FA\u8EAC\uA5FB\u8EAC\uA5FC\u8EAC\uA5FD" + // 6065 - 6069 + "\u8EAC\uA5FE\u8EAC\uA6A1\u8EAC\uA6A2\u8EAC\uA6A3\u8EAC\uA6A4" + // 6070 - 6074 + "\u8EAC\uA6A5\u8EAC\uA6A6\u8EAC\uA6A7\u8EAC\uA6A8\u8EAC\uA6A9" + // 6075 - 6079 + "\u8EAC\uA6AA\u8EAC\uA6AB\u8EAC\uA6AC\u8EAC\uA6AD\u8EAC\uA6AE" + // 6080 - 6084 + "\u8EAC\uA6AF\u8EAC\uA6B0\u8EAC\uA6B1\u8EAC\uA6B2\u8EAC\uA6B3" + // 6085 - 6089 + "\u8EAC\uA6B4\u8EAC\uA6B5\u8EAC\uA6B6\u8EAC\uA6B7\u8EAC\uA6B8" + // 6090 - 6094 + "\u8EAC\uA6B9\u8EAC\uA6BA\u8EAC\uA6BB\u8EAC\uA6BC\u8EAC\uA6BD" + // 6095 - 6099 + "\u8EAC\uA6BE\u8EAC\uA6BF\u8EAC\uA6C0\u8EAC\uA6C1\u8EAC\uA6C2" + // 6100 - 6104 + "\u8EAC\uA6C3\u8EAC\uA6C4\u8EAC\uA6C5\u8EAC\uA6C6\u8EAC\uA6C7" + // 6105 - 6109 + "\u8EAC\uA6C8\u8EAC\uA6C9\u8EAC\uA6CA\u8EAC\uA5A9\u8EAC\uA5AA" + // 6110 - 6114 + "\u8EAC\uA5AB\u8EAC\uA5AC\u8EAC\uA5AD\u8EAC\uA5AE\u8EAC\uA5AF" + // 6115 - 6119 + "\u8EAC\uA5B0\u8EAC\uA5B1\u8EAC\uA5B2\u8EAC\uA5B3\u8EAC\uA5B4" + // 6120 - 6124 + "\u8EAC\uA5B5\u8EAC\uA5B6\u8EAC\uA5B7\u8EAC\uA5B8\u8EAC\uA5B9" + // 6125 - 6129 + "\u8EAC\uA5BA\u8EAC\uA5BB\u8EAC\uA5BC\u8EAC\uA5BD\u8EAC\uA5BE" + // 6130 - 6134 + "\u8EAC\uA5BF\u8EAC\uA5C0\u8EAC\uA5C1\u8EAC\uA5C2\u8EAC\uA5C3" + // 6135 - 6139 + "\u8EAC\uA5C4\u8EAC\uA5C5\u8EAC\uA5C6\u8EAC\uA5C7\u8EAC\uA5C8" + // 6140 - 6144 + "\u8EAC\uA5C9\u8EAC\uA5CA\u8EAC\uA5CB\u8EAC\uA5CC\u8EAC\uA5CD" + // 6145 - 6149 + "\u8EAC\uA5CE\u8EAC\uA5CF\u8EAC\uA5D0\u8EAC\uA5D1\u8EAC\uA5D2" + // 6150 - 6154 + "\u8EAC\uA5D3\u8EAC\uA5D4\u8EAC\uA5D5\u8EAC\uA5D6\u8EAC\uA5D7" + // 6155 - 6159 + "\u8EAC\uA5D8\u8EAC\uA5D9\u8EAC\uA5DA\u8EAC\uA5DB\u8EAC\uA5DC" + // 6160 - 6164 + "\u8EAC\uA5DD\u8EAC\uA5DE\u8EAC\uA5DF\u8EAC\uA5E0\u8EAC\uA5E1" + // 6165 - 6169 + "\u8EAC\uA5E2\u8EAC\uA5E3\u8EAC\uA5E4\u8EAC\uA5E5\u8EAC\uA5E6" + // 6170 - 6174 + "\u8EAC\uA5E7\u8EAC\uA5E8\u8EAC\uA4C7\u8EAC\uA4C8\u8EAC\uA4C9" + // 6175 - 6179 + "\u8EAC\uA4CA\u8EAC\uA4CB\u8EAC\uA4CC\u8EAC\uA4CD\u8EAC\uA4CE" + // 6180 - 6184 + "\u8EAC\uA4CF\u8EAC\uA4D0\u8EAC\uA4D1\u8EAC\uA4D2\u8EAC\uA4D3" + // 6185 - 6189 + "\u8EAC\uA4D4\u8EAC\uA4D5\u8EAC\uA4D6\u8EAC\uA4D7\u8EAC\uA4D8" + // 6190 - 6194 + "\u8EAC\uA4D9\u8EAC\uA4DA\u8EAC\uA4DB\u8EAC\uA4DC\u8EAC\uA4DD" + // 6195 - 6199 + "\u8EAC\uA4DE\u8EAC\uA4DF\u8EAC\uA4E0\u8EAC\uA4E1\u8EAC\uA4E2" + // 6200 - 6204 + "\u8EAC\uA4E3\u8EAC\uA4E4\u8EAC\uA4E5\u8EAC\uA4E6\u8EAC\uA4E7" + // 6205 - 6209 + "\u8EAC\uA4E8\u8EAC\uA4E9\u8EAC\uA4EA\u8EAC\uA4EB\u8EAC\uA4EC" + // 6210 - 6214 + "\u8EAC\uA4ED\u8EAC\uA4EE\u8EAC\uA4EF\u8EAC\uA4F0\u8EAC\uA4F1" + // 6215 - 6219 + "\u8EAC\uA4F2\u8EAC\uA4F3\u8EAC\uA4F4\u8EAC\uA4F5\u8EAC\uA4F6" + // 6220 - 6224 + "\u8EAC\uA4F7\u8EAC\uA4F8\u8EAC\uA4F9\u8EAC\uA4FA\u8EAC\uA4FB" + // 6225 - 6229 + "\u8EAC\uA4FC\u8EAC\uA4FD\u8EAC\uA4FE\u8EAC\uA5A1\u8EAC\uA5A2" + // 6230 - 6234 + "\u8EAC\uA5A3\u8EAC\uA5A4\u8EAC\uA5A5\u8EAC\uA5A6\u8EAC\uA5A7" + // 6235 - 6239 + "\u8EAC\uA5A8\u8EAC\uA3E5\u8EAC\uA3E6\u8EAC\uA3E7\u8EAC\uA3E8" + // 6240 - 6244 + "\u8EAC\uA3E9\u8EAC\uA3EA\u8EAC\uA3EB\u8EAC\uA3EC\u8EAC\uA3ED" + // 6245 - 6249 + "\u8EAC\uA3EE\u8EAC\uA3EF\u8EAC\uA3F0\u8EAC\uA3F1\u8EAC\uA3F2" + // 6250 - 6254 + "\u8EAC\uA3F3\u8EAC\uA3F4\u8EAC\uA3F5\u8EAC\uA3F6\u8EAC\uA3F7" + // 6255 - 6259 + "\u8EAC\uA3F8\u8EAC\uA3F9\u8EAC\uA3FA\u8EAC\uA3FB\u8EAC\uA3FC" + // 6260 - 6264 + "\u8EAC\uA3FD\u8EAC\uA3FE\u8EAC\uA4A1\u8EAC\uA4A2\u8EAC\uA4A3" + // 6265 - 6269 + "\u8EAC\uA4A4\u8EAC\uA4A5\u8EAC\uA4A6\u8EAC\uA4A7\u8EAC\uA4A8" + // 6270 - 6274 + "\u8EAC\uA4A9\u8EAC\uA4AA\u8EAC\uA4AB\u8EAC\uA4AC\u8EAC\uA4AD" + // 6275 - 6279 + "\u8EAC\uA4AE\u8EAC\uA4AF\u8EAC\uA4B0\u8EAC\uA4B1\u8EAC\uA4B2" + // 6280 - 6284 + "\u8EAC\uA4B3\u8EAC\uA4B4\u8EAC\uA4B5\u8EAC\uA4B6\u8EAC\uA4B7" + // 6285 - 6289 + "\u8EAC\uA4B8\u8EAC\uA4B9\u8EAC\uA4BA\u8EAC\uA4BB\u8EAC\uA4BC" + // 6290 - 6294 + "\u8EAC\uA4BD\u8EAC\uA4BE\u8EAC\uA4BF\u8EAC\uA4C0\u8EAC\uA4C1" + // 6295 - 6299 + "\u8EAC\uA4C2\u8EAC\uA4C3\u8EAC\uA4C4\u8EAC\uA4C5\u8EAC\uA4C6" + // 6300 - 6304 + "\u8EAC\uA3A5\u8EAC\uA3A6\u8EAC\uA3A7\u8EAC\uA3A8\u8EAC\uA3A9" + // 6305 - 6309 + "\u8EAC\uA3AA\u8EAC\uA3AB\u8EAC\uA3AC\u8EAC\uA3AD\u8EAC\uA3AE" + // 6310 - 6314 + "\u8EAC\uA3AF\u8EAC\uA3B0\u8EAC\uA3B1\u8EAC\uA3B2\u8EAC\uA3B3" + // 6315 - 6319 + "\u8EAC\uA3B4\u8EAC\uA3B5\u8EAC\uA3B6\u8EAC\uA3B7\u8EAC\uA3B8" + // 6320 - 6324 + "\u8EAC\uA3B9\u8EAC\uA3BA\u8EAC\uA3BB\u8EAC\uA3BC\u8EAC\uA3BD" + // 6325 - 6329 + "\u8EAC\uA3BE\u8EAC\uA3BF\u8EAC\uA3C0\u8EAC\uA3C1\u8EAC\uA3C2" + // 6330 - 6334 + "\u8EAC\uA3C3\u8EAC\uA3C4\u8EAC\uA3C5\u8EAC\uA3C6\u8EAC\uA3C7" + // 6335 - 6339 + "\u8EAC\uA3C8\u8EAC\uA3C9\u8EAC\uA3CA\u8EAC\uA3CB\u8EAC\uA3CC" + // 6340 - 6344 + "\u8EAC\uA3CD\u8EAC\uA3CE\u8EAC\uA3CF\u8EAC\uA3D0\u8EAC\uA3D1" + // 6345 - 6349 + "\u8EAC\uA3D2\u8EAC\uA3D3\u8EAC\uA3D4\u8EAC\uA3D5\u8EAC\uA3D6" + // 6350 - 6354 + "\u8EAC\uA3D7\u8EAC\uA3D8\u8EAC\uA3D9\u8EAC\uA3DA\u8EAC\uA3DB" + // 6355 - 6359 + "\u8EAC\uA3DC\u8EAC\uA3DD\u8EAC\uA3DE\u8EAC\uA3DF\u8EAC\uA3E0" + // 6360 - 6364 + "\u8EAC\uA3E1\u8EAC\uA3E2\u8EAC\uA3E3\u8EAC\uA3E4\u8EAC\uA2C3" + // 6365 - 6369 + "\u8EAC\uA2C4\u8EAC\uA2C5\u8EAC\uA2C6\u8EAC\uA2C7\u8EAC\uA2C8" + // 6370 - 6374 + "\u8EAC\uA2C9\u8EAC\uA2CA\u8EAC\uA2CB\u8EAC\uA2CC\u8EAC\uA2CD" + // 6375 - 6379 + "\u8EAC\uA2CE\u8EAC\uA2CF\u8EAC\uA2D0\u8EAC\uA2D1\u8EAC\uA2D2" + // 6380 - 6384 + "\u8EAC\uA2D3\u8EAC\uA2D4\u8EAC\uA2D5\u8EAC\uA2D6\u8EAC\uA2D7" + // 6385 - 6389 + "\u8EAC\uA2D8\u8EAC\uA2D9\u8EAC\uA2DA\u8EAC\uA2DB\u8EAC\uA2DC" + // 6390 - 6394 + "\u8EAC\uA2DD\u8EAC\uA2DE\u8EAC\uA2DF\u8EAC\uA2E0\u8EAC\uA2E1" + // 6395 - 6399 + "\u8EAC\uA2E2\u8EAC\uA2E3\u8EAC\uA2E4\u8EAC\uA2E5\u8EAC\uA2E6" + // 6400 - 6404 + "\u8EAC\uA2E7\u8EAC\uA2E8\u8EAC\uA2E9\u8EAC\uA2EA\u8EAC\uA2EB" + // 6405 - 6409 + "\u8EAC\uA2EC\u8EAC\uA2ED\u8EAC\uA2EE\u8EAC\uA2EF\u8EAC\uA2F0" + // 6410 - 6414 + "\u8EAC\uA2F1\u8EAC\uA2F2\u8EAC\uA2F3\u8EAC\uA2F4\u8EAC\uA2F5" + // 6415 - 6419 + "\u8EAC\uA2F6\u8EAC\uA2F7\u8EAC\uA2F8\u8EAC\uA2F9\u8EAC\uA2FA" + // 6420 - 6424 + "\u8EAC\uA2FB\u8EAC\uA2FC\u8EAC\uA2FD\u8EAC\uA2FE\u8EAC\uA3A1" + // 6425 - 6429 + "\u8EAC\uA3A2\u8EAC\uA3A3\u8EAC\uA3A4\u8EAC\uA1E1\u8EAC\uA1E2" + // 6430 - 6434 + "\u8EAC\uA1E3\u8EAC\uA1E4\u8EAC\uA1E5\u8EAC\uA1E6\u8EAC\uA1E7" + // 6435 - 6439 + "\u8EAC\uA1E8\u8EAC\uA1E9\u8EAC\uA1EA\u8EAC\uA1EB\u8EAC\uA1EC" + // 6440 - 6444 + "\u8EAC\uA1ED\u8EAC\uA1EE\u8EAC\uA1EF\u8EAC\uA1F0\u8EAC\uA1F1" + // 6445 - 6449 + "\u8EAC\uA1F2\u8EAC\uA1F3\u8EAC\uA1F4\u8EAC\uA1F5\u8EAC\uA1F6" + // 6450 - 6454 + "\u8EAC\uA1F7\u8EAC\uA1F8\u8EAC\uA1F9\u8EAC\uA1FA\u8EAC\uA1FB" + // 6455 - 6459 + "\u8EAC\uA1FC\u8EAC\uA1FD\u8EAC\uA1FE\u8EAC\uA2A1\u8EAC\uA2A2" + // 6460 - 6464 + "\u8EAC\uA2A3\u8EAC\uA2A4\u8EAC\uA2A5\u8EAC\uA2A6\u8EAC\uA2A7" + // 6465 - 6469 + "\u8EAC\uA2A8\u8EAC\uA2A9\u8EAC\uA2AA\u8EAC\uA2AB\u8EAC\uA2AC" + // 6470 - 6474 + "\u8EAC\uA2AD\u8EAC\uA2AE\u8EAC\uA2AF\u8EAC\uA2B0\u8EAC\uA2B1" + // 6475 - 6479 + "\u8EAC\uA2B2\u8EAC\uA2B3\u8EAC\uA2B4\u8EAC\uA2B5\u8EAC\uA2B6" + // 6480 - 6484 + "\u8EAC\uA2B7\u8EAC\uA2B8\u8EAC\uA2B9\u8EAC\uA2BA\u8EAC\uA2BB" + // 6485 - 6489 + "\u8EAC\uA2BC\u8EAC\uA2BD\u8EAC\uA2BE\u8EAC\uA2BF\u8EAC\uA2C0" + // 6490 - 6494 + "\u8EAC\uA2C1\u8EAC\uA2C2\u8EAC\uA1A1\u8EAC\uA1A2\u8EAC\uA1A3" + // 6495 - 6499 + "\u8EAC\uA1A4\u8EAC\uA1A5\u8EAC\uA1A6\u8EAC\uA1A7\u8EAC\uA1A8" + // 6500 - 6504 + "\u8EAC\uA1A9\u8EAC\uA1AA\u8EAC\uA1AB\u8EAC\uA1AC\u8EAC\uA1AD" + // 6505 - 6509 + "\u8EAC\uA1AE\u8EAC\uA1AF\u8EAC\uA1B0\u8EAC\uA1B1\u8EAC\uA1B2" + // 6510 - 6514 + "\u8EAC\uA1B3\u8EAC\uA1B4\u8EAC\uA1B5\u8EAC\uA1B6\u8EAC\uA1B7" + // 6515 - 6519 + "\u8EAC\uA1B8\u8EAC\uA1B9\u8EAC\uA1BA\u8EAC\uA1BB\u8EAC\uA1BC" + // 6520 - 6524 + "\u8EAC\uA1BD\u8EAC\uA1BE\u8EAC\uA1BF\u8EAC\uA1C0\u8EAC\uA1C1" + // 6525 - 6529 + "\u8EAC\uA1C2\u8EAC\uA1C3\u8EAC\uA1C4\u8EAC\uA1C5\u8EAC\uA1C6" + // 6530 - 6534 + "\u8EAC\uA1C7\u8EAC\uA1C8\u8EAC\uA1C9\u8EAC\uA1CA\u8EAC\uA1CB" + // 6535 - 6539 + "\u8EAC\uA1CC\u8EAC\uA1CD\u8EAC\uA1CE\u8EAC\uA1CF\u8EAC\uA1D0" + // 6540 - 6544 + "\u8EAC\uA1D1\u8EAC\uA1D2\u8EAC\uA1D3\u8EAC\uA1D4\u8EAC\uA1D5" + // 6545 - 6549 + "\u8EAC\uA1D6\u8EAC\uA1D7\u8EAC\uA1D8\u8EAC\uA1D9\u8EAC\uA1DA" + // 6550 - 6554 + "\u8EAC\uA1DB\u8EAC\uA1DC\u8EAC\uA1DD\u8EAC\uA1DE\u8EAC\uA1DF" + // 6555 - 6559 + "\u8EAC\uA1E0\u8EA2\uE8E4\u8EA2\uE8E5\u8EA2\uEEF9\u8EA2\uF0AD" + // 6560 - 6564 + "\u0000\u0000\u0000\u0000\u8EA2\uF0F8\u8EA2\uF1D5\u8EA2\uF2A9" + // 6565 - 6569 + "\u8EA2\uF2C3\u0000\uEBFD\u0000\uF6BA\u8EA2\uE5B5\u8EA2\uE8E6" + // 6570 - 6574 + "\u8EA2\uEDC0\u8EA2\uF0AE\u0000\u0000\u0000\u0000\u0000\uF0AF" + // 6575 - 6579 + "\u0000\u0000\u8EA2\uE1C1\u8EA2\uE5B6\u8EA2\uE8E7\u8EA2\uE8E8" + // 6580 - 6584 + "\u8EA2\uE8E9\u8EA2\uEBCC\u0000\u0000\u8EA2\uEBC8\u0000\uFBD4" + // 6585 - 6589 + "\u8EA2\uEBCB\u8EA2\uEBCA\u0000\uFADF\u8EA2\uEBC9\u0000\uFAE1" + // 6590 - 6594 + "\u0000\u0000\u0000\uFAE0\u8EA2\uEDC2\u8EA2\uEDC1\u0000\uFBD5" + // 6595 - 6599 + "\u0000\uFBD6\u0000\u0000\u0000\u0000\u0000\uFCB2\u8EA2\uEEFA" + // 6600 - 6604 + "\u0000\uFCB1\u0000\u0000\u8EA2\uF0B1\u8EA2\uF0B2\u8EA2\uF0B0" + // 6605 - 6609 + "\u8EA2\uF0AF\u0000\uFDA1\u0000\u0000\u8EA2\uF0F9\u8EA2\uF0FA" + // 6610 - 6614 + "\u8EA2\uF0FB\u0000\uFCFE\u8EA2\uF1D6\u8EA2\uF1D9\u8EA2\uF1D8" + // 6615 - 6619 + "\u8EA2\uF1D7\u0000\u0000\u0000\u0000\u8EA2\uF2C2\u0000\u0000" + // 6620 - 6624 + "\u0000\uC4A1\u0000\uC4A3\u0000\u0000\u0000\uC4A4\u0000\u0000" + // 6625 - 6629 + "\u0000\u0000\u0000\u0000\u8EA2\uA1A6\u0000\uC4B7\u0000\uC4B5" + // 6630 - 6634 + "\u0000\uC4B8\u0000\uC4B6\u8EA2\uA1A7\u0000\uC4E2\u8EA2\uA1AF" + // 6635 - 6639 + "\u8EA2\uA1AD\u0000\uC4E1\u0000\uC4E0\u0000\u0000\u0000\u0000" + // 6640 - 6644 + "\u0000\uC5E2\u0000\uC5E1\u0000\uC5E0\u0000\u0000\u0000\uC5E3" + // 6645 - 6649 + "\u0000\uC5DF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6650 - 6654 + "\u0000\uC7A2\u0000\uC7A3\u0000\u0000\u0000\u0000\u0000\u0000" + // 6655 - 6659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBE4\u0000\u0000" + // 6660 - 6664 + "\u0000\uA7A2\u0000\u0000\u0000\u0000\u0000\uC4B9\u0000\u0000" + // 6665 - 6669 + "\u0000\uC4E3\u8EA2\uA1B0\u0000\u0000\u0000\uC4E4\u8EA2\uA1C3" + // 6670 - 6674 + "\u0000\uC8EB\u8EA2\uA5B1\u0000\u0000\u0000\u0000\u0000\uA7A3" + // 6675 - 6679 + "\u0000\u0000\u0000\uC4BA\u0000\uC4E5\u0000\u0000\u0000\uC5E4" + // 6680 - 6684 + "\u8EA2\uA1C4\u0000\u0000\u0000\u0000\u0000\uA7A4\u8EA2\uE5AE" + // 6685 - 6689 + "\u8EA2\uE5AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 6690 - 6694 + "\u8EA2\uF0A8\u0000\uFCFD\u0000\u0000\u8EA2\uF1D4\u8EA2\uF1EE" + // 6695 - 6699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7EF\u8EA2\uD6BD" + // 6700 - 6704 + "\u8EA2\uD6BE\u0000\u0000\u8EA2\uDCC3\u0000\uE7F0\u0000\u0000" + // 6705 - 6709 + "\u0000\uF8A9\u8EA2\uE5AF\u0000\u0000\u8EA2\uEDBD\u0000\uFBD3" + // 6710 - 6714 + "\u8EA2\uEDBE\u8EA2\uEDBC\u8EA2\uF0A9\u0000\u0000\u8EA2\uF0F7" + // 6715 - 6719 + "\u0000\u0000\u0000\uE7F1\u0000\u0000\u8EA2\uE1C0\u8EA2\uE1BF" + // 6720 - 6724 + "\u8EA2\uE1BE\u8EA2\uE5B0\u0000\u0000\u0000\u0000\u8EA2\uE5B4" + // 6725 - 6729 + "\u8EA2\uE5B3\u8EA2\uE5B2\u8EA2\uE5B1\u0000\uF8AA\u8EA2\uE8E3" + // 6730 - 6734 + "\u8EA2\uEBC7\u0000\uFADE\u8EA2\uEBC6\u8EA2\uEDBF\u8EA2\uEEF8" + // 6735 - 6739 + "\u8EA2\uEEF7\u0000\uFCB0\u8EA2\uEEF6\u8EA2\uF0AC\u8EA2\uF0AB" + // 6740 - 6744 + "\u8EA2\uF0AA\u0000\u0000\u0000\u0000\u0000\uEBFC\u0000\u0000" + // 6745 - 6749 + "\u8EA2\uDCC4\u0000\uF6B9\u0000\u0000\u8EAD\uA2E7\u8EAD\uA2E8" + // 6750 - 6754 + "\u8EAD\uA2E9\u8EAD\uA2EA\u8EAD\uA2EB\u8EAD\uA2EC\u8EAD\uA2ED" + // 6755 - 6759 + "\u8EAD\uA2EE\u8EAD\uA2EF\u8EAD\uA2F0\u8EAD\uA2F1\u8EAD\uA2F2" + // 6760 - 6764 + "\u8EAD\uA2F3\u8EAD\uA2F4\u8EAD\uA2F5\u8EAD\uA2F6\u8EAD\uA2F7" + // 6765 - 6769 + "\u8EAD\uA2F8\u8EAD\uA2F9\u8EAD\uA2FA\u8EAD\uA2FB\u8EAD\uA2FC" + // 6770 - 6774 + "\u8EAD\uA2FD\u8EAD\uA2FE\u8EAD\uA3A1\u8EAD\uA3A2\u8EAD\uA3A3" + // 6775 - 6779 + "\u8EAD\uA3A4\u8EAD\uA3A5\u8EAD\uA3A6\u8EAD\uA3A7\u8EAD\uA3A8" + // 6780 - 6784 + "\u8EAD\uA3A9\u8EAD\uA3AA\u8EAD\uA3AB\u8EAD\uA3AC\u8EAD\uA3AD" + // 6785 - 6789 + "\u8EAD\uA3AE\u8EAD\uA3AF\u8EAD\uA3B0\u8EAD\uA3B1\u8EAD\uA3B2" + // 6790 - 6794 + "\u8EAD\uA3B3\u8EAD\uA3B4\u8EAD\uA3B5\u8EAD\uA3B6\u8EAD\uA3B7" + // 6795 - 6799 + "\u8EAD\uA3B8\u8EAD\uA3B9\u8EAD\uA3BA\u8EAD\uA3BB\u8EAD\uA3BC" + // 6800 - 6804 + "\u8EAD\uA3BD\u8EAD\uA3BE\u8EAD\uA3BF\u0000\u0000\u0000\u0000" + // 6805 - 6809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1AE\u8EAD\uA1A5" + // 6810 - 6814 + "\u8EAD\uA1A6\u0000\u0000\u0000\uD5E9\u8EA2\uAFD4\u8EA2\uAFD2" + // 6815 - 6819 + "\u8EA2\uAFD3\u0000\uD5EA\u8EA2\uAFD1\u0000\u0000\u0000\u0000" + // 6820 - 6824 + "\u0000\u0000\u0000\u0000\u0000\uDBA1\u0000\uDBA2\u8EA2\uB5EA" + // 6825 - 6829 + "\u0000\uDBA3\u0000\uDAFE\u8EA2\uBCC2\u0000\u0000\u8EA2\uBCC3" + // 6830 - 6834 + "\u8EA2\uC3C4\u0000\u0000\u0000\u0000\u0000\uE9A2\u0000\uE9A1" + // 6835 - 6839 + "\u0000\u0000\u0000\u0000\u8EA2\uDCFC\u8EA2\uE1E5\u0000\u0000" + // 6840 - 6844 + "\u8EA2\uE5D5\u8EA2\uE5D4\u8EA2\uE8FB\u0000\uA7E6\u8EA2\uA1BE" + // 6845 - 6849 + "\u0000\uD1CD\u0000\u0000\u0000\u0000\u0000\uC5CA\u0000\uC6D9" + // 6850 - 6854 + "\u0000\u0000\u0000\uC8AF\u0000\uC8AE\u0000\u0000\u0000\u0000" + // 6855 - 6859 + "\u0000\uC8B0\u0000\uC8B1\u8EA2\uA2CF\u8EA2\uA2CE\u8EA2\uA4B9" + // 6860 - 6864 + "\u0000\uCAD4\u8EA2\uA4BC\u8EA2\uA4BB\u8EA2\uA4BA\u8EA2\uA4BD" + // 6865 - 6869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDF6" + // 6870 - 6874 + "\u8EA2\uA7AA\u8EA2\uA7A6\u8EA2\uA7AF\u0000\u0000\u0000\u0000" + // 6875 - 6879 + "\u8EA2\uA3A6\u0000\u0000\u0000\uC8FB\u0000\u0000\u0000\u0000" + // 6880 - 6884 + "\u0000\uC8FE\u0000\uC8EF\u0000\uC9A9\u8EA2\uA2FD\u0000\u0000" + // 6885 - 6889 + "\u0000\u0000\u8EA2\uA5BD\u0000\uC8ED\u0000\uC9A5\u0000\uC8EE" + // 6890 - 6894 + "\u0000\uC8F6\u0000\uC8F7\u8EA2\uA3A4\u8EA2\uA2FE\u0000\uC8FC" + // 6895 - 6899 + "\u0000\uC8F4\u8EA2\uA2FA\u0000\uC8F0\u8EA2\uA3A7\u0000\uC9A7" + // 6900 - 6904 + "\u0000\uC9AA\u0000\uC8F3\u0000\uC9A2\u0000\uC9A8\u0000\uC8F1" + // 6905 - 6909 + "\u8EA2\uA3A5\u0000\uC9A3\u8EA2\uA3AB\u8EA2\uA2FC\u0000\uC9A1" + // 6910 - 6914 + "\u8EA2\uA3A1\u0000\u0000\u0000\u0000\u8EA2\uA3A3\u0000\u0000" + // 6915 - 6919 + "\u0000\uCBF9\u8EA2\uA5BF\u8EA2\uA5CB\u0000\uCBF1\u0000\u0000" + // 6920 - 6924 + "\u8EA2\uA5CC\u0000\uCBEC\u0000\uCBF6\u0000\u0000\u0000\u0000" + // 6925 - 6929 + "\u0000\uCBEF\u8EA2\uA5B9\u0000\uCBF7\u8EA2\uA5B8\u8EA2\uA5BC" + // 6930 - 6934 + "\u8EA2\uA5C3\u8EA2\uA5C1\u0000\uCCA1\u0000\uCBFA\u8EA2\uA5B3" + // 6935 - 6939 + "\u8EA2\uA5B5\u0000\uCBFC\u0000\uCBF0\u8EA2\uE8E1\u0000\u0000" + // 6940 - 6944 + "\u8EA2\uF0A4\u0000\uE3A7\u0000\u0000\u0000\u0000\u0000\u0000" + // 6945 - 6949 + "\u0000\u0000\u8EA2\uE1BA\u0000\u0000\u0000\u0000\u0000\u0000" + // 6950 - 6954 + "\u0000\uFDB1\u0000\uE3A8\u0000\uF0AD\u0000\uF6B4\u8EA2\uF0A5" + // 6955 - 6959 + "\u0000\uE3A9\u0000\u0000\u8EA2\uD6BC\u0000\uF3BD\u8EA2\uDCC0" + // 6960 - 6964 + "\u8EA2\uDCC1\u0000\u0000\u0000\uF3BC\u0000\u0000\u8EA2\uE1BB" + // 6965 - 6969 + "\u0000\uF6B8\u0000\uF6B6\u0000\uF6B7\u0000\uF6B5\u8EA2\uE5AC" + // 6970 - 6974 + "\u0000\uF8A8\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEBC3" + // 6975 - 6979 + "\u8EA2\uEBC2\u8EA2\uEBC5\u8EA2\uEBC4\u0000\uFADD\u0000\u0000" + // 6980 - 6984 + "\u0000\u0000\u8EA2\uEDB9\u0000\u0000\u8EA2\uEDBB\u8EA2\uEDBA" + // 6985 - 6989 + "\u0000\uFBD2\u8EA2\uEEF5\u0000\u0000\u8EA2\uF0A6\u8EA2\uF0A7" + // 6990 - 6994 + "\u0000\uFCDC\u8EA2\uF1D3\u8EA2\uF1ED\u0000\uFDC2\u0000\u0000" + // 6995 - 6999 + "\u8EA2\uC1D5\u8EA2\uDCC2\u8EA2\uE1BC\u8EA2\uE8E2\u8EA2\uC8F5" + // 7000 - 7004 + "\u0000\u0000\u8EA2\uE1BD\u8EA2\uC8F4\u0000\u0000\u0000\uE7EE" + // 7005 - 7009 + "\u8EA2\uD6BB\u0000\u0000\u0000\u0000\u8EA2\uDCBC\u8EA2\uDCBD" + // 7010 - 7014 + "\u8EA2\uDCBB\u8EA2\uE1B7\u8EA2\uE1B6\u0000\uF6B3\u8EA2\uE5AB" + // 7015 - 7019 + "\u8EA2\uE1B8\u8EA2\uE5AA\u0000\u0000\u0000\u0000\u8EA2\uE8E0" + // 7020 - 7024 + "\u0000\uF9DD\u0000\uF9DF\u8EA2\uE8DF\u0000\u0000\u0000\u0000" + // 7025 - 7029 + "\u0000\uF9DE\u0000\u0000\u8EA2\uEBBF\u8EA2\uEBC1\u8EA2\uEBC0" + // 7030 - 7034 + "\u8EA2\uEDB8\u0000\uFBD1\u0000\u0000\u0000\uFCDB\u8EA2\uF0F6" + // 7035 - 7039 + "\u8EA2\uF1D2\u0000\u0000\u0000\u0000\u8EA2\uF2C1\u0000\uDEA7" + // 7040 - 7044 + "\u0000\u0000\u8EA2\uCFCD\u0000\u0000\u0000\uF0AB\u0000\u0000" + // 7045 - 7049 + "\u0000\u0000\u0000\u0000\u8EA2\uDCBF\u8EA2\uDCBE\u0000\u0000" + // 7050 - 7054 + "\u8EA2\uE1B9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF9E0" + // 7055 - 7059 + "\u0000\uFADC\u8EA2\uEEF4\u8EA2\uF2B9\u0000\u0000\u0000\u0000" + // 7060 - 7064 + "\u0000\u0000\u0000\uDEA8\u0000\uEBFB\u0000\u0000\u0000\uF0AC" + // 7065 - 7069 + "\u0000\u0000\u8EAD\uA2B4\u8EAD\uA2B5\u8EAD\uA2B6\u8EAD\uA2B7" + // 7070 - 7074 + "\u8EAD\uA2B8\u8EAD\uA2B9\u8EAD\uA2BA\u8EAD\uA2BB\u8EAD\uA2BC" + // 7075 - 7079 + "\u8EAD\uA2BD\u8EAD\uA2BE\u8EAD\uA2BF\u8EAD\uA2C0\u8EAD\uA2C1" + // 7080 - 7084 + "\u8EAD\uA2C2\u8EAD\uA2C3\u8EAD\uA2C4\u8EAD\uA2C5\u8EAD\uA2C6" + // 7085 - 7089 + "\u8EAD\uA2C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7090 - 7094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1A1\u8EAD\uA1A2" + // 7095 - 7099 + "\u8EAD\uA1A7\u8EAD\uA1A8\u0000\u0000\u0000\u0000\u8EAD\uA2C8" + // 7100 - 7104 + "\u8EAD\uA2C9\u8EAD\uA2CA\u8EAD\uA2CB\u8EAD\uA2CC\u8EAD\uA2CD" + // 7105 - 7109 + "\u8EAD\uA2CE\u8EAD\uA2CF\u8EAD\uA2D0\u8EAD\uA2D1\u8EAD\uA2D2" + // 7110 - 7114 + "\u8EAD\uA2D3\u8EAD\uA2D4\u8EAD\uA2D5\u8EAD\uA2D6\u8EAD\uA2D7" + // 7115 - 7119 + "\u8EAD\uA2D8\u8EAD\uA2D9\u8EAD\uA2DA\u8EAD\uA2DB\u8EAD\uA2DC" + // 7120 - 7124 + "\u8EAD\uA2DD\u8EAD\uA2DE\u8EAD\uA2DF\u8EAD\uA2E0\u8EAD\uA2E1" + // 7125 - 7129 + "\u8EAD\uA2E2\u8EAD\uA2E3\u8EAD\uA2E4\u8EAD\uA2E5\u8EAD\uA2E6" + // 7130 - 7134 + "\u8EA2\uF0F0\u8EA2\uF0F1\u8EA2\uF0EA\u8EA2\uF0ED\u8EA2\uF0F5" + // 7135 - 7139 + "\u8EA2\uF0EF\u8EA2\uF0EE\u8EA2\uF0EC\u0000\u0000\u8EA2\uF0F2" + // 7140 - 7144 + "\u0000\u0000\u8EA2\uF1CC\u0000\u0000\u8EA2\uF1CD\u0000\u0000" + // 7145 - 7149 + "\u8EA2\uF1CF\u8EA2\uF1CE\u8EA2\uF1D1\u8EA2\uF1D0\u8EA2\uF1EC" + // 7150 - 7154 + "\u8EA2\uF1EB\u8EA2\uF2A7\u0000\u0000\u8EA2\uF2A8\u0000\u0000" + // 7155 - 7159 + "\u8EA2\uF2B4\u0000\uFDC5\u0000\uFDC9\u0000\u0000\u8EA2\uF2BD" + // 7160 - 7164 + "\u0000\uFDCA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7165 - 7169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7170 - 7174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7175 - 7179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7180 - 7184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7185 - 7189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7190 - 7194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7195 - 7199 + "\u0000\u0000\u0000\uA2A1\u0000\u0000\u0000\u0000\u0000\u0000" + // 7200 - 7204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7205 - 7209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7210 - 7214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7215 - 7219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7220 - 7224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7225 - 7229 + "\u0000\uCBD0\u0000\uE2CB\u0000\u0000\u0000\u0000\u0000\uE7A8" + // 7230 - 7234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBBE\u0000\u0000" + // 7235 - 7239 + "\u0000\u0000\u0000\uF2D6\u0000\u0000\u0000\uF2D5\u0000\u0000" + // 7240 - 7244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF9B0\u0000\uFAA9" + // 7245 - 7249 + "\u0000\uFBB3\u0000\uCBD1\u0000\uD8C9\u0000\uE7A9\u0000\u0000" + // 7250 - 7254 + "\u8EA2\uE7C7\u0000\uA8E3\u0000\u0000\u0000\u0000\u0000\u0000" + // 7255 - 7259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7260 - 7264 + "\u0000\u0000\u8EA2\uA5A6\u8EA2\uE8CB\u8EA2\uE8C6\u8EA2\uE8D9" + // 7265 - 7269 + "\u0000\u0000\u8EA2\uE8C8\u8EA2\uE8D6\u8EA2\uE8CD\u8EA2\uE8DD" + // 7270 - 7274 + "\u8EA2\uE8C9\u0000\uF9D8\u8EA2\uE8C7\u8EA2\uE8CE\u8EA2\uE8CF" + // 7275 - 7279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEBAD\u0000\u0000" + // 7280 - 7284 + "\u8EA2\uEBAB\u0000\u0000\u8EA2\uEBB0\u0000\u0000\u8EA2\uEBBC" + // 7285 - 7289 + "\u8EA2\uEBB3\u8EA2\uEBAC\u8EA2\uEBA8\u8EA2\uEBB5\u8EA2\uEBAE" + // 7290 - 7294 + "\u8EA2\uEBB1\u8EA2\uEBAA\u8EA2\uEBB8\u8EA2\uEBA7\u8EA2\uEBAF" + // 7295 - 7299 + "\u8EA2\uEBB4\u8EA2\uEBB6\u8EA2\uEBB9\u8EA2\uEBA9\u0000\u0000" + // 7300 - 7304 + "\u8EA2\uEBBD\u8EA2\uEBBE\u8EA2\uEBB7\u8EA2\uEBBB\u8EA2\uEBB2" + // 7305 - 7309 + "\u0000\u0000\u8EA2\uEDAF\u8EA2\uEDB2\u0000\u0000\u0000\uFBCD" + // 7310 - 7314 + "\u0000\u0000\u8EA2\uEDB1\u8EA2\uEDB6\u8EA2\uEDB4\u0000\uFBCE" + // 7315 - 7319 + "\u8EA2\uEDAB\u8EA2\uEDA1\u8EA2\uECFE\u0000\uFBD0\u8EA2\uEDAD" + // 7320 - 7324 + "\u8EA2\uEDAE\u8EA2\uEDAA\u8EA2\uEDA2\u0000\u0000\u8EA2\uEDA7" + // 7325 - 7329 + "\u8EA2\uEBBA\u8EA2\uE1B2\u8EA2\uE1A5\u8EA2\uE1AE\u8EA2\uE1AF" + // 7330 - 7334 + "\u0000\u0000\u8EA2\uE1AD\u0000\u0000\u0000\u0000\u0000\u0000" + // 7335 - 7339 + "\u0000\u0000\u8EA2\uE4FC\u8EA2\uE4FE\u8EA2\uE5A3\u0000\u0000" + // 7340 - 7344 + "\u0000\u0000\u8EA2\uE4FB\u0000\u0000\u0000\uF8A5\u8EA2\uE5A5" + // 7345 - 7349 + "\u8EA2\uE4FA\u8EA2\uE5A6\u0000\u0000\u8EA2\uE5A2\u8EA2\uE5A4" + // 7350 - 7354 + "\u8EA2\uE5A8\u8EA2\uE5A1\u8EA2\uE5A9\u8EA2\uE4FD\u8EA2\uE4F9" + // 7355 - 7359 + "\u0000\uF8A6\u0000\u0000\u8EA2\uE5A7\u0000\uF8A7\u0000\uF9D9" + // 7360 - 7364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7365 - 7369 + "\u8EA2\uE1A6\u8EA2\uE8DE\u8EA2\uE8D5\u0000\uF9DB\u8EA2\uE8D1" + // 7370 - 7374 + "\u0000\uF9DC\u0000\u0000\u0000\u0000\u8EA2\uE8DA\u8EA2\uE8D4" + // 7375 - 7379 + "\u8EA2\uE8CA\u0000\uF9DA\u8EA2\uE8D7\u8EA2\uE8D2\u8EA2\uE8D3" + // 7380 - 7384 + "\u0000\u0000\u8EA2\uE8C5\u8EA2\uE8CC\u8EA2\uE8DB\u0000\u0000" + // 7385 - 7389 + "\u8EA2\uE8D8\u0000\u0000\u8EA2\uE8D0\u0000\u0000\u8EA2\uE8DC" + // 7390 - 7394 + "\u8EA2\uD6B5\u0000\u0000\u0000\u0000\u0000\uF0AA\u8EA2\uD6BA" + // 7395 - 7399 + "\u8EA2\uD6B9\u0000\uF0A8\u8EA2\uD6B4\u8EA2\uD6B8\u0000\uF0A9" + // 7400 - 7404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7405 - 7409 + "\u0000\u0000\u8EA2\uDCB8\u0000\u0000\u0000\uF3BA\u0000\u0000" + // 7410 - 7414 + "\u8EA2\uDCB3\u0000\uF3B6\u0000\u0000\u8EA2\uDCB0\u8EA2\uDCB6" + // 7415 - 7419 + "\u8EA2\uDCB9\u0000\u0000\u0000\uF3BB\u0000\u0000\u8EA2\uDCB5" + // 7420 - 7424 + "\u8EA2\uDCB2\u8EA2\uDCBA\u8EA2\uDCB1\u0000\u0000\u8EA2\uDCB7" + // 7425 - 7429 + "\u0000\uF3B7\u0000\u0000\u8EA2\uDCAF\u0000\uF3B8\u0000\u0000" + // 7430 - 7434 + "\u0000\uF3B9\u8EA2\uDCB4\u0000\u0000\u0000\u0000\u0000\u0000" + // 7435 - 7439 + "\u8EA2\uE1B5\u8EA2\uE1A8\u8EA2\uE1A9\u8EA2\uE1AC\u8EA2\uE1AA" + // 7440 - 7444 + "\u0000\u0000\u8EA2\uE1A4\u0000\u0000\u0000\u0000\u8EA2\uE1A7" + // 7445 - 7449 + "\u8EA2\uE1B1\u8EA2\uE1AB\u0000\u0000\u0000\u0000\u0000\uF6B1" + // 7450 - 7454 + "\u0000\u0000\u8EA2\uE1B3\u8EA2\uE1B0\u0000\uF6B2\u8EA2\uEAFE" + // 7455 - 7459 + "\u8EA2\uEEDD\u0000\u0000\u0000\u0000\u8EA2\uEEDA\u0000\u0000" + // 7460 - 7464 + "\u8EA2\uEED7\u0000\u0000\u8EA2\uEED8\u0000\uFCAA\u8EA2\uEFE8" + // 7465 - 7469 + "\u8EA2\uEFEA\u8EA2\uEFED\u8EA2\uEFE9\u8EA2\uEFEE\u0000\u0000" + // 7470 - 7474 + "\u8EA2\uEFE7\u0000\u0000\u8EA2\uEFE5\u0000\u0000\u0000\uFCD7" + // 7475 - 7479 + "\u8EA2\uEFEB\u0000\uFCD9\u0000\uFCD8\u8EA2\uEFE6\u8EA2\uEFEC" + // 7480 - 7484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uF0E8" + // 7485 - 7489 + "\u0000\uFCF8\u8EA2\uF0E9\u0000\u0000\u8EA2\uF0E7\u8EA2\uF0E4" + // 7490 - 7494 + "\u0000\u0000\u0000\u0000\u8EA2\uF0E6\u8EA2\uF0E5\u8EA2\uF1C9" // 7495 - 7499 + ; + + index2a = + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uF1CB" + // 7500 - 7504 + "\u8EA2\uF1CA\u0000\u0000\u0000\u0000\u8EA2\uF1E9\u0000\u0000" + // 7505 - 7509 + "\u8EA2\uF1E8\u8EA2\uF1E7\u8EA2\uF1EA\u0000\u0000\u0000\uFDC0" + // 7510 - 7514 + "\u0000\uFDC1\u8EA2\uF2B8\u8EA2\uF2BC\u0000\u0000\u0000\u0000" + // 7515 - 7519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBBAA\u8EA2\uBBA7" + // 7520 - 7524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7525 - 7529 + "\u8EA2\uBBA8\u8EA2\uBBAB\u8EA2\uBBAF\u0000\u0000\u8EA2\uB4E6" + // 7530 - 7534 + "\u8EA2\uBBB1\u8EA2\uC2CD\u0000\u0000\u0000\uDEE8\u8EA2\uBBB2" + // 7535 - 7539 + "\u8EA2\uBAF8\u8EA2\uBBA4\u0000\u0000\u8EA2\uBBA9\u0000\u0000" + // 7540 - 7544 + "\u0000\u0000\u0000\uDEE6\u0000\uDEE9\u8EA2\uBBB0\u8EA2\uBBB3" + // 7545 - 7549 + "\u8EA2\uBAFC\u8EA2\uBAF9\u0000\u0000\u0000\u0000\u8EA2\uBAFB" + // 7550 - 7554 + "\u0000\u0000\u0000\u0000\u8EA2\uBBA2\u8EA2\uBAFE\u0000\uDEEA" + // 7555 - 7559 + "\u0000\u0000\u8EA2\uBBAC\u0000\u0000\u0000\u0000\u8EA2\uBBA3" + // 7560 - 7564 + "\u0000\u0000\u8EA2\uBBA5\u8EA2\uBAF7\u8EA2\uC2C1\u8EA2\uC2BF" + // 7565 - 7569 + "\u0000\uE3E8\u0000\uE3E6\u8EA2\uC2C9\u8EA2\uC2C0\u8EA2\uC2CA" + // 7570 - 7574 + "\u8EA2\uC2C6\u8EA2\uC2BE\u8EA2\uC2CC\u8EA2\uC2BD\u8EA2\uC2C4" + // 7575 - 7579 + "\u0000\uE3E5\u0000\uE3E4\u0000\uE3E3\u8EA2\uC2C2\u0000\uF8A4" + // 7580 - 7584 + "\u8EA2\uE4F1\u0000\u0000\u8EA2\uE4EF\u8EA2\uE4F6\u0000\u0000" + // 7585 - 7589 + "\u8EA2\uE4EE\u8EA2\uE4ED\u0000\uF8A3\u0000\uF8A1\u0000\uF7FE" + // 7590 - 7594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7595 - 7599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE4F4\u8EA2\uE8BC" + // 7600 - 7604 + "\u8EA2\uE8C2\u0000\uF9D6\u8EA2\uE8BD\u0000\u0000\u8EA2\uE8C0" + // 7605 - 7609 + "\u8EA2\uE8C4\u0000\uF9D7\u8EA2\uE8BF\u0000\u0000\u8EA2\uE8B7" + // 7610 - 7614 + "\u0000\u0000\u8EA2\uE8B6\u8EA2\uE8C3\u8EA2\uE8BA\u0000\u0000" + // 7615 - 7619 + "\u8EA2\uE8B8\u8EA2\uE8C1\u8EA2\uE8B9\u0000\uF9D5\u0000\uF9D4" + // 7620 - 7624 + "\u0000\u0000\u8EA2\uE8B4\u8EA2\uE8B5\u8EA2\uE8BE\u0000\u0000" + // 7625 - 7629 + "\u0000\u0000\u0000\u0000\u8EA2\uE8BB\u0000\u0000\u0000\u0000" + // 7630 - 7634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEAFA" + // 7635 - 7639 + "\u8EA2\uEAFD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7640 - 7644 + "\u0000\uF8A2\u0000\u0000\u0000\u0000\u8EA2\uD0A6\u8EA2\uD0A4" + // 7645 - 7649 + "\u8EA2\uD0A2\u0000\u0000\u8EA2\uD0A8\u0000\u0000\u8EA2\uD0A7" + // 7650 - 7654 + "\u0000\uECCD\u0000\uECC8\u0000\u0000\u0000\uECCA\u0000\uECCC" + // 7655 - 7659 + "\u0000\u0000\u0000\u0000\u8EA2\uD0A9\u8EA2\uD6E1\u0000\u0000" + // 7660 - 7664 + "\u0000\u0000\u8EA2\uD6E0\u8EA2\uD6DF\u0000\u0000\u8EA2\uD6E2" + // 7665 - 7669 + "\u8EA2\uD6DB\u0000\u0000\u8EA2\uD6DC\u8EA2\uD6E4\u8EA2\uD6DD" + // 7670 - 7674 + "\u0000\u0000\u0000\uF0CE\u8EA2\uD6E6\u0000\u0000\u8EA2\uD6E5" + // 7675 - 7679 + "\u8EA2\uD6DE\u0000\u0000\u8EA2\uDCDD\u0000\uF3D1\u8EA2\uDCDB" + // 7680 - 7684 + "\u8EA2\uDCE0\u8EA2\uDCDF\u8EA2\uD6E3\u0000\u0000\u0000\uF3D0" + // 7685 - 7689 + "\u0000\u0000\u8EA2\uDCDE\u8EA2\uDCDA\u8EA2\uDCE2\u8EA2\uDCE1" + // 7690 - 7694 + "\u0000\uF3CF\u0000\u0000\u8EA2\uDCDC\u0000\u0000\u0000\uF0CF" + // 7695 - 7699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF6C0\u0000\u0000" + // 7700 - 7704 + "\u0000\u0000\u0000\u0000\u8EA2\uE1CD\u8EA2\uE5C6\u8EA2\uE5C7" + // 7705 - 7709 + "\u8EA2\uE5C8\u8EA2\uDCA1\u0000\u0000\u8EA2\uDCA7\u0000\u0000" + // 7710 - 7714 + "\u0000\u0000\u8EA2\uDCA2\u8EA2\uE0FE\u8EA2\uDCA3\u8EA2\uDCAE" + // 7715 - 7719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7720 - 7724 + "\u0000\u0000\u0000\u0000\u8EA2\uDCAA\u0000\uF3B5\u8EA2\uDCA9" + // 7725 - 7729 + "\u8EA2\uDCA8\u0000\u0000\u8EA2\uDCAC\u0000\u0000\u0000\u0000" + // 7730 - 7734 + "\u0000\u0000\u0000\u0000\u8EA2\uE0F6\u8EA2\uE0F9\u0000\u0000" + // 7735 - 7739 + "\u0000\u0000\u8EA2\uE0F8\u0000\u0000\u8EA2\uE1A2\u8EA2\uE0FB" + // 7740 - 7744 + "\u8EA2\uE1A1\u0000\u0000\u8EA2\uE0FD\u8EA2\uE0FC\u8EA2\uE0FA" + // 7745 - 7749 + "\u0000\u0000\u8EA2\uE0F7\u0000\u0000\u0000\uF6AF\u0000\uF6AE" + // 7750 - 7754 + "\u0000\u0000\u0000\uF6B0\u0000\uF6AD\u8EA2\uE1A3\u0000\u0000" + // 7755 - 7759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE4F2" + // 7760 - 7764 + "\u8EA2\uE4F5\u0000\u0000\u8EA2\uE4F3\u8EA2\uE4F7\u0000\u0000" + // 7765 - 7769 + "\u0000\u0000\u0000\u0000\u8EA2\uE4F8\u0000\u0000\u8EA2\uE4F0" + // 7770 - 7774 + "\u8EA2\uE4E3\u0000\uF7F8\u8EA2\uE8AE\u0000\u0000\u0000\u0000" + // 7775 - 7779 + "\u0000\u0000\u8EA2\uEAF4\u8EA2\uEAF2\u0000\u0000\u0000\u0000" + // 7780 - 7784 + "\u8EA2\uEAF3\u0000\u0000\u0000\u0000\u8EA2\uECEE\u0000\u0000" + // 7785 - 7789 + "\u0000\uFBC8\u8EA2\uEED1\u0000\uFCD6\u0000\uFCA8\u0000\uFCD4" + // 7790 - 7794 + "\u0000\uFCD5\u8EA2\uF0E2\u0000\uFDAF\u0000\u0000\u0000\uD8ED" + // 7795 - 7799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE4E4\u0000\u0000" + // 7800 - 7804 + "\u0000\u0000\u8EA2\uB3CC\u0000\u0000\u0000\uE7EC\u0000\u0000" + // 7805 - 7809 + "\u8EA2\uCFC3\u0000\u0000\u0000\u0000\u0000\uEBF5\u8EA2\uCFC4" + // 7810 - 7814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD5FD\u8EA2\uD5FC" + // 7815 - 7819 + "\u0000\uF3B3\u0000\uF0A1\u0000\uF0A2\u0000\u0000\u8EA2\uD6A2" + // 7820 - 7824 + "\u8EA2\uD6A1\u8EA2\uD5FE\u0000\u0000\u0000\u0000\u8EA2\uDBFA" + // 7825 - 7829 + "\u8EA2\uDBFD\u0000\u0000\u8EA2\uDBFC\u8EA2\uDBFB\u0000\uF3B2" + // 7830 - 7834 + "\u8EA2\uE0F4\u8EA2\uE0F2\u8EA2\uE0F1\u0000\u0000\u0000\uA1A1" + // 7835 - 7839 + "\u0000\uA1A3\u0000\uA1A4\u0000\uA1F1\u0000\u0000\u8EAD\uA1AB" + // 7840 - 7844 + "\u8EAD\uA1AC\u8EAD\uA1AD\u0000\uA1D2\u0000\uA1D3\u0000\uA1CE" + // 7845 - 7849 + "\u0000\uA1CF\u0000\uA1D6\u0000\uA1D7\u0000\uA1DA\u0000\uA1DB" + // 7850 - 7854 + "\u0000\uA1CA\u0000\uA1CB\u0000\uA2E5\u8EAD\uA1B5\u0000\uA1C6" + // 7855 - 7859 + "\u0000\uA1C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7860 - 7864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1E8\u0000\uA1E9" + // 7865 - 7869 + "\u0000\u0000\u0000\u0000\u0000\uA4B5\u0000\uA4B6\u0000\uA4B7" + // 7870 - 7874 + "\u0000\uA4B8\u0000\uA4B9\u0000\uA4BA\u0000\uA4BB\u0000\uA4BC" + // 7875 - 7879 + "\u0000\uA4BD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7880 - 7884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7885 - 7889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7890 - 7894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7895 - 7899 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uAFEE\u0000\uD6AE" + // 7900 - 7904 + "\u8EA2\uAFEF\u0000\u0000\u8EA2\uAFE3\u0000\uD6A3\u0000\uD6AF" + // 7905 - 7909 + "\u0000\u0000\u8EA2\uAFDC\u0000\u0000\u8EA2\uAFE5\u8EA2\uAFED" + // 7910 - 7914 + "\u0000\u0000\u8EA2\uAFDB\u8EA2\uAFF6\u0000\uD5F7\u0000\u0000" + // 7915 - 7919 + "\u0000\u0000\u0000\u0000\u8EA2\uAFF5\u8EA2\uAFF0\u0000\u0000" + // 7920 - 7924 + "\u8EA2\uAFF1\u0000\uD6A1\u0000\uD6AC\u8EA2\uAFE7\u0000\u0000" + // 7925 - 7929 + "\u8EA2\uAFE8\u8EA2\uAFF2\u8EA2\uAFE9\u0000\u0000\u8EA2\uAFE4" + // 7930 - 7934 + "\u8EA2\uAFDE\u8EA2\uAFDF\u8EA2\uAFEC\u8EA2\uAFE6\u0000\u0000" + // 7935 - 7939 + "\u0000\u0000\u0000\uD5F8\u0000\uD5FC\u8EA2\uAFF4\u8EA2\uAFE0" + // 7940 - 7944 + "\u0000\uD6AA\u0000\uD6A6\u0000\u0000\u0000\u0000\u8EA2\uACFC" + // 7945 - 7949 + "\u0000\uD3AA\u0000\uD3AB\u8EA2\uACFE\u0000\u0000\u8EA2\uACF8" + // 7950 - 7954 + "\u8EA2\uACFB\u8EA2\uADA6\u8EA2\uADA4\u0000\u0000\u0000\uD3AC" + // 7955 - 7959 + "\u8EA2\uADAA\u0000\uD3AF\u8EA2\uADA7\u8EA2\uACFD\u8EA2\uACFA" + // 7960 - 7964 + "\u0000\u0000\u0000\u0000\u8EA2\uBFAE\u8EA2\uADA5\u0000\uD3A7" + // 7965 - 7969 + "\u8EA2\uADA8\u8EA2\uACF7\u8EA2\uADA2\u0000\uD3A9\u0000\uD3AE" + // 7970 - 7974 + "\u8EA2\uADA3\u0000\uD3B2\u0000\uD3B0\u0000\u0000\u8EA2\uACF9" + // 7975 - 7979 + "\u0000\uD3AD\u0000\u0000\u8EA2\uADA1\u0000\uD3B1\u0000\uD3A8" + // 7980 - 7984 + "\u8EA2\uADA9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 7985 - 7989 + "\u0000\u0000\u0000\u0000\u0000\uD7DE\u0000\u0000\u0000\uD7E7" + // 7990 - 7994 + "\u0000\uD7DC\u0000\uD7DA\u8EA2\uB2AE\u0000\uD7E2\u0000\uD7DF" + // 7995 - 7999 + "\u8EA2\uB2B0\u0000\u0000\u0000\u0000\u0000\uD7E1\u8EA2\uB2AF" + // 8000 - 8004 + "\u8EA2\uB2AD\u8EA2\uB2B2\u0000\uD7E6\u0000\uD7E4\u8EA2\uBFAD" + // 8005 - 8009 + "\u0000\u0000\u8EA2\uAAE8\u0000\u0000\u8EA2\uAAE9\u0000\u0000" + // 8010 - 8014 + "\u0000\uD1CA\u8EA2\uAFD0\u0000\u0000\u0000\uD5E6\u0000\uD5E7" + // 8015 - 8019 + "\u8EA2\uAFCF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8020 - 8024 + "\u0000\uDAF7\u0000\u0000\u0000\uDAF3\u0000\u0000\u8EA2\uB5E9" + // 8025 - 8029 + "\u0000\uDAFA\u0000\uDAF9\u0000\uDAF2\u0000\uDAF5\u0000\uDAF8" + // 8030 - 8034 + "\u0000\uDAF4\u0000\u0000\u0000\u0000\u8EA2\uBCBB\u0000\uDAF1" + // 8035 - 8039 + "\u0000\uDFD4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFD6" + // 8040 - 8044 + "\u0000\uDFD7\u8EA2\uBCBA\u8EA2\uBCBD\u0000\uDFD5\u8EA2\uBCB8" + // 8045 - 8049 + "\u8EA2\uBCBC\u0000\u0000\u8EA2\uBCB9\u0000\u0000\u0000\uE4B9" + // 8050 - 8054 + "\u0000\u0000\u0000\u0000\u8EA2\uC3C2\u0000\u0000\u0000\u0000" + // 8055 - 8059 + "\u0000\uE8FD\u8EA2\uCADB\u0000\uF0EC\u0000\uEDAE\u8EA2\uD0E4" + // 8060 - 8064 + "\u0000\uEDAF\u0000\uEDB0\u8EA2\uD0E6\u8EA2\uD0E5\u8EA2\uD0E7" + // 8065 - 8069 + "\u8EA2\uD7BC\u0000\u0000\u0000\u0000\u8EA2\uD7BB\u0000\uFBC6" + // 8070 - 8074 + "\u8EA2\uECEB\u8EA2\uECEA\u0000\uFBC5\u8EA2\uECE9\u0000\uFBC4" + // 8075 - 8079 + "\u8EA2\uECE8\u0000\u0000\u8EA2\uEECC\u8EA2\uEECE\u8EA2\uEECD" + // 8080 - 8084 + "\u0000\u0000\u8EA2\uEECA\u0000\uFCA7\u8EA2\uEEC7\u8EA2\uEECB" + // 8085 - 8089 + "\u8EA2\uEED0\u0000\u0000\u8EA2\uEECF\u8EA2\uEEC8\u8EA2\uEEC9" + // 8090 - 8094 + "\u0000\uFCA6\u8EA2\uEFE0\u0000\uFCD3\u0000\u0000\u8EA2\uEFE1" + // 8095 - 8099 + "\u0000\uFCD1\u0000\uFCD2\u0000\u0000\u0000\u0000\u8EA2\uF0E1" + // 8100 - 8104 + "\u0000\uFCF5\u8EA2\uF1E5\u0000\u0000\u0000\uFDB7\u0000\u0000" + // 8105 - 8109 + "\u8EA2\uF2A3\u0000\uFDB8\u8EA2\uF2A4\u8EA2\uF2A5\u8EA2\uF2B3" + // 8110 - 8114 + "\u8EA2\uF2B2\u0000\uFDC7\u8EA2\uF2BB\u0000\u0000\u0000\u0000" + // 8115 - 8119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8120 - 8124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8125 - 8129 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8130 - 8134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2F5\u0000\uA2F6" + // 8135 - 8139 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8140 - 8144 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8145 - 8149 + "\u0000\u0000\u0000\u0000\u0000\uA2F0\u0000\uA2F1\u0000\uA2F2" + // 8150 - 8154 + "\u0000\u0000\u0000\u0000\u0000\uA2F4\u0000\u0000\u0000\u0000" + // 8155 - 8159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8160 - 8164 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8165 - 8169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8170 - 8174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8175 - 8179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8180 - 8184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8185 - 8189 + "\u0000\uA2E6\u0000\uA2E7\u8EAD\uA4C0\u0000\uA2A4\u8EAD\uA4C1" + // 8190 - 8194 + "\u0000\uA2E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8195 - 8199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8200 - 8204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8205 - 8209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8210 - 8214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8215 - 8219 + "\u0000\u0000\u0000\u0000\u8EAD\uA1BE\u0000\u0000\u8EAD\uA1BF" + // 8220 - 8224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8225 - 8229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8230 - 8234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8235 - 8239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA4BD\u0000\u0000" + // 8240 - 8244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8245 - 8249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8250 - 8254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8255 - 8259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8260 - 8264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1C2\u0000\u0000" + // 8265 - 8269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8270 - 8274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8275 - 8279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8280 - 8284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8285 - 8289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8290 - 8294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8295 - 8299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8300 - 8304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8305 - 8309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8310 - 8314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA4C4\u0000\u0000" + // 8315 - 8319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8320 - 8324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8325 - 8329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBDB1\u0000\uE0C6" + // 8330 - 8334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC4C7\u8EA2\uC4C6" + // 8335 - 8339 + "\u8EA2\uC4AC\u0000\uE4E3\u0000\u0000\u8EA2\uC4C5\u8EA2\uC4AF" + // 8340 - 8344 + "\u8EA2\uC4B0\u0000\u0000\u0000\uE4ED\u8EA2\uC4CE\u0000\uE4E8" + // 8345 - 8349 + "\u8EA2\uC4C4\u0000\u0000\u8EA2\uC4A9\u0000\uE4EE\u0000\uE4E4" + // 8350 - 8354 + "\u8EA2\uC4B8\u8EA2\uC4AE\u8EA2\uC4B1\u8EA2\uC4C9\u0000\uE4DE" + // 8355 - 8359 + "\u8EA2\uC4D0\u8EA2\uC4C8\u0000\uE4E7\u8EA2\uC4BD\u0000\uE4F2" + // 8360 - 8364 + "\u0000\u0000\u0000\u0000\u0000\uE4F1\u0000\uE4EB\u0000\u0000" + // 8365 - 8369 + "\u0000\u0000\u8EA2\uC4CF\u0000\uE4DF\u8EA2\uC4BB\u8EA2\uC4B2" + // 8370 - 8374 + "\u8EA2\uC4BF\u8EA2\uC4CB\u0000\uE4F3\u0000\u0000\u0000\uE4E1" + // 8375 - 8379 + "\u8EA2\uC4BA\u8EAD\uA4CB\u8EA2\uC4B3\u0000\uE4EA\u0000\u0000" + // 8380 - 8384 + "\u0000\uE4E9\u8EA2\uC4B6\u8EA2\uC4C0\u8EA2\uC4CA\u8EA2\uCFB7" + // 8385 - 8389 + "\u0000\u0000\u8EA2\uCFB6\u0000\uEBEE\u0000\u0000\u0000\uEBEF" + // 8390 - 8394 + "\u0000\u0000\u8EA2\uCFB8\u8EA2\uD5E8\u0000\uEBF1\u0000\uEFF2" + // 8395 - 8399 + "\u0000\u0000\u0000\uEBF0\u0000\u0000\u0000\u0000\u0000\u0000" + // 8400 - 8404 + "\u0000\uF3A7\u8EA2\uD5EA\u0000\uEFF4\u0000\uEFF3\u8EA2\uD5EB" + // 8405 - 8409 + "\u8EA2\uD5EE\u8EA2\uD5EC\u8EA2\uD5ED\u0000\uEFF5\u0000\u0000" + // 8410 - 8414 + "\u0000\uF3AC\u0000\uF3AA\u0000\u0000\u0000\u0000\u0000\uF3A9" + // 8415 - 8419 + "\u8EA2\uDBE9\u0000\u0000\u0000\uF3AB\u0000\u0000\u0000\u0000" + // 8420 - 8424 + "\u8EA2\uDBE8\u8EA2\uE0D7\u0000\u0000\u8EA2\uDBEA\u0000\uF3A8" + // 8425 - 8429 + "\u8EA2\uDBEB\u8EA2\uE0DA\u8EA2\uE0D8\u8EA2\uE0D9\u8EA2\uE0DE" + // 8430 - 8434 + "\u0000\uF7F5\u8EA2\uE0DD\u8EA2\uE0E0\u8EA2\uE0DF\u8EA2\uE0DC" + // 8435 - 8439 + "\u8EA2\uE0DB\u0000\u0000\u0000\uF6AA\u0000\u0000\u0000\u0000" + // 8440 - 8444 + "\u0000\u0000\u0000\u0000\u8EA2\uE4D8\u0000\u0000\u8EA2\uE4D7" + // 8445 - 8449 + "\u0000\uF7F4\u0000\uF7F2\u0000\uF7F3\u8EA2\uECE3\u8EA2\uEAE0" + // 8450 - 8454 + "\u8EA2\uEAE1\u0000\u0000\u0000\uFAD1\u0000\u0000\u8EA2\uECE2" + // 8455 - 8459 + "\u0000\u0000\u0000\u0000\u8EA2\uECE1\u0000\u0000\u8EA2\uEEC4" + // 8460 - 8464 + "\u8EA2\uF1FE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8465 - 8469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8470 - 8474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8475 - 8479 + "\u0000\uD3F9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3FA" + // 8480 - 8484 + "\u0000\u0000\u0000\u0000\u0000\uD8EA\u8EA2\uB3CB\u0000\u0000" + // 8485 - 8489 + "\u8EA2\uBAB3\u0000\u0000\u0000\uE2FD\u0000\u0000\u0000\uE3A2" + // 8490 - 8494 + "\u0000\uE2FE\u8EA2\uC1D3\u0000\u0000\u0000\uE3A4\u0000\u0000" + // 8495 - 8499 + "\u0000\uE3A1\u0000\u0000\u0000\u0000\u0000\uE3A3\u0000\u0000" + // 8500 - 8504 + "\u0000\uE7E6\u0000\u0000\u8EA2\uC8E8\u0000\u0000\u0000\u0000" + // 8505 - 8509 + "\u8EA2\uC8E9\u8EA2\uD5E9\u0000\u0000\u0000\uE7E5\u0000\uE7E7" + // 8510 - 8514 + "\u0000\uE7E8\u0000\u0000\u0000\uA2D1\u0000\u0000\u0000\uA2D2" + // 8515 - 8519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8520 - 8524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8525 - 8529 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8530 - 8534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8535 - 8539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8540 - 8544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8545 - 8549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8550 - 8554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1CE" + // 8555 - 8559 + "\u0000\u0000\u0000\u0000\u8EAD\uA1CD\u0000\u0000\u8EAD\uA1CC" + // 8560 - 8564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8565 - 8569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8570 - 8574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8575 - 8579 + "\u0000\u0000\u8EA2\uBAE9\u8EA2\uC2AE\u0000\uE3DB\u0000\uE3DE" + // 8580 - 8584 + "\u0000\uE3D9\u8EA2\uC2AD\u8EA2\uC2B1\u8EA2\uC2AC\u0000\u0000" + // 8585 - 8589 + "\u0000\uE3D3\u0000\uE3DD\u8EA2\uC2A9\u0000\uE3D7\u8EA2\uC2B0" + // 8590 - 8594 + "\u0000\u0000\u0000\uE3D5\u0000\uE3D4\u8EA2\uC2B3\u0000\uE3D6" + // 8595 - 8599 + "\u8EA2\uC2B5\u0000\u0000\u8EA2\uC2B2\u0000\uE3D2\u0000\u0000" + // 8600 - 8604 + "\u0000\u0000\u0000\u0000\u0000\uE3DC\u8EA2\uC2B7\u8EA2\uC2AB" + // 8605 - 8609 + "\u8EA2\uC2B4\u0000\u0000\u0000\u0000\u8EA2\uC2AA\u0000\u0000" + // 8610 - 8614 + "\u0000\u0000\u0000\uE3D8\u0000\u0000\u0000\uE3DA\u0000\u0000" + // 8615 - 8619 + "\u8EA2\uC2AF\u0000\u0000\u8EA2\uC2B8\u0000\u0000\u0000\u0000" + // 8620 - 8624 + "\u8EA2\uC9C4\u0000\uE8B8\u8EA2\uC9CA\u0000\u0000\u0000\u0000" + // 8625 - 8629 + "\u0000\uE8BD\u8EA2\uC9C6\u8EA2\uC9CD\u8EA2\uC9BE\u0000\uE8BF" + // 8630 - 8634 + "\u0000\uE8B9\u8EA2\uC9C3\u8EA2\uEFDF\u0000\uD3F7\u0000\uDEA1" + // 8635 - 8639 + "\u0000\uDEA2\u8EA2\uBAB2\u0000\uE2FA\u0000\uE2FB\u8EA2\uC1D1" + // 8640 - 8644 + "\u0000\uE2FC\u0000\u0000\u0000\uE7E2\u0000\u0000\u0000\uE7E4" + // 8645 - 8649 + "\u8EA2\uC8E5\u8EA2\uC8E6\u8EA2\uC8E4\u0000\uE7DF\u0000\uE7E0" + // 8650 - 8654 + "\u0000\uE7E3\u0000\uE7E1\u0000\u0000\u0000\u0000\u8EA2\uCFB3" + // 8655 - 8659 + "\u0000\uEBEA\u0000\uEBEB\u0000\u0000\u0000\u0000\u8EA2\uD5E5" + // 8660 - 8664 + "\u0000\uEFF0\u8EA2\uD5E0\u8EA2\uD5DF\u0000\u0000\u8EA2\uD5E4" + // 8665 - 8669 + "\u0000\uEFEE\u0000\u0000\u0000\u0000\u0000\uF3A6\u0000\u0000" + // 8670 - 8674 + "\u8EA2\uD5E1\u8EA2\uD5E6\u8EA2\uD5E3\u8EA2\uD5E2\u0000\u0000" + // 8675 - 8679 + "\u0000\uEFEF\u0000\u0000\u0000\uF3A4\u0000\u0000\u8EA2\uDBE6" + // 8680 - 8684 + "\u0000\uF2FE\u0000\u0000\u8EA2\uDBE7\u0000\u0000\u0000\u0000" + // 8685 - 8689 + "\u8EA2\uDBE5\u0000\u0000\u0000\uF3A3\u0000\uF3A1\u0000\uF3A5" + // 8690 - 8694 + "\u0000\u0000\u0000\uF3A2\u0000\u0000\u0000\u0000\u0000\u0000" + // 8695 - 8699 + "\u0000\u0000\u8EA2\uA1A1\u0000\uC4A5\u0000\u0000\u0000\uC4BC" + // 8700 - 8704 + "\u0000\u0000\u8EA2\uA1A8\u0000\uC4BD\u0000\u0000\u0000\u0000" + // 8705 - 8709 + "\u0000\uC4E6\u0000\u0000\u0000\uC5E5\u0000\uC5E7\u0000\uC5E6" + // 8710 - 8714 + "\u0000\u0000\u0000\u0000\u0000\uC7A4\u0000\uC7A5\u0000\u0000" + // 8715 - 8719 + "\u0000\u0000\u0000\uCBE5\u0000\u0000\u0000\uD3FD\u0000\uC4A2" + // 8720 - 8724 + "\u0000\u0000\u0000\u0000\u8EA2\uA1A2\u0000\uC4A6\u0000\uC4BF" + // 8725 - 8729 + "\u0000\uC4BE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8730 - 8734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8735 - 8739 + "\u0000\uC7A6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8740 - 8744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8745 - 8749 + "\u0000\uCBE6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8750 - 8754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 8755 - 8759 + "\u0000\u0000\u0000\uD8F1\u8EA2\uB3CE\u8EA2\uCFAB\u8EA2\uCFAF" + // 8760 - 8764 + "\u8EA2\uCFAC\u8EA2\uCFAA\u8EA2\uCFAE\u0000\uEBE8\u0000\u0000" + // 8765 - 8769 + "\u0000\u0000\u8EA2\uD5DC\u0000\u0000\u8EA2\uD5DA\u0000\uEFEC" + // 8770 - 8774 + "\u0000\u0000\u0000\uEFEB\u8EA2\uD5DB\u0000\uEFED\u0000\u0000" + // 8775 - 8779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDBE2\u0000\u0000" + // 8780 - 8784 + "\u0000\u0000\u8EA2\uDBE1\u0000\uF2FD\u8EA2\uDBE0\u8EA2\uE0C9" + // 8785 - 8789 + "\u0000\u0000\u8EA2\uE0CB\u8EA2\uE0CD\u8EA2\uE0CC\u0000\u0000" + // 8790 - 8794 + "\u0000\uF6A6\u8EA2\uE0CA\u8EA2\uE4CB\u0000\uF7E8\u8EA2\uE4C9" + // 8795 - 8799 + "\u8EA2\uE4CC\u0000\uF7E9\u0000\u0000\u8EA2\uE4C7\u0000\u0000" + // 8800 - 8804 + "\u8EA2\uE4CA\u8EA2\uE4C8\u8EA2\uE4C5\u0000\uF7EA\u8EA2\uE4C6" + // 8805 - 8809 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE7EC" + // 8810 - 8814 + "\u0000\u0000\u0000\u0000\u8EA2\uE7EE\u8EA2\uE7ED\u0000\u0000" + // 8815 - 8819 + "\u8EA2\uEAD8\u0000\u0000\u8EA2\uEAD9\u0000\u0000\u0000\u0000" + // 8820 - 8824 + "\u0000\u0000\u8EA2\uECDE\u0000\uEBE6\u0000\u0000\u8EA2\uD5D8" + // 8825 - 8829 + "\u0000\u0000\u0000\uEFE6\u8EA2\uD5D6\u0000\uEFE7\u0000\uEFE8" + // 8830 - 8834 + "\u8EA2\uD5D7\u0000\uEFE9\u0000\u0000\u8EA2\uDBDD\u0000\u0000" + // 8835 - 8839 + "\u0000\uF2F7\u0000\uF2F4\u0000\uF2F9\u8EA2\uDBDF\u0000\uF2F5" + // 8840 - 8844 + "\u8EA2\uDBDE\u0000\uF2F8\u0000\u0000\u0000\u0000\u0000\uF2F6" + // 8845 - 8849 + "\u0000\u0000\u8EA2\uE0C6\u8EA2\uE0C8\u0000\u0000\u0000\u0000" + // 8850 - 8854 + "\u0000\uF6A4\u8EA2\uE0C7\u0000\uF6A5\u8EA2\uE0C5\u8EA2\uE0C4" + // 8855 - 8859 + "\u0000\u0000\u8EA2\uE4C3\u8EA2\uE4C2\u0000\uF7E7\u8EA2\uE4C4" + // 8860 - 8864 + "\u8EA2\uE7EB\u0000\uF9C7\u8EA2\uE7EA\u8EA2\uE7E7\u0000\uF9C6" + // 8865 - 8869 + "\u8EA2\uE7E8\u8EA2\uE7E9\u0000\u0000\u8EA2\uEAD6\u8EA2\uEAD7" + // 8870 - 8874 + "\u0000\uFAD0\u0000\u0000\u0000\uFBBF\u0000\u0000\u0000\u0000" + // 8875 - 8879 + "\u8EA2\uECDC\u0000\u0000\u0000\u0000\u0000\uFBBD\u0000\uFBBE" + // 8880 - 8884 + "\u8EA2\uECDD\u0000\u0000\u0000\u0000\u0000\uFBFE\u0000\uFCA1" + // 8885 - 8889 + "\u8EA2\uEEC0\u0000\uDDFA\u0000\uE2F2\u8EA2\uC1C9\u8EA2\uC1CB" + // 8890 - 8894 + "\u0000\uE2F4\u0000\uE2F3\u0000\uE2F5\u0000\uE2F6\u8EA2\uC1CA" + // 8895 - 8899 + "\u0000\uE7D6\u0000\uE7D7\u0000\uE7D5\u0000\uEBE4\u0000\uE7D4" + // 8900 - 8904 + "\u8EA2\uC8DB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEBE5" + // 8905 - 8909 + "\u8EA2\uD5D5\u8EA2\uDBDC\u0000\uF2F3\u0000\uF6A3\u8EA2\uE4BC" + // 8910 - 8914 + "\u8EA2\uE4C0\u0000\uF7E4\u8EA2\uE4BD\u0000\uF7E5\u0000\uF7E3" + // 8915 - 8919 + "\u8EA2\uE4C1\u0000\uF7E6\u8EA2\uE4BF\u0000\u0000\u8EA2\uE7E6" + // 8920 - 8924 + "\u0000\uF7E2\u0000\uF9C5\u0000\u0000\u8EA2\uF0DB\u0000\u0000" + // 8925 - 8929 + "\u0000\u0000\u0000\uCFE4\u0000\uDDFC\u0000\uDDFB\u0000\u0000" + // 8930 - 8934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE2F7\u8EA2\uC1CD" + // 8935 - 8939 + "\u8EA2\uC1CC\u0000\uE2F8\u0000\u0000\u0000\u0000\u8EA2\uC8DF" + // 8940 - 8944 + "\u0000\uE7DB\u0000\uE7D8\u8EA2\uC8DE\u0000\uE7DA\u8EA2\uC8DC" + // 8945 - 8949 + "\u0000\uE7D9\u0000\u0000\u8EA2\uC8DD\u0000\u0000\u8EA2\uCFA5" + // 8950 - 8954 + "\u8EA2\uC1C8\u0000\u0000\u0000\u0000\u8EA2\uC1C7\u0000\uE2F1" + // 8955 - 8959 + "\u0000\uE2ED\u0000\uE2EE\u8EA2\uC1C3\u8EA2\uC1C5\u8EA2\uC1C6" + // 8960 - 8964 + "\u0000\uE2E9\u0000\uE2EB\u0000\u0000\u0000\uE2EF\u0000\uE2EA" + // 8965 - 8969 + "\u0000\u0000\u0000\u0000\u8EA2\uC8D9\u8EA2\uC8D7\u8EA2\uC8D8" + // 8970 - 8974 + "\u0000\uE7D2\u0000\uE7D3\u0000\u0000\u8EA2\uC8DA\u0000\uE7D1" + // 8975 - 8979 + "\u0000\uEBE1\u0000\u0000\u0000\uEBE3\u0000\uEBE2\u0000\u0000" + // 8980 - 8984 + "\u8EA2\uCFA3\u0000\u0000\u0000\u0000\u8EA2\uCFA4\u8EA2\uD5D4" + // 8985 - 8989 + "\u0000\u0000\u8EA2\uD5D3\u0000\u0000\u0000\u0000\u0000\uF2F0" + // 8990 - 8994 + "\u0000\uF2F1\u8EA2\uDBDB\u0000\uF2F2\u0000\u0000\u8EA2\uE0C3" + // 8995 - 8999 + "\u0000\u0000\u8EA2\uE0C1\u0000\u0000\u8EA2\uE0C2\u0000\uF6A1" + // 9000 - 9004 + "\u0000\u0000\u8EA2\uE4BB\u0000\uF9C4\u0000\u0000\u0000\uA8EC" + // 9005 - 9009 + "\u0000\u0000\u0000\uF6A2\u0000\uCFE3\u0000\u0000\u0000\uD8E9" + // 9010 - 9014 + "\u8EA2\uB3CA\u0000\u0000\u0000\u0000\u8EA2\uBAB0\u0000\uCFDE" + // 9015 - 9019 + "\u0000\u0000\u0000\uCFE2\u8EA2\uA8FC\u0000\uCFE1\u0000\u0000" + // 9020 - 9024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uADE7" + // 9025 - 9029 + "\u0000\uD3EF\u0000\uD3F0\u0000\uD3F1\u8EA2\uADE8\u8EA2\uADE4" + // 9030 - 9034 + "\u0000\uD3EE\u8EA2\uADE5\u0000\u0000\u8EA2\uADE6\u8EA2\uADE3" + // 9035 - 9039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8E7\u0000\u0000" + // 9040 - 9044 + "\u0000\u0000\u0000\uD8E4\u8EA2\uB3C8\u0000\uD8E5\u0000\uD8E8" + // 9045 - 9049 + "\u8EA2\uB3C9\u0000\u0000\u0000\uD8E3\u0000\uD8E1\u0000\uD8E2" + // 9050 - 9054 + "\u0000\uD8E6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9055 - 9059 + "\u0000\u0000\u0000\uDDF1\u8EA2\uBAAD\u0000\uDDF9\u8EA2\uBAAC" + // 9060 - 9064 + "\u0000\u0000\u8EA2\uBAAF\u0000\uDDF5\u8EA2\uBAAE\u0000\uE2F0" + // 9065 - 9069 + "\u0000\uDDF3\u0000\uDDF6\u0000\uDDF2\u0000\uDDF7\u0000\uDDF8" + // 9070 - 9074 + "\u0000\uDDF4\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBAAB" + // 9075 - 9079 + "\u0000\uE2EC\u8EA2\uC1C4\u0000\u0000\u0000\uA3B9\u8EAD\uA4A4" + // 9080 - 9084 + "\u0000\uA3BA\u8EAD\uA4A5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9085 - 9089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9090 - 9094 + "\u0000\uA3BC\u0000\u0000\u0000\u0000\u8EAD\uA4A6\u0000\uA3BD" + // 9095 - 9099 + "\u0000\u0000\u0000\u0000\u8EAD\uA4A7\u0000\uA3BE\u0000\u0000" + // 9100 - 9104 + "\u0000\u0000\u8EAD\uA4A9\u0000\uA3BF\u0000\u0000\u0000\u0000" + // 9105 - 9109 + "\u8EAD\uA4A8\u0000\uA3B7\u8EAD\uA4B4\u0000\u0000\u0000\u0000" + // 9110 - 9114 + "\u8EAD\uA4AF\u0000\u0000\u0000\u0000\u8EAD\uA4AA\u0000\uA3B6" + // 9115 - 9119 + "\u8EAD\uA4B6\u0000\u0000\u0000\u0000\u8EAD\uA4B1\u0000\u0000" + // 9120 - 9124 + "\u0000\u0000\u8EAD\uA4AC\u0000\uA3B5\u0000\u0000\u0000\u0000" + // 9125 - 9129 + "\u8EAD\uA4B0\u8EAD\uA4B5\u0000\u0000\u0000\u0000\u8EAD\uA4AB" + // 9130 - 9134 + "\u0000\uA3B4\u0000\u0000\u0000\u0000\u8EAD\uA4B2\u8EAD\uA4B7" + // 9135 - 9139 + "\u0000\u0000\u0000\u0000\u8EAD\uA4AD\u0000\uA3B3\u0000\u0000" + // 9140 - 9144 + "\u0000\u0000\u8EAD\uA4B3\u8EA2\uE0BC\u0000\u0000\u0000\u0000" + // 9145 - 9149 + "\u8EA2\uE0BE\u0000\u0000\u8EA2\uE0BF\u0000\uF5FE\u8EA2\uE0BB" + // 9150 - 9154 + "\u0000\uF5FD\u8EA2\uE0BD\u0000\uF5FA\u0000\uF5FB\u0000\uF5FC" + // 9155 - 9159 + "\u8EA2\uDBD3\u0000\u0000\u0000\u0000\u0000\uF7E0\u8EA2\uE4BA" + // 9160 - 9164 + "\u8EA2\uE4B8\u8EA2\uE4B9\u0000\uF7DE\u0000\uF7E1\u0000\uF7DF" + // 9165 - 9169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE7E4\u8EA2\uE7E5" + // 9170 - 9174 + "\u0000\uF9C3\u0000\u0000\u8EA2\uEAD3\u8EA2\uEAD5\u8EA2\uEAD4" + // 9175 - 9179 + "\u0000\uFACF\u0000\uFBBC\u8EA2\uECDB\u8EA2\uECDA\u8EA2\uECD9" + // 9180 - 9184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9185 - 9189 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9190 - 9194 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9195 - 9199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9200 - 9204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9205 - 9209 + "\u0000\u0000\u0000\uA2BD\u0000\u0000\u0000\u0000\u0000\u0000" + // 9210 - 9214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9215 - 9219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9220 - 9224 + "\u0000\uA2BB\u0000\uA2BE\u0000\u0000\u0000\u0000\u0000\u0000" + // 9225 - 9229 + "\u0000\u0000\u0000\uA2B9\u0000\uA2BA\u0000\u0000\u0000\u0000" + // 9230 - 9234 + "\u8EAD\uA1C5\u8EAD\uA1C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 9235 - 9239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9240 - 9244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9245 - 9249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9250 - 9254 + "\u0000\u0000\u0000\u0000\u0000\uA2D8\u0000\uA2D5\u0000\uA2D7" + // 9255 - 9259 + "\u0000\uA2D6\u0000\u0000\u0000\u0000\u0000\uA2D9\u0000\uA2DA" + // 9260 - 9264 + "\u0000\uA2DC\u0000\uA2DB\u0000\u0000\u0000\u0000\u0000\u0000" + // 9265 - 9269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9270 - 9274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9275 - 9279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9280 - 9284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9285 - 9289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9290 - 9294 + "\u0000\u0000\u0000\u0000\u8EAD\uA4BE\u8EAD\uA4BF\u0000\u0000" + // 9295 - 9299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9300 - 9304 + "\u0000\uA2F7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9305 - 9309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9310 - 9314 + "\u0000\uA2F3\u0000\u0000\u0000\u0000\u0000\uA2CC\u0000\uA2CB" + // 9315 - 9319 + "\u0000\u0000\u0000\u0000\u0000\uA2EF\u0000\u0000\u0000\u0000" + // 9320 - 9324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9325 - 9329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9330 - 9334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9335 - 9339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9340 - 9344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9345 - 9349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9350 - 9354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9355 - 9359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9360 - 9364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9365 - 9369 + "\u0000\u0000\u0000\uA1AB\u0000\uA1B6\u0000\uA1B8\u0000\uA1BA" + // 9370 - 9374 + "\u0000\uA1BC\u0000\uA1C0\u0000\uA1C1\u0000\uA1C4\u0000\uA1C5" + // 9375 - 9379 + "\u0000\uA1C8\u0000\uA1C9\u0000\uA1CC\u0000\uA1CD\u0000\uA1D0" + // 9380 - 9384 + "\u0000\uA1D1\u0000\uA1D4\u0000\uCFDC\u0000\u0000\u0000\uD3ED" + // 9385 - 9389 + "\u0000\uD8E0\u0000\u0000\u0000\u0000\u8EA2\uBAA9\u0000\u0000" + // 9390 - 9394 + "\u8EA2\uBAAA\u0000\uDDF0\u0000\u0000\u0000\uE2E4\u8EA2\uC1C1" + // 9395 - 9399 + "\u8EA2\uC1C0\u0000\uE2E8\u0000\uE2E3\u8EA2\uC1C2\u0000\uE2E5" + // 9400 - 9404 + "\u0000\uE2E7\u0000\uE2E6\u0000\uE2E2\u0000\u0000\u0000\u0000" + // 9405 - 9409 + "\u0000\u0000\u0000\uE7D0\u0000\u0000\u0000\u0000\u8EA2\uC8D6" + // 9410 - 9414 + "\u8EA2\uC8D4\u0000\u0000\u8EA2\uC8D5\u8EA2\uC8D3\u0000\u0000" + // 9415 - 9419 + "\u0000\uEBDB\u0000\u0000\u0000\uEBDE\u0000\uEBE0\u0000\uEBDF" + // 9420 - 9424 + "\u0000\u0000\u0000\u0000\u0000\uEBDC\u0000\uEBDD\u0000\u0000" + // 9425 - 9429 + "\u8EA2\uD5D0\u8EA2\uD5CF\u0000\uEFE4\u8EA2\uD5D1\u0000\u0000" + // 9430 - 9434 + "\u8EA2\uD5D2\u0000\uEFE5\u0000\u0000\u0000\u0000\u0000\u0000" + // 9435 - 9439 + "\u8EA2\uDBD9\u8EA2\uDBD7\u8EA2\uE0C0\u0000\u0000\u8EA2\uDBD5" + // 9440 - 9444 + "\u8EA2\uDBD6\u0000\uF2EF\u8EA2\uDBD2\u8EA2\uDBDA\u8EA2\uDBD4" + // 9445 - 9449 + "\u8EA2\uDBD8\u8EA2\uF2B0\u8EA2\uF2B1\u8EA2\uF2AF\u8EA2\uF2B7" + // 9450 - 9454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9455 - 9459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9460 - 9464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9465 - 9469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9470 - 9474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9475 - 9479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9480 - 9484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9485 - 9489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9490 - 9494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9495 - 9499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9500 - 9504 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9505 - 9509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9510 - 9514 + "\u8EAD\uA1A9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9515 - 9519 + "\u0000\u0000\u8EA2\uA1BA\u0000\u0000\u0000\u0000\u8EA2\uA3D0" + // 9520 - 9524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA7C2\u0000\u0000" + // 9525 - 9529 + "\u8EA2\uA6A3\u8EA2\uA9E1\u8EA2\uAEDB\u0000\uD4EE\u0000\u0000" + // 9530 - 9534 + "\u0000\u0000\u8EA2\uE5C5\u0000\u0000\u0000\uFAE7\u0000\uC4CA" + // 9535 - 9539 + "\u0000\uC6C0\u8EA2\uA1D1\u0000\u0000\u0000\uC7E8\u0000\uC7E9" + // 9540 - 9544 + "\u0000\u0000\u0000\uCCDF\u0000\u0000\u0000\u0000\u0000\u0000" + // 9545 - 9549 + "\u0000\uD9DC\u0000\u0000\u0000\uE8C2\u0000\u0000\u0000\uE8C3" + // 9550 - 9554 + "\u0000\uE8C1\u0000\u0000\u0000\uC4CB\u0000\u0000\u0000\uC5B2" + // 9555 - 9559 + "\u0000\uC5B4\u0000\uC5B3\u8EA2\uA1BB\u0000\uC5B5\u0000\uC6C1" + // 9560 - 9564 + "\u8EA2\uA1D2\u0000\u0000\u0000\uC6C2\u0000\u0000\u0000\u0000" + // 9565 - 9569 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7EA\u0000\uC7EB" + // 9570 - 9574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA2AB\u0000\u0000" + // 9575 - 9579 + "\u0000\uC9E8\u0000\u0000\u0000\uC2A1\u0000\uC2A2\u0000\uC2A3" + // 9580 - 9584 + "\u0000\uC2A4\u0000\uC2A5\u0000\uC2A6\u0000\uC2A7\u0000\uC2A8" + // 9585 - 9589 + "\u0000\uC2A9\u0000\uC2AA\u0000\uC2AB\u0000\uC2AC\u0000\uC2AD" + // 9590 - 9594 + "\u0000\uC2AE\u0000\uC2AF\u0000\uC2B0\u0000\uC2B1\u0000\uC2B2" + // 9595 - 9599 + "\u0000\uC2B3\u0000\uC2B4\u0000\uC2B5\u0000\uC2B6\u0000\uC2B7" + // 9600 - 9604 + "\u0000\uC2B8\u0000\uC2B9\u0000\uC2BA\u0000\uC2BB\u0000\uC2BC" + // 9605 - 9609 + "\u0000\uC2BD\u0000\uC2BE\u0000\uC2BF\u0000\uC2C0\u0000\u0000" + // 9610 - 9614 + "\u0000\uC2C1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9615 - 9619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9620 - 9624 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9625 - 9629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9630 - 9634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9635 - 9639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9640 - 9644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA6A1\u0000\uA6A2" + // 9645 - 9649 + "\u0000\uA6A3\u0000\uA6A4\u0000\uA6A5\u0000\uA6A6\u0000\uA6A7" + // 9650 - 9654 + "\u0000\uA6A8\u0000\uA6A9\u0000\uA6AA\u0000\u0000\u0000\u0000" + // 9655 - 9659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9660 - 9664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA6AB\u0000\uA6AC" + // 9665 - 9669 + "\u0000\uA6AD\u0000\uA6AE\u0000\uA6AF\u0000\uA6B0\u0000\uA6B1" + // 9670 - 9674 + "\u0000\uA6B2\u0000\uA6B3\u0000\uA6B4\u0000\u0000\u0000\u0000" + // 9675 - 9679 + "\u8EA2\uCCE4\u0000\u0000\u0000\uEAB1\u8EA2\uCCDF\u0000\u0000" + // 9680 - 9684 + "\u0000\uEAB0\u8EA2\uCCD5\u0000\u0000\u8EA2\uCCD7\u0000\uEAA9" + // 9685 - 9689 + "\u8EA2\uCCDC\u8EA2\uCCDB\u8EA2\uCCDE\u0000\uEAAE\u8EA2\uCCD9" + // 9690 - 9694 + "\u8EA2\uCCD8\u0000\u0000\u0000\u0000\u0000\uEAAD\u0000\uEAA8" + // 9695 - 9699 + "\u8EA2\uCCDA\u0000\uEAAB\u8EA2\uCCE0\u8EA2\uCCE2\u0000\u0000" + // 9700 - 9704 + "\u8EA2\uCCDD\u8EA2\uCCD6\u0000\uEAAC\u0000\u0000\u0000\u0000" + // 9705 - 9709 + "\u0000\uEEB4\u0000\uEAA7\u0000\u0000\u0000\u0000\u8EA2\uCCE3" + // 9710 - 9714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9715 - 9719 + "\u0000\u0000\u0000\u0000\u8EA2\uD2E1\u0000\uEEAD\u0000\u0000" + // 9720 - 9724 + "\u8EA2\uD2E3\u0000\u0000\u0000\uEEAE\u0000\u0000\u0000\u0000" + // 9725 - 9729 + "\u0000\uEEB0\u8EA2\uD2E6\u0000\u0000\u8EA2\uD2DE\u0000\uEAAF" + // 9730 - 9734 + "\u8EA2\uD2E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9735 - 9739 + "\u8EA2\uD2E0\u0000\u0000\u0000\uF0DF\u0000\uF0E6\u8EA2\uD7B6" + // 9740 - 9744 + "\u0000\uF0E4\u0000\uF0DE\u0000\u0000\u0000\uF0E5\u0000\u0000" + // 9745 - 9749 + "\u8EA2\uD7B3\u0000\uF3E4\u0000\uF0E0\u0000\u0000\u0000\uF0E7" + // 9750 - 9754 + "\u0000\uF3E3\u8EA2\uD7B2\u8EA2\uD7B1\u0000\u0000\u0000\uF0E9" + // 9755 - 9759 + "\u0000\u0000\u0000\uF0EA\u0000\u0000\u8EA2\uD7B0\u8EA2\uD7AF" + // 9760 - 9764 + "\u0000\uF3E5\u8EA2\uD7B9\u0000\uF0E3\u8EA2\uD7B7\u0000\u0000" + // 9765 - 9769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF3E6\u0000\u0000" + // 9770 - 9774 + "\u0000\uF3EB\u8EA2\uDCF5\u8EA2\uDCF7\u0000\u0000\u0000\uF3E8" + // 9775 - 9779 + "\u0000\u0000\u8EA2\uDCF8\u8EA2\uDCF4\u0000\u0000\u8EA2\uDCF6" + // 9780 - 9784 + "\u0000\uF3E9\u0000\uF3EC\u0000\u0000\u8EA2\uDCF3\u0000\uF3E7" + // 9785 - 9789 + "\u0000\uF3EA\u0000\uF6C5\u8EA2\uD7B8\u0000\uF6C4\u0000\u0000" + // 9790 - 9794 + "\u0000\u0000\u0000\uF6CA\u8EA2\uE1E0\u0000\u0000\u0000\uF6C8" + // 9795 - 9799 + "\u0000\uF6C9\u8EA2\uE1E3\u8EA2\uE1DF\u0000\uF6C6\u8EA2\uE1DD" + // 9800 - 9804 + "\u8EA2\uECD7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFBFB" + // 9805 - 9809 + "\u8EA2\uEEBC\u8EA2\uEEBF\u8EA2\uEEBB\u8EA2\uEEBD\u8EA2\uEEBE" + // 9810 - 9814 + "\u8EA2\uEEB8\u8EA2\uEEB9\u8EA2\uEEB6\u0000\u0000\u0000\u0000" + // 9815 - 9819 + "\u8EA2\uEEBA\u8EA2\uEEB7\u0000\uFBFC\u0000\uFBFD\u0000\u0000" + // 9820 - 9824 + "\u0000\u0000\u8EA2\uEFD9\u0000\u0000\u8EA2\uEFDB\u0000\u0000" + // 9825 - 9829 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEFDA" + // 9830 - 9834 + "\u8EA2\uEFDC\u0000\u0000\u0000\uFCCC\u0000\u0000\u8EA2\uEFD8" + // 9835 - 9839 + "\u0000\uFCCB\u0000\uFCCD\u0000\u0000\u0000\u0000\u0000\u0000" + // 9840 - 9844 + "\u8EA2\uF0D9\u8EA2\uF0DA\u0000\uFCEF\u8EA2\uF0D8\u0000\u0000" + // 9845 - 9849 + "\u8EA2\uF1C2\u8EA2\uF1C1\u8EA2\uF1C3\u0000\uFDAC\u8EA2\uF1C4" + // 9850 - 9854 + "\u0000\uFDAB\u8EA2\uF1C5\u8EA2\uF1E1\u8EA2\uF1E4\u8EA2\uF1E3" + // 9855 - 9859 + "\u0000\uFDB5\u8EA2\uF1E2\u0000\u0000\u0000\u0000\u0000\u0000" + // 9860 - 9864 + "\u0000\uFDBF\u0000\uFDBD\u0000\uFDBE\u0000\uFDC4\u8EA2\uEAC2" + // 9865 - 9869 + "\u0000\u0000\u0000\u0000\u0000\uFACD\u0000\u0000\u0000\u0000" + // 9870 - 9874 + "\u8EA2\uEAD2\u8EA2\uEAC4\u0000\u0000\u8EA2\uEAC9\u8EA2\uEACC" + // 9875 - 9879 + "\u8EA2\uEAB5\u8EA2\uEACF\u8EA2\uEAC0\u8EA2\uEAC5\u8EA2\uEAB9" + // 9880 - 9884 + "\u8EA2\uEABD\u8EA2\uEAD1\u8EA2\uEAC7\u8EA2\uEAB6\u8EA2\uEABA" + // 9885 - 9889 + "\u8EA2\uEABC\u8EA2\uEAC6\u0000\u0000\u0000\uFACC\u8EA2\uEABF" + // 9890 - 9894 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9895 - 9899 + "\u0000\u0000\u8EA2\uEAB8\u0000\u0000\u0000\u0000\u0000\u0000" + // 9900 - 9904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEABE" + // 9905 - 9909 + "\u8EA2\uECD0\u8EA2\uECD4\u0000\uFBBB\u8EA2\uECD6\u0000\u0000" + // 9910 - 9914 + "\u0000\uFBB5\u0000\u0000\u8EA2\uECD2\u8EA2\uECD8\u0000\uFBBA" + // 9915 - 9919 + "\u0000\uFBB6\u0000\u0000\u0000\uFBB7\u8EA2\uECCF\u8EA2\uECD5" + // 9920 - 9924 + "\u0000\uFBB9\u8EA2\uECD3\u0000\uFBB8\u8EA2\uECCE\u8EA2\uEACB" + // 9925 - 9929 + "\u8EA2\uECD1\u0000\u0000\u8EA2\uECCD\u8EA2\uE7DF\u0000\u0000" + // 9930 - 9934 + "\u8EA2\uE7CF\u0000\uF9B8\u8EA2\uE7DD\u0000\u0000\u0000\u0000" + // 9935 - 9939 + "\u8EA2\uE7CD\u0000\uF9B9\u0000\u0000\u8EA2\uE7D8\u0000\u0000" + // 9940 - 9944 + "\u8EA2\uE7D4\u0000\uF9BE\u8EA2\uE7DE\u8EA2\uE7CE\u8EA2\uE7D1" + // 9945 - 9949 + "\u0000\uF9B6\u8EA2\uE7E0\u0000\u0000\u8EA2\uE7D9\u8EA2\uE7DC" + // 9950 - 9954 + "\u0000\uF9BC\u0000\uF9C1\u0000\uF9BF\u8EA2\uE7D5\u8EA2\uE7D0" + // 9955 - 9959 + "\u0000\u0000\u0000\uF9BA\u0000\uF9BB\u8EA2\uE7CC\u0000\uF9B7" + // 9960 - 9964 + "\u0000\u0000\u0000\uF9B5\u0000\uF9BD\u8EA2\uE7DB\u0000\uF9C0" + // 9965 - 9969 + "\u0000\u0000\u8EA2\uE7D7\u8EA2\uE7E2\u0000\uF9C2\u0000\u0000" + // 9970 - 9974 + "\u0000\u0000\u0000\u0000\u8EA2\uE7D3\u0000\u0000\u8EA2\uE7DA" + // 9975 - 9979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 9980 - 9984 + "\u0000\u0000\u8EA2\uEAC1\u8EA2\uEAD0\u8EA2\uEAC3\u8EA2\uEACA" + // 9985 - 9989 + "\u8EA2\uE7D2\u8EA2\uEAC8\u8EA2\uEAB7\u8EA2\uEACE\u0000\uFACE" + // 9990 - 9994 + "\u8EA2\uEABB\u8EA2\uEACD\u8EA2\uE4AF\u0000\u0000\u0000\uF5ED" + // 9995 - 9999 + "\u8EA2\uDFFA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10000 - 10004 + "\u8EA2\uE4AC\u8EA2\uE4A5\u0000\uF7D4\u0000\u0000\u8EA2\uE3FE" + // 10005 - 10009 + "\u8EA2\uE4B0\u8EA2\uE4A7\u8EA2\uE0AA\u0000\u0000\u8EA2\uE4B2" + // 10010 - 10014 + "\u8EA2\uE4A1\u0000\u0000\u0000\uF7D3\u8EA2\uE4AB\u0000\uF7D5" + // 10015 - 10019 + "\u0000\uF7DD\u0000\uF7DB\u8EA2\uE4AD\u0000\uF7DC\u8EA2\uE4A3" + // 10020 - 10024 + "\u0000\u0000\u8EA2\uE4A4\u8EA2\uE4A9\u8EA2\uE4AE\u0000\u0000" + // 10025 - 10029 + "\u8EA2\uDFF9\u0000\uF7D6\u8EA2\uE4B7\u8EA2\uE4B4\u8EA2\uE7E1" + // 10030 - 10034 + "\u8EA2\uE4AA\u8EA2\uE4A6\u8EA2\uE4B5\u8EA2\uE7D6\u8EA2\uE4A8" + // 10035 - 10039 + "\u0000\u0000\u0000\uF7D9\u0000\u0000\u0000\uF7D8\u8EA2\uDFFB" + // 10040 - 10044 + "\u0000\uF7DA\u8EA2\uE4B1\u8EA2\uE4B3\u0000\uF7D7\u8EA2\uE4B6" + // 10045 - 10049 + "\u8EA2\uE3FD\u0000\u0000\u8EA2\uE4A2\u0000\u0000\u0000\u0000" + // 10050 - 10054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10055 - 10059 + "\u0000\u0000\u8EA2\uDBB8\u8EA2\uDFFE\u0000\u0000\u8EA2\uE0A5" + // 10060 - 10064 + "\u0000\uF5F0\u0000\uF5F2\u8EA2\uE0AB\u0000\uF5EC\u8EA2\uE0B9" + // 10065 - 10069 + "\u8EA2\uE0B1\u8EA2\uE0A6\u8EA2\uE0A7\u8EA2\uE0B0\u0000\u0000" + // 10070 - 10074 + "\u0000\uF5F9\u8EA2\uE0AF\u8EA2\uDFFD\u8EA2\uE0AE\u8EA2\uE0A2" + // 10075 - 10079 + "\u8EA2\uE0BA\u0000\uF5F8\u0000\uF5F6\u8EA2\uE0A3\u0000\u0000" + // 10080 - 10084 + "\u8EA2\uE0B6\u0000\u0000\u8EA2\uE0A8\u8EA2\uE0B5\u0000\u0000" + // 10085 - 10089 + "\u8EA2\uE0B7\u8EA2\uDFFC\u0000\uF5F1\u0000\u0000\u8EA2\uE0B8" + // 10090 - 10094 + "\u0000\u0000\u0000\u0000\u8EA2\uE0AC\u0000\u0000\u0000\uF5F5" + // 10095 - 10099 + "\u8EA2\uE0A9\u0000\u0000\u0000\u0000\u0000\uF5F7\u8EA2\uE0B2" + // 10100 - 10104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF5EF\u8EA2\uE0A4" + // 10105 - 10109 + "\u8EA2\uE0B3\u0000\u0000\u8EA2\uE0AD\u8EA2\uDBB1\u8EA2\uE0B4" + // 10110 - 10114 + "\u8EA2\uE0A1\u0000\u0000\u0000\uF5F4\u0000\u0000\u8EAD\uA1C0" + // 10115 - 10119 + "\u0000\u0000\u8EAD\uA1C3\u8EAD\uA1C1\u0000\u0000\u0000\u0000" + // 10120 - 10124 + "\u0000\u0000\u8EAD\uA1C4\u8EAD\uA1B6\u0000\u0000\u0000\u0000" + // 10125 - 10129 + "\u8EAD\uA1B7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10130 - 10134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10135 - 10139 + "\u0000\uA2E1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10140 - 10144 + "\u0000\uA2B5\u0000\u0000\u0000\u0000\u8EAD\uA1C8\u0000\uA2BC" + // 10145 - 10149 + "\u0000\uA2C9\u0000\uA2C8\u0000\u0000\u0000\u0000\u0000\u0000" + // 10150 - 10154 + "\u0000\u0000\u0000\uA2DD\u0000\u0000\u8EAD\uA1BC\u8EAD\uA1BD" + // 10155 - 10159 + "\u0000\uA2C5\u0000\uA2C6\u0000\uA2CD\u8EAD\uA1C9\u0000\u0000" + // 10160 - 10164 + "\u0000\uA2CE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10165 - 10169 + "\u0000\u0000\u0000\uA2D0\u0000\uA2CF\u0000\u0000\u0000\u0000" + // 10170 - 10174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2C4" + // 10175 - 10179 + "\u8EAD\uA1C7\u0000\u0000\u0000\u0000\u8EA2\uE2C1\u0000\u0000" + // 10180 - 10184 + "\u8EA2\uE6A7\u0000\u0000\u0000\u0000\u0000\uF8D4\u0000\u0000" + // 10185 - 10189 + "\u8EA2\uE6A5\u8EA2\uE6A6\u0000\u0000\u8EA2\uE9C3\u8EA2\uE9C6" + // 10190 - 10194 + "\u8EA2\uE9C4\u8EA2\uE9C5\u8EA2\uEBEE\u0000\u0000\u0000\u0000" + // 10195 - 10199 + "\u0000\uFAFA\u8EA2\uEFBA\u8EA2\uF0C2\u0000\u0000\u0000\uFCE3" + // 10200 - 10204 + "\u8EA2\uF1A8\u8EA2\uF1A9\u0000\uFDB3\u0000\uC6F9\u0000\uD2E4" + // 10205 - 10209 + "\u0000\u0000\u8EA2\uBEC7\u0000\u0000\u8EA2\uC5C9\u0000\u0000" + // 10210 - 10214 + "\u0000\uC6FA\u0000\uCBB2\u0000\u0000\u0000\uCFA1\u0000\u0000" + // 10215 - 10219 + "\u8EA2\uACCA\u8EA2\uACCB\u0000\uD6FB\u0000\u0000\u0000\u0000" + // 10220 - 10224 + "\u8EA2\uBEC8\u0000\uE0FB\u0000\uE5CE\u0000\uF4CC\u8EA2\uDDEE" + // 10225 - 10229 + "\u8EA2\uE6A8\u8EA2\uE9C7\u0000\uC6FB\u0000\u0000\u0000\u0000" + // 10230 - 10234 + "\u0000\u0000\u8EA2\uA8C1\u8EA2\uA8BC\u8EA2\uA8BE\u8EA2\uA8C0" + // 10235 - 10239 + "\u8EA2\uA8BF\u8EA2\uA8BD\u0000\uCFA2\u0000\u0000\u0000\u0000" + // 10240 - 10244 + "\u0000\uD2E5\u8EA2\uACD4\u0000\u0000\u8EA2\uACCF\u8EA2\uACCC" + // 10245 - 10249 + "\u0000\u0000\u0000\u0000\u8EA2\uACD3\u0000\u0000\u0000\u0000" + // 10250 - 10254 + "\u0000\uD2E7\u0000\uD2E8\u8EA2\uACD2\u8EA2\uACD1\u8EA2\uACD0" + // 10255 - 10259 + "\u8EA2\uACCD\u8EA2\uACCE\u8EA2\uACD5\u0000\uD2E6\u0000\u0000" + // 10260 - 10264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10265 - 10269 + "\u0000\u0000\u0000\u0000\u0000\uD7A1\u0000\u0000\u0000\uD7A7" + // 10270 - 10274 + "\u0000\uD7A6\u8EA2\uB1C4\u8EA2\uB1BE\u8EA2\uB1BC\u0000\u0000" + // 10275 - 10279 + "\u0000\uD7A4\u8EA2\uB7DC\u0000\uD6FD\u8EA2\uB1C1\u8EA2\uB1C5" + // 10280 - 10284 + "\u8EA2\uB1C7\u8EA2\uB1C3\u8EA2\uB1BD\u0000\uD7A5\u8EA2\uB1C2" + // 10285 - 10289 + "\u8EA2\uB1C0\u0000\uD6FC\u8EA2\uB1C8\u0000\uD7A8\u8EA2\uB1C6" + // 10290 - 10294 + "\u0000\uD7A2\u8EA2\uB1BF\u0000\u0000\u0000\uD7A3\u0000\uD6FE" + // 10295 - 10299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10300 - 10304 + "\u0000\u0000\u0000\u0000\u0000\uA5C7\u0000\uA5C8\u0000\uA5C9" + // 10305 - 10309 + "\u0000\uA5CA\u0000\uA5CB\u0000\uA5CC\u0000\uA5CD\u0000\uA5CE" + // 10310 - 10314 + "\u0000\uA5CF\u0000\uA5D0\u0000\uA5D1\u0000\uA5D2\u0000\uA5D3" + // 10315 - 10319 + "\u0000\uA5D4\u0000\uA5D5\u0000\uA5D6\u0000\uA5D7\u0000\uA5D8" + // 10320 - 10324 + "\u0000\uA5D9\u0000\uA5DA\u0000\uA5DB\u0000\uA5DC\u0000\uA5DD" + // 10325 - 10329 + "\u0000\uA5DE\u0000\uA5DF\u0000\uA5E0\u0000\uA5E1\u0000\uA5E2" + // 10330 - 10334 + "\u0000\uA5E3\u0000\uA5E4\u0000\uA5E5\u0000\uA5E6\u0000\uA5E7" + // 10335 - 10339 + "\u0000\uA5E8\u0000\uA5E9\u0000\uA5EA\u0000\uA5EB\u0000\u0000" + // 10340 - 10344 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10345 - 10349 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10350 - 10354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10355 - 10359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10360 - 10364 + "\u0000\u0000\u0000\uA4F5\u0000\uA4F6\u0000\uA4F7\u0000\uA4F8" + // 10365 - 10369 + "\u0000\uA4F9\u0000\uA4FA\u0000\uA4FB\u0000\uA4FC\u0000\uA4FD" + // 10370 - 10374 + "\u0000\uA4FE\u0000\uA5A1\u0000\uA5A2\u0000\uA5A3\u0000\uA5A4" + // 10375 - 10379 + "\u0000\uA5A5\u0000\uA5A6\u0000\uA5A7\u0000\u0000\u0000\uA5A8" + // 10380 - 10384 + "\u0000\uA5A9\u0000\uA5AA\u0000\uA5AB\u0000\uA5AC\u0000\uA5AD" + // 10385 - 10389 + "\u0000\uA5AE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10390 - 10394 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA5AF\u0000\uA5B0" + // 10395 - 10399 + "\u0000\uA5B1\u0000\uA5B2\u0000\uA5B3\u0000\uA5B4\u0000\uA5B5" + // 10400 - 10404 + "\u0000\uA5B6\u0000\uA5B7\u0000\uA5B8\u0000\uA5B9\u0000\uA5BA" + // 10405 - 10409 + "\u0000\uA5BB\u0000\uA5BC\u0000\uA5BD\u8EA2\uDBCF\u8EA2\uDBB7" + // 10410 - 10414 + "\u8EA2\uDBC3\u0000\u0000\u0000\uF2E7\u0000\u0000\u8EA2\uDBAF" + // 10415 - 10419 + "\u0000\u0000\u8EA2\uDBAD\u8EA2\uDBCE\u0000\u0000\u8EA2\uDBCC" + // 10420 - 10424 + "\u8EA2\uDBCB\u8EA2\uDBBB\u8EA2\uDBBA\u8EA2\uDBB0\u0000\uF2E9" + // 10425 - 10429 + "\u0000\u0000\u8EA2\uDBB6\u8EA2\uDBBF\u8EA2\uDBCA\u0000\uF2EC" + // 10430 - 10434 + "\u8EA2\uDBD1\u0000\u0000\u0000\uF5F3\u0000\uF2EE\u0000\uF2E8" + // 10435 - 10439 + "\u8EA2\uDBB4\u0000\u0000\u8EA2\uDBBD\u8EA2\uDBAC\u8EA2\uDBAE" + // 10440 - 10444 + "\u0000\uF2DF\u0000\uF2EB\u0000\uF2E4\u8EA2\uDBB5\u8EA2\uDBC4" + // 10445 - 10449 + "\u8EA2\uD5CB\u0000\uF2EA\u8EA2\uDBAB\u0000\uF5EE\u8EA2\uDBC6" + // 10450 - 10454 + "\u8EA2\uDBC9\u0000\uF2E6\u0000\u0000\u8EA2\uDBB9\u0000\uF2ED" + // 10455 - 10459 + "\u0000\uF2E3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2E2" + // 10460 - 10464 + "\u8EA2\uDBC2\u8EA2\uDBC8\u0000\uF2E0\u0000\u0000\u8EA2\uDBB2" + // 10465 - 10469 + "\u8EA2\uDBC7\u0000\u0000\u0000\u0000\u8EA2\uDBB3\u0000\u0000" + // 10470 - 10474 + "\u0000\u0000\u0000\u0000\u0000\uF4C3\u8EA2\uDDE0\u0000\u0000" + // 10475 - 10479 + "\u0000\u0000\u0000\uF4C2\u8EA2\uDDE3\u8EA2\uDDE1\u8EA2\uDDE2" + // 10480 - 10484 + "\u0000\u0000\u0000\u0000\u0000\uF4C4\u0000\u0000\u0000\u0000" + // 10485 - 10489 + "\u0000\u0000\u8EA2\uE2BA\u0000\u0000\u0000\uF6EB\u8EA2\uE2BB" + // 10490 - 10494 + "\u0000\u0000\u0000\u0000\u0000\uF6E9\u8EA2\uE2BC\u0000\uF6EA" + // 10495 - 10499 + "\u8EA2\uE2B9\u8EA2\uE2BD\u0000\u0000\u8EA2\uE2B7\u0000\u0000" + // 10500 - 10504 + "\u0000\u0000\u0000\uF8D2\u8EA2\uE6A4\u0000\uF8D3\u0000\uF9FA" + // 10505 - 10509 + "\u0000\u0000\u8EA2\uE2B8\u0000\uF9FB\u0000\u0000\u0000\u0000" + // 10510 - 10514 + "\u0000\u0000\u0000\uFAF9\u8EA2\uEBED\u0000\u0000\u0000\uFBEA" + // 10515 - 10519 + "\u8EA2\uEDE2\u0000\uFBE9\u0000\u0000\u8EA2\uEFB9\u0000\uFCE1" + // 10520 - 10524 + "\u0000\uFCE2\u0000\u0000\u0000\u0000\u8EA2\uF2BA\u0000\uA8AA" + // 10525 - 10529 + "\u0000\u0000\u0000\uD2D4\u8EA2\uACC0\u0000\u0000\u0000\uE0F4" + // 10530 - 10534 + "\u0000\uE0F5\u0000\uC6F5\u0000\uC8CB\u8EA2\uA2E7\u8EA2\uD5AF" + // 10535 - 10539 + "\u0000\uEFDD\u8EA2\uD5BB\u8EA2\uD5AD\u8EA2\uD5AE\u0000\uEFD8" + // 10540 - 10544 + "\u8EA2\uD5CC\u0000\uEFE1\u8EA2\uD5BE\u8EA2\uD5C3\u8EA2\uD5BD" + // 10545 - 10549 + "\u8EA2\uDBBC\u8EA2\uD5B9\u8EA2\uD5C1\u8EA2\uD5BF\u8EA2\uD5B2" + // 10550 - 10554 + "\u8EA2\uD5AC\u8EA2\uD5C7\u0000\uEFE0\u8EA2\uD5C8\u0000\u0000" + // 10555 - 10559 + "\u8EA2\uD5C2\u0000\u0000\u8EA2\uD5B7\u8EA2\uD5B5\u8EA2\uD5B0" + // 10560 - 10564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD5B8\u8EA2\uD5C5" + // 10565 - 10569 + "\u8EA2\uD5B4\u8EA2\uD5C4\u8EA2\uD5CA\u0000\u0000\u0000\u0000" + // 10570 - 10574 + "\u0000\uEFDC\u0000\u0000\u8EA2\uD5C0\u8EA2\uD5C6\u8EA2\uD5BC" + // 10575 - 10579 + "\u8EA2\uD5B6\u0000\uEFDB\u0000\u0000\u0000\u0000\u0000\u0000" + // 10580 - 10584 + "\u8EA2\uCEEE\u8EA2\uD5BA\u0000\uEFE2\u8EA2\uD5B3\u0000\u0000" + // 10585 - 10589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDBC1" + // 10590 - 10594 + "\u0000\uF2E1\u8EA2\uDBC0\u8EA2\uDBBE\u8EA2\uDBD0\u0000\uF2E5" + // 10595 - 10599 + "\u0000\u0000\u8EA2\uDBCD\u8EA2\uDBC5\u0000\uEBD1\u0000\u0000" + // 10600 - 10604 + "\u8EA2\uCEF0\u8EA2\uC8BC\u0000\u0000\u0000\uEBD2\u8EA2\uCEF7" + // 10605 - 10609 + "\u8EA2\uCEFC\u8EA2\uCEF4\u0000\u0000\u8EA2\uCEF6\u8EA2\uCFA1" + // 10610 - 10614 + "\u8EA2\uCEF8\u8EA2\uCEE6\u8EA2\uCEEF\u0000\u0000\u0000\u0000" + // 10615 - 10619 + "\u0000\uEBDA\u0000\u0000\u0000\uEBD6\u8EA2\uCEE4\u8EA2\uCEF1" + // 10620 - 10624 + "\u0000\uEBD4\u8EA2\uCEEB\u0000\uEBD3\u8EA2\uCEF9\u8EA2\uCEE8" + // 10625 - 10629 + "\u8EA2\uCEE1\u0000\uEBD7\u8EA2\uCEFE\u0000\u0000\u0000\u0000" + // 10630 - 10634 + "\u8EA2\uCEE3\u8EA2\uCEF5\u8EA2\uCEF2\u8EA2\uCEED\u8EA2\uCEDF" + // 10635 - 10639 + "\u8EA2\uCEDE\u8EA2\uCEE7\u8EA2\uCEFA\u0000\uEBD8\u8EA2\uCEFD" + // 10640 - 10644 + "\u8EA2\uCEE5\u8EA2\uCEE9\u0000\uEBD0\u0000\u0000\u0000\u0000" + // 10645 - 10649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFE3\u0000\uEFDE" + // 10650 - 10654 + "\u8EA2\uD5CD\u8EA2\uD5C9\u8EA2\uD5B1\u0000\uEFDA\u0000\u0000" + // 10655 - 10659 + "\u0000\u0000\u0000\u0000\u0000\uEFD9\u0000\uEFDF\u0000\u0000" + // 10660 - 10664 + "\u0000\u0000\u0000\u0000\u8EA2\uA7F8\u8EA2\uA7FA\u8EA2\uA7F5" + // 10665 - 10669 + "\u8EA2\uA7F2\u8EA2\uA7F4\u0000\u0000\u0000\u0000\u0000\u0000" + // 10670 - 10674 + "\u0000\uCEE5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEE3" + // 10675 - 10679 + "\u0000\u0000\u0000\u0000\u8EA2\uA7F6\u0000\uCEE4\u8EA2\uA7F3" + // 10680 - 10684 + "\u8EA2\uA7F0\u0000\uCEE2\u8EA2\uA7F7\u0000\u0000\u8EA2\uA7F1" + // 10685 - 10689 + "\u0000\uCEE6\u8EA2\uA7F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 10690 - 10694 + "\u0000\u0000\u8EA2\uABEF\u0000\u0000\u8EA2\uABF3\u0000\u0000" + // 10695 - 10699 + "\u0000\u0000\u0000\uD2BA\u0000\u0000\u0000\u0000\u0000\u0000" + // 10700 - 10704 + "\u0000\u0000\u8EA2\uABF6\u0000\u0000\u0000\uD2B2\u0000\uD2B5" + // 10705 - 10709 + "\u0000\uD2B7\u0000\uD2B9\u0000\uD2B6\u8EA2\uABF2\u8EA2\uABF1" + // 10710 - 10714 + "\u0000\u0000\u0000\uD2B4\u8EA2\uABF4\u8EA2\uABF5\u0000\u0000" + // 10715 - 10719 + "\u8EA2\uABEE\u0000\uD2B8\u0000\u0000\u0000\uD2B3\u0000\u0000" + // 10720 - 10724 + "\u0000\u0000\u0000\u0000\u8EA2\uABF0\u0000\u0000\u8EAD\uA3F2" + // 10725 - 10729 + "\u8EAD\uA3F3\u8EAD\uA3F4\u8EAD\uA3F5\u8EAD\uA3F6\u8EAD\uA3F7" + // 10730 - 10734 + "\u8EAD\uA3F8\u8EAD\uA3F9\u8EAD\uA3FA\u8EAD\uA3FB\u8EAD\uA3FC" + // 10735 - 10739 + "\u8EAD\uA3FD\u8EAD\uA3FE\u8EAD\uA4A1\u8EAD\uA4A2\u8EAD\uA4A3" + // 10740 - 10744 + "\u0000\u0000\u8EAD\uA3E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 10745 - 10749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10750 - 10754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10755 - 10759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10760 - 10764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10765 - 10769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10770 - 10774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10775 - 10779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10780 - 10784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10785 - 10789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10790 - 10794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10795 - 10799 + "\u0000\uDEA5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFADB" + // 10800 - 10804 + "\u8EA2\uEDB7\u0000\u0000\u0000\uFCFB\u0000\uFCFC\u0000\u0000" + // 10805 - 10809 + "\u0000\uDEA6\u0000\uE7C3\u0000\u0000\u0000\u0000\u0000\u0000" + // 10810 - 10814 + "\u0000\u0000\u0000\uE7CC\u8EA2\uC8BF\u0000\u0000\u8EA2\uC8B4" + // 10815 - 10819 + "\u0000\uE7CA\u8EA2\uC8BE\u0000\uE7C6\u8EA2\uC8D0\u0000\uE7CB" + // 10820 - 10824 + "\u8EA2\uC8CE\u8EA2\uC8C2\u8EA2\uC8CC\u0000\uE7C8\u8EA2\uC8B5" + // 10825 - 10829 + "\u8EA2\uC8CF\u8EA2\uC8CA\u0000\u0000\u8EA2\uC8D1\u0000\uE7C0" + // 10830 - 10834 + "\u0000\u0000\u0000\u0000\u0000\uE7CF\u0000\uE7C5\u0000\u0000" + // 10835 - 10839 + "\u0000\u0000\u8EA2\uC8BB\u0000\u0000\u8EA2\uC8C3\u8EA2\uC8C7" + // 10840 - 10844 + "\u0000\u0000\u8EA2\uC8CB\u0000\uE7C7\u8EA2\uC8BA\u8EA2\uC8B8" + // 10845 - 10849 + "\u8EA2\uC8C4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10850 - 10854 + "\u8EA2\uC8C1\u8EA2\uC8C0\u0000\u0000\u8EA2\uC8C5\u8EA2\uC8C8" + // 10855 - 10859 + "\u0000\u0000\u8EA2\uC8CD\u0000\u0000\u0000\u0000\u0000\u0000" + // 10860 - 10864 + "\u8EA2\uCEE0\u0000\u0000\u0000\uEBCF\u8EA2\uCEEA\u8EA2\uCEE2" + // 10865 - 10869 + "\u0000\uEBD5\u0000\uEBD9\u8EA2\uCEF3\u8EA2\uCEFB\u8EA2\uCEEC" + // 10870 - 10874 + "\u8EA2\uC1B1\u8EA2\uC1A9\u8EA2\uC1B8\u8EA2\uC1AC\u8EA2\uC1B6" + // 10875 - 10879 + "\u8EA2\uC1BD\u8EA2\uC1B5\u0000\uE2E0\u0000\u0000\u0000\uE2DC" + // 10880 - 10884 + "\u8EA2\uC1AA\u0000\u0000\u8EA2\uC1B0\u0000\uE2DE\u0000\u0000" + // 10885 - 10889 + "\u8EA2\uC1AF\u0000\uE2DF\u0000\uE2E1\u8EA2\uC1B2\u0000\u0000" + // 10890 - 10894 + "\u0000\uE2D9\u0000\uE2DA\u8EA2\uC1BE\u8EA2\uC1BC\u0000\u0000" + // 10895 - 10899 + "\u8EA2\uC1BB\u8EA2\uC1AD\u0000\u0000\u8EA2\uC1B9\u0000\u0000" + // 10900 - 10904 + "\u0000\uE2DD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10905 - 10909 + "\u0000\uE2DB\u8EA2\uC1BA\u8EA2\uC1AB\u8EA2\uC1AE\u8EA2\uC1B7" + // 10910 - 10914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 10915 - 10919 + "\u8EA2\uCFA2\u8EA2\uC8BD\u0000\u0000\u8EA2\uC8B6\u8EA2\uC8C9" + // 10920 - 10924 + "\u8EA2\uC8D2\u8EA2\uC8B9\u0000\uE7C9\u0000\u0000\u8EA2\uC8C6" + // 10925 - 10929 + "\u0000\uE7BF\u0000\uE7C1\u0000\uE7CD\u8EA2\uC8B7\u0000\u0000" + // 10930 - 10934 + "\u0000\u0000\u0000\uE7C2\u0000\uE7C4\u0000\uE7CE\u0000\uFCEE" + // 10935 - 10939 + "\u0000\uFDAA\u8EA2\uF0D7\u8EA2\uF1E0\u0000\u0000\u0000\uFDBC" + // 10940 - 10944 + "\u0000\uCBDD\u0000\uCFD9\u0000\u0000\u0000\uE7BE\u0000\u0000" + // 10945 - 10949 + "\u0000\uFACB\u0000\uCBDE\u0000\uD3EC\u0000\uDDE9\u0000\uE2D8" + // 10950 - 10954 + "\u0000\uF7D2\u0000\uCFDA\u0000\u0000\u8EA2\uADE2\u8EA2\uADE1" + // 10955 - 10959 + "\u8EA2\uB3C5\u0000\u0000\u0000\uD8DD\u0000\uD8DB\u0000\uD8DF" + // 10960 - 10964 + "\u8EA2\uB3C7\u0000\u0000\u0000\uD8DE\u0000\uD8DC\u0000\u0000" + // 10965 - 10969 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB3C6\u0000\uDDEC" + // 10970 - 10974 + "\u8EA2\uBAA1\u0000\u0000\u0000\uDDEB\u0000\uDDED\u8EA2\uBAA6" + // 10975 - 10979 + "\u0000\uDDEF\u8EA2\uBAA3\u8EA2\uBAA4\u8EA2\uB9FA\u0000\uDDEE" + // 10980 - 10984 + "\u8EA2\uBAA7\u0000\u0000\u0000\u0000\u8EA2\uB9FC\u0000\u0000" + // 10985 - 10989 + "\u8EA2\uB9FD\u8EA2\uB9FB\u0000\uDDEA\u0000\u0000\u8EA2\uBAA5" + // 10990 - 10994 + "\u8EA2\uB9FE\u8EA2\uBAA2\u0000\u0000\u0000\u0000\u0000\u0000" + // 10995 - 10999 + "\u8EA2\uC1B4\u0000\u0000\u8EA2\uC1B3\u8EA2\uD5AB\u8EA2\uD5A8" + // 11000 - 11004 + "\u8EA2\uD5A9\u0000\uEFD7\u8EA2\uD5AA\u8EA2\uD5A5\u8EA2\uD5A6" + // 11005 - 11009 + "\u0000\uEFD4\u0000\u0000\u0000\uEFD5\u8EA2\uD5A7\u0000\uEFD6" + // 11010 - 11014 + "\u0000\u0000\u8EA2\uDBA9\u0000\u0000\u8EA2\uDBAA\u8EA2\uDBA7" + // 11015 - 11019 + "\u8EA2\uDBA8\u0000\uF2DE\u8EA2\uDBA6\u0000\u0000\u0000\u0000" + // 11020 - 11024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDFF4\u8EA2\uDFF1" + // 11025 - 11029 + "\u8EA2\uDFF3\u0000\uF5EB\u8EA2\uDFF7\u0000\uF5EA\u8EA2\uDFF5" + // 11030 - 11034 + "\u8EA2\uDFF8\u8EA2\uDFF6\u8EA2\uDFF2\u0000\uF5E9\u0000\u0000" + // 11035 - 11039 + "\u8EA2\uE3F9\u0000\u0000\u8EA2\uE3FA\u8EA2\uE3F8\u0000\u0000" + // 11040 - 11044 + "\u8EA2\uE3FC\u0000\uF7D0\u0000\uF7D1\u8EA2\uE7CB\u0000\uF9B4" + // 11045 - 11049 + "\u8EA2\uE3FB\u8EA2\uE7CA\u0000\uF9B3\u8EA2\uEAB3\u8EA2\uEAB4" + // 11050 - 11054 + "\u0000\uFACA\u8EA2\uEAB2\u0000\u0000\u8EA2\uEAB1\u0000\u0000" + // 11055 - 11059 + "\u8EA2\uECCC\u0000\uFBB4\u0000\u0000\u8EA2\uEFD7\u8EA2\uF0D6" + // 11060 - 11064 + "\u8EA2\uF0D5\u0000\u0000\u0000\uA5BE\u0000\uA5BF\u0000\u0000" + // 11065 - 11069 + "\u0000\uA5C0\u0000\uA5C1\u0000\uA5C2\u0000\uA5C3\u0000\uA5C4" + // 11070 - 11074 + "\u0000\uA5C5\u0000\uA5C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 11075 - 11079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11080 - 11084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11085 - 11089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11090 - 11094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11095 - 11099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11100 - 11104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11105 - 11109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11110 - 11114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11115 - 11119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11120 - 11124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11125 - 11129 + "\u0000\u0000\u0000\u0000\u0000\uCFDB\u0000\u0000\u0000\u0000" + // 11130 - 11134 + "\u8EA2\uBAA8\u8EA2\uC1BF\u8EA2\uD5CE\u8EA2\uE7E3\u0000\u0000" + // 11135 - 11139 + "\u0000\u0000\u0000\uCEFC\u0000\uD2DA\u8EA2\uACC1\u0000\uD2DB" + // 11140 - 11144 + "\u0000\uD2D9\u0000\u0000\u0000\uD2D8\u8EA2\uB1AF\u0000\uD6F4" + // 11145 - 11149 + "\u0000\u0000\u0000\u0000\u0000\uD6F5\u0000\uD6F6\u0000\u0000" + // 11150 - 11154 + "\u0000\u0000\u0000\u0000\u0000\uDCB5\u8EA2\uB7CA\u0000\uDCB4" + // 11155 - 11159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11160 - 11164 + "\u8EA2\uBEBD\u0000\uDCB6\u0000\uE0F9\u8EA2\uC5BD\u0000\uE5C0" + // 11165 - 11169 + "\u0000\uE5C1\u0000\u0000\u0000\uE9F0\u0000\u0000\u0000\uE9F1" + // 11170 - 11174 + "\u0000\uEDF5\u0000\uF1CE\u8EA2\uD8E1\u0000\uF1CD\u0000\u0000" + // 11175 - 11179 + "\u8EA2\uDDE5\u0000\uF4C5\u0000\u0000\u8EA2\uE2C0\u8EA2\uE9C2" + // 11180 - 11184 + "\u0000\uC6F8\u0000\uCBB1\u8EA2\uA8BA\u8EA2\uA8B9\u0000\uCEFD" + // 11185 - 11189 + "\u8EA2\uA8B8\u0000\uCEFE\u8EA2\uA8BB\u0000\u0000\u8EA2\uACC7" + // 11190 - 11194 + "\u0000\uD2DE\u0000\uD2DD\u8EA2\uACC9\u8EA2\uACC8\u0000\uD2E2" + // 11195 - 11199 + "\u0000\u0000\u0000\uD2E1\u0000\u0000\u8EA2\uD7A9\u0000\uF3DD" + // 11200 - 11204 + "\u8EA2\uDCEA\u0000\u0000\u8EA2\uD7A6\u8EA2\uD7A8\u0000\uF3DE" + // 11205 - 11209 + "\u0000\uF0DC\u0000\uF3DC\u0000\uF0DB\u0000\uF3E0\u8EA2\uD7AA" + // 11210 - 11214 + "\u0000\uF0D8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11215 - 11219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE1DB" + // 11220 - 11224 + "\u0000\u0000\u8EA2\uE1D3\u0000\u0000\u0000\u0000\u0000\u0000" + // 11225 - 11229 + "\u0000\u0000\u0000\u0000\u8EA2\uDCF2\u8EA2\uE1D4\u8EA2\uDCEE" + // 11230 - 11234 + "\u0000\u0000\u0000\u0000\u0000\uF6C2\u8EA2\uDCF0\u8EA2\uDCEF" + // 11235 - 11239 + "\u0000\uF3DF\u8EA2\uDCED\u8EA2\uDCF1\u8EA2\uE1DC\u8EA2\uE1D8" + // 11240 - 11244 + "\u8EA2\uE1DA\u0000\u0000\u8EA2\uE1D5\u8EA2\uE1D6\u0000\u0000" + // 11245 - 11249 + "\u8EA2\uE1D9\u8EA2\uE1D7\u0000\uF8B4\u0000\u0000\u0000\u0000" + // 11250 - 11254 + "\u0000\uF8B7\u0000\uF8B6\u0000\uF8B5\u0000\uF9EC\u8EA2\uE8F6" + // 11255 - 11259 + "\u0000\uF9ED\u8EA2\uE5CC\u0000\uFAEA\u8EA2\uEBDA\u0000\uFAEB" + // 11260 - 11264 + "\u0000\uFBE0\u8EA2\uE7C8\u8EA2\uEAB0\u0000\u0000\u8EA2\uEAAF" + // 11265 - 11269 + "\u8EA2\uECCA\u8EA2\uECCB\u8EA2\uECC9\u8EA2\uEEB5\u0000\uFBFA" + // 11270 - 11274 + "\u0000\uCBDC\u0000\uD3EB\u0000\uD3EA\u0000\uD8DA\u0000\uD8D9" + // 11275 - 11279 + "\u8EA2\uB3C3\u8EA2\uB3C4\u8EA2\uB3C2\u0000\u0000\u0000\uD8D8" + // 11280 - 11284 + "\u8EA2\uB9F8\u0000\u0000\u8EA2\uB9F9\u8EA2\uB9F5\u0000\uDDE8" + // 11285 - 11289 + "\u8EA2\uB9F6\u0000\u0000\u8EA2\uB9F7\u0000\u0000\u0000\u0000" + // 11290 - 11294 + "\u0000\u0000\u0000\u0000\u8EA2\uC1A6\u8EA2\uC1A8\u8EA2\uC1A4" + // 11295 - 11299 + "\u8EA2\uC1A7\u0000\uE2D6\u8EA2\uC1A5\u0000\uE2D7\u0000\u0000" + // 11300 - 11304 + "\u0000\u0000\u8EA2\uC2F9\u0000\uE7BD\u0000\uE7BC\u0000\u0000" + // 11305 - 11309 + "\u0000\uE7BB\u0000\u0000\u8EA2\uC8B2\u8EA2\uC8B3\u0000\u0000" + // 11310 - 11314 + "\u0000\u0000\u8EA2\uCEDB\u8EA2\uCEDD\u0000\uEBCE\u0000\uEBCB" + // 11315 - 11319 + "\u0000\u0000\u0000\uEBCD\u0000\uEBCC\u8EA2\uCEDC\u8EA2\uCEDA" + // 11320 - 11324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11325 - 11329 + "\u0000\uD9B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11330 - 11334 + "\u0000\uC4B4\u0000\uC4C6\u0000\uC5AF\u0000\uC5AE\u0000\u0000" + // 11335 - 11339 + "\u0000\uC5B0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11340 - 11344 + "\u0000\u0000\u0000\u0000\u0000\uCCBE\u0000\u0000\u0000\uCCBD" + // 11345 - 11349 + "\u0000\uCCBF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0B5" + // 11350 - 11354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4D1\u0000\u0000" + // 11355 - 11359 + "\u8EA2\uD6C6\u0000\uF6BB\u0000\uC4C7\u0000\uC6AC\u0000\uC6BB" + // 11360 - 11364 + "\u0000\uC6B6\u0000\u0000\u0000\uC6B1\u0000\uC6B0\u0000\uC6B7" + // 11365 - 11369 + "\u0000\uC6B5\u0000\uC6AE\u0000\uC6BC\u0000\uC6AF\u0000\uC6AB" + // 11370 - 11374 + "\u0000\uC6BA\u0000\uC6B9\u0000\uC6B8\u0000\uC6AD\u0000\u0000" + // 11375 - 11379 + "\u0000\uC6B4\u0000\u0000\u0000\u0000\u0000\uC6B3\u0000\u0000" + // 11380 - 11384 + "\u0000\u0000\u0000\uC6BD\u0000\uC6B2\u0000\u0000\u0000\u0000" + // 11385 - 11389 + "\u0000\u0000\u0000\uF6D3\u0000\uF6D0\u0000\u0000\u8EA2\uE1E9" + // 11390 - 11394 + "\u8EA2\uE1ED\u8EA2\uE1F1\u0000\u0000\u0000\u0000\u0000\u0000" + // 11395 - 11399 + "\u8EA2\uE5DC\u8EA2\uE5D9\u8EA2\uE5E2\u0000\u0000\u8EA2\uE5E1" + // 11400 - 11404 + "\u8EA2\uE5DF\u8EA2\uE5DA\u0000\u0000\u0000\uF8BF\u0000\u0000" + // 11405 - 11409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE5DB" + // 11410 - 11414 + "\u0000\uF8BE\u0000\uF3F6\u8EA2\uE5DE\u0000\uF8BD\u8EA2\uE5E3" + // 11415 - 11419 + "\u8EA2\uE5DD\u8EA2\uE5D8\u8EA2\uE1EC\u0000\u0000\u0000\u0000" + // 11420 - 11424 + "\u0000\u0000\u0000\uF8BC\u0000\u0000\u8EA2\uE5D7\u8EA2\uE9A4" + // 11425 - 11429 + "\u0000\u0000\u8EA2\uE9A3\u8EA2\uE5E0\u0000\uF9F3\u0000\u0000" + // 11430 - 11434 + "\u8EA2\uE9A7\u8EA2\uE9A8\u8EA2\uE9A2\u8EA2\uE9A6\u0000\u0000" + // 11435 - 11439 + "\u8EA2\uE9A1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11440 - 11444 + "\u8EA2\uEBDF\u8EA2\uE9A5\u0000\uFAF2\u0000\uFAF0\u8EA2\uEBDD" + // 11445 - 11449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDDA\u0000\u0000" + // 11450 - 11454 + "\u0000\uCDD8\u0000\u0000\u0000\uCDF0\u0000\uCDE8\u0000\uCDE2" + // 11455 - 11459 + "\u0000\uCDD6\u8EA2\uA6F8\u0000\uCDE1\u0000\uCDD7\u0000\uCDE9" + // 11460 - 11464 + "\u0000\uCDF2\u8EA2\uAAD5\u0000\uCDE6\u8EA2\uA6F4\u0000\uCDDC" + // 11465 - 11469 + "\u0000\uCDDF\u0000\uCDE0\u0000\u0000\u0000\uCDEE\u0000\uCDEF" + // 11470 - 11474 + "\u0000\uCDED\u0000\uCDE7\u0000\uCDEB\u0000\uCDDD\u0000\uD1B8" + // 11475 - 11479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11480 - 11484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11485 - 11489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uAADB" + // 11490 - 11494 + "\u0000\uD1C4\u0000\uD1BC\u0000\uD1BE\u0000\uD1C3\u8EA2\uAAE7" + // 11495 - 11499 + "\u0000\uD1C1\u8EA2\uAFB3\u0000\uD5D3\u0000\uD1C6\u8EA2\uAAD8" + // 11500 - 11504 + "\u8EA2\uAAE0\u0000\uD1C2\u8EA2\uAADF\u8EA2\uAADC\u8EA2\uAAE4" + // 11505 - 11509 + "\u8EA2\uAAE6\u0000\uD1BB\u0000\uD1BF\u0000\uD1C5\u0000\uD5D5" + // 11510 - 11514 + "\u8EA2\uC0FE\u8EA2\uC0FD\u0000\uE2D2\u8EA2\uC1A3\u8EA2\uC0F7" + // 11515 - 11519 + "\u8EA2\uC1A2\u8EA2\uC0F5\u8EA2\uC1A1\u8EA2\uC0FA\u0000\uE2D4" + // 11520 - 11524 + "\u0000\u0000\u8EA2\uC8B0\u0000\u0000\u8EA2\uC8AB\u8EA2\uC8B1" + // 11525 - 11529 + "\u8EA2\uC8AC\u8EA2\uC8AA\u8EA2\uC8AD\u0000\uE7B9\u0000\u0000" + // 11530 - 11534 + "\u8EA2\uC8AF\u0000\u0000\u8EA2\uC8AE\u0000\uE7BA\u0000\uEBC9" + // 11535 - 11539 + "\u0000\uEBC8\u8EA2\uCED6\u8EA2\uCED9\u8EA2\uCED1\u8EA2\uCED5" + // 11540 - 11544 + "\u0000\uEBCA\u8EA2\uCED4\u8EA2\uCED2\u8EA2\uCED8\u8EA2\uCED3" + // 11545 - 11549 + "\u8EA2\uCED0\u8EA2\uCED7\u0000\u0000\u8EA2\uD5A3\u0000\uEFD2" + // 11550 - 11554 + "\u8EA2\uE3F4\u8EA2\uD4FE\u8EA2\uD5A1\u8EA2\uD4FD\u8EA2\uC0F6" + // 11555 - 11559 + "\u0000\uEFD1\u8EA2\uD5A4\u8EA2\uD4FC\u0000\uEFD0\u0000\uEFD3" + // 11560 - 11564 + "\u8EA2\uD5A2\u8EA2\uDBA3\u0000\uF2DD\u8EA2\uDBA4\u8EA2\uDBA5" + // 11565 - 11569 + "\u0000\u0000\u8EA2\uDFF0\u0000\uF5E8\u8EA2\uE3F5\u8EA2\uE3F6" + // 11570 - 11574 + "\u0000\u0000\u0000\u0000\u8EA2\uE3F7\u8EA2\uE7C9\u0000\uF5E7" + // 11575 - 11579 + "\u0000\uF5E5\u0000\uF5E6\u0000\uF7CE\u0000\uF5E4\u8EA2\uDFEE" + // 11580 - 11584 + "\u8EA2\uDBA1\u0000\uF7CD\u0000\uF7CF\u0000\u0000\u0000\uF9B1" + // 11585 - 11589 + "\u0000\uF9B2\u0000\u0000\u8EA2\uEAAE\u0000\u0000\u0000\uFCCA" + // 11590 - 11594 + "\u0000\uFCC9\u0000\uCBD7\u0000\u0000\u0000\u0000\u8EA2\uA2F5" + // 11595 - 11599 + "\u0000\uD8D4\u0000\u0000\u8EA2\uA2F2\u8EA2\uA2F3\u8EA2\uA2F1" + // 11600 - 11604 + "\u0000\u0000\u8EA2\uA2F4\u0000\u0000\u0000\u0000\u8EA2\uA5AA" + // 11605 - 11609 + "\u8EA2\uA5A7\u8EA2\uA5AC\u8EA2\uA5A8\u0000\uCBD8\u0000\uCBDB" + // 11610 - 11614 + "\u0000\u0000\u8EA2\uA5A9\u0000\uCBDA\u8EA2\uA5AB\u0000\u0000" + // 11615 - 11619 + "\u0000\u0000\u0000\uCBD9\u0000\u0000\u0000\u0000\u0000\u0000" + // 11620 - 11624 + "\u0000\u0000\u8EA2\uA8F5\u8EA2\uA8F7\u0000\uCFD7\u8EA2\uA8F3" + // 11625 - 11629 + "\u8EA2\uA8F6\u8EA2\uA8F4\u0000\uCFD5\u0000\uCFD8\u0000\u0000" + // 11630 - 11634 + "\u0000\uCFD6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11635 - 11639 + "\u8EA2\uADD9\u8EA2\uADDD\u8EA2\uADDA\u0000\uD8CD\u0000\uD8CA" + // 11640 - 11644 + "\u8EA2\uB3B0\u0000\uD8D0\u8EA2\uB3B3\u0000\uD8D2\u0000\uD8CB" + // 11645 - 11649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB9E6" + // 11650 - 11654 + "\u8EA2\uB9E9\u0000\uDDD6\u0000\u0000\u0000\uDDE0\u0000\uDDDC" + // 11655 - 11659 + "\u8EA2\uB9E7\u0000\u0000\u0000\u0000\u0000\uDDE4\u0000\uDDDD" + // 11660 - 11664 + "\u0000\uDDE2\u0000\uDDD8\u0000\u0000\u0000\uDDD5\u0000\uDDD7" + // 11665 - 11669 + "\u0000\uDDE3\u8EA2\uB9E8\u0000\uDDDB\u0000\uDDDE\u0000\uDDDA" + // 11670 - 11674 + "\u0000\uDDDF\u8EA2\uB9EA\u0000\uDDE1\u0000\uDDD9\u8EA2\uB9E5" + // 11675 - 11679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11680 - 11684 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC0F2\u0000\uE2CC" + // 11685 - 11689 + "\u8EA2\uC0F4\u0000\u0000\u0000\uE2CE\u0000\uE2D0\u0000\u0000" + // 11690 - 11694 + "\u8EA2\uC0F3\u0000\uE2CD\u0000\uE2D1\u0000\u0000\u0000\uE2CF" + // 11695 - 11699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7AF\u8EA2\uC8A9" + // 11700 - 11704 + "\u0000\uE7B7\u8EA2\uC8A6\u8EA2\uDFED\u0000\u0000\u0000\uF5DF" + // 11705 - 11709 + "\u8EA2\uDFEC\u0000\uF5DD\u0000\uF5E0\u8EA2\uE3F0\u8EA2\uE3F1" + // 11710 - 11714 + "\u8EA2\uE3F2\u0000\uF7CB\u0000\u0000\u8EA2\uE3F3\u0000\u0000" + // 11715 - 11719 + "\u0000\uF7CC\u0000\uF9AF\u8EA2\uE7C4\u8EA2\uE7C5\u8EA2\uE7C3" + // 11720 - 11724 + "\u8EA2\uE7C2\u8EA2\uE7C6\u0000\uF9AE\u8EA2\uEAAB\u8EA2\uEAA9" + // 11725 - 11729 + "\u8EA2\uEAAA\u8EA2\uEAAC\u8EA2\uEAA8\u8EA2\uEAAD\u8EA2\uECC7" + // 11730 - 11734 + "\u0000\u0000\u8EA2\uECC8\u8EA2\uECC6\u0000\uFBB2\u8EA2\uEEB3" + // 11735 - 11739 + "\u0000\uFBF9\u8EA2\uEEB4\u8EA2\uEFD6\u8EA2\uEFD5\u0000\u0000" + // 11740 - 11744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11745 - 11749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11750 - 11754 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11755 - 11759 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11760 - 11764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11765 - 11769 + "\u0000\u0000\u0000\uA2B2\u0000\u0000\u0000\u0000\u0000\u0000" + // 11770 - 11774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11775 - 11779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11780 - 11784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11785 - 11789 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11790 - 11794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11795 - 11799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2B3\u0000\u0000" + // 11800 - 11804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 11805 - 11809 + "\u0000\u0000\u0000\u0000\u0000\uE4B8\u0000\u0000\u0000\u0000" + // 11810 - 11814 + "\u8EA2\uC3A4\u8EA2\uC3B7\u8EA2\uC3BC\u8EA2\uC3B0\u0000\uE4B4" + // 11815 - 11819 + "\u8EA2\uC3C1\u0000\uE4B1\u8EA2\uC3A2\u0000\u0000\u8EA2\uC3A3" + // 11820 - 11824 + "\u0000\uE4AA\u0000\uE4B3\u8EA2\uC3AA\u0000\uE4B6\u0000\uE4B7" + // 11825 - 11829 + "\u8EA2\uC3AB\u0000\u0000\u8EA2\uC3B8\u8EA2\uC3BE\u0000\uE4B2" + // 11830 - 11834 + "\u0000\u0000\u0000\uE4AC\u8EA2\uC3A9\u8EA2\uC3A5\u8EA2\uC3C0" + // 11835 - 11839 + "\u8EA2\uC3AE\u8EA2\uC3AF\u8EA2\uC3A6\u8EA2\uC3BA\u8EA2\uC3B1" + // 11840 - 11844 + "\u8EA2\uC3BB\u8EA2\uC3B3\u0000\u0000\u0000\uE4AD\u8EA2\uCAC0" + // 11845 - 11849 + "\u0000\uE4B0\u0000\uE4AE\u8EA2\uC3BF\u8EA2\uC3B6\u8EA2\uC3B2" + // 11850 - 11854 + "\u0000\u0000\u0000\u0000\u8EA2\uC3A7\u0000\uE8FA\u8EA2\uC3B5" + // 11855 - 11859 + "\u0000\uE4B5\u8EA2\uC3AD\u0000\u0000\u8EA2\uC3AC\u0000\u0000" + // 11860 - 11864 + "\u0000\u0000\u0000\u0000\u0000\uE4AF\u0000\uE4AB\u8EA2\uCAD5" + // 11865 - 11869 + "\u8EA2\uC7FD\u8EA2\uC7FC\u8EA2\uC8A3\u0000\uE7A4\u0000\u0000" + // 11870 - 11874 + "\u8EA2\uC7FE\u8EA2\uC7FA\u8EA2\uC8A1\u8EA2\uC8A2\u0000\uE7A5" + // 11875 - 11879 + "\u0000\uE7A7\u8EA2\uC8A4\u0000\u0000\u8EA2\uCECF\u8EA2\uCECE" + // 11880 - 11884 + "\u0000\u0000\u8EA2\uCECC\u8EA2\uCECD\u0000\uEBBB\u0000\uEBBD" + // 11885 - 11889 + "\u0000\uEBBA\u0000\uEBBC\u8EA2\uD4F5\u8EA2\uD4F6\u8EA2\uD4F1" + // 11890 - 11894 + "\u0000\u0000\u8EA2\uD4F2\u0000\uEFC3\u0000\uEFC8\u0000\uEFC2" + // 11895 - 11899 + "\u0000\uEFC9\u0000\uEFC4\u8EA2\uD4F3\u0000\u0000\u0000\u0000" + // 11900 - 11904 + "\u8EA2\uD4F4\u8EA2\uD4F0\u0000\uEFCA\u0000\uEFC6\u0000\u0000" + // 11905 - 11909 + "\u0000\u0000\u0000\uEFC5\u0000\uEFC7\u0000\u0000\u8EA2\uD4EF" + // 11910 - 11914 + "\u0000\u0000\u8EA2\uDAF6\u0000\uF2D2\u0000\u0000\u0000\u0000" + // 11915 - 11919 + "\u8EA2\uDAF8\u0000\uF2D4\u8EA2\uDAFB\u8EA2\uDAF7\u8EA2\uDAF5" + // 11920 - 11924 + "\u8EA2\uDAFA\u0000\uF2D3\u8EA2\uDAF9\u0000\u0000\u0000\uF2D1" + // 11925 - 11929 + "\u0000\u0000\u0000\u0000\u0000\uF5DE\u0000\uF5E1\u0000\uF7CA" + // 11930 - 11934 + "\u0000\u0000\u8EA2\uE7C1\u0000\u0000\u0000\u0000\u0000\u0000" + // 11935 - 11939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uF1FA\u0000\uCBCF" + // 11940 - 11944 + "\u0000\uCFD1\u0000\uD3DC\u0000\uD3DB\u0000\u0000\u0000\uD8C8" + // 11945 - 11949 + "\u0000\u0000\u8EA2\uB3AC\u0000\uD8C6\u8EA2\uB3AD\u0000\uD8C7" + // 11950 - 11954 + "\u0000\u0000\u0000\u0000\u8EA2\uB9E2\u8EA2\uB9DE\u0000\u0000" + // 11955 - 11959 + "\u0000\u0000\u0000\uDDD3\u8EA2\uB9E1\u8EA2\uB9E0\u8EA2\uB9DF" + // 11960 - 11964 + "\u0000\uDDD4\u8EA2\uB9E3\u8EA2\uB9E4\u0000\u0000\u0000\u0000" + // 11965 - 11969 + "\u0000\u0000\u8EA2\uC0E8\u8EA2\uC0E6\u8EA2\uC0EA\u8EA2\uC0EB" + // 11970 - 11974 + "\u8EA2\uC0F1\u0000\u0000\u8EA2\uC0ED\u8EA2\uC0EF\u0000\u0000" + // 11975 - 11979 + "\u8EA2\uC0E7\u8EA2\uC0E2\u0000\u0000\u8EA2\uC0EE\u0000\u0000" + // 11980 - 11984 + "\u0000\u0000\u8EA2\uC0F0\u8EA2\uC0E9\u8EA2\uC0EC\u8EA2\uC0E3" + // 11985 - 11989 + "\u0000\uE2C9\u8EA2\uC0E5\u8EA2\uC0E4\u0000\uE2C8\u0000\uE2CA" + // 11990 - 11994 + "\u0000\u0000\u0000\uE7A6\u8EA2\uC7FB\u8EA2\uDAEC\u8EA2\uDAF1" + // 11995 - 11999 + "\u0000\uF2CE\u0000\u0000\u0000\uF2CB\u8EA2\uDAED\u0000\u0000" + // 12000 - 12004 + "\u8EA2\uDFEB\u0000\uF5DB\u0000\uF5D9\u0000\uF5DC\u0000\uF5DA" + // 12005 - 12009 + "\u8EA2\uDFEA\u8EA2\uDFE7\u8EA2\uDFE6\u0000\u0000\u8EA2\uDFE9" + // 12010 - 12014 + "\u0000\u0000\u0000\u0000\u8EA2\uDFE8\u8EA2\uE3EF\u0000\uF7C9" + // 12015 - 12019 + "\u8EA2\uE3E6\u8EA2\uE3E5\u0000\u0000\u0000\uF7C4\u8EA2\uE3EB" + // 12020 - 12024 + "\u8EA2\uE3EA\u8EA2\uE3E2\u8EA2\uE3ED\u8EA2\uE3E7\u0000\uF7C8" + // 12025 - 12029 + "\u8EA2\uE3E4\u8EA2\uE3EC\u8EA2\uE3E3\u0000\uF7C5\u0000\uF7C7" + // 12030 - 12034 + "\u8EA2\uE3E8\u0000\uF7C6\u8EA2\uE3E9\u0000\u0000\u8EA2\uE3EE" + // 12035 - 12039 + "\u8EA2\uE7BE\u0000\u0000\u0000\uF9AB\u8EA2\uE7BB\u0000\u0000" + // 12040 - 12044 + "\u8EA2\uE7BF\u0000\u0000\u0000\u0000\u0000\uF9A8\u8EA2\uE7BD" + // 12045 - 12049 + "\u0000\uF9AD\u0000\u0000\u0000\uF9AA\u0000\u0000\u8EA2\uE7BC" + // 12050 - 12054 + "\u0000\u0000\u0000\uF9AC\u8EA2\uE7C0\u0000\uF9A7\u0000\u0000" + // 12055 - 12059 + "\u0000\u0000\u0000\u0000\u0000\uE4CD\u8EA2\uC5AC\u0000\u0000" + // 12060 - 12064 + "\u0000\u0000\u0000\uE4D4\u0000\u0000\u0000\u0000\u0000\u0000" + // 12065 - 12069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCAF0\u8EA2\uCAEE" + // 12070 - 12074 + "\u0000\u0000\u8EA2\uCBA6\u8EA2\uCAEC\u0000\u0000\u8EA2\uCAFE" + // 12075 - 12079 + "\u0000\uE4C5\u0000\uE9A8\u8EA2\uCAE8\u8EA2\uCBA5\u0000\u0000" + // 12080 - 12084 + "\u8EA2\uCAED\u8EA2\uCAFB\u0000\uE9AD\u0000\uE9A6\u0000\u0000" + // 12085 - 12089 + "\u8EA2\uCBA3\u0000\u0000\u8EA2\uCAE6\u8EA2\uCBA2\u0000\u0000" + // 12090 - 12094 + "\u0000\uE9B8\u8EA2\uCAF7\u8EA2\uCBA9\u0000\uE9B6\u8EA2\uCAEF" + // 12095 - 12099 + "\u0000\uE9A7\u8EA2\uCAF1\u8EA2\uCBA1\u0000\uE9B0\u8EA2\uCAEA" + // 12100 - 12104 + "\u0000\uE9B4\u0000\uE9AA\u8EA2\uCAF3\u8EA2\uCAE9\u8EA2\uCAE3" + // 12105 - 12109 + "\u0000\u0000\u8EA2\uCAFD\u0000\uE9B1\u8EA2\uCBA8\u8EA2\uCAE4" + // 12110 - 12114 + "\u0000\uE9AE\u0000\u0000\u8EA2\uCAF9\u0000\u0000\u0000\uE9AF" + // 12115 - 12119 + "\u8EA2\uCAEB\u8EA2\uCAF6\u8EA2\uCAF2\u8EA2\uCAF4\u8EA2\uCECA" + // 12120 - 12124 + "\u0000\u0000\u8EA2\uCEC0\u8EA2\uCEC5\u8EA2\uCECB\u8EA2\uCEC8" + // 12125 - 12129 + "\u8EA2\uCEC7\u8EA2\uCEC6\u0000\u0000\u8EA2\uCEBF\u8EA2\uCEC4" + // 12130 - 12134 + "\u0000\u0000\u0000\u0000\u8EA2\uCEC2\u0000\u0000\u0000\uEFBC" + // 12135 - 12139 + "\u0000\uEFB9\u8EA2\uD4E4\u8EA2\uD4E9\u8EA2\uD4EB\u8EA2\uD4E8" + // 12140 - 12144 + "\u8EA2\uD4E1\u8EA2\uD4E3\u8EA2\uD4ED\u8EA2\uD4EA\u8EA2\uD4E5" + // 12145 - 12149 + "\u8EA2\uD4EE\u8EA2\uD4E2\u8EA2\uD4EC\u0000\uEFBA\u0000\uEFC0" + // 12150 - 12154 + "\u0000\uEFBE\u8EA2\uD4DC\u0000\uEFBF\u0000\uEFBB\u8EA2\uD4DD" + // 12155 - 12159 + "\u8EA2\uD4DF\u8EA2\uD4DE\u8EA2\uD4E6\u8EA2\uD4E7\u0000\u0000" + // 12160 - 12164 + "\u0000\uEFBD\u0000\u0000\u0000\uEFB8\u0000\u0000\u0000\u0000" + // 12165 - 12169 + "\u8EA2\uD4E0\u0000\u0000\u8EA2\uDAF2\u0000\uF2CC\u0000\u0000" + // 12170 - 12174 + "\u8EA2\uDAE9\u0000\uF2CD\u0000\uF2D0\u8EA2\uDAEE\u0000\u0000" + // 12175 - 12179 + "\u8EA2\uDAEB\u0000\uF2CF\u0000\u0000\u0000\u0000\u8EA2\uDAEF" + // 12180 - 12184 + "\u8EA2\uDAF0\u8EA2\uDAEA\u8EA2\uDAF3\u8EA2\uC0D4\u0000\uE2BF" + // 12185 - 12189 + "\u0000\u0000\u0000\u0000\u8EA2\uC0D3\u0000\uE2BE\u0000\u0000" + // 12190 - 12194 + "\u0000\u0000\u0000\u0000\u8EA2\uC0D5\u0000\uE2BD\u0000\u0000" + // 12195 - 12199 + "\u8EA2\uC7E5\u8EA2\uC7E8\u8EA2\uC7E6\u8EA2\uC7E7\u8EA2\uC7EB" + // 12200 - 12204 + "\u8EA2\uC7E4\u8EA2\uC7EC\u8EA2\uC7E9\u8EA2\uC7EA\u0000\uEBB8" + // 12205 - 12209 + "\u8EA2\uCEBE\u0000\u0000\u0000\u0000\u0000\uEBB7\u0000\u0000" + // 12210 - 12214 + "\u8EA2\uD4DB\u8EA2\uD4D9\u0000\u0000\u0000\u0000\u0000\uEFB6" + // 12215 - 12219 + "\u8EA2\uD4D8\u8EA2\uD4DA\u0000\u0000\u0000\uEFB7\u0000\u0000" + // 12220 - 12224 + "\u8EA2\uDAE7\u0000\u0000\u8EA2\uDAE8\u0000\uF5D8\u0000\u0000" + // 12225 - 12229 + "\u8EA2\uE7B8\u8EA2\uE7BA\u8EA2\uE7B7\u8EA2\uE7B9\u8EA2\uEAA4" + // 12230 - 12234 + "\u8EA2\uECC3\u0000\u0000\u0000\u0000\u8EA2\uF1DE\u0000\uCBCD" + // 12235 - 12239 + "\u0000\uD3DA\u8EA2\uB3A9\u8EA2\uB3AB\u8EA2\uB3AA\u0000\u0000" + // 12240 - 12244 + "\u8EA2\uB9DB\u0000\uDDD2\u0000\u0000\u8EA2\uB9D9\u0000\u0000" + // 12245 - 12249 + "\u0000\uDDD1\u8EA2\uB9DC\u8EA2\uE3E1\u0000\u0000\u8EA2\uE3E0" + // 12250 - 12254 + "\u0000\u0000\u8EA2\uE3DF\u0000\uF7C3\u8EA2\uE7B4\u8EA2\uE7B5" + // 12255 - 12259 + "\u0000\uF9A5\u8EA2\uE7B6\u0000\uF9A6\u0000\u0000\u0000\u0000" + // 12260 - 12264 + "\u0000\uFAC5\u0000\u0000\u0000\uFAC4\u8EA2\uECC1\u0000\u0000" + // 12265 - 12269 + "\u0000\u0000\u0000\uFBAE\u8EA2\uECC2\u8EA2\uEEAB\u0000\uFBF5" + // 12270 - 12274 + "\u0000\uFBF6\u0000\u0000\u8EA2\uEFD3\u0000\u0000\u0000\uFCED" + // 12275 - 12279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12280 - 12284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12285 - 12289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12290 - 12294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12295 - 12299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12300 - 12304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12305 - 12309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12310 - 12314 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12315 - 12319 + "\u0000\uD8EC\u0000\u0000\u0000\u0000\u8EA2\uC8EF\u0000\u0000" + // 12320 - 12324 + "\u8EA2\uC8EE\u0000\u0000\u0000\uEBF3\u0000\uEBF4\u8EA2\uCFC2" + // 12325 - 12329 + "\u0000\u0000\u8EA2\uD5FB\u8EA2\uDBF8\u0000\u0000\u0000\u0000" + // 12330 - 12334 + "\u0000\uEFFE\u0000\uF3B0\u8EA2\uDBF6\u0000\u0000\u8EA2\uDBF9" + // 12335 - 12339 + "\u0000\uF3B1\u0000\u0000\u8EA2\uE0F0\u8EA2\uDBF7\u0000\uE2B7" + // 12340 - 12344 + "\u0000\uE2B5\u0000\uE6F6\u0000\uE6F5\u0000\uE6F3\u0000\uE6F7" + // 12345 - 12349 + "\u0000\u0000\u0000\uE6F1\u0000\uE6F2\u0000\u0000\u0000\uE6F0" + // 12350 - 12354 + "\u0000\u0000\u8EA2\uC7E1\u0000\u0000\u0000\u0000\u8EA2\uCEBC" + // 12355 - 12359 + "\u0000\u0000\u0000\uEBB4\u0000\uEBB5\u0000\uEBB3\u0000\u0000" + // 12360 - 12364 + "\u8EA2\uCEBB\u0000\u0000\u8EA2\uCEBD\u0000\u0000\u8EA2\uD4D3" + // 12365 - 12369 + "\u8EA2\uD4D5\u0000\u0000\u0000\uEFB2\u8EA2\uD4D6\u0000\uEFAB" + // 12370 - 12374 + "\u8EA2\uD4D2\u0000\uEFAA\u0000\uEFB4\u0000\uEFB0\u0000\uEFB1" + // 12375 - 12379 + "\u0000\uEFAD\u8EA2\uD4D1\u0000\uEFAC\u8EA2\uD4D7\u8EA2\uD4D4" + // 12380 - 12384 + "\u0000\u0000\u0000\uEFB3\u0000\u0000\u0000\uEFAE\u0000\uEFAF" + // 12385 - 12389 + "\u8EA2\uDAE1\u0000\u0000\u8EA2\uDAE3\u8EA2\uDAE2\u0000\u0000" + // 12390 - 12394 + "\u8EA2\uDAE4\u0000\uF2CA\u8EA2\uDAE0\u0000\u0000\u0000\u0000" + // 12395 - 12399 + "\u0000\uF5D6\u8EA2\uDFE4\u0000\uF5D3\u0000\uF5D7\u0000\uF5D5" + // 12400 - 12404 + "\u0000\uF5D4\u8EA2\uE3DE\u0000\u0000\u0000\u0080\u0000\u0081" + // 12405 - 12409 + "\u0000\u0082\u0000\u0083\u0000\u0084\u0000\u0085\u0000\u0086" + // 12410 - 12414 + "\u0000\u0087\u0000\u0088\u0000\u0089\u0000\u008A\u0000\u008B" + // 12415 - 12419 + "\u0000\u008C\u0000\u008D\u0000\u0000\u0000\u0000\u0000\u0090" + // 12420 - 12424 + "\u0000\u0091\u0000\u0092\u0000\u0093\u0000\u0094\u0000\u0095" + // 12425 - 12429 + "\u0000\u0096\u0000\u0097\u0000\u0098\u0000\u0099\u0000\u009A" + // 12430 - 12434 + "\u0000\u009B\u0000\u009C\u0000\u009D\u0000\u009E\u0000\u009F" + // 12435 - 12439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12440 - 12444 + "\u0000\u0000\u0000\u0000\u0000\uA1F0\u8EAD\uA1A3\u0000\u0000" + // 12445 - 12449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12450 - 12454 + "\u0000\u0000\u0000\uA2F8\u0000\uA2B4\u0000\u0000\u0000\u0000" + // 12455 - 12459 + "\u8EAD\uA1B2\u0000\u0000\u8EAD\uA1D1\u0000\uA1B1\u0000\u0000" + // 12460 - 12464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12465 - 12469 + "\u0000\u0000\u0000\u0000\u8EA2\uAAAC\u8EA2\uAAA5\u8EA2\uAAAB" + // 12470 - 12474 + "\u8EA2\uAAA4\u8EA2\uAAA6\u8EA2\uA9FE\u8EA2\uA9FC\u0000\u0000" + // 12475 - 12479 + "\u8EA2\uAAAD\u8EA2\uAAAA\u8EA2\uA9FA\u0000\u0000\u0000\uD0F2" + // 12480 - 12484 + "\u8EA2\uAAA8\u8EA2\uAAA9\u0000\u0000\u8EA2\uAAA7\u8EA2\uA9FD" + // 12485 - 12489 + "\u8EA2\uA9FB\u0000\uD0F1\u8EA2\uAAA3\u8EA2\uAAA1\u0000\u0000" + // 12490 - 12494 + "\u0000\u0000\u8EA2\uAAA2\u8EA2\uAAAE\u0000\u0000\u0000\u0000" + // 12495 - 12499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12500 - 12504 + "\u0000\u0000\u0000\uD5B3\u0000\u0000\u0000\uD5B2\u0000\u0000" + // 12505 - 12509 + "\u8EA2\uAEEE\u0000\uD5AF\u8EA2\uAEF0\u0000\u0000\u0000\uD5B4" + // 12510 - 12514 + "\u8EA2\uAEF1\u0000\u0000\u0000\u0000\u0000\uD5B7\u0000\u0000" + // 12515 - 12519 + "\u0000\uD5B5\u8EA2\uAEF2\u8EA2\uAAAF\u8EA2\uAEF4\u0000\u0000" + // 12520 - 12524 + "\u0000\uD5B1\u0000\u0000\u0000\uD5B0\u0000\u0000\u8EA2\uAEEF" + // 12525 - 12529 + "\u8EA2\uC0CD\u8EA2\uC0CC\u0000\uE2B0\u0000\u0000\u8EA2\uC7DF" + // 12530 - 12534 + "\u8EA2\uC7E0\u8EA2\uC7DE\u0000\u0000\u0000\u0000\u0000\uE6EF" + // 12535 - 12539 + "\u0000\uE6EE\u0000\u0000\u0000\uEBB2\u0000\uEBB1\u0000\u0000" + // 12540 - 12544 + "\u8EA2\uD4D0\u8EA2\uDADF\u8EA2\uDADD\u8EA2\uDADE\u0000\uF2C9" + // 12545 - 12549 + "\u8EA2\uDFE3\u8EA2\uDFE2\u0000\u0000\u8EA2\uE3DD\u8EA2\uE3DC" + // 12550 - 12554 + "\u8EA2\uE3DB\u8EA2\uE7B3\u0000\u0000\u8EA2\uF1F8\u0000\uCBCA" + // 12555 - 12559 + "\u0000\uD3D6\u0000\u0000\u0000\uD3D7\u0000\uD8C2\u0000\uD8C3" + // 12560 - 12564 + "\u8EA2\uB3A6\u8EA2\uB3A5\u8EA2\uB9D5\u0000\u0000\u0000\uDDCE" + // 12565 - 12569 + "\u0000\uDDCC\u0000\uDDC9\u0000\uDDCD\u0000\uDDCB\u0000\uDDCA" + // 12570 - 12574 + "\u0000\u0000\u0000\u0000\u0000\uE2B1\u8EA2\uC0D0\u0000\u0000" + // 12575 - 12579 + "\u0000\uE6F4\u0000\uE2B3\u0000\uE2B8\u8EA2\uC0D2\u0000\uE2BA" + // 12580 - 12584 + "\u0000\uE2B9\u0000\uE2BC\u8EA2\uC0D1\u8EA2\uC0CE\u0000\uE2B6" + // 12585 - 12589 + "\u0000\uE2B2\u0000\uE2B4\u8EA2\uC0CF\u0000\uE2BB\u0000\uFBF4" + // 12590 - 12594 + "\u0000\u0000\u8EA2\uEEA7\u0000\u0000\u8EA2\uEEA6\u8EA2\uEEA9" + // 12595 - 12599 + "\u8EA2\uEEA8\u0000\u0000\u8EA2\uEFD1\u0000\u0000\u0000\uFCC8" + // 12600 - 12604 + "\u8EA2\uEFD0\u8EA2\uEFCE\u0000\u0000\u8EA2\uEFCF\u0000\u0000" + // 12605 - 12609 + "\u0000\u0000\u0000\u0000\u0000\uFCEA\u0000\uFCE9\u8EA2\uF0CF" + // 12610 - 12614 + "\u8EA2\uF0D0\u0000\uFCEB\u0000\u0000\u8EA2\uF1BC\u8EA2\uF1BD" + // 12615 - 12619 + "\u0000\uFDB4\u0000\u0000\u0000\uFDBA\u0000\u0000\u8EA2\uF1F7" + // 12620 - 12624 + "\u8EA2\uF2B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12625 - 12629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12630 - 12634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12635 - 12639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12640 - 12644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12645 - 12649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12650 - 12654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA4AB\u0000\uA4AC" + // 12655 - 12659 + "\u0000\uA4AD\u0000\uA4AE\u0000\uA4AF\u0000\uA4B0\u0000\uA4B1" + // 12660 - 12664 + "\u0000\uA4B2\u0000\uA4B3\u0000\uA4B4\u0000\u0000\u0000\u0000" + // 12665 - 12669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA6B5" + // 12670 - 12674 + "\u0000\uA6B6\u0000\uA6B7\u0000\uA6B8\u0000\uA6B9\u0000\uA6BA" + // 12675 - 12679 + "\u0000\uA6BB\u0000\uA6BC\u0000\uA6BD\u0000\uA6BE\u0000\u0000" + // 12680 - 12684 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12685 - 12689 + "\u8EA2\uEAFB\u8EA2\uEBA3\u8EA2\uEBA1\u8EA2\uEAF9\u8EA2\uEBA6" + // 12690 - 12694 + "\u0000\u0000\u8EA2\uEAF8\u0000\u0000\u0000\uFADA\u8EA2\uEBA2" + // 12695 - 12699 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEAFC\u0000\uFAD9" + // 12700 - 12704 + "\u8EA2\uEBA5\u0000\u0000\u0000\u0000\u8EA2\uEBA4\u0000\u0000" + // 12705 - 12709 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uECF7\u8EA2\uECF6" + // 12710 - 12714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uECFD\u0000\u0000" + // 12715 - 12719 + "\u8EA2\uECF9\u8EA2\uECFC\u0000\uFBCC\u0000\u0000\u0000\u0000" + // 12720 - 12724 + "\u8EA2\uECFA\u8EA2\uECFB\u0000\u0000\u8EA2\uECF5\u8EA2\uECF8" + // 12725 - 12729 + "\u0000\uFBCB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFCAB" + // 12730 - 12734 + "\u8EA2\uEEE1\u8EA2\uEEDC\u8EA2\uEEE0\u0000\u0000\u8EA2\uEEE3" + // 12735 - 12739 + "\u8EA2\uEEDF\u0000\u0000\u8EA2\uEEDB\u0000\u0000\u0000\uFCAD" + // 12740 - 12744 + "\u8EA2\uEEDE\u8EA2\uEEE2\u0000\uFCAC\u8EA2\uEED9\u8EA2\uE7A6" + // 12745 - 12749 + "\u0000\uF8FA\u8EA2\uE7AD\u0000\u0000\u0000\u0000\u8EA2\uECBE" + // 12750 - 12754 + "\u0000\uF9A3\u8EA2\uE3CB\u8EA2\uE7A4\u0000\uF8FD\u8EA2\uE7A5" + // 12755 - 12759 + "\u8EA2\uE7AA\u0000\u0000\u0000\u0000\u0000\uF9A1\u0000\uF9A2" + // 12760 - 12764 + "\u8EA2\uE7A3\u8EA2\uE7AC\u8EA2\uE7AE\u8EA2\uE7A7\u8EA2\uE7A9" + // 12765 - 12769 + "\u8EA2\uE7AB\u8EA2\uE7A8\u8EA2\uE7AF\u0000\uF8FC\u0000\uF9A4" + // 12770 - 12774 + "\u0000\uF8FE\u0000\u0000\u0000\uF8FB\u8EA2\uE9FC\u0000\u0000" + // 12775 - 12779 + "\u0000\uFAC2\u8EA2\uE9FA\u0000\u0000\u0000\u0000\u8EA2\uE9FE" + // 12780 - 12784 + "\u0000\u0000\u8EA2\uEAA1\u0000\uFAC0\u8EA2\uEAA2\u8EA2\uE9FD" + // 12785 - 12789 + "\u0000\u0000\u8EA2\uE9FB\u0000\uFAC3\u0000\uFABF\u8EA2\uEAA3" + // 12790 - 12794 + "\u0000\u0000\u0000\uFAC1\u0000\uFABE\u0000\u0000\u0000\u0000" + // 12795 - 12799 + "\u0000\u0000\u0000\uFBAB\u0000\u0000\u0000\u0000\u0000\uFBAC" + // 12800 - 12804 + "\u8EA2\uECBD\u8EA2\uECBC\u8EA2\uECBF\u8EA2\uECC0\u0000\u0000" + // 12805 - 12809 + "\u0000\uFBAD\u8EA2\uEEA5\u8EA2\uEEAA\u0000\uF2BB\u0000\uF2C0" + // 12810 - 12814 + "\u0000\uF2C1\u0000\u0000\u0000\uF5CE\u8EA2\uDFD0\u8EA2\uDFD9" + // 12815 - 12819 + "\u8EA2\uDFD6\u8EA2\uDFD8\u0000\u0000\u0000\uF5CB\u8EA2\uDFD1" + // 12820 - 12824 + "\u0000\u0000\u8EA2\uDFD7\u0000\uF5C7\u8EA2\uDFD3\u0000\uF5CF" + // 12825 - 12829 + "\u8EA2\uDFCF\u8EA2\uDFD4\u8EA2\uDFDB\u8EA2\uDAD2\u8EA2\uDFD5" + // 12830 - 12834 + "\u8EA2\uDFCE\u0000\uF5C8\u8EA2\uDFCD\u0000\uF5C9\u8EA2\uDFDC" + // 12835 - 12839 + "\u0000\uF5CA\u8EA2\uDFDA\u0000\uF5CD\u8EA2\uDFCC\u0000\u0000" + // 12840 - 12844 + "\u0000\uF5CC\u0000\u0000\u8EA2\uDFD2\u8EA2\uE3C7\u8EA2\uE3D5" + // 12845 - 12849 + "\u8EA2\uE3D0\u8EA2\uE3D2\u8EA2\uE3C6\u0000\uF7BE\u0000\u0000" + // 12850 - 12854 + "\u8EA2\uE3C5\u0000\uF7C1\u0000\uF7C0\u0000\u0000\u8EA2\uE3D4" + // 12855 - 12859 + "\u8EA2\uE3CC\u8EA2\uE3C9\u8EA2\uE3CF\u0000\u0000\u8EA2\uE3C8" + // 12860 - 12864 + "\u0000\u0000\u8EA2\uE3CA\u8EA2\uE3D3\u8EA2\uE3D1\u0000\u0000" + // 12865 - 12869 + "\u0000\uF7BF\u8EA2\uE3D8\u8EA2\uE3D6\u8EA2\uE3CD\u8EA2\uE3D7" + // 12870 - 12874 + "\u8EA2\uE3CE\u0000\u0000\u0000\uA4DB\u0000\uA4DC\u0000\uA4DD" + // 12875 - 12879 + "\u0000\uA4DE\u0000\uA4DF\u0000\uA4E0\u0000\uA4E1\u0000\uA4E2" + // 12880 - 12884 + "\u0000\uA4E3\u0000\uA4E4\u0000\uA4E5\u0000\uA4E6\u0000\uA4E7" + // 12885 - 12889 + "\u0000\uA4E8\u0000\uA4E9\u0000\uA4EA\u0000\uA4EB\u0000\uA4EC" + // 12890 - 12894 + "\u0000\uA4ED\u0000\uA4EE\u0000\uA4EF\u0000\uA4F0\u0000\uA4F1" + // 12895 - 12899 + "\u0000\uA4F2\u0000\uA4F3\u0000\uA4F4\u0000\uA1C2\u0000\uA2DE" + // 12900 - 12904 + "\u0000\uA1C3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12905 - 12909 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12910 - 12914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12915 - 12919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12920 - 12924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12925 - 12929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12930 - 12934 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 12935 - 12939 + "\u0000\u0000\u0000\u0000\u0000\uCBCB\u0000\u0000\u0000\uDDD0" + // 12940 - 12944 + "\u0000\uDDCF\u8EA2\uC7E2\u8EA2\uC7E3\u0000\u0000\u0000\uEBB6" + // 12945 - 12949 + "\u8EA2\uDAE5\u0000\uEFB5\u8EA2\uDAE6\u8EA2\uDFE5\u0000\uCBCC" + // 12950 - 12954 + "\u0000\u0000\u8EA2\uADD3\u0000\uD3D9\u0000\uD3D8\u0000\u0000" + // 12955 - 12959 + "\u8EA2\uB3A7\u0000\uD8C4\u8EA2\uB3A8\u8EA2\uB9D8\u0000\u0000" + // 12960 - 12964 + "\u8EA2\uB9D7\u0000\u0000\u8EA2\uB9D6\u0000\u0000\u0000\u0000" + // 12965 - 12969 + "\u0000\uC6CF\u8EA2\uA2BD\u0000\u0000\u0000\uCAAD\u8EA2\uA3F2" + // 12970 - 12974 + "\u0000\u0000\u8EA2\uAABC\u0000\uD0FB\u0000\u0000\u0000\uE8E2" + // 12975 - 12979 + "\u0000\uC4DD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8A2" + // 12980 - 12984 + "\u0000\u0000\u0000\u0000\u0000\uE3EF\u0000\uC4DE\u0000\uC5BE" + // 12985 - 12989 + "\u0000\uC5BF\u0000\u0000\u0000\uC6D1\u0000\uC6D0\u0000\u0000" + // 12990 - 12994 + "\u8EA2\uA2BE\u0000\uC8A3\u0000\u0000\u8EA2\uA3F3\u0000\u0000" + // 12995 - 12999 + "\u0000\uCAAE\u0000\u0000\u0000\u0000\u8EA2\uA6D2\u8EA2\uA6D3" + // 13000 - 13004 + "\u8EA2\uA6D4\u0000\u0000\u0000\uCDB8\u0000\uCDB9\u8EA2\uA6D1" + // 13005 - 13009 + "\u0000\uCDBA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0FC" + // 13010 - 13014 + "\u8EA2\uAABD\u0000\u0000\u8EA2\uAEFC\u0000\uD5BE\u0000\u0000" + // 13015 - 13019 + "\u8EA2\uAEFB\u0000\u0000\u0000\uDAAF\u8EA2\uB5B0\u0000\uDAB0" + // 13020 - 13024 + "\u8EA2\uB5B1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFA4" + // 13025 - 13029 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC6CB\u0000\uC6CC" + // 13030 - 13034 + "\u8EA2\uA1D9\u0000\u0000\u0000\uC7FD\u0000\u0000\u0000\u0000" + // 13035 - 13039 + "\u0000\u0000\u8EA2\uA3EB\u0000\u0000\u0000\uCAA8\u0000\u0000" + // 13040 - 13044 + "\u8EA2\uA3EC\u0000\u0000\u0000\u0000\u0000\uCDB0\u0000\u0000" + // 13045 - 13049 + "\u0000\u0000\u8EA2\uA6CF\u0000\uCDAE\u0000\uCDAD\u8EA2\uA6CE" + // 13050 - 13054 + "\u0000\uCDAB\u8EA2\uA6D0\u0000\uCDAC\u0000\uCDAF\u0000\u0000" + // 13055 - 13059 + "\u0000\uD0F4\u0000\u0000\u0000\uD0F6\u8EA2\uAAB4\u8EA2\uAAB1" + // 13060 - 13064 + "\u8EA2\uAAB2\u8EA2\uAAB3\u8EA2\uAAB5\u0000\uD0F5\u0000\u0000" + // 13065 - 13069 + "\u0000\u0000\u8EA2\uAEF6\u8EA2\uAEF5\u0000\u0000\u0000\uD5BA" + // 13070 - 13074 + "\u0000\u0000\u0000\uD5B9\u0000\u0000\u0000\u0000\u0000\u0000" + // 13075 - 13079 + "\u0000\u0000\u0000\u0000\u0000\uDAA8\u8EA2\uB5AA\u0000\u0000" + // 13080 - 13084 + "\u0000\uDAA7\u0000\uDAA9\u0000\uDAA6\u0000\u0000\u0000\u0000" + // 13085 - 13089 + "\u0000\u0000\u0000\u0000\u0000\uDEFA\u8EA2\uB5A9\u0000\u0000" + // 13090 - 13094 + "\u0000\u0040\u0000\u0041\u0000\u0042\u0000\u0043\u0000\u0044" + // 13095 - 13099 + "\u0000\u0045\u0000\u0046\u0000\u0047\u0000\u0048\u0000\u0049" + // 13100 - 13104 + "\u0000\u004A\u0000\u004B\u0000\u004C\u0000\u004D\u0000\u004E" + // 13105 - 13109 + "\u0000\u004F\u0000\u0050\u0000\u0051\u0000\u0052\u0000\u0053" + // 13110 - 13114 + "\u0000\u0054\u0000\u0055\u0000\u0056\u0000\u0057\u0000\u0058" + // 13115 - 13119 + "\u0000\u0059\u0000\u005A\u0000\u005B\u0000\\\u0000\u005D" + // 13120 - 13124 + "\u0000\u005E\u0000\u005F\u0000\u0060\u0000\u0061\u0000\u0062" + // 13125 - 13129 + "\u0000\u0063\u0000\u0064\u0000\u0065\u0000\u0066\u0000\u0067" + // 13130 - 13134 + "\u0000\u0068\u0000\u0069\u0000\u006A\u0000\u006B\u0000\u006C" + // 13135 - 13139 + "\u0000\u006D\u0000\u006E\u0000\u006F\u0000\u0070\u0000\u0071" + // 13140 - 13144 + "\u0000\u0072\u0000\u0073\u0000\u0074\u0000\u0075\u0000\u0076" + // 13145 - 13149 + "\u0000\u0077\u0000\u0078\u0000\u0079\u0000\u007A\u0000\u007B" + // 13150 - 13154 + "\u0000\u007C\u0000\u007D\u0000\u007E\u0000\u007F\u8EA2\uD4C7" + // 13155 - 13159 + "\u0000\u0000\u0000\uEEFD\u8EA2\uD4CA\u0000\uEEF7\u8EA2\uD4C8" + // 13160 - 13164 + "\u8EA2\uD4BE\u0000\uEEF6\u8EA2\uDAD6\u0000\uEEFC\u0000\u0000" + // 13165 - 13169 + "\u0000\uEEF9\u0000\u0000\u0000\uEFA3\u0000\u0000\u8EA2\uD4BD" + // 13170 - 13174 + "\u0000\u0000\u8EA2\uD4C1\u0000\uEEF5\u8EA2\uD4C0\u8EA2\uD4C2" + // 13175 - 13179 + "\u8EA2\uD4C3\u0000\uEFA2\u8EA2\uD4C5\u8EA2\uD4C9\u8EA2\uD4CD" + // 13180 - 13184 + "\u0000\u0000\u0000\uEFA6\u0000\uF2BC\u8EA2\uDAD1\u8EA2\uDAD7" + // 13185 - 13189 + "\u8EA2\uDAD4\u8EA2\uDACC\u8EA2\uDAD8\u8EA2\uDACD\u0000\u0000" + // 13190 - 13194 + "\u8EA2\uDAD3\u0000\u0000\u0000\uF2B7\u0000\uF2BD\u8EA2\uDAD9" + // 13195 - 13199 + "\u0000\u0000\u0000\u0000\u0000\uF2B9\u0000\u0000\u0000\uF2C3" + // 13200 - 13204 + "\u0000\uF2BE\u8EA2\uDADB\u8EA2\uDAD5\u0000\uF2BA\u8EA2\uDACE" + // 13205 - 13209 + "\u0000\uF2C4\u8EA2\uDACF\u8EA2\uDAD0\u0000\uF2C5\u0000\uF2C2" + // 13210 - 13214 + "\u0000\uEEFA\u0000\u0000\u0000\uF2B8\u8EA2\uDADC\u0000\uF2C6" + // 13215 - 13219 + "\u0000\u0000\u0000\uF2BF\u8EA2\uDADA\u8EA2\uC0C0\u0000\uE2A6" + // 13220 - 13224 + "\u0000\u0000\u0000\u0000\u8EA2\uC0C3\u8EA2\uC0C4\u0000\uE2AA" + // 13225 - 13229 + "\u0000\u0000\u8EA2\uC0C6\u0000\u0000\u8EA2\uC0C8\u0000\u0000" + // 13230 - 13234 + "\u8EA2\uC0C9\u8EA2\uC0BD\u8EA2\uC0BC\u8EA2\uC0CA\u0000\uE2A9" + // 13235 - 13239 + "\u8EA2\uC0C7\u8EA2\uC0C5\u0000\u0000\u0000\uE2A7\u0000\uE2A3" + // 13240 - 13244 + "\u0000\uE2AE\u8EA2\uC0C1\u8EA2\uC0C2\u8EA2\uC0BF\u0000\u0000" + // 13245 - 13249 + "\u0000\uE2A8\u0000\u0000\u0000\u0000\u0000\uE2A4\u0000\u0000" + // 13250 - 13254 + "\u0000\uE2A2\u8EA2\uC7CE\u0000\uE6E6\u0000\uE6E1\u0000\u0000" + // 13255 - 13259 + "\u0000\u0000\u0000\uE6DC\u0000\u0000\u0000\uE6EC\u0000\uE6DD" + // 13260 - 13264 + "\u0000\u0000\u0000\uE6D9\u0000\uE6E8\u0000\uE6E5\u0000\uE6E7" + // 13265 - 13269 + "\u0000\u0000\u0000\uE6DE\u0000\uE6E3\u0000\uE6DA\u0000\uE6DB" + // 13270 - 13274 + "\u8EA2\uC7D6\u8EA2\uC7D3\u8EA2\uC7CB\u8EA2\uC7D0\u0000\u0000" + // 13275 - 13279 + "\u0000\uE6E9\u8EA2\uC7D7\u0000\uE6EA\u0000\uE6E0\u0000\u0000" + // 13280 - 13284 + "\u0000\u0000\u8EA2\uC7CD\u0000\uCBC6\u0000\u0000\u0000\uD3D4" + // 13285 - 13289 + "\u0000\uD3D5\u8EA2\uADD1\u0000\u0000\u0000\u0000\u8EA2\uADD2" + // 13290 - 13294 + "\u0000\uD3D3\u0000\u0000\u0000\uD8B9\u0000\u0000\u0000\uD8B7" + // 13295 - 13299 + "\u0000\u0000\u0000\uD8B6\u0000\uD8BD\u0000\uD8B5\u0000\uD8BE" + // 13300 - 13304 + "\u8EA2\uB3A1\u0000\uD8BB\u0000\u0000\u0000\uD8B8\u0000\uD8BC" + // 13305 - 13309 + "\u0000\uD8BA\u0000\uD8B4\u0000\u0000\u0000\u0000\u0000\uDDC5" + // 13310 - 13314 + "\u0000\u0000\u0000\uDDBF\u8EA2\uB9CF\u0000\uDDC4\u0000\u0000" + // 13315 - 13319 + "\u0000\u0000\u0000\uDDC6\u0000\uDDC0\u0000\u0000\u0000\uDDC1" + // 13320 - 13324 + "\u0000\u0000\u8EA2\uB9CD\u0000\u0000\u0000\u0000\u0000\uDDBE" + // 13325 - 13329 + "\u0000\u0000\u8EA2\uB9CE\u0000\uDDC3\u0000\u0000\u0000\u0000" + // 13330 - 13334 + "\u8EA2\uB9CC\u0000\uDDC2\u0000\u0000\u0000\u0000\u0000\uE2AB" + // 13335 - 13339 + "\u0000\u0000\u0000\uE2AD\u0000\u0000\u0000\u0000\u8EA2\uC0BE" + // 13340 - 13344 + "\u0000\uE2AC\u0000\uE2A1\u0000\uE2A5\u0000\u0000\u0000\uE6EB" + // 13345 - 13349 + "\u8EA2\uC7CF\u0000\uFDA8\u0000\u0000\u0000\u0000\u0000\u0000" + // 13350 - 13354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13355 - 13359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13360 - 13364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBC5" + // 13365 - 13369 + "\u8EA2\uADD0\u0000\uD3D2\u8EA2\uB9CB\u8EA2\uB9C9\u0000\u0000" + // 13370 - 13374 + "\u0000\u0000\u8EA2\uB9CA\u8EA2\uC0BA\u8EA2\uC0BB\u8EA2\uC7C9" + // 13375 - 13379 + "\u8EA2\uC0B9\u0000\u0000\u8EA2\uC7C3\u8EA2\uC7C7\u8EA2\uC7C6" + // 13380 - 13384 + "\u8EA2\uC7C8\u0000\uE6D8\u8EA2\uC7C5\u8EA2\uC7C4\u8EA2\uC7CA" + // 13385 - 13389 + "\u0000\u0000\u8EA2\uCEB1\u8EA2\uCEAF\u0000\u0000\u8EA2\uCEB0" + // 13390 - 13394 + "\u8EA2\uD4BC\u8EA2\uD4BA\u0000\u0000\u0000\u0000\u8EA2\uD4BB" + // 13395 - 13399 + "\u8EA2\uDACB\u8EA2\uDFCA\u8EA2\uDFCB\u0000\uF7BD\u0000\u0000" + // 13400 - 13404 + "\u8EA2\uE7A2\u8EA2\uE9F9\u0000\uFABD\u0000\u0000\u8EA2\uECBB" + // 13405 - 13409 + "\u8EA2\uEEA4\u0000\uFBF3\u0000\u0000\u8EA2\uEFCD\u8EA2\uF1BB" + // 13410 - 13414 + "\u8EA2\uC0AD\u0000\uE1FA\u0000\uE1FB\u0000\u0000\u0000\u0000" + // 13415 - 13419 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC0B5" + // 13420 - 13424 + "\u0000\uE6D4\u8EA2\uC7B9\u8EA2\uC7BF\u8EA2\uC7BA\u8EA2\uC7BB" + // 13425 - 13429 + "\u0000\u0000\u8EA2\uC7C0\u0000\u0000\u0000\uE6D6\u0000\u0000" + // 13430 - 13434 + "\u0000\uE6CE\u0000\uE6D5\u8EA2\uC7B8\u8EA2\uC0AA\u0000\uE6D1" + // 13435 - 13439 + "\u0000\uE6CF\u8EA2\uC7BE\u8EA2\uC7BD\u0000\uE6D0\u0000\uE6D2" + // 13440 - 13444 + "\u8EA2\uC7BC\u0000\uE6CD\u0000\u0000\u0000\uE6D3\u0000\u0000" + // 13445 - 13449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCDFD" + // 13450 - 13454 + "\u0000\uEAFC\u0000\u0000\u0000\u0000\u8EA2\uCEAA\u8EA2\uCEA9" + // 13455 - 13459 + "\u0000\u0000\u8EA2\uCEA4\u0000\uEAFE\u8EA2\uCEA8\u8EA2\uCDFE" + // 13460 - 13464 + "\u8EA2\uCEA1\u0000\uEAF6\u0000\uEAF8\u0000\u0000\u8EA2\uCEA6" + // 13465 - 13469 + "\u8EA2\uCDFC\u0000\uEAFA\u0000\uEAF9\u8EA2\uCEA2\u8EA2\uCEA7" + // 13470 - 13474 + "\u8EA2\uCEA5\u0000\uEAFB\u8EA2\uCEA3\u0000\u0000\u0000\uA1AA" + // 13475 - 13479 + "\u8EAD\uA4C3\u0000\uA1EC\u0000\uA2E3\u0000\uA2E8\u0000\uA1ED" + // 13480 - 13484 + "\u8EAD\uA4C2\u0000\uA1BE\u0000\uA1BF\u0000\uA1EE\u0000\uA2B0" + // 13485 - 13489 + "\u0000\uA1A2\u0000\uA2B1\u0000\uA1A5\u0000\uA2DF\u0000\uA4A1" + // 13490 - 13494 + "\u0000\uA4A2\u0000\uA4A3\u0000\uA4A4\u0000\uA4A5\u0000\uA4A6" + // 13495 - 13499 + "\u0000\uA4A7\u0000\uA4A8\u0000\uA4A9\u0000\uA4AA\u0000\uA1A8" + // 13500 - 13504 + "\u0000\uA1A7\u0000\uA2B6\u0000\uA2B8\u0000\uA2B7\u0000\uA1A9" + // 13505 - 13509 + "\u0000\uA2E9\u0000\uA4C1\u0000\uA4C2\u0000\uA4C3\u0000\uA4C4" + // 13510 - 13514 + "\u0000\uA4C5\u0000\uA4C6\u0000\uA4C7\u0000\uA4C8\u0000\uA4C9" + // 13515 - 13519 + "\u0000\uA4CA\u0000\uA4CB\u0000\uA4CC\u0000\uA4CD\u0000\uA4CE" + // 13520 - 13524 + "\u0000\uA4CF\u0000\uA4D0\u0000\uA4D1\u0000\uA4D2\u0000\uA4D3" + // 13525 - 13529 + "\u0000\uA4D4\u0000\uA4D5\u0000\uA4D6\u0000\uA4D7\u0000\uA4D8" + // 13530 - 13534 + "\u0000\uA4D9\u0000\uA4DA\u8EAD\uA1B0\u0000\uA2E0\u8EAD\uA1B1" + // 13535 - 13539 + "\u8EAD\uA1A4\u0000\uA2A5\u8EA2\uB2F7\u0000\uD8B0\u0000\uD8B1" + // 13540 - 13544 + "\u8EA2\uB2FB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13545 - 13549 + "\u0000\uDDB6\u8EA2\uB9B5\u0000\u0000\u0000\uDDBB\u8EA2\uB9C5" + // 13550 - 13554 + "\u0000\uDDBA\u8EA2\uB9C7\u0000\u0000\u0000\u0000\u8EA2\uB9BB" + // 13555 - 13559 + "\u0000\uDDB8\u8EA2\uB9C6\u0000\u0000\u8EA2\uB9B6\u0000\uDDB9" + // 13560 - 13564 + "\u8EA2\uB9C2\u8EA2\uB9BE\u8EA2\uB9C0\u8EA2\uB9BA\u8EA2\uB9C1" + // 13565 - 13569 + "\u0000\u0000\u0000\u0000\u0000\uDDB5\u8EA2\uB9BD\u0000\u0000" + // 13570 - 13574 + "\u8EA2\uB9BC\u8EA2\uB9B8\u0000\u0000\u8EA2\uB9C3\u0000\u0000" + // 13575 - 13579 + "\u0000\u0000\u8EA2\uB9BF\u8EA2\uB9B7\u0000\u0000\u8EA2\uB9B9" + // 13580 - 13584 + "\u0000\uDDB7\u8EA2\uB9C4\u0000\u0000\u0000\u0000\u0000\u0000" + // 13585 - 13589 + "\u0000\u0000\u0000\uE1FC\u8EA2\uC0B3\u0000\u0000\u0000\u0000" + // 13590 - 13594 + "\u0000\u0000\u8EA2\uC0AF\u8EA2\uC0B1\u8EA2\uC0AC\u8EA2\uC0AB" + // 13595 - 13599 + "\u8EA2\uC0A9\u0000\u0000\u8EA2\uC0B0\u8EA2\uC0B2\u8EA2\uC0AE" + // 13600 - 13604 + "\u0000\u0000\u0000\uEBF6\u0000\uEBF7\u8EA2\uD6A4\u0000\uF0A5" + // 13605 - 13609 + "\u0000\uF0A4\u8EA2\uD6A3\u0000\u0000\u8EA2\uE0F5\u0000\u0000" + // 13610 - 13614 + "\u8EA2\uE4EA\u8EA2\uE4EC\u8EA2\uE4EB\u0000\uF7FD\u0000\uF7FC" + // 13615 - 13619 + "\u0000\uF7FB\u0000\u0000\u0000\uFBCA\u8EA2\uECF4\u0000\u0000" + // 13620 - 13624 + "\u0000\uFBC9\u8EA2\uEED6\u8EA2\uEED5\u0000\u0000\u0000\uFCF7" + // 13625 - 13629 + "\u8EA2\uF0E3\u0000\uDEA3\u8EA2\uC8F0\u0000\u0000\u0000\u0000" + // 13630 - 13634 + "\u0000\u0000\u8EA2\uCFC9\u8EA2\uCFC7\u8EA2\uCFC8\u0000\u0000" + // 13635 - 13639 + "\u0000\u0000\u8EA2\uD6AD\u0000\u0000\u8EA2\uD6A8\u8EA2\uD6A5" + // 13640 - 13644 + "\u8EA2\uD6AC\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD6AE" + // 13645 - 13649 + "\u0000\u0000\u0000\u0000\u0000\uF0A7\u8EA2\uD6AB\u8EA2\uD6A7" + // 13650 - 13654 + "\u0000\u0000\u0000\u0000\u8EA2\uD6A6\u8EA2\uD6AA\u8EA2\uD6A9" + // 13655 - 13659 + "\u0000\uF0A6\u0000\u0000\u0000\u0000\u8EA2\uDCAB\u8EA2\uDCA6" + // 13660 - 13664 + "\u8EA2\uDCA4\u8EA2\uDCAD\u8EA2\uDCA5\u0000\u0000\u8EA2\uEDA3" + // 13665 - 13669 + "\u0000\uFBCF\u8EA2\uEDA9\u0000\u0000\u8EA2\uEDA8\u0000\u0000" + // 13670 - 13674 + "\u8EA2\uEDA4\u8EA2\uEDB0\u0000\u0000\u8EA2\uEDA5\u8EA2\uEEE8" + // 13675 - 13679 + "\u8EA2\uEDB3\u8EA2\uEDB5\u8EA2\uEDAC\u8EA2\uEDA6\u8EA2\uEEE9" + // 13680 - 13684 + "\u8EA2\uEEEB\u8EA2\uEEE5\u0000\uFCAE\u0000\u0000\u8EA2\uEEF2" + // 13685 - 13689 + "\u8EA2\uEEF0\u0000\uFCAF\u8EA2\uEEEF\u8EA2\uEEEE\u8EA2\uEEE7" + // 13690 - 13694 + "\u8EA2\uEEE4\u8EA2\uEEEA\u8EA2\uEEF3\u8EA2\uEEE6\u8EA2\uEEEC" + // 13695 - 13699 + "\u0000\u0000\u8EA2\uEFF7\u8EA2\uEFFC\u8EA2\uEFF2\u8EA2\uEFF5" + // 13700 - 13704 + "\u0000\uFCDA\u8EA2\uEFF9\u0000\u0000\u8EA2\uF0A2\u8EA2\uEEED" + // 13705 - 13709 + "\u0000\u0000\u8EA2\uEFF3\u8EA2\uEFFD\u8EA2\uF0A3\u8EA2\uEFF8" + // 13710 - 13714 + "\u8EA2\uEFF1\u8EA2\uEFFB\u0000\u0000\u8EA2\uEFFA\u8EA2\uF0A1" + // 13715 - 13719 + "\u8EA2\uEFFE\u8EA2\uEEF1\u8EA2\uEFF6\u8EA2\uEFF0\u8EA2\uEFF4" + // 13720 - 13724 + "\u0000\uFCF9\u0000\uFCFA\u8EA2\uEFEF\u0000\u0000\u8EA2\uF0F4" + // 13725 - 13729 + "\u8EA2\uF0EB\u8EA2\uF0F3\u0000\uC8E6\u8EA2\uADCB\u0000\u0000" + // 13730 - 13734 + "\u8EA2\uB2F1\u8EA2\uB2F2\u0000\u0000\u0000\u0000\u0000\u0000" + // 13735 - 13739 + "\u8EA2\uC0A6\u0000\u0000\u8EA2\uECB6\u8EA2\uF0CE\u0000\uC8E7" + // 13740 - 13744 + "\u0000\uD3CF\u8EA2\uADCC\u0000\u0000\u0000\u0000\u0000\u0000" + // 13745 - 13749 + "\u8EA2\uB9B4\u0000\uDDB4\u0000\u0000\u8EA2\uC0A8\u8EA2\uC0A7" + // 13750 - 13754 + "\u0000\uE1F9\u0000\u0000\u0000\uE6CC\u8EA2\uD4AA\u0000\uEEEC" + // 13755 - 13759 + "\u0000\u0000\u0000\uEEED\u0000\u0000\u0000\u0000\u0000\u0000" + // 13760 - 13764 + "\u0000\uF2AF\u0000\uFCE8\u0000\uC8E8\u0000\u0000\u0000\u0000" + // 13765 - 13769 + "\u0000\u0000\u8EA2\uADCD\u0000\uCFD0\u8EA2\uADCF\u8EA2\uADCE" + // 13770 - 13774 + "\u0000\uD3D0\u0000\u0000\u8EA2\uB2F3\u0000\u0000\u8EA2\uB2FA" + // 13775 - 13779 + "\u0000\uD8AE\u8EA2\uB2F8\u8EA2\uB2F6\u0000\u0000\u8EA2\uB2FD" + // 13780 - 13784 + "\u8EA2\uB2F4\u8EA2\uB2F5\u0000\uD8AF\u0000\u0000\u0000\uD8B3" + // 13785 - 13789 + "\u0000\u0000\u0000\u0000\u8EA2\uB2FE\u0000\uD8B2\u8EA2\uB2FC" + // 13790 - 13794 + "\u8EA2\uB2F9\u8EA2\uE6E6\u8EA2\uE3B9\u8EA2\uE6F3\u8EA2\uE6E8" + // 13795 - 13799 + "\u0000\u0000\u0000\uF8F2\u8EA2\uE6EE\u0000\u0000\u8EA2\uE6F0" + // 13800 - 13804 + "\u8EA2\uE6EB\u8EA2\uE6F2\u8EA2\uE6ED\u8EA2\uE6EC\u0000\uF8F3" + // 13805 - 13809 + "\u0000\u0000\u0000\u0000\u8EA2\uE9F1\u8EA2\uE9F2\u0000\u0000" + // 13810 - 13814 + "\u8EA2\uE9F4\u0000\uFAB9\u0000\uFABA\u8EA2\uE9F5\u8EA2\uE9F3" + // 13815 - 13819 + "\u0000\u0000\u8EA2\uE9F0\u0000\u0000\u8EA2\uECB1\u8EA2\uECB4" + // 13820 - 13824 + "\u8EA2\uECB0\u0000\u0000\u0000\uFBA7\u8EA2\uECB2\u0000\uFBA6" + // 13825 - 13829 + "\u0000\uFBA5\u0000\uFBA4\u8EA2\uECB3\u8EA2\uEDFE\u8EA2\uEDFC" + // 13830 - 13834 + "\u0000\u0000\u8EA2\uEDFB\u8EA2\uECAF\u8EA2\uEDFD\u8EA2\uECB5" + // 13835 - 13839 + "\u8EA2\uEDFA\u0000\u0000\u8EA2\uEFC8\u8EA2\uEFA6\u8EA2\uEFC6" + // 13840 - 13844 + "\u0000\uFCC7\u8EA2\uEFC7\u8EA2\uEFC9\u0000\u0000\u8EA2\uF0CD" + // 13845 - 13849 + "\u0000\uFCE6\u8EA2\uF0CC\u8EA2\uF0CB\u0000\uFCE7\u0000\u0000" + // 13850 - 13854 + "\u0000\uFDA7\u8EA2\uF1DD\u8EA2\uF1F5\u8EA2\uF1F4\u8EA2\uF1F6" + // 13855 - 13859 + "\u0000\uF5B7\u0000\u0000\u8EA2\uDFB2\u8EA2\uDFB1\u8EA2\uDFB6" + // 13860 - 13864 + "\u8EA2\uDFA9\u0000\uF5BB\u0000\u0000\u0000\uF5BF\u8EA2\uDFB0" + // 13865 - 13869 + "\u8EA2\uDFB7\u0000\uF5C0\u8EA2\uDFB3\u0000\u0000\u0000\u0000" + // 13870 - 13874 + "\u0000\u0000\u0000\u0000\u0000\uF5B8\u0000\uF5BA\u8EA2\uE3B3" + // 13875 - 13879 + "\u8EA2\uE3B1\u0000\u0000\u0000\u0000\u8EA2\uE3B7\u8EA2\uE3B5" + // 13880 - 13884 + "\u8EA2\uE3B8\u0000\u0000\u8EA2\uE3AA\u8EA2\uE3B2\u8EA2\uE3BC" + // 13885 - 13889 + "\u8EA2\uDFBD\u8EA2\uE3AE\u0000\uF7BA\u8EA2\uD3EC\u8EA2\uE3A9" + // 13890 - 13894 + "\u8EA2\uE3B6\u8EA2\uE3B0\u8EA2\uE3AD\u8EA2\uE3A8\u8EA2\uE3A7" + // 13895 - 13899 + "\u8EA2\uE3BB\u0000\u0000\u8EA2\uE3AC\u8EA2\uE3AB\u0000\uF7B8" + // 13900 - 13904 + "\u8EA2\uE3B4\u0000\u0000\u0000\uF7B7\u0000\u0000\u0000\u0000" + // 13905 - 13909 + "\u0000\uF7B9\u8EA2\uE3AF\u8EA2\uE3BA\u0000\u0000\u8EA2\uE6E9" + // 13910 - 13914 + "\u8EA2\uE6EA\u0000\u0000\u0000\uF8F4\u8EA2\uE6E7\u0000\uF8F1" + // 13915 - 13919 + "\u8EA2\uE6EF\u0000\u0000\u0000\uF8F5\u8EA2\uE6F1\u8EA2\uCDE0" + // 13920 - 13924 + "\u8EA2\uCDE8\u8EA2\uCDF4\u8EA2\uCDE6\u0000\u0000\u0000\u0000" + // 13925 - 13929 + "\u8EA2\uCDEA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13930 - 13934 + "\u0000\u0000\u0000\uEEEA\u8EA2\uC7B0\u8EA2\uD3F9\u8EA2\uD4A4" + // 13935 - 13939 + "\u8EA2\uD3F8\u8EA2\uD3F4\u8EA2\uD3F1\u0000\uEEEB\u8EA2\uD3EF" + // 13940 - 13944 + "\u0000\uEAF3\u8EA2\uD3E8\u0000\uEEE9\u8EA2\uD3EE\u0000\uEEE8" + // 13945 - 13949 + "\u8EA2\uD3F3\u8EA2\uD3F0\u8EA2\uD4A2\u8EA2\uD3FB\u8EA2\uD3F5" + // 13950 - 13954 + "\u8EA2\uD3FA\u0000\uEEE4\u8EA2\uD3F2\u8EA2\uD4A7\u8EA2\uD3E9" + // 13955 - 13959 + "\u8EA2\uD3EA\u8EA2\uD4A3\u0000\uEEE5\u8EA2\uD4A8\u0000\uEEE7" + // 13960 - 13964 + "\u8EA2\uD4A9\u8EA2\uD3F7\u8EA2\uCDDF\u8EA2\uD3FD\u8EA2\uD3F6" + // 13965 - 13969 + "\u8EA2\uD4A1\u8EA2\uD3FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 13970 - 13974 + "\u8EA2\uD3ED\u0000\uEEE2\u8EA2\uD4A6\u0000\uEEE3\u8EA2\uD3EB" + // 13975 - 13979 + "\u0000\uEEE6\u8EA2\uDAB2\u8EA2\uD3FE\u8EA2\uD4A5\u0000\u0000" + // 13980 - 13984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA4B8\u0000\u0000" + // 13985 - 13989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 13990 - 13994 + "\u0000\u0000\u0000\u0000\u8EAD\uA4AE\u0000\u0000\u0000\u0000" + // 13995 - 13999 + "\u0000\u0000\u0000\u0000\u0000\uA3C4\u0000\u0000\u0000\u0000" + // 14000 - 14004 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14005 - 14009 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14010 - 14014 + "\u0000\u0000\u0000\uA3C5\u0000\u0000\u0000\u0000\u0000\uA3C7" + // 14015 - 14019 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14020 - 14024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA3C6\u0000\u0000" + // 14025 - 14029 + "\u0000\u0000\u0000\uA3C0\u0000\uA3C1\u0000\uA3C3\u0000\uA3C2" + // 14030 - 14034 + "\u0000\uA3CC\u0000\uA3CD\u0000\uA3CE\u0000\uA1BB\u0000\u0000" + // 14035 - 14039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14040 - 14044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14045 - 14049 + "\u0000\uA5EF\u0000\u0000\u0000\uA5ED\u0000\uA5EE\u0000\uA5F0" + // 14050 - 14054 + "\u0000\u0000\u0000\uA2A6\u0000\u0000\u0000\u0000\u0000\u0000" + // 14055 - 14059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14060 - 14064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA5EC\u0000\u0000" + // 14065 - 14069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14070 - 14074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14075 - 14079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14080 - 14084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14085 - 14089 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14090 - 14094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14095 - 14099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14100 - 14104 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3F8" + // 14105 - 14109 + "\u8EA2\uC1D2\u0000\u0000\u0000\u0000\u8EA2\uC8E7\u8EA2\uCFB4" + // 14110 - 14114 + "\u8EA2\uCFB5\u0000\uEBEC\u0000\u0000\u0000\uEBED\u8EA2\uD5E7" + // 14115 - 14119 + "\u0000\uEFF1\u0000\u0000\u0000\u0000\u0000\uF6A9\u0000\u0000" + // 14120 - 14124 + "\u8EA2\uE4D5\u0000\u0000\u0000\uF7F1\u8EA2\uE7F8\u0000\uF9CE" + // 14125 - 14129 + "\u8EA2\uE7F7\u8EA2\uE7F9\u8EA2\uE7F6\u0000\uE6C5\u8EA2\uC7B4" + // 14130 - 14134 + "\u0000\uE6C8\u0000\uE6C9\u8EA2\uC7AA\u8EA2\uC7B7\u0000\uE6CA" + // 14135 - 14139 + "\u0000\uE6C4\u0000\uE6C3\u8EA2\uC7B3\u0000\uE6CB\u8EA2\uC7A9" + // 14140 - 14144 + "\u8EA2\uC7AC\u8EA2\uC7B6\u8EA2\uC7B2\u0000\u0000\u0000\u0000" + // 14145 - 14149 + "\u8EA2\uCDFB\u8EA2\uCDF0\u0000\uE6C2\u0000\u0000\u0000\u0000" + // 14150 - 14154 + "\u0000\u0000\u0000\u0000\u0000\uEAF2\u8EA2\uCDE4\u8EA2\uCDF9" + // 14155 - 14159 + "\u8EA2\uCDE5\u0000\uEAED\u0000\u0000\u8EA2\uCDE2\u0000\u0000" + // 14160 - 14164 + "\u8EA2\uCDEB\u8EA2\uCDE3\u0000\uEAEF\u8EA2\uCDDD\u8EA2\uCDF8" + // 14165 - 14169 + "\u0000\uEAF0\u8EA2\uCDF5\u8EA2\uCDF6\u8EA2\uCDDE\u0000\uEAF5" + // 14170 - 14174 + "\u8EA2\uCDED\u0000\u0000\u8EA2\uCDE7\u8EA2\uCDEE\u8EA2\uCDE1" + // 14175 - 14179 + "\u0000\u0000\u8EA2\uCDFA\u8EA2\uCDF2\u8EA2\uCDEC\u8EA2\uCDDC" + // 14180 - 14184 + "\u0000\uEAF1\u8EA2\uCDF3\u0000\u0000\u0000\uEAF4\u8EA2\uCDF7" + // 14185 - 14189 + "\u0000\u0000\u8EA2\uCDF1\u0000\uEAEE\u8EA2\uCDEF\u0000\u0000" + // 14190 - 14194 + "\u8EA2\uCDE9\u0000\uEAEC\u0000\uDDAB\u8EA2\uB9A5\u8EA2\uB9A4" + // 14195 - 14199 + "\u8EA2\uB9B1\u0000\uDDAD\u8EA2\uB9A6\u0000\uDDAF\u0000\uDDAA" + // 14200 - 14204 + "\u8EA2\uB9A9\u0000\uDDB3\u0000\u0000\u0000\uDDB0\u8EA2\uB9AD" + // 14205 - 14209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE1F7\u8EA2\uC0A5" + // 14210 - 14214 + "\u0000\u0000\u8EA2\uBFF8\u0000\uE1F4\u0000\u0000\u8EA2\uC7AE" + // 14215 - 14219 + "\u8EA2\uC0A3\u8EA2\uBFF5\u0000\uE1F2\u8EA2\uBFFA\u0000\uE1F5" + // 14220 - 14224 + "\u8EA2\uBFFE\u8EA2\uBFFC\u0000\uE1F8\u0000\uE1F1\u0000\u0000" + // 14225 - 14229 + "\u0000\u0000\u8EA2\uBFF6\u8EA2\uBFF9\u0000\uE1F6\u0000\u0000" + // 14230 - 14234 + "\u8EA2\uBFF7\u0000\u0000\u8EA2\uC0A4\u8EA2\uC0A2\u8EA2\uBFFB" + // 14235 - 14239 + "\u8EA2\uBFFD\u8EA2\uC0A1\u0000\uE1F3\u0000\u0000\u0000\u0000" + // 14240 - 14244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14245 - 14249 + "\u8EA2\uC7AF\u8EA2\uC7B5\u8EA2\uC7AB\u8EA2\uC7B1\u0000\uE6C1" + // 14250 - 14254 + "\u8EA2\uC7AD\u0000\uE6C7\u0000\u0000\u0000\u0000\u0000\uE6C6" + // 14255 - 14259 + "\u0000\u0000\u8EA2\uE0F3\u0000\u0000\u0000\uF7F9\u8EA2\uE4E6" + // 14260 - 14264 + "\u8EA2\uE4E7\u0000\uF7FA\u0000\u0000\u8EA2\uE4E5\u0000\u0000" + // 14265 - 14269 + "\u8EA2\uE8B0\u8EA2\uE8AF\u8EA2\uE8B2\u0000\uF9D3\u8EA2\uE8B1" + // 14270 - 14274 + "\u0000\u0000\u8EA2\uEAF5\u8EA2\uEAF7\u8EA2\uEAF6\u0000\u0000" + // 14275 - 14279 + "\u0000\u0000\u8EA2\uECEF\u8EA2\uECF2\u8EA2\uECF0\u8EA2\uECF1" + // 14280 - 14284 + "\u8EA2\uEED2\u0000\uFCA9\u0000\u0000\u0000\u0000\u0000\u0000" + // 14285 - 14289 + "\u8EA2\uEFE2\u8EA2\uEFE3\u8EA2\uEFE4\u0000\u0000\u0000\uFCF6" + // 14290 - 14294 + "\u0000\uFDB0\u8EA2\uF2A6\u0000\uD8EE\u0000\u0000\u0000\uF0A3" + // 14295 - 14299 + "\u0000\uF3B4\u8EA2\uE4E8\u0000\u0000\u8EA2\uEED3\u0000\u0000" + // 14300 - 14304 + "\u0000\u0000\u8EA2\uF1E6\u8EA2\uB3CD\u0000\u0000\u0000\uFDC8" + // 14305 - 14309 + "\u0000\uD8EF\u8EA2\uDBFE\u0000\u0000\u8EA2\uE4E9\u0000\u0000" + // 14310 - 14314 + "\u8EA2\uE8B3\u0000\u0000\u0000\u0000\u8EA2\uECF3\u8EA2\uEED4" + // 14315 - 14319 + "\u0000\uD8F0\u0000\u0000\u8EA2\uCFC5\u8EA2\uCFC6\u8EA2\uEFC0" + // 14320 - 14324 + "\u0000\u0000\u0000\u0000\u8EA2\uF0CA\u0000\u0000\u0000\u0000" + // 14325 - 14329 + "\u8EA2\uF1B4\u8EA2\uF1B5\u8EA2\uF1B6\u0000\u0000\u0000\u0000" + // 14330 - 14334 + "\u8EA2\uF2B5\u8EA2\uF2AD\u8EA2\uA2EF\u0000\uCFCD\u0000\u0000" + // 14335 - 14339 + "\u0000\uD3CB\u0000\u0000\u8EA2\uB2DA\u8EA2\uB2D9\u0000\uD8A5" + // 14340 - 14344 + "\u0000\uDDA8\u8EA2\uB9A1\u0000\u0000\u0000\u0000\u8EA2\uB8FE" + // 14345 - 14349 + "\u0000\u0000\u0000\uE1F0\u0000\uE6BF\u0000\u0000\u0000\uE6BE" + // 14350 - 14354 + "\u0000\uE6C0\u0000\u0000\u8EA2\uCDDB\u8EA2\uD3E7\u8EA2\uDAA5" + // 14355 - 14359 + "\u8EA2\uDAA7\u8EA2\uDAA6\u0000\u0000\u0000\uF5B6\u8EA2\uDFA5" + // 14360 - 14364 + "\u8EA2\uE3A6\u8EA2\uF1F3\u0000\uC8E5\u0000\u0000\u8EA2\uA8E9" + // 14365 - 14369 + "\u8EA2\uA8EA\u8EA2\uA8E8\u8EA2\uA8E7\u0000\uCFCE\u0000\u0000" + // 14370 - 14374 + "\u8EA2\uADCA\u8EA2\uADC8\u0000\u0000\u0000\u0000\u8EA2\uADC7" + // 14375 - 14379 + "\u0000\u0000\u0000\uD3CC\u0000\uD3CE\u0000\uD3CD\u8EA2\uADC9" + // 14380 - 14384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCACD\u0000\u0000" + // 14385 - 14389 + "\u0000\u0000\u8EA2\uD0D8\u0000\u0000\u0000\uE8F1\u0000\uECFC" + // 14390 - 14394 + "\u8EA2\uD7B5\u8EA2\uD0DD\u8EA2\uD0DC\u8EA2\uD0DE\u0000\u0000" + // 14395 - 14399 + "\u0000\u0000\u8EA2\uD0DB\u0000\uECFD\u8EAD\uA4C7\u0000\uEDA5" + // 14400 - 14404 + "\u0000\uEDA2\u0000\u0000\u0000\uEDA3\u8EA2\uD0D6\u8EA2\uD0D9" + // 14405 - 14409 + "\u8EA2\uD0E3\u0000\uEDAB\u0000\uEDA9\u0000\u0000\u8EA2\uD0DA" + // 14410 - 14414 + "\u8EA2\uBBFD\u0000\uECFA\u8EA2\uD0E0\u8EA2\uD0D7\u0000\u0000" + // 14415 - 14419 + "\u0000\uEDAC\u8EA2\uD0DF\u0000\uE8F4\u0000\uEDA1\u8EA2\uCACB" + // 14420 - 14424 + "\u0000\u0000\u0000\u0000\u0000\uEDA4\u0000\u0000\u0000\uEDA8" + // 14425 - 14429 + "\u0000\uEDAA\u0000\uEDA7\u0000\uEDA6\u0000\u0000\u0000\uECFE" + // 14430 - 14434 + "\u8EA2\uD0E2\u0000\uECFB\u0000\uEDAD\u0000\u0000\u0000\u0000" + // 14435 - 14439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14440 - 14444 + "\u0000\uF0E1\u0000\uF0E2\u8EA2\uD7B4\u0000\uF0EB\u0000\uF0E8" + // 14445 - 14449 + "\u8EA2\uE9E9\u8EA2\uE9E6\u0000\u0000\u0000\u0000\u8EA2\uE9EB" + // 14450 - 14454 + "\u8EA2\uE9ED\u0000\uFAB5\u0000\uFAB7\u0000\u0000\u8EA2\uE9EC" + // 14455 - 14459 + "\u0000\uFAB8\u0000\uFAB6\u8EA2\uE9EE\u0000\u0000\u0000\u0000" + // 14460 - 14464 + "\u0000\u0000\u0000\u0000\u0000\uFAB3\u0000\u0000\u0000\u0000" + // 14465 - 14469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFBA1\u8EA2\uECA1" + // 14470 - 14474 + "\u8EA2\uECA7\u0000\uFBA3\u8EA2\uE9E8\u8EA2\uECA6\u0000\u0000" + // 14475 - 14479 + "\u8EA2\uECAD\u8EA2\uECA4\u8EA2\uECAB\u8EA2\uECAA\u8EA2\uE9E4" + // 14480 - 14484 + "\u8EA2\uECA5\u8EA2\uE3A2\u8EA2\uECAE\u8EA2\uECA3\u8EA2\uECA8" + // 14485 - 14489 + "\u0000\u0000\u8EA2\uECAC\u8EA2\uECA2\u0000\u0000\u8EA2\uEDF7" + // 14490 - 14494 + "\u0000\uFBA2\u8EA2\uECA9\u0000\u0000\u0000\u0000\u8EA2\uEFC3" + // 14495 - 14499 + "\u8EA2\uEDF8\u8EA2\uEDF6\u8EA2\uEDF4\u8EA2\uEDF5\u8EA2\uEDF9" + // 14500 - 14504 + "\u0000\u0000\u0000\uFCC5\u8EA2\uEFC1\u8EA2\uEFBF\u8EA2\uEFC4" + // 14505 - 14509 + "\u8EA2\uEFC2\u0000\u0000\u8EA2\uEFC5\u0000\uFCC6\u8EA2\uE2F0" + // 14510 - 14514 + "\u0000\u0000\u8EA2\uE2F2\u8EA2\uE2F1\u8EA2\uE2F7\u8EA2\uE2FC" + // 14515 - 14519 + "\u8EA2\uE2EF\u8EA2\uE2F6\u8EA2\uE2FB\u0000\uF7B3\u0000\u0000" + // 14520 - 14524 + "\u8EA2\uE2F9\u0000\u0000\u0000\uF7B1\u8EA2\uE2FA\u0000\uF7AF" + // 14525 - 14529 + "\u0000\uF7B2\u8EA2\uE6E0\u8EA2\uE3A1\u0000\u0000\u0000\u0000" + // 14530 - 14534 + "\u0000\uF8ED\u0000\u0000\u8EA2\uE6D8\u8EA2\uE6DC\u8EA2\uE6D4" + // 14535 - 14539 + "\u8EA2\uE6D7\u0000\u0000\u8EA2\uE6DF\u0000\uF8EB\u8EA2\uE6E4" + // 14540 - 14544 + "\u8EA2\uE6DD\u0000\u0000\u8EA2\uE6D5\u8EA2\uE6E5\u8EA2\uE6DE" + // 14545 - 14549 + "\u0000\uF8EE\u0000\uF8EF\u8EA2\uE6E2\u0000\u0000\u8EA2\uE6D6" + // 14550 - 14554 + "\u0000\uF8EA\u0000\uF8EC\u8EA2\uE6D1\u8EA2\uE6D9\u8EA2\uE6D3" + // 14555 - 14559 + "\u0000\u0000\u8EA2\uE6E3\u8EA2\uE6E1\u8EA2\uE6D2\u8EA2\uE6DA" + // 14560 - 14564 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE9EA\u0000\uF8F0" + // 14565 - 14569 + "\u8EA2\uE6DB\u0000\uFAB2\u0000\uFAB4\u0000\uFAB1\u0000\u0000" + // 14570 - 14574 + "\u8EA2\uE9EF\u8EA2\uE9E7\u8EA2\uE9E5\u8EA2\uDEE1\u8EA2\uDEF9" + // 14575 - 14579 + "\u8EA2\uDEFB\u8EA2\uDEE0\u0000\uF5AB\u8EA2\uDEFD\u8EA2\uDEF5" + // 14580 - 14584 + "\u0000\uF5B2\u8EA2\uDEFC\u8EA2\uDEEE\u0000\uF5B4\u8EA2\uDEE6" + // 14585 - 14589 + "\u8EA2\uD9FD\u8EA2\uDEF6\u8EA2\uDEF3\u8EA2\uDEE2\u8EA2\uDFA3" + // 14590 - 14594 + "\u0000\uF5AE\u0000\u0000\u0000\u0000\u0000\uF5AF\u8EA2\uDEE4" + // 14595 - 14599 + "\u8EA2\uDEF4\u0000\u0000\u8EA2\uDFA2\u8EA2\uDEF7\u8EA2\uDEEA" + // 14600 - 14604 + "\u0000\uF5B1\u0000\uF5AD\u8EA2\uDEF8\u8EA2\uDEEB\u8EA2\uDFA4" + // 14605 - 14609 + "\u8EA2\uDEE5\u8EA2\uDEEF\u8EA2\uDEFA\u8EA2\uDEE7\u8EA2\uDEE9" + // 14610 - 14614 + "\u0000\u0000\u0000\uF5B5\u8EA2\uDEE3\u0000\uF5B3\u0000\uF7B0" + // 14615 - 14619 + "\u0000\uF5AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14620 - 14624 + "\u0000\uF5B0\u0000\uF7B4\u8EA2\uE2FD\u0000\u0000\u8EA2\uE2F3" + // 14625 - 14629 + "\u8EA2\uE2EE\u8EA2\uE2F4\u8EA2\uE2FE\u8EA2\uE3A4\u8EA2\uE3A3" + // 14630 - 14634 + "\u0000\uF7B6\u0000\uF7B5\u0000\u0000\u0000\u0000\u8EA2\uE2F5" + // 14635 - 14639 + "\u8EA2\uE3A5\u8EA2\uE2F8\u8EA2\uD9EB\u8EA2\uD9EE\u0000\u0000" + // 14640 - 14644 + "\u0000\uF2A5\u8EA2\uD9F0\u8EA2\uD9E5\u8EA2\uD9EC\u8EA2\uD9F2" + // 14645 - 14649 + "\u0000\uF2A2\u0000\uF2A6\u0000\uF1FE\u0000\u0000\u0000\u0000" + // 14650 - 14654 + "\u8EA2\uD9E7\u8EA2\uD9F7\u0000\u0000\u0000\u0000\u8EA2\uD9F1" + // 14655 - 14659 + "\u0000\u0000\u8EA2\uD9E8\u8EA2\uDAA2\u8EA2\uD9FA\u8EA2\uD9E4" + // 14660 - 14664 + "\u8EA2\uDEF2\u8EA2\uD9EA\u0000\uF2A1\u0000\u0000\u8EA2\uD9F5" + // 14665 - 14669 + "\u0000\u0000\u8EA2\uDAA1\u0000\uF2A9\u0000\u0000\u8EA2\uD9FC" + // 14670 - 14674 + "\u8EA2\uD9E9\u8EA2\uD9EF\u8EA2\uD9F3\u8EA2\uD9ED\u8EA2\uDAA3" + // 14675 - 14679 + "\u8EA2\uD9FE\u8EA2\uD9FB\u0000\uF2A3\u0000\uF2A4\u0000\uF2A8" + // 14680 - 14684 + "\u8EA2\uD9E6\u8EA2\uDAA4\u0000\uF2A7\u8EA2\uD9F8\u0000\u0000" + // 14685 - 14689 + "\u0000\u0000\u8EA2\uD9F6\u0000\u0000\u0000\u0000\u0000\u0000" + // 14690 - 14694 + "\u8EA2\uD9F9\u8EA2\uDFA1\u8EA2\uDEEC\u8EA2\uDEF1\u8EA2\uDEFE" + // 14695 - 14699 + "\u8EA2\uDEF0\u8EA2\uDEE8\u8EA2\uDEED\u0000\u0000\u0000\uF5AC" + // 14700 - 14704 + "\u0000\u0000\u0000\uF6AB\u8EA2\uE0E4\u8EA2\uE0EF\u8EA2\uE4DD" + // 14705 - 14709 + "\u8EA2\uE4E0\u8EA2\uE4E2\u8EA2\uE4E1\u0000\u0000\u8EA2\uE4DB" + // 14710 - 14714 + "\u8EA2\uE4DF\u8EA2\uE4DA\u0000\u0000\u8EA2\uE4DC\u0000\uF7F7" + // 14715 - 14719 + "\u8EA2\uE4D9\u0000\u0000\u8EA2\uE4DE\u0000\u0000\u0000\u0000" + // 14720 - 14724 + "\u8EA2\uE8AD\u8EA2\uE8A2\u0000\uF9D1\u0000\u0000\u0000\u0000" + // 14725 - 14729 + "\u0000\uF9D2\u8EA2\uE8A1\u8EA2\uE8A6\u8EA2\uE8AC\u8EA2\uE8A4" + // 14730 - 14734 + "\u8EA2\uE8AB\u0000\u0000\u8EA2\uE8A8\u0000\u0000\u8EA2\uE8A7" + // 14735 - 14739 + "\u8EA2\uE8AA\u8EA2\uE8A5\u8EA2\uE8A3\u0000\u0000\u8EA2\uE8A9" + // 14740 - 14744 + "\u0000\u0000\u8EA2\uEAEE\u8EA2\uEAEC\u0000\uFAD5\u8EA2\uEAEB" + // 14745 - 14749 + "\u8EA2\uEAF1\u8EA2\uEAEF\u0000\u0000\u0000\uFAD6\u8EA2\uEAEA" + // 14750 - 14754 + "\u8EA2\uEAE8\u0000\u0000\u8EA2\uEAE9\u0000\uFAD8\u8EA2\uEAED" + // 14755 - 14759 + "\u0000\uFAD7\u8EA2\uEAF0\u8EA2\uECE6\u8EA2\uECEC\u0000\u0000" + // 14760 - 14764 + "\u0000\u0000\u8EA2\uECE7\u0000\uFBC7\u8EA2\uECED\u8EA2\uD3B8" + // 14765 - 14769 + "\u0000\u0000\u8EA2\uD3D6\u0000\u0000\u0000\u0000\u0000\u0000" + // 14770 - 14774 + "\u0000\uEEE0\u8EA2\uC6E2\u8EA2\uD3C4\u8EA2\uD3BB\u8EA2\uD3BE" + // 14775 - 14779 + "\u8EA2\uD3E4\u8EA2\uD3C5\u8EA2\uD3BC\u8EA2\uD3BA\u8EA2\uD3B7" + // 14780 - 14784 + "\u0000\u0000\u0000\uEED9\u8EA2\uD3CE\u0000\uEED8\u0000\uEEDC" + // 14785 - 14789 + "\u8EA2\uD3C9\u8EA2\uD3D1\u0000\uEED2\u8EA2\uD3E1\u8EA2\uD3E5" + // 14790 - 14794 + "\u0000\uEED4\u0000\u0000\u8EA2\uD3C0\u8EA2\uD3D4\u8EA2\uD3D8" + // 14795 - 14799 + "\u8EA2\uD3BD\u8EA2\uD3E2\u0000\uEEDB\u0000\u0000\u0000\uEEDA" + // 14800 - 14804 + "\u8EA2\uD3B5\u0000\uEEDE\u8EA2\uD3DB\u8EA2\uD3BF\u8EA2\uD3D3" + // 14805 - 14809 + "\u8EA2\uD3B9\u8EA2\uD3C7\u8EA2\uD3C2\u0000\uEED6\u0000\uEED7" + // 14810 - 14814 + "\u8EA2\uD3D5\u8EA2\uD3E6\u8EA2\uD3E3\u8EA2\uD3DA\u0000\u0000" + // 14815 - 14819 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14820 - 14824 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD3B6\u0000\u0000" + // 14825 - 14829 + "\u0000\uEED3\u8EA2\uD9F4\u0000\u0000\u0000\uEBF2\u8EA2\uCFBF" + // 14830 - 14834 + "\u8EA2\uCFBB\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCFC1" + // 14835 - 14839 + "\u0000\u0000\u8EA2\uD5F5\u0000\u0000\u8EA2\uD5F8\u8EA2\uD5FA" + // 14840 - 14844 + "\u8EA2\uD5F0\u8EA2\uD5F4\u8EA2\uD5F1\u0000\uEFF7\u0000\uEFFA" + // 14845 - 14849 + "\u0000\uEFFC\u8EA2\uD5F2\u8EA2\uD5F3\u0000\uEFFB\u8EA2\uD5F6" + // 14850 - 14854 + "\u8EA2\uD5F9\u8EA2\uD5F7\u0000\uEFFD\u0000\u0000\u0000\uEFF9" + // 14855 - 14859 + "\u8EA2\uD5EF\u0000\uEFF6\u0000\u0000\u0000\uEFF8\u0000\u0000" + // 14860 - 14864 + "\u0000\u0000\u0000\uF3AE\u8EA2\uDBF2\u8EA2\uDBF0\u8EA2\uDBEF" + // 14865 - 14869 + "\u0000\u0000\u8EA2\uDBF5\u0000\u0000\u8EA2\uDBF4\u8EA2\uDBF3" + // 14870 - 14874 + "\u0000\u0000\u8EA2\uDBEE\u0000\uF3AD\u8EA2\uDBED\u0000\u0000" + // 14875 - 14879 + "\u8EA2\uDBF1\u0000\uF3AF\u0000\u0000\u0000\u0000\u8EA2\uE0E6" + // 14880 - 14884 + "\u0000\u0000\u8EA2\uE0EA\u8EA2\uE0E7\u8EA2\uE0E9\u8EA2\uE0E8" + // 14885 - 14889 + "\u8EA2\uE0E5\u8EA2\uE0EB\u8EA2\uE0EE\u8EA2\uE0EC\u8EA2\uE0ED" + // 14890 - 14894 + "\u0000\uF6AC\u0000\uEAE6\u8EA2\uCDBF\u8EA2\uCDB9\u0000\u0000" + // 14895 - 14899 + "\u0000\uEADF\u8EA2\uC6F9\u0000\uEADE\u8EA2\uCDCA\u0000\u0000" + // 14900 - 14904 + "\u0000\uEADC\u0000\uEAEB\u0000\uEAE4\u8EA2\uCDCB\u8EA2\uCDC0" + // 14905 - 14909 + "\u8EA2\uCDB8\u8EA2\uCDD3\u8EA2\uCDC4\u0000\uEAEA\u8EA2\uCDD7" + // 14910 - 14914 + "\u0000\uEAE7\u8EA2\uCDD6\u0000\u0000\u8EA2\uCDD0\u8EA2\uCDD5" + // 14915 - 14919 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCDD8\u0000\u0000" + // 14920 - 14924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14925 - 14929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 14930 - 14934 + "\u8EA2\uD3DC\u8EA2\uD3DD\u8EA2\uD3D0\u8EA2\uD3CF\u8EA2\uD3CB" + // 14935 - 14939 + "\u0000\uEEDD\u0000\u0000\u0000\uEED5\u8EA2\uD3DF\u8EA2\uD3DE" + // 14940 - 14944 + "\u8EA2\uC6CE\u8EA2\uD3C8\u8EA2\uD3CC\u8EA2\uD3C6\u0000\u0000" + // 14945 - 14949 + "\u8EA2\uD3D9\u8EA2\uD3CA\u0000\u0000\u8EA2\uD3E0\u8EA2\uD3C3" + // 14950 - 14954 + "\u8EA2\uD3C1\u8EA2\uD3CD\u8EA2\uD3D7\u8EA2\uD3D2\u0000\uEEDF" + // 14955 - 14959 + "\u8EA2\uC6D7\u0000\u0000\u8EA2\uC6F0\u8EA2\uC6E8\u8EA2\uC7A3" + // 14960 - 14964 + "\u8EA2\uC6EB\u0000\uE6BD\u8EA2\uC6FC\u0000\u0000\u0000\uE6B4" + // 14965 - 14969 + "\u0000\u0000\u8EA2\uC6EE\u8EA2\uC6F6\u8EA2\uC6DB\u8EA2\uC6F5" + // 14970 - 14974 + "\u0000\u0000\u8EA2\uC7A8\u8EA2\uC6D6\u8EA2\uC6F7\u0000\u0000" + // 14975 - 14979 + "\u8EA2\uC7A6\u0000\u0000\u8EA2\uC6D0\u0000\uE1E1\u0000\u0000" + // 14980 - 14984 + "\u8EA2\uC6DE\u8EA2\uC6DD\u0000\uE6B6\u0000\u0000\u8EA2\uC6E1" + // 14985 - 14989 + "\u8EA2\uC6E3\u8EA2\uC6F2\u8EA2\uC7A5\u0000\uE6B9\u0000\u0000" + // 14990 - 14994 + "\u0000\uE6BA\u0000\u0000\u8EA2\uC6D5\u0000\uE6B2\u8EA2\uC6D9" // 14995 - 14999 + ; + + index2b = + "\u8EA2\uC7A1\u0000\uE6BB\u0000\u0000\u0000\uE6B3\u0000\uE6B5" + // 15000 - 15004 + "\u0000\uE6BC\u8EA2\uC7A7\u8EA2\uC6F8\u8EA2\uC6F3\u0000\u0000" + // 15005 - 15009 + "\u0000\u0000\u8EA2\uC6E0\u8EA2\uC6DF\u0000\uE6B1\u8EA2\uC6D1" + // 15010 - 15014 + "\u0000\uE6AE\u8EA2\uC6E9\u8EA2\uC6D2\u8EA2\uC6E7\u0000\u0000" + // 15015 - 15019 + "\u0000\u0000\u8EA2\uC6DC\u8EA2\uC7A2\u0000\u0000\u8EA2\uE4D6" + // 15020 - 15024 + "\u0000\u0000\u8EA2\uE7FC\u0000\u0000\u0000\uF9CF\u0000\u0000" + // 15025 - 15029 + "\u8EA2\uE7FB\u8EA2\uE7FA\u0000\uF9D0\u0000\u0000\u8EA2\uEAE6" + // 15030 - 15034 + "\u8EA2\uEAE5\u0000\u0000\u8EA2\uEAE3\u0000\u0000\u8EA2\uEAE2" + // 15035 - 15039 + "\u0000\uFAD3\u0000\uFAD2\u8EA2\uEAE7\u8EA2\uEEC5\u0000\uFCA5" + // 15040 - 15044 + "\u8EA2\uECE5\u0000\uFBC3\u8EA2\uECE4\u8EA2\uEAE4\u0000\u0000" + // 15045 - 15049 + "\u8EA2\uEEC6\u0000\uFCD0\u0000\u0000\u0000\uFDAE\u8EA2\uF1C8" + // 15050 - 15054 + "\u0000\u0000\u8EA2\uF2A1\u0000\u0000\u0000\u0000\u0000\u0000" + // 15055 - 15059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15060 - 15064 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15065 - 15069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15070 - 15074 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15075 - 15079 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15080 - 15084 + "\u0000\u0000\u0000\u0000\u0000\uCFDD\u0000\u0000\u8EA2\uA1E9" + // 15085 - 15089 + "\u0000\u0000\u8EA2\uA2F8\u0000\uC8EA\u8EA2\uA2F6\u8EA2\uA2F9" + // 15090 - 15094 + "\u8EA2\uA2F7\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA5AE" + // 15095 - 15099 + "\u0000\u0000\u0000\uCBE2\u0000\u0000\u0000\uCBE3\u8EA2\uA5B0" + // 15100 - 15104 + "\u0000\uCBE0\u8EA2\uA5AF\u8EA2\uA5AD\u0000\uCBE1\u0000\uCBDF" + // 15105 - 15109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15110 - 15114 + "\u0000\u0000\u8EA2\uA8F8\u8EA2\uA8FB\u0000\uCFE0\u8EA2\uA8FA" + // 15115 - 15119 + "\u8EA2\uA8F9\u0000\u0000\u0000\uCFDF\u8EA2\uBFC5\u0000\uE1DD" + // 15120 - 15124 + "\u8EA2\uBFE2\u8EA2\uBFEB\u8EA2\uBFEE\u0000\uE1DB\u8EA2\uBFCD" + // 15125 - 15129 + "\u8EA2\uBFE6\u8EA2\uBFCE\u8EA2\uBFDC\u0000\uE1E8\u8EA2\uBFD8" + // 15130 - 15134 + "\u0000\uE1E5\u0000\u0000\u8EA2\uBFD9\u8EA2\uBFC2\u0000\u0000" + // 15135 - 15139 + "\u8EA2\uBFE7\u0000\u0000\u0000\u0000\u0000\uE1EE\u8EA2\uBFE4" + // 15140 - 15144 + "\u8EA2\uBFDA\u8EA2\uBFF0\u8EA2\uBFD5\u8EA2\uC6ED\u0000\u0000" + // 15145 - 15149 + "\u8EA2\uBFF3\u0000\uE1EC\u8EA2\uBFD3\u8EA2\uBFDF\u0000\uE1EF" + // 15150 - 15154 + "\u0000\uE1DA\u8EA2\uBFD7\u8EA2\uBFF1\u8EA2\uBFD0\u8EA2\uBFC9" + // 15155 - 15159 + "\u8EA2\uBFD4\u0000\u0000\u8EA2\uBFC8\u8EA2\uBFC6\u0000\uE1D6" + // 15160 - 15164 + "\u8EA2\uBFE8\u8EA2\uBFCF\u8EA2\uBFEC\u0000\u0000\u8EA2\uBFED" + // 15165 - 15169 + "\u0000\uE1DE\u0000\uE1E3\u0000\uE1DF\u0000\uE1E7\u8EA2\uBFE3" + // 15170 - 15174 + "\u0000\uE1E0\u8EA2\uBFDB\u8EA2\uBFCB\u0000\u0000\u0000\uE1D8" + // 15175 - 15179 + "\u8EA2\uBFC3\u8EA2\uBFE5\u8EA2\uBFEF\u8EA2\uBFCA\u0000\uE1E6" + // 15180 - 15184 + "\u8EA2\uBFF4\u8EA2\uBFD6\u0000\uD8A1\u8EA2\uB2BF\u8EA2\uB2C3" + // 15185 - 15189 + "\u0000\uD8A4\u8EA2\uB2B9\u8EA2\uB2D1\u0000\u0000\u8EA2\uB2D0" + // 15190 - 15194 + "\u8EA2\uB2D8\u0000\uD7F7\u0000\uD7F4\u8EA2\uB2D6\u8EA2\uB2D2" + // 15195 - 15199 + "\u8EA2\uB2C9\u8EA2\uB2C4\u0000\uD7FA\u0000\uD7F6\u8EA2\uB2BB" + // 15200 - 15204 + "\u0000\uD7F2\u8EA2\uB2D3\u0000\uD7F3\u0000\u0000\u8EA2\uB2BD" + // 15205 - 15209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15210 - 15214 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15215 - 15219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15220 - 15224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15225 - 15229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15230 - 15234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB8E2\u8EA2\uB8E4" + // 15235 - 15239 + "\u8EA2\uB8E9\u8EA2\uB8FD\u0000\uDDA3\u0000\uDCF7\u0000\u0000" + // 15240 - 15244 + "\u8EA2\uB8E1\u0000\uDDA4\u0000\uDDA5\u8EA2\uB8EC\u8EA2\uB8F3" + // 15245 - 15249 + "\u8EA2\uB8F9\u8EA2\uADB3\u0000\uD3C2\u0000\uD3BD\u0000\uD3B6" + // 15250 - 15254 + "\u0000\uD3BB\u0000\uD3B7\u0000\uD3CA\u8EA2\uADB1\u8EA2\uB2C7" + // 15255 - 15259 + "\u0000\uD3BE\u0000\u0000\u0000\u0000\u8EA2\uADBD\u8EA2\uA9C2" + // 15260 - 15264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15265 - 15269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB2CA\u0000\uD7FE" + // 15270 - 15274 + "\u0000\u0000\u8EA2\uB2BA\u0000\u0000\u8EA2\uB2C5\u8EA2\uB2C1" + // 15275 - 15279 + "\u0000\u0000\u8EA2\uB2D4\u0000\u0000\u8EA2\uB2CC\u0000\u0000" + // 15280 - 15284 + "\u8EA2\uB2C2\u0000\u0000\u8EA2\uB2CB\u8EA2\uB2BC\u8EA2\uB2C0" + // 15285 - 15289 + "\u8EA2\uB2D7\u0000\uD8A3\u8EA2\uB2CF\u8EA2\uB2C6\u0000\uD7F1" + // 15290 - 15294 + "\u8EA2\uB2D5\u8EA2\uB2B8\u0000\u0000\u8EA2\uB2CE\u0000\u0000" + // 15295 - 15299 + "\u0000\uD8A2\u0000\uD7FB\u8EA2\uB2B7\u0000\uD7F9\u0000\uD7F8" + // 15300 - 15304 + "\u0000\uD7FD\u8EA2\uB2CD\u0000\uD7F5\u0000\uD7FC\u8EA2\uADB5" + // 15305 - 15309 + "\u8EA2\uBFC1\u8EA2\uB2C8\u0000\u0000\u0000\u0000\u8EA2\uB2BE" + // 15310 - 15314 + "\u8EA2\uA8D3\u0000\u0000\u8EA2\uA8E2\u8EA2\uA8E4\u0000\u0000" + // 15315 - 15319 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15320 - 15324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15325 - 15329 + "\u0000\u0000\u0000\u0000\u0000\uD3C5\u0000\uD3BF\u0000\uD3C7" + // 15330 - 15334 + "\u0000\uD3C4\u8EA2\uADB4\u8EA2\uADB7\u0000\uD3C0\u0000\u0000" + // 15335 - 15339 + "\u8EA2\uADAE\u0000\u0000\u0000\uD3B9\u0000\uD3C3\u0000\u0000" + // 15340 - 15344 + "\u0000\uD3C6\u0000\uD3C8\u8EA2\uADC3\u8EA2\uADBA\u0000\u0000" + // 15345 - 15349 + "\u0000\uD3B8\u8EA2\uADC2\u0000\uD3BC\u0000\uD3BA\u0000\uD3B5" + // 15350 - 15354 + "\u8EA2\uADB2\u0000\u0000\u8EA2\uADC1\u8EA2\uADB6\u8EA2\uADB9" + // 15355 - 15359 + "\u8EA2\uADC6\u0000\u0000\u0000\uD3C9\u8EA2\uADC0\u0000\uD3C1" + // 15360 - 15364 + "\u8EA2\uADBB\u8EA2\uADC5\u8EA2\uADB8\u8EA2\uADBC\u8EA2\uADBF" + // 15365 - 15369 + "\u0000\u0000\u0000\u0000\u8EA2\uADB0\u8EA2\uADC4\u8EA2\uADBE" + // 15370 - 15374 + "\u0000\u0000\u0000\u0000\u8EA2\uADAF\u0000\u0000\u8EA2\uE0D1" + // 15375 - 15379 + "\u0000\u0000\u8EA2\uE0D6\u8EA2\uE0D2\u8EA2\uE0D5\u0000\uF6A8" + // 15380 - 15384 + "\u0000\u0000\u8EA2\uDEC1\u8EA2\uE0D4\u8EA2\uE0D3\u0000\u0000" + // 15385 - 15389 + "\u0000\uF7EE\u0000\uF7EC\u0000\uF7EF\u0000\uF7ED\u8EA2\uE4D2" + // 15390 - 15394 + "\u8EA2\uE4D3\u8EA2\uE4D4\u0000\uF7F0\u0000\u0000\u0000\u0000" + // 15395 - 15399 + "\u0000\u0000\u8EA2\uE7F5\u0000\uF9CC\u8EA2\uE7F3\u0000\u0000" + // 15400 - 15404 + "\u0000\uF9CD\u8EA2\uE7F2\u8EA2\uE7F4\u0000\uF9CB\u8EA2\uEADF" + // 15405 - 15409 + "\u8EA2\uEADC\u0000\u0000\u8EA2\uEADD\u8EA2\uEADE\u8EA2\uECE0" + // 15410 - 15414 + "\u0000\uFBC2\u0000\u0000\u0000\uFBC1\u0000\u0000\u8EA2\uEEC3" + // 15415 - 15419 + "\u8EA2\uEEC2\u0000\uFCA4\u0000\u0000\u0000\u0000\u0000\u0000" + // 15420 - 15424 + "\u0000\uFCCF\u0000\uFCF4\u0000\uFDAD\u8EA2\uF1C7\u8EA2\uF1FC" + // 15425 - 15429 + "\u8EA2\uF1FD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15430 - 15434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15435 - 15439 + "\u0000\u0000\u0000\u0000\u0000\uA1FA\u0000\uA1F9\u0000\u0000" + // 15440 - 15444 + "\u0000\u0000\u0000\u0000\u0000\uA1F2\u0000\u0000\u0000\u0000" + // 15445 - 15449 + "\u0000\uA1F6\u0000\uA1F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 15450 - 15454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15455 - 15459 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15460 - 15464 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15465 - 15469 + "\u0000\uA3C8\u0000\uA3C9\u0000\uA3CB\u0000\uA3CA\u0000\u0000" + // 15470 - 15474 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15475 - 15479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1D2\u0000\u0000" + // 15480 - 15484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15485 - 15489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15490 - 15494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15495 - 15499 + "\u8EAD\uA4C8\u8EAD\uA4CA\u0000\u0000\u0000\u0000\u0000\u0000" + // 15500 - 15504 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15505 - 15509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15510 - 15514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15515 - 15519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15520 - 15524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15525 - 15529 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15530 - 15534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15535 - 15539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15540 - 15544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15545 - 15549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15550 - 15554 + "\u0000\u0000\u0000\uC8CF\u0000\u0000\u0000\uD2FE\u8EA2\uB0D2" + // 15555 - 15559 + "\u0000\uD7CD\u0000\u0000\u0000\u0000\u0000\uDCDF\u8EA2\uBFA2" + // 15560 - 15564 + "\u8EA2\uBFA3\u8EA2\uA2EC\u0000\u0000\u0000\u0000\u8EA2\uA5A2" + // 15565 - 15569 + "\u8EA2\uA5A3\u8EA2\uA4FB\u0000\u0000\u0000\u0000\u0000\u0000" + // 15570 - 15574 + "\u0000\u0000\u8EA2\uA5A1\u0000\uCBC2\u0000\u0000\u0000\uCBC3" + // 15575 - 15579 + "\u8EA2\uA4FC\u8EA2\uA4FA\u8EA2\uA4F9\u8EA2\uA4FD\u0000\uCBC1" + // 15580 - 15584 + "\u8EA2\uA4FE\u8EA2\uADAD\u0000\u0000\u0000\u0000\u0000\u0000" + // 15585 - 15589 + "\u8EA2\uA8D6\u0000\uCFBF\u8EA2\uA8D5\u8EA2\uA8D7\u0000\u0000" + // 15590 - 15594 + "\u0000\uCFBE\u8EA2\uA8DC\u0000\uCFC2\u8EA2\uA8D2\u8EA2\uA8E0" + // 15595 - 15599 + "\u8EA2\uA8E6\u0000\uCFC9\u8EA2\uA8E3\u0000\uCFC6\u0000\u0000" + // 15600 - 15604 + "\u8EA2\uA8D9\u8EA2\uA8DF\u8EA2\uA8E1\u0000\u0000\u8EA2\uA8D4" + // 15605 - 15609 + "\u0000\uCFC5\u0000\uCFC0\u8EA2\uA8DA\u0000\uCFC7\u0000\uCFCA" + // 15610 - 15614 + "\u0000\uCFC4\u0000\u0000\u0000\uCFBD\u8EA2\uA8DE\u8EA2\uA8D8" + // 15615 - 15619 + "\u8EA2\uA8E5\u0000\uCFCC\u0000\uCFC8\u0000\uCFC3\u8EA2\uA8DD" + // 15620 - 15624 + "\u0000\uD7F0\u8EA2\uA8DB\u0000\uCFC1\u0000\uCFCB\u0000\u0000" + // 15625 - 15629 + "\u0000\uFCA3\u0000\u0000\u0000\uFCA2\u8EA2\uEFDD\u8EA2\uEFDE" + // 15630 - 15634 + "\u0000\uFCF3\u8EA2\uF0DF\u0000\u0000\u0000\uFDB6\u0000\u0000" + // 15635 - 15639 + "\u0000\uD3F4\u0000\uE2F9\u8EA2\uCFB2\u8EA2\uCFB1\u8EA2\uD5DE" + // 15640 - 15644 + "\u8EA2\uD5DD\u0000\u0000\u0000\u0000\u0000\uF6A7\u8EA2\uE0CF" + // 15645 - 15649 + "\u8EA2\uE0CE\u8EA2\uE4CF\u8EA2\uE4CD\u8EA2\uE4D0\u8EA2\uE4CE" + // 15650 - 15654 + "\u0000\u0000\u0000\u0000\u0000\uF9C9\u8EA2\uE7EF\u8EA2\uE7F0" + // 15655 - 15659 + "\u8EA2\uE7F1\u0000\u0000\u8EA2\uECDF\u0000\u0000\u8EA2\uEEC1" + // 15660 - 15664 + "\u0000\u0000\u8EA2\uF0E0\u0000\u0000\u0000\u0000\u0000\u0000" + // 15665 - 15669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3F5" + // 15670 - 15674 + "\u0000\u0000\u0000\u0000\u8EA2\uDBE3\u8EA2\uE0D0\u0000\u0000" + // 15675 - 15679 + "\u0000\uD3F6\u0000\u0000\u0000\u0000\u0000\uEBE9\u0000\u0000" + // 15680 - 15684 + "\u8EA2\uDBE4\u0000\uF7EB\u8EA2\uE4D1\u0000\uF9CA\u0000\u0000" + // 15685 - 15689 + "\u8EA2\uEADA\u8EA2\uEADB\u0000\uFBC0\u8EA2\uC6CB\u0000\u0000" + // 15690 - 15694 + "\u8EA2\uC6CC\u0000\u0000\u8EA2\uC6CA\u8EA2\uC6CD\u0000\u0000" + // 15695 - 15699 + "\u0000\uE6AC\u0000\u0000\u8EA2\uC6C9\u0000\u0000\u0000\uEADB" + // 15700 - 15704 + "\u0000\u0000\u0000\u0000\u8EA2\uD3B3\u8EA2\uD3AF\u8EA2\uD3B2" + // 15705 - 15709 + "\u8EA2\uD3B4\u8EA2\uD3B1\u8EA2\uD3B0\u0000\u0000\u8EA2\uD9E1" + // 15710 - 15714 + "\u8EA2\uD9E2\u8EA2\uD9E3\u0000\uF1FC\u0000\uF1FD\u8EA2\uDEDE" + // 15715 - 15719 + "\u8EA2\uDEDD\u8EA2\uDEDF\u0000\u0000\u8EA2\uE2ED\u8EA2\uE2EC" + // 15720 - 15724 + "\u0000\u0000\u8EA2\uE6CF\u0000\u0000\u8EA2\uE6D0\u8EA2\uE6CE" + // 15725 - 15729 + "\u0000\u0000\u0000\uFAB0\u0000\u0000\u8EA2\uE9E2\u8EA2\uE9E3" + // 15730 - 15734 + "\u0000\u0000\u8EA2\uEDF3\u8EA2\uEFBE\u8EA2\uF0C9\u0000\uC8E2" + // 15735 - 15739 + "\u0000\uCBC0\u0000\u0000\u0000\uF5A9\u0000\uC8E3\u0000\u0000" + // 15740 - 15744 + "\u8EA2\uB8DC\u8EA2\uBFC0\u0000\u0000\u0000\uFCEC\u8EA2\uA2EA" + // 15745 - 15749 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA2EB\u8EA2\uA2ED" + // 15750 - 15754 + "\u0000\uC8E4\u8EA2\uA2EE\u0000\uD7EA\u8EA2\uB2B4\u0000\uDCEF" + // 15755 - 15759 + "\u0000\u0000\u8EA2\uBFBC\u0000\uE6AB\u0000\u0000\u0000\uEAD8" + // 15760 - 15764 + "\u0000\uF1FB\u0000\uF5A8\u0000\uF7AE\u8EA2\uE6CD\u0000\uC8DF" + // 15765 - 15769 + "\u0000\uCFBC\u0000\u0000\u0000\u0000\u0000\uD7EB\u8EA2\uB8D6" + // 15770 - 15774 + "\u0000\uE1D4\u0000\u0000\u0000\uEAD9\u8EA2\uCDB2\u8EA2\uD3AE" + // 15775 - 15779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC8E0" + // 15780 - 15784 + "\u0000\uE1D5\u8EA2\uC6C8\u0000\uEADA\u0000\uC8E1\u8EA2\uA8D1" + // 15785 - 15789 + "\u8EA2\uADAC\u0000\uD3B4\u0000\u0000\u0000\u0000\u8EA2\uB2B6" + // 15790 - 15794 + "\u0000\u0000\u0000\u0000\u0000\uD7EE\u0000\u0000\u0000\uD7EC" + // 15795 - 15799 + "\u0000\uD7ED\u0000\uD7EF\u0000\u0000\u0000\u0000\u8EA2\uB2B5" + // 15800 - 15804 + "\u0000\u0000\u0000\u0000\u8EA2\uB8DB\u8EA2\uB8D8\u8EA2\uB8DA" + // 15805 - 15809 + "\u0000\uDCF0\u0000\uDCF2\u0000\uDCF1\u8EA2\uB8D7\u0000\uDCF3" + // 15810 - 15814 + "\u8EA2\uB8D9\u0000\u0000\u8EA2\uBFBD\u8EA2\uBFBE\u0000\u0000" + // 15815 - 15819 + "\u8EA2\uBFBF\u0000\uF5A2\u0000\u0000\u0000\uF5A1\u0000\uF4FD" + // 15820 - 15824 + "\u8EA2\uDED6\u8EA2\uDED9\u0000\uF4FC\u8EA2\uDEDA\u0000\u0000" + // 15825 - 15829 + "\u0000\uF5A5\u8EA2\uDED8\u0000\u0000\u8EA2\uDED7\u0000\uF7AC" + // 15830 - 15834 + "\u0000\u0000\u0000\uF7AD\u8EA2\uE2EB\u8EA2\uE2E9\u8EA2\uE2EA" + // 15835 - 15839 + "\u0000\u0000\u0000\u0000\u8EA2\uE6CC\u0000\u0000\u8EA2\uE6CB" + // 15840 - 15844 + "\u0000\uF8E9\u8EA2\uE9E1\u0000\uFAAF\u8EA2\uE9E0\u0000\u0000" + // 15845 - 15849 + "\u8EA2\uEBFE\u8EA2\uEDF2\u0000\uFBF0\u8EA2\uF1B2\u8EA2\uF1B3" + // 15850 - 15854 + "\u0000\uFCC4\u0000\uC8DB\u0000\u0000\u0000\uCFBA\u8EA2\uBFB7" + // 15855 - 15859 + "\u0000\uEAD6\u0000\uF5A7\u8EA2\uDEDC\u0000\uC8DC\u0000\u0000" + // 15860 - 15864 + "\u0000\uD7E9\u0000\uD7E8\u8EA2\uBFB8\u0000\u0000\u0000\u0000" + // 15865 - 15869 + "\u0000\u0000\u8EA2\uD9E0\u0000\uC8DD\u0000\uD3B3\u0000\u0000" + // 15870 - 15874 + "\u0000\u0000\u8EA2\uBFB9\u8EA2\uBFBA\u8EA2\uBFBB\u0000\uEAD7" + // 15875 - 15879 + "\u0000\uF1FA\u0000\uC8DE\u0000\u0000\u0000\uCFBB\u8EA2\uADAB" + // 15880 - 15884 + "\u0000\uEAD0\u0000\u0000\u0000\uEAD5\u8EA2\uCDAD\u0000\u0000" + // 15885 - 15889 + "\u0000\u0000\u8EA2\uCDAC\u8EA2\uCDAE\u0000\uEAD2\u8EA2\uCDAB" + // 15890 - 15894 + "\u0000\uEAD3\u8EA2\uCDB1\u8EA2\uCDB0\u8EA2\uCDAF\u0000\u0000" + // 15895 - 15899 + "\u0000\uEAD1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15900 - 15904 + "\u0000\u0000\u8EA2\uD3AA\u0000\u0000\u8EA2\uD3AD\u0000\uEED1" + // 15905 - 15909 + "\u8EA2\uD3AC\u0000\uEED0\u0000\uEECC\u0000\uEECD\u0000\uEECE" + // 15910 - 15914 + "\u8EA2\uD3A9\u8EA2\uD3A8\u0000\uEECF\u0000\u0000\u8EA2\uD3AB" + // 15915 - 15919 + "\u8EA2\uD3A7\u0000\u0000\u0000\u0000\u8EA2\uD9D5\u8EA2\uD9DF" + // 15920 - 15924 + "\u0000\uF1F9\u0000\uF1F8\u0000\u0000\u8EA2\uD9D9\u8EA2\uD9DB" + // 15925 - 15929 + "\u0000\u0000\u8EA2\uD9D6\u0000\u0000\u8EA2\uD9DA\u8EA2\uD9D4" + // 15930 - 15934 + "\u8EA2\uD9DD\u0000\uF1F7\u8EA2\uD9DC\u8EA2\uD9D8\u0000\u0000" + // 15935 - 15939 + "\u8EA2\uD9DE\u0000\u0000\u8EA2\uD9D7\u0000\uF4FE\u8EA2\uDED5" + // 15940 - 15944 + "\u8EA2\uDEDB\u0000\uF5A4\u0000\uF5A6\u0000\uF5A3\u8EA2\uB2B3" + // 15945 - 15949 + "\u8EA2\uB2B1\u0000\uD7DB\u0000\u0000\u0000\u0000\u0000\uD7DD" + // 15950 - 15954 + "\u0000\uD7E0\u0000\u0000\u0000\uD7E3\u0000\u0000\u0000\uD7E5" + // 15955 - 15959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15960 - 15964 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 15965 - 15969 + "\u8EA2\uB8D2\u0000\uDCE9\u0000\u0000\u8EA2\uB8C9\u8EA2\uB8CB" + // 15970 - 15974 + "\u0000\u0000\u8EA2\uB8CC\u0000\u0000\u8EA2\uB8D4\u8EA2\uB8D0" + // 15975 - 15979 + "\u8EA2\uB8CE\u0000\u0000\u8EA2\uB8D1\u8EA2\uB8D5\u0000\uDCEA" + // 15980 - 15984 + "\u0000\uDCEE\u8EA2\uB8CA\u0000\u0000\u8EA2\uB8D3\u0000\u0000" + // 15985 - 15989 + "\u0000\uDCEC\u0000\u0000\u0000\uDCEB\u8EA2\uB8CF\u8EA2\uB8CD" + // 15990 - 15994 + "\u0000\u0000\u0000\uDCE8\u0000\uDCED\u0000\u0000\u0000\u0000" + // 15995 - 15999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16000 - 16004 + "\u0000\u0000\u0000\uE1CE\u8EA2\uBFB6\u0000\u0000\u0000\u0000" + // 16005 - 16009 + "\u8EA2\uBFB4\u0000\uE1D0\u0000\u0000\u0000\uD3E8\u0000\u0000" + // 16010 - 16014 + "\u0000\uD3E9\u0000\u0000\u8EA2\uADDC\u0000\u0000\u8EA2\uADDE" + // 16015 - 16019 + "\u8EA2\uADE0\u0000\u0000\u0000\uD3E6\u8EA2\uADDF\u0000\u0000" + // 16020 - 16024 + "\u0000\u0000\u0000\uD3E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 16025 - 16029 + "\u0000\u0000\u0000\u0000\u8EA2\uB9F1\u8EA2\uADDB\u8EA2\uB3B6" + // 16030 - 16034 + "\u8EA2\uB3BF\u8EA2\uB3BD\u8EA2\uB3B8\u8EA2\uB3B9\u8EA2\uB3BE" + // 16035 - 16039 + "\u8EA2\uB3C0\u0000\uD8D6\u0000\u0000\u8EA2\uB3BB\u8EA2\uB3B7" + // 16040 - 16044 + "\u0000\uD8D5\u0000\uD8D7\u8EA2\uB3BA\u8EA2\uB3C1\u8EA2\uB3BC" + // 16045 - 16049 + "\u0000\u0000\u0000\u0000\u0000\uDDE5\u8EA2\uB9F4\u8EA2\uB9EC" + // 16050 - 16054 + "\u8EA2\uB9F2\u8EA2\uB9F3\u0000\uDDE6\u0000\u0000\u8EA2\uB9EB" + // 16055 - 16059 + "\u8EA2\uB9ED\u8EA2\uADD8\u8EA2\uB9EF\u8EA2\uB9F0\u8EA2\uB9EE" + // 16060 - 16064 + "\u0000\uE2D3\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC0FB" + // 16065 - 16069 + "\u8EA2\uDBA2\u8EA2\uC0FC\u8EA2\uC0F9\u0000\uDDE7\u0000\uE2D5" + // 16070 - 16074 + "\u8EA2\uC0F8\u0000\uFAAE\u0000\uC8D2\u0000\u0000\u0000\uC8D3" + // 16075 - 16079 + "\u0000\uD7D3\u0000\uCFAF\u0000\uD7D2\u8EA2\uACF2\u0000\u0000" + // 16080 - 16084 + "\u0000\u0000\u0000\u0000\u0000\uE1C6\u0000\uC8D4\u0000\uD3A4" + // 16085 - 16089 + "\u8EA2\uACF3\u8EA2\uACF4\u0000\uD3A3\u0000\uD3A5\u0000\uC8D5" + // 16090 - 16094 + "\u0000\u0000\u8EA2\uACF5\u0000\uD7D5\u8EA2\uB2AA\u0000\uD7D7" + // 16095 - 16099 + "\u0000\uD7D4\u0000\uD7D6\u0000\u0000\u8EA2\uB8C5\u0000\uDCE5" + // 16100 - 16104 + "\u0000\u0000\u8EA2\uB8C4\u8EA2\uB8C3\u0000\u0000\u8EA2\uC6B8" + // 16105 - 16109 + "\u0000\u0000\u0000\u0000\u8EA2\uCDA8\u0000\u0000\u0000\uEECB" + // 16110 - 16114 + "\u0000\u0000\u0000\uF1F6\u8EA2\uD9D2\u8EA2\uD9D1\u0000\u0000" + // 16115 - 16119 + "\u8EA2\uDED4\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEBFD" + // 16120 - 16124 + "\u0000\u0000\u0000\u0000\u0000\uC8D6\u8EA2\uA4F3\u8EA2\uA8CA" + // 16125 - 16129 + "\u0000\uD3A6\u8EA2\uACF6\u0000\u0000\u8EA2\uB2AC\u0000\u0000" + // 16130 - 16134 + "\u0000\u0000\u0000\u0000\u0000\uD7D8\u8EA2\uB2AB\u0000\uD7D9" + // 16135 - 16139 + "\u8EA2\uB2A9\u0000\uD7D1\u8EA2\uB2A8\u8EA2\uB2A7\u0000\u0000" + // 16140 - 16144 + "\u0000\uD7D0\u0000\u0000\u8EA2\uB8C0\u0000\u0000\u8EA2\uB8C2" + // 16145 - 16149 + "\u8EA2\uB8BB\u8EA2\uB8BC\u0000\uDCE2\u8EA2\uB8BD\u0000\uDCE3" + // 16150 - 16154 + "\u8EA2\uB8C1\u8EA2\uB8BE\u8EA2\uB8BF\u0000\uDCE4\u0000\u0000" + // 16155 - 16159 + "\u0000\uE1C4\u0000\uE1C5\u0000\u0000\u8EA2\uBFA9\u0000\u0000" + // 16160 - 16164 + "\u0000\u0000\u0000\u0000\u8EA2\uC6B6\u8EA2\uC6B7\u0000\u0000" + // 16165 - 16169 + "\u8EA2\uCDA7\u0000\uEACB\u0000\uEAC9\u0000\uEACA\u8EA2\uCDA4" + // 16170 - 16174 + "\u8EA2\uCDA5\u0000\u0000\u8EA2\uCDA6\u8EA2\uD3A3\u0000\u0000" + // 16175 - 16179 + "\u8EA2\uD3A4\u0000\uEECA\u8EA2\uD3A1\u8EA2\uD2FE\u8EA2\uD3A2" + // 16180 - 16184 + "\u8EA2\uD2FD\u0000\uF1F5\u8EA2\uD9D0\u0000\uF1F3\u0000\uF1F4" + // 16185 - 16189 + "\u8EA2\uDED3\u0000\uF4F5\u8EA2\uDED2\u8EA2\uE1B4\u0000\u0000" + // 16190 - 16194 + "\u8EA2\uE2E6\u8EA2\uE2E7\u0000\uF7A8\u0000\u0000\u0000\uF7A9" + // 16195 - 16199 + "\u0000\uF4F6\u8EA2\uE6C8\u8EA2\uE6C9\u8EA2\uE9DE\u8EA2\uE9DC" + // 16200 - 16204 + "\u8EA2\uE9DB\u0000\uFAAC\u0000\u0000\u0000\u0000\u0000\u0000" + // 16205 - 16209 + "\u8EA2\uEBF9\u8EA2\uEBF6\u8EA2\uEBF7\u0000\u0000\u8EA2\uEBF5" + // 16210 - 16214 + "\u8EA2\uEBF8\u0000\uFAFD\u8EA2\uEBFA\u0000\u0000\u0000\uFAFC" + // 16215 - 16219 + "\u0000\u0000\u8EA2\uEDEF\u0000\u0000\u0000\uFCC1\u0000\uFCC3" + // 16220 - 16224 + "\u8EA2\uEFBD\u0000\uFCC2\u8EA2\uF0C7\u8EA2\uF1AF\u8EA2\uF1B1" + // 16225 - 16229 + "\u8EA2\uF1AE\u8EA2\uF1B0\u0000\uFDB9\u0000\u0000\u0000\u0000" + // 16230 - 16234 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16235 - 16239 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16240 - 16244 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16245 - 16249 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16250 - 16254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16255 - 16259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16260 - 16264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16265 - 16269 + "\u0000\uDEA4\u8EA2\uC1D4\u8EA2\uC8F3\u0000\u0000\u0000\uE7ED" + // 16270 - 16274 + "\u8EA2\uC8F1\u0000\u0000\u0000\u0000\u8EA2\uC8F2\u0000\u0000" + // 16275 - 16279 + "\u0000\u0000\u0000\u0000\u8EA2\uCFCA\u8EA2\uCFCB\u0000\uEBFA" + // 16280 - 16284 + "\u0000\uEBF8\u8EA2\uCFCC\u0000\uEBF9\u8EA2\uD6B3\u0000\u0000" + // 16285 - 16289 + "\u8EA2\uD6B6\u8EA2\uD6B0\u8EA2\uD6B7\u8EA2\uD6AF\u8EA2\uD6B1" + // 16290 - 16294 + "\u0000\u0000\u8EA2\uD6B2\u8EA2\uDEC5\u0000\uF4ED\u8EA2\uDEBF" + // 16295 - 16299 + "\u0000\uF4E8\u8EA2\uDECC\u0000\uF4EC\u0000\uF4E5\u8EA2\uDEC6" + // 16300 - 16304 + "\u0000\uF4F0\u8EA2\uDEC4\u0000\u0000\u0000\u0000\u8EA2\uDEC8" + // 16305 - 16309 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE2DA\u8EA2\uE2E0" + // 16310 - 16314 + "\u0000\uF7A5\u8EA2\uE2E3\u0000\uF6FE\u0000\uF7A1\u8EA2\uE2DB" + // 16315 - 16319 + "\u8EA2\uE2E2\u8EA2\uE2DD\u0000\uF7A6\u0000\uF7A3\u0000\u0000" + // 16320 - 16324 + "\u8EA2\uE2D9\u0000\u0000\u0000\uF7A2\u8EA2\uE2DF\u8EA2\uE2E1" + // 16325 - 16329 + "\u0000\uF7A4\u8EA2\uE2DE\u8EA2\uE2DC\u0000\u0000\u0000\u0000" + // 16330 - 16334 + "\u0000\u0000\u0000\u0000\u8EA2\uE6C2\u0000\uF8E2\u0000\uF8E3" + // 16335 - 16339 + "\u0000\uF8DF\u0000\u0000\u0000\uF8E0\u0000\u0000\u8EA2\uE6BE" + // 16340 - 16344 + "\u8EA2\uE6BC\u0000\u0000\u8EA2\uE6C0\u0000\uF8E4\u8EA2\uE6C1" + // 16345 - 16349 + "\u8EA2\uE6BA\u8EA2\uE6B9\u8EA2\uE6BD\u8EA2\uE6BB\u0000\uF8E1" + // 16350 - 16354 + "\u8EA2\uE6BF\u8EA2\uE9D9\u0000\uFAAB\u0000\uFAAA\u8EA2\uE9DA" + // 16355 - 16359 + "\u0000\u0000\u0000\uE7B8\u0000\uE7AD\u0000\u0000\u8EA2\uC8A7" + // 16360 - 16364 + "\u0000\u0000\u0000\u0000\u0000\uE7B2\u0000\u0000\u8EA2\uC8A8" + // 16365 - 16369 + "\u0000\uE7AB\u0000\uE7AA\u0000\u0000\u0000\uE7B5\u0000\uE7B4" + // 16370 - 16374 + "\u0000\uE7B3\u0000\uE7B1\u0000\uE7B6\u8EA2\uC8A5\u0000\uE7AC" + // 16375 - 16379 + "\u0000\uE7AE\u0000\uE7B0\u0000\u0000\u0000\u0000\u0000\uEBC0" + // 16380 - 16384 + "\u0000\uEBC3\u0000\u0000\u0000\uEBC7\u0000\uEBC1\u0000\uEBC6" + // 16385 - 16389 + "\u0000\uEBC4\u0000\u0000\u0000\uEBBF\u0000\u0000\u0000\uEBC5" + // 16390 - 16394 + "\u0000\uEBC2\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD4FA" + // 16395 - 16399 + "\u0000\uEFCD\u0000\uEFCB\u0000\u0000\u8EA2\uD4FB\u0000\u0000" + // 16400 - 16404 + "\u0000\uEFCE\u0000\uEFCC\u8EA2\uD4F9\u8EA2\uD4F8\u0000\u0000" + // 16405 - 16409 + "\u0000\uF2DA\u8EA2\uD4F7\u0000\uF2D8\u0000\uF2D7\u8EA2\uDAFC" + // 16410 - 16414 + "\u0000\uEFCF\u0000\uF2D9\u8EA2\uDAFD\u0000\uF2DC\u8EA2\uDAFE" + // 16415 - 16419 + "\u0000\uF2DB\u0000\uF5E3\u8EA2\uDFEF\u0000\uF5E2\u8EA2\uCCEF" + // 16420 - 16424 + "\u8EA2\uCCEE\u8EA2\uCCEB\u0000\u0000\u8EA2\uCCF3\u8EA2\uCCF0" + // 16425 - 16429 + "\u8EA2\uCCF4\u0000\uEAC6\u0000\u0000\u8EA2\uCDA2\u0000\uEABB" + // 16430 - 16434 + "\u8EA2\uCCF5\u8EA2\uCCF6\u0000\u0000\u8EA2\uCCF2\u0000\u0000" + // 16435 - 16439 + "\u0000\u0000\u0000\u0000\u0000\uEAC5\u0000\u0000\u0000\u0000" + // 16440 - 16444 + "\u0000\u0000\u0000\u0000\u8EA2\uD2EF\u0000\uEEBB\u0000\uEEC4" + // 16445 - 16449 + "\u0000\uEEC0\u8EA2\uD2EC\u0000\u0000\u0000\uEEBD\u0000\uEEC1" + // 16450 - 16454 + "\u8EA2\uD2F8\u0000\uEEB7\u8EA2\uD2F0\u0000\u0000\u0000\uEEBF" + // 16455 - 16459 + "\u0000\u0000\u0000\u0000\u8EA2\uD2F3\u8EA2\uD2EE\u0000\uEEBE" + // 16460 - 16464 + "\u0000\uEEC2\u8EA2\uD2ED\u0000\u0000\u0000\uEEBC\u0000\u0000" + // 16465 - 16469 + "\u8EA2\uD2F7\u0000\uEEB9\u8EA2\uD2F6\u8EA2\uD2F5\u0000\uEEC5" + // 16470 - 16474 + "\u8EA2\uD0BB\u0000\uEEB8\u0000\u0000\u8EA2\uD2F4\u8EA2\uD2EB" + // 16475 - 16479 + "\u0000\u0000\u0000\uEEC6\u8EA2\uD2F2\u0000\uEEBA\u0000\u0000" + // 16480 - 16484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1BA\u8EAD\uA1BB" + // 16485 - 16489 + "\u0000\u0000\u0000\u0000\u8EAD\uA1B8\u8EAD\uA1B9\u0000\u0000" + // 16490 - 16494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16495 - 16499 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16500 - 16504 + "\u0000\u0000\u0000\u0000\u0000\uA2D3\u0000\u0000\u0000\u0000" + // 16505 - 16509 + "\u0000\u0000\u0000\uA2D4\u0000\u0000\u0000\u0000\u0000\u0000" + // 16510 - 16514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16515 - 16519 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2C7\u0000\u0000" + // 16520 - 16524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16525 - 16529 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16530 - 16534 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16535 - 16539 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16540 - 16544 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA2CA" + // 16545 - 16549 + "\u8EA2\uC5FE\u0000\uE5F1\u0000\u0000\u8EA2\uC6A7\u8EA2\uC6AB" + // 16550 - 16554 + "\u8EA2\uC6A3\u8EA2\uC5FD\u0000\u0000\u8EA2\uC5FC\u0000\u0000" + // 16555 - 16559 + "\u0000\u0000\u0000\u0000\u8EA2\uC6A9\u8EA2\uC6A1\u8EA2\uC6A5" + // 16560 - 16564 + "\u0000\uE5F2\u0000\u0000\u0000\uE5F0\u8EA2\uC6AD\u0000\uE5EE" + // 16565 - 16569 + "\u8EA2\uC6AA\u0000\u0000\u8EA2\uCCF9\u0000\u0000\u0000\u0000" + // 16570 - 16574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAB7\u8EA2\uCCF1" + // 16575 - 16579 + "\u0000\uEEC3\u8EA2\uCCFB\u0000\uEABA\u8EA2\uCDA1\u0000\uEAC0" + // 16580 - 16584 + "\u8EA2\uCCEC\u0000\u0000\u0000\u0000\u8EA2\uCCFC\u8EA2\uCCE9" + // 16585 - 16589 + "\u0000\u0000\u8EA2\uCCFE\u8EA2\uCCED\u0000\u0000\u0000\uEAC7" + // 16590 - 16594 + "\u0000\uEAC4\u8EA2\uCCFD\u8EA2\uCCF7\u0000\uEAB6\u0000\uEABE" + // 16595 - 16599 + "\u0000\uEABD\u0000\u0000\u0000\uEABC\u0000\uEAC2\u0000\u0000" + // 16600 - 16604 + "\u8EA2\uCCEA\u0000\uEAC3\u8EA2\uCCF8\u0000\uEABF\u0000\uEAB5" + // 16605 - 16609 + "\u8EA2\uCCFA\u0000\uEAB8\u0000\uEAB9\u0000\uEAC1\u0000\uDCD7" + // 16610 - 16614 + "\u8EA2\uB8AF\u0000\uDCDC\u0000\uDCD2\u0000\uDCDA\u8EA2\uB8AC" + // 16615 - 16619 + "\u0000\uDCD1\u8EA2\uB8B0\u0000\u0000\u0000\u0000\u8EA2\uB8B3" + // 16620 - 16624 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBFA1\u8EA2\uBEF6" + // 16625 - 16629 + "\u0000\uE1B8\u8EA2\uBEFD\u8EA2\uBEFA\u8EA2\uBEF2\u8EA2\uBEFB" + // 16630 - 16634 + "\u0000\uE1BA\u8EA2\uBEF3\u0000\u0000\u8EA2\uBEEF\u0000\u0000" + // 16635 - 16639 + "\u0000\u0000\u0000\uE5F3\u8EA2\uBEF8\u0000\u0000\u0000\uE1B7" + // 16640 - 16644 + "\u8EA2\uBEFE\u0000\u0000\u0000\uE1BE\u0000\uE1C0\u8EA2\uBEF1" + // 16645 - 16649 + "\u0000\u0000\u0000\u0000\u0000\uE1BF\u8EA2\uBEF4\u0000\uE1B9" + // 16650 - 16654 + "\u8EA2\uBEFC\u8EA2\uBEF5\u8EA2\uBEF9\u0000\u0000\u8EA2\uBEF7" + // 16655 - 16659 + "\u0000\uE1BC\u8EA2\uBEF0\u0000\uE1C1\u0000\uDCD3\u0000\uE1BD" + // 16660 - 16664 + "\u0000\uE1C2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16665 - 16669 + "\u0000\u0000\u0000\uE5EF\u8EA2\uC6A4\u8EA2\uC6A6\u8EA2\uC6A8" + // 16670 - 16674 + "\u8EA2\uC6AC\u0000\u0000\u8EA2\uC6A2\u0000\uD2F9\u8EA2\uACED" + // 16675 - 16679 + "\u0000\uD2F7\u8EA2\uACEB\u0000\uD2FC\u0000\uD2F8\u0000\uD2FD" + // 16680 - 16684 + "\u0000\uD2FB\u8EA2\uACEC\u0000\uD2FA\u0000\uD7C2\u0000\uD7C1" + // 16685 - 16689 + "\u8EA2\uB1FC\u0000\uD7CA\u8EA2\uB1F5\u8EA2\uB1FB\u0000\uD7C6" + // 16690 - 16694 + "\u8EA2\uB1F4\u8EA2\uB1FA\u8EA2\uB1F8\u0000\uD7C5\u0000\uD7C7" + // 16695 - 16699 + "\u8EA2\uB1F7\u0000\uD7C0\u8EA2\uB1F6\u0000\uD7CB\u0000\uD7C8" + // 16700 - 16704 + "\u0000\uD7CC\u0000\uD7C9\u8EA2\uB1F3\u8EA2\uB1F2\u8EA2\uB1F9" + // 16705 - 16709 + "\u0000\uD7C3\u0000\uD7BF\u0000\uD7C4\u0000\u0000\u0000\u0000" + // 16710 - 16714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB8B5\u8EA2\uB8AE" + // 16715 - 16719 + "\u0000\u0000\u0000\uE1BB\u8EA2\uB8AD\u0000\u0000\u0000\uDCD4" + // 16720 - 16724 + "\u0000\uDCDB\u0000\uDCD8\u0000\uDCDE\u0000\uDCDD\u0000\uDCD9" + // 16725 - 16729 + "\u0000\u0000\u8EA2\uB8A7\u8EA2\uB8AA\u0000\u0000\u8EA2\uB8A9" + // 16730 - 16734 + "\u0000\uDCD5\u8EA2\uB8AB\u8EA2\uB8B4\u0000\uDCD6\u8EA2\uB8A8" + // 16735 - 16739 + "\u8EA2\uB8B1\u8EA2\uB8B2\u8EA2\uACE9\u8EA2\uACEA\u0000\u0000" + // 16740 - 16744 + "\u0000\u0000\u8EA2\uB1EB\u8EA2\uB1F1\u0000\u0000\u0000\u0000" + // 16745 - 16749 + "\u8EA2\uB1EF\u0000\uD7BE\u8EA2\uB1ED\u0000\u0000\u8EA2\uB1EE" + // 16750 - 16754 + "\u8EA2\uB1F0\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB1EC" + // 16755 - 16759 + "\u0000\uDCCE\u0000\u0000\u8EA2\uB8A3\u0000\uDCD0\u8EA2\uB8A5" + // 16760 - 16764 + "\u0000\uDCCF\u8EA2\uB8A4\u0000\u0000\u0000\u0000\u0000\u0000" + // 16765 - 16769 + "\u0000\u0000\u0000\u0000\u8EA2\uBEEC\u0000\uE1B5\u0000\u0000" + // 16770 - 16774 + "\u8EA2\uBEEE\u8EA2\uBEEB\u8EA2\uB8A6\u0000\u0000\u0000\uE1B6" + // 16775 - 16779 + "\u0000\u0000\u0000\u0000\u8EA2\uBEED\u0000\u0000\u0000\u0000" + // 16780 - 16784 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC5FB" + // 16785 - 16789 + "\u0000\u0000\u0000\uE5EB\u8EA2\uC5F9\u0000\uE5EC\u8EA2\uC5FA" + // 16790 - 16794 + "\u0000\uE5ED\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEAB2" + // 16795 - 16799 + "\u8EA2\uCCE8\u8EA2\uCCE5\u8EA2\uCCE7\u0000\uEAB3\u0000\uEAB4" + // 16800 - 16804 + "\u8EA2\uCCE6\u0000\uF8DE\u0000\u0000\u0000\u0000\u0000\uFAA5" + // 16805 - 16809 + "\u0000\u0000\u8EA2\uE9D7\u0000\u0000\u8EA2\uE9D6\u8EA2\uE9D4" + // 16810 - 16814 + "\u8EA2\uE9D3\u8EA2\uE9D5\u0000\u0000\u0000\uFAA4\u0000\uFAA6" + // 16815 - 16819 + "\u0000\u0000\u0000\u0000\u0000\uFAFB\u0000\u0000\u0000\u0000" + // 16820 - 16824 + "\u8EA2\uEBF3\u8EA2\uEBF2\u0000\u0000\u0000\u0000\u8EA2\uEDE8" + // 16825 - 16829 + "\u0000\u0000\u8EA2\uEDEA\u8EA2\uEDEC\u8EA2\uEDEB\u8EA2\uEDE9" + // 16830 - 16834 + "\u0000\u0000\u0000\u0000\u0000\uFBED\u0000\uFBEC\u0000\u0000" + // 16835 - 16839 + "\u0000\u0000\u0000\uFCBF\u0000\uFCBE\u0000\uFCC0\u8EA2\uEFBC" + // 16840 - 16844 + "\u8EA2\uEFBB\u0000\u0000\u8EA2\uF1AB\u8EA2\uF0C6\u8EA2\uF1AC" + // 16845 - 16849 + "\u0000\uFDA5\u0000\u0000\u0000\uFDA6\u8EA2\uF1DC\u0000\u0000" + // 16850 - 16854 + "\u0000\u0000\u0000\uFDCB\u0000\uC8CD\u0000\u0000\u8EA2\uA8C8" + // 16855 - 16859 + "\u0000\u0000\u0000\u0000\u8EA2\uACE6\u8EA2\uACE7\u8EA2\uACE5" + // 16860 - 16864 + "\u0000\u0000\u0000\u0000\u0000\uD2F6\u0000\u0000\u8EA2\uACE8" + // 16865 - 16869 + "\u8EA2\uDEAA\u8EA2\uDEB4\u8EA2\uDEB1\u8EA2\uDEB3\u0000\u0000" + // 16870 - 16874 + "\u8EA2\uDEA7\u8EA2\uDEB7\u0000\uF4D6\u0000\u0000\u8EA2\uDEB2" + // 16875 - 16879 + "\u8EA2\uDEBB\u8EA2\uDEAF\u0000\uF4DA\u0000\uF4D7\u8EA2\uDEAD" + // 16880 - 16884 + "\u8EA2\uDEA8\u8EA2\uDEBA\u0000\uF1DF\u0000\u0000\u0000\u0000" + // 16885 - 16889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 16890 - 16894 + "\u8EA2\uE2CF\u0000\u0000\u0000\u0000\u8EA2\uE2CD\u8EA2\uE2D3" + // 16895 - 16899 + "\u0000\uF6FA\u8EA2\uE2D1\u8EA2\uE2D0\u0000\uF6FC\u8EA2\uE2D6" + // 16900 - 16904 + "\u0000\uF6FB\u0000\u0000\u8EA2\uE2D7\u8EA2\uE2D4\u0000\uF6F8" + // 16905 - 16909 + "\u8EA2\uE2D5\u8EA2\uE2CE\u0000\uF6F9\u0000\uF6F7\u8EA2\uE6B5" + // 16910 - 16914 + "\u8EA2\uE2D2\u0000\u0000\u0000\u0000\u8EA2\uE2D8\u0000\u0000" + // 16915 - 16919 + "\u0000\u0000\u8EA2\uE6B2\u0000\u0000\u0000\u0000\u0000\u0000" + // 16920 - 16924 + "\u0000\uF8DD\u0000\uF8DB\u8EA2\uE6B4\u0000\u0000\u8EA2\uE6B6" + // 16925 - 16929 + "\u8EA2\uE6B3\u0000\uF8DC\u0000\uF8D9\u0000\uF8DA\u0000\uE5E7" + // 16930 - 16934 + "\u0000\uEEB3\u0000\u0000\u0000\u0000\u0000\uEEAF\u0000\u0000" + // 16935 - 16939 + "\u0000\uEEB1\u0000\uEEB2\u0000\u0000\u0000\uF1E0\u8EA2\uD2E5" + // 16940 - 16944 + "\u8EA2\uD2DF\u0000\uEEB5\u0000\u0000\u8EA2\uD2E2\u0000\u0000" + // 16945 - 16949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD9AC" + // 16950 - 16954 + "\u8EA2\uD9A7\u0000\u0000\u0000\u0000\u8EA2\uD9B1\u0000\uF1DE" + // 16955 - 16959 + "\u8EA2\uD9A9\u0000\uF1E2\u8EA2\uD9AF\u8EA2\uD9A6\u8EA2\uD9A3" + // 16960 - 16964 + "\u8EA2\uD9B2\u0000\uF4DB\u0000\uF1E3\u8EA2\uD9AE\u8EA2\uD9A4" + // 16965 - 16969 + "\u0000\uF1E1\u8EA2\uD9A8\u0000\uF1E5\u8EA2\uD9A5\u8EA2\uD9AA" + // 16970 - 16974 + "\u0000\uF1E4\u8EA2\uD9AD\u8EA2\uD9B0\u0000\u0000\u0000\u0000" + // 16975 - 16979 + "\u0000\u0000\u0000\u0000\u8EA2\uDEB8\u8EA2\uDEB9\u8EA2\uDEA9" + // 16980 - 16984 + "\u8EA2\uDEB0\u8EA2\uDEAE\u0000\u0000\u0000\u0000\u0000\uF4D9" + // 16985 - 16989 + "\u8EA2\uDEB5\u8EA2\uD9AB\u0000\u0000\u8EA2\uDEAC\u0000\u0000" + // 16990 - 16994 + "\u8EA2\uDEB6\u0000\uF4D8\u8EA2\uDEAB\u8EA2\uBEE8\u0000\u0000" + // 16995 - 16999 + "\u0000\u0000\u0000\u0000\u8EA2\uBEE4\u8EA2\uBEEA\u0000\uE1AD" + // 17000 - 17004 + "\u8EA2\uBEE3\u8EA2\uBEE5\u0000\uE1AB\u8EA2\uBEE2\u0000\uE1B2" + // 17005 - 17009 + "\u8EA2\uBEE6\u0000\uE1B1\u8EA2\uBEE7\u0000\uE1B3\u0000\uE1AE" + // 17010 - 17014 + "\u0000\uE1B4\u0000\uE1AF\u0000\u0000\u0000\uE1B0\u0000\u0000" + // 17015 - 17019 + "\u0000\uE1AC\u0000\u0000\u8EA2\uBEE9\u0000\u0000\u0000\u0000" + // 17020 - 17024 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17025 - 17029 + "\u0000\uE5E8\u8EA2\uC5F5\u0000\u0000\u8EA2\uC5F8\u8EA2\uC5EC" + // 17030 - 17034 + "\u8EA2\uC5F1\u8EA2\uC5EB\u0000\uE5EA\u0000\u0000\u8EA2\uC5EF" + // 17035 - 17039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC5ED\u0000\uE5E9" + // 17040 - 17044 + "\u0000\u0000\u8EA2\uC5F4\u8EA2\uC5F3\u8EA2\uC5F0\u8EA2\uC5F2" + // 17045 - 17049 + "\u8EA2\uC5EE\u0000\uEAAA\u8EA2\uC5F7\u0000\uE5E6\u8EA2\uC5F6" + // 17050 - 17054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17055 - 17059 + "\u0000\u0000\u0000\u0000\u0000\uC5FD\u0000\uC5FE\u0000\u0000" + // 17060 - 17064 + "\u0000\uC7CA\u8EA2\uA3B3\u8EA2\uA5D6\u8EA2\uA5D7\u0000\u0000" + // 17065 - 17069 + "\u0000\uD0B1\u8EA2\uAEB1\u0000\uD9B1\u0000\uD9B0\u8EA2\uBAC7" + // 17070 - 17074 + "\u8EA2\uBAC6\u0000\u0000\u0000\u0000\u0000\uC4B1\u0000\uC5A7" + // 17075 - 17079 + "\u0000\uC6A1\u0000\u0000\u0000\uD9B2\u8EA2\uA1A4\u0000\u0000" + // 17080 - 17084 + "\u8EA2\uA1CD\u0000\uC6A2\u0000\u0000\u8EA2\uA2A2\u0000\uC7CC" + // 17085 - 17089 + "\u0000\uC7CB\u8EA2\uA2A1\u0000\uC9BA\u0000\u0000\u0000\u0000" + // 17090 - 17094 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4CD" + // 17095 - 17099 + "\u0000\u0000\u0000\u0000\u8EA2\uB3F2\u0000\u0000\u0000\uE3BE" + // 17100 - 17104 + "\u8EA2\uC9AC\u0000\uE8A4\u0000\u0000\u0000\u0000\u8EA2\uD6C5" + // 17105 - 17109 + "\u0000\u0000\u0000\u0000\u8EA2\uE8EA\u0000\uA7B7\u0000\uC5A8" + // 17110 - 17114 + "\u0000\u0000\u0000\u0000\u8EA2\uA5D8\u8EA2\uA9B6\u0000\uD9B5" + // 17115 - 17119 + "\u0000\uD9B3\u8EA2\uACE3\u8EA2\uACE4\u0000\u0000\u0000\u0000" + // 17120 - 17124 + "\u8EA2\uB1E2\u8EA2\uB1E4\u0000\uD7BC\u0000\u0000\u8EA2\uB1E6" + // 17125 - 17129 + "\u8EA2\uB1E9\u8EA2\uB1E7\u0000\u0000\u0000\u0000\u0000\u0000" + // 17130 - 17134 + "\u8EA2\uB1E8\u8EA2\uB1E5\u8EA2\uB1E1\u0000\uD7BD\u8EA2\uB1EA" + // 17135 - 17139 + "\u8EA2\uB1E3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17140 - 17144 + "\u8EA2\uB7F6\u0000\uDCCB\u8EA2\uB8A1\u0000\uDCC8\u0000\u0000" + // 17145 - 17149 + "\u8EA2\uB7F8\u0000\uDCCC\u0000\u0000\u0000\uDCC6\u0000\u0000" + // 17150 - 17154 + "\u8EA2\uB7F3\u8EA2\uB8A2\u8EA2\uB7F4\u8EA2\uB7F1\u0000\uDCCA" + // 17155 - 17159 + "\u0000\u0000\u0000\uDCC7\u0000\u0000\u8EA2\uB7F7\u8EA2\uB7FA" + // 17160 - 17164 + "\u0000\uDCC9\u8EA2\uB7FB\u0000\uDCCD\u8EA2\uB7FC\u8EA2\uB7F2" + // 17165 - 17169 + "\u8EA2\uB7F9\u8EA2\uB7FD\u8EA2\uB7F5\u8EA2\uB7F0\u8EA2\uB7EE" + // 17170 - 17174 + "\u0000\u0000\u0000\u0000\u8EA2\uB7FE\u0000\u0000\u0000\u0000" + // 17175 - 17179 + "\u8EA2\uB7EF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17180 - 17184 + "\u8EA2\uBFAF\u8EA2\uC6C6\u0000\u0000\u0000\uE1CF\u8EA2\uBFB3" + // 17185 - 17189 + "\u0000\u0000\u0000\u0000\u8EA2\uBFB0\u0000\uE1CB\u0000\uE1D1" + // 17190 - 17194 + "\u8EA2\uBFB5\u0000\uE1CD\u8EA2\uBFB2\u0000\uEACF\u0000\uE1CC" + // 17195 - 17199 + "\u8EA2\uBFB1\u0000\uE1D2\u0000\uE1CA\u0000\uE1C9\u0000\u0000" + // 17200 - 17204 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC6BE" + // 17205 - 17209 + "\u8EA2\uC6BC\u0000\u0000\u8EA2\uC6C2\u0000\u0000\u8EA2\uC6BA" + // 17210 - 17214 + "\u8EA2\uC6C7\u8EA2\uC6BF\u0000\u0000\u8EA2\uC6B9\u0000\uE6A4" + // 17215 - 17219 + "\u0000\uE6AA\u8EA2\uC6C4\u0000\u0000\u8EA2\uC6BD\u0000\u0000" + // 17220 - 17224 + "\u0000\uE6A7\u0000\u0000\u0000\u0000\u0000\uE6A5\u8EA2\uC6C5" + // 17225 - 17229 + "\u0000\uE6A2\u0000\uE6A1\u8EA2\uC6C0\u0000\uE6A6\u0000\uE1D3" + // 17230 - 17234 + "\u0000\u0000\u8EA2\uC6C3\u8EA2\uC6BB\u0000\uE6A3\u0000\uE6A8" + // 17235 - 17239 + "\u0000\uE6A9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17240 - 17244 + "\u0000\uEAD4\u8EA2\uDEA5\u8EA2\uDEA6\u0000\u0000\u0000\u0000" + // 17245 - 17249 + "\u0000\uF6F5\u0000\uF6F6\u0000\u0000\u0000\uFAA2\u0000\u0000" + // 17250 - 17254 + "\u0000\u0000\u0000\uFCBD\u0000\uC7A1\u0000\u0000\u0000\u0000" + // 17255 - 17259 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uACE2\u0000\u0000" + // 17260 - 17264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17265 - 17269 + "\u8EA2\uB1E0\u0000\uD7BB\u0000\u0000\u0000\u0000\u0000\u0000" + // 17270 - 17274 + "\u0000\u0000\u0000\u0000\u0000\uDDFE\u0000\uDDFD\u0000\u0000" + // 17275 - 17279 + "\u0000\u0000\u0000\uE1AA\u8EA2\uBEE1\u0000\uE1A9\u8EA2\uBEE0" + // 17280 - 17284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC5EA" + // 17285 - 17289 + "\u0000\u0000\u0000\uEAA5\u8EA2\uCCD4\u0000\uEAA6\u0000\u0000" + // 17290 - 17294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17295 - 17299 + "\u0000\uFAA3\u8EA2\uE9D2\u0000\u0000\u0000\uC8CC\u0000\uCFAA" + // 17300 - 17304 + "\u8EA2\uA8C7\u0000\u0000\u0000\uD2F5\u0000\u0000\u0000\uD2F4" + // 17305 - 17309 + "\u8EA2\uACE0\u0000\uD2F3\u0000\u0000\u0000\u0000\u0000\uD7B9" + // 17310 - 17314 + "\u8EA2\uB1DB\u8EA2\uB1D9\u8EA2\uB1DF\u0000\uD7BA\u8EA2\uB1DA" + // 17315 - 17319 + "\u8EA2\uB1DE\u8EA2\uB1DC\u8EA2\uB1DD\u0000\u0000\u0000\u0000" + // 17320 - 17324 + "\u8EA2\uB7EB\u8EA2\uB7ED\u0000\u0000\u0000\uDCC4\u0000\u0000" + // 17325 - 17329 + "\u8EA2\uB7EC\u0000\uDCC5\u0000\uE1A8\u0000\uE1A7\u0000\uE1A6" + // 17330 - 17334 + "\u8EA2\uBEDF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17335 - 17339 + "\u8EA2\uC5E9\u0000\uE5E4\u0000\uE5E5\u0000\u0000\u8EA2\uC5E8" + // 17340 - 17344 + "\u8EA2\uC5E7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17345 - 17349 + "\u8EA2\uCCD1\u0000\uEAA4\u0000\uEAA3\u8EA2\uCCD2\u8EA2\uCCD3" + // 17350 - 17354 + "\u0000\u0000\u0000\uEEAC\u0000\uEEAB\u0000\u0000\u8EA2\uD9A1" + // 17355 - 17359 + "\u8EA2\uD2DB\u8EA2\uD2DD\u8EA2\uD2DC\u8EA2\uD8FE\u8EA2\uD8FC" + // 17360 - 17364 + "\u8EA2\uD9A2\u8EA2\uD8FD\u0000\u0000\u0000\uF1DD\u0000\u0000" + // 17365 - 17369 + "\u0000\u0000\u0000\u0000\u8EA2\uDEA4\u0000\uF4D5\u0000\uEEA7" + // 17370 - 17374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD8F9\u0000\u0000" + // 17375 - 17379 + "\u0000\uF1DA\u8EA2\uD8FB\u8EA2\uD8FA\u0000\u0000\u8EA2\uCCCC" + // 17380 - 17384 + "\u0000\uF1DC\u0000\uF1DB\u0000\uF1D8\u0000\uF1D9\u0000\u0000" + // 17385 - 17389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDEA2" + // 17390 - 17394 + "\u0000\u0000\u8EA2\uDDFE\u0000\uF4D4\u8EA2\uDEA1\u0000\u0000" + // 17395 - 17399 + "\u8EA2\uDEA3\u8EA2\uDDFD\u8EA2\uDDFC\u0000\u0000\u0000\u0000" + // 17400 - 17404 + "\u8EA2\uE2CC\u0000\uF6F4\u0000\uF6F2\u0000\uF6F3\u0000\u0000" + // 17405 - 17409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE6B0\u8EA2\uE6B1" + // 17410 - 17414 + "\u0000\uF8D8\u0000\u0000\u0000\uF8D7\u8EA2\uE9D0\u8EA2\uE9D1" + // 17415 - 17419 + "\u8EA2\uE9CF\u0000\u0000\u8EA2\uEDE6\u8EA2\uEDE7\u0000\u0000" + // 17420 - 17424 + "\u0000\u0000\u0000\uC6FE\u8EA2\uA2E8\u0000\uCBB6\u0000\u0000" + // 17425 - 17429 + "\u8EA2\uA8C5\u0000\uCFA9\u0000\uCFA8\u8EA2\uA8C6\u0000\u0000" + // 17430 - 17434 + "\u0000\u0000\u8EA2\uACE1\u0000\uD2F2\u0000\uE1A5\u0000\u0000" + // 17435 - 17439 + "\u8EA2\uBEDA\u8EA2\uBEDC\u8EA2\uBEDE\u0000\uE1A4\u0000\u0000" + // 17440 - 17444 + "\u0000\u0000\u0000\uE1A2\u0000\u0000\u8EA2\uBEDB\u0000\uE1A3" + // 17445 - 17449 + "\u8EA2\uBEDD\u0000\uE1A1\u0000\u0000\u0000\u0000\u8EA2\uC5E6" + // 17450 - 17454 + "\u8EA2\uC5DD\u8EA2\uC5E0\u8EA2\uC5E4\u0000\uE5E1\u8EA2\uC5E2" + // 17455 - 17459 + "\u0000\u0000\u8EA2\uC5E1\u8EA2\uC5DE\u8EA2\uC5DF\u0000\uE5DF" + // 17460 - 17464 + "\u8EA2\uC5E5\u0000\uE5DE\u0000\u0000\u0000\uE5E3\u0000\uE5E2" + // 17465 - 17469 + "\u0000\uE5E0\u0000\u0000\u8EA2\uC5E3\u0000\u0000\u0000\u0000" + // 17470 - 17474 + "\u0000\u0000\u8EA2\uCCD0\u0000\u0000\u8EA2\uCCCF\u0000\u0000" + // 17475 - 17479 + "\u0000\u0000\u8EA2\uCCCB\u0000\u0000\u0000\u0000\u0000\uEAA1" + // 17480 - 17484 + "\u8EA2\uCCCE\u8EA2\uCCCD\u0000\uEAA2\u0000\u0000\u0000\u0000" + // 17485 - 17489 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEEA9\u0000\u0000" + // 17490 - 17494 + "\u8EA2\uD2DA\u0000\u0000\u0000\uEEAA\u0000\uEEA6\u0000\uEEA8" + // 17495 - 17499 + "\u0000\u0000\u0000\uEEA5\u0000\uCBB4\u0000\uCBB3\u0000\u0000" + // 17500 - 17504 + "\u0000\u0000\u0000\u0000\u8EA2\uA8C4\u0000\u0000\u0000\u0000" + // 17505 - 17509 + "\u0000\uCFA7\u0000\uCFA6\u0000\u0000\u0000\uD2F1\u0000\u0000" + // 17510 - 17514 + "\u8EA2\uACDC\u8EA2\uACDF\u8EA2\uACDD\u0000\u0000\u0000\uD2EF" + // 17515 - 17519 + "\u0000\uD2F0\u0000\u0000\u0000\u0000\u8EA2\uACDB\u8EA2\uACDE" + // 17520 - 17524 + "\u0000\u0000\u0000\uD7B8\u0000\u0000\u0000\u0000\u0000\u0000" + // 17525 - 17529 + "\u8EA2\uB1D6\u8EA2\uB1D8\u8EA2\uB1D7\u0000\uD7B5\u8EA2\uB1D2" + // 17530 - 17534 + "\u0000\u0000\u0000\u0000\u0000\uD7B3\u0000\uD7B2\u0000\u0000" + // 17535 - 17539 + "\u0000\uD7B6\u0000\uD7B4\u0000\u0000\u0000\uD7B7\u8EA2\uB1D5" + // 17540 - 17544 + "\u8EA2\uB1D0\u8EA2\uB1D1\u8EA2\uB1D4\u8EA2\uB1D3\u0000\u0000" + // 17545 - 17549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17550 - 17554 + "\u0000\u0000\u8EA2\uB7E9\u8EA2\uB7EA\u8EA2\uB7E8\u0000\u0000" + // 17555 - 17559 + "\u8EA2\uB7E7\u0000\uDCC3\u0000\u0000\u0000\u0000\u0000\u0000" + // 17560 - 17564 + "\u0000\u0000\u8EA2\uB8C7\u0000\u0000\u0000\u0000\u0000\uDCE7" + // 17565 - 17569 + "\u8EA2\uB8C6\u8EA2\uB8C8\u0000\u0000\u0000\uDCE6\u0000\u0000" + // 17570 - 17574 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBFAB\u8EA2\uBFAC" + // 17575 - 17579 + "\u8EA2\uBFAA\u0000\uE1C7\u0000\u0000\u0000\u0000\u0000\u0000" + // 17580 - 17584 + "\u0000\uE5FB\u0000\u0000\u0000\uE5FC\u0000\u0000\u0000\uEACD" + // 17585 - 17589 + "\u0000\u0000\u8EA2\uCDAA\u8EA2\uCDA9\u0000\uEACC\u0000\u0000" + // 17590 - 17594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD3A5" + // 17595 - 17599 + "\u0000\u0000\u0000\u0000\u8EA2\uD3A6\u0000\u0000\u0000\u0000" + // 17600 - 17604 + "\u0000\u0000\u0000\u0000\u8EA2\uD9D3\u0000\u0000\u0000\u0000" + // 17605 - 17609 + "\u0000\uF4FA\u0000\uF4F9\u0000\uF4F7\u0000\uF4F8\u0000\uF4FB" + // 17610 - 17614 + "\u0000\u0000\u8EA2\uE2E8\u0000\uF7AB\u0000\uF7AA\u8EA2\uE6CA" + // 17615 - 17619 + "\u8EA2\uE9DF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFBEF" + // 17620 - 17624 + "\u0000\uFBEE\u0000\uC8D7\u0000\uCFA4\u0000\uCFA5\u8EA2\uA8C2" + // 17625 - 17629 + "\u0000\u0000\u8EA2\uACDA\u8EA2\uACD9\u0000\uD2E9\u0000\uD2EC" + // 17630 - 17634 + "\u0000\uD2EB\u0000\uD2EA\u8EA2\uACD6\u8EA2\uACD8\u8EA2\uACD7" + // 17635 - 17639 + "\u0000\u0000\u0000\u0000\u8EA2\uB1CB\u0000\uD7AA\u8EA2\uB1CF" + // 17640 - 17644 + "\u8EA2\uB1CE\u8EA2\uB1CD\u8EA2\uB1C9\u0000\uD7A9\u0000\uD7AD" + // 17645 - 17649 + "\u0000\uD7B0\u0000\u0000\u0000\u0000\u0000\uD7B1\u8EA2\uB1CA" + // 17650 - 17654 + "\u8EA2\uB1CC\u0000\uD7AF\u0000\uD7AE\u0000\uD7AC\u0000\uD7AB" + // 17655 - 17659 + "\u8EA2\uB7E5\u0000\u0000\u8EA2\uB7E3\u8EA2\uB7DF\u0000\uDCC0" + // 17660 - 17664 + "\u0000\u0000\u8EA2\uB7E0\u0000\uDCC1\u8EA2\uB7E1\u8EA2\uB7E2" + // 17665 - 17669 + "\u8EA2\uB7E4\u0000\u0000\u0000\uDCC2\u0000\u0000\u0000\u0000" + // 17670 - 17674 + "\u8EA2\uBED9\u0000\u0000\u8EA2\uBED8\u8EA2\uBED7\u8EA2\uBED6" + // 17675 - 17679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC5DC" + // 17680 - 17684 + "\u0000\uE5D9\u0000\u0000\u8EA2\uC5D9\u8EA2\uC5DB\u0000\u0000" + // 17685 - 17689 + "\u0000\uE5DA\u8EA2\uC5D6\u0000\u0000\u0000\u0000\u8EA2\uC5D3" + // 17690 - 17694 + "\u8EA2\uC5CE\u8EA2\uC5D0\u8EA2\uC5D1\u8EA2\uC5CA\u0000\u0000" + // 17695 - 17699 + "\u0000\uE5D4\u0000\u0000\u0000\u0000\u0000\uE5D3\u0000\u0000" + // 17700 - 17704 + "\u0000\uE5CF\u8EA2\uC5CD\u0000\u0000\u0000\uE5D6\u0000\u0000" + // 17705 - 17709 + "\u0000\uE5D7\u8EA2\uC5CC\u8EA2\uC5CF\u8EA2\uC5D7\u0000\uE5D1" + // 17710 - 17714 + "\u0000\uE5D2\u8EA2\uC5D5\u8EA2\uC5CB\u0000\u0000\u0000\u0000" + // 17715 - 17719 + "\u0000\u0000\u8EA2\uCCBA\u0000\uE9F7\u8EA2\uCCBC\u8EA2\uC5D2" + // 17720 - 17724 + "\u8EA2\uCCBE\u0000\uE9FB\u8EA2\uCCBF\u8EA2\uCCBB\u0000\u0000" + // 17725 - 17729 + "\u0000\uE9F8\u8EA2\uCCB7\u0000\uE9FA\u8EA2\uCCB4\u8EA2\uCCB9" + // 17730 - 17734 + "\u8EA2\uCCBD\u8EA2\uCCB6\u0000\u0000\u0000\u0000\u0000\uE5D0" + // 17735 - 17739 + "\u0000\u0000\u8EA2\uCCB3\u0000\uE9F9\u8EA2\uCCB5\u0000\u0000" + // 17740 - 17744 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDFD" + // 17745 - 17749 + "\u8EA2\uD2C9\u0000\uEEA3\u0000\u0000\u0000\uEEA1\u0000\u0000" + // 17750 - 17754 + "\u0000\uFAC7\u0000\uFAC9\u0000\u0000\u8EA2\uEAA7\u0000\uFAC8" + // 17755 - 17759 + "\u8EA2\uEAA5\u0000\uF9A9\u8EA2\uEAA6\u0000\uFAC6\u0000\uFBAF" + // 17760 - 17764 + "\u0000\uFBB1\u8EA2\uECC5\u0000\uFBB0\u8EA2\uECC4\u0000\u0000" + // 17765 - 17769 + "\u8EA2\uEEB0\u0000\uFBF7\u8EA2\uEEAF\u0000\uFBF8\u8EA2\uEEAD" + // 17770 - 17774 + "\u8EA2\uEEAC\u8EA2\uEEB1\u8EA2\uEEB2\u8EA2\uEFD4\u0000\u0000" + // 17775 - 17779 + "\u8EA2\uEEAE\u0000\u0000\u0000\u0000\u8EA2\uF0D4\u8EA2\uF0D1" + // 17780 - 17784 + "\u8EA2\uF0D2\u8EA2\uF0D3\u0000\uFDA9\u0000\u0000\u8EA2\uF1C0" + // 17785 - 17789 + "\u8EA2\uF1BF\u8EA2\uF1BE\u8EA2\uF1DF\u0000\u0000\u8EA2\uF2AE" + // 17790 - 17794 + "\u8EA2\uF1F9\u0000\uFDBB\u0000\uCBCE\u0000\uD8C5\u0000\u0000" + // 17795 - 17799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE7A3" + // 17800 - 17804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 17805 - 17809 + "\u0000\u0000\u0000\u0000\u0000\uEFC1\u0000\u0000\u0000\u0000" + // 17810 - 17814 + "\u8EA2\uDAF4\u0000\u0000\u0000\u0000\u0000\uF0C9\u8EA2\uD6D8" + // 17815 - 17819 + "\u0000\u0000\u0000\u0000\u0000\uF0CC\u8EA2\uD6DA\u0000\uF0CB" + // 17820 - 17824 + "\u8EA2\uD6D3\u8EA2\uD6D5\u0000\u0000\u0000\u0000\u0000\u0000" + // 17825 - 17829 + "\u0000\u0000\u0000\uF3CE\u8EA2\uDCD8\u0000\u0000\u0000\uF3CD" + // 17830 - 17834 + "\u8EA2\uDCD9\u0000\uF3CC\u8EA2\uDCD7\u0000\uF3CB\u8EA2\uDCD6" + // 17835 - 17839 + "\u0000\u0000\u0000\uF6BF\u0000\uF6BE\u8EA2\uE5C2\u8EA2\uE5C4" + // 17840 - 17844 + "\u0000\u0000\u8EA2\uE5C3\u0000\uF8AE\u0000\uF8AF\u0000\u0000" + // 17845 - 17849 + "\u0000\u0000\u0000\uF8B0\u8EA2\uE8EE\u0000\uF9E6\u0000\u0000" + // 17850 - 17854 + "\u0000\u0000\u8EA2\uEFA2\u8EA2\uEFA1\u0000\uFCDE\u0000\u0000" + // 17855 - 17859 + "\u0000\uC4C9\u0000\uC5B1\u0000\u0000\u0000\u0000\u0000\uC9E7" + // 17860 - 17864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA9E0" + // 17865 - 17869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEE2" + // 17870 - 17874 + "\u0000\uDEE3\u0000\u0000\u8EA2\uC2B9\u0000\uE8C0\u8EA2\uC9D0" + // 17875 - 17879 + "\u8EA2\uCFFA\u8EA2\uCCAF\u8EA2\uCCAA\u8EA2\uCCAD\u8EA2\uCCB2" + // 17880 - 17884 + "\u0000\uE9F2\u8EA2\uCCAC\u0000\u0000\u0000\uEDF7\u8EA2\uD2C7" + // 17885 - 17889 + "\u8EA2\uD2C6\u0000\u0000\u0000\uEDFA\u0000\uEDF8\u8EA2\uD2C4" + // 17890 - 17894 + "\u0000\uEDF6\u8EA2\uD2C5\u0000\u0000\u0000\uEDF9\u0000\u0000" + // 17895 - 17899 + "\u0000\u0000\u0000\u0000\u8EA2\uD8E9\u0000\u0000\u8EA2\uD8EB" + // 17900 - 17904 + "\u0000\u0000\u8EA2\uD8EA\u8EA2\uD8E2\u8EA2\uD8E6\u8EA2\uD8E5" + // 17905 - 17909 + "\u8EA2\uD8E3\u0000\uF1D0\u0000\uF1D1\u0000\uF1CF\u8EA2\uD8E4" + // 17910 - 17914 + "\u8EA2\uD8E7\u8EA2\uD8E8\u0000\u0000\u0000\uF1D2\u0000\u0000" + // 17915 - 17919 + "\u0000\uF4CA\u8EA2\uDDED\u0000\u0000\u0000\uF4C7\u8EA2\uDDE7" + // 17920 - 17924 + "\u0000\uF4C9\u0000\uF4CB\u0000\u0000\u0000\u0000\u0000\uF4C8" + // 17925 - 17929 + "\u8EA2\uDDEC\u8EA2\uDDE8\u0000\uF4C6\u8EA2\uDDEB\u8EA2\uDDE6" + // 17930 - 17934 + "\u8EA2\uDDEA\u8EA2\uDDE9\u0000\u0000\u0000\u0000\u8EA2\uE2C2" + // 17935 - 17939 + "\u0000\uF6EE\u0000\uF6EF\u0000\uF6EC\u0000\u0000\u0000\uF6ED" + // 17940 - 17944 + "\u0000\uE5B7\u0000\uE5B9\u0000\u0000\u8EA2\uC5AF\u0000\u0000" + // 17945 - 17949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCCA3\u0000\uE9EE" + // 17950 - 17954 + "\u8EA2\uCCA7\u0000\uE9ED\u8EA2\uCCA4\u0000\uE9EC\u0000\u0000" + // 17955 - 17959 + "\u8EA2\uC5AE\u8EA2\uC5B5\u8EA2\uCCA6\u0000\u0000\u0000\uE9EF" + // 17960 - 17964 + "\u8EA2\uCCA8\u8EA2\uCCA5\u8EA2\uCCA2\u0000\u0000\u0000\u0000" + // 17965 - 17969 + "\u8EA2\uD2B9\u8EA2\uD2BD\u8EA2\uD2BF\u8EA2\uD2BB\u8EA2\uD2BA" + // 17970 - 17974 + "\u8EA2\uD2B8\u0000\uEDEE\u0000\uEDEC\u0000\uEDF1\u0000\uEDF2" + // 17975 - 17979 + "\u8EA2\uD2BC\u0000\uEDEF\u8EA2\uD2B7\u0000\uEDF0\u0000\uE9EB" + // 17980 - 17984 + "\u8EA2\uD2BE\u0000\uEDED\u0000\u0000\u0000\u0000\u0000\u0000" + // 17985 - 17989 + "\u8EA2\uD8D8\u0000\u0000\u8EA2\uD8D7\u8EA2\uD8DF\u8EA2\uD8D9" + // 17990 - 17994 + "\u8EA2\uD8DE\u8EA2\uD8DB\u0000\uF1CA\u8EA2\uD8DD\u0000\u0000" + // 17995 - 17999 + "\u0000\u0000\u0000\uF1CB\u0000\u0000\u0000\uF1CC\u0000\u0000" + // 18000 - 18004 + "\u8EA2\uD8DC\u8EA2\uD8DA\u0000\u0000\u0000\u0000\u8EA2\uAECA" + // 18005 - 18009 + "\u8EA2\uAECB\u0000\uD4E7\u0000\uD4E8\u0000\u0000\u0000\u0000" + // 18010 - 18014 + "\u8EA2\uB4AF\u0000\uD9CE\u0000\uD9D0\u8EA2\uB4AE\u0000\uD9CF" + // 18015 - 18019 + "\u8EA2\uBAE0\u0000\uDED8\u0000\u0000\u0000\u0000\u0000\u0000" + // 18020 - 18024 + "\u0000\u0000\u0000\uE3D0\u0000\uE3D1\u8EA2\uC2A8\u0000\u0000" + // 18025 - 18029 + "\u0000\uE8B7\u0000\u0000\u0000\uE8B6\u0000\u0000\u8EA2\uCFF0" + // 18030 - 18034 + "\u8EA2\uD6D2\u8EA2\uD6D1\u0000\u0000\u8EA2\uF1DA\u0000\uC4C8" + // 18035 - 18039 + "\u8EA2\uA1B9\u0000\u0000\u8EA2\uA1CF\u8EA2\uA1D0\u0000\u0000" + // 18040 - 18044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7E3\u0000\uC7E7" + // 18045 - 18049 + "\u8EA2\uA2A9\u0000\u0000\u0000\uC7E5\u0000\uC7E4\u8EA2\uA2A8" + // 18050 - 18054 + "\u0000\uC7E6\u0000\uC7E2\u0000\u0000\u0000\u0000\u0000\uC7E1" + // 18055 - 18059 + "\u8EA2\uA2AA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18060 - 18064 + "\u0000\u0000\u0000\u0000\u0000\uC9E6\u0000\u0000\u0000\u0000" + // 18065 - 18069 + "\u0000\uC9E3\u0000\u0000\u8EA2\uB9DD\u8EA2\uB9DA\u0000\u0000" + // 18070 - 18074 + "\u0000\u0000\u8EA2\uC0E1\u0000\uE2C7\u8EA2\uC0D9\u8EA2\uC0DF" + // 18075 - 18079 + "\u0000\u0000\u0000\u0000\u0000\uE2C2\u0000\uE2C5\u8EA2\uC0D8" + // 18080 - 18084 + "\u0000\uE2C0\u8EA2\uC0DC\u8EA2\uC7F2\u0000\uE2C4\u0000\u0000" + // 18085 - 18089 + "\u8EA2\uC0D7\u0000\u0000\u8EA2\uC0DD\u8EA2\uC0DA\u8EA2\uC0E0" + // 18090 - 18094 + "\u8EA2\uC0D6\u8EA2\uC0DE\u0000\uE2C3\u0000\uE2C6\u8EA2\uC0DB" + // 18095 - 18099 + "\u0000\uE2C1\u0000\u0000\u0000\uE6F9\u8EA2\uC7EE\u0000\uE6F8" + // 18100 - 18104 + "\u8EA2\uC7F5\u8EA2\uC7F4\u0000\uE7A1\u0000\u0000\u0000\uE7A2" + // 18105 - 18109 + "\u8EA2\uC7F6\u0000\uE6FA\u8EA2\uC7F3\u0000\uE6FE\u8EA2\uC7F8" + // 18110 - 18114 + "\u8EA2\uC7EF\u0000\u0000\u8EA2\uC7F1\u0000\uE6FB\u8EA2\uC7ED" + // 18115 - 18119 + "\u8EA2\uC7F0\u8EA2\uC7F7\u0000\uE6FC\u8EA2\uC7F9\u0000\u0000" + // 18120 - 18124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE6FD" + // 18125 - 18129 + "\u0000\u0000\u0000\uEBB9\u8EA2\uCEC3\u8EA2\uCEC9\u8EA2\uCEC1" + // 18130 - 18134 + "\u8EA2\uB1AA\u8EA2\uB1A7\u0000\uD6F0\u0000\u0000\u8EA2\uB1A9" + // 18135 - 18139 + "\u0000\uD6E9\u0000\u0000\u0000\uD6EA\u0000\u0000\u0000\u0000" + // 18140 - 18144 + "\u0000\uDCB1\u8EA2\uB7C4\u8EA2\uB7C5\u0000\uDCB2\u8EA2\uB7C1" + // 18145 - 18149 + "\u8EA2\uB7C3\u8EA2\uB7C7\u8EA2\uB7C6\u8EA2\uB7C2\u0000\u0000" + // 18150 - 18154 + "\u0000\uDCAE\u0000\uDCAF\u0000\u0000\u8EA2\uBEBA\u0000\uE0F1" + // 18155 - 18159 + "\u0000\uE0F0\u8EA2\uBEB4\u0000\uE0EE\u0000\u0000\u8EA2\uBEB7" + // 18160 - 18164 + "\u0000\uE0F2\u8EA2\uBEB8\u0000\uE0F3\u8EA2\uBEB5\u0000\uE0ED" + // 18165 - 18169 + "\u0000\uE0EF\u8EA2\uBEB9\u0000\u0000\u8EA2\uBEB6\u8EA2\uBEB3" + // 18170 - 18174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18175 - 18179 + "\u8EA2\uC5B8\u0000\u0000\u8EA2\uC5AD\u0000\uE5B8\u0000\uE5BB" + // 18180 - 18184 + "\u0000\uE5BA\u0000\uE5BF\u0000\uE5BE\u8EA2\uC5B9\u8EA2\uC5B7" + // 18185 - 18189 + "\u8EA2\uC5B0\u8EA2\uC5B4\u8EA2\uC5B3\u0000\uE5BC\u8EA2\uC5B6" + // 18190 - 18194 + "\u8EA2\uC5B2\u8EA2\uC5BA\u8EA2\uC5B1\u0000\uE5BD\u8EA2\uD8D6" + // 18195 - 18199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDDDF\u0000\u0000" + // 18200 - 18204 + "\u0000\uF8D1\u0000\uF8D0\u0000\u0000\u0000\u0000\u0000\uFBE8" + // 18205 - 18209 + "\u0000\uC6F4\u8EA2\uA8B5\u0000\u0000\u0000\u0000\u0000\uDCAD" + // 18210 - 18214 + "\u8EA2\uCCA1\u0000\uE9EA\u0000\uA8A9\u0000\u0000\u8EA2\uA4EF" + // 18215 - 18219 + "\u8EA2\uA4F0\u0000\u0000\u0000\u0000\u8EA2\uA8B6\u0000\uCEF9" + // 18220 - 18224 + "\u0000\uCEFA\u0000\u0000\u0000\u0000\u0000\uCEF8\u0000\u0000" + // 18225 - 18229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD2D2\u0000\uD2D3" + // 18230 - 18234 + "\u0000\uD2D0\u0000\uD2D1\u0000\u0000\u8EA2\uACBE\u0000\u0000" + // 18235 - 18239 + "\u0000\u0000\u8EA2\uACBF\u0000\uD2CF\u0000\u0000\u0000\u0000" + // 18240 - 18244 + "\u0000\u0000\u0000\u0000\u8EA2\uB1A6\u0000\u0000\u0000\uD6EB" + // 18245 - 18249 + "\u0000\uD6EC\u0000\u0000\u0000\uDCB0\u8EA2\uB1AC\u0000\u0000" + // 18250 - 18254 + "\u0000\uD6F1\u0000\uD6EF\u8EA2\uB1AD\u8EA2\uB1A8\u0000\uD6EE" + // 18255 - 18259 + "\u0000\uD6ED\u0000\uD6E8\u8EA2\uB1AB\u8EA2\uA8B3\u0000\u0000" + // 18260 - 18264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18265 - 18269 + "\u8EA2\uACBC\u8EA2\uACBD\u0000\u0000\u0000\u0000\u0000\uD2CE" + // 18270 - 18274 + "\u0000\uD2CC\u0000\u0000\u0000\uD2CD\u0000\uD2CB\u0000\u0000" + // 18275 - 18279 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6E3\u0000\u0000" + // 18280 - 18284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6E7\u0000\uD6E6" + // 18285 - 18289 + "\u8EA2\uB1A4\u0000\uD6E5\u0000\uD6E4\u0000\u0000\u8EA2\uB1A5" + // 18290 - 18294 + "\u0000\u0000\u0000\u0000\u0000\uDCAB\u8EA2\uB7C0\u8EA2\uB7BF" + // 18295 - 18299 + "\u0000\uDCA9\u0000\uDCAA\u0000\u0000\u0000\u0000\u0000\u0000" + // 18300 - 18304 + "\u0000\uE0EC\u0000\uE0EB\u8EA2\uBEB2\u0000\u0000\u0000\u0000" + // 18305 - 18309 + "\u8EA2\uBEB1\u0000\uDCAC\u0000\u0000\u0000\u0000\u0000\u0000" + // 18310 - 18314 + "\u0000\u0000\u0000\u0000\u0000\uE5B5\u8EA2\uC5AB\u0000\uE5B6" + // 18315 - 18319 + "\u8EA2\uC5AA\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCBFE" + // 18320 - 18324 + "\u8EA2\uD2B6\u0000\uEDEB\u8EA2\uCBFB\u0000\u0000\u8EA2\uCBFC" + // 18325 - 18329 + "\u8EA2\uCBFD\u0000\uE9E9\u0000\u0000\u0000\u0000\u8EA2\uD2B5" + // 18330 - 18334 + "\u8EA2\uD2B4\u0000\u0000\u0000\u0000\u8EA2\uD8D5\u0000\uF1C8" + // 18335 - 18339 + "\u0000\uF1C9\u0000\u0000\u8EA2\uDDDE\u8EA2\uDDDB\u8EA2\uDDDC" + // 18340 - 18344 + "\u8EA2\uDDDD\u8EA2\uE2B6\u8EA2\uE2B5\u0000\uF6E8\u8EA2\uE6A3" + // 18345 - 18349 + "\u8EA2\uEBEC\u0000\uC6EC\u0000\u0000\u0000\uD2C9\u0000\u0000" + // 18350 - 18354 + "\u0000\uDCA7\u8EA2\uC5A9\u0000\u0000\u0000\uC6ED\u0000\u0000" + // 18355 - 18359 + "\u8EA2\uB1A3\u0000\uDCA8\u0000\u0000\u0000\u0000\u0000\uE0E9" + // 18360 - 18364 + "\u0000\uE0EA\u0000\u0000\u0000\uC6EE\u0000\uC6EF\u8EA2\uA2E6" + // 18365 - 18369 + "\u0000\uCBAD\u0000\uCBAC\u0000\uD2CA\u8EA2\uACBB\u8EA2\uBEB0" + // 18370 - 18374 + "\u0000\uC6F0\u0000\uC6F1\u0000\uC6F2\u0000\uC6F3\u0000\u0000" + // 18375 - 18379 + "\u0000\u0000\u0000\u0000\u0000\uCBAE\u0000\uCBAF\u8EA2\uA4EE" + // 18380 - 18384 + "\u8EA2\uA4ED\u0000\u0000\u0000\u0000\u0000\uCEF7\u8EA2\uA8B4" + // 18385 - 18389 + "\u8EA2\uA8B2\u8EA2\uE2AE\u8EA2\uE2B0\u8EA2\uE2B4\u8EA2\uE6A2" + // 18390 - 18394 + "\u0000\u0000\u8EA2\uE5FE\u0000\u0000\u0000\u0000\u0000\u0000" + // 18395 - 18399 + "\u0000\u0000\u0000\uF8CE\u8EA2\uE5FD\u0000\u0000\u0000\u0000" + // 18400 - 18404 + "\u0000\u0000\u0000\uF9F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 18405 - 18409 + "\u0000\u0000\u0000\uFAF8\u8EA2\uEDDF\u0000\uFAF7\u8EA2\uEDE1" + // 18410 - 18414 + "\u8EA2\uEDDE\u8EA2\uEDE0\u0000\uFCBC\u8EA2\uF0C0\u0000\uC6EA" + // 18415 - 18419 + "\u8EA2\uA8B0\u8EA2\uB0FD\u8EA2\uB0FE\u0000\uDCA4\u8EA2\uC5A5" + // 18420 - 18424 + "\u0000\uF1C7\u0000\uF8CF\u0000\uFBE7\u8EA2\uF0C1\u0000\uC6EB" + // 18425 - 18429 + "\u0000\u0000\u8EA2\uA8B1\u0000\uA3A2\u0000\u0000\u0000\u0000" + // 18430 - 18434 + "\u8EA2\uACB9\u0000\u0000\u8EA2\uACBA\u0000\u0000\u0000\u0000" + // 18435 - 18439 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB1A1\u8EA2\uB1A2" + // 18440 - 18444 + "\u0000\uDCA5\u0000\uDCA6\u0000\u0000\u0000\u0000\u0000\u0000" + // 18445 - 18449 + "\u8EA2\uBEAF\u0000\u0000\u8EA2\uC5A8\u8EA2\uC5A7\u8EA2\uC5A6" + // 18450 - 18454 + "\u0000\uEDEA\u8EA2\uD2AE\u0000\u0000\u0000\uEDE8\u0000\u0000" + // 18455 - 18459 + "\u8EA2\uD2B0\u8EA2\uD2AD\u8EA2\uD2AA\u8EA2\uD2B1\u8EA2\uD2AB" + // 18460 - 18464 + "\u8EA2\uD2AC\u0000\uEDE7\u0000\u0000\u0000\u0000\u0000\u0000" + // 18465 - 18469 + "\u0000\u0000\u8EA2\uDDD5\u0000\u0000\u8EA2\uD8D2\u0000\u0000" + // 18470 - 18474 + "\u8EA2\uD8D1\u8EA2\uD8D3\u0000\u0000\u8EA2\uDDD2\u0000\uF1C4" + // 18475 - 18479 + "\u0000\u0000\u8EA2\uD8CF\u0000\u0000\u0000\uF1C2\u0000\u0000" + // 18480 - 18484 + "\u0000\uF1C6\u0000\uF1C5\u8EA2\uD8D0\u8EA2\uD8D4\u0000\u0000" + // 18485 - 18489 + "\u0000\uF1C3\u0000\u0000\u8EA2\uDDD9\u0000\uF4C0\u0000\uF6E6" + // 18490 - 18494 + "\u0000\uF4C1\u0000\uF4BE\u8EA2\uDDD6\u8EA2\uDDD4\u0000\u0000" + // 18495 - 18499 + "\u8EA2\uDDD7\u0000\u0000\u8EA2\uDDDA\u0000\uF4BF\u8EA2\uDDD8" + // 18500 - 18504 + "\u8EA2\uDDD3\u0000\u0000\u0000\u0000\u8EA2\uE2AF\u8EA2\uE2B2" + // 18505 - 18509 + "\u8EA2\uE6A1\u8EA2\uE2AD\u0000\u0000\u8EA2\uE9BE\u8EA2\uE2B3" + // 18510 - 18514 + "\u0000\u0000\u0000\uF8CD\u8EA2\uE2B1\u0000\uF6E7\u8EA2\uC4FB" + // 18515 - 18519 + "\u0000\uE5B0\u8EA2\uC5A1\u0000\u0000\u8EA2\uC4F5\u0000\u0000" + // 18520 - 18524 + "\u8EA2\uC5A2\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC4F6" + // 18525 - 18529 + "\u8EA2\uC4F7\u0000\u0000\u8EA2\uC5A3\u8EA2\uC4FE\u8EA2\uC4FC" + // 18530 - 18534 + "\u8EA2\uC4FD\u8EA2\uC4F9\u8EA2\uC4F8\u0000\u0000\u8EA2\uC5A4" + // 18535 - 18539 + "\u0000\uE5AD\u0000\u0000\u8EA2\uC4FA\u0000\u0000\u0000\uE5B2" + // 18540 - 18544 + "\u0000\uE5AC\u0000\uE5B3\u0000\uE5B4\u0000\u0000\u0000\uE5AF" + // 18545 - 18549 + "\u0000\uE5AE\u0000\u0000\u0000\u0000\u8EA2\uCBF4\u0000\uE9E5" + // 18550 - 18554 + "\u0000\uE9E4\u0000\u0000\u0000\u0000\u8EA2\uCBF9\u0000\u0000" + // 18555 - 18559 + "\u0000\uEDE6\u0000\uE9E6\u0000\u0000\u0000\u0000\u0000\uE9E8" + // 18560 - 18564 + "\u8EA2\uCBFA\u0000\uE5AB\u0000\uE9E7\u8EA2\uCBF6\u8EA2\uCBF8" + // 18565 - 18569 + "\u8EA2\uCBF5\u0000\u0000\u8EA2\uCBF7\u0000\u0000\u0000\u0000" + // 18570 - 18574 + "\u0000\u0000\u8EA2\uD2B3\u0000\u0000\u0000\u0000\u8EA2\uD2B2" + // 18575 - 18579 + "\u8EA2\uD2AF\u0000\uEDE9\u8EA2\uD8CE\u8EA2\uB7B7\u8EA2\uB7B4" + // 18580 - 18584 + "\u0000\u0000\u0000\uDBFE\u8EA2\uB7B3\u0000\uDBFC\u0000\uDCA1" + // 18585 - 18589 + "\u8EA2\uB7B6\u8EA2\uB7BE\u0000\uD6DE\u0000\uDBFD\u8EA2\uB7BC" + // 18590 - 18594 + "\u8EA2\uB7BB\u0000\uDCA3\u0000\u0000\u0000\u0000\u0000\u0000" + // 18595 - 18599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18600 - 18604 + "\u8EA2\uBEA4\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBEA5" + // 18605 - 18609 + "\u0000\uE0E6\u0000\u0000\u8EA2\uBEAB\u0000\u0000\u0000\u0000" + // 18610 - 18614 + "\u8EA2\uBEAD\u8EA2\uBEA6\u0000\uE0E0\u8EA2\uBEAA\u8EA2\uBEA9" + // 18615 - 18619 + "\u0000\uE0E1\u0000\uE0E7\u0000\u0000\u0000\uE0E8\u8EA2\uBEAC" + // 18620 - 18624 + "\u0000\uE0DE\u8EA2\uBEA3\u8EA2\uBEA1\u8EA2\uBEA7\u8EA2\uBDFE" + // 18625 - 18629 + "\u0000\uE0E5\u8EA2\uBEA2\u8EA2\uBEA8\u8EA2\uBEAE\u0000\uE0DF" + // 18630 - 18634 + "\u0000\uE0E4\u0000\uE0E2\u0000\uE0E3\u0000\u0000\u0000\u0000" + // 18635 - 18639 + "\u0000\u0000\u0000\uE0DD\u0000\u0000\u0000\u0000\u0000\u0000" + // 18640 - 18644 + "\u0000\u0000\u0000\uE5B1\u0000\uD2C7\u0000\u0000\u8EA2\uACAA" + // 18645 - 18649 + "\u8EA2\uACB5\u0000\u0000\u8EA2\uACAC\u8EA2\uACB6\u8EA2\uACB3" + // 18650 - 18654 + "\u8EA2\uACAB\u0000\u0000\u0000\uD2C3\u8EA2\uACB8\u8EA2\uACA9" + // 18655 - 18659 + "\u0000\uD2C6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18660 - 18664 + "\u8EA2\uB0F5\u8EA2\uB0EC\u8EA2\uB0F7\u0000\u0000\u8EA2\uB0EF" + // 18665 - 18669 + "\u8EA2\uB0FA\u8EA2\uB0FB\u8EA2\uB0ED\u8EA2\uB0F9\u8EA2\uB0F6" + // 18670 - 18674 + "\u8EA2\uB0F4\u8EA2\uB0F8\u0000\uD6E2\u0000\u0000\u0000\uD6E0" + // 18675 - 18679 + "\u0000\u0000\u0000\u0000\u8EA2\uB0F2\u0000\u0000\u8EA2\uB0EE" + // 18680 - 18684 + "\u0000\u0000\u8EA2\uB0F1\u8EA2\uB0FC\u8EA2\uB0F3\u0000\uD6E1" + // 18685 - 18689 + "\u8EA2\uACB1\u0000\u0000\u0000\uD6DD\u0000\uD6DF\u0000\u0000" + // 18690 - 18694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB7BD" + // 18695 - 18699 + "\u8EA2\uB7B2\u8EA2\uB7B0\u0000\u0000\u8EA2\uB7B1\u0000\u0000" + // 18700 - 18704 + "\u8EA2\uB7B8\u0000\u0000\u8EA2\uB7B9\u8EA2\uB7B5\u0000\uDCA2" + // 18705 - 18709 + "\u8EA2\uB7BA\u0000\uFBE6\u8EA2\uEFB7\u8EA2\uEFB6\u8EA2\uEFB8" + // 18710 - 18714 + "\u0000\uC6E8\u8EA2\uACA8\u0000\uD6DC\u0000\uDBFB\u8EA2\uB7AF" + // 18715 - 18719 + "\u0000\uC6E9\u8EA2\uA1E6\u0000\uC5DE\u0000\u0000\u0000\u0000" + // 18720 - 18724 + "\u8EA2\uA2E5\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA4EC" + // 18725 - 18729 + "\u8EA2\uA4EA\u8EA2\uA4EB\u8EA2\uA4E8\u0000\uCBAB\u8EA2\uA4E9" + // 18730 - 18734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18735 - 18739 + "\u8EA2\uA8AF\u0000\u0000\u0000\uCEF4\u8EA2\uA8AD\u8EA2\uA8A9" + // 18740 - 18744 + "\u8EA2\uA8AC\u0000\u0000\u8EA2\uA8A8\u0000\uCEF6\u8EA2\uA8AB" + // 18745 - 18749 + "\u0000\u0000\u0000\uCEF3\u0000\uCEF2\u0000\u0000\u0000\uCEF5" + // 18750 - 18754 + "\u8EA2\uA8AE\u8EA2\uA8AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 18755 - 18759 + "\u0000\u0000\u0000\uD2C5\u0000\uD2C8\u8EA2\uACB0\u8EA2\uACAF" + // 18760 - 18764 + "\u8EA2\uACAE\u0000\uD2C2\u8EA2\uACB7\u8EA2\uACAD\u0000\u0000" + // 18765 - 18769 + "\u0000\uD2C4\u8EA2\uB0F0\u0000\u0000\u8EA2\uACB4\u8EA2\uACB2" + // 18770 - 18774 + "\u8EA2\uC4F2\u0000\u0000\u8EA2\uC4EF\u8EA2\uCBF0\u0000\uE9E2" + // 18775 - 18779 + "\u0000\uE5A8\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC4F4" + // 18780 - 18784 + "\u8EA2\uC4F3\u0000\u0000\u8EA2\uCBF3\u8EA2\uCBF1\u0000\uEDE4" + // 18785 - 18789 + "\u0000\u0000\u0000\uE9E3\u8EA2\uCBF2\u8EA2\uD1FE\u0000\u0000" + // 18790 - 18794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEDE5\u8EA2\uD1FD" + // 18795 - 18799 + "\u8EA2\uD2A8\u8EA2\uD2A7\u8EA2\uD2A5\u0000\u0000\u8EA2\uD2A4" + // 18800 - 18804 + "\u8EA2\uD2A1\u8EA2\uD2A2\u8EA2\uD2A3\u8EA2\uD2A6\u8EA2\uD2A9" + // 18805 - 18809 + "\u0000\u0000\u0000\u0000\u8EA2\uD8CB\u8EA2\uD8C8\u8EA2\uD8C9" + // 18810 - 18814 + "\u0000\uF1C1\u8EA2\uD8C7\u8EA2\uD8CD\u8EA2\uD8CC\u8EA2\uD8CA" + // 18815 - 18819 + "\u0000\u0000\u8EA2\uDDD0\u8EA2\uDDD1\u0000\uF4BC\u0000\u0000" + // 18820 - 18824 + "\u0000\uF4BD\u8EA2\uDDCF\u0000\u0000\u0000\uF6E5\u8EA2\uE2AC" + // 18825 - 18829 + "\u0000\uF6E4\u0000\uF8CB\u0000\u0000\u0000\uF8CC\u0000\uF9F8" + // 18830 - 18834 + "\u8EA2\uE9BD\u8EA2\uE9BC\u8EA2\uEBEB\u8EA2\uEDDD\u8EA2\uB0E4" + // 18835 - 18839 + "\u8EA2\uB0EB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 18840 - 18844 + "\u0000\u0000\u8EA2\uB7A7\u8EA2\uB7AB\u0000\u0000\u8EA2\uB7AA" + // 18845 - 18849 + "\u8EA2\uBDF2\u8EA2\uBDFD\u0000\u0000\u0000\u0000\u8EA2\uB7AD" + // 18850 - 18854 + "\u0000\u0000\u8EA2\uB7A8\u8EA2\uBDF1\u0000\uDBF9\u0000\u0000" + // 18855 - 18859 + "\u0000\u0000\u0000\uDBF8\u8EA2\uB7A6\u8EA2\uB7A9\u0000\uDBFA" + // 18860 - 18864 + "\u0000\u0000\u0000\uDBF7\u0000\uDBF6\u8EA2\uB7A5\u8EA2\uB7AE" + // 18865 - 18869 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBDF4\u8EA2\uBDFB" + // 18870 - 18874 + "\u0000\u0000\u0000\uE0DA\u8EA2\uBDFA\u8EA2\uBDF7\u0000\u0000" + // 18875 - 18879 + "\u0000\uE0DC\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBDF9" + // 18880 - 18884 + "\u0000\u0000\u0000\u0000\u8EA2\uBDF3\u8EA2\uBDF5\u8EA2\uBDF8" + // 18885 - 18889 + "\u8EA2\uBDF6\u0000\uE0DB\u8EA2\uBDFC\u0000\uE0D9\u0000\uE5A7" + // 18890 - 18894 + "\u0000\u0000\u0000\u0000\u8EA2\uC4F1\u8EA2\uC4F0\u8EA2\uC4EE" + // 18895 - 18899 + "\u0000\u0000\u0000\uE5AA\u0000\uE5A9\u0000\uCEED\u8EA2\uA4E5" + // 18900 - 18904 + "\u0000\uCBAA\u8EA2\uA4E3\u0000\uCBA9\u8EA2\uA4E7\u8EA2\uA4E4" + // 18905 - 18909 + "\u0000\u0000\u0000\u0000\u8EA2\uA8A1\u8EA2\uABFE\u8EA2\uA7FD" + // 18910 - 18914 + "\u8EA2\uA8A6\u0000\u0000\u0000\uCEEE\u0000\u0000\u0000\uCEF1" + // 18915 - 18919 + "\u8EA2\uA8A7\u8EA2\uA8A3\u0000\u0000\u8EA2\uA8A4\u0000\u0000" + // 18920 - 18924 + "\u8EA2\uA7FC\u0000\uCEF0\u8EA2\uA7FE\u0000\uCEEF\u8EA2\uA8A5" + // 18925 - 18929 + "\u0000\u0000\u8EA2\uA8A2\u0000\u0000\u0000\u0000\u8EA2\uACA4" + // 18930 - 18934 + "\u0000\uD2C0\u0000\uD2C1\u0000\u0000\u8EA2\uACA7\u8EA2\uACA1" + // 18935 - 18939 + "\u0000\u0000\u8EA2\uACA6\u0000\u0000\u8EA2\uACA2\u0000\uD2BF" + // 18940 - 18944 + "\u8EA2\uACA5\u8EA2\uACA3\u0000\u0000\u0000\u0000\u0000\u0000" + // 18945 - 18949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB0E9" + // 18950 - 18954 + "\u8EA2\uB0E6\u0000\u0000\u8EA2\uB0E8\u0000\uD6DB\u0000\uD6DA" + // 18955 - 18959 + "\u0000\uD6D8\u8EA2\uB0E5\u8EA2\uB0EA\u0000\uD6D7\u0000\uD6D9" + // 18960 - 18964 + "\u8EA2\uB0E7\u8EA2\uB7AC\u0000\uE0D8\u0000\uDBF5\u0000\u0000" + // 18965 - 18969 + "\u0000\u0000\u0000\uE0D7\u8EA2\uBDEF\u8EA2\uBDEE\u0000\u0000" + // 18970 - 18974 + "\u8EA2\uBDEC\u8EA2\uBDED\u0000\u0000\u8EA2\uBDF0\u8EA2\uC4EA" + // 18975 - 18979 + "\u8EA2\uC4E9\u8EA2\uC4ED\u0000\u0000\u8EA2\uC4EC\u8EA2\uC4EB" + // 18980 - 18984 + "\u0000\uE9E0\u8EA2\uCBEF\u0000\u0000\u8EA2\uCBEE\u0000\uE9E1" + // 18985 - 18989 + "\u8EA2\uCBED\u8EA2\uD1FB\u0000\u0000\u8EA2\uD1FC\u0000\uEDE3" + // 18990 - 18994 + "\u0000\u0000\u8EA2\uD8C5\u8EA2\uD8C6\u0000\u0000\u0000\u0000" + // 18995 - 18999 + "\u8EA2\uE5FC\u0000\uF8CA\u8EA2\uE5FB\u8EA2\uE5FA\u8EA2\uE5F8" + // 19000 - 19004 + "\u8EA2\uE5F9\u0000\uFAF6\u8EA2\uE9BB\u8EA2\uEDDC\u8EA2\uF1A7" + // 19005 - 19009 + "\u0000\u0000\u0000\uC5DD\u0000\u0000\u8EA2\uA1E4\u0000\uC6E7" + // 19010 - 19014 + "\u8EA2\uA1E5\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA2E3" + // 19015 - 19019 + "\u8EA2\uA2E4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19020 - 19024 + "\u8EA2\uA4E6\u0000\u0000\u0000\u0000\u8EA2\uA4E2\u0000\u0000" + // 19025 - 19029 + "\u8EA2\uA4E1\u8EA2\uD8B8\u8EA2\uD8B9\u8EA2\uD8B4\u0000\uF1BF" + // 19030 - 19034 + "\u0000\uF1C0\u8EA2\uD8B3\u8EA2\uD8C2\u8EA2\uD8BD\u0000\uF1B9" + // 19035 - 19039 + "\u0000\uF1B6\u8EA2\uD8BC\u8EA2\uD8BA\u0000\u0000\u0000\u0000" + // 19040 - 19044 + "\u0000\uF1BC\u8EA2\uD8BE\u0000\uF1B7\u0000\u0000\u0000\uF1B8" + // 19045 - 19049 + "\u0000\u0000\u8EA2\uD8BB\u0000\uF1BA\u8EA2\uD8B7\u0000\u0000" + // 19050 - 19054 + "\u8EA2\uD8C0\u0000\uF1BD\u8EA2\uD8C3\u8EA2\uD8C4\u0000\uF1BE" + // 19055 - 19059 + "\u0000\u0000\u0000\u0000\u0000\uF4B2\u0000\uF4B9\u8EA2\uDDC8" + // 19060 - 19064 + "\u8EA2\uDDCE\u0000\u0000\u8EA2\uDDCC\u0000\uF4B5\u0000\uF4B4" + // 19065 - 19069 + "\u0000\uF4B1\u8EA2\uDDCA\u0000\u0000\u0000\u0000\u0000\u0000" + // 19070 - 19074 + "\u0000\uF4B7\u0000\uF4B6\u0000\uF4B3\u0000\u0000\u8EA2\uDDCD" + // 19075 - 19079 + "\u8EA2\uDDC9\u8EA2\uDDCB\u0000\u0000\u0000\uF4B8\u0000\u0000" + // 19080 - 19084 + "\u0000\u0000\u0000\u0000\u0000\uF6E3\u8EA2\uE2A9\u0000\u0000" + // 19085 - 19089 + "\u0000\uF6E0\u0000\uF6E1\u8EA2\uE2AB\u0000\uF6E2\u8EA2\uE2A8" + // 19090 - 19094 + "\u8EA2\uCBE3\u8EA2\uCBE9\u8EA2\uCBE5\u0000\u0000\u0000\uE9DD" + // 19095 - 19099 + "\u8EA2\uCBE4\u8EA2\uCBE8\u8EA2\uCBE0\u0000\u0000\u8EA2\uCBE2" + // 19100 - 19104 + "\u0000\uE9DC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19105 - 19109 + "\u8EA2\uCBE6\u8EA2\uCBE1\u0000\u0000\u0000\uE9DE\u0000\u0000" + // 19110 - 19114 + "\u0000\uE9D9\u0000\u0000\u0000\u0000\u8EA2\uCBEA\u0000\u0000" + // 19115 - 19119 + "\u0000\uE9DA\u8EA2\uD1EF\u8EA2\uD1EC\u8EA2\uD1F8\u8EA2\uD1F2" + // 19120 - 19124 + "\u8EA2\uD1F4\u0000\uEDDE\u8EA2\uD1EE\u8EA2\uD1F6\u0000\u0000" + // 19125 - 19129 + "\u0000\u0000\u8EA2\uD1F5\u8EA2\uD1F3\u0000\u0000\u8EA2\uD1F9" + // 19130 - 19134 + "\u0000\uEDE1\u8EA2\uD1F0\u8EA2\uD1F7\u0000\u0000\u0000\uEDDF" + // 19135 - 19139 + "\u0000\u0000\u0000\u0000\u8EA2\uD1EB\u8EA2\uD1ED\u0000\uEDE0" + // 19140 - 19144 + "\u8EA2\uD1EA\u8EA2\uD1FA\u0000\u0000\u8EA2\uD1F1\u0000\u0000" + // 19145 - 19149 + "\u0000\u0000\u8EA2\uD8B6\u0000\uF1BB\u0000\u0000\u0000\u0000" + // 19150 - 19154 + "\u8EA2\uD8C1\u8EA2\uD8BF\u0000\uF1B5\u8EA2\uD8B5\u8EA2\uB6F9" + // 19155 - 19159 + "\u0000\u0000\u8EA2\uB6FD\u0000\u0000\u8EA2\uB6F2\u0000\u0000" + // 19160 - 19164 + "\u8EA2\uB6F7\u0000\u0000\u0000\u0000\u0000\uDBEF\u0000\uDBF0" + // 19165 - 19169 + "\u8EA2\uB6FC\u8EA2\uB6F0\u8EA2\uB6EC\u8EA2\uB6FE\u0000\u0000" + // 19170 - 19174 + "\u8EA2\uB6F4\u0000\u0000\u0000\u0000\u8EA2\uB6F8\u0000\u0000" + // 19175 - 19179 + "\u0000\u0000\u0000\u0000\u8EA2\uB6EE\u0000\u0000\u0000\uE0CE" + // 19180 - 19184 + "\u0000\uE0CF\u8EA2\uBDE9\u0000\uE0D5\u0000\u0000\u8EA2\uBDDD" + // 19185 - 19189 + "\u8EA2\uBDE6\u8EA2\uBDDC\u0000\uE0D2\u8EA2\uBDE4\u8EA2\uBDE2" + // 19190 - 19194 + "\u0000\u0000\u8EA2\uBDE3\u0000\uE0D0\u0000\u0000\u8EA2\uBDE7" + // 19195 - 19199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19200 - 19204 + "\u8EA2\uBDE0\u8EA2\uBDDE\u0000\uE0D1\u8EA2\uBDE1\u8EA2\uBDE5" + // 19205 - 19209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0D3\u0000\u0000" + // 19210 - 19214 + "\u0000\u0000\u0000\u0000\u8EA2\uBDE8\u0000\u0000\u0000\u0000" + // 19215 - 19219 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBEC0\u8EA2\uBEC2" + // 19220 - 19224 + "\u8EA2\uBEBE\u8EA2\uBEBF\u0000\u0000\u0000\u0000\u8EA2\uBEC3" + // 19225 - 19229 + "\u8EA2\uBEC5\u8EA2\uBEC6\u8EA2\uBEC1\u8EA2\uBEC4\u0000\uE0FA" + // 19230 - 19234 + "\u0000\u0000\u0000\u0000\u8EA2\uC5C1\u0000\u0000\u8EA2\uC5C6" + // 19235 - 19239 + "\u8EA2\uC5BE\u8EA2\uC5C2\u0000\u0000\u0000\u0000\u8EA2\uC5C7" + // 19240 - 19244 + "\u8EA2\uC5C3\u0000\uE5C2\u0000\uE5CA\u0000\u0000\u0000\uE5C5" + // 19245 - 19249 + "\u8EA2\uC5BF\u8EA2\uC5C0\u0000\uE9F5\u0000\uE5CD\u0000\uE5C6" + // 19250 - 19254 + "\u0000\u0000\u0000\uE5CB\u0000\uE5C4\u8EA2\uC5C5\u0000\uE5CC" + // 19255 - 19259 + "\u8EA2\uC5C4\u0000\uE5C8\u0000\uE5C3\u0000\uE5C9\u8EA2\uC5C8" + // 19260 - 19264 + "\u8EA2\uCCAE\u8EA2\uCCB0\u0000\u0000\u0000\u0000\u0000\u0000" + // 19265 - 19269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19270 - 19274 + "\u0000\u0000\u0000\uE5C7\u0000\u0000\u0000\u0000\u8EA2\uCCAB" + // 19275 - 19279 + "\u0000\uE9F3\u8EA2\uCCB1\u0000\uE9F4\u8EA2\uE9B5\u8EA2\uE9B8" + // 19280 - 19284 + "\u8EA2\uE9AF\u8EA2\uEBE1\u8EA2\uEBE2\u8EA2\uEBE6\u8EA2\uEBE7" + // 19285 - 19289 + "\u0000\u0000\u8EA2\uEBE4\u8EA2\uEBE5\u8EA2\uEBE3\u0000\u0000" + // 19290 - 19294 + "\u0000\uFAF4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19295 - 19299 + "\u0000\uFBE4\u8EA2\uEDD9\u0000\u0000\u0000\u0000\u8EA2\uEDD6" + // 19300 - 19304 + "\u8EA2\uEDD7\u8EA2\uEDD8\u0000\uFBE5\u0000\u0000\u8EA2\uEFB4" + // 19305 - 19309 + "\u8EA2\uEFB3\u0000\u0000\u8EA2\uF0BD\u0000\uFCE0\u8EA2\uF0BB" + // 19310 - 19314 + "\u8EA2\uF0BE\u8EA2\uF0BC\u8EA2\uF1A5\u0000\uFDA4\u0000\uFDB2" + // 19315 - 19319 + "\u8EA2\uF1F1\u8EA2\uF1DB\u0000\u0000\u8EA2\uF1F0\u8EA2\uF2BE" + // 19320 - 19324 + "\u8EA2\uF2BF\u0000\uC5D6\u0000\u0000\u0000\u0000\u0000\u0000" + // 19325 - 19329 + "\u0000\u0000\u0000\uC8C8\u8EA2\uA2E1\u0000\u0000\u0000\u0000" + // 19330 - 19334 + "\u8EA2\uA4DE\u0000\u0000\u0000\uCBA2\u0000\u0000\u0000\uCBA5" + // 19335 - 19339 + "\u0000\u0000\u8EA2\uA4DF\u0000\u0000\u0000\uCBA3\u0000\uCBA4" + // 19340 - 19344 + "\u0000\u0000\u0000\u0000\u0000\uC7D4\u0000\u0000\u0000\uC7DA" + // 19345 - 19349 + "\u0000\uC7D6\u0000\u0000\u0000\uC7DC\u8EA2\uA2A5\u0000\uC7D9" + // 19350 - 19354 + "\u0000\uC7CF\u0000\uC7D2\u0000\uC7D5\u0000\uC7D1\u0000\uC7D8" + // 19355 - 19359 + "\u0000\uC7DB\u0000\uC7D0\u0000\uC7D3\u0000\uC7D7\u0000\uC7DD" + // 19360 - 19364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19365 - 19369 + "\u8EA2\uA3C0\u8EA2\uA3BD\u0000\u0000\u0000\uC9C9\u8EA2\uA3BE" + // 19370 - 19374 + "\u0000\uC9BD\u0000\uC9BF\u0000\uC9D7\u0000\uC9D2\u0000\u0000" + // 19375 - 19379 + "\u0000\u0000\u0000\u0000\u8EA2\uA3C5\u8EA2\uA3BF\u0000\uC9C1" + // 19380 - 19384 + "\u0000\uC9C3\u8EA2\uA3C4\u0000\uC9CA\u8EA2\uA3BA\u0000\uC9D6" + // 19385 - 19389 + "\u0000\uC9D8\u0000\uC9BE\u0000\uC9CF\u0000\u0000\u8EA2\uA3B8" + // 19390 - 19394 + "\u0000\uC9D5\u0000\u0000\u0000\uC9C6\u0000\u0000\u0000\uC9D0" + // 19395 - 19399 + "\u0000\uC9D1\u8EA2\uA3B9\u0000\uC9CE\u0000\uC9CC\u0000\u0000" + // 19400 - 19404 + "\u0000\uC9CD\u0000\uC9D3\u8EA2\uA3C1\u0000\uC9C0\u0000\u0000" + // 19405 - 19409 + "\u0000\uF5D0\u8EA2\uE3D9\u8EA2\uE7B0\u0000\u0000\u8EA2\uEFD2" + // 19410 - 19414 + "\u0000\uCBC8\u8EA2\uB3A2\u0000\uD8BF\u0000\uDDC7\u8EA2\uC7DA" + // 19415 - 19419 + "\u8EA2\uC7D9\u0000\uEFA7\u8EA2\uD4CF\u0000\uEFA8\u8EA2\uDFDD" + // 19420 - 19424 + "\u0000\uF7C2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uFDC3" + // 19425 - 19429 + "\u0000\uCBC9\u8EA2\uA8EB\u8EA2\uB3A3\u0000\u0000\u0000\u0000" + // 19430 - 19434 + "\u0000\uDDC8\u0000\u0000\u8EA2\uB9D2\u8EA2\uB9D3\u0000\u0000" + // 19435 - 19439 + "\u8EA2\uC0CB\u0000\u0000\u0000\uE2AF\u0000\uE6ED\u0000\u0000" + // 19440 - 19444 + "\u8EA2\uC7DC\u8EA2\uC7DB\u8EA2\uC7DD\u0000\u0000\u8EA2\uCEB9" + // 19445 - 19449 + "\u8EA2\uCEBA\u0000\uEBB0\u0000\uF2C7\u0000\uEFA9\u0000\uF2C8" + // 19450 - 19454 + "\u0000\u0000\u8EA2\uDFE1\u8EA2\uDFDE\u8EA2\uDFE0\u8EA2\uDFDF" + // 19455 - 19459 + "\u0000\uF5D2\u0000\u0000\u8EA2\uE3DA\u8EA2\uE7B2\u8EA2\uE7B1" + // 19460 - 19464 + "\u8EA2\uA5A4\u0000\uD8C1\u0000\uD8C0\u8EA2\uB3A4\u0000\u0000" + // 19465 - 19469 + "\u8EA2\uB9D4\u0000\u0000\u0000\u0000\u8EA2\uAEDA\u0000\uD4E9" + // 19470 - 19474 + "\u0000\uD4EC\u0000\u0000\u0000\u0000\u8EA2\uAED1\u8EA2\uAED7" + // 19475 - 19479 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4EB\u8EA2\uAECC" + // 19480 - 19484 + "\u0000\u0000\u0000\uD0D2\u8EA2\uB4BD\u8EA2\uAED8\u0000\u0000" + // 19485 - 19489 + "\u8EA2\uAECF\u0000\u0000\u0000\uD4EA\u8EA2\uAECE\u0000\u0000" + // 19490 - 19494 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19495 - 19499 + "\u8EA2\uB4B4\u0000\u0000\u0000\u0000\u0000\uD9D1\u0000\uD9D5" + // 19500 - 19504 + "\u8EA2\uB4C4\u8EA2\uB4B2\u8EA2\uB4C0\u0000\uD9D6\u8EA2\uB4C2" + // 19505 - 19509 + "\u0000\u0000\u8EA2\uB4C8\u0000\u0000\u8EA2\uB4CC\u0000\u0000" + // 19510 - 19514 + "\u0000\u0000\u8EA2\uB4C3\u8EA2\uB4B7\u8EA2\uB4BF\u0000\u0000" + // 19515 - 19519 + "\u8EA2\uB4CD\u8EA2\uB4CB\u8EA2\uB4C1\u8EA2\uB4BC\u8EA2\uB4B5" + // 19520 - 19524 + "\u8EA2\uBAE8\u8EA2\uB4B3\u0000\uD9DA\u8EA2\uB4BA\u0000\uD9DB" + // 19525 - 19529 + "\u0000\uD9D7\u8EA2\uB4B0\u8EA2\uB4C6\u8EA2\uB4B8\u0000\u0000" + // 19530 - 19534 + "\u0000\u0000\u0000\u0001\u0000\u0002\u0000\u0003\u0000\u0004" + // 19535 - 19539 + "\u0000\u0005\u0000\u0006\u0000\u0007\u0000\u0008\u0000\u0009" + // 19540 - 19544 + "\u0000\n\u0000\u000B\u0000\u000C\u0000\r\u0000\u000E" + // 19545 - 19549 + "\u0000\u000F\u0000\u0010\u0000\u0011\u0000\u0012\u0000\u0013" + // 19550 - 19554 + "\u0000\u0014\u0000\u0015\u0000\u0016\u0000\u0017\u0000\u0018" + // 19555 - 19559 + "\u0000\u0019\u0000\u001A\u0000\u001B\u0000\u001C\u0000\u001D" + // 19560 - 19564 + "\u0000\u001E\u0000\u001F\u0000\u0020\u0000\u0021\u0000\"" + // 19565 - 19569 + "\u0000\u0023\u0000\u0024\u0000\u0025\u0000\u0026\u0000\u0027" + // 19570 - 19574 + "\u0000\u0028\u0000\u0029\u0000\u002A\u0000\u002B\u0000\u002C" + // 19575 - 19579 + "\u0000\u002D\u0000\u002E\u0000\u002F\u0000\u0030\u0000\u0031" + // 19580 - 19584 + "\u0000\u0032\u0000\u0033\u0000\u0034\u0000\u0035\u0000\u0036" + // 19585 - 19589 + "\u0000\u0037\u0000\u0038\u0000\u0039\u0000\u003A\u0000\u003B" + // 19590 - 19594 + "\u0000\u003C\u0000\u003D\u0000\u003E\u0000\u003F\u8EA2\uE2A1" + // 19595 - 19599 + "\u8EA2\uE1FA\u0000\u0000\u0000\u0000\u8EA2\uD8A3\u8EA2\uE1FB" + // 19600 - 19604 + "\u0000\uF6DC\u8EA2\uE1F7\u0000\u0000\u0000\uF6D9\u8EA2\uE2A6" + // 19605 - 19609 + "\u0000\uF6DA\u8EA2\uE1F8\u8EA2\uE1F9\u8EA2\uE1FD\u0000\uF6DF" + // 19610 - 19614 + "\u0000\u0000\u0000\uF6DE\u0000\u0000\u0000\u0000\u8EA2\uE1FC" + // 19615 - 19619 + "\u0000\uF8C5\u8EA2\uE5EA\u8EA2\uE5F0\u0000\uF8C6\u8EA2\uE5E7" + // 19620 - 19624 + "\u0000\uF8C3\u0000\uF8C0\u8EA2\uE5F2\u0000\uF8C4\u0000\u0000" + // 19625 - 19629 + "\u0000\uF8C1\u8EA2\uE5E9\u8EA2\uE5EC\u8EA2\uE5ED\u8EA2\uE5EE" + // 19630 - 19634 + "\u8EA2\uE5F1\u0000\u0000\u8EA2\uE2A3\u8EA2\uE5E8\u0000\uF8C2" + // 19635 - 19639 + "\u8EA2\uE5EF\u8EA2\uE9B4\u8EA2\uE5EB\u0000\u0000\u0000\u0000" + // 19640 - 19644 + "\u0000\u0000\u8EA2\uE9AB\u0000\uF9F5\u8EA2\uE9AE\u0000\uF9F6" + // 19645 - 19649 + "\u8EA2\uE9B7\u8EA2\uE9AD\u8EA2\uE9AA\u0000\u0000\u8EA2\uE9AC" + // 19650 - 19654 + "\u8EA2\uE9B0\u8EA2\uE9B3\u8EA2\uE9B2\u8EA2\uE9B6\u8EA2\uE9A9" + // 19655 - 19659 + "\u0000\u0000\u0000\uF9F4\u8EA2\uE9B1\u0000\uF1AF\u0000\uF1AC" + // 19660 - 19664 + "\u0000\uF1A7\u0000\uF1AA\u8EA2\uD7FC\u0000\u0000\u8EA2\uD1C6" + // 19665 - 19669 + "\u8EA2\uD7F7\u8EA2\uD7FA\u8EA2\uD8AD\u8EA2\uD8A1\u8EA2\uD7F5" + // 19670 - 19674 + "\u8EA2\uDDBA\u8EA2\uD8AF\u8EA2\uD7F9\u8EA2\uD8A9\u0000\u0000" + // 19675 - 19679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDDBD\u0000\uF4AE" + // 19680 - 19684 + "\u0000\u0000\u0000\u0000\u0000\uF4A2\u0000\u0000\u0000\u0000" + // 19685 - 19689 + "\u0000\uF4A6\u8EA2\uDDBF\u8EA2\uDDC5\u8EA2\uDDC3\u0000\uF4A4" + // 19690 - 19694 + "\u0000\uF4A5\u0000\uF4AC\u8EA2\uDDC6\u8EA2\uDDBE\u0000\uF4A7" + // 19695 - 19699 + "\u0000\u0000\u8EA2\uDDC2\u8EA2\uDDC1\u8EA2\uDDC7\u0000\uF4AD" + // 19700 - 19704 + "\u0000\u0000\u0000\uF4A8\u0000\uF4AB\u8EA2\uDDC0\u0000\uF4AF" + // 19705 - 19709 + "\u0000\uF4A9\u0000\uF4B0\u0000\uF4A3\u8EA2\uDDC4\u0000\u0000" + // 19710 - 19714 + "\u8EA2\uDDBC\u0000\u0000\u0000\u0000\u8EA2\uE2A5\u0000\u0000" + // 19715 - 19719 + "\u0000\u0000\u0000\uF6DD\u8EA2\uE2A2\u8EA2\uE2A4\u0000\u0000" + // 19720 - 19724 + "\u0000\uF6DB\u8EA2\uE1FE\u0000\uF4AA\u0000\u0000\u8EA2\uD1D2" + // 19725 - 19729 + "\u0000\u0000\u0000\uEDCB\u8EA2\uD1CD\u0000\uEDCF\u8EA2\uD1C5" + // 19730 - 19734 + "\u0000\uE9D2\u8EA2\uD1C9\u0000\u0000\u8EA2\uD1E2\u8EA2\uD1CA" + // 19735 - 19739 + "\u8EA2\uD1C8\u0000\uEDD4\u0000\u0000\u8EA2\uD1E0\u0000\u0000" + // 19740 - 19744 + "\u8EA2\uD1C7\u8EA2\uD1D9\u8EA2\uD1D8\u8EA2\uD1E5\u8EA2\uD1CF" + // 19745 - 19749 + "\u0000\uEDD8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19750 - 19754 + "\u0000\u0000\u0000\u0000\u8EA2\uD7FE\u0000\u0000\u0000\uF1B3" + // 19755 - 19759 + "\u0000\uF1A9\u8EA2\uD8AC\u8EA2\uD7F6\u0000\uF1AB\u8EA2\uD8A4" + // 19760 - 19764 + "\u0000\uF1B2\u0000\uF1AD\u8EA2\uD8A2\u8EA2\uDDBB\u8EA2\uD8A8" + // 19765 - 19769 + "\u8EA2\uD8AE\u8EA2\uD8A7\u8EA2\uD7F4\u8EA2\uD8A5\u8EA2\uD8B0" + // 19770 - 19774 + "\u8EA2\uD8B2\u0000\uF1A8\u8EA2\uD8B1\u0000\uF1AE\u0000\uF1B4" + // 19775 - 19779 + "\u0000\u0000\u0000\uF1B1\u0000\u0000\u8EA2\uD8AB\u0000\uF1B0" + // 19780 - 19784 + "\u8EA2\uD8A6\u0000\u0000\u8EA2\uD7F8\u8EA2\uD7FD\u0000\u0000" + // 19785 - 19789 + "\u8EA2\uD8AA\u8EA2\uCBCC\u8EA2\uD1C2\u0000\u0000\u8EA2\uCBD3" + // 19790 - 19794 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19795 - 19799 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19800 - 19804 + "\u8EA2\uCBBE\u8EA2\uD1CC\u8EA2\uD1D6\u0000\uEDCC\u8EA2\uD1D5" + // 19805 - 19809 + "\u8EA2\uD1E1\u0000\uEDCE\u8EA2\uD1D3\u0000\u0000\u8EA2\uD1D7" + // 19810 - 19814 + "\u0000\uEDD9\u0000\u0000\u8EA2\uD1CE\u0000\uEDD1\u0000\u0000" + // 19815 - 19819 + "\u8EA2\uD1DA\u8EA2\uD7FB\u0000\uEDDD\u0000\uEDDC\u8EA2\uD1DC" + // 19820 - 19824 + "\u8EA2\uD1CB\u8EA2\uD1E6\u0000\uEDD7\u0000\u0000\u0000\uEDCD" + // 19825 - 19829 + "\u8EA2\uD1DF\u0000\u0000\u8EA2\uD1E3\u8EA2\uD1E8\u8EA2\uD1DD" + // 19830 - 19834 + "\u8EA2\uD1D1\u0000\uEDD0\u0000\uEDD3\u0000\uEDDB\u0000\uEDD6" + // 19835 - 19839 + "\u0000\u0000\u8EA2\uD1D4\u8EA2\uCBC8\u0000\u0000\u0000\u0000" + // 19840 - 19844 + "\u8EA2\uD1D0\u8EA2\uD1E7\u0000\uEDD2\u0000\u0000\u0000\uEDD5" + // 19845 - 19849 + "\u8EA2\uD1E9\u0000\uEDCA\u8EA2\uD1DE\u8EA2\uD1C4\u8EA2\uD1E4" + // 19850 - 19854 + "\u8EA2\uD1DB\u0000\uE9D5\u0000\uE9C6\u8EA2\uCBB7\u0000\u0000" + // 19855 - 19859 + "\u8EA2\uCBD4\u0000\uE9CA\u8EA2\uCBD1\u8EA2\uCBDE\u8EA2\uCBBD" + // 19860 - 19864 + "\u8EA2\uCBC6\u0000\u0000\u0000\u0000\u8EA2\uCBDC\u8EA2\uCBD2" + // 19865 - 19869 + "\u0000\uE9C5\u0000\u0000\u0000\u0000\u8EA2\uCBC4\u0000\uE9BF" + // 19870 - 19874 + "\u0000\uE9BD\u0000\uE9CF\u0000\u0000\u0000\u0000\u8EA2\uCBC2" + // 19875 - 19879 + "\u8EA2\uCBBF\u8EA2\uCBC0\u0000\u0000\u8EA2\uCBD8\u0000\u0000" + // 19880 - 19884 + "\u8EA2\uCBDD\u8EA2\uCBDB\u0000\uE9C3\u8EA2\uCBDF\u0000\uE9C7" + // 19885 - 19889 + "\u0000\uE9CE\u0000\u0000\u8EA2\uCBB8\u8EA2\uD1C3\u8EA2\uCBC1" + // 19890 - 19894 + "\u0000\u0000\u0000\uE9C1\u0000\uE9D3\u0000\uE9D0\u0000\uE9C4" + // 19895 - 19899 + "\u8EA2\uCBCB\u8EA2\uCBBC\u0000\uE9D1\u8EA2\uCBCD\u0000\uE9CB" + // 19900 - 19904 + "\u0000\uE9CD\u0000\uE9BC\u0000\u0000\u8EA2\uCBCF\u8EA2\uCBC7" + // 19905 - 19909 + "\u8EA2\uCBBA\u0000\uE9CC\u8EA2\uCBD7\u8EA2\uCBDA\u8EA2\uCBC3" + // 19910 - 19914 + "\u8EA2\uCBCE\u0000\u0000\u0000\uE9C2\u0000\uEDC9\u8EA2\uC4AD" + // 19915 - 19919 + "\u8EA2\uC4B7\u0000\uE4E2\u8EA2\uC4C1\u0000\uE4EF\u0000\uE4E6" + // 19920 - 19924 + "\u8EA2\uC4B4\u0000\uE4E5\u8EA2\uC4AB\u8EA2\uC4B9\u8EA2\uC4CD" + // 19925 - 19929 + "\u0000\uE0C5\u0000\uE9D7\u8EA2\uC4BC\u8EA2\uCBB4\u8EA2\uC4BE" + // 19930 - 19934 + "\u8EA2\uC4CC\u0000\uE4EC\u8EA2\uC4B5\u0000\uE4E0\u0000\uE4F0" + // 19935 - 19939 + "\u0000\uEDDA\u8EA2\uC4AA\u0000\u0000\u8EA2\uC4C3\u0000\u0000" + // 19940 - 19944 + "\u0000\u0000\u0000\u0000\u8EA2\uC4C2\u0000\u0000\u0000\u0000" + // 19945 - 19949 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19950 - 19954 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 19955 - 19959 + "\u0000\u0000\u0000\u0000\u8EA2\uCBD0\u0000\uE9D4\u8EA2\uCBC5" + // 19960 - 19964 + "\u8EA2\uCBCA\u0000\uE9C9\u0000\u0000\u8EA2\uCBB6\u0000\uE9D6" + // 19965 - 19969 + "\u0000\u0000\u0000\uE9C0\u8EA2\uCBB5\u8EA2\uCBD6\u0000\uE9D8" + // 19970 - 19974 + "\u8EA2\uCBB9\u8EA2\uCBC9\u0000\u0000\u8EA2\uCBBB\u8EA2\uCBD9" + // 19975 - 19979 + "\u8EA2\uCBD5\u0000\uE9BE\u0000\uE9C8\u8EA2\uBDBE\u8EA2\uBDB8" + // 19980 - 19984 + "\u0000\u0000\u0000\uE0C2\u0000\uE0CA\u8EA2\uBDB4\u8EA2\uBDAD" + // 19985 - 19989 + "\u8EA2\uBDAE\u0000\u0000\u8EA2\uBDB0\u0000\uE0B1\u8EA2\uBDBD" + // 19990 - 19994 + "\u0000\u0000\u0000\uE0BF\u0000\uE0C8\u0000\u0000\u0000\u0000" + // 19995 - 19999 + "\u8EA2\uBDBF\u8EA2\uBDD7\u8EA2\uBDCF\u0000\uE0AD\u8EA2\uBDD5" + // 20000 - 20004 + "\u0000\uE0B9\u0000\u0000\u0000\uE0B7\u0000\u0000\u8EA2\uBDDB" + // 20005 - 20009 + "\u0000\uE0B6\u8EA2\uBDC5\u8EA2\uBDB9\u8EA2\uBDC3\u0000\uE0CD" + // 20010 - 20014 + "\u8EA2\uBDC9\u8EA2\uBDC6\u8EA2\uBDB5\u0000\uE0C9\u8EA2\uBDD3" + // 20015 - 20019 + "\u8EA2\uBDD0\u8EA2\uBDD8\u0000\uE0B0\u8EA2\uBDC4\u0000\uE0CC" + // 20020 - 20024 + "\u0000\u0000\u8EA2\uBDCB\u0000\u0000\u0000\u0000\u0000\uE0BA" + // 20025 - 20029 + "\u0000\uE0BD\u0000\u0000\u8EA2\uBDCA\u0000\uE0CB\u8EA2\uBDBA" + // 20030 - 20034 + "\u8EA2\uB6C8\u0000\u0000\u0000\u0000\u8EA2\uBDD4\u8EA2\uBDD2" + // 20035 - 20039 + "\u8EA2\uBDD6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20040 - 20044 + "\u0000\u0000\u0000\u0000\u8EA2\uB2E6\u8EA2\uB2E3\u8EA2\uB2E1" + // 20045 - 20049 + "\u0000\u0000\u0000\u0000\u0000\uD8A6\u8EA2\uB2E4\u0000\uD8AB" + // 20050 - 20054 + "\u8EA2\uB2DE\u8EA2\uB2ED\u0000\u0000\u8EA2\uB2EF\u8EA2\uB2DF" + // 20055 - 20059 + "\u0000\u0000\u0000\uD8A8\u8EA2\uB2F0\u8EA2\uB2EB\u8EA2\uB2DD" + // 20060 - 20064 + "\u8EA2\uB2E2\u8EA2\uB2EC\u8EA2\uB2E8\u8EA2\uB2E5\u0000\u0000" + // 20065 - 20069 + "\u0000\uD8AD\u8EA2\uB2EE\u8EA2\uB2E0\u0000\u0000\u0000\u0000" + // 20070 - 20074 + "\u8EA2\uB2E9\u8EA2\uB2DB\u0000\uD8AC\u0000\uD8A9\u8EA2\uB2E7" + // 20075 - 20079 + "\u0000\u0000\u8EA2\uB2EA\u8EA2\uB2DC\u0000\uD8AA\u0000\uD8A7" + // 20080 - 20084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDDB2" + // 20085 - 20089 + "\u8EA2\uB9A8\u0000\uDDB1\u0000\u0000\u8EA2\uB9AB\u8EA2\uB9AE" + // 20090 - 20094 + "\u0000\uDDAE\u0000\uDDAC\u8EA2\uB9A3\u8EA2\uB9AC\u8EA2\uB9AA" + // 20095 - 20099 + "\u8EA2\uB9A7\u8EA2\uB9AF\u8EA2\uB9B0\u8EA2\uB9B2\u8EA2\uB9B3" + // 20100 - 20104 + "\u8EA2\uB9A2\u8EA2\uB6CF\u0000\u0000\u0000\u0000\u8EA2\uBDC1" + // 20105 - 20109 + "\u0000\u0000\u0000\uDBD2\u0000\u0000\u0000\u0000\u0000\u0000" + // 20110 - 20114 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20115 - 20119 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20120 - 20124 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20125 - 20129 + "\u0000\u0000\u0000\uE0C7\u0000\uDBDF\u0000\uE0B5\u8EA2\uBDBB" + // 20130 - 20134 + "\u0000\uE0C3\u0000\u0000\u8EA2\uBDAF\u0000\uE0B2\u0000\uE0AE" + // 20135 - 20139 + "\u8EA2\uBDCD\u0000\uE0B4\u0000\uE0B8\u0000\uE0B3\u0000\uE0BC" + // 20140 - 20144 + "\u8EA2\uBDD1\u8EA2\uBDC8\u0000\u0000\u0000\u0000\u8EA2\uBDB6" + // 20145 - 20149 + "\u0000\uE0C1\u0000\uE0BB\u8EA2\uBDC2\u0000\uE0AB\u8EA2\uBDCE" + // 20150 - 20154 + "\u8EA2\uBDC7\u0000\uE0AF\u8EA2\uBDBC\u0000\uE0BE\u8EA2\uBDD9" + // 20155 - 20159 + "\u8EA2\uBDDA\u0000\u0000\u0000\uE0AC\u8EA2\uBDCC\u0000\uE0C0" + // 20160 - 20164 + "\u8EA2\uBDC0\u8EA2\uBDB2\u8EA2\uBDB3\u0000\uE0C4\u8EA2\uBDB7" + // 20165 - 20169 + "\u8EA2\uB6C4\u0000\u0000\u8EA2\uB6E0\u0000\u0000\u0000\uDBE9" + // 20170 - 20174 + "\u0000\uDBDD\u0000\uDBE8\u0000\uDBD3\u8EA2\uB6D0\u8EA2\uB6E2" + // 20175 - 20179 + "\u8EA2\uB6DA\u0000\uDBD4\u0000\uDBCE\u8EA2\uB6E8\u0000\u0000" + // 20180 - 20184 + "\u8EA2\uB6E1\u8EA2\uB6E3\u0000\uDBD6\u0000\uDBDE\u8EA2\uB6E5" + // 20185 - 20189 + "\u8EA2\uB6CE\u8EA2\uB6DF\u8EA2\uB6D3\u8EA2\uB6E7\u0000\uDBE3" + // 20190 - 20194 + "\u0000\uDBCB\u0000\uDBE1\u8EA2\uB6D8\u8EA2\uB6D6\u8EA2\uB6D7" + // 20195 - 20199 + "\u0000\uDBD8\u8EA2\uB6D2\u8EA2\uB6D1\u0000\uDBCD\u8EA2\uB6CB" + // 20200 - 20204 + "\u8EA2\uB6E9\u0000\uDBCF\u8EA2\uB6D5\u0000\uDBED\u0000\u0000" + // 20205 - 20209 + "\u0000\uDBE7\u8EA2\uB6CA\u0000\uDBE4\u0000\uDBE2\u0000\uDBEB" + // 20210 - 20214 + "\u8EA2\uB6DC\u0000\uDBE6\u8EA2\uB0B5\u8EA2\uB6DD\u0000\uDBE5" + // 20215 - 20219 + "\u8EA2\uB6E4\u0000\uDBCA\u8EA2\uB6D9\u0000\uDBDC\u8EA2\uB6CD" + // 20220 - 20224 + "\u0000\uDBDB\u0000\u0000\u0000\uDBD9\u0000\uDBD1\u0000\uDBD0" + // 20225 - 20229 + "\u8EA2\uBDAC\u8EA2\uB6E6\u0000\u0000\u0000\u0000\u0000\uCCB2" + // 20230 - 20234 + "\u0000\u0000\u0000\uD0A7\u8EA2\uA9B2\u0000\u0000\u8EA2\uA5D3" + // 20235 - 20239 + "\u0000\uD0AC\u0000\u0000\u8EA2\uA9B3\u0000\uD0A8\u0000\uD0AB" + // 20240 - 20244 + "\u0000\uD0AA\u0000\uD0A9\u0000\uD0A6\u0000\u0000\u0000\u0000" + // 20245 - 20249 + "\u0000\u0000\u8EA2\uAEAB\u0000\u0000\u0000\uD4CA\u8EA2\uAEAE" + // 20250 - 20254 + "\u0000\uD4C8\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uAEAA" + // 20255 - 20259 + "\u0000\uD4CB\u0000\uD4C9\u0000\uD4CC\u8EA2\uAEAC\u8EA2\uAEAD" + // 20260 - 20264 + "\u0000\u0000\u8EA2\uAEA9\u8EA2\uAEAF\u0000\u0000\u0000\u0000" + // 20265 - 20269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEB7" + // 20270 - 20274 + "\u0000\uD9AA\u8EA2\uB3EC\u8EA2\uB3EE\u8EA2\uB3ED\u8EA2\uB3EF" + // 20275 - 20279 + "\u0000\uD9AB\u0000\u0000\u0000\u0000\u0000\uDEB4\u0000\u0000" + // 20280 - 20284 + "\u0000\uDEB5\u0000\uDEB6\u0000\u0000\u0000\uE3B7\u8EA2\uC1EC" + // 20285 - 20289 + "\u0000\u0000\u8EA2\uC1EB\u8EA2\uC1ED\u8EA2\uC1EE\u0000\uE3B8" + // 20290 - 20294 + "\u0000\u0000\u0000\uE3B6\u8EA2\uB0C5\u0000\u0000\u8EA2\uB0BF" + // 20295 - 20299 + "\u8EA2\uB0C8\u8EA2\uB0C6\u0000\uD6CC\u8EA2\uB0B7\u0000\uD6BD" + // 20300 - 20304 + "\u0000\uD6BC\u0000\uD6C4\u0000\uD6CA\u8EA2\uB0C3\u0000\uD6C9" + // 20305 - 20309 + "\u8EA2\uB0B4\u0000\uDBC8\u0000\u0000\u8EA2\uB0CC\u8EA2\uB0B3" + // 20310 - 20314 + "\u8EA2\uB0AE\u0000\uD6C2\u0000\uD6CE\u0000\uD6BB\u0000\u0000" + // 20315 - 20319 + "\u8EA2\uB0BB\u8EA2\uB0C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 20320 - 20324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20325 - 20329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20330 - 20334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDBEA" + // 20335 - 20339 + "\u8EA2\uB6C5\u8EA2\uB6C9\u0000\u0000\u0000\uDBD7\u0000\uDBD5" + // 20340 - 20344 + "\u0000\u0000\u0000\u0000\u0000\uDBCC\u8EA2\uB6C7\u8EA2\uB6C6" + // 20345 - 20349 + "\u0000\uDBE0\u0000\u0000\u8EA2\uB6CC\u0000\uDBDA\u0000\u0000" + // 20350 - 20354 + "\u8EA2\uB6DE\u8EA2\uB6EA\u0000\uDBC9\u8EA2\uB6DB\u8EA2\uB6D4" + // 20355 - 20359 + "\u0000\uDBEC\u8EA2\uABE6\u0000\uD1FC\u8EA2\uABE0\u0000\u0000" + // 20360 - 20364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20365 - 20369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20370 - 20374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20375 - 20379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20380 - 20384 + "\u8EA2\uB0AF\u0000\uD6C1\u0000\uD6C6\u0000\u0000\u0000\u0000" + // 20385 - 20389 + "\u0000\u0000\u8EA2\uB0B8\u8EA2\uB0BE\u8EA2\uB0BA\u8EA2\uB0AD" + // 20390 - 20394 + "\u8EA2\uB0B0\u8EA2\uB0A9\u8EA2\uB0AA\u0000\uD6CD\u0000\uD6BE" + // 20395 - 20399 + "\u8EA2\uB0B9\u8EA2\uB0C2\u0000\uD6C8\u0000\uD6BA\u0000\u0000" + // 20400 - 20404 + "\u0000\uD6C3\u8EA2\uB0B1\u0000\uD6C5\u8EA2\uB0B2\u8EA2\uB0BC" + // 20405 - 20409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD6C7\u8EA2\uB0CB" + // 20410 - 20414 + "\u8EA2\uB0AB\u0000\uD6C0\u0000\uD6BF\u0000\uD6CB\u8EA2\uB0A8" + // 20415 - 20419 + "\u8EA2\uB0C9\u8EA2\uB0BD\u8EA2\uB0CA\u8EA2\uB0C4\u8EA2\uB0B6" + // 20420 - 20424 + "\u8EA2\uABDE\u8EA2\uABE1\u0000\u0000\u8EA2\uABE4\u8EA2\uABD9" + // 20425 - 20429 + "\u0000\u0000\u0000\u0000\u8EA2\uABE7\u8EA2\uABEA\u8EA2\uABEC" + // 20430 - 20434 + "\u8EA2\uABD6\u0000\uD1F9\u0000\uD1FE\u8EA2\uB0AC\u0000\uD2B0" + // 20435 - 20439 + "\u8EA2\uABE5\u8EA2\uABED\u8EA2\uABDD\u8EA2\uABD5\u0000\u0000" + // 20440 - 20444 + "\u0000\u0000\u0000\u0000\u8EA2\uB0C7\u0000\uD2A3\u8EA2\uABE2" + // 20445 - 20449 + "\u8EA2\uABDA\u8EA2\uABDC\u0000\uD2A8\u0000\u0000\u8EA2\uABDF" + // 20450 - 20454 + "\u0000\uD2A2\u8EA2\uABD2\u8EA2\uABE8\u0000\u0000\u8EA2\uABEB" + // 20455 - 20459 + "\u0000\u0000\u0000\u0000\u0000\uD1FD\u0000\u0000\u0000\uD2AB" + // 20460 - 20464 + "\u8EA2\uABCF\u0000\uD2AD\u0000\uD1FB\u0000\uD2B1\u8EA2\uABE9" + // 20465 - 20469 + "\u8EA2\uABD1\u0000\uD2AE\u8EA2\uB0C1\u8EA2\uA7E8\u0000\uD2A1" + // 20470 - 20474 + "\u0000\uD1FA\u8EA2\uABD8\u8EA2\uABD0\u0000\uD2AF\u0000\uD2A7" + // 20475 - 20479 + "\u8EA2\uABE3\u0000\uD2AC\u0000\uD2AA\u8EA2\uABDB\u0000\uD2A4" + // 20480 - 20484 + "\u8EA2\uABD3\u0000\uD2A5\u0000\uD2A6\u8EA2\uABD4\u8EA2\uA7E7" + // 20485 - 20489 + "\u0000\uCED3\u8EA2\uA7DA\u8EA2\uA7DC\u0000\uCED1\u0000\uCED6" + // 20490 - 20494 + "\u8EA2\uA7DD\u8EA2\uA7EA\u0000\u0000\u0000\uD1F8\u0000\uCEDC" + // 20495 - 20499 + "\u0000\u0000\u0000\uCEC6\u8EA2\uA7E9\u0000\u0000\u8EA2\uA7ED" + // 20500 - 20504 + "\u8EA2\uA7D9\u8EA2\uA7EF\u8EA2\uA7E0\u0000\uCECF\u8EA2\uA7D5" + // 20505 - 20509 + "\u0000\uCECE\u0000\uCEE0\u0000\uCED5\u0000\u0000\u8EA2\uA7D3" + // 20510 - 20514 + "\u8EA2\uABD7\u0000\uCEDB\u0000\uCEDF\u8EA2\uA7E1\u8EA2\uA7E6" + // 20515 - 20519 + "\u0000\u0000\u0000\uCEE1\u0000\uCEDA\u0000\uCECC\u0000\uCEC2" + // 20520 - 20524 + "\u0000\u0000\u0000\uCEC7\u0000\u0000\u8EA2\uA7D7\u0000\uCEC3" + // 20525 - 20529 + "\u8EA2\uA7EE\u0000\u0000\u8EA2\uA7D1\u8EA2\uA7D0\u8EA2\uA7DE" + // 20530 - 20534 + "\u8EA2\uA7D2\u0000\uCEDE\u0000\uD6B9\u0000\uCED7\u8EA2\uA7DF" + // 20535 - 20539 + "\u0000\uCEC4\u0000\u0000\u0000\uD2A9\u0000\u0000\u0000\u0000" + // 20540 - 20544 + "\u0000\u0000\u8EA2\uA7EC\u0000\u0000\u0000\u0000\u0000\u0000" + // 20545 - 20549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD2E7\u0000\u0000" + // 20550 - 20554 + "\u0000\u0000\u8EA2\uD2E8\u0000\u0000\u0000\uEEB6\u8EA2\uD2EA" + // 20555 - 20559 + "\u8EA2\uD2E9\u0000\u0000\u0000\uA3A3\u0000\u0000\u8EA2\uD9B6" + // 20560 - 20564 + "\u8EA2\uD9B7\u8EA2\uD9B3\u0000\u0000\u8EA2\uD9B4\u0000\uF1E6" + // 20565 - 20569 + "\u0000\uF1E7\u8EA2\uD9B5\u0000\u0000\u0000\uF4E1\u0000\u0000" + // 20570 - 20574 + "\u0000\u0000\u0000\uF4DD\u0000\uF4E2\u0000\uF4DE\u0000\uF4E0" + // 20575 - 20579 + "\u0000\uF4DC\u0000\u0000\u0000\uF4DF\u0000\u0000\u0000\u0000" + // 20580 - 20584 + "\u0000\u0000\u0000\u0000\u0000\uF6FD\u8EA2\uDEBC\u0000\u0000" + // 20585 - 20589 + "\u8EA2\uE6B8\u0000\u0000\u8EA2\uE6B7\u0000\u0000\u8EA2\uE9D8" + // 20590 - 20594 + "\u0000\uFAA7\u0000\uFAA8\u8EA2\uEDEE\u8EA2\uEBF4\u0000\u0000" + // 20595 - 20599 + "\u8EA2\uEDED\u0000\u0000\u8EA2\uF1AD\u8EA2\uF1F2\u0000\uC8CE" + // 20600 - 20604 + "\u0000\u0000\u0000\u0000\u0000\uCBB7\u0000\u0000\u8EA2\uA8C9" + // 20605 - 20609 + "\u0000\uCFAB\u0000\u0000\u8EA2\uC7D5\u8EA2\uC7D1\u8EA2\uC7D4" + // 20610 - 20614 + "\u8EA2\uC7D2\u0000\uE6E4\u8EA2\uC7CC\u0000\uE6DF\u0000\u0000" + // 20615 - 20619 + "\u0000\u0000\u0000\u0000\u8EA2\uCEB4\u0000\uEBA2\u0000\uEBA5" + // 20620 - 20624 + "\u0000\u0000\u8EA2\uCEB6\u0000\u0000\u0000\uEBAD\u8EA2\uCEB5" + // 20625 - 20629 + "\u0000\uEBA7\u0000\u0000\u0000\uEEF8\u8EA2\uCEB7\u0000\u0000" + // 20630 - 20634 + "\u0000\uEBAC\u8EA2\uCEB3\u0000\uEBAE\u0000\u0000\u0000\u0000" + // 20635 - 20639 + "\u0000\u0000\u0000\uEBA3\u0000\u0000\u0000\uE6E2\u0000\uEBA6" + // 20640 - 20644 + "\u0000\u0000\u0000\uEBA4\u0000\uEBA8\u0000\uEBAA\u0000\uEBA1" + // 20645 - 20649 + "\u0000\uEBAF\u0000\uEBAB\u0000\u0000\u0000\uEBA9\u8EA2\uCEB2" + // 20650 - 20654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uEFA1" + // 20655 - 20659 + "\u0000\u0000\u0000\uEEFB\u0000\u0000\u0000\u0000\u0000\u0000" + // 20660 - 20664 + "\u0000\uEFA4\u0000\u0000\u8EA2\uD4BF\u0000\uEFA5\u8EA2\uD4CB" + // 20665 - 20669 + "\u8EA2\uD4C4\u0000\uEEF4\u8EA2\uD4CC\u8EA2\uD4C6\u0000\uEEFE" + // 20670 - 20674 + "\u8EA2\uA7E5\u0000\uCAE8\u0000\uCBA1\u0000\uCAF6\u8EA2\uA4CE" + // 20675 - 20679 + "\u0000\uCAEB\u0000\uCAFA\u8EA2\uA4D6\u0000\uCAE9\u0000\uCAEA" + // 20680 - 20684 + "\u8EA2\uA7E3\u8EA2\uA4CF\u0000\uCAF1\u0000\uCAFC\u8EA2\uA4DD" + // 20685 - 20689 + "\u8EA2\uA4D0\u0000\uCAEF\u0000\u0000\u0000\uCAF4\u8EA2\uA7CF" + // 20690 - 20694 + "\u0000\uCAFD\u8EA2\uA4D7\u0000\uCAF3\u0000\u0000\u0000\uCAFE" + // 20695 - 20699 + "\u0000\uCAE7\u8EA2\uA4D4\u0000\uCAEC\u8EA2\uA4D8\u8EA2\uA7E4" + // 20700 - 20704 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20705 - 20709 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 20710 - 20714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCECD\u0000\uCEDD" + // 20715 - 20719 + "\u8EA2\uA7D6\u0000\uCED4\u0000\u0000\u8EA2\uA7EB\u0000\uCEC5" + // 20720 - 20724 + "\u0000\u0000\u0000\uCEC8\u8EA2\uA7E2\u0000\u0000\u8EA2\uA7D4" + // 20725 - 20729 + "\u8EA2\uA7D8\u0000\uCED0\u0000\uCED2\u8EA2\uA7DB\u0000\uCED9" + // 20730 - 20734 + "\u0000\uCECB\u0000\uCEC9\u0000\uCECA\u0000\uCED8\u0000\uC6E5" + // 20735 - 20739 + "\u0000\uC6E4\u0000\uCAE5\u8EA2\uA1E1\u0000\u0000\u0000\u0000" + // 20740 - 20744 + "\u8EA2\uA2D9\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA2DD" + // 20745 - 20749 + "\u8EA2\uA2DF\u8EA2\uA2E0\u0000\uC8C6\u0000\uC8C7\u8EA2\uA2DC" + // 20750 - 20754 + "\u0000\uC8C2\u0000\u0000\u8EA2\uA2DA\u0000\u0000\u8EA2\uA2DE" + // 20755 - 20759 + "\u0000\uC8C3\u0000\u0000\u0000\uC8BE\u0000\u0000\u0000\uC8BF" + // 20760 - 20764 + "\u0000\u0000\u0000\uC8C5\u8EA2\uA2DB\u0000\uC8BD\u0000\uCAE6" + // 20765 - 20769 + "\u0000\uC8C0\u0000\uC8C1\u0000\uC8C4\u0000\u0000\u0000\u0000" + // 20770 - 20774 + "\u0000\u0000\u8EA2\uA4DB\u8EA2\uA4D9\u8EA2\uA4CC\u0000\uCAF2" + // 20775 - 20779 + "\u8EA2\uA4D3\u0000\uCAED\u8EA2\uA4CD\u0000\u0000\u8EA2\uA4D5" + // 20780 - 20784 + "\u0000\u0000\u8EA2\uA4D2\u0000\uCAF0\u8EA2\uA4D1\u0000\uCAF7" + // 20785 - 20789 + "\u8EA2\uA4DA\u0000\uCAF9\u0000\u0000\u0000\uCAFB\u0000\u0000" + // 20790 - 20794 + "\u8EA2\uA4CB\u0000\u0000\u0000\uCAEE\u8EA2\uA4DC\u0000\u0000" + // 20795 - 20799 + "\u0000\uCAF5\u0000\uCAF8\u0000\u0000\u0000\uD3D1\u8EA2\uB9C8" + // 20800 - 20804 + "\u0000\uE1FD\u0000\u0000\u8EA2\uC7C1\u0000\uF7BB\u0000\u0000" + // 20805 - 20809 + "\u8EA2\uE6FD\u0000\u0000\u0000\u0000\u0000\uCBC4\u0000\u0000" + // 20810 - 20814 + "\u0000\u0000\u0000\u0000\u0000\uDDBD\u0000\u0000\u0000\u0000" + // 20815 - 20819 + "\u0000\u0000\u0000\uDDBC\u0000\u0000\u8EA2\uC0B6\u0000\uE1FE" + // 20820 - 20824 + "\u8EA2\uC0B8\u8EA2\uC0B7\u0000\u0000\u0000\u0000\u8EA2\uC7C2" + // 20825 - 20829 + "\u0000\uE6D7\u8EA2\uCEAB\u8EA2\uCEAE\u8EA2\uCEAD\u0000\u0000" + // 20830 - 20834 + "\u8EA2\uCEAC\u8EA2\uD4B7\u8EA2\uD4B9\u8EA2\uD4B8\u0000\u0000" + // 20835 - 20839 + "\u0000\uF2B6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2B5" + // 20840 - 20844 + "\u0000\u0000\u0000\uF5C6\u8EA2\uDFC7\u8EA2\uDFC9\u8EA2\uDFC8" + // 20845 - 20849 + "\u0000\u0000\u0000\u0000\u0000\uF7BC\u0000\u0000\u0000\u0000" + // 20850 - 20854 + "\u0000\u0000\u8EA2\uE7A1\u8EA2\uE6FE\u0000\u0000\u8EA2\uE9F8" + // 20855 - 20859 + "\u0000\uFABC\u0000\u0000\u0000\u0000\u0000\uFBAA\u8EA2\uEEA3" + // 20860 - 20864 + "\u8EA2\uEEA2\u8EA2\uD1BF\u8EA2\uD1BE\u8EA2\uD1C1\u8EA2\uD7F2" + // 20865 - 20869 + "\u8EA2\uD7F1\u0000\uF1A6\u8EA2\uD7F3\u0000\u0000\u0000\uF4A1" + // 20870 - 20874 + "\u8EA2\uDDB9\u0000\u0000\u8EA2\uE1F6\u8EA2\uE5E6\u8EA2\uEDD5" + // 20875 - 20879 + "\u0000\u0000\u0000\uC5D4\u0000\uC6E2\u0000\uC6E1\u0000\u0000" + // 20880 - 20884 + "\u0000\uCEC0\u8EA2\uA1C1\u8EA2\uA1DF\u0000\uC8BC\u0000\u0000" + // 20885 - 20889 + "\u8EA2\uA2D8\u8EA2\uA4C9\u8EA2\uA4CA\u0000\uCEC1\u0000\u0000" + // 20890 - 20894 + "\u8EA2\uA7CE\u0000\u0000\u0000\uD1F7\u8EA2\uABCD\u8EA2\uABCE" + // 20895 - 20899 + "\u0000\u0000\u0000\uD6B4\u0000\uD6B8\u8EA2\uB0A7\u0000\uD6B7" + // 20900 - 20904 + "\u0000\uD6B5\u0000\uD6B6\u0000\u0000\u8EA2\uB6C3\u0000\uDBC7" + // 20905 - 20909 + "\u0000\uE0AA\u0000\u0000\u0000\uE0A8\u0000\uE0A9\u8EA2\uBDAB" + // 20910 - 20914 + "\u0000\u0000\u0000\u0000\u0000\uE9BB\u0000\uC5D5\u0000\u0000" + // 20915 - 20919 + "\u8EA2\uA1E0\u0000\u0000\u0000\uC6E3\u0000\u0000\u0000\u0000" + // 20920 - 20924 + "\u8EA2\uA1E3\u0000\u0000\u0000\u0000\u0000\uC6E6\u8EA2\uA1E2" + // 20925 - 20929 + "\u0000\uE4DA\u0000\u0000\u0000\u0000\u8EA2\uCBB1\u8EA2\uCBB2" + // 20930 - 20934 + "\u0000\uEDC7\u0000\uEDC8\u8EA2\uD7F0\u8EA2\uD7EF\u8EA2\uE1F5" + // 20935 - 20939 + "\u8EA2\uEFB2\u0000\uC5D1\u8EA2\uA1C0\u0000\uC6E0\u0000\u0000" + // 20940 - 20944 + "\u0000\uCAE4\u8EA2\uA4C8\u0000\u0000\u0000\uD1F5\u0000\uE4DC" + // 20945 - 20949 + "\u0000\uC5D2\u0000\u0000\u8EA2\uABCA\u0000\uD1F6\u8EA2\uABCB" + // 20950 - 20954 + "\u0000\u0000\u0000\uF3FE\u0000\uC5D3\u0000\u0000\u0000\u0000" + // 20955 - 20959 + "\u8EA2\uA7CD\u0000\u0000\u8EA2\uABCC\u0000\u0000\u8EA2\uB0A5" + // 20960 - 20964 + "\u8EA2\uB0A4\u8EA2\uB0A2\u0000\u0000\u8EA2\uB0A1\u8EA2\uB0A6" + // 20965 - 20969 + "\u8EA2\uB0A3\u0000\u0000\u0000\u0000\u0000\uDBC5\u0000\uDBC6" + // 20970 - 20974 + "\u0000\u0000\u0000\u0000\u0000\uE0A7\u8EA2\uBDA8\u0000\u0000" + // 20975 - 20979 + "\u8EA2\uBDA9\u8EA2\uBDAA\u0000\u0000\u0000\u0000\u0000\u0000" + // 20980 - 20984 + "\u8EA2\uC4A7\u8EA2\uC4A8\u8EA2\uC4A6\u0000\u0000\u8EA2\uC4A4" + // 20985 - 20989 + "\u8EA2\uC4A5\u0000\uE4DD\u8EA2\uCBB3\u8EA2\uD1C0\u8EA2\uA7CB" + // 20990 - 20994 + "\u0000\u0000\u8EA2\uABC7\u0000\uD1F2\u8EA2\uABC8\u0000\u0000" + // 20995 - 20999 + "\u0000\uD1F3\u0000\u0000\u8EA2\uAFFE\u0000\uD6B2\u0000\uD6B1" + // 21000 - 21004 + "\u0000\u0000\u8EA2\uB6C2\u8EA2\uB6C0\u8EA2\uB6C1\u8EA2\uB6BF" + // 21005 - 21009 + "\u0000\u0000\u8EA2\uB6BE\u0000\u0000\u0000\u0000\u8EA2\uBDA3" + // 21010 - 21014 + "\u8EA2\uBDA6\u0000\uE0A5\u8EA2\uBDA4\u0000\uE0A4\u8EA2\uBDA5" + // 21015 - 21019 + "\u0000\u0000\u8EA2\uC4A3\u0000\u0000\u0000\u0000\u8EA2\uCBAE" + // 21020 - 21024 + "\u8EA2\uCBAF\u8EA2\uCBB0\u0000\u0000\u8EA2\uD1BC\u8EA2\uD1BB" + // 21025 - 21029 + "\u0000\uEDC6\u8EA2\uD1BA\u8EA2\uD1BD\u8EA2\uD7EC\u0000\u0000" + // 21030 - 21034 + "\u0000\u0000\u8EA2\uD7ED\u8EA2\uD7EE\u0000\u0000\u8EA2\uDDB8" + // 21035 - 21039 + "\u0000\uF3FD\u0000\uF6D8\u8EA2\uE5E5\u0000\u0000\u0000\uFAF3" + // 21040 - 21044 + "\u8EA2\uA1BF\u0000\u0000\u0000\uD1F4\u8EA2\uABC9\u0000\uD6B3" + // 21045 - 21049 + "\u0000\u0000\u0000\u0000\u0000\uDBC4\u0000\u0000\u0000\uE0A6" + // 21050 - 21054 + "\u8EA2\uBDA7\u0000\u0000\u0000\uE4DB\u8EA2\uEBE0\u0000\u0000" + // 21055 - 21059 + "\u8EA2\uEBDC\u8EA2\uEBDE\u0000\uFAF1\u0000\u0000\u0000\u0000" + // 21060 - 21064 + "\u0000\u0000\u8EA2\uEDD3\u8EA2\uEDD4\u0000\uFBE2\u8EA2\uEDD2" + // 21065 - 21069 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uEFB1\u0000\uFCBB" + // 21070 - 21074 + "\u8EA2\uEFAF\u8EA2\uEFB0\u8EA2\uF0BA\u0000\u0000\u0000\u0000" + // 21075 - 21079 + "\u0000\uFDA3\u8EA2\uF1A3\u8EA2\uF1A1\u8EA2\uF1A2\u8EA2\uF1A4" + // 21080 - 21084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uF2AB\u0000\u0000" + // 21085 - 21089 + "\u0000\uC5CE\u0000\uC8B9\u0000\u0000\u0000\uCEBC\u0000\u0000" + // 21090 - 21094 + "\u8EA2\uA7CA\u0000\u0000\u0000\u0000\u8EA2\uABC6\u0000\u0000" + // 21095 - 21099 + "\u0000\u0000\u0000\u0000\u8EA2\uAFF7\u8EA2\uAFF9\u0000\u0000" + // 21100 - 21104 + "\u8EA2\uAFF8\u0000\u0000\u8EA2\uAFFA\u0000\uDBC3\u8EA2\uB6BB" + // 21105 - 21109 + "\u8EA2\uAFFB\u0000\u0000\u8EA2\uB6BA\u8EA2\uB6BC\u8EA2\uB6BD" + // 21110 - 21114 + "\u8EA2\uBCFD\u0000\uE0A2\u8EA2\uBCFE\u8EA2\uBDA2\u0000\uE0A3" + // 21115 - 21119 + "\u0000\uE0A1\u8EA2\uBDA1\u0000\uF3F1\u8EA2\uDDA2\u0000\u0000" + // 21120 - 21124 + "\u8EA2\uDDAD\u0000\uF3F3\u8EA2\uDDB4\u0000\u0000\u8EA2\uDDA9" + // 21125 - 21129 + "\u0000\u0000\u8EA2\uDDA4\u0000\u0000\u0000\u0000\u8EA2\uDDB5" + // 21130 - 21134 + "\u8EA2\uDCFE\u8EA2\uDDAB\u0000\u0000\u0000\uF3FA\u8EA2\uDDB0" + // 21135 - 21139 + "\u8EA2\uDDB6\u8EA2\uDDAA\u0000\uF3F2\u8EA2\uDDAC\u8EA2\uDDA1" + // 21140 - 21144 + "\u0000\uF3F9\u0000\u0000\u0000\u0000\u8EA2\uDDB3\u8EA2\uDDA6" + // 21145 - 21149 + "\u0000\uF3F5\u0000\u0000\u8EA2\uDDA8\u8EA2\uDDA5\u0000\uF3FB" + // 21150 - 21154 + "\u8EA2\uDDA7\u0000\uF3F4\u0000\uF3F7\u8EA2\uDDAF\u8EA2\uDDA3" + // 21155 - 21159 + "\u8EA2\uDDB2\u0000\u0000\u8EA2\uDDAE\u0000\u0000\u0000\u0000" + // 21160 - 21164 + "\u0000\u0000\u0000\uF6CF\u8EA2\uE1F3\u0000\uF6D4\u0000\uF6D5" + // 21165 - 21169 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF6CE\u8EA2\uE1F2" + // 21170 - 21174 + "\u0000\u0000\u8EA2\uE1EF\u8EA2\uE1F0\u0000\uF6D2\u8EA2\uE1EA" + // 21175 - 21179 + "\u8EA2\uE1EE\u0000\uF6D1\u0000\u0000\u8EA2\uE1EB\u0000\u0000" + // 21180 - 21184 + "\u0000\u0000\u8EA2\uC1E3\u8EA2\uC1E1\u0000\u0000\u8EA2\uC1DD" + // 21185 - 21189 + "\u0000\uE3AF\u8EA2\uC1DB\u0000\uE3B5\u8EA2\uC1E0\u8EA2\uC1E7" + // 21190 - 21194 + "\u8EA2\uC1DE\u8EA2\uC1E6\u0000\u0000\u0000\u0000\u0000\uE7FC" + // 21195 - 21199 + "\u0000\uE7F9\u0000\u0000\u0000\uE7FA\u0000\u0000\u8EA2\uC9A1" + // 21200 - 21204 + "\u8EA2\uC8F7\u0000\uE7F8\u0000\uE7F5\u8EA2\uC8F8\u0000\u0000" + // 21205 - 21209 + "\u0000\u0000\u0000\uE7F7\u8EA2\uC8FB\u0000\u0000\u8EA2\uC8FD" + // 21210 - 21214 + "\u0000\u0000\u0000\u0000\u8EA2\uC9A6\u0000\u0000\u0000\u0000" + // 21215 - 21219 + "\u8EA2\uC9A5\u8EA2\uC8FE\u0000\uE7F4\u8EA2\uC8F6\u0000\uE7F2" + // 21220 - 21224 + "\u8EA2\uC8F9\u0000\uE7FD\u8EA2\uC8FC\u0000\u0000\u8EA2\uC9A2" + // 21225 - 21229 + "\u0000\uE7F6\u0000\uE7F3\u8EA2\uC9A4\u8EA2\uC9A3\u0000\uE7FB" + // 21230 - 21234 + "\u0000\u0000\u8EA2\uC8FA\u0000\u0000\u0000\uECA3\u8EA2\uCFD4" + // 21235 - 21239 + "\u0000\u0000\u8EA2\uCFD1\u0000\uECA4\u0000\u0000\u0000\uECA2" + // 21240 - 21244 + "\u0000\u0000\u8EA2\uCFD8\u8EA2\uCFD5\u8EA2\uCFCE\u8EA2\uD1A8" + // 21245 - 21249 + "\u8EA2\uD7D9\u0000\u0000\u0000\u0000\u0000\uF0FA\u0000\u0000" + // 21250 - 21254 + "\u8EA2\uD7E8\u0000\uF0FE\u0000\uF1A3\u8EA2\uD7CB\u0000\u0000" + // 21255 - 21259 + "\u0000\uF0FD\u0000\u0000\u8EA2\uD7E6\u8EA2\uD7E7\u8EA2\uD7DD" + // 21260 - 21264 + "\u8EA2\uD7DC\u8EA2\uD7D4\u0000\u0000\u0000\u0000\u8EA2\uD7DE" + // 21265 - 21269 + "\u8EA2\uD7E5\u8EA2\uD7E4\u0000\u0000\u0000\uF0F8\u0000\uF0F6" + // 21270 - 21274 + "\u8EA2\uD7D6\u8EA2\uD7D3\u0000\u0000\u8EA2\uD7D0\u8EA2\uD7E3" + // 21275 - 21279 + "\u0000\uF1A2\u8EA2\uD7E1\u0000\uF0FC\u0000\uF0FB\u0000\u0000" + // 21280 - 21284 + "\u8EA2\uD7DB\u0000\u0000\u8EA2\uD7CA\u8EA2\uD7CC\u8EA2\uD7CE" + // 21285 - 21289 + "\u8EA2\uD7E0\u8EA2\uD7DA\u0000\uF0F7\u0000\u0000\u8EA2\uD7D1" + // 21290 - 21294 + "\u0000\u0000\u8EA2\uD7DF\u0000\u0000\u0000\u0000\u0000\u0000" + // 21295 - 21299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD7D2\u0000\u0000" + // 21300 - 21304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21305 - 21309 + "\u0000\u0000\u0000\uF3F8\u8EA2\uDDB1\u8EA2\uD0F1\u0000\uEDB8" + // 21310 - 21314 + "\u0000\uEDC0\u0000\u0000\u8EA2\uD1B0\u0000\uEDC1\u8EA2\uD0F2" + // 21315 - 21319 + "\u8EA2\uD1B6\u8EA2\uD1A9\u8EA2\uD1AF\u0000\uEDBE\u0000\u0000" + // 21320 - 21324 + "\u0000\u0000\u8EA2\uD1AC\u0000\u0000\u8EA2\uD1B3\u0000\u0000" + // 21325 - 21329 + "\u0000\uEDC3\u0000\u0000\u0000\uEDBD\u8EA2\uD1A5\u8EA2\uD0F6" + // 21330 - 21334 + "\u8EA2\uD1B8\u8EA2\uD0F3\u8EA2\uD1B1\u0000\uEDBA\u0000\u0000" + // 21335 - 21339 + "\u8EA2\uD0FD\u0000\u0000\u8EA2\uD0FE\u0000\uEDB9\u0000\uEDB6" + // 21340 - 21344 + "\u8EA2\uD0F9\u0000\uEDBC\u0000\u0000\u0000\uEDB5\u0000\u0000" + // 21345 - 21349 + "\u8EA2\uD1B2\u8EA2\uD1B5\u8EA2\uD1A2\u8EA2\uD7D5\u0000\u0000" + // 21350 - 21354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21355 - 21359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD7CD\u0000\u0000" + // 21360 - 21364 + "\u8EA2\uD7C9\u0000\uF1A1\u0000\u0000\u0000\u0000\u0000\uF0F4" + // 21365 - 21369 + "\u0000\uF0F9\u0000\uF0F5\u8EA2\uD7D7\u8EA2\uD7E2\u0000\uF0F3" + // 21370 - 21374 + "\u8EA2\uD7CF\u8EA2\uD7D8\u8EA2\uC3E8\u8EA2\uC3ED\u8EA2\uC3D9" + // 21375 - 21379 + "\u0000\u0000\u8EA2\uC3EA\u8EA2\uC3D6\u0000\u0000\u0000\u0000" + // 21380 - 21384 + "\u8EA2\uC3DD\u8EA2\uC3DE\u0000\uE4CE\u8EA2\uC3F1\u8EA2\uC3EF" + // 21385 - 21389 + "\u0000\u0000\u8EA2\uC3D2\u8EA2\uC3F4\u0000\u0000\u8EA2\uC3F5" + // 21390 - 21394 + "\u8EA2\uC3F7\u0000\uE4D2\u0000\uE4CA\u0000\u0000\u8EA2\uB5FA" + // 21395 - 21399 + "\u8EA2\uC3DA\u8EA2\uC3EC\u8EA2\uC3DB\u0000\uE4C7\u0000\uE4D7" + // 21400 - 21404 + "\u8EA2\uC3F3\u0000\uE4D5\u0000\uE4D1\u8EA2\uC3D0\u0000\uE4C9" + // 21405 - 21409 + "\u0000\u0000\u8EA2\uC3D3\u0000\uE4D6\u0000\u0000\u8EA2\uC3E3" + // 21410 - 21414 + "\u8EA2\uC3CF\u0000\u0000\u0000\uE4CF\u8EA2\uC3E7\u8EA2\uC3D7" + // 21415 - 21419 + "\u0000\uE4D0\u8EA2\uC3E0\u0000\uE4C6\u0000\uDFFD\u8EA2\uC3E9" + // 21420 - 21424 + "\u8EA2\uBCE2\u8EA2\uC3D4\u0000\u0000\u0000\u0000\u8EA2\uC3EE" + // 21425 - 21429 + "\u0000\uE4CB\u8EA2\uC3EB\u0000\uE4C8\u8EA2\uC3E5\u0000\uE4D3" + // 21430 - 21434 + "\u8EA2\uC3DC\u8EA2\uC3F9\u0000\u0000\u0000\u0000\u0000\u0000" + // 21435 - 21439 + "\u0000\u0000\u8EA2\uE6C5\u0000\u0000\u0000\uF8E5\u8EA2\uE6C6" + // 21440 - 21444 + "\u8EA2\uEDF1\u0000\uFCE5\u8EA2\uF0C8\u0000\uC8D0\u0000\uCFAE" + // 21445 - 21449 + "\u0000\uCFAD\u8EA2\uACF0\u0000\uD3A1\u0000\u0000\u0000\u0000" + // 21450 - 21454 + "\u8EA2\uACEF\u8EA2\uB2A6\u0000\u0000\u0000\uD7CF\u8EA2\uB8B7" + // 21455 - 21459 + "\u8EA2\uB2A5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCE1" + // 21460 - 21464 + "\u8EA2\uB8BA\u8EA2\uB8B8\u8EA2\uB8B9\u0000\uDCE0\u0000\u0000" + // 21465 - 21469 + "\u8EA2\uBFA7\u8EA2\uBFA8\u8EA2\uBFA6\u0000\u0000\u0000\uE5FA" + // 21470 - 21474 + "\u8EA2\uC6B4\u8EA2\uC6B3\u8EA2\uC6B5\u0000\uE5F9\u0000\uE5F8" + // 21475 - 21479 + "\u0000\u0000\u0000\u0000\u8EA2\uD2FA\u8EA2\uD2FC\u0000\u0000" + // 21480 - 21484 + "\u0000\uEEC9\u8EA2\uD2FB\u8EA2\uD9CF\u0000\uF1F2\u8EA2\uE2E5" + // 21485 - 21489 + "\u0000\u0000\u8EA2\uE2E4\u0000\uF8E6\u8EA2\uE6C7\u0000\uF8E8" + // 21490 - 21494 + "\u0000\uF8E7\u8EA2\uE9DD\u8EA2\uEBFC\u0000\uFAFE\u0000\uC8D1" + // 21495 - 21499 + "\u8EA2\uACF1\u0000\uD3A2\u8EA2\uB6AB\u0000\uDBAD\u0000\uDBC2" + // 21500 - 21504 + "\u0000\uDBB8\u0000\u0000\u0000\uDBBC\u0000\uDBBB\u8EA2\uB5F3" + // 21505 - 21509 + "\u0000\u0000\u8EA2\uB6B2\u8EA2\uB6B8\u8EA2\uB6B0\u8EA2\uB6B7" + // 21510 - 21514 + "\u0000\u0000\u0000\u0000\u8EA2\uB6A4\u8EA2\uB5F4\u8EA2\uB6B6" + // 21515 - 21519 + "\u8EA2\uB6A6\u0000\uDBB0\u0000\uDBBD\u0000\u0000\u8EA2\uB6AF" + // 21520 - 21524 + "\u0000\uDBB6\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB6AE" + // 21525 - 21529 + "\u8EA2\uB5F5\u0000\uDBBE\u0000\u0000\u0000\uDBC0\u8EA2\uB6B1" + // 21530 - 21534 + "\u0000\uDBC1\u0000\uDBAF\u8EA2\uB5FC\u8EA2\uB6B3\u0000\u0000" + // 21535 - 21539 + "\u0000\u0000\u0000\uDBB5\u0000\uDBBF\u8EA2\uB5FE\u8EA2\uB6AA" + // 21540 - 21544 + "\u8EA2\uB5F9\u8EA2\uB5FD\u0000\uDBBA\u8EA2\uB5F8\u0000\uDBAE" + // 21545 - 21549 + "\u0000\uDBB7\u0000\uDBB4\u8EA2\uB6A3\u0000\uD6A2\u8EA2\uBCE3" + // 21550 - 21554 + "\u0000\uDBB1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21555 - 21559 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21560 - 21564 + "\u0000\u0000\u8EA2\uA3E7\u0000\u0000\u8EA2\uA3E2\u8EA2\uA3E4" + // 21565 - 21569 + "\u8EA2\uA3E6\u8EA2\uA3E3\u0000\uCAA6\u8EA2\uA3E0\u0000\u0000" + // 21570 - 21574 + "\u8EA2\uA3E1\u0000\uCAA3\u0000\uCAA4\u8EA2\uA3E5\u8EA2\uA3E8" + // 21575 - 21579 + "\u0000\uCAA5\u8EA2\uA3E9\u0000\u0000\u0000\u0000\u0000\u0000" + // 21580 - 21584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA6C9" + // 21585 - 21589 + "\u0000\u0000\u8EA2\uA6C3\u8EA2\uA6BE\u0000\uCDA5\u8EA2\uA6C6" + // 21590 - 21594 + "\u8EA2\uA6C4\u8EA2\uA6BD\u8EA2\uA6CA\u8EA2\uA6CD\u8EA2\uA6C8" + // 21595 - 21599 + "\u8EA2\uA6C1\u0000\uCDA7\u8EA2\uA6C7\u0000\uCDA8\u8EA2\uA6C2" + // 21600 - 21604 + "\u8EA2\uA6C5\u8EA2\uA6BC\u8EA2\uA6C0\u8EA2\uA6CC\u0000\uCDA9" + // 21605 - 21609 + "\u0000\u0000\u0000\uCDAA\u0000\u0000\u8EA2\uA6BF\u8EA2\uA6CB" + // 21610 - 21614 + "\u0000\uCDA4\u0000\uCDA6\u0000\u0000\u0000\u0000\u0000\u0000" + // 21615 - 21619 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1F8" + // 21620 - 21624 + "\u0000\uA1F7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21625 - 21629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21630 - 21634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21635 - 21639 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21640 - 21644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21645 - 21649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21650 - 21654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21655 - 21659 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21660 - 21664 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21665 - 21669 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21670 - 21674 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21675 - 21679 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCBC7\u0000\u0000" + // 21680 - 21684 + "\u8EA2\uB9D0\u0000\u0000\u8EA2\uB9D1\u8EA2\uC7D8\u8EA2\uCEB8" + // 21685 - 21689 + "\u8EA2\uD4CE\u0000\uF5D1\u0000\uD6A9\u0000\uD6B0\u0000\uD5FD" + // 21690 - 21694 + "\u0000\uD6AB\u8EA2\uAFEB\u0000\uD6AD\u0000\uD5FA\u0000\u0000" + // 21695 - 21699 + "\u0000\uD5F9\u8EA2\uAFDD\u0000\u0000\u8EA2\uAFE1\u0000\uD6A4" + // 21700 - 21704 + "\u8EA2\uAFF3\u8EA2\uAFEA\u8EA2\uAFE2\u0000\uD6A8\u0000\uD6A5" + // 21705 - 21709 + "\u0000\u0000\u0000\uD5FB\u0000\uD5FE\u0000\u0000\u0000\u0000" + // 21710 - 21714 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21715 - 21719 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21720 - 21724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21725 - 21729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 21730 - 21734 + "\u8EA2\uB6A8\u0000\u0000\u8EA2\uB5F6\u8EA2\uB5F7\u8EA2\uB5FB" + // 21735 - 21739 + "\u0000\u0000\u8EA2\uB6AC\u8EA2\uB6A9\u0000\u0000\u8EA2\uB6A2" + // 21740 - 21744 + "\u8EA2\uB6A1\u0000\uDBB3\u8EA2\uB6A5\u8EA2\uB6B4\u8EA2\uB5F2" + // 21745 - 21749 + "\u0000\u0000\u8EA2\uB6B5\u8EA2\uB6A7\u8EA2\uB6B9\u8EA2\uB6AD" + // 21750 - 21754 + "\u0000\uDBB2\u8EA2\uABA6\u8EA2\uAAFC\u8EA2\uABB3\u8EA2\uABC3" + // 21755 - 21759 + "\u0000\uD1E3\u8EA2\uABA8\u8EA2\uABBA\u0000\u0000\u8EA2\uAAFE" + // 21760 - 21764 + "\u8EA2\uABC1\u8EA2\uABC2\u8EA2\uABC5\u8EA2\uABBC\u8EA2\uABAD" + // 21765 - 21769 + "\u8EA2\uABB5\u0000\uD1E9\u0000\uD1DC\u0000\uD1E4\u0000\uD1F0" + // 21770 - 21774 + "\u0000\uD1D9\u0000\uD1DB\u0000\u0000\u0000\u0000\u0000\u0000" + // 21775 - 21779 + "\u8EA2\uABA5\u0000\uD1ED\u0000\uD1E6\u8EA2\uABBF\u8EA2\uABA2" + // 21780 - 21784 + "\u0000\uD1EF\u0000\uD1EA\u8EA2\uABAB\u0000\u0000\u0000\u0000" + // 21785 - 21789 + "\u0000\uD1EE\u8EA2\uABB2\u8EA2\uABAA\u0000\uD1E7\u8EA2\uABBE" + // 21790 - 21794 + "\u8EA2\uABB6\u0000\u0000\u0000\uD1E1\u8EA2\uABC4\u8EA2\uABA9" + // 21795 - 21799 + "\u0000\uD1DD\u8EA2\uABBB\u8EA2\uABB1\u0000\uD1E2\u8EA2\uABB7" + // 21800 - 21804 + "\u0000\uD1DA\u8EA2\uAAFD\u0000\uD1EB\u0000\uD6A7\u0000\uD1E0" + // 21805 - 21809 + "\u8EA2\uABB0\u8EA2\uABAF\u8EA2\uABA4\u0000\u0000\u8EA2\uABC0" + // 21810 - 21814 + "\u0000\u0000\u8EA2\uABB9\u0000\u0000\u0000\u0000\u0000\uD1D8" + // 21815 - 21819 + "\u0000\uDEB2\u8EA2\uBCCC\u0000\u0000\u0000\uE4C4\u8EA2\uCAE1" + // 21820 - 21824 + "\u8EA2\uCAE0\u0000\u0000\u0000\u0000\u0000\uC5CC\u0000\uC8B4" + // 21825 - 21829 + "\u8EA2\uA7B3\u0000\uCEA5\u0000\u0000\u0000\uCEA4\u0000\u0000" + // 21830 - 21834 + "\u8EA2\uAAFA\u8EA2\uAAFB\u0000\u0000\u8EA2\uAFD9\u8EA2\uAFDA" + // 21835 - 21839 + "\u0000\uD5F4\u0000\uD5F5\u0000\u0000\u0000\uD5F6\u8EA2\uB5F1" + // 21840 - 21844 + "\u0000\u0000\u0000\u0000\u0000\uDBAC\u0000\u0000\u0000\uDFE7" + // 21845 - 21849 + "\u0000\u0000\u0000\uDFE6\u8EA2\uC3CE\u8EA2\uC6C1\u8EA2\uCAE2" + // 21850 - 21854 + "\u8EA2\uD7C8\u0000\u0000\u0000\u0000\u0000\uF6CD\u0000\uF9F2" + // 21855 - 21859 + "\u0000\uC5CD\u0000\u0000\u0000\uC6DC\u0000\uC6DD\u0000\uC6DB" + // 21860 - 21864 + "\u0000\uC6DE\u0000\uC6DA\u0000\u0000\u0000\u0000\u0000\uC8B7" + // 21865 - 21869 + "\u0000\u0000\u8EA2\uA2D7\u0000\uC8B6\u0000\uC8B8\u0000\u0000" + // 21870 - 21874 + "\u0000\u0000\u8EA2\uA2D2\u8EA2\uA2D1\u8EA2\uA2D4\u8EA2\uA2D3" + // 21875 - 21879 + "\u8EA2\uA2D6\u0000\uC8B5\u8EA2\uA2D0\u8EA2\uA2D5\u8EA2\uD7C0" + // 21880 - 21884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0F0\u0000\u0000" + // 21885 - 21889 + "\u0000\uF0ED\u0000\uF0F1\u8EA2\uD7BE\u0000\uF0EE\u8EA2\uD7C1" + // 21890 - 21894 + "\u8EA2\uD7C2\u8EA2\uD7C7\u0000\u0000\u0000\u0000\u8EA2\uD7C3" + // 21895 - 21899 + "\u0000\u0000\u0000\u0000\u8EA2\uDCFD\u0000\u0000\u0000\u0000" + // 21900 - 21904 + "\u0000\u0000\u0000\uF3F0\u0000\u0000\u8EA2\uE1E8\u0000\uF3EF" + // 21905 - 21909 + "\u8EA2\uE1E6\u8EA2\uE1E7\u0000\uF6CC\u0000\uF8BB\u8EA2\uE5D6" + // 21910 - 21914 + "\u0000\u0000\u0000\uF8BA\u0000\u0000\u0000\u0000\u8EA2\uE8FD" + // 21915 - 21919 + "\u8EA2\uE8FE\u0000\u0000\u0000\uF9F1\u0000\u0000\u8EA2\uE8FC" + // 21920 - 21924 + "\u0000\uFAEF\u0000\u0000\u8EA2\uEFAE\u0000\uFCBA\u8EA2\uF0B8" + // 21925 - 21929 + "\u8EA2\uF0B9\u0000\u0000\u0000\uC5CB\u0000\u0000\u0000\uC8B2" + // 21930 - 21934 + "\u0000\uC8B3\u0000\uCAD5\u0000\u0000\u8EA2\uA7B2\u0000\uD1D7" + // 21935 - 21939 + "\u0000\uD5F3\u0000\uDBAA\u0000\u0000\u0000\u0000\u0000\uD9B7" + // 21940 - 21944 + "\u0000\u0000\u0000\uDFE4\u0000\uDFE5\u8EA2\uBCC7\u0000\u0000" + // 21945 - 21949 + "\u0000\u0000\u0000\u0000\u0000\uE4C1\u0000\u0000\u8EA2\uC3C5" + // 21950 - 21954 + "\u0000\uE4BE\u0000\uE4BF\u0000\uE4BD\u8EA2\uC3CA\u8EA2\uC3C9" + // 21955 - 21959 + "\u8EA2\uC3C6\u0000\uE4C3\u0000\u0000\u0000\u0000\u8EA2\uC3C8" + // 21960 - 21964 + "\u0000\uDFE0\u0000\u0000\u0000\u0000\u8EA2\uC3CC\u8EA2\uC3C7" + // 21965 - 21969 + "\u0000\uE4C0\u0000\uE4BC\u0000\uE4C2\u8EA2\uC3CB\u0000\u0000" + // 21970 - 21974 + "\u0000\u0000\u0000\u0000\u0000\uE9A5\u0000\u0000\u8EA2\uCADF" + // 21975 - 21979 + "\u8EA2\uCADE\u8EA2\uCADD\u0000\uE9A3\u0000\u0000\u0000\u0000" + // 21980 - 21984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE9A4\u8EA2\uD0EC" + // 21985 - 21989 + "\u8EA2\uD0EF\u0000\uEDB2\u0000\u0000\u0000\u0000\u0000\uEDB1" + // 21990 - 21994 + "\u8EA2\uD0F0\u8EA2\uD0EB\u0000\uEDB4\u8EA2\uD0ED\u0000\u0000" + // 21995 - 21999 + "\u0000\uEDB3\u8EA2\uD0EA\u0000\u0000\u8EA2\uD0EE\u0000\uF0F2" + // 22000 - 22004 + "\u0000\uF0EF\u8EA2\uD7C6\u8EA2\uD7C5\u0000\u0000\u8EA2\uD7C4" + // 22005 - 22009 + "\u8EA2\uD7BF\u0000\u0000\u8EA2\uC0B4\u0000\uEAF7\u0000\u0000" + // 22010 - 22014 + "\u0000\u0000\u8EA2\uD4AB\u8EA2\uD4B2\u0000\uEEEF\u0000\u0000" + // 22015 - 22019 + "\u8EA2\uD4B6\u0000\uEEF3\u8EA2\uD4AE\u8EA2\uD4AC\u0000\u0000" + // 22020 - 22024 + "\u8EA2\uD4B5\u0000\u0000\u0000\uEEEE\u8EA2\uD4B4\u0000\uEEF0" + // 22025 - 22029 + "\u0000\uEEF1\u8EA2\uD4AD\u0000\uEEF2\u8EA2\uD4B3\u8EA2\uD4AF" + // 22030 - 22034 + "\u8EA2\uD4B0\u8EA2\uD4B1\u0000\uEAFD\u0000\u0000\u0000\u0000" + // 22035 - 22039 + "\u0000\u0000\u8EA2\uDABE\u8EA2\uDACA\u0000\u0000\u0000\uF2B4" + // 22040 - 22044 + "\u8EA2\uDAC5\u8EA2\uDAC7\u0000\u0000\u0000\uF2B2\u8EA2\uDABF" + // 22045 - 22049 + "\u8EA2\uDAC3\u0000\u0000\u8EA2\uDAC6\u0000\uF2B0\u0000\uF2B3" + // 22050 - 22054 + "\u8EA2\uDAC9\u8EA2\uDAC1\u8EA2\uDAC2\u8EA2\uDAC8\u8EA2\uDAC0" + // 22055 - 22059 + "\u8EA2\uDAC4\u0000\uF2B1\u8EA2\uDFC0\u0000\u0000\u8EA2\uDFBF" + // 22060 - 22064 + "\u0000\uF5C2\u8EA2\uDFC5\u0000\uF5C4\u0000\u0000\u0000\u0000" + // 22065 - 22069 + "\u0000\uF5C1\u8EA2\uDFC1\u0000\uF5C5\u8EA2\uDFC2\u0000\u0000" + // 22070 - 22074 + "\u8EA2\uDFC3\u8EA2\uDFC6\u0000\u0000\u0000\uF5C3\u0000\u0000" + // 22075 - 22079 + "\u8EA2\uE3C1\u0000\u0000\u0000\u0000\u8EA2\uE3C4\u0000\u0000" + // 22080 - 22084 + "\u8EA2\uE3BE\u8EA2\uE3C0\u0000\u0000\u0000\u0000\u8EA2\uE3BF" + // 22085 - 22089 + "\u8EA2\uE3C2\u8EA2\uE3C3\u8EA2\uDFC4\u8EA2\uE3BD\u0000\u0000" + // 22090 - 22094 + "\u0000\u0000\u0000\uF8F8\u8EA2\uE6F7\u8EA2\uE6FA\u8EA2\uE6FC" + // 22095 - 22099 + "\u8EA2\uE6F5\u8EA2\uE6F6\u8EA2\uE6F9\u8EA2\uE6FB\u0000\uF8F9" + // 22100 - 22104 + "\u0000\uF8F7\u0000\uF8F6\u8EA2\uE6F8\u8EA2\uE6F4\u8EA2\uE9F6" + // 22105 - 22109 + "\u0000\uFABB\u0000\u0000\u8EA2\uE9F7\u0000\u0000\u0000\u0000" + // 22110 - 22114 + "\u8EA2\uECB8\u0000\uFBA8\u8EA2\uECBA\u0000\uFBA9\u8EA2\uECB7" + // 22115 - 22119 + "\u8EA2\uECB9\u0000\uFBF2\u0000\u0000\u8EA2\uEEA1\u0000\uFBF1" + // 22120 - 22124 + "\u8EA2\uEFCC\u8EA2\uEFCB\u0000\u0000\u8EA2\uEFCA\u0000\u0000" + // 22125 - 22129 + "\u0000\u0000\u8EA2\uF1B7\u8EA2\uF1B8\u8EA2\uF1BA\u8EA2\uF1B9" + // 22130 - 22134 + "\u0000\u0000\u8EA2\uA2F0\u0000\uC8E9\u0000\uCDFD\u0000\u0000" + // 22135 - 22139 + "\u0000\uCDFB\u8EA2\uA7AB\u8EA2\uA7A7\u8EA2\uA7AE\u0000\uCDFA" + // 22140 - 22144 + "\u0000\uCEA3\u8EA2\uA7A9\u8EA2\uA7A5\u0000\uCEA2\u8EA2\uA7AC" + // 22145 - 22149 + "\u0000\uCDF9\u8EA2\uA7AD\u0000\uCDFC\u0000\uCDFE\u8EA2\uA7B1" + // 22150 - 22154 + "\u8EA2\uA7B0\u8EA2\uA7A8\u0000\uCDF8\u0000\uCDF7\u0000\uCEA1" + // 22155 - 22159 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22160 - 22164 + "\u0000\u0000\u8EA2\uAAF0\u8EA2\uAAF6\u0000\u0000\u0000\uD1D3" + // 22165 - 22169 + "\u0000\uD1D0\u8EA2\uAAED\u8EA2\uAAF2\u0000\u0000\u0000\uD1D6" + // 22170 - 22174 + "\u0000\uD1CE\u8EA2\uAAF1\u0000\uD1D1\u0000\uD1D4\u0000\u0000" + // 22175 - 22179 + "\u0000\u0000\u8EA2\uAAF4\u0000\u0000\u0000\uD1CF\u8EA2\uAAF9" + // 22180 - 22184 + "\u0000\uD1D2\u0000\u0000\u0000\uD1D5\u8EA2\uAAEE\u8EA2\uAAF3" + // 22185 - 22189 + "\u8EA2\uAAF7\u8EA2\uAAEF\u8EA2\uAAEC\u0000\u0000\u0000\u0000" + // 22190 - 22194 + "\u8EA2\uAAF8\u8EA2\uAAF5\u0000\u0000\u0000\u0000\u0000\u0000" + // 22195 - 22199 + "\u0000\u0000\u0000\u0000\u8EA2\uBCCF\u0000\uDFED\u8EA2\uBCF7" + // 22200 - 22204 + "\u8EA2\uBCDF\u8EA2\uBCE1\u0000\u0000\u8EA2\uBCEE\u8EA2\uBCED" + // 22205 - 22209 + "\u8EA2\uBCCD\u0000\uDFF8\u0000\uDFFA\u8EA2\uBCD5\u8EA2\uBCDC" + // 22210 - 22214 + "\u8EA2\uBCE4\u0000\uDFF9\u8EA2\uBCDB\u8EA2\uBCE7\u8EA2\uBCFA" + // 22215 - 22219 + "\u0000\u0000\u8EA2\uBCEF\u0000\u0000\u0000\u0000\u0000\u0000" + // 22220 - 22224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA4C9\u0000\u0000" + // 22225 - 22229 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22230 - 22234 + "\u8EA2\uBCFB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22235 - 22239 + "\u8EA2\uBCD1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22240 - 22244 + "\u8EA2\uC3F8\u0000\uE4CC\u0000\u0000\u8EA2\uC3F6\u8EA2\uC3E1" + // 22245 - 22249 + "\u8EA2\uC3E6\u8EA2\uC3DF\u0000\u0000\u8EA2\uC3F2\u8EA2\uC3D1" + // 22250 - 22254 + "\u8EA2\uC3D8\u0000\u0000\u8EA2\uC3F0\u8EA2\uC3FA\u8EA2\uC3E2" + // 22255 - 22259 + "\u0000\u0000\u8EA2\uC3D5\u8EA2\uDCFA\u8EA2\uDCF9\u0000\uF3ED" + // 22260 - 22264 + "\u0000\uF3EE\u8EA2\uE5D3\u0000\u0000\u0000\u0000\u0000\uC5C6" + // 22265 - 22269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBCBE" + // 22270 - 22274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFD9\u0000\uDFD8" + // 22275 - 22279 + "\u8EA2\uC3C3\u0000\u0000\u8EA2\uE1E4\u0000\uFAEE\u8EA2\uEFAD" + // 22280 - 22284 + "\u0000\uC5C7\u0000\u0000\u0000\uD5E8\u0000\u0000\u0000\uDAFC" + // 22285 - 22289 + "\u0000\uDAFB\u8EA2\uBCBF\u8EA2\uBCC0\u0000\uE4BA\u8EA2\uCADC" + // 22290 - 22294 + "\u0000\uE8FE\u8EA2\uD7BD\u0000\u0000\u0000\uC5C8\u0000\uC6D8" + // 22295 - 22299 + "\u0000\u0000\u0000\uCDF4\u8EA2\uA7A3\u0000\u0000\u8EA2\uAAEA" + // 22300 - 22304 + "\u0000\uD1CB\u0000\uDAFD\u0000\u0000\u8EA2\uBCC1\u0000\uDFDA" + // 22305 - 22309 + "\u0000\uE4BB\u0000\u0000\u8EA2\uD0E8\u8EA2\uD0E9\u0000\u0000" + // 22310 - 22314 + "\u0000\u0000\u8EA2\uDCFB\u0000\uF6CB\u8EA2\uF0FE\u0000\uC5C9" + // 22315 - 22319 + "\u0000\u0000\u8EA2\uA7A4\u0000\uCDF5\u0000\uD1CC\u0000\u0000" + // 22320 - 22324 + "\u8EA2\uAAEB\u0000\uF8B8\u8EA2\uE1E1\u0000\u0000\u8EA2\uE1E2" + // 22325 - 22329 + "\u8EA2\uE1DE\u0000\u0000\u0000\uF6C7\u8EA2\uE5CD\u0000\u0000" + // 22330 - 22334 + "\u8EA2\uE5D0\u0000\u0000\u0000\u0000\u8EA2\uE5D1\u8EA2\uE5CF" + // 22335 - 22339 + "\u8EA2\uE5D2\u0000\uF8B9\u8EA2\uE5CE\u0000\u0000\u0000\u0000" + // 22340 - 22344 + "\u8EA2\uE8FA\u0000\uF9EF\u8EA2\uE8F9\u8EA2\uE8F8\u8EA2\uE8F7" + // 22345 - 22349 + "\u0000\uF9EE\u0000\uF9F0\u0000\u0000\u8EA2\uEBDB\u0000\uFAED" + // 22350 - 22354 + "\u0000\uFAEC\u0000\u0000\u0000\u0000\u8EA2\uEDCF\u8EA2\uEDCE" + // 22355 - 22359 + "\u8EA2\uEDD1\u0000\uFCB7\u0000\uFBE1\u8EA2\uEFAC\u8EA2\uEDD0" + // 22360 - 22364 + "\u0000\u0000\u0000\u0000\u8EA2\uEFAB\u0000\uFCB9\u0000\uFCB8" + // 22365 - 22369 + "\u0000\uFCDF\u8EA2\uF0B7\u8EA2\uF0FD\u0000\uC5C5\u0000\u0000" + // 22370 - 22374 + "\u0000\u0000\u8EA2\uBCB7\u8EA2\uD7BA\u0000\uA7E1\u0000\u0000" + // 22375 - 22379 + "\u0000\uC8AD\u8EA2\uA2CD\u0000\uCAD3\u0000\uCAD1\u0000\u0000" + // 22380 - 22384 + "\u0000\uCAD2\u0000\u0000\u8EA2\uA7A2\u0000\uCDF3\u0000\uD1C9" + // 22385 - 22389 + "\u8EA2\uC3B9\u8EA2\uC3B4\u0000\u0000\u8EA2\uC3A8\u0000\u0000" + // 22390 - 22394 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22395 - 22399 + "\u0000\u0000\u8EA2\uCAD0\u0000\u0000\u8EA2\uCAC1\u8EA2\uCACC" + // 22400 - 22404 + "\u0000\u0000\u8EA2\uCAD3\u0000\uE8F8\u0000\uDFD1\u8EA2\uCAD1" + // 22405 - 22409 + "\u0000\uE8F3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8F2" + // 22410 - 22414 + "\u8EA2\uCAD8\u0000\u0000\u8EA2\uCAC2\u8EA2\uCACF\u8EA2\uCAC3" + // 22415 - 22419 + "\u8EA2\uCACE\u0000\uE8F6\u8EA2\uCAD2\u8EA2\uBCA7\u0000\u0000" + // 22420 - 22424 + "\u0000\u0000\u0000\u0000\u8EA2\uCAD9\u8EA2\uCACA\u0000\uE8F9" + // 22425 - 22429 + "\u8EA2\uD0E1\u0000\uECF7\u0000\u0000\u8EA2\uCAD7\u8EA2\uCAD6" + // 22430 - 22434 + "\u0000\uE8FB\u8EA2\uD0D4\u0000\uECF8\u8EA2\uD0D5\u0000\u0000" + // 22435 - 22439 + "\u8EA2\uCAC6\u8EA2\uCAC7\u8EA2\uCAC4\u8EA2\uCAC9\u8EA2\uCAC5" + // 22440 - 22444 + "\u8EA2\uCADA\u0000\uE8F5\u0000\uECF9\u0000\uE8F7\u0000\uE8FC" + // 22445 - 22449 + "\u0000\u0000\u8EA2\uCAC8\u0000\u0000\u8EA2\uCAD4\u0000\uDFC1" + // 22450 - 22454 + "\u0000\u0000\u8EA2\uBCAF\u8EA2\uBBFC\u8EA2\uBCAC\u8EA2\uC2FD" + // 22455 - 22459 + "\u0000\uDFC4\u8EA2\uBCB0\u8EA2\uBCB3\u0000\uDFC3\u8EA2\uBCA1" + // 22460 - 22464 + "\u8EA2\uBCB2\u8EA2\uBCB1\u0000\uDFC5\u8EA2\uBBF8\u0000\uDFC0" + // 22465 - 22469 + "\u0000\uDFC8\u0000\u0000\u0000\uDFC6\u8EA2\uBCAE\u0000\u0000" + // 22470 - 22474 + "\u8EA2\uBCA4\u0000\uDFCA\u8EA2\uBCB5\u8EA2\uBCAD\u8EA2\uBCB6" + // 22475 - 22479 + "\u0000\uDFD2\u0000\uDFD0\u8EA2\uBCAB\u8EA2\uBCAA\u0000\u0000" + // 22480 - 22484 + "\u8EA2\uBCA8\u8EA2\uBCA2\u0000\uDFC9\u0000\u0000\u0000\uDFC7" + // 22485 - 22489 + "\u8EA2\uAFBB\u8EA2\uBBF9\u0000\u0000\u8EA2\uC3BD\u8EA2\uBBFA" + // 22490 - 22494 + "\u0000\uDFC2\u0000\uDFCF\u8EA2\uC3A1\u0000\u0000\u0000\uDFCB" // 22495 - 22499 + ; + + index2c = + "\u0000\uDFCC\u8EA2\uBBFB\u8EA2\uBCB4\u8EA2\uC2FE\u8EA2\uBCA5" + // 22500 - 22504 + "\u8EA2\uBBFE\u0000\uDFCE\u8EA2\uBCA6\u8EA2\uBCA3\u0000\u0000" + // 22505 - 22509 + "\u0000\u0000\u0000\uDFD3\u0000\u0000\u0000\u0000\u0000\u0000" + // 22510 - 22514 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB0DA\u8EA2\uB0DB" + // 22515 - 22519 + "\u8EA2\uB0DC\u0000\uD6D3\u0000\u0000\u0000\uD6CF\u8EA2\uB0D1" + // 22520 - 22524 + "\u0000\u0000\u8EA2\uB0D9\u8EA2\uB0DE\u0000\uD6D4\u0000\u0000" + // 22525 - 22529 + "\u8EA2\uB0CF\u8EA2\uB0D5\u8EA2\uB0CE\u8EA2\uB0D8\u0000\u0000" + // 22530 - 22534 + "\u0000\u0000\u8EA2\uB0D4\u0000\uD6D0\u0000\uD6D2\u8EA2\uB0DD" + // 22535 - 22539 + "\u0000\u0000\u8EA2\uB0CD\u8EA2\uB0D0\u8EA2\uB0D6\u0000\u0000" + // 22540 - 22544 + "\u8EA2\uB0D7\u8EA2\uB0DF\u8EA2\uB0D3\u0000\u0000\u0000\uD6D1" + // 22545 - 22549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22550 - 22554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22555 - 22559 + "\u0000\uDBF2\u8EA2\uB6F1\u0000\u0000\u0000\u0000\u8EA2\uB6F3" + // 22560 - 22564 + "\u8EA2\uB6EF\u0000\u0000\u8EA2\uB6FB\u8EA2\uB6ED\u8EA2\uB6FA" + // 22565 - 22569 + "\u0000\uDBEE\u8EA2\uB6EB\u8EA2\uBDDF\u8EA2\uB6F5\u0000\uDBF1" + // 22570 - 22574 + "\u0000\u0000\u8EA2\uB6F6\u0000\uDAEC\u8EA2\uB5E5\u8EA2\uB5CD" + // 22575 - 22579 + "\u0000\uDAE1\u0000\uDAE5\u8EA2\uB5E4\u0000\u0000\u8EA2\uB5D7" + // 22580 - 22584 + "\u0000\uDAE6\u0000\uDAE0\u8EA2\uB5CC\u0000\u0000\u0000\uDFBF" + // 22585 - 22589 + "\u8EA2\uB5E7\u8EA2\uB5D5\u0000\uDAEB\u8EA2\uB5D8\u8EA2\uB5E6" + // 22590 - 22594 + "\u0000\uDAEA\u0000\u0000\u8EA2\uBBF5\u0000\u0000\u0000\uDAD7" + // 22595 - 22599 + "\u8EA2\uB5D3\u0000\uDADC\u0000\uDAE7\u0000\u0000\u0000\uDAE2" + // 22600 - 22604 + "\u8EA2\uB5DC\u8EA2\uB5D2\u8EA2\uB5D0\u8EA2\uB5E2\u0000\uDAD4" + // 22605 - 22609 + "\u0000\uDAE8\u0000\uDAD8\u0000\uDFBE\u8EA2\uB5E0\u0000\uDAD9" + // 22610 - 22614 + "\u0000\u0000\u0000\uDAD5\u0000\uDAE4\u0000\uDADF\u0000\uDADD" + // 22615 - 22619 + "\u8EA2\uB5D4\u0000\uDAE9\u8EA2\uB5D1\u8EA2\uB5DE\u8EA2\uB5DA" + // 22620 - 22624 + "\u8EA2\uBBF7\u8EA2\uBBF6\u0000\u0000\u0000\u0000\u0000\u0000" + // 22625 - 22629 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22630 - 22634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB5CF\u8EA2\uBCA9" + // 22635 - 22639 + "\u0000\u0000\u8EA2\uDAAF\u0000\uEEE1\u0000\uF2AA\u8EA2\uDAB9" + // 22640 - 22644 + "\u8EA2\uDAB5\u0000\u0000\u8EA2\uDAB3\u8EA2\uDAAE\u8EA2\uDABD" + // 22645 - 22649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2AE\u0000\u0000" + // 22650 - 22654 + "\u8EA2\uDAAA\u8EA2\uDAB6\u8EA2\uDAB7\u8EA2\uDAAD\u8EA2\uDAAC" + // 22655 - 22659 + "\u8EA2\uDABA\u0000\u0000\u8EA2\uDAB0\u8EA2\uDAAB\u8EA2\uDAB1" + // 22660 - 22664 + "\u0000\u0000\u8EA2\uDABC\u8EA2\uDAA9\u8EA2\uDABB\u8EA2\uDAB8" + // 22665 - 22669 + "\u0000\uF2AC\u0000\uF2AB\u0000\u0000\u0000\u0000\u0000\uF2AD" + // 22670 - 22674 + "\u8EA2\uDAB4\u8EA2\uDAA8\u0000\u0000\u0000\u0000\u0000\u0000" + // 22675 - 22679 + "\u0000\u0000\u0000\u0000\u8EA2\uDFA7\u0000\uF5BC\u8EA2\uDFAB" + // 22680 - 22684 + "\u8EA2\uDFA8\u8EA2\uDFAF\u8EA2\uDFB5\u8EA2\uDFAA\u0000\u0000" + // 22685 - 22689 + "\u8EA2\uDFBE\u0000\uF5B9\u8EA2\uDFB8\u8EA2\uDFAD\u8EA2\uDFB9" + // 22690 - 22694 + "\u8EA2\uDFB4\u8EA2\uDFBB\u8EA2\uDFAC\u0000\uF5BE\u0000\uF5BD" + // 22695 - 22699 + "\u8EA2\uDFAE\u8EA2\uDFBC\u8EA2\uDFA6\u8EA2\uDFBA\u8EA2\uAFCD" + // 22700 - 22704 + "\u8EA2\uAFC1\u0000\uD5DA\u8EA2\uAFBA\u8EA2\uAFB7\u8EA2\uAFB8" + // 22705 - 22709 + "\u0000\uD5DB\u8EA2\uAFC7\u8EA2\uAFCE\u0000\uD5DD\u8EA2\uAFBE" + // 22710 - 22714 + "\u8EA2\uAFBD\u0000\uD5E5\u0000\uD5E4\u0000\uD5D6\u0000\uD5DC" + // 22715 - 22719 + "\u0000\uD5DF\u8EA2\uAFCA\u0000\u0000\u0000\u0000\u8EA2\uAFC4" + // 22720 - 22724 + "\u0000\uD5D9\u8EA2\uAFB5\u8EA2\uAFCC\u8EA2\uAFC3\u8EA2\uAFC5" + // 22725 - 22729 + "\u8EA2\uAFC9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22730 - 22734 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22735 - 22739 + "\u0000\u0000\u8EA2\uB5CB\u0000\u0000\u0000\uDADB\u0000\uDAEF" + // 22740 - 22744 + "\u0000\uDAEE\u0000\u0000\u0000\uDAE3\u0000\u0000\u8EA2\uB5DD" + // 22745 - 22749 + "\u8EA2\uB5D9\u8EA2\uB5D6\u8EA2\uB5E8\u0000\uDADE\u0000\uDAD6" + // 22750 - 22754 + "\u0000\u0000\u0000\u0000\u8EA2\uB5DB\u0000\uDFCD\u0000\uDADA" + // 22755 - 22759 + "\u8EA2\uB5E3\u0000\u0000\u0000\uDAF0\u0000\uDAED\u8EA2\uB5DF" + // 22760 - 22764 + "\u8EA2\uB5CE\u0000\u0000\u0000\u0000\u8EAD\uA1D3\u8EAD\uA1D4" + // 22765 - 22769 + "\u8EAD\uA1D5\u8EAD\uA1D6\u8EAD\uA1D7\u8EAD\uA1D8\u8EAD\uA1D9" + // 22770 - 22774 + "\u8EAD\uA1DA\u8EAD\uA1DB\u8EAD\uA1DC\u8EAD\uA1DD\u8EAD\uA1DE" + // 22775 - 22779 + "\u8EAD\uA1DF\u8EAD\uA1E0\u8EAD\uA1E1\u8EAD\uA1E2\u8EAD\uA1E3" + // 22780 - 22784 + "\u8EAD\uA1E4\u8EAD\uA1E5\u8EAD\uA1E6\u8EAD\uA1E7\u8EAD\uA1E8" + // 22785 - 22789 + "\u8EAD\uA1E9\u8EAD\uA1EA\u8EAD\uA1EB\u8EAD\uA1EC\u8EAD\uA1ED" + // 22790 - 22794 + "\u8EAD\uA1EE\u8EAD\uA1EF\u8EAD\uA1F0\u8EAD\uA1F1\u8EAD\uA1F2" + // 22795 - 22799 + "\u8EAD\uA1F3\u8EAD\uA1F4\u8EAD\uA1F5\u8EAD\uA1F6\u8EAD\uA1F7" + // 22800 - 22804 + "\u8EAD\uA1F8\u8EAD\uA1F9\u8EAD\uA1FA\u8EAD\uA1FB\u8EAD\uA1FC" + // 22805 - 22809 + "\u8EAD\uA1FD\u8EAD\uA1FE\u8EAD\uA2A1\u8EAD\uA2A2\u8EAD\uA2A3" + // 22810 - 22814 + "\u8EAD\uA2A4\u8EAD\uA2A5\u8EAD\uA2A6\u8EAD\uA2A7\u8EAD\uA2A8" + // 22815 - 22819 + "\u8EAD\uA2A9\u8EAD\uA2AA\u8EAD\uA2AB\u8EAD\uA2AC\u8EAD\uA2AD" + // 22820 - 22824 + "\u8EAD\uA2AE\u8EAD\uA2AF\u8EAD\uA2B0\u8EAD\uA2B1\u8EAD\uA2B2" + // 22825 - 22829 + "\u8EAD\uA2B3\u8EA2\uAAE1\u0000\uD1BD\u0000\uD1C8\u8EA2\uAADA" + // 22830 - 22834 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD1C0\u0000\uD5D4" + // 22835 - 22839 + "\u0000\uD1BA\u0000\u0000\u8EA2\uAAD7\u8EA2\uAADE\u8EA2\uAAD6" + // 22840 - 22844 + "\u8EA2\uAAD9\u8EA2\uAADD\u8EA2\uAFB4\u0000\uD1C7\u0000\u0000" + // 22845 - 22849 + "\u8EA2\uAAE2\u8EA2\uAAE3\u8EA2\uAAE5\u0000\uD1B9\u0000\u0000" + // 22850 - 22854 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22855 - 22859 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22860 - 22864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22865 - 22869 + "\u0000\u0000\u0000\uD5E3\u8EA2\uAFC0\u0000\uD5E1\u0000\uD5E2" + // 22870 - 22874 + "\u8EA2\uAFB6\u8EA2\uAFC6\u0000\u0000\u0000\uD5D8\u0000\u0000" + // 22875 - 22879 + "\u0000\u0000\u8EA2\uB5CA\u8EA2\uAFC8\u8EA2\uAFC2\u0000\u0000" + // 22880 - 22884 + "\u8EA2\uAFB9\u0000\u0000\u8EA2\uAFCB\u8EA2\uAFBC\u0000\uD5DE" + // 22885 - 22889 + "\u8EA2\uB5E1\u8EA2\uAFBF\u0000\uD5E0\u0000\uD5D7\u0000\u0000" + // 22890 - 22894 + "\u8EA2\uBFD2\u0000\u0000\u0000\uE1D7\u0000\uE1EB\u0000\u0000" + // 22895 - 22899 + "\u8EA2\uBFE1\u0000\uE1ED\u0000\u0000\u8EA2\uBFDD\u0000\uE1E2" + // 22900 - 22904 + "\u0000\uE1DC\u0000\uE1E4\u0000\uE1D9\u0000\uE1EA\u8EA2\uBFDE" + // 22905 - 22909 + "\u8EA2\uBFCC\u8EA2\uBFE0\u8EA2\uBFC7\u8EA2\uBFE9\u0000\u0000" + // 22910 - 22914 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22915 - 22919 + "\u0000\u0000\u8EA2\uBFF2\u0000\u0000\u0000\u0000\u0000\u0000" + // 22920 - 22924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBFC4" + // 22925 - 22929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22930 - 22934 + "\u8EA2\uC6EC\u0000\u0000\u8EA2\uC7A4\u0000\uE5DC\u8EA2\uC6F1" + // 22935 - 22939 + "\u0000\u0000\u8EA2\uC6EF\u8EA2\uC6DA\u0000\uE6B0\u8EA2\uC6EA" + // 22940 - 22944 + "\u8EA2\uC6FE\u8EA2\uC6E6\u0000\uE6B8\u8EA2\uC6FD\u8EA2\uC6E4" + // 22945 - 22949 + "\u0000\uE1E9\u8EA2\uC6F4\u8EA2\uC6E5\u8EA2\uC6FB\u0000\uE6B7" + // 22950 - 22954 + "\u0000\uE6AF\u0000\u0000\u8EA2\uC6CF\u0000\uCAC0\u8EA2\uA4B3" + // 22955 - 22959 + "\u0000\u0000\u8EA2\uA4A9\u0000\uCABD\u0000\u0000\u0000\uCAD0" + // 22960 - 22964 + "\u8EA2\uA4AE\u8EA2\uA4B4\u0000\uCAC2\u0000\uCAC4\u0000\u0000" + // 22965 - 22969 + "\u8EA2\uA4AA\u0000\u0000\u8EA2\uA4AB\u8EA2\uA4AC\u0000\u0000" + // 22970 - 22974 + "\u0000\uCACF\u0000\uCAC9\u0000\uCACE\u8EA2\uA4AD\u0000\uCACD" + // 22975 - 22979 + "\u0000\uCABF\u0000\uCABE\u0000\uCACB\u0000\u0000\u0000\u0000" + // 22980 - 22984 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22985 - 22989 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 22990 - 22994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDE3\u8EA2\uA6FD" + // 22995 - 22999 + "\u8EA2\uA6F6\u0000\uCDDE\u0000\uCDF1\u8EA2\uA6F2\u8EA2\uA6F9" + // 23000 - 23004 + "\u8EA2\uA6FB\u8EA2\uA6FE\u0000\uCDEC\u0000\u0000\u8EA2\uA6FA" + // 23005 - 23009 + "\u8EA2\uA6F3\u0000\uCDEA\u8EA2\uA6F7\u0000\u0000\u8EA2\uA7A1" + // 23010 - 23014 + "\u0000\uCDDB\u0000\u0000\u8EA2\uA6FC\u0000\uCDE5\u0000\uCDE4" + // 23015 - 23019 + "\u8EA2\uA6F5\u0000\uCDD9\u0000\uCDD4\u0000\uD1B7\u8EA2\uAAD3" + // 23020 - 23024 + "\u8EA2\uAAD4\u0000\u0000\u0000\u0000\u8EA2\uAFB2\u0000\uD5D2" + // 23025 - 23029 + "\u0000\uDAD3\u0000\uDFBD\u8EA2\uBBF4\u0000\uC5C3\u0000\u0000" + // 23030 - 23034 + "\u0000\uC4DF\u0000\uC5C4\u0000\u0000\u8EA2\uA1DE\u0000\uC6D7" + // 23035 - 23039 + "\u0000\uC6D6\u0000\uC6D4\u0000\uC6D5\u0000\u0000\u0000\u0000" + // 23040 - 23044 + "\u0000\u0000\u0000\uC8AC\u8EA2\uA2C9\u8EA2\uA2CB\u0000\uC8AB" + // 23045 - 23049 + "\u8EA2\uA2C3\u0000\u0000\u8EA2\uA2C4\u0000\u0000\u8EA2\uA2CA" + // 23050 - 23054 + "\u8EA2\uA2C6\u8EA2\uA2C8\u0000\uC8AA\u8EA2\uA2C5\u8EA2\uA2CC" + // 23055 - 23059 + "\u8EA2\uA2C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23060 - 23064 + "\u0000\u0000\u0000\u0000\u0000\uCAC3\u0000\uCACC\u0000\uCACA" + // 23065 - 23069 + "\u8EA2\uA4B2\u8EA2\uA4AF\u8EA2\uA4B7\u0000\uCAC8\u8EA2\uA4B8" + // 23070 - 23074 + "\u0000\u0000\u0000\uCAC1\u8EA2\uA4B5\u0000\u0000\u0000\uCAC7" + // 23075 - 23079 + "\u8EA2\uA4B1\u8EA2\uA4B0\u0000\uCAC5\u8EA2\uA4B6\u0000\uCAC6" + // 23080 - 23084 + "\u0000\uCDD5\u0000\uFCB6\u8EA2\uEFA8\u0000\u0000\u8EA2\uEFA9" + // 23085 - 23089 + "\u8EA2\uEFAA\u0000\u0000\u0000\u0000\u8EA2\uF2AA\u0000\uC5C1" + // 23090 - 23094 + "\u8EA2\uA1DD\u0000\uC6D3\u0000\u0000\u0000\uC8A7\u0000\uC8A8" + // 23095 - 23099 + "\u0000\uC8A6\u0000\u0000\u0000\uC8A9\u0000\uCABC\u0000\uCABB" + // 23100 - 23104 + "\u0000\u0000\u8EA2\uA6F0\u0000\uCDD1\u0000\uCDD0\u0000\u0000" + // 23105 - 23109 + "\u0000\u0000\u8EA2\uAFB1\u0000\uDAD1\u0000\uDAD2\u0000\u0000" + // 23110 - 23114 + "\u0000\u0000\u0000\u0000\u0000\uDFBC\u8EA2\uC2F8\u0000\uE4A8" + // 23115 - 23119 + "\u0000\uE4A9\u8EA2\uC2FA\u8EA2\uC2FC\u8EA2\uC2FB\u0000\u0000" + // 23120 - 23124 + "\u8EA2\uCABE\u0000\u0000\u8EA2\uCABD\u0000\uE8F0\u8EA2\uCABF" + // 23125 - 23129 + "\u0000\u0000\u8EA2\uD0D3\u0000\uECF6\u0000\u0000\u0000\uF0DD" + // 23130 - 23134 + "\u0000\u0000\u0000\uF3E1\u0000\uF6C3\u0000\uF3E2\u0000\u0000" + // 23135 - 23139 + "\u0000\uC5C2\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA4A8" + // 23140 - 23144 + "\u0000\u0000\u0000\u0000\u8EA2\uA6F1\u0000\uCDD3\u0000\uCDD2" + // 23145 - 23149 + "\u8EA2\uCAB3\u0000\u0000\u0000\uECE8\u8EA2\uD0C2\u0000\u0000" + // 23150 - 23154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD0C7" + // 23155 - 23159 + "\u0000\uF0D7\u8EA2\uD0C1\u8EA2\uD7AE\u8EA2\uD0D0\u0000\uECF0" + // 23160 - 23164 + "\u0000\u0000\u0000\uECEE\u0000\uF0D5\u8EA2\uD0CD\u8EA2\uD0C9" + // 23165 - 23169 + "\u0000\uECF4\u0000\u0000\u8EA2\uD7A5\u0000\u0000\u0000\u0000" + // 23170 - 23174 + "\u0000\u0000\u0000\uECF2\u8EA2\uD0C8\u0000\u0000\u8EA2\uD7A3" + // 23175 - 23179 + "\u0000\u0000\u8EA2\uD0CC\u0000\u0000\u8EA2\uD0CF\u8EA2\uD0C6" + // 23180 - 23184 + "\u0000\u0000\u0000\uECF3\u0000\u0000\u0000\u0000\u0000\uECED" + // 23185 - 23189 + "\u8EA2\uD7A4\u0000\uF0D6\u8EA2\uD0CE\u0000\uECEF\u0000\uECF1" + // 23190 - 23194 + "\u8EA2\uD0CB\u0000\uECF5\u8EA2\uD0CA\u8EA2\uD0C5\u8EA2\uD0C4" + // 23195 - 23199 + "\u0000\uF0D4\u8EA2\uD0D2\u8EA2\uD7A7\u8EA2\uDCEB\u0000\uF0D9" + // 23200 - 23204 + "\u0000\u0000\u8EA2\uD7AD\u0000\u0000\u8EA2\uD7AB\u0000\u0000" + // 23205 - 23209 + "\u8EA2\uDCEC\u0000\u0000\u0000\uF0DA\u8EA2\uD7AC\u8EA2\uC2F7" + // 23210 - 23214 + "\u8EA2\uCAAB\u0000\u0000\u0000\u0000\u0000\uE3FE\u8EA2\uC2EF" + // 23215 - 23219 + "\u8EA2\uC2F3\u0000\uE8E6\u0000\uE3F5\u8EA2\uC2EC\u8EA2\uC2ED" + // 23220 - 23224 + "\u0000\uE8E8\u0000\uE3FD\u0000\uE4A1\u0000\uE3FC\u8EA2\uC2F5" + // 23225 - 23229 + "\u0000\u0000\u0000\u0000\u8EA2\uCAAF\u8EA2\uCAB0\u8EA2\uCAB5" + // 23230 - 23234 + "\u0000\uECE7\u8EA2\uCABC\u0000\u0000\u0000\uE8EE\u0000\u0000" + // 23235 - 23239 + "\u0000\uE8ED\u8EA2\uCAB7\u0000\u0000\u0000\uECE6\u8EA2\uCAAC" + // 23240 - 23244 + "\u0000\uE8EC\u0000\u0000\u8EA2\uCABB\u0000\uE8EA\u0000\uE8EB" + // 23245 - 23249 + "\u0000\u0000\u8EA2\uCAB8\u8EA2\uD0D1\u0000\uECE4\u0000\uDFB5" + // 23250 - 23254 + "\u0000\u0000\u8EA2\uCABA\u0000\uECEB\u8EA2\uCAB2\u0000\u0000" + // 23255 - 23259 + "\u0000\uECE5\u0000\u0000\u0000\uECEA\u8EA2\uCAAD\u8EA2\uCAB1" + // 23260 - 23264 + "\u8EA2\uCAAE\u8EA2\uCAB4\u0000\uE8EF\u0000\uECE3\u0000\uE8E9" + // 23265 - 23269 + "\u0000\u0000\u8EA2\uD0C3\u8EA2\uCAB6\u0000\u0000\u0000\uECE9" + // 23270 - 23274 + "\u0000\u0000\u0000\uECEC\u0000\u0000\u8EA2\uB8E6\u0000\u0000" + // 23275 - 23279 + "\u8EA2\uB8ED\u0000\u0000\u0000\u0000\u0000\uDDA6\u8EA2\uB8FB" + // 23280 - 23284 + "\u8EA2\uB8F6\u0000\uDDA1\u0000\uDCFD\u8EA2\uB8F2\u8EA2\uB8EE" + // 23285 - 23289 + "\u8EA2\uB8E0\u0000\uDCF4\u8EA2\uB8E5\u8EA2\uB8DD\u0000\u0000" + // 23290 - 23294 + "\u0000\uDCFC\u0000\uDCFE\u8EA2\uB8EA\u8EA2\uB8E7\u0000\uDCF9" + // 23295 - 23299 + "\u8EA2\uB8F7\u0000\uDCF6\u8EA2\uB8E8\u8EA2\uBFEA\u8EA2\uB8F0" + // 23300 - 23304 + "\u0000\u0000\u8EA2\uB8EF\u0000\uDCF5\u0000\u0000\u0000\uDDA2" + // 23305 - 23309 + "\u0000\u0000\u0000\uDCF8\u8EA2\uB8DE\u8EA2\uB8E3\u8EA2\uB8F4" + // 23310 - 23314 + "\u8EA2\uB8FA\u0000\uDDA7\u8EA2\uB8DF\u8EA2\uB8EB\u8EA2\uB8F1" + // 23315 - 23319 + "\u0000\uDCFB\u0000\u0000\u0000\u0000\u8EA2\uB8FC\u8EA2\uB8F5" + // 23320 - 23324 + "\u8EA2\uB8F8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23325 - 23329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23330 - 23334 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDCFA\u0000\u0000" + // 23335 - 23339 + "\u8EA2\uBFD1\u0000\uDFBA\u0000\uE3FA\u0000\u0000\u8EA2\uBBED" + // 23340 - 23344 + "\u8EA2\uBBF2\u8EA2\uBBE6\u0000\uE4A6\u0000\u0000\u0000\uE3FB" + // 23345 - 23349 + "\u0000\uDFB9\u8EA2\uBBE4\u8EA2\uBBF3\u0000\u0000\u0000\uE4A5" + // 23350 - 23354 + "\u0000\uDFB7\u0000\uE3F4\u8EA2\uBBF0\u0000\u0000\u0000\uDFBB" + // 23355 - 23359 + "\u8EA2\uBBE8\u8EA2\uBBE2\u0000\uDFB1\u8EA2\uBBE5\u0000\u0000" + // 23360 - 23364 + "\u8EA2\uBBEE\u0000\u0000\u0000\uE3F3\u0000\uE3F8\u0000\uDFAE" + // 23365 - 23369 + "\u8EA2\uBBEF\u0000\u0000\u0000\uE3F6\u0000\u0000\u0000\u0000" + // 23370 - 23374 + "\u0000\u0000\u0000\uDFAF\u0000\u0000\u0000\u0000\u0000\u0000" + // 23375 - 23379 + "\u0000\uE4A4\u8EA2\uCAAA\u8EA2\uC2F6\u0000\u0000\u8EA2\uC2EE" + // 23380 - 23384 + "\u8EA2\uCAA9\u0000\u0000\u8EA2\uC2F2\u8EA2\uC2F4\u0000\u0000" + // 23385 - 23389 + "\u0000\u0000\u8EA2\uC2F1\u0000\u0000\u0000\uE4A3\u0000\u0000" + // 23390 - 23394 + "\u8EA2\uC2F0\u0000\uE4A7\u0000\u0000\u0000\u0000\u0000\u0000" + // 23395 - 23399 + "\u8EA2\uCAB9\u0000\u0000\u0000\u0000\u0000\uE4A2\u0000\uE8E7" + // 23400 - 23404 + "\u8EA2\uB5C9\u8EA2\uBBEC\u0000\u0000\u8EA2\uB5C7\u8EA2\uBBE1" + // 23405 - 23409 + "\u0000\uDAC5\u0000\uDACC\u0000\uDAD0\u8EA2\uB5C1\u8EA2\uBBDC" + // 23410 - 23414 + "\u8EA2\uB5C5\u0000\uDAC1\u8EA2\uBBDE\u8EA2\uB5C8\u8EA2\uBBE0" + // 23415 - 23419 + "\u8EA2\uB5BD\u0000\u0000\u0000\uDFA9\u0000\u0000\u8EA2\uB5BB" + // 23420 - 23424 + "\u8EA2\uB5BC\u0000\uDACB\u0000\u0000\u0000\u0000\u0000\uDACA" + // 23425 - 23429 + "\u8EA2\uB5BF\u0000\uDACF\u8EA2\uB5C3\u0000\uDAC8\u8EA2\uB5C0" + // 23430 - 23434 + "\u0000\u0000\u0000\uDACD\u0000\uDFAD\u0000\uDFAA\u8EA2\uBBDF" + // 23435 - 23439 + "\u0000\u0000\u8EA2\uB5BE\u0000\u0000\u0000\uDAC3\u0000\u0000" + // 23440 - 23444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23445 - 23449 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDFB2\u0000\uDFB6" + // 23450 - 23454 + "\u8EA2\uBBE3\u0000\uE3F7\u0000\uDFB4\u8EA2\uBBE7\u0000\uDFB8" + // 23455 - 23459 + "\u8EA2\uC2EB\u8EA2\uBBE9\u0000\uE3F9\u0000\uDFB0\u0000\uDFB3" + // 23460 - 23464 + "\u8EA2\uBBEA\u0000\u0000\u8EA2\uBBEB\u8EA2\uBBF1\u8EA2\uAFA6" + // 23465 - 23469 + "\u8EA2\uAFA8\u0000\u0000\u8EA2\uAFAA\u0000\uD5CA\u0000\uD5D0" + // 23470 - 23474 + "\u8EA2\uB5B7\u8EA2\uAFAE\u8EA2\uAFA5\u0000\uDABE\u8EA2\uB5B5" + // 23475 - 23479 + "\u0000\u0000\u0000\uD5CF\u0000\uD5CD\u8EA2\uAFB0\u0000\u0000" + // 23480 - 23484 + "\u8EA2\uB5B6\u0000\u0000\u8EA2\uAFA7\u0000\u0000\u0000\uD5CE" + // 23485 - 23489 + "\u8EA2\uAFAB\u0000\uD5D1\u8EA2\uAFAD\u0000\u0000\u0000\u0000" + // 23490 - 23494 + "\u0000\uD5CC\u8EA2\uAFAC\u8EA2\uAFAF\u8EA2\uAFA9\u0000\u0000" + // 23495 - 23499 + "\u0000\uD5CB\u0000\uDABF\u0000\u0000\u8EA2\uAFA4\u0000\uDABD" + // 23500 - 23504 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAC0" + // 23505 - 23509 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23510 - 23514 + "\u0000\u0000\u0000\u0000\u8EA2\uB5B9\u8EA2\uB5C2\u0000\uDFAB" + // 23515 - 23519 + "\u0000\u0000\u0000\uDAC2\u0000\uDAC7\u0000\uDFAC\u8EA2\uB5C4" + // 23520 - 23524 + "\u0000\uDACE\u8EA2\uBBDD\u8EA2\uB5BA\u0000\uDAC6\u0000\uDAC9" + // 23525 - 23529 + "\u0000\uDAC4\u8EA2\uB5B8\u8EA2\uB5C6\u8EA2\uAACB\u8EA2\uAFA3" + // 23530 - 23534 + "\u8EA2\uAACC\u0000\uD1B2\u8EA2\uAACF\u8EA2\uAAC5\u0000\uD1B1" + // 23535 - 23539 + "\u8EA2\uAAC7\u0000\u0000\u8EA2\uAAC8\u0000\u0000\u0000\u0000" + // 23540 - 23544 + "\u8EA2\uAACA\u0000\uD1AD\u0000\u0000\u0000\u0000\u0000\uD5C5" + // 23545 - 23549 + "\u0000\u0000\u0000\u0000\u8EA2\uAAC6\u8EA2\uAAC2\u0000\uD5C6" + // 23550 - 23554 + "\u0000\u0000\u0000\u0000\u8EA2\uAAD0\u0000\uD5C2\u8EA2\uAFA1" + // 23555 - 23559 + "\u8EA2\uAAC9\u0000\u0000\u8EA2\uAEFE\u8EA2\uAAC4\u8EA2\uAACD" + // 23560 - 23564 + "\u0000\u0000\u0000\u0000\u0000\uD1B0\u0000\uD5C3\u0000\uD1B6" + // 23565 - 23569 + "\u0000\uD5C4\u8EA2\uAAD1\u8EA2\uAFA2\u0000\uD1AF\u0000\uD5C8" + // 23570 - 23574 + "\u0000\uD1B5\u0000\uD1B4\u0000\uD1B3\u0000\uD5C7\u8EA2\uAAD2" + // 23575 - 23579 + "\u0000\uD5C9\u0000\uD1AE\u0000\u0000\u8EA2\uAAC3\u0000\u0000" + // 23580 - 23584 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23585 - 23589 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23590 - 23594 + "\u0000\u0000\u0000\uDABC\u8EA2\uA4A6\u0000\u0000\u0000\u0000" + // 23595 - 23599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23600 - 23604 + "\u0000\u0000\u8EA2\uA6EE\u8EA2\uA6E1\u8EA2\uA6DF\u8EA2\uA6ED" + // 23605 - 23609 + "\u8EA2\uA6E8\u0000\uD1AB\u0000\uCDC4\u8EA2\uA6E9\u8EA2\uA6EC" + // 23610 - 23614 + "\u0000\uD1A7\u8EA2\uA6EB\u0000\uCDC5\u0000\uCDCA\u0000\uCDC8" + // 23615 - 23619 + "\u8EA2\uA6E2\u0000\u0000\u8EA2\uA6DD\u8EA2\uA6E4\u0000\uCDCF" + // 23620 - 23624 + "\u8EA2\uA6EF\u0000\uD1A8\u8EA2\uA6E5\u0000\u0000\u0000\uD1A9" + // 23625 - 23629 + "\u0000\uCDCB\u8EA2\uA6E7\u0000\u0000\u8EA2\uAACE\u0000\uD1AA" + // 23630 - 23634 + "\u8EA2\uA6DC\u0000\uCDCC\u0000\uD1AC\u0000\uCDCD\u0000\uCDC9" + // 23635 - 23639 + "\u0000\uCDCE\u8EA2\uA6E6\u8EA2\uA6DB\u8EA2\uA6EA\u0000\uCDC6" + // 23640 - 23644 + "\u0000\u0000\u0000\u0000\u8EA2\uA6DE\u8EA2\uA6E3\u8EA2\uA6E0" + // 23645 - 23649 + "\u0000\uCDC7\u0000\u0000\u8EA2\uAAC0\u0000\u0000\u8EA2\uAAC1" + // 23650 - 23654 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23655 - 23659 + "\u0000\u0000\u8EA2\uA4BE\u0000\uCADF\u8EA2\uA4BF\u8EA2\uA4C3" + // 23660 - 23664 + "\u0000\uCADE\u0000\u0000\u8EA2\uA4C7\u8EA2\uA4C2\u8EA2\uA4C5" + // 23665 - 23669 + "\u0000\uCAD7\u0000\uCAD8\u0000\uCAD9\u0000\uCADA\u0000\u0000" + // 23670 - 23674 + "\u0000\uCAE1\u0000\u0000\u8EA2\uA4C1\u0000\uCADC\u0000\uCAE2" + // 23675 - 23679 + "\u0000\u0000\u8EA2\uA4C0\u8EA2\uA4C6\u0000\u0000\u0000\uCADB" + // 23680 - 23684 + "\u8EA2\uA4C4\u0000\uCADD\u0000\uCAD6\u0000\uCAE0\u0000\u0000" + // 23685 - 23689 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 23690 - 23694 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCEBA\u0000\u0000" + // 23695 - 23699 + "\u8EA2\uA7B5\u0000\uCEA6\u0000\u0000\u0000\uCEB0\u0000\uCEB1" + // 23700 - 23704 + "\u0000\uCEA9\u0000\uCEBB\u0000\uCEAB\u8EA2\uA7BD\u0000\uCEB6" + // 23705 - 23709 + "\u8EA2\uA7B8\u0000\uCEAC\u8EA2\uA7C7\u8EA2\uA7C8\u8EA2\uA7C0" + // 23710 - 23714 + "\u8EA2\uA7B9\u0000\uCEB9\u8EA2\uA7C5\u0000\uCEB4\u0000\uCEB2" + // 23715 - 23719 + "\u8EA2\uE8F4\u8EA2\uE8F5\u0000\u0000\u0000\uC5C0\u0000\u0000" + // 23720 - 23724 + "\u0000\uC6D2\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA1DC" + // 23725 - 23729 + "\u0000\u0000\u0000\u0000\u0000\uCAB4\u0000\uCAB6\u0000\u0000" + // 23730 - 23734 + "\u8EA2\uA2C2\u8EA2\uA3F8\u8EA2\uA3F7\u8EA2\uA3F6\u0000\u0000" + // 23735 - 23739 + "\u8EA2\uA2C1\u8EA2\uA2C0\u0000\uC8A5\u0000\uCAB5\u0000\uCAB3" + // 23740 - 23744 + "\u0000\uC8A4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDBF" + // 23745 - 23749 + "\u8EA2\uA6D9\u0000\u0000\u0000\uCDC0\u8EA2\uA3FD\u0000\u0000" + // 23750 - 23754 + "\u8EA2\uA4A1\u8EA2\uA3FE\u8EA2\uA6DA\u0000\u0000\u0000\u0000" + // 23755 - 23759 + "\u8EA2\uA3FA\u0000\u0000\u0000\uCABA\u0000\uCAB8\u0000\u0000" + // 23760 - 23764 + "\u8EA2\uA3F9\u8EA2\uA3FB\u8EA2\uA4A3\u0000\u0000\u0000\uCAB7" + // 23765 - 23769 + "\u0000\u0000\u8EA2\uA3FC\u8EA2\uA4A7\u0000\uCDC2\u0000\u0000" + // 23770 - 23774 + "\u8EA2\uA4A4\u0000\uCAB9\u0000\u0000\u8EA2\uA4A2\u8EA2\uA4A5" + // 23775 - 23779 + "\u0000\u0000\u0000\uCDC1\u0000\u0000\u0000\uCDC3\u0000\uCDBB" + // 23780 - 23784 + "\u0000\uCDBC\u8EA2\uA6D6\u0000\u0000\u0000\u0000\u0000\uD1A1" + // 23785 - 23789 + "\u8EA2\uAABF\u0000\uD1A4\u0000\uD0FE\u0000\uD1A6\u0000\uD1A2" + // 23790 - 23794 + "\u0000\uD1A3\u0000\uD1A5\u0000\u0000\u0000\u0000\u0000\u0000" + // 23795 - 23799 + "\u0000\uD5C1\u0000\uD5C0\u0000\uD5BF\u0000\u0000\u0000\u0000" + // 23800 - 23804 + "\u0000\u0000\u8EA2\uB5B3\u0000\uDAB5\u0000\uDAB8\u0000\uDAB6" + // 23805 - 23809 + "\u0000\u0000\u8EA2\uB5B2\u0000\uDABB\u0000\u0000\u0000\uDAB7" + // 23810 - 23814 + "\u8EA2\uB5B4\u0000\uDABA\u0000\uDAB9\u0000\u0000\u0000\u0000" + // 23815 - 23819 + "\u0000\u0000\u8EA2\uBBDA\u8EA2\uBBD9\u0000\u0000\u0000\uDFA8" + // 23820 - 23824 + "\u0000\uDFA6\u0000\uDFA7\u8EA2\uBBDB\u0000\uE3F1\u8EA2\uC2EA" + // 23825 - 23829 + "\u0000\uE3F2\u8EA2\uC2E9\u0000\u0000\u0000\u0000\u8EA2\uD0C0" + // 23830 - 23834 + "\u0000\u0000\u0000\u0000\u0000\uECE2\u8EA2\uCAA8\u0000\uECE1" + // 23835 - 23839 + "\u0000\u0000\u0000\uE8E5\u0000\u0000\u8EA2\uD7A2\u8EA2\uD7A1" + // 23840 - 23844 + "\u0000\uF3DB\u8EA2\uDCE9\u8EA2\uE5CB\u8EA2\uC2E8\u0000\u0000" + // 23845 - 23849 + "\u0000\u0000\u8EA2\uCAA6\u8EA2\uCAA5\u0000\u0000\u0000\uE8E3" + // 23850 - 23854 + "\u0000\u0000\u0000\uECDF\u8EA2\uD0BF\u0000\uF0D3\u8EA2\uD6FE" + // 23855 - 23859 + "\u0000\uF3DA\u0000\u0000\u0000\uFBDF\u8EA2\uEFA7\u0000\uA7D9" + // 23860 - 23864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA6D5\u0000\u0000" + // 23865 - 23869 + "\u8EA2\uAABE\u0000\uDAB1\u8EA2\uBBD8\u0000\uE3F0\u0000\u0000" + // 23870 - 23874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF6C1\u0000\u0000" + // 23875 - 23879 + "\u0000\u0000\u0000\uA7DA\u0000\uCAB0\u0000\u0000\u0000\uCAAF" + // 23880 - 23884 + "\u0000\uD0FD\u0000\u0000\u8EA2\uAEFD\u0000\u0000\u0000\uDAB3" + // 23885 - 23889 + "\u0000\uDDA9\u0000\uDAB4\u0000\uDAB2\u0000\uDFA5\u0000\u0000" + // 23890 - 23894 + "\u8EA2\uCAA7\u0000\uE8E4\u0000\uECE0\u0000\u0000\u8EA2\uA1AC" + // 23895 - 23899 + "\u8EA2\uA2BF\u0000\u0000\u8EA2\uA3F5\u0000\uCAB1\u8EA2\uA3F4" + // 23900 - 23904 + "\u0000\uCAB2\u0000\u0000\u0000\u0000\u0000\uCDBE\u8EA2\uA6D8" + // 23905 - 23909 + "\u8EA2\uA6D7\u0000\uCDBD\u8EA2\uA1DA\u0000\u0000\u8EA2\uA1DB" + // 23910 - 23914 + "\u0000\u0000\u8EA2\uA2BC\u0000\u0000\u0000\u0000\u0000\uCAAA" + // 23915 - 23919 + "\u8EA2\uA3F0\u8EA2\uA3EE\u0000\uCAAB\u8EA2\uA3ED\u8EA2\uA3EF" + // 23920 - 23924 + "\u8EA2\uA3F1\u0000\u0000\u0000\uCAA9\u0000\u0000\u0000\u0000" + // 23925 - 23929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCDB5\u0000\uCDB6" + // 23930 - 23934 + "\u0000\uCDB3\u0000\u0000\u0000\u0000\u0000\uCDB2\u8EA2\uAAB9" + // 23935 - 23939 + "\u0000\uCDB4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0F8" + // 23940 - 23944 + "\u0000\u0000\u8EA2\uAAB8\u8EA2\uAABA\u8EA2\uAAB7\u8EA2\uAABB" + // 23945 - 23949 + "\u0000\uD0F9\u0000\uD5BD\u8EA2\uAEF7\u0000\u0000\u8EA2\uAEF9" + // 23950 - 23954 + "\u0000\uD5BB\u8EA2\uAEFA\u0000\uD5BC\u8EA2\uAEF8\u0000\u0000" + // 23955 - 23959 + "\u8EA2\uAAB6\u8EA2\uB5AB\u8EA2\uB5AE\u8EA2\uB5AF\u8EA2\uB5AC" + // 23960 - 23964 + "\u0000\uDAAD\u0000\uDAAC\u0000\uDAAA\u0000\uDAAB\u8EA2\uB5AD" + // 23965 - 23969 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDAAE" + // 23970 - 23974 + "\u0000\u0000\u0000\uE5DB\u8EA2\uC5DA\u0000\u0000\u0000\u0000" + // 23975 - 23979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCCC8\u0000\u0000" + // 23980 - 23984 + "\u8EA2\uCCC1\u8EA2\uCCC2\u0000\u0000\u0000\uE9FE\u0000\uE9FC" + // 23985 - 23989 + "\u0000\uE9FD\u8EA2\uCCCA\u0000\u0000\u8EA2\uCCC9\u8EA2\uCCC6" + // 23990 - 23994 + "\u8EA2\uCCC5\u8EA2\uCCC4\u8EA2\uCCC3\u8EA2\uCCC7\u8EA2\uCCC0" + // 23995 - 23999 + "\u0000\u0000\u8EA2\uD2D3\u8EA2\uD2D8\u8EA2\uD2D6\u0000\u0000" + // 24000 - 24004 + "\u0000\u0000\u0000\u0000\u8EA2\uD2D5\u8EA2\uD2D4\u8EA2\uD2D7" + // 24005 - 24009 + "\u0000\u0000\u8EA2\uD8F8\u0000\u0000\u0000\uF1D7\u0000\uF4D2" + // 24010 - 24014 + "\u8EA2\uDDFB\u0000\u0000\u0000\uF4D3\u8EA2\uDDFA\u8EA2\uE2CB" + // 24015 - 24019 + "\u8EA2\uE2CA\u0000\uF6F1\u0000\u0000\u8EA2\uE6AF\u0000\uF8D6" + // 24020 - 24024 + "\u8EA2\uE9CE\u0000\uFBEB\u8EA2\uEDE5\u0000\u0000\u8EA2\uF0C5" + // 24025 - 24029 + "\u8EA2\uF0C4\u8EA2\uA1E7\u0000\uD2ED\u0000\uD2EE\u8EA2\uB7E6" + // 24030 - 24034 + "\u0000\u0000\u0000\uE5DD\u0000\uC6FD\u0000\uCBB5\u0000\uDEFB" + // 24035 - 24039 + "\u8EA2\uBBD7\u0000\u0000\u0000\uDEFC\u8EA2\uBBD6\u0000\uDEF9" + // 24040 - 24044 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC2E0" + // 24045 - 24049 + "\u8EA2\uC2E2\u0000\uE3EB\u8EA2\uC2E1\u8EA2\uC2DF\u8EA2\uC2DE" + // 24050 - 24054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC9F9\u0000\uE8DF" + // 24055 - 24059 + "\u0000\uE8DD\u0000\u0000\u0000\uE8DE\u8EA2\uC9F7\u8EA2\uC9F8" + // 24060 - 24064 + "\u0000\u0000\u0000\uE8DB\u8EA2\uD0BA\u8EA2\uD0B8\u0000\u0000" + // 24065 - 24069 + "\u0000\uECD7\u8EA2\uD0B9\u0000\uECD8\u0000\uECD6\u0000\uE8DC" + // 24070 - 24074 + "\u0000\u0000\u0000\u0000\u8EA2\uD6F7\u8EA2\uD6F5\u8EA2\uD6F6" + // 24075 - 24079 + "\u8EA2\uD0B7\u8EA2\uDCE8\u0000\uF3D9\u8EA2\uDCE7\u8EA2\uE1D1" + // 24080 - 24084 + "\u8EA2\uE1D2\u8EA2\uD6F8\u8EA2\uE5CA\u0000\u0000\u0000\uC4DB" + // 24085 - 24089 + "\u0000\uC6CD\u0000\uC8A1\u8EA2\uA2BB\u0000\uC7FE\u0000\u0000" + // 24090 - 24094 + "\u0000\uCDB1\u0000\uE3EC\u0000\uA7D3\u0000\uC5BC\u0000\uC6CE" + // 24095 - 24099 + "\u0000\uD0F7\u0000\uDEFD\u0000\uA7D4\u8EA2\uE1D0\u0000\u0000" + // 24100 - 24104 + "\u8EA2\uE4BE\u8EA2\uE5C9\u0000\u0000\u0000\u0000\u8EA2\uE8F0" + // 24105 - 24109 + "\u8EA2\uE8F1\u0000\u0000\u0000\uF9EB\u0000\u0000\u8EA2\uEBD7" + // 24110 - 24114 + "\u0000\u0000\u0000\uFAE9\u0000\u0000\u8EA2\uEBD8\u0000\u0000" + // 24115 - 24119 + "\u8EA2\uEDCC\u0000\uFBDE\u0000\u0000\u0000\uFBDD\u8EA2\uEDCB" + // 24120 - 24124 + "\u0000\uFCB5\u0000\u0000\u8EA2\uEFA5\u0000\u0000\u0000\u0000" + // 24125 - 24129 + "\u0000\uA7CE\u0000\u0000\u0000\uC4D5\u0000\uC7FC\u8EA2\uA2BA" + // 24130 - 24134 + "\u8EA2\uA3EA\u0000\uCBD6\u0000\uDAA5\u0000\u0000\u0000\u0000" + // 24135 - 24139 + "\u0000\uC4D6\u0000\uC6CA\u0000\uC6C9\u0000\uC6C8\u0000\u0000" + // 24140 - 24144 + "\u0000\u0000\u0000\uCAA7\u0000\u0000\u0000\u0000\u0000\uD5B8" + // 24145 - 24149 + "\u0000\u0000\u8EA2\uC2DD\u0000\uC4D7\u0000\uC4D8\u0000\uC4D9" + // 24150 - 24154 + "\u0000\uC5BB\u0000\u0000\u0000\u0000\u0000\uD0F3\u0000\u0000" + // 24155 - 24159 + "\u8EA2\uAAB0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEF8" + // 24160 - 24164 + "\u0000\uC4DA\u8EA2\uA1BD\u8EA2\uC9E9\u8EA2\uC9ED\u8EA2\uC9E8" + // 24165 - 24169 + "\u0000\u0000\u0000\uE8D9\u0000\u0000\u8EA2\uC9EB\u0000\uE8DA" + // 24170 - 24174 + "\u8EA2\uC9F1\u8EA2\uC9F0\u8EA2\uC9EF\u0000\u0000\u0000\u0000" + // 24175 - 24179 + "\u8EA2\uC9F4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24180 - 24184 + "\u8EA2\uD0AE\u8EA2\uD0B0\u0000\uECD5\u8EA2\uD0B1\u0000\u0000" + // 24185 - 24189 + "\u8EA2\uD0AC\u0000\u0000\u8EA2\uD0AB\u8EA2\uD0B5\u0000\u0000" + // 24190 - 24194 + "\u8EA2\uD0B3\u0000\uECD4\u8EA2\uD0B6\u8EA2\uD0AD\u8EA2\uD0B2" + // 24195 - 24199 + "\u8EA2\uD0B4\u8EA2\uD0AF\u0000\u0000\u0000\u0000\u0000\u0000" + // 24200 - 24204 + "\u0000\u0000\u8EA2\uD6EB\u8EA2\uD6F0\u8EA2\uD6EA\u8EA2\uD6EF" + // 24205 - 24209 + "\u0000\u0000\u8EA2\uD6E8\u8EA2\uD6F2\u8EA2\uD6EE\u8EA2\uD6F3" + // 24210 - 24214 + "\u8EA2\uD6ED\u8EA2\uD6E9\u8EA2\uD6F1\u0000\u0000\u8EA2\uD6F4" + // 24215 - 24219 + "\u8EA2\uD6EC\u0000\u0000\u8EA2\uDCE6\u0000\uF3D8\u0000\u0000" + // 24220 - 24224 + "\u0000\uF3D6\u0000\u0000\u0000\uF3D5\u0000\uF3D7\u0000\u0000" + // 24225 - 24229 + "\u0000\u0000\u0000\uA3A4\u0000\uA3A5\u0000\uA3A6\u0000\uA3A7" + // 24230 - 24234 + "\u0000\uA3A8\u0000\uA3A9\u0000\uA3AA\u0000\uA3AB\u0000\uA3B2" + // 24235 - 24239 + "\u0000\uA3B1\u0000\uA3B0\u0000\uA3AF\u0000\uA3AE\u0000\uA3AD" + // 24240 - 24244 + "\u0000\uA3AC\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24245 - 24249 + "\u0000\uA3B8\u0000\uA3BB\u0000\u0000\u0000\u0000\u0000\u0000" + // 24250 - 24254 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24255 - 24259 + "\u0000\u0000\u0000\u0000\u0000\uA1FC\u0000\uA1FB\u0000\u0000" + // 24260 - 24264 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24265 - 24269 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24270 - 24274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24275 - 24279 + "\u0000\uA1F5\u0000\uA1F4\u0000\u0000\u0000\u0000\u0000\u0000" + // 24280 - 24284 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24285 - 24289 + "\u0000\uA1FE\u0000\uA1FD\u0000\u0000\u0000\u0000\u8EA2\uAFFD" + // 24290 - 24294 + "\u0000\u0000\u0000\uE5FE\u0000\uE1C8\u0000\uE5FD\u0000\uEACE" + // 24295 - 24299 + "\u0000\u0000\u0000\uC8D8\u8EA2\uA1E8\u0000\uC8D9\u0000\uC8DA" + // 24300 - 24304 + "\u0000\u0000\u0000\u0000\u8EA2\uA8CB\u8EA2\uA4F6\u0000\u0000" + // 24305 - 24309 + "\u8EA2\uA4F7\u0000\uCBBA\u0000\u0000\u8EA2\uA4F4\u0000\uCBB9" + // 24310 - 24314 + "\u0000\u0000\u0000\uCBBC\u8EA2\uA4F5\u0000\uCBBE\u0000\uCBBD" + // 24315 - 24319 + "\u8EA2\uA4F8\u0000\uCBBB\u0000\u0000\u0000\u0000\u0000\u0000" + // 24320 - 24324 + "\u0000\uCFB4\u0000\uCFB2\u8EA2\uA8CD\u0000\u0000\u0000\uCFB1" + // 24325 - 24329 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFB6\u0000\uCFB8" + // 24330 - 24334 + "\u0000\uCFB5\u0000\u0000\u8EA2\uA8D0\u8EA2\uA8CC\u0000\uCFB9" + // 24335 - 24339 + "\u0000\u0000\u0000\uCFB3\u0000\uCBBF\u0000\u0000\u0000\uCFB7" + // 24340 - 24344 + "\u8EA2\uA8CF\u0000\u0000\u0000\u0000\u8EA2\uA8CE\u0000\u0000" + // 24345 - 24349 + "\u0000\uCFB0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24350 - 24354 + "\u0000\u0000\u0000\uA2EA\u0000\u0000\u0000\uA2A2\u0000\u0000" + // 24355 - 24359 + "\u0000\u0000\u0000\u0000\u0000\uA2EB\u0000\u0000\u0000\u0000" + // 24360 - 24364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24365 - 24369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24370 - 24374 + "\u8EAD\uA4C5\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24375 - 24379 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24380 - 24384 + "\u0000\u0000\u8EAD\uA4C6\u0000\u0000\u0000\u0000\u0000\u0000" + // 24385 - 24389 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24390 - 24394 + "\u0000\u0000\u8EAD\uA1CA\u0000\u0000\u0000\u0000\u0000\u0000" + // 24395 - 24399 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24400 - 24404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24405 - 24409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24410 - 24414 + "\u0000\u0000\u0000\u0000\u8EAD\uA1AF\u0000\u0000\u0000\u0000" + // 24415 - 24419 + "\u0000\u0000\u0000\uA1B7\u0000\u0000\u0000\u0000\u0000\u0000" + // 24420 - 24424 + "\u0000\uA1E4\u0000\uA1E5\u0000\u0000\u0000\u0000\u0000\uA1E6" + // 24425 - 24429 + "\u0000\uA1E7\u0000\u0000\u0000\u0000\u8EAD\uA1CF\u8EAD\uA1D0" + // 24430 - 24434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uA1AD\u0000\uA1AC" + // 24435 - 24439 + "\u0000\uA1A6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24440 - 24444 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EAD\uA1CB" + // 24445 - 24449 + "\u0000\u0000\u0000\uA1EB\u8EAD\uA1B3\u0000\u0000\u0000\uA1EA" + // 24450 - 24454 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24455 - 24459 + "\u0000\uA1EF\u0000\u0000\u0000\u0000\u0000\uA2A3\u0000\u0000" + // 24460 - 24464 + "\u0000\uF4D1\u8EA2\uDDF1\u0000\u0000\u8EA2\uDDF9\u8EA2\uDDF6" + // 24465 - 24469 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE2C6\u0000\u0000" + // 24470 - 24474 + "\u0000\u0000\u8EA2\uE2C3\u0000\u0000\u0000\uF6F0\u0000\u0000" + // 24475 - 24479 + "\u8EA2\uE2C7\u8EA2\uE2C9\u8EA2\uE2C8\u8EA2\uE2C4\u8EA2\uE2C5" + // 24480 - 24484 + "\u0000\u0000\u0000\u0000\u8EA2\uE6AD\u0000\u0000\u0000\uF8D5" + // 24485 - 24489 + "\u0000\u0000\u8EA2\uE6AA\u8EA2\uE6AC\u8EA2\uE6A9\u8EA2\uE6AE" + // 24490 - 24494 + "\u0000\u0000\u0000\u0000\u8EA2\uE6AB\u0000\u0000\u8EA2\uE9C9" + // 24495 - 24499 + "\u8EA2\uE9CC\u8EA2\uE9C8\u0000\uF9FC\u8EA2\uE9CA\u8EA2\uE9CB" + // 24500 - 24504 + "\u8EA2\uE9CD\u0000\uF9FD\u0000\uFAA1\u0000\uF9FE\u8EA2\uEBEF" + // 24505 - 24509 + "\u0000\u0000\u8EA2\uEBF1\u0000\u0000\u8EA2\uEBF0\u0000\u0000" + // 24510 - 24514 + "\u0000\u0000\u0000\u0000\u8EA2\uEDE4\u0000\u0000\u0000\u0000" + // 24515 - 24519 + "\u8EA2\uF0C3\u8EA2\uF1AA\u0000\uC6FC\u0000\u0000\u0000\u0000" + // 24520 - 24524 + "\u8EA2\uA4F2\u0000\uCFA3\u8EA2\uA8C3\u8EA2\uBBD4\u8EA2\uBBC0" + // 24525 - 24529 + "\u8EA2\uBBCD\u8EA2\uBBBE\u0000\u0000\u8EA2\uBBD5\u0000\u0000" + // 24530 - 24534 + "\u0000\uDEF7\u0000\u0000\u8EA2\uBBCF\u8EA2\uC2D5\u8EA2\uBBC1" + // 24535 - 24539 + "\u0000\uDEF4\u0000\u0000\u8EA2\uBBC5\u0000\u0000\u0000\uDEF5" + // 24540 - 24544 + "\u8EA2\uBBC4\u8EA2\uBBC9\u0000\u0000\u0000\u0000\u8EA2\uBBC6" + // 24545 - 24549 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBBCC\u0000\u0000" + // 24550 - 24554 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC2D9\u0000\u0000" + // 24555 - 24559 + "\u0000\u0000\u0000\u0000\u8EA2\uC2DC\u8EA2\uC2D4\u0000\u0000" + // 24560 - 24564 + "\u8EA2\uC2D6\u0000\u0000\u8EA2\uC2DB\u8EA2\uC2DA\u0000\uE3E9" + // 24565 - 24569 + "\u0000\u0000\u8EA2\uBBBF\u8EA2\uC2D8\u0000\u0000\u0000\u0000" + // 24570 - 24574 + "\u0000\uE3EA\u0000\u0000\u8EA2\uC2D3\u8EA2\uC2D7\u0000\u0000" + // 24575 - 24579 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC9EE\u0000\u0000" + // 24580 - 24584 + "\u8EA2\uC9F5\u8EA2\uC9EC\u0000\u0000\u8EA2\uC9F3\u8EA2\uC9EA" + // 24585 - 24589 + "\u8EA2\uC9F2\u8EA2\uC9F6\u8EA2\uAEF3\u0000\uD5B6\u0000\u0000" + // 24590 - 24594 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9F7\u0000\uD9F6" + // 24595 - 24599 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB4F8\u8EA2\uB4FC" + // 24600 - 24604 + "\u8EA2\uB4FE\u0000\uD9F8\u8EA2\uB5A3\u0000\u0000\u0000\uD9FC" + // 24605 - 24609 + "\u8EA2\uB5A5\u0000\u0000\u0000\uD9FE\u0000\u0000\u0000\uD9FA" + // 24610 - 24614 + "\u0000\uDAA4\u0000\u0000\u0000\uDAA1\u8EA2\uB4FA\u0000\uD9F9" + // 24615 - 24619 + "\u0000\u0000\u8EA2\uB4F9\u8EA2\uB4F7\u8EA2\uB5A7\u8EA2\uB4FB" + // 24620 - 24624 + "\u0000\u0000\u0000\uD9FB\u8EA2\uB5A6\u0000\uDAA2\u8EA2\uB5A2" + // 24625 - 24629 + "\u8EA2\uB5A1\u0000\uDAA3\u8EA2\uB4FD\u0000\uD9FD\u0000\u0000" + // 24630 - 24634 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB5A8\u0000\u0000" + // 24635 - 24639 + "\u8EA2\uB5A4\u8EA2\uBBCB\u8EA2\uBBD2\u8EA2\uBBC7\u0000\uDEF6" + // 24640 - 24644 + "\u8EA2\uBBC3\u8EA2\uBBD3\u8EA2\uBBBD\u8EA2\uBBD0\u8EA2\uBBCE" + // 24645 - 24649 + "\u8EA2\uBBC8\u0000\u0000\u8EA2\uBBD1\u8EA2\uBBCA\u0000\u0000" + // 24650 - 24654 + "\u8EA2\uBBC2\u0000\uC9FD\u0000\uC9FE\u0000\u0000\u0000\u0000" + // 24655 - 24659 + "\u8EA2\uA6BA\u0000\uCDA2\u0000\uCDA3\u8EA2\uA6BB\u0000\uCDA1" + // 24660 - 24664 + "\u0000\u0000\u0000\u0000\u0000\uD0F0\u8EA2\uA9F9\u0000\uD0EF" + // 24665 - 24669 + "\u0000\uD0ED\u0000\uD0EE\u0000\uD5AE\u0000\uD5AC\u0000\u0000" + // 24670 - 24674 + "\u0000\u0000\u8EA2\uAEED\u0000\uD5AD\u8EA2\uAEEC\u0000\u0000" + // 24675 - 24679 + "\u0000\uD5A2\u8EA2\uB4F6\u0000\u0000\u0000\u0000\u0000\uD9F4" + // 24680 - 24684 + "\u0000\uD9F5\u0000\u0000\u0000\u0000\u0000\uD9F3\u0000\u0000" + // 24685 - 24689 + "\u0000\uE8D8\u8EA2\uC9E7\u0000\uECD2\u0000\uECD3\u0000\u0000" + // 24690 - 24694 + "\u8EA2\uD0AA\u0000\uF3D4\u8EA2\uE1CE\u8EA2\uE1CF\u0000\u0000" + // 24695 - 24699 + "\u0000\uFAE8\u8EA2\uF0B6\u8EA2\uA1AB\u0000\uC5BA\u0000\u0000" + // 24700 - 24704 + "\u0000\uC4D4\u0000\u0000\u8EA2\uA1D8\u8EA2\uA1D7\u0000\u0000" + // 24705 - 24709 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7FB\u8EA2\uA2B7" + // 24710 - 24714 + "\u8EA2\uA2B8\u8EA2\uA2B6\u0000\u0000\u8EA2\uA2B9\u0000\u0000" + // 24715 - 24719 + "\u0000\uD2DC\u0000\u0000\u8EA2\uACC3\u8EA2\uACC4\u8EA2\uACC5" + // 24720 - 24724 + "\u0000\u0000\u0000\uD2E3\u8EA2\uACC2\u0000\uD2DF\u8EA2\uACC6" + // 24725 - 24729 + "\u0000\uD2E0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24730 - 24734 + "\u8EA2\uB1B2\u8EA2\uB1B6\u8EA2\uB1B4\u8EA2\uB1B3\u0000\u0000" + // 24735 - 24739 + "\u8EA2\uB1B7\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB1B8" + // 24740 - 24744 + "\u8EA2\uB1B9\u8EA2\uB1B1\u0000\u0000\u8EA2\uB1B0\u0000\u0000" + // 24745 - 24749 + "\u0000\uD6F8\u0000\uD6F9\u0000\u0000\u8EA2\uB1BA\u8EA2\uB1B5" + // 24750 - 24754 + "\u0000\u0000\u8EA2\uB7D3\u0000\u0000\u8EA2\uB1BB\u0000\uD6FA" + // 24755 - 24759 + "\u0000\uD6F7\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uB7CD" + // 24760 - 24764 + "\u0000\u0000\u8EA2\uB7CC\u0000\u0000\u8EA2\uB7CE\u8EA2\uB7CF" + // 24765 - 24769 + "\u8EA2\uB7D1\u8EA2\uB7D0\u8EA2\uB7D5\u0000\uDCBA\u0000\uDCB7" + // 24770 - 24774 + "\u0000\uDCBB\u8EA2\uB7CB\u0000\uDCBC\u8EA2\uB7D4\u0000\uDCB9" + // 24775 - 24779 + "\u8EA2\uB7D2\u0000\uDCB8\u0000\u0000\u0000\uE9F6\u0000\u0000" + // 24780 - 24784 + "\u8EA2\uD2D0\u8EA2\uD2D1\u0000\uEDFC\u0000\u0000\u0000\u0000" + // 24785 - 24789 + "\u8EA2\uD2CF\u8EA2\uD2D2\u0000\uEDFE\u0000\uEDFB\u8EA2\uD2CB" + // 24790 - 24794 + "\u8EA2\uD2C8\u8EA2\uD2CD\u8EA2\uD2CA\u0000\uEEA4\u8EA2\uD2CC" + // 24795 - 24799 + "\u0000\u0000\u0000\u0000\u8EA2\uD2CE\u0000\uEEA2\u0000\u0000" + // 24800 - 24804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF1D4\u8EA2\uD8F2" + // 24805 - 24809 + "\u0000\u0000\u8EA2\uD8EC\u8EA2\uD8F0\u8EA2\uD8F6\u8EA2\uD8F7" + // 24810 - 24814 + "\u8EA2\uD8F3\u8EA2\uD8F4\u8EA2\uD8F1\u0000\u0000\u8EA2\uD8EE" + // 24815 - 24819 + "\u0000\u0000\u0000\uF1D6\u0000\uF1D3\u8EA2\uD8ED\u8EA2\uD8EF" + // 24820 - 24824 + "\u0000\u0000\u0000\uF1D5\u8EA2\uD8F5\u0000\u0000\u0000\uF4D0" + // 24825 - 24829 + "\u0000\u0000\u0000\u0000\u8EA2\uDDF5\u8EA2\uDDEF\u0000\uF4CF" + // 24830 - 24834 + "\u0000\u0000\u0000\u0000\u0000\uF4CD\u0000\u0000\u8EA2\uDDF7" + // 24835 - 24839 + "\u0000\uF4CE\u8EA2\uDDF2\u8EA2\uDDF3\u8EA2\uDDF0\u8EA2\uDDF8" + // 24840 - 24844 + "\u8EA2\uDDF4\u8EA2\uB4F5\u8EA2\uB4F4\u0000\uD9ED\u0000\u0000" + // 24845 - 24849 + "\u0000\uD9EC\u0000\uD9EB\u0000\uD9EF\u0000\uD9EA\u0000\u0000" + // 24850 - 24854 + "\u0000\u0000\u8EA2\uBBB9\u8EA2\uBBB6\u0000\uDEEE\u8EA2\uBBB5" + // 24855 - 24859 + "\u8EA2\uBBBA\u0000\u0000\u0000\uDEF0\u8EA2\uBBB8\u0000\uDEED" + // 24860 - 24864 + "\u0000\uDEEF\u8EA2\uBBB7\u0000\u0000\u8EA2\uC2CE\u0000\u0000" + // 24865 - 24869 + "\u8EA2\uC2CF\u8EA2\uC2D0\u0000\u0000\u0000\u0000\u0000\u0000" + // 24870 - 24874 + "\u0000\u0000\u0000\uE8CE\u0000\uE8D6\u8EA2\uC9E5\u0000\uE8D0" + // 24875 - 24879 + "\u0000\uE8D4\u8EA2\uC9E6\u0000\uE8D5\u0000\uE8D1\u0000\uE8D2" + // 24880 - 24884 + "\u0000\uE8CF\u0000\uE8D3\u0000\uECD0\u8EA2\uBBB4\u0000\uECD1" + // 24885 - 24889 + "\u0000\uECCF\u0000\u0000\u0000\uECCE\u8EA2\uD6E7\u0000\uF0D1" + // 24890 - 24894 + "\u8EA2\uDCE4\u8EA2\uDCE5\u0000\u0000\u0000\u0000\u0000\uF8B1" + // 24895 - 24899 + "\u0000\uF9EA\u0000\u0000\u0000\uC4D0\u0000\u0000\u0000\uC7F9" + // 24900 - 24904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24905 - 24909 + "\u0000\uDBB9\u0000\u0000\u8EA2\uBCE6\u8EA2\uBCFC\u8EA2\uBCF1" + // 24910 - 24914 + "\u0000\uDFFB\u0000\u0000\u0000\uDFF6\u8EA2\uBCE0\u0000\uDFF7" + // 24915 - 24919 + "\u8EA2\uBCF0\u0000\u0000\u8EA2\uBCE9\u8EA2\uBCF6\u0000\uDFF3" + // 24920 - 24924 + "\u8EA2\uBCCE\u8EA2\uBCF8\u0000\uDFE9\u8EA2\uBCD6\u0000\uDFEC" + // 24925 - 24929 + "\u0000\uDFEB\u0000\u0000\u0000\uDFFC\u0000\u0000\u8EA2\uBCD0" + // 24930 - 24934 + "\u8EA2\uBCF2\u8EA2\uBCF3\u0000\uDFEE\u0000\uDFEA\u8EA2\uBCDE" + // 24935 - 24939 + "\u0000\u0000\u0000\uDFF5\u8EA2\uBCD9\u0000\u0000\u8EA2\uBCF4" + // 24940 - 24944 + "\u0000\uDFF1\u8EA2\uBCEC\u8EA2\uBCF9\u8EA2\uBCD3\u8EA2\uBCD8" + // 24945 - 24949 + "\u8EA2\uBCD2\u0000\u0000\u0000\uDFF0\u8EA2\uBCE5\u8EA2\uC3E4" + // 24950 - 24954 + "\u8EA2\uBCD4\u0000\uDFF4\u8EA2\uBCDD\u8EA2\uBCF5\u0000\uDFEF" + // 24955 - 24959 + "\u8EA2\uBCDA\u8EA2\uBCD7\u8EA2\uBCE8\u0000\uDFF2\u0000\uDFE8" + // 24960 - 24964 + "\u0000\uDFFE\u8EA2\uBCEB\u8EA2\uBCEA\u0000\u0000\u0000\u0000" + // 24965 - 24969 + "\u8EAD\uA3C6\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24970 - 24974 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24975 - 24979 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 24980 - 24984 + "\u8EAD\uA3C0\u8EAD\uA3C1\u8EAD\uA3C2\u8EAD\uA3C3\u8EAD\uA3C4" + // 24985 - 24989 + "\u8EAD\uA3C5\u8EAD\uA3C7\u8EAD\uA3C8\u8EAD\uA3C9\u8EAD\uA3CA" + // 24990 - 24994 + "\u8EAD\uA3CB\u8EAD\uA3CC\u8EAD\uA3CD\u8EAD\uA3CE\u8EAD\uA3CF" + // 24995 - 24999 + "\u8EAD\uA3D0\u8EAD\uA3D1\u8EAD\uA3D2\u8EAD\uA3D3\u8EAD\uA3D4" + // 25000 - 25004 + "\u8EAD\uA3D5\u8EAD\uA3D6\u8EAD\uA3D7\u8EAD\uA3D8\u8EAD\uA3D9" + // 25005 - 25009 + "\u8EAD\uA3DA\u8EAD\uA3DB\u8EAD\uA3DC\u8EAD\uA3DD\u8EAD\uA3DE" + // 25010 - 25014 + "\u8EAD\uA3DF\u8EAD\uA3E0\u8EAD\uA3E1\u8EAD\uA3E2\u8EAD\uA3E3" + // 25015 - 25019 + "\u8EAD\uA3E4\u8EAD\uA3E5\u8EAD\uA3E6\u8EAD\uA3E8\u8EAD\uA3E9" + // 25020 - 25024 + "\u8EAD\uA3EA\u8EAD\uA3EB\u8EAD\uA3EC\u8EAD\uA3ED\u8EAD\uA3EE" + // 25025 - 25029 + "\u8EAD\uA3EF\u8EAD\uA3F0\u8EAD\uA3F1\u0000\uA7C7\u8EA2\uA1D3" + // 25030 - 25034 + "\u0000\u0000\u0000\uC6C6\u8EA2\uA1D4\u0000\uC7F7\u0000\u0000" + // 25035 - 25039 + "\u0000\uC7F5\u0000\uC7F6\u0000\uC7F8\u0000\u0000\u0000\uC9FA" + // 25040 - 25044 + "\u0000\uC9F9\u0000\u0000\u8EA2\uA3DC\u0000\uC9FB\u0000\u0000" + // 25045 - 25049 + "\u0000\u0000\u8EA2\uA3DD\u8EA2\uA6B8\u0000\u0000\u8EA2\uA6B9" + // 25050 - 25054 + "\u0000\u0000\u0000\uCCF8\u0000\uCCFA\u0000\uCCFC\u0000\uCCF9" + // 25055 - 25059 + "\u0000\uCCFD\u0000\uCCFB\u0000\u0000\u0000\u0000\u0000\u0000" + // 25060 - 25064 + "\u0000\u0000\u0000\u0000\u0000\uD0EA\u0000\uD0E7\u0000\uD0E9" + // 25065 - 25069 + "\u0000\uD0EB\u0000\uD0E8\u8EA2\uAEE8\u8EA2\uA9F8\u0000\u0000" + // 25070 - 25074 + "\u0000\u0000\u0000\u0000\u8EA2\uAEEA\u8EA2\uAEE9\u0000\uD5A7" + // 25075 - 25079 + "\u0000\u0000\u0000\uD5A3\u0000\u0000\u0000\u0000\u0000\uD5A4" + // 25080 - 25084 + "\u0000\uD5A6\u0000\uD5A8\u0000\uD5A5\u0000\u0000\u0000\uD5AA" + // 25085 - 25089 + "\u0000\uD5A9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25090 - 25094 + "\u0000\u0000\u0000\uD9EE\u0000\uF9E7\u0000\u0000\u0000\u0000" + // 25095 - 25099 + "\u0000\uF9E8\u0000\u0000\u8EA2\uE8EF\u0000\u0000\u8EA2\uEBD6" + // 25100 - 25104 + "\u8EA2\uEBD5\u0000\u0000\u0000\u0000\u8EA2\uEDC9\u8EA2\uEDCA" + // 25105 - 25109 + "\u8EA2\uEFA4\u8EA2\uF0B5\u0000\u0000\u0000\uC4CD\u0000\uC4CE" + // 25110 - 25114 + "\u0000\u0000\u0000\uC4CF\u0000\uC5B6\u0000\uC6C5\u8EA2\uA2B3" + // 25115 - 25119 + "\u0000\uC7F3\u0000\uC7F4\u0000\u0000\u0000\uC9F7\u0000\uC9F8" + // 25120 - 25124 + "\u0000\uC9F6\u0000\uC9F5\u0000\u0000\u0000\uCCF5\u0000\u0000" + // 25125 - 25129 + "\u0000\u0000\u8EA2\uA6B6\u0000\uCCF7\u0000\uCCF6\u8EA2\uA6B7" + // 25130 - 25134 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0E6\u0000\u0000" + // 25135 - 25139 + "\u0000\uD5A1\u8EA2\uAEE7\u0000\u0000\u8EA2\uB4F3\u0000\u0000" + // 25140 - 25144 + "\u0000\uD9E9\u0000\uDEEC\u8EA2\uB4F2\u0000\uDEEB\u0000\u0000" + // 25145 - 25149 + "\u0000\uE8CD\u0000\u0000\u8EA2\uC9E4\u0000\uF0D0\u0000\u0000" + // 25150 - 25154 + "\u0000\uF3D2\u8EA2\uDCE3\u0000\u0000\u0000\uF9E9\u0000\u0000" + // 25155 - 25159 + "\u0000\uFBDC\u8EA2\uC2C7\u0000\uE3E0\u0000\uE3E7\u0000\u0000" + // 25160 - 25164 + "\u8EA2\uC2BB\u0000\u0000\u8EA2\uC2C5\u8EA2\uC2BA\u8EA2\uC2C3" + // 25165 - 25169 + "\u0000\uE3E1\u8EA2\uC2C8\u8EA2\uC2BC\u0000\uE3E2\u8EA2\uC2CB" + // 25170 - 25174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25175 - 25179 + "\u0000\u0000\u0000\u0000\u8EA2\uC9D5\u0000\uE8CA\u0000\uE8C9" + // 25180 - 25184 + "\u0000\uE8CB\u8EA2\uC9E1\u8EA2\uC9D7\u8EA2\uC9DD\u8EA2\uC9D2" + // 25185 - 25189 + "\u8EA2\uC9E0\u8EA2\uC9DF\u8EA2\uC9E3\u8EA2\uC9DC\u0000\uE8C6" + // 25190 - 25194 + "\u8EA2\uC9DB\u0000\uE8CC\u0000\u0000\u8EA2\uC9D4\u0000\uE8C7" + // 25195 - 25199 + "\u0000\u0000\u8EA2\uC9E2\u0000\uE8C8\u8EA2\uC9D6\u8EA2\uC9D9" + // 25200 - 25204 + "\u8EA2\uC9DE\u8EA2\uC9D8\u8EA2\uC9D3\u0000\u0000\u0000\u0000" + // 25205 - 25209 + "\u0000\u0000\u0000\u0000\u8EA2\uC9DA\u8EA2\uCFFC\u0000\uECCB" + // 25210 - 25214 + "\u8EA2\uD0A1\u8EA2\uCFFE\u8EA2\uD0A3\u8EA2\uD0A5\u0000\u0000" + // 25215 - 25219 + "\u0000\uECC9\u0000\u0000\u8EA2\uCFFD\u0000\u0000\u8EA2\uCFFB" + // 25220 - 25224 + "\u0000\uD9E3\u0000\uD9DF\u8EA2\uB4F0\u8EA2\uB4E0\u8EA2\uB4E3" + // 25225 - 25229 + "\u0000\u0000\u0000\uD9E7\u8EA2\uB4ED\u8EA2\uB4E5\u0000\uD9E0" + // 25230 - 25234 + "\u0000\uD9E8\u0000\u0000\u8EA2\uB4EA\u8EA2\uB4E8\u0000\u0000" + // 25235 - 25239 + "\u0000\u0000\u8EA2\uB4D9\u8EA2\uB4EE\u8EA2\uB4E2\u8EA2\uB4DD" + // 25240 - 25244 + "\u0000\u0000\u8EA2\uB4D3\u8EA2\uB4EF\u8EA2\uB4DF\u8EA2\uB4D2" + // 25245 - 25249 + "\u0000\u0000\u0000\uD9E6\u8EA2\uB4E4\u8EA2\uB4F1\u8EA2\uB4E1" + // 25250 - 25254 + "\u8EA2\uB4D5\u8EA2\uB4DA\u8EA2\uB4D1\u0000\u0000\u0000\uD9E5" + // 25255 - 25259 + "\u0000\u0000\u8EA2\uB4DE\u8EA2\uB4DB\u0000\uD9E1\u8EA2\uB4D4" + // 25260 - 25264 + "\u0000\u0000\u8EA2\uB4EC\u0000\uD9E2\u0000\u0000\u8EA2\uB4DC" + // 25265 - 25269 + "\u8EA2\uB4D8\u0000\u0000\u0000\u0000\u8EA2\uB4EB\u0000\u0000" + // 25270 - 25274 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25275 - 25279 + "\u0000\uDEE5\u8EA2\uBAFD\u0000\u0000\u8EA2\uBAFA\u8EA2\uBBAD" + // 25280 - 25284 + "\u8EA2\uBBA1\u8EA2\uBBAE\u0000\u0000\u0000\uDEE7\u8EA2\uA9E6" + // 25285 - 25289 + "\u0000\uD0E4\u0000\u0000\u0000\uD0DF\u0000\u0000\u0000\u0000" + // 25290 - 25294 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD4FE\u8EA2\uAEE4" + // 25295 - 25299 + "\u0000\u0000\u0000\uD4FD\u0000\u0000\u0000\u0000\u8EA2\uAEE2" + // 25300 - 25304 + "\u0000\u0000\u0000\uD4F2\u0000\u0000\u0000\uD4F7\u0000\u0000" + // 25305 - 25309 + "\u8EA2\uAEE1\u8EA2\uAEDE\u8EA2\uAEE3\u0000\uD4F3\u8EA2\uAEDD" + // 25310 - 25314 + "\u0000\u0000\u0000\uD4F6\u0000\uD4F4\u0000\u0000\u8EA2\uAEE5" + // 25315 - 25319 + "\u0000\uD4F5\u0000\uD4F9\u0000\u0000\u0000\u0000\u0000\uD4FA" + // 25320 - 25324 + "\u0000\u0000\u0000\uD4FC\u0000\u0000\u0000\u0000\u0000\u0000" + // 25325 - 25329 + "\u0000\uD4FB\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uAEDF" + // 25330 - 25334 + "\u8EA2\uAEE0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25335 - 25339 + "\u8EA2\uAEE6\u0000\u0000\u8EA2\uB4D7\u0000\uD9DE\u8EA2\uBBA6" + // 25340 - 25344 + "\u8EA2\uB4D6\u8EA2\uB4E9\u0000\u0000\u0000\u0000\u0000\uD9E4" + // 25345 - 25349 + "\u0000\u0000\u8EA2\uB4E7\u0000\u0000\u8EA2\uA4F1\u0000\uCBB0" + // 25350 - 25354 + "\u0000\u0000\u0000\uCEFB\u0000\u0000\u0000\uD2D5\u0000\uD2D6" + // 25355 - 25359 + "\u0000\uD2D7\u8EA2\uB7C9\u8EA2\uB1AE\u0000\uD6F2\u0000\u0000" + // 25360 - 25364 + "\u0000\u0000\u0000\uDCB3\u8EA2\uB7C8\u0000\u0000\u0000\u0000" + // 25365 - 25369 + "\u8EA2\uBEBC\u0000\uE0F7\u0000\u0000\u8EA2\uBEBB\u0000\uE0F6" + // 25370 - 25374 + "\u0000\u0000\u0000\u0000\u8EA2\uC5BB\u0000\uEDF3\u8EA2\uD2C3" + // 25375 - 25379 + "\u8EA2\uD2C0\u8EA2\uD2C1\u8EA2\uD2C2\u0000\u0000\u0000\u0000" + // 25380 - 25384 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDDE4\u0000\u0000" + // 25385 - 25389 + "\u8EA2\uE2BE\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE9C0" + // 25390 - 25394 + "\u8EA2\uE9BF\u0000\u0000\u8EA2\uEDE3\u0000\uC6F6\u8EA2\uA8B7" + // 25395 - 25399 + "\u0000\uD6F3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE0F8" + // 25400 - 25404 + "\u8EA2\uC5BC\u0000\u0000\u0000\u0000\u8EA2\uCCA9\u0000\u0000" + // 25405 - 25409 + "\u0000\uEDF4\u8EA2\uD8E0\u0000\u0000\u8EA2\uE2BF\u8EA2\uE9C1" + // 25410 - 25414 + "\u0000\uC6F7\u8EA2\uA6B2\u8EA2\uA6AB\u0000\u0000\u8EA2\uA6AE" + // 25415 - 25419 + "\u0000\u0000\u0000\uCCF4\u0000\uCCEB\u8EA2\uA6B5\u8EA2\uA6B3" + // 25420 - 25424 + "\u0000\u0000\u0000\uCCF0\u0000\uCCEE\u8EA2\uA6AA\u0000\uCCED" + // 25425 - 25429 + "\u8EA2\uA6A8\u8EA2\uA6A7\u0000\uCCEC\u0000\uCCEA\u0000\uCCF3" + // 25430 - 25434 + "\u0000\uCCEF\u0000\uCCE7\u0000\u0000\u8EA2\uA6AF\u0000\u0000" + // 25435 - 25439 + "\u0000\uD0DB\u0000\u0000\u0000\uD0E2\u8EA2\uA9F0\u0000\uD0DA" + // 25440 - 25444 + "\u8EA2\uA9E8\u8EA2\uA9E4\u0000\u0000\u8EA2\uA9F4\u8EA2\uA9E3" + // 25445 - 25449 + "\u0000\u0000\u0000\uD0DD\u8EA2\uA9ED\u0000\uD0E0\u0000\uD0E3" + // 25450 - 25454 + "\u0000\u0000\u0000\uD0DE\u8EA2\uA9F1\u0000\uD0E1\u0000\u0000" + // 25455 - 25459 + "\u0000\uD4F8\u8EA2\uA9F7\u8EA2\uA9E5\u0000\u0000\u0000\u0000" + // 25460 - 25464 + "\u8EA2\uA9E7\u8EA2\uA9EE\u8EA2\uA9F2\u8EA2\uA9F6\u8EA2\uA9F3" + // 25465 - 25469 + "\u8EA2\uA9EC\u8EA2\uA9EF\u0000\u0000\u0000\u0000\u8EA2\uA9E9" + // 25470 - 25474 + "\u0000\uD0E5\u8EA2\uA9EB\u8EA2\uA9EA\u8EA2\uA9F5\u0000\uD0DC" + // 25475 - 25479 + "\u8EA2\uA2AC\u0000\uC7F2\u0000\uC7F1\u0000\uC7EE\u0000\uC7EC" + // 25480 - 25484 + "\u8EA2\uA2AE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25485 - 25489 + "\u0000\uC9F3\u0000\u0000\u0000\u0000\u0000\uC9F0\u8EA2\uA3D6" + // 25490 - 25494 + "\u8EA2\uA3D9\u8EA2\uA3D8\u0000\u0000\u0000\uC9EA\u0000\uC9F2" + // 25495 - 25499 + "\u0000\u0000\u0000\u0000\u0000\uC9EF\u8EA2\uA3D5\u8EA2\uA3D3" + // 25500 - 25504 + "\u0000\uC9EE\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9E9" + // 25505 - 25509 + "\u0000\uC9EC\u0000\u0000\u8EA2\uA3D4\u8EA2\uA3DB\u8EA2\uA3D7" + // 25510 - 25514 + "\u0000\uC9ED\u0000\uC9F1\u0000\uC9F4\u8EA2\uA3D2\u8EA2\uA3DA" + // 25515 - 25519 + "\u0000\uC9EB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25520 - 25524 + "\u0000\u0000\u0000\uCCE9\u0000\uCCF1\u0000\u0000\u8EA2\uA6B0" + // 25525 - 25529 + "\u8EA2\uA6A9\u0000\uCCF2\u8EA2\uA6B4\u8EA2\uA6A5\u8EA2\uA6AC" + // 25530 - 25534 + "\u0000\u0000\u0000\u0000\u0000\uCCE8\u8EA2\uA6A6\u0000\uCCE6" + // 25535 - 25539 + "\u8EA2\uA6AD\u8EA2\uA6B1\u0000\uCCE5\u0000\u0000\u8EA2\uABF7" + // 25540 - 25544 + "\u8EA2\uB0E0\u0000\u0000\u8EA2\uCBEB\u0000\u0000\u0000\uF4BB" + // 25545 - 25549 + "\u0000\uC5DA\u0000\uCEEA\u8EA2\uABF8\u8EA2\uABF9\u8EA2\uBDEA" + // 25550 - 25554 + "\u0000\uE0D6\u0000\u0000\u0000\u0000\u8EA2\uC4E8\u0000\u0000" + // 25555 - 25559 + "\u0000\u0000\u0000\uE5A6\u8EA2\uCBEC\u0000\u0000\u0000\u0000" + // 25560 - 25564 + "\u0000\uEDE2\u0000\u0000\u0000\uF8C9\u0000\uC5DB\u8EA2\uBDEB" + // 25565 - 25569 + "\u0000\uC5DC\u0000\u0000\u0000\uC8CA\u8EA2\uA2E2\u0000\uC8C9" + // 25570 - 25574 + "\u0000\uCBA8\u0000\uCBA7\u0000\uCBA6\u8EA2\uA4E0\u0000\u0000" + // 25575 - 25579 + "\u0000\u0000\u0000\u0000\u0000\uCEEB\u0000\u0000\u0000\uCEEC" + // 25580 - 25584 + "\u8EA2\uA7FB\u0000\u0000\u8EA2\uABFA\u0000\u0000\u8EA2\uABFD" + // 25585 - 25589 + "\u0000\uD2BD\u8EA2\uABFB\u0000\u0000\u0000\uD2BC\u8EA2\uABFC" + // 25590 - 25594 + "\u0000\uD2BE\u0000\u0000\u8EA2\uB0E3\u8EA2\uB0E2\u8EA2\uB0E1" + // 25595 - 25599 + "\u0000\uD6D6\u0000\u0000\u8EA2\uB7A2\u8EA2\uB7A3\u0000\uDBF4" + // 25600 - 25604 + "\u8EA2\uB7A1\u8EA2\uB7A4\u8EA2\uA3D1\u0000\u0000\u0000\u0000" + // 25605 - 25609 + "\u0000\u0000\u0000\uCCE3\u8EA2\uA6A4\u0000\u0000\u0000\uCCE1" + // 25610 - 25614 + "\u0000\uCCE2\u0000\uCCE0\u8EA2\uAEDC\u0000\u0000\u0000\u0000" + // 25615 - 25619 + "\u0000\u0000\u0000\uD0D8\u0000\uD0D7\u0000\uD0D9\u0000\uD0D6" + // 25620 - 25624 + "\u0000\u0000\u8EA2\uA9E2\u0000\uCCE4\u0000\uD0D5\u0000\u0000" + // 25625 - 25629 + "\u0000\uD4EF\u0000\uD4F0\u0000\u0000\u0000\uD4F1\u0000\u0000" + // 25630 - 25634 + "\u8EA2\uB4D0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uDEE4" + // 25635 - 25639 + "\u8EA2\uBAF6\u0000\uD9DD\u0000\u0000\u0000\u0000\u0000\u0000" + // 25640 - 25644 + "\u0000\u0000\u0000\uE3DF\u0000\u0000\u0000\uE8C5\u0000\uE8C4" + // 25645 - 25649 + "\u8EA2\uC9D1\u0000\u0000\u0000\uECC7\u0000\uF0CD\u0000\u0000" + // 25650 - 25654 + "\u8EA2\uE1CC\u8EA2\uEDC8\u8EA2\uEFA3\u0000\uC4CC\u0000\uC6C3" + // 25655 - 25659 + "\u0000\u0000\u0000\uC6C4\u8EA2\uA2B1\u0000\uC7ED\u0000\uC7F0" + // 25660 - 25664 + "\u0000\u0000\u8EA2\uA2AF\u8EA2\uA2AD\u0000\uC7EF\u8EA2\uA2B0" + // 25665 - 25669 + "\u8EA2\uA2B2\u0000\uECBF\u8EA2\uC9C2\u8EA2\uC9CB\u0000\uE8BA" + // 25670 - 25674 + "\u0000\u0000\u0000\uE8BE\u8EA2\uC9C1\u8EA2\uC9C7\u8EA2\uC9CC" + // 25675 - 25679 + "\u8EA2\uC9BD\u0000\uE8BC\u8EA2\uC9C5\u0000\u0000\u0000\u0000" + // 25680 - 25684 + "\u8EA2\uC9C9\u8EA2\uC9CF\u8EA2\uC9BF\u8EA2\uC9C8\u0000\u0000" + // 25685 - 25689 + "\u0000\uE8BB\u8EA2\uC9CE\u0000\u0000\u0000\u0000\u0000\u0000" + // 25690 - 25694 + "\u8EA2\uC9C0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECC3" + // 25695 - 25699 + "\u8EA2\uCFF2\u0000\uECC1\u0000\uECC0\u8EA2\uCFF4\u8EA2\uCFF9" + // 25700 - 25704 + "\u0000\u0000\u8EA2\uCFF5\u0000\u0000\u8EA2\uCFF8\u0000\uECC6" + // 25705 - 25709 + "\u0000\u0000\u0000\uF0AE\u0000\uECC5\u0000\u0000\u8EA2\uCFF1" + // 25710 - 25714 + "\u8EA2\uCFF7\u0000\u0000\u0000\uECC4\u8EA2\uCFF6\u0000\u0000" + // 25715 - 25719 + "\u8EA2\uCFF3\u0000\u0000\u0000\uECC2\u0000\u0000\u0000\u0000" + // 25720 - 25724 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD6D7" + // 25725 - 25729 + "\u0000\u0000\u8EA2\uD6D9\u8EA2\uD6D4\u0000\uF0CA\u8EA2\uD6D6" + // 25730 - 25734 + "\u8EA2\uB4B6\u8EA2\uB4C9\u0000\uD9D8\u0000\u0000\u8EA2\uB4CF" + // 25735 - 25739 + "\u0000\uD9D2\u0000\uD9D4\u8EA2\uB4BE\u8EA2\uB4B9\u0000\uD4ED" + // 25740 - 25744 + "\u0000\uD9D3\u8EA2\uB4BB\u8EA2\uB4CA\u8EA2\uB4CE\u8EA2\uB4C5" + // 25745 - 25749 + "\u0000\u0000\u8EA2\uB4C7\u0000\u0000\u0000\u0000\u0000\u0000" + // 25750 - 25754 + "\u8EA2\uB4B1\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25755 - 25759 + "\u8EA2\uBAE3\u0000\u0000\u8EA2\uBAEC\u8EA2\uBAEB\u0000\uDEE0" + // 25760 - 25764 + "\u8EA2\uBAE4\u0000\u0000\u0000\uDEE1\u0000\uDEDF\u0000\u0000" + // 25765 - 25769 + "\u8EA2\uBAE6\u0000\uDEDC\u8EA2\uBAEA\u0000\u0000\u8EA2\uBAE5" + // 25770 - 25774 + "\u8EA2\uBAE7\u8EA2\uBAE1\u0000\uDEDA\u0000\u0000\u8EA2\uBAF4" + // 25775 - 25779 + "\u8EA2\uBAF3\u8EA2\uBAF0\u0000\uDED9\u0000\uDEDD\u0000\uDEDE" + // 25780 - 25784 + "\u8EA2\uAECD\u8EA2\uBAED\u0000\uDEDB\u0000\uD9D9\u8EA2\uBAEF" + // 25785 - 25789 + "\u8EA2\uBAE2\u8EA2\uBAF2\u8EA2\uBAF1\u0000\u0000\u8EA2\uBAF5" + // 25790 - 25794 + "\u0000\u0000\u8EA2\uC2B6\u0000\u0000\u8EA2\uBAEE\u8EA2\uA5F9" + // 25795 - 25799 + "\u0000\u0000\u0000\uD0CD\u0000\uCCD7\u0000\u0000\u0000\u0000" + // 25800 - 25804 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25805 - 25809 + "\u0000\uD0CE\u8EA2\uA9D5\u0000\u0000\u0000\u0000\u8EA2\uA9DB" + // 25810 - 25814 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0D4\u8EA2\uA9D9" + // 25815 - 25819 + "\u8EA2\uA9DF\u0000\u0000\u8EA2\uA9D6\u8EA2\uA9DA\u8EA2\uA9DC" + // 25820 - 25824 + "\u8EA2\uA9DE\u8EA2\uA9D8\u0000\u0000\u8EA2\uA9D7\u8EA2\uA9D2" + // 25825 - 25829 + "\u8EA2\uA9D3\u0000\uD0CF\u0000\u0000\u0000\uD0D1\u0000\uD0D0" + // 25830 - 25834 + "\u8EA2\uA9D4\u8EA2\uA9DD\u0000\u0000\u0000\u0000\u0000\u0000" + // 25835 - 25839 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25840 - 25844 + "\u0000\uD0D3\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25845 - 25849 + "\u0000\u0000\u0000\u0000\u8EA2\uA9D1\u8EA2\uAED5\u0000\u0000" + // 25850 - 25854 + "\u8EA2\uAED4\u8EA2\uAED9\u8EA2\uAED0\u0000\u0000\u8EA2\uAED3" + // 25855 - 25859 + "\u8EA2\uAED2\u0000\u0000\u8EA2\uAED6\u0000\uC9DF\u8EA2\uA3CA" + // 25860 - 25864 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA3CB\u0000\u0000" + // 25865 - 25869 + "\u0000\uC9E1\u0000\u0000\u8EA2\uA3CD\u0000\uC9DD\u8EA2\uA3CE" + // 25870 - 25874 + "\u8EA2\uA3CC\u0000\uC9E0\u0000\uC9E2\u0000\uC9E5\u0000\uC9E4" + // 25875 - 25879 + "\u0000\uC9DE\u8EA2\uA3CF\u0000\u0000\u0000\u0000\u0000\u0000" + // 25880 - 25884 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25885 - 25889 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 25890 - 25894 + "\u0000\u0000\u0000\uCCDB\u8EA2\uA5FE\u0000\u0000\u0000\uCCDD" + // 25895 - 25899 + "\u0000\u0000\u0000\uCCDC\u0000\u0000\u8EA2\uA6A1\u0000\uCCDA" + // 25900 - 25904 + "\u0000\uCCD9\u8EA2\uA5F5\u0000\u0000\u8EA2\uA5F4\u0000\u0000" + // 25905 - 25909 + "\u8EA2\uA5F2\u8EA2\uA5F7\u8EA2\uA5F6\u8EA2\uA5F3\u8EA2\uA5FC" + // 25910 - 25914 + "\u8EA2\uA5FD\u8EA2\uA5FA\u8EA2\uA5F8\u0000\uCCD8\u0000\u0000" + // 25915 - 25919 + "\u0000\u0000\u0000\u0000\u8EA2\uA5FB\u0000\uCCDE\u8EA2\uA6A2" + // 25920 - 25924 + "\u0000\u0000\u0000\u0000\u0000\uFCF0\u8EA2\uF0DD\u0000\uFCF2" + // 25925 - 25929 + "\u0000\u0000\u8EA2\uF0DC\u8EA2\uF0DE\u0000\uFCF1\u8EA2\uF1C6" + // 25930 - 25934 + "\u0000\u0000\u8EA2\uF1FB\u0000\u0000\u0000\u0000\u0000\u0000" + // 25935 - 25939 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFE5\u0000\u0000" + // 25940 - 25944 + "\u0000\u0000\u0000\u0000\u0000\uE7DC\u0000\u0000\u8EA2\uCFA6" + // 25945 - 25949 + "\u0000\u0000\u8EA2\uD5D9\u0000\uF2FA\u0000\uF2FB\u0000\u0000" + // 25950 - 25954 + "\u0000\uCFE6\u0000\u0000\u0000\uEFEA\u0000\uF9C8\u0000\uD3F2" + // 25955 - 25959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF2FC\u0000\u0000" + // 25960 - 25964 + "\u0000\uFCCE\u0000\uD3F3\u8EA2\uBAB1\u0000\u0000\u8EA2\uC1CE" + // 25965 - 25969 + "\u0000\u0000\u8EA2\uC1D0\u0000\u0000\u8EA2\uC1CF\u0000\u0000" + // 25970 - 25974 + "\u8EA2\uC8E3\u8EA2\uC8E0\u0000\uE7DD\u0000\u0000\u0000\uE7DE" + // 25975 - 25979 + "\u8EA2\uC8E1\u8EA2\uC8E2\u0000\u0000\u8EA2\uCFA8\u8EA2\uCFAD" + // 25980 - 25984 + "\u0000\uEBE7\u8EA2\uCFA7\u8EA2\uCFA9\u8EA2\uCFB0\u0000\uFAE5" + // 25985 - 25989 + "\u0000\uFAE4\u0000\uFAE6\u8EA2\uEBD2\u0000\u0000\u8EA2\uEDC6" + // 25990 - 25994 + "\u8EA2\uEDC5\u0000\u0000\u0000\uFBD9\u0000\uFBDB\u0000\uFBDA" + // 25995 - 25999 + "\u8EA2\uEDC7\u0000\uFCB4\u8EA2\uF0B4\u0000\u0000\u0000\u0000" + // 26000 - 26004 + "\u0000\u0000\u0000\uFCDD\u0000\u0000\u8EA2\uF0B3\u8EA2\uF0FC" + // 26005 - 26009 + "\u0000\u0000\u0000\u0000\u8EA2\uA1AA\u0000\u0000\u0000\u0000" + // 26010 - 26014 + "\u0000\uC6BF\u0000\uC6BE\u0000\u0000\u0000\uC7E0\u0000\uC7DF" + // 26015 - 26019 + "\u8EA2\uA2A7\u0000\uC7DE\u8EA2\uA2A6\u0000\u0000\u0000\u0000" + // 26020 - 26024 + "\u0000\uC9DB\u8EA2\uA3C9\u0000\u0000\u8EA2\uA3C8\u0000\u0000" + // 26025 - 26029 + "\u0000\u0000\u0000\uC9D9\u0000\uC9DC\u0000\u0000\u0000\u0000" + // 26030 - 26034 + "\u8EA2\uA3C7\u0000\u0000\u0000\uC9DA\u0000\u0000\u0000\u0000" + // 26035 - 26039 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA5F0" + // 26040 - 26044 + "\u0000\u0000\u8EA2\uA5F1\u0000\uCCD6\u0000\u0000\u0000\u0000" + // 26045 - 26049 + "\u0000\u0000\u0000\u0000\u0000\uD0CC\u0000\uF3C6\u8EA2\uDCD5" + // 26050 - 26054 + "\u8EA2\uDCD3\u8EA2\uDCD1\u8EA2\uDCD0\u0000\uF3C8\u8EA2\uDCCF" + // 26055 - 26059 + "\u0000\uF3C9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26060 - 26064 + "\u8EA2\uDCCD\u8EA2\uDCCE\u0000\uF3C5\u0000\uF3CA\u0000\uF3C7" + // 26065 - 26069 + "\u0000\u0000\u0000\u0000\u8EA2\uDCCC\u0000\u0000\u0000\uF6BC" + // 26070 - 26074 + "\u0000\u0000\u8EA2\uE1C8\u8EA2\uE1C6\u8EA2\uE1CB\u8EA2\uE1C9" + // 26075 - 26079 + "\u0000\u0000\u8EA2\uE1C7\u8EA2\uE1CA\u0000\u0000\u0000\u0000" + // 26080 - 26084 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26085 - 26089 + "\u0000\uF8AC\u8EA2\uE5BE\u8EA2\uE5BF\u0000\uF8AD\u0000\u0000" + // 26090 - 26094 + "\u8EA2\uE5C0\u8EA2\uE5BC\u8EA2\uE5C1\u8EA2\uE5BD\u0000\uF6BD" + // 26095 - 26099 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uE8EB\u8EA2\uE8ED" + // 26100 - 26104 + "\u0000\uF9E4\u8EA2\uE8EC\u0000\uF9E3\u0000\uF9E2\u0000\u0000" + // 26105 - 26109 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF9E5\u8EA2\uEBD3" + // 26110 - 26114 + "\u8EA2\uEBD4\u0000\u0000\u8EA2\uC4D6\u8EA2\uC4DD\u8EA2\uC4DF" + // 26115 - 26119 + "\u8EA2\uC4E5\u0000\u0000\u0000\uE5A2\u8EA2\uC4D1\u0000\u0000" + // 26120 - 26124 + "\u0000\uE4F8\u0000\u0000\u8EA2\uC4E0\u0000\uE4FD\u8EA2\uC4E6" + // 26125 - 26129 + "\u0000\uE4F4\u0000\u0000\u8EA2\uC4E3\u0000\u0000\u8EA2\uC4D3" + // 26130 - 26134 + "\u8EA2\uC4E4\u8EA2\uC4D2\u0000\u0000\u0000\uE5A4\u0000\u0000" + // 26135 - 26139 + "\u8EA2\uC4DE\u0000\uE4F5\u8EA2\uC4E7\u0000\u0000\u0000\uE4FA" + // 26140 - 26144 + "\u8EA2\uC4D7\u0000\uE5A1\u8EA2\uC4E2\u8EA2\uC4D5\u8EA2\uC4DC" + // 26145 - 26149 + "\u8EA2\uC4D8\u8EA2\uC4D4\u0000\uE4F7\u0000\uE4FE\u0000\uE4FC" + // 26150 - 26154 + "\u0000\uE4F9\u0000\uE5A3\u0000\uE4F6\u8EA2\uC4DB\u0000\u0000" + // 26155 - 26159 + "\u0000\uE4FB\u0000\u0000\u0000\uE0D4\u0000\u0000\u8EA2\uC4E1" + // 26160 - 26164 + "\u0000\u0000\u8EA2\uC4D9\u0000\u0000\u0000\u0000\u0000\u0000" + // 26165 - 26169 + "\u0000\u0000\u0000\u0000\u8EA2\uC4DA\u0000\u0000\u0000\u0000" + // 26170 - 26174 + "\u8EA2\uCBE7\u0000\u0000\u0000\uE9DB\u0000\u0000\u0000\u0000" + // 26175 - 26179 + "\u0000\uD4D4\u0000\u0000\u8EA2\uAEC8\u8EA2\uAEBC\u8EA2\uAEC0" + // 26180 - 26184 + "\u0000\uD4D9\u0000\uD4E4\u8EA2\uAEBD\u0000\uD4DF\u8EA2\uAEC2" + // 26185 - 26189 + "\u8EA2\uAEC9\u8EA2\uB4A4\u0000\u0000\u8EA2\uAEC7\u0000\uD4E6" + // 26190 - 26194 + "\u0000\uD4D3\u8EA2\uAEBF\u8EA2\uAEB6\u0000\u0000\u0000\uD4DB" + // 26195 - 26199 + "\u0000\u0000\u0000\u0000\u8EA2\uAEB5\u0000\u0000\u0000\u0000" + // 26200 - 26204 + "\u8EA2\uAEBA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26205 - 26209 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26210 - 26214 + "\u0000\u0000\u0000\u0000\u8EA2\uAEB3\u0000\uD4E3\u0000\u0000" + // 26215 - 26219 + "\u0000\u0000\u8EA2\uB3FA\u0000\u0000\u0000\uD9C9\u8EA2\uB4AA" + // 26220 - 26224 + "\u0000\uD9C7\u0000\uD9C4\u8EA2\uB4A1\u0000\uD9C0\u8EA2\uB4A5" + // 26225 - 26229 + "\u0000\uD9CB\u8EA2\uB3F9\u8EA2\uB3FE\u8EA2\uB3FD\u0000\uD4D5" + // 26230 - 26234 + "\u0000\uD9C6\u8EA2\uB4A8\u0000\u0000\u8EA2\uB4AB\u8EA2\uB3F6" + // 26235 - 26239 + "\u0000\u0000\u0000\uDECE\u0000\u0000\u8EA2\uE2A7\u8EA2\uE5F6" + // 26240 - 26244 + "\u8EA2\uE2AA\u0000\u0000\u8EA2\uE5F7\u0000\uF8C7\u8EA2\uE5F5" + // 26245 - 26249 + "\u0000\u0000\u0000\u0000\u8EA2\uE5F4\u0000\u0000\u8EA2\uE5F3" + // 26250 - 26254 + "\u0000\uF8C8\u0000\u0000\u0000\u0000\u0000\uF9F7\u0000\u0000" + // 26255 - 26259 + "\u0000\u0000\u8EA2\uE9B9\u8EA2\uE9BA\u0000\u0000\u0000\u0000" + // 26260 - 26264 + "\u0000\u0000\u0000\u0000\u8EA2\uEBEA\u8EA2\uEBE9\u0000\uFAF5" + // 26265 - 26269 + "\u0000\u0000\u8EA2\uEBE8\u8EA2\uEDDA\u8EA2\uEDDB\u0000\u0000" + // 26270 - 26274 + "\u0000\u0000\u8EA2\uEFB5\u8EA2\uF0BF\u0000\u0000\u0000\u0000" + // 26275 - 26279 + "\u8EA2\uF1A6\u8EA2\uF2AC\u0000\uFDC6\u8EA2\uF2C0\u0000\uC5D7" + // 26280 - 26284 + "\u0000\u0000\u0000\uCEE7\u0000\uCEE8\u0000\u0000\u0000\u0000" + // 26285 - 26289 + "\u0000\uD2BB\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26290 - 26294 + "\u0000\uF4BA\u0000\uC5D8\u0000\u0000\u0000\uCEE9\u0000\uD6D5" + // 26295 - 26299 + "\u0000\uE5A5\u0000\uC5D9\u0000\u0000\u0000\uDBF3\u0000\uE9DF" + // 26300 - 26304 + "\u8EA2\uA1C2\u8EA2\uCFE8\u8EA2\uCFE2\u8EA2\uCFDF\u0000\u0000" + // 26305 - 26309 + "\u0000\u0000\u8EA2\uBACF\u8EA2\uCFE5\u0000\u0000\u8EA2\uCFDE" + // 26310 - 26314 + "\u8EA2\uCFE4\u8EA2\uCFE3\u0000\u0000\u8EA2\uCFE0\u0000\u0000" + // 26315 - 26319 + "\u0000\uECB9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26320 - 26324 + "\u0000\uECB8\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uECBA" + // 26325 - 26329 + "\u8EA2\uCFE6\u0000\uF0BB\u8EA2\uCFE7\u0000\u0000\u0000\u0000" + // 26330 - 26334 + "\u0000\u0000\u8EA2\uD6CF\u0000\u0000\u8EA2\uD6C8\u0000\u0000" + // 26335 - 26339 + "\u0000\uF0C7\u8EA2\uD6CC\u0000\uF0BF\u0000\uF0C3\u8EA2\uD6CB" + // 26340 - 26344 + "\u0000\u0000\u0000\uF0C2\u0000\uF0BE\u0000\uF0C1\u0000\uF0BC" + // 26345 - 26349 + "\u0000\uF0C6\u8EA2\uD6CD\u8EA2\uD6C9\u0000\uF0C5\u8EA2\uD6C7" + // 26350 - 26354 + "\u0000\uF0C4\u8EA2\uD6CE\u8EA2\uD6CA\u0000\uECBB\u0000\u0000" + // 26355 - 26359 + "\u0000\uF0C8\u8EA2\uD6D0\u0000\uF0C0\u0000\uF0BD\u0000\u0000" + // 26360 - 26364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDCD2\u8EA2\uDCD4" + // 26365 - 26369 + "\u0000\uE8A7\u8EA2\uC9B5\u8EA2\uC9B7\u0000\u0000\u8EA2\uC9BA" + // 26370 - 26374 + "\u0000\u0000\u0000\uE8AC\u0000\u0000\u0000\uE8B3\u0000\uE8AD" + // 26375 - 26379 + "\u0000\u0000\u0000\u0000\u8EA2\uC9B0\u0000\uE8AE\u0000\uE8AF" + // 26380 - 26384 + "\u8EA2\uC9B3\u0000\uE8B4\u0000\u0000\u8EA2\uC9B1\u8EA2\uC9B6" + // 26385 - 26389 + "\u0000\uE8AB\u8EA2\uC9AF\u0000\uE8B1\u0000\uE8A9\u0000\u0000" + // 26390 - 26394 + "\u0000\u0000\u0000\u0000\u0000\uE8A8\u8EA2\uC9B4\u8EA2\uC9B9" + // 26395 - 26399 + "\u0000\u0000\u0000\uE8B2\u0000\u0000\u0000\u0000\u0000\u0000" + // 26400 - 26404 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC9AE" + // 26405 - 26409 + "\u0000\u0000\u0000\uECB7\u8EA2\uCFEE\u0000\u0000\u8EA2\uCFEB" + // 26410 - 26414 + "\u0000\u0000\u0000\uECB1\u0000\uECBD\u0000\uECBE\u0000\u0000" + // 26415 - 26419 + "\u0000\uECB4\u8EA2\uCFE9\u0000\uECB6\u8EA2\uCFE1\u0000\uECBC" + // 26420 - 26424 + "\u0000\u0000\u8EA2\uCFED\u0000\uECB3\u8EA2\uCFEF\u0000\uECB2" + // 26425 - 26429 + "\u0000\u0000\u8EA2\uCFEA\u8EA2\uCFEC\u0000\uECB5\u8EA2\uC1F6" + // 26430 - 26434 + "\u0000\u0000\u8EA2\uC2A7\u8EA2\uC1EF\u8EA2\uC1F8\u0000\uE3CC" + // 26435 - 26439 + "\u0000\uE3CD\u0000\uE3C5\u8EA2\uC2A3\u0000\uE3CF\u8EA2\uC1F4" + // 26440 - 26444 + "\u8EA2\uC1F3\u8EA2\uC1F1\u8EA2\uC2A5\u0000\uE3C3\u8EA2\uC1FD" + // 26445 - 26449 + "\u8EA2\uC1F2\u0000\uE3C6\u8EA2\uC1FB\u0000\uE3C1\u8EA2\uC1F7" + // 26450 - 26454 + "\u8EA2\uC1FE\u8EA2\uC2A2\u0000\u0000\u0000\u0000\u8EA2\uC2A6" + // 26455 - 26459 + "\u0000\uE3CA\u8EA2\uC1F0\u0000\uE3C4\u8EA2\uC1F5\u0000\u0000" + // 26460 - 26464 + "\u0000\uE3BF\u0000\u0000\u0000\uE3CB\u8EA2\uC2A1\u0000\uE3C7" + // 26465 - 26469 + "\u0000\uE3C8\u0000\uE3CE\u0000\uE3C2\u0000\uA3A1\u0000\uE3C0" + // 26470 - 26474 + "\u8EA2\uC1F9\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26475 - 26479 + "\u0000\u0000\u0000\uE3C9\u0000\u0000\u0000\u0000\u8EA2\uC2A4" + // 26480 - 26484 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE8B5\u0000\uE8B0" + // 26485 - 26489 + "\u0000\u0000\u8EA2\uC9BC\u8EA2\uC9B8\u0000\u0000\u8EA2\uC9B2" + // 26490 - 26494 + "\u0000\uE8AA\u0000\uE8A6\u8EA2\uC9BB\u0000\uDEBE\u8EA2\uBAD5" + // 26495 - 26499 + "\u0000\uDEC4\u0000\uDECA\u0000\uE1C3\u0000\u0000\u0000\u0000" + // 26500 - 26504 + "\u0000\uDEC8\u8EA2\uBAD2\u0000\uDED5\u0000\uDEC1\u0000\uDEC9" + // 26505 - 26509 + "\u8EA2\uBADA\u8EA2\uC1FC\u8EA2\uBADF\u8EA2\uBAD3\u0000\u0000" + // 26510 - 26514 + "\u8EA2\uBACA\u8EA2\uBAD7\u8EA2\uBAD1\u0000\uDEC7\u8EA2\uBADD" + // 26515 - 26519 + "\u0000\u0000\u0000\u0000\u0000\uDEC3\u0000\uDED7\u0000\uDED0" + // 26520 - 26524 + "\u0000\u0000\u0000\uDEC5\u0000\uDEC2\u0000\u0000\u0000\uDECD" + // 26525 - 26529 + "\u0000\u0000\u8EA2\uBADE\u8EA2\uBAD0\u8EA2\uBAD6\u8EA2\uBAD8" + // 26530 - 26534 + "\u8EA2\uBACC\u8EA2\uBADB\u0000\uDEBF\u8EA2\uBACB\u0000\u0000" + // 26535 - 26539 + "\u0000\uDEC6\u0000\uDED6\u0000\uDED2\u8EA2\uBACD\u0000\uDECC" + // 26540 - 26544 + "\u0000\u0000\u0000\u0000\u0000\uDED3\u0000\uDECF\u0000\uDECB" + // 26545 - 26549 + "\u0000\u0000\u8EA2\uBAD4\u0000\u0000\u0000\u0000\u0000\u0000" + // 26550 - 26554 + "\u0000\u0000\u0000\u0000\u0000\uDED1\u0000\u0000\u0000\u0000" + // 26555 - 26559 + "\u0000\u0000\u8EA2\uC1FA\u8EA2\uB4AC\u0000\uD9CC\u0000\u0000" + // 26560 - 26564 + "\u0000\uD9BE\u0000\uD9BB\u8EA2\uB4A3\u0000\uD9B8\u0000\u0000" + // 26565 - 26569 + "\u8EA2\uB4A9\u0000\u0000\u0000\uD9BF\u8EA2\uB4AD\u0000\u0000" + // 26570 - 26574 + "\u8EA2\uB3F7\u8EA2\uB4A7\u0000\uD9C2\u8EA2\uB3F8\u8EA2\uB3FB" + // 26575 - 26579 + "\u8EA2\uB4A2\u0000\u0000\u0000\u0000\u0000\uD9C3\u0000\uD9C1" + // 26580 - 26584 + "\u0000\uD9CD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26585 - 26589 + "\u0000\uD9C8\u0000\u0000\u0000\uD9BC\u0000\uDAF6\u0000\u0000" + // 26590 - 26594 + "\u0000\uD9BD\u8EA2\uB3FC\u0000\uD9CA\u0000\uD9C5\u8EA2\uB4A6" + // 26595 - 26599 + "\u0000\uD9BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD9B9" + // 26600 - 26604 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26605 - 26609 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26610 - 26614 + "\u8EA2\uB3F4\u8EA2\uB3F5\u8EA2\uBACE\u0000\u0000\u0000\u0000" + // 26615 - 26619 + "\u0000\u0000\u0000\uDEBD\u0000\uDEC0\u8EA2\uBAD9\u0000\uDED4" + // 26620 - 26624 + "\u8EA2\uBADC\u0000\uD0B7\u0000\uD0C2\u0000\uD0BF\u8EA2\uA9C1" + // 26625 - 26629 + "\u0000\uD0C3\u8EA2\uA9C7\u8EA2\uA9C8\u0000\uD0BE\u0000\uD0C4" + // 26630 - 26634 + "\u0000\uD0BA\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26635 - 26639 + "\u0000\uD0B9\u8EA2\uA9C0\u0000\u0000\u0000\u0000\u0000\u0000" + // 26640 - 26644 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA9C5\u0000\u0000" + // 26645 - 26649 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26650 - 26654 + "\u0000\u0000\u8EA2\uA9CF\u0000\u0000\u8EA2\uAEC6\u0000\uD4DE" + // 26655 - 26659 + "\u8EA2\uAEB4\u0000\u0000\u8EA2\uAEB9\u0000\uD4D7\u0000\uD4E2" + // 26660 - 26664 + "\u8EA2\uAEB7\u0000\uD4D2\u0000\uD4DC\u0000\uD4E1\u8EA2\uAEBE" + // 26665 - 26669 + "\u0000\u0000\u0000\uD4DD\u0000\uD4E0\u0000\u0000\u0000\u0000" + // 26670 - 26674 + "\u8EA2\uAEC1\u0000\uD4D8\u8EA2\uAEB8\u0000\u0000\u0000\u0000" + // 26675 - 26679 + "\u0000\u0000\u8EA2\uAEC4\u8EA2\uAEC5\u0000\u0000\u0000\uD4DA" + // 26680 - 26684 + "\u8EA2\uAEC3\u0000\uD4D6\u0000\uD4E5\u0000\u0000\u8EA2\uAEBB" + // 26685 - 26689 + "\u0000\uCCC5\u8EA2\uA5DD\u8EA2\uA5DF\u0000\u0000\u0000\uCCC8" + // 26690 - 26694 + "\u0000\u0000\u0000\uCCCA\u8EA2\uA5DB\u8EA2\uA5E0\u0000\u0000" + // 26695 - 26699 + "\u0000\u0000\u0000\uCCD3\u0000\uCCCF\u8EA2\uA5E8\u0000\uCCD5" + // 26700 - 26704 + "\u0000\u0000\u0000\uCCCC\u8EA2\uA5DE\u0000\uCCC9\u0000\u0000" + // 26705 - 26709 + "\u0000\u0000\u0000\uCCC4\u0000\uCCC2\u0000\u0000\u8EA2\uA5EC" + // 26710 - 26714 + "\u0000\u0000\u0000\uCCD0\u0000\u0000\u0000\u0000\u0000\u0000" + // 26715 - 26719 + "\u0000\u0000\u0000\u0000\u8EA2\uA9C9\u8EA2\uA9BD\u8EA2\uA9CC" + // 26720 - 26724 + "\u0000\u0000\u0000\u0000\u8EA2\uA9BF\u0000\uD0BC\u0000\uD0CA" + // 26725 - 26729 + "\u0000\uD0B8\u0000\uD0C9\u0000\uD0C1\u0000\uD0C6\u0000\uD0B6" + // 26730 - 26734 + "\u8EA2\uA9BE\u8EA2\uA9C4\u0000\uD0C5\u8EA2\uA9D0\u0000\uD0C7" + // 26735 - 26739 + "\u0000\u0000\u0000\uD0BD\u0000\u0000\u0000\u0000\u8EA2\uA9C6" + // 26740 - 26744 + "\u8EA2\uA9C3\u0000\uD0BB\u0000\u0000\u8EA2\uA9BC\u0000\uD0C8" + // 26745 - 26749 + "\u8EA2\uA9CB\u0000\uD0C0\u8EA2\uA9CD\u0000\uD0CB\u0000\uC9D4" + // 26750 - 26754 + "\u8EA2\uA3C3\u0000\uC9C8\u0000\uC9C5\u0000\u0000\u8EA2\uA3BC" + // 26755 - 26759 + "\u0000\uC9C4\u8EA2\uA3C6\u0000\uC9C7\u0000\u0000\u0000\uC9CB" + // 26760 - 26764 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9C2\u8EA2\uA3C2" + // 26765 - 26769 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA3BB" + // 26770 - 26774 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26775 - 26779 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26780 - 26784 + "\u0000\u0000\u8EA2\uA5EB\u8EA2\uA5EA\u0000\uCCD1\u8EA2\uA5ED" + // 26785 - 26789 + "\u8EA2\uA5EF\u8EA2\uA5E4\u8EA2\uA5E7\u8EA2\uA5EE\u0000\uCCD2" + // 26790 - 26794 + "\u0000\u0000\u0000\u0000\u8EA2\uA5E1\u8EA2\uA5E5\u0000\u0000" + // 26795 - 26799 + "\u0000\u0000\u8EA2\uA5E9\u8EA2\uA9CA\u0000\uCCCD\u8EA2\uA9CE" + // 26800 - 26804 + "\u0000\uCCC0\u8EA2\uA5E6\u0000\uCCC1\u0000\uCCCE\u0000\uCCC7" + // 26805 - 26809 + "\u0000\uCCC3\u0000\u0000\u8EA2\uA5E2\u0000\uCCC6\u0000\uCCCB" + // 26810 - 26814 + "\u0000\uCCD4\u8EA2\uA5E3\u8EA2\uA5DC\u0000\uD9B4\u0000\uC4B2" + // 26815 - 26819 + "\u0000\u0000\u0000\uC4C5\u0000\uA4BF\u0000\uC5AB\u0000\u0000" + // 26820 - 26824 + "\u0000\uC5AA\u0000\uC5A9\u0000\uC6A5\u0000\uC6A4\u0000\u0000" + // 26825 - 26829 + "\u8EA2\uA1CE\u8EA2\uA2A3\u0000\u0000\u0000\u0000\u0000\u0000" + // 26830 - 26834 + "\u0000\uCCB8\u0000\uCCB5\u0000\uCCB7\u0000\uCCB6\u0000\u0000" + // 26835 - 26839 + "\u0000\u0000\u0000\uD0B2\u0000\u0000\u0000\u0000\u0000\uDEBB" + // 26840 - 26844 + "\u0000\u0000\u0000\uC4B3\u0000\u0000\u0000\uC5AC\u0000\u0000" + // 26845 - 26849 + "\u0000\uC6A7\u0000\uC6A6\u0000\u0000\u8EA2\uA3B4\u0000\u0000" + // 26850 - 26854 + "\u0000\u0000\u0000\uCCB9\u0000\u0000\u0000\u0000\u0000\uA7BA" + // 26855 - 26859 + "\u0000\u0000\u0000\u0000\u8EA2\uA1B7\u0000\u0000\u0000\uC6A9" + // 26860 - 26864 + "\u0000\uC6A8\u0000\uC7CD\u0000\uC7CE\u8EA2\uA3B5\u0000\uC9BB" + // 26865 - 26869 + "\u0000\u0000\u0000\uC9BC\u0000\u0000\u0000\uCCBA\u0000\uCCBB" + // 26870 - 26874 + "\u0000\uCCBC\u0000\u0000\u0000\uD0B3\u8EA2\uA9B7\u0000\u0000" + // 26875 - 26879 + "\u0000\u0000\u0000\uD4CE\u8EA2\uA9B4\u0000\uD0B0\u8EA2\uA9B5" + // 26880 - 26884 + "\u0000\uD0AF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD0AD" + // 26885 - 26889 + "\u0000\u0000\u0000\uD0AE\u0000\u0000\u0000\u0000\u0000\u0000" + // 26890 - 26894 + "\u8EA2\uAEB0\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26895 - 26899 + "\u0000\uD9AC\u8EA2\uB3F1\u0000\u0000\u0000\uD9AF\u8EA2\uB3F0" + // 26900 - 26904 + "\u0000\uDBAB\u0000\uD9AE\u0000\uD9AD\u0000\u0000\u0000\uDEBA" + // 26905 - 26909 + "\u0000\u0000\u0000\uDEB9\u0000\uDEB8\u0000\uE3B9\u0000\u0000" + // 26910 - 26914 + "\u0000\u0000\u0000\uE3BC\u0000\uE3BD\u0000\uE3BB\u0000\u0000" + // 26915 - 26919 + "\u0000\uE3BA\u0000\u0000\u0000\u0000\u8EA2\uC9AA\u0000\u0000" + // 26920 - 26924 + "\u8EA2\uC9AB\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uCFDD" + // 26925 - 26929 + "\u0000\uECAF\u8EA2\uCFDC\u0000\u0000\u0000\uF0BA\u8EA2\uDCCB" + // 26930 - 26934 + "\u0000\uF3C4\u0000\u0000\u8EA2\uE5BA\u0000\uF9E1\u0000\uA7B4" + // 26935 - 26939 + "\u0000\uC4C4\u0000\uC5A4\u8EA2\uA1B6\u0000\u0000\u0000\uC5A5" + // 26940 - 26944 + "\u0000\uC5A6\u8EA2\uC9A8\u8EA2\uC9A9\u0000\uE8A3\u0000\uE8A2" + // 26945 - 26949 + "\u8EA2\uCCE1\u0000\u0000\u0000\u0000\u0000\uECAA\u0000\uECAB" + // 26950 - 26954 + "\u0000\uECAC\u0000\uECAE\u8EA2\uCFDA\u8EA2\uCFDB\u0000\uECAD" + // 26955 - 26959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0B8\u0000\u0000" + // 26960 - 26964 + "\u0000\uF0B9\u0000\u0000\u0000\u0000\u8EA2\uE5B9\u8EA2\uEBD1" + // 26965 - 26969 + "\u8EA2\uEBD0\u8EA2\uEEFE\u0000\u0000\u0000\uC4B0\u0000\u0000" + // 26970 - 26974 + "\u0000\u0000\u0000\u0000\u0000\uC5FC\u0000\uC5FB\u0000\u0000" + // 26975 - 26979 + "\u0000\u0000\u0000\uC7C9\u0000\u0000\u0000\u0000\u8EA2\uA1FE" + // 26980 - 26984 + "\u0000\u0000\u0000\u0000\u0000\uC9B7\u0000\uC9B8\u0000\uC9B6" + // 26985 - 26989 + "\u0000\uC9B9\u8EA2\uA3B1\u8EA2\uA3B2\u0000\u0000\u0000\u0000" + // 26990 - 26994 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 26995 - 26999 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27000 - 27004 + "\u0000\uCCB4\u8EA2\uA5D5\u0000\u0000\u0000\uCCB3\u0000\u0000" + // 27005 - 27009 + "\u8EA2\uC4A2\u8EA2\uC3FE\u8EA2\uC3FD\u0000\u0000\u8EA2\uC3FC" + // 27010 - 27014 + "\u8EA2\uC3FB\u0000\uE4D8\u8EA2\uC4A1\u0000\uE9B9\u8EA2\uCBAB" + // 27015 - 27019 + "\u8EA2\uCBAD\u0000\uE9BA\u8EA2\uCBAC\u0000\uEDC5\u0000\u0000" + // 27020 - 27024 + "\u0000\uEDC4\u8EA2\uD1B9\u0000\u0000\u0000\u0000\u8EA2\uD7EA" + // 27025 - 27029 + "\u8EA2\uD7E9\u8EA2\uD7EB\u0000\u0000\u0000\u0000\u0000\uF1A4" + // 27030 - 27034 + "\u0000\u0000\u8EA2\uDDB7\u0000\uF3FC\u0000\u0000\u8EA2\uE1F4" + // 27035 - 27039 + "\u0000\uF6D6\u8EA2\uE5E4\u0000\uFBE3\u0000\uC5CF\u0000\uC6DF" + // 27040 - 27044 + "\u0000\uC8BA\u0000\uCAE3\u0000\uCEBD\u0000\uCEBE\u0000\u0000" + // 27045 - 27049 + "\u0000\u0000\u0000\uD1F1\u0000\u0000\u0000\u0000\u8EA2\uAFFC" + // 27050 - 27054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uE4D9" + // 27055 - 27059 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD2D9\u0000\uF1A5" + // 27060 - 27064 + "\u0000\uF6D7\u0000\uC5D0\u0000\u0000\u0000\uC8BB\u0000\u0000" + // 27065 - 27069 + "\u0000\u0000\u8EA2\uA7CC\u0000\uCEBF\u0000\uC4AE\u0000\uC4AF" + // 27070 - 27074 + "\u0000\u0000\u0000\uC4C3\u0000\u0000\u0000\u0000\u0000\uC5A1" + // 27075 - 27079 + "\u0000\uC5A2\u0000\uC5A3\u8EA2\uA1CC\u0000\uC5FA\u0000\u0000" + // 27080 - 27084 + "\u8EA2\uA1CB\u0000\u0000\u0000\uC7C7\u0000\u0000\u8EA2\uA1FD" + // 27085 - 27089 + "\u0000\uC7C5\u0000\uC7C6\u8EA2\uA1FC\u0000\u0000\u0000\u0000" + // 27090 - 27094 + "\u0000\uC7C8\u0000\uC7C4\u0000\u0000\u0000\u0000\u0000\u0000" + // 27095 - 27099 + "\u0000\u0000\u8EA2\uA3AE\u0000\uCFCF\u8EA2\uA3AF\u0000\u0000" + // 27100 - 27104 + "\u0000\u0000\u8EA2\uA3B0\u0000\u0000\u0000\u0000\u0000\uC9B2" + // 27105 - 27109 + "\u0000\uC9B1\u0000\u0000\u0000\u0000\u0000\uC9B5\u0000\uC9B3" + // 27110 - 27114 + "\u0000\uC9B4\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCB0" + // 27115 - 27119 + "\u0000\u0000\u0000\uCCAF\u8EA2\uA5D4\u8EA2\uA5D1\u8EA2\uA5D2" + // 27120 - 27124 + "\u0000\u0000\u8EA2\uA5D0\u0000\uCCB1\u0000\uCCAD\u0000\uCCAC" + // 27125 - 27129 + "\u0000\u0000\u0000\uCCAE\u0000\uCCAB\u0000\u0000\u0000\u0000" + // 27130 - 27134 + "\u0000\u0000\u0000\u0000\u0000\uDCBE\u0000\u0000\u8EA2\uB7DD" + // 27135 - 27139 + "\u0000\u0000\u0000\u0000\u8EA2\uB7D6\u8EA2\uB7D8\u8EA2\uB7DA" + // 27140 - 27144 + "\u0000\u0000\u8EA2\uB7DB\u8EA2\uB7D9\u0000\uDCBF\u0000\u0000" + // 27145 - 27149 + "\u8EA2\uB7DE\u0000\u0000\u8EA2\uB7D7\u0000\u0000\u0000\u0000" + // 27150 - 27154 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27155 - 27159 + "\u0000\u0000\u0000\u0000\u8EA2\uBECC\u0000\uE0FC\u8EA2\uBED4" + // 27160 - 27164 + "\u0000\u0000\u8EA2\uBEC9\u0000\u0000\u8EA2\uBED5\u0000\u0000" + // 27165 - 27169 + "\u8EA2\uBECA\u8EA2\uBECB\u0000\u0000\u0000\u0000\u8EA2\uBED3" + // 27170 - 27174 + "\u8EA2\uBED2\u8EA2\uBECF\u0000\uDCBD\u0000\uE0FD\u8EA2\uBECD" + // 27175 - 27179 + "\u8EA2\uBED0\u0000\uE0FE\u8EA2\uBED1\u8EA2\uBECE\u0000\u0000" + // 27180 - 27184 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27185 - 27189 + "\u0000\u0000\u8EA2\uC5D4\u0000\u0000\u8EA2\uC5D8\u0000\uE5D5" + // 27190 - 27194 + "\u0000\u0000\u8EA2\uCCB8\u0000\uE5D8\u0000\uF0B5\u0000\u0000" + // 27195 - 27199 + "\u0000\uA7AD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27200 - 27204 + "\u8EA2\uA1AE\u0000\u0000\u0000\uC5F4\u0000\uC5F5\u0000\u0000" + // 27205 - 27209 + "\u0000\u0000\u0000\uC7C2\u0000\u0000\u8EA2\uA3AC\u0000\u0000" + // 27210 - 27214 + "\u0000\uD0A4\u0000\uD0A3\u8EA2\uAEA3\u8EA2\uAEA2\u0000\uD9A8" + // 27215 - 27219 + "\u0000\uA7AE\u0000\uC4FD\u8EA2\uA1B5\u0000\u0000\u0000\u0000" + // 27220 - 27224 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA5CD\u0000\u0000" + // 27225 - 27229 + "\u0000\uD0A5\u0000\u0000\u0000\uD4C3\u0000\u0000\u0000\uD4C1" + // 27230 - 27234 + "\u0000\uD4C2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27235 - 27239 + "\u0000\uF0B6\u0000\uA7AF\u0000\uC5F6\u0000\u0000\u0000\u0000" + // 27240 - 27244 + "\u0000\u0000\u0000\uC7C3\u8EA2\uA1FB\u0000\u0000\u0000\u0000" + // 27245 - 27249 + "\u0000\u0000\u0000\u0000\u0000\uC9AF\u0000\uC9B0\u0000\u0000" + // 27250 - 27254 + "\u8EA2\uA3AD\u0000\u0000\u0000\u0000\u8EA2\uA5CE\u0000\uCCA9" + // 27255 - 27259 + "\u8EA2\uA5CF\u0000\u0000\u0000\uE9A9\u8EA2\uCBA7\u0000\uE9B7" + // 27260 - 27264 + "\u8EA2\uCAF5\u0000\u0000\u8EA2\uCBAA\u0000\u0000\u0000\u0000" + // 27265 - 27269 + "\u8EA2\uCAE5\u8EA2\uCAFA\u0000\uE9AC\u0000\uE9B5\u0000\uE9B3" + // 27270 - 27274 + "\u8EA2\uCAE7\u8EA2\uCAFC\u0000\uE9B2\u0000\u0000\u0000\u0000" + // 27275 - 27279 + "\u0000\uE9AB\u8EA2\uCAF8\u0000\u0000\u0000\u0000\u0000\u0000" + // 27280 - 27284 + "\u0000\u0000\u8EA2\uCBA4\u0000\u0000\u0000\u0000\u0000\u0000" + // 27285 - 27289 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27290 - 27294 + "\u8EA2\uD0FC\u0000\u0000\u8EA2\uD0F8\u8EA2\uD0F4\u8EA2\uD1AA" + // 27295 - 27299 + "\u8EA2\uD1A7\u0000\uEDB7\u0000\u0000\u0000\u0000\u8EA2\uD1AE" + // 27300 - 27304 + "\u8EA2\uD0FB\u0000\uEDC2\u8EA2\uD1A4\u0000\u0000\u0000\u0000" + // 27305 - 27309 + "\u8EA2\uD0F7\u8EA2\uD1A3\u0000\uEDBF\u8EA2\uD1B7\u0000\u0000" + // 27310 - 27314 + "\u8EA2\uD1B4\u8EA2\uD1A6\u8EA2\uD0F5\u0000\u0000\u0000\u0000" + // 27315 - 27319 + "\u8EA2\uD1AB\u8EA2\uD1AD\u0000\uEDBB\u8EA2\uD1A1\u8EA2\uD0FA" + // 27320 - 27324 + "\u0000\uC4C2\u0000\uC4F8\u0000\u0000\u0000\uC4F7\u0000\uC5F3" + // 27325 - 27329 + "\u0000\uC5F2\u0000\uC7BE\u0000\uC7BD\u0000\uC7BF\u0000\uC7BC" + // 27330 - 27334 + "\u0000\u0000\u0000\uC9AC\u0000\uC9AB\u0000\uC9AD\u0000\u0000" + // 27335 - 27339 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCCA3\u0000\u0000" + // 27340 - 27344 + "\u0000\uCCA2\u0000\uCCA4\u0000\u0000\u0000\uD0A2\u0000\u0000" + // 27345 - 27349 + "\u0000\uA2F9\u8EA2\uAEA1\u0000\uA2FA\u0000\uD9A7\u0000\uA2FC" + // 27350 - 27354 + "\u0000\uA2FB\u8EA2\uBAC4\u0000\u0000\u0000\uA2FD\u0000\uE7FE" + // 27355 - 27359 + "\u0000\uA2FE\u0000\u0000\u0000\uC4AB\u0000\u0000\u0000\uC4F9" + // 27360 - 27364 + "\u0000\uC7C0\u0000\uCCA5\u0000\u0000\u0000\uC4AC\u0000\uC4FC" + // 27365 - 27369 + "\u0000\uC4FA\u0000\uC4FB\u0000\u0000\u0000\u0000\u0000\uC7C1" + // 27370 - 27374 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC9AE\u0000\uCCA7" + // 27375 - 27379 + "\u0000\uCCA6\u0000\uCCA8\u0000\u0000\u0000\u0000\u0000\u0000" + // 27380 - 27384 + "\u0000\uD4C0\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA1A5" + // 27385 - 27389 + "\u0000\u0000\u0000\uC5AD\u0000\u0000\u0000\u0000\u0000\u0000" + // 27390 - 27394 + "\u0000\u0000\u0000\u0000\u8EA2\uA2A4\u0000\u0000\u0000\u0000" + // 27395 - 27399 + "\u0000\u0000\u8EA2\uA3B6\u8EA2\uA3B7\u0000\u0000\u0000\u0000" + // 27400 - 27404 + "\u8EA2\uA5D9\u0000\u0000\u8EA2\uA5DA\u0000\u0000\u8EA2\uA9B9" + // 27405 - 27409 + "\u8EA2\uA9B8\u8EA2\uA9BB\u8EA2\uA9BA\u0000\uD0B4\u0000\u0000" + // 27410 - 27414 + "\u8EA2\uB3F3\u0000\uD4D0\u8EA2\uAEB2\u0000\uD4CF\u0000\u0000" + // 27415 - 27419 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBAC8\u0000\uDEBC" + // 27420 - 27424 + "\u0000\u0000\u8EA2\uBAC9\u0000\u0000\u0000\u0000\u0000\u0000" + // 27425 - 27429 + "\u0000\u0000\u8EA2\uC9AD\u0000\uE8A5\u0000\u0000\u0000\u0000" + // 27430 - 27434 + "\u0000\u0000\u0000\u0000\u0000\uECB0\u0000\u0000\u8EA2\uE5BB" + // 27435 - 27439 + "\u0000\u0000\u0000\uA7BC\u0000\u0000\u0000\u0000\u8EA2\uA1B8" + // 27440 - 27444 + "\u0000\u0000\u0000\uC6AA\u0000\u0000\u0000\u0000\u0000\u0000" + // 27445 - 27449 + "\u0000\u0000\u8EA2\uD2F1\u0000\u0000\u0000\u0000\u0000\u0000" + // 27450 - 27454 + "\u0000\u0000\u0000\uF1EA\u0000\uF1EF\u0000\uF1E8\u8EA2\uD9C4" + // 27455 - 27459 + "\u8EA2\uD9BB\u8EA2\uD9C7\u8EA2\uD9BF\u8EA2\uD9C5\u0000\uF1F0" + // 27460 - 27464 + "\u0000\uF1E9\u8EA2\uD9B8\u8EA2\uD9BE\u8EA2\uD9C8\u8EA2\uD9C1" + // 27465 - 27469 + "\u8EA2\uD9C6\u8EA2\uD9BA\u0000\u0000\u0000\u0000\u8EA2\uD9C2" + // 27470 - 27474 + "\u0000\uF1EB\u8EA2\uD9C0\u0000\uF1EE\u0000\uF1ED\u8EA2\uD9BC" + // 27475 - 27479 + "\u8EA2\uD9BD\u8EA2\uD9B9\u8EA2\uD9C3\u0000\uF1EC\u8EA2\uD9CA" + // 27480 - 27484 + "\u8EA2\uD9C9\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uDEC7" + // 27485 - 27489 + "\u8EA2\uDEC3\u0000\uF4E9\u0000\u0000\u8EA2\uDEBD\u0000\uF4E3" + // 27490 - 27494 + "\u0000\uF4F3\u8EA2\uDEC9\u0000\uF4EB\u0000\uF4E7\u8EA2\uDEC0" + // 27495 - 27499 + "\u0000\uF4EE\u0000\uF4F1\u8EA2\uDECB\u0000\uF4E6\u8EA2\uDEC2" + // 27500 - 27504 + "\u0000\uF4EF\u8EA2\uDECD\u8EA2\uDECA\u8EA2\uDEBE\u0000\uF4EA" + // 27505 - 27509 + "\u0000\uF4E4\u0000\uF4F2\u0000\uECA1\u0000\u0000\u0000\uECA5" + // 27510 - 27514 + "\u8EA2\uCFCF\u0000\uEBFE\u0000\uECA8\u8EA2\uCFD2\u8EA2\uCFD3" + // 27515 - 27519 + "\u0000\uECA6\u0000\uECA7\u8EA2\uCFD9\u8EA2\uCFD6\u8EA2\uCFD7" + // 27520 - 27524 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uF0B3\u8EA2\uD6C3" + // 27525 - 27529 + "\u0000\uF0B0\u8EA2\uD6C0\u0000\uF0B2\u0000\uF0B4\u0000\u0000" + // 27530 - 27534 + "\u8EA2\uD6C1\u0000\uF0B1\u0000\u0000\u8EA2\uD6C2\u0000\u0000" + // 27535 - 27539 + "\u8EA2\uD6BF\u0000\u0000\u0000\u0000\u0000\uF3C1\u8EA2\uDCC9" + // 27540 - 27544 + "\u0000\uF3C2\u8EA2\uDCC7\u0000\u0000\u8EA2\uDCC8\u8EA2\uDCC6" + // 27545 - 27549 + "\u8EA2\uDCC5\u0000\u0000\u0000\u0000\u8EA2\uDCCA\u0000\uF3C0" + // 27550 - 27554 + "\u0000\u0000\u0000\u0000\u8EA2\uE1C4\u8EA2\uE1C5\u0000\u0000" + // 27555 - 27559 + "\u8EA2\uCFD0\u8EA2\uE1C3\u0000\uF3C3\u0000\uF8AB\u8EA2\uE5B7" + // 27560 - 27564 + "\u8EA2\uE5B8\u0000\u0000\u0000\uFAE2\u0000\uFAE3\u8EA2\uEBCF" + // 27565 - 27569 + "\u8EA2\uEBCE\u0000\uFBD8\u0000\uFBD7\u8EA2\uEEFD\u0000\u0000" + // 27570 - 27574 + "\u0000\uC4AA\u0000\uDEAE\u0000\u0000\u8EA2\uBAC2\u8EA2\uBABB" + // 27575 - 27579 + "\u0000\u0000\u0000\uDEAB\u0000\u0000\u8EA2\uBAC3\u0000\u0000" + // 27580 - 27584 + "\u0000\u0000\u0000\u0000\u8EA2\uBAB9\u8EA2\uBABC\u0000\uDEAA" + // 27585 - 27589 + "\u8EA2\uBABD\u0000\u0000\u0000\u0000\u0000\uDEAD\u8EA2\uBAC1" + // 27590 - 27594 + "\u0000\u0000\u8EA2\uBAB7\u8EA2\uBAB6\u0000\uDEAF\u0000\u0000" + // 27595 - 27599 + "\u0000\uDEB0\u0000\uDEAC\u0000\uDEB1\u8EA2\uBAB5\u8EA2\uBAC0" + // 27600 - 27604 + "\u8EA2\uBABE\u8EA2\uBAB8\u0000\u0000\u0000\u0000\u0000\u0000" + // 27605 - 27609 + "\u0000\uDEA9\u8EA2\uBABA\u0000\u0000\u0000\u0000\u0000\u0000" + // 27610 - 27614 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27615 - 27619 + "\u0000\uE3B1\u0000\uE3AB\u8EA2\uC1DC\u0000\uE3B4\u8EA2\uC1E2" + // 27620 - 27624 + "\u8EA2\uC1E5\u0000\uE3AD\u0000\uE3AE\u8EA2\uC1DF\u0000\uE3AC" + // 27625 - 27629 + "\u8EA2\uC1E8\u0000\uE3B2\u8EA2\uC1E9\u0000\u0000\u8EA2\uC1E4" + // 27630 - 27634 + "\u0000\uE3B3\u0000\u0000\u8EA2\uC1D9\u0000\uE3B0\u8EA2\uC1DA" + // 27635 - 27639 + "\u8EA2\uB3E6\u8EA2\uB3DA\u0000\u0000\u0000\uD8F6\u0000\u0000" + // 27640 - 27644 + "\u8EA2\uB3DF\u8EA2\uB3E5\u0000\uD8F5\u8EA2\uB3D8\u0000\uD8F9" + // 27645 - 27649 + "\u8EA2\uB3DC\u8EA2\uB3D5\u0000\uD8F7\u8EA2\uB3D9\u0000\uD8FC" + // 27650 - 27654 + "\u0000\uD9A3\u0000\u0000\u8EA2\uB3EA\u0000\u0000\u8EA2\uB3D4" + // 27655 - 27659 + "\u0000\u0000\u0000\uD8FD\u0000\u0000\u8EA2\uB3E9\u0000\u0000" + // 27660 - 27664 + "\u0000\u0000\u0000\uD8F8\u8EA2\uB3DB\u0000\uD8F4\u8EA2\uB3D6" + // 27665 - 27669 + "\u8EA2\uB3D2\u8EA2\uB3E0\u8EA2\uB3D3\u8EA2\uB3D1\u8EA2\uB3DD" + // 27670 - 27674 + "\u8EA2\uB3E3\u8EA2\uB3E4\u0000\uD8FA\u0000\u0000\u0000\u0000" + // 27675 - 27679 + "\u8EA2\uBABF\u8EA2\uB3E1\u8EA2\uB3D0\u8EA2\uB3E2\u0000\u0000" + // 27680 - 27684 + "\u0000\uD9A6\u8EA2\uB3E7\u0000\uD9A5\u8EA2\uB3CF\u0000\u0000" + // 27685 - 27689 + "\u8EA2\uB3D7\u8EA2\uB3E8\u0000\uD9A1\u0000\uD8FE\u0000\uD8FB" + // 27690 - 27694 + "\u0000\uD9A2\u0000\u0000\u0000\u0000\u0000\uD8F2\u0000\u0000" + // 27695 - 27699 + "\u0000\u0000\u0000\uD8F3\u0000\u0000\u0000\u0000\u0000\uCBD2" + // 27700 - 27704 + "\u0000\u0000\u0000\uCBD5\u0000\uCBD4\u0000\uCBD3\u0000\u0000" + // 27705 - 27709 + "\u0000\u0000\u8EA2\uA5A5\u0000\u0000\u8EA2\uA8ED\u0000\u0000" + // 27710 - 27714 + "\u8EA2\uA8EF\u0000\uCFD2\u0000\u0000\u0000\u0000\u0000\uCFD4" + // 27715 - 27719 + "\u8EA2\uA8EC\u8EA2\uA8EE\u0000\uCFD3\u8EA2\uA8F1\u8EA2\uA8F0" + // 27720 - 27724 + "\u8EA2\uA8F2\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27725 - 27729 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uADD7" + // 27730 - 27734 + "\u8EA2\uADD5\u0000\uD3DF\u8EA2\uADD4\u0000\uD3E4\u0000\uD3E1" + // 27735 - 27739 + "\u0000\uD3DE\u0000\u0000\u0000\uD3E5\u0000\u0000\u0000\uD3E0" + // 27740 - 27744 + "\u0000\uD3E3\u0000\u0000\u0000\uD3E2\u8EA2\uADD6\u0000\u0000" + // 27745 - 27749 + "\u0000\uD3DD\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD8CF" + // 27750 - 27754 + "\u8EA2\uB3AF\u8EA2\uB3B5\u0000\uD8CC\u0000\uD8D3\u0000\u0000" + // 27755 - 27759 + "\u0000\uD8CE\u8EA2\uB3B2\u8EA2\uB3B4\u0000\uD8D1\u8EA2\uB3AE" + // 27760 - 27764 + "\u8EA2\uB3B1\u0000\uD4B1\u0000\u0000\u0000\u0000\u0000\u0000" + // 27765 - 27769 + "\u0000\u0000\u8EA2\uADEA\u0000\uD4AA\u8EA2\uADEB\u0000\u0000" + // 27770 - 27774 + "\u0000\uD4BF\u0000\u0000\u0000\uD4B6\u0000\uD4A1\u0000\uD4A2" + // 27775 - 27779 + "\u8EA2\uADFE\u0000\uD9A4\u0000\u0000\u0000\uD4AF\u0000\uD4AE" + // 27780 - 27784 + "\u8EA2\uADEC\u0000\uD4B2\u8EA2\uB3DE\u0000\uD4A9\u8EA2\uADF7" + // 27785 - 27789 + "\u0000\uD4B8\u0000\uD4B7\u0000\uD4AD\u8EA2\uADEF\u8EA2\uADF8" + // 27790 - 27794 + "\u0000\u0000\u8EA2\uADE9\u0000\uD4AC\u8EA2\uADF9\u0000\uD4B5" + // 27795 - 27799 + "\u8EA2\uADED\u0000\uD4A3\u0000\u0000\u0000\uD4A6\u0000\uD4A5" + // 27800 - 27804 + "\u8EA2\uADFA\u0000\uD4B3\u0000\uD4A8\u0000\uD4BC\u0000\uD4BE" + // 27805 - 27809 + "\u8EA2\uADF4\u0000\uD4BB\u0000\u0000\u8EA2\uADFC\u8EA2\uADEE" + // 27810 - 27814 + "\u8EA2\uADFD\u0000\u0000\u8EA2\uADF2\u0000\u0000\u8EA2\uADFB" + // 27815 - 27819 + "\u0000\u0000\u8EA2\uADF3\u0000\u0000\u0000\u0000\u0000\u0000" + // 27820 - 27824 + "\u0000\u0000\u0000\uD4AB\u0000\u0000\u0000\u0000\u0000\u0000" + // 27825 - 27829 + "\u0000\uE3AA\u8EA2\uC1D6\u8EA2\uC1D7\u0000\uA7A6\u0000\uC4A7" + // 27830 - 27834 + "\u0000\u0000\u0000\uC4E8\u0000\u0000\u0000\u0000\u0000\uCBE7" + // 27835 - 27839 + "\u0000\uC4A8\u8EA2\uA1A9\u0000\uC4C0\u0000\u0000\u0000\u0000" + // 27840 - 27844 + "\u0000\uC4E9\u0000\uC4EB\u8EA2\uA1B1\u0000\uC4EC\u0000\uC4EA" + // 27845 - 27849 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC7A7\u0000\u0000" + // 27850 - 27854 + "\u0000\uCBE8\u0000\u0000\u0000\u0000\u0000\uCBE9\u0000\uCFE7" + // 27855 - 27859 + "\u0000\uA7A8\u0000\uC4C1\u0000\uC4ED\u0000\u0000\u0000\uC7A8" + // 27860 - 27864 + "\u0000\uC7AA\u0000\uC7A9\u0000\u0000\u0000\uC8EC\u0000\u0000" + // 27865 - 27869 + "\u0000\u0000\u0000\uCBEA\u0000\uCBEB\u0000\uCFE8\u0000\uCFE9" + // 27870 - 27874 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uD3FE" + // 27875 - 27879 + "\u0000\u0000\u0000\u0000\u8EA2\uC1D8\u0000\u0000\u0000\u0000" + // 27880 - 27884 + "\u8EA2\uEDC4\u0000\uC4A9\u0000\u0000\u0000\u0000\u0000\u0000" + // 27885 - 27889 + "\u0000\u0000\u0000\u0000\u8EA2\uAEA5\u8EA2\uAEA6\u0000\uD4C6" + // 27890 - 27894 + "\u0000\u0000\u8EA2\uAEA7\u0000\u0000\u8EA2\uAEA4\u0000\uD4C7" + // 27895 - 27899 + "\u0000\uD4C5\u0000\uD4C4\u8EA2\uAEA8\u0000\u0000\u8EA2\uB3EB" + // 27900 - 27904 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uBAC5\u0000\u0000" + // 27905 - 27909 + "\u0000\u0000\u8EA2\uC1EA\u8EA2\uC9A7\u0000\u0000\u0000\u0000" + // 27910 - 27914 + "\u0000\u0000\u0000\uECA9\u0000\uF0B7\u8EA2\uD6C4\u0000\u0000" + // 27915 - 27919 + "\u0000\uC4AD\u0000\uC4BB\u0000\u0000\u0000\u0000\u0000\u0000" + // 27920 - 27924 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27925 - 27929 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 27930 - 27934 + "\u0000\u0000\u0000\uD9A9\u0000\uDEB3\u0000\u0000\u0000\uE8A1" + // 27935 - 27939 + "\u0000\u0000\u8EA2\uA1A3\u0000\uC4FE\u0000\u0000\u0000\uC5F9" + // 27940 - 27944 + "\u0000\uC5F7\u0000\uC5F8\u0000\u0000\u0000\u0000\u0000\uCCAA" + // 27945 - 27949 + "\u0000\u0000\u0000\u0000\u0000\uE6AD\u0000\u0000\u0000\u0000" + // 27950 - 27954 + "\u0000\u0000\u8EA2\uC6D8\u0000\u0000\u0000\u0000\u0000\u0000" + // 27955 - 27959 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uC6D4\u8EA2\uC6FA" + // 27960 - 27964 + "\u8EA2\uC6D3\u0000\uEAE8\u8EA2\uCDDA\u0000\u0000\u0000\u0000" + // 27965 - 27969 + "\u8EA2\uCDC9\u0000\u0000\u0000\u0000\u8EA2\uCDB3\u8EA2\uCDD1" + // 27970 - 27974 + "\u0000\uEAE0\u8EA2\uCDC2\u8EA2\uCDCC\u0000\uEAE3\u8EA2\uCDC5" + // 27975 - 27979 + "\u0000\uEAE1\u8EA2\uCDB6\u8EA2\uCDD4\u8EA2\uCDB5\u8EA2\uCDC8" + // 27980 - 27984 + "\u0000\u0000\u8EA2\uCDB4\u0000\u0000\u0000\u0000\u8EA2\uCDC6" + // 27985 - 27989 + "\u8EA2\uCDCF\u8EA2\uCDCD\u8EA2\uCDC1\u8EA2\uCDBC\u8EA2\uCDBA" + // 27990 - 27994 + "\u0000\u0000\u8EA2\uCDBB\u8EA2\uCDCE\u8EA2\uCDD9\u8EA2\uCDC3" + // 27995 - 27999 + "\u0000\uEAE2\u0000\u0000\u8EA2\uCDBE\u0000\u0000\u8EA2\uCDD2" + // 28000 - 28004 + "\u0000\u0000\u0000\uEAE5\u8EA2\uCDBD\u8EA2\uCDB7\u8EA2\uCDC7" + // 28005 - 28009 + "\u0000\uEAE9\u0000\u0000\u0000\u0000\u0000\uEADD\u8EA2\uA9AF" + // 28010 - 28014 + "\u8EA2\uA9A5\u0000\uCFFB\u0000\uCFF2\u0000\uCFFA\u8EA2\uA8FE" + // 28015 - 28019 + "\u0000\u0000\u8EA2\uA9AC\u0000\u0000\u8EA2\uA9A3\u0000\uCFF6" + // 28020 - 28024 + "\u8EA2\uA9A4\u0000\u0000\u8EA2\uA8FD\u0000\uCFFD\u0000\uCFF0" + // 28025 - 28029 + "\u0000\uCFF9\u0000\uCFEF\u0000\u0000\u8EA2\uA9A1\u8EA2\uA9A6" + // 28030 - 28034 + "\u0000\u0000\u8EA2\uA9AD\u0000\uCFF7\u0000\uCFF4\u8EA2\uA9A8" + // 28035 - 28039 + "\u0000\uCFFC\u8EA2\uA9AB\u8EA2\uA9A7\u0000\uCFF1\u0000\uCFFE" + // 28040 - 28044 + "\u0000\uCFF5\u0000\uCFEE\u0000\uCFEA\u0000\u0000\u0000\u0000" + // 28045 - 28049 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28050 - 28054 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA9B1\u0000\u0000" + // 28055 - 28059 + "\u0000\uD4BA\u0000\uD4A4\u0000\u0000\u0000\uD4B4\u0000\u0000" + // 28060 - 28064 + "\u0000\uD4B9\u8EA2\uADF1\u8EA2\uADF0\u8EA2\uADF5\u8EA2\uADF6" + // 28065 - 28069 + "\u0000\uD4A7\u0000\u0000\u0000\uD4B0\u0000\u0000\u0000\u0000" + // 28070 - 28074 + "\u0000\u0000\u0000\uD4BD\u0000\u0000\u0000\uD5F2\u0000\uD5EB" + // 28075 - 28079 + "\u0000\uD5EE\u0000\u0000\u0000\uD5F1\u0000\u0000\u8EA2\uAFD7" + // 28080 - 28084 + "\u0000\u0000\u0000\uD5EC\u8EA2\uAFD5\u0000\u0000\u0000\uD5F0" + // 28085 - 28089 + "\u0000\u0000\u0000\u0000\u0000\uD5ED\u0000\u0000\u8EA2\uAFD8" + // 28090 - 28094 + "\u0000\uD5EF\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28095 - 28099 + "\u0000\u0000\u0000\u0000\u8EA2\uB5EE\u0000\uDBA5\u8EA2\uB5ED" + // 28100 - 28104 + "\u8EA2\uB5EF\u0000\uDBA4\u0000\uDBA9\u8EA2\uAFD6\u0000\u0000" + // 28105 - 28109 + "\u8EA2\uB5EC\u8EA2\uB5F0\u0000\u0000\u0000\uDBA6\u8EA2\uB5EB" + // 28110 - 28114 + "\u0000\uDBA8\u0000\u0000\u0000\uDBA7\u0000\u0000\u8EA2\uBCCA" + // 28115 - 28119 + "\u0000\u0000\u8EA2\uBCC5\u0000\u0000\u0000\uDFDB\u0000\uDFDF" + // 28120 - 28124 + "\u0000\uDFDC\u8EA2\uBCC8\u8EA2\uBCCB\u0000\u0000\u0000\uDFDD" + // 28125 - 28129 + "\u0000\u0000\u0000\uDFDE\u0000\uDFE3\u8EA2\uC3CD\u8EA2\uBCC9" + // 28130 - 28134 + "\u0000\uDFE1\u8EA2\uBCC6\u8EA2\uBCC4\u0000\u0000\u0000\uDFE2" + // 28135 - 28139 + "\u0000\u0000\u8EA2\uA7C6\u0000\u0000\u8EA2\uA7C4\u8EA2\uA7BC" + // 28140 - 28144 + "\u8EA2\uA7B4\u8EA2\uA7BB\u0000\uCEAD\u0000\u0000\u0000\uCEB3" + // 28145 - 28149 + "\u0000\u0000\u0000\uCEA7\u8EA2\uA7BF\u8EA2\uA7BE\u8EA2\uA7B6" + // 28150 - 28154 + "\u0000\u0000\u0000\uCEB5\u8EA2\uA7C2\u8EA2\uA7B7\u0000\uCEB8" + // 28155 - 28159 + "\u8EA2\uA7C9\u0000\uCEA8\u0000\u0000\u0000\uCEAF\u8EA2\uA7BA" + // 28160 - 28164 + "\u8EA2\uA7C3\u0000\uCEB7\u0000\u0000\u0000\uCEAA\u0000\uCEAE" + // 28165 - 28169 + "\u0000\u0000\u8EA2\uA7C1\u0000\u0000\u0000\u0000\u0000\u0000" + // 28170 - 28174 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28175 - 28179 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28180 - 28184 + "\u0000\u0000\u8EA2\uABBD\u0000\uD1DF\u0000\uD1EC\u0000\u0000" + // 28185 - 28189 + "\u8EA2\uABB8\u8EA2\uABAE\u0000\uD1E5\u8EA2\uABAC\u0000\uD1DE" + // 28190 - 28194 + "\u8EA2\uABA7\u0000\uD1E8\u8EA2\uABB4\u8EA2\uABA1\u8EA2\uABA3" + // 28195 - 28199 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uD9CB" + // 28200 - 28204 + "\u0000\uF4F4\u8EA2\uDECE\u0000\u0000\u0000\u0000\u0000\uF7A7" + // 28205 - 28209 + "\u0000\u0000\u8EA2\uE6C4\u8EA2\uE6C3\u0000\uFAAD\u8EA2\uEBFB" + // 28210 - 28214 + "\u0000\u0000\u8EA2\uEDF0\u0000\uFCE4\u8EA2\uA2E9\u0000\u0000" + // 28215 - 28219 + "\u0000\u0000\u0000\uCFAC\u0000\uCBB8\u0000\u0000\u0000\u0000" + // 28220 - 28224 + "\u8EA2\uACEE\u0000\u0000\u0000\u0000\u8EA2\uB2A4\u8EA2\uB1FD" + // 28225 - 28229 + "\u8EA2\uB2A3\u8EA2\uB2A1\u0000\uD7CE\u8EA2\uB2A2\u8EA2\uB1FE" + // 28230 - 28234 + "\u0000\u0000\u8EA2\uB8B6\u0000\u0000\u8EA2\uBFA4\u8EA2\uBFA5" + // 28235 - 28239 + "\u8EA2\uC6B0\u8EA2\uC6B1\u0000\uE5F5\u0000\uE5F6\u8EA2\uC6AF" + // 28240 - 28244 + "\u8EA2\uC6B2\u8EA2\uC6AE\u0000\uE5F4\u0000\u0000\u0000\uEAC8" + // 28245 - 28249 + "\u0000\u0000\u0000\uE5F7\u8EA2\uCDA3\u0000\u0000\u0000\uEEC7" + // 28250 - 28254 + "\u8EA2\uD2F9\u0000\uEEC8\u0000\u0000\u0000\uF1F1\u8EA2\uD9CE" + // 28255 - 28259 + "\u8EA2\uD9CC\u8EA2\uD9CD\u8EA2\uDED1\u8EA2\uDED0\u8EA2\uDECF" + // 28260 - 28264 + "\u8EA2\uA5B6\u8EA2\uA5C2\u8EA2\uA5C9\u0000\uCBF5\u8EA2\uA5BB" + // 28265 - 28269 + "\u8EA2\uA5B4\u0000\uCBF4\u8EA2\uA5B7\u0000\uCBF8\u8EA2\uA5BA" + // 28270 - 28274 + "\u0000\u0000\u0000\uCBF3\u0000\u0000\u0000\uCBEE\u0000\u0000" + // 28275 - 28279 + "\u0000\uCBFD\u8EA2\uA5C4\u0000\uCBFE\u8EA2\uA5C8\u0000\u0000" + // 28280 - 28284 + "\u8EA2\uA5C6\u8EA2\uA5CA\u0000\uCBFB\u8EA2\uA5BE\u8EA2\uA5B2" + // 28285 - 28289 + "\u0000\u0000\u8EA2\uA5C0\u0000\uCBF2\u8EA2\uA5C5\u0000\uCBED" + // 28290 - 28294 + "\u8EA2\uA5C7\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28295 - 28299 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28300 - 28304 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28305 - 28309 + "\u0000\u0000\u0000\uCFF8\u0000\uCFEC\u0000\u0000\u0000\u0000" + // 28310 - 28314 + "\u8EA2\uA9A2\u8EA2\uA9AA\u0000\u0000\u0000\uCFEB\u0000\uCFF3" + // 28315 - 28319 + "\u0000\uD0A1\u0000\u0000\u8EA2\uA9B0\u8EA2\uA9AE\u8EA2\uA9A9" + // 28320 - 28324 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uCFED\u8EA2\uA1F0" + // 28325 - 28329 + "\u0000\uC7BA\u8EA2\uA1F4\u0000\u0000\u8EA2\uA1F8\u8EA2\uA1F5" + // 28330 - 28334 + "\u0000\u0000\u0000\u0000\u8EA2\uA1F2\u0000\uC7AC\u0000\uC7AE" + // 28335 - 28339 + "\u0000\uC7BB\u0000\u0000\u0000\uC7B0\u8EA2\uA1EA\u0000\uC7B3" + // 28340 - 28344 + "\u0000\uC7B1\u0000\uC7B2\u8EA2\uA1FA\u8EA2\uA1F7\u8EA2\uA1EE" + // 28345 - 28349 + "\u0000\uC7AF\u0000\u0000\u0000\u0000\u8EA2\uA1EB\u0000\uC7AD" + // 28350 - 28354 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA1F3\u0000\u0000" + // 28355 - 28359 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA1F6\u0000\u0000" + // 28360 - 28364 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28365 - 28369 + "\u0000\u0000\u0000\u0000\u0000\u0000\u8EA2\uA1EC\u8EA2\uA3A8" + // 28370 - 28374 + "\u0000\u0000\u0000\uC9A4\u0000\uC8F5\u0000\u0000\u0000\u0000" + // 28375 - 28379 + "\u8EA2\uA3A9\u0000\uC8F2\u0000\u0000\u0000\uC9A6\u0000\u0000" + // 28380 - 28384 + "\u0000\uC8FA\u0000\u0000\u0000\uC8F9\u8EA2\uA2FB\u0000\uC8FD" + // 28385 - 28389 + "\u0000\uC8F8\u8EA2\uA3A2\u8EA2\uA3AA\u0000\uC4EF\u0000\uC4EE" + // 28390 - 28394 + "\u8EA2\uA1B2\u0000\uC4F0\u0000\uC4F6\u0000\u0000\u0000\uC4F1" + // 28395 - 28399 + "\u0000\uC4F2\u8EA2\uA1B4\u8EA2\uA1B3\u0000\uC4F4\u0000\uC4F5" + // 28400 - 28404 + "\u0000\u0000\u0000\uC4F3\u0000\u0000\u0000\u0000\u0000\u0000" + // 28405 - 28409 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\uC5EA\u0000\uC5EB" + // 28410 - 28414 + "\u0000\uC5EC\u0000\uC5ED\u0000\uC5E9\u0000\uC5F0\u8EA2\uA1CA" + // 28415 - 28419 + "\u0000\u0000\u8EA2\uA1C6\u8EA2\uA1C9\u0000\uC5F1\u0000\uC6A3" + // 28420 - 28424 + "\u0000\u0000\u8EA2\uA1C8\u0000\u0000\u0000\uC5EE\u0000\uC5EF" + // 28425 - 28429 + "\u0000\uC5E8\u0000\u0000\u0000\u0000\u8EA2\uA1C5\u8EA2\uA1C7" + // 28430 - 28434 + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" + // 28435 - 28439 + "\u0000\u0000\u0000\uC7B7\u8EA2\uA1EF\u0000\uC7B4\u0000\uC7B8" + // 28440 - 28444 + "\u8EA2\uA1F9\u8EA2\uA1ED\u0000\uC7B5\u8EA2\uA1F1\u0000\u0000" + // 28445 - 28449 + "\u0000\u0000\u0000\u0000\u0000\uC7B6\u0000\u0000\u0000\uC7B9" + // 28450 - 28454 + "\u0000\u0000\u0000\uC7AB" + ; + } + } +} diff --git a/src/sun/nio/cs/ext/ISCII91.java b/src/sun/nio/cs/ext/ISCII91.java new file mode 100644 index 00000000..b0130983 --- /dev/null +++ b/src/sun/nio/cs/ext/ISCII91.java @@ -0,0 +1,964 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.nio.cs.Surrogate; + +public class ISCII91 extends Charset implements HistoricallyNamedCharset +{ + private static final char NUKTA_CHAR = '\u093c'; + private static final char HALANT_CHAR = '\u094d'; + private static final byte NO_CHAR = (byte)255; + + public ISCII91() { + super("x-ISCII91", ExtendedCharsets.aliasesFor("x-ISCII91")); + } + + public String historicalName() { + return "ISCII91"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof ISCII91)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static final char[] directMapTable = { + '\u0000', // ascii character + '\u0001', // ascii character + '\u0002', // ascii character + '\u0003', // ascii character + '\u0004', // ascii character + '\u0005', // ascii character + '\u0006', // ascii character + '\u0007', // ascii character + '\u0008', // ascii character + '\u0009', // ascii character + '\012', // ascii character + '\u000b', // ascii character + '\u000c', // ascii character + '\015', // ascii character + '\u000e', // ascii character + '\u000f', // ascii character + '\u0010', // ascii character + '\u0011', // ascii character + '\u0012', // ascii character + '\u0013', // ascii character + '\u0014', // ascii character + '\u0015', // ascii character + '\u0016', // ascii character + '\u0017', // ascii character + '\u0018', // ascii character + '\u0019', // ascii character + '\u001a', // ascii character + '\u001b', // ascii character + '\u001c', // ascii character + '\u001d', // ascii character + '\u001e', // ascii character + '\u001f', // ascii character + '\u0020', // ascii character + '\u0021', // ascii character + '\u0022', // ascii character + '\u0023', // ascii character + '\u0024', // ascii character + '\u0025', // ascii character + '\u0026', // ascii character + (char)0x0027, // '\u0027' control -- ascii character + '\u0028', // ascii character + '\u0029', // ascii character + '\u002a', // ascii character + '\u002b', // ascii character + '\u002c', // ascii character + '\u002d', // ascii character + '\u002e', // ascii character + '\u002f', // ascii character + '\u0030', // ascii character + '\u0031', // ascii character + '\u0032', // ascii character + '\u0033', // ascii character + '\u0034', // ascii character + '\u0035', // ascii character + '\u0036', // ascii character + '\u0037', // ascii character + '\u0038', // ascii character + '\u0039', // ascii character + '\u003a', // ascii character + '\u003b', // ascii character + '\u003c', // ascii character + '\u003d', // ascii character + '\u003e', // ascii character + '\u003f', // ascii character + '\u0040', // ascii character + '\u0041', // ascii character + '\u0042', // ascii character + '\u0043', // ascii character + '\u0044', // ascii character + '\u0045', // ascii character + '\u0046', // ascii character + '\u0047', // ascii character + '\u0048', // ascii character + '\u0049', // ascii character + '\u004a', // ascii character + '\u004b', // ascii character + '\u004c', // ascii character + '\u004d', // ascii character + '\u004e', // ascii character + '\u004f', // ascii character + '\u0050', // ascii character + '\u0051', // ascii character + '\u0052', // ascii character + '\u0053', // ascii character + '\u0054', // ascii character + '\u0055', // ascii character + '\u0056', // ascii character + '\u0057', // ascii character + '\u0058', // ascii character + '\u0059', // ascii character + '\u005a', // ascii character + '\u005b', // ascii character + '\\',// '\u005c' -- ascii character + '\u005d', // ascii character + '\u005e', // ascii character + '\u005f', // ascii character + '\u0060', // ascii character + '\u0061', // ascii character + '\u0062', // ascii character + '\u0063', // ascii character + '\u0064', // ascii character + '\u0065', // ascii character + '\u0066', // ascii character + '\u0067', // ascii character + '\u0068', // ascii character + '\u0069', // ascii character + '\u006a', // ascii character + '\u006b', // ascii character + '\u006c', // ascii character + '\u006d', // ascii character + '\u006e', // ascii character + '\u006f', // ascii character + '\u0070', // ascii character + '\u0071', // ascii character + '\u0072', // ascii character + '\u0073', // ascii character + '\u0074', // ascii character + '\u0075', // ascii character + '\u0076', // ascii character + '\u0077', // ascii character + '\u0078', // ascii character + '\u0079', // ascii character + '\u007a', // ascii character + '\u007b', // ascii character + '\u007c', // ascii character + '\u007d', // ascii character + '\u007e', // ascii character + '\u007f', // ascii character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\uffff', // unknown character + '\u0901', // a1 -- Vowel-modifier CHANDRABINDU + '\u0902', // a2 -- Vowel-modifier ANUSWAR + '\u0903', // a3 -- Vowel-modifier VISARG + + '\u0905', // a4 -- Vowel A + '\u0906', // a5 -- Vowel AA + '\u0907', // a6 -- Vowel I + '\u0908', // a7 -- Vowel II + '\u0909', // a8 -- Vowel U + '\u090a', // a9 -- Vowel UU + '\u090b', // aa -- Vowel RI + '\u090e', // ab -- Vowel E ( Southern Scripts ) + '\u090f', // ac -- Vowel EY + '\u0910', // ad -- Vowel AI + '\u090d', // ae -- Vowel AYE ( Devanagari Script ) + '\u0912', // af -- Vowel O ( Southern Scripts ) + '\u0913', // b0 -- Vowel OW + '\u0914', // b1 -- Vowel AU + '\u0911', // b2 -- Vowel AWE ( Devanagari Script ) + '\u0915', // b3 -- Consonant KA + '\u0916', // b4 -- Consonant KHA + '\u0917', // b5 -- Consonant GA + '\u0918', // b6 -- Consonant GHA + '\u0919', // b7 -- Consonant NGA + '\u091a', // b8 -- Consonant CHA + '\u091b', // b9 -- Consonant CHHA + '\u091c', // ba -- Consonant JA + '\u091d', // bb -- Consonant JHA + '\u091e', // bc -- Consonant JNA + '\u091f', // bd -- Consonant Hard TA + '\u0920', // be -- Consonant Hard THA + '\u0921', // bf -- Consonant Hard DA + '\u0922', // c0 -- Consonant Hard DHA + '\u0923', // c1 -- Consonant Hard NA + '\u0924', // c2 -- Consonant Soft TA + '\u0925', // c3 -- Consonant Soft THA + '\u0926', // c4 -- Consonant Soft DA + '\u0927', // c5 -- Consonant Soft DHA + '\u0928', // c6 -- Consonant Soft NA + '\u0929', // c7 -- Consonant NA ( Tamil ) + '\u092a', // c8 -- Consonant PA + '\u092b', // c9 -- Consonant PHA + '\u092c', // ca -- Consonant BA + '\u092d', // cb -- Consonant BHA + '\u092e', // cc -- Consonant MA + '\u092f', // cd -- Consonant YA + '\u095f', // ce -- Consonant JYA ( Bengali, Assamese & Oriya ) + '\u0930', // cf -- Consonant RA + '\u0931', // d0 -- Consonant Hard RA ( Southern Scripts ) + '\u0932', // d1 -- Consonant LA + '\u0933', // d2 -- Consonant Hard LA + '\u0934', // d3 -- Consonant ZHA ( Tamil & Malayalam ) + '\u0935', // d4 -- Consonant VA + '\u0936', // d5 -- Consonant SHA + '\u0937', // d6 -- Consonant Hard SHA + '\u0938', // d7 -- Consonant SA + '\u0939', // d8 -- Consonant HA + + '\u200d', // d9 -- Consonant INVISIBLE + '\u093e', // da -- Vowel Sign AA + + '\u093f', // db -- Vowel Sign I + '\u0940', // dc -- Vowel Sign II + '\u0941', // dd -- Vowel Sign U + '\u0942', // de -- Vowel Sign UU + '\u0943', // df -- Vowel Sign RI + '\u0946', // e0 -- Vowel Sign E ( Southern Scripts ) + '\u0947', // e1 -- Vowel Sign EY + '\u0948', // e2 -- Vowel Sign AI + '\u0945', // e3 -- Vowel Sign AYE ( Devanagari Script ) + '\u094a', // e4 -- Vowel Sign O ( Southern Scripts ) + '\u094b', // e5 -- Vowel Sign OW + '\u094c', // e6 -- Vowel Sign AU + '\u0949', // e7 -- Vowel Sign AWE ( Devanagari Script ) + + '\u094d', // e8 -- Vowel Omission Sign ( Halant ) + '\u093c', // e9 -- Diacritic Sign ( Nukta ) + '\u0964', // ea -- Full Stop ( Viram, Northern Scripts ) + + '\uffff', // eb -- This position shall not be used + '\uffff', // ec -- This position shall not be used + '\uffff', // ed -- This position shall not be used + '\uffff', // ee -- This position shall not be used + + '\ufffd', // ef -- Attribute Code ( ATR ) + '\ufffd', // f0 -- Extension Code ( EXT ) + + '\u0966', // f1 -- Digit 0 + '\u0967', // f2 -- Digit 1 + '\u0968', // f3 -- Digit 2 + '\u0969', // f4 -- Digit 3 + '\u096a', // f5 -- Digit 4 + '\u096b', // f6 -- Digit 5 + '\u096c', // f7 -- Digit 6 + '\u096d', // f8 -- Digit 7 + '\u096e', // f9 -- Digit 8 + '\u096f', // fa -- Digit 9 + + '\uffff', // fb -- This position shall not be used + '\uffff', // fc -- This position shall not be used + '\uffff', // fd -- This position shall not be used + '\uffff', // fe -- This position shall not be used + '\uffff' // ff -- This position shall not be used + }; //end of table definition + + private static final byte[] encoderMappingTable = { + NO_CHAR,NO_CHAR, //0900 + (byte)161,NO_CHAR, //0901 -- DEVANAGARI SIGN CANDRABINDU = anunasika + (byte)162,NO_CHAR, //0902 -- DEVANAGARI SIGN ANUSVARA = bindu + (byte)163,NO_CHAR, //0903 -- DEVANAGARI SIGN VISARGA + NO_CHAR,NO_CHAR, //0904 + (byte)164,NO_CHAR, //0905 -- DEVANAGARI LETTER A + (byte)165,NO_CHAR, //0906 -- DEVANAGARI LETTER AA + (byte)166,NO_CHAR, //0907 -- DEVANAGARI LETTER I + (byte)167,NO_CHAR, //0908 -- DEVANAGARI LETTER II + (byte)168,NO_CHAR, //0909 -- DEVANAGARI LETTER U + (byte)169,NO_CHAR, //090a -- DEVANAGARI LETTER UU + (byte)170,NO_CHAR, //090b -- DEVANAGARI LETTER VOCALIC R + (byte)166,(byte)233, //090c -- DEVANAGARI LETTER VOVALIC L + (byte)174,NO_CHAR, //090d -- DEVANAGARI LETTER CANDRA E + (byte)171,NO_CHAR, //090e -- DEVANAGARI LETTER SHORT E + (byte)172,NO_CHAR, //090f -- DEVANAGARI LETTER E + (byte)173,NO_CHAR, //0910 -- DEVANAGARI LETTER AI + (byte)178,NO_CHAR, //0911 -- DEVANAGARI LETTER CANDRA O + (byte)175,NO_CHAR, //0912 -- DEVANAGARI LETTER SHORT O + (byte)176,NO_CHAR, //0913 -- DEVANAGARI LETTER O + (byte)177,NO_CHAR, //0914 -- DEVANAGARI LETTER AU + (byte)179,NO_CHAR, //0915 -- DEVANAGARI LETTER KA + (byte)180,NO_CHAR, //0916 -- DEVANAGARI LETTER KHA + (byte)181,NO_CHAR, //0917 -- DEVANAGARI LETTER GA + (byte)182,NO_CHAR, //0918 -- DEVANAGARI LETTER GHA + (byte)183,NO_CHAR, //0919 -- DEVANAGARI LETTER NGA + (byte)184,NO_CHAR, //091a -- DEVANAGARI LETTER CA + (byte)185,NO_CHAR, //091b -- DEVANAGARI LETTER CHA + (byte)186,NO_CHAR, //091c -- DEVANAGARI LETTER JA + (byte)187,NO_CHAR, //091d -- DEVANAGARI LETTER JHA + (byte)188,NO_CHAR, //091e -- DEVANAGARI LETTER NYA + (byte)189,NO_CHAR, //091f -- DEVANAGARI LETTER TTA + (byte)190,NO_CHAR, //0920 -- DEVANAGARI LETTER TTHA + (byte)191,NO_CHAR, //0921 -- DEVANAGARI LETTER DDA + (byte)192,NO_CHAR, //0922 -- DEVANAGARI LETTER DDHA + (byte)193,NO_CHAR, //0923 -- DEVANAGARI LETTER NNA + (byte)194,NO_CHAR, //0924 -- DEVANAGARI LETTER TA + (byte)195,NO_CHAR, //0925 -- DEVANAGARI LETTER THA + (byte)196,NO_CHAR, //0926 -- DEVANAGARI LETTER DA + (byte)197,NO_CHAR, //0927 -- DEVANAGARI LETTER DHA + (byte)198,NO_CHAR, //0928 -- DEVANAGARI LETTER NA + (byte)199,NO_CHAR, //0929 -- DEVANAGARI LETTER NNNA <=> 0928 + 093C + (byte)200,NO_CHAR, //092a -- DEVANAGARI LETTER PA + (byte)201,NO_CHAR, //092b -- DEVANAGARI LETTER PHA + (byte)202,NO_CHAR, //092c -- DEVANAGARI LETTER BA + (byte)203,NO_CHAR, //092d -- DEVANAGARI LETTER BHA + (byte)204,NO_CHAR, //092e -- DEVANAGARI LETTER MA + (byte)205,NO_CHAR, //092f -- DEVANAGARI LETTER YA + (byte)207,NO_CHAR, //0930 -- DEVANAGARI LETTER RA + (byte)208,NO_CHAR, //0931 -- DEVANAGARI LETTER RRA <=> 0930 + 093C + (byte)209,NO_CHAR, //0932 -- DEVANAGARI LETTER LA + (byte)210,NO_CHAR, //0933 -- DEVANAGARI LETTER LLA + (byte)211,NO_CHAR, //0934 -- DEVANAGARI LETTER LLLA <=> 0933 + 093C + (byte)212,NO_CHAR, //0935 -- DEVANAGARI LETTER VA + (byte)213,NO_CHAR, //0936 -- DEVANAGARI LETTER SHA + (byte)214,NO_CHAR, //0937 -- DEVANAGARI LETTER SSA + (byte)215,NO_CHAR, //0938 -- DEVANAGARI LETTER SA + (byte)216,NO_CHAR, //0939 -- DEVANAGARI LETTER HA + NO_CHAR,NO_CHAR, //093a + NO_CHAR,NO_CHAR, //093b + (byte)233,NO_CHAR, //093c -- DEVANAGARI SIGN NUKTA + (byte)234,(byte)233, //093d -- DEVANAGARI SIGN AVAGRAHA + (byte)218,NO_CHAR, //093e -- DEVANAGARI VOWEL SIGN AA + (byte)219,NO_CHAR, //093f -- DEVANAGARI VOWEL SIGN I + (byte)220,NO_CHAR, //0940 -- DEVANAGARI VOWEL SIGN II + (byte)221,NO_CHAR, //0941 -- DEVANAGARI VOWEL SIGN U + (byte)222,NO_CHAR, //0942 -- DEVANAGARI VOWEL SIGN UU + (byte)223,NO_CHAR, //0943 -- DEVANAGARI VOWEL SIGN VOCALIC R + (byte)223,(byte)233, //0944 -- DEVANAGARI VOWEL SIGN VOCALIC RR + (byte)227,NO_CHAR, //0945 -- DEVANAGARI VOWEL SIGN CANDRA E + (byte)224,NO_CHAR, //0946 -- DEVANAGARI VOWEL SIGN SHORT E + (byte)225,NO_CHAR, //0947 -- DEVANAGARI VOWEL SIGN E + (byte)226,NO_CHAR, //0948 -- DEVANAGARI VOWEL SIGN AI + (byte)231,NO_CHAR, //0949 -- DEVANAGARI VOWEL SIGN CANDRA O + (byte)228,NO_CHAR, //094a -- DEVANAGARI VOWEL SIGN SHORT O + (byte)229,NO_CHAR, //094b -- DEVANAGARI VOWEL SIGN O + (byte)230,NO_CHAR, //094c -- DEVANAGARI VOWEL SIGN AU + (byte)232,NO_CHAR, //094d -- DEVANAGARI SIGN VIRAMA ( halant ) + NO_CHAR,NO_CHAR, //094e + NO_CHAR,NO_CHAR, //094f + (byte)161,(byte)233, //0950 -- DEVANAGARI OM + (byte)240,(byte)181, //0951 -- DEVANAGARI STRESS SIGN UDATTA + (byte)240,(byte)184, //0952 -- DEVANAGARI STRESS SIGN ANUDATTA + (byte)254,NO_CHAR, //0953 -- DEVANAGARI GRAVE ACCENT || MISSING + (byte)254,NO_CHAR, //0954 -- DEVANAGARI ACUTE ACCENT || MISSING + NO_CHAR,NO_CHAR, //0955 + NO_CHAR,NO_CHAR, //0956 + NO_CHAR,NO_CHAR, //0957 + (byte)179,(byte)233, //0958 -- DEVANAGARI LETTER QA <=> 0915 + 093C + (byte)180,(byte)233, //0959 -- DEVANAGARI LETTER KHHA <=> 0916 + 093C + (byte)181,(byte)233, //095a -- DEVANAGARI LETTER GHHA <=> 0917 + 093C + (byte)186,(byte)233, //095b -- DEVANAGARI LETTER ZA <=> 091C + 093C + (byte)191,(byte)233, //095c -- DEVANAGARI LETTER DDDHA <=> 0921 + 093C + (byte)192,(byte)233, //095d -- DEVANAGARI LETTER RHA <=> 0922 + 093C + (byte)201,(byte)233, //095e -- DEVANAGARI LETTER FA <=> 092B + 093C + (byte)206,NO_CHAR, //095f -- DEVANAGARI LETTER YYA <=> 092F + 093C + (byte)170,(byte)233, //0960 -- DEVANAGARI LETTER VOCALIC RR + (byte)167,(byte)233, //0961 -- DEVANAGARI LETTER VOCALIC LL + (byte)219,(byte)233, //0962 -- DEVANAGARI VOWEL SIGN VOCALIC L + (byte)220,(byte)233, //0963 -- DEVANAGARI VOWEL SIGN VOCALIC LL + (byte)234,NO_CHAR, //0964 -- DEVANAGARI DANDA ( phrase separator ) + (byte)234,(byte)234, //0965 -- DEVANAGARI DOUBLE DANDA + (byte)241,NO_CHAR, //0966 -- DEVANAGARI DIGIT ZERO + (byte)242,NO_CHAR, //0967 -- DEVANAGARI DIGIT ONE + (byte)243,NO_CHAR, //0968 -- DEVANAGARI DIGIT TWO + (byte)244,NO_CHAR, //0969 -- DEVANAGARI DIGIT THREE + (byte)245,NO_CHAR, //096a -- DEVANAGARI DIGIT FOUR + (byte)246,NO_CHAR, //096b -- DEVANAGARI DIGIT FIVE + (byte)247,NO_CHAR, //096c -- DEVANAGARI DIGIT SIX + (byte)248,NO_CHAR, //096d -- DEVANAGARI DIGIT SEVEN + (byte)249,NO_CHAR, //096e -- DEVANAGARI DIGIT EIGHT + (byte)250,NO_CHAR, //096f -- DEVANAGARI DIGIT NINE + (byte)240,(byte)191, //0970 -- DEVANAGARI ABBREVIATION SIGN + NO_CHAR,NO_CHAR, //0971 -- reserved + NO_CHAR,NO_CHAR, //0972 -- reserved + NO_CHAR,NO_CHAR, //0973 -- reserved + NO_CHAR,NO_CHAR, //0974 -- reserved + NO_CHAR,NO_CHAR, //0975 -- reserved + NO_CHAR,NO_CHAR, //0976 -- reserved + NO_CHAR,NO_CHAR, //0977 -- reserved + NO_CHAR,NO_CHAR, //0978 -- reserved + NO_CHAR,NO_CHAR, //0979 -- reserved + NO_CHAR,NO_CHAR, //097a -- reserved + NO_CHAR,NO_CHAR, //097b -- reserved + NO_CHAR,NO_CHAR, //097c -- reserved + NO_CHAR,NO_CHAR, //097d -- reserved + NO_CHAR,NO_CHAR, //097e -- reserved + NO_CHAR,NO_CHAR //097f -- reserved + }; //end of table definition + + private static class Decoder extends CharsetDecoder { + + private static final char ZWNJ_CHAR = '\u200c'; + private static final char ZWJ_CHAR = '\u200d'; + private static final char INVALID_CHAR = '\uffff'; + + private char contextChar = INVALID_CHAR; + private boolean needFlushing = false; + + + private Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + protected CoderResult implFlush(CharBuffer out) { + if(needFlushing) { + if (out.remaining() < 1) { + return CoderResult.OVERFLOW; + } else { + out.put(contextChar); + } + } + contextChar = INVALID_CHAR; + needFlushing = false; + return CoderResult.UNDERFLOW; + } + + /*Rules: + * 1)ATR,EXT,following character to be replaced with '\ufffd' + * 2)Halant + Halant => '\u094d' (Virama) + '\u200c'(ZWNJ) + * 3)Halant + Nukta => '\u094d' (Virama) + '\u200d'(ZWJ) + */ + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + int index = sa[sp]; + index = ( index < 0 )? ( index + 255 ):index; + char currentChar = directMapTable[index]; + + // if the contextChar is either ATR || EXT + // set the output to '\ufffd' + if(contextChar == '\ufffd') { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = '\ufffd'; + contextChar = INVALID_CHAR; + needFlushing = false; + sp++; + continue; + } + + switch(currentChar) { + case '\u0901': + case '\u0907': + case '\u0908': + case '\u090b': + case '\u093f': + case '\u0940': + case '\u0943': + case '\u0964': + if(needFlushing) { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + contextChar = currentChar; + needFlushing = true; + sp++; + continue; + case NUKTA_CHAR: + if (dl - dp < 1) + return CoderResult.OVERFLOW; + switch(contextChar) { + case '\u0901': + da[dp++] = '\u0950'; + break; + case '\u0907': + da[dp++] = '\u090c'; + break; + case '\u0908': + da[dp++] = '\u0961'; + break; + case '\u090b': + da[dp++] = '\u0960'; + break; + case '\u093f': + da[dp++] = '\u0962'; + break; + case '\u0940': + da[dp++] = '\u0963'; + break; + case '\u0943': + da[dp++] = '\u0944'; + break; + case '\u0964': + da[dp++] = '\u093d'; + break; + case HALANT_CHAR: + if(needFlushing) { + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + da[dp++] = ZWJ_CHAR; + break; + default: + if(needFlushing) { + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + da[dp++] = NUKTA_CHAR; + } + break; + case HALANT_CHAR: + if (dl - dp < 1) + return CoderResult.OVERFLOW; + if(needFlushing) { + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + if(contextChar == HALANT_CHAR) { + da[dp++] = ZWNJ_CHAR; + break; + } + da[dp++] = HALANT_CHAR; + break; + case INVALID_CHAR: + if(needFlushing) { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + return CoderResult.unmappableForLength(1); + default: + if (dl - dp < 1) + return CoderResult.OVERFLOW; + if(needFlushing) { + da[dp++] = contextChar; + contextChar = currentChar; + sp++; + continue; + } + da[dp++] = currentChar; + break; + }//end switch + + contextChar = currentChar; + needFlushing = false; + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + + try { + while (src.hasRemaining()) { + int index = src.get(); + index = ( index < 0 )? ( index + 255 ):index; + char currentChar = directMapTable[index]; + + // if the contextChar is either ATR || EXT + // set the output to '\ufffd' + if(contextChar == '\ufffd') { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put('\ufffd'); + contextChar = INVALID_CHAR; + needFlushing = false; + mark++; + continue; + } + + switch(currentChar) { + case '\u0901': + case '\u0907': + case '\u0908': + case '\u090b': + case '\u093f': + case '\u0940': + case '\u0943': + case '\u0964': + if(needFlushing) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + contextChar = currentChar; + needFlushing = true; + mark++; + continue; + case NUKTA_CHAR: + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + switch(contextChar) { + case '\u0901': + dst.put('\u0950'); + break; + case '\u0907': + dst.put('\u090c'); + break; + case '\u0908': + dst.put('\u0961'); + break; + case '\u090b': + dst.put('\u0960'); + break; + case '\u093f': + dst.put('\u0962'); + break; + case '\u0940': + dst.put('\u0963'); + break; + case '\u0943': + dst.put('\u0944'); + break; + case '\u0964': + dst.put('\u093d'); + break; + case HALANT_CHAR: + if(needFlushing) { + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + dst.put(ZWJ_CHAR); + break; + default: + if(needFlushing) { + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + dst.put(NUKTA_CHAR); + } + break; + case HALANT_CHAR: + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + if(needFlushing) { + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + if(contextChar == HALANT_CHAR) { + dst.put(ZWNJ_CHAR); + break; + } + dst.put(HALANT_CHAR); + break; + case INVALID_CHAR: + if(needFlushing) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + return CoderResult.unmappableForLength(1); + default: + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + if(needFlushing) { + dst.put(contextChar); + contextChar = currentChar; + mark++; + continue; + } + dst.put(currentChar); + break; + }//end switch + contextChar = currentChar; + needFlushing = false; + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + } + + private static class Encoder extends CharsetEncoder { + + private static final byte NO_CHAR = (byte)255; + + //private static CharToByteISCII91 c2b = new CharToByteISCII91(); + //private static final byte[] directMapTable = c2b.getISCIIEncoderMap(); + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + private Encoder(Charset cs) { + super(cs, 2.0f, 2.0f); + } + + public boolean canEncode(char ch) { + //check for Devanagari range,ZWJ,ZWNJ and ASCII range. + return ((ch >= '\u0900' && ch <= '\u097f' && + encoderMappingTable[2*(ch-'\u0900')] != NO_CHAR) || + (ch == '\u200d') || + (ch == '\u200c') || + (ch <= '\u007f')); + } + + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int outputSize = 0; + + try { + char inputChar; + while (sp < sl) { + int index = Integer.MIN_VALUE; + inputChar = sa[sp]; + + if (inputChar >= 0x0000 && inputChar <= 0x007f) { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte) inputChar; + sp++; + continue; + } + + // if inputChar == ZWJ replace it with halant + // if inputChar == ZWNJ replace it with Nukta + + if (inputChar == 0x200c) { + inputChar = HALANT_CHAR; + } + else if (inputChar == 0x200d) { + inputChar = NUKTA_CHAR; + } + + if (inputChar >= 0x0900 && inputChar <= 0x097f) { + index = ((int)(inputChar) - 0x0900)*2; + } + + if (Character.isSurrogate(inputChar)) { + if (sgp.parse(inputChar, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + + if (index == Integer.MIN_VALUE || + encoderMappingTable[index] == NO_CHAR) { + return CoderResult.unmappableForLength(1); + } else { + if(encoderMappingTable[index + 1] == NO_CHAR) { + if(dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = encoderMappingTable[index]; + } else { + if(dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = encoderMappingTable[index]; + da[dp++] = encoderMappingTable[index + 1]; + } + sp++; + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + + try { + char inputChar; + while (src.hasRemaining()) { + int index = Integer.MIN_VALUE; + inputChar = src.get(); + + if (inputChar >= 0x0000 && inputChar <= 0x007f) { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte) inputChar); + mark++; + continue; + } + + // if inputChar == ZWJ replace it with halant + // if inputChar == ZWNJ replace it with Nukta + + if (inputChar == 0x200c) { + inputChar = HALANT_CHAR; + } + else if (inputChar == 0x200d) { + inputChar = NUKTA_CHAR; + } + + if (inputChar >= 0x0900 && inputChar <= 0x097f) { + index = ((int)(inputChar) - 0x0900)*2; + } + + if (Character.isSurrogate(inputChar)) { + if (sgp.parse(inputChar, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + + if (index == Integer.MIN_VALUE || + encoderMappingTable[index] == NO_CHAR) { + return CoderResult.unmappableForLength(1); + } else { + if(encoderMappingTable[index + 1] == NO_CHAR) { + if(dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(encoderMappingTable[index]); + } else { + if(dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put(encoderMappingTable[index]); + dst.put(encoderMappingTable[index + 1]); + } + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022.java b/src/sun/nio/cs/ext/ISO2022.java new file mode 100644 index 00000000..71995be4 --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022.java @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.Surrogate; + +abstract class ISO2022 + extends Charset +{ + + private static final byte ISO_ESC = 0x1b; + private static final byte ISO_SI = 0x0f; + private static final byte ISO_SO = 0x0e; + private static final byte ISO_SS2_7 = 0x4e; + private static final byte ISO_SS3_7 = 0x4f; + private static final byte MSB = (byte)0x80; + private static final char REPLACE_CHAR = '\uFFFD'; + private static final byte minDesignatorLength = 3; + + public ISO2022(String csname, String[] aliases) { + super(csname, aliases); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + protected static class Decoder extends CharsetDecoder { + + // Value to be filled by subclass + protected byte SODesig[][]; + protected byte SS2Desig[][] = null; + protected byte SS3Desig[][] = null; + + protected CharsetDecoder SODecoder[]; + protected CharsetDecoder SS2Decoder[] = null; + protected CharsetDecoder SS3Decoder[] = null; + + private static final byte SOFlag = 0; + private static final byte SS2Flag = 1; + private static final byte SS3Flag = 2; + + private int curSODes, curSS2Des, curSS3Des; + private boolean shiftout; + private CharsetDecoder tmpDecoder[]; + + protected Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + } + + protected void implReset() { + curSODes = 0; + curSS2Des = 0; + curSS3Des = 0; + shiftout = false; + } + + private char decode(byte byte1, byte byte2, byte shiftFlag) + { + byte1 |= MSB; + byte2 |= MSB; + + byte[] tmpByte = { byte1,byte2 }; + char[] tmpChar = new char[1]; + int i = 0, + tmpIndex = 0; + + switch(shiftFlag) { + case SOFlag: + tmpIndex = curSODes; + tmpDecoder = SODecoder; + break; + case SS2Flag: + tmpIndex = curSS2Des; + tmpDecoder = SS2Decoder; + break; + case SS3Flag: + tmpIndex = curSS3Des; + tmpDecoder = SS3Decoder; + break; + } + + if (tmpDecoder != null) { + for(i = 0; i < tmpDecoder.length; i++) { + if(tmpIndex == i) { + try { + ByteBuffer bb = ByteBuffer.wrap(tmpByte,0,2); + CharBuffer cc = CharBuffer.wrap(tmpChar,0,1); + tmpDecoder[i].decode(bb, cc, true); + cc.flip(); + return cc.get(); + } catch (Exception e) {} + } + } + } + return REPLACE_CHAR; + } + + private int findDesig(byte[] in, int sp, int sl, byte[][] desigs) { + if (desigs == null) return -1; + int i = 0; + while (i < desigs.length) { + if (desigs[i] != null && sl - sp >= desigs[i].length) { + int j = 0; + while (j < desigs[i].length && in[sp+j] == desigs[i][j]) { j++; } + if (j == desigs[i].length) + return i; + } + i++; + } + return -1; + } + + private int findDesigBuf(ByteBuffer in, byte[][] desigs) { + if (desigs == null) return -1; + int i = 0; + while (i < desigs.length) { + if (desigs[i] != null && in.remaining() >= desigs[i].length) { + int j = 0; + in.mark(); + while (j < desigs[i].length && in.get() == desigs[i][j]) { j++; } + if (j == desigs[i].length) + return i; + in.reset(); + } + i++; + } + return -1; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int b1 = 0, b2 = 0, b3 = 0; + + try { + while (sp < sl) { + b1 = sa[sp] & 0xff; + int inputSize = 1; + switch (b1) { + case ISO_SO: + shiftout = true; + inputSize = 1; + break; + case ISO_SI: + shiftout = false; + inputSize = 1; + break; + case ISO_ESC: + if (sl - sp - 1 < minDesignatorLength) + return CoderResult.UNDERFLOW; + + int desig = findDesig(sa, sp + 1, sl, SODesig); + if (desig != -1) { + curSODes = desig; + inputSize = SODesig[desig].length + 1; + break; + } + desig = findDesig(sa, sp + 1, sl, SS2Desig); + if (desig != -1) { + curSS2Des = desig; + inputSize = SS2Desig[desig].length + 1; + break; + } + desig = findDesig(sa, sp + 1, sl, SS3Desig); + if (desig != -1) { + curSS3Des = desig; + inputSize = SS3Desig[desig].length + 1; + break; + } + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + b1 = sa[sp + 1]; + switch(b1) { + case ISO_SS2_7: + if (sl - sp < 4) + return CoderResult.UNDERFLOW; + b2 = sa[sp +2]; + b3 = sa[sp +3]; + if (dl - dp <1) + return CoderResult.OVERFLOW; + da[dp] = decode((byte)b2, + (byte)b3, + SS2Flag); + dp++; + inputSize = 4; + break; + case ISO_SS3_7: + if (sl - sp < 4) + return CoderResult.UNDERFLOW; + b2 = sa[sp + 2]; + b3 = sa[sp + 3]; + if (dl - dp <1) + return CoderResult.OVERFLOW; + da[dp] = decode((byte)b2, + (byte)b3, + SS3Flag); + dp++; + inputSize = 4; + break; + default: + return CoderResult.malformedForLength(2); + } + break; + default: + if (dl - dp < 1) + return CoderResult.OVERFLOW; + if (!shiftout) { + da[dp++]=(char)(sa[sp] & 0xff); + } else { + if (dl - dp < 1) + return CoderResult.OVERFLOW; + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + b2 = sa[sp+1] & 0xff; + da[dp++] = decode((byte)b1, + (byte)b2, + SOFlag); + inputSize = 2; + } + break; + } + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + int b1 = 0, b2 = 0, b3 = 0; + + try { + while (src.hasRemaining()) { + b1 = src.get(); + int inputSize = 1; + switch (b1) { + case ISO_SO: + shiftout = true; + break; + case ISO_SI: + shiftout = false; + break; + case ISO_ESC: + if (src.remaining() < minDesignatorLength) + return CoderResult.UNDERFLOW; + + int desig = findDesigBuf(src, SODesig); + if (desig != -1) { + curSODes = desig; + inputSize = SODesig[desig].length + 1; + break; + } + desig = findDesigBuf(src, SS2Desig); + if (desig != -1) { + curSS2Des = desig; + inputSize = SS2Desig[desig].length + 1; + break; + } + desig = findDesigBuf(src, SS3Desig); + if (desig != -1) { + curSS3Des = desig; + inputSize = SS3Desig[desig].length + 1; + break; + } + + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b1 = src.get(); + switch(b1) { + case ISO_SS2_7: + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + b2 = src.get(); + b3 = src.get(); + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(decode((byte)b2, + (byte)b3, + SS2Flag)); + inputSize = 4; + break; + case ISO_SS3_7: + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + b2 = src.get(); + b3 = src.get(); + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put(decode((byte)b2, + (byte)b3, + SS3Flag)); + inputSize = 4; + break; + default: + return CoderResult.malformedForLength(2); + } + break; + default: + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + if (!shiftout) { + dst.put((char)(b1 & 0xff)); + } else { + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b2 = src.get() & 0xff; + dst.put(decode((byte)b1, + (byte)b2, + SOFlag)); + inputSize = 2; + } + break; + } + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } catch (Exception e) { e.printStackTrace(); return CoderResult.OVERFLOW; } + finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + } + + protected static class Encoder extends CharsetEncoder { + private final Surrogate.Parser sgp = new Surrogate.Parser(); + public static final byte SS2 = (byte)0x8e; + public static final byte PLANE2 = (byte)0xA2; + public static final byte PLANE3 = (byte)0xA3; + private final byte MSB = (byte)0x80; + + protected final byte maximumDesignatorLength = 4; + + protected String SODesig, + SS2Desig = null, + SS3Desig = null; + + protected CharsetEncoder ISOEncoder; + + private boolean shiftout = false; + private boolean SODesDefined = false; + private boolean SS2DesDefined = false; + private boolean SS3DesDefined = false; + + private boolean newshiftout = false; + private boolean newSODesDefined = false; + private boolean newSS2DesDefined = false; + private boolean newSS3DesDefined = false; + + protected Encoder(Charset cs) { + super(cs, 4.0f, 8.0f); + } + + public boolean canEncode(char c) { + return (ISOEncoder.canEncode(c)); + } + + protected void implReset() { + shiftout = false; + SODesDefined = false; + SS2DesDefined = false; + SS3DesDefined = false; + } + + private int unicodeToNative(char unicode, byte ebyte[]) + { + int index = 0; + byte tmpByte[]; + char convChar[] = {unicode}; + byte convByte[] = new byte[4]; + int converted; + + try{ + CharBuffer cc = CharBuffer.wrap(convChar); + ByteBuffer bb = ByteBuffer.allocate(4); + ISOEncoder.encode(cc, bb, true); + bb.flip(); + converted = bb.remaining(); + bb.get(convByte,0,converted); + } catch(Exception e) { + return -1; + } + + if (converted == 2) { + if (!SODesDefined) { + newSODesDefined = true; + ebyte[0] = ISO_ESC; + tmpByte = SODesig.getBytes(); + System.arraycopy(tmpByte,0,ebyte,1,tmpByte.length); + index = tmpByte.length+1; + } + if (!shiftout) { + newshiftout = true; + ebyte[index++] = ISO_SO; + } + ebyte[index++] = (byte)(convByte[0] & 0x7f); + ebyte[index++] = (byte)(convByte[1] & 0x7f); + } else { + if(convByte[0] == SS2) { + if (convByte[1] == PLANE2) { + if (!SS2DesDefined) { + newSS2DesDefined = true; + ebyte[0] = ISO_ESC; + tmpByte = SS2Desig.getBytes(); + System.arraycopy(tmpByte, 0, ebyte, 1, tmpByte.length); + index = tmpByte.length+1; + } + ebyte[index++] = ISO_ESC; + ebyte[index++] = ISO_SS2_7; + ebyte[index++] = (byte)(convByte[2] & 0x7f); + ebyte[index++] = (byte)(convByte[3] & 0x7f); + } else if (convByte[1] == PLANE3) { + if(!SS3DesDefined){ + newSS3DesDefined = true; + ebyte[0] = ISO_ESC; + tmpByte = SS3Desig.getBytes(); + System.arraycopy(tmpByte, 0, ebyte, 1, tmpByte.length); + index = tmpByte.length+1; + } + ebyte[index++] = ISO_ESC; + ebyte[index++] = ISO_SS3_7; + ebyte[index++] = (byte)(convByte[2] & 0x7f); + ebyte[index++] = (byte)(convByte[3] & 0x7f); + } + } + } + return index; + } + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int outputSize = 0; + byte[] outputByte = new byte[8]; + newshiftout = shiftout; + newSODesDefined = SODesDefined; + newSS2DesDefined = SS2DesDefined; + newSS3DesDefined = SS3DesDefined; + + try { + while (sp < sl) { + char c = sa[sp]; + if (Character.isSurrogate(c)) { + if (sgp.parse(c, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + + if (c < 0x80) { // ASCII + if (shiftout){ + newshiftout = false; + outputSize = 2; + outputByte[0] = ISO_SI; + outputByte[1] = (byte)(c & 0x7f); + } else { + outputSize = 1; + outputByte[0] = (byte)(c & 0x7f); + } + if(sa[sp] == '\n'){ + newSODesDefined = false; + newSS2DesDefined = false; + newSS3DesDefined = false; + } + } else { + outputSize = unicodeToNative(c, outputByte); + if (outputSize == 0) { + return CoderResult.unmappableForLength(1); + } + } + if (dl - dp < outputSize) + return CoderResult.OVERFLOW; + + for (int i = 0; i < outputSize; i++) + da[dp++] = outputByte[i]; + sp++; + shiftout = newshiftout; + SODesDefined = newSODesDefined; + SS2DesDefined = newSS2DesDefined; + SS3DesDefined = newSS3DesDefined; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int outputSize = 0; + byte[] outputByte = new byte[8]; + int inputSize = 0; // Size of input + newshiftout = shiftout; + newSODesDefined = SODesDefined; + newSS2DesDefined = SS2DesDefined; + newSS3DesDefined = SS3DesDefined; + int mark = src.position(); + + try { + while (src.hasRemaining()) { + char inputChar = src.get(); + if (Character.isSurrogate(inputChar)) { + if (sgp.parse(inputChar, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + if (inputChar < 0x80) { // ASCII + if (shiftout){ + newshiftout = false; + outputSize = 2; + outputByte[0] = ISO_SI; + outputByte[1] = (byte)(inputChar & 0x7f); + } else { + outputSize = 1; + outputByte[0] = (byte)(inputChar & 0x7f); + } + if(inputChar == '\n'){ + newSODesDefined = false; + newSS2DesDefined = false; + newSS3DesDefined = false; + } + } else { + outputSize = unicodeToNative(inputChar, outputByte); + if (outputSize == 0) { + return CoderResult.unmappableForLength(1); + } + } + + if (dst.remaining() < outputSize) + return CoderResult.OVERFLOW; + for (int i = 0; i < outputSize; i++) + dst.put(outputByte[i]); + mark++; + shiftout = newshiftout; + SODesDefined = newSODesDefined; + SS2DesDefined = newSS2DesDefined; + SS3DesDefined = newSS3DesDefined; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_CN.java b/src/sun/nio/cs/ext/ISO2022_CN.java new file mode 100644 index 00000000..48fd0af3 --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_CN.java @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.nio.cs.US_ASCII; + +public class ISO2022_CN + extends Charset + implements HistoricallyNamedCharset +{ + private static final byte ISO_ESC = 0x1b; + private static final byte ISO_SI = 0x0f; + private static final byte ISO_SO = 0x0e; + private static final byte ISO_SS2_7 = 0x4e; + private static final byte ISO_SS3_7 = 0x4f; + private static final byte MSB = (byte)0x80; + private static final char REPLACE_CHAR = '\uFFFD'; + + private static final byte SODesigGB = 0; + private static final byte SODesigCNS = 1; + + public ISO2022_CN() { + super("ISO-2022-CN", ExtendedCharsets.aliasesFor("ISO-2022-CN")); + } + + public String historicalName() { + return "ISO2022CN"; + } + + public boolean contains(Charset cs) { + return ((cs instanceof EUC_CN) // GB2312-80 repertoire + || (cs instanceof US_ASCII) + || (cs instanceof EUC_TW) // CNS11643 repertoire + || (cs instanceof ISO2022_CN)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + throw new UnsupportedOperationException(); + } + + public boolean canEncode() { + return false; + } + + static class Decoder extends CharsetDecoder { + private boolean shiftOut; + private byte currentSODesig; + + private static final Charset gb2312 = new EUC_CN(); + private static final Charset cns = new EUC_TW(); + private final DoubleByte.Decoder gb2312Decoder; + private final EUC_TW.Decoder cnsDecoder; + + Decoder(Charset cs) { + super(cs, 1.0f, 1.0f); + shiftOut = false; + currentSODesig = SODesigGB; + gb2312Decoder = (DoubleByte.Decoder)gb2312.newDecoder(); + cnsDecoder = (EUC_TW.Decoder)cns.newDecoder(); + } + + protected void implReset() { + shiftOut= false; + currentSODesig = SODesigGB; + } + + private char cnsDecode(byte byte1, byte byte2, byte SS) { + byte1 |= MSB; + byte2 |= MSB; + int p = 0; + if (SS == ISO_SS2_7) + p = 1; //plane 2, index -- 1 + else if (SS == ISO_SS3_7) + p = 2; //plane 3, index -- 2 + else + return REPLACE_CHAR; //never happen. + char[] ret = cnsDecoder.toUnicode(byte1 & 0xff, + byte2 & 0xff, + p); + if (ret == null || ret.length == 2) + return REPLACE_CHAR; + return ret[0]; + } + + private char SODecode(byte byte1, byte byte2, byte SOD) { + byte1 |= MSB; + byte2 |= MSB; + if (SOD == SODesigGB) { + return gb2312Decoder.decodeDouble(byte1 & 0xff, + byte2 & 0xff); + } else { // SOD == SODesigCNS + char[] ret = cnsDecoder.toUnicode(byte1 & 0xff, + byte2 & 0xff, + 0); + if (ret == null) + return REPLACE_CHAR; + return ret[0]; + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + int inputSize = 0; + char c = REPLACE_CHAR; + try { + while (src.hasRemaining()) { + b1 = src.get(); + inputSize = 1; + + while (b1 == ISO_ESC || + b1 == ISO_SO || + b1 == ISO_SI) { + if (b1 == ISO_ESC) { // ESC + currentSODesig = SODesigGB; + + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + + b2 = src.get(); + inputSize++; + + if ((b2 & (byte)0x80) != 0) + return CoderResult.malformedForLength(inputSize); + + if (b2 == (byte)0x24) { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + + b3 = src.get(); + inputSize++; + + if ((b3 & (byte)0x80) != 0) + return CoderResult.malformedForLength(inputSize); + if (b3 == 'A'){ // "$A" + currentSODesig = SODesigGB; + } else if (b3 == ')') { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b4 = src.get(); + inputSize++; + if (b4 == 'A'){ // "$)A" + currentSODesig = SODesigGB; + } else if (b4 == 'G'){ // "$)G" + currentSODesig = SODesigCNS; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b3 == '*') { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b4 = src.get(); + inputSize++; + if (b4 != 'H') { // "$*H" + //SS2Desig -> CNS-P1 + return CoderResult.malformedForLength(inputSize); + } + } else if (b3 == '+') { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b4 = src.get(); + inputSize++; + if (b4 != 'I'){ // "$+I" + //SS3Desig -> CNS-P2. + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b2 == ISO_SS2_7 || b2 == ISO_SS3_7) { + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + b3 = src.get(); + b4 = src.get(); + inputSize += 2; + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + //SS2->CNS-P2, SS3->CNS-P3 + c = cnsDecode(b3, b4, b2); + if (c == REPLACE_CHAR) + return CoderResult.unmappableForLength(inputSize); + dst.put(c); + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b1 == ISO_SO) { + shiftOut = true; + } else if (b1 == ISO_SI) { // shift back in + shiftOut = false; + } + mark += inputSize; + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b1 = src.get(); + inputSize = 1; + } + + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + + if (!shiftOut) { + dst.put((char)(b1 & 0xff)); //clear the upper byte + mark += inputSize; + } else { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + b2 = src.get(); + inputSize++; + c = SODecode(b1, b2, currentSODesig); + if (c == REPLACE_CHAR) + return CoderResult.unmappableForLength(inputSize); + dst.put(c); + mark += inputSize; + } + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + int inputSize = 0; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char c = REPLACE_CHAR; + + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + b1 = sa[sp]; + inputSize = 1; + + while (b1 == ISO_ESC || b1 == ISO_SO || b1 == ISO_SI) { + if (b1 == ISO_ESC) { // ESC + currentSODesig = SODesigGB; + + if (sp + 2 > sl) + return CoderResult.UNDERFLOW; + + b2 = sa[sp + 1]; + inputSize++; + + if ((b2 & (byte)0x80) != 0) + return CoderResult.malformedForLength(inputSize); + if (b2 == (byte)0x24) { + if (sp + 3 > sl) + return CoderResult.UNDERFLOW; + + b3 = sa[sp + 2]; + inputSize++; + + if ((b3 & (byte)0x80) != 0) + return CoderResult.malformedForLength(inputSize); + if (b3 == 'A'){ // "$A" + /* $A is not a legal designator sequence for + ISO2022_CN, it is listed as an escape sequence + for GB2312 in ISO2022-JP-2. Keep it here just for + the sake of "compatibility". + */ + currentSODesig = SODesigGB; + } else if (b3 == ')') { + if (sp + 4 > sl) + return CoderResult.UNDERFLOW; + b4 = sa[sp + 3]; + inputSize++; + + if (b4 == 'A'){ // "$)A" + currentSODesig = SODesigGB; + } else if (b4 == 'G'){ // "$)G" + currentSODesig = SODesigCNS; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b3 == '*') { + if (sp + 4 > sl) + return CoderResult.UNDERFLOW; + b4 = sa[sp + 3]; + inputSize++; + if (b4 != 'H'){ // "$*H" + return CoderResult.malformedForLength(inputSize); + } + } else if (b3 == '+') { + if (sp + 4 > sl) + return CoderResult.UNDERFLOW; + b4 = sa[sp + 3]; + inputSize++; + if (b4 != 'I'){ // "$+I" + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b2 == ISO_SS2_7 || b2 == ISO_SS3_7) { + if (sp + 4 > sl) { + return CoderResult.UNDERFLOW; + } + b3 = sa[sp + 2]; + b4 = sa[sp + 3]; + if (dl - dp < 1) { + return CoderResult.OVERFLOW; + } + inputSize += 2; + c = cnsDecode(b3, b4, b2); + if (c == REPLACE_CHAR) + return CoderResult.unmappableForLength(inputSize); + da[dp++] = c; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b1 == ISO_SO) { + shiftOut = true; + } else if (b1 == ISO_SI) { // shift back in + shiftOut = false; + } + sp += inputSize; + if (sp + 1 > sl) + return CoderResult.UNDERFLOW; + b1 = sa[sp]; + inputSize = 1; + } + + if (dl - dp < 1) { + return CoderResult.OVERFLOW; + } + + if (!shiftOut) { + da[dp++] = (char)(b1 & 0xff); //clear the upper byte + } else { + if (sp + 2 > sl) + return CoderResult.UNDERFLOW; + b2 = sa[sp + 1]; + inputSize++; + c = SODecode(b1, b2, currentSODesig); + if (c == REPLACE_CHAR) + return CoderResult.unmappableForLength(inputSize); + da[dp++] = c; + } + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, + CharBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_CN_CNS.java b/src/sun/nio/cs/ext/ISO2022_CN_CNS.java new file mode 100644 index 00000000..e68a57b8 --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_CN_CNS.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class ISO2022_CN_CNS extends ISO2022 implements HistoricallyNamedCharset +{ + public ISO2022_CN_CNS() { + super("x-ISO-2022-CN-CNS", ExtendedCharsets.aliasesFor("x-ISO-2022-CN-CNS")); + } + + public boolean contains(Charset cs) { + // overlapping repertoire of EUC_TW, CNS11643 + return ((cs instanceof EUC_TW) || + (cs.name().equals("US-ASCII")) || + (cs instanceof ISO2022_CN_CNS)); + } + + public String historicalName() { + return "ISO2022CN_CNS"; + } + + public CharsetDecoder newDecoder() { + return new ISO2022_CN.Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Encoder extends ISO2022.Encoder { + + public Encoder(Charset cs) + { + super(cs); + SODesig = "$)G"; + SS2Desig = "$*H"; + SS3Desig = "$+I"; + + try { + Charset cset = Charset.forName("EUC_TW"); // CNS11643 + ISOEncoder = cset.newEncoder(); + } catch (Exception e) { } + } + + private byte[] bb = new byte[4]; + public boolean canEncode(char c) { + int n = 0; + return (c <= '\u007f' || + (n = ((EUC_TW.Encoder)ISOEncoder).toEUC(c, bb)) == 2 || + (n == 4 && bb[0] == SS2 && + (bb[1] == PLANE2 || bb[1] == PLANE3))); + } + + /* + * Since ISO2022-CN-CNS possesses a CharsetEncoder + * without the corresponding CharsetDecoder half the + * default replacement check needs to be overridden + * since the parent class version attempts to + * decode 0x3f (?). + */ + + public boolean isLegalReplacement(byte[] repl) { + // 0x3f is OK as the replacement byte + return (repl.length == 1 && repl[0] == (byte) 0x3f); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_CN_GB.java b/src/sun/nio/cs/ext/ISO2022_CN_GB.java new file mode 100644 index 00000000..ebb90813 --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_CN_GB.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class ISO2022_CN_GB extends ISO2022 implements HistoricallyNamedCharset +{ + public ISO2022_CN_GB() { + super("x-ISO-2022-CN-GB", + ExtendedCharsets.aliasesFor("x-ISO-2022-CN-GB")); + } + + public boolean contains(Charset cs) { + // overlapping repertoire of EUC_CN, GB2312 + return ((cs instanceof EUC_CN) || + (cs.name().equals("US-ASCII")) || + (cs instanceof ISO2022_CN_GB)); + } + + public String historicalName() { + return "ISO2022CN_GB"; + } + + public CharsetDecoder newDecoder() { + return new ISO2022_CN.Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Encoder extends ISO2022.Encoder { + + public Encoder(Charset cs) + { + super(cs); + SODesig = "$)A"; + + try { + Charset cset = Charset.forName("EUC_CN"); // GB2312 + ISOEncoder = cset.newEncoder(); + } catch (Exception e) { } + } + + /* + * Since ISO2022-CN-GB possesses a CharsetEncoder + * without the corresponding CharsetDecoder half the + * default replacement check needs to be overridden + * since the parent class version attempts to + * decode 0x3f (?). + */ + + public boolean isLegalReplacement(byte[] repl) { + // 0x3f is OK as the replacement byte + return (repl.length == 1 && repl[0] == (byte) 0x3f); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_JP.java b/src/sun/nio/cs/ext/ISO2022_JP.java new file mode 100644 index 00000000..eef1b4df --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_JP.java @@ -0,0 +1,765 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.nio.cs.Surrogate; +import sun.nio.cs.US_ASCII; + +/* + * Implementation notes: + * + * (1)"Standard based" (ASCII, JIS_X_0201 and JIS_X_0208) ISO2022-JP charset + * is provided by the base implementation of this class. + * + * Three Microsoft ISO2022-JP variants, MS50220, MS50221 and MSISO2022JP + * are provided via subclasses. + * + * (2)MS50220 and MS50221 are assumed to work the same way as Microsoft + * CP50220 and CP50221's 7-bit implementation works by using CP5022X + * specific JIS0208 and JIS0212 mapping tables (generated via Microsoft's + * MultiByteToWideChar/WideCharToMultiByte APIs). The only difference + * between these 2 classes is that MS50220 does not support singlebyte + * halfwidth kana (Uff61-Uff9f) shiftin mechanism when "encoding", instead + * these halfwidth kana characters are converted to their fullwidth JIS0208 + * counterparts. + * + * The difference between the standard JIS_X_0208 and JIS_X_0212 mappings + * and the CP50220/50221 specific are + * + * 0208 mapping: + * 1)0x213d <-> U2015 (compared to U2014) + * 2)One way mappings for 5 characters below + * u2225 (ms) -> 0x2142 <-> u2016 (jis) + * uff0d (ms) -> 0x215d <-> u2212 (jis) + * uffe0 (ms) -> 0x2171 <-> u00a2 (jis) + * uffe1 (ms) -> 0x2172 <-> u00a3 (jis) + * uffe2 (ms) -> 0x224c <-> u00ac (jis) + * //should consider 0xff5e -> 0x2141 <-> U301c? + * 3)NEC Row13 0x2d21-0x2d79 + * 4)85-94 ku <-> UE000,UE3AB (includes NEC selected + * IBM kanji in 89-92ku) + * 5)UFF61-UFF9f -> Fullwidth 0208 KANA + * + * 0212 mapping: + * 1)0x2237 <-> UFF5E (Fullwidth Tilde) + * 2)0x2271 <-> U2116 (Numero Sign) + * 3)85-94 ku <-> UE3AC - UE757 + * + * (3)MSISO2022JP uses a JIS0208 mapping generated from MS932DB.b2c + * and MS932DB.c2b by converting the SJIS codepoints back to their + * JIS0208 counterparts. With the exception of + * + * (a)Codepoints with a resulting JIS0208 codepoints beyond 0x7e00 are + * dropped (this includs the IBM Extended Kanji/Non-kanji from 0x9321 + * to 0x972c) + * (b)The Unicode codepoints that the IBM Extended Kanji/Non-kanji are + * mapped to (in MS932) are mapped back to NEC selected IBM Kanji/ + * Non-kanji area at 0x7921-0x7c7e. + * + * Compared to JIS_X_0208 mapping, this MS932 based mapping has + + * (a)different mappings for 7 JIS codepoints + * 0x213d <-> U2015 + * 0x2141 <-> UFF5E + * 0x2142 <-> U2225 + * 0x215d <-> Uff0d + * 0x2171 <-> Uffe0 + * 0x2172 <-> Uffe1 + * 0x224c <-> Uffe2 + * (b)added one-way c2b mappings for + * U00b8 -> 0x2124 + * U00b7 -> 0x2126 + * U00af -> 0x2131 + * U00ab -> 0x2263 + * U00bb -> 0x2264 + * U3094 -> 0x2574 + * U00b5 -> 0x264c + * (c)NEC Row 13 + * (d)NEC selected IBM extended Kanji/Non-kanji + * These codepoints are mapped to the same Unicode codepoints as + * the MS932 does, while MS50220/50221 maps them to the Unicode + * private area. + * + * # There is also an interesting difference when compared to MS5022X + * 0208 mapping for JIS codepoint "0x2D60", MS932 maps it to U301d + * but MS5022X maps it to U301e, obvious MS5022X is wrong, but... + */ + +public class ISO2022_JP + extends Charset + implements HistoricallyNamedCharset +{ + private static final int ASCII = 0; // ESC ( B + private static final int JISX0201_1976 = 1; // ESC ( J + private static final int JISX0208_1978 = 2; // ESC $ @ + private static final int JISX0208_1983 = 3; // ESC $ B + private static final int JISX0212_1990 = 4; // ESC $ ( D + private static final int JISX0201_1976_KANA = 5; // ESC ( I + private static final int SHIFTOUT = 6; + + private static final int ESC = 0x1b; + private static final int SO = 0x0e; + private static final int SI = 0x0f; + + public ISO2022_JP() { + super("ISO-2022-JP", + ExtendedCharsets.aliasesFor("ISO-2022-JP")); + } + + protected ISO2022_JP(String canonicalName, + String[] aliases) { + super(canonicalName, aliases); + } + + public String historicalName() { + return "ISO2022JP"; + } + + public boolean contains(Charset cs) { + return ((cs instanceof JIS_X_0201) + || (cs instanceof US_ASCII) + || (cs instanceof JIS_X_0208) + || (cs instanceof ISO2022_JP)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + protected boolean doSBKANA() { + return true; + } + + static class Decoder extends CharsetDecoder + implements DelegatableDecoder { + + final static DoubleByte.Decoder DEC0208 = + (DoubleByte.Decoder)new JIS_X_0208().newDecoder(); + + private int currentState; + private int previousState; + + private DoubleByte.Decoder dec0208; + private DoubleByte.Decoder dec0212; + + private Decoder(Charset cs) { + this(cs, DEC0208, null); + } + + protected Decoder(Charset cs, + DoubleByte.Decoder dec0208, + DoubleByte.Decoder dec0212) { + super(cs, 0.5f, 1.0f); + this.dec0208 = dec0208; + this.dec0212 = dec0212; + currentState = ASCII; + previousState = ASCII; + } + + public void implReset() { + currentState = ASCII; + previousState = ASCII; + } + + private CoderResult decodeArrayLoop(ByteBuffer src, + CharBuffer dst) + { + int inputSize = 0; + int b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char c = UNMAPPABLE_DECODING; + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + b1 = sa[sp] & 0xff; + inputSize = 1; + if ((b1 & 0x80) != 0) { + return CoderResult.malformedForLength(inputSize); + } + if (b1 == ESC || b1 == SO || b1 == SI) { + if (b1 == ESC) { + if (sp + inputSize + 2 > sl) + return CoderResult.UNDERFLOW; + b2 = sa[sp + inputSize++] & 0xff; + if (b2 == '(') { + b3 = sa[sp + inputSize++] & 0xff; + if (b3 == 'B'){ + currentState = ASCII; + } else if (b3 == 'J'){ + currentState = JISX0201_1976; + } else if (b3 == 'I'){ + currentState = JISX0201_1976_KANA; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b2 == '$'){ + b3 = sa[sp + inputSize++] & 0xff; + if (b3 == '@'){ + currentState = JISX0208_1978; + } else if (b3 == 'B'){ + currentState = JISX0208_1983; + } else if (b3 == '(' && dec0212 != null) { + if (sp + inputSize + 1 > sl) + return CoderResult.UNDERFLOW; + b4 = sa[sp + inputSize++] & 0xff; + if (b4 == 'D') { + currentState = JISX0212_1990; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b1 == SO) { + previousState = currentState; + currentState = SHIFTOUT; + } else if (b1 == SI) { + currentState = previousState; + } + sp += inputSize; + continue; + } + if (dp + 1 > dl) + return CoderResult.OVERFLOW; + + switch (currentState){ + case ASCII: + da[dp++] = (char)(b1 & 0xff); + break; + case JISX0201_1976: + switch (b1) { + case 0x5c: // Yen/tilde substitution + da[dp++] = '\u00a5'; + break; + case 0x7e: + da[dp++] = '\u203e'; + break; + default: + da[dp++] = (char)b1; + break; + } + break; + case JISX0208_1978: + case JISX0208_1983: + if (sp + inputSize + 1 > sl) + return CoderResult.UNDERFLOW; + b2 = sa[sp + inputSize++] & 0xff; + c = dec0208.decodeDouble(b1,b2); + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(inputSize); + da[dp++] = c; + break; + case JISX0212_1990: + if (sp + inputSize + 1 > sl) + return CoderResult.UNDERFLOW; + b2 = sa[sp + inputSize++] & 0xff; + c = dec0212.decodeDouble(b1,b2); + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(inputSize); + da[dp++] = c; + break; + case JISX0201_1976_KANA: + case SHIFTOUT: + if (b1 > 0x60) { + return CoderResult.malformedForLength(inputSize); + } + da[dp++] = (char)(b1 + 0xff40); + break; + } + sp += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, + CharBuffer dst) + { + int mark = src.position(); + int b1 = 0, b2 = 0, b3 = 0, b4=0; + char c = UNMAPPABLE_DECODING; + int inputSize = 0; + try { + while (src.hasRemaining()) { + b1 = src.get() & 0xff; + inputSize = 1; + if ((b1 & 0x80) != 0) + return CoderResult.malformedForLength(inputSize); + if (b1 == ESC || b1 == SO || b1 == SI) { + if (b1 == ESC) { // ESC + if (src.remaining() < 2) + return CoderResult.UNDERFLOW; + b2 = src.get() & 0xff; + inputSize++; + if (b2 == '(') { + b3 = src.get() & 0xff; + inputSize++; + if (b3 == 'B'){ + currentState = ASCII; + } else if (b3 == 'J'){ + currentState = JISX0201_1976; + } else if (b3 == 'I'){ + currentState = JISX0201_1976_KANA; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b2 == '$'){ + b3 = src.get() & 0xff; + inputSize++; + if (b3 == '@'){ + currentState = JISX0208_1978; + } else if (b3 == 'B'){ + currentState = JISX0208_1983; + } else if (b3 == '(' && dec0212 != null) { + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + b4 = src.get() & 0xff; + inputSize++; + if (b4 == 'D') { + currentState = JISX0212_1990; + } else { + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else { + return CoderResult.malformedForLength(inputSize); + } + } else if (b1 == SO) { + previousState = currentState; + currentState = SHIFTOUT; + } else if (b1 == SI) { // shift back in + currentState = previousState; + } + mark += inputSize; + continue; + } + if (!dst.hasRemaining()) + return CoderResult.OVERFLOW; + + switch (currentState){ + case ASCII: + dst.put((char)(b1 & 0xff)); + break; + case JISX0201_1976: + switch (b1) { + case 0x5c: // Yen/tilde substitution + dst.put('\u00a5'); + break; + case 0x7e: + dst.put('\u203e'); + break; + default: + dst.put((char)b1); + break; + } + break; + case JISX0208_1978: + case JISX0208_1983: + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + b2 = src.get() & 0xff; + inputSize++; + c = dec0208.decodeDouble(b1,b2); + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(inputSize); + dst.put(c); + break; + case JISX0212_1990: + if (!src.hasRemaining()) + return CoderResult.UNDERFLOW; + b2 = src.get() & 0xff; + inputSize++; + c = dec0212.decodeDouble(b1,b2); + if (c == UNMAPPABLE_DECODING) + return CoderResult.unmappableForLength(inputSize); + dst.put(c); + break; + case JISX0201_1976_KANA: + case SHIFTOUT: + if (b1 > 0x60) { + return CoderResult.malformedForLength(inputSize); + } + dst.put((char)(b1 + 0xff40)); + break; + } + mark += inputSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + // Make some protected methods public for use by JISAutoDetect + public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + public CoderResult implFlush(CharBuffer out) { + return super.implFlush(out); + } + } + + static class Encoder extends CharsetEncoder { + + final static DoubleByte.Encoder ENC0208 = + (DoubleByte.Encoder)new JIS_X_0208().newEncoder(); + + private static byte[] repl = { (byte)0x21, (byte)0x29 }; + private int currentMode = ASCII; + private int replaceMode = JISX0208_1983; + private DoubleByte.Encoder enc0208; + private DoubleByte.Encoder enc0212; + private boolean doSBKANA; + + private Encoder(Charset cs) { + this(cs, ENC0208, null, true); + } + + Encoder(Charset cs, + DoubleByte.Encoder enc0208, + DoubleByte.Encoder enc0212, + boolean doSBKANA) { + super(cs, 4.0f, (enc0212 != null)? 9.0f : 8.0f, repl); + this.enc0208 = enc0208; + this.enc0212 = enc0212; + this.doSBKANA = doSBKANA; + } + + protected int encodeSingle(char inputChar) { + return -1; + } + + protected void implReset() { + currentMode = ASCII; + } + + protected void implReplaceWith(byte[] newReplacement) { + /* It's almost impossible to decide which charset they belong + to. The best thing we can do here is to "guess" based on + the length of newReplacement. + */ + if (newReplacement.length == 1) { + replaceMode = ASCII; + } else if (newReplacement.length == 2) { + replaceMode = JISX0208_1983; + } + } + + protected CoderResult implFlush(ByteBuffer out) { + if (currentMode != ASCII) { + if (out.remaining() < 3) + return CoderResult.OVERFLOW; + out.put((byte)0x1b); + out.put((byte)0x28); + out.put((byte)0x42); + currentMode = ASCII; + } + return CoderResult.UNDERFLOW; + } + + public boolean canEncode(char c) { + return ((c <= '\u007F') || + (c >= 0xFF61 && c <= 0xFF9F) || + (c == '\u00A5') || + (c == '\u203E') || + enc0208.canEncode(c) || + (enc0212!=null && enc0212.canEncode(c))); + } + + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + private CoderResult encodeArrayLoop(CharBuffer src, + ByteBuffer dst) + { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + try { + while (sp < sl) { + char c = sa[sp]; + if (c <= '\u007F') { + if (currentMode != ASCII) { + if (dl - dp < 3) + return CoderResult.OVERFLOW; + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x28; + da[dp++] = (byte)0x42; + currentMode = ASCII; + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)c; + } else if (c >= 0xff61 && c <= 0xff9f && doSBKANA) { + //a single byte kana + if (currentMode != JISX0201_1976_KANA) { + if (dl - dp < 3) + return CoderResult.OVERFLOW; + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x28; + da[dp++] = (byte)0x49; + currentMode = JISX0201_1976_KANA; + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(c - 0xff40); + } else if (c == '\u00A5' || c == '\u203E') { + //backslash or tilde + if (currentMode != JISX0201_1976) { + if (dl - dp < 3) + return CoderResult.OVERFLOW; + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x28; + da[dp++] = (byte)0x4a; + currentMode = JISX0201_1976; + } + if (dl - dp < 1) + return CoderResult.OVERFLOW; + da[dp++] = (c == '\u00A5')?(byte)0x5C:(byte)0x7e; + } else { + int index = enc0208.encodeChar(c); + if (index != UNMAPPABLE_ENCODING) { + if (currentMode != JISX0208_1983) { + if (dl - dp < 3) + return CoderResult.OVERFLOW; + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x24; + da[dp++] = (byte)0x42; + currentMode = JISX0208_1983; + } + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(index >> 8); + da[dp++] = (byte)(index & 0xff); + } else if (enc0212 != null && + (index = enc0212.encodeChar(c)) != UNMAPPABLE_ENCODING) { + if (currentMode != JISX0212_1990) { + if (dl - dp < 4) + return CoderResult.OVERFLOW; + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x24; + da[dp++] = (byte)0x28; + da[dp++] = (byte)0x44; + currentMode = JISX0212_1990; + } + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(index >> 8); + da[dp++] = (byte)(index & 0xff); + } else { + if (Character.isSurrogate(c) && sgp.parse(c, sa, sp, sl) < 0) + return sgp.error(); + if (unmappableCharacterAction() + == CodingErrorAction.REPLACE + && currentMode != replaceMode) { + if (dl - dp < 3) + return CoderResult.OVERFLOW; + if (replaceMode == ASCII) { + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x28; + da[dp++] = (byte)0x42; + } else { + da[dp++] = (byte)0x1b; + da[dp++] = (byte)0x24; + da[dp++] = (byte)0x42; + } + currentMode = replaceMode; + } + if (Character.isSurrogate(c)) + return sgp.unmappableResult(); + return CoderResult.unmappableForLength(1); + } + } + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, + ByteBuffer dst) + { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char c = src.get(); + + if (c <= '\u007F') { + if (currentMode != ASCII) { + if (dst.remaining() < 3) + return CoderResult.OVERFLOW; + dst.put((byte)0x1b); + dst.put((byte)0x28); + dst.put((byte)0x42); + currentMode = ASCII; + } + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)c); + } else if (c >= 0xff61 && c <= 0xff9f && doSBKANA) { + //Is it a single byte kana? + if (currentMode != JISX0201_1976_KANA) { + if (dst.remaining() < 3) + return CoderResult.OVERFLOW; + dst.put((byte)0x1b); + dst.put((byte)0x28); + dst.put((byte)0x49); + currentMode = JISX0201_1976_KANA; + } + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)(c - 0xff40)); + } else if (c == '\u00a5' || c == '\u203E') { + if (currentMode != JISX0201_1976) { + if (dst.remaining() < 3) + return CoderResult.OVERFLOW; + dst.put((byte)0x1b); + dst.put((byte)0x28); + dst.put((byte)0x4a); + currentMode = JISX0201_1976; + } + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((c == '\u00A5')?(byte)0x5C:(byte)0x7e); + } else { + int index = enc0208.encodeChar(c); + if (index != UNMAPPABLE_ENCODING) { + if (currentMode != JISX0208_1983) { + if (dst.remaining() < 3) + return CoderResult.OVERFLOW; + dst.put((byte)0x1b); + dst.put((byte)0x24); + dst.put((byte)0x42); + currentMode = JISX0208_1983; + } + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(index >> 8)); + dst.put((byte)(index & 0xff)); + } else if (enc0212 != null && + (index = enc0212.encodeChar(c)) != UNMAPPABLE_ENCODING) { + if (currentMode != JISX0212_1990) { + if (dst.remaining() < 4) + return CoderResult.OVERFLOW; + dst.put((byte)0x1b); + dst.put((byte)0x24); + dst.put((byte)0x28); + dst.put((byte)0x44); + currentMode = JISX0212_1990; + } + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(index >> 8)); + dst.put((byte)(index & 0xff)); + } else { + if (Character.isSurrogate(c) && sgp.parse(c, src) < 0) + return sgp.error(); + if (unmappableCharacterAction() == CodingErrorAction.REPLACE + && currentMode != replaceMode) { + if (dst.remaining() < 3) + return CoderResult.OVERFLOW; + if (replaceMode == ASCII) { + dst.put((byte)0x1b); + dst.put((byte)0x28); + dst.put((byte)0x42); + } else { + dst.put((byte)0x1b); + dst.put((byte)0x24); + dst.put((byte)0x42); + } + currentMode = replaceMode; + } + if (Character.isSurrogate(c)) + return sgp.unmappableResult(); + return CoderResult.unmappableForLength(1); + } + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, + ByteBuffer dst) + { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_JP_2.java b/src/sun/nio/cs/ext/ISO2022_JP_2.java new file mode 100644 index 00000000..8d0d4e0b --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_JP_2.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class ISO2022_JP_2 extends ISO2022_JP +{ + public ISO2022_JP_2() { + super("ISO-2022-JP-2", + ExtendedCharsets.aliasesFor("ISO-2022-JP-2")); + } + + public String historicalName() { + return "ISO2022JP2"; + } + + public boolean contains(Charset cs) { + return super.contains(cs) || + (cs instanceof JIS_X_0212) || + (cs instanceof ISO2022_JP_2); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this, Decoder.DEC0208, CoderHolder.DEC0212); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this, Encoder.ENC0208, CoderHolder.ENC0212, true); + } + + private static class CoderHolder { + final static DoubleByte.Decoder DEC0212 = + (DoubleByte.Decoder)new JIS_X_0212().newDecoder(); + final static DoubleByte.Encoder ENC0212 = + (DoubleByte.Encoder)new JIS_X_0212().newEncoder(); + } +} diff --git a/src/sun/nio/cs/ext/ISO2022_KR.java b/src/sun/nio/cs/ext/ISO2022_KR.java new file mode 100644 index 00000000..cd387141 --- /dev/null +++ b/src/sun/nio/cs/ext/ISO2022_KR.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.nio.cs.ext.EUC_KR; + +public class ISO2022_KR extends ISO2022 +implements HistoricallyNamedCharset +{ + private static Charset ksc5601_cs; + + public ISO2022_KR() { + super("ISO-2022-KR", ExtendedCharsets.aliasesFor("ISO-2022-KR")); + ksc5601_cs = new EUC_KR(); + } + + public boolean contains(Charset cs) { + // overlapping repertoire of EUC_KR, aka KSC5601 + return ((cs instanceof EUC_KR) || + (cs.name().equals("US-ASCII")) || + (cs instanceof ISO2022_KR)); + } + + public String historicalName() { + return "ISO2022KR"; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + private static class Decoder extends ISO2022.Decoder { + public Decoder(Charset cs) + { + super(cs); + SODesig = new byte[][] {{(byte)'$', (byte)')', (byte)'C'}}; + SODecoder = new CharsetDecoder[1]; + + try { + SODecoder[0] = ksc5601_cs.newDecoder(); + } catch (Exception e) {}; + } + } + + private static class Encoder extends ISO2022.Encoder { + + public Encoder(Charset cs) + { + super(cs); + SODesig = "$)C"; + + try { + ISOEncoder = ksc5601_cs.newEncoder(); + } catch (Exception e) { } + } + + public boolean canEncode(char c) { + return (ISOEncoder.canEncode(c)); + } + } +} diff --git a/src/sun/nio/cs/ext/JISAutoDetect.java b/src/sun/nio/cs/ext/JISAutoDetect.java new file mode 100644 index 00000000..5e43e918 --- /dev/null +++ b/src/sun/nio/cs/ext/JISAutoDetect.java @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.security.AccessController; + +import sun.nio.cs.HistoricallyNamedCharset; +import sun.security.action.GetPropertyAction; + + +public class JISAutoDetect + extends Charset + implements HistoricallyNamedCharset +{ + + private final static int EUCJP_MASK = 0x01; + private final static int SJIS2B_MASK = 0x02; + private final static int SJIS1B_MASK = 0x04; + private final static int EUCJP_KANA1_MASK = 0x08; + private final static int EUCJP_KANA2_MASK = 0x10; + + public JISAutoDetect() { + super("x-JISAutoDetect", ExtendedCharsets.aliasesFor("x-JISAutoDetect")); + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof SJIS) + || (cs instanceof EUC_JP) + || (cs instanceof ISO2022_JP)); + } + + public boolean canEncode() { + return false; + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public String historicalName() { + return "JISAutoDetect"; + } + + public CharsetEncoder newEncoder() { + throw new UnsupportedOperationException(); + } + + /** + * accessor methods used to share byte masking tables + * with the sun.io JISAutoDetect implementation + */ + + public static byte[] getByteMask1() { + return Decoder.maskTable1; + } + + public static byte[] getByteMask2() { + return Decoder.maskTable2; + } + + public final static boolean canBeSJIS1B(int mask) { + return (mask & SJIS1B_MASK) != 0; + } + + public final static boolean canBeEUCJP(int mask) { + return (mask & EUCJP_MASK) != 0; + } + + public final static boolean canBeEUCKana(int mask1, int mask2) { + return ((mask1 & EUCJP_KANA1_MASK) != 0) + && ((mask2 & EUCJP_KANA2_MASK) != 0); + } + + // A heuristic algorithm for guessing if EUC-decoded text really + // might be Japanese text. Better heuristics are possible... + private static boolean looksLikeJapanese(CharBuffer cb) { + int hiragana = 0; // Fullwidth Hiragana + int katakana = 0; // Halfwidth Katakana + while (cb.hasRemaining()) { + char c = cb.get(); + if (0x3040 <= c && c <= 0x309f && ++hiragana > 1) return true; + if (0xff65 <= c && c <= 0xff9f && ++katakana > 1) return true; + } + return false; + } + + private static class Decoder extends CharsetDecoder { + + private final static String SJISName = getSJISName(); + private final static String EUCJPName = getEUCJPName(); + private DelegatableDecoder detectedDecoder = null; + + public Decoder(Charset cs) { + super(cs, 0.5f, 1.0f); + } + + private static boolean isPlainASCII(byte b) { + return b >= 0 && b != 0x1b; + } + + private static void copyLeadingASCII(ByteBuffer src, CharBuffer dst) { + int start = src.position(); + int limit = start + Math.min(src.remaining(), dst.remaining()); + int p; + byte b; + for (p = start; p < limit && isPlainASCII(b = src.get(p)); p++) + dst.put((char)(b & 0xff)); + src.position(p); + } + + private CoderResult decodeLoop(Charset cs, + ByteBuffer src, CharBuffer dst) { + detectedDecoder = (DelegatableDecoder) cs.newDecoder(); + return detectedDecoder.decodeLoop(src, dst); + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (detectedDecoder == null) { + copyLeadingASCII(src, dst); + + // All ASCII? + if (! src.hasRemaining()) + return CoderResult.UNDERFLOW; + if (! dst.hasRemaining()) + return CoderResult.OVERFLOW; + + // We need to perform double, not float, arithmetic; otherwise + // we lose low order bits when src is larger than 2**24. + int cbufsiz = (int)(src.limit() * (double)maxCharsPerByte()); + CharBuffer sandbox = CharBuffer.allocate(cbufsiz); + + // First try ISO-2022-JP, since there is no ambiguity + Charset cs2022 = Charset.forName("ISO-2022-JP"); + DelegatableDecoder dd2022 + = (DelegatableDecoder) cs2022.newDecoder(); + ByteBuffer src2022 = src.asReadOnlyBuffer(); + CoderResult res2022 = dd2022.decodeLoop(src2022, sandbox); + if (! res2022.isError()) + return decodeLoop(cs2022, src, dst); + + // We must choose between EUC and SJIS + Charset csEUCJ = Charset.forName(EUCJPName); + Charset csSJIS = Charset.forName(SJISName); + + DelegatableDecoder ddEUCJ + = (DelegatableDecoder) csEUCJ.newDecoder(); + ByteBuffer srcEUCJ = src.asReadOnlyBuffer(); + sandbox.clear(); + CoderResult resEUCJ = ddEUCJ.decodeLoop(srcEUCJ, sandbox); + // If EUC decoding fails, must be SJIS + if (resEUCJ.isError()) + return decodeLoop(csSJIS, src, dst); + + DelegatableDecoder ddSJIS + = (DelegatableDecoder) csSJIS.newDecoder(); + ByteBuffer srcSJIS = src.asReadOnlyBuffer(); + CharBuffer sandboxSJIS = CharBuffer.allocate(cbufsiz); + CoderResult resSJIS = ddSJIS.decodeLoop(srcSJIS, sandboxSJIS); + // If SJIS decoding fails, must be EUC + if (resSJIS.isError()) + return decodeLoop(csEUCJ, src, dst); + + // From here on, we have some ambiguity, and must guess. + + // We prefer input that does not appear to end mid-character. + if (srcEUCJ.position() > srcSJIS.position()) + return decodeLoop(csEUCJ, src, dst); + + if (srcEUCJ.position() < srcSJIS.position()) + return decodeLoop(csSJIS, src, dst); + + // end-of-input is after the first byte of the first char? + if (src.position() == srcEUCJ.position()) + return CoderResult.UNDERFLOW; + + // Use heuristic knowledge of typical Japanese text + sandbox.flip(); + Charset guess = looksLikeJapanese(sandbox) ? csEUCJ : csSJIS; + return decodeLoop(guess, src, dst); + } + + return detectedDecoder.decodeLoop(src, dst); + } + + protected void implReset() { + detectedDecoder = null; + } + + protected CoderResult implFlush(CharBuffer out) { + if (detectedDecoder != null) + return detectedDecoder.implFlush(out); + else + return super.implFlush(out); + } + + public boolean isAutoDetecting() { + return true; + } + + public boolean isCharsetDetected() { + return detectedDecoder != null; + } + + public Charset detectedCharset() { + if (detectedDecoder == null) + throw new IllegalStateException("charset not yet detected"); + return ((CharsetDecoder) detectedDecoder).charset(); + } + + /** + * Returned Shift_JIS Charset name is OS dependent + */ + private static String getSJISName() { + String osName = AccessController.doPrivileged( + new GetPropertyAction("os.name")); + if (osName.equals("Solaris") || osName.equals("SunOS")) + return("PCK"); + else if (osName.startsWith("Windows")) + return("windows-31J"); + else + return("Shift_JIS"); + } + + /** + * Returned EUC-JP Charset name is OS dependent + */ + + private static String getEUCJPName() { + String osName = AccessController.doPrivileged( + new GetPropertyAction("os.name")); + if (osName.equals("Solaris") || osName.equals("SunOS")) + return("x-eucjp-open"); + else + return("EUC_JP"); + } + + // Mask tables - each entry indicates possibility of first or + // second byte being SJIS or EUC_JP + private static final byte maskTable1[] = { + 0, 0, 0, 0, // 0x00 - 0x03 + 0, 0, 0, 0, // 0x04 - 0x07 + 0, 0, 0, 0, // 0x08 - 0x0b + 0, 0, 0, 0, // 0x0c - 0x0f + 0, 0, 0, 0, // 0x10 - 0x13 + 0, 0, 0, 0, // 0x14 - 0x17 + 0, 0, 0, 0, // 0x18 - 0x1b + 0, 0, 0, 0, // 0x1c - 0x1f + 0, 0, 0, 0, // 0x20 - 0x23 + 0, 0, 0, 0, // 0x24 - 0x27 + 0, 0, 0, 0, // 0x28 - 0x2b + 0, 0, 0, 0, // 0x2c - 0x2f + 0, 0, 0, 0, // 0x30 - 0x33 + 0, 0, 0, 0, // 0x34 - 0x37 + 0, 0, 0, 0, // 0x38 - 0x3b + 0, 0, 0, 0, // 0x3c - 0x3f + 0, 0, 0, 0, // 0x40 - 0x43 + 0, 0, 0, 0, // 0x44 - 0x47 + 0, 0, 0, 0, // 0x48 - 0x4b + 0, 0, 0, 0, // 0x4c - 0x4f + 0, 0, 0, 0, // 0x50 - 0x53 + 0, 0, 0, 0, // 0x54 - 0x57 + 0, 0, 0, 0, // 0x58 - 0x5b + 0, 0, 0, 0, // 0x5c - 0x5f + 0, 0, 0, 0, // 0x60 - 0x63 + 0, 0, 0, 0, // 0x64 - 0x67 + 0, 0, 0, 0, // 0x68 - 0x6b + 0, 0, 0, 0, // 0x6c - 0x6f + 0, 0, 0, 0, // 0x70 - 0x73 + 0, 0, 0, 0, // 0x74 - 0x77 + 0, 0, 0, 0, // 0x78 - 0x7b + 0, 0, 0, 0, // 0x7c - 0x7f + 0, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x80 - 0x83 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x84 - 0x87 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x88 - 0x8b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0x8c - 0x8f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x90 - 0x93 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x94 - 0x97 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x98 - 0x9b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x9c - 0x9f + 0, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xa0 - 0xa3 + SJIS1B_MASK|EUCJP_MASK|EUCJP_KANA1_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xa4 - 0xa7 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xa8 - 0xab + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xac - 0xaf + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xb0 - 0xb3 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xb4 - 0xb7 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xb8 - 0xbb + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xbc - 0xbf + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xc0 - 0xc3 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xc4 - 0xc7 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xc8 - 0xcb + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xcc - 0xcf + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xd0 - 0xd3 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xd4 - 0xd7 + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xd8 - 0xdb + SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, SJIS1B_MASK|EUCJP_MASK, // 0xdc - 0xdf + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xe0 - 0xe3 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xe4 - 0xe7 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xe8 - 0xeb + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xec - 0xef + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xf0 - 0xf3 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xf4 - 0xf7 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xf8 - 0xfb + SJIS2B_MASK|EUCJP_MASK, EUCJP_MASK, EUCJP_MASK, 0 // 0xfc - 0xff + }; + + private static final byte maskTable2[] = { + 0, 0, 0, 0, // 0x00 - 0x03 + 0, 0, 0, 0, // 0x04 - 0x07 + 0, 0, 0, 0, // 0x08 - 0x0b + 0, 0, 0, 0, // 0x0c - 0x0f + 0, 0, 0, 0, // 0x10 - 0x13 + 0, 0, 0, 0, // 0x14 - 0x17 + 0, 0, 0, 0, // 0x18 - 0x1b + 0, 0, 0, 0, // 0x1c - 0x1f + 0, 0, 0, 0, // 0x20 - 0x23 + 0, 0, 0, 0, // 0x24 - 0x27 + 0, 0, 0, 0, // 0x28 - 0x2b + 0, 0, 0, 0, // 0x2c - 0x2f + 0, 0, 0, 0, // 0x30 - 0x33 + 0, 0, 0, 0, // 0x34 - 0x37 + 0, 0, 0, 0, // 0x38 - 0x3b + 0, 0, 0, 0, // 0x3c - 0x3f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x40 - 0x43 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x44 - 0x47 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x48 - 0x4b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x4c - 0x4f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x50 - 0x53 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x54 - 0x57 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x58 - 0x5b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x5c - 0x5f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x60 - 0x63 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x64 - 0x67 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x68 - 0x6b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x6c - 0x6f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x70 - 0x73 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x74 - 0x77 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x78 - 0x7b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, 0, // 0x7c - 0x7f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x80 - 0x83 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x84 - 0x87 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x88 - 0x8b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x8c - 0x8f + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x90 - 0x93 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x94 - 0x97 + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x98 - 0x9b + SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, SJIS2B_MASK, // 0x9c - 0x9f + SJIS2B_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xa0 - 0xa3 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xa4 - 0xa7 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xa8 - 0xab + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xac - 0xaf + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xb0 - 0xb3 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xb4 - 0xb7 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xb8 - 0xbb + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xbc - 0xbf + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xc0 - 0xc3 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xc4 - 0xc7 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xc8 - 0xcb + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xcc - 0xcf + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xd0 - 0xd3 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xd4 - 0xd7 + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xd8 - 0xdb + SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS1B_MASK|SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xdc - 0xdf + SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xe0 - 0xe3 + SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xe4 - 0xe7 + SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xe8 - 0xeb + SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xec - 0xef + SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, SJIS2B_MASK|EUCJP_MASK|EUCJP_KANA2_MASK, // 0xf0 - 0xf3 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xf4 - 0xf7 + SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, SJIS2B_MASK|EUCJP_MASK, // 0xf8 - 0xfb + SJIS2B_MASK|EUCJP_MASK, EUCJP_MASK, EUCJP_MASK, 0 // 0xfc - 0xff + }; + } +} diff --git a/src/sun/nio/cs/ext/MS50220.java b/src/sun/nio/cs/ext/MS50220.java new file mode 100644 index 00000000..9cb36f3a --- /dev/null +++ b/src/sun/nio/cs/ext/MS50220.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class MS50220 extends ISO2022_JP +{ + public MS50220() { + super("x-windows-50220", + ExtendedCharsets.aliasesFor("x-windows-50220")); + } + + protected MS50220(String canonicalName, String[] aliases) { + super(canonicalName, aliases); + } + + public String historicalName() { + return "MS50220"; + } + + public boolean contains(Charset cs) { + return super.contains(cs) || + (cs instanceof JIS_X_0212) || + (cs instanceof MS50220); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this, DEC0208, DEC0212); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this, ENC0208, ENC0212, doSBKANA()); + } + + private final static DoubleByte.Decoder DEC0208 = + (DoubleByte.Decoder)new JIS_X_0208_MS5022X().newDecoder(); + + private final static DoubleByte.Decoder DEC0212 = + (DoubleByte.Decoder)new JIS_X_0212_MS5022X().newDecoder(); + + private final static DoubleByte.Encoder ENC0208 = + (DoubleByte.Encoder)new JIS_X_0208_MS5022X().newEncoder(); + + private final static DoubleByte.Encoder ENC0212 = + (DoubleByte.Encoder)new JIS_X_0212_MS5022X().newEncoder(); + + protected boolean doSBKANA() { + return false; + } +} diff --git a/src/sun/nio/cs/ext/MS50221.java b/src/sun/nio/cs/ext/MS50221.java new file mode 100644 index 00000000..cdc18944 --- /dev/null +++ b/src/sun/nio/cs/ext/MS50221.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; + +public class MS50221 extends MS50220 +{ + public MS50221() { + super("x-windows-50221", + ExtendedCharsets.aliasesFor("x-windows-50221")); + } + + public String historicalName() { + return "MS50221"; + } + + public boolean contains(Charset cs) { + return super.contains(cs) || + (cs instanceof JIS_X_0212) || + (cs instanceof MS50221); + } + + protected boolean doSBKANA() { + return true; + } +} diff --git a/src/sun/nio/cs/ext/MS932_0213.java b/src/sun/nio/cs/ext/MS932_0213.java new file mode 100644 index 00000000..e04fffc7 --- /dev/null +++ b/src/sun/nio/cs/ext/MS932_0213.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class MS932_0213 extends Charset { + public MS932_0213() { + super("x-MS932_0213", ExtendedCharsets.aliasesFor("MS932_0213")); + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof MS932) + || (cs instanceof MS932_0213)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + protected static class Decoder extends SJIS_0213.Decoder { + static DoubleByte.Decoder decMS932 = + (DoubleByte.Decoder)new MS932().newDecoder(); + protected Decoder(Charset cs) { + super(cs); + } + + protected char decodeDouble(int b1, int b2) { + char c = decMS932.decodeDouble(b1, b2); + if (c == UNMAPPABLE_DECODING) + return super.decodeDouble(b1, b2); + return c; + } + } + + protected static class Encoder extends SJIS_0213.Encoder { + // we only use its encodeChar() method + static DoubleByte.Encoder encMS932 = + (DoubleByte.Encoder)new MS932().newEncoder(); + protected Encoder(Charset cs) { + super(cs); + } + + protected int encodeChar(char ch) { + int db = encMS932.encodeChar(ch); + if (db == UNMAPPABLE_ENCODING) + return super.encodeChar(ch); + return db; + } + } +} diff --git a/src/sun/nio/cs/ext/MS950_HKSCS.java b/src/sun/nio/cs/ext/MS950_HKSCS.java new file mode 100644 index 00000000..8ea4ea75 --- /dev/null +++ b/src/sun/nio/cs/ext/MS950_HKSCS.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +import sun.nio.cs.HistoricallyNamedCharset; + +public class MS950_HKSCS extends Charset implements HistoricallyNamedCharset +{ + public MS950_HKSCS() { + super("x-MS950-HKSCS", ExtendedCharsets.aliasesFor("x-MS950-HKSCS")); + } + + public String historicalName() { + return "MS950_HKSCS"; + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof MS950) + || (cs instanceof MS950_HKSCS)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + static class Decoder extends HKSCS.Decoder { + private static DoubleByte.Decoder ms950 = + (DoubleByte.Decoder)new MS950().newDecoder(); + + private static char[][] b2cBmp = new char[0x100][]; + private static char[][] b2cSupp = new char[0x100][]; + static { + initb2c(b2cBmp, HKSCSMapping.b2cBmpStr); + initb2c(b2cSupp, HKSCSMapping.b2cSuppStr); + } + + private Decoder(Charset cs) { + super(cs, ms950, b2cBmp, b2cSupp); + } + } + + private static class Encoder extends HKSCS.Encoder { + private static DoubleByte.Encoder ms950 = + (DoubleByte.Encoder)new MS950().newEncoder(); + + static char[][] c2bBmp = new char[0x100][]; + static char[][] c2bSupp = new char[0x100][]; + static { + initc2b(c2bBmp, HKSCSMapping.b2cBmpStr, HKSCSMapping.pua); + initc2b(c2bSupp, HKSCSMapping.b2cSuppStr, null); + } + + private Encoder(Charset cs) { + super(cs, ms950, c2bBmp, c2bSupp); + } + } +} diff --git a/src/sun/nio/cs/ext/MS950_HKSCS_XP.java b/src/sun/nio/cs/ext/MS950_HKSCS_XP.java new file mode 100644 index 00000000..a9ad4c7d --- /dev/null +++ b/src/sun/nio/cs/ext/MS950_HKSCS_XP.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_DECODING; +import static sun.nio.cs.CharsetMapping.UNMAPPABLE_ENCODING; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class MS950_HKSCS_XP extends Charset +{ + public MS950_HKSCS_XP() { + super("x-MS950-HKSCS-XP", ExtendedCharsets.aliasesFor("x-MS950-HKSCS-XP")); + } + + @Override + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof MS950) + || (cs instanceof MS950_HKSCS_XP)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + static class Decoder extends HKSCS.Decoder { + private static DoubleByte.Decoder ms950 = + (DoubleByte.Decoder)new MS950().newDecoder(); + + /* + * Note current decoder decodes 0x8BC2 --> U+F53A + * ie. maps to Unicode PUA. + * Unaccounted discrepancy between this mapping + * inferred from MS950/windows-950 and the published + * MS HKSCS mappings which maps 0x8BC2 --> U+5C22 + * a character defined with the Unified CJK block + */ + private static char[][] b2cBmp = new char[0x100][]; + static { + initb2c(b2cBmp, HKSCS_XPMapping.b2cBmpStr); + } + + public char decodeDoubleEx(int b1, int b2) { + return UNMAPPABLE_DECODING; + } + + private Decoder(Charset cs) { + super(cs, ms950, b2cBmp, null); + } + } + + private static class Encoder extends HKSCS.Encoder { + private static DoubleByte.Encoder ms950 = + (DoubleByte.Encoder)new MS950().newEncoder(); + + /* + * Note current encoder encodes U+F53A --> 0x8BC2 + * Published MS HKSCS mappings show + * U+5C22 <--> 0x8BC2 + */ + static char[][] c2bBmp = new char[0x100][]; + static { + initc2b(c2bBmp, HKSCS_XPMapping.b2cBmpStr, null); + } + + public int encodeSupp(int cp) { + return UNMAPPABLE_ENCODING; + } + + private Encoder(Charset cs) { + super(cs, ms950, c2bBmp, null); + } + } +} diff --git a/src/sun/nio/cs/ext/MSISO2022JP.java b/src/sun/nio/cs/ext/MSISO2022JP.java new file mode 100644 index 00000000..188116ed --- /dev/null +++ b/src/sun/nio/cs/ext/MSISO2022JP.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +public class MSISO2022JP extends ISO2022_JP +{ + public MSISO2022JP() { + super("x-windows-iso2022jp", + ExtendedCharsets.aliasesFor("x-windows-iso2022jp")); + } + + public String historicalName() { + return "windows-iso2022jp"; + } + + public boolean contains(Charset cs) { + return super.contains(cs) || + (cs instanceof MSISO2022JP); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this, CoderHolder.DEC0208, null); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this, CoderHolder.ENC0208, null, true); + } + + private static class CoderHolder { + final static DoubleByte.Decoder DEC0208 = + (DoubleByte.Decoder)new JIS_X_0208_MS932().newDecoder(); + final static DoubleByte.Encoder ENC0208 = + (DoubleByte.Encoder)new JIS_X_0208_MS932().newEncoder(); + } +} diff --git a/src/sun/nio/cs/ext/SJIS_0213.java b/src/sun/nio/cs/ext/SJIS_0213.java new file mode 100644 index 00000000..2adc8aa5 --- /dev/null +++ b/src/sun/nio/cs/ext/SJIS_0213.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import sun.nio.cs.CharsetMapping; + +/* + * 5 types of entry in SJIS_X_0213/Unicode mapping table + * + * (1)Single-Byte + * JIS_X_0213 does not define single-byte character itself, the + * JIS_X_0201 entries are added in for sjis implementation. + * + * (2)Double-Byte SJIS <-> BMP Unicode + * ex: 0x8140 U+3000 # IDEOGRAPHIC SPACE + * + * (3)Double-Byte SJIS <-> Supplementary + * ex: 0xFCF0 U+2A61A # [2000] [Unicode3.1] + * + * (4)Double-Byte SJIS <-> Composite + * ex: 0x83F6 U+31F7+309A # [2000] + * + * (5)"Windows-only" special mapping entries + * are handled by MS932_0213. + */ + +public class SJIS_0213 extends Charset { + public SJIS_0213() { + super("x-SJIS_0213", ExtendedCharsets.aliasesFor("SJIS_0213")); + } + + public boolean contains(Charset cs) { + return ((cs.name().equals("US-ASCII")) + || (cs instanceof SJIS) + || (cs instanceof SJIS_0213)); + } + + public CharsetDecoder newDecoder() { + return new Decoder(this); + } + + public CharsetEncoder newEncoder() { + return new Encoder(this); + } + + static CharsetMapping mapping = AccessController.doPrivileged( + new PrivilegedAction() { + public CharsetMapping run() { + return CharsetMapping.get(SJIS_0213.class.getResourceAsStream("sjis0213.dat")); + } + }); + + protected static class Decoder extends CharsetDecoder { + protected static final char UNMAPPABLE = CharsetMapping.UNMAPPABLE_DECODING; + + protected Decoder(Charset cs) { + super(cs, 0.5f, 1.0f); + } + + private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { + byte[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + + char[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + int b1 = sa[sp] & 0xff; + char c = decodeSingle(b1); + int inSize = 1, outSize = 1; + char[] cc = null; + if (c == UNMAPPABLE) { + if (sl - sp < 2) + return CoderResult.UNDERFLOW; + int b2 = sa[sp + 1] & 0xff; + c = decodeDouble(b1, b2); + inSize++; + if (c == UNMAPPABLE) { + cc = decodeDoubleEx(b1, b2); + if (cc == null) { + if (decodeSingle(b2) == UNMAPPABLE) + return CoderResult.unmappableForLength(2); + else + return CoderResult.unmappableForLength(1); + } + outSize++; + } + } + if (dl - dp < outSize) + return CoderResult.OVERFLOW; + if (outSize == 2) { + da[dp++] = cc[0]; + da[dp++] = cc[1]; + } else { + da[dp++] = c; + } + sp += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + char[] cc = null; + int b1 = src.get() & 0xff; + char c = decodeSingle(b1); + int inSize = 1, outSize = 1; + if (c == UNMAPPABLE) { + if (src.remaining() < 1) + return CoderResult.UNDERFLOW; + int b2 = src.get() & 0xff; + inSize++; + c = decodeDouble(b1, b2); + if (c == UNMAPPABLE) { + cc = decodeDoubleEx(b1, b2); + if (cc == null) { + if (decodeSingle(b2) == UNMAPPABLE) + return CoderResult.unmappableForLength(2); + else + return CoderResult.unmappableForLength(1); + } + outSize++; + } + } + if (dst.remaining() < outSize) + return CoderResult.OVERFLOW; + if (outSize == 2) { + dst.put(cc[0]); + dst.put(cc[1]); + } else { + dst.put(c); + } + mark += inSize; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return decodeArrayLoop(src, dst); + else + return decodeBufferLoop(src, dst); + } + + protected char decodeSingle(int b) { + return mapping.decodeSingle(b); + } + + protected char decodeDouble(int b1, int b2) { + return mapping.decodeDouble(b1, b2); + } + + private char[] cc = new char[2]; + private CharsetMapping.Entry comp = new CharsetMapping.Entry(); + protected char[] decodeDoubleEx(int b1, int b2) { + int db = (b1 << 8) | b2; + if (mapping.decodeSurrogate(db, cc) != null) + return cc; + comp.bs = db; + if (mapping.decodeComposite(comp, cc) != null) + return cc; + return null; + } + } + + protected static class Encoder extends CharsetEncoder { + protected static final int UNMAPPABLE = CharsetMapping.UNMAPPABLE_ENCODING; + protected static final int MAX_SINGLEBYTE = 0xff; + + protected Encoder(Charset cs) { + super(cs, 2.0f, 2.0f); + } + + public boolean canEncode(char c) { + return (encodeChar(c) != UNMAPPABLE); + } + + protected int encodeChar(char ch) { + return mapping.encodeChar(ch); + } + + protected int encodeSurrogate(char hi, char lo) { + return mapping.encodeSurrogate(hi, lo); + } + + private CharsetMapping.Entry comp = new CharsetMapping.Entry(); + protected int encodeComposite(char base, char cc) { + comp.cp = base; + comp.cp2 = cc; + return mapping.encodeComposite(comp); + } + + protected boolean isCompositeBase(char ch) { + comp.cp = ch; + return mapping.isCompositeBase(comp); + } + + // Unlike surrogate pair, the base character of a base+cc composite + // itself is a legal codepoint in 0213, if we simply return UNDERFLOW + // when a base candidate is the last input char in the CharBuffer, like + // what we do for the surrogte pair, encoding will fail if this base + // character is indeed the last character of the input char sequence. + // Keep this base candidate in "leftoverBase" so we can flush it out + // at the end of the encoding circle. + char leftoverBase = 0; + protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + + try { + while (sp < sl) { + int db; + char c = sa[sp]; + if (leftoverBase != 0) { + boolean isComp = false; + db = encodeComposite(leftoverBase, c); + if (db == UNMAPPABLE) + db = encodeChar(leftoverBase); + else + isComp = true; + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(db >> 8); + da[dp++] = (byte)db; + leftoverBase = 0; + if (isComp) { + sp++; + continue; + } + } + if (isCompositeBase(c)) { + leftoverBase = c; + } else { + db = encodeChar(c); + if (db <= MAX_SINGLEBYTE) { // SingleByte + if (dl <= dp) + return CoderResult.OVERFLOW; + da[dp++] = (byte)db; + } else if (db != UNMAPPABLE) { // DoubleByte + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(db >> 8); + da[dp++] = (byte)db; + } else if (Character.isHighSurrogate(c)) { + if ((sp + 1) == sl) + return CoderResult.UNDERFLOW; + char c2 = sa[sp + 1]; + if (!Character.isLowSurrogate(c2)) + return CoderResult.malformedForLength(1); + db = encodeSurrogate(c, c2); + if (db == UNMAPPABLE) + return CoderResult.unmappableForLength(2); + if (dl - dp < 2) + return CoderResult.OVERFLOW; + da[dp++] = (byte)(db >> 8); + da[dp++] = (byte)db; + sp++; + } else if (Character.isLowSurrogate(c)) { + return CoderResult.malformedForLength(1); + } else { + return CoderResult.unmappableForLength(1); + } + } + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int mark = src.position(); + try { + while (src.hasRemaining()) { + int db; + char c = src.get(); + if (leftoverBase != 0) { + boolean isComp = false; + db = encodeComposite(leftoverBase, c); + if (db == UNMAPPABLE) + db = encodeChar(leftoverBase); + else + isComp = true; + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(db >> 8)); + dst.put((byte)(db)); + leftoverBase = 0; + if (isComp) { + mark++; + continue; + } + } + if (isCompositeBase(c)) { + leftoverBase = c; + } else { + db = encodeChar(c); + if (db <= MAX_SINGLEBYTE) { // Single-byte + if (dst.remaining() < 1) + return CoderResult.OVERFLOW; + dst.put((byte)db); + } else if (db != UNMAPPABLE) { // DoubleByte + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(db >> 8)); + dst.put((byte)(db)); + } else if (Character.isHighSurrogate(c)) { + if (!src.hasRemaining()) // Surrogates + return CoderResult.UNDERFLOW; + char c2 = src.get(); + if (!Character.isLowSurrogate(c2)) + return CoderResult.malformedForLength(1); + db = encodeSurrogate(c, c2); + if (db == UNMAPPABLE) + return CoderResult.unmappableForLength(2); + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + dst.put((byte)(db >> 8)); + dst.put((byte)(db)); + mark++; + } else if (Character.isLowSurrogate(c)) { + return CoderResult.malformedForLength(1); + } else { + return CoderResult.unmappableForLength(1); + } + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + protected CoderResult implFlush(ByteBuffer dst) { + if (leftoverBase > 0) { + if (dst.remaining() < 2) + return CoderResult.OVERFLOW; + int db = encodeChar(leftoverBase); + dst.put((byte)(db >> 8)); + dst.put((byte)(db)); + leftoverBase = 0; + } + return CoderResult.UNDERFLOW; + } + + protected void implReset() { + leftoverBase = 0; + } + } +} diff --git a/src/sun/nio/cs/ext/SimpleEUCEncoder.java b/src/sun/nio/cs/ext/SimpleEUCEncoder.java new file mode 100644 index 00000000..3cea0d4e --- /dev/null +++ b/src/sun/nio/cs/ext/SimpleEUCEncoder.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.nio.cs.ext; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; + +import sun.nio.cs.Surrogate; + + +public abstract class SimpleEUCEncoder + extends CharsetEncoder +{ + + protected short index1[]; + protected String index2; + protected String index2a; + protected String index2b; + protected String index2c; + protected int mask1; + protected int mask2; + protected int shift; + + private byte[] outputByte = new byte[4]; + private final Surrogate.Parser sgp = new Surrogate.Parser(); + + protected SimpleEUCEncoder(Charset cs) + { + super(cs, 3.0f, 4.0f); + } + + /** + * Returns true if the given character can be converted to the + * target character encoding. + */ + + public boolean canEncode(char ch) { + int index; + String theChars; + + index = index1[((ch & mask1) >> shift)] + (ch & mask2); + + if (index < 7500) + theChars = index2; + else + if (index < 15000) { + index = index - 7500; + theChars = index2a; + } + else + if (index < 22500){ + index = index - 15000; + theChars = index2b; + } + else { + index = index - 22500; + theChars = index2c; + } + + if (theChars.charAt(2*index) != '\u0000' || + theChars.charAt(2*index + 1) != '\u0000') + return (true); + + // only return true if input char was unicode null - all others are + // undefined + return( ch == '\u0000'); + + } + private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { + char[] sa = src.array(); + int sp = src.arrayOffset() + src.position(); + int sl = src.arrayOffset() + src.limit(); + assert (sp <= sl); + sp = (sp <= sl ? sp : sl); + byte[] da = dst.array(); + int dp = dst.arrayOffset() + dst.position(); + int dl = dst.arrayOffset() + dst.limit(); + assert (dp <= dl); + dp = (dp <= dl ? dp : dl); + + int index; + int spaceNeeded; + int i; + + try { + while (sp < sl) { + boolean allZeroes = true; + char inputChar = sa[sp]; + if (Character.isSurrogate(inputChar)) { + if (sgp.parse(inputChar, sa, sp, sl) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + + if (inputChar >= '\uFFFE') + return CoderResult.unmappableForLength(1); + + String theChars; + char aChar; + + // We have a valid character, get the bytes for it + index = index1[((inputChar & mask1) >> shift)] + (inputChar & mask2); + + if (index < 7500) + theChars = index2; + else if (index < 15000) { + index = index - 7500; + theChars = index2a; + } else if (index < 22500){ + index = index - 15000; + theChars = index2b; + } + else { + index = index - 22500; + theChars = index2c; + } + + aChar = theChars.charAt(2*index); + outputByte[0] = (byte)((aChar & 0xff00)>>8); + outputByte[1] = (byte)(aChar & 0x00ff); + aChar = theChars.charAt(2*index + 1); + outputByte[2] = (byte)((aChar & 0xff00)>>8); + outputByte[3] = (byte)(aChar & 0x00ff); + + for (i = 0; i < outputByte.length; i++) { + if (outputByte[i] != 0x00) { + allZeroes = false; + break; + } + } + + if (allZeroes && inputChar != '\u0000') { + return CoderResult.unmappableForLength(1); + } + + int oindex = 0; + + for (spaceNeeded = outputByte.length; + spaceNeeded > 1; spaceNeeded--){ + if (outputByte[oindex++] != 0x00 ) + break; + } + + if (dp + spaceNeeded > dl) + return CoderResult.OVERFLOW; + + for (i = outputByte.length - spaceNeeded; + i < outputByte.length; i++) { + da[dp++] = outputByte[i]; + } + sp++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(sp - src.arrayOffset()); + dst.position(dp - dst.arrayOffset()); + } + } + + private CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) { + int index; + int spaceNeeded; + int i; + int mark = src.position(); + try { + while (src.hasRemaining()) { + char inputChar = src.get(); + boolean allZeroes = true; + if (Character.isSurrogate(inputChar)) { + if (sgp.parse(inputChar, src) < 0) + return sgp.error(); + return sgp.unmappableResult(); + } + + if (inputChar >= '\uFFFE') + return CoderResult.unmappableForLength(1); + + String theChars; + char aChar; + + // We have a valid character, get the bytes for it + index = index1[((inputChar & mask1) >> shift)] + (inputChar & mask2); + + if (index < 7500) + theChars = index2; + else if (index < 15000) { + index = index - 7500; + theChars = index2a; + } else if (index < 22500){ + index = index - 15000; + theChars = index2b; + } + else { + index = index - 22500; + theChars = index2c; + } + + aChar = theChars.charAt(2*index); + outputByte[0] = (byte)((aChar & 0xff00)>>8); + outputByte[1] = (byte)(aChar & 0x00ff); + aChar = theChars.charAt(2*index + 1); + outputByte[2] = (byte)((aChar & 0xff00)>>8); + outputByte[3] = (byte)(aChar & 0x00ff); + + for (i = 0; i < outputByte.length; i++) { + if (outputByte[i] != 0x00) { + allZeroes = false; + break; + } + } + if (allZeroes && inputChar != '\u0000') { + return CoderResult.unmappableForLength(1); + } + + int oindex = 0; + + for (spaceNeeded = outputByte.length; + spaceNeeded > 1; spaceNeeded--){ + if (outputByte[oindex++] != 0x00 ) + break; + } + if (dst.remaining() < spaceNeeded) + return CoderResult.OVERFLOW; + + for (i = outputByte.length - spaceNeeded; + i < outputByte.length; i++) { + dst.put(outputByte[i]); + } + mark++; + } + return CoderResult.UNDERFLOW; + } finally { + src.position(mark); + } + } + + protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { + if (true && src.hasArray() && dst.hasArray()) + return encodeArrayLoop(src, dst); + else + return encodeBufferLoop(src, dst); + } + + public byte encode(char inputChar) { + return (byte)index2.charAt(index1[(inputChar & mask1) >> shift] + + (inputChar & mask2)); + } +} diff --git a/src/sun/nio/cs/standard-charsets b/src/sun/nio/cs/standard-charsets new file mode 100644 index 00000000..0785a4ed --- /dev/null +++ b/src/sun/nio/cs/standard-charsets @@ -0,0 +1,342 @@ +# +# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code 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 +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# Standard charsets provided by StandardCharsets provider. +# +# Note that these "standard" charsets listed here are not +# necessary to be the "Standard charsets" defined in the +# specification of java.nio.charset.Charset. Instead these +# are the charsets that this implementation believes should +# be packaged into the charsets provider class "StandardCharsets" +# which is initialized at startup time by java.nio.charset.Charset, +# compared to the charsets packaged in "ExtendedCharsets" provider, +# which is lazy initialized. + +# This year should only change if the generated source is modified. +copyright 2000, 2007, +package sun.nio.cs +class StandardCharsets + +charset US-ASCII US_ASCII + + # IANA aliases + alias iso-ir-6 + alias ANSI_X3.4-1986 + alias ISO_646.irv:1991 + alias ASCII + alias ISO646-US + alias us + alias IBM367 + alias cp367 + alias csASCII + alias default + + # Other aliases + alias 646 # Solaris POSIX locale + alias iso_646.irv:1983 + alias ANSI_X3.4-1968 # Linux POSIX locale (RedHat) + alias ascii7 + +charset UTF-8 UTF_8 + alias UTF8 # JDK historical + alias unicode-1-1-utf-8 + +charset CESU-8 CESU_8 + alias CESU8 + alias csCESU-8 + +charset UTF-16 UTF_16 + alias UTF_16 # JDK historical + alias utf16 + alias unicode + alias UnicodeBig + +charset UTF-16BE UTF_16BE + alias UTF_16BE + alias ISO-10646-UCS-2 + alias X-UTF-16BE + alias UnicodeBigUnmarked + +charset UTF-16LE UTF_16LE + alias UTF_16LE + alias X-UTF-16LE + alias UnicodeLittleUnmarked + +charset x-UTF-16LE-BOM UTF_16LE_BOM + alias UnicodeLittle + +charset UTF-32 UTF_32 + alias UTF_32 + alias UTF32 + +charset UTF-32LE UTF_32LE + alias UTF_32LE + alias X-UTF-32LE + +charset UTF-32BE UTF_32BE + alias UTF_32BE + alias X-UTF-32BE + +charset X-UTF-32LE-BOM UTF_32LE_BOM + alias UTF_32LE_BOM + alias UTF-32LE-BOM + +charset X-UTF-32BE-BOM UTF_32BE_BOM + alias UTF_32BE_BOM + alias UTF-32BE-BOM + +charset ISO-8859-1 ISO_8859_1 + + # IANA aliases + alias iso-ir-100 + alias ISO_8859-1 + alias latin1 + alias l1 + alias IBM819 + alias cp819 + alias csISOLatin1 + + # Other aliases + alias 819 + alias IBM-819 + alias ISO8859_1 + alias ISO_8859-1:1987 + alias ISO_8859_1 + alias 8859_1 + alias ISO8859-1 + +charset ISO-8859-2 ISO_8859_2 + alias iso8859_2 # JDK historical + alias 8859_2 + alias iso-ir-101 + alias ISO_8859-2 + alias ISO_8859-2:1987 + alias ISO8859-2 + alias latin2 + alias l2 + alias ibm912 + alias ibm-912 + alias cp912 + alias 912 + alias csISOLatin2 + +charset ISO-8859-4 ISO_8859_4 + alias iso8859_4 # JDK historical + alias iso8859-4 + alias 8859_4 + alias iso-ir-110 + alias ISO_8859-4 + alias ISO_8859-4:1988 + alias latin4 + alias l4 + alias ibm914 + alias ibm-914 + alias cp914 + alias 914 + alias csISOLatin4 + +charset ISO-8859-5 ISO_8859_5 + alias iso8859_5 # JDK historical + alias 8859_5 + alias iso-ir-144 + alias ISO_8859-5 + alias ISO_8859-5:1988 + alias ISO8859-5 + alias cyrillic + alias ibm915 + alias ibm-915 + alias cp915 + alias 915 + alias csISOLatinCyrillic + +charset ISO-8859-7 ISO_8859_7 + alias iso8859_7 # JDK historical + alias 8859_7 + alias iso-ir-126 + alias ISO_8859-7 + alias ISO_8859-7:1987 + alias ELOT_928 + alias ECMA-118 + alias greek + alias greek8 + alias csISOLatinGreek + alias sun_eu_greek # Solaris 7/8 compatibility + alias ibm813 + alias ibm-813 + alias 813 + alias cp813 + alias iso8859-7 # Solaris 9 compatibility + +charset ISO-8859-9 ISO_8859_9 + alias iso8859_9 # JDK historical + alias 8859_9 + alias iso-ir-148 + alias ISO_8859-9 + alias ISO_8859-9:1989 + alias ISO8859-9 + alias latin5 + alias l5 + alias ibm920 + alias ibm-920 + alias 920 + alias cp920 + alias csISOLatin5 + +charset ISO-8859-13 ISO_8859_13 + alias iso8859_13 # JDK historical + alias 8859_13 + alias iso_8859-13 + alias ISO8859-13 + +charset ISO-8859-15 ISO_8859_15 + + # IANA alias + alias ISO_8859-15 + + # Other aliases + alias 8859_15 + alias ISO-8859-15 + alias ISO8859_15 + alias ISO8859-15 + alias IBM923 + alias IBM-923 + alias cp923 + alias 923 + alias LATIN0 + alias LATIN9 + alias L9 + alias csISOlatin0 + alias csISOlatin9 + alias ISO8859_15_FDIS + +charset KOI8-R KOI8_R + alias koi8_r # JDK historical + alias koi8 + alias cskoi8r + +charset KOI8-U KOI8_U + alias koi8_u + +charset windows-1250 MS1250 + alias cp1250 # JDK historical + alias cp5346 # Euro IBM CCSID + +charset windows-1251 MS1251 + alias cp1251 # JDK historical + alias cp5347 # Euro IBM CCSID + alias ansi-1251 # Solaris compatibility + +charset windows-1252 MS1252 + alias cp1252 # JDK historical + alias cp5348 # Euro IBM CCSID + +charset windows-1253 MS1253 + alias cp1253 # JDK historical + alias cp5349 # Euro IBM CCSID + +charset windows-1254 MS1254 + alias cp1254 # JDK historical + alias cp5350 # Euro IBM CCSID + +charset windows-1257 MS1257 + alias cp1257 # JDK historical + alias cp5353 # Euro IBM CCSID + + +charset IBM437 IBM437 + alias cp437 #JDK historical + alias ibm437 + alias ibm-437 + alias 437 + alias cspc8codepage437 + alias windows-437 + +charset x-IBM737 IBM737 + alias cp737 #JDK historical + alias ibm737 + alias ibm-737 + alias 737 + +charset IBM775 IBM775 + alias cp775 #JDK historical + alias ibm775 + alias ibm-775 + alias 775 + +charset IBM850 IBM850 + alias cp850 #JDK historical + alias ibm-850 + alias ibm850 + alias 850 + alias cspc850multilingual + +charset IBM852 IBM852 + alias cp852 #JDK historical + alias ibm852 + alias ibm-852 + alias 852 + alias csPCp852 + +charset IBM855 IBM855 + alias cp855 #JDK historical + alias ibm-855 + alias ibm855 + alias 855 + alias cspcp855 + +charset IBM857 IBM857 + alias cp857 #JDK historical + alias ibm857 + alias ibm-857 + alias 857 + alias csIBM857 + +charset IBM00858 IBM858 + alias cp858 #JDK historical + alias ccsid00858 + alias cp00858 + alias 858 + alias PC-Multilingual-850+euro + +charset IBM862 IBM862 + alias cp862 #JDK historical + alias ibm862 + alias ibm-862 + alias 862 + alias csIBM862 + alias cspc862latinhebrew + +charset IBM866 IBM866 + alias cp866 #JDK historical + alias ibm866 + alias ibm-866 + alias 866 + alias csIBM866 + +charset x-IBM874 IBM874 + alias cp874 #JDK historical + alias ibm874 + alias ibm-874 + alias 874 diff --git a/src/sun/nio/fs/AbstractAclFileAttributeView.java b/src/sun/nio/fs/AbstractAclFileAttributeView.java new file mode 100644 index 00000000..79636eff --- /dev/null +++ b/src/sun/nio/fs/AbstractAclFileAttributeView.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.attribute.AclEntry; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.UserPrincipal; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Base implementation of AclFileAttributeView + */ + +abstract class AbstractAclFileAttributeView + implements AclFileAttributeView, DynamicFileAttributeView +{ + private static final String OWNER_NAME = "owner"; + private static final String ACL_NAME = "acl"; + + @Override + public final String name() { + return "acl"; + } + + @Override + @SuppressWarnings("unchecked") + public final void setAttribute(String attribute, Object value) + throws IOException + { + if (attribute.equals(OWNER_NAME)) { + setOwner((UserPrincipal)value); + return; + } + if (attribute.equals(ACL_NAME)) { + setAcl((List)value); + return; + } + throw new IllegalArgumentException("'" + name() + ":" + + attribute + "' not recognized"); + } + + @Override + public final Map readAttributes(String[] attributes) + throws IOException + { + boolean acl = false; + boolean owner = false; + for (String attribute: attributes) { + if (attribute.equals("*")) { + owner = true; + acl = true; + continue; + } + if (attribute.equals(ACL_NAME)) { + acl = true; + continue; + } + if (attribute.equals(OWNER_NAME)) { + owner = true; + continue; + } + throw new IllegalArgumentException("'" + name() + ":" + + attribute + "' not recognized"); + } + Map result = new HashMap<>(2); + if (acl) + result.put(ACL_NAME, getAcl()); + if (owner) + result.put(OWNER_NAME, getOwner()); + return Collections.unmodifiableMap(result); + } +} diff --git a/src/sun/nio/fs/AbstractBasicFileAttributeView.java b/src/sun/nio/fs/AbstractBasicFileAttributeView.java new file mode 100644 index 00000000..8eceb902 --- /dev/null +++ b/src/sun/nio/fs/AbstractBasicFileAttributeView.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Base implementation of BasicFileAttributeView + */ + +abstract class AbstractBasicFileAttributeView + implements BasicFileAttributeView, DynamicFileAttributeView +{ + private static final String SIZE_NAME = "size"; + private static final String CREATION_TIME_NAME = "creationTime"; + private static final String LAST_ACCESS_TIME_NAME = "lastAccessTime"; + private static final String LAST_MODIFIED_TIME_NAME = "lastModifiedTime"; + private static final String FILE_KEY_NAME = "fileKey"; + private static final String IS_DIRECTORY_NAME = "isDirectory"; + private static final String IS_REGULAR_FILE_NAME = "isRegularFile"; + private static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink"; + private static final String IS_OTHER_NAME = "isOther"; + + // the names of the basic attributes + static final Set basicAttributeNames = + Util.newSet(SIZE_NAME, + CREATION_TIME_NAME, + LAST_ACCESS_TIME_NAME, + LAST_MODIFIED_TIME_NAME, + FILE_KEY_NAME, + IS_DIRECTORY_NAME, + IS_REGULAR_FILE_NAME, + IS_SYMBOLIC_LINK_NAME, + IS_OTHER_NAME); + + protected AbstractBasicFileAttributeView() { } + + @Override + public String name() { + return "basic"; + } + + @Override + public void setAttribute(String attribute, Object value) + throws IOException + { + if (attribute.equals(LAST_MODIFIED_TIME_NAME)) { + setTimes((FileTime)value, null, null); + return; + } + if (attribute.equals(LAST_ACCESS_TIME_NAME)) { + setTimes(null, (FileTime)value, null); + return; + } + if (attribute.equals(CREATION_TIME_NAME)) { + setTimes(null, null, (FileTime)value); + return; + } + throw new IllegalArgumentException("'" + name() + ":" + + attribute + "' not recognized"); + } + + /** + * Used to build a map of attribute name/values. + */ + static class AttributesBuilder { + private Set names = new HashSet<>(); + private Map map = new HashMap<>(); + private boolean copyAll; + + private AttributesBuilder(Set allowed, String[] requested) { + for (String name: requested) { + if (name.equals("*")) { + copyAll = true; + } else { + if (!allowed.contains(name)) + throw new IllegalArgumentException("'" + name + "' not recognized"); + names.add(name); + } + } + } + + /** + * Creates builder to build up a map of the matching attributes + */ + static AttributesBuilder create(Set allowed, String[] requested) { + return new AttributesBuilder(allowed, requested); + } + + /** + * Returns true if the attribute should be returned in the map + */ + boolean match(String name) { + return copyAll || names.contains(name); + } + + void add(String name, Object value) { + map.put(name, value); + } + + /** + * Returns the map. Discard all references to the AttributesBuilder + * after invoking this method. + */ + Map unmodifiableMap() { + return Collections.unmodifiableMap(map); + } + } + + /** + * Invoked by readAttributes or sub-classes to add all matching basic + * attributes to the builder + */ + final void addRequestedBasicAttributes(BasicFileAttributes attrs, + AttributesBuilder builder) + { + if (builder.match(SIZE_NAME)) + builder.add(SIZE_NAME, attrs.size()); + if (builder.match(CREATION_TIME_NAME)) + builder.add(CREATION_TIME_NAME, attrs.creationTime()); + if (builder.match(LAST_ACCESS_TIME_NAME)) + builder.add(LAST_ACCESS_TIME_NAME, attrs.lastAccessTime()); + if (builder.match(LAST_MODIFIED_TIME_NAME)) + builder.add(LAST_MODIFIED_TIME_NAME, attrs.lastModifiedTime()); + if (builder.match(FILE_KEY_NAME)) + builder.add(FILE_KEY_NAME, attrs.fileKey()); + if (builder.match(IS_DIRECTORY_NAME)) + builder.add(IS_DIRECTORY_NAME, attrs.isDirectory()); + if (builder.match(IS_REGULAR_FILE_NAME)) + builder.add(IS_REGULAR_FILE_NAME, attrs.isRegularFile()); + if (builder.match(IS_SYMBOLIC_LINK_NAME)) + builder.add(IS_SYMBOLIC_LINK_NAME, attrs.isSymbolicLink()); + if (builder.match(IS_OTHER_NAME)) + builder.add(IS_OTHER_NAME, attrs.isOther()); + } + + @Override + public Map readAttributes(String[] requested) + throws IOException + { + AttributesBuilder builder = + AttributesBuilder.create(basicAttributeNames, requested); + addRequestedBasicAttributes(readAttributes(), builder); + return builder.unmodifiableMap(); + } +} diff --git a/src/sun/nio/fs/AbstractFileSystemProvider.java b/src/sun/nio/fs/AbstractFileSystemProvider.java new file mode 100644 index 00000000..903a1252 --- /dev/null +++ b/src/sun/nio/fs/AbstractFileSystemProvider.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.spi.FileSystemProvider; +import java.util.Map; + +/** + * Base implementation class of FileSystemProvider + */ + +abstract class AbstractFileSystemProvider extends FileSystemProvider { + protected AbstractFileSystemProvider() { } + + /** + * Splits the given attribute name into the name of an attribute view and + * the attribute. If the attribute view is not identified then it assumed + * to be "basic". + */ + private static String[] split(String attribute) { + String[] s = new String[2]; + int pos = attribute.indexOf(':'); + if (pos == -1) { + s[0] = "basic"; + s[1] = attribute; + } else { + s[0] = attribute.substring(0, pos++); + s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos); + } + return s; + } + + /** + * Gets a DynamicFileAttributeView by name. Returns {@code null} if the + * view is not available. + */ + abstract DynamicFileAttributeView getFileAttributeView(Path file, + String name, + LinkOption... options); + + @Override + public final void setAttribute(Path file, + String attribute, + Object value, + LinkOption... options) + throws IOException + { + String[] s = split(attribute); + if (s[0].length() == 0) + throw new IllegalArgumentException(attribute); + DynamicFileAttributeView view = getFileAttributeView(file, s[0], options); + if (view == null) + throw new UnsupportedOperationException("View '" + s[0] + "' not available"); + view.setAttribute(s[1], value); + } + + @Override + public final Map readAttributes(Path file, String attributes, LinkOption... options) + throws IOException + { + String[] s = split(attributes); + if (s[0].length() == 0) + throw new IllegalArgumentException(attributes); + DynamicFileAttributeView view = getFileAttributeView(file, s[0], options); + if (view == null) + throw new UnsupportedOperationException("View '" + s[0] + "' not available"); + return view.readAttributes(s[1].split(",")); + } + + /** + * Deletes a file. The {@code failIfNotExists} parameters determines if an + * {@code IOException} is thrown when the file does not exist. + */ + abstract boolean implDelete(Path file, boolean failIfNotExists) throws IOException; + + @Override + public final void delete(Path file) throws IOException { + implDelete(file, true); + } + + @Override + public final boolean deleteIfExists(Path file) throws IOException { + return implDelete(file, false); + } +} diff --git a/src/sun/nio/fs/AbstractFileTypeDetector.java b/src/sun/nio/fs/AbstractFileTypeDetector.java new file mode 100644 index 00000000..e34f8728 --- /dev/null +++ b/src/sun/nio/fs/AbstractFileTypeDetector.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.spi.FileTypeDetector; +import java.util.Locale; + +/** + * Base implementation of FileTypeDetector + */ + +public abstract class AbstractFileTypeDetector + extends FileTypeDetector +{ + protected AbstractFileTypeDetector() { + super(); + } + + /** + * Invokes the appropriate probe method to guess a file's content type, + * and checks that the content type's syntax is valid. + */ + @Override + public final String probeContentType(Path file) throws IOException { + if (file == null) + throw new NullPointerException("'file' is null"); + String result = implProbeContentType(file); + return (result == null) ? null : parse(result); + } + + /** + * Probes the given file to guess its content type. + */ + protected abstract String implProbeContentType(Path file) + throws IOException; + + /** + * Parses a candidate content type into its type and subtype, returning + * null if either token is invalid. + */ + private static String parse(String s) { + int slash = s.indexOf('/'); + int semicolon = s.indexOf(';'); + if (slash < 0) + return null; // no subtype + String type = s.substring(0, slash).trim().toLowerCase(Locale.ENGLISH); + if (!isValidToken(type)) + return null; // invalid type + String subtype = (semicolon < 0) ? s.substring(slash + 1) : + s.substring(slash + 1, semicolon); + subtype = subtype.trim().toLowerCase(Locale.ENGLISH); + if (!isValidToken(subtype)) + return null; // invalid subtype + StringBuilder sb = new StringBuilder(type.length() + subtype.length() + 1); + sb.append(type); + sb.append('/'); + sb.append(subtype); + return sb.toString(); + } + + /** + * Special characters + */ + private static final String TSPECIALS = "()<>@,;:/[]?=\\\""; + + /** + * Returns true if the character is a valid token character. + */ + private static boolean isTokenChar(char c) { + return (c > 040) && (c < 0177) && (TSPECIALS.indexOf(c) < 0); + } + + /** + * Returns true if the given string is a legal type or subtype. + */ + private static boolean isValidToken(String s) { + int len = s.length(); + if (len == 0) + return false; + for (int i = 0; i < len; i++) { + if (!isTokenChar(s.charAt(i))) + return false; + } + return true; + } +} diff --git a/src/sun/nio/fs/AbstractPath.java b/src/sun/nio/fs/AbstractPath.java new file mode 100644 index 00000000..f3d5cbeb --- /dev/null +++ b/src/sun/nio/fs/AbstractPath.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Base implementation class of {@code Path}. + */ + +abstract class AbstractPath implements Path { + protected AbstractPath() { } + + @Override + public final boolean startsWith(String other) { + return startsWith(getFileSystem().getPath(other)); + } + + @Override + public final boolean endsWith(String other) { + return endsWith(getFileSystem().getPath(other)); + } + + @Override + public final Path resolve(String other) { + return resolve(getFileSystem().getPath(other)); + } + + @Override + public final Path resolveSibling(Path other) { + if (other == null) + throw new NullPointerException(); + Path parent = getParent(); + return (parent == null) ? other : parent.resolve(other); + } + + @Override + public final Path resolveSibling(String other) { + return resolveSibling(getFileSystem().getPath(other)); + } + + @Override + public final Iterator iterator() { + return new Iterator() { + private int i = 0; + @Override + public boolean hasNext() { + return (i < getNameCount()); + } + @Override + public Path next() { + if (i < getNameCount()) { + Path result = getName(i); + i++; + return result; + } else { + throw new NoSuchElementException(); + } + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public final File toFile() { + return new File(toString()); + } + + @Override + public final WatchKey register(WatchService watcher, + WatchEvent.Kind... events) + throws IOException + { + return register(watcher, events, new WatchEvent.Modifier[0]); + } +} diff --git a/src/sun/nio/fs/AbstractPoller.java b/src/sun/nio/fs/AbstractPoller.java new file mode 100644 index 00000000..9fb49954 --- /dev/null +++ b/src/sun/nio/fs/AbstractPoller.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +/** + * Base implementation of background poller thread used in watch service + * implementations. A poller thread waits on events from the file system and + * also services "requests" from clients to register for new events or cancel + * existing registrations. + */ + +abstract class AbstractPoller implements Runnable { + + // list of requests pending to the poller thread + private final LinkedList requestList; + + // set to true when shutdown + private boolean shutdown; + + protected AbstractPoller() { + this.requestList = new LinkedList(); + this.shutdown = false; + } + + /** + * Starts the poller thread + */ + public void start() { + final Runnable thisRunnable = this; + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + Thread thr = new Thread(thisRunnable); + thr.setDaemon(true); + thr.start(); + return null; + } + }); + } + + /** + * Wakeup poller thread so that it can service pending requests + */ + abstract void wakeup() throws IOException; + + /** + * Executed by poller thread to register directory for changes + */ + abstract Object implRegister(Path path, + Set> events, + WatchEvent.Modifier... modifiers); + + /** + * Executed by poller thread to cancel key + */ + abstract void implCancelKey(WatchKey key); + + /** + * Executed by poller thread to shutdown and cancel all keys + */ + abstract void implCloseAll(); + + /** + * Requests, and waits on, poller thread to register given file. + */ + final WatchKey register(Path dir, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) + throws IOException + { + // validate arguments before request to poller + if (dir == null) + throw new NullPointerException(); + Set> eventSet = new HashSet<>(events.length); + for (WatchEvent.Kind event: events) { + // standard events + if (event == StandardWatchEventKinds.ENTRY_CREATE || + event == StandardWatchEventKinds.ENTRY_MODIFY || + event == StandardWatchEventKinds.ENTRY_DELETE) + { + eventSet.add(event); + continue; + } + + // OVERFLOW is ignored + if (event == StandardWatchEventKinds.OVERFLOW) + continue; + + // null/unsupported + if (event == null) + throw new NullPointerException("An element in event set is 'null'"); + throw new UnsupportedOperationException(event.name()); + } + if (eventSet.isEmpty()) + throw new IllegalArgumentException("No events to register"); + return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers); + } + + /** + * Cancels, and waits on, poller thread to cancel given key. + */ + final void cancel(WatchKey key) { + try { + invoke(RequestType.CANCEL, key); + } catch (IOException x) { + // should not happen + throw new AssertionError(x.getMessage()); + } + } + + /** + * Shutdown poller thread + */ + final void close() throws IOException { + invoke(RequestType.CLOSE); + } + + /** + * Types of request that the poller thread must handle + */ + private static enum RequestType { + REGISTER, + CANCEL, + CLOSE; + } + + /** + * Encapsulates a request (command) to the poller thread. + */ + private static class Request { + private final RequestType type; + private final Object[] params; + + private boolean completed = false; + private Object result = null; + + Request(RequestType type, Object... params) { + this.type = type; + this.params = params; + } + + RequestType type() { + return type; + } + + Object[] parameters() { + return params; + } + + void release(Object result) { + synchronized (this) { + this.completed = true; + this.result = result; + notifyAll(); + } + } + + /** + * Await completion of the request. The return value is the result of + * the request. + */ + Object awaitResult() { + boolean interrupted = false; + synchronized (this) { + while (!completed) { + try { + wait(); + } catch (InterruptedException x) { + interrupted = true; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + return result; + } + } + } + + /** + * Enqueues request to poller thread and waits for result + */ + private Object invoke(RequestType type, Object... params) throws IOException { + // submit request + Request req = new Request(type, params); + synchronized (requestList) { + if (shutdown) { + throw new ClosedWatchServiceException(); + } + requestList.add(req); + } + + // wakeup thread + wakeup(); + + // wait for result + Object result = req.awaitResult(); + + if (result instanceof RuntimeException) + throw (RuntimeException)result; + if (result instanceof IOException ) + throw (IOException)result; + return result; + } + + /** + * Invoked by poller thread to process all pending requests + * + * @return true if poller thread should shutdown + */ + @SuppressWarnings("unchecked") + boolean processRequests() { + synchronized (requestList) { + Request req; + while ((req = requestList.poll()) != null) { + // if in process of shutdown then reject request + if (shutdown) { + req.release(new ClosedWatchServiceException()); + } + + switch (req.type()) { + /** + * Register directory + */ + case REGISTER: { + Object[] params = req.parameters(); + Path path = (Path)params[0]; + Set> events = + (Set>)params[1]; + WatchEvent.Modifier[] modifiers = + (WatchEvent.Modifier[])params[2]; + req.release(implRegister(path, events, modifiers)); + break; + } + /** + * Cancel existing key + */ + case CANCEL : { + Object[] params = req.parameters(); + WatchKey key = (WatchKey)params[0]; + implCancelKey(key); + req.release(null); + break; + } + /** + * Close watch service + */ + case CLOSE: { + implCloseAll(); + req.release(null); + shutdown = true; + break; + } + + default: + req.release(new IOException("request not recognized")); + } + } + } + return shutdown; + } +} diff --git a/src/sun/nio/fs/AbstractUserDefinedFileAttributeView.java b/src/sun/nio/fs/AbstractUserDefinedFileAttributeView.java new file mode 100644 index 00000000..9371dccc --- /dev/null +++ b/src/sun/nio/fs/AbstractUserDefinedFileAttributeView.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.attribute.UserDefinedFileAttributeView; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Base implementation of UserDefinedAttributeView + */ + +abstract class AbstractUserDefinedFileAttributeView + implements UserDefinedFileAttributeView, DynamicFileAttributeView +{ + protected AbstractUserDefinedFileAttributeView() { } + + protected void checkAccess(String file, + boolean checkRead, + boolean checkWrite) + { + assert checkRead || checkWrite; + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (checkRead) + sm.checkRead(file); + if (checkWrite) + sm.checkWrite(file); + sm.checkPermission(new RuntimePermission("accessUserDefinedAttributes")); + } + } + + @Override + public final String name() { + return "user"; + } + + @Override + public final void setAttribute(String attribute, Object value) + throws IOException + { + ByteBuffer bb; + if (value instanceof byte[]) { + bb = ByteBuffer.wrap((byte[])value); + } else { + bb = (ByteBuffer)value; + } + write(attribute, bb); + } + + @Override + public final Map readAttributes(String[] attributes) + throws IOException + { + // names of attributes to return + List names = new ArrayList<>(); + for (String name: attributes) { + if (name.equals("*")) { + names = list(); + break; + } else { + if (name.length() == 0) + throw new IllegalArgumentException(); + names.add(name); + } + } + + // read each value and return in map + Map result = new HashMap<>(); + for (String name: names) { + int size = size(name); + byte[] buf = new byte[size]; + int n = read(name, ByteBuffer.wrap(buf)); + byte[] value = (n == size) ? buf : Arrays.copyOf(buf, n); + result.put(name, value); + } + return result; + } +} diff --git a/src/sun/nio/fs/AbstractWatchKey.java b/src/sun/nio/fs/AbstractWatchKey.java new file mode 100644 index 00000000..316b5f6e --- /dev/null +++ b/src/sun/nio/fs/AbstractWatchKey.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Base implementation class for watch keys. + */ + +abstract class AbstractWatchKey implements WatchKey { + + /** + * Maximum size of event list (in the future this may be tunable) + */ + static final int MAX_EVENT_LIST_SIZE = 512; + + /** + * Special event to signal overflow + */ + static final Event OVERFLOW_EVENT = + new Event(StandardWatchEventKinds.OVERFLOW, null); + + /** + * Possible key states + */ + private static enum State { READY, SIGNALLED }; + + // reference to watcher + private final AbstractWatchService watcher; + + // reference to the original directory + private final Path dir; + + // key state + private State state; + + // pending events + private List> events; + + // maps a context to the last event for the context (iff the last queued + // event for the context is an ENTRY_MODIFY event). + private Map> lastModifyEvents; + + protected AbstractWatchKey(Path dir, AbstractWatchService watcher) { + this.watcher = watcher; + this.dir = dir; + this.state = State.READY; + this.events = new ArrayList>(); + this.lastModifyEvents = new HashMap>(); + } + + final AbstractWatchService watcher() { + return watcher; + } + + /** + * Return the original watchable (Path) + */ + @Override + public Path watchable() { + return dir; + } + + /** + * Enqueues this key to the watch service + */ + final void signal() { + synchronized (this) { + if (state == State.READY) { + state = State.SIGNALLED; + watcher.enqueueKey(this); + } + } + } + + /** + * Adds the event to this key and signals it. + */ + @SuppressWarnings("unchecked") + final void signalEvent(WatchEvent.Kind kind, Object context) { + boolean isModify = (kind == StandardWatchEventKinds.ENTRY_MODIFY); + synchronized (this) { + int size = events.size(); + if (size > 0) { + // if the previous event is an OVERFLOW event or this is a + // repeated event then we simply increment the counter + WatchEvent prev = events.get(size-1); + if ((prev.kind() == StandardWatchEventKinds.OVERFLOW) || + ((kind == prev.kind() && + Objects.equals(context, prev.context())))) + { + ((Event)prev).increment(); + return; + } + + // if this is a modify event and the last entry for the context + // is a modify event then we simply increment the count + if (!lastModifyEvents.isEmpty()) { + if (isModify) { + WatchEvent ev = lastModifyEvents.get(context); + if (ev != null) { + assert ev.kind() == StandardWatchEventKinds.ENTRY_MODIFY; + ((Event)ev).increment(); + return; + } + } else { + // not a modify event so remove from the map as the + // last event will no longer be a modify event. + lastModifyEvents.remove(context); + } + } + + // if the list has reached the limit then drop pending events + // and queue an OVERFLOW event + if (size >= MAX_EVENT_LIST_SIZE) { + kind = StandardWatchEventKinds.OVERFLOW; + isModify = false; + context = null; + } + } + + // non-repeated event + Event ev = + new Event((WatchEvent.Kind)kind, context); + if (isModify) { + lastModifyEvents.put(context, ev); + } else if (kind == StandardWatchEventKinds.OVERFLOW) { + // drop all pending events + events.clear(); + lastModifyEvents.clear(); + } + events.add(ev); + signal(); + } + } + + @Override + public final List> pollEvents() { + synchronized (this) { + List> result = events; + events = new ArrayList>(); + lastModifyEvents.clear(); + return result; + } + } + + @Override + public final boolean reset() { + synchronized (this) { + if (state == State.SIGNALLED && isValid()) { + if (events.isEmpty()) { + state = State.READY; + } else { + // pending events so re-queue key + watcher.enqueueKey(this); + } + } + return isValid(); + } + } + + /** + * WatchEvent implementation + */ + private static class Event implements WatchEvent { + private final Kind kind; + private final T context; + + // synchronize on watch key to access/increment count + private int count; + + Event(Kind type, T context) { + this.kind = type; + this.context = context; + this.count = 1; + } + + @Override + public Kind kind() { + return kind; + } + + @Override + public T context() { + return context; + } + + @Override + public int count() { + return count; + } + + // for repeated events + void increment() { + count++; + } + } +} diff --git a/src/sun/nio/fs/AbstractWatchService.java b/src/sun/nio/fs/AbstractWatchService.java new file mode 100644 index 00000000..932542d7 --- /dev/null +++ b/src/sun/nio/fs/AbstractWatchService.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.Path; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +/** + * Base implementation class for watch services. + */ + +abstract class AbstractWatchService implements WatchService { + + // signaled keys waiting to be dequeued + private final LinkedBlockingDeque pendingKeys = + new LinkedBlockingDeque(); + + // special key to indicate that watch service is closed + private final WatchKey CLOSE_KEY = + new AbstractWatchKey(null, null) { + @Override + public boolean isValid() { + return true; + } + + @Override + public void cancel() { + } + }; + + // used when closing watch service + private volatile boolean closed; + private final Object closeLock = new Object(); + + protected AbstractWatchService() { + } + + /** + * Register the given object with this watch service + */ + abstract WatchKey register(Path path, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifers) + throws IOException; + + // used by AbstractWatchKey to enqueue key + final void enqueueKey(WatchKey key) { + pendingKeys.offer(key); + } + + /** + * Throws ClosedWatchServiceException if watch service is closed + */ + private void checkOpen() { + if (closed) + throw new ClosedWatchServiceException(); + } + + /** + * Checks the key isn't the special CLOSE_KEY used to unblock threads when + * the watch service is closed. + */ + private void checkKey(WatchKey key) { + if (key == CLOSE_KEY) { + // re-queue in case there are other threads blocked in take/poll + enqueueKey(key); + } + checkOpen(); + } + + @Override + public final WatchKey poll() { + checkOpen(); + WatchKey key = pendingKeys.poll(); + checkKey(key); + return key; + } + + @Override + public final WatchKey poll(long timeout, TimeUnit unit) + throws InterruptedException + { + checkOpen(); + WatchKey key = pendingKeys.poll(timeout, unit); + checkKey(key); + return key; + } + + @Override + public final WatchKey take() + throws InterruptedException + { + checkOpen(); + WatchKey key = pendingKeys.take(); + checkKey(key); + return key; + } + + /** + * Tells whether or not this watch service is open. + */ + final boolean isOpen() { + return !closed; + } + + /** + * Retrieves the object upon which the close method synchronizes. + */ + final Object closeLock() { + return closeLock; + } + + /** + * Closes this watch service. This method is invoked by the close + * method to perform the actual work of closing the watch service. + */ + abstract void implClose() throws IOException; + + @Override + public final void close() + throws IOException + { + synchronized (closeLock) { + // nothing to do if already closed + if (closed) + return; + closed = true; + + implClose(); + + // clear pending keys and queue special key to ensure that any + // threads blocked in take/poll wakeup + pendingKeys.clear(); + pendingKeys.offer(CLOSE_KEY); + } + } +} diff --git a/src/sun/nio/fs/BasicFileAttributesHolder.java b/src/sun/nio/fs/BasicFileAttributesHolder.java new file mode 100644 index 00000000..d88f027e --- /dev/null +++ b/src/sun/nio/fs/BasicFileAttributesHolder.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.nio.file.attribute.BasicFileAttributes; + +/** + * Implemented by objects that may hold or cache the attributes of a file. + */ + +public interface BasicFileAttributesHolder { + /** + * Returns cached attributes (may be null). If file is a symbolic link then + * the attributes are the link attributes and not the final target of the + * file. + */ + BasicFileAttributes get(); + + /** + * Invalidates cached attributes + */ + void invalidate(); +} diff --git a/src/sun/nio/fs/Cancellable.java b/src/sun/nio/fs/Cancellable.java new file mode 100644 index 00000000..29f7e6d9 --- /dev/null +++ b/src/sun/nio/fs/Cancellable.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.util.concurrent.ExecutionException; + +import sun.misc.Unsafe; + +/** + * Base implementation of a task (typically native) that polls a memory location + * during execution so that it may be aborted/cancelled before completion. The + * task is executed by invoking the {@link runInterruptibly} method defined + * here and cancelled by invoking Thread.interrupt. + */ + +abstract class Cancellable implements Runnable { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + private final long pollingAddress; + private final Object lock = new Object(); + + // the following require lock when examining or changing + private boolean completed; + private Throwable exception; + + protected Cancellable() { + pollingAddress = unsafe.allocateMemory(4); + unsafe.putIntVolatile(null, pollingAddress, 0); + } + + /** + * Returns the memory address of a 4-byte int that should be polled to + * detect cancellation. + */ + protected long addressToPollForCancel() { + return pollingAddress; + } + + /** + * The value to write to the polled memory location to indicate that the + * task has been cancelled. If this method is not overridden then it + * defaults to MAX_VALUE. + */ + protected int cancelValue() { + return Integer.MAX_VALUE; + } + + /** + * "cancels" the task by writing bits into memory location that it polled + * by the task. + */ + final void cancel() { + synchronized (lock) { + if (!completed) { + unsafe.putIntVolatile(null, pollingAddress, cancelValue()); + } + } + } + + /** + * Returns the exception thrown by the task or null if the task completed + * successfully. + */ + private Throwable exception() { + synchronized (lock) { + return exception; + } + } + + @Override + public final void run() { + try { + implRun(); + } catch (Throwable t) { + synchronized (lock) { + exception = t; + } + } finally { + synchronized (lock) { + completed = true; + unsafe.freeMemory(pollingAddress); + } + } + } + + /** + * The task body. This should periodically poll the memory location + * to check for cancellation. + */ + abstract void implRun() throws Throwable; + + /** + * Invokes the given task in its own thread. If this (meaning the current) + * thread is interrupted then an attempt is make to cancel the background + * thread by writing into the memory location that it polls cooperatively. + */ + static void runInterruptibly(Cancellable task) throws ExecutionException { + Thread t = new Thread(task); + t.start(); + boolean cancelledByInterrupt = false; + while (t.isAlive()) { + try { + t.join(); + } catch (InterruptedException e) { + cancelledByInterrupt = true; + task.cancel(); + } + } + if (cancelledByInterrupt) + Thread.currentThread().interrupt(); + Throwable exc = task.exception(); + if (exc != null) + throw new ExecutionException(exc); + } +} diff --git a/src/sun/nio/fs/DynamicFileAttributeView.java b/src/sun/nio/fs/DynamicFileAttributeView.java new file mode 100644 index 00000000..db346f08 --- /dev/null +++ b/src/sun/nio/fs/DynamicFileAttributeView.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.util.Map; + +/** + * Implemented by FileAttributeView implementations to support access to + * attributes by names. + */ + +interface DynamicFileAttributeView { + /** + * Sets/updates the value of an attribute. + */ + void setAttribute(String attribute, Object value) throws IOException; + + /** + * Reads a set of file attributes as a bulk operation. + */ + Map readAttributes(String[] attributes) throws IOException; +} diff --git a/src/sun/nio/fs/FileOwnerAttributeViewImpl.java b/src/sun/nio/fs/FileOwnerAttributeViewImpl.java new file mode 100644 index 00000000..3cb0d39d --- /dev/null +++ b/src/sun/nio/fs/FileOwnerAttributeViewImpl.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.UserPrincipal; +import java.util.HashMap; +import java.util.Map; + +/** + * An implementation of FileOwnerAttributeView that delegates to a given + * PosixFileAttributeView or AclFileAttributeView object. + */ + +final class FileOwnerAttributeViewImpl + implements FileOwnerAttributeView, DynamicFileAttributeView +{ + private static final String OWNER_NAME = "owner"; + + private final FileAttributeView view; + private final boolean isPosixView; + + FileOwnerAttributeViewImpl(PosixFileAttributeView view) { + this.view = view; + this.isPosixView = true; + } + + FileOwnerAttributeViewImpl(AclFileAttributeView view) { + this.view = view; + this.isPosixView = false; + } + + @Override + public String name() { + return "owner"; + } + + @Override + public void setAttribute(String attribute, Object value) + throws IOException + { + if (attribute.equals(OWNER_NAME)) { + setOwner((UserPrincipal)value); + } else { + throw new IllegalArgumentException("'" + name() + ":" + + attribute + "' not recognized"); + } + } + + @Override + public Map readAttributes(String[] attributes) throws IOException { + Map result = new HashMap<>(); + for (String attribute: attributes) { + if (attribute.equals("*") || attribute.equals(OWNER_NAME)) { + result.put(OWNER_NAME, getOwner()); + } else { + throw new IllegalArgumentException("'" + name() + ":" + + attribute + "' not recognized"); + } + } + return result; + } + + @Override + public UserPrincipal getOwner() throws IOException { + if (isPosixView) { + return ((PosixFileAttributeView)view).readAttributes().owner(); + } else { + return ((AclFileAttributeView)view).getOwner(); + } + } + + @Override + public void setOwner(UserPrincipal owner) + throws IOException + { + if (isPosixView) { + ((PosixFileAttributeView)view).setOwner(owner); + } else { + ((AclFileAttributeView)view).setOwner(owner); + } + } + } diff --git a/src/sun/nio/fs/Globs.java b/src/sun/nio/fs/Globs.java new file mode 100644 index 00000000..5bcf8d6d --- /dev/null +++ b/src/sun/nio/fs/Globs.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.util.regex.PatternSyntaxException; + +public class Globs { + private Globs() { } + + private static final String regexMetaChars = ".^$+{[]|()"; + private static final String globMetaChars = "\\*?[{"; + + private static boolean isRegexMeta(char c) { + return regexMetaChars.indexOf(c) != -1; + } + + private static boolean isGlobMeta(char c) { + return globMetaChars.indexOf(c) != -1; + } + private static char EOL = 0; //TBD + + private static char next(String glob, int i) { + if (i < glob.length()) { + return glob.charAt(i); + } + return EOL; + } + + /** + * Creates a regex pattern from the given glob expression. + * + * @throws PatternSyntaxException + */ + private static String toRegexPattern(String globPattern, boolean isDos) { + boolean inGroup = false; + StringBuilder regex = new StringBuilder("^"); + + int i = 0; + while (i < globPattern.length()) { + char c = globPattern.charAt(i++); + switch (c) { + case '\\': + // escape special characters + if (i == globPattern.length()) { + throw new PatternSyntaxException("No character to escape", + globPattern, i - 1); + } + char next = globPattern.charAt(i++); + if (isGlobMeta(next) || isRegexMeta(next)) { + regex.append('\\'); + } + regex.append(next); + break; + case '/': + if (isDos) { + regex.append("\\\\"); + } else { + regex.append(c); + } + break; + case '[': + // don't match name separator in class + if (isDos) { + regex.append("[[^\\\\]&&["); + } else { + regex.append("[[^/]&&["); + } + if (next(globPattern, i) == '^') { + // escape the regex negation char if it appears + regex.append("\\^"); + i++; + } else { + // negation + if (next(globPattern, i) == '!') { + regex.append('^'); + i++; + } + // hyphen allowed at start + if (next(globPattern, i) == '-') { + regex.append('-'); + i++; + } + } + boolean hasRangeStart = false; + char last = 0; + while (i < globPattern.length()) { + c = globPattern.charAt(i++); + if (c == ']') { + break; + } + if (c == '/' || (isDos && c == '\\')) { + throw new PatternSyntaxException("Explicit 'name separator' in class", + globPattern, i - 1); + } + // TBD: how to specify ']' in a class? + if (c == '\\' || c == '[' || + c == '&' && next(globPattern, i) == '&') { + // escape '\', '[' or "&&" for regex class + regex.append('\\'); + } + regex.append(c); + + if (c == '-') { + if (!hasRangeStart) { + throw new PatternSyntaxException("Invalid range", + globPattern, i - 1); + } + if ((c = next(globPattern, i++)) == EOL || c == ']') { + break; + } + if (c < last) { + throw new PatternSyntaxException("Invalid range", + globPattern, i - 3); + } + regex.append(c); + hasRangeStart = false; + } else { + hasRangeStart = true; + last = c; + } + } + if (c != ']') { + throw new PatternSyntaxException("Missing ']", globPattern, i - 1); + } + regex.append("]]"); + break; + case '{': + if (inGroup) { + throw new PatternSyntaxException("Cannot nest groups", + globPattern, i - 1); + } + regex.append("(?:(?:"); + inGroup = true; + break; + case '}': + if (inGroup) { + regex.append("))"); + inGroup = false; + } else { + regex.append('}'); + } + break; + case ',': + if (inGroup) { + regex.append(")|(?:"); + } else { + regex.append(','); + } + break; + case '*': + if (next(globPattern, i) == '*') { + // crosses directory boundaries + regex.append(".*"); + i++; + } else { + // within directory boundary + if (isDos) { + regex.append("[^\\\\]*"); + } else { + regex.append("[^/]*"); + } + } + break; + case '?': + if (isDos) { + regex.append("[^\\\\]"); + } else { + regex.append("[^/]"); + } + break; + + default: + if (isRegexMeta(c)) { + regex.append('\\'); + } + regex.append(c); + } + } + + if (inGroup) { + throw new PatternSyntaxException("Missing '}", globPattern, i - 1); + } + + return regex.append('$').toString(); + } + + static String toUnixRegexPattern(String globPattern) { + return toRegexPattern(globPattern, false); + } + + static String toWindowsRegexPattern(String globPattern) { + return toRegexPattern(globPattern, true); + } +} diff --git a/src/sun/nio/fs/NativeBuffer.java b/src/sun/nio/fs/NativeBuffer.java new file mode 100644 index 00000000..c89c4f03 --- /dev/null +++ b/src/sun/nio/fs/NativeBuffer.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import sun.misc.Cleaner; +import sun.misc.Unsafe; + +/** + * A light-weight buffer in native memory. + */ + +class NativeBuffer { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + private final long address; + private final int size; + private final Cleaner cleaner; + + // optional "owner" to avoid copying + // (only safe for use by thread-local caches) + private Object owner; + + private static class Deallocator implements Runnable { + private final long address; + Deallocator(long address) { + this.address = address; + } + public void run() { + unsafe.freeMemory(address); + } + } + + NativeBuffer(int size) { + this.address = unsafe.allocateMemory(size); + this.size = size; + this.cleaner = Cleaner.create(this, new Deallocator(address)); + } + + void release() { + NativeBuffers.releaseNativeBuffer(this); + } + + long address() { + return address; + } + + int size() { + return size; + } + + Cleaner cleaner() { + return cleaner; + } + + // not synchronized; only safe for use by thread-local caches + void setOwner(Object owner) { + this.owner = owner; + } + + // not synchronized; only safe for use by thread-local caches + Object owner() { + return owner; + } +} diff --git a/src/sun/nio/fs/NativeBuffers.java b/src/sun/nio/fs/NativeBuffers.java new file mode 100644 index 00000000..41ae7ea8 --- /dev/null +++ b/src/sun/nio/fs/NativeBuffers.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import sun.misc.Unsafe; + +/** + * Factory for native buffers. + */ + +class NativeBuffers { + private NativeBuffers() { } + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + private static final int TEMP_BUF_POOL_SIZE = 3; + private static ThreadLocal threadLocal = + new ThreadLocal(); + + /** + * Allocates a native buffer, of at least the given size, from the heap. + */ + static NativeBuffer allocNativeBuffer(int size) { + // Make a new one of at least 2k + if (size < 2048) size = 2048; + return new NativeBuffer(size); + } + + /** + * Returns a native buffer, of at least the given size, from the thread + * local cache. + */ + static NativeBuffer getNativeBufferFromCache(int size) { + // return from cache if possible + NativeBuffer[] buffers = threadLocal.get(); + if (buffers != null) { + for (int i=0; i= size) { + buffers[i] = null; + return buffer; + } + } + } + return null; + } + + /** + * Returns a native buffer, of at least the given size. The native buffer + * is taken from the thread local cache if possible; otherwise it is + * allocated from the heap. + */ + static NativeBuffer getNativeBuffer(int size) { + NativeBuffer buffer = getNativeBufferFromCache(size); + if (buffer != null) { + buffer.setOwner(null); + return buffer; + } else { + return allocNativeBuffer(size); + } + } + + /** + * Releases the given buffer. If there is space in the thread local cache + * then the buffer goes into the cache; otherwise the memory is deallocated. + */ + static void releaseNativeBuffer(NativeBuffer buffer) { + // create cache if it doesn't exist + NativeBuffer[] buffers = threadLocal.get(); + if (buffers == null) { + buffers = new NativeBuffer[TEMP_BUF_POOL_SIZE]; + buffers[0] = buffer; + threadLocal.set(buffers); + return; + } + // Put it in an empty slot if such exists + for (int i=0; i= (len + 1); + unsafe.copyMemory(cstr, offset, null, buffer.address(), len); + unsafe.putByte(buffer.address() + len, (byte)0); + } + + /** + * Copies a byte array and zero terminator into a native buffer, returning + * the buffer. + */ + static NativeBuffer asNativeBuffer(byte[] cstr) { + NativeBuffer buffer = getNativeBuffer(cstr.length+1); + copyCStringToNativeBuffer(cstr, buffer); + return buffer; + } +} diff --git a/src/sun/nio/fs/PollingWatchService.java b/src/sun/nio/fs/PollingWatchService.java new file mode 100644 index 00000000..de3b0f4d --- /dev/null +++ b/src/sun/nio/fs/PollingWatchService.java @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.io.IOException; +import java.nio.file.ClosedWatchServiceException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.attribute.BasicFileAttributes; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import com.sun.nio.file.SensitivityWatchEventModifier; + +/** + * Simple WatchService implementation that uses periodic tasks to poll + * registered directories for changes. This implementation is for use on + * operating systems that do not have native file change notification support. + */ + +class PollingWatchService + extends AbstractWatchService +{ + // map of registrations + private final Map map = + new HashMap(); + + // used to execute the periodic tasks that poll for changes + private final ScheduledExecutorService scheduledExecutor; + + PollingWatchService() { + // TBD: Make the number of threads configurable + scheduledExecutor = Executors + .newSingleThreadScheduledExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + }}); + } + + /** + * Register the given file with this watch service + */ + @Override + WatchKey register(final Path path, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) + throws IOException + { + // check events - CCE will be thrown if there are invalid elements + final Set> eventSet = + new HashSet>(events.length); + for (WatchEvent.Kind event: events) { + // standard events + if (event == StandardWatchEventKinds.ENTRY_CREATE || + event == StandardWatchEventKinds.ENTRY_MODIFY || + event == StandardWatchEventKinds.ENTRY_DELETE) + { + eventSet.add(event); + continue; + } + + // OVERFLOW is ignored + if (event == StandardWatchEventKinds.OVERFLOW) { + continue; + } + + // null/unsupported + if (event == null) + throw new NullPointerException("An element in event set is 'null'"); + throw new UnsupportedOperationException(event.name()); + } + if (eventSet.isEmpty()) + throw new IllegalArgumentException("No events to register"); + + // A modifier may be used to specify the sensitivity level + SensitivityWatchEventModifier sensivity = SensitivityWatchEventModifier.MEDIUM; + if (modifiers.length > 0) { + for (WatchEvent.Modifier modifier: modifiers) { + if (modifier == null) + throw new NullPointerException(); + if (modifier instanceof SensitivityWatchEventModifier) { + sensivity = (SensitivityWatchEventModifier)modifier; + continue; + } + throw new UnsupportedOperationException("Modifier not supported"); + } + } + + // check if watch service is closed + if (!isOpen()) + throw new ClosedWatchServiceException(); + + // registration is done in privileged block as it requires the + // attributes of the entries in the directory. + try { + final SensitivityWatchEventModifier s = sensivity; + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public PollingWatchKey run() throws IOException { + return doPrivilegedRegister(path, eventSet, s); + } + }); + } catch (PrivilegedActionException pae) { + Throwable cause = pae.getCause(); + if (cause != null && cause instanceof IOException) + throw (IOException)cause; + throw new AssertionError(pae); + } + } + + // registers directory returning a new key if not already registered or + // existing key if already registered + private PollingWatchKey doPrivilegedRegister(Path path, + Set> events, + SensitivityWatchEventModifier sensivity) + throws IOException + { + // check file is a directory and get its file key if possible + BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); + if (!attrs.isDirectory()) { + throw new NotDirectoryException(path.toString()); + } + Object fileKey = attrs.fileKey(); + if (fileKey == null) + throw new AssertionError("File keys must be supported"); + + // grab close lock to ensure that watch service cannot be closed + synchronized (closeLock()) { + if (!isOpen()) + throw new ClosedWatchServiceException(); + + PollingWatchKey watchKey; + synchronized (map) { + watchKey = map.get(fileKey); + if (watchKey == null) { + // new registration + watchKey = new PollingWatchKey(path, this, fileKey); + map.put(fileKey, watchKey); + } else { + // update to existing registration + watchKey.disable(); + } + } + watchKey.enable(events, sensivity.sensitivityValueInSeconds()); + return watchKey; + } + + } + + @Override + void implClose() throws IOException { + synchronized (map) { + for (Map.Entry entry: map.entrySet()) { + PollingWatchKey watchKey = entry.getValue(); + watchKey.disable(); + watchKey.invalidate(); + } + map.clear(); + } + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + scheduledExecutor.shutdown(); + return null; + } + }); + } + + /** + * Entry in directory cache to record file last-modified-time and tick-count + */ + private static class CacheEntry { + private long lastModified; + private int lastTickCount; + + CacheEntry(long lastModified, int lastTickCount) { + this.lastModified = lastModified; + this.lastTickCount = lastTickCount; + } + + int lastTickCount() { + return lastTickCount; + } + + long lastModified() { + return lastModified; + } + + void update(long lastModified, int tickCount) { + this.lastModified = lastModified; + this.lastTickCount = tickCount; + } + } + + /** + * WatchKey implementation that encapsulates a map of the entries of the + * entries in the directory. Polling the key causes it to re-scan the + * directory and queue keys when entries are added, modified, or deleted. + */ + private class PollingWatchKey extends AbstractWatchKey { + private final Object fileKey; + + // current event set + private Set> events; + + // the result of the periodic task that causes this key to be polled + private ScheduledFuture poller; + + // indicates if the key is valid + private volatile boolean valid; + + // used to detect files that have been deleted + private int tickCount; + + // map of entries in directory + private Map entries; + + PollingWatchKey(Path dir, PollingWatchService watcher, Object fileKey) + throws IOException + { + super(dir, watcher); + this.fileKey = fileKey; + this.valid = true; + this.tickCount = 0; + this.entries = new HashMap(); + + // get the initial entries in the directory + try (DirectoryStream stream = Files.newDirectoryStream(dir)) { + for (Path entry: stream) { + // don't follow links + long lastModified = + Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis(); + entries.put(entry.getFileName(), new CacheEntry(lastModified, tickCount)); + } + } catch (DirectoryIteratorException e) { + throw e.getCause(); + } + } + + Object fileKey() { + return fileKey; + } + + @Override + public boolean isValid() { + return valid; + } + + void invalidate() { + valid = false; + } + + // enables periodic polling + void enable(Set> events, long period) { + synchronized (this) { + // update the events + this.events = events; + + // create the periodic task + Runnable thunk = new Runnable() { public void run() { poll(); }}; + this.poller = scheduledExecutor + .scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS); + } + } + + // disables periodic polling + void disable() { + synchronized (this) { + if (poller != null) + poller.cancel(false); + } + } + + @Override + public void cancel() { + valid = false; + synchronized (map) { + map.remove(fileKey()); + } + disable(); + } + + /** + * Polls the directory to detect for new files, modified files, or + * deleted files. + */ + synchronized void poll() { + if (!valid) { + return; + } + + // update tick + tickCount++; + + // open directory + DirectoryStream stream = null; + try { + stream = Files.newDirectoryStream(watchable()); + } catch (IOException x) { + // directory is no longer accessible so cancel key + cancel(); + signal(); + return; + } + + // iterate over all entries in directory + try { + for (Path entry: stream) { + long lastModified = 0L; + try { + lastModified = + Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis(); + } catch (IOException x) { + // unable to get attributes of entry. If file has just + // been deleted then we'll report it as deleted on the + // next poll + continue; + } + + // lookup cache + CacheEntry e = entries.get(entry.getFileName()); + if (e == null) { + // new file found + entries.put(entry.getFileName(), + new CacheEntry(lastModified, tickCount)); + + // queue ENTRY_CREATE if event enabled + if (events.contains(StandardWatchEventKinds.ENTRY_CREATE)) { + signalEvent(StandardWatchEventKinds.ENTRY_CREATE, entry.getFileName()); + continue; + } else { + // if ENTRY_CREATE is not enabled and ENTRY_MODIFY is + // enabled then queue event to avoid missing out on + // modifications to the file immediately after it is + // created. + if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { + signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName()); + } + } + continue; + } + + // check if file has changed + if (e.lastModified != lastModified) { + if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { + signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, + entry.getFileName()); + } + } + // entry in cache so update poll time + e.update(lastModified, tickCount); + + } + } catch (DirectoryIteratorException e) { + // ignore for now; if the directory is no longer accessible + // then the key will be cancelled on the next poll + } finally { + + // close directory stream + try { + stream.close(); + } catch (IOException x) { + // ignore + } + } + + // iterate over cache to detect entries that have been deleted + Iterator> i = entries.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry mapEntry = i.next(); + CacheEntry entry = mapEntry.getValue(); + if (entry.lastTickCount() != tickCount) { + Path name = mapEntry.getKey(); + // remove from map and queue delete event (if enabled) + i.remove(); + if (events.contains(StandardWatchEventKinds.ENTRY_DELETE)) { + signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name); + } + } + } + } + } +} diff --git a/src/sun/nio/fs/Reflect.java b/src/sun/nio/fs/Reflect.java new file mode 100644 index 00000000..5289d6c2 --- /dev/null +++ b/src/sun/nio/fs/Reflect.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Utility class for reflection. + */ + +class Reflect { + private Reflect() {} + + private static void setAccessible(final AccessibleObject ao) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + ao.setAccessible(true); + return null; + }}); + } + + /** + * Lookup the field of a given class. + */ + static Field lookupField(String className, String fieldName) { + try { + Class cl = Class.forName(className); + Field f = cl.getDeclaredField(fieldName); + setAccessible(f); + return f; + } catch (ClassNotFoundException x) { + throw new AssertionError(x); + } catch (NoSuchFieldException x) { + throw new AssertionError(x); + } + } +} diff --git a/src/sun/nio/fs/Util.java b/src/sun/nio/fs/Util.java new file mode 100644 index 00000000..666c8fde --- /dev/null +++ b/src/sun/nio/fs/Util.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.fs; + +import java.nio.charset.Charset; +import java.nio.file.LinkOption; +import java.security.AccessController; +import java.util.HashSet; +import java.util.Set; + +import sun.security.action.*; + +/** + * Utility methods + */ + +class Util { + private Util() { } + + private static final Charset jnuEncoding = Charset.forName( + AccessController.doPrivileged(new GetPropertyAction("sun.jnu.encoding"))); + + /** + * Returns {@code Charset} corresponding to the sun.jnu.encoding property + */ + static Charset jnuEncoding() { + return jnuEncoding; + } + + /** + * Encodes the given String into a sequence of bytes using the {@code Charset} + * specified by the sun.jnu.encoding property. + */ + static byte[] toBytes(String s) { + return s.getBytes(jnuEncoding); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the + * {@code Charset} specified by the sun.jnu.encoding property. + */ + static String toString(byte[] bytes) { + return new String(bytes, jnuEncoding); + } + + + /** + * Splits a string around the given character. The array returned by this + * method contains each substring that is terminated by the character. Use + * for simple string spilting cases when needing to avoid loading regex. + */ + static String[] split(String s, char c) { + int count = 0; + for (int i=0; i Set newSet(E... elements) { + HashSet set = new HashSet<>(); + for (E e: elements) { + set.add(e); + } + return set; + } + + /** + * Returns a Set containing all the elements of the given Set plus + * the given elements. + */ + @SafeVarargs + static Set newSet(Set other, E... elements) { + HashSet set = new HashSet<>(other); + for (E e: elements) { + set.add(e); + } + return set; + } + + /** + * Returns {@code true} if symbolic links should be followed + */ + static boolean followLinks(LinkOption... options) { + boolean followLinks = true; + for (LinkOption option: options) { + if (option == LinkOption.NOFOLLOW_LINKS) { + followLinks = false; + } else if (option == null) { + throw new NullPointerException(); + } else { + throw new AssertionError("Should not get here"); + } + } + return followLinks; + } +} diff --git a/src/sun/reflect/AccessorGenerator.java b/src/sun/reflect/AccessorGenerator.java new file mode 100644 index 00000000..330f2bf6 --- /dev/null +++ b/src/sun/reflect/AccessorGenerator.java @@ -0,0 +1,717 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Modifier; + +import sun.misc.Unsafe; + +/** Shared functionality for all accessor generators */ + +class AccessorGenerator implements ClassFileConstants { + static final Unsafe unsafe = Unsafe.getUnsafe(); + + // Constants because there's no way to say "short integer constant", + // i.e., "1S" + protected static final short S0 = (short) 0; + protected static final short S1 = (short) 1; + protected static final short S2 = (short) 2; + protected static final short S3 = (short) 3; + protected static final short S4 = (short) 4; + protected static final short S5 = (short) 5; + protected static final short S6 = (short) 6; + + // Instance variables for shared functionality between + // FieldAccessorGenerator and MethodAccessorGenerator + protected ClassFileAssembler asm; + protected int modifiers; + protected short thisClass; + protected short superClass; + protected short targetClass; + // Common constant pool entries to FieldAccessor and MethodAccessor + protected short throwableClass; + protected short classCastClass; + protected short nullPointerClass; + protected short illegalArgumentClass; + protected short invocationTargetClass; + protected short initIdx; + protected short initNameAndTypeIdx; + protected short initStringNameAndTypeIdx; + protected short nullPointerCtorIdx; + protected short illegalArgumentCtorIdx; + protected short illegalArgumentStringCtorIdx; + protected short invocationTargetCtorIdx; + protected short superCtorIdx; + protected short objectClass; + protected short toStringIdx; + protected short codeIdx; + protected short exceptionsIdx; + // Boxing + protected short booleanIdx; + protected short booleanCtorIdx; + protected short booleanUnboxIdx; + protected short byteIdx; + protected short byteCtorIdx; + protected short byteUnboxIdx; + protected short characterIdx; + protected short characterCtorIdx; + protected short characterUnboxIdx; + protected short doubleIdx; + protected short doubleCtorIdx; + protected short doubleUnboxIdx; + protected short floatIdx; + protected short floatCtorIdx; + protected short floatUnboxIdx; + protected short integerIdx; + protected short integerCtorIdx; + protected short integerUnboxIdx; + protected short longIdx; + protected short longCtorIdx; + protected short longUnboxIdx; + protected short shortIdx; + protected short shortCtorIdx; + protected short shortUnboxIdx; + + protected final short NUM_COMMON_CPOOL_ENTRIES = (short) 30; + protected final short NUM_BOXING_CPOOL_ENTRIES = (short) 72; + + // Requires that superClass has been set up + protected void emitCommonConstantPoolEntries() { + // + [UTF-8] "java/lang/Throwable" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/ClassCastException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/NullPointerException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/IllegalArgumentException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/InvocationTargetException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "" + // + [UTF-8] "()V" + // + [CONSTANT_NameAndType_info] for above + // + [CONSTANT_Methodref_info] for NullPointerException's constructor + // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor + // + [UTF-8] "(Ljava/lang/String;)V" + // + [CONSTANT_NameAndType_info] for "(Ljava/lang/String;)V" + // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String + // + [UTF-8] "(Ljava/lang/Throwable;)V" + // + [CONSTANT_NameAndType_info] for "(Ljava/lang/Throwable;)V" + // + [CONSTANT_Methodref_info] for InvocationTargetException's constructor + // + [CONSTANT_Methodref_info] for "super()" + // + [UTF-8] "java/lang/Object" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "toString" + // + [UTF-8] "()Ljava/lang/String;" + // + [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;" + // + [CONSTANT_Methodref_info] for Object's toString method + // + [UTF-8] "Code" + // + [UTF-8] "Exceptions" + asm.emitConstantPoolUTF8("java/lang/Throwable"); + asm.emitConstantPoolClass(asm.cpi()); + throwableClass = asm.cpi(); + asm.emitConstantPoolUTF8("java/lang/ClassCastException"); + asm.emitConstantPoolClass(asm.cpi()); + classCastClass = asm.cpi(); + asm.emitConstantPoolUTF8("java/lang/NullPointerException"); + asm.emitConstantPoolClass(asm.cpi()); + nullPointerClass = asm.cpi(); + asm.emitConstantPoolUTF8("java/lang/IllegalArgumentException"); + asm.emitConstantPoolClass(asm.cpi()); + illegalArgumentClass = asm.cpi(); + asm.emitConstantPoolUTF8("java/lang/reflect/InvocationTargetException"); + asm.emitConstantPoolClass(asm.cpi()); + invocationTargetClass = asm.cpi(); + asm.emitConstantPoolUTF8(""); + initIdx = asm.cpi(); + asm.emitConstantPoolUTF8("()V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + initNameAndTypeIdx = asm.cpi(); + asm.emitConstantPoolMethodref(nullPointerClass, initNameAndTypeIdx); + nullPointerCtorIdx = asm.cpi(); + asm.emitConstantPoolMethodref(illegalArgumentClass, initNameAndTypeIdx); + illegalArgumentCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(Ljava/lang/String;)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + initStringNameAndTypeIdx = asm.cpi(); + asm.emitConstantPoolMethodref(illegalArgumentClass, initStringNameAndTypeIdx); + illegalArgumentStringCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(Ljava/lang/Throwable;)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(invocationTargetClass, asm.cpi()); + invocationTargetCtorIdx = asm.cpi(); + asm.emitConstantPoolMethodref(superClass, initNameAndTypeIdx); + superCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("java/lang/Object"); + asm.emitConstantPoolClass(asm.cpi()); + objectClass = asm.cpi(); + asm.emitConstantPoolUTF8("toString"); + asm.emitConstantPoolUTF8("()Ljava/lang/String;"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(objectClass, asm.cpi()); + toStringIdx = asm.cpi(); + asm.emitConstantPoolUTF8("Code"); + codeIdx = asm.cpi(); + asm.emitConstantPoolUTF8("Exceptions"); + exceptionsIdx = asm.cpi(); + } + + /** Constant pool entries required to be able to box/unbox primitive + types. Note that we don't emit these if we don't need them. */ + protected void emitBoxingContantPoolEntries() { + // * [UTF-8] "java/lang/Boolean" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(Z)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "booleanValue" + // * [UTF-8] "()Z" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Byte" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(B)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "byteValue" + // * [UTF-8] "()B" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Character" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(C)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "charValue" + // * [UTF-8] "()C" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Double" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(D)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "doubleValue" + // * [UTF-8] "()D" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Float" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(F)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "floatValue" + // * [UTF-8] "()F" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Integer" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(I)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "intValue" + // * [UTF-8] "()I" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Long" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(J)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "longValue" + // * [UTF-8] "()J" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Short" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(S)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "shortValue" + // * [UTF-8] "()S" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // Boolean + asm.emitConstantPoolUTF8("java/lang/Boolean"); + asm.emitConstantPoolClass(asm.cpi()); + booleanIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(Z)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + booleanCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("booleanValue"); + asm.emitConstantPoolUTF8("()Z"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + booleanUnboxIdx = asm.cpi(); + + // Byte + asm.emitConstantPoolUTF8("java/lang/Byte"); + asm.emitConstantPoolClass(asm.cpi()); + byteIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(B)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + byteCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("byteValue"); + asm.emitConstantPoolUTF8("()B"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + byteUnboxIdx = asm.cpi(); + + // Character + asm.emitConstantPoolUTF8("java/lang/Character"); + asm.emitConstantPoolClass(asm.cpi()); + characterIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(C)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + characterCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("charValue"); + asm.emitConstantPoolUTF8("()C"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + characterUnboxIdx = asm.cpi(); + + // Double + asm.emitConstantPoolUTF8("java/lang/Double"); + asm.emitConstantPoolClass(asm.cpi()); + doubleIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(D)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + doubleCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("doubleValue"); + asm.emitConstantPoolUTF8("()D"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + doubleUnboxIdx = asm.cpi(); + + // Float + asm.emitConstantPoolUTF8("java/lang/Float"); + asm.emitConstantPoolClass(asm.cpi()); + floatIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(F)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + floatCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("floatValue"); + asm.emitConstantPoolUTF8("()F"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + floatUnboxIdx = asm.cpi(); + + // Integer + asm.emitConstantPoolUTF8("java/lang/Integer"); + asm.emitConstantPoolClass(asm.cpi()); + integerIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(I)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + integerCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("intValue"); + asm.emitConstantPoolUTF8("()I"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + integerUnboxIdx = asm.cpi(); + + // Long + asm.emitConstantPoolUTF8("java/lang/Long"); + asm.emitConstantPoolClass(asm.cpi()); + longIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(J)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + longCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("longValue"); + asm.emitConstantPoolUTF8("()J"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + longUnboxIdx = asm.cpi(); + + // Short + asm.emitConstantPoolUTF8("java/lang/Short"); + asm.emitConstantPoolClass(asm.cpi()); + shortIdx = asm.cpi(); + asm.emitConstantPoolUTF8("(S)V"); + asm.emitConstantPoolNameAndType(initIdx, asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S2), asm.cpi()); + shortCtorIdx = asm.cpi(); + asm.emitConstantPoolUTF8("shortValue"); + asm.emitConstantPoolUTF8("()S"); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + asm.emitConstantPoolMethodref(sub(asm.cpi(), S6), asm.cpi()); + shortUnboxIdx = asm.cpi(); + } + + // Necessary because of Java's annoying promotion rules + protected static short add(short s1, short s2) { + return (short) (s1 + s2); + } + + protected static short sub(short s1, short s2) { + return (short) (s1 - s2); + } + + protected boolean isStatic() { + return Modifier.isStatic(modifiers); + } + + protected boolean isPrivate() { + return Modifier.isPrivate(modifiers); + } + + /** Returns class name in "internal" form (i.e., '/' separators + instead of '.') */ + protected static String getClassName + (Class c, boolean addPrefixAndSuffixForNonPrimitiveTypes) + { + if (c.isPrimitive()) { + if (c == Boolean.TYPE) { + return "Z"; + } else if (c == Byte.TYPE) { + return "B"; + } else if (c == Character.TYPE) { + return "C"; + } else if (c == Double.TYPE) { + return "D"; + } else if (c == Float.TYPE) { + return "F"; + } else if (c == Integer.TYPE) { + return "I"; + } else if (c == Long.TYPE) { + return "J"; + } else if (c == Short.TYPE) { + return "S"; + } else if (c == Void.TYPE) { + return "V"; + } + throw new InternalError("Should have found primitive type"); + } else if (c.isArray()) { + return "[" + getClassName(c.getComponentType(), true); + } else { + if (addPrefixAndSuffixForNonPrimitiveTypes) { + return internalize("L" + c.getName() + ";"); + } else { + return internalize(c.getName()); + } + } + } + + private static String internalize(String className) { + return className.replace('.', '/'); + } + + protected void emitConstructor() { + // Generate code into fresh code buffer + ClassFileAssembler cb = new ClassFileAssembler(); + // 0 incoming arguments + cb.setMaxLocals(1); + cb.opc_aload_0(); + cb.opc_invokespecial(superCtorIdx, 0, 0); + cb.opc_return(); + + // Emit method + emitMethod(initIdx, cb.getMaxLocals(), cb, null, null); + } + + // The descriptor's index in the constant pool must be (1 + + // nameIdx). "numArgs" must indicate ALL arguments, including the + // implicit "this" argument; double and long arguments each count + // as 2 in this count. The code buffer must NOT contain the code + // length. The exception table may be null, but if non-null must + // NOT contain the exception table's length. The checked exception + // indices may be null. + protected void emitMethod(short nameIdx, + int numArgs, + ClassFileAssembler code, + ClassFileAssembler exceptionTable, + short[] checkedExceptionIndices) + { + int codeLen = code.getLength(); + int excLen = 0; + if (exceptionTable != null) { + excLen = exceptionTable.getLength(); + if ((excLen % 8) != 0) { + throw new IllegalArgumentException("Illegal exception table"); + } + } + int attrLen = 12 + codeLen + excLen; + excLen = excLen / 8; // No-op if no exception table + + asm.emitShort(ACC_PUBLIC); + asm.emitShort(nameIdx); + asm.emitShort(add(nameIdx, S1)); + if (checkedExceptionIndices == null) { + // Code attribute only + asm.emitShort(S1); + } else { + // Code and Exceptions attributes + asm.emitShort(S2); + } + // Code attribute + asm.emitShort(codeIdx); + asm.emitInt(attrLen); + asm.emitShort(code.getMaxStack()); + asm.emitShort((short) Math.max(numArgs, code.getMaxLocals())); + asm.emitInt(codeLen); + asm.append(code); + asm.emitShort((short) excLen); + if (exceptionTable != null) { + asm.append(exceptionTable); + } + asm.emitShort(S0); // No additional attributes for Code attribute + if (checkedExceptionIndices != null) { + // Exceptions attribute + asm.emitShort(exceptionsIdx); + asm.emitInt(2 + 2 * checkedExceptionIndices.length); + asm.emitShort((short) checkedExceptionIndices.length); + for (int i = 0; i < checkedExceptionIndices.length; i++) { + asm.emitShort(checkedExceptionIndices[i]); + } + } + } + + protected short indexForPrimitiveType(Class type) { + if (type == Boolean.TYPE) { + return booleanIdx; + } else if (type == Byte.TYPE) { + return byteIdx; + } else if (type == Character.TYPE) { + return characterIdx; + } else if (type == Double.TYPE) { + return doubleIdx; + } else if (type == Float.TYPE) { + return floatIdx; + } else if (type == Integer.TYPE) { + return integerIdx; + } else if (type == Long.TYPE) { + return longIdx; + } else if (type == Short.TYPE) { + return shortIdx; + } + throw new InternalError("Should have found primitive type"); + } + + protected short ctorIndexForPrimitiveType(Class type) { + if (type == Boolean.TYPE) { + return booleanCtorIdx; + } else if (type == Byte.TYPE) { + return byteCtorIdx; + } else if (type == Character.TYPE) { + return characterCtorIdx; + } else if (type == Double.TYPE) { + return doubleCtorIdx; + } else if (type == Float.TYPE) { + return floatCtorIdx; + } else if (type == Integer.TYPE) { + return integerCtorIdx; + } else if (type == Long.TYPE) { + return longCtorIdx; + } else if (type == Short.TYPE) { + return shortCtorIdx; + } + throw new InternalError("Should have found primitive type"); + } + + /** Returns true for widening or identity conversions for primitive + types only */ + protected static boolean canWidenTo(Class type, Class otherType) { + if (!type.isPrimitive()) { + return false; + } + + // Widening conversions (from JVM spec): + // byte to short, int, long, float, or double + // short to int, long, float, or double + // char to int, long, float, or double + // int to long, float, or double + // long to float or double + // float to double + + if (type == Boolean.TYPE) { + if (otherType == Boolean.TYPE) { + return true; + } + } else if (type == Byte.TYPE) { + if ( otherType == Byte.TYPE + || otherType == Short.TYPE + || otherType == Integer.TYPE + || otherType == Long.TYPE + || otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Short.TYPE) { + if ( otherType == Short.TYPE + || otherType == Integer.TYPE + || otherType == Long.TYPE + || otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Character.TYPE) { + if ( otherType == Character.TYPE + || otherType == Integer.TYPE + || otherType == Long.TYPE + || otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Integer.TYPE) { + if ( otherType == Integer.TYPE + || otherType == Long.TYPE + || otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Long.TYPE) { + if ( otherType == Long.TYPE + || otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Float.TYPE) { + if ( otherType == Float.TYPE + || otherType == Double.TYPE) { + return true; + } + } else if (type == Double.TYPE) { + if (otherType == Double.TYPE) { + return true; + } + } + + return false; + } + + /** Emits the widening bytecode for the given primitive conversion + (or none if the identity conversion). Requires that a primitive + conversion exists; i.e., canWidenTo must have already been + called and returned true. */ + protected static void emitWideningBytecodeForPrimitiveConversion + (ClassFileAssembler cb, + Class fromType, + Class toType) + { + // Note that widening conversions for integral types (i.e., "b2s", + // "s2i") are no-ops since values on the Java stack are + // sign-extended. + + // Widening conversions (from JVM spec): + // byte to short, int, long, float, or double + // short to int, long, float, or double + // char to int, long, float, or double + // int to long, float, or double + // long to float or double + // float to double + + if ( fromType == Byte.TYPE + || fromType == Short.TYPE + || fromType == Character.TYPE + || fromType == Integer.TYPE) { + if (toType == Long.TYPE) { + cb.opc_i2l(); + } else if (toType == Float.TYPE) { + cb.opc_i2f(); + } else if (toType == Double.TYPE) { + cb.opc_i2d(); + } + } else if (fromType == Long.TYPE) { + if (toType == Float.TYPE) { + cb.opc_l2f(); + } else if (toType == Double.TYPE) { + cb.opc_l2d(); + } + } else if (fromType == Float.TYPE) { + if (toType == Double.TYPE) { + cb.opc_f2d(); + } + } + + // Otherwise, was identity or no-op conversion. Fall through. + } + + protected short unboxingMethodForPrimitiveType(Class primType) { + if (primType == Boolean.TYPE) { + return booleanUnboxIdx; + } else if (primType == Byte.TYPE) { + return byteUnboxIdx; + } else if (primType == Character.TYPE) { + return characterUnboxIdx; + } else if (primType == Short.TYPE) { + return shortUnboxIdx; + } else if (primType == Integer.TYPE) { + return integerUnboxIdx; + } else if (primType == Long.TYPE) { + return longUnboxIdx; + } else if (primType == Float.TYPE) { + return floatUnboxIdx; + } else if (primType == Double.TYPE) { + return doubleUnboxIdx; + } + throw new InternalError("Illegal primitive type " + primType.getName()); + } + + protected static final Class[] primitiveTypes = new Class[] { + Boolean.TYPE, + Byte.TYPE, + Character.TYPE, + Short.TYPE, + Integer.TYPE, + Long.TYPE, + Float.TYPE, + Double.TYPE + }; + + /** We don't consider "Void" to be a primitive type */ + protected static boolean isPrimitive(Class c) { + return (c.isPrimitive() && c != Void.TYPE); + } + + protected int typeSizeInStackSlots(Class c) { + if (c == Void.TYPE) { + return 0; + } + if (c == Long.TYPE || c == Double.TYPE) { + return 2; + } + return 1; + } + + private ClassFileAssembler illegalArgumentCodeBuffer; + protected ClassFileAssembler illegalArgumentCodeBuffer() { + if (illegalArgumentCodeBuffer == null) { + illegalArgumentCodeBuffer = new ClassFileAssembler(); + illegalArgumentCodeBuffer.opc_new(illegalArgumentClass); + illegalArgumentCodeBuffer.opc_dup(); + illegalArgumentCodeBuffer.opc_invokespecial(illegalArgumentCtorIdx, 0, 0); + illegalArgumentCodeBuffer.opc_athrow(); + } + + return illegalArgumentCodeBuffer; + } +} diff --git a/src/sun/reflect/BootstrapConstructorAccessorImpl.java b/src/sun/reflect/BootstrapConstructorAccessorImpl.java new file mode 100644 index 00000000..b6f4cecf --- /dev/null +++ b/src/sun/reflect/BootstrapConstructorAccessorImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** Uses Unsafe.allocateObject() to instantiate classes; only used for + bootstrapping. */ + +class BootstrapConstructorAccessorImpl extends ConstructorAccessorImpl { + private final Constructor constructor; + + BootstrapConstructorAccessorImpl(Constructor c) { + this.constructor = c; + } + + public Object newInstance(Object[] args) + throws IllegalArgumentException, InvocationTargetException + { + try { + return UnsafeFieldAccessorImpl.unsafe. + allocateInstance(constructor.getDeclaringClass()); + } catch (InstantiationException e) { + throw new InvocationTargetException(e); + } + } +} diff --git a/src/sun/reflect/ByteVector.java b/src/sun/reflect/ByteVector.java new file mode 100644 index 00000000..ad28ee3a --- /dev/null +++ b/src/sun/reflect/ByteVector.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** A growable array of bytes. */ + +interface ByteVector { + public int getLength(); + public byte get(int index); + public void put(int index, byte value); + public void add(byte value); + public void trim(); + public byte[] getData(); +} diff --git a/src/sun/reflect/ByteVectorFactory.java b/src/sun/reflect/ByteVectorFactory.java new file mode 100644 index 00000000..64af68dd --- /dev/null +++ b/src/sun/reflect/ByteVectorFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +class ByteVectorFactory { + static ByteVector create() { + return new ByteVectorImpl(); + } + + static ByteVector create(int sz) { + return new ByteVectorImpl(sz); + } +} diff --git a/src/sun/reflect/ByteVectorImpl.java b/src/sun/reflect/ByteVectorImpl.java new file mode 100644 index 00000000..efa1c8ab --- /dev/null +++ b/src/sun/reflect/ByteVectorImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +class ByteVectorImpl implements ByteVector { + private byte[] data; + private int pos; + + public ByteVectorImpl() { + this(100); + } + + public ByteVectorImpl(int sz) { + data = new byte[sz]; + pos = -1; + } + + public int getLength() { + return pos + 1; + } + + public byte get(int index) { + if (index >= data.length) { + resize(index); + pos = index; + } + return data[index]; + } + + public void put(int index, byte value) { + if (index >= data.length) { + resize(index); + pos = index; + } + data[index] = value; + } + + public void add(byte value) { + if (++pos >= data.length) { + resize(pos); + } + data[pos] = value; + } + + public void trim() { + if (pos != data.length - 1) { + byte[] newData = new byte[pos + 1]; + System.arraycopy(data, 0, newData, 0, pos + 1); + data = newData; + } + } + + public byte[] getData() { + return data; + } + + private void resize(int minSize) { + if (minSize <= 2 * data.length) { + minSize = 2 * data.length; + } + byte[] newData = new byte[minSize]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + } +} diff --git a/src/sun/reflect/CallerSensitive.java b/src/sun/reflect/CallerSensitive.java new file mode 100644 index 00000000..7935abd9 --- /dev/null +++ b/src/sun/reflect/CallerSensitive.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A method annotated @CallerSensitive is sensitive to its calling class, + * via {@link Reflection#getCallerClass Reflection.getCallerClass}, + * or via some equivalent. + * + * @author John R. Rose + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({METHOD}) +public @interface CallerSensitive { +} diff --git a/src/sun/reflect/ClassDefiner.java b/src/sun/reflect/ClassDefiner.java new file mode 100644 index 00000000..f50f20c3 --- /dev/null +++ b/src/sun/reflect/ClassDefiner.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import sun.misc.Unsafe; + +/** Utility class which assists in calling Unsafe.defineClass() by + creating a new class loader which delegates to the one needed in + order for proper resolution of the given bytecodes to occur. */ + +class ClassDefiner { + static final Unsafe unsafe = Unsafe.getUnsafe(); + + /**

    We define generated code into a new class loader which + delegates to the defining loader of the target class. It is + necessary for the VM to be able to resolve references to the + target class from the generated bytecodes, which could not occur + if the generated code was loaded into the bootstrap class + loader.

    + +

    There are two primary reasons for creating a new loader + instead of defining these bytecodes directly into the defining + loader of the target class: first, it avoids any possible + security risk of having these bytecodes in the same loader. + Second, it allows the generated bytecodes to be unloaded earlier + than would otherwise be possible, decreasing run-time + footprint.

    + */ + static Class defineClass(String name, byte[] bytes, int off, int len, + final ClassLoader parentClassLoader) + { + ClassLoader newLoader = AccessController.doPrivileged( + new PrivilegedAction() { + public ClassLoader run() { + return new DelegatingClassLoader(parentClassLoader); + } + }); + return unsafe.defineClass(name, bytes, off, len, newLoader, null); + } +} + + +// NOTE: this class's name and presence are known to the virtual +// machine as of the fix for 4474172. +class DelegatingClassLoader extends ClassLoader { + DelegatingClassLoader(ClassLoader parent) { + super(parent); + } +} diff --git a/src/sun/reflect/ClassFileAssembler.java b/src/sun/reflect/ClassFileAssembler.java new file mode 100644 index 00000000..7f6ceccf --- /dev/null +++ b/src/sun/reflect/ClassFileAssembler.java @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2001, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +class ClassFileAssembler implements ClassFileConstants { + private ByteVector vec; + private short cpIdx = 0; + + public ClassFileAssembler() { + this(ByteVectorFactory.create()); + } + + public ClassFileAssembler(ByteVector vec) { + this.vec = vec; + } + + public ByteVector getData() { + return vec; + } + + /** Length in bytes */ + public short getLength() { + return (short) vec.getLength(); + } + + public void emitMagicAndVersion() { + emitInt(0xCAFEBABE); + emitShort((short) 0); + emitShort((short) 49); + } + + public void emitInt(int val) { + emitByte((byte) (val >> 24)); + emitByte((byte) ((val >> 16) & 0xFF)); + emitByte((byte) ((val >> 8) & 0xFF)); + emitByte((byte) (val & 0xFF)); + } + + public void emitShort(short val) { + emitByte((byte) ((val >> 8) & 0xFF)); + emitByte((byte) (val & 0xFF)); + } + + // Support for labels; package-private + void emitShort(short bci, short val) { + vec.put(bci, (byte) ((val >> 8) & 0xFF)); + vec.put(bci + 1, (byte) (val & 0xFF)); + } + + public void emitByte(byte val) { + vec.add(val); + } + + public void append(ClassFileAssembler asm) { + append(asm.vec); + } + + public void append(ByteVector vec) { + for (int i = 0; i < vec.getLength(); i++) { + emitByte(vec.get(i)); + } + } + + /** Keeps track of the current (one-based) constant pool index; + incremented after emitting one of the following constant pool + entries. Can fetch the current constant pool index for use in + later entries. Index points at the last valid constant pool + entry; initially invalid. It is illegal to fetch the constant + pool index before emitting at least one constant pool entry. */ + public short cpi() { + if (cpIdx == 0) { + throw new RuntimeException("Illegal use of ClassFileAssembler"); + } + return cpIdx; + } + + public void emitConstantPoolUTF8(String str) { + // NOTE: can not use str.getBytes("UTF-8") here because of + // bootstrapping issues with the character set converters. + byte[] bytes = UTF8.encode(str); + emitByte(CONSTANT_Utf8); + emitShort((short) bytes.length); + for (int i = 0; i < bytes.length; i++) { + emitByte(bytes[i]); + } + cpIdx++; + } + + public void emitConstantPoolClass(short index) { + emitByte(CONSTANT_Class); + emitShort(index); + cpIdx++; + } + + public void emitConstantPoolNameAndType(short nameIndex, short typeIndex) { + emitByte(CONSTANT_NameAndType); + emitShort(nameIndex); + emitShort(typeIndex); + cpIdx++; + } + + public void emitConstantPoolFieldref + (short classIndex, short nameAndTypeIndex) + { + emitByte(CONSTANT_Fieldref); + emitShort(classIndex); + emitShort(nameAndTypeIndex); + cpIdx++; + } + + public void emitConstantPoolMethodref + (short classIndex, short nameAndTypeIndex) + { + emitByte(CONSTANT_Methodref); + emitShort(classIndex); + emitShort(nameAndTypeIndex); + cpIdx++; + } + + public void emitConstantPoolInterfaceMethodref + (short classIndex, short nameAndTypeIndex) + { + emitByte(CONSTANT_InterfaceMethodref); + emitShort(classIndex); + emitShort(nameAndTypeIndex); + cpIdx++; + } + + public void emitConstantPoolString(short utf8Index) { + emitByte(CONSTANT_String); + emitShort(utf8Index); + cpIdx++; + } + + //---------------------------------------------------------------------- + // Opcodes. Keeps track of maximum stack and locals. Make a new + // assembler for each piece of assembled code, then append the + // result to the previous assembler's class file. + // + + private int stack = 0; + private int maxStack = 0; + private int maxLocals = 0; + + private void incStack() { + setStack(stack + 1); + } + + private void decStack() { + --stack; + } + + public short getMaxStack() { + return (short) maxStack; + } + + public short getMaxLocals() { + return (short) maxLocals; + } + + /** It's necessary to be able to specify the number of arguments at + the beginning of the method (which translates to the initial + value of max locals) */ + public void setMaxLocals(int maxLocals) { + this.maxLocals = maxLocals; + } + + /** Needed to do flow control. Returns current stack depth. */ + public int getStack() { + return stack; + } + + /** Needed to do flow control. */ + public void setStack(int value) { + stack = value; + if (stack > maxStack) { + maxStack = stack; + } + } + + /////////////// + // Constants // + /////////////// + + public void opc_aconst_null() { + emitByte(opc_aconst_null); + incStack(); + } + + public void opc_sipush(short constant) { + emitByte(opc_sipush); + emitShort(constant); + incStack(); + } + + public void opc_ldc(byte cpIdx) { + emitByte(opc_ldc); + emitByte(cpIdx); + incStack(); + } + + ///////////////////////////////////// + // Local variable loads and stores // + ///////////////////////////////////// + + public void opc_iload_0() { + emitByte(opc_iload_0); + if (maxLocals < 1) maxLocals = 1; + incStack(); + } + + public void opc_iload_1() { + emitByte(opc_iload_1); + if (maxLocals < 2) maxLocals = 2; + incStack(); + } + + public void opc_iload_2() { + emitByte(opc_iload_2); + if (maxLocals < 3) maxLocals = 3; + incStack(); + } + + public void opc_iload_3() { + emitByte(opc_iload_3); + if (maxLocals < 4) maxLocals = 4; + incStack(); + } + + public void opc_lload_0() { + emitByte(opc_lload_0); + if (maxLocals < 2) maxLocals = 2; + incStack(); + incStack(); + } + + public void opc_lload_1() { + emitByte(opc_lload_1); + if (maxLocals < 3) maxLocals = 3; + incStack(); + incStack(); + } + + public void opc_lload_2() { + emitByte(opc_lload_2); + if (maxLocals < 4) maxLocals = 4; + incStack(); + incStack(); + } + + public void opc_lload_3() { + emitByte(opc_lload_3); + if (maxLocals < 5) maxLocals = 5; + incStack(); + incStack(); + } + + public void opc_fload_0() { + emitByte(opc_fload_0); + if (maxLocals < 1) maxLocals = 1; + incStack(); + } + + public void opc_fload_1() { + emitByte(opc_fload_1); + if (maxLocals < 2) maxLocals = 2; + incStack(); + } + + public void opc_fload_2() { + emitByte(opc_fload_2); + if (maxLocals < 3) maxLocals = 3; + incStack(); + } + + public void opc_fload_3() { + emitByte(opc_fload_3); + if (maxLocals < 4) maxLocals = 4; + incStack(); + } + + public void opc_dload_0() { + emitByte(opc_dload_0); + if (maxLocals < 2) maxLocals = 2; + incStack(); + incStack(); + } + + public void opc_dload_1() { + emitByte(opc_dload_1); + if (maxLocals < 3) maxLocals = 3; + incStack(); + incStack(); + } + + public void opc_dload_2() { + emitByte(opc_dload_2); + if (maxLocals < 4) maxLocals = 4; + incStack(); + incStack(); + } + + public void opc_dload_3() { + emitByte(opc_dload_3); + if (maxLocals < 5) maxLocals = 5; + incStack(); + incStack(); + } + + public void opc_aload_0() { + emitByte(opc_aload_0); + if (maxLocals < 1) maxLocals = 1; + incStack(); + } + + public void opc_aload_1() { + emitByte(opc_aload_1); + if (maxLocals < 2) maxLocals = 2; + incStack(); + } + + public void opc_aload_2() { + emitByte(opc_aload_2); + if (maxLocals < 3) maxLocals = 3; + incStack(); + } + + public void opc_aload_3() { + emitByte(opc_aload_3); + if (maxLocals < 4) maxLocals = 4; + incStack(); + } + + public void opc_aaload() { + emitByte(opc_aaload); + decStack(); + } + + public void opc_astore_0() { + emitByte(opc_astore_0); + if (maxLocals < 1) maxLocals = 1; + decStack(); + } + + public void opc_astore_1() { + emitByte(opc_astore_1); + if (maxLocals < 2) maxLocals = 2; + decStack(); + } + + public void opc_astore_2() { + emitByte(opc_astore_2); + if (maxLocals < 3) maxLocals = 3; + decStack(); + } + + public void opc_astore_3() { + emitByte(opc_astore_3); + if (maxLocals < 4) maxLocals = 4; + decStack(); + } + + //////////////////////// + // Stack manipulation // + //////////////////////// + + public void opc_pop() { + emitByte(opc_pop); + decStack(); + } + + public void opc_dup() { + emitByte(opc_dup); + incStack(); + } + + public void opc_dup_x1() { + emitByte(opc_dup_x1); + incStack(); + } + + public void opc_swap() { + emitByte(opc_swap); + } + + /////////////////////////////// + // Widening conversions only // + /////////////////////////////// + + public void opc_i2l() { + emitByte(opc_i2l); + } + + public void opc_i2f() { + emitByte(opc_i2f); + } + + public void opc_i2d() { + emitByte(opc_i2d); + } + + public void opc_l2f() { + emitByte(opc_l2f); + } + + public void opc_l2d() { + emitByte(opc_l2d); + } + + public void opc_f2d() { + emitByte(opc_f2d); + } + + ////////////////// + // Control flow // + ////////////////// + + public void opc_ifeq(short bciOffset) { + emitByte(opc_ifeq); + emitShort(bciOffset); + decStack(); + } + + /** Control flow with forward-reference BCI. Stack assumes + straight-through control flow. */ + public void opc_ifeq(Label l) { + short instrBCI = getLength(); + emitByte(opc_ifeq); + l.add(this, instrBCI, getLength(), getStack() - 1); + emitShort((short) -1); // Must be patched later + } + + public void opc_if_icmpeq(short bciOffset) { + emitByte(opc_if_icmpeq); + emitShort(bciOffset); + setStack(getStack() - 2); + } + + /** Control flow with forward-reference BCI. Stack assumes straight + control flow. */ + public void opc_if_icmpeq(Label l) { + short instrBCI = getLength(); + emitByte(opc_if_icmpeq); + l.add(this, instrBCI, getLength(), getStack() - 2); + emitShort((short) -1); // Must be patched later + } + + public void opc_goto(short bciOffset) { + emitByte(opc_goto); + emitShort(bciOffset); + } + + /** Control flow with forward-reference BCI. Stack assumes straight + control flow. */ + public void opc_goto(Label l) { + short instrBCI = getLength(); + emitByte(opc_goto); + l.add(this, instrBCI, getLength(), getStack()); + emitShort((short) -1); // Must be patched later + } + + public void opc_ifnull(short bciOffset) { + emitByte(opc_ifnull); + emitShort(bciOffset); + decStack(); + } + + /** Control flow with forward-reference BCI. Stack assumes straight + control flow. */ + public void opc_ifnull(Label l) { + short instrBCI = getLength(); + emitByte(opc_ifnull); + l.add(this, instrBCI, getLength(), getStack() - 1); + emitShort((short) -1); // Must be patched later + decStack(); + } + + public void opc_ifnonnull(short bciOffset) { + emitByte(opc_ifnonnull); + emitShort(bciOffset); + decStack(); + } + + /** Control flow with forward-reference BCI. Stack assumes straight + control flow. */ + public void opc_ifnonnull(Label l) { + short instrBCI = getLength(); + emitByte(opc_ifnonnull); + l.add(this, instrBCI, getLength(), getStack() - 1); + emitShort((short) -1); // Must be patched later + decStack(); + } + + ///////////////////////// + // Return instructions // + ///////////////////////// + + public void opc_ireturn() { + emitByte(opc_ireturn); + setStack(0); + } + + public void opc_lreturn() { + emitByte(opc_lreturn); + setStack(0); + } + + public void opc_freturn() { + emitByte(opc_freturn); + setStack(0); + } + + public void opc_dreturn() { + emitByte(opc_dreturn); + setStack(0); + } + + public void opc_areturn() { + emitByte(opc_areturn); + setStack(0); + } + + public void opc_return() { + emitByte(opc_return); + setStack(0); + } + + ////////////////////// + // Field operations // + ////////////////////// + + public void opc_getstatic(short fieldIndex, int fieldSizeInStackSlots) { + emitByte(opc_getstatic); + emitShort(fieldIndex); + setStack(getStack() + fieldSizeInStackSlots); + } + + public void opc_putstatic(short fieldIndex, int fieldSizeInStackSlots) { + emitByte(opc_putstatic); + emitShort(fieldIndex); + setStack(getStack() - fieldSizeInStackSlots); + } + + public void opc_getfield(short fieldIndex, int fieldSizeInStackSlots) { + emitByte(opc_getfield); + emitShort(fieldIndex); + setStack(getStack() + fieldSizeInStackSlots - 1); + } + + public void opc_putfield(short fieldIndex, int fieldSizeInStackSlots) { + emitByte(opc_putfield); + emitShort(fieldIndex); + setStack(getStack() - fieldSizeInStackSlots - 1); + } + + //////////////////////// + // Method invocations // + //////////////////////// + + /** Long and double arguments and return types count as 2 arguments; + other values count as 1. */ + public void opc_invokevirtual(short methodIndex, + int numArgs, + int numReturnValues) + { + emitByte(opc_invokevirtual); + emitShort(methodIndex); + setStack(getStack() - numArgs - 1 + numReturnValues); + } + + /** Long and double arguments and return types count as 2 arguments; + other values count as 1. */ + public void opc_invokespecial(short methodIndex, + int numArgs, + int numReturnValues) + { + emitByte(opc_invokespecial); + emitShort(methodIndex); + setStack(getStack() - numArgs - 1 + numReturnValues); + } + + /** Long and double arguments and return types count as 2 arguments; + other values count as 1. */ + public void opc_invokestatic(short methodIndex, + int numArgs, + int numReturnValues) + { + emitByte(opc_invokestatic); + emitShort(methodIndex); + setStack(getStack() - numArgs + numReturnValues); + } + + /** Long and double arguments and return types count as 2 arguments; + other values count as 1. */ + public void opc_invokeinterface(short methodIndex, + int numArgs, + byte count, + int numReturnValues) + { + emitByte(opc_invokeinterface); + emitShort(methodIndex); + emitByte(count); + emitByte((byte) 0); + setStack(getStack() - numArgs - 1 + numReturnValues); + } + + ////////////////// + // Array length // + ////////////////// + + public void opc_arraylength() { + emitByte(opc_arraylength); + } + + ///////// + // New // + ///////// + + public void opc_new(short classIndex) { + emitByte(opc_new); + emitShort(classIndex); + incStack(); + } + + //////////// + // Athrow // + //////////// + + public void opc_athrow() { + emitByte(opc_athrow); + setStack(1); + } + + ////////////////////////////// + // Checkcast and instanceof // + ////////////////////////////// + + /** Assumes the checkcast succeeds */ + public void opc_checkcast(short classIndex) { + emitByte(opc_checkcast); + emitShort(classIndex); + } + + public void opc_instanceof(short classIndex) { + emitByte(opc_instanceof); + emitShort(classIndex); + } +} diff --git a/src/sun/reflect/ClassFileConstants.java b/src/sun/reflect/ClassFileConstants.java new file mode 100644 index 00000000..0bf391a0 --- /dev/null +++ b/src/sun/reflect/ClassFileConstants.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** Minimal set of class file constants for assembly of field and + method accessors. */ + +interface ClassFileConstants { + // Constants + public static final byte opc_aconst_null = (byte) 0x1; + public static final byte opc_sipush = (byte) 0x11; + public static final byte opc_ldc = (byte) 0x12; + + // Local variable loads and stores + public static final byte opc_iload_0 = (byte) 0x1a; + public static final byte opc_iload_1 = (byte) 0x1b; + public static final byte opc_iload_2 = (byte) 0x1c; + public static final byte opc_iload_3 = (byte) 0x1d; + public static final byte opc_lload_0 = (byte) 0x1e; + public static final byte opc_lload_1 = (byte) 0x1f; + public static final byte opc_lload_2 = (byte) 0x20; + public static final byte opc_lload_3 = (byte) 0x21; + public static final byte opc_fload_0 = (byte) 0x22; + public static final byte opc_fload_1 = (byte) 0x23; + public static final byte opc_fload_2 = (byte) 0x24; + public static final byte opc_fload_3 = (byte) 0x25; + public static final byte opc_dload_0 = (byte) 0x26; + public static final byte opc_dload_1 = (byte) 0x27; + public static final byte opc_dload_2 = (byte) 0x28; + public static final byte opc_dload_3 = (byte) 0x29; + public static final byte opc_aload_0 = (byte) 0x2a; + public static final byte opc_aload_1 = (byte) 0x2b; + public static final byte opc_aload_2 = (byte) 0x2c; + public static final byte opc_aload_3 = (byte) 0x2d; + public static final byte opc_aaload = (byte) 0x32; + public static final byte opc_astore_0 = (byte) 0x4b; + public static final byte opc_astore_1 = (byte) 0x4c; + public static final byte opc_astore_2 = (byte) 0x4d; + public static final byte opc_astore_3 = (byte) 0x4e; + + // Stack manipulation + public static final byte opc_pop = (byte) 0x57; + public static final byte opc_dup = (byte) 0x59; + public static final byte opc_dup_x1 = (byte) 0x5a; + public static final byte opc_swap = (byte) 0x5f; + + // Conversions + public static final byte opc_i2l = (byte) 0x85; + public static final byte opc_i2f = (byte) 0x86; + public static final byte opc_i2d = (byte) 0x87; + public static final byte opc_l2i = (byte) 0x88; + public static final byte opc_l2f = (byte) 0x89; + public static final byte opc_l2d = (byte) 0x8a; + public static final byte opc_f2i = (byte) 0x8b; + public static final byte opc_f2l = (byte) 0x8c; + public static final byte opc_f2d = (byte) 0x8d; + public static final byte opc_d2i = (byte) 0x8e; + public static final byte opc_d2l = (byte) 0x8f; + public static final byte opc_d2f = (byte) 0x90; + public static final byte opc_i2b = (byte) 0x91; + public static final byte opc_i2c = (byte) 0x92; + public static final byte opc_i2s = (byte) 0x93; + + // Control flow + public static final byte opc_ifeq = (byte) 0x99; + public static final byte opc_if_icmpeq = (byte) 0x9f; + public static final byte opc_goto = (byte) 0xa7; + + // Return instructions + public static final byte opc_ireturn = (byte) 0xac; + public static final byte opc_lreturn = (byte) 0xad; + public static final byte opc_freturn = (byte) 0xae; + public static final byte opc_dreturn = (byte) 0xaf; + public static final byte opc_areturn = (byte) 0xb0; + public static final byte opc_return = (byte) 0xb1; + + // Field operations + public static final byte opc_getstatic = (byte) 0xb2; + public static final byte opc_putstatic = (byte) 0xb3; + public static final byte opc_getfield = (byte) 0xb4; + public static final byte opc_putfield = (byte) 0xb5; + + // Method invocations + public static final byte opc_invokevirtual = (byte) 0xb6; + public static final byte opc_invokespecial = (byte) 0xb7; + public static final byte opc_invokestatic = (byte) 0xb8; + public static final byte opc_invokeinterface = (byte) 0xb9; + + // Array length + public static final byte opc_arraylength = (byte) 0xbe; + + // New + public static final byte opc_new = (byte) 0xbb; + + // Athrow + public static final byte opc_athrow = (byte) 0xbf; + + // Checkcast and instanceof + public static final byte opc_checkcast = (byte) 0xc0; + public static final byte opc_instanceof = (byte) 0xc1; + + // Ifnull and ifnonnull + public static final byte opc_ifnull = (byte) 0xc6; + public static final byte opc_ifnonnull = (byte) 0xc7; + + // Constant pool tags + public static final byte CONSTANT_Class = (byte) 7; + public static final byte CONSTANT_Fieldref = (byte) 9; + public static final byte CONSTANT_Methodref = (byte) 10; + public static final byte CONSTANT_InterfaceMethodref = (byte) 11; + public static final byte CONSTANT_NameAndType = (byte) 12; + public static final byte CONSTANT_String = (byte) 8; + public static final byte CONSTANT_Utf8 = (byte) 1; + + // Access flags + public static final short ACC_PUBLIC = (short) 0x0001; +} diff --git a/src/sun/reflect/ConstantPool.java b/src/sun/reflect/ConstantPool.java new file mode 100644 index 00000000..bf33bc5b --- /dev/null +++ b/src/sun/reflect/ConstantPool.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Member; + +/** Provides reflective access to the constant pools of classes. + Currently this is needed to provide reflective access to annotations + but may be used by other internal subsystems in the future. */ + +public class ConstantPool { + // Number of entries in this constant pool (= maximum valid constant pool index) + public int getSize() { return getSize0 (constantPoolOop); } + public Class getClassAt (int index) { return getClassAt0 (constantPoolOop, index); } + public Class getClassAtIfLoaded (int index) { return getClassAtIfLoaded0 (constantPoolOop, index); } + // Returns either a Method or Constructor. + // Static initializers are returned as Method objects. + public Member getMethodAt (int index) { return getMethodAt0 (constantPoolOop, index); } + public Member getMethodAtIfLoaded(int index) { return getMethodAtIfLoaded0(constantPoolOop, index); } + public Field getFieldAt (int index) { return getFieldAt0 (constantPoolOop, index); } + public Field getFieldAtIfLoaded (int index) { return getFieldAtIfLoaded0 (constantPoolOop, index); } + // Fetches the class name, member (field, method or interface + // method) name, and type descriptor as an array of three Strings + public String[] getMemberRefInfoAt (int index) { return getMemberRefInfoAt0 (constantPoolOop, index); } + public int getIntAt (int index) { return getIntAt0 (constantPoolOop, index); } + public long getLongAt (int index) { return getLongAt0 (constantPoolOop, index); } + public float getFloatAt (int index) { return getFloatAt0 (constantPoolOop, index); } + public double getDoubleAt (int index) { return getDoubleAt0 (constantPoolOop, index); } + public String getStringAt (int index) { return getStringAt0 (constantPoolOop, index); } + public String getUTF8At (int index) { return getUTF8At0 (constantPoolOop, index); } + + //--------------------------------------------------------------------------- + // Internals only below this point + // + + static { + Reflection.registerFieldsToFilter(ConstantPool.class, new String[] { "constantPoolOop" }); + } + + // HotSpot-internal constant pool object (set by the VM, name known to the VM) + private Object constantPoolOop; + + private native int getSize0 (Object constantPoolOop); + private native Class getClassAt0 (Object constantPoolOop, int index); + private native Class getClassAtIfLoaded0 (Object constantPoolOop, int index); + private native Member getMethodAt0 (Object constantPoolOop, int index); + private native Member getMethodAtIfLoaded0(Object constantPoolOop, int index); + private native Field getFieldAt0 (Object constantPoolOop, int index); + private native Field getFieldAtIfLoaded0 (Object constantPoolOop, int index); + private native String[] getMemberRefInfoAt0 (Object constantPoolOop, int index); + private native int getIntAt0 (Object constantPoolOop, int index); + private native long getLongAt0 (Object constantPoolOop, int index); + private native float getFloatAt0 (Object constantPoolOop, int index); + private native double getDoubleAt0 (Object constantPoolOop, int index); + private native String getStringAt0 (Object constantPoolOop, int index); + private native String getUTF8At0 (Object constantPoolOop, int index); +} diff --git a/src/sun/reflect/ConstructorAccessor.java b/src/sun/reflect/ConstructorAccessor.java new file mode 100644 index 00000000..7aac26f8 --- /dev/null +++ b/src/sun/reflect/ConstructorAccessor.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** This interface provides the declaration for + java.lang.reflect.Constructor.invoke(). Each Constructor object is + configured with a (possibly dynamically-generated) class which + implements this interface. */ + +public interface ConstructorAccessor { + /** Matches specification in {@link java.lang.reflect.Constructor} */ + public Object newInstance(Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException; +} diff --git a/src/sun/reflect/ConstructorAccessorImpl.java b/src/sun/reflect/ConstructorAccessorImpl.java new file mode 100644 index 00000000..9b098312 --- /dev/null +++ b/src/sun/reflect/ConstructorAccessorImpl.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** Package-private implementation of the ConstructorAccessor + interface which has access to all classes and all fields, + regardless of language restrictions. See MagicAccessorImpl. */ + +abstract class ConstructorAccessorImpl extends MagicAccessorImpl + implements ConstructorAccessor { + /** Matches specification in {@link java.lang.reflect.Constructor} */ + public abstract Object newInstance(Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException; +} diff --git a/src/sun/reflect/DelegatingConstructorAccessorImpl.java b/src/sun/reflect/DelegatingConstructorAccessorImpl.java new file mode 100644 index 00000000..6ee4d5f4 --- /dev/null +++ b/src/sun/reflect/DelegatingConstructorAccessorImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** Delegates its invocation to another ConstructorAccessorImpl and can + change its delegate at run time. */ + +class DelegatingConstructorAccessorImpl extends ConstructorAccessorImpl { + private ConstructorAccessorImpl delegate; + + DelegatingConstructorAccessorImpl(ConstructorAccessorImpl delegate) { + setDelegate(delegate); + } + + public Object newInstance(Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException + { + return delegate.newInstance(args); + } + + void setDelegate(ConstructorAccessorImpl delegate) { + this.delegate = delegate; + } +} diff --git a/src/sun/reflect/DelegatingMethodAccessorImpl.java b/src/sun/reflect/DelegatingMethodAccessorImpl.java new file mode 100644 index 00000000..115d9aa1 --- /dev/null +++ b/src/sun/reflect/DelegatingMethodAccessorImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** Delegates its invocation to another MethodAccessorImpl and can + change its delegate at run time. */ + +class DelegatingMethodAccessorImpl extends MethodAccessorImpl { + private MethodAccessorImpl delegate; + + DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) { + setDelegate(delegate); + } + + public Object invoke(Object obj, Object[] args) + throws IllegalArgumentException, InvocationTargetException + { + return delegate.invoke(obj, args); + } + + void setDelegate(MethodAccessorImpl delegate) { + this.delegate = delegate; + } +} diff --git a/src/sun/reflect/FieldAccessor.java b/src/sun/reflect/FieldAccessor.java new file mode 100644 index 00000000..7b974f6c --- /dev/null +++ b/src/sun/reflect/FieldAccessor.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** This interface provides the declarations for the accessor methods + of java.lang.reflect.Field. Each Field object is configured with a + (possibly dynamically-generated) class which implements this + interface. */ + +public interface FieldAccessor { + /** Matches specification in {@link java.lang.reflect.Field} */ + public Object get(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public boolean getBoolean(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public byte getByte(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public char getChar(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public short getShort(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public int getInt(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public long getLong(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public float getFloat(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public double getDouble(Object obj) throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException; +} diff --git a/src/sun/reflect/FieldAccessorImpl.java b/src/sun/reflect/FieldAccessorImpl.java new file mode 100644 index 00000000..cbfa3c1e --- /dev/null +++ b/src/sun/reflect/FieldAccessorImpl.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** Package-private implementation of the FieldAccessor interface + which has access to all classes and all fields, regardless of + language restrictions. See MagicAccessorImpl. */ + +abstract class FieldAccessorImpl extends MagicAccessorImpl + implements FieldAccessor { + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract Object get(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract boolean getBoolean(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract byte getByte(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract char getChar(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract short getShort(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract int getInt(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract long getLong(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract float getFloat(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract double getDouble(Object obj) + throws IllegalArgumentException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException; + + /** Matches specification in {@link java.lang.reflect.Field} */ + public abstract void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException; +} diff --git a/src/sun/reflect/FieldInfo.java b/src/sun/reflect/FieldInfo.java new file mode 100644 index 00000000..120f34d1 --- /dev/null +++ b/src/sun/reflect/FieldInfo.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Modifier; + +/** NOTE: obsolete as of JDK 1.4 B75 and should be removed from the + workspace (FIXME) */ + +public class FieldInfo { + // Set by the VM directly. Do not move these fields around or add + // others before (or after) them without also modifying the VM's code. + private String name; + private String signature; + private int modifiers; + // This is compatible with the old reflection implementation's + // "slot" value to allow sun.misc.Unsafe to work + private int slot; + + // Not really necessary to provide a constructor since the VM + // creates these directly + FieldInfo() { + } + + public String name() { + return name; + } + + /** This is in "external" format, i.e. having '.' as separator + rather than '/' */ + public String signature() { + return signature; + } + + public int modifiers() { + return modifiers; + } + + public int slot() { + return slot; + } + + /** Convenience routine */ + public boolean isPublic() { + return (Modifier.isPublic(modifiers())); + } +} diff --git a/src/sun/reflect/InstantiationExceptionConstructorAccessorImpl.java b/src/sun/reflect/InstantiationExceptionConstructorAccessorImpl.java new file mode 100644 index 00000000..d251d751 --- /dev/null +++ b/src/sun/reflect/InstantiationExceptionConstructorAccessorImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** Throws an InstantiationException with given error message upon + newInstance() call */ + +class InstantiationExceptionConstructorAccessorImpl + extends ConstructorAccessorImpl { + private final String message; + + InstantiationExceptionConstructorAccessorImpl(String message) { + this.message = message; + } + + public Object newInstance(Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException + { + if (message == null) { + throw new InstantiationException(); + } + throw new InstantiationException(message); + } +} diff --git a/src/sun/reflect/Label.java b/src/sun/reflect/Label.java new file mode 100644 index 00000000..24167ffa --- /dev/null +++ b/src/sun/reflect/Label.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.util.ArrayList; +import java.util.List; + +/** Allows forward references in bytecode streams emitted by + ClassFileAssembler. Assumes that the start of the method body is + the first byte in the assembler's buffer. May be used at more than + one branch site. */ + +class Label { + static class PatchInfo { + PatchInfo(ClassFileAssembler asm, + short instrBCI, + short patchBCI, + int stackDepth) + { + this.asm = asm; + this.instrBCI = instrBCI; + this.patchBCI = patchBCI; + this.stackDepth = stackDepth; + } + // This won't work for more than one assembler anyway, so this is + // unnecessary + final ClassFileAssembler asm; + final short instrBCI; + final short patchBCI; + final int stackDepth; + } + private List patches = new ArrayList<>(); + + public Label() { + } + + void add(ClassFileAssembler asm, + short instrBCI, + short patchBCI, + int stackDepth) + { + patches.add(new PatchInfo(asm, instrBCI, patchBCI, stackDepth)); + } + + public void bind() { + for (PatchInfo patch : patches){ + short curBCI = patch.asm.getLength(); + short offset = (short) (curBCI - patch.instrBCI); + patch.asm.emitShort(patch.patchBCI, offset); + patch.asm.setStack(patch.stackDepth); + } + } +} diff --git a/src/sun/reflect/LangReflectAccess.java b/src/sun/reflect/LangReflectAccess.java new file mode 100644 index 00000000..7eef3a90 --- /dev/null +++ b/src/sun/reflect/LangReflectAccess.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** An interface which gives privileged packages Java-level access to + internals of java.lang.reflect. */ + +public interface LangReflectAccess { + /** Creates a new java.lang.reflect.Field. Access checks as per + java.lang.reflect.AccessibleObject are not overridden. */ + public Field newField(Class declaringClass, + String name, + Class type, + int modifiers, + int slot, + String signature, + byte[] annotations); + + /** Creates a new java.lang.reflect.Method. Access checks as per + java.lang.reflect.AccessibleObject are not overridden. */ + public Method newMethod(Class declaringClass, + String name, + Class[] parameterTypes, + Class returnType, + Class[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations, + byte[] annotationDefault); + + /** Creates a new java.lang.reflect.Constructor. Access checks as + per java.lang.reflect.AccessibleObject are not overridden. */ + public Constructor newConstructor(Class declaringClass, + Class[] parameterTypes, + Class[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations); + + /** Gets the MethodAccessor object for a java.lang.reflect.Method */ + public MethodAccessor getMethodAccessor(Method m); + + /** Sets the MethodAccessor object for a java.lang.reflect.Method */ + public void setMethodAccessor(Method m, MethodAccessor accessor); + + /** Gets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public ConstructorAccessor getConstructorAccessor(Constructor c); + + /** Sets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public void setConstructorAccessor(Constructor c, + ConstructorAccessor accessor); + + /** Gets the byte[] that encodes TypeAnnotations on an Executable. */ + public byte[] getExecutableTypeAnnotationBytes(Executable ex); + + /** Gets the "slot" field from a Constructor (used for serialization) */ + public int getConstructorSlot(Constructor c); + + /** Gets the "signature" field from a Constructor (used for serialization) */ + public String getConstructorSignature(Constructor c); + + /** Gets the "annotations" field from a Constructor (used for serialization) */ + public byte[] getConstructorAnnotations(Constructor c); + + /** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */ + public byte[] getConstructorParameterAnnotations(Constructor c); + + // + // Copying routines, needed to quickly fabricate new Field, + // Method, and Constructor objects from templates + // + + /** Makes a "child" copy of a Method */ + public Method copyMethod(Method arg); + + /** Makes a "child" copy of a Field */ + public Field copyField(Field arg); + + /** Makes a "child" copy of a Constructor */ + public Constructor copyConstructor(Constructor arg); +} diff --git a/src/sun/reflect/MagicAccessorImpl.java b/src/sun/reflect/MagicAccessorImpl.java new file mode 100644 index 00000000..59624366 --- /dev/null +++ b/src/sun/reflect/MagicAccessorImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/**

    MagicAccessorImpl (named for parity with FieldAccessorImpl and + others, not because it actually implements an interface) is a + marker class in the hierarchy. All subclasses of this class are + "magically" granted access by the VM to otherwise inaccessible + fields and methods of other classes. It is used to hold the code + for dynamically-generated FieldAccessorImpl and MethodAccessorImpl + subclasses. (Use of the word "unsafe" was avoided in this class's + name to avoid confusion with {@link sun.misc.Unsafe}.)

    + +

    The bug fix for 4486457 also necessitated disabling + verification for this class and all subclasses, as opposed to just + SerializationConstructorAccessorImpl and subclasses, to avoid + having to indicate to the VM which of these dynamically-generated + stub classes were known to be able to pass the verifier.

    + +

    Do not change the name of this class without also changing the + VM's code.

    */ + +class MagicAccessorImpl { +} diff --git a/src/sun/reflect/MethodAccessor.java b/src/sun/reflect/MethodAccessor.java new file mode 100644 index 00000000..b38514ae --- /dev/null +++ b/src/sun/reflect/MethodAccessor.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/** This interface provides the declaration for + java.lang.reflect.Method.invoke(). Each Method object is + configured with a (possibly dynamically-generated) class which + implements this interface. +*/ + +public interface MethodAccessor { + /** Matches specification in {@link java.lang.reflect.Method} */ + public Object invoke(Object obj, Object[] args) + throws IllegalArgumentException, InvocationTargetException; +} diff --git a/src/sun/reflect/MethodAccessorGenerator.java b/src/sun/reflect/MethodAccessorGenerator.java new file mode 100644 index 00000000..6f212a8a --- /dev/null +++ b/src/sun/reflect/MethodAccessorGenerator.java @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** Generator for sun.reflect.MethodAccessor and + sun.reflect.ConstructorAccessor objects using bytecodes to + implement reflection. A java.lang.reflect.Method or + java.lang.reflect.Constructor object can delegate its invoke or + newInstance method to an accessor using native code or to one + generated by this class. (Methods and Constructors were merged + together in this class to ensure maximum code sharing.) */ + +class MethodAccessorGenerator extends AccessorGenerator { + + private static final short NUM_BASE_CPOOL_ENTRIES = (short) 12; + // One for invoke() plus one for constructor + private static final short NUM_METHODS = (short) 2; + // Only used if forSerialization is true + private static final short NUM_SERIALIZATION_CPOOL_ENTRIES = (short) 2; + + private static volatile int methodSymnum = 0; + private static volatile int constructorSymnum = 0; + private static volatile int serializationConstructorSymnum = 0; + + private Class declaringClass; + private Class[] parameterTypes; + private Class returnType; + private boolean isConstructor; + private boolean forSerialization; + + private short targetMethodRef; + private short invokeIdx; + private short invokeDescriptorIdx; + // Constant pool index of CONSTANT_Class_info for first + // non-primitive parameter type. Should be incremented by 2. + private short nonPrimitiveParametersBaseIdx; + + MethodAccessorGenerator() { + } + + /** This routine is not thread-safe */ + public MethodAccessor generateMethod(Class declaringClass, + String name, + Class[] parameterTypes, + Class returnType, + Class[] checkedExceptions, + int modifiers) + { + return (MethodAccessor) generate(declaringClass, + name, + parameterTypes, + returnType, + checkedExceptions, + modifiers, + false, + false, + null); + } + + /** This routine is not thread-safe */ + public ConstructorAccessor generateConstructor(Class declaringClass, + Class[] parameterTypes, + Class[] checkedExceptions, + int modifiers) + { + return (ConstructorAccessor) generate(declaringClass, + "", + parameterTypes, + Void.TYPE, + checkedExceptions, + modifiers, + true, + false, + null); + } + + /** This routine is not thread-safe */ + public SerializationConstructorAccessorImpl + generateSerializationConstructor(Class declaringClass, + Class[] parameterTypes, + Class[] checkedExceptions, + int modifiers, + Class targetConstructorClass) + { + return (SerializationConstructorAccessorImpl) + generate(declaringClass, + "", + parameterTypes, + Void.TYPE, + checkedExceptions, + modifiers, + true, + true, + targetConstructorClass); + } + + /** This routine is not thread-safe */ + private MagicAccessorImpl generate(final Class declaringClass, + String name, + Class[] parameterTypes, + Class returnType, + Class[] checkedExceptions, + int modifiers, + boolean isConstructor, + boolean forSerialization, + Class serializationTargetClass) + { + ByteVector vec = ByteVectorFactory.create(); + asm = new ClassFileAssembler(vec); + this.declaringClass = declaringClass; + this.parameterTypes = parameterTypes; + this.returnType = returnType; + this.modifiers = modifiers; + this.isConstructor = isConstructor; + this.forSerialization = forSerialization; + + asm.emitMagicAndVersion(); + + // Constant pool entries: + // ( * = Boxing information: optional) + // (+ = Shared entries provided by AccessorGenerator) + // (^ = Only present if generating SerializationConstructorAccessor) + // [UTF-8] [This class's name] + // [CONSTANT_Class_info] for above + // [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}" + // [CONSTANT_Class_info] for above + // [UTF-8] [Target class's name] + // [CONSTANT_Class_info] for above + // ^ [UTF-8] [Serialization: Class's name in which to invoke constructor] + // ^ [CONSTANT_Class_info] for above + // [UTF-8] target method or constructor name + // [UTF-8] target method or constructor signature + // [CONSTANT_NameAndType_info] for above + // [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method + // [UTF-8] "invoke" or "newInstance" + // [UTF-8] invoke or newInstance descriptor + // [UTF-8] descriptor for type of non-primitive parameter 1 + // [CONSTANT_Class_info] for type of non-primitive parameter 1 + // ... + // [UTF-8] descriptor for type of non-primitive parameter n + // [CONSTANT_Class_info] for type of non-primitive parameter n + // + [UTF-8] "java/lang/Exception" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/ClassCastException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/NullPointerException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/IllegalArgumentException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "java/lang/InvocationTargetException" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "" + // + [UTF-8] "()V" + // + [CONSTANT_NameAndType_info] for above + // + [CONSTANT_Methodref_info] for NullPointerException's constructor + // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor + // + [UTF-8] "(Ljava/lang/String;)V" + // + [CONSTANT_NameAndType_info] for "(Ljava/lang/String;)V" + // + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String + // + [UTF-8] "(Ljava/lang/Throwable;)V" + // + [CONSTANT_NameAndType_info] for "(Ljava/lang/Throwable;)V" + // + [CONSTANT_Methodref_info] for InvocationTargetException's constructor + // + [CONSTANT_Methodref_info] for "super()" + // + [UTF-8] "java/lang/Object" + // + [CONSTANT_Class_info] for above + // + [UTF-8] "toString" + // + [UTF-8] "()Ljava/lang/String;" + // + [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;" + // + [CONSTANT_Methodref_info] for Object's toString method + // + [UTF-8] "Code" + // + [UTF-8] "Exceptions" + // * [UTF-8] "java/lang/Boolean" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(Z)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "booleanValue" + // * [UTF-8] "()Z" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Byte" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(B)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "byteValue" + // * [UTF-8] "()B" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Character" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(C)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "charValue" + // * [UTF-8] "()C" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Double" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(D)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "doubleValue" + // * [UTF-8] "()D" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Float" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(F)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "floatValue" + // * [UTF-8] "()F" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Integer" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(I)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "intValue" + // * [UTF-8] "()I" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Long" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(J)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "longValue" + // * [UTF-8] "()J" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "java/lang/Short" + // * [CONSTANT_Class_info] for above + // * [UTF-8] "(S)V" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + // * [UTF-8] "shortValue" + // * [UTF-8] "()S" + // * [CONSTANT_NameAndType_info] for above + // * [CONSTANT_Methodref_info] for above + + short numCPEntries = NUM_BASE_CPOOL_ENTRIES + NUM_COMMON_CPOOL_ENTRIES; + boolean usesPrimitives = usesPrimitiveTypes(); + if (usesPrimitives) { + numCPEntries += NUM_BOXING_CPOOL_ENTRIES; + } + if (forSerialization) { + numCPEntries += NUM_SERIALIZATION_CPOOL_ENTRIES; + } + + // Add in variable-length number of entries to be able to describe + // non-primitive parameter types and checked exceptions. + numCPEntries += (short) (2 * numNonPrimitiveParameterTypes()); + + asm.emitShort(add(numCPEntries, S1)); + + final String generatedName = generateName(isConstructor, forSerialization); + asm.emitConstantPoolUTF8(generatedName); + asm.emitConstantPoolClass(asm.cpi()); + thisClass = asm.cpi(); + if (isConstructor) { + if (forSerialization) { + asm.emitConstantPoolUTF8 + ("sun/reflect/SerializationConstructorAccessorImpl"); + } else { + asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl"); + } + } else { + asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl"); + } + asm.emitConstantPoolClass(asm.cpi()); + superClass = asm.cpi(); + asm.emitConstantPoolUTF8(getClassName(declaringClass, false)); + asm.emitConstantPoolClass(asm.cpi()); + targetClass = asm.cpi(); + short serializationTargetClassIdx = (short) 0; + if (forSerialization) { + asm.emitConstantPoolUTF8(getClassName(serializationTargetClass, false)); + asm.emitConstantPoolClass(asm.cpi()); + serializationTargetClassIdx = asm.cpi(); + } + asm.emitConstantPoolUTF8(name); + asm.emitConstantPoolUTF8(buildInternalSignature()); + asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi()); + if (isInterface()) { + asm.emitConstantPoolInterfaceMethodref(targetClass, asm.cpi()); + } else { + if (forSerialization) { + asm.emitConstantPoolMethodref(serializationTargetClassIdx, asm.cpi()); + } else { + asm.emitConstantPoolMethodref(targetClass, asm.cpi()); + } + } + targetMethodRef = asm.cpi(); + if (isConstructor) { + asm.emitConstantPoolUTF8("newInstance"); + } else { + asm.emitConstantPoolUTF8("invoke"); + } + invokeIdx = asm.cpi(); + if (isConstructor) { + asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;"); + } else { + asm.emitConstantPoolUTF8 + ("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + } + invokeDescriptorIdx = asm.cpi(); + + // Output class information for non-primitive parameter types + nonPrimitiveParametersBaseIdx = add(asm.cpi(), S2); + for (int i = 0; i < parameterTypes.length; i++) { + Class c = parameterTypes[i]; + if (!isPrimitive(c)) { + asm.emitConstantPoolUTF8(getClassName(c, false)); + asm.emitConstantPoolClass(asm.cpi()); + } + } + + // Entries common to FieldAccessor, MethodAccessor and ConstructorAccessor + emitCommonConstantPoolEntries(); + + // Boxing entries + if (usesPrimitives) { + emitBoxingContantPoolEntries(); + } + + if (asm.cpi() != numCPEntries) { + throw new InternalError("Adjust this code (cpi = " + asm.cpi() + + ", numCPEntries = " + numCPEntries + ")"); + } + + // Access flags + asm.emitShort(ACC_PUBLIC); + + // This class + asm.emitShort(thisClass); + + // Superclass + asm.emitShort(superClass); + + // Interfaces count and interfaces + asm.emitShort(S0); + + // Fields count and fields + asm.emitShort(S0); + + // Methods count and methods + asm.emitShort(NUM_METHODS); + + emitConstructor(); + emitInvoke(); + + // Additional attributes (none) + asm.emitShort(S0); + + // Load class + vec.trim(); + final byte[] bytes = vec.getData(); + // Note: the class loader is the only thing that really matters + // here -- it's important to get the generated code into the + // same namespace as the target class. Since the generated code + // is privileged anyway, the protection domain probably doesn't + // matter. + return AccessController.doPrivileged( + new PrivilegedAction() { + public MagicAccessorImpl run() { + try { + return (MagicAccessorImpl) + ClassDefiner.defineClass + (generatedName, + bytes, + 0, + bytes.length, + declaringClass.getClassLoader()).newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new InternalError(e); + } + } + }); + } + + /** This emits the code for either invoke() or newInstance() */ + private void emitInvoke() { + // NOTE that this code will only handle 65535 parameters since we + // use the sipush instruction to get the array index on the + // operand stack. + if (parameterTypes.length > 65535) { + throw new InternalError("Can't handle more than 65535 parameters"); + } + + // Generate code into fresh code buffer + ClassFileAssembler cb = new ClassFileAssembler(); + if (isConstructor) { + // 1 incoming argument + cb.setMaxLocals(2); + } else { + // 2 incoming arguments + cb.setMaxLocals(3); + } + + short illegalArgStartPC = 0; + + if (isConstructor) { + // Instantiate target class before continuing + // new + // dup + cb.opc_new(targetClass); + cb.opc_dup(); + } else { + // Setup before iterating down argument list + if (isPrimitive(returnType)) { + // new + // dup + // ... (see below:) + // invokespecial + // areturn + cb.opc_new(indexForPrimitiveType(returnType)); + cb.opc_dup(); + } + + // Get target object on operand stack if necessary. + + // We need to do an explicit null check here; we won't see + // NullPointerExceptions from the invoke bytecode, since it's + // covered by an exception handler. + if (!isStatic()) { + // aload_1 + // ifnonnull + // new + // dup + // invokespecial + // athrow + // + // aload_1 + // checkcast + cb.opc_aload_1(); + Label l = new Label(); + cb.opc_ifnonnull(l); + cb.opc_new(nullPointerClass); + cb.opc_dup(); + cb.opc_invokespecial(nullPointerCtorIdx, 0, 0); + cb.opc_athrow(); + l.bind(); + illegalArgStartPC = cb.getLength(); + cb.opc_aload_1(); + cb.opc_checkcast(targetClass); + } + } + + // Have to check length of incoming array and throw + // IllegalArgumentException if not correct. A concession to the + // JCK (isn't clearly specified in the spec): we allow null in the + // case where the argument list is zero length. + // if no-arg: + // aload_2 | aload_1 (Method | Constructor) + // ifnull + // aload_2 | aload_1 + // arraylength + // sipush + // if_icmpeq + // new + // dup + // invokespecial + // athrow + // + Label successLabel = new Label(); + if (parameterTypes.length == 0) { + if (isConstructor) { + cb.opc_aload_1(); + } else { + cb.opc_aload_2(); + } + cb.opc_ifnull(successLabel); + } + if (isConstructor) { + cb.opc_aload_1(); + } else { + cb.opc_aload_2(); + } + cb.opc_arraylength(); + cb.opc_sipush((short) parameterTypes.length); + cb.opc_if_icmpeq(successLabel); + cb.opc_new(illegalArgumentClass); + cb.opc_dup(); + cb.opc_invokespecial(illegalArgumentCtorIdx, 0, 0); + cb.opc_athrow(); + successLabel.bind(); + + // Iterate through incoming actual parameters, ensuring that each + // is compatible with the formal parameter type, and pushing the + // actual on the operand stack (unboxing and widening if necessary). + + short paramTypeCPIdx = nonPrimitiveParametersBaseIdx; + Label nextParamLabel = null; + byte count = 1; // both invokeinterface opcode's "count" as well as + // num args of other invoke bytecodes + for (int i = 0; i < parameterTypes.length; i++) { + Class paramType = parameterTypes[i]; + count += (byte) typeSizeInStackSlots(paramType); + if (nextParamLabel != null) { + nextParamLabel.bind(); + nextParamLabel = null; + } + // aload_2 | aload_1 + // sipush + // aaload + if (isConstructor) { + cb.opc_aload_1(); + } else { + cb.opc_aload_2(); + } + cb.opc_sipush((short) i); + cb.opc_aaload(); + if (isPrimitive(paramType)) { + // Unboxing code. + // Put parameter into temporary local variable + // astore_3 | astore_2 + if (isConstructor) { + cb.opc_astore_2(); + } else { + cb.opc_astore_3(); + } + + // repeat for all possible widening conversions: + // aload_3 | aload_2 + // instanceof + // ifeq + // aload_3 | aload_2 + // checkcast // Note: this is "redundant", + // // but necessary for the verifier + // invokevirtual + // + // goto + // ... + // last unboxing label: + // new + // dup + // invokespecial + // athrow + + Label l = null; // unboxing label + nextParamLabel = new Label(); + + for (int j = 0; j < primitiveTypes.length; j++) { + Class c = primitiveTypes[j]; + if (canWidenTo(c, paramType)) { + if (l != null) { + l.bind(); + } + // Emit checking and unboxing code for this type + if (isConstructor) { + cb.opc_aload_2(); + } else { + cb.opc_aload_3(); + } + cb.opc_instanceof(indexForPrimitiveType(c)); + l = new Label(); + cb.opc_ifeq(l); + if (isConstructor) { + cb.opc_aload_2(); + } else { + cb.opc_aload_3(); + } + cb.opc_checkcast(indexForPrimitiveType(c)); + cb.opc_invokevirtual(unboxingMethodForPrimitiveType(c), + 0, + typeSizeInStackSlots(c)); + emitWideningBytecodeForPrimitiveConversion(cb, + c, + paramType); + cb.opc_goto(nextParamLabel); + } + } + + if (l == null) { + throw new InternalError + ("Must have found at least identity conversion"); + } + + // Fell through; given object is null or invalid. According to + // the spec, we can throw IllegalArgumentException for both of + // these cases. + + l.bind(); + cb.opc_new(illegalArgumentClass); + cb.opc_dup(); + cb.opc_invokespecial(illegalArgumentCtorIdx, 0, 0); + cb.opc_athrow(); + } else { + // Emit appropriate checkcast + cb.opc_checkcast(paramTypeCPIdx); + paramTypeCPIdx = add(paramTypeCPIdx, S2); + // Fall through to next argument + } + } + // Bind last goto if present + if (nextParamLabel != null) { + nextParamLabel.bind(); + } + + short invokeStartPC = cb.getLength(); + + // OK, ready to perform the invocation. + if (isConstructor) { + cb.opc_invokespecial(targetMethodRef, count, 0); + } else { + if (isStatic()) { + cb.opc_invokestatic(targetMethodRef, + count, + typeSizeInStackSlots(returnType)); + } else { + if (isInterface()) { + if (isPrivate()) { + cb.opc_invokespecial(targetMethodRef, count, 0); + } else { + cb.opc_invokeinterface(targetMethodRef, + count, + count, + typeSizeInStackSlots(returnType)); + } + } else { + cb.opc_invokevirtual(targetMethodRef, + count, + typeSizeInStackSlots(returnType)); + } + } + } + + short invokeEndPC = cb.getLength(); + + if (!isConstructor) { + // Box return value if necessary + if (isPrimitive(returnType)) { + cb.opc_invokespecial(ctorIndexForPrimitiveType(returnType), + typeSizeInStackSlots(returnType), + 0); + } else if (returnType == Void.TYPE) { + cb.opc_aconst_null(); + } + } + cb.opc_areturn(); + + // We generate two exception handlers; one which is responsible + // for catching ClassCastException and NullPointerException and + // throwing IllegalArgumentException, and the other which catches + // all java/lang/Throwable objects thrown from the target method + // and wraps them in InvocationTargetExceptions. + + short classCastHandler = cb.getLength(); + + // ClassCast, etc. exception handler + cb.setStack(1); + cb.opc_invokespecial(toStringIdx, 0, 1); + cb.opc_new(illegalArgumentClass); + cb.opc_dup_x1(); + cb.opc_swap(); + cb.opc_invokespecial(illegalArgumentStringCtorIdx, 1, 0); + cb.opc_athrow(); + + short invocationTargetHandler = cb.getLength(); + + // InvocationTargetException exception handler + cb.setStack(1); + cb.opc_new(invocationTargetClass); + cb.opc_dup_x1(); + cb.opc_swap(); + cb.opc_invokespecial(invocationTargetCtorIdx, 1, 0); + cb.opc_athrow(); + + // Generate exception table. We cover the entire code sequence + // with an exception handler which catches ClassCastException and + // converts it into an IllegalArgumentException. + + ClassFileAssembler exc = new ClassFileAssembler(); + + exc.emitShort(illegalArgStartPC); // start PC + exc.emitShort(invokeStartPC); // end PC + exc.emitShort(classCastHandler); // handler PC + exc.emitShort(classCastClass); // catch type + + exc.emitShort(illegalArgStartPC); // start PC + exc.emitShort(invokeStartPC); // end PC + exc.emitShort(classCastHandler); // handler PC + exc.emitShort(nullPointerClass); // catch type + + exc.emitShort(invokeStartPC); // start PC + exc.emitShort(invokeEndPC); // end PC + exc.emitShort(invocationTargetHandler); // handler PC + exc.emitShort(throwableClass); // catch type + + emitMethod(invokeIdx, cb.getMaxLocals(), cb, exc, + new short[] { invocationTargetClass }); + } + + private boolean usesPrimitiveTypes() { + // We need to emit boxing/unboxing constant pool information if + // the method takes a primitive type for any of its parameters or + // returns a primitive value (except void) + if (returnType.isPrimitive()) { + return true; + } + for (int i = 0; i < parameterTypes.length; i++) { + if (parameterTypes[i].isPrimitive()) { + return true; + } + } + return false; + } + + private int numNonPrimitiveParameterTypes() { + int num = 0; + for (int i = 0; i < parameterTypes.length; i++) { + if (!parameterTypes[i].isPrimitive()) { + ++num; + } + } + return num; + } + + private boolean isInterface() { + return declaringClass.isInterface(); + } + + private String buildInternalSignature() { + StringBuffer buf = new StringBuffer(); + buf.append("("); + for (int i = 0; i < parameterTypes.length; i++) { + buf.append(getClassName(parameterTypes[i], true)); + } + buf.append(")"); + buf.append(getClassName(returnType, true)); + return buf.toString(); + } + + private static synchronized String generateName(boolean isConstructor, + boolean forSerialization) + { + if (isConstructor) { + if (forSerialization) { + int num = ++serializationConstructorSymnum; + return "sun/reflect/GeneratedSerializationConstructorAccessor" + num; + } else { + int num = ++constructorSymnum; + return "sun/reflect/GeneratedConstructorAccessor" + num; + } + } else { + int num = ++methodSymnum; + return "sun/reflect/GeneratedMethodAccessor" + num; + } + } +} diff --git a/src/sun/reflect/MethodAccessorImpl.java b/src/sun/reflect/MethodAccessorImpl.java new file mode 100644 index 00000000..14282fb6 --- /dev/null +++ b/src/sun/reflect/MethodAccessorImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; + +/**

    Package-private implementation of the MethodAccessor interface + which has access to all classes and all fields, regardless of + language restrictions. See MagicAccessor.

    + +

    This class is known to the VM; do not change its name without + also changing the VM's code.

    + +

    NOTE: ALL methods of subclasses are skipped during security + walks up the stack. The assumption is that the only such methods + that will persistently show up on the stack are the implementing + methods for java.lang.reflect.Method.invoke().

    +*/ + +abstract class MethodAccessorImpl extends MagicAccessorImpl + implements MethodAccessor { + /** Matches specification in {@link java.lang.reflect.Method} */ + public abstract Object invoke(Object obj, Object[] args) + throws IllegalArgumentException, InvocationTargetException; +} diff --git a/src/sun/reflect/NativeConstructorAccessorImpl.java b/src/sun/reflect/NativeConstructorAccessorImpl.java new file mode 100644 index 00000000..5acb6a59 --- /dev/null +++ b/src/sun/reflect/NativeConstructorAccessorImpl.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import sun.reflect.misc.ReflectUtil; + +/** Used only for the first few invocations of a Constructor; + afterward, switches to bytecode-based implementation */ + +class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { + private final Constructor c; + private DelegatingConstructorAccessorImpl parent; + private int numInvocations; + + NativeConstructorAccessorImpl(Constructor c) { + this.c = c; + } + + public Object newInstance(Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException + { + // We can't inflate a constructor belonging to a vm-anonymous class + // because that kind of class can't be referred to by name, hence can't + // be found from the generated bytecode. + if (++numInvocations > ReflectionFactory.inflationThreshold() + && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) { + ConstructorAccessorImpl acc = (ConstructorAccessorImpl) + new MethodAccessorGenerator(). + generateConstructor(c.getDeclaringClass(), + c.getParameterTypes(), + c.getExceptionTypes(), + c.getModifiers()); + parent.setDelegate(acc); + } + + return newInstance0(c, args); + } + + void setParent(DelegatingConstructorAccessorImpl parent) { + this.parent = parent; + } + + private static native Object newInstance0(Constructor c, Object[] args) + throws InstantiationException, + IllegalArgumentException, + InvocationTargetException; +} diff --git a/src/sun/reflect/NativeMethodAccessorImpl.java b/src/sun/reflect/NativeMethodAccessorImpl.java new file mode 100644 index 00000000..4f592d2e --- /dev/null +++ b/src/sun/reflect/NativeMethodAccessorImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import sun.reflect.misc.ReflectUtil; + +/** Used only for the first few invocations of a Method; afterward, + switches to bytecode-based implementation */ + +class NativeMethodAccessorImpl extends MethodAccessorImpl { + private final Method method; + private DelegatingMethodAccessorImpl parent; + private int numInvocations; + + NativeMethodAccessorImpl(Method method) { + this.method = method; + } + + public Object invoke(Object obj, Object[] args) + throws IllegalArgumentException, InvocationTargetException + { + // We can't inflate methods belonging to vm-anonymous classes because + // that kind of class can't be referred to by name, hence can't be + // found from the generated bytecode. + if (++numInvocations > ReflectionFactory.inflationThreshold() + && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { + MethodAccessorImpl acc = (MethodAccessorImpl) + new MethodAccessorGenerator(). + generateMethod(method.getDeclaringClass(), + method.getName(), + method.getParameterTypes(), + method.getReturnType(), + method.getExceptionTypes(), + method.getModifiers()); + parent.setDelegate(acc); + } + + return invoke0(method, obj, args); + } + + void setParent(DelegatingMethodAccessorImpl parent) { + this.parent = parent; + } + + private static native Object invoke0(Method m, Object obj, Object[] args); +} diff --git a/src/sun/reflect/Reflection.java b/src/sun/reflect/Reflection.java new file mode 100644 index 00000000..196a6c0b --- /dev/null +++ b/src/sun/reflect/Reflection.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +/** Common utility routines used by both java.lang and + java.lang.reflect */ + +public class Reflection { + + /** Used to filter out fields and methods from certain classes from public + view, where they are sensitive or they may contain VM-internal objects. + These Maps are updated very rarely. Rather than synchronize on + each access, we use copy-on-write */ + private static volatile Map,String[]> fieldFilterMap; + private static volatile Map,String[]> methodFilterMap; + + static { + Map,String[]> map = new HashMap,String[]>(); + map.put(Reflection.class, + new String[] {"fieldFilterMap", "methodFilterMap"}); + map.put(System.class, new String[] {"security"}); + map.put(Class.class, new String[] {"classLoader"}); + fieldFilterMap = map; + + methodFilterMap = new HashMap<>(); + } + + /** Returns the class of the caller of the method calling this method, + ignoring frames associated with java.lang.reflect.Method.invoke() + and its implementation. */ + @CallerSensitive + public static native Class getCallerClass(); + + /** + * @deprecated This method will be removed in JDK 9. + * This method is a private JDK API and retained temporarily for + * existing code to run until a replacement API is defined. + */ + @Deprecated + public static native Class getCallerClass(int depth); + + /** Retrieves the access flags written to the class file. For + inner classes these flags may differ from those returned by + Class.getModifiers(), which searches the InnerClasses + attribute to find the source-level access flags. This is used + instead of Class.getModifiers() for run-time access checks due + to compatibility reasons; see 4471811. Only the values of the + low 13 bits (i.e., a mask of 0x1FFF) are guaranteed to be + valid. */ + public static native int getClassAccessFlags(Class c); + + /** A quick "fast-path" check to try to avoid getCallerClass() + calls. */ + public static boolean quickCheckMemberAccess(Class memberClass, + int modifiers) + { + return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers); + } + + public static void ensureMemberAccess(Class currentClass, + Class memberClass, + Object target, + int modifiers) + throws IllegalAccessException + { + if (currentClass == null || memberClass == null) { + throw new InternalError(); + } + + if (!verifyMemberAccess(currentClass, memberClass, target, modifiers)) { + throw new IllegalAccessException("Class " + currentClass.getName() + + " can not access a member of class " + + memberClass.getName() + + " with modifiers \"" + + Modifier.toString(modifiers) + + "\""); + } + } + + public static boolean verifyMemberAccess(Class currentClass, + // Declaring class of field + // or method + Class memberClass, + // May be NULL in case of statics + Object target, + int modifiers) + { + // Verify that currentClass can access a field, method, or + // constructor of memberClass, where that member's access bits are + // "modifiers". + + boolean gotIsSameClassPackage = false; + boolean isSameClassPackage = false; + + if (currentClass == memberClass) { + // Always succeeds + return true; + } + + if (!Modifier.isPublic(getClassAccessFlags(memberClass))) { + isSameClassPackage = isSameClassPackage(currentClass, memberClass); + gotIsSameClassPackage = true; + if (!isSameClassPackage) { + return false; + } + } + + // At this point we know that currentClass can access memberClass. + + if (Modifier.isPublic(modifiers)) { + return true; + } + + boolean successSoFar = false; + + if (Modifier.isProtected(modifiers)) { + // See if currentClass is a subclass of memberClass + if (isSubclassOf(currentClass, memberClass)) { + successSoFar = true; + } + } + + if (!successSoFar && !Modifier.isPrivate(modifiers)) { + if (!gotIsSameClassPackage) { + isSameClassPackage = isSameClassPackage(currentClass, + memberClass); + gotIsSameClassPackage = true; + } + + if (isSameClassPackage) { + successSoFar = true; + } + } + + if (!successSoFar) { + return false; + } + + if (Modifier.isProtected(modifiers)) { + // Additional test for protected members: JLS 6.6.2 + Class targetClass = (target == null ? memberClass : target.getClass()); + if (targetClass != currentClass) { + if (!gotIsSameClassPackage) { + isSameClassPackage = isSameClassPackage(currentClass, memberClass); + gotIsSameClassPackage = true; + } + if (!isSameClassPackage) { + if (!isSubclassOf(targetClass, currentClass)) { + return false; + } + } + } + } + + return true; + } + + private static boolean isSameClassPackage(Class c1, Class c2) { + return isSameClassPackage(c1.getClassLoader(), c1.getName(), + c2.getClassLoader(), c2.getName()); + } + + /** Returns true if two classes are in the same package; classloader + and classname information is enough to determine a class's package */ + private static boolean isSameClassPackage(ClassLoader loader1, String name1, + ClassLoader loader2, String name2) + { + if (loader1 != loader2) { + return false; + } else { + int lastDot1 = name1.lastIndexOf('.'); + int lastDot2 = name2.lastIndexOf('.'); + if ((lastDot1 == -1) || (lastDot2 == -1)) { + // One of the two doesn't have a package. Only return true + // if the other one also doesn't have a package. + return (lastDot1 == lastDot2); + } else { + int idx1 = 0; + int idx2 = 0; + + // Skip over '['s + if (name1.charAt(idx1) == '[') { + do { + idx1++; + } while (name1.charAt(idx1) == '['); + if (name1.charAt(idx1) != 'L') { + // Something is terribly wrong. Shouldn't be here. + throw new InternalError("Illegal class name " + name1); + } + } + if (name2.charAt(idx2) == '[') { + do { + idx2++; + } while (name2.charAt(idx2) == '['); + if (name2.charAt(idx2) != 'L') { + // Something is terribly wrong. Shouldn't be here. + throw new InternalError("Illegal class name " + name2); + } + } + + // Check that package part is identical + int length1 = lastDot1 - idx1; + int length2 = lastDot2 - idx2; + + if (length1 != length2) { + return false; + } + return name1.regionMatches(false, idx1, name2, idx2, length1); + } + } + } + + static boolean isSubclassOf(Class queryClass, + Class ofClass) + { + while (queryClass != null) { + if (queryClass == ofClass) { + return true; + } + queryClass = queryClass.getSuperclass(); + } + return false; + } + + // fieldNames must contain only interned Strings + public static synchronized void registerFieldsToFilter(Class containingClass, + String ... fieldNames) { + fieldFilterMap = + registerFilter(fieldFilterMap, containingClass, fieldNames); + } + + // methodNames must contain only interned Strings + public static synchronized void registerMethodsToFilter(Class containingClass, + String ... methodNames) { + methodFilterMap = + registerFilter(methodFilterMap, containingClass, methodNames); + } + + private static Map,String[]> registerFilter(Map,String[]> map, + Class containingClass, String ... names) { + if (map.get(containingClass) != null) { + throw new IllegalArgumentException + ("Filter already registered: " + containingClass); + } + map = new HashMap,String[]>(map); + map.put(containingClass, names); + return map; + } + + public static Field[] filterFields(Class containingClass, + Field[] fields) { + if (fieldFilterMap == null) { + // Bootstrapping + return fields; + } + return (Field[])filter(fields, fieldFilterMap.get(containingClass)); + } + + public static Method[] filterMethods(Class containingClass, Method[] methods) { + if (methodFilterMap == null) { + // Bootstrapping + return methods; + } + return (Method[])filter(methods, methodFilterMap.get(containingClass)); + } + + private static Member[] filter(Member[] members, String[] filteredNames) { + if ((filteredNames == null) || (members.length == 0)) { + return members; + } + int numNewMembers = 0; + for (Member member : members) { + boolean shouldSkip = false; + for (String filteredName : filteredNames) { + if (member.getName() == filteredName) { + shouldSkip = true; + break; + } + } + if (!shouldSkip) { + ++numNewMembers; + } + } + Member[] newMembers = + (Member[])Array.newInstance(members[0].getClass(), numNewMembers); + int destIdx = 0; + for (Member member : members) { + boolean shouldSkip = false; + for (String filteredName : filteredNames) { + if (member.getName() == filteredName) { + shouldSkip = true; + break; + } + } + if (!shouldSkip) { + newMembers[destIdx++] = member; + } + } + return newMembers; + } + + /** + * Tests if the given method is caller-sensitive and the declaring class + * is defined by either the bootstrap class loader or extension class loader. + */ + public static boolean isCallerSensitive(Method m) { + final ClassLoader loader = m.getDeclaringClass().getClassLoader(); + if (sun.misc.VM.isSystemDomainLoader(loader) || isExtClassLoader(loader)) { + return m.isAnnotationPresent(CallerSensitive.class); + } + return false; + } + + private static boolean isExtClassLoader(ClassLoader loader) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + while (cl != null) { + if (cl.getParent() == null && cl == loader) { + return true; + } + cl = cl.getParent(); + } + return false; + } +} diff --git a/src/sun/reflect/ReflectionFactory.java b/src/sun/reflect/ReflectionFactory.java new file mode 100644 index 00000000..f86dae72 --- /dev/null +++ b/src/sun/reflect/ReflectionFactory.java @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; + +import sun.reflect.misc.ReflectUtil; + +/**

    The master factory for all reflective objects, both those in + java.lang.reflect (Fields, Methods, Constructors) as well as their + delegates (FieldAccessors, MethodAccessors, ConstructorAccessors). +

    + +

    The methods in this class are extremely unsafe and can cause + subversion of both the language and the verifier. For this reason, + they are all instance methods, and access to the constructor of + this factory is guarded by a security check, in similar style to + {@link sun.misc.Unsafe}.

    +*/ + +public class ReflectionFactory { + + private static boolean initted = false; + private static final Permission reflectionFactoryAccessPerm + = new RuntimePermission("reflectionFactoryAccess"); + private static final ReflectionFactory soleInstance = new ReflectionFactory(); + // Provides access to package-private mechanisms in java.lang.reflect + private static volatile LangReflectAccess langReflectAccess; + + // + // "Inflation" mechanism. Loading bytecodes to implement + // Method.invoke() and Constructor.newInstance() currently costs + // 3-4x more than an invocation via native code for the first + // invocation (though subsequent invocations have been benchmarked + // to be over 20x faster). Unfortunately this cost increases + // startup time for certain applications that use reflection + // intensively (but only once per class) to bootstrap themselves. + // To avoid this penalty we reuse the existing JVM entry points + // for the first few invocations of Methods and Constructors and + // then switch to the bytecode-based implementations. + // + // Package-private to be accessible to NativeMethodAccessorImpl + // and NativeConstructorAccessorImpl + private static boolean noInflation = false; + private static int inflationThreshold = 15; + + private ReflectionFactory() { + } + + /** + * A convenience class for acquiring the capability to instantiate + * reflective objects. Use this instead of a raw call to {@link + * #getReflectionFactory} in order to avoid being limited by the + * permissions of your callers. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + */ + public static final class GetReflectionFactoryAction + implements PrivilegedAction { + public ReflectionFactory run() { + return getReflectionFactory(); + } + } + + /** + * Provides the caller with the capability to instantiate reflective + * objects. + * + *

    First, if there is a security manager, its + * checkPermission method is called with a {@link + * RuntimePermission} with target + * "reflectionFactoryAccess". This may result in a + * security exception. + * + *

    The returned ReflectionFactory object should be + * carefully guarded by the caller, since it can be used to read and + * write private data and invoke private methods, as well as to load + * unverified bytecodes. It must never be passed to untrusted code. + * + * @exception SecurityException if a security manager exists and its + * checkPermission method doesn't allow + * access to the RuntimePermission "reflectionFactoryAccess". */ + public static ReflectionFactory getReflectionFactory() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + // TO DO: security.checkReflectionFactoryAccess(); + security.checkPermission(reflectionFactoryAccessPerm); + } + return soleInstance; + } + + //-------------------------------------------------------------------------- + // + // Routines used by java.lang.reflect + // + // + + /** Called only by java.lang.reflect.Modifier's static initializer */ + public void setLangReflectAccess(LangReflectAccess access) { + langReflectAccess = access; + } + + /** + * Note: this routine can cause the declaring class for the field + * be initialized and therefore must not be called until the + * first get/set of this field. + * @param field the field + * @param override true if caller has overridden aaccessibility + */ + public FieldAccessor newFieldAccessor(Field field, boolean override) { + checkInitted(); + return UnsafeFieldAccessorFactory.newFieldAccessor(field, override); + } + + public MethodAccessor newMethodAccessor(Method method) { + checkInitted(); + + if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { + return new MethodAccessorGenerator(). + generateMethod(method.getDeclaringClass(), + method.getName(), + method.getParameterTypes(), + method.getReturnType(), + method.getExceptionTypes(), + method.getModifiers()); + } else { + NativeMethodAccessorImpl acc = + new NativeMethodAccessorImpl(method); + DelegatingMethodAccessorImpl res = + new DelegatingMethodAccessorImpl(acc); + acc.setParent(res); + return res; + } + } + + public ConstructorAccessor newConstructorAccessor(Constructor c) { + checkInitted(); + + Class declaringClass = c.getDeclaringClass(); + if (Modifier.isAbstract(declaringClass.getModifiers())) { + return new InstantiationExceptionConstructorAccessorImpl(null); + } + if (declaringClass == Class.class) { + return new InstantiationExceptionConstructorAccessorImpl + ("Can not instantiate java.lang.Class"); + } + // Bootstrapping issue: since we use Class.newInstance() in + // the ConstructorAccessor generation process, we have to + // break the cycle here. + if (Reflection.isSubclassOf(declaringClass, + ConstructorAccessorImpl.class)) { + return new BootstrapConstructorAccessorImpl(c); + } + + if (noInflation && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) { + return new MethodAccessorGenerator(). + generateConstructor(c.getDeclaringClass(), + c.getParameterTypes(), + c.getExceptionTypes(), + c.getModifiers()); + } else { + NativeConstructorAccessorImpl acc = + new NativeConstructorAccessorImpl(c); + DelegatingConstructorAccessorImpl res = + new DelegatingConstructorAccessorImpl(acc); + acc.setParent(res); + return res; + } + } + + //-------------------------------------------------------------------------- + // + // Routines used by java.lang + // + // + + /** Creates a new java.lang.reflect.Field. Access checks as per + java.lang.reflect.AccessibleObject are not overridden. */ + public Field newField(Class declaringClass, + String name, + Class type, + int modifiers, + int slot, + String signature, + byte[] annotations) + { + return langReflectAccess().newField(declaringClass, + name, + type, + modifiers, + slot, + signature, + annotations); + } + + /** Creates a new java.lang.reflect.Method. Access checks as per + java.lang.reflect.AccessibleObject are not overridden. */ + public Method newMethod(Class declaringClass, + String name, + Class[] parameterTypes, + Class returnType, + Class[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations, + byte[] annotationDefault) + { + return langReflectAccess().newMethod(declaringClass, + name, + parameterTypes, + returnType, + checkedExceptions, + modifiers, + slot, + signature, + annotations, + parameterAnnotations, + annotationDefault); + } + + /** Creates a new java.lang.reflect.Constructor. Access checks as + per java.lang.reflect.AccessibleObject are not overridden. */ + public Constructor newConstructor(Class declaringClass, + Class[] parameterTypes, + Class[] checkedExceptions, + int modifiers, + int slot, + String signature, + byte[] annotations, + byte[] parameterAnnotations) + { + return langReflectAccess().newConstructor(declaringClass, + parameterTypes, + checkedExceptions, + modifiers, + slot, + signature, + annotations, + parameterAnnotations); + } + + /** Gets the MethodAccessor object for a java.lang.reflect.Method */ + public MethodAccessor getMethodAccessor(Method m) { + return langReflectAccess().getMethodAccessor(m); + } + + /** Sets the MethodAccessor object for a java.lang.reflect.Method */ + public void setMethodAccessor(Method m, MethodAccessor accessor) { + langReflectAccess().setMethodAccessor(m, accessor); + } + + /** Gets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public ConstructorAccessor getConstructorAccessor(Constructor c) { + return langReflectAccess().getConstructorAccessor(c); + } + + /** Sets the ConstructorAccessor object for a + java.lang.reflect.Constructor */ + public void setConstructorAccessor(Constructor c, + ConstructorAccessor accessor) + { + langReflectAccess().setConstructorAccessor(c, accessor); + } + + /** Makes a copy of the passed method. The returned method is a + "child" of the passed one; see the comments in Method.java for + details. */ + public Method copyMethod(Method arg) { + return langReflectAccess().copyMethod(arg); + } + + /** Makes a copy of the passed field. The returned field is a + "child" of the passed one; see the comments in Field.java for + details. */ + public Field copyField(Field arg) { + return langReflectAccess().copyField(arg); + } + + /** Makes a copy of the passed constructor. The returned + constructor is a "child" of the passed one; see the comments + in Constructor.java for details. */ + public Constructor copyConstructor(Constructor arg) { + return langReflectAccess().copyConstructor(arg); + } + + /** Gets the byte[] that encodes TypeAnnotations on an executable. + */ + public byte[] getExecutableTypeAnnotationBytes(Executable ex) { + return langReflectAccess().getExecutableTypeAnnotationBytes(ex); + } + + //-------------------------------------------------------------------------- + // + // Routines used by serialization + // + // + + public Constructor newConstructorForSerialization + (Class classToInstantiate, Constructor constructorToCall) + { + // Fast path + if (constructorToCall.getDeclaringClass() == classToInstantiate) { + return constructorToCall; + } + + ConstructorAccessor acc = new MethodAccessorGenerator(). + generateSerializationConstructor(classToInstantiate, + constructorToCall.getParameterTypes(), + constructorToCall.getExceptionTypes(), + constructorToCall.getModifiers(), + constructorToCall.getDeclaringClass()); + Constructor c = newConstructor(constructorToCall.getDeclaringClass(), + constructorToCall.getParameterTypes(), + constructorToCall.getExceptionTypes(), + constructorToCall.getModifiers(), + langReflectAccess(). + getConstructorSlot(constructorToCall), + langReflectAccess(). + getConstructorSignature(constructorToCall), + langReflectAccess(). + getConstructorAnnotations(constructorToCall), + langReflectAccess(). + getConstructorParameterAnnotations(constructorToCall)); + setConstructorAccessor(c, acc); + return c; + } + + //-------------------------------------------------------------------------- + // + // Internals only below this point + // + + static int inflationThreshold() { + return inflationThreshold; + } + + /** We have to defer full initialization of this class until after + the static initializer is run since java.lang.reflect.Method's + static initializer (more properly, that for + java.lang.reflect.AccessibleObject) causes this class's to be + run, before the system properties are set up. */ + private static void checkInitted() { + if (initted) return; + AccessController.doPrivileged( + new PrivilegedAction() { + public Void run() { + // Tests to ensure the system properties table is fully + // initialized. This is needed because reflection code is + // called very early in the initialization process (before + // command-line arguments have been parsed and therefore + // these user-settable properties installed.) We assume that + // if System.out is non-null then the System class has been + // fully initialized and that the bulk of the startup code + // has been run. + + if (System.out == null) { + // java.lang.System not yet fully initialized + return null; + } + + String val = System.getProperty("sun.reflect.noInflation"); + if (val != null && val.equals("true")) { + noInflation = true; + } + + val = System.getProperty("sun.reflect.inflationThreshold"); + if (val != null) { + try { + inflationThreshold = Integer.parseInt(val); + } catch (NumberFormatException e) { + throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e); + } + } + + initted = true; + return null; + } + }); + } + + private static LangReflectAccess langReflectAccess() { + if (langReflectAccess == null) { + // Call a static method to get class java.lang.reflect.Modifier + // initialized. Its static initializer will cause + // setLangReflectAccess() to be called from the context of the + // java.lang.reflect package. + Modifier.isPublic(Modifier.PUBLIC); + } + return langReflectAccess; + } +} diff --git a/src/sun/reflect/SerializationConstructorAccessorImpl.java b/src/sun/reflect/SerializationConstructorAccessorImpl.java new file mode 100644 index 00000000..a26d0664 --- /dev/null +++ b/src/sun/reflect/SerializationConstructorAccessorImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/**

    Java serialization (in java.io) expects to be able to + instantiate a class and invoke a no-arg constructor of that + class's first non-Serializable superclass. This is not a valid + operation according to the VM specification; one can not (for + classes A and B, where B is a subclass of A) write "new B; + invokespecial A()" without getting a verification error.

    + +

    In all other respects, the bytecode-based reflection framework + can be reused for this purpose. This marker class was originally + known to the VM and verification disabled for it and all + subclasses, but the bug fix for 4486457 necessitated disabling + verification for all of the dynamically-generated bytecodes + associated with reflection. This class has been left in place to + make future debugging easier.

    */ + +abstract class SerializationConstructorAccessorImpl + extends ConstructorAccessorImpl { +} diff --git a/src/sun/reflect/SignatureIterator.java b/src/sun/reflect/SignatureIterator.java new file mode 100644 index 00000000..ea7fd3c8 --- /dev/null +++ b/src/sun/reflect/SignatureIterator.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** Assists in iterating down a method's signature */ + +public class SignatureIterator { + private final String sig; + private int idx; + + public SignatureIterator(String sig) { + this.sig = sig; + reset(); + } + + public void reset() { + idx = 1; + } + + public boolean atEnd() { + return sig.charAt(idx) == ')'; + } + + public String next() { + if (atEnd()) return null; + char c = sig.charAt(idx); + if (c != '[' && c != 'L') { + ++idx; + return new String(new char[] { c }); + } + // Walk forward to end of entry + int endIdx = idx; + if (c == '[') { + while ((c = sig.charAt(endIdx)) == '[') { + endIdx++; + } + } + + if (c == 'L') { + while (sig.charAt(endIdx) != ';') { + endIdx++; + } + } + + int beginIdx = idx; + idx = endIdx + 1; + return sig.substring(beginIdx, idx); + } + + /** Should only be called when atEnd() is true. Does not change + state of iterator. */ + public String returnType() { + if (!atEnd()) { + throw new InternalError("Illegal use of SignatureIterator"); + } + return sig.substring(idx + 1, sig.length()); + } +} diff --git a/src/sun/reflect/UTF8.java b/src/sun/reflect/UTF8.java new file mode 100644 index 00000000..ae482f32 --- /dev/null +++ b/src/sun/reflect/UTF8.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +/** It is necessary to use a "bootstrap" UTF-8 encoder for encoding + constant pool entries because the character set converters rely on + Class.newInstance(). */ + +class UTF8 { + // This encoder is not quite correct. It does not handle surrogate pairs. + static byte[] encode(String str) { + int len = str.length(); + byte[] res = new byte[utf8Length(str)]; + int utf8Idx = 0; + try { + for (int i = 0; i < len; i++) { + int c = str.charAt(i) & 0xFFFF; + if (c >= 0x0001 && c <= 0x007F) { + res[utf8Idx++] = (byte) c; + } else if (c == 0x0000 || + (c >= 0x0080 && c <= 0x07FF)) { + res[utf8Idx++] = (byte) (0xC0 + (c >> 6)); + res[utf8Idx++] = (byte) (0x80 + (c & 0x3F)); + } else { + res[utf8Idx++] = (byte) (0xE0 + (c >> 12)); + res[utf8Idx++] = (byte) (0x80 + ((c >> 6) & 0x3F)); + res[utf8Idx++] = (byte) (0x80 + (c & 0x3F)); + } + } + } catch (ArrayIndexOutOfBoundsException e) { + throw new InternalError + ("Bug in sun.reflect bootstrap UTF-8 encoder", e); + } + return res; + } + + private static int utf8Length(String str) { + int len = str.length(); + int utf8Len = 0; + for (int i = 0; i < len; i++) { + int c = str.charAt(i) & 0xFFFF; + if (c >= 0x0001 && c <= 0x007F) { + utf8Len += 1; + } else if (c == 0x0000 || + (c >= 0x0080 && c <= 0x07FF)) { + utf8Len += 2; + } else { + utf8Len += 3; + } + } + return utf8Len; + } +} diff --git a/src/sun/reflect/UnsafeBooleanFieldAccessorImpl.java b/src/sun/reflect/UnsafeBooleanFieldAccessorImpl.java new file mode 100644 index 00000000..8c7cdd45 --- /dev/null +++ b/src/sun/reflect/UnsafeBooleanFieldAccessorImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeBooleanFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeBooleanFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Boolean(getBoolean(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getBoolean(obj, fieldOffset); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Boolean) { + unsafe.putBoolean(obj, fieldOffset, ((Boolean) value).booleanValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(z); + } + unsafe.putBoolean(obj, fieldOffset, z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeByteFieldAccessorImpl.java b/src/sun/reflect/UnsafeByteFieldAccessorImpl.java new file mode 100644 index 00000000..8095fc29 --- /dev/null +++ b/src/sun/reflect/UnsafeByteFieldAccessorImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeByteFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeByteFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Byte(getByte(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getByte(obj, fieldOffset); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putByte(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(b); + } + unsafe.putByte(obj, fieldOffset, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeCharacterFieldAccessorImpl.java b/src/sun/reflect/UnsafeCharacterFieldAccessorImpl.java new file mode 100644 index 00000000..bdf83bc2 --- /dev/null +++ b/src/sun/reflect/UnsafeCharacterFieldAccessorImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeCharacterFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeCharacterFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Character(getChar(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getChar(obj, fieldOffset); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Character) { + unsafe.putChar(obj, fieldOffset, ((Character) value).charValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(c); + } + unsafe.putChar(obj, fieldOffset, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeDoubleFieldAccessorImpl.java b/src/sun/reflect/UnsafeDoubleFieldAccessorImpl.java new file mode 100644 index 00000000..df43f596 --- /dev/null +++ b/src/sun/reflect/UnsafeDoubleFieldAccessorImpl.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeDoubleFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeDoubleFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Double(getDouble(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getDouble(obj, fieldOffset); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putDouble(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putDouble(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putDouble(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putDouble(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putDouble(obj, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putDouble(obj, fieldOffset, ((Float) value).floatValue()); + return; + } + if (value instanceof Double) { + unsafe.putDouble(obj, fieldOffset, ((Double) value).doubleValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(d); + } + unsafe.putDouble(obj, fieldOffset, d); + } +} diff --git a/src/sun/reflect/UnsafeFieldAccessorFactory.java b/src/sun/reflect/UnsafeFieldAccessorFactory.java new file mode 100644 index 00000000..72689b3e --- /dev/null +++ b/src/sun/reflect/UnsafeFieldAccessorFactory.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +class UnsafeFieldAccessorFactory { + static FieldAccessor newFieldAccessor(Field field, boolean override) { + Class type = field.getType(); + boolean isStatic = Modifier.isStatic(field.getModifiers()); + boolean isFinal = Modifier.isFinal(field.getModifiers()); + boolean isVolatile = Modifier.isVolatile(field.getModifiers()); + boolean isQualified = isFinal || isVolatile; + boolean isReadOnly = isFinal && (isStatic || !override); + if (isStatic) { + // This code path does not guarantee that the field's + // declaring class has been initialized, but it must be + // before performing reflective operations. + UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(field.getDeclaringClass()); + + if (!isQualified) { + if (type == Boolean.TYPE) { + return new UnsafeStaticBooleanFieldAccessorImpl(field); + } else if (type == Byte.TYPE) { + return new UnsafeStaticByteFieldAccessorImpl(field); + } else if (type == Short.TYPE) { + return new UnsafeStaticShortFieldAccessorImpl(field); + } else if (type == Character.TYPE) { + return new UnsafeStaticCharacterFieldAccessorImpl(field); + } else if (type == Integer.TYPE) { + return new UnsafeStaticIntegerFieldAccessorImpl(field); + } else if (type == Long.TYPE) { + return new UnsafeStaticLongFieldAccessorImpl(field); + } else if (type == Float.TYPE) { + return new UnsafeStaticFloatFieldAccessorImpl(field); + } else if (type == Double.TYPE) { + return new UnsafeStaticDoubleFieldAccessorImpl(field); + } else { + return new UnsafeStaticObjectFieldAccessorImpl(field); + } + } else { + if (type == Boolean.TYPE) { + return new UnsafeQualifiedStaticBooleanFieldAccessorImpl(field, isReadOnly); + } else if (type == Byte.TYPE) { + return new UnsafeQualifiedStaticByteFieldAccessorImpl(field, isReadOnly); + } else if (type == Short.TYPE) { + return new UnsafeQualifiedStaticShortFieldAccessorImpl(field, isReadOnly); + } else if (type == Character.TYPE) { + return new UnsafeQualifiedStaticCharacterFieldAccessorImpl(field, isReadOnly); + } else if (type == Integer.TYPE) { + return new UnsafeQualifiedStaticIntegerFieldAccessorImpl(field, isReadOnly); + } else if (type == Long.TYPE) { + return new UnsafeQualifiedStaticLongFieldAccessorImpl(field, isReadOnly); + } else if (type == Float.TYPE) { + return new UnsafeQualifiedStaticFloatFieldAccessorImpl(field, isReadOnly); + } else if (type == Double.TYPE) { + return new UnsafeQualifiedStaticDoubleFieldAccessorImpl(field, isReadOnly); + } else { + return new UnsafeQualifiedStaticObjectFieldAccessorImpl(field, isReadOnly); + } + } + } else { + if (!isQualified) { + if (type == Boolean.TYPE) { + return new UnsafeBooleanFieldAccessorImpl(field); + } else if (type == Byte.TYPE) { + return new UnsafeByteFieldAccessorImpl(field); + } else if (type == Short.TYPE) { + return new UnsafeShortFieldAccessorImpl(field); + } else if (type == Character.TYPE) { + return new UnsafeCharacterFieldAccessorImpl(field); + } else if (type == Integer.TYPE) { + return new UnsafeIntegerFieldAccessorImpl(field); + } else if (type == Long.TYPE) { + return new UnsafeLongFieldAccessorImpl(field); + } else if (type == Float.TYPE) { + return new UnsafeFloatFieldAccessorImpl(field); + } else if (type == Double.TYPE) { + return new UnsafeDoubleFieldAccessorImpl(field); + } else { + return new UnsafeObjectFieldAccessorImpl(field); + } + } else { + if (type == Boolean.TYPE) { + return new UnsafeQualifiedBooleanFieldAccessorImpl(field, isReadOnly); + } else if (type == Byte.TYPE) { + return new UnsafeQualifiedByteFieldAccessorImpl(field, isReadOnly); + } else if (type == Short.TYPE) { + return new UnsafeQualifiedShortFieldAccessorImpl(field, isReadOnly); + } else if (type == Character.TYPE) { + return new UnsafeQualifiedCharacterFieldAccessorImpl(field, isReadOnly); + } else if (type == Integer.TYPE) { + return new UnsafeQualifiedIntegerFieldAccessorImpl(field, isReadOnly); + } else if (type == Long.TYPE) { + return new UnsafeQualifiedLongFieldAccessorImpl(field, isReadOnly); + } else if (type == Float.TYPE) { + return new UnsafeQualifiedFloatFieldAccessorImpl(field, isReadOnly); + } else if (type == Double.TYPE) { + return new UnsafeQualifiedDoubleFieldAccessorImpl(field, isReadOnly); + } else { + return new UnsafeQualifiedObjectFieldAccessorImpl(field, isReadOnly); + } + } + } + } +} diff --git a/src/sun/reflect/UnsafeFieldAccessorImpl.java b/src/sun/reflect/UnsafeFieldAccessorImpl.java new file mode 100644 index 00000000..5bfa697a --- /dev/null +++ b/src/sun/reflect/UnsafeFieldAccessorImpl.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import sun.misc.Unsafe; + +/** Base class for sun.misc.Unsafe-based FieldAccessors. The + observation is that there are only nine types of fields from the + standpoint of reflection code: the eight primitive types and + Object. Using class Unsafe instead of generated bytecodes saves + memory and loading time for the dynamically-generated + FieldAccessors. */ + +abstract class UnsafeFieldAccessorImpl extends FieldAccessorImpl { + static final Unsafe unsafe = Unsafe.getUnsafe(); + + protected final Field field; + protected final long fieldOffset; + protected final boolean isFinal; + + UnsafeFieldAccessorImpl(Field field) { + this.field = field; + if (Modifier.isStatic(field.getModifiers())) + fieldOffset = unsafe.staticFieldOffset(field); + else + fieldOffset = unsafe.objectFieldOffset(field); + isFinal = Modifier.isFinal(field.getModifiers()); + } + + protected void ensureObj(Object o) { + // NOTE: will throw NullPointerException, as specified, if o is null + if (!field.getDeclaringClass().isAssignableFrom(o.getClass())) { + throwSetIllegalArgumentException(o); + } + } + + private String getQualifiedFieldName() { + return field.getDeclaringClass().getName() + "." +field.getName(); + } + + protected IllegalArgumentException newGetIllegalArgumentException(String type) { + return new IllegalArgumentException( + "Attempt to get "+field.getType().getName()+" field \"" + + getQualifiedFieldName() + "\" with illegal data type conversion to "+type + ); + } + + protected void throwFinalFieldIllegalAccessException(String attemptedType, + String attemptedValue) + throws IllegalAccessException { + throw new IllegalAccessException(getSetMessage(attemptedType, attemptedValue)); + + } + protected void throwFinalFieldIllegalAccessException(Object o) throws IllegalAccessException { + throwFinalFieldIllegalAccessException(o != null ? o.getClass().getName() : "", ""); + } + + protected void throwFinalFieldIllegalAccessException(boolean z) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("boolean", Boolean.toString(z)); + } + + protected void throwFinalFieldIllegalAccessException(char b) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("char", Character.toString(b)); + } + + protected void throwFinalFieldIllegalAccessException(byte b) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("byte", Byte.toString(b)); + } + + protected void throwFinalFieldIllegalAccessException(short b) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("short", Short.toString(b)); + } + + protected void throwFinalFieldIllegalAccessException(int i) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("int", Integer.toString(i)); + } + + protected void throwFinalFieldIllegalAccessException(long i) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("long", Long.toString(i)); + } + + protected void throwFinalFieldIllegalAccessException(float f) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("float", Float.toString(f)); + } + + protected void throwFinalFieldIllegalAccessException(double f) throws IllegalAccessException { + throwFinalFieldIllegalAccessException("double", Double.toString(f)); + } + + protected IllegalArgumentException newGetBooleanIllegalArgumentException() { + return newGetIllegalArgumentException("boolean"); + } + + protected IllegalArgumentException newGetByteIllegalArgumentException() { + return newGetIllegalArgumentException("byte"); + } + + protected IllegalArgumentException newGetCharIllegalArgumentException() { + return newGetIllegalArgumentException("char"); + } + + protected IllegalArgumentException newGetShortIllegalArgumentException() { + return newGetIllegalArgumentException("short"); + } + + protected IllegalArgumentException newGetIntIllegalArgumentException() { + return newGetIllegalArgumentException("int"); + } + + protected IllegalArgumentException newGetLongIllegalArgumentException() { + return newGetIllegalArgumentException("long"); + } + + protected IllegalArgumentException newGetFloatIllegalArgumentException() { + return newGetIllegalArgumentException("float"); + } + + protected IllegalArgumentException newGetDoubleIllegalArgumentException() { + return newGetIllegalArgumentException("double"); + } + + protected String getSetMessage(String attemptedType, String attemptedValue) { + String err = "Can not set"; + if (Modifier.isStatic(field.getModifiers())) + err += " static"; + if (isFinal) + err += " final"; + err += " " + field.getType().getName() + " field " + getQualifiedFieldName() + " to "; + if (attemptedValue.length() > 0) { + err += "(" + attemptedType + ")" + attemptedValue; + } else { + if (attemptedType.length() > 0) + err += attemptedType; + else + err += "null value"; + } + return err; + } + + protected void throwSetIllegalArgumentException(String attemptedType, + String attemptedValue) { + throw new IllegalArgumentException(getSetMessage(attemptedType,attemptedValue)); + } + + protected void throwSetIllegalArgumentException(Object o) { + throwSetIllegalArgumentException(o != null ? o.getClass().getName() : "", ""); + } + + protected void throwSetIllegalArgumentException(boolean b) { + throwSetIllegalArgumentException("boolean", Boolean.toString(b)); + } + + protected void throwSetIllegalArgumentException(byte b) { + throwSetIllegalArgumentException("byte", Byte.toString(b)); + } + + protected void throwSetIllegalArgumentException(char c) { + throwSetIllegalArgumentException("char", Character.toString(c)); + } + + protected void throwSetIllegalArgumentException(short s) { + throwSetIllegalArgumentException("short", Short.toString(s)); + } + + protected void throwSetIllegalArgumentException(int i) { + throwSetIllegalArgumentException("int", Integer.toString(i)); + } + + protected void throwSetIllegalArgumentException(long l) { + throwSetIllegalArgumentException("long", Long.toString(l)); + } + + protected void throwSetIllegalArgumentException(float f) { + throwSetIllegalArgumentException("float", Float.toString(f)); + } + + protected void throwSetIllegalArgumentException(double d) { + throwSetIllegalArgumentException("double", Double.toString(d)); + } + +} diff --git a/src/sun/reflect/UnsafeFloatFieldAccessorImpl.java b/src/sun/reflect/UnsafeFloatFieldAccessorImpl.java new file mode 100644 index 00000000..ca7789ac --- /dev/null +++ b/src/sun/reflect/UnsafeFloatFieldAccessorImpl.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeFloatFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeFloatFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Float(getFloat(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getFloat(obj, fieldOffset); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getFloat(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putFloat(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putFloat(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putFloat(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putFloat(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putFloat(obj, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putFloat(obj, fieldOffset, ((Float) value).floatValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(f); + } + unsafe.putFloat(obj, fieldOffset, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeIntegerFieldAccessorImpl.java b/src/sun/reflect/UnsafeIntegerFieldAccessorImpl.java new file mode 100644 index 00000000..951a2202 --- /dev/null +++ b/src/sun/reflect/UnsafeIntegerFieldAccessorImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeIntegerFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeIntegerFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Integer(getInt(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getInt(obj, fieldOffset); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putInt(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putInt(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putInt(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putInt(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(i); + } + unsafe.putInt(obj, fieldOffset, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeLongFieldAccessorImpl.java b/src/sun/reflect/UnsafeLongFieldAccessorImpl.java new file mode 100644 index 00000000..85fcf328 --- /dev/null +++ b/src/sun/reflect/UnsafeLongFieldAccessorImpl.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeLongFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeLongFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Long(getLong(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getLong(obj, fieldOffset); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putLong(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putLong(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putLong(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putLong(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putLong(obj, fieldOffset, ((Long) value).longValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(l); + } + unsafe.putLong(obj, fieldOffset, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeObjectFieldAccessorImpl.java b/src/sun/reflect/UnsafeObjectFieldAccessorImpl.java new file mode 100644 index 00000000..bb5b5b6c --- /dev/null +++ b/src/sun/reflect/UnsafeObjectFieldAccessorImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeObjectFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeObjectFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getObject(obj, fieldOffset); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value != null) { + if (!field.getType().isAssignableFrom(value.getClass())) { + throwSetIllegalArgumentException(value); + } + } + unsafe.putObject(obj, fieldOffset, value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java new file mode 100644 index 00000000..8e66c7fe --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedBooleanFieldAccessorImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedBooleanFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedBooleanFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Boolean(getBoolean(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getBooleanVolatile(obj, fieldOffset); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Boolean) { + unsafe.putBooleanVolatile(obj, fieldOffset, ((Boolean) value).booleanValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(z); + } + unsafe.putBooleanVolatile(obj, fieldOffset, z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java new file mode 100644 index 00000000..f3cc9280 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedByteFieldAccessorImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedByteFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedByteFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Byte(getByte(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getByteVolatile(obj, fieldOffset); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putByteVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(b); + } + unsafe.putByteVolatile(obj, fieldOffset, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java new file mode 100644 index 00000000..247e3b23 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedCharacterFieldAccessorImpl.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedCharacterFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedCharacterFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Character(getChar(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getCharVolatile(obj, fieldOffset); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Character) { + unsafe.putCharVolatile(obj, fieldOffset, ((Character) value).charValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(c); + } + unsafe.putCharVolatile(obj, fieldOffset, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java new file mode 100644 index 00000000..c7b2ae1f --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedDoubleFieldAccessorImpl.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedDoubleFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedDoubleFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Double(getDouble(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getDoubleVolatile(obj, fieldOffset); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Float) value).floatValue()); + return; + } + if (value instanceof Double) { + unsafe.putDoubleVolatile(obj, fieldOffset, ((Double) value).doubleValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(d); + } + unsafe.putDoubleVolatile(obj, fieldOffset, d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java new file mode 100644 index 00000000..8c547a09 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedFieldAccessorImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +/** + * Base class for sun.misc.Unsafe-based FieldAccessors for fields with + * final or volatile qualifiers. These differ from unqualified + * versions in that (1) they check for read-only status (2) they use + * the volatile forms of Unsafe get/put methods. (When accessed via + * reflection, finals act as slightly "lighter" forms of volatiles. So + * the volatile forms are heavier than necessary in terms of + * underlying reordering rules and memory barriers, but preserve + * correctness.) + */ + +abstract class UnsafeQualifiedFieldAccessorImpl + extends UnsafeFieldAccessorImpl +{ + protected final boolean isReadOnly; + + UnsafeQualifiedFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field); + this.isReadOnly = isReadOnly; + } +} diff --git a/src/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java new file mode 100644 index 00000000..c06af79e --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedFloatFieldAccessorImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedFloatFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedFloatFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Float(getFloat(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getFloatVolatile(obj, fieldOffset); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getFloat(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putFloatVolatile(obj, fieldOffset, ((Float) value).floatValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(f); + } + unsafe.putFloatVolatile(obj, fieldOffset, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java new file mode 100644 index 00000000..b6751583 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedIntegerFieldAccessorImpl.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedIntegerFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedIntegerFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Integer(getInt(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getIntVolatile(obj, fieldOffset); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putIntVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putIntVolatile(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putIntVolatile(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putIntVolatile(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(i); + } + unsafe.putIntVolatile(obj, fieldOffset, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java new file mode 100644 index 00000000..50fa38dc --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedLongFieldAccessorImpl.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedLongFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedLongFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Long(getLong(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getLongVolatile(obj, fieldOffset); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putLongVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putLongVolatile(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putLongVolatile(obj, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putLongVolatile(obj, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putLongVolatile(obj, fieldOffset, ((Long) value).longValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(l); + } + unsafe.putLongVolatile(obj, fieldOffset, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java new file mode 100644 index 00000000..1a502ab7 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedObjectFieldAccessorImpl.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedObjectFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedObjectFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getObjectVolatile(obj, fieldOffset); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value != null) { + if (!field.getType().isAssignableFrom(value.getClass())) { + throwSetIllegalArgumentException(value); + } + } + unsafe.putObjectVolatile(obj, fieldOffset, value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java new file mode 100644 index 00000000..fddfef24 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedShortFieldAccessorImpl.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedShortFieldAccessorImpl + extends UnsafeQualifiedFieldAccessorImpl +{ + UnsafeQualifiedShortFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Short(getShort(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getShortVolatile(obj, fieldOffset); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putShortVolatile(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putShortVolatile(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setShort(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isReadOnly) { + throwFinalFieldIllegalAccessException(s); + } + unsafe.putShortVolatile(obj, fieldOffset, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java new file mode 100644 index 00000000..5b1e3f3e --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticBooleanFieldAccessorImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticBooleanFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticBooleanFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Boolean(getBoolean(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + return unsafe.getBooleanVolatile(base, fieldOffset); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Boolean) { + unsafe.putBooleanVolatile(base, fieldOffset, ((Boolean) value).booleanValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(z); + } + unsafe.putBooleanVolatile(base, fieldOffset, z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java new file mode 100644 index 00000000..a3e087e3 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticByteFieldAccessorImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticByteFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticByteFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Byte(getByte(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + return unsafe.getByteVolatile(base, fieldOffset); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putByteVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(b); + } + unsafe.putByteVolatile(base, fieldOffset, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java new file mode 100644 index 00000000..c171064e --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticCharacterFieldAccessorImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticCharacterFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticCharacterFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Character(getChar(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + return unsafe.getCharVolatile(base, fieldOffset); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Character) { + unsafe.putCharVolatile(base, fieldOffset, ((Character) value).charValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(c); + } + unsafe.putCharVolatile(base, fieldOffset, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java new file mode 100644 index 00000000..4acd9f52 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticDoubleFieldAccessorImpl.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticDoubleFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticDoubleFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Double(getDouble(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return unsafe.getDoubleVolatile(base, fieldOffset); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Float) value).floatValue()); + return; + } + if (value instanceof Double) { + unsafe.putDoubleVolatile(base, fieldOffset, ((Double) value).doubleValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(d); + } + unsafe.putDoubleVolatile(base, fieldOffset, d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java new file mode 100644 index 00000000..3047fe4c --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticFieldAccessorImpl.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +/** Base class for sun.misc.Unsafe-based FieldAccessors for final or + volatile static fields. */ + +abstract class UnsafeQualifiedStaticFieldAccessorImpl + extends UnsafeStaticFieldAccessorImpl +{ + protected final boolean isReadOnly; + + UnsafeQualifiedStaticFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field); + this.isReadOnly = isReadOnly; + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java new file mode 100644 index 00000000..86eb74e8 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticFloatFieldAccessorImpl.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticFloatFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticFloatFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Float(getFloat(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return unsafe.getFloatVolatile(base, fieldOffset); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getFloat(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putFloatVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putFloatVolatile(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putFloatVolatile(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putFloatVolatile(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putFloatVolatile(base, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putFloatVolatile(base, fieldOffset, ((Float) value).floatValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(f); + } + unsafe.putFloatVolatile(base, fieldOffset, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java new file mode 100644 index 00000000..7ddcd8c6 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticIntegerFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticIntegerFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Integer(getInt(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return unsafe.getIntVolatile(base, fieldOffset); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putIntVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putIntVolatile(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putIntVolatile(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putIntVolatile(base, fieldOffset, ((Integer) value).intValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(i); + } + unsafe.putIntVolatile(base, fieldOffset, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java new file mode 100644 index 00000000..97da2f25 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticLongFieldAccessorImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticLongFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticLongFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Long(getLong(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return unsafe.getLongVolatile(base, fieldOffset); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putLongVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putLongVolatile(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putLongVolatile(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putLongVolatile(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putLongVolatile(base, fieldOffset, ((Long) value).longValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(l); + } + unsafe.putLongVolatile(base, fieldOffset, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java new file mode 100644 index 00000000..8bea19e8 --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticObjectFieldAccessorImpl.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticObjectFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticObjectFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return unsafe.getObjectVolatile(base, fieldOffset); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value != null) { + if (!field.getType().isAssignableFrom(value.getClass())) { + throwSetIllegalArgumentException(value); + } + } + unsafe.putObjectVolatile(base, fieldOffset, value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java b/src/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java new file mode 100644 index 00000000..4caf4d1d --- /dev/null +++ b/src/sun/reflect/UnsafeQualifiedStaticShortFieldAccessorImpl.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeQualifiedStaticShortFieldAccessorImpl + extends UnsafeQualifiedStaticFieldAccessorImpl +{ + UnsafeQualifiedStaticShortFieldAccessorImpl(Field field, boolean isReadOnly) { + super(field, isReadOnly); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Short(getShort(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return unsafe.getShortVolatile(base, fieldOffset); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putShortVolatile(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putShortVolatile(base, fieldOffset, ((Short) value).shortValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setShort(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + if (isReadOnly) { + throwFinalFieldIllegalAccessException(s); + } + unsafe.putShortVolatile(base, fieldOffset, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeShortFieldAccessorImpl.java b/src/sun/reflect/UnsafeShortFieldAccessorImpl.java new file mode 100644 index 00000000..d8fe8dc9 --- /dev/null +++ b/src/sun/reflect/UnsafeShortFieldAccessorImpl.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeShortFieldAccessorImpl extends UnsafeFieldAccessorImpl { + UnsafeShortFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Short(getShort(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + ensureObj(obj); + return unsafe.getShort(obj, fieldOffset); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putShort(obj, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putShort(obj, fieldOffset, ((Short) value).shortValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setShort(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + ensureObj(obj); + if (isFinal) { + throwFinalFieldIllegalAccessException(s); + } + unsafe.putShort(obj, fieldOffset, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java new file mode 100644 index 00000000..741c5ff4 --- /dev/null +++ b/src/sun/reflect/UnsafeStaticBooleanFieldAccessorImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticBooleanFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticBooleanFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Boolean(getBoolean(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + return unsafe.getBoolean(base, fieldOffset); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Boolean) { + unsafe.putBoolean(base, fieldOffset, ((Boolean) value).booleanValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(z); + } + unsafe.putBoolean(base, fieldOffset, z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java new file mode 100644 index 00000000..726961ba --- /dev/null +++ b/src/sun/reflect/UnsafeStaticByteFieldAccessorImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticByteFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticByteFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Byte(getByte(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + return unsafe.getByte(base, fieldOffset); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getByte(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putByte(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(b); + } + unsafe.putByte(base, fieldOffset, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java new file mode 100644 index 00000000..29b03037 --- /dev/null +++ b/src/sun/reflect/UnsafeStaticCharacterFieldAccessorImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticCharacterFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticCharacterFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Character(getChar(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + return unsafe.getChar(base, fieldOffset); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getChar(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Character) { + unsafe.putChar(base, fieldOffset, ((Character) value).charValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(c); + } + unsafe.putChar(base, fieldOffset, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java new file mode 100644 index 00000000..34c97844 --- /dev/null +++ b/src/sun/reflect/UnsafeStaticDoubleFieldAccessorImpl.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticDoubleFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticDoubleFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Double(getDouble(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return unsafe.getDouble(base, fieldOffset); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putDouble(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putDouble(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putDouble(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putDouble(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putDouble(base, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putDouble(base, fieldOffset, ((Float) value).floatValue()); + return; + } + if (value instanceof Double) { + unsafe.putDouble(base, fieldOffset, ((Double) value).doubleValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + setDouble(obj, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(d); + } + unsafe.putDouble(base, fieldOffset, d); + } +} diff --git a/src/sun/reflect/UnsafeStaticFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticFieldAccessorImpl.java new file mode 100644 index 00000000..ef7d0bfd --- /dev/null +++ b/src/sun/reflect/UnsafeStaticFieldAccessorImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +/** Base class for sun.misc.Unsafe-based FieldAccessors for static + fields. The observation is that there are only nine types of + fields from the standpoint of reflection code: the eight primitive + types and Object. Using class Unsafe instead of generated + bytecodes saves memory and loading time for the + dynamically-generated FieldAccessors. */ + +abstract class UnsafeStaticFieldAccessorImpl extends UnsafeFieldAccessorImpl { + static { + Reflection.registerFieldsToFilter(UnsafeStaticFieldAccessorImpl.class, + new String[] { "base" }); + } + + protected final Object base; // base + + UnsafeStaticFieldAccessorImpl(Field field) { + super(field); + base = unsafe.staticFieldBase(field); + } +} diff --git a/src/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java new file mode 100644 index 00000000..780a794c --- /dev/null +++ b/src/sun/reflect/UnsafeStaticFloatFieldAccessorImpl.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticFloatFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticFloatFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Float(getFloat(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return unsafe.getFloat(base, fieldOffset); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getFloat(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putFloat(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putFloat(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putFloat(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putFloat(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putFloat(base, fieldOffset, ((Long) value).longValue()); + return; + } + if (value instanceof Float) { + unsafe.putFloat(base, fieldOffset, ((Float) value).floatValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + setFloat(obj, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(f); + } + unsafe.putFloat(base, fieldOffset, f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java new file mode 100644 index 00000000..1b21b766 --- /dev/null +++ b/src/sun/reflect/UnsafeStaticIntegerFieldAccessorImpl.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticIntegerFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticIntegerFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Integer(getInt(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return unsafe.getInt(base, fieldOffset); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getInt(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putInt(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putInt(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putInt(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putInt(base, fieldOffset, ((Integer) value).intValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setInt(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(i); + } + unsafe.putInt(base, fieldOffset, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java new file mode 100644 index 00000000..7d50ac2d --- /dev/null +++ b/src/sun/reflect/UnsafeStaticLongFieldAccessorImpl.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticLongFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticLongFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Long(getLong(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return unsafe.getLong(base, fieldOffset); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getLong(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putLong(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putLong(base, fieldOffset, ((Short) value).shortValue()); + return; + } + if (value instanceof Character) { + unsafe.putLong(base, fieldOffset, ((Character) value).charValue()); + return; + } + if (value instanceof Integer) { + unsafe.putLong(base, fieldOffset, ((Integer) value).intValue()); + return; + } + if (value instanceof Long) { + unsafe.putLong(base, fieldOffset, ((Long) value).longValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + setLong(obj, i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(l); + } + unsafe.putLong(base, fieldOffset, l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticObjectFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticObjectFieldAccessorImpl.java new file mode 100644 index 00000000..e8b4548e --- /dev/null +++ b/src/sun/reflect/UnsafeStaticObjectFieldAccessorImpl.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticObjectFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticObjectFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return unsafe.getObject(base, fieldOffset); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + throw newGetShortIllegalArgumentException(); + } + + public int getInt(Object obj) throws IllegalArgumentException { + throw newGetIntIllegalArgumentException(); + } + + public long getLong(Object obj) throws IllegalArgumentException { + throw newGetLongIllegalArgumentException(); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + throw newGetFloatIllegalArgumentException(); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + throw newGetDoubleIllegalArgumentException(); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value != null) { + if (!field.getType().isAssignableFrom(value.getClass())) { + throwSetIllegalArgumentException(value); + } + } + unsafe.putObject(base, fieldOffset, value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java b/src/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java new file mode 100644 index 00000000..d56d621e --- /dev/null +++ b/src/sun/reflect/UnsafeStaticShortFieldAccessorImpl.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect; + +import java.lang.reflect.Field; + +class UnsafeStaticShortFieldAccessorImpl extends UnsafeStaticFieldAccessorImpl { + UnsafeStaticShortFieldAccessorImpl(Field field) { + super(field); + } + + public Object get(Object obj) throws IllegalArgumentException { + return new Short(getShort(obj)); + } + + public boolean getBoolean(Object obj) throws IllegalArgumentException { + throw newGetBooleanIllegalArgumentException(); + } + + public byte getByte(Object obj) throws IllegalArgumentException { + throw newGetByteIllegalArgumentException(); + } + + public char getChar(Object obj) throws IllegalArgumentException { + throw newGetCharIllegalArgumentException(); + } + + public short getShort(Object obj) throws IllegalArgumentException { + return unsafe.getShort(base, fieldOffset); + } + + public int getInt(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public long getLong(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public float getFloat(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public double getDouble(Object obj) throws IllegalArgumentException { + return getShort(obj); + } + + public void set(Object obj, Object value) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(value); + } + if (value == null) { + throwSetIllegalArgumentException(value); + } + if (value instanceof Byte) { + unsafe.putShort(base, fieldOffset, ((Byte) value).byteValue()); + return; + } + if (value instanceof Short) { + unsafe.putShort(base, fieldOffset, ((Short) value).shortValue()); + return; + } + throwSetIllegalArgumentException(value); + } + + public void setBoolean(Object obj, boolean z) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(z); + } + + public void setByte(Object obj, byte b) + throws IllegalArgumentException, IllegalAccessException + { + setShort(obj, b); + } + + public void setChar(Object obj, char c) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(c); + } + + public void setShort(Object obj, short s) + throws IllegalArgumentException, IllegalAccessException + { + if (isFinal) { + throwFinalFieldIllegalAccessException(s); + } + unsafe.putShort(base, fieldOffset, s); + } + + public void setInt(Object obj, int i) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(i); + } + + public void setLong(Object obj, long l) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(l); + } + + public void setFloat(Object obj, float f) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(f); + } + + public void setDouble(Object obj, double d) + throws IllegalArgumentException, IllegalAccessException + { + throwSetIllegalArgumentException(d); + } +} diff --git a/src/sun/reflect/annotation/AnnotatedTypeFactory.java b/src/sun/reflect/annotation/AnnotatedTypeFactory.java new file mode 100644 index 00000000..196bec68 --- /dev/null +++ b/src/sun/reflect/annotation/AnnotatedTypeFactory.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import static sun.reflect.annotation.TypeAnnotation.LocationInfo; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedArrayType; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AnnotatedTypeVariable; +import java.lang.reflect.AnnotatedWildcardType; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public final class AnnotatedTypeFactory { + /** + * Create an AnnotatedType. + * + * @param type the type this AnnotatedType corresponds to + * @param currentLoc the location this AnnotatedType corresponds to + * @param actualTypeAnnos the type annotations this AnnotatedType has + * @param allOnSameTarget all type annotation on the same TypeAnnotationTarget + * as the AnnotatedType being built + * @param decl the declaration having the type use this AnnotatedType + * corresponds to + */ + public static AnnotatedType buildAnnotatedType(Type type, + LocationInfo currentLoc, + TypeAnnotation[] actualTypeAnnos, + TypeAnnotation[] allOnSameTarget, + AnnotatedElement decl) { + if (type == null) { + return EMPTY_ANNOTATED_TYPE; + } + if (isArray(type)) + return new AnnotatedArrayTypeImpl(type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + if (type instanceof Class) { + return new AnnotatedTypeBaseImpl(type, + addNesting(type, currentLoc), + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof TypeVariable) { + return new AnnotatedTypeVariableImpl((TypeVariable)type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof ParameterizedType) { + return new AnnotatedParameterizedTypeImpl((ParameterizedType)type, + addNesting(type, currentLoc), + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof WildcardType) { + return new AnnotatedWildcardTypeImpl((WildcardType) type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + } + throw new AssertionError("Unknown instance of Type: " + type + "\nThis should not happen."); + } + + private static LocationInfo addNesting(Type type, LocationInfo addTo) { + if (isArray(type)) + return addTo; + if (type instanceof Class) { + Class clz = (Class)type; + if (clz.getEnclosingClass() == null) + return addTo; + if (Modifier.isStatic(clz.getModifiers())) + return addNesting(clz.getEnclosingClass(), addTo); + return addNesting(clz.getEnclosingClass(), addTo.pushInner()); + } else if (type instanceof ParameterizedType) { + ParameterizedType t = (ParameterizedType)type; + if (t.getOwnerType() == null) + return addTo; + return addNesting(t.getOwnerType(), addTo.pushInner()); + } + return addTo; + } + + private static boolean isArray(Type t) { + if (t instanceof Class) { + Class c = (Class)t; + if (c.isArray()) + return true; + } else if (t instanceof GenericArrayType) { + return true; + } + return false; + } + + static final AnnotatedType EMPTY_ANNOTATED_TYPE = new AnnotatedTypeBaseImpl(null, LocationInfo.BASE_LOCATION, + new TypeAnnotation[0], new TypeAnnotation[0], null); + static final AnnotatedType[] EMPTY_ANNOTATED_TYPE_ARRAY = new AnnotatedType[0]; + + private static class AnnotatedTypeBaseImpl implements AnnotatedType { + private final Type type; + private final AnnotatedElement decl; + private final LocationInfo location; + private final TypeAnnotation[] allOnSameTargetTypeAnnotations; + private final Map, Annotation> annotations; + + AnnotatedTypeBaseImpl(Type type, LocationInfo location, + TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations, + AnnotatedElement decl) { + this.type = type; + this.decl = decl; + this.location = location; + this.allOnSameTargetTypeAnnotations = allOnSameTargetTypeAnnotations; + this.annotations = TypeAnnotationParser.mapTypeAnnotations(location.filter(actualTypeAnnotations)); + } + + // AnnotatedElement + @Override + public final Annotation[] getAnnotations() { + return getDeclaredAnnotations(); + } + + @Override + public final T getAnnotation(Class annotation) { + return getDeclaredAnnotation(annotation); + } + + @Override + public final T[] getAnnotationsByType(Class annotation) { + return getDeclaredAnnotationsByType(annotation); + } + + @Override + public final Annotation[] getDeclaredAnnotations() { + return annotations.values().toArray(new Annotation[0]); + } + + @Override + @SuppressWarnings("unchecked") + public final T getDeclaredAnnotation(Class annotation) { + return (T)annotations.get(annotation); + } + + @Override + public final T[] getDeclaredAnnotationsByType(Class annotation) { + return AnnotationSupport.getDirectlyAndIndirectlyPresent(annotations, annotation); + } + + // AnnotatedType + @Override + public final Type getType() { + return type; + } + + // Implementation details + final LocationInfo getLocation() { + return location; + } + final TypeAnnotation[] getTypeAnnotations() { + return allOnSameTargetTypeAnnotations; + } + final AnnotatedElement getDecl() { + return decl; + } + } + + private static final class AnnotatedArrayTypeImpl extends AnnotatedTypeBaseImpl implements AnnotatedArrayType { + AnnotatedArrayTypeImpl(Type type, LocationInfo location, + TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations, + AnnotatedElement decl) { + super(type, location, actualTypeAnnotations, allOnSameTargetTypeAnnotations, decl); + } + + @Override + public AnnotatedType getAnnotatedGenericComponentType() { + return AnnotatedTypeFactory.buildAnnotatedType(getComponentType(), + getLocation().pushArray(), + getTypeAnnotations(), + getTypeAnnotations(), + getDecl()); + } + + private Type getComponentType() { + Type t = getType(); + if (t instanceof Class) { + Class c = (Class)t; + return c.getComponentType(); + } + return ((GenericArrayType)t).getGenericComponentType(); + } + } + + private static final class AnnotatedTypeVariableImpl extends AnnotatedTypeBaseImpl implements AnnotatedTypeVariable { + AnnotatedTypeVariableImpl(TypeVariable type, LocationInfo location, + TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations, + AnnotatedElement decl) { + super(type, location, actualTypeAnnotations, allOnSameTargetTypeAnnotations, decl); + } + + @Override + public AnnotatedType[] getAnnotatedBounds() { + return getTypeVariable().getAnnotatedBounds(); + } + + private TypeVariable getTypeVariable() { + return (TypeVariable)getType(); + } + } + + private static final class AnnotatedParameterizedTypeImpl extends AnnotatedTypeBaseImpl + implements AnnotatedParameterizedType { + AnnotatedParameterizedTypeImpl(ParameterizedType type, LocationInfo location, + TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations, + AnnotatedElement decl) { + super(type, location, actualTypeAnnotations, allOnSameTargetTypeAnnotations, decl); + } + + @Override + public AnnotatedType[] getAnnotatedActualTypeArguments() { + Type[] arguments = getParameterizedType().getActualTypeArguments(); + AnnotatedType[] res = new AnnotatedType[arguments.length]; + Arrays.fill(res, EMPTY_ANNOTATED_TYPE); + int initialCapacity = getTypeAnnotations().length; + for (int i = 0; i < res.length; i++) { + List l = new ArrayList<>(initialCapacity); + LocationInfo newLoc = getLocation().pushTypeArg((byte)i); + for (TypeAnnotation t : getTypeAnnotations()) + if (t.getLocationInfo().isSameLocationInfo(newLoc)) + l.add(t); + res[i] = buildAnnotatedType(arguments[i], + newLoc, + l.toArray(new TypeAnnotation[0]), + getTypeAnnotations(), + getDecl()); + } + return res; + } + + private ParameterizedType getParameterizedType() { + return (ParameterizedType)getType(); + } + } + + private static final class AnnotatedWildcardTypeImpl extends AnnotatedTypeBaseImpl implements AnnotatedWildcardType { + private final boolean hasUpperBounds; + AnnotatedWildcardTypeImpl(WildcardType type, LocationInfo location, + TypeAnnotation[] actualTypeAnnotations, TypeAnnotation[] allOnSameTargetTypeAnnotations, + AnnotatedElement decl) { + super(type, location, actualTypeAnnotations, allOnSameTargetTypeAnnotations, decl); + hasUpperBounds = (type.getLowerBounds().length == 0); + } + + @Override + public AnnotatedType[] getAnnotatedUpperBounds() { + if (!hasUpperBounds()) + return new AnnotatedType[0]; + return getAnnotatedBounds(getWildcardType().getUpperBounds()); + } + + @Override + public AnnotatedType[] getAnnotatedLowerBounds() { + if (hasUpperBounds) + return new AnnotatedType[0]; + return getAnnotatedBounds(getWildcardType().getLowerBounds()); + } + + private AnnotatedType[] getAnnotatedBounds(Type[] bounds) { + AnnotatedType[] res = new AnnotatedType[bounds.length]; + Arrays.fill(res, EMPTY_ANNOTATED_TYPE); + LocationInfo newLoc = getLocation().pushWildcard(); + int initialCapacity = getTypeAnnotations().length; + for (int i = 0; i < res.length; i++) { + List l = new ArrayList<>(initialCapacity); + for (TypeAnnotation t : getTypeAnnotations()) + if (t.getLocationInfo().isSameLocationInfo(newLoc)) + l.add(t); + res[i] = buildAnnotatedType(bounds[i], + newLoc, + l.toArray(new TypeAnnotation[0]), + getTypeAnnotations(), + getDecl()); + } + return res; + } + + private WildcardType getWildcardType() { + return (WildcardType)getType(); + } + + private boolean hasUpperBounds() { + return hasUpperBounds; + } + } +} diff --git a/src/sun/reflect/annotation/AnnotationInvocationHandler.java b/src/sun/reflect/annotation/AnnotationInvocationHandler.java new file mode 100644 index 00000000..3008680c --- /dev/null +++ b/src/sun/reflect/annotation/AnnotationInvocationHandler.java @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.annotation.IncompleteAnnotationException; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Map; + +/** + * InvocationHandler for dynamic proxy implementation of Annotation. + * + * @author Josh Bloch + * @since 1.5 + */ +class AnnotationInvocationHandler implements InvocationHandler, Serializable { + private static final long serialVersionUID = 6182022883658399397L; + private final Class type; + private final Map memberValues; + + AnnotationInvocationHandler(Class type, Map memberValues) { + Class[] superInterfaces = type.getInterfaces(); + if (!type.isAnnotation() || + superInterfaces.length != 1 || + superInterfaces[0] != Annotation.class) + throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); + this.type = type; + this.memberValues = memberValues; + } + + public Object invoke(Object proxy, Method method, Object[] args) { + String member = method.getName(); + Class[] paramTypes = method.getParameterTypes(); + + // Handle Object and Annotation methods + if (member.equals("equals") && paramTypes.length == 1 && + paramTypes[0] == Object.class) + return equalsImpl(args[0]); + if (paramTypes.length != 0) + throw new AssertionError("Too many parameters for an annotation method"); + + switch(member) { + case "toString": + return toStringImpl(); + case "hashCode": + return hashCodeImpl(); + case "annotationType": + return type; + } + + // Handle annotation member accessors + Object result = memberValues.get(member); + + if (result == null) + throw new IncompleteAnnotationException(type, member); + + if (result instanceof ExceptionProxy) + throw ((ExceptionProxy) result).generateException(); + + if (result.getClass().isArray() && Array.getLength(result) != 0) + result = cloneArray(result); + + return result; + } + + /** + * This method, which clones its array argument, would not be necessary + * if Cloneable had a public clone method. + */ + private Object cloneArray(Object array) { + Class type = array.getClass(); + + if (type == byte[].class) { + byte[] byteArray = (byte[])array; + return byteArray.clone(); + } + if (type == char[].class) { + char[] charArray = (char[])array; + return charArray.clone(); + } + if (type == double[].class) { + double[] doubleArray = (double[])array; + return doubleArray.clone(); + } + if (type == float[].class) { + float[] floatArray = (float[])array; + return floatArray.clone(); + } + if (type == int[].class) { + int[] intArray = (int[])array; + return intArray.clone(); + } + if (type == long[].class) { + long[] longArray = (long[])array; + return longArray.clone(); + } + if (type == short[].class) { + short[] shortArray = (short[])array; + return shortArray.clone(); + } + if (type == boolean[].class) { + boolean[] booleanArray = (boolean[])array; + return booleanArray.clone(); + } + + Object[] objectArray = (Object[])array; + return objectArray.clone(); + } + + + /** + * Implementation of dynamicProxy.toString() + */ + private String toStringImpl() { + StringBuilder result = new StringBuilder(128); + result.append('@'); + result.append(type.getName()); + result.append('('); + boolean firstMember = true; + for (Map.Entry e : memberValues.entrySet()) { + if (firstMember) + firstMember = false; + else + result.append(", "); + + result.append(e.getKey()); + result.append('='); + result.append(memberValueToString(e.getValue())); + } + result.append(')'); + return result.toString(); + } + + /** + * Translates a member value (in "dynamic proxy return form") into a string + */ + private static String memberValueToString(Object value) { + Class type = value.getClass(); + if (!type.isArray()) // primitive, string, class, enum const, + // or annotation + return value.toString(); + + if (type == byte[].class) + return Arrays.toString((byte[]) value); + if (type == char[].class) + return Arrays.toString((char[]) value); + if (type == double[].class) + return Arrays.toString((double[]) value); + if (type == float[].class) + return Arrays.toString((float[]) value); + if (type == int[].class) + return Arrays.toString((int[]) value); + if (type == long[].class) + return Arrays.toString((long[]) value); + if (type == short[].class) + return Arrays.toString((short[]) value); + if (type == boolean[].class) + return Arrays.toString((boolean[]) value); + return Arrays.toString((Object[]) value); + } + + /** + * Implementation of dynamicProxy.equals(Object o) + */ + private Boolean equalsImpl(Object o) { + if (o == this) + return true; + + if (!type.isInstance(o)) + return false; + for (Method memberMethod : getMemberMethods()) { + String member = memberMethod.getName(); + Object ourValue = memberValues.get(member); + Object hisValue = null; + AnnotationInvocationHandler hisHandler = asOneOfUs(o); + if (hisHandler != null) { + hisValue = hisHandler.memberValues.get(member); + } else { + try { + hisValue = memberMethod.invoke(o); + } catch (InvocationTargetException e) { + return false; + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + if (!memberValueEquals(ourValue, hisValue)) + return false; + } + return true; + } + + /** + * Returns an object's invocation handler if that object is a dynamic + * proxy with a handler of type AnnotationInvocationHandler. + * Returns null otherwise. + */ + private AnnotationInvocationHandler asOneOfUs(Object o) { + if (Proxy.isProxyClass(o.getClass())) { + InvocationHandler handler = Proxy.getInvocationHandler(o); + if (handler instanceof AnnotationInvocationHandler) + return (AnnotationInvocationHandler) handler; + } + return null; + } + + /** + * Returns true iff the two member values in "dynamic proxy return form" + * are equal using the appropriate equality function depending on the + * member type. The two values will be of the same type unless one of + * the containing annotations is ill-formed. If one of the containing + * annotations is ill-formed, this method will return false unless the + * two members are identical object references. + */ + private static boolean memberValueEquals(Object v1, Object v2) { + Class type = v1.getClass(); + + // Check for primitive, string, class, enum const, annotation, + // or ExceptionProxy + if (!type.isArray()) + return v1.equals(v2); + + // Check for array of string, class, enum const, annotation, + // or ExceptionProxy + if (v1 instanceof Object[] && v2 instanceof Object[]) + return Arrays.equals((Object[]) v1, (Object[]) v2); + + // Check for ill formed annotation(s) + if (v2.getClass() != type) + return false; + + // Deal with array of primitives + if (type == byte[].class) + return Arrays.equals((byte[]) v1, (byte[]) v2); + if (type == char[].class) + return Arrays.equals((char[]) v1, (char[]) v2); + if (type == double[].class) + return Arrays.equals((double[]) v1, (double[]) v2); + if (type == float[].class) + return Arrays.equals((float[]) v1, (float[]) v2); + if (type == int[].class) + return Arrays.equals((int[]) v1, (int[]) v2); + if (type == long[].class) + return Arrays.equals((long[]) v1, (long[]) v2); + if (type == short[].class) + return Arrays.equals((short[]) v1, (short[]) v2); + assert type == boolean[].class; + return Arrays.equals((boolean[]) v1, (boolean[]) v2); + } + + /** + * Returns the member methods for our annotation type. These are + * obtained lazily and cached, as they're expensive to obtain + * and we only need them if our equals method is invoked (which should + * be rare). + */ + private Method[] getMemberMethods() { + if (memberMethods == null) { + memberMethods = AccessController.doPrivileged( + new PrivilegedAction() { + public Method[] run() { + final Method[] mm = type.getDeclaredMethods(); + validateAnnotationMethods(mm); + AccessibleObject.setAccessible(mm, true); + return mm; + } + }); + } + return memberMethods; + } + private transient volatile Method[] memberMethods = null; + + /** + * Validates that a method is structurally appropriate for an + * annotation type. As of Java SE 8, annotation types cannot + * contain static methods and the declared methods of an + * annotation type must take zero arguments and there are + * restrictions on the return type. + */ + private void validateAnnotationMethods(Method[] memberMethods) { + /* + * Specification citations below are from JLS + * 9.6.1. Annotation Type Elements + */ + boolean valid = true; + for(Method method : memberMethods) { + /* + * "By virtue of the AnnotationTypeElementDeclaration + * production, a method declaration in an annotation type + * declaration cannot have formal parameters, type + * parameters, or a throws clause. + * + * "By virtue of the AnnotationTypeElementModifier + * production, a method declaration in an annotation type + * declaration cannot be default or static." + */ + if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) || + method.isDefault() || + method.getParameterCount() != 0 || + method.getExceptionTypes().length != 0) { + valid = false; + break; + } + + /* + * "It is a compile-time error if the return type of a + * method declared in an annotation type is not one of the + * following: a primitive type, String, Class, any + * parameterized invocation of Class, an enum type + * (section 8.9), an annotation type, or an array type + * (chapter 10) whose element type is one of the preceding + * types." + */ + Class returnType = method.getReturnType(); + if (returnType.isArray()) { + returnType = returnType.getComponentType(); + if (returnType.isArray()) { // Only single dimensional arrays + valid = false; + break; + } + } + + if (!((returnType.isPrimitive() && returnType != void.class) || + returnType == String.class || + returnType == Class.class || + returnType.isEnum() || + returnType.isAnnotation())) { + valid = false; + break; + } + + /* + * "It is a compile-time error if any method declared in an + * annotation type has a signature that is + * override-equivalent to that of any public or protected + * method declared in class Object or in the interface + * java.lang.annotation.Annotation." + * + * The methods in Object or Annotation meeting the other + * criteria (no arguments, contrained return type, etc.) + * above are: + * + * String toString() + * int hashCode() + * Class annotationType() + */ + String methodName = method.getName(); + if ((methodName.equals("toString") && returnType == String.class) || + (methodName.equals("hashCode") && returnType == int.class) || + (methodName.equals("annotationType") && returnType == Class.class)) { + valid = false; + break; + } + } + if (valid) + return; + else + throw new AnnotationFormatError("Malformed method on an annotation type"); + } + + /** + * Implementation of dynamicProxy.hashCode() + */ + private int hashCodeImpl() { + int result = 0; + for (Map.Entry e : memberValues.entrySet()) { + result += (127 * e.getKey().hashCode()) ^ + memberValueHashCode(e.getValue()); + } + return result; + } + + /** + * Computes hashCode of a member value (in "dynamic proxy return form") + */ + private static int memberValueHashCode(Object value) { + Class type = value.getClass(); + if (!type.isArray()) // primitive, string, class, enum const, + // or annotation + return value.hashCode(); + + if (type == byte[].class) + return Arrays.hashCode((byte[]) value); + if (type == char[].class) + return Arrays.hashCode((char[]) value); + if (type == double[].class) + return Arrays.hashCode((double[]) value); + if (type == float[].class) + return Arrays.hashCode((float[]) value); + if (type == int[].class) + return Arrays.hashCode((int[]) value); + if (type == long[].class) + return Arrays.hashCode((long[]) value); + if (type == short[].class) + return Arrays.hashCode((short[]) value); + if (type == boolean[].class) + return Arrays.hashCode((boolean[]) value); + return Arrays.hashCode((Object[]) value); + } + + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + // Check to make sure that types have not evolved incompatibly + + AnnotationType annotationType = null; + try { + annotationType = AnnotationType.getInstance(type); + } catch(IllegalArgumentException e) { + // Class is no longer an annotation type; time to punch out + throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); + } + + Map> memberTypes = annotationType.memberTypes(); + + // If there are annotation members without values, that + // situation is handled by the invoke method. + for (Map.Entry memberValue : memberValues.entrySet()) { + String name = memberValue.getKey(); + Class memberType = memberTypes.get(name); + if (memberType != null) { // i.e. member still exists + Object value = memberValue.getValue(); + if (!(memberType.isInstance(value) || + value instanceof ExceptionProxy)) { + memberValue.setValue( + new AnnotationTypeMismatchExceptionProxy( + value.getClass() + "[" + value + "]").setMember( + annotationType.members().get(name))); + } + } + } + } +} diff --git a/src/sun/reflect/annotation/AnnotationParser.java b/src/sun/reflect/annotation/AnnotationParser.java new file mode 100644 index 00000000..4dda287a --- /dev/null +++ b/src/sun/reflect/annotation/AnnotationParser.java @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import sun.reflect.ConstantPool; +import sun.reflect.generics.factory.CoreReflectionFactory; +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.parser.SignatureParser; +import sun.reflect.generics.scope.ClassScope; +import sun.reflect.generics.tree.TypeSignature; +import sun.reflect.generics.visitor.Reifier; + +/** + * Parser for Java programming language annotations. Translates + * annotation byte streams emitted by compiler into annotation objects. + * + * @author Josh Bloch + * @since 1.5 + */ +public class AnnotationParser { + /** + * Parses the annotations described by the specified byte array. + * resolving constant references in the specified constant pool. + * The array must contain an array of annotations as described + * in the RuntimeVisibleAnnotations_attribute: + * + * u2 num_annotations; + * annotation annotations[num_annotations]; + * + * @throws AnnotationFormatError if an annotation is found to be + * malformed. + */ + public static Map, Annotation> parseAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, + Class container) { + if (rawAnnotations == null) + return Collections.emptyMap(); + + try { + return parseAnnotations2(rawAnnotations, constPool, container, null); + } catch(BufferUnderflowException e) { + throw new AnnotationFormatError("Unexpected end of annotations."); + } catch(IllegalArgumentException e) { + // Type mismatch in constant pool + throw new AnnotationFormatError(e); + } + } + + /** + * Like {@link #parseAnnotations(byte[], ConstantPool, Class)} + * with an additional parameter {@code selectAnnotationClasses} which selects the + * annotation types to parse (other than selected are quickly skipped).

    + * This method is only used to parse select meta annotations in the construction + * phase of {@link AnnotationType} instances to prevent infinite recursion. + * + * @param selectAnnotationClasses an array of annotation types to select when parsing + */ + @SafeVarargs + @SuppressWarnings("varargs") // selectAnnotationClasses is used safely + static Map, Annotation> parseSelectAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, + Class container, + Class ... selectAnnotationClasses) { + if (rawAnnotations == null) + return Collections.emptyMap(); + + try { + return parseAnnotations2(rawAnnotations, constPool, container, selectAnnotationClasses); + } catch(BufferUnderflowException e) { + throw new AnnotationFormatError("Unexpected end of annotations."); + } catch(IllegalArgumentException e) { + // Type mismatch in constant pool + throw new AnnotationFormatError(e); + } + } + + private static Map, Annotation> parseAnnotations2( + byte[] rawAnnotations, + ConstantPool constPool, + Class container, + Class[] selectAnnotationClasses) { + Map, Annotation> result = + new LinkedHashMap, Annotation>(); + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + int numAnnotations = buf.getShort() & 0xFFFF; + for (int i = 0; i < numAnnotations; i++) { + Annotation a = parseAnnotation2(buf, constPool, container, false, selectAnnotationClasses); + if (a != null) { + Class klass = a.annotationType(); + if (AnnotationType.getInstance(klass).retention() == RetentionPolicy.RUNTIME && + result.put(klass, a) != null) { + throw new AnnotationFormatError( + "Duplicate annotation for class: "+klass+": " + a); + } + } + } + return result; + } + + /** + * Parses the parameter annotations described by the specified byte array. + * resolving constant references in the specified constant pool. + * The array must contain an array of annotations as described + * in the RuntimeVisibleParameterAnnotations_attribute: + * + * u1 num_parameters; + * { + * u2 num_annotations; + * annotation annotations[num_annotations]; + * } parameter_annotations[num_parameters]; + * + * Unlike parseAnnotations, rawAnnotations must not be null! + * A null value must be handled by the caller. This is so because + * we cannot determine the number of parameters if rawAnnotations + * is null. Also, the caller should check that the number + * of parameters indicated by the return value of this method + * matches the actual number of method parameters. A mismatch + * indicates that an AnnotationFormatError should be thrown. + * + * @throws AnnotationFormatError if an annotation is found to be + * malformed. + */ + public static Annotation[][] parseParameterAnnotations( + byte[] rawAnnotations, + ConstantPool constPool, + Class container) { + try { + return parseParameterAnnotations2(rawAnnotations, constPool, container); + } catch(BufferUnderflowException e) { + throw new AnnotationFormatError( + "Unexpected end of parameter annotations."); + } catch(IllegalArgumentException e) { + // Type mismatch in constant pool + throw new AnnotationFormatError(e); + } + } + + private static Annotation[][] parseParameterAnnotations2( + byte[] rawAnnotations, + ConstantPool constPool, + Class container) { + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + int numParameters = buf.get() & 0xFF; + Annotation[][] result = new Annotation[numParameters][]; + + for (int i = 0; i < numParameters; i++) { + int numAnnotations = buf.getShort() & 0xFFFF; + List annotations = + new ArrayList(numAnnotations); + for (int j = 0; j < numAnnotations; j++) { + Annotation a = parseAnnotation(buf, constPool, container, false); + if (a != null) { + AnnotationType type = AnnotationType.getInstance( + a.annotationType()); + if (type.retention() == RetentionPolicy.RUNTIME) + annotations.add(a); + } + } + result[i] = annotations.toArray(EMPTY_ANNOTATIONS_ARRAY); + } + return result; + } + + private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = + new Annotation[0]; + + /** + * Parses the annotation at the current position in the specified + * byte buffer, resolving constant references in the specified constant + * pool. The cursor of the byte buffer must point to an "annotation + * structure" as described in the RuntimeVisibleAnnotations_attribute: + * + * annotation { + * u2 type_index; + * u2 num_member_value_pairs; + * { u2 member_name_index; + * member_value value; + * } member_value_pairs[num_member_value_pairs]; + * } + * } + * + * Returns the annotation, or null if the annotation's type cannot + * be found by the VM, or is not a valid annotation type. + * + * @param exceptionOnMissingAnnotationClass if true, throw + * TypeNotPresentException if a referenced annotation type is not + * available at runtime + */ + static Annotation parseAnnotation(ByteBuffer buf, + ConstantPool constPool, + Class container, + boolean exceptionOnMissingAnnotationClass) { + return parseAnnotation2(buf, constPool, container, exceptionOnMissingAnnotationClass, null); + } + + @SuppressWarnings("unchecked") + private static Annotation parseAnnotation2(ByteBuffer buf, + ConstantPool constPool, + Class container, + boolean exceptionOnMissingAnnotationClass, + Class[] selectAnnotationClasses) { + int typeIndex = buf.getShort() & 0xFFFF; + Class annotationClass = null; + String sig = "[unknown]"; + try { + try { + sig = constPool.getUTF8At(typeIndex); + annotationClass = (Class)parseSig(sig, container); + } catch (IllegalArgumentException ex) { + // support obsolete early jsr175 format class files + annotationClass = (Class)constPool.getClassAt(typeIndex); + } + } catch (NoClassDefFoundError e) { + if (exceptionOnMissingAnnotationClass) + // note: at this point sig is "[unknown]" or VM-style + // name instead of a binary name + throw new TypeNotPresentException(sig, e); + skipAnnotation(buf, false); + return null; + } + catch (TypeNotPresentException e) { + if (exceptionOnMissingAnnotationClass) + throw e; + skipAnnotation(buf, false); + return null; + } + if (selectAnnotationClasses != null && !contains(selectAnnotationClasses, annotationClass)) { + skipAnnotation(buf, false); + return null; + } + AnnotationType type = null; + try { + type = AnnotationType.getInstance(annotationClass); + } catch (IllegalArgumentException e) { + skipAnnotation(buf, false); + return null; + } + + Map> memberTypes = type.memberTypes(); + Map memberValues = + new LinkedHashMap(type.memberDefaults()); + + int numMembers = buf.getShort() & 0xFFFF; + for (int i = 0; i < numMembers; i++) { + int memberNameIndex = buf.getShort() & 0xFFFF; + String memberName = constPool.getUTF8At(memberNameIndex); + Class memberType = memberTypes.get(memberName); + + if (memberType == null) { + // Member is no longer present in annotation type; ignore it + skipMemberValue(buf); + } else { + Object value = parseMemberValue(memberType, buf, constPool, container); + if (value instanceof AnnotationTypeMismatchExceptionProxy) + ((AnnotationTypeMismatchExceptionProxy) value). + setMember(type.members().get(memberName)); + memberValues.put(memberName, value); + } + } + return annotationForMap(annotationClass, memberValues); + } + + /** + * Returns an annotation of the given type backed by the given + * member -> value map. + */ + public static Annotation annotationForMap(final Class type, + final Map memberValues) + { + return AccessController.doPrivileged(new PrivilegedAction() { + public Annotation run() { + return (Annotation) Proxy.newProxyInstance( + type.getClassLoader(), new Class[] { type }, + new AnnotationInvocationHandler(type, memberValues)); + }}); + } + + /** + * Parses the annotation member value at the current position in the + * specified byte buffer, resolving constant references in the specified + * constant pool. The cursor of the byte buffer must point to a + * "member_value structure" as described in the + * RuntimeVisibleAnnotations_attribute: + * + * member_value { + * u1 tag; + * union { + * u2 const_value_index; + * { + * u2 type_name_index; + * u2 const_name_index; + * } enum_const_value; + * u2 class_info_index; + * annotation annotation_value; + * { + * u2 num_values; + * member_value values[num_values]; + * } array_value; + * } value; + * } + * + * The member must be of the indicated type. If it is not, this + * method returns an AnnotationTypeMismatchExceptionProxy. + */ + @SuppressWarnings("unchecked") + public static Object parseMemberValue(Class memberType, + ByteBuffer buf, + ConstantPool constPool, + Class container) { + Object result = null; + int tag = buf.get(); + switch(tag) { + case 'e': + return parseEnumValue((Class>)memberType, buf, constPool, container); + case 'c': + result = parseClassValue(buf, constPool, container); + break; + case '@': + result = parseAnnotation(buf, constPool, container, true); + break; + case '[': + return parseArray(memberType, buf, constPool, container); + default: + result = parseConst(tag, buf, constPool); + } + + if (!(result instanceof ExceptionProxy) && + !memberType.isInstance(result)) + result = new AnnotationTypeMismatchExceptionProxy( + result.getClass() + "[" + result + "]"); + return result; + } + + /** + * Parses the primitive or String annotation member value indicated by + * the specified tag byte at the current position in the specified byte + * buffer, resolving constant reference in the specified constant pool. + * The cursor of the byte buffer must point to an annotation member value + * of the type indicated by the specified tag, as described in the + * RuntimeVisibleAnnotations_attribute: + * + * u2 const_value_index; + */ + private static Object parseConst(int tag, + ByteBuffer buf, ConstantPool constPool) { + int constIndex = buf.getShort() & 0xFFFF; + switch(tag) { + case 'B': + return Byte.valueOf((byte) constPool.getIntAt(constIndex)); + case 'C': + return Character.valueOf((char) constPool.getIntAt(constIndex)); + case 'D': + return Double.valueOf(constPool.getDoubleAt(constIndex)); + case 'F': + return Float.valueOf(constPool.getFloatAt(constIndex)); + case 'I': + return Integer.valueOf(constPool.getIntAt(constIndex)); + case 'J': + return Long.valueOf(constPool.getLongAt(constIndex)); + case 'S': + return Short.valueOf((short) constPool.getIntAt(constIndex)); + case 'Z': + return Boolean.valueOf(constPool.getIntAt(constIndex) != 0); + case 's': + return constPool.getUTF8At(constIndex); + default: + throw new AnnotationFormatError( + "Invalid member-value tag in annotation: " + tag); + } + } + + /** + * Parses the Class member value at the current position in the + * specified byte buffer, resolving constant references in the specified + * constant pool. The cursor of the byte buffer must point to a "class + * info index" as described in the RuntimeVisibleAnnotations_attribute: + * + * u2 class_info_index; + */ + private static Object parseClassValue(ByteBuffer buf, + ConstantPool constPool, + Class container) { + int classIndex = buf.getShort() & 0xFFFF; + try { + try { + String sig = constPool.getUTF8At(classIndex); + return parseSig(sig, container); + } catch (IllegalArgumentException ex) { + // support obsolete early jsr175 format class files + return constPool.getClassAt(classIndex); + } + } catch (NoClassDefFoundError e) { + return new TypeNotPresentExceptionProxy("[unknown]", e); + } + catch (TypeNotPresentException e) { + return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause()); + } + } + + private static Class parseSig(String sig, Class container) { + if (sig.equals("V")) return void.class; + SignatureParser parser = SignatureParser.make(); + TypeSignature typeSig = parser.parseTypeSig(sig); + GenericsFactory factory = CoreReflectionFactory.make(container, ClassScope.make(container)); + Reifier reify = Reifier.make(factory); + typeSig.accept(reify); + Type result = reify.getResult(); + return toClass(result); + } + static Class toClass(Type o) { + if (o instanceof GenericArrayType) + return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()), + 0) + .getClass(); + return (Class)o; + } + + /** + * Parses the enum constant member value at the current position in the + * specified byte buffer, resolving constant references in the specified + * constant pool. The cursor of the byte buffer must point to a + * "enum_const_value structure" as described in the + * RuntimeVisibleAnnotations_attribute: + * + * { + * u2 type_name_index; + * u2 const_name_index; + * } enum_const_value; + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + private static Object parseEnumValue(Class enumType, ByteBuffer buf, + ConstantPool constPool, + Class container) { + int typeNameIndex = buf.getShort() & 0xFFFF; + String typeName = constPool.getUTF8At(typeNameIndex); + int constNameIndex = buf.getShort() & 0xFFFF; + String constName = constPool.getUTF8At(constNameIndex); + + if (!typeName.endsWith(";")) { + // support now-obsolete early jsr175-format class files. + if (!enumType.getName().equals(typeName)) + return new AnnotationTypeMismatchExceptionProxy( + typeName + "." + constName); + } else if (enumType != parseSig(typeName, container)) { + return new AnnotationTypeMismatchExceptionProxy( + typeName + "." + constName); + } + + try { + return Enum.valueOf(enumType, constName); + } catch(IllegalArgumentException e) { + return new EnumConstantNotPresentExceptionProxy( + (Class>)enumType, constName); + } + } + + /** + * Parses the array value at the current position in the specified byte + * buffer, resolving constant references in the specified constant pool. + * The cursor of the byte buffer must point to an array value struct + * as specified in the RuntimeVisibleAnnotations_attribute: + * + * { + * u2 num_values; + * member_value values[num_values]; + * } array_value; + * + * If the array values do not match arrayType, an + * AnnotationTypeMismatchExceptionProxy will be returned. + */ + @SuppressWarnings("unchecked") + private static Object parseArray(Class arrayType, + ByteBuffer buf, + ConstantPool constPool, + Class container) { + int length = buf.getShort() & 0xFFFF; // Number of array components + Class componentType = arrayType.getComponentType(); + + if (componentType == byte.class) { + return parseByteArray(length, buf, constPool); + } else if (componentType == char.class) { + return parseCharArray(length, buf, constPool); + } else if (componentType == double.class) { + return parseDoubleArray(length, buf, constPool); + } else if (componentType == float.class) { + return parseFloatArray(length, buf, constPool); + } else if (componentType == int.class) { + return parseIntArray(length, buf, constPool); + } else if (componentType == long.class) { + return parseLongArray(length, buf, constPool); + } else if (componentType == short.class) { + return parseShortArray(length, buf, constPool); + } else if (componentType == boolean.class) { + return parseBooleanArray(length, buf, constPool); + } else if (componentType == String.class) { + return parseStringArray(length, buf, constPool); + } else if (componentType == Class.class) { + return parseClassArray(length, buf, constPool, container); + } else if (componentType.isEnum()) { + return parseEnumArray(length, (Class>)componentType, buf, + constPool, container); + } else { + assert componentType.isAnnotation(); + return parseAnnotationArray(length, (Class )componentType, buf, + constPool, container); + } + } + + private static Object parseByteArray(int length, + ByteBuffer buf, ConstantPool constPool) { + byte[] result = new byte[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'B') { + int index = buf.getShort() & 0xFFFF; + result[i] = (byte) constPool.getIntAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseCharArray(int length, + ByteBuffer buf, ConstantPool constPool) { + char[] result = new char[length]; + boolean typeMismatch = false; + byte tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'C') { + int index = buf.getShort() & 0xFFFF; + result[i] = (char) constPool.getIntAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseDoubleArray(int length, + ByteBuffer buf, ConstantPool constPool) { + double[] result = new double[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'D') { + int index = buf.getShort() & 0xFFFF; + result[i] = constPool.getDoubleAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseFloatArray(int length, + ByteBuffer buf, ConstantPool constPool) { + float[] result = new float[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'F') { + int index = buf.getShort() & 0xFFFF; + result[i] = constPool.getFloatAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseIntArray(int length, + ByteBuffer buf, ConstantPool constPool) { + int[] result = new int[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'I') { + int index = buf.getShort() & 0xFFFF; + result[i] = constPool.getIntAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseLongArray(int length, + ByteBuffer buf, ConstantPool constPool) { + long[] result = new long[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'J') { + int index = buf.getShort() & 0xFFFF; + result[i] = constPool.getLongAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseShortArray(int length, + ByteBuffer buf, ConstantPool constPool) { + short[] result = new short[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'S') { + int index = buf.getShort() & 0xFFFF; + result[i] = (short) constPool.getIntAt(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseBooleanArray(int length, + ByteBuffer buf, ConstantPool constPool) { + boolean[] result = new boolean[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'Z') { + int index = buf.getShort() & 0xFFFF; + result[i] = (constPool.getIntAt(index) != 0); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseStringArray(int length, + ByteBuffer buf, ConstantPool constPool) { + String[] result = new String[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 's') { + int index = buf.getShort() & 0xFFFF; + result[i] = constPool.getUTF8At(index); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseClassArray(int length, + ByteBuffer buf, + ConstantPool constPool, + Class container) { + Object[] result = new Class[length]; + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'c') { + result[i] = parseClassValue(buf, constPool, container); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseEnumArray(int length, Class> enumType, + ByteBuffer buf, + ConstantPool constPool, + Class container) { + Object[] result = (Object[]) Array.newInstance(enumType, length); + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == 'e') { + result[i] = parseEnumValue(enumType, buf, constPool, container); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + private static Object parseAnnotationArray(int length, + Class annotationType, + ByteBuffer buf, + ConstantPool constPool, + Class container) { + Object[] result = (Object[]) Array.newInstance(annotationType, length); + boolean typeMismatch = false; + int tag = 0; + + for (int i = 0; i < length; i++) { + tag = buf.get(); + if (tag == '@') { + result[i] = parseAnnotation(buf, constPool, container, true); + } else { + skipMemberValue(tag, buf); + typeMismatch = true; + } + } + return typeMismatch ? exceptionProxy(tag) : result; + } + + /** + * Return an appropriate exception proxy for a mismatching array + * annotation where the erroneous array has the specified tag. + */ + private static ExceptionProxy exceptionProxy(int tag) { + return new AnnotationTypeMismatchExceptionProxy( + "Array with component tag: " + tag); + } + + /** + * Skips the annotation at the current position in the specified + * byte buffer. The cursor of the byte buffer must point to + * an "annotation structure" OR two bytes into an annotation + * structure (i.e., after the type index). + * + * @parameter complete true if the byte buffer points to the beginning + * of an annotation structure (rather than two bytes in). + */ + private static void skipAnnotation(ByteBuffer buf, boolean complete) { + if (complete) + buf.getShort(); // Skip type index + int numMembers = buf.getShort() & 0xFFFF; + for (int i = 0; i < numMembers; i++) { + buf.getShort(); // Skip memberNameIndex + skipMemberValue(buf); + } + } + + /** + * Skips the annotation member value at the current position in the + * specified byte buffer. The cursor of the byte buffer must point to a + * "member_value structure." + */ + private static void skipMemberValue(ByteBuffer buf) { + int tag = buf.get(); + skipMemberValue(tag, buf); + } + + /** + * Skips the annotation member value at the current position in the + * specified byte buffer. The cursor of the byte buffer must point + * immediately after the tag in a "member_value structure." + */ + private static void skipMemberValue(int tag, ByteBuffer buf) { + switch(tag) { + case 'e': // Enum value + buf.getInt(); // (Two shorts, actually.) + break; + case '@': + skipAnnotation(buf, true); + break; + case '[': + skipArray(buf); + break; + default: + // Class, primitive, or String + buf.getShort(); + } + } + + /** + * Skips the array value at the current position in the specified byte + * buffer. The cursor of the byte buffer must point to an array value + * struct. + */ + private static void skipArray(ByteBuffer buf) { + int length = buf.getShort() & 0xFFFF; + for (int i = 0; i < length; i++) + skipMemberValue(buf); + } + + /** + * Searches for given {@code element} in given {@code array} by identity. + * Returns {@code true} if found {@code false} if not. + */ + private static boolean contains(Object[] array, Object element) { + for (Object e : array) + if (e == element) + return true; + return false; + } + + /* + * This method converts the annotation map returned by the parseAnnotations() + * method to an array. It is called by Field.getDeclaredAnnotations(), + * Method.getDeclaredAnnotations(), and Constructor.getDeclaredAnnotations(). + * This avoids the reflection classes to load the Annotation class until + * it is needed. + */ + private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + public static Annotation[] toArray(Map, Annotation> annotations) { + return annotations.values().toArray(EMPTY_ANNOTATION_ARRAY); + } + + static Annotation[] getEmptyAnnotationArray() { return EMPTY_ANNOTATION_ARRAY; } +} diff --git a/src/sun/reflect/annotation/AnnotationSupport.java b/src/sun/reflect/annotation/AnnotationSupport.java new file mode 100644 index 00000000..d0b35d3b --- /dev/null +++ b/src/sun/reflect/annotation/AnnotationSupport.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.annotation.Repeatable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import sun.misc.JavaLangAccess; + +public final class AnnotationSupport { + private static final JavaLangAccess LANG_ACCESS = sun.misc.SharedSecrets.getJavaLangAccess(); + + /** + * Finds and returns all annotations in {@code annotations} matching + * the given {@code annoClass}. + * + * Apart from annotations directly present in {@code annotations} this + * method searches for annotations inside containers i.e. indirectly + * present annotations. + * + * The order of the elements in the array returned depends on the iteration + * order of the provided map. Specifically, the directly present annotations + * come before the indirectly present annotations if and only if the + * directly present annotations come before the indirectly present + * annotations in the map. + * + * @param annotations the {@code Map} in which to search for annotations + * @param annoClass the type of annotation to search for + * + * @return an array of instances of {@code annoClass} or an empty + * array if none were found + */ + public static A[] getDirectlyAndIndirectlyPresent( + Map, Annotation> annotations, + Class annoClass) { + List result = new ArrayList(); + + @SuppressWarnings("unchecked") + A direct = (A) annotations.get(annoClass); + if (direct != null) + result.add(direct); + + A[] indirect = getIndirectlyPresent(annotations, annoClass); + if (indirect != null && indirect.length != 0) { + boolean indirectFirst = direct == null || + containerBeforeContainee(annotations, annoClass); + + result.addAll((indirectFirst ? 0 : 1), Arrays.asList(indirect)); + } + + @SuppressWarnings("unchecked") + A[] arr = (A[]) Array.newInstance(annoClass, result.size()); + return result.toArray(arr); + } + + /** + * Finds and returns all annotations matching the given {@code annoClass} + * indirectly present in {@code annotations}. + * + * @param annotations annotations to search indexed by their types + * @param annoClass the type of annotation to search for + * + * @return an array of instances of {@code annoClass} or an empty array if no + * indirectly present annotations were found + */ + private static A[] getIndirectlyPresent( + Map, Annotation> annotations, + Class annoClass) { + + Repeatable repeatable = annoClass.getDeclaredAnnotation(Repeatable.class); + if (repeatable == null) + return null; // Not repeatable -> no indirectly present annotations + + Class containerClass = repeatable.value(); + + Annotation container = annotations.get(containerClass); + if (container == null) + return null; + + // Unpack container + A[] valueArray = getValueArray(container); + checkTypes(valueArray, container, annoClass); + + return valueArray; + } + + + /** + * Figures out if conatiner class comes before containee class among the + * keys of the given map. + * + * @return true if container class is found before containee class when + * iterating over annotations.keySet(). + */ + private static boolean containerBeforeContainee( + Map, Annotation> annotations, + Class annoClass) { + + Class containerClass = + annoClass.getDeclaredAnnotation(Repeatable.class).value(); + + for (Class c : annotations.keySet()) { + if (c == containerClass) return true; + if (c == annoClass) return false; + } + + // Neither containee nor container present + return false; + } + + + /** + * Finds and returns all associated annotations matching the given class. + * + * The order of the elements in the array returned depends on the iteration + * order of the provided maps. Specifically, the directly present annotations + * come before the indirectly present annotations if and only if the + * directly present annotations come before the indirectly present + * annotations in the relevant map. + * + * @param declaredAnnotations the declared annotations indexed by their types + * @param decl the class declaration on which to search for annotations + * @param annoClass the type of annotation to search for + * + * @return an array of instances of {@code annoClass} or an empty array if none were found. + */ + public static A[] getAssociatedAnnotations( + Map, Annotation> declaredAnnotations, + Class decl, + Class annoClass) { + Objects.requireNonNull(decl); + + // Search declared + A[] result = getDirectlyAndIndirectlyPresent(declaredAnnotations, annoClass); + + // Search inherited + if(AnnotationType.getInstance(annoClass).isInherited()) { + Class superDecl = decl.getSuperclass(); + while (result.length == 0 && superDecl != null) { + result = getDirectlyAndIndirectlyPresent(LANG_ACCESS.getDeclaredAnnotationMap(superDecl), annoClass); + superDecl = superDecl.getSuperclass(); + } + } + + return result; + } + + + /* Reflectively invoke the values-method of the given annotation + * (container), cast it to an array of annotations and return the result. + */ + private static A[] getValueArray(Annotation container) { + try { + // According to JLS the container must have an array-valued value + // method. Get the AnnotationType, get the "value" method and invoke + // it to get the content. + + Class containerClass = container.annotationType(); + AnnotationType annoType = AnnotationType.getInstance(containerClass); + if (annoType == null) + throw invalidContainerException(container, null); + + Method m = annoType.members().get("value"); + if (m == null) + throw invalidContainerException(container, null); + + m.setAccessible(true); + + // This will erase to (Annotation[]) but we do a runtime cast on the + // return-value in the method that call this method. + @SuppressWarnings("unchecked") + A[] values = (A[]) m.invoke(container); + + return values; + + } catch (IllegalAccessException | // couldn't loosen security + IllegalArgumentException | // parameters doesn't match + InvocationTargetException | // the value method threw an exception + ClassCastException e) { + + throw invalidContainerException(container, e); + + } + } + + + private static AnnotationFormatError invalidContainerException(Annotation anno, + Throwable cause) { + return new AnnotationFormatError( + anno + " is an invalid container for repeating annotations", + cause); + } + + + /* Sanity check type of all the annotation instances of type {@code annoClass} + * from {@code container}. + */ + private static void checkTypes(A[] annotations, + Annotation container, + Class annoClass) { + for (A a : annotations) { + if (!annoClass.isInstance(a)) { + throw new AnnotationFormatError( + String.format("%s is an invalid container for " + + "repeating annotations of type: %s", + container, annoClass)); + } + } + } +} diff --git a/src/sun/reflect/annotation/AnnotationType.java b/src/sun/reflect/annotation/AnnotationType.java new file mode 100644 index 00000000..7ab50c2e --- /dev/null +++ b/src/sun/reflect/annotation/AnnotationType.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Map; + +import sun.misc.JavaLangAccess; + +/** + * Represents an annotation type at run time. Used to type-check annotations + * and apply member defaults. + * + * @author Josh Bloch + * @since 1.5 + */ +public class AnnotationType { + /** + * Member name -> type mapping. Note that primitive types + * are represented by the class objects for the corresponding wrapper + * types. This matches the return value that must be used for a + * dynamic proxy, allowing for a simple isInstance test. + */ + private final Map> memberTypes; + + /** + * Member name -> default value mapping. + */ + private final Map memberDefaults; + + /** + * Member name -> Method object mapping. This (and its assoicated + * accessor) are used only to generate AnnotationTypeMismatchExceptions. + */ + private final Map members; + + /** + * The retention policy for this annotation type. + */ + private final RetentionPolicy retention; + + /** + * Whether this annotation type is inherited. + */ + private final boolean inherited; + + /** + * Returns an AnnotationType instance for the specified annotation type. + * + * @throw IllegalArgumentException if the specified class object for + * does not represent a valid annotation type + */ + public static AnnotationType getInstance( + Class annotationClass) + { + JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); + AnnotationType result = jla.getAnnotationType(annotationClass); // volatile read + if (result == null) { + result = new AnnotationType(annotationClass); + // try to CAS the AnnotationType: null -> result + if (!jla.casAnnotationType(annotationClass, null, result)) { + // somebody was quicker -> read it's result + result = jla.getAnnotationType(annotationClass); + assert result != null; + } + } + + return result; + } + + /** + * Sole constructor. + * + * @param annotationClass the class object for the annotation type + * @throw IllegalArgumentException if the specified class object for + * does not represent a valid annotation type + */ + private AnnotationType(final Class annotationClass) { + if (!annotationClass.isAnnotation()) + throw new IllegalArgumentException("Not an annotation type"); + + Method[] methods = + AccessController.doPrivileged(new PrivilegedAction() { + public Method[] run() { + // Initialize memberTypes and defaultValues + return annotationClass.getDeclaredMethods(); + } + }); + + memberTypes = new HashMap>(methods.length+1, 1.0f); + memberDefaults = new HashMap(0); + members = new HashMap(methods.length+1, 1.0f); + + for (Method method : methods) { + if (method.getParameterTypes().length != 0) + throw new IllegalArgumentException(method + " has params"); + String name = method.getName(); + Class type = method.getReturnType(); + memberTypes.put(name, invocationHandlerReturnType(type)); + members.put(name, method); + + Object defaultValue = method.getDefaultValue(); + if (defaultValue != null) + memberDefaults.put(name, defaultValue); + } + + // Initialize retention, & inherited fields. Special treatment + // of the corresponding annotation types breaks infinite recursion. + if (annotationClass != Retention.class && + annotationClass != Inherited.class) { + JavaLangAccess jla = sun.misc.SharedSecrets.getJavaLangAccess(); + Map, Annotation> metaAnnotations = + AnnotationParser.parseSelectAnnotations( + jla.getRawClassAnnotations(annotationClass), + jla.getConstantPool(annotationClass), + annotationClass, + Retention.class, Inherited.class + ); + Retention ret = (Retention) metaAnnotations.get(Retention.class); + retention = (ret == null ? RetentionPolicy.CLASS : ret.value()); + inherited = metaAnnotations.containsKey(Inherited.class); + } + else { + retention = RetentionPolicy.RUNTIME; + inherited = false; + } + } + + /** + * Returns the type that must be returned by the invocation handler + * of a dynamic proxy in order to have the dynamic proxy return + * the specified type (which is assumed to be a legal member type + * for an annotation). + */ + public static Class invocationHandlerReturnType(Class type) { + // Translate primitives to wrappers + if (type == byte.class) + return Byte.class; + if (type == char.class) + return Character.class; + if (type == double.class) + return Double.class; + if (type == float.class) + return Float.class; + if (type == int.class) + return Integer.class; + if (type == long.class) + return Long.class; + if (type == short.class) + return Short.class; + if (type == boolean.class) + return Boolean.class; + + // Otherwise, just return declared type + return type; + } + + /** + * Returns member types for this annotation type + * (member name -> type mapping). + */ + public Map> memberTypes() { + return memberTypes; + } + + /** + * Returns members of this annotation type + * (member name -> associated Method object mapping). + */ + public Map members() { + return members; + } + + /** + * Returns the default values for this annotation type + * (Member name -> default value mapping). + */ + public Map memberDefaults() { + return memberDefaults; + } + + /** + * Returns the retention policy for this annotation type. + */ + public RetentionPolicy retention() { + return retention; + } + + /** + * Returns true if this this annotation type is inherited. + */ + public boolean isInherited() { + return inherited; + } + + /** + * For debugging. + */ + public String toString() { + return "Annotation Type:\n" + + " Member types: " + memberTypes + "\n" + + " Member defaults: " + memberDefaults + "\n" + + " Retention policy: " + retention + "\n" + + " Inherited: " + inherited; + } +} diff --git a/src/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java b/src/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java new file mode 100644 index 00000000..06ea9b93 --- /dev/null +++ b/src/sun/reflect/annotation/AnnotationTypeMismatchExceptionProxy.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.lang.annotation.AnnotationTypeMismatchException; +import java.lang.reflect.Method; + +/** + * ExceptionProxy for AnnotationTypeMismatchException. + * + * @author Josh Bloch + * @since 1.5 + */ +class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy { + private static final long serialVersionUID = 7844069490309503934L; + private Method member; + private String foundType; + + /** + * It turns out to be convenient to construct these proxies in + * two stages. Since this is a private implementation class, we + * permit ourselves this liberty even though it's normally a very + * bad idea. + */ + AnnotationTypeMismatchExceptionProxy(String foundType) { + this.foundType = foundType; + } + + AnnotationTypeMismatchExceptionProxy setMember(Method member) { + this.member = member; + return this; + } + + protected RuntimeException generateException() { + return new AnnotationTypeMismatchException(member, foundType); + } +} diff --git a/src/sun/reflect/annotation/EnumConstantNotPresentExceptionProxy.java b/src/sun/reflect/annotation/EnumConstantNotPresentExceptionProxy.java new file mode 100644 index 00000000..b229c004 --- /dev/null +++ b/src/sun/reflect/annotation/EnumConstantNotPresentExceptionProxy.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +/** + * ExceptionProxy for EnumConstantNotPresentException. + * + * @author Josh Bloch + * @since 1.5 + */ +public class EnumConstantNotPresentExceptionProxy extends ExceptionProxy { + private static final long serialVersionUID = -604662101303187330L; + Class> enumType; + String constName; + + public EnumConstantNotPresentExceptionProxy(Class> enumType, + String constName) { + this.enumType = enumType; + this.constName = constName; + } + + protected RuntimeException generateException() { + return new EnumConstantNotPresentException(enumType, constName); + } +} diff --git a/src/sun/reflect/annotation/ExceptionProxy.java b/src/sun/reflect/annotation/ExceptionProxy.java new file mode 100644 index 00000000..0fa18065 --- /dev/null +++ b/src/sun/reflect/annotation/ExceptionProxy.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +/** + * An instance of this class is stored in an AnnotationInvocationHandler's + * "memberValues" map in lieu of a value for an annotation member that + * cannot be returned due to some exceptional condition (typically some + * form of illegal evolution of the annotation class). The ExceptionProxy + * instance describes the exception that the dynamic proxy should throw if + * it is queried for this member. + * + * @author Josh Bloch + * @since 1.5 + */ +public abstract class ExceptionProxy implements java.io.Serializable { + protected abstract RuntimeException generateException(); +} diff --git a/src/sun/reflect/annotation/TypeAnnotation.java b/src/sun/reflect/annotation/TypeAnnotation.java new file mode 100644 index 00000000..e1d1bba5 --- /dev/null +++ b/src/sun/reflect/annotation/TypeAnnotation.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.reflect.annotation; + +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.reflect.AnnotatedElement; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * A TypeAnnotation contains all the information needed to transform type + * annotations on declarations in the class file to actual Annotations in + * AnnotatedType instances. + * + * TypeAnnotaions contain a base Annotation, location info (which lets you + * distinguish between '@A Inner.@B Outer' in for example nested types), + * target info and the declaration the TypeAnnotaiton was parsed from. + */ +public final class TypeAnnotation { + private final TypeAnnotationTargetInfo targetInfo; + private final LocationInfo loc; + private final Annotation annotation; + private final AnnotatedElement baseDeclaration; + + public TypeAnnotation(TypeAnnotationTargetInfo targetInfo, + LocationInfo loc, + Annotation annotation, + AnnotatedElement baseDeclaration) { + this.targetInfo = targetInfo; + this.loc = loc; + this.annotation = annotation; + this.baseDeclaration = baseDeclaration; + } + + public TypeAnnotationTargetInfo getTargetInfo() { + return targetInfo; + } + public Annotation getAnnotation() { + return annotation; + } + public AnnotatedElement getBaseDeclaration() { + return baseDeclaration; + } + public LocationInfo getLocationInfo() { + return loc; + } + + public static List filter(TypeAnnotation[] typeAnnotations, + TypeAnnotationTarget predicate) { + ArrayList typeAnnos = new ArrayList<>(typeAnnotations.length); + for (TypeAnnotation t : typeAnnotations) + if (t.getTargetInfo().getTarget() == predicate) + typeAnnos.add(t); + typeAnnos.trimToSize(); + return typeAnnos; + } + + public static enum TypeAnnotationTarget { + CLASS_TYPE_PARAMETER, + METHOD_TYPE_PARAMETER, + CLASS_EXTENDS, + CLASS_IMPLEMENTS, // Not in the spec + CLASS_TYPE_PARAMETER_BOUND, + METHOD_TYPE_PARAMETER_BOUND, + FIELD, + METHOD_RETURN, + METHOD_RECEIVER, + METHOD_FORMAL_PARAMETER, + THROWS; + } + + public static final class TypeAnnotationTargetInfo { + private final TypeAnnotationTarget target; + private final int count; + private final int secondaryIndex; + private static final int UNUSED_INDEX = -2; // this is not a valid index in the 308 spec + + public TypeAnnotationTargetInfo(TypeAnnotationTarget target) { + this(target, UNUSED_INDEX, UNUSED_INDEX); + } + + public TypeAnnotationTargetInfo(TypeAnnotationTarget target, + int count) { + this(target, count, UNUSED_INDEX); + } + + public TypeAnnotationTargetInfo(TypeAnnotationTarget target, + int count, + int secondaryIndex) { + this.target = target; + this.count = count; + this.secondaryIndex = secondaryIndex; + } + + public TypeAnnotationTarget getTarget() { + return target; + } + public int getCount() { + return count; + } + public int getSecondaryIndex() { + return secondaryIndex; + } + + @Override + public String toString() { + return "" + target + ": " + count + ", " + secondaryIndex; + } + } + + public static final class LocationInfo { + private final int depth; + private final Location[] locations; + + private LocationInfo() { + this(0, new Location[0]); + } + private LocationInfo(int depth, Location[] locations) { + this.depth = depth; + this.locations = locations; + } + + public static final LocationInfo BASE_LOCATION = new LocationInfo(); + + public static LocationInfo parseLocationInfo(ByteBuffer buf) { + int depth = buf.get() & 0xFF; + if (depth == 0) + return BASE_LOCATION; + Location[] locations = new Location[depth]; + for (int i = 0; i < depth; i++) { + byte tag = buf.get(); + short index = (short)(buf.get() & 0xFF); + if (!(tag == 0 || tag == 1 | tag == 2 || tag == 3)) + throw new AnnotationFormatError("Bad Location encoding in Type Annotation"); + if (tag != 3 && index != 0) + throw new AnnotationFormatError("Bad Location encoding in Type Annotation"); + locations[i] = new Location(tag, index); + } + return new LocationInfo(depth, locations); + } + + public LocationInfo pushArray() { + return pushLocation((byte)0, (short)0); + } + + public LocationInfo pushInner() { + return pushLocation((byte)1, (short)0); + } + + public LocationInfo pushWildcard() { + return pushLocation((byte) 2, (short) 0); + } + + public LocationInfo pushTypeArg(short index) { + return pushLocation((byte) 3, index); + } + + public LocationInfo pushLocation(byte tag, short index) { + int newDepth = this.depth + 1; + Location[] res = new Location[newDepth]; + System.arraycopy(this.locations, 0, res, 0, depth); + res[newDepth - 1] = new Location(tag, (short)(index & 0xFF)); + return new LocationInfo(newDepth, res); + } + + public TypeAnnotation[] filter(TypeAnnotation[] ta) { + ArrayList l = new ArrayList<>(ta.length); + for (TypeAnnotation t : ta) { + if (isSameLocationInfo(t.getLocationInfo())) + l.add(t); + } + return l.toArray(new TypeAnnotation[0]); + } + + boolean isSameLocationInfo(LocationInfo other) { + if (depth != other.depth) + return false; + for (int i = 0; i < depth; i++) + if (!locations[i].isSameLocation(other.locations[i])) + return false; + return true; + } + + public static final class Location { + public final byte tag; + public final short index; + + boolean isSameLocation(Location other) { + return tag == other.tag && index == other.index; + } + + public Location(byte tag, short index) { + this.tag = tag; + this.index = index; + } + } + } + + @Override + public String toString() { + return annotation.toString() + " with Targetnfo: " + + targetInfo.toString() + " on base declaration: " + + baseDeclaration.toString(); + } +} diff --git a/src/sun/reflect/annotation/TypeAnnotationParser.java b/src/sun/reflect/annotation/TypeAnnotationParser.java new file mode 100644 index 00000000..7cc6cc7a --- /dev/null +++ b/src/sun/reflect/annotation/TypeAnnotationParser.java @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import static sun.reflect.annotation.TypeAnnotation.LocationInfo; +import static sun.reflect.annotation.TypeAnnotation.TypeAnnotationTarget; +import static sun.reflect.annotation.TypeAnnotation.TypeAnnotationTargetInfo; + +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Executable; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Type; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import sun.misc.JavaLangAccess; +import sun.reflect.ConstantPool; + +/** + * TypeAnnotationParser implements the logic needed to parse + * TypeAnnotations from an array of bytes. + */ +public final class TypeAnnotationParser { + private static final TypeAnnotation[] EMPTY_TYPE_ANNOTATION_ARRAY = new TypeAnnotation[0]; + + /** + * Build an AnnotatedType from the parameters supplied. + * + * This method and {@code buildAnnotatedTypes} are probably + * the entry points you are looking for. + * + * @param rawAnnotations the byte[] encoding of all type annotations on this declaration + * @param cp the ConstantPool needed to parse the embedded Annotation + * @param decl the declaration this type annotation is on + * @param container the Class this type annotation is on (may be the same as decl) + * @param type the type the AnnotatedType corresponds to + * @param filter the type annotation targets included in this AnnotatedType + */ + public static AnnotatedType buildAnnotatedType(byte[] rawAnnotations, + ConstantPool cp, + AnnotatedElement decl, + Class container, + Type type, + TypeAnnotationTarget filter) { + TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations, + cp, + decl, + container); + List l = new ArrayList<>(tas.length); + for (TypeAnnotation t : tas) { + TypeAnnotationTargetInfo ti = t.getTargetInfo(); + if (ti.getTarget() == filter) + l.add(t); + } + TypeAnnotation[] typeAnnotations = l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); + return AnnotatedTypeFactory.buildAnnotatedType(type, + LocationInfo.BASE_LOCATION, + typeAnnotations, + typeAnnotations, + decl); + } + + /** + * Build an array of AnnotatedTypes from the parameters supplied. + * + * This method and {@code buildAnnotatedType} are probably + * the entry points you are looking for. + * + * @param rawAnnotations the byte[] encoding of all type annotations on this declaration + * @param cp the ConstantPool needed to parse the embedded Annotation + * @param decl the declaration this type annotation is on + * @param container the Class this type annotation is on (may be the same as decl) + * @param types the Types the AnnotatedTypes corresponds to + * @param filter the type annotation targets that included in this AnnotatedType + */ + public static AnnotatedType[] buildAnnotatedTypes(byte[] rawAnnotations, + ConstantPool cp, + AnnotatedElement decl, + Class container, + Type[] types, + TypeAnnotationTarget filter) { + int size = types.length; + AnnotatedType[] result = new AnnotatedType[size]; + Arrays.fill(result, AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE); + @SuppressWarnings("rawtypes") + ArrayList[] l = new ArrayList[size]; // array of ArrayList + + TypeAnnotation[] tas = parseTypeAnnotations(rawAnnotations, + cp, + decl, + container); + for (TypeAnnotation t : tas) { + TypeAnnotationTargetInfo ti = t.getTargetInfo(); + if (ti.getTarget() == filter) { + int pos = ti.getCount(); + if (l[pos] == null) { + ArrayList tmp = new ArrayList<>(tas.length); + l[pos] = tmp; + } + @SuppressWarnings("unchecked") + ArrayList tmp = l[pos]; + tmp.add(t); + } + } + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") + ArrayList list = l[i]; + TypeAnnotation[] typeAnnotations; + if (list != null) { + typeAnnotations = list.toArray(new TypeAnnotation[list.size()]); + } else { + typeAnnotations = EMPTY_TYPE_ANNOTATION_ARRAY; + } + result[i] = AnnotatedTypeFactory.buildAnnotatedType(types[i], + LocationInfo.BASE_LOCATION, + typeAnnotations, + typeAnnotations, + decl); + + } + return result; + } + + // Class helpers + + /** + * Build an AnnotatedType for the class decl's supertype. + * + * @param rawAnnotations the byte[] encoding of all type annotations on this declaration + * @param cp the ConstantPool needed to parse the embedded Annotation + * @param decl the Class which annotated supertype is being built + */ + public static AnnotatedType buildAnnotatedSuperclass(byte[] rawAnnotations, + ConstantPool cp, + Class decl) { + Type supertype = decl.getGenericSuperclass(); + if (supertype == null) + return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE; + return buildAnnotatedType(rawAnnotations, + cp, + decl, + decl, + supertype, + TypeAnnotationTarget.CLASS_EXTENDS); + } + + /** + * Build an array of AnnotatedTypes for the class decl's implemented + * interfaces. + * + * @param rawAnnotations the byte[] encoding of all type annotations on this declaration + * @param cp the ConstantPool needed to parse the embedded Annotation + * @param decl the Class whose annotated implemented interfaces is being built + */ + public static AnnotatedType[] buildAnnotatedInterfaces(byte[] rawAnnotations, + ConstantPool cp, + Class decl) { + if (decl == Object.class || + decl.isArray() || + decl.isPrimitive() || + decl == Void.TYPE) + return AnnotatedTypeFactory.EMPTY_ANNOTATED_TYPE_ARRAY; + return buildAnnotatedTypes(rawAnnotations, + cp, + decl, + decl, + decl.getGenericInterfaces(), + TypeAnnotationTarget.CLASS_IMPLEMENTS); + } + + // TypeVariable helpers + + /** + * Parse regular annotations on a TypeVariable declared on genericDecl. + * + * Regular Annotations on TypeVariables are stored in the type + * annotation byte[] in the class file. + * + * @param genericsDecl the declaration declaring the type variable + * @param typeVarIndex the 0-based index of this type variable in the declaration + */ + public static Annotation[] parseTypeVariableAnnotations(D genericDecl, + int typeVarIndex) { + AnnotatedElement decl; + TypeAnnotationTarget predicate; + if (genericDecl instanceof Class) { + decl = (Class)genericDecl; + predicate = TypeAnnotationTarget.CLASS_TYPE_PARAMETER; + } else if (genericDecl instanceof Executable) { + decl = (Executable)genericDecl; + predicate = TypeAnnotationTarget.METHOD_TYPE_PARAMETER; + } else { + throw new AssertionError("Unknown GenericDeclaration " + genericDecl + "\nthis should not happen."); + } + List typeVarAnnos = TypeAnnotation.filter(parseAllTypeAnnotations(decl), + predicate); + List res = new ArrayList<>(typeVarAnnos.size()); + for (TypeAnnotation t : typeVarAnnos) + if (t.getTargetInfo().getCount() == typeVarIndex) + res.add(t.getAnnotation()); + return res.toArray(new Annotation[0]); + } + + /** + * Build an array of AnnotatedTypes for the declaration decl's bounds. + * + * @param bounds the bounds corresponding to the annotated bounds + * @param decl the declaration whose annotated bounds is being built + * @param typeVarIndex the index of this type variable on the decl + */ + public static AnnotatedType[] parseAnnotatedBounds(Type[] bounds, + D decl, + int typeVarIndex) { + return parseAnnotatedBounds(bounds, decl, typeVarIndex, LocationInfo.BASE_LOCATION); + } + //helper for above + private static AnnotatedType[] parseAnnotatedBounds(Type[] bounds, + D decl, + int typeVarIndex, + LocationInfo loc) { + List candidates = fetchBounds(decl); + if (bounds != null) { + int startIndex = 0; + AnnotatedType[] res = new AnnotatedType[bounds.length]; + + // Adjust bounds index + // + // Figure out if the type annotations for this bound starts with 0 + // or 1. The spec says within a bound the 0:th type annotation will + // always be on an bound of a Class type (not Interface type). So + // if the programmer starts with an Interface type for the first + // (and following) bound(s) the implicit Object bound is considered + // the first (that is 0:th) bound and type annotations start on + // index 1. + if (bounds.length > 0) { + Type b0 = bounds[0]; + if (!(b0 instanceof Class)) { + startIndex = 1; + } else { + Class c = (Class)b0; + if (c.isInterface()) { + startIndex = 1; + } + } + } + + for (int i = 0; i < bounds.length; i++) { + List l = new ArrayList<>(candidates.size()); + for (TypeAnnotation t : candidates) { + TypeAnnotationTargetInfo tInfo = t.getTargetInfo(); + if (tInfo.getSecondaryIndex() == i + startIndex && + tInfo.getCount() == typeVarIndex) { + l.add(t); + } + } + res[i] = AnnotatedTypeFactory.buildAnnotatedType(bounds[i], + loc, + l.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), + candidates.toArray(EMPTY_TYPE_ANNOTATION_ARRAY), + (AnnotatedElement)decl); + } + return res; + } + return new AnnotatedType[0]; + } + private static List fetchBounds(D decl) { + AnnotatedElement boundsDecl; + TypeAnnotationTarget target; + if (decl instanceof Class) { + target = TypeAnnotationTarget.CLASS_TYPE_PARAMETER_BOUND; + boundsDecl = (Class)decl; + } else { + target = TypeAnnotationTarget.METHOD_TYPE_PARAMETER_BOUND; + boundsDecl = (Executable)decl; + } + return TypeAnnotation.filter(TypeAnnotationParser.parseAllTypeAnnotations(boundsDecl), target); + } + + /* + * Parse all type annotations on the declaration supplied. This is needed + * when you go from for example an annotated return type on a method that + * is a type variable declared on the class. In this case you need to + * 'jump' to the decl of the class and parse all type annotations there to + * find the ones that are applicable to the type variable. + */ + static TypeAnnotation[] parseAllTypeAnnotations(AnnotatedElement decl) { + Class container; + byte[] rawBytes; + JavaLangAccess javaLangAccess = sun.misc.SharedSecrets.getJavaLangAccess(); + if (decl instanceof Class) { + container = (Class)decl; + rawBytes = javaLangAccess.getRawClassTypeAnnotations(container); + } else if (decl instanceof Executable) { + container = ((Executable)decl).getDeclaringClass(); + rawBytes = javaLangAccess.getRawExecutableTypeAnnotations((Executable)decl); + } else { + // Should not reach here. Assert? + return EMPTY_TYPE_ANNOTATION_ARRAY; + } + return parseTypeAnnotations(rawBytes, javaLangAccess.getConstantPool(container), + decl, container); + } + + /* Parse type annotations encoded as an array of bytes */ + private static TypeAnnotation[] parseTypeAnnotations(byte[] rawAnnotations, + ConstantPool cp, + AnnotatedElement baseDecl, + Class container) { + if (rawAnnotations == null) + return EMPTY_TYPE_ANNOTATION_ARRAY; + + ByteBuffer buf = ByteBuffer.wrap(rawAnnotations); + int annotationCount = buf.getShort() & 0xFFFF; + List typeAnnotations = new ArrayList<>(annotationCount); + + // Parse each TypeAnnotation + for (int i = 0; i < annotationCount; i++) { + TypeAnnotation ta = parseTypeAnnotation(buf, cp, baseDecl, container); + if (ta != null) + typeAnnotations.add(ta); + } + + return typeAnnotations.toArray(EMPTY_TYPE_ANNOTATION_ARRAY); + } + + + // Helper + static Map, Annotation> mapTypeAnnotations(TypeAnnotation[] typeAnnos) { + Map, Annotation> result = + new LinkedHashMap<>(); + for (TypeAnnotation t : typeAnnos) { + Annotation a = t.getAnnotation(); + Class klass = a.annotationType(); + AnnotationType type = AnnotationType.getInstance(klass); + if (type.retention() == RetentionPolicy.RUNTIME) + if (result.put(klass, a) != null) + throw new AnnotationFormatError("Duplicate annotation for class: "+klass+": " + a); + } + return result; + } + + // Position codes + // Regular type parameter annotations + private static final byte CLASS_TYPE_PARAMETER = 0x00; + private static final byte METHOD_TYPE_PARAMETER = 0x01; + // Type Annotations outside method bodies + private static final byte CLASS_EXTENDS = 0x10; + private static final byte CLASS_TYPE_PARAMETER_BOUND = 0x11; + private static final byte METHOD_TYPE_PARAMETER_BOUND = 0x12; + private static final byte FIELD = 0x13; + private static final byte METHOD_RETURN = 0x14; + private static final byte METHOD_RECEIVER = 0x15; + private static final byte METHOD_FORMAL_PARAMETER = 0x16; + private static final byte THROWS = 0x17; + // Type Annotations inside method bodies + private static final byte LOCAL_VARIABLE = (byte)0x40; + private static final byte RESOURCE_VARIABLE = (byte)0x41; + private static final byte EXCEPTION_PARAMETER = (byte)0x42; + private static final byte INSTANCEOF = (byte)0x43; + private static final byte NEW = (byte)0x44; + private static final byte CONSTRUCTOR_REFERENCE = (byte)0x45; + private static final byte METHOD_REFERENCE = (byte)0x46; + private static final byte CAST = (byte)0x47; + private static final byte CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = (byte)0x48; + private static final byte METHOD_INVOCATION_TYPE_ARGUMENT = (byte)0x49; + private static final byte CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = (byte)0x4A; + private static final byte METHOD_REFERENCE_TYPE_ARGUMENT = (byte)0x4B; + + private static TypeAnnotation parseTypeAnnotation(ByteBuffer buf, + ConstantPool cp, + AnnotatedElement baseDecl, + Class container) { + try { + TypeAnnotationTargetInfo ti = parseTargetInfo(buf); + LocationInfo locationInfo = LocationInfo.parseLocationInfo(buf); + Annotation a = AnnotationParser.parseAnnotation(buf, cp, container, false); + if (ti == null) // Inside a method for example + return null; + return new TypeAnnotation(ti, locationInfo, a, baseDecl); + } catch (IllegalArgumentException | // Bad type in const pool at specified index + BufferUnderflowException e) { + throw new AnnotationFormatError(e); + } + } + + private static TypeAnnotationTargetInfo parseTargetInfo(ByteBuffer buf) { + int posCode = buf.get() & 0xFF; + switch(posCode) { + case CLASS_TYPE_PARAMETER: + case METHOD_TYPE_PARAMETER: { + int index = buf.get() & 0xFF; + TypeAnnotationTargetInfo res; + if (posCode == CLASS_TYPE_PARAMETER) + res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_TYPE_PARAMETER, + index); + else + res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_TYPE_PARAMETER, + index); + return res; + } // unreachable break; + case CLASS_EXTENDS: { + short index = buf.getShort(); //needs to be signed + if (index == -1) { + return new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_EXTENDS); + } else if (index >= 0) { + TypeAnnotationTargetInfo res = new TypeAnnotationTargetInfo(TypeAnnotationTarget.CLASS_IMPLEMENTS, + index); + return res; + }} break; + case CLASS_TYPE_PARAMETER_BOUND: + return parse2ByteTarget(TypeAnnotationTarget.CLASS_TYPE_PARAMETER_BOUND, buf); + case METHOD_TYPE_PARAMETER_BOUND: + return parse2ByteTarget(TypeAnnotationTarget.METHOD_TYPE_PARAMETER_BOUND, buf); + case FIELD: + return new TypeAnnotationTargetInfo(TypeAnnotationTarget.FIELD); + case METHOD_RETURN: + return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RETURN); + case METHOD_RECEIVER: + return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_RECEIVER); + case METHOD_FORMAL_PARAMETER: { + int index = buf.get() & 0xFF; + return new TypeAnnotationTargetInfo(TypeAnnotationTarget.METHOD_FORMAL_PARAMETER, + index); + } //unreachable break; + case THROWS: + return parseShortTarget(TypeAnnotationTarget.THROWS, buf); + + /* + * The ones below are inside method bodies, we don't care about them for core reflection + * other than adjusting for them in the byte stream. + */ + case LOCAL_VARIABLE: + case RESOURCE_VARIABLE: + short length = buf.getShort(); + for (int i = 0; i < length; ++i) { + short offset = buf.getShort(); + short varLength = buf.getShort(); + short index = buf.getShort(); + } + return null; + case EXCEPTION_PARAMETER: { + byte index = buf.get(); + } + return null; + case INSTANCEOF: + case NEW: + case CONSTRUCTOR_REFERENCE: + case METHOD_REFERENCE: { + short offset = buf.getShort(); + } + return null; + case CAST: + case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case METHOD_INVOCATION_TYPE_ARGUMENT: + case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case METHOD_REFERENCE_TYPE_ARGUMENT: { + short offset = buf.getShort(); + byte index = buf.get(); + } + return null; + + default: + // will throw error below + break; + } + throw new AnnotationFormatError("Could not parse bytes for type annotations"); + } + + private static TypeAnnotationTargetInfo parseShortTarget(TypeAnnotationTarget target, ByteBuffer buf) { + int index = buf.getShort() & 0xFFFF; + return new TypeAnnotationTargetInfo(target, index); + } + private static TypeAnnotationTargetInfo parse2ByteTarget(TypeAnnotationTarget target, ByteBuffer buf) { + int count = buf.get() & 0xFF; + int secondaryIndex = buf.get() & 0xFF; + return new TypeAnnotationTargetInfo(target, + count, + secondaryIndex); + } +} diff --git a/src/sun/reflect/annotation/TypeNotPresentExceptionProxy.java b/src/sun/reflect/annotation/TypeNotPresentExceptionProxy.java new file mode 100644 index 00000000..7f361a3d --- /dev/null +++ b/src/sun/reflect/annotation/TypeNotPresentExceptionProxy.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +/** + * ExceptionProxy for TypeNotPresentException. + * + * @author Josh Bloch + * @since 1.5 + */ +public class TypeNotPresentExceptionProxy extends ExceptionProxy { + private static final long serialVersionUID = 5565925172427947573L; + String typeName; + Throwable cause; + + public TypeNotPresentExceptionProxy(String typeName, Throwable cause) { + this.typeName = typeName; + this.cause = cause; + } + + protected RuntimeException generateException() { + return new TypeNotPresentException(typeName, cause); + } +} diff --git a/src/sun/reflect/generics/factory/CoreReflectionFactory.java b/src/sun/reflect/generics/factory/CoreReflectionFactory.java new file mode 100644 index 00000000..b7d5d73b --- /dev/null +++ b/src/sun/reflect/generics/factory/CoreReflectionFactory.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.factory; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; + +import sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl; +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; +import sun.reflect.generics.reflectiveObjects.TypeVariableImpl; +import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl; +import sun.reflect.generics.scope.Scope; +import sun.reflect.generics.tree.FieldTypeSignature; + + +/** + * Factory for reflective generic type objects for use by + * core reflection (java.lang.reflect). + */ +public class CoreReflectionFactory implements GenericsFactory { + private final GenericDeclaration decl; + private final Scope scope; + + private CoreReflectionFactory(GenericDeclaration d, Scope s) { + decl = d; + scope = s; + } + + private GenericDeclaration getDecl(){ return decl;} + + private Scope getScope(){ return scope;} + + + private ClassLoader getDeclsLoader() { + if (decl instanceof Class) {return ((Class) decl).getClassLoader();} + if (decl instanceof Method) { + return ((Method) decl).getDeclaringClass().getClassLoader(); + } + assert decl instanceof Constructor : "Constructor expected"; + return ((Constructor) decl).getDeclaringClass().getClassLoader(); + + } + + /** + * Factory for this class. Returns an instance of + * CoreReflectionFactory for the declaration and scope + * provided. + * This factory will produce reflective objects of the appropriate + * kind. Classes produced will be those that would be loaded by the + * defining class loader of the declaration d (if d + * is a type declaration, or by the defining loader of the declaring + * class of d otherwise. + *

    Type variables will be created or lookup as necessary in the + * scope s. + * @param d - the generic declaration (class, interface, method or + * constructor) that thsi factory services + * @param s the scope in which the factory will allocate and search for + * type variables + * @return an instance of CoreReflectionFactory + */ + public static CoreReflectionFactory make(GenericDeclaration d, Scope s) { + return new CoreReflectionFactory(d, s); + } + + public TypeVariable makeTypeVariable(String name, + FieldTypeSignature[] bounds){ + return TypeVariableImpl.make(getDecl(), name, bounds, this); + } + + public WildcardType makeWildcard(FieldTypeSignature[] ubs, + FieldTypeSignature[] lbs) { + return WildcardTypeImpl.make(ubs, lbs, this); + } + + public ParameterizedType makeParameterizedType(Type declaration, + Type[] typeArgs, + Type owner) { + return ParameterizedTypeImpl.make((Class) declaration, + typeArgs, owner); + } + + public TypeVariable findTypeVariable(String name){ + return getScope().lookup(name); + } + + public Type makeNamedType(String name){ + try {return Class.forName(name, false, // don't initialize + getDeclsLoader());} + catch (ClassNotFoundException c) { + throw new TypeNotPresentException(name, c); + } + } + + public Type makeArrayType(Type componentType){ + if (componentType instanceof Class) + return Array.newInstance((Class) componentType, 0).getClass(); + else + return GenericArrayTypeImpl.make(componentType); + } + + public Type makeByte(){return byte.class;} + public Type makeBool(){return boolean.class;} + public Type makeShort(){return short.class;} + public Type makeChar(){return char.class;} + public Type makeInt(){return int.class;} + public Type makeLong(){return long.class;} + public Type makeFloat(){return float.class;} + public Type makeDouble(){return double.class;} + + public Type makeVoid(){return void.class;} +} diff --git a/src/sun/reflect/generics/factory/GenericsFactory.java b/src/sun/reflect/generics/factory/GenericsFactory.java new file mode 100644 index 00000000..f1b57480 --- /dev/null +++ b/src/sun/reflect/generics/factory/GenericsFactory.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.factory; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; + +import sun.reflect.generics.tree.FieldTypeSignature; + +/** + * A factory interface for reflective objects representing generic types. + * Implementors (such as core reflection or JDI, or possibly javadoc + * will manufacture instances of (potentially) different classes + * in response to invocations of the methods described here. + *

    The intent is that reflective systems use these factories to + * produce generic type information on demand. + * Certain components of such reflective systems can be independent + * of a specific implementation by using this interface. For example, + * repositories of generic type information are initialized with a + * factory conforming to this interface, and use it to generate the + * tpe information they are required to provide. As a result, such + * repository code can be shared across different reflective systems. + */ +public interface GenericsFactory { + /** + * Returns a new type variable declaration. Note that name + * may be empty (but not null). If bounds is + * empty, a bound of java.lang.Object is used. + * @param name The name of the type variable + * @param bounds An array of abstract syntax trees representing + * the upper bound(s) on the type variable being declared + * @return a new type variable declaration + * @throws NullPointerException - if any of the actual parameters + * or any of the elements of bounds are null. + */ + TypeVariable makeTypeVariable(String name, + FieldTypeSignature[] bounds); + /** + * Return an instance of the ParameterizedType interface + * that corresponds to a generic type instantiation of the + * generic declaration declaration with actual type arguments + * typeArgs. + * If owner is null, the declaring class of + * declaration is used as the owner of this parameterized + * type. + *

    This method throws a MalformedParameterizedTypeException + * under the following circumstances: + * If the type declaration does not represent a generic declaration + * (i.e., it is not an instance of GenericDeclaration). + * If the number of actual type arguments (i.e., the size of the + * array typeArgs) does not correspond to the number of + * formal type arguments. + * If any of the actual type arguments is not an instance of the + * bounds on the corresponding formal. + * @param declaration - the generic type declaration that is to be + * instantiated + * @param typeArgs - the list of actual type arguments + * @return - a parameterized type representing the instantiation + * of the declaration with the actual type arguments + * @throws MalformedParameterizedTypeException - if the instantiation + * is invalid + * @throws NullPointerException - if any of declaration + * , typeArgs + * or any of the elements of typeArgs are null + */ + ParameterizedType makeParameterizedType(Type declaration, + Type[] typeArgs, + Type owner); + + /** + * Returns the type variable with name name, if such + * a type variable is declared in the + * scope used to create this factory. + * Returns null otherwise. + * @param name - the name of the type variable to search for + * @return - the type variable with name name, or null + * @throws NullPointerException - if any of actual parameters are + * null + */ + TypeVariable findTypeVariable(String name); + + /** + * Returns a new wildcard type variable. If + * ubs is empty, a bound of java.lang.Object is used. + * @param ubs An array of abstract syntax trees representing + * the upper bound(s) on the type variable being declared + * @param lbs An array of abstract syntax trees representing + * the lower bound(s) on the type variable being declared + * @return a new wildcard type variable + * @throws NullPointerException - if any of the actual parameters + * or any of the elements of ubs or lbsare + * null + */ + WildcardType makeWildcard(FieldTypeSignature[] ubs, + FieldTypeSignature[] lbs); + + Type makeNamedType(String name); + + /** + * Returns a (possibly generic) array type. + * If the component type is a parameterized type, it must + * only have unbounded wildcard arguemnts, otherwise + * a MalformedParameterizedTypeException is thrown. + * @param componentType - the component type of the array + * @return a (possibly generic) array type. + * @throws MalformedParameterizedTypeException if componentType + * is a parameterized type with non-wildcard type arguments + * @throws NullPointerException - if any of the actual parameters + * are null + */ + Type makeArrayType(Type componentType); + + /** + * Returns the reflective representation of type byte. + * @return the reflective representation of type byte. + */ + Type makeByte(); + + /** + * Returns the reflective representation of type boolean. + * @return the reflective representation of type boolean. + */ + Type makeBool(); + + /** + * Returns the reflective representation of type short. + * @return the reflective representation of type short. + */ + Type makeShort(); + + /** + * Returns the reflective representation of type char. + * @return the reflective representation of type char. + */ + Type makeChar(); + + /** + * Returns the reflective representation of type int. + * @return the reflective representation of type int. + */ + Type makeInt(); + + /** + * Returns the reflective representation of type long. + * @return the reflective representation of type long. + */ + Type makeLong(); + + /** + * Returns the reflective representation of type float. + * @return the reflective representation of type float. + */ + Type makeFloat(); + + /** + * Returns the reflective representation of type double. + * @return the reflective representation of type double. + */ + Type makeDouble(); + + /** + * Returns the reflective representation of void. + * @return the reflective representation of void. + */ + Type makeVoid(); +} diff --git a/src/sun/reflect/generics/parser/SignatureParser.java b/src/sun/reflect/generics/parser/SignatureParser.java new file mode 100644 index 00000000..f28f1ce0 --- /dev/null +++ b/src/sun/reflect/generics/parser/SignatureParser.java @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.parser; + +import java.lang.reflect.GenericSignatureFormatError; +import java.util.ArrayList; +import java.util.List; + +import sun.reflect.generics.tree.ArrayTypeSignature; +import sun.reflect.generics.tree.BaseType; +import sun.reflect.generics.tree.BooleanSignature; +import sun.reflect.generics.tree.BottomSignature; +import sun.reflect.generics.tree.ByteSignature; +import sun.reflect.generics.tree.CharSignature; +import sun.reflect.generics.tree.ClassSignature; +import sun.reflect.generics.tree.ClassTypeSignature; +import sun.reflect.generics.tree.DoubleSignature; +import sun.reflect.generics.tree.FieldTypeSignature; +import sun.reflect.generics.tree.FloatSignature; +import sun.reflect.generics.tree.FormalTypeParameter; +import sun.reflect.generics.tree.IntSignature; +import sun.reflect.generics.tree.LongSignature; +import sun.reflect.generics.tree.MethodTypeSignature; +import sun.reflect.generics.tree.ReturnType; +import sun.reflect.generics.tree.ShortSignature; +import sun.reflect.generics.tree.SimpleClassTypeSignature; +import sun.reflect.generics.tree.TypeArgument; +import sun.reflect.generics.tree.TypeSignature; +import sun.reflect.generics.tree.TypeVariableSignature; +import sun.reflect.generics.tree.VoidDescriptor; +import sun.reflect.generics.tree.Wildcard; + +/** + * Parser for type signatures, as defined in the Java Virtual + * Machine Specification (JVMS) chapter 4. + * Converts the signatures into an abstract syntax tree (AST) representation. + * See the package sun.reflect.generics.tree for details of the AST. + */ +public class SignatureParser { + // The input is conceptually a character stream (though currently it's + // a string). This is slightly different than traditional parsers, + // because there is no lexical scanner performing tokenization. + // Having a separate tokenizer does not fit with the nature of the + // input format. + // Other than the absence of a tokenizer, this parser is a classic + // recursive descent parser. Its structure corresponds as closely + // as possible to the grammar in the JVMS. + // + // A note on asserts vs. errors: The code contains assertions + // in situations that should never occur. An assertion failure + // indicates a failure of the parser logic. A common pattern + // is an assertion that the current input is a particular + // character. This is often paired with a separate check + // that this is the case, which seems redundant. For example: + // + // assert(current() != x); + // if (current != x {error("expected an x"); + // + // where x is some character constant. + // The assertion indicates, that, as currently written, + // the code should never reach this point unless the input is an + // x. On the other hand, the test is there to check the legality + // of the input wrt to a given production. It may be that at a later + // time the code might be called directly, and if the input is + // invalid, the parser should flag an error in accordance + // with its logic. + + private char[] input; // the input signature + private int index = 0; // index into the input + // used to mark end of input + private static final char EOI = ':'; + private static final boolean DEBUG = false; + + // private constructor - enforces use of static factory + private SignatureParser(){} + + // Utility methods. + + // Most parsing routines use the following routines to access the + // input stream, and advance it as necessary. + // This makes it easy to adapt the parser to operate on streams + // of various kinds as well as strings. + + // returns current element of the input and advances the input + private char getNext(){ + assert(index <= input.length); + try { + return input[index++]; + } catch (ArrayIndexOutOfBoundsException e) { return EOI;} + } + + // returns current element of the input + private char current(){ + assert(index <= input.length); + try { + return input[index]; + } catch (ArrayIndexOutOfBoundsException e) { return EOI;} + } + + // advance the input + private void advance(){ + assert(index <= input.length); + index++; + } + + // For debugging, prints current character to the end of the input. + private String remainder() { + return new String(input, index, input.length-index); + } + + // Match c against a "set" of characters + private boolean matches(char c, char... set) { + for (char e : set) { + if (c == e) return true; + } + return false; + } + + // Error handling routine. Encapsulates error handling. + // Takes a string error message as argument. + // Currently throws a GenericSignatureFormatError. + + private Error error(String errorMsg) { + return new GenericSignatureFormatError("Signature Parse error: " + errorMsg + + "\n\tRemaining input: " + remainder()); + } + + /** + * Verify the parse has made forward progress; throw an exception + * if no progress. + */ + private void progress(int startingPosition) { + if (index <= startingPosition) + throw error("Failure to make progress!"); + } + + /** + * Static factory method. Produces a parser instance. + * @return an instance of SignatureParser + */ + public static SignatureParser make() { + return new SignatureParser(); + } + + /** + * Parses a class signature (as defined in the JVMS, chapter 4) + * and produces an abstract syntax tree representing it. + * @param s a string representing the input class signature + * @return An abstract syntax tree for a class signature + * corresponding to the input string + * @throws GenericSignatureFormatError if the input is not a valid + * class signature + */ + public ClassSignature parseClassSig(String s) { + if (DEBUG) System.out.println("Parsing class sig:" + s); + input = s.toCharArray(); + return parseClassSignature(); + } + + /** + * Parses a method signature (as defined in the JVMS, chapter 4) + * and produces an abstract syntax tree representing it. + * @param s a string representing the input method signature + * @return An abstract syntax tree for a method signature + * corresponding to the input string + * @throws GenericSignatureFormatError if the input is not a valid + * method signature + */ + public MethodTypeSignature parseMethodSig(String s) { + if (DEBUG) System.out.println("Parsing method sig:" + s); + input = s.toCharArray(); + return parseMethodTypeSignature(); + } + + + /** + * Parses a type signature + * and produces an abstract syntax tree representing it. + * + * @param s a string representing the input type signature + * @return An abstract syntax tree for a type signature + * corresponding to the input string + * @throws GenericSignatureFormatError if the input is not a valid + * type signature + */ + public TypeSignature parseTypeSig(String s) { + if (DEBUG) System.out.println("Parsing type sig:" + s); + input = s.toCharArray(); + return parseTypeSignature(); + } + + // Parsing routines. + // As a rule, the parsing routines access the input using the + // utilities current(), getNext() and/or advance(). + // The convention is that when a parsing routine is invoked + // it expects the current input to be the first character it should parse + // and when it completes parsing, it leaves the input at the first + // character after the input parses. + + /* + * Note on grammar conventions: a trailing "*" matches zero or + * more occurrences, a trailing "+" matches one or more occurrences, + * "_opt" indicates an optional component. + */ + + /** + * ClassSignature: + * FormalTypeParameters_opt SuperclassSignature SuperinterfaceSignature* + */ + private ClassSignature parseClassSignature() { + // parse a class signature based on the implicit input. + assert(index == 0); + return ClassSignature.make(parseZeroOrMoreFormalTypeParameters(), + parseClassTypeSignature(), // Only rule for SuperclassSignature + parseSuperInterfaces()); + } + + private FormalTypeParameter[] parseZeroOrMoreFormalTypeParameters(){ + if (current() == '<') { + return parseFormalTypeParameters(); + } else { + return new FormalTypeParameter[0]; + } + } + + /** + * FormalTypeParameters: + * "<" FormalTypeParameter+ ">" + */ + private FormalTypeParameter[] parseFormalTypeParameters(){ + List ftps = new ArrayList<>(3); + assert(current() == '<'); // should not have been called at all + if (current() != '<') { throw error("expected '<'");} + advance(); + ftps.add(parseFormalTypeParameter()); + while (current() != '>') { + int startingPosition = index; + ftps.add(parseFormalTypeParameter()); + progress(startingPosition); + } + advance(); + return ftps.toArray(new FormalTypeParameter[ftps.size()]); + } + + /** + * FormalTypeParameter: + * Identifier ClassBound InterfaceBound* + */ + private FormalTypeParameter parseFormalTypeParameter(){ + String id = parseIdentifier(); + FieldTypeSignature[] bs = parseBounds(); + return FormalTypeParameter.make(id, bs); + } + + private String parseIdentifier(){ + StringBuilder result = new StringBuilder(); + while (!Character.isWhitespace(current())) { + char c = current(); + switch(c) { + case ';': + case '.': + case '/': + case '[': + case ':': + case '>': + case '<': + return result.toString(); + default:{ + result.append(c); + advance(); + } + + } + } + return result.toString(); + } + /** + * FieldTypeSignature: + * ClassTypeSignature + * ArrayTypeSignature + * TypeVariableSignature + */ + private FieldTypeSignature parseFieldTypeSignature() { + return parseFieldTypeSignature(true); + } + + private FieldTypeSignature parseFieldTypeSignature(boolean allowArrays) { + switch(current()) { + case 'L': + return parseClassTypeSignature(); + case 'T': + return parseTypeVariableSignature(); + case '[': + if (allowArrays) + return parseArrayTypeSignature(); + else + throw error("Array signature not allowed here."); + default: throw error("Expected Field Type Signature"); + } + } + + /** + * ClassTypeSignature: + * "L" PackageSpecifier_opt SimpleClassTypeSignature ClassTypeSignatureSuffix* ";" + */ + private ClassTypeSignature parseClassTypeSignature(){ + assert(current() == 'L'); + if (current() != 'L') { throw error("expected a class type");} + advance(); + List scts = new ArrayList<>(5); + scts.add(parsePackageNameAndSimpleClassTypeSignature()); + + parseClassTypeSignatureSuffix(scts); + if (current() != ';') + throw error("expected ';' got '" + current() + "'"); + + advance(); + return ClassTypeSignature.make(scts); + } + + /** + * PackageSpecifier: + * Identifier "/" PackageSpecifier* + */ + private SimpleClassTypeSignature parsePackageNameAndSimpleClassTypeSignature() { + // Parse both any optional leading PackageSpecifier as well as + // the following SimpleClassTypeSignature. + + String id = parseIdentifier(); + + if (current() == '/') { // package name + StringBuilder idBuild = new StringBuilder(id); + + while(current() == '/') { + advance(); + idBuild.append("."); + idBuild.append(parseIdentifier()); + } + id = idBuild.toString(); + } + + switch (current()) { + case ';': + return SimpleClassTypeSignature.make(id, false, new TypeArgument[0]); // all done! + case '<': + if (DEBUG) System.out.println("\t remainder: " + remainder()); + return SimpleClassTypeSignature.make(id, false, parseTypeArguments()); + default: + throw error("expected '<' or ';' but got " + current()); + } + } + + /** + * SimpleClassTypeSignature: + * Identifier TypeArguments_opt + */ + private SimpleClassTypeSignature parseSimpleClassTypeSignature(boolean dollar){ + String id = parseIdentifier(); + char c = current(); + + switch (c) { + case ';': + case '.': + return SimpleClassTypeSignature.make(id, dollar, new TypeArgument[0]) ; + case '<': + return SimpleClassTypeSignature.make(id, dollar, parseTypeArguments()); + default: + throw error("expected '<' or ';' or '.', got '" + c + "'."); + } + } + + /** + * ClassTypeSignatureSuffix: + * "." SimpleClassTypeSignature + */ + private void parseClassTypeSignatureSuffix(List scts) { + while (current() == '.') { + advance(); + scts.add(parseSimpleClassTypeSignature(true)); + } + } + + private TypeArgument[] parseTypeArgumentsOpt() { + if (current() == '<') {return parseTypeArguments();} + else {return new TypeArgument[0];} + } + + /** + * TypeArguments: + * "<" TypeArgument+ ">" + */ + private TypeArgument[] parseTypeArguments() { + List tas = new ArrayList<>(3); + assert(current() == '<'); + if (current() != '<') { throw error("expected '<'");} + advance(); + tas.add(parseTypeArgument()); + while (current() != '>') { + //(matches(current(), '+', '-', 'L', '[', 'T', '*')) { + tas.add(parseTypeArgument()); + } + advance(); + return tas.toArray(new TypeArgument[tas.size()]); + } + + /** + * TypeArgument: + * WildcardIndicator_opt FieldTypeSignature + * "*" + */ + private TypeArgument parseTypeArgument() { + FieldTypeSignature[] ub, lb; + ub = new FieldTypeSignature[1]; + lb = new FieldTypeSignature[1]; + TypeArgument[] ta = new TypeArgument[0]; + char c = current(); + switch (c) { + case '+': { + advance(); + ub[0] = parseFieldTypeSignature(); + lb[0] = BottomSignature.make(); // bottom + return Wildcard.make(ub, lb); + } + case '*':{ + advance(); + ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta); + lb[0] = BottomSignature.make(); // bottom + return Wildcard.make(ub, lb); + } + case '-': { + advance(); + lb[0] = parseFieldTypeSignature(); + ub[0] = SimpleClassTypeSignature.make("java.lang.Object", false, ta); + return Wildcard.make(ub, lb); + } + default: + return parseFieldTypeSignature(); + } + } + + /** + * TypeVariableSignature: + * "T" Identifier ";" + */ + private TypeVariableSignature parseTypeVariableSignature() { + assert(current() == 'T'); + if (current() != 'T') { throw error("expected a type variable usage");} + advance(); + TypeVariableSignature ts = TypeVariableSignature.make(parseIdentifier()); + if (current() != ';') { + throw error("; expected in signature of type variable named" + + ts.getIdentifier()); + } + advance(); + return ts; + } + + /** + * ArrayTypeSignature: + * "[" TypeSignature + */ + private ArrayTypeSignature parseArrayTypeSignature() { + if (current() != '[') {throw error("expected array type signature");} + advance(); + return ArrayTypeSignature.make(parseTypeSignature()); + } + + /** + * TypeSignature: + * FieldTypeSignature + * BaseType + */ + private TypeSignature parseTypeSignature() { + switch (current()) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + return parseBaseType(); + + default: + return parseFieldTypeSignature(); + } + } + + private BaseType parseBaseType() { + switch(current()) { + case 'B': + advance(); + return ByteSignature.make(); + case 'C': + advance(); + return CharSignature.make(); + case 'D': + advance(); + return DoubleSignature.make(); + case 'F': + advance(); + return FloatSignature.make(); + case 'I': + advance(); + return IntSignature.make(); + case 'J': + advance(); + return LongSignature.make(); + case 'S': + advance(); + return ShortSignature.make(); + case 'Z': + advance(); + return BooleanSignature.make(); + default: { + assert(false); + throw error("expected primitive type"); + } + } + } + + /** + * ClassBound: + * ":" FieldTypeSignature_opt + * + * InterfaceBound: + * ":" FieldTypeSignature + */ + private FieldTypeSignature[] parseBounds() { + List fts = new ArrayList<>(3); + + if (current() == ':') { + advance(); + switch(current()) { + case ':': // empty class bound + break; + + default: // parse class bound + fts.add(parseFieldTypeSignature()); + } + + // zero or more interface bounds + while (current() == ':') { + advance(); + fts.add(parseFieldTypeSignature()); + } + } else + error("Bound expected"); + + return fts.toArray(new FieldTypeSignature[fts.size()]); + } + + /** + * SuperclassSignature: + * ClassTypeSignature + */ + private ClassTypeSignature[] parseSuperInterfaces() { + List cts = new ArrayList<>(5); + while(current() == 'L') { + cts.add(parseClassTypeSignature()); + } + return cts.toArray(new ClassTypeSignature[cts.size()]); + } + + + /** + * MethodTypeSignature: + * FormalTypeParameters_opt "(" TypeSignature* ")" ReturnType ThrowsSignature* + */ + private MethodTypeSignature parseMethodTypeSignature() { + // Parse a method signature based on the implicit input. + FieldTypeSignature[] ets; + + assert(index == 0); + return MethodTypeSignature.make(parseZeroOrMoreFormalTypeParameters(), + parseFormalParameters(), + parseReturnType(), + parseZeroOrMoreThrowsSignatures()); + } + + // "(" TypeSignature* ")" + private TypeSignature[] parseFormalParameters() { + if (current() != '(') {throw error("expected '('");} + advance(); + TypeSignature[] pts = parseZeroOrMoreTypeSignatures(); + if (current() != ')') {throw error("expected ')'");} + advance(); + return pts; + } + + // TypeSignature* + private TypeSignature[] parseZeroOrMoreTypeSignatures() { + List ts = new ArrayList<>(); + boolean stop = false; + while (!stop) { + switch(current()) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + case 'L': + case 'T': + case '[': { + ts.add(parseTypeSignature()); + break; + } + default: stop = true; + } + } + return ts.toArray(new TypeSignature[ts.size()]); + } + + /** + * ReturnType: + * TypeSignature + * VoidDescriptor + */ + private ReturnType parseReturnType(){ + if (current() == 'V') { + advance(); + return VoidDescriptor.make(); + } else + return parseTypeSignature(); + } + + // ThrowSignature* + private FieldTypeSignature[] parseZeroOrMoreThrowsSignatures(){ + List ets = new ArrayList<>(3); + while( current() == '^') { + ets.add(parseThrowsSignature()); + } + return ets.toArray(new FieldTypeSignature[ets.size()]); + } + + /** + * ThrowsSignature: + * "^" ClassTypeSignature + * "^" TypeVariableSignature + */ + private FieldTypeSignature parseThrowsSignature() { + assert(current() == '^'); + if (current() != '^') { throw error("expected throws signature");} + advance(); + return parseFieldTypeSignature(false); + } + } diff --git a/src/sun/reflect/generics/reflectiveObjects/GenericArrayTypeImpl.java b/src/sun/reflect/generics/reflectiveObjects/GenericArrayTypeImpl.java new file mode 100644 index 00000000..7520403f --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/GenericArrayTypeImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Type; +import java.util.Objects; + +/** + * Implementation of GenericArrayType interface for core reflection. + */ +public class GenericArrayTypeImpl + implements GenericArrayType { + private final Type genericComponentType; + + // private constructor enforces use of static factory + private GenericArrayTypeImpl(Type ct) { + genericComponentType = ct; + } + + /** + * Factory method. + * @param ct - the desired component type of the generic array type + * being created + * @return a generic array type with the desired component type + */ + public static GenericArrayTypeImpl make(Type ct) { + return new GenericArrayTypeImpl(ct); + } + + + /** + * Returns a Type object representing the component type + * of this array. + * + * @return a Type object representing the component type + * of this array + * @since 1.5 + */ + public Type getGenericComponentType() { + return genericComponentType; // return cached component type + } + + public String toString() { + Type componentType = getGenericComponentType(); + StringBuilder sb = new StringBuilder(); + + if (componentType instanceof Class) + sb.append(((Class)componentType).getName() ); + else + sb.append(componentType.toString()); + sb.append("[]"); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof GenericArrayType) { + GenericArrayType that = (GenericArrayType) o; + + return Objects.equals(genericComponentType, that.getGenericComponentType()); + } else + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(genericComponentType); + } +} diff --git a/src/sun/reflect/generics/reflectiveObjects/LazyReflectiveObjectGenerator.java b/src/sun/reflect/generics/reflectiveObjects/LazyReflectiveObjectGenerator.java new file mode 100644 index 00000000..0bc6f608 --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/LazyReflectiveObjectGenerator.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.visitor.Reifier; + + +/** + * Common infrastructure for things that lazily generate reflective generics + * objects. + *

    In all these cases, one needs produce a visitor that will, on demand, + * traverse the stored AST(s) and reify them into reflective objects. + * The visitor needs to be initialized with a factory, which will be + * provided when the instance is initialized. + * The factory should be cached. + * +*/ +public abstract class LazyReflectiveObjectGenerator { + private final GenericsFactory factory; // cached factory + + protected LazyReflectiveObjectGenerator(GenericsFactory f) { + factory = f; + } + + // accessor for factory + private GenericsFactory getFactory() { + return factory; + } + + // produce a reifying visitor (could this be typed as a TypeTreeVisitor? + protected Reifier getReifier(){return Reifier.make(getFactory());} + +} diff --git a/src/sun/reflect/generics/reflectiveObjects/NotImplementedException.java b/src/sun/reflect/generics/reflectiveObjects/NotImplementedException.java new file mode 100644 index 00000000..85bcbc52 --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/NotImplementedException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + +/** Temporary class used to indicate missing functionality */ +public class NotImplementedException extends RuntimeException { + private static final long serialVersionUID = -9177857708926624790L; +} diff --git a/src/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java b/src/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java new file mode 100644 index 00000000..ebf124b4 --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + +import java.lang.reflect.MalformedParameterizedTypeException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.Objects; + +/** Implementing class for ParameterizedType interface. */ + +public class ParameterizedTypeImpl implements ParameterizedType { + private final Type[] actualTypeArguments; + private final Class rawType; + private final Type ownerType; + + private ParameterizedTypeImpl(Class rawType, + Type[] actualTypeArguments, + Type ownerType) { + this.actualTypeArguments = actualTypeArguments; + this.rawType = rawType; + this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass(); + validateConstructorArguments(); + } + + private void validateConstructorArguments() { + TypeVariable[] formals = rawType.getTypeParameters(); + // check correct arity of actual type args + if (formals.length != actualTypeArguments.length){ + throw new MalformedParameterizedTypeException(); + } + for (int i = 0; i < actualTypeArguments.length; i++) { + // check actuals against formals' bounds + } + } + + /** + * Static factory. Given a (generic) class, actual type arguments + * and an owner type, creates a parameterized type. + * This class can be instantiated with a a raw type that does not + * represent a generic type, provided the list of actual type + * arguments is empty. + * If the ownerType argument is null, the declaring class of the + * raw type is used as the owner type. + *

    This method throws a MalformedParameterizedTypeException + * under the following circumstances: + * If the number of actual type arguments (i.e., the size of the + * array typeArgs) does not correspond to the number of + * formal type arguments. + * If any of the actual type arguments is not an instance of the + * bounds on the corresponding formal. + * @param rawType the Class representing the generic type declaration being + * instantiated + * @param actualTypeArguments - a (possibly empty) array of types + * representing the actual type arguments to the parameterized type + * @param ownerType - the enclosing type, if known. + * @return An instance of ParameterizedType + * @throws MalformedParameterizedTypeException - if the instantiation + * is invalid + */ + public static ParameterizedTypeImpl make(Class rawType, + Type[] actualTypeArguments, + Type ownerType) { + return new ParameterizedTypeImpl(rawType, actualTypeArguments, + ownerType); + } + + + /** + * Returns an array of Type objects representing the actual type + * arguments to this type. + * + *

    Note that in some cases, the returned array be empty. This can occur + * if this type represents a non-parameterized type nested within + * a parameterized type. + * + * @return an array of Type objects representing the actual type + * arguments to this type + * @throws TypeNotPresentException if any of the + * actual type arguments refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if any of the + * actual type parameters refer to a parameterized type that cannot + * be instantiated for any reason + * @since 1.5 + */ + public Type[] getActualTypeArguments() { + return actualTypeArguments.clone(); + } + + /** + * Returns the Type object representing the class or interface + * that declared this type. + * + * @return the Type object representing the class or interface + * that declared this type + */ + public Class getRawType() { + return rawType; + } + + + /** + * Returns a Type object representing the type that this type + * is a member of. For example, if this type is O.I, + * return a representation of O. + * + *

    If this type is a top-level type, null is returned. + * + * @return a Type object representing the type that + * this type is a member of. If this type is a top-level type, + * null is returned + * @throws TypeNotPresentException if the owner type + * refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if the owner type + * refers to a parameterized type that cannot be instantiated + * for any reason + * + */ + public Type getOwnerType() { + return ownerType; + } + + /* + * From the JavaDoc for java.lang.reflect.ParameterizedType + * "Instances of classes that implement this interface must + * implement an equals() method that equates any two instances + * that share the same generic type declaration and have equal + * type parameters." + */ + @Override + public boolean equals(Object o) { + if (o instanceof ParameterizedType) { + // Check that information is equivalent + ParameterizedType that = (ParameterizedType) o; + + if (this == that) + return true; + + Type thatOwner = that.getOwnerType(); + Type thatRawType = that.getRawType(); + + if (false) { // Debugging + boolean ownerEquality = (ownerType == null ? + thatOwner == null : + ownerType.equals(thatOwner)); + boolean rawEquality = (rawType == null ? + thatRawType == null : + rawType.equals(thatRawType)); + + boolean typeArgEquality = Arrays.equals(actualTypeArguments, // avoid clone + that.getActualTypeArguments()); + for (Type t : actualTypeArguments) { + System.out.printf("\t\t%s%s%n", t, t.getClass()); + } + + System.out.printf("\towner %s\traw %s\ttypeArg %s%n", + ownerEquality, rawEquality, typeArgEquality); + return ownerEquality && rawEquality && typeArgEquality; + } + + return + Objects.equals(ownerType, thatOwner) && + Objects.equals(rawType, thatRawType) && + Arrays.equals(actualTypeArguments, // avoid clone + that.getActualTypeArguments()); + } else + return false; + } + + @Override + public int hashCode() { + return + Arrays.hashCode(actualTypeArguments) ^ + Objects.hashCode(ownerType) ^ + Objects.hashCode(rawType); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + + if (ownerType != null) { + if (ownerType instanceof Class) + sb.append(((Class)ownerType).getName()); + else + sb.append(ownerType.toString()); + + sb.append("."); + + if (ownerType instanceof ParameterizedTypeImpl) { + // Find simple name of nested type by removing the + // shared prefix with owner. + sb.append(rawType.getName().replace( ((ParameterizedTypeImpl)ownerType).rawType.getName() + "$", + "")); + } else + sb.append(rawType.getName()); + } else + sb.append(rawType.getName()); + + if (actualTypeArguments != null && + actualTypeArguments.length > 0) { + sb.append("<"); + boolean first = true; + for(Type t: actualTypeArguments) { + if (!first) + sb.append(", "); + sb.append(t.getTypeName()); + first = false; + } + sb.append(">"); + } + + return sb.toString(); + } +} diff --git a/src/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java b/src/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java new file mode 100644 index 00000000..bcc09ac0 --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/TypeVariableImpl.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + +import java.lang.annotation.Annotation; +import java.lang.annotation.AnnotationFormatError; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +import sun.reflect.annotation.AnnotationSupport; +import sun.reflect.annotation.AnnotationType; +import sun.reflect.annotation.TypeAnnotationParser; +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.tree.FieldTypeSignature; +import sun.reflect.generics.visitor.Reifier; +import sun.reflect.misc.ReflectUtil; + +/** + * Implementation of java.lang.reflect.TypeVariable interface + * for core reflection. + */ +public class TypeVariableImpl + extends LazyReflectiveObjectGenerator implements TypeVariable { + D genericDeclaration; + private String name; + // upper bounds - evaluated lazily + private Type[] bounds; + + // The ASTs for the bounds. We are required to evaluate the bounds + // lazily, so we store these at least until we are first asked + // for the bounds. This also neatly solves the + // problem with F-bounds - you can't reify them before the formal + // is defined. + private FieldTypeSignature[] boundASTs; + + // constructor is private to enforce access through static factory + private TypeVariableImpl(D decl, String n, FieldTypeSignature[] bs, + GenericsFactory f) { + super(f); + genericDeclaration = decl; + name = n; + boundASTs = bs; + } + + // Accessors + + // accessor for ASTs for bounds. Must not be called after + // bounds have been evaluated, because we might throw the ASTs + // away (but that is not thread-safe, is it?) + private FieldTypeSignature[] getBoundASTs() { + // check that bounds were not evaluated yet + assert(bounds == null); + return boundASTs; + } + + /** + * Factory method. + * @param decl - the reflective object that declared the type variable + * that this method should create + * @param name - the name of the type variable to be returned + * @param bs - an array of ASTs representing the bounds for the type + * variable to be created + * @param f - a factory that can be used to manufacture reflective + * objects that represent the bounds of this type variable + * @return A type variable with name, bounds, declaration and factory + * specified + */ + public static + TypeVariableImpl make(T decl, String name, + FieldTypeSignature[] bs, + GenericsFactory f) { + + if (!((decl instanceof Class) || + (decl instanceof Method) || + (decl instanceof Constructor))) { + throw new AssertionError("Unexpected kind of GenericDeclaration" + + decl.getClass().toString()); + } + return new TypeVariableImpl(decl, name, bs, f); + } + + + /** + * Returns an array of Type objects representing the + * upper bound(s) of this type variable. Note that if no upper bound is + * explicitly declared, the upper bound is Object. + * + *

    For each upper bound B: + *

    + * + * @throws TypeNotPresentException if any of the + * bounds refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if any of the + * bounds refer to a parameterized type that cannot be instantiated + * for any reason + * @return an array of Types representing the upper bound(s) of this + * type variable + */ + public Type[] getBounds() { + // lazily initialize bounds if necessary + if (bounds == null) { + FieldTypeSignature[] fts = getBoundASTs(); // get AST + // allocate result array; note that + // keeping ts and bounds separate helps with threads + Type[] ts = new Type[fts.length]; + // iterate over bound trees, reifying each in turn + for ( int j = 0; j < fts.length; j++) { + Reifier r = getReifier(); + fts[j].accept(r); + ts[j] = r.getResult(); + } + // cache result + bounds = ts; + // could throw away bound ASTs here; thread safety? + } + return bounds.clone(); // return cached bounds + } + + /** + * Returns the GenericDeclaration object representing the + * generic declaration that declared this type variable. + * + * @return the generic declaration that declared this type variable. + * + * @since 1.5 + */ + public D getGenericDeclaration(){ + if (genericDeclaration instanceof Class) + ReflectUtil.checkPackageAccess((Class)genericDeclaration); + else if ((genericDeclaration instanceof Method) || + (genericDeclaration instanceof Constructor)) + ReflectUtil.conservativeCheckMemberAccess((Member)genericDeclaration); + else + throw new AssertionError("Unexpected kind of GenericDeclaration"); + return genericDeclaration; + } + + + /** + * Returns the name of this type variable, as it occurs in the source code. + * + * @return the name of this type variable, as it appears in the source code + */ + public String getName() { return name; } + + public String toString() {return getName();} + + @Override + public boolean equals(Object o) { + if (o instanceof TypeVariable && + o.getClass() == TypeVariableImpl.class) { + TypeVariable that = (TypeVariable) o; + + GenericDeclaration thatDecl = that.getGenericDeclaration(); + String thatName = that.getName(); + + return Objects.equals(genericDeclaration, thatDecl) && + Objects.equals(name, thatName); + + } else + return false; + } + + @Override + public int hashCode() { + return genericDeclaration.hashCode() ^ name.hashCode(); + } + + // Implementations of AnnotatedElement methods. + @SuppressWarnings("unchecked") + public T getAnnotation(Class annotationClass) { + Objects.requireNonNull(annotationClass); + // T is an Annotation type, the return value of get will be an annotation + return (T)mapAnnotations(getAnnotations()).get(annotationClass); + } + + public T getDeclaredAnnotation(Class annotationClass) { + Objects.requireNonNull(annotationClass); + return getAnnotation(annotationClass); + } + + @Override + public T[] getAnnotationsByType(Class annotationClass) { + Objects.requireNonNull(annotationClass); + return AnnotationSupport.getDirectlyAndIndirectlyPresent(mapAnnotations(getAnnotations()), annotationClass); + } + + @Override + public T[] getDeclaredAnnotationsByType(Class annotationClass) { + Objects.requireNonNull(annotationClass); + return getAnnotationsByType(annotationClass); + } + + public Annotation[] getAnnotations() { + int myIndex = typeVarIndex(); + if (myIndex < 0) + throw new AssertionError("Index must be non-negative."); + return TypeAnnotationParser.parseTypeVariableAnnotations(getGenericDeclaration(), myIndex); + } + + public Annotation[] getDeclaredAnnotations() { + return getAnnotations(); + } + + public AnnotatedType[] getAnnotatedBounds() { + return TypeAnnotationParser.parseAnnotatedBounds(getBounds(), + getGenericDeclaration(), + typeVarIndex()); + } + + private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + + // Helpers for annotation methods + private int typeVarIndex() { + TypeVariable[] tVars = getGenericDeclaration().getTypeParameters(); + int i = -1; + for (TypeVariable v : tVars) { + i++; + if (equals(v)) + return i; + } + return -1; + } + + private static Map, Annotation> mapAnnotations(Annotation[] annos) { + Map, Annotation> result = + new LinkedHashMap<>(); + for (Annotation a : annos) { + Class klass = a.annotationType(); + AnnotationType type = AnnotationType.getInstance(klass); + if (type.retention() == RetentionPolicy.RUNTIME) + if (result.put(klass, a) != null) + throw new AnnotationFormatError("Duplicate annotation for class: "+klass+": " + a); + } + return result; + } +} diff --git a/src/sun/reflect/generics/reflectiveObjects/WildcardTypeImpl.java b/src/sun/reflect/generics/reflectiveObjects/WildcardTypeImpl.java new file mode 100644 index 00000000..b6d193b0 --- /dev/null +++ b/src/sun/reflect/generics/reflectiveObjects/WildcardTypeImpl.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.reflectiveObjects; + + +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.Arrays; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.tree.FieldTypeSignature; +import sun.reflect.generics.visitor.Reifier; + + +/** + * Implementation of WildcardType interface for core reflection. + */ +public class WildcardTypeImpl extends LazyReflectiveObjectGenerator + implements WildcardType { + // upper bounds - evaluated lazily + private Type[] upperBounds; + // lower bounds - evaluated lazily + private Type[] lowerBounds; + // The ASTs for the bounds. We are required to evaluate the bounds + // lazily, so we store these at least until we are first asked + // for the bounds. This also neatly solves the + // problem with F-bounds - you can't reify them before the formal + // is defined. + private FieldTypeSignature[] upperBoundASTs; + private FieldTypeSignature[] lowerBoundASTs; + + // constructor is private to enforce access through static factory + private WildcardTypeImpl(FieldTypeSignature[] ubs, + FieldTypeSignature[] lbs, + GenericsFactory f) { + super(f); + upperBoundASTs = ubs; + lowerBoundASTs = lbs; + } + + /** + * Factory method. + * @param ubs - an array of ASTs representing the upper bounds for the type + * variable to be created + * @param lbs - an array of ASTs representing the lower bounds for the type + * variable to be created + * @param f - a factory that can be used to manufacture reflective + * objects that represent the bounds of this wildcard type + * @return a wild card type with the requested bounds and factory + */ + public static WildcardTypeImpl make(FieldTypeSignature[] ubs, + FieldTypeSignature[] lbs, + GenericsFactory f) { + return new WildcardTypeImpl(ubs, lbs, f); + } + + // Accessors + + // accessor for ASTs for upper bounds. Must not be called after upper + // bounds have been evaluated, because we might throw the ASTs + // away (but that is not thread-safe, is it?) + private FieldTypeSignature[] getUpperBoundASTs() { + // check that upper bounds were not evaluated yet + assert(upperBounds == null); + return upperBoundASTs; + } + // accessor for ASTs for lower bounds. Must not be called after lower + // bounds have been evaluated, because we might throw the ASTs + // away (but that is not thread-safe, is it?) + private FieldTypeSignature[] getLowerBoundASTs() { + // check that lower bounds were not evaluated yet + assert(lowerBounds == null); + return lowerBoundASTs; + } + + /** + * Returns an array of Type objects representing the upper + * bound(s) of this type variable. Note that if no upper bound is + * explicitly declared, the upper bound is Object. + * + *

    For each upper bound B : + *

      + *
    • if B is a parameterized type or a type variable, it is created, + * (see {@link #ParameterizedType} for the details of the creation + * process for parameterized types). + *
    • Otherwise, B is resolved. + *
    + * + * @return an array of Types representing the upper bound(s) of this + * type variable + * @throws TypeNotPresentException if any of the + * bounds refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if any of the + * bounds refer to a parameterized type that cannot be instantiated + * for any reason + */ + public Type[] getUpperBounds() { + // lazily initialize bounds if necessary + if (upperBounds == null) { + FieldTypeSignature[] fts = getUpperBoundASTs(); // get AST + + // allocate result array; note that + // keeping ts and bounds separate helps with threads + Type[] ts = new Type[fts.length]; + // iterate over bound trees, reifying each in turn + for ( int j = 0; j < fts.length; j++) { + Reifier r = getReifier(); + fts[j].accept(r); + ts[j] = r.getResult(); + } + // cache result + upperBounds = ts; + // could throw away upper bound ASTs here; thread safety? + } + return upperBounds.clone(); // return cached bounds + } + + /** + * Returns an array of Type objects representing the + * lower bound(s) of this type variable. Note that if no lower bound is + * explicitly declared, the lower bound is the type of null. + * In this case, a zero length array is returned. + * + *

    For each lower bound B : + *

      + *
    • if B is a parameterized type or a type variable, it is created, + * (see {@link #ParameterizedType} for the details of the creation + * process for parameterized types). + *
    • Otherwise, B is resolved. + *
    + * + * @return an array of Types representing the lower bound(s) of this + * type variable + * @throws TypeNotPresentException if any of the + * bounds refers to a non-existent type declaration + * @throws MalformedParameterizedTypeException if any of the + * bounds refer to a parameterized type that cannot be instantiated + * for any reason + */ + public Type[] getLowerBounds() { + // lazily initialize bounds if necessary + if (lowerBounds == null) { + FieldTypeSignature[] fts = getLowerBoundASTs(); // get AST + // allocate result array; note that + // keeping ts and bounds separate helps with threads + Type[] ts = new Type[fts.length]; + // iterate over bound trees, reifying each in turn + for ( int j = 0; j < fts.length; j++) { + Reifier r = getReifier(); + fts[j].accept(r); + ts[j] = r.getResult(); + } + // cache result + lowerBounds = ts; + // could throw away lower bound ASTs here; thread safety? + } + return lowerBounds.clone(); // return cached bounds + } + + public String toString() { + Type[] lowerBounds = getLowerBounds(); + Type[] bounds = lowerBounds; + StringBuilder sb = new StringBuilder(); + + if (lowerBounds.length > 0) + sb.append("? super "); + else { + Type[] upperBounds = getUpperBounds(); + if (upperBounds.length > 0 && !upperBounds[0].equals(Object.class) ) { + bounds = upperBounds; + sb.append("? extends "); + } else + return "?"; + } + + assert bounds.length > 0; + + boolean first = true; + for(Type bound: bounds) { + if (!first) + sb.append(" & "); + + first = false; + sb.append(bound.getTypeName()); + } + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof WildcardType) { + WildcardType that = (WildcardType) o; + return + Arrays.equals(this.getLowerBounds(), + that.getLowerBounds()) && + Arrays.equals(this.getUpperBounds(), + that.getUpperBounds()); + } else + return false; + } + + @Override + public int hashCode() { + Type [] lowerBounds = getLowerBounds(); + Type [] upperBounds = getUpperBounds(); + + return Arrays.hashCode(lowerBounds) ^ Arrays.hashCode(upperBounds); + } +} diff --git a/src/sun/reflect/generics/repository/AbstractRepository.java b/src/sun/reflect/generics/repository/AbstractRepository.java new file mode 100644 index 00000000..42f9446c --- /dev/null +++ b/src/sun/reflect/generics/repository/AbstractRepository.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.tree.Tree; +import sun.reflect.generics.visitor.Reifier; + + +/** + * Abstract superclass for representing the generic type information for + * a reflective entity. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public abstract class AbstractRepository { + + // A factory used to produce reflective objects. Provided when the + //repository is created. Will vary across implementations. + private final GenericsFactory factory; + + private final T tree; // the AST for the generic type info + + //accessors + private GenericsFactory getFactory() { return factory;} + + /** + * Accessor for tree. + * @return the cached AST this repository holds + */ + protected T getTree(){ return tree;} + + /** + * Returns a Reifier used to convert parts of the + * AST into reflective objects. + * @return a Reifier used to convert parts of the + * AST into reflective objects + */ + protected Reifier getReifier(){return Reifier.make(getFactory());} + + /** + * Constructor. Should only be used by subclasses. Concrete subclasses + * should make their constructors private and provide public factory + * methods. + * @param rawSig - the generic signature of the reflective object + * that this repository is servicing + * @param f - a factory that will provide instances of reflective + * objects when this repository converts its AST + */ + protected AbstractRepository(String rawSig, GenericsFactory f) { + tree = parse(rawSig); + factory = f; + } + + /** + * Returns the AST for the genric type info of this entity. + * @param s - a string representing the generic signature of this + * entity + * @return the AST for the generic type info of this entity. + */ + protected abstract T parse(String s); +} diff --git a/src/sun/reflect/generics/repository/ClassRepository.java b/src/sun/reflect/generics/repository/ClassRepository.java new file mode 100644 index 00000000..1e2faa21 --- /dev/null +++ b/src/sun/reflect/generics/repository/ClassRepository.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + +import java.lang.reflect.Type; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.parser.SignatureParser; +import sun.reflect.generics.tree.ClassSignature; +import sun.reflect.generics.tree.TypeTree; +import sun.reflect.generics.visitor.Reifier; + + +/** + * This class represents the generic type information for a class. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public class ClassRepository extends GenericDeclRepository { + + public static final ClassRepository NONE = ClassRepository.make("Ljava/lang/Object;", null); + + private volatile Type superclass; // caches the generic superclass info + private volatile Type[] superInterfaces; // caches the generic superinterface info + + // private, to enforce use of static factory + private ClassRepository(String rawSig, GenericsFactory f) { + super(rawSig, f); + } + + protected ClassSignature parse(String s) { + return SignatureParser.make().parseClassSig(s); + } + + /** + * Static factory method. + * @param rawSig - the generic signature of the reflective object + * that this repository is servicing + * @param f - a factory that will provide instances of reflective + * objects when this repository converts its AST + * @return a ClassRepository that manages the generic type + * information represented in the signature rawSig + */ + public static ClassRepository make(String rawSig, GenericsFactory f) { + return new ClassRepository(rawSig, f); + } + + // public API + /* + * When queried for a particular piece of type information, the + * general pattern is to consult the corresponding cached value. + * If the corresponding field is non-null, it is returned. + * If not, it is created lazily. This is done by selecting the appropriate + * part of the tree and transforming it into a reflective object + * using a visitor. + * a visitor, which is created by feeding it the factory + * with which the repository was created. + */ + + public Type getSuperclass(){ + Type superclass = this.superclass; + if (superclass == null) { // lazily initialize superclass + Reifier r = getReifier(); // obtain visitor + // Extract superclass subtree from AST and reify + getTree().getSuperclass().accept(r); + // extract result from visitor and cache it + superclass = r.getResult(); + this.superclass = superclass; + } + return superclass; // return cached result + } + + public Type[] getSuperInterfaces(){ + Type[] superInterfaces = this.superInterfaces; + if (superInterfaces == null) { // lazily initialize super interfaces + // first, extract super interface subtree(s) from AST + TypeTree[] ts = getTree().getSuperInterfaces(); + // create array to store reified subtree(s) + Type[] sis = new Type[ts.length]; + // reify all subtrees + for (int i = 0; i < ts.length; i++) { + Reifier r = getReifier(); // obtain visitor + ts[i].accept(r);// reify subtree + // extract result from visitor and store it + sis[i] = r.getResult(); + } + superInterfaces = sis; // cache overall result + this.superInterfaces = superInterfaces; + } + return superInterfaces.clone(); // return cached result + } +} diff --git a/src/sun/reflect/generics/repository/ConstructorRepository.java b/src/sun/reflect/generics/repository/ConstructorRepository.java new file mode 100644 index 00000000..4a59f28b --- /dev/null +++ b/src/sun/reflect/generics/repository/ConstructorRepository.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + +import java.lang.reflect.Type; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.parser.SignatureParser; +import sun.reflect.generics.tree.FieldTypeSignature; +import sun.reflect.generics.tree.MethodTypeSignature; +import sun.reflect.generics.tree.TypeSignature; +import sun.reflect.generics.visitor.Reifier; + + +/** + * This class represents the generic type information for a constructor. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public class ConstructorRepository + extends GenericDeclRepository { + + private Type[] paramTypes; // caches the generic parameter types info + private Type[] exceptionTypes; // caches the generic exception types info + + // protected, to enforce use of static factory yet allow subclassing + protected ConstructorRepository(String rawSig, GenericsFactory f) { + super(rawSig, f); + } + + protected MethodTypeSignature parse(String s) { + return SignatureParser.make().parseMethodSig(s); + } + + /** + * Static factory method. + * @param rawSig - the generic signature of the reflective object + * that this repository is servicing + * @param f - a factory that will provide instances of reflective + * objects when this repository converts its AST + * @return a ConstructorRepository that manages the generic type + * information represented in the signature rawSig + */ + public static ConstructorRepository make(String rawSig, + GenericsFactory f) { + return new ConstructorRepository(rawSig, f); + } + + // public API + + /* + * When queried for a particular piece of type information, the + * general pattern is to consult the corresponding cached value. + * If the corresponding field is non-null, it is returned. + * If not, it is created lazily. This is done by selecting the appropriate + * part of the tree and transforming it into a reflective object + * using a visitor. + * a visitor, which is created by feeding it the factory + * with which the repository was created. + */ + + public Type[] getParameterTypes(){ + if (paramTypes == null) { // lazily initialize parameter types + // first, extract parameter type subtree(s) from AST + TypeSignature[] pts = getTree().getParameterTypes(); + // create array to store reified subtree(s) + Type[] ps = new Type[pts.length]; + // reify all subtrees + for (int i = 0; i < pts.length; i++) { + Reifier r = getReifier(); // obtain visitor + pts[i].accept(r); // reify subtree + // extract result from visitor and store it + ps[i] = r.getResult(); + } + paramTypes = ps; // cache overall result + } + return paramTypes.clone(); // return cached result + } + + public Type[] getExceptionTypes(){ + if (exceptionTypes == null) { // lazily initialize exception types + // first, extract exception type subtree(s) from AST + FieldTypeSignature[] ets = getTree().getExceptionTypes(); + // create array to store reified subtree(s) + Type[] es = new Type[ets.length]; + // reify all subtrees + for (int i = 0; i < ets.length; i++) { + Reifier r = getReifier(); // obtain visitor + ets[i].accept(r); // reify subtree + // extract result from visitor and store it + es[i] = r.getResult(); + } + exceptionTypes = es; // cache overall result + } + return exceptionTypes.clone(); // return cached result + } +} diff --git a/src/sun/reflect/generics/repository/FieldRepository.java b/src/sun/reflect/generics/repository/FieldRepository.java new file mode 100644 index 00000000..52f9ed51 --- /dev/null +++ b/src/sun/reflect/generics/repository/FieldRepository.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + + +import java.lang.reflect.Type; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.parser.SignatureParser; +import sun.reflect.generics.tree.TypeSignature; +import sun.reflect.generics.visitor.Reifier; + + +/** + * This class represents the generic type information for a constructor. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public class FieldRepository extends AbstractRepository { + + private Type genericType; // caches the generic type info + + // protected, to enforce use of static factory yet allow subclassing + protected FieldRepository(String rawSig, GenericsFactory f) { + super(rawSig, f); + } + + protected TypeSignature parse(String s) { + return SignatureParser.make().parseTypeSig(s); + } + + /** + * Static factory method. + * @param rawSig - the generic signature of the reflective object + * that this repository is servicing + * @param f - a factory that will provide instances of reflective + * objects when this repository converts its AST + * @return a FieldRepository that manages the generic type + * information represented in the signature rawSig + */ + public static FieldRepository make(String rawSig, + GenericsFactory f) { + return new FieldRepository(rawSig, f); + } + + // public API + + /* + * When queried for a particular piece of type information, the + * general pattern is to consult the corresponding cached value. + * If the corresponding field is non-null, it is returned. + * If not, it is created lazily. This is done by selecting the appropriate + * part of the tree and transforming it into a reflective object + * using a visitor. + * a visitor, which is created by feeding it the factory + * with which the repository was created. + */ + + public Type getGenericType(){ + if (genericType == null) { // lazily initialize generic type + Reifier r = getReifier(); // obtain visitor + getTree().accept(r); // reify subtree + // extract result from visitor and cache it + genericType = r.getResult(); + } + return genericType; // return cached result + } +} diff --git a/src/sun/reflect/generics/repository/GenericDeclRepository.java b/src/sun/reflect/generics/repository/GenericDeclRepository.java new file mode 100644 index 00000000..58837873 --- /dev/null +++ b/src/sun/reflect/generics/repository/GenericDeclRepository.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + +import java.lang.reflect.TypeVariable; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.tree.FormalTypeParameter; +import sun.reflect.generics.tree.Signature; +import sun.reflect.generics.visitor.Reifier; + + +/** + * This class represents the generic type information for a generic + * declaration. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public abstract class GenericDeclRepository + extends AbstractRepository { + + private volatile TypeVariable[] typeParams; // caches the formal type parameters + + protected GenericDeclRepository(String rawSig, GenericsFactory f) { + super(rawSig, f); + } + + // public API + /* + * When queried for a particular piece of type information, the + * general pattern is to consult the corresponding cached value. + * If the corresponding field is non-null, it is returned. + * If not, it is created lazily. This is done by selecting the appropriate + * part of the tree and transforming it into a reflective object + * using a visitor. + * a visitor, which is created by feeding it the factory + * with which the repository was created. + */ + + /** + * Return the formal type parameters of this generic declaration. + * @return the formal type parameters of this generic declaration + */ + public TypeVariable[] getTypeParameters(){ + TypeVariable[] typeParams = this.typeParams; + if (typeParams == null) { // lazily initialize type parameters + // first, extract type parameter subtree(s) from AST + FormalTypeParameter[] ftps = getTree().getFormalTypeParameters(); + // create array to store reified subtree(s) + TypeVariable[] tps = new TypeVariable[ftps.length]; + // reify all subtrees + for (int i = 0; i < ftps.length; i++) { + Reifier r = getReifier(); // obtain visitor + ftps[i].accept(r); // reify subtree + // extract result from visitor and store it + tps[i] = (TypeVariable) r.getResult(); + } + typeParams = tps; // cache overall result + this.typeParams = typeParams; + } + return typeParams.clone(); // return cached result + } +} diff --git a/src/sun/reflect/generics/repository/MethodRepository.java b/src/sun/reflect/generics/repository/MethodRepository.java new file mode 100644 index 00000000..f12b9754 --- /dev/null +++ b/src/sun/reflect/generics/repository/MethodRepository.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.repository; + + +import java.lang.reflect.Type; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.visitor.Reifier; + + +/** + * This class represents the generic type information for a method. + * The code is not dependent on a particular reflective implementation. + * It is designed to be used unchanged by at least core reflection and JDI. + */ +public class MethodRepository extends ConstructorRepository { + + private Type returnType; // caches the generic return type info + + // private, to enforce use of static factory + private MethodRepository(String rawSig, GenericsFactory f) { + super(rawSig, f); + } + + /** + * Static factory method. + * @param rawSig - the generic signature of the reflective object + * that this repository is servicing + * @param f - a factory that will provide instances of reflective + * objects when this repository converts its AST + * @return a MethodRepository that manages the generic type + * information represented in the signature rawSig + */ + public static MethodRepository make(String rawSig, GenericsFactory f) { + return new MethodRepository(rawSig, f); + } + + // public API + + public Type getReturnType() { + if (returnType == null) { // lazily initialize return type + Reifier r = getReifier(); // obtain visitor + // Extract return type subtree from AST and reify + getTree().getReturnType().accept(r); + // extract result from visitor and cache it + returnType = r.getResult(); + } + return returnType; // return cached result + } + + +} diff --git a/src/sun/reflect/generics/scope/AbstractScope.java b/src/sun/reflect/generics/scope/AbstractScope.java new file mode 100644 index 00000000..143790a9 --- /dev/null +++ b/src/sun/reflect/generics/scope/AbstractScope.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.TypeVariable; + + +/** + * Abstract superclass for lazy scope objects, used when building + * factories for generic information repositories. + * The type parameter D represents the type of reflective + * object whose scope this class is representing. + *

    To subclass this, all one needs to do is implement + * computeEnclosingScope and the subclass' constructor. + */ +public abstract class AbstractScope + implements Scope { + + private final D recvr; // the declaration whose scope this instance represents + private volatile Scope enclosingScope; // the enclosing scope of this scope + + /** + * Constructor. Takes a reflective object whose scope the newly + * constructed instance will represent. + * @param D - A generic declaration whose scope the newly + * constructed instance will represent + */ + protected AbstractScope(D decl){ recvr = decl;} + + /** + * Accessor for the receiver - the object whose scope this Scope + * object represents. + * @return The object whose scope this Scope object represents + */ + protected D getRecvr() {return recvr;} + + /** This method must be implemented by any concrete subclass. + * It must return the enclosing scope of this scope. If this scope + * is a top-level scope, an instance of DummyScope must be returned. + * @return The enclosing scope of this scope + */ + protected abstract Scope computeEnclosingScope(); + + /** + * Accessor for the enclosing scope, which is computed lazily and cached. + * @return the enclosing scope + */ + protected Scope getEnclosingScope(){ + Scope enclosingScope = this.enclosingScope; + if (enclosingScope == null) { + enclosingScope = computeEnclosingScope(); + this.enclosingScope = enclosingScope; + } + return enclosingScope; + } + + /** + * Lookup a type variable in the scope, using its name. Returns null if + * no type variable with this name is declared in this scope or any of its + * surrounding scopes. + * @param name - the name of the type variable being looked up + * @return the requested type variable, if found + */ + public TypeVariable lookup(String name) { + TypeVariable[] tas = getRecvr().getTypeParameters(); + for (TypeVariable tv : tas) { + if (tv.getName().equals(name)) {return tv;} + } + return getEnclosingScope().lookup(name); + } +} diff --git a/src/sun/reflect/generics/scope/ClassScope.java b/src/sun/reflect/generics/scope/ClassScope.java new file mode 100644 index 00000000..ce1853f0 --- /dev/null +++ b/src/sun/reflect/generics/scope/ClassScope.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + + +/** + * This class represents the scope containing the type variables of + * a class. + */ +public class ClassScope extends AbstractScope> implements Scope { + + // constructor is private to enforce use of factory method + private ClassScope(Class c){ + super(c); + } + + /** + * Overrides the abstract method in the superclass. + * @return the enclosing scope + */ + protected Scope computeEnclosingScope() { + Class receiver = getRecvr(); + + Method m = receiver.getEnclosingMethod(); + if (m != null) + // Receiver is a local or anonymous class enclosed in a + // method. + return MethodScope.make(m); + + Constructor cnstr = receiver.getEnclosingConstructor(); + if (cnstr != null) + // Receiver is a local or anonymous class enclosed in a + // constructor. + return ConstructorScope.make(cnstr); + + Class c = receiver.getEnclosingClass(); + // if there is a declaring class, recvr is a member class + // and its enclosing scope is that of the declaring class + if (c != null) + // Receiver is a local class, an anonymous class, or a + // member class (static or not). + return ClassScope.make(c); + + // otherwise, recvr is a top level class, and it has no real + // enclosing scope. + return DummyScope.make(); + } + + /** + * Factory method. Takes a Class object and creates a + * scope for it. + * @param c - a Class whose scope we want to obtain + * @return The type-variable scope for the class c + */ + public static ClassScope make(Class c) { return new ClassScope(c);} + +} diff --git a/src/sun/reflect/generics/scope/ConstructorScope.java b/src/sun/reflect/generics/scope/ConstructorScope.java new file mode 100644 index 00000000..43acdb6f --- /dev/null +++ b/src/sun/reflect/generics/scope/ConstructorScope.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.Constructor; + + +/** + * This class represents the scope containing the type variables of + * a constructor. + */ +public class ConstructorScope extends AbstractScope> { + + // constructor is private to enforce use of factory method + private ConstructorScope(Constructor c){ + super(c); + } + + // utility method; computes enclosing class, from which we can + // derive enclosing scope. + private Class getEnclosingClass(){ + return getRecvr().getDeclaringClass(); + } + + /** + * Overrides the abstract method in the superclass. + * @return the enclosing scope + */ + protected Scope computeEnclosingScope() { + // the enclosing scope of a (generic) constructor is the scope of the + // class in which it was declared. + return ClassScope.make(getEnclosingClass()); + } + + /** + * Factory method. Takes a Constructor object and creates a + * scope for it. + * @param m - A Constructor whose scope we want to obtain + * @return The type-variable scope for the constructor m + */ + public static ConstructorScope make(Constructor c) { + return new ConstructorScope(c); + } +} diff --git a/src/sun/reflect/generics/scope/DummyScope.java b/src/sun/reflect/generics/scope/DummyScope.java new file mode 100644 index 00000000..8a012dc3 --- /dev/null +++ b/src/sun/reflect/generics/scope/DummyScope.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.TypeVariable; + +/** + * This class is used to provide enclosing scopes for top level classes. + * We cannot use null to represent such a scope, since the + * enclosing scope is computed lazily, and so the field storing it is + * null until it has been computed. Therefore, null is reserved + * to represent an as-yet-uncomputed scope, and cannot be used for any + * other kind of scope. + */ +public class DummyScope implements Scope { + // Caches the unique instance of this class; instances contain no data + // so we can use the singleton pattern + private static final DummyScope singleton = new DummyScope(); + + // constructor is private to enforce use of factory method + private DummyScope(){} + + /** + * Factory method. Enforces the singleton pattern - only one + * instance of this class ever exists. + */ + public static DummyScope make() { + return singleton; + } + + /** + * Lookup a type variable in the scope, using its name. Always returns + * null. + * @param name - the name of the type variable being looked up + * @return null + */ + public TypeVariable lookup(String name) {return null;} +} diff --git a/src/sun/reflect/generics/scope/MethodScope.java b/src/sun/reflect/generics/scope/MethodScope.java new file mode 100644 index 00000000..83a1ab8c --- /dev/null +++ b/src/sun/reflect/generics/scope/MethodScope.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.Method; + + +/** + * This class represents the scope containing the type variables of + * a method. + */ +public class MethodScope extends AbstractScope { + + // constructor is private to enforce use of factory method + private MethodScope(Method m){ + super(m); + } + + // utility method; computes enclosing class, from which we can + // derive enclosing scope. + private Class getEnclosingClass(){ + return getRecvr().getDeclaringClass(); + } + + /** + * Overrides the abstract method in the superclass. + * @return the enclosing scope + */ + protected Scope computeEnclosingScope() { + // the enclosing scope of a (generic) method is the scope of the + // class in which it was declared. + return ClassScope.make(getEnclosingClass()); + } + + /** + * Factory method. Takes a Method object and creates a + * scope for it. + * @param m - A Method whose scope we want to obtain + * @return The type-variable scope for the method m + */ + public static MethodScope make(Method m) { + return new MethodScope(m); + } +} diff --git a/src/sun/reflect/generics/scope/Scope.java b/src/sun/reflect/generics/scope/Scope.java new file mode 100644 index 00000000..19e1ea0a --- /dev/null +++ b/src/sun/reflect/generics/scope/Scope.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.scope; + +import java.lang.reflect.TypeVariable; + + +public interface Scope { + TypeVariable lookup(String name); +} diff --git a/src/sun/reflect/generics/tree/ArrayTypeSignature.java b/src/sun/reflect/generics/tree/ArrayTypeSignature.java new file mode 100644 index 00000000..ba0a0d41 --- /dev/null +++ b/src/sun/reflect/generics/tree/ArrayTypeSignature.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +public class ArrayTypeSignature implements FieldTypeSignature { + private final TypeSignature componentType; + + private ArrayTypeSignature(TypeSignature ct) {componentType = ct;} + + public static ArrayTypeSignature make(TypeSignature ct) { + return new ArrayTypeSignature(ct); + } + + public TypeSignature getComponentType(){return componentType;} + + public void accept(TypeTreeVisitor v){ + v.visitArrayTypeSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/BaseType.java b/src/sun/reflect/generics/tree/BaseType.java new file mode 100644 index 00000000..19c47eb4 --- /dev/null +++ b/src/sun/reflect/generics/tree/BaseType.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** + * Common superinterface for all nodes representing a primitive type. + * Corresponds to the production of the same name in the JVMS + * section on signatures. + */ +public interface BaseType + extends TypeSignature{} diff --git a/src/sun/reflect/generics/tree/BooleanSignature.java b/src/sun/reflect/generics/tree/BooleanSignature.java new file mode 100644 index 00000000..133c7e19 --- /dev/null +++ b/src/sun/reflect/generics/tree/BooleanSignature.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type boolean. */ +public class BooleanSignature implements BaseType { + private static final BooleanSignature singleton = new BooleanSignature(); + + private BooleanSignature(){} + + public static BooleanSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){ + v.visitBooleanSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/BottomSignature.java b/src/sun/reflect/generics/tree/BottomSignature.java new file mode 100644 index 00000000..0fbbe2ea --- /dev/null +++ b/src/sun/reflect/generics/tree/BottomSignature.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +public class BottomSignature implements FieldTypeSignature { + private static final BottomSignature singleton = new BottomSignature(); + + private BottomSignature(){} + + public static BottomSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){v.visitBottomSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/ByteSignature.java b/src/sun/reflect/generics/tree/ByteSignature.java new file mode 100644 index 00000000..5335782f --- /dev/null +++ b/src/sun/reflect/generics/tree/ByteSignature.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type byte. */ +public class ByteSignature implements BaseType { + private static final ByteSignature singleton = new ByteSignature(); + + private ByteSignature(){} + + public static ByteSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){ + v.visitByteSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/CharSignature.java b/src/sun/reflect/generics/tree/CharSignature.java new file mode 100644 index 00000000..44766a57 --- /dev/null +++ b/src/sun/reflect/generics/tree/CharSignature.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type char. */ +public class CharSignature implements BaseType { + private static final CharSignature singleton = new CharSignature(); + + private CharSignature(){} + + public static CharSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){ + v.visitCharSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/ClassSignature.java b/src/sun/reflect/generics/tree/ClassSignature.java new file mode 100644 index 00000000..9347f977 --- /dev/null +++ b/src/sun/reflect/generics/tree/ClassSignature.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.Visitor; + +public class ClassSignature implements Signature { + private final FormalTypeParameter[] formalTypeParams; + private final ClassTypeSignature superclass; + private final ClassTypeSignature[] superInterfaces; + + private ClassSignature(FormalTypeParameter[] ftps, + ClassTypeSignature sc, + ClassTypeSignature[] sis) { + formalTypeParams = ftps; + superclass = sc; + superInterfaces = sis; + } + + public static ClassSignature make(FormalTypeParameter[] ftps, + ClassTypeSignature sc, + ClassTypeSignature[] sis) { + return new ClassSignature(ftps, sc, sis); + } + + public FormalTypeParameter[] getFormalTypeParameters(){ + return formalTypeParams; + } + public ClassTypeSignature getSuperclass(){return superclass;} + public ClassTypeSignature[] getSuperInterfaces(){return superInterfaces;} + + public void accept(Visitor v){v.visitClassSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/ClassTypeSignature.java b/src/sun/reflect/generics/tree/ClassTypeSignature.java new file mode 100644 index 00000000..97ce5005 --- /dev/null +++ b/src/sun/reflect/generics/tree/ClassTypeSignature.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import java.util.List; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + + +/** + * AST representing class types. + */ +public class ClassTypeSignature implements FieldTypeSignature { + private final List path; + + + private ClassTypeSignature(List p) { + path = p; + } + + public static ClassTypeSignature make(List p) { + return new ClassTypeSignature(p); + } + + public List getPath(){return path;} + + public void accept(TypeTreeVisitor v){v.visitClassTypeSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/DoubleSignature.java b/src/sun/reflect/generics/tree/DoubleSignature.java new file mode 100644 index 00000000..a394228e --- /dev/null +++ b/src/sun/reflect/generics/tree/DoubleSignature.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type double. */ +public class DoubleSignature implements BaseType { + private static final DoubleSignature singleton = new DoubleSignature(); + + private DoubleSignature(){} + + public static DoubleSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){v.visitDoubleSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/FieldTypeSignature.java b/src/sun/reflect/generics/tree/FieldTypeSignature.java new file mode 100644 index 00000000..5eaefcd7 --- /dev/null +++ b/src/sun/reflect/generics/tree/FieldTypeSignature.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** + * Common superinterface for nodes that represent a (possibly generic) + * type. + * Corresponds to the production of the same name in the JVMS + * section on signatures. + */ +public interface FieldTypeSignature + extends BaseType, TypeSignature, TypeArgument {} diff --git a/src/sun/reflect/generics/tree/FloatSignature.java b/src/sun/reflect/generics/tree/FloatSignature.java new file mode 100644 index 00000000..70bf8c5c --- /dev/null +++ b/src/sun/reflect/generics/tree/FloatSignature.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type float. */ +public class FloatSignature implements BaseType { + private static final FloatSignature singleton = new FloatSignature(); + + private FloatSignature(){} + + public static FloatSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){v.visitFloatSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/FormalTypeParameter.java b/src/sun/reflect/generics/tree/FormalTypeParameter.java new file mode 100644 index 00000000..c25c4179 --- /dev/null +++ b/src/sun/reflect/generics/tree/FormalTypeParameter.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents a formal type parameter. */ +public class FormalTypeParameter implements TypeTree { + private final String name; + private final FieldTypeSignature[] bounds; + + private FormalTypeParameter(String n, FieldTypeSignature[] bs) { + name = n; + bounds = bs; + } + + /** + * Factory method. + * Returns a formal type parameter with the requested name and bounds. + * @param n the name of the type variable to be created by this method. + * @param bs - the bounds of the type variable to be created by this method. + * @return a formal type parameter with the requested name and bounds + */ + public static FormalTypeParameter make(String n, FieldTypeSignature[] bs){ + return new FormalTypeParameter(n,bs); + } + + public FieldTypeSignature[] getBounds(){return bounds;} + public String getName(){return name;} + + public void accept(TypeTreeVisitor v){v.visitFormalTypeParameter(this);} +} diff --git a/src/sun/reflect/generics/tree/IntSignature.java b/src/sun/reflect/generics/tree/IntSignature.java new file mode 100644 index 00000000..e1d3a443 --- /dev/null +++ b/src/sun/reflect/generics/tree/IntSignature.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type int. */ +public class IntSignature implements BaseType { + private static final IntSignature singleton = new IntSignature(); + + private IntSignature(){} + + public static IntSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){v.visitIntSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/LongSignature.java b/src/sun/reflect/generics/tree/LongSignature.java new file mode 100644 index 00000000..13eef059 --- /dev/null +++ b/src/sun/reflect/generics/tree/LongSignature.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type long. */ +public class LongSignature implements BaseType { + private static final LongSignature singleton = new LongSignature(); + + private LongSignature(){} + + public static LongSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){v.visitLongSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/MethodTypeSignature.java b/src/sun/reflect/generics/tree/MethodTypeSignature.java new file mode 100644 index 00000000..efbda03b --- /dev/null +++ b/src/sun/reflect/generics/tree/MethodTypeSignature.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.Visitor; + +public class MethodTypeSignature implements Signature { + private final FormalTypeParameter[] formalTypeParams; + private final TypeSignature[] parameterTypes; + private final ReturnType returnType; + private final FieldTypeSignature[] exceptionTypes; + + private MethodTypeSignature(FormalTypeParameter[] ftps, + TypeSignature[] pts, + ReturnType rt, + FieldTypeSignature[] ets) { + formalTypeParams = ftps; + parameterTypes = pts; + returnType = rt; + exceptionTypes = ets; + } + + public static MethodTypeSignature make(FormalTypeParameter[] ftps, + TypeSignature[] pts, + ReturnType rt, + FieldTypeSignature[] ets) { + return new MethodTypeSignature(ftps, pts, rt, ets); + } + + public FormalTypeParameter[] getFormalTypeParameters(){ + return formalTypeParams; + } + public TypeSignature[] getParameterTypes(){return parameterTypes;} + public ReturnType getReturnType(){return returnType;} + public FieldTypeSignature[] getExceptionTypes(){return exceptionTypes;} + + public void accept(Visitor v){v.visitMethodTypeSignature(this);} +} diff --git a/src/sun/reflect/generics/tree/ReturnType.java b/src/sun/reflect/generics/tree/ReturnType.java new file mode 100644 index 00000000..7b1054c4 --- /dev/null +++ b/src/sun/reflect/generics/tree/ReturnType.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +public interface ReturnType extends TypeTree{} diff --git a/src/sun/reflect/generics/tree/ShortSignature.java b/src/sun/reflect/generics/tree/ShortSignature.java new file mode 100644 index 00000000..374f6fba --- /dev/null +++ b/src/sun/reflect/generics/tree/ShortSignature.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** AST that represents the type short. */ +public class ShortSignature implements BaseType { + private static final ShortSignature singleton = new ShortSignature(); + + private ShortSignature(){} + + public static ShortSignature make() {return singleton;} + + public void accept(TypeTreeVisitor v){ + v.visitShortSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/Signature.java b/src/sun/reflect/generics/tree/Signature.java new file mode 100644 index 00000000..f4a54f82 --- /dev/null +++ b/src/sun/reflect/generics/tree/Signature.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** + * Common superinterface for generic signatures. These are the signatures + * of complete class and method/constructor delcarations. + */ +public interface Signature extends Tree{ + FormalTypeParameter[] getFormalTypeParameters(); +} diff --git a/src/sun/reflect/generics/tree/SimpleClassTypeSignature.java b/src/sun/reflect/generics/tree/SimpleClassTypeSignature.java new file mode 100644 index 00000000..8c287018 --- /dev/null +++ b/src/sun/reflect/generics/tree/SimpleClassTypeSignature.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +public class SimpleClassTypeSignature implements FieldTypeSignature { + private final boolean dollar; + private final String name; + private final TypeArgument[] typeArgs; + + private SimpleClassTypeSignature(String n, boolean dollar, TypeArgument[] tas) { + name = n; + this.dollar = dollar; + typeArgs = tas; + } + + public static SimpleClassTypeSignature make(String n, + boolean dollar, + TypeArgument[] tas){ + return new SimpleClassTypeSignature(n, dollar, tas); + } + + /* + * Should a '$' be used instead of '.' to separate this component + * of the name from the previous one when composing a string to + * pass to Class.forName; in other words, is this a transition to + * a nested class. + */ + public boolean getDollar(){return dollar;} + public String getName(){return name;} + public TypeArgument[] getTypeArguments(){return typeArgs;} + + public void accept(TypeTreeVisitor v){ + v.visitSimpleClassTypeSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/Tree.java b/src/sun/reflect/generics/tree/Tree.java new file mode 100644 index 00000000..a3d1deb4 --- /dev/null +++ b/src/sun/reflect/generics/tree/Tree.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** Root of the abstract syntax tree hierarchy for generic signatures */ +public interface Tree{} diff --git a/src/sun/reflect/generics/tree/TypeArgument.java b/src/sun/reflect/generics/tree/TypeArgument.java new file mode 100644 index 00000000..832e57d0 --- /dev/null +++ b/src/sun/reflect/generics/tree/TypeArgument.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** Common supertype for all possible type arguments in the + * generic signature AST. Corresponds to the production TypeArgument + * in the JVMS. + */ +public interface TypeArgument extends TypeTree {} diff --git a/src/sun/reflect/generics/tree/TypeSignature.java b/src/sun/reflect/generics/tree/TypeSignature.java new file mode 100644 index 00000000..63663499 --- /dev/null +++ b/src/sun/reflect/generics/tree/TypeSignature.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +/** + * Common superinterface for all signatures that represent a + * type expression. + * Corresponds to the production of the same name in the JVMS + * section on signatures. + */ +public interface TypeSignature extends ReturnType {} diff --git a/src/sun/reflect/generics/tree/TypeTree.java b/src/sun/reflect/generics/tree/TypeTree.java new file mode 100644 index 00000000..aaceb8d2 --- /dev/null +++ b/src/sun/reflect/generics/tree/TypeTree.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +/** Common supertype for all nodes that represent type expressions in + * the generic signature AST. + */ +public interface TypeTree extends Tree { + /** + * Accept method for the visitor pattern. + * @param v - a TypeTreeVisitor that will process this + * tree + */ + void accept(TypeTreeVisitor v); +} diff --git a/src/sun/reflect/generics/tree/TypeVariableSignature.java b/src/sun/reflect/generics/tree/TypeVariableSignature.java new file mode 100644 index 00000000..9f119658 --- /dev/null +++ b/src/sun/reflect/generics/tree/TypeVariableSignature.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +public class TypeVariableSignature implements FieldTypeSignature { + private final String identifier; + + private TypeVariableSignature(String id) {identifier = id;} + + + public static TypeVariableSignature make(String id) { + return new TypeVariableSignature(id); + } + + public String getIdentifier(){return identifier;} + + public void accept(TypeTreeVisitor v){ + v.visitTypeVariableSignature(this); + } +} diff --git a/src/sun/reflect/generics/tree/VoidDescriptor.java b/src/sun/reflect/generics/tree/VoidDescriptor.java new file mode 100644 index 00000000..5544ebc6 --- /dev/null +++ b/src/sun/reflect/generics/tree/VoidDescriptor.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + + +/** AST that represents the pseudo-type void. */ +public class VoidDescriptor implements ReturnType { + private static final VoidDescriptor singleton = new VoidDescriptor(); + + private VoidDescriptor(){} + + public static VoidDescriptor make() {return singleton;} + + + + public void accept(TypeTreeVisitor v){v.visitVoidDescriptor(this);} +} diff --git a/src/sun/reflect/generics/tree/Wildcard.java b/src/sun/reflect/generics/tree/Wildcard.java new file mode 100644 index 00000000..5e3b6b84 --- /dev/null +++ b/src/sun/reflect/generics/tree/Wildcard.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.tree; + +import sun.reflect.generics.visitor.TypeTreeVisitor; + +public class Wildcard implements TypeArgument { + private FieldTypeSignature[] upperBounds; + private FieldTypeSignature[] lowerBounds; + + private Wildcard(FieldTypeSignature[] ubs, FieldTypeSignature[] lbs) { + upperBounds = ubs; + lowerBounds = lbs; + } + + private static final FieldTypeSignature[] emptyBounds = new FieldTypeSignature[0]; + + public static Wildcard make(FieldTypeSignature[] ubs, + FieldTypeSignature[] lbs) { + return new Wildcard(ubs, lbs); + } + + public FieldTypeSignature[] getUpperBounds(){ + return upperBounds; + } + + public FieldTypeSignature[] getLowerBounds(){ + if (lowerBounds.length == 1 && + lowerBounds[0] == BottomSignature.make()) + return emptyBounds; + else + return lowerBounds; + } + + public void accept(TypeTreeVisitor v){v.visitWildcard(this);} +} diff --git a/src/sun/reflect/generics/visitor/Reifier.java b/src/sun/reflect/generics/visitor/Reifier.java new file mode 100644 index 00000000..ebb915f3 --- /dev/null +++ b/src/sun/reflect/generics/visitor/Reifier.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.visitor; + + +import java.lang.reflect.Type; +import java.util.Iterator; +import java.util.List; + +import sun.reflect.generics.factory.GenericsFactory; +import sun.reflect.generics.tree.ArrayTypeSignature; +import sun.reflect.generics.tree.BooleanSignature; +import sun.reflect.generics.tree.BottomSignature; +import sun.reflect.generics.tree.ByteSignature; +import sun.reflect.generics.tree.CharSignature; +import sun.reflect.generics.tree.ClassTypeSignature; +import sun.reflect.generics.tree.DoubleSignature; +import sun.reflect.generics.tree.FloatSignature; +import sun.reflect.generics.tree.FormalTypeParameter; +import sun.reflect.generics.tree.IntSignature; +import sun.reflect.generics.tree.LongSignature; +import sun.reflect.generics.tree.ShortSignature; +import sun.reflect.generics.tree.SimpleClassTypeSignature; +import sun.reflect.generics.tree.TypeArgument; +import sun.reflect.generics.tree.TypeVariableSignature; +import sun.reflect.generics.tree.VoidDescriptor; +import sun.reflect.generics.tree.Wildcard; + + +/** + * Visitor that converts AST to reified types. + */ +public class Reifier implements TypeTreeVisitor { + private Type resultType; + private GenericsFactory factory; + + private Reifier(GenericsFactory f){ + factory = f; + } + + private GenericsFactory getFactory(){ return factory;} + + /** + * Factory method. The resulting visitor will convert an AST + * representing generic signatures into corresponding reflective + * objects, using the provided factory, f. + * @param f - a factory that can be used to manufacture reflective + * objects returned by this visitor + * @return A visitor that can be used to reify ASTs representing + * generic type information into reflective objects + */ + public static Reifier make(GenericsFactory f){ + return new Reifier(f); + } + + // Helper method. Visits an array of TypeArgument and produces + // reified Type array. + private Type[] reifyTypeArguments(TypeArgument[] tas) { + Type[] ts = new Type[tas.length]; + for (int i = 0; i < tas.length; i++) { + tas[i].accept(this); + ts[i] = resultType; + } + return ts; + } + + + /** + * Accessor for the result of the last visit by this visitor, + * @return The type computed by this visitor based on its last + * visit + */ + public Type getResult() { assert resultType != null;return resultType;} + + public void visitFormalTypeParameter(FormalTypeParameter ftp){ + resultType = getFactory().makeTypeVariable(ftp.getName(), + ftp.getBounds()); + } + + + public void visitClassTypeSignature(ClassTypeSignature ct){ + // This method examines the pathname stored in ct, which has the form + // n1.n2...nk.... + // where n1 ... nk-1 might not exist OR + // nk might not exist (but not both). It may be that k equals 1. + // The idea is that nk is the simple class type name that has + // any type parameters associated with it. + // We process this path in two phases. + // First, we scan until we reach nk (if it exists). + // If nk does not exist, this identifies a raw class n1 ... nk-1 + // which we can return. + // if nk does exist, we begin the 2nd phase. + // Here nk defines a parameterized type. Every further step nj (j > k) + // down the path must also be represented as a parameterized type, + // whose owner is the representation of the previous step in the path, + // n{j-1}. + + // extract iterator on list of simple class type sigs + List scts = ct.getPath(); + assert(!scts.isEmpty()); + Iterator iter = scts.iterator(); + SimpleClassTypeSignature sc = iter.next(); + StringBuilder n = new StringBuilder(sc.getName()); + boolean dollar = sc.getDollar(); + + // phase 1: iterate over simple class types until + // we are either done or we hit one with non-empty type parameters + while (iter.hasNext() && sc.getTypeArguments().length == 0) { + sc = iter.next(); + dollar = sc.getDollar(); + n.append(dollar?"$":".").append(sc.getName()); + } + + // Now, either sc is the last element of the list, or + // it has type arguments (or both) + assert(!(iter.hasNext()) || (sc.getTypeArguments().length > 0)); + // Create the raw type + Type c = getFactory().makeNamedType(n.toString()); + // if there are no type arguments + if (sc.getTypeArguments().length == 0) { + //we have surely reached the end of the path + assert(!iter.hasNext()); + resultType = c; // the result is the raw type + } else { + assert(sc.getTypeArguments().length > 0); + // otherwise, we have type arguments, so we create a parameterized + // type, whose declaration is the raw type c, and whose owner is + // the declaring class of c (if any). This latter fact is indicated + // by passing null as the owner. + // First, we reify the type arguments + Type[] pts = reifyTypeArguments(sc.getTypeArguments()); + + Type owner = getFactory().makeParameterizedType(c, pts, null); + // phase 2: iterate over remaining simple class types + dollar =false; + while (iter.hasNext()) { + sc = iter.next(); + dollar = sc.getDollar(); + n.append(dollar?"$":".").append(sc.getName()); // build up raw class name + c = getFactory().makeNamedType(n.toString()); // obtain raw class + pts = reifyTypeArguments(sc.getTypeArguments());// reify params + // Create a parameterized type, based on type args, raw type + // and previous owner + owner = getFactory().makeParameterizedType(c, pts, owner); + } + resultType = owner; + } + } + + public void visitArrayTypeSignature(ArrayTypeSignature a){ + // extract and reify component type + a.getComponentType().accept(this); + Type ct = resultType; + resultType = getFactory().makeArrayType(ct); + } + + public void visitTypeVariableSignature(TypeVariableSignature tv){ + resultType = getFactory().findTypeVariable(tv.getIdentifier()); + } + + public void visitWildcard(Wildcard w){ + resultType = getFactory().makeWildcard(w.getUpperBounds(), + w.getLowerBounds()); + } + + public void visitSimpleClassTypeSignature(SimpleClassTypeSignature sct){ + resultType = getFactory().makeNamedType(sct.getName()); + } + + public void visitBottomSignature(BottomSignature b){ + + } + + public void visitByteSignature(ByteSignature b){ + resultType = getFactory().makeByte(); + } + + public void visitBooleanSignature(BooleanSignature b){ + resultType = getFactory().makeBool(); + } + + public void visitShortSignature(ShortSignature s){ + resultType = getFactory().makeShort(); + } + + public void visitCharSignature(CharSignature c){ + resultType = getFactory().makeChar(); + } + + public void visitIntSignature(IntSignature i){ + resultType = getFactory().makeInt(); + } + + public void visitLongSignature(LongSignature l){ + resultType = getFactory().makeLong(); + } + + public void visitFloatSignature(FloatSignature f){ + resultType = getFactory().makeFloat(); + } + + public void visitDoubleSignature(DoubleSignature d){ + resultType = getFactory().makeDouble(); + } + + public void visitVoidDescriptor(VoidDescriptor v){ + resultType = getFactory().makeVoid(); + } + + +} diff --git a/src/sun/reflect/generics/visitor/TypeTreeVisitor.java b/src/sun/reflect/generics/visitor/TypeTreeVisitor.java new file mode 100644 index 00000000..5abe8776 --- /dev/null +++ b/src/sun/reflect/generics/visitor/TypeTreeVisitor.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.visitor; + +import sun.reflect.generics.tree.ArrayTypeSignature; +import sun.reflect.generics.tree.BooleanSignature; +import sun.reflect.generics.tree.BottomSignature; +import sun.reflect.generics.tree.ByteSignature; +import sun.reflect.generics.tree.CharSignature; +import sun.reflect.generics.tree.ClassTypeSignature; +import sun.reflect.generics.tree.DoubleSignature; +import sun.reflect.generics.tree.FloatSignature; +import sun.reflect.generics.tree.FormalTypeParameter; +import sun.reflect.generics.tree.IntSignature; +import sun.reflect.generics.tree.LongSignature; +import sun.reflect.generics.tree.ShortSignature; +import sun.reflect.generics.tree.SimpleClassTypeSignature; +import sun.reflect.generics.tree.TypeVariableSignature; +import sun.reflect.generics.tree.VoidDescriptor; +import sun.reflect.generics.tree.Wildcard; + +/** + * Visit a TypeTree and produce a result of type T. + */ +public interface TypeTreeVisitor { + + /** + * Returns the result of the visit. + * @return the result of the visit + */ + T getResult(); + + // Visitor methods, per node type + + void visitFormalTypeParameter(FormalTypeParameter ftp); + + void visitClassTypeSignature(ClassTypeSignature ct); + void visitArrayTypeSignature(ArrayTypeSignature a); + void visitTypeVariableSignature(TypeVariableSignature tv); + void visitWildcard(Wildcard w); + + void visitSimpleClassTypeSignature(SimpleClassTypeSignature sct); + void visitBottomSignature(BottomSignature b); + + // Primitives and Void + void visitByteSignature(ByteSignature b); + void visitBooleanSignature(BooleanSignature b); + void visitShortSignature(ShortSignature s); + void visitCharSignature(CharSignature c); + void visitIntSignature(IntSignature i); + void visitLongSignature(LongSignature l); + void visitFloatSignature(FloatSignature f); + void visitDoubleSignature(DoubleSignature d); + + void visitVoidDescriptor(VoidDescriptor v); +} diff --git a/src/sun/reflect/generics/visitor/Visitor.java b/src/sun/reflect/generics/visitor/Visitor.java new file mode 100644 index 00000000..6a7cd041 --- /dev/null +++ b/src/sun/reflect/generics/visitor/Visitor.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.generics.visitor; + +import sun.reflect.generics.tree.ClassSignature; +import sun.reflect.generics.tree.MethodTypeSignature; + +public interface Visitor extends TypeTreeVisitor { + + void visitClassSignature(ClassSignature cs); + void visitMethodTypeSignature(MethodTypeSignature ms); +} diff --git a/src/sun/reflect/misc/ConstructorUtil.java b/src/sun/reflect/misc/ConstructorUtil.java new file mode 100644 index 00000000..e0e4233c --- /dev/null +++ b/src/sun/reflect/misc/ConstructorUtil.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.misc; + +import java.lang.reflect.Constructor; + +public final class ConstructorUtil { + + private ConstructorUtil() { + } + + public static Constructor getConstructor(Class cls, Class[] params) + throws NoSuchMethodException { + ReflectUtil.checkPackageAccess(cls); + return cls.getConstructor(params); + } + + public static Constructor[] getConstructors(Class cls) { + ReflectUtil.checkPackageAccess(cls); + return cls.getConstructors(); + } +} diff --git a/src/sun/reflect/misc/FieldUtil.java b/src/sun/reflect/misc/FieldUtil.java new file mode 100644 index 00000000..d0cb48f0 --- /dev/null +++ b/src/sun/reflect/misc/FieldUtil.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.misc; + +import java.lang.reflect.Field; + +/* + * Create a trampoline class. + */ +public final class FieldUtil { + + private FieldUtil() { + } + + public static Field getField(Class cls, String name) + throws NoSuchFieldException { + ReflectUtil.checkPackageAccess(cls); + return cls.getField(name); + } + + public static Field[] getFields(Class cls) { + ReflectUtil.checkPackageAccess(cls); + return cls.getFields(); + } + + public static Field[] getDeclaredFields(Class cls) { + ReflectUtil.checkPackageAccess(cls); + return cls.getDeclaredFields(); + } +} diff --git a/src/sun/reflect/misc/MethodUtil.java b/src/sun/reflect/misc/MethodUtil.java new file mode 100644 index 00000000..5fa9ef00 --- /dev/null +++ b/src/sun/reflect/misc/MethodUtil.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.misc; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLConnection; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.PrivilegedExceptionAction; +import java.security.SecureClassLoader; +import java.util.HashMap; +import java.util.Map; + +import sun.misc.IOUtils; + + +class Trampoline { + static { + if (Trampoline.class.getClassLoader() == null) { + throw new Error( + "Trampoline must not be defined by the bootstrap classloader"); + } + } + + private static void ensureInvocableMethod(Method m) + throws InvocationTargetException + { + Class clazz = m.getDeclaringClass(); + if (clazz.equals(AccessController.class) || + clazz.equals(Method.class) || + clazz.getName().startsWith("java.lang.invoke.")) + throw new InvocationTargetException( + new UnsupportedOperationException("invocation not supported")); + } + + private static Object invoke(Method m, Object obj, Object[] params) + throws InvocationTargetException, IllegalAccessException + { + ensureInvocableMethod(m); + return m.invoke(obj, params); + } +} + +/* + * Create a trampoline class. + */ +public final class MethodUtil extends SecureClassLoader { + private static final String MISC_PKG = "sun.reflect.misc."; + private static final String TRAMPOLINE = MISC_PKG + "Trampoline"; + private static final Method bounce = getTrampoline(); + + private MethodUtil() { + super(); + } + + public static Method getMethod(Class cls, String name, Class[] args) + throws NoSuchMethodException { + ReflectUtil.checkPackageAccess(cls); + return cls.getMethod(name, args); + } + + public static Method[] getMethods(Class cls) { + ReflectUtil.checkPackageAccess(cls); + return cls.getMethods(); + } + + /* + * Discover the public methods on public classes + * and interfaces accessible to any caller by calling + * Class.getMethods() and walking towards Object until + * we're done. + */ + public static Method[] getPublicMethods(Class cls) { + // compatibility for update release + if (System.getSecurityManager() == null) { + return cls.getMethods(); + } + Map sigs = new HashMap(); + while (cls != null) { + boolean done = getInternalPublicMethods(cls, sigs); + if (done) { + break; + } + getInterfaceMethods(cls, sigs); + cls = cls.getSuperclass(); + } + return sigs.values().toArray(new Method[sigs.size()]); + } + + /* + * Process the immediate interfaces of this class or interface. + */ + private static void getInterfaceMethods(Class cls, + Map sigs) { + Class[] intfs = cls.getInterfaces(); + for (int i=0; i < intfs.length; i++) { + Class intf = intfs[i]; + boolean done = getInternalPublicMethods(intf, sigs); + if (!done) { + getInterfaceMethods(intf, sigs); + } + } + } + + /* + * + * Process the methods in this class or interface + */ + private static boolean getInternalPublicMethods(Class cls, + Map sigs) { + Method[] methods = null; + try { + /* + * This class or interface is non-public so we + * can't use any of it's methods. Go back and + * try again with a superclass or superinterface. + */ + if (!Modifier.isPublic(cls.getModifiers())) { + return false; + } + if (!ReflectUtil.isPackageAccessible(cls)) { + return false; + } + + methods = cls.getMethods(); + } catch (SecurityException se) { + return false; + } + + /* + * Check for inherited methods with non-public + * declaring classes. They might override and hide + * methods from their superclasses or + * superinterfaces. + */ + boolean done = true; + for (int i=0; i < methods.length; i++) { + Class dc = methods[i].getDeclaringClass(); + if (!Modifier.isPublic(dc.getModifiers())) { + done = false; + break; + } + } + + if (done) { + /* + * We're done. Spray all the methods into + * the list and then we're out of here. + */ + for (int i=0; i < methods.length; i++) { + addMethod(sigs, methods[i]); + } + } else { + /* + * Simulate cls.getDeclaredMethods() by + * stripping away inherited methods. + */ + for (int i=0; i < methods.length; i++) { + Class dc = methods[i].getDeclaringClass(); + if (cls.equals(dc)) { + addMethod(sigs, methods[i]); + } + } + } + return done; + } + + private static void addMethod(Map sigs, Method method) { + Signature signature = new Signature(method); + if (!sigs.containsKey(signature)) { + sigs.put(signature, method); + } else if (!method.getDeclaringClass().isInterface()){ + /* + * Superclasses beat interfaces. + */ + Method old = sigs.get(signature); + if (old.getDeclaringClass().isInterface()) { + sigs.put(signature, method); + } + } + } + + /** + * A class that represents the unique elements of a method that will be a + * key in the method cache. + */ + private static class Signature { + private String methodName; + private Class[] argClasses; + + private volatile int hashCode = 0; + + Signature(Method m) { + this.methodName = m.getName(); + this.argClasses = m.getParameterTypes(); + } + + public boolean equals(Object o2) { + if (this == o2) { + return true; + } + Signature that = (Signature)o2; + if (!(methodName.equals(that.methodName))) { + return false; + } + if (argClasses.length != that.argClasses.length) { + return false; + } + for (int i = 0; i < argClasses.length; i++) { + if (!(argClasses[i] == that.argClasses[i])) { + return false; + } + } + return true; + } + + /** + * Hash code computed using algorithm suggested in + * Effective Java, Item 8. + */ + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37 * result + methodName.hashCode(); + if (argClasses != null) { + for (int i = 0; i < argClasses.length; i++) { + result = 37 * result + ((argClasses[i] == null) ? 0 : + argClasses[i].hashCode()); + } + } + hashCode = result; + } + return hashCode; + } + } + + + /* + * Bounce through the trampoline. + */ + public static Object invoke(Method m, Object obj, Object[] params) + throws InvocationTargetException, IllegalAccessException { + try { + return bounce.invoke(null, new Object[] {m, obj, params}); + } catch (InvocationTargetException ie) { + Throwable t = ie.getCause(); + + if (t instanceof InvocationTargetException) { + throw (InvocationTargetException)t; + } else if (t instanceof IllegalAccessException) { + throw (IllegalAccessException)t; + } else if (t instanceof RuntimeException) { + throw (RuntimeException)t; + } else if (t instanceof Error) { + throw (Error)t; + } else { + throw new Error("Unexpected invocation error", t); + } + } catch (IllegalAccessException iae) { + // this can't happen + throw new Error("Unexpected invocation error", iae); + } + } + + private static Method getTrampoline() { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Method run() throws Exception { + Class t = getTrampolineClass(); + Class[] types = { + Method.class, Object.class, Object[].class + }; + Method b = t.getDeclaredMethod("invoke", types); + b.setAccessible(true); + return b; + } + }); + } catch (Exception e) { + throw new InternalError("bouncer cannot be found", e); + } + } + + + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException + { + // First, check if the class has already been loaded + ReflectUtil.checkPackageAccess(name); + Class c = findLoadedClass(name); + if (c == null) { + try { + c = findClass(name); + } catch (ClassNotFoundException e) { + // Fall through ... + } + if (c == null) { + c = getParent().loadClass(name); + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + + + protected Class findClass(final String name) + throws ClassNotFoundException + { + if (!name.startsWith(MISC_PKG)) { + throw new ClassNotFoundException(name); + } + String path = name.replace('.', '/').concat(".class"); + URL res = getResource(path); + if (res != null) { + try { + return defineClass(name, res); + } catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } else { + throw new ClassNotFoundException(name); + } + } + + + /* + * Define the proxy classes + */ + private Class defineClass(String name, URL url) throws IOException { + byte[] b = getBytes(url); + CodeSource cs = new CodeSource(null, (java.security.cert.Certificate[])null); + if (!name.equals(TRAMPOLINE)) { + throw new IOException("MethodUtil: bad name " + name); + } + return defineClass(name, b, 0, b.length, cs); + } + + + /* + * Returns the contents of the specified URL as an array of bytes. + */ + private static byte[] getBytes(URL url) throws IOException { + URLConnection uc = url.openConnection(); + if (uc instanceof java.net.HttpURLConnection) { + java.net.HttpURLConnection huc = (java.net.HttpURLConnection) uc; + int code = huc.getResponseCode(); + if (code >= java.net.HttpURLConnection.HTTP_BAD_REQUEST) { + throw new IOException("open HTTP connection failed."); + } + } + int len = uc.getContentLength(); + InputStream in = new BufferedInputStream(uc.getInputStream()); + + byte[] b; + try { + b = IOUtils.readFully(in, len, true); + } finally { + in.close(); + } + return b; + } + + + protected PermissionCollection getPermissions(CodeSource codesource) + { + PermissionCollection perms = super.getPermissions(codesource); + perms.add(new AllPermission()); + return perms; + } + + private static Class getTrampolineClass() { + try { + return Class.forName(TRAMPOLINE, true, new MethodUtil()); + } catch (ClassNotFoundException e) { + } + return null; + } + +} diff --git a/src/sun/reflect/misc/ReflectUtil.java b/src/sun/reflect/misc/ReflectUtil.java new file mode 100644 index 00000000..989f3b6c --- /dev/null +++ b/src/sun/reflect/misc/ReflectUtil.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.reflect.misc; + +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; + +import sun.reflect.Reflection; +import sun.security.util.SecurityConstants; + +public final class ReflectUtil { + + private ReflectUtil() { + } + + public static Class forName(String name) + throws ClassNotFoundException { + checkPackageAccess(name); + return Class.forName(name); + } + + public static Object newInstance(Class cls) + throws InstantiationException, IllegalAccessException { + checkPackageAccess(cls); + return cls.newInstance(); + } + + /* + * Reflection.ensureMemberAccess is overly-restrictive + * due to a bug. We awkwardly work around it for now. + */ + public static void ensureMemberAccess(Class currentClass, + Class memberClass, + Object target, + int modifiers) + throws IllegalAccessException + { + if (target == null && Modifier.isProtected(modifiers)) { + int mods = modifiers; + mods = mods & (~Modifier.PROTECTED); + mods = mods | Modifier.PUBLIC; + + /* + * See if we fail because of class modifiers + */ + Reflection.ensureMemberAccess(currentClass, + memberClass, + target, + mods); + try { + /* + * We're still here so class access was ok. + * Now try with default field access. + */ + mods = mods & (~Modifier.PUBLIC); + Reflection.ensureMemberAccess(currentClass, + memberClass, + target, + mods); + /* + * We're still here so access is ok without + * checking for protected. + */ + return; + } catch (IllegalAccessException e) { + /* + * Access failed but we're 'protected' so + * if the test below succeeds then we're ok. + */ + if (isSubclassOf(currentClass, memberClass)) { + return; + } else { + throw e; + } + } + } else { + Reflection.ensureMemberAccess(currentClass, + memberClass, + target, + modifiers); + } + } + + private static boolean isSubclassOf(Class queryClass, + Class ofClass) + { + while (queryClass != null) { + if (queryClass == ofClass) { + return true; + } + queryClass = queryClass.getSuperclass(); + } + return false; + } + + /** + * Does a conservative approximation of member access check. Use this if + * you don't have an actual 'userland' caller Class/ClassLoader available. + * This might be more restrictive than a precise member access check where + * you have a caller, but should never allow a member access that is + * forbidden. + * + * @param m the {@code Member} about to be accessed + */ + public static void conservativeCheckMemberAccess(Member m) throws SecurityException{ + final SecurityManager sm = System.getSecurityManager(); + if (sm == null) + return; + + // Check for package access on the declaring class. + // + // In addition, unless the member and the declaring class are both + // public check for access declared member permissions. + // + // This is done regardless of ClassLoader relations between the {@code + // Member m} and any potential caller. + + final Class declaringClass = m.getDeclaringClass(); + + checkPackageAccess(declaringClass); + + if (Modifier.isPublic(m.getModifiers()) && + Modifier.isPublic(declaringClass.getModifiers())) + return; + + // Check for declared member access. + sm.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION); + } + + /** + * Checks package access on the given class. + * + * If it is a {@link Proxy#isProxyClass(Class)} that implements + * a non-public interface (i.e. may be in a non-restricted package), + * also check the package access on the proxy interfaces. + */ + public static void checkPackageAccess(Class clazz) { + checkPackageAccess(clazz.getName()); + if (isNonPublicProxyClass(clazz)) { + checkProxyPackageAccess(clazz); + } + } + + /** + * Checks package access on the given classname. + * This method is typically called when the Class instance is not + * available and the caller attempts to load a class on behalf + * the true caller (application). + */ + public static void checkPackageAccess(String name) { + SecurityManager s = System.getSecurityManager(); + if (s != null) { + String cname = name.replace('/', '.'); + if (cname.startsWith("[")) { + int b = cname.lastIndexOf('[') + 2; + if (b > 1 && b < cname.length()) { + cname = cname.substring(b); + } + } + int i = cname.lastIndexOf('.'); + if (i != -1) { + s.checkPackageAccess(cname.substring(0, i)); + } + } + } + + public static boolean isPackageAccessible(Class clazz) { + try { + checkPackageAccess(clazz); + } catch (SecurityException e) { + return false; + } + return true; + } + + // Returns true if p is an ancestor of cl i.e. class loader 'p' can + // be found in the cl's delegation chain + private static boolean isAncestor(ClassLoader p, ClassLoader cl) { + ClassLoader acl = cl; + do { + acl = acl.getParent(); + if (p == acl) { + return true; + } + } while (acl != null); + return false; + } + + /** + * Returns true if package access check is needed for reflective + * access from a class loader 'from' to classes or members in + * a class defined by class loader 'to'. This method returns true + * if 'from' is not the same as or an ancestor of 'to'. All code + * in a system domain are granted with all permission and so this + * method returns false if 'from' class loader is a class loader + * loading system classes. On the other hand, if a class loader + * attempts to access system domain classes, it requires package + * access check and this method will return true. + */ + public static boolean needsPackageAccessCheck(ClassLoader from, ClassLoader to) { + if (from == null || from == to) + return false; + + if (to == null) + return true; + + return !isAncestor(from, to); + } + + /** + * Check package access on the proxy interfaces that the given proxy class + * implements. + * + * @param clazz Proxy class object + */ + public static void checkProxyPackageAccess(Class clazz) { + SecurityManager s = System.getSecurityManager(); + if (s != null) { + // check proxy interfaces if the given class is a proxy class + if (Proxy.isProxyClass(clazz)) { + for (Class intf : clazz.getInterfaces()) { + checkPackageAccess(intf); + } + } + } + } + + /** + * Access check on the interfaces that a proxy class implements and throw + * {@code SecurityException} if it accesses a restricted package from + * the caller's class loader. + * + * @param ccl the caller's class loader + * @param interfaces the list of interfaces that a proxy class implements + */ + public static void checkProxyPackageAccess(ClassLoader ccl, + Class... interfaces) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + for (Class intf : interfaces) { + ClassLoader cl = intf.getClassLoader(); + if (needsPackageAccessCheck(ccl, cl)) { + checkPackageAccess(intf); + } + } + } + } + + // Note that bytecode instrumentation tools may exclude 'sun.*' + // classes but not generated proxy classes and so keep it in com.sun.* + public static final String PROXY_PACKAGE = "com.sun.proxy"; + + /** + * Test if the given class is a proxy class that implements + * non-public interface. Such proxy class may be in a non-restricted + * package that bypasses checkPackageAccess. + */ + public static boolean isNonPublicProxyClass(Class cls) { + String name = cls.getName(); + int i = name.lastIndexOf('.'); + String pkg = (i != -1) ? name.substring(0, i) : ""; + return Proxy.isProxyClass(cls) && !pkg.equals(PROXY_PACKAGE); + } + + /** + * Check if the given method is a method declared in the proxy interface + * implemented by the given proxy instance. + * + * @param proxy a proxy instance + * @param method an interface method dispatched to a InvocationHandler + * + * @throws IllegalArgumentException if the given proxy or method is invalid. + */ + public static void checkProxyMethod(Object proxy, Method method) { + // check if it is a valid proxy instance + if (proxy == null || !Proxy.isProxyClass(proxy.getClass())) { + throw new IllegalArgumentException("Not a Proxy instance"); +} + if (Modifier.isStatic(method.getModifiers())) { + throw new IllegalArgumentException("Can't handle static method"); + } + + Class c = method.getDeclaringClass(); + if (c == Object.class) { + String name = method.getName(); + if (name.equals("hashCode") || name.equals("equals") || name.equals("toString")) { + return; + } + } + + if (isSuperInterface(proxy.getClass(), c)) { + return; + } + + // disallow any method not declared in one of the proxy intefaces + throw new IllegalArgumentException("Can't handle: " + method); + } + + private static boolean isSuperInterface(Class c, Class intf) { + for (Class i : c.getInterfaces()) { + if (i == intf) { + return true; + } + if (isSuperInterface(i, intf)) { + return true; + } + } + return false; + } + + /** + * Checks if {@code Class cls} is a VM-anonymous class + * as defined by {@link sun.misc.Unsafe#defineAnonymousClass} + * (not to be confused with a Java Language anonymous inner class). + */ + public static boolean isVMAnonymousClass(Class cls) { + return cls.getName().indexOf("/") > -1; + } +} diff --git a/src/sun/reflect/package.html b/src/sun/reflect/package.html new file mode 100644 index 00000000..18ff6162 --- /dev/null +++ b/src/sun/reflect/package.html @@ -0,0 +1,187 @@ + + + + + + +

    + + Licensee impact of JDK 1.4 reflection changes + +

    +

    + +Sun's JDK 1.4 contains a new implementation of java.lang.reflect which +offers substantially higher performance than previous JDKs' native +code. Licensees can at their discretion port these changes. There are +no public API or documentation changes associated with the new +reflection implementation aside from a few minor clarifications in the +specifications of Method.invoke(), Constructor.newInstance(), and a +few methods in java.lang.reflect.Field. + +

    +

    + +The bulk of the new implementation is Java programming language code +which generates bytecodes, and is therefore portable. If licensees +desire to port it, the following JVM changes are required: + +

      +
    1. The following four new JVM entry points must be added: + +
        +
      • JVM_GetClassDeclaredConstructors +
      • JVM_GetClassDeclaredFields +
      • JVM_GetClassDeclaredMethods +
      • JVM_GetClassAccessFlags +
      + +The first three return the declared constructors, fields, and methods +for a given class, with an option to return only the public ones. They +are similar in functionality to the earlier GetClassConstructors, +GetClassFields, and GetClassMethods. JVM_GetClassDeclaredFields and +JVM_GetClassDeclaredMethods must intern the Strings for the names of +the Field and Method objects returned. The fouth returns the access +flags for a given class as marked in the class file, as opposed to in +the InnerClasses attribute if the class is an inner class, and +therefore differs from JVM_GetClassModifiers for inner classes (most +importantly, protected inner classes; see 4471811.) + +
    2. The JVM's link resolver must be modified to allow all field and +method references from subclasses of sun.reflect.MagicAccessorImpl to +any other class (even to private members of other classes) to +succeed. This allows setAccessible() and its associated checks to be +implemented in Java. + +
    3. The code which calls the verifier must skip verification for all +subclasses of sun.reflect.MagicAccessorImpl. (It was originally +intended that only a subset of the stub classes used for serialization +would not pass the verifier, specifically, those subclassing +SerializationConstructorAccessorImpl; see 4486457 for a case where +this does not work.) + +
    4. The stack walker for security checks must be modified to skip not +only all Method.invoke() frames, but also any frames for which the +class is a subclass of sun.reflect.MethodAccessorImpl. + +
    5. The JVM entry points JVM_InvokeMethod and +JVM_NewInstanceFromConstructor are currently still used because the +first invocation of the bytecode-based reflection is currently slower +than the original native code. The security checks they perform can, +however, be disabled, as they are now performed by Java programming +language code. + +
    + +

    +

    + +The following changes were discovered to be necessary for backward +compatibility with certain applications (see bug 4474172): + +

      + +
    1. The existing JVM entry point JVM_LatestUserDefinedLoader +(typically used in applications which rely on the 1.1 security +framework) must skip reflection-related frames in its stack walk: +specifically all frames associated with subclasses of +sun.reflect.MethodAccessorImpl and +sun.reflect.ConstructorAccessorImpl. + +
    2. The new reflection implementation can cause class loading to +occur in previously-unexpected places (namely during reflective +calls). This can cause class loaders which contain subtle bugs to +break. In general it is not possible to guarantee complete backward +bug compatibility, but one kind of bug has been observed more than +once: the inability of a user-defined loader to handle delegation to +it for a class it has already loaded. The new reflection +implementation is predicated on delegation working properly, as it +loads stub classes into newly-fabricated class loaders of type +sun.reflect.DelegatingClassLoader, one stub class per loader, to allow +unloading of the stub classes to occur more quickly. To handle this +kind of bug, the JVM's internal class lookup mechanism must be +slightly modified to check for instances of +sun.reflect.DelegatingClassLoader as the incoming class loader and +silently traverse the "parent" field once for such loaders before +entering the bulk of the resolution code. This avoids an upcall to +Java programming language code which certain loaders can not handle. + +
    + +

    +

    + +The following JVM entry points may be deleted: + +

      +
    • JVM_GetClassFields +
    • JVM_GetClassMethods +
    • JVM_GetClassConstructors +
    • JVM_GetClassField +
    • JVM_GetClassMethod +
    • JVM_GetClassConstructor +
    • JVM_NewInstance +
    • JVM_GetField +
    • JVM_GetPrimitiveField +
    • JVM_SetField +
    • JVM_SetPrimitiveField +
    + +

    +

    + +To keep using the previous reflection implementation, licensees should +not take changes from Sun's JDK 1.4 relating specifically to the +implementation of reflection in the following classes/methods and +any associated native code: + +

      +
    • java.lang.Class.newInstance0 +
    • java.lang.Class.getClassLoader0 +
    • java.lang.Class.getFields +
    • java.lang.Class.getMethods +
    • java.lang.Class.getDeclaredFields +
    • java.lang.Class.getDeclaredMethods +
    • java.lang.Class.getFields0 +
    • java.lang.Class.getMethods0 +
    • java.lang.Class.getConstructors0 +
    • java.lang.Class.getField0 +
    • java.lang.Class.getMethod0 +
    • java.lang.Class.getConstructor0 +
    • java.lang.ClassLoader.getCallerClassLoader +
    • java.lang.System.getCallerClass +
    • java.lang.reflect.AccessibleObject +
    • java.lang.reflect.Constructor +
    • java.lang.reflect.Field +
    • java.lang.reflect.Method +
    • java.lang.reflect.Modifier +
    • sun.misc.ClassReflector +
    + +

    + diff --git a/src/sun/security/acl/AclEntryImpl.java b/src/sun/security/acl/AclEntryImpl.java new file mode 100644 index 00000000..79479f23 --- /dev/null +++ b/src/sun/security/acl/AclEntryImpl.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.acl; + +import java.util.*; +import java.security.Principal; +import java.security.acl.*; + +/** + * This is a class that describes one entry that associates users + * or groups with permissions in the ACL. + * The entry may be used as a way of granting or denying permissions. + * @author Satish Dharmaraj + */ +public class AclEntryImpl implements AclEntry { + private Principal user = null; + private Vector permissionSet = new Vector<>(10, 10); + private boolean negative = false; + + /** + * Construct an ACL entry that associates a user with permissions + * in the ACL. + * @param user The user that is associated with this entry. + */ + public AclEntryImpl(Principal user) { + this.user = user; + } + + /** + * Construct a null ACL entry + */ + public AclEntryImpl() { + } + + /** + * Sets the principal in the entity. If a group or a + * principal had already been set, a false value is + * returned, otherwise a true value is returned. + * @param user The user that is associated with this entry. + * @return true if the principal is set, false if there is + * one already. + */ + public boolean setPrincipal(Principal user) { + if (this.user != null) + return false; + this.user = user; + return true; + } + + /** + * This method sets the ACL to have negative permissions. + * That is the user or group is denied the permission set + * specified in the entry. + */ + public void setNegativePermissions() { + negative = true; + } + + /** + * Returns true if this is a negative ACL. + */ + public boolean isNegative() { + return negative; + } + + /** + * A principal or a group can be associated with multiple + * permissions. This method adds a permission to the ACL entry. + * @param permission The permission to be associated with + * the principal or the group in the entry. + * @return true if the permission was added, false if the + * permission was already part of the permission set. + */ + public boolean addPermission(Permission permission) { + + if (permissionSet.contains(permission)) + return false; + + permissionSet.addElement(permission); + + return true; + } + + /** + * The method disassociates the permission from the Principal + * or the Group in this ACL entry. + * @param permission The permission to be disassociated with + * the principal or the group in the entry. + * @return true if the permission is removed, false if the + * permission is not part of the permission set. + */ + public boolean removePermission(Permission permission) { + return permissionSet.removeElement(permission); + } + + /** + * Checks if the passed permission is part of the allowed + * permission set in this entry. + * @param permission The permission that has to be part of + * the permission set in the entry. + * @return true if the permission passed is part of the + * permission set in the entry, false otherwise. + */ + public boolean checkPermission(Permission permission) { + return permissionSet.contains(permission); + } + + /** + * return an enumeration of the permissions in this ACL entry. + */ + public Enumeration permissions() { + return permissionSet.elements(); + } + + /** + * Return a string representation of the contents of the ACL entry. + */ + public String toString() { + StringBuffer s = new StringBuffer(); + if (negative) + s.append("-"); + else + s.append("+"); + if (user instanceof Group) + s.append("Group."); + else + s.append("User."); + s.append(user + "="); + Enumeration e = permissions(); + while(e.hasMoreElements()) { + Permission p = e.nextElement(); + s.append(p); + if (e.hasMoreElements()) + s.append(","); + } + return new String(s); + } + + /** + * Clones an AclEntry. + */ + @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly + public synchronized Object clone() { + AclEntryImpl cloned; + cloned = new AclEntryImpl(user); + cloned.permissionSet = (Vector) permissionSet.clone(); + cloned.negative = negative; + return cloned; + } + + /** + * Return the Principal associated in this ACL entry. + * The method returns null if the entry uses a group + * instead of a principal. + */ + public Principal getPrincipal() { + return user; + } +} diff --git a/src/sun/security/acl/AclImpl.java b/src/sun/security/acl/AclImpl.java new file mode 100644 index 00000000..ba46f1a8 --- /dev/null +++ b/src/sun/security/acl/AclImpl.java @@ -0,0 +1,407 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.util.*; +import java.security.Principal; +import java.security.acl.*; + +/** + * An Access Control List (ACL) is encapsulated by this class. + * @author Satish Dharmaraj + */ +public class AclImpl extends OwnerImpl implements Acl { + // + // Maintain four tables. one each for positive and negative + // ACLs. One each depending on whether the entity is a group + // or principal. + // + private Hashtable allowedUsersTable = + new Hashtable<>(23); + private Hashtable allowedGroupsTable = + new Hashtable<>(23); + private Hashtable deniedUsersTable = + new Hashtable<>(23); + private Hashtable deniedGroupsTable = + new Hashtable<>(23); + private String aclName = null; + private Vector zeroSet = new Vector<>(1,1); + + + /** + * Constructor for creating an empty ACL. + */ + public AclImpl(Principal owner, String name) { + super(owner); + try { + setName(owner, name); + } catch (Exception e) {} + } + + /** + * Sets the name of the ACL. + * @param caller the principal who is invoking this method. + * @param name the name of the ACL. + * @exception NotOwnerException if the caller principal is + * not on the owners list of the Acl. + */ + public void setName(Principal caller, String name) + throws NotOwnerException + { + if (!isOwner(caller)) + throw new NotOwnerException(); + + aclName = name; + } + + /** + * Returns the name of the ACL. + * @return the name of the ACL. + */ + public String getName() { + return aclName; + } + + /** + * Adds an ACL entry to this ACL. An entry associates a + * group or a principal with a set of permissions. Each + * user or group can have one positive ACL entry and one + * negative ACL entry. If there is one of the type (negative + * or positive) already in the table, a false value is returned. + * The caller principal must be a part of the owners list of + * the ACL in order to invoke this method. + * @param caller the principal who is invoking this method. + * @param entry the ACL entry that must be added to the ACL. + * @return true on success, false if the entry is already present. + * @exception NotOwnerException if the caller principal + * is not on the owners list of the Acl. + */ + public synchronized boolean addEntry(Principal caller, AclEntry entry) + throws NotOwnerException + { + if (!isOwner(caller)) + throw new NotOwnerException(); + + Hashtable aclTable = findTable(entry); + Principal key = entry.getPrincipal(); + + if (aclTable.get(key) != null) + return false; + + aclTable.put(key, entry); + return true; + } + + /** + * Removes an ACL entry from this ACL. + * The caller principal must be a part of the owners list of the ACL + * in order to invoke this method. + * @param caller the principal who is invoking this method. + * @param entry the ACL entry that must be removed from the ACL. + * @return true on success, false if the entry is not part of the ACL. + * @exception NotOwnerException if the caller principal is not + * the owners list of the Acl. + */ + public synchronized boolean removeEntry(Principal caller, AclEntry entry) + throws NotOwnerException + { + if (!isOwner(caller)) + throw new NotOwnerException(); + + Hashtable aclTable = findTable(entry); + Principal key = entry.getPrincipal(); + + AclEntry o = aclTable.remove(key); + return (o != null); + } + + /** + * This method returns the set of allowed permissions for the + * specified principal. This set of allowed permissions is calculated + * as follows: + * + * If there is no entry for a group or a principal an empty permission + * set is assumed. + * + * The group positive permission set is the union of all + * the positive permissions of each group that the individual belongs to. + * The group negative permission set is the union of all + * the negative permissions of each group that the individual belongs to. + * If there is a specific permission that occurs in both + * the postive permission set and the negative permission set, + * it is removed from both. The group positive and negatoive permission + * sets are calculated. + * + * The individial positive permission set and the individual negative + * permission set is then calculated. Again abscence of an entry means + * the empty set. + * + * The set of permissions granted to the principal is then calculated using + * the simple rule: Individual permissions always override the Group permissions. + * Specifically, individual negative permission set (specific + * denial of permissions) overrides the group positive permission set. + * And the individual positive permission set override the group negative + * permission set. + * + * @param user the principal for which the ACL entry is returned. + * @return The resulting permission set that the principal is allowed. + */ + public synchronized Enumeration getPermissions(Principal user) { + + Enumeration individualPositive; + Enumeration individualNegative; + Enumeration groupPositive; + Enumeration groupNegative; + + // + // canonicalize the sets. That is remove common permissions from + // positive and negative sets. + // + groupPositive = + subtract(getGroupPositive(user), getGroupNegative(user)); + groupNegative = + subtract(getGroupNegative(user), getGroupPositive(user)); + individualPositive = + subtract(getIndividualPositive(user), getIndividualNegative(user)); + individualNegative = + subtract(getIndividualNegative(user), getIndividualPositive(user)); + + // + // net positive permissions is individual positive permissions + // plus (group positive - individual negative). + // + Enumeration temp1 = + subtract(groupPositive, individualNegative); + Enumeration netPositive = + union(individualPositive, temp1); + + // recalculate the enumeration since we lost it in performing the + // subtraction + // + individualPositive = + subtract(getIndividualPositive(user), getIndividualNegative(user)); + individualNegative = + subtract(getIndividualNegative(user), getIndividualPositive(user)); + + // + // net negative permissions is individual negative permissions + // plus (group negative - individual positive). + // + temp1 = subtract(groupNegative, individualPositive); + Enumeration netNegative = union(individualNegative, temp1); + + return subtract(netPositive, netNegative); + } + + /** + * This method checks whether or not the specified principal + * has the required permission. If permission is denied + * permission false is returned, a true value is returned otherwise. + * This method does not authenticate the principal. It presumes that + * the principal is a valid authenticated principal. + * @param principal the name of the authenticated principal + * @param permission the permission that the principal must have. + * @return true of the principal has the permission desired, false + * otherwise. + */ + public boolean checkPermission(Principal principal, Permission permission) + { + Enumeration permSet = getPermissions(principal); + while (permSet.hasMoreElements()) { + Permission p = permSet.nextElement(); + if (p.equals(permission)) + return true; + } + return false; + } + + /** + * returns an enumeration of the entries in this ACL. + */ + public synchronized Enumeration entries() { + return new AclEnumerator(this, + allowedUsersTable, allowedGroupsTable, + deniedUsersTable, deniedGroupsTable); + } + + /** + * return a stringified version of the + * ACL. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + Enumeration entries = entries(); + while (entries.hasMoreElements()) { + AclEntry entry = entries.nextElement(); + sb.append(entry.toString().trim()); + sb.append("\n"); + } + + return sb.toString(); + } + + // + // Find the table that this entry belongs to. There are 4 + // tables that are maintained. One each for postive and + // negative ACLs and one each for groups and users. + // This method figures out which + // table is the one that this AclEntry belongs to. + // + private Hashtable findTable(AclEntry entry) { + Hashtable aclTable = null; + + Principal p = entry.getPrincipal(); + if (p instanceof Group) { + if (entry.isNegative()) + aclTable = deniedGroupsTable; + else + aclTable = allowedGroupsTable; + } else { + if (entry.isNegative()) + aclTable = deniedUsersTable; + else + aclTable = allowedUsersTable; + } + return aclTable; + } + + // + // returns the set e1 U e2. + // + private static Enumeration union(Enumeration e1, + Enumeration e2) { + Vector v = new Vector<>(20, 20); + + while (e1.hasMoreElements()) + v.addElement(e1.nextElement()); + + while (e2.hasMoreElements()) { + Permission o = e2.nextElement(); + if (!v.contains(o)) + v.addElement(o); + } + + return v.elements(); + } + + // + // returns the set e1 - e2. + // + private Enumeration subtract(Enumeration e1, + Enumeration e2) { + Vector v = new Vector<>(20, 20); + + while (e1.hasMoreElements()) + v.addElement(e1.nextElement()); + + while (e2.hasMoreElements()) { + Permission o = e2.nextElement(); + if (v.contains(o)) + v.removeElement(o); + } + + return v.elements(); + } + + private Enumeration getGroupPositive(Principal user) { + Enumeration groupPositive = zeroSet.elements(); + Enumeration e = allowedGroupsTable.keys(); + while (e.hasMoreElements()) { + Group g = (Group)e.nextElement(); + if (g.isMember(user)) { + AclEntry ae = allowedGroupsTable.get(g); + groupPositive = union(ae.permissions(), groupPositive); + } + } + return groupPositive; + } + + private Enumeration getGroupNegative(Principal user) { + Enumeration groupNegative = zeroSet.elements(); + Enumeration e = deniedGroupsTable.keys(); + while (e.hasMoreElements()) { + Group g = (Group)e.nextElement(); + if (g.isMember(user)) { + AclEntry ae = deniedGroupsTable.get(g); + groupNegative = union(ae.permissions(), groupNegative); + } + } + return groupNegative; + } + + private Enumeration getIndividualPositive(Principal user) { + Enumeration individualPositive = zeroSet.elements(); + AclEntry ae = allowedUsersTable.get(user); + if (ae != null) + individualPositive = ae.permissions(); + return individualPositive; + } + + private Enumeration getIndividualNegative(Principal user) { + Enumeration individualNegative = zeroSet.elements(); + AclEntry ae = deniedUsersTable.get(user); + if (ae != null) + individualNegative = ae.permissions(); + return individualNegative; + } +} + +final class AclEnumerator implements Enumeration { + Acl acl; + Enumeration u1, u2, g1, g2; + + AclEnumerator(Acl acl, Hashtable u1, Hashtable g1, + Hashtable u2, Hashtable g2) { + this.acl = acl; + this.u1 = u1.elements(); + this.u2 = u2.elements(); + this.g1 = g1.elements(); + this.g2 = g2.elements(); + } + + public boolean hasMoreElements() { + return (u1.hasMoreElements() || + u2.hasMoreElements() || + g1.hasMoreElements() || + g2.hasMoreElements()); + } + + public AclEntry nextElement() + { + AclEntry o; + synchronized (acl) { + if (u1.hasMoreElements()) + return u1.nextElement(); + if (u2.hasMoreElements()) + return u2.nextElement(); + if (g1.hasMoreElements()) + return g1.nextElement(); + if (g2.hasMoreElements()) + return g2.nextElement(); + } + throw new NoSuchElementException("Acl Enumerator"); + } +} diff --git a/src/sun/security/acl/AllPermissionsImpl.java b/src/sun/security/acl/AllPermissionsImpl.java new file mode 100644 index 00000000..750bcffa --- /dev/null +++ b/src/sun/security/acl/AllPermissionsImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1996, 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.security.acl.*; + +/** + * This class implements the principal interface for the set of all permissions. + * @author Satish Dharmaraj + */ +public class AllPermissionsImpl extends PermissionImpl { + + public AllPermissionsImpl(String s) { + super(s); + } + + /** + * This function returns true if the permission passed matches the permission represented in + * this interface. + * @param another The Permission object to compare with. + * @returns true always + */ + public boolean equals(Permission another) { + return true; + } +} diff --git a/src/sun/security/acl/GroupImpl.java b/src/sun/security/acl/GroupImpl.java new file mode 100644 index 00000000..40842a78 --- /dev/null +++ b/src/sun/security/acl/GroupImpl.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; +import java.util.Vector; + +/** + * This class implements a group of principals. + * @author Satish Dharmaraj + */ +public class GroupImpl implements Group { + private Vector groupMembers = new Vector<>(50, 100); + private String group; + + /** + * Constructs a Group object with no members. + * @param groupName the name of the group + */ + public GroupImpl(String groupName) { + this.group = groupName; + } + + /** + * adds the specified member to the group. + * @param user The principal to add to the group. + * @return true if the member was added - false if the + * member could not be added. + */ + public boolean addMember(Principal user) { + if (groupMembers.contains(user)) + return false; + + // do not allow groups to be added to itself. + if (group.equals(user.toString())) + throw new IllegalArgumentException(); + + groupMembers.addElement(user); + return true; + } + + /** + * removes the specified member from the group. + * @param user The principal to remove from the group. + * @param true if the principal was removed false if + * the principal was not a member + */ + public boolean removeMember(Principal user) { + return groupMembers.removeElement(user); + } + + /** + * returns the enumeration of the members in the group. + */ + public Enumeration members() { + return groupMembers.elements(); + } + + /** + * This function returns true if the group passed matches + * the group represented in this interface. + * @param another The group to compare this group to. + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Group == false) { + return false; + } + Group another = (Group)obj; + return group.equals(another.toString()); + } + + // equals(Group) for compatibility + public boolean equals(Group another) { + return equals((Object)another); + } + + /** + * Prints a stringified version of the group. + */ + public String toString() { + return group; + } + + /** + * return a hashcode for the principal. + */ + public int hashCode() { + return group.hashCode(); + } + + /** + * returns true if the passed principal is a member of the group. + * @param member The principal whose membership must be checked for. + * @return true if the principal is a member of this group, + * false otherwise + */ + public boolean isMember(Principal member) { + + // + // if the member is part of the group (common case), return true. + // if not, recursively search depth first in the group looking for the + // principal. + // + if (groupMembers.contains(member)) { + return true; + } else { + Vector alreadySeen = new Vector<>(10); + return isMemberRecurse(member, alreadySeen); + } + } + + /** + * return the name of the principal. + */ + public String getName() { + return group; + } + + // + // This function is the recursive search of groups for this + // implementation of the Group. The search proceeds building up + // a vector of already seen groups. Only new groups are considered, + // thereby avoiding loops. + // + boolean isMemberRecurse(Principal member, Vector alreadySeen) { + Enumeration e = members(); + while (e.hasMoreElements()) { + boolean mem = false; + Principal p = (Principal) e.nextElement(); + + // if the member is in this collection, return true + if (p.equals(member)) { + return true; + } else if (p instanceof GroupImpl) { + // + // if not recurse if the group has not been checked already. + // Can call method in this package only if the object is an + // instance of this class. Otherwise call the method defined + // in the interface. (This can lead to a loop if a mixture of + // implementations form a loop, but we live with this improbable + // case rather than clutter the interface by forcing the + // implementation of this method.) + // + GroupImpl g = (GroupImpl) p; + alreadySeen.addElement(this); + if (!alreadySeen.contains(g)) + mem = g.isMemberRecurse(member, alreadySeen); + } else if (p instanceof Group) { + Group g = (Group) p; + if (!alreadySeen.contains(g)) + mem = g.isMember(member); + } + + if (mem) + return mem; + } + return false; + } +} diff --git a/src/sun/security/acl/OwnerImpl.java b/src/sun/security/acl/OwnerImpl.java new file mode 100644 index 00000000..6727949d --- /dev/null +++ b/src/sun/security/acl/OwnerImpl.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.util.*; +import java.security.*; +import java.security.acl.*; + +/** + * Class implementing the Owner interface. The + * initial owner principal is configured as + * part of the constructor. + * @author Satish Dharmaraj + */ +public class OwnerImpl implements Owner { + private Group ownerGroup; + + public OwnerImpl(Principal owner) { + ownerGroup = new GroupImpl("AclOwners"); + ownerGroup.addMember(owner); + } + + /** + * Adds an owner. Owners can modify ACL contents and can disassociate + * ACLs from the objects they protect in the AclConfig interface. + * The caller principal must be a part of the owners list of the ACL in + * order to invoke this method. The initial owner is configured + * at ACL construction time. + * @param caller the principal who is invoking this method. + * @param owner The owner that should be added to the owners list. + * @return true if success, false if already an owner. + * @exception NotOwnerException if the caller principal is not on + * the owners list of the Acl. + */ + public synchronized boolean addOwner(Principal caller, Principal owner) + throws NotOwnerException + { + if (!isOwner(caller)) + throw new NotOwnerException(); + + ownerGroup.addMember(owner); + return false; + } + + /** + * Delete owner. If this is the last owner in the ACL, an exception is + * raised. + * The caller principal must be a part of the owners list of the ACL in + * order to invoke this method. + * @param caller the principal who is invoking this method. + * @param owner The owner to be removed from the owners list. + * @return true if the owner is removed, false if the owner is not part + * of the owners list. + * @exception NotOwnerException if the caller principal is not on + * the owners list of the Acl. + * @exception LastOwnerException if there is only one owner left in the group, then + * deleteOwner would leave the ACL owner-less. This exception is raised in such a case. + */ + public synchronized boolean deleteOwner(Principal caller, Principal owner) + throws NotOwnerException, LastOwnerException + { + if (!isOwner(caller)) + throw new NotOwnerException(); + + Enumeration e = ownerGroup.members(); + // + // check if there is atleast 2 members left. + // + Object o = e.nextElement(); + if (e.hasMoreElements()) + return ownerGroup.removeMember(owner); + else + throw new LastOwnerException(); + + } + + /** + * returns if the given principal belongs to the owner list. + * @param owner The owner to check if part of the owners list + * @return true if the passed principal is in the owner list, false if not. + */ + public synchronized boolean isOwner(Principal owner) { + return ownerGroup.isMember(owner); + } +} diff --git a/src/sun/security/acl/PermissionImpl.java b/src/sun/security/acl/PermissionImpl.java new file mode 100644 index 00000000..5e281897 --- /dev/null +++ b/src/sun/security/acl/PermissionImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.security.acl.*; + +/** + * The PermissionImpl class implements the permission + * interface for permissions that are strings. + * @author Satish Dharmaraj + */ +public class PermissionImpl implements Permission { + + private String permission; + + /** + * Construct a permission object using a string. + * @param permission the stringified version of the permission. + */ + public PermissionImpl(String permission) { + this.permission = permission; + } + + /** + * This function returns true if the object passed matches the permission + * represented in this interface. + * @param another The Permission object to compare with. + * @return true if the Permission objects are equal, false otherwise + */ + public boolean equals(Object another) { + if (another instanceof Permission) { + Permission p = (Permission) another; + return permission.equals(p.toString()); + } else { + return false; + } + } + + /** + * Prints a stringified version of the permission. + * @return the string representation of the Permission. + */ + public String toString() { + return permission; + } + + /** + * Returns a hashcode for this PermissionImpl. + * + * @return a hashcode for this PermissionImpl. + */ + public int hashCode() { + return toString().hashCode(); + } + +} diff --git a/src/sun/security/acl/PrincipalImpl.java b/src/sun/security/acl/PrincipalImpl.java new file mode 100644 index 00000000..d5296dfe --- /dev/null +++ b/src/sun/security/acl/PrincipalImpl.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.security.*; + +/** + * This class implements the principal interface. + * + * @author Satish Dharmaraj + */ +public class PrincipalImpl implements Principal { + + private String user; + + /** + * Construct a principal from a string user name. + * @param user The string form of the principal name. + */ + public PrincipalImpl(String user) { + this.user = user; + } + + /** + * This function returns true if the object passed matches + * the principal represented in this implementation + * @param another the Principal to compare with. + * @return true if the Principal passed is the same as that + * encapsulated in this object, false otherwise + */ + public boolean equals(Object another) { + if (another instanceof PrincipalImpl) { + PrincipalImpl p = (PrincipalImpl) another; + return user.equals(p.toString()); + } else + return false; + } + + /** + * Prints a stringified version of the principal. + */ + public String toString() { + return user; + } + + /** + * return a hashcode for the principal. + */ + public int hashCode() { + return user.hashCode(); + } + + /** + * return the name of the principal. + */ + public String getName() { + return user; + } + +} diff --git a/src/sun/security/acl/WorldGroupImpl.java b/src/sun/security/acl/WorldGroupImpl.java new file mode 100644 index 00000000..a1000d3c --- /dev/null +++ b/src/sun/security/acl/WorldGroupImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1996, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.acl; + +import java.security.*; + +/** + * This class implements a group of principals. + * @author Satish Dharmaraj + */ +public class WorldGroupImpl extends GroupImpl { + + public WorldGroupImpl(String s) { + super(s); + } + + /** + * returns true for all passed principals + * @param member The principal whose membership must be checked in this Group. + * @return true always since this is the "world" group. + */ + public boolean isMember(Principal member) { + return true; + } +} diff --git a/src/sun/security/action/GetBooleanAction.java b/src/sun/security/action/GetBooleanAction.java new file mode 100644 index 00000000..e3a5d199 --- /dev/null +++ b/src/sun/security/action/GetBooleanAction.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +/** + * A convenience class for retrieving the boolean value of a system property + * as a privileged action. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + * + *

    The following code retrieves the boolean value of the system + * property named "prop" as a privileged action:

    + * + *

    + * boolean b = java.security.AccessController.doPrivileged
    + *              (new GetBooleanAction("prop")).booleanValue();
    + * 
    + * + * @author Roland Schemers + * @see java.security.PrivilegedAction + * @see java.security.AccessController + * @since 1.2 + */ + +public class GetBooleanAction + implements java.security.PrivilegedAction { + private String theProp; + + /** + * Constructor that takes the name of the system property whose boolean + * value needs to be determined. + * + * @param theProp the name of the system property. + */ + public GetBooleanAction(String theProp) { + this.theProp = theProp; + } + + /** + * Determines the boolean value of the system property whose name was + * specified in the constructor. + * + * @return the Boolean value of the system property. + */ + public Boolean run() { + return Boolean.getBoolean(theProp); + } +} diff --git a/src/sun/security/action/GetBooleanSecurityPropertyAction.java b/src/sun/security/action/GetBooleanSecurityPropertyAction.java new file mode 100644 index 00000000..a96d1e05 --- /dev/null +++ b/src/sun/security/action/GetBooleanSecurityPropertyAction.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +import java.security.Security; + +/** + * A convenience class for retrieving the boolean value of a security property + * as a privileged action. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + * + *

    The following code retrieves the boolean value of the security + * property named "prop" as a privileged action:

    + * + *

    + * boolean b = java.security.AccessController.doPrivileged
    + *              (new GetBooleanSecurityPropertyAction("prop")).booleanValue();
    + * 
    + * + */ +public class GetBooleanSecurityPropertyAction + implements java.security.PrivilegedAction { + private String theProp; + + /** + * Constructor that takes the name of the security property whose boolean + * value needs to be determined. + * + * @param theProp the name of the security property + */ + public GetBooleanSecurityPropertyAction(String theProp) { + this.theProp = theProp; + } + + /** + * Determines the boolean value of the security property whose name was + * specified in the constructor. + * + * @return the Boolean value of the security property. + */ + public Boolean run() { + boolean b = false; + try { + String value = Security.getProperty(theProp); + b = (value != null) && value.equalsIgnoreCase("true"); + } catch (NullPointerException e) {} + return b; + } +} diff --git a/src/sun/security/action/GetIntegerAction.java b/src/sun/security/action/GetIntegerAction.java new file mode 100644 index 00000000..edf9542d --- /dev/null +++ b/src/sun/security/action/GetIntegerAction.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +/** + * A convenience class for retrieving the integer value of a system property + * as a privileged action. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + * + *

    The following code retrieves the integer value of the system + * property named "prop" as a privileged action. Since it does + * not pass a default value to be used in case the property + * "prop" is not defined, it has to check the result for + * null:

    + * + *

    + * Integer tmp = java.security.AccessController.doPrivileged
    + *     (new sun.security.action.GetIntegerAction("prop"));
    + * int i;
    + * if (tmp != null) {
    + *     i = tmp.intValue();
    + * }
    + * 
    + * + *

    The following code retrieves the integer value of the system + * property named "prop" as a privileged action, and also passes + * a default value to be used in case the property "prop" is not + * defined:

    + * + *

    + * int i = ((Integer)java.security.AccessController.doPrivileged(
    + *                         new GetIntegerAction("prop", 3))).intValue();
    + * 
    + * + * @author Roland Schemers + * @see java.security.PrivilegedAction + * @see java.security.AccessController + * @since 1.2 + */ + +public class GetIntegerAction + implements java.security.PrivilegedAction { + private String theProp; + private int defaultVal; + private boolean defaultSet = false; + + /** + * Constructor that takes the name of the system property whose integer + * value needs to be determined. + * + * @param theProp the name of the system property. + */ + public GetIntegerAction(String theProp) { + this.theProp = theProp; + } + + /** + * Constructor that takes the name of the system property and the default + * value of that property. + * + * @param theProp the name of the system property. + * @param defaulVal the default value. + */ + public GetIntegerAction(String theProp, int defaultVal) { + this.theProp = theProp; + this.defaultVal = defaultVal; + this.defaultSet = true; + } + + /** + * Determines the integer value of the system property whose name was + * specified in the constructor. + * + *

    If there is no property of the specified name, or if the property + * does not have the correct numeric format, then an Integer + * object representing the default value that was specified in the + * constructor is returned, or null if no default value was + * specified. + * + * @return the Integer value of the property. + */ + public Integer run() { + Integer value = Integer.getInteger(theProp); + if ((value == null) && defaultSet) + return new Integer(defaultVal); + return value; + } +} diff --git a/src/sun/security/action/GetLongAction.java b/src/sun/security/action/GetLongAction.java new file mode 100644 index 00000000..4b99f02a --- /dev/null +++ b/src/sun/security/action/GetLongAction.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +/** + * A convenience class for retrieving the Long value of a system + * property as a privileged action. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + * + *

    The following code retrieves the Long value of the system + * property named "prop" as a privileged action. Since it does + * not pass a default value to be used in case the property + * "prop" is not defined, it has to check the result for + * null:

    + * + *

    + * Long tmp = java.security.AccessController.doPrivileged
    + *     (new sun.security.action.GetLongAction("prop"));
    + * long l;
    + * if (tmp != null) {
    + *     l = tmp.longValue();
    + * }
    + * 
    + * + *

    The following code retrieves the Long value of the system + * property named "prop" as a privileged action, and also passes + * a default value to be used in case the property "prop" is not + * defined:

    + * + *

    + * long l = java.security.AccessController.doPrivileged
    + *      (new GetLongAction("prop")).longValue();
    + * 
    + * + * @author Roland Schemers + * @see java.security.PrivilegedAction + * @see java.security.AccessController + * @since 1.2 + */ + +public class GetLongAction implements java.security.PrivilegedAction { + private String theProp; + private long defaultVal; + private boolean defaultSet = false; + + /** + * Constructor that takes the name of the system property whose + * Long value needs to be determined. + * + * @param theProp the name of the system property. + */ + public GetLongAction(String theProp) { + this.theProp = theProp; + } + + /** + * Constructor that takes the name of the system property and the default + * value of that property. + * + * @param theProp the name of the system property. + * @param defaulVal the default value. + */ + public GetLongAction(String theProp, long defaultVal) { + this.theProp = theProp; + this.defaultVal = defaultVal; + this.defaultSet = true; + } + + /** + * Determines the Long value of the system property whose + * name was specified in the constructor. + * + *

    If there is no property of the specified name, or if the property + * does not have the correct numeric format, then a Long + * object representing the default value that was specified in the + * constructor is returned, or null if no default value was + * specified. + * + * @return the Long value of the property. + */ + public Long run() { + Long value = Long.getLong(theProp); + if ((value == null) && defaultSet) + return new Long(defaultVal); + return value; + } +} diff --git a/src/sun/security/action/GetPropertyAction.java b/src/sun/security/action/GetPropertyAction.java new file mode 100644 index 00000000..82b94cf9 --- /dev/null +++ b/src/sun/security/action/GetPropertyAction.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Properties; + +/** + * A convenience class for retrieving the string value of a system + * property as a privileged action. + * + *

    An instance of this class can be used as the argument of + * AccessController.doPrivileged. + * + *

    The following code retrieves the value of the system + * property named "prop" as a privileged action: + * + *

    + * String s = java.security.AccessController.doPrivileged
    + *                      (new GetPropertyAction("prop"));
    + * 
    + * + * @author Roland Schemers + * @see PrivilegedAction + * @see AccessController + * @since 1.2 + */ + +public class GetPropertyAction implements PrivilegedAction { + private String theProp; + private String defaultVal; + + /** + * Constructor that takes the name of the system property whose + * string value needs to be determined. + * + * @param theProp the name of the system property. + */ + public GetPropertyAction(String theProp) { + this.theProp = theProp; + } + + /** + * Constructor that takes the name of the system property and the default + * value of that property. + * + * @param theProp the name of the system property. + * @param defaultVal the default value. + */ + public GetPropertyAction(String theProp, String defaultVal) { + this.theProp = theProp; + this.defaultVal = defaultVal; + } + + /** + * Determines the string value of the system property whose + * name was specified in the constructor. + * + * @return the string value of the system property, + * or the default value if there is no property with that key. + */ + public String run() { + String value = System.getProperty(theProp); + return (value == null) ? defaultVal : value; + } + + /** + * Convenience method to get a property without going through doPrivileged + * if no security manager is present. This is unsafe for inclusion in a + * public API but allowable here since this class is now encapsulated. + * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * + * @param theProp the name of the system property. + */ + public static String privilegedGetProperty(String theProp) { + if (System.getSecurityManager() == null) { + return System.getProperty(theProp); + } else { + return AccessController.doPrivileged( + new GetPropertyAction(theProp)); + } + } + + /** + * Convenience method to get a property without going through doPrivileged + * if no security manager is present. This is unsafe for inclusion in a + * public API but allowable here since this class is now encapsulated. + * + * Note that this method performs a privileged action using caller-provided + * inputs. The caller of this method should take care to ensure that the + * inputs are not tainted and the returned property is not made accessible + * to untrusted code if it contains sensitive information. + * + * @param theProp the name of the system property. + * @param defaultVal the default value. + */ + public static String privilegedGetProperty(String theProp, + String defaultVal) { + if (System.getSecurityManager() == null) { + return System.getProperty(theProp, defaultVal); + } else { + return AccessController.doPrivileged( + new GetPropertyAction(theProp, defaultVal)); + } + } + + /** + * Convenience method to call System.getProperties without + * having to go through doPrivileged if no security manager is present. + * This is unsafe for inclusion in a public API but allowable here since + * this class is now encapsulated. + * + * Note that this method performs a privileged action, and callers of + * this method should take care to ensure that the returned properties + * are not made accessible to untrusted code since it may contain + * sensitive information. + */ + public static Properties privilegedGetProperties() { + if (System.getSecurityManager() == null) { + return System.getProperties(); + } else { + return AccessController.doPrivileged( + new PrivilegedAction() { + public Properties run() { + return System.getProperties(); + } + } + ); + } + } +} diff --git a/src/sun/security/action/OpenFileInputStreamAction.java b/src/sun/security/action/OpenFileInputStreamAction.java new file mode 100644 index 00000000..4692a22e --- /dev/null +++ b/src/sun/security/action/OpenFileInputStreamAction.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2002, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +import java.io.*; + +import java.security.PrivilegedExceptionAction; + +/** + * A convenience class for opening a FileInputStream as a privileged action. + * + * @author Andreas Sterbenz + */ +public class OpenFileInputStreamAction + implements PrivilegedExceptionAction { + + private final File file; + + public OpenFileInputStreamAction(File file) { + this.file = file; + } + + public OpenFileInputStreamAction(String filename) { + this.file = new File(filename); + } + + public FileInputStream run() throws Exception { + return new FileInputStream(file); + } +} diff --git a/src/sun/security/action/PutAllAction.java b/src/sun/security/action/PutAllAction.java new file mode 100644 index 00000000..d8b17993 --- /dev/null +++ b/src/sun/security/action/PutAllAction.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.action; + +import java.util.Map; + +import java.security.Provider; +import java.security.PrivilegedAction; + +/** + * A convenience PrivilegedAction class for setting the properties of + * a provider. See the SunRsaSign provider for a usage example. + * + * @see sun.security.rsa.SunRsaSign + * @author Andreas Sterbenz + * @since 1.5 + */ +public class PutAllAction implements PrivilegedAction { + + private final Provider provider; + private final Map map; + + public PutAllAction(Provider provider, Map map) { + this.provider = provider; + this.map = map; + } + + public Void run() { + provider.putAll(map); + return null; + } + +} diff --git a/src/sun/security/ec/CurveDB.java b/src/sun/security/ec/CurveDB.java new file mode 100644 index 00000000..a3b170ca --- /dev/null +++ b/src/sun/security/ec/CurveDB.java @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.math.BigInteger; + +import java.security.spec.*; + +import java.util.*; +import java.util.regex.Pattern; +import sun.security.util.ECUtil; + +/** + * Repository for well-known Elliptic Curve parameters. It is used by both + * the SunPKCS11 and SunJSSE code. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public class CurveDB { + private final static int P = 1; // prime curve + private final static int B = 2; // binary curve + private final static int PD = 5; // prime curve, mark as default + private final static int BD = 6; // binary curve, mark as default + + private static final Map oidMap = + new LinkedHashMap(); + private static final Map nameMap = + new HashMap(); + private static final Map lengthMap = + new HashMap(); + + private static Collection specCollection; + + static final String SPLIT_PATTERN = ",|\\[|\\]"; + + // Used by SunECEntries + static CollectiongetSupportedCurves() { + return specCollection; + } + + // Return a NamedCurve for the specified OID/name or null if unknown. + static NamedCurve lookup(String name) { + NamedCurve spec = oidMap.get(name); + if (spec != null) { + return spec; + } + + return nameMap.get(name); + } + + // Return EC parameters for the specified field size. If there are known + // NIST recommended parameters for the given length, they are returned. + // Otherwise, if there are multiple matches for the given size, an + // arbitrary one is returns. + // If no parameters are known, the method returns null. + // NOTE that this method returns both prime and binary curves. + static NamedCurve lookup(int length) { + return lengthMap.get(length); + } + + // Convert the given ECParameterSpec object to a NamedCurve object. + // If params does not represent a known named curve, return null. + static NamedCurve lookup(ECParameterSpec params) { + if ((params instanceof NamedCurve) || (params == null)) { + return (NamedCurve)params; + } + + // This is a hack to allow SunJSSE to work with 3rd party crypto + // providers for ECC and not just SunPKCS11. + // This can go away once we decide how to expose curve names in the + // public API. + // Note that it assumes that the 3rd party provider encodes named + // curves using the short form, not explicitly. If it did that, then + // the SunJSSE TLS ECC extensions are wrong, which could lead to + // interoperability problems. + int fieldSize = params.getCurve().getField().getFieldSize(); + for (NamedCurve namedCurve : specCollection) { + // ECParameterSpec does not define equals, so check all the + // components ourselves. + // Quick field size check first + if (namedCurve.getCurve().getField().getFieldSize() != fieldSize) { + continue; + } + if (ECUtil.equals(namedCurve, params)) { + // everything matches our named curve, return it + return namedCurve; + } + } + // no match found + return null; + } + + private static BigInteger bi(String s) { + return new BigInteger(s, 16); + } + + private static void add(String name, String soid, int type, String sfield, + String a, String b, String x, String y, String n, int h, + Pattern nameSplitPattern) { + BigInteger p = bi(sfield); + ECField field; + if ((type == P) || (type == PD)) { + field = new ECFieldFp(p); + } else if ((type == B) || (type == BD)) { + field = new ECFieldF2m(p.bitLength() - 1, p); + } else { + throw new RuntimeException("Invalid type: " + type); + } + + EllipticCurve curve = new EllipticCurve(field, bi(a), bi(b)); + ECPoint g = new ECPoint(bi(x), bi(y)); + + NamedCurve params = new NamedCurve(name, soid, curve, g, bi(n), h); + if (oidMap.put(soid, params) != null) { + throw new RuntimeException("Duplication oid: " + soid); + } + + String[] commonNames = nameSplitPattern.split(name); + for (String commonName : commonNames) { + if (nameMap.put(commonName.trim(), params) != null) { + throw new RuntimeException("Duplication name: " + commonName); + } + } + + int len = field.getFieldSize(); + if ((type == PD) || (type == BD) || (lengthMap.get(len) == null)) { + // add entry if none present for this field size or if + // the curve is marked as a default curve. + lengthMap.put(len, params); + } + } + + static { + Pattern nameSplitPattern = Pattern.compile(SPLIT_PATTERN); + + /* SEC2 prime curves */ + add("secp112r1", "1.3.132.0.6", P, + "DB7C2ABF62E35E668076BEAD208B", + "DB7C2ABF62E35E668076BEAD2088", + "659EF8BA043916EEDE8911702B22", + "09487239995A5EE76B55F9C2F098", + "A89CE5AF8724C0A23E0E0FF77500", + "DB7C2ABF62E35E7628DFAC6561C5", + 1, nameSplitPattern); + + add("secp112r2", "1.3.132.0.7", P, + "DB7C2ABF62E35E668076BEAD208B", + "6127C24C05F38A0AAAF65C0EF02C", + "51DEF1815DB5ED74FCC34C85D709", + "4BA30AB5E892B4E1649DD0928643", + "adcd46f5882e3747def36e956e97", + "36DF0AAFD8B8D7597CA10520D04B", + 4, nameSplitPattern); + + add("secp128r1", "1.3.132.0.28", P, + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", + "E87579C11079F43DD824993C2CEE5ED3", + "161FF7528B899B2D0C28607CA52C5B86", + "CF5AC8395BAFEB13C02DA292DDED7A83", + "FFFFFFFE0000000075A30D1B9038A115", + 1, nameSplitPattern); + + add("secp128r2", "1.3.132.0.29", P, + "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", + "D6031998D1B3BBFEBF59CC9BBFF9AEE1", + "5EEEFCA380D02919DC2C6558BB6D8A5D", + "7B6AA5D85E572983E6FB32A7CDEBC140", + "27B6916A894D3AEE7106FE805FC34B44", + "3FFFFFFF7FFFFFFFBE0024720613B5A3", + 4, nameSplitPattern); + + add("secp160k1", "1.3.132.0.9", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", + "0000000000000000000000000000000000000000", + "0000000000000000000000000000000000000007", + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", + "938CF935318FDCED6BC28286531733C3F03C4FEE", + "0100000000000000000001B8FA16DFAB9ACA16B6B3", + 1, nameSplitPattern); + + add("secp160r1", "1.3.132.0.8", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", + "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", + "4A96B5688EF573284664698968C38BB913CBFC82", + "23A628553168947D59DCC912042351377AC5FB32", + "0100000000000000000001F4C8F927AED3CA752257", + 1, nameSplitPattern); + + add("secp160r2", "1.3.132.0.30", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC70", + "B4E134D3FB59EB8BAB57274904664D5AF50388BA", + "52DCB034293A117E1F4FF11B30F7199D3144CE6D", + "FEAFFEF2E331F296E071FA0DF9982CFEA7D43F2E", + "0100000000000000000000351EE786A818F3A1A16B", + 1, nameSplitPattern); + + add("secp192k1", "1.3.132.0.31", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", + "000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000003", + "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", + "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", + "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", + 1, nameSplitPattern); + + add("secp192r1 [NIST P-192, X9.62 prime192v1]", "1.2.840.10045.3.1.1", PD, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", + "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", + "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", + 1, nameSplitPattern); + + add("secp224k1", "1.3.132.0.32", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", + "00000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000005", + "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", + "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", + "010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7", + 1, nameSplitPattern); + + add("secp224r1 [NIST P-224]", "1.3.132.0.33", PD, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", + "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", + "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", + "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", + 1, nameSplitPattern); + + add("secp256k1", "1.3.132.0.10", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000007", + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", + 1, nameSplitPattern); + + add("secp256r1 [NIST P-256, X9.62 prime256v1]", "1.2.840.10045.3.1.7", PD, + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", + 1, nameSplitPattern); + + add("secp384r1 [NIST P-384]", "1.3.132.0.34", PD, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", + 1, nameSplitPattern); + + add("secp521r1 [NIST P-521]", "1.3.132.0.35", PD, + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", + "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + 1, nameSplitPattern); + + /* ANSI X9.62 prime curves */ + add("X9.62 prime192v2", "1.2.840.10045.3.1.2", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "CC22D6DFB95C6B25E49C0D6364A4E5980C393AA21668D953", + "EEA2BAE7E1497842F2DE7769CFE9C989C072AD696F48034A", + "6574D11D69B6EC7A672BB82A083DF2F2B0847DE970B2DE15", + "FFFFFFFFFFFFFFFFFFFFFFFE5FB1A724DC80418648D8DD31", + 1, nameSplitPattern); + + add("X9.62 prime192v3", "1.2.840.10045.3.1.3", P, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", + "22123DC2395A05CAA7423DAECCC94760A7D462256BD56916", + "7D29778100C65A1DA1783716588DCE2B8B4AEE8E228F1896", + "38A90F22637337334B49DCB66A6DC8F9978ACA7648A943B0", + "FFFFFFFFFFFFFFFFFFFFFFFF7A62D031C83F4294F640EC13", + 1, nameSplitPattern); + + add("X9.62 prime239v1", "1.2.840.10045.3.1.4", P, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "6B016C3BDCF18941D0D654921475CA71A9DB2FB27D1D37796185C2942C0A", + "0FFA963CDCA8816CCC33B8642BEDF905C3D358573D3F27FBBD3B3CB9AAAF", + "7DEBE8E4E90A5DAE6E4054CA530BA04654B36818CE226B39FCCB7B02F1AE", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF9E5E9A9F5D9071FBD1522688909D0B", + 1, nameSplitPattern); + + add("X9.62 prime239v2", "1.2.840.10045.3.1.5", P, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "617FAB6832576CBBFED50D99F0249C3FEE58B94BA0038C7AE84C8C832F2C", + "38AF09D98727705120C921BB5E9E26296A3CDCF2F35757A0EAFD87B830E7", + "5B0125E4DBEA0EC7206DA0FC01D9B081329FB555DE6EF460237DFF8BE4BA", + "7FFFFFFFFFFFFFFFFFFFFFFF800000CFA7E8594377D414C03821BC582063", + 1, nameSplitPattern); + + add("X9.62 prime239v3", "1.2.840.10045.3.1.6", P, + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFF", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFF8000000000007FFFFFFFFFFC", + "255705FA2A306654B1F4CB03D6A750A30C250102D4988717D9BA15AB6D3E", + "6768AE8E18BB92CFCF005C949AA2C6D94853D0E660BBF854B1C9505FE95A", + "1607E6898F390C06BC1D552BAD226F3B6FCFE48B6E818499AF18E3ED6CF3", + "7FFFFFFFFFFFFFFFFFFFFFFF7FFFFF975DEB41B3A6057C3C432146526551", + 1, nameSplitPattern); + + /* SEC2 binary curves */ + add("sect113r1", "1.3.132.0.4", B, + "020000000000000000000000000201", + "003088250CA6E7C7FE649CE85820F7", + "00E8BEE4D3E2260744188BE0E9C723", + "009D73616F35F4AB1407D73562C10F", + "00A52830277958EE84D1315ED31886", + "0100000000000000D9CCEC8A39E56F", + 2, nameSplitPattern); + + add("sect113r2", "1.3.132.0.5", B, + "020000000000000000000000000201", + "00689918DBEC7E5A0DD6DFC0AA55C7", + "0095E9A9EC9B297BD4BF36E059184F", + "01A57A6A7B26CA5EF52FCDB8164797", + "00B3ADC94ED1FE674C06E695BABA1D", + "010000000000000108789B2496AF93", + 2, nameSplitPattern); + + add("sect131r1", "1.3.132.0.22", B, + "080000000000000000000000000000010D", + "07A11B09A76B562144418FF3FF8C2570B8", + "0217C05610884B63B9C6C7291678F9D341", + "0081BAF91FDF9833C40F9C181343638399", + "078C6E7EA38C001F73C8134B1B4EF9E150", + "0400000000000000023123953A9464B54D", + 2, nameSplitPattern); + + add("sect131r2", "1.3.132.0.23", B, + "080000000000000000000000000000010D", + "03E5A88919D7CAFCBF415F07C2176573B2", + "04B8266A46C55657AC734CE38F018F2192", + "0356DCD8F2F95031AD652D23951BB366A8", + "0648F06D867940A5366D9E265DE9EB240F", + "0400000000000000016954A233049BA98F", + 2, nameSplitPattern); + + add("sect163k1 [NIST K-163]", "1.3.132.0.1", BD, + "0800000000000000000000000000000000000000C9", + "000000000000000000000000000000000000000001", + "000000000000000000000000000000000000000001", + "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8", + "0289070FB05D38FF58321F2E800536D538CCDAA3D9", + "04000000000000000000020108A2E0CC0D99F8A5EF", + 2, nameSplitPattern); + + add("sect163r1", "1.3.132.0.2", B, + "0800000000000000000000000000000000000000C9", + "07B6882CAAEFA84F9554FF8428BD88E246D2782AE2", + "0713612DCDDCB40AAB946BDA29CA91F73AF958AFD9", + "0369979697AB43897789566789567F787A7876A654", + "00435EDB42EFAFB2989D51FEFCE3C80988F41FF883", + "03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B", + 2, nameSplitPattern); + + add("sect163r2 [NIST B-163]", "1.3.132.0.15", BD, + "0800000000000000000000000000000000000000C9", + "000000000000000000000000000000000000000001", + "020A601907B8C953CA1481EB10512F78744A3205FD", + "03F0EBA16286A2D57EA0991168D4994637E8343E36", + "00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1", + "040000000000000000000292FE77E70C12A4234C33", + 2, nameSplitPattern); + + add("sect193r1", "1.3.132.0.24", B, + "02000000000000000000000000000000000000000000008001", + "0017858FEB7A98975169E171F77B4087DE098AC8A911DF7B01", + "00FDFB49BFE6C3A89FACADAA7A1E5BBC7CC1C2E5D831478814", + "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1", + "0025E399F2903712CCF3EA9E3A1AD17FB0B3201B6AF7CE1B05", + "01000000000000000000000000C7F34A778F443ACC920EBA49", + 2, nameSplitPattern); + + add("sect193r2", "1.3.132.0.25", B, + "02000000000000000000000000000000000000000000008001", + "0163F35A5137C2CE3EA6ED8667190B0BC43ECD69977702709B", + "00C9BB9E8927D4D64C377E2AB2856A5B16E3EFB7F61D4316AE", + "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F", + "01CE94335607C304AC29E7DEFBD9CA01F596F927224CDECF6C", + "010000000000000000000000015AAB561B005413CCD4EE99D5", + 2, nameSplitPattern); + + add("sect233k1 [NIST K-233]", "1.3.132.0.26", BD, + "020000000000000000000000000000000000000004000000000000000001", + "000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000001", + "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126", + "01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3", + "008000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF", + 4, nameSplitPattern); + + add("sect233r1 [NIST B-233]", "1.3.132.0.27", B, + "020000000000000000000000000000000000000004000000000000000001", + "000000000000000000000000000000000000000000000000000000000001", + "0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD", + "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B", + "01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052", + "01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7", + 2, nameSplitPattern); + + add("sect239k1", "1.3.132.0.3", B, + "800000000000000000004000000000000000000000000000000000000001", + "000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000001", + "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC", + "76310804F12E549BDB011C103089E73510ACB275FC312A5DC6B76553F0CA", + "2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5", + 4, nameSplitPattern); + + add("sect283k1 [NIST K-283]", "1.3.132.0.16", BD, + "0800000000000000000000000000000000000000000000000000000000000000000010A1", + "000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000001", + "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836", + "01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61", + 4, nameSplitPattern); + + add("sect283r1 [NIST B-283]", "1.3.132.0.17", B, + "0800000000000000000000000000000000000000000000000000000000000000000010A1", + "000000000000000000000000000000000000000000000000000000000000000000000001", + "027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5", + "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053", + "03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4", + "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307", + 2, nameSplitPattern); + + add("sect409k1 [NIST K-409]", "1.3.132.0.36", BD, + "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746", + "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B", + "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF", + 4, nameSplitPattern); + + add("sect409r1 [NIST B-409]", "1.3.132.0.37", B, + "02000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000001", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F", + "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7", + "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706", + "010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173", + 2, nameSplitPattern); + + add("sect571k1 [NIST K-571]", "1.3.132.0.38", BD, + "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972", + "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3", + "020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001", + 4, nameSplitPattern); + + add("sect571r1 [NIST B-571]", "1.3.132.0.39", B, + "080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000425", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A", + "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19", + "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B", + "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47", + 2, nameSplitPattern); + + /* ANSI X9.62 binary curves */ + add("X9.62 c2tnb191v1", "1.2.840.10045.3.0.5", B, + "800000000000000000000000000000000000000000000201", + "2866537B676752636A68F56554E12640276B649EF7526267", + "2E45EF571F00786F67B0081B9495A3D95462F5DE0AA185EC", + "36B3DAF8A23206F9C4F299D7B21A9C369137F2C84AE1AA0D", + "765BE73433B3F95E332932E70EA245CA2418EA0EF98018FB", + "40000000000000000000000004A20E90C39067C893BBB9A5", + 2, nameSplitPattern); + + add("X9.62 c2tnb191v2", "1.2.840.10045.3.0.6", B, + "800000000000000000000000000000000000000000000201", + "401028774D7777C7B7666D1366EA432071274F89FF01E718", + "0620048D28BCBD03B6249C99182B7C8CD19700C362C46A01", + "3809B2B7CC1B28CC5A87926AAD83FD28789E81E2C9E3BF10", + "17434386626D14F3DBF01760D9213A3E1CF37AEC437D668A", + "20000000000000000000000050508CB89F652824E06B8173", + 4, nameSplitPattern); + + add("X9.62 c2tnb191v3", "1.2.840.10045.3.0.7", B, + "800000000000000000000000000000000000000000000201", + "6C01074756099122221056911C77D77E77A777E7E7E77FCB", + "71FE1AF926CF847989EFEF8DB459F66394D90F32AD3F15E8", + "375D4CE24FDE434489DE8746E71786015009E66E38A926DD", + "545A39176196575D985999366E6AD34CE0A77CD7127B06BE", + "155555555555555555555555610C0B196812BFB6288A3EA3", + 6, nameSplitPattern); + + add("X9.62 c2tnb239v1", "1.2.840.10045.3.0.11", B, + "800000000000000000000000000000000000000000000000001000000001", + "32010857077C5431123A46B808906756F543423E8D27877578125778AC76", + "790408F2EEDAF392B012EDEFB3392F30F4327C0CA3F31FC383C422AA8C16", + "57927098FA932E7C0A96D3FD5B706EF7E5F5C156E16B7E7C86038552E91D", + "61D8EE5077C33FECF6F1A16B268DE469C3C7744EA9A971649FC7A9616305", + "2000000000000000000000000000000F4D42FFE1492A4993F1CAD666E447", + 4, nameSplitPattern); + + add("X9.62 c2tnb239v2", "1.2.840.10045.3.0.12", B, + "800000000000000000000000000000000000000000000000001000000001", + "4230017757A767FAE42398569B746325D45313AF0766266479B75654E65F", + "5037EA654196CFF0CD82B2C14A2FCF2E3FF8775285B545722F03EACDB74B", + "28F9D04E900069C8DC47A08534FE76D2B900B7D7EF31F5709F200C4CA205", + "5667334C45AFF3B5A03BAD9DD75E2C71A99362567D5453F7FA6E227EC833", + "1555555555555555555555555555553C6F2885259C31E3FCDF154624522D", + 6, nameSplitPattern); + + add("X9.62 c2tnb239v3", "1.2.840.10045.3.0.13", B, + "800000000000000000000000000000000000000000000000001000000001", + "01238774666A67766D6676F778E676B66999176666E687666D8766C66A9F", + "6A941977BA9F6A435199ACFC51067ED587F519C5ECB541B8E44111DE1D40", + "70F6E9D04D289C4E89913CE3530BFDE903977D42B146D539BF1BDE4E9C92", + "2E5A0EAF6E5E1305B9004DCE5C0ED7FE59A35608F33837C816D80B79F461", + "0CCCCCCCCCCCCCCCCCCCCCCCCCCCCCAC4912D2D9DF903EF9888B8A0E4CFF", + 0xA, nameSplitPattern); + + add("X9.62 c2tnb359v1", "1.2.840.10045.3.0.18", B, + "800000000000000000000000000000000000000000000000000000000000000000000000100000000000000001", + "5667676A654B20754F356EA92017D946567C46675556F19556A04616B567D223A5E05656FB549016A96656A557", + "2472E2D0197C49363F1FE7F5B6DB075D52B6947D135D8CA445805D39BC345626089687742B6329E70680231988", + "3C258EF3047767E7EDE0F1FDAA79DAEE3841366A132E163ACED4ED2401DF9C6BDCDE98E8E707C07A2239B1B097", + "53D7E08529547048121E9C95F3791DD804963948F34FAE7BF44EA82365DC7868FE57E4AE2DE211305A407104BD", + "01AF286BCA1AF286BCA1AF286BCA1AF286BCA1AF286BC9FB8F6B85C556892C20A7EB964FE7719E74F490758D3B", + 0x4C, nameSplitPattern); + + add("X9.62 c2tnb431r1", "1.2.840.10045.3.0.20", B, + "800000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000001", + "1A827EF00DD6FC0E234CAF046C6A5D8A85395B236CC4AD2CF32A0CADBDC9DDF620B0EB9906D0957F6C6FEACD615468DF104DE296CD8F", + "10D9B4A3D9047D8B154359ABFB1B7F5485B04CEB868237DDC9DEDA982A679A5A919B626D4E50A8DD731B107A9962381FB5D807BF2618", + "120FC05D3C67A99DE161D2F4092622FECA701BE4F50F4758714E8A87BBF2A658EF8C21E7C5EFE965361F6C2999C0C247B0DBD70CE6B7", + "20D0AF8903A96F8D5FA2C255745D3C451B302C9346D9B7E485E7BCE41F6B591F3E8F6ADDCBB0BC4C2F947A7DE1A89B625D6A598B3760", + "0340340340340340340340340340340340340340340340340340340323C313FAB50589703B5EC68D3587FEC60D161CC149C1AD4A91", + 0x2760, nameSplitPattern); + + /* ANSI X9.62 binary curves from the 1998 standard but forbidden + * in the 2005 version of the standard. + * We don't register them but leave them here for the time being in + * case we need to support them after all. + */ +/* + add("X9.62 c2pnb163v1", "1.2.840.10045.3.0.1", B, + "080000000000000000000000000000000000000107", + "072546B5435234A422E0789675F432C89435DE5242", + "00C9517D06D5240D3CFF38C74B20B6CD4D6F9DD4D9", + "07AF69989546103D79329FCC3D74880F33BBE803CB", + "01EC23211B5966ADEA1D3F87F7EA5848AEF0B7CA9F", + "0400000000000000000001E60FC8821CC74DAEAFC1", + 2, nameSplitPattern); + + add("X9.62 c2pnb163v2", "1.2.840.10045.3.0.2", B, + "080000000000000000000000000000000000000107", + "0108B39E77C4B108BED981ED0E890E117C511CF072", + "0667ACEB38AF4E488C407433FFAE4F1C811638DF20", + "0024266E4EB5106D0A964D92C4860E2671DB9B6CC5", + "079F684DDF6684C5CD258B3890021B2386DFD19FC5", + "03FFFFFFFFFFFFFFFFFFFDF64DE1151ADBB78F10A7", + 2, nameSplitPattern); + + add("X9.62 c2pnb163v3", "1.2.840.10045.3.0.3", B, + "080000000000000000000000000000000000000107", + "07A526C63D3E25A256A007699F5447E32AE456B50E", + "03F7061798EB99E238FD6F1BF95B48FEEB4854252B", + "02F9F87B7C574D0BDECF8A22E6524775F98CDEBDCB", + "05B935590C155E17EA48EB3FF3718B893DF59A05D0", + "03FFFFFFFFFFFFFFFFFFFE1AEE140F110AFF961309", + 2, nameSplitPattern); + + add("X9.62 c2pnb176w1", "1.2.840.10045.3.0.4", B, + "0100000000000000000000000000000000080000000007", + "E4E6DB2995065C407D9D39B8D0967B96704BA8E9C90B", + "5DDA470ABE6414DE8EC133AE28E9BBD7FCEC0AE0FFF2", + "8D16C2866798B600F9F08BB4A8E860F3298CE04A5798", + "6FA4539C2DADDDD6BAB5167D61B436E1D92BB16A562C", + "00010092537397ECA4F6145799D62B0A19CE06FE26AD", + 0xFF6E, nameSplitPattern); + + add("X9.62 c2pnb208w1", "1.2.840.10045.3.0.10", B, + "010000000000000000000000000000000800000000000000000007", + "0000000000000000000000000000000000000000000000000000", + "C8619ED45A62E6212E1160349E2BFA844439FAFC2A3FD1638F9E", + "89FDFBE4ABE193DF9559ECF07AC0CE78554E2784EB8C1ED1A57A", + "0F55B51A06E78E9AC38A035FF520D8B01781BEB1A6BB08617DE3", + "000101BAF95C9723C57B6C21DA2EFF2D5ED588BDD5717E212F9D", + 0xFE48, nameSplitPattern); + + add("X9.62 c2pnb272w1", "1.2.840.10045.3.0.16", B, + "010000000000000000000000000000000000000000000000000000010000000000000B", + "91A091F03B5FBA4AB2CCF49C4EDD220FB028712D42BE752B2C40094DBACDB586FB20", + "7167EFC92BB2E3CE7C8AAAFF34E12A9C557003D7C73A6FAF003F99F6CC8482E540F7", + "6108BABB2CEEBCF787058A056CBE0CFE622D7723A289E08A07AE13EF0D10D171DD8D", + "10C7695716851EEF6BA7F6872E6142FBD241B830FF5EFCACECCAB05E02005DDE9D23", + "000100FAF51354E0E39E4892DF6E319C72C8161603FA45AA7B998A167B8F1E629521", + 0xFF06, nameSplitPattern); + + add("X9.62 c2pnb304w1", "1.2.840.10045.3.0.17", B, + "010000000000000000000000000000000000000000000000000000000000000000000000000807", + "FD0D693149A118F651E6DCE6802085377E5F882D1B510B44160074C1288078365A0396C8E681", + "BDDB97E555A50A908E43B01C798EA5DAA6788F1EA2794EFCF57166B8C14039601E55827340BE", + "197B07845E9BE2D96ADB0F5F3C7F2CFFBD7A3EB8B6FEC35C7FD67F26DDF6285A644F740A2614", + "E19FBEB76E0DA171517ECF401B50289BF014103288527A9B416A105E80260B549FDC1B92C03B", + "000101D556572AABAC800101D556572AABAC8001022D5C91DD173F8FB561DA6899164443051D", + 0xFE2E, nameSplitPattern); + + add("X9.62 c2pnb368w1", "1.2.840.10045.3.0.19", B, + "0100000000000000000000000000000000000000000000000000000000000000000000002000000000000000000007", + "E0D2EE25095206F5E2A4F9ED229F1F256E79A0E2B455970D8D0D865BD94778C576D62F0AB7519CCD2A1A906AE30D", + "FC1217D4320A90452C760A58EDCD30C8DD069B3C34453837A34ED50CB54917E1C2112D84D164F444F8F74786046A", + "1085E2755381DCCCE3C1557AFA10C2F0C0C2825646C5B34A394CBCFA8BC16B22E7E789E927BE216F02E1FB136A5F", + "7B3EB1BDDCBA62D5D8B2059B525797FC73822C59059C623A45FF3843CEE8F87CD1855ADAA81E2A0750B80FDA2310", + "00010090512DA9AF72B08349D98A5DD4C7B0532ECA51CE03E2D10F3B7AC579BD87E909AE40A6F131E9CFCE5BD967", + 0xFF70, nameSplitPattern); +*/ + + specCollection = Collections.unmodifiableCollection(oidMap.values()); + } +} diff --git a/src/sun/security/ec/ECDHKeyAgreement.java b/src/sun/security/ec/ECDHKeyAgreement.java new file mode 100644 index 00000000..fbb2838f --- /dev/null +++ b/src/sun/security/ec/ECDHKeyAgreement.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.util.ECUtil; + +/** + * KeyAgreement implementation for ECDH. + * + * @since 1.7 + */ +public final class ECDHKeyAgreement extends KeyAgreementSpi { + + // private key, if initialized + private ECPrivateKey privateKey; + + // encoded public point, non-null between doPhase() & generateSecret() only + private byte[] publicValue; + + // length of the secret to be derived + private int secretLen; + + /** + * Constructs a new ECDHKeyAgreement. + */ + public ECDHKeyAgreement() { + } + + // see JCE spec + @Override + protected void engineInit(Key key, SecureRandom random) + throws InvalidKeyException { + if (!(key instanceof PrivateKey)) { + throw new InvalidKeyException + ("Key must be instance of PrivateKey"); + } + privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key); + publicValue = null; + } + + // see JCE spec + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException + ("Parameters not supported"); + } + engineInit(key, random); + } + + // see JCE spec + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicValue != null) { + throw new IllegalStateException("Phase already executed"); + } + if (!lastPhase) { + throw new IllegalStateException + ("Only two party agreement supported, lastPhase must be true"); + } + if (!(key instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be a PublicKey with algorithm EC"); + } + + ECPublicKey ecKey = (ECPublicKey)key; + ECParameterSpec params = ecKey.getParams(); + + if (ecKey instanceof ECPublicKeyImpl) { + publicValue = ((ECPublicKeyImpl)ecKey).getEncodedPublicValue(); + } else { // instanceof ECPublicKey + publicValue = + ECUtil.encodePoint(ecKey.getW(), params.getCurve()); + } + int keyLenBits = params.getCurve().getField().getFieldSize(); + secretLen = (keyLenBits + 7) >> 3; + + return null; + } + + // see JCE spec + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + if ((privateKey == null) || (publicValue == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + + byte[] s = privateKey.getS().toByteArray(); + byte[] encodedParams = // DER OID + ECUtil.encodeECParameterSpec(null, privateKey.getParams()); + + try { + + return deriveKey(s, publicValue, encodedParams); + + } catch (GeneralSecurityException e) { + throw new ProviderException("Could not derive key", e); + } + + } + + // see JCE spec + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int + offset) throws IllegalStateException, ShortBufferException { + if (offset + secretLen > sharedSecret.length) { + throw new ShortBufferException("Need " + secretLen + + " bytes, only " + (sharedSecret.length - offset) + " available"); + } + byte[] secret = engineGenerateSecret(); + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + // see JCE spec + @Override + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, + InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + if (!(algorithm.equals("TlsPremasterSecret"))) { + throw new NoSuchAlgorithmException + ("Only supported for algorithm TlsPremasterSecret"); + } + return new SecretKeySpec(engineGenerateSecret(), "TlsPremasterSecret"); + } + + /** + * Generates a secret key using the public and private keys. + * + * @param s the private key's S value. + * @param w the public key's W point (in uncompressed form). + * @param encodedParams the curve's DER encoded object identifier. + * + * @return byte[] the secret key. + */ + private static native byte[] deriveKey(byte[] s, byte[] w, + byte[] encodedParams) throws GeneralSecurityException; +} diff --git a/src/sun/security/ec/ECDSASignature.java b/src/sun/security/ec/ECDSASignature.java new file mode 100644 index 00000000..b472eb4c --- /dev/null +++ b/src/sun/security/ec/ECDSASignature.java @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.nio.ByteBuffer; +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.jca.JCAUtil; +import sun.security.util.*; + +/** + * ECDSA signature implementation. This class currently supports the + * following algorithm names: + * + * . "NONEwithECDSA" + * . "SHA1withECDSA" + * . "SHA224withECDSA" + * . "SHA256withECDSA" + * . "SHA384withECDSA" + * . "SHA512withECDSA" + * + * @since 1.7 + */ +abstract class ECDSASignature extends SignatureSpi { + + // message digest implementation we use + private final MessageDigest messageDigest; + + // supplied entropy + private SecureRandom random; + + // flag indicating whether the digest has been reset + private boolean needsReset; + + // private key, if initialized for signing + private ECPrivateKey privateKey; + + // public key, if initialized for verifying + private ECPublicKey publicKey; + + // signature parameters + private ECParameterSpec sigParams = null; + + /** + * Constructs a new ECDSASignature. Used by Raw subclass. + * + * @exception ProviderException if the native ECC library is unavailable. + */ + ECDSASignature() { + messageDigest = null; + } + + /** + * Constructs a new ECDSASignature. Used by subclasses. + */ + ECDSASignature(String digestName) { + try { + messageDigest = MessageDigest.getInstance(digestName); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + needsReset = false; + } + + // Nested class for NONEwithECDSA signatures + public static final class Raw extends ECDSASignature { + + // the longest supported digest is 512 bits (SHA-512) + private static final int RAW_ECDSA_MAX = 64; + + private final byte[] precomputedDigest; + private int offset = 0; + + public Raw() { + precomputedDigest = new byte[RAW_ECDSA_MAX]; + } + + // Stores the precomputed message digest value. + @Override + protected void engineUpdate(byte b) throws SignatureException { + if (offset >= precomputedDigest.length) { + offset = RAW_ECDSA_MAX + 1; + return; + } + precomputedDigest[offset++] = b; + } + + // Stores the precomputed message digest value. + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + if (offset >= precomputedDigest.length) { + offset = RAW_ECDSA_MAX + 1; + return; + } + System.arraycopy(b, off, precomputedDigest, offset, len); + offset += len; + } + + // Stores the precomputed message digest value. + @Override + protected void engineUpdate(ByteBuffer byteBuffer) { + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + if (offset + len >= precomputedDigest.length) { + offset = RAW_ECDSA_MAX + 1; + return; + } + byteBuffer.get(precomputedDigest, offset, len); + offset += len; + } + + @Override + protected void resetDigest(){ + offset = 0; + } + + // Returns the precomputed message digest value. + @Override + protected byte[] getDigestValue() throws SignatureException { + if (offset > RAW_ECDSA_MAX) { + throw new SignatureException("Message digest is too long"); + + } + byte[] result = new byte[offset]; + System.arraycopy(precomputedDigest, 0, result, 0, offset); + offset = 0; + + return result; + } + } + + // Nested class for SHA1withECDSA signatures + public static final class SHA1 extends ECDSASignature { + public SHA1() { + super("SHA1"); + } + } + + // Nested class for SHA224withECDSA signatures + public static final class SHA224 extends ECDSASignature { + public SHA224() { + super("SHA-224"); + } + } + + // Nested class for SHA256withECDSA signatures + public static final class SHA256 extends ECDSASignature { + public SHA256() { + super("SHA-256"); + } + } + + // Nested class for SHA384withECDSA signatures + public static final class SHA384 extends ECDSASignature { + public SHA384() { + super("SHA-384"); + } + } + + // Nested class for SHA512withECDSA signatures + public static final class SHA512 extends ECDSASignature { + public SHA512() { + super("SHA-512"); + } + } + + // initialize for verification. See JCA doc + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + ECPublicKey key = (ECPublicKey) ECKeyFactory.toECKey(publicKey); + if (!isCompatible(this.sigParams, key.getParams())) { + throw new InvalidKeyException("Key params does not match signature params"); + } + + // Should check that the supplied key is appropriate for signature + // algorithm (e.g. P-256 for SHA256withECDSA) + this.publicKey = key; + this.privateKey = null; + resetDigest(); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + ECPrivateKey key = (ECPrivateKey) ECKeyFactory.toECKey(privateKey); + if (!isCompatible(this.sigParams, key.getParams())) { + throw new InvalidKeyException("Key params does not match signature params"); + } + + // Should check that the supplied key is appropriate for signature + // algorithm (e.g. P-256 for SHA256withECDSA) + this.privateKey = key; + this.publicKey = null; + this.random = random; + resetDigest(); + } + + /** + * Resets the message digest if needed. + */ + protected void resetDigest() { + if (needsReset) { + if (messageDigest != null) { + messageDigest.reset(); + } + needsReset = false; + } + } + + /** + * Returns the message digest value. + */ + protected byte[] getDigestValue() throws SignatureException { + needsReset = false; + return messageDigest.digest(); + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte b) throws SignatureException { + messageDigest.update(b); + needsReset = true; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + messageDigest.update(b, off, len); + needsReset = true; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(ByteBuffer byteBuffer) { + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + + messageDigest.update(byteBuffer); + needsReset = true; + } + + private static boolean isCompatible(ECParameterSpec sigParams, + ECParameterSpec keyParams) { + if (sigParams == null) { + // no restriction on key param + return true; + } + return ECUtil.equals(sigParams, keyParams); + } + + // sign the data and return the signature. See JCA doc + @Override + protected byte[] engineSign() throws SignatureException { + byte[] s = privateKey.getS().toByteArray(); + ECParameterSpec params = privateKey.getParams(); + // DER OID + byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params); + int keySize = params.getCurve().getField().getFieldSize(); + + // seed is twice the key size (in bytes) plus 1 + byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2]; + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + random.nextBytes(seed); + + try { + + return encodeSignature( + signDigest(getDigestValue(), s, encodedParams, seed)); + + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not sign data", e); + } + } + + // verify the data and return the result. See JCA doc + @Override + protected boolean engineVerify(byte[] signature) throws SignatureException { + + byte[] w; + ECParameterSpec params = publicKey.getParams(); + // DER OID + byte[] encodedParams = ECUtil.encodeECParameterSpec(null, params); + + if (publicKey instanceof ECPublicKeyImpl) { + w = ((ECPublicKeyImpl)publicKey).getEncodedPublicValue(); + } else { // instanceof ECPublicKey + w = ECUtil.encodePoint(publicKey.getW(), params.getCurve()); + } + + try { + + return verifySignedDigest( + decodeSignature(signature), getDigestValue(), w, encodedParams); + + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not verify signature", e); + } + } + + // set parameter, not supported. See JCA doc + @Override + @Deprecated + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null && !(params instanceof ECParameterSpec)) { + throw new InvalidAlgorithmParameterException("No parameter accepted"); + } + ECKey key = (this.privateKey == null? this.publicKey : this.privateKey); + if ((key != null) && !isCompatible((ECParameterSpec)params, key.getParams())) { + throw new InvalidAlgorithmParameterException + ("Signature params does not match key params"); + } + + sigParams = (ECParameterSpec) params; + } + + // get parameter, not supported. See JCA doc + @Override + @Deprecated + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + if (sigParams == null) { + return null; + } + try { + AlgorithmParameters ap = AlgorithmParameters.getInstance("EC"); + ap.init(sigParams); + return ap; + } catch (Exception e) { + // should never happen + throw new ProviderException("Error retrieving EC parameters", e); + } + } + + // Convert the concatenation of R and S into their DER encoding + private byte[] encodeSignature(byte[] signature) throws SignatureException { + + try { + + int n = signature.length >> 1; + byte[] bytes = new byte[n]; + System.arraycopy(signature, 0, bytes, 0, n); + BigInteger r = new BigInteger(1, bytes); + System.arraycopy(signature, n, bytes, 0, n); + BigInteger s = new BigInteger(1, bytes); + + DerOutputStream out = new DerOutputStream(signature.length + 10); + out.putInteger(r); + out.putInteger(s); + DerValue result = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + + return result.toByteArray(); + + } catch (Exception e) { + throw new SignatureException("Could not encode signature", e); + } + } + + // Convert the DER encoding of R and S into a concatenation of R and S + private byte[] decodeSignature(byte[] signature) throws SignatureException { + + try { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getPositiveBigInteger(); + BigInteger s = values[1].getPositiveBigInteger(); + // trim leading zeroes + byte[] rBytes = trimZeroes(r.toByteArray()); + byte[] sBytes = trimZeroes(s.toByteArray()); + int k = Math.max(rBytes.length, sBytes.length); + // r and s each occupy half the array + byte[] result = new byte[k << 1]; + System.arraycopy(rBytes, 0, result, k - rBytes.length, + rBytes.length); + System.arraycopy(sBytes, 0, result, result.length - sBytes.length, + sBytes.length); + return result; + + } catch (Exception e) { + throw new SignatureException("Could not decode signature", e); + } + } + + // trim leading (most significant) zeroes from the result + private static byte[] trimZeroes(byte[] b) { + int i = 0; + while ((i < b.length - 1) && (b[i] == 0)) { + i++; + } + if (i == 0) { + return b; + } + byte[] t = new byte[b.length - i]; + System.arraycopy(b, i, t, 0, t.length); + return t; + } + + /** + * Signs the digest using the private key. + * + * @param digest the digest to be signed. + * @param s the private key's S value. + * @param encodedParams the curve's DER encoded object identifier. + * @param seed the random seed. + * + * @return byte[] the signature. + */ + private static native byte[] signDigest(byte[] digest, byte[] s, + byte[] encodedParams, byte[] seed) throws GeneralSecurityException; + + /** + * Verifies the signed digest using the public key. + * + * @param signedDigest the signature to be verified. It is encoded + * as a concatenation of the key's R and S values. + * @param digest the digest to be used. + * @param w the public key's W point (in uncompressed form). + * @param encodedParams the curve's DER encoded object identifier. + * + * @return boolean true if the signature is successfully verified. + */ + private static native boolean verifySignedDigest(byte[] signature, + byte[] digest, byte[] w, byte[] encodedParams) + throws GeneralSecurityException; +} diff --git a/src/sun/security/ec/ECKeyFactory.java b/src/sun/security/ec/ECKeyFactory.java new file mode 100644 index 00000000..16ee6778 --- /dev/null +++ b/src/sun/security/ec/ECKeyFactory.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +/** + * KeyFactory for EC keys. Keys must be instances of PublicKey or PrivateKey + * and getAlgorithm() must return "EC". For such keys, it supports conversion + * between the following: + * + * For public keys: + * . PublicKey with an X.509 encoding + * . ECPublicKey + * . ECPublicKeySpec + * . X509EncodedKeySpec + * + * For private keys: + * . PrivateKey with a PKCS#8 encoding + * . ECPrivateKey + * . ECPrivateKeySpec + * . PKCS8EncodedKeySpec + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ECKeyFactory extends KeyFactorySpi { + + // Used by translateKey() + private static KeyFactory instance; + + private static KeyFactory getInstance() { + if (instance == null) { + try { + instance = KeyFactory.getInstance("EC", "SunEC"); + } catch (NoSuchProviderException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + return instance; + } + + public ECKeyFactory() { + // empty + } + + /** + * Static method to convert Key into a useable instance of + * ECPublicKey or ECPrivateKey. Check the key and convert it + * to a Sun key if necessary. If the key is not an EC key + * or cannot be used, throw an InvalidKeyException. + * + * The difference between this method and engineTranslateKey() is that + * we do not convert keys of other providers that are already an + * instance of ECPublicKey or ECPrivateKey. + * + * To be used by future Java ECDSA and ECDH implementations. + */ + public static ECKey toECKey(Key key) throws InvalidKeyException { + if (key instanceof ECKey) { + ECKey ecKey = (ECKey)key; + checkKey(ecKey); + return ecKey; + } else { + /* + * We don't call the engineTranslateKey method directly + * because KeyFactory.translateKey adds code to loop through + * all key factories. + */ + return (ECKey)getInstance().translateKey(key); + } + } + + /** + * Check that the given EC key is valid. + */ + private static void checkKey(ECKey key) throws InvalidKeyException { + // check for subinterfaces, omit additional checks for our keys + if (key instanceof ECPublicKey) { + if (key instanceof ECPublicKeyImpl) { + return; + } + } else if (key instanceof ECPrivateKey) { + if (key instanceof ECPrivateKeyImpl) { + return; + } + } else { + throw new InvalidKeyException("Neither a public nor a private key"); + } + // ECKey does not extend Key, so we need to do a cast + String keyAlg = ((Key)key).getAlgorithm(); + if (keyAlg.equals("EC") == false) { + throw new InvalidKeyException("Not an EC key: " + keyAlg); + } + // XXX further sanity checks about whether this key uses supported + // fields, point formats, etc. would go here + } + + /** + * Translate an EC key into a Sun EC key. If conversion is + * not possible, throw an InvalidKeyException. + * See also JCA doc. + */ + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + String keyAlg = key.getAlgorithm(); + if (keyAlg.equals("EC") == false) { + throw new InvalidKeyException("Not an EC key: " + keyAlg); + } + if (key instanceof PublicKey) { + return implTranslatePublicKey((PublicKey)key); + } else if (key instanceof PrivateKey) { + return implTranslatePrivateKey((PrivateKey)key); + } else { + throw new InvalidKeyException("Neither a public nor a private key"); + } + } + + // see JCA doc + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + try { + return implGeneratePublic(keySpec); + } catch (InvalidKeySpecException e) { + throw e; + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + // see JCA doc + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + try { + return implGeneratePrivate(keySpec); + } catch (InvalidKeySpecException e) { + throw e; + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + // internal implementation of translateKey() for public keys. See JCA doc + private PublicKey implTranslatePublicKey(PublicKey key) + throws InvalidKeyException { + if (key instanceof ECPublicKey) { + if (key instanceof ECPublicKeyImpl) { + return key; + } + ECPublicKey ecKey = (ECPublicKey)key; + return new ECPublicKeyImpl( + ecKey.getW(), + ecKey.getParams() + ); + } else if ("X.509".equals(key.getFormat())) { + byte[] encoded = key.getEncoded(); + return new ECPublicKeyImpl(encoded); + } else { + throw new InvalidKeyException("Public keys must be instance " + + "of ECPublicKey or have X.509 encoding"); + } + } + + // internal implementation of translateKey() for private keys. See JCA doc + private PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException { + if (key instanceof ECPrivateKey) { + if (key instanceof ECPrivateKeyImpl) { + return key; + } + ECPrivateKey ecKey = (ECPrivateKey)key; + return new ECPrivateKeyImpl( + ecKey.getS(), + ecKey.getParams() + ); + } else if ("PKCS#8".equals(key.getFormat())) { + return new ECPrivateKeyImpl(key.getEncoded()); + } else { + throw new InvalidKeyException("Private keys must be instance " + + "of ECPrivateKey or have PKCS#8 encoding"); + } + } + + // internal implementation of generatePublic. See JCA doc + private PublicKey implGeneratePublic(KeySpec keySpec) + throws GeneralSecurityException { + if (keySpec instanceof X509EncodedKeySpec) { + X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; + return new ECPublicKeyImpl(x509Spec.getEncoded()); + } else if (keySpec instanceof ECPublicKeySpec) { + ECPublicKeySpec ecSpec = (ECPublicKeySpec)keySpec; + return new ECPublicKeyImpl( + ecSpec.getW(), + ecSpec.getParams() + ); + } else { + throw new InvalidKeySpecException("Only ECPublicKeySpec " + + "and X509EncodedKeySpec supported for EC public keys"); + } + } + + // internal implementation of generatePrivate. See JCA doc + private PrivateKey implGeneratePrivate(KeySpec keySpec) + throws GeneralSecurityException { + if (keySpec instanceof PKCS8EncodedKeySpec) { + PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; + return new ECPrivateKeyImpl(pkcsSpec.getEncoded()); + } else if (keySpec instanceof ECPrivateKeySpec) { + ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec; + return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams()); + } else { + throw new InvalidKeySpecException("Only ECPrivateKeySpec " + + "and PKCS8EncodedKeySpec supported for EC private keys"); + } + } + + protected T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + try { + // convert key to one of our keys + // this also verifies that the key is a valid EC key and ensures + // that the encoding is X.509/PKCS#8 for public/private keys + key = engineTranslateKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + if (key instanceof ECPublicKey) { + ECPublicKey ecKey = (ECPublicKey)key; + if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) { + return keySpec.cast(new ECPublicKeySpec( + ecKey.getW(), + ecKey.getParams() + )); + } else if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) { + return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); + } else { + throw new InvalidKeySpecException + ("KeySpec must be ECPublicKeySpec or " + + "X509EncodedKeySpec for EC public keys"); + } + } else if (key instanceof ECPrivateKey) { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) { + return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded())); + } else if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) { + ECPrivateKey ecKey = (ECPrivateKey)key; + return keySpec.cast(new ECPrivateKeySpec( + ecKey.getS(), + ecKey.getParams() + )); + } else { + throw new InvalidKeySpecException + ("KeySpec must be ECPrivateKeySpec or " + + "PKCS8EncodedKeySpec for EC private keys"); + } + } else { + // should not occur, caught in engineTranslateKey() + throw new InvalidKeySpecException("Neither public nor private key"); + } + } +} diff --git a/src/sun/security/ec/ECKeyPairGenerator.java b/src/sun/security/ec/ECKeyPairGenerator.java new file mode 100644 index 00000000..ee7db8d1 --- /dev/null +++ b/src/sun/security/ec/ECKeyPairGenerator.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; + +import sun.security.jca.JCAUtil; +import sun.security.util.ECUtil; + +/** + * EC keypair generator. + * Standard algorithm, minimum key length is 112 bits, maximum is 571 bits. + * + * @since 1.7 + */ +public final class ECKeyPairGenerator extends KeyPairGeneratorSpi { + + private static final int KEY_SIZE_MIN = 112; // min bits (see ecc_impl.h) + private static final int KEY_SIZE_MAX = 571; // max bits (see ecc_impl.h) + private static final int KEY_SIZE_DEFAULT = 256; + + // used to seed the keypair generator + private SecureRandom random; + + // size of the key to generate, KEY_SIZE_MIN <= keySize <= KEY_SIZE_MAX + private int keySize; + + // parameters specified via init, if any + private AlgorithmParameterSpec params = null; + + /** + * Constructs a new ECKeyPairGenerator. + */ + public ECKeyPairGenerator() { + // initialize to default in case the app does not call initialize() + initialize(KEY_SIZE_DEFAULT, null); + } + + // initialize the generator. See JCA doc + @Override + public void initialize(int keySize, SecureRandom random) { + + checkKeySize(keySize); + this.params = ECUtil.getECParameterSpec(null, keySize); + if (params == null) { + throw new InvalidParameterException( + "No EC parameters available for key size " + keySize + " bits"); + } + this.random = random; + } + + // second initialize method. See JCA doc + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + + if (params instanceof ECParameterSpec) { + this.params = ECUtil.getECParameterSpec(null, + (ECParameterSpec)params); + if (this.params == null) { + throw new InvalidAlgorithmParameterException( + "Unsupported curve: " + params); + } + } else if (params instanceof ECGenParameterSpec) { + String name = ((ECGenParameterSpec)params).getName(); + this.params = ECUtil.getECParameterSpec(null, name); + if (this.params == null) { + throw new InvalidAlgorithmParameterException( + "Unknown curve name: " + name); + } + } else { + throw new InvalidAlgorithmParameterException( + "ECParameterSpec or ECGenParameterSpec required for EC"); + } + this.keySize = + ((ECParameterSpec)this.params).getCurve().getField().getFieldSize(); + this.random = random; + } + + // generate the keypair. See JCA doc + @Override + public KeyPair generateKeyPair() { + + byte[] encodedParams = + ECUtil.encodeECParameterSpec(null, (ECParameterSpec)params); + + // seed is twice the key size (in bytes) plus 1 + byte[] seed = new byte[(((keySize + 7) >> 3) + 1) * 2]; + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + random.nextBytes(seed); + + try { + + Object[] keyBytes = generateECKeyPair(keySize, encodedParams, seed); + + // The 'params' object supplied above is equivalent to the native + // one so there is no need to fetch it. + // keyBytes[0] is the encoding of the native private key + BigInteger s = new BigInteger(1, (byte[])keyBytes[0]); + + PrivateKey privateKey = + new ECPrivateKeyImpl(s, (ECParameterSpec)params); + + // keyBytes[1] is the encoding of the native public key + ECPoint w = ECUtil.decodePoint((byte[])keyBytes[1], + ((ECParameterSpec)params).getCurve()); + PublicKey publicKey = + new ECPublicKeyImpl(w, (ECParameterSpec)params); + + return new KeyPair(publicKey, privateKey); + + } catch (Exception e) { + throw new ProviderException(e); + } + } + + private void checkKeySize(int keySize) throws InvalidParameterException { + if (keySize < KEY_SIZE_MIN) { + throw new InvalidParameterException + ("Key size must be at least " + KEY_SIZE_MIN + " bits"); + } + if (keySize > KEY_SIZE_MAX) { + throw new InvalidParameterException + ("Key size must be at most " + KEY_SIZE_MAX + " bits"); + } + this.keySize = keySize; + } + + /* + * Generates the keypair and returns a 2-element array of encoding bytes. + * The first one is for the private key, the second for the public key. + */ + private static native Object[] generateECKeyPair(int keySize, + byte[] encodedParams, byte[] seed) throws GeneralSecurityException; +} diff --git a/src/sun/security/ec/ECParameters.java b/src/sun/security/ec/ECParameters.java new file mode 100644 index 00000000..8292c95d --- /dev/null +++ b/src/sun/security/ec/ECParameters.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.io.IOException; + +import java.security.*; +import java.security.spec.*; + +import sun.security.util.*; + +/** + * This class implements encoding and decoding of Elliptic Curve parameters + * as specified in RFC 3279. + * + * However, only named curves are currently supported. + * + * ASN.1 from RFC 3279 follows. Note that X9.62 (2005) has added some additional + * options. + * + *
    + *    EcpkParameters ::= CHOICE {
    + *      ecParameters  ECParameters,
    + *      namedCurve    OBJECT IDENTIFIER,
    + *      implicitlyCA  NULL }
    + *
    + *    ECParameters ::= SEQUENCE {
    + *       version   ECPVer,          -- version is always 1
    + *       fieldID   FieldID,         -- identifies the finite field over
    + *                                  -- which the curve is defined
    + *       curve     Curve,           -- coefficients a and b of the
    + *                                  -- elliptic curve
    + *       base      ECPoint,         -- specifies the base point P
    + *                                  -- on the elliptic curve
    + *       order     INTEGER,         -- the order n of the base point
    + *       cofactor  INTEGER OPTIONAL -- The integer h = #E(Fq)/n
    + *       }
    + *
    + *    ECPVer ::= INTEGER {ecpVer1(1)}
    + *
    + *    Curve ::= SEQUENCE {
    + *       a         FieldElement,
    + *       b         FieldElement,
    + *       seed      BIT STRING OPTIONAL }
    + *
    + *    FieldElement ::= OCTET STRING
    + *
    + *    ECPoint ::= OCTET STRING
    + * 
    + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ECParameters extends AlgorithmParametersSpi { + + // used by ECPublicKeyImpl and ECPrivateKeyImpl + static AlgorithmParameters getAlgorithmParameters(ECParameterSpec spec) + throws InvalidKeyException { + try { + AlgorithmParameters params = + AlgorithmParameters.getInstance("EC", "SunEC"); + params.init(spec); + return params; + } catch (GeneralSecurityException e) { + throw new InvalidKeyException("EC parameters error", e); + } + } + + /* + * The parameters these AlgorithmParameters object represents. + * Currently, it is always an instance of NamedCurve. + */ + private NamedCurve namedCurve; + + // A public constructor is required by AlgorithmParameters class. + public ECParameters() { + // empty + } + + // AlgorithmParameterSpi methods + + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException { + + if (paramSpec == null) { + throw new InvalidParameterSpecException + ("paramSpec must not be null"); + } + + if (paramSpec instanceof NamedCurve) { + namedCurve = (NamedCurve)paramSpec; + return; + } + + if (paramSpec instanceof ECParameterSpec) { + namedCurve = CurveDB.lookup((ECParameterSpec)paramSpec); + } else if (paramSpec instanceof ECGenParameterSpec) { + String name = ((ECGenParameterSpec)paramSpec).getName(); + namedCurve = CurveDB.lookup(name); + } else if (paramSpec instanceof ECKeySizeParameterSpec) { + int keySize = ((ECKeySizeParameterSpec)paramSpec).getKeySize(); + namedCurve = CurveDB.lookup(keySize); + } else { + throw new InvalidParameterSpecException + ("Only ECParameterSpec and ECGenParameterSpec supported"); + } + + if (namedCurve == null) { + throw new InvalidParameterSpecException( + "Not a supported curve: " + paramSpec); + } + } + + protected void engineInit(byte[] params) throws IOException { + DerValue encodedParams = new DerValue(params); + if (encodedParams.tag == DerValue.tag_ObjectId) { + ObjectIdentifier oid = encodedParams.getOID(); + NamedCurve spec = CurveDB.lookup(oid.toString()); + if (spec == null) { + throw new IOException("Unknown named curve: " + oid); + } + + namedCurve = spec; + return; + } + + throw new IOException("Only named ECParameters supported"); + + // The code below is incomplete. + // It is left as a starting point for a complete parsing implementation. + +/* + if (encodedParams.tag != DerValue.tag_Sequence) { + throw new IOException("Unsupported EC parameters, tag: " + + encodedParams.tag); + } + + encodedParams.data.reset(); + + DerInputStream in = encodedParams.data; + + int version = in.getInteger(); + if (version != 1) { + throw new IOException("Unsupported EC parameters version: " + + version); + } + ECField field = parseField(in); + EllipticCurve curve = parseCurve(in, field); + ECPoint point = parsePoint(in, curve); + + BigInteger order = in.getBigInteger(); + int cofactor = 0; + + if (in.available() != 0) { + cofactor = in.getInteger(); + } + + // XXX HashAlgorithm optional + + if (encodedParams.data.available() != 0) { + throw new IOException("encoded params have " + + encodedParams.data.available() + + " extra bytes"); + } + + return new ECParameterSpec(curve, point, order, cofactor); +*/ + } + + protected void engineInit(byte[] params, String decodingMethod) + throws IOException { + engineInit(params); + } + + protected T + engineGetParameterSpec(Class spec) + throws InvalidParameterSpecException { + + if (spec.isAssignableFrom(ECParameterSpec.class)) { + return spec.cast(namedCurve); + } + + if (spec.isAssignableFrom(ECGenParameterSpec.class)) { + // Ensure the name is the Object ID + String name = namedCurve.getObjectId(); + return spec.cast(new ECGenParameterSpec(name)); + } + + if (spec.isAssignableFrom(ECKeySizeParameterSpec.class)) { + int keySize = namedCurve.getCurve().getField().getFieldSize(); + return spec.cast(new ECKeySizeParameterSpec(keySize)); + } + + throw new InvalidParameterSpecException( + "Only ECParameterSpec and ECGenParameterSpec supported"); + } + + protected byte[] engineGetEncoded() throws IOException { + return namedCurve.getEncoded(); + } + + protected byte[] engineGetEncoded(String encodingMethod) + throws IOException { + return engineGetEncoded(); + } + + protected String engineToString() { + if (namedCurve == null) { + return "Not initialized"; + } + + return namedCurve.toString(); + } +} + diff --git a/src/sun/security/ec/ECPrivateKeyImpl.java b/src/sun/security/ec/ECPrivateKeyImpl.java new file mode 100644 index 00000000..b974c3ca --- /dev/null +++ b/src/sun/security/ec/ECPrivateKeyImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.util.*; +import sun.security.x509.AlgorithmId; +import sun.security.pkcs.PKCS8Key; + +/** + * Key implementation for EC private keys. + * + * ASN.1 syntax for EC private keys from SEC 1 v1.5 (draft): + * + *
    + * EXPLICIT TAGS
    + *
    + * ECPrivateKey ::= SEQUENCE {
    + *   version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
    + *   privateKey OCTET STRING,
    + *   parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL,
    + *   publicKey [1] BIT STRING OPTIONAL
    + * }
    + * 
    + * + * We currently ignore the optional parameters and publicKey fields. We + * require that the parameters are encoded as part of the AlgorithmIdentifier, + * not in the private key structure. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey { + + private static final long serialVersionUID = 88695385615075129L; + + private BigInteger s; // private value + private ECParameterSpec params; + + /** + * Construct a key from its encoding. Called by the ECKeyFactory. + */ + ECPrivateKeyImpl(byte[] encoded) throws InvalidKeyException { + decode(encoded); + } + + /** + * Construct a key from its components. Used by the + * KeyFactory. + */ + ECPrivateKeyImpl(BigInteger s, ECParameterSpec params) + throws InvalidKeyException { + this.s = s; + this.params = params; + // generate the encoding + algid = new AlgorithmId + (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params)); + try { + DerOutputStream out = new DerOutputStream(); + out.putInteger(1); // version 1 + byte[] privBytes = ECUtil.trimZeroes(s.toByteArray()); + out.putOctetString(privBytes); + DerValue val = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + key = val.toByteArray(); + } catch (IOException exc) { + // should never occur + throw new InvalidKeyException(exc); + } + } + + // see JCA doc + public String getAlgorithm() { + return "EC"; + } + + // see JCA doc + public BigInteger getS() { + return s; + } + + // see JCA doc + public ECParameterSpec getParams() { + return params; + } + + /** + * Parse the key. Called by PKCS8Key. + */ + protected void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(key); + DerValue derValue = in.getDerValue(); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + DerInputStream data = derValue.data; + int version = data.getInteger(); + if (version != 1) { + throw new IOException("Version must be 1"); + } + byte[] privData = data.getOctetString(); + s = new BigInteger(1, privData); + while (data.available() != 0) { + DerValue value = data.getDerValue(); + if (value.isContextSpecific((byte)0)) { + // ignore for now + } else if (value.isContextSpecific((byte)1)) { + // ignore for now + } else { + throw new InvalidKeyException("Unexpected value: " + value); + } + } + AlgorithmParameters algParams = this.algid.getParameters(); + if (algParams == null) { + throw new InvalidKeyException("EC domain parameters must be " + + "encoded in the algorithm identifier"); + } + params = algParams.getParameterSpec(ECParameterSpec.class); + } catch (IOException e) { + throw new InvalidKeyException("Invalid EC private key", e); + } catch (InvalidParameterSpecException e) { + throw new InvalidKeyException("Invalid EC private key", e); + } + } +} diff --git a/src/sun/security/ec/ECPublicKeyImpl.java b/src/sun/security/ec/ECPublicKeyImpl.java new file mode 100644 index 00000000..0e82e734 --- /dev/null +++ b/src/sun/security/ec/ECPublicKeyImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.io.IOException; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.util.*; +import sun.security.x509.*; + +/** + * Key implementation for EC public keys. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class ECPublicKeyImpl extends X509Key implements ECPublicKey { + + private static final long serialVersionUID = -2462037275160462289L; + + private ECPoint w; + private ECParameterSpec params; + + /** + * Construct a key from its components. Used by the + * ECKeyFactory. + */ + @SuppressWarnings("deprecation") + ECPublicKeyImpl(ECPoint w, ECParameterSpec params) + throws InvalidKeyException { + this.w = w; + this.params = params; + // generate the encoding + algid = new AlgorithmId + (AlgorithmId.EC_oid, ECParameters.getAlgorithmParameters(params)); + key = ECUtil.encodePoint(w, params.getCurve()); + } + + /** + * Construct a key from its encoding. + */ + ECPublicKeyImpl(byte[] encoded) throws InvalidKeyException { + decode(encoded); + } + + // see JCA doc + public String getAlgorithm() { + return "EC"; + } + + // see JCA doc + public ECPoint getW() { + return w; + } + + // see JCA doc + public ECParameterSpec getParams() { + return params; + } + + // Internal API to get the encoded point. Currently used by SunPKCS11. + // This may change/go away depending on what we do with the public API. + @SuppressWarnings("deprecation") + public byte[] getEncodedPublicValue() { + return key.clone(); + } + + /** + * Parse the key. Called by X509Key. + */ + @SuppressWarnings("deprecation") + protected void parseKeyBits() throws InvalidKeyException { + AlgorithmParameters algParams = this.algid.getParameters(); + if (algParams == null) { + throw new InvalidKeyException("EC domain parameters must be " + + "encoded in the algorithm identifier"); + } + + try { + params = algParams.getParameterSpec(ECParameterSpec.class); + w = ECUtil.decodePoint(key, params.getCurve()); + } catch (IOException e) { + throw new InvalidKeyException("Invalid EC key", e); + } catch (InvalidParameterSpecException e) { + throw new InvalidKeyException("Invalid EC key", e); + } + } + + // return a string representation of this key for debugging + public String toString() { + return "Sun EC public key, " + params.getCurve().getField().getFieldSize() + + " bits\n public x coord: " + w.getAffineX() + + "\n public y coord: " + w.getAffineY() + + "\n parameters: " + params; + } + + protected Object writeReplace() throws java.io.ObjectStreamException { + return new KeyRep(KeyRep.Type.PUBLIC, + getAlgorithm(), + getFormat(), + getEncoded()); + } +} diff --git a/src/sun/security/ec/NamedCurve.java b/src/sun/security/ec/NamedCurve.java new file mode 100644 index 00000000..97597c86 --- /dev/null +++ b/src/sun/security/ec/NamedCurve.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.spec.*; + +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; + + +/** + * Contains Elliptic Curve parameters. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +class NamedCurve extends ECParameterSpec { + + // friendly name for toString() output + private final String name; + + // well known OID + private final String oid; + + // encoded form (as NamedCurve identified via OID) + private final byte[] encoded; + + NamedCurve(String name, String oid, EllipticCurve curve, + ECPoint g, BigInteger n, int h) { + super(curve, g, n, h); + this.name = name; + this.oid = oid; + + DerOutputStream out = new DerOutputStream(); + + try { + out.putOID(new ObjectIdentifier(oid)); + } catch (IOException e) { + throw new RuntimeException("Internal error", e); + } + + encoded = out.toByteArray(); + } + + String getName() { + return name; + } + + byte[] getEncoded() { + return encoded.clone(); + } + + String getObjectId() { + return oid; + } + + public String toString() { + return name + " (" + oid + ")"; + } +} diff --git a/src/sun/security/ec/SunEC.java b/src/sun/security/ec/SunEC.java new file mode 100644 index 00000000..31891696 --- /dev/null +++ b/src/sun/security/ec/SunEC.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.util.*; +import java.security.*; +import sun.security.action.PutAllAction; + +/** + * Provider class for the Elliptic Curve provider. + * Supports EC keypair and parameter generation, ECDSA signing and + * ECDH key agreement. + * + * IMPLEMENTATION NOTE: + * The Java classes in this provider access a native ECC implementation + * via JNI to a C++ wrapper class which in turn calls C functions. + * The Java classes are packaged into the signed sunec.jar in the JRE + * extensions directory and the C++ and C functions are packaged into + * libsunec.so or sunec.dll in the JRE native libraries directory. + * If the native library is not present then this provider is registered + * with support for fewer ECC algorithms (KeyPairGenerator, Signature and + * KeyAgreement are omitted). + * + * @since 1.7 + */ +public final class SunEC extends Provider { + + private static final long serialVersionUID = -2279741672933606418L; + + // flag indicating whether the full EC implementation is present + // (when native library is absent then fewer EC algorithms are available) + private static boolean useFullImplementation = true; + static { + try { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + System.loadLibrary("sunec"); // check for native library + return null; + } + }); + } catch (UnsatisfiedLinkError e) { + useFullImplementation = false; + } + } + + public SunEC() { + super("SunEC", 1.8d, "Sun Elliptic Curve provider (EC, ECDSA, ECDH)"); + + // if there is no security manager installed, put directly into + // the provider. Otherwise, create a temporary map and use a + // doPrivileged() call at the end to transfer the contents + if (System.getSecurityManager() == null) { + SunECEntries.putEntries(this, useFullImplementation); + } else { + Map map = new HashMap(); + SunECEntries.putEntries(map, useFullImplementation); + AccessController.doPrivileged(new PutAllAction(this, map)); + } + } + +} diff --git a/src/sun/security/ec/SunECEntries.java b/src/sun/security/ec/SunECEntries.java new file mode 100644 index 00000000..4e297183 --- /dev/null +++ b/src/sun/security/ec/SunECEntries.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ec; + +import java.util.Collection; +import java.util.Map; + +import java.util.regex.Pattern; + +/** + * Defines the entries of the SunEC provider. + * + * @since 1.7 + */ +final class SunECEntries { + + private SunECEntries() { + // empty + } + + static void putEntries(Map map, + boolean useFullImplementation) { + + /* + * Key Factory engine + */ + map.put("KeyFactory.EC", "sun.security.ec.ECKeyFactory"); + map.put("Alg.Alias.KeyFactory.EllipticCurve", "EC"); + + map.put("KeyFactory.EC ImplementedIn", "Software"); + + /* + * Algorithm Parameter engine + */ + map.put("AlgorithmParameters.EC", "sun.security.ec.ECParameters"); + map.put("Alg.Alias.AlgorithmParameters.EllipticCurve", "EC"); + map.put("Alg.Alias.AlgorithmParameters.1.2.840.10045.2.1", "EC"); + + map.put("AlgorithmParameters.EC KeySize", "256"); + + map.put("AlgorithmParameters.EC ImplementedIn", "Software"); + + // "AlgorithmParameters.EC SupportedCurves" prop used by unit test + boolean firstCurve = true; + StringBuilder names = new StringBuilder(); + Pattern nameSplitPattern = Pattern.compile(CurveDB.SPLIT_PATTERN); + + Collection supportedCurves = + CurveDB.getSupportedCurves(); + for (NamedCurve namedCurve : supportedCurves) { + if (!firstCurve) { + names.append("|"); + } else { + firstCurve = false; + } + + names.append("["); + + String[] commonNames = nameSplitPattern.split(namedCurve.getName()); + for (String commonName : commonNames) { + names.append(commonName.trim()); + names.append(","); + } + + names.append(namedCurve.getObjectId()); + names.append("]"); + } + + map.put("AlgorithmParameters.EC SupportedCurves", names.toString()); + + /* + * Register the algorithms below only when the full ECC implementation + * is available + */ + if (!useFullImplementation) { + return; + } + + /* + * Signature engines + */ + map.put("Signature.NONEwithECDSA", + "sun.security.ec.ECDSASignature$Raw"); + map.put("Signature.SHA1withECDSA", + "sun.security.ec.ECDSASignature$SHA1"); + map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.1", "SHA1withECDSA"); + map.put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA"); + + map.put("Signature.SHA224withECDSA", + "sun.security.ec.ECDSASignature$SHA224"); + map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.1", "SHA224withECDSA"); + map.put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA"); + + map.put("Signature.SHA256withECDSA", + "sun.security.ec.ECDSASignature$SHA256"); + map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.2", "SHA256withECDSA"); + map.put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA"); + + map.put("Signature.SHA384withECDSA", + "sun.security.ec.ECDSASignature$SHA384"); + map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.3", "SHA384withECDSA"); + map.put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA"); + + map.put("Signature.SHA512withECDSA", + "sun.security.ec.ECDSASignature$SHA512"); + map.put("Alg.Alias.Signature.OID.1.2.840.10045.4.3.4", "SHA512withECDSA"); + map.put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA"); + + String ecKeyClasses = "java.security.interfaces.ECPublicKey" + + "|java.security.interfaces.ECPrivateKey"; + map.put("Signature.NONEwithECDSA SupportedKeyClasses", ecKeyClasses); + map.put("Signature.SHA1withECDSA SupportedKeyClasses", ecKeyClasses); + map.put("Signature.SHA224withECDSA SupportedKeyClasses", ecKeyClasses); + map.put("Signature.SHA256withECDSA SupportedKeyClasses", ecKeyClasses); + map.put("Signature.SHA384withECDSA SupportedKeyClasses", ecKeyClasses); + map.put("Signature.SHA512withECDSA SupportedKeyClasses", ecKeyClasses); + + map.put("Signature.SHA1withECDSA KeySize", "256"); + + map.put("Signature.NONEwithECDSA ImplementedIn", "Software"); + map.put("Signature.SHA1withECDSA ImplementedIn", "Software"); + map.put("Signature.SHA224withECDSA ImplementedIn", "Software"); + map.put("Signature.SHA256withECDSA ImplementedIn", "Software"); + map.put("Signature.SHA384withECDSA ImplementedIn", "Software"); + map.put("Signature.SHA512withECDSA ImplementedIn", "Software"); + + /* + * Key Pair Generator engine + */ + map.put("KeyPairGenerator.EC", "sun.security.ec.ECKeyPairGenerator"); + map.put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC"); + + map.put("KeyPairGenerator.EC KeySize", "256"); + + map.put("KeyPairGenerator.EC ImplementedIn", "Software"); + + /* + * Key Agreement engine + */ + map.put("KeyAgreement.ECDH", "sun.security.ec.ECDHKeyAgreement"); + + map.put("KeyAgreement.ECDH SupportedKeyClasses", ecKeyClasses); + + map.put("KeyAgreement.ECDH ImplementedIn", "Software"); + } +} diff --git a/src/sun/security/internal/interfaces/TlsMasterSecret.java b/src/sun/security/internal/interfaces/TlsMasterSecret.java new file mode 100644 index 00000000..f3eb222c --- /dev/null +++ b/src/sun/security/internal/interfaces/TlsMasterSecret.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.interfaces; + +import javax.crypto.SecretKey; + +/** + * An SSL/TLS master secret key. It is a SecretKey that optionally + * contains protocol version information that is used to detect version + * rollback attacks during the SSL/TLS handshake. + * + *

    Implementation of this interface are returned by the + * generateKey() method of KeyGenerators of the type + * "TlsMasterSecret". + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public interface TlsMasterSecret extends SecretKey { + + public static final long serialVersionUID = -461748105810469773L; + + /** + * Returns the major version number encapsulated in the premaster secret + * this master secret was derived from, or -1 if it is not available. + * + *

    This information will only usually only be available when RSA + * was used as the key exchange algorithm. + * + * @return the major version number, or -1 if it is not available + */ + public int getMajorVersion(); + + /** + * Returns the minor version number encapsulated in the premaster secret + * this master secret was derived from, or -1 if it is not available. + * + *

    This information will only usually only be available when RSA + * was used as the key exchange algorithm. + * + * @return the major version number, or -1 if it is not available + */ + public int getMinorVersion(); + +} diff --git a/src/sun/security/internal/spec/TlsKeyMaterialParameterSpec.java b/src/sun/security/internal/spec/TlsKeyMaterialParameterSpec.java new file mode 100644 index 00000000..a7733d4f --- /dev/null +++ b/src/sun/security/internal/spec/TlsKeyMaterialParameterSpec.java @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.spec; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.SecretKey; + +/** + * Parameters for SSL/TLS key material generation. + * This class is used to initialize KeyGenerator of the type + * "TlsKeyMaterial". The keys returned by such KeyGenerators will be + * instances of {@link TlsKeyMaterialSpec}. + * + *

    Instances of this class are immutable. + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public class TlsKeyMaterialParameterSpec implements AlgorithmParameterSpec { + + private final SecretKey masterSecret; + private final int majorVersion, minorVersion; + private final byte[] clientRandom, serverRandom; + private final String cipherAlgorithm; + private final int cipherKeyLength, ivLength, macKeyLength; + private final int expandedCipherKeyLength; // == 0 for domestic ciphersuites + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; + + /** + * Constructs a new TlsKeyMaterialParameterSpec. + * + * @param masterSecret the master secret + * @param majorVersion the major number of the protocol version + * @param minorVersion the minor number of the protocol version + * @param clientRandom the client's random value + * @param serverRandom the server's random value + * @param cipherAlgorithm the algorithm name of the cipher keys to + * be generated + * @param cipherKeyLength if 0, no cipher keys will be generated; + * otherwise, the length in bytes of cipher keys to be + * generated for domestic cipher suites; for cipher suites defined as + * exportable, the number of key material bytes to be generated; + * @param expandedCipherKeyLength 0 for domestic cipher suites; for + * exportable cipher suites the length in bytes of the key to be + * generated. + * @param ivLength the length in bytes of the initialization vector + * to be generated, or 0 if no initialization vector is required + * @param macKeyLength the length in bytes of the MAC key to be generated + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * + * @throws NullPointerException if masterSecret, clientRandom, + * serverRandom, or cipherAlgorithm are null + * @throws IllegalArgumentException if the algorithm of masterSecret is + * not TlsMasterSecret, or if majorVersion or minorVersion are + * negative or larger than 255; or if cipherKeyLength, expandedKeyLength, + * ivLength, or macKeyLength are negative + */ + public TlsKeyMaterialParameterSpec(SecretKey masterSecret, + int majorVersion, int minorVersion, byte[] clientRandom, + byte[] serverRandom, String cipherAlgorithm, int cipherKeyLength, + int expandedCipherKeyLength, int ivLength, int macKeyLength, + String prfHashAlg, int prfHashLength, int prfBlockSize) { + if (masterSecret.getAlgorithm().equals("TlsMasterSecret") == false) { + throw new IllegalArgumentException("Not a TLS master secret"); + } + if (cipherAlgorithm == null) { + throw new NullPointerException(); + } + this.masterSecret = masterSecret; + this.majorVersion = + TlsMasterSecretParameterSpec.checkVersion(majorVersion); + this.minorVersion = + TlsMasterSecretParameterSpec.checkVersion(minorVersion); + this.clientRandom = clientRandom.clone(); + this.serverRandom = serverRandom.clone(); + this.cipherAlgorithm = cipherAlgorithm; + this.cipherKeyLength = checkSign(cipherKeyLength); + this.expandedCipherKeyLength = checkSign(expandedCipherKeyLength); + this.ivLength = checkSign(ivLength); + this.macKeyLength = checkSign(macKeyLength); + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; + } + + private static int checkSign(int k) { + if (k < 0) { + throw new IllegalArgumentException("Value must not be negative"); + } + return k; + } + + /** + * Returns the master secret. + * + * @return the master secret. + */ + public SecretKey getMasterSecret() { + return masterSecret; + } + + /** + * Returns the major version number. + * + * @return the major version number. + */ + public int getMajorVersion() { + return majorVersion; + } + + /** + * Returns the minor version number. + * + * @return the minor version number. + */ + public int getMinorVersion() { + return minorVersion; + } + + /** + * Returns a copy of the client's random value. + * + * @return a copy of the client's random value. + */ + public byte[] getClientRandom() { + return clientRandom.clone(); + } + + /** + * Returns a copy of the server's random value. + * + * @return a copy of the server's random value. + */ + public byte[] getServerRandom() { + return serverRandom.clone(); + } + + /** + * Returns the cipher algorithm. + * + * @return the cipher algorithm. + */ + public String getCipherAlgorithm() { + return cipherAlgorithm; + } + + /** + * Returns the length in bytes of the encryption key to be generated. + * + * @return the length in bytes of the encryption key to be generated. + */ + public int getCipherKeyLength() { + return cipherKeyLength; + } + + /** + * Returns the length in bytes of the expanded encryption key to be + * generated. Returns zero if the expanded encryption key is not + * supposed to be generated. + * + * @return the length in bytes of the expanded encryption key to be + * generated. + */ + public int getExpandedCipherKeyLength() { + // TLS v1.1 disables the exportable weak cipher suites. + if (majorVersion >= 0x03 && minorVersion >= 0x02) { + return 0; + } + return expandedCipherKeyLength; + } + + /** + * Returns the length in bytes of the initialization vector to be + * generated. Returns zero if the initialization vector is not + * supposed to be generated. + * + * @return the length in bytes of the initialization vector to be + * generated. + */ + public int getIvLength() { + return ivLength; + } + + /** + * Returns the length in bytes of the MAC key to be generated. + * + * @return the length in bytes of the MAC key to be generated. + */ + public int getMacKeyLength() { + return macKeyLength; + } + + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of the PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the block size of the PRF hash algorithm. + * + * @return the hash algorithm block size + */ + public int getPRFBlockSize() { + return prfBlockSize; + } +} diff --git a/src/sun/security/internal/spec/TlsKeyMaterialSpec.java b/src/sun/security/internal/spec/TlsKeyMaterialSpec.java new file mode 100644 index 00000000..44414996 --- /dev/null +++ b/src/sun/security/internal/spec/TlsKeyMaterialSpec.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.spec; + +import java.security.spec.KeySpec; + +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * KeySpec class for SSL/TLS key material. + * + *

    Instances of this class are returned by the generateKey() + * method of KeyGenerators of the type "TlsKeyMaterial". + * Instances of this class are immutable. + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public class TlsKeyMaterialSpec implements KeySpec, SecretKey { + + static final long serialVersionUID = 812912859129525028L; + + private final SecretKey clientMacKey, serverMacKey; + private final SecretKey clientCipherKey, serverCipherKey; + private final IvParameterSpec clientIv, serverIv; + + /** + * Constructs a new TlsKeymaterialSpec from the client and server MAC + * keys. + * This call is equivalent to + * new TlsKeymaterialSpec(clientMacKey, serverMacKey, + * null, null, null, null). + * + * @param clientMacKey the client MAC key (or null) + * @param serverMacKey the server MAC key (or null) + */ + public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey) { + this(clientMacKey, serverMacKey, null, null, null, null); + } + + /** + * Constructs a new TlsKeymaterialSpec from the client and server MAC + * keys and client and server cipher keys. + * This call is equivalent to + * new TlsKeymaterialSpec(clientMacKey, serverMacKey, + * clientCipherKey, serverCipherKey, null, null). + * + * @param clientMacKey the client MAC key (or null) + * @param serverMacKey the server MAC key (or null) + * @param clientCipherKey the client cipher key (or null) + * @param serverCipherKey the server cipher key (or null) + */ + public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey, + SecretKey clientCipherKey, SecretKey serverCipherKey) { + this(clientMacKey, serverMacKey, clientCipherKey, null, + serverCipherKey, null); + } + + /** + * Constructs a new TlsKeymaterialSpec from the client and server MAC + * keys, client and server cipher keys, and client and server + * initialization vectors. + * + * @param clientMacKey the client MAC key (or null) + * @param serverMacKey the server MAC key (or null) + * @param clientCipherKey the client cipher key (or null) + * @param clientIv the client initialization vector (or null) + * @param serverCipherKey the server cipher key (or null) + * @param serverIv the server initialization vector (or null) + */ + public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey, + SecretKey clientCipherKey, IvParameterSpec clientIv, + SecretKey serverCipherKey, IvParameterSpec serverIv) { + + this.clientMacKey = clientMacKey; + this.serverMacKey = serverMacKey; + this.clientCipherKey = clientCipherKey; + this.serverCipherKey = serverCipherKey; + this.clientIv = clientIv; + this.serverIv = serverIv; + } + + /** + * Returns TlsKeyMaterial. + * + * @return TlsKeyMaterial. + */ + public String getAlgorithm() { + return "TlsKeyMaterial"; + } + + /** + * Returns null because keys of this type have no encoding. + * + * @return null because keys of this type have no encoding. + */ + public String getFormat() { + return null; + } + + /** + * Returns null because keys of this type have no encoding. + * + * @return null because keys of this type have no encoding. + */ + public byte[] getEncoded() { + return null; + } + + /** + * Returns the client MAC key. + * + * @return the client MAC key (or null). + */ + public SecretKey getClientMacKey() { + return clientMacKey; + } + + /** + * Return the server MAC key. + * + * @return the server MAC key (or null). + */ + public SecretKey getServerMacKey() { + return serverMacKey; + } + + /** + * Return the client cipher key (or null). + * + * @return the client cipher key (or null). + */ + public SecretKey getClientCipherKey() { + return clientCipherKey; + } + + /** + * Return the client initialization vector (or null). + * + * @return the client initialization vector (or null). + */ + public IvParameterSpec getClientIv() { + return clientIv; + } + + /** + * Return the server cipher key (or null). + * + * @return the server cipher key (or null). + */ + public SecretKey getServerCipherKey() { + return serverCipherKey; + } + + /** + * Return the server initialization vector (or null). + * + * @return the server initialization vector (or null). + */ + public IvParameterSpec getServerIv() { + return serverIv; + } + +} diff --git a/src/sun/security/internal/spec/TlsMasterSecretParameterSpec.java b/src/sun/security/internal/spec/TlsMasterSecretParameterSpec.java new file mode 100644 index 00000000..832b38ed --- /dev/null +++ b/src/sun/security/internal/spec/TlsMasterSecretParameterSpec.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.spec; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.SecretKey; + +/** + * Parameters for SSL/TLS master secret generation. + * This class encapsulates the information necessary to calculate a SSL/TLS + * master secret from the premaster secret and other parameters. + * It is used to initialize KeyGenerators of the type "TlsMasterSecret". + * + *

    Instances of this class are immutable. + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { + + private final SecretKey premasterSecret; + private final int majorVersion, minorVersion; + private final byte[] clientRandom, serverRandom; + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; + + /** + * Constructs a new TlsMasterSecretParameterSpec. + * + *

    The getAlgorithm() method of premasterSecret + * should return "TlsRsaPremasterSecret" if the key exchange + * algorithm was RSA and "TlsPremasterSecret" otherwise. + * + * @param premasterSecret the premaster secret + * @param majorVersion the major number of the protocol version + * @param minorVersion the minor number of the protocol version + * @param clientRandom the client's random value + * @param serverRandom the server's random value + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * + * @throws NullPointerException if premasterSecret, clientRandom, + * or serverRandom are null + * @throws IllegalArgumentException if minorVersion or majorVersion are + * negative or larger than 255 + */ + public TlsMasterSecretParameterSpec(SecretKey premasterSecret, + int majorVersion, int minorVersion, + byte[] clientRandom, byte[] serverRandom, + String prfHashAlg, int prfHashLength, int prfBlockSize) { + if (premasterSecret == null) { + throw new NullPointerException("premasterSecret must not be null"); + } + this.premasterSecret = premasterSecret; + this.majorVersion = checkVersion(majorVersion); + this.minorVersion = checkVersion(minorVersion); + this.clientRandom = clientRandom.clone(); + this.serverRandom = serverRandom.clone(); + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; + } + + static int checkVersion(int version) { + if ((version < 0) || (version > 255)) { + throw new IllegalArgumentException( + "Version must be between 0 and 255"); + } + return version; + } + + /** + * Returns the premaster secret. + * + * @return the premaster secret. + */ + public SecretKey getPremasterSecret() { + return premasterSecret; + } + + /** + * Returns the major version number. + * + * @return the major version number. + */ + public int getMajorVersion() { + return majorVersion; + } + + /** + * Returns the minor version number. + * + * @return the minor version number. + */ + public int getMinorVersion() { + return minorVersion; + } + + /** + * Returns a copy of the client's random value. + * + * @return a copy of the client's random value. + */ + public byte[] getClientRandom() { + return clientRandom.clone(); + } + + /** + * Returns a copy of the server's random value. + * + * @return a copy of the server's random value. + */ + public byte[] getServerRandom() { + return serverRandom.clone(); + } + + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of the PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the block size of the PRF hash algorithm. + * + * @return the hash algorithm block size. + */ + public int getPRFBlockSize() { + return prfBlockSize; + } +} diff --git a/src/sun/security/internal/spec/TlsPrfParameterSpec.java b/src/sun/security/internal/spec/TlsPrfParameterSpec.java new file mode 100644 index 00000000..5958663b --- /dev/null +++ b/src/sun/security/internal/spec/TlsPrfParameterSpec.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.spec; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.SecretKey; + +/** + * Parameters for the TLS PRF (pseudo-random function). The PRF function + * is defined in RFC 2246. + * This class is used to initialize KeyGenerators of the type "TlsPrf". + * + *

    Instances of this class are immutable. + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public class TlsPrfParameterSpec implements AlgorithmParameterSpec { + + private final SecretKey secret; + private final String label; + private final byte[] seed; + private final int outputLength; + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; + + /** + * Constructs a new TlsPrfParameterSpec. + * + * @param secret the secret to use in the calculation (or null) + * @param label the label to use in the calculation + * @param seed the random seed to use in the calculation + * @param outputLength the length in bytes of the output key to be produced + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * + * @throws NullPointerException if label or seed is null + * @throws IllegalArgumentException if outputLength is negative + */ + public TlsPrfParameterSpec(SecretKey secret, String label, + byte[] seed, int outputLength, + String prfHashAlg, int prfHashLength, int prfBlockSize) { + if ((label == null) || (seed == null)) { + throw new NullPointerException("label and seed must not be null"); + } + if (outputLength <= 0) { + throw new IllegalArgumentException("outputLength must be positive"); + } + this.secret = secret; + this.label = label; + this.seed = seed.clone(); + this.outputLength = outputLength; + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; + } + + /** + * Returns the secret to use in the PRF calculation, or null if there is no + * secret. + * + * @return the secret to use in the PRF calculation, or null if there is no + * secret. + */ + public SecretKey getSecret() { + return secret; + } + + /** + * Returns the label to use in the PRF calcuation. + * + * @return the label to use in the PRF calcuation. + */ + public String getLabel() { + return label; + } + + /** + * Returns a copy of the seed to use in the PRF calcuation. + * + * @return a copy of the seed to use in the PRF calcuation. + */ + public byte[] getSeed() { + return seed.clone(); + } + + /** + * Returns the length in bytes of the output key to be produced. + * + * @return the length in bytes of the output key to be produced. + */ + public int getOutputLength() { + return outputLength; + } + + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm, or null if no algorithm was specified. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of PRF hash algorithm. + * + * It would have been preferred to use MessageDigest.getDigestLength(), + * but the API does not require implementations to support the method. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the length of PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFBlockSize() { + return prfBlockSize; + } +} diff --git a/src/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java b/src/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java new file mode 100644 index 00000000..0741499b --- /dev/null +++ b/src/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.internal.spec; + +import java.security.spec.AlgorithmParameterSpec; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Parameters for SSL/TLS RSA premaster secret. + * + *

    Instances of this class are immutable. + * + * @since 1.6 + * @author Andreas Sterbenz + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. + */ +@Deprecated +public class TlsRsaPremasterSecretParameterSpec + implements AlgorithmParameterSpec { + + /* + * The TLS spec says that the version in the RSA premaster secret must + * be the maximum version supported by the client (i.e. the version it + * requested in its client hello version). However, we (and other + * implementations) used to send the active negotiated version. The + * system property below allows to toggle the behavior. + */ + private final static String PROP_NAME = + "com.sun.net.ssl.rsaPreMasterSecretFix"; + + /* + * Default is "false" (old behavior) for compatibility reasons in + * SSLv3/TLSv1. Later protocols (TLSv1.1+) do not use this property. + */ + private final static boolean rsaPreMasterSecretFix = + AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + String value = System.getProperty(PROP_NAME); + if (value != null && value.equalsIgnoreCase("true")) { + return Boolean.TRUE; + } + + return Boolean.FALSE; + } + }); + + private final int clientVersion; + private final int serverVersion; + + /** + * Constructs a new TlsRsaPremasterSecretParameterSpec. + * + * @param clientVersion the version of the TLS protocol by which the + * client wishes to communicate during this session + * @param serverVersion the negotiated version of the TLS protocol which + * contains the lower of that suggested by the client in the client + * hello and the highest supported by the server. + * + * @throws IllegalArgumentException if clientVersion or serverVersion are + * negative or larger than (2^16 - 1) + */ + public TlsRsaPremasterSecretParameterSpec( + int clientVersion, int serverVersion) { + + this.clientVersion = checkVersion(clientVersion); + this.serverVersion = checkVersion(serverVersion); + } + + /** + * Returns the version of the TLS protocol by which the client wishes to + * communicate during this session. + * + * @return the version of the TLS protocol in ClientHello message + */ + public int getClientVersion() { + return clientVersion; + } + + /** + * Returns the negotiated version of the TLS protocol which contains the + * lower of that suggested by the client in the client hello and the + * highest supported by the server. + * + * @return the negotiated version of the TLS protocol in ServerHello message + */ + public int getServerVersion() { + return serverVersion; + } + + /** + * Returns the major version used in RSA premaster secret. + * + * @return the major version used in RSA premaster secret. + */ + public int getMajorVersion() { + if (rsaPreMasterSecretFix || clientVersion >= 0x0302) { + // 0x0302: TLSv1.1 + return (clientVersion >>> 8) & 0xFF; + } + + return (serverVersion >>> 8) & 0xFF; + } + + /** + * Returns the minor version used in RSA premaster secret. + * + * @return the minor version used in RSA premaster secret. + */ + public int getMinorVersion() { + if (rsaPreMasterSecretFix || clientVersion >= 0x0302) { + // 0x0302: TLSv1.1 + return clientVersion & 0xFF; + } + + return serverVersion & 0xFF; + } + + private int checkVersion(int version) { + if ((version < 0) || (version > 0xFFFF)) { + throw new IllegalArgumentException( + "Version must be between 0 and 65,535"); + } + return version; + } +} diff --git a/src/sun/security/jca/GetInstance.java b/src/sun/security/jca/GetInstance.java new file mode 100644 index 00000000..85ca272e --- /dev/null +++ b/src/sun/security/jca/GetInstance.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +import java.util.*; + +import java.security.*; +import java.security.Provider.Service; + +/** + * Collection of utility methods to facilitate implementing getInstance() + * methods in the JCA/JCE/JSSE/... framework. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public class GetInstance { + + private GetInstance() { + // empty + } + + /** + * Static inner class representing a newly created instance. + */ + public static final class Instance { + // public final fields, access directly without accessors + public final Provider provider; + public final Object impl; + private Instance(Provider provider, Object impl) { + this.provider = provider; + this.impl = impl; + } + // Return Provider and implementation as an array as used in the + // old Security.getImpl() methods. + public Object[] toArray() { + return new Object[] {impl, provider}; + } + } + + public static Service getService(String type, String algorithm) + throws NoSuchAlgorithmException { + ProviderList list = Providers.getProviderList(); + Service s = list.getService(type, algorithm); + if (s == null) { + throw new NoSuchAlgorithmException + (algorithm + " " + type + " not available"); + } + return s; + } + + public static Service getService(String type, String algorithm, + String provider) throws NoSuchAlgorithmException, + NoSuchProviderException { + if ((provider == null) || (provider.length() == 0)) { + throw new IllegalArgumentException("missing provider"); + } + Provider p = Providers.getProviderList().getProvider(provider); + if (p == null) { + throw new NoSuchProviderException("no such provider: " + provider); + } + Service s = p.getService(type, algorithm); + if (s == null) { + throw new NoSuchAlgorithmException("no such algorithm: " + + algorithm + " for provider " + provider); + } + return s; + } + + public static Service getService(String type, String algorithm, + Provider provider) throws NoSuchAlgorithmException { + if (provider == null) { + throw new IllegalArgumentException("missing provider"); + } + Service s = provider.getService(type, algorithm); + if (s == null) { + throw new NoSuchAlgorithmException("no such algorithm: " + + algorithm + " for provider " + provider.getName()); + } + return s; + } + + /** + * Return a List of all the available Services that implement + * (type, algorithm). Note that the list is initialized lazily + * and Provider loading and lookup is only trigered when + * necessary. + */ + public static List getServices(String type, String algorithm) { + ProviderList list = Providers.getProviderList(); + return list.getServices(type, algorithm); + } + + /** + * This method exists for compatibility with JCE only. It will be removed + * once JCE has been changed to use the replacement method. + * @deprecated use getServices(List) instead + */ + @Deprecated + public static List getServices(String type, + List algorithms) { + ProviderList list = Providers.getProviderList(); + return list.getServices(type, algorithms); + } + + /** + * Return a List of all the available Services that implement any of + * the specified algorithms. See getServices(String, String) for detals. + */ + public static List getServices(List ids) { + ProviderList list = Providers.getProviderList(); + return list.getServices(ids); + } + + /* + * For all the getInstance() methods below: + * @param type the type of engine (e.g. MessageDigest) + * @param clazz the Spi class that the implementation must subclass + * (e.g. MessageDigestSpi.class) or null if no superclass check + * is required + * @param algorithm the name of the algorithm (or alias), e.g. MD5 + * @param provider the provider (String or Provider object) + * @param param the parameter to pass to the Spi constructor + * (for CertStores) + * + * There are overloaded methods for all the permutations. + */ + + public static Instance getInstance(String type, Class clazz, + String algorithm) throws NoSuchAlgorithmException { + // in the almost all cases, the first service will work + // avoid taking long path if so + ProviderList list = Providers.getProviderList(); + Service firstService = list.getService(type, algorithm); + if (firstService == null) { + throw new NoSuchAlgorithmException + (algorithm + " " + type + " not available"); + } + NoSuchAlgorithmException failure; + try { + return getInstance(firstService, clazz); + } catch (NoSuchAlgorithmException e) { + failure = e; + } + // if we cannot get the service from the preferred provider, + // fail over to the next + for (Service s : list.getServices(type, algorithm)) { + if (s == firstService) { + // do not retry initial failed service + continue; + } + try { + return getInstance(s, clazz); + } catch (NoSuchAlgorithmException e) { + failure = e; + } + } + throw failure; + } + + public static Instance getInstance(String type, Class clazz, + String algorithm, Object param) throws NoSuchAlgorithmException { + List services = getServices(type, algorithm); + NoSuchAlgorithmException failure = null; + for (Service s : services) { + try { + return getInstance(s, clazz, param); + } catch (NoSuchAlgorithmException e) { + failure = e; + } + } + if (failure != null) { + throw failure; + } else { + throw new NoSuchAlgorithmException + (algorithm + " " + type + " not available"); + } + } + + public static Instance getInstance(String type, Class clazz, + String algorithm, String provider) throws NoSuchAlgorithmException, + NoSuchProviderException { + return getInstance(getService(type, algorithm, provider), clazz); + } + + public static Instance getInstance(String type, Class clazz, + String algorithm, Object param, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + return getInstance(getService(type, algorithm, provider), clazz, param); + } + + public static Instance getInstance(String type, Class clazz, + String algorithm, Provider provider) + throws NoSuchAlgorithmException { + return getInstance(getService(type, algorithm, provider), clazz); + } + + public static Instance getInstance(String type, Class clazz, + String algorithm, Object param, Provider provider) + throws NoSuchAlgorithmException { + return getInstance(getService(type, algorithm, provider), clazz, param); + } + + /* + * The two getInstance() methods below take a service. They are + * intended for classes that cannot use the standard methods, e.g. + * because they implement delayed provider selection like the + * Signature class. + */ + + public static Instance getInstance(Service s, Class clazz) + throws NoSuchAlgorithmException { + Object instance = s.newInstance(null); + checkSuperClass(s, instance.getClass(), clazz); + return new Instance(s.getProvider(), instance); + } + + public static Instance getInstance(Service s, Class clazz, + Object param) throws NoSuchAlgorithmException { + Object instance = s.newInstance(param); + checkSuperClass(s, instance.getClass(), clazz); + return new Instance(s.getProvider(), instance); + } + + /** + * Check is subClass is a subclass of superClass. If not, + * throw a NoSuchAlgorithmException. + */ + public static void checkSuperClass(Service s, Class subClass, + Class superClass) throws NoSuchAlgorithmException { + if (superClass == null) { + return; + } + if (superClass.isAssignableFrom(subClass) == false) { + throw new NoSuchAlgorithmException + ("class configured for " + s.getType() + ": " + + s.getClassName() + " not a " + s.getType()); + } + } + +} diff --git a/src/sun/security/jca/JCAUtil.java b/src/sun/security/jca/JCAUtil.java new file mode 100644 index 00000000..9b40d04c --- /dev/null +++ b/src/sun/security/jca/JCAUtil.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +import java.security.*; + +/** + * Collection of static utility methods used by the security framework. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class JCAUtil { + + private JCAUtil() { + // no instantiation + } + + // lock to use for synchronization + private static final Object LOCK = JCAUtil.class; + + // cached SecureRandom instance + private static volatile SecureRandom secureRandom; + + // size of the temporary arrays we use. Should fit into the CPU's 1st + // level cache and could be adjusted based on the platform + private final static int ARRAY_SIZE = 4096; + + /** + * Get the size of a temporary buffer array to use in order to be + * cache efficient. totalSize indicates the total amount of data to + * be buffered. Used by the engineUpdate(ByteBuffer) methods. + */ + public static int getTempArraySize(int totalSize) { + return Math.min(ARRAY_SIZE, totalSize); + } + + /** + * Get a SecureRandom instance. This method should me used by JDK + * internal code in favor of calling "new SecureRandom()". That needs to + * iterate through the provider table to find the default SecureRandom + * implementation, which is fairly inefficient. + */ + public static SecureRandom getSecureRandom() { + // we use double checked locking to minimize synchronization + // works because we use a volatile reference + SecureRandom r = secureRandom; + if (r == null) { + synchronized (LOCK) { + r = secureRandom; + if (r == null) { + r = new SecureRandom(); + secureRandom = r; + } + } + } + return r; + } + +} diff --git a/src/sun/security/jca/ProviderConfig.java b/src/sun/security/jca/ProviderConfig.java new file mode 100644 index 00000000..62f8bddc --- /dev/null +++ b/src/sun/security/jca/ProviderConfig.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +import java.io.File; +import java.lang.reflect.*; + +import java.security.*; + +import sun.security.util.PropertyExpander; + +/** + * Class representing a configured provider. Encapsulates configuration + * (className plus optional argument), the provider loading logic, and + * the loaded Provider object itself. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class ProviderConfig { + + private final static sun.security.util.Debug debug = + sun.security.util.Debug.getInstance("jca", "ProviderConfig"); + + // classname of the SunPKCS11-Solaris provider + private static final String P11_SOL_NAME = + "sun.security.pkcs11.SunPKCS11"; + + // config file argument of the SunPKCS11-Solaris provider + private static final String P11_SOL_ARG = + "${java.home}/lib/security/sunpkcs11-solaris.cfg"; + + // maximum number of times to try loading a provider before giving up + private final static int MAX_LOAD_TRIES = 30; + + // parameters for the Provider(String) constructor, + // use by doLoadProvider() + private final static Class[] CL_STRING = { String.class }; + + // name of the provider class + private final String className; + + // argument to the provider constructor, + // empty string indicates no-arg constructor + private final String argument; + + // number of times we have already tried to load this provider + private int tries; + + // Provider object, if loaded + private volatile Provider provider; + + // flag indicating if we are currently trying to load the provider + // used to detect recursion + private boolean isLoading; + + ProviderConfig(String className, String argument) { + if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) { + checkSunPKCS11Solaris(); + } + this.className = className; + this.argument = expand(argument); + } + + ProviderConfig(String className) { + this(className, ""); + } + + ProviderConfig(Provider provider) { + this.className = provider.getClass().getName(); + this.argument = ""; + this.provider = provider; + } + + // check if we should try to load the SunPKCS11-Solaris provider + // avoid if not available (pre Solaris 10) to reduce startup time + // or if disabled via system property + private void checkSunPKCS11Solaris() { + Boolean o = AccessController.doPrivileged( + new PrivilegedAction() { + public Boolean run() { + File file = new File("/usr/lib/libpkcs11.so"); + if (file.exists() == false) { + return Boolean.FALSE; + } + if ("false".equalsIgnoreCase(System.getProperty + ("sun.security.pkcs11.enable-solaris"))) { + return Boolean.FALSE; + } + return Boolean.TRUE; + } + }); + if (o == Boolean.FALSE) { + tries = MAX_LOAD_TRIES; + } + } + + private boolean hasArgument() { + return argument.length() != 0; + } + + // should we try to load this provider? + private boolean shouldLoad() { + return (tries < MAX_LOAD_TRIES); + } + + // do not try to load this provider again + private void disableLoad() { + tries = MAX_LOAD_TRIES; + } + + boolean isLoaded() { + return (provider != null); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ProviderConfig == false) { + return false; + } + ProviderConfig other = (ProviderConfig)obj; + return this.className.equals(other.className) + && this.argument.equals(other.argument); + } + + public int hashCode() { + return className.hashCode() + argument.hashCode(); + } + + public String toString() { + if (hasArgument()) { + return className + "('" + argument + "')"; + } else { + return className; + } + } + + /** + * Get the provider object. Loads the provider if it is not already loaded. + */ + synchronized Provider getProvider() { + // volatile variable load + Provider p = provider; + if (p != null) { + return p; + } + if (shouldLoad() == false) { + return null; + } + if (isLoading) { + // because this method is synchronized, this can only + // happen if there is recursion. + if (debug != null) { + debug.println("Recursion loading provider: " + this); + new Exception("Call trace").printStackTrace(); + } + return null; + } + try { + isLoading = true; + tries++; + p = doLoadProvider(); + } finally { + isLoading = false; + } + provider = p; + return p; + } + + /** + * Load and instantiate the Provider described by this class. + * + * NOTE use of doPrivileged(). + * + * @return null if the Provider could not be loaded + * + * @throws ProviderException if executing the Provider's constructor + * throws a ProviderException. All other Exceptions are ignored. + */ + private Provider doLoadProvider() { + return AccessController.doPrivileged(new PrivilegedAction() { + public Provider run() { + if (debug != null) { + debug.println("Loading provider: " + ProviderConfig.this); + } + try { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + Class provClass; + if (cl != null) { + provClass = cl.loadClass(className); + } else { + provClass = Class.forName(className); + } + Object obj; + if (hasArgument() == false) { + obj = provClass.newInstance(); + } else { + Constructor cons = provClass.getConstructor(CL_STRING); + obj = cons.newInstance(argument); + } + if (obj instanceof Provider) { + if (debug != null) { + debug.println("Loaded provider " + obj); + } + return (Provider)obj; + } else { + if (debug != null) { + debug.println(className + " is not a provider"); + } + disableLoad(); + return null; + } + } catch (Exception e) { + Throwable t; + if (e instanceof InvocationTargetException) { + t = ((InvocationTargetException)e).getCause(); + } else { + t = e; + } + if (debug != null) { + debug.println("Error loading provider " + ProviderConfig.this); + t.printStackTrace(); + } + // provider indicates fatal error, pass through exception + if (t instanceof ProviderException) { + throw (ProviderException)t; + } + // provider indicates that loading should not be retried + if (t instanceof UnsupportedOperationException) { + disableLoad(); + } + return null; + } + } + }); + } + + /** + * Perform property expansion of the provider value. + * + * NOTE use of doPrivileged(). + */ + private static String expand(final String value) { + // shortcut if value does not contain any properties + if (value.contains("${") == false) { + return value; + } + return AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + try { + return PropertyExpander.expand(value); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + }); + } + +} diff --git a/src/sun/security/jca/ProviderList.java b/src/sun/security/jca/ProviderList.java new file mode 100644 index 00000000..66c82624 --- /dev/null +++ b/src/sun/security/jca/ProviderList.java @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +import java.util.*; + +import java.security.*; +import java.security.Provider.Service; + +/** + * List of Providers. Used to represent the provider preferences. + * + * The system starts out with a ProviderList that only has the classNames + * of the Providers. Providers are loaded on demand only when needed. + * + * For compatibility reasons, Providers that could not be loaded are ignored + * and internally presented as the instance EMPTY_PROVIDER. However, those + * objects cannot be presented to applications. Call the convert() method + * to force all Providers to be loaded and to obtain a ProviderList with + * invalid entries removed. All this is handled by the Security class. + * + * Note that all indices used by this class are 0-based per general Java + * convention. These must be converted to the 1-based indices used by the + * Security class externally when needed. + * + * Instances of this class are immutable. This eliminates the need for + * cloning and synchronization in consumers. The add() and remove() style + * methods are static in order to avoid confusion about the immutability. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class ProviderList { + + final static sun.security.util.Debug debug = + sun.security.util.Debug.getInstance("jca", "ProviderList"); + + private final static ProviderConfig[] PC0 = new ProviderConfig[0]; + + private final static Provider[] P0 = new Provider[0]; + + // constant for an ProviderList with no elements + static final ProviderList EMPTY = new ProviderList(PC0, true); + + // dummy provider object to use during initialization + // used to avoid explicit null checks in various places + private static final Provider EMPTY_PROVIDER = + new Provider("##Empty##", 1.0d, "initialization in progress") { + private static final long serialVersionUID = 1151354171352296389L; + // override getService() to return null slightly faster + public Service getService(String type, String algorithm) { + return null; + } + }; + + // construct a ProviderList from the security properties + // (static provider configuration in the java.security file) + static ProviderList fromSecurityProperties() { + // doPrivileged() because of Security.getProperty() + return AccessController.doPrivileged( + new PrivilegedAction() { + public ProviderList run() { + return new ProviderList(); + } + }); + } + + public static ProviderList add(ProviderList providerList, Provider p) { + return insertAt(providerList, p, -1); + } + + public static ProviderList insertAt(ProviderList providerList, Provider p, + int position) { + if (providerList.getProvider(p.getName()) != null) { + return providerList; + } + List list = new ArrayList<> + (Arrays.asList(providerList.configs)); + int n = list.size(); + if ((position < 0) || (position > n)) { + position = n; + } + list.add(position, new ProviderConfig(p)); + return new ProviderList(list.toArray(PC0), true); + } + + public static ProviderList remove(ProviderList providerList, String name) { + // make sure provider exists + if (providerList.getProvider(name) == null) { + return providerList; + } + // copy all except matching to new list + ProviderConfig[] configs = new ProviderConfig[providerList.size() - 1]; + int j = 0; + for (ProviderConfig config : providerList.configs) { + if (config.getProvider().getName().equals(name) == false) { + configs[j++] = config; + } + } + return new ProviderList(configs, true); + } + + // Create a new ProviderList from the specified Providers. + // This method is for use by SunJSSE. + public static ProviderList newList(Provider ... providers) { + ProviderConfig[] configs = new ProviderConfig[providers.length]; + for (int i = 0; i < providers.length; i++) { + configs[i] = new ProviderConfig(providers[i]); + } + return new ProviderList(configs, true); + } + + // configuration of the providers + private final ProviderConfig[] configs; + + // flag indicating whether all configs have been loaded successfully + private volatile boolean allLoaded; + + // List returned by providers() + private final List userList = new AbstractList() { + public int size() { + return configs.length; + } + public Provider get(int index) { + return getProvider(index); + } + }; + + /** + * Create a new ProviderList from an array of configs + */ + private ProviderList(ProviderConfig[] configs, boolean allLoaded) { + this.configs = configs; + this.allLoaded = allLoaded; + } + + /** + * Return a new ProviderList parsed from the java.security Properties. + */ + private ProviderList() { + List configList = new ArrayList<>(); + for (int i = 1; true; i++) { + String entry = Security.getProperty("security.provider." + i); + if (entry == null) { + break; + } + entry = entry.trim(); + if (entry.length() == 0) { + System.err.println("invalid entry for " + + "security.provider." + i); + break; + } + int k = entry.indexOf(' '); + ProviderConfig config; + if (k == -1) { + config = new ProviderConfig(entry); + } else { + String className = entry.substring(0, k); + String argument = entry.substring(k + 1).trim(); + config = new ProviderConfig(className, argument); + } + + // Get rid of duplicate providers. + if (configList.contains(config) == false) { + configList.add(config); + } + } + configs = configList.toArray(PC0); + if (debug != null) { + debug.println("provider configuration: " + configList); + } + } + + /** + * Construct a special ProviderList for JAR verification. It consists + * of the providers specified via jarClassNames, which must be on the + * bootclasspath and cannot be in signed JAR files. This is to avoid + * possible recursion and deadlock during verification. + */ + ProviderList getJarList(String[] jarClassNames) { + List newConfigs = new ArrayList<>(); + for (String className : jarClassNames) { + ProviderConfig newConfig = new ProviderConfig(className); + for (ProviderConfig config : configs) { + // if the equivalent object is present in this provider list, + // use the old object rather than the new object. + // this ensures that when the provider is loaded in the + // new thread local list, it will also become available + // in this provider list + if (config.equals(newConfig)) { + newConfig = config; + break; + } + } + newConfigs.add(newConfig); + } + ProviderConfig[] configArray = newConfigs.toArray(PC0); + return new ProviderList(configArray, false); + } + + public int size() { + return configs.length; + } + + /** + * Return the Provider at the specified index. Returns EMPTY_PROVIDER + * if the provider could not be loaded at this time. + */ + Provider getProvider(int index) { + Provider p = configs[index].getProvider(); + return (p != null) ? p : EMPTY_PROVIDER; + } + + /** + * Return an unmodifiable List of all Providers in this List. The + * individual Providers are loaded on demand. Elements that could not + * be initialized are replaced with EMPTY_PROVIDER. + */ + public List providers() { + return userList; + } + + private ProviderConfig getProviderConfig(String name) { + int index = getIndex(name); + return (index != -1) ? configs[index] : null; + } + + // return the Provider with the specified name or null + public Provider getProvider(String name) { + ProviderConfig config = getProviderConfig(name); + return (config == null) ? null : config.getProvider(); + } + + /** + * Return the index at which the provider with the specified name is + * installed or -1 if it is not present in this ProviderList. + */ + public int getIndex(String name) { + for (int i = 0; i < configs.length; i++) { + Provider p = getProvider(i); + if (p.getName().equals(name)) { + return i; + } + } + return -1; + } + + // attempt to load all Providers not already loaded + private int loadAll() { + if (allLoaded) { + return configs.length; + } + if (debug != null) { + debug.println("Loading all providers"); + new Exception("Call trace").printStackTrace(); + } + int n = 0; + for (int i = 0; i < configs.length; i++) { + Provider p = configs[i].getProvider(); + if (p != null) { + n++; + } + } + if (n == configs.length) { + allLoaded = true; + } + return n; + } + + /** + * Try to load all Providers and return the ProviderList. If one or + * more Providers could not be loaded, a new ProviderList with those + * entries removed is returned. Otherwise, the method returns this. + */ + ProviderList removeInvalid() { + int n = loadAll(); + if (n == configs.length) { + return this; + } + ProviderConfig[] newConfigs = new ProviderConfig[n]; + for (int i = 0, j = 0; i < configs.length; i++) { + ProviderConfig config = configs[i]; + if (config.isLoaded()) { + newConfigs[j++] = config; + } + } + return new ProviderList(newConfigs, true); + } + + // return the providers as an array + public Provider[] toArray() { + return providers().toArray(P0); + } + + // return a String representation of this ProviderList + public String toString() { + return Arrays.asList(configs).toString(); + } + + /** + * Return a Service describing an implementation of the specified + * algorithm from the Provider with the highest precedence that + * supports that algorithm. Return null if no Provider supports this + * algorithm. + */ + public Service getService(String type, String name) { + for (int i = 0; i < configs.length; i++) { + Provider p = getProvider(i); + Service s = p.getService(type, name); + if (s != null) { + return s; + } + } + return null; + } + + /** + * Return a List containing all the Services describing implementations + * of the specified algorithms in precedence order. If no implementation + * exists, this method returns an empty List. + * + * The elements of this list are determined lazily on demand. + * + * The List returned is NOT thread safe. + */ + public List getServices(String type, String algorithm) { + return new ServiceList(type, algorithm); + } + + /** + * This method exists for compatibility with JCE only. It will be removed + * once JCE has been changed to use the replacement method. + * @deprecated use getServices(List) instead + */ + @Deprecated + public List getServices(String type, List algorithms) { + List ids = new ArrayList<>(); + for (String alg : algorithms) { + ids.add(new ServiceId(type, alg)); + } + return getServices(ids); + } + + public List getServices(List ids) { + return new ServiceList(ids); + } + + /** + * Inner class for a List of Services. Custom List implementation in + * order to delay Provider initialization and lookup. + * Not thread safe. + */ + private final class ServiceList extends AbstractList { + + // type and algorithm for simple lookup + // avoid allocating/traversing the ServiceId list for these lookups + private final String type; + private final String algorithm; + + // list of ids for parallel lookup + // if ids is non-null, type and algorithm are null + private final List ids; + + // first service we have found + // it is stored in a separate variable so that we can avoid + // allocating the services list if we do not need the second service. + // this is the case if we don't failover (failovers are typically rare) + private Service firstService; + + // list of the services we have found so far + private List services; + + // index into config[] of the next provider we need to query + private int providerIndex; + + ServiceList(String type, String algorithm) { + this.type = type; + this.algorithm = algorithm; + this.ids = null; + } + + ServiceList(List ids) { + this.type = null; + this.algorithm = null; + this.ids = ids; + } + + private void addService(Service s) { + if (firstService == null) { + firstService = s; + } else { + if (services == null) { + services = new ArrayList(4); + services.add(firstService); + } + services.add(s); + } + } + + private Service tryGet(int index) { + while (true) { + if ((index == 0) && (firstService != null)) { + return firstService; + } else if ((services != null) && (services.size() > index)) { + return services.get(index); + } + if (providerIndex >= configs.length) { + return null; + } + // check all algorithms in this provider before moving on + Provider p = getProvider(providerIndex++); + if (type != null) { + // simple lookup + Service s = p.getService(type, algorithm); + if (s != null) { + addService(s); + } + } else { + // parallel lookup + for (ServiceId id : ids) { + Service s = p.getService(id.type, id.algorithm); + if (s != null) { + addService(s); + } + } + } + } + } + + public Service get(int index) { + Service s = tryGet(index); + if (s == null) { + throw new IndexOutOfBoundsException(); + } + return s; + } + + public int size() { + int n; + if (services != null) { + n = services.size(); + } else { + n = (firstService != null) ? 1 : 0; + } + while (tryGet(n) != null) { + n++; + } + return n; + } + + // override isEmpty() and iterator() to not call size() + // this avoids loading + checking all Providers + + public boolean isEmpty() { + return (tryGet(0) == null); + } + + public Iterator iterator() { + return new Iterator() { + int index; + + public boolean hasNext() { + return tryGet(index) != null; + } + + public Service next() { + Service s = tryGet(index); + if (s == null) { + throw new NoSuchElementException(); + } + index++; + return s; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + +} diff --git a/src/sun/security/jca/Providers.java b/src/sun/security/jca/Providers.java new file mode 100644 index 00000000..b0c39bf1 --- /dev/null +++ b/src/sun/security/jca/Providers.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +import java.security.Provider; + +/** + * Collection of methods to get and set provider list. Also includes + * special code for the provider list during JAR verification. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public class Providers { + + private static final ThreadLocal threadLists = + new InheritableThreadLocal<>(); + + // number of threads currently using thread-local provider lists + // tracked to allow an optimization if == 0 + private static volatile int threadListsUsed; + + // current system-wide provider list + // Note volatile immutable object, so no synchronization needed. + private static volatile ProviderList providerList; + + static { + // set providerList to empty list first in case initialization somehow + // triggers a getInstance() call (although that should not happen) + providerList = ProviderList.EMPTY; + providerList = ProviderList.fromSecurityProperties(); + } + + private Providers() { + // empty + } + + // we need special handling to resolve circularities when loading + // signed JAR files during startup. The code below is part of that. + + // Basically, before we load data from a signed JAR file, we parse + // the PKCS#7 file and verify the signature. We need a + // CertificateFactory, Signatures, etc. to do that. We have to make + // sure that we do not try to load the implementation from the JAR + // file we are just verifying. + // + // To avoid that, we use different provider settings during JAR + // verification. However, we do not want those provider settings to + // interfere with other parts of the system. Therefore, we make them local + // to the Thread executing the JAR verification code. + // + // The code here is used by sun.security.util.SignatureFileVerifier. + // See there for details. + + private static final String BACKUP_PROVIDER_CLASSNAME = + "sun.security.provider.VerificationProvider"; + + // Hardcoded classnames of providers to use for JAR verification. + // MUST NOT be on the bootclasspath and not in signed JAR files. + private static final String[] jarVerificationProviders = { + "sun.security.provider.Sun", + "sun.security.rsa.SunRsaSign", + // Note: SunEC *is* in a signed JAR file, but it's not signed + // by EC itself. So it's still safe to be listed here. + "sun.security.ec.SunEC", + BACKUP_PROVIDER_CLASSNAME, + }; + + // Return to Sun provider or its backup. + // This method should only be called by + // sun.security.util.ManifestEntryVerifier and java.security.SecureRandom. + public static Provider getSunProvider() { + try { + Class clazz = Class.forName(jarVerificationProviders[0]); + return (Provider)clazz.newInstance(); + } catch (Exception e) { + try { + Class clazz = Class.forName(BACKUP_PROVIDER_CLASSNAME); + return (Provider)clazz.newInstance(); + } catch (Exception ee) { + throw new RuntimeException("Sun provider not found", e); + } + } + } + + /** + * Start JAR verification. This sets a special provider list for + * the current thread. You MUST save the return value from this + * method and you MUST call stopJarVerification() with that object + * once you are done. + */ + public static Object startJarVerification() { + ProviderList currentList = getProviderList(); + ProviderList jarList = currentList.getJarList(jarVerificationProviders); + // return the old thread-local provider list, usually null + return beginThreadProviderList(jarList); + } + + /** + * Stop JAR verification. Call once you have completed JAR verification. + */ + public static void stopJarVerification(Object obj) { + // restore old thread-local provider list + endThreadProviderList((ProviderList)obj); + } + + /** + * Return the current ProviderList. If the thread-local list is set, + * it is returned. Otherwise, the system wide list is returned. + */ + public static ProviderList getProviderList() { + ProviderList list = getThreadProviderList(); + if (list == null) { + list = getSystemProviderList(); + } + return list; + } + + /** + * Set the current ProviderList. Affects the thread-local list if set, + * otherwise the system wide list. + */ + public static void setProviderList(ProviderList newList) { + if (getThreadProviderList() == null) { + setSystemProviderList(newList); + } else { + changeThreadProviderList(newList); + } + } + + /** + * Get the full provider list with invalid providers (those that + * could not be loaded) removed. This is the list we need to + * present to applications. + */ + public static ProviderList getFullProviderList() { + ProviderList list; + synchronized (Providers.class) { + list = getThreadProviderList(); + if (list != null) { + ProviderList newList = list.removeInvalid(); + if (newList != list) { + changeThreadProviderList(newList); + list = newList; + } + return list; + } + } + list = getSystemProviderList(); + ProviderList newList = list.removeInvalid(); + if (newList != list) { + setSystemProviderList(newList); + list = newList; + } + return list; + } + + private static ProviderList getSystemProviderList() { + return providerList; + } + + private static void setSystemProviderList(ProviderList list) { + providerList = list; + } + + public static ProviderList getThreadProviderList() { + // avoid accessing the threadlocal if none are currently in use + // (first use of ThreadLocal.get() for a Thread allocates a Map) + if (threadListsUsed == 0) { + return null; + } + return threadLists.get(); + } + + // Change the thread local provider list. Use only if the current thread + // is already using a thread local list and you want to change it in place. + // In other cases, use the begin/endThreadProviderList() methods. + private static void changeThreadProviderList(ProviderList list) { + threadLists.set(list); + } + + /** + * Methods to manipulate the thread local provider list. It is for use by + * JAR verification (see above) and the SunJSSE FIPS mode only. + * + * It should be used as follows: + * + * ProviderList list = ...; + * ProviderList oldList = Providers.beginThreadProviderList(list); + * try { + * // code that needs thread local provider list + * } finally { + * Providers.endThreadProviderList(oldList); + * } + * + */ + + public static synchronized ProviderList beginThreadProviderList(ProviderList list) { + if (ProviderList.debug != null) { + ProviderList.debug.println("ThreadLocal providers: " + list); + } + ProviderList oldList = threadLists.get(); + threadListsUsed++; + threadLists.set(list); + return oldList; + } + + public static synchronized void endThreadProviderList(ProviderList list) { + if (list == null) { + if (ProviderList.debug != null) { + ProviderList.debug.println("Disabling ThreadLocal providers"); + } + threadLists.remove(); + } else { + if (ProviderList.debug != null) { + ProviderList.debug.println + ("Restoring previous ThreadLocal providers: " + list); + } + threadLists.set(list); + } + threadListsUsed--; + } + +} diff --git a/src/sun/security/jca/ServiceId.java b/src/sun/security/jca/ServiceId.java new file mode 100644 index 00000000..34e047d9 --- /dev/null +++ b/src/sun/security/jca/ServiceId.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jca; + +/** + * Simple class encapsulating a service type and algorithm for lookup. + * Put in a separate file rather than nested to allow import via ...jca.*. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class ServiceId { + + public final String type; + public final String algorithm; + + public ServiceId(String type, String algorithm) { + this.type = type; + this.algorithm = algorithm; + } + +} diff --git a/src/sun/security/jgss/GSSCaller.java b/src/sun/security/jgss/GSSCaller.java new file mode 100644 index 00000000..c0610787 --- /dev/null +++ b/src/sun/security/jgss/GSSCaller.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +/** + * Denotes what client is calling the JGSS-API. The object can be sent deep + * into the mechanism level so that special actions can be performed for + * different callers. + */ +public class GSSCaller { + public static final GSSCaller CALLER_UNKNOWN = new GSSCaller("UNKNOWN"); + public static final GSSCaller CALLER_INITIATE = new GSSCaller("INITIATE"); + public static final GSSCaller CALLER_ACCEPT = new GSSCaller("ACCEPT"); + public static final GSSCaller CALLER_SSL_CLIENT = new GSSCaller("SSL_CLIENT"); + public static final GSSCaller CALLER_SSL_SERVER = new GSSCaller("SSL_SERVER"); + + private String name; + GSSCaller(String s) { + name = s; + } + @Override + public String toString() { + return "GSSCaller{" + name + '}'; + } +} + diff --git a/src/sun/security/jgss/GSSContextImpl.java b/src/sun/security/jgss/GSSContextImpl.java new file mode 100644 index 00000000..b25987ff --- /dev/null +++ b/src/sun/security/jgss/GSSContextImpl.java @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.*; +import sun.security.jgss.spi.*; +import sun.security.util.ObjectIdentifier; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import com.sun.security.jgss.*; + +/** + * This class represents the JGSS security context and its associated + * operations. JGSS security contexts are established between + * peers using locally established credentials. Multiple contexts + * may exist simultaneously between a pair of peers, using the same + * or different set of credentials. The JGSS is independent of + * the underlying transport protocols and depends on its callers to + * transport the tokens between peers. + *

    + * The context object can be thought of as having 3 implicit states: + * before it is established, during its context establishment, and + * after a fully established context exists. + *

    + * Before the context establishment phase is initiated, the context + * initiator may request specific characteristics desired of the + * established context. These can be set using the set methods. After the + * context is established, the caller can check the actual characteristic + * and services offered by the context using the query methods. + *

    + * The context establishment phase begins with the first call to the + * initSecContext method by the context initiator. During this phase the + * initSecContext and acceptSecContext methods will produce GSS-API + * authentication tokens which the calling application needs to send to its + * peer. The initSecContext and acceptSecContext methods may + * return a CONTINUE_NEEDED code which indicates that a token is needed + * from its peer in order to continue the context establishment phase. A + * return code of COMPLETE signals that the local end of the context is + * established. This may still require that a token be sent to the peer, + * depending if one is produced by GSS-API. The isEstablished method can + * also be used to determine if the local end of the context has been + * fully established. During the context establishment phase, the + * isProtReady method may be called to determine if the context can be + * used for the per-message operations. This allows implementation to + * use per-message operations on contexts which aren't fully established. + *

    + * After the context has been established or the isProtReady method + * returns "true", the query routines can be invoked to determine the actual + * characteristics and services of the established context. The + * application can also start using the per-message methods of wrap and + * getMIC to obtain cryptographic operations on application supplied data. + *

    + * When the context is no longer needed, the application should call + * dispose to release any system resources the context may be using. + *

    RFC 2078 + *
    This class corresponds to the context level calls together with + * the per message calls of RFC 2078. The gss_init_sec_context and + * gss_accept_sec_context calls have been made simpler by only taking + * required parameters. The context can have its properties set before + * the first call to initSecContext. The supplementary status codes for the + * per-message operations are returned in an instance of the MessageProp + * class, which is used as an argument in these calls.
    + */ +class GSSContextImpl implements ExtendedGSSContext { + + private final GSSManagerImpl gssManager; + private final boolean initiator; + + // private flags for the context state + private static final int PRE_INIT = 1; + private static final int IN_PROGRESS = 2; + private static final int READY = 3; + private static final int DELETED = 4; + + // instance variables + private int currentState = PRE_INIT; + + private GSSContextSpi mechCtxt = null; + private Oid mechOid = null; + private ObjectIdentifier objId = null; + + private GSSCredentialImpl myCred = null; + + private GSSNameImpl srcName = null; + private GSSNameImpl targName = null; + + private int reqLifetime = INDEFINITE_LIFETIME; + private ChannelBinding channelBindings = null; + + private boolean reqConfState = true; + private boolean reqIntegState = true; + private boolean reqMutualAuthState = true; + private boolean reqReplayDetState = true; + private boolean reqSequenceDetState = true; + private boolean reqCredDelegState = false; + private boolean reqAnonState = false; + private boolean reqDelegPolicyState = false; + + /** + * Creates a GSSContextImp on the context initiator's side. + */ + public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer, Oid mech, + GSSCredential myCred, int lifetime) + throws GSSException { + if ((peer == null) || !(peer instanceof GSSNameImpl)) { + throw new GSSException(GSSException.BAD_NAME); + } + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + this.gssManager = gssManager; + this.myCred = (GSSCredentialImpl) myCred; // XXX Check first + reqLifetime = lifetime; + targName = (GSSNameImpl)peer; + this.mechOid = mech; + initiator = true; + } + + /** + * Creates a GSSContextImpl on the context acceptor's side. + */ + public GSSContextImpl(GSSManagerImpl gssManager, GSSCredential myCred) + throws GSSException { + this.gssManager = gssManager; + this.myCred = (GSSCredentialImpl) myCred; // XXX Check first + initiator = false; + } + + /** + * Creates a GSSContextImpl out of a previously exported + * GSSContext. + * + * @see #isTransferable + */ + public GSSContextImpl(GSSManagerImpl gssManager, byte[] interProcessToken) + throws GSSException { + this.gssManager = gssManager; + mechCtxt = gssManager.getMechanismContext(interProcessToken); + initiator = mechCtxt.isInitiator(); + this.mechOid = mechCtxt.getMech(); + } + + public byte[] initSecContext(byte inputBuf[], int offset, int len) + throws GSSException { + /* + * Size of ByteArrayOutputStream will double each time that extra + * bytes are to be written. Usually, without delegation, a GSS + * initial token containing the Kerberos AP-REQ is between 400 and + * 600 bytes. + */ + ByteArrayOutputStream bos = new ByteArrayOutputStream(600); + ByteArrayInputStream bin = + new ByteArrayInputStream(inputBuf, offset, len); + int size = initSecContext(bin, bos); + return (size == 0? null : bos.toByteArray()); + } + + public int initSecContext(InputStream inStream, + OutputStream outStream) throws GSSException { + + if (mechCtxt != null && currentState != IN_PROGRESS) { + throw new GSSExceptionImpl(GSSException.FAILURE, + "Illegal call to initSecContext"); + } + + GSSHeader gssHeader = null; + int inTokenLen = -1; + GSSCredentialSpi credElement = null; + boolean firstToken = false; + + try { + if (mechCtxt == null) { + if (myCred != null) { + try { + credElement = myCred.getElement(mechOid, true); + } catch (GSSException ge) { + if (GSSUtil.isSpNegoMech(mechOid) && + ge.getMajor() == GSSException.NO_CRED) { + credElement = myCred.getElement + (myCred.getMechs()[0], true); + } else { + throw ge; + } + } + } + GSSNameSpi nameElement = targName.getElement(mechOid); + mechCtxt = gssManager.getMechanismContext(nameElement, + credElement, + reqLifetime, + mechOid); + mechCtxt.requestConf(reqConfState); + mechCtxt.requestInteg(reqIntegState); + mechCtxt.requestCredDeleg(reqCredDelegState); + mechCtxt.requestMutualAuth(reqMutualAuthState); + mechCtxt.requestReplayDet(reqReplayDetState); + mechCtxt.requestSequenceDet(reqSequenceDetState); + mechCtxt.requestAnonymity(reqAnonState); + mechCtxt.setChannelBinding(channelBindings); + mechCtxt.requestDelegPolicy(reqDelegPolicyState); + + objId = new ObjectIdentifier(mechOid.toString()); + + currentState = IN_PROGRESS; + firstToken = true; + } else { + if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || + GSSUtil.isSpNegoMech(mechOid)) { + // do not parse GSS header for native provider or SPNEGO + // mech + } else { + // parse GSS header + gssHeader = new GSSHeader(inStream); + if (!gssHeader.getOid().equals((Object) objId)) + throw new GSSExceptionImpl + (GSSException.DEFECTIVE_TOKEN, + "Mechanism not equal to " + + mechOid.toString() + + " in initSecContext token"); + inTokenLen = gssHeader.getMechTokenLength(); + } + } + + byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen); + + int retVal = 0; + + if (obuf != null) { + retVal = obuf.length; + if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || + (!firstToken && GSSUtil.isSpNegoMech(mechOid))) { + // do not add GSS header for native provider or SPNEGO + // except for the first SPNEGO token + } else { + // add GSS header + gssHeader = new GSSHeader(objId, obuf.length); + retVal += gssHeader.encode(outStream); + } + outStream.write(obuf); + } + + if (mechCtxt.isEstablished()) + currentState = READY; + + return retVal; + + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, + e.getMessage()); + } + } + + public byte[] acceptSecContext(byte inTok[], int offset, int len) + throws GSSException { + + /* + * Usually initial GSS token containing a Kerberos AP-REP is less + * than 100 bytes. + */ + ByteArrayOutputStream bos = new ByteArrayOutputStream(100); + acceptSecContext(new ByteArrayInputStream(inTok, offset, len), + bos); + byte[] out = bos.toByteArray(); + return (out.length == 0) ? null : out; + } + + public void acceptSecContext(InputStream inStream, + OutputStream outStream) throws GSSException { + + if (mechCtxt != null && currentState != IN_PROGRESS) { + throw new GSSExceptionImpl(GSSException.FAILURE, + "Illegal call to acceptSecContext"); + } + + GSSHeader gssHeader = null; + int inTokenLen = -1; + GSSCredentialSpi credElement = null; + + try { + if (mechCtxt == null) { + // mechOid will be null for an acceptor's context + gssHeader = new GSSHeader(inStream); + inTokenLen = gssHeader.getMechTokenLength(); + + /* + * Convert ObjectIdentifier to Oid + */ + objId = gssHeader.getOid(); + mechOid = new Oid(objId.toString()); + // System.out.println("Entered GSSContextImpl.acceptSecContext" + // + " with mechanism = " + mechOid); + if (myCred != null) { + credElement = myCred.getElement(mechOid, false); + } + + mechCtxt = gssManager.getMechanismContext(credElement, + mechOid); + mechCtxt.setChannelBinding(channelBindings); + + currentState = IN_PROGRESS; + } else { + if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || + (GSSUtil.isSpNegoMech(mechOid))) { + // do not parse GSS header for native provider and SPNEGO + } else { + // parse GSS Header + gssHeader = new GSSHeader(inStream); + if (!gssHeader.getOid().equals((Object) objId)) + throw new GSSExceptionImpl + (GSSException.DEFECTIVE_TOKEN, + "Mechanism not equal to " + + mechOid.toString() + + " in acceptSecContext token"); + inTokenLen = gssHeader.getMechTokenLength(); + } + } + + byte[] obuf = mechCtxt.acceptSecContext(inStream, inTokenLen); + + if (obuf != null) { + int retVal = obuf.length; + if (mechCtxt.getProvider().getName().equals("SunNativeGSS") || + (GSSUtil.isSpNegoMech(mechOid))) { + // do not add GSS header for native provider and SPNEGO + } else { + // add GSS header + gssHeader = new GSSHeader(objId, obuf.length); + retVal += gssHeader.encode(outStream); + } + outStream.write(obuf); + } + + if (mechCtxt.isEstablished()) { + currentState = READY; + } + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, + e.getMessage()); + } + } + + public boolean isEstablished() { + if (mechCtxt == null) + return false; + else + return (currentState == READY); + } + + public int getWrapSizeLimit(int qop, boolean confReq, + int maxTokenSize) throws GSSException { + if (mechCtxt != null) + return mechCtxt.getWrapSizeLimit(qop, confReq, maxTokenSize); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public byte[] wrap(byte inBuf[], int offset, int len, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + return mechCtxt.wrap(inBuf, offset, len, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public void wrap(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + mechCtxt.wrap(inStream, outStream, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public byte [] unwrap(byte[] inBuf, int offset, int len, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + return mechCtxt.unwrap(inBuf, offset, len, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public void unwrap(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + mechCtxt.unwrap(inStream, outStream, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public byte[] getMIC(byte []inMsg, int offset, int len, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + return mechCtxt.getMIC(inMsg, offset, len, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public void getMIC(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + mechCtxt.getMIC(inStream, outStream, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public void verifyMIC(byte[] inTok, int tokOffset, int tokLen, + byte[] inMsg, int msgOffset, int msgLen, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + mechCtxt.verifyMIC(inTok, tokOffset, tokLen, + inMsg, msgOffset, msgLen, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public void verifyMIC(InputStream tokStream, InputStream msgStream, + MessageProp msgProp) throws GSSException { + if (mechCtxt != null) + mechCtxt.verifyMIC(tokStream, msgStream, msgProp); + else + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + } + + public byte[] export() throws GSSException { + // Defaults to null to match old behavior + byte[] result = null; + // Only allow context export from native provider since JGSS + // still has not defined its own interprocess token format + if (mechCtxt.isTransferable() && + mechCtxt.getProvider().getName().equals("SunNativeGSS")) { + result = mechCtxt.export(); + } + return result; + } + + public void requestMutualAuth(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqMutualAuthState = state; + } + + public void requestReplayDet(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqReplayDetState = state; + } + + public void requestSequenceDet(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqSequenceDetState = state; + } + + public void requestCredDeleg(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqCredDelegState = state; + } + + public void requestAnonymity(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqAnonState = state; + } + + public void requestConf(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqConfState = state; + } + + public void requestInteg(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqIntegState = state; + } + + public void requestLifetime(int lifetime) throws GSSException { + if (mechCtxt == null && initiator) + reqLifetime = lifetime; + } + + public void setChannelBinding(ChannelBinding channelBindings) + throws GSSException { + + if (mechCtxt == null) + this.channelBindings = channelBindings; + + } + + public boolean getCredDelegState() { + if (mechCtxt != null) + return mechCtxt.getCredDelegState(); + else + return reqCredDelegState; + } + + public boolean getMutualAuthState() { + if (mechCtxt != null) + return mechCtxt.getMutualAuthState(); + else + return reqMutualAuthState; + } + + public boolean getReplayDetState() { + if (mechCtxt != null) + return mechCtxt.getReplayDetState(); + else + return reqReplayDetState; + } + + public boolean getSequenceDetState() { + if (mechCtxt != null) + return mechCtxt.getSequenceDetState(); + else + return reqSequenceDetState; + } + + public boolean getAnonymityState() { + if (mechCtxt != null) + return mechCtxt.getAnonymityState(); + else + return reqAnonState; + } + + public boolean isTransferable() throws GSSException { + if (mechCtxt != null) + return mechCtxt.isTransferable(); + else + return false; + } + + public boolean isProtReady() { + if (mechCtxt != null) + return mechCtxt.isProtReady(); + else + return false; + } + + public boolean getConfState() { + if (mechCtxt != null) + return mechCtxt.getConfState(); + else + return reqConfState; + } + + public boolean getIntegState() { + if (mechCtxt != null) + return mechCtxt.getIntegState(); + else + return reqIntegState; + } + + public int getLifetime() { + if (mechCtxt != null) + return mechCtxt.getLifetime(); + else + return reqLifetime; + } + + public GSSName getSrcName() throws GSSException { + if (srcName == null) { + srcName = GSSNameImpl.wrapElement + (gssManager, mechCtxt.getSrcName()); + } + return srcName; + } + + public GSSName getTargName() throws GSSException { + if (targName == null) { + targName = GSSNameImpl.wrapElement + (gssManager, mechCtxt.getTargName()); + } + return targName; + } + + public Oid getMech() throws GSSException { + if (mechCtxt != null) { + return mechCtxt.getMech(); + } + return mechOid; + } + + public GSSCredential getDelegCred() throws GSSException { + + if (mechCtxt == null) + throw new GSSExceptionImpl(GSSException.NO_CONTEXT, + "No mechanism context yet!"); + GSSCredentialSpi delCredElement = mechCtxt.getDelegCred(); + return (delCredElement == null ? + null : new GSSCredentialImpl(gssManager, delCredElement)); + } + + public boolean isInitiator() throws GSSException { + return initiator; + } + + public void dispose() throws GSSException { + currentState = DELETED; + if (mechCtxt != null) { + mechCtxt.dispose(); + mechCtxt = null; + } + myCred = null; + srcName = null; + targName = null; + } + + // ExtendedGSSContext methods: + + @Override + public Object inquireSecContext(InquireType type) throws GSSException { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(new InquireSecContextPermission(type.toString())); + } + if (mechCtxt == null) { + throw new GSSException(GSSException.NO_CONTEXT); + } + return mechCtxt.inquireSecContext(type); + } + + @Override + public void requestDelegPolicy(boolean state) throws GSSException { + if (mechCtxt == null && initiator) + reqDelegPolicyState = state; + } + + @Override + public boolean getDelegPolicyState() { + if (mechCtxt != null) + return mechCtxt.getDelegPolicyState(); + else + return reqDelegPolicyState; + } +} diff --git a/src/sun/security/jgss/GSSCredentialImpl.java b/src/sun/security/jgss/GSSCredentialImpl.java new file mode 100644 index 00000000..65f6847f --- /dev/null +++ b/src/sun/security/jgss/GSSCredentialImpl.java @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.*; +import sun.security.jgss.spi.*; +import java.util.*; +import com.sun.security.jgss.*; +import sun.security.jgss.spnego.SpNegoCredElement; + +public class GSSCredentialImpl implements ExtendedGSSCredential { + + private GSSManagerImpl gssManager = null; + private boolean destroyed = false; + + /* + * We store all elements in a hashtable, using as the + * key. This makes it easy to locate the specific kind of credential we + * need. The implementation needs to be optimized for the case where + * there is just one element (tempCred). + */ + private Hashtable hashtable = null; + + // XXX Optimization for single mech usage + private GSSCredentialSpi tempCred = null; + + GSSCredentialImpl(GSSManagerImpl gssManager, int usage) + throws GSSException { + this(gssManager, null, GSSCredential.DEFAULT_LIFETIME, + (Oid[]) null, usage); + } + + GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, + int lifetime, Oid mech, int usage) + throws GSSException { + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + init(gssManager); + add(name, lifetime, lifetime, mech, usage); + } + + GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name, + int lifetime, Oid mechs[], int usage) + throws GSSException { + init(gssManager); + boolean defaultList = false; + if (mechs == null) { + mechs = gssManager.getMechs(); + defaultList = true; + } + + for (int i = 0; i < mechs.length; i++) { + try { + add(name, lifetime, lifetime, mechs[i], usage); + } catch (GSSException e) { + if (defaultList) { + // Try the next mechanism + GSSUtil.debug("Ignore " + e + " while acquring cred for " + + mechs[i]); + //e.printStackTrace(); + } else throw e; // else try the next mechanism + } + } + if ((hashtable.size() == 0) || (usage != getUsage())) + throw new GSSException(GSSException.NO_CRED); + } + + // Wrap a mech cred into a GSS cred + public GSSCredentialImpl(GSSManagerImpl gssManager, + GSSCredentialSpi mechElement) throws GSSException { + + init(gssManager); + int usage = GSSCredential.ACCEPT_ONLY; + if (mechElement.isInitiatorCredential()) { + if (mechElement.isAcceptorCredential()) { + usage = GSSCredential.INITIATE_AND_ACCEPT; + } else { + usage = GSSCredential.INITIATE_ONLY; + } + } + SearchKey key = new SearchKey(mechElement.getMechanism(), + usage); + tempCred = mechElement; + hashtable.put(key, tempCred); + // More mechs that can use this cred, say, SPNEGO + if (!GSSUtil.isSpNegoMech(mechElement.getMechanism())) { + key = new SearchKey(GSSUtil.GSS_SPNEGO_MECH_OID, usage); + hashtable.put(key, new SpNegoCredElement(mechElement)); + } + } + + void init(GSSManagerImpl gssManager) { + this.gssManager = gssManager; + hashtable = new Hashtable( + gssManager.getMechs().length); + } + + public void dispose() throws GSSException { + if (!destroyed) { + GSSCredentialSpi element; + Enumeration values = hashtable.elements(); + while (values.hasMoreElements()) { + element = values.nextElement(); + element.dispose(); + } + destroyed = true; + } + } + + public GSSCredential impersonate(GSSName name) throws GSSException { + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + Oid mech = tempCred.getMechanism(); + GSSNameSpi nameElement = (name == null ? null : + ((GSSNameImpl)name).getElement(mech)); + GSSCredentialSpi cred = tempCred.impersonate(nameElement); + return (cred == null ? + null : new GSSCredentialImpl(gssManager, cred)); + } + + public GSSName getName() throws GSSException { + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + return GSSNameImpl.wrapElement(gssManager, tempCred.getName()); + } + + public GSSName getName(Oid mech) throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + SearchKey key = null; + GSSCredentialSpi element = null; + + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); + element = hashtable.get(key); + + if (element == null) { + key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); + element = hashtable.get(key); + } + + if (element == null) { + key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); + element = hashtable.get(key); + } + + if (element == null) { + throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); + } + + return GSSNameImpl.wrapElement(gssManager, element.getName()); + + } + + /** + * Returns the remaining lifetime of this credential. The remaining + * lifetime is defined as the minimum lifetime, either for initiate or + * for accept, across all elements contained in it. Not terribly + * useful, but required by GSS-API. + */ + public int getRemainingLifetime() throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + SearchKey tempKey; + GSSCredentialSpi tempCred; + int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0; + int min = INDEFINITE_LIFETIME; + + for (Enumeration e = hashtable.keys(); + e.hasMoreElements(); ) { + tempKey = e.nextElement(); + tempCred = hashtable.get(tempKey); + if (tempKey.getUsage() == INITIATE_ONLY) + tempLife = tempCred.getInitLifetime(); + else if (tempKey.getUsage() == ACCEPT_ONLY) + tempLife = tempCred.getAcceptLifetime(); + else { + tempInitLife = tempCred.getInitLifetime(); + tempAcceptLife = tempCred.getAcceptLifetime(); + tempLife = (tempInitLife < tempAcceptLife ? + tempInitLife: + tempAcceptLife); + } + if (min > tempLife) + min = tempLife; + } + + return min; + } + + public int getRemainingInitLifetime(Oid mech) throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + GSSCredentialSpi element = null; + SearchKey key = null; + boolean found = false; + int max = 0; + + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); + element = hashtable.get(key); + + if (element != null) { + found = true; + if (max < element.getInitLifetime()) + max = element.getInitLifetime(); + } + + key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); + element = hashtable.get(key); + + if (element != null) { + found = true; + if (max < element.getInitLifetime()) + max = element.getInitLifetime(); + } + + if (!found) { + throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); + } + + return max; + + } + + public int getRemainingAcceptLifetime(Oid mech) throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + GSSCredentialSpi element = null; + SearchKey key = null; + boolean found = false; + int max = 0; + + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); + element = hashtable.get(key); + + if (element != null) { + found = true; + if (max < element.getAcceptLifetime()) + max = element.getAcceptLifetime(); + } + + key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); + element = hashtable.get(key); + + if (element != null) { + found = true; + if (max < element.getAcceptLifetime()) + max = element.getAcceptLifetime(); + } + + if (!found) { + throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); + } + + return max; + + } + + /** + * Returns the usage mode for this credential. Returns + * INITIATE_AND_ACCEPT if any one element contained in it supports + * INITIATE_AND_ACCEPT or if two different elements exist where one + * support INITIATE_ONLY and the other supports ACCEPT_ONLY. + */ + public int getUsage() throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + SearchKey tempKey; + boolean initiate = false; + boolean accept = false; + + for (Enumeration e = hashtable.keys(); + e.hasMoreElements(); ) { + tempKey = e.nextElement(); + if (tempKey.getUsage() == INITIATE_ONLY) + initiate = true; + else if (tempKey.getUsage() == ACCEPT_ONLY) + accept = true; + else + return INITIATE_AND_ACCEPT; + } + if (initiate) { + if (accept) + return INITIATE_AND_ACCEPT; + else + return INITIATE_ONLY; + } else + return ACCEPT_ONLY; + } + + public int getUsage(Oid mech) throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + GSSCredentialSpi element = null; + SearchKey key = null; + boolean initiate = false; + boolean accept = false; + + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + key = new SearchKey(mech, GSSCredential.INITIATE_ONLY); + element = hashtable.get(key); + + if (element != null) { + initiate = true; + } + + key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY); + element = hashtable.get(key); + + if (element != null) { + accept = true; + } + + key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT); + element = hashtable.get(key); + + if (element != null) { + initiate = true; + accept = true; + } + + if (initiate && accept) + return GSSCredential.INITIATE_AND_ACCEPT; + else if (initiate) + return GSSCredential.INITIATE_ONLY; + else if (accept) + return GSSCredential.ACCEPT_ONLY; + else { + throw new GSSExceptionImpl(GSSException.BAD_MECH, mech); + } + } + + public Oid[] getMechs() throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + Vector result = new Vector(hashtable.size()); + + for (Enumeration e = hashtable.keys(); + e.hasMoreElements(); ) { + SearchKey tempKey = e.nextElement(); + result.addElement(tempKey.getMech()); + } + return result.toArray(new Oid[0]); + } + + public void add(GSSName name, int initLifetime, int acceptLifetime, + Oid mech, int usage) throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + SearchKey key = new SearchKey(mech, usage); + if (hashtable.containsKey(key)) { + throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT, + "Duplicate element found: " + + getElementStr(mech, usage)); + } + + // XXX If not instance of GSSNameImpl then throw exception + // Application mixing GSS implementations + GSSNameSpi nameElement = (name == null ? null : + ((GSSNameImpl)name).getElement(mech)); + + tempCred = gssManager.getCredentialElement(nameElement, + initLifetime, + acceptLifetime, + mech, + usage); + /* + * Not all mechanisms support the concept of one credential element + * that can be used for both initiating and accepting a context. In + * the event that an application requests usage INITIATE_AND_ACCEPT + * for a credential from such a mechanism, the GSS framework will + * need to obtain two different credential elements from the + * mechanism, one that will have usage INITIATE_ONLY and another + * that will have usage ACCEPT_ONLY. The mechanism will help the + * GSS-API realize this by returning a credential element with + * usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another + * call to getCredentialElement, this time with the other usage + * mode. + */ + + if (tempCred != null) { + if (usage == GSSCredential.INITIATE_AND_ACCEPT && + (!tempCred.isAcceptorCredential() || + !tempCred.isInitiatorCredential())) { + + int currentUsage; + int desiredUsage; + + if (!tempCred.isInitiatorCredential()) { + currentUsage = GSSCredential.ACCEPT_ONLY; + desiredUsage = GSSCredential.INITIATE_ONLY; + } else { + currentUsage = GSSCredential.INITIATE_ONLY; + desiredUsage = GSSCredential.ACCEPT_ONLY; + } + + key = new SearchKey(mech, currentUsage); + hashtable.put(key, tempCred); + + tempCred = gssManager.getCredentialElement(nameElement, + initLifetime, + acceptLifetime, + mech, + desiredUsage); + + key = new SearchKey(mech, desiredUsage); + hashtable.put(key, tempCred); + } else { + hashtable.put(key, tempCred); + } + } + } + + public boolean equals(Object another) { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + if (this == another) { + return true; + } + + if (!(another instanceof GSSCredentialImpl)) { + return false; + } + + // NOTE: The specification does not define the criteria to compare + // credentials. + /* + * XXX + * The RFC says: "Tests if this GSSCredential refers to the same + * entity as the supplied object. The two credentials must be + * acquired over the same mechanisms and must refer to the same + * principal. Returns "true" if the two GSSCredentials refer to + * the same entity; "false" otherwise." + * + * Well, when do two credentials refer to the same principal? Do + * they need to have one GSSName in common for the different + * GSSName's that the credential elements return? Or do all + * GSSName's have to be in common when the names are exported with + * their respective mechanisms for the credential elements? + */ + return false; + + } + + /** + * Returns a hashcode value for this GSSCredential. + * + * @return a hashCode value + */ + public int hashCode() { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + // NOTE: The specification does not define the criteria to compare + // credentials. + /* + * XXX + * Decide on a criteria for equals first then do this. + */ + return 1; + } + + /** + * Returns the specified mechanism's credential-element. + * + * @param mechOid - the oid for mechanism to retrieve + * @param throwExcep - boolean indicating if the function is + * to throw exception or return null when element is not + * found. + * @return mechanism credential object + * @exception GSSException of invalid mechanism + */ + public GSSCredentialSpi getElement(Oid mechOid, boolean initiate) + throws GSSException { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + SearchKey key; + GSSCredentialSpi element; + + if (mechOid == null) { + /* + * First see if the default mechanism satisfies the + * desired usage. + */ + mechOid = ProviderList.DEFAULT_MECH_OID; + key = new SearchKey(mechOid, + initiate? INITIATE_ONLY : ACCEPT_ONLY); + element = hashtable.get(key); + if (element == null) { + key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); + element = hashtable.get(key); + if (element == null) { + /* + * Now just return any element that satisfies the + * desired usage. + */ + Object[] elements = hashtable.entrySet().toArray(); + for (int i = 0; i < elements.length; i++) { + element = (GSSCredentialSpi) + ((Map.Entry)elements[i]).getValue(); + if (element.isInitiatorCredential() == initiate) + break; + } // for loop + } + } + } else { + + if (initiate) + key = new SearchKey(mechOid, INITIATE_ONLY); + else + key = new SearchKey(mechOid, ACCEPT_ONLY); + + element = hashtable.get(key); + + if (element == null) { + key = new SearchKey(mechOid, INITIATE_AND_ACCEPT); + element = hashtable.get(key); + } + } + + if (element == null) + throw new GSSExceptionImpl(GSSException.NO_CRED, + "No credential found for: " + + getElementStr(mechOid, + initiate? INITIATE_ONLY : ACCEPT_ONLY)); + return element; + } + + Set getElements() { + HashSet retVal = + new HashSet(hashtable.size()); + Enumeration values = hashtable.elements(); + while (values.hasMoreElements()) { + GSSCredentialSpi o = values.nextElement(); + retVal.add(o); + } + return retVal; + } + + private static String getElementStr(Oid mechOid, int usage) { + String displayString = mechOid.toString(); + if (usage == GSSCredential.INITIATE_ONLY) { + displayString = + displayString.concat(" usage: Initiate"); + } else if (usage == GSSCredential.ACCEPT_ONLY) { + displayString = + displayString.concat(" usage: Accept"); + } else { + displayString = + displayString.concat(" usage: Initiate and Accept"); + } + return displayString; + } + + public String toString() { + + if (destroyed) { + throw new IllegalStateException("This credential is " + + "no longer valid"); + } + + GSSCredentialSpi element = null; + StringBuffer buffer = new StringBuffer("[GSSCredential: "); + Object[] elements = hashtable.entrySet().toArray(); + for (int i = 0; i < elements.length; i++) { + try { + buffer.append('\n'); + element = (GSSCredentialSpi) + ((Map.Entry)elements[i]).getValue(); + buffer.append(element.getName()); + buffer.append(' '); + buffer.append(element.getMechanism()); + buffer.append(element.isInitiatorCredential() ? + " Initiate" : ""); + buffer.append(element.isAcceptorCredential() ? + " Accept" : ""); + buffer.append(" ["); + buffer.append(element.getClass()); + buffer.append(']'); + } catch (GSSException e) { + // skip to next element + } + } + buffer.append(']'); + return buffer.toString(); + } + + static class SearchKey { + private Oid mechOid = null; + private int usage = GSSCredential.INITIATE_AND_ACCEPT; + public SearchKey(Oid mechOid, int usage) { + + this.mechOid = mechOid; + this.usage = usage; + } + public Oid getMech() { + return mechOid; + } + public int getUsage() { + return usage; + } + public boolean equals(Object other) { + if (! (other instanceof SearchKey)) + return false; + SearchKey that = (SearchKey) other; + return ((this.mechOid.equals(that.mechOid)) && + (this.usage == that.usage)); + } + public int hashCode() { + return mechOid.hashCode(); + } + } + +} diff --git a/src/sun/security/jgss/GSSExceptionImpl.java b/src/sun/security/jgss/GSSExceptionImpl.java new file mode 100644 index 00000000..cc2dd268 --- /dev/null +++ b/src/sun/security/jgss/GSSExceptionImpl.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.*; + +/** + * This class helps overcome a limitation of the org.ietf.jgss.GSSException + * class that does not allow the thrower to set a string corresponding to + * the major code. + */ +public class GSSExceptionImpl extends GSSException { + + private static final long serialVersionUID = 4251197939069005575L; + + private String majorMessage; + + /** + * A constructor that takes the majorCode as well as the mech oid that + * will be appended to the standard message defined in its super class. + */ + GSSExceptionImpl(int majorCode, Oid mech) { + super(majorCode); + this.majorMessage = super.getMajorString() + ": " + mech; + } + + /** + * A constructor that takes the majorCode as well as the message that + * corresponds to it. + */ + public GSSExceptionImpl(int majorCode, String majorMessage) { + super(majorCode); + this.majorMessage = majorMessage; + } + + /** + * A constructor that takes the majorCode and the exception cause. + */ + public GSSExceptionImpl(int majorCode, Exception cause) { + super(majorCode); + initCause(cause); + } + + /** + * A constructor that takes the majorCode, the message that + * corresponds to it, and the exception cause. + */ + public GSSExceptionImpl(int majorCode, String majorMessage, + Exception cause) { + this(majorCode, majorMessage); + initCause(cause); + } + + /** + * Returns the message that was embedded in this object, otherwise it + * returns the default message that an org.ietf.jgss.GSSException + * generates. + */ + public String getMessage() { + if (majorMessage != null) + return majorMessage; + else + return super.getMessage(); + } + +} diff --git a/src/sun/security/jgss/GSSHeader.java b/src/sun/security/jgss/GSSHeader.java new file mode 100644 index 00000000..fc34eaeb --- /dev/null +++ b/src/sun/security/jgss/GSSHeader.java @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.GSSException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import sun.security.util.*; + +/** + * This class represents the mechanism independent part of a GSS-API + * context establishment token. Some mechanisms may choose to encode + * all subsequent tokens as well such that they start with an encoding + * of an instance of this class. e.g., The Kerberos v5 GSS-API Mechanism + * uses this header for all GSS-API tokens. + *

    + * The format is specified in RFC 2743 section 3.1. + * + * @author Mayank Upadhyay + */ + +/* + * The RFC states that implementations should explicitly follow the + * encoding scheme descibed in this section rather than use ASN.1 + * compilers. However, we should consider removing duplicate ASN.1 + * like code from here and depend on sun.security.util if possible. + */ + +public class GSSHeader { + + private ObjectIdentifier mechOid = null; + private byte[] mechOidBytes = null; + private int mechTokenLength = 0; + + /** + * The tag defined in the GSS-API mechanism independent token + * format. + */ + public static final int TOKEN_ID=0x60; + + /** + * Creates a GSSHeader instance whose encoding can be used as the + * prefix for a particular mechanism token. + * @param mechOid the Oid of the mechanism which generated the token + * @param mechTokenLength the length of the subsequent portion that + * the mechanism will be adding. + */ + public GSSHeader(ObjectIdentifier mechOid, int mechTokenLength) + throws IOException { + + this.mechOid = mechOid; + DerOutputStream temp = new DerOutputStream(); + temp.putOID(mechOid); + mechOidBytes = temp.toByteArray(); + this.mechTokenLength = mechTokenLength; + } + + /** + * Reads in a GSSHeader from an InputStream. Typically this would be + * used as part of reading the complete token from an InputStream + * that is obtained from a socket. + */ + public GSSHeader(InputStream is) + throws IOException, GSSException { + + // debug("Parsing GSS token: "); + + int tag = is.read(); + + // debug("tag=" + tag); + + if (tag != TOKEN_ID) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "GSSHeader did not find the right tag"); + + int length = getLength(is); + + DerValue temp = new DerValue(is); + mechOidBytes = temp.toByteArray(); + mechOid = temp.getOID(); + // debug (" oid=" + mechOid); + + // debug (" len starting with oid=" + length); + mechTokenLength = length - mechOidBytes.length; + + // debug(" mechToken length=" + mechTokenLength); + + } + + /** + * Used to obtain the Oid stored in this GSSHeader instance. + * @return the Oid of the mechanism. + */ + public ObjectIdentifier getOid() { + return mechOid; + } + + /** + * Used to obtain the length of the mechanism specific token that + * will follow the encoding of this GSSHeader instance. + * @return the length of the mechanism specific token portion that + * will follow this GSSHeader. + */ + public int getMechTokenLength() { + return mechTokenLength; + } + + /** + * Used to obtain the length of the encoding of this GSSHeader. + * @return the lenght of the encoding of this GSSHeader instance. + */ + public int getLength() { + int lenField = mechOidBytes.length + mechTokenLength; + return (1 + getLenFieldSize(lenField) + mechOidBytes.length); + } + + /** + * Used to determine what the maximum possible mechanism token + * size is if the complete GSSToken returned to the application + * (including a GSSHeader) is not to exceed some pre-determined + * value in size. + * @param mechOid the Oid of the mechanism that will generate + * this GSS-API token + * @param maxTotalSize the pre-determined value that serves as a + * maximum size for the complete GSS-API token (including a + * GSSHeader) + * @return the maximum size of mechanism token that can be used + * so as to not exceed maxTotalSize with the GSS-API token + */ + public static int getMaxMechTokenSize(ObjectIdentifier mechOid, + int maxTotalSize) { + + int mechOidBytesSize = 0; + try { + DerOutputStream temp = new DerOutputStream(); + temp.putOID(mechOid); + mechOidBytesSize = temp.toByteArray().length; + } catch (IOException e) { + } + + // Subtract bytes needed for 0x60 tag and mechOidBytes + maxTotalSize -= (1 + mechOidBytesSize); + + // Subtract maximum len bytes + maxTotalSize -= 5; + + return maxTotalSize; + + /* + * Len field and mechanism token must fit in remaining + * space. The range of the len field that we allow is + * 1 through 5. + * + + int mechTokenSize = 0; + for (int lenFieldSize = 1; lenFieldSize <= 5; + lenFieldSize++) { + mechTokenSize = maxTotalSize - lenFieldSize; + if (getLenFieldSize(mechTokenSize + mechOidBytesSize + + lenFieldSize) <= lenFieldSize) + break; + } + + return mechTokenSize; + */ + + + } + + /** + * Used to determine the number of bytes that will be need to encode + * the length field of the GSSHeader. + */ + private int getLenFieldSize(int len) { + int retVal = 1; + if (len < 128) { + retVal=1; + } else if (len < (1 << 8)) { + retVal=2; + } else if (len < (1 << 16)) { + retVal=3; + } else if (len < (1 << 24)) { + retVal=4; + } else { + retVal=5; // See getMaxMechTokenSize + } + return retVal; + } + + /** + * Encodes this GSSHeader instance onto the provided OutputStream. + * @param os the OutputStream to which the token should be written. + * @return the number of bytes that are output as a result of this + * encoding + */ + public int encode(OutputStream os) throws IOException { + int retVal = 1 + mechOidBytes.length; + os.write(TOKEN_ID); + int length = mechOidBytes.length + mechTokenLength; + retVal += putLength(length, os); + os.write(mechOidBytes); + return retVal; + } + + /** + * 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!) + * + * @return the length or -1 if indefinite length found. + * @exception IOException on parsing error or unsupported lengths. + */ + // shameless lifted from sun.security.util.DerInputStream. + private int getLength(InputStream in) throws IOException { + return getLength(in.read(), in); + } + + /** + * 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!) + * + * @return the length or -1 if indefinite length found. + * @exception IOException on parsing error or unsupported lengths. + */ + // shameless lifted from sun.security.util.DerInputStream. + private int getLength(int lenByte, InputStream in) throws IOException { + int value, tmp; + + tmp = lenByte; + if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum + value = tmp; + } else { // long form or indefinite + tmp &= 0x07f; + + /* + * NOTE: tmp == 0 indicates indefinite length encoded data. + * tmp > 4 indicates more than 4Gb of data. + */ + if (tmp == 0) + return -1; + if (tmp < 0 || tmp > 4) + throw new IOException("DerInputStream.getLength(): lengthTag=" + + tmp + ", " + + ((tmp < 0) ? "incorrect DER encoding." : "too big.")); + + for (value = 0; tmp > 0; tmp --) { + value <<= 8; + value += 0x0ff & in.read(); + } + if (value < 0) { + throw new IOException("Invalid length bytes"); + } + } + return value; + } + + /** + * Put the encoding of the length in the specified stream. + * + * @params len the length of the attribute. + * @param out the outputstream to write the length to + * @return the number of bytes written + * @exception IOException on writing errors. + */ + // Shameless lifted from sun.security.util.DerOutputStream. + private int putLength(int len, OutputStream out) throws IOException { + int retVal = 0; + if (len < 128) { + out.write((byte)len); + retVal=1; + + } else if (len < (1 << 8)) { + out.write((byte)0x081); + out.write((byte)len); + retVal=2; + + } else if (len < (1 << 16)) { + out.write((byte)0x082); + out.write((byte)(len >> 8)); + out.write((byte)len); + retVal=3; + + } else if (len < (1 << 24)) { + out.write((byte)0x083); + out.write((byte)(len >> 16)); + out.write((byte)(len >> 8)); + out.write((byte)len); + retVal=4; + + } else { + out.write((byte)0x084); + out.write((byte)(len >> 24)); + out.write((byte)(len >> 16)); + out.write((byte)(len >> 8)); + out.write((byte)len); + retVal=5; + } + + return retVal; + } + + // XXX Call these two in some central class + private void debug(String str) { + System.err.print(str); + } + + private String getHexBytes(byte[] bytes, int len) + throws IOException { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < len; i++) { + + int b1 = (bytes[i]>>4) & 0x0f; + int b2 = bytes[i] & 0x0f; + + sb.append(Integer.toHexString(b1)); + sb.append(Integer.toHexString(b2)); + sb.append(' '); + } + return sb.toString(); + } +} diff --git a/src/sun/security/jgss/GSSManagerImpl.java b/src/sun/security/jgss/GSSManagerImpl.java new file mode 100644 index 00000000..602f1807 --- /dev/null +++ b/src/sun/security/jgss/GSSManagerImpl.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.*; +import sun.security.jgss.spi.*; +import java.security.Provider; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This class provides the default implementation of the GSSManager + * interface. + */ +public class GSSManagerImpl extends GSSManager { + + // Undocumented property + private static final String USE_NATIVE_PROP = + "sun.security.jgss.native"; + private static final Boolean USE_NATIVE; + + static { + USE_NATIVE = + AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + String osname = System.getProperty("os.name"); + if (osname.startsWith("SunOS") || + osname.contains("OS X") || + osname.startsWith("Linux")) { + return new Boolean(System.getProperty + (USE_NATIVE_PROP)); + } + return Boolean.FALSE; + } + }); + + } + + private ProviderList list; + + // Used by java SPNEGO impl to make sure native is disabled + public GSSManagerImpl(GSSCaller caller, boolean useNative) { + list = new ProviderList(caller, useNative); + } + + // Used by HTTP/SPNEGO NegotiatorImpl + public GSSManagerImpl(GSSCaller caller) { + list = new ProviderList(caller, USE_NATIVE); + } + + public GSSManagerImpl() { + list = new ProviderList(GSSCaller.CALLER_UNKNOWN, USE_NATIVE); + } + + public Oid[] getMechs(){ + return list.getMechs(); + } + + public Oid[] getNamesForMech(Oid mech) + throws GSSException { + MechanismFactory factory = list.getMechFactory(mech); + return factory.getNameTypes().clone(); + } + + public Oid[] getMechsForName(Oid nameType){ + Oid[] mechs = list.getMechs(); + Oid[] retVal = new Oid[mechs.length]; + int pos = 0; + + // Compatibility with RFC 2853 old NT_HOSTBASED_SERVICE value. + if (nameType.equals(GSSNameImpl.oldHostbasedServiceName)) { + nameType = GSSName.NT_HOSTBASED_SERVICE; + } + + // Iterate thru all mechs in GSS + for (int i = 0; i < mechs.length; i++) { + // what nametypes does this mech support? + Oid mech = mechs[i]; + try { + Oid[] namesForMech = getNamesForMech(mech); + // Is the desired Oid present in that list? + if (nameType.containedIn(namesForMech)) { + retVal[pos++] = mech; + } + } catch (GSSException e) { + // Squelch it and just skip over this mechanism + GSSUtil.debug("Skip " + mech + + ": error retrieving supported name types"); + } + } + + // Trim the list if needed + if (pos < retVal.length) { + Oid[] temp = new Oid[pos]; + for (int i = 0; i < pos; i++) + temp[i] = retVal[i]; + retVal = temp; + } + + return retVal; + } + + public GSSName createName(String nameStr, Oid nameType) + throws GSSException { + return new GSSNameImpl(this, nameStr, nameType); + } + + public GSSName createName(byte name[], Oid nameType) + throws GSSException { + return new GSSNameImpl(this, name, nameType); + } + + public GSSName createName(String nameStr, Oid nameType, + Oid mech) throws GSSException { + return new GSSNameImpl(this, nameStr, nameType, mech); + } + + public GSSName createName(byte name[], Oid nameType, Oid mech) + throws GSSException { + return new GSSNameImpl(this, name, nameType, mech); + } + + public GSSCredential createCredential(int usage) + throws GSSException { + return new GSSCredentialImpl(this, usage); + } + + public GSSCredential createCredential(GSSName aName, + int lifetime, Oid mech, int usage) + throws GSSException { + return new GSSCredentialImpl(this, aName, lifetime, mech, usage); + } + + public GSSCredential createCredential(GSSName aName, + int lifetime, Oid mechs[], int usage) + throws GSSException { + return new GSSCredentialImpl(this, aName, lifetime, mechs, usage); + } + + public GSSContext createContext(GSSName peer, Oid mech, + GSSCredential myCred, int lifetime) + throws GSSException { + return new GSSContextImpl(this, peer, mech, myCred, lifetime); + } + + public GSSContext createContext(GSSCredential myCred) + throws GSSException { + return new GSSContextImpl(this, myCred); + } + + public GSSContext createContext(byte[] interProcessToken) + throws GSSException { + return new GSSContextImpl(this, interProcessToken); + } + + public void addProviderAtFront(Provider p, Oid mech) + throws GSSException { + list.addProviderAtFront(p, mech); + } + + public void addProviderAtEnd(Provider p, Oid mech) + throws GSSException { + list.addProviderAtEnd(p, mech); + } + + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, int initLifetime, + int acceptLifetime, Oid mech, int usage) + throws GSSException { + MechanismFactory factory = list.getMechFactory(mech); + return factory.getCredentialElement(name, initLifetime, + acceptLifetime, usage); + } + + // Used by java SPNEGO impl + public GSSNameSpi getNameElement(String name, Oid nameType, Oid mech) + throws GSSException { + // Just use the most preferred MF impl assuming GSSNameSpi + // objects are interoperable among providers + MechanismFactory factory = list.getMechFactory(mech); + return factory.getNameElement(name, nameType); + } + + // Used by java SPNEGO impl + public GSSNameSpi getNameElement(byte[] name, Oid nameType, Oid mech) + throws GSSException { + // Just use the most preferred MF impl assuming GSSNameSpi + // objects are interoperable among providers + MechanismFactory factory = list.getMechFactory(mech); + return factory.getNameElement(name, nameType); + } + + GSSContextSpi getMechanismContext(GSSNameSpi peer, + GSSCredentialSpi myInitiatorCred, + int lifetime, Oid mech) + throws GSSException { + Provider p = null; + if (myInitiatorCred != null) { + p = myInitiatorCred.getProvider(); + } + MechanismFactory factory = list.getMechFactory(mech, p); + return factory.getMechanismContext(peer, myInitiatorCred, lifetime); + } + + GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred, + Oid mech) + throws GSSException { + Provider p = null; + if (myAcceptorCred != null) { + p = myAcceptorCred.getProvider(); + } + MechanismFactory factory = list.getMechFactory(mech, p); + return factory.getMechanismContext(myAcceptorCred); + } + + GSSContextSpi getMechanismContext(byte[] exportedContext) + throws GSSException { + if ((exportedContext == null) || (exportedContext.length == 0)) { + throw new GSSException(GSSException.NO_CONTEXT); + } + GSSContextSpi result = null; + + // Only allow context import with native provider since JGSS + // still has not defined its own interprocess token format + Oid[] mechs = list.getMechs(); + for (int i = 0; i < mechs.length; i++) { + MechanismFactory factory = list.getMechFactory(mechs[i]); + if (factory.getProvider().getName().equals("SunNativeGSS")) { + result = factory.getMechanismContext(exportedContext); + if (result != null) break; + } + } + if (result == null) { + throw new GSSException(GSSException.UNAVAILABLE); + } + return result; + } +} diff --git a/src/sun/security/jgss/GSSNameImpl.java b/src/sun/security/jgss/GSSNameImpl.java new file mode 100644 index 00000000..b6fd6e0c --- /dev/null +++ b/src/sun/security/jgss/GSSNameImpl.java @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +import sun.security.jgss.spi.GSSNameSpi; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; + +/** + * This is the implementation class for GSSName. Conceptually the + * GSSName is a container with mechanism specific name elements. Each + * name element is a representation of how that particular mechanism + * would canonicalize this principal. + * + * Generally a GSSName is created by an application when it supplies + * a sequence of bytes and a nametype that helps each mechanism + * decide how to interpret those bytes. + * + * It is not necessary to create name elements for each available + * mechanism at the time the application creates the GSSName. This + * implementation does this lazily, as and when name elements for + * mechanisms are required to be handed out. (Generally, other GSS + * classes like GSSContext and GSSCredential request specific + * elements depending on the mechanisms that they are dealing with.) + * Assume that getting a mechanism to parse the applciation specified + * bytes is an expensive call. + * + * When a GSSName is canonicalized wrt some mechanism, it is supposed + * to discard all elements of other mechanisms and retain only the + * element for this mechanism. In GSS terminology this is called a + * Mechanism Name or MN. This implementation tries to retain the + * application provided bytes and name type just in case the MN is + * asked to produce an element for a mechanism that is different. + * + * When a GSSName is to be exported, the name element for the desired + * mechanism is converted to a byte representation and written + * out. It might happen that a name element for that mechanism cannot + * be obtained. This happens when the mechanism is just not supported + * in this GSS-API or when the mechanism is supported but bytes + * corresponding to the nametypes that it understands are not + * available in this GSSName. + * + * This class is safe for sharing. Each retrieval of a name element + * from getElement() might potentially add a new element to the + * hashmap of elements, but getElement() is synchronized. + * + * @author Mayank Upadhyay + * @since 1.4 + */ + +public class GSSNameImpl implements GSSName { + + /** + * The old Oid used in RFC 2853. Now supported as + * input parameters in: + * + * 1. The four overloaded GSSManager.createName(*) methods + * 2. GSSManager.getMechsForName(Oid) + * + * Note that even if a GSSName is created with this old Oid, + * its internal name type and getStringNameType() output are + * always the new value. + */ + final static Oid oldHostbasedServiceName; + + static { + Oid tmp = null; + try { + tmp = new Oid("1.3.6.1.5.6.2"); + } catch (Exception e) { + // should never happen + } + oldHostbasedServiceName = tmp; + } + + private GSSManagerImpl gssManager = null; + + /* + * Store whatever the application passed in. We will use this to + * get individual mechanisms to create name elements as and when + * needed. + * Store both the String and the byte[]. Leave I18N to the + * mechanism by allowing it to extract bytes from the String! + */ + + private String appNameStr = null; + private byte[] appNameBytes = null; + private Oid appNameType = null; + + /* + * When we figure out what the printable name would be, we store + * both the name and its type. + */ + + private String printableName = null; + private Oid printableNameType = null; + + private HashMap elements = null; + private GSSNameSpi mechElement = null; + + static GSSNameImpl wrapElement(GSSManagerImpl gssManager, + GSSNameSpi mechElement) throws GSSException { + return (mechElement == null ? + null : new GSSNameImpl(gssManager, mechElement)); + } + + GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) { + this.gssManager = gssManager; + appNameStr = printableName = mechElement.toString(); + appNameType = printableNameType = mechElement.getStringNameType(); + this.mechElement = mechElement; + elements = new HashMap(1); + elements.put(mechElement.getMechanism(), this.mechElement); + } + + GSSNameImpl(GSSManagerImpl gssManager, + Object appName, + Oid appNameType) + throws GSSException { + this(gssManager, appName, appNameType, null); + } + + GSSNameImpl(GSSManagerImpl gssManager, + Object appName, + Oid appNameType, + Oid mech) + throws GSSException { + + if (oldHostbasedServiceName.equals(appNameType)) { + appNameType = GSSName.NT_HOSTBASED_SERVICE; + } + if (appName == null) + throw new GSSExceptionImpl(GSSException.BAD_NAME, + "Cannot import null name"); + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + if (NT_EXPORT_NAME.equals(appNameType)) { + importName(gssManager, appName); + } else { + init(gssManager, appName, appNameType, mech); + } + } + + private void init(GSSManagerImpl gssManager, + Object appName, Oid appNameType, + Oid mech) + throws GSSException { + + this.gssManager = gssManager; + this.elements = + new HashMap(gssManager.getMechs().length); + + if (appName instanceof String) { + this.appNameStr = (String) appName; + /* + * If appNameType is null, then the nametype for this printable + * string is determined only by interrogating the + * mechanism. Thus, defer the setting of printableName and + * printableNameType till later. + */ + if (appNameType != null) { + printableName = appNameStr; + printableNameType = appNameType; + } + } else { + this.appNameBytes = (byte[]) appName; + } + + this.appNameType = appNameType; + + mechElement = getElement(mech); + + /* + * printableName will be null if appName was in a byte[] or if + * appName was in a String but appNameType was null. + */ + if (printableName == null) { + printableName = mechElement.toString(); + printableNameType = mechElement.getStringNameType(); + } + + /* + * At this point the GSSNameImpl has the following set: + * appNameStr or appNameBytes + * appNameType (could be null) + * printableName + * printableNameType + * mechElement (which also exists in the hashmap of elements) + */ + } + + private void importName(GSSManagerImpl gssManager, + Object appName) + throws GSSException { + + int pos = 0; + byte[] bytes = null; + + if (appName instanceof String) { + try { + bytes = ((String) appName).getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + // Won't happen + } + } else + bytes = (byte[]) appName; + + if ((bytes[pos++] != 0x04) || + (bytes[pos++] != 0x01)) + throw new GSSExceptionImpl(GSSException.BAD_NAME, + "Exported name token id is corrupted!"); + + int oidLen = (((0xFF & bytes[pos++]) << 8) | + (0xFF & bytes[pos++])); + ObjectIdentifier temp = null; + try { + DerInputStream din = new DerInputStream(bytes, pos, + oidLen); + temp = new ObjectIdentifier(din); + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.BAD_NAME, + "Exported name Object identifier is corrupted!"); + } + Oid oid = new Oid(temp.toString()); + pos += oidLen; + int mechPortionLen = (((0xFF & bytes[pos++]) << 24) | + ((0xFF & bytes[pos++]) << 16) | + ((0xFF & bytes[pos++]) << 8) | + (0xFF & bytes[pos++])); + if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) { + throw new GSSExceptionImpl(GSSException.BAD_NAME, + "Exported name mech name is corrupted!"); + } + byte[] mechPortion = new byte[mechPortionLen]; + System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen); + + init(gssManager, mechPortion, NT_EXPORT_NAME, oid); + } + + public GSSName canonicalize(Oid mech) throws GSSException { + if (mech == null) mech = ProviderList.DEFAULT_MECH_OID; + + return wrapElement(gssManager, getElement(mech)); + } + + /** + * This method may return false negatives. But if it says two + * names are equals, then there is some mechanism that + * authenticates them as the same principal. + */ + public boolean equals(GSSName other) throws GSSException { + + if (this.isAnonymous() || other.isAnonymous()) + return false; + + if (other == this) + return true; + + if (! (other instanceof GSSNameImpl)) + return equals(gssManager.createName(other.toString(), + other.getStringNameType())); + + /* + * XXX Do a comparison of the appNameStr/appNameBytes if + * available. If that fails, then proceed with this test. + */ + + GSSNameImpl that = (GSSNameImpl) other; + + GSSNameSpi myElement = this.mechElement; + GSSNameSpi element = that.mechElement; + + /* + * XXX If they are not of the same mechanism type, convert both to + * Kerberos since it is guaranteed to be present. + */ + if ((myElement == null) && (element != null)) { + myElement = this.getElement(element.getMechanism()); + } else if ((myElement != null) && (element == null)) { + element = that.getElement(myElement.getMechanism()); + } + + if (myElement != null && element != null) { + return myElement.equals(element); + } + + if ((this.appNameType != null) && + (that.appNameType != null)) { + if (!this.appNameType.equals(that.appNameType)) { + return false; + } + byte[] myBytes = null; + byte[] bytes = null; + try { + myBytes = + (this.appNameStr != null ? + this.appNameStr.getBytes("UTF-8") : + this.appNameBytes); + bytes = + (that.appNameStr != null ? + that.appNameStr.getBytes("UTF-8") : + that.appNameBytes); + } catch (UnsupportedEncodingException e) { + // Won't happen + } + + return Arrays.equals(myBytes, bytes); + } + + return false; + + } + + /** + * Returns a hashcode value for this GSSName. + * + * @return a hashCode value + */ + public int hashCode() { + /* + * XXX + * In order to get this to work reliably and properly(!), obtain a + * Kerberos name element for the name and then call hashCode on its + * string representation. But this cannot be done if the nametype + * is not one of those supported by the Kerberos provider and hence + * this name cannot be imported by Kerberos. In that case return a + * constant value! + */ + + return 1; + } + + public boolean equals(Object another) { + + try { + // XXX This can lead to an infinite loop. Extract info + // and create a GSSNameImpl with it. + + if (another instanceof GSSName) + return equals((GSSName) another); + } catch (GSSException e) { + // Squelch it and return false + } + + return false; + } + + /** + * Returns a flat name representation for this object. The name + * format is defined in RFC 2743: + *

    +     * Length           Name          Description
    +     * 2               TOK_ID          Token Identifier
    +     *                                 For exported name objects, this
    +     *                                 must be hex 04 01.
    +     * 2               MECH_OID_LEN    Length of the Mechanism OID
    +     * MECH_OID_LEN    MECH_OID        Mechanism OID, in DER
    +     * 4               NAME_LEN        Length of name
    +     * NAME_LEN        NAME            Exported name; format defined in
    +     *                                 applicable mechanism draft.
    +     *
    + * + * Note that it is not required to canonicalize a name before + * calling export(). i.e., the name need not be an MN. If it is + * not an MN, an implementation defined algorithm can be used for + * choosing the mechanism which should export this name. + * + * @return the flat name representation for this object + * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME, + * BAD_NAME, FAILURE. + */ + public byte[] export() throws GSSException { + + if (mechElement == null) { + /* Use default mech */ + mechElement = getElement(ProviderList.DEFAULT_MECH_OID); + } + + byte[] mechPortion = mechElement.export(); + byte[] oidBytes = null; + ObjectIdentifier oid = null; + + try { + oid = new ObjectIdentifier + (mechElement.getMechanism().toString()); + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.FAILURE, + "Invalid OID String "); + } + DerOutputStream dout = new DerOutputStream(); + try { + dout.putOID(oid); + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.FAILURE, + "Could not ASN.1 Encode " + + oid.toString()); + } + oidBytes = dout.toByteArray(); + + byte[] retVal = new byte[2 + + 2 + oidBytes.length + + 4 + mechPortion.length]; + int pos = 0; + retVal[pos++] = 0x04; + retVal[pos++] = 0x01; + retVal[pos++] = (byte) (oidBytes.length>>>8); + retVal[pos++] = (byte) oidBytes.length; + System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length); + pos += oidBytes.length; + retVal[pos++] = (byte) (mechPortion.length>>>24); + retVal[pos++] = (byte) (mechPortion.length>>>16); + retVal[pos++] = (byte) (mechPortion.length>>>8); + retVal[pos++] = (byte) mechPortion.length; + System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length); + return retVal; + } + + public String toString() { + return printableName; + + } + + public Oid getStringNameType() throws GSSException { + return printableNameType; + } + + public boolean isAnonymous() { + if (printableNameType == null) { + return false; + } else { + return GSSName.NT_ANONYMOUS.equals(printableNameType); + } + } + + public boolean isMN() { + return true; // Since always canonicalized for some mech + } + + public synchronized GSSNameSpi getElement(Oid mechOid) + throws GSSException { + + GSSNameSpi retVal = elements.get(mechOid); + + if (retVal == null) { + if (appNameStr != null) { + retVal = gssManager.getNameElement + (appNameStr, appNameType, mechOid); + } else { + retVal = gssManager.getNameElement + (appNameBytes, appNameType, mechOid); + } + elements.put(mechOid, retVal); + } + return retVal; + } + + Set getElements() { + return new HashSet(elements.values()); + } + + private static String getNameTypeStr(Oid nameTypeOid) { + + if (nameTypeOid == null) + return "(NT is null)"; + + if (nameTypeOid.equals(NT_USER_NAME)) + return "NT_USER_NAME"; + if (nameTypeOid.equals(NT_HOSTBASED_SERVICE)) + return "NT_HOSTBASED_SERVICE"; + if (nameTypeOid.equals(NT_EXPORT_NAME)) + return "NT_EXPORT_NAME"; + if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL)) + return "NT_GSS_KRB5_PRINCIPAL"; + else + return "Unknown"; + } +} diff --git a/src/sun/security/jgss/GSSToken.java b/src/sun/security/jgss/GSSToken.java new file mode 100644 index 00000000..3ec1c9a9 --- /dev/null +++ b/src/sun/security/jgss/GSSToken.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.EOFException; + +/** + * Utilities for processing GSS Tokens. + * + */ + +public abstract class GSSToken { + + /** + * Copies an integer value to a byte array in little endian form. + * @param value the integer value to write + * @param array the byte array into which the integer must be copied. It + * is assumed that the array will be large enough to hold the 4 bytes of + * the integer. + */ + public static final void writeLittleEndian(int value, byte[] array) { + writeLittleEndian(value, array, 0); + } + + /** + * Copies an integer value to a byte array in little endian form. + * @param value the integer value to write + * @param array the byte array into which the integer must be copied. It + * is assumed that the array will be large enough to hold the 4 bytes of + * the integer. + * @param pos the position at which to start writing + */ + public static final void writeLittleEndian(int value, byte[] array, + int pos) { + array[pos++] = (byte)(value); + array[pos++] = (byte)((value>>>8)); + array[pos++] = (byte)((value>>>16)); + array[pos++] = (byte)((value>>>24)); + } + + public static final void writeBigEndian(int value, byte[] array) { + writeBigEndian(value, array, 0); + } + + public static final void writeBigEndian(int value, byte[] array, + int pos) { + array[pos++] = (byte)((value>>>24)); + array[pos++] = (byte)((value>>>16)); + array[pos++] = (byte)((value>>>8)); + array[pos++] = (byte)(value); + } + + /** + * Reads an integer value from a byte array in little endian form. This + * method allows the reading of two byte values as well as four bytes + * values both of which are needed in the Kerberos v5 GSS-API mechanism. + * + * @param data the array containing the bytes of the integer value + * @param pos the offset in the array + * @size the number of bytes to read from the array. + * @return the integer value + */ + public static final int readLittleEndian(byte[] data, int pos, int size) { + int retVal = 0; + int shifter = 0; + while (size > 0) { + retVal += (data[pos] & 0xff) << shifter; + shifter += 8; + pos++; + size--; + } + return retVal; + } + + public static final int readBigEndian(byte[] data, int pos, int size) { + int retVal = 0; + int shifter = (size-1)*8; + while (size > 0) { + retVal += (data[pos] & 0xff) << shifter; + shifter -= 8; + pos++; + size--; + } + return retVal; + } + + /** + * Writes a two byte integer value to a OutputStream. + * + * @param val the integer value. It will lose the high-order two bytes. + * @param os the OutputStream to write to + * @throws IOException if an error occurs while writing to the OutputStream + */ + public static final void writeInt(int val, OutputStream os) + throws IOException { + os.write(val>>>8); + os.write(val); + } + + /** + * Writes a two byte integer value to a byte array. + * + * @param val the integer value. It will lose the high-order two bytes. + * @param dest the byte array to write to + * @param pos the offset to start writing to + */ + public static final int writeInt(int val, byte[] dest, int pos) { + dest[pos++] = (byte)(val>>>8); + dest[pos++] = (byte)val; + return pos; + } + + /** + * Reads a two byte integer value from an InputStream. + * + * @param is the InputStream to read from + * @returns the integer value + * @throws IOException if some errors occurs while reading the integer + * bytes. + */ + public static final int readInt(InputStream is) throws IOException { + return (((0xFF & is.read()) << 8) + | (0xFF & is.read())); + } + + /** + * Reads a two byte integer value from a byte array. + * + * @param src the byte arra to read from + * @param pos the offset to start reading from + * @returns the integer value + */ + public static final int readInt(byte[] src, int pos) { + return ((0xFF & src[pos])<<8 | (0xFF & src[pos+1])); + } + + /** + * Blocks till the required number of bytes have been read from the + * input stream. + * + * @param is the InputStream to read from + * @param buffer the buffer to store the bytes into + * @param throws EOFException if EOF is reached before all bytes are + * read. + * @throws IOException is an error occurs while reading + */ + public static final void readFully(InputStream is, byte[] buffer) + throws IOException { + readFully(is, buffer, 0, buffer.length); + } + + /** + * Blocks till the required number of bytes have been read from the + * input stream. + * + * @param is the InputStream to read from + * @param buffer the buffer to store the bytes into + * @param offset the offset to start storing at + * @param len the number of bytes to read + * @param throws EOFException if EOF is reached before all bytes are + * read. + * @throws IOException is an error occurs while reading + */ + public static final void readFully(InputStream is, + byte[] buffer, int offset, int len) + throws IOException { + int temp; + while (len > 0) { + temp = is.read(buffer, offset, len); + if (temp == -1) + throw new EOFException("Cannot read all " + + len + + " bytes needed to form this token!"); + offset += temp; + len -= temp; + } + } + + public static final void debug(String str) { + System.err.print(str); + } + + public static final String getHexBytes(byte[] bytes) { + return getHexBytes(bytes, 0, bytes.length); + } + + public static final String getHexBytes(byte[] bytes, int len) { + return getHexBytes(bytes, 0, len); + } + + public static final String getHexBytes(byte[] bytes, int pos, int len) { + StringBuffer sb = new StringBuffer(); + for (int i = pos; i < (pos+len); i++) { + int b1 = (bytes[i]>>4) & 0x0f; + int b2 = bytes[i] & 0x0f; + + sb.append(Integer.toHexString(b1)); + sb.append(Integer.toHexString(b2)); + sb.append(' '); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/jgss/GSSUtil.java b/src/sun/security/jgss/GSSUtil.java new file mode 100644 index 00000000..f2d9f376 --- /dev/null +++ b/src/sun/security/jgss/GSSUtil.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import com.sun.security.auth.callback.TextCallbackHandler; +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.kerberos.KerberosKey; +import org.ietf.jgss.*; +import sun.security.jgss.spi.GSSNameSpi; +import sun.security.jgss.spi.GSSCredentialSpi; +import sun.security.action.GetPropertyAction; +import sun.security.jgss.krb5.Krb5NameElement; +import sun.security.jgss.spnego.SpNegoCredElement; +import java.util.Set; +import java.util.HashSet; +import java.util.Vector; +import java.util.Iterator; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import sun.security.action.GetBooleanAction; + +/** + * The GSSUtilImplementation that knows how to work with the internals of + * the GSS-API. + */ +public class GSSUtil { + + public static final Oid GSS_KRB5_MECH_OID = + GSSUtil.createOid("1.2.840.113554.1.2.2"); + public static final Oid GSS_KRB5_MECH_OID2 = + GSSUtil.createOid("1.3.5.1.5.2"); + + public static final Oid GSS_SPNEGO_MECH_OID = + GSSUtil.createOid("1.3.6.1.5.5.2"); + + public static final Oid NT_GSS_KRB5_PRINCIPAL = + GSSUtil.createOid("1.2.840.113554.1.2.2.1"); + + private static final String DEFAULT_HANDLER = + "auth.login.defaultCallbackHandler"; + + static final boolean DEBUG; + static { + DEBUG = (AccessController.doPrivileged + (new GetBooleanAction("sun.security.jgss.debug"))). + booleanValue(); + } + + static void debug(String message) { + if (DEBUG) { + assert(message != null); + System.out.println(message); + } + } + + // NOTE: this method is only for creating Oid objects with + // known to be valid oidStr given it ignores + // the GSSException + public static Oid createOid(String oidStr) { + try { + return new Oid(oidStr); + } catch (GSSException e) { + debug("Ignored invalid OID: " + oidStr); + return null; + } + } + + public static boolean isSpNegoMech(Oid oid) { + return (GSS_SPNEGO_MECH_OID.equals(oid)); + } + + public static boolean isKerberosMech(Oid oid) { + return (GSS_KRB5_MECH_OID.equals(oid) || + GSS_KRB5_MECH_OID2.equals(oid)); + + } + + public static String getMechStr(Oid oid) { + if (isSpNegoMech(oid)) { + return "SPNEGO"; + } else if (isKerberosMech(oid)) { + return "Kerberos V5"; + } else { + return oid.toString(); + } + } + + /** + * Note: The current impl only works with Sun's impl of + * GSSName and GSSCredential since it depends on package + * private APIs. + */ + public static Subject getSubject(GSSName name, + GSSCredential creds) { + + HashSet privCredentials = null; + HashSet pubCredentials = new HashSet(); // empty Set + + Set gssCredentials = null; + + Set krb5Principals = + new HashSet(); + + if (name instanceof GSSNameImpl) { + try { + GSSNameSpi ne = ((GSSNameImpl) name).getElement + (GSS_KRB5_MECH_OID); + String krbName = ne.toString(); + if (ne instanceof Krb5NameElement) { + krbName = + ((Krb5NameElement) ne).getKrb5PrincipalName().getName(); + } + KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName); + krb5Principals.add(krbPrinc); + } catch (GSSException ge) { + debug("Skipped name " + name + " due to " + ge); + } + } + + if (creds instanceof GSSCredentialImpl) { + gssCredentials = ((GSSCredentialImpl) creds).getElements(); + privCredentials = new HashSet(gssCredentials.size()); + populateCredentials(privCredentials, gssCredentials); + } else { + privCredentials = new HashSet(); // empty Set + } + debug("Created Subject with the following"); + debug("principals=" + krb5Principals); + debug("public creds=" + pubCredentials); + debug("private creds=" + privCredentials); + + return new Subject(false, krb5Principals, pubCredentials, + privCredentials); + + } + + /** + * Populates the set credentials with elements from gssCredentials. At + * the same time, it converts any subclasses of KerberosTicket + * into KerberosTicket instances and any subclasses of KerberosKey into + * KerberosKey instances. (It is not desirable to expose the customer + * to sun.security.jgss.krb5.Krb5InitCredential which extends + * KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which + * extends KerberosKey.) + */ + private static void populateCredentials(Set credentials, + Set gssCredentials) { + + Object cred; + + Iterator elements = gssCredentials.iterator(); + while (elements.hasNext()) { + + cred = elements.next(); + + // Retrieve the internal cred out of SpNegoCredElement + if (cred instanceof SpNegoCredElement) { + cred = ((SpNegoCredElement) cred).getInternalCred(); + } + + if (cred instanceof KerberosTicket) { + if (!cred.getClass().getName().equals + ("javax.security.auth.kerberos.KerberosTicket")) { + KerberosTicket tempTkt = (KerberosTicket) cred; + cred = new KerberosTicket(tempTkt.getEncoded(), + tempTkt.getClient(), + tempTkt.getServer(), + tempTkt.getSessionKey().getEncoded(), + tempTkt.getSessionKeyType(), + tempTkt.getFlags(), + tempTkt.getAuthTime(), + tempTkt.getStartTime(), + tempTkt.getEndTime(), + tempTkt.getRenewTill(), + tempTkt.getClientAddresses()); + } + credentials.add(cred); + } else if (cred instanceof KerberosKey) { + if (!cred.getClass().getName().equals + ("javax.security.auth.kerberos.KerberosKey")) { + KerberosKey tempKey = (KerberosKey) cred; + cred = new KerberosKey(tempKey.getPrincipal(), + tempKey.getEncoded(), + tempKey.getKeyType(), + tempKey.getVersionNumber()); + } + credentials.add(cred); + } else { + // Ignore non-KerberosTicket and non-KerberosKey elements + debug("Skipped cred element: " + cred); + } + } + } + + /** + * Authenticate using the login module from the specified + * configuration entry. + * + * @param caller the caller of JAAS Login + * @param mech the mech to be used + * @return the authenticated subject + */ + public static Subject login(GSSCaller caller, Oid mech) throws LoginException { + + CallbackHandler cb = null; + if (caller instanceof HttpCaller) { + cb = new sun.net.www.protocol.http.spnego.NegotiateCallbackHandler( + ((HttpCaller)caller).info()); + } else { + String defaultHandler = + java.security.Security.getProperty(DEFAULT_HANDLER); + // get the default callback handler + if ((defaultHandler != null) && (defaultHandler.length() != 0)) { + cb = null; + } else { + cb = new TextCallbackHandler(); + } + } + + // New instance of LoginConfigImpl must be created for each login, + // since the entry name is not passed as the first argument, but + // generated with caller and mech inside LoginConfigImpl + LoginContext lc = new LoginContext("", null, cb, + new LoginConfigImpl(caller, mech)); + lc.login(); + return lc.getSubject(); + } + + /** + * Determines if the application doesn't mind if the mechanism obtains + * the required credentials from outside of the current Subject. Our + * Kerberos v5 mechanism would do a JAAS login on behalf of the + * application if this were the case. + * + * The application indicates this by explicitly setting the system + * property javax.security.auth.useSubjectCredsOnly to false. + */ + public static boolean useSubjectCredsOnly(GSSCaller caller) { + + // HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it + // uses the java.net.Authenticator style, therefore always return + // false here. + if (caller instanceof HttpCaller) { + return false; + } + /* + * Don't use GetBooleanAction because the default value in the JRE + * (when this is unset) has to treated as true. + */ + String propValue = AccessController.doPrivileged( + new GetPropertyAction("javax.security.auth.useSubjectCredsOnly", + "true")); + /* + * This property has to be explicitly set to "false". Invalid + * values should be ignored and the default "true" assumed. + */ + return (!propValue.equalsIgnoreCase("false")); + } + + /** + * Determines the SPNEGO interoperability mode with Microsoft; + * by default it is set to true. + * + * To disable it, the application indicates this by explicitly setting + * the system property sun.security.spnego.interop to false. + */ + public static boolean useMSInterop() { + /* + * Don't use GetBooleanAction because the default value in the JRE + * (when this is unset) has to treated as true. + */ + String propValue = AccessController.doPrivileged( + new GetPropertyAction("sun.security.spnego.msinterop", + "true")); + /* + * This property has to be explicitly set to "false". Invalid + * values should be ignored and the default "true" assumed. + */ + return (!propValue.equalsIgnoreCase("false")); + } + + /** + * Searches the private credentials of current Subject with the + * specified criteria and returns the matching GSSCredentialSpi + * object out of Sun's impl of GSSCredential. Returns null if + * no Subject present or a Vector which contains 0 or more + * matching GSSCredentialSpi objects. + */ + public static Vector + searchSubject(final GSSNameSpi name, + final Oid mech, + final boolean initiate, + final Class credCls) { + debug("Search Subject for " + getMechStr(mech) + + (initiate? " INIT" : " ACCEPT") + " cred (" + + (name == null? "<>" : name.toString()) + ", " + + credCls.getName() + ")"); + final AccessControlContext acc = AccessController.getContext(); + try { + Vector creds = + AccessController.doPrivileged + (new PrivilegedExceptionAction>() { + public Vector run() throws Exception { + Subject accSubj = Subject.getSubject(acc); + Vector result = null; + if (accSubj != null) { + result = new Vector(); + Iterator iterator = + accSubj.getPrivateCredentials + (GSSCredentialImpl.class).iterator(); + while (iterator.hasNext()) { + GSSCredentialImpl cred = iterator.next(); + debug("...Found cred" + cred); + try { + GSSCredentialSpi ce = + cred.getElement(mech, initiate); + debug("......Found element: " + ce); + if (ce.getClass().equals(credCls) && + (name == null || + name.equals((Object) ce.getName()))) { + result.add(credCls.cast(ce)); + } else { + debug("......Discard element"); + } + } catch (GSSException ge) { + debug("...Discard cred (" + ge + ")"); + } + } + } else debug("No Subject"); + return result; + } + }); + return creds; + } catch (PrivilegedActionException pae) { + debug("Unexpected exception when searching Subject:"); + if (DEBUG) pae.printStackTrace(); + return null; + } + } +} diff --git a/src/sun/security/jgss/HttpCaller.java b/src/sun/security/jgss/HttpCaller.java new file mode 100644 index 00000000..b8847b88 --- /dev/null +++ b/src/sun/security/jgss/HttpCaller.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import sun.net.www.protocol.http.HttpCallerInfo; + +/** + * A special kind of GSSCaller, which origins from HTTP/Negotiate and contains + * info about what triggers the JGSS calls. + */ +public class HttpCaller extends GSSCaller { + final private HttpCallerInfo hci; + + public HttpCaller(HttpCallerInfo hci) { + super("HTTP_CLIENT"); + this.hci = hci; + } + + public HttpCallerInfo info() { + return hci; + } +} + diff --git a/src/sun/security/jgss/LoginConfigImpl.java b/src/sun/security/jgss/LoginConfigImpl.java new file mode 100644 index 00000000..a06d3db0 --- /dev/null +++ b/src/sun/security/jgss/LoginConfigImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import java.util.HashMap; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import org.ietf.jgss.Oid; + +/** + * A Configuration implementation especially designed for JGSS. + * + * @author weijun.wang + * @since 1.6 + */ +public class LoginConfigImpl extends Configuration { + + private final Configuration config; + private final GSSCaller caller; + private final String mechName; + private static final sun.security.util.Debug debug = + sun.security.util.Debug.getInstance("gssloginconfig", "\t[GSS LoginConfigImpl]"); + + /** + * A new instance of LoginConfigImpl must be created for each login request + * since it's only used by a single (caller, mech) pair + * @param caller defined in GSSUtil as CALLER_XXX final fields + * @param oid defined in GSSUtil as XXX_MECH_OID final fields + */ + public LoginConfigImpl(GSSCaller caller, Oid mech) { + + this.caller = caller; + + if (mech.equals(GSSUtil.GSS_KRB5_MECH_OID)) { + mechName = "krb5"; + } else { + throw new IllegalArgumentException(mech.toString() + " not supported"); + } + config = java.security.AccessController.doPrivileged + (new java.security.PrivilegedAction () { + public Configuration run() { + return Configuration.getConfiguration(); + } + }); + } + + /** + * @param name Almost useless, since the (caller, mech) is already passed + * into constructor. The only use will be detecting OTHER which + * is called in LoginContext + */ + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + + AppConfigurationEntry[] entries = null; + + // This is the second call from LoginContext, which we will just ignore + if ("OTHER".equalsIgnoreCase(name)) { + return null; + } + + String[] alts = null; + + // Compatibility: + // For the 4 old callers, old entry names will be used if the new + // entry name is not provided. + + if ("krb5".equals(mechName)) { + if (caller == GSSCaller.CALLER_INITIATE) { + alts = new String[] { + "com.sun.security.jgss.krb5.initiate", + "com.sun.security.jgss.initiate", + }; + } else if (caller == GSSCaller.CALLER_ACCEPT) { + alts = new String[] { + "com.sun.security.jgss.krb5.accept", + "com.sun.security.jgss.accept", + }; + } else if (caller == GSSCaller.CALLER_SSL_CLIENT) { + alts = new String[] { + "com.sun.security.jgss.krb5.initiate", + "com.sun.net.ssl.client", + }; + } else if (caller == GSSCaller.CALLER_SSL_SERVER) { + alts = new String[] { + "com.sun.security.jgss.krb5.accept", + "com.sun.net.ssl.server", + }; + } else if (caller instanceof HttpCaller) { + alts = new String[] { + "com.sun.security.jgss.krb5.initiate", + }; + } else if (caller == GSSCaller.CALLER_UNKNOWN) { + throw new AssertionError("caller not defined"); + } + } else { + throw new IllegalArgumentException(mechName + " not supported"); + // No other mech at the moment, maybe -- + /* + switch (caller) { + case GSSUtil.CALLER_INITIATE: + case GSSUtil.CALLER_SSL_CLIENT: + case GSSUtil.CALLER_HTTP_NEGOTIATE: + alts = new String[] { + "com.sun.security.jgss." + mechName + ".initiate", + }; + break; + case GSSUtil.CALLER_ACCEPT: + case GSSUtil.CALLER_SSL_SERVER: + alts = new String[] { + "com.sun.security.jgss." + mechName + ".accept", + }; + break; + case GSSUtil.CALLER_UNKNOWN: + // should never use + throw new AssertionError("caller cannot be unknown"); + default: + throw new AssertionError("caller not defined"); + } + */ + } + for (String alt: alts) { + entries = config.getAppConfigurationEntry(alt); + if (debug != null) { + debug.println("Trying " + alt + + ((entries == null)?": does not exist.":": Found!")); + } + if (entries != null) { + break; + } + } + + if (entries == null) { + if (debug != null) { + debug.println("Cannot read JGSS entry, use default values instead."); + } + entries = getDefaultConfigurationEntry(); + } + return entries; + } + + /** + * Default value for a caller-mech pair when no entry is defined in + * the system-wide Configuration object. + */ + private AppConfigurationEntry[] getDefaultConfigurationEntry() { + HashMap options = new HashMap (2); + + if (mechName == null || mechName.equals("krb5")) { + if (isServerSide(caller)) { + // Assuming the keytab file can be found through + // krb5 config file or under user home directory + options.put("useKeyTab", "true"); + options.put("storeKey", "true"); + options.put("doNotPrompt", "true"); + options.put("principal", "*"); + options.put("isInitiator", "false"); + } else { + options.put("useTicketCache", "true"); + options.put("doNotPrompt", "false"); + } + return new AppConfigurationEntry[] { + new AppConfigurationEntry( + "com.sun.security.auth.module.Krb5LoginModule", + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options) + }; + } + return null; + } + + private static boolean isServerSide (GSSCaller caller) { + return GSSCaller.CALLER_ACCEPT == caller || + GSSCaller.CALLER_SSL_SERVER == caller; + } +} diff --git a/src/sun/security/jgss/ProviderList.java b/src/sun/security/jgss/ProviderList.java new file mode 100644 index 00000000..bfc48acf --- /dev/null +++ b/src/sun/security/jgss/ProviderList.java @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import java.lang.reflect.InvocationTargetException; +import org.ietf.jgss.*; +import java.security.AccessController; +import java.security.Provider; +import java.security.Security; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Enumeration; +import java.util.Iterator; +import sun.security.jgss.spi.*; +import sun.security.jgss.wrapper.NativeGSSFactory; +import sun.security.jgss.wrapper.SunNativeProvider; +import sun.security.action.GetPropertyAction; + +/** + * This class stores the list of providers that this + * GSS-Implementation is configured to use. The GSSManagerImpl class + * queries this class whenever it needs a mechanism's factory.

    + * + * This class stores an ordered list of pairs of the form + * . When it attempts to instantiate a mechanism + * defined by oid o, it steps through the list looking for an entry + * with oid=o, or with oid=null. (An entry with oid=null matches all + * mechanisms.) When it finds such an entry, the corresponding + * provider is approached for the mechanism's factory class. + * At instantiation time this list in initialized to contain those + * system wide providers that contain a property of the form + * "GssApiMechanism.x.y.z..." where "x.y.z..." is a numeric object + * identifier with numbers x, y, z, etc. Such a property is defined + * to map to that provider's implementation of the MechanismFactory + * interface for the mechanism x.y.z... + * As and when a MechanismFactory is instantiated, it is + * cached for future use.

    + * + * An application can cause more providers to be added by means of + * the addProviderAtFront and addProviderAtEnd methods on + * GSSManager which get delegated to this class. The + * addProviderAtFront method can also cause a change in the ordering + * of the providers without adding any new providers, by causing a + * provider to move up in a list. The method addProviderAtEnd can + * only add providers at the end of the list if they are not already + * in the list. The rationale is that an application will call + * addProviderAtFront when it wants a provider to be used in + * preference over the default ones. And it will call + * addProviderAtEnd when it wants a provider to be used in case + * the system ones don't suffice.

    + * + * If a mechanism's factory is being obtained from a provider as a + * result of encountering a entryof the form where + * oid is non-null, then the assumption is that the application added + * this entry and it wants this mechanism to be obtained from this + * provider. Thus is the provider does not actually contain the + * requested mechanism, an exception will be thrown. However, if the + * entry were of the form , then it is viewed more + * liberally and is simply skipped over if the provider does not claim to + * support the requested mechanism. + */ + +public final class ProviderList { + + private static final String PROV_PROP_PREFIX = "GssApiMechanism."; + private static final int PROV_PROP_PREFIX_LEN = + PROV_PROP_PREFIX.length(); + + private static final String SPI_MECH_FACTORY_TYPE + = "sun.security.jgss.spi.MechanismFactory"; + + // Undocumented property? + private static final String DEFAULT_MECH_PROP = + "sun.security.jgss.mechanism"; + + public static final Oid DEFAULT_MECH_OID; + + static { + /* + * Set the default mechanism. Kerberos v5 is the default + * mechanism unless it is overridden by a system property. + * with a valid OID value + */ + Oid defOid = null; + String defaultOidStr = AccessController.doPrivileged + (new GetPropertyAction(DEFAULT_MECH_PROP)); + if (defaultOidStr != null) { + defOid = GSSUtil.createOid(defaultOidStr); + } + DEFAULT_MECH_OID = + (defOid == null ? GSSUtil.GSS_KRB5_MECH_OID : defOid); + } + + private ArrayList preferences = + new ArrayList(5); + private HashMap factories = + new HashMap(5); + private HashSet mechs = new HashSet(5); + + final private GSSCaller caller; + + public ProviderList(GSSCaller caller, boolean useNative) { + this.caller = caller; + Provider[] provList; + if (useNative) { + provList = new Provider[1]; + provList[0] = new SunNativeProvider(); + } else { + provList = Security.getProviders(); + } + + for (int i = 0; i < provList.length; i++) { + Provider prov = provList[i]; + try { + addProviderAtEnd(prov, null); + } catch (GSSException ge) { + // Move on to the next provider + GSSUtil.debug("Error in adding provider " + + prov.getName() + ": " + ge); + } + } // End of for loop + } + + /** + * Determines if the given provider property represents a GSS-API + * Oid to MechanismFactory mapping. + * @return true if this is a GSS-API property, false otherwise. + */ + private boolean isMechFactoryProperty(String prop) { + return (prop.startsWith(PROV_PROP_PREFIX) || + prop.regionMatches(true, 0, // Try ignoring case + PROV_PROP_PREFIX, 0, + PROV_PROP_PREFIX_LEN)); + } + + private Oid getOidFromMechFactoryProperty(String prop) + throws GSSException { + + String oidPart = prop.substring(PROV_PROP_PREFIX_LEN); + return new Oid(oidPart); + } + + // So the existing code do not have to be changed + synchronized public MechanismFactory getMechFactory(Oid mechOid) + throws GSSException { + if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID; + return getMechFactory(mechOid, null); + } + + /** + * Obtains a MechanismFactory for a given mechanism. If the + * specified provider is not null, then the impl from the + * provider is used. Otherwise, the most preferred impl based + * on the configured preferences is used. + * @param mechOid the oid of the desired mechanism + * @return a MechanismFactory for the desired mechanism. + * @throws GSSException when the specified provider does not + * support the desired mechanism, or when no provider supports + * the desired mechanism. + */ + synchronized public MechanismFactory getMechFactory(Oid mechOid, + Provider p) + throws GSSException { + + if (mechOid == null) mechOid = ProviderList.DEFAULT_MECH_OID; + + if (p == null) { + // Iterate thru all preferences to find right provider + String className; + PreferencesEntry entry; + + Iterator list = preferences.iterator(); + while (list.hasNext()) { + entry = list.next(); + if (entry.impliesMechanism(mechOid)) { + MechanismFactory retVal = getMechFactory(entry, mechOid); + if (retVal != null) return retVal; + } + } // end of while loop + throw new GSSExceptionImpl(GSSException.BAD_MECH, mechOid); + } else { + // Use the impl from the specified provider; return null if the + // the mech is unsupported by the specified provider. + PreferencesEntry entry = new PreferencesEntry(p, mechOid); + return getMechFactory(entry, mechOid); + } + } + + /** + * Helper routine that uses a preferences entry to obtain an + * implementation of a MechanismFactory from it. + * @param e the preferences entry that contains the provider and + * either a null of an explicit oid that matched the oid of the + * desired mechanism. + * @param mechOid the oid of the desired mechanism + * @throws GSSException If the application explicitly requested + * this entry's provider to be used for the desired mechanism but + * some problem is encountered + */ + private MechanismFactory getMechFactory(PreferencesEntry e, Oid mechOid) + throws GSSException { + Provider p = e.getProvider(); + + /* + * See if a MechanismFactory was previously instantiated for + * this provider and mechanism combination. + */ + PreferencesEntry searchEntry = new PreferencesEntry(p, mechOid); + MechanismFactory retVal = factories.get(searchEntry); + if (retVal == null) { + /* + * Apparently not. Now try to instantiate this class from + * the provider. + */ + String prop = PROV_PROP_PREFIX + mechOid.toString(); + String className = p.getProperty(prop); + if (className != null) { + retVal = getMechFactoryImpl(p, className, mechOid, caller); + factories.put(searchEntry, retVal); + } else { + /* + * This provider does not support this mechanism. + * If the application explicitly requested that + * this provider be used for this mechanism, then + * throw an exception + */ + if (e.getOid() != null) { + throw new GSSExceptionImpl(GSSException.BAD_MECH, + "Provider " + p.getName() + + " does not support mechanism " + mechOid); + } + } + } + return retVal; + } + + /** + * Helper routine to obtain a MechanismFactory implementation + * from the same class loader as the provider of this + * implementation. + * @param p the provider whose classloader must be used for + * instantiating the desired MechanismFactory + * @ param className the name of the MechanismFactory class + * @throws GSSException If some error occurs when trying to + * instantiate this MechanismFactory. + */ + private static MechanismFactory getMechFactoryImpl(Provider p, + String className, + Oid mechOid, + GSSCaller caller) + throws GSSException { + + try { + Class baseClass = Class.forName(SPI_MECH_FACTORY_TYPE); + + /* + * Load the implementation class with the same class loader + * that was used to load the provider. + * In order to get the class loader of a class, the + * caller's class loader must be the same as or an ancestor of + * the class loader being returned. Otherwise, the caller must + * have "getClassLoader" permission, or a SecurityException + * will be thrown. + */ + + ClassLoader cl = p.getClass().getClassLoader(); + Class implClass; + if (cl != null) { + implClass = cl.loadClass(className); + } else { + implClass = Class.forName(className); + } + + if (baseClass.isAssignableFrom(implClass)) { + + java.lang.reflect.Constructor c = + implClass.getConstructor(GSSCaller.class); + MechanismFactory mf = (MechanismFactory) (c.newInstance(caller)); + + if (mf instanceof NativeGSSFactory) { + ((NativeGSSFactory) mf).setMech(mechOid); + } + return mf; + } else { + throw createGSSException(p, className, "is not a " + + SPI_MECH_FACTORY_TYPE, null); + } + } catch (ClassNotFoundException e) { + throw createGSSException(p, className, "cannot be created", e); + } catch (NoSuchMethodException e) { + throw createGSSException(p, className, "cannot be created", e); + } catch (InvocationTargetException e) { + throw createGSSException(p, className, "cannot be created", e); + } catch (InstantiationException e) { + throw createGSSException(p, className, "cannot be created", e); + } catch (IllegalAccessException e) { + throw createGSSException(p, className, "cannot be created", e); + } catch (SecurityException e) { + throw createGSSException(p, className, "cannot be created", e); + } + } + + // Only used by getMechFactoryImpl + private static GSSException createGSSException(Provider p, + String className, + String trailingMsg, + Exception cause) { + String errClassInfo = className + " configured by " + + p.getName() + " for GSS-API Mechanism Factory "; + return new GSSExceptionImpl(GSSException.BAD_MECH, + errClassInfo + trailingMsg, + cause); + } + + public Oid[] getMechs() { + return mechs.toArray(new Oid[] {}); + } + + synchronized public void addProviderAtFront(Provider p, Oid mechOid) + throws GSSException { + + PreferencesEntry newEntry = new PreferencesEntry(p, mechOid); + PreferencesEntry oldEntry; + boolean foundSomeMech; + + Iterator list = preferences.iterator(); + while (list.hasNext()) { + oldEntry = list.next(); + if (newEntry.implies(oldEntry)) + list.remove(); + } + + if (mechOid == null) { + foundSomeMech = addAllMechsFromProvider(p); + } else { + String oidStr = mechOid.toString(); + if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null) + throw new GSSExceptionImpl(GSSException.BAD_MECH, + "Provider " + p.getName() + + " does not support " + + oidStr); + mechs.add(mechOid); + foundSomeMech = true; + } + + if (foundSomeMech) { + preferences.add(0, newEntry); + } + } + + synchronized public void addProviderAtEnd(Provider p, Oid mechOid) + throws GSSException { + + PreferencesEntry newEntry = new PreferencesEntry(p, mechOid); + PreferencesEntry oldEntry; + boolean foundSomeMech; + + Iterator list = preferences.iterator(); + while (list.hasNext()) { + oldEntry = list.next(); + if (oldEntry.implies(newEntry)) + return; + } + + // System.out.println("addProviderAtEnd: No it is not redundant"); + + if (mechOid == null) + foundSomeMech = addAllMechsFromProvider(p); + else { + String oidStr = mechOid.toString(); + if (p.getProperty(PROV_PROP_PREFIX + oidStr) == null) + throw new GSSExceptionImpl(GSSException.BAD_MECH, + "Provider " + p.getName() + + " does not support " + + oidStr); + mechs.add(mechOid); + foundSomeMech = true; + } + + if (foundSomeMech) { + preferences.add(newEntry); + } + } + + /** + * Helper routine to go through all properties contined in a + * provider and add its mechanisms to the list of supported + * mechanisms. If no default mechanism has been assinged so far, + * it sets the default MechanismFactory and Oid as well. + * @param p the provider to query + * @return true if there is at least one mechanism that this + * provider contributed, false otherwise + */ + private boolean addAllMechsFromProvider(Provider p) { + + String prop; + boolean retVal = false; + + // Get all props for this provider + Enumeration props = p.keys(); + + // See if there are any GSS prop's + while (props.hasMoreElements()) { + prop = (String) props.nextElement(); + if (isMechFactoryProperty(prop)) { + // Ok! This is a GSS provider! + try { + Oid mechOid = getOidFromMechFactoryProperty(prop); + mechs.add(mechOid); + retVal = true; + } catch (GSSException e) { + // Skip to next property + GSSUtil.debug("Ignore the invalid property " + + prop + " from provider " + p.getName()); + } + } // Processed GSS property + } // while loop + + return retVal; + + } + + /** + * Stores a provider and a mechanism oid indicating that the + * provider should be used for the mechanism. If the mechanism + * Oid is null, then it indicates that this preference holds for + * any mechanism.

    + * + * The ProviderList maintains an ordered list of + * PreferencesEntry's and iterates thru them as it tries to + * instantiate MechanismFactory's. + */ + private static final class PreferencesEntry { + private Provider p; + private Oid oid; + PreferencesEntry(Provider p, Oid oid) { + this.p = p; + this.oid = oid; + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof PreferencesEntry)) { + return false; + } + + PreferencesEntry that = (PreferencesEntry)other; + if (this.p.getName().equals(that.p.getName())) { + if (this.oid != null && that.oid != null) { + return this.oid.equals(that.oid); + } else { + return (this.oid == null && that.oid == null); + } + } + + return false; + } + + public int hashCode() { + int result = 17; + + result = 37 * result + p.getName().hashCode(); + if (oid != null) { + result = 37 * result + oid.hashCode(); + } + + return result; + } + + /** + * Determines if a preference implies another. A preference + * implies another if the latter is subsumed by the + * former. e.g., implies + * because the null in the former indicates that it should + * be used for all mechanisms. + */ + boolean implies(Object other) { + + if (other instanceof PreferencesEntry) { + PreferencesEntry temp = (PreferencesEntry) other; + return (equals(temp) || + p.getName().equals(temp.p.getName()) && + oid == null); + } else { + return false; + } + } + + Provider getProvider() { + return p; + } + + Oid getOid() { + return oid; + } + + /** + * Determines if this entry is applicable to the desired + * mechanism. The entry is applicable to the desired mech if + * it contains the same oid or if it contains a null oid + * indicating that it is applicable to all mechs. + * @param mechOid the desired mechanism + * @return true if the provider in this entry should be + * queried for this mechanism. + */ + boolean impliesMechanism(Oid oid) { + return (this.oid == null || this.oid.equals(oid)); + } + + // For debugging + public String toString() { + StringBuffer buf = new StringBuffer("<"); + buf.append(p.getName()); + buf.append(", "); + buf.append(oid); + buf.append(">"); + return buf.toString(); + } + } +} diff --git a/src/sun/security/jgss/SunProvider.java b/src/sun/security/jgss/SunProvider.java new file mode 100644 index 00000000..42396754 --- /dev/null +++ b/src/sun/security/jgss/SunProvider.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import java.security.Provider; +import java.security.AccessController; + +/** + * Defines the Sun JGSS provider. + * Will merger this with the Sun security provider + * sun.security.provider.Sun when the JGSS src is merged with the JDK + * src. + * + * Mechanisms supported are: + * + * - Kerberos v5 as defined in RFC 1964. + * Oid is 1.2.840.113554.1.2.2 + * + * - SPNEGO as defined in RFC 2478 + * Oid is 1.3.6.1.5.5.2 + * + * [Dummy mechanism is no longer compiled: + * - Dummy mechanism. This is primarily useful to test a multi-mech + * environment. + * Oid is 1.3.6.1.4.1.42.2.26.1.2] + * + * @author Mayank Upadhyay + */ + +public final class SunProvider extends Provider { + + private static final long serialVersionUID = -238911724858694198L; + + private static final String INFO = "Sun " + + "(Kerberos v5, SPNEGO)"; + // "(Kerberos v5, Dummy GSS-API Mechanism)"; + + public static final SunProvider INSTANCE = new SunProvider(); + + public SunProvider() { + /* We are the Sun JGSS provider */ + super("SunJGSS", 1.8d, INFO); + + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Void run() { + put("GssApiMechanism.1.2.840.113554.1.2.2", + "sun.security.jgss.krb5.Krb5MechFactory"); + put("GssApiMechanism.1.3.6.1.5.5.2", + "sun.security.jgss.spnego.SpNegoMechFactory"); + /* + put("GssApiMechanism.1.3.6.1.4.1.42.2.26.1.2", + "sun.security.jgss.dummy.DummyMechFactory"); + */ + return null; + } + }); + } +} diff --git a/src/sun/security/jgss/TokenTracker.java b/src/sun/security/jgss/TokenTracker.java new file mode 100644 index 00000000..ee63e9c2 --- /dev/null +++ b/src/sun/security/jgss/TokenTracker.java @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss; + +import org.ietf.jgss.MessageProp; +import java.util.LinkedList; + +/** + * A utility class that implements a number list that keeps track of which + * tokens have arrived by storing their token numbers in the list. It helps + * detect old tokens, out of sequence tokens, and duplicate tokens. + * + * Each element of the list is an interval [a, b]. Its existence in the + * list implies that all token numbers in the range a, a+1, ..., b-1, b + * have arrived. Gaps in arrived token numbers are represented by the + * numbers that fall in between two elements of the list. eg. {[a,b], + * [c,d]} indicates that the token numbers b+1, ..., c-1 have not arrived + * yet. + * + * The maximum number of intervals that we keep track of is + * MAX_INTERVALS. Thus if there are too many gaps, then some of the older + * sequence numbers are deleted from the list. The earliest sequence number + * that exists in the list is the windowStart. The next expected sequence + * number, or expectedNumber, is one greater than the latest sequence + * number in the list. + * + * The list keeps track the first token number that should have arrived + * (initNumber) so that it is able to detect if certain numbers occur after + * the first valid token number but before windowStart. That would happen + * if the number of elements (intervals) exceeds MAX_INTERVALS and some + * initial elements had to be deleted. + * + * The working of the list is optimized for the normal case where the + * tokens arrive in sequence. + * + * @author Mayank Upadhyay + * @since 1.4 + */ +public class TokenTracker { + + static final int MAX_INTERVALS = 5; + + private int initNumber; + private int windowStart; + private int expectedNumber; + + private int windowStartIndex = 0; + + private LinkedList list = new LinkedList(); + + public TokenTracker(int initNumber) { + + this.initNumber = initNumber; + this.windowStart = initNumber; + this.expectedNumber = initNumber; + + // Make an entry with one less than the expected first token + Entry entry = new Entry(initNumber-1); + + list.add(entry); + } + + /** + * Returns the index for the entry into which this number will fit. If + * there is none, it returns the index of the last interval + * which precedes this number. It returns -1 if the number needs to be + * a in a new interval ahead of the whole list. + */ + private int getIntervalIndex(int number) { + Entry entry = null; + int i; + // Start from the rear to optimize for the normal case + for (i = list.size() - 1; i >= 0; i--) { + entry = list.get(i); + if (entry.compareTo(number) <= 0) + break; + } + return i; + } + + /** + * Sets the sequencing and replay information for the given token + * number. + * + * The following represents the number line with positions of + * initNumber, windowStart, expectedNumber marked on it. Regions in + * between them show the different sequencing and replay state + * possibilites for tokens that fall in there. + * + * (1) windowStart + * initNumber expectedNumber + * | | + * ---|---------------------------|--- + * GAP | DUP/UNSEQ | GAP + * + * + * (2) initNumber windowStart expectedNumber + * | | | + * ---|---------------|--------------|--- + * GAP | OLD | DUP/UNSEQ | GAP + * + * + * (3) windowStart + * expectedNumber initNumber + * | | + * ---|---------------------------|--- + * DUP/UNSEQ | GAP | DUP/UNSEQ + * + * + * (4) expectedNumber initNumber windowStart + * | | | + * ---|---------------|--------------|--- + * DUP/UNSEQ | GAP | OLD | DUP/UNSEQ + * + * + * + * (5) windowStart expectedNumber initNumber + * | | | + * ---|---------------|--------------|--- + * OLD | DUP/UNSEQ | GAP | OLD + * + * + * + * (This analysis leaves out the possibility that expectedNumber passes + * initNumber after wrapping around. That may be added later.) + */ + synchronized public final void getProps(int number, MessageProp prop) { + + boolean gap = false; + boolean old = false; + boolean unsequenced = false; + boolean duplicate = false; + + // System.out.println("\n\n=========="); + // System.out.println("TokenTracker.getProps(): number=" + number); + // System.out.println(toString()); + + int pos = getIntervalIndex(number); + Entry entry = null; + if (pos != -1) + entry = list.get(pos); + + // Optimize for the expected case: + + if (number == expectedNumber) { + expectedNumber++; + } else { + + // Next trivial case is to check for duplicate + if (entry != null && entry.contains(number)) + duplicate = true; + else { + + if (expectedNumber >= initNumber) { + + // Cases (1) and (2) + + if (number > expectedNumber) { + gap = true; + } else if (number >= windowStart) { + unsequenced = true; + } else if (number >= initNumber) { + old = true; + } else { + gap = true; + } + } else { + + // Cases (3), (4) and (5) + + if (number > expectedNumber) { + if (number < initNumber) { + gap = true; + } else if (windowStart >= initNumber) { + if (number >= windowStart) { + unsequenced = true; + } else + old = true; + } else { + old = true; + } + } else if (windowStart > expectedNumber) { + unsequenced = true; + } else if (number < windowStart) { + old = true; + } else + unsequenced = true; + } + } + } + + if (!duplicate && !old) + add(number, pos); + + if (gap) + expectedNumber = number+1; + + prop.setSupplementaryStates(duplicate, old, unsequenced, gap, + 0, null); + + // System.out.println("Leaving with state:"); + // System.out.println(toString()); + // System.out.println("==========\n"); + } + + /** + * Adds the number to the list just after the entry that is currently + * at position prevEntryPos. If prevEntryPos is -1, then the number + * will begin a new interval at the front of the list. + */ + private void add(int number, int prevEntryPos) { + + Entry entry; + Entry entryBefore = null; + Entry entryAfter = null; + + boolean appended = false; + boolean prepended = false; + + if (prevEntryPos != -1) { + entryBefore = list.get(prevEntryPos); + + // Can this number simply be added to the previous interval? + if (number == (entryBefore.getEnd() + 1)) { + entryBefore.setEnd(number); + appended = true; + } + } + + // Now check the interval that follows this number + + int nextEntryPos = prevEntryPos + 1; + if ((nextEntryPos) < list.size()) { + entryAfter = list.get(nextEntryPos); + + // Can this number simply be added to the next interval? + if (number == (entryAfter.getStart() - 1)) { + if (!appended) { + entryAfter.setStart(number); + } else { + // Merge the two entries + entryAfter.setStart(entryBefore.getStart()); + list.remove(prevEntryPos); + // Index of any entry following this gets decremented + if (windowStartIndex > prevEntryPos) + windowStartIndex--; + } + prepended = true; + } + } + + if (prepended || appended) + return; + + /* + * At this point we know that the number will start a new interval + * which needs to be added to the list. We might have to recyle an + * older entry in the list. + */ + + if (list.size() < MAX_INTERVALS) { + entry = new Entry(number); + if (prevEntryPos < windowStartIndex) + windowStartIndex++; // due to the insertion which will happen + } else { + /* + * Delete the entry that marks the start of the current window. + * The marker will automatically point to the next entry in the + * list when this happens. If the current entry is at the end + * of the list then set the marker to the start of the list. + */ + int oldWindowStartIndex = windowStartIndex; + if (windowStartIndex == (list.size() - 1)) + windowStartIndex = 0; + + entry = list.remove(oldWindowStartIndex); + windowStart = list.get(windowStartIndex).getStart(); + entry.setStart(number); + entry.setEnd(number); + + if (prevEntryPos >= oldWindowStartIndex) { + prevEntryPos--; // due to the deletion that just happened + } else { + /* + * If the start of the current window just moved from the + * end of the list to the front of the list, and if the new + * entry will be added to the front of the list, then + * the new entry is the actual window start. + * eg, Consider { [-10, -8], ..., [-6, -3], [3, 9]}. In + * this list, suppose the element [3, 9] is the start of + * the window and has to be deleted to make place to add + * [-12, -12]. The resultant list will be + * {[-12, -12], [-10, -8], ..., [-6, -3]} and the new start + * of the window should be the element [-12, -12], not + * [-10, -8] which succeeded [3, 9] in the old list. + */ + if (oldWindowStartIndex != windowStartIndex) { + // windowStartIndex is 0 at this point + if (prevEntryPos == -1) + // The new entry is going to the front + windowStart = number; + } else { + // due to the insertion which will happen: + windowStartIndex++; + } + } + } + + // Finally we are ready to actually add to the list at index + // 'prevEntryPos+1' + + list.add(prevEntryPos+1, entry); + } + + public String toString() { + StringBuffer buf = new StringBuffer("TokenTracker: "); + buf.append(" initNumber=").append(initNumber); + buf.append(" windowStart=").append(windowStart); + buf.append(" expectedNumber=").append(expectedNumber); + buf.append(" windowStartIndex=").append(windowStartIndex); + buf.append("\n\tIntervals are: {"); + for (int i = 0; i < list.size(); i++) { + if (i != 0) + buf.append(", "); + buf.append(list.get(i).toString()); + } + buf.append('}'); + return buf.toString(); + } + + /** + * An entry in the list that represents the sequence of received + * tokens. Each entry is actaully an interval of numbers, all of which + * have been received. + */ + class Entry { + + private int start; + private int end; + + Entry(int number) { + start = number; + end = number; + } + + /** + * Returns -1 if this interval represented by this entry precedes + * the number, 0 if the the number is contained in the interval, + * and -1 if the interval occurs after the number. + */ + final int compareTo(int number) { + if (start > number) + return 1; + else if (end < number) + return -1; + else + return 0; + } + + final boolean contains(int number) { + return (number >= start && + number <= end); + } + + final void append(int number) { + if (number == (end + 1)) + end = number; + } + + final void setInterval(int start, int end) { + this.start = start; + this.end = end; + } + + final void setEnd(int end) { + this.end = end; + } + + final void setStart(int start) { + this.start = start; + } + + final int getStart() { + return start; + } + + final int getEnd() { + return end; + } + + public String toString() { + return ("[" + start + ", " + end + "]"); + } + + } +} diff --git a/src/sun/security/jgss/krb5/AcceptSecContextToken.java b/src/sun/security/jgss/krb5/AcceptSecContextToken.java new file mode 100644 index 00000000..35459edb --- /dev/null +++ b/src/sun/security/jgss/krb5/AcceptSecContextToken.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.IOException; +import java.security.AccessController; + +import sun.security.action.GetBooleanAction; +import sun.security.krb5.*; + +class AcceptSecContextToken extends InitialToken { + + private KrbApRep apRep = null; + + /** + * Creates an AcceptSecContextToken for the context acceptor to send to + * the context initiator. + */ + public AcceptSecContextToken(Krb5Context context, + KrbApReq apReq) + throws KrbException, IOException, GSSException { + + boolean useSubkey = AccessController.doPrivileged( + new GetBooleanAction("sun.security.krb5.acceptor.subkey")); + + boolean useSequenceNumber = true; + + EncryptionKey subKey = null; + if (useSubkey) { + subKey = new EncryptionKey(apReq.getCreds().getSessionKey()); + context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey); + } + apRep = new KrbApRep(apReq, useSequenceNumber, subKey); + + context.resetMySequenceNumber(apRep.getSeqNumber().intValue()); + + /* + * Note: The acceptor side context key was set when the + * InitSecContextToken was received. + */ + } + + /** + * Creates an AcceptSecContextToken at the context initiator's side + * using the bytes received from the acceptor. + */ + public AcceptSecContextToken(Krb5Context context, + Credentials serviceCreds, KrbApReq apReq, + InputStream is) + throws IOException, GSSException, KrbException { + + int tokenId = ((is.read()<<8) | is.read()); + + if (tokenId != Krb5Token.AP_REP_ID) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "AP_REP token id does not match!"); + + byte[] apRepBytes = + new sun.security.util.DerValue(is).toByteArray(); + + KrbApRep apRep = new KrbApRep(apRepBytes, serviceCreds, apReq); + + /* + * Allow the context acceptor to set a subkey if desired, even + * though our context acceptor will not do so. + */ + EncryptionKey subKey = apRep.getSubKey(); + if (subKey != null) { + context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey); + /* + System.out.println("\n\nSub-Session key from AP-REP is: " + + getHexBytes(subKey.getBytes()) + "\n"); + */ + } + + Integer apRepSeqNumber = apRep.getSeqNumber(); + int peerSeqNumber = (apRepSeqNumber != null ? + apRepSeqNumber.intValue() : + 0); + context.resetPeerSequenceNumber(peerSeqNumber); + } + + public final byte[] encode() throws IOException { + byte[] apRepBytes = apRep.getMessage(); + byte[] retVal = new byte[2 + apRepBytes.length]; + writeInt(Krb5Token.AP_REP_ID, retVal, 0); + System.arraycopy(apRepBytes, 0, retVal, 2, apRepBytes.length); + return retVal; + } +} diff --git a/src/sun/security/jgss/krb5/CipherHelper.java b/src/sun/security/jgss/krb5/CipherHelper.java new file mode 100644 index 00000000..c637b016 --- /dev/null +++ b/src/sun/security/jgss/krb5/CipherHelper.java @@ -0,0 +1,1504 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import org.ietf.jgss.*; + +import java.security.MessageDigest; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import sun.security.krb5.*; +import sun.security.krb5.internal.crypto.Des3; +import sun.security.krb5.internal.crypto.Aes128; +import sun.security.krb5.internal.crypto.Aes256; +import sun.security.krb5.internal.crypto.ArcFourHmac; + +class CipherHelper { + + // From draft-raeburn-cat-gssapi-krb5-3des-00 + // Key usage values when deriving keys + private static final int KG_USAGE_SEAL = 22; + private static final int KG_USAGE_SIGN = 23; + private static final int KG_USAGE_SEQ = 24; + + private static final int DES_CHECKSUM_SIZE = 8; + private static final int DES_IV_SIZE = 8; + private static final int AES_IV_SIZE = 16; + + // ARCFOUR-HMAC + // Save first 8 octets of HMAC Sgn_Cksum + private static final int HMAC_CHECKSUM_SIZE = 8; + // key usage for MIC tokens used by MS + private static final int KG_USAGE_SIGN_MS = 15; + + // debug flag + private static final boolean DEBUG = Krb5Util.DEBUG; + + /** + * A zero initial vector to be used for checksum calculation and for + * DesCbc application data encryption/decryption. + */ + private static final byte[] ZERO_IV = new byte[DES_IV_SIZE]; + private static final byte[] ZERO_IV_AES = new byte[AES_IV_SIZE]; + + private int etype; + private int sgnAlg, sealAlg; + private byte[] keybytes; + + // new token format from draft-ietf-krb-wg-gssapi-cfx-07 + // proto is used to determine new GSS token format for "newer" etypes + private int proto = 0; + + CipherHelper(EncryptionKey key) throws GSSException { + etype = key.getEType(); + keybytes = key.getBytes(); + + switch (etype) { + case EncryptedData.ETYPE_DES_CBC_CRC: + case EncryptedData.ETYPE_DES_CBC_MD5: + sgnAlg = MessageToken.SGN_ALG_DES_MAC_MD5; + sealAlg = MessageToken.SEAL_ALG_DES; + break; + + case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD: + sgnAlg = MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD; + sealAlg = MessageToken.SEAL_ALG_DES3_KD; + break; + + case EncryptedData.ETYPE_ARCFOUR_HMAC: + sgnAlg = MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR; + sealAlg = MessageToken.SEAL_ALG_ARCFOUR_HMAC; + break; + + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + sgnAlg = -1; + sealAlg = -1; + proto = 1; + break; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported encryption type: " + etype); + } + } + + int getSgnAlg() { + return sgnAlg; + } + + int getSealAlg() { + return sealAlg; + } + + int getProto() { + return proto; + } + + int getEType() { + return etype; + } + + boolean isArcFour() { + boolean flag = false; + if (etype == EncryptedData.ETYPE_ARCFOUR_HMAC) { + flag = true; + } + return flag; + } + + @SuppressWarnings("fallthrough") + byte[] calculateChecksum(int alg, byte[] header, byte[] trailer, + byte[] data, int start, int len, int tokenId) throws GSSException { + + switch (alg) { + case MessageToken.SGN_ALG_DES_MAC_MD5: + /* + * With this sign algorithm, first an MD5 hash is computed on the + * application data. The 16 byte hash is then DesCbc encrypted. + */ + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + + // debug("\t\tdata=["); + + // debug(getHexBytes(checksumDataHeader, + // checksumDataHeader.length) + " "); + md5.update(header); + + // debug(getHexBytes(data, start, len)); + md5.update(data, start, len); + + if (trailer != null) { + // debug(" " + + // getHexBytes(trailer, + // optionalTrailer.length)); + md5.update(trailer); + } + // debug("]\n"); + + data = md5.digest(); + start = 0; + len = data.length; + // System.out.println("\tMD5 Checksum is [" + + // getHexBytes(data) + "]\n"); + header = null; + trailer = null; + } catch (NoSuchAlgorithmException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not get MD5 Message Digest - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + // fall through to encrypt checksum + + case MessageToken.SGN_ALG_DES_MAC: + return getDesCbcChecksum(keybytes, header, data, start, len); + + case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD: + byte[] buf; + int offset, total; + if (header == null && trailer == null) { + buf = data; + total = len; + offset = start; + } else { + total = ((header != null ? header.length : 0) + len + + (trailer != null ? trailer.length : 0)); + + buf = new byte[total]; + int pos = 0; + if (header != null) { + System.arraycopy(header, 0, buf, 0, header.length); + pos = header.length; + } + System.arraycopy(data, start, buf, pos, len); + pos += len; + if (trailer != null) { + System.arraycopy(trailer, 0, buf, pos, trailer.length); + } + + offset = 0; + } + + try { + + /* + Krb5Token.debug("\nkeybytes: " + + Krb5Token.getHexBytes(keybytes)); + Krb5Token.debug("\nheader: " + (header == null ? "NONE" : + Krb5Token.getHexBytes(header))); + Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" : + Krb5Token.getHexBytes(trailer))); + Krb5Token.debug("\ndata: " + + Krb5Token.getHexBytes(data, start, len)); + Krb5Token.debug("\nbuf: " + Krb5Token.getHexBytes(buf, offset, + total)); + */ + + byte[] answer = Des3.calculateChecksum(keybytes, + KG_USAGE_SIGN, buf, offset, total); + // Krb5Token.debug("\nanswer: " + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use HMAC-SHA1-DES3-KD signing algorithm - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR: + byte[] buffer; + int off, tot; + if (header == null && trailer == null) { + buffer = data; + tot = len; + off = start; + } else { + tot = ((header != null ? header.length : 0) + len + + (trailer != null ? trailer.length : 0)); + + buffer = new byte[tot]; + int pos = 0; + + if (header != null) { + System.arraycopy(header, 0, buffer, 0, header.length); + pos = header.length; + } + System.arraycopy(data, start, buffer, pos, len); + pos += len; + if (trailer != null) { + System.arraycopy(trailer, 0, buffer, pos, trailer.length); + } + + off = 0; + } + + try { + + /* + Krb5Token.debug("\nkeybytes: " + + Krb5Token.getHexBytes(keybytes)); + Krb5Token.debug("\nheader: " + (header == null ? "NONE" : + Krb5Token.getHexBytes(header))); + Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" : + Krb5Token.getHexBytes(trailer))); + Krb5Token.debug("\ndata: " + + Krb5Token.getHexBytes(data, start, len)); + Krb5Token.debug("\nbuffer: " + + Krb5Token.getHexBytes(buffer, off, tot)); + */ + + // for MIC tokens, key derivation salt is 15 + // NOTE: Required for interoperability. The RC4-HMAC spec + // defines key_usage of 23, however all Kerberos impl. + // MS/Solaris/MIT all use key_usage of 15 for MIC tokens + int key_usage = KG_USAGE_SIGN; + if (tokenId == Krb5Token.MIC_ID) { + key_usage = KG_USAGE_SIGN_MS; + } + byte[] answer = ArcFourHmac.calculateChecksum(keybytes, + key_usage, buffer, off, tot); + // Krb5Token.debug("\nanswer: " + + // Krb5Token.getHexBytes(answer)); + + // Save first 8 octets of HMAC Sgn_Cksum + byte[] output = new byte[getChecksumLength()]; + System.arraycopy(answer, 0, output, 0, output.length); + // Krb5Token.debug("\nanswer (trimmed): " + + // Krb5Token.getHexBytes(output)); + return output; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use HMAC_MD5_ARCFOUR signing algorithm - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported signing algorithm: " + sgnAlg); + } + } + + // calculate Checksum for the new GSS tokens + byte[] calculateChecksum(byte[] header, byte[] data, int start, int len, + int key_usage) throws GSSException { + + // total length + int total = ((header != null ? header.length : 0) + len); + + // get_mic("plaintext-data" | "header") + byte[] buf = new byte[total]; + + // data + System.arraycopy(data, start, buf, 0, len); + + // token header + if (header != null) { + System.arraycopy(header, 0, buf, len, header.length); + } + + // Krb5Token.debug("\nAES calculate checksum on: " + + // Krb5Token.getHexBytes(buf)); + switch (etype) { + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + try { + byte[] answer = Aes128.calculateChecksum(keybytes, key_usage, + buf, 0, total); + // Krb5Token.debug("\nAES128 checksum: " + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES128 signing algorithm - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + try { + byte[] answer = Aes256.calculateChecksum(keybytes, key_usage, + buf, 0, total); + // Krb5Token.debug("\nAES256 checksum: " + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES256 signing algorithm - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported encryption type: " + etype); + } + } + + byte[] encryptSeq(byte[] ivec, byte[] plaintext, int start, int len) + throws GSSException { + + switch (sgnAlg) { + case MessageToken.SGN_ALG_DES_MAC_MD5: + case MessageToken.SGN_ALG_DES_MAC: + try { + Cipher des = getInitializedDes(true, keybytes, ivec); + return des.doFinal(plaintext, start, len); + + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not encrypt sequence number using DES - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD: + byte[] iv; + if (ivec.length == DES_IV_SIZE) { + iv = ivec; + } else { + iv = new byte[DES_IV_SIZE]; + System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE); + } + try { + return Des3.encryptRaw(keybytes, KG_USAGE_SEQ, iv, + plaintext, start, len); + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not encrypt sequence number using DES3-KD - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR: + // ivec passed is the checksum + byte[] checksum; + if (ivec.length == HMAC_CHECKSUM_SIZE) { + checksum = ivec; + } else { + checksum = new byte[HMAC_CHECKSUM_SIZE]; + System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE); + } + + try { + return ArcFourHmac.encryptSeq(keybytes, KG_USAGE_SEQ, checksum, + plaintext, start, len); + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not encrypt sequence number using RC4-HMAC - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported signing algorithm: " + sgnAlg); + } + } + + byte[] decryptSeq(byte[] ivec, byte[] ciphertext, int start, int len) + throws GSSException { + + switch (sgnAlg) { + case MessageToken.SGN_ALG_DES_MAC_MD5: + case MessageToken.SGN_ALG_DES_MAC: + try { + Cipher des = getInitializedDes(false, keybytes, ivec); + return des.doFinal(ciphertext, start, len); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not decrypt sequence number using DES - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD: + byte[] iv; + if (ivec.length == DES_IV_SIZE) { + iv = ivec; + } else { + iv = new byte[8]; + System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE); + } + + try { + return Des3.decryptRaw(keybytes, KG_USAGE_SEQ, iv, + ciphertext, start, len); + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not decrypt sequence number using DES3-KD - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR: + // ivec passed is the checksum + byte[] checksum; + if (ivec.length == HMAC_CHECKSUM_SIZE) { + checksum = ivec; + } else { + checksum = new byte[HMAC_CHECKSUM_SIZE]; + System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE); + } + + try { + return ArcFourHmac.decryptSeq(keybytes, KG_USAGE_SEQ, checksum, + ciphertext, start, len); + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not decrypt sequence number using RC4-HMAC - " + + e.getMessage()); + ge.initCause(e); + throw ge; + } + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported signing algorithm: " + sgnAlg); + } + } + + int getChecksumLength() throws GSSException { + switch (etype) { + case EncryptedData.ETYPE_DES_CBC_CRC: + case EncryptedData.ETYPE_DES_CBC_MD5: + return DES_CHECKSUM_SIZE; + + case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD: + return Des3.getChecksumLength(); + + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + return Aes128.getChecksumLength(); + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + return Aes256.getChecksumLength(); + + case EncryptedData.ETYPE_ARCFOUR_HMAC: + // only first 8 octets of HMAC Sgn_Cksum are used + return HMAC_CHECKSUM_SIZE; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported encryption type: " + etype); + } + } + + void decryptData(WrapToken token, byte[] ciphertext, int cStart, int cLen, + byte[] plaintext, int pStart) throws GSSException { + + /* + Krb5Token.debug("decryptData : ciphertext = " + + Krb5Token.getHexBytes(ciphertext)); + */ + + switch (sealAlg) { + case MessageToken.SEAL_ALG_DES: + desCbcDecrypt(token, getDesEncryptionKey(keybytes), + ciphertext, cStart, cLen, plaintext, pStart); + break; + + case MessageToken.SEAL_ALG_DES3_KD: + des3KdDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart); + break; + + case MessageToken.SEAL_ALG_ARCFOUR_HMAC: + arcFourDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart); + break; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported seal algorithm: " + sealAlg); + } + } + + // decrypt data in the new GSS tokens + void decryptData(WrapToken_v2 token, byte[] ciphertext, int cStart, + int cLen, byte[] plaintext, int pStart, int key_usage) + throws GSSException { + + /* + Krb5Token.debug("decryptData : ciphertext = " + + Krb5Token.getHexBytes(ciphertext)); + */ + + switch (etype) { + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + aes128Decrypt(token, ciphertext, cStart, cLen, + plaintext, pStart, key_usage); + break; + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + aes256Decrypt(token, ciphertext, cStart, cLen, + plaintext, pStart, key_usage); + break; + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported etype: " + etype); + } + } + + void decryptData(WrapToken token, InputStream cipherStream, int cLen, + byte[] plaintext, int pStart) + throws GSSException, IOException { + + switch (sealAlg) { + case MessageToken.SEAL_ALG_DES: + desCbcDecrypt(token, getDesEncryptionKey(keybytes), + cipherStream, cLen, plaintext, pStart); + break; + + case MessageToken.SEAL_ALG_DES3_KD: + + // Read encrypted data from stream + byte[] ciphertext = new byte[cLen]; + try { + Krb5Token.readFully(cipherStream, ciphertext, 0, cLen); + } catch (IOException e) { + GSSException ge = new GSSException( + GSSException.DEFECTIVE_TOKEN, -1, + "Cannot read complete token"); + ge.initCause(e); + throw ge; + } + + des3KdDecrypt(token, ciphertext, 0, cLen, plaintext, pStart); + break; + + case MessageToken.SEAL_ALG_ARCFOUR_HMAC: + + // Read encrypted data from stream + byte[] ctext = new byte[cLen]; + try { + Krb5Token.readFully(cipherStream, ctext, 0, cLen); + } catch (IOException e) { + GSSException ge = new GSSException( + GSSException.DEFECTIVE_TOKEN, -1, + "Cannot read complete token"); + ge.initCause(e); + throw ge; + } + + arcFourDecrypt(token, ctext, 0, cLen, plaintext, pStart); + break; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported seal algorithm: " + sealAlg); + } + } + + void decryptData(WrapToken_v2 token, InputStream cipherStream, int cLen, + byte[] plaintext, int pStart, int key_usage) + throws GSSException, IOException { + + // Read encrypted data from stream + byte[] ciphertext = new byte[cLen]; + try { + Krb5Token.readFully(cipherStream, ciphertext, 0, cLen); + } catch (IOException e) { + GSSException ge = new GSSException( + GSSException.DEFECTIVE_TOKEN, -1, + "Cannot read complete token"); + ge.initCause(e); + throw ge; + } + switch (etype) { + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + aes128Decrypt(token, ciphertext, 0, cLen, + plaintext, pStart, key_usage); + break; + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + aes256Decrypt(token, ciphertext, 0, cLen, + plaintext, pStart, key_usage); + break; + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported etype: " + etype); + } + } + + void encryptData(WrapToken token, byte[] confounder, byte[] plaintext, + int start, int len, byte[] padding, OutputStream os) + throws GSSException, IOException { + + switch (sealAlg) { + case MessageToken.SEAL_ALG_DES: + // Encrypt on the fly and write + Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes), + ZERO_IV); + CipherOutputStream cos = new CipherOutputStream(os, des); + // debug(getHexBytes(confounder, confounder.length)); + cos.write(confounder); + // debug(" " + getHexBytes(plaintext, start, len)); + cos.write(plaintext, start, len); + // debug(" " + getHexBytes(padding, padding.length)); + cos.write(padding); + break; + + case MessageToken.SEAL_ALG_DES3_KD: + byte[] ctext = des3KdEncrypt(confounder, plaintext, start, len, + padding); + + // Write to stream + os.write(ctext); + break; + + case MessageToken.SEAL_ALG_ARCFOUR_HMAC: + byte[] ciphertext = arcFourEncrypt(token, confounder, plaintext, + start, len, padding); + + // Write to stream + os.write(ciphertext); + break; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported seal algorithm: " + sealAlg); + } + } + + /* + * Encrypt data in the new GSS tokens + * + * Wrap Tokens (with confidentiality) + * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) | + * 12-byte HMAC } + * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header} + * HMAC is not encrypted; it is appended at the end. + */ + byte[] encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader, + byte[] plaintext, int start, int len, int key_usage) + throws GSSException { + + switch (etype) { + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + return aes128Encrypt(confounder, tokenHeader, + plaintext, start, len, key_usage); + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + return aes256Encrypt(confounder, tokenHeader, + plaintext, start, len, key_usage); + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported etype: " + etype); + } + } + + void encryptData(WrapToken token, byte[] confounder, byte[] plaintext, + int pStart, int pLen, byte[] padding, byte[] ciphertext, int cStart) + throws GSSException { + + switch (sealAlg) { + case MessageToken.SEAL_ALG_DES: + int pos = cStart; + // Encrypt and write + Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes), + ZERO_IV); + try { + // debug(getHexBytes(confounder, confounder.length)); + pos += des.update(confounder, 0, confounder.length, + ciphertext, pos); + // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen)); + pos += des.update(plaintext, pStart, pLen, + ciphertext, pos); + // debug(" " + getHexBytes(padding, padding.length)); + des.update(padding, 0, padding.length, + ciphertext, pos); + des.doFinal(); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + break; + + case MessageToken.SEAL_ALG_DES3_KD: + byte[] ctext = des3KdEncrypt(confounder, plaintext, pStart, pLen, + padding); + System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length); + break; + + case MessageToken.SEAL_ALG_ARCFOUR_HMAC: + byte[] ctext2 = arcFourEncrypt(token, confounder, plaintext, pStart, + pLen, padding); + System.arraycopy(ctext2, 0, ciphertext, cStart, ctext2.length); + break; + + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported seal algorithm: " + sealAlg); + } + } + + /* + * Encrypt data in the new GSS tokens + * + * Wrap Tokens (with confidentiality) + * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) | + * 12-byte HMAC } + * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header} + * HMAC is not encrypted; it is appended at the end. + */ + int encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader, + byte[] plaintext, int pStart, int pLen, byte[] ciphertext, int cStart, + int key_usage) throws GSSException { + + byte[] ctext = null; + switch (etype) { + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + ctext = aes128Encrypt(confounder, tokenHeader, + plaintext, pStart, pLen, key_usage); + break; + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + ctext = aes256Encrypt(confounder, tokenHeader, + plaintext, pStart, pLen, key_usage); + break; + default: + throw new GSSException(GSSException.FAILURE, -1, + "Unsupported etype: " + etype); + } + System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length); + return ctext.length; + } + + // --------------------- DES methods + + /** + * Computes the DesCbc checksum based on the algorithm published in FIPS + * Publication 113. This involves applying padding to the data passed + * in, then performing DesCbc encryption on the data with a zero initial + * vector, and finally returning the last 8 bytes of the encryption + * result. + * + * @param key the bytes for the DES key + * @param header a header to process first before the data is. + * @param data the data to checksum + * @param offset the offset where the data begins + * @param len the length of the data + * @throws GSSException when an error occuse in the encryption + */ + private byte[] getDesCbcChecksum(byte key[], + byte[] header, + byte[] data, int offset, int len) + throws GSSException { + + Cipher des = getInitializedDes(true, key, ZERO_IV); + + int blockSize = des.getBlockSize(); + + /* + * Here the data need not be a multiple of the blocksize + * (8). Encrypt and throw away results for all blocks except for + * the very last block. + */ + + byte[] finalBlock = new byte[blockSize]; + + int numBlocks = len / blockSize; + int lastBytes = len % blockSize; + if (lastBytes == 0) { + // No need for padding. Save last block from application data + numBlocks -= 1; + System.arraycopy(data, offset + numBlocks*blockSize, + finalBlock, 0, blockSize); + } else { + System.arraycopy(data, offset + numBlocks*blockSize, + finalBlock, 0, lastBytes); + // Zero padding automatically done + } + + try { + byte[] temp = new byte[Math.max(blockSize, + (header == null? blockSize : header.length))]; + + if (header != null) { + // header will be null when doing DES-MD5 Checksum + des.update(header, 0, header.length, temp, 0); + } + + // Iterate over all but the last block + for (int i = 0; i < numBlocks; i++) { + des.update(data, offset, blockSize, + temp, 0); + offset += blockSize; + } + + // Now process the final block + byte[] retVal = new byte[blockSize]; + des.update(finalBlock, 0, blockSize, retVal, 0); + des.doFinal(); + + return retVal; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + /** + * Obtains an initialized DES cipher. + * + * @param encryptMode true if encryption is desired, false is decryption + * is desired. + * @param key the bytes for the DES key + * @param ivBytes the initial vector bytes + */ + private final Cipher getInitializedDes(boolean encryptMode, byte[] key, + byte[] ivBytes) + throws GSSException { + + + try { + IvParameterSpec iv = new IvParameterSpec(ivBytes); + SecretKey jceKey = (SecretKey) (new SecretKeySpec(key, "DES")); + + Cipher desCipher = Cipher.getInstance("DES/CBC/NoPadding"); + desCipher.init( + (encryptMode ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + jceKey, iv); + return desCipher; + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + /** + * Helper routine to decrypt fromm a byte array and write the + * application data straight to an output array with minimal + * buffer copies. The confounder and the padding are stored + * separately and not copied into this output array. + * @param key the DES key to use + * @param cipherText the encrypted data + * @param offset the offset for the encrypted data + * @param len the length of the encrypted data + * @param dataOutBuf the output buffer where the application data + * should be writte + * @param dataOffset the offser where the application data should + * be written. + * @throws GSSException is an error occurs while decrypting the + * data + */ + private void desCbcDecrypt(WrapToken token, byte[] key, byte[] cipherText, + int offset, int len, byte[] dataOutBuf, int dataOffset) + throws GSSException { + + try { + + int temp = 0; + + Cipher des = getInitializedDes(false, key, ZERO_IV); + + /* + * Remove the counfounder first. + * CONFOUNDER_SIZE is one DES block ie 8 bytes. + */ + temp = des.update(cipherText, offset, WrapToken.CONFOUNDER_SIZE, + token.confounder); + // temp should be CONFOUNDER_SIZE + // debug("\n\ttemp is " + temp + " and CONFOUNDER_SIZE is " + // + CONFOUNDER_SIZE); + + offset += WrapToken.CONFOUNDER_SIZE; + len -= WrapToken.CONFOUNDER_SIZE; + + /* + * len is a multiple of 8 due to padding. + * Decrypt all blocks directly into the output buffer except for + * the very last block. Remove the trailing padding bytes from the + * very last block and copy that into the output buffer. + */ + + int blockSize = des.getBlockSize(); + int numBlocks = len / blockSize - 1; + + // Iterate over all but the last block + for (int i = 0; i < numBlocks; i++) { + temp = des.update(cipherText, offset, blockSize, + dataOutBuf, dataOffset); + // temp should be blockSize + // debug("\n\ttemp is " + temp + " and blockSize is " + // + blockSize); + + offset += blockSize; + dataOffset += blockSize; + } + + // Now process the last block + byte[] finalBlock = new byte[blockSize]; + des.update(cipherText, offset, blockSize, finalBlock); + + des.doFinal(); + + /* + * There is always at least one padding byte. The padding bytes + * are all the value of the number of padding bytes. + */ + + int padSize = finalBlock[blockSize - 1]; + if (padSize < 1 || padSize > 8) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid padding on Wrap Token"); + token.padding = WrapToken.pads[padSize]; + blockSize -= padSize; + + // Copy this last block into the output buffer + System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset, + blockSize); + + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + /** + * Helper routine to decrypt from an InputStream and write the + * application data straight to an output array with minimal + * buffer copies. The confounder and the padding are stored + * separately and not copied into this output array. + * @param key the DES key to use + * @param is the InputStream from which the cipher text should be + * read + * @param len the length of the ciphertext data + * @param dataOutBuf the output buffer where the application data + * should be writte + * @param dataOffset the offser where the application data should + * be written. + * @throws GSSException is an error occurs while decrypting the + * data + */ + private void desCbcDecrypt(WrapToken token, byte[] key, + InputStream is, int len, byte[] dataOutBuf, int dataOffset) + throws GSSException, IOException { + + int temp = 0; + + Cipher des = getInitializedDes(false, key, ZERO_IV); + + WrapTokenInputStream truncatedInputStream = + new WrapTokenInputStream(is, len); + CipherInputStream cis = new CipherInputStream(truncatedInputStream, + des); + /* + * Remove the counfounder first. + * CONFOUNDER_SIZE is one DES block ie 8 bytes. + */ + temp = cis.read(token.confounder); + + len -= temp; + // temp should be CONFOUNDER_SIZE + // debug("Got " + temp + " bytes; CONFOUNDER_SIZE is " + // + CONFOUNDER_SIZE + "\n"); + // debug("Confounder is " + getHexBytes(confounder) + "\n"); + + + /* + * len is a multiple of 8 due to padding. + * Decrypt all blocks directly into the output buffer except for + * the very last block. Remove the trailing padding bytes from the + * very last block and copy that into the output buffer. + */ + + int blockSize = des.getBlockSize(); + int numBlocks = len / blockSize - 1; + + // Iterate over all but the last block + for (int i = 0; i < numBlocks; i++) { + // debug("dataOffset is " + dataOffset + "\n"); + temp = cis.read(dataOutBuf, dataOffset, blockSize); + + // temp should be blockSize + // debug("Got " + temp + " bytes and blockSize is " + // + blockSize + "\n"); + // debug("Bytes are: " + // + getHexBytes(dataOutBuf, dataOffset, temp) + "\n"); + dataOffset += blockSize; + } + + // Now process the last block + byte[] finalBlock = new byte[blockSize]; + // debug("Will call read on finalBlock" + "\n"); + temp = cis.read(finalBlock); + // temp should be blockSize + /* + debug("Got " + temp + " bytes and blockSize is " + + blockSize + "\n"); + debug("Bytes are: " + + getHexBytes(finalBlock, 0, temp) + "\n"); + debug("Will call doFinal" + "\n"); + */ + try { + des.doFinal(); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + + /* + * There is always at least one padding byte. The padding bytes + * are all the value of the number of padding bytes. + */ + + int padSize = finalBlock[blockSize - 1]; + if (padSize < 1 || padSize > 8) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid padding on Wrap Token"); + token.padding = WrapToken.pads[padSize]; + blockSize -= padSize; + + // Copy this last block into the output buffer + System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset, + blockSize); + } + + private static byte[] getDesEncryptionKey(byte[] key) + throws GSSException { + + /* + * To meet export control requirements, double check that the + * key being used is no longer than 64 bits. + * + * Note that from a protocol point of view, an + * algorithm that is not DES will be rejected before this + * point. Also, a DES key that is not 64 bits will be + * rejected by a good JCE provider. + */ + if (key.length > 8) + throw new GSSException(GSSException.FAILURE, -100, + "Invalid DES Key!"); + + byte[] retVal = new byte[key.length]; + for (int i = 0; i < key.length; i++) + retVal[i] = (byte)(key[i] ^ 0xf0); // RFC 1964, Section 1.2.2 + return retVal; + } + + // ---- DES3-KD methods + private void des3KdDecrypt(WrapToken token, byte[] ciphertext, + int cStart, int cLen, byte[] plaintext, int pStart) + throws GSSException { + byte[] ptext; + try { + ptext = Des3.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV, + ciphertext, cStart, cLen); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES3-KD Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + + /* + Krb5Token.debug("\ndes3KdDecrypt in: " + + Krb5Token.getHexBytes(ciphertext, cStart, cLen)); + Krb5Token.debug("\ndes3KdDecrypt plain: " + + Krb5Token.getHexBytes(ptext)); + */ + + // Strip out confounder and padding + /* + * There is always at least one padding byte. The padding bytes + * are all the value of the number of padding bytes. + */ + int padSize = ptext[ptext.length - 1]; + if (padSize < 1 || padSize > 8) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid padding on Wrap Token"); + + token.padding = WrapToken.pads[padSize]; + int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize; + + System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE, + plaintext, pStart, len); + + // Needed to calculate checksum + System.arraycopy(ptext, 0, token.confounder, + 0, WrapToken.CONFOUNDER_SIZE); + } + + private byte[] des3KdEncrypt(byte[] confounder, byte[] plaintext, + int start, int len, byte[] padding) throws GSSException { + + + // [confounder | plaintext | padding] + byte[] all = new byte[confounder.length + len + padding.length]; + System.arraycopy(confounder, 0, all, 0, confounder.length); + System.arraycopy(plaintext, start, all, confounder.length, len); + System.arraycopy(padding, 0, all, confounder.length + len, + padding.length); + + // Krb5Token.debug("\ndes3KdEncrypt:" + Krb5Token.getHexBytes(all)); + + // Encrypt + try { + byte[] answer = Des3.encryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV, + all, 0, all.length); + // Krb5Token.debug("\ndes3KdEncrypt encrypted:" + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use DES3-KD Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + // ---- RC4-HMAC methods + private void arcFourDecrypt(WrapToken token, byte[] ciphertext, + int cStart, int cLen, byte[] plaintext, int pStart) + throws GSSException { + + // obtain Sequence number needed for decryption + // first decrypt the Sequence Number using checksum + byte[] seqNum = decryptSeq(token.getChecksum(), + token.getEncSeqNumber(), 0, 8); + + byte[] ptext; + try { + ptext = ArcFourHmac.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV, + ciphertext, cStart, cLen, seqNum); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use ArcFour Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + + /* + Krb5Token.debug("\narcFourDecrypt in: " + + Krb5Token.getHexBytes(ciphertext, cStart, cLen)); + Krb5Token.debug("\narcFourDecrypt plain: " + + Krb5Token.getHexBytes(ptext)); + */ + + // Strip out confounder and padding + /* + * There is always at least one padding byte. The padding bytes + * are all the value of the number of padding bytes. + */ + int padSize = ptext[ptext.length - 1]; + if (padSize < 1) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid padding on Wrap Token"); + + token.padding = WrapToken.pads[padSize]; + int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize; + + System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE, + plaintext, pStart, len); + + // Krb5Token.debug("\narcFourDecrypt plaintext: " + + // Krb5Token.getHexBytes(plaintext)); + + // Needed to calculate checksum + System.arraycopy(ptext, 0, token.confounder, + 0, WrapToken.CONFOUNDER_SIZE); + } + + private byte[] arcFourEncrypt(WrapToken token, byte[] confounder, + byte[] plaintext, int start, int len, byte[] padding) + throws GSSException { + + // [confounder | plaintext | padding] + byte[] all = new byte[confounder.length + len + padding.length]; + System.arraycopy(confounder, 0, all, 0, confounder.length); + System.arraycopy(plaintext, start, all, confounder.length, len); + System.arraycopy(padding, 0, all, confounder.length + len, + padding.length); + + // get the token Sequence Number required for encryption + // Note: When using this RC4 based encryption type, the sequence number + // is always sent in big-endian rather than little-endian order. + byte[] seqNum = new byte[4]; + WrapToken.writeBigEndian(token.getSequenceNumber(), seqNum); + + // Krb5Token.debug("\narcFourEncrypt:" + Krb5Token.getHexBytes(all)); + + // Encrypt + try { + byte[] answer = ArcFourHmac.encryptRaw(keybytes, KG_USAGE_SEAL, + seqNum, all, 0, all.length); + // Krb5Token.debug("\narcFourEncrypt encrypted:" + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use ArcFour Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + // ---- AES methods + private byte[] aes128Encrypt(byte[] confounder, byte[] tokenHeader, + byte[] plaintext, int start, int len, int key_usage) + throws GSSException { + + // encrypt { AES-plaintext-data | filler | header } + // AES-plaintext-data { confounder | plaintext } + // WrapToken = { tokenHeader | + // Encrypt (confounder | plaintext | tokenHeader ) | HMAC } + + byte[] all = new byte[confounder.length + len + tokenHeader.length]; + System.arraycopy(confounder, 0, all, 0, confounder.length); + System.arraycopy(plaintext, start, all, confounder.length, len); + System.arraycopy(tokenHeader, 0, all, confounder.length+len, + tokenHeader.length); + + // Krb5Token.debug("\naes128Encrypt:" + Krb5Token.getHexBytes(all)); + try { + byte[] answer = Aes128.encryptRaw(keybytes, key_usage, + ZERO_IV_AES, + all, 0, all.length); + // Krb5Token.debug("\naes128Encrypt encrypted:" + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES128 Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + private void aes128Decrypt(WrapToken_v2 token, byte[] ciphertext, + int cStart, int cLen, byte[] plaintext, int pStart, int key_usage) + throws GSSException { + + byte[] ptext = null; + + try { + ptext = Aes128.decryptRaw(keybytes, key_usage, + ZERO_IV_AES, ciphertext, cStart, cLen); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES128 Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + + /* + Krb5Token.debug("\naes128Decrypt in: " + + Krb5Token.getHexBytes(ciphertext, cStart, cLen)); + Krb5Token.debug("\naes128Decrypt plain: " + + Krb5Token.getHexBytes(ptext)); + Krb5Token.debug("\naes128Decrypt ptext: " + + Krb5Token.getHexBytes(ptext)); + */ + + // Strip out confounder and token header + int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE - + WrapToken_v2.TOKEN_HEADER_SIZE; + System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE, + plaintext, pStart, len); + + /* + Krb5Token.debug("\naes128Decrypt plaintext: " + + Krb5Token.getHexBytes(plaintext, pStart, len)); + */ + } + + private byte[] aes256Encrypt(byte[] confounder, byte[] tokenHeader, + byte[] plaintext, int start, int len, int key_usage) + throws GSSException { + + // encrypt { AES-plaintext-data | filler | header } + // AES-plaintext-data { confounder | plaintext } + // WrapToken = { tokenHeader | + // Encrypt (confounder | plaintext | tokenHeader ) | HMAC } + + byte[] all = new byte[confounder.length + len + tokenHeader.length]; + System.arraycopy(confounder, 0, all, 0, confounder.length); + System.arraycopy(plaintext, start, all, confounder.length, len); + System.arraycopy(tokenHeader, 0, all, confounder.length+len, + tokenHeader.length); + + // Krb5Token.debug("\naes256Encrypt:" + Krb5Token.getHexBytes(all)); + + try { + byte[] answer = Aes256.encryptRaw(keybytes, key_usage, + ZERO_IV_AES, all, 0, all.length); + // Krb5Token.debug("\naes256Encrypt encrypted:" + + // Krb5Token.getHexBytes(answer)); + return answer; + } catch (Exception e) { + // GeneralSecurityException, KrbCryptoException + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES256 Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + } + + private void aes256Decrypt(WrapToken_v2 token, byte[] ciphertext, + int cStart, int cLen, byte[] plaintext, int pStart, int key_usage) + throws GSSException { + + byte[] ptext; + try { + ptext = Aes256.decryptRaw(keybytes, key_usage, + ZERO_IV_AES, ciphertext, cStart, cLen); + } catch (GeneralSecurityException e) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "Could not use AES128 Cipher - " + e.getMessage()); + ge.initCause(e); + throw ge; + } + + /* + Krb5Token.debug("\naes256Decrypt in: " + + Krb5Token.getHexBytes(ciphertext, cStart, cLen)); + Krb5Token.debug("\naes256Decrypt plain: " + + Krb5Token.getHexBytes(ptext)); + Krb5Token.debug("\naes256Decrypt ptext: " + + Krb5Token.getHexBytes(ptext)); + */ + + // Strip out confounder and token header + int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE - + WrapToken_v2.TOKEN_HEADER_SIZE; + System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE, + plaintext, pStart, len); + + /* + Krb5Token.debug("\naes128Decrypt plaintext: " + + Krb5Token.getHexBytes(plaintext, pStart, len)); + */ + + } + + /** + * This class provides a truncated inputstream needed by WrapToken. The + * truncated inputstream is passed to CipherInputStream. It prevents + * the CipherInputStream from treating the bytes of the following token + * as part fo the ciphertext for this token. + */ + class WrapTokenInputStream extends InputStream { + + private InputStream is; + private int length; + private int remaining; + + private int temp; + + public WrapTokenInputStream(InputStream is, int length) { + this.is = is; + this.length = length; + remaining = length; + } + + public final int read() throws IOException { + if (remaining == 0) + return -1; + else { + temp = is.read(); + if (temp != -1) + remaining -= temp; + return temp; + } + } + + public final int read(byte[] b) throws IOException { + if (remaining == 0) + return -1; + else { + temp = Math.min(remaining, b.length); + temp = is.read(b, 0, temp); + if (temp != -1) + remaining -= temp; + return temp; + } + } + + public final int read(byte[] b, + int off, + int len) throws IOException { + if (remaining == 0) + return -1; + else { + temp = Math.min(remaining, len); + temp = is.read(b, off, temp); + if (temp != -1) + remaining -= temp; + return temp; + } + } + + public final long skip(long n) throws IOException { + if (remaining == 0) + return 0; + else { + temp = (int) Math.min(remaining, n); + temp = (int) is.skip(temp); + remaining -= temp; + return temp; + } + } + + public final int available() throws IOException { + return Math.min(remaining, is.available()); + } + + public final void close() throws IOException { + remaining = 0; + } + } +} diff --git a/src/sun/security/jgss/krb5/InitSecContextToken.java b/src/sun/security/jgss/krb5/InitSecContextToken.java new file mode 100644 index 00000000..0077e7ed --- /dev/null +++ b/src/sun/security/jgss/krb5/InitSecContextToken.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import com.sun.security.jgss.AuthorizationDataEntry; +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.IOException; +import sun.security.krb5.*; +import java.net.InetAddress; +import sun.security.krb5.internal.AuthorizationData; +import sun.security.krb5.internal.KerberosTime; + +class InitSecContextToken extends InitialToken { + + private KrbApReq apReq = null; + + /** + * For the context initiator to call. It constructs a new + * InitSecContextToken to send over to the peer containing the desired + * flags and the AP-REQ. It also updates the context with the local + * sequence number and shared context key. + * (When mutual auth is enabled the peer has an opportunity to + * renegotiate the session key in the followup AcceptSecContextToken + * that it sends.) + */ + InitSecContextToken(Krb5Context context, + Credentials tgt, + Credentials serviceTicket) + throws KrbException, IOException, GSSException { + + boolean mutualRequired = context.getMutualAuthState(); + boolean useSubkey = true; // MIT Impl will crash if this is not set! + boolean useSequenceNumber = true; + + OverloadedChecksum gssChecksum = + new OverloadedChecksum(context, tgt, serviceTicket); + + Checksum checksum = gssChecksum.getChecksum(); + + context.setTktFlags(serviceTicket.getFlags()); + context.setAuthTime( + new KerberosTime(serviceTicket.getAuthTime()).toString()); + apReq = new KrbApReq(serviceTicket, + mutualRequired, + useSubkey, + useSequenceNumber, + checksum); + + context.resetMySequenceNumber(apReq.getSeqNumber().intValue()); + + EncryptionKey subKey = apReq.getSubKey(); + if (subKey != null) + context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey); + else + context.setKey(Krb5Context.SESSION_KEY, serviceTicket.getSessionKey()); + + if (!mutualRequired) + context.resetPeerSequenceNumber(0); + } + + /** + * For the context acceptor to call. It reads the bytes out of an + * InputStream and constructs an InitSecContextToken with them. + */ + InitSecContextToken(Krb5Context context, Krb5AcceptCredential cred, + InputStream is) + throws IOException, GSSException, KrbException { + + int tokenId = ((is.read()<<8) | is.read()); + + if (tokenId != Krb5Token.AP_REQ_ID) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "AP_REQ token id does not match!"); + + // XXX Modify KrbApReq cons to take an InputStream + byte[] apReqBytes = + new sun.security.util.DerValue(is).toByteArray(); + //debug("=====ApReqBytes: [" + getHexBytes(apReqBytes) + "]\n"); + + InetAddress addr = null; + if (context.getChannelBinding() != null) { + addr = context.getChannelBinding().getInitiatorAddress(); + } + apReq = new KrbApReq(apReqBytes, cred, addr); + //debug("\nReceived AP-REQ and authenticated it.\n"); + + EncryptionKey sessionKey = apReq.getCreds().getSessionKey(); + + /* + System.out.println("\n\nSession key from service ticket is: " + + getHexBytes(sessionKey.getBytes())); + */ + + EncryptionKey subKey = apReq.getSubKey(); + if (subKey != null) { + context.setKey(Krb5Context.INITIATOR_SUBKEY, subKey); + /* + System.out.println("Sub-Session key from authenticator is: " + + getHexBytes(subKey.getBytes()) + "\n"); + */ + } else { + context.setKey(Krb5Context.SESSION_KEY, sessionKey); + //System.out.println("Sub-Session Key Missing in Authenticator.\n"); + } + + OverloadedChecksum gssChecksum = new OverloadedChecksum( + context, apReq.getChecksum(), sessionKey, subKey); + gssChecksum.setContextFlags(context); + Credentials delegCred = gssChecksum.getDelegatedCreds(); + if (delegCred != null) { + Krb5CredElement credElement = + Krb5InitCredential.getInstance( + (Krb5NameElement)context.getSrcName(), + delegCred); + context.setDelegCred(credElement); + } + + Integer apReqSeqNumber = apReq.getSeqNumber(); + int peerSeqNumber = (apReqSeqNumber != null ? + apReqSeqNumber.intValue() : + 0); + context.resetPeerSequenceNumber(peerSeqNumber); + if (!context.getMutualAuthState()) + // Use the same sequence number as the peer + // (Behaviour exhibited by the Windows SSPI server) + context.resetMySequenceNumber(peerSeqNumber); + context.setAuthTime( + new KerberosTime(apReq.getCreds().getAuthTime()).toString()); + context.setTktFlags(apReq.getCreds().getFlags()); + AuthorizationData ad = apReq.getCreds().getAuthzData(); + if (ad == null) { + context.setAuthzData(null); + } else { + AuthorizationDataEntry[] authzData = + new AuthorizationDataEntry[ad.count()]; + for (int i=0; i 0x0000ffff) + throw new GSSException(GSSException.FAILURE, -1, + "Incorrect message length"); + + writeLittleEndian(krbCredMessage.length, temp); + checksumBytes[pos++] = temp[0]; + checksumBytes[pos++] = temp[1]; + System.arraycopy(krbCredMessage, 0, + checksumBytes, pos, krbCredMessage.length); + } + + } + + /** + * Called on the acceptor side when reading an InitSecContextToken. + */ + // XXX Passing in Checksum is not required. byte[] can + // be passed in if this checksum type denotes a + // raw_checksum. In that case, make Checksum class krb5 + // internal. + public OverloadedChecksum(Krb5Context context, Checksum checksum, + EncryptionKey key, EncryptionKey subKey) + throws GSSException, KrbException, IOException { + + int pos = 0; + + if (checksum == null) { + GSSException ge = new GSSException(GSSException.FAILURE, -1, + "No cksum in AP_REQ's authenticator"); + ge.initCause(new KrbException(Krb5.KRB_AP_ERR_INAPP_CKSUM)); + throw ge; + } + checksumBytes = checksum.getBytes(); + + if ((checksumBytes[0] != CHECKSUM_FIRST_BYTES[0]) || + (checksumBytes[1] != CHECKSUM_FIRST_BYTES[1]) || + (checksumBytes[2] != CHECKSUM_FIRST_BYTES[2]) || + (checksumBytes[3] != CHECKSUM_FIRST_BYTES[3])) { + throw new GSSException(GSSException.FAILURE, -1, + "Incorrect checksum"); + } + + ChannelBinding localBindings = context.getChannelBinding(); + + // Ignore remote channel binding info when not requested at + // local side (RFC 4121 4.1.1.2: the acceptor MAY ignore...). + // + // All major krb5 implementors implement this "MAY", + // and some applications depend on it as a workaround + // for not having a way to negotiate the use of channel + // binding -- the initiator application always uses CB + // and hopes the acceptor will ignore the CB if the + // acceptor doesn't support CB. + if (localBindings != null) { + byte[] remoteBindingBytes = new byte[CHECKSUM_BINDINGS_SIZE]; + System.arraycopy(checksumBytes, 4, remoteBindingBytes, 0, + CHECKSUM_BINDINGS_SIZE); + + byte[] noBindings = new byte[CHECKSUM_BINDINGS_SIZE]; + if (!Arrays.equals(noBindings, remoteBindingBytes)) { + byte[] localBindingsBytes = + computeChannelBinding(localBindings); + if (!Arrays.equals(localBindingsBytes, + remoteBindingBytes)) { + throw new GSSException(GSSException.BAD_BINDINGS, -1, + "Bytes mismatch!"); + } + } else { + throw new GSSException(GSSException.BAD_BINDINGS, -1, + "Token missing ChannelBinding!"); + } + } + + flags = readLittleEndian(checksumBytes, 20, 4); + + if ((flags & CHECKSUM_DELEG_FLAG) > 0) { + + /* + * XXX + * if ((checksumBytes[24] != (byte)0x01) && + * (checksumBytes[25] != (byte)0x00)) + */ + + int credLen = readLittleEndian(checksumBytes, 26, 2); + byte[] credBytes = new byte[credLen]; + System.arraycopy(checksumBytes, 28, credBytes, 0, credLen); + + KrbCred cred; + try { + cred = new KrbCred(credBytes, key); + } catch (KrbException ke) { + if (subKey != null) { + cred = new KrbCred(credBytes, subKey); + } else { + throw ke; + } + } + delegCreds = cred.getDelegatedCreds()[0]; + } + } + + // check if KRB-CRED message should use NULL_KEY for encryption + private boolean useNullKey(CipherHelper ch) { + boolean flag = true; + // for "newer" etypes and RC4-HMAC do not use NULL KEY + if ((ch.getProto() == 1) || ch.isArcFour()) { + flag = false; + } + return flag; + } + + public Checksum getChecksum() throws KrbException { + return new Checksum(checksumBytes, CHECKSUM_TYPE); + } + + public Credentials getDelegatedCreds() { + return delegCreds; + } + + // Only called by acceptor + public void setContextFlags(Krb5Context context) { + // default for cred delegation is false + if ((flags & CHECKSUM_DELEG_FLAG) > 0) + context.setCredDelegState(true); + // default for the following are true + if ((flags & CHECKSUM_MUTUAL_FLAG) == 0) { + context.setMutualAuthState(false); + } + if ((flags & CHECKSUM_REPLAY_FLAG) == 0) { + context.setReplayDetState(false); + } + if ((flags & CHECKSUM_SEQUENCE_FLAG) == 0) { + context.setSequenceDetState(false); + } + if ((flags & CHECKSUM_CONF_FLAG) == 0) { + context.setConfState(false); + } + if ((flags & CHECKSUM_INTEG_FLAG) == 0) { + context.setIntegState(false); + } + } + } + + private int getAddrType(InetAddress addr) { + int addressType = CHANNEL_BINDING_AF_NULL_ADDR; + + if (addr instanceof Inet4Address) + addressType = CHANNEL_BINDING_AF_INET; + else if (addr instanceof Inet6Address) + addressType = CHANNEL_BINDING_AF_INET6; + return (addressType); + } + + private byte[] getAddrBytes(InetAddress addr) throws GSSException { + int addressType = getAddrType(addr); + byte[] addressBytes = addr.getAddress(); + if (addressBytes != null) { + switch (addressType) { + case CHANNEL_BINDING_AF_INET: + if (addressBytes.length != Inet4_ADDRSZ) { + throw new GSSException(GSSException.FAILURE, -1, + "Incorrect AF-INET address length in ChannelBinding."); + } + return (addressBytes); + case CHANNEL_BINDING_AF_INET6: + if (addressBytes.length != Inet6_ADDRSZ) { + throw new GSSException(GSSException.FAILURE, -1, + "Incorrect AF-INET6 address length in ChannelBinding."); + } + return (addressBytes); + default: + throw new GSSException(GSSException.FAILURE, -1, + "Cannot handle non AF-INET addresses in ChannelBinding."); + } + } + return null; + } + + private byte[] computeChannelBinding(ChannelBinding channelBinding) + throws GSSException { + + InetAddress initiatorAddress = channelBinding.getInitiatorAddress(); + InetAddress acceptorAddress = channelBinding.getAcceptorAddress(); + int size = 5*4; + + int initiatorAddressType = getAddrType(initiatorAddress); + int acceptorAddressType = getAddrType(acceptorAddress); + + byte[] initiatorAddressBytes = null; + if (initiatorAddress != null) { + initiatorAddressBytes = getAddrBytes(initiatorAddress); + size += initiatorAddressBytes.length; + } + + byte[] acceptorAddressBytes = null; + if (acceptorAddress != null) { + acceptorAddressBytes = getAddrBytes(acceptorAddress); + size += acceptorAddressBytes.length; + } + + byte[] appDataBytes = channelBinding.getApplicationData(); + if (appDataBytes != null) { + size += appDataBytes.length; + } + + byte[] data = new byte[size]; + + int pos = 0; + + writeLittleEndian(initiatorAddressType, data, pos); + pos += 4; + + if (initiatorAddressBytes != null) { + writeLittleEndian(initiatorAddressBytes.length, data, pos); + pos += 4; + System.arraycopy(initiatorAddressBytes, 0, + data, pos, initiatorAddressBytes.length); + pos += initiatorAddressBytes.length; + } else { + // Write length 0 + pos += 4; + } + + writeLittleEndian(acceptorAddressType, data, pos); + pos += 4; + + if (acceptorAddressBytes != null) { + writeLittleEndian(acceptorAddressBytes.length, data, pos); + pos += 4; + System.arraycopy(acceptorAddressBytes, 0, + data, pos, acceptorAddressBytes.length); + pos += acceptorAddressBytes.length; + } else { + // Write length 0 + pos += 4; + } + + if (appDataBytes != null) { + writeLittleEndian(appDataBytes.length, data, pos); + pos += 4; + System.arraycopy(appDataBytes, 0, data, pos, + appDataBytes.length); + pos += appDataBytes.length; + } else { + // Write 0 + pos += 4; + } + + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + return md5.digest(data); + } catch (NoSuchAlgorithmException e) { + throw new GSSException(GSSException.FAILURE, -1, + "Could not get MD5 Message Digest - " + + e.getMessage()); + } + } + + public abstract byte[] encode() throws IOException; + +} diff --git a/src/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/sun/security/jgss/krb5/Krb5AcceptCredential.java new file mode 100644 index 00000000..25819e20 --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5AcceptCredential.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.GSSCaller; +import sun.security.jgss.spi.*; +import sun.security.krb5.*; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.AccessController; +import java.security.AccessControlContext; +import javax.security.auth.DestroyFailedException; + +/** + * Implements the krb5 acceptor credential element. + * + * @author Mayank Upadhyay + * @since 1.4 + */ +public class Krb5AcceptCredential + implements Krb5CredElement { + + private final Krb5NameElement name; + private final ServiceCreds screds; + + private Krb5AcceptCredential(Krb5NameElement name, ServiceCreds creds) { + /* + * Initialize this instance with the data from the acquired + * KerberosKey. This class needs to be a KerberosKey too + * hence we can't just store a reference. + */ + + this.name = name; + this.screds = creds; + } + + static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name) + throws GSSException { + + final String serverPrinc = (name == null? null: + name.getKrb5PrincipalName().getName()); + final AccessControlContext acc = AccessController.getContext(); + + ServiceCreds creds = null; + try { + creds = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public ServiceCreds run() throws Exception { + return Krb5Util.getServiceCreds( + caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller, + serverPrinc, acc); + }}); + } catch (PrivilegedActionException e) { + GSSException ge = + new GSSException(GSSException.NO_CRED, -1, + "Attempt to obtain new ACCEPT credentials failed!"); + ge.initCause(e.getException()); + throw ge; + } + + if (creds == null) + throw new GSSException(GSSException.NO_CRED, -1, + "Failed to find any Kerberos credentails"); + + if (name == null) { + String fullName = creds.getName(); + if (fullName != null) { + name = Krb5NameElement.getInstance(fullName, + Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); + } + } + + return new Krb5AcceptCredential(name, creds); + } + + /** + * Returns the principal name for this credential. The name + * is in mechanism specific format. + * + * @return GSSNameSpi representing principal name of this credential + * @exception GSSException may be thrown + */ + public final GSSNameSpi getName() throws GSSException { + return name; + } + + /** + * Returns the init lifetime remaining. + * + * @return the init lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getInitLifetime() throws GSSException { + return 0; + } + + /** + * Returns the accept lifetime remaining. + * + * @return the accept lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getAcceptLifetime() throws GSSException { + return GSSCredential.INDEFINITE_LIFETIME; + } + + public boolean isInitiatorCredential() throws GSSException { + return false; + } + + public boolean isAcceptorCredential() throws GSSException { + return true; + } + + /** + * Returns the oid representing the underlying credential + * mechanism oid. + * + * @return the Oid for this credential mechanism + * @exception GSSException may be thrown + */ + public final Oid getMechanism() { + return Krb5MechFactory.GSS_KRB5_MECH_OID; + } + + public final java.security.Provider getProvider() { + return Krb5MechFactory.PROVIDER; + } + + public EncryptionKey[] getKrb5EncryptionKeys(PrincipalName princ) { + return screds.getEKeys(princ); + } + + /** + * Called to invalidate this credential element. + */ + public void dispose() throws GSSException { + try { + destroy(); + } catch (DestroyFailedException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, + "Could not destroy credentials - " + e.getMessage()); + gssException.initCause(e); + } + } + + /** + * Destroys the locally cached EncryptionKey value and then calls + * destroy in the base class. + */ + public void destroy() throws DestroyFailedException { + screds.destroy(); + } + + /** + * Impersonation is only available on the initiator side. The + * service must starts as an initiator to get an initial TGT to complete + * the S4U2self protocol. + */ + @Override + public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { + Credentials cred = screds.getInitCred(); + if (cred != null) { + return Krb5InitCredential.getInstance(this.name, cred) + .impersonate(name); + } else { + throw new GSSException(GSSException.FAILURE, -1, + "Only an initiate credentials can impersonate"); + } + } +} diff --git a/src/sun/security/jgss/krb5/Krb5Context.java b/src/sun/security/jgss/krb5/Krb5Context.java new file mode 100644 index 00000000..a4ab572a --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5Context.java @@ -0,0 +1,1462 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import com.sun.security.jgss.InquireType; +import org.ietf.jgss.*; +import sun.misc.HexDumpEncoder; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.GSSCaller; +import sun.security.jgss.spi.*; +import sun.security.jgss.TokenTracker; +import sun.security.krb5.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.security.Provider; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.Key; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import javax.crypto.Cipher; +import javax.security.auth.Subject; +import javax.security.auth.kerberos.*; +import sun.security.krb5.internal.Ticket; + +/** + * Implements the mechanism specific context class for the Kerberos v5 + * GSS-API mechanism. + * + * @author Mayank Upadhyay + * @author Ram Marti + * @since 1.4 + */ +class Krb5Context implements GSSContextSpi { + + /* + * The different states that this context can be in. + */ + + private static final int STATE_NEW = 1; + private static final int STATE_IN_PROCESS = 2; + private static final int STATE_DONE = 3; + private static final int STATE_DELETED = 4; + + private int state = STATE_NEW; + + public static final int SESSION_KEY = 0; + public static final int INITIATOR_SUBKEY = 1; + public static final int ACCEPTOR_SUBKEY = 2; + + /* + * Optional features that the application can set and their default + * values. + */ + + private boolean credDelegState = false; // now only useful at client + private boolean mutualAuthState = true; + private boolean replayDetState = true; + private boolean sequenceDetState = true; + private boolean confState = true; + private boolean integState = true; + private boolean delegPolicyState = false; + + private boolean isConstrainedDelegationTried = false; + + private int mySeqNumber; + private int peerSeqNumber; + private int keySrc; + private TokenTracker peerTokenTracker; + + private CipherHelper cipherHelper = null; + + /* + * Separate locks for the sequence numbers allow the application to + * receive tokens at the same time that it is sending tokens. Note + * that the application must synchronize the generation and + * transmission of tokens such that tokens are processed in the same + * order that they are generated. This is important when sequence + * checking of per-message tokens is enabled. + */ + + private Object mySeqNumberLock = new Object(); + private Object peerSeqNumberLock = new Object(); + + private EncryptionKey key; + private Krb5NameElement myName; + private Krb5NameElement peerName; + private int lifetime; + private boolean initiator; + private ChannelBinding channelBinding; + + private Krb5CredElement myCred; + private Krb5CredElement delegatedCred; // Set only on acceptor side + + // XXX See if the required info from these can be extracted and + // stored elsewhere + private Credentials serviceCreds; + private KrbApReq apReq; + Ticket serviceTicket; + final private GSSCaller caller; + private static final boolean DEBUG = Krb5Util.DEBUG; + + /** + * Constructor for Krb5Context to be called on the context initiator's + * side. + */ + Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred, + int lifetime) + throws GSSException { + + if (peerName == null) + throw new IllegalArgumentException("Cannot have null peer name"); + + this.caller = caller; + this.peerName = peerName; + this.myCred = myCred; + this.lifetime = lifetime; + this.initiator = true; + } + + /** + * Constructor for Krb5Context to be called on the context acceptor's + * side. + */ + Krb5Context(GSSCaller caller, Krb5CredElement myCred) + throws GSSException { + this.caller = caller; + this.myCred = myCred; + this.initiator = false; + } + + /** + * Constructor for Krb5Context to import a previously exported context. + */ + public Krb5Context(GSSCaller caller, byte [] interProcessToken) + throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, + -1, "GSS Import Context not available"); + } + + /** + * Method to determine if the context can be exported and then + * re-imported. + */ + public final boolean isTransferable() throws GSSException { + return false; + } + + /** + * The lifetime remaining for this context. + */ + public final int getLifetime() { + // XXX Return service ticket lifetime + return GSSContext.INDEFINITE_LIFETIME; + } + + /* + * Methods that may be invoked by the GSS framework in response + * to an application request for setting/getting these + * properties. + * + * These can only be called on the initiator side. + * + * Notice that an application can only request these + * properties. The mechanism may or may not support them. The + * application must make getXXX calls after context establishment + * to see if the mechanism implementations on both sides support + * these features. requestAnonymity is an exception where the + * application will want to call getAnonymityState prior to sending any + * GSS token during context establishment. + * + * Also note that the requests can only be placed before context + * establishment starts. i.e. when state is STATE_NEW + */ + + /** + * Requests the desired lifetime. Can only be used on the context + * initiator's side. + */ + public void requestLifetime(int lifetime) throws GSSException { + if (state == STATE_NEW && isInitiator()) + this.lifetime = lifetime; + } + + /** + * Requests that confidentiality be available. + */ + public final void requestConf(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + confState = value; + } + + /** + * Is confidentiality available? + */ + public final boolean getConfState() { + return confState; + } + + /** + * Requests that integrity be available. + */ + public final void requestInteg(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + integState = value; + } + + /** + * Is integrity available? + */ + public final boolean getIntegState() { + return integState; + } + + /** + * Requests that credential delegation be done during context + * establishment. + */ + public final void requestCredDeleg(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) { + if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) { + credDelegState = value; + } + } + } + + /** + * Is credential delegation enabled? + */ + public final boolean getCredDelegState() { + if (isInitiator()) { + return credDelegState; + } else { + // Server side deleg state is not flagged by credDelegState. + // It can use constrained delegation. + tryConstrainedDelegation(); + return delegatedCred != null; + } + } + + /** + * Requests that mutual authentication be done during context + * establishment. Since this is fromm the client's perspective, it + * essentially requests that the server be authenticated. + */ + public final void requestMutualAuth(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) { + mutualAuthState = value; + } + } + + /** + * Is mutual authentication enabled? Since this is from the client's + * perspective, it essentially meas that the server is being + * authenticated. + */ + public final boolean getMutualAuthState() { + return mutualAuthState; + } + + /** + * Requests that replay detection be done on the GSS wrap and MIC + * tokens. + */ + public final void requestReplayDet(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + replayDetState = value; + } + + /** + * Is replay detection enabled on the GSS wrap and MIC tokens? + * We enable replay detection if sequence checking is enabled. + */ + public final boolean getReplayDetState() { + return replayDetState || sequenceDetState; + } + + /** + * Requests that sequence checking be done on the GSS wrap and MIC + * tokens. + */ + public final void requestSequenceDet(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + sequenceDetState = value; + } + + /** + * Is sequence checking enabled on the GSS Wrap and MIC tokens? + * We enable sequence checking if replay detection is enabled. + */ + public final boolean getSequenceDetState() { + return sequenceDetState || replayDetState; + } + + /** + * Requests that the deleg policy be respected. + */ + public final void requestDelegPolicy(boolean value) { + if (state == STATE_NEW && isInitiator()) + delegPolicyState = value; + } + + /** + * Is deleg policy respected? + */ + public final boolean getDelegPolicyState() { + return delegPolicyState; + } + + /* + * Anonymity is a little different in that after an application + * requests anonymity it will want to know whether the mechanism + * can support it or not, prior to sending any tokens across for + * context establishment. Since this is from the initiator's + * perspective, it essentially requests that the initiator be + * anonymous. + */ + + public final void requestAnonymity(boolean value) throws GSSException { + // Ignore silently. Application will check back with + // getAnonymityState. + } + + // RFC 2853 actually calls for this to be called after context + // establishment to get the right answer, but that is + // incorrect. The application may not want to send over any + // tokens if anonymity is not available. + public final boolean getAnonymityState() { + return false; + } + + /* + * Package private methods invoked by other Krb5 plugin classes. + */ + + /** + * Get the context specific DESCipher instance, invoked in + * MessageToken.init() + */ + final CipherHelper getCipherHelper(EncryptionKey ckey) throws GSSException { + EncryptionKey cipherKey = null; + if (cipherHelper == null) { + cipherKey = (getKey() == null) ? ckey: getKey(); + cipherHelper = new CipherHelper(cipherKey); + } + return cipherHelper; + } + + final int incrementMySequenceNumber() { + int retVal; + synchronized (mySeqNumberLock) { + retVal = mySeqNumber; + mySeqNumber = retVal + 1; + } + return retVal; + } + + final void resetMySequenceNumber(int seqNumber) { + if (DEBUG) { + System.out.println("Krb5Context setting mySeqNumber to: " + + seqNumber); + } + synchronized (mySeqNumberLock) { + mySeqNumber = seqNumber; + } + } + + final void resetPeerSequenceNumber(int seqNumber) { + if (DEBUG) { + System.out.println("Krb5Context setting peerSeqNumber to: " + + seqNumber); + } + synchronized (peerSeqNumberLock) { + peerSeqNumber = seqNumber; + peerTokenTracker = new TokenTracker(peerSeqNumber); + } + } + + final void setKey(int keySrc, EncryptionKey key) throws GSSException { + this.key = key; + this.keySrc = keySrc; + // %%% to do: should clear old cipherHelper first + cipherHelper = new CipherHelper(key); // Need to use new key + } + + public final int getKeySrc() { + return keySrc; + } + + private final EncryptionKey getKey() { + return key; + } + + /** + * Called on the acceptor side to store the delegated credentials + * received in the AcceptSecContextToken. + */ + final void setDelegCred(Krb5CredElement delegatedCred) { + this.delegatedCred = delegatedCred; + } + + /* + * While the application can only request the following features, + * other classes in the package can call the actual set methods + * for them. They are called as context establishment tokens are + * received on an acceptor side and the context feature list that + * the initiator wants becomes known. + */ + + /* + * This method is also called by InitialToken.OverloadedChecksum if the + * TGT is not forwardable and the user requested delegation. + */ + final void setCredDelegState(boolean state) { + credDelegState = state; + } + + final void setMutualAuthState(boolean state) { + mutualAuthState = state; + } + + final void setReplayDetState(boolean state) { + replayDetState = state; + } + + final void setSequenceDetState(boolean state) { + sequenceDetState = state; + } + + final void setConfState(boolean state) { + confState = state; + } + + final void setIntegState(boolean state) { + integState = state; + } + + final void setDelegPolicyState(boolean state) { + delegPolicyState = state; + } + + /** + * Sets the channel bindings to be used during context + * establishment. + */ + public final void setChannelBinding(ChannelBinding channelBinding) + throws GSSException { + this.channelBinding = channelBinding; + } + + final ChannelBinding getChannelBinding() { + return channelBinding; + } + + /** + * Returns the mechanism oid. + * + * @return the Oid of this context + */ + public final Oid getMech() { + return (Krb5MechFactory.GSS_KRB5_MECH_OID); + } + + /** + * Returns the context initiator name. + * + * @return initiator name + * @exception GSSException + */ + public final GSSNameSpi getSrcName() throws GSSException { + return (isInitiator()? myName : peerName); + } + + /** + * Returns the context acceptor. + * + * @return context acceptor(target) name + * @exception GSSException + */ + public final GSSNameSpi getTargName() throws GSSException { + return (!isInitiator()? myName : peerName); + } + + /** + * Returns the delegated credential for the context. This + * is an optional feature of contexts which not all + * mechanisms will support. A context can be requested to + * support credential delegation by using the CRED_DELEG, + * or it can request for a constrained delegation. + * This is only valid on the acceptor side of the context. + * @return GSSCredentialSpi object for the delegated credential + * @exception GSSException + * @see GSSContext#getDelegCredState + */ + public final GSSCredentialSpi getDelegCred() throws GSSException { + if (state != STATE_IN_PROCESS && state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT); + if (isInitiator()) { + throw new GSSException(GSSException.NO_CRED); + } + tryConstrainedDelegation(); + if (delegatedCred == null) { + throw new GSSException(GSSException.NO_CRED); + } + return delegatedCred; + } + + private void tryConstrainedDelegation() { + if (state != STATE_IN_PROCESS && state != STATE_DONE) { + return; + } + // We will only try constrained delegation once (if necessary). + if (!isConstrainedDelegationTried) { + if (delegatedCred == null) { + if (DEBUG) { + System.out.println(">>> Constrained deleg from " + caller); + } + // The constrained delegation part. The acceptor needs to have + // isInitiator=true in order to get a TGT, either earlier at + // logon stage, if useSubjectCredsOnly, or now. + try { + delegatedCred = new Krb5ProxyCredential( + Krb5InitCredential.getInstance( + GSSCaller.CALLER_ACCEPT, myName, lifetime), + peerName, serviceTicket); + } catch (GSSException gsse) { + // OK, delegatedCred is null then + } + } + isConstrainedDelegationTried = true; + } + } + /** + * Tests if this is the initiator side of the context. + * + * @return boolean indicating if this is initiator (true) + * or target (false) + */ + public final boolean isInitiator() { + return initiator; + } + + /** + * Tests if the context can be used for per-message service. + * Context may allow the calls to the per-message service + * functions before being fully established. + * + * @return boolean indicating if per-message methods can + * be called. + */ + public final boolean isProtReady() { + return (state == STATE_DONE); + } + + /** + * Initiator context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + * + * @param is contains the token received from the peer. On the + * first call it will be ignored. + * @return any token required to be sent to the peer + * It is responsibility of the caller + * to send the token to its peer for processing. + * @exception GSSException + */ + public final byte[] initSecContext(InputStream is, int mechTokenSize) + throws GSSException { + + byte[] retVal = null; + InitialToken token = null; + int errorCode = GSSException.FAILURE; + if (DEBUG) { + System.out.println("Entered Krb5Context.initSecContext with " + + "state=" + printState(state)); + } + if (!isInitiator()) { + throw new GSSException(GSSException.FAILURE, -1, + "initSecContext on an acceptor " + + "GSSContext"); + } + + try { + if (state == STATE_NEW) { + state = STATE_IN_PROCESS; + + errorCode = GSSException.NO_CRED; + + if (myCred == null) { + myCred = Krb5InitCredential.getInstance(caller, myName, + GSSCredential.DEFAULT_LIFETIME); + } else if (!myCred.isInitiatorCredential()) { + throw new GSSException(errorCode, -1, + "No TGT available"); + } + myName = (Krb5NameElement) myCred.getName(); + Credentials tgt; + final Krb5ProxyCredential second; + if (myCred instanceof Krb5InitCredential) { + second = null; + tgt = ((Krb5InitCredential) myCred).getKrb5Credentials(); + } else { + second = (Krb5ProxyCredential) myCred; + tgt = second.self.getKrb5Credentials(); + } + + checkPermission(peerName.getKrb5PrincipalName().getName(), + "initiate"); + /* + * If useSubjectCredsonly is true then + * we check whether we already have the ticket + * for this service in the Subject and reuse it + */ + + final AccessControlContext acc = + AccessController.getContext(); + + if (GSSUtil.useSubjectCredsOnly(caller)) { + KerberosTicket kerbTicket = null; + try { + // get service ticket from caller's subject + kerbTicket = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public KerberosTicket run() throws Exception { + // XXX to be cleaned + // highly consider just calling: + // Subject.getSubject + // SubjectComber.find + // instead of Krb5Util.getTicket + return Krb5Util.getTicket( + GSSCaller.CALLER_UNKNOWN, + // since it's useSubjectCredsOnly here, + // don't worry about the null + second == null ? + myName.getKrb5PrincipalName().getName(): + second.getName().getKrb5PrincipalName().getName(), + peerName.getKrb5PrincipalName().getName(), + acc); + }}); + } catch (PrivilegedActionException e) { + if (DEBUG) { + System.out.println("Attempt to obtain service" + + " ticket from the subject failed!"); + } + } + if (kerbTicket != null) { + if (DEBUG) { + System.out.println("Found service ticket in " + + "the subject" + + kerbTicket); + } + + // convert Ticket to serviceCreds + // XXX Should merge these two object types + // avoid converting back and forth + serviceCreds = Krb5Util.ticketToCreds(kerbTicket); + } + } + if (serviceCreds == null) { + // either we did not find the serviceCreds in the + // Subject or useSubjectCreds is false + if (DEBUG) { + System.out.println("Service ticket not found in " + + "the subject"); + } + // Get Service ticket using the Kerberos protocols + if (second == null) { + serviceCreds = Credentials.acquireServiceCreds( + peerName.getKrb5PrincipalName().getName(), + tgt); + } else { + serviceCreds = Credentials.acquireS4U2proxyCreds( + peerName.getKrb5PrincipalName().getName(), + second.tkt, + second.getName().getKrb5PrincipalName(), + tgt); + } + if (GSSUtil.useSubjectCredsOnly(caller)) { + final Subject subject = + AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Subject run() { + return (Subject.getSubject(acc)); + } + }); + if (subject != null && + !subject.isReadOnly()) { + /* + * Store the service credentials as + * javax.security.auth.kerberos.KerberosTicket in + * the Subject. We could wait till the context is + * succesfully established; however it is easier + * to do here and there is no harm indoing it here. + */ + final KerberosTicket kt = + Krb5Util.credsToTicket(serviceCreds); + AccessController.doPrivileged ( + new java.security.PrivilegedAction() { + public Void run() { + subject.getPrivateCredentials().add(kt); + return null; + } + }); + } else { + // log it for debugging purpose + if (DEBUG) { + System.out.println("Subject is " + + "readOnly;Kerberos Service "+ + "ticket not stored"); + } + } + } + } + + errorCode = GSSException.FAILURE; + token = new InitSecContextToken(this, tgt, serviceCreds); + apReq = ((InitSecContextToken)token).getKrbApReq(); + retVal = token.encode(); + myCred = null; + if (!getMutualAuthState()) { + state = STATE_DONE; + } + if (DEBUG) { + System.out.println("Created InitSecContextToken:\n"+ + new HexDumpEncoder().encodeBuffer(retVal)); + } + } else if (state == STATE_IN_PROCESS) { + // No need to write anything; + // just validate the incoming token + new AcceptSecContextToken(this, serviceCreds, apReq, is); + serviceCreds = null; + apReq = null; + state = STATE_DONE; + } else { + // XXX Use logging API? + if (DEBUG) { + System.out.println(state); + } + } + } catch (KrbException e) { + if (DEBUG) { + e.printStackTrace(); + } + GSSException gssException = + new GSSException(errorCode, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } catch (IOException e) { + GSSException gssException = + new GSSException(errorCode, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + return retVal; + } + + public final boolean isEstablished() { + return (state == STATE_DONE); + } + + /** + * Acceptor's context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + * + * @param is contains the token received from the peer. + * @return any token required to be sent to the peer + * It is responsibility of the caller + * to send the token to its peer for processing. + * @exception GSSException + */ + public final byte[] acceptSecContext(InputStream is, int mechTokenSize) + throws GSSException { + + byte[] retVal = null; + + if (DEBUG) { + System.out.println("Entered Krb5Context.acceptSecContext with " + + "state=" + printState(state)); + } + + if (isInitiator()) { + throw new GSSException(GSSException.FAILURE, -1, + "acceptSecContext on an initiator " + + "GSSContext"); + } + try { + if (state == STATE_NEW) { + state = STATE_IN_PROCESS; + if (myCred == null) { + myCred = Krb5AcceptCredential.getInstance(caller, myName); + } else if (!myCred.isAcceptorCredential()) { + throw new GSSException(GSSException.NO_CRED, -1, + "No Secret Key available"); + } + myName = (Krb5NameElement) myCred.getName(); + + // If there is already a bound name, check now + if (myName != null) { + Krb5MechFactory.checkAcceptCredPermission(myName, myName); + } + + InitSecContextToken token = new InitSecContextToken(this, + (Krb5AcceptCredential) myCred, is); + PrincipalName clientName = token.getKrbApReq().getClient(); + peerName = Krb5NameElement.getInstance(clientName); + + // If unbound, check after the bound name is found + if (myName == null) { + myName = Krb5NameElement.getInstance( + token.getKrbApReq().getCreds().getServer()); + Krb5MechFactory.checkAcceptCredPermission(myName, myName); + } + + if (getMutualAuthState()) { + retVal = new AcceptSecContextToken(this, + token.getKrbApReq()).encode(); + } + serviceTicket = token.getKrbApReq().getCreds().getTicket(); + myCred = null; + state = STATE_DONE; + } else { + // XXX Use logging API? + if (DEBUG) { + System.out.println(state); + } + } + } catch (KrbException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } catch (IOException e) { + if (DEBUG) { + e.printStackTrace(); + } + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + + return retVal; + } + + /** + * Queries the context for largest data size to accommodate + * the specified protection and be <= maxTokSize. + * + * @param qop the quality of protection that the context will be + * asked to provide. + * @param confReq a flag indicating whether confidentiality will be + * requested or not + * @param outputSize the maximum size of the output token + * @return the maximum size for the input message that can be + * provided to the wrap() method in order to guarantee that these + * requirements are met. + * @throws GSSException + */ + public final int getWrapSizeLimit(int qop, boolean confReq, + int maxTokSize) throws GSSException { + + int retVal = 0; + if (cipherHelper.getProto() == 0) { + retVal = WrapToken.getSizeLimit(qop, confReq, maxTokSize, + getCipherHelper(null)); + } else if (cipherHelper.getProto() == 1) { + retVal = WrapToken_v2.getSizeLimit(qop, confReq, maxTokSize, + getCipherHelper(null)); + } + return retVal; + } + + /* + * Per-message calls depend on the sequence number. The sequence number + * synchronization is at a finer granularity because wrap and getMIC + * care about the local sequence number (mySeqNumber) where are unwrap + * and verifyMIC care about the remote sequence number (peerSeqNumber). + */ + + public final byte[] wrap(byte inBuf[], int offset, int len, + MessageProp msgProp) throws GSSException { + if (DEBUG) { + System.out.println("Krb5Context.wrap: data=[" + + getHexBytes(inBuf, offset, len) + + "]"); + } + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + + byte[] encToken = null; + try { + if (cipherHelper.getProto() == 0) { + WrapToken token = + new WrapToken(this, msgProp, inBuf, offset, len); + encToken = token.encode(); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = + new WrapToken_v2(this, msgProp, inBuf, offset, len); + encToken = token.encode(); + } + if (DEBUG) { + System.out.println("Krb5Context.wrap: token=[" + + getHexBytes(encToken, 0, encToken.length) + + "]"); + } + return encToken; + } catch (IOException e) { + encToken = null; + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + public final int wrap(byte inBuf[], int inOffset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException { + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + + int retVal = 0; + try { + if (cipherHelper.getProto() == 0) { + WrapToken token = + new WrapToken(this, msgProp, inBuf, inOffset, len); + retVal = token.encode(outBuf, outOffset); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = + new WrapToken_v2(this, msgProp, inBuf, inOffset, len); + retVal = token.encode(outBuf, outOffset); + } + if (DEBUG) { + System.out.println("Krb5Context.wrap: token=[" + + getHexBytes(outBuf, outOffset, retVal) + + "]"); + } + return retVal; + } catch (IOException e) { + retVal = 0; + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + public final void wrap(byte inBuf[], int offset, int len, + OutputStream os, MessageProp msgProp) + throws GSSException { + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + + byte[] encToken = null; + try { + if (cipherHelper.getProto() == 0) { + WrapToken token = + new WrapToken(this, msgProp, inBuf, offset, len); + token.encode(os); + if (DEBUG) { + encToken = token.encode(); + } + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = + new WrapToken_v2(this, msgProp, inBuf, offset, len); + token.encode(os); + if (DEBUG) { + encToken = token.encode(); + } + } + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + + if (DEBUG) { + System.out.println("Krb5Context.wrap: token=[" + + getHexBytes(encToken, 0, encToken.length) + + "]"); + } + } + + public final void wrap(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + + byte[] data; + try { + data = new byte[is.available()]; + is.read(data); + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + wrap(data, 0, data.length, os, msgProp); + } + + public final byte[] unwrap(byte inBuf[], int offset, int len, + MessageProp msgProp) + throws GSSException { + + if (DEBUG) { + System.out.println("Krb5Context.unwrap: token=[" + + getHexBytes(inBuf, offset, len) + + "]"); + } + + if (state != STATE_DONE) { + throw new GSSException(GSSException.NO_CONTEXT, -1, + " Unwrap called in invalid state!"); + } + + byte[] data = null; + if (cipherHelper.getProto() == 0) { + WrapToken token = + new WrapToken(this, inBuf, offset, len, msgProp); + data = token.getData(); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = + new WrapToken_v2(this, inBuf, offset, len, msgProp); + data = token.getData(); + setSequencingAndReplayProps(token, msgProp); + } + + if (DEBUG) { + System.out.println("Krb5Context.unwrap: data=[" + + getHexBytes(data, 0, data.length) + + "]"); + } + + return data; + } + + public final int unwrap(byte inBuf[], int inOffset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException { + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Unwrap called in invalid state!"); + + if (cipherHelper.getProto() == 0) { + WrapToken token = + new WrapToken(this, inBuf, inOffset, len, msgProp); + len = token.getData(outBuf, outOffset); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = + new WrapToken_v2(this, inBuf, inOffset, len, msgProp); + len = token.getData(outBuf, outOffset); + setSequencingAndReplayProps(token, msgProp); + } + return len; + } + + public final int unwrap(InputStream is, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException { + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Unwrap called in invalid state!"); + + int len = 0; + if (cipherHelper.getProto() == 0) { + WrapToken token = new WrapToken(this, is, msgProp); + len = token.getData(outBuf, outOffset); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); + len = token.getData(outBuf, outOffset); + setSequencingAndReplayProps(token, msgProp); + } + return len; + } + + + public final void unwrap(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + + if (state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Unwrap called in invalid state!"); + + byte[] data = null; + if (cipherHelper.getProto() == 0) { + WrapToken token = new WrapToken(this, is, msgProp); + data = token.getData(); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); + data = token.getData(); + setSequencingAndReplayProps(token, msgProp); + } + + try { + os.write(data); + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + public final byte[] getMIC(byte []inMsg, int offset, int len, + MessageProp msgProp) + throws GSSException { + + byte[] micToken = null; + try { + if (cipherHelper.getProto() == 0) { + MicToken token = + new MicToken(this, msgProp, inMsg, offset, len); + micToken = token.encode(); + } else if (cipherHelper.getProto() == 1) { + MicToken_v2 token = + new MicToken_v2(this, msgProp, inMsg, offset, len); + micToken = token.encode(); + } + return micToken; + } catch (IOException e) { + micToken = null; + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + private int getMIC(byte []inMsg, int offset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) + throws GSSException { + + int retVal = 0; + try { + if (cipherHelper.getProto() == 0) { + MicToken token = + new MicToken(this, msgProp, inMsg, offset, len); + retVal = token.encode(outBuf, outOffset); + } else if (cipherHelper.getProto() == 1) { + MicToken_v2 token = + new MicToken_v2(this, msgProp, inMsg, offset, len); + retVal = token.encode(outBuf, outOffset); + } + return retVal; + } catch (IOException e) { + retVal = 0; + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + /* + * Checksum calculation requires a byte[]. Hence might as well pass + * a byte[] into the MicToken constructor. However, writing the + * token can be optimized for cases where the application passed in + * an OutputStream. + */ + + private void getMIC(byte[] inMsg, int offset, int len, + OutputStream os, MessageProp msgProp) + throws GSSException { + + try { + if (cipherHelper.getProto() == 0) { + MicToken token = + new MicToken(this, msgProp, inMsg, offset, len); + token.encode(os); + } else if (cipherHelper.getProto() == 1) { + MicToken_v2 token = + new MicToken_v2(this, msgProp, inMsg, offset, len); + token.encode(os); + } + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + } + + public final void getMIC(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + byte[] data; + try { + data = new byte[is.available()]; + is.read(data); + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + getMIC(data, 0, data.length, os, msgProp); + } + + public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, + byte[] inMsg, int msgOffset, int msgLen, + MessageProp msgProp) + throws GSSException { + + if (cipherHelper.getProto() == 0) { + MicToken token = + new MicToken(this, inTok, tokOffset, tokLen, msgProp); + token.verify(inMsg, msgOffset, msgLen); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + MicToken_v2 token = + new MicToken_v2(this, inTok, tokOffset, tokLen, msgProp); + token.verify(inMsg, msgOffset, msgLen); + setSequencingAndReplayProps(token, msgProp); + } + } + + private void verifyMIC(InputStream is, + byte[] inMsg, int msgOffset, int msgLen, + MessageProp msgProp) + throws GSSException { + + if (cipherHelper.getProto() == 0) { + MicToken token = new MicToken(this, is, msgProp); + token.verify(inMsg, msgOffset, msgLen); + setSequencingAndReplayProps(token, msgProp); + } else if (cipherHelper.getProto() == 1) { + MicToken_v2 token = new MicToken_v2(this, is, msgProp); + token.verify(inMsg, msgOffset, msgLen); + setSequencingAndReplayProps(token, msgProp); + } + } + + public final void verifyMIC(InputStream is, InputStream msgStr, + MessageProp mProp) throws GSSException { + byte[] msg; + try { + msg = new byte[msgStr.available()]; + msgStr.read(msg); + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + verifyMIC(is, msg, 0, msg.length, mProp); + } + + /** + * Produces a token representing this context. After this call + * the context will no longer be usable until an import is + * performed on the returned token. + * + * @param os the output token will be written to this stream + * @exception GSSException + */ + public final byte [] export() throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "GSS Export Context not available"); + } + + /** + * Releases context resources and terminates the + * context between 2 peer. + * + * @exception GSSException with major codes NO_CONTEXT, FAILURE. + */ + + public final void dispose() throws GSSException { + state = STATE_DELETED; + delegatedCred = null; + } + + public final Provider getProvider() { + return Krb5MechFactory.PROVIDER; + } + + /** + * Sets replay and sequencing information for a message token received + * form the peer. + */ + private void setSequencingAndReplayProps(MessageToken token, + MessageProp prop) { + if (replayDetState || sequenceDetState) { + int seqNum = token.getSequenceNumber(); + peerTokenTracker.getProps(seqNum, prop); + } + } + + /** + * Sets replay and sequencing information for a message token received + * form the peer. + */ + private void setSequencingAndReplayProps(MessageToken_v2 token, + MessageProp prop) { + if (replayDetState || sequenceDetState) { + int seqNum = token.getSequenceNumber(); + peerTokenTracker.getProps(seqNum, prop); + } + } + + private void checkPermission(String principal, String action) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + ServicePermission perm = + new ServicePermission(principal, action); + sm.checkPermission(perm); + } + } + + private static String getHexBytes(byte[] bytes, int pos, int len) { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < len; i++) { + + int b1 = (bytes[i]>>4) & 0x0f; + int b2 = bytes[i] & 0x0f; + + sb.append(Integer.toHexString(b1)); + sb.append(Integer.toHexString(b2)); + sb.append(' '); + } + return sb.toString(); + } + + private static String printState(int state) { + switch (state) { + case STATE_NEW: + return ("STATE_NEW"); + case STATE_IN_PROCESS: + return ("STATE_IN_PROCESS"); + case STATE_DONE: + return ("STATE_DONE"); + case STATE_DELETED: + return ("STATE_DELETED"); + default: + return ("Unknown state " + state); + } + } + + GSSCaller getCaller() { + // Currently used by InitialToken only + return caller; + } + + /** + * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY) + */ + static class KerberosSessionKey implements Key { + private static final long serialVersionUID = 699307378954123869L; + + private final EncryptionKey key; + + KerberosSessionKey(EncryptionKey key) { + this.key = key; + } + + @Override + public String getAlgorithm() { + return Integer.toString(key.getEType()); + } + + @Override + public String getFormat() { + return "RAW"; + } + + @Override + public byte[] getEncoded() { + return key.getBytes().clone(); + } + + @Override + public String toString() { + return "Kerberos session key: etype: " + key.getEType() + "\n" + + new HexDumpEncoder().encodeBuffer(key.getBytes()); + } + } + + /** + * Return the mechanism-specific attribute associated with {@code type}. + */ + public Object inquireSecContext(InquireType type) + throws GSSException { + if (!isEstablished()) { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Security context not established."); + } + switch (type) { + case KRB5_GET_SESSION_KEY: + return new KerberosSessionKey(key); + case KRB5_GET_TKT_FLAGS: + return tktFlags.clone(); + case KRB5_GET_AUTHZ_DATA: + if (isInitiator()) { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "AuthzData not available on initiator side."); + } else { + return (authzData==null)?null:authzData.clone(); + } + case KRB5_GET_AUTHTIME: + return authTime; + } + throw new GSSException(GSSException.UNAVAILABLE, -1, + "Inquire type not supported."); + } + + // Helpers for inquireSecContext + private boolean[] tktFlags; + private String authTime; + private com.sun.security.jgss.AuthorizationDataEntry[] authzData; + + public void setTktFlags(boolean[] tktFlags) { + this.tktFlags = tktFlags; + } + + public void setAuthTime(String authTime) { + this.authTime = authTime; + } + + public void setAuthzData(com.sun.security.jgss.AuthorizationDataEntry[] authzData) { + this.authzData = authzData; + } +} diff --git a/src/sun/security/jgss/krb5/Krb5CredElement.java b/src/sun/security/jgss/krb5/Krb5CredElement.java new file mode 100644 index 00000000..b57f8180 --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5CredElement.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import sun.security.jgss.spi.*; + +/** + * Provides type safety for Krb5 credential elements. + * + * @author Mayank Upadhyay + * @since 1.4 + */ +interface Krb5CredElement + extends GSSCredentialSpi { +} diff --git a/src/sun/security/jgss/krb5/Krb5InitCredential.java b/src/sun/security/jgss/krb5/Krb5InitCredential.java new file mode 100644 index 00000000..69b7b44c --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5InitCredential.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.GSSCaller; +import sun.security.jgss.spi.*; +import sun.security.krb5.*; + +import javax.security.auth.kerberos.*; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Date; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; + +/** + * Implements the krb5 initiator credential element. + * + * @author Mayank Upadhyay + * @author Ram Marti + * @since 1.4 + */ + +public class Krb5InitCredential + extends KerberosTicket + implements Krb5CredElement { + + private static final long serialVersionUID = 7723415700837898232L; + + private Krb5NameElement name; + private Credentials krb5Credentials; + + private Krb5InitCredential(Krb5NameElement name, + byte[] asn1Encoding, + KerberosPrincipal client, + KerberosPrincipal server, + byte[] sessionKey, + int keyType, + boolean[] flags, + Date authTime, + Date startTime, + Date endTime, + Date renewTill, + InetAddress[] clientAddresses) + throws GSSException { + super(asn1Encoding, + client, + server, + sessionKey, + keyType, + flags, + authTime, + startTime, + endTime, + renewTill, + clientAddresses); + + this.name = name; + + try { + // Cache this for later use by the sun.security.krb5 package. + krb5Credentials = new Credentials(asn1Encoding, + client.getName(), + server.getName(), + sessionKey, + keyType, + flags, + authTime, + startTime, + endTime, + renewTill, + clientAddresses); + } catch (KrbException e) { + throw new GSSException(GSSException.NO_CRED, -1, + e.getMessage()); + } catch (IOException e) { + throw new GSSException(GSSException.NO_CRED, -1, + e.getMessage()); + } + + } + + private Krb5InitCredential(Krb5NameElement name, + Credentials delegatedCred, + byte[] asn1Encoding, + KerberosPrincipal client, + KerberosPrincipal server, + byte[] sessionKey, + int keyType, + boolean[] flags, + Date authTime, + Date startTime, + Date endTime, + Date renewTill, + InetAddress[] clientAddresses) + throws GSSException { + super(asn1Encoding, + client, + server, + sessionKey, + keyType, + flags, + authTime, + startTime, + endTime, + renewTill, + clientAddresses); + + this.name = name; + // A delegated cred does not have all fields set. So do not try to + // creat new Credentials out of the delegatedCred. + this.krb5Credentials = delegatedCred; + } + + static Krb5InitCredential getInstance(GSSCaller caller, Krb5NameElement name, + int initLifetime) + throws GSSException { + + KerberosTicket tgt = getTgt(caller, name, initLifetime); + if (tgt == null) + throw new GSSException(GSSException.NO_CRED, -1, + "Failed to find any Kerberos tgt"); + + if (name == null) { + String fullName = tgt.getClient().getName(); + name = Krb5NameElement.getInstance(fullName, + Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); + } + + return new Krb5InitCredential(name, + tgt.getEncoded(), + tgt.getClient(), + tgt.getServer(), + tgt.getSessionKey().getEncoded(), + tgt.getSessionKeyType(), + tgt.getFlags(), + tgt.getAuthTime(), + tgt.getStartTime(), + tgt.getEndTime(), + tgt.getRenewTill(), + tgt.getClientAddresses()); + } + + static Krb5InitCredential getInstance(Krb5NameElement name, + Credentials delegatedCred) + throws GSSException { + + EncryptionKey sessionKey = delegatedCred.getSessionKey(); + + /* + * all of the following data is optional in a KRB-CRED + * messages. This check for each field. + */ + + PrincipalName cPrinc = delegatedCred.getClient(); + PrincipalName sPrinc = delegatedCred.getServer(); + + KerberosPrincipal client = null; + KerberosPrincipal server = null; + + Krb5NameElement credName = null; + + if (cPrinc != null) { + String fullName = cPrinc.getName(); + credName = Krb5NameElement.getInstance(fullName, + Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); + client = new KerberosPrincipal(fullName); + } + + // XXX Compare name to credName + + if (sPrinc != null) { + server = + new KerberosPrincipal(sPrinc.getName(), + KerberosPrincipal.KRB_NT_SRV_INST); + } + + return new Krb5InitCredential(credName, + delegatedCred, + delegatedCred.getEncoded(), + client, + server, + sessionKey.getBytes(), + sessionKey.getEType(), + delegatedCred.getFlags(), + delegatedCred.getAuthTime(), + delegatedCred.getStartTime(), + delegatedCred.getEndTime(), + delegatedCred.getRenewTill(), + delegatedCred.getClientAddresses()); + } + + /** + * Returns the principal name for this credential. The name + * is in mechanism specific format. + * + * @return GSSNameSpi representing principal name of this credential + * @exception GSSException may be thrown + */ + public final GSSNameSpi getName() throws GSSException { + return name; + } + + /** + * Returns the init lifetime remaining. + * + * @return the init lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getInitLifetime() throws GSSException { + int retVal = 0; + retVal = (int)(getEndTime().getTime() + - (new Date().getTime())); + + return retVal/1000; + } + + /** + * Returns the accept lifetime remaining. + * + * @return the accept lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getAcceptLifetime() throws GSSException { + return 0; + } + + public boolean isInitiatorCredential() throws GSSException { + return true; + } + + public boolean isAcceptorCredential() throws GSSException { + return false; + } + + /** + * Returns the oid representing the underlying credential + * mechanism oid. + * + * @return the Oid for this credential mechanism + * @exception GSSException may be thrown + */ + public final Oid getMechanism() { + return Krb5MechFactory.GSS_KRB5_MECH_OID; + } + + public final java.security.Provider getProvider() { + return Krb5MechFactory.PROVIDER; + } + + + /** + * Returns a sun.security.krb5.Credentials instance so that it maybe + * used in that package for th Kerberos protocol. + */ + Credentials getKrb5Credentials() { + return krb5Credentials; + } + + /* + * XXX Call to this.refresh() should refresh the locally cached copy + * of krb5Credentials also. + */ + + /** + * Called to invalidate this credential element. + */ + public void dispose() throws GSSException { + try { + destroy(); + } catch (javax.security.auth.DestroyFailedException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, + "Could not destroy credentials - " + e.getMessage()); + gssException.initCause(e); + } + } + + // XXX call to this.destroy() should destroy the locally cached copy + // of krb5Credentials and then call super.destroy(). + + private static KerberosTicket getTgt(GSSCaller caller, Krb5NameElement name, + int initLifetime) + throws GSSException { + + final String clientPrincipal; + + /* + * Find the TGT for the realm that the client is in. If the client + * name is not available, then use the default realm. + */ + if (name != null) { + clientPrincipal = (name.getKrb5PrincipalName()).getName(); + } else { + clientPrincipal = null; + } + + final AccessControlContext acc = AccessController.getContext(); + + try { + final GSSCaller realCaller = (caller == GSSCaller.CALLER_UNKNOWN) + ? GSSCaller.CALLER_INITIATE + : caller; + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public KerberosTicket run() throws Exception { + // It's OK to use null as serverPrincipal. TGT is almost + // the first ticket for a principal and we use list. + return Krb5Util.getTicket( + realCaller, + clientPrincipal, null, acc); + }}); + } catch (PrivilegedActionException e) { + GSSException ge = + new GSSException(GSSException.NO_CRED, -1, + "Attempt to obtain new INITIATE credentials failed!" + + " (" + e.getMessage() + ")"); + ge.initCause(e.getException()); + throw ge; + } + } + + @Override + public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { + try { + Krb5NameElement kname = (Krb5NameElement)name; + Credentials newCred = Credentials.acquireS4U2selfCreds( + kname.getKrb5PrincipalName(), krb5Credentials); + return new Krb5ProxyCredential(this, kname, newCred.getTicket()); + } catch (IOException | KrbException ke) { + GSSException ge = + new GSSException(GSSException.FAILURE, -1, + "Attempt to obtain S4U2self credentials failed!"); + ge.initCause(ke); + throw ge; + } + } +} diff --git a/src/sun/security/jgss/krb5/Krb5MechFactory.java b/src/sun/security/jgss/krb5/Krb5MechFactory.java new file mode 100644 index 00000000..ffae2cdc --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5MechFactory.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.GSSCaller; +import sun.security.jgss.spi.*; +import javax.security.auth.kerberos.ServicePermission; +import java.security.Provider; +import java.util.Vector; + +/** + * Krb5 Mechanism plug in for JGSS + * This is the properties object required by the JGSS framework. + * All mechanism specific information is defined here. + * + * @author Mayank Upadhyay + */ + +public final class Krb5MechFactory implements MechanismFactory { + + private static final boolean DEBUG = Krb5Util.DEBUG; + + static final Provider PROVIDER = + new sun.security.jgss.SunProvider(); + + static final Oid GSS_KRB5_MECH_OID = + createOid("1.2.840.113554.1.2.2"); + + static final Oid NT_GSS_KRB5_PRINCIPAL = + createOid("1.2.840.113554.1.2.2.1"); + + private static Oid[] nameTypes = + new Oid[] { GSSName.NT_USER_NAME, + GSSName.NT_HOSTBASED_SERVICE, + GSSName.NT_EXPORT_NAME, + NT_GSS_KRB5_PRINCIPAL}; + + final private GSSCaller caller; + + private static Krb5CredElement getCredFromSubject(GSSNameSpi name, + boolean initiate) + throws GSSException { + Vector creds = + GSSUtil.searchSubject(name, GSS_KRB5_MECH_OID, initiate, + (initiate ? + Krb5InitCredential.class : + Krb5AcceptCredential.class)); + + Krb5CredElement result = ((creds == null || creds.isEmpty()) ? + null : creds.firstElement()); + + // Force permission check before returning the cred to caller + if (result != null) { + if (initiate) { + checkInitCredPermission((Krb5NameElement) result.getName()); + } else { + checkAcceptCredPermission + ((Krb5NameElement) result.getName(), name); + } + } + return result; + } + + public Krb5MechFactory(GSSCaller caller) { + this.caller = caller; + } + + public GSSNameSpi getNameElement(String nameStr, Oid nameType) + throws GSSException { + return Krb5NameElement.getInstance(nameStr, nameType); + } + + public GSSNameSpi getNameElement(byte[] name, Oid nameType) + throws GSSException { + // At this point, even an exported name is stripped down to safe + // bytes only + // XXX Use encoding here + return Krb5NameElement.getInstance(new String(name), nameType); + } + + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + int initLifetime, int acceptLifetime, + int usage) throws GSSException { + + if (name != null && !(name instanceof Krb5NameElement)) { + name = Krb5NameElement.getInstance(name.toString(), + name.getStringNameType()); + } + + Krb5CredElement credElement = getCredFromSubject + (name, (usage != GSSCredential.ACCEPT_ONLY)); + + if (credElement == null) { + if (usage == GSSCredential.INITIATE_ONLY || + usage == GSSCredential.INITIATE_AND_ACCEPT) { + credElement = Krb5InitCredential.getInstance + (caller, (Krb5NameElement) name, initLifetime); + checkInitCredPermission + ((Krb5NameElement) credElement.getName()); + } else if (usage == GSSCredential.ACCEPT_ONLY) { + credElement = + Krb5AcceptCredential.getInstance(caller, + (Krb5NameElement) name); + checkAcceptCredPermission + ((Krb5NameElement) credElement.getName(), name); + } else + throw new GSSException(GSSException.FAILURE, -1, + "Unknown usage mode requested"); + } + return credElement; + } + + public static void checkInitCredPermission(Krb5NameElement name) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String realm = (name.getKrb5PrincipalName()).getRealmAsString(); + String tgsPrincipal = + new String("krbtgt/" + realm + '@' + realm); + ServicePermission perm = + new ServicePermission(tgsPrincipal, "initiate"); + try { + sm.checkPermission(perm); + } catch (SecurityException e) { + if (DEBUG) { + System.out.println("Permission to initiate" + + "kerberos init credential" + e.getMessage()); + } + throw e; + } + } + } + + public static void checkAcceptCredPermission(Krb5NameElement name, + GSSNameSpi originalName) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null && name != null) { + ServicePermission perm = new ServicePermission + (name.getKrb5PrincipalName().getName(), "accept"); + try { + sm.checkPermission(perm); + } catch (SecurityException e) { + if (originalName == null) { + // Don't disclose the name of the principal + e = new SecurityException("No permission to acquire " + + "Kerberos accept credential"); + // Don't call e.initCause() with caught exception + } + throw e; + } + } + } + + public GSSContextSpi getMechanismContext(GSSNameSpi peer, + GSSCredentialSpi myInitiatorCred, int lifetime) + throws GSSException { + if (peer != null && !(peer instanceof Krb5NameElement)) { + peer = Krb5NameElement.getInstance(peer.toString(), + peer.getStringNameType()); + } + // XXX Convert myInitiatorCred to Krb5CredElement + if (myInitiatorCred == null) { + myInitiatorCred = getCredentialElement(null, lifetime, 0, + GSSCredential.INITIATE_ONLY); + } + return new Krb5Context(caller, (Krb5NameElement)peer, + (Krb5CredElement)myInitiatorCred, lifetime); + } + + public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred) + throws GSSException { + // XXX Convert myAcceptorCred to Krb5CredElement + if (myAcceptorCred == null) { + myAcceptorCred = getCredentialElement(null, 0, + GSSCredential.INDEFINITE_LIFETIME, GSSCredential.ACCEPT_ONLY); + } + return new Krb5Context(caller, (Krb5CredElement)myAcceptorCred); + } + + public GSSContextSpi getMechanismContext(byte[] exportedContext) + throws GSSException { + return new Krb5Context(caller, exportedContext); + } + + + public final Oid getMechanismOid() { + return GSS_KRB5_MECH_OID; + } + + public Provider getProvider() { + return PROVIDER; + } + + public Oid[] getNameTypes() { + // nameTypes is cloned in GSSManager.getNamesForMech + return nameTypes; + } + + private static Oid createOid(String oidStr) { + Oid retVal = null; + try { + retVal = new Oid(oidStr); + } catch (GSSException e) { + // Should not happen! + } + return retVal; + } +} diff --git a/src/sun/security/jgss/krb5/Krb5NameElement.java b/src/sun/security/jgss/krb5/Krb5NameElement.java new file mode 100644 index 00000000..e06ca3ed --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5NameElement.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.spi.*; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.KrbException; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.Provider; +import java.util.Locale; + +/** + * Implements the GSSNameSpi for the krb5 mechanism. + * + * @author Mayank Upadhyay + */ +public class Krb5NameElement + implements GSSNameSpi { + + private PrincipalName krb5PrincipalName; + + private String gssNameStr = null; + private Oid gssNameType = null; + + // XXX Move this concept into PrincipalName's asn1Encode() sometime + private static String CHAR_ENCODING = "UTF-8"; + + private Krb5NameElement(PrincipalName principalName, + String gssNameStr, + Oid gssNameType) { + this.krb5PrincipalName = principalName; + this.gssNameStr = gssNameStr; + this.gssNameType = gssNameType; + } + + /** + * Instantiates a new Krb5NameElement object. Internally it stores the + * information provided by the input parameters so that they may later + * be used for output when a printable representaion of this name is + * needed in GSS-API format rather than in Kerberos format. + * + */ + static Krb5NameElement getInstance(String gssNameStr, Oid gssNameType) + throws GSSException { + + /* + * A null gssNameType implies that the mechanism default + * Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL be used. + */ + if (gssNameType == null) + gssNameType = Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL; + else + if (!gssNameType.equals(GSSName.NT_USER_NAME) && + !gssNameType.equals(GSSName.NT_HOSTBASED_SERVICE) && + !gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL) && + !gssNameType.equals(GSSName.NT_EXPORT_NAME)) + throw new GSSException(GSSException.BAD_NAMETYPE, -1, + gssNameType.toString() + +" is an unsupported nametype"); + + PrincipalName principalName; + try { + + if (gssNameType.equals(GSSName.NT_EXPORT_NAME) || + gssNameType.equals(Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL)) { + principalName = new PrincipalName(gssNameStr, + PrincipalName.KRB_NT_PRINCIPAL); + } else { + + String[] components = getComponents(gssNameStr); + + /* + * We have forms of GSS name strings that can come in: + * + * 1. names of the form "foo" with just one + * component. (This might include a "@" but only in escaped + * form like "\@") + * 2. names of the form "foo@bar" with two components + * + * The nametypes that are accepted are NT_USER_NAME, and + * NT_HOSTBASED_SERVICE. + */ + + if (gssNameType.equals(GSSName.NT_USER_NAME)) + principalName = new PrincipalName(gssNameStr, + PrincipalName.KRB_NT_PRINCIPAL); + else { + String hostName = null; + String service = components[0]; + if (components.length >= 2) + hostName = components[1]; + + String principal = getHostBasedInstance(service, hostName); + principalName = new PrincipalName(principal, + PrincipalName.KRB_NT_SRV_HST); + } + } + + } catch (KrbException e) { + throw new GSSException(GSSException.BAD_NAME, -1, e.getMessage()); + } + + return new Krb5NameElement(principalName, gssNameStr, gssNameType); + } + + static Krb5NameElement getInstance(PrincipalName principalName) { + return new Krb5NameElement(principalName, + principalName.getName(), + Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); + } + + private static String[] getComponents(String gssNameStr) + throws GSSException { + + String[] retVal; + + // XXX Perhaps provide this parsing code in PrincipalName + + // Look for @ as in service@host + // Assumes host name will not have an escaped '@' + int separatorPos = gssNameStr.lastIndexOf('@', gssNameStr.length()); + + // Not really a separator if it is escaped. Then this is just part + // of the principal name or service name + if ((separatorPos > 0) && + (gssNameStr.charAt(separatorPos-1) == '\\')) { + // Is the `\` character escaped itself? + if ((separatorPos - 2 < 0) || + (gssNameStr.charAt(separatorPos-2) != '\\')) + separatorPos = -1; + } + + if (separatorPos > 0) { + String serviceName = gssNameStr.substring(0, separatorPos); + String hostName = gssNameStr.substring(separatorPos+1); + retVal = new String[] { serviceName, hostName}; + } else { + retVal = new String[] {gssNameStr}; + } + + return retVal; + + } + + private static String getHostBasedInstance(String serviceName, + String hostName) + throws GSSException { + StringBuffer temp = new StringBuffer(serviceName); + + try { + // A lack of "@" defaults to the service being on the local + // host as per RFC 2743 + // XXX Move this part into JGSS framework + if (hostName == null) + hostName = InetAddress.getLocalHost().getHostName(); + + } catch (UnknownHostException e) { + // use hostname as it is + } + hostName = hostName.toLowerCase(Locale.ENGLISH); + + temp = temp.append('/').append(hostName); + return temp.toString(); + } + + public final PrincipalName getKrb5PrincipalName() { + return krb5PrincipalName; + } + + /** + * Equal method for the GSSNameSpi objects. + * If either name denotes an anonymous principal, the call should + * return false. + * + * @param name to be compared with + * @returns true if they both refer to the same entity, else false + * @exception GSSException with major codes of BAD_NAMETYPE, + * BAD_NAME, FAILURE + */ + public boolean equals(GSSNameSpi other) throws GSSException { + + if (other == this) + return true; + + if (other instanceof Krb5NameElement) { + Krb5NameElement that = (Krb5NameElement) other; + return (this.krb5PrincipalName.getName().equals( + that.krb5PrincipalName.getName())); + } + return false; + } + + /** + * Compares this GSSNameSpi object to another Object + * that might be a GSSNameSpi. The behaviour is exactly + * the same as in {@link #equals(GSSNameSpi) equals} except that + * no GSSException is thrown; instead, false will be returned in the + * situation where an error occurs. + * + * @param another the object to be compared to + * @returns true if they both refer to the same entity, else false + * @see #equals(GSSNameSpi) + */ + public boolean equals(Object another) { + if (this == another) { + return true; + } + + try { + if (another instanceof Krb5NameElement) + return equals((Krb5NameElement) another); + } catch (GSSException e) { + // ignore exception + } + return false; + } + + /** + * Returns a hashcode value for this GSSNameSpi. + * + * @return a hashCode value + */ + public int hashCode() { + return 37 * 17 + krb5PrincipalName.getName().hashCode(); + } + + + /** + * Returns the principal name in the form user@REALM or + * host/service@REALM but with the following constraints that are + * imposed by RFC 1964: + *

    +     *  (1) all occurrences of the characters `@`,  `/`, and `\` within
    +     *   principal components or realm names shall be quoted with an
    +     *   immediately-preceding `\`.
    +     *
    +     *   (2) all occurrences of the null, backspace, tab, or newline
    +     *   characters within principal components or realm names will be
    +     *   represented, respectively, with `\0`, `\b`, `\t`, or `\n`.
    +     *
    +     *   (3) the `\` quoting character shall not be emitted within an
    +     *   exported name except to accommodate cases (1) and (2).
    +     * 
    + */ + public byte[] export() throws GSSException { + // XXX Apply the above constraints. + byte[] retVal = null; + try { + retVal = krb5PrincipalName.getName().getBytes(CHAR_ENCODING); + } catch (UnsupportedEncodingException e) { + // Can't happen + } + return retVal; + } + + /** + * Get the mechanism type that this NameElement corresponds to. + * + * @return the Oid of the mechanism type + */ + public Oid getMechanism() { + return (Krb5MechFactory.GSS_KRB5_MECH_OID); + } + + /** + * Returns a string representation for this name. The printed + * name type can be obtained by calling getStringNameType(). + * + * @return string form of this name + * @see #getStringNameType() + * @overrides Object#toString + */ + public String toString() { + return (gssNameStr); + // For testing: return (super.toString()); + } + + /** + * Returns the name type oid. + */ + public Oid getGSSNameType() { + return (gssNameType); + } + + /** + * Returns the oid describing the format of the printable name. + * + * @return the Oid for the format of the printed name + */ + public Oid getStringNameType() { + // XXX For NT_EXPORT_NAME return a different name type. Infact, + // don't even store NT_EXPORT_NAME in the cons. + return (gssNameType); + } + + /** + * Indicates if this name object represents an Anonymous name. + */ + public boolean isAnonymousName() { + return (gssNameType.equals(GSSName.NT_ANONYMOUS)); + } + + public Provider getProvider() { + return Krb5MechFactory.PROVIDER; + } + +} diff --git a/src/sun/security/jgss/krb5/Krb5ProxyCredential.java b/src/sun/security/jgss/krb5/Krb5ProxyCredential.java new file mode 100644 index 00000000..789904df --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5ProxyCredential.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.spi.*; +import sun.security.krb5.internal.Ticket; + +/** + * Implements the krb5 proxy credential element used in constrained + * delegation. It is used in both impersonation (where there is no Kerberos 5 + * communication between the middle server and the client) and normal + * constrained delegation (where there is, but client has not called + * requestCredDeleg(true)). + * @since 1.8 + */ + +public class Krb5ProxyCredential + implements Krb5CredElement { + + public final Krb5InitCredential self; // the middle server + private final Krb5NameElement client; // the client + + // The ticket with cname=client and sname=self. This can be a normal + // service ticket or an S4U2self ticket. + public final Ticket tkt; + + Krb5ProxyCredential(Krb5InitCredential self, Krb5NameElement client, + Ticket tkt) { + this.self = self; + this.tkt = tkt; + this.client = client; + } + + // The client name behind the proxy + @Override + public final Krb5NameElement getName() throws GSSException { + return client; + } + + @Override + public int getInitLifetime() throws GSSException { + // endTime of tkt is not used by KDC, and it's also not + // available in the case of kerberos constr deleg + return self.getInitLifetime(); + } + + @Override + public int getAcceptLifetime() throws GSSException { + return 0; + } + + @Override + public boolean isInitiatorCredential() throws GSSException { + return true; + } + + @Override + public boolean isAcceptorCredential() throws GSSException { + return false; + } + + @Override + public final Oid getMechanism() { + return Krb5MechFactory.GSS_KRB5_MECH_OID; + } + + @Override + public final java.security.Provider getProvider() { + return Krb5MechFactory.PROVIDER; + } + + @Override + public void dispose() throws GSSException { + try { + self.destroy(); + } catch (javax.security.auth.DestroyFailedException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, + "Could not destroy credentials - " + e.getMessage()); + gssException.initCause(e); + } + } + + @Override + public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { + // Cannot impersonate multiple levels without the impersonatee's TGT. + throw new GSSException(GSSException.FAILURE, -1, + "Only an initiate credentials can impersonate"); + } +} diff --git a/src/sun/security/jgss/krb5/Krb5Token.java b/src/sun/security/jgss/krb5/Krb5Token.java new file mode 100644 index 00000000..ef812dab --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5Token.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import java.io.IOException; +import sun.security.util.*; +import sun.security.jgss.*; + +/** + * This class represents a base class for all Kerberos v5 GSS-API + * tokens. It contains commonly used definitions and utilities. + * + * @author Mayank Upadhyay + */ + +abstract class Krb5Token extends GSSToken { + + /** + * The token id defined for the token emitted by the initSecContext call + * carrying the AP_REQ . + */ + public static final int AP_REQ_ID = 0x0100; + + /** + * The token id defined for the token emitted by the acceptSecContext call + * carrying the AP_REP . + */ + public static final int AP_REP_ID = 0x0200; + + /** + * The token id defined for any token carrying a KRB-ERR message. + */ + public static final int ERR_ID = 0x0300; + + /** + * The token id defined for the token emitted by the getMIC call. + */ + public static final int MIC_ID = 0x0101; + + /** + * The token id defined for the token emitted by the wrap call. + */ + public static final int WRAP_ID = 0x0201; + + // new token ID draft-ietf-krb-wg-gssapi-cfx-07.txt + public static final int MIC_ID_v2 = 0x0404; + public static final int WRAP_ID_v2 = 0x0504; + + /** + * The object identifier corresponding to the Kerberos v5 GSS-API + * mechanism. + */ + public static ObjectIdentifier OID; + + static { + try { + OID = new ObjectIdentifier(Krb5MechFactory. + GSS_KRB5_MECH_OID.toString()); + } catch (IOException ioe) { + // should not happen + } + } + + /** + * Returns a strign representing the token type. + * + * @param tokenId the token id for which a string name is desired + * @return the String name of this token type + */ + public static String getTokenName(int tokenId) { + String retVal = null; + switch (tokenId) { + case AP_REQ_ID: + case AP_REP_ID: + retVal = "Context Establishment Token"; + break; + case MIC_ID: + retVal = "MIC Token"; + break; + case MIC_ID_v2: + retVal = "MIC Token (new format)"; + break; + case WRAP_ID: + retVal = "Wrap Token"; + break; + case WRAP_ID_v2: + retVal = "Wrap Token (new format)"; + break; + default: + retVal = "Kerberos GSS-API Mechanism Token"; + break; + } + return retVal; + } +} diff --git a/src/sun/security/jgss/krb5/Krb5Util.java b/src/sun/security/jgss/krb5/Krb5Util.java new file mode 100644 index 00000000..0ad0d641 --- /dev/null +++ b/src/sun/security/jgss/krb5/Krb5Util.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KeyTab; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; +import java.security.AccessControlContext; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.GSSCaller; + +import sun.security.krb5.Credentials; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.KrbException; +import java.io.IOException; + +import sun.security.krb5.KerberosSecrets; +import sun.security.krb5.PrincipalName; + +/** + * Utilities for obtaining and converting Kerberos tickets. + * + */ +public class Krb5Util { + + static final boolean DEBUG = + java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction + ("sun.security.krb5.debug")).booleanValue(); + + /** + * Default constructor + */ + private Krb5Util() { // Cannot create one of these + } + + /** + * Retrieve the service ticket for serverPrincipal from caller's Subject + * or from Subject obtained by logging in, or if not found, via the + * Ticket Granting Service using the TGT obtained from the Subject. + * + * Caller must have permission to: + * - access and update Subject's private credentials + * - create LoginContext + * - read the auth.login.defaultCallbackHandler security property + * + * NOTE: This method is used by JSSE Kerberos Cipher Suites + */ + public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller, + String clientPrincipal, String serverPrincipal, String tgsPrincipal, + AccessControlContext acc) + throws LoginException, KrbException, IOException { + + // 1. Try to find service ticket in acc subject + Subject accSubj = Subject.getSubject(acc); + KerberosTicket ticket = SubjectComber.find(accSubj, + serverPrincipal, clientPrincipal, KerberosTicket.class); + + if (ticket != null) { + return ticket; // found it + } + + Subject loginSubj = null; + if (!GSSUtil.useSubjectCredsOnly(caller)) { + // 2. Try to get ticket from login + try { + loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + ticket = SubjectComber.find(loginSubj, + serverPrincipal, clientPrincipal, KerberosTicket.class); + if (ticket != null) { + return ticket; // found it + } + } catch (LoginException e) { + // No login entry to use + // ignore and continue + } + } + + // Service ticket not found in subject or login + // Try to get TGT to acquire service ticket + + // 3. Try to get TGT from acc subject + KerberosTicket tgt = SubjectComber.find(accSubj, + tgsPrincipal, clientPrincipal, KerberosTicket.class); + + boolean fromAcc; + if (tgt == null && loginSubj != null) { + // 4. Try to get TGT from login subject + tgt = SubjectComber.find(loginSubj, + tgsPrincipal, clientPrincipal, KerberosTicket.class); + fromAcc = false; + } else { + fromAcc = true; + } + + // 5. Try to get service ticket using TGT + if (tgt != null) { + Credentials tgtCreds = ticketToCreds(tgt); + Credentials serviceCreds = Credentials.acquireServiceCreds( + serverPrincipal, tgtCreds); + if (serviceCreds != null) { + ticket = credsToTicket(serviceCreds); + + // Store service ticket in acc's Subject + if (fromAcc && accSubj != null && !accSubj.isReadOnly()) { + accSubj.getPrivateCredentials().add(ticket); + } + } + } + return ticket; + } + + /** + * Retrieves the ticket corresponding to the client/server principal + * pair from the Subject in the specified AccessControlContext. + * If the ticket can not be found in the Subject, and if + * useSubjectCredsOnly is false, then obtain ticket from + * a LoginContext. + */ + static KerberosTicket getTicket(GSSCaller caller, + String clientPrincipal, String serverPrincipal, + AccessControlContext acc) throws LoginException { + + // Try to get ticket from acc's Subject + Subject accSubj = Subject.getSubject(acc); + KerberosTicket ticket = + SubjectComber.find(accSubj, serverPrincipal, clientPrincipal, + KerberosTicket.class); + + // Try to get ticket from Subject obtained from GSSUtil + if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) { + Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + ticket = SubjectComber.find(subject, + serverPrincipal, clientPrincipal, KerberosTicket.class); + } + return ticket; + } + + /** + * Retrieves the caller's Subject, or Subject obtained by logging in + * via the specified caller. + * + * Caller must have permission to: + * - access the Subject + * - create LoginContext + * - read the auth.login.defaultCallbackHandler security property + * + * NOTE: This method is used by JSSE Kerberos Cipher Suites + */ + public static Subject getSubject(GSSCaller caller, + AccessControlContext acc) throws LoginException { + + // Try to get the Subject from acc + Subject subject = Subject.getSubject(acc); + + // Try to get Subject obtained from GSSUtil + if (subject == null && !GSSUtil.useSubjectCredsOnly(caller)) { + subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + } + return subject; + } + + /** + * Retrieves the ServiceCreds for the specified server principal from + * the Subject in the specified AccessControlContext. If not found, and if + * useSubjectCredsOnly is false, then obtain from a LoginContext. + * + * NOTE: This method is also used by JSSE Kerberos Cipher Suites + */ + public static ServiceCreds getServiceCreds(GSSCaller caller, + String serverPrincipal, AccessControlContext acc) + throws LoginException { + + Subject accSubj = Subject.getSubject(acc); + ServiceCreds sc = null; + if (accSubj != null) { + sc = ServiceCreds.getInstance(accSubj, serverPrincipal); + } + if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) { + Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + sc = ServiceCreds.getInstance(subject, serverPrincipal); + } + return sc; + } + + public static KerberosTicket credsToTicket(Credentials serviceCreds) { + EncryptionKey sessionKey = serviceCreds.getSessionKey(); + return new KerberosTicket( + serviceCreds.getEncoded(), + new KerberosPrincipal(serviceCreds.getClient().getName()), + new KerberosPrincipal(serviceCreds.getServer().getName(), + KerberosPrincipal.KRB_NT_SRV_INST), + sessionKey.getBytes(), + sessionKey.getEType(), + serviceCreds.getFlags(), + serviceCreds.getAuthTime(), + serviceCreds.getStartTime(), + serviceCreds.getEndTime(), + serviceCreds.getRenewTill(), + serviceCreds.getClientAddresses()); + }; + + public static Credentials ticketToCreds(KerberosTicket kerbTicket) + throws KrbException, IOException { + return new Credentials( + kerbTicket.getEncoded(), + kerbTicket.getClient().getName(), + kerbTicket.getServer().getName(), + kerbTicket.getSessionKey().getEncoded(), + kerbTicket.getSessionKeyType(), + kerbTicket.getFlags(), + kerbTicket.getAuthTime(), + kerbTicket.getStartTime(), + kerbTicket.getEndTime(), + kerbTicket.getRenewTill(), + kerbTicket.getClientAddresses()); + } + + /** + * A helper method to get a sun..KeyTab from a javax..KeyTab + * @param ktab the javax..KeyTab object + * @return the sun..KeyTab object + */ + public static sun.security.krb5.internal.ktab.KeyTab + snapshotFromJavaxKeyTab(KeyTab ktab) { + return KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .keyTabTakeSnapshot(ktab); + } + + /** + * A helper method to get EncryptionKeys from a javax..KeyTab + * @param ktab the javax..KeyTab object + * @param cname the PrincipalName + * @return the EKeys, never null, might be empty + */ + public static EncryptionKey[] keysFromJavaxKeyTab( + KeyTab ktab, PrincipalName cname) { + return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname); + } +} diff --git a/src/sun/security/jgss/krb5/MessageToken.java b/src/sun/security/jgss/krb5/MessageToken.java new file mode 100644 index 00000000..5aeb6c0d --- /dev/null +++ b/src/sun/security/jgss/krb5/MessageToken.java @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.security.MessageDigest; + +/** + * This class is a base class for other token definitions that pertain to + * per-message GSS-API calls. Conceptually GSS-API has two types of + * per-message tokens: WrapToken and MicToken. They differ in the respect + * that a WrapToken carries additional plaintext or ciphertext application + * data besides just the sequence number and checksum. This class + * encapsulates the commonality in the structure of the WrapToken and the + * MicToken. This structure can be represented as: + *

    + *

    + *     0..1           TOK_ID          Identification field.
    + *                                    01 01 - Mic token
    + *                                    02 01 - Wrap token
    + *     2..3           SGN_ALG         Checksum algorithm indicator.
    + *                                    00 00 - DES MAC MD5
    + *                                    01 00 - MD2.5
    + *                                    02 00 - DES MAC
    + *                                    04 00 - HMAC SHA1 DES3-KD
    + *                                    11 00 - RC4-HMAC
    + *     4..5           SEAL_ALG        ff ff - none
    + *                                    00 00 - DES
    + *                                    02 00 - DES3-KD
    + *                                    10 00 - RC4-HMAC
    + *     6..7           Filler          Contains ff ff
    + *     8..15          SND_SEQ         Encrypted sequence number field.
    + *     16..s+15       SGN_CKSUM       Checksum of plaintext padded data,
    + *                                   calculated according to algorithm
    + *                                  specified in SGN_ALG field.
    + *     s+16..last     Data            encrypted or plaintext padded data
    + * 
    + * Where "s" indicates the size of the checksum. + *

    + * As always, this is preceeded by a GSSHeader. + * + * @author Mayank Upadhyay + * @author Ram Marti + * @see GSSHeader + */ + +abstract class MessageToken extends Krb5Token { + /* Fields in header minus checksum size */ + private static final int TOKEN_NO_CKSUM_SIZE = 16; + + /** + * Filler data as defined in the specification of the Kerberos v5 GSS-API + * Mechanism. + */ + private static final int FILLER = 0xffff; + + // Signing algorithm values (for the SNG_ALG field) + + // From RFC 1964 + /* Use a DES MAC MD5 checksum */ + static final int SGN_ALG_DES_MAC_MD5 = 0x0000; + + /* Use DES MAC checksum. */ + static final int SGN_ALG_DES_MAC = 0x0200; + + // From draft-raeburn-cat-gssapi-krb5-3des-00 + /* Use a HMAC SHA1 DES3 -KD checksum */ + static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400; + + // Sealing algorithm values (for the SEAL_ALG field) + + // RFC 1964 + /** + * A value for the SEAL_ALG field that indicates that no encryption was + * used. + */ + static final int SEAL_ALG_NONE = 0xffff; + /* Use DES CBC encryption algorithm. */ + static final int SEAL_ALG_DES = 0x0000; + + // From draft-raeburn-cat-gssapi-krb5-3des-00 + /** + * Use DES3-KD sealing algorithm. (draft-raeburn-cat-gssapi-krb5-3des-00) + * This algorithm uses triple-DES with key derivation, with a usage + * value KG_USAGE_SEAL. Padding is still to 8-byte multiples, and the + * IV for encrypting application data is zero. + */ + static final int SEAL_ALG_DES3_KD = 0x0200; + + // draft draft-brezak-win2k-krb-rc4-hmac-04.txt + static final int SEAL_ALG_ARCFOUR_HMAC = 0x1000; + static final int SGN_ALG_HMAC_MD5_ARCFOUR = 0x1100; + + private static final int TOKEN_ID_POS = 0; + private static final int SIGN_ALG_POS = 2; + private static final int SEAL_ALG_POS = 4; + + private int seqNumber; + + private boolean confState = true; + private boolean initiator = true; + + private int tokenId = 0; + private GSSHeader gssHeader = null; + private MessageTokenHeader tokenHeader = null; + private byte[] checksum = null; + private byte[] encSeqNumber = null; + private byte[] seqNumberData = null; + + /* cipher instance used by the corresponding GSSContext */ + CipherHelper cipherHelper = null; + + + /** + * Constructs a MessageToken from a byte array. If there are more bytes + * in the array than needed, the extra bytes are simply ignroed. + * + * @param tokenId the token id that should be contained in this token as + * it is read. + * @param context the Kerberos context associated with this token + * @param tokenBytes the byte array containing the token + * @param tokenOffset the offset where the token begins + * @param tokenLen the length of the token + * @param prop the MessageProp structure in which the properties of the + * token should be stored. + * @throws GSSException if there is a problem parsing the token + */ + MessageToken(int tokenId, Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + this(tokenId, context, + new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen), + prop); + } + + /** + * Constructs a MessageToken from an InputStream. Bytes will be read on + * demand and the thread might block if there are not enough bytes to + * complete the token. + * + * @param tokenId the token id that should be contained in this token as + * it is read. + * @param context the Kerberos context associated with this token + * @param is the InputStream from which to read + * @param prop the MessageProp structure in which the properties of the + * token should be stored. + * @throws GSSException if there is a problem reading from the + * InputStream or parsing the token + */ + MessageToken(int tokenId, Krb5Context context, InputStream is, + MessageProp prop) throws GSSException { + init(tokenId, context); + + try { + gssHeader = new GSSHeader(is); + + if (!gssHeader.getOid().equals((Object)OID)) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId)); + } + if (!confState) { + prop.setPrivacy(false); + } + + tokenHeader = new MessageTokenHeader(is, prop); + + encSeqNumber = new byte[8]; + readFully(is, encSeqNumber); + + // debug("\n\tRead EncSeq#=" + + // getHexBytes(encSeqNumber, encSeqNumber.length)); + + checksum = new byte[cipherHelper.getChecksumLength()]; + readFully(is, checksum); + + // debug("\n\tRead checksum=" + + // getHexBytes(checksum, checksum.length)); + // debug("\nLeaving MessageToken.Cons\n"); + + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + e.getMessage()); + } + } + + /** + * Used to obtain the GSSHeader that was at the start of this + * token. + */ + public final GSSHeader getGSSHeader() { + return gssHeader; + } + + /** + * Used to obtain the token id that was contained in this token. + * @return the token id in the token + */ + public final int getTokenId() { + return tokenId; + } + + /** + * Used to obtain the encrypted sequence number in this token. + * @return the encrypted sequence number in the token + */ + public final byte[] getEncSeqNumber() { + return encSeqNumber; + } + + /** + * Used to obtain the checksum that was contained in this token. + * @return the checksum in the token + */ + public final byte[] getChecksum() { + return checksum; + } + + /** + * Used to determine if this token contains any encrypted data. + * @return true if it contains any encrypted data, false if there is only + * plaintext data or if there is no data. + */ + public final boolean getConfState() { + return confState; + } + + /** + * Generates the checksum field and the encrypted sequence number + * field. The encrypted sequence number uses the 8 bytes of the checksum + * as an initial vector in a fixed DesCbc algorithm. + * + * @param prop the MessageProp structure that determines what sort of + * checksum and sealing algorithm should be used. The lower byte + * of qop determines the checksum algorithm while the upper byte + * determines the signing algorithm. + * Checksum values are: + * 0 - default (DES_MAC) + * 1 - MD5 + * 2 - DES_MD5 + * 3 - DES_MAC + * 4 - HMAC_SHA1 + * Sealing values are: + * 0 - default (DES) + * 1 - DES + * 2 - DES3-KD + * + * @param optionalHeader an optional header that will be processed first + * during checksum calculation + * + * @param data the application data to checksum + * @param offset the offset where the data starts + * @param len the length of the data + * + * @param optionalTrailer an optional trailer that will be processed + * last during checksum calculation. e.g., padding that should be + * appended to the application data + * + * @throws GSSException if an error occurs in the checksum calculation or + * encryption sequence number calculation. + */ + public void genSignAndSeqNumber(MessageProp prop, + byte[] optionalHeader, + byte[] data, int offset, int len, + byte[] optionalTrailer) + throws GSSException { + + // debug("Inside MessageToken.genSignAndSeqNumber:\n"); + + int qop = prop.getQOP(); + if (qop != 0) { + qop = 0; + prop.setQOP(qop); + } + + if (!confState) { + prop.setPrivacy(false); + } + + // Create a token header with the correct sign and seal algorithm + // values. + tokenHeader = + new MessageTokenHeader(tokenId, prop.getPrivacy(), qop); + + // Calculate SGN_CKSUM + + checksum = + getChecksum(optionalHeader, data, offset, len, optionalTrailer); + + // debug("\n\tCalc checksum=" + + // getHexBytes(checksum, checksum.length)); + + // Calculate SND_SEQ + + seqNumberData = new byte[8]; + + // When using this RC4 based encryption type, the sequence number is + // always sent in big-endian rather than little-endian order. + if (cipherHelper.isArcFour()) { + writeBigEndian(seqNumber, seqNumberData); + } else { + // for all other etypes + writeLittleEndian(seqNumber, seqNumberData); + } + if (!initiator) { + seqNumberData[4] = (byte)0xff; + seqNumberData[5] = (byte)0xff; + seqNumberData[6] = (byte)0xff; + seqNumberData[7] = (byte)0xff; + } + + encSeqNumber = cipherHelper.encryptSeq(checksum, seqNumberData, 0, 8); + + // debug("\n\tCalc seqNum=" + + // getHexBytes(seqNumberData, seqNumberData.length)); + // debug("\n\tCalc encSeqNum=" + + // getHexBytes(encSeqNumber, encSeqNumber.length)); + } + + /** + * Verifies that the checksum field and sequence number direction bytes + * are valid and consistent with the application data. + * + * @param optionalHeader an optional header that will be processed first + * during checksum calculation. + * + * @param data the application data + * @param offset the offset where the data begins + * @param len the length of the application data + * + * @param optionalTrailer an optional trailer that will be processed last + * during checksum calculation. e.g., padding that should be appended to + * the application data + * + * @throws GSSException if an error occurs in the checksum calculation or + * encryption sequence number calculation. + */ + public final boolean verifySignAndSeqNumber(byte[] optionalHeader, + byte[] data, int offset, int len, + byte[] optionalTrailer) + throws GSSException { + // debug("\tIn verifySign:\n"); + + // debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n"); + + byte[] myChecksum = + getChecksum(optionalHeader, data, offset, len, optionalTrailer); + + // debug("\t\tmychecksum: [" + getHexBytes(myChecksum) +"]\n"); + // debug("\t\tchecksum: [" + getHexBytes(checksum) + "]\n"); + + if (MessageDigest.isEqual(checksum, myChecksum)) { + + seqNumberData = cipherHelper.decryptSeq( + checksum, encSeqNumber, 0, 8); + + // debug("\t\tencSeqNumber: [" + getHexBytes(encSeqNumber) + // + "]\n"); + // debug("\t\tseqNumberData: [" + getHexBytes(seqNumberData) + // + "]\n"); + + /* + * The token from the initiator has direction bytes 0x00 and + * the token from the acceptor has direction bytes 0xff. + */ + byte directionByte = 0; + if (initiator) + directionByte = (byte) 0xff; // Received token from acceptor + + if ((seqNumberData[4] == directionByte) && + (seqNumberData[5] == directionByte) && + (seqNumberData[6] == directionByte) && + (seqNumberData[7] == directionByte)) + return true; + } + + return false; + + } + + public final int getSequenceNumber() { + int sequenceNum = 0; + if (cipherHelper.isArcFour()) { + sequenceNum = readBigEndian(seqNumberData, 0, 4); + } else { + sequenceNum = readLittleEndian(seqNumberData, 0, 4); + } + return sequenceNum; + } + + /** + * Computes the checksum based on the algorithm stored in the + * tokenHeader. + * + * @param optionalHeader an optional header that will be processed first + * during checksum calculation. + * + * @param data the application data + * @param offset the offset where the data begins + * @param len the length of the application data + * + * @param optionalTrailer an optional trailer that will be processed last + * during checksum calculation. e.g., padding that should be appended to + * the application data + * + * @throws GSSException if an error occurs in the checksum calculation. + */ + private byte[] getChecksum(byte[] optionalHeader, + byte[] data, int offset, int len, + byte[] optionalTrailer) + throws GSSException { + + // debug("Will do getChecksum:\n"); + + /* + * For checksum calculation the token header bytes i.e., the first 8 + * bytes following the GSSHeader, are logically prepended to the + * application data to bind the data to this particular token. + * + * Note: There is no such requirement wrt adding padding to the + * application data for checksumming, although the cryptographic + * algorithm used might itself apply some padding. + */ + + byte[] tokenHeaderBytes = tokenHeader.getBytes(); + byte[] existingHeader = optionalHeader; + byte[] checksumDataHeader = tokenHeaderBytes; + + if (existingHeader != null) { + checksumDataHeader = new byte[tokenHeaderBytes.length + + existingHeader.length]; + System.arraycopy(tokenHeaderBytes, 0, + checksumDataHeader, 0, tokenHeaderBytes.length); + System.arraycopy(existingHeader, 0, + checksumDataHeader, tokenHeaderBytes.length, + existingHeader.length); + } + + return cipherHelper.calculateChecksum(tokenHeader.getSignAlg(), + checksumDataHeader, optionalTrailer, data, offset, len, tokenId); + } + + + /** + * Constructs an empty MessageToken for the local context to send to + * the peer. It also increments the local sequence number in the + * Krb5Context instance it uses after obtaining the object lock for + * it. + * + * @param tokenId the token id that should be contained in this token + * @param context the Kerberos context associated with this token + */ + MessageToken(int tokenId, Krb5Context context) throws GSSException { + /* + debug("\n============================"); + debug("\nMySessionKey=" + + getHexBytes(context.getMySessionKey().getBytes())); + debug("\nPeerSessionKey=" + + getHexBytes(context.getPeerSessionKey().getBytes())); + debug("\n============================\n"); + */ + init(tokenId, context); + this.seqNumber = context.incrementMySequenceNumber(); + } + + private void init(int tokenId, Krb5Context context) throws GSSException { + this.tokenId = tokenId; + // Just for consistency check in Wrap + this.confState = context.getConfState(); + + this.initiator = context.isInitiator(); + + this.cipherHelper = context.getCipherHelper(null); + // debug("In MessageToken.Cons"); + } + + /** + * Encodes a GSSHeader and this token onto an OutputStream. + * + * @param os the OutputStream to which this should be written + * @throws GSSException if an error occurs while writing to the OutputStream + */ + public void encode(OutputStream os) throws IOException, GSSException { + gssHeader = new GSSHeader(OID, getKrb5TokenSize()); + gssHeader.encode(os); + tokenHeader.encode(os); + // debug("Writing seqNumber: " + getHexBytes(encSeqNumber)); + os.write(encSeqNumber); + // debug("Writing checksum: " + getHexBytes(checksum)); + os.write(checksum); + } + + /** + * Obtains the size of this token. Note that this excludes the size of + * the GSSHeader. + * @return token size + */ + protected int getKrb5TokenSize() throws GSSException { + return getTokenSize(); + } + + protected final int getTokenSize() throws GSSException { + return TOKEN_NO_CKSUM_SIZE + cipherHelper.getChecksumLength(); + } + + protected static final int getTokenSize(CipherHelper ch) + throws GSSException { + return TOKEN_NO_CKSUM_SIZE + ch.getChecksumLength(); + } + + /** + * Obtains the conext key that is associated with this token. + * @return the context key + */ + /* + public final byte[] getContextKey() { + return contextKey; + } + */ + + /** + * Obtains the encryption algorithm that should be used in this token + * given the state of confidentiality the application requested. + * Requested qop must be consistent with negotiated session key. + * @param confRequested true if the application desired confidentiality + * on this token, false otherwise + * @param qop the qop requested by the application + * @throws GSSException if qop is incompatible with the negotiated + * session key + */ + protected abstract int getSealAlg(boolean confRequested, int qop) + throws GSSException; + + // ******************************************* // + // I N N E R C L A S S E S F O L L O W + // ******************************************* // + + /** + * This inner class represents the initial portion of the message token + * and contains information about the checksum and encryption algorithms + * that are in use. It constitutes the first 8 bytes of the + * message token: + *

    +     *     0..1           TOK_ID          Identification field.
    +     *                                    01 01 - Mic token
    +     *                                    02 01 - Wrap token
    +     *     2..3           SGN_ALG         Checksum algorithm indicator.
    +     *                                    00 00 - DES MAC MD5
    +     *                                    01 00 - MD2.5
    +     *                                    02 00 - DES MAC
    +     *                                    04 00 - HMAC SHA1 DES3-KD
    +     *                                    11 00 - RC4-HMAC
    +     *     4..5           SEAL_ALG        ff ff - none
    +     *                                    00 00 - DES
    +     *                                    02 00 - DES3-KD
    +     *                                    10 00 - RC4-HMAC
    +     *     6..7           Filler          Contains ff ff
    +     * 
    + */ + class MessageTokenHeader { + + private int tokenId; + private int signAlg; + private int sealAlg; + + private byte[] bytes = new byte[8]; + + /** + * Constructs a MessageTokenHeader for the specified token type with + * appropriate checksum and encryption algorithms fields. + * + * @param tokenId the token id for this message token + * @param conf true if confidentiality will be resuested with this + * message token, false otherwise. + * @param qop the value of the quality of protection that will be + * desired. + */ + public MessageTokenHeader(int tokenId, boolean conf, int qop) + throws GSSException { + + this.tokenId = tokenId; + + signAlg = MessageToken.this.getSgnAlg(qop); + + sealAlg = MessageToken.this.getSealAlg(conf, qop); + + bytes[0] = (byte) (tokenId >>> 8); + bytes[1] = (byte) (tokenId); + + bytes[2] = (byte) (signAlg >>> 8); + bytes[3] = (byte) (signAlg); + + bytes[4] = (byte) (sealAlg >>> 8); + bytes[5] = (byte) (sealAlg); + + bytes[6] = (byte) (MessageToken.FILLER >>> 8); + bytes[7] = (byte) (MessageToken.FILLER); + } + + /** + * Constructs a MessageTokenHeader by reading it from an InputStream + * and sets the appropriate confidentiality and quality of protection + * values in a MessageProp structure. + * + * @param is the InputStream to read from + * @param prop the MessageProp to populate + * @throws IOException is an error occurs while reading from the + * InputStream + */ + public MessageTokenHeader(InputStream is, MessageProp prop) + throws IOException { + readFully(is, bytes); + tokenId = readInt(bytes, TOKEN_ID_POS); + signAlg = readInt(bytes, SIGN_ALG_POS); + sealAlg = readInt(bytes, SEAL_ALG_POS); + // debug("\nMessageTokenHeader read tokenId=" + + // getHexBytes(bytes) + "\n"); + // XXX compare to FILLER + int temp = readInt(bytes, SEAL_ALG_POS + 2); + + // debug("SIGN_ALG=" + signAlg); + + switch (sealAlg) { + case SEAL_ALG_DES: + case SEAL_ALG_DES3_KD: + case SEAL_ALG_ARCFOUR_HMAC: + prop.setPrivacy(true); + break; + + default: + prop.setPrivacy(false); + } + + prop.setQOP(0); // default + } + + /** + * Encodes this MessageTokenHeader onto an OutputStream + * @param os the OutputStream to write to + * @throws IOException is an error occurs while writing + */ + public final void encode(OutputStream os) throws IOException { + os.write(bytes); + } + + + /** + * Returns the token id for the message token. + * @return the token id + * @see Krb5Token#MIC_ID + * @see Krb5Token#WRAP_ID + */ + public final int getTokenId() { + return tokenId; + } + + /** + * Returns the sign algorithm for the message token. + * @return the sign algorithm + * @see MessageToken#SIGN_DES_MAC + * @see MessageToken#SIGN_DES_MAC_MD5 + */ + public final int getSignAlg() { + return signAlg; + } + + /** + * Returns the seal algorithm for the message token. + * @return the seal algorithm + * @see MessageToken#SEAL_ALG_DES + * @see MessageToken#SEAL_ALG_NONE + */ + public final int getSealAlg() { + return sealAlg; + } + + /** + * Returns the bytes of this header. + * @return 8 bytes that form this header + */ + public final byte[] getBytes() { + return bytes; + } + } // end of class MessageTokenHeader + + + /** + * Determine signing algorithm based on QOP. + */ + protected int getSgnAlg(int qop) throws GSSException { + // QOP ignored + return cipherHelper.getSgnAlg(); + } +} diff --git a/src/sun/security/jgss/krb5/MessageToken_v2.java b/src/sun/security/jgss/krb5/MessageToken_v2.java new file mode 100644 index 00000000..e82b54a2 --- /dev/null +++ b/src/sun/security/jgss/krb5/MessageToken_v2.java @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.security.MessageDigest; +import java.util.Arrays; + +/** + * This class is a base class for new GSS token definitions, as defined + * in RFC 4121, that pertain to per-message GSS-API calls. Conceptually + * GSS-API has two types of per-message tokens: WrapToken and MicToken. + * They differ in the respect that a WrapToken carries additional plaintext + * or ciphertext application data besides just the sequence number and + * checksum. This class encapsulates the commonality in the structure of + * the WrapToken and the MicToken. This structure can be represented as: + *

    + *

    + * Wrap Tokens
    + *
    + *     Octet no   Name        Description
    + *    ---------------------------------------------------------------
    + *      0..1     TOK_ID     Identification field.  Tokens emitted by
    + *                          GSS_Wrap() contain the hex value 05 04
    + *                          expressed in big-endian order in this field.
    + *      2        Flags      Attributes field, as described in section
    + *                          4.2.2.
    + *      3        Filler     Contains the hex value FF.
    + *      4..5     EC         Contains the "extra count" field, in big-
    + *                          endian order as described in section 4.2.3.
    + *      6..7     RRC        Contains the "right rotation count" in big
    + *                          endian order, as described in section 4.2.5.
    + *      8..15    SND_SEQ    Sequence number field in clear text,
    + *                          expressed in big-endian order.
    + *      16..last Data       Encrypted data for Wrap tokens with
    + *                          confidentiality, or plaintext data followed
    + *                          by the checksum for Wrap tokens without
    + *                          confidentiality, as described in section
    + *                          4.2.4.
    + * MIC Tokens
    + *
    + *     Octet no   Name        Description
    + *     -----------------------------------------------------------------
    + *      0..1     TOK_ID     Identification field.  Tokens emitted by
    + *                          GSS_GetMIC() contain the hex value 04 04
    + *                          expressed in big-endian order in this field.
    + *      2        Flags      Attributes field, as described in section
    + *                          4.2.2.
    + *      3..7     Filler     Contains five octets of hex value FF.
    + *      8..15    SND_SEQ    Sequence number field in clear text,
    + *                          expressed in big-endian order.
    + *      16..last SGN_CKSUM  Checksum of the "to-be-signed" data and
    + *                          octet 0..15, as described in section 4.2.4.
    + *
    + * 
    + *

    + * This class is the super class of WrapToken_v2 and MicToken_v2. The token's + * header (bytes[0..15]) and data (byte[16..]) are saved in tokenHeader and + * tokenData fields. Since there is no easy way to find out the exact length + * of a WrapToken_v2 token from any header info, in the case of reading from + * stream, we read all available() bytes into the token. + *

    + * All read actions are performed in this super class. On the write part, the + * super class only write the tokenHeader, and the content writing is inside + * child classes. + * + * @author Seema Malkani + */ + +abstract class MessageToken_v2 extends Krb5Token { + + protected static final int TOKEN_HEADER_SIZE = 16; + private static final int TOKEN_ID_POS = 0; + private static final int TOKEN_FLAG_POS = 2; + private static final int TOKEN_EC_POS = 4; + private static final int TOKEN_RRC_POS = 6; + + /** + * The size of the random confounder used in a WrapToken. + */ + protected static final int CONFOUNDER_SIZE = 16; + + // RFC 4121, key usage values + static final int KG_USAGE_ACCEPTOR_SEAL = 22; + static final int KG_USAGE_ACCEPTOR_SIGN = 23; + static final int KG_USAGE_INITIATOR_SEAL = 24; + static final int KG_USAGE_INITIATOR_SIGN = 25; + + // RFC 4121, Flags Field + private static final int FLAG_SENDER_IS_ACCEPTOR = 1; + private static final int FLAG_WRAP_CONFIDENTIAL = 2; + private static final int FLAG_ACCEPTOR_SUBKEY = 4; + private static final int FILLER = 0xff; + + private MessageTokenHeader tokenHeader = null; + + // Common field + private int tokenId = 0; + private int seqNumber; + protected byte[] tokenData; // content of token, without the header + protected int tokenDataLen; + + // Key usage number for crypto action + private int key_usage = 0; + + // EC and RRC fields, WrapToken only + private int ec = 0; + private int rrc = 0; + + // Checksum. Always in MicToken, might be in WrapToken + byte[] checksum = null; + + // Context properties + private boolean confState = true; + private boolean initiator = true; + private boolean have_acceptor_subkey = false; + + /* cipher instance used by the corresponding GSSContext */ + CipherHelper cipherHelper = null; + + /** + * Constructs a MessageToken from a byte array. + * + * @param tokenId the token id that should be contained in this token as + * it is read. + * @param context the Kerberos context associated with this token + * @param tokenBytes the byte array containing the token + * @param tokenOffset the offset where the token begins + * @param tokenLen the length of the token + * @param prop the MessageProp structure in which the properties of the + * token should be stored. + * @throws GSSException if there is a problem parsing the token + */ + MessageToken_v2(int tokenId, Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + this(tokenId, context, + new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen), + prop); + } + + /** + * Constructs a MessageToken from an InputStream. Bytes will be read on + * demand and the thread might block if there are not enough bytes to + * complete the token. Please note there is no accurate way to find out + * the size of a token, but we try our best to make sure there is + * enough bytes to construct one. + * + * @param tokenId the token id that should be contained in this token as + * it is read. + * @param context the Kerberos context associated with this token + * @param is the InputStream from which to read + * @param prop the MessageProp structure in which the properties of the + * token should be stored. + * @throws GSSException if there is a problem reading from the + * InputStream or parsing the token + */ + MessageToken_v2(int tokenId, Krb5Context context, InputStream is, + MessageProp prop) throws GSSException { + init(tokenId, context); + + try { + if (!confState) { + prop.setPrivacy(false); + } + tokenHeader = new MessageTokenHeader(is, prop, tokenId); + + // set key_usage + if (tokenId == Krb5Token.WRAP_ID_v2) { + key_usage = (!initiator ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL); + } else if (tokenId == Krb5Token.MIC_ID_v2) { + key_usage = (!initiator ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN); + } + + int minSize = 0; // minimal size for token data + if (tokenId == Krb5Token.WRAP_ID_v2 && prop.getPrivacy()) { + minSize = CONFOUNDER_SIZE + + TOKEN_HEADER_SIZE + cipherHelper.getChecksumLength(); + } else { + minSize = cipherHelper.getChecksumLength(); + } + + // Read token data + if (tokenId == Krb5Token.MIC_ID_v2) { + // The only case we can precisely predict the token data length + tokenDataLen = minSize; + tokenData = new byte[minSize]; + readFully(is, tokenData); + } else { + tokenDataLen = is.available(); + if (tokenDataLen >= minSize) { // read in one shot + tokenData = new byte[tokenDataLen]; + readFully(is, tokenData); + } else { + byte[] tmp = new byte[minSize]; + readFully(is, tmp); + // Hope while blocked in the read above, more data would + // come and is.available() below contains the whole token. + int more = is.available(); + tokenDataLen = minSize + more; + tokenData = Arrays.copyOf(tmp, tokenDataLen); + readFully(is, tokenData, minSize, more); + } + } + + if (tokenId == Krb5Token.WRAP_ID_v2) { + rotate(); + } + + if (tokenId == Krb5Token.MIC_ID_v2 || + (tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy())) { + // Read checksum + int chkLen = cipherHelper.getChecksumLength(); + checksum = new byte[chkLen]; + System.arraycopy(tokenData, tokenDataLen-chkLen, + checksum, 0, chkLen); + + // validate EC for Wrap tokens without confidentiality + if (tokenId == Krb5Token.WRAP_ID_v2 && !prop.getPrivacy()) { + if (chkLen != ec) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + "EC incorrect!"); + } + } + } + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + e.getMessage()); + } + } + + /** + * Used to obtain the token id that was contained in this token. + * @return the token id in the token + */ + public final int getTokenId() { + return tokenId; + } + + /** + * Used to obtain the key_usage type for this token. + * @return the key_usage for the token + */ + public final int getKeyUsage() { + return key_usage; + } + + /** + * Used to determine if this token contains any encrypted data. + * @return true if it contains any encrypted data, false if there is only + * plaintext data or if there is no data. + */ + public final boolean getConfState() { + return confState; + } + + /** + * Generates the checksum field and the sequence number field. + * + * @param prop the MessageProp structure + * @param data the application data to checksum + * @param offset the offset where the data starts + * @param len the length of the data + * + * @throws GSSException if an error occurs in the checksum calculation or + * sequence number calculation. + */ + public void genSignAndSeqNumber(MessageProp prop, + byte[] data, int offset, int len) + throws GSSException { + + // debug("Inside MessageToken.genSignAndSeqNumber:\n"); + + int qop = prop.getQOP(); + if (qop != 0) { + qop = 0; + prop.setQOP(qop); + } + + if (!confState) { + prop.setPrivacy(false); + } + + // Create a new gss token header as defined in RFC 4121 + tokenHeader = new MessageTokenHeader(tokenId, prop.getPrivacy()); + // debug("\n\t Message Header = " + + // getHexBytes(tokenHeader.getBytes(), tokenHeader.getBytes().length)); + + // set key_usage + if (tokenId == Krb5Token.WRAP_ID_v2) { + key_usage = (initiator ? KG_USAGE_INITIATOR_SEAL + : KG_USAGE_ACCEPTOR_SEAL); + } else if (tokenId == Krb5Token.MIC_ID_v2) { + key_usage = (initiator ? KG_USAGE_INITIATOR_SIGN + : KG_USAGE_ACCEPTOR_SIGN); + } + + // Calculate SGN_CKSUM + if ((tokenId == MIC_ID_v2) || + (!prop.getPrivacy() && (tokenId == WRAP_ID_v2))) { + checksum = getChecksum(data, offset, len); + // debug("\n\tCalc checksum=" + + // getHexBytes(checksum, checksum.length)); + } + + // In Wrap tokens without confidentiality, the EC field SHALL be used + // to encode the number of octets in the trailing checksum + if (!prop.getPrivacy() && (tokenId == WRAP_ID_v2)) { + byte[] tok_header = tokenHeader.getBytes(); + tok_header[4] = (byte) (checksum.length >>> 8); + tok_header[5] = (byte) (checksum.length); + } + } + + /** + * Verifies the validity of checksum field + * + * @param data the application data + * @param offset the offset where the data begins + * @param len the length of the application data + * + * @throws GSSException if an error occurs in the checksum calculation + */ + public final boolean verifySign(byte[] data, int offset, int len) + throws GSSException { + + // debug("\t====In verifySign:====\n"); + // debug("\t\t checksum: [" + getHexBytes(checksum) + "]\n"); + // debug("\t\t data = [" + getHexBytes(data) + "]\n"); + + byte[] myChecksum = getChecksum(data, offset, len); + // debug("\t\t mychecksum: [" + getHexBytes(myChecksum) +"]\n"); + + if (MessageDigest.isEqual(checksum, myChecksum)) { + // debug("\t\t====Checksum PASS:====\n"); + return true; + } + return false; + } + + /** + * Rotate bytes as per the "RRC" (Right Rotation Count) received. + * Our implementation does not do any rotates when sending, only + * when receiving, we rotate left as per the RRC count, to revert it. + */ + private void rotate() { + if (rrc % tokenDataLen != 0) { + rrc = rrc % tokenDataLen; + byte[] newBytes = new byte[tokenDataLen]; + + System.arraycopy(tokenData, rrc, newBytes, 0, tokenDataLen-rrc); + System.arraycopy(tokenData, 0, newBytes, tokenDataLen-rrc, rrc); + + tokenData = newBytes; + } + } + + public final int getSequenceNumber() { + return seqNumber; + } + + /** + * Computes the checksum based on the algorithm stored in the + * tokenHeader. + * + * @param data the application data + * @param offset the offset where the data begins + * @param len the length of the application data + * + * @throws GSSException if an error occurs in the checksum calculation. + */ + byte[] getChecksum(byte[] data, int offset, int len) + throws GSSException { + + // debug("Will do getChecksum:\n"); + + /* + * For checksum calculation the token header bytes i.e., the first 16 + * bytes following the GSSHeader, are logically prepended to the + * application data to bind the data to this particular token. + * + * Note: There is no such requirement wrt adding padding to the + * application data for checksumming, although the cryptographic + * algorithm used might itself apply some padding. + */ + + byte[] tokenHeaderBytes = tokenHeader.getBytes(); + + // check confidentiality + int conf_flag = tokenHeaderBytes[TOKEN_FLAG_POS] & + FLAG_WRAP_CONFIDENTIAL; + + // clear EC and RRC in token header for checksum calculation + if ((conf_flag == 0) && (tokenId == WRAP_ID_v2)) { + tokenHeaderBytes[4] = 0; + tokenHeaderBytes[5] = 0; + tokenHeaderBytes[6] = 0; + tokenHeaderBytes[7] = 0; + } + return cipherHelper.calculateChecksum(tokenHeaderBytes, data, + offset, len, key_usage); + } + + + /** + * Constructs an empty MessageToken for the local context to send to + * the peer. It also increments the local sequence number in the + * Krb5Context instance it uses after obtaining the object lock for + * it. + * + * @param tokenId the token id that should be contained in this token + * @param context the Kerberos context associated with this token + */ + MessageToken_v2(int tokenId, Krb5Context context) throws GSSException { + /* + debug("\n============================"); + debug("\nMySessionKey=" + + getHexBytes(context.getMySessionKey().getBytes())); + debug("\nPeerSessionKey=" + + getHexBytes(context.getPeerSessionKey().getBytes())); + debug("\n============================\n"); + */ + init(tokenId, context); + this.seqNumber = context.incrementMySequenceNumber(); + } + + private void init(int tokenId, Krb5Context context) throws GSSException { + this.tokenId = tokenId; + // Just for consistency check in Wrap + this.confState = context.getConfState(); + + this.initiator = context.isInitiator(); + + this.have_acceptor_subkey = context.getKeySrc() == Krb5Context.ACCEPTOR_SUBKEY; + + this.cipherHelper = context.getCipherHelper(null); + // debug("In MessageToken.Cons"); + } + + /** + * Encodes a MessageTokenHeader onto an OutputStream. + * + * @param os the OutputStream to which this should be written + * @throws IOException is an error occurs while writing to the OutputStream + */ + protected void encodeHeader(OutputStream os) throws IOException { + tokenHeader.encode(os); + } + + /** + * Encodes a MessageToken_v2 onto an OutputStream. + * + * @param os the OutputStream to which this should be written + * @throws IOException is an error occurs while encoding the token + */ + public abstract void encode(OutputStream os) throws IOException; + + protected final byte[] getTokenHeader() { + return (tokenHeader.getBytes()); + } + + // ******************************************* // + // I N N E R C L A S S E S F O L L O W + // ******************************************* // + + /** + * This inner class represents the initial portion of the message token. + * It constitutes the first 16 bytes of the message token. + */ + class MessageTokenHeader { + + private int tokenId; + private byte[] bytes = new byte[TOKEN_HEADER_SIZE]; + + // Writes a new token header + public MessageTokenHeader(int tokenId, boolean conf) throws GSSException { + + this.tokenId = tokenId; + + bytes[0] = (byte) (tokenId >>> 8); + bytes[1] = (byte) (tokenId); + + // Flags (Note: MIT impl requires subkey) + int flags = 0; + flags = (initiator ? 0 : FLAG_SENDER_IS_ACCEPTOR) | + ((conf && tokenId != MIC_ID_v2) ? + FLAG_WRAP_CONFIDENTIAL : 0) | + (have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0); + bytes[2] = (byte) flags; + + // filler + bytes[3] = (byte) FILLER; + + if (tokenId == WRAP_ID_v2) { + // EC field + bytes[4] = (byte) 0; + bytes[5] = (byte) 0; + // RRC field + bytes[6] = (byte) 0; + bytes[7] = (byte) 0; + } else if (tokenId == MIC_ID_v2) { + // more filler for MicToken + for (int i = 4; i < 8; i++) { + bytes[i] = (byte) FILLER; + } + } + + // Calculate SND_SEQ, only write 4 bytes from the 12th position + writeBigEndian(seqNumber, bytes, 12); + } + + /** + * Reads a MessageTokenHeader from an InputStream and sets the + * appropriate confidentiality and quality of protection + * values in a MessageProp structure. + * + * @param is the InputStream to read from + * @param prop the MessageProp to populate + * @throws IOException is an error occurs while reading from the + * InputStream + */ + public MessageTokenHeader(InputStream is, MessageProp prop, int tokId) + throws IOException, GSSException { + + readFully(is, bytes, 0, TOKEN_HEADER_SIZE); + tokenId = readInt(bytes, TOKEN_ID_POS); + + // validate Token ID + if (tokenId != tokId) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + "Defective Token ID!"); + } + + /* + * Validate new GSS TokenHeader + */ + + // valid acceptor_flag + // If I am initiator, the received token should have ACCEPTOR on + int acceptor_flag = (initiator ? FLAG_SENDER_IS_ACCEPTOR : 0); + int flag = bytes[TOKEN_FLAG_POS] & FLAG_SENDER_IS_ACCEPTOR; + if (flag != acceptor_flag) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + "Acceptor Flag Error!"); + } + + // check for confidentiality + int conf_flag = bytes[TOKEN_FLAG_POS] & FLAG_WRAP_CONFIDENTIAL; + if ((conf_flag == FLAG_WRAP_CONFIDENTIAL) && + (tokenId == WRAP_ID_v2)) { + prop.setPrivacy(true); + } else { + prop.setPrivacy(false); + } + + if (tokenId == WRAP_ID_v2) { + // validate filler + if ((bytes[3] & 0xff) != FILLER) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(tokenId) + ":" + "Defective Token Filler!"); + } + + // read EC field + ec = readBigEndian(bytes, TOKEN_EC_POS, 2); + + // read RRC field + rrc = readBigEndian(bytes, TOKEN_RRC_POS, 2); + } else if (tokenId == MIC_ID_v2) { + for (int i = 3; i < 8; i++) { + if ((bytes[i] & 0xff) != FILLER) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, + -1, getTokenName(tokenId) + ":" + + "Defective Token Filler!"); + } + } + } + + // set default QOP + prop.setQOP(0); + + // sequence number + seqNumber = readBigEndian(bytes, 0, 8); + } + + /** + * Encodes this MessageTokenHeader onto an OutputStream + * @param os the OutputStream to write to + * @throws IOException is an error occurs while writing + */ + public final void encode(OutputStream os) throws IOException { + os.write(bytes); + } + + + /** + * Returns the token id for the message token. + * @return the token id + * @see Krb5Token#MIC_ID_v2 + * @see Krb5Token#WRAP_ID_v2 + */ + public final int getTokenId() { + return tokenId; + } + + /** + * Returns the bytes of this header. + * @return 8 bytes that form this header + */ + public final byte[] getBytes() { + return bytes; + } + } // end of class MessageTokenHeader +} diff --git a/src/sun/security/jgss/krb5/MicToken.java b/src/sun/security/jgss/krb5/MicToken.java new file mode 100644 index 00000000..638b8904 --- /dev/null +++ b/src/sun/security/jgss/krb5/MicToken.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +class MicToken extends MessageToken { + + public MicToken(Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + super(Krb5Token.MIC_ID, context, + tokenBytes, tokenOffset, tokenLen, prop); + } + + public MicToken(Krb5Context context, + InputStream is, MessageProp prop) + throws GSSException { + super(Krb5Token.MIC_ID, context, is, prop); + } + + public void verify(byte[] data, int offset, int len) throws GSSException { + if (!verifySignAndSeqNumber(null, data, offset, len, null)) + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in MIC token"); + } + + public void verify(InputStream data) throws GSSException { + byte[] dataBytes = null; + try { + dataBytes = new byte[data.available()]; + data.read(dataBytes); + } catch (IOException e) { + // Error reading application data + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in MIC token"); + } + verify(dataBytes, 0, dataBytes.length); + } + + public MicToken(Krb5Context context, MessageProp prop, + byte[] data, int pos, int len) + throws GSSException { + super(Krb5Token.MIC_ID, context); + + // debug("Application data to MicToken verify is [" + + // getHexBytes(data, pos, len) + "]\n"); + if (prop == null) prop = new MessageProp(0, false); + genSignAndSeqNumber(prop, null, data, pos, len, null); + } + + public MicToken(Krb5Context context, MessageProp prop, + InputStream data) + throws GSSException, IOException { + super(Krb5Token.MIC_ID, context); + byte[] dataBytes = new byte[data.available()]; + data.read(dataBytes); + + //debug("Application data to MicToken cons is [" + + // getHexBytes(dataBytes) + "]\n"); + if (prop == null) prop = new MessageProp(0, false); + genSignAndSeqNumber(prop, null, dataBytes, 0, dataBytes.length, null); + } + + protected int getSealAlg(boolean confRequested, int qop) { + return (SEAL_ALG_NONE); + } + + public int encode(byte[] outToken, int offset) + throws IOException, GSSException { + // Token is small + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + super.encode(bos); + byte[] token = bos.toByteArray(); + System.arraycopy(token, 0, outToken, offset, token.length); + return token.length; + } + + public byte[] encode() throws IOException, GSSException{ + // XXX Fine tune this initial size + ByteArrayOutputStream bos = new ByteArrayOutputStream(50); + encode(bos); + return bos.toByteArray(); + } + +} diff --git a/src/sun/security/jgss/krb5/MicToken_v2.java b/src/sun/security/jgss/krb5/MicToken_v2.java new file mode 100644 index 00000000..bd4a263a --- /dev/null +++ b/src/sun/security/jgss/krb5/MicToken_v2.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +/** + * This class represents the new format of GSS MIC tokens, as specified + * in RFC 4121 + * + * MIC tokens = { 16-byte token-header | HMAC } + * where HMAC is on { plaintext | 16-byte token-header } + * + * @author Seema Malkani + */ + +class MicToken_v2 extends MessageToken_v2 { + + public MicToken_v2(Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + super(Krb5Token.MIC_ID_v2, context, + tokenBytes, tokenOffset, tokenLen, prop); + } + + public MicToken_v2(Krb5Context context, InputStream is, MessageProp prop) + throws GSSException { + super(Krb5Token.MIC_ID_v2, context, is, prop); + } + + public void verify(byte[] data, int offset, int len) throws GSSException { + if (!verifySign(data, offset, len)) + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in MIC token"); + } + + public void verify(InputStream data) throws GSSException { + byte[] dataBytes = null; + try { + dataBytes = new byte[data.available()]; + data.read(dataBytes); + } catch (IOException e) { + // Error reading application data + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in MIC token"); + } + verify(dataBytes, 0, dataBytes.length); + } + + public MicToken_v2(Krb5Context context, MessageProp prop, + byte[] data, int pos, int len) + throws GSSException { + super(Krb5Token.MIC_ID_v2, context); + + // debug("Application data to MicToken verify is [" + + // getHexBytes(data, pos, len) + "]\n"); + if (prop == null) prop = new MessageProp(0, false); + genSignAndSeqNumber(prop, data, pos, len); + } + + public MicToken_v2(Krb5Context context, MessageProp prop, InputStream data) + throws GSSException, IOException { + + super(Krb5Token.MIC_ID_v2, context); + byte[] dataBytes = new byte[data.available()]; + data.read(dataBytes); + + // debug("Application data to MicToken cons is [" + + // getHexBytes(dataBytes) + "]\n"); + if (prop == null) prop = new MessageProp(0, false); + genSignAndSeqNumber(prop, dataBytes, 0, dataBytes.length); + } + + public byte[] encode() throws IOException { + // XXX Fine tune this initial size + ByteArrayOutputStream bos = new ByteArrayOutputStream(50); + encode(bos); + return bos.toByteArray(); + } + + public int encode(byte[] outToken, int offset) throws IOException { + byte[] token = encode(); + System.arraycopy(token, 0, outToken, offset, token.length); + return token.length; + } + + public void encode(OutputStream os) throws IOException { + encodeHeader(os); + os.write(checksum); + } +} diff --git a/src/sun/security/jgss/krb5/ServiceCreds.java b/src/sun/security/jgss/krb5/ServiceCreds.java new file mode 100644 index 00000000..8cb740a4 --- /dev/null +++ b/src/sun/security/jgss/krb5/ServiceCreds.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KeyTab; +import javax.security.auth.Subject; + +import sun.security.krb5.Credentials; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.KrbException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import sun.security.krb5.*; + +/** + * Credentials of a kerberos acceptor. A KerberosPrincipal object (kp) is + * the principal. It can be specified as the serverPrincipal argument + * in the getInstance() method, or uses only KerberosPrincipal in the subject. + * Otherwise, the creds object is unbound and kp is null. + * + * The class also encapsulates various secrets, which can be: + * + * 1. Some KerberosKeys (generated from password) + * 2. Some KeyTabs (for a typical service based on keytabs) + * 3. A TGT (for S4U2proxy extension or user2user) + * + * Note that some secrets can coexist. For example, a user2user service + * can use its keytab (or keys) if the client can successfully obtain a + * normal service ticket, or it can use the TGT (actually, the session key + * of the TGT) if the client can only acquire a service ticket + * of ENC-TKT-IN-SKEY style. + * + * @since 1.8 + */ +public final class ServiceCreds { + // The principal, or null if unbound + private KerberosPrincipal kp; + + // All principals in the subject's princ set + private Set allPrincs; + + // All private credentials that can be used + private List ktabs; + private List kk; + private KerberosTicket tgt; + + private boolean destroyed; + + private ServiceCreds() { + // Make sure this class cannot be instantiated externally. + } + + /** + * Creates a ServiceCreds object based on info in a Subject for + * a given principal name (if specified). + * @return the object, or null if there is no private creds for it + */ + public static ServiceCreds getInstance( + Subject subj, String serverPrincipal) { + + ServiceCreds sc = new ServiceCreds(); + + sc.allPrincs = + subj.getPrincipals(KerberosPrincipal.class); + + // Compatibility. A key implies its own principal + for (KerberosKey key: SubjectComber.findMany( + subj, serverPrincipal, null, KerberosKey.class)) { + sc.allPrincs.add(key.getPrincipal()); + } + + if (serverPrincipal != null) { // A named principal + sc.kp = new KerberosPrincipal(serverPrincipal); + } else { + // For compatibility reason, we set the name of default principal + // to the "only possible" name it can take, which means there is + // only one KerberosPrincipal and there is no unbound keytabs + if (sc.allPrincs.size() == 1) { + boolean hasUnbound = false; + for (KeyTab ktab: SubjectComber.findMany( + subj, null, null, KeyTab.class)) { + if (!ktab.isBound()) { + hasUnbound = true; + break; + } + } + if (!hasUnbound) { + sc.kp = sc.allPrincs.iterator().next(); + serverPrincipal = sc.kp.getName(); + } + } + } + + sc.ktabs = SubjectComber.findMany( + subj, serverPrincipal, null, KeyTab.class); + sc.kk = SubjectComber.findMany( + subj, serverPrincipal, null, KerberosKey.class); + sc.tgt = SubjectComber.find( + subj, null, serverPrincipal, KerberosTicket.class); + if (sc.ktabs.isEmpty() && sc.kk.isEmpty() && sc.tgt == null) { + return null; + } + + sc.destroyed = false; + + return sc; + } + + // can be null + public String getName() { + if (destroyed) { + throw new IllegalStateException("This object is destroyed"); + } + return kp == null ? null : kp.getName(); + } + + /** + * Gets keys for "someone". Used in 2 cases: + * 1. By TLS because it needs to get keys before client comes in. + * 2. As a fallback in getEKeys() below. + * This method can still return an empty array. + */ + public KerberosKey[] getKKeys() { + if (destroyed) { + throw new IllegalStateException("This object is destroyed"); + } + KerberosPrincipal one = kp; // named principal + if (one == null && !allPrincs.isEmpty()) { // or, a known principal + one = allPrincs.iterator().next(); + } + if (one == null) { // Or, some random one + for (KeyTab ktab: ktabs) { + // Must be unbound keytab, otherwise, allPrincs is not empty + PrincipalName pn = + Krb5Util.snapshotFromJavaxKeyTab(ktab).getOneName(); + if (pn != null) { + one = new KerberosPrincipal(pn.getName()); + break; + } + } + } + if (one != null) { + return getKKeys(one); + } else { + return new KerberosKey[0]; + } + } + + /** + * Get kkeys for a principal, + * @param princ the target name initiator requests. Not null. + * @return keys for the princ, never null, might be empty + */ + public KerberosKey[] getKKeys(KerberosPrincipal princ) { + if (destroyed) { + throw new IllegalStateException("This object is destroyed"); + } + ArrayList keys = new ArrayList<>(); + if (kp != null && !princ.equals(kp)) { // named principal + return new KerberosKey[0]; + } + for (KerberosKey k: kk) { + if (k.getPrincipal().equals(princ)) { + keys.add(k); + } + } + for (KeyTab ktab: ktabs) { + if (ktab.getPrincipal() == null && ktab.isBound()) { + // legacy bound keytab. although we don't know who + // the bound principal is, it must be in allPrincs + if (!allPrincs.contains(princ)) { + continue; // skip this legacy bound keytab + } + } + for (KerberosKey k: ktab.getKeys(princ)) { + keys.add(k); + } + } + return keys.toArray(new KerberosKey[keys.size()]); + } + + /** + * Gets EKeys for a principal. + * @param princ the target name initiator requests. Not null. + * @return keys for the princ, never null, might be empty + */ + public EncryptionKey[] getEKeys(PrincipalName princ) { + if (destroyed) { + throw new IllegalStateException("This object is destroyed"); + } + KerberosKey[] kkeys = getKKeys(new KerberosPrincipal(princ.getName())); + if (kkeys.length == 0) { + // Fallback: old JDK does not perform real name checking. If the + // acceptor has host.sun.com but initiator requests for host, + // as long as their keys match (i.e. keys for one can decrypt + // the other's service ticket), the authentication is OK. + // There are real customers depending on this to use different + // names for a single service. + kkeys = getKKeys(); + } + EncryptionKey[] ekeys = new EncryptionKey[kkeys.length]; + for (int i=0; i T find(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass) { + + // findAux returns T if oneOnly. + return credClass.cast(findAux(subject, serverPrincipal, + clientPrincipal, credClass, true)); + } + + @SuppressWarnings("unchecked") // findAux returns List if !oneOnly. + static List findMany(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass) { + + return (List)findAux(subject, serverPrincipal, clientPrincipal, + credClass, false); + } + + /** + * Find private credentials for the specified client/server principals + * in the subject. Returns null if the subject is null. + * + * @return the private credentials + */ + // Returns T if oneOnly and List if !oneOnly. + private static Object findAux(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass, boolean oneOnly) { + + if (subject == null) { + return null; + } else { + List answer = (oneOnly ? null : new ArrayList()); + + if (credClass == KeyTab.class) { + Iterator iterator = + subject.getPrivateCredentials(KeyTab.class).iterator(); + while (iterator.hasNext()) { + KeyTab t = iterator.next(); + if (serverPrincipal != null && t.isBound()) { + KerberosPrincipal name = t.getPrincipal(); + if (name != null) { + if (!serverPrincipal.equals(name.getName())) { + continue; + } + } else { + // legacy bound keytab. although we don't know who + // the bound principal is, it must be in allPrincs + boolean found = false; + for (KerberosPrincipal princ: + subject.getPrincipals(KerberosPrincipal.class)) { + if (princ.getName().equals(serverPrincipal)) { + found = true; + break; + } + } + if (!found) continue; + } + } + // Check passed, we can add now + if (DEBUG) { + System.out.println("Found " + credClass.getSimpleName() + + " " + t); + } + if (oneOnly) { + return t; + } else { + answer.add(credClass.cast(t)); + } + } + } else if (credClass == KerberosKey.class) { + // We are looking for credentials for the serverPrincipal + Iterator iterator = + subject.getPrivateCredentials(KerberosKey.class).iterator(); + while (iterator.hasNext()) { + KerberosKey t = iterator.next(); + String name = t.getPrincipal().getName(); + if (serverPrincipal == null || serverPrincipal.equals(name)) { + if (DEBUG) { + System.out.println("Found " + + credClass.getSimpleName() + " for " + name); + } + if (oneOnly) { + return t; + } else { + answer.add(credClass.cast(t)); + } + } + } + } else if (credClass == KerberosTicket.class) { + // we are looking for a KerberosTicket credentials + // for client-service principal pair + Set pcs = subject.getPrivateCredentials(); + synchronized (pcs) { + Iterator iterator = pcs.iterator(); + while (iterator.hasNext()) { + Object obj = iterator.next(); + if (obj instanceof KerberosTicket) { + @SuppressWarnings("unchecked") + KerberosTicket ticket = (KerberosTicket)obj; + if (DEBUG) { + System.out.println("Found ticket for " + + ticket.getClient() + + " to go to " + + ticket.getServer() + + " expiring on " + + ticket.getEndTime()); + } + if (!ticket.isCurrent()) { + // let us remove the ticket from the Subject + // Note that both TGT and service ticket will be + // removed upon expiration + if (!subject.isReadOnly()) { + iterator.remove(); + try { + ticket.destroy(); + if (DEBUG) { + System.out.println("Removed and destroyed " + + "the expired Ticket \n" + + ticket); + + } + } catch (DestroyFailedException dfe) { + if (DEBUG) { + System.out.println("Expired ticket not" + + " detroyed successfully. " + dfe); + } + } + + } + } else { + if (serverPrincipal == null || + ticket.getServer().getName().equals(serverPrincipal)) { + + if (clientPrincipal == null || + clientPrincipal.equals( + ticket.getClient().getName())) { + if (oneOnly) { + return ticket; + } else { + // Record names so that tickets will + // all belong to same principals + if (clientPrincipal == null) { + clientPrincipal = + ticket.getClient().getName(); + } + if (serverPrincipal == null) { + serverPrincipal = + ticket.getServer().getName(); + } + answer.add(credClass.cast(ticket)); + } + } + } + } + } + } + } + } + return answer; + } + } +} diff --git a/src/sun/security/jgss/krb5/WrapToken.java b/src/sun/security/jgss/krb5/WrapToken.java new file mode 100644 index 00000000..154be82d --- /dev/null +++ b/src/sun/security/jgss/krb5/WrapToken.java @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import sun.security.krb5.Confounder; + +/** + * This class represents a token emitted by the GSSContext.wrap() + * call. It is a MessageToken except that it also contains plaintext + * or encrypted data at the end. A wrapToken has certain other rules + * that are peculiar to it and different from a MICToken, which is + * another type of MessageToken. All data in a WrapToken is prepended + * by a random counfounder of 8 bytes. All data in a WrapToken is + * also padded with one to eight bytes where all bytes are equal in + * value to the number of bytes being padded. Thus, all application + * data is replaced by (confounder || data || padding). + * + * @author Mayank Upadhyay + */ +class WrapToken extends MessageToken { + /** + * The size of the random confounder used in a WrapToken. + */ + static final int CONFOUNDER_SIZE = 8; + + /* + * The padding used with a WrapToken. All data is padded to the + * next multiple of 8 bytes, even if its length is already + * multiple of 8. + * Use this table as a quick way to obtain padding bytes by + * indexing it with the number of padding bytes required. + */ + static final byte[][] pads = { + null, // No, no one escapes padding + {0x01}, + {0x02, 0x02}, + {0x03, 0x03, 0x03}, + {0x04, 0x04, 0x04, 0x04}, + {0x05, 0x05, 0x05, 0x05, 0x05}, + {0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, + {0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}, + {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08} + }; + + /* + * A token may come in either in an InputStream or as a + * byte[]. Store a reference to it in either case and process + * it's data only later when getData() is called and + * decryption/copying is needed to be done. Note that JCE can + * decrypt both from a byte[] and from an InputStream. + */ + private boolean readTokenFromInputStream = true; + private InputStream is = null; + private byte[] tokenBytes = null; + private int tokenOffset = 0; + private int tokenLen = 0; + + /* + * Application data may come from an InputStream or from a + * byte[]. However, it will always be stored and processed as a + * byte[] since + * (a) the MessageDigest class only accepts a byte[] as input and + * (b) It allows writing to an OuputStream via a CipherOutputStream. + */ + private byte[] dataBytes = null; + private int dataOffset = 0; + private int dataLen = 0; + + // the len of the token data: (confounder || data || padding) + private int dataSize = 0; + + // Accessed by CipherHelper + byte[] confounder = null; + byte[] padding = null; + + private boolean privacy = false; + + /** + * Constructs a WrapToken from token bytes obtained from the + * peer. + * @param context the mechanism context associated with this + * token + * @param tokenBytes the bytes of the token + * @param tokenOffset the offset of the token + * @param tokenLen the length of the token + * @param prop the MessageProp into which characteristics of the + * parsed token will be stored. + * @throws GSSException if the token is defective + */ + public WrapToken(Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + + // Just parse the MessageToken part first + super(Krb5Token.WRAP_ID, context, + tokenBytes, tokenOffset, tokenLen, prop); + + this.readTokenFromInputStream = false; + + // Will need the token bytes again when extracting data + this.tokenBytes = tokenBytes; + this.tokenOffset = tokenOffset; + this.tokenLen = tokenLen; + this.privacy = prop.getPrivacy(); + dataSize = + getGSSHeader().getMechTokenLength() - getKrb5TokenSize(); + } + + /** + * Constructs a WrapToken from token bytes read on the fly from + * an InputStream. + * @param context the mechanism context associated with this + * token + * @param is the InputStream containing the token bytes + * @param prop the MessageProp into which characteristics of the + * parsed token will be stored. + * @throws GSSException if the token is defective or if there is + * a problem reading from the InputStream + */ + public WrapToken(Krb5Context context, + InputStream is, MessageProp prop) + throws GSSException { + + // Just parse the MessageToken part first + super(Krb5Token.WRAP_ID, context, is, prop); + + // Will need the token bytes again when extracting data + this.is = is; + this.privacy = prop.getPrivacy(); + /* + debug("WrapToken Cons: gssHeader.getMechTokenLength=" + + getGSSHeader().getMechTokenLength()); + debug("\n token size=" + + getTokenSize()); + */ + + dataSize = + getGSSHeader().getMechTokenLength() - getTokenSize(); + // debug("\n dataSize=" + dataSize); + // debug("\n"); + } + + /** + * Obtains the application data that was transmitted in this + * WrapToken. + * @return a byte array containing the application data + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + public byte[] getData() throws GSSException { + + byte[] temp = new byte[dataSize]; + getData(temp, 0); + + // Remove the confounder and the padding + byte[] retVal = new byte[dataSize - confounder.length - + padding.length]; + System.arraycopy(temp, 0, retVal, 0, retVal.length); + + return retVal; + } + + /** + * Obtains the application data that was transmitted in this + * WrapToken, writing it into an application provided output + * array. + * @param dataBuf the output buffer into which the data must be + * written + * @param dataBufOffset the offset at which to write the data + * @return the size of the data written + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + public int getData(byte[] dataBuf, int dataBufOffset) + throws GSSException { + + if (readTokenFromInputStream) + getDataFromStream(dataBuf, dataBufOffset); + else + getDataFromBuffer(dataBuf, dataBufOffset); + + return (dataSize - confounder.length - padding.length); + } + + /** + * Helper routine to obtain the application data transmitted in + * this WrapToken. It is called if the WrapToken was constructed + * with a byte array as input. + * @param dataBuf the output buffer into which the data must be + * written + * @param dataBufOffset the offset at which to write the data + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset) + throws GSSException { + + GSSHeader gssHeader = getGSSHeader(); + int dataPos = tokenOffset + + gssHeader.getLength() + getTokenSize(); + + if (dataPos + dataSize > tokenOffset + tokenLen) + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Insufficient data in " + + getTokenName(getTokenId())); + + // debug("WrapToken cons: data is token is [" + + // getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n"); + + confounder = new byte[CONFOUNDER_SIZE]; + + // Do decryption if this token was privacy protected. + + if (privacy) { + cipherHelper.decryptData(this, + tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset); + /* + debug("\t\tDecrypted data is [" + + getHexBytes(confounder) + " " + + getHexBytes(dataBuf, dataBufOffset, + dataSize - CONFOUNDER_SIZE - padding.length) + + getHexBytes(padding) + + "]\n"); + */ + + } else { + + // Token data is in cleartext + // debug("\t\tNo encryption was performed by peer.\n"); + System.arraycopy(tokenBytes, dataPos, + confounder, 0, CONFOUNDER_SIZE); + int padSize = tokenBytes[dataPos + dataSize - 1]; + if (padSize < 0) + padSize = 0; + if (padSize > 8) + padSize %= 8; + + padding = pads[padSize]; + // debug("\t\tPadding applied was: " + padSize + "\n"); + + System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE, + dataBuf, dataBufOffset, dataSize - + CONFOUNDER_SIZE - padSize); + + // byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize]; + // System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE, + // debugbuf, 0, debugbuf.length); + // debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length)); + } + + /* + * Make sure sign and sequence number are not corrupt + */ + + if (!verifySignAndSeqNumber(confounder, + dataBuf, dataBufOffset, + dataSize - CONFOUNDER_SIZE + - padding.length, + padding)) + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in Wrap token"); + } + + /** + * Helper routine to obtain the application data transmitted in + * this WrapToken. It is called if the WrapToken was constructed + * with an Inputstream. + * @param dataBuf the output buffer into which the data must be + * written + * @param dataBufOffset the offset at which to write the data + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + private void getDataFromStream(byte[] dataBuf, int dataBufOffset) + throws GSSException { + + GSSHeader gssHeader = getGSSHeader(); + + // Don't check the token length. Data will be read on demand from + // the InputStream. + + // debug("WrapToken cons: data will be read from InputStream.\n"); + + confounder = new byte[CONFOUNDER_SIZE]; + + try { + + // Do decryption if this token was privacy protected. + + if (privacy) { + cipherHelper.decryptData(this, is, dataSize, + dataBuf, dataBufOffset); + + // debug("\t\tDecrypted data is [" + + // getHexBytes(confounder) + " " + + // getHexBytes(dataBuf, dataBufOffset, + // dataSize - CONFOUNDER_SIZE - padding.length) + + // getHexBytes(padding) + + // "]\n"); + + } else { + + // Token data is in cleartext + // debug("\t\tNo encryption was performed by peer.\n"); + readFully(is, confounder); + + if (cipherHelper.isArcFour()) { + padding = pads[1]; + readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1); + } else { + // Data is always a multiple of 8 with this GSS Mech + // Copy all but last block as they are + int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1; + int offset = dataBufOffset; + for (int i = 0; i < numBlocks; i++) { + readFully(is, dataBuf, offset, 8); + offset += 8; + } + + byte[] finalBlock = new byte[8]; + readFully(is, finalBlock); + + int padSize = finalBlock[7]; + padding = pads[padSize]; + + // debug("\t\tPadding applied was: " + padSize + "\n"); + System.arraycopy(finalBlock, 0, dataBuf, offset, + finalBlock.length - padSize); + } + } + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + getTokenName(getTokenId()) + + ": " + e.getMessage()); + } + + /* + * Make sure sign and sequence number are not corrupt + */ + + if (!verifySignAndSeqNumber(confounder, + dataBuf, dataBufOffset, + dataSize - CONFOUNDER_SIZE + - padding.length, + padding)) + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum or sequence number in Wrap token"); + } + + + /** + * Helper routine to pick the right padding for a certain length + * of application data. Every application message has some + * padding between 1 and 8 bytes. + * @param len the length of the application data + * @return the padding to be applied + */ + private byte[] getPadding(int len) { + int padSize = 0; + // For RC4-HMAC, all padding is rounded up to 1 byte. + // One byte is needed to say that there is 1 byte of padding. + if (cipherHelper.isArcFour()) { + padSize = 1; + } else { + padSize = len % 8; + padSize = 8 - padSize; + } + return pads[padSize]; + } + + public WrapToken(Krb5Context context, MessageProp prop, + byte[] dataBytes, int dataOffset, int dataLen) + throws GSSException { + + super(Krb5Token.WRAP_ID, context); + + confounder = Confounder.bytes(CONFOUNDER_SIZE); + + padding = getPadding(dataLen); + dataSize = confounder.length + dataLen + padding.length; + this.dataBytes = dataBytes; + this.dataOffset = dataOffset; + this.dataLen = dataLen; + + /* + debug("\nWrapToken cons: data to wrap is [" + + getHexBytes(confounder) + " " + + getHexBytes(dataBytes, dataOffset, dataLen) + " " + + // padding is never null for Wrap + getHexBytes(padding) + "]\n"); + */ + + genSignAndSeqNumber(prop, + confounder, + dataBytes, dataOffset, dataLen, + padding); + + /* + * If the application decides to ask for privacy when the context + * did not negotiate for it, do not provide it. The peer might not + * have support for it. The app will realize this with a call to + * pop.getPrivacy() after wrap(). + */ + if (!context.getConfState()) + prop.setPrivacy(false); + + privacy = prop.getPrivacy(); + } + + public void encode(OutputStream os) throws IOException, GSSException { + + super.encode(os); + + // debug("Writing data: ["); + if (!privacy) { + + // debug(getHexBytes(confounder, confounder.length)); + os.write(confounder); + + // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen)); + os.write(dataBytes, dataOffset, dataLen); + + // debug(" " + getHexBytes(padding, padding.length)); + os.write(padding); + + } else { + + cipherHelper.encryptData(this, confounder, + dataBytes, dataOffset, dataLen, padding, os); + } + // debug("]\n"); + } + + public byte[] encode() throws IOException, GSSException { + // XXX Fine tune this initial size + ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50); + encode(bos); + return bos.toByteArray(); + } + + public int encode(byte[] outToken, int offset) + throws IOException, GSSException { + + // Token header is small + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + super.encode(bos); + byte[] header = bos.toByteArray(); + System.arraycopy(header, 0, outToken, offset, header.length); + offset += header.length; + + // debug("WrapToken.encode: Writing data: ["); + if (!privacy) { + + // debug(getHexBytes(confounder, confounder.length)); + System.arraycopy(confounder, 0, outToken, offset, + confounder.length); + offset += confounder.length; + + // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen)); + System.arraycopy(dataBytes, dataOffset, outToken, offset, + dataLen); + offset += dataLen; + + // debug(" " + getHexBytes(padding, padding.length)); + System.arraycopy(padding, 0, outToken, offset, padding.length); + + } else { + + cipherHelper.encryptData(this, confounder, dataBytes, + dataOffset, dataLen, padding, outToken, offset); + + // debug(getHexBytes(outToken, offset, dataSize)); + } + + // debug("]\n"); + + // %%% assume that plaintext length == ciphertext len + return (header.length + confounder.length + dataLen + padding.length); + + } + + protected int getKrb5TokenSize() throws GSSException { + return (getTokenSize() + dataSize); + } + + protected int getSealAlg(boolean conf, int qop) throws GSSException { + if (!conf) { + return SEAL_ALG_NONE; + } + + // ignore QOP + return cipherHelper.getSealAlg(); + } + + // This implementation is way too conservative. And it certainly + // doesn't return the maximum limit. + static int getSizeLimit(int qop, boolean confReq, int maxTokenSize, + CipherHelper ch) throws GSSException { + return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) - + (getTokenSize(ch) + CONFOUNDER_SIZE) - 8); /* safety */ + } + +} diff --git a/src/sun/security/jgss/krb5/WrapToken_v2.java b/src/sun/security/jgss/krb5/WrapToken_v2.java new file mode 100644 index 00000000..fa4de1dd --- /dev/null +++ b/src/sun/security/jgss/krb5/WrapToken_v2.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.krb5; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import sun.security.krb5.Confounder; + +/** + * This class represents the new format of GSS tokens, as specified in RFC + * 4121, emitted by the GSSContext.wrap() call. It is a MessageToken except + * that it also contains plaintext or encrypted data at the end. A WrapToken + * has certain other rules that are peculiar to it and different from a + * MICToken, which is another type of MessageToken. All data in a WrapToken is + * prepended by a random confounder of 16 bytes. Thus, all application data + * is replaced by (confounder || data || tokenHeader || checksum). + * + * @author Seema Malkani + */ +class WrapToken_v2 extends MessageToken_v2 { + + // Accessed by CipherHelper + byte[] confounder = null; + + private final boolean privacy; + + /** + * Constructs a WrapToken from token bytes obtained from the + * peer. + * @param context the mechanism context associated with this + * token + * @param tokenBytes the bytes of the token + * @param tokenOffset the offset of the token + * @param tokenLen the length of the token + * @param prop the MessageProp into which characteristics of the + * parsed token will be stored. + * @throws GSSException if the token is defective + */ + public WrapToken_v2(Krb5Context context, + byte[] tokenBytes, int tokenOffset, int tokenLen, + MessageProp prop) throws GSSException { + + super(Krb5Token.WRAP_ID_v2, context, + tokenBytes, tokenOffset, tokenLen, prop); + this.privacy = prop.getPrivacy(); + } + + /** + * Constructs a WrapToken from token bytes read on the fly from + * an InputStream. + * @param context the mechanism context associated with this + * token + * @param is the InputStream containing the token bytes + * @param prop the MessageProp into which characteristics of the + * parsed token will be stored. + * @throws GSSException if the token is defective or if there is + * a problem reading from the InputStream + */ + public WrapToken_v2(Krb5Context context, + InputStream is, MessageProp prop) + throws GSSException { + + super(Krb5Token.WRAP_ID_v2, context, is, prop); + this.privacy = prop.getPrivacy(); + } + + /** + * Obtains the application data that was transmitted in this + * WrapToken. + * @return a byte array containing the application data + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + public byte[] getData() throws GSSException { + + byte[] temp = new byte[tokenDataLen]; + int len = getData(temp, 0); + return Arrays.copyOf(temp, len); + } + + /** + * Obtains the application data that was transmitted in this + * WrapToken, writing it into an application provided output + * array. + * @param dataBuf the output buffer into which the data must be + * written + * @param dataBufOffset the offset at which to write the data + * @return the size of the data written + * @throws GSSException if an error occurs while decrypting any + * cipher text and checking for validity + */ + public int getData(byte[] dataBuf, int dataBufOffset) + throws GSSException { + + // debug("WrapToken cons: data is token is [" + + // getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n"); + + // Do decryption if this token was privacy protected. + if (privacy) { + + // decrypt data + cipherHelper.decryptData(this, tokenData, 0, tokenDataLen, + dataBuf, dataBufOffset, getKeyUsage()); + + return tokenDataLen - CONFOUNDER_SIZE - + TOKEN_HEADER_SIZE - cipherHelper.getChecksumLength(); + } else { + + // Token data is in cleartext + // debug("\t\tNo encryption was performed by peer.\n"); + + // data + int data_length = tokenDataLen - cipherHelper.getChecksumLength(); + System.arraycopy(tokenData, 0, + dataBuf, dataBufOffset, + data_length); + // debug("\t\tData is: " + getHexBytes(dataBuf, data_length)); + + /* + * Make sure checksum is not corrupt + */ + if (!verifySign(dataBuf, dataBufOffset, data_length)) { + throw new GSSException(GSSException.BAD_MIC, -1, + "Corrupt checksum in Wrap token"); + } + return data_length; + } + } + + /** + * Writes a WrapToken_v2 object + */ + public WrapToken_v2(Krb5Context context, MessageProp prop, + byte[] dataBytes, int dataOffset, int dataLen) + throws GSSException { + + super(Krb5Token.WRAP_ID_v2, context); + + confounder = Confounder.bytes(CONFOUNDER_SIZE); + + // debug("\nWrapToken cons: data to wrap is [" + + // getHexBytes(confounder) + " " + + // getHexBytes(dataBytes, dataOffset, dataLen) + "]\n"); + + genSignAndSeqNumber(prop, dataBytes, dataOffset, dataLen); + + /* + * If the application decides to ask for privacy when the context + * did not negotiate for it, do not provide it. The peer might not + * have support for it. The app will realize this with a call to + * pop.getPrivacy() after wrap(). + */ + if (!context.getConfState()) + prop.setPrivacy(false); + + privacy = prop.getPrivacy(); + + if (!privacy) { + // Wrap Tokens (without confidentiality) = + // { 16 byte token_header | plaintext | 12-byte HMAC } + // where HMAC is on { plaintext | token_header } + + tokenData = new byte[dataLen + checksum.length]; + System.arraycopy(dataBytes, dataOffset, tokenData, 0, dataLen); + System.arraycopy(checksum, 0, tokenData, dataLen, checksum.length); + } else { + // Wrap Tokens (with confidentiality) = + // { 16 byte token_header | + // Encrypt(16-byte confounder | plaintext | token_header) | + // 12-byte HMAC } + + tokenData = cipherHelper.encryptData(this, confounder, getTokenHeader(), + dataBytes, dataOffset, dataLen, getKeyUsage()); + } + } + + public void encode(OutputStream os) throws IOException { + encodeHeader(os); + os.write(tokenData); + } + + public byte[] encode() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream( + MessageToken_v2.TOKEN_HEADER_SIZE + tokenData.length); + encode(bos); + return bos.toByteArray(); + } + + public int encode(byte[] outToken, int offset) throws IOException { + byte[] token = encode(); + System.arraycopy(token, 0, outToken, offset, token.length); + return token.length; + } + + // This implementation is way to conservative. And it certainly + // doesn't return the maximum limit. + static int getSizeLimit(int qop, boolean confReq, int maxTokenSize, + CipherHelper ch) throws GSSException { + return (GSSHeader.getMaxMechTokenSize(OID, maxTokenSize) - + (TOKEN_HEADER_SIZE + ch.getChecksumLength() + CONFOUNDER_SIZE) + - 8 /* safety */); + } +} diff --git a/src/sun/security/jgss/spi/GSSContextSpi.java b/src/sun/security/jgss/spi/GSSContextSpi.java new file mode 100644 index 00000000..1a68a445 --- /dev/null +++ b/src/sun/security/jgss/spi/GSSContextSpi.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ +package sun.security.jgss.spi; + +import org.ietf.jgss.*; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Provider; +import com.sun.security.jgss.*; + +/** + * This interface is implemented by a mechanism specific instance of a GSS + * security context. + * A GSSContextSpi object can be thought of having 3 states: + * -before initialization + * -during initialization with its peer + * -after it is established + *

    + * The context options can only be requested in state 1. In state 3, + * the per message operations are available to the callers. The get + * methods for the context options will return the requested options + * while in state 1 and 2, and the established values in state 3. + * Some mechanisms may allow the access to the per-message operations + * and the context flags before the context is fully established. The + * isProtReady method is used to indicate that these services are + * available. + *

    + * + * Context establishment tokens are defined in a mechanism independent + * format in section 3.1 of RFC 2743. The GSS-Framework will add + * and remove the mechanism independent header portion of this token format + * depending on whether a token is received or is being sent. The mechanism + * should only generate or expect to read the inner-context token portion.. + *

    + * On the other hands, tokens used for per-message calls are generated + * entirely by the mechanism. It is possible that the mechanism chooses to + * encase inner-level per-message tokens in a header similar to that used + * for initial tokens, however, this is upto the mechanism to do. The token + * to/from the per-message calls are opaque to the GSS-Framework. + * + *

    + * An attempt has been made to allow for reading the peer's tokens from an + * InputStream and writing tokens for the peer to an OutputStream. This + * allows applications to pass in streams that are obtained from their network + * connections and thus minimize the buffer copies that will happen. This + * is especially important for tokens generated by wrap() which are + * proportional in size to the length of the application data being + * wrapped, and are probably also the most frequently used type of tokens. + *

    + * It is anticipated that most applications will want to use wrap() in a + * fashion where they obtain the application bytes to wrap from a byte[] + * but want to output the wrap token straight to an + * OutputStream. Similarly, they will want to use unwrap() where they read + * the token directly form an InputStream but output it to some byte[] for + * the application to process. Unfortunately the high level GSS bindings + * do not contain overloaded forms of wrap() and unwrap() that do just + * this, however we have accomodated those cases here with the expectation + * that this will be rolled into the high level bindings sooner or later. + * + * @author Mayank Upadhyay + */ + +public interface GSSContextSpi { + + public Provider getProvider(); + + // The specification for the following methods mirrors the + // specification of the same methods in the GSSContext interface, as + // defined in RFC 2853. + + public void requestLifetime(int lifetime) throws GSSException; + + public void requestMutualAuth(boolean state) throws GSSException; + + public void requestReplayDet(boolean state) throws GSSException; + + public void requestSequenceDet(boolean state) throws GSSException; + + public void requestCredDeleg(boolean state) throws GSSException; + + public void requestAnonymity(boolean state) throws GSSException; + + public void requestConf(boolean state) throws GSSException; + + public void requestInteg(boolean state) throws GSSException; + + public void requestDelegPolicy(boolean state) throws GSSException; + + public void setChannelBinding(ChannelBinding cb) throws GSSException; + + public boolean getCredDelegState(); + + public boolean getMutualAuthState(); + + public boolean getReplayDetState(); + + public boolean getSequenceDetState(); + + public boolean getAnonymityState(); + + public boolean getDelegPolicyState(); + + public boolean isTransferable() throws GSSException; + + public boolean isProtReady(); + + public boolean isInitiator(); + + public boolean getConfState(); + + public boolean getIntegState(); + + public int getLifetime(); + + public boolean isEstablished(); + + public GSSNameSpi getSrcName() throws GSSException; + + public GSSNameSpi getTargName() throws GSSException; + + public Oid getMech() throws GSSException; + + public GSSCredentialSpi getDelegCred() throws GSSException; + + /** + * Initiator context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + *

    + * This method is called by the GSS-Framework when the application + * calls the initSecContext method on the GSSContext implementation + * that it has a reference to. + *

    + * All overloaded forms of GSSContext.initSecContext() can be handled + * with this mechanism level initSecContext. Since the output token + * from this method is a fixed size, not exeedingly large, and a one + * time deal, an overloaded form that takes an OutputStream has not + * been defined. The GSS-Framwork can write the returned byte[] to any + * application provided OutputStream. Similarly, any application input + * int he form of byte arrays will be wrapped in an input stream by the + * GSS-Framework and then passed here. + *

    + * + * The GSS-Framework will strip off the leading mechanism independent + * GSS-API header. In other words, only the mechanism specific + * inner-context token of RFC 2743 section 3.1 will be available on the + * InputStream. + * + * + * @param is contains the inner context token portion of the GSS token + * received from the peer. On the first call to initSecContext, there + * will be no token hence it will be ignored. + * @param mechTokenSize the size of the inner context token as read by + * the GSS-Framework from the mechanism independent GSS-API level + * header. + * @return any inner-context token required to be sent to the peer as + * part of a GSS token. The mechanism should not add the mechanism + * independent part of the token. The GSS-Framework will add that on + * the way out. + * @exception GSSException may be thrown + */ + public byte[] initSecContext(InputStream is, int mechTokenSize) + throws GSSException; + + /** + * Acceptor's context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + *

    + * This method is called by the GSS-Framework when the application + * calls the acceptSecContext method on the GSSContext implementation + * that it has a reference to. + *

    + * All overloaded forms of GSSContext.acceptSecContext() can be handled + * with this mechanism level acceptSecContext. Since the output token + * from this method is a fixed size, not exeedingly large, and a one + * time deal, an overloaded form that takes an OutputStream has not + * been defined. The GSS-Framwork can write the returned byte[] to any + * application provided OutputStream. Similarly, any application input + * int he form of byte arrays will be wrapped in an input stream by the + * GSS-Framework and then passed here. + *

    + * + * The GSS-Framework will strip off the leading mechanism independent + * GSS-API header. In other words, only the mechanism specific + * inner-context token of RFC 2743 section 3.1 will be available on the + * InputStream. + * + * + * @param is contains the inner context token portion of the GSS token + * received from the peer. + * @param mechTokenSize the size of the inner context token as read by + * the GSS-Framework from the mechanism independent GSS-API level + * header. + * @return any inner-context token required to be sent to the peer as + * part of a GSS token. The mechanism should not add the mechanism + * independent part of the token. The GSS-Framework will add that on + * the way out. + * @exception GSSException may be thrown + */ + public byte[] acceptSecContext(InputStream is, int mechTokenSize) + throws GSSException; + + /** + * Queries the context for largest data size to accommodate + * the specified protection and for the token to remain less then + * maxTokSize. + * + * @param qop the quality of protection that the context will be + * asked to provide. + * @param confReq a flag indicating whether confidentiality will be + * requested or not + * @param outputSize the maximum size of the output token + * @return the maximum size for the input message that can be + * provided to the wrap() method in order to guarantee that these + * requirements are met. + * @exception GSSException may be thrown + */ + public int getWrapSizeLimit(int qop, boolean confReq, int maxTokSize) + throws GSSException; + + /** + * Provides per-message token encapsulation. + * + * @param is the user-provided message to be protected + * @param os the token to be sent to the peer. It includes + * the message from is with the requested protection. + * @param msgPro on input it contains the requested qop and + * confidentiality state, on output, the applied values + * @exception GSSException may be thrown + * @see unwrap + */ + public void wrap(InputStream is, OutputStream os, MessageProp msgProp) + throws GSSException; + + /** + * For apps that want simplicity and don't care about buffer copies. + */ + public byte[] wrap(byte inBuf[], int offset, int len, + MessageProp msgProp) throws GSSException; + + /** + * For apps that care about buffer copies but either cannot use streams + * or want to avoid them for whatever reason. (Say, they are using + * block ciphers.) + * + * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext + * + public int wrap(byte inBuf[], int inOffset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException; + + */ + + /** + * For apps that want to read from a specific application provided + * buffer but want to write directly to the network stream. + */ + /* + * Can be achieved by converting the input buffer to a + * ByteInputStream. Provided to keep the API consistent + * with unwrap. + * + * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext + * + public void wrap(byte inBuf[], int offset, int len, + OutputStream os, MessageProp msgProp) + throws GSSException; + */ + + /** + * Retrieves the message token previously encapsulated in the wrap + * call. + * + * @param is the token from the peer + * @param os unprotected message data + * @param msgProp will contain the applied qop and confidentiality + * of the input token and any informatory status values + * @exception GSSException may be thrown + * @see wrap + */ + public void unwrap(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException; + + /** + * For apps that want simplicity and don't care about buffer copies. + */ + public byte[] unwrap(byte inBuf[], int offset, int len, + MessageProp msgProp) throws GSSException; + + /** + * For apps that care about buffer copies but either cannot use streams + * or want to avoid them for whatever reason. (Say, they are using + * block ciphers.) + * + * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext + * + public int unwrap(byte inBuf[], int inOffset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException; + + */ + + /** + * For apps that care about buffer copies and want to read + * straight from the network, but also want the output in a specific + * application provided buffer, say to reduce buffer allocation or + * subsequent copy. + * + * NOTE: This method is not defined in public class org.ietf.jgss.GSSContext + * + public int unwrap(InputStream is, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException; + */ + + /** + * Applies per-message integrity services. + * + * @param is the user-provided message + * @param os the token to be sent to the peer along with the + * message token. The message token is not encapsulated. + * @param msgProp on input the desired QOP and output the applied QOP + * @exception GSSException + */ + public void getMIC(InputStream is, OutputStream os, + MessageProp msgProp) + throws GSSException; + + public byte[] getMIC(byte []inMsg, int offset, int len, + MessageProp msgProp) throws GSSException; + + /** + * Checks the integrity of the supplied tokens. + * This token was previously generated by getMIC. + * + * @param is token generated by getMIC + * @param msgStr the message to check integrity for + * @param msgProp will contain the applied QOP and confidentiality + * states of the token as well as any informatory status codes + * @exception GSSException may be thrown + */ + public void verifyMIC(InputStream is, InputStream msgStr, + MessageProp mProp) throws GSSException; + + public void verifyMIC(byte []inTok, int tokOffset, int tokLen, + byte[] inMsg, int msgOffset, int msgLen, + MessageProp msgProp) throws GSSException; + + /** + * Produces a token representing this context. After this call + * the context will no longer be usable until an import is + * performed on the returned token. + * + * @return exported context token + * @exception GSSException may be thrown + */ + public byte[] export() throws GSSException; + + /** + * Releases context resources and terminates the + * context between 2 peer. + * + * @exception GSSException may be thrown + */ + public void dispose() throws GSSException; + + /** + * Return the mechanism-specific attribute associated with (@code type}. + * + * @param type the type of the attribute requested + * @return the attribute + * @throws GSSException see {@link ExtendedGSSContext#inquireSecContext} + * for details + */ + public Object inquireSecContext(InquireType type) + throws GSSException; +} diff --git a/src/sun/security/jgss/spi/GSSCredentialSpi.java b/src/sun/security/jgss/spi/GSSCredentialSpi.java new file mode 100644 index 00000000..69646d7a --- /dev/null +++ b/src/sun/security/jgss/spi/GSSCredentialSpi.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spi; + +import org.ietf.jgss.*; +import java.security.Provider; + +/** + * This interface is implemented by a mechanism specific credential + * element. A GSSCredential is conceptually a container class of several + * credential elements from different mechanisms. + * + * @author Mayank Upadhyay + */ +public interface GSSCredentialSpi { + + public Provider getProvider(); + + /** + * Called to invalidate this credential element and release + * any system recourses and cryptographic information owned + * by the credential. + * + * @exception GSSException with major codes NO_CRED and FAILURE + */ + public void dispose() throws GSSException; + + /** + * Returns the principal name for this credential. The name + * is in mechanism specific format. + * + * @return GSSNameSpi representing principal name of this credential + * @exception GSSException may be thrown + */ + public GSSNameSpi getName() throws GSSException; + + /** + * Returns the init lifetime remaining. + * + * @return the init lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getInitLifetime() throws GSSException; + + + /** + * Returns the accept lifetime remaining. + * + * @return the accept lifetime remaining in seconds + * @exception GSSException may be thrown + */ + public int getAcceptLifetime() throws GSSException; + + /** + * Determines if this credential element can be used by a context + * initiator. + * @return true if it can be used for initiating contexts + */ + public boolean isInitiatorCredential() throws GSSException; + + /** + * Determines if this credential element can be used by a context + * acceptor. + * @return true if it can be used for accepting contexts + */ + public boolean isAcceptorCredential() throws GSSException; + + /** + * Returns the oid representing the underlying credential + * mechanism oid. + * + * @return the Oid for this credential mechanism + * @exception GSSException may be thrown + */ + public Oid getMechanism(); + + /** + * Impersonates another client. + * + * @param name the client to impersonate + * @return the new credential + * @exception GSSException may be thrown + */ + public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException; +} diff --git a/src/sun/security/jgss/spi/GSSNameSpi.java b/src/sun/security/jgss/spi/GSSNameSpi.java new file mode 100644 index 00000000..61fc2074 --- /dev/null +++ b/src/sun/security/jgss/spi/GSSNameSpi.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spi; + +import org.ietf.jgss.*; +import java.security.Provider; + +/** + * This interface is implemented by a mechanism specific name element. A + * GSSName is conceptually a container class of several name elements from + * different mechanisms. + * + * @author Mayank Upadhyay + */ + +public interface GSSNameSpi { + + public Provider getProvider(); + + /** + * Equals method for the GSSNameSpi objects. + * If either name denotes an anonymous principal, the call should + * return false. + * + * @param name to be compared with + * @returns true if they both refer to the same entity, else false + * @exception GSSException with major codes of BAD_NAMETYPE, + * BAD_NAME, FAILURE + */ + public boolean equals(GSSNameSpi name) throws GSSException; + + /** + * Compares this GSSNameSpi object to another Object + * that might be a GSSNameSpi. The behaviour is exactly + * the same as in {@link #equals(GSSNameSpi) equals} except that + * no GSSException is thrown; instead, false will be returned in the + * situation where an error occurs. + * + * @param another the object to be compared to + * @returns true if they both refer to the same entity, else false + * @see #equals(GSSNameSpi) + */ + public boolean equals(Object another); + + /** + * Returns a hashcode value for this GSSNameSpi. + * + * @return a hashCode value + */ + public int hashCode(); + + /** + * Returns a flat name representation for this object. The name + * format is defined in RFC 2078. + * + * @return the flat name representation for this object + * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME, + * BAD_NAME, FAILURE. + */ + public byte[] export() throws GSSException; + + + /** + * Get the mechanism type that this NameElement corresponds to. + * + * @return the Oid of the mechanism type + */ + public Oid getMechanism(); + + /** + * Returns a string representation for this name. The printed + * name type can be obtained by calling getStringNameType(). + * + * @return string form of this name + * @see #getStringNameType() + * @overrides Object#toString + */ + public String toString(); + + + /** + * Returns the oid describing the format of the printable name. + * + * @return the Oid for the format of the printed name + */ + public Oid getStringNameType(); + + /** + * Indicates if this name object represents an Anonymous name. + */ + public boolean isAnonymousName(); +} diff --git a/src/sun/security/jgss/spi/MechanismFactory.java b/src/sun/security/jgss/spi/MechanismFactory.java new file mode 100644 index 00000000..6b6f5ab6 --- /dev/null +++ b/src/sun/security/jgss/spi/MechanismFactory.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spi; + +import org.ietf.jgss.*; +import java.security.Provider; + +/** + * This interface is implemented by the factory class for every + * plugin mechanism. The GSSManager locates an implementation of this + * interface by querying the security providers installed on the + * system. For a provider to support a mechanism defined by Oid x.y.z, + * the provider master file would have to contain a mapping from the + * property "GssApiMechanism.x.y.z" to an implementation class that serves + * as the factory for that mechanism. + *

    + * e.g., If a provider master file contained the a mapping from the + * property "GssApiMechanism.1.2.840.113554.1.2.2" to the class name + * "com.foo.krb5.Krb5GssFactory", then the GSS-API framework would assume + * that com.foo.krb5.Krb5GssFactory implements the MechanismFactory + * interface and that it can be used to obtain elements required by for + * supporting this mechanism. + * + * @author Mayank Upadhyay + */ + +public interface MechanismFactory { + + /** + * Returns the Oid of the mechanism that this factory supports. + * @return the Oid + */ + public Oid getMechanismOid(); + + /** + * Returns the provider that this factory came from. + * @return the provider + */ + public Provider getProvider(); + + /** + * Returns the GSS-API nametypes that this mechanism can + * support. Having this method helps the GSS-Framework decide quickly + * if a certain mechanism can be skipped when importing a name. + * @return an array of the Oid's corresponding to the different GSS-API + * nametypes supported + * @see GSSName + */ + public Oid[] getNameTypes() throws GSSException; + + /** + * Creates a credential element for this mechanism to be included as + * part of a GSSCredential implementation. A GSSCredential is + * conceptually a container class of several credential elements from + * different mechanisms. A GSS-API credential can be used either for + * initiating GSS security contexts or for accepting them. This method + * also accepts parameters that indicate what usage is expected and how + * long the life of the credential should be. It is not necessary that + * the mechanism honor the request for lifetime. An application will + * always query an acquired GSSCredential to determine what lifetime it + * got back.

    + * + * Not all mechanisms support the concept of one credential element + * that can be used for both initiating and accepting a context. In the + * event that an application requests usage INITIATE_AND_ACCEPT for a + * credential from such a mechanism, the GSS framework will need to + * obtain two different credential elements from the mechanism, one + * that will have usage INITIATE_ONLY and another that will have usage + * ACCEPT_ONLY. The mechanism will help the GSS-API realize this by + * returning a credential element with usage INITIATE_ONLY or + * ACCEPT_ONLY prompting it to make another call to + * getCredentialElement, this time with the other usage mode. The + * mechanism indicates the missing mode by returning a 0 lifetime for + * it. + * + * @param name the mechanism level name element for the entity whose + * credential is desired. A null value indicates that a mechanism + * dependent default choice is to be made. + * @param initLifetime indicates the lifetime (in seconds) that is + * requested for this credential to be used at the context initiator's + * end. This value should be ignored if the usage is + * ACCEPT_ONLY. Predefined contants are available in the + * org.ietf.jgss.GSSCredential interface. + * @param acceptLifetime indicates the lifetime (in seconds) that is + * requested for this credential to be used at the context acceptor's + * end. This value should be ignored if the usage is + * INITIATE_ONLY. Predefined contants are available in the + * org.ietf.jgss.GSSCredential interface. + * @param usage One of the values GSSCredential.INIATE_ONLY, + * GSSCredential.ACCEPT_ONLY, and GSSCredential.INITIATE_AND_ACCEPT. + * @see GSSCredential + * @throws GSSException if one of the error situations described in RFC + * 2743 with the GSS_Acquire_Cred or GSS_Add_Cred calls occurs. + */ + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + int initLifetime, int acceptLifetime, int usage) throws GSSException; + + /** + * Creates a name element for this mechanism to be included as part of + * a GSSName implementation. A GSSName is conceptually a container + * class of several name elements from different mechanisms. A GSSName + * can be created either with a String or with a sequence of + * bytes. This factory method accepts the name in a String. Such a name + * can generally be assumed to be printable and may be returned from + * the name element's toString() method. + * + * @param nameStr a string containing the characters describing this + * entity to the mechanism + * @param nameType an Oid serving as a clue as to how the mechanism should + * interpret the nameStr + * @throws GSSException if any of the errors described in RFC 2743 for + * the GSS_Import_Name or GSS_Canonicalize_Name calls occur. + */ + public GSSNameSpi getNameElement(String nameStr, Oid nameType) + throws GSSException; + + /** + * This is a variation of the factory method that accepts a String for + * the characters that make up the name. Usually the String characters + * are assumed to be printable. The bytes passed in to this method have + * to be converted to characters using some encoding of the mechanism's + * choice. It is recommended that UTF-8 be used. (Note that UTF-8 + * preserves the encoding for 7-bit ASCII characters.) + *

    + * An exported name will generally be passed in using this method. + * + * @param nameBytes the bytes describing this entity to the mechanism + * @param nameType an Oid serving as a clue as to how the mechanism should + * interpret the nameStr + * @throws GSSException if any of the errors described in RFC 2743 for + * the GSS_Import_Name or GSS_Canonicalize_Name calls occur. + */ + public GSSNameSpi getNameElement(byte[] name, Oid nameType) + throws GSSException; + + /** + * Creates a security context for this mechanism so that it can be used + * on the context initiator's side. + * + * @param peer the name element from this mechanism that represents the + * peer + * @param myInitiatorCred a credential element for the context + * initiator obtained previously from this mechanism. The identity of + * the context initiator can be obtained from this credential. Passing + * a value of null here indicates that a default entity of the + * mechanism's choice should be assumed to be the context initiator and + * that default credentials should be applied. + * @param lifetime the requested lifetime (in seconds) for the security + * context. Predefined contants are available in the + * org.ietf.jgss.GSSContext interface. + * @throws GSSException if any of the errors described in RFC 2743 in + * the GSS_Init_Sec_Context call occur. + */ + public GSSContextSpi getMechanismContext(GSSNameSpi peer, + GSSCredentialSpi myInitiatorCred, + int lifetime) throws GSSException; + + /** + * Creates a security context for this mechanism so thatit can be used + * on the context acceptor's side. + * + * @param myAcceptorCred a credential element for the context acceptor + * obtained previously from this mechanism. The identity of the context + * acceptor cna be obtained from this credential. Passing a value of + * null here indicates that tha default entity of the mechanism's + * choice should be assumed to be the context acceptor and default + * credentials should be applied. + * + * @throws GSSException if any of the errors described in RFC 2743 in + * the GSS_Accept_Sec_Context call occur. + */ + public GSSContextSpi getMechanismContext(GSSCredentialSpi myAcceptorCred) + throws GSSException; + + /** + * Creates a security context from a previously exported (serialized) + * security context. Note that this is different from Java + * serialization and is defined at a mechanism level to interoperate + * over the wire with non-Java implementations. Either the initiator or + * the acceptor can export and then import a security context. + * Implementations of mechanism contexts are not required to implement + * exporting and importing. + * + * @param exportedContext the bytes representing this security context + * @throws GSSException is any of the errors described in RFC 2743 in + * the GSS_Import_Sec_Context call occur. + */ + public GSSContextSpi getMechanismContext(byte[] exportedContext) + throws GSSException; + +} diff --git a/src/sun/security/jgss/spnego/NegTokenInit.java b/src/sun/security/jgss/spnego/NegTokenInit.java new file mode 100644 index 00000000..9881a493 --- /dev/null +++ b/src/sun/security/jgss/spnego/NegTokenInit.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spnego; + +import java.io.*; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import sun.security.util.*; + +/** + * Implements the SPNEGO NegTokenInit token + * as specified in RFC 2478 + * + * NegTokenInit ::= SEQUENCE { + * mechTypes [0] MechTypeList OPTIONAL, + * reqFlags [1] ContextFlags OPTIONAL, + * mechToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL + * } + * + * MechTypeList ::= SEQUENCE OF MechType + * + * MechType::= OBJECT IDENTIFIER + * + * ContextFlags ::= BIT STRING { + * delegFlag (0), + * mutualFlag (1), + * replayFlag (2), + * sequenceFlag (3), + * anonFlag (4), + * confFlag (5), + * integFlag (6) + * } + * + * @author Seema Malkani + * @since 1.6 + */ + +public class NegTokenInit extends SpNegoToken { + + // DER-encoded mechTypes + private byte[] mechTypes = null; + private Oid[] mechTypeList = null; + + private BitArray reqFlags = null; + private byte[] mechToken = null; + private byte[] mechListMIC = null; + + NegTokenInit(byte[] mechTypes, BitArray flags, + byte[] token, byte[] mechListMIC) + { + super(NEG_TOKEN_INIT_ID); + this.mechTypes = mechTypes; + this.reqFlags = flags; + this.mechToken = token; + this.mechListMIC = mechListMIC; + } + + // Used by sun.security.jgss.wrapper.NativeGSSContext + // to parse SPNEGO tokens + public NegTokenInit(byte[] in) throws GSSException { + super(NEG_TOKEN_INIT_ID); + parseToken(in); + } + + final byte[] encode() throws GSSException { + try { + // create negInitToken + DerOutputStream initToken = new DerOutputStream(); + + // DER-encoded mechTypes with CONTEXT 00 + if (mechTypes != null) { + initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), mechTypes); + } + + // write context flags with CONTEXT 01 + if (reqFlags != null) { + DerOutputStream flags = new DerOutputStream(); + flags.putUnalignedBitString(reqFlags); + initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), flags); + } + + // mechToken with CONTEXT 02 + if (mechToken != null) { + DerOutputStream dataValue = new DerOutputStream(); + dataValue.putOctetString(mechToken); + initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), dataValue); + } + + // mechListMIC with CONTEXT 03 + if (mechListMIC != null) { + if (DEBUG) { + System.out.println("SpNegoToken NegTokenInit: " + + "sending MechListMIC"); + } + DerOutputStream mic = new DerOutputStream(); + mic.putOctetString(mechListMIC); + initToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), mic); + } + + // insert in a SEQUENCE + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, initToken); + + return out.toByteArray(); + + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid SPNEGO NegTokenInit token : " + e.getMessage()); + } + } + + private void parseToken(byte[] in) throws GSSException { + try { + DerValue der = new DerValue(in); + // verify NegotiationToken type token + if (!der.isContextSpecific((byte) NEG_TOKEN_INIT_ID)) { + throw new IOException("SPNEGO NegoTokenInit : " + + "did not have right token type"); + } + DerValue tmp1 = der.data.getDerValue(); + if (tmp1.tag != DerValue.tag_Sequence) { + throw new IOException("SPNEGO NegoTokenInit : " + + "did not have the Sequence tag"); + } + + // parse various fields if present + int lastField = -1; + while (tmp1.data.available() > 0) { + DerValue tmp2 = tmp1.data.getDerValue(); + if (tmp2.isContextSpecific((byte)0x00)) { + // get the DER-encoded sequence of mechTypes + lastField = checkNextField(lastField, 0); + DerInputStream mValue = tmp2.data; + mechTypes = mValue.toByteArray(); + + // read all the mechTypes + DerValue[] mList = mValue.getSequence(0); + mechTypeList = new Oid[mList.length]; + ObjectIdentifier mech = null; + for (int i = 0; i < mList.length; i++) { + mech = mList[i].getOID(); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenInit: " + + "reading Mechanism Oid = " + mech); + } + mechTypeList[i] = new Oid(mech.toString()); + } + } else if (tmp2.isContextSpecific((byte)0x01)) { + lastField = checkNextField(lastField, 1); + // received reqFlags, skip it + } else if (tmp2.isContextSpecific((byte)0x02)) { + lastField = checkNextField(lastField, 2); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenInit: " + + "reading Mech Token"); + } + mechToken = tmp2.data.getOctetString(); + } else if (tmp2.isContextSpecific((byte)0x03)) { + lastField = checkNextField(lastField, 3); + if (!GSSUtil.useMSInterop()) { + mechListMIC = tmp2.data.getOctetString(); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenInit: " + + "MechListMIC Token = " + + getHexBytes(mechListMIC)); + } + } + } + } + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid SPNEGO NegTokenInit token : " + e.getMessage()); + } + } + + byte[] getMechTypes() { + return mechTypes; + } + + // Used by sun.security.jgss.wrapper.NativeGSSContext + // to find the mechs in SPNEGO tokens + public Oid[] getMechTypeList() { + return mechTypeList; + } + + BitArray getReqFlags() { + return reqFlags; + } + + // Used by sun.security.jgss.wrapper.NativeGSSContext + // to access the mech token portion of SPNEGO tokens + public byte[] getMechToken() { + return mechToken; + } + + byte[] getMechListMIC() { + return mechListMIC; + } + +} diff --git a/src/sun/security/jgss/spnego/NegTokenTarg.java b/src/sun/security/jgss/spnego/NegTokenTarg.java new file mode 100644 index 00000000..1435f52a --- /dev/null +++ b/src/sun/security/jgss/spnego/NegTokenTarg.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spnego; + +import java.io.*; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import sun.security.util.*; + +/** + * Implements the SPNEGO NegTokenTarg token + * as specified in RFC 2478 + * + * NegTokenTarg ::= SEQUENCE { + * negResult [0] ENUMERATED { + * accept_completed (0), + * accept_incomplete (1), + * reject (2) } OPTIONAL, + * supportedMech [1] MechType OPTIONAL, + * responseToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL + * } + * + * MechType::= OBJECT IDENTIFIER + * + * + * @author Seema Malkani + * @since 1.6 + */ + +public class NegTokenTarg extends SpNegoToken { + + private int negResult = 0; + private Oid supportedMech = null; + private byte[] responseToken = null; + private byte[] mechListMIC = null; + + NegTokenTarg(int result, Oid mech, byte[] token, byte[] mechListMIC) + { + super(NEG_TOKEN_TARG_ID); + this.negResult = result; + this.supportedMech = mech; + this.responseToken = token; + this.mechListMIC = mechListMIC; + } + + // Used by sun.security.jgss.wrapper.NativeGSSContext + // to parse SPNEGO tokens + public NegTokenTarg(byte[] in) throws GSSException { + super(NEG_TOKEN_TARG_ID); + parseToken(in); + } + + final byte[] encode() throws GSSException { + try { + // create negTargToken + DerOutputStream targToken = new DerOutputStream(); + + // write the negotiated result with CONTEXT 00 + DerOutputStream result = new DerOutputStream(); + result.putEnumerated(negResult); + targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), result); + + // supportedMech with CONTEXT 01 + if (supportedMech != null) { + DerOutputStream mech = new DerOutputStream(); + byte[] mechType = supportedMech.getDER(); + mech.write(mechType); + targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), mech); + } + + // response Token with CONTEXT 02 + if (responseToken != null) { + DerOutputStream rspToken = new DerOutputStream(); + rspToken.putOctetString(responseToken); + targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), rspToken); + } + + // mechListMIC with CONTEXT 03 + if (mechListMIC != null) { + if (DEBUG) { + System.out.println("SpNegoToken NegTokenTarg: " + + "sending MechListMIC"); + } + DerOutputStream mic = new DerOutputStream(); + mic.putOctetString(mechListMIC); + targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), mic); + } else if (GSSUtil.useMSInterop()) { + // required for MS-interoperability + if (responseToken != null) { + if (DEBUG) { + System.out.println("SpNegoToken NegTokenTarg: " + + "sending additional token for MS Interop"); + } + DerOutputStream rspToken = new DerOutputStream(); + rspToken.putOctetString(responseToken); + targToken.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), rspToken); + } + } + + // insert in a SEQUENCE + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, targToken); + + return out.toByteArray(); + + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid SPNEGO NegTokenTarg token : " + e.getMessage()); + } + } + + private void parseToken(byte[] in) throws GSSException { + try { + DerValue der = new DerValue(in); + // verify NegotiationToken type token + if (!der.isContextSpecific((byte) NEG_TOKEN_TARG_ID)) { + throw new IOException("SPNEGO NegoTokenTarg : " + + "did not have the right token type"); + } + DerValue tmp1 = der.data.getDerValue(); + if (tmp1.tag != DerValue.tag_Sequence) { + throw new IOException("SPNEGO NegoTokenTarg : " + + "did not have the Sequence tag"); + } + + // parse various fields if present + int lastField = -1; + while (tmp1.data.available() > 0) { + DerValue tmp2 = tmp1.data.getDerValue(); + if (tmp2.isContextSpecific((byte)0x00)) { + lastField = checkNextField(lastField, 0); + negResult = tmp2.data.getEnumerated(); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenTarg: negotiated" + + " result = " + getNegoResultString(negResult)); + } + } else if (tmp2.isContextSpecific((byte)0x01)) { + lastField = checkNextField(lastField, 1); + ObjectIdentifier mech = tmp2.data.getOID(); + supportedMech = new Oid(mech.toString()); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenTarg: " + + "supported mechanism = " + supportedMech); + } + } else if (tmp2.isContextSpecific((byte)0x02)) { + lastField = checkNextField(lastField, 2); + responseToken = tmp2.data.getOctetString(); + } else if (tmp2.isContextSpecific((byte)0x03)) { + lastField = checkNextField(lastField, 3); + if (!GSSUtil.useMSInterop()) { + mechListMIC = tmp2.data.getOctetString(); + if (DEBUG) { + System.out.println("SpNegoToken NegTokenTarg: " + + "MechListMIC Token = " + + getHexBytes(mechListMIC)); + } + } + } + } + } catch (IOException e) { + throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, + "Invalid SPNEGO NegTokenTarg token : " + e.getMessage()); + } + } + + int getNegotiatedResult() { + return negResult; + } + + // Used by sun.security.jgss.wrapper.NativeGSSContext + // to find the supported mech in SPNEGO tokens + public Oid getSupportedMech() { + return supportedMech; + } + + byte[] getResponseToken() { + return responseToken; + } + + byte[] getMechListMIC() { + return mechListMIC; + } +} diff --git a/src/sun/security/jgss/spnego/SpNegoContext.java b/src/sun/security/jgss/spnego/SpNegoContext.java new file mode 100644 index 00000000..23906395 --- /dev/null +++ b/src/sun/security/jgss/spnego/SpNegoContext.java @@ -0,0 +1,1234 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spnego; + +import com.sun.security.jgss.ExtendedGSSContext; +import com.sun.security.jgss.InquireType; +import java.io.*; +import java.security.Provider; +import org.ietf.jgss.*; +import sun.security.jgss.*; +import sun.security.jgss.spi.*; +import sun.security.util.*; + +/** + * Implements the mechanism specific context class for SPNEGO + * GSS-API mechanism + * + * @author Seema Malkani + * @since 1.6 + */ +public class SpNegoContext implements GSSContextSpi { + + /* + * The different states that this context can be in. + */ + private static final int STATE_NEW = 1; + private static final int STATE_IN_PROCESS = 2; + private static final int STATE_DONE = 3; + private static final int STATE_DELETED = 4; + + private int state = STATE_NEW; + + /* + * Optional features that the application can set and their default + * values. + */ + private boolean credDelegState = false; + private boolean mutualAuthState = true; + private boolean replayDetState = true; + private boolean sequenceDetState = true; + private boolean confState = true; + private boolean integState = true; + private boolean delegPolicyState = false; + + private GSSNameSpi peerName = null; + private GSSNameSpi myName = null; + private SpNegoCredElement myCred = null; + + private GSSContext mechContext = null; + private byte[] DER_mechTypes = null; + + private int lifetime; + private ChannelBinding channelBinding; + private boolean initiator; + + // the underlying negotiated mechanism + private Oid internal_mech = null; + + // the SpNegoMechFactory that creates this context + final private SpNegoMechFactory factory; + + // debug property + static final boolean DEBUG = + java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction + ("sun.security.spnego.debug")).booleanValue(); + + /** + * Constructor for SpNegoContext to be called on the context initiator's + * side. + */ + public SpNegoContext(SpNegoMechFactory factory, GSSNameSpi peerName, + GSSCredentialSpi myCred, + int lifetime) throws GSSException { + + if (peerName == null) + throw new IllegalArgumentException("Cannot have null peer name"); + if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) { + throw new IllegalArgumentException("Wrong cred element type"); + } + this.peerName = peerName; + this.myCred = (SpNegoCredElement) myCred; + this.lifetime = lifetime; + this.initiator = true; + this.factory = factory; + } + + /** + * Constructor for SpNegoContext to be called on the context acceptor's + * side. + */ + public SpNegoContext(SpNegoMechFactory factory, GSSCredentialSpi myCred) + throws GSSException { + if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) { + throw new IllegalArgumentException("Wrong cred element type"); + } + this.myCred = (SpNegoCredElement) myCred; + this.initiator = false; + this.factory = factory; + } + + /** + * Constructor for SpNegoContext to import a previously exported context. + */ + public SpNegoContext(SpNegoMechFactory factory, byte [] interProcessToken) + throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, + -1, "GSS Import Context not available"); + } + + /** + * Requests that confidentiality be available. + */ + public final void requestConf(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + confState = value; + } + + /** + * Is confidentiality available? + */ + public final boolean getConfState() { + return confState; + } + + /** + * Requests that integrity be available. + */ + public final void requestInteg(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + integState = value; + } + + /** + * Requests that deleg policy be respected. + */ + public final void requestDelegPolicy(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + delegPolicyState = value; + } + + /** + * Is integrity available? + */ + public final boolean getIntegState() { + return integState; + } + + /** + * Is deleg policy respected? + */ + public final boolean getDelegPolicyState() { + if (isInitiator() && mechContext != null && + mechContext instanceof ExtendedGSSContext && + (state == STATE_IN_PROCESS || state == STATE_DONE)) { + return ((ExtendedGSSContext)mechContext).getDelegPolicyState(); + } else { + return delegPolicyState; + } + } + + /** + * Requests that credential delegation be done during context + * establishment. + */ + public final void requestCredDeleg(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + credDelegState = value; + } + + /** + * Is credential delegation enabled? + */ + public final boolean getCredDelegState() { + if (isInitiator() && mechContext != null && + (state == STATE_IN_PROCESS || state == STATE_DONE)) { + return mechContext.getCredDelegState(); + } else { + return credDelegState; + } + } + + /** + * Requests that mutual authentication be done during context + * establishment. Since this is fromm the client's perspective, it + * essentially requests that the server be authenticated. + */ + public final void requestMutualAuth(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) { + mutualAuthState = value; + } + } + + /** + * Is mutual authentication enabled? Since this is from the client's + * perspective, it essentially meas that the server is being + * authenticated. + */ + public final boolean getMutualAuthState() { + return mutualAuthState; + } + + /** + * Returns the mechanism oid. + * + * @return the Oid of this context + */ + public final Oid getMech() { + if (isEstablished()) { + return getNegotiatedMech(); + } + return (SpNegoMechFactory.GSS_SPNEGO_MECH_OID); + } + + public final Oid getNegotiatedMech() { + return (internal_mech); + } + + public final Provider getProvider() { + return SpNegoMechFactory.PROVIDER; + } + + public final void dispose() throws GSSException { + mechContext = null; + state = STATE_DELETED; + } + + /** + * Tests if this is the initiator side of the context. + * + * @return boolean indicating if this is initiator (true) + * or target (false) + */ + public final boolean isInitiator() { + return initiator; + } + + /** + * Tests if the context can be used for per-message service. + * Context may allow the calls to the per-message service + * functions before being fully established. + * + * @return boolean indicating if per-message methods can + * be called. + */ + public final boolean isProtReady() { + return (state == STATE_DONE); + } + + /** + * Initiator context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + * + * @param is contains the token received from the peer. On the + * first call it will be ignored. + * @return any token required to be sent to the peer + * It is responsibility of the caller to send the token + * to its peer for processing. + * @exception GSSException + */ + public final byte[] initSecContext(InputStream is, int mechTokenSize) + throws GSSException { + + byte[] retVal = null; + NegTokenInit initToken = null; + byte[] mechToken = null; + int errorCode = GSSException.FAILURE; + + if (DEBUG) { + System.out.println("Entered SpNego.initSecContext with " + + "state=" + printState(state)); + } + if (!isInitiator()) { + throw new GSSException(GSSException.FAILURE, -1, + "initSecContext on an acceptor GSSContext"); + } + + try { + if (state == STATE_NEW) { + state = STATE_IN_PROCESS; + + errorCode = GSSException.NO_CRED; + + // determine available mech set + Oid[] mechList = getAvailableMechs(); + DER_mechTypes = getEncodedMechs(mechList); + + // pull out first mechanism + internal_mech = mechList[0]; + + // get the token for first mechanism + mechToken = GSS_initSecContext(null); + + errorCode = GSSException.DEFECTIVE_TOKEN; + // generate SPNEGO token + initToken = new NegTokenInit(DER_mechTypes, getContextFlags(), + mechToken, null); + if (DEBUG) { + System.out.println("SpNegoContext.initSecContext: " + + "sending token of type = " + + SpNegoToken.getTokenName(initToken.getType())); + } + // get the encoded token + retVal = initToken.getEncoded(); + + } else if (state == STATE_IN_PROCESS) { + + errorCode = GSSException.FAILURE; + if (is == null) { + throw new GSSException(errorCode, -1, + "No token received from peer!"); + } + + errorCode = GSSException.DEFECTIVE_TOKEN; + byte[] server_token = new byte[is.available()]; + SpNegoToken.readFully(is, server_token); + if (DEBUG) { + System.out.println("SpNegoContext.initSecContext: " + + "process received token = " + + SpNegoToken.getHexBytes(server_token)); + } + + // read the SPNEGO token + // token will be validated when parsing + NegTokenTarg targToken = new NegTokenTarg(server_token); + + if (DEBUG) { + System.out.println("SpNegoContext.initSecContext: " + + "received token of type = " + + SpNegoToken.getTokenName(targToken.getType())); + } + + // pull out mechanism + internal_mech = targToken.getSupportedMech(); + if (internal_mech == null) { + // return wth failure + throw new GSSException(errorCode, -1, + "supported mechanism from server is null"); + } + + // get the negotiated result + SpNegoToken.NegoResult negoResult = null; + int result = targToken.getNegotiatedResult(); + switch (result) { + case 0: + negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; + state = STATE_DONE; + break; + case 1: + negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; + state = STATE_IN_PROCESS; + break; + case 2: + negoResult = SpNegoToken.NegoResult.REJECT; + state = STATE_DELETED; + break; + default: + state = STATE_DONE; + break; + } + + errorCode = GSSException.BAD_MECH; + + if (negoResult == SpNegoToken.NegoResult.REJECT) { + throw new GSSException(errorCode, -1, + internal_mech.toString()); + } + + errorCode = GSSException.DEFECTIVE_TOKEN; + + if ((negoResult == SpNegoToken.NegoResult.ACCEPT_COMPLETE) || + (negoResult == SpNegoToken.NegoResult.ACCEPT_INCOMPLETE)) { + + // pull out the mechanism token + byte[] accept_token = targToken.getResponseToken(); + if (accept_token == null) { + if (!isMechContextEstablished()) { + // return with failure + throw new GSSException(errorCode, -1, + "mechanism token from server is null"); + } + } else { + mechToken = GSS_initSecContext(accept_token); + } + // verify MIC + if (!GSSUtil.useMSInterop()) { + byte[] micToken = targToken.getMechListMIC(); + if (!verifyMechListMIC(DER_mechTypes, micToken)) { + throw new GSSException(errorCode, -1, + "verification of MIC on MechList Failed!"); + } + } + if (isMechContextEstablished()) { + state = STATE_DONE; + retVal = mechToken; + if (DEBUG) { + System.out.println("SPNEGO Negotiated Mechanism = " + + internal_mech + " " + + GSSUtil.getMechStr(internal_mech)); + } + } else { + // generate SPNEGO token + initToken = new NegTokenInit(null, null, + mechToken, null); + if (DEBUG) { + System.out.println("SpNegoContext.initSecContext:" + + " continue sending token of type = " + + SpNegoToken.getTokenName(initToken.getType())); + } + // get the encoded token + retVal = initToken.getEncoded(); + } + } + + } else { + // XXX Use logging API + if (DEBUG) { + System.out.println(state); + } + } + if (DEBUG) { + if (retVal != null) { + System.out.println("SNegoContext.initSecContext: " + + "sending token = " + SpNegoToken.getHexBytes(retVal)); + } + } + } catch (GSSException e) { + GSSException gssException = + new GSSException(errorCode, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + + return retVal; + } + + + /** + * Acceptor's context establishment call. This method may be + * required to be called several times. A CONTINUE_NEEDED return + * call indicates that more calls are needed after the next token + * is received from the peer. + * + * @param is contains the token received from the peer. + * @return any token required to be sent to the peer + * It is responsibility of the caller to send the token + * to its peer for processing. + * @exception GSSException + */ + public final byte[] acceptSecContext(InputStream is, int mechTokenSize) + throws GSSException { + + byte[] retVal = null; + SpNegoToken.NegoResult negoResult; + boolean valid = true; + + if (DEBUG) { + System.out.println("Entered SpNegoContext.acceptSecContext with " + + "state=" + printState(state)); + } + + if (isInitiator()) { + throw new GSSException(GSSException.FAILURE, -1, + "acceptSecContext on an initiator " + + "GSSContext"); + } + try { + if (state == STATE_NEW) { + state = STATE_IN_PROCESS; + + // read data + byte[] token = new byte[is.available()]; + SpNegoToken.readFully(is, token); + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "receiving token = " + + SpNegoToken.getHexBytes(token)); + } + + // read the SPNEGO token + // token will be validated when parsing + NegTokenInit initToken = new NegTokenInit(token); + + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "received token of type = " + + SpNegoToken.getTokenName(initToken.getType())); + } + + Oid[] mechList = initToken.getMechTypeList(); + DER_mechTypes = initToken.getMechTypes(); + if (DER_mechTypes == null) { + valid = false; + } + + /* + * Select the best match between the list of mechs + * that the initiator requested and the list that + * the acceptor will support. + */ + Oid[] supported_mechSet = getAvailableMechs(); + Oid mech_wanted = + negotiate_mech_type(supported_mechSet, mechList); + if (mech_wanted == null) { + valid = false; + } + // save the desired mechanism + internal_mech = mech_wanted; + + // get the token for mechanism + byte[] accept_token; + + if (mechList[0].equals(mech_wanted)) { + // get the mechanism token + byte[] mechToken = initToken.getMechToken(); + if (mechToken == null) { + throw new GSSException(GSSException.FAILURE, -1, + "mechToken is missing"); + } + accept_token = GSS_acceptSecContext(mechToken); + } else { + accept_token = null; + } + + // verify MIC + if (!GSSUtil.useMSInterop() && valid) { + valid = verifyMechListMIC(DER_mechTypes, + initToken.getMechListMIC()); + } + + // determine negotiated result status + if (valid) { + if (isMechContextEstablished()) { + negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; + state = STATE_DONE; + // now set the context flags for acceptor + setContextFlags(); + // print the negotiated mech info + if (DEBUG) { + System.out.println("SPNEGO Negotiated Mechanism = " + + internal_mech + " " + + GSSUtil.getMechStr(internal_mech)); + } + } else { + negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; + state = STATE_IN_PROCESS; + } + } else { + negoResult = SpNegoToken.NegoResult.REJECT; + state = STATE_DONE; + } + + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "mechanism wanted = " + mech_wanted); + System.out.println("SpNegoContext.acceptSecContext: " + + "negotiated result = " + negoResult); + } + + // generate SPNEGO token + NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), + mech_wanted, accept_token, null); + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "sending token of type = " + + SpNegoToken.getTokenName(targToken.getType())); + } + // get the encoded token + retVal = targToken.getEncoded(); + + } else if (state == STATE_IN_PROCESS) { + // read data + byte[] token = new byte[is.available()]; + SpNegoToken.readFully(is, token); + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "receiving token = " + + SpNegoToken.getHexBytes(token)); + } + + // read the SPNEGO token + // token will be validated when parsing + NegTokenTarg inputToken = new NegTokenTarg(token); + + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "received token of type = " + + SpNegoToken.getTokenName(inputToken.getType())); + } + + // read the token + byte[] client_token = inputToken.getResponseToken(); + byte[] accept_token = GSS_acceptSecContext(client_token); + if (accept_token == null) { + valid = false; + } + + // determine negotiated result status + if (valid) { + if (isMechContextEstablished()) { + negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE; + state = STATE_DONE; + } else { + negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE; + state = STATE_IN_PROCESS; + } + } else { + negoResult = SpNegoToken.NegoResult.REJECT; + state = STATE_DONE; + } + + // generate SPNEGO token + NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(), + null, accept_token, null); + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "sending token of type = " + + SpNegoToken.getTokenName(targToken.getType())); + } + // get the encoded token + retVal = targToken.getEncoded(); + + } else { + // XXX Use logging API + if (DEBUG) { + System.out.println("AcceptSecContext: state = " + state); + } + } + if (DEBUG) { + System.out.println("SpNegoContext.acceptSecContext: " + + "sending token = " + SpNegoToken.getHexBytes(retVal)); + } + } catch (IOException e) { + GSSException gssException = + new GSSException(GSSException.FAILURE, -1, e.getMessage()); + gssException.initCause(e); + throw gssException; + } + + if (state == STATE_DONE) { + // now set the context flags for acceptor + setContextFlags(); + } + return retVal; + } + + /** + * obtain the available mechanisms + */ + private Oid[] getAvailableMechs() { + if (myCred != null) { + Oid[] mechs = new Oid[1]; + mechs[0] = myCred.getInternalMech(); + return mechs; + } else { + return factory.availableMechs; + } + } + + /** + * get ther DER encoded MechList + */ + private byte[] getEncodedMechs(Oid[] mechSet) + throws IOException, GSSException { + + DerOutputStream mech = new DerOutputStream(); + for (int i = 0; i < mechSet.length; i++) { + byte[] mechType = mechSet[i].getDER(); + mech.write(mechType); + } + // insert in SEQUENCE + DerOutputStream mechTypeList = new DerOutputStream(); + mechTypeList.write(DerValue.tag_Sequence, mech); + byte[] encoded = mechTypeList.toByteArray(); + return encoded; + } + + /** + * get the context flags + */ + private BitArray getContextFlags() { + BitArray out = new BitArray(7); + + if (getCredDelegState()) out.set(0, true); + if (getMutualAuthState()) out.set(1, true); + if (getReplayDetState()) out.set(2, true); + if (getSequenceDetState()) out.set(3, true); + if (getConfState()) out.set(5, true); + if (getIntegState()) out.set(6, true); + + return out; + } + + // Only called on acceptor side. On the initiator side, most flags + // are already set at request. For those that might get chanegd, + // state from mech below is used. + private void setContextFlags() { + + if (mechContext != null) { + // default for cred delegation is false + if (mechContext.getCredDelegState()) { + credDelegState = true; + } + // default for the following are true + if (!mechContext.getMutualAuthState()) { + mutualAuthState = false; + } + if (!mechContext.getReplayDetState()) { + replayDetState = false; + } + if (!mechContext.getSequenceDetState()) { + sequenceDetState = false; + } + if (!mechContext.getIntegState()) { + integState = false; + } + if (!mechContext.getConfState()) { + confState = false; + } + } + } + + /** + * generate MIC on mechList. Not used at the moment. + */ + /*private byte[] generateMechListMIC(byte[] mechTypes) + throws GSSException { + + // sanity check the required input + if (mechTypes == null) { + if (DEBUG) { + System.out.println("SpNegoContext: no MIC token included"); + } + return null; + } + + // check if mechanism supports integrity + if (!mechContext.getIntegState()) { + if (DEBUG) { + System.out.println("SpNegoContext: no MIC token included" + + " - mechanism does not support integrity"); + } + return null; + } + + // compute MIC on DER encoded mechanism list + byte[] mic = null; + try { + MessageProp prop = new MessageProp(0, true); + mic = getMIC(mechTypes, 0, mechTypes.length, prop); + if (DEBUG) { + System.out.println("SpNegoContext: getMIC = " + + SpNegoToken.getHexBytes(mic)); + } + } catch (GSSException e) { + mic = null; + if (DEBUG) { + System.out.println("SpNegoContext: no MIC token included" + + " - getMIC failed : " + e.getMessage()); + } + } + return mic; + }*/ + + /** + * verify MIC on MechList + */ + private boolean verifyMechListMIC(byte[] mechTypes, byte[] token) + throws GSSException { + + // sanity check the input + if (token == null) { + if (DEBUG) { + System.out.println("SpNegoContext: no MIC token validation"); + } + return true; + } + + // check if mechanism supports integrity + if (!mechContext.getIntegState()) { + if (DEBUG) { + System.out.println("SpNegoContext: no MIC token validation" + + " - mechanism does not support integrity"); + } + return true; + } + + // now verify the token + boolean valid = false; + try { + MessageProp prop = new MessageProp(0, true); + verifyMIC(token, 0, token.length, mechTypes, + 0, mechTypes.length, prop); + valid = true; + } catch (GSSException e) { + valid = false; + if (DEBUG) { + System.out.println("SpNegoContext: MIC validation failed! " + + e.getMessage()); + } + } + return valid; + } + + /** + * call gss_init_sec_context for the corresponding underlying mechanism + */ + private byte[] GSS_initSecContext(byte[] token) throws GSSException { + byte[] tok = null; + + if (mechContext == null) { + // initialize mech context + GSSName serverName = + factory.manager.createName(peerName.toString(), + peerName.getStringNameType(), internal_mech); + GSSCredential cred = null; + if (myCred != null) { + // create context with provided credential + cred = new GSSCredentialImpl(factory.manager, + myCred.getInternalCred()); + } + mechContext = + factory.manager.createContext(serverName, + internal_mech, cred, GSSContext.DEFAULT_LIFETIME); + mechContext.requestConf(confState); + mechContext.requestInteg(integState); + mechContext.requestCredDeleg(credDelegState); + mechContext.requestMutualAuth(mutualAuthState); + mechContext.requestReplayDet(replayDetState); + mechContext.requestSequenceDet(sequenceDetState); + if (mechContext instanceof ExtendedGSSContext) { + ((ExtendedGSSContext)mechContext).requestDelegPolicy( + delegPolicyState); + } + } + + // pass token + if (token != null) { + tok = token; + } else { + tok = new byte[0]; + } + + // pass token to mechanism initSecContext + byte[] init_token = mechContext.initSecContext(tok, 0, tok.length); + + return init_token; + } + + /** + * call gss_accept_sec_context for the corresponding underlying mechanism + */ + private byte[] GSS_acceptSecContext(byte[] token) throws GSSException { + + if (mechContext == null) { + // initialize mech context + GSSCredential cred = null; + if (myCred != null) { + // create context with provided credential + cred = new GSSCredentialImpl(factory.manager, + myCred.getInternalCred()); + } + mechContext = + factory.manager.createContext(cred); + } + + // pass token to mechanism acceptSecContext + byte[] accept_token = + mechContext.acceptSecContext(token, 0, token.length); + + return accept_token; + } + + /** + * This routine compares the recieved mechset to the mechset that + * this server can support. It looks sequentially through the mechset + * and the first one that matches what the server can support is + * chosen as the negotiated mechanism. If one is found, negResult + * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult + * is set to REJECT. + */ + private static Oid negotiate_mech_type(Oid[] supported_mechSet, + Oid[] mechSet) { + for (int i = 0; i < supported_mechSet.length; i++) { + for (int j = 0; j < mechSet.length; j++) { + if (mechSet[j].equals(supported_mechSet[i])) { + if (DEBUG) { + System.out.println("SpNegoContext: " + + "negotiated mechanism = " + mechSet[j]); + } + return (mechSet[j]); + } + } + } + return null; + } + + public final boolean isEstablished() { + return (state == STATE_DONE); + } + + public final boolean isMechContextEstablished() { + if (mechContext != null) { + return mechContext.isEstablished(); + } else { + if (DEBUG) { + System.out.println("The underlying mechanism context has " + + "not been initialized"); + } + return false; + } + } + + public final byte [] export() throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "GSS Export Context not available"); + } + + /** + * Sets the channel bindings to be used during context + * establishment. + */ + public final void setChannelBinding(ChannelBinding channelBinding) + throws GSSException { + this.channelBinding = channelBinding; + } + + final ChannelBinding getChannelBinding() { + return channelBinding; + } + + /* + * Anonymity is a little different in that after an application + * requests anonymity it will want to know whether the mechanism + * can support it or not, prior to sending any tokens across for + * context establishment. Since this is from the initiator's + * perspective, it essentially requests that the initiator be + * anonymous. + */ + public final void requestAnonymity(boolean value) throws GSSException { + // Ignore silently. Application will check back with + // getAnonymityState. + } + + // RFC 2853 actually calls for this to be called after context + // establishment to get the right answer, but that is + // incorrect. The application may not want to send over any + // tokens if anonymity is not available. + public final boolean getAnonymityState() { + return false; + } + + /** + * Requests the desired lifetime. Can only be used on the context + * initiator's side. + */ + public void requestLifetime(int lifetime) throws GSSException { + if (state == STATE_NEW && isInitiator()) + this.lifetime = lifetime; + } + + /** + * The lifetime remaining for this context. + */ + public final int getLifetime() { + if (mechContext != null) { + return mechContext.getLifetime(); + } else { + return GSSContext.INDEFINITE_LIFETIME; + } + } + + public final boolean isTransferable() throws GSSException { + return false; + } + + /** + * Requests that sequence checking be done on the GSS wrap and MIC + * tokens. + */ + public final void requestSequenceDet(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + sequenceDetState = value; + } + + /** + * Is sequence checking enabled on the GSS Wrap and MIC tokens? + * We enable sequence checking if replay detection is enabled. + */ + public final boolean getSequenceDetState() { + return sequenceDetState || replayDetState; + } + + /** + * Requests that replay detection be done on the GSS wrap and MIC + * tokens. + */ + public final void requestReplayDet(boolean value) throws GSSException { + if (state == STATE_NEW && isInitiator()) + replayDetState = value; + } + + /** + * Is replay detection enabled on the GSS wrap and MIC tokens? + * We enable replay detection if sequence checking is enabled. + */ + public final boolean getReplayDetState() { + return replayDetState || sequenceDetState; + } + + public final GSSNameSpi getTargName() throws GSSException { + // fill-in the GSSName + // get the peer name for the mechanism + if (mechContext != null) { + GSSNameImpl targName = (GSSNameImpl)mechContext.getTargName(); + peerName = targName.getElement(internal_mech); + return peerName; + } else { + if (DEBUG) { + System.out.println("The underlying mechanism context has " + + "not been initialized"); + } + return null; + } + } + + public final GSSNameSpi getSrcName() throws GSSException { + // fill-in the GSSName + // get the src name for the mechanism + if (mechContext != null) { + GSSNameImpl srcName = (GSSNameImpl)mechContext.getSrcName(); + myName = srcName.getElement(internal_mech); + return myName; + } else { + if (DEBUG) { + System.out.println("The underlying mechanism context has " + + "not been initialized"); + } + return null; + } + } + + /** + * Returns the delegated credential for the context. This + * is an optional feature of contexts which not all + * mechanisms will support. A context can be requested to + * support credential delegation by using the CRED_DELEG. + * This is only valid on the acceptor side of the context. + * @return GSSCredentialSpi object for the delegated credential + * @exception GSSException + * @see GSSContext#getCredDelegState + */ + public final GSSCredentialSpi getDelegCred() throws GSSException { + if (state != STATE_IN_PROCESS && state != STATE_DONE) + throw new GSSException(GSSException.NO_CONTEXT); + if (mechContext != null) { + GSSCredentialImpl delegCred = + (GSSCredentialImpl)mechContext.getDelegCred(); + if (delegCred == null) { + return null; + } + // determine delegated cred element usage + boolean initiate = false; + if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) { + initiate = true; + } + GSSCredentialSpi mechCred = + delegCred.getElement(internal_mech, initiate); + SpNegoCredElement cred = new SpNegoCredElement(mechCred); + return cred.getInternalCred(); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "getDelegCred called in invalid state!"); + } + } + + public final int getWrapSizeLimit(int qop, boolean confReq, + int maxTokSize) throws GSSException { + if (mechContext != null) { + return mechContext.getWrapSizeLimit(qop, confReq, maxTokSize); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "getWrapSizeLimit called in invalid state!"); + } + } + + public final byte[] wrap(byte inBuf[], int offset, int len, + MessageProp msgProp) throws GSSException { + if (mechContext != null) { + return mechContext.wrap(inBuf, offset, len, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + } + } + + public final void wrap(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + if (mechContext != null) { + mechContext.wrap(is, os, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Wrap called in invalid state!"); + } + } + + public final byte[] unwrap(byte inBuf[], int offset, int len, + MessageProp msgProp) + throws GSSException { + if (mechContext != null) { + return mechContext.unwrap(inBuf, offset, len, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "UnWrap called in invalid state!"); + } + } + + public final void unwrap(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + if (mechContext != null) { + mechContext.unwrap(is, os, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "UnWrap called in invalid state!"); + } + } + + public final byte[] getMIC(byte []inMsg, int offset, int len, + MessageProp msgProp) + throws GSSException { + if (mechContext != null) { + return mechContext.getMIC(inMsg, offset, len, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "getMIC called in invalid state!"); + } + } + + public final void getMIC(InputStream is, OutputStream os, + MessageProp msgProp) throws GSSException { + if (mechContext != null) { + mechContext.getMIC(is, os, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "getMIC called in invalid state!"); + } + } + + public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, + byte[] inMsg, int msgOffset, int msgLen, + MessageProp msgProp) + throws GSSException { + if (mechContext != null) { + mechContext.verifyMIC(inTok, tokOffset, tokLen, inMsg, msgOffset, + msgLen, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "verifyMIC called in invalid state!"); + } + } + + public final void verifyMIC(InputStream is, InputStream msgStr, + MessageProp msgProp) throws GSSException { + if (mechContext != null) { + mechContext.verifyMIC(is, msgStr, msgProp); + } else { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "verifyMIC called in invalid state!"); + } + } + + private static String printState(int state) { + switch (state) { + case STATE_NEW: + return ("STATE_NEW"); + case STATE_IN_PROCESS: + return ("STATE_IN_PROCESS"); + case STATE_DONE: + return ("STATE_DONE"); + case STATE_DELETED: + return ("STATE_DELETED"); + default: + return ("Unknown state " + state); + } + } + + /** + * Retrieve attribute of the context for {@code type}. + */ + public Object inquireSecContext(InquireType type) + throws GSSException { + if (mechContext == null) { + throw new GSSException(GSSException.NO_CONTEXT, -1, + "Underlying mech not established."); + } + if (mechContext instanceof ExtendedGSSContext) { + return ((ExtendedGSSContext)mechContext).inquireSecContext(type); + } else { + throw new GSSException(GSSException.BAD_MECH, -1, + "inquireSecContext not supported by underlying mech."); + } + } +} + diff --git a/src/sun/security/jgss/spnego/SpNegoCredElement.java b/src/sun/security/jgss/spnego/SpNegoCredElement.java new file mode 100644 index 00000000..c7cf9477 --- /dev/null +++ b/src/sun/security/jgss/spnego/SpNegoCredElement.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.jgss.spnego; + +import org.ietf.jgss.*; +import java.security.Provider; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.spi.GSSNameSpi; +import sun.security.jgss.spi.GSSCredentialSpi; + +/** + * This class is the cred element implementation for SPNEGO mech. + * NOTE: The current implementation can only support one mechanism. + * This should be changed once multi-mechanism support is needed. + * + * @author Valerie Peng + * @since 1.6 + */ +public class SpNegoCredElement implements GSSCredentialSpi { + + private GSSCredentialSpi cred = null; + + public SpNegoCredElement(GSSCredentialSpi cred) throws GSSException { + this.cred = cred; + } + + Oid getInternalMech() { + return cred.getMechanism(); + } + + // Used by GSSUtil.populateCredentials() + public GSSCredentialSpi getInternalCred() { + return cred; + } + + public Provider getProvider() { + return SpNegoMechFactory.PROVIDER; + } + + public void dispose() throws GSSException { + cred.dispose(); + } + + public GSSNameSpi getName() throws GSSException { + return cred.getName(); + } + + public int getInitLifetime() throws GSSException { + return cred.getInitLifetime(); + } + + public int getAcceptLifetime() throws GSSException { + return cred.getAcceptLifetime(); + } + + public boolean isInitiatorCredential() throws GSSException { + return cred.isInitiatorCredential(); + } + + public boolean isAcceptorCredential() throws GSSException { + return cred.isAcceptorCredential(); + } + + public Oid getMechanism() { + return GSSUtil.GSS_SPNEGO_MECH_OID; + } + + @Override + public GSSCredentialSpi impersonate(GSSNameSpi name) throws GSSException { + return cred.impersonate(name); + } +} diff --git a/src/sun/security/jgss/spnego/SpNegoMechFactory.java b/src/sun/security/jgss/spnego/SpNegoMechFactory.java new file mode 100644 index 00000000..ce0c6cec --- /dev/null +++ b/src/sun/security/jgss/spnego/SpNegoMechFactory.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.spnego; + +import org.ietf.jgss.*; +import sun.security.jgss.*; +import sun.security.jgss.spi.*; +import sun.security.jgss.krb5.Krb5MechFactory; +import sun.security.jgss.krb5.Krb5InitCredential; +import sun.security.jgss.krb5.Krb5AcceptCredential; +import sun.security.jgss.krb5.Krb5NameElement; +import java.security.Provider; +import java.util.Vector; + +/** + * SpNego Mechanism plug in for JGSS + * This is the properties object required by the JGSS framework. + * All mechanism specific information is defined here. + * + * @author Seema Malkani + * @since 1.6 + */ + +public final class SpNegoMechFactory implements MechanismFactory { + + static final Provider PROVIDER = + new SunProvider(); + + static final Oid GSS_SPNEGO_MECH_OID = + GSSUtil.createOid("1.3.6.1.5.5.2"); + + private static Oid[] nameTypes = + new Oid[] { GSSName.NT_USER_NAME, + GSSName.NT_HOSTBASED_SERVICE, + GSSName.NT_EXPORT_NAME}; + + // The default underlying mech of SPNEGO, must not be SPNEGO itself. + private static final Oid DEFAULT_SPNEGO_MECH_OID = + ProviderList.DEFAULT_MECH_OID.equals(GSS_SPNEGO_MECH_OID)? + GSSUtil.GSS_KRB5_MECH_OID: + ProviderList.DEFAULT_MECH_OID; + + // Use an instance of a GSSManager whose provider list + // does not include native provider + final GSSManagerImpl manager; + final Oid[] availableMechs; + + private static SpNegoCredElement getCredFromSubject(GSSNameSpi name, + boolean initiate) + throws GSSException { + Vector creds = + GSSUtil.searchSubject(name, GSS_SPNEGO_MECH_OID, + initiate, SpNegoCredElement.class); + + SpNegoCredElement result = ((creds == null || creds.isEmpty()) ? + null : creds.firstElement()); + + // Force permission check before returning the cred to caller + if (result != null) { + GSSCredentialSpi cred = result.getInternalCred(); + if (GSSUtil.isKerberosMech(cred.getMechanism())) { + if (initiate) { + Krb5InitCredential krbCred = (Krb5InitCredential) cred; + Krb5MechFactory.checkInitCredPermission + ((Krb5NameElement) krbCred.getName()); + } else { + Krb5AcceptCredential krbCred = (Krb5AcceptCredential) cred; + Krb5MechFactory.checkAcceptCredPermission + ((Krb5NameElement) krbCred.getName(), name); + } + } + } + return result; + } + + public SpNegoMechFactory(GSSCaller caller) { + manager = new GSSManagerImpl(caller, false); + Oid[] mechs = manager.getMechs(); + availableMechs = new Oid[mechs.length-1]; + for (int i = 0, j = 0; i < mechs.length; i++) { + // Skip SpNego mechanism + if (!mechs[i].equals(GSS_SPNEGO_MECH_OID)) { + availableMechs[j++] = mechs[i]; + } + } + // Move the preferred mech to first place + for (int i=0; i + table = new Hashtable(5); + + static GSSLibStub getInstance(Oid mech) throws GSSException { + GSSLibStub s = table.get(mech); + if (s == null) { + s = new GSSLibStub(mech); + table.put(mech, s); + } + return s; + } + private GSSLibStub(Oid mech) throws GSSException { + SunNativeProvider.debug("Created GSSLibStub for mech " + mech); + this.mech = mech; + this.pMech = getMechPtr(mech.getDER()); + } + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof GSSLibStub)) { + return false; + } + return (mech.equals(((GSSLibStub) obj).getMech())); + } + public int hashCode() { + return mech.hashCode(); + } + Oid getMech() { + return mech; + } +} diff --git a/src/sun/security/jgss/wrapper/GSSNameElement.java b/src/sun/security/jgss/wrapper/GSSNameElement.java new file mode 100644 index 00000000..bf9691a1 --- /dev/null +++ b/src/sun/security/jgss/wrapper/GSSNameElement.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.wrapper; + +import org.ietf.jgss.*; +import java.security.Provider; +import java.io.IOException; + +import sun.security.jgss.GSSUtil; +import sun.security.util.ObjectIdentifier; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.jgss.GSSExceptionImpl; +import sun.security.jgss.spi.GSSNameSpi; + +/** + * This class is essentially a wrapper class for the gss_name_t + * structure of the native GSS library. + * @author Valerie Peng + * @since 1.6 + */ + +public class GSSNameElement implements GSSNameSpi { + + long pName = 0; // Pointer to the gss_name_t structure + private String printableName; + private Oid printableType; + private GSSLibStub cStub; + + static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement(); + + private static Oid getNativeNameType(Oid nameType, GSSLibStub stub) { + if (GSSUtil.NT_GSS_KRB5_PRINCIPAL.equals(nameType)) { + Oid[] supportedNTs = null; + try { + supportedNTs = stub.inquireNamesForMech(); + } catch (GSSException ge) { + if (ge.getMajor() == GSSException.BAD_MECH && + GSSUtil.isSpNegoMech(stub.getMech())) { + // Workaround known Heimdal issue and retry with KRB5 + try { + stub = GSSLibStub.getInstance + (GSSUtil.GSS_KRB5_MECH_OID); + supportedNTs = stub.inquireNamesForMech(); + } catch (GSSException ge2) { + // Should never happen + SunNativeProvider.debug("Name type list unavailable: " + + ge2.getMajorString()); + } + } else { + SunNativeProvider.debug("Name type list unavailable: " + + ge.getMajorString()); + } + } + if (supportedNTs != null) { + for (int i = 0; i < supportedNTs.length; i++) { + if (supportedNTs[i].equals(nameType)) return nameType; + } + // Special handling the specified name type + SunNativeProvider.debug("Override " + nameType + + " with mechanism default(null)"); + return null; // Use mechanism specific default + } + } + return nameType; + } + + private GSSNameElement() { + printableName = ""; + } + + GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException { + assert(stub != null); + if (pNativeName == 0) { + throw new GSSException(GSSException.BAD_NAME); + } + // Note: pNativeName is assumed to be a MN. + pName = pNativeName; + cStub = stub; + setPrintables(); + } + + GSSNameElement(byte[] nameBytes, Oid nameType, GSSLibStub stub) + throws GSSException { + assert(stub != null); + if (nameBytes == null) { + throw new GSSException(GSSException.BAD_NAME); + } + cStub = stub; + byte[] name = nameBytes; + + if (nameType != null) { + // Special handling the specified name type if + // necessary + nameType = getNativeNameType(nameType, stub); + + if (GSSName.NT_EXPORT_NAME.equals(nameType)) { + // Need to add back the mech Oid portion (stripped + // off by GSSNameImpl class prior to calling this + // method) for "NT_EXPORT_NAME" + byte[] mechBytes = null; + DerOutputStream dout = new DerOutputStream(); + Oid mech = cStub.getMech(); + try { + dout.putOID(new ObjectIdentifier(mech.toString())); + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.FAILURE, e); + } + mechBytes = dout.toByteArray(); + name = new byte[2 + 2 + mechBytes.length + 4 + nameBytes.length]; + int pos = 0; + name[pos++] = 0x04; + name[pos++] = 0x01; + name[pos++] = (byte) (mechBytes.length>>>8); + name[pos++] = (byte) mechBytes.length; + System.arraycopy(mechBytes, 0, name, pos, mechBytes.length); + pos += mechBytes.length; + name[pos++] = (byte) (nameBytes.length>>>24); + name[pos++] = (byte) (nameBytes.length>>>16); + name[pos++] = (byte) (nameBytes.length>>>8); + name[pos++] = (byte) nameBytes.length; + System.arraycopy(nameBytes, 0, name, pos, nameBytes.length); + } + } + pName = cStub.importName(name, nameType); + setPrintables(); + + SunNativeProvider.debug("Imported " + printableName + " w/ type " + + printableType); + } + + private void setPrintables() throws GSSException { + Object[] printables = null; + printables = cStub.displayName(pName); + assert((printables != null) && (printables.length == 2)); + printableName = (String) printables[0]; + assert(printableName != null); + printableType = (Oid) printables[1]; + if (printableType == null) { + printableType = GSSName.NT_USER_NAME; + } + } + + // Need to be public for GSSUtil.getSubject() + public String getKrbName() throws GSSException { + long mName = 0; + GSSLibStub stub = cStub; + if (!GSSUtil.isKerberosMech(cStub.getMech())) { + stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID); + } + mName = stub.canonicalizeName(pName); + Object[] printables2 = stub.displayName(mName); + stub.releaseName(mName); + SunNativeProvider.debug("Got kerberized name: " + printables2[0]); + return (String) printables2[0]; + } + + public Provider getProvider() { + return SunNativeProvider.INSTANCE; + } + + public boolean equals(GSSNameSpi other) throws GSSException { + if (!(other instanceof GSSNameElement)) { + return false; + } + return cStub.compareName(pName, ((GSSNameElement)other).pName); + } + + public boolean equals(Object other) { + if (!(other instanceof GSSNameElement)) { + return false; + } + try { + return equals((GSSNameElement) other); + } catch (GSSException ex) { + return false; + } + } + + public int hashCode() { + return new Long(pName).hashCode(); + } + + public byte[] export() throws GSSException { + byte[] nameVal = cStub.exportName(pName); + + // Need to strip off the mech Oid portion of the exported + // bytes since GSSNameImpl class will subsequently add it. + int pos = 0; + if ((nameVal[pos++] != 0x04) || + (nameVal[pos++] != 0x01)) + throw new GSSException(GSSException.BAD_NAME); + + int mechOidLen = (((0xFF & nameVal[pos++]) << 8) | + (0xFF & nameVal[pos++])); + ObjectIdentifier temp = null; + try { + DerInputStream din = new DerInputStream(nameVal, pos, + mechOidLen); + temp = new ObjectIdentifier(din); + } catch (IOException e) { + throw new GSSExceptionImpl(GSSException.BAD_NAME, e); + } + Oid mech2 = new Oid(temp.toString()); + assert(mech2.equals(getMechanism())); + pos += mechOidLen; + int mechPortionLen = (((0xFF & nameVal[pos++]) << 24) | + ((0xFF & nameVal[pos++]) << 16) | + ((0xFF & nameVal[pos++]) << 8) | + (0xFF & nameVal[pos++])); + if (mechPortionLen < 0) { + throw new GSSException(GSSException.BAD_NAME); + } + byte[] mechPortion = new byte[mechPortionLen]; + System.arraycopy(nameVal, pos, mechPortion, 0, mechPortionLen); + return mechPortion; + } + + public Oid getMechanism() { + return cStub.getMech(); + } + + public String toString() { + return printableName; + } + + public Oid getStringNameType() { + return printableType; + } + + public boolean isAnonymousName() { + return (GSSName.NT_ANONYMOUS.equals(printableType)); + } + + public void dispose() { + if (pName != 0) { + cStub.releaseName(pName); + pName = 0; + } + } + + protected void finalize() throws Throwable { + dispose(); + } +} diff --git a/src/sun/security/jgss/wrapper/Krb5Util.java b/src/sun/security/jgss/wrapper/Krb5Util.java new file mode 100644 index 00000000..e2ccf4db --- /dev/null +++ b/src/sun/security/jgss/wrapper/Krb5Util.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.jgss.wrapper; + +import javax.security.auth.kerberos.ServicePermission; + +import org.ietf.jgss.GSSException; + +/** + * This class is an utility class for Kerberos related stuff. + * @author Valerie Peng + * @since 1.6 + */ +class Krb5Util { + + // Return the Kerberos TGS principal name using the domain + // of the specified name + static String getTGSName(GSSNameElement name) + throws GSSException { + String krbPrinc = name.getKrbName(); + int atIndex = krbPrinc.indexOf("@"); + String realm = krbPrinc.substring(atIndex + 1); + StringBuffer buf = new StringBuffer("krbtgt/"); + buf.append(realm).append('@').append(realm); + return buf.toString(); + } + + // Perform the Service Permission check using the specified + // target and action + static void checkServicePermission(String target, String action) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + SunNativeProvider.debug("Checking ServicePermission(" + + target + ", " + action + ")"); + ServicePermission perm = + new ServicePermission(target, action); + sm.checkPermission(perm); + } + } +} diff --git a/src/sun/security/jgss/wrapper/NativeGSSContext.java b/src/sun/security/jgss/wrapper/NativeGSSContext.java new file mode 100644 index 00000000..2a20c96c --- /dev/null +++ b/src/sun/security/jgss/wrapper/NativeGSSContext.java @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.wrapper; + +import org.ietf.jgss.*; +import java.security.Provider; +import sun.security.jgss.GSSHeader; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.GSSExceptionImpl; +import sun.security.jgss.spi.*; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; +import sun.security.jgss.spnego.NegTokenInit; +import sun.security.jgss.spnego.NegTokenTarg; +import javax.security.auth.kerberos.DelegationPermission; +import com.sun.security.jgss.InquireType; +import java.io.*; + + +/** + * This class is essentially a wrapper class for the gss_ctx_id_t + * structure of the native GSS library. + * @author Valerie Peng + * @since 1.6 + */ +class NativeGSSContext implements GSSContextSpi { + + private static final int GSS_C_DELEG_FLAG = 1; + private static final int GSS_C_MUTUAL_FLAG = 2; + private static final int GSS_C_REPLAY_FLAG = 4; + private static final int GSS_C_SEQUENCE_FLAG = 8; + private static final int GSS_C_CONF_FLAG = 16; + private static final int GSS_C_INTEG_FLAG = 32; + private static final int GSS_C_ANON_FLAG = 64; + private static final int GSS_C_PROT_READY_FLAG = 128; + private static final int GSS_C_TRANS_FLAG = 256; + + private static final int NUM_OF_INQUIRE_VALUES = 6; + + private long pContext = 0; // Pointer to the gss_ctx_id_t structure + private GSSNameElement srcName; + private GSSNameElement targetName; + private GSSCredElement cred; + private boolean isInitiator; + private boolean isEstablished; + private Oid actualMech; // Assigned during context establishment + + private ChannelBinding cb; + private GSSCredElement delegatedCred; + private int flags; + private int lifetime = GSSCredential.DEFAULT_LIFETIME; + private final GSSLibStub cStub; + + private boolean skipDelegPermCheck; + private boolean skipServicePermCheck; + + // Retrieve the (preferred) mech out of SPNEGO tokens, i.e. + // NegTokenInit & NegTokenTarg + private static Oid getMechFromSpNegoToken(byte[] token, + boolean isInitiator) + throws GSSException { + Oid mech = null; + if (isInitiator) { + GSSHeader header = null; + try { + header = new GSSHeader(new ByteArrayInputStream(token)); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + int negTokenLen = header.getMechTokenLength(); + byte[] negToken = new byte[negTokenLen]; + System.arraycopy(token, token.length-negTokenLen, + negToken, 0, negToken.length); + + NegTokenInit ntok = new NegTokenInit(negToken); + if (ntok.getMechToken() != null) { + Oid[] mechList = ntok.getMechTypeList(); + mech = mechList[0]; + } + } else { + NegTokenTarg ntok = new NegTokenTarg(token); + mech = ntok.getSupportedMech(); + } + return mech; + } + + // Perform the Service permission check + private void doServicePermCheck() throws GSSException { + if (System.getSecurityManager() != null) { + String action = (isInitiator? "initiate" : "accept"); + // Need to check Service permission for accessing + // initiator cred for SPNEGO during context establishment + if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator + && !isEstablished) { + if (srcName == null) { + // Check by creating default initiator KRB5 cred + GSSCredElement tempCred = + new GSSCredElement(null, lifetime, + GSSCredential.INITIATE_ONLY, + GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID)); + tempCred.dispose(); + } else { + String tgsName = Krb5Util.getTGSName(srcName); + Krb5Util.checkServicePermission(tgsName, action); + } + } + String targetStr = targetName.getKrbName(); + Krb5Util.checkServicePermission(targetStr, action); + skipServicePermCheck = true; + } + } + + // Perform the Delegation permission check + private void doDelegPermCheck() throws GSSException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + String targetStr = targetName.getKrbName(); + String tgsStr = Krb5Util.getTGSName(targetName); + StringBuffer buf = new StringBuffer("\""); + buf.append(targetStr).append("\" \""); + buf.append(tgsStr).append('\"'); + String krbPrincPair = buf.toString(); + SunNativeProvider.debug("Checking DelegationPermission (" + + krbPrincPair + ")"); + DelegationPermission perm = + new DelegationPermission(krbPrincPair); + sm.checkPermission(perm); + skipDelegPermCheck = true; + } + } + + private byte[] retrieveToken(InputStream is, int mechTokenLen) + throws GSSException { + try { + byte[] result = null; + if (mechTokenLen != -1) { + // Need to add back the GSS header for a complete GSS token + SunNativeProvider.debug("Precomputed mechToken length: " + + mechTokenLen); + GSSHeader gssHeader = new GSSHeader + (new ObjectIdentifier(cStub.getMech().toString()), + mechTokenLen); + ByteArrayOutputStream baos = new ByteArrayOutputStream(600); + + byte[] mechToken = new byte[mechTokenLen]; + int len = is.read(mechToken); + assert(mechTokenLen == len); + gssHeader.encode(baos); + baos.write(mechToken); + result = baos.toByteArray(); + } else { + // Must be unparsed GSS token or SPNEGO's NegTokenTarg token + assert(mechTokenLen == -1); + DerValue dv = new DerValue(is); + result = dv.toByteArray(); + } + SunNativeProvider.debug("Complete Token length: " + + result.length); + return result; + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + + // Constructor for context initiator + NativeGSSContext(GSSNameElement peer, GSSCredElement myCred, + int time, GSSLibStub stub) throws GSSException { + if (peer == null) { + throw new GSSException(GSSException.FAILURE, 1, "null peer"); + } + cStub = stub; + cred = myCred; + targetName = peer; + isInitiator = true; + lifetime = time; + + if (GSSUtil.isKerberosMech(cStub.getMech())) { + doServicePermCheck(); + if (cred == null) { + cred = new GSSCredElement(null, lifetime, + GSSCredential.INITIATE_ONLY, cStub); + } + srcName = cred.getName(); + } + } + + // Constructor for context acceptor + NativeGSSContext(GSSCredElement myCred, GSSLibStub stub) + throws GSSException { + cStub = stub; + cred = myCred; + + if (cred != null) targetName = cred.getName(); + + isInitiator = false; + // Defer Service permission check for default acceptor cred + // to acceptSecContext() + if (GSSUtil.isKerberosMech(cStub.getMech()) && targetName != null) { + doServicePermCheck(); + } + + // srcName and potentially targetName (when myCred is null) + // will be set in GSSLibStub.acceptContext(...) + } + + // Constructor for imported context + NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException { + assert(pContext != 0); + pContext = pCtxt; + cStub = stub; + + // Set everything except cred, cb, delegatedCred + long[] info = cStub.inquireContext(pContext); + if (info.length != NUM_OF_INQUIRE_VALUES) { + throw new RuntimeException("Bug w/ GSSLibStub.inquireContext()"); + } + srcName = new GSSNameElement(info[0], cStub); + targetName = new GSSNameElement(info[1], cStub); + isInitiator = (info[2] != 0); + isEstablished = (info[3] != 0); + flags = (int) info[4]; + lifetime = (int) info[5]; + + // Do Service Permission check when importing SPNEGO context + // just to be safe + Oid mech = cStub.getMech(); + if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) { + doServicePermCheck(); + } + } + + public Provider getProvider() { + return SunNativeProvider.INSTANCE; + } + + public byte[] initSecContext(InputStream is, int mechTokenLen) + throws GSSException { + byte[] outToken = null; + if ((!isEstablished) && (isInitiator)) { + byte[] inToken = null; + // Ignore the specified input stream on the first call + if (pContext != 0) { + inToken = retrieveToken(is, mechTokenLen); + SunNativeProvider.debug("initSecContext=> inToken len=" + + inToken.length); + } + + if (!getCredDelegState()) skipDelegPermCheck = true; + + if (GSSUtil.isKerberosMech(cStub.getMech()) && !skipDelegPermCheck) { + doDelegPermCheck(); + } + + long pCred = (cred == null? 0 : cred.pCred); + outToken = cStub.initContext(pCred, targetName.pName, + cb, inToken, this); + SunNativeProvider.debug("initSecContext=> outToken len=" + + (outToken == null ? 0 : outToken.length)); + + // Only inspect the token when the permission check + // has not been performed + if (GSSUtil.isSpNegoMech(cStub.getMech()) && outToken != null) { + // WORKAROUND for SEAM bug#6287358 + actualMech = getMechFromSpNegoToken(outToken, true); + + if (GSSUtil.isKerberosMech(actualMech)) { + if (!skipServicePermCheck) doServicePermCheck(); + if (!skipDelegPermCheck) doDelegPermCheck(); + } + } + + if (isEstablished) { + if (srcName == null) { + srcName = new GSSNameElement + (cStub.getContextName(pContext, true), cStub); + } + if (cred == null) { + cred = new GSSCredElement(srcName, lifetime, + GSSCredential.INITIATE_ONLY, + cStub); + } + } + } + return outToken; + } + + public byte[] acceptSecContext(InputStream is, int mechTokenLen) + throws GSSException { + byte[] outToken = null; + if ((!isEstablished) && (!isInitiator)) { + byte[] inToken = retrieveToken(is, mechTokenLen); + SunNativeProvider.debug("acceptSecContext=> inToken len=" + + inToken.length); + long pCred = (cred == null? 0 : cred.pCred); + outToken = cStub.acceptContext(pCred, cb, inToken, this); + SunNativeProvider.debug("acceptSecContext=> outToken len=" + + (outToken == null? 0 : outToken.length)); + + if (targetName == null) { + targetName = new GSSNameElement + (cStub.getContextName(pContext, false), cStub); + // Replace the current default acceptor cred now that + // the context acceptor name is available + if (cred != null) cred.dispose(); + cred = new GSSCredElement(targetName, lifetime, + GSSCredential.ACCEPT_ONLY, cStub); + } + + // Only inspect token when the permission check has not + // been performed + if (GSSUtil.isSpNegoMech(cStub.getMech()) && + (outToken != null) && !skipServicePermCheck) { + if (GSSUtil.isKerberosMech(getMechFromSpNegoToken + (outToken, false))) { + doServicePermCheck(); + } + } + } + return outToken; + } + + public boolean isEstablished() { + return isEstablished; + } + + public void dispose() throws GSSException { + srcName = null; + targetName = null; + cred = null; + delegatedCred = null; + if (pContext != 0) { + pContext = cStub.deleteContext(pContext); + pContext = 0; + } + } + + public int getWrapSizeLimit(int qop, boolean confReq, + int maxTokenSize) + throws GSSException { + return cStub.wrapSizeLimit(pContext, (confReq? 1:0), qop, + maxTokenSize); + } + + public byte[] wrap(byte[] inBuf, int offset, int len, + MessageProp msgProp) throws GSSException { + byte[] data = inBuf; + if ((offset != 0) || (len != inBuf.length)) { + data = new byte[len]; + System.arraycopy(inBuf, offset, data, 0, len); + } + return cStub.wrap(pContext, data, msgProp); + } + public void wrap(byte inBuf[], int offset, int len, + OutputStream os, MessageProp msgProp) + throws GSSException { + try { + byte[] result = wrap(inBuf, offset, len, msgProp); + os.write(result); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf, + int outOffset, MessageProp msgProp) + throws GSSException { + byte[] result = wrap(inBuf, inOffset, len, msgProp); + System.arraycopy(result, 0, outBuf, outOffset, result.length); + return result.length; + } + public void wrap(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + try { + byte[] data = new byte[inStream.available()]; + int length = inStream.read(data); + byte[] token = wrap(data, 0, length, msgProp); + outStream.write(token); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + + public byte[] unwrap(byte[] inBuf, int offset, int len, + MessageProp msgProp) + throws GSSException { + if ((offset != 0) || (len != inBuf.length)) { + byte[] temp = new byte[len]; + System.arraycopy(inBuf, offset, temp, 0, len); + return cStub.unwrap(pContext, temp, msgProp); + } else { + return cStub.unwrap(pContext, inBuf, msgProp); + } + } + public int unwrap(byte[] inBuf, int inOffset, int len, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException { + byte[] result = null; + if ((inOffset != 0) || (len != inBuf.length)) { + byte[] temp = new byte[len]; + System.arraycopy(inBuf, inOffset, temp, 0, len); + result = cStub.unwrap(pContext, temp, msgProp); + } else { + result = cStub.unwrap(pContext, inBuf, msgProp); + } + System.arraycopy(result, 0, outBuf, outOffset, result.length); + return result.length; + } + public void unwrap(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + try { + byte[] wrapped = new byte[inStream.available()]; + int wLength = inStream.read(wrapped); + byte[] data = unwrap(wrapped, 0, wLength, msgProp); + outStream.write(data); + outStream.flush(); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + + public int unwrap(InputStream inStream, + byte[] outBuf, int outOffset, + MessageProp msgProp) throws GSSException { + byte[] wrapped = null; + int wLength = 0; + try { + wrapped = new byte[inStream.available()]; + wLength = inStream.read(wrapped); + byte[] result = unwrap(wrapped, 0, wLength, msgProp); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + byte[] result = unwrap(wrapped, 0, wLength, msgProp); + System.arraycopy(result, 0, outBuf, outOffset, result.length); + return result.length; + } + + public byte[] getMIC(byte[] in, int offset, int len, + MessageProp msgProp) throws GSSException { + int qop = (msgProp == null? 0:msgProp.getQOP()); + byte[] inMsg = in; + if ((offset != 0) || (len != in.length)) { + inMsg = new byte[len]; + System.arraycopy(in, offset, inMsg, 0, len); + } + return cStub.getMic(pContext, qop, inMsg); + } + + public void getMIC(InputStream inStream, OutputStream outStream, + MessageProp msgProp) throws GSSException { + try { + int length = 0; + byte[] msg = new byte[inStream.available()]; + length = inStream.read(msg); + + byte[] msgToken = getMIC(msg, 0, length, msgProp); + if ((msgToken != null) && msgToken.length != 0) { + outStream.write(msgToken); + } + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + + public void verifyMIC(byte[] inToken, int tOffset, int tLen, + byte[] inMsg, int mOffset, int mLen, + MessageProp msgProp) throws GSSException { + byte[] token = inToken; + byte[] msg = inMsg; + if ((tOffset != 0) || (tLen != inToken.length)) { + token = new byte[tLen]; + System.arraycopy(inToken, tOffset, token, 0, tLen); + } + if ((mOffset != 0) || (mLen != inMsg.length)) { + msg = new byte[mLen]; + System.arraycopy(inMsg, mOffset, msg, 0, mLen); + } + cStub.verifyMic(pContext, token, msg, msgProp); + } + + public void verifyMIC(InputStream tokStream, InputStream msgStream, + MessageProp msgProp) throws GSSException { + try { + byte[] msg = new byte[msgStream.available()]; + int mLength = msgStream.read(msg); + byte[] tok = new byte[tokStream.available()]; + int tLength = tokStream.read(tok); + verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp); + } catch (IOException ioe) { + throw new GSSExceptionImpl(GSSException.FAILURE, ioe); + } + } + + public byte[] export() throws GSSException { + byte[] result = cStub.exportContext(pContext); + pContext = 0; + return result; + } + + private void changeFlags(int flagMask, boolean isEnable) { + if (isInitiator && pContext == 0) { + if (isEnable) { + flags |= flagMask; + } else { + flags &= ~flagMask; + } + } + } + public void requestMutualAuth(boolean state) throws GSSException { + changeFlags(GSS_C_MUTUAL_FLAG, state); + } + public void requestReplayDet(boolean state) throws GSSException { + changeFlags(GSS_C_REPLAY_FLAG, state); + } + public void requestSequenceDet(boolean state) throws GSSException { + changeFlags(GSS_C_SEQUENCE_FLAG, state); + } + public void requestCredDeleg(boolean state) throws GSSException { + changeFlags(GSS_C_DELEG_FLAG, state); + } + public void requestAnonymity(boolean state) throws GSSException { + changeFlags(GSS_C_ANON_FLAG, state); + } + public void requestConf(boolean state) throws GSSException { + changeFlags(GSS_C_CONF_FLAG, state); + } + public void requestInteg(boolean state) throws GSSException { + changeFlags(GSS_C_INTEG_FLAG, state); + } + public void requestDelegPolicy(boolean state) throws GSSException { + // Not supported, ignore + } + public void requestLifetime(int lifetime) throws GSSException { + if (isInitiator && pContext == 0) { + this.lifetime = lifetime; + } + } + public void setChannelBinding(ChannelBinding cb) throws GSSException { + if (pContext == 0) { + this.cb = cb; + } + } + + private boolean checkFlags(int flagMask) { + return ((flags & flagMask) != 0); + } + public boolean getCredDelegState() { + return checkFlags(GSS_C_DELEG_FLAG); + } + public boolean getMutualAuthState() { + return checkFlags(GSS_C_MUTUAL_FLAG); + } + public boolean getReplayDetState() { + return checkFlags(GSS_C_REPLAY_FLAG); + } + public boolean getSequenceDetState() { + return checkFlags(GSS_C_SEQUENCE_FLAG); + } + public boolean getAnonymityState() { + return checkFlags(GSS_C_ANON_FLAG); + } + public boolean isTransferable() throws GSSException { + return checkFlags(GSS_C_TRANS_FLAG); + } + public boolean isProtReady() { + return checkFlags(GSS_C_PROT_READY_FLAG); + } + public boolean getConfState() { + return checkFlags(GSS_C_CONF_FLAG); + } + public boolean getIntegState() { + return checkFlags(GSS_C_INTEG_FLAG); + } + public boolean getDelegPolicyState() { + return false; + } + public int getLifetime() { + return cStub.getContextTime(pContext); + } + public GSSNameSpi getSrcName() throws GSSException { + return srcName; + } + public GSSNameSpi getTargName() throws GSSException { + return targetName; + } + public Oid getMech() throws GSSException { + if (isEstablished && actualMech != null) { + return actualMech; + } else { + return cStub.getMech(); + } + } + public GSSCredentialSpi getDelegCred() throws GSSException { + return delegatedCred; + } + public boolean isInitiator() { + return isInitiator; + } + + protected void finalize() throws Throwable { + dispose(); + } + + public Object inquireSecContext(InquireType type) + throws GSSException { + throw new GSSException(GSSException.UNAVAILABLE, -1, + "Inquire type not supported."); + } +} diff --git a/src/sun/security/jgss/wrapper/NativeGSSFactory.java b/src/sun/security/jgss/wrapper/NativeGSSFactory.java new file mode 100644 index 00000000..89012727 --- /dev/null +++ b/src/sun/security/jgss/wrapper/NativeGSSFactory.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.wrapper; + +import java.io.UnsupportedEncodingException; +import java.security.Provider; +import java.util.Vector; +import org.ietf.jgss.*; +import sun.security.jgss.GSSUtil; +import sun.security.jgss.GSSCaller; +import sun.security.jgss.GSSExceptionImpl; +import sun.security.jgss.spi.*; + +/** + * JGSS plugin for generic mechanisms provided through native GSS framework. + * + * @author Valerie Peng + */ + +public final class NativeGSSFactory implements MechanismFactory { + + GSSLibStub cStub = null; + private final GSSCaller caller; + + private GSSCredElement getCredFromSubject(GSSNameElement name, + boolean initiate) + throws GSSException { + Oid mech = cStub.getMech(); + Vector creds = GSSUtil.searchSubject + (name, mech, initiate, GSSCredElement.class); + + // If Subject is present but no native creds available + if (creds != null && creds.isEmpty()) { + if (GSSUtil.useSubjectCredsOnly(caller)) { + throw new GSSException(GSSException.NO_CRED); + } + } + + GSSCredElement result = ((creds == null || creds.isEmpty()) ? + null : creds.firstElement()); + // Force permission check before returning the cred to caller + if (result != null) { + result.doServicePermCheck(); + } + return result; + } + + public NativeGSSFactory(GSSCaller caller) { + this.caller = caller; + // Have to call setMech(Oid) explicitly before calling other + // methods. Otherwise, NPE may be thrown unexpectantly + } + + public void setMech(Oid mech) throws GSSException { + cStub = GSSLibStub.getInstance(mech); + } + + public GSSNameSpi getNameElement(String nameStr, Oid nameType) + throws GSSException { + try { + byte[] nameBytes = + (nameStr == null ? null : nameStr.getBytes("UTF-8")); + return new GSSNameElement(nameBytes, nameType, cStub); + } catch (UnsupportedEncodingException uee) { + // Shouldn't happen + throw new GSSExceptionImpl(GSSException.FAILURE, uee); + } + } + + public GSSNameSpi getNameElement(byte[] name, Oid nameType) + throws GSSException { + return new GSSNameElement(name, nameType, cStub); + } + + public GSSCredentialSpi getCredentialElement(GSSNameSpi name, + int initLifetime, + int acceptLifetime, + int usage) + throws GSSException { + GSSNameElement nname = null; + if (name != null && !(name instanceof GSSNameElement)) { + nname = (GSSNameElement) + getNameElement(name.toString(), name.getStringNameType()); + } else nname = (GSSNameElement) name; + + if (usage == GSSCredential.INITIATE_AND_ACCEPT) { + // Force separate acqusition of cred element since + // MIT's impl does not correctly report NO_CRED error. + usage = GSSCredential.INITIATE_ONLY; + } + + GSSCredElement credElement = + getCredFromSubject(nname, (usage == GSSCredential.INITIATE_ONLY)); + + if (credElement == null) { + // No cred in the Subject + if (usage == GSSCredential.INITIATE_ONLY) { + credElement = new GSSCredElement(nname, initLifetime, + usage, cStub); + } else if (usage == GSSCredential.ACCEPT_ONLY) { + if (nname == null) { + nname = GSSNameElement.DEF_ACCEPTOR; + } + credElement = new GSSCredElement(nname, acceptLifetime, + usage, cStub); + } else { + throw new GSSException(GSSException.FAILURE, -1, + "Unknown usage mode requested"); + } + } + return credElement; + } + + public GSSContextSpi getMechanismContext(GSSNameSpi peer, + GSSCredentialSpi myCred, + int lifetime) + throws GSSException { + if (peer == null) { + throw new GSSException(GSSException.BAD_NAME); + } else if (!(peer instanceof GSSNameElement)) { + peer = (GSSNameElement) + getNameElement(peer.toString(), peer.getStringNameType()); + } + if (myCred == null) { + myCred = getCredFromSubject(null, true); + } else if (!(myCred instanceof GSSCredElement)) { + throw new GSSException(GSSException.NO_CRED); + } + return new NativeGSSContext((GSSNameElement) peer, + (GSSCredElement) myCred, + lifetime, cStub); + } + + public GSSContextSpi getMechanismContext(GSSCredentialSpi myCred) + throws GSSException { + if (myCred == null) { + myCred = getCredFromSubject(null, false); + } else if (!(myCred instanceof GSSCredElement)) { + throw new GSSException(GSSException.NO_CRED); + } + return new NativeGSSContext((GSSCredElement) myCred, cStub); + } + + public GSSContextSpi getMechanismContext(byte[] exportedContext) + throws GSSException { + return cStub.importContext(exportedContext); + } + + public final Oid getMechanismOid() { + return cStub.getMech(); + } + + public Provider getProvider() { + return SunNativeProvider.INSTANCE; + } + + public Oid[] getNameTypes() throws GSSException { + return cStub.inquireNamesForMech(); + } +} diff --git a/src/sun/security/jgss/wrapper/SunNativeProvider.java b/src/sun/security/jgss/wrapper/SunNativeProvider.java new file mode 100644 index 00000000..25da168d --- /dev/null +++ b/src/sun/security/jgss/wrapper/SunNativeProvider.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.jgss.wrapper; + +import java.util.HashMap; +import java.security.Provider; +import java.security.AccessController; +import java.security.PrivilegedAction; +import org.ietf.jgss.Oid; +import sun.security.action.PutAllAction; + +/** + * Defines the Sun NativeGSS provider for plugging in the + * native GSS mechanisms to Java GSS. + * + * List of supported mechanisms depends on the local + * machine configuration. + * + * @author Yu-Ching Valerie Peng + */ + +public final class SunNativeProvider extends Provider { + + private static final long serialVersionUID = -238911724858694204L; + + private static final String NAME = "SunNativeGSS"; + private static final String INFO = "Sun Native GSS provider"; + private static final String MF_CLASS = + "sun.security.jgss.wrapper.NativeGSSFactory"; + private static final String LIB_PROP = "sun.security.jgss.lib"; + private static final String DEBUG_PROP = "sun.security.nativegss.debug"; + private static HashMap MECH_MAP; + static final Provider INSTANCE = new SunNativeProvider(); + static boolean DEBUG; + static void debug(String message) { + if (DEBUG) { + if (message == null) { + throw new NullPointerException(); + } + System.out.println(NAME + ": " + message); + } + } + + static { + MECH_MAP = + AccessController.doPrivileged( + new PrivilegedAction>() { + public HashMap run() { + DEBUG = Boolean.parseBoolean + (System.getProperty(DEBUG_PROP)); + try { + System.loadLibrary("j2gss"); + } catch (Error err) { + debug("No j2gss library found!"); + if (DEBUG) err.printStackTrace(); + return null; + } + String gssLibs[] = new String[0]; + String defaultLib = System.getProperty(LIB_PROP); + if (defaultLib == null || defaultLib.trim().equals("")) { + String osname = System.getProperty("os.name"); + if (osname.startsWith("SunOS")) { + gssLibs = new String[]{ "libgss.so" }; + } else if (osname.startsWith("Linux")) { + gssLibs = new String[]{ + "libgssapi.so", + "libgssapi_krb5.so", + "libgssapi_krb5.so.2", + }; + } else if (osname.contains("OS X")) { + gssLibs = new String[]{ + "libgssapi_krb5.dylib", + "/usr/lib/sasl2/libgssapiv2.2.so", + }; + } + } else { + gssLibs = new String[]{ defaultLib }; + } + for (String libName: gssLibs) { + if (GSSLibStub.init(libName, DEBUG)) { + debug("Loaded GSS library: " + libName); + Oid[] mechs = GSSLibStub.indicateMechs(); + HashMap map = + new HashMap(); + for (int i = 0; i < mechs.length; i++) { + debug("Native MF for " + mechs[i]); + map.put("GssApiMechanism." + mechs[i], + MF_CLASS); + } + return map; + } + } + return null; + } + }); + } + + public SunNativeProvider() { + /* We are the Sun NativeGSS provider */ + super(NAME, 1.8d, INFO); + + if (MECH_MAP != null) { + AccessController.doPrivileged(new PutAllAction(this, MECH_MAP)); + } + } +} diff --git a/src/sun/security/krb5/Asn1Exception.java b/src/sun/security/krb5/Asn1Exception.java new file mode 100644 index 00000000..7899a571 --- /dev/null +++ b/src/sun/security/krb5/Asn1Exception.java @@ -0,0 +1,41 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +public class Asn1Exception extends KrbException { + + private static final long serialVersionUID = 8291288984575084132L; + + public Asn1Exception(int i) { + super(i); + } + +} diff --git a/src/sun/security/krb5/Checksum.java b/src/sun/security/krb5/Checksum.java new file mode 100644 index 00000000..6a7f4912 --- /dev/null +++ b/src/sun/security/krb5/Checksum.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import java.util.Arrays; +import sun.security.util.*; +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * This class encapsulates the concept of a Kerberos checksum. + */ +public class Checksum { + + private int cksumType; + private byte[] checksum; + + // ----------------------------------------------+-------------+----------- + // Checksum type |sumtype |checksum + // |value | size + // ----------------------------------------------+-------------+----------- + public static final int CKSUMTYPE_NULL = 0; // 0 + public static final int CKSUMTYPE_CRC32 = 1; // 4 + public static final int CKSUMTYPE_RSA_MD4 = 2; // 16 + public static final int CKSUMTYPE_RSA_MD4_DES = 3; // 24 + public static final int CKSUMTYPE_DES_MAC = 4; // 16 + public static final int CKSUMTYPE_DES_MAC_K = 5; // 8 + public static final int CKSUMTYPE_RSA_MD4_DES_K = 6; // 16 + public static final int CKSUMTYPE_RSA_MD5 = 7; // 16 + public static final int CKSUMTYPE_RSA_MD5_DES = 8; // 24 + + // draft-ietf-krb-wg-crypto-07.txt + public static final int CKSUMTYPE_HMAC_SHA1_DES3_KD = 12; // 20 + + // draft-raeburn-krb-rijndael-krb-07.txt + public static final int CKSUMTYPE_HMAC_SHA1_96_AES128 = 15; // 96 + public static final int CKSUMTYPE_HMAC_SHA1_96_AES256 = 16; // 96 + + // draft-brezak-win2k-krb-rc4-hmac-04.txt + public static final int CKSUMTYPE_HMAC_MD5_ARCFOUR = -138; + + static int CKSUMTYPE_DEFAULT; + static int SAFECKSUMTYPE_DEFAULT; + + private static boolean DEBUG = Krb5.DEBUG; + static { + initStatic(); + } + + public static void initStatic() { + String temp = null; + Config cfg = null; + try { + cfg = Config.getInstance(); + temp = cfg.get("libdefaults", "default_checksum"); + if (temp != null) + { + CKSUMTYPE_DEFAULT = Config.getType(temp); + } else { + /* + * If the default checksum is not + * specified in the configuration we + * set it to RSA_MD5. We follow the MIT and + * SEAM implementation. + */ + CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5; + } + } catch (Exception exc) { + if (DEBUG) { + System.out.println("Exception in getting default checksum "+ + "value from the configuration " + + "Setting default checksum to be RSA-MD5"); + exc.printStackTrace(); + } + CKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5; + } + + + try { + temp = cfg.get("libdefaults", "safe_checksum_type"); + if (temp != null) + { + SAFECKSUMTYPE_DEFAULT = Config.getType(temp); + } else { + SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES; + } + } catch (Exception exc) { + if (DEBUG) { + System.out.println("Exception in getting safe default " + + "checksum value " + + "from the configuration Setting " + + "safe default checksum to be RSA-MD5"); + exc.printStackTrace(); + } + SAFECKSUMTYPE_DEFAULT = CKSUMTYPE_RSA_MD5_DES; + } + } + + /** + * Constructs a new Checksum using the raw data and type. + * @data the byte array of checksum. + * @new_cksumType the type of checksum. + * + */ + // used in InitialToken + public Checksum(byte[] data, int new_cksumType) { + cksumType = new_cksumType; + checksum = data; + } + + /** + * Constructs a new Checksum by calculating the checksum over the data + * using specified checksum type. + * @new_cksumType the type of checksum. + * @data the data that needs to be performed a checksum calculation on. + */ + public Checksum(int new_cksumType, byte[] data) + throws KdcErrException, KrbCryptoException { + + cksumType = new_cksumType; + CksumType cksumEngine = CksumType.getInstance(cksumType); + if (!cksumEngine.isSafe()) { + checksum = cksumEngine.calculateChecksum(data, data.length); + } else { + throw new KdcErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM); + } + } + + /** + * Constructs a new Checksum by calculating the keyed checksum + * over the data using specified checksum type. + * @new_cksumType the type of checksum. + * @data the data that needs to be performed a checksum calculation on. + */ + // KrbSafe, KrbTgsReq + public Checksum(int new_cksumType, byte[] data, + EncryptionKey key, int usage) + throws KdcErrException, KrbApErrException, KrbCryptoException { + cksumType = new_cksumType; + CksumType cksumEngine = CksumType.getInstance(cksumType); + if (!cksumEngine.isSafe()) + throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM); + checksum = + cksumEngine.calculateKeyedChecksum(data, + data.length, + key.getBytes(), + usage); + } + + /** + * Verifies the keyed checksum over the data passed in. + */ + public boolean verifyKeyedChecksum(byte[] data, EncryptionKey key, + int usage) + throws KdcErrException, KrbApErrException, KrbCryptoException { + CksumType cksumEngine = CksumType.getInstance(cksumType); + if (!cksumEngine.isSafe()) + throw new KrbApErrException(Krb5.KRB_AP_ERR_INAPP_CKSUM); + return cksumEngine.verifyKeyedChecksum(data, + data.length, + key.getBytes(), + checksum, + usage); + } + + /* + public Checksum(byte[] data) throws KdcErrException, KrbCryptoException { + this(Checksum.CKSUMTYPE_DEFAULT, data); + } + */ + + boolean isEqual(Checksum cksum) throws KdcErrException { + if (cksumType != cksum.cksumType) + return false; + CksumType cksumEngine = CksumType.getInstance(cksumType); + return CksumType.isChecksumEqual(checksum, cksum.checksum); + } + + /** + * Constructs an instance of Checksum from an ASN.1 encoded representation. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 + * encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + private Checksum(DerValue encoding) throws Asn1Exception, IOException { + DerValue der; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x00) { + cksumType = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x01) { + checksum = der.getData().getOctetString(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + if (encoding.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes a Checksum object. + *

    + * Checksum ::= SEQUENCE { + * cksumtype [0] Int32, + * checksum [1] OCTET STRING + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + * @return byte array of enocded Checksum. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(cksumType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putOctetString(checksum); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + + /** + * Parse (unmarshal) a checksum object from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + * @param data the Der input stream value, which contains one or more + * marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of Checksum. + * + */ + public static Checksum parse(DerInputStream data, + byte explicitTag, boolean optional) + throws Asn1Exception, IOException { + + if ((optional) && + (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) { + return null; + } + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new Checksum(subDer); + } + } + + /** + * Returns the raw bytes of the checksum, not in ASN.1 encoded form. + */ + public final byte[] getBytes() { + return checksum; + } + + public final int getType() { + return cksumType; + } + + @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Checksum)) { + return false; + } + + try { + return isEqual((Checksum)obj); + } catch (KdcErrException kee) { + return false; + } + } + + @Override public int hashCode() { + int result = 17; + result = 37 * result + cksumType; + if (checksum != null) { + result = 37 * result + Arrays.hashCode(checksum); + } + return result; + } +} diff --git a/src/sun/security/krb5/Config.java b/src/sun/security/krb5/Config.java new file mode 100644 index 00000000..66a8145c --- /dev/null +++ b/src/sun/security/krb5/Config.java @@ -0,0 +1,1241 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ +package sun.security.krb5; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Hashtable; +import java.util.Vector; +import java.util.ArrayList; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.IOException; +import java.util.StringTokenizer; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import sun.net.dns.ResolverConfiguration; +import sun.security.krb5.internal.crypto.EType; +import sun.security.krb5.internal.Krb5; + +/** + * This class maintains key-value pairs of Kerberos configurable constants + * from configuration file or from user specified system properties. + */ + +public class Config { + + /* + * Only allow a single instance of Config. + */ + private static Config singleton = null; + + /* + * Hashtable used to store configuration information. + */ + private Hashtable stanzaTable = new Hashtable<>(); + + private static boolean DEBUG = Krb5.DEBUG; + + // these are used for hexdecimal calculation. + private static final int BASE16_0 = 1; + private static final int BASE16_1 = 16; + private static final int BASE16_2 = 16 * 16; + private static final int BASE16_3 = 16 * 16 * 16; + + /** + * Specified by system properties. Must be both null or non-null. + */ + private final String defaultRealm; + private final String defaultKDC; + + // used for native interface + private static native String getWindowsDirectory(boolean isSystem); + + + /** + * Gets an instance of Config class. One and only one instance (the + * singleton) is returned. + * + * @exception KrbException if error occurs when constructing a Config + * instance. Possible causes would be either of java.security.krb5.realm or + * java.security.krb5.kdc not specified, error reading configuration file. + */ + public static synchronized Config getInstance() throws KrbException { + if (singleton == null) { + singleton = new Config(); + } + return singleton; + } + + /** + * Refresh and reload the Configuration. This could involve, + * for example reading the Configuration file again or getting + * the java.security.krb5.* system properties again. This method + * also tries its best to update static fields in other classes + * that depend on the configuration. + * + * @exception KrbException if error occurs when constructing a Config + * instance. Possible causes would be either of java.security.krb5.realm or + * java.security.krb5.kdc not specified, error reading configuration file. + */ + + public static synchronized void refresh() throws KrbException { + singleton = new Config(); + KdcComm.initStatic(); + EType.initStatic(); + Checksum.initStatic(); + } + + + private static boolean isMacosLionOrBetter() { + // split the "10.x.y" version number + String osname = getProperty("os.name"); + if (!osname.contains("OS X")) { + return false; + } + + String osVersion = getProperty("os.version"); + String[] fragments = osVersion.split("\\."); + + // sanity check the "10." part of the version + if (!fragments[0].equals("10")) return false; + if (fragments.length < 2) return false; + + // check if Mac OS X 10.7(.y) + try { + int minorVers = Integer.parseInt(fragments[1]); + if (minorVers >= 7) return true; + } catch (NumberFormatException e) { + // was not an integer + } + + return false; + } + + /** + * Private constructor - can not be instantiated externally. + */ + private Config() throws KrbException { + /* + * If either one system property is specified, we throw exception. + */ + String tmp = getProperty("java.security.krb5.kdc"); + if (tmp != null) { + // The user can specify a list of kdc hosts separated by ":" + defaultKDC = tmp.replace(':', ' '); + } else { + defaultKDC = null; + } + defaultRealm = getProperty("java.security.krb5.realm"); + if ((defaultKDC == null && defaultRealm != null) || + (defaultRealm == null && defaultKDC != null)) { + throw new KrbException + ("System property java.security.krb5.kdc and " + + "java.security.krb5.realm both must be set or " + + "neither must be set."); + } + + // Always read the Kerberos configuration file + try { + List configFile; + String fileName = getJavaFileName(); + if (fileName != null) { + configFile = loadConfigFile(fileName); + stanzaTable = parseStanzaTable(configFile); + if (DEBUG) { + System.out.println("Loaded from Java config"); + } + } else { + boolean found = false; + if (isMacosLionOrBetter()) { + try { + stanzaTable = SCDynamicStoreConfig.getConfig(); + if (DEBUG) { + System.out.println("Loaded from SCDynamicStoreConfig"); + } + found = true; + } catch (IOException ioe) { + // OK. Will go on with file + } + } + if (!found) { + fileName = getNativeFileName(); + configFile = loadConfigFile(fileName); + stanzaTable = parseStanzaTable(configFile); + if (DEBUG) { + System.out.println("Loaded from native config"); + } + } + } + } catch (IOException ioe) { + // I/O error, mostly like krb5.conf missing. + // No problem. We'll use DNS or system property etc. + } + } + + /** + * Gets the last-defined string value for the specified keys. + * @param keys the keys, as an array from section name, sub-section names + * (if any), to value name. + * @return the value. When there are multiple values for the same key, + * returns the last one. {@code null} is returned if not all the keys are + * defined. For example, {@code get("libdefaults", "forwardable")} will + * return null if "forwardable" is not defined in [libdefaults], and + * {@code get("realms", "R", "kdc")} will return null if "R" is not + * defined in [realms] or "kdc" is not defined for "R". + * @throws IllegalArgumentException if any of the keys is illegal, either + * because a key not the last one is not a (sub)section name or the last + * key is still a section name. For example, {@code get("libdefaults")} + * throws this exception because [libdefaults] is a section name instead of + * a value name, and {@code get("libdefaults", "forwardable", "tail")} + * also throws this exception because "forwardable" is already a value name + * and has no sub-key at all (given "forwardable" is defined, otherwise, + * this method has no knowledge if it's a value name or a section name), + */ + public String get(String... keys) { + Vector v = getString0(keys); + if (v == null) return null; + return v.lastElement(); + } + + /** + * Gets all values for the specified keys. + * @throws IllegalArgumentException if any of the keys is illegal + * (See {@link #get}) + */ + public String getAll(String... keys) { + Vector v = getString0(keys); + if (v == null) return null; + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String s: v) { + if (first) { + sb.append(s); + first = false; + } else { + sb.append(' ').append(s); + } + } + return sb.toString(); + } + + /** + * Returns true if keys exists, can be either final string(s) or sub-stanza + * @throws IllegalArgumentException if any of the keys is illegal + * (See {@link #get}) + */ + public boolean exists(String... keys) { + return get0(keys) != null; + } + + // Returns final string value(s) for given keys. + @SuppressWarnings("unchecked") + private Vector getString0(String... keys) { + try { + return (Vector)get0(keys); + } catch (ClassCastException cce) { + throw new IllegalArgumentException(cce); + } + } + + // Internal method. Returns the value for keys, which can be a sub-stanza + // or final string value(s). + // The only method (except for toString) that reads stanzaTable directly. + @SuppressWarnings("unchecked") + private Object get0(String... keys) { + Object current = stanzaTable; + try { + for (String key: keys) { + current = ((Hashtable)current).get(key); + if (current == null) return null; + } + return current; + } catch (ClassCastException cce) { + throw new IllegalArgumentException(cce); + } + } + + /** + * Gets the int value for the specified keys. + * @param keys the keys + * @return the int value, Integer.MIN_VALUE is returned if it cannot be + * found or the value is not a legal integer. + * @throw IllegalArgumentException if any of the keys is illegal + * @see #get(String[]) + */ + public int getIntValue(String... keys) { + String result = get(keys); + int value = Integer.MIN_VALUE; + if (result != null) { + try { + value = parseIntValue(result); + } catch (NumberFormatException e) { + if (DEBUG) { + System.out.println("Exception in getting value of " + + Arrays.toString(keys) + " " + + e.getMessage()); + System.out.println("Setting " + Arrays.toString(keys) + + " to minimum value"); + } + value = Integer.MIN_VALUE; + } + } + return value; + } + + /** + * Gets the boolean value for the specified keys. + * @param keys the keys + * @return the boolean value, false is returned if it cannot be + * found or the value is not "true" (case insensitive). + * @throw IllegalArgumentException if any of the keys is illegal + * @see #get(String[]) + */ + public boolean getBooleanValue(String... keys) { + String val = get(keys); + if (val != null && val.equalsIgnoreCase("true")) { + return true; + } else { + return false; + } + } + + /** + * Parses a string to an integer. The convertible strings include the + * string representations of positive integers, negative integers, and + * hex decimal integers. Valid inputs are, e.g., -1234, +1234, + * 0x40000. + * + * @param input the String to be converted to an Integer. + * @return an numeric value represented by the string + * @exception NumberFormationException if the String does not contain a + * parsable integer. + */ + private int parseIntValue(String input) throws NumberFormatException { + int value = 0; + if (input.startsWith("+")) { + String temp = input.substring(1); + return Integer.parseInt(temp); + } else if (input.startsWith("0x")) { + String temp = input.substring(2); + char[] chars = temp.toCharArray(); + if (chars.length > 8) { + throw new NumberFormatException(); + } else { + for (int i = 0; i < chars.length; i++) { + int index = chars.length - i - 1; + switch (chars[i]) { + case '0': + value += 0; + break; + case '1': + value += 1 * getBase(index); + break; + case '2': + value += 2 * getBase(index); + break; + case '3': + value += 3 * getBase(index); + break; + case '4': + value += 4 * getBase(index); + break; + case '5': + value += 5 * getBase(index); + break; + case '6': + value += 6 * getBase(index); + break; + case '7': + value += 7 * getBase(index); + break; + case '8': + value += 8 * getBase(index); + break; + case '9': + value += 9 * getBase(index); + break; + case 'a': + case 'A': + value += 10 * getBase(index); + break; + case 'b': + case 'B': + value += 11 * getBase(index); + break; + case 'c': + case 'C': + value += 12 * getBase(index); + break; + case 'd': + case 'D': + value += 13 * getBase(index); + break; + case 'e': + case 'E': + value += 14 * getBase(index); + break; + case 'f': + case 'F': + value += 15 * getBase(index); + break; + default: + throw new NumberFormatException("Invalid numerical format"); + } + } + } + if (value < 0) { + throw new NumberFormatException("Data overflow."); + } + } else { + value = Integer.parseInt(input); + } + return value; + } + + private int getBase(int i) { + int result = 16; + switch (i) { + case 0: + result = BASE16_0; + break; + case 1: + result = BASE16_1; + break; + case 2: + result = BASE16_2; + break; + case 3: + result = BASE16_3; + break; + default: + for (int j = 1; j < i; j++) { + result *= 16; + } + } + return result; + } + + /** + * Reads lines to the memory from the configuration file. + * + * Configuration file contains information about the default realm, + * ticket parameters, location of the KDC and the admin server for + * known realms, etc. The file is divided into sections. Each section + * contains one or more name/value pairs with one pair per line. A + * typical file would be: + *

    +     * [libdefaults]
    +     *          default_realm = EXAMPLE.COM
    +     *          default_tgs_enctypes = des-cbc-md5
    +     *          default_tkt_enctypes = des-cbc-md5
    +     * [realms]
    +     *          EXAMPLE.COM = {
    +     *                  kdc = kerberos.example.com
    +     *                  kdc = kerberos-1.example.com
    +     *                  admin_server = kerberos.example.com
    +     *                  }
    +     *          SAMPLE_COM = {
    +     *                  kdc = orange.sample.com
    +     *                  admin_server = orange.sample.com
    +     *                  }
    +     * [domain_realm]
    +     *          blue.sample.com = TEST.SAMPLE.COM
    +     *          .backup.com     = EXAMPLE.COM
    +     * 
    + * @return an ordered list of strings representing the config file after + * some initial processing, including:
      + *
    1. Comment lines and empty lines are removed + *
    2. "{" not at the end of a line is appended to the previous line + *
    3. The content of a section is also placed between "{" and "}". + *
    4. Lines are trimmed
    + * @throws IOException if there is an I/O error + * @throws KrbException if there is a file format error + */ + private List loadConfigFile(final String fileName) + throws IOException, KrbException { + try { + List v = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new InputStreamReader( + AccessController.doPrivileged( + new PrivilegedExceptionAction () { + public FileInputStream run() throws IOException { + return new FileInputStream(fileName); + } + })))) { + String line; + String previous = null; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.startsWith("#") || line.isEmpty()) { + // ignore comments and blank line + // Comments start with #. + continue; + } + // In practice, a subsection might look like: + // [realms] + // EXAMPLE.COM = + // { + // kdc = kerberos.example.com + // ... + // } + // Before parsed into stanza table, it needs to be + // converted into a canonicalized style (no indent): + // realms = { + // EXAMPLE.COM = { + // kdc = kerberos.example.com + // ... + // } + // } + // + if (line.startsWith("[")) { + if (!line.endsWith("]")) { + throw new KrbException("Illegal config content:" + + line); + } + if (previous != null) { + v.add(previous); + v.add("}"); + } + String title = line.substring( + 1, line.length()-1).trim(); + if (title.isEmpty()) { + throw new KrbException("Illegal config content:" + + line); + } + previous = title + " = {"; + } else if (line.startsWith("{")) { + if (previous == null) { + throw new KrbException( + "Config file should not start with \"{\""); + } + previous += " {"; + if (line.length() > 1) { + // { and content on the same line + v.add(previous); + previous = line.substring(1).trim(); + } + } else { + // Lines before the first section are ignored + if (previous != null) { + v.add(previous); + previous = line; + } + } + } + if (previous != null) { + v.add(previous); + v.add("}"); + } + } + return v; + } catch (java.security.PrivilegedActionException pe) { + throw (IOException)pe.getException(); + } + } + + /** + * Parses stanza names and values from configuration file to + * stanzaTable (Hashtable). Hashtable key would be stanza names, + * (libdefaults, realms, domain_realms, etc), and the hashtable value + * would be another hashtable which contains the key-value pairs under + * a stanza name. The value of this sub-hashtable can be another hashtable + * containing another sub-sub-section or a vector of strings for + * final values (even if there is only one value defined). + *

    + * For duplicates section names, the latter overwrites the former. For + * duplicate value names, the values are in a vector in its appearing order. + * + * Please note that this behavior is Java traditional. and it is + * not the same as the MIT krb5 behavior, where:

      + *
    1. Duplicated root sections will be merged + *
    2. For duplicated sub-sections, the former overwrites the latter + *
    3. Duplicate keys for values are always saved in a vector + *
    + * @param v the strings in the file, never null, might be empty + * @throws KrbException if there is a file format error + */ + @SuppressWarnings("unchecked") + private Hashtable parseStanzaTable(List v) + throws KrbException { + Hashtable current = stanzaTable; + for (String line: v) { + // There are 3 kinds of lines + // 1. a = b + // 2. a = { + // 3. } + if (line.equals("}")) { + // Go back to parent, see below + current = (Hashtable)current.remove(" PARENT "); + if (current == null) { + throw new KrbException("Unmatched close brace"); + } + } else { + int pos = line.indexOf('='); + if (pos < 0) { + throw new KrbException("Illegal config content:" + line); + } + String key = line.substring(0, pos).trim(); + String value = trimmed(line.substring(pos+1)); + if (value.equals("{")) { + Hashtable subTable; + if (current == stanzaTable) { + key = key.toLowerCase(Locale.US); + } + subTable = new Hashtable<>(); + current.put(key, subTable); + // A special entry for its parent. Put whitespaces around, + // so will never be confused with a normal key + subTable.put(" PARENT ", current); + current = subTable; + } else { + Vector values; + if (current.containsKey(key)) { + Object obj = current.get(key); + // If a key first shows as a section and then a value, + // this is illegal. However, we haven't really forbid + // first value then section, which the final result + // is a section. + if (!(obj instanceof Vector)) { + throw new KrbException("Key " + key + + "used for both value and section"); + } + values = (Vector)current.get(key); + } else { + values = new Vector(); + current.put(key, values); + } + values.add(value); + } + } + } + if (current != stanzaTable) { + throw new KrbException("Not closed"); + } + return current; + } + + /** + * Gets the default Java configuration file name. + * + * If the system property "java.security.krb5.conf" is defined, we'll + * use its value, no matter if the file exists or not. Otherwise, we + * will look at $JAVA_HOME/lib/security directory with "krb5.conf" name, + * and return it if the file exists. + * + * The method returns null if it cannot find a Java config file. + */ + private String getJavaFileName() { + String name = getProperty("java.security.krb5.conf"); + if (name == null) { + name = getProperty("java.home") + File.separator + + "lib" + File.separator + "security" + + File.separator + "krb5.conf"; + if (!fileExists(name)) { + name = null; + } + } + if (DEBUG) { + System.out.println("Java config name: " + name); + } + return name; + } + + /** + * Gets the default native configuration file name. + * + * Depending on the OS type, the method returns the default native + * kerberos config file name, which is at windows directory with + * the name of "krb5.ini" for Windows, /etc/krb5/krb5.conf for Solaris, + * /etc/krb5.conf otherwise. Mac OSX X has a different file name. + * + * Note: When the Terminal Service is started in Windows (from 2003), + * there are two kinds of Windows directories: A system one (say, + * C:\Windows), and a user-private one (say, C:\Users\Me\Windows). + * We will first look for krb5.ini in the user-private one. If not + * found, try the system one instead. + * + * This method will always return a non-null non-empty file name, + * even if that file does not exist. + */ + private String getNativeFileName() { + String name = null; + String osname = getProperty("os.name"); + if (osname.startsWith("Windows")) { + try { + Credentials.ensureLoaded(); + } catch (Exception e) { + // ignore exceptions + } + if (Credentials.alreadyLoaded) { + String path = getWindowsDirectory(false); + if (path != null) { + if (path.endsWith("\\")) { + path = path + "krb5.ini"; + } else { + path = path + "\\krb5.ini"; + } + if (fileExists(path)) { + name = path; + } + } + if (name == null) { + path = getWindowsDirectory(true); + if (path != null) { + if (path.endsWith("\\")) { + path = path + "krb5.ini"; + } else { + path = path + "\\krb5.ini"; + } + name = path; + } + } + } + if (name == null) { + name = "c:\\winnt\\krb5.ini"; + } + } else if (osname.startsWith("SunOS")) { + name = "/etc/krb5/krb5.conf"; + } else if (osname.contains("OS X")) { + name = findMacosConfigFile(); + } else { + name = "/etc/krb5.conf"; + } + if (DEBUG) { + System.out.println("Native config name: " + name); + } + return name; + } + + private static String getProperty(String property) { + return AccessController.doPrivileged( + new sun.security.action.GetPropertyAction(property)); + } + + private String findMacosConfigFile() { + String userHome = getProperty("user.home"); + final String PREF_FILE = "/Library/Preferences/edu.mit.Kerberos"; + String userPrefs = userHome + PREF_FILE; + + if (fileExists(userPrefs)) { + return userPrefs; + } + + if (fileExists(PREF_FILE)) { + return PREF_FILE; + } + + return "/etc/krb5.conf"; + } + + private static String trimmed(String s) { + s = s.trim(); + if (s.isEmpty()) return s; + if (s.charAt(0) == '"' && s.charAt(s.length()-1) == '"' || + s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\'') { + s = s.substring(1, s.length()-1).trim(); + } + return s; + } + + /** + * For testing purpose. This method lists all information being parsed from + * the configuration file to the hashtable. + */ + public void listTable() { + System.out.println(this); + } + + /** + * Returns all etypes specified in krb5.conf for the given configName, + * or all the builtin defaults. This result is always non-empty. + * If no etypes are found, an exception is thrown. + */ + public int[] defaultEtype(String configName) throws KrbException { + String default_enctypes; + default_enctypes = get("libdefaults", configName); + int[] etype; + if (default_enctypes == null) { + if (DEBUG) { + System.out.println("Using builtin default etypes for " + + configName); + } + etype = EType.getBuiltInDefaults(); + } else { + String delim = " "; + StringTokenizer st; + for (int j = 0; j < default_enctypes.length(); j++) { + if (default_enctypes.substring(j, j + 1).equals(",")) { + // only two delimiters are allowed to use + // according to Kerberos DCE doc. + delim = ","; + break; + } + } + st = new StringTokenizer(default_enctypes, delim); + int len = st.countTokens(); + ArrayList ls = new ArrayList<>(len); + int type; + for (int i = 0; i < len; i++) { + type = Config.getType(st.nextToken()); + if (type != -1 && EType.isSupported(type)) { + ls.add(type); + } + } + if (ls.isEmpty()) { + throw new KrbException("no supported default etypes for " + + configName); + } else { + etype = new int[ls.size()]; + for (int i = 0; i < etype.length; i++) { + etype[i] = ls.get(i); + } + } + } + + if (DEBUG) { + System.out.print("default etypes for " + configName + ":"); + for (int i = 0; i < etype.length; i++) { + System.out.print(" " + etype[i]); + } + System.out.println("."); + } + return etype; + } + + + /** + * Get the etype and checksum value for the specified encryption and + * checksum type. + * + */ + /* + * This method converts the string representation of encryption type and + * checksum type to int value that can be later used by EType and + * Checksum classes. + */ + public static int getType(String input) { + int result = -1; + if (input == null) { + return result; + } + if (input.startsWith("d") || (input.startsWith("D"))) { + if (input.equalsIgnoreCase("des-cbc-crc")) { + result = EncryptedData.ETYPE_DES_CBC_CRC; + } else if (input.equalsIgnoreCase("des-cbc-md5")) { + result = EncryptedData.ETYPE_DES_CBC_MD5; + } else if (input.equalsIgnoreCase("des-mac")) { + result = Checksum.CKSUMTYPE_DES_MAC; + } else if (input.equalsIgnoreCase("des-mac-k")) { + result = Checksum.CKSUMTYPE_DES_MAC_K; + } else if (input.equalsIgnoreCase("des-cbc-md4")) { + result = EncryptedData.ETYPE_DES_CBC_MD4; + } else if (input.equalsIgnoreCase("des3-cbc-sha1") || + input.equalsIgnoreCase("des3-hmac-sha1") || + input.equalsIgnoreCase("des3-cbc-sha1-kd") || + input.equalsIgnoreCase("des3-cbc-hmac-sha1-kd")) { + result = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD; + } + } else if (input.startsWith("a") || (input.startsWith("A"))) { + // AES + if (input.equalsIgnoreCase("aes128-cts") || + input.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) { + result = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96; + } else if (input.equalsIgnoreCase("aes256-cts") || + input.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) { + result = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96; + // ARCFOUR-HMAC + } else if (input.equalsIgnoreCase("arcfour-hmac") || + input.equalsIgnoreCase("arcfour-hmac-md5")) { + result = EncryptedData.ETYPE_ARCFOUR_HMAC; + } + // RC4-HMAC + } else if (input.equalsIgnoreCase("rc4-hmac")) { + result = EncryptedData.ETYPE_ARCFOUR_HMAC; + } else if (input.equalsIgnoreCase("CRC32")) { + result = Checksum.CKSUMTYPE_CRC32; + } else if (input.startsWith("r") || (input.startsWith("R"))) { + if (input.equalsIgnoreCase("rsa-md5")) { + result = Checksum.CKSUMTYPE_RSA_MD5; + } else if (input.equalsIgnoreCase("rsa-md5-des")) { + result = Checksum.CKSUMTYPE_RSA_MD5_DES; + } + } else if (input.equalsIgnoreCase("hmac-sha1-des3-kd")) { + result = Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD; + } else if (input.equalsIgnoreCase("hmac-sha1-96-aes128")) { + result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128; + } else if (input.equalsIgnoreCase("hmac-sha1-96-aes256")) { + result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256; + } else if (input.equalsIgnoreCase("hmac-md5-rc4") || + input.equalsIgnoreCase("hmac-md5-arcfour") || + input.equalsIgnoreCase("hmac-md5-enc")) { + result = Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR; + } else if (input.equalsIgnoreCase("NULL")) { + result = EncryptedData.ETYPE_NULL; + } + + return result; + } + + /** + * Resets the default kdc realm. + * We do not need to synchronize these methods since assignments are atomic + * + * This method was useless. Kept here in case some class still calls it. + */ + public void resetDefaultRealm(String realm) { + if (DEBUG) { + System.out.println(">>> Config try resetting default kdc " + realm); + } + } + + /** + * Check to use addresses in tickets + * use addresses if "no_addresses" or "noaddresses" is set to false + */ + public boolean useAddresses() { + boolean useAddr = false; + // use addresses if "no_addresses" is set to false + String value = get("libdefaults", "no_addresses"); + useAddr = (value != null && value.equalsIgnoreCase("false")); + if (useAddr == false) { + // use addresses if "noaddresses" is set to false + value = get("libdefaults", "noaddresses"); + useAddr = (value != null && value.equalsIgnoreCase("false")); + } + return useAddr; + } + + /** + * Check if need to use DNS to locate Kerberos services + */ + private boolean useDNS(String name) { + String value = get("libdefaults", name); + if (value == null) { + value = get("libdefaults", "dns_fallback"); + if ("false".equalsIgnoreCase(value)) { + return false; + } else { + return true; + } + } else { + return value.equalsIgnoreCase("true"); + } + } + + /** + * Check if need to use DNS to locate the KDC + */ + private boolean useDNS_KDC() { + return useDNS("dns_lookup_kdc"); + } + + /* + * Check if need to use DNS to locate the Realm + */ + private boolean useDNS_Realm() { + return useDNS("dns_lookup_realm"); + } + + /** + * Gets default realm. + * @throws KrbException where no realm can be located + * @return the default realm, always non null + */ + public String getDefaultRealm() throws KrbException { + if (defaultRealm != null) { + return defaultRealm; + } + Exception cause = null; + String realm = get("libdefaults", "default_realm"); + if ((realm == null) && useDNS_Realm()) { + // use DNS to locate Kerberos realm + try { + realm = getRealmFromDNS(); + } catch (KrbException ke) { + cause = ke; + } + } + if (realm == null) { + realm = AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public String run() { + String osname = System.getProperty("os.name"); + if (osname.startsWith("Windows")) { + return System.getenv("USERDNSDOMAIN"); + } + return null; + } + }); + } + if (realm == null) { + KrbException ke = new KrbException("Cannot locate default realm"); + if (cause != null) { + ke.initCause(cause); + } + throw ke; + } + return realm; + } + + /** + * Returns a list of KDC's with each KDC separated by a space + * + * @param realm the realm for which the KDC list is desired + * @throws KrbException if there's no way to find KDC for the realm + * @return the list of KDCs separated by a space, always non null + */ + public String getKDCList(String realm) throws KrbException { + if (realm == null) { + realm = getDefaultRealm(); + } + if (realm.equalsIgnoreCase(defaultRealm)) { + return defaultKDC; + } + Exception cause = null; + String kdcs = getAll("realms", realm, "kdc"); + if ((kdcs == null) && useDNS_KDC()) { + // use DNS to locate KDC + try { + kdcs = getKDCFromDNS(realm); + } catch (KrbException ke) { + cause = ke; + } + } + if (kdcs == null) { + kdcs = AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public String run() { + String osname = System.getProperty("os.name"); + if (osname.startsWith("Windows")) { + String logonServer = System.getenv("LOGONSERVER"); + if (logonServer != null + && logonServer.startsWith("\\\\")) { + logonServer = logonServer.substring(2); + } + return logonServer; + } + return null; + } + }); + } + if (kdcs == null) { + if (defaultKDC != null) { + return defaultKDC; + } + KrbException ke = new KrbException("Cannot locate KDC"); + if (cause != null) { + ke.initCause(cause); + } + throw ke; + } + return kdcs; + } + + /** + * Locate Kerberos realm using DNS + * + * @return the Kerberos realm + */ + private String getRealmFromDNS() throws KrbException { + // use DNS to locate Kerberos realm + String realm = null; + String hostName = null; + try { + hostName = InetAddress.getLocalHost().getCanonicalHostName(); + } catch (UnknownHostException e) { + KrbException ke = new KrbException(Krb5.KRB_ERR_GENERIC, + "Unable to locate Kerberos realm: " + e.getMessage()); + ke.initCause(e); + throw (ke); + } + // get the domain realm mapping from the configuration + String mapRealm = PrincipalName.mapHostToRealm(hostName); + if (mapRealm == null) { + // No match. Try search and/or domain in /etc/resolv.conf + List srchlist = ResolverConfiguration.open().searchlist(); + for (String domain: srchlist) { + realm = checkRealm(domain); + if (realm != null) { + break; + } + } + } else { + realm = checkRealm(mapRealm); + } + if (realm == null) { + throw new KrbException(Krb5.KRB_ERR_GENERIC, + "Unable to locate Kerberos realm"); + } + return realm; + } + + /** + * Check if the provided realm is the correct realm + * @return the realm if correct, or null otherwise + */ + private static String checkRealm(String mapRealm) { + if (DEBUG) { + System.out.println("getRealmFromDNS: trying " + mapRealm); + } + String[] records = null; + String newRealm = mapRealm; + while ((records == null) && (newRealm != null)) { + // locate DNS TXT record + records = KrbServiceLocator.getKerberosService(newRealm); + newRealm = Realm.parseRealmComponent(newRealm); + // if no DNS TXT records found, try again using sub-realm + } + if (records != null) { + for (int i = 0; i < records.length; i++) { + if (records[i].equalsIgnoreCase(mapRealm)) { + return records[i]; + } + } + } + return null; + } + + /** + * Locate KDC using DNS + * + * @param realm the realm for which the master KDC is desired + * @return the KDC + */ + private String getKDCFromDNS(String realm) throws KrbException { + // use DNS to locate KDC + String kdcs = ""; + String[] srvs = null; + // locate DNS SRV record using UDP + if (DEBUG) { + System.out.println("getKDCFromDNS using UDP"); + } + srvs = KrbServiceLocator.getKerberosService(realm, "_udp"); + if (srvs == null) { + // locate DNS SRV record using TCP + if (DEBUG) { + System.out.println("getKDCFromDNS using TCP"); + } + srvs = KrbServiceLocator.getKerberosService(realm, "_tcp"); + } + if (srvs == null) { + // no DNS SRV records + throw new KrbException(Krb5.KRB_ERR_GENERIC, + "Unable to locate KDC for realm " + realm); + } + if (srvs.length == 0) { + return null; + } + for (int i = 0; i < srvs.length; i++) { + kdcs += srvs[i].trim() + " "; + } + kdcs = kdcs.trim(); + if (kdcs.equals("")) { + return null; + } + return kdcs; + } + + private boolean fileExists(String name) { + return AccessController.doPrivileged( + new FileExistsAction(name)); + } + + static class FileExistsAction + implements java.security.PrivilegedAction { + + private String fileName; + + public FileExistsAction(String fileName) { + this.fileName = fileName; + } + + public Boolean run() { + return new File(fileName).exists(); + } + } + + // Shows the content of the Config object for debug purpose. + // + // { + // libdefaults = { + // default_realm = R + // } + // realms = { + // R = { + // kdc = [k1,k2] + // } + // } + // } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + toStringInternal("", stanzaTable, sb); + return sb.toString(); + } + private static void toStringInternal(String prefix, Object obj, + StringBuffer sb) { + if (obj instanceof String) { + // A string value, just print it + sb.append(obj).append('\n'); + } else if (obj instanceof Hashtable) { + // A table, start a new sub-section... + Hashtable tab = (Hashtable)obj; + sb.append("{\n"); + for (Object o: tab.keySet()) { + // ...indent, print "key = ", and + sb.append(prefix).append(" ").append(o).append(" = "); + // ...go recursively into value + toStringInternal(prefix + " ", tab.get(o), sb); + } + sb.append(prefix).append("}\n"); + } else if (obj instanceof Vector) { + // A vector of strings, print them inside [ and ] + Vector v = (Vector)obj; + sb.append("["); + boolean first = true; + for (Object o: v.toArray()) { + if (!first) sb.append(","); + sb.append(o); + first = false; + } + sb.append("]\n"); + } + } +} diff --git a/src/sun/security/krb5/Confounder.java b/src/sun/security/krb5/Confounder.java new file mode 100644 index 00000000..0b83e669 --- /dev/null +++ b/src/sun/security/krb5/Confounder.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import java.security.SecureRandom; + +public final class Confounder { + private static SecureRandom srand = new SecureRandom(); + + private Confounder() { // not instantiable + } + + public static byte[] bytes(int size) { + byte[] data = new byte[size]; + srand.nextBytes(data); + return data; + } + + public static int intValue() { + return srand.nextInt(); + } + + public static long longValue() { + return srand.nextLong(); + } +} diff --git a/src/sun/security/krb5/Credentials.java b/src/sun/security/krb5/Credentials.java new file mode 100644 index 00000000..80095ac9 --- /dev/null +++ b/src/sun/security/krb5/Credentials.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.ccache.CredentialsCache; +import sun.security.krb5.internal.crypto.EType; +import java.io.IOException; +import java.util.Date; +import java.util.Locale; +import java.net.InetAddress; + +/** + * This class encapsulates the concept of a Kerberos service + * credential. That includes a Kerberos ticket and an associated + * session key. + */ +public class Credentials { + + Ticket ticket; + PrincipalName client; + PrincipalName server; + EncryptionKey key; + TicketFlags flags; + KerberosTime authTime; + KerberosTime startTime; + KerberosTime endTime; + KerberosTime renewTill; + HostAddresses cAddr; + EncryptionKey serviceKey; + AuthorizationData authzData; + private static boolean DEBUG = Krb5.DEBUG; + private static CredentialsCache cache; + static boolean alreadyLoaded = false; + private static boolean alreadyTried = false; + + // Read native ticket with session key type in the given list + private static native Credentials acquireDefaultNativeCreds(int[] eTypes); + + public Credentials(Ticket new_ticket, + PrincipalName new_client, + PrincipalName new_server, + EncryptionKey new_key, + TicketFlags new_flags, + KerberosTime authTime, + KerberosTime new_startTime, + KerberosTime new_endTime, + KerberosTime renewTill, + HostAddresses cAddr, + AuthorizationData authzData) { + this(new_ticket, new_client, new_server, new_key, new_flags, + authTime, new_startTime, new_endTime, renewTill, cAddr); + this.authzData = authzData; + } + + public Credentials(Ticket new_ticket, + PrincipalName new_client, + PrincipalName new_server, + EncryptionKey new_key, + TicketFlags new_flags, + KerberosTime authTime, + KerberosTime new_startTime, + KerberosTime new_endTime, + KerberosTime renewTill, + HostAddresses cAddr) { + ticket = new_ticket; + client = new_client; + server = new_server; + key = new_key; + flags = new_flags; + this.authTime = authTime; + startTime = new_startTime; + endTime = new_endTime; + this.renewTill = renewTill; + this.cAddr = cAddr; + } + + public Credentials(byte[] encoding, + String client, + String server, + byte[] keyBytes, + int keyType, + boolean[] flags, + Date authTime, + Date startTime, + Date endTime, + Date renewTill, + InetAddress[] cAddrs) throws KrbException, IOException { + this(new Ticket(encoding), + new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL), + new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST), + new EncryptionKey(keyType, keyBytes), + (flags == null? null: new TicketFlags(flags)), + (authTime == null? null: new KerberosTime(authTime)), + (startTime == null? null: new KerberosTime(startTime)), + (endTime == null? null: new KerberosTime(endTime)), + (renewTill == null? null: new KerberosTime(renewTill)), + null); // caddrs are in the encoding at this point + } + + /** + * Acquires a service ticket for the specified service + * principal. If the service ticket is not already available, it + * obtains a new one from the KDC. + */ + /* + public Credentials(Credentials tgt, PrincipalName service) + throws KrbException { + } + */ + + public final PrincipalName getClient() { + return client; + } + + public final PrincipalName getServer() { + return server; + } + + public final EncryptionKey getSessionKey() { + return key; + } + + public final Date getAuthTime() { + if (authTime != null) { + return authTime.toDate(); + } else { + return null; + } + } + + public final Date getStartTime() { + if (startTime != null) + { + return startTime.toDate(); + } + return null; + } + + public final Date getEndTime() { + if (endTime != null) + { + return endTime.toDate(); + } + return null; + } + + public final Date getRenewTill() { + if (renewTill != null) + { + return renewTill.toDate(); + } + return null; + } + + public final boolean[] getFlags() { + if (flags == null) // Can be in a KRB-CRED + return null; + return flags.toBooleanArray(); + } + + public final InetAddress[] getClientAddresses() { + + if (cAddr == null) + return null; + + return cAddr.getInetAddresses(); + } + + public final byte[] getEncoded() { + byte[] retVal = null; + try { + retVal = ticket.asn1Encode(); + } catch (Asn1Exception e) { + if (DEBUG) + System.out.println(e); + } catch (IOException ioe) { + if (DEBUG) + System.out.println(ioe); + } + return retVal; + } + + public boolean isForwardable() { + return flags.get(Krb5.TKT_OPTS_FORWARDABLE); + } + + public boolean isRenewable() { + return flags.get(Krb5.TKT_OPTS_RENEWABLE); + } + + public Ticket getTicket() { + return ticket; + } + + public TicketFlags getTicketFlags() { + return flags; + } + + public AuthorizationData getAuthzData() { + return authzData; + } + /** + * Checks if the service ticket returned by the KDC has the OK-AS-DELEGATE + * flag set + * @return true if OK-AS_DELEGATE flag is set, otherwise, return false. + */ + public boolean checkDelegate() { + return flags.get(Krb5.TKT_OPTS_DELEGATE); + } + + /** + * Reset TKT_OPTS_DELEGATE to false, called at credentials acquirement + * when one of the cross-realm TGTs does not have the OK-AS-DELEGATE + * flag set. This info must be preservable and restorable through + * the Krb5Util.credsToTicket/ticketToCreds() methods so that even if + * the service ticket is cached it still remembers the cross-realm + * authentication result. + */ + public void resetDelegate() { + flags.set(Krb5.TKT_OPTS_DELEGATE, false); + } + + public Credentials renew() throws KrbException, IOException { + KDCOptions options = new KDCOptions(); + options.set(KDCOptions.RENEW, true); + /* + * Added here to pass KrbKdcRep.check:73 + */ + options.set(KDCOptions.RENEWABLE, true); + + return new KrbTgsReq(options, + this, + server, + null, // from + null, // till + null, // rtime + null, // eTypes + cAddr, + null, + null, + null).sendAndGetCreds(); + } + + /** + * Returns a TGT for the given client principal from a ticket cache. + * + * @param princ the client principal. A value of null means that the + * default principal name in the credentials cache will be used. + * @param ticketCache the path to the tickets file. A value + * of null will be accepted to indicate that the default + * path should be searched + * @returns the TGT credentials or null if none were found. If the tgt + * expired, it is the responsibility of the caller to determine this. + */ + public static Credentials acquireTGTFromCache(PrincipalName princ, + String ticketCache) + throws KrbException, IOException { + + if (ticketCache == null) { + // The default ticket cache on Windows and Mac is not a file. + String os = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("os.name")); + if (os.toUpperCase(Locale.ENGLISH).startsWith("WINDOWS") || + os.toUpperCase(Locale.ENGLISH).contains("OS X")) { + Credentials creds = acquireDefaultCreds(); + if (creds == null) { + if (DEBUG) { + System.out.println(">>> Found no TGT's in LSA"); + } + return null; + } + if (princ != null) { + if (creds.getClient().equals(princ)) { + if (DEBUG) { + System.out.println(">>> Obtained TGT from LSA: " + + creds); + } + return creds; + } else { + if (DEBUG) { + System.out.println(">>> LSA contains TGT for " + + creds.getClient() + + " not " + + princ); + } + return null; + } + } else { + if (DEBUG) { + System.out.println(">>> Obtained TGT from LSA: " + + creds); + } + return creds; + } + } + } + + /* + * Returns the appropriate cache. If ticketCache is null, it is the + * default cache otherwise it is the cache filename contained in it. + */ + CredentialsCache ccache = + CredentialsCache.getInstance(princ, ticketCache); + + if (ccache == null) { + return null; + } + + sun.security.krb5.internal.ccache.Credentials tgtCred = + ccache.getDefaultCreds(); + + if (tgtCred == null) { + return null; + } + + if (EType.isSupported(tgtCred.getEType())) { + return tgtCred.setKrbCreds(); + } else { + if (DEBUG) { + System.out.println( + ">>> unsupported key type found the default TGT: " + + tgtCred.getEType()); + } + return null; + } + } + + /** + * Acquires default credentials. + *
    The possible locations for default credentials cache is searched in + * the following order: + *
      + *
    1. The directory and cache file name specified by "KRB5CCNAME" system. + * property. + *
    2. The directory and cache file name specified by "KRB5CCNAME" + * environment variable. + *
    3. A cache file named krb5cc_{user.name} at {user.home} directory. + *
    + * @return a KrbCreds object if the credential is found, + * otherwise return null. + */ + + // this method is intentionally changed to not check if the caller's + // principal name matches cache file's principal name. + // It assumes that the GSS call has + // the privilege to access the default cache file. + + // This method is only called on Windows and Mac OS X, the native + // acquireDefaultNativeCreds is also available on these platforms. + public static synchronized Credentials acquireDefaultCreds() { + Credentials result = null; + + if (cache == null) { + cache = CredentialsCache.getInstance(); + } + if (cache != null) { + sun.security.krb5.internal.ccache.Credentials temp = + cache.getDefaultCreds(); + if (temp != null) { + if (DEBUG) { + System.out.println(">>> KrbCreds found the default ticket" + + " granting ticket in credential cache."); + } + if (EType.isSupported(temp.getEType())) { + result = temp.setKrbCreds(); + } else { + if (DEBUG) { + System.out.println( + ">>> unsupported key type found the default TGT: " + + temp.getEType()); + } + } + } + } + if (result == null) { + // Doesn't seem to be a default cache on this system or + // TGT has unsupported encryption type + + if (!alreadyTried) { + // See if there's any native code to load + try { + ensureLoaded(); + } catch (Exception e) { + if (DEBUG) { + System.out.println("Can not load credentials cache"); + e.printStackTrace(); + } + alreadyTried = true; + } + } + if (alreadyLoaded) { + // There is some native code + if (DEBUG) { + System.out.println(">> Acquire default native Credentials"); + } + try { + result = acquireDefaultNativeCreds( + EType.getDefaults("default_tkt_enctypes")); + } catch (KrbException ke) { + // when there is no default_tkt_enctypes. + } + } + } + return result; + } + + /** + * Acquires credentials for a specified service using initial credential. + * When the service has a different realm + * from the initial credential, we do cross-realm authentication + * - first, we use the current credential to get + * a cross-realm credential from the local KDC, then use that + * cross-realm credential to request service credential + * from the foreigh KDC. + * + * @param service the name of service principal using format + * components@realm + * @param ccreds client's initial credential. + * @exception IOException if an error occurs in reading the credentials + * cache + * @exception KrbException if an error occurs specific to Kerberos + * @return a Credentials object. + */ + + public static Credentials acquireServiceCreds(String service, + Credentials ccreds) + throws KrbException, IOException { + return CredentialsUtil.acquireServiceCreds(service, ccreds); + } + + public static Credentials acquireS4U2selfCreds(PrincipalName user, + Credentials ccreds) throws KrbException, IOException { + return CredentialsUtil.acquireS4U2selfCreds(user, ccreds); + } + + public static Credentials acquireS4U2proxyCreds(String service, + Ticket second, PrincipalName client, Credentials ccreds) + throws KrbException, IOException { + return CredentialsUtil.acquireS4U2proxyCreds( + service, second, client, ccreds); + } + + public CredentialsCache getCache() { + return cache; + } + + public EncryptionKey getServiceKey() { + return serviceKey; + } + + /* + * Prints out debug info. + */ + public static void printDebug(Credentials c) { + System.out.println(">>> DEBUG: ----Credentials----"); + System.out.println("\tclient: " + c.client.toString()); + System.out.println("\tserver: " + c.server.toString()); + System.out.println("\tticket: sname: " + c.ticket.sname.toString()); + if (c.startTime != null) { + System.out.println("\tstartTime: " + c.startTime.getTime()); + } + System.out.println("\tendTime: " + c.endTime.getTime()); + System.out.println(" ----Credentials end----"); + } + + + static void ensureLoaded() { + java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction () { + public Void run() { + if (System.getProperty("os.name").contains("OS X")) { + System.loadLibrary("osxkrb5"); + } else { + System.loadLibrary("w2k_lsa_auth"); + } + return null; + } + }); + alreadyLoaded = true; + } + + public String toString() { + StringBuffer buffer = new StringBuffer("Credentials:"); + buffer.append( "\n client=").append(client); + buffer.append( "\n server=").append(server); + if (authTime != null) { + buffer.append("\n authTime=").append(authTime); + } + if (startTime != null) { + buffer.append("\n startTime=").append(startTime); + } + buffer.append( "\n endTime=").append(endTime); + buffer.append( "\n renewTill=").append(renewTill); + buffer.append( "\n flags=").append(flags); + buffer.append( "\nEType (skey)=").append(key.getEType()); + buffer.append( "\n (tkt key)=").append(ticket.encPart.eType); + return buffer.toString(); + } + +} diff --git a/src/sun/security/krb5/EncryptedData.java b/src/sun/security/krb5/EncryptedData.java new file mode 100644 index 00000000..4c5cab78 --- /dev/null +++ b/src/sun/security/krb5/EncryptedData.java @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.util.*; +import sun.security.krb5.internal.crypto.*; +import sun.security.krb5.internal.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * This class encapsulates Kerberos encrypted data. It allows + * callers access to both the ASN.1 encoded form of the EncryptedData + * type as well as the raw cipher text. + */ + +public class EncryptedData implements Cloneable { + int eType; + Integer kvno; // optional + byte[] cipher; + byte[] plain; // not part of ASN.1 encoding + + // ----------------+-----------+----------+----------------+--------------- + // Encryption type |etype value|block size|minimum pad size|confounder size + // ----------------+-----------+----------+----------------+--------------- + public static final int + ETYPE_NULL = 0; // 1 0 0 + public static final int + ETYPE_DES_CBC_CRC = 1; // 8 4 8 + public static final int + ETYPE_DES_CBC_MD4 = 2; // 8 0 8 + public static final int + ETYPE_DES_CBC_MD5 = 3; // 8 0 8 + + // draft-brezak-win2k-krb-rc4-hmac-04.txt + public static final int + ETYPE_ARCFOUR_HMAC = 23; // 1 + // NOTE: the exportable RC4-HMAC is not supported; + // it is no longer a usable encryption type + public static final int + ETYPE_ARCFOUR_HMAC_EXP = 24; // 1 + + // draft-ietf-krb-wg-crypto-07.txt + public static final int + ETYPE_DES3_CBC_HMAC_SHA1_KD = 16; // 8 0 8 + + // draft-raeburn-krb-rijndael-krb-07.txt + public static final int + ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; // 16 0 16 + public static final int + ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; // 16 0 16 + + /* used by self */ + private EncryptedData() { + } + + public Object clone() { + EncryptedData new_encryptedData = new EncryptedData(); + new_encryptedData.eType = eType; + if (kvno != null) { + new_encryptedData.kvno = new Integer(kvno.intValue()); + } + if (cipher != null) { + new_encryptedData.cipher = new byte[cipher.length]; + System.arraycopy(cipher, 0, new_encryptedData.cipher, + 0, cipher.length); + } + return new_encryptedData; + } + + // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) + public EncryptedData( + int new_eType, + Integer new_kvno, + byte[] new_cipher) { + eType = new_eType; + kvno = new_kvno; + cipher = new_cipher; + } + + /* + // Not used. + public EncryptedData( + EncryptionKey key, + byte[] plaintext) + throws KdcErrException, KrbCryptoException { + EType etypeEngine = EType.getInstance(key.getEType()); + cipher = etypeEngine.encrypt(plaintext, key.getBytes()); + eType = key.getEType(); + kvno = key.getKeyVersionNumber(); + } + */ + + // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv + // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) + public EncryptedData( + EncryptionKey key, + byte[] plaintext, + int usage) + throws KdcErrException, KrbCryptoException { + EType etypeEngine = EType.getInstance(key.getEType()); + cipher = etypeEngine.encrypt(plaintext, key.getBytes(), usage); + eType = key.getEType(); + kvno = key.getKeyVersionNumber(); + } + + /* + // Not used. + public EncryptedData( + EncryptionKey key, + byte[] ivec, + byte[] plaintext) + throws KdcErrException, KrbCryptoException { + EType etypeEngine = EType.getInstance(key.getEType()); + cipher = etypeEngine.encrypt(plaintext, key.getBytes(), ivec); + eType = key.getEType(); + kvno = key.getKeyVersionNumber(); + } + */ + + /* + // Not used. + EncryptedData( + StringBuffer password, + byte[] plaintext) + throws KdcErrException, KrbCryptoException { + EncryptionKey key = new EncryptionKey(password); + EType etypeEngine = EType.getInstance(key.getEType()); + cipher = etypeEngine.encrypt(plaintext, key.getBytes()); + eType = key.getEType(); + kvno = key.getKeyVersionNumber(); + } + */ + public byte[] decrypt( + EncryptionKey key, int usage) + throws KdcErrException, KrbApErrException, KrbCryptoException { + if (eType != key.getEType()) { + throw new KrbCryptoException( + "EncryptedData is encrypted using keytype " + + EType.toString(eType) + + " but decryption key is of type " + + EType.toString(key.getEType())); + } + + EType etypeEngine = EType.getInstance(eType); + plain = etypeEngine.decrypt(cipher, key.getBytes(), usage); + // The service ticket will be used in S4U2proxy request. Therefore + // the raw ticket is still needed. + //cipher = null; + return etypeEngine.decryptedData(plain); + } + + /* + // currently destructive on cipher + // Not used. + public byte[] decrypt( + EncryptionKey key, + byte[] ivec, int usage) + throws KdcErrException, KrbApErrException, KrbCryptoException { + // XXX check for matching eType and kvno here + EType etypeEngine = EType.getInstance(eType); + plain = etypeEngine.decrypt(cipher, key.getBytes(), ivec, usage); + cipher = null; + return etypeEngine.decryptedData(plain); + } + + // currently destructive on cipher + // Not used. + byte[] decrypt(StringBuffer password) + throws KdcErrException, KrbApErrException, KrbCryptoException { + EncryptionKey key = new EncryptionKey(password); + // XXX check for matching eType here + EType etypeEngine = EType.getInstance(eType); + plain = etypeEngine.decrypt(cipher, key.getBytes()); + cipher = null; + return etypeEngine.decryptedData(plain); + } + */ + + private byte[] decryptedData() throws KdcErrException { + if (plain != null) { + EType etypeEngine = EType.getInstance(eType); + return etypeEngine.decryptedData(plain); + } + return null; + } + + /** + * Constructs an instance of EncryptedData type. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded + * data. + * + */ + /* Used by self */ + private EncryptedData(DerValue encoding) + throws Asn1Exception, IOException { + + DerValue der = null; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x00) { + eType = (der.getData().getBigInteger()).intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + if ((encoding.getData().peekByte() & 0x1F) == 1) { + der = encoding.getData().getDerValue(); + int i = (der.getData().getBigInteger()).intValue(); + kvno = new Integer(i); + } else { + kvno = null; + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x02) { + cipher = der.getData().getOctetString(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Returns an ASN.1 encoded EncryptedData type. + * + * + * EncryptedData ::= SEQUENCE { + * etype [0] Int32 -- EncryptionType --, + * kvno [1] UInt32 OPTIONAL, + * cipher [2] OCTET STRING -- ciphertext + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + *

    + * @return byte array of encoded EncryptedData object. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(this.eType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0x00), temp); + temp = new DerOutputStream(); + if (kvno != null) { + // encode as an unsigned integer (UInt32) + temp.putInteger(BigInteger.valueOf(this.kvno.longValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0x01), temp); + temp = new DerOutputStream(); + } + temp.putOctetString(this.cipher); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + (byte)0x02), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + + /** + * Parse (unmarshal) an EncryptedData from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @param data the Der input stream value, which contains one or more + * marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + * @return an instance of EncryptedData. + * + */ + public static EncryptedData parse(DerInputStream data, + byte explicitTag, + boolean optional) + throws Asn1Exception, IOException { + if ((optional) && + (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new EncryptedData(subDer); + } + } + + /** + * Reset asn.1 data stream after decryption, remove redundant bytes. + * @param data the decrypted data from decrypt(). + * @return the reset byte array which holds exactly one asn1 datum + * including its tag and length. + * + */ + public byte[] reset(byte[] data) { + byte[] bytes = null; + // for asn.1 encoded data, we use length field to + // determine the data length and remove redundant paddings. + if ((data[1] & 0xFF) < 128) { + bytes = new byte[data[1] + 2]; + System.arraycopy(data, 0, bytes, 0, data[1] + 2); + } else { + if ((data[1] & 0xFF) > 128) { + int len = data[1] & (byte)0x7F; + int result = 0; + for (int i = 0; i < len; i++) { + result |= (data[i + 2] & 0xFF) << (8 * (len - i - 1)); + } + bytes = new byte[result + len + 2]; + System.arraycopy(data, 0, bytes, 0, result + len + 2); + } + } + return bytes; + } + + public int getEType() { + return eType; + } + + public Integer getKeyVersionNumber() { + return kvno; + } + + /** + * Returns the raw cipher text bytes, not in ASN.1 encoding. + */ + public byte[] getBytes() { + return cipher; + } +} diff --git a/src/sun/security/krb5/EncryptionKey.java b/src/sun/security/krb5/EncryptionKey.java new file mode 100644 index 00000000..f0b908d8 --- /dev/null +++ b/src/sun/security/krb5/EncryptionKey.java @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.util.*; +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Arrays; +import sun.security.krb5.internal.ktab.KeyTab; +import sun.security.krb5.internal.ccache.CCacheOutputStream; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.DESedeKeySpec; + +/** + * This class encapsulates the concept of an EncryptionKey. An encryption + * key is defined in RFC 4120 as: + * + * EncryptionKey ::= SEQUENCE { + * keytype [0] Int32 -- actually encryption type --, + * keyvalue [1] OCTET STRING + * } + * + * keytype + * This field specifies the encryption type of the encryption key + * that follows in the keyvalue field. Although its name is + * "keytype", it actually specifies an encryption type. Previously, + * multiple cryptosystems that performed encryption differently but + * were capable of using keys with the same characteristics were + * permitted to share an assigned number to designate the type of + * key; this usage is now deprecated. + * + * keyvalue + * This field contains the key itself, encoded as an octet string. + */ + +public class EncryptionKey + implements Cloneable { + + public static final EncryptionKey NULL_KEY = + new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null); + + private int keyType; + private byte[] keyValue; + private Integer kvno; // not part of ASN1 encoding; + + private static final boolean DEBUG = Krb5.DEBUG; + + public synchronized int getEType() { + return keyType; + } + + public final Integer getKeyVersionNumber() { + return kvno; + } + + /** + * Returns the raw key bytes, not in any ASN.1 encoding. + */ + public final byte[] getBytes() { + // This method cannot be called outside sun.security, hence no + // cloning. getEncoded() calls this method. + return keyValue; + } + + public synchronized Object clone() { + return new EncryptionKey(keyValue, keyType, kvno); + } + + /** + * Obtains all versions of the secret key of the principal from a + * keytab. + * + * @Param princ the principal whose secret key is desired + * @param keytab the path to the keytab file. A value of null + * will be accepted to indicate that the default path should be + * searched. + * @returns an array of secret keys or null if none were found. + */ + public static EncryptionKey[] acquireSecretKeys(PrincipalName princ, + String keytab) { + + if (princ == null) + throw new IllegalArgumentException( + "Cannot have null pricipal name to look in keytab."); + + // KeyTab getInstance(keytab) will call KeyTab.getInstance() + // if keytab is null + KeyTab ktab = KeyTab.getInstance(keytab); + return ktab.readServiceKeys(princ); + } + + /** + * Obtains a key for a given etype of a principal with possible new salt + * and s2kparams + * @param cname NOT null + * @param password NOT null + * @param etype + * @param snp can be NULL + * @returns never null + */ + public static EncryptionKey acquireSecretKey(PrincipalName cname, + char[] password, int etype, PAData.SaltAndParams snp) + throws KrbException { + String salt; + byte[] s2kparams; + if (snp != null) { + salt = snp.salt != null ? snp.salt : cname.getSalt(); + s2kparams = snp.params; + } else { + salt = cname.getSalt(); + s2kparams = null; + } + return acquireSecretKey(password, salt, etype, s2kparams); + } + + /** + * Obtains a key for a given etype with salt and optional s2kparams + * @param password NOT null + * @param salt NOT null + * @param etype + * @param s2kparams can be NULL + * @returns never null + */ + public static EncryptionKey acquireSecretKey(char[] password, + String salt, int etype, byte[] s2kparams) + throws KrbException { + + return new EncryptionKey( + stringToKey(password, salt, s2kparams, etype), + etype, null); + } + + /** + * Generate a list of keys using the given principal and password. + * Construct a key for each configured etype. + * Caller is responsible for clearing password. + */ + /* + * Usually, when keyType is decoded from ASN.1 it will contain a + * value indicating what the algorithm to be used is. However, when + * converting from a password to a key for the AS-EXCHANGE, this + * keyType will not be available. Use builtin list of default etypes + * as the default in that case. If default_tkt_enctypes was set in + * the libdefaults of krb5.conf, then use that sequence. + */ + public static EncryptionKey[] acquireSecretKeys(char[] password, + String salt) throws KrbException { + + int[] etypes = EType.getDefaults("default_tkt_enctypes"); + + EncryptionKey[] encKeys = new EncryptionKey[etypes.length]; + for (int i = 0; i < etypes.length; i++) { + if (EType.isSupported(etypes[i])) { + encKeys[i] = new EncryptionKey( + stringToKey(password, salt, null, etypes[i]), + etypes[i], null); + } else { + if (DEBUG) { + System.out.println("Encryption Type " + + EType.toString(etypes[i]) + + " is not supported/enabled"); + } + } + } + return encKeys; + } + + // Used in Krb5AcceptCredential, self + public EncryptionKey(byte[] keyValue, + int keyType, + Integer kvno) { + + if (keyValue != null) { + this.keyValue = new byte[keyValue.length]; + System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length); + } else { + throw new IllegalArgumentException("EncryptionKey: " + + "Key bytes cannot be null!"); + } + this.keyType = keyType; + this.kvno = kvno; + } + + /** + * Constructs an EncryptionKey by using the specified key type and key + * value. It is used to recover the key when retrieving data from + * credential cache file. + * + */ + // Used in JSSE (KerberosWrapper), Credentials, + // javax.security.auth.kerberos.KeyImpl + public EncryptionKey(int keyType, + byte[] keyValue) { + this(keyValue, keyType, null); + } + + private static byte[] stringToKey(char[] password, String salt, + byte[] s2kparams, int keyType) throws KrbCryptoException { + + char[] slt = salt.toCharArray(); + char[] pwsalt = new char[password.length + slt.length]; + System.arraycopy(password, 0, pwsalt, 0, password.length); + System.arraycopy(slt, 0, pwsalt, password.length, slt.length); + Arrays.fill(slt, '0'); + + try { + switch (keyType) { + case EncryptedData.ETYPE_DES_CBC_CRC: + case EncryptedData.ETYPE_DES_CBC_MD5: + return Des.string_to_key_bytes(pwsalt); + + case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD: + return Des3.stringToKey(pwsalt); + + case EncryptedData.ETYPE_ARCFOUR_HMAC: + return ArcFourHmac.stringToKey(password); + + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + return Aes128.stringToKey(password, salt, s2kparams); + + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + return Aes256.stringToKey(password, salt, s2kparams); + + default: + throw new IllegalArgumentException("encryption type " + + EType.toString(keyType) + " not supported"); + } + + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } finally { + Arrays.fill(pwsalt, '0'); + } + } + + // Used in javax.security.auth.kerberos.KeyImpl + public EncryptionKey(char[] password, + String salt, + String algorithm) throws KrbCryptoException { + + if (algorithm == null || algorithm.equalsIgnoreCase("DES")) { + keyType = EncryptedData.ETYPE_DES_CBC_MD5; + } else if (algorithm.equalsIgnoreCase("DESede")) { + keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD; + } else if (algorithm.equalsIgnoreCase("AES128")) { + keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96; + } else if (algorithm.equalsIgnoreCase("ArcFourHmac")) { + keyType = EncryptedData.ETYPE_ARCFOUR_HMAC; + } else if (algorithm.equalsIgnoreCase("AES256")) { + keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96; + // validate if AES256 is enabled + if (!EType.isSupported(keyType)) { + throw new IllegalArgumentException("Algorithm " + algorithm + + " not enabled"); + } + } else { + throw new IllegalArgumentException("Algorithm " + algorithm + + " not supported"); + } + + keyValue = stringToKey(password, salt, null, keyType); + kvno = null; + } + + /** + * Generates a sub-sessionkey from a given session key. + * + * Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator- + * side respectively. + */ + public EncryptionKey(EncryptionKey key) throws KrbCryptoException { + // generate random sub-session key + keyValue = Confounder.bytes(key.keyValue.length); + for (int i = 0; i < keyValue.length; i++) { + keyValue[i] ^= key.keyValue[i]; + } + keyType = key.keyType; + + // check for key parity and weak keys + try { + // check for DES key + if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) || + (keyType == EncryptedData.ETYPE_DES_CBC_CRC)) { + // fix DES key parity + if (!DESKeySpec.isParityAdjusted(keyValue, 0)) { + keyValue = Des.set_parity(keyValue); + } + // check for weak key + if (DESKeySpec.isWeak(keyValue, 0)) { + keyValue[7] = (byte)(keyValue[7] ^ 0xF0); + } + } + // check for 3DES key + if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { + // fix 3DES key parity + if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) { + keyValue = Des3.parityFix(keyValue); + } + // check for weak keys + byte[] oneKey = new byte[8]; + for (int i=0; i 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Returns the ASN.1 encoding of this EncryptionKey. + * + *

    + * EncryptionKey ::= SEQUENCE { + * keytype[0] INTEGER, + * keyvalue[1] OCTET STRING } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + * + * @return byte array of encoded EncryptionKey object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 + * encoded data. + * @exception IOException if an I/O error occurs while reading encoded + * data. + * + */ + public synchronized byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(keyType); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putOctetString(keyValue); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + (byte)0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + public synchronized void destroy() { + if (keyValue != null) + for (int i = 0; i < keyValue.length; i++) + keyValue[i] = 0; + } + + + /** + * Parse (unmarshal) an Encryption key from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @param data the Der input stream value, which contains one or more + * marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @exception Asn1Exception if an error occurs while decoding an ASN1 + * encoded data. + * @exception IOException if an I/O error occurs while reading encoded + * data. + * @return an instance of EncryptionKey. + * + */ + public static EncryptionKey parse(DerInputStream data, byte + explicitTag, boolean optional) throws + Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != + explicitTag)) { + return null; + } + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new EncryptionKey(subDer); + } + } + + /** + * Writes key value in FCC format to a CCacheOutputStream. + * + * @param cos a CCacheOutputStream to be written to. + * @exception IOException if an I/O exception occurs. + * @see CCacheOutputStream + * + */ + public synchronized void writeKey(CCacheOutputStream cos) + throws IOException { + + cos.write16(keyType); + // we use KRB5_FCC_FVNO_3 + cos.write16(keyType); // key type is recorded twice. + cos.write32(keyValue.length); + for (int i = 0; i < keyValue.length; i++) { + cos.write8(keyValue[i]); + } + } + + public String toString() { + return new String("EncryptionKey: keyType=" + keyType + + " kvno=" + kvno + + " keyValue (hex dump)=" + + (keyValue == null || keyValue.length == 0 ? + " Empty Key" : '\n' + + Krb5.hexDumper.encodeBuffer(keyValue) + + '\n')); + } + + /** + * Find a key with given etype + */ + public static EncryptionKey findKey(int etype, EncryptionKey[] keys) + throws KrbException { + return findKey(etype, null, keys); + } + + /** + * Determines if a kvno matches another kvno. Used in the method + * findKey(type, kvno, keys). Always returns true if either input + * is null or zero, in case any side does not have kvno info available. + * + * Note: zero is included because N/A is not a legal value for kvno + * in javax.security.auth.kerberos.KerberosKey. Therefore, the info + * that the kvno is N/A might be lost when converting between this + * class and KerberosKey. + */ + private static boolean versionMatches(Integer v1, Integer v2) { + if (v1 == null || v1 == 0 || v2 == null || v2 == 0) { + return true; + } + return v1.equals(v2); + } + + /** + * Find a key with given etype and kvno + * @param kvno if null, return any (first?) key + */ + public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys) + throws KrbException { + + // check if encryption type is supported + if (!EType.isSupported(etype)) { + throw new KrbException("Encryption type " + + EType.toString(etype) + " is not supported/enabled"); + } + + int ktype; + boolean etypeFound = false; + + // When no matched kvno is found, returns tke key of the same + // etype with the highest kvno + int kvno_found = 0; + EncryptionKey key_found = null; + + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getEType(); + if (EType.isSupported(ktype)) { + Integer kv = keys[i].getKeyVersionNumber(); + if (etype == ktype) { + etypeFound = true; + if (versionMatches(kvno, kv)) { + return keys[i]; + } else if (kv > kvno_found) { + // kv is not null + key_found = keys[i]; + kvno_found = kv; + } + } + } + } + + // Key not found. + // allow DES key to be used for the DES etypes + if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || + etype == EncryptedData.ETYPE_DES_CBC_MD5)) { + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getEType(); + if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || + ktype == EncryptedData.ETYPE_DES_CBC_MD5) { + Integer kv = keys[i].getKeyVersionNumber(); + etypeFound = true; + if (versionMatches(kvno, kv)) { + return new EncryptionKey(etype, keys[i].getBytes()); + } else if (kv > kvno_found) { + key_found = new EncryptionKey(etype, keys[i].getBytes()); + kvno_found = kv; + } + } + } + } + if (etypeFound) { + return key_found; + // For compatibility, will not fail here. + //throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER); + } + return null; + } +} diff --git a/src/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java b/src/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java new file mode 100644 index 00000000..9afe2412 --- /dev/null +++ b/src/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5; + +import javax.security.auth.kerberos.KeyTab; + +/** + * An unsafe tunnel to get non-public access to classes in the + * javax.security.auth.kerberos package. + */ +public interface JavaxSecurityAuthKerberosAccess { + /** + * Returns a snapshot to the backing keytab + */ + public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot( + KeyTab ktab); +} diff --git a/src/sun/security/krb5/KdcComm.java b/src/sun/security/krb5/KdcComm.java new file mode 100644 index 00000000..721c105d --- /dev/null +++ b/src/sun/security/krb5/KdcComm.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import java.security.PrivilegedAction; +import java.security.Security; +import java.util.Locale; +import sun.security.krb5.internal.Krb5; +import sun.security.krb5.internal.NetClient; +import java.io.IOException; +import java.net.SocketTimeoutException; +import java.util.StringTokenizer; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.HashSet; +import java.util.Iterator; +import sun.security.krb5.internal.KRBError; + +/** + * KDC-REQ/KDC-REP communication. No more base class for KrbAsReq and + * KrbTgsReq. This class is now communication only. + */ +public final class KdcComm { + + // The following settings can be configured in [libdefaults] + // section of krb5.conf, which are global for all realms. Each of + // them can also be defined in a realm, which overrides value here. + + /** + * max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3) + */ + private static int defaultKdcRetryLimit; + /** + * timeout requesting a ticket from KDC, in millisec, default 30 sec + */ + private static int defaultKdcTimeout; + /** + * max UDP packet size, default unlimited (-1) + */ + private static int defaultUdpPrefLimit; + + private static final boolean DEBUG = Krb5.DEBUG; + + private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy"; + + /** + * What to do when a KDC is unavailable, specified in the + * java.security file with key krb5.kdc.bad.policy. + * Possible values can be TRY_LAST or TRY_LESS. Reloaded when refreshed. + */ + private enum BpType { + NONE, TRY_LAST, TRY_LESS + } + private static int tryLessMaxRetries = 1; + private static int tryLessTimeout = 5000; + + private static BpType badPolicy; + + static { + initStatic(); + } + + /** + * Read global settings + */ + public static void initStatic() { + String value = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return Security.getProperty(BAD_POLICY_KEY); + } + }); + if (value != null) { + value = value.toLowerCase(Locale.ENGLISH); + String[] ss = value.split(":"); + if ("tryless".equals(ss[0])) { + if (ss.length > 1) { + String[] params = ss[1].split(","); + try { + int tmp0 = Integer.parseInt(params[0]); + if (params.length > 1) { + tryLessTimeout = Integer.parseInt(params[1]); + } + // Assign here in case of exception at params[1] + tryLessMaxRetries = tmp0; + } catch (NumberFormatException nfe) { + // Ignored. Please note that tryLess is recognized and + // used, parameters using default values + if (DEBUG) { + System.out.println("Invalid " + BAD_POLICY_KEY + + " parameter for tryLess: " + + value + ", use default"); + } + } + } + badPolicy = BpType.TRY_LESS; + } else if ("trylast".equals(ss[0])) { + badPolicy = BpType.TRY_LAST; + } else { + badPolicy = BpType.NONE; + } + } else { + badPolicy = BpType.NONE; + } + + + int timeout = -1; + int max_retries = -1; + int udp_pref_limit = -1; + + try { + Config cfg = Config.getInstance(); + String temp = cfg.get("libdefaults", "kdc_timeout"); + timeout = parseTimeString(temp); + + temp = cfg.get("libdefaults", "max_retries"); + max_retries = parsePositiveIntString(temp); + temp = cfg.get("libdefaults", "udp_preference_limit"); + udp_pref_limit = parsePositiveIntString(temp); + } catch (Exception exc) { + // ignore any exceptions; use default values + if (DEBUG) { + System.out.println ("Exception in getting KDC communication " + + "settings, using default value " + + exc.getMessage()); + } + } + defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds + defaultKdcRetryLimit = + max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT; + + if (udp_pref_limit < 0) { + defaultUdpPrefLimit = Krb5.KDC_DEFAULT_UDP_PREF_LIMIT; + } else if (udp_pref_limit > Krb5.KDC_HARD_UDP_LIMIT) { + defaultUdpPrefLimit = Krb5.KDC_HARD_UDP_LIMIT; + } else { + defaultUdpPrefLimit = udp_pref_limit; + } + + KdcAccessibility.reset(); + } + + /** + * The instance fields + */ + private String realm; + + public KdcComm(String realm) throws KrbException { + if (realm == null) { + realm = Config.getInstance().getDefaultRealm(); + if (realm == null) { + throw new KrbException(Krb5.KRB_ERR_GENERIC, + "Cannot find default realm"); + } + } + this.realm = realm; + } + + public byte[] send(byte[] obuf) + throws IOException, KrbException { + int udpPrefLimit = getRealmSpecificValue( + realm, "udp_preference_limit", defaultUdpPrefLimit); + + boolean useTCP = (udpPrefLimit > 0 && + (obuf != null && obuf.length > udpPrefLimit)); + + return send(obuf, useTCP); + } + + private byte[] send(byte[] obuf, boolean useTCP) + throws IOException, KrbException { + + if (obuf == null) + return null; + Config cfg = Config.getInstance(); + + if (realm == null) { + realm = cfg.getDefaultRealm(); + if (realm == null) { + throw new KrbException(Krb5.KRB_ERR_GENERIC, + "Cannot find default realm"); + } + } + + String kdcList = cfg.getKDCList(realm); + if (kdcList == null) { + throw new KrbException("Cannot get kdc for realm " + realm); + } + // tempKdc may include the port number also + Iterator tempKdc = KdcAccessibility.list(kdcList).iterator(); + if (!tempKdc.hasNext()) { + throw new KrbException("Cannot get kdc for realm " + realm); + } + byte[] ibuf = null; + try { + ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP); + } catch(Exception first) { + boolean ok = false; + while(tempKdc.hasNext()) { + try { + ibuf = sendIfPossible(obuf, tempKdc.next(), useTCP); + ok = true; + break; + } catch(Exception ignore) {} + } + if (!ok) throw first; + } + if (ibuf == null) { + throw new IOException("Cannot get a KDC reply"); + } + return ibuf; + } + + // send the AS Request to the specified KDC + // failover to using TCP if useTCP is not set and response is too big + private byte[] sendIfPossible(byte[] obuf, String tempKdc, boolean useTCP) + throws IOException, KrbException { + + try { + byte[] ibuf = send(obuf, tempKdc, useTCP); + KRBError ke = null; + try { + ke = new KRBError(ibuf); + } catch (Exception e) { + // OK + } + if (ke != null && ke.getErrorCode() == + Krb5.KRB_ERR_RESPONSE_TOO_BIG) { + ibuf = send(obuf, tempKdc, true); + } + KdcAccessibility.removeBad(tempKdc); + return ibuf; + } catch(Exception e) { + if (DEBUG) { + System.out.println(">>> KrbKdcReq send: error trying " + + tempKdc); + e.printStackTrace(System.out); + } + KdcAccessibility.addBad(tempKdc); + throw e; + } + } + + // send the AS Request to the specified KDC + + private byte[] send(byte[] obuf, String tempKdc, boolean useTCP) + throws IOException, KrbException { + + if (obuf == null) + return null; + + int port = Krb5.KDC_INET_DEFAULT_PORT; + int retries = getRealmSpecificValue( + realm, "max_retries", defaultKdcRetryLimit); + int timeout = getRealmSpecificValue( + realm, "kdc_timeout", defaultKdcTimeout); + if (badPolicy == BpType.TRY_LESS && + KdcAccessibility.isBad(tempKdc)) { + if (retries > tryLessMaxRetries) { + retries = tryLessMaxRetries; // less retries + } + if (timeout > tryLessTimeout) { + timeout = tryLessTimeout; // less time + } + } + + String kdc = null; + String portStr = null; + + if (tempKdc.charAt(0) == '[') { // Explicit IPv6 in [] + int pos = tempKdc.indexOf(']', 1); + if (pos == -1) { + throw new IOException("Illegal KDC: " + tempKdc); + } + kdc = tempKdc.substring(1, pos); + if (pos != tempKdc.length() - 1) { // with port number + if (tempKdc.charAt(pos+1) != ':') { + throw new IOException("Illegal KDC: " + tempKdc); + } + portStr = tempKdc.substring(pos+2); + } + } else { + int colon = tempKdc.indexOf(':'); + if (colon == -1) { // Hostname or IPv4 host only + kdc = tempKdc; + } else { + int nextColon = tempKdc.indexOf(':', colon+1); + if (nextColon > 0) { // >=2 ":", IPv6 with no port + kdc = tempKdc; + } else { // 1 ":", hostname or IPv4 with port + kdc = tempKdc.substring(0, colon); + portStr = tempKdc.substring(colon+1); + } + } + } + if (portStr != null) { + int tempPort = parsePositiveIntString(portStr); + if (tempPort > 0) + port = tempPort; + } + + if (DEBUG) { + System.out.println(">>> KrbKdcReq send: kdc=" + kdc + + (useTCP ? " TCP:":" UDP:") + + port + ", timeout=" + + timeout + + ", number of retries =" + + retries + + ", #bytes=" + obuf.length); + } + + KdcCommunication kdcCommunication = + new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf); + try { + byte[] ibuf = AccessController.doPrivileged(kdcCommunication); + if (DEBUG) { + System.out.println(">>> KrbKdcReq send: #bytes read=" + + (ibuf != null ? ibuf.length : 0)); + } + return ibuf; + } catch (PrivilegedActionException e) { + Exception wrappedException = e.getException(); + if (wrappedException instanceof IOException) { + throw (IOException) wrappedException; + } else { + throw (KrbException) wrappedException; + } + } + } + + private static class KdcCommunication + implements PrivilegedExceptionAction { + + private String kdc; + private int port; + private boolean useTCP; + private int timeout; + private int retries; + private byte[] obuf; + + public KdcCommunication(String kdc, int port, boolean useTCP, + int timeout, int retries, byte[] obuf) { + this.kdc = kdc; + this.port = port; + this.useTCP = useTCP; + this.timeout = timeout; + this.retries = retries; + this.obuf = obuf; + } + + // The caller only casts IOException and KrbException so don't + // add any new ones! + + public byte[] run() throws IOException, KrbException { + + byte[] ibuf = null; + + for (int i=1; i <= retries; i++) { + String proto = useTCP?"TCP":"UDP"; + try (NetClient kdcClient = NetClient.getInstance( + proto, kdc, port, timeout)) { + if (DEBUG) { + System.out.println(">>> KDCCommunication: kdc=" + kdc + + " " + proto + ":" + + port + ", timeout=" + + timeout + + ",Attempt =" + i + + ", #bytes=" + obuf.length); + } + try { + /* + * Send the data to the kdc. + */ + kdcClient.send(obuf); + /* + * And get a response. + */ + ibuf = kdcClient.receive(); + break; + } catch (SocketTimeoutException se) { + if (DEBUG) { + System.out.println ("SocketTimeOutException with " + + "attempt: " + i); + } + if (i == retries) { + ibuf = null; + throw se; + } + } + } + } + return ibuf; + } + } + + /** + * Parses a time value string. If it ends with "s", parses as seconds. + * Otherwise, parses as milliseconds. + * @param s the time string + * @return the integer value in milliseconds, or -1 if input is null or + * has an invalid format + */ + private static int parseTimeString(String s) { + if (s == null) { + return -1; + } + if (s.endsWith("s")) { + int seconds = parsePositiveIntString(s.substring(0, s.length()-1)); + return (seconds < 0) ? -1 : (seconds*1000); + } else { + return parsePositiveIntString(s); + } + } + + /** + * Returns krb5.conf setting of {@code key} for a specific realm, + * which can be: + * 1. defined in the sub-stanza for the given realm inside [realms], or + * 2. defined in [libdefaults], or + * 3. defValue + * @param realm the given realm in which the setting is requested. Returns + * the global setting if null + * @param key the key for the setting + * @param defValue default value + * @return a value for the key + */ + private int getRealmSpecificValue(String realm, String key, int defValue) { + int v = defValue; + + if (realm == null) return v; + + int temp = -1; + try { + String value = + Config.getInstance().get("realms", realm, key); + if (key.equals("kdc_timeout")) { + temp = parseTimeString(value); + } else { + temp = parsePositiveIntString(value); + } + } catch (Exception exc) { + // Ignored, defValue will be picked up + } + + if (temp > 0) v = temp; + + return v; + } + + private static int parsePositiveIntString(String intString) { + if (intString == null) + return -1; + + int ret = -1; + + try { + ret = Integer.parseInt(intString); + } catch (Exception exc) { + return -1; + } + + if (ret >= 0) + return ret; + + return -1; + } + + /** + * Maintains a KDC accessible list. Unavailable KDCs are put into a + * blacklist, when a KDC in the blacklist is available, it's removed + * from there. No insertion order in the blacklist. + * + * There are two methods to deal with KDCs in the blacklist. 1. Only try + * them when there's no KDC not on the blacklist. 2. Still try them, but + * with lesser number of retries and smaller timeout value. + */ + static class KdcAccessibility { + // Known bad KDCs + private static Set bads = new HashSet<>(); + + private static synchronized void addBad(String kdc) { + if (DEBUG) { + System.out.println(">>> KdcAccessibility: add " + kdc); + } + bads.add(kdc); + } + + private static synchronized void removeBad(String kdc) { + if (DEBUG) { + System.out.println(">>> KdcAccessibility: remove " + kdc); + } + bads.remove(kdc); + } + + private static synchronized boolean isBad(String kdc) { + return bads.contains(kdc); + } + + private static synchronized void reset() { + if (DEBUG) { + System.out.println(">>> KdcAccessibility: reset"); + } + bads.clear(); + } + + // Returns a preferred KDC list by putting the bad ones at the end + private static synchronized List list(String kdcList) { + StringTokenizer st = new StringTokenizer(kdcList); + List list = new ArrayList<>(); + if (badPolicy == BpType.TRY_LAST) { + List badkdcs = new ArrayList<>(); + while (st.hasMoreTokens()) { + String t = st.nextToken(); + if (bads.contains(t)) badkdcs.add(t); + else list.add(t); + } + // Bad KDCs are put at last + list.addAll(badkdcs); + } else { + // All KDCs are returned in their original order, + // This include TRY_LESS and NONE + while (st.hasMoreTokens()) { + list.add(st.nextToken()); + } + } + return list; + } + } +} + diff --git a/src/sun/security/krb5/KerberosSecrets.java b/src/sun/security/krb5/KerberosSecrets.java new file mode 100644 index 00000000..44f2f0f9 --- /dev/null +++ b/src/sun/security/krb5/KerberosSecrets.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5; + +import javax.security.auth.kerberos.KeyTab; +import sun.misc.Unsafe; + +public class KerberosSecrets { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess; + + public static void setJavaxSecurityAuthKerberosAccess + (JavaxSecurityAuthKerberosAccess jsaka) { + javaxSecurityAuthKerberosAccess = jsaka; + } + + public static JavaxSecurityAuthKerberosAccess + getJavaxSecurityAuthKerberosAccess() { + if (javaxSecurityAuthKerberosAccess == null) + unsafe.ensureClassInitialized(KeyTab.class); + return javaxSecurityAuthKerberosAccess; + } +} diff --git a/src/sun/security/krb5/KrbApRep.java b/src/sun/security/krb5/KrbApRep.java new file mode 100644 index 00000000..44216bfd --- /dev/null +++ b/src/sun/security/krb5/KrbApRep.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.util.*; +import java.io.IOException; + +/** + * This class encapsulates a KRB-AP-REP sent from the service to the + * client. + */ +public class KrbApRep { + private byte[] obuf; + private byte[] ibuf; + private EncAPRepPart encPart; // although in plain text + private APRep apRepMessg; + + /** + * Constructs a KRB-AP-REP to send to a client. + * @throws KrbException + * @throws IOException + */ + // Used in AcceptSecContextToken + public KrbApRep(KrbApReq incomingReq, + boolean useSeqNumber, + EncryptionKey subKey) + throws KrbException, IOException { + + SeqNumber seqNum = new LocalSeqNumber(); + + init(incomingReq, subKey, seqNum); + } + + /** + * Constructs a KRB-AP-REQ from the bytes received from a service. + * @throws KrbException + * @throws IOException + */ + // Used in AcceptSecContextToken + public KrbApRep(byte[] message, Credentials tgtCreds, + KrbApReq outgoingReq) throws KrbException, IOException { + this(message, tgtCreds); + authenticate(outgoingReq); + } + + private void init(KrbApReq apReq, + EncryptionKey subKey, + SeqNumber seqNumber) + throws KrbException, IOException { + createMessage( + apReq.getCreds().key, + apReq.getCtime(), + apReq.cusec(), + subKey, + seqNumber); + obuf = apRepMessg.asn1Encode(); + } + + + /** + * Constructs a KrbApRep object. + * @param msg a byte array of reply message. + * @param tgs_creds client's credential. + * @exception KrbException + * @exception IOException + */ + private KrbApRep(byte[] msg, Credentials tgs_creds) + throws KrbException, IOException { + this(new DerValue(msg), tgs_creds); + } + + /** + * Constructs a KrbApRep object. + * @param msg a byte array of reply message. + * @param tgs_creds client's credential. + * @exception KrbException + * @exception IOException + */ + private KrbApRep(DerValue encoding, Credentials tgs_creds) + throws KrbException, IOException { + APRep rep = null; + try { + rep = new APRep(encoding); + } catch (Asn1Exception e) { + rep = null; + KRBError err = new KRBError(encoding); + String errStr = err.getErrorString(); + String eText; + if (errStr.charAt(errStr.length() - 1) == 0) + eText = errStr.substring(0, errStr.length() - 1); + else + eText = errStr; + KrbException ke = new KrbException(err.getErrorCode(), eText); + ke.initCause(e); + throw ke; + } + + byte[] temp = rep.encPart.decrypt(tgs_creds.key, + KeyUsage.KU_ENC_AP_REP_PART); + byte[] enc_ap_rep_part = rep.encPart.reset(temp); + + encoding = new DerValue(enc_ap_rep_part); + encPart = new EncAPRepPart(encoding); + } + + private void authenticate(KrbApReq apReq) + throws KrbException, IOException { + if (encPart.ctime.getSeconds() != apReq.getCtime().getSeconds() || + encPart.cusec != apReq.getCtime().getMicroSeconds()) + throw new KrbApErrException(Krb5.KRB_AP_ERR_MUT_FAIL); + } + + + /** + * Returns the optional subkey stored in + * this message. Returns null if none is stored. + */ + public EncryptionKey getSubKey() { + // XXX Can encPart be null + return encPart.getSubKey(); + + } + + /** + * Returns the optional sequence number stored in the + * this message. Returns null if none is stored. + */ + public Integer getSeqNumber() { + // XXX Can encPart be null + return encPart.getSeqNumber(); + } + + /** + * Returns the ASN.1 encoding that should be sent to the peer. + */ + public byte[] getMessage() { + return obuf; + } + + private void createMessage( + EncryptionKey key, + KerberosTime ctime, + int cusec, + EncryptionKey subKey, + SeqNumber seqNumber) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + Integer seqno = null; + + if (seqNumber != null) + seqno = new Integer(seqNumber.current()); + + encPart = new EncAPRepPart(ctime, + cusec, + subKey, + seqno); + + byte[] encPartEncoding = encPart.asn1Encode(); + + EncryptedData encEncPart = new EncryptedData(key, encPartEncoding, + KeyUsage.KU_ENC_AP_REP_PART); + + apRepMessg = new APRep(encEncPart); + } + +} diff --git a/src/sun/security/krb5/KrbApReq.java b/src/sun/security/krb5/KrbApReq.java new file mode 100644 index 00000000..b88e42d9 --- /dev/null +++ b/src/sun/security/krb5/KrbApReq.java @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import sun.security.jgss.krb5.Krb5AcceptCredential; +import java.net.InetAddress; +import sun.security.util.*; +import java.io.IOException; +import java.util.Arrays; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import sun.security.krb5.internal.rcache.AuthTimeWithHash; + +/** + * This class encapsulates a KRB-AP-REQ that a client sends to a + * server for authentication. + */ +public class KrbApReq { + + private byte[] obuf; + private KerberosTime ctime; + private int cusec; + private Authenticator authenticator; + private Credentials creds; + private APReq apReqMessg; + + // Used by acceptor side + private static ReplayCache rcache = ReplayCache.getInstance(); + private static boolean DEBUG = Krb5.DEBUG; + private static final char[] hexConst = "0123456789ABCDEF".toCharArray(); + + private static final MessageDigest md; + + static { + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException("Impossible"); + } + } + + /** + * Constructs an AP-REQ message to send to the peer. + * @param tgsCred the Credentials to be used to construct the + * AP Request protocol message. + * @param mutualRequired Whether mutual authentication is required + * @param useSubkey Whether the subkey is to be used to protect this + * specific application session. If this is not set then the + * session key from the ticket will be used. + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + /* + // Not Used + public KrbApReq(Credentials tgsCred, + boolean mutualRequired, + boolean useSubKey, + boolean useSeqNumber) throws Asn1Exception, + KrbCryptoException, KrbException, IOException { + + this(tgsCred, mutualRequired, useSubKey, useSeqNumber, null); + } +*/ + + /** + * Constructs an AP-REQ message to send to the peer. + * @param tgsCred the Credentials to be used to construct the + * AP Request protocol message. + * @param mutualRequired Whether mutual authentication is required + * @param useSubkey Whether the subkey is to be used to protect this + * specific application session. If this is not set then the + * session key from the ticket will be used. + * @param checksum checksum of the the application data that accompanies + * the KRB_AP_REQ. + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + // Used in InitSecContextToken + public KrbApReq(Credentials tgsCred, + boolean mutualRequired, + boolean useSubKey, + boolean useSeqNumber, + Checksum cksum) throws Asn1Exception, + KrbCryptoException, KrbException, IOException { + + APOptions apOptions = (mutualRequired? + new APOptions(Krb5.AP_OPTS_MUTUAL_REQUIRED): + new APOptions()); + if (DEBUG) + System.out.println(">>> KrbApReq: APOptions are " + apOptions); + + EncryptionKey subKey = (useSubKey? + new EncryptionKey(tgsCred.getSessionKey()): + null); + + SeqNumber seqNum = new LocalSeqNumber(); + + init(apOptions, + tgsCred, + cksum, + subKey, + seqNum, + null, // AuthorizationData authzData + KeyUsage.KU_AP_REQ_AUTHENTICATOR); + + } + + /** + * Constructs an AP-REQ message from the bytes received from the + * peer. + * @param message The message received from the peer + * @param keys EncrtyptionKeys to decrypt the message; + * key selected will depend on etype used to encrypte data + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + // Used in InitSecContextToken (for AP_REQ and not TGS REQ) + public KrbApReq(byte[] message, + Krb5AcceptCredential cred, + InetAddress initiator) + throws KrbException, IOException { + obuf = message; + if (apReqMessg == null) + decode(); + authenticate(cred, initiator); + } + + /** + * Constructs an AP-REQ message from the bytes received from the + * peer. + * @param value The DerValue that contains the + * DER enoded AP-REQ protocol message + * @param keys EncrtyptionKeys to decrypt the message; + * + * @throws KrbException for any Kerberos protocol specific error + * @throws IOException for any IO related errors + * (e.g. socket operations) + */ + /* + public KrbApReq(DerValue value, EncryptionKey[] key, InetAddress initiator) + throws KrbException, IOException { + obuf = value.toByteArray(); + if (apReqMessg == null) + decode(value); + authenticate(keys, initiator); + } + + KrbApReq(APOptions options, + Credentials tgs_creds, + Checksum cksum, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData) + throws KrbException, IOException { + init(options, tgs_creds, cksum, subKey, seqNumber, authorizationData); + } +*/ + + /** used by KrbTgsReq **/ + KrbApReq(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + init(apOptions, ticket, key, cname, + cksum, ctime, subKey, seqNumber, authorizationData, + KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR); + + } + + private void init(APOptions options, + Credentials tgs_creds, + Checksum cksum, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws KrbException, IOException { + + ctime = KerberosTime.now(); + init(options, + tgs_creds.ticket, + tgs_creds.key, + tgs_creds.client, + cksum, + ctime, + subKey, + seqNumber, + authorizationData, + usage); + } + + private void init(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + createMessage(apOptions, ticket, key, cname, + cksum, ctime, subKey, seqNumber, authorizationData, + usage); + obuf = apReqMessg.asn1Encode(); + } + + + void decode() throws KrbException, IOException { + DerValue encoding = new DerValue(obuf); + decode(encoding); + } + + void decode(DerValue encoding) throws KrbException, IOException { + apReqMessg = null; + try { + apReqMessg = new APReq(encoding); + } catch (Asn1Exception e) { + apReqMessg = null; + KRBError err = new KRBError(encoding); + String errStr = err.getErrorString(); + String eText; + if (errStr.charAt(errStr.length() - 1) == 0) + eText = errStr.substring(0, errStr.length() - 1); + else + eText = errStr; + KrbException ke = new KrbException(err.getErrorCode(), eText); + ke.initCause(e); + throw ke; + } + } + + private void authenticate(Krb5AcceptCredential cred, InetAddress initiator) + throws KrbException, IOException { + int encPartKeyType = apReqMessg.ticket.encPart.getEType(); + Integer kvno = apReqMessg.ticket.encPart.getKeyVersionNumber(); + EncryptionKey[] keys = cred.getKrb5EncryptionKeys(apReqMessg.ticket.sname); + EncryptionKey dkey = EncryptionKey.findKey(encPartKeyType, kvno, keys); + + if (dkey == null) { + throw new KrbException(Krb5.API_INVALID_ARG, + "Cannot find key of appropriate type to decrypt AP REP - " + + EType.toString(encPartKeyType)); + } + + byte[] bytes = apReqMessg.ticket.encPart.decrypt(dkey, + KeyUsage.KU_TICKET); + byte[] temp = apReqMessg.ticket.encPart.reset(bytes); + EncTicketPart enc_ticketPart = new EncTicketPart(temp); + + checkPermittedEType(enc_ticketPart.key.getEType()); + + byte[] bytes2 = apReqMessg.authenticator.decrypt(enc_ticketPart.key, + KeyUsage.KU_AP_REQ_AUTHENTICATOR); + byte[] temp2 = apReqMessg.authenticator.reset(bytes2); + authenticator = new Authenticator(temp2); + ctime = authenticator.ctime; + cusec = authenticator.cusec; + authenticator.ctime = + authenticator.ctime.withMicroSeconds(authenticator.cusec); + + if (!authenticator.cname.equals(enc_ticketPart.cname)) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADMATCH); + } + + if (!authenticator.ctime.inClockSkew()) + throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + + byte[] hash = md.digest(apReqMessg.authenticator.cipher); + char[] h = new char[hash.length * 2]; + for (int i=0; i>4]; + h[2*i+1] = hexConst[hash[i]&0xf]; + } + AuthTimeWithHash time = new AuthTimeWithHash( + authenticator.cname.toString(), + apReqMessg.ticket.sname.toString(), + authenticator.ctime.getSeconds(), + authenticator.cusec, + new String(h)); + rcache.checkAndStore(KerberosTime.now(), time); + + if (initiator != null) { + // sender host address + HostAddress sender = new HostAddress(initiator); + if (enc_ticketPart.caddr != null + && !enc_ticketPart.caddr.inList(sender)) { + if (DEBUG) { + System.out.println(">>> KrbApReq: initiator is " + + sender.getInetAddress() + + ", but caddr is " + + Arrays.toString( + enc_ticketPart.caddr.getInetAddresses())); + } + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); + } + } + + // XXX check for repeated authenticator + // if found + // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + // else + // save authenticator to check for later + + KerberosTime now = KerberosTime.now(); + + if ((enc_ticketPart.starttime != null && + enc_ticketPart.starttime.greaterThanWRTClockSkew(now)) || + enc_ticketPart.flags.get(Krb5.TKT_OPTS_INVALID)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_NYV); + + // if the current time is later than end time by more + // than the allowable clock skew, throws ticket expired exception. + if (enc_ticketPart.endtime != null && + now.greaterThanWRTClockSkew(enc_ticketPart.endtime)) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_TKT_EXPIRED); + } + + creds = new Credentials( + apReqMessg.ticket, + authenticator.cname, + apReqMessg.ticket.sname, + enc_ticketPart.key, + enc_ticketPart.flags, + enc_ticketPart.authtime, + enc_ticketPart.starttime, + enc_ticketPart.endtime, + enc_ticketPart.renewTill, + enc_ticketPart.caddr, + enc_ticketPart.authorizationData); + if (DEBUG) { + System.out.println(">>> KrbApReq: authenticate succeed."); + } + } + + /** + * Returns the credentials that are contained in the ticket that + * is part of this this AP-REP. + */ + public Credentials getCreds() { + return creds; + } + + KerberosTime getCtime() { + if (ctime != null) + return ctime; + return authenticator.ctime; + } + + int cusec() { + return cusec; + } + + APOptions getAPOptions() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions; + return null; + } + + /** + * Returns true if mutual authentication is required and hence an + * AP-REP will need to be generated. + * @throws KrbException + * @throws IOException + */ + public boolean getMutualAuthRequired() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions.get(Krb5.AP_OPTS_MUTUAL_REQUIRED); + return false; + } + + boolean useSessionKey() throws KrbException, IOException { + if (apReqMessg == null) + decode(); + if (apReqMessg != null) + return apReqMessg.apOptions.get(Krb5.AP_OPTS_USE_SESSION_KEY); + return false; + } + + /** + * Returns the optional subkey stored in the Authenticator for + * this message. Returns null if none is stored. + */ + public EncryptionKey getSubKey() { + // XXX Can authenticator be null + return authenticator.getSubKey(); + } + + /** + * Returns the optional sequence number stored in the + * Authenticator for this message. Returns null if none is + * stored. + */ + public Integer getSeqNumber() { + // XXX Can authenticator be null + return authenticator.getSeqNumber(); + } + + /** + * Returns the optional Checksum stored in the + * Authenticator for this message. Returns null if none is + * stored. + */ + public Checksum getChecksum() { + return authenticator.getChecksum(); + } + + /** + * Returns the ASN.1 encoding that should be sent to the peer. + */ + public byte[] getMessage() { + return obuf; + } + + /** + * Returns the principal name of the client that generated this + * message. + */ + public PrincipalName getClient() { + return creds.getClient(); + } + + private void createMessage(APOptions apOptions, + Ticket ticket, + EncryptionKey key, + PrincipalName cname, + Checksum cksum, + KerberosTime ctime, + EncryptionKey subKey, + SeqNumber seqNumber, + AuthorizationData authorizationData, + int usage) + throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + Integer seqno = null; + + if (seqNumber != null) + seqno = new Integer(seqNumber.current()); + + authenticator = + new Authenticator(cname, + cksum, + ctime.getMicroSeconds(), + ctime, + subKey, + seqno, + authorizationData); + + byte[] temp = authenticator.asn1Encode(); + + EncryptedData encAuthenticator = + new EncryptedData(key, temp, usage); + + apReqMessg = + new APReq(apOptions, ticket, encAuthenticator); + } + + // Check that key is one of the permitted types + private static void checkPermittedEType(int target) throws KrbException { + int[] etypes = EType.getDefaults("permitted_enctypes"); + if (!EType.isSupported(target, etypes)) { + throw new KrbException(EType.toString(target) + + " encryption type not in permitted_enctypes list"); + } + } +} diff --git a/src/sun/security/krb5/KrbAppMessage.java b/src/sun/security/krb5/KrbAppMessage.java new file mode 100644 index 00000000..47aa1681 --- /dev/null +++ b/src/sun/security/krb5/KrbAppMessage.java @@ -0,0 +1,115 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; + +abstract class KrbAppMessage { + + private static boolean DEBUG = Krb5.DEBUG; + /** + * Common checks for KRB-PRIV and KRB-SAFE + */ + void check(KerberosTime packetTimestamp, + Integer packetUsec, + Integer packetSeqNumber, + HostAddress packetSAddress, + HostAddress packetRAddress, + SeqNumber seqNumber, + HostAddress sAddress, + HostAddress rAddress, + boolean timestampRequired, + boolean seqNumberRequired, + PrincipalName packetPrincipal) + throws KrbApErrException { + + if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || sAddress != null) { + if (packetSAddress == null || sAddress == null || + !packetSAddress.equals(sAddress)) { + if (DEBUG && packetSAddress == null) { + System.out.println("packetSAddress is null"); + } + if (DEBUG && sAddress == null) { + System.out.println("sAddress is null"); + } + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); + } + } + + if (!Krb5.AP_EMPTY_ADDRESSES_ALLOWED || rAddress != null) { + if (packetRAddress == null || rAddress == null || + !packetRAddress.equals(rAddress)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADADDR); + } + + if (packetTimestamp != null) { + if (packetUsec != null) { + packetTimestamp = + packetTimestamp.withMicroSeconds(packetUsec.intValue()); + } + if (!packetTimestamp.inClockSkew()) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + } + } else { + if (timestampRequired) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + } + } + + // XXX check replay cache + // if (rcache.repeated(packetTimestamp, packetUsec, packetSAddress)) + // throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + + // XXX consider moving up to api level + if (seqNumber == null && seqNumberRequired == true) + throw new KrbApErrException(Krb5.API_INVALID_ARG); + + if (packetSeqNumber != null && seqNumber != null) { + if (packetSeqNumber.intValue() != seqNumber.current()) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER); + // should be done only when no more exceptions are possible + seqNumber.step(); + } else { + if (seqNumberRequired) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADORDER); + } + } + + // Must not be relaxed, per RFC 4120 + if (packetTimestamp == null && packetSeqNumber == null) + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + + // XXX check replay cache + // rcache.save_identifier(packetTimestamp, packetUsec, packetSAddress, + // packetPrincipal, pcaketRealm); + } + +} diff --git a/src/sun/security/krb5/KrbAsRep.java b/src/sun/security/krb5/KrbAsRep.java new file mode 100644 index 00000000..bf7a562e --- /dev/null +++ b/src/sun/security/krb5/KrbAsRep.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.krb5.internal.crypto.EType; +import sun.security.util.*; +import java.io.IOException; +import java.util.Objects; +import javax.security.auth.kerberos.KeyTab; +import sun.security.jgss.krb5.Krb5Util; + +/** + * This class encapsulates a AS-REP message that the KDC sends to the + * client. + */ +class KrbAsRep extends KrbKdcRep { + + private ASRep rep; // The AS-REP message + private Credentials creds; // The Credentials provide by the AS-REP + // message, created by initiator after calling + // the decrypt() method + + private boolean DEBUG = Krb5.DEBUG; + + KrbAsRep(byte[] ibuf) throws + KrbException, Asn1Exception, IOException { + DerValue encoding = new DerValue(ibuf); + try { + rep = new ASRep(encoding); + } catch (Asn1Exception e) { + rep = null; + KRBError err = new KRBError(encoding); + String errStr = err.getErrorString(); + String eText = null; // pick up text sent by the server (if any) + + if (errStr != null && errStr.length() > 0) { + if (errStr.charAt(errStr.length() - 1) == 0) + eText = errStr.substring(0, errStr.length() - 1); + else + eText = errStr; + } + KrbException ke; + if (eText == null) { + // no text sent from server + ke = new KrbException(err); + } else { + if (DEBUG) { + System.out.println("KRBError received: " + eText); + } + // override default text with server text + ke = new KrbException(err, eText); + } + ke.initCause(e); + throw ke; + } + } + + // KrbAsReqBuilder need to read back the PA for key generation + PAData[] getPA() { + return rep.pAData; + } + + /** + * Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab. + * @param ktab the keytab, not null + * @param asReq the original AS-REQ sent, used to validate AS-REP + * @param cname the user principal name, used to locate keys in ktab + */ + void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname) + throws KrbException, Asn1Exception, IOException { + EncryptionKey dkey = null; + int encPartKeyType = rep.encPart.getEType(); + Integer encPartKvno = rep.encPart.kvno; + try { + dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno, + Krb5Util.keysFromJavaxKeyTab(ktab, cname)); + } catch (KrbException ke) { + if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) { + // Fallback to no kvno. In some cases, keytab is generated + // not by sysadmin but Java's ktab command + dkey = EncryptionKey.findKey(encPartKeyType, + Krb5Util.keysFromJavaxKeyTab(ktab, cname)); + } + } + if (dkey == null) { + throw new KrbException(Krb5.API_INVALID_ARG, + "Cannot find key for type/kvno to decrypt AS REP - " + + EType.toString(encPartKeyType) + "/" + encPartKvno); + } + decrypt(dkey, asReq); + } + + /** + * Called by KrbAsReqBuilder to resolve a AS-REP message using a password. + * @param password user provided password. not null + * @param asReq the original AS-REQ sent, used to validate AS-REP + * @param cname the user principal name, used to provide salt + */ + void decryptUsingPassword(char[] password, + KrbAsReq asReq, PrincipalName cname) + throws KrbException, Asn1Exception, IOException { + int encPartKeyType = rep.encPart.getEType(); + EncryptionKey dkey = EncryptionKey.acquireSecretKey( + cname, + password, + encPartKeyType, + PAData.getSaltAndParams(encPartKeyType, rep.pAData)); + decrypt(dkey, asReq); + } + + /** + * Decrypts encrypted content inside AS-REP. Called by initiator. + * @param dkey the decryption key to use + * @param asReq the original AS-REQ sent, used to validate AS-REP + */ + private void decrypt(EncryptionKey dkey, KrbAsReq asReq) + throws KrbException, Asn1Exception, IOException { + byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey, + KeyUsage.KU_ENC_AS_REP_PART); + byte[] enc_as_rep_part = rep.encPart.reset(enc_as_rep_bytes); + + DerValue encoding = new DerValue(enc_as_rep_part); + EncASRepPart enc_part = new EncASRepPart(encoding); + rep.encKDCRepPart = enc_part; + + ASReq req = asReq.getMessage(); + check(true, req, rep); + + creds = new Credentials( + rep.ticket, + req.reqBody.cname, + rep.ticket.sname, + enc_part.key, + enc_part.flags, + enc_part.authtime, + enc_part.starttime, + enc_part.endtime, + enc_part.renewTill, + enc_part.caddr); + if (DEBUG) { + System.out.println(">>> KrbAsRep cons in KrbAsReq.getReply " + + req.reqBody.cname.getNameString()); + } + } + + Credentials getCreds() { + return Objects.requireNonNull(creds, "Creds not available yet."); + } + + sun.security.krb5.internal.ccache.Credentials getCCreds() { + return new sun.security.krb5.internal.ccache.Credentials(rep); + } +} diff --git a/src/sun/security/krb5/KrbAsReq.java b/src/sun/security/krb5/KrbAsReq.java new file mode 100644 index 00000000..8de29b34 --- /dev/null +++ b/src/sun/security/krb5/KrbAsReq.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.Nonce; +import sun.security.krb5.internal.crypto.KeyUsage; +import java.io.IOException; + +/** + * This class encapsulates the KRB-AS-REQ message that the client + * sends to the KDC. + */ +public class KrbAsReq { + private ASReq asReqMessg; + + private boolean DEBUG = Krb5.DEBUG; + + /** + * Constructs an AS-REQ message. + */ + // Can be null? has default? + public KrbAsReq(EncryptionKey pakey, // ok + KDCOptions options, // ok, new KDCOptions() + PrincipalName cname, // NO and must have realm + PrincipalName sname, // ok, krgtgt@CREALM + KerberosTime from, // ok + KerberosTime till, // ok, will use + KerberosTime rtime, // ok + int[] eTypes, // NO + HostAddresses addresses // ok + ) + throws KrbException, IOException { + + if (options == null) { + options = new KDCOptions(); + } + + // check if they are valid arguments. The optional fields should be + // consistent with settings in KDCOptions. Mar 17 2000 + if (options.get(KDCOptions.FORWARDED) || + options.get(KDCOptions.PROXY) || + options.get(KDCOptions.ENC_TKT_IN_SKEY) || + options.get(KDCOptions.RENEW) || + options.get(KDCOptions.VALIDATE)) { + // this option is only specified in a request to the + // ticket-granting server + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.POSTDATED)) { + // if (from == null) + // throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } else { + if (from != null) from = null; + } + if (options.get(KDCOptions.RENEWABLE)) { + // if (rtime == null) + // throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } else { + if (rtime != null) rtime = null; + } + + PAData[] paData = null; + if (pakey != null) { + PAEncTSEnc ts = new PAEncTSEnc(); + byte[] temp = ts.asn1Encode(); + EncryptedData encTs = new EncryptedData(pakey, temp, + KeyUsage.KU_PA_ENC_TS); + paData = new PAData[1]; + paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP, + encTs.asn1Encode()); + } + + if (cname.getRealm() == null) { + throw new RealmException(Krb5.REALM_NULL, + "default realm not specified "); + } + + if (DEBUG) { + System.out.println(">>> KrbAsReq creating message"); + } + + // check to use addresses in tickets + if (addresses == null && Config.getInstance().useAddresses()) { + addresses = HostAddresses.getLocalAddresses(); + } + + if (sname == null) { + String realm = cname.getRealmAsString(); + sname = PrincipalName.tgsService(realm, realm); + } + + if (till == null) { + till = new KerberosTime(0); // Choose KDC maximum allowed + } + + // enc-authorization-data and additional-tickets never in AS-REQ + KDCReqBody kdc_req_body = new KDCReqBody(options, + cname, + sname, + from, + till, + rtime, + Nonce.value(), + eTypes, + addresses, + null, + null); + + asReqMessg = new ASReq( + paData, + kdc_req_body); + } + + byte[] encoding() throws IOException, Asn1Exception { + return asReqMessg.asn1Encode(); + } + + // Used by KrbAsRep to validate AS-REP + ASReq getMessage() { + return asReqMessg; + } +} diff --git a/src/sun/security/krb5/KrbAsReqBuilder.java b/src/sun/security/krb5/KrbAsReqBuilder.java new file mode 100644 index 00000000..29043890 --- /dev/null +++ b/src/sun/security/krb5/KrbAsReqBuilder.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5; + +import java.io.IOException; +import java.util.Arrays; +import javax.security.auth.kerberos.KeyTab; +import sun.security.jgss.krb5.Krb5Util; +import sun.security.krb5.internal.HostAddresses; +import sun.security.krb5.internal.KDCOptions; +import sun.security.krb5.internal.KRBError; +import sun.security.krb5.internal.KerberosTime; +import sun.security.krb5.internal.Krb5; +import sun.security.krb5.internal.PAData; +import sun.security.krb5.internal.crypto.EType; + +/** + * A manager class for AS-REQ communications. + * + * This class does: + * 1. Gather information to create AS-REQ + * 2. Create and send AS-REQ + * 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them + * 4. Emit credentials and secret keys (for JAAS storeKey=true with password) + * + * This class does not: + * 1. Deal with real communications (KdcComm does it, and TGS-REQ) + * a. Name of KDCs for a realm + * b. Server availability, timeout, UDP or TCP + * d. KRB_ERR_RESPONSE_TOO_BIG + * 2. Stores its own copy of password, this means: + * a. Do not change/wipe it before Builder finish + * b. Builder will not wipe it for you + * + * With this class: + * 1. KrbAsReq has only one constructor + * 2. Krb5LoginModule and Kinit call a single builder + * 3. Better handling of sensitive info + * + * @since 1.7 + */ + +public final class KrbAsReqBuilder { + + // Common data for AS-REQ fields + private KDCOptions options; + private PrincipalName cname; + private PrincipalName sname; + private KerberosTime from; + private KerberosTime till; + private KerberosTime rtime; + private HostAddresses addresses; + + // Secret source: can't be changed once assigned, only one (of the two + // sources) can be set to non-null + private final char[] password; + private final KeyTab ktab; + + // Used to create a ENC-TIMESTAMP in the 2nd AS-REQ + private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP. + // Used by getKeys() only. + // Only AS-REP should be enough per RFC, + // combined in case etypes are different. + + // The generated and received: + private KrbAsReq req; + private KrbAsRep rep; + + private static enum State { + INIT, // Initialized, can still add more initialization info + REQ_OK, // AS-REQ performed + DESTROYED, // Destroyed, not usable anymore + } + private State state; + + // Called by other constructors + private void init(PrincipalName cname) + throws KrbException { + this.cname = cname; + state = State.INIT; + } + + /** + * Creates a builder to be used by {@code cname} with existing keys. + * + * @param cname the client of the AS-REQ. Must not be null. Might have no + * realm, where default realm will be used. This realm will be the target + * realm for AS-REQ. I believe a client should only get initial TGT from + * its own realm. + * @param keys must not be null. if empty, might be quite useless. + * This argument will neither be modified nor stored by the method. + * @throws KrbException + */ + public KrbAsReqBuilder(PrincipalName cname, KeyTab ktab) + throws KrbException { + init(cname); + this.ktab = ktab; + this.password = null; + } + + /** + * Creates a builder to be used by {@code cname} with a known password. + * + * @param cname the client of the AS-REQ. Must not be null. Might have no + * realm, where default realm will be used. This realm will be the target + * realm for AS-REQ. I believe a client should only get initial TGT from + * its own realm. + * @param pass must not be null. This argument will neither be modified + * nor stored by the method. + * @throws KrbException + */ + public KrbAsReqBuilder(PrincipalName cname, char[] pass) + throws KrbException { + init(cname); + this.password = pass.clone(); + this.ktab = null; + } + + /** + * Retrieves an array of secret keys for the client. This is used when + * the client supplies password but need keys to act as an acceptor. For + * an initiator, it must be called after AS-REQ is performed (state is OK). + * For an acceptor, it can be called when this KrbAsReqBuilder object is + * constructed (state is INIT). + * @param isInitiator if the caller is an initiator + * @return generated keys from password. PA-DATA from server might be used. + * All "default_tkt_enctypes" keys will be generated, Never null. + * @throws IllegalStateException if not constructed from a password + * @throws KrbException + */ + public EncryptionKey[] getKeys(boolean isInitiator) throws KrbException { + checkState(isInitiator? State.REQ_OK: State.INIT, "Cannot get keys"); + if (password != null) { + int[] eTypes = EType.getDefaults("default_tkt_enctypes"); + EncryptionKey[] result = new EncryptionKey[eTypes.length]; + + /* + * Returns an array of keys. Before KrbAsReqBuilder, all etypes + * use the same salt which is either the default one or a new salt + * coming from PA-DATA. After KrbAsReqBuilder, each etype uses its + * own new salt from PA-DATA. For an etype with no PA-DATA new salt + * at all, what salt should it use? + * + * Commonly, the stored keys are only to be used by an acceptor to + * decrypt service ticket in AP-REQ. Most impls only allow keys + * from a keytab on acceptor, but unfortunately (?) Java supports + * acceptor using password. In this case, if the service ticket is + * encrypted using an etype which we don't have PA-DATA new salt, + * using the default salt might be wrong (say, case-insensitive + * user name). Instead, we would use the new salt of another etype. + */ + + String salt = null; // the saved new salt + try { + for (int i=0; i 0) { + int oldLen = paList.length; + paList = Arrays.copyOf(paList, paList.length + extraLen); + System.arraycopy(rep.getPA(), 0, paList, oldLen, extraLen); + } + } + } + return this; + } + + /** + * Communication until AS-REP or non preauth-related KRB-ERROR received + * @throws KrbException + * @throws IOException + */ + private KrbAsReqBuilder send() throws KrbException, IOException { + boolean preAuthFailedOnce = false; + KdcComm comm = new KdcComm(cname.getRealmAsString()); + EncryptionKey pakey = null; + while (true) { + try { + req = build(pakey); + rep = new KrbAsRep(comm.send(req.encoding())); + return this; + } catch (KrbException ke) { + if (!preAuthFailedOnce && ( + ke.returnCode() == Krb5.KDC_ERR_PREAUTH_FAILED || + ke.returnCode() == Krb5.KDC_ERR_PREAUTH_REQUIRED)) { + if (Krb5.DEBUG) { + System.out.println("KrbAsReqBuilder: " + + "PREAUTH FAILED/REQ, re-send AS-REQ"); + } + preAuthFailedOnce = true; + KRBError kerr = ke.getError(); + int paEType = PAData.getPreferredEType(kerr.getPA(), + EType.getDefaults("default_tkt_enctypes")[0]); + if (password == null) { + EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname); + pakey = EncryptionKey.findKey(paEType, ks); + if (pakey != null) pakey = (EncryptionKey)pakey.clone(); + for (EncryptionKey k: ks) k.destroy(); + } else { + pakey = EncryptionKey.acquireSecretKey(cname, + password, + paEType, + PAData.getSaltAndParams( + paEType, kerr.getPA())); + } + paList = kerr.getPA(); // Update current paList + } else { + throw ke; + } + } + } + } + + /** + * Performs AS-REQ send and AS-REP receive. + * Maybe a state is needed here, to divide prepare process and getCreds. + * @throws KrbException + * @throws Asn1Exception + * @throws IOException + */ + public KrbAsReqBuilder action() + throws KrbException, Asn1Exception, IOException { + checkState(State.INIT, "Cannot call action"); + state = State.REQ_OK; + return send().resolve(); + } + + /** + * Gets Credentials object after action + */ + public Credentials getCreds() { + checkState(State.REQ_OK, "Cannot retrieve creds"); + return rep.getCreds(); + } + + /** + * Gets another type of Credentials after action + */ + public sun.security.krb5.internal.ccache.Credentials getCCreds() { + checkState(State.REQ_OK, "Cannot retrieve CCreds"); + return rep.getCCreds(); + } + + /** + * Destroys the object and clears keys and password info. + */ + public void destroy() { + state = State.DESTROYED; + if (password != null) { + Arrays.fill(password, (char)0); + } + } + + /** + * Checks if the current state is the specified one. + * @param st the expected state + * @param msg error message if state is not correct + * @throws IllegalStateException if state is not correct + */ + private void checkState(State st, String msg) { + if (state != st) { + throw new IllegalStateException(msg + " at " + st + " state"); + } + } +} diff --git a/src/sun/security/krb5/KrbCred.java b/src/sun/security/krb5/KrbCred.java new file mode 100644 index 00000000..e6a47858 --- /dev/null +++ b/src/sun/security/krb5/KrbCred.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.KeyUsage; +import java.io.IOException; +import sun.security.util.DerValue; + +/** + * This class encapsulates the KRB-CRED message that a client uses to + * send its delegated credentials to a server. + * + * Supports delegation of one ticket only. + * @author Mayank Upadhyay + */ +public class KrbCred { + + private static boolean DEBUG = Krb5.DEBUG; + + private byte[] obuf = null; + private KRBCred credMessg = null; + private Ticket ticket = null; + private EncKrbCredPart encPart = null; + private Credentials creds = null; + private KerberosTime timeStamp = null; + + // Used in InitialToken with null key + public KrbCred(Credentials tgt, + Credentials serviceTicket, + EncryptionKey key) + throws KrbException, IOException { + + PrincipalName client = tgt.getClient(); + PrincipalName tgService = tgt.getServer(); + PrincipalName server = serviceTicket.getServer(); + if (!serviceTicket.getClient().equals(client)) + throw new KrbException(Krb5.KRB_ERR_GENERIC, + "Client principal does not match"); + + // XXX Check Windows flag OK-TO-FORWARD-TO + + // Invoke TGS-REQ to get a forwarded TGT for the peer + + KDCOptions options = new KDCOptions(); + options.set(KDCOptions.FORWARDED, true); + options.set(KDCOptions.FORWARDABLE, true); + + HostAddresses sAddrs = null; + // XXX Also NT_GSS_KRB5_PRINCIPAL can be a host based principal + // GSSName.NT_HOSTBASED_SERVICE should display with KRB_NT_SRV_HST + if (server.getNameType() == PrincipalName.KRB_NT_SRV_HST) + sAddrs= new HostAddresses(server); + + KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService, + null, null, null, null, sAddrs, null, null, null); + credMessg = createMessage(tgsReq.sendAndGetCreds(), key); + + obuf = credMessg.asn1Encode(); + } + + KRBCred createMessage(Credentials delegatedCreds, EncryptionKey key) + throws KrbException, IOException { + + EncryptionKey sessionKey + = delegatedCreds.getSessionKey(); + PrincipalName princ = delegatedCreds.getClient(); + Realm realm = princ.getRealm(); + PrincipalName tgService = delegatedCreds.getServer(); + + KrbCredInfo credInfo = new KrbCredInfo(sessionKey, + princ, delegatedCreds.flags, delegatedCreds.authTime, + delegatedCreds.startTime, delegatedCreds.endTime, + delegatedCreds.renewTill, tgService, + delegatedCreds.cAddr); + + timeStamp = KerberosTime.now(); + KrbCredInfo[] credInfos = {credInfo}; + EncKrbCredPart encPart = + new EncKrbCredPart(credInfos, + timeStamp, null, null, null, null); + + EncryptedData encEncPart = new EncryptedData(key, + encPart.asn1Encode(), KeyUsage.KU_ENC_KRB_CRED_PART); + + Ticket[] tickets = {delegatedCreds.ticket}; + + credMessg = new KRBCred(tickets, encEncPart); + + return credMessg; + } + + // Used in InitialToken, NULL_KEY might be used + public KrbCred(byte[] asn1Message, EncryptionKey key) + throws KrbException, IOException { + + credMessg = new KRBCred(asn1Message); + + ticket = credMessg.tickets[0]; + + if (credMessg.encPart.getEType() == 0) { + key = EncryptionKey.NULL_KEY; + } + byte[] temp = credMessg.encPart.decrypt(key, + KeyUsage.KU_ENC_KRB_CRED_PART); + byte[] plainText = credMessg.encPart.reset(temp); + DerValue encoding = new DerValue(plainText); + EncKrbCredPart encPart = new EncKrbCredPart(encoding); + + timeStamp = encPart.timeStamp; + + KrbCredInfo credInfo = encPart.ticketInfo[0]; + EncryptionKey credInfoKey = credInfo.key; + PrincipalName pname = credInfo.pname; + TicketFlags flags = credInfo.flags; + KerberosTime authtime = credInfo.authtime; + KerberosTime starttime = credInfo.starttime; + KerberosTime endtime = credInfo.endtime; + KerberosTime renewTill = credInfo.renewTill; + PrincipalName sname = credInfo.sname; + HostAddresses caddr = credInfo.caddr; + + if (DEBUG) { + System.out.println(">>>Delegated Creds have pname=" + pname + + " sname=" + sname + + " authtime=" + authtime + + " starttime=" + starttime + + " endtime=" + endtime + + "renewTill=" + renewTill); + } + creds = new Credentials(ticket, pname, sname, credInfoKey, + flags, authtime, starttime, endtime, renewTill, caddr); + } + + /** + * Returns the delegated credentials from the peer. + */ + public Credentials[] getDelegatedCreds() { + + Credentials[] allCreds = {creds}; + return allCreds; + } + + /** + * Returns the ASN.1 encoding that should be sent to the peer. + */ + public byte[] getMessage() { + return obuf; + } +} diff --git a/src/sun/security/krb5/KrbCryptoException.java b/src/sun/security/krb5/KrbCryptoException.java new file mode 100644 index 00000000..28522e83 --- /dev/null +++ b/src/sun/security/krb5/KrbCryptoException.java @@ -0,0 +1,45 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +/** + * KrbCryptoExceptoin is a wrapper exception for exceptions thrown by JCE. + * + * @author Yanni Zhang + */ +public class KrbCryptoException extends KrbException { + + private static final long serialVersionUID = -1657367919979982250L; + + public KrbCryptoException (String s) { + super(s); + } +} diff --git a/src/sun/security/krb5/KrbException.java b/src/sun/security/krb5/KrbException.java new file mode 100644 index 00000000..ac49fc00 --- /dev/null +++ b/src/sun/security/krb5/KrbException.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.Krb5; +import sun.security.krb5.internal.KRBError; + +public class KrbException extends Exception { + + private static final long serialVersionUID = -4993302876451928596L; + + private int returnCode; + private KRBError error; + + public KrbException(String s) { + super(s); + } + + public KrbException(Throwable cause) { + super(cause); + } + + public KrbException(int i) { + returnCode = i; + } + + public KrbException(int i, String s) { + this(s); + returnCode = i; + } + + public KrbException(KRBError e) { + returnCode = e.getErrorCode(); + error = e; + } + + public KrbException(KRBError e, String s) { + this(s); + returnCode = e.getErrorCode(); + error = e; + } + + public KRBError getError() { + return error; + } + + + public int returnCode() { + return returnCode; + } + + public String returnCodeSymbol() { + return returnCodeSymbol(returnCode); + } + + public static String returnCodeSymbol(int i) { + return "not yet implemented"; + } + + public String returnCodeMessage() { + return Krb5.getErrorMessage(returnCode); + } + + public static String errorMessage(int i) { + return Krb5.getErrorMessage(i); + } + + + public String krbErrorMessage() { + StringBuffer strbuf = new StringBuffer("krb_error " + returnCode); + String msg = getMessage(); + if (msg != null) { + strbuf.append(" "); + strbuf.append(msg); + } + return strbuf.toString(); + } + + /** + * Returns messages like: + * "Integrity check on decrypted field failed (31) - \ + * Could not decrypt service ticket" + * If the error code is 0 then the first half is skipped. + */ + public String getMessage() { + StringBuffer message = new StringBuffer(); + int returnCode = returnCode(); + if (returnCode != 0) { + message.append(returnCodeMessage()); + message.append(" (").append(returnCode()).append(')'); + } + String consMessage = super.getMessage(); + if (consMessage != null && consMessage.length() != 0) { + if (returnCode != 0) + message.append(" - "); + message.append(consMessage); + } + return message.toString(); + } + + public String toString() { + return ("KrbException: " + getMessage()); + } + + @Override public int hashCode() { + int result = 17; + result = 37 * result + returnCode; + if (error != null) { + result = 37 * result + error.hashCode(); + } + return result; + } + + @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof KrbException)) { + return false; + } + + KrbException other = (KrbException)obj; + if (returnCode != other.returnCode) { + return false; + } + return (error == null)?(other.error == null): + (error.equals(other.error)); + } +} diff --git a/src/sun/security/krb5/KrbKdcRep.java b/src/sun/security/krb5/KrbKdcRep.java new file mode 100644 index 00000000..dd0e9510 --- /dev/null +++ b/src/sun/security/krb5/KrbKdcRep.java @@ -0,0 +1,128 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; + +abstract class KrbKdcRep { + + static void check( + boolean isAsReq, + KDCReq req, + KDCRep rep + ) throws KrbApErrException { + + if (isAsReq && !req.reqBody.cname.equals(rep.cname)) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if (!req.reqBody.sname.equals(rep.encKDCRepPart.sname)) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if (req.reqBody.getNonce() != rep.encKDCRepPart.nonce) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if ( + ((req.reqBody.addresses != null && rep.encKDCRepPart.caddr != null) && + !req.reqBody.addresses.equals(rep.encKDCRepPart.caddr))) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + for (int i = 1; i < 6; i++) { + if (req.reqBody.kdcOptions.get(i) != + rep.encKDCRepPart.flags.get(i)) { + if (Krb5.DEBUG) { + System.out.println("> KrbKdcRep.check: at #" + i + + ". request for " + req.reqBody.kdcOptions.get(i) + + ", received " + rep.encKDCRepPart.flags.get(i)); + } + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + } + + // XXX Can renew a ticket but not ask for a renewable renewed ticket + // See impl of Credentials.renew(). + if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE) != + rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + if ((req.reqBody.from == null) || req.reqBody.from.isZero()) + // verify this is allowed + if ((rep.encKDCRepPart.starttime != null) && + !rep.encKDCRepPart.starttime.inClockSkew()) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + } + + if ((req.reqBody.from != null) && !req.reqBody.from.isZero()) + // verify this is allowed + if ((rep.encKDCRepPart.starttime != null) && + !req.reqBody.from.equals(rep.encKDCRepPart.starttime)) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if (!req.reqBody.till.isZero() && + rep.encKDCRepPart.endtime.greaterThan(req.reqBody.till)) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE)) + if (req.reqBody.rtime != null && !req.reqBody.rtime.isZero()) + // verify this is required + if ((rep.encKDCRepPart.renewTill == null) || + rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.rtime) + ) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + + if (req.reqBody.kdcOptions.get(KDCOptions.RENEWABLE_OK) && + rep.encKDCRepPart.flags.get(KDCOptions.RENEWABLE)) + if (!req.reqBody.till.isZero()) + // verify this is required + if ((rep.encKDCRepPart.renewTill == null) || + rep.encKDCRepPart.renewTill.greaterThan(req.reqBody.till) + ) { + rep.encKDCRepPart.key.destroy(); + throw new KrbApErrException(Krb5.KRB_AP_ERR_MODIFIED); + } + } + + +} diff --git a/src/sun/security/krb5/KrbPriv.java b/src/sun/security/krb5/KrbPriv.java new file mode 100644 index 00000000..090f5422 --- /dev/null +++ b/src/sun/security/krb5/KrbPriv.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import sun.security.util.*; +import java.io.IOException; + +/** XXX This class does not appear to be used. **/ + +class KrbPriv extends KrbAppMessage { + private byte[] obuf; + private byte[] userData; + + private KrbPriv(byte[] userData, + Credentials creds, + EncryptionKey subKey, + KerberosTime timestamp, + SeqNumber seqNumber, + HostAddress saddr, + HostAddress raddr + ) throws KrbException, IOException { + EncryptionKey reqKey = null; + if (subKey != null) + reqKey = subKey; + else + reqKey = creds.key; + + obuf = mk_priv( + userData, + reqKey, + timestamp, + seqNumber, + saddr, + raddr + ); + } + + private KrbPriv(byte[] msg, + Credentials creds, + EncryptionKey subKey, + SeqNumber seqNumber, + HostAddress saddr, + HostAddress raddr, + boolean timestampRequired, + boolean seqNumberRequired + ) throws KrbException, IOException { + + KRBPriv krb_priv = new KRBPriv(msg); + EncryptionKey reqKey = null; + if (subKey != null) + reqKey = subKey; + else + reqKey = creds.key; + userData = rd_priv(krb_priv, + reqKey, + seqNumber, + saddr, + raddr, + timestampRequired, + seqNumberRequired, + creds.client + ); + } + + public byte[] getMessage() throws KrbException { + return obuf; + } + + public byte[] getData() { + return userData; + } + + private byte[] mk_priv(byte[] userData, + EncryptionKey key, + KerberosTime timestamp, + SeqNumber seqNumber, + HostAddress sAddress, + HostAddress rAddress + ) throws Asn1Exception, IOException, + KdcErrException, KrbCryptoException { + + Integer usec = null; + Integer seqno = null; + + if (timestamp != null) + usec = new Integer(timestamp.getMicroSeconds()); + + if (seqNumber != null) { + seqno = new Integer(seqNumber.current()); + seqNumber.step(); + } + + EncKrbPrivPart unenc_encKrbPrivPart = + new EncKrbPrivPart(userData, + timestamp, + usec, + seqno, + sAddress, + rAddress + ); + + byte[] temp = unenc_encKrbPrivPart.asn1Encode(); + + EncryptedData encKrbPrivPart = + new EncryptedData(key, temp, + KeyUsage.KU_ENC_KRB_PRIV_PART); + + KRBPriv krb_priv = new KRBPriv(encKrbPrivPart); + + temp = krb_priv.asn1Encode(); + + return krb_priv.asn1Encode(); + } + + private byte[] rd_priv(KRBPriv krb_priv, + EncryptionKey key, + SeqNumber seqNumber, + HostAddress sAddress, + HostAddress rAddress, + boolean timestampRequired, + boolean seqNumberRequired, + PrincipalName cname + ) throws Asn1Exception, KdcErrException, + KrbApErrException, IOException, KrbCryptoException { + + byte[] bytes = krb_priv.encPart.decrypt(key, + KeyUsage.KU_ENC_KRB_PRIV_PART); + byte[] temp = krb_priv.encPart.reset(bytes); + DerValue ref = new DerValue(temp); + EncKrbPrivPart enc_part = new EncKrbPrivPart(ref); + + check(enc_part.timestamp, + enc_part.usec, + enc_part.seqNumber, + enc_part.sAddress, + enc_part.rAddress, + seqNumber, + sAddress, + rAddress, + timestampRequired, + seqNumberRequired, + cname + ); + + return enc_part.userData; + } +} diff --git a/src/sun/security/krb5/KrbSafe.java b/src/sun/security/krb5/KrbSafe.java new file mode 100644 index 00000000..a1dfccd1 --- /dev/null +++ b/src/sun/security/krb5/KrbSafe.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import java.io.IOException; + +class KrbSafe extends KrbAppMessage { + + private byte[] obuf; + private byte[] userData; + + public KrbSafe(byte[] userData, + Credentials creds, + EncryptionKey subKey, + KerberosTime timestamp, + SeqNumber seqNumber, + HostAddress saddr, + HostAddress raddr + ) throws KrbException, IOException { + EncryptionKey reqKey = null; + if (subKey != null) + reqKey = subKey; + else + reqKey = creds.key; + + obuf = mk_safe(userData, + reqKey, + timestamp, + seqNumber, + saddr, + raddr + ); + } + + public KrbSafe(byte[] msg, + Credentials creds, + EncryptionKey subKey, + SeqNumber seqNumber, + HostAddress saddr, + HostAddress raddr, + boolean timestampRequired, + boolean seqNumberRequired + ) throws KrbException, IOException { + + KRBSafe krb_safe = new KRBSafe(msg); + + EncryptionKey reqKey = null; + if (subKey != null) + reqKey = subKey; + else + reqKey = creds.key; + + userData = rd_safe( + krb_safe, + reqKey, + seqNumber, + saddr, + raddr, + timestampRequired, + seqNumberRequired, + creds.client + ); + } + + public byte[] getMessage() { + return obuf; + } + + public byte[] getData() { + return userData; + } + + private byte[] mk_safe(byte[] userData, + EncryptionKey key, + KerberosTime timestamp, + SeqNumber seqNumber, + HostAddress sAddress, + HostAddress rAddress + ) throws Asn1Exception, IOException, KdcErrException, + KrbApErrException, KrbCryptoException { + + Integer usec = null; + Integer seqno = null; + + if (timestamp != null) + usec = new Integer(timestamp.getMicroSeconds()); + + if (seqNumber != null) { + seqno = new Integer(seqNumber.current()); + seqNumber.step(); + } + + KRBSafeBody krb_safeBody = + new KRBSafeBody(userData, + timestamp, + usec, + seqno, + sAddress, + rAddress + ); + + byte[] temp = krb_safeBody.asn1Encode(); + Checksum cksum = new Checksum( + Checksum.SAFECKSUMTYPE_DEFAULT, + temp, + key, + KeyUsage.KU_KRB_SAFE_CKSUM + ); + + KRBSafe krb_safe = new KRBSafe(krb_safeBody, cksum); + + temp = krb_safe.asn1Encode(); + + return krb_safe.asn1Encode(); + } + + private byte[] rd_safe(KRBSafe krb_safe, + EncryptionKey key, + SeqNumber seqNumber, + HostAddress sAddress, + HostAddress rAddress, + boolean timestampRequired, + boolean seqNumberRequired, + PrincipalName cname + ) throws Asn1Exception, KdcErrException, + KrbApErrException, IOException, KrbCryptoException { + + byte[] temp = krb_safe.safeBody.asn1Encode(); + + if (!krb_safe.cksum.verifyKeyedChecksum(temp, key, + KeyUsage.KU_KRB_SAFE_CKSUM)) { + throw new KrbApErrException( + Krb5.KRB_AP_ERR_MODIFIED); + } + + check(krb_safe.safeBody.timestamp, + krb_safe.safeBody.usec, + krb_safe.safeBody.seqNumber, + krb_safe.safeBody.sAddress, + krb_safe.safeBody.rAddress, + seqNumber, + sAddress, + rAddress, + timestampRequired, + seqNumberRequired, + cname + ); + + return krb_safe.safeBody.userData; + } +} diff --git a/src/sun/security/krb5/KrbServiceLocator.java b/src/sun/security/krb5/KrbServiceLocator.java new file mode 100644 index 00000000..fa557c35 --- /dev/null +++ b/src/sun/security/krb5/KrbServiceLocator.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5; + +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Random; +import java.util.StringTokenizer; + +import javax.naming.*; +import javax.naming.directory.*; +import javax.naming.spi.NamingManager; + +/** + * This class discovers the location of Kerberos services by querying DNS, + * as defined in RFC 4120. + * + * @author Seema Malkani + * @since 1.7 + */ + +class KrbServiceLocator { + + private static final String SRV_RR = "SRV"; + private static final String[] SRV_RR_ATTR = new String[] {SRV_RR}; + + private static final String SRV_TXT = "TXT"; + private static final String[] SRV_TXT_ATTR = new String[] {SRV_TXT}; + + private static final Random random = new Random(); + + private KrbServiceLocator() { + } + + /** + * Locates the KERBEROS service for a given domain. + * Queries DNS for a list of KERBEROS Service Text Records (TXT) for a + * given domain name. + * Information on the mapping of DNS hostnames and domain names + * to Kerberos realms is stored using DNS TXT records + * + * @param domainName A string domain name. + * @param environment The possibly null environment of the context. + * @return An ordered list of hostports for the Kerberos service or null if + * the service has not been located. + */ + static String[] getKerberosService(String realmName) { + + // search realm in SRV TXT records + String dnsUrl = "dns:///_kerberos." + realmName; + String[] records = null; + try { + // Create the DNS context using NamingManager rather than using + // the initial context constructor. This avoids having the initial + // context constructor call itself (when processing the URL + // argument in the getAttributes call). + Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0)); + if (!(ctx instanceof DirContext)) { + return null; // cannot create a DNS context + } + Attributes attrs = + ((DirContext)ctx).getAttributes(dnsUrl, SRV_TXT_ATTR); + Attribute attr; + + if (attrs != null && ((attr = attrs.get(SRV_TXT)) != null)) { + int numValues = attr.size(); + int numRecords = 0; + String[] txtRecords = new String[numValues]; + + // gather the text records + int i = 0; + int j = 0; + while (i < numValues) { + try { + txtRecords[j] = (String)attr.get(i); + j++; + } catch (Exception e) { + // ignore bad value + } + i++; + } + numRecords = j; + + // trim + if (numRecords < numValues) { + String[] trimmed = new String[numRecords]; + System.arraycopy(txtRecords, 0, trimmed, 0, numRecords); + records = trimmed; + } else { + records = txtRecords; + } + } + } catch (NamingException e) { + // ignore + } + return records; + } + + /** + * Locates the KERBEROS service for a given domain. + * Queries DNS for a list of KERBEROS Service Location Records (SRV) for a + * given domain name. + * + * @param domainName A string domain name. + * @return An ordered list of hostports for the Kerberos service or null if + * the service has not been located. + */ + static String[] getKerberosService(String realmName, String protocol) { + + String dnsUrl = "dns:///_kerberos." + protocol + "." + realmName; + String[] hostports = null; + + try { + // Create the DNS context using NamingManager rather than using + // the initial context constructor. This avoids having the initial + // context constructor call itself (when processing the URL + // argument in the getAttributes call). + Context ctx = NamingManager.getURLContext("dns", new Hashtable<>(0)); + if (!(ctx instanceof DirContext)) { + return null; // cannot create a DNS context + } + Attributes attrs = + ((DirContext)ctx).getAttributes(dnsUrl, SRV_RR_ATTR); + Attribute attr; + + if (attrs != null && ((attr = attrs.get(SRV_RR)) != null)) { + int numValues = attr.size(); + int numRecords = 0; + SrvRecord[] srvRecords = new SrvRecord[numValues]; + + // create the service records + int i = 0; + int j = 0; + while (i < numValues) { + try { + srvRecords[j] = new SrvRecord((String) attr.get(i)); + j++; + } catch (Exception e) { + // ignore bad value + } + i++; + } + numRecords = j; + + // trim + if (numRecords < numValues) { + SrvRecord[] trimmed = new SrvRecord[numRecords]; + System.arraycopy(srvRecords, 0, trimmed, 0, numRecords); + srvRecords = trimmed; + } + + // Sort the service records in ascending order of their + // priority value. For records with equal priority, move + // those with weight 0 to the top of the list. + if (numRecords > 1) { + Arrays.sort(srvRecords); + } + + // extract the host and port number from each service record + hostports = extractHostports(srvRecords); + } + } catch (NamingException e) { + // e.printStackTrace(); + // ignore + } + return hostports; + } + + /** + * Extract hosts and port numbers from a list of SRV records. + * An array of hostports is returned or null if none were found. + */ + private static String[] extractHostports(SrvRecord[] srvRecords) { + String[] hostports = null; + + int head = 0; + int tail = 0; + int sublistLength = 0; + int k = 0; + for (int i = 0; i < srvRecords.length; i++) { + if (hostports == null) { + hostports = new String[srvRecords.length]; + } + // find the head and tail of the list of records having the same + // priority value. + head = i; + while (i < srvRecords.length - 1 && + srvRecords[i].priority == srvRecords[i + 1].priority) { + i++; + } + tail = i; + + // select hostports from the sublist + sublistLength = (tail - head) + 1; + for (int j = 0; j < sublistLength; j++) { + hostports[k++] = selectHostport(srvRecords, head, tail); + } + } + return hostports; + } + + /* + * Randomly select a service record in the range [head, tail] and return + * its hostport value. Follows the algorithm in RFC 2782. + */ + private static String selectHostport(SrvRecord[] srvRecords, int head, + int tail) { + if (head == tail) { + return srvRecords[head].hostport; + } + + // compute the running sum for records between head and tail + int sum = 0; + for (int i = head; i <= tail; i++) { + if (srvRecords[i] != null) { + sum += srvRecords[i].weight; + srvRecords[i].sum = sum; + } + } + String hostport = null; + + // If all records have zero weight, select first available one; + // otherwise, randomly select a record according to its weight + int target = (sum == 0 ? 0 : random.nextInt(sum + 1)); + for (int i = head; i <= tail; i++) { + if (srvRecords[i] != null && srvRecords[i].sum >= target) { + hostport = srvRecords[i].hostport; + srvRecords[i] = null; // make this record unavailable + break; + } + } + return hostport; + } + +/** + * This class holds a DNS service (SRV) record. + * See http://www.ietf.org/rfc/rfc2782.txt + */ + +static class SrvRecord implements Comparable { + + int priority; + int weight; + int sum; + String hostport; + + /** + * Creates a service record object from a string record. + * DNS supplies the string record in the following format: + *

    +     *           " "  " "  " " 
    +     * 
    + */ + SrvRecord(String srvRecord) throws Exception { + StringTokenizer tokenizer = new StringTokenizer(srvRecord, " "); + String port; + + if (tokenizer.countTokens() == 4) { + priority = Integer.parseInt(tokenizer.nextToken()); + weight = Integer.parseInt(tokenizer.nextToken()); + port = tokenizer.nextToken(); + hostport = tokenizer.nextToken() + ":" + port; + } else { + throw new IllegalArgumentException(); + } + } + + /* + * Sort records in ascending order of priority value. For records with + * equal priority move those with weight 0 to the top of the list. + */ + public int compareTo(SrvRecord that) { + if (priority > that.priority) { + return 1; // this > that + } else if (priority < that.priority) { + return -1; // this < that + } else if (weight == 0 && that.weight != 0) { + return -1; // this < that + } else if (weight != 0 && that.weight == 0) { + return 1; // this > that + } else { + return 0; // this == that + } + } +} +} diff --git a/src/sun/security/krb5/KrbTgsRep.java b/src/sun/security/krb5/KrbTgsRep.java new file mode 100644 index 00000000..6fc6ceba --- /dev/null +++ b/src/sun/security/krb5/KrbTgsRep.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.util.*; +import java.io.IOException; + +/** + * This class encapsulates a TGS-REP that is sent from the KDC to the + * Kerberos client. + */ +public class KrbTgsRep extends KrbKdcRep { + private TGSRep rep; + private Credentials creds; + private Ticket secondTicket; + private static final boolean DEBUG = Krb5.DEBUG; + + KrbTgsRep(byte[] ibuf, KrbTgsReq tgsReq) + throws KrbException, IOException { + DerValue ref = new DerValue(ibuf); + TGSReq req = tgsReq.getMessage(); + TGSRep rep = null; + try { + rep = new TGSRep(ref); + } catch (Asn1Exception e) { + rep = null; + KRBError err = new KRBError(ref); + String errStr = err.getErrorString(); + String eText = null; // pick up text sent by the server (if any) + if (errStr != null && errStr.length() > 0) { + if (errStr.charAt(errStr.length() - 1) == 0) + eText = errStr.substring(0, errStr.length() - 1); + else + eText = errStr; + } + KrbException ke; + if (eText == null) { + // no text sent from server + ke = new KrbException(err.getErrorCode()); + } else { + // override default text with server text + ke = new KrbException(err.getErrorCode(), eText); + } + ke.initCause(e); + throw ke; + } + byte[] enc_tgs_rep_bytes = rep.encPart.decrypt(tgsReq.tgsReqKey, + tgsReq.usedSubkey() ? KeyUsage.KU_ENC_TGS_REP_PART_SUBKEY : + KeyUsage.KU_ENC_TGS_REP_PART_SESSKEY); + + byte[] enc_tgs_rep_part = rep.encPart.reset(enc_tgs_rep_bytes); + ref = new DerValue(enc_tgs_rep_part); + EncTGSRepPart enc_part = new EncTGSRepPart(ref); + rep.encKDCRepPart = enc_part; + + check(false, req, rep); + + this.creds = new Credentials(rep.ticket, + rep.cname, + rep.ticket.sname, + enc_part.key, + enc_part.flags, + enc_part.authtime, + enc_part.starttime, + enc_part.endtime, + enc_part.renewTill, + enc_part.caddr + ); + this.rep = rep; + this.secondTicket = tgsReq.getSecondTicket(); + } + + /** + * Return the credentials that were contained in this KRB-TGS-REP. + */ + public Credentials getCreds() { + return creds; + } + + sun.security.krb5.internal.ccache.Credentials setCredentials() { + return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket); + } +} diff --git a/src/sun/security/krb5/KrbTgsReq.java b/src/sun/security/krb5/KrbTgsReq.java new file mode 100644 index 00000000..45f25e82 --- /dev/null +++ b/src/sun/security/krb5/KrbTgsReq.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import java.io.IOException; +import java.net.UnknownHostException; + +/** + * This class encapsulates a Kerberos TGS-REQ that is sent from the + * client to the KDC. + */ +public class KrbTgsReq { + + private PrincipalName princName; + private PrincipalName servName; + private TGSReq tgsReqMessg; + private KerberosTime ctime; + private Ticket secondTicket = null; + private boolean useSubkey = false; + EncryptionKey tgsReqKey; + + private static final boolean DEBUG = Krb5.DEBUG; + + private byte[] obuf; + private byte[] ibuf; + + // Used in CredentialsUtil + public KrbTgsReq(Credentials asCreds, + PrincipalName sname) + throws KrbException, IOException { + this(new KDCOptions(), + asCreds, + sname, + null, // KerberosTime from + null, // KerberosTime till + null, // KerberosTime rtime + null, // eTypes, // null, // int[] eTypes + null, // HostAddresses addresses + null, // AuthorizationData authorizationData + null, // Ticket[] additionalTickets + null); // EncryptionKey subSessionKey + } + + // S4U2proxy + public KrbTgsReq(Credentials asCreds, + Ticket second, + PrincipalName sname) + throws KrbException, IOException { + this(KDCOptions.with(KDCOptions.CNAME_IN_ADDL_TKT, + KDCOptions.FORWARDABLE), + asCreds, + sname, + null, + null, + null, + null, + null, + null, + new Ticket[] {second}, // the service ticket + null); + } + + // S4U2user + public KrbTgsReq(Credentials asCreds, + PrincipalName sname, + PAData extraPA) + throws KrbException, IOException { + this(KDCOptions.with(KDCOptions.FORWARDABLE), + asCreds, + asCreds.getClient(), + sname, + null, + null, + null, + null, + null, + null, + null, + null, + extraPA); // the PA-FOR-USER + } + + // Called by Credentials, KrbCred + KrbTgsReq( + KDCOptions options, + Credentials asCreds, + PrincipalName sname, + KerberosTime from, + KerberosTime till, + KerberosTime rtime, + int[] eTypes, + HostAddresses addresses, + AuthorizationData authorizationData, + Ticket[] additionalTickets, + EncryptionKey subKey) throws KrbException, IOException { + this(options, asCreds, asCreds.getClient(), sname, + from, till, rtime, eTypes, addresses, + authorizationData, additionalTickets, subKey, null); + } + + private KrbTgsReq( + KDCOptions options, + Credentials asCreds, + PrincipalName cname, + PrincipalName sname, + KerberosTime from, + KerberosTime till, + KerberosTime rtime, + int[] eTypes, + HostAddresses addresses, + AuthorizationData authorizationData, + Ticket[] additionalTickets, + EncryptionKey subKey, + PAData extraPA) throws KrbException, IOException { + + princName = cname; + servName = sname; + ctime = KerberosTime.now(); + + // check if they are valid arguments. The optional fields + // should be consistent with settings in KDCOptions. + + // TODO: Is this necessary? If the TGT is not FORWARDABLE, + // you can still request for a FORWARDABLE ticket, just the + // KDC will give you a non-FORWARDABLE one. Even if you + // cannot use the ticket expected, it still contains info. + // This means there will be problem later. We already have + // flags check in KrbTgsRep. Of course, sometimes the KDC + // will not issue the ticket at all. + + if (options.get(KDCOptions.FORWARDABLE) && + (!(asCreds.flags.get(Krb5.TKT_OPTS_FORWARDABLE)))) { + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.FORWARDED)) { + if (!(asCreds.flags.get(KDCOptions.FORWARDABLE))) + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.PROXIABLE) && + (!(asCreds.flags.get(Krb5.TKT_OPTS_PROXIABLE)))) { + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.PROXY)) { + if (!(asCreds.flags.get(KDCOptions.PROXIABLE))) + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.ALLOW_POSTDATE) && + (!(asCreds.flags.get(Krb5.TKT_OPTS_MAY_POSTDATE)))) { + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + if (options.get(KDCOptions.RENEWABLE) && + (!(asCreds.flags.get(Krb5.TKT_OPTS_RENEWABLE)))) { + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } + + if (options.get(KDCOptions.POSTDATED)) { + if (!(asCreds.flags.get(KDCOptions.POSTDATED))) + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } else { + if (from != null) from = null; + } + if (options.get(KDCOptions.RENEWABLE)) { + if (!(asCreds.flags.get(KDCOptions.RENEWABLE))) + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + } else { + if (rtime != null) rtime = null; + } + if (options.get(KDCOptions.ENC_TKT_IN_SKEY) || options.get(KDCOptions.CNAME_IN_ADDL_TKT)) { + if (additionalTickets == null) + throw new KrbException(Krb5.KRB_AP_ERR_REQ_OPTIONS); + // in TGS_REQ there could be more than one additional + // tickets, but in file-based credential cache, + // there is only one additional ticket field. + secondTicket = additionalTickets[0]; + } else { + if (additionalTickets != null) + additionalTickets = null; + } + + tgsReqMessg = createRequest( + options, + asCreds.ticket, + asCreds.key, + ctime, + princName, + servName, + from, + till, + rtime, + eTypes, + addresses, + authorizationData, + additionalTickets, + subKey, + extraPA); + obuf = tgsReqMessg.asn1Encode(); + + // XXX We need to revisit this to see if can't move it + // up such that FORWARDED flag set in the options + // is included in the marshaled request. + /* + * If this is based on a forwarded ticket, record that in the + * options, because the returned TgsRep will contain the + * FORWARDED flag set. + */ + if (asCreds.flags.get(KDCOptions.FORWARDED)) + options.set(KDCOptions.FORWARDED, true); + + + } + + /** + * Sends a TGS request to the realm of the target. + * @throws KrbException + * @throws IOException + */ + public void send() throws IOException, KrbException { + String realmStr = null; + if (servName != null) + realmStr = servName.getRealmString(); + KdcComm comm = new KdcComm(realmStr); + ibuf = comm.send(obuf); + } + + public KrbTgsRep getReply() + throws KrbException, IOException { + return new KrbTgsRep(ibuf, this); + } + + /** + * Sends the request, waits for a reply, and returns the Credentials. + * Used in Credentials, KrbCred, and internal/CredentialsUtil. + */ + public Credentials sendAndGetCreds() throws IOException, KrbException { + KrbTgsRep tgs_rep = null; + String kdc = null; + send(); + tgs_rep = getReply(); + return tgs_rep.getCreds(); + } + + KerberosTime getCtime() { + return ctime; + } + + private TGSReq createRequest( + KDCOptions kdc_options, + Ticket ticket, + EncryptionKey key, + KerberosTime ctime, + PrincipalName cname, + PrincipalName sname, + KerberosTime from, + KerberosTime till, + KerberosTime rtime, + int[] eTypes, + HostAddresses addresses, + AuthorizationData authorizationData, + Ticket[] additionalTickets, + EncryptionKey subKey, + PAData extraPA) + throws IOException, KrbException, UnknownHostException { + KerberosTime req_till = null; + if (till == null) { + req_till = new KerberosTime(0); + } else { + req_till = till; + } + + /* + * RFC 4120, Section 5.4.2. + * For KRB_TGS_REP, the ciphertext is encrypted in the + * sub-session key from the Authenticator, or if absent, + * the session key from the ticket-granting ticket used + * in the request. + * + * To support this, use tgsReqKey to remember which key to use. + */ + tgsReqKey = key; + + int[] req_eTypes = null; + if (eTypes == null) { + req_eTypes = EType.getDefaults("default_tgs_enctypes"); + } else { + req_eTypes = eTypes; + } + + EncryptionKey reqKey = null; + EncryptedData encAuthorizationData = null; + if (authorizationData != null) { + byte[] ad = authorizationData.asn1Encode(); + if (subKey != null) { + reqKey = subKey; + tgsReqKey = subKey; // Key to use to decrypt reply + useSubkey = true; + encAuthorizationData = new EncryptedData(reqKey, ad, + KeyUsage.KU_TGS_REQ_AUTH_DATA_SUBKEY); + } else + encAuthorizationData = new EncryptedData(key, ad, + KeyUsage.KU_TGS_REQ_AUTH_DATA_SESSKEY); + } + + KDCReqBody reqBody = new KDCReqBody( + kdc_options, + cname, + sname, + from, + req_till, + rtime, + Nonce.value(), + req_eTypes, + addresses, + encAuthorizationData, + additionalTickets); + + byte[] temp = reqBody.asn1Encode(Krb5.KRB_TGS_REQ); + // if the checksum type is one of the keyed checksum types, + // use session key. + Checksum cksum; + switch (Checksum.CKSUMTYPE_DEFAULT) { + case Checksum.CKSUMTYPE_RSA_MD4_DES: + case Checksum.CKSUMTYPE_DES_MAC: + case Checksum.CKSUMTYPE_DES_MAC_K: + case Checksum.CKSUMTYPE_RSA_MD4_DES_K: + case Checksum.CKSUMTYPE_RSA_MD5_DES: + case Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD: + case Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR: + case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128: + case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256: + cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp, key, + KeyUsage.KU_PA_TGS_REQ_CKSUM); + break; + case Checksum.CKSUMTYPE_CRC32: + case Checksum.CKSUMTYPE_RSA_MD4: + case Checksum.CKSUMTYPE_RSA_MD5: + default: + cksum = new Checksum(Checksum.CKSUMTYPE_DEFAULT, temp); + } + + // Usage will be KeyUsage.KU_PA_TGS_REQ_AUTHENTICATOR + + byte[] tgs_ap_req = new KrbApReq( + new APOptions(), + ticket, + key, + cname, + cksum, + ctime, + reqKey, + null, + null).getMessage(); + + PAData tgsPAData = new PAData(Krb5.PA_TGS_REQ, tgs_ap_req); + return new TGSReq( + extraPA != null ? + new PAData[] {extraPA, tgsPAData } : + new PAData[] {tgsPAData}, + reqBody); + } + + TGSReq getMessage() { + return tgsReqMessg; + } + + Ticket getSecondTicket() { + return secondTicket; + } + + private static void debug(String message) { + // System.err.println(">>> KrbTgsReq: " + message); + } + + boolean usedSubkey() { + return useSubkey; + } + +} diff --git a/src/sun/security/krb5/PrincipalName.java b/src/sun/security/krb5/PrincipalName.java new file mode 100644 index 00000000..b9c0f7a3 --- /dev/null +++ b/src/sun/security/krb5/PrincipalName.java @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.*; +import sun.security.util.*; +import java.net.*; +import java.util.Vector; +import java.util.Locale; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import sun.security.krb5.internal.ccache.CCacheOutputStream; +import sun.security.krb5.internal.util.KerberosString; + + +/** + * Implements the ASN.1 PrincipalName type and its realm in a single class. + * + * Realm ::= KerberosString + * + * PrincipalName ::= SEQUENCE { + * name-type [0] Int32, + * name-string [1] SEQUENCE OF KerberosString + * } + * + * This class is immutable. + * @see Realm + */ +public class PrincipalName implements Cloneable { + + //name types + + /** + * Name type not known + */ + public static final int KRB_NT_UNKNOWN = 0; + + /** + * Just the name of the principal as in DCE, or for users + */ + public static final int KRB_NT_PRINCIPAL = 1; + + /** + * Service and other unique instance (krbtgt) + */ + public static final int KRB_NT_SRV_INST = 2; + + /** + * Service with host name as instance (telnet, rcommands) + */ + public static final int KRB_NT_SRV_HST = 3; + + /** + * Service with host as remaining components + */ + public static final int KRB_NT_SRV_XHST = 4; + + /** + * Unique ID + */ + public static final int KRB_NT_UID = 5; + + /** + * TGS Name + */ + public static final String TGS_DEFAULT_SRV_NAME = "krbtgt"; + public static final int TGS_DEFAULT_NT = KRB_NT_SRV_INST; + + public static final char NAME_COMPONENT_SEPARATOR = '/'; + public static final char NAME_REALM_SEPARATOR = '@'; + public static final char REALM_COMPONENT_SEPARATOR = '.'; + + public static final String NAME_COMPONENT_SEPARATOR_STR = "/"; + public static final String NAME_REALM_SEPARATOR_STR = "@"; + public static final String REALM_COMPONENT_SEPARATOR_STR = "."; + + // Instance fields. + + /** + * The name type, from PrincipalName's name-type field. + */ + private final int nameType; + + /** + * The name strings, from PrincipalName's name-strings field. This field + * must be neither null nor empty. Each entry of it must also be neither + * null nor empty. Make sure to clone the field when it's passed in or out. + */ + private final String[] nameStrings; + + /** + * The realm this principal belongs to. + */ + private final Realm nameRealm; // not null + + // cached default salt, not used in clone + private transient String salt = null; + + // There are 3 basic constructors. All other constructors must call them. + // All basic constructors must call validateNameStrings. + // 1. From name components + // 2. From name + // 3. From DER encoding + + /** + * Creates a PrincipalName. + */ + public PrincipalName(int nameType, String[] nameStrings, Realm nameRealm) { + if (nameRealm == null) { + throw new IllegalArgumentException("Null realm not allowed"); + } + validateNameStrings(nameStrings); + this.nameType = nameType; + this.nameStrings = nameStrings.clone(); + this.nameRealm = nameRealm; + } + + // This method is called by Windows NativeCred.c + public PrincipalName(String[] nameParts, String realm) throws RealmException { + this(KRB_NT_UNKNOWN, nameParts, new Realm(realm)); + } + + public PrincipalName(String[] nameParts, int type) + throws IllegalArgumentException, RealmException { + this(type, nameParts, Realm.getDefault()); + } + + // Validate a nameStrings argument + private static void validateNameStrings(String[] ns) { + if (ns == null) { + throw new IllegalArgumentException("Null nameStrings not allowed"); + } + if (ns.length == 0) { + throw new IllegalArgumentException("Empty nameStrings not allowed"); + } + for (String s: ns) { + if (s == null) { + throw new IllegalArgumentException("Null nameString not allowed"); + } + if (s.isEmpty()) { + throw new IllegalArgumentException("Empty nameString not allowed"); + } + } + } + + public Object clone() { + try { + PrincipalName pName = (PrincipalName) super.clone(); + UNSAFE.putObject(this, NAME_STRINGS_OFFSET, nameStrings.clone()); + return pName; + } catch (CloneNotSupportedException ex) { + throw new AssertionError("Should never happen"); + } + } + + private static final long NAME_STRINGS_OFFSET; + private static final sun.misc.Unsafe UNSAFE; + static { + try { + sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); + NAME_STRINGS_OFFSET = unsafe.objectFieldOffset( + PrincipalName.class.getDeclaredField("nameStrings")); + UNSAFE = unsafe; + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof PrincipalName) { + PrincipalName other = (PrincipalName)o; + return nameRealm.equals(other.nameRealm) && + Arrays.equals(nameStrings, other.nameStrings); + } + return false; + } + + /** + * Returns the ASN.1 encoding of the + * + * PrincipalName ::= SEQUENCE { + * name-type [0] Int32, + * name-string [1] SEQUENCE OF KerberosString + * } + * + * KerberosString ::= GeneralString (IA5String) + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + * + * @param encoding a Der-encoded data. + * @param realm the realm for this name + * @exception Asn1Exception if an error occurs while decoding + * an ASN1 encoded data. + * @exception Asn1Exception if there is an ASN1 encoding error + * @exception IOException if an I/O error occurs + * @exception IllegalArgumentException if encoding is null + * reading encoded data. + */ + public PrincipalName(DerValue encoding, Realm realm) + throws Asn1Exception, IOException { + if (realm == null) { + throw new IllegalArgumentException("Null realm not allowed"); + } + nameRealm = realm; + DerValue der; + if (encoding == null) { + throw new IllegalArgumentException("Null encoding not allowed"); + } + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + BigInteger bint = der.getData().getBigInteger(); + nameType = bint.intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x01F) == 0x01) { + DerValue subDer = der.getData().getDerValue(); + if (subDer.getTag() != DerValue.tag_SequenceOf) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + Vector v = new Vector<>(); + DerValue subSubDer; + while(subDer.getData().available() > 0) { + subSubDer = subDer.getData().getDerValue(); + String namePart = new KerberosString(subSubDer).toString(); + v.addElement(namePart); + } + nameStrings = new String[v.size()]; + v.copyInto(nameStrings); + validateNameStrings(nameStrings); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Parse (unmarshal) a PrincipalName from a DER + * input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or + * more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @param realm the realm for the name + * @return an instance of PrincipalName, or null if the + * field is optional and missing. + */ + public static PrincipalName parse(DerInputStream data, + byte explicitTag, boolean + optional, + Realm realm) + throws Asn1Exception, IOException, RealmException { + + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != + explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + if (realm == null) { + realm = Realm.getDefault(); + } + return new PrincipalName(subDer, realm); + } + } + + + // XXX Error checkin consistent with MIT krb5_parse_name + // Code repetition, realm parsed again by class Realm + private static String[] parseName(String name) { + + Vector tempStrings = new Vector<>(); + String temp = name; + int i = 0; + int componentStart = 0; + String component; + + while (i < temp.length()) { + if (temp.charAt(i) == NAME_COMPONENT_SEPARATOR) { + /* + * If this separator is escaped then don't treat it + * as a separator + */ + if (i > 0 && temp.charAt(i - 1) == '\\') { + temp = temp.substring(0, i - 1) + + temp.substring(i, temp.length()); + continue; + } + else { + if (componentStart <= i) { + component = temp.substring(componentStart, i); + tempStrings.addElement(component); + } + componentStart = i + 1; + } + } else { + if (temp.charAt(i) == NAME_REALM_SEPARATOR) { + /* + * If this separator is escaped then don't treat it + * as a separator + */ + if (i > 0 && temp.charAt(i - 1) == '\\') { + temp = temp.substring(0, i - 1) + + temp.substring(i, temp.length()); + continue; + } else { + if (componentStart < i) { + component = temp.substring(componentStart, i); + tempStrings.addElement(component); + } + componentStart = i + 1; + break; + } + } + } + i++; + } + + if (i == temp.length()) { + component = temp.substring(componentStart, i); + tempStrings.addElement(component); + } + + String[] result = new String[tempStrings.size()]; + tempStrings.copyInto(result); + return result; + } + + /** + * Constructs a PrincipalName from a string. + * @param name the name + * @param type the type + * @param realm the realm, null if not known. Note that when realm is not + * null, it will be always used even if there is a realm part in name. When + * realm is null, will read realm part from name, or try to map a realm + * (for KRB_NT_SRV_HST), or use the default realm, or fail + * @throws RealmException + */ + public PrincipalName(String name, int type, String realm) + throws RealmException { + if (name == null) { + throw new IllegalArgumentException("Null name not allowed"); + } + String[] nameParts = parseName(name); + validateNameStrings(nameParts); + if (realm == null) { + realm = Realm.parseRealmAtSeparator(name); + } + switch (type) { + case KRB_NT_SRV_HST: + if (nameParts.length >= 2) { + String hostName = nameParts[1]; + try { + // RFC4120 does not recommend canonicalizing a hostname. + // However, for compatibility reason, we will try + // canonicalize it and see if the output looks better. + + String canonicalized = (InetAddress.getByName(hostName)). + getCanonicalHostName(); + + // Looks if canonicalized is a longer format of hostName, + // we accept cases like + // bunny -> bunny.rabbit.hole + if (canonicalized.toLowerCase(Locale.ENGLISH).startsWith( + hostName.toLowerCase(Locale.ENGLISH)+".")) { + hostName = canonicalized; + } + } catch (UnknownHostException e) { + // no canonicalization, use old + } + nameParts[1] = hostName.toLowerCase(Locale.ENGLISH); + } + nameStrings = nameParts; + nameType = type; + + if (realm != null) { + nameRealm = new Realm(realm); + } else { + // We will try to get realm name from the mapping in + // the configuration. If it is not specified + // we will use the default realm. This nametype does + // not allow a realm to be specified. The name string must of + // the form service@host and this is internally changed into + // service/host by Kerberos + String mapRealm = mapHostToRealm(nameParts[1]); + if (mapRealm != null) { + nameRealm = new Realm(mapRealm); + } else { + nameRealm = Realm.getDefault(); + } + } + break; + case KRB_NT_UNKNOWN: + case KRB_NT_PRINCIPAL: + case KRB_NT_SRV_INST: + case KRB_NT_SRV_XHST: + case KRB_NT_UID: + nameStrings = nameParts; + nameType = type; + if (realm != null) { + nameRealm = new Realm(realm); + } else { + nameRealm = Realm.getDefault(); + } + break; + default: + throw new IllegalArgumentException("Illegal name type"); + } + } + + public PrincipalName(String name, int type) throws RealmException { + this(name, type, (String)null); + } + + public PrincipalName(String name) throws RealmException { + this(name, KRB_NT_UNKNOWN); + } + + public PrincipalName(String name, String realm) throws RealmException { + this(name, KRB_NT_UNKNOWN, realm); + } + + public static PrincipalName tgsService(String r1, String r2) + throws KrbException { + return new PrincipalName(PrincipalName.KRB_NT_SRV_INST, + new String[] {PrincipalName.TGS_DEFAULT_SRV_NAME, r1}, + new Realm(r2)); + } + + public String getRealmAsString() { + return getRealmString(); + } + + public String getPrincipalNameAsString() { + StringBuffer temp = new StringBuffer(nameStrings[0]); + for (int i = 1; i < nameStrings.length; i++) + temp.append(nameStrings[i]); + return temp.toString(); + } + + public int hashCode() { + return toString().hashCode(); + } + + public String getName() { + return toString(); + } + + public int getNameType() { + return nameType; + } + + public String[] getNameStrings() { + return nameStrings.clone(); + } + + public byte[][] toByteArray() { + byte[][] result = new byte[nameStrings.length][]; + for (int i = 0; i < nameStrings.length; i++) { + result[i] = new byte[nameStrings[i].length()]; + result[i] = nameStrings[i].getBytes(); + } + return result; + } + + public String getRealmString() { + return nameRealm.toString(); + } + + public Realm getRealm() { + return nameRealm; + } + + public String getSalt() { + if (salt == null) { + StringBuffer salt = new StringBuffer(); + salt.append(nameRealm.toString()); + for (int i = 0; i < nameStrings.length; i++) { + salt.append(nameStrings[i]); + } + return salt.toString(); + } + return salt; + } + + public String toString() { + StringBuffer str = new StringBuffer(); + for (int i = 0; i < nameStrings.length; i++) { + if (i > 0) + str.append("/"); + str.append(nameStrings[i]); + } + str.append("@"); + str.append(nameRealm.toString()); + return str.toString(); + } + + public String getNameString() { + StringBuffer str = new StringBuffer(); + for (int i = 0; i < nameStrings.length; i++) { + if (i > 0) + str.append("/"); + str.append(nameStrings[i]); + } + return str.toString(); + } + + /** + * Encodes a PrincipalName object. Note that only the type and + * names are encoded. To encode the realm, call getRealm().asn1Encode(). + * @return the byte array of the encoded PrncipalName object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + BigInteger bint = BigInteger.valueOf(this.nameType); + temp.putInteger(bint); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + DerValue der[] = new DerValue[nameStrings.length]; + for (int i = 0; i < nameStrings.length; i++) { + der[i] = new KerberosString(nameStrings[i]).toDerValue(); + } + temp.putSequence(der); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + + /** + * Checks if two PrincipalName objects have identical values in their corresponding data fields. + * + * @param pname the other PrincipalName object. + * @return true if two have identical values, otherwise, return false. + */ + // It is used in sun.security.krb5.internal.ccache package. + public boolean match(PrincipalName pname) { + boolean matched = true; + //name type is just a hint, no two names can be the same ignoring name type. + // if (this.nameType != pname.nameType) { + // matched = false; + // } + if ((this.nameRealm != null) && (pname.nameRealm != null)) { + if (!(this.nameRealm.toString().equalsIgnoreCase(pname.nameRealm.toString()))) { + matched = false; + } + } + if (this.nameStrings.length != pname.nameStrings.length) { + matched = false; + } else { + for (int i = 0; i < this.nameStrings.length; i++) { + if (!(this.nameStrings[i].equalsIgnoreCase(pname.nameStrings[i]))) { + matched = false; + } + } + } + return matched; + } + + /** + * Writes data field values of PrincipalName in FCC format to an output stream. + * + * @param cos a CCacheOutputStream for writing data. + * @exception IOException if an I/O exception occurs. + * @see CCacheOutputStream + */ + public void writePrincipal(CCacheOutputStream cos) throws IOException { + cos.write32(nameType); + cos.write32(nameStrings.length); + byte[] realmBytes = null; + realmBytes = nameRealm.toString().getBytes(); + cos.write32(realmBytes.length); + cos.write(realmBytes, 0, realmBytes.length); + byte[] bytes = null; + for (int i = 0; i < nameStrings.length; i++) { + bytes = nameStrings[i].getBytes(); + cos.write32(bytes.length); + cos.write(bytes, 0, bytes.length); + } + } + + /** + * Returns the instance component of a name. + * In a multi-component name such as a KRB_NT_SRV_INST + * name, the second component is returned. + * Null is returned if there are not two or more + * components in the name. + * @returns instance component of a multi-component name. + */ + public String getInstanceComponent() + { + if (nameStrings != null && nameStrings.length >= 2) + { + return new String(nameStrings[1]); + } + + return null; + } + + static String mapHostToRealm(String name) { + String result = null; + try { + String subname = null; + Config c = Config.getInstance(); + if ((result = c.get("domain_realm", name)) != null) + return result; + else { + for (int i = 1; i < name.length(); i++) { + if ((name.charAt(i) == '.') && (i != name.length() - 1)) { //mapping could be .ibm.com = AUSTIN.IBM.COM + subname = name.substring(i); + result = c.get("domain_realm", subname); + if (result != null) { + break; + } + else { + subname = name.substring(i + 1); //or mapping could be ibm.com = AUSTIN.IBM.COM + result = c.get("domain_realm", subname); + if (result != null) { + break; + } + } + } + } + } + } catch (KrbException e) { + } + return result; + } + +} diff --git a/src/sun/security/krb5/Realm.java b/src/sun/security/krb5/Realm.java new file mode 100644 index 00000000..65f1f6fd --- /dev/null +++ b/src/sun/security/krb5/Realm.java @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5; + +import sun.security.krb5.internal.Krb5; +import sun.security.util.*; +import java.io.IOException; +import java.util.*; + +import sun.security.krb5.internal.util.KerberosString; + +/** + * Implements the ASN.1 Realm type. + * + *

    + * Realm ::= GeneralString + * + * This class is immutable. + */ +public class Realm implements Cloneable { + private final String realm; // not null nor empty + + public Realm(String name) throws RealmException { + realm = parseRealm(name); + } + + public static Realm getDefault() throws RealmException { + try { + return new Realm(Config.getInstance().getDefaultRealm()); + } catch (RealmException re) { + throw re; + } catch (KrbException ke) { + throw new RealmException(ke); + } + } + + // Immutable class, no need to clone + public Object clone() { + return this; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof Realm)) { + return false; + } + + Realm that = (Realm)obj; + return this.realm.equals(that.realm); + } + + public int hashCode() { + return realm.hashCode(); + } + + /** + * Constructs a Realm object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + public Realm(DerValue encoding) + throws Asn1Exception, RealmException, IOException { + if (encoding == null) { + throw new IllegalArgumentException("encoding can not be null"); + } + realm = new KerberosString(encoding).toString(); + if (realm == null || realm.length() == 0) + throw new RealmException(Krb5.REALM_NULL); + if (!isValidRealmString(realm)) + throw new RealmException(Krb5.REALM_ILLCHAR); + } + + public String toString() { + return realm; + } + + // Extract realm from a string like dummy@REALM + public static String parseRealmAtSeparator(String name) + throws RealmException { + if (name == null) { + throw new IllegalArgumentException + ("null input name is not allowed"); + } + String temp = new String(name); + String result = null; + int i = 0; + while (i < temp.length()) { + if (temp.charAt(i) == PrincipalName.NAME_REALM_SEPARATOR) { + if (i == 0 || temp.charAt(i - 1) != '\\') { + if (i + 1 < temp.length()) { + result = temp.substring(i + 1, temp.length()); + } else { + throw new IllegalArgumentException + ("empty realm part not allowed"); + } + break; + } + } + i++; + } + if (result != null) { + if (result.length() == 0) + throw new RealmException(Krb5.REALM_NULL); + if (!isValidRealmString(result)) + throw new RealmException(Krb5.REALM_ILLCHAR); + } + return result; + } + + public static String parseRealmComponent(String name) { + if (name == null) { + throw new IllegalArgumentException + ("null input name is not allowed"); + } + String temp = new String(name); + String result = null; + int i = 0; + while (i < temp.length()) { + if (temp.charAt(i) == PrincipalName.REALM_COMPONENT_SEPARATOR) { + if (i == 0 || temp.charAt(i - 1) != '\\') { + if (i + 1 < temp.length()) + result = temp.substring(i + 1, temp.length()); + break; + } + } + i++; + } + return result; + } + + protected static String parseRealm(String name) throws RealmException { + String result = parseRealmAtSeparator(name); + if (result == null) + result = name; + if (result == null || result.length() == 0) + throw new RealmException(Krb5.REALM_NULL); + if (!isValidRealmString(result)) + throw new RealmException(Krb5.REALM_ILLCHAR); + return result; + } + + // This is protected because the definition of a realm + // string is fixed + protected static boolean isValidRealmString(String name) { + if (name == null) + return false; + if (name.length() == 0) + return false; + for (int i = 0; i < name.length(); i++) { + if (name.charAt(i) == '/' || + name.charAt(i) == ':' || + name.charAt(i) == '\0') { + return false; + } + } + return true; + } + + /** + * Encodes a Realm object. + * @return the byte array of encoded KrbCredInfo object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream out = new DerOutputStream(); + out.putDerValue(new KerberosString(this.realm).toDerValue()); + return out.toByteArray(); + } + + + /** + * Parse (unmarshal) a realm from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @return an instance of Realm. + * + */ + public static Realm parse(DerInputStream data, byte explicitTag, boolean optional) + throws Asn1Exception, IOException, RealmException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) { + return null; + } + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new Realm(subDer); + } + } + + /** + * Returns an array of realms that may be traversed to obtain + * a TGT from the initiating realm cRealm to the target realm + * sRealm. + *
    + * This method would read [capaths] to create a path, or generate a + * hierarchical path if [capaths] does not contain a sub-stanza for cRealm + * or the sub-stanza does not contain a tag for sRealm. + *
    + * The returned list would never be null, and it always contains + * cRealm as the head entry. sRealm is not included as the tail. + * + * @param cRealm the initiating realm, not null + * @param sRealm the target realm, not null, not equals to cRealm + * @returns array of realms including at least cRealm as the first + * element + */ + public static String[] getRealmsList(String cRealm, String sRealm) { + try { + // Try [capaths] + return parseCapaths(cRealm, sRealm); + } catch (KrbException ke) { + // Now assume the realms are organized hierarchically. + return parseHierarchy(cRealm, sRealm); + } + } + + /** + * Parses the [capaths] stanza of the configuration file for a + * list of realms to traverse to obtain credentials from the + * initiating realm cRealm to the target realm sRealm. + * + * For a given client realm C there is a tag C in [capaths] whose + * subtag S has a value which is a (possibly partial) path from C + * to S. When the path is partial, it contains only the tail of the + * full path. Values of other subtags will be used to build the full + * path. The value "." means a direct path from C to S. If realm S + * does not appear as a subtag, there is no path defined here. + * + * The implementation ignores all values which equals to C or S, or + * a "." in multiple values, or any duplicated realm names. + * + * When a path value has more than two realms, they can be specified + * with multiple key-value pairs each having a single value, but the + * order must not change. + * + * For example: + * + * [capaths] + * TIVOLI.COM = { + * IBM.COM = IBM_LDAPCENTRAL.COM MOONLITE.ORG + * IBM_LDAPCENTRAL.COM = LDAPCENTRAL.NET + * LDAPCENTRAL.NET = . + * } + * + * TIVOLI.COM has a direct path to LDAPCENTRAL.NET, which has a direct + * path to IBM_LDAPCENTRAL.COM. It also has a partial path to IBM.COM + * being "IBM_LDAPCENTRAL.COM MOONLITE.ORG". Merging these info together, + * a full path from TIVOLI.COM to IBM.COM will be + * + * TIVOLI.COM -> LDAPCENTRAL.NET -> IBM_LDAPCENTRAL.COM + * -> IBM_LDAPCENTRAL.COM -> MOONLITE.ORG + * + * Please note the sRealm IBM.COM does not appear in the path. + * + * @param cRealm the initiating realm + * @param sRealm the target realm, not the same as cRealm + * @returns array of realms including at least cRealm as the first + * element + * @throws KrbException if the config does not contain a sub-stanza + * for cRealm in [capaths] or the sub-stanza does not contain + * sRealm as a tag + */ + private static String[] parseCapaths(String cRealm, String sRealm) + throws KrbException { + + // This line could throw a KrbException + Config cfg = Config.getInstance(); + + if (!cfg.exists("capaths", cRealm, sRealm)) { + throw new KrbException("No conf"); + } + + LinkedList path = new LinkedList<>(); + + String head = sRealm; + while (true) { + String value = cfg.getAll("capaths", cRealm, head); + if (value == null) { + break; + } + String[] more = value.split("\\s+"); + boolean changed = false; + for (int i=more.length-1; i>=0; i--) { + if (path.contains(more[i]) + || more[i].equals(".") + || more[i].equals(cRealm) + || more[i].equals(sRealm) + || more[i].equals(head)) { + // Ignore invalid values + continue; + } + changed = true; + path.addFirst(more[i]); + } + if (!changed) break; + head = path.getFirst(); + } + path.addFirst(cRealm); + return path.toArray(new String[path.size()]); + } + + /** + * Build a list of realm that can be traversed + * to obtain credentials from the initiating realm cRealm + * for a service in the target realm sRealm. + * @param cRealm the initiating realm + * @param sRealm the target realm, not the same as cRealm + * @returns array of realms including cRealm as the first element + */ + private static String[] parseHierarchy(String cRealm, String sRealm) { + + String[] cComponents = cRealm.split("\\."); + String[] sComponents = sRealm.split("\\."); + + int cPos = cComponents.length; + int sPos = sComponents.length; + + boolean hasCommon = false; + for (sPos--, cPos--; sPos >=0 && cPos >= 0 && + sComponents[sPos].equals(cComponents[cPos]); + sPos--, cPos--) { + hasCommon = true; + } + + // For those with common components: + // length pos + // SITES1.SALES.EXAMPLE.COM 4 1 + // EVERYWHERE.EXAMPLE.COM 3 0 + + // For those without common components: + // length pos + // DEVEL.EXAMPLE.COM 3 2 + // PROD.EXAMPLE.ORG 3 2 + + LinkedList path = new LinkedList<>(); + + // Un-common ones for client side + for (int i=0; i<=cPos; i++) { + path.addLast(subStringFrom(cComponents, i)); + } + + // Common one + if (hasCommon) { + path.addLast(subStringFrom(cComponents, cPos+1)); + } + + // Un-common ones for server side + for (int i=sPos; i>=0; i--) { + path.addLast(subStringFrom(sComponents, i)); + } + + // Remove sRealm from path. Note that it might be added at last loop + // or as a common component, if sRealm is a parent of cRealm + path.removeLast(); + + return path.toArray(new String[path.size()]); + } + + /** + * Creates a realm name using components from the given position. + * For example, subStringFrom({"A", "B", "C"}, 1) is "B.C". + */ + private static String subStringFrom(String[] components, int from) { + StringBuilder sb = new StringBuilder(); + for (int i=from; i getKerberosConfig(); + private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG; + + static { + boolean isMac = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Boolean run() { + String osname = System.getProperty("os.name"); + if (osname.contains("OS X")) { + System.loadLibrary("osx"); + return true; + } + return false; + } + }); + if (isMac) installNotificationCallback(); + } + + private static Vector unwrapHost( + Collection> c) { + Vector vector = new Vector(); + for (Hashtable m : c) { + vector.add(m.get("host")); + } + return vector; + } + + /** + * convertRealmConfigs: Maps the Object graph that we get from JNI to the + * object graph that Config expects. Also the items inside the kdc array + * are wrapped inside Hashtables + */ + @SuppressWarnings("unchecked") + private static Hashtable + convertRealmConfigs(Hashtable configs) { + Hashtable realmsTable = new Hashtable(); + + for (String realm : configs.keySet()) { + // get the kdc + Hashtable> map = + (Hashtable>) configs.get(realm); + Hashtable> realmMap = + new Hashtable>(); + + // put the kdc into the realmMap + Collection> kdc = + (Collection>) map.get("kdc"); + if (kdc != null) realmMap.put("kdc", unwrapHost(kdc)); + + // put the admin server into the realmMap + Collection> kadmin = + (Collection>) map.get("kadmin"); + if (kadmin != null) realmMap.put("admin_server", unwrapHost(kadmin)); + + // add the full entry to the realmTable + realmsTable.put(realm, realmMap); + } + + return realmsTable; + } + + /** + * Calls down to JNI to get the raw Kerberos Config and maps the object + * graph to the one that Kerberos Config in Java expects + * + * @return + * @throws IOException + */ + public static Hashtable getConfig() throws IOException { + Hashtable stanzaTable = getKerberosConfig(); + if (stanzaTable == null) { + throw new IOException( + "Could not load configuration from SCDynamicStore"); + } + if (DEBUG) System.out.println("Raw map from JNI: " + stanzaTable); + return convertNativeConfig(stanzaTable); + } + + @SuppressWarnings("unchecked") + private static Hashtable convertNativeConfig( + Hashtable stanzaTable) { + // convert SCDynamicStore realm structure to Java realm structure + Hashtable realms = + (Hashtable) stanzaTable.get("realms"); + if (realms != null) { + stanzaTable.remove("realms"); + Hashtable realmsTable = convertRealmConfigs(realms); + stanzaTable.put("realms", realmsTable); + } + WrapAllStringInVector(stanzaTable); + if (DEBUG) System.out.println("stanzaTable : " + stanzaTable); + return stanzaTable; + } + + @SuppressWarnings("unchecked") + private static void WrapAllStringInVector( + Hashtable stanzaTable) { + for (String s: stanzaTable.keySet()) { + Object v = stanzaTable.get(s); + if (v instanceof Hashtable) { + WrapAllStringInVector((Hashtable)v); + } else if (v instanceof String) { + Vector vec = new Vector<>(); + vec.add((String)v); + stanzaTable.put(s, vec); + } + } + } +} diff --git a/src/sun/security/krb5/internal/APOptions.java b/src/sun/security/krb5/internal/APOptions.java new file mode 100644 index 00000000..c9644af6 --- /dev/null +++ b/src/sun/security/krb5/internal/APOptions.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.internal.util.KerberosFlags; +import sun.security.util.*; +import java.io.IOException; + +/** + * Implements the ASN.1 APOptions type. + * + * + * APOptions ::= KerberosFlags + * -- reserved(0), + * -- use-session-key(1), + * -- mutual-required(2) + * + * + * KerberosFlags ::= BIT STRING (SIZE (32..MAX)) + * -- minimum number of bits shall be sent, + * -- but no fewer than 32 + * + *

    + * This definition reflects the Network Working Group RFC4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class APOptions extends KerberosFlags { + public APOptions() { + super(Krb5.AP_OPTS_MAX + 1); + } + + public APOptions(int oneBit) throws Asn1Exception { + super(Krb5.AP_OPTS_MAX + 1); + set(oneBit, true); + } + public APOptions(int size, byte[] data) throws Asn1Exception { + super(size, data); + if ((size > data.length * BITS_PER_UNIT) || (size > Krb5.AP_OPTS_MAX + 1)) { + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + } + + public APOptions(boolean[] data) throws Asn1Exception { + super(data); + if (data.length > Krb5.AP_OPTS_MAX + 1) { + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + } + + public APOptions(DerValue encoding) throws IOException, Asn1Exception { + this(encoding.getUnalignedBitString(true).toBooleanArray()); + } + + /** + * Parse (unmarshal) an APOptions from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional. + * @return an instance of APOptions. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public static APOptions parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new APOptions(subDer); + } + } +} diff --git a/src/sun/security/krb5/internal/APRep.java b/src/sun/security/krb5/internal/APRep.java new file mode 100644 index 00000000..4a8bea86 --- /dev/null +++ b/src/sun/security/krb5/internal/APRep.java @@ -0,0 +1,141 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 AP-REP type. + * + *

    + * AP-REP ::= [APPLICATION 15] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (15), + * enc-part [2] EncryptedData -- EncAPRepPart + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class APRep { + + public int pvno; + public int msgType; + public EncryptedData encPart; + + public APRep(EncryptedData new_encPart) { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_AP_REP; + encPart = new_encPart; + } + + public APRep(byte[] data) throws Asn1Exception, + KrbApErrException, IOException { + init(new DerValue(data)); + } + + public APRep(DerValue encoding) throws Asn1Exception, + KrbApErrException, IOException { + init(encoding); + } + + /** + * Initializes an APRep object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + */ + private void init(DerValue encoding) throws Asn1Exception, + KrbApErrException, IOException { + + if (((encoding.getTag() & (byte) (0x1F)) != Krb5.KRB_AP_REP) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + DerValue der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + DerValue subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x01) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_AP_REP) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + encPart = EncryptedData.parse(der.getData(), (byte) 0x02, false); + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an APRep object. + * @return byte array of encoded APRep object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), encPart.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + DerOutputStream aprep = new DerOutputStream(); + aprep.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x0F), temp); + return aprep.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/APReq.java b/src/sun/security/krb5/internal/APReq.java new file mode 100644 index 00000000..c70fe202 --- /dev/null +++ b/src/sun/security/krb5/internal/APReq.java @@ -0,0 +1,151 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 AP-REQ type. + * + *

    + * AP-REQ ::= [APPLICATION 14] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (14), + * ap-options [2] APOptions, + * ticket [3] Ticket, + * authenticator [4] EncryptedData -- Authenticator + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class APReq { + + public int pvno; + public int msgType; + public APOptions apOptions; + public Ticket ticket; + public EncryptedData authenticator; + + public APReq( + APOptions new_apOptions, + Ticket new_ticket, + EncryptedData new_authenticator) { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_AP_REQ; + apOptions = new_apOptions; + ticket = new_ticket; + authenticator = new_authenticator; + } + + public APReq(byte[] data) throws Asn1Exception, IOException, KrbApErrException, RealmException { + init(new DerValue(data)); + } + + public APReq(DerValue encoding) throws Asn1Exception, IOException, KrbApErrException, RealmException { + init(encoding); + } + + /** + * Initializes an APReq object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data stream does not match the pre-defined value. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private void init(DerValue encoding) throws Asn1Exception, + IOException, KrbApErrException, RealmException { + DerValue der, subDer; + if (((encoding.getTag() & (byte) 0x1F) != Krb5.KRB_AP_REQ) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x01) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_AP_REQ) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + apOptions = APOptions.parse(der.getData(), (byte) 0x02, false); + ticket = Ticket.parse(der.getData(), (byte) 0x03, false); + authenticator = EncryptedData.parse(der.getData(), (byte) 0x04, false); + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an APReq object. + * @return byte array of encoded APReq object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), apOptions.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), ticket.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x04), authenticator.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + DerOutputStream apreq = new DerOutputStream(); + apreq.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x0E), temp); + return apreq.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/ASRep.java b/src/sun/security/krb5/internal/ASRep.java new file mode 100644 index 00000000..e1961054 --- /dev/null +++ b/src/sun/security/krb5/internal/ASRep.java @@ -0,0 +1,65 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.PrincipalName; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.RealmException; +import sun.security.util.*; +import java.io.IOException; + +public class ASRep extends KDCRep { + + public ASRep( + PAData[] new_pAData, + PrincipalName new_cname, + Ticket new_ticket, + EncryptedData new_encPart) throws IOException { + super(new_pAData, new_cname, new_ticket, + new_encPart, Krb5.KRB_AS_REP); + } + + public ASRep(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + } + + public ASRep(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding, Krb5.KRB_AS_REP); + } +} diff --git a/src/sun/security/krb5/internal/ASReq.java b/src/sun/security/krb5/internal/ASReq.java new file mode 100644 index 00000000..b689c704 --- /dev/null +++ b/src/sun/security/krb5/internal/ASReq.java @@ -0,0 +1,54 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.io.IOException; + +public class ASReq extends KDCReq { + + public ASReq(PAData[] new_pAData, KDCReqBody new_reqBody) throws IOException { + super(new_pAData, new_reqBody, Krb5.KRB_AS_REQ); + } + + public ASReq(byte[] data) throws Asn1Exception, KrbException, IOException { + init(new DerValue(data)); + } + + public ASReq(DerValue encoding) throws Asn1Exception, KrbException, IOException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, IOException, KrbException { + super.init(encoding, Krb5.KRB_AS_REQ); + } +} diff --git a/src/sun/security/krb5/internal/AuthContext.java b/src/sun/security/krb5/internal/AuthContext.java new file mode 100644 index 00000000..05ed71b3 --- /dev/null +++ b/src/sun/security/krb5/internal/AuthContext.java @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.EncryptionKey; +import java.util.BitSet; + +public class AuthContext { + public HostAddress remoteAddress; + public int remotePort; + public HostAddress localAddress; + public int localPort; + public EncryptionKey keyBlock; + public EncryptionKey localSubkey; + public EncryptionKey remoteSubkey; + public BitSet authContextFlags; + public int remoteSeqNumber; + public int localSeqNumber; + public Authenticator authenticator; + public int reqCksumType; + public int safeCksumType; + public byte[] initializationVector; + //public ReplayCache replayCache; +}; diff --git a/src/sun/security/krb5/internal/Authenticator.java b/src/sun/security/krb5/internal/Authenticator.java new file mode 100644 index 00000000..62201355 --- /dev/null +++ b/src/sun/security/krb5/internal/Authenticator.java @@ -0,0 +1,221 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.util.Vector; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 Authenticator type. + * + *

    + * Authenticator ::= [APPLICATION 2] SEQUENCE { + * authenticator-vno [0] INTEGER (5), + * crealm [1] Realm, + * cname [2] PrincipalName, + * cksum [3] Checksum OPTIONAL, + * cusec [4] Microseconds, + * ctime [5] KerberosTime, + * subkey [6] EncryptionKey OPTIONAL, + * seq-number [7] UInt32 OPTIONAL, + * authorization-data [8] AuthorizationData OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class Authenticator { + + public int authenticator_vno; + public PrincipalName cname; + Checksum cksum; //optional + public int cusec; + public KerberosTime ctime; + EncryptionKey subKey; //optional + Integer seqNumber; //optional + public AuthorizationData authorizationData; //optional + + public Authenticator( + PrincipalName new_cname, + Checksum new_cksum, + int new_cusec, + KerberosTime new_ctime, + EncryptionKey new_subKey, + Integer new_seqNumber, + AuthorizationData new_authorizationData) { + authenticator_vno = Krb5.AUTHNETICATOR_VNO; + cname = new_cname; + cksum = new_cksum; + cusec = new_cusec; + ctime = new_ctime; + subKey = new_subKey; + seqNumber = new_seqNumber; + authorizationData = new_authorizationData; + } + + public Authenticator(byte[] data) + throws Asn1Exception, IOException, KrbApErrException, RealmException { + init(new DerValue(data)); + } + + public Authenticator(DerValue encoding) + throws Asn1Exception, IOException, KrbApErrException, RealmException { + init(encoding); + } + + /** + * Initializes an Authenticator object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private void init(DerValue encoding) + throws Asn1Exception, IOException, KrbApErrException, RealmException { + DerValue der, subDer; + //may not be the correct error code for a tag + //mismatch on an encrypted structure + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x02) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) != (byte) 0x00) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + authenticator_vno = subDer.getData().getBigInteger().intValue(); + if (authenticator_vno != 5) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + Realm crealm = Realm.parse(der.getData(), (byte) 0x01, false); + cname = PrincipalName.parse(der.getData(), (byte) 0x02, false, crealm); + cksum = Checksum.parse(der.getData(), (byte) 0x03, true); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == 0x04) { + cusec = subDer.getData().getBigInteger().intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + ctime = KerberosTime.parse(der.getData(), (byte) 0x05, false); + if (der.getData().available() > 0) { + subKey = EncryptionKey.parse(der.getData(), (byte) 0x06, true); + } else { + subKey = null; + seqNumber = null; + authorizationData = null; + } + if (der.getData().available() > 0) { + if ((der.getData().peekByte() & 0x1F) == 0x07) { + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x07) { + seqNumber = new Integer(subDer.getData().getBigInteger().intValue()); + } + } + } else { + seqNumber = null; + authorizationData = null; + } + if (der.getData().available() > 0) { + authorizationData = AuthorizationData.parse(der.getData(), (byte) 0x08, true); + } else { + authorizationData = null; + } + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an Authenticator object. + * @return byte array of encoded Authenticator object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + Vector v = new Vector<>(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(authenticator_vno)); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp.toByteArray())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), cname.getRealm().asn1Encode())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), cname.asn1Encode())); + if (cksum != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), cksum.asn1Encode())); + } + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(cusec)); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x04), temp.toByteArray())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x05), ctime.asn1Encode())); + if (subKey != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x06), subKey.asn1Encode())); + } + if (seqNumber != null) { + temp = new DerOutputStream(); + // encode as an unsigned integer (UInt32) + temp.putInteger(BigInteger.valueOf(seqNumber.longValue())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x07), temp.toByteArray())); + } + if (authorizationData != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x08), authorizationData.asn1Encode())); + } + DerValue der[] = new DerValue[v.size()]; + v.copyInto(der); + temp = new DerOutputStream(); + temp.putSequence(der); + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x02), temp); + return out.toByteArray(); + } + + public final Checksum getChecksum() { + return cksum; + } + + public final Integer getSeqNumber() { + return seqNumber; + } + + public final EncryptionKey getSubKey() { + return subKey; + } +} diff --git a/src/sun/security/krb5/internal/AuthorizationData.java b/src/sun/security/krb5/internal/AuthorizationData.java new file mode 100644 index 00000000..9a9de819 --- /dev/null +++ b/src/sun/security/krb5/internal/AuthorizationData.java @@ -0,0 +1,184 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.util.Vector; +import java.io.IOException; +import sun.security.krb5.internal.ccache.CCacheOutputStream; + +/** + * In RFC4120, the ASN.1 AuthorizationData is defined as: + * + * AuthorizationData ::= SEQUENCE OF SEQUENCE { + * ad-type [0] Int32, + * ad-data [1] OCTET STRING + * } + * + * Here, two classes are used to implement it and they can be represented as follows: + * + * AuthorizationData ::= SEQUENCE OF AuthorizationDataEntry + * AuthorizationDataEntry ::= SEQUENCE { + * ad-type[0] Int32, + * ad-data[1] OCTET STRING + * } + */ +public class AuthorizationData implements Cloneable { + + private AuthorizationDataEntry[] entry = null; + + private AuthorizationData() { + } + + public AuthorizationData(AuthorizationDataEntry[] new_entries) + throws IOException { + if (new_entries != null) { + entry = new AuthorizationDataEntry[new_entries.length]; + for (int i = 0; i < new_entries.length; i++) { + if (new_entries[i] == null) { + throw new IOException("Cannot create an AuthorizationData"); + } else { + entry[i] = (AuthorizationDataEntry) new_entries[i].clone(); + } + } + } + } + + public AuthorizationData(AuthorizationDataEntry new_entry) { + entry = new AuthorizationDataEntry[1]; + entry[0] = new_entry; + } + + public Object clone() { + AuthorizationData new_authorizationData = + new AuthorizationData(); + if (entry != null) { + new_authorizationData.entry = + new AuthorizationDataEntry[entry.length]; + for (int i = 0; i < entry.length; i++) { + new_authorizationData.entry[i] = + (AuthorizationDataEntry) entry[i].clone(); + } + } + return new_authorizationData; + } + + /** + * Constructs a new AuthorizationData, instance. + * @param der a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public AuthorizationData(DerValue der) throws Asn1Exception, IOException { + Vector v = new Vector<>(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + while (der.getData().available() > 0) { + v.addElement(new AuthorizationDataEntry(der.getData().getDerValue())); + } + if (v.size() > 0) { + entry = new AuthorizationDataEntry[v.size()]; + v.copyInto(entry); + } + } + + /** + * Encodes an AuthorizationData object. + * @return byte array of encoded AuthorizationData object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerValue der[] = new DerValue[entry.length]; + for (int i = 0; i < entry.length; i++) { + der[i] = new DerValue(entry[i].asn1Encode()); + } + bytes.putSequence(der); + return bytes.toByteArray(); + } + + /** + * Parse (unmarshal) an AuthorizationData object from a DER input stream. + * This form of parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of AuthorizationData. + * + */ + public static AuthorizationData parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte) data.peekByte() & (byte) 0x1F) != explicitTag)) { + return null; + } + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte) 0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new AuthorizationData(subDer); + } + } + + /** + * Writes AuthorizationData data fields to a output stream. + * + * @param cos a CCacheOutputStream to be written to. + * @exception IOException if an I/O exception occurs. + */ + public void writeAuth(CCacheOutputStream cos) throws IOException { + for (int i = 0; i < entry.length; i++) { + entry[i].writeEntry(cos); + } + } + + public String toString() { + String retVal = "AuthorizationData:\n"; + for (int i = 0; i < entry.length; i++) { + retVal += entry[i].toString(); + } + return retVal; + } + + public int count() { + return entry.length; + } + + public AuthorizationDataEntry item(int i) { + return (AuthorizationDataEntry)entry[i].clone(); + } +} diff --git a/src/sun/security/krb5/internal/AuthorizationDataEntry.java b/src/sun/security/krb5/internal/AuthorizationDataEntry.java new file mode 100644 index 00000000..bde49893 --- /dev/null +++ b/src/sun/security/krb5/internal/AuthorizationDataEntry.java @@ -0,0 +1,124 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import java.io.IOException; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.internal.ccache.CCacheOutputStream; + +public class AuthorizationDataEntry implements Cloneable { + + public int adType; + public byte[] adData; + + private AuthorizationDataEntry() { + } + + public AuthorizationDataEntry( + int new_adType, + byte[] new_adData) { + adType = new_adType; + adData = new_adData; + } + + public Object clone() { + AuthorizationDataEntry new_authorizationDataEntry = + new AuthorizationDataEntry(); + new_authorizationDataEntry.adType = adType; + if (adData != null) { + new_authorizationDataEntry.adData = new byte[adData.length]; + System.arraycopy(adData, 0, + new_authorizationDataEntry.adData, 0, adData.length); + } + return new_authorizationDataEntry; + } + + /** + * Constructs an instance of AuthorizationDataEntry. + * @param encoding a single DER-encoded value. + */ + public AuthorizationDataEntry(DerValue encoding) throws Asn1Exception, IOException { + DerValue der; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte) 0x1F) == (byte) 0x00) { + adType = der.getData().getBigInteger().intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte) 0x1F) == (byte) 0x01) { + adData = der.getData().getOctetString(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an AuthorizationDataEntry object. + * @return byte array of encoded AuthorizationDataEntry object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(adType); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp); + temp = new DerOutputStream(); + temp.putOctetString(adData); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Writes the entry's data fields in FCC format to an output stream. + * + * @param cos a CCacheOutputStream. + * @exception IOException if an I/O exception occurs. + */ + public void writeEntry(CCacheOutputStream cos) throws IOException { + cos.write16(adType); + cos.write32(adData.length); + cos.write(adData, 0, adData.length); + } + + public String toString() { + return ("adType=" + adType + " adData.length=" + adData.length); + } +} diff --git a/src/sun/security/krb5/internal/CredentialsUtil.java b/src/sun/security/krb5/internal/CredentialsUtil.java new file mode 100644 index 00000000..6b61f7bc --- /dev/null +++ b/src/sun/security/krb5/internal/CredentialsUtil.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import java.io.IOException; + +/** + * This class is a utility that contains much of the TGS-Exchange + * protocol. It is used by ../Credentials.java for service ticket + * acquisition in both the normal and the x-realm case. + */ +public class CredentialsUtil { + + private static boolean DEBUG = Krb5.DEBUG; + + /** + * Used by a middle server to acquire credentials on behalf of a + * client to itself using the S4U2self extension. + * @param client the client to impersonate + * @param ccreds the TGT of the middle service + * @return the new creds (cname=client, sname=middle) + */ + public static Credentials acquireS4U2selfCreds(PrincipalName client, + Credentials ccreds) throws KrbException, IOException { + String uRealm = client.getRealmString(); + String localRealm = ccreds.getClient().getRealmString(); + if (!uRealm.equals(localRealm)) { + // TODO: we do not support kerberos referral now + throw new KrbException("Cross realm impersonation not supported"); + } + KrbTgsReq req = new KrbTgsReq( + ccreds, + ccreds.getClient(), + new PAData(Krb5.PA_FOR_USER, + new PAForUserEnc(client, + ccreds.getSessionKey()).asn1Encode())); + Credentials creds = req.sendAndGetCreds(); + if (!creds.getClient().equals(client)) { + throw new KrbException("S4U2self request not honored by KDC"); + } + return creds; + } + + /** + * Used by a middle server to acquire a service ticket to a backend + * server using the S4U2proxy extension. + * @param backend the name of the backend service + * @param second the client's service ticket to the middle server + * @param ccreds the TGT of the middle server + * @return the creds (cname=client, sname=backend) + */ + public static Credentials acquireS4U2proxyCreds( + String backend, Ticket second, + PrincipalName client, Credentials ccreds) + throws KrbException, IOException { + KrbTgsReq req = new KrbTgsReq( + ccreds, + second, + new PrincipalName(backend)); + Credentials creds = req.sendAndGetCreds(); + if (!creds.getClient().equals(client)) { + throw new KrbException("S4U2proxy request not honored by KDC"); + } + return creds; + } + + /** + * Acquires credentials for a specified service using initial + * credential. When the service has a different realm from the initial + * credential, we do cross-realm authentication - first, we use the + * current credential to get a cross-realm credential from the local KDC, + * then use that cross-realm credential to request service credential + * from the foreign KDC. + * + * @param service the name of service principal + * @param ccreds client's initial credential + */ + public static Credentials acquireServiceCreds( + String service, Credentials ccreds) + throws KrbException, IOException { + PrincipalName sname = new PrincipalName(service); + String serviceRealm = sname.getRealmString(); + String localRealm = ccreds.getClient().getRealmString(); + + if (localRealm.equals(serviceRealm)) { + if (DEBUG) { + System.out.println( + ">>> Credentials acquireServiceCreds: same realm"); + } + return serviceCreds(sname, ccreds); + } + Credentials theCreds = null; + + boolean[] okAsDelegate = new boolean[1]; + Credentials theTgt = getTGTforRealm(localRealm, serviceRealm, + ccreds, okAsDelegate); + if (theTgt != null) { + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "got right tgt"); + System.out.println(">>> Credentials acquireServiceCreds: " + + "obtaining service creds for " + sname); + } + + try { + theCreds = serviceCreds(sname, theTgt); + } catch (Exception exc) { + if (DEBUG) { + System.out.println(exc); + } + theCreds = null; + } + } + + if (theCreds != null) { + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "returning creds:"); + Credentials.printDebug(theCreds); + } + if (!okAsDelegate[0]) { + theCreds.resetDelegate(); + } + return theCreds; + } + throw new KrbApErrException(Krb5.KRB_AP_ERR_GEN_CRED, + "No service creds"); + } + + /** + * Gets a TGT to another realm + * @param localRealm this realm + * @param serviceRealm the other realm, cannot equals to localRealm + * @param ccreds TGT in this realm + * @param okAsDelegate an [out] argument to receive the okAsDelegate + * property. True only if all realms allow delegation. + * @return the TGT for the other realm, null if cannot find a path + * @throws KrbException if something goes wrong + */ + private static Credentials getTGTforRealm(String localRealm, + String serviceRealm, Credentials ccreds, boolean[] okAsDelegate) + throws KrbException { + + // Get a list of realms to traverse + String[] realms = Realm.getRealmsList(localRealm, serviceRealm); + + int i = 0, k = 0; + Credentials cTgt = null, newTgt = null, theTgt = null; + PrincipalName tempService = null; + String newTgtRealm = null; + + okAsDelegate[0] = true; + for (cTgt = ccreds, i = 0; i < realms.length;) { + tempService = PrincipalName.tgsService(serviceRealm, realms[i]); + + if (DEBUG) { + System.out.println( + ">>> Credentials acquireServiceCreds: main loop: [" + + i +"] tempService=" + tempService); + } + + try { + newTgt = serviceCreds(tempService, cTgt); + } catch (Exception exc) { + newTgt = null; + } + + if (newTgt == null) { + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "no tgt; searching thru capath"); + } + + /* + * No tgt found. Let's go thru the realms list one by one. + */ + for (newTgt = null, k = i+1; + newTgt == null && k < realms.length; k++) { + tempService = PrincipalName.tgsService(realms[k], realms[i]); + if (DEBUG) { + System.out.println( + ">>> Credentials acquireServiceCreds: " + + "inner loop: [" + k + + "] tempService=" + tempService); + } + try { + newTgt = serviceCreds(tempService, cTgt); + } catch (Exception exc) { + newTgt = null; + } + } + } // Ends 'if (newTgt == null)' + + if (newTgt == null) { + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "no tgt; cannot get creds"); + } + break; + } + + /* + * We have a tgt. It may or may not be for the target. + * If it's for the target realm, we're done looking for a tgt. + */ + newTgtRealm = newTgt.getServer().getInstanceComponent(); + if (okAsDelegate[0] && !newTgt.checkDelegate()) { + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "global OK-AS-DELEGATE turned off at " + + newTgt.getServer()); + } + okAsDelegate[0] = false; + } + + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "got tgt"); + } + + if (newTgtRealm.equals(serviceRealm)) { + /* We got the right tgt */ + theTgt = newTgt; + break; + } + + /* + * The new tgt is not for the target realm. + * See if the realm of the new tgt is in the list of realms + * and continue looking from there. + */ + for (k = i+1; k < realms.length; k++) { + if (newTgtRealm.equals(realms[k])) { + break; + } + } + + if (k < realms.length) { + /* + * (re)set the counter so we start looking + * from the realm we just obtained a tgt for. + */ + i = k; + cTgt = newTgt; + + if (DEBUG) { + System.out.println(">>> Credentials acquireServiceCreds: " + + "continuing with main loop counter reset to " + i); + } + continue; + } + else { + /* + * The new tgt's realm is not in the hierarchy of realms. + * It's probably not safe to get a tgt from + * a tgs that is outside the known list of realms. + * Give up now. + */ + break; + } + } // Ends outermost/main 'for' loop + + return theTgt; + } + + /* + * This method does the real job to request the service credential. + */ + private static Credentials serviceCreds( + PrincipalName service, Credentials ccreds) + throws KrbException, IOException { + return new KrbTgsReq(ccreds, service).sendAndGetCreds(); + } +} diff --git a/src/sun/security/krb5/internal/ETypeInfo.java b/src/sun/security/krb5/internal/ETypeInfo.java new file mode 100644 index 00000000..e53ba424 --- /dev/null +++ b/src/sun/security/krb5/internal/ETypeInfo.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import sun.security.krb5.internal.util.KerberosString; + +/** + * Implements the ASN.1 ETYPE-INFO-ENTRY type. + * + * ETYPE-INFO-ENTRY ::= SEQUENCE { + * etype [0] Int32, + * salt [1] OCTET STRING OPTIONAL + * } + * + * @author Seema Malkani + */ + +public class ETypeInfo { + + private int etype; + private String salt = null; + + private static final byte TAG_TYPE = 0; + private static final byte TAG_VALUE = 1; + + private ETypeInfo() { + } + + public ETypeInfo(int etype, String salt) { + this.etype = etype; + this.salt = salt; + } + + public Object clone() { + return new ETypeInfo(etype, salt); + } + + /** + * Constructs a ETypeInfo object. + * @param encoding a DER-encoded data. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public ETypeInfo(DerValue encoding) throws Asn1Exception, IOException { + DerValue der = null; + + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + // etype + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + this.etype = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + + // salt + if (encoding.getData().available() > 0) { + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x01) { + byte[] saltBytes = der.getData().getOctetString(); + + // Although salt is defined as an OCTET STRING, it's the + // encoding from of a string. As RFC 4120 says: + // + // "The salt, ..., is also completely unspecified with respect + // to character set and is probably locale-specific". + // + // It's known that this field is using the same encoding as + // KerberosString in most implementations. + + if (KerberosString.MSNAME) { + this.salt = new String(saltBytes, "UTF8"); + } else { + this.salt = new String(saltBytes); + } + } + } + + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes this object to an OutputStream. + * + * @return byte array of the encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception Asn1Exception on encoding errors. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + + temp.putInteger(etype); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_TYPE), temp); + + if (salt != null) { + temp = new DerOutputStream(); + if (KerberosString.MSNAME) { + temp.putOctetString(salt.getBytes("UTF8")); + } else { + temp.putOctetString(salt.getBytes()); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_VALUE), temp); + } + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + // accessor methods + public int getEType() { + return etype; + } + + public String getSalt() { + return salt; + } + +} diff --git a/src/sun/security/krb5/internal/ETypeInfo2.java b/src/sun/security/krb5/internal/ETypeInfo2.java new file mode 100644 index 00000000..c1abbaa3 --- /dev/null +++ b/src/sun/security/krb5/internal/ETypeInfo2.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import sun.security.krb5.internal.util.KerberosString; + +/** + * Implements the ASN.1 ETYPE-INFO-ENTRY type. + * + * ETYPE-INFO2-ENTRY ::= SEQUENCE { + * etype [0] Int32, + * salt [1] KerberosString OPTIONAL, + * s2kparams [2] OCTET STRING OPTIONAL + * } + * + * @author Seema Malkani + */ + +public class ETypeInfo2 { + + private int etype; + private String saltStr = null; + private byte[] s2kparams = null; + + private static final byte TAG_TYPE = 0; + private static final byte TAG_VALUE1 = 1; + private static final byte TAG_VALUE2 = 2; + + private ETypeInfo2() { + } + + public ETypeInfo2(int etype, String salt, byte[] s2kparams) { + this.etype = etype; + this.saltStr = salt; + if (s2kparams != null) { + this.s2kparams = s2kparams.clone(); + } + } + + public Object clone() { + ETypeInfo2 etypeInfo2 = new ETypeInfo2(); + etypeInfo2.etype = etype; + etypeInfo2.saltStr = saltStr; + if (s2kparams != null) { + etypeInfo2.s2kparams = new byte[s2kparams.length]; + System.arraycopy(s2kparams, 0, etypeInfo2.s2kparams, + 0, s2kparams.length); + } + return etypeInfo2; + } + + /** + * Constructs a ETypeInfo2 object. + * @param encoding a DER-encoded data. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public ETypeInfo2(DerValue encoding) throws Asn1Exception, IOException { + DerValue der = null; + + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + // etype + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + this.etype = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + + // salt + if (encoding.getData().available() > 0) { + if ((encoding.getData().peekByte() & 0x1F) == 0x01) { + der = encoding.getData().getDerValue(); + this.saltStr = new KerberosString( + der.getData().getDerValue()).toString(); + } + } + + // s2kparams + if (encoding.getData().available() > 0) { + if ((encoding.getData().peekByte() & 0x1F) == 0x02) { + der = encoding.getData().getDerValue(); + this.s2kparams = der.getData().getOctetString(); + } + } + + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes this object to an OutputStream. + * + * @return byte array of the encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception Asn1Exception on encoding errors. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + + temp.putInteger(etype); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_TYPE), temp); + + if (saltStr != null) { + temp = new DerOutputStream(); + temp.putDerValue(new KerberosString(saltStr).toDerValue()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_VALUE1), temp); + } + if (s2kparams != null) { + temp = new DerOutputStream(); + temp.putOctetString(s2kparams); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_VALUE2), temp); + } + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + // accessor methods + public int getEType() { + return etype; + } + + public String getSalt() { + return saltStr; + } + + public byte[] getParams() { + return ((s2kparams == null) ? null : s2kparams.clone()); + } + +} diff --git a/src/sun/security/krb5/internal/EncAPRepPart.java b/src/sun/security/krb5/internal/EncAPRepPart.java new file mode 100644 index 00000000..fdebf1de --- /dev/null +++ b/src/sun/security/krb5/internal/EncAPRepPart.java @@ -0,0 +1,171 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.util.Vector; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 EncAPRepPart type. + * + *

    + * EncAPRepPart ::= [APPLICATION 27] SEQUENCE { + * ctime [0] KerberosTime, + * cusec [1] Microseconds, + * subkey [2] EncryptionKey OPTIONAL, + * seq-number [3] UInt32 OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class EncAPRepPart { + + public KerberosTime ctime; + public int cusec; + EncryptionKey subKey; //optional + Integer seqNumber; //optional + + public EncAPRepPart( + KerberosTime new_ctime, + int new_cusec, + EncryptionKey new_subKey, + Integer new_seqNumber) { + ctime = new_ctime; + cusec = new_cusec; + subKey = new_subKey; + seqNumber = new_seqNumber; + } + + public EncAPRepPart(byte[] data) + throws Asn1Exception, IOException { + init(new DerValue(data)); + } + + public EncAPRepPart(DerValue encoding) + throws Asn1Exception, IOException { + init(encoding); + } + + /** + * Initializes an EncaPRepPart object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + private void init(DerValue encoding) throws Asn1Exception, IOException { + DerValue der, subDer; + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x1B) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + ctime = KerberosTime.parse(der.getData(), (byte) 0x00, true); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x01) { + cusec = subDer.getData().getBigInteger().intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (der.getData().available() > 0) { + subKey = EncryptionKey.parse(der.getData(), (byte) 0x02, true); + } else { + subKey = null; + seqNumber = null; + } + if (der.getData().available() > 0) { + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) != 0x03) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + seqNumber = new Integer(subDer.getData().getBigInteger().intValue()); + } else { + seqNumber = null; + } + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an EncAPRepPart object. + * @return byte array of encoded EncAPRepPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + Vector v = new Vector<>(); + DerOutputStream temp = new DerOutputStream(); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), ctime.asn1Encode())); + temp.putInteger(BigInteger.valueOf(cusec)); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), temp.toByteArray())); + if (subKey != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), subKey.asn1Encode())); + } + if (seqNumber != null) { + temp = new DerOutputStream(); + // encode as an unsigned integer (UInt32) + temp.putInteger(BigInteger.valueOf(seqNumber.longValue())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), temp.toByteArray())); + } + DerValue der[] = new DerValue[v.size()]; + v.copyInto(der); + temp = new DerOutputStream(); + temp.putSequence(der); + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) 0x1B), temp); + return out.toByteArray(); + } + + public final EncryptionKey getSubKey() { + return subKey; + } + + public final Integer getSeqNumber() { + return seqNumber; + } +} diff --git a/src/sun/security/krb5/internal/EncASRepPart.java b/src/sun/security/krb5/internal/EncASRepPart.java new file mode 100644 index 00000000..7e5d037d --- /dev/null +++ b/src/sun/security/krb5/internal/EncASRepPart.java @@ -0,0 +1,88 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.io.IOException; + +public class EncASRepPart extends EncKDCRepPart { + + public EncASRepPart( + EncryptionKey new_key, + LastReq new_lastReq, + int new_nonce, + KerberosTime new_keyExpiration, + TicketFlags new_flags, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + PrincipalName new_sname, + HostAddresses new_caddr) { + super( + new_key, + new_lastReq, + new_nonce, + new_keyExpiration, + new_flags, + new_authtime, + new_starttime, + new_endtime, + new_renewTill, + new_sname, + new_caddr, + Krb5.KRB_ENC_AS_REP_PART + ); + //may need to use Krb5.KRB_ENC_TGS_REP_PART to mimic + //behavior of other implementaions, instead of above + } + + public EncASRepPart(byte[] data) throws Asn1Exception, + IOException, KrbException { + init(new DerValue(data)); + } + + public EncASRepPart(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding, Krb5.KRB_ENC_AS_REP_PART); + } + + public byte[] asn1Encode() throws Asn1Exception, + IOException { + return asn1Encode(Krb5.KRB_ENC_AS_REP_PART); + } +} diff --git a/src/sun/security/krb5/internal/EncKDCRepPart.java b/src/sun/security/krb5/internal/EncKDCRepPart.java new file mode 100644 index 00000000..8791a57b --- /dev/null +++ b/src/sun/security/krb5/internal/EncKDCRepPart.java @@ -0,0 +1,224 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.krb5.EncryptionKey; +import sun.security.util.*; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 EncKDCRepPart type. + * + *

    + * EncKDCRepPart ::= SEQUENCE { + * key [0] EncryptionKey, + * last-req [1] LastReq, + * nonce [2] UInt32, + * key-expiration [3] KerberosTime OPTIONAL, + * flags [4] TicketFlags, + * authtime [5] KerberosTime, + * starttime [6] KerberosTime OPTIONAL, + * endtime [7] KerberosTime, + * renew-till [8] KerberosTime OPTIONAL, + * srealm [9] Realm, + * sname [10] PrincipalName, + * caddr [11] HostAddresses OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class EncKDCRepPart { + + public EncryptionKey key; + public LastReq lastReq; + public int nonce; + public KerberosTime keyExpiration; //optional + public TicketFlags flags; + public KerberosTime authtime; + public KerberosTime starttime; //optional + public KerberosTime endtime; + public KerberosTime renewTill; //optional + public PrincipalName sname; + public HostAddresses caddr; //optional + public int msgType; //not included in sequence + + public EncKDCRepPart( + EncryptionKey new_key, + LastReq new_lastReq, + int new_nonce, + KerberosTime new_keyExpiration, + TicketFlags new_flags, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + PrincipalName new_sname, + HostAddresses new_caddr, + int new_msgType) { + key = new_key; + lastReq = new_lastReq; + nonce = new_nonce; + keyExpiration = new_keyExpiration; + flags = new_flags; + authtime = new_authtime; + starttime = new_starttime; + endtime = new_endtime; + renewTill = new_renewTill; + sname = new_sname; + caddr = new_caddr; + msgType = new_msgType; + } + + public EncKDCRepPart() { + } + + public EncKDCRepPart(byte[] data, int rep_type) + throws Asn1Exception, IOException, RealmException { + init(new DerValue(data), rep_type); + } + + public EncKDCRepPart(DerValue encoding, int rep_type) + throws Asn1Exception, IOException, RealmException { + init(encoding, rep_type); + } + + /** + * Initializes an EncKDCRepPart object. + * + * @param encoding a single DER-encoded value. + * @param rep_type type of the encrypted reply message. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while decoding an Realm object. + */ + protected void init(DerValue encoding, int rep_type) + throws Asn1Exception, IOException, RealmException { + DerValue der, subDer; + //implementations return the incorrect tag value, so + //we don't use the above line; instead we use the following + msgType = (encoding.getTag() & (byte) 0x1F); + if (msgType != Krb5.KRB_ENC_AS_REP_PART && + msgType != Krb5.KRB_ENC_TGS_REP_PART) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + key = EncryptionKey.parse(der.getData(), (byte) 0x00, false); + lastReq = LastReq.parse(der.getData(), (byte) 0x01, false); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x02) { + nonce = subDer.getData().getBigInteger().intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + keyExpiration = KerberosTime.parse(der.getData(), (byte) 0x03, true); + flags = TicketFlags.parse(der.getData(), (byte) 0x04, false); + authtime = KerberosTime.parse(der.getData(), (byte) 0x05, false); + starttime = KerberosTime.parse(der.getData(), (byte) 0x06, true); + endtime = KerberosTime.parse(der.getData(), (byte) 0x07, false); + renewTill = KerberosTime.parse(der.getData(), (byte) 0x08, true); + Realm srealm = Realm.parse(der.getData(), (byte) 0x09, false); + sname = PrincipalName.parse(der.getData(), (byte) 0x0A, false, srealm); + if (der.getData().available() > 0) { + caddr = HostAddresses.parse(der.getData(), (byte) 0x0B, true); + } + // We observe extra data from MSAD + /*if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + }*/ + } + + /** + * Encodes an EncKDCRepPart object. + * @param rep_type type of encrypted reply message. + * @return byte array of encoded EncKDCRepPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode(int rep_type) throws Asn1Exception, + IOException { + DerOutputStream temp = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), key.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), lastReq.asn1Encode()); + temp.putInteger(BigInteger.valueOf(nonce)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), temp); + + if (keyExpiration != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), keyExpiration.asn1Encode()); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x04), flags.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x05), authtime.asn1Encode()); + if (starttime != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x06), starttime.asn1Encode()); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x07), endtime.asn1Encode()); + if (renewTill != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x08), renewTill.asn1Encode()); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x09), sname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x0A), sname.asn1Encode()); + if (caddr != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x0B), caddr.asn1Encode()); + } + //should use the rep_type to build the encoding + //but other implementations do not; it is ignored and + //the cached msgType is used instead + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) msgType), temp); + return bytes.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/EncKrbCredPart.java b/src/sun/security/krb5/internal/EncKrbCredPart.java new file mode 100644 index 00000000..c1cfc3b4 --- /dev/null +++ b/src/sun/security/krb5/internal/EncKrbCredPart.java @@ -0,0 +1,216 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import java.io.IOException; +import java.math.BigInteger; + +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.RealmException; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * Implements the ASN.1 EncKrbCredPart type. + * + *

    + * EncKrbCredPart ::= [APPLICATION 29] SEQUENCE { + * ticket-info [0] SEQUENCE OF KrbCredInfo, + * nonce [1] UInt32 OPTIONAL, + * timestamp [2] KerberosTime OPTIONAL, + * usec [3] Microseconds OPTIONAL, + * s-address [4] HostAddress OPTIONAL, + * r-address [5] HostAddress OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class EncKrbCredPart { + + public KrbCredInfo[] ticketInfo = null; + public KerberosTime timeStamp; //optional + private Integer nonce; //optional + private Integer usec; //optional + private HostAddress sAddress; //optional + private HostAddresses rAddress; //optional + + public EncKrbCredPart( + KrbCredInfo[] new_ticketInfo, + KerberosTime new_timeStamp, + Integer new_usec, + Integer new_nonce, + HostAddress new_sAddress, + HostAddresses new_rAddress) throws IOException { + if (new_ticketInfo != null) { + ticketInfo = new KrbCredInfo[new_ticketInfo.length]; + for (int i = 0; i < new_ticketInfo.length; i++) { + if (new_ticketInfo[i] == null) { + throw new IOException("Cannot create a EncKrbCredPart"); + } else { + ticketInfo[i] = (KrbCredInfo) new_ticketInfo[i].clone(); + } + } + } + timeStamp = new_timeStamp; + usec = new_usec; + nonce = new_nonce; + sAddress = new_sAddress; + rAddress = new_rAddress; + } + + public EncKrbCredPart(byte[] data) throws Asn1Exception, + IOException, RealmException { + init(new DerValue(data)); + } + + public EncKrbCredPart(DerValue encoding) throws Asn1Exception, + IOException, RealmException { + init(encoding); + } + + /** + * Initializes an EncKrbCredPart object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private void init(DerValue encoding) throws Asn1Exception, + IOException, RealmException { + DerValue der, subDer; + //may not be the correct error code for a tag + //mismatch on an encrypted structure + nonce = null; + timeStamp = null; + usec = null; + sAddress = null; + rAddress = null; + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x1D) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x00) { + DerValue derValues[] = subDer.getData().getSequence(1); + ticketInfo = new KrbCredInfo[derValues.length]; + for (int i = 0; i < derValues.length; i++) { + ticketInfo[i] = new KrbCredInfo(derValues[i]); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (der.getData().available() > 0) { + if (((byte) (der.getData().peekByte()) & (byte) 0x1F) == (byte) 0x01) { + subDer = der.getData().getDerValue(); + nonce = new Integer(subDer.getData().getBigInteger().intValue()); + } + } + if (der.getData().available() > 0) { + timeStamp = KerberosTime.parse(der.getData(), (byte) 0x02, true); + } + if (der.getData().available() > 0) { + if (((byte) (der.getData().peekByte()) & (byte) 0x1F) == (byte) 0x03) { + subDer = der.getData().getDerValue(); + usec = new Integer(subDer.getData().getBigInteger().intValue()); + } + } + if (der.getData().available() > 0) { + sAddress = HostAddress.parse(der.getData(), (byte) 0x04, true); + } + if (der.getData().available() > 0) { + rAddress = HostAddresses.parse(der.getData(), (byte) 0x05, true); + } + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an EncKrbCredPart object. + * @return byte array of encoded EncKrbCredPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + DerValue[] tickets = new DerValue[ticketInfo.length]; + for (int i = 0; i < ticketInfo.length; i++) { + tickets[i] = new DerValue(ticketInfo[i].asn1Encode()); + } + temp.putSequence(tickets); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), temp); + + if (nonce != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(nonce.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), temp); + } + if (timeStamp != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), timeStamp.asn1Encode()); + } + if (usec != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(usec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), temp); + } + if (sAddress != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x04), sAddress.asn1Encode()); + } + if (rAddress != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x05), rAddress.asn1Encode()); + } + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) 0x1D), temp); + return bytes.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/EncKrbPrivPart.java b/src/sun/security/krb5/internal/EncKrbPrivPart.java new file mode 100644 index 00000000..cc30408e --- /dev/null +++ b/src/sun/security/krb5/internal/EncKrbPrivPart.java @@ -0,0 +1,173 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 EncKrbPrivPart type. + * + *

    + * EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE { + * user-data [0] OCTET STRING, + * timestamp [1] KerberosTime OPTIONAL, + * usec [2] Microseconds OPTIONAL, + * seq-number [3] UInt32 OPTIONAL, + * s-address [4] HostAddress -- sender's addr --, + * r-address [5] HostAddress OPTIONAL -- recip's addr + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class EncKrbPrivPart { + + public byte[] userData = null; + public KerberosTime timestamp; //optional + public Integer usec; //optional + public Integer seqNumber; //optional + public HostAddress sAddress; //optional + public HostAddress rAddress; //optional + + public EncKrbPrivPart( + byte[] new_userData, + KerberosTime new_timestamp, + Integer new_usec, + Integer new_seqNumber, + HostAddress new_sAddress, + HostAddress new_rAddress) { + if (new_userData != null) { + userData = new_userData.clone(); + } + timestamp = new_timestamp; + usec = new_usec; + seqNumber = new_seqNumber; + sAddress = new_sAddress; + rAddress = new_rAddress; + } + + public EncKrbPrivPart(byte[] data) throws Asn1Exception, IOException { + init(new DerValue(data)); + } + + public EncKrbPrivPart(DerValue encoding) throws Asn1Exception, IOException { + init(encoding); + } + + /** + * Initializes an EncKrbPrivPart object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + private void init(DerValue encoding) throws Asn1Exception, IOException { + DerValue der, subDer; + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x1C) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte) 0x1F) == (byte) 0x00) { + userData = subDer.getData().getOctetString(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + timestamp = KerberosTime.parse(der.getData(), (byte) 0x01, true); + if ((der.getData().peekByte() & 0x1F) == 0x02) { + subDer = der.getData().getDerValue(); + usec = new Integer(subDer.getData().getBigInteger().intValue()); + } else { + usec = null; + } + if ((der.getData().peekByte() & 0x1F) == 0x03) { + subDer = der.getData().getDerValue(); + seqNumber = new Integer(subDer.getData().getBigInteger().intValue()); + } else { + seqNumber = null; + } + sAddress = HostAddress.parse(der.getData(), (byte) 0x04, false); + if (der.getData().available() > 0) { + rAddress = HostAddress.parse(der.getData(), (byte) 0x05, true); + } + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an EncKrbPrivPart object. + * @return byte array of encoded EncKrbPrivPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + + temp.putOctetString(userData); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x00), temp); + if (timestamp != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x01), timestamp.asn1Encode()); + } + if (usec != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(usec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x02), temp); + } + if (seqNumber != null) { + temp = new DerOutputStream(); + // encode as an unsigned integer (UInt32) + temp.putInteger(BigInteger.valueOf(seqNumber.longValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x03), temp); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x04), sAddress.asn1Encode()); + if (rAddress != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0x05), rAddress.asn1Encode()); + } + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte) 0x1C), temp); + return bytes.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/EncTGSRepPart.java b/src/sun/security/krb5/internal/EncTGSRepPart.java new file mode 100644 index 00000000..cdca881e --- /dev/null +++ b/src/sun/security/krb5/internal/EncTGSRepPart.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.io.IOException; + +public class EncTGSRepPart extends EncKDCRepPart { + + public EncTGSRepPart( + EncryptionKey new_key, + LastReq new_lastReq, + int new_nonce, + KerberosTime new_keyExpiration, + TicketFlags new_flags, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + PrincipalName new_sname, + HostAddresses new_caddr) { + super( + new_key, + new_lastReq, + new_nonce, + new_keyExpiration, + new_flags, + new_authtime, + new_starttime, + new_endtime, + new_renewTill, + new_sname, + new_caddr, + Krb5.KRB_ENC_TGS_REP_PART); + } + + public EncTGSRepPart(byte[] data) throws Asn1Exception, + IOException, KrbException { + init(new DerValue(data)); + } + + public EncTGSRepPart(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding, Krb5.KRB_ENC_TGS_REP_PART); + } + + public byte[] asn1Encode() throws Asn1Exception, + IOException { + return asn1Encode(Krb5.KRB_ENC_TGS_REP_PART); + } +} diff --git a/src/sun/security/krb5/internal/EncTicketPart.java b/src/sun/security/krb5/internal/EncTicketPart.java new file mode 100644 index 00000000..9f1550a6 --- /dev/null +++ b/src/sun/security/krb5/internal/EncTicketPart.java @@ -0,0 +1,219 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; + +import java.io.IOException; + +/** + * Implements the ASN.1 EncTicketPart type. + * + *

    + * EncTicketPart ::= [APPLICATION 3] SEQUENCE { + * flags [0] TicketFlags, + * key [1] EncryptionKey, + * crealm [2] Realm, + * cname [3] PrincipalName, + * transited [4] TransitedEncoding, + * authtime [5] KerberosTime, + * starttime [6] KerberosTime OPTIONAL, + * endtime [7] KerberosTime, + * renew-till [8] KerberosTime OPTIONAL, + * caddr [9] HostAddresses OPTIONAL, + * authorization-data [10] AuthorizationData OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class EncTicketPart { + + public TicketFlags flags; + public EncryptionKey key; + public PrincipalName cname; + public TransitedEncoding transited; + public KerberosTime authtime; + public KerberosTime starttime; //optional + public KerberosTime endtime; + public KerberosTime renewTill; //optional + public HostAddresses caddr; //optional + public AuthorizationData authorizationData; //optional + + public EncTicketPart( + TicketFlags new_flags, + EncryptionKey new_key, + PrincipalName new_cname, + TransitedEncoding new_transited, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + HostAddresses new_caddr, + AuthorizationData new_authorizationData) { + flags = new_flags; + key = new_key; + cname = new_cname; + transited = new_transited; + authtime = new_authtime; + starttime = new_starttime; + endtime = new_endtime; + renewTill = new_renewTill; + caddr = new_caddr; + authorizationData = new_authorizationData; + } + + public EncTicketPart(byte[] data) + throws Asn1Exception, KrbException, IOException { + init(new DerValue(data)); + } + + public EncTicketPart(DerValue encoding) + throws Asn1Exception, KrbException, IOException { + init(encoding); + } + + /** + * Initializes an EncTicketPart object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private static String getHexBytes(byte[] bytes, int len) + throws IOException { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < len; i++) { + + int b1 = (bytes[i] >> 4) & 0x0f; + int b2 = bytes[i] & 0x0f; + + sb.append(Integer.toHexString(b1)); + sb.append(Integer.toHexString(b2)); + sb.append(' '); + } + return sb.toString(); + } + + private void init(DerValue encoding) + throws Asn1Exception, IOException, RealmException { + DerValue der, subDer; + + renewTill = null; + caddr = null; + authorizationData = null; + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x03) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + flags = TicketFlags.parse(der.getData(), (byte) 0x00, false); + key = EncryptionKey.parse(der.getData(), (byte) 0x01, false); + Realm crealm = Realm.parse(der.getData(), (byte) 0x02, false); + cname = PrincipalName.parse(der.getData(), (byte) 0x03, false, crealm); + transited = TransitedEncoding.parse(der.getData(), (byte) 0x04, false); + authtime = KerberosTime.parse(der.getData(), (byte) 0x05, false); + starttime = KerberosTime.parse(der.getData(), (byte) 0x06, true); + endtime = KerberosTime.parse(der.getData(), (byte) 0x07, false); + if (der.getData().available() > 0) { + renewTill = KerberosTime.parse(der.getData(), (byte) 0x08, true); + } + if (der.getData().available() > 0) { + caddr = HostAddresses.parse(der.getData(), (byte) 0x09, true); + } + if (der.getData().available() > 0) { + authorizationData = AuthorizationData.parse(der.getData(), (byte) 0x0A, true); + } + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + } + + /** + * Encodes an EncTicketPart object. + * @return byte array of encoded EncTicketPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), flags.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), key.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), cname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), cname.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x04), transited.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x05), authtime.asn1Encode()); + if (starttime != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x06), starttime.asn1Encode()); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x07), endtime.asn1Encode()); + + if (renewTill != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x08), renewTill.asn1Encode()); + } + + if (caddr != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x09), caddr.asn1Encode()); + } + + if (authorizationData != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x0A), authorizationData.asn1Encode()); + } + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) 0x03), temp); + return bytes.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/HostAddress.java b/src/sun/security/krb5/internal/HostAddress.java new file mode 100644 index 00000000..062ce8e6 --- /dev/null +++ b/src/sun/security/krb5/internal/HostAddress.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Asn1Exception; +import sun.security.util.*; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.io.IOException; + +/** + * Implements the ASN.1 HostAddress type. + * + *

    + * HostAddress ::= SEQUENCE { + * addr-type [0] Int32, + * address [1] OCTET STRING + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class HostAddress implements Cloneable { + int addrType; + byte[] address = null; + + private static InetAddress localInetAddress; //caches local inet address + private static final boolean DEBUG = Krb5.DEBUG; + private volatile int hashCode = 0; + + private HostAddress(int dummy) {} + + public Object clone() { + HostAddress new_hostAddress = new HostAddress(0); + new_hostAddress.addrType = addrType; + if (address != null) { + new_hostAddress.address = address.clone(); + } + return new_hostAddress; + } + + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + addrType; + if (address != null) { + for (int i=0; i < address.length; i++) { + result = 37*result + address[i]; + } + } + hashCode = result; + } + return hashCode; + + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof HostAddress)) { + return false; + } + + HostAddress h = (HostAddress)obj; + if (addrType != h.addrType || + (address != null && h.address == null) || + (address == null && h.address != null)) + return false; + if (address != null && h.address != null) { + if (address.length != h.address.length) + return false; + for (int i = 0; i < address.length; i++) + if (address[i] != h.address[i]) + return false; + } + return true; + } + + private static synchronized InetAddress getLocalInetAddress() + throws UnknownHostException { + + if (localInetAddress == null) { + localInetAddress = InetAddress.getLocalHost(); + } + if (localInetAddress == null) { + throw new UnknownHostException(); + } + return (localInetAddress); + } + + /** + * Gets the InetAddress of this HostAddress. + * @return the IP address for this specified host. + * @exception if no IP address for the host could be found. + * + */ + public InetAddress getInetAddress() throws UnknownHostException { + // the type of internet addresses is 2. + if (addrType == Krb5.ADDRTYPE_INET || + addrType == Krb5.ADDRTYPE_INET6) { + return (InetAddress.getByAddress(address)); + } else { + // if it is other type (ISO address, XNS address, etc) + return null; + } + } + + private int getAddrType(InetAddress inetAddress) { + int addressType = 0; + if (inetAddress instanceof Inet4Address) + addressType = Krb5.ADDRTYPE_INET; + else if (inetAddress instanceof Inet6Address) + addressType = Krb5.ADDRTYPE_INET6; + return (addressType); + } + + // implicit default not in Config.java + public HostAddress() throws UnknownHostException { + InetAddress inetAddress = getLocalInetAddress(); + addrType = getAddrType(inetAddress); + address = inetAddress.getAddress(); + } + + /** + * Creates a HostAddress from the specified address and address type. + * + * @param new_addrType the value of the address type which matches the defined + * address family constants in the Berkeley Standard + * Distributions of Unix. + * @param new_address network address. + * @exception KrbApErrException if address type and address length do not match defined value. + * + */ + public HostAddress(int new_addrType, byte[] new_address) + throws KrbApErrException, UnknownHostException { + switch(new_addrType) { + case Krb5.ADDRTYPE_INET: //Internet address + if (new_address.length != 4) + throw new KrbApErrException(0, "Invalid Internet address"); + break; + case Krb5.ADDRTYPE_CHAOS: + if (new_address.length != 2) //CHAOSnet address + throw new KrbApErrException(0, "Invalid CHAOSnet address"); + break; + case Krb5.ADDRTYPE_ISO: // ISO address + break; + case Krb5.ADDRTYPE_IPX: // XNS address + if (new_address.length != 6) + throw new KrbApErrException(0, "Invalid XNS address"); + break; + case Krb5.ADDRTYPE_APPLETALK: //AppleTalk DDP address + if (new_address.length != 3) + throw new KrbApErrException(0, "Invalid DDP address"); + break; + case Krb5.ADDRTYPE_DECNET: //DECnet Phase IV address + if (new_address.length != 2) + throw new KrbApErrException(0, "Invalid DECnet Phase IV address"); + break; + case Krb5.ADDRTYPE_INET6: //Internet IPv6 address + if (new_address.length != 16) + throw new KrbApErrException(0, "Invalid Internet IPv6 address"); + break; + } + + addrType = new_addrType; + if (new_address != null) { + address = new_address.clone(); + } + if (DEBUG) { + if (addrType == Krb5.ADDRTYPE_INET || + addrType == Krb5.ADDRTYPE_INET6) { + System.out.println("Host address is " + + InetAddress.getByAddress(address)); + } + } + } + + public HostAddress(InetAddress inetAddress) { + addrType = getAddrType(inetAddress); + address = inetAddress.getAddress(); + } + + /** + * Constructs a host address from a single DER-encoded value. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public HostAddress(DerValue encoding) throws Asn1Exception, IOException { + DerValue der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x00) { + addrType = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x01) { + address = der.getData().getOctetString(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes a HostAddress object. + * @return a byte array of encoded HostAddress object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(this.addrType); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putOctetString(address); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Parses (unmarshal) a host address from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @exception IOException if an I/O error occurs while reading encoded data. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of HostAddress. + * + */ + public static HostAddress parse(DerInputStream data, byte explicitTag, + boolean optional) + throws Asn1Exception, IOException{ + if ((optional) && + (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) { + return null; + } + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + return new HostAddress(subDer); + } + } + +} diff --git a/src/sun/security/krb5/internal/HostAddresses.java b/src/sun/security/krb5/internal/HostAddresses.java new file mode 100644 index 00000000..3c80d687 --- /dev/null +++ b/src/sun/security/krb5/internal/HostAddresses.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.PrincipalName; +import sun.security.krb5.KrbException; +import sun.security.krb5.Asn1Exception; +import sun.security.util.*; +import java.util.Vector; +import java.util.ArrayList; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.io.IOException; +import sun.security.krb5.internal.ccache.CCacheOutputStream; + +/** + * Implements the ASN.1 HostAddresses type. + * + *

    + * HostAddresses -- NOTE: subtly different from rfc1510, + * -- but has a value mapping and encodes the same + * ::= SEQUENCE OF HostAddress + * + * HostAddress ::= SEQUENCE { + * addr-type [0] Int32, + * address [1] OCTET STRING + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class HostAddresses implements Cloneable { + private static boolean DEBUG = Krb5.DEBUG; + private HostAddress[] addresses = null; + private volatile int hashCode = 0; + + public HostAddresses(HostAddress[] new_addresses) throws IOException { + if (new_addresses != null) { + addresses = new HostAddress[new_addresses.length]; + for (int i = 0; i < new_addresses.length; i++) { + if (new_addresses[i] == null) { + throw new IOException("Cannot create a HostAddress"); + } else { + addresses[i] = (HostAddress)new_addresses[i].clone(); + } + } + } + } + + public HostAddresses() throws UnknownHostException { + addresses = new HostAddress[1]; + addresses[0] = new HostAddress(); + } + + private HostAddresses(int dummy) {} + + public HostAddresses(PrincipalName serverPrincipal) + throws UnknownHostException, KrbException { + + String[] components = serverPrincipal.getNameStrings(); + + if (serverPrincipal.getNameType() != PrincipalName.KRB_NT_SRV_HST || + components.length < 2) + throw new KrbException(Krb5.KRB_ERR_GENERIC, "Bad name"); + + String host = components[1]; + InetAddress addr[] = InetAddress.getAllByName(host); + HostAddress hAddrs[] = new HostAddress[addr.length]; + + for (int i = 0; i < addr.length; i++) { + hAddrs[i] = new HostAddress(addr[i]); + } + + addresses = hAddrs; + } + + public Object clone() { + HostAddresses new_hostAddresses = new HostAddresses(0); + if (addresses != null) { + new_hostAddresses.addresses = new HostAddress[addresses.length]; + for (int i = 0; i < addresses.length; i++) { + new_hostAddresses.addresses[i] = + (HostAddress)addresses[i].clone(); + } + } + return new_hostAddresses; + } + + public boolean inList(HostAddress addr) { + if (addresses != null) { + for (int i = 0; i < addresses.length; i++) + if (addresses[i].equals(addr)) + return true; + } + return false; + } + + public int hashCode() { + if (hashCode == 0) { + int result = 17; + if (addresses != null) { + for (int i=0; i < addresses.length; i++) { + result = 37*result + addresses[i].hashCode(); + } + } + hashCode = result; + } + return hashCode; + + } + + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof HostAddresses)) { + return false; + } + + HostAddresses addrs = (HostAddresses)obj; + if ((addresses == null && addrs.addresses != null) || + (addresses != null && addrs.addresses == null)) + return false; + if (addresses != null && addrs.addresses != null) { + if (addresses.length != addrs.addresses.length) + return false; + for (int i = 0; i < addresses.length; i++) + if (!addresses[i].equals(addrs.addresses[i])) + return false; + } + return true; + } + + /** + * Constructs a new HostAddresses object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + */ + public HostAddresses(DerValue encoding) + throws Asn1Exception, IOException { + Vector tempAddresses = new Vector<>(); + DerValue der = null; + while (encoding.getData().available() > 0) { + der = encoding.getData().getDerValue(); + tempAddresses.addElement(new HostAddress(der)); + } + if (tempAddresses.size() > 0) { + addresses = new HostAddress[tempAddresses.size()]; + tempAddresses.copyInto(addresses); + } + } + + + /** + * Encodes a HostAddresses object. + * @return byte array of encoded HostAddresses object. + * @exception Asn1Exception if an error occurs while decoding an + * ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading + * encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + + if (addresses != null && addresses.length > 0) { + for (int i = 0; i < addresses.length; i++) + bytes.write(addresses[i].asn1Encode()); + } + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Parse (unmarshal) a HostAddresses from a DER input stream. + * This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception if an Asn1Exception occurs. + * @param data the Der input stream value, which contains one or more + * marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional. + * @return an instance of HostAddresses. + */ + public static HostAddresses parse(DerInputStream data, + byte explicitTag, boolean optional) + throws Asn1Exception, IOException { + if ((optional) && + (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new HostAddresses(subDer); + } + } + + /** + * Writes data field values in HostAddresses in FCC + * format to a CCacheOutputStream. + * + * @param cos a CCacheOutputStream to be written to. + * @exception IOException if an I/O exception occurs. + * @see CCacheOutputStream + */ + + public void writeAddrs(CCacheOutputStream cos) throws IOException { + cos.write32(addresses.length); + for (int i = 0; i < addresses.length; i++) { + cos.write16(addresses[i].addrType); + cos.write32(addresses[i].address.length); + cos.write(addresses[i].address, 0, + addresses[i].address.length); + } + } + + + public InetAddress[] getInetAddresses() { + + if (addresses == null || addresses.length == 0) + return null; + + ArrayList ipAddrs = new ArrayList<>(addresses.length); + + for (int i = 0; i < addresses.length; i++) { + try { + if ((addresses[i].addrType == Krb5.ADDRTYPE_INET) || + (addresses[i].addrType == Krb5.ADDRTYPE_INET6)) { + ipAddrs.add(addresses[i].getInetAddress()); + } + } catch (UnknownHostException e) { + // Should not happen since IP address given + return null; + } + } + + InetAddress[] retVal = new InetAddress[ipAddrs.size()]; + return ipAddrs.toArray(retVal); + + } + + /** + * Returns all the IP addresses of the local host. + */ + public static HostAddresses getLocalAddresses() throws IOException + { + String hostname = null; + InetAddress[] inetAddresses = null; + try { + InetAddress localHost = InetAddress.getLocalHost(); + hostname = localHost.getHostName(); + inetAddresses = InetAddress.getAllByName(hostname); + HostAddress[] hAddresses = new HostAddress[inetAddresses.length]; + for (int i = 0; i < inetAddresses.length; i++) + { + hAddresses[i] = new HostAddress(inetAddresses[i]); + } + if (DEBUG) { + System.out.println(">>> KrbKdcReq local addresses for " + + hostname + " are: "); + + for (int i = 0; i < inetAddresses.length; i++) { + System.out.println("\n\t" + inetAddresses[i]); + if (inetAddresses[i] instanceof Inet4Address) + System.out.println("IPv4 address"); + if (inetAddresses[i] instanceof Inet6Address) + System.out.println("IPv6 address"); + } + } + return (new HostAddresses(hAddresses)); + } catch (Exception exc) { + throw new IOException(exc.toString()); + } + + } + + /** + * Creates a new HostAddresses instance from the supplied list + * of InetAddresses. + */ + public HostAddresses(InetAddress[] inetAddresses) + { + if (inetAddresses == null) + { + addresses = null; + return; + } + + addresses = new HostAddress[inetAddresses.length]; + for (int i = 0; i < inetAddresses.length; i++) + addresses[i] = new HostAddress(inetAddresses[i]); + } +} diff --git a/src/sun/security/krb5/internal/KDCOptions.java b/src/sun/security/krb5/internal/KDCOptions.java new file mode 100644 index 00000000..24b0a9c1 --- /dev/null +++ b/src/sun/security/krb5/internal/KDCOptions.java @@ -0,0 +1,330 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Config; +import sun.security.krb5.KrbException; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.internal.util.KerberosFlags; +import sun.security.util.*; +import java.io.IOException; + +/** + * Implements the ASN.1 KDCOptions type. + * + *

    + * KDCOptions ::= KerberosFlags + * -- reserved(0), + * -- forwardable(1), + * -- forwarded(2), + * -- proxiable(3), + * -- proxy(4), + * -- allow-postdate(5), + * -- postdated(6), + * -- unused7(7), + * -- renewable(8), + * -- unused9(9), + * -- unused10(10), + * -- opt-hardware-auth(11), + * -- unused12(12), + * -- unused13(13), + * -- 15 is reserved for canonicalize + * -- unused15(15), + * -- 26 was unused in 1510 + * -- disable-transited-check(26), + * -- renewable-ok(27), + * -- enc-tkt-in-skey(28), + * -- renew(30), + * -- validate(31) + * + * KerberosFlags ::= BIT STRING (SIZE (32..MAX)) + * -- minimum number of bits shall be sent, + * -- but no fewer than 32 + * + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + * + *

    + * This class appears as data field in the initial request(KRB_AS_REQ) + * or subsequent request(KRB_TGS_REQ) to the KDC and indicates the flags + * that the client wants to set on the tickets. + * + * The optional bits are: + *

      + *
    • KDCOptions.RESERVED + *
    • KDCOptions.FORWARDABLE + *
    • KDCOptions.FORWARDED + *
    • KDCOptions.PROXIABLE + *
    • KDCOptions.PROXY + *
    • KDCOptions.ALLOW_POSTDATE + *
    • KDCOptions.POSTDATED + *
    • KDCOptions.RENEWABLE + *
    • KDCOptions.RENEWABLE_OK + *
    • KDCOptions.ENC_TKT_IN_SKEY + *
    • KDCOptions.RENEW + *
    • KDCOptions.VALIDATE + *
    + *

    Various checks must be made before honoring an option. The restrictions + * on the use of some options are as follows: + *

      + *
    1. FORWARDABLE, FORWARDED, PROXIABLE, RENEWABLE options may be set in + * subsequent request only if the ticket_granting ticket on which it is based has + * the same options (FORWARDABLE, FORWARDED, PROXIABLE, RENEWABLE) set. + *
    2. ALLOW_POSTDATE may be set in subsequent request only if the + * ticket-granting ticket on which it is based also has its MAY_POSTDATE flag set. + *
    3. POSTDATED may be set in subsequent request only if the + * ticket-granting ticket on which it is based also has its MAY_POSTDATE flag set. + *
    4. RENEWABLE or RENEW may be set in subsequent request only if the + * ticket-granting ticket on which it is based also has its RENEWABLE flag set. + *
    5. POXY may be set in subsequent request only if the ticket-granting ticket + * on which it is based also has its PROXIABLE flag set, and the address(es) of + * the host from which the resulting ticket is to be valid should be included + * in the addresses field of the request. + *
    6. FORWARDED, PROXY, ENC_TKT_IN_SKEY, RENEW, VALIDATE are used only in + * subsequent requests. + *

    + */ + +public class KDCOptions extends KerberosFlags { + + private static final int KDC_OPT_PROXIABLE = 0x10000000; + private static final int KDC_OPT_RENEWABLE_OK = 0x00000010; + private static final int KDC_OPT_FORWARDABLE = 0x40000000; + + + // KDC Options + + public static final int RESERVED = 0; + public static final int FORWARDABLE = 1; + public static final int FORWARDED = 2; + public static final int PROXIABLE = 3; + public static final int PROXY = 4; + public static final int ALLOW_POSTDATE = 5; + public static final int POSTDATED = 6; + public static final int UNUSED7 = 7; + public static final int RENEWABLE = 8; + public static final int UNUSED9 = 9; + public static final int UNUSED10 = 10; + public static final int UNUSED11 = 11; + public static final int CNAME_IN_ADDL_TKT = 14; + public static final int RENEWABLE_OK = 27; + public static final int ENC_TKT_IN_SKEY = 28; + public static final int RENEW = 30; + public static final int VALIDATE = 31; + + private static final String[] names = { + "RESERVED", //0 + "FORWARDABLE", //1; + "FORWARDED", //2; + "PROXIABLE", //3; + "PROXY", //4; + "ALLOW_POSTDATE", //5; + "POSTDATED", //6; + "UNUSED7", //7; + "RENEWABLE", //8; + "UNUSED9", //9; + "UNUSED10", //10; + "UNUSED11", //11; + null,null, + "CNAME_IN_ADDL_TKT",//14; + null,null,null,null,null,null,null,null,null,null,null,null, + "RENEWABLE_OK", //27; + "ENC_TKT_IN_SKEY", //28; + null, + "RENEW", //30; + "VALIDATE", //31; + }; + + private boolean DEBUG = Krb5.DEBUG; + + public static KDCOptions with(int... flags) { + KDCOptions options = new KDCOptions(); + for (int flag: flags) { + options.set(flag, true); + } + return options; + } + + public KDCOptions() { + super(Krb5.KDC_OPTS_MAX + 1); + setDefault(); + } + + public KDCOptions(int size, byte[] data) throws Asn1Exception { + super(size, data); + if ((size > data.length * BITS_PER_UNIT) || (size > Krb5.KDC_OPTS_MAX + 1)) + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + + /** + * Constructs a KDCOptions from the specified bit settings. + * + * @param data the bits to be set for the KDCOptions. + * @exception Asn1Exception if an error occurs while decoding an ASN1 + * encoded data. + * + */ + public KDCOptions(boolean[] data) throws Asn1Exception { + super(data); + if (data.length > Krb5.KDC_OPTS_MAX + 1) { + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + } + + public KDCOptions(DerValue encoding) throws Asn1Exception, IOException { + this(encoding.getUnalignedBitString(true).toBooleanArray()); + } + + /** + * Constructs a KDCOptions from the passed bit settings. + * + * @param options the bits to be set for the KDCOptions. + * + */ + public KDCOptions(byte[] options) { + super(options.length * BITS_PER_UNIT, options); + } + + /** + * Parse (unmarshal) a KDCOptions from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @param data the Der input stream value, which contains one or more + * marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @return an instance of KDCOptions. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + + public static KDCOptions parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } else { + DerValue subDer = der.getData().getDerValue(); + return new KDCOptions(subDer); + } + } + + /** + * Sets the value(true/false) for one of the KDCOptions. + * + * @param option an option bit. + * @param value true if the option is selected, false if the option is not selected. + * @exception ArrayIndexOutOfBoundsException if array index out of bound occurs. + * @see Krb5 + */ + public void set(int option, boolean value) throws ArrayIndexOutOfBoundsException { + super.set(option, value); + } + + /** + * Gets the value(true/false) for one of the KDCOptions. + * + * @param option an option bit. + * @return value true if the option is selected, false if the option is not selected. + * @exception ArrayIndexOutOfBoundsException if array index out of bound occurs. + * @see Krb5 + */ + + public boolean get(int option) throws ArrayIndexOutOfBoundsException { + return super.get(option); + } + + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("KDCOptions: "); + for (int i=0; i + * KDC-REP ::= SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (11 -- AS -- | 13 -- TGS --), + * padata [2] SEQUENCE OF PA-DATA OPTIONAL + * -- NOTE: not empty --, + * crealm [3] Realm, + * cname [4] PrincipalName, + * ticket [5] Ticket, + * enc-part [6] EncryptedData + * -- EncASRepPart or EncTGSRepPart, + * -- as appropriate + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class KDCRep { + + public PrincipalName cname; + public Ticket ticket; + public EncryptedData encPart; + public EncKDCRepPart encKDCRepPart; //not part of ASN.1 encoding + private int pvno; + private int msgType; + public PAData[] pAData = null; //optional + private boolean DEBUG = Krb5.DEBUG; + + public KDCRep( + PAData[] new_pAData, + PrincipalName new_cname, + Ticket new_ticket, + EncryptedData new_encPart, + int req_type) throws IOException { + pvno = Krb5.PVNO; + msgType = req_type; + if (new_pAData != null) { + pAData = new PAData[new_pAData.length]; + for (int i = 0; i < new_pAData.length; i++) { + if (new_pAData[i] == null) { + throw new IOException("Cannot create a KDCRep"); + } else { + pAData[i] = (PAData) new_pAData[i].clone(); + } + } + } + cname = new_cname; + ticket = new_ticket; + encPart = new_encPart; + } + + public KDCRep() { + } + + public KDCRep(byte[] data, int req_type) throws Asn1Exception, + KrbApErrException, RealmException, IOException { + init(new DerValue(data), req_type); + } + + public KDCRep(DerValue encoding, int req_type) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding, req_type); + } + + /* + // Not used? Don't know what keyusage to use here %%% + public void decrypt(EncryptionKey key) throws Asn1Exception, + IOException, KrbException, RealmException { + encKDCRepPart = new EncKDCRepPart(encPart.decrypt(key), msgType); + } + */ + /** + * Initializes an KDCRep object. + * + * @param encoding a single DER-encoded value. + * @param req_type reply message type. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while constructing + * a Realm object from DER-encoded data. + * @exception KrbApErrException if the value read from the DER-encoded + * data stream does not match the pre-defined value. + * + */ + protected void init(DerValue encoding, int req_type) + throws Asn1Exception, RealmException, IOException, + KrbApErrException { + DerValue der, subDer; + if ((encoding.getTag() & 0x1F) != req_type) { + if (DEBUG) { + System.out.println(">>> KDCRep: init() " + + "encoding tag is " + + encoding.getTag() + + " req type is " + req_type); + } + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x00) { + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x01) { + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != req_type) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if ((der.getData().peekByte() & 0x1F) == 0x02) { + subDer = der.getData().getDerValue(); + DerValue[] padata = subDer.getData().getSequence(1); + pAData = new PAData[padata.length]; + for (int i = 0; i < padata.length; i++) { + pAData[i] = new PAData(padata[i]); + } + } else { + pAData = null; + } + Realm crealm = Realm.parse(der.getData(), (byte) 0x03, false); + cname = PrincipalName.parse(der.getData(), (byte) 0x04, false, crealm); + ticket = Ticket.parse(der.getData(), (byte) 0x05, false); + encPart = EncryptedData.parse(der.getData(), (byte) 0x06, false); + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes this object to a byte array. + * @return byte array of encoded APReq object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), temp); + if (pAData != null && pAData.length > 0) { + DerOutputStream padata_stream = new DerOutputStream(); + for (int i = 0; i < pAData.length; i++) { + padata_stream.write(pAData[i].asn1Encode()); + } + temp = new DerOutputStream(); + temp.write(DerValue.tag_SequenceOf, padata_stream); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), temp); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), cname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x04), cname.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x05), ticket.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x06), encPart.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/KDCReq.java b/src/sun/security/krb5/internal/KDCReq.java new file mode 100644 index 00000000..16591e80 --- /dev/null +++ b/src/sun/security/krb5/internal/KDCReq.java @@ -0,0 +1,215 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import java.util.Vector; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 KRB_KDC_REQ type. + * + *

    + * KDC-REQ ::= SEQUENCE { + * -- NOTE: first tag is [1], not [0] + * pvno [1] INTEGER (5) , + * msg-type [2] INTEGER (10 -- AS -- | 12 -- TGS --), + * padata [3] SEQUENCE OF PA-DATA OPTIONAL + * -- NOTE: not empty --, + * req-body [4] KDC-REQ-BODY + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class KDCReq { + + public KDCReqBody reqBody; + private int pvno; + private int msgType; + private PAData[] pAData = null; //optional + + public KDCReq(PAData[] new_pAData, KDCReqBody new_reqBody, + int req_type) throws IOException { + pvno = Krb5.PVNO; + msgType = req_type; + if (new_pAData != null) { + pAData = new PAData[new_pAData.length]; + for (int i = 0; i < new_pAData.length; i++) { + if (new_pAData[i] == null) { + throw new IOException("Cannot create a KDCRep"); + } else { + pAData[i] = (PAData) new_pAData[i].clone(); + } + } + } + reqBody = new_reqBody; + } + + public KDCReq() { + } + + public KDCReq(byte[] data, int req_type) throws Asn1Exception, + IOException, KrbException { + init(new DerValue(data), req_type); + } + + /** + * Creates an KDCReq object from a DerValue object and asn1 type. + * + * @param der a DER value of an KDCReq object. + * @param req_type a encoded asn1 type value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exceptoin KrbErrException + */ + public KDCReq(DerValue der, int req_type) throws Asn1Exception, + IOException, KrbException { + init(der, req_type); + } + + /** + * Initializes a KDCReq object from a DerValue. The DER encoding + * must be in the format specified by the KRB_KDC_REQ ASN.1 notation. + * + * @param encoding a DER-encoded KDCReq object. + * @param req_type an int indicating whether it's KRB_AS_REQ or KRB_TGS_REQ type + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbException if an error occurs while constructing a Realm object, + * or a Krb object from DER-encoded data. + */ + protected void init(DerValue encoding, int req_type) throws Asn1Exception, + IOException, KrbException { + DerValue der, subDer; + BigInteger bint; + if ((encoding.getTag() & 0x1F) != req_type) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x01F) == 0x01) { + bint = subDer.getData().getBigInteger(); + this.pvno = bint.intValue(); + if (this.pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x01F) == 0x02) { + bint = subDer.getData().getBigInteger(); + this.msgType = bint.intValue(); + if (this.msgType != req_type) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if ((der.getData().peekByte() & 0x1F) == 0x03) { + subDer = der.getData().getDerValue(); + DerValue subsubDer = subDer.getData().getDerValue(); + if (subsubDer.getTag() != DerValue.tag_SequenceOf) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + Vector v = new Vector<>(); + while (subsubDer.getData().available() > 0) { + v.addElement(new PAData(subsubDer.getData().getDerValue())); + } + if (v.size() > 0) { + pAData = new PAData[v.size()]; + v.copyInto(pAData); + } + } else { + pAData = null; + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x01F) == 0x04) { + DerValue subsubDer = subDer.getData().getDerValue(); + reqBody = new KDCReqBody(subsubDer, msgType); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes this object to a byte array. + * + * @return an byte array of encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp, bytes, out; + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), temp); + if (pAData != null && pAData.length > 0) { + temp = new DerOutputStream(); + for (int i = 0; i < pAData.length; i++) { + temp.write(pAData[i].asn1Encode()); + } + bytes = new DerOutputStream(); + bytes.write(DerValue.tag_SequenceOf, temp); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), bytes); + } + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x04), reqBody.asn1Encode(msgType)); + bytes = new DerOutputStream(); + bytes.write(DerValue.tag_Sequence, out); + out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) msgType), bytes); + return out.toByteArray(); + } + + public byte[] asn1EncodeReqBody() throws Asn1Exception, IOException { + return reqBody.asn1Encode(msgType); + } +} diff --git a/src/sun/security/krb5/internal/KDCReqBody.java b/src/sun/security/krb5/internal/KDCReqBody.java new file mode 100644 index 00000000..83178b6c --- /dev/null +++ b/src/sun/security/krb5/internal/KDCReqBody.java @@ -0,0 +1,282 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.util.Vector; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 KDC-REQ-BODY type. + * + *

    + * KDC-REQ-BODY ::= SEQUENCE { + * kdc-options [0] KDCOptions, + * cname [1] PrincipalName OPTIONAL + * -- Used only in AS-REQ --, + * realm [2] Realm + * -- Server's realm + * -- Also client's in AS-REQ --, + * sname [3] PrincipalName OPTIONAL, + * from [4] KerberosTime OPTIONAL, + * till [5] KerberosTime, + * rtime [6] KerberosTime OPTIONAL, + * nonce [7] UInt32, + * etype [8] SEQUENCE OF Int32 -- EncryptionType + * -- in preference order --, + * addresses [9] HostAddresses OPTIONAL, + * enc-authorization-data [10] EncryptedData OPTIONAL + * -- AuthorizationData --, + * additional-tickets [11] SEQUENCE OF Ticket OPTIONAL + * -- NOTE: not empty + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KDCReqBody { + public KDCOptions kdcOptions; + public PrincipalName cname; //optional in ASReq only + public PrincipalName sname; //optional + public KerberosTime from; //optional + public KerberosTime till; + public KerberosTime rtime; //optional + public HostAddresses addresses; //optional + + private int nonce; + private int[] eType = null; //a sequence; not optional + private EncryptedData encAuthorizationData; //optional + private Ticket[] additionalTickets; //optional + + public KDCReqBody( + KDCOptions new_kdcOptions, + PrincipalName new_cname, //optional in ASReq only + PrincipalName new_sname, //optional + KerberosTime new_from, //optional + KerberosTime new_till, + KerberosTime new_rtime, //optional + int new_nonce, + int[] new_eType, //a sequence; not optional + HostAddresses new_addresses, //optional + EncryptedData new_encAuthorizationData, //optional + Ticket[] new_additionalTickets //optional + ) throws IOException { + kdcOptions = new_kdcOptions; + cname = new_cname; + sname = new_sname; + from = new_from; + till = new_till; + rtime = new_rtime; + nonce = new_nonce; + if (new_eType != null) { + eType = new_eType.clone(); + } + addresses = new_addresses; + encAuthorizationData = new_encAuthorizationData; + if (new_additionalTickets != null) { + additionalTickets = new Ticket[new_additionalTickets.length]; + for (int i = 0; i < new_additionalTickets.length; i++) { + if (new_additionalTickets[i] == null) { + throw new IOException("Cannot create a KDCReqBody"); + } else { + additionalTickets[i] = (Ticket)new_additionalTickets[i].clone(); + } + } + } + } + + /** + * Constructs a KDCReqBody object. + * @param encoding a DER-encoded data. + * @param msgType an int indicating whether it's KRB_AS_REQ or KRB_TGS_REQ type. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while constructing a Realm object from the encoded data. + * + */ + public KDCReqBody(DerValue encoding, int msgType) + throws Asn1Exception, RealmException, KrbException, IOException { + DerValue der, subDer; + addresses = null; + encAuthorizationData = null; + additionalTickets = null; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + kdcOptions = KDCOptions.parse(encoding.getData(), (byte)0x00, false); + + // cname only appears in AS-REQ and it shares the realm field with + // sname. This is the only place where realm comes after the name. + // We first give cname a fake realm and reassign it the correct + // realm after the realm field is read. + cname = PrincipalName.parse(encoding.getData(), (byte)0x01, true, + new Realm("PLACEHOLDER")); + if ((msgType != Krb5.KRB_AS_REQ) && (cname != null)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + Realm realm = Realm.parse(encoding.getData(), (byte)0x02, false); + if (cname != null) { + cname = new PrincipalName( + cname.getNameType(), cname.getNameStrings(), realm); + } + sname = PrincipalName.parse(encoding.getData(), (byte)0x03, true, realm); + from = KerberosTime.parse(encoding.getData(), (byte)0x04, true); + till = KerberosTime.parse(encoding.getData(), (byte)0x05, false); + rtime = KerberosTime.parse(encoding.getData(), (byte)0x06, true); + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x07) { + nonce = der.getData().getBigInteger().intValue(); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + Vector v = new Vector<>(); + if ((der.getTag() & (byte)0x1F) == (byte)0x08) { + subDer = der.getData().getDerValue(); + + if (subDer.getTag() == DerValue.tag_SequenceOf) { + while(subDer.getData().available() > 0) { + v.addElement(subDer.getData().getBigInteger().intValue()); + } + eType = new int[v.size()]; + for (int i = 0; i < v.size(); i++) { + eType[i] = v.elementAt(i); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) { + addresses = HostAddresses.parse(encoding.getData(), (byte)0x09, true); + } + if (encoding.getData().available() > 0) { + encAuthorizationData = EncryptedData.parse(encoding.getData(), (byte)0x0A, true); + } + if (encoding.getData().available() > 0) { + Vector tempTickets = new Vector<>(); + der = encoding.getData().getDerValue(); + if ((der.getTag() & (byte)0x1F) == (byte)0x0B) { + subDer = der.getData().getDerValue(); + if (subDer.getTag() == DerValue.tag_SequenceOf) { + while (subDer.getData().available() > 0) { + tempTickets.addElement(new Ticket(subDer.getData().getDerValue())); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (tempTickets.size() > 0) { + additionalTickets = new Ticket[tempTickets.size()]; + tempTickets.copyInto(additionalTickets); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + if (encoding.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes this object to an OutputStream. + * + * @return an byte array of encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * + */ + public byte[] asn1Encode(int msgType) throws Asn1Exception, IOException { + Vector v = new Vector<>(); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), kdcOptions.asn1Encode())); + if (msgType == Krb5.KRB_AS_REQ) { + if (cname != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), cname.asn1Encode())); + } + } + if (sname != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), sname.getRealm().asn1Encode())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), sname.asn1Encode())); + } else if (cname != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cname.getRealm().asn1Encode())); + } + if (from != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), from.asn1Encode())); + } + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), till.asn1Encode())); + if (rtime != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), rtime.asn1Encode())); + } + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(nonce)); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), temp.toByteArray())); + //revisit, if empty eType sequences are allowed + temp = new DerOutputStream(); + for (int i = 0; i < eType.length; i++) { + temp.putInteger(BigInteger.valueOf(eType[i])); + } + DerOutputStream eTypetemp = new DerOutputStream(); + eTypetemp.write(DerValue.tag_SequenceOf, temp); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), eTypetemp.toByteArray())); + if (addresses != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), addresses.asn1Encode())); + } + if (encAuthorizationData != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), encAuthorizationData.asn1Encode())); + } + if (additionalTickets != null && additionalTickets.length > 0) { + temp = new DerOutputStream(); + for (int i = 0; i < additionalTickets.length; i++) { + temp.write(additionalTickets[i].asn1Encode()); + } + DerOutputStream ticketsTemp = new DerOutputStream(); + ticketsTemp.write(DerValue.tag_SequenceOf, temp); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), ticketsTemp.toByteArray())); + } + DerValue der[] = new DerValue[v.size()]; + v.copyInto(der); + temp = new DerOutputStream(); + temp.putSequence(der); + return temp.toByteArray(); + } + + public int getNonce() { + return nonce; + } +} diff --git a/src/sun/security/krb5/internal/KRBCred.java b/src/sun/security/krb5/internal/KRBCred.java new file mode 100644 index 00000000..ff719f43 --- /dev/null +++ b/src/sun/security/krb5/internal/KRBCred.java @@ -0,0 +1,189 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.RealmException; +import sun.security.util.*; +import java.util.Vector; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 Authenticator type. + * + *

    + * KRB-CRED ::= [APPLICATION 22] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (22), + * tickets [2] SEQUENCE OF Ticket, + * enc-part [3] EncryptedData -- EncKrbCredPart + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public class KRBCred { + + public Ticket[] tickets = null; + public EncryptedData encPart; + private int pvno; + private int msgType; + + public KRBCred(Ticket[] new_tickets, EncryptedData new_encPart) throws IOException { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_CRED; + if (new_tickets != null) { + tickets = new Ticket[new_tickets.length]; + for (int i = 0; i < new_tickets.length; i++) { + if (new_tickets[i] == null) { + throw new IOException("Cannot create a KRBCred"); + } else { + tickets[i] = (Ticket) new_tickets[i].clone(); + } + } + } + encPart = new_encPart; + } + + public KRBCred(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + } + + public KRBCred(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + } + + /** + * Initializes an KRBCred object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + if (((encoding.getTag() & (byte) 0x1F) != (byte) 0x16) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + DerValue der, subDer; + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x00) { + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x01) { + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_CRED) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x02) { + DerValue subsubDer = subDer.getData().getDerValue(); + if (subsubDer.getTag() != DerValue.tag_SequenceOf) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + Vector v = new Vector<>(); + while (subsubDer.getData().available() > 0) { + v.addElement(new Ticket(subsubDer.getData().getDerValue())); + } + if (v.size() > 0) { + tickets = new Ticket[v.size()]; + v.copyInto(tickets); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + encPart = EncryptedData.parse(der.getData(), (byte) 0x03, false); + + if (der.getData().available() > 0) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + } + + /** + * Encodes an KRBCred object. + * @return the data of encoded EncAPRepPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp, bytes, out; + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x01), temp); + temp = new DerOutputStream(); + for (int i = 0; i < tickets.length; i++) { + temp.write(tickets[i].asn1Encode()); + } + bytes = new DerOutputStream(); + bytes.write(DerValue.tag_SequenceOf, temp); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x02), bytes); + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0x03), encPart.asn1Encode()); + bytes = new DerOutputStream(); + bytes.write(DerValue.tag_Sequence, out); + out = new DerOutputStream(); + out.write(DerValue.createTag(DerValue.TAG_APPLICATION, + true, (byte) 0x16), bytes); + return out.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/KRBError.java b/src/sun/security/krb5/internal/KRBError.java new file mode 100644 index 00000000..f947d740 --- /dev/null +++ b/src/sun/security/krb5/internal/KRBError.java @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import java.io.ObjectOutputStream; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.Checksum; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.Realm; +import sun.security.krb5.RealmException; +import sun.security.util.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import sun.security.krb5.internal.util.KerberosString; + +/** + * Implements the ASN.1 KRBError type. + * + *

    + * KRB-ERROR ::= [APPLICATION 30] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (30), + * ctime [2] KerberosTime OPTIONAL, + * cusec [3] Microseconds OPTIONAL, + * stime [4] KerberosTime, + * susec [5] Microseconds, + * error-code [6] Int32, + * crealm [7] Realm OPTIONAL, + * cname [8] PrincipalName OPTIONAL, + * realm [9] Realm -- service realm --, + * sname [10] PrincipalName -- service name --, + * e-text [11] KerberosString OPTIONAL, + * e-data [12] OCTET STRING OPTIONAL + * } + * + * METHOD-DATA ::= SEQUENCE OF PA-DATA + * + * TYPED-DATA ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { + * data-type [0] Int32, + * data-value [1] OCTET STRING OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KRBError implements java.io.Serializable { + static final long serialVersionUID = 3643809337475284503L; + + private int pvno; + private int msgType; + private KerberosTime cTime; //optional + private Integer cuSec; //optional + private KerberosTime sTime; + private Integer suSec; + private int errorCode; + private PrincipalName cname; //optional + private PrincipalName sname; + private String eText; //optional + private byte[] eData; //optional + private Checksum eCksum; //optional + + private PAData[] pa; // PA-DATA in eData + + private static boolean DEBUG = Krb5.DEBUG; + + private void readObject(ObjectInputStream is) + throws IOException, ClassNotFoundException { + try { + init(new DerValue((byte[])is.readObject())); + parseEData(eData); + } catch (Exception e) { + throw new IOException(e); + } + } + + private void writeObject(ObjectOutputStream os) + throws IOException { + try { + os.writeObject(asn1Encode()); + } catch (Exception e) { + throw new IOException(e); + } + } + + public KRBError( + APOptions new_apOptions, + KerberosTime new_cTime, + Integer new_cuSec, + KerberosTime new_sTime, + Integer new_suSec, + int new_errorCode, + PrincipalName new_cname, + PrincipalName new_sname, + String new_eText, + byte[] new_eData + ) throws IOException, Asn1Exception { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_ERROR; + cTime = new_cTime; + cuSec = new_cuSec; + sTime = new_sTime; + suSec = new_suSec; + errorCode = new_errorCode; + cname = new_cname; + sname = new_sname; + eText = new_eText; + eData = new_eData; + + parseEData(eData); + } + + public KRBError( + APOptions new_apOptions, + KerberosTime new_cTime, + Integer new_cuSec, + KerberosTime new_sTime, + Integer new_suSec, + int new_errorCode, + PrincipalName new_cname, + PrincipalName new_sname, + String new_eText, + byte[] new_eData, + Checksum new_eCksum + ) throws IOException, Asn1Exception { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_ERROR; + cTime = new_cTime; + cuSec = new_cuSec; + sTime = new_sTime; + suSec = new_suSec; + errorCode = new_errorCode; + cname = new_cname; + sname = new_sname; + eText = new_eText; + eData = new_eData; + eCksum = new_eCksum; + + parseEData(eData); + } + + public KRBError(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + parseEData(eData); + } + + public KRBError(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + showDebug(); + parseEData(eData); + } + + /* + * Attention: + * + * According to RFC 4120, e-data field in a KRB-ERROR message is + * a METHOD-DATA when errorCode is KDC_ERR_PREAUTH_REQUIRED, + * and application-specific otherwise (The RFC suggests using + * TYPED-DATA). + * + * Hence, the ideal procedure to parse e-data should look like: + * + * if (errorCode is KDC_ERR_PREAUTH_REQUIRED) { + * parse as METHOD-DATA + * } else { + * try parsing as TYPED-DATA + * } + * + * Unfortunately, we know that some implementations also use the + * METHOD-DATA format for errorcode KDC_ERR_PREAUTH_FAILED, and + * do not use the TYPED-DATA for other errorcodes (say, + * KDC_ERR_CLIENT_REVOKED). + */ + + // parse the edata field + private void parseEData(byte[] data) throws IOException { + if (data == null) { + return; + } + + // We need to parse eData as METHOD-DATA for both errorcodes. + if (errorCode == Krb5.KDC_ERR_PREAUTH_REQUIRED + || errorCode == Krb5.KDC_ERR_PREAUTH_FAILED) { + try { + // RFC 4120 does not guarantee that eData is METHOD-DATA when + // errorCode is KDC_ERR_PREAUTH_FAILED. Therefore, the parse + // may fail. + parsePAData(data); + } catch (Exception e) { + if (DEBUG) { + System.out.println("Unable to parse eData field of KRB-ERROR:\n" + + new sun.misc.HexDumpEncoder().encodeBuffer(data)); + } + IOException ioe = new IOException( + "Unable to parse eData field of KRB-ERROR"); + ioe.initCause(e); + throw ioe; + } + } else { + if (DEBUG) { + System.out.println("Unknown eData field of KRB-ERROR:\n" + + new sun.misc.HexDumpEncoder().encodeBuffer(data)); + } + } + } + + /** + * Try parsing the data as a sequence of PA-DATA. + * @param data the data block + */ + private void parsePAData(byte[] data) + throws IOException, Asn1Exception { + DerValue derPA = new DerValue(data); + List paList = new ArrayList<>(); + while (derPA.data.available() > 0) { + // read the PA-DATA + DerValue tmp = derPA.data.getDerValue(); + PAData pa_data = new PAData(tmp); + paList.add(pa_data); + if (DEBUG) { + System.out.println(pa_data); + } + } + pa = paList.toArray(new PAData[paList.size()]); + } + + public final KerberosTime getServerTime() { + return sTime; + } + + public final KerberosTime getClientTime() { + return cTime; + } + + public final Integer getServerMicroSeconds() { + return suSec; + } + + public final Integer getClientMicroSeconds() { + return cuSec; + } + + public final int getErrorCode() { + return errorCode; + } + + // access pre-auth info + public final PAData[] getPA() { + return pa; + } + + public final String getErrorString() { + return eText; + } + + /** + * Initializes a KRBError object. + * @param encoding a DER-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + DerValue der, subDer; + if (((encoding.getTag() & (byte)0x1F) != (byte)0x1E) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte)0x1F) == (byte)0x00) { + + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte)0x1F) == (byte)0x01) { + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_ERROR) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + cTime = KerberosTime.parse(der.getData(), (byte)0x02, true); + if ((der.getData().peekByte() & 0x1F) == 0x03) { + subDer = der.getData().getDerValue(); + cuSec = new Integer(subDer.getData().getBigInteger().intValue()); + } + else cuSec = null; + sTime = KerberosTime.parse(der.getData(), (byte)0x04, false); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte)0x1F) == (byte)0x05) { + suSec = new Integer (subDer.getData().getBigInteger().intValue()); + } + else throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte)0x1F) == (byte)0x06) { + errorCode = subDer.getData().getBigInteger().intValue(); + } + else throw new Asn1Exception(Krb5.ASN1_BAD_ID); + Realm crealm = Realm.parse(der.getData(), (byte)0x07, true); + cname = PrincipalName.parse(der.getData(), (byte)0x08, true, crealm); + Realm realm = Realm.parse(der.getData(), (byte)0x09, false); + sname = PrincipalName.parse(der.getData(), (byte)0x0A, false, realm); + eText = null; + eData = null; + eCksum = null; + if (der.getData().available() >0) { + if ((der.getData().peekByte() & 0x1F) == 0x0B) { + subDer = der.getData().getDerValue(); + eText = new KerberosString(subDer.getData().getDerValue()) + .toString(); + } + } + if (der.getData().available() >0) { + if ((der.getData().peekByte() & 0x1F) == 0x0C) { + subDer = der.getData().getDerValue(); + eData = subDer.getData().getOctetString(); + } + } + if (der.getData().available() >0) { + eCksum = Checksum.parse(der.getData(), (byte)0x0D, true); + } + if (der.getData().available() >0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * For debug use only + */ + private void showDebug() { + if (DEBUG) { + System.out.println(">>>KRBError:"); + if (cTime != null) + System.out.println("\t cTime is " + cTime.toDate().toString() + " " + cTime.toDate().getTime()); + if (cuSec != null) { + System.out.println("\t cuSec is " + cuSec.intValue()); + } + + System.out.println("\t sTime is " + sTime.toDate().toString + () + " " + sTime.toDate().getTime()); + System.out.println("\t suSec is " + suSec); + System.out.println("\t error code is " + errorCode); + System.out.println("\t error Message is " + Krb5.getErrorMessage(errorCode)); + if (cname != null) { + System.out.println("\t cname is " + cname.toString()); + } + if (sname != null) { + System.out.println("\t sname is " + sname.toString()); + } + if (eData != null) { + System.out.println("\t eData provided."); + } + if (eCksum != null) { + System.out.println("\t checksum provided."); + } + System.out.println("\t msgType is " + msgType); + } + } + + /** + * Encodes an KRBError object. + * @return the byte array of encoded KRBError object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + + temp.putInteger(BigInteger.valueOf(pvno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + + if (cTime != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cTime.asn1Encode()); + } + if (cuSec != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(cuSec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp); + } + + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), sTime.asn1Encode()); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(suSec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(errorCode)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), temp); + + if (cname != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), cname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), cname.asn1Encode()); + } + + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), sname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), sname.asn1Encode()); + + if (eText != null) { + temp = new DerOutputStream(); + temp.putDerValue(new KerberosString(eText).toDerValue()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), temp); + } + if (eData != null) { + temp = new DerOutputStream(); + temp.putOctetString(eData); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0C), temp); + } + if (eCksum != null) { + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0D), eCksum.asn1Encode()); + } + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x1E), temp); + return bytes.toByteArray(); + } + + @Override public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof KRBError)) { + return false; + } + + KRBError other = (KRBError)obj; + return pvno == other.pvno && + msgType == other.msgType && + isEqual(cTime, other.cTime) && + isEqual(cuSec, other.cuSec) && + isEqual(sTime, other.sTime) && + isEqual(suSec, other.suSec) && + errorCode == other.errorCode && + isEqual(cname, other.cname) && + isEqual(sname, other.sname) && + isEqual(eText, other.eText) && + Arrays.equals(eData, other.eData) && + isEqual(eCksum, other.eCksum); + } + + private static boolean isEqual(Object a, Object b) { + return (a == null)?(b == null):(a.equals(b)); + } + + @Override public int hashCode() { + int result = 17; + result = 37 * result + pvno; + result = 37 * result + msgType; + if (cTime != null) result = 37 * result + cTime.hashCode(); + if (cuSec != null) result = 37 * result + cuSec.hashCode(); + if (sTime != null) result = 37 * result + sTime.hashCode(); + if (suSec != null) result = 37 * result + suSec.hashCode(); + result = 37 * result + errorCode; + if (cname != null) result = 37 * result + cname.hashCode(); + if (sname != null) result = 37 * result + sname.hashCode(); + if (eText != null) result = 37 * result + eText.hashCode(); + result = 37 * result + Arrays.hashCode(eData); + if (eCksum != null) result = 37 * result + eCksum.hashCode(); + return result; + } +} diff --git a/src/sun/security/krb5/internal/KRBPriv.java b/src/sun/security/krb5/internal/KRBPriv.java new file mode 100644 index 00000000..f0867ef5 --- /dev/null +++ b/src/sun/security/krb5/internal/KRBPriv.java @@ -0,0 +1,143 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 KRB-PRIV type. + * + *

    + * KRB-PRIV ::= [APPLICATION 21] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (21), + * -- NOTE: there is no [2] tag + * enc-part [3] EncryptedData -- EncKrbPrivPart + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KRBPriv { + public int pvno; + public int msgType; + public EncryptedData encPart; + + public KRBPriv(EncryptedData new_encPart) { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_PRIV; + encPart = new_encPart; + } + + public KRBPriv(byte[] data) throws Asn1Exception, + KrbApErrException, IOException { + init(new DerValue(data)); + } + + public KRBPriv(DerValue encoding) throws Asn1Exception, + KrbApErrException, IOException { + init(encoding); + } + + + /** + * Initializes an KRBPriv object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + */ + private void init(DerValue encoding) throws Asn1Exception, + KrbApErrException, IOException { + DerValue der, subDer; + if (((encoding.getTag() & (byte)0x1F) != (byte)0x15) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x00) { + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x01) { + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_PRIV) + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + encPart = EncryptedData.parse(der.getData(), (byte)0x03, false); + if (der.getData().available() >0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an KRBPriv object. + * @return byte array of encoded EncAPRepPart object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp, bytes; + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), encPart.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x15), temp); + return bytes.toByteArray(); + } + +} diff --git a/src/sun/security/krb5/internal/KRBSafe.java b/src/sun/security/krb5/internal/KRBSafe.java new file mode 100644 index 00000000..52da89a7 --- /dev/null +++ b/src/sun/security/krb5/internal/KRBSafe.java @@ -0,0 +1,146 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Checksum; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.RealmException; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 KRBSafe type. + * + *

    + * KRB-SAFE ::= [APPLICATION 20] SEQUENCE { + * pvno [0] INTEGER (5), + * msg-type [1] INTEGER (20), + * safe-body [2] KRB-SAFE-BODY, + * cksum [3] Checksum + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specifications available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KRBSafe { + public int pvno; + public int msgType; + public KRBSafeBody safeBody; + public Checksum cksum; + + public KRBSafe(KRBSafeBody new_safeBody, Checksum new_cksum) { + pvno = Krb5.PVNO; + msgType = Krb5.KRB_SAFE; + safeBody = new_safeBody; + cksum = new_cksum; + } + + public KRBSafe(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + } + + public KRBSafe(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + } + + /** + * Initializes an KRBSafe object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while parsing a Realm object. + * @exception KrbApErrException if the value read from the DER-encoded data + * stream does not match the pre-defined value. + */ + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + DerValue der, subDer; + if (((encoding.getTag() & (byte)0x1F) != (byte)0x14) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x00) { + pvno = subDer.getData().getBigInteger().intValue(); + if (pvno != Krb5.PVNO) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & 0x1F) == 0x01) { + msgType = subDer.getData().getBigInteger().intValue(); + if (msgType != Krb5.KRB_SAFE) + throw new KrbApErrException(Krb5.KRB_AP_ERR_MSG_TYPE); + } + + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + safeBody = KRBSafeBody.parse(der.getData(), (byte)0x02, false); + cksum = Checksum.parse(der.getData(), (byte)0x03, false); + if (der.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an KRBSafe object. + * @return byte array of encoded KRBSafe object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream temp = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pvno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(msgType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), safeBody.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), cksum.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x14), temp); + return bytes.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/KRBSafeBody.java b/src/sun/security/krb5/internal/KRBSafeBody.java new file mode 100644 index 00000000..ae21e877 --- /dev/null +++ b/src/sun/security/krb5/internal/KRBSafeBody.java @@ -0,0 +1,177 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; + +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 KRBSafeBody type. + * + *

    + * KRB-SAFE-BODY ::= SEQUENCE { + * user-data [0] OCTET STRING, + * timestamp [1] KerberosTime OPTIONAL, + * usec [2] Microseconds OPTIONAL, + * seq-number [3] UInt32 OPTIONAL, + * s-address [4] HostAddress, + * r-address [5] HostAddress OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KRBSafeBody { + public byte[] userData = null; + public KerberosTime timestamp; //optional + public Integer usec; //optional + public Integer seqNumber; //optional + public HostAddress sAddress; + public HostAddress rAddress; //optional + + public KRBSafeBody( + byte[] new_userData, + KerberosTime new_timestamp, + Integer new_usec, + Integer new_seqNumber, + HostAddress new_sAddress, + HostAddress new_rAddress + ) { + if (new_userData != null) { + userData = new_userData.clone(); + } + timestamp = new_timestamp; + usec = new_usec; + seqNumber = new_seqNumber; + sAddress = new_sAddress; + rAddress = new_rAddress; + } + + + /** + * Constructs a KRBSafeBody object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public KRBSafeBody(DerValue encoding) throws Asn1Exception, IOException { + DerValue der; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + userData = der.getData().getOctetString(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + timestamp = KerberosTime.parse(encoding.getData(), (byte)0x01, true); + if ((encoding.getData().peekByte() & 0x1F) == 0x02) { + der = encoding.getData().getDerValue(); + usec = new Integer(der.getData().getBigInteger().intValue()); + } + if ((encoding.getData().peekByte() & 0x1F) == 0x03) { + der = encoding.getData().getDerValue(); + seqNumber = new Integer(der.getData().getBigInteger().intValue()); + } + sAddress = HostAddress.parse(encoding.getData(), (byte)0x04, false); + if (encoding.getData().available() > 0) + rAddress = HostAddress.parse(encoding.getData(), (byte)0x05, true); + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an KRBSafeBody object. + * @return the byte array of encoded KRBSafeBody object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putOctetString(userData); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + if (timestamp != null) + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), timestamp.asn1Encode()); + if (usec != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(usec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), temp); + } + if (seqNumber != null) { + temp = new DerOutputStream(); + // encode as an unsigned integer (UInt32) + temp.putInteger(BigInteger.valueOf(seqNumber.longValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp); + } + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), sAddress.asn1Encode()); + if (rAddress != null) + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Parse (unmarshal) a KRBSafeBody from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of KRBSafeBody. + * + */ + public static KRBSafeBody parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + else { + DerValue subDer = der.getData().getDerValue(); + return new KRBSafeBody(subDer); + } + } + + + +} diff --git a/src/sun/security/krb5/internal/KdcErrException.java b/src/sun/security/krb5/internal/KdcErrException.java new file mode 100644 index 00000000..c55670f4 --- /dev/null +++ b/src/sun/security/krb5/internal/KdcErrException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +public class KdcErrException extends sun.security.krb5.KrbException { + + private static final long serialVersionUID = -8788186031117310306L; + + public KdcErrException(int i) { + super(i); + } + + public KdcErrException(int i, String s) { + super(i, s); + } +} diff --git a/src/sun/security/krb5/internal/KerberosTime.java b/src/sun/security/krb5/internal/KerberosTime.java new file mode 100644 index 00000000..586bceff --- /dev/null +++ b/src/sun/security/krb5/internal/KerberosTime.java @@ -0,0 +1,308 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.Config; +import sun.security.krb5.KrbException; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +import java.io.IOException; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * Implements the ASN.1 KerberosTime type. This is an immutable class. + * + *

    + * KerberosTime ::= GeneralizedTime -- with no fractional seconds + * + * + * The timestamps used in Kerberos are encoded as GeneralizedTimes. A + * KerberosTime value shall not include any fractional portions of the + * seconds. As required by the DER, it further shall not include any + * separators, and it shall specify the UTC time zone (Z). + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + * + * The implementation also includes the microseconds info so that the + * same class can be used as a precise timestamp in Authenticator etc. + */ + +public class KerberosTime { + + private final long kerberosTime; // milliseconds since epoch, Date.getTime() + private final int microSeconds; // last 3 digits of the real microsecond + + // The time when this class is loaded. Used in setNow() + private static long initMilli = System.currentTimeMillis(); + private static long initMicro = System.nanoTime() / 1000; + + private static boolean DEBUG = Krb5.DEBUG; + + // Do not make this public. It's a little confusing that micro + // is only the last 3 digits of microsecond. + private KerberosTime(long time, int micro) { + kerberosTime = time; + microSeconds = micro; + } + + /** + * Creates a KerberosTime object from milliseconds since epoch. + */ + public KerberosTime(long time) { + this(time, 0); + } + + // This constructor is used in the native code + // src/windows/native/sun/security/krb5/NativeCreds.c + public KerberosTime(String time) throws Asn1Exception { + this(toKerberosTime(time), 0); + } + + private static long toKerberosTime(String time) throws Asn1Exception { + // ASN.1 GeneralizedTime format: + + // "19700101000000Z" + // | | | | | | | + // 0 4 6 8 | | | + // 10 | | + // 12 | + // 14 + + if (time.length() != 15) + throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); + if (time.charAt(14) != 'Z') + throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); + int year = Integer.parseInt(time.substring(0, 4)); + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.clear(); // so that millisecond is zero + calendar.set(year, + Integer.parseInt(time.substring(4, 6)) - 1, + Integer.parseInt(time.substring(6, 8)), + Integer.parseInt(time.substring(8, 10)), + Integer.parseInt(time.substring(10, 12)), + Integer.parseInt(time.substring(12, 14))); + return calendar.getTimeInMillis(); + } + + /** + * Creates a KerberosTime object from a Date object. + */ + public KerberosTime(Date time) { + this(time.getTime(), 0); + } + + /** + * Creates a KerberosTime object for now. It uses System.nanoTime() + * to get a more precise time than "new Date()". + */ + public static KerberosTime now() { + long newMilli = System.currentTimeMillis(); + long newMicro = System.nanoTime() / 1000; + long microElapsed = newMicro - initMicro; + long calcMilli = initMilli + microElapsed/1000; + if (calcMilli - newMilli > 100 || newMilli - calcMilli > 100) { + if (DEBUG) { + System.out.println("System time adjusted"); + } + initMilli = newMilli; + initMicro = newMicro; + return new KerberosTime(newMilli, 0); + } else { + return new KerberosTime(calcMilli, (int)(microElapsed % 1000)); + } + } + + /** + * Returns a string representation of KerberosTime object. + * @return a string representation of this object. + */ + public String toGeneralizedTimeString() { + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.clear(); + + calendar.setTimeInMillis(kerberosTime); + return String.format("%04d%02d%02d%02d%02d%02dZ", + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH), + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + calendar.get(Calendar.SECOND)); + } + + /** + * Encodes this object to a byte array. + * @return a byte array of encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream out = new DerOutputStream(); + out.putGeneralizedTime(this.toDate()); + return out.toByteArray(); + } + + public long getTime() { + return kerberosTime; + } + + public Date toDate() { + return new Date(kerberosTime); + } + + public int getMicroSeconds() { + Long temp_long = new Long((kerberosTime % 1000L) * 1000L); + return temp_long.intValue() + microSeconds; + } + + /** + * Returns a new KerberosTime object with the original seconds + * and the given microseconds. + */ + public KerberosTime withMicroSeconds(int usec) { + return new KerberosTime( + kerberosTime - kerberosTime%1000L + usec/1000L, + usec%1000); + } + + private boolean inClockSkew(int clockSkew) { + return Math.abs(kerberosTime - System.currentTimeMillis()) + <= clockSkew * 1000L; + } + + public boolean inClockSkew() { + return inClockSkew(getDefaultSkew()); + } + + public boolean greaterThanWRTClockSkew(KerberosTime time, int clockSkew) { + if ((kerberosTime - time.kerberosTime) > clockSkew * 1000L) + return true; + return false; + } + + public boolean greaterThanWRTClockSkew(KerberosTime time) { + return greaterThanWRTClockSkew(time, getDefaultSkew()); + } + + public boolean greaterThan(KerberosTime time) { + return kerberosTime > time.kerberosTime || + kerberosTime == time.kerberosTime && + microSeconds > time.microSeconds; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof KerberosTime)) { + return false; + } + + return kerberosTime == ((KerberosTime)obj).kerberosTime && + microSeconds == ((KerberosTime)obj).microSeconds; + } + + public int hashCode() { + int result = 37 * 17 + (int)(kerberosTime ^ (kerberosTime >>> 32)); + return result * 17 + microSeconds; + } + + public boolean isZero() { + return kerberosTime == 0 && microSeconds == 0; + } + + public int getSeconds() { + Long temp_long = new Long(kerberosTime / 1000L); + return temp_long.intValue(); + } + + /** + * Parse (unmarshal) a kerberostime from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains + * one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of KerberosTime. + * + */ + public static KerberosTime parse( + DerInputStream data, byte explicitTag, boolean optional) + throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + Date temp = subDer.getGeneralizedTime(); + return new KerberosTime(temp.getTime(), 0); + } + } + + public static int getDefaultSkew() { + int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; + try { + if ((tdiff = Config.getInstance().getIntValue( + "libdefaults", "clockskew")) + == Integer.MIN_VALUE) { //value is not defined + tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; + } + } catch (KrbException e) { + if (DEBUG) { + System.out.println("Exception in getting clockskew from " + + "Configuration " + + "using default value " + + e.getMessage()); + } + } + return tdiff; + } + + public String toString() { + return toGeneralizedTimeString(); + } +} diff --git a/src/sun/security/krb5/internal/Krb5.java b/src/sun/security/krb5/internal/Krb5.java new file mode 100644 index 00000000..2be21dc7 --- /dev/null +++ b/src/sun/security/krb5/internal/Krb5.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import java.util.Hashtable; + +// Constants and other defined values from RFC 4120 + +public class Krb5 { + + //Recommended KDC values + public static final int DEFAULT_ALLOWABLE_CLOCKSKEW = 5 * 60; //5 minutes + public static final int DEFAULT_MINIMUM_LIFETIME = 5 * 60; //5 minutes + public static final int DEFAULT_MAXIMUM_RENEWABLE_LIFETIME = 7 * 24 * 60 * 60; //1 week + public static final int DEFAULT_MAXIMUM_TICKET_LIFETIME = 24 * 60 * 60; //1 day + public static final boolean DEFAULT_FORWARDABLE_ALLOWED = true; + public static final boolean DEFAULT_PROXIABLE_ALLOWED = true; + public static final boolean DEFAULT_POSTDATE_ALLOWED = true; + public static final boolean DEFAULT_RENEWABLE_ALLOWED = true; + public static final boolean AP_EMPTY_ADDRESSES_ALLOWED = true; + + //AP_REQ Options + + public static final int AP_OPTS_RESERVED = 0; + public static final int AP_OPTS_USE_SESSION_KEY = 1; + public static final int AP_OPTS_MUTUAL_REQUIRED = 2; + public static final int AP_OPTS_MAX = 31; + + //Ticket Flags + + public static final int TKT_OPTS_RESERVED = 0; + public static final int TKT_OPTS_FORWARDABLE = 1; + public static final int TKT_OPTS_FORWARDED = 2; + public static final int TKT_OPTS_PROXIABLE = 3; + public static final int TKT_OPTS_PROXY = 4; + public static final int TKT_OPTS_MAY_POSTDATE = 5; + public static final int TKT_OPTS_POSTDATED = 6; + public static final int TKT_OPTS_INVALID = 7; + public static final int TKT_OPTS_RENEWABLE = 8; + public static final int TKT_OPTS_INITIAL = 9; + public static final int TKT_OPTS_PRE_AUTHENT = 10; + public static final int TKT_OPTS_HW_AUTHENT = 11; + public static final int TKT_OPTS_DELEGATE = 13; + public static final int TKT_OPTS_MAX = 31; + + // KDC Options + // (option values defined in KDCOptions.java) + public static final int KDC_OPTS_MAX = 31; + + // KerberosFlags + public static final int KRB_FLAGS_MAX = 31; + + //Last Request types + + public static final int LRTYPE_NONE = 0; + public static final int LRTYPE_TIME_OF_INITIAL_TGT = 1; + public static final int LRTYPE_TIME_OF_INITIAL_REQ = 2; + public static final int LRTYPE_TIME_OF_NEWEST_TGT = 3; + public static final int LRTYPE_TIME_OF_LAST_RENEWAL = 4; + public static final int LRTYPE_TIME_OF_LAST_REQ = 5; + + //Host address lengths + + public static final int ADDR_LEN_INET = 4; + public static final int ADDR_LEN_CHAOS = 2; + public static final int ADDR_LEN_OSI = 0; //means variable + public static final int ADDR_LEN_XNS = 6; + public static final int ADDR_LEN_APPLETALK = 3; + public static final int ADDR_LEN_DECNET = 2; + + //Host address types + + public static final int ADDRTYPE_UNIX = 1; // Local + public static final int ADDRTYPE_INET = 2; // Internet + public static final int ADDRTYPE_IMPLINK = 3; // Arpanet + public static final int ADDRTYPE_PUP = 4; // PUP + public static final int ADDRTYPE_CHAOS = 5; // CHAOS + public static final int ADDRTYPE_XNS = 6; // XEROX Network Services + public static final int ADDRTYPE_IPX = 6; // IPX + public static final int ADDRTYPE_ISO = 7; // ISO + public static final int ADDRTYPE_ECMA = 8; // European Computer Manufacturers + public static final int ADDRTYPE_DATAKIT = 9; // Datakit + public static final int ADDRTYPE_CCITT = 10; // CCITT + public static final int ADDRTYPE_SNA = 11; // SNA + public static final int ADDRTYPE_DECNET = 12; // DECnet + public static final int ADDRTYPE_DLI = 13; // Direct Data Link Interface + public static final int ADDRTYPE_LAT = 14; // LAT + public static final int ADDRTYPE_HYLINK = 15; // NSC Hyperchannel + public static final int ADDRTYPE_APPLETALK = 16; // AppleTalk + public static final int ADDRTYPE_NETBIOS = 17; // NetBios + public static final int ADDRTYPE_VOICEVIEW = 18; // VoiceView + public static final int ADDRTYPE_FIREFOX = 19; // Firefox + public static final int ADDRTYPE_BAN = 21; // Banyan + public static final int ADDRTYPE_ATM = 22; // ATM + public static final int ADDRTYPE_INET6 = 24; // Internet Protocol V6 + + //IP Transport UDP Port for KDC Messages + + public static final int KDC_INET_DEFAULT_PORT = 88; + + // number of retries before giving up + + public static final int KDC_RETRY_LIMIT = 3; + public static final int KDC_DEFAULT_UDP_PREF_LIMIT = 1465; + public static final int KDC_HARD_UDP_LIMIT = 32700; + + //OSI authentication mechanism OID + + //public static final int[] OSI_AUTH_MECH_TYPE = { /*iso*/ 1, /*org*/ 3, + // /*dod*/ 5, /*internet*/ 1, /*security*/ 5, /*kerberosv5*/ 2 }; + + //Protocol constants and associated values + + //Key Types + public static final int KEYTYPE_NULL = 0; + public static final int KEYTYPE_DES = 1; + + public static final int KEYTYPE_DES3 = 2; + public static final int KEYTYPE_AES = 3; + public static final int KEYTYPE_ARCFOUR_HMAC = 4; + + + //----------------------------------------+----------------- + // padata type |padata-type value + //----------------------------------------+----------------- + public static final int PA_TGS_REQ = 1; + public static final int PA_ENC_TIMESTAMP = 2; + public static final int PA_PW_SALT = 3; + + // new preauth types + public static final int PA_ETYPE_INFO = 11; + public static final int PA_ETYPE_INFO2 = 19; + + // S4U2user info + public static final int PA_FOR_USER = 129; + + //-------------------------------+------------- + //authorization data type |ad-type value + //-------------------------------+------------- + //reserved values 0-63 + public static final int OSF_DCE = 64; + public static final int SESAME = 65; + + //----------------------------------------------+----------------- + //alternate authentication type |method-type value + //----------------------------------------------+----------------- + // reserved values 0-63 + public static final int ATT_CHALLENGE_RESPONSE = 64; + + //--------------------------------------------+------------- + //transited encoding type |tr-type value + //--------------------------------------------+------------- + public static final int DOMAIN_X500_COMPRESS = 1; + // reserved values all others + + //----------------------------+-------+----------------------------------------- + // Label |Value |Meaning + //----------------------------+-------+----------------------------------------- + public static final int PVNO = 5; // current Kerberos protocol version number + public static final int AUTHNETICATOR_VNO = 5; // current authenticator version number + public static final int TICKET_VNO = 5; // current ticket version number + + //message types + + // there are several message sub-components not included here + public static final int KRB_AS_REQ = 10; //Request for initial authentication + public static final int KRB_AS_REP = 11; //Response to KRB_AS_REQ request + public static final int KRB_TGS_REQ = 12; //Request for authentication based on TGT + public static final int KRB_TGS_REP = 13; //Response to KRB_TGS_REQ request + public static final int KRB_AP_REQ = 14; //application request to server + public static final int KRB_AP_REP = 15; //Response to KRB_AP_REQ_MUTUAL + public static final int KRB_SAFE = 20; //Safe (checksummed) application message + public static final int KRB_PRIV = 21; //Private (encrypted) application message + public static final int KRB_CRED = 22; //Private (encrypted) message to forward credentials + public static final int KRB_ERROR = 30; //Error response + + //message component types + + public static final int KRB_TKT = 1; //Ticket + public static final int KRB_AUTHENTICATOR = 2; //Authenticator + public static final int KRB_ENC_TKT_PART = 3; //Encrypted ticket part + public static final int KRB_ENC_AS_REP_PART = 25; //Encrypted initial authentication part + public static final int KRB_ENC_TGS_REP_PART = 26; //Encrypted TGS request part + public static final int KRB_ENC_AP_REP_PART = 27; //Encrypted application request part + public static final int KRB_ENC_KRB_PRIV_PART = 28; //Encrypted application message part + public static final int KRB_ENC_KRB_CRED_PART = 29; //Encrypted credentials forward part + + + //error codes + + public static final int KDC_ERR_NONE = 0; //No error + public static final int KDC_ERR_NAME_EXP = 1; //Client's entry in database expired + public static final int KDC_ERR_SERVICE_EXP = 2; //Server's entry in database has expired + public static final int KDC_ERR_BAD_PVNO = 3; //Requested protocol version number not supported + public static final int KDC_ERR_C_OLD_MAST_KVNO = 4; //Client's key encrypted in old master key + public static final int KDC_ERR_S_OLD_MAST_KVNO = 5; //Server's key encrypted in old master key + public static final int KDC_ERR_C_PRINCIPAL_UNKNOWN = 6; //Client not found in Kerberos database + public static final int KDC_ERR_S_PRINCIPAL_UNKNOWN = 7; //Server not found in Kerberos database + public static final int KDC_ERR_PRINCIPAL_NOT_UNIQUE = 8; //Multiple principal entries in database + public static final int KDC_ERR_NULL_KEY = 9; //The client or server has a null key + public static final int KDC_ERR_CANNOT_POSTDATE = 10; //Ticket not eligible for postdating + public static final int KDC_ERR_NEVER_VALID = 11; //Requested start time is later than end time + public static final int KDC_ERR_POLICY = 12; //KDC policy rejects request + public static final int KDC_ERR_BADOPTION = 13; //KDC cannot accommodate requested option + public static final int KDC_ERR_ETYPE_NOSUPP = 14; //KDC has no support for encryption type + public static final int KDC_ERR_SUMTYPE_NOSUPP = 15; //KDC has no support for checksum type + public static final int KDC_ERR_PADATA_TYPE_NOSUPP = 16; //KDC has no support for padata type + public static final int KDC_ERR_TRTYPE_NOSUPP = 17; //KDC has no support for transited type + public static final int KDC_ERR_CLIENT_REVOKED = 18; //Clients credentials have been revoked + public static final int KDC_ERR_SERVICE_REVOKED = 19; //Credentials for server have been revoked + public static final int KDC_ERR_TGT_REVOKED = 20; //TGT has been revoked + public static final int KDC_ERR_CLIENT_NOTYET = 21; //Client not yet valid - try again later + public static final int KDC_ERR_SERVICE_NOTYET = 22; //Server not yet valid - try again later + public static final int KDC_ERR_KEY_EXPIRED = 23; //Password has expired - change password to reset + public static final int KDC_ERR_PREAUTH_FAILED = 24; //Pre-authentication information was invalid + public static final int KDC_ERR_PREAUTH_REQUIRED = 25; //Additional pre-authentication required + public static final int KRB_AP_ERR_BAD_INTEGRITY = 31; //Integrity check on decrypted field failed + public static final int KRB_AP_ERR_TKT_EXPIRED = 32; //Ticket expired + public static final int KRB_AP_ERR_TKT_NYV = 33; //Ticket not yet valid + public static final int KRB_AP_ERR_REPEAT = 34; //Request is a replay + public static final int KRB_AP_ERR_NOT_US = 35; //The ticket isn't for us + public static final int KRB_AP_ERR_BADMATCH = 36; //Ticket and authenticator don't match + public static final int KRB_AP_ERR_SKEW = 37; //Clock skew too great + public static final int KRB_AP_ERR_BADADDR = 38; //Incorrect net address + public static final int KRB_AP_ERR_BADVERSION = 39; //Protocol version mismatch + public static final int KRB_AP_ERR_MSG_TYPE = 40; //Invalid msg type + public static final int KRB_AP_ERR_MODIFIED = 41; //Message stream modified + public static final int KRB_AP_ERR_BADORDER = 42; //Message out of order + public static final int KRB_AP_ERR_BADKEYVER = 44; //Specified version of key is not available + public static final int KRB_AP_ERR_NOKEY = 45; //Service key not available + public static final int KRB_AP_ERR_MUT_FAIL = 46; //Mutual authentication failed + public static final int KRB_AP_ERR_BADDIRECTION = 47; //Incorrect message direction + public static final int KRB_AP_ERR_METHOD = 48; //Alternative authentication method required + public static final int KRB_AP_ERR_BADSEQ = 49; //Incorrect sequence number in message + public static final int KRB_AP_ERR_INAPP_CKSUM = 50; //Inappropriate type of checksum in message + public static final int KRB_ERR_RESPONSE_TOO_BIG = 52; //Response too big for UDP, retry with TCP + public static final int KRB_ERR_GENERIC = 60; //Generic error (description in e-text) + public static final int KRB_ERR_FIELD_TOOLONG = 61; //Field is too long for this implementation + public static final int KRB_CRYPTO_NOT_SUPPORT = 100; //Client does not support this crypto type + public static final int KRB_AP_ERR_NOREALM = 62; + public static final int KRB_AP_ERR_GEN_CRED = 63; + // public static final int KRB_AP_ERR_CKSUM_NOKEY =101; //Lack of the key to generate the checksum + // error codes specific to this implementation + public static final int KRB_AP_ERR_REQ_OPTIONS = 101; //Invalid TGS_REQ + public static final int API_INVALID_ARG = 400; //Invalid argument + + public static final int BITSTRING_SIZE_INVALID = 500; //BitString size does not match input byte array + public static final int BITSTRING_INDEX_OUT_OF_BOUNDS = 501; //BitString bit index does not fall within size + public static final int BITSTRING_BAD_LENGTH = 502; //BitString length is wrong for the expected type + + public static final int REALM_ILLCHAR = 600; //Illegal character in realm name; one of: '/', ':', '\0' + public static final int REALM_NULL = 601; //Null realm name + + public static final int ASN1_BAD_TIMEFORMAT = 900; //Input not in GeneralizedTime format + public static final int ASN1_MISSING_FIELD = 901; //Structure is missing a required field + public static final int ASN1_MISPLACED_FIELD = 902; //Unexpected field number + public static final int ASN1_TYPE_MISMATCH = 903; //Type numbers are inconsistent + public static final int ASN1_OVERFLOW = 904; //Value too large + public static final int ASN1_OVERRUN = 905; //Encoding ended unexpectedly + public static final int ASN1_BAD_ID = 906; //Identifier doesn't match expected value + public static final int ASN1_BAD_LENGTH = 907; //Length doesn't match expected value + public static final int ASN1_BAD_FORMAT = 908; //Badly-formatted encoding + public static final int ASN1_PARSE_ERROR = 909; //Parse error + public static final int ASN1_BAD_CLASS = 910; //Bad class number + public static final int ASN1_BAD_TYPE = 911; //Bad type number + public static final int ASN1_BAD_TAG = 912; //Bad tag number + public static final int ASN1_UNSUPPORTED_TYPE = 913; //Unsupported ASN.1 type encountered + public static final int ASN1_CANNOT_ENCODE = 914; //Encoding failed due to invalid parameter(s) + + private static Hashtable errMsgList; + + public static String getErrorMessage(int i) { + return errMsgList.get(i); + } + + + public static final boolean DEBUG = + java.security.AccessController.doPrivileged( + new sun.security.action.GetBooleanAction("sun.security.krb5.debug")); + public static final sun.misc.HexDumpEncoder hexDumper = + new sun.misc.HexDumpEncoder(); + + static { + errMsgList = new Hashtable (); + errMsgList.put(KDC_ERR_NONE, "No error"); + errMsgList.put(KDC_ERR_NAME_EXP, "Client's entry in database expired"); + errMsgList.put(KDC_ERR_SERVICE_EXP, "Server's entry in database has expired"); + errMsgList.put(KDC_ERR_BAD_PVNO, "Requested protocol version number not supported"); + errMsgList.put(KDC_ERR_C_OLD_MAST_KVNO, "Client's key encrypted in old master key"); + errMsgList.put(KDC_ERR_S_OLD_MAST_KVNO, "Server's key encrypted in old master key"); + errMsgList.put(KDC_ERR_C_PRINCIPAL_UNKNOWN, "Client not found in Kerberos database"); + errMsgList.put(KDC_ERR_S_PRINCIPAL_UNKNOWN, "Server not found in Kerberos database"); + errMsgList.put(KDC_ERR_PRINCIPAL_NOT_UNIQUE, "Multiple principal entries in database"); + errMsgList.put(KDC_ERR_NULL_KEY, "The client or server has a null key"); + errMsgList.put(KDC_ERR_CANNOT_POSTDATE, "Ticket not eligible for postdating"); + errMsgList.put(KDC_ERR_NEVER_VALID, "Requested start time is later than end time"); + errMsgList.put(KDC_ERR_POLICY, "KDC policy rejects request"); + errMsgList.put(KDC_ERR_BADOPTION, "KDC cannot accommodate requested option"); + errMsgList.put(KDC_ERR_ETYPE_NOSUPP, "KDC has no support for encryption type"); + errMsgList.put(KDC_ERR_SUMTYPE_NOSUPP, "KDC has no support for checksum type"); + errMsgList.put(KDC_ERR_PADATA_TYPE_NOSUPP, "KDC has no support for padata type"); + errMsgList.put(KDC_ERR_TRTYPE_NOSUPP, "KDC has no support for transited type"); + errMsgList.put(KDC_ERR_CLIENT_REVOKED, "Clients credentials have been revoked"); + errMsgList.put(KDC_ERR_SERVICE_REVOKED, "Credentials for server have been revoked"); + errMsgList.put(KDC_ERR_TGT_REVOKED, "TGT has been revoked"); + errMsgList.put(KDC_ERR_CLIENT_NOTYET, "Client not yet valid - try again later"); + errMsgList.put(KDC_ERR_SERVICE_NOTYET, "Server not yet valid - try again later"); + errMsgList.put(KDC_ERR_KEY_EXPIRED, "Password has expired - change password to reset"); + errMsgList.put(KDC_ERR_PREAUTH_FAILED, "Pre-authentication information was invalid"); + errMsgList.put(KDC_ERR_PREAUTH_REQUIRED, "Additional pre-authentication required"); + errMsgList.put(KRB_AP_ERR_BAD_INTEGRITY, "Integrity check on decrypted field failed"); + errMsgList.put(KRB_AP_ERR_TKT_EXPIRED, "Ticket expired"); + errMsgList.put(KRB_AP_ERR_TKT_NYV, "Ticket not yet valid"); + errMsgList.put(KRB_AP_ERR_REPEAT, "Request is a replay"); + errMsgList.put(KRB_AP_ERR_NOT_US, "The ticket isn't for us"); + errMsgList.put(KRB_AP_ERR_BADMATCH, "Ticket and authenticator don't match"); + errMsgList.put(KRB_AP_ERR_SKEW, "Clock skew too great"); + errMsgList.put(KRB_AP_ERR_BADADDR, "Incorrect net address"); + errMsgList.put(KRB_AP_ERR_BADVERSION, "Protocol version mismatch"); + errMsgList.put(KRB_AP_ERR_MSG_TYPE, "Invalid msg type"); + errMsgList.put(KRB_AP_ERR_MODIFIED, "Message stream modified"); + errMsgList.put(KRB_AP_ERR_BADORDER, "Message out of order"); + errMsgList.put(KRB_AP_ERR_BADKEYVER, "Specified version of key is not available"); + errMsgList.put(KRB_AP_ERR_NOKEY, "Service key not available"); + errMsgList.put(KRB_AP_ERR_MUT_FAIL, "Mutual authentication failed"); + errMsgList.put(KRB_AP_ERR_BADDIRECTION, "Incorrect message direction"); + errMsgList.put(KRB_AP_ERR_METHOD, "Alternative authentication method required"); + errMsgList.put(KRB_AP_ERR_BADSEQ, "Incorrect sequence number in message"); + errMsgList.put(KRB_AP_ERR_INAPP_CKSUM, "Inappropriate type of checksum in message"); + errMsgList.put(KRB_ERR_RESPONSE_TOO_BIG, "Response too big for UDP, retry with TCP"); + errMsgList.put(KRB_ERR_GENERIC, "Generic error (description in e-text)"); + errMsgList.put(KRB_ERR_FIELD_TOOLONG, "Field is too long for this implementation"); + errMsgList.put(KRB_AP_ERR_NOREALM, "Realm name not available"); //used in setDefaultCreds() in sun.security.krb5.Credentials + + // error messages specific to this implementation + + errMsgList.put(API_INVALID_ARG, "Invalid argument"); + + errMsgList.put(BITSTRING_SIZE_INVALID, "BitString size does not match input byte array"); + errMsgList.put(BITSTRING_INDEX_OUT_OF_BOUNDS, "BitString bit index does not fall within size"); + errMsgList.put(BITSTRING_BAD_LENGTH, "BitString length is wrong for the expected type"); + + errMsgList.put(REALM_ILLCHAR, "Illegal character in realm name; one of: '/', ':', '\0'"); + errMsgList.put(REALM_NULL, "Null realm name"); + + errMsgList.put(ASN1_BAD_TIMEFORMAT, "Input not in GeneralizedTime format"); + errMsgList.put(ASN1_MISSING_FIELD, "Structure is missing a required field"); + errMsgList.put(ASN1_MISPLACED_FIELD, "Unexpected field number"); + errMsgList.put(ASN1_TYPE_MISMATCH, "Type numbers are inconsistent"); + errMsgList.put(ASN1_OVERFLOW, "Value too large"); + errMsgList.put(ASN1_OVERRUN, "Encoding ended unexpectedly"); + errMsgList.put(ASN1_BAD_ID, "Identifier doesn't match expected value"); + errMsgList.put(ASN1_BAD_LENGTH, "Length doesn't match expected value"); + errMsgList.put(ASN1_BAD_FORMAT, "Badly-formatted encoding"); + errMsgList.put(ASN1_PARSE_ERROR, "Parse error"); + errMsgList.put(ASN1_BAD_CLASS, "Bad class number"); + errMsgList.put(ASN1_BAD_TYPE, "Bad type number"); + errMsgList.put(ASN1_BAD_TAG, "Bad tag number"); + errMsgList.put(ASN1_UNSUPPORTED_TYPE, "Unsupported ASN.1 type encountered"); + errMsgList.put(ASN1_CANNOT_ENCODE, "Encoding failed due to invalid parameter(s)"); + errMsgList.put(KRB_CRYPTO_NOT_SUPPORT, "Client has no support for crypto type"); + errMsgList.put(KRB_AP_ERR_REQ_OPTIONS, "Invalid option setting in ticket request."); + errMsgList.put(KRB_AP_ERR_GEN_CRED, "Fail to create credential."); + } + +} diff --git a/src/sun/security/krb5/internal/KrbApErrException.java b/src/sun/security/krb5/internal/KrbApErrException.java new file mode 100644 index 00000000..04048cb7 --- /dev/null +++ b/src/sun/security/krb5/internal/KrbApErrException.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2000, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +public class KrbApErrException extends sun.security.krb5.KrbException { + + private static final long serialVersionUID = 7545264413323118315L; + + public KrbApErrException(int i) { + super(i); + } + + public KrbApErrException(int i, String s) { + super(i, s); + } + +} diff --git a/src/sun/security/krb5/internal/KrbCredInfo.java b/src/sun/security/krb5/internal/KrbCredInfo.java new file mode 100644 index 00000000..18fa7412 --- /dev/null +++ b/src/sun/security/krb5/internal/KrbCredInfo.java @@ -0,0 +1,201 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.util.Vector; +import java.io.IOException; + +/** + * Implements the ASN.1 KrbCredInfo type. + * + *

    + * KrbCredInfo ::= SEQUENCE { + * key [0] EncryptionKey, + * prealm [1] Realm OPTIONAL, + * pname [2] PrincipalName OPTIONAL, + * flags [3] TicketFlags OPTIONAL, + * authtime [4] KerberosTime OPTIONAL, + * starttime [5] KerberosTime OPTIONAL, + * endtime [6] KerberosTime OPTIONAL, + * renew-till [7] KerberosTime OPTIONAL, + * srealm [8] Realm OPTIONAL, + * sname [9] PrincipalName OPTIONAL, + * caddr [10] HostAddresses OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class KrbCredInfo { + public EncryptionKey key; + public PrincipalName pname; //optional + public TicketFlags flags; //optional + public KerberosTime authtime; //optional + public KerberosTime starttime; //optional + public KerberosTime endtime; //optional + public KerberosTime renewTill; //optional + public PrincipalName sname; //optional + public HostAddresses caddr; //optional + + private KrbCredInfo() { + } + + public KrbCredInfo( + EncryptionKey new_key, + PrincipalName new_pname, + TicketFlags new_flags, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + PrincipalName new_sname, + HostAddresses new_caddr + ) { + key = new_key; + pname = new_pname; + flags = new_flags; + authtime = new_authtime; + starttime = new_starttime; + endtime = new_endtime; + renewTill = new_renewTill; + sname = new_sname; + caddr = new_caddr; + } + + /** + * Constructs a KrbCredInfo object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + public KrbCredInfo(DerValue encoding) + throws Asn1Exception, IOException, RealmException{ + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + pname = null; + flags = null; + authtime = null; + starttime = null; + endtime = null; + renewTill = null; + sname = null; + caddr = null; + key = EncryptionKey.parse(encoding.getData(), (byte)0x00, false); + Realm prealm = null, srealm = null; + if (encoding.getData().available() > 0) + prealm = Realm.parse(encoding.getData(), (byte)0x01, true); + if (encoding.getData().available() > 0) + pname = PrincipalName.parse(encoding.getData(), (byte)0x02, true, prealm); + if (encoding.getData().available() > 0) + flags = TicketFlags.parse(encoding.getData(), (byte)0x03, true); + if (encoding.getData().available() > 0) + authtime = KerberosTime.parse(encoding.getData(), (byte)0x04, true); + if (encoding.getData().available() > 0) + starttime = KerberosTime.parse(encoding.getData(), (byte)0x05, true); + if (encoding.getData().available() > 0) + endtime = KerberosTime.parse(encoding.getData(), (byte)0x06, true); + if (encoding.getData().available() > 0) + renewTill = KerberosTime.parse(encoding.getData(), (byte)0x07, true); + if (encoding.getData().available() > 0) + srealm = Realm.parse(encoding.getData(), (byte)0x08, true); + if (encoding.getData().available() > 0) + sname = PrincipalName.parse(encoding.getData(), (byte)0x09, true, srealm); + if (encoding.getData().available() > 0) + caddr = HostAddresses.parse(encoding.getData(), (byte)0x0A, true); + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an KrbCredInfo object. + * @return the byte array of encoded KrbCredInfo object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + Vector v = new Vector<>(); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), key.asn1Encode())); + if (pname != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), pname.getRealm().asn1Encode())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), pname.asn1Encode())); + } + if (flags != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), flags.asn1Encode())); + if (authtime != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x04), authtime.asn1Encode())); + if (starttime != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x05), starttime.asn1Encode())); + if (endtime != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x06), endtime.asn1Encode())); + if (renewTill != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x07), renewTill.asn1Encode())); + if (sname != null) { + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x08), sname.getRealm().asn1Encode())); + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x09), sname.asn1Encode())); + } + if (caddr != null) + v.addElement(new DerValue(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0A), caddr.asn1Encode())); + DerValue der[] = new DerValue[v.size()]; + v.copyInto(der); + DerOutputStream out = new DerOutputStream(); + out.putSequence(der); + return out.toByteArray(); + } + + public Object clone() { + KrbCredInfo kcred = new KrbCredInfo(); + kcred.key = (EncryptionKey)key.clone(); + // optional fields + if (pname != null) + kcred.pname = (PrincipalName)pname.clone(); + if (flags != null) + kcred.flags = (TicketFlags)flags.clone(); + kcred.authtime = authtime; + kcred.starttime = starttime; + kcred.endtime = endtime; + kcred.renewTill = renewTill; + if (sname != null) + kcred.sname = (PrincipalName)sname.clone(); + if (caddr != null) + kcred.caddr = (HostAddresses)caddr.clone(); + return kcred; + } + +} diff --git a/src/sun/security/krb5/internal/KrbErrException.java b/src/sun/security/krb5/internal/KrbErrException.java new file mode 100644 index 00000000..62e84959 --- /dev/null +++ b/src/sun/security/krb5/internal/KrbErrException.java @@ -0,0 +1,45 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +public class KrbErrException extends sun.security.krb5.KrbException { + + private static final long serialVersionUID = 2186533836785448317L; + + public KrbErrException(int i) { + super(i); + } + + public KrbErrException(int i, String s) { + super(i, s); + } + +} diff --git a/src/sun/security/krb5/internal/LastReq.java b/src/sun/security/krb5/internal/LastReq.java new file mode 100644 index 00000000..45ff8a81 --- /dev/null +++ b/src/sun/security/krb5/internal/LastReq.java @@ -0,0 +1,137 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.util.Vector; +import java.io.IOException; + +/** + * Implements the ASN.1 LastReq type. + * + *

    + * LastReq ::= SEQUENCE OF SEQUENCE { + * lr-type [0] Int32, + * lr-value [1] KerberosTime + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class LastReq { + private LastReqEntry[] entry = null; + + public LastReq(LastReqEntry[] entries) throws IOException { + if (entries != null) { + entry = new LastReqEntry[entries.length]; + for (int i = 0; i < entries.length; i++) { + if (entries[i] == null) { + throw new IOException("Cannot create a LastReqEntry"); + } else { + entry[i] = (LastReqEntry)entries[i].clone(); + } + } + } + + } + + /** + * Constructs a LastReq object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + + public LastReq(DerValue encoding) throws Asn1Exception, IOException { + Vector v= new Vector<>(); + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + while (encoding.getData().available() > 0) { + v.addElement(new LastReqEntry(encoding.getData().getDerValue())); + } + if (v.size() > 0) { + entry = new LastReqEntry[v.size()]; + v.copyInto(entry); + } + } + + /** + * Encodes an LastReq object. + * @return the byte array of encoded LastReq object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + if (entry != null && entry.length > 0) { + DerOutputStream temp = new DerOutputStream(); + for (int i = 0; i < entry.length; i++) + temp.write(entry[i].asn1Encode()); + bytes.write(DerValue.tag_Sequence, temp); + return bytes.toByteArray(); + } + return null; + } + + /** + * Parse (unmarshal) a last request from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicates if this data field is optional + * @return an instance of LastReq. + * + */ + + public static LastReq parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + return new LastReq(subDer); + } + } + +} diff --git a/src/sun/security/krb5/internal/LastReqEntry.java b/src/sun/security/krb5/internal/LastReqEntry.java new file mode 100644 index 00000000..396d3b8d --- /dev/null +++ b/src/sun/security/krb5/internal/LastReqEntry.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; + +public class LastReqEntry { + private int lrType; + private KerberosTime lrValue; + + private LastReqEntry() { + } + + public LastReqEntry(int Type, KerberosTime time){ + lrType = Type; + lrValue = time; + // XXX check the type and time. + } + + /** + * Constructs a LastReqEntry object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public LastReqEntry(DerValue encoding) throws Asn1Exception, IOException { + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + DerValue der; + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00){ + lrType = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + + lrValue = KerberosTime.parse(encoding.getData(), (byte)0x01, false); + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an LastReqEntry object. + * @return the byte array of encoded LastReqEntry object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(lrType); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), lrValue.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + public Object clone() { + LastReqEntry newEntry = new LastReqEntry(); + newEntry.lrType = lrType; + newEntry.lrValue = lrValue; + return newEntry; + } +} diff --git a/src/sun/security/krb5/internal/LocalSeqNumber.java b/src/sun/security/krb5/internal/LocalSeqNumber.java new file mode 100644 index 00000000..10a1c2f0 --- /dev/null +++ b/src/sun/security/krb5/internal/LocalSeqNumber.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Confounder; + +public class LocalSeqNumber implements SeqNumber { + private int lastSeqNumber; + + public LocalSeqNumber() { + randInit(); + } + + public LocalSeqNumber(int start) { + init(start); + } + + public LocalSeqNumber(Integer start) { + init(start.intValue()); + } + + public synchronized void randInit() { + /* + * Sequence numbers fall in the range 0 through 2^32 - 1 and wrap + * to zero following the value 2^32 - 1. + * Previous implementations used signed sequence numbers. + * Workaround implementation incompatibilities by not generating + * initial sequence numbers greater than 2^30, as done + * in MIT distribution. + */ + // get the random confounder + byte[] data = Confounder.bytes(4); + data[0] = (byte)(data[0] & 0x3f); + int result = ((data[3] & 0xff) | + ((data[2] & 0xff) << 8) | + ((data[1] & 0xff) << 16) | + ((data[0] & 0xff) << 24)); + if (result == 0) { + result = 1; + } + lastSeqNumber = result; + } + + public synchronized void init(int start) { + lastSeqNumber = start; + } + + public synchronized int current() { + return lastSeqNumber; + } + + public synchronized int next() { + return lastSeqNumber + 1; + } + + public synchronized int step() { + return ++lastSeqNumber; + } + +} diff --git a/src/sun/security/krb5/internal/LoginOptions.java b/src/sun/security/krb5/internal/LoginOptions.java new file mode 100644 index 00000000..9147a46c --- /dev/null +++ b/src/sun/security/krb5/internal/LoginOptions.java @@ -0,0 +1,88 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +/** + * Implements the ASN.1 KDCOptions type. + * + *

    + * KDCOptions ::= KerberosFlags + * -- reserved(0), + * -- forwardable(1), + * -- forwarded(2), + * -- proxiable(3), + * -- proxy(4), + * -- allow-postdate(5), + * -- postdated(6), + * -- unused7(7), + * -- renewable(8), + * -- unused9(9), + * -- unused10(10), + * -- opt-hardware-auth(11), + * -- unused12(12), + * -- unused13(13), + * -- 15 is reserved for canonicalize + * -- unused15(15), + * -- 26 was unused in 1510 + * -- disable-transited-check(26), + * -- renewable-ok(27), + * -- enc-tkt-in-skey(28), + * -- renew(30), + * -- validate(31) + * + * KerberosFlags ::= BIT STRING (SIZE (32..MAX)) + * -- minimum number of bits shall be sent, + * -- but no fewer than 32 + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class LoginOptions extends KDCOptions { + + // Login Options + + public static final int RESERVED = 0; + public static final int FORWARDABLE = 1; + public static final int PROXIABLE = 3; + public static final int ALLOW_POSTDATE = 5; + public static final int RENEWABLE = 8; + public static final int RENEWABLE_OK = 27; + public static final int ENC_TKT_IN_SKEY = 28; + public static final int RENEW = 30; + public static final int VALIDATE = 31; + public static final int MAX = 31; + +} diff --git a/src/sun/security/krb5/internal/MethodData.java b/src/sun/security/krb5/internal/MethodData.java new file mode 100644 index 00000000..ef29461f --- /dev/null +++ b/src/sun/security/krb5/internal/MethodData.java @@ -0,0 +1,111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 EncKrbPrivPart type. + * + *

    + * METHOD-DATA ::= SEQUENCE { + * method-type[0] INTEGER, + * method-data[1] OCTET STRING OPTIONAL + * } + * + */ +public class MethodData { + private int methodType; + private byte[] methodData = null; //optional + + public MethodData(int type, byte[] data) { + methodType = type; + if (data != null) { + methodData = data.clone(); + } + } + + /** + * Constructs a MethodData object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public MethodData(DerValue encoding) throws Asn1Exception, IOException { + DerValue der; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + BigInteger bint = der.getData().getBigInteger(); + methodType = bint.intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + if (encoding.getData().available() > 0) { + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x01) { + methodData = der.getData().getOctetString(); + } + else throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes an MethodData object. + * @return the byte array of encoded MethodData object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(methodType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + if (methodData != null) { + temp = new DerOutputStream(); + temp.putOctetString(methodData); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + } + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + +} diff --git a/src/sun/security/krb5/internal/NetClient.java b/src/sun/security/krb5/internal/NetClient.java new file mode 100644 index 00000000..62243560 --- /dev/null +++ b/src/sun/security/krb5/internal/NetClient.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.misc.IOUtils; + +import java.io.*; +import java.net.*; + +public abstract class NetClient implements AutoCloseable { + public static NetClient getInstance(String protocol, String hostname, int port, + int timeout) throws IOException { + if (protocol.equals("TCP")) { + return new TCPClient(hostname, port, timeout); + } else { + return new UDPClient(hostname, port, timeout); + } + } + + abstract public void send(byte[] data) throws IOException; + abstract public byte[] receive() throws IOException; + abstract public void close() throws IOException; +} + +class TCPClient extends NetClient { + + private Socket tcpSocket; + private BufferedOutputStream out; + private BufferedInputStream in; + + TCPClient(String hostname, int port, int timeout) + throws IOException { + tcpSocket = new Socket(); + tcpSocket.connect(new InetSocketAddress(hostname, port), timeout); + out = new BufferedOutputStream(tcpSocket.getOutputStream()); + in = new BufferedInputStream(tcpSocket.getInputStream()); + tcpSocket.setSoTimeout(timeout); + } + + @Override + public void send(byte[] data) throws IOException { + byte[] lenField = new byte[4]; + intToNetworkByteOrder(data.length, lenField, 0, 4); + out.write(lenField); + + out.write(data); + out.flush(); + } + + @Override + public byte[] receive() throws IOException { + byte[] lenField = new byte[4]; + int count = readFully(lenField, 4); + + if (count != 4) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient could not read length field"); + } + return null; + } + + int len = networkByteOrderToInt(lenField, 0, 4); + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient reading " + len + " bytes"); + } + if (len <= 0) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient zero or negative length field: "+len); + } + return null; + } + + try { + return IOUtils.readFully(in, len, true); + } catch (IOException ioe) { + if (Krb5.DEBUG) { + System.out.println( + ">>>DEBUG: TCPClient could not read complete packet (" + + len + "/" + count + ")"); + } + return null; + } + } + + @Override + public void close() throws IOException { + tcpSocket.close(); + } + + /** + * Read requested number of bytes before returning. + * @return The number of bytes actually read; -1 if none read + */ + private int readFully(byte[] inBuf, int total) throws IOException { + int count, pos = 0; + + while (total > 0) { + count = in.read(inBuf, pos, total); + + if (count == -1) { + return (pos == 0? -1 : pos); + } + pos += count; + total -= count; + } + return pos; + } + + /** + * Returns the integer represented by 4 bytes in network byte order. + */ + private static int networkByteOrderToInt(byte[] buf, int start, + int count) { + if (count > 4) { + throw new IllegalArgumentException( + "Cannot handle more than 4 bytes"); + } + + int answer = 0; + + for (int i = 0; i < count; i++) { + answer <<= 8; + answer |= ((int)buf[start+i] & 0xff); + } + return answer; + } + + /** + * Encodes an integer into 4 bytes in network byte order in the buffer + * supplied. + */ + private static void intToNetworkByteOrder(int num, byte[] buf, + int start, int count) { + if (count > 4) { + throw new IllegalArgumentException( + "Cannot handle more than 4 bytes"); + } + + for (int i = count-1; i >= 0; i--) { + buf[start+i] = (byte)(num & 0xff); + num >>>= 8; + } + } +} + +class UDPClient extends NetClient { + InetAddress iaddr; + int iport; + int bufSize = 65507; + DatagramSocket dgSocket; + DatagramPacket dgPacketIn; + + UDPClient(String hostname, int port, int timeout) + throws UnknownHostException, SocketException { + iaddr = InetAddress.getByName(hostname); + iport = port; + dgSocket = new DatagramSocket(); + dgSocket.setSoTimeout(timeout); + dgSocket.connect(iaddr, iport); + } + + @Override + public void send(byte[] data) throws IOException { + DatagramPacket dgPacketOut = new DatagramPacket(data, data.length, + iaddr, iport); + dgSocket.send(dgPacketOut); + } + + @Override + public byte[] receive() throws IOException { + byte ibuf[] = new byte[bufSize]; + dgPacketIn = new DatagramPacket(ibuf, ibuf.length); + try { + dgSocket.receive(dgPacketIn); + } + catch (SocketException e) { + if (e instanceof PortUnreachableException) { + throw e; + } + dgSocket.receive(dgPacketIn); + } + byte[] data = new byte[dgPacketIn.getLength()]; + System.arraycopy(dgPacketIn.getData(), 0, data, 0, + dgPacketIn.getLength()); + return data; + } + + @Override + public void close() { + dgSocket.close(); + } +} diff --git a/src/sun/security/krb5/internal/PAData.java b/src/sun/security/krb5/internal/PAData.java new file mode 100644 index 00000000..5e323157 --- /dev/null +++ b/src/sun/security/krb5/internal/PAData.java @@ -0,0 +1,323 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import sun.security.krb5.internal.util.KerberosString; + +/** + * Implements the ASN.1 PA-DATA type. + * + * + * PA-DATA ::= SEQUENCE { + * -- NOTE: first tag is [1], not [0] + * padata-type [1] Int32, + * padata-value [2] OCTET STRING -- might be encoded AP-REQ + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class PAData { + private int pADataType; + private byte[] pADataValue = null; + private static final byte TAG_PATYPE = 1; + private static final byte TAG_PAVALUE = 2; + + private PAData() { + } + + public PAData(int new_pADataType, byte[] new_pADataValue) { + pADataType = new_pADataType; + if (new_pADataValue != null) { + pADataValue = new_pADataValue.clone(); + } + } + + public Object clone() { + PAData new_pAData = new PAData(); + new_pAData.pADataType = pADataType; + if (pADataValue != null) { + new_pAData.pADataValue = new byte[pADataValue.length]; + System.arraycopy(pADataValue, 0, new_pAData.pADataValue, + 0, pADataValue.length); + } + return new_pAData; + } + + /** + * Constructs a PAData object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public PAData(DerValue encoding) throws Asn1Exception, IOException { + DerValue der = null; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x01) { + this.pADataType = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x02) { + this.pADataValue = der.getData().getOctetString(); + } + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes this object to an OutputStream. + * + * @return byte array of the encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception Asn1Exception on encoding errors. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + + temp.putInteger(pADataType); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_PATYPE), temp); + temp = new DerOutputStream(); + temp.putOctetString(pADataValue); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_PAVALUE), temp); + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + // accessor methods + public int getType() { + return pADataType; + } + + public byte[] getValue() { + return ((pADataValue == null) ? null : pADataValue.clone()); + } + + /** + * Gets the preferred etype from the PAData array. + * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored + * 2. ETYPE-INFO2 preferred to ETYPE-INFO + * 3. multiple entries for same etype in one PA-DATA, use the first one. + * 4. Multiple PA-DATA with same type, choose the last one + * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). + * @return the etype, or defaultEType if not enough info + * @throws Asn1Exception|IOException if there is an encoding error + */ + public static int getPreferredEType(PAData[] pas, int defaultEType) + throws IOException, Asn1Exception { + + if (pas == null) return defaultEType; + + DerValue d = null, d2 = null; + for (PAData p: pas) { + if (p.getValue() == null) continue; + switch (p.getType()) { + case Krb5.PA_ETYPE_INFO: + d = new DerValue(p.getValue()); + break; + case Krb5.PA_ETYPE_INFO2: + d2 = new DerValue(p.getValue()); + break; + } + } + if (d2 != null) { + while (d2.data.available() > 0) { + DerValue value = d2.data.getDerValue(); + ETypeInfo2 tmp = new ETypeInfo2(value); + if (tmp.getParams() == null) { + // we don't support non-null s2kparams + return tmp.getEType(); + } + } + } + if (d != null) { + while (d.data.available() > 0) { + DerValue value = d.data.getDerValue(); + ETypeInfo tmp = new ETypeInfo(value); + return tmp.getEType(); + } + } + return defaultEType; + } + + /** + * A place to store a pair of salt and s2kparams. + * An empty salt is changed to null, to be interoperable + * with Windows 2000 server. This is in fact not correct. + */ + public static class SaltAndParams { + public final String salt; + public final byte[] params; + public SaltAndParams(String s, byte[] p) { + if (s != null && s.isEmpty()) s = null; + this.salt = s; + this.params = p; + } + } + + /** + * Fetches salt and s2kparams value for eType in a series of PA-DATAs. + * 1. ETYPE-INFO2-ENTRY with unknown s2kparams ignored + * 2. PA-ETYPE-INFO2 preferred to PA-ETYPE-INFO preferred to PA-PW-SALT. + * 3. multiple entries for same etype in one PA-DATA, use the first one. + * 4. Multiple PA-DATA with same type, choose the last one + * (This is useful when PA-DATAs from KRB-ERROR and AS-REP are combined). + * @return salt and s2kparams. can be null if not found + */ + public static SaltAndParams getSaltAndParams(int eType, PAData[] pas) + throws Asn1Exception, IOException { + + if (pas == null) return null; + + DerValue d = null, d2 = null; + String paPwSalt = null; + + for (PAData p: pas) { + if (p.getValue() == null) continue; + switch (p.getType()) { + case Krb5.PA_PW_SALT: + paPwSalt = new String(p.getValue(), + KerberosString.MSNAME?"UTF8":"8859_1"); + break; + case Krb5.PA_ETYPE_INFO: + d = new DerValue(p.getValue()); + break; + case Krb5.PA_ETYPE_INFO2: + d2 = new DerValue(p.getValue()); + break; + } + } + if (d2 != null) { + while (d2.data.available() > 0) { + DerValue value = d2.data.getDerValue(); + ETypeInfo2 tmp = new ETypeInfo2(value); + if (tmp.getParams() == null && tmp.getEType() == eType) { + // we don't support non-null s2kparams + return new SaltAndParams(tmp.getSalt(), tmp.getParams()); + } + } + } + if (d != null) { + while (d.data.available() > 0) { + DerValue value = d.data.getDerValue(); + ETypeInfo tmp = new ETypeInfo(value); + if (tmp.getEType() == eType) { + return new SaltAndParams(tmp.getSalt(), null); + } + } + } + if (paPwSalt != null) { + return new SaltAndParams(paPwSalt, null); + } + return null; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(">>>Pre-Authentication Data:\n\t PA-DATA type = ") + .append(pADataType).append('\n'); + + switch(pADataType) { + case Krb5.PA_ENC_TIMESTAMP: + sb.append("\t PA-ENC-TIMESTAMP"); + break; + case Krb5.PA_ETYPE_INFO: + if (pADataValue != null) { + try { + DerValue der = new DerValue(pADataValue); + while (der.data.available() > 0) { + DerValue value = der.data.getDerValue(); + ETypeInfo info = new ETypeInfo(value); + sb.append("\t PA-ETYPE-INFO etype = ") + .append(info.getEType()) + .append(", salt = ") + .append(info.getSalt()) + .append('\n'); + } + } catch (IOException|Asn1Exception e) { + sb.append("\t \n"); + } + } + break; + case Krb5.PA_ETYPE_INFO2: + if (pADataValue != null) { + try { + DerValue der = new DerValue(pADataValue); + while (der.data.available() > 0) { + DerValue value = der.data.getDerValue(); + ETypeInfo2 info2 = new ETypeInfo2(value); + sb.append("\t PA-ETYPE-INFO2 etype = ") + .append(info2.getEType()) + .append(", salt = ") + .append(info2.getSalt()) + .append(", s2kparams = "); + byte[] s2kparams = info2.getParams(); + if (s2kparams == null) { + sb.append("null\n"); + } else if (s2kparams.length == 0) { + sb.append("empty\n"); + } else { + sb.append(new sun.misc.HexDumpEncoder() + .encodeBuffer(s2kparams)); + } + } + } catch (IOException|Asn1Exception e) { + sb.append("\t \n"); + } + } + break; + case Krb5.PA_FOR_USER: + sb.append("\t PA-FOR-USER\n"); + break; + default: + // Unknown Pre-auth type + break; + } + return sb.toString(); + } +} diff --git a/src/sun/security/krb5/internal/PAEncTSEnc.java b/src/sun/security/krb5/internal/PAEncTSEnc.java new file mode 100644 index 00000000..83375647 --- /dev/null +++ b/src/sun/security/krb5/internal/PAEncTSEnc.java @@ -0,0 +1,116 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 PAEncTSEnc type. + * + *

    + * PA-ENC-TS-ENC ::= SEQUENCE { + * patimestamp [0] KerberosTime -- client's time --, + * pausec [1] Microseconds OPTIONAL + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class PAEncTSEnc { + public KerberosTime pATimeStamp; + public Integer pAUSec; //optional + + public PAEncTSEnc( + KerberosTime new_pATimeStamp, + Integer new_pAUSec + ) { + pATimeStamp = new_pATimeStamp; + pAUSec = new_pAUSec; + } + + public PAEncTSEnc() { + KerberosTime now = KerberosTime.now(); + pATimeStamp = now; + pAUSec = new Integer(now.getMicroSeconds()); + } + + /** + * Constructs a PAEncTSEnc object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public PAEncTSEnc(DerValue encoding) throws Asn1Exception, IOException { + DerValue der; + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + pATimeStamp = KerberosTime.parse(encoding.getData(), (byte)0x00, false); + if (encoding.getData().available() > 0) { + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x01) { + pAUSec = new Integer(der.getData().getBigInteger().intValue()); + } + else throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + + /** + * Encodes a PAEncTSEnc object. + * @return the byte array of encoded PAEncTSEnc object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), pATimeStamp.asn1Encode()); + if (pAUSec != null) { + temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(pAUSec.intValue())); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + } + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/PAForUserEnc.java b/src/sun/security/krb5/internal/PAForUserEnc.java new file mode 100644 index 00000000..42c9caae --- /dev/null +++ b/src/sun/security/krb5/internal/PAForUserEnc.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import sun.security.krb5.*; +import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.krb5.internal.util.KerberosString; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * Implements the ASN.1 PA-FOR-USER type. + * + *

    + * padata-type ::= PA-FOR-USER + * -- value 129 + * padata-value ::= EncryptedData + * -- PA-FOR-USER-ENC + * PA-FOR-USER-ENC ::= SEQUENCE { + * userName[0] PrincipalName, + * userRealm[1] Realm, + * cksum[2] Checksum, + * auth-package[3] KerberosString + * } + * + * + *

    + * This definition reflects MS-SFU. + */ + +public class PAForUserEnc { + final public PrincipalName name; + final private EncryptionKey key; + final public static String AUTH_PACKAGE = "Kerberos"; + + public PAForUserEnc(PrincipalName name, EncryptionKey key) { + this.name = name; + this.key = key; + } + + /** + * Constructs a PA-FOR-USER object from a DER encoding. + * @param encoding the input object + * @param key the key to verify the checksum inside encoding + * @throws KrbException if the verification fails. + * Note: this method is now only used by test KDC, therefore + * the verification is ignored (at the moment). + */ + public PAForUserEnc(DerValue encoding, EncryptionKey key) + throws Asn1Exception, KrbException, IOException { + DerValue der = null; + this.key = key; + + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + // Realm after name? Quite abnormal. + PrincipalName tmpName = null; + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + try { + tmpName = new PrincipalName(der.getData().getDerValue(), + new Realm("PLACEHOLDER")); + } catch (RealmException re) { + // Impossible + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x01) { + try { + Realm realm = new Realm(der.getData().getDerValue()); + name = new PrincipalName( + tmpName.getNameType(), tmpName.getNameStrings(), realm); + } catch (RealmException re) { + throw new IOException(re); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x02) { + // Deal with the checksum + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x03) { + String authPackage = new KerberosString(der.getData().getDerValue()).toString(); + if (!authPackage.equalsIgnoreCase(AUTH_PACKAGE)) { + throw new IOException("Incorrect auth-package"); + } + } else { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + if (encoding.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), name.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), name.getRealm().asn1Encode()); + + try { + Checksum cks = new Checksum( + Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR, + getS4UByteArray(), + key, + KeyUsage.KU_PA_FOR_USER_ENC_CKSUM); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), cks.asn1Encode()); + } catch (KrbException ke) { + throw new IOException(ke); + } + + DerOutputStream temp = new DerOutputStream(); + temp.putDerValue(new KerberosString(AUTH_PACKAGE).toDerValue()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), temp); + + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Returns S4UByteArray, the block to calculate checksum inside a + * PA-FOR-USER-ENC data structure. It includes: + * 1. userName.name-type encoded as a 4-byte integer in little endian + * byte order + * 2. all string values in the sequence of strings contained in the + * userName.name-string field + * 3. the string value of the userRealm field + * 4. the string value of auth-package field + */ + public byte[] getS4UByteArray() { + try { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ba.write(new byte[4]); + for (String s: name.getNameStrings()) { + ba.write(s.getBytes("UTF-8")); + } + ba.write(name.getRealm().toString().getBytes("UTF-8")); + ba.write(AUTH_PACKAGE.getBytes("UTF-8")); + byte[] output = ba.toByteArray(); + int pnType = name.getNameType(); + output[0] = (byte)(pnType & 0xff); + output[1] = (byte)((pnType>>8) & 0xff); + output[2] = (byte)((pnType>>16) & 0xff); + output[3] = (byte)((pnType>>24) & 0xff); + return output; + } catch (IOException ioe) { + // not possible + throw new AssertionError("Cannot write ByteArrayOutputStream", ioe); + } + } + + public String toString() { + return "PA-FOR-USER: " + name; + } +} diff --git a/src/sun/security/krb5/internal/ReplayCache.java b/src/sun/security/krb5/internal/ReplayCache.java new file mode 100644 index 00000000..f6dc5702 --- /dev/null +++ b/src/sun/security/krb5/internal/ReplayCache.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal; + +import sun.security.action.GetPropertyAction; +import sun.security.krb5.internal.rcache.AuthTimeWithHash; +import sun.security.krb5.internal.rcache.MemoryCache; +import sun.security.krb5.internal.rcache.DflCache; + +import java.security.AccessController; + +/** + * Models the replay cache of an acceptor as described in + * RFC 4120 3.2.3. + * @since 1.8 + */ +public abstract class ReplayCache { + public static ReplayCache getInstance(String type) { + if (type == null) { + return new MemoryCache(); + } else if (type.equals("dfl") || type.startsWith("dfl:")) { + return new DflCache(type); + } else if (type.equals("none")) { + return new ReplayCache() { + @Override + public void checkAndStore(KerberosTime currTime, AuthTimeWithHash time) + throws KrbApErrException { + // no check at all + } + }; + } else { + throw new IllegalArgumentException("Unknown type: " + type); + } + } + public static ReplayCache getInstance() { + String type = AccessController.doPrivileged( + new GetPropertyAction("sun.security.krb5.rcache")); + return getInstance(type); + } + + /** + * Accepts or rejects an AuthTime. + * @param currTime the current time + * @param time AuthTimeWithHash object calculated from authenticator + * @throws KrbApErrException if the authenticator is a replay + */ + public abstract void checkAndStore(KerberosTime currTime, AuthTimeWithHash time) + throws KrbApErrException; +} diff --git a/src/sun/security/krb5/internal/SeqNumber.java b/src/sun/security/krb5/internal/SeqNumber.java new file mode 100644 index 00000000..eb2ea992 --- /dev/null +++ b/src/sun/security/krb5/internal/SeqNumber.java @@ -0,0 +1,39 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +public interface SeqNumber { + public void randInit(); + public void init(int start); + public int current(); + public int next(); + public int step(); +} diff --git a/src/sun/security/krb5/internal/TGSRep.java b/src/sun/security/krb5/internal/TGSRep.java new file mode 100644 index 00000000..edd363f2 --- /dev/null +++ b/src/sun/security/krb5/internal/TGSRep.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.PrincipalName; +import sun.security.krb5.RealmException; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.util.*; +import java.io.IOException; + +public class TGSRep extends KDCRep { + + public TGSRep( + PAData[] new_pAData, + PrincipalName new_cname, + Ticket new_ticket, + EncryptedData new_encPart + ) throws IOException { + super(new_pAData, new_cname, new_ticket, + new_encPart, Krb5.KRB_TGS_REP); + } + + public TGSRep(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + } + + public TGSRep(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding, Krb5.KRB_TGS_REP); + } + +} diff --git a/src/sun/security/krb5/internal/TGSReq.java b/src/sun/security/krb5/internal/TGSReq.java new file mode 100644 index 00000000..2f45ce97 --- /dev/null +++ b/src/sun/security/krb5/internal/TGSReq.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.*; +import sun.security.util.*; +import java.io.IOException; + +public class TGSReq extends KDCReq { + + public TGSReq(PAData[] new_pAData, KDCReqBody new_reqBody) throws IOException { + super(new_pAData, new_reqBody, Krb5.KRB_TGS_REQ); + } + + public TGSReq(byte[] data) throws Asn1Exception, + IOException, KrbException { + init(new DerValue(data)); + } + + public TGSReq(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding); + } + + private void init(DerValue encoding) throws Asn1Exception, + IOException, KrbException { + init(encoding, Krb5.KRB_TGS_REQ); + } + +} diff --git a/src/sun/security/krb5/internal/Ticket.java b/src/sun/security/krb5/internal/Ticket.java new file mode 100644 index 00000000..0f1c3d9a --- /dev/null +++ b/src/sun/security/krb5/internal/Ticket.java @@ -0,0 +1,176 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.PrincipalName; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.Realm; +import sun.security.krb5.RealmException; +import sun.security.util.*; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 Ticket type. + * + *

    + * Ticket ::= [APPLICATION 1] SEQUENCE { + * tkt-vno [0] INTEGER (5), + * realm [1] Realm, + * sname [2] PrincipalName, + * enc-part [3] EncryptedData -- EncTicketPart + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class Ticket implements Cloneable { + public int tkt_vno; + public PrincipalName sname; + public EncryptedData encPart; + + private Ticket() { + } + + public Object clone() { + Ticket new_ticket = new Ticket(); + new_ticket.sname = (PrincipalName)sname.clone(); + new_ticket.encPart = (EncryptedData)encPart.clone(); + new_ticket.tkt_vno = tkt_vno; + return new_ticket; + } + + public Ticket( + PrincipalName new_sname, + EncryptedData new_encPart + ) { + tkt_vno = Krb5.TICKET_VNO; + sname = new_sname; + encPart = new_encPart; + } + + public Ticket(byte[] data) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(new DerValue(data)); + } + + public Ticket(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + init(encoding); + } + + /** + * Initializes a Ticket object. + * @param encoding a single DER-encoded value. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + * @exception KrbApErrException if the value read from the DER-encoded data stream does not match the pre-defined value. + * @exception RealmException if an error occurs while parsing a Realm object. + */ + + private void init(DerValue encoding) throws Asn1Exception, + RealmException, KrbApErrException, IOException { + DerValue der; + DerValue subDer; + if (((encoding.getTag() & (byte)0x1F) != Krb5.KRB_TKT) + || (encoding.isApplication() != true) + || (encoding.isConstructed() != true)) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + if (der.getTag() != DerValue.tag_Sequence) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + subDer = der.getData().getDerValue(); + if ((subDer.getTag() & (byte)0x1F) != (byte)0x00) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + tkt_vno = subDer.getData().getBigInteger().intValue(); + if (tkt_vno != Krb5.TICKET_VNO) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BADVERSION); + Realm srealm = Realm.parse(der.getData(), (byte)0x01, false); + sname = PrincipalName.parse(der.getData(), (byte)0x02, false, srealm); + encPart = EncryptedData.parse(der.getData(), (byte)0x03, false); + if (der.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes a Ticket object. + * @return byte array of encoded ticket object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + DerValue der[] = new DerValue[4]; + temp.putInteger(BigInteger.valueOf(tkt_vno)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), sname.getRealm().asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x02), sname.asn1Encode()); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x03), encPart.asn1Encode()); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + DerOutputStream ticket = new DerOutputStream(); + ticket.write(DerValue.createTag(DerValue.TAG_APPLICATION, true, (byte)0x01), temp); + return ticket.toByteArray(); + } + + /** + * Parse (unmarshal) a Ticket from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @return an instance of Ticket. + */ + public static Ticket parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException, RealmException, KrbApErrException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + return new Ticket(subDer); + } + } + + +} diff --git a/src/sun/security/krb5/internal/TicketFlags.java b/src/sun/security/krb5/internal/TicketFlags.java new file mode 100644 index 00000000..1f77e0f0 --- /dev/null +++ b/src/sun/security/krb5/internal/TicketFlags.java @@ -0,0 +1,190 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.krb5.Asn1Exception; +import sun.security.krb5.internal.util.KerberosFlags; +import sun.security.util.*; +import java.io.IOException; + +/** + * Implements the ASN.1TicketFlags type. + * + * TicketFlags ::= BIT STRING + * { + * reserved(0), + * forwardable(1), + * forwarded(2), + * proxiable(3), + * proxy(4), + * may-postdate(5), + * postdated(6), + * invalid(7), + * renewable(8), + * initial(9), + * pre-authent(10), + * hw-authent(11) + * } + */ +public class TicketFlags extends KerberosFlags { + public TicketFlags() { + super(Krb5.TKT_OPTS_MAX + 1); + } + + public TicketFlags (boolean[] flags) throws Asn1Exception { + super(flags); + if (flags.length > Krb5.TKT_OPTS_MAX + 1) { + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + } + + public TicketFlags(int size, byte[] data) throws Asn1Exception { + super(size, data); + if ((size > data.length * BITS_PER_UNIT) || (size > Krb5.TKT_OPTS_MAX + 1)) + throw new Asn1Exception(Krb5.BITSTRING_BAD_LENGTH); + } + + public TicketFlags(DerValue encoding) throws IOException, Asn1Exception { + this(encoding.getUnalignedBitString(true).toBooleanArray()); + } + + /** + * Parse (unmarshal) a ticket flag from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @return an instance of TicketFlags. + * + */ + public static TicketFlags parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + return new TicketFlags(subDer); + } + } + + public Object clone() { + try { + return new TicketFlags(this.toBooleanArray()); + } + catch (Exception e) { + return null; + } + } + + public boolean match(LoginOptions options) { + boolean matched = false; + //We currently only consider if forwardable renewable and proxiable are match + if (this.get(Krb5.TKT_OPTS_FORWARDABLE) == (options.get(KDCOptions.FORWARDABLE))) { + if (this.get(Krb5.TKT_OPTS_PROXIABLE) == (options.get(KDCOptions.PROXIABLE))) { + if (this.get(Krb5.TKT_OPTS_RENEWABLE) == (options.get(KDCOptions.RENEWABLE))) { + matched = true; + } + } + } + return matched; + } + public boolean match(TicketFlags flags) { + boolean matched = true; + for (int i = 0; i <= Krb5.TKT_OPTS_MAX; i++) { + if (this.get(i) != flags.get(i)) { + return false; + } + } + return matched; + } + + + /** + * Returns the string representative of ticket flags. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + boolean[] flags = toBooleanArray(); + for (int i = 0; i < flags.length; i++) { + if (flags[i] == true) { + switch (i) { + case 0: + sb.append("RESERVED;"); + break; + case 1: + sb.append("FORWARDABLE;"); + break; + case 2: + sb.append("FORWARDED;"); + break; + case 3: + sb.append("PROXIABLE;"); + break; + case 4: + sb.append("PROXY;"); + break; + case 5: + sb.append("MAY-POSTDATE;"); + break; + case 6: + sb.append("POSTDATED;"); + break; + case 7: + sb.append("INVALID;"); + break; + case 8: + sb.append("RENEWABLE;"); + break; + case 9: + sb.append("INITIAL;"); + break; + case 10: + sb.append("PRE-AUTHENT;"); + break; + case 11: + sb.append("HW-AUTHENT;"); + break; + } + } + } + String result = sb.toString(); + if (result.length() > 0) { + result = result.substring(0, result.length() - 1); + } + return result; + } +} diff --git a/src/sun/security/krb5/internal/TransitedEncoding.java b/src/sun/security/krb5/internal/TransitedEncoding.java new file mode 100644 index 00000000..fc33f9fd --- /dev/null +++ b/src/sun/security/krb5/internal/TransitedEncoding.java @@ -0,0 +1,136 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal; + +import sun.security.util.*; +import sun.security.krb5.Asn1Exception; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Implements the ASN.1 TransitedEncoding type. + * + *

    + * TransitedEncoding ::= SEQUENCE { + * tr-type [0] Int32 -- must be registered --, + * contents [1] OCTET STRING + * } + * + * + *

    + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ + +public class TransitedEncoding { + public int trType; + public byte[] contents; + + public TransitedEncoding(int type, byte[] cont) { + trType = type; + contents = cont; + } + + /** + * Constructs a TransitedEncoding object. + * @param encoding a Der-encoded data. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + + public TransitedEncoding(DerValue encoding) throws Asn1Exception, IOException { + if (encoding.getTag() != DerValue.tag_Sequence) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + DerValue der; + der = encoding.getData().getDerValue(); + if ((der.getTag() & 0x1F) == 0x00) { + trType = der.getData().getBigInteger().intValue(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + der = encoding.getData().getDerValue(); + + if ((der.getTag() & 0x1F) == 0x01) { + contents = der.getData().getOctetString(); + } + else + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + if (der.getData().available() > 0) + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + + /** + * Encodes a TransitedEncoding object. + * @return the byte array of the encoded TransitedEncoding object. + * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. + * @exception IOException if an I/O error occurs while reading encoded data. + */ + public byte[] asn1Encode() throws Asn1Exception, IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream temp = new DerOutputStream(); + temp.putInteger(BigInteger.valueOf(trType)); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x00), temp); + temp = new DerOutputStream(); + temp.putOctetString(contents); + bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); + temp = new DerOutputStream(); + temp.write(DerValue.tag_Sequence, bytes); + return temp.toByteArray(); + } + + /** + * Parse (unmarshal) a TransitedEncoding object from a DER input stream. This form + * parsing might be used when expanding a value which is part of + * a constructed sequence and uses explicitly tagged type. + * + * @exception Asn1Exception on error. + * @param data the Der input stream value, which contains one or more marshaled value. + * @param explicitTag tag number. + * @param optional indicate if this data field is optional + * @return an instance of TransitedEncoding. + * + */ + public static TransitedEncoding parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { + if ((optional) && (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) + return null; + DerValue der = data.getDerValue(); + if (explicitTag != (der.getTag() & (byte)0x1F)) { + throw new Asn1Exception(Krb5.ASN1_BAD_ID); + } + else { + DerValue subDer = der.getData().getDerValue(); + return new TransitedEncoding(subDer); + } + } +} diff --git a/src/sun/security/krb5/internal/ccache/CCacheInputStream.java b/src/sun/security/krb5/internal/ccache/CCacheInputStream.java new file mode 100644 index 00000000..8ca6e11d --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/CCacheInputStream.java @@ -0,0 +1,407 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import sun.misc.IOUtils; +import sun.security.krb5.*; +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.util.KrbDataInputStream; + +/** + * This class extends KrbDataInputStream. It is used for parsing FCC-format + * data from file to memory. + * + * @author Yanni Zhang + * + */ +public class CCacheInputStream extends KrbDataInputStream implements FileCCacheConstants { + + /* + * FCC version 2 contains type information for principals. FCC + * version 1 does not. + * + * FCC version 3 contains keyblock encryption type information, and is + * architecture independent. Previous versions are not. + * + * The code will accept version 1, 2, and 3 ccaches, and depending + * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2, + * or 3 FCC caches. + * + * The default credentials cache should be type 3 for now (see + * init_ctx.c). + */ + /* V4 of the credentials cache format allows for header tags */ + + private static boolean DEBUG = Krb5.DEBUG; + + public CCacheInputStream(InputStream is){ + super(is); + } + + /* Read tag field introduced in KRB5_FCC_FVNO_4 */ + // this needs to be public for Kinit. + public Tag readTag() throws IOException { + char[] buf = new char[1024]; + int len; + int tag = -1; + int taglen; + Integer time_offset = null; + Integer usec_offset = null; + + len = read(2); + if (len < 0) { + throw new IOException("stop."); + } + if (len > buf.length) { + throw new IOException("Invalid tag length."); + } + while (len > 0) { + tag = read(2); + taglen = read(2); + switch (tag) { + case FCC_TAG_DELTATIME: + time_offset = new Integer(read(4)); + usec_offset = new Integer(read(4)); + break; + default: + } + len = len - (4 + taglen); + } + return new Tag(len, tag, time_offset, usec_offset); + } + /* + * In file-based credential cache, the realm name is stored as part of + * principal name at the first place. + */ + // made public for KinitOptions to call directly + public PrincipalName readPrincipal(int version) throws IOException, RealmException { + int type, length, namelength, kret; + String[] pname = null; + String realm; + /* Read principal type */ + if (version == KRB5_FCC_FVNO_1) { + type = KRB5_NT_UNKNOWN; + } else { + type = read(4); + } + length = readLength4(); + List result = new ArrayList(); + /* + * DCE includes the principal's realm in the count; the new format + * does not. + */ + if (version == KRB5_FCC_FVNO_1) + length--; + for (int i = 0; i <= length; i++) { + namelength = readLength4(); + byte[] bytes = IOUtils.readFully(this, namelength, true); + result.add(new String(bytes)); + } + if (result.isEmpty()) { + throw new IOException("No realm or principal"); + } + if (isRealm(result.get(0))) { + realm = result.remove(0); + if (result.isEmpty()) { + throw new IOException("No principal name components"); + } + return new PrincipalName( + type, + result.toArray(new String[result.size()]), + new Realm(realm)); + } + try { + return new PrincipalName( + result.toArray(new String[result.size()]), + type); + } catch (RealmException re) { + return null; + } + } + + /* + * In practice, a realm is named by uppercasing the DNS domain name. we currently + * rely on this to determine if the string within the principal identifier is realm + * name. + * + */ + boolean isRealm(String str) { + try { + Realm r = new Realm(str); + } + catch (Exception e) { + return false; + } + StringTokenizer st = new StringTokenizer(str, "."); + String s; + while (st.hasMoreTokens()) { + s = st.nextToken(); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) >= 141) { + return false; + } + } + } + return true; + } + + EncryptionKey readKey(int version) throws IOException { + int keyType, keyLen; + keyType = read(2); + if (version == KRB5_FCC_FVNO_3) + read(2); /* keytype recorded twice in fvno 3 */ + keyLen = readLength4(); + byte[] bytes = IOUtils.readFully(this, keyLen, true); + return new EncryptionKey(bytes, keyType, new Integer(version)); + } + + long[] readTimes() throws IOException { + long[] times = new long[4]; + times[0] = (long)read(4) * 1000; + times[1] = (long)read(4) * 1000; + times[2] = (long)read(4) * 1000; + times[3] = (long)read(4) * 1000; + return times; + } + + boolean readskey() throws IOException { + if (read() == 0) { + return false; + } + else return true; + } + + HostAddress[] readAddr() throws IOException, KrbApErrException { + int numAddrs, addrType, addrLength; + numAddrs = readLength4(); + if (numAddrs > 0) { + List addrs = new ArrayList<>(); + for (int i = 0; i < numAddrs; i++) { + addrType = read(2); + addrLength = readLength4(); + if (!(addrLength == 4 || addrLength == 16)) { + if (DEBUG) { + System.out.println("Incorrect address format."); + } + return null; + } + byte[] result = new byte[addrLength]; + for (int j = 0; j < addrLength; j++) + result[j] = (byte)read(1); + addrs.add(new HostAddress(addrType, result)); + } + return addrs.toArray(new HostAddress[addrs.size()]); + } + return null; + } + + AuthorizationDataEntry[] readAuth() throws IOException { + int num, adtype, adlength; + num = readLength4(); + if (num > 0) { + List auData = new ArrayList<>(); + byte[] data = null; + for (int i = 0; i < num; i++) { + adtype = read(2); + adlength = readLength4(); + data = IOUtils.readFully(this, adlength, true); + auData.add(new AuthorizationDataEntry(adtype, data)); + } + return auData.toArray(new AuthorizationDataEntry[auData.size()]); + } + else return null; + } + + byte[] readData() throws IOException { + int length; + length = readLength4(); + if (length == 0) { + return null; + } else { + return IOUtils.readFully(this, length, true); + } + } + + boolean[] readFlags() throws IOException { + boolean[] flags = new boolean[Krb5.TKT_OPTS_MAX+1]; + int ticketFlags; + ticketFlags = read(4); + if ((ticketFlags & 0x40000000) == TKT_FLG_FORWARDABLE) + flags[1] = true; + if ((ticketFlags & 0x20000000) == TKT_FLG_FORWARDED) + flags[2] = true; + if ((ticketFlags & 0x10000000) == TKT_FLG_PROXIABLE) + flags[3] = true; + if ((ticketFlags & 0x08000000) == TKT_FLG_PROXY) + flags[4] = true; + if ((ticketFlags & 0x04000000) == TKT_FLG_MAY_POSTDATE) + flags[5] = true; + if ((ticketFlags & 0x02000000) == TKT_FLG_POSTDATED) + flags[6] = true; + if ((ticketFlags & 0x01000000) == TKT_FLG_INVALID) + flags[7] = true; + if ((ticketFlags & 0x00800000) == TKT_FLG_RENEWABLE) + flags[8] = true; + if ((ticketFlags & 0x00400000) == TKT_FLG_INITIAL) + flags[9] = true; + if ((ticketFlags & 0x00200000) == TKT_FLG_PRE_AUTH) + flags[10] = true; + if ((ticketFlags & 0x00100000) == TKT_FLG_HW_AUTH) + flags[11] = true; + if (DEBUG) { + String msg = ">>> CCacheInputStream: readFlags() "; + if (flags[1] == true) { + msg += " FORWARDABLE;"; + } + if (flags[2] == true) { + msg += " FORWARDED;"; + } + if (flags[3] == true) { + msg += " PROXIABLE;"; + } + if (flags[4] == true) { + msg += " PROXY;"; + } + if (flags[5] == true) { + msg += " MAY_POSTDATE;"; + } + if (flags[6] == true) { + msg += " POSTDATED;"; + } + if (flags[7] == true) { + msg += " INVALID;"; + } + if (flags[8] == true) { + msg += " RENEWABLE;"; + } + + if (flags[9] == true) { + msg += " INITIAL;"; + } + if (flags[10] == true) { + msg += " PRE_AUTH;"; + } + if (flags[11] == true) { + msg += " HW_AUTH;"; + } + System.out.println(msg); + } + return flags; + } + + /** + * Reads the next cred in stream. + * @return the next cred, null if ticket or second_ticket unparseable. + * + * Note: MIT krb5 1.8.1 might generate a config entry with server principal + * X-CACHECONF:/krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM. The + * entry is used by KDC to inform the client that it support certain + * features. Its ticket is not a valid krb5 ticket and thus this method + * returns null. + */ + Credentials readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception { + PrincipalName cpname = null; + try { + cpname = readPrincipal(version); + } catch (Exception e) { + // Do not return here. All data for this cred should be fully + // consumed so that we can read the next one. + } + if (DEBUG) { + System.out.println(">>>DEBUG client principal is " + cpname); + } + PrincipalName spname = null; + try { + spname = readPrincipal(version); + } catch (Exception e) { + // same as above + } + if (DEBUG) { + System.out.println(">>>DEBUG server principal is " + spname); + } + EncryptionKey key = readKey(version); + if (DEBUG) { + System.out.println(">>>DEBUG key type: " + key.getEType()); + } + long times[] = readTimes(); + KerberosTime authtime = new KerberosTime(times[0]); + KerberosTime starttime = + (times[1]==0) ? null : new KerberosTime(times[1]); + KerberosTime endtime = new KerberosTime(times[2]); + KerberosTime renewTill = + (times[3]==0) ? null : new KerberosTime(times[3]); + + if (DEBUG) { + System.out.println(">>>DEBUG auth time: " + authtime.toDate().toString()); + System.out.println(">>>DEBUG start time: " + + ((starttime==null)?"null":starttime.toDate().toString())); + System.out.println(">>>DEBUG end time: " + endtime.toDate().toString()); + System.out.println(">>>DEBUG renew_till time: " + + ((renewTill==null)?"null":renewTill.toDate().toString())); + } + boolean skey = readskey(); + boolean flags[] = readFlags(); + TicketFlags tFlags = new TicketFlags(flags); + HostAddress addr[] = readAddr(); + HostAddresses addrs = null; + if (addr != null) { + addrs = new HostAddresses(addr); + } + AuthorizationDataEntry[] auDataEntry = readAuth(); + AuthorizationData auData = null; + if (auDataEntry != null) { + auData = new AuthorizationData(auDataEntry); + } + byte[] ticketData = readData(); + byte[] ticketData2 = readData(); + + // Skip this cred if either cpname or spname isn't created. + if (cpname == null || spname == null) { + return null; + } + + try { + return new Credentials(cpname, spname, key, authtime, starttime, + endtime, renewTill, skey, tFlags, + addrs, auData, + ticketData != null ? new Ticket(ticketData) : null, + ticketData2 != null ? new Ticket(ticketData2) : null); + } catch (Exception e) { // If any of new Ticket(*) fails. + return null; + } + } +} diff --git a/src/sun/security/krb5/internal/ccache/CCacheOutputStream.java b/src/sun/security/krb5/internal/ccache/CCacheOutputStream.java new file mode 100644 index 00000000..230d5050 --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/CCacheOutputStream.java @@ -0,0 +1,150 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import java.io.IOException; +import java.io.OutputStream; +import sun.security.krb5.internal.util.KrbDataOutputStream; +import sun.security.krb5.*; +import sun.security.krb5.internal.*; + +/** + * This class implements a buffered output stream. It provides functions to write FCC-format data to a disk file. + * + * @author Yanni Zhang + * + */ +public class CCacheOutputStream extends KrbDataOutputStream implements FileCCacheConstants { + public CCacheOutputStream(OutputStream os) { + super(os); + } + + public void writeHeader(PrincipalName p, int version) throws IOException { + write((version & 0xff00) >> 8); + write(version & 0x00ff); + p.writePrincipal(this); + } + + /** + * Writes a credentials in FCC format to this cache output stream. + * + * @param creds the credentials to be written to the output stream. + * @exception IOException if an I/O exception occurs. + * @exception Asn1Exception if an Asn1Exception occurs. + */ + /*For object data fields which themselves have multiple data fields, such as PrincipalName, EncryptionKey + HostAddresses, AuthorizationData, I created corresponding write methods (writePrincipal, + writeKey,...) in each class, since converting the object into FCC format data stream + should be encapsulated in object itself. + */ + public void addCreds(Credentials creds) throws IOException, Asn1Exception { + creds.cname.writePrincipal(this); + creds.sname.writePrincipal(this); + creds.key.writeKey(this); + write32((int)(creds.authtime.getTime()/1000)); + if (creds.starttime != null) + write32((int)(creds.starttime.getTime()/1000)); + else write32(0); + write32((int)(creds.endtime.getTime()/1000)); + if (creds.renewTill != null) + write32((int)(creds.renewTill.getTime()/1000)); + + else write32(0); + if (creds.isEncInSKey) { + write8(1); + } + else write8(0); + writeFlags(creds.flags); + if (creds.caddr == null) + write32(0); + else + creds.caddr.writeAddrs(this); + + if (creds.authorizationData == null) { + write32(0); + } + else + creds.authorizationData.writeAuth(this); + writeTicket(creds.ticket); + writeTicket(creds.secondTicket); + } + + void writeTicket(Ticket t) throws IOException, Asn1Exception { + if (t == null) { + write32(0); + } + else { + byte[] bytes = t.asn1Encode(); + write32(bytes.length); + write(bytes, 0, bytes.length); + } + } + + void writeFlags(TicketFlags flags) throws IOException { + int tFlags = 0; + boolean[] f = flags.toBooleanArray(); + if (f[1] == true) { + tFlags |= TKT_FLG_FORWARDABLE; + } + if (f[2] == true) { + tFlags |= TKT_FLG_FORWARDED; + } + if (f[3] == true) { + tFlags |= TKT_FLG_PROXIABLE; + } + if (f[4] == true) { + tFlags |= TKT_FLG_PROXY; + } + if (f[5] == true) { + tFlags |= TKT_FLG_MAY_POSTDATE; + } + if (f[6] == true) { + tFlags |= TKT_FLG_POSTDATED; + } + if (f[7] == true) { + tFlags |= TKT_FLG_INVALID; + } + if (f[8] == true) { + tFlags |= TKT_FLG_RENEWABLE; + } + if (f[9] == true) { + tFlags |= TKT_FLG_INITIAL; + } + if (f[10] == true) { + tFlags |= TKT_FLG_PRE_AUTH; + } + if (f[11] == true) { + tFlags |= TKT_FLG_HW_AUTH; + } + write32(tFlags); + + } +} diff --git a/src/sun/security/krb5/internal/ccache/Credentials.java b/src/sun/security/krb5/internal/ccache/Credentials.java new file mode 100644 index 00000000..7128545a --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/Credentials.java @@ -0,0 +1,214 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; + +public class Credentials { + + PrincipalName cname; + PrincipalName sname; + EncryptionKey key; + KerberosTime authtime; + KerberosTime starttime;//optional + KerberosTime endtime; + KerberosTime renewTill; //optional + HostAddresses caddr; //optional; for proxied tickets only + AuthorizationData authorizationData; //optional, not being actually used + public boolean isEncInSKey; // true if ticket is encrypted in another ticket's skey + TicketFlags flags; + Ticket ticket; + Ticket secondTicket; //optional + private boolean DEBUG = Krb5.DEBUG; + + public Credentials( + PrincipalName new_cname, + PrincipalName new_sname, + EncryptionKey new_key, + KerberosTime new_authtime, + KerberosTime new_starttime, + KerberosTime new_endtime, + KerberosTime new_renewTill, + boolean new_isEncInSKey, + TicketFlags new_flags, + HostAddresses new_caddr, + AuthorizationData new_authData, + Ticket new_ticket, + Ticket new_secondTicket) { + cname = (PrincipalName) new_cname.clone(); + sname = (PrincipalName) new_sname.clone(); + key = (EncryptionKey) new_key.clone(); + + authtime = new_authtime; + starttime = new_starttime; + endtime = new_endtime; + renewTill = new_renewTill; + + if (new_caddr != null) { + caddr = (HostAddresses) new_caddr.clone(); + } + if (new_authData != null) { + authorizationData = (AuthorizationData) new_authData.clone(); + } + + isEncInSKey = new_isEncInSKey; + flags = (TicketFlags) new_flags.clone(); + ticket = (Ticket) (new_ticket.clone()); + if (new_secondTicket != null) { + secondTicket = (Ticket) new_secondTicket.clone(); + } + } + + public Credentials( + KDCRep kdcRep, + Ticket new_secondTicket, + AuthorizationData new_authorizationData, + boolean new_isEncInSKey) { + if (kdcRep.encKDCRepPart == null) //can't store while encrypted + { + return; + } + cname = (PrincipalName) kdcRep.cname.clone(); + ticket = (Ticket) kdcRep.ticket.clone(); + key = (EncryptionKey) kdcRep.encKDCRepPart.key.clone(); + flags = (TicketFlags) kdcRep.encKDCRepPart.flags.clone(); + authtime = kdcRep.encKDCRepPart.authtime; + starttime = kdcRep.encKDCRepPart.starttime; + endtime = kdcRep.encKDCRepPart.endtime; + renewTill = kdcRep.encKDCRepPart.renewTill; + + sname = (PrincipalName) kdcRep.encKDCRepPart.sname.clone(); + caddr = (HostAddresses) kdcRep.encKDCRepPart.caddr.clone(); + secondTicket = (Ticket) new_secondTicket.clone(); + authorizationData = + (AuthorizationData) new_authorizationData.clone(); + isEncInSKey = new_isEncInSKey; + } + + public Credentials(KDCRep kdcRep) { + this(kdcRep, null); + } + + public Credentials(KDCRep kdcRep, Ticket new_ticket) { + sname = (PrincipalName) kdcRep.encKDCRepPart.sname.clone(); + cname = (PrincipalName) kdcRep.cname.clone(); + key = (EncryptionKey) kdcRep.encKDCRepPart.key.clone(); + authtime = kdcRep.encKDCRepPart.authtime; + starttime = kdcRep.encKDCRepPart.starttime; + endtime = kdcRep.encKDCRepPart.endtime; + renewTill = kdcRep.encKDCRepPart.renewTill; + // if (kdcRep.msgType == Krb5.KRB_AS_REP) { + // isEncInSKey = false; + // secondTicket = null; + // } + flags = kdcRep.encKDCRepPart.flags; + if (kdcRep.encKDCRepPart.caddr != null) { + caddr = (HostAddresses) kdcRep.encKDCRepPart.caddr.clone(); + } else { + caddr = null; + } + ticket = (Ticket) kdcRep.ticket.clone(); + if (new_ticket != null) { + secondTicket = (Ticket) new_ticket.clone(); + isEncInSKey = true; + } else { + secondTicket = null; + isEncInSKey = false; + } + } + + /** + * Checks if this credential is expired + */ + public boolean isValid() { + boolean valid = true; + if (endtime.getTime() < System.currentTimeMillis()) { + valid = false; + } else if (starttime != null) { + if (starttime.getTime() > System.currentTimeMillis()) { + valid = false; + } + } else { + if (authtime.getTime() > System.currentTimeMillis()) { + valid = false; + } + } + return valid; + } + + public PrincipalName getServicePrincipal() throws RealmException { + return sname; + } + + public sun.security.krb5.Credentials setKrbCreds() { + // Note: We will not pass authorizationData to s.s.k.Credentials. The + // field in that class will be passed to Krb5Context as the return + // value of ExtendedGSSContext.inquireSecContext(KRB5_GET_AUTHZ_DATA), + // which is documented as the authData in the service ticket. That + // is on the acceptor side. + // + // This class is for the initiator side. Also, authdata inside a ccache + // is most likely to be the one in Authenticator in PA-TGS-REQ encoded + // in TGS-REQ, therefore only stored with a service ticket. Currently + // in Java, we only reads TGTs. + return new sun.security.krb5.Credentials(ticket, + cname, sname, key, flags, authtime, starttime, endtime, renewTill, caddr); + } + + public KerberosTime getStartTime() { + return starttime; + } + + public KerberosTime getAuthTime() { + return authtime; + } + + public KerberosTime getEndTime() { + return endtime; + } + + public KerberosTime getRenewTill() { + return renewTill; + } + + public TicketFlags getTicketFlags() { + return flags; + } + + public int getEType() { + return key.getEType(); + } + + public int getTktEType() { + return ticket.encPart.getEType(); + } +} diff --git a/src/sun/security/krb5/internal/ccache/CredentialsCache.java b/src/sun/security/krb5/internal/ccache/CredentialsCache.java new file mode 100644 index 00000000..d3e9eab1 --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/CredentialsCache.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; + +import java.io.IOException; + +/** + * CredentialsCache stores credentials(tickets, session keys, etc) in a semi-permanent store + * for later use by different program. + * + * @author Yanni Zhang + */ +public abstract class CredentialsCache { + static CredentialsCache singleton = null; + static String cacheName; + private static boolean DEBUG = Krb5.DEBUG; + + public static CredentialsCache getInstance(PrincipalName principal) { + return FileCredentialsCache.acquireInstance(principal, null); + } + + public static CredentialsCache getInstance(String cache) { + if ((cache.length() >= 5) && cache.substring(0, 5).equalsIgnoreCase("FILE:")) { + return FileCredentialsCache.acquireInstance(null, cache.substring(5)); + } + // XXX else, memory credential cache + // default is file credential cache. + return FileCredentialsCache.acquireInstance(null, cache); + } + + public static CredentialsCache getInstance(PrincipalName principal, + String cache) { + + // XXX Modify this to use URL framework of the JDK + if (cache != null && + (cache.length() >= 5) && + cache.regionMatches(true, 0, "FILE:", 0, 5)) { + return FileCredentialsCache.acquireInstance(principal, + cache.substring(5)); + } + + // When cache is null, read the default cache. + // XXX else ..we haven't provided support for memory credential cache + // yet. (supported in native code) + // default is file credentials cache. + return FileCredentialsCache.acquireInstance(principal, cache); + + } + + /** + * Gets the default credentials cache. + */ + public static CredentialsCache getInstance() { + // Default credentials cache is file-based. + return FileCredentialsCache.acquireInstance(); + } + + public static CredentialsCache create(PrincipalName principal, String name) { + if (name == null) { + throw new RuntimeException("cache name error"); + } + if ((name.length() >= 5) + && name.regionMatches(true, 0, "FILE:", 0, 5)) { + name = name.substring(5); + return (FileCredentialsCache.New(principal, name)); + } + // else return file credentials cache + // default is file credentials cache. + return (FileCredentialsCache.New(principal, name)); + } + + public static CredentialsCache create(PrincipalName principal) { + // create a default credentials cache for a specified principal + return (FileCredentialsCache.New(principal)); + } + + public static String cacheName() { + return cacheName; + } + + public abstract PrincipalName getPrimaryPrincipal(); + public abstract void update(Credentials c); + public abstract void save() throws IOException, KrbException; + public abstract Credentials[] getCredsList(); + public abstract Credentials getDefaultCreds(); + public abstract Credentials getCreds(PrincipalName sname); + public abstract Credentials getCreds(LoginOptions options, PrincipalName sname); +} diff --git a/src/sun/security/krb5/internal/ccache/FileCCacheConstants.java b/src/sun/security/krb5/internal/ccache/FileCCacheConstants.java new file mode 100644 index 00000000..c01ec623 --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/FileCCacheConstants.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +/** + * Constants used by file-based credential cache classes. + * + * @author Yanni Zhang + * + */ +public interface FileCCacheConstants { + /* + * FCC version 2 contains type information for principals. FCC + * version 1 does not. + * + * FCC version 3 contains keyblock encryption type information, and is + * architecture independent. Previous versions are not. */ + public final int KRB5_FCC_FVNO_1 = 0x501; + public final int KRB5_FCC_FVNO_2 = 0x502; + public final int KRB5_FCC_FVNO_3 = 0x503; + public final int KRB5_FCC_FVNO_4 = 0x504; + public final int FCC_TAG_DELTATIME = 1; + public final int KRB5_NT_UNKNOWN = 0; + public final int TKT_FLG_FORWARDABLE = 0x40000000; + public final int TKT_FLG_FORWARDED = 0x20000000; + public final int TKT_FLG_PROXIABLE = 0x10000000; + public final int TKT_FLG_PROXY = 0x08000000; + public final int TKT_FLG_MAY_POSTDATE = 0x04000000; + public final int TKT_FLG_POSTDATED = 0x02000000; + public final int TKT_FLG_INVALID = 0x01000000; + public final int TKT_FLG_RENEWABLE = 0x00800000; + public final int TKT_FLG_INITIAL = 0x00400000; + public final int TKT_FLG_PRE_AUTH = 0x00200000; + public final int TKT_FLG_HW_AUTH = 0x00100000; +} diff --git a/src/sun/security/krb5/internal/ccache/FileCredentialsCache.java b/src/sun/security/krb5/internal/ccache/FileCredentialsCache.java new file mode 100644 index 00000000..85fb0dba --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/FileCredentialsCache.java @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * =========================================================================== + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * + * Copyright 1997 The Open Group Research Institute. All rights reserved. + * =========================================================================== + * + */ +package sun.security.krb5.internal.ccache; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; +import java.util.StringTokenizer; +import java.util.Vector; +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.reflect.*; + +/** + * CredentialsCache stores credentials(tickets, session keys, etc) in a + * semi-permanent store + * for later use by different program. + * + * @author Yanni Zhang + * @author Ram Marti + */ + +public class FileCredentialsCache extends CredentialsCache + implements FileCCacheConstants { + public int version; + public Tag tag; // optional + public PrincipalName primaryPrincipal; + private Vector credentialsList; + private static String dir; + private static boolean DEBUG = Krb5.DEBUG; + + public static synchronized FileCredentialsCache acquireInstance( + PrincipalName principal, String cache) { + try { + FileCredentialsCache fcc = new FileCredentialsCache(); + if (cache == null) { + cacheName = FileCredentialsCache.getDefaultCacheName(); + } else { + cacheName = FileCredentialsCache.checkValidation(cache); + } + if ((cacheName == null) || !(new File(cacheName)).exists()) { + // invalid cache name or the file doesn't exist + return null; + } + if (principal != null) { + fcc.primaryPrincipal = principal; + } + fcc.load(cacheName); + return fcc; + } catch (IOException e) { + // we don't handle it now, instead we return a null at the end. + if (DEBUG) { + e.printStackTrace(); + } + } catch (KrbException e) { + // we don't handle it now, instead we return a null at the end. + if (DEBUG) { + e.printStackTrace(); + } + } + return null; + } + + public static FileCredentialsCache acquireInstance() { + return acquireInstance(null, null); + } + + static synchronized FileCredentialsCache New(PrincipalName principal, + String name) { + try { + FileCredentialsCache fcc = new FileCredentialsCache(); + cacheName = FileCredentialsCache.checkValidation(name); + if (cacheName == null) { + // invalid cache name or the file doesn't exist + return null; + } + fcc.init(principal, cacheName); + return fcc; + } + catch (IOException e) { + } + catch (KrbException e) { + } + return null; + } + + static synchronized FileCredentialsCache New(PrincipalName principal) { + try { + FileCredentialsCache fcc = new FileCredentialsCache(); + cacheName = FileCredentialsCache.getDefaultCacheName(); + fcc.init(principal, cacheName); + return fcc; + } + catch (IOException e) { + if (DEBUG) { + e.printStackTrace(); + } + } catch (KrbException e) { + if (DEBUG) { + e.printStackTrace(); + } + + } + return null; + } + + private FileCredentialsCache() { + } + + boolean exists(String cache) { + File file = new File(cache); + if (file.exists()) { + return true; + } else return false; + } + + synchronized void init(PrincipalName principal, String name) + throws IOException, KrbException { + primaryPrincipal = principal; + try (FileOutputStream fos = new FileOutputStream(name); + CCacheOutputStream cos = new CCacheOutputStream(fos)) { + version = KRB5_FCC_FVNO_3; + cos.writeHeader(primaryPrincipal, version); + } + load(name); + } + + synchronized void load(String name) throws IOException, KrbException { + PrincipalName p; + try (FileInputStream fis = new FileInputStream(name); + CCacheInputStream cis = new CCacheInputStream(fis)) { + version = cis.readVersion(); + if (version == KRB5_FCC_FVNO_4) { + tag = cis.readTag(); + } else { + tag = null; + if (version == KRB5_FCC_FVNO_1 || version == KRB5_FCC_FVNO_2) { + cis.setNativeByteOrder(); + } + } + p = cis.readPrincipal(version); + + if (primaryPrincipal != null) { + if (!(primaryPrincipal.match(p))) { + throw new IOException("Primary principals don't match."); + } + } else + primaryPrincipal = p; + credentialsList = new Vector(); + while (cis.available() > 0) { + Credentials cred = cis.readCred(version); + if (cred != null) { + credentialsList.addElement(cred); + } + } + } + } + + + /** + * Updates the credentials list. If the specified credentials for the + * service is new, add it to the list. If there is an entry in the list, + * replace the old credentials with the new one. + * @param c the credentials. + */ + + public synchronized void update(Credentials c) { + if (credentialsList != null) { + if (credentialsList.isEmpty()) { + credentialsList.addElement(c); + } else { + Credentials tmp = null; + boolean matched = false; + + for (int i = 0; i < credentialsList.size(); i++) { + tmp = credentialsList.elementAt(i); + if (match(c.sname.getNameStrings(), + tmp.sname.getNameStrings()) && + ((c.sname.getRealmString()).equalsIgnoreCase( + tmp.sname.getRealmString()))) { + matched = true; + if (c.endtime.getTime() >= tmp.endtime.getTime()) { + if (DEBUG) { + System.out.println(" >>> FileCredentialsCache " + + "Ticket matched, overwrite " + + "the old one."); + } + credentialsList.removeElementAt(i); + credentialsList.addElement(c); + } + } + } + if (matched == false) { + if (DEBUG) { + System.out.println(" >>> FileCredentialsCache Ticket " + + "not exactly matched, " + + "add new one into cache."); + } + + credentialsList.addElement(c); + } + } + } + } + + public synchronized PrincipalName getPrimaryPrincipal() { + return primaryPrincipal; + } + + + /** + * Saves the credentials cache file to the disk. + */ + public synchronized void save() throws IOException, Asn1Exception { + try (FileOutputStream fos = new FileOutputStream(cacheName); + CCacheOutputStream cos = new CCacheOutputStream(fos)) { + cos.writeHeader(primaryPrincipal, version); + Credentials[] tmp = null; + if ((tmp = getCredsList()) != null) { + for (int i = 0; i < tmp.length; i++) { + cos.addCreds(tmp[i]); + } + } + } + } + + boolean match(String[] s1, String[] s2) { + if (s1.length != s2.length) { + return false; + } else { + for (int i = 0; i < s1.length; i++) { + if (!(s1[i].equalsIgnoreCase(s2[i]))) { + return false; + } + } + } + return true; + } + + /** + * Returns the list of credentials entries in the cache file. + */ + public synchronized Credentials[] getCredsList() { + if ((credentialsList == null) || (credentialsList.isEmpty())) { + return null; + } else { + Credentials[] tmp = new Credentials[credentialsList.size()]; + for (int i = 0; i < credentialsList.size(); i++) { + tmp[i] = credentialsList.elementAt(i); + } + return tmp; + } + + } + + public Credentials getCreds(LoginOptions options, PrincipalName sname) { + if (options == null) { + return getCreds(sname); + } else { + Credentials[] list = getCredsList(); + if (list == null) { + return null; + } else { + for (int i = 0; i < list.length; i++) { + if (sname.match(list[i].sname)) { + if (list[i].flags.match(options)) { + return list[i]; + } + } + } + } + return null; + } + } + + + /** + * Gets a credentials for a specified service. + * @param sname service principal name. + */ + public Credentials getCreds(PrincipalName sname) { + Credentials[] list = getCredsList(); + if (list == null) { + return null; + } else { + for (int i = 0; i < list.length; i++) { + if (sname.match(list[i].sname)) { + return list[i]; + } + } + } + return null; + } + + public Credentials getDefaultCreds() { + Credentials[] list = getCredsList(); + if (list == null) { + return null; + } else { + for (int i = list.length-1; i >= 0; i--) { + if (list[i].sname.toString().startsWith("krbtgt")) { + String[] nameStrings = list[i].sname.getNameStrings(); + // find the TGT for the current realm krbtgt/realm@realm + if (nameStrings[1].equals(list[i].sname.getRealm().toString())) { + return list[i]; + } + } + } + } + return null; + } + + /* + * Returns path name of the credentials cache file. + * The path name is searched in the following order: + * + * 1. KRB5CCNAME (bare file name without FILE:) + * 2. /tmp/krb5cc_ on unix systems + * 3. /krb5cc_ + * 4. /krb5cc (if can't get ) + */ + + public static String getDefaultCacheName() { + + String stdCacheNameComponent = "krb5cc"; + String name; + + // The env var can start with TYPE:, we only support FILE: here. + // https://docs.oracle.com/cd/E19082-01/819-2252/6n4i8rtr3/index.html + name = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public String run() { + String cache = System.getenv("KRB5CCNAME"); + if (cache != null && + (cache.length() >= 5) && + cache.regionMatches(true, 0, "FILE:", 0, 5)) { + cache = cache.substring(5); + } + return cache; + } + }); + if (name != null) { + if (DEBUG) { + System.out.println(">>>KinitOptions cache name is " + name); + } + return name; + } + + // get cache name from system.property + String osname = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("os.name")); + + /* + * For Unix platforms we use the default cache name to be + * /tmp/krbcc_uid ; for all other platforms we use + * {user_home}/krb5_cc{user_name} + * Please note that for Windows 2K we will use LSA to get + * the TGT from the the default cache even before we come here; + * however when we create cache we will create a cache under + * {user_home}/krb5_cc{user_name} for non-Unix platforms including + * Windows 2K. + */ + + if (osname != null) { + String cmd = null; + String uidStr = null; + long uid = 0; + + if (!osname.startsWith("Windows")) { + try { + Class c = Class.forName + ("com.sun.security.auth.module.UnixSystem"); + Constructor constructor = c.getConstructor(); + Object obj = constructor.newInstance(); + Method method = c.getMethod("getUid"); + uid = ((Long)method.invoke(obj)).longValue(); + name = File.separator + "tmp" + + File.separator + stdCacheNameComponent + "_" + uid; + if (DEBUG) { + System.out.println(">>>KinitOptions cache name is " + + name); + } + return name; + } catch (Exception e) { + if (DEBUG) { + System.out.println("Exception in obtaining uid " + + "for Unix platforms " + + "Using user's home directory"); + + + e.printStackTrace(); + } + } + } + } + + // we did not get the uid; + + + String user_name = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.name")); + + String user_home = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.home")); + + if (user_home == null) { + user_home = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.dir")); + } + + if (user_name != null) { + name = user_home + File.separator + + stdCacheNameComponent + "_" + user_name; + } else { + name = user_home + File.separator + stdCacheNameComponent; + } + + if (DEBUG) { + System.out.println(">>>KinitOptions cache name is " + name); + } + + return name; + } + + public static String checkValidation(String name) { + String fullname = null; + if (name == null) { + return null; + } + try { + // get full path name + fullname = (new File(name)).getCanonicalPath(); + File fCheck = new File(fullname); + if (!(fCheck.exists())) { + // get absolute directory + File temp = new File(fCheck.getParent()); + // test if the directory exists + if (!(temp.isDirectory())) + fullname = null; + temp = null; + } + fCheck = null; + + } catch (IOException e) { + fullname = null; // invalid name + } + return fullname; + } + + + private static String exec(String c) { + StringTokenizer st = new StringTokenizer(c); + Vector v = new Vector<>(); + while (st.hasMoreTokens()) { + v.addElement(st.nextToken()); + } + final String[] command = new String[v.size()]; + v.copyInto(command); + try { + + Process p = + java.security.AccessController.doPrivileged + (new java.security.PrivilegedAction () { + public Process run() { + try { + return (Runtime.getRuntime().exec(command)); + } catch (IOException e) { + if (DEBUG) { + e.printStackTrace(); + } + return null; + } + } + }); + if (p == null) { + // exception occurred during executing the command + return null; + } + + BufferedReader commandResult = + new BufferedReader + (new InputStreamReader(p.getInputStream(), "8859_1")); + String s1 = null; + if ((command.length == 1) && + (command[0].equals("/usr/bin/env"))) { + while ((s1 = commandResult.readLine()) != null) { + if (s1.length() >= 11) { + if ((s1.substring(0, 11)).equalsIgnoreCase + ("KRB5CCNAME=")) { + s1 = s1.substring(11); + break; + } + } + } + } else s1 = commandResult.readLine(); + commandResult.close(); + return s1; + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + } + return null; + } +} diff --git a/src/sun/security/krb5/internal/ccache/MemoryCredentialsCache.java b/src/sun/security/krb5/internal/ccache/MemoryCredentialsCache.java new file mode 100644 index 00000000..4198f2c7 --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/MemoryCredentialsCache.java @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import sun.security.krb5.*; + +import java.io.IOException; +import java.io.File; + +//Windows supports the "API: cache" type, which is a shared memory cache. This is +//implemented by krbcc32.dll as part of the MIT Kerberos for Win32 distribution. +//MemoryCredentialsCache will provide future functions to access shared memeory cache on +//Windows platform. Native code implementation may be necessary. + +/** + * This class extends CredentialsCache. It is used for accessing data in shared memory + * cache on Windows platforms. + * + * @author Yanni Zhang + */ +public abstract class MemoryCredentialsCache extends CredentialsCache { + + private static CredentialsCache getCCacheInstance(PrincipalName p) { + return null; + } + + private static CredentialsCache getCCacheInstance(PrincipalName p, File cacheFile) { + return null; + } + + + public abstract boolean exists(String cache); + + public abstract void update(Credentials c); + + public abstract void save() throws IOException, KrbException; + + public abstract Credentials[] getCredsList(); + + public abstract Credentials getCreds(PrincipalName sname) ; + + public abstract PrincipalName getPrimaryPrincipal(); + +} diff --git a/src/sun/security/krb5/internal/ccache/Tag.java b/src/sun/security/krb5/internal/ccache/Tag.java new file mode 100644 index 00000000..5971a17d --- /dev/null +++ b/src/sun/security/krb5/internal/ccache/Tag.java @@ -0,0 +1,72 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ccache; + +import java.io.ByteArrayOutputStream; + +/** + * tag field introduced in KRB5_FCC_FVNO_4 + * + * @author Yanni Zhang + */ +public class Tag{ + int length; + int tag; + int tagLen; + Integer time_offset; + Integer usec_offset; + + public Tag(int len, int new_tag, Integer new_time, Integer new_usec) { + tag = new_tag; + tagLen = 8; + time_offset = new_time; + usec_offset = new_usec; + length = 4 + tagLen; + } + public Tag(int new_tag) { + tag = new_tag; + tagLen = 0; + length = 4 + tagLen; + } + public byte[] toByteArray() { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write(length); + os.write(tag); + os.write(tagLen); + if (time_offset != null) { + os.write(time_offset.intValue()); + } + if (usec_offset != null) { + os.write(usec_offset.intValue()); + } + return os.toByteArray(); + } +} diff --git a/src/sun/security/krb5/internal/crypto/Aes128.java b/src/sun/security/krb5/internal/crypto/Aes128.java new file mode 100644 index 00000000..cf0552c9 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Aes128.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.internal.crypto.dk.AesDkCrypto; +import sun.security.krb5.KrbCryptoException; +import java.security.GeneralSecurityException; + +/** + * Class with static methods for doing AES operations. + * + * @author Seema Malkani + */ + +public class Aes128 { + private static final AesDkCrypto CRYPTO = new AesDkCrypto(128); + + private Aes128() { + } + + public static byte[] stringToKey(char[] password, String salt, byte[] params) + throws GeneralSecurityException { + return CRYPTO.stringToKey(password, salt, params); + } + + // in bytes + public static int getChecksumLength() { + return CRYPTO.getChecksumLength(); + } + + public static byte[] calculateChecksum(byte[] baseKey, int usage, + byte[] input, int start, int len) throws GeneralSecurityException { + return CRYPTO.calculateChecksum(baseKey, usage, input, start, len); + } + + public static byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encrypt(baseKey, usage, ivec, null /* new_ivec */, + plaintext, start, len); + } + + /* Encrypt plaintext; do not add confounder, or checksum */ + public static byte[] encryptRaw(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encryptRaw(baseKey, usage, ivec, plaintext, start, len); + } + + public static byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decrypt(baseKey, usage, ivec, ciphertext, start, len); + } + + /* Decrypt ciphertext; do not remove confounder, or check checksum */ + public static byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decryptRaw(baseKey, usage, ivec, ciphertext, start, len); + } +}; diff --git a/src/sun/security/krb5/internal/crypto/Aes128CtsHmacSha1EType.java b/src/sun/security/krb5/internal/crypto/Aes128CtsHmacSha1EType.java new file mode 100644 index 00000000..9b6e7823 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Aes128CtsHmacSha1EType.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import java.security.GeneralSecurityException; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Checksum; + +/* + * This class encapsulates the encryption type for AES128 + * + * @author Seema Malkani + */ + +public final class Aes128CtsHmacSha1EType extends EType { + + public int eType() { + return EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96; + } + + public int minimumPadSize() { + return 0; + } + + public int confounderSize() { + return blockSize(); + } + + public int checksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128; + } + + public int checksumSize() { + return Aes128.getChecksumLength(); + } + + public int blockSize() { + return 16; + } + + public int keyType() { + return Krb5.KEYTYPE_AES; + } + + public int keySize() { + return 16; // bytes + } + + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return encrypt(data, key, ivec, usage); + } + + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, int usage) + throws KrbCryptoException { + try { + return Aes128.encrypt(key, usage, ivec, data, 0, data.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return decrypt(cipher, key, ivec, usage); + } + + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException, KrbCryptoException { + try { + return Aes128.decrypt(key, usage, ivec, cipher, 0, cipher.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + // Override default, because our decrypted data does not return confounder + // Should eventually get rid of EType.decryptedData and + // EncryptedData.decryptedData altogether + public byte[] decryptedData(byte[] data) { + return data; + } +} diff --git a/src/sun/security/krb5/internal/crypto/Aes256.java b/src/sun/security/krb5/internal/crypto/Aes256.java new file mode 100644 index 00000000..2cefe54b --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Aes256.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.internal.crypto.dk.AesDkCrypto; +import sun.security.krb5.KrbCryptoException; +import java.security.GeneralSecurityException; + +/** + * Class with static methods for doing AES operations. + * + * @author Seema Malkani + */ + +public class Aes256 { + private static final AesDkCrypto CRYPTO = new AesDkCrypto(256); + + private Aes256() { + } + + public static byte[] stringToKey(char[] password, String salt, byte[] params) + throws GeneralSecurityException { + return CRYPTO.stringToKey(password, salt, params); + } + + // in bytes + public static int getChecksumLength() { + return CRYPTO.getChecksumLength(); + } + + public static byte[] calculateChecksum(byte[] baseKey, int usage, + byte[] input, int start, int len) throws GeneralSecurityException { + return CRYPTO.calculateChecksum(baseKey, usage, input, start, len); + } + + public static byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encrypt(baseKey, usage, ivec, null /* new_ivec */, + plaintext, start, len); + } + + /* Encrypt plaintext; do not add confounder, padding, or checksum */ + public static byte[] encryptRaw(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encryptRaw(baseKey, usage, ivec, plaintext, start, len); + } + + public static byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decrypt(baseKey, usage, ivec, ciphertext, start, len); + } + + /* + * Decrypt ciphertext; do not remove confounder, padding, or check + * checksum + */ + public static byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decryptRaw(baseKey, usage, ivec, ciphertext, start, len); + } +}; diff --git a/src/sun/security/krb5/internal/crypto/Aes256CtsHmacSha1EType.java b/src/sun/security/krb5/internal/crypto/Aes256CtsHmacSha1EType.java new file mode 100644 index 00000000..aa52e716 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Aes256CtsHmacSha1EType.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import java.security.GeneralSecurityException; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Checksum; + +/* + * This class encapsulates the encryption type for AES256 + * + * @author Seema Malkani + */ + +public final class Aes256CtsHmacSha1EType extends EType { + + public int eType() { + return EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96; + } + + public int minimumPadSize() { + return 0; + } + + public int confounderSize() { + return blockSize(); + } + + public int checksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256; + } + + public int checksumSize() { + return Aes256.getChecksumLength(); + } + + public int blockSize() { + return 16; + } + + public int keyType() { + return Krb5.KEYTYPE_AES; + } + + public int keySize() { + return 32; // bytes + } + + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return encrypt(data, key, ivec, usage); + } + + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, int usage) + throws KrbCryptoException { + try { + return Aes256.encrypt(key, usage, ivec, data, 0, data.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return decrypt(cipher, key, ivec, usage); + } + + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException, KrbCryptoException { + try { + return Aes256.decrypt(key, usage, ivec, cipher, 0, cipher.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + // Override default, because our decrypted data does not return confounder + // Should eventually get rid of EType.decryptedData and + // EncryptedData.decryptedData altogether + public byte[] decryptedData(byte[] data) { + return data; + } +} diff --git a/src/sun/security/krb5/internal/crypto/ArcFourHmac.java b/src/sun/security/krb5/internal/crypto/ArcFourHmac.java new file mode 100644 index 00000000..cdde62ec --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/ArcFourHmac.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.internal.crypto.dk.ArcFourCrypto; +import sun.security.krb5.KrbCryptoException; +import java.security.GeneralSecurityException; + +/** + * Class with static methods for doing RC4-HMAC operations. + * + * @author Seema Malkani + */ + +public class ArcFourHmac { + private static final ArcFourCrypto CRYPTO = new ArcFourCrypto(128); + + private ArcFourHmac() { + } + + public static byte[] stringToKey(char[] password) + throws GeneralSecurityException { + return CRYPTO.stringToKey(password); + } + + // in bytes + public static int getChecksumLength() { + return CRYPTO.getChecksumLength(); + } + + public static byte[] calculateChecksum(byte[] baseKey, int usage, + byte[] input, int start, int len) throws GeneralSecurityException { + return CRYPTO.calculateChecksum(baseKey, usage, input, start, len); + } + + /* Encrypt Sequence Number */ + public static byte[] encryptSeq(byte[] baseKey, int usage, + byte[] checksum, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encryptSeq(baseKey, usage, checksum, plaintext, start, len); + } + + /* Decrypt Sequence Number */ + public static byte[] decryptSeq(byte[] baseKey, int usage, byte[] checksum, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.decryptSeq(baseKey, usage, checksum, ciphertext, start, len); + } + + public static byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encrypt(baseKey, usage, ivec, null /* new_ivec */, + plaintext, start, len); + } + + /* Encrypt plaintext; do not add confounder, or checksum */ + public static byte[] encryptRaw(byte[] baseKey, int usage, + byte[] seqNum, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encryptRaw(baseKey, usage, seqNum, plaintext, start, len); + } + + public static byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decrypt(baseKey, usage, ivec, ciphertext, start, len); + } + + /* Decrypt ciphertext; do not remove confounder, or check checksum */ + public static byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len, byte[] seqNum) + throws GeneralSecurityException { + return CRYPTO.decryptRaw(baseKey, usage, ivec, ciphertext, start, len, seqNum); + } +}; diff --git a/src/sun/security/krb5/internal/crypto/ArcFourHmacEType.java b/src/sun/security/krb5/internal/crypto/ArcFourHmacEType.java new file mode 100644 index 00000000..b613ecd6 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/ArcFourHmacEType.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import java.security.GeneralSecurityException; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Checksum; + +/* + * This class encapsulates the encryption type for RC4-HMAC + * + * @author Seema Malkani + */ + +public final class ArcFourHmacEType extends EType { + + public int eType() { + return EncryptedData.ETYPE_ARCFOUR_HMAC; + } + + public int minimumPadSize() { + return 1; + } + + public int confounderSize() { + return 8; + } + + public int checksumType() { + return Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR; + } + + public int checksumSize() { + return ArcFourHmac.getChecksumLength(); + } + + public int blockSize() { + return 1; + } + + public int keyType() { + return Krb5.KEYTYPE_ARCFOUR_HMAC; + } + + public int keySize() { + return 16; // bytes + } + + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return encrypt(data, key, ivec, usage); + } + + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, int usage) + throws KrbCryptoException { + try { + return ArcFourHmac.encrypt(key, usage, ivec, data, 0, data.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return decrypt(cipher, key, ivec, usage); + } + + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException, KrbCryptoException { + try { + return ArcFourHmac.decrypt(key, usage, ivec, cipher, 0, cipher.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + // Override default, because our decrypted data does not return confounder + // Should eventually get rid of EType.decryptedData and + // EncryptedData.decryptedData altogether + public byte[] decryptedData(byte[] data) { + return data; + } +} diff --git a/src/sun/security/krb5/internal/crypto/CksumType.java b/src/sun/security/krb5/internal/crypto/CksumType.java new file mode 100644 index 00000000..f5e1b9ca --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/CksumType.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Config; +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbException; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; + +public abstract class CksumType { + + private static boolean DEBUG = Krb5.DEBUG; + + public static CksumType getInstance(int cksumTypeConst) + throws KdcErrException { + CksumType cksumType = null; + String cksumTypeName = null; + switch (cksumTypeConst) { + case Checksum.CKSUMTYPE_CRC32: + cksumType = new Crc32CksumType(); + cksumTypeName = "sun.security.krb5.internal.crypto.Crc32CksumType"; + break; + case Checksum.CKSUMTYPE_DES_MAC: + cksumType = new DesMacCksumType(); + cksumTypeName = "sun.security.krb5.internal.crypto.DesMacCksumType"; + break; + case Checksum.CKSUMTYPE_DES_MAC_K: + cksumType = new DesMacKCksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.DesMacKCksumType"; + break; + case Checksum.CKSUMTYPE_RSA_MD5: + cksumType = new RsaMd5CksumType(); + cksumTypeName = "sun.security.krb5.internal.crypto.RsaMd5CksumType"; + break; + case Checksum.CKSUMTYPE_RSA_MD5_DES: + cksumType = new RsaMd5DesCksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.RsaMd5DesCksumType"; + break; + + case Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD: + cksumType = new HmacSha1Des3KdCksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.HmacSha1Des3KdCksumType"; + break; + + case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128: + cksumType = new HmacSha1Aes128CksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.HmacSha1Aes128CksumType"; + break; + case Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256: + cksumType = new HmacSha1Aes256CksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.HmacSha1Aes256CksumType"; + break; + + case Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR: + cksumType = new HmacMd5ArcFourCksumType(); + cksumTypeName = + "sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType"; + break; + + // currently we don't support MD4. + case Checksum.CKSUMTYPE_RSA_MD4_DES_K: + // cksumType = new RsaMd4DesKCksumType(); + // cksumTypeName = + // "sun.security.krb5.internal.crypto.RsaMd4DesKCksumType"; + case Checksum.CKSUMTYPE_RSA_MD4: + // cksumType = new RsaMd4CksumType(); + // linux box support rsamd4, how to solve conflict? + // cksumTypeName = + // "sun.security.krb5.internal.crypto.RsaMd4CksumType"; + case Checksum.CKSUMTYPE_RSA_MD4_DES: + // cksumType = new RsaMd4DesCksumType(); + // cksumTypeName = + // "sun.security.krb5.internal.crypto.RsaMd4DesCksumType"; + + default: + throw new KdcErrException(Krb5.KDC_ERR_SUMTYPE_NOSUPP); + } + if (DEBUG) { + System.out.println(">>> CksumType: " + cksumTypeName); + } + return cksumType; + } + + + /** + * Returns default checksum type. + */ + public static CksumType getInstance() throws KdcErrException { + // this method provided for Kerberos applications. + int cksumType = Checksum.CKSUMTYPE_RSA_MD5; // default + try { + Config c = Config.getInstance(); + if ((cksumType = (Config.getType(c.get("libdefaults", + "ap_req_checksum_type")))) == - 1) { + if ((cksumType = Config.getType(c.get("libdefaults", + "checksum_type"))) == -1) { + cksumType = Checksum.CKSUMTYPE_RSA_MD5; // default + } + } + } catch (KrbException e) { + } + return getInstance(cksumType); + } + + public abstract int confounderSize(); + + public abstract int cksumType(); + + public abstract boolean isSafe(); + + public abstract int cksumSize(); + + public abstract int keyType(); + + public abstract int keySize(); + + public abstract byte[] calculateChecksum(byte[] data, int size) + throws KrbCryptoException; + + public abstract byte[] calculateKeyedChecksum(byte[] data, int size, + byte[] key, int usage) throws KrbCryptoException; + + public abstract boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException; + + public static boolean isChecksumEqual(byte[] cksum1, byte[] cksum2) { + if (cksum1 == cksum2) + return true; + if ((cksum1 == null && cksum2 != null) || + (cksum1 != null && cksum2 == null)) + return false; + if (cksum1.length != cksum2.length) + return false; + for (int i = 0; i < cksum1.length; i++) + if (cksum1[i] != cksum2[i]) + return false; + return true; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/Crc32CksumType.java b/src/sun/security/krb5/internal/crypto/Crc32CksumType.java new file mode 100644 index 00000000..c72a021a --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Crc32CksumType.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; + +public class Crc32CksumType extends CksumType { + + public Crc32CksumType() { + } + + public int confounderSize() { + return 0; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_CRC32; + } + + public boolean isSafe() { + return false; + } + + public int cksumSize() { + return 4; + } + + public int keyType() { + return Krb5.KEYTYPE_NULL; + } + + public int keySize() { + return 0; + } + + public byte[] calculateChecksum(byte[] data, int size) { + return crc32.byte2crc32sum_bytes(data, size); + } + + public byte[] calculateKeyedChecksum(byte[] data, int size, + byte[] key, int usage) { + return null; + } + + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) { + return false; + } + + public static byte[] int2quad(long input) { + byte[] output = new byte[4]; + for (int i = 0; i < 4; i++) { + output[i] = (byte)((input >>> (i * 8)) & 0xff); + } + return output; + } + + public static long bytes2long(byte[] input) { + long result = 0; + + result |= (((long)input[0]) & 0xffL) << 24; + result |= (((long)input[1]) & 0xffL) << 16; + result |= (((long)input[2]) & 0xffL) << 8; + result |= (((long)input[3]) & 0xffL); + return result; + } +} diff --git a/src/sun/security/krb5/internal/crypto/Des.java b/src/sun/security/krb5/internal/crypto/Des.java new file mode 100644 index 00000000..73dbc55b --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Des.java @@ -0,0 +1,368 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SecretKey; +import java.security.GeneralSecurityException; +import javax.crypto.spec.IvParameterSpec; +import sun.security.krb5.KrbCryptoException; +import java.util.Arrays; +import sun.security.action.GetPropertyAction; + +public final class Des { + + // RFC 3961 demands that UTF-8 encoding be used in DES's + // string-to-key function. For historical reasons, some + // implementations use a locale-specific encoding. Even + // so, when the client and server use different locales, + // they must agree on a common value, normally the one + // used when the password is set/reset. + // + // The following system property is provided to perform the + // string-to-key encoding. When set, the specified charset + // name is used. Otherwise, the system default charset. + + private final static String CHARSET = + java.security.AccessController.doPrivileged( + new GetPropertyAction("sun.security.krb5.msinterop.des.s2kcharset")); + + private static final long[] bad_keys = { + 0x0101010101010101L, 0xfefefefefefefefeL, + 0x1f1f1f1f1f1f1f1fL, 0xe0e0e0e0e0e0e0e0L, + 0x01fe01fe01fe01feL, 0xfe01fe01fe01fe01L, + 0x1fe01fe00ef10ef1L, 0xe01fe01ff10ef10eL, + 0x01e001e001f101f1L, 0xe001e001f101f101L, + 0x1ffe1ffe0efe0efeL, 0xfe1ffe1ffe0efe0eL, + 0x011f011f010e010eL, 0x1f011f010e010e01L, + 0xe0fee0fef1fef1feL, 0xfee0fee0fef1fef1L + }; + + private static final byte[] good_parity = { + 1, 1, 2, 2, 4, 4, 7, 7, + 8, 8, 11, 11, 13, 13, 14, 14, + 16, 16, 19, 19, 21, 21, 22, 22, + 25, 25, 26, 26, 28, 28, 31, 31, + 32, 32, 35, 35, 37, 37, 38, 38, + 41, 41, 42, 42, 44, 44, 47, 47, + 49, 49, 50, 50, 52, 52, 55, 55, + 56, 56, 59, 59, 61, 61, 62, 62, + 64, 64, 67, 67, 69, 69, 70, 70, + 73, 73, 74, 74, 76, 76, 79, 79, + 81, 81, 82, 82, 84, 84, 87, 87, + 88, 88, 91, 91, 93, 93, 94, 94, + 97, 97, 98, 98, 100, 100, 103, 103, + 104, 104, 107, 107, 109, 109, 110, 110, + 112, 112, 115, 115, 117, 117, 118, 118, + 121, 121, 122, 122, 124, 124, 127, 127, + (byte)128, (byte)128, (byte)131, (byte)131, + (byte)133, (byte)133, (byte)134, (byte)134, + (byte)137, (byte)137, (byte)138, (byte)138, + (byte)140, (byte)140, (byte)143, (byte)143, + (byte)145, (byte)145, (byte)146, (byte)146, + (byte)148, (byte)148, (byte)151, (byte)151, + (byte)152, (byte)152, (byte)155, (byte)155, + (byte)157, (byte)157, (byte)158, (byte)158, + (byte)161, (byte)161, (byte)162, (byte)162, + (byte)164, (byte)164, (byte)167, (byte)167, + (byte)168, (byte)168, (byte)171, (byte)171, + (byte)173, (byte)173, (byte)174, (byte)174, + (byte)176, (byte)176, (byte)179, (byte)179, + (byte)181, (byte)181, (byte)182, (byte)182, + (byte)185, (byte)185, (byte)186, (byte)186, + (byte)188, (byte)188, (byte)191, (byte)191, + (byte)193, (byte)193, (byte)194, (byte)194, + (byte)196, (byte)196, (byte)199, (byte)199, + (byte)200, (byte)200, (byte)203, (byte)203, + (byte)205, (byte)205, (byte)206, (byte)206, + (byte)208, (byte)208, (byte)211, (byte)211, + (byte)213, (byte)213, (byte)214, (byte)214, + (byte)217, (byte)217, (byte)218, (byte)218, + (byte)220, (byte)220, (byte)223, (byte)223, + (byte)224, (byte)224, (byte)227, (byte)227, + (byte)229, (byte)229, (byte)230, (byte)230, + (byte)233, (byte)233, (byte)234, (byte)234, + (byte)236, (byte)236, (byte)239, (byte)239, + (byte)241, (byte)241, (byte)242, (byte)242, + (byte)244, (byte)244, (byte)247, (byte)247, + (byte)248, (byte)248, (byte)251, (byte)251, + (byte)253, (byte)253, (byte)254, (byte)254 + }; + + public static final byte[] set_parity(byte[] key) { + for (int i=0; i < 8; i++) { + key[i] = good_parity[key[i] & 0xff]; + } + return key; + } + + public static final long set_parity(long key) { + return octet2long(set_parity(long2octet(key))); + } + + public static final boolean bad_key(long key) { + for (int i = 0; i < bad_keys.length; i++) { + if (bad_keys[i] == key) { + return true; + } + } + return false; + } + + public static final boolean bad_key(byte[] key) { + return bad_key(octet2long(key)); + } + + public static long octet2long(byte[] input) { + return octet2long(input, 0); + } + + public static long octet2long(byte[] input, int offset) { //convert a 8-byte to a long + long result = 0; + for (int i = 0; i < 8; i++) { + if (i + offset < input.length) { + result |= (((long)input[i + offset]) & 0xffL) << ((7 - i) * 8); + } + } + return result; + } + + public static byte[] long2octet(long input) { + byte[] output = new byte[8]; + for (int i = 0; i < 8; i++) { + output[i] = (byte)((input >>> ((7 - i) * 8)) & 0xffL); + } + return output; + } + + public static void long2octet(long input, byte[] output) { + long2octet(input, output, 0); + } + + public static void long2octet(long input, byte[] output, int offset) { + for (int i = 0; i < 8; i++) { + if (i + offset < output.length) { + output[i + offset] = + (byte)((input >>> ((7 - i) * 8)) & 0xffL); + } + } + } + + /** + * Creates a DES cipher in Electronic Codebook mode, with no padding. + * @param input plain text. + * @param output the buffer for the result. + * @param key DES the key to encrypt the text. + * @param ivec initialization vector. + * + * @created by Yanni Zhang, Dec 6 99. + */ + public static void cbc_encrypt ( + byte[] input, + byte[] output, + byte[] key, + byte[] ivec, + boolean encrypt) throws KrbCryptoException { + + Cipher cipher = null; + + try { + cipher = Cipher.getInstance("DES/CBC/NoPadding"); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException("JCE provider may not be installed. " + + e.getMessage()); + ke.initCause(e); + throw ke; + } + IvParameterSpec params = new IvParameterSpec(ivec); + SecretKeySpec skSpec = new SecretKeySpec(key, "DES"); + try { + SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); + // SecretKey sk = skf.generateSecret(skSpec); + SecretKey sk = (SecretKey) skSpec; + if (encrypt) + cipher.init(Cipher.ENCRYPT_MODE, sk, params); + else + cipher.init(Cipher.DECRYPT_MODE, sk, params); + byte[] result; + result = cipher.doFinal(input); + System.arraycopy(result, 0, output, 0, result.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + /** + * Generates DES key from the password. + * @param password a char[] used to create the key. + * @return DES key. + * + * @modified by Yanni Zhang, Dec 6, 99 + */ + public static long char_to_key(char[] passwdChars) throws KrbCryptoException { + long key = 0; + long octet, octet1, octet2 = 0; + byte[] cbytes = null; + + // Convert password to byte array + try { + if (CHARSET == null) { + cbytes = (new String(passwdChars)).getBytes(); + } else { + cbytes = (new String(passwdChars)).getBytes(CHARSET); + } + } catch (Exception e) { + // clear-up sensitive information + if (cbytes != null) { + Arrays.fill(cbytes, 0, cbytes.length, (byte) 0); + } + KrbCryptoException ce = + new KrbCryptoException("Unable to convert passwd, " + e); + ce.initCause(e); + throw ce; + } + + // pad data + byte[] passwdBytes = pad(cbytes); + + byte[] newkey = new byte[8]; + int length = (passwdBytes.length / 8) + (passwdBytes.length % 8 == 0 ? 0 : 1); + for (int i = 0; i < length; i++) { + octet = octet2long(passwdBytes, i * 8) & 0x7f7f7f7f7f7f7f7fL; + if (i % 2 == 1) { + octet1 = 0; + for (int j = 0; j < 64; j++) { + octet1 |= ((octet & (1L << j)) >>> j) << (63 - j); + } + octet = octet1 >>> 1; + } + key ^= (octet << 1); + } + key = set_parity(key); + if (bad_key(key)) { + byte [] temp = long2octet(key); + temp[7] ^= 0xf0; + key = octet2long(temp); + } + + newkey = des_cksum(long2octet(key), passwdBytes, long2octet(key)); + key = octet2long(set_parity(newkey)); + if (bad_key(key)) { + byte [] temp = long2octet(key); + temp[7] ^= 0xf0; + key = octet2long(temp); + } + + // clear-up sensitive information + if (cbytes != null) { + Arrays.fill(cbytes, 0, cbytes.length, (byte) 0); + } + if (passwdBytes != null) { + Arrays.fill(passwdBytes, 0, passwdBytes.length, (byte) 0); + } + + return key; + } + + /** + * Encrypts the message blocks using DES CBC and output the + * final block of 8-byte ciphertext. + * @param ivec Initialization vector. + * @param msg Input message as an byte array. + * @param key DES key to encrypt the message. + * @return the last block of ciphertext. + * + * @created by Yanni Zhang, Dec 6, 99. + */ + public static byte[] des_cksum(byte[] ivec, byte[] msg, byte[] key) throws KrbCryptoException { + Cipher cipher = null; + + byte[] result = new byte[8]; + try{ + cipher = Cipher.getInstance("DES/CBC/NoPadding"); + } catch (Exception e) { + KrbCryptoException ke = new KrbCryptoException("JCE provider may not be installed. " + + e.getMessage()); + ke.initCause(e); + throw ke; + } + IvParameterSpec params = new IvParameterSpec(ivec); + SecretKeySpec skSpec = new SecretKeySpec(key, "DES"); + try { + SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); + // SecretKey sk = skf.generateSecret(skSpec); + SecretKey sk = (SecretKey) skSpec; + cipher.init(Cipher.ENCRYPT_MODE, sk, params); + for (int i = 0; i < msg.length / 8; i++) { + result = cipher.doFinal(msg, i * 8, 8); + cipher.init(Cipher.ENCRYPT_MODE, sk, (new IvParameterSpec(result))); + } + } + catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + return result; + } + + /** + * Pads the data so that its length is a multiple of 8 bytes. + * @param data the raw data. + * @return the data being padded. + * + * @created by Yanni Zhang, Dec 6 99. //Kerberos does not use PKCS5 padding. + */ + static byte[] pad(byte[] data) { + int len; + if (data.length < 8) len = data.length; + else len = data.length % 8; + if (len == 0) return data; + else { + byte[] padding = new byte[ 8 - len + data.length]; + for (int i = padding.length - 1; i > data.length - 1; i--) { + padding[i] = 0; + } + System.arraycopy(data, 0, padding, 0, data.length); + return padding; + } + } + + // Caller is responsible for clearing password + public static byte[] string_to_key_bytes(char[] passwdChars) + throws KrbCryptoException { + return long2octet(char_to_key(passwdChars)); + } +} diff --git a/src/sun/security/krb5/internal/crypto/Des3.java b/src/sun/security/krb5/internal/crypto/Des3.java new file mode 100644 index 00000000..09d8877b --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Des3.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.internal.crypto.dk.Des3DkCrypto; +import sun.security.krb5.KrbCryptoException; +import java.security.GeneralSecurityException; + +/** + * Class with static methods for doing Triple DES operations. + */ +public class Des3 { + private static final Des3DkCrypto CRYPTO = new Des3DkCrypto(); + + private Des3() { + } + + public static byte[] stringToKey(char[] chars) + throws GeneralSecurityException { + return CRYPTO.stringToKey(chars); + } + + public static byte[] parityFix(byte[] value) + throws GeneralSecurityException { + return CRYPTO.parityFix(value); + } + + // in bytes + public static int getChecksumLength() { + return CRYPTO.getChecksumLength(); + } + + public static byte[] calculateChecksum(byte[] baseKey, int usage, + byte[] input, int start, int len) throws GeneralSecurityException { + return CRYPTO.calculateChecksum(baseKey, usage, input, start, len); + } + + public static byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encrypt(baseKey, usage, ivec, null /* new_ivec */, + plaintext, start, len); + } + + /* Encrypt plaintext; do not add confounder, padding, or checksum */ + public static byte[] encryptRaw(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + return CRYPTO.encryptRaw(baseKey, usage, ivec, plaintext, start, len); + } + + public static byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decrypt(baseKey, usage, ivec, ciphertext, start, len); + } + + /** + * Decrypt ciphertext; do not remove confounder, padding, + * or check checksum + */ + public static byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + return CRYPTO.decryptRaw(baseKey, usage, ivec, ciphertext, start, len); + } +}; diff --git a/src/sun/security/krb5/internal/crypto/Des3CbcHmacSha1KdEType.java b/src/sun/security/krb5/internal/crypto/Des3CbcHmacSha1KdEType.java new file mode 100644 index 00000000..b0798356 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Des3CbcHmacSha1KdEType.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import java.security.GeneralSecurityException; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.Checksum; + +public final class Des3CbcHmacSha1KdEType extends EType { + + public int eType() { + return EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD; + } + + public int minimumPadSize() { + return 0; + } + + public int confounderSize() { + return blockSize(); + } + + public int checksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD; + } + + public int checksumSize() { + return Des3.getChecksumLength(); + } + + public int blockSize() { + return 8; + } + + public int keyType() { + return Krb5.KEYTYPE_DES3; + } + + public int keySize() { + return 24; // bytes + } + + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + byte[] ivec = new byte[blockSize()]; + return encrypt(data, key, ivec, usage); + } + + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, int usage) + throws KrbCryptoException { + try { + return Des3.encrypt(key, usage, ivec, data, 0, data.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException{ + byte[] ivec = new byte[blockSize()]; + return decrypt(cipher, key, ivec, usage); + } + + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException, KrbCryptoException { + try { + return Des3.decrypt(key, usage, ivec, cipher, 0, cipher.length); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + // Override default, because our decrypted data does not return confounder + // Should eventually get rid of EType.decryptedData and + // EncryptedData.decryptedData altogether + public byte[] decryptedData(byte[] data) { + return data; + } +} diff --git a/src/sun/security/krb5/internal/crypto/DesCbcCrcEType.java b/src/sun/security/krb5/internal/crypto/DesCbcCrcEType.java new file mode 100644 index 00000000..08d9d555 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/DesCbcCrcEType.java @@ -0,0 +1,92 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; + +public class DesCbcCrcEType extends DesCbcEType { + + public DesCbcCrcEType() { + } + + public int eType() { + return EncryptedData.ETYPE_DES_CBC_CRC; + } + + public int minimumPadSize() { + return 4; + } + + public int confounderSize() { + return 8; + } + + public int checksumType() { + return Checksum.CKSUMTYPE_CRC32; + } + + public int checksumSize() { + return 4; + } + + /** + * Encrypts data using DES in CBC mode with CRC32. + * @param data the data to be encrypted. + * @param key the secret key to encrypt the data. It is also used as initialization vector during cipher block chaining. + * @return the buffer for cipher text. + * + * @written by Yanni Zhang, Dec 10, 1999 + */ + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + return encrypt(data, key, key, usage); + } + + /** + * Decrypts data with provided key using DES in CBC mode with CRC32. + * @param cipher the cipher text to be decrypted. + * @param key the secret key to decrypt the data. + * + * @written by Yanni Zhang, Dec 10, 1999 + */ + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException{ + return decrypt(cipher, key, key, usage); + } + + protected byte[] calculateChecksum(byte[] data, int size) { + return crc32.byte2crc32sum_bytes(data, size); + } + +} diff --git a/src/sun/security/krb5/internal/crypto/DesCbcEType.java b/src/sun/security/krb5/internal/crypto/DesCbcEType.java new file mode 100644 index 00000000..9c6ce647 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/DesCbcEType.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Confounder; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; + +abstract class DesCbcEType extends EType { + protected abstract byte[] calculateChecksum(byte[] data, int size) + throws KrbCryptoException; + + public int blockSize() { + return 8; + } + + public int keyType() { + return Krb5.KEYTYPE_DES; + } + + public int keySize() { + return 8; + } + + /** + * Encrypts the data using DES in CBC mode. + * @param data the buffer for plain text. + * @param key the key to encrypt the data. + * @return the buffer for encrypted data. + * + * @written by Yanni Zhang, Dec 6 99. + */ + + public byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException { + byte[] ivec = new byte[keySize()]; + return encrypt(data, key, ivec, usage); + } + + /** + * Encrypts the data using DES in CBC mode. + * @param data the buffer for plain text. + * @param key the key to encrypt the data. + * @param ivec initialization vector. + * @return buffer for encrypted data. + * + * @modified by Yanni Zhang, Feb 24 00. + */ + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, + int usage) throws KrbCryptoException { + + /* + * To meet export control requirements, double check that the + * key being used is no longer than 64 bits. + * + * Note that from a protocol point of view, an + * algorithm that is not DES will be rejected before this + * point. Also, a DES key that is not 64 bits will be + * rejected by a good implementations of JCE. + */ + if (key.length > 8) + throw new KrbCryptoException("Invalid DES Key!"); + + int new_size = data.length + confounderSize() + checksumSize(); + byte[] new_data; + byte pad; + /*Data padding: using Kerberos 5 GSS-API mechanism (1.2.2.3), Jun 1996. + *Before encryption, plain text data is padded to the next highest multiple of blocksize. + *by appending between 1 and 8 bytes, the value of each such byte being the total number + *of pad bytes. For example, if new_size = 10, blockSize is 8, we should pad 2 bytes, + *and the value of each byte is 2. + *If plaintext data is a multiple of blocksize, we pad a 8 bytes of 8. + */ + if (new_size % blockSize() == 0) { + new_data = new byte[new_size + blockSize()]; + pad = (byte)8; + } + else { + new_data = new byte[new_size + blockSize() - new_size % blockSize()]; + pad = (byte)(blockSize() - new_size % blockSize()); + } + for (int i = new_size; i < new_data.length; i++) { + new_data[i] = pad; + } + byte[] conf = Confounder.bytes(confounderSize()); + System.arraycopy(conf, 0, new_data, 0, confounderSize()); + System.arraycopy(data, 0, new_data, startOfData(), data.length); + byte[] cksum = calculateChecksum(new_data, new_data.length); + System.arraycopy(cksum, 0, new_data, startOfChecksum(), + checksumSize()); + byte[] cipher = new byte[new_data.length]; + Des.cbc_encrypt(new_data, cipher, key, ivec, true); + return cipher; + } + + /** + * Decrypts the data using DES in CBC mode. + * @param cipher the input buffer. + * @param key the key to decrypt the data. + * + * @written by Yanni Zhang, Dec 6 99. + */ + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException{ + byte[] ivec = new byte[keySize()]; + return decrypt(cipher, key, ivec, usage); + } + + /** + * Decrypts the data using DES in CBC mode. + * @param cipher the input buffer. + * @param key the key to decrypt the data. + * @param ivec initialization vector. + * + * @modified by Yanni Zhang, Dec 6 99. + */ + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException, KrbCryptoException { + + /* + * To meet export control requirements, double check that the + * key being used is no longer than 64 bits. + * + * Note that from a protocol point of view, an + * algorithm that is not DES will be rejected before this + * point. Also, a DES key that is not 64 bits will be + * rejected by a good JCE provider. + */ + if (key.length > 8) + throw new KrbCryptoException("Invalid DES Key!"); + + byte[] data = new byte[cipher.length]; + Des.cbc_encrypt(cipher, data, key, ivec, false); + if (!isChecksumValid(data)) + throw new KrbApErrException(Krb5.KRB_AP_ERR_BAD_INTEGRITY); + return data; + } + + private void copyChecksumField(byte[] data, byte[] cksum) { + for (int i = 0; i < checksumSize(); i++) + data[startOfChecksum() + i] = cksum[i]; + } + + private byte[] checksumField(byte[] data) { + byte[] result = new byte[checksumSize()]; + for (int i = 0; i < checksumSize(); i++) + result[i] = data[startOfChecksum() + i]; + return result; + } + + private void resetChecksumField(byte[] data) { + for (int i = startOfChecksum(); i < startOfChecksum() + + checksumSize(); i++) + data[i] = 0; + } + + /* + // Not used. + public void setChecksum(byte[] data, int size) throws KrbCryptoException{ + resetChecksumField(data); + byte[] cksum = calculateChecksum(data, size); + copyChecksumField(data, cksum); + } +*/ + + private byte[] generateChecksum(byte[] data) throws KrbCryptoException{ + byte[] cksum1 = checksumField(data); + resetChecksumField(data); + byte[] cksum2 = calculateChecksum(data, data.length); + copyChecksumField(data, cksum1); + return cksum2; + } + + private boolean isChecksumEqual(byte[] cksum1, byte[] cksum2) { + if (cksum1 == cksum2) + return true; + if ((cksum1 == null && cksum2 != null) || + (cksum1 != null && cksum2 == null)) + return false; + if (cksum1.length != cksum2.length) + return false; + for (int i = 0; i < cksum1.length; i++) + if (cksum1[i] != cksum2[i]) + return false; + return true; + } + + protected boolean isChecksumValid(byte[] data) throws KrbCryptoException { + byte[] cksum1 = checksumField(data); + byte[] cksum2 = generateChecksum(data); + return isChecksumEqual(cksum1, cksum2); + } +} diff --git a/src/sun/security/krb5/internal/crypto/DesCbcMd5EType.java b/src/sun/security/krb5/internal/crypto/DesCbcMd5EType.java new file mode 100644 index 00000000..cfe73d14 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/DesCbcMd5EType.java @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.KrbCryptoException; +import java.security.MessageDigest; + +public final class DesCbcMd5EType extends DesCbcEType { + + public DesCbcMd5EType() { + } + + public int eType() { + return EncryptedData.ETYPE_DES_CBC_MD5; + } + + public int minimumPadSize() { + return 0; + } + + public int confounderSize() { + return 8; + } + + public int checksumType() { + return Checksum.CKSUMTYPE_RSA_MD5; + } + + public int checksumSize() { + return 16; + } + + /** + * Calculates checksum using MD5. + * @param data the input data. + * @param size the length of data. + * @return the checksum. + * + * @modified by Yanni Zhang, 12/06/99. + */ + protected byte[] calculateChecksum(byte[] data, int size) + throws KrbCryptoException { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new KrbCryptoException("JCE provider may not be installed. " + e.getMessage()); + } + try { + md5.update(data); + return(md5.digest()); + } catch (Exception e) { + throw new KrbCryptoException(e.getMessage()); + } + } +} diff --git a/src/sun/security/krb5/internal/crypto/DesMacCksumType.java b/src/sun/security/krb5/internal/crypto/DesMacCksumType.java new file mode 100644 index 00000000..3c3842f5 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/DesMacCksumType.java @@ -0,0 +1,184 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.Confounder; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; +import java.security.InvalidKeyException; + +public class DesMacCksumType extends CksumType { + + public DesMacCksumType() { + } + + public int confounderSize() { + return 8; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_DES_MAC; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 16; + } + + public int keyType() { + return Krb5.KEYTYPE_DES; + } + + public int keySize() { + return 8; + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + byte[] new_data = new byte[size + confounderSize()]; + byte[] conf = Confounder.bytes(confounderSize()); + System.arraycopy(conf, 0, new_data, 0, confounderSize()); + System.arraycopy(data, 0, new_data, confounderSize(), size); + + //check for weak keys + try { + if (DESKeySpec.isWeak(key, 0)) { + key[7] = (byte)(key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] residue_ivec = new byte[key.length]; + byte[] residue = Des.des_cksum(residue_ivec, new_data, key); + byte[] cksum = new byte[cksumSize()]; + System.arraycopy(conf, 0, cksum, 0, confounderSize()); + System.arraycopy(residue, 0, cksum, confounderSize(), + cksumSize() - confounderSize()); + + byte[] new_key = new byte[keySize()]; + System.arraycopy(key, 0, new_key, 0, key.length); + for (int i = 0; i < new_key.length; i++) + new_key[i] = (byte)(new_key[i] ^ 0xf0); + //check for weak keys + try { + if (DESKeySpec.isWeak(new_key, 0)) { + new_key[7] = (byte)(new_key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[new_key.length]; + + //des-cbc encrypt + byte[] enc_cksum = new byte[cksum.length]; + Des.cbc_encrypt(cksum, enc_cksum, new_key, ivec, true); + return enc_cksum; + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + byte[] cksum = decryptKeyedChecksum(checksum, key); + + byte[] new_data = new byte[size + confounderSize()]; + System.arraycopy(cksum, 0, new_data, 0, confounderSize()); + System.arraycopy(data, 0, new_data, confounderSize(), size); + + //check for weak keys + try { + if (DESKeySpec.isWeak(key, 0)) { + key[7] = (byte)(key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[key.length]; + byte[] new_cksum = Des.des_cksum(ivec, new_data, key); + byte[] orig_cksum = new byte[cksumSize() - confounderSize()]; + System.arraycopy(cksum, confounderSize(), orig_cksum, 0, + cksumSize() - confounderSize()); + return isChecksumEqual(orig_cksum, new_cksum); + } + + /** + * Decrypts keyed checksum. + * @param enc_cksum the buffer for encrypted checksum. + * @param key the key. + * @return the checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + private byte[] decryptKeyedChecksum(byte[] enc_cksum, byte[] key) throws KrbCryptoException { + byte[] new_key = new byte[keySize()]; + System.arraycopy(key, 0, new_key, 0, key.length); + for (int i = 0; i < new_key.length; i++) + new_key[i] = (byte)(new_key[i] ^ 0xf0); + //check for weak keys + try { + if (DESKeySpec.isWeak(new_key, 0)) { + new_key[7] = (byte)(new_key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[new_key.length]; + byte[] cksum = new byte[enc_cksum.length]; + Des.cbc_encrypt(enc_cksum, cksum, new_key, ivec, false); + return cksum; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/DesMacKCksumType.java b/src/sun/security/krb5/internal/crypto/DesMacKCksumType.java new file mode 100644 index 00000000..805f931e --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/DesMacKCksumType.java @@ -0,0 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; +import java.security.InvalidKeyException; + +public class DesMacKCksumType extends CksumType { + + public DesMacKCksumType() { + } + + public int confounderSize() { + return 0; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_DES_MAC_K; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 16; + } + + public int keyType() { + return Krb5.KEYTYPE_DES; + } + + public int keySize() { + return 8; + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + //check for weak keys + try { + if (DESKeySpec.isWeak(key, 0)) { + key[7] = (byte)(key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[key.length]; + System.arraycopy(key, 0, ivec, 0, key.length); + byte[] cksum = Des.des_cksum(ivec, data, key); + return cksum; + } + + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + byte[] new_cksum = calculateKeyedChecksum(data, data.length, key, usage); + return isChecksumEqual(checksum, new_cksum); + } + +} diff --git a/src/sun/security/krb5/internal/crypto/EType.java b/src/sun/security/krb5/internal/crypto/EType.java new file mode 100644 index 00000000..feed5d8b --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/EType.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.internal.*; +import sun.security.krb5.Config; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.KrbException; +import sun.security.krb5.KrbCryptoException; +import javax.crypto.*; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; + +//only needed if dataSize() implementation changes back to spec; +//see dataSize() below + +public abstract class EType { + + private static final boolean DEBUG = Krb5.DEBUG; + private static boolean allowWeakCrypto; + + static { + initStatic(); + } + + public static void initStatic() { + boolean allowed = false; + try { + Config cfg = Config.getInstance(); + String temp = cfg.get("libdefaults", "allow_weak_crypto"); + if (temp != null && temp.equals("true")) allowed = true; + } catch (Exception exc) { + if (DEBUG) { + System.out.println ("Exception in getting allow_weak_crypto, " + + "using default value " + + exc.getMessage()); + } + } + allowWeakCrypto = allowed; + } + + public static EType getInstance (int eTypeConst) + throws KdcErrException { + EType eType = null; + String eTypeName = null; + switch (eTypeConst) { + case EncryptedData.ETYPE_NULL: + eType = new NullEType(); + eTypeName = "sun.security.krb5.internal.crypto.NullEType"; + break; + case EncryptedData.ETYPE_DES_CBC_CRC: + eType = new DesCbcCrcEType(); + eTypeName = "sun.security.krb5.internal.crypto.DesCbcCrcEType"; + break; + case EncryptedData.ETYPE_DES_CBC_MD5: + eType = new DesCbcMd5EType(); + eTypeName = "sun.security.krb5.internal.crypto.DesCbcMd5EType"; + break; + + case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD: + eType = new Des3CbcHmacSha1KdEType(); + eTypeName = + "sun.security.krb5.internal.crypto.Des3CbcHmacSha1KdEType"; + break; + + case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96: + eType = new Aes128CtsHmacSha1EType(); + eTypeName = + "sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType"; + break; + + case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96: + eType = new Aes256CtsHmacSha1EType(); + eTypeName = + "sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType"; + break; + + case EncryptedData.ETYPE_ARCFOUR_HMAC: + eType = new ArcFourHmacEType(); + eTypeName = "sun.security.krb5.internal.crypto.ArcFourHmacEType"; + break; + + default: + String msg = "encryption type = " + toString(eTypeConst) + + " (" + eTypeConst + ")"; + throw new KdcErrException(Krb5.KDC_ERR_ETYPE_NOSUPP, msg); + } + if (DEBUG) { + System.out.println(">>> EType: " + eTypeName); + } + return eType; + } + + public abstract int eType(); + + public abstract int minimumPadSize(); + + public abstract int confounderSize(); + + public abstract int checksumType(); + + public abstract int checksumSize(); + + public abstract int blockSize(); + + public abstract int keyType(); + + public abstract int keySize(); + + public abstract byte[] encrypt(byte[] data, byte[] key, int usage) + throws KrbCryptoException; + + public abstract byte[] encrypt(byte[] data, byte[] key, byte[] ivec, + int usage) throws KrbCryptoException; + + public abstract byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException, KrbCryptoException; + + public abstract byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, + int usage) throws KrbApErrException, KrbCryptoException; + + public int dataSize(byte[] data) + // throws Asn1Exception + { + // EncodeRef ref = new EncodeRef(data, startOfData()); + // return ref.end - startOfData(); + // should be the above according to spec, but in fact + // implementations include the pad bytes in the data size + return data.length - startOfData(); + } + + public int padSize(byte[] data) { + return data.length - confounderSize() - checksumSize() - + dataSize(data); + } + + public int startOfChecksum() { + return confounderSize(); + } + + public int startOfData() { + return confounderSize() + checksumSize(); + } + + public int startOfPad(byte[] data) { + return confounderSize() + checksumSize() + dataSize(data); + } + + public byte[] decryptedData(byte[] data) { + int tempSize = dataSize(data); + byte[] result = new byte[tempSize]; + System.arraycopy(data, startOfData(), result, 0, tempSize); + return result; + } + + // Note: the first 2 entries of BUILTIN_ETYPES and BUILTIN_ETYPES_NOAES256 + // should be kept DES-related. They will be removed when allow_weak_crypto + // is set to false. + + private static final int[] BUILTIN_ETYPES = new int[] { + EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96, + EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96, + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD, + EncryptedData.ETYPE_ARCFOUR_HMAC, + EncryptedData.ETYPE_DES_CBC_CRC, + EncryptedData.ETYPE_DES_CBC_MD5, + }; + + private static final int[] BUILTIN_ETYPES_NOAES256 = new int[] { + EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96, + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD, + EncryptedData.ETYPE_ARCFOUR_HMAC, + EncryptedData.ETYPE_DES_CBC_CRC, + EncryptedData.ETYPE_DES_CBC_MD5, + }; + + + // used in Config + public static int[] getBuiltInDefaults() { + int allowed = 0; + try { + allowed = Cipher.getMaxAllowedKeyLength("AES"); + } catch (Exception e) { + // should not happen + } + int[] result; + if (allowed < 256) { + result = BUILTIN_ETYPES_NOAES256; + } else { + result = BUILTIN_ETYPES; + } + if (!allowWeakCrypto) { + // The last 2 etypes are now weak ones + return Arrays.copyOfRange(result, 0, result.length - 2); + } + return result; + } + + /** + * Retrieves the default etypes from the configuration file, or + * if that's not available, return the built-in list of default etypes. + * This result is always non-empty. If no etypes are found, + * an exception is thrown. + */ + public static int[] getDefaults(String configName) + throws KrbException { + Config config = null; + try { + config = Config.getInstance(); + } catch (KrbException exc) { + if (DEBUG) { + System.out.println("Exception while getting " + + configName + exc.getMessage()); + System.out.println("Using default builtin etypes"); + } + return getBuiltInDefaults(); + } + return config.defaultEtype(configName); + } + + /** + * Retrieve the default etypes from the configuration file for + * those etypes for which there are corresponding keys. + * Used in scenario we have some keys from a keytab with etypes + * different from those named in configName. Then, in order + * to decrypt an AS-REP, we should only ask for etypes for which + * we have keys. + */ + public static int[] getDefaults(String configName, EncryptionKey[] keys) + throws KrbException { + int[] answer = getDefaults(configName); + + List list = new ArrayList<>(answer.length); + for (int i = 0; i < answer.length; i++) { + if (EncryptionKey.findKey(answer[i], keys) != null) { + list.add(answer[i]); + } + } + int len = list.size(); + if (len <= 0) { + StringBuffer keystr = new StringBuffer(); + for (int i = 0; i < keys.length; i++) { + keystr.append(toString(keys[i].getEType())); + keystr.append(" "); + } + throw new KrbException( + "Do not have keys of types listed in " + configName + + " available; only have keys of following type: " + + keystr.toString()); + } else { + answer = new int[len]; + for (int i = 0; i < len; i++) { + answer[i] = list.get(i); + } + return answer; + } + } + + public static boolean isSupported(int eTypeConst, int[] config) { + for (int i = 0; i < config.length; i++) { + if (eTypeConst == config[i]) { + return true; + } + } + return false; + } + + public static boolean isSupported(int eTypeConst) { + int[] enabledETypes = getBuiltInDefaults(); + return isSupported(eTypeConst, enabledETypes); + } + + public static String toString(int type) { + switch (type) { + case 0: + return "NULL"; + case 1: + return "DES CBC mode with CRC-32"; + case 2: + return "DES CBC mode with MD4"; + case 3: + return "DES CBC mode with MD5"; + case 4: + return "reserved"; + case 5: + return "DES3 CBC mode with MD5"; + case 6: + return "reserved"; + case 7: + return "DES3 CBC mode with SHA1"; + case 9: + return "DSA with SHA1- Cms0ID"; + case 10: + return "MD5 with RSA encryption - Cms0ID"; + case 11: + return "SHA1 with RSA encryption - Cms0ID"; + case 12: + return "RC2 CBC mode with Env0ID"; + case 13: + return "RSA encryption with Env0ID"; + case 14: + return "RSAES-0AEP-ENV-0ID"; + case 15: + return "DES-EDE3-CBC-ENV-0ID"; + case 16: + return "DES3 CBC mode with SHA1-KD"; + case 17: + return "AES128 CTS mode with HMAC SHA1-96"; + case 18: + return "AES256 CTS mode with HMAC SHA1-96"; + case 23: + return "RC4 with HMAC"; + case 24: + return "RC4 with HMAC EXP"; + + } + return "Unknown (" + type + ")"; + } +} diff --git a/src/sun/security/krb5/internal/crypto/HmacMd5ArcFourCksumType.java b/src/sun/security/krb5/internal/crypto/HmacMd5ArcFourCksumType.java new file mode 100644 index 00000000..0b9f79bf --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/HmacMd5ArcFourCksumType.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; + +import java.security.GeneralSecurityException; + +/** + * This class encapsulates the checksum type for HMAC RC4 + * + * @author Seema Malkani + */ + +public class HmacMd5ArcFourCksumType extends CksumType { + + public HmacMd5ArcFourCksumType() { + } + + public int confounderSize() { + return 8; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 16; // bytes + } + + public int keyType() { + return Krb5.KEYTYPE_ARCFOUR_HMAC; + } + + public int keySize() { + return 16; // bytes + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + + try { + return ArcFourHmac.calculateChecksum(key, usage, data, 0, size); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + + try { + byte[] newCksum = ArcFourHmac.calculateChecksum(key, usage, + data, 0, size); + + return isChecksumEqual(checksum, newCksum); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } +} diff --git a/src/sun/security/krb5/internal/crypto/HmacSha1Aes128CksumType.java b/src/sun/security/krb5/internal/crypto/HmacSha1Aes128CksumType.java new file mode 100644 index 00000000..d0832476 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/HmacSha1Aes128CksumType.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; + +import java.security.GeneralSecurityException; + +/* + * This class encapsulates the checksum type for AES128 + * + * @author Seema Malkani + */ + +public class HmacSha1Aes128CksumType extends CksumType { + + public HmacSha1Aes128CksumType() { + } + + public int confounderSize() { + return 16; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 12; // bytes + } + + public int keyType() { + return Krb5.KEYTYPE_AES; + } + + public int keySize() { + return 16; // bytes + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + + try { + return Aes128.calculateChecksum(key, usage, data, 0, size); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + + try { + byte[] newCksum = Aes128.calculateChecksum(key, usage, + data, 0, size); + return isChecksumEqual(checksum, newCksum); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } +} diff --git a/src/sun/security/krb5/internal/crypto/HmacSha1Aes256CksumType.java b/src/sun/security/krb5/internal/crypto/HmacSha1Aes256CksumType.java new file mode 100644 index 00000000..d4c3b892 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/HmacSha1Aes256CksumType.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; + +import java.security.GeneralSecurityException; + +/* + * This class encapsulates the checksum type for AES256 + * + * @author Seema Malkani + */ + +public class HmacSha1Aes256CksumType extends CksumType { + + public HmacSha1Aes256CksumType() { + } + + public int confounderSize() { + return 16; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 12; // bytes + } + + public int keyType() { + return Krb5.KEYTYPE_AES; + } + + public int keySize() { + return 32; // bytes + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + + try { + return Aes256.calculateChecksum(key, usage, data, 0, size); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + + try { + byte[] newCksum = Aes256.calculateChecksum(key, usage, data, + 0, size); + return isChecksumEqual(checksum, newCksum); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } +} diff --git a/src/sun/security/krb5/internal/crypto/HmacSha1Des3KdCksumType.java b/src/sun/security/krb5/internal/crypto/HmacSha1Des3KdCksumType.java new file mode 100644 index 00000000..d95768d3 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/HmacSha1Des3KdCksumType.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.spec.DESKeySpec; + +import java.security.GeneralSecurityException; + +public class HmacSha1Des3KdCksumType extends CksumType { + + public HmacSha1Des3KdCksumType() { + } + + public int confounderSize() { + return 8; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 20; // bytes + } + + public int keyType() { + return Krb5.KEYTYPE_DES3; + } + + public int keySize() { + return 24; // bytes + } + + public byte[] calculateChecksum(byte[] data, int size) { + return null; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + + try { + return Des3.calculateChecksum(key, usage, data, 0, size); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + + try { + byte[] newCksum = Des3.calculateChecksum(key, usage, + data, 0, size); + + return isChecksumEqual(checksum, newCksum); + } catch (GeneralSecurityException e) { + KrbCryptoException ke = new KrbCryptoException(e.getMessage()); + ke.initCause(e); + throw ke; + } + } +} diff --git a/src/sun/security/krb5/internal/crypto/KeyUsage.java b/src/sun/security/krb5/internal/crypto/KeyUsage.java new file mode 100644 index 00000000..f9be46c9 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/KeyUsage.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.security.krb5.internal.crypto; + +/** + * Key usages used for key derivation in Kerberos. + */ +public class KeyUsage { + + private KeyUsage() { + } + + public static final int KU_UNKNOWN = 0; // Cannot be 0 + + // Defined in draft-yu-krb-wg-kerberos-extensions-00.txt, Appendix A + public static final int KU_PA_ENC_TS = 1; // KrbAsReq + public static final int KU_TICKET = 2; // KrbApReq (ticket) + public static final int KU_ENC_AS_REP_PART = 3; // KrbAsRep + public static final int KU_TGS_REQ_AUTH_DATA_SESSKEY= 4; // KrbTgsReq + public static final int KU_TGS_REQ_AUTH_DATA_SUBKEY = 5; // KrbTgsReq + public static final int KU_PA_TGS_REQ_CKSUM = 6; // KrbTgsReq + public static final int KU_PA_TGS_REQ_AUTHENTICATOR = 7; // KrbApReq + public static final int KU_ENC_TGS_REP_PART_SESSKEY = 8; // KrbTgsRep + public static final int KU_ENC_TGS_REP_PART_SUBKEY = 9; // KrbTgsRep + public static final int KU_AUTHENTICATOR_CKSUM = 10; + public static final int KU_AP_REQ_AUTHENTICATOR = 11; // KrbApReq + public static final int KU_ENC_AP_REP_PART = 12; // KrbApRep + public static final int KU_ENC_KRB_PRIV_PART = 13; // KrbPriv + public static final int KU_ENC_KRB_CRED_PART = 14; // KrbCred + public static final int KU_KRB_SAFE_CKSUM = 15; // KrbSafe + public static final int KU_PA_FOR_USER_ENC_CKSUM = 17; // S4U2user + public static final int KU_AD_KDC_ISSUED_CKSUM = 19; + + public static final boolean isValid(int usage) { + return usage >= 0; + } +} diff --git a/src/sun/security/krb5/internal/crypto/Nonce.java b/src/sun/security/krb5/internal/crypto/Nonce.java new file mode 100644 index 00000000..6a54a5a6 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/Nonce.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +public class Nonce { + + public static synchronized int value() { + return sun.security.krb5.Confounder.intValue() & 0x7fffffff; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/NullEType.java b/src/sun/security/krb5/internal/crypto/NullEType.java new file mode 100644 index 00000000..6af38169 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/NullEType.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.internal.*; + +public class NullEType extends EType { + + public NullEType() { + } + + public int eType() { + return EncryptedData.ETYPE_NULL; + } + + public int minimumPadSize() { + return 0; + } + + public int confounderSize() { + return 0; + } + + public int checksumType() { + return Checksum.CKSUMTYPE_NULL; + } + + public int checksumSize() { + return 0; + } + + public int blockSize() { + return 1; + } + + public int keyType() { + return Krb5.KEYTYPE_NULL; + } + + public int keySize() { + return 0; + } + + public byte[] encrypt(byte[] data, byte[] key, int usage) { + byte[] cipher = new byte[data.length]; + System.arraycopy(data, 0, cipher, 0, data.length); + return cipher; + } + + public byte[] encrypt(byte[] data, byte[] key, byte[] ivec, int usage) { + byte[] cipher = new byte[data.length]; + System.arraycopy(data, 0, cipher, 0, data.length); + return cipher; + } + + public byte[] decrypt(byte[] cipher, byte[] key, int usage) + throws KrbApErrException { + return cipher.clone(); + } + + public byte[] decrypt(byte[] cipher, byte[] key, byte[] ivec, int usage) + throws KrbApErrException { + return cipher.clone(); + } +} diff --git a/src/sun/security/krb5/internal/crypto/RsaMd5CksumType.java b/src/sun/security/krb5/internal/crypto/RsaMd5CksumType.java new file mode 100644 index 00000000..52b96b5e --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/RsaMd5CksumType.java @@ -0,0 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import java.security.MessageDigest; + +public final class RsaMd5CksumType extends CksumType { + + public RsaMd5CksumType() { + } + + public int confounderSize() { + return 0; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_RSA_MD5; + } + + public boolean isSafe() { + return false; + } + + public int cksumSize() { + return 16; + } + + public int keyType() { + return Krb5.KEYTYPE_NULL; + } + + public int keySize() { + return 0; + } + + /** + * Calculates checksum using MD5. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @return the checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + + public byte[] calculateChecksum(byte[] data, int size) throws KrbCryptoException{ + MessageDigest md5; + byte[] result = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new KrbCryptoException("JCE provider may not be installed. " + e.getMessage()); + } + try { + md5.update(data); + result = md5.digest(); + } catch (Exception e) { + throw new KrbCryptoException(e.getMessage()); + } + return result; + } + + public byte[] calculateKeyedChecksum(byte[] data, int size, + byte[] key, int usage) throws KrbCryptoException { + return null; + } + + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + return false; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/RsaMd5DesCksumType.java b/src/sun/security/krb5/internal/crypto/RsaMd5DesCksumType.java new file mode 100644 index 00000000..e28caea6 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/RsaMd5DesCksumType.java @@ -0,0 +1,199 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.crypto; + +import sun.security.krb5.Checksum; +import sun.security.krb5.Confounder; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.internal.*; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.DESKeySpec; +import java.security.MessageDigest; +import java.security.InvalidKeyException; + +public final class RsaMd5DesCksumType extends CksumType { + + public RsaMd5DesCksumType() { + } + + public int confounderSize() { + return 8; + } + + public int cksumType() { + return Checksum.CKSUMTYPE_RSA_MD5_DES; + } + + public boolean isSafe() { + return true; + } + + public int cksumSize() { + return 24; + } + + public int keyType() { + return Krb5.KEYTYPE_DES; + } + + public int keySize() { + return 8; + } + + /** + * Calculates keyed checksum. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @param key the key used to encrypt the checksum. + * @return keyed checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public byte[] calculateKeyedChecksum(byte[] data, int size, byte[] key, + int usage) throws KrbCryptoException { + //prepend confounder + byte[] new_data = new byte[size + confounderSize()]; + byte[] conf = Confounder.bytes(confounderSize()); + System.arraycopy(conf, 0, new_data, 0, confounderSize()); + System.arraycopy(data, 0, new_data, confounderSize(), size); + + //calculate md5 cksum + byte[] mdc_cksum = calculateChecksum(new_data, new_data.length); + byte[] cksum = new byte[cksumSize()]; + System.arraycopy(conf, 0, cksum, 0, confounderSize()); + System.arraycopy(mdc_cksum, 0, cksum, confounderSize(), + cksumSize() - confounderSize()); + + //compute modified key + byte[] new_key = new byte[keySize()]; + System.arraycopy(key, 0, new_key, 0, key.length); + for (int i = 0; i < new_key.length; i++) + new_key[i] = (byte)(new_key[i] ^ 0xf0); + //check for weak keys + try { + if (DESKeySpec.isWeak(new_key, 0)) { + new_key[7] = (byte)(new_key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[new_key.length]; + + //des-cbc encrypt + byte[] enc_cksum = new byte[cksum.length]; + Des.cbc_encrypt(cksum, enc_cksum, new_key, ivec, true); + return enc_cksum; + } + + /** + * Verifies keyed checksum. + * @param data the data. + * @param size the length of data. + * @param key the key used to encrypt the checksum. + * @param checksum + * @return true if verification is successful. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public boolean verifyKeyedChecksum(byte[] data, int size, + byte[] key, byte[] checksum, int usage) throws KrbCryptoException { + //decrypt checksum + byte[] cksum = decryptKeyedChecksum(checksum, key); + + //prepend confounder + byte[] new_data = new byte[size + confounderSize()]; + System.arraycopy(cksum, 0, new_data, 0, confounderSize()); + System.arraycopy(data, 0, new_data, confounderSize(), size); + + byte[] new_cksum = calculateChecksum(new_data, new_data.length); + //extract original cksum value + byte[] orig_cksum = new byte[cksumSize() - confounderSize()]; + System.arraycopy(cksum, confounderSize(), orig_cksum, 0, + cksumSize() - confounderSize()); + + return isChecksumEqual(orig_cksum, new_cksum); + } + + /** + * Decrypts keyed checksum. + * @param enc_cksum the buffer for encrypted checksum. + * @param key the key. + * @return the checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + private byte[] decryptKeyedChecksum(byte[] enc_cksum, byte[] key) throws KrbCryptoException { + //compute modified key + byte[] new_key = new byte[keySize()]; + System.arraycopy(key, 0, new_key, 0, key.length); + for (int i = 0; i < new_key.length; i++) + new_key[i] = (byte)(new_key[i] ^ 0xf0); + //check for weak keys + try { + if (DESKeySpec.isWeak(new_key, 0)) { + new_key[7] = (byte)(new_key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + byte[] ivec = new byte[new_key.length]; + + byte[] cksum = new byte[enc_cksum.length]; + Des.cbc_encrypt(enc_cksum, cksum, new_key, ivec, false); + return cksum; + } + + /** + * Calculates checksum using MD5. + * @param data the data used to generate the checksum. + * @param size length of the data. + * @return the checksum. + * + * @modified by Yanni Zhang, 12/08/99. + */ + public byte[] calculateChecksum(byte[] data, int size) throws KrbCryptoException{ + MessageDigest md5; + byte[] result = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new KrbCryptoException("JCE provider may not be installed. " + e.getMessage()); + } + try { + md5.update(data); + result = md5.digest(); + } catch (Exception e) { + throw new KrbCryptoException(e.getMessage()); + } + return result; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/crc32.java b/src/sun/security/krb5/internal/crypto/crc32.java new file mode 100644 index 00000000..7639ba48 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/crc32.java @@ -0,0 +1,278 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ +// crc32.java + +package sun.security.krb5.internal.crypto; + +import java.security.MessageDigestSpi; +import java.security.DigestException; + +public final class crc32 extends MessageDigestSpi implements Cloneable { + private static final int CRC32_LENGTH = 4; //32-bit + private int seed; + private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG; + // buffer; + // private int bufferIndex, bufferLeft; + + public crc32() { + init(); + } + + public Object clone() { + try { + crc32 crc = (crc32)super.clone(); + crc.init(); + return crc; + } + catch (CloneNotSupportedException e) { + } + return null; + } + + /** + * Return the digest length in bytes + */ + protected int engineGetDigestLength() { + return (CRC32_LENGTH); + } + + /** + */ + protected byte[] engineDigest() { + byte[] result = new byte[CRC32_LENGTH]; + result = int2quad(seed); + //processBuffer(buffer, 0, bufferIndex, result, 0); + init(); + return result; + } + + /** + */ + protected int engineDigest(byte[] buf, int offset, int len) throws DigestException { + byte[] result = new byte[CRC32_LENGTH]; + result = int2quad(seed); + if (len < CRC32_LENGTH) { + throw new DigestException("partial digests not returned"); + } + if (buf.length - offset < CRC32_LENGTH) { + throw new DigestException("insufficient space in the output " + + "buffer to store the digest"); + } + System.arraycopy(result, 0, buf, offset, CRC32_LENGTH); + //processBuffer(buffer, 0, bufferIndex, result, 0); + /*if (len < CRC32_LENGTH) { + throw new DigestException("partial digests not returned"); + } + if (buf.length - offset < CRC32_LENGTH) { + throw new DigestException("insufficient space in the output " + + "buffer to store the digest"); + } + System.arraycopy(result, 0, buf, offset, CRC32_LENGTH); */ + init(); + return CRC32_LENGTH; + } + /** + * Update adds the passed byte to the digested data. + */ + protected synchronized void engineUpdate(byte b) { + byte[] input = new byte[1]; + input[0] = b; + //engineUpdate(input, 0, 1); + engineUpdate(input, seed, 1); + } + + /** + * Update adds the selected part of an array of bytes to the digest. + * This version is more efficient than the byte-at-a-time version; + * it avoids data copies and reduces per-byte call overhead. + */ + protected synchronized void engineUpdate(byte input[], int offset, + int len) { + processData(input, offset, len); + } + private static int[] crc32Table = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + protected void engineReset() { + init(); + } + + /** + * Initialize the CRC32 information + */ + public void init() { + seed = 0; + } + + private void processData(byte[] data, int off, int len) { + int result = seed; + for (int i = 0; i < len; i++) + result = (result >>> 8) ^ crc32Table[(result ^ data[i]) & 0xff]; + seed = result; + } + + public static int int2crc32(int b) { + int crc = b; + + for (int i = 8; i > 0; i--) { + if ((crc & 1) != 0) + crc = (crc >>> 1) ^ 0xedb88320; + else + crc = crc >>> 1; + } + return crc; + } + + public static void printcrc32Table() { + String temp; + String zerofill = "00000000"; + + System.out.print("\tpublic static int[] crc32Table = {"); + for (int i = 0; i < 256; i++) { + if ((i % 4) == 0) + System.out.print("\n\t\t"); + temp = Integer.toHexString(int2crc32(i)); + System.out.print("0x" + + zerofill.substring(temp.length()) + temp); + if (i != 255) + System.out.print(", "); + } + System.out.println("\n\t};"); + } + + public static int byte2crc32sum(int seed, byte[] data, int size) { + int crc = seed; + + for (int i = 0; i < size; i++) + crc = (crc >>> 8) ^ crc32Table[(crc ^ data[i]) & 0xff]; + return crc; + } + + public static int byte2crc32sum(int seed, byte[] data) { + return byte2crc32sum(seed, data, data.length); + } + + //sum from zero, i.e., no pre- or post-conditioning + public static int byte2crc32sum(byte[] data) { + return byte2crc32sum(0, data); + } + + //CCITT ITU-T 3309 CRC-32 w/ standard pre- and post-conditioning + public static int byte2crc32(byte[] data) { + return ~byte2crc32sum(0xffffffff, data); + } + + public static byte[] byte2crc32sum_bytes(byte[] data) { + int temp = byte2crc32sum(data); + return int2quad(temp); + } + + public static byte[] byte2crc32sum_bytes(byte[] data, int size) { + int temp = byte2crc32sum(0, data, size); + if (DEBUG) { + System.out.println(">>>crc32: " + Integer.toHexString(temp)); + System.out.println(">>>crc32: " + Integer.toBinaryString(temp)); + } + return int2quad(temp); + } + + public static byte[] int2quad(long input) { + byte[] output = new byte[4]; + for (int i = 0; i < 4; i++) { + output[i] = (byte)((input >>> (i * 8)) & 0xff); + } + return output; + } + + +} diff --git a/src/sun/security/krb5/internal/crypto/dk/AesDkCrypto.java b/src/sun/security/krb5/internal/crypto/dk/AesDkCrypto.java new file mode 100644 index 00000000..600c45f2 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/dk/AesDkCrypto.java @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + */ + +package sun.security.krb5.internal.crypto.dk; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; + +import java.security.GeneralSecurityException; +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.Confounder; +import sun.security.krb5.internal.crypto.KeyUsage; +import java.util.Arrays; + +/** + * This class provides the implementation of AES Encryption for Kerberos + * as defined RFC 3962. + * http://www.ietf.org/rfc/rfc3962.txt + * + * Algorithm profile described in [KCRYPTO]: + * +--------------------------------------------------------------------+ + * | protocol key format 128- or 256-bit string | + * | | + * | string-to-key function PBKDF2+DK with variable | + * | iteration count (see | + * | above) | + * | | + * | default string-to-key parameters 00 00 10 00 | + * | | + * | key-generation seed length key size | + * | | + * | random-to-key function identity function | + * | | + * | hash function, H SHA-1 | + * | | + * | HMAC output size, h 12 octets (96 bits) | + * | | + * | message block size, m 1 octet | + * | | + * | encryption/decryption functions, AES in CBC-CTS mode | + * | E and D (cipher block size 16 | + * | octets), with next to | + * | last block as CBC-style | + * | ivec | + * +--------------------------------------------------------------------+ + * + * Supports AES128 and AES256 + * + * @author Seema Malkani + */ + +public class AesDkCrypto extends DkCrypto { + + private static final boolean debug = false; + + private static final int BLOCK_SIZE = 16; + private static final int DEFAULT_ITERATION_COUNT = 4096; + private static final byte[] ZERO_IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }; + private static final int hashSize = 96/8; + private final int keyLength; + + public AesDkCrypto(int length) { + keyLength = length; + } + + protected int getKeySeedLength() { + return keyLength; // bits; AES key material + } + + public byte[] stringToKey(char[] password, String salt, byte[] s2kparams) + throws GeneralSecurityException { + + byte[] saltUtf8 = null; + try { + saltUtf8 = salt.getBytes("UTF-8"); + return stringToKey(password, saltUtf8, s2kparams); + } catch (Exception e) { + return null; + } finally { + if (saltUtf8 != null) { + Arrays.fill(saltUtf8, (byte)0); + } + } + } + + private byte[] stringToKey(char[] secret, byte[] salt, byte[] params) + throws GeneralSecurityException { + + int iter_count = DEFAULT_ITERATION_COUNT; + if (params != null) { + if (params.length != 4) { + throw new RuntimeException("Invalid parameter to stringToKey"); + } + iter_count = readBigEndian(params, 0, 4); + } + + byte[] tmpKey = randomToKey(PBKDF2(secret, salt, iter_count, + getKeySeedLength())); + byte[] result = dk(tmpKey, KERBEROS_CONSTANT); + return result; + } + + protected byte[] randomToKey(byte[] in) { + // simple identity operation + return in; + } + + protected Cipher getCipher(byte[] key, byte[] ivec, int mode) + throws GeneralSecurityException { + + // IV + if (ivec == null) { + ivec = ZERO_IV; + } + SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); + cipher.init(mode, secretKey, encIv); + return cipher; + } + + // get an instance of the AES Cipher in CTS mode + public int getChecksumLength() { + return hashSize; // bytes + } + + /** + * Get the truncated HMAC + */ + protected byte[] getHmac(byte[] key, byte[] msg) + throws GeneralSecurityException { + + SecretKey keyKi = new SecretKeySpec(key, "HMAC"); + Mac m = Mac.getInstance("HmacSHA1"); + m.init(keyKi); + + // generate hash + byte[] hash = m.doFinal(msg); + + // truncate hash + byte[] output = new byte[hashSize]; + System.arraycopy(hash, 0, output, 0, hashSize); + return output; + } + + /** + * Calculate the checksum + */ + public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, + int start, int len) throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + // Derive keys + byte[] constant = new byte[5]; + constant[0] = (byte) ((usage>>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + + constant[4] = (byte) 0x99; + + byte[] Kc = dk(baseKey, constant); // Checksum key + if (debug) { + System.err.println("usage: " + usage); + traceOutput("input", input, start, Math.min(len, 32)); + traceOutput("constant", constant, 0, constant.length); + traceOutput("baseKey", baseKey, 0, baseKey.length); + traceOutput("Kc", Kc, 0, Kc.length); + } + + try { + // Generate checksum + // H1 = HMAC(Kc, input) + byte[] hmac = getHmac(Kc, input); + if (debug) { + traceOutput("hmac", hmac, 0, hmac.length); + } + if (hmac.length == getChecksumLength()) { + return hmac; + } else if (hmac.length > getChecksumLength()) { + byte[] buf = new byte[getChecksumLength()]; + System.arraycopy(hmac, 0, buf, 0, buf.length); + return buf; + } else { + throw new GeneralSecurityException("checksum size too short: " + + hmac.length + "; expecting : " + getChecksumLength()); + } + } finally { + Arrays.fill(Kc, 0, Kc.length, (byte)0); + } + } + + /** + * Performs encryption using derived key; adds confounder. + */ + public byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + byte[] output = encryptCTS(baseKey, usage, ivec, new_ivec, plaintext, + start, len, true); + return output; + } + + /** + * Performs encryption using derived key; does not add confounder. + */ + public byte[] encryptRaw(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + byte[] output = encryptCTS(baseKey, usage, ivec, null, plaintext, + start, len, false); + return output; + } + + /** + * @param baseKey key from which keys are to be derived using usage + * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] + */ + public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext, + start, len, true); + return output; + } + + /** + * Decrypts data using specified key and initial vector. + * @param baseKey encryption key to use + * @param ciphertext encrypted data to be decrypted + * @param usage ignored + */ + public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext, + start, len, false); + return output; + } + + /** + * Encrypt AES in CBC-CTS mode using derived keys. + */ + private byte[] encryptCTS(byte[] baseKey, int usage, byte[] ivec, + byte[] new_ivec, byte[] plaintext, int start, int len, + boolean confounder_exists) + throws GeneralSecurityException, KrbCryptoException { + + byte[] Ke = null; + byte[] Ki = null; + + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("plaintext", plaintext, start, Math.min(len, 32)); + traceOutput("baseKey", baseKey, 0, baseKey.length); + } + + try { + // derive Encryption key + byte[] constant = new byte[5]; + constant[0] = (byte) ((usage>>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + constant[4] = (byte) 0xaa; + Ke = dk(baseKey, constant); // Encryption key + + byte[] toBeEncrypted = null; + if (confounder_exists) { + byte[] confounder = Confounder.bytes(BLOCK_SIZE); + toBeEncrypted = new byte[confounder.length + len]; + System.arraycopy(confounder, 0, toBeEncrypted, + 0, confounder.length); + System.arraycopy(plaintext, start, toBeEncrypted, + confounder.length, len); + } else { + toBeEncrypted = new byte[len]; + System.arraycopy(plaintext, start, toBeEncrypted, 0, len); + } + + // encryptedData + HMAC + byte[] output = new byte[toBeEncrypted.length + hashSize]; + + // AES in JCE + Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding"); + SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES"); + IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, encIv); + cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length, output); + + // Derive integrity key + constant[4] = (byte) 0x55; + Ki = dk(baseKey, constant); + if (debug) { + traceOutput("constant", constant, 0, constant.length); + traceOutput("Ki", Ki, 0, Ke.length); + } + + // Generate checksum + // H1 = HMAC(Ki, conf | plaintext | pad) + byte[] hmac = getHmac(Ki, toBeEncrypted); + + // encryptedData + HMAC + System.arraycopy(hmac, 0, output, toBeEncrypted.length, + hmac.length); + return output; + } finally { + if (Ke != null) { + Arrays.fill(Ke, 0, Ke.length, (byte) 0); + } + if (Ki != null) { + Arrays.fill(Ki, 0, Ki.length, (byte) 0); + } + } + } + + /** + * Decrypt AES in CBC-CTS mode using derived keys. + */ + private byte[] decryptCTS(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len, boolean confounder_exists) + throws GeneralSecurityException { + + byte[] Ke = null; + byte[] Ki = null; + + try { + // Derive encryption key + byte[] constant = new byte[5]; + constant[0] = (byte) ((usage>>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + + constant[4] = (byte) 0xaa; + Ke = dk(baseKey, constant); // Encryption key + + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); + traceOutput("constant", constant, 0, constant.length); + traceOutput("baseKey", baseKey, 0, baseKey.length); + traceOutput("Ke", Ke, 0, Ke.length); + } + + // Decrypt [confounder | plaintext ] (without checksum) + + // AES in JCE + Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding"); + SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES"); + IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); + cipher.init(Cipher.DECRYPT_MODE, secretKey, encIv); + byte[] plaintext = cipher.doFinal(ciphertext, start, len-hashSize); + + if (debug) { + traceOutput("AES PlainText", plaintext, 0, + Math.min(plaintext.length, 32)); + } + + // Derive integrity key + constant[4] = (byte) 0x55; + Ki = dk(baseKey, constant); // Integrity key + if (debug) { + traceOutput("constant", constant, 0, constant.length); + traceOutput("Ki", Ki, 0, Ke.length); + } + + // Verify checksum + // H1 = HMAC(Ki, conf | plaintext | pad) + byte[] calculatedHmac = getHmac(Ki, plaintext); + int hmacOffset = start + len - hashSize; + if (debug) { + traceOutput("calculated Hmac", calculatedHmac, + 0, calculatedHmac.length); + traceOutput("message Hmac", ciphertext, hmacOffset, hashSize); + } + boolean cksumFailed = false; + if (calculatedHmac.length >= hashSize) { + for (int i = 0; i < hashSize; i++) { + if (calculatedHmac[i] != ciphertext[hmacOffset+i]) { + cksumFailed = true; + if (debug) { + System.err.println("Checksum failed !"); + } + break; + } + } + } + if (cksumFailed) { + throw new GeneralSecurityException("Checksum failed"); + } + + if (confounder_exists) { + // Get rid of confounder + // [ confounder | plaintext ] + byte[] output = new byte[plaintext.length - BLOCK_SIZE]; + System.arraycopy(plaintext, BLOCK_SIZE, output, + 0, output.length); + return output; + } else { + return plaintext; + } + } finally { + if (Ke != null) { + Arrays.fill(Ke, 0, Ke.length, (byte) 0); + } + if (Ki != null) { + Arrays.fill(Ki, 0, Ki.length, (byte) 0); + } + } + } + + /* + * Invoke the PKCS#5 PBKDF2 algorithm + */ + private static byte[] PBKDF2(char[] secret, byte[] salt, + int count, int keyLength) throws GeneralSecurityException { + + PBEKeySpec keySpec = new PBEKeySpec(secret, salt, count, keyLength); + SecretKeyFactory skf = + SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + SecretKey key = skf.generateSecret(keySpec); + byte[] result = key.getEncoded(); + + return result; + } + + public static final int readBigEndian(byte[] data, int pos, int size) { + int retVal = 0; + int shifter = (size-1)*8; + while (size > 0) { + retVal += (data[pos] & 0xff) << shifter; + shifter -= 8; + pos++; + size--; + } + return retVal; + } + +} diff --git a/src/sun/security/krb5/internal/crypto/dk/ArcFourCrypto.java b/src/sun/security/krb5/internal/crypto/dk/ArcFourCrypto.java new file mode 100644 index 00000000..270e2d94 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/dk/ArcFourCrypto.java @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto.dk; + +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.*; +import java.util.*; + +import sun.security.krb5.KrbCryptoException; +import sun.security.krb5.Confounder; +import sun.security.krb5.internal.crypto.KeyUsage; + +/** + * Support for ArcFour in Kerberos + * as defined in RFC 4757. + * http://www.ietf.org/rfc/rfc4757.txt + * + * @author Seema Malkani + */ + +public class ArcFourCrypto extends DkCrypto { + + private static final boolean debug = false; + + private static final int confounderSize = 8; + private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; + private static final int hashSize = 16; + private final int keyLength; + + public ArcFourCrypto(int length) { + keyLength = length; + } + + protected int getKeySeedLength() { + return keyLength; // bits; RC4 key material + } + + protected byte[] randomToKey(byte[] in) { + // simple identity operation + return in; + } + + public byte[] stringToKey(char[] passwd) + throws GeneralSecurityException { + return stringToKey(passwd, null); + } + + /* + * String2Key(Password) + * K = MD4(UNICODE(password)) + */ + private byte[] stringToKey(char[] secret, byte[] opaque) + throws GeneralSecurityException { + + if (opaque != null && opaque.length > 0) { + throw new RuntimeException("Invalid parameter to stringToKey"); + } + + byte[] passwd = null; + byte[] digest = null; + try { + // convert ascii to unicode + passwd = charToUtf16(secret); + + // provider for MD4 + MessageDigest md = sun.security.provider.MD4.getInstance(); + md.update(passwd); + digest = md.digest(); + } catch (Exception e) { + return null; + } finally { + if (passwd != null) { + Arrays.fill(passwd, (byte)0); + } + } + + return digest; + } + + protected Cipher getCipher(byte[] key, byte[] ivec, int mode) + throws GeneralSecurityException { + + // IV + if (ivec == null) { + ivec = ZERO_IV; + } + SecretKeySpec secretKey = new SecretKeySpec(key, "ARCFOUR"); + Cipher cipher = Cipher.getInstance("ARCFOUR"); + IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length); + cipher.init(mode, secretKey, encIv); + return cipher; + } + + public int getChecksumLength() { + return hashSize; // bytes + } + + /** + * Get the HMAC-MD5 + */ + protected byte[] getHmac(byte[] key, byte[] msg) + throws GeneralSecurityException { + + SecretKey keyKi = new SecretKeySpec(key, "HmacMD5"); + Mac m = Mac.getInstance("HmacMD5"); + m.init(keyKi); + + // generate hash + byte[] hash = m.doFinal(msg); + return hash; + } + + /** + * Calculate the checksum + */ + public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, + int start, int len) throws GeneralSecurityException { + + if (debug) { + System.out.println("ARCFOUR: calculateChecksum with usage = " + + usage); + } + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + byte[] Ksign = null; + // Derive signing key from session key + try { + byte[] ss = "signaturekey".getBytes(); + // need to append end-of-string 00 + byte[] new_ss = new byte[ss.length+1]; + System.arraycopy(ss, 0, new_ss, 0, ss.length); + Ksign = getHmac(baseKey, new_ss); + } catch (Exception e) { + GeneralSecurityException gse = + new GeneralSecurityException("Calculate Checkum Failed!"); + gse.initCause(e); + throw gse; + } + + // get the salt using key usage + byte[] salt = getSalt(usage); + + // Generate checksum of message + MessageDigest messageDigest = null; + try { + messageDigest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + GeneralSecurityException gse = + new GeneralSecurityException("Calculate Checkum Failed!"); + gse.initCause(e); + throw gse; + } + messageDigest.update(salt); + messageDigest.update(input, start, len); + byte[] md5tmp = messageDigest.digest(); + + // Generate checksum + byte[] hmac = getHmac(Ksign, md5tmp); + if (debug) { + traceOutput("hmac", hmac, 0, hmac.length); + } + if (hmac.length == getChecksumLength()) { + return hmac; + } else if (hmac.length > getChecksumLength()) { + byte[] buf = new byte[getChecksumLength()]; + System.arraycopy(hmac, 0, buf, 0, buf.length); + return buf; + } else { + throw new GeneralSecurityException("checksum size too short: " + + hmac.length + "; expecting : " + getChecksumLength()); + } + } + + /** + * Performs encryption of Sequence Number using derived key. + */ + public byte[] encryptSeq(byte[] baseKey, int usage, + byte[] checksum, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + // derive encryption for sequence number + byte[] salt = new byte[4]; + byte[] kSeq = getHmac(baseKey, salt); + + // derive new encryption key salted with sequence number + kSeq = getHmac(kSeq, checksum); + + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] output = cipher.doFinal(plaintext, start, len); + + return output; + } + + /** + * Performs decryption of Sequence Number using derived key. + */ + public byte[] decryptSeq(byte[] baseKey, int usage, + byte[] checksum, byte[] ciphertext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + // derive decryption for sequence number + byte[] salt = new byte[4]; + byte[] kSeq = getHmac(baseKey, salt); + + // derive new encryption key salted with sequence number + kSeq = getHmac(kSeq, checksum); + + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] output = cipher.doFinal(ciphertext, start, len); + + return output; + } + + /** + * Performs encryption using derived key; adds confounder. + */ + public byte[] encrypt(byte[] baseKey, int usage, + byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + if (debug) { + System.out.println("ArcFour: ENCRYPT with key usage = " + usage); + } + + // get the confounder + byte[] confounder = Confounder.bytes(confounderSize); + + // add confounder to the plaintext for encryption + int plainSize = roundup(confounder.length + len, 1); + byte[] toBeEncrypted = new byte[plainSize]; + System.arraycopy(confounder, 0, toBeEncrypted, 0, confounder.length); + System.arraycopy(plaintext, start, toBeEncrypted, + confounder.length, len); + + /* begin the encryption, compute K1 */ + byte[] k1 = new byte[baseKey.length]; + System.arraycopy(baseKey, 0, k1, 0, baseKey.length); + + // get the salt using key usage + byte[] salt = getSalt(usage); + + // compute K2 using K1 + byte[] k2 = getHmac(k1, salt); + + // generate checksum using K2 + byte[] checksum = getHmac(k2, toBeEncrypted); + + // compute K3 using K2 and checksum + byte[] k3 = getHmac(k2, checksum); + + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] output = cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length); + + // encryptedData + HMAC + byte[] result = new byte[hashSize + output.length]; + System.arraycopy(checksum, 0, result, 0, hashSize); + System.arraycopy(output, 0, result, hashSize, output.length); + + return result; + } + + /** + * Performs encryption using derived key; does not add confounder. + */ + public byte[] encryptRaw(byte[] baseKey, int usage, + byte[] seqNum, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + if (debug) { + System.out.println("\nARCFOUR: encryptRaw with usage = " + usage); + } + + // Derive encryption key for data + // Key derivation salt = 0 + byte[] klocal = new byte[baseKey.length]; + for (int i = 0; i <= 15; i++) { + klocal[i] = (byte) (baseKey[i] ^ 0xF0); + } + byte[] salt = new byte[4]; + byte[] kcrypt = getHmac(klocal, salt); + + // Note: When using this RC4 based encryption type, the sequence number + // is always sent in big-endian rather than little-endian order. + + // new encryption key salted with sequence number + kcrypt = getHmac(kcrypt, seqNum); + + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] output = cipher.doFinal(plaintext, start, len); + + return output; + } + + /** + * @param baseKey key from which keys are to be derived using usage + * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] + */ + public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + if (debug) { + System.out.println("\nARCFOUR: DECRYPT using key usage = " + usage); + } + + // compute K1 + byte[] k1 = new byte[baseKey.length]; + System.arraycopy(baseKey, 0, k1, 0, baseKey.length); + + // get the salt using key usage + byte[] salt = getSalt(usage); + + // compute K2 using K1 + byte[] k2 = getHmac(k1, salt); + + // compute K3 using K2 and checksum + byte[] checksum = new byte[hashSize]; + System.arraycopy(ciphertext, start, checksum, 0, hashSize); + byte[] k3 = getHmac(k2, checksum); + + // Decrypt [confounder | plaintext ] (without checksum) + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] plaintext = cipher.doFinal(ciphertext, start+hashSize, + len-hashSize); + + // Verify checksum + byte[] calculatedHmac = getHmac(k2, plaintext); + if (debug) { + traceOutput("calculated Hmac", calculatedHmac, 0, + calculatedHmac.length); + traceOutput("message Hmac", ciphertext, 0, + hashSize); + } + boolean cksumFailed = false; + if (calculatedHmac.length >= hashSize) { + for (int i = 0; i < hashSize; i++) { + if (calculatedHmac[i] != ciphertext[i]) { + cksumFailed = true; + if (debug) { + System.err.println("Checksum failed !"); + } + break; + } + } + } + if (cksumFailed) { + throw new GeneralSecurityException("Checksum failed"); + } + + // Get rid of confounder + // [ confounder | plaintext ] + byte[] output = new byte[plaintext.length - confounderSize]; + System.arraycopy(plaintext, confounderSize, output, 0, output.length); + + return output; + } + + /** + * Decrypts data using specified key and initial vector. + * @param baseKey encryption key to use + * @param ciphertext encrypted data to be decrypted + * @param usage ignored + */ + public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len, byte[] seqNum) + throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + if (debug) { + System.out.println("\nARCFOUR: decryptRaw with usage = " + usage); + } + + // Derive encryption key for data + // Key derivation salt = 0 + byte[] klocal = new byte[baseKey.length]; + for (int i = 0; i <= 15; i++) { + klocal[i] = (byte) (baseKey[i] ^ 0xF0); + } + byte[] salt = new byte[4]; + byte[] kcrypt = getHmac(klocal, salt); + + // need only first 4 bytes of sequence number + byte[] sequenceNum = new byte[4]; + System.arraycopy(seqNum, 0, sequenceNum, 0, sequenceNum.length); + + // new encryption key salted with sequence number + kcrypt = getHmac(kcrypt, sequenceNum); + + Cipher cipher = Cipher.getInstance("ARCFOUR"); + SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + byte[] output = cipher.doFinal(ciphertext, start, len); + + return output; + } + + // get the salt using key usage + private byte[] getSalt(int usage) { + int ms_usage = arcfour_translate_usage(usage); + byte[] salt = new byte[4]; + salt[0] = (byte)(ms_usage & 0xff); + salt[1] = (byte)((ms_usage >> 8) & 0xff); + salt[2] = (byte)((ms_usage >> 16) & 0xff); + salt[3] = (byte)((ms_usage >> 24) & 0xff); + return salt; + } + + // Key usage translation for MS + private int arcfour_translate_usage(int usage) { + switch (usage) { + case 3: return 8; + case 9: return 8; + case 23: return 13; + default: return usage; + } + } + +} diff --git a/src/sun/security/krb5/internal/crypto/dk/Des3DkCrypto.java b/src/sun/security/krb5/internal/crypto/dk/Des3DkCrypto.java new file mode 100644 index 00000000..9d4503d3 --- /dev/null +++ b/src/sun/security/krb5/internal/crypto/dk/Des3DkCrypto.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.crypto.dk; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +import java.security.spec.KeySpec; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.util.Arrays; + +public class Des3DkCrypto extends DkCrypto { + + private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; + + public Des3DkCrypto() { + } + + protected int getKeySeedLength() { + return 168; // bits; 3DES key material has 21 bytes + } + + public byte[] stringToKey(char[] salt) throws GeneralSecurityException { + byte[] saltUtf8 = null; + try { + saltUtf8 = charToUtf8(salt); + return stringToKey(saltUtf8, null); + } finally { + if (saltUtf8 != null) { + Arrays.fill(saltUtf8, (byte)0); + } + // Caller responsible for clearing its own salt + } + } + + private byte[] stringToKey(byte[] secretAndSalt, byte[] opaque) + throws GeneralSecurityException { + + if (opaque != null && opaque.length > 0) { + throw new RuntimeException("Invalid parameter to stringToKey"); + } + + byte[] tmpKey = randomToKey(nfold(secretAndSalt, getKeySeedLength())); + return dk(tmpKey, KERBEROS_CONSTANT); + } + + public byte[] parityFix(byte[] value) + throws GeneralSecurityException { + // fix key parity + setParityBit(value); + return value; + } + + /* + * From RFC 3961. + * + * The 168 bits of random key data are converted to a protocol key value + * as follows. First, the 168 bits are divided into three groups of 56 + * bits, which are expanded individually into 64 bits as in des3Expand(). + * Result is a 24 byte (192-bit) key. + */ + protected byte[] randomToKey(byte[] in) { + if (in.length != 21) { + throw new IllegalArgumentException("input must be 168 bits"); + } + + byte[] one = keyCorrection(des3Expand(in, 0, 7)); + byte[] two = keyCorrection(des3Expand(in, 7, 14)); + byte[] three = keyCorrection(des3Expand(in, 14, 21)); + + byte[] key = new byte[24]; + System.arraycopy(one, 0, key, 0, 8); + System.arraycopy(two, 0, key, 8, 8); + System.arraycopy(three, 0, key, 16, 8); + + return key; + } + + private static byte[] keyCorrection(byte[] key) { + // check for weak key + try { + if (DESKeySpec.isWeak(key, 0)) { + key[7] = (byte)(key[7] ^ 0xF0); + } + } catch (InvalidKeyException ex) { + // swallow, since it should never happen + } + return key; + } + + /** + * From RFC 3961. + * + * Expands a 7-byte array into an 8-byte array that contains parity bits. + * The 56 bits are expanded into 64 bits as follows: + * 1 2 3 4 5 6 7 p + * 9 10 11 12 13 14 15 p + * 17 18 19 20 21 22 23 p + * 25 26 27 28 29 30 31 p + * 33 34 35 36 37 38 39 p + * 41 42 43 44 45 46 47 p + * 49 50 51 52 53 54 55 p + * 56 48 40 32 24 16 8 p + * + * (PI,P2,...,P8) are reserved for parity bits computed on the preceding + * seven independent bits and set so that the parity of the octet is odd, + * i.e., there is an odd number of "1" bits in the octet. + * + * @param start index of starting byte (inclusive) + * @param end index of ending byte (exclusive) + */ + private static byte[] des3Expand(byte[] input, int start, int end) { + if ((end - start) != 7) + throw new IllegalArgumentException( + "Invalid length of DES Key Value:" + start + "," + end); + + byte[] result = new byte[8]; + byte last = 0; + System.arraycopy(input, start, result, 0, 7); + byte posn = 0; + + // Fill in last row + for (int i = start; i < end; i++) { + byte bit = (byte) (input[i]&0x01); + if (debug) { + System.out.println(i + ": " + Integer.toHexString(input[i]) + + " bit= " + Integer.toHexString(bit)); + } + ++posn; + if (bit != 0) { + last |= (bit<>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + + constant[4] = (byte) 0xaa; + + Ke = dk(baseKey, constant); + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("plaintext", plaintext, start, Math.min(len, 32)); + traceOutput("constant", constant, 0, constant.length); + traceOutput("baseKey", baseKey, 0, baseKey.length); + traceOutput("Ke", Ke, 0, Ke.length); + } + + // Encrypt + // C1 = E(Ke, conf | plaintext | pad, oldivec) + Cipher encCipher = getCipher(Ke, ivec, Cipher.ENCRYPT_MODE); + int blockSize = encCipher.getBlockSize(); + byte[] confounder = Confounder.bytes(blockSize); + + int plainSize = roundup(confounder.length + len, blockSize); + if (debug) { + System.err.println("confounder = " + confounder.length + + "; plaintext = " + len + "; padding = " + + (plainSize - confounder.length - len) + "; total = " + + plainSize); + traceOutput("confounder", confounder, 0, confounder.length); + } + + byte[] toBeEncrypted = new byte[plainSize]; + System.arraycopy(confounder, 0, toBeEncrypted, + 0, confounder.length); + System.arraycopy(plaintext, start, toBeEncrypted, + confounder.length, len); + + // Set padding bytes to zero + Arrays.fill(toBeEncrypted, confounder.length + len, plainSize, + (byte)0); + + int cipherSize = encCipher.getOutputSize(plainSize); + int ccSize = cipherSize + getChecksumLength(); // cipher | hmac + + byte[] ciphertext = new byte[ccSize]; + + encCipher.doFinal(toBeEncrypted, 0, plainSize, ciphertext, 0); + + // Update ivec for next operation + // (last blockSize bytes of ciphertext) + // newstate.ivec = newIV + if (new_ivec != null && new_ivec.length == blockSize) { + System.arraycopy(ciphertext, cipherSize - blockSize, + new_ivec, 0, blockSize); + if (debug) { + traceOutput("new_ivec", new_ivec, 0, new_ivec.length); + } + } + + // Derive integrity key + constant[4] = (byte) 0x55; + Ki = dk(baseKey, constant); + if (debug) { + traceOutput("constant", constant, 0, constant.length); + traceOutput("Ki", Ki, 0, Ke.length); + } + + // Generate checksum + // H1 = HMAC(Ki, conf | plaintext | pad) + byte[] hmac = getHmac(Ki, toBeEncrypted); + + if (debug) { + traceOutput("hmac", hmac, 0, hmac.length); + traceOutput("ciphertext", ciphertext, 0, + Math.min(ciphertext.length, 32)); + } + + // C1 | H1[1..h] + System.arraycopy(hmac, 0, ciphertext, cipherSize, + getChecksumLength()); + return ciphertext; + } finally { + if (Ke != null) { + Arrays.fill(Ke, 0, Ke.length, (byte) 0); + } + if (Ki != null) { + Arrays.fill(Ki, 0, Ki.length, (byte) 0); + } + } + } + + /** + * Performs encryption using given key only; does not add + * confounder, padding, or checksum. Incoming data to be encrypted + * assumed to have the correct blocksize. + * Ignore key usage. + */ + public byte[] encryptRaw(byte[] baseKey, int usage, + byte[] ivec, byte[] plaintext, int start, int len) + throws GeneralSecurityException, KrbCryptoException { + + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("plaintext", plaintext, start, Math.min(len, 32)); + traceOutput("baseKey", baseKey, 0, baseKey.length); + } + + // Encrypt + Cipher encCipher = getCipher(baseKey, ivec, Cipher.ENCRYPT_MODE); + int blockSize = encCipher.getBlockSize(); + + if ((len % blockSize) != 0) { + throw new GeneralSecurityException( + "length of data to be encrypted (" + len + + ") is not a multiple of the blocksize (" + blockSize + ")"); + } + + int cipherSize = encCipher.getOutputSize(len); + byte[] ciphertext = new byte[cipherSize]; + + encCipher.doFinal(plaintext, 0, len, ciphertext, 0); + return ciphertext; + } + + /** + * Decrypts data using specified key and initial vector. + * @param baseKey encryption key to use + * @param ciphertext encrypted data to be decrypted + * @param usage ignored + */ + public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) + throws GeneralSecurityException { + + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); + traceOutput("baseKey", baseKey, 0, baseKey.length); + } + + Cipher decCipher = getCipher(baseKey, ivec, Cipher.DECRYPT_MODE); + + int blockSize = decCipher.getBlockSize(); + + if ((len % blockSize) != 0) { + throw new GeneralSecurityException( + "length of data to be decrypted (" + len + + ") is not a multiple of the blocksize (" + blockSize + ")"); + } + + byte[] decrypted = decCipher.doFinal(ciphertext, start, len); + + if (debug) { + traceOutput("decrypted", decrypted, 0, + Math.min(decrypted.length, 32)); + } + + return decrypted; + } + + /** + * @param baseKey key from which keys are to be derived using usage + * @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h] + */ + public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec, + byte[] ciphertext, int start, int len) throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + byte[] Ke = null; + byte[] Ki = null; + + try { + // Derive encryption key + byte[] constant = new byte[5]; + constant[0] = (byte) ((usage>>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + + constant[4] = (byte) 0xaa; + + Ke = dk(baseKey, constant); // Encryption key + + if (debug) { + System.err.println("usage: " + usage); + if (ivec != null) { + traceOutput("old_state.ivec", ivec, 0, ivec.length); + } + traceOutput("ciphertext", ciphertext, start, Math.min(len, 32)); + traceOutput("constant", constant, 0, constant.length); + traceOutput("baseKey", baseKey, 0, baseKey.length); + traceOutput("Ke", Ke, 0, Ke.length); + } + + Cipher decCipher = getCipher(Ke, ivec, Cipher.DECRYPT_MODE); + int blockSize = decCipher.getBlockSize(); + + // Decrypt [confounder | plaintext | padding] (without checksum) + int cksumSize = getChecksumLength(); + int cipherSize = len - cksumSize; + byte[] decrypted = decCipher.doFinal(ciphertext, start, cipherSize); + + if (debug) { + traceOutput("decrypted", decrypted, 0, + Math.min(decrypted.length, 32)); + } + + // decrypted = [confounder | plaintext | padding] + + // Derive integrity key + constant[4] = (byte) 0x55; + Ki = dk(baseKey, constant); // Integrity key + if (debug) { + traceOutput("constant", constant, 0, constant.length); + traceOutput("Ki", Ki, 0, Ke.length); + } + + // Verify checksum + // H1 = HMAC(Ki, conf | plaintext | pad) + byte[] calculatedHmac = getHmac(Ki, decrypted); + + if (debug) { + traceOutput("calculated Hmac", calculatedHmac, 0, + calculatedHmac.length); + traceOutput("message Hmac", ciphertext, cipherSize, + cksumSize); + } + + boolean cksumFailed = false; + if (calculatedHmac.length >= cksumSize) { + for (int i = 0; i < cksumSize; i++) { + if (calculatedHmac[i] != ciphertext[cipherSize+i]) { + cksumFailed = true; + break; + } + } + } + + if (cksumFailed) { + throw new GeneralSecurityException("Checksum failed"); + } + + // Prepare decrypted msg and ivec to be returned + // Last blockSize bytes of ciphertext without checksum + if (ivec != null && ivec.length == blockSize) { + System.arraycopy(ciphertext, start + cipherSize - blockSize, + ivec, 0, blockSize); + if (debug) { + traceOutput("new_state.ivec", ivec, 0, ivec.length); + } + } + + // Get rid of confounder + // [plaintext | padding] + byte[] plaintext = new byte[decrypted.length - blockSize]; + System.arraycopy(decrypted, blockSize, plaintext, + 0, plaintext.length); + return plaintext; // padding still there + } finally { + if (Ke != null) { + Arrays.fill(Ke, 0, Ke.length, (byte) 0); + } + if (Ki != null) { + Arrays.fill(Ki, 0, Ki.length, (byte) 0); + } + } + } + + // Round up to the next blocksize + int roundup(int n, int blocksize) { + return (((n + blocksize - 1) / blocksize) * blocksize); + } + + public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input, + int start, int len) throws GeneralSecurityException { + + if (!KeyUsage.isValid(usage)) { + throw new GeneralSecurityException("Invalid key usage number: " + + usage); + } + + // Derive keys + byte[] constant = new byte[5]; + constant[0] = (byte) ((usage>>24)&0xff); + constant[1] = (byte) ((usage>>16)&0xff); + constant[2] = (byte) ((usage>>8)&0xff); + constant[3] = (byte) (usage&0xff); + + constant[4] = (byte) 0x99; + + byte[] Kc = dk(baseKey, constant); // Checksum key + if (debug) { + System.err.println("usage: " + usage); + traceOutput("input", input, start, Math.min(len, 32)); + traceOutput("constant", constant, 0, constant.length); + traceOutput("baseKey", baseKey, 0, baseKey.length); + traceOutput("Kc", Kc, 0, Kc.length); + } + + try { + // Generate checksum + // H1 = HMAC(Kc, input) + byte[] hmac = getHmac(Kc, input); + if (debug) { + traceOutput("hmac", hmac, 0, hmac.length); + } + if (hmac.length == getChecksumLength()) { + return hmac; + } else if (hmac.length > getChecksumLength()) { + byte[] buf = new byte[getChecksumLength()]; + System.arraycopy(hmac, 0, buf, 0, buf.length); + return buf; + } else { + throw new GeneralSecurityException("checksum size too short: " + + hmac.length + "; expecting : " + getChecksumLength()); + } + } finally { + Arrays.fill(Kc, 0, Kc.length, (byte)0); + } + } + + // DK(Key, Constant) = random-to-key(DR(Key, Constant)) + byte[] dk(byte[] key, byte[] constant) + throws GeneralSecurityException { + return randomToKey(dr(key, constant)); + } + + /* + * From RFC 3961. + * + * DR(Key, Constant) = k-truncate(E(Key, Constant, + * initial-cipher-state)) + * + * Here DR is the random-octet generation function described below, and + * DK is the key-derivation function produced from it. In this + * construction, E(Key, Plaintext, CipherState) is a cipher, Constant is + * a well-known constant determined by the specific usage of this + * function, and k-truncate truncates its argument by taking the first k + * bits. Here, k is the key generation seed length needed for the + * encryption system. + * + * The output of the DR function is a string of bits; the actual key is + * produced by applying the cryptosystem's random-to-key operation on + * this bitstring. + * + * If the Constant is smaller than the cipher block size of E, then it + * must be expanded with n-fold() so it can be encrypted. If the output + * of E is shorter than k bits it is fed back into the encryption as + * many times as necessary. The construct is as follows (where | + * indicates concatentation): + * + * K1 = E(Key, n-fold(Constant), initial-cipher-state) + * K2 = E(Key, K1, initial-cipher-state) + * K3 = E(Key, K2, initial-cipher-state) + * K4 = ... + * + * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...) + */ + private byte[] dr(byte[] key, byte[] constant) + throws GeneralSecurityException { + + Cipher encCipher = getCipher(key, null, Cipher.ENCRYPT_MODE); + int blocksize = encCipher.getBlockSize(); + + if (constant.length != blocksize) { + constant = nfold(constant, blocksize * 8); + } + byte[] toBeEncrypted = constant; + + int keybytes = (getKeySeedLength()>>3); // from bits to bytes + byte[] rawkey = new byte[keybytes]; + int posn = 0; + + /* loop encrypting the blocks until enough key bytes are generated */ + int n = 0, len; + while (n < keybytes) { + if (debug) { + System.err.println("Encrypting: " + + bytesToString(toBeEncrypted)); + } + + byte[] cipherBlock = encCipher.doFinal(toBeEncrypted); + if (debug) { + System.err.println("K: " + ++posn + " = " + + bytesToString(cipherBlock)); + } + + len = (keybytes - n <= cipherBlock.length ? (keybytes - n) : + cipherBlock.length); + if (debug) { + System.err.println("copying " + len + " key bytes"); + } + System.arraycopy(cipherBlock, 0, rawkey, n, len); + n += len; + toBeEncrypted = cipherBlock; + } + return rawkey; + } + +// --------------------------------- + + // From MIT-1.3.1 distribution + /* + * n-fold(k-bits): + * l = lcm(n,k) + * r = l/k + * s = k-bits | k-bits rot 13 | k-bits rot 13*2 | ... | k-bits rot 13*(r-1) + * compute the 1's complement sum: + * n-fold = s[0..n-1]+s[n..2n-1]+s[2n..3n-1]+..+s[(k-1)*n..k*n-1] + */ + + /* + * representation: msb first, assume n and k are multiples of 8, and + * that k>=16. this is the case of all the cryptosystems which are + * likely to be used. this function can be replaced if that + * assumption ever fails. + */ + + /* input length is in bits */ + static byte[] nfold(byte[] in, int outbits) { + + int inbits = in.length; + outbits >>= 3; // count in bytes + + /* first compute lcm(n,k) */ + int a, b, c, lcm; + a = outbits; // n + b = inbits; // k + + while (b != 0) { + c = b; + b = a % b; + a = c; + } + lcm = outbits*inbits/a; + + if (debug) { + System.err.println("k: " + inbits); + System.err.println("n: " + outbits); + System.err.println("lcm: " + lcm); + } + + /* now do the real work */ + byte[] out = new byte[outbits]; + Arrays.fill(out, (byte)0); + + int thisbyte = 0; + int msbit, i, bval, oval; + + // this will end up cycling through k lcm(k,n)/k times, which + // is correct + for (i = lcm-1; i >= 0; i--) { + /* compute the msbit in k which gets added into this byte */ + msbit = (/* first, start with msbit in the first, unrotated byte */ + ((inbits<<3)-1) + /* then, for each byte, shift to right for each repetition */ + + (((inbits<<3)+13)*(i/inbits)) + /* last, pick out correct byte within that shifted repetition */ + + ((inbits-(i%inbits)) << 3)) % (inbits << 3); + + /* pull out the byte value itself */ + // Mask off values using &0xff to get only the lower byte + // Use >>> to avoid sign extension + bval = ((((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8)| + (in[((inbits)-(msbit>>>3))%inbits]&0xff)) + >>>((msbit&7)+1))&0xff; + + /* + System.err.println("((" + + ((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8) + + "|" + (in[((inbits)-(msbit>>>3))%inbits]&0xff) + ")" + + ">>>" + ((msbit&7)+1) + ")&0xff = " + bval); + */ + + thisbyte += bval; + + /* do the addition */ + // Mask off values using &0xff to get only the lower byte + oval = (out[i%outbits]&0xff); + thisbyte += oval; + out[i%outbits] = (byte) (thisbyte&0xff); + + if (debug) { + System.err.println("msbit[" + i + "] = " + msbit + "\tbval=" + + Integer.toHexString(bval) + "\toval=" + + Integer.toHexString(oval) + + "\tsum = " + Integer.toHexString(thisbyte)); + } + + + /* keep around the carry bit, if any */ + thisbyte >>>= 8; + + if (debug) { + System.err.println("carry=" + thisbyte); + } + } + + /* if there's a carry bit left over, add it back in */ + if (thisbyte != 0) { + for (i = outbits-1; i >= 0; i--) { + /* do the addition */ + thisbyte += (out[i]&0xff); + out[i] = (byte) (thisbyte&0xff); + + /* keep around the carry bit, if any */ + thisbyte >>>= 8; + } + } + + return out; + } + + // Routines used for debugging + static String bytesToString(byte[] digest) { + // Get character representation of digest + StringBuffer digestString = new StringBuffer(); + + for (int i = 0; i < digest.length; i++) { + if ((digest[i] & 0x000000ff) < 0x10) { + digestString.append("0" + + Integer.toHexString(digest[i] & 0x000000ff)); + } else { + digestString.append( + Integer.toHexString(digest[i] & 0x000000ff)); + } + } + return digestString.toString(); + } + + private static byte[] binaryStringToBytes(String str) { + char[] usageStr = str.toCharArray(); + byte[] usage = new byte[usageStr.length/2]; + for (int i = 0; i < usage.length; i++) { + byte a = Byte.parseByte(new String(usageStr, i*2, 1), 16); + byte b = Byte.parseByte(new String(usageStr, i*2 + 1, 1), 16); + usage[i] = (byte) ((a<<4)|b); + } + return usage; + } + + static void traceOutput(String traceTag, byte[] output, int offset, + int len) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(len); + new HexDumpEncoder().encodeBuffer( + new ByteArrayInputStream(output, offset, len), out); + + System.err.println(traceTag + ":" + out.toString()); + } catch (Exception e) { + } + } + +// String.getBytes("UTF-8"); +// Do this instead of using String to avoid making password immutable + static byte[] charToUtf8(char[] chars) { + Charset utf8 = Charset.forName("UTF-8"); + + CharBuffer cb = CharBuffer.wrap(chars); + ByteBuffer bb = utf8.encode(cb); + int len = bb.limit(); + byte[] answer = new byte[len]; + bb.get(answer, 0, len); + return answer; + } + + static byte[] charToUtf16(char[] chars) { + Charset utf8 = Charset.forName("UTF-16LE"); + + CharBuffer cb = CharBuffer.wrap(chars); + ByteBuffer bb = utf8.encode(cb); + int len = bb.limit(); + byte[] answer = new byte[len]; + bb.get(answer, 0, len); + return answer; + } +} diff --git a/src/sun/security/krb5/internal/ktab/KeyTab.java b/src/sun/security/krb5/internal/ktab/KeyTab.java new file mode 100644 index 00000000..5da7bc6f --- /dev/null +++ b/src/sun/security/krb5/internal/ktab/KeyTab.java @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ktab; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; +import sun.security.krb5.internal.crypto.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.Vector; +import sun.security.jgss.krb5.ServiceCreds; + +/** + * This class represents key table. The key table functions deal with storing + * and retrieving service keys for use in authentication exchanges. + * + * A KeyTab object is always constructed, if the file specified does not + * exist, it's still valid but empty. If there is an I/O error or file format + * error, it's invalid. + * + * The class is immutable on the read side (the write side is only used by + * the ktab tool). + * + * @author Yanni Zhang + */ +public class KeyTab implements KeyTabConstants { + + private static final boolean DEBUG = Krb5.DEBUG; + private static String defaultTabName = null; + + // Attention: Currently there is no way to remove a keytab from this map, + // this might lead to a memory leak. + private static Map map = new HashMap<>(); + + // KeyTab file does not exist. Note: a missing keytab is still valid + private boolean isMissing = false; + + // KeyTab file is invalid, possibly an I/O error or a file format error. + private boolean isValid = true; + + private final String tabName; + private long lastModified; + private int kt_vno = KRB5_KT_VNO; + + private Vector entries = new Vector<>(); + + /** + * Constructs a KeyTab object. + * + * If there is any I/O error or format errot during the loading, the + * isValid flag is set to false, and all half-read entries are dismissed. + * @param filename path name for the keytab file, must not be null + */ + private KeyTab(String filename) { + tabName = filename; + try { + lastModified = new File(tabName).lastModified(); + try (KeyTabInputStream kis = + new KeyTabInputStream(new FileInputStream(filename))) { + load(kis); + } + } catch (FileNotFoundException e) { + entries.clear(); + isMissing = true; + } catch (Exception ioe) { + entries.clear(); + isValid = false; + } + } + + /** + * Read a keytab file. Returns a new object and save it into cache when + * new content (modified since last read) is available. If keytab file is + * invalid, the old object will be returned. This is a safeguard for + * partial-written keytab files or non-stable network. Please note that + * a missing keytab is valid, which is equivalent to an empty keytab. + * + * @param s file name of keytab, must not be null + * @return the keytab object, can be invalid, but never null. + */ + private synchronized static KeyTab getInstance0(String s) { + long lm = new File(s).lastModified(); + KeyTab old = map.get(s); + if (old != null && old.isValid() && old.lastModified == lm) { + return old; + } + KeyTab ktab = new KeyTab(s); + if (ktab.isValid()) { // A valid new keytab + map.put(s, ktab); + return ktab; + } else if (old != null) { // An existing old one + return old; + } else { + return ktab; // first read is invalid + } + } + + /** + * Gets a KeyTab object. + * @param s the key tab file name. + * @return the KeyTab object, never null. + */ + public static KeyTab getInstance(String s) { + if (s == null) { + return getInstance(); + } else { + return getInstance0(normalize(s)); + } + } + + /** + * Gets a KeyTab object. + * @param file the key tab file. + * @return the KeyTab object, never null. + */ + public static KeyTab getInstance(File file) { + if (file == null) { + return getInstance(); + } else { + return getInstance0(file.getPath()); + } + } + + /** + * Gets the default KeyTab object. + * @return the KeyTab object, never null. + */ + public static KeyTab getInstance() { + return getInstance(getDefaultTabName()); + } + + public boolean isMissing() { + return isMissing; + } + + public boolean isValid() { + return isValid; + } + + /** + * The location of keytab file will be read from the configuration file + * If it is not specified, consider user.home as the keytab file's + * default location. + * @return never null + */ + private static String getDefaultTabName() { + if (defaultTabName != null) { + return defaultTabName; + } else { + String kname = null; + try { + String keytab_names = Config.getInstance().get + ("libdefaults", "default_keytab_name"); + if (keytab_names != null) { + StringTokenizer st = new StringTokenizer(keytab_names, " "); + while (st.hasMoreTokens()) { + kname = normalize(st.nextToken()); + if (new File(kname).exists()) { + break; + } + } + } + } catch (KrbException e) { + kname = null; + } + + if (kname == null) { + String user_home = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.home")); + + if (user_home == null) { + user_home = + java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.dir")); + } + + kname = user_home + File.separator + "krb5.keytab"; + } + defaultTabName = kname; + return kname; + } + } + + /** + * Normalizes some common keytab name formats into the bare file name. + * For example, FILE:/etc/krb5.keytab to /etc/krb5.keytab + * @param name never null + * @return never null + */ + // This method is used in this class and Krb5LoginModule + public static String normalize(String name) { + String kname; + if ((name.length() >= 5) && + (name.substring(0, 5).equalsIgnoreCase("FILE:"))) { + kname = name.substring(5); + } else if ((name.length() >= 9) && + (name.substring(0, 9).equalsIgnoreCase("ANY:FILE:"))) { + // this format found in MIT's krb5.ini. + kname = name.substring(9); + } else if ((name.length() >= 7) && + (name.substring(0, 7).equalsIgnoreCase("SRVTAB:"))) { + // this format found in MIT's krb5.ini. + kname = name.substring(7); + } else + kname = name; + return kname; + } + + private void load(KeyTabInputStream kis) + throws IOException, RealmException { + + entries.clear(); + kt_vno = kis.readVersion(); + if (kt_vno == KRB5_KT_VNO_1) { + kis.setNativeByteOrder(); + } + int entryLength = 0; + KeyTabEntry entry; + while (kis.available() > 0) { + entryLength = kis.readEntryLength(); + entry = kis.readEntry(entryLength, kt_vno); + if (DEBUG) { + System.out.println(">>> KeyTab: load() entry length: " + + entryLength + "; type: " + + (entry != null? entry.keyType : 0)); + } + if (entry != null) + entries.addElement(entry); + } + } + + /** + * Returns a principal name in this keytab. Used by + * {@link ServiceCreds#getKKeys()}. + */ + public PrincipalName getOneName() { + int size = entries.size(); + return size > 0 ? entries.elementAt(size-1).service : null; + } + + /** + * Reads all keys for a service from the keytab file that have + * etypes that have been configured for use. + * @param service the PrincipalName of the requested service + * @return an array containing all the service keys, never null + */ + public EncryptionKey[] readServiceKeys(PrincipalName service) { + KeyTabEntry entry; + EncryptionKey key; + int size = entries.size(); + ArrayList keys = new ArrayList<>(size); + if (DEBUG) { + System.out.println("Looking for keys for: " + service); + } + for (int i = size-1; i >= 0; i--) { + entry = entries.elementAt(i); + if (entry.service.match(service)) { + if (EType.isSupported(entry.keyType)) { + key = new EncryptionKey(entry.keyblock, + entry.keyType, + new Integer(entry.keyVersion)); + keys.add(key); + if (DEBUG) { + System.out.println("Added key: " + entry.keyType + + "version: " + entry.keyVersion); + } + } else if (DEBUG) { + System.out.println("Found unsupported keytype (" + + entry.keyType + ") for " + service); + } + } + } + size = keys.size(); + EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]); + + // Sort the keys by kvno. Sometimes we must choose a single key (say, + // generate encrypted timestamp in AS-REQ). A key with a higher KVNO + // sounds like a newer one. + Arrays.sort(retVal, new Comparator() { + @Override + public int compare(EncryptionKey o1, EncryptionKey o2) { + return o2.getKeyVersionNumber().intValue() + - o1.getKeyVersionNumber().intValue(); + } + }); + + return retVal; + } + + + + /** + * Searches for the service entry in the keytab file. + * The etype of the key must be one that has been configured + * to be used. + * @param service the PrincipalName of the requested service. + * @return true if the entry is found, otherwise, return false. + */ + public boolean findServiceEntry(PrincipalName service) { + KeyTabEntry entry; + for (int i = 0; i < entries.size(); i++) { + entry = entries.elementAt(i); + if (entry.service.match(service)) { + if (EType.isSupported(entry.keyType)) { + return true; + } else if (DEBUG) { + System.out.println("Found unsupported keytype (" + + entry.keyType + ") for " + service); + } + } + } + return false; + } + + public String tabName() { + return tabName; + } + + /////////////////// THE WRITE SIDE /////////////////////// + /////////////// only used by ktab tool ////////////////// + + /** + * Adds a new entry in the key table. + * @param service the service which will have a new entry in the key table. + * @param psswd the password which generates the key. + * @param kvno the kvno to use, -1 means automatic increasing + * @param append false if entries with old kvno would be removed. + * Note: if kvno is not -1, entries with the same kvno are always removed + */ + public void addEntry(PrincipalName service, char[] psswd, + int kvno, boolean append) throws KrbException { + addEntry(service, service.getSalt(), psswd, kvno, append); + } + + // Called by KDC test + public void addEntry(PrincipalName service, String salt, char[] psswd, + int kvno, boolean append) throws KrbException { + + EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys( + psswd, salt); + + // There should be only one maximum KVNO value for all etypes, so that + // all added keys can have the same KVNO. + + int maxKvno = 0; // only useful when kvno == -1 + for (int i = entries.size()-1; i >= 0; i--) { + KeyTabEntry e = entries.get(i); + if (e.service.match(service)) { + if (e.keyVersion > maxKvno) { + maxKvno = e.keyVersion; + } + if (!append || e.keyVersion == kvno) { + entries.removeElementAt(i); + } + } + } + if (kvno == -1) { + kvno = maxKvno + 1; + } + + for (int i = 0; encKeys != null && i < encKeys.length; i++) { + int keyType = encKeys[i].getEType(); + byte[] keyValue = encKeys[i].getBytes(); + + KeyTabEntry newEntry = new KeyTabEntry(service, + service.getRealm(), + new KerberosTime(System.currentTimeMillis()), + kvno, keyType, keyValue); + entries.addElement(newEntry); + } + } + + /** + * Gets the list of service entries in key table. + * @return array of KeyTabEntry. + */ + public KeyTabEntry[] getEntries() { + KeyTabEntry[] kentries = new KeyTabEntry[entries.size()]; + for (int i = 0; i < kentries.length; i++) { + kentries[i] = entries.elementAt(i); + } + return kentries; + } + + /** + * Creates a new default key table. + */ + public synchronized static KeyTab create() + throws IOException, RealmException { + String dname = getDefaultTabName(); + return create(dname); + } + + /** + * Creates a new default key table. + */ + public synchronized static KeyTab create(String name) + throws IOException, RealmException { + + try (KeyTabOutputStream kos = + new KeyTabOutputStream(new FileOutputStream(name))) { + kos.writeVersion(KRB5_KT_VNO); + } + return new KeyTab(name); + } + + /** + * Saves the file at the directory. + */ + public synchronized void save() throws IOException { + try (KeyTabOutputStream kos = + new KeyTabOutputStream(new FileOutputStream(tabName))) { + kos.writeVersion(kt_vno); + for (int i = 0; i < entries.size(); i++) { + kos.writeEntry(entries.elementAt(i)); + } + } + } + + /** + * Removes entries from the key table. + * @param service the service PrincipalName. + * @param etype the etype to match, remove all if -1 + * @param kvno what kvno to remove, -1 for all, -2 for old + * @return the number of entries deleted + */ + public int deleteEntries(PrincipalName service, int etype, int kvno) { + int count = 0; + + // Remember the highest KVNO for each etype. Used for kvno == -2 + Map highest = new HashMap<>(); + + for (int i = entries.size()-1; i >= 0; i--) { + KeyTabEntry e = entries.get(i); + if (service.match(e.getService())) { + if (etype == -1 || e.keyType == etype) { + if (kvno == -2) { + // Two rounds for kvno == -2. In the first round (here), + // only find out highest KVNO for each etype + if (highest.containsKey(e.keyType)) { + int n = highest.get(e.keyType); + if (e.keyVersion > n) { + highest.put(e.keyType, e.keyVersion); + } + } else { + highest.put(e.keyType, e.keyVersion); + } + } else if (kvno == -1 || e.keyVersion == kvno) { + entries.removeElementAt(i); + count++; + } + } + } + } + + // Second round for kvno == -2, remove old entries + if (kvno == -2) { + for (int i = entries.size()-1; i >= 0; i--) { + KeyTabEntry e = entries.get(i); + if (service.match(e.getService())) { + if (etype == -1 || e.keyType == etype) { + int n = highest.get(e.keyType); + if (e.keyVersion != n) { + entries.removeElementAt(i); + count++; + } + } + } + } + } + return count; + } + + /** + * Creates key table file version. + * @param file the key table file. + * @exception IOException. + */ + public synchronized void createVersion(File file) throws IOException { + try (KeyTabOutputStream kos = + new KeyTabOutputStream(new FileOutputStream(file))) { + kos.write16(KRB5_KT_VNO); + } + } +} diff --git a/src/sun/security/krb5/internal/ktab/KeyTabConstants.java b/src/sun/security/krb5/internal/ktab/KeyTabConstants.java new file mode 100644 index 00000000..7e4dd2a1 --- /dev/null +++ b/src/sun/security/krb5/internal/ktab/KeyTabConstants.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ktab; + +/** + * This class represents a Key Table entry. Each entry contains the service principal of + * the key, time stamp, key version and secret key itself. + * + * @author Yanni Zhang + */ +public interface KeyTabConstants { + final int principalComponentSize = 2; + final int realmSize = 2; + final int principalSize = 2; + final int principalTypeSize = 4; + final int timestampSize = 4; + final int keyVersionSize = 1; + final int keyTypeSize = 2; + final int keySize = 2; + static final int KRB5_KT_VNO_1 = 0x0501; /* krb v5, keytab version 1 (DCE compat) */ + static final int KRB5_KT_VNO = 0x0502; /* krb v5, keytab version 2 (standard) */ +} diff --git a/src/sun/security/krb5/internal/ktab/KeyTabEntry.java b/src/sun/security/krb5/internal/ktab/KeyTabEntry.java new file mode 100644 index 00000000..4113cd07 --- /dev/null +++ b/src/sun/security/krb5/internal/ktab/KeyTabEntry.java @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ktab; + +import sun.security.krb5.*; +import sun.security.krb5.internal.*; +import java.io.UnsupportedEncodingException; + +/** + * This class represents a Key Table entry. Each entry contains the service principal of + * the key, time stamp, key version and secret key itself. + * + * @author Yanni Zhang + */ +public class KeyTabEntry implements KeyTabConstants { + PrincipalName service; + Realm realm; + KerberosTime timestamp; + int keyVersion; + int keyType; + byte[] keyblock = null; + boolean DEBUG = Krb5.DEBUG; + + public KeyTabEntry (PrincipalName new_service, Realm new_realm, KerberosTime new_time, + int new_keyVersion, int new_keyType, byte[] new_keyblock) { + service = new_service; + realm = new_realm; + timestamp = new_time; + keyVersion = new_keyVersion; + keyType = new_keyType; + if (new_keyblock != null) { + keyblock = new_keyblock.clone(); + } + } + + public PrincipalName getService() { + return service; + } + + public EncryptionKey getKey() { + EncryptionKey key = new EncryptionKey(keyblock, + keyType, + new Integer(keyVersion)); + return key; + } + + public String getKeyString() { + StringBuffer sb = new StringBuffer("0x"); + for (int i = 0; i < keyblock.length; i++) { + sb.append(String.format("%02x", keyblock[i]&0xff)); + } + return sb.toString(); + } + public int entryLength() { + int totalPrincipalLength = 0; + String[] names = service.getNameStrings(); + for (int i = 0; i < names.length; i++) { + try { + totalPrincipalLength += principalSize + names[i].getBytes("8859_1").length; + } catch (UnsupportedEncodingException exc) { + } + } + + int realmLen = 0; + try { + realmLen = realm.toString().getBytes("8859_1").length; + } catch (UnsupportedEncodingException exc) { + } + + int size = principalComponentSize + realmSize + realmLen + + totalPrincipalLength + principalTypeSize + + timestampSize + keyVersionSize + + keyTypeSize + keySize + keyblock.length; + + if (DEBUG) { + System.out.println(">>> KeyTabEntry: key tab entry size is " + size); + } + return size; + } + + public KerberosTime getTimeStamp() { + return timestamp; + } +} diff --git a/src/sun/security/krb5/internal/ktab/KeyTabInputStream.java b/src/sun/security/krb5/internal/ktab/KeyTabInputStream.java new file mode 100644 index 00000000..9584131e --- /dev/null +++ b/src/sun/security/krb5/internal/ktab/KeyTabInputStream.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ktab; + +import sun.security.krb5.internal.*; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.Realm; +import sun.security.krb5.RealmException; +import sun.security.krb5.internal.util.KrbDataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * This class implements a buffered input stream. It is used for parsing key table + * data to memory. + * + * @author Yanni Zhang + * + */ +public class KeyTabInputStream extends KrbDataInputStream implements KeyTabConstants { + + boolean DEBUG = Krb5.DEBUG; + int index; + + public KeyTabInputStream(InputStream is) { + super(is); + } + /** + * Reads the number of bytes this entry data occupy. + */ + int readEntryLength() throws IOException { + return read(4); + } + + + KeyTabEntry readEntry(int entryLen, int ktVersion) throws IOException, RealmException { + index = entryLen; + if (index == 0) { //in native implementation, when the last entry is deleted, a byte 0 is left. + return null; + } + if (index < 0) { //in native implementation, when one of the entries is deleted, the entry length turns to be negative, and + skip(Math.abs(index)); //the fields are left with 0 bytes + return null; + } + int principalNum = read(2); //the number of service names. + index -= 2; + if (ktVersion == KRB5_KT_VNO_1) { //V1 includes realm in the count. + principalNum -= 1; + } + Realm realm = new Realm(readName()); + String[] nameParts = new String[principalNum]; + for (int i = 0; i < principalNum; i++) { + nameParts[i] = readName(); + } + int nameType = read(4); + index -= 4; + PrincipalName service = new PrincipalName(nameType, nameParts, realm); + KerberosTime timeStamp = readTimeStamp(); + + int keyVersion = read() & 0xff; + index -= 1; + int keyType = read(2); + index -= 2; + int keyLength = read(2); + index -= 2; + byte[] keyblock = readKey(keyLength); + index -= keyLength; + // There might be a 32 bit kvno here. + // If index is zero, assume that the 8 bit key version number was + // right, otherwise trust the new nonzero value. + if (index >= 4) { + int extKvno = read(4); + if (extKvno != 0) { + keyVersion = extKvno; + } + index -= 4; + } + + // if index is negative, the keytab format must be wrong. + if (index < 0) { + throw new RealmException("Keytab is corrupted"); + } + + // ignore the left bytes. + skip(index); + + return new KeyTabEntry(service, realm, timeStamp, keyVersion, keyType, keyblock); + } + + byte[] readKey(int length) throws IOException { + byte[] bytes = new byte[length]; + read(bytes, 0, length); + return bytes; + } + + KerberosTime readTimeStamp() throws IOException { + index -= 4; + return new KerberosTime((long)read(4) * 1000); + } + + String readName() throws IOException { + String name; + int length = read(2); //length of the realm name or service name + index -= 2; + byte[] bytes = new byte[length]; + read(bytes, 0, length); + index -= length; + name = new String(bytes); + if (DEBUG) { + System.out.println(">>> KeyTabInputStream, readName(): " + name); + } + return name; + } +} diff --git a/src/sun/security/krb5/internal/ktab/KeyTabOutputStream.java b/src/sun/security/krb5/internal/ktab/KeyTabOutputStream.java new file mode 100644 index 00000000..96764c72 --- /dev/null +++ b/src/sun/security/krb5/internal/ktab/KeyTabOutputStream.java @@ -0,0 +1,102 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.ktab; + +import sun.security.krb5.internal.util.KrbDataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +/** + * This class implements a buffered input stream. It is used for parsing key table + * data to memory. + * + * @author Yanni Zhang + * + */ +public class KeyTabOutputStream extends KrbDataOutputStream implements KeyTabConstants { + private KeyTabEntry entry; + private int keyType; + private byte[] keyValue; + public int version; + + public KeyTabOutputStream(OutputStream os) { + super(os); + } + + public void writeVersion(int num) throws IOException { + version = num; + write16(num); //we use the standard version. + } + + public void writeEntry(KeyTabEntry entry) throws IOException { + write32(entry.entryLength()); + String[] serviceNames = entry.service.getNameStrings(); + int comp_num = serviceNames.length; + if (version == KRB5_KT_VNO_1) { + write16(comp_num + 1); + } + else write16(comp_num); + + byte[] realm = null; + try { + realm = entry.service.getRealmString().getBytes("8859_1"); + } catch (UnsupportedEncodingException exc) { + } + + write16(realm.length); + write(realm); + for (int i = 0; i < comp_num; i++) { + try { + write16(serviceNames[i].getBytes("8859_1").length); + write(serviceNames[i].getBytes("8859_1")); + } catch (UnsupportedEncodingException exc) { + } + } + write32(entry.service.getNameType()); + //time is long, but we only use 4 bytes to store the data. + write32((int)(entry.timestamp.getTime()/1000)); + + // the key version might be a 32 bit extended number. + write8(entry.keyVersion % 256 ); + write16(entry.keyType); + write16(entry.keyblock.length); + write(entry.keyblock); + + // if the key version isn't smaller than 256, it could be saved as + // extension key version number in 4 bytes. The nonzero extension + // key version number will be trusted. However, it isn't standardized + // yet, we won't support it. + // if (entry.keyVersion >= 256) { + // write32(entry.keyVersion); + //} + } +} diff --git a/src/sun/security/krb5/internal/rcache/AuthList.java b/src/sun/security/krb5/internal/rcache/AuthList.java new file mode 100644 index 00000000..1764271a --- /dev/null +++ b/src/sun/security/krb5/internal/rcache/AuthList.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.rcache; + +import sun.security.krb5.internal.Krb5; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; +import sun.security.krb5.internal.KerberosTime; +import sun.security.krb5.internal.KrbApErrException; + +/** + * This class provides an efficient caching mechanism to store AuthTimeWithHash + * from client authenticators. The cache minimizes the memory usage by doing + * self-cleanup of expired items in the cache. + * + * AuthTimeWithHash objects inside a cache are always sorted from big (new) to + * small (old) as determined by {@see AuthTimeWithHash#compareTo}. In the most + * common case a newcomer should be newer than the first element. + * + * @author Yanni Zhang + */ +public class AuthList { + + private final LinkedList entries; + private final int lifespan; + + /** + * Constructs a AuthList. + */ + public AuthList(int lifespan) { + this.lifespan = lifespan; + entries = new LinkedList<>(); + } + + /** + * Puts the authenticator timestamp into the cache in descending order, + * and throw an exception if it's already there. + */ + public void put(AuthTimeWithHash t, KerberosTime currentTime) + throws KrbApErrException { + + if (entries.isEmpty()) { + entries.addFirst(t); + } else { + AuthTimeWithHash temp = entries.getFirst(); + int cmp = temp.compareTo(t); + if (cmp < 0) { + // This is the most common case, newly received authenticator + // has larger timestamp. + entries.addFirst(t); + } else if (cmp == 0) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + } else { + //unless client clock being re-adjusted. + ListIterator it = entries.listIterator(1); + boolean found = false; + while (it.hasNext()) { + temp = it.next(); + cmp = temp.compareTo(t); + if (cmp < 0) { + // Find an older one, put in front of it + entries.add(entries.indexOf(temp), t); + found = true; + break; + } else if (cmp == 0) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + } + } + if (!found) { + // All is newer than the newcomer. Sigh. + entries.addLast(t); + } + } + } + + // let us cleanup while we are here + long timeLimit = currentTime.getSeconds() - lifespan; + ListIterator it = entries.listIterator(0); + AuthTimeWithHash temp = null; + int index = -1; + while (it.hasNext()) { + // search expired timestamps. + temp = it.next(); + if (temp.ctime < timeLimit) { + index = entries.indexOf(temp); + break; + } + } + // It would be nice if LinkedList has a method called truncate(index). + if (index > -1) { + do { + // remove expired timestamps from the list. + entries.removeLast(); + } while(entries.size() > index); + } + } + + public boolean isEmpty() { + return entries.isEmpty(); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + Iterator iter = entries.descendingIterator(); + int pos = entries.size(); + while (iter.hasNext()) { + AuthTimeWithHash at = iter.next(); + sb.append('#').append(pos--).append(": ") + .append(at.toString()).append('\n'); + } + return sb.toString(); + } +} diff --git a/src/sun/security/krb5/internal/rcache/AuthTime.java b/src/sun/security/krb5/internal/rcache/AuthTime.java new file mode 100644 index 00000000..7183d7d5 --- /dev/null +++ b/src/sun/security/krb5/internal/rcache/AuthTime.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.rcache; + +import java.io.IOException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.SeekableByteChannel; +import java.nio.charset.StandardCharsets; +import java.util.StringTokenizer; + +/** + * The class represents an old style replay cache entry. It is only used in + * a dfl file. + * + * @author Sun/Oracle + * @author Yanni Zhang + */ +public class AuthTime { + final int ctime; + final int cusec; + final String client; + final String server; + + /** + * Constructs an AuthTime. + */ + public AuthTime(String client, String server, + int ctime, int cusec) { + this.ctime = ctime; + this.cusec = cusec; + this.client = client; + this.server = server; + } + + @Override + public String toString() { + return String.format("%d/%06d/----/%s", ctime, cusec, client); + } + + // Methods used when saved in a dfl file. See DflCache.java + + /** + * Reads an LC style string from a channel, which is a int32 length + * plus a UTF-8 encoded string possibly ends with \0. + * @throws IOException if there is a format error + * @throws BufferUnderflowException if goes beyond the end + */ + private static String readStringWithLength(SeekableByteChannel chan) + throws IOException { + ByteBuffer bb = ByteBuffer.allocate(4); + bb.order(ByteOrder.nativeOrder()); + chan.read(bb); + bb.flip(); + int len = bb.getInt(); + if (len > 1024) { + // Memory attack? The string should be fairly short. + throw new IOException("Invalid string length"); + } + bb = ByteBuffer.allocate(len); + if (chan.read(bb) != len) { + throw new IOException("Not enough string"); + } + byte[] data = bb.array(); + return (data[len-1] == 0)? + new String(data, 0, len-1, StandardCharsets.UTF_8): + new String(data, StandardCharsets.UTF_8); + } + + /** + * Reads an AuthTime or AuthTimeWithHash object from a channel. + * @throws IOException if there is a format error + * @throws BufferUnderflowException if goes beyond the end + */ + public static AuthTime readFrom(SeekableByteChannel chan) + throws IOException { + String client = readStringWithLength(chan); + String server = readStringWithLength(chan); + ByteBuffer bb = ByteBuffer.allocate(8); + chan.read(bb); + bb.order(ByteOrder.nativeOrder()); + int cusec = bb.getInt(0); + int ctime = bb.getInt(4); + if (client.isEmpty()) { + StringTokenizer st = new StringTokenizer(server, " :"); + if (st.countTokens() != 6) { + throw new IOException("Incorrect rcache style"); + } + st.nextToken(); + String hash = st.nextToken(); + st.nextToken(); + client = st.nextToken(); + st.nextToken(); + server = st.nextToken(); + return new AuthTimeWithHash( + client, server, ctime, cusec, hash); + } else { + return new AuthTime( + client, server, ctime, cusec); + } + } + + /** + * Encodes to be used in a dfl file + */ + protected byte[] encode0(String cstring, String sstring) { + byte[] c = cstring.getBytes(StandardCharsets.UTF_8);; + byte[] s = sstring.getBytes(StandardCharsets.UTF_8);; + byte[] zero = new byte[1]; + int len = 4 + c.length + 1 + 4 + s.length + 1 + 4 + 4; + ByteBuffer bb = ByteBuffer.allocate(len) + .order(ByteOrder.nativeOrder()); + bb.putInt(c.length+1).put(c).put(zero) + .putInt(s.length+1).put(s).put(zero) + .putInt(cusec).putInt(ctime); + return bb.array(); + } + + /** + * Encodes to be used in a dfl file + * @param withHash useless here + */ + public byte[] encode(boolean withHash) { + return encode0(client, server); + } +} diff --git a/src/sun/security/krb5/internal/rcache/AuthTimeWithHash.java b/src/sun/security/krb5/internal/rcache/AuthTimeWithHash.java new file mode 100644 index 00000000..52bc8a82 --- /dev/null +++ b/src/sun/security/krb5/internal/rcache/AuthTimeWithHash.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.rcache; + +import java.util.Objects; + +/** + * The class represents a new style replay cache entry. It can be either used + * inside memory or in a dfl file. + */ +public class AuthTimeWithHash extends AuthTime + implements Comparable { + + final String hash; + + /** + * Constructs a new AuthTimeWithHash. + */ + public AuthTimeWithHash(String client, String server, + int ctime, int cusec, String hash) { + super(client, server, ctime, cusec); + this.hash = hash; + } + + /** + * Compares if an object equals to an AuthTimeWithHash object. + * @param o an object. + * @return true if two objects are equivalent, otherwise, return false. + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof AuthTimeWithHash)) return false; + AuthTimeWithHash that = (AuthTimeWithHash)o; + return Objects.equals(hash, that.hash) + && Objects.equals(client, that.client) + && Objects.equals(server, that.server) + && ctime == that.ctime + && cusec == that.cusec; + } + + /** + * Returns a hash code for this AuthTimeWithHash object. + */ + @Override + public int hashCode() { + return Objects.hash(hash); + } + + @Override + public String toString() { + return String.format("%d/%06d/%s/%s", ctime, cusec, hash, client); + } + + @Override + public int compareTo(AuthTimeWithHash other) { + int cmp = 0; + if (ctime != other.ctime) { + cmp = Integer.compare(ctime, other.ctime); + } else if (cusec != other.cusec) { + cmp = Integer.compare(cusec, other.cusec); + } else { + cmp = hash.compareTo(other.hash); + } + return cmp; + } + + /** + * Compares with a possibly old style object. Used + * in DflCache$Storage#loadAndCheck. + * @return true if all AuthTime fields are the same + */ + public boolean isSameIgnoresHash(AuthTime old) { + return client.equals(old.client) && + server.equals(old.server) && + ctime == old.ctime && + cusec == old.cusec; + } + + // Methods used when saved in a dfl file. See DflCache.java + + /** + * Encodes to be used in a dfl file + * @param withHash write new style if true + */ + @Override + public byte[] encode(boolean withHash) { + String cstring; + String sstring; + if (withHash) { + cstring = ""; + sstring = String.format("HASH:%s %d:%s %d:%s", hash, + client.length(), client, + server.length(), server); + } else { + cstring = client; + sstring = server; + } + return encode0(cstring, sstring); + } +} diff --git a/src/sun/security/krb5/internal/rcache/DflCache.java b/src/sun/security/krb5/internal/rcache/DflCache.java new file mode 100644 index 00000000..6c5d124f --- /dev/null +++ b/src/sun/security/krb5/internal/rcache/DflCache.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.krb5.internal.rcache; + +import java.io.*; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.PosixFilePermission; +import java.security.AccessController; +import java.util.*; + +import sun.security.action.GetPropertyAction; +import sun.security.krb5.internal.KerberosTime; +import sun.security.krb5.internal.Krb5; +import sun.security.krb5.internal.KrbApErrException; +import sun.security.krb5.internal.ReplayCache; + + +/** + * A dfl file is used to sustores AuthTime entries when the system property + * sun.security.krb5.rcache is set to + * + * dfl(|:path/|:path/name|:name) + * + * The file will be path/name. If path is not given, it will be + * + * System.getProperty("java.io.tmpdir") + * + * If name is not given, it will be + * + * service_euid + * + * Java does not have a method to get euid, so uid is used instead. This + * should normally to be since a Java program is seldom used as a setuid app. + * + * The file has a header: + * + * i16 0x0501 (KRB5_RC_VNO) in network order + * i32 number of seconds for lifespan (in native order, same below) + * + * followed by cache entries concatenated, which can be encoded in + * 2 styles: + * + * The traditional style is: + * + * LC of client principal + * LC of server principal + * i32 cusec of Authenticator + * i32 ctime of Authenticator + * + * The new style has a hash: + * + * LC of "" + * LC of "HASH:%s %lu:%s %lu:%s" of (hash, clientlen, client, serverlen, + * server) where msghash is 32 char (lower case) text mode md5sum + * of the ciphertext of authenticator. + * i32 cusec of Authenticator + * i32 ctime of Authenticator + * + * where LC of a string means + * + * i32 strlen(string) + 1 + * octets of string, with the \0x00 ending + * + * The old style block is always created by MIT krb5 used even if a new style + * is available, which means there can be 2 entries for a single Authenticator. + * Java also does this way. + * + * See src/lib/krb5/rcache/rc_io.c and src/lib/krb5/rcache/rc_dfl.c. + */ +public class DflCache extends ReplayCache { + + private static final int KRB5_RV_VNO = 0x501; + private static final int EXCESSREPS = 30; // if missed-hit>this, recreate + + private final String source; + + private static int uid; + static { + try { + // Available on Solaris, Linux and Mac. Otherwise, no _euid suffix + Class clazz = Class.forName("com.sun.security.auth.module.UnixSystem"); + uid = (int)(long)(Long) + clazz.getMethod("getUid").invoke(clazz.newInstance()); + } catch (Exception e) { + uid = -1; + } + } + + public DflCache (String source) { + this.source = source; + } + + private static String defaultPath() { + return AccessController.doPrivileged( + new GetPropertyAction("java.io.tmpdir")); + } + + private static String defaultFile(String server) { + // service/host@REALM -> service + int slash = server.indexOf('/'); + if (slash == -1) { + // A normal principal? say, dummy@REALM + slash = server.indexOf('@'); + } + if (slash != -1) { + // Should not happen, but be careful + server= server.substring(0, slash); + } + if (uid != -1) { + server += "_" + uid; + } + return server; + } + + private static Path getFileName(String source, String server) { + String path, file; + if (source.equals("dfl")) { + path = defaultPath(); + file = defaultFile(server); + } else if (source.startsWith("dfl:")) { + source = source.substring(4); + int pos = source.lastIndexOf('/'); + int pos1 = source.lastIndexOf('\\'); + if (pos1 > pos) pos = pos1; + if (pos == -1) { + // Only file name + path = defaultPath(); + file = source; + } else if (new File(source).isDirectory()) { + // Only path + path = source; + file = defaultFile(server); + } else { + // Full pathname + path = null; + file = source; + } + } else { + throw new IllegalArgumentException(); + } + return new File(path, file).toPath(); + } + + @Override + public void checkAndStore(KerberosTime currTime, AuthTimeWithHash time) + throws KrbApErrException { + try { + checkAndStore0(currTime, time); + } catch (IOException ioe) { + KrbApErrException ke = new KrbApErrException(Krb5.KRB_ERR_GENERIC); + ke.initCause(ioe); + throw ke; + } + } + + private synchronized void checkAndStore0(KerberosTime currTime, AuthTimeWithHash time) + throws IOException, KrbApErrException { + Path p = getFileName(source, time.server); + int missed = 0; + try (Storage s = new Storage()) { + try { + missed = s.loadAndCheck(p, time, currTime); + } catch (IOException ioe) { + // Non-existing or invalid file + Storage.create(p); + missed = s.loadAndCheck(p, time, currTime); + } + s.append(time); + } + if (missed > EXCESSREPS) { + Storage.expunge(p, currTime); + } + } + + + private static class Storage implements Closeable { + // Static methods + @SuppressWarnings("try") + private static void create(Path p) throws IOException { + try (SeekableByteChannel newChan = createNoClose(p)) { + // Do nothing, wait for close + } + makeMine(p); + } + + private static void makeMine(Path p) throws IOException { + // chmod to owner-rw only, otherwise MIT krb5 rejects + try { + Set attrs = new HashSet<>(); + attrs.add(PosixFilePermission.OWNER_READ); + attrs.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(p, attrs); + } catch (UnsupportedOperationException uoe) { + // No POSIX permission. That's OK. + } + } + + private static SeekableByteChannel createNoClose(Path p) + throws IOException { + SeekableByteChannel newChan = Files.newByteChannel( + p, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE); + ByteBuffer buffer = ByteBuffer.allocate(6); + buffer.putShort((short)KRB5_RV_VNO); + buffer.order(ByteOrder.nativeOrder()); + buffer.putInt(KerberosTime.getDefaultSkew()); + buffer.flip(); + newChan.write(buffer); + return newChan; + } + + private static void expunge(Path p, KerberosTime currTime) + throws IOException { + Path p2 = Files.createTempFile(p.getParent(), "rcache", null); + try (SeekableByteChannel oldChan = Files.newByteChannel(p); + SeekableByteChannel newChan = createNoClose(p2)) { + long timeLimit = currTime.getSeconds() - readHeader(oldChan); + while (true) { + try { + AuthTime at = AuthTime.readFrom(oldChan); + if (at.ctime > timeLimit) { + ByteBuffer bb = ByteBuffer.wrap(at.encode(true)); + newChan.write(bb); + } + } catch (BufferUnderflowException e) { + break; + } + } + } + makeMine(p2); + Files.move(p2, p, + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.ATOMIC_MOVE); + } + + // Instance methods + SeekableByteChannel chan; + private int loadAndCheck(Path p, AuthTimeWithHash time, + KerberosTime currTime) + throws IOException, KrbApErrException { + int missed = 0; + if (Files.isSymbolicLink(p)) { + throw new IOException("Symlink not accepted"); + } + try { + Set perms = + Files.getPosixFilePermissions(p); + if (uid != -1 && + (Integer)Files.getAttribute(p, "unix:uid") != uid) { + throw new IOException("Not mine"); + } + if (perms.contains(PosixFilePermission.GROUP_READ) || + perms.contains(PosixFilePermission.GROUP_WRITE) || + perms.contains(PosixFilePermission.GROUP_EXECUTE) || + perms.contains(PosixFilePermission.OTHERS_READ) || + perms.contains(PosixFilePermission.OTHERS_WRITE) || + perms.contains(PosixFilePermission.OTHERS_EXECUTE)) { + throw new IOException("Accessible by someone else"); + } + } catch (UnsupportedOperationException uoe) { + // No POSIX permissions? Ignore it. + } + chan = Files.newByteChannel(p, StandardOpenOption.WRITE, + StandardOpenOption.READ); + + long timeLimit = currTime.getSeconds() - readHeader(chan); + + long pos = 0; + boolean seeNewButNotSame = false; + while (true) { + try { + pos = chan.position(); + AuthTime a = AuthTime.readFrom(chan); + if (a instanceof AuthTimeWithHash) { + if (time.equals(a)) { + // Exact match, must be a replay + throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + } else if (time.isSameIgnoresHash(a)) { + // Two different authenticators in the same second. + // Remember it + seeNewButNotSame = true; + } + } else { + if (time.isSameIgnoresHash(a)) { + // Two authenticators in the same second. Considered + // same if we haven't seen a new style version of it + if (!seeNewButNotSame) { + throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); + } + } + } + if (a.ctime < timeLimit) { + missed++; + } else { + missed--; + } + } catch (BufferUnderflowException e) { + // Half-written file? + chan.position(pos); + break; + } + } + return missed; + } + + private static int readHeader(SeekableByteChannel chan) + throws IOException { + ByteBuffer bb = ByteBuffer.allocate(6); + chan.read(bb); + if (bb.getShort(0) != KRB5_RV_VNO) { + throw new IOException("Not correct rcache version"); + } + bb.order(ByteOrder.nativeOrder()); + return bb.getInt(2); + } + + private void append(AuthTimeWithHash at) throws IOException { + // Write an entry with hash, to be followed by one without it, + // for the benefit of old implementations. + ByteBuffer bb; + bb = ByteBuffer.wrap(at.encode(true)); + chan.write(bb); + bb = ByteBuffer.wrap(at.encode(false)); + chan.write(bb); + } + + @Override + public void close() throws IOException { + if (chan != null) chan.close(); + chan = null; + } + } +} diff --git a/src/sun/security/krb5/internal/rcache/MemoryCache.java b/src/sun/security/krb5/internal/rcache/MemoryCache.java new file mode 100644 index 00000000..403d97f8 --- /dev/null +++ b/src/sun/security/krb5/internal/rcache/MemoryCache.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.rcache; + +import java.util.*; +import sun.security.krb5.internal.KerberosTime; +import sun.security.krb5.internal.KrbApErrException; +import sun.security.krb5.internal.ReplayCache; + +/** + * This class stores replay caches. AuthTimeWithHash objects are categorized + * into AuthLists keyed by the names of client and server. + * + * @author Yanni Zhang + */ +public class MemoryCache extends ReplayCache { + + // TODO: One day we'll need to read dynamic krb5.conf. + private static final int lifespan = KerberosTime.getDefaultSkew(); + private static final boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG; + + private final Map content = new HashMap<>(); + + @Override + public synchronized void checkAndStore(KerberosTime currTime, AuthTimeWithHash time) + throws KrbApErrException { + String key = time.client + "|" + time.server; + AuthList rc = content.get(key); + if (DEBUG) { + System.out.println("MemoryCache: add " + time + " to " + key); + } + if (rc == null) { + rc = new AuthList(lifespan); + rc.put(time, currTime); + if (!rc.isEmpty()) { + content.put(key, rc); + } + } else { + if (DEBUG) { + System.out.println("MemoryCache: Existing AuthList:\n" + rc); + } + rc.put(time, currTime); + if (rc.isEmpty()) { + content.remove(key); + } + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + for (AuthList rc: content.values()) { + sb.append(rc.toString()); + } + return sb.toString(); + } +} diff --git a/src/sun/security/krb5/internal/util/KerberosFlags.java b/src/sun/security/krb5/internal/util/KerberosFlags.java new file mode 100644 index 00000000..37a3309f --- /dev/null +++ b/src/sun/security/krb5/internal/util/KerberosFlags.java @@ -0,0 +1,107 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.util; + +import java.io.IOException; +import java.util.Arrays; +import sun.security.krb5.internal.Krb5; +import sun.security.util.BitArray; +import sun.security.util.DerOutputStream; + +/** + * A wrapper class around sun.security.util.BitArray, so that KDCOptions, + * TicketFlags and ApOptions in krb5 classes can utilize some functions + * in BitArray classes. + * + * The data type is defined in RFC 4120 as: + * + * 5.2.8. KerberosFlags + * + * For several message types, a specific constrained bit string type, + * KerberosFlags, is used. + * + * KerberosFlags ::= BIT STRING (SIZE (32..MAX)) + * -- minimum number of bits shall be sent, + * -- but no fewer than 32 + * + * @author Yanni Zhang + */ +public class KerberosFlags { + BitArray bits; + + // This constant is used by child classes. + protected static final int BITS_PER_UNIT = 8; + + public KerberosFlags(int length) throws IllegalArgumentException { + bits = new BitArray(length); + } + + public KerberosFlags(int length, byte[] a) throws IllegalArgumentException { + bits = new BitArray(length, a); + if (length != Krb5.KRB_FLAGS_MAX+1) { + bits = new BitArray(Arrays.copyOf(bits.toBooleanArray(), Krb5.KRB_FLAGS_MAX+1)); + } + } + + public KerberosFlags(boolean[] bools) { + bits = new BitArray((bools.length==Krb5.KRB_FLAGS_MAX+1)? + bools: + Arrays.copyOf(bools, Krb5.KRB_FLAGS_MAX+1)); + } + + public void set(int index, boolean value) { + bits.set(index, value); + } + + public boolean get(int index) { + return bits.get(index); + } + + public boolean[] toBooleanArray() { + return bits.toBooleanArray(); + } + + /** + * Writes the encoded data. + * + * @exception IOException if an I/O error occurs while reading encoded data. + * @return an byte array of encoded KDCOptions. + */ + public byte[] asn1Encode() throws IOException { + DerOutputStream out = new DerOutputStream(); + out.putUnalignedBitString(bits); + return out.toByteArray(); + } + + public String toString() { + return bits.toString(); + } +} diff --git a/src/sun/security/krb5/internal/util/KerberosString.java b/src/sun/security/krb5/internal/util/KerberosString.java new file mode 100644 index 00000000..bdca3cf7 --- /dev/null +++ b/src/sun/security/krb5/internal/util/KerberosString.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.krb5.internal.util; + +import java.io.IOException; +import java.security.AccessController; +import sun.security.action.GetBooleanAction; +import sun.security.util.DerValue; + +/** + * Implements the ASN.1 KerberosString type. + * + *

    + * KerberosString  ::= GeneralString (IA5String)
    + * 
    + * + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public final class KerberosString { + /** + * RFC 4120 defines KerberosString as GeneralString (IA5String), which + * only includes ASCII characters. However, other implementations have been + * known to use GeneralString to contain UTF-8 encoding. To interop + * with these implementations, the following system property is defined. + * When set as true, KerberosString is encoded as UTF-8. Note that this + * only affects the byte encoding, the tag of the ASN.1 type is still + * GeneralString. + */ + public static final boolean MSNAME = AccessController.doPrivileged( + new GetBooleanAction("sun.security.krb5.msinterop.kstring")); + + private final String s; + + public KerberosString(String s) { + this.s = s; + } + + public KerberosString(DerValue der) throws IOException { + if (der.tag != DerValue.tag_GeneralString) { + throw new IOException( + "KerberosString's tag is incorrect: " + der.tag); + } + s = new String(der.getDataBytes(), MSNAME?"UTF8":"ASCII"); + } + + public String toString() { + return s; + } + + public DerValue toDerValue() throws IOException { + // No need to cache the result since this method is + // only called once. + return new DerValue(DerValue.tag_GeneralString, + s.getBytes(MSNAME?"UTF8":"ASCII")); + } +} diff --git a/src/sun/security/krb5/internal/util/KrbDataInputStream.java b/src/sun/security/krb5/internal/util/KrbDataInputStream.java new file mode 100644 index 00000000..d7a531be --- /dev/null +++ b/src/sun/security/krb5/internal/util/KrbDataInputStream.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.util; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * This class implements a buffered input stream. It provides methods to read a chunck + * of data from underlying data stream. + * + * @author Yanni Zhang + * + */ +public class KrbDataInputStream extends BufferedInputStream{ + private boolean bigEndian = true; + + public void setNativeByteOrder() { + if (java.nio.ByteOrder.nativeOrder(). + equals(java.nio.ByteOrder.BIG_ENDIAN)) { + bigEndian = true; + } else { + bigEndian = false; + } + } + public KrbDataInputStream(InputStream is){ + super(is); + } + + /** + * Reads a length value which is represented in 4 bytes from + * this input stream. The value must be positive. + * @return the length value represented by this byte array. + * @throws IOException if there are not enough bytes or it represents + * a negative value + */ + final public int readLength4() throws IOException { + int len = read(4); + if (len < 0) { + throw new IOException("Invalid encoding"); + } + return len; + } + + /** + * Reads up to the specific number of bytes from this input stream. + * @param num the number of bytes to be read. + * @return the int value of this byte array. + * @throws IOException if there are not enough bytes + */ + public int read(int num) throws IOException { + byte[] bytes = new byte[num]; + if (read(bytes, 0, num) != num) { + throw new IOException("Premature end of stream reached"); + } + int result = 0; + for (int i = 0; i < num; i++) { + if (bigEndian) { + result |= (bytes[i] & 0xff) << (num - i - 1) * 8; + } else { + result |= (bytes[i] & 0xff) << i * 8; + } + } + return result; + } + + public int readVersion() throws IOException { + // always read in big-endian mode + int result = (read() & 0xff) << 8; + return result | (read() & 0xff); + } +} diff --git a/src/sun/security/krb5/internal/util/KrbDataOutputStream.java b/src/sun/security/krb5/internal/util/KrbDataOutputStream.java new file mode 100644 index 00000000..7eca1ebd --- /dev/null +++ b/src/sun/security/krb5/internal/util/KrbDataOutputStream.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * + * (C) Copyright IBM Corp. 1999 All Rights Reserved. + * Copyright 1997 The Open Group Research Institute. All rights reserved. + */ + +package sun.security.krb5.internal.util; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * This class implements a buffered output stream. It provides methods to write a chunck of + * bytes to underlying data stream. + * + * @author Yanni Zhang + * + */ +public class KrbDataOutputStream extends BufferedOutputStream { + public KrbDataOutputStream(OutputStream os) { + super(os); + } + public void write32(int num) throws IOException { + byte[] bytes = new byte[4]; + bytes[0] = (byte)((num & 0xff000000) >> 24 & 0xff); + bytes[1] = (byte)((num & 0x00ff0000) >> 16 & 0xff); + bytes[2] = (byte)((num & 0x0000ff00) >> 8 & 0xff); + bytes[3] = (byte)(num & 0xff); + write(bytes, 0, 4); + } + + public void write16(int num) throws IOException { + byte[] bytes = new byte[2]; + bytes[0] = (byte)((num & 0xff00) >> 8 & 0xff); + bytes[1] = (byte)(num & 0xff); + write(bytes, 0, 2); + } + + public void write8(int num) throws IOException { + write(num & 0xff); + } +} diff --git a/src/sun/security/pkcs/ContentInfo.java b/src/sun/security/pkcs/ContentInfo.java new file mode 100644 index 00000000..bc78d0f1 --- /dev/null +++ b/src/sun/security/pkcs/ContentInfo.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.*; + +import sun.security.util.*; + +/** + * A ContentInfo type, as defined in PKCS#7. + * + * @author Benjamin Renaud + */ + +public class ContentInfo { + + // pkcs7 pre-defined content types + private static int[] pkcs7 = {1, 2, 840, 113549, 1, 7}; + private static int[] data = {1, 2, 840, 113549, 1, 7, 1}; + private static int[] sdata = {1, 2, 840, 113549, 1, 7, 2}; + private static int[] edata = {1, 2, 840, 113549, 1, 7, 3}; + private static int[] sedata = {1, 2, 840, 113549, 1, 7, 4}; + private static int[] ddata = {1, 2, 840, 113549, 1, 7, 5}; + private static int[] crdata = {1, 2, 840, 113549, 1, 7, 6}; + private static int[] nsdata = {2, 16, 840, 1, 113730, 2, 5}; + // timestamp token (id-ct-TSTInfo) from RFC 3161 + private static int[] tstInfo = {1, 2, 840, 113549, 1, 9, 16, 1, 4}; + // this is for backwards-compatibility with JDK 1.1.x + private static final int[] OLD_SDATA = {1, 2, 840, 1113549, 1, 7, 2}; + private static final int[] OLD_DATA = {1, 2, 840, 1113549, 1, 7, 1}; + public static ObjectIdentifier PKCS7_OID; + public static ObjectIdentifier DATA_OID; + public static ObjectIdentifier SIGNED_DATA_OID; + public static ObjectIdentifier ENVELOPED_DATA_OID; + public static ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID; + public static ObjectIdentifier DIGESTED_DATA_OID; + public static ObjectIdentifier ENCRYPTED_DATA_OID; + public static ObjectIdentifier OLD_SIGNED_DATA_OID; + public static ObjectIdentifier OLD_DATA_OID; + public static ObjectIdentifier NETSCAPE_CERT_SEQUENCE_OID; + public static ObjectIdentifier TIMESTAMP_TOKEN_INFO_OID; + + static { + PKCS7_OID = ObjectIdentifier.newInternal(pkcs7); + DATA_OID = ObjectIdentifier.newInternal(data); + SIGNED_DATA_OID = ObjectIdentifier.newInternal(sdata); + ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(edata); + SIGNED_AND_ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(sedata); + DIGESTED_DATA_OID = ObjectIdentifier.newInternal(ddata); + ENCRYPTED_DATA_OID = ObjectIdentifier.newInternal(crdata); + OLD_SIGNED_DATA_OID = ObjectIdentifier.newInternal(OLD_SDATA); + OLD_DATA_OID = ObjectIdentifier.newInternal(OLD_DATA); + /** + * The ASN.1 systax for the Netscape Certificate Sequence + * data type is defined + * + * here. + */ + NETSCAPE_CERT_SEQUENCE_OID = ObjectIdentifier.newInternal(nsdata); + TIMESTAMP_TOKEN_INFO_OID = ObjectIdentifier.newInternal(tstInfo); + } + + ObjectIdentifier contentType; + DerValue content; // OPTIONAL + + public ContentInfo(ObjectIdentifier contentType, DerValue content) { + this.contentType = contentType; + this.content = content; + } + + /** + * Make a contentInfo of type data. + */ + public ContentInfo(byte[] bytes) { + DerValue octetString = new DerValue(DerValue.tag_OctetString, bytes); + this.contentType = DATA_OID; + this.content = octetString; + } + + /** + * Parses a PKCS#7 content info. + */ + public ContentInfo(DerInputStream derin) + throws IOException, ParsingException + { + this(derin, false); + } + + /** + * Parses a PKCS#7 content info. + * + *

    This constructor is used only for backwards compatibility with + * PKCS#7 blocks that were generated using JDK1.1.x. + * + * @param derin the ASN.1 encoding of the content info. + * @param oldStyle flag indicating whether or not the given content info + * is encoded according to JDK1.1.x. + */ + public ContentInfo(DerInputStream derin, boolean oldStyle) + throws IOException, ParsingException + { + DerInputStream disType; + DerInputStream disTaggedContent; + DerValue type; + DerValue taggedContent; + DerValue[] typeAndContent; + DerValue[] contents; + + typeAndContent = derin.getSequence(2); + + // Parse the content type + type = typeAndContent[0]; + disType = new DerInputStream(type.toByteArray()); + contentType = disType.getOID(); + + if (oldStyle) { + // JDK1.1.x-style encoding + content = typeAndContent[1]; + } else { + // This is the correct, standards-compliant encoding. + // Parse the content (OPTIONAL field). + // Skip the [0] EXPLICIT tag by pretending that the content is the + // one and only element in an implicitly tagged set + if (typeAndContent.length > 1) { // content is OPTIONAL + taggedContent = typeAndContent[1]; + disTaggedContent + = new DerInputStream(taggedContent.toByteArray()); + contents = disTaggedContent.getSet(1, true); + content = contents[0]; + } + } + } + + public DerValue getContent() { + return content; + } + + public ObjectIdentifier getContentType() { + return contentType; + } + + public byte[] getData() throws IOException { + if (contentType.equals((Object)DATA_OID) || + contentType.equals((Object)OLD_DATA_OID) || + contentType.equals((Object)TIMESTAMP_TOKEN_INFO_OID)) { + if (content == null) + return null; + else + return content.getOctetString(); + } + throw new IOException("content type is not DATA: " + contentType); + } + + public void encode(DerOutputStream out) throws IOException { + DerOutputStream contentDerCode; + DerOutputStream seq; + + seq = new DerOutputStream(); + seq.putOID(contentType); + + // content is optional, it could be external + if (content != null) { + DerValue taggedContent = null; + contentDerCode = new DerOutputStream(); + content.encode(contentDerCode); + + // Add the [0] EXPLICIT tag in front of the content encoding + taggedContent = new DerValue((byte)0xA0, + contentDerCode.toByteArray()); + seq.putDerValue(taggedContent); + } + + out.write(DerValue.tag_Sequence, seq); + } + + /** + * Returns a byte array representation of the data held in + * the content field. + */ + public byte[] getContentBytes() throws IOException { + if (content == null) + return null; + + DerInputStream dis = new DerInputStream(content.toByteArray()); + return dis.getOctetString(); + } + + public String toString() { + String out = ""; + + out += "Content Info Sequence\n\tContent type: " + contentType + "\n"; + out += "\tContent: " + content; + return out; + } +} diff --git a/src/sun/security/pkcs/EncryptedPrivateKeyInfo.java b/src/sun/security/pkcs/EncryptedPrivateKeyInfo.java new file mode 100644 index 00000000..d9a5dcf1 --- /dev/null +++ b/src/sun/security/pkcs/EncryptedPrivateKeyInfo.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.*; +import sun.security.x509.*; +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * This class implements the EncryptedPrivateKeyInfo type, + * which is defined in PKCS #8 as follows: + * + *

    + * EncryptedPrivateKeyInfo ::=  SEQUENCE {
    + *     encryptionAlgorithm   AlgorithmIdentifier,
    + *     encryptedData   OCTET STRING }
    + * 
    + * + * @author Jan Luehe + * + */ + +public class EncryptedPrivateKeyInfo { + + // the "encryptionAlgorithm" field + private AlgorithmId algid; + + // the "encryptedData" field + private byte[] encryptedData; + + // the ASN.1 encoded contents of this class + private byte[] encoded; + + /** + * Constructs (i.e., parses) an EncryptedPrivateKeyInfo from + * its encoding. + */ + public EncryptedPrivateKeyInfo(byte[] encoded) + throws IOException + { + if (encoded == null) { + throw new IllegalArgumentException("encoding must not be null"); + } + + DerValue val = new DerValue(encoded); + + DerValue[] seq = new DerValue[2]; + + seq[0] = val.data.getDerValue(); + seq[1] = val.data.getDerValue(); + + if (val.data.available() != 0) { + throw new IOException("overrun, bytes = " + val.data.available()); + } + + this.algid = AlgorithmId.parse(seq[0]); + if (seq[0].data.available() != 0) { + throw new IOException("encryptionAlgorithm field overrun"); + } + + this.encryptedData = seq[1].getOctetString(); + if (seq[1].data.available() != 0) + throw new IOException("encryptedData field overrun"); + + this.encoded = encoded.clone(); + } + + /** + * Constructs an EncryptedPrivateKeyInfo from the + * encryption algorithm and the encrypted data. + */ + public EncryptedPrivateKeyInfo(AlgorithmId algid, byte[] encryptedData) { + this.algid = algid; + this.encryptedData = encryptedData.clone(); + } + + /** + * Returns the encryption algorithm. + */ + public AlgorithmId getAlgorithm() { + return this.algid; + } + + /** + * Returns the encrypted data. + */ + public byte[] getEncryptedData() { + return this.encryptedData.clone(); + } + + /** + * Returns the ASN.1 encoding of this class. + */ + public byte[] getEncoded() + throws IOException + { + if (this.encoded != null) return this.encoded.clone(); + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + // encode encryption algorithm + algid.encode(tmp); + + // encode encrypted data + tmp.putOctetString(encryptedData); + + // wrap everything into a SEQUENCE + out.write(DerValue.tag_Sequence, tmp); + this.encoded = out.toByteArray(); + + return this.encoded.clone(); + } + + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof EncryptedPrivateKeyInfo)) + return false; + try { + byte[] thisEncrInfo = this.getEncoded(); + byte[] otherEncrInfo + = ((EncryptedPrivateKeyInfo)other).getEncoded(); + + if (thisEncrInfo.length != otherEncrInfo.length) + return false; + for (int i = 0; i < thisEncrInfo.length; i++) + if (thisEncrInfo[i] != otherEncrInfo[i]) + return false; + return true; + } catch (IOException e) { + return false; + } + } + + /** + * Returns a hashcode for this EncryptedPrivateKeyInfo. + * + * @return a hashcode for this EncryptedPrivateKeyInfo. + */ + public int hashCode() { + int retval = 0; + + for (int i = 0; i < this.encryptedData.length; i++) + retval += this.encryptedData[i] * i; + return retval; + } +} diff --git a/src/sun/security/pkcs/PKCS7.java b/src/sun/security/pkcs/PKCS7.java new file mode 100644 index 00000000..fdc295c6 --- /dev/null +++ b/src/sun/security/pkcs/PKCS7.java @@ -0,0 +1,951 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.*; +import java.math.BigInteger; +import java.net.URI; +import java.util.*; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509CRL; +import java.security.cert.CRLException; +import java.security.cert.CertificateFactory; +import java.security.*; + +import sun.security.timestamp.*; +import sun.security.util.*; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CertInfo; +import sun.security.x509.X509CRLImpl; +import sun.security.x509.X500Name; + +/** + * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile + * Supports only SignedData ContentInfo + * type, where to the type of data signed is plain Data. + * For signedData, crls, attributes and + * PKCS#6 Extended Certificates are not supported. + * + * @author Benjamin Renaud + */ +public class PKCS7 { + + private ObjectIdentifier contentType; + + // the ASN.1 members for a signedData (and other) contentTypes + private BigInteger version = null; + private AlgorithmId[] digestAlgorithmIds = null; + private ContentInfo contentInfo = null; + private X509Certificate[] certificates = null; + private X509CRL[] crls = null; + private SignerInfo[] signerInfos = null; + + private boolean oldStyle = false; // Is this JDK1.1.x-style? + + private Principal[] certIssuerNames; + + /* + * Random number generator for creating nonce values + * (Lazy initialization) + */ + private static class SecureRandomHolder { + static final SecureRandom RANDOM; + static { + SecureRandom tmp = null; + try { + tmp = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + // should not happen + } + RANDOM = tmp; + } + } + + /* + * Object identifier for the timestamping key purpose. + */ + private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8"; + + /* + * Object identifier for extendedKeyUsage extension + */ + private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; + + /** + * Unmarshals a PKCS7 block from its encoded form, parsing the + * encoded bytes from the InputStream. + * + * @param in an input stream holding at least one PKCS7 block. + * @exception ParsingException on parsing errors. + * @exception IOException on other errors. + */ + public PKCS7(InputStream in) throws ParsingException, IOException { + DataInputStream dis = new DataInputStream(in); + byte[] data = new byte[dis.available()]; + dis.readFully(data); + + parse(new DerInputStream(data)); + } + + /** + * Unmarshals a PKCS7 block from its encoded form, parsing the + * encoded bytes from the DerInputStream. + * + * @param derin a DerInputStream holding at least one PKCS7 block. + * @exception ParsingException on parsing errors. + */ + public PKCS7(DerInputStream derin) throws ParsingException { + parse(derin); + } + + /** + * Unmarshals a PKCS7 block from its encoded form, parsing the + * encoded bytes. + * + * @param bytes the encoded bytes. + * @exception ParsingException on parsing errors. + */ + public PKCS7(byte[] bytes) throws ParsingException { + try { + DerInputStream derin = new DerInputStream(bytes); + parse(derin); + } catch (IOException ioe1) { + ParsingException pe = new ParsingException( + "Unable to parse the encoded bytes"); + pe.initCause(ioe1); + throw pe; + } + } + + /* + * Parses a PKCS#7 block. + */ + private void parse(DerInputStream derin) + throws ParsingException + { + try { + derin.mark(derin.available()); + // try new (i.e., JDK1.2) style + parse(derin, false); + } catch (IOException ioe) { + try { + derin.reset(); + // try old (i.e., JDK1.1.x) style + parse(derin, true); + oldStyle = true; + } catch (IOException ioe1) { + ParsingException pe = new ParsingException( + ioe1.getMessage()); + pe.initCause(ioe); + pe.addSuppressed(ioe1); + throw pe; + } + } + } + + /** + * Parses a PKCS#7 block. + * + * @param derin the ASN.1 encoding of the PKCS#7 block. + * @param oldStyle flag indicating whether or not the given PKCS#7 block + * is encoded according to JDK1.1.x. + */ + private void parse(DerInputStream derin, boolean oldStyle) + throws IOException + { + contentInfo = new ContentInfo(derin, oldStyle); + contentType = contentInfo.contentType; + DerValue content = contentInfo.getContent(); + + if (contentType.equals((Object)ContentInfo.SIGNED_DATA_OID)) { + parseSignedData(content); + } else if (contentType.equals((Object)ContentInfo.OLD_SIGNED_DATA_OID)) { + // This is for backwards compatibility with JDK 1.1.x + parseOldSignedData(content); + } else if (contentType.equals((Object) + ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){ + parseNetscapeCertChain(content); + } else { + throw new ParsingException("content type " + contentType + + " not supported."); + } + } + + /** + * Construct an initialized PKCS7 block. + * + * @param digestAlgorithmIds the message digest algorithm identifiers. + * @param contentInfo the content information. + * @param certificates an array of X.509 certificates. + * @param crls an array of CRLs + * @param signerInfos an array of signer information. + */ + public PKCS7(AlgorithmId[] digestAlgorithmIds, + ContentInfo contentInfo, + X509Certificate[] certificates, + X509CRL[] crls, + SignerInfo[] signerInfos) { + + version = BigInteger.ONE; + this.digestAlgorithmIds = digestAlgorithmIds; + this.contentInfo = contentInfo; + this.certificates = certificates; + this.crls = crls; + this.signerInfos = signerInfos; + } + + public PKCS7(AlgorithmId[] digestAlgorithmIds, + ContentInfo contentInfo, + X509Certificate[] certificates, + SignerInfo[] signerInfos) { + this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos); + } + + private void parseNetscapeCertChain(DerValue val) + throws ParsingException, IOException { + DerInputStream dis = new DerInputStream(val.toByteArray()); + DerValue[] contents = dis.getSequence(2); + certificates = new X509Certificate[contents.length]; + + CertificateFactory certfac = null; + try { + certfac = CertificateFactory.getInstance("X.509"); + } catch (CertificateException ce) { + // do nothing + } + + for (int i=0; i < contents.length; i++) { + ByteArrayInputStream bais = null; + try { + if (certfac == null) + certificates[i] = new X509CertImpl(contents[i]); + else { + byte[] encoded = contents[i].toByteArray(); + bais = new ByteArrayInputStream(encoded); + certificates[i] = + (X509Certificate)certfac.generateCertificate(bais); + bais.close(); + bais = null; + } + } catch (CertificateException ce) { + ParsingException pe = new ParsingException(ce.getMessage()); + pe.initCause(ce); + throw pe; + } catch (IOException ioe) { + ParsingException pe = new ParsingException(ioe.getMessage()); + pe.initCause(ioe); + throw pe; + } finally { + if (bais != null) + bais.close(); + } + } + } + + private void parseSignedData(DerValue val) + throws ParsingException, IOException { + + DerInputStream dis = val.toDerInputStream(); + + // Version + version = dis.getBigInteger(); + + // digestAlgorithmIds + DerValue[] digestAlgorithmIdVals = dis.getSet(1); + int len = digestAlgorithmIdVals.length; + digestAlgorithmIds = new AlgorithmId[len]; + try { + for (int i = 0; i < len; i++) { + DerValue oid = digestAlgorithmIdVals[i]; + digestAlgorithmIds[i] = AlgorithmId.parse(oid); + } + + } catch (IOException e) { + ParsingException pe = + new ParsingException("Error parsing digest AlgorithmId IDs: " + + e.getMessage()); + pe.initCause(e); + throw pe; + } + // contentInfo + contentInfo = new ContentInfo(dis); + + CertificateFactory certfac = null; + try { + certfac = CertificateFactory.getInstance("X.509"); + } catch (CertificateException ce) { + // do nothing + } + + /* + * check if certificates (implicit tag) are provided + * (certificates are OPTIONAL) + */ + if ((byte)(dis.peekByte()) == (byte)0xA0) { + DerValue[] certVals = dis.getSet(2, true); + + len = certVals.length; + certificates = new X509Certificate[len]; + int count = 0; + + for (int i = 0; i < len; i++) { + ByteArrayInputStream bais = null; + try { + byte tag = certVals[i].getTag(); + // We only parse the normal certificate. Other types of + // CertificateChoices ignored. + if (tag == DerValue.tag_Sequence) { + if (certfac == null) { + certificates[count] = new X509CertImpl(certVals[i]); + } else { + byte[] encoded = certVals[i].toByteArray(); + bais = new ByteArrayInputStream(encoded); + certificates[count] = + (X509Certificate)certfac.generateCertificate(bais); + bais.close(); + bais = null; + } + count++; + } + } catch (CertificateException ce) { + ParsingException pe = new ParsingException(ce.getMessage()); + pe.initCause(ce); + throw pe; + } catch (IOException ioe) { + ParsingException pe = new ParsingException(ioe.getMessage()); + pe.initCause(ioe); + throw pe; + } finally { + if (bais != null) + bais.close(); + } + } + if (count != len) { + certificates = Arrays.copyOf(certificates, count); + } + } + + // check if crls (implicit tag) are provided (crls are OPTIONAL) + if ((byte)(dis.peekByte()) == (byte)0xA1) { + DerValue[] crlVals = dis.getSet(1, true); + + len = crlVals.length; + crls = new X509CRL[len]; + + for (int i = 0; i < len; i++) { + ByteArrayInputStream bais = null; + try { + if (certfac == null) + crls[i] = new X509CRLImpl(crlVals[i]); + else { + byte[] encoded = crlVals[i].toByteArray(); + bais = new ByteArrayInputStream(encoded); + crls[i] = (X509CRL) certfac.generateCRL(bais); + bais.close(); + bais = null; + } + } catch (CRLException e) { + ParsingException pe = + new ParsingException(e.getMessage()); + pe.initCause(e); + throw pe; + } finally { + if (bais != null) + bais.close(); + } + } + } + + // signerInfos + DerValue[] signerInfoVals = dis.getSet(1); + + len = signerInfoVals.length; + signerInfos = new SignerInfo[len]; + + for (int i = 0; i < len; i++) { + DerInputStream in = signerInfoVals[i].toDerInputStream(); + signerInfos[i] = new SignerInfo(in); + } + } + + /* + * Parses an old-style SignedData encoding (for backwards + * compatibility with JDK1.1.x). + */ + private void parseOldSignedData(DerValue val) + throws ParsingException, IOException + { + DerInputStream dis = val.toDerInputStream(); + + // Version + version = dis.getBigInteger(); + + // digestAlgorithmIds + DerValue[] digestAlgorithmIdVals = dis.getSet(1); + int len = digestAlgorithmIdVals.length; + + digestAlgorithmIds = new AlgorithmId[len]; + try { + for (int i = 0; i < len; i++) { + DerValue oid = digestAlgorithmIdVals[i]; + digestAlgorithmIds[i] = AlgorithmId.parse(oid); + } + } catch (IOException e) { + throw new ParsingException("Error parsing digest AlgorithmId IDs"); + } + + // contentInfo + contentInfo = new ContentInfo(dis, true); + + // certificates + CertificateFactory certfac = null; + try { + certfac = CertificateFactory.getInstance("X.509"); + } catch (CertificateException ce) { + // do nothing + } + DerValue[] certVals = dis.getSet(2); + len = certVals.length; + certificates = new X509Certificate[len]; + + for (int i = 0; i < len; i++) { + ByteArrayInputStream bais = null; + try { + if (certfac == null) + certificates[i] = new X509CertImpl(certVals[i]); + else { + byte[] encoded = certVals[i].toByteArray(); + bais = new ByteArrayInputStream(encoded); + certificates[i] = + (X509Certificate)certfac.generateCertificate(bais); + bais.close(); + bais = null; + } + } catch (CertificateException ce) { + ParsingException pe = new ParsingException(ce.getMessage()); + pe.initCause(ce); + throw pe; + } catch (IOException ioe) { + ParsingException pe = new ParsingException(ioe.getMessage()); + pe.initCause(ioe); + throw pe; + } finally { + if (bais != null) + bais.close(); + } + } + + // crls are ignored. + dis.getSet(0); + + // signerInfos + DerValue[] signerInfoVals = dis.getSet(1); + len = signerInfoVals.length; + signerInfos = new SignerInfo[len]; + for (int i = 0; i < len; i++) { + DerInputStream in = signerInfoVals[i].toDerInputStream(); + signerInfos[i] = new SignerInfo(in, true); + } + } + + /** + * Encodes the signed data to an output stream. + * + * @param out the output stream to write the encoded data to. + * @exception IOException on encoding errors. + */ + public void encodeSignedData(OutputStream out) throws IOException { + DerOutputStream derout = new DerOutputStream(); + encodeSignedData(derout); + out.write(derout.toByteArray()); + } + + /** + * Encodes the signed data to a DerOutputStream. + * + * @param out the DerOutputStream to write the encoded data to. + * @exception IOException on encoding errors. + */ + public void encodeSignedData(DerOutputStream out) + throws IOException + { + DerOutputStream signedData = new DerOutputStream(); + + // version + signedData.putInteger(version); + + // digestAlgorithmIds + signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds); + + // contentInfo + contentInfo.encode(signedData); + + // certificates (optional) + if (certificates != null && certificates.length != 0) { + // cast to X509CertImpl[] since X509CertImpl implements DerEncoder + X509CertImpl implCerts[] = new X509CertImpl[certificates.length]; + for (int i = 0; i < certificates.length; i++) { + if (certificates[i] instanceof X509CertImpl) + implCerts[i] = (X509CertImpl) certificates[i]; + else { + try { + byte[] encoded = certificates[i].getEncoded(); + implCerts[i] = new X509CertImpl(encoded); + } catch (CertificateException ce) { + throw new IOException(ce); + } + } + } + + // Add the certificate set (tagged with [0] IMPLICIT) + // to the signed data + signedData.putOrderedSetOf((byte)0xA0, implCerts); + } + + // CRLs (optional) + if (crls != null && crls.length != 0) { + // cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder + Set implCRLs = new HashSet(crls.length); + for (X509CRL crl: crls) { + if (crl instanceof X509CRLImpl) + implCRLs.add((X509CRLImpl) crl); + else { + try { + byte[] encoded = crl.getEncoded(); + implCRLs.add(new X509CRLImpl(encoded)); + } catch (CRLException ce) { + throw new IOException(ce); + } + } + } + + // Add the CRL set (tagged with [1] IMPLICIT) + // to the signed data + signedData.putOrderedSetOf((byte)0xA1, + implCRLs.toArray(new X509CRLImpl[implCRLs.size()])); + } + + // signerInfos + signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos); + + // making it a signed data block + DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence, + signedData.toByteArray()); + + // making it a content info sequence + ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID, + signedDataSeq); + + // writing out the contentInfo sequence + block.encode(out); + } + + /** + * This verifies a given SignerInfo. + * + * @param info the signer information. + * @param bytes the DER encoded content information. + * + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception SignatureException on signature handling errors. + */ + public SignerInfo verify(SignerInfo info, byte[] bytes) + throws NoSuchAlgorithmException, SignatureException { + return info.verify(this, bytes); + } + + /** + * Returns all signerInfos which self-verify. + * + * @param bytes the DER encoded content information. + * + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception SignatureException on signature handling errors. + */ + public SignerInfo[] verify(byte[] bytes) + throws NoSuchAlgorithmException, SignatureException { + + Vector intResult = new Vector(); + for (int i = 0; i < signerInfos.length; i++) { + + SignerInfo signerInfo = verify(signerInfos[i], bytes); + if (signerInfo != null) { + intResult.addElement(signerInfo); + } + } + if (!intResult.isEmpty()) { + + SignerInfo[] result = new SignerInfo[intResult.size()]; + intResult.copyInto(result); + return result; + } + return null; + } + + /** + * Returns all signerInfos which self-verify. + * + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception SignatureException on signature handling errors. + */ + public SignerInfo[] verify() + throws NoSuchAlgorithmException, SignatureException { + return verify(null); + } + + /** + * Returns the version number of this PKCS7 block. + * @return the version or null if version is not specified + * for the content type. + */ + public BigInteger getVersion() { + return version; + } + + /** + * Returns the message digest algorithms specified in this PKCS7 block. + * @return the array of Digest Algorithms or null if none are specified + * for the content type. + */ + public AlgorithmId[] getDigestAlgorithmIds() { + return digestAlgorithmIds; + } + + /** + * Returns the content information specified in this PKCS7 block. + */ + public ContentInfo getContentInfo() { + return contentInfo; + } + + /** + * Returns the X.509 certificates listed in this PKCS7 block. + * @return a clone of the array of X.509 certificates or null if + * none are specified for the content type. + */ + public X509Certificate[] getCertificates() { + if (certificates != null) + return certificates.clone(); + else + return null; + } + + /** + * Returns the X.509 crls listed in this PKCS7 block. + * @return a clone of the array of X.509 crls or null if none + * are specified for the content type. + */ + public X509CRL[] getCRLs() { + if (crls != null) + return crls.clone(); + else + return null; + } + + /** + * Returns the signer's information specified in this PKCS7 block. + * @return the array of Signer Infos or null if none are specified + * for the content type. + */ + public SignerInfo[] getSignerInfos() { + return signerInfos; + } + + /** + * Returns the X.509 certificate listed in this PKCS7 block + * which has a matching serial number and Issuer name, or + * null if one is not found. + * + * @param serial the serial number of the certificate to retrieve. + * @param issuerName the Distinguished Name of the Issuer. + */ + public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) { + if (certificates != null) { + if (certIssuerNames == null) + populateCertIssuerNames(); + for (int i = 0; i < certificates.length; i++) { + X509Certificate cert = certificates[i]; + BigInteger thisSerial = cert.getSerialNumber(); + if (serial.equals(thisSerial) + && issuerName.equals(certIssuerNames[i])) + { + return cert; + } + } + } + return null; + } + + /** + * Populate array of Issuer DNs from certificates and convert + * each Principal to type X500Name if necessary. + */ + private void populateCertIssuerNames() { + if (certificates == null) + return; + + certIssuerNames = new Principal[certificates.length]; + for (int i = 0; i < certificates.length; i++) { + X509Certificate cert = certificates[i]; + Principal certIssuerName = cert.getIssuerDN(); + if (!(certIssuerName instanceof X500Name)) { + // must extract the original encoded form of DN for + // subsequent name comparison checks (converting to a + // String and back to an encoded DN could cause the + // types of String attribute values to be changed) + try { + X509CertInfo tbsCert = + new X509CertInfo(cert.getTBSCertificate()); + certIssuerName = (Principal) + tbsCert.get(X509CertInfo.ISSUER + "." + + X509CertInfo.DN_NAME); + } catch (Exception e) { + // error generating X500Name object from the cert's + // issuer DN, leave name as is. + } + } + certIssuerNames[i] = certIssuerName; + } + } + + /** + * Returns the PKCS7 block in a printable string form. + */ + public String toString() { + String out = ""; + + out += contentInfo + "\n"; + if (version != null) + out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n"; + if (digestAlgorithmIds != null) { + out += "PKCS7 :: digest AlgorithmIds: \n"; + for (int i = 0; i < digestAlgorithmIds.length; i++) + out += "\t" + digestAlgorithmIds[i] + "\n"; + } + if (certificates != null) { + out += "PKCS7 :: certificates: \n"; + for (int i = 0; i < certificates.length; i++) + out += "\t" + i + ". " + certificates[i] + "\n"; + } + if (crls != null) { + out += "PKCS7 :: crls: \n"; + for (int i = 0; i < crls.length; i++) + out += "\t" + i + ". " + crls[i] + "\n"; + } + if (signerInfos != null) { + out += "PKCS7 :: signer infos: \n"; + for (int i = 0; i < signerInfos.length; i++) + out += ("\t" + i + ". " + signerInfos[i] + "\n"); + } + return out; + } + + /** + * Returns true if this is a JDK1.1.x-style PKCS#7 block, and false + * otherwise. + */ + public boolean isOldStyle() { + return this.oldStyle; + } + + /** + * Assembles a PKCS #7 signed data message that optionally includes a + * signature timestamp. + * + * @param signature the signature bytes + * @param signerChain the signer's X.509 certificate chain + * @param content the content that is signed; specify null to not include + * it in the PKCS7 data + * @param signatureAlgorithm the name of the signature algorithm + * @param tsaURI the URI of the Timestamping Authority; or null if no + * timestamp is requested + * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a + * numerical object identifier; or null if we leave the TSA server + * to choose one. This argument is only used when tsaURI is provided + * @return the bytes of the encoded PKCS #7 signed data message + * @throws NoSuchAlgorithmException The exception is thrown if the signature + * algorithm is unrecognised. + * @throws CertificateException The exception is thrown if an error occurs + * while processing the signer's certificate or the TSA's + * certificate. + * @throws IOException The exception is thrown if an error occurs while + * generating the signature timestamp or while generating the signed + * data message. + */ + public static byte[] generateSignedData(byte[] signature, + X509Certificate[] signerChain, + byte[] content, + String signatureAlgorithm, + URI tsaURI, + String tSAPolicyID) + throws CertificateException, IOException, NoSuchAlgorithmException + { + + // Generate the timestamp token + PKCS9Attributes unauthAttrs = null; + if (tsaURI != null) { + // Timestamp the signature + HttpTimestamper tsa = new HttpTimestamper(tsaURI); + byte[] tsToken = generateTimestampToken(tsa, tSAPolicyID, signature); + + // Insert the timestamp token into the PKCS #7 signer info element + // (as an unsigned attribute) + unauthAttrs = + new PKCS9Attributes(new PKCS9Attribute[]{ + new PKCS9Attribute( + PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_STR, + tsToken)}); + } + + // Create the SignerInfo + X500Name issuerName = + X500Name.asX500Name(signerChain[0].getIssuerX500Principal()); + BigInteger serialNumber = signerChain[0].getSerialNumber(); + String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm); + String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm); + SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber, + AlgorithmId.get(digAlg), null, + AlgorithmId.get(encAlg), + signature, unauthAttrs); + + // Create the PKCS #7 signed data message + SignerInfo[] signerInfos = {signerInfo}; + AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()}; + // Include or exclude content + ContentInfo contentInfo = (content == null) + ? new ContentInfo(ContentInfo.DATA_OID, null) + : new ContentInfo(content); + PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo, + signerChain, signerInfos); + ByteArrayOutputStream p7out = new ByteArrayOutputStream(); + pkcs7.encodeSignedData(p7out); + + return p7out.toByteArray(); + } + + /** + * Requests, processes and validates a timestamp token from a TSA using + * common defaults. Uses the following defaults in the timestamp request: + * SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate + * set to true. + * + * @param tsa the timestamping authority to use + * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a + * numerical object identifier; or null if we leave the TSA server + * to choose one + * @param toBeTimestamped the token that is to be timestamped + * @return the encoded timestamp token + * @throws IOException The exception is thrown if an error occurs while + * communicating with the TSA, or a non-null + * TSAPolicyID is specified in the request but it + * does not match the one in the reply + * @throws CertificateException The exception is thrown if the TSA's + * certificate is not permitted for timestamping. + */ + private static byte[] generateTimestampToken(Timestamper tsa, + String tSAPolicyID, + byte[] toBeTimestamped) + throws IOException, CertificateException + { + // Generate a timestamp + MessageDigest messageDigest = null; + TSRequest tsQuery = null; + try { + // SHA-1 is always used. + messageDigest = MessageDigest.getInstance("SHA-1"); + tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest); + } catch (NoSuchAlgorithmException e) { + // ignore + } + + // Generate a nonce + BigInteger nonce = null; + if (SecureRandomHolder.RANDOM != null) { + nonce = new BigInteger(64, SecureRandomHolder.RANDOM); + tsQuery.setNonce(nonce); + } + tsQuery.requestCertificate(true); + + TSResponse tsReply = tsa.generateTimestamp(tsQuery); + int status = tsReply.getStatusCode(); + // Handle TSP error + if (status != 0 && status != 1) { + throw new IOException("Error generating timestamp: " + + tsReply.getStatusCodeAsText() + " " + + tsReply.getFailureCodeAsText()); + } + + if (tSAPolicyID != null && + !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) { + throw new IOException("TSAPolicyID changed in " + + "timestamp token"); + } + PKCS7 tsToken = tsReply.getToken(); + + TimestampToken tst = tsReply.getTimestampToken(); + if (!tst.getHashAlgorithm().getName().equals("SHA-1")) { + throw new IOException("Digest algorithm not SHA-1 in " + + "timestamp token"); + } + if (!MessageDigest.isEqual(tst.getHashedMessage(), + tsQuery.getHashedMessage())) { + throw new IOException("Digest octets changed in timestamp token"); + } + + BigInteger replyNonce = tst.getNonce(); + if (replyNonce == null && nonce != null) { + throw new IOException("Nonce missing in timestamp token"); + } + if (replyNonce != null && !replyNonce.equals(nonce)) { + throw new IOException("Nonce changed in timestamp token"); + } + + // Examine the TSA's certificate (if present) + for (SignerInfo si: tsToken.getSignerInfos()) { + X509Certificate cert = si.getCertificate(tsToken); + if (cert == null) { + // Error, we've already set tsRequestCertificate = true + throw new CertificateException( + "Certificate not included in timestamp token"); + } else { + if (!cert.getCriticalExtensionOIDs().contains( + EXTENDED_KEY_USAGE_OID)) { + throw new CertificateException( + "Certificate is not valid for timestamping"); + } + List keyPurposes = cert.getExtendedKeyUsage(); + if (keyPurposes == null || + !keyPurposes.contains(KP_TIMESTAMPING_OID)) { + throw new CertificateException( + "Certificate is not valid for timestamping"); + } + } + } + return tsReply.getEncodedToken(); + } +} diff --git a/src/sun/security/pkcs/PKCS8Key.java b/src/sun/security/pkcs/PKCS8Key.java new file mode 100644 index 00000000..01ee1038 --- /dev/null +++ b/src/sun/security/pkcs/PKCS8Key.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.*; +import java.util.Properties; +import java.math.*; +import java.security.Key; +import java.security.KeyRep; +import java.security.PrivateKey; +import java.security.KeyFactory; +import java.security.Security; +import java.security.Provider; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; + +import sun.security.x509.*; +import sun.security.util.*; + +/** + * Holds a PKCS#8 key, for example a private key + * + * @author Dave Brownell + * @author Benjamin Renaud + */ +public class PKCS8Key implements PrivateKey { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = -3836890099307167124L; + + /* The algorithm information (name, parameters, etc). */ + protected AlgorithmId algid; + + /* The key bytes, without the algorithm information */ + protected byte[] key; + + /* The encoded for the key. */ + protected byte[] encodedKey; + + /* The version for this key */ + public static final BigInteger version = BigInteger.ZERO; + + /** + * Default constructor. The key constructed must have its key + * and algorithm initialized before it may be used, for example + * by using decode. + */ + public PKCS8Key() { } + + /* + * Build and initialize as a "default" key. All PKCS#8 key + * data is stored and transmitted losslessly, but no knowledge + * about this particular algorithm is available. + */ + private PKCS8Key (AlgorithmId algid, byte key []) + throws InvalidKeyException { + this.algid = algid; + this.key = key; + encode(); + } + + /* + * Binary backwards compatibility. New uses should call parseKey(). + */ + public static PKCS8Key parse (DerValue in) throws IOException { + PrivateKey key; + + key = parseKey(in); + if (key instanceof PKCS8Key) + return (PKCS8Key)key; + + throw new IOException("Provider did not return PKCS8Key"); + } + + /** + * Construct PKCS#8 subject public key from a DER value. If + * the runtime environment is configured with a specific class for + * this kind of key, a subclass is returned. Otherwise, a generic + * PKCS8Key object is returned. + * + *

    This mechanism gurantees that keys (and algorithms) may be + * freely manipulated and transferred, without risk of losing + * information. Also, when a key (or algorithm) needs some special + * handling, that specific need can be accomodated. + * + * @param in the DER-encoded SubjectPublicKeyInfo value + * @exception IOException on data format errors + */ + public static PrivateKey parseKey (DerValue in) throws IOException + { + AlgorithmId algorithm; + PrivateKey privKey; + + if (in.tag != DerValue.tag_Sequence) + throw new IOException ("corrupt private key"); + + BigInteger parsedVersion = in.data.getBigInteger(); + if (!version.equals(parsedVersion)) { + throw new IOException("version mismatch: (supported: " + + Debug.toHexString(version) + + ", parsed: " + + Debug.toHexString(parsedVersion)); + } + + algorithm = AlgorithmId.parse (in.data.getDerValue ()); + + try { + privKey = buildPKCS8Key (algorithm, in.data.getOctetString ()); + + } catch (InvalidKeyException e) { + throw new IOException("corrupt private key"); + } + + if (in.data.available () != 0) + throw new IOException ("excess private key"); + return privKey; + } + + /** + * Parse the key bits. This may be redefined by subclasses to take + * advantage of structure within the key. For example, RSA public + * keys encapsulate two unsigned integers (modulus and exponent) as + * DER values within the key bits; Diffie-Hellman and + * DSS/DSA keys encapsulate a single unsigned integer. + * + *

    This function is called when creating PKCS#8 SubjectPublicKeyInfo + * values using the PKCS8Key member functions, such as parse + * and decode. + * + * @exception IOException if a parsing error occurs. + * @exception InvalidKeyException if the key encoding is invalid. + */ + protected void parseKeyBits () throws IOException, InvalidKeyException { + encode(); + } + + /* + * Factory interface, building the kind of key associated with this + * specific algorithm ID or else returning this generic base class. + * See the description above. + */ + static PrivateKey buildPKCS8Key (AlgorithmId algid, byte[] key) + throws IOException, InvalidKeyException + { + /* + * Use the algid and key parameters to produce the ASN.1 encoding + * of the key, which will then be used as the input to the + * key factory. + */ + DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream(); + encode(pkcs8EncodedKeyStream, algid, key); + PKCS8EncodedKeySpec pkcs8KeySpec + = new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray()); + + try { + // Instantiate the key factory of the appropriate algorithm + KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); + + // Generate the private key + return keyFac.generatePrivate(pkcs8KeySpec); + } catch (NoSuchAlgorithmException e) { + // Return generic PKCS8Key with opaque key data (see below) + } catch (InvalidKeySpecException e) { + // Return generic PKCS8Key with opaque key data (see below) + } + + /* + * Try again using JDK1.1-style for backwards compatibility. + */ + String classname = ""; + try { + Properties props; + String keytype; + Provider sunProvider; + + sunProvider = Security.getProvider("SUN"); + if (sunProvider == null) + throw new InstantiationException(); + classname = sunProvider.getProperty("PrivateKey.PKCS#8." + + algid.getName()); + if (classname == null) { + throw new InstantiationException(); + } + + Class keyClass = null; + try { + keyClass = Class.forName(classname); + } catch (ClassNotFoundException e) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + if (cl != null) { + keyClass = cl.loadClass(classname); + } + } + + Object inst = null; + PKCS8Key result; + + if (keyClass != null) + inst = keyClass.newInstance(); + if (inst instanceof PKCS8Key) { + result = (PKCS8Key) inst; + result.algid = algid; + result.key = key; + result.parseKeyBits(); + return result; + } + } catch (ClassNotFoundException e) { + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + // this should not happen. + throw new IOException (classname + " [internal error]"); + } + + PKCS8Key result = new PKCS8Key(); + result.algid = algid; + result.key = key; + return result; + } + + /** + * Returns the algorithm to be used with this key. + */ + public String getAlgorithm() { + return algid.getName(); + } + + /** + * Returns the algorithm ID to be used with this key. + */ + public AlgorithmId getAlgorithmId () { return algid; } + + /** + * PKCS#8 sequence on the DER output stream. + */ + public final void encode(DerOutputStream out) throws IOException + { + encode(out, this.algid, this.key); + } + + /** + * Returns the DER-encoded form of the key as a byte array. + */ + public synchronized byte[] getEncoded() { + byte[] result = null; + try { + result = encode(); + } catch (InvalidKeyException e) { + } + return result; + } + + /** + * Returns the format for this key: "PKCS#8" + */ + public String getFormat() { + return "PKCS#8"; + } + + /** + * Returns the DER-encoded form of the key as a byte array. + * + * @exception InvalidKeyException if an encoding error occurs. + */ + public byte[] encode() throws InvalidKeyException { + if (encodedKey == null) { + try { + DerOutputStream out; + + out = new DerOutputStream (); + encode (out); + encodedKey = out.toByteArray(); + + } catch (IOException e) { + throw new InvalidKeyException ("IOException : " + + e.getMessage()); + } + } + return encodedKey.clone(); + } + + /** + * Initialize an PKCS8Key object from an input stream. The data + * on that input stream must be encoded using DER, obeying the + * PKCS#8 format: a sequence consisting of a version, an algorithm + * ID and a bit string which holds the key. (That bit string is + * often used to encapsulate another DER encoded sequence.) + * + *

    Subclasses should not normally redefine this method; they should + * instead provide a parseKeyBits method to parse any + * fields inside the key member. + * + * @param in an input stream with a DER-encoded PKCS#8 + * SubjectPublicKeyInfo value + * + * @exception InvalidKeyException if a parsing error occurs. + */ + public void decode(InputStream in) throws InvalidKeyException + { + DerValue val; + + try { + val = new DerValue (in); + if (val.tag != DerValue.tag_Sequence) + throw new InvalidKeyException ("invalid key format"); + + + BigInteger version = val.data.getBigInteger(); + if (!version.equals(PKCS8Key.version)) { + throw new IOException("version mismatch: (supported: " + + Debug.toHexString(PKCS8Key.version) + + ", parsed: " + + Debug.toHexString(version)); + } + algid = AlgorithmId.parse (val.data.getDerValue ()); + key = val.data.getOctetString (); + parseKeyBits (); + + if (val.data.available () != 0) { + // OPTIONAL attributes not supported yet + } + + } catch (IOException e) { + // e.printStackTrace (); + throw new InvalidKeyException("IOException : " + + e.getMessage()); + } + } + + public void decode(byte[] encodedKey) throws InvalidKeyException { + decode(new ByteArrayInputStream(encodedKey)); + } + + protected Object writeReplace() throws ObjectStreamException { + return new KeyRep(KeyRep.Type.PRIVATE, + getAlgorithm(), + getFormat(), + getEncoded()); + } + + /** + * Serialization read ... PKCS#8 keys serialize as + * themselves, and they're parsed when they get read back. + */ + private void readObject (ObjectInputStream stream) + throws IOException { + + try { + decode(stream); + + } catch (InvalidKeyException e) { + e.printStackTrace(); + throw new IOException("deserialized key is invalid: " + + e.getMessage()); + } + } + + /* + * Produce PKCS#8 encoding from algorithm id and key material. + */ + static void encode(DerOutputStream out, AlgorithmId algid, byte[] key) + throws IOException { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(version); + algid.encode(tmp); + tmp.putOctetString(key); + out.write(DerValue.tag_Sequence, tmp); + } + + /** + * Compares two private keys. This returns false if the object with which + * to compare is not of type Key. + * Otherwise, the encoding of this key object is compared with the + * encoding of the given key object. + * + * @param object the object with which to compare + * @return true if this key has the same encoding as the + * object argument; false otherwise. + */ + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object instanceof Key) { + + // this encoding + byte[] b1; + if (encodedKey != null) { + b1 = encodedKey; + } else { + b1 = getEncoded(); + } + + // that encoding + byte[] b2 = ((Key)object).getEncoded(); + + // do the comparison + int i; + if (b1.length != b2.length) + return false; + for (i = 0; i < b1.length; i++) { + if (b1[i] != b2[i]) { + return false; + } + } + return true; + } + + return false; + } + + /** + * Calculates a hash code value for this object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + int retval = 0; + byte[] b1 = getEncoded(); + + for (int i = 1; i < b1.length; i++) { + retval += b1[i] * i; + } + return(retval); + } +} diff --git a/src/sun/security/pkcs/PKCS9Attribute.java b/src/sun/security/pkcs/PKCS9Attribute.java new file mode 100644 index 00000000..d847b104 --- /dev/null +++ b/src/sun/security/pkcs/PKCS9Attribute.java @@ -0,0 +1,878 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Locale; +import java.util.Date; +import java.util.Hashtable; +import sun.security.x509.CertificateExtensions; +import sun.security.util.Debug; +import sun.security.util.DerEncoder; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; +import sun.misc.HexDumpEncoder; + +/** + * Class supporting any PKCS9 attributes. + * Supports DER decoding/encoding and access to attribute values. + * + *

    Type/Class Table

    + * The following table shows the correspondence between + * PKCS9 attribute types and value component classes. + * For types not listed here, its name is the OID + * in string form, its value is a (single-valued) + * byte array that is the SET's encoding. + * + *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Object IdentifierAttribute NameTypeValue Class
    1.2.840.113549.1.9.1EmailAddressMulti-valuedString[]
    1.2.840.113549.1.9.2UnstructuredNameMulti-valuedString[]
    1.2.840.113549.1.9.3ContentTypeSingle-valuedObjectIdentifier
    1.2.840.113549.1.9.4MessageDigestSingle-valuedbyte[]
    1.2.840.113549.1.9.5SigningTimeSingle-valuedDate
    1.2.840.113549.1.9.6CountersignatureMulti-valuedSignerInfo[]
    1.2.840.113549.1.9.7ChallengePasswordSingle-valuedString
    1.2.840.113549.1.9.8UnstructuredAddressSingle-valuedString
    1.2.840.113549.1.9.9ExtendedCertificateAttributesMulti-valued(not supported)
    1.2.840.113549.1.9.10IssuerAndSerialNumberSingle-valued(not supported)
    1.2.840.113549.1.9.{11,12}RSA DSI proprietarySingle-valued(not supported)
    1.2.840.113549.1.9.13S/MIME unused assignmentSingle-valued(not supported)
    1.2.840.113549.1.9.14ExtensionRequestSingle-valuedCertificateExtensions
    1.2.840.113549.1.9.15SMIMECapabilitySingle-valued(not supported)
    1.2.840.113549.1.9.16.2.12SigningCertificateSingle-valuedSigningCertificateInfo
    1.2.840.113549.1.9.16.2.14SignatureTimestampTokenSingle-valuedbyte[]
    + * + * @author Douglas Hoover + */ +public class PKCS9Attribute implements DerEncoder { + + /* Are we debugging ? */ + private static final Debug debug = Debug.getInstance("jar"); + + /** + * Array of attribute OIDs defined in PKCS9, by number. + */ + static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[18]; + + private final static Class BYTE_ARRAY_CLASS; + + static { // static initializer for PKCS9_OIDS + for (int i = 1; i < PKCS9_OIDS.length - 2; i++) { + PKCS9_OIDS[i] = + ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,i}); + } + // Initialize SigningCertificate and SignatureTimestampToken + // separately (because their values are out of sequence) + PKCS9_OIDS[PKCS9_OIDS.length - 2] = + ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,12}); + PKCS9_OIDS[PKCS9_OIDS.length - 1] = + ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,14}); + + try { + BYTE_ARRAY_CLASS = Class.forName("[B"); + } catch (ClassNotFoundException e) { + throw new ExceptionInInitializerError(e.toString()); + } + } + + // first element [0] not used + public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1]; + public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2]; + public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3]; + public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4]; + public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5]; + public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6]; + public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = PKCS9_OIDS[7]; + public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = PKCS9_OIDS[8]; + public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID + = PKCS9_OIDS[9]; + public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID = PKCS9_OIDS[10]; + // [11], [12] are RSA DSI proprietary + // [13] ==> signingDescription, S/MIME, not used anymore + public static final ObjectIdentifier EXTENSION_REQUEST_OID = PKCS9_OIDS[14]; + public static final ObjectIdentifier SMIME_CAPABILITY_OID = PKCS9_OIDS[15]; + public static final ObjectIdentifier SIGNING_CERTIFICATE_OID = PKCS9_OIDS[16]; + public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID = + PKCS9_OIDS[17]; + public static final String EMAIL_ADDRESS_STR = "EmailAddress"; + public static final String UNSTRUCTURED_NAME_STR = "UnstructuredName"; + public static final String CONTENT_TYPE_STR = "ContentType"; + public static final String MESSAGE_DIGEST_STR = "MessageDigest"; + public static final String SIGNING_TIME_STR = "SigningTime"; + public static final String COUNTERSIGNATURE_STR = "Countersignature"; + public static final String CHALLENGE_PASSWORD_STR = "ChallengePassword"; + public static final String UNSTRUCTURED_ADDRESS_STR = "UnstructuredAddress"; + public static final String EXTENDED_CERTIFICATE_ATTRIBUTES_STR = + "ExtendedCertificateAttributes"; + public static final String ISSUER_SERIALNUMBER_STR = "IssuerAndSerialNumber"; + // [11], [12] are RSA DSI proprietary + private static final String RSA_PROPRIETARY_STR = "RSAProprietary"; + // [13] ==> signingDescription, S/MIME, not used anymore + private static final String SMIME_SIGNING_DESC_STR = "SMIMESigningDesc"; + public static final String EXTENSION_REQUEST_STR = "ExtensionRequest"; + public static final String SMIME_CAPABILITY_STR = "SMIMECapability"; + public static final String SIGNING_CERTIFICATE_STR = "SigningCertificate"; + public static final String SIGNATURE_TIMESTAMP_TOKEN_STR = + "SignatureTimestampToken"; + + /** + * Hashtable mapping names and variant names of supported + * attributes to their OIDs. This table contains all name forms + * that occur in PKCS9, in lower case. + */ + private static final Hashtable NAME_OID_TABLE = + new Hashtable(18); + + static { // static initializer for PCKS9_NAMES + NAME_OID_TABLE.put("emailaddress", PKCS9_OIDS[1]); + NAME_OID_TABLE.put("unstructuredname", PKCS9_OIDS[2]); + NAME_OID_TABLE.put("contenttype", PKCS9_OIDS[3]); + NAME_OID_TABLE.put("messagedigest", PKCS9_OIDS[4]); + NAME_OID_TABLE.put("signingtime", PKCS9_OIDS[5]); + NAME_OID_TABLE.put("countersignature", PKCS9_OIDS[6]); + NAME_OID_TABLE.put("challengepassword", PKCS9_OIDS[7]); + NAME_OID_TABLE.put("unstructuredaddress", PKCS9_OIDS[8]); + NAME_OID_TABLE.put("extendedcertificateattributes", PKCS9_OIDS[9]); + NAME_OID_TABLE.put("issuerandserialnumber", PKCS9_OIDS[10]); + NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[11]); + NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[12]); + NAME_OID_TABLE.put("signingdescription", PKCS9_OIDS[13]); + NAME_OID_TABLE.put("extensionrequest", PKCS9_OIDS[14]); + NAME_OID_TABLE.put("smimecapability", PKCS9_OIDS[15]); + NAME_OID_TABLE.put("signingcertificate", PKCS9_OIDS[16]); + NAME_OID_TABLE.put("signaturetimestamptoken", PKCS9_OIDS[17]); + }; + + /** + * Hashtable mapping attribute OIDs defined in PKCS9 to the + * corresponding attribute value type. + */ + private static final Hashtable OID_NAME_TABLE = + new Hashtable(16); + static { + OID_NAME_TABLE.put(PKCS9_OIDS[1], EMAIL_ADDRESS_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[2], UNSTRUCTURED_NAME_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[3], CONTENT_TYPE_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[4], MESSAGE_DIGEST_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[5], SIGNING_TIME_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[6], COUNTERSIGNATURE_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[7], CHALLENGE_PASSWORD_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[8], UNSTRUCTURED_ADDRESS_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[9], EXTENDED_CERTIFICATE_ATTRIBUTES_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[10], ISSUER_SERIALNUMBER_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[11], RSA_PROPRIETARY_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[12], RSA_PROPRIETARY_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[13], SMIME_SIGNING_DESC_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[14], EXTENSION_REQUEST_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[15], SMIME_CAPABILITY_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[16], SIGNING_CERTIFICATE_STR); + OID_NAME_TABLE.put(PKCS9_OIDS[17], SIGNATURE_TIMESTAMP_TOKEN_STR); + } + + /** + * Acceptable ASN.1 tags for DER encodings of values of PKCS9 + * attributes, by index in PKCS9_OIDS. + * Sets of acceptable tags are represented as arrays. + */ + private static final Byte[][] PKCS9_VALUE_TAGS = { + null, + {new Byte(DerValue.tag_IA5String)}, // EMailAddress + {new Byte(DerValue.tag_IA5String), // UnstructuredName + new Byte(DerValue.tag_PrintableString)}, + {new Byte(DerValue.tag_ObjectId)}, // ContentType + {new Byte(DerValue.tag_OctetString)}, // MessageDigest + {new Byte(DerValue.tag_UtcTime)}, // SigningTime + {new Byte(DerValue.tag_Sequence)}, // Countersignature + {new Byte(DerValue.tag_PrintableString), + new Byte(DerValue.tag_T61String)}, // ChallengePassword + {new Byte(DerValue.tag_PrintableString), + new Byte(DerValue.tag_T61String)}, // UnstructuredAddress + {new Byte(DerValue.tag_SetOf)}, // ExtendedCertificateAttributes + {new Byte(DerValue.tag_Sequence)}, // issuerAndSerialNumber + null, + null, + null, + {new Byte(DerValue.tag_Sequence)}, // extensionRequest + {new Byte(DerValue.tag_Sequence)}, // SMIMECapability + {new Byte(DerValue.tag_Sequence)}, // SigningCertificate + {new Byte(DerValue.tag_Sequence)} // SignatureTimestampToken + }; + + private static final Class[] VALUE_CLASSES = new Class[18]; + + static { + try { + Class str = Class.forName("[Ljava.lang.String;"); + + VALUE_CLASSES[0] = null; // not used + VALUE_CLASSES[1] = str; // EMailAddress + VALUE_CLASSES[2] = str; // UnstructuredName + VALUE_CLASSES[3] = // ContentType + Class.forName("sun.security.util.ObjectIdentifier"); + VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[]) + VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime + VALUE_CLASSES[6] = // Countersignature + Class.forName("[Lsun.security.pkcs.SignerInfo;"); + VALUE_CLASSES[7] = // ChallengePassword + Class.forName("java.lang.String"); + VALUE_CLASSES[8] = str; // UnstructuredAddress + VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes + VALUE_CLASSES[10] = null; // IssuerAndSerialNumber + VALUE_CLASSES[11] = null; // not used + VALUE_CLASSES[12] = null; // not used + VALUE_CLASSES[13] = null; // not used + VALUE_CLASSES[14] = // ExtensionRequest + Class.forName("sun.security.x509.CertificateExtensions"); + VALUE_CLASSES[15] = null; // not supported yet + VALUE_CLASSES[16] = null; // not supported yet + VALUE_CLASSES[17] = BYTE_ARRAY_CLASS; // SignatureTimestampToken + } catch (ClassNotFoundException e) { + throw new ExceptionInInitializerError(e.toString()); + } + } + + /** + * Array indicating which PKCS9 attributes are single-valued, + * by index in PKCS9_OIDS. + */ + private static final boolean[] SINGLE_VALUED = { + false, + false, // EMailAddress + false, // UnstructuredName + true, // ContentType + true, // MessageDigest + true, // SigningTime + false, // Countersignature + true, // ChallengePassword + false, // UnstructuredAddress + false, // ExtendedCertificateAttributes + true, // IssuerAndSerialNumber - not supported yet + false, // not used + false, // not used + false, // not used + true, // ExtensionRequest + true, // SMIMECapability - not supported yet + true, // SigningCertificate + true // SignatureTimestampToken + }; + + /** + * The OID of this attribute. + */ + private ObjectIdentifier oid; + + /** + * The index of the OID of this attribute in PKCS9_OIDS, + * or -1 if it's unknown. + */ + private int index; + + /** + * Value set of this attribute. Its class is given by + * VALUE_CLASSES[index]. The SET itself + * as byte[] if unknown. + */ + private Object value; + + /** + * Construct an attribute object from the attribute's OID and + * value. If the attribute is single-valued, provide only one + * value. If the attribute is multi-valued, provide an array + * containing all the values. + * Arrays of length zero are accepted, though probably useless. + * + *

    The + * table gives the class that value + * must have for a given attribute. + * + * @exception IllegalArgumentException + * if the value has the wrong type. + */ + public PKCS9Attribute(ObjectIdentifier oid, Object value) + throws IllegalArgumentException { + init(oid, value); + } + + /** + * Construct an attribute object from the attribute's name and + * value. If the attribute is single-valued, provide only one + * value. If the attribute is multi-valued, provide an array + * containing all the values. + * Arrays of length zero are accepted, though probably useless. + * + *

    The + * table gives the class that value + * must have for a given attribute. Reasonable variants of these + * attributes are accepted; in particular, case does not matter. + * + * @exception IllegalArgumentException + * if the name is not recognized or the + * value has the wrong type. + */ + public PKCS9Attribute(String name, Object value) + throws IllegalArgumentException { + ObjectIdentifier oid = getOID(name); + + if (oid == null) + throw new IllegalArgumentException( + "Unrecognized attribute name " + name + + " constructing PKCS9Attribute."); + + init(oid, value); + } + + private void init(ObjectIdentifier oid, Object value) + throws IllegalArgumentException { + + this.oid = oid; + index = indexOf(oid, PKCS9_OIDS, 1); + Class clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index]; + if (!clazz.isInstance(value)) { + throw new IllegalArgumentException( + "Wrong value class " + + " for attribute " + oid + + " constructing PKCS9Attribute; was " + + value.getClass().toString() + ", should be " + + clazz.toString()); + } + this.value = value; + } + + + /** + * Construct a PKCS9Attribute from its encoding on an input + * stream. + * + * @param val the DerValue representing the DER encoding of the attribute. + * @exception IOException on parsing error. + */ + public PKCS9Attribute(DerValue derVal) throws IOException { + + DerInputStream derIn = new DerInputStream(derVal.toByteArray()); + DerValue[] val = derIn.getSequence(2); + + if (derIn.available() != 0) + throw new IOException("Excess data parsing PKCS9Attribute"); + + if (val.length != 2) + throw new IOException("PKCS9Attribute doesn't have two components"); + + // get the oid + oid = val[0].getOID(); + byte[] content = val[1].toByteArray(); + DerValue[] elems = new DerInputStream(content).getSet(1); + + index = indexOf(oid, PKCS9_OIDS, 1); + if (index == -1) { + if (debug != null) { + debug.println("Unsupported signer attribute: " + oid); + } + value = content; + return; + } + + // check single valued have only one value + if (SINGLE_VALUED[index] && elems.length > 1) + throwSingleValuedException(); + + // check for illegal element tags + Byte tag; + for (int i=0; i < elems.length; i++) { + tag = new Byte(elems[i].tag); + + if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1) + throwTagException(tag); + } + + switch (index) { + case 1: // email address + case 2: // unstructured name + case 8: // unstructured address + { // open scope + String[] values = new String[elems.length]; + + for (int i=0; i < elems.length; i++) + values[i] = elems[i].getAsString(); + value = values; + } // close scope + break; + + case 3: // content type + value = elems[0].getOID(); + break; + + case 4: // message digest + value = elems[0].getOctetString(); + break; + + case 5: // signing time + value = (new DerInputStream(elems[0].toByteArray())).getUTCTime(); + break; + + case 6: // countersignature + { // open scope + SignerInfo[] values = new SignerInfo[elems.length]; + for (int i=0; i < elems.length; i++) + values[i] = + new SignerInfo(elems[i].toDerInputStream()); + value = values; + } // close scope + break; + + case 7: // challenge password + value = elems[0].getAsString(); + break; + + case 9: // extended-certificate attribute -- not supported + throw new IOException("PKCS9 extended-certificate " + + "attribute not supported."); + // break unnecessary + case 10: // issuerAndserialNumber attribute -- not supported + throw new IOException("PKCS9 IssuerAndSerialNumber" + + "attribute not supported."); + // break unnecessary + case 11: // RSA DSI proprietary + case 12: // RSA DSI proprietary + throw new IOException("PKCS9 RSA DSI attributes" + + "11 and 12, not supported."); + // break unnecessary + case 13: // S/MIME unused attribute + throw new IOException("PKCS9 attribute #13 not supported."); + // break unnecessary + + case 14: // ExtensionRequest + value = new CertificateExtensions( + new DerInputStream(elems[0].toByteArray())); + break; + + case 15: // SMIME-capability attribute -- not supported + throw new IOException("PKCS9 SMIMECapability " + + "attribute not supported."); + // break unnecessary + case 16: // SigningCertificate attribute + value = new SigningCertificateInfo(elems[0].toByteArray()); + break; + + case 17: // SignatureTimestampToken attribute + value = elems[0].toByteArray(); + break; + default: // can't happen + } + } + + /** + * Write the DER encoding of this attribute to an output stream. + * + *

    N.B.: This method always encodes values of + * ChallengePassword and UnstructuredAddress attributes as ASN.1 + * PrintableStrings, without checking whether they + * should be encoded as T61Strings. + */ + public void derEncode(OutputStream out) throws IOException { + DerOutputStream temp = new DerOutputStream(); + temp.putOID(oid); + switch (index) { + case -1: // Unknown + temp.write((byte[])value); + break; + case 1: // email address + case 2: // unstructured name + { // open scope + String[] values = (String[]) value; + DerOutputStream[] temps = new + DerOutputStream[values.length]; + + for (int i=0; i < values.length; i++) { + temps[i] = new DerOutputStream(); + temps[i].putIA5String( values[i]); + } + temp.putOrderedSetOf(DerValue.tag_Set, temps); + } // close scope + break; + + case 3: // content type + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putOID((ObjectIdentifier) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 4: // message digest + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putOctetString((byte[]) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 5: // signing time + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putUTCTime((Date) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 6: // countersignature + temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value); + break; + + case 7: // challenge password + { + DerOutputStream temp2 = new DerOutputStream(); + temp2.putPrintableString((String) value); + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + + case 8: // unstructured address + { // open scope + String[] values = (String[]) value; + DerOutputStream[] temps = new + DerOutputStream[values.length]; + + for (int i=0; i < values.length; i++) { + temps[i] = new DerOutputStream(); + temps[i].putPrintableString(values[i]); + } + temp.putOrderedSetOf(DerValue.tag_Set, temps); + } // close scope + break; + + case 9: // extended-certificate attribute -- not supported + throw new IOException("PKCS9 extended-certificate " + + "attribute not supported."); + // break unnecessary + case 10: // issuerAndserialNumber attribute -- not supported + throw new IOException("PKCS9 IssuerAndSerialNumber" + + "attribute not supported."); + // break unnecessary + case 11: // RSA DSI proprietary + case 12: // RSA DSI proprietary + throw new IOException("PKCS9 RSA DSI attributes" + + "11 and 12, not supported."); + // break unnecessary + case 13: // S/MIME unused attribute + throw new IOException("PKCS9 attribute #13 not supported."); + // break unnecessary + + case 14: // ExtensionRequest + { + DerOutputStream temp2 = new DerOutputStream(); + CertificateExtensions exts = (CertificateExtensions)value; + try { + exts.encode(temp2, true); + } catch (CertificateException ex) { + throw new IOException(ex.toString()); + } + temp.write(DerValue.tag_Set, temp2.toByteArray()); + } + break; + case 15: // SMIMECapability + throw new IOException("PKCS9 attribute #15 not supported."); + // break unnecessary + + case 16: // SigningCertificate + throw new IOException( + "PKCS9 SigningCertificate attribute not supported."); + // break unnecessary + + case 17: // SignatureTimestampToken + temp.write(DerValue.tag_Set, (byte[])value); + break; + + default: // can't happen + } + + DerOutputStream derOut = new DerOutputStream(); + derOut.write(DerValue.tag_Sequence, temp.toByteArray()); + + out.write(derOut.toByteArray()); + + } + + /** + * Returns if the attribute is known. Unknown attributes can be created + * from DER encoding with unknown OIDs. + */ + public boolean isKnown() { + return index != -1; + } + + /** + * Get the value of this attribute. If the attribute is + * single-valued, return just the one value. If the attribute is + * multi-valued, return an array containing all the values. + * It is possible for this array to be of length 0. + * + *

    The + * table gives the class of the value returned, + * depending on the type of this attribute. + */ + public Object getValue() { + return value; + } + + /** + * Show whether this attribute is single-valued. + */ + public boolean isSingleValued() { + return index == -1 || SINGLE_VALUED[index]; + } + + /** + * Return the OID of this attribute. + */ + public ObjectIdentifier getOID() { + return oid; + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return index == -1 ? + oid.toString() : + OID_NAME_TABLE.get(PKCS9_OIDS[index]); + } + + /** + * Return the OID for a given attribute name or null if we don't recognize + * the name. + */ + public static ObjectIdentifier getOID(String name) { + return NAME_OID_TABLE.get(name.toLowerCase(Locale.ENGLISH)); + } + + /** + * Return the attribute name for a given OID or null if we don't recognize + * the oid. + */ + public static String getName(ObjectIdentifier oid) { + return OID_NAME_TABLE.get(oid); + } + + /** + * Returns a string representation of this attribute. + */ + public String toString() { + StringBuffer buf = new StringBuffer(100); + + buf.append("["); + + if (index == -1) { + buf.append(oid.toString()); + } else { + buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index])); + } + buf.append(": "); + + if (index == -1 || SINGLE_VALUED[index]) { + if (value instanceof byte[]) { // special case for octet string + HexDumpEncoder hexDump = new HexDumpEncoder(); + buf.append(hexDump.encodeBuffer((byte[]) value)); + } else { + buf.append(value.toString()); + } + buf.append("]"); + return buf.toString(); + } else { // multi-valued + boolean first = true; + Object[] values = (Object[]) value; + + for (int j=0; j < values.length; j++) { + if (first) + first = false; + else + buf.append(", "); + + buf.append(values[j].toString()); + } + return buf.toString(); + } + } + + /** + * Beginning the search at start, find the first + * index i such that a[i] = obj. + * + * @return the index, if found, and -1 otherwise. + */ + static int indexOf(Object obj, Object[] a, int start) { + for (int i=start; i < a.length; i++) { + if (obj.equals(a[i])) return i; + } + return -1; + } + + /** + * Throw an exception when there are multiple values for + * a single-valued attribute. + */ + private void throwSingleValuedException() throws IOException { + throw new IOException("Single-value attribute " + + oid + " (" + getName() + ")" + + " has multiple values."); + } + + /** + * Throw an exception when the tag on a value encoding is + * wrong for the attribute whose value it is. This method + * will only be called for known tags. + */ + private void throwTagException(Byte tag) + throws IOException { + Byte[] expectedTags = PKCS9_VALUE_TAGS[index]; + StringBuffer msg = new StringBuffer(100); + msg.append("Value of attribute "); + msg.append(oid.toString()); + msg.append(" ("); + msg.append(getName()); + msg.append(") has wrong tag: "); + msg.append(tag.toString()); + msg.append(". Expected tags: "); + + msg.append(expectedTags[0].toString()); + + for (int i = 1; i < expectedTags.length; i++) { + msg.append(", "); + msg.append(expectedTags[i].toString()); + } + msg.append("."); + throw new IOException(msg.toString()); + } +} diff --git a/src/sun/security/pkcs/PKCS9Attributes.java b/src/sun/security/pkcs/PKCS9Attributes.java new file mode 100644 index 00000000..7f72a4eb --- /dev/null +++ b/src/sun/security/pkcs/PKCS9Attributes.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Hashtable; +import sun.security.util.DerEncoder; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; + +/** + * A set of attributes of class PKCS9Attribute. + * + * @author Douglas Hoover + */ +public class PKCS9Attributes { + /** + * Attributes in this set indexed by OID. + */ + private final Hashtable attributes = + new Hashtable(3); + + /** + * The keys of this hashtable are the OIDs of permitted attributes. + */ + private final Hashtable permittedAttributes; + + /** + * The DER encoding of this attribute set. The tag byte must be + * DerValue.tag_SetOf. + */ + private final byte[] derEncoding; + + /* + * Contols how attributes, which are not recognized by the PKCS9Attribute + * class, are handled during parsing. + */ + private boolean ignoreUnsupportedAttributes = false; + + /** + * Construct a set of PKCS9 Attributes from its + * DER encoding on a DerInputStream, accepting only attributes + * with OIDs on the given + * list. If the array is null, accept all attributes supported by + * class PKCS9Attribute. + * + * @param permittedAttributes + * Array of attribute OIDs that will be accepted. + * @param in + * the contents of the DER encoding of the attribute set. + * + * @exception IOException + * on i/o error, encoding syntax error, unacceptable or + * unsupported attribute, or duplicate attribute. + * + * @see PKCS9Attribute + */ + public PKCS9Attributes(ObjectIdentifier[] permittedAttributes, + DerInputStream in) throws IOException { + if (permittedAttributes != null) { + this.permittedAttributes = + new Hashtable( + permittedAttributes.length); + + for (int i = 0; i < permittedAttributes.length; i++) + this.permittedAttributes.put(permittedAttributes[i], + permittedAttributes[i]); + } else { + this.permittedAttributes = null; + } + + // derEncoding initialized in decode() + derEncoding = decode(in); + } + + /** + * Construct a set of PKCS9 Attributes from the contents of its + * DER encoding on a DerInputStream. Accept all attributes + * supported by class PKCS9Attribute and reject any unsupported + * attributes. + * + * @param in the contents of the DER encoding of the attribute set. + * @exception IOException + * on i/o error, encoding syntax error, or unsupported or + * duplicate attribute. + * + * @see PKCS9Attribute + */ + public PKCS9Attributes(DerInputStream in) throws IOException { + this(in, false); + } + + /** + * Construct a set of PKCS9 Attributes from the contents of its + * DER encoding on a DerInputStream. Accept all attributes + * supported by class PKCS9Attribute and ignore any unsupported + * attributes, if directed. + * + * @param in the contents of the DER encoding of the attribute set. + * @param ignoreUnsupportedAttributes If true then any attributes + * not supported by the PKCS9Attribute class are ignored. Otherwise + * unsupported attributes cause an exception to be thrown. + * @exception IOException + * on i/o error, encoding syntax error, or unsupported or + * duplicate attribute. + * + * @see PKCS9Attribute + */ + public PKCS9Attributes(DerInputStream in, + boolean ignoreUnsupportedAttributes) throws IOException { + + this.ignoreUnsupportedAttributes = ignoreUnsupportedAttributes; + // derEncoding initialized in decode() + derEncoding = decode(in); + permittedAttributes = null; + } + + /** + * Construct a set of PKCS9 Attributes from the given array of + * PKCS9 attributes. + * DER encoding on a DerInputStream. All attributes in + * attribs must be + * supported by class PKCS9Attribute. + * + * @exception IOException + * on i/o error, encoding syntax error, or unsupported or + * duplicate attribute. + * + * @see PKCS9Attribute + */ + public PKCS9Attributes(PKCS9Attribute[] attribs) + throws IllegalArgumentException, IOException { + ObjectIdentifier oid; + for (int i=0; i < attribs.length; i++) { + oid = attribs[i].getOID(); + if (attributes.containsKey(oid)) + throw new IllegalArgumentException( + "PKCSAttribute " + attribs[i].getOID() + + " duplicated while constructing " + + "PKCS9Attributes."); + + attributes.put(oid, attribs[i]); + } + derEncoding = generateDerEncoding(); + permittedAttributes = null; + } + + + /** + * Decode this set of PKCS9 attributes from the contents of its + * DER encoding. Ignores unsupported attributes when directed. + * + * @param in + * the contents of the DER encoding of the attribute set. + * + * @exception IOException + * on i/o error, encoding syntax error, unacceptable or + * unsupported attribute, or duplicate attribute. + */ + private byte[] decode(DerInputStream in) throws IOException { + + DerValue val = in.getDerValue(); + + // save the DER encoding with its proper tag byte. + byte[] derEncoding = val.toByteArray(); + derEncoding[0] = DerValue.tag_SetOf; + + DerInputStream derIn = new DerInputStream(derEncoding); + DerValue[] derVals = derIn.getSet(3,true); + + PKCS9Attribute attrib; + ObjectIdentifier oid; + boolean reuseEncoding = true; + + for (int i=0; i < derVals.length; i++) { + + try { + attrib = new PKCS9Attribute(derVals[i]); + + } catch (ParsingException e) { + if (ignoreUnsupportedAttributes) { + reuseEncoding = false; // cannot reuse supplied DER encoding + continue; // skip + } else { + throw e; + } + } + oid = attrib.getOID(); + + if (attributes.get(oid) != null) + throw new IOException("Duplicate PKCS9 attribute: " + oid); + + if (permittedAttributes != null && + !permittedAttributes.containsKey(oid)) + throw new IOException("Attribute " + oid + + " not permitted in this attribute set"); + + attributes.put(oid, attrib); + } + return reuseEncoding ? derEncoding : generateDerEncoding(); + } + + /** + * Put the DER encoding of this PKCS9 attribute set on an + * DerOutputStream, tagged with the given implicit tag. + * + * @param tag the implicit tag to use in the DER encoding. + * @param out the output stream on which to put the DER encoding. + * + * @exception IOException on output error. + */ + public void encode(byte tag, OutputStream out) throws IOException { + out.write(tag); + out.write(derEncoding, 1, derEncoding.length -1); + } + + private byte[] generateDerEncoding() throws IOException { + DerOutputStream out = new DerOutputStream(); + Object[] attribVals = attributes.values().toArray(); + + out.putOrderedSetOf(DerValue.tag_SetOf, + castToDerEncoder(attribVals)); + return out.toByteArray(); + } + + /** + * Return the DER encoding of this attribute set, tagged with + * DerValue.tag_SetOf. + */ + public byte[] getDerEncoding() throws IOException { + return derEncoding.clone(); + + } + + /** + * Get an attribute from this set. + */ + public PKCS9Attribute getAttribute(ObjectIdentifier oid) { + return attributes.get(oid); + } + + /** + * Get an attribute from this set. + */ + public PKCS9Attribute getAttribute(String name) { + return attributes.get(PKCS9Attribute.getOID(name)); + } + + + /** + * Get an array of all attributes in this set, in order of OID. + */ + public PKCS9Attribute[] getAttributes() { + PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()]; + ObjectIdentifier oid; + + int j = 0; + for (int i=1; i < PKCS9Attribute.PKCS9_OIDS.length && + j < attribs.length; i++) { + attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); + + if (attribs[j] != null) + j++; + } + return attribs; + } + + /** + * Get an attribute value by OID. + */ + public Object getAttributeValue(ObjectIdentifier oid) + throws IOException { + try { + Object value = getAttribute(oid).getValue(); + return value; + } catch (NullPointerException ex) { + throw new IOException("No value found for attribute " + oid); + } + + } + + /** + * Get an attribute value by type name. + */ + public Object getAttributeValue(String name) throws IOException { + ObjectIdentifier oid = PKCS9Attribute.getOID(name); + + if (oid == null) + throw new IOException("Attribute name " + name + + " not recognized or not supported."); + + return getAttributeValue(oid); + } + + + /** + * Returns the PKCS9 block in a printable string form. + */ + public String toString() { + StringBuffer buf = new StringBuffer(200); + buf.append("PKCS9 Attributes: [\n\t"); + + ObjectIdentifier oid; + PKCS9Attribute value; + + boolean first = true; + for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) { + value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); + + if (value == null) continue; + + // we have a value; print it + if (first) + first = false; + else + buf.append(";\n\t"); + + buf.append(value.toString()); + } + + buf.append("\n\t] (end PKCS9 Attributes)"); + + return buf.toString(); + } + + /** + * Cast an object array whose components are + * DerEncoders to DerEncoder[]. + */ + static DerEncoder[] castToDerEncoder(Object[] objs) { + + DerEncoder[] encoders = new DerEncoder[objs.length]; + + for (int i=0; i < encoders.length; i++) + encoders[i] = (DerEncoder) objs[i]; + + return encoders; + } +} diff --git a/src/sun/security/pkcs/ParsingException.java b/src/sun/security/pkcs/ParsingException.java new file mode 100644 index 00000000..cfca8e27 --- /dev/null +++ b/src/sun/security/pkcs/ParsingException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Generic PKCS Parsing exception. + * + * @author Benjamin Renaud + */ + +package sun.security.pkcs; + +import java.io.IOException; + +public class ParsingException extends IOException { + + private static final long serialVersionUID = -6316569918966181883L; + + public ParsingException() { + super(); + } + + public ParsingException(String s) { + super(s); + } +} diff --git a/src/sun/security/pkcs/SignerInfo.java b/src/sun/security/pkcs/SignerInfo.java new file mode 100644 index 00000000..a1c07cb9 --- /dev/null +++ b/src/sun/security/pkcs/SignerInfo.java @@ -0,0 +1,569 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.OutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertPath; +import java.security.cert.X509Certificate; +import java.security.*; +import java.util.ArrayList; +import java.util.Arrays; + +import sun.security.timestamp.TimestampToken; +import sun.security.util.*; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X500Name; +import sun.security.x509.KeyUsageExtension; +import sun.security.util.SignatureUtil; +import sun.misc.HexDumpEncoder; + +/** + * A SignerInfo, as defined in PKCS#7's signedData type. + * + * @author Benjamin Renaud + */ +public class SignerInfo implements DerEncoder { + + BigInteger version; + X500Name issuerName; + BigInteger certificateSerialNumber; + AlgorithmId digestAlgorithmId; + AlgorithmId digestEncryptionAlgorithmId; + byte[] encryptedDigest; + Timestamp timestamp; + private boolean hasTimestamp = true; + private static final Debug debug = Debug.getInstance("jar"); + + PKCS9Attributes authenticatedAttributes; + PKCS9Attributes unauthenticatedAttributes; + + public SignerInfo(X500Name issuerName, + BigInteger serial, + AlgorithmId digestAlgorithmId, + AlgorithmId digestEncryptionAlgorithmId, + byte[] encryptedDigest) { + this.version = BigInteger.ONE; + this.issuerName = issuerName; + this.certificateSerialNumber = serial; + this.digestAlgorithmId = digestAlgorithmId; + this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; + this.encryptedDigest = encryptedDigest; + } + + public SignerInfo(X500Name issuerName, + BigInteger serial, + AlgorithmId digestAlgorithmId, + PKCS9Attributes authenticatedAttributes, + AlgorithmId digestEncryptionAlgorithmId, + byte[] encryptedDigest, + PKCS9Attributes unauthenticatedAttributes) { + this.version = BigInteger.ONE; + this.issuerName = issuerName; + this.certificateSerialNumber = serial; + this.digestAlgorithmId = digestAlgorithmId; + this.authenticatedAttributes = authenticatedAttributes; + this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId; + this.encryptedDigest = encryptedDigest; + this.unauthenticatedAttributes = unauthenticatedAttributes; + } + + /** + * Parses a PKCS#7 signer info. + */ + public SignerInfo(DerInputStream derin) + throws IOException, ParsingException + { + this(derin, false); + } + + /** + * Parses a PKCS#7 signer info. + * + *

    This constructor is used only for backwards compatibility with + * PKCS#7 blocks that were generated using JDK1.1.x. + * + * @param derin the ASN.1 encoding of the signer info. + * @param oldStyle flag indicating whether or not the given signer info + * is encoded according to JDK1.1.x. + */ + public SignerInfo(DerInputStream derin, boolean oldStyle) + throws IOException, ParsingException + { + // version + version = derin.getBigInteger(); + + // issuerAndSerialNumber + DerValue[] issuerAndSerialNumber = derin.getSequence(2); + byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray(); + issuerName = new X500Name(new DerValue(DerValue.tag_Sequence, + issuerBytes)); + certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger(); + + // digestAlgorithmId + DerValue tmp = derin.getDerValue(); + + digestAlgorithmId = AlgorithmId.parse(tmp); + + // authenticatedAttributes + if (oldStyle) { + // In JDK1.1.x, the authenticatedAttributes are always present, + // encoded as an empty Set (Set of length zero) + derin.getSet(0); + } else { + // check if set of auth attributes (implicit tag) is provided + // (auth attributes are OPTIONAL) + if ((byte)(derin.peekByte()) == (byte)0xA0) { + authenticatedAttributes = new PKCS9Attributes(derin); + } + } + + // digestEncryptionAlgorithmId - little RSA naming scheme - + // signature == encryption... + tmp = derin.getDerValue(); + + digestEncryptionAlgorithmId = AlgorithmId.parse(tmp); + + // encryptedDigest + encryptedDigest = derin.getOctetString(); + + // unauthenticatedAttributes + if (oldStyle) { + // In JDK1.1.x, the unauthenticatedAttributes are always present, + // encoded as an empty Set (Set of length zero) + derin.getSet(0); + } else { + // check if set of unauth attributes (implicit tag) is provided + // (unauth attributes are OPTIONAL) + if (derin.available() != 0 + && (byte)(derin.peekByte()) == (byte)0xA1) { + unauthenticatedAttributes = + new PKCS9Attributes(derin, true);// ignore unsupported attrs + } + } + + // all done + if (derin.available() != 0) { + throw new ParsingException("extra data at the end"); + } + } + + public void encode(DerOutputStream out) throws IOException { + + derEncode(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the DerEncoder interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode(OutputStream out) throws IOException { + DerOutputStream seq = new DerOutputStream(); + seq.putInteger(version); + DerOutputStream issuerAndSerialNumber = new DerOutputStream(); + issuerName.encode(issuerAndSerialNumber); + issuerAndSerialNumber.putInteger(certificateSerialNumber); + seq.write(DerValue.tag_Sequence, issuerAndSerialNumber); + + digestAlgorithmId.encode(seq); + + // encode authenticated attributes if there are any + if (authenticatedAttributes != null) + authenticatedAttributes.encode((byte)0xA0, seq); + + digestEncryptionAlgorithmId.encode(seq); + + seq.putOctetString(encryptedDigest); + + // encode unauthenticated attributes if there are any + if (unauthenticatedAttributes != null) + unauthenticatedAttributes.encode((byte)0xA1, seq); + + DerOutputStream tmp = new DerOutputStream(); + tmp.write(DerValue.tag_Sequence, seq); + + out.write(tmp.toByteArray()); + } + + + + /* + * Returns the (user) certificate pertaining to this SignerInfo. + */ + public X509Certificate getCertificate(PKCS7 block) + throws IOException + { + return block.getCertificate(certificateSerialNumber, issuerName); + } + + /* + * Returns the certificate chain pertaining to this SignerInfo. + */ + public ArrayList getCertificateChain(PKCS7 block) + throws IOException + { + X509Certificate userCert; + userCert = block.getCertificate(certificateSerialNumber, issuerName); + if (userCert == null) + return null; + + ArrayList certList = new ArrayList(); + certList.add(userCert); + + X509Certificate[] pkcsCerts = block.getCertificates(); + if (pkcsCerts == null + || userCert.getSubjectDN().equals(userCert.getIssuerDN())) { + return certList; + } + + Principal issuer = userCert.getIssuerDN(); + int start = 0; + while (true) { + boolean match = false; + int i = start; + while (i < pkcsCerts.length) { + if (issuer.equals(pkcsCerts[i].getSubjectDN())) { + // next cert in chain found + certList.add(pkcsCerts[i]); + // if selected cert is self-signed, we're done + // constructing the chain + if (pkcsCerts[i].getSubjectDN().equals( + pkcsCerts[i].getIssuerDN())) { + start = pkcsCerts.length; + } else { + issuer = pkcsCerts[i].getIssuerDN(); + X509Certificate tmpCert = pkcsCerts[start]; + pkcsCerts[start] = pkcsCerts[i]; + pkcsCerts[i] = tmpCert; + start++; + } + match = true; + break; + } else { + i++; + } + } + if (!match) + break; + } + + return certList; + } + + /* Returns null if verify fails, this signerInfo if + verify succeeds. */ + SignerInfo verify(PKCS7 block, byte[] data) + throws NoSuchAlgorithmException, SignatureException { + + try { + + ContentInfo content = block.getContentInfo(); + if (data == null) { + data = content.getContentBytes(); + } + + String digestAlgname = getDigestAlgorithmId().getName(); + + byte[] dataSigned; + + // if there are authenticate attributes, get the message + // digest and compare it with the digest of data + if (authenticatedAttributes == null) { + dataSigned = data; + } else { + + // first, check content type + ObjectIdentifier contentType = (ObjectIdentifier) + authenticatedAttributes.getAttributeValue( + PKCS9Attribute.CONTENT_TYPE_OID); + if (contentType == null || + !contentType.equals((Object)content.contentType)) + return null; // contentType does not match, bad SignerInfo + + // now, check message digest + byte[] messageDigest = (byte[]) + authenticatedAttributes.getAttributeValue( + PKCS9Attribute.MESSAGE_DIGEST_OID); + + if (messageDigest == null) // fail if there is no message digest + return null; + + MessageDigest md = MessageDigest.getInstance(digestAlgname); + byte[] computedMessageDigest = md.digest(data); + + if (messageDigest.length != computedMessageDigest.length) + return null; + for (int i = 0; i < messageDigest.length; i++) { + if (messageDigest[i] != computedMessageDigest[i]) + return null; + } + + // message digest attribute matched + // digest of original data + + // the data actually signed is the DER encoding of + // the authenticated attributes (tagged with + // the "SET OF" tag, not 0xA0). + dataSigned = authenticatedAttributes.getDerEncoding(); + } + + // put together digest algorithm and encryption algorithm + // to form signing algorithm + String encryptionAlgname = + getDigestEncryptionAlgorithmId().getName(); + + // Workaround: sometimes the encryptionAlgname is actually + // a signature name + String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); + if (tmp != null) encryptionAlgname = tmp; + String algname = AlgorithmId.makeSigAlg( + digestAlgname, encryptionAlgname); + + Signature sig = Signature.getInstance(algname); + X509Certificate cert = getCertificate(block); + + if (cert == null) { + return null; + } + if (cert.hasUnsupportedCriticalExtension()) { + throw new SignatureException("Certificate has unsupported " + + "critical extension(s)"); + } + + // Make sure that if the usage of the key in the certificate is + // restricted, it can be used for digital signatures. + // XXX We may want to check for additional extensions in the + // future. + boolean[] keyUsageBits = cert.getKeyUsage(); + if (keyUsageBits != null) { + KeyUsageExtension keyUsage; + try { + // We don't care whether or not this extension was marked + // critical in the certificate. + // We're interested only in its value (i.e., the bits set) + // and treat the extension as critical. + keyUsage = new KeyUsageExtension(keyUsageBits); + } catch (IOException ioe) { + throw new SignatureException("Failed to parse keyUsage " + + "extension"); + } + + boolean digSigAllowed = keyUsage.get( + KeyUsageExtension.DIGITAL_SIGNATURE).booleanValue(); + + boolean nonRepuAllowed = keyUsage.get( + KeyUsageExtension.NON_REPUDIATION).booleanValue(); + + if (!digSigAllowed && !nonRepuAllowed) { + throw new SignatureException("Key usage restricted: " + + "cannot be used for " + + "digital signatures"); + } + } + + PublicKey key = cert.getPublicKey(); + + AlgorithmParameters ap = + digestEncryptionAlgorithmId.getParameters(); + try { + SignatureUtil.initVerifyWithParam(sig, key, + SignatureUtil.getParamSpec(algname, ap)); + } catch (ProviderException | InvalidAlgorithmParameterException | + InvalidKeyException e) { + throw new SignatureException(e.getMessage(), e); + } + + sig.update(dataSigned); + + if (sig.verify(encryptedDigest)) { + return this; + } + + } catch (IOException e) { + throw new SignatureException("IO error verifying signature:\n" + + e.getMessage()); + } + return null; + } + + /* Verify the content of the pkcs7 block. */ + SignerInfo verify(PKCS7 block) + throws NoSuchAlgorithmException, SignatureException { + return verify(block, null); + } + + + public BigInteger getVersion() { + return version; + } + + public X500Name getIssuerName() { + return issuerName; + } + + public BigInteger getCertificateSerialNumber() { + return certificateSerialNumber; + } + + public AlgorithmId getDigestAlgorithmId() { + return digestAlgorithmId; + } + + public PKCS9Attributes getAuthenticatedAttributes() { + return authenticatedAttributes; + } + + public AlgorithmId getDigestEncryptionAlgorithmId() { + return digestEncryptionAlgorithmId; + } + + public byte[] getEncryptedDigest() { + return encryptedDigest; + } + + public PKCS9Attributes getUnauthenticatedAttributes() { + return unauthenticatedAttributes; + } + + /* + * Extracts a timestamp from a PKCS7 SignerInfo. + * + * Examines the signer's unsigned attributes for a + * signatureTimestampToken attribute. If present, + * then it is parsed to extract the date and time at which the + * timestamp was generated. + * + * @param info A signer information element of a PKCS 7 block. + * + * @return A timestamp token or null if none is present. + * @throws IOException if an error is encountered while parsing the + * PKCS7 data. + * @throws NoSuchAlgorithmException if an error is encountered while + * verifying the PKCS7 object. + * @throws SignatureException if an error is encountered while + * verifying the PKCS7 object. + * @throws CertificateException if an error is encountered while generating + * the TSA's certpath. + */ + public Timestamp getTimestamp() + throws IOException, NoSuchAlgorithmException, SignatureException, + CertificateException + { + if (timestamp != null || !hasTimestamp) + return timestamp; + + if (unauthenticatedAttributes == null) { + hasTimestamp = false; + return null; + } + PKCS9Attribute tsTokenAttr = + unauthenticatedAttributes.getAttribute( + PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID); + if (tsTokenAttr == null) { + hasTimestamp = false; + return null; + } + + PKCS7 tsToken = new PKCS7((byte[])tsTokenAttr.getValue()); + // Extract the content (an encoded timestamp token info) + byte[] encTsTokenInfo = tsToken.getContentInfo().getData(); + // Extract the signer (the Timestamping Authority) + // while verifying the content + SignerInfo[] tsa = tsToken.verify(encTsTokenInfo); + // Expect only one signer + ArrayList chain = tsa[0].getCertificateChain(tsToken); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + CertPath tsaChain = cf.generateCertPath(chain); + // Create a timestamp token info object + TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo); + // Check that the signature timestamp applies to this signature + verifyTimestamp(tsTokenInfo); + // Create a timestamp object + timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain); + return timestamp; + } + + /* + * Check that the signature timestamp applies to this signature. + * Match the hash present in the signature timestamp token against the hash + * of this signature. + */ + private void verifyTimestamp(TimestampToken token) + throws NoSuchAlgorithmException, SignatureException { + + MessageDigest md = + MessageDigest.getInstance(token.getHashAlgorithm().getName()); + + if (!Arrays.equals(token.getHashedMessage(), + md.digest(encryptedDigest))) { + + throw new SignatureException("Signature timestamp (#" + + token.getSerialNumber() + ") generated on " + token.getDate() + + " is inapplicable"); + } + + if (debug != null) { + debug.println(); + debug.println("Detected signature timestamp (#" + + token.getSerialNumber() + ") generated on " + token.getDate()); + debug.println(); + } + } + + public String toString() { + HexDumpEncoder hexDump = new HexDumpEncoder(); + + String out = ""; + + out += "Signer Info for (issuer): " + issuerName + "\n"; + out += "\tversion: " + Debug.toHexString(version) + "\n"; + out += "\tcertificateSerialNumber: " + + Debug.toHexString(certificateSerialNumber) + "\n"; + out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n"; + if (authenticatedAttributes != null) { + out += "\tauthenticatedAttributes: " + authenticatedAttributes + + "\n"; + } + out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId + + "\n"; + + out += "\tencryptedDigest: " + "\n" + + hexDump.encodeBuffer(encryptedDigest) + "\n"; + if (unauthenticatedAttributes != null) { + out += "\tunauthenticatedAttributes: " + + unauthenticatedAttributes + "\n"; + } + return out; + } +} diff --git a/src/sun/security/pkcs/SigningCertificateInfo.java b/src/sun/security/pkcs/SigningCertificateInfo.java new file mode 100644 index 00000000..56759349 --- /dev/null +++ b/src/sun/security/pkcs/SigningCertificateInfo.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs; + +import java.io.IOException; + +import sun.misc.HexDumpEncoder; +import sun.security.util.DerValue; +import sun.security.x509.GeneralNames; +import sun.security.x509.SerialNumber; + +/** + * This class represents a signing certificate attribute. + * Its attribute value is defined by the following ASN.1 definition. + *

    + *
    + *   id-aa-signingCertificate OBJECT IDENTIFIER ::= { iso(1)
    + *     member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
    + *     smime(16) id-aa(2) 12 }
    + *
    + *   SigningCertificate ::=  SEQUENCE {
    + *       certs       SEQUENCE OF ESSCertID,
    + *       policies    SEQUENCE OF PolicyInformation OPTIONAL
    + *   }
    + *
    + *   ESSCertID ::=  SEQUENCE {
    + *       certHash        Hash,
    + *       issuerSerial    IssuerSerial OPTIONAL
    + *   }
    + *
    + *   Hash ::= OCTET STRING -- SHA1 hash of entire certificate
    + *
    + *   IssuerSerial ::= SEQUENCE {
    + *       issuer         GeneralNames,
    + *       serialNumber   CertificateSerialNumber
    + *   }
    + *
    + *   PolicyInformation ::= SEQUENCE {
    + *       policyIdentifier   CertPolicyId,
    + *       policyQualifiers   SEQUENCE SIZE (1..MAX) OF
    + *               PolicyQualifierInfo OPTIONAL }
    + *
    + *   CertPolicyId ::= OBJECT IDENTIFIER
    + *
    + *   PolicyQualifierInfo ::= SEQUENCE {
    + *       policyQualifierId  PolicyQualifierId,
    + *       qualifier        ANY DEFINED BY policyQualifierId }
    + *
    + *   -- Implementations that recognize additional policy qualifiers MUST
    + *   -- augment the following definition for PolicyQualifierId
    + *
    + *   PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
    + *
    + * 
    + * + * @since 1.5 + * @author Vincent Ryan + */ +public class SigningCertificateInfo { + + private byte[] ber = null; + + private ESSCertId[] certId = null; + + public SigningCertificateInfo(byte[] ber) throws IOException { + parse(ber); + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("[\n"); + for (int i = 0; i < certId.length; i++) { + buffer.append(certId[i].toString()); + } + // format policies as a string + buffer.append("\n]"); + + return buffer.toString(); + } + + public void parse(byte[] bytes) throws IOException { + + // Parse signingCertificate + DerValue derValue = new DerValue(bytes); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding for signingCertificate"); + } + + // Parse certs + DerValue[] certs = derValue.data.getSequence(1); + certId = new ESSCertId[certs.length]; + for (int i = 0; i < certs.length; i++) { + certId[i] = new ESSCertId(certs[i]); + } + + // Parse policies, if present + if (derValue.data.available() > 0) { + DerValue[] policies = derValue.data.getSequence(1); + for (int i = 0; i < policies.length; i++) { + // parse PolicyInformation + } + } + } +} + +class ESSCertId { + + private static volatile HexDumpEncoder hexDumper; + + private byte[] certHash; + private GeneralNames issuer; + private SerialNumber serialNumber; + + ESSCertId(DerValue certId) throws IOException { + // Parse certHash + certHash = certId.data.getDerValue().toByteArray(); + + // Parse issuerSerial, if present + if (certId.data.available() > 0) { + DerValue issuerSerial = certId.data.getDerValue(); + // Parse issuer + issuer = new GeneralNames(issuerSerial.data.getDerValue()); + // Parse serialNumber + serialNumber = new SerialNumber(issuerSerial.data.getDerValue()); + } + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append("[\n\tCertificate hash (SHA-1):\n"); + if (hexDumper == null) { + hexDumper = new HexDumpEncoder(); + } + buffer.append(hexDumper.encode(certHash)); + if (issuer != null && serialNumber != null) { + buffer.append("\n\tIssuer: " + issuer + "\n"); + buffer.append("\t" + serialNumber); + } + buffer.append("\n]"); + return buffer.toString(); + } +} diff --git a/src/sun/security/pkcs10/PKCS10.java b/src/sun/security/pkcs10/PKCS10.java new file mode 100644 index 00000000..1d6e446a --- /dev/null +++ b/src/sun/security/pkcs10/PKCS10.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.pkcs10; + +import java.io.PrintStream; +import java.io.IOException; +import java.math.BigInteger; + +import java.security.cert.CertificateException; +import java.security.*; + +import java.util.Base64; + +import sun.security.util.*; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X509Key; +import sun.security.x509.X500Name; +import sun.security.util.SignatureUtil; + + +/** + * A PKCS #10 certificate request is created and sent to a Certificate + * Authority, which then creates an X.509 certificate and returns it to + * the entity that requested it. A certificate request basically consists + * of the subject's X.500 name, public key, and optionally some attributes, + * signed using the corresponding private key. + * + * The ASN.1 syntax for a Certification Request is: + *
    + * CertificationRequest ::= SEQUENCE {
    + *    certificationRequestInfo CertificationRequestInfo,
    + *    signatureAlgorithm       SignatureAlgorithmIdentifier,
    + *    signature                Signature
    + *  }
    + *
    + * SignatureAlgorithmIdentifier ::= AlgorithmIdentifier
    + * Signature ::= BIT STRING
    + *
    + * CertificationRequestInfo ::= SEQUENCE {
    + *    version                 Version,
    + *    subject                 Name,
    + *    subjectPublicKeyInfo    SubjectPublicKeyInfo,
    + *    attributes [0] IMPLICIT Attributes
    + * }
    + * Attributes ::= SET OF Attribute
    + * 
    + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class PKCS10 { + /** + * Constructs an unsigned PKCS #10 certificate request. Before this + * request may be used, it must be encoded and signed. Then it + * must be retrieved in some conventional format (e.g. string). + * + * @param publicKey the public key that should be placed + * into the certificate generated by the CA. + */ + public PKCS10(PublicKey publicKey) { + subjectPublicKeyInfo = publicKey; + attributeSet = new PKCS10Attributes(); + } + + /** + * Constructs an unsigned PKCS #10 certificate request. Before this + * request may be used, it must be encoded and signed. Then it + * must be retrieved in some conventional format (e.g. string). + * + * @param publicKey the public key that should be placed + * into the certificate generated by the CA. + * @param attributes additonal set of PKCS10 attributes requested + * for in the certificate. + */ + public PKCS10(PublicKey publicKey, PKCS10Attributes attributes) { + subjectPublicKeyInfo = publicKey; + attributeSet = attributes; + } + + /** + * Parses an encoded, signed PKCS #10 certificate request, verifying + * the request's signature as it does so. This constructor would + * typically be used by a Certificate Authority, from which a new + * certificate would then be constructed. + * + * @param data the DER-encoded PKCS #10 request. + * @exception IOException for low level errors reading the data + * @exception SignatureException when the signature is invalid + * @exception NoSuchAlgorithmException when the signature + * algorithm is not supported in this environment + */ + public PKCS10(byte[] data) + throws IOException, SignatureException, NoSuchAlgorithmException { + DerInputStream in; + DerValue[] seq; + AlgorithmId id; + byte[] sigData; + Signature sig; + + encoded = data; + + // + // Outer sequence: request, signature algorithm, signature. + // Parse, and prepare to verify later. + // + in = new DerInputStream(data); + seq = in.getSequence(3); + + if (seq.length != 3) + throw new IllegalArgumentException("not a PKCS #10 request"); + + data = seq[0].toByteArray(); // reusing this variable + id = AlgorithmId.parse(seq[1]); + sigData = seq[2].getBitString(); + + // + // Inner sequence: version, name, key, attributes + // + BigInteger serial; + DerValue val; + + serial = seq[0].data.getBigInteger(); + if (!serial.equals(BigInteger.ZERO)) + throw new IllegalArgumentException("not PKCS #10 v1"); + + subject = new X500Name(seq[0].data); + subjectPublicKeyInfo = X509Key.parse(seq[0].data.getDerValue()); + + // Cope with a somewhat common illegal PKCS #10 format + if (seq[0].data.available() != 0) + attributeSet = new PKCS10Attributes(seq[0].data); + else + attributeSet = new PKCS10Attributes(); + + if (seq[0].data.available() != 0) + throw new IllegalArgumentException("illegal PKCS #10 data"); + + // + // OK, we parsed it all ... validate the signature using the + // key and signature algorithm we found. + // + try { + String sigAlg = id.getName(); + sig = Signature.getInstance(sigAlg); + + SignatureUtil.initVerifyWithParam(sig, subjectPublicKeyInfo, + SignatureUtil.getParamSpec(sigAlg, id.getParameters())); + + sig.update(data); + if (!sig.verify(sigData)) { + throw new SignatureException("Invalid PKCS #10 signature"); + } + } catch (InvalidKeyException e) { + throw new SignatureException("Invalid key"); + } catch (InvalidAlgorithmParameterException e) { + throw new SignatureException("Invalid signature parameters", e); + } catch (ProviderException e) { + throw new SignatureException("Error parsing signature parameters", + e.getCause()); + } + + } + + /** + * Create the signed certificate request. This will later be + * retrieved in either string or binary format. + * + * @param subject identifies the signer (by X.500 name). + * @param signature private key and signing algorithm to use. + * @exception IOException on errors. + * @exception CertificateException on certificate handling errors. + * @exception SignatureException on signature handling errors. + */ + public void encodeAndSign(X500Name subject, Signature signature) + throws CertificateException, IOException, SignatureException { + DerOutputStream out, scratch; + byte[] certificateRequestInfo; + byte[] sig; + + if (encoded != null) + throw new SignatureException("request is already signed"); + + this.subject = subject; + + /* + * Encode cert request info, wrap in a sequence for signing + */ + scratch = new DerOutputStream(); + scratch.putInteger(BigInteger.ZERO); // PKCS #10 v1.0 + subject.encode(scratch); // X.500 name + scratch.write(subjectPublicKeyInfo.getEncoded()); // public key + attributeSet.encode(scratch); + + out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, scratch); // wrap it! + certificateRequestInfo = out.toByteArray(); + scratch = out; + + /* + * Sign it ... + */ + signature.update(certificateRequestInfo, 0, + certificateRequestInfo.length); + sig = signature.sign(); + + /* + * Build guts of SIGNED macro + */ + AlgorithmId algId = null; + try { + AlgorithmParameters params = signature.getParameters(); + algId = params == null + ? AlgorithmId.get(signature.getAlgorithm()) + : AlgorithmId.get(params); + } catch (NoSuchAlgorithmException nsae) { + throw new SignatureException(nsae); + } + + algId.encode(scratch); // sig algorithm + scratch.putBitString(sig); // sig + + /* + * Wrap those guts in a sequence + */ + out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, scratch); + encoded = out.toByteArray(); + } + + /** + * Returns the subject's name. + */ + public X500Name getSubjectName() { return subject; } + + /** + * Returns the subject's public key. + */ + public PublicKey getSubjectPublicKeyInfo() + { return subjectPublicKeyInfo; } + + /** + * Returns the additional attributes requested. + */ + public PKCS10Attributes getAttributes() + { return attributeSet; } + + /** + * Returns the encoded and signed certificate request as a + * DER-encoded byte array. + * + * @return the certificate request, or null if encodeAndSign() + * has not yet been called. + */ + public byte[] getEncoded() { + if (encoded != null) + return encoded.clone(); + else + return null; + } + + /** + * Prints an E-Mailable version of the certificate request on the print + * stream passed. The format is a common base64 encoded one, supported + * by most Certificate Authorities because Netscape web servers have + * used this for some time. Some certificate authorities expect some + * more information, in particular contact information for the web + * server administrator. + * + * @param out the print stream where the certificate request + * will be printed. + * @exception IOException when an output operation failed + * @exception SignatureException when the certificate request was + * not yet signed. + */ + public void print(PrintStream out) + throws IOException, SignatureException { + if (encoded == null) + throw new SignatureException("Cert request was not signed"); + + + out.println("-----BEGIN NEW CERTIFICATE REQUEST-----"); + out.println(Base64.getMimeEncoder().encodeToString(encoded)); + out.println("-----END NEW CERTIFICATE REQUEST-----"); + } + + /** + * Provides a short description of this request. + */ + public String toString() { + return "[PKCS #10 certificate request:\n" + + subjectPublicKeyInfo.toString() + + " subject: <" + subject + ">" + "\n" + + " attributes: " + attributeSet.toString() + + "\n]"; + } + + /** + * Compares this object for equality with the specified + * object. If the other object is an + * instanceof PKCS10, then + * its encoded form is retrieved and compared with the + * encoded form of this certificate request. + * + * @param other the object to test for equality with this object. + * @return true iff the encoded forms of the two certificate + * requests match, false otherwise. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof PKCS10)) + return false; + if (encoded == null) // not signed yet + return false; + byte[] otherEncoded = ((PKCS10)other).getEncoded(); + if (otherEncoded == null) + return false; + + return java.util.Arrays.equals(encoded, otherEncoded); + } + + /** + * Returns a hashcode value for this certificate request from its + * encoded form. + * + * @return the hashcode value. + */ + public int hashCode() { + int retval = 0; + if (encoded != null) + for (int i = 1; i < encoded.length; i++) + retval += encoded[i] * i; + return(retval); + } + + private X500Name subject; + private PublicKey subjectPublicKeyInfo; + private PKCS10Attributes attributeSet; + private byte[] encoded; // signed +} diff --git a/src/sun/security/pkcs10/PKCS10Attribute.java b/src/sun/security/pkcs10/PKCS10Attribute.java new file mode 100644 index 00000000..c7cac3ba --- /dev/null +++ b/src/sun/security/pkcs10/PKCS10Attribute.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs10; + +import java.io.OutputStream; +import java.io.IOException; + +import sun.security.pkcs.PKCS9Attribute; +import sun.security.util.*; + +/** + * Represent a PKCS#10 Attribute. + * + *

    Attributes are additonal information which can be inserted in a PKCS#10 + * certificate request. For example a "Driving License Certificate" could have + * the driving license number as an attribute. + * + *

    Attributes are represented as a sequence of the attribute identifier + * (Object Identifier) and a set of DER encoded attribute values. + * + * ASN.1 definition of Attribute: + *

    + * Attribute :: SEQUENCE {
    + *    type    AttributeType,
    + *    values  SET OF AttributeValue
    + * }
    + * AttributeType  ::= OBJECT IDENTIFIER
    + * AttributeValue ::= ANY defined by type
    + * 
    + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class PKCS10Attribute implements DerEncoder { + + protected ObjectIdentifier attributeId = null; + protected Object attributeValue = null; + + /** + * Constructs an attribute from a DER encoding. + * This constructor expects the value to be encoded as defined above, + * i.e. a SEQUENCE of OID and SET OF value(s), not a literal + * X.509 v3 extension. Only PKCS9 defined attributes are supported + * currently. + * + * @param derVal the der encoded attribute. + * @exception IOException on parsing errors. + */ + public PKCS10Attribute(DerValue derVal) throws IOException { + PKCS9Attribute attr = new PKCS9Attribute(derVal); + this.attributeId = attr.getOID(); + this.attributeValue = attr.getValue(); + } + + /** + * Constructs an attribute from individual components of + * ObjectIdentifier and the value (any java object). + * + * @param attributeId the ObjectIdentifier of the attribute. + * @param attributeValue an instance of a class that implements + * the attribute identified by the ObjectIdentifier. + */ + public PKCS10Attribute(ObjectIdentifier attributeId, + Object attributeValue) { + this.attributeId = attributeId; + this.attributeValue = attributeValue; + } + + /** + * Constructs an attribute from PKCS9 attribute. + * + * @param attr the PKCS9Attribute to create from. + */ + public PKCS10Attribute(PKCS9Attribute attr) { + this.attributeId = attr.getOID(); + this.attributeValue = attr.getValue(); + } + + /** + * DER encode this object onto an output stream. + * Implements the DerEncoder interface. + * + * @param out + * the OutputStream on which to write the DER encoding. + * + * @exception IOException on encoding errors. + */ + public void derEncode(OutputStream out) throws IOException { + PKCS9Attribute attr = new PKCS9Attribute(attributeId, attributeValue); + attr.derEncode(out); + } + + /** + * Returns the ObjectIdentifier of the attribute. + */ + public ObjectIdentifier getAttributeId() { + return (attributeId); + } + + /** + * Returns the attribute value. + */ + public Object getAttributeValue() { + return (attributeValue); + } + + /** + * Returns the attribute in user readable form. + */ + public String toString() { + return (attributeValue.toString()); + } +} diff --git a/src/sun/security/pkcs10/PKCS10Attributes.java b/src/sun/security/pkcs10/PKCS10Attributes.java new file mode 100644 index 00000000..8ee37b5a --- /dev/null +++ b/src/sun/security/pkcs10/PKCS10Attributes.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs10; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; + +import sun.security.util.*; + +/** + * This class defines the PKCS10 attributes for the request. + * The ASN.1 syntax for this is: + *
    + * Attributes ::= SET OF Attribute
    + * 
    + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see PKCS10 + * @see PKCS10Attribute + */ +public class PKCS10Attributes implements DerEncoder { + + private Hashtable map = + new Hashtable(3); + + /** + * Default constructor for the PKCS10 attribute. + */ + public PKCS10Attributes() { } + + /** + * Create the object from the array of PKCS10Attribute objects. + * + * @param attrs the array of PKCS10Attribute objects. + */ + public PKCS10Attributes(PKCS10Attribute[] attrs) { + for (int i = 0; i < attrs.length; i++) { + map.put(attrs[i].getAttributeId().toString(), attrs[i]); + } + } + + /** + * Create the object, decoding the values from the passed DER stream. + * The DER stream contains the SET OF Attribute. + * + * @param in the DerInputStream to read the attributes from. + * @exception IOException on decoding errors. + */ + public PKCS10Attributes(DerInputStream in) throws IOException { + DerValue[] attrs = in.getSet(3, true); + + if (attrs == null) + throw new IOException("Illegal encoding of attributes"); + for (int i = 0; i < attrs.length; i++) { + PKCS10Attribute attr = new PKCS10Attribute(attrs[i]); + map.put(attr.getAttributeId().toString(), attr); + } + } + + /** + * Encode the attributes in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + derEncode(out); + } + + /** + * Encode the attributes in DER form to the stream. + * Implements the DerEncoder interface. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on encoding errors. + */ + public void derEncode(OutputStream out) throws IOException { + // first copy the elements into an array + Collection allAttrs = map.values(); + PKCS10Attribute[] attribs = + allAttrs.toArray(new PKCS10Attribute[map.size()]); + + DerOutputStream attrOut = new DerOutputStream(); + attrOut.putOrderedSetOf(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0), + attribs); + out.write(attrOut.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void setAttribute(String name, Object obj) { + if (obj instanceof PKCS10Attribute) { + map.put(name, (PKCS10Attribute)obj); + } + } + + /** + * Get the attribute value. + */ + public Object getAttribute(String name) { + return map.get(name); + } + + /** + * Delete the attribute value. + */ + public void deleteAttribute(String name) { + map.remove(name); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + return (map.elements()); + } + + /** + * Return a Collection of attributes existing within this + * PKCS10Attributes object. + */ + public Collection getAttributes() { + return (Collections.unmodifiableCollection(map.values())); + } + + /** + * Compares this PKCS10Attributes for equality with the specified + * object. If the other object is an + * instanceof PKCS10Attributes, then + * all the entries are compared with the entries from this. + * + * @param other the object to test for equality with this PKCS10Attributes. + * @return true if all the entries match that of the Other, + * false otherwise. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof PKCS10Attributes)) + return false; + + Collection othersAttribs = + ((PKCS10Attributes)other).getAttributes(); + PKCS10Attribute[] attrs = + othersAttribs.toArray(new PKCS10Attribute[othersAttribs.size()]); + int len = attrs.length; + if (len != map.size()) + return false; + PKCS10Attribute thisAttr, otherAttr; + String key = null; + for (int i=0; i < len; i++) { + otherAttr = attrs[i]; + key = otherAttr.getAttributeId().toString(); + + if (key == null) + return false; + thisAttr = map.get(key); + if (thisAttr == null) + return false; + if (! thisAttr.equals(otherAttr)) + return false; + } + return true; + } + + /** + * Returns a hashcode value for this PKCS10Attributes. + * + * @return the hashcode value. + */ + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns a string representation of this PKCS10Attributes object + * in the form of a set of entries, enclosed in braces and separated + * by the ASCII characters "" (comma and space). + *

    Overrides the toString method of Object. + * + * @return a string representation of this PKCS10Attributes. + */ + public String toString() { + String s = map.size() + "\n" + map.toString(); + return s; + } +} diff --git a/src/sun/security/pkcs11/Config.java b/src/sun/security/pkcs11/Config.java new file mode 100644 index 00000000..a92eb68f --- /dev/null +++ b/src/sun/security/pkcs11/Config.java @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import static java.io.StreamTokenizer.*; +import java.math.BigInteger; +import java.util.*; + +import java.security.*; + +import sun.security.action.GetPropertyAction; +import sun.security.util.PropertyExpander; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_ANY; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.DECRYPT_NULL; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.DECRYPT_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.DERIVE_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.ENCRYPT_NULL; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.ENCRYPT_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.EXTRACTABLE_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.SENSITIVE_FALSE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.SIGN_RECOVER_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.SIGN_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.TOKEN_FALSE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.UNWRAP_NULL; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.UNWRAP_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.VERIFY_RECOVER_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.VERIFY_TRUE; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.WRAP_NULL; +import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.WRAP_TRUE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * Configuration container and file parsing. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class Config { + + static final int ERR_HALT = 1; + static final int ERR_IGNORE_ALL = 2; + static final int ERR_IGNORE_LIB = 3; + + // same as allowSingleThreadedModules but controlled via a system property + // and applied to all providers. if set to false, no SunPKCS11 instances + // will accept single threaded modules regardless of the setting in their + // config files. + private static final boolean staticAllowSingleThreadedModules; + + static { + String p = "sun.security.pkcs11.allowSingleThreadedModules"; + String s = AccessController.doPrivileged(new GetPropertyAction(p)); + if ("false".equalsIgnoreCase(s)) { + staticAllowSingleThreadedModules = false; + } else { + staticAllowSingleThreadedModules = true; + } + } + + // temporary storage for configurations + // needed because the SunPKCS11 needs to call the superclass constructor + // in provider before accessing any instance variables + private final static Map configMap = + new HashMap(); + + static Config getConfig(final String name, final InputStream stream) { + Config config = configMap.get(name); + if (config != null) { + return config; + } + try { + config = new Config(name, stream); + configMap.put(name, config); + return config; + } catch (Exception e) { + throw new ProviderException("Error parsing configuration", e); + } + } + + static Config removeConfig(String name) { + return configMap.remove(name); + } + + private final static boolean DEBUG = false; + + private static void debug(Object o) { + if (DEBUG) { + System.out.println(o); + } + } + + // Reader and StringTokenizer used during parsing + private Reader reader; + + private StreamTokenizer st; + + private Set parsedKeywords; + + // name suffix of the provider + private String name; + + // name of the PKCS#11 library + private String library; + + // description to pass to the provider class + private String description; + + // slotID of the slot to use + private int slotID = -1; + + // slot to use, specified as index in the slotlist + private int slotListIndex = -1; + + // set of enabled mechanisms (or null to use default) + private Set enabledMechanisms; + + // set of disabled mechanisms + private Set disabledMechanisms; + + // whether to print debug info during startup + private boolean showInfo = false; + + // template manager, initialized from parsed attributes + private TemplateManager templateManager; + + // how to handle error during startup, one of ERR_ + private int handleStartupErrors = ERR_HALT; + + // flag indicating whether the P11KeyStore should + // be more tolerant of input parameters + private boolean keyStoreCompatibilityMode = true; + + // flag indicating whether we need to explicitly cancel operations + // see Token + private boolean explicitCancel = true; + + // how often to test for token insertion, if no token is present + private int insertionCheckInterval = 2000; + + // flag inidicating whether to omit the call to C_Initialize() + // should be used only if we are running within a process that + // has already called it (e.g. Plugin inside of Mozilla/NSS) + private boolean omitInitialize = false; + + // whether to allow modules that only support single threaded access. + // they cannot be used safely from multiple PKCS#11 consumers in the + // same process, for example NSS and SunPKCS11 + private boolean allowSingleThreadedModules = true; + + // name of the C function that returns the PKCS#11 functionlist + // This option primarily exists for the deprecated + // Secmod.Module.getProvider() method. + private String functionList = "C_GetFunctionList"; + + // whether to use NSS secmod mode. Implicitly set if nssLibraryDirectory, + // nssSecmodDirectory, or nssModule is specified. + private boolean nssUseSecmod; + + // location of the NSS library files (libnss3.so, etc.) + private String nssLibraryDirectory; + + // location of secmod.db + private String nssSecmodDirectory; + + // which NSS module to use + private String nssModule; + + private Secmod.DbMode nssDbMode = Secmod.DbMode.READ_WRITE; + + // Whether the P11KeyStore should specify the CKA_NETSCAPE_DB attribute + // when creating private keys. Only valid if nssUseSecmod is true. + private boolean nssNetscapeDbWorkaround = true; + + // Special init argument string for the NSS softtoken. + // This is used when using the NSS softtoken directly without secmod mode. + private String nssArgs; + + // whether to use NSS trust attributes for the KeyStore of this provider + // this option is for internal use by the SunPKCS11 code only and + // works only for NSS providers created via the Secmod API + private boolean nssUseSecmodTrust = false; + + // Flag to indicate whether the X9.63 encoding for EC points shall be used + // (true) or whether that encoding shall be wrapped in an ASN.1 OctetString + // (false). + private boolean useEcX963Encoding = false; + + // Flag to indicate whether NSS should favour performance (false) or + // memory footprint (true). + private boolean nssOptimizeSpace = false; + + private Config(String filename, InputStream in) throws IOException { + if (in == null) { + if (filename.startsWith("--")) { + // inline config + String config = filename.substring(2).replace("\\n", "\n"); + reader = new StringReader(config); + } else { + in = new FileInputStream(expand(filename)); + } + } + if (reader == null) { + reader = new BufferedReader(new InputStreamReader(in)); + } + parsedKeywords = new HashSet(); + st = new StreamTokenizer(reader); + setupTokenizer(); + parse(); + } + + String getName() { + return name; + } + + String getLibrary() { + return library; + } + + String getDescription() { + if (description != null) { + return description; + } + return "SunPKCS11-" + name + " using library " + library; + } + + int getSlotID() { + return slotID; + } + + int getSlotListIndex() { + if ((slotID == -1) && (slotListIndex == -1)) { + // if neither is set, default to first slot + return 0; + } else { + return slotListIndex; + } + } + + boolean getShowInfo() { + return (SunPKCS11.debug != null) || showInfo; + } + + TemplateManager getTemplateManager() { + if (templateManager == null) { + templateManager = new TemplateManager(); + } + return templateManager; + } + + boolean isEnabled(long m) { + if (enabledMechanisms != null) { + return enabledMechanisms.contains(Long.valueOf(m)); + } + if (disabledMechanisms != null) { + return !disabledMechanisms.contains(Long.valueOf(m)); + } + return true; + } + + int getHandleStartupErrors() { + return handleStartupErrors; + } + + boolean getKeyStoreCompatibilityMode() { + return keyStoreCompatibilityMode; + } + + boolean getExplicitCancel() { + return explicitCancel; + } + + int getInsertionCheckInterval() { + return insertionCheckInterval; + } + + boolean getOmitInitialize() { + return omitInitialize; + } + + boolean getAllowSingleThreadedModules() { + return staticAllowSingleThreadedModules && allowSingleThreadedModules; + } + + String getFunctionList() { + return functionList; + } + + boolean getNssUseSecmod() { + return nssUseSecmod; + } + + String getNssLibraryDirectory() { + return nssLibraryDirectory; + } + + String getNssSecmodDirectory() { + return nssSecmodDirectory; + } + + String getNssModule() { + return nssModule; + } + + Secmod.DbMode getNssDbMode() { + return nssDbMode; + } + + public boolean getNssNetscapeDbWorkaround() { + return nssUseSecmod && nssNetscapeDbWorkaround; + } + + String getNssArgs() { + return nssArgs; + } + + boolean getNssUseSecmodTrust() { + return nssUseSecmodTrust; + } + + boolean getUseEcX963Encoding() { + return useEcX963Encoding; + } + + boolean getNssOptimizeSpace() { + return nssOptimizeSpace; + } + + private static String expand(final String s) throws IOException { + try { + return PropertyExpander.expand(s); + } catch (Exception e) { + throw new RuntimeException(e.getMessage()); + } + } + + private void setupTokenizer() { + st.resetSyntax(); + st.wordChars('a', 'z'); + st.wordChars('A', 'Z'); + st.wordChars('0', '9'); + st.wordChars(':', ':'); + st.wordChars('.', '.'); + st.wordChars('_', '_'); + st.wordChars('-', '-'); + st.wordChars('/', '/'); + st.wordChars('\\', '\\'); + st.wordChars('$', '$'); + st.wordChars('{', '{'); // need {} for property subst + st.wordChars('}', '}'); + st.wordChars('*', '*'); + st.wordChars('+', '+'); + st.wordChars('~', '~'); + // XXX check ASCII table and add all other characters except special + + // special: #="(), + st.whitespaceChars(0, ' '); + st.commentChar('#'); + st.eolIsSignificant(true); + st.quoteChar('\"'); + } + + private ConfigurationException excToken(String msg) { + return new ConfigurationException(msg + " " + st); + } + + private ConfigurationException excLine(String msg) { + return new ConfigurationException(msg + ", line " + st.lineno()); + } + + private void parse() throws IOException { + while (true) { + int token = nextToken(); + if (token == TT_EOF) { + break; + } + if (token == TT_EOL) { + continue; + } + if (token != TT_WORD) { + throw excToken("Unexpected token:"); + } + String word = st.sval; + if (word.equals("name")) { + name = parseStringEntry(word); + } else if (word.equals("library")) { + library = parseLibrary(word); + } else if (word.equals("description")) { + parseDescription(word); + } else if (word.equals("slot")) { + parseSlotID(word); + } else if (word.equals("slotListIndex")) { + parseSlotListIndex(word); + } else if (word.equals("enabledMechanisms")) { + parseEnabledMechanisms(word); + } else if (word.equals("disabledMechanisms")) { + parseDisabledMechanisms(word); + } else if (word.equals("attributes")) { + parseAttributes(word); + } else if (word.equals("handleStartupErrors")) { + parseHandleStartupErrors(word); + } else if (word.endsWith("insertionCheckInterval")) { + insertionCheckInterval = parseIntegerEntry(word); + if (insertionCheckInterval < 100) { + throw excLine(word + " must be at least 100 ms"); + } + } else if (word.equals("showInfo")) { + showInfo = parseBooleanEntry(word); + } else if (word.equals("keyStoreCompatibilityMode")) { + keyStoreCompatibilityMode = parseBooleanEntry(word); + } else if (word.equals("explicitCancel")) { + explicitCancel = parseBooleanEntry(word); + } else if (word.equals("omitInitialize")) { + omitInitialize = parseBooleanEntry(word); + } else if (word.equals("allowSingleThreadedModules")) { + allowSingleThreadedModules = parseBooleanEntry(word); + } else if (word.equals("functionList")) { + functionList = parseStringEntry(word); + } else if (word.equals("nssUseSecmod")) { + nssUseSecmod = parseBooleanEntry(word); + } else if (word.equals("nssLibraryDirectory")) { + nssLibraryDirectory = parseLibrary(word); + nssUseSecmod = true; + } else if (word.equals("nssSecmodDirectory")) { + nssSecmodDirectory = expand(parseStringEntry(word)); + nssUseSecmod = true; + } else if (word.equals("nssModule")) { + nssModule = parseStringEntry(word); + nssUseSecmod = true; + } else if (word.equals("nssDbMode")) { + String mode = parseStringEntry(word); + if (mode.equals("readWrite")) { + nssDbMode = Secmod.DbMode.READ_WRITE; + } else if (mode.equals("readOnly")) { + nssDbMode = Secmod.DbMode.READ_ONLY; + } else if (mode.equals("noDb")) { + nssDbMode = Secmod.DbMode.NO_DB; + } else { + throw excToken("nssDbMode must be one of readWrite, readOnly, and noDb:"); + } + nssUseSecmod = true; + } else if (word.equals("nssNetscapeDbWorkaround")) { + nssNetscapeDbWorkaround = parseBooleanEntry(word); + nssUseSecmod = true; + } else if (word.equals("nssArgs")) { + parseNSSArgs(word); + } else if (word.equals("nssUseSecmodTrust")) { + nssUseSecmodTrust = parseBooleanEntry(word); + } else if (word.equals("useEcX963Encoding")) { + useEcX963Encoding = parseBooleanEntry(word); + } else if (word.equals("nssOptimizeSpace")) { + nssOptimizeSpace = parseBooleanEntry(word); + } else { + throw new ConfigurationException + ("Unknown keyword '" + word + "', line " + st.lineno()); + } + parsedKeywords.add(word); + } + reader.close(); + reader = null; + st = null; + parsedKeywords = null; + if (name == null) { + throw new ConfigurationException("name must be specified"); + } + if (nssUseSecmod == false) { + if (library == null) { + throw new ConfigurationException("library must be specified"); + } + } else { + if (library != null) { + throw new ConfigurationException + ("library must not be specified in NSS mode"); + } + if ((slotID != -1) || (slotListIndex != -1)) { + throw new ConfigurationException + ("slot and slotListIndex must not be specified in NSS mode"); + } + if (nssArgs != null) { + throw new ConfigurationException + ("nssArgs must not be specified in NSS mode"); + } + if (nssUseSecmodTrust != false) { + throw new ConfigurationException("nssUseSecmodTrust is an " + + "internal option and must not be specified in NSS mode"); + } + } + } + + // + // Parsing helper methods + // + + private int nextToken() throws IOException { + int token = st.nextToken(); + debug(st); + return token; + } + + private void parseEquals() throws IOException { + int token = nextToken(); + if (token != '=') { + throw excToken("Expected '=', read"); + } + } + + private void parseOpenBraces() throws IOException { + while (true) { + int token = nextToken(); + if (token == TT_EOL) { + continue; + } + if ((token == TT_WORD) && st.sval.equals("{")) { + return; + } + throw excToken("Expected '{', read"); + } + } + + private boolean isCloseBraces(int token) { + return (token == TT_WORD) && st.sval.equals("}"); + } + + private String parseWord() throws IOException { + int token = nextToken(); + if (token != TT_WORD) { + throw excToken("Unexpected value:"); + } + return st.sval; + } + + private String parseStringEntry(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + + int token = nextToken(); + if (token != TT_WORD && token != '\"') { + // not a word token nor a string enclosed by double quotes + throw excToken("Unexpected value:"); + } + String value = st.sval; + + debug(keyword + ": " + value); + return value; + } + + private boolean parseBooleanEntry(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + boolean value = parseBoolean(); + debug(keyword + ": " + value); + return value; + } + + private int parseIntegerEntry(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + int value = decodeNumber(parseWord()); + debug(keyword + ": " + value); + return value; + } + + private boolean parseBoolean() throws IOException { + String val = parseWord(); + switch (val) { + case "true": + return true; + case "false": + return false; + default: + throw excToken("Expected boolean value, read:"); + } + } + + private String parseLine() throws IOException { + String s = parseWord(); + while (true) { + int token = nextToken(); + if ((token == TT_EOL) || (token == TT_EOF)) { + break; + } + if (token != TT_WORD) { + throw excToken("Unexpected value"); + } + s = s + " " + st.sval; + } + return s; + } + + private int decodeNumber(String str) throws IOException { + try { + if (str.startsWith("0x") || str.startsWith("0X")) { + return Integer.parseInt(str.substring(2), 16); + } else { + return Integer.parseInt(str); + } + } catch (NumberFormatException e) { + throw excToken("Expected number, read"); + } + } + + private static boolean isNumber(String s) { + if (s.length() == 0) { + return false; + } + char ch = s.charAt(0); + return ((ch >= '0') && (ch <= '9')); + } + + private void parseComma() throws IOException { + int token = nextToken(); + if (token != ',') { + throw excToken("Expected ',', read"); + } + } + + private static boolean isByteArray(String val) { + return val.startsWith("0h"); + } + + private byte[] decodeByteArray(String str) throws IOException { + if (str.startsWith("0h") == false) { + throw excToken("Expected byte array value, read"); + } + str = str.substring(2); + // XXX proper hex parsing + try { + return new BigInteger(str, 16).toByteArray(); + } catch (NumberFormatException e) { + throw excToken("Expected byte array value, read"); + } + } + + private void checkDup(String keyword) throws IOException { + if (parsedKeywords.contains(keyword)) { + throw excLine(keyword + " must only be specified once"); + } + } + + // + // individual entry parsing methods + // + + private String parseLibrary(String keyword) throws IOException { + String lib = parseStringEntry(keyword); + lib = expand(lib); + int i = lib.indexOf("/$ISA/"); + if (i != -1) { + // replace "/$ISA/" with "/sparcv9/" on 64-bit Solaris SPARC + // and with "/amd64/" on Solaris AMD64. + // On all other platforms, just turn it into a "/" + String osName = System.getProperty("os.name", ""); + String osArch = System.getProperty("os.arch", ""); + String prefix = lib.substring(0, i); + String suffix = lib.substring(i + 5); + if (osName.equals("SunOS") && osArch.equals("sparcv9")) { + lib = prefix + "/sparcv9" + suffix; + } else if (osName.equals("SunOS") && osArch.equals("amd64")) { + lib = prefix + "/amd64" + suffix; + } else { + lib = prefix + suffix; + } + } + debug(keyword + ": " + lib); + + // Check to see if full path is specified to prevent the DLL + // preloading attack + if (!(new File(lib)).isAbsolute()) { + throw new ConfigurationException( + "Absolute path required for library value: " + lib); + } + return lib; + } + + private void parseDescription(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + description = parseLine(); + debug("description: " + description); + } + + private void parseSlotID(String keyword) throws IOException { + if (slotID >= 0) { + throw excLine("Duplicate slot definition"); + } + if (slotListIndex >= 0) { + throw excLine + ("Only one of slot and slotListIndex must be specified"); + } + parseEquals(); + String slotString = parseWord(); + slotID = decodeNumber(slotString); + debug("slot: " + slotID); + } + + private void parseSlotListIndex(String keyword) throws IOException { + if (slotListIndex >= 0) { + throw excLine("Duplicate slotListIndex definition"); + } + if (slotID >= 0) { + throw excLine + ("Only one of slot and slotListIndex must be specified"); + } + parseEquals(); + String slotString = parseWord(); + slotListIndex = decodeNumber(slotString); + debug("slotListIndex: " + slotListIndex); + } + + private void parseEnabledMechanisms(String keyword) throws IOException { + enabledMechanisms = parseMechanisms(keyword); + } + + private void parseDisabledMechanisms(String keyword) throws IOException { + disabledMechanisms = parseMechanisms(keyword); + } + + private Set parseMechanisms(String keyword) throws IOException { + checkDup(keyword); + Set mechs = new HashSet(); + parseEquals(); + parseOpenBraces(); + while (true) { + int token = nextToken(); + if (isCloseBraces(token)) { + break; + } + if (token == TT_EOL) { + continue; + } + if (token != TT_WORD) { + throw excToken("Expected mechanism, read"); + } + long mech = parseMechanism(st.sval); + mechs.add(Long.valueOf(mech)); + } + if (DEBUG) { + System.out.print("mechanisms: ["); + for (Long mech : mechs) { + System.out.print(Functions.getMechanismName(mech)); + System.out.print(", "); + } + System.out.println("]"); + } + return mechs; + } + + private long parseMechanism(String mech) throws IOException { + if (isNumber(mech)) { + return decodeNumber(mech); + } else { + try { + return Functions.getMechanismId(mech); + } catch (IllegalArgumentException e) { + throw excLine("Unknown mechanism: " + mech); + } + } + } + + private void parseAttributes(String keyword) throws IOException { + if (templateManager == null) { + templateManager = new TemplateManager(); + } + int token = nextToken(); + if (token == '=') { + String s = parseWord(); + if (s.equals("compatibility") == false) { + throw excLine("Expected 'compatibility', read " + s); + } + setCompatibilityAttributes(); + return; + } + if (token != '(') { + throw excToken("Expected '(' or '=', read"); + } + String op = parseOperation(); + parseComma(); + long objectClass = parseObjectClass(); + parseComma(); + long keyAlg = parseKeyAlgorithm(); + token = nextToken(); + if (token != ')') { + throw excToken("Expected ')', read"); + } + parseEquals(); + parseOpenBraces(); + List attributes = new ArrayList(); + while (true) { + token = nextToken(); + if (isCloseBraces(token)) { + break; + } + if (token == TT_EOL) { + continue; + } + if (token != TT_WORD) { + throw excToken("Expected mechanism, read"); + } + String attributeName = st.sval; + long attributeId = decodeAttributeName(attributeName); + parseEquals(); + String attributeValue = parseWord(); + attributes.add(decodeAttributeValue(attributeId, attributeValue)); + } + templateManager.addTemplate + (op, objectClass, keyAlg, attributes.toArray(CK_A0)); + } + + private void setCompatibilityAttributes() { + // all secret keys + templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY, + new CK_ATTRIBUTE[] { + TOKEN_FALSE, + SENSITIVE_FALSE, + EXTRACTABLE_TRUE, + ENCRYPT_TRUE, + DECRYPT_TRUE, + WRAP_TRUE, + UNWRAP_TRUE, + }); + + // generic secret keys are special + // They are used as MAC keys plus for the SSL/TLS (pre)master secrets + templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET, + new CK_ATTRIBUTE[] { + SIGN_TRUE, + VERIFY_TRUE, + ENCRYPT_NULL, + DECRYPT_NULL, + WRAP_NULL, + UNWRAP_NULL, + DERIVE_TRUE, + }); + + // all private and public keys + templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY, + new CK_ATTRIBUTE[] { + TOKEN_FALSE, + SENSITIVE_FALSE, + EXTRACTABLE_TRUE, + }); + templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY, + new CK_ATTRIBUTE[] { + TOKEN_FALSE, + }); + + // additional attributes for RSA private keys + templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA, + new CK_ATTRIBUTE[] { + DECRYPT_TRUE, + SIGN_TRUE, + SIGN_RECOVER_TRUE, + UNWRAP_TRUE, + }); + // additional attributes for RSA public keys + templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA, + new CK_ATTRIBUTE[] { + ENCRYPT_TRUE, + VERIFY_TRUE, + VERIFY_RECOVER_TRUE, + WRAP_TRUE, + }); + + // additional attributes for DSA private keys + templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA, + new CK_ATTRIBUTE[] { + SIGN_TRUE, + }); + // additional attributes for DSA public keys + templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA, + new CK_ATTRIBUTE[] { + VERIFY_TRUE, + }); + + // additional attributes for DH private keys + templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH, + new CK_ATTRIBUTE[] { + DERIVE_TRUE, + }); + + // additional attributes for EC private keys + templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC, + new CK_ATTRIBUTE[] { + SIGN_TRUE, + DERIVE_TRUE, + }); + // additional attributes for EC public keys + templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC, + new CK_ATTRIBUTE[] { + VERIFY_TRUE, + }); + } + + private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0]; + + private String parseOperation() throws IOException { + String op = parseWord(); + switch (op) { + case "*": + return TemplateManager.O_ANY; + case "generate": + return TemplateManager.O_GENERATE; + case "import": + return TemplateManager.O_IMPORT; + default: + throw excLine("Unknown operation " + op); + } + } + + private long parseObjectClass() throws IOException { + String name = parseWord(); + try { + return Functions.getObjectClassId(name); + } catch (IllegalArgumentException e) { + throw excLine("Unknown object class " + name); + } + } + + private long parseKeyAlgorithm() throws IOException { + String name = parseWord(); + if (isNumber(name)) { + return decodeNumber(name); + } else { + try { + return Functions.getKeyId(name); + } catch (IllegalArgumentException e) { + throw excLine("Unknown key algorithm " + name); + } + } + } + + private long decodeAttributeName(String name) throws IOException { + if (isNumber(name)) { + return decodeNumber(name); + } else { + try { + return Functions.getAttributeId(name); + } catch (IllegalArgumentException e) { + throw excLine("Unknown attribute name " + name); + } + } + } + + private CK_ATTRIBUTE decodeAttributeValue(long id, String value) + throws IOException { + if (value.equals("null")) { + return new CK_ATTRIBUTE(id); + } else if (value.equals("true")) { + return new CK_ATTRIBUTE(id, true); + } else if (value.equals("false")) { + return new CK_ATTRIBUTE(id, false); + } else if (isByteArray(value)) { + return new CK_ATTRIBUTE(id, decodeByteArray(value)); + } else if (isNumber(value)) { + return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value))); + } else { + throw excLine("Unknown attribute value " + value); + } + } + + private void parseNSSArgs(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + int token = nextToken(); + if (token != '"') { + throw excToken("Expected quoted string"); + } + nssArgs = expand(st.sval); + debug("nssArgs: " + nssArgs); + } + + private void parseHandleStartupErrors(String keyword) throws IOException { + checkDup(keyword); + parseEquals(); + String val = parseWord(); + if (val.equals("ignoreAll")) { + handleStartupErrors = ERR_IGNORE_ALL; + } else if (val.equals("ignoreMissingLibrary")) { + handleStartupErrors = ERR_IGNORE_LIB; + } else if (val.equals("halt")) { + handleStartupErrors = ERR_HALT; + } else { + throw excToken("Invalid value for handleStartupErrors:"); + } + debug("handleStartupErrors: " + handleStartupErrors); + } + +} + +class ConfigurationException extends IOException { + private static final long serialVersionUID = 254492758807673194L; + ConfigurationException(String msg) { + super(msg); + } +} diff --git a/src/sun/security/pkcs11/KeyCache.java b/src/sun/security/pkcs11/KeyCache.java new file mode 100644 index 00000000..1687649e --- /dev/null +++ b/src/sun/security/pkcs11/KeyCache.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.util.*; +import java.lang.ref.*; + +import java.security.Key; + +import sun.security.util.Cache; + +/** + * Key to P11Key translation cache. The PKCS#11 token can only perform + * operations on keys stored on the token (permanently or temporarily). That + * means that in order to allow the PKCS#11 provider to use keys from other + * providers, we need to transparently convert them to P11Keys. The engines + * do that using (Secret)KeyFactories, which in turn use this class as a + * cache. + * + * There are two KeyCache instances per provider, one for secret keys and + * one for public and private keys. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class KeyCache { + + private final Cache strongCache; + + private WeakReference> cacheReference; + + KeyCache() { + strongCache = Cache.newHardMemoryCache(16); + } + + private static final class IdentityWrapper { + final Object obj; + IdentityWrapper(Object obj) { + this.obj = obj; + } + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof IdentityWrapper == false) { + return false; + } + IdentityWrapper other = (IdentityWrapper)o; + return this.obj == other.obj; + } + public int hashCode() { + return System.identityHashCode(obj); + } + } + + synchronized P11Key get(Key key) { + P11Key p11Key = strongCache.get(new IdentityWrapper(key)); + if (p11Key != null) { + return p11Key; + } + Map map = + (cacheReference == null) ? null : cacheReference.get(); + if (map == null) { + return null; + } + return map.get(key); + } + + synchronized void put(Key key, P11Key p11Key) { + strongCache.put(new IdentityWrapper(key), p11Key); + Map map = + (cacheReference == null) ? null : cacheReference.get(); + if (map == null) { + map = new IdentityHashMap<>(); + cacheReference = new WeakReference<>(map); + } + map.put(key, p11Key); + } + +} diff --git a/src/sun/security/pkcs11/P11Cipher.java b/src/sun/security/pkcs11/P11Cipher.java new file mode 100644 index 00000000..6fe59f8d --- /dev/null +++ b/src/sun/security/pkcs11/P11Cipher.java @@ -0,0 +1,902 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.pkcs11; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Locale; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.nio.ch.DirectBuffer; +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * Cipher implementation class. This class currently supports + * DES, DESede, AES, ARCFOUR, and Blowfish. + * + * This class is designed to support ECB, CBC, CTR with NoPadding + * and ECB, CBC with PKCS5Padding. It will use its own padding impl + * if the native mechanism does not support padding. + * + * Note that PKCS#11 currently only supports ECB, CBC, and CTR. + * There are no provisions for other modes such as CFB, OFB, and PCBC. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11Cipher extends CipherSpi { + + // mode constant for ECB mode + private final static int MODE_ECB = 3; + // mode constant for CBC mode + private final static int MODE_CBC = 4; + // mode constant for CTR mode + private final static int MODE_CTR = 5; + + // padding constant for NoPadding + private final static int PAD_NONE = 5; + // padding constant for PKCS5Padding + private final static int PAD_PKCS5 = 6; + + private static interface Padding { + // ENC: format the specified buffer with padding bytes and return the + // actual padding length + int setPaddingBytes(byte[] paddingBuffer, int padLen); + + // DEC: return the length of trailing padding bytes given the specified + // padded data + int unpad(byte[] paddedData, int len) + throws BadPaddingException, IllegalBlockSizeException; + } + + private static class PKCS5Padding implements Padding { + + private final int blockSize; + + PKCS5Padding(int blockSize) + throws NoSuchPaddingException { + if (blockSize == 0) { + throw new NoSuchPaddingException + ("PKCS#5 padding not supported with stream ciphers"); + } + this.blockSize = blockSize; + } + + public int setPaddingBytes(byte[] paddingBuffer, int padLen) { + Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f)); + return padLen; + } + + public int unpad(byte[] paddedData, int len) + throws BadPaddingException, IllegalBlockSizeException { + if ((len < 1) || (len % blockSize != 0)) { + throw new IllegalBlockSizeException + ("Input length must be multiples of " + blockSize); + } + byte padValue = paddedData[len - 1]; + if (padValue < 1 || padValue > blockSize) { + throw new BadPaddingException("Invalid pad value!"); + } + // sanity check padding bytes + int padStartIndex = len - padValue; + for (int i = padStartIndex; i < len; i++) { + if (paddedData[i] != padValue) { + throw new BadPaddingException("Invalid pad bytes!"); + } + } + return padValue; + } + } + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // name of the key algorithm, e.g. DES instead of algorithm DES/CBC/... + private final String keyAlgorithm; + + // mechanism id + private final long mechanism; + + // associated session, if any + private Session session; + + // key, if init() was called + private P11Key p11Key; + + // flag indicating whether an operation is initialized + private boolean initialized; + + // falg indicating encrypt or decrypt mode + private boolean encrypt; + + // mode, one of MODE_* above (MODE_ECB for stream ciphers) + private int blockMode; + + // block size, 0 for stream ciphers + private final int blockSize; + + // padding type, on of PAD_* above (PAD_NONE for stream ciphers) + private int paddingType; + + // when the padding is requested but unsupported by the native mechanism, + // we use the following to do padding and necessary data buffering. + // padding object which generate padding and unpad the decrypted data + private Padding paddingObj; + // buffer for holding back the block which contains padding bytes + private byte[] padBuffer; + private int padBufferLen; + + // original IV, if in MODE_CBC or MODE_CTR + private byte[] iv; + + // number of bytes buffered internally by the native mechanism and padBuffer + // if we do the padding + private int bytesBuffered; + + // length of key size in bytes; currently only used by AES given its oid + // specification mandates a fixed size of the key + private int fixedKeySize = -1; + + P11Cipher(Token token, String algorithm, long mechanism) + throws PKCS11Exception, NoSuchAlgorithmException { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + + String algoParts[] = algorithm.split("/"); + + if (algoParts[0].startsWith("AES")) { + blockSize = 16; + int index = algoParts[0].indexOf('_'); + if (index != -1) { + // should be well-formed since we specify what we support + fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1))/8; + } + keyAlgorithm = "AES"; + } else { + keyAlgorithm = algoParts[0]; + if (keyAlgorithm.equals("RC4") || + keyAlgorithm.equals("ARCFOUR")) { + blockSize = 0; + } else { // DES, DESede, Blowfish + blockSize = 8; + } + } + this.blockMode = + (algoParts.length > 1 ? parseMode(algoParts[1]) : MODE_ECB); + String defPadding = (blockSize == 0 ? "NoPadding" : "PKCS5Padding"); + String paddingStr = + (algoParts.length > 2 ? algoParts[2] : defPadding); + try { + engineSetPadding(paddingStr); + } catch (NoSuchPaddingException nspe) { + // should not happen + throw new ProviderException(nspe); + } + } + + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + // Disallow change of mode for now since currently it's explicitly + // defined in transformation strings + throw new NoSuchAlgorithmException("Unsupported mode " + mode); + } + + private int parseMode(String mode) throws NoSuchAlgorithmException { + mode = mode.toUpperCase(Locale.ENGLISH); + int result; + if (mode.equals("ECB")) { + result = MODE_ECB; + } else if (mode.equals("CBC")) { + if (blockSize == 0) { + throw new NoSuchAlgorithmException + ("CBC mode not supported with stream ciphers"); + } + result = MODE_CBC; + } else if (mode.equals("CTR")) { + result = MODE_CTR; + } else { + throw new NoSuchAlgorithmException("Unsupported mode " + mode); + } + return result; + } + + // see JCE spec + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + paddingObj = null; + padBuffer = null; + padding = padding.toUpperCase(Locale.ENGLISH); + if (padding.equals("NOPADDING")) { + paddingType = PAD_NONE; + } else if (padding.equals("PKCS5PADDING")) { + if (this.blockMode == MODE_CTR) { + throw new NoSuchPaddingException + ("PKCS#5 padding not supported with CTR mode"); + } + paddingType = PAD_PKCS5; + if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD && + mechanism != CKM_AES_CBC_PAD) { + // no native padding support; use our own padding impl + paddingObj = new PKCS5Padding(blockSize); + padBuffer = new byte[blockSize]; + } + } else { + throw new NoSuchPaddingException("Unsupported padding " + padding); + } + } + + // see JCE spec + protected int engineGetBlockSize() { + return blockSize; + } + + // see JCE spec + protected int engineGetOutputSize(int inputLen) { + return doFinalLength(inputLen); + } + + // see JCE spec + protected byte[] engineGetIV() { + return (iv == null) ? null : iv.clone(); + } + + // see JCE spec + protected AlgorithmParameters engineGetParameters() { + if (iv == null) { + return null; + } + IvParameterSpec ivSpec = new IvParameterSpec(iv); + try { + AlgorithmParameters params = + AlgorithmParameters.getInstance(keyAlgorithm, + P11Util.getSunJceProvider()); + params.init(ivSpec); + return params; + } catch (GeneralSecurityException e) { + // NoSuchAlgorithmException, NoSuchProviderException + // InvalidParameterSpecException + throw new ProviderException("Could not encode parameters", e); + } + } + + // see JCE spec + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + try { + implInit(opmode, key, null, random); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException("init() failed", e); + } + } + + // see JCE spec + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + byte[] ivValue; + if (params != null) { + if (params instanceof IvParameterSpec == false) { + throw new InvalidAlgorithmParameterException + ("Only IvParameterSpec supported"); + } + IvParameterSpec ivSpec = (IvParameterSpec) params; + ivValue = ivSpec.getIV(); + } else { + ivValue = null; + } + implInit(opmode, key, ivValue, random); + } + + // see JCE spec + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + byte[] ivValue; + if (params != null) { + try { + IvParameterSpec ivSpec = + params.getParameterSpec(IvParameterSpec.class); + ivValue = ivSpec.getIV(); + } catch (InvalidParameterSpecException e) { + throw new InvalidAlgorithmParameterException + ("Could not decode IV", e); + } + } else { + ivValue = null; + } + implInit(opmode, key, ivValue, random); + } + + // actual init() implementation + private void implInit(int opmode, Key key, byte[] iv, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + cancelOperation(); + if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) { + throw new InvalidKeyException("Key size is invalid"); + } + switch (opmode) { + case Cipher.ENCRYPT_MODE: + encrypt = true; + break; + case Cipher.DECRYPT_MODE: + encrypt = false; + break; + default: + throw new InvalidAlgorithmParameterException + ("Unsupported mode: " + opmode); + } + if (blockMode == MODE_ECB) { // ECB or stream cipher + if (iv != null) { + if (blockSize == 0) { + throw new InvalidAlgorithmParameterException + ("IV not used with stream ciphers"); + } else { + throw new InvalidAlgorithmParameterException + ("IV not used in ECB mode"); + } + } + } else { // MODE_CBC or MODE_CTR + if (iv == null) { + if (encrypt == false) { + String exMsg = + (blockMode == MODE_CBC ? + "IV must be specified for decryption in CBC mode" : + "IV must be specified for decryption in CTR mode"); + throw new InvalidAlgorithmParameterException(exMsg); + } + // generate random IV + if (random == null) { + random = new SecureRandom(); + } + iv = new byte[blockSize]; + random.nextBytes(iv); + } else { + if (iv.length != blockSize) { + throw new InvalidAlgorithmParameterException + ("IV length must match block size"); + } + } + } + this.iv = iv; + p11Key = P11SecretKeyFactory.convertKey(token, key, keyAlgorithm); + try { + initialize(); + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not initialize cipher", e); + } + } + + private void cancelOperation() { + if (initialized == false) { + return; + } + initialized = false; + if ((session == null) || (token.explicitCancel == false)) { + return; + } + // cancel operation by finishing it + int bufLen = doFinalLength(0); + byte[] buffer = new byte[bufLen]; + try { + if (encrypt) { + token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen); + } else { + token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen); + } + } catch (PKCS11Exception e) { + throw new ProviderException("Cancel failed", e); + } finally { + reset(); + } + } + + private void ensureInitialized() throws PKCS11Exception { + if (initialized == false) { + initialize(); + } + } + + private void initialize() throws PKCS11Exception { + if (session == null) { + session = token.getOpSession(); + } + CK_MECHANISM mechParams = (blockMode == MODE_CTR? + new CK_MECHANISM(mechanism, new CK_AES_CTR_PARAMS(iv)) : + new CK_MECHANISM(mechanism, iv)); + + try { + if (encrypt) { + token.p11.C_EncryptInit(session.id(), mechParams, p11Key.keyID); + } else { + token.p11.C_DecryptInit(session.id(), mechParams, p11Key.keyID); + } + } catch (PKCS11Exception ex) { + // release session when initialization failed + session = token.releaseSession(session); + throw ex; + } + bytesBuffered = 0; + padBufferLen = 0; + initialized = true; + } + + // if update(inLen) is called, how big does the output buffer have to be? + private int updateLength(int inLen) { + if (inLen <= 0) { + return 0; + } + + int result = inLen + bytesBuffered; + if (blockSize != 0 && blockMode != MODE_CTR) { + // minus the number of bytes in the last incomplete block. + result -= (result & (blockSize - 1)); + } + return result; + } + + // if doFinal(inLen) is called, how big does the output buffer have to be? + private int doFinalLength(int inLen) { + if (inLen < 0) { + return 0; + } + + int result = inLen + bytesBuffered; + if (blockSize != 0 && encrypt && paddingType != PAD_NONE) { + // add the number of bytes to make the last block complete. + result += (blockSize - (result & (blockSize - 1))); + } + return result; + } + + // reset the states to the pre-initialized values + private void reset() { + initialized = false; + bytesBuffered = 0; + padBufferLen = 0; + if (session != null) { + session = token.releaseSession(session); + } + } + + // see JCE spec + protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { + try { + byte[] out = new byte[updateLength(inLen)]; + int n = engineUpdate(in, inOfs, inLen, out, 0); + return P11Util.convert(out, 0, n); + } catch (ShortBufferException e) { + // convert since the output length is calculated by updateLength() + throw new ProviderException(e); + } + } + + // see JCE spec + protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) throws ShortBufferException { + int outLen = out.length - outOfs; + return implUpdate(in, inOfs, inLen, out, outOfs, outLen); + } + + // see JCE spec + @Override + protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer) + throws ShortBufferException { + return implUpdate(inBuffer, outBuffer); + } + + // see JCE spec + protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) + throws IllegalBlockSizeException, BadPaddingException { + try { + byte[] out = new byte[doFinalLength(inLen)]; + int n = engineDoFinal(in, inOfs, inLen, out, 0); + return P11Util.convert(out, 0, n); + } catch (ShortBufferException e) { + // convert since the output length is calculated by doFinalLength() + throw new ProviderException(e); + } + } + + // see JCE spec + protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + int n = 0; + if ((inLen != 0) && (in != null)) { + n = engineUpdate(in, inOfs, inLen, out, outOfs); + outOfs += n; + } + n += implDoFinal(out, outOfs, out.length - outOfs); + return n; + } + + // see JCE spec + @Override + protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + int n = engineUpdate(inBuffer, outBuffer); + n += implDoFinal(outBuffer); + return n; + } + + private int implUpdate(byte[] in, int inOfs, int inLen, + byte[] out, int outOfs, int outLen) throws ShortBufferException { + if (outLen < updateLength(inLen)) { + throw new ShortBufferException(); + } + try { + ensureInitialized(); + int k = 0; + if (encrypt) { + k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen, + 0, out, outOfs, outLen); + } else { + int newPadBufferLen = 0; + if (paddingObj != null) { + if (padBufferLen != 0) { + // NSS throws up when called with data not in multiple + // of blocks. Try to work around this by holding the + // extra data in padBuffer. + if (padBufferLen != padBuffer.length) { + int bufCapacity = padBuffer.length - padBufferLen; + if (inLen > bufCapacity) { + bufferInputBytes(in, inOfs, bufCapacity); + inOfs += bufCapacity; + inLen -= bufCapacity; + } else { + bufferInputBytes(in, inOfs, inLen); + return 0; + } + } + k = token.p11.C_DecryptUpdate(session.id(), + 0, padBuffer, 0, padBufferLen, + 0, out, outOfs, outLen); + padBufferLen = 0; + } + newPadBufferLen = inLen & (blockSize - 1); + if (newPadBufferLen == 0) { + newPadBufferLen = padBuffer.length; + } + inLen -= newPadBufferLen; + } + if (inLen > 0) { + k += token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs, + inLen, 0, out, (outOfs + k), (outLen - k)); + } + // update 'padBuffer' if using our own padding impl. + if (paddingObj != null) { + bufferInputBytes(in, inOfs + inLen, newPadBufferLen); + } + } + bytesBuffered += (inLen - k); + return k; + } catch (PKCS11Exception e) { + if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) { + throw (ShortBufferException) + (new ShortBufferException().initCause(e)); + } + reset(); + throw new ProviderException("update() failed", e); + } + } + + private int implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer) + throws ShortBufferException { + int inLen = inBuffer.remaining(); + if (inLen <= 0) { + return 0; + } + + int outLen = outBuffer.remaining(); + if (outLen < updateLength(inLen)) { + throw new ShortBufferException(); + } + int origPos = inBuffer.position(); + try { + ensureInitialized(); + + long inAddr = 0; + int inOfs = 0; + byte[] inArray = null; + + if (inBuffer instanceof DirectBuffer) { + inAddr = ((DirectBuffer) inBuffer).address(); + inOfs = origPos; + } else if (inBuffer.hasArray()) { + inArray = inBuffer.array(); + inOfs = (origPos + inBuffer.arrayOffset()); + } + + long outAddr = 0; + int outOfs = 0; + byte[] outArray = null; + if (outBuffer instanceof DirectBuffer) { + outAddr = ((DirectBuffer) outBuffer).address(); + outOfs = outBuffer.position(); + } else { + if (outBuffer.hasArray()) { + outArray = outBuffer.array(); + outOfs = (outBuffer.position() + outBuffer.arrayOffset()); + } else { + outArray = new byte[outLen]; + } + } + + int k = 0; + if (encrypt) { + if (inAddr == 0 && inArray == null) { + inArray = new byte[inLen]; + inBuffer.get(inArray); + } else { + inBuffer.position(origPos + inLen); + } + k = token.p11.C_EncryptUpdate(session.id(), + inAddr, inArray, inOfs, inLen, + outAddr, outArray, outOfs, outLen); + } else { + int newPadBufferLen = 0; + if (paddingObj != null) { + if (padBufferLen != 0) { + // NSS throws up when called with data not in multiple + // of blocks. Try to work around this by holding the + // extra data in padBuffer. + if (padBufferLen != padBuffer.length) { + int bufCapacity = padBuffer.length - padBufferLen; + if (inLen > bufCapacity) { + bufferInputBytes(inBuffer, bufCapacity); + inOfs += bufCapacity; + inLen -= bufCapacity; + } else { + bufferInputBytes(inBuffer, inLen); + return 0; + } + } + k = token.p11.C_DecryptUpdate(session.id(), 0, + padBuffer, 0, padBufferLen, outAddr, outArray, + outOfs, outLen); + padBufferLen = 0; + } + newPadBufferLen = inLen & (blockSize - 1); + if (newPadBufferLen == 0) { + newPadBufferLen = padBuffer.length; + } + inLen -= newPadBufferLen; + } + if (inLen > 0) { + if (inAddr == 0 && inArray == null) { + inArray = new byte[inLen]; + inBuffer.get(inArray); + } else { + inBuffer.position(inBuffer.position() + inLen); + } + k += token.p11.C_DecryptUpdate(session.id(), inAddr, + inArray, inOfs, inLen, outAddr, outArray, + (outOfs + k), (outLen - k)); + } + // update 'padBuffer' if using our own padding impl. + if (paddingObj != null && newPadBufferLen != 0) { + bufferInputBytes(inBuffer, newPadBufferLen); + } + } + bytesBuffered += (inLen - k); + if (!(outBuffer instanceof DirectBuffer) && + !outBuffer.hasArray()) { + outBuffer.put(outArray, outOfs, k); + } else { + outBuffer.position(outBuffer.position() + k); + } + return k; + } catch (PKCS11Exception e) { + // Reset input buffer to its original position for + inBuffer.position(origPos); + if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) { + throw (ShortBufferException) + (new ShortBufferException().initCause(e)); + } + reset(); + throw new ProviderException("update() failed", e); + } + } + + private int implDoFinal(byte[] out, int outOfs, int outLen) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + int requiredOutLen = doFinalLength(0); + if (outLen < requiredOutLen) { + throw new ShortBufferException(); + } + try { + ensureInitialized(); + int k = 0; + if (encrypt) { + if (paddingObj != null) { + int actualPadLen = paddingObj.setPaddingBytes(padBuffer, + requiredOutLen - bytesBuffered); + k = token.p11.C_EncryptUpdate(session.id(), + 0, padBuffer, 0, actualPadLen, + 0, out, outOfs, outLen); + } + k += token.p11.C_EncryptFinal(session.id(), + 0, out, (outOfs + k), (outLen - k)); + } else { + if (paddingObj != null) { + if (padBufferLen != 0) { + k = token.p11.C_DecryptUpdate(session.id(), 0, + padBuffer, 0, padBufferLen, 0, padBuffer, 0, + padBuffer.length); + } + k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k, + padBuffer.length - k); + int actualPadLen = paddingObj.unpad(padBuffer, k); + k -= actualPadLen; + System.arraycopy(padBuffer, 0, out, outOfs, k); + } else { + k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs, + outLen); + } + } + return k; + } catch (PKCS11Exception e) { + handleException(e); + throw new ProviderException("doFinal() failed", e); + } finally { + reset(); + } + } + + private int implDoFinal(ByteBuffer outBuffer) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + int outLen = outBuffer.remaining(); + int requiredOutLen = doFinalLength(0); + if (outLen < requiredOutLen) { + throw new ShortBufferException(); + } + + try { + ensureInitialized(); + + long outAddr = 0; + byte[] outArray = null; + int outOfs = 0; + if (outBuffer instanceof DirectBuffer) { + outAddr = ((DirectBuffer) outBuffer).address(); + outOfs = outBuffer.position(); + } else { + if (outBuffer.hasArray()) { + outArray = outBuffer.array(); + outOfs = outBuffer.position() + outBuffer.arrayOffset(); + } else { + outArray = new byte[outLen]; + } + } + + int k = 0; + + if (encrypt) { + if (paddingObj != null) { + int actualPadLen = paddingObj.setPaddingBytes(padBuffer, + requiredOutLen - bytesBuffered); + k = token.p11.C_EncryptUpdate(session.id(), + 0, padBuffer, 0, actualPadLen, + outAddr, outArray, outOfs, outLen); + } + k += token.p11.C_EncryptFinal(session.id(), + outAddr, outArray, (outOfs + k), (outLen - k)); + } else { + if (paddingObj != null) { + if (padBufferLen != 0) { + k = token.p11.C_DecryptUpdate(session.id(), + 0, padBuffer, 0, padBufferLen, + 0, padBuffer, 0, padBuffer.length); + padBufferLen = 0; + } + k += token.p11.C_DecryptFinal(session.id(), + 0, padBuffer, k, padBuffer.length - k); + int actualPadLen = paddingObj.unpad(padBuffer, k); + k -= actualPadLen; + outArray = padBuffer; + outOfs = 0; + } else { + k = token.p11.C_DecryptFinal(session.id(), + outAddr, outArray, outOfs, outLen); + } + } + if ((!encrypt && paddingObj != null) || + (!(outBuffer instanceof DirectBuffer) && + !outBuffer.hasArray())) { + outBuffer.put(outArray, outOfs, k); + } else { + outBuffer.position(outBuffer.position() + k); + } + return k; + } catch (PKCS11Exception e) { + handleException(e); + throw new ProviderException("doFinal() failed", e); + } finally { + reset(); + } + } + + private void handleException(PKCS11Exception e) + throws ShortBufferException, IllegalBlockSizeException { + long errorCode = e.getErrorCode(); + if (errorCode == CKR_BUFFER_TOO_SMALL) { + throw (ShortBufferException) + (new ShortBufferException().initCause(e)); + } else if (errorCode == CKR_DATA_LEN_RANGE || + errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) { + throw (IllegalBlockSizeException) + (new IllegalBlockSizeException(e.toString()).initCause(e)); + } + } + + // see JCE spec + protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, + InvalidKeyException { + // XXX key wrapping + throw new UnsupportedOperationException("engineWrap()"); + } + + // see JCE spec + protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException, NoSuchAlgorithmException { + // XXX key unwrapping + throw new UnsupportedOperationException("engineUnwrap()"); + } + + // see JCE spec + @Override + protected int engineGetKeySize(Key key) throws InvalidKeyException { + int n = P11SecretKeyFactory.convertKey + (token, key, keyAlgorithm).length(); + return n; + } + + private final void bufferInputBytes(byte[] in, int inOfs, int len) { + System.arraycopy(in, inOfs, padBuffer, padBufferLen, len); + padBufferLen += len; + bytesBuffered += len; + } + + private final void bufferInputBytes(ByteBuffer inBuffer, int len) { + inBuffer.get(padBuffer, padBufferLen, len); + padBufferLen += len; + bytesBuffered += len; + } +} diff --git a/src/sun/security/pkcs11/P11DHKeyFactory.java b/src/sun/security/pkcs11/P11DHKeyFactory.java new file mode 100644 index 00000000..4cbbe684 --- /dev/null +++ b/src/sun/security/pkcs11/P11DHKeyFactory.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.interfaces.*; +import javax.crypto.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_BASE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CLASS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_KEY_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PRIVATE_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PUBLIC_KEY; + +/** + * DH KeyFactory implementation. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11DHKeyFactory extends P11KeyFactory { + + P11DHKeyFactory(Token token, String algorithm) { + super(token, algorithm); + } + + PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException { + try { + if (key instanceof DHPublicKey) { + DHPublicKey dhKey = (DHPublicKey)key; + DHParameterSpec params = dhKey.getParams(); + return generatePublic( + dhKey.getY(), + params.getP(), + params.getG() + ); + } else if ("X.509".equals(key.getFormat())) { + // let SunJCE provider parse for us, then recurse + try { + KeyFactory factory = implGetSoftwareFactory(); + key = (PublicKey)factory.translateKey(key); + return implTranslatePublicKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeyException("Could not translate key", e); + } + } else { + throw new InvalidKeyException("PublicKey must be instance " + + "of DHPublicKey or have X.509 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create DH public key", e); + } + } + + PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException { + try { + if (key instanceof DHPrivateKey) { + DHPrivateKey dhKey = (DHPrivateKey)key; + DHParameterSpec params = dhKey.getParams(); + return generatePrivate( + dhKey.getX(), + params.getP(), + params.getG() + ); + } else if ("PKCS#8".equals(key.getFormat())) { + // let SunJCE provider parse for us, then recurse + try { + KeyFactory factory = implGetSoftwareFactory(); + key = (PrivateKey)factory.translateKey(key); + return implTranslatePrivateKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeyException("Could not translate key", e); + } + } else { + throw new InvalidKeyException("PrivateKey must be instance " + + "of DHPrivateKey or have PKCS#8 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create DH private key", e); + } + } + + // see JCA spec + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof X509EncodedKeySpec) { + try { + KeyFactory factory = implGetSoftwareFactory(); + PublicKey key = factory.generatePublic(keySpec); + return implTranslatePublicKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException + ("Could not create DH public key", e); + } + } + if (keySpec instanceof DHPublicKeySpec == false) { + throw new InvalidKeySpecException("Only DHPublicKeySpec and " + + "X509EncodedKeySpec supported for DH public keys"); + } + try { + DHPublicKeySpec ds = (DHPublicKeySpec)keySpec; + return generatePublic( + ds.getY(), + ds.getP(), + ds.getG() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create DH public key", e); + } + } + + // see JCA spec + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof PKCS8EncodedKeySpec) { + try { + KeyFactory factory = implGetSoftwareFactory(); + PrivateKey key = factory.generatePrivate(keySpec); + return implTranslatePrivateKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException + ("Could not create DH private key", e); + } + } + if (keySpec instanceof DHPrivateKeySpec == false) { + throw new InvalidKeySpecException("Only DHPrivateKeySpec and " + + "PKCS8EncodedKeySpec supported for DH private keys"); + } + try { + DHPrivateKeySpec ds = (DHPrivateKeySpec)keySpec; + return generatePrivate( + ds.getX(), + ds.getP(), + ds.getG() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create DH private key", e); + } + } + + private PublicKey generatePublic(BigInteger y, BigInteger p, BigInteger g) + throws PKCS11Exception { + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH), + new CK_ATTRIBUTE(CKA_VALUE, y), + new CK_ATTRIBUTE(CKA_PRIME, p), + new CK_ATTRIBUTE(CKA_BASE, g), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PUBLIC_KEY, CKK_DH, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.publicKey + (session, keyID, "DH", p.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + private PrivateKey generatePrivate(BigInteger x, BigInteger p, + BigInteger g) throws PKCS11Exception { + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH), + new CK_ATTRIBUTE(CKA_VALUE, x), + new CK_ATTRIBUTE(CKA_PRIME, p), + new CK_ATTRIBUTE(CKA_BASE, g), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.privateKey + (session, keyID, "DH", p.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + T implGetPublicKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (DHPublicKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new DHPublicKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + return keySpec.cast(spec); + } else { // X.509 handled in superclass + throw new InvalidKeySpecException("Only DHPublicKeySpec and " + + "X509EncodedKeySpec supported for DH public keys"); + } + } + + T implGetPrivateKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (DHPrivateKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new DHPrivateKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + return keySpec.cast(spec); + } else { // PKCS#8 handled in superclass + throw new InvalidKeySpecException("Only DHPrivateKeySpec " + + "and PKCS8EncodedKeySpec supported for DH private keys"); + } + } + + KeyFactory implGetSoftwareFactory() throws GeneralSecurityException { + return KeyFactory.getInstance("DH", P11Util.getSunJceProvider()); + } + +} diff --git a/src/sun/security/pkcs11/P11DSAKeyFactory.java b/src/sun/security/pkcs11/P11DSAKeyFactory.java new file mode 100644 index 00000000..3716ac84 --- /dev/null +++ b/src/sun/security/pkcs11/P11DSAKeyFactory.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * DSA KeyFactory implementation. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11DSAKeyFactory extends P11KeyFactory { + + P11DSAKeyFactory(Token token, String algorithm) { + super(token, algorithm); + } + + PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException { + try { + if (key instanceof DSAPublicKey) { + DSAPublicKey dsaKey = (DSAPublicKey)key; + DSAParams params = dsaKey.getParams(); + return generatePublic( + dsaKey.getY(), + params.getP(), + params.getQ(), + params.getG() + ); + } else if ("X.509".equals(key.getFormat())) { + // let Sun provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + key = new sun.security.provider.DSAPublicKey(encoded); + return implTranslatePublicKey(key); + } else { + throw new InvalidKeyException("PublicKey must be instance " + + "of DSAPublicKey or have X.509 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create DSA public key", e); + } + } + + PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException { + try { + if (key instanceof DSAPrivateKey) { + DSAPrivateKey dsaKey = (DSAPrivateKey)key; + DSAParams params = dsaKey.getParams(); + return generatePrivate( + dsaKey.getX(), + params.getP(), + params.getQ(), + params.getG() + ); + } else if ("PKCS#8".equals(key.getFormat())) { + // let Sun provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + key = new sun.security.provider.DSAPrivateKey(encoded); + return implTranslatePrivateKey(key); + } else { + throw new InvalidKeyException("PrivateKey must be instance " + + "of DSAPrivateKey or have PKCS#8 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create DSA private key", e); + } + } + + // see JCA spec + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof X509EncodedKeySpec) { + try { + byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded(); + PublicKey key = new sun.security.provider.DSAPublicKey(encoded); + return implTranslatePublicKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException + ("Could not create DSA public key", e); + } + } + if (keySpec instanceof DSAPublicKeySpec == false) { + throw new InvalidKeySpecException("Only DSAPublicKeySpec and " + + "X509EncodedKeySpec supported for DSA public keys"); + } + try { + DSAPublicKeySpec ds = (DSAPublicKeySpec)keySpec; + return generatePublic( + ds.getY(), + ds.getP(), + ds.getQ(), + ds.getG() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create DSA public key", e); + } + } + + // see JCA spec + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof PKCS8EncodedKeySpec) { + try { + byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + PrivateKey key = new sun.security.provider.DSAPrivateKey(encoded); + return implTranslatePrivateKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException + ("Could not create DSA private key", e); + } + } + if (keySpec instanceof DSAPrivateKeySpec == false) { + throw new InvalidKeySpecException("Only DSAPrivateKeySpec and " + + "PKCS8EncodedKeySpec supported for DSA private keys"); + } + try { + DSAPrivateKeySpec ds = (DSAPrivateKeySpec)keySpec; + return generatePrivate( + ds.getX(), + ds.getP(), + ds.getQ(), + ds.getG() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create DSA private key", e); + } + } + + private PublicKey generatePublic(BigInteger y, BigInteger p, BigInteger q, + BigInteger g) throws PKCS11Exception { + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA), + new CK_ATTRIBUTE(CKA_VALUE, y), + new CK_ATTRIBUTE(CKA_PRIME, p), + new CK_ATTRIBUTE(CKA_SUBPRIME, q), + new CK_ATTRIBUTE(CKA_BASE, g), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PUBLIC_KEY, CKK_DSA, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.publicKey + (session, keyID, "DSA", p.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + private PrivateKey generatePrivate(BigInteger x, BigInteger p, + BigInteger q, BigInteger g) throws PKCS11Exception { + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA), + new CK_ATTRIBUTE(CKA_VALUE, x), + new CK_ATTRIBUTE(CKA_PRIME, p), + new CK_ATTRIBUTE(CKA_SUBPRIME, q), + new CK_ATTRIBUTE(CKA_BASE, g), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.privateKey + (session, keyID, "DSA", p.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + T implGetPublicKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (DSAPublicKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new DSAPublicKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + return keySpec.cast(spec); + } else { // X.509 handled in superclass + throw new InvalidKeySpecException("Only DSAPublicKeySpec and " + + "X509EncodedKeySpec supported for DSA public keys"); + } + } + + T implGetPrivateKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (DSAPrivateKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new DSAPrivateKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + return keySpec.cast(spec); + } else { // PKCS#8 handled in superclass + throw new InvalidKeySpecException("Only DSAPrivateKeySpec " + + "and PKCS8EncodedKeySpec supported for DSA private keys"); + } + } + + KeyFactory implGetSoftwareFactory() throws GeneralSecurityException { + return KeyFactory.getInstance("DSA", P11Util.getSunProvider()); + } + +} diff --git a/src/sun/security/pkcs11/P11Digest.java b/src/sun/security/pkcs11/P11Digest.java new file mode 100644 index 00000000..208b17e2 --- /dev/null +++ b/src/sun/security/pkcs11/P11Digest.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.nio.ByteBuffer; + +import java.security.*; + +import javax.crypto.SecretKey; + +import sun.nio.ch.DirectBuffer; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * MessageDigest implementation class. This class currently supports + * MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512. + * + * Note that many digest operations are on fairly small amounts of data + * (less than 100 bytes total). For example, the 2nd hashing in HMAC or + * the PRF in TLS. In order to speed those up, we use some buffering to + * minimize number of the Java->native transitions. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11Digest extends MessageDigestSpi implements Cloneable { + + /* fields initialized, no session acquired */ + private final static int S_BLANK = 1; + + /* data in buffer, session acquired, but digest not initialized */ + private final static int S_BUFFERED = 2; + + /* session initialized for digesting */ + private final static int S_INIT = 3; + + private final static int BUFFER_SIZE = 96; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id object + private final CK_MECHANISM mechanism; + + // length of the digest in bytes + private final int digestLength; + + // associated session, if any + private Session session; + + // current state, one of S_* above + private int state; + + // buffer to reduce number of JNI calls + private byte[] buffer; + + // offset into the buffer + private int bufOfs; + + P11Digest(Token token, String algorithm, long mechanism) { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = new CK_MECHANISM(mechanism); + switch ((int)mechanism) { + case (int)CKM_MD2: + case (int)CKM_MD5: + digestLength = 16; + break; + case (int)CKM_SHA_1: + digestLength = 20; + break; + case (int)CKM_SHA224: + digestLength = 28; + break; + case (int)CKM_SHA256: + digestLength = 32; + break; + case (int)CKM_SHA384: + digestLength = 48; + break; + case (int)CKM_SHA512: + digestLength = 64; + break; + default: + throw new ProviderException("Unknown mechanism: " + mechanism); + } + buffer = new byte[BUFFER_SIZE]; + state = S_BLANK; + } + + // see JCA spec + protected int engineGetDigestLength() { + return digestLength; + } + + private void fetchSession() { + token.ensureValid(); + if (state == S_BLANK) { + try { + session = token.getOpSession(); + state = S_BUFFERED; + } catch (PKCS11Exception e) { + throw new ProviderException("No more session available", e); + } + } + } + + // see JCA spec + protected void engineReset() { + token.ensureValid(); + + if (session != null) { + if (state == S_INIT && token.explicitCancel == true) { + session = token.killSession(session); + } else { + session = token.releaseSession(session); + } + } + state = S_BLANK; + bufOfs = 0; + } + + // see JCA spec + protected byte[] engineDigest() { + try { + byte[] digest = new byte[digestLength]; + int n = engineDigest(digest, 0, digestLength); + return digest; + } catch (DigestException e) { + throw new ProviderException("internal error", e); + } + } + + // see JCA spec + protected int engineDigest(byte[] digest, int ofs, int len) + throws DigestException { + if (len < digestLength) { + throw new DigestException("Length must be at least " + + digestLength); + } + + fetchSession(); + try { + int n; + if (state == S_BUFFERED) { + n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0, + bufOfs, digest, ofs, len); + bufOfs = 0; + } else { + if (bufOfs != 0) { + token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, + bufOfs); + bufOfs = 0; + } + n = token.p11.C_DigestFinal(session.id(), digest, ofs, len); + } + if (n != digestLength) { + throw new ProviderException("internal digest length error"); + } + return n; + } catch (PKCS11Exception e) { + throw new ProviderException("digest() failed", e); + } finally { + engineReset(); + } + } + + // see JCA spec + protected void engineUpdate(byte in) { + byte[] temp = { in }; + engineUpdate(temp, 0, 1); + } + + // see JCA spec + protected void engineUpdate(byte[] in, int ofs, int len) { + if (len <= 0) { + return; + } + + fetchSession(); + try { + if (state == S_BUFFERED) { + token.p11.C_DigestInit(session.id(), mechanism); + state = S_INIT; + } + if ((bufOfs != 0) && (bufOfs + len > buffer.length)) { + // process the buffered data + token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs); + bufOfs = 0; + } + if (bufOfs + len > buffer.length) { + // process the new data + token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len); + } else { + // buffer the new data + System.arraycopy(in, ofs, buffer, bufOfs, len); + bufOfs += len; + } + } catch (PKCS11Exception e) { + engineReset(); + throw new ProviderException("update() failed", e); + } + } + + // Called by SunJSSE via reflection during the SSL 3.0 handshake if + // the master secret is sensitive. We may want to consider making this + // method public in a future release. + protected void implUpdate(SecretKey key) throws InvalidKeyException { + + // SunJSSE calls this method only if the key does not have a RAW + // encoding, i.e. if it is sensitive. Therefore, no point in calling + // SecretKeyFactory to try to convert it. Just verify it ourselves. + if (key instanceof P11Key == false) { + throw new InvalidKeyException("Not a P11Key: " + key); + } + P11Key p11Key = (P11Key)key; + if (p11Key.token != token) { + throw new InvalidKeyException("Not a P11Key of this provider: " + + key); + } + + fetchSession(); + try { + if (state == S_BUFFERED) { + token.p11.C_DigestInit(session.id(), mechanism); + state = S_INIT; + } + + if (bufOfs != 0) { + token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs); + bufOfs = 0; + } + token.p11.C_DigestKey(session.id(), p11Key.keyID); + } catch (PKCS11Exception e) { + engineReset(); + throw new ProviderException("update(SecretKey) failed", e); + } + } + + // see JCA spec + protected void engineUpdate(ByteBuffer byteBuffer) { + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + + if (byteBuffer instanceof DirectBuffer == false) { + super.engineUpdate(byteBuffer); + return; + } + + fetchSession(); + long addr = ((DirectBuffer)byteBuffer).address(); + int ofs = byteBuffer.position(); + try { + if (state == S_BUFFERED) { + token.p11.C_DigestInit(session.id(), mechanism); + state = S_INIT; + } + if (bufOfs != 0) { + token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs); + bufOfs = 0; + } + token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len); + byteBuffer.position(ofs + len); + } catch (PKCS11Exception e) { + engineReset(); + throw new ProviderException("update() failed", e); + } + } + + public Object clone() throws CloneNotSupportedException { + P11Digest copy = (P11Digest) super.clone(); + copy.buffer = buffer.clone(); + try { + if (session != null) { + copy.session = copy.token.getOpSession(); + } + if (state == S_INIT) { + byte[] stateValues = + token.p11.C_GetOperationState(session.id()); + token.p11.C_SetOperationState(copy.session.id(), + stateValues, 0, 0); + } + } catch (PKCS11Exception e) { + throw (CloneNotSupportedException) + (new CloneNotSupportedException(algorithm).initCause(e)); + } + return copy; + } +} diff --git a/src/sun/security/pkcs11/P11ECDHKeyAgreement.java b/src/sun/security/pkcs11/P11ECDHKeyAgreement.java new file mode 100644 index 00000000..ade7673a --- /dev/null +++ b/src/sun/security/pkcs11/P11ECDHKeyAgreement.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyAgreement implementation for ECDH. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +final class P11ECDHKeyAgreement extends KeyAgreementSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private final long mechanism; + + // private key, if initialized + private P11Key privateKey; + + // encoded public point, non-null between doPhase() and generateSecret() only + private byte[] publicValue; + + // length of the secret to be derived + private int secretLen; + + P11ECDHKeyAgreement(Token token, String algorithm, long mechanism) { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + // see JCE spec + protected void engineInit(Key key, SecureRandom random) + throws InvalidKeyException { + if (key instanceof PrivateKey == false) { + throw new InvalidKeyException + ("Key must be instance of PrivateKey"); + } + privateKey = P11KeyFactory.convertKey(token, key, "EC"); + publicValue = null; + } + + // see JCE spec + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException + ("Parameters not supported"); + } + engineInit(key, random); + } + + // see JCE spec + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicValue != null) { + throw new IllegalStateException("Phase already executed"); + } + if (lastPhase == false) { + throw new IllegalStateException + ("Only two party agreement supported, lastPhase must be true"); + } + if (key instanceof ECPublicKey == false) { + throw new InvalidKeyException + ("Key must be a PublicKey with algorithm EC"); + } + ECPublicKey ecKey = (ECPublicKey)key; + int keyLenBits = ecKey.getParams().getCurve().getField().getFieldSize(); + secretLen = (keyLenBits + 7) >> 3; + publicValue = P11ECKeyFactory.getEncodedPublicValue(ecKey); + return null; + } + + // see JCE spec + protected byte[] engineGenerateSecret() throws IllegalStateException { + if ((privateKey == null) || (publicValue == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + Session session = null; + try { + session = token.getOpSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET), + }; + CK_ECDH1_DERIVE_PARAMS ckParams = + new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue); + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, ckParams), privateKey.keyID, + attributes); + attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE) + }; + token.p11.C_GetAttributeValue(session.id(), keyID, attributes); + byte[] secret = attributes[0].getByteArray(); + token.p11.C_DestroyObject(session.id(), keyID); + return secret; + } catch (PKCS11Exception e) { + throw new ProviderException("Could not derive key", e); + } finally { + publicValue = null; + token.releaseSession(session); + } + } + + // see JCE spec + protected int engineGenerateSecret(byte[] sharedSecret, int + offset) throws IllegalStateException, ShortBufferException { + if (offset + secretLen > sharedSecret.length) { + throw new ShortBufferException("Need " + secretLen + + " bytes, only " + (sharedSecret.length - offset) + " available"); + } + byte[] secret = engineGenerateSecret(); + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + // see JCE spec + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, + InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + if (algorithm.equals("TlsPremasterSecret") == false) { + throw new NoSuchAlgorithmException + ("Only supported for algorithm TlsPremasterSecret"); + } + return nativeGenerateSecret(algorithm); + } + + private SecretKey nativeGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, + InvalidKeyException { + if ((privateKey == null) || (publicValue == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + long keyType = CKK_GENERIC_SECRET; + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), + }; + CK_ECDH1_DERIVE_PARAMS ckParams = + new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue); + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, ckParams), privateKey.keyID, + attributes); + CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE_LEN), + }; + token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes); + int keyLen = (int)lenAttributes[0].getLong(); + SecretKey key = P11Key.secretKey + (session, keyID, algorithm, keyLen << 3, attributes); + return key; + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not derive key", e); + } finally { + publicValue = null; + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11ECKeyFactory.java b/src/sun/security/pkcs11/P11ECKeyFactory.java new file mode 100644 index 00000000..2cca0a6f --- /dev/null +++ b/src/sun/security/pkcs11/P11ECKeyFactory.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CLASS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EC_PARAMS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EC_POINT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_KEY_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_EC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PRIVATE_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PUBLIC_KEY; + +import sun.security.util.DerValue; +import sun.security.util.ECUtil; + +/** + * EC KeyFactory implementation. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +final class P11ECKeyFactory extends P11KeyFactory { + private static Provider sunECprovider; + + private static Provider getSunECProvider() { + if (sunECprovider == null) { + sunECprovider = Security.getProvider("SunEC"); + if (sunECprovider == null) { + throw new RuntimeException("Cannot load SunEC provider"); + } + } + + return sunECprovider; + } + + P11ECKeyFactory(Token token, String algorithm) { + super(token, algorithm); + } + + static ECParameterSpec getECParameterSpec(String name) { + return ECUtil.getECParameterSpec(getSunECProvider(), name); + } + + static ECParameterSpec getECParameterSpec(int keySize) { + return ECUtil.getECParameterSpec(getSunECProvider(), keySize); + } + + // Check that spec is a known supported curve and convert it to our + // ECParameterSpec subclass. If not possible, return null. + static ECParameterSpec getECParameterSpec(ECParameterSpec spec) { + return ECUtil.getECParameterSpec(getSunECProvider(), spec); + } + + static ECParameterSpec decodeParameters(byte[] params) throws IOException { + return ECUtil.getECParameterSpec(getSunECProvider(), params); + } + + static byte[] encodeParameters(ECParameterSpec params) { + return ECUtil.encodeECParameterSpec(getSunECProvider(), params); + } + + static ECPoint decodePoint(byte[] encoded, EllipticCurve curve) throws IOException { + return ECUtil.decodePoint(encoded, curve); + } + + // Used by ECDH KeyAgreement + static byte[] getEncodedPublicValue(PublicKey key) throws InvalidKeyException { + if (key instanceof ECPublicKey) { + ECPublicKey ecKey = (ECPublicKey)key; + ECPoint w = ecKey.getW(); + ECParameterSpec params = ecKey.getParams(); + return ECUtil.encodePoint(w, params.getCurve()); + } else { + // should never occur + throw new InvalidKeyException + ("Key class not yet supported: " + key.getClass().getName()); + } + } + + PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException { + try { + if (key instanceof ECPublicKey) { + ECPublicKey ecKey = (ECPublicKey)key; + return generatePublic( + ecKey.getW(), + ecKey.getParams() + ); + } else if ("X.509".equals(key.getFormat())) { + // let Sun provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + + try { + key = ECUtil.decodeX509ECPublicKey(encoded); + } catch (InvalidKeySpecException ikse) { + throw new InvalidKeyException(ikse); + } + + return implTranslatePublicKey(key); + } else { + throw new InvalidKeyException("PublicKey must be instance " + + "of ECPublicKey or have X.509 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create EC public key", e); + } + } + + PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException { + try { + if (key instanceof ECPrivateKey) { + ECPrivateKey ecKey = (ECPrivateKey)key; + return generatePrivate( + ecKey.getS(), + ecKey.getParams() + ); + } else if ("PKCS#8".equals(key.getFormat())) { + // let Sun provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + + try { + key = ECUtil.decodePKCS8ECPrivateKey(encoded); + } catch (InvalidKeySpecException ikse) { + throw new InvalidKeyException(ikse); + } + + return implTranslatePrivateKey(key); + } else { + throw new InvalidKeyException("PrivateKey must be instance " + + "of ECPrivateKey or have PKCS#8 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create EC private key", e); + } + } + + // see JCA spec + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof X509EncodedKeySpec) { + try { + byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded(); + PublicKey key = ECUtil.decodeX509ECPublicKey(encoded); + return implTranslatePublicKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException + ("Could not create EC public key", e); + } + } + if (keySpec instanceof ECPublicKeySpec == false) { + throw new InvalidKeySpecException("Only ECPublicKeySpec and " + + "X509EncodedKeySpec supported for EC public keys"); + } + try { + ECPublicKeySpec ec = (ECPublicKeySpec)keySpec; + return generatePublic( + ec.getW(), + ec.getParams() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create EC public key", e); + } + } + + // see JCA spec + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof PKCS8EncodedKeySpec) { + try { + byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + PrivateKey key = ECUtil.decodePKCS8ECPrivateKey(encoded); + return implTranslatePrivateKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException + ("Could not create EC private key", e); + } + } + if (keySpec instanceof ECPrivateKeySpec == false) { + throw new InvalidKeySpecException("Only ECPrivateKeySpec and " + + "PKCS8EncodedKeySpec supported for EC private keys"); + } + try { + ECPrivateKeySpec ec = (ECPrivateKeySpec)keySpec; + return generatePrivate( + ec.getS(), + ec.getParams() + ); + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException + ("Could not create EC private key", e); + } + } + + private PublicKey generatePublic(ECPoint point, ECParameterSpec params) + throws PKCS11Exception { + byte[] encodedParams = + ECUtil.encodeECParameterSpec(getSunECProvider(), params); + byte[] encodedPoint = + ECUtil.encodePoint(point, params.getCurve()); + + // Check whether the X9.63 encoding of an EC point shall be wrapped + // in an ASN.1 OCTET STRING + if (!token.config.getUseEcX963Encoding()) { + try { + encodedPoint = + new DerValue(DerValue.tag_OctetString, encodedPoint) + .toByteArray(); + } catch (IOException e) { + throw new + IllegalArgumentException("Could not DER encode point", e); + } + } + + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC), + new CK_ATTRIBUTE(CKA_EC_POINT, encodedPoint), + new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PUBLIC_KEY, CKK_EC, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.publicKey + (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes); + } finally { + token.releaseSession(session); + } + } + + private PrivateKey generatePrivate(BigInteger s, ECParameterSpec params) + throws PKCS11Exception { + byte[] encodedParams = + ECUtil.encodeECParameterSpec(getSunECProvider(), params); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC), + new CK_ATTRIBUTE(CKA_VALUE, s), + new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.privateKey + (session, keyID, "EC", params.getCurve().getField().getFieldSize(), attributes); + } finally { + token.releaseSession(session); + } + } + + T implGetPublicKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (ECPublicKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_EC_POINT), + new CK_ATTRIBUTE(CKA_EC_PARAMS), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + try { + ECParameterSpec params = decodeParameters(attributes[1].getByteArray()); + ECPoint point = decodePoint(attributes[0].getByteArray(), params.getCurve()); + return keySpec.cast(new ECPublicKeySpec(point, params)); + } catch (IOException e) { + throw new InvalidKeySpecException("Could not parse key", e); + } + } else { // X.509 handled in superclass + throw new InvalidKeySpecException("Only ECPublicKeySpec and " + + "X509EncodedKeySpec supported for EC public keys"); + } + } + + T implGetPrivateKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (ECPrivateKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_EC_PARAMS), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + try { + ECParameterSpec params = decodeParameters(attributes[1].getByteArray()); + return keySpec.cast( + new ECPrivateKeySpec(attributes[0].getBigInteger(), params)); + } catch (IOException e) { + throw new InvalidKeySpecException("Could not parse key", e); + } + } else { // PKCS#8 handled in superclass + throw new InvalidKeySpecException("Only ECPrivateKeySpec " + + "and PKCS8EncodedKeySpec supported for EC private keys"); + } + } + + KeyFactory implGetSoftwareFactory() throws GeneralSecurityException { + return KeyFactory.getInstance("EC", getSunECProvider()); + } + +} diff --git a/src/sun/security/pkcs11/P11Key.java b/src/sun/security/pkcs11/P11Key.java new file mode 100644 index 00000000..929d2a34 --- /dev/null +++ b/src/sun/security/pkcs11/P11Key.java @@ -0,0 +1,1156 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import java.lang.ref.*; +import java.math.BigInteger; +import java.util.*; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.interfaces.*; +import javax.crypto.spec.*; + +import sun.security.rsa.RSAUtil.KeyType; +import sun.security.rsa.RSAPublicKeyImpl; +import sun.security.rsa.RSAPrivateCrtKeyImpl; + +import sun.security.internal.interfaces.TlsMasterSecret; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +import sun.security.util.DerValue; +import sun.security.util.Length; +import sun.security.util.ECUtil; + +/** + * Key implementation classes. + * + * In PKCS#11, the components of private and secret keys may or may not + * be accessible. If they are, we use the algorithm specific key classes + * (e.g. DSAPrivateKey) for compatibility with existing applications. + * If the components are not accessible, we use a generic class that + * only implements PrivateKey (or SecretKey). Whether the components of a + * key are extractable is automatically determined when the key object is + * created. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +abstract class P11Key implements Key, Length { + + private static final long serialVersionUID = -2575874101938349339L; + + private final static String PUBLIC = "public"; + private final static String PRIVATE = "private"; + private final static String SECRET = "secret"; + + // type of key, one of (PUBLIC, PRIVATE, SECRET) + final String type; + + // token instance + final Token token; + + // algorithm name, returned by getAlgorithm(), etc. + final String algorithm; + + // key id + final long keyID; + + // effective key length of the key, e.g. 56 for a DES key + final int keyLength; + + // flags indicating whether the key is a token object, sensitive, extractable + final boolean tokenObject, sensitive, extractable; + + // phantom reference notification clean up for session keys + private final SessionKeyRef sessionKeyRef; + + P11Key(String type, Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + this.type = type; + this.token = session.token; + this.keyID = keyID; + this.algorithm = algorithm; + this.keyLength = keyLength; + boolean tokenObject = false; + boolean sensitive = false; + boolean extractable = true; + int n = (attributes == null) ? 0 : attributes.length; + for (int i = 0; i < n; i++) { + CK_ATTRIBUTE attr = attributes[i]; + if (attr.type == CKA_TOKEN) { + tokenObject = attr.getBoolean(); + } else if (attr.type == CKA_SENSITIVE) { + sensitive = attr.getBoolean(); + } else if (attr.type == CKA_EXTRACTABLE) { + extractable = attr.getBoolean(); + } + } + this.tokenObject = tokenObject; + this.sensitive = sensitive; + this.extractable = extractable; + if (tokenObject == false) { + sessionKeyRef = new SessionKeyRef(this, keyID, session); + } else { + sessionKeyRef = null; + } + } + + // see JCA spec + public final String getAlgorithm() { + token.ensureValid(); + return algorithm; + } + + // see JCA spec + public final byte[] getEncoded() { + byte[] b = getEncodedInternal(); + return (b == null) ? null : b.clone(); + } + + abstract byte[] getEncodedInternal(); + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (obj instanceof Key == false) { + return false; + } + String thisFormat = getFormat(); + if (thisFormat == null) { + // no encoding, key only equal to itself + // XXX getEncoded() for unextractable keys will change that + return false; + } + Key other = (Key)obj; + if (thisFormat.equals(other.getFormat()) == false) { + return false; + } + byte[] thisEnc = this.getEncodedInternal(); + byte[] otherEnc; + if (obj instanceof P11Key) { + otherEnc = ((P11Key)other).getEncodedInternal(); + } else { + otherEnc = other.getEncoded(); + } + return Arrays.equals(thisEnc, otherEnc); + } + + public int hashCode() { + // hashCode() should never throw exceptions + if (token.isValid() == false) { + return 0; + } + byte[] b1 = getEncodedInternal(); + if (b1 == null) { + return 0; + } + int r = b1.length; + for (int i = 0; i < b1.length; i++) { + r += (b1[i] & 0xff) * 37; + } + return r; + } + + protected Object writeReplace() throws ObjectStreamException { + KeyRep.Type type; + String format = getFormat(); + if (isPrivate() && "PKCS#8".equals(format)) { + type = KeyRep.Type.PRIVATE; + } else if (isPublic() && "X.509".equals(format)) { + type = KeyRep.Type.PUBLIC; + } else if (isSecret() && "RAW".equals(format)) { + type = KeyRep.Type.SECRET; + } else { + // XXX short term serialization for unextractable keys + throw new NotSerializableException + ("Cannot serialize sensitive and unextractable keys"); + } + return new KeyRep(type, getAlgorithm(), format, getEncoded()); + } + + public String toString() { + token.ensureValid(); + String s1 = token.provider.getName() + " " + algorithm + " " + type + + " key, " + keyLength + " bits"; + s1 += " (id " + keyID + ", " + + (tokenObject ? "token" : "session") + " object"; + if (isPublic()) { + s1 += ")"; + } else { + s1 += ", " + (sensitive ? "" : "not ") + "sensitive"; + s1 += ", " + (extractable ? "" : "un") + "extractable)"; + } + return s1; + } + + /** + * Return bit length of the key. + */ + @Override + public int length() { + return keyLength; + } + + boolean isPublic() { + return type == PUBLIC; + } + + boolean isPrivate() { + return type == PRIVATE; + } + + boolean isSecret() { + return type == SECRET; + } + + void fetchAttributes(CK_ATTRIBUTE[] attributes) { + Session tempSession = null; + try { + tempSession = token.getOpSession(); + token.p11.C_GetAttributeValue(tempSession.id(), keyID, attributes); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + token.releaseSession(tempSession); + } + } + + private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0]; + + private static CK_ATTRIBUTE[] getAttributes(Session session, long keyID, + CK_ATTRIBUTE[] knownAttributes, CK_ATTRIBUTE[] desiredAttributes) { + if (knownAttributes == null) { + knownAttributes = A0; + } + for (int i = 0; i < desiredAttributes.length; i++) { + // For each desired attribute, check to see if we have the value + // available already. If everything is here, we save a native call. + CK_ATTRIBUTE attr = desiredAttributes[i]; + for (CK_ATTRIBUTE known : knownAttributes) { + if ((attr.type == known.type) && (known.pValue != null)) { + attr.pValue = known.pValue; + break; // break inner for loop + } + } + if (attr.pValue == null) { + // nothing found, need to call C_GetAttributeValue() + for (int j = 0; j < i; j++) { + // clear values copied from knownAttributes + desiredAttributes[j].pValue = null; + } + try { + session.token.p11.C_GetAttributeValue + (session.id(), keyID, desiredAttributes); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } + break; // break loop, goto return + } + } + return desiredAttributes; + } + + static SecretKey secretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + return new P11SecretKey(session, keyID, algorithm, keyLength, attributes); + } + + static SecretKey masterSecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + return new P11TlsMasterSecretKey + (session, keyID, algorithm, keyLength, attributes, major, minor); + } + + // we assume that all components of public keys are always accessible + static PublicKey publicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + switch (algorithm) { + case "RSA": + return new P11RSAPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "DSA": + return new P11DSAPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "DH": + return new P11DHPublicKey + (session, keyID, algorithm, keyLength, attributes); + case "EC": + return new P11ECPublicKey + (session, keyID, algorithm, keyLength, attributes); + default: + throw new ProviderException + ("Unknown public key algorithm " + algorithm); + } + } + + static PrivateKey privateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + attributes = getAttributes(session, keyID, attributes, new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN), + new CK_ATTRIBUTE(CKA_SENSITIVE), + new CK_ATTRIBUTE(CKA_EXTRACTABLE), + }); + if (attributes[1].getBoolean() || (attributes[2].getBoolean() == false)) { + return new P11PrivateKey + (session, keyID, algorithm, keyLength, attributes); + } else { + switch (algorithm) { + case "RSA": + // XXX better test for RSA CRT keys (single getAttributes() call) + // we need to determine whether this is a CRT key + // see if we can obtain the public exponent + // this should also be readable for sensitive/extractable keys + CK_ATTRIBUTE[] attrs2 = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + }; + boolean crtKey; + try { + session.token.p11.C_GetAttributeValue + (session.id(), keyID, attrs2); + crtKey = (attrs2[0].pValue instanceof byte[]); + } catch (PKCS11Exception e) { + // ignore, assume not available + crtKey = false; + } + if (crtKey) { + return new P11RSAPrivateKey + (session, keyID, algorithm, keyLength, attributes); + } else { + return new P11RSAPrivateNonCRTKey + (session, keyID, algorithm, keyLength, attributes); + } + case "DSA": + return new P11DSAPrivateKey + (session, keyID, algorithm, keyLength, attributes); + case "DH": + return new P11DHPrivateKey + (session, keyID, algorithm, keyLength, attributes); + case "EC": + return new P11ECPrivateKey + (session, keyID, algorithm, keyLength, attributes); + default: + throw new ProviderException + ("Unknown private key algorithm " + algorithm); + } + } + } + + // class for sensitive and unextractable private keys + private static final class P11PrivateKey extends P11Key + implements PrivateKey { + private static final long serialVersionUID = -2138581185214187615L; + + P11PrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + // XXX temporary encoding for serialization purposes + public String getFormat() { + token.ensureValid(); + return null; + } + byte[] getEncodedInternal() { + token.ensureValid(); + return null; + } + } + + private static class P11SecretKey extends P11Key implements SecretKey { + private static final long serialVersionUID = -7828241727014329084L; + private volatile byte[] encoded; + P11SecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(SECRET, session, keyID, algorithm, keyLength, attributes); + } + public String getFormat() { + token.ensureValid(); + if (sensitive || (extractable == false)) { + return null; + } else { + return "RAW"; + } + } + byte[] getEncodedInternal() { + token.ensureValid(); + if (getFormat() == null) { + return null; + } + byte[] b = encoded; + if (b == null) { + synchronized (this) { + b = encoded; + if (b == null) { + Session tempSession = null; + try { + tempSession = token.getOpSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + }; + token.p11.C_GetAttributeValue + (tempSession.id(), keyID, attributes); + b = attributes[0].getByteArray(); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + token.releaseSession(tempSession); + } + encoded = b; + } + } + } + return b; + } + } + + private static class P11TlsMasterSecretKey extends P11SecretKey + implements TlsMasterSecret { + private static final long serialVersionUID = -1318560923770573441L; + + private final int majorVersion, minorVersion; + P11TlsMasterSecretKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes, int major, int minor) { + super(session, keyID, algorithm, keyLength, attributes); + this.majorVersion = major; + this.minorVersion = minor; + } + public int getMajorVersion() { + return majorVersion; + } + + public int getMinorVersion() { + return minorVersion; + } + } + + // RSA CRT private key + private static final class P11RSAPrivateKey extends P11Key + implements RSAPrivateCrtKey { + private static final long serialVersionUID = 9215872438913515220L; + + private BigInteger n, e, d, p, q, pe, qe, coeff; + private byte[] encoded; + P11RSAPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + new CK_ATTRIBUTE(CKA_PRIME_1), + new CK_ATTRIBUTE(CKA_PRIME_2), + new CK_ATTRIBUTE(CKA_EXPONENT_1), + new CK_ATTRIBUTE(CKA_EXPONENT_2), + new CK_ATTRIBUTE(CKA_COEFFICIENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + e = attributes[1].getBigInteger(); + d = attributes[2].getBigInteger(); + p = attributes[3].getBigInteger(); + q = attributes[4].getBigInteger(); + pe = attributes[5].getBigInteger(); + qe = attributes[6].getBigInteger(); + coeff = attributes[7].getBigInteger(); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key newKey = RSAPrivateCrtKeyImpl.newKey + (KeyType.RSA, null, n, e, d, p, q, pe, qe, coeff); + encoded = newKey.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPublicExponent() { + fetchValues(); + return e; + } + public BigInteger getPrivateExponent() { + fetchValues(); + return d; + } + public BigInteger getPrimeP() { + fetchValues(); + return p; + } + public BigInteger getPrimeQ() { + fetchValues(); + return q; + } + public BigInteger getPrimeExponentP() { + fetchValues(); + return pe; + } + public BigInteger getPrimeExponentQ() { + fetchValues(); + return qe; + } + public BigInteger getCrtCoefficient() { + fetchValues(); + return coeff; + } + } + + // RSA non-CRT private key + private static final class P11RSAPrivateNonCRTKey extends P11Key + implements RSAPrivateKey { + private static final long serialVersionUID = 1137764983777411481L; + + private BigInteger n, d; + private byte[] encoded; + P11RSAPrivateNonCRTKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + d = attributes[1].getBigInteger(); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + // XXX make constructor in SunRsaSign provider public + // and call it directly + KeyFactory factory = KeyFactory.getInstance + ("RSA", P11Util.getSunRsaSignProvider()); + Key newKey = factory.translateKey(this); + encoded = newKey.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPrivateExponent() { + fetchValues(); + return d; + } + } + + private static final class P11RSAPublicKey extends P11Key + implements RSAPublicKey { + private static final long serialVersionUID = -826726289023854455L; + private BigInteger n, e; + private byte[] encoded; + P11RSAPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (n != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + }; + fetchAttributes(attributes); + n = attributes[0].getBigInteger(); + e = attributes[1].getBigInteger(); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + encoded = RSAPublicKeyImpl.newKey + (KeyType.RSA, null, n, e).getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getModulus() { + fetchValues(); + return n; + } + public BigInteger getPublicExponent() { + fetchValues(); + return e; + } + public String toString() { + fetchValues(); + return super.toString() + "\n modulus: " + n + + "\n public exponent: " + e; + } + } + + private static final class P11DSAPublicKey extends P11Key + implements DSAPublicKey { + private static final long serialVersionUID = 5989753793316396637L; + + private BigInteger y; + private DSAParams params; + private byte[] encoded; + P11DSAPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (y != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + y = attributes[0].getBigInteger(); + params = new DSAParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = new sun.security.provider.DSAPublicKey + (y, params.getP(), params.getQ(), params.getG()); + encoded = key.getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getY() { + fetchValues(); + return y; + } + public DSAParams getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + "\n y: " + y + "\n p: " + params.getP() + + "\n q: " + params.getQ() + "\n g: " + params.getG(); + } + } + + private static final class P11DSAPrivateKey extends P11Key + implements DSAPrivateKey { + private static final long serialVersionUID = 3119629997181999389L; + + private BigInteger x; + private DSAParams params; + private byte[] encoded; + P11DSAPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (x != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_SUBPRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + x = attributes[0].getBigInteger(); + params = new DSAParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = new sun.security.provider.DSAPrivateKey + (x, params.getP(), params.getQ(), params.getG()); + encoded = key.getEncoded(); + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getX() { + fetchValues(); + return x; + } + public DSAParams getParams() { + fetchValues(); + return params; + } + } + + private static final class P11DHPrivateKey extends P11Key + implements DHPrivateKey { + private static final long serialVersionUID = -1698576167364928838L; + + private BigInteger x; + private DHParameterSpec params; + private byte[] encoded; + P11DHPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (x != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + x = attributes[0].getBigInteger(); + params = new DHParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + DHPrivateKeySpec spec = new DHPrivateKeySpec + (x, params.getP(), params.getG()); + KeyFactory kf = KeyFactory.getInstance + ("DH", P11Util.getSunJceProvider()); + Key key = kf.generatePrivate(spec); + encoded = key.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getX() { + fetchValues(); + return x; + } + public DHParameterSpec getParams() { + fetchValues(); + return params; + } + public int hashCode() { + if (token.isValid() == false) { + return 0; + } + fetchValues(); + return Objects.hash(x, params.getP(), params.getG()); + } + public boolean equals(Object obj) { + if (this == obj) return true; + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (!(obj instanceof DHPrivateKey)) { + return false; + } + fetchValues(); + DHPrivateKey other = (DHPrivateKey) obj; + DHParameterSpec otherParams = other.getParams(); + return ((this.x.compareTo(other.getX()) == 0) && + (this.params.getP().compareTo(otherParams.getP()) == 0) && + (this.params.getG().compareTo(otherParams.getG()) == 0)); + } + } + + private static final class P11DHPublicKey extends P11Key + implements DHPublicKey { + static final long serialVersionUID = -598383872153843657L; + + private BigInteger y; + private DHParameterSpec params; + private byte[] encoded; + P11DHPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (y != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_PRIME), + new CK_ATTRIBUTE(CKA_BASE), + }; + fetchAttributes(attributes); + y = attributes[0].getBigInteger(); + params = new DHParameterSpec( + attributes[1].getBigInteger(), + attributes[2].getBigInteger() + ); + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + DHPublicKeySpec spec = new DHPublicKeySpec + (y, params.getP(), params.getG()); + KeyFactory kf = KeyFactory.getInstance + ("DH", P11Util.getSunJceProvider()); + Key key = kf.generatePublic(spec); + encoded = key.getEncoded(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getY() { + fetchValues(); + return y; + } + public DHParameterSpec getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + "\n y: " + y + "\n p: " + params.getP() + + "\n g: " + params.getG(); + } + public int hashCode() { + if (token.isValid() == false) { + return 0; + } + fetchValues(); + return Objects.hash(y, params.getP(), params.getG()); + } + public boolean equals(Object obj) { + if (this == obj) return true; + // equals() should never throw exceptions + if (token.isValid() == false) { + return false; + } + if (!(obj instanceof DHPublicKey)) { + return false; + } + fetchValues(); + DHPublicKey other = (DHPublicKey) obj; + DHParameterSpec otherParams = other.getParams(); + return ((this.y.compareTo(other.getY()) == 0) && + (this.params.getP().compareTo(otherParams.getP()) == 0) && + (this.params.getG().compareTo(otherParams.getG()) == 0)); + } + } + + private static final class P11ECPrivateKey extends P11Key + implements ECPrivateKey { + private static final long serialVersionUID = -7786054399510515515L; + + private BigInteger s; + private ECParameterSpec params; + private byte[] encoded; + P11ECPrivateKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PRIVATE, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (s != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE), + new CK_ATTRIBUTE(CKA_EC_PARAMS, params), + }; + fetchAttributes(attributes); + s = attributes[0].getBigInteger(); + try { + params = P11ECKeyFactory.decodeParameters + (attributes[1].getByteArray()); + } catch (Exception e) { + throw new RuntimeException("Could not parse key values", e); + } + } + public String getFormat() { + token.ensureValid(); + return "PKCS#8"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + Key key = ECUtil.generateECPrivateKey(s, params); + encoded = key.getEncoded(); + } catch (InvalidKeySpecException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public BigInteger getS() { + fetchValues(); + return s; + } + public ECParameterSpec getParams() { + fetchValues(); + return params; + } + } + + private static final class P11ECPublicKey extends P11Key + implements ECPublicKey { + private static final long serialVersionUID = -6371481375154806089L; + + private ECPoint w; + private ECParameterSpec params; + private byte[] encoded; + P11ECPublicKey(Session session, long keyID, String algorithm, + int keyLength, CK_ATTRIBUTE[] attributes) { + super(PUBLIC, session, keyID, algorithm, keyLength, attributes); + } + private synchronized void fetchValues() { + token.ensureValid(); + if (w != null) { + return; + } + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_EC_POINT), + new CK_ATTRIBUTE(CKA_EC_PARAMS), + }; + fetchAttributes(attributes); + + try { + params = P11ECKeyFactory.decodeParameters + (attributes[1].getByteArray()); + byte[] ecKey = attributes[0].getByteArray(); + + // Check whether the X9.63 encoding of an EC point is wrapped + // in an ASN.1 OCTET STRING + if (!token.config.getUseEcX963Encoding()) { + DerValue wECPoint = new DerValue(ecKey); + + if (wECPoint.getTag() != DerValue.tag_OctetString) { + throw new IOException("Could not DER decode EC point." + + " Unexpected tag: " + wECPoint.getTag()); + } + w = P11ECKeyFactory.decodePoint + (wECPoint.getDataBytes(), params.getCurve()); + + } else { + w = P11ECKeyFactory.decodePoint(ecKey, params.getCurve()); + } + + } catch (Exception e) { + throw new RuntimeException("Could not parse key values", e); + } + } + public String getFormat() { + token.ensureValid(); + return "X.509"; + } + synchronized byte[] getEncodedInternal() { + token.ensureValid(); + if (encoded == null) { + fetchValues(); + try { + return ECUtil.x509EncodeECPublicKey(w, params); + } catch (InvalidKeySpecException e) { + throw new ProviderException(e); + } + } + return encoded; + } + public ECPoint getW() { + fetchValues(); + return w; + } + public ECParameterSpec getParams() { + fetchValues(); + return params; + } + public String toString() { + fetchValues(); + return super.toString() + + "\n public x coord: " + w.getAffineX() + + "\n public y coord: " + w.getAffineY() + + "\n parameters: " + params; + } + } +} + +/* + * NOTE: Must use PhantomReference here and not WeakReference + * otherwise the key maybe cleared before other objects which + * still use these keys during finalization such as SSLSocket. + */ +final class SessionKeyRef extends PhantomReference + implements Comparable { + private static ReferenceQueue refQueue = + new ReferenceQueue(); + private static Set refList = + Collections.synchronizedSortedSet(new TreeSet()); + + static ReferenceQueue referenceQueue() { + return refQueue; + } + + private static void drainRefQueueBounded() { + while (true) { + SessionKeyRef next = (SessionKeyRef) refQueue.poll(); + if (next == null) break; + next.dispose(); + } + } + + // handle to the native key + private long keyID; + private Session session; + + SessionKeyRef(P11Key key , long keyID, Session session) { + super(key, refQueue); + this.keyID = keyID; + this.session = session; + this.session.addObject(); + refList.add(this); + // TBD: run at some interval and not every time? + drainRefQueueBounded(); + } + + private void dispose() { + refList.remove(this); + if (session.token.isValid()) { + Session newSession = null; + try { + newSession = session.token.getOpSession(); + session.token.p11.C_DestroyObject(newSession.id(), keyID); + } catch (PKCS11Exception e) { + // ignore + } finally { + this.clear(); + session.token.releaseSession(newSession); + session.removeObject(); + } + } + } + + public int compareTo(SessionKeyRef other) { + if (this.keyID == other.keyID) { + return 0; + } else { + return (this.keyID < other.keyID) ? -1 : 1; + } + } +} diff --git a/src/sun/security/pkcs11/P11KeyAgreement.java b/src/sun/security/pkcs11/P11KeyAgreement.java new file mode 100644 index 00000000..c0a4a341 --- /dev/null +++ b/src/sun/security/pkcs11/P11KeyAgreement.java @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.interfaces.*; +import javax.crypto.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.util.KeyUtil; + +/** + * KeyAgreement implementation class. This class currently supports + * DH. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11KeyAgreement extends KeyAgreementSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private final long mechanism; + + // private key, if initialized + private P11Key privateKey; + + // other sides public value ("y"), if doPhase() already called + private BigInteger publicValue; + + // length of the secret to be derived + private int secretLen; + + // KeyAgreement from SunJCE as fallback for > 2 party agreement + private KeyAgreement multiPartyAgreement; + + P11KeyAgreement(Token token, String algorithm, long mechanism) { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + // see JCE spec + protected void engineInit(Key key, SecureRandom random) + throws InvalidKeyException { + if (key instanceof PrivateKey == false) { + throw new InvalidKeyException + ("Key must be instance of PrivateKey"); + } + privateKey = P11KeyFactory.convertKey(token, key, algorithm); + publicValue = null; + multiPartyAgreement = null; + } + + // see JCE spec + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException + ("Parameters not supported"); + } + engineInit(key, random); + } + + // see JCE spec + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicValue != null) { + throw new IllegalStateException("Phase already executed"); + } + // PKCS#11 only allows key agreement between 2 parties + // JCE allows >= 2 parties. To support that case (for compatibility + // and to pass JCK), fall back to SunJCE in this case. + // NOTE that we initialize using the P11Key, which will fail if it + // is sensitive/unextractable. However, this is not an issue in the + // compatibility configuration, which is all we are targeting here. + if ((multiPartyAgreement != null) || (lastPhase == false)) { + if (multiPartyAgreement == null) { + try { + multiPartyAgreement = KeyAgreement.getInstance + ("DH", P11Util.getSunJceProvider()); + multiPartyAgreement.init(privateKey); + } catch (NoSuchAlgorithmException e) { + throw new InvalidKeyException + ("Could not initialize multi party agreement", e); + } + } + return multiPartyAgreement.doPhase(key, lastPhase); + } + if ((key instanceof PublicKey == false) + || (key.getAlgorithm().equals(algorithm) == false)) { + throw new InvalidKeyException + ("Key must be a PublicKey with algorithm DH"); + } + BigInteger p, g, y; + if (key instanceof DHPublicKey) { + DHPublicKey dhKey = (DHPublicKey)key; + + // validate the Diffie-Hellman public key + KeyUtil.validate(dhKey); + + y = dhKey.getY(); + DHParameterSpec params = dhKey.getParams(); + p = params.getP(); + g = params.getG(); + } else { + // normally, DH PublicKeys will always implement DHPublicKey + // just in case not, attempt conversion + P11DHKeyFactory kf = new P11DHKeyFactory(token, "DH"); + try { + DHPublicKeySpec spec = kf.engineGetKeySpec( + key, DHPublicKeySpec.class); + + // validate the Diffie-Hellman public key + KeyUtil.validate(spec); + + y = spec.getY(); + p = spec.getP(); + g = spec.getG(); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Could not obtain key values", e); + } + } + // if parameters of private key are accessible, verify that + // they match parameters of public key + // XXX p and g should always be readable, even if the key is sensitive + if (privateKey instanceof DHPrivateKey) { + DHPrivateKey dhKey = (DHPrivateKey)privateKey; + DHParameterSpec params = dhKey.getParams(); + if ((p.equals(params.getP()) == false) + || (g.equals(params.getG()) == false)) { + throw new InvalidKeyException + ("PublicKey DH parameters must match PrivateKey DH parameters"); + } + } + publicValue = y; + // length of the secret is length of key + secretLen = (p.bitLength() + 7) >> 3; + return null; + } + + // see JCE spec + protected byte[] engineGenerateSecret() throws IllegalStateException { + if (multiPartyAgreement != null) { + byte[] val = multiPartyAgreement.generateSecret(); + multiPartyAgreement = null; + return val; + } + if ((privateKey == null) || (publicValue == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + Session session = null; + try { + session = token.getOpSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET), + }; + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, + attributes); + attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE) + }; + token.p11.C_GetAttributeValue(session.id(), keyID, attributes); + byte[] secret = attributes[0].getByteArray(); + token.p11.C_DestroyObject(session.id(), keyID); + // Some vendors, e.g. NSS, trim off the leading 0x00 byte(s) from + // the generated secret. Thus, we need to check the secret length + // and trim/pad it so the returned value has the same length as + // the modulus size + if (secret.length == secretLen) { + return secret; + } else { + if (secret.length > secretLen) { + // Shouldn't happen; but check just in case + throw new ProviderException("generated secret is out-of-range"); + } + byte[] newSecret = new byte[secretLen]; + System.arraycopy(secret, 0, newSecret, secretLen - secret.length, + secret.length); + return newSecret; + } + } catch (PKCS11Exception e) { + throw new ProviderException("Could not derive key", e); + } finally { + publicValue = null; + token.releaseSession(session); + } + } + + // see JCE spec + protected int engineGenerateSecret(byte[] sharedSecret, int + offset) throws IllegalStateException, ShortBufferException { + if (multiPartyAgreement != null) { + int n = multiPartyAgreement.generateSecret(sharedSecret, offset); + multiPartyAgreement = null; + return n; + } + if (offset + secretLen > sharedSecret.length) { + throw new ShortBufferException("Need " + secretLen + + " bytes, only " + (sharedSecret.length - offset) + " available"); + } + byte[] secret = engineGenerateSecret(); + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + // see JCE spec + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, + InvalidKeyException { + if (multiPartyAgreement != null) { + SecretKey key = multiPartyAgreement.generateSecret(algorithm); + multiPartyAgreement = null; + return key; + } + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + if (algorithm.equals("TlsPremasterSecret")) { + // For now, only perform native derivation for TlsPremasterSecret + // as that is required for FIPS compliance. + // For other algorithms, there are unresolved issues regarding + // how this should work in JCE plus a Solaris truncation bug. + // (bug not yet filed). + return nativeGenerateSecret(algorithm); + } + byte[] secret = engineGenerateSecret(); + // Maintain compatibility for SunJCE: + // verify secret length is sensible for algorithm / truncate + // return generated key itself if possible + int keyLen; + if (algorithm.equalsIgnoreCase("DES")) { + keyLen = 8; + } else if (algorithm.equalsIgnoreCase("DESede")) { + keyLen = 24; + } else if (algorithm.equalsIgnoreCase("Blowfish")) { + keyLen = Math.min(56, secret.length); + } else if (algorithm.equalsIgnoreCase("TlsPremasterSecret")) { + keyLen = secret.length; + } else { + throw new NoSuchAlgorithmException + ("Unknown algorithm " + algorithm); + } + if (secret.length < keyLen) { + throw new InvalidKeyException("Secret too short"); + } + if (algorithm.equalsIgnoreCase("DES") || + algorithm.equalsIgnoreCase("DESede")) { + for (int i = 0; i < keyLen; i+=8) { + P11SecretKeyFactory.fixDESParity(secret, i); + } + } + return new SecretKeySpec(secret, 0, keyLen, algorithm); + } + + private SecretKey nativeGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, + InvalidKeyException { + if ((privateKey == null) || (publicValue == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + long keyType = CKK_GENERIC_SECRET; + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), + }; + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, publicValue), privateKey.keyID, + attributes); + CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE_LEN), + }; + token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes); + int keyLen = (int)lenAttributes[0].getLong(); + SecretKey key = P11Key.secretKey + (session, keyID, algorithm, keyLen << 3, attributes); + if ("RAW".equals(key.getFormat())) { + // Workaround for Solaris bug 6318543. + // Strip leading zeroes ourselves if possible (key not sensitive). + // This should be removed once the Solaris fix is available + // as here we always retrieve the CKA_VALUE even for tokens + // that do not have that bug. + byte[] keyBytes = key.getEncoded(); + byte[] newBytes = KeyUtil.trimZeroes(keyBytes); + if (keyBytes != newBytes) { + key = new SecretKeySpec(newBytes, algorithm); + } + } + return key; + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not derive key", e); + } finally { + publicValue = null; + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11KeyFactory.java b/src/sun/security/pkcs11/P11KeyFactory.java new file mode 100644 index 00000000..0442203e --- /dev/null +++ b/src/sun/security/pkcs11/P11KeyFactory.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.*; + +import sun.security.pkcs11.wrapper.PKCS11Exception; + +/** + * KeyFactory base class. Provides common infrastructure for the RSA, DSA, + * and DH implementations. + * + * The subclasses support conversion between keys and keyspecs + * using X.509, PKCS#8, and their individual algorithm specific formats, + * assuming keys are extractable. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +abstract class P11KeyFactory extends KeyFactorySpi { + + // token instance + final Token token; + + // algorithm name, currently one of RSA, DSA, DH + final String algorithm; + + P11KeyFactory(Token token, String algorithm) { + super(); + this.token = token; + this.algorithm = algorithm; + } + + /** + * Convert an arbitrary key of algorithm into a P11Key of token. + * Used by P11Signature.init() and RSACipher.init(). + */ + static P11Key convertKey(Token token, Key key, String algorithm) + throws InvalidKeyException { + return (P11Key)token.getKeyFactory(algorithm).engineTranslateKey(key); + } + + // see JCA spec + protected final T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if ((key == null) || (keySpec == null)) { + throw new InvalidKeySpecException + ("key and keySpec must not be null"); + } + // delegate to our Java based providers for PKCS#8 and X.509 + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec) + || X509EncodedKeySpec.class.isAssignableFrom(keySpec)) { + try { + return implGetSoftwareFactory().getKeySpec(key, keySpec); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException("Could not encode key", e); + } + } + // first translate into a key of this token, if it is not already + P11Key p11Key; + try { + p11Key = (P11Key)engineTranslateKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException("Could not convert key", e); + } + Session[] session = new Session[1]; + try { + if (p11Key.isPublic()) { + return implGetPublicKeySpec(p11Key, keySpec, session); + } else { + return implGetPrivateKeySpec(p11Key, keySpec, session); + } + } catch (PKCS11Exception e) { + throw new InvalidKeySpecException("Could not generate KeySpec", e); + } finally { + session[0] = token.releaseSession(session[0]); + } + } + + // see JCA spec + protected final Key engineTranslateKey(Key key) throws InvalidKeyException { + token.ensureValid(); + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + if (key.getAlgorithm().equals(this.algorithm) == false) { + throw new InvalidKeyException + ("Key algorithm must be " + algorithm); + } + if (key instanceof P11Key) { + P11Key p11Key = (P11Key)key; + if (p11Key.token == token) { + // already a key of this token, no need to translate + return key; + } + } + P11Key p11Key = token.privateCache.get(key); + if (p11Key != null) { + return p11Key; + } + if (key instanceof PublicKey) { + PublicKey publicKey = implTranslatePublicKey((PublicKey)key); + token.privateCache.put(key, (P11Key)publicKey); + return publicKey; + } else if (key instanceof PrivateKey) { + PrivateKey privateKey = implTranslatePrivateKey((PrivateKey)key); + token.privateCache.put(key, (P11Key)privateKey); + return privateKey; + } else { + throw new InvalidKeyException + ("Key must be instance of PublicKey or PrivateKey"); + } + } + + abstract T implGetPublicKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException; + + abstract T implGetPrivateKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException; + + abstract PublicKey implTranslatePublicKey(PublicKey key) + throws InvalidKeyException; + + abstract PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException; + + abstract KeyFactory implGetSoftwareFactory() throws GeneralSecurityException; + +} diff --git a/src/sun/security/pkcs11/P11KeyGenerator.java b/src/sun/security/pkcs11/P11KeyGenerator.java new file mode 100644 index 00000000..44252d9f --- /dev/null +++ b/src/sun/security/pkcs11/P11KeyGenerator.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyGenerator implementation class. This class currently supports + * DES, DESede, AES, ARCFOUR, and Blowfish. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11KeyGenerator extends KeyGeneratorSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private long mechanism; + + // raw key size in bits, e.g. 64 for DES. Always valid. + private int keySize; + + // bits of entropy in the key, e.g. 56 for DES. Always valid. + private int significantKeySize; + + // keyType (CKK_*), needed for TemplateManager call only. + private long keyType; + + // for determining if both 112 and 168 bits of DESede key lengths + // are supported. + private boolean supportBothKeySizes; + + /** + * Utility method for checking if the specified key size is valid + * and within the supported range. Return the significant key size + * upon successful validation. + * @param keyGenMech the PKCS#11 key generation mechanism. + * @param keySize the to-be-checked key size for this mechanism. + * @param token token which provides this mechanism. + * @return the significant key size (in bits) corresponding to the + * specified key size. + * @throws InvalidParameterException if the specified key size is invalid. + * @throws ProviderException if this mechanism isn't supported by SunPKCS11 + * or underlying native impl. + */ + static int checkKeySize(long keyGenMech, int keySize, Token token) + throws InvalidAlgorithmParameterException, ProviderException { + int sigKeySize; + switch ((int)keyGenMech) { + case (int)CKM_DES_KEY_GEN: + if ((keySize != 64) && (keySize != 56)) { + throw new InvalidAlgorithmParameterException + ("DES key length must be 56 bits"); + } + sigKeySize = 56; + break; + case (int)CKM_DES2_KEY_GEN: + case (int)CKM_DES3_KEY_GEN: + if ((keySize == 112) || (keySize == 128)) { + sigKeySize = 112; + } else if ((keySize == 168) || (keySize == 192)) { + sigKeySize = 168; + } else { + throw new InvalidAlgorithmParameterException + ("DESede key length must be 112, or 168 bits"); + } + break; + default: + // Handle all variable-key-length algorithms here + CK_MECHANISM_INFO info = null; + try { + info = token.getMechanismInfo(keyGenMech); + } catch (PKCS11Exception p11e) { + // Should never happen + throw new ProviderException + ("Cannot retrieve mechanism info", p11e); + } + if (info == null) { + // XXX Unable to retrieve the supported key length from + // the underlying native impl. Skip the checking for now. + return keySize; + } + // PKCS#11 defines these to be in number of bytes except for + // RC4 which is in bits. However, some PKCS#11 impls still use + // bytes for all mechs, e.g. NSS. We try to detect this + // inconsistency if the minKeySize seems unreasonably small. + int minKeySize = (int)info.ulMinKeySize; + int maxKeySize = (int)info.ulMaxKeySize; + if (keyGenMech != CKM_RC4_KEY_GEN || minKeySize < 8) { + minKeySize = (int)info.ulMinKeySize << 3; + maxKeySize = (int)info.ulMaxKeySize << 3; + } + // Explicitly disallow keys shorter than 40-bits for security + if (minKeySize < 40) minKeySize = 40; + if (keySize < minKeySize || keySize > maxKeySize) { + throw new InvalidAlgorithmParameterException + ("Key length must be between " + minKeySize + + " and " + maxKeySize + " bits"); + } + if (keyGenMech == CKM_AES_KEY_GEN) { + if ((keySize != 128) && (keySize != 192) && + (keySize != 256)) { + throw new InvalidAlgorithmParameterException + ("AES key length must be " + minKeySize + + (maxKeySize >= 192? ", 192":"") + + (maxKeySize >= 256? ", or 256":"") + " bits"); + } + } + sigKeySize = keySize; + } + return sigKeySize; + } + + P11KeyGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + + if (this.mechanism == CKM_DES3_KEY_GEN) { + /* Given the current lookup order specified in SunPKCS11.java, + if CKM_DES2_KEY_GEN is used to construct this object, it + means that CKM_DES3_KEY_GEN is disabled or unsupported. + */ + supportBothKeySizes = + (token.provider.config.isEnabled(CKM_DES2_KEY_GEN) && + (token.getMechanismInfo(CKM_DES2_KEY_GEN) != null)); + } + setDefaultKeySize(); + } + + // set default keysize and also initialize keyType + private void setDefaultKeySize() { + switch ((int)mechanism) { + case (int)CKM_DES_KEY_GEN: + keySize = 64; + keyType = CKK_DES; + break; + case (int)CKM_DES2_KEY_GEN: + keySize = 128; + keyType = CKK_DES2; + break; + case (int)CKM_DES3_KEY_GEN: + keySize = 192; + keyType = CKK_DES3; + break; + case (int)CKM_AES_KEY_GEN: + keySize = 128; + keyType = CKK_AES; + break; + case (int)CKM_RC4_KEY_GEN: + keySize = 128; + keyType = CKK_RC4; + break; + case (int)CKM_BLOWFISH_KEY_GEN: + keySize = 128; + keyType = CKK_BLOWFISH; + break; + default: + throw new ProviderException("Unknown mechanism " + mechanism); + } + try { + significantKeySize = checkKeySize(mechanism, keySize, token); + } catch (InvalidAlgorithmParameterException iape) { + throw new ProviderException("Unsupported default key size", iape); + } + } + + // see JCE spec + protected void engineInit(SecureRandom random) { + token.ensureValid(); + setDefaultKeySize(); + } + + // see JCE spec + protected void engineInit(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException + ("AlgorithmParameterSpec not supported"); + } + + // see JCE spec + protected void engineInit(int keySize, SecureRandom random) { + token.ensureValid(); + int newSignificantKeySize; + try { + newSignificantKeySize = checkKeySize(mechanism, keySize, token); + } catch (InvalidAlgorithmParameterException iape) { + throw (InvalidParameterException) + (new InvalidParameterException().initCause(iape)); + } + if ((mechanism == CKM_DES2_KEY_GEN) || + (mechanism == CKM_DES3_KEY_GEN)) { + long newMechanism = (newSignificantKeySize == 112 ? + CKM_DES2_KEY_GEN : CKM_DES3_KEY_GEN); + if (mechanism != newMechanism) { + if (supportBothKeySizes) { + mechanism = newMechanism; + // Adjust keyType to reflect the mechanism change + keyType = (mechanism == CKM_DES2_KEY_GEN ? + CKK_DES2 : CKK_DES3); + } else { + throw new InvalidParameterException + ("Only " + significantKeySize + + "-bit DESede is supported"); + } + } + } + this.keySize = keySize; + this.significantKeySize = newSignificantKeySize; + } + + // see JCE spec + protected SecretKey engineGenerateKey() { + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes; + switch ((int)keyType) { + case (int)CKK_DES: + case (int)CKK_DES2: + case (int)CKK_DES3: + // fixed length, do not specify CKA_VALUE_LEN + attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + }; + break; + default: + attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_VALUE_LEN, keySize >> 3), + }; + break; + } + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); + long keyID = token.p11.C_GenerateKey + (session.id(), new CK_MECHANISM(mechanism), attributes); + return P11Key.secretKey + (session, keyID, algorithm, significantKeySize, attributes); + } catch (PKCS11Exception e) { + throw new ProviderException("Could not generate key", e); + } finally { + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11KeyPairGenerator.java b/src/sun/security/pkcs11/P11KeyPairGenerator.java new file mode 100644 index 00000000..4d9f3385 --- /dev/null +++ b/src/sun/security/pkcs11/P11KeyPairGenerator.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.spec.DHParameterSpec; + +import sun.security.provider.ParameterCache; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_BASE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EC_PARAMS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_MODULUS_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PUBLIC_EXPONENT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SUBPRIME; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_EC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PRIVATE_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PUBLIC_KEY; + +import sun.security.rsa.RSAKeyFactory; + +/** + * KeyPairGenerator implementation class. This class currently supports + * RSA, DSA, DH, and EC. + * + * Note that for DSA and DH we rely on the Sun and SunJCE providers to + * obtain the parameters from. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11KeyPairGenerator extends KeyPairGeneratorSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private final long mechanism; + + // selected or default key size, always valid + private int keySize; + + // parameters specified via init, if any + private AlgorithmParameterSpec params; + + // for RSA, selected or default value of public exponent, always valid + private BigInteger rsaPublicExponent = RSAKeyGenParameterSpec.F4; + + // the supported keysize range of the native PKCS11 library + // if the value cannot be retrieved or unspecified, -1 is used. + private final int minKeySize; + private final int maxKeySize; + + // SecureRandom instance, if specified in init + private SecureRandom random; + + P11KeyPairGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + int minKeyLen = -1; + int maxKeyLen = -1; + try { + CK_MECHANISM_INFO mechInfo = token.getMechanismInfo(mechanism); + if (mechInfo != null) { + minKeyLen = (int) mechInfo.ulMinKeySize; + maxKeyLen = (int) mechInfo.ulMaxKeySize; + } + } catch (PKCS11Exception p11e) { + // Should never happen + throw new ProviderException + ("Unexpected error while getting mechanism info", p11e); + } + // set default key sizes and apply our own algorithm-specific limits + // override lower limit to disallow unsecure keys being generated + // override upper limit to deter DOS attack + if (algorithm.equals("EC")) { + keySize = 256; + if ((minKeyLen == -1) || (minKeyLen < 112)) { + minKeyLen = 112; + } + if ((maxKeyLen == -1) || (maxKeyLen > 2048)) { + maxKeyLen = 2048; + } + } else { + // RSA, DH, and DSA + keySize = 1024; + if ((minKeyLen == -1) || (minKeyLen < 512)) { + minKeyLen = 512; + } + if (algorithm.equals("RSA")) { + if ((maxKeyLen == -1) || (maxKeyLen > 64 * 1024)) { + maxKeyLen = 64 * 1024; + } + } + } + + // auto-adjust default keysize in case it's out-of-range + if ((minKeyLen != -1) && (keySize < minKeyLen)) { + keySize = minKeyLen; + } + if ((maxKeyLen != -1) && (keySize > maxKeyLen)) { + keySize = maxKeyLen; + } + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + this.minKeySize = minKeyLen; + this.maxKeySize = maxKeyLen; + initialize(keySize, null); + } + + // see JCA spec + public void initialize(int keySize, SecureRandom random) { + token.ensureValid(); + try { + checkKeySize(keySize, null); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidParameterException(e.getMessage()); + } + this.params = null; + if (algorithm.equals("EC")) { + params = P11ECKeyFactory.getECParameterSpec(keySize); + if (params == null) { + throw new InvalidParameterException( + "No EC parameters available for key size " + + keySize + " bits"); + } + } + this.keySize = keySize; + this.random = random; + } + + // see JCA spec + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + token.ensureValid(); + int tmpKeySize; + if (algorithm.equals("DH")) { + if (params instanceof DHParameterSpec == false) { + throw new InvalidAlgorithmParameterException + ("DHParameterSpec required for Diffie-Hellman"); + } + DHParameterSpec dhParams = (DHParameterSpec) params; + tmpKeySize = dhParams.getP().bitLength(); + checkKeySize(tmpKeySize, null); + // XXX sanity check params + } else if (algorithm.equals("RSA")) { + if (params instanceof RSAKeyGenParameterSpec == false) { + throw new InvalidAlgorithmParameterException + ("RSAKeyGenParameterSpec required for RSA"); + } + RSAKeyGenParameterSpec rsaParams = + (RSAKeyGenParameterSpec) params; + tmpKeySize = rsaParams.getKeysize(); + checkKeySize(tmpKeySize, rsaParams); + // override the supplied params to null + params = null; + this.rsaPublicExponent = rsaParams.getPublicExponent(); + // XXX sanity check params + } else if (algorithm.equals("DSA")) { + if (params instanceof DSAParameterSpec == false) { + throw new InvalidAlgorithmParameterException + ("DSAParameterSpec required for DSA"); + } + DSAParameterSpec dsaParams = (DSAParameterSpec) params; + tmpKeySize = dsaParams.getP().bitLength(); + checkKeySize(tmpKeySize, null); + // XXX sanity check params + } else if (algorithm.equals("EC")) { + ECParameterSpec ecParams; + if (params instanceof ECParameterSpec) { + ecParams = P11ECKeyFactory.getECParameterSpec( + (ECParameterSpec)params); + if (ecParams == null) { + throw new InvalidAlgorithmParameterException + ("Unsupported curve: " + params); + } + } else if (params instanceof ECGenParameterSpec) { + String name = ((ECGenParameterSpec) params).getName(); + ecParams = P11ECKeyFactory.getECParameterSpec(name); + if (ecParams == null) { + throw new InvalidAlgorithmParameterException + ("Unknown curve name: " + name); + } + // override the supplied params with the derived one + params = ecParams; + } else { + throw new InvalidAlgorithmParameterException + ("ECParameterSpec or ECGenParameterSpec required for EC"); + } + tmpKeySize = ecParams.getCurve().getField().getFieldSize(); + checkKeySize(tmpKeySize, null); + } else { + throw new ProviderException("Unknown algorithm: " + algorithm); + } + this.keySize = tmpKeySize; + this.params = params; + this.random = random; + } + + // NOTE: 'params' is only used for checking RSA keys currently. + private void checkKeySize(int keySize, RSAKeyGenParameterSpec params) + throws InvalidAlgorithmParameterException { + // check native range first + if ((minKeySize != -1) && (keySize < minKeySize)) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be at least " + minKeySize + " bits"); + } + if ((maxKeySize != -1) && (keySize > maxKeySize)) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be at most " + maxKeySize + " bits"); + } + + // check our own algorithm-specific limits also + if (algorithm.equals("EC")) { + if (keySize < 112) { + throw new InvalidAlgorithmParameterException + ("Key size must be at least 112 bit"); + } + if (keySize > 2048) { + // sanity check, nobody really wants keys this large + throw new InvalidAlgorithmParameterException + ("Key size must be at most 2048 bit"); + } + } else { + // RSA, DH, DSA + if (keySize < 512) { + throw new InvalidAlgorithmParameterException + ("Key size must be at least 512 bit"); + } + if (algorithm.equals("RSA")) { + BigInteger tmpExponent = rsaPublicExponent; + if (params != null) { + tmpExponent = params.getPublicExponent(); + } + try { + // Reuse the checking in SunRsaSign provider. + // If maxKeySize is -1, then replace it with + // Integer.MAX_VALUE to indicate no limit. + RSAKeyFactory.checkKeyLengths(keySize, tmpExponent, + minKeySize, + (maxKeySize==-1? Integer.MAX_VALUE:maxKeySize)); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException(e.getMessage()); + } + } else { + if (algorithm.equals("DH") && (params != null)) { + // sanity check, nobody really wants keys this large + if (keySize > 64 * 1024) { + throw new InvalidAlgorithmParameterException + ("Key size must be at most 65536 bit"); + } + } else { + // this restriction is in the spec for DSA + // since we currently use DSA parameters for DH as well, + // it also applies to DH if no parameters are specified + if ((keySize != 2048) && + ((keySize > 1024) || ((keySize & 0x3f) != 0))) { + throw new InvalidAlgorithmParameterException(algorithm + + " key must be multiples of 64 if less than 1024 bits" + + ", or 2048 bits"); + } + } + } + } + } + + // see JCA spec + public KeyPair generateKeyPair() { + token.ensureValid(); + CK_ATTRIBUTE[] publicKeyTemplate; + CK_ATTRIBUTE[] privateKeyTemplate; + long keyType; + if (algorithm.equals("RSA")) { + keyType = CKK_RSA; + publicKeyTemplate = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS_BITS, keySize), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, rsaPublicExponent), + }; + privateKeyTemplate = new CK_ATTRIBUTE[] { + // empty + }; + } else if (algorithm.equals("DSA")) { + keyType = CKK_DSA; + DSAParameterSpec dsaParams; + if (params == null) { + try { + dsaParams = ParameterCache.getDSAParameterSpec + (keySize, random); + } catch (GeneralSecurityException e) { + throw new ProviderException + ("Could not generate DSA parameters", e); + } + } else { + dsaParams = (DSAParameterSpec)params; + } + publicKeyTemplate = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_PRIME, dsaParams.getP()), + new CK_ATTRIBUTE(CKA_SUBPRIME, dsaParams.getQ()), + new CK_ATTRIBUTE(CKA_BASE, dsaParams.getG()), + }; + privateKeyTemplate = new CK_ATTRIBUTE[] { + // empty + }; + } else if (algorithm.equals("DH")) { + keyType = CKK_DH; + DHParameterSpec dhParams; + int privateBits; + if (params == null) { + try { + dhParams = ParameterCache.getDHParameterSpec + (keySize, random); + } catch (GeneralSecurityException e) { + throw new ProviderException + ("Could not generate DH parameters", e); + } + privateBits = 0; + } else { + dhParams = (DHParameterSpec)params; + privateBits = dhParams.getL(); + } + if (privateBits <= 0) { + // XXX find better defaults + privateBits = (keySize >= 1024) ? 768 : 512; + } + publicKeyTemplate = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_PRIME, dhParams.getP()), + new CK_ATTRIBUTE(CKA_BASE, dhParams.getG()) + }; + privateKeyTemplate = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_VALUE_BITS, privateBits), + }; + } else if (algorithm.equals("EC")) { + keyType = CKK_EC; + byte[] encodedParams = + P11ECKeyFactory.encodeParameters((ECParameterSpec)params); + publicKeyTemplate = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), + }; + privateKeyTemplate = new CK_ATTRIBUTE[] { + // empty + }; + } else { + throw new ProviderException("Unknown algorithm: " + algorithm); + } + Session session = null; + try { + session = token.getObjSession(); + publicKeyTemplate = token.getAttributes + (O_GENERATE, CKO_PUBLIC_KEY, keyType, publicKeyTemplate); + privateKeyTemplate = token.getAttributes + (O_GENERATE, CKO_PRIVATE_KEY, keyType, privateKeyTemplate); + long[] keyIDs = token.p11.C_GenerateKeyPair + (session.id(), new CK_MECHANISM(mechanism), + publicKeyTemplate, privateKeyTemplate); + PublicKey publicKey = P11Key.publicKey + (session, keyIDs[0], algorithm, keySize, publicKeyTemplate); + PrivateKey privateKey = P11Key.privateKey + (session, keyIDs[1], algorithm, keySize, privateKeyTemplate); + return new KeyPair(publicKey, privateKey); + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + token.releaseSession(session); + } + } +} diff --git a/src/sun/security/pkcs11/P11KeyStore.java b/src/sun/security/pkcs11/P11KeyStore.java new file mode 100644 index 00000000..85ff1ef1 --- /dev/null +++ b/src/sun/security/pkcs11/P11KeyStore.java @@ -0,0 +1,2668 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Enumeration; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.HashMap; +import java.util.Set; + +import java.security.*; +import java.security.KeyStore.*; + +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; + +import java.security.interfaces.*; +import java.security.spec.*; + +import javax.crypto.SecretKey; +import javax.crypto.interfaces.*; + +import javax.security.auth.x500.X500Principal; +import javax.security.auth.login.LoginException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import sun.security.util.Debug; +import sun.security.util.DerValue; +import sun.security.util.ECUtil; + +import sun.security.pkcs11.Secmod.*; +import static sun.security.pkcs11.P11Util.*; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +import sun.security.rsa.RSAKeyFactory; + +final class P11KeyStore extends KeyStoreSpi { + + private static final CK_ATTRIBUTE ATTR_CLASS_CERT = + new CK_ATTRIBUTE(CKA_CLASS, CKO_CERTIFICATE); + private static final CK_ATTRIBUTE ATTR_CLASS_PKEY = + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY); + private static final CK_ATTRIBUTE ATTR_CLASS_SKEY = + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY); + + private static final CK_ATTRIBUTE ATTR_X509_CERT_TYPE = + new CK_ATTRIBUTE(CKA_CERTIFICATE_TYPE, CKC_X_509); + + private static final CK_ATTRIBUTE ATTR_TOKEN_TRUE = + new CK_ATTRIBUTE(CKA_TOKEN, true); + + // XXX for testing purposes only + // - NSS doesn't support persistent secret keys + // (key type gets mangled if secret key is a token key) + // - if debug is turned on, then this is set to false + private static CK_ATTRIBUTE ATTR_SKEY_TOKEN_TRUE = ATTR_TOKEN_TRUE; + + private static final CK_ATTRIBUTE ATTR_TRUSTED_TRUE = + new CK_ATTRIBUTE(CKA_TRUSTED, true); + private static final CK_ATTRIBUTE ATTR_PRIVATE_TRUE = + new CK_ATTRIBUTE(CKA_PRIVATE, true); + + private static final long NO_HANDLE = -1; + private static final long FINDOBJECTS_MAX = 100; + private static final String ALIAS_SEP = "/"; + + private static final boolean NSS_TEST = false; + private static final Debug debug = + Debug.getInstance("pkcs11keystore"); + private static boolean CKA_TRUSTED_SUPPORTED = true; + + private final Token token; + + // If multiple certs are found to share the same CKA_LABEL + // at load time (NSS-style keystore), then the keystore is read + // and the unique keystore aliases are mapped to the entries. + // However, write capabilities are disabled. + private boolean writeDisabled = false; + + // Map of unique keystore aliases to entries in the token + private HashMap aliasMap; + + // whether to use NSS Secmod info for trust attributes + private final boolean useSecmodTrust; + + // if useSecmodTrust == true, which type of trust we are interested in + private TrustType nssTrustType; + + /** + * The underlying token may contain multiple certs belonging to the + * same "personality" (for example, a signing cert and encryption cert), + * all sharing the same CKA_LABEL. These must be resolved + * into unique keystore aliases. + * + * In addition, private keys and certs may not have a CKA_LABEL. + * It is assumed that a private key and corresponding certificate + * share the same CKA_ID, and that the CKA_ID is unique across the token. + * The CKA_ID may not be human-readable. + * These pairs must be resolved into unique keystore aliases. + * + * Furthermore, secret keys are assumed to have a CKA_LABEL + * unique across the entire token. + * + * When the KeyStore is loaded, instances of this class are + * created to represent the private keys/secret keys/certs + * that reside on the token. + */ + private static class AliasInfo { + + // CKA_CLASS - entry type + private CK_ATTRIBUTE type = null; + + // CKA_LABEL of cert and secret key + private String label = null; + + // CKA_ID of the private key/cert pair + private byte[] id = null; + + // CKA_TRUSTED - true if cert is trusted + private boolean trusted = false; + + // either end-entity cert or trusted cert depending on 'type' + private X509Certificate cert = null; + + // chain + private X509Certificate chain[] = null; + + // true if CKA_ID for private key and cert match up + private boolean matched = false; + + // SecretKeyEntry + public AliasInfo(String label) { + this.type = ATTR_CLASS_SKEY; + this.label = label; + } + + // PrivateKeyEntry + public AliasInfo(String label, + byte[] id, + boolean trusted, + X509Certificate cert) { + this.type = ATTR_CLASS_PKEY; + this.label = label; + this.id = id; + this.trusted = trusted; + this.cert = cert; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (type == ATTR_CLASS_PKEY) { + sb.append("\ttype=[private key]\n"); + } else if (type == ATTR_CLASS_SKEY) { + sb.append("\ttype=[secret key]\n"); + } else if (type == ATTR_CLASS_CERT) { + sb.append("\ttype=[trusted cert]\n"); + } + sb.append("\tlabel=[" + label + "]\n"); + if (id == null) { + sb.append("\tid=[null]\n"); + } else { + sb.append("\tid=" + P11KeyStore.getID(id) + "\n"); + } + sb.append("\ttrusted=[" + trusted + "]\n"); + sb.append("\tmatched=[" + matched + "]\n"); + if (cert == null) { + sb.append("\tcert=[null]\n"); + } else { + sb.append("\tcert=[\tsubject: " + + cert.getSubjectX500Principal() + + "\n\t\tissuer: " + + cert.getIssuerX500Principal() + + "\n\t\tserialNum: " + + cert.getSerialNumber().toString() + + "]"); + } + return sb.toString(); + } + } + + /** + * callback handler for passing password to Provider.login method + */ + private static class PasswordCallbackHandler implements CallbackHandler { + + private char[] password; + + private PasswordCallbackHandler(char[] password) { + if (password != null) { + this.password = password.clone(); + } + } + + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + if (!(callbacks[0] instanceof PasswordCallback)) { + throw new UnsupportedCallbackException(callbacks[0]); + } + PasswordCallback pc = (PasswordCallback)callbacks[0]; + pc.setPassword(password); // this clones the password if not null + } + + protected void finalize() throws Throwable { + if (password != null) { + Arrays.fill(password, ' '); + } + super.finalize(); + } + } + + /** + * getTokenObject return value. + * + * if object is not found, type is set to null. + * otherwise, type is set to the requested type. + */ + private static class THandle { + private final long handle; // token object handle + private final CK_ATTRIBUTE type; // CKA_CLASS + + private THandle(long handle, CK_ATTRIBUTE type) { + this.handle = handle; + this.type = type; + } + } + + P11KeyStore(Token token) { + this.token = token; + this.useSecmodTrust = token.provider.nssUseSecmodTrust; + } + + /** + * Returns the key associated with the given alias. + * The key must have been associated with + * the alias by a call to setKeyEntry, + * or by a call to setEntry with a + * PrivateKeyEntry or SecretKeyEntry. + * + * @param alias the alias name + * @param password the password, which must be null + * + * @return the requested key, or null if the given alias does not exist + * or does not identify a key-related entry. + * + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * key cannot be found + * @exception UnrecoverableKeyException if the key cannot be recovered + */ + public synchronized Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException { + + token.ensureValid(); + if (password != null && !token.config.getKeyStoreCompatibilityMode()) { + throw new NoSuchAlgorithmException("password must be null"); + } + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) { + return null; + } + + Session session = null; + try { + session = token.getOpSession(); + + if (aliasInfo.type == ATTR_CLASS_PKEY) { + THandle h = getTokenObject(session, + aliasInfo.type, + aliasInfo.id, + null); + if (h.type == ATTR_CLASS_PKEY) { + return loadPkey(session, h.handle); + } + } else { + THandle h = getTokenObject(session, + ATTR_CLASS_SKEY, + null, + alias); + if (h.type == ATTR_CLASS_SKEY) { + return loadSkey(session, h.handle); + } + } + + // did not find anything + return null; + } catch (PKCS11Exception | KeyStoreException e) { + throw new ProviderException(e); + } finally { + token.releaseSession(session); + } + } + + /** + * Returns the certificate chain associated with the given alias. + * The certificate chain must have been associated with the alias + * by a call to setKeyEntry, + * or by a call to setEntry with a + * PrivateKeyEntry. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last), or null if the given alias + * does not exist or does not contain a certificate chain + */ + public synchronized Certificate[] engineGetCertificateChain(String alias) { + + token.ensureValid(); + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_PKEY) { + return null; + } + return aliasInfo.chain; + } + + /** + * Returns the certificate associated with the given alias. + * + *

    If the given alias name identifies an entry + * created by a call to setCertificateEntry, + * or created by a call to setEntry with a + * TrustedCertificateEntry, + * then the trusted certificate contained in that entry is returned. + * + *

    If the given alias name identifies an entry + * created by a call to setKeyEntry, + * or created by a call to setEntry with a + * PrivateKeyEntry, + * then the first element of the certificate chain in that entry + * (if a chain exists) is returned. + * + * @param alias the alias name + * + * @return the certificate, or null if the given alias does not exist or + * does not contain a certificate. + */ + public synchronized Certificate engineGetCertificate(String alias) { + token.ensureValid(); + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null) { + return null; + } + return aliasInfo.cert; + } + + /** + * Returns the creation date of the entry identified by the given alias. + * + * @param alias the alias name + * + * @return the creation date of this entry, or null if the given alias does + * not exist + */ + public Date engineGetCreationDate(String alias) { + token.ensureValid(); + throw new ProviderException(new UnsupportedOperationException()); + } + + /** + * Assigns the given key to the given alias, protecting it with the given + * password. + * + *

    If the given key is of type java.security.PrivateKey, + * it must be accompanied by a certificate chain certifying the + * corresponding public key. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key to be associated with the alias + * @param password the password to protect the key + * @param chain the certificate chain for the corresponding public + * key (only required if the given key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if the given key cannot be protected, or + * this operation fails for some other reason + */ + public synchronized void engineSetKeyEntry(String alias, Key key, + char[] password, + Certificate[] chain) + throws KeyStoreException { + + token.ensureValid(); + checkWrite(); + + if (!(key instanceof PrivateKey) && !(key instanceof SecretKey)) { + throw new KeyStoreException("key must be PrivateKey or SecretKey"); + } else if (key instanceof PrivateKey && chain == null) { + throw new KeyStoreException + ("PrivateKey must be accompanied by non-null chain"); + } else if (key instanceof SecretKey && chain != null) { + throw new KeyStoreException + ("SecretKey must be accompanied by null chain"); + } else if (password != null && + !token.config.getKeyStoreCompatibilityMode()) { + throw new KeyStoreException("Password must be null"); + } + + Entry entry = null; + try { + if (key instanceof PrivateKey) { + entry = new PrivateKeyEntry((PrivateKey)key, chain); + } else if (key instanceof SecretKey) { + entry = new SecretKeyEntry((SecretKey)key); + } + } catch (NullPointerException | IllegalArgumentException e) { + throw new KeyStoreException(e); + } + engineSetEntry(alias, entry, new PasswordProtection(password)); + } + + /** + * Assigns the given key (that has already been protected) to the given + * alias. + * + *

    If the protected key is of type + * java.security.PrivateKey, + * it must be accompanied by a certificate chain certifying the + * corresponding public key. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key (in protected format) to be associated with the alias + * @param chain the certificate chain for the corresponding public + * key (only useful if the protected key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if this operation fails. + */ + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) + throws KeyStoreException { + token.ensureValid(); + throw new ProviderException(new UnsupportedOperationException()); + } + + /** + * Assigns the given certificate to the given alias. + * + *

    If the given alias identifies an existing entry + * created by a call to setCertificateEntry, + * or created by a call to setEntry with a + * TrustedCertificateEntry, + * the trusted certificate in the existing entry + * is overridden by the given certificate. + * + * @param alias the alias name + * @param cert the certificate + * + * @exception KeyStoreException if the given alias already exists and does + * not identify an entry containing a trusted certificate, + * or this operation fails for some other reason. + */ + public synchronized void engineSetCertificateEntry + (String alias, Certificate cert) throws KeyStoreException { + + token.ensureValid(); + checkWrite(); + + if (cert == null) { + throw new KeyStoreException("invalid null certificate"); + } + + Entry entry = null; + entry = new TrustedCertificateEntry(cert); + engineSetEntry(alias, entry, null); + } + + /** + * Deletes the entry identified by the given alias from this keystore. + * + * @param alias the alias name + * + * @exception KeyStoreException if the entry cannot be removed. + */ + public synchronized void engineDeleteEntry(String alias) + throws KeyStoreException { + token.ensureValid(); + + if (token.isWriteProtected()) { + throw new KeyStoreException("token write-protected"); + } + checkWrite(); + deleteEntry(alias); + } + + /** + * XXX - not sure whether to keep this + */ + private boolean deleteEntry(String alias) throws KeyStoreException { + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo != null) { + + aliasMap.remove(alias); + + try { + if (aliasInfo.type == ATTR_CLASS_CERT) { + // trusted certificate entry + return destroyCert(aliasInfo.id); + } else if (aliasInfo.type == ATTR_CLASS_PKEY) { + // private key entry + return destroyPkey(aliasInfo.id) && + destroyChain(aliasInfo.id); + } else if (aliasInfo.type == ATTR_CLASS_SKEY) { + // secret key entry + return destroySkey(alias); + } else { + throw new KeyStoreException("unexpected entry type"); + } + } catch (PKCS11Exception | CertificateException e) { + throw new KeyStoreException(e); + } + } + return false; + } + + /** + * Lists all the alias names of this keystore. + * + * @return enumeration of the alias names + */ + public synchronized Enumeration engineAliases() { + token.ensureValid(); + + // don't want returned enumeration to iterate off actual keySet - + // otherwise applications that iterate and modify the keystore + // may run into concurrent modification problems + return Collections.enumeration(new HashSet(aliasMap.keySet())); + } + + /** + * Checks if the given alias exists in this keystore. + * + * @param alias the alias name + * + * @return true if the alias exists, false otherwise + */ + public synchronized boolean engineContainsAlias(String alias) { + token.ensureValid(); + return aliasMap.containsKey(alias); + } + + /** + * Retrieves the number of entries in this keystore. + * + * @return the number of entries in this keystore + */ + public synchronized int engineSize() { + token.ensureValid(); + return aliasMap.size(); + } + + /** + * Returns true if the entry identified by the given alias + * was created by a call to setKeyEntry, + * or created by a call to setEntry with a + * PrivateKeyEntry or a SecretKeyEntry. + * + * @param alias the alias for the keystore entry to be checked + * + * @return true if the entry identified by the given alias is a + * key-related, false otherwise. + */ + public synchronized boolean engineIsKeyEntry(String alias) { + token.ensureValid(); + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null || aliasInfo.type == ATTR_CLASS_CERT) { + return false; + } + return true; + } + + /** + * Returns true if the entry identified by the given alias + * was created by a call to setCertificateEntry, + * or created by a call to setEntry with a + * TrustedCertificateEntry. + * + * @param alias the alias for the keystore entry to be checked + * + * @return true if the entry identified by the given alias contains a + * trusted certificate, false otherwise. + */ + public synchronized boolean engineIsCertificateEntry(String alias) { + token.ensureValid(); + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null || aliasInfo.type != ATTR_CLASS_CERT) { + return false; + } + return true; + } + + /** + * Returns the (alias) name of the first keystore entry whose certificate + * matches the given certificate. + * + *

    This method attempts to match the given certificate with each + * keystore entry. If the entry being considered was + * created by a call to setCertificateEntry, + * or created by a call to setEntry with a + * TrustedCertificateEntry, + * then the given certificate is compared to that entry's certificate. + * + *

    If the entry being considered was + * created by a call to setKeyEntry, + * or created by a call to setEntry with a + * PrivateKeyEntry, + * then the given certificate is compared to the first + * element of that entry's certificate chain. + * + * @param cert the certificate to match with. + * + * @return the alias name of the first entry with matching certificate, + * or null if no such entry exists in this keystore. + */ + public synchronized String engineGetCertificateAlias(Certificate cert) { + token.ensureValid(); + Enumeration e = engineAliases(); + while (e.hasMoreElements()) { + String alias = e.nextElement(); + Certificate tokenCert = engineGetCertificate(alias); + if (tokenCert != null && tokenCert.equals(cert)) { + return alias; + } + } + return null; + } + + /** + * engineStore currently is a No-op. + * Entries are stored to the token during engineSetEntry + * + * @param stream this must be null + * @param password this must be null + */ + public synchronized void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + token.ensureValid(); + if (stream != null && !token.config.getKeyStoreCompatibilityMode()) { + throw new IOException("output stream must be null"); + } + + if (password != null && !token.config.getKeyStoreCompatibilityMode()) { + throw new IOException("password must be null"); + } + } + + /** + * engineStore currently is a No-op. + * Entries are stored to the token during engineSetEntry + * + * @param param this must be null + * + * @exception IllegalArgumentException if the given + * KeyStore.LoadStoreParameter + * input is not null + */ + public synchronized void engineStore(LoadStoreParameter param) + throws IOException, NoSuchAlgorithmException, CertificateException { + token.ensureValid(); + if (param != null) { + throw new IllegalArgumentException + ("LoadStoreParameter must be null"); + } + } + + /** + * Loads the keystore. + * + * @param stream the input stream, which must be null + * @param password the password used to unlock the keystore, + * or null if the token supports a + * CKF_PROTECTED_AUTHENTICATION_PATH + * + * @exception IOException if the given stream is not + * null, if the token supports a + * CKF_PROTECTED_AUTHENTICATION_PATH and a non-null + * password is given, of if the token login operation failed + */ + public synchronized void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + + token.ensureValid(); + + if (NSS_TEST) { + ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false); + } + + if (stream != null && !token.config.getKeyStoreCompatibilityMode()) { + throw new IOException("input stream must be null"); + } + + if (useSecmodTrust) { + nssTrustType = TrustType.ALL; + } + + try { + if (password == null) { + login(null); + } else { + login(new PasswordCallbackHandler(password)); + } + if (mapLabels() == true) { + // CKA_LABELs are shared by multiple certs + writeDisabled = true; + } + if (debug != null) { + dumpTokenMap(); + } + } catch (LoginException | KeyStoreException | PKCS11Exception e) { + throw new IOException("load failed", e); + } + } + + /** + * Loads the keystore using the given + * KeyStore.LoadStoreParameter. + * + *

    The LoadStoreParameter.getProtectionParameter() + * method is expected to return a KeyStore.PasswordProtection + * object. The password is retrieved from that object and used + * to unlock the PKCS#11 token. + * + *

    If the token supports a CKF_PROTECTED_AUTHENTICATION_PATH + * then the provided password must be null. + * + * @param param the KeyStore.LoadStoreParameter + * + * @exception IllegalArgumentException if the given + * KeyStore.LoadStoreParameter is null, + * or if that parameter returns a null + * ProtectionParameter object. + * input is not recognized + * @exception IOException if the token supports a + * CKF_PROTECTED_AUTHENTICATION_PATH and the provided password + * is non-null, or if the token login operation fails + */ + public synchronized void engineLoad(LoadStoreParameter param) + throws IOException, NoSuchAlgorithmException, + CertificateException { + + token.ensureValid(); + + if (NSS_TEST) { + ATTR_SKEY_TOKEN_TRUE = new CK_ATTRIBUTE(CKA_TOKEN, false); + } + + // if caller wants to pass a NULL password, + // force it to pass a non-NULL PasswordProtection that returns + // a NULL password + + if (param == null) { + throw new IllegalArgumentException + ("invalid null LoadStoreParameter"); + } + if (useSecmodTrust) { + if (param instanceof KeyStoreLoadParameter) { + nssTrustType = ((KeyStoreLoadParameter)param).getTrustType(); + } else { + nssTrustType = TrustType.ALL; + } + } + + CallbackHandler handler; + ProtectionParameter pp = param.getProtectionParameter(); + if (pp instanceof PasswordProtection) { + char[] password = ((PasswordProtection)pp).getPassword(); + if (password == null) { + handler = null; + } else { + handler = new PasswordCallbackHandler(password); + } + } else if (pp instanceof CallbackHandlerProtection) { + handler = ((CallbackHandlerProtection)pp).getCallbackHandler(); + } else { + throw new IllegalArgumentException + ("ProtectionParameter must be either " + + "PasswordProtection or CallbackHandlerProtection"); + } + + try { + login(handler); + if (mapLabels() == true) { + // CKA_LABELs are shared by multiple certs + writeDisabled = true; + } + if (debug != null) { + dumpTokenMap(); + } + } catch (LoginException | KeyStoreException | PKCS11Exception e) { + throw new IOException("load failed", e); + } + } + + private void login(CallbackHandler handler) throws LoginException { + if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) { + token.provider.login(null, handler); + } else { + // token supports protected authentication path + // (external pin-pad, for example) + if (handler != null && + !token.config.getKeyStoreCompatibilityMode()) { + throw new LoginException("can not specify password if token " + + "supports protected authentication path"); + } + + // must rely on application-set or default handler + // if one is necessary + token.provider.login(null, null); + } + } + + /** + * Get a KeyStore.Entry for the specified alias + * + * @param alias get the KeyStore.Entry for this alias + * @param protParam this must be null + * + * @return the KeyStore.Entry for the specified alias, + * or null if there is no such entry + * + * @exception KeyStoreException if the operation failed + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * entry cannot be found + * @exception UnrecoverableEntryException if the specified + * protParam were insufficient or invalid + * + * @since 1.5 + */ + public synchronized Entry engineGetEntry(String alias, + ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableEntryException { + + token.ensureValid(); + + if (protParam != null && + protParam instanceof PasswordProtection && + ((PasswordProtection)protParam).getPassword() != null && + !token.config.getKeyStoreCompatibilityMode()) { + throw new KeyStoreException("ProtectionParameter must be null"); + } + + AliasInfo aliasInfo = aliasMap.get(alias); + if (aliasInfo == null) { + if (debug != null) { + debug.println("engineGetEntry did not find alias [" + + alias + + "] in map"); + } + return null; + } + + Session session = null; + try { + session = token.getOpSession(); + + if (aliasInfo.type == ATTR_CLASS_CERT) { + // trusted certificate entry + if (debug != null) { + debug.println("engineGetEntry found trusted cert entry"); + } + return new TrustedCertificateEntry(aliasInfo.cert); + } else if (aliasInfo.type == ATTR_CLASS_SKEY) { + // secret key entry + if (debug != null) { + debug.println("engineGetEntry found secret key entry"); + } + + THandle h = getTokenObject + (session, ATTR_CLASS_SKEY, null, aliasInfo.label); + if (h.type != ATTR_CLASS_SKEY) { + throw new KeyStoreException + ("expected but could not find secret key"); + } else { + SecretKey skey = loadSkey(session, h.handle); + return new SecretKeyEntry(skey); + } + } else { + // private key entry + if (debug != null) { + debug.println("engineGetEntry found private key entry"); + } + + THandle h = getTokenObject + (session, ATTR_CLASS_PKEY, aliasInfo.id, null); + if (h.type != ATTR_CLASS_PKEY) { + throw new KeyStoreException + ("expected but could not find private key"); + } else { + PrivateKey pkey = loadPkey(session, h.handle); + Certificate[] chain = aliasInfo.chain; + if ((pkey != null) && (chain != null)) { + return new PrivateKeyEntry(pkey, chain); + } else { + if (debug != null) { + debug.println + ("engineGetEntry got null cert chain or private key"); + } + } + } + } + return null; + } catch (PKCS11Exception pe) { + throw new KeyStoreException(pe); + } finally { + token.releaseSession(session); + } + } + + /** + * Save a KeyStore.Entry under the specified alias. + * + *

    If an entry already exists for the specified alias, + * it is overridden. + * + *

    This KeyStore implementation only supports the standard + * entry types, and only supports X509Certificates in + * TrustedCertificateEntries. Also, this implementation does not support + * protecting entries using a different password + * from the one used for token login. + * + *

    Entries are immediately stored on the token. + * + * @param alias save the KeyStore.Entry under this alias + * @param entry the Entry to save + * @param protParam this must be null + * + * @exception KeyStoreException if this operation fails + * + * @since 1.5 + */ + public synchronized void engineSetEntry(String alias, Entry entry, + ProtectionParameter protParam) + throws KeyStoreException { + + token.ensureValid(); + checkWrite(); + + if (protParam != null && + protParam instanceof PasswordProtection && + ((PasswordProtection)protParam).getPassword() != null && + !token.config.getKeyStoreCompatibilityMode()) { + throw new KeyStoreException(new UnsupportedOperationException + ("ProtectionParameter must be null")); + } + + if (token.isWriteProtected()) { + throw new KeyStoreException("token write-protected"); + } + + if (entry instanceof TrustedCertificateEntry) { + + if (useSecmodTrust == false) { + // PKCS #11 does not allow app to modify trusted certs - + throw new KeyStoreException(new UnsupportedOperationException + ("trusted certificates may only be set by " + + "token initialization application")); + } + Module module = token.provider.nssModule; + if ((module.type != ModuleType.KEYSTORE) && (module.type != ModuleType.FIPS)) { + // XXX allow TRUSTANCHOR module + throw new KeyStoreException("Trusted certificates can only be " + + "added to the NSS KeyStore module"); + } + Certificate cert = ((TrustedCertificateEntry)entry).getTrustedCertificate(); + if (cert instanceof X509Certificate == false) { + throw new KeyStoreException("Certificate must be an X509Certificate"); + } + X509Certificate xcert = (X509Certificate)cert; + AliasInfo info = aliasMap.get(alias); + if (info != null) { + // XXX try to update + deleteEntry(alias); + } + try { + storeCert(alias, xcert); + module.setTrust(token, xcert); + mapLabels(); + } catch (PKCS11Exception | CertificateException e) { + throw new KeyStoreException(e); + } + + } else { + + if (entry instanceof PrivateKeyEntry) { + + PrivateKey key = + ((PrivateKeyEntry)entry).getPrivateKey(); + if (!(key instanceof P11Key) && + !(key instanceof RSAPrivateKey) && + !(key instanceof DSAPrivateKey) && + !(key instanceof DHPrivateKey) && + !(key instanceof ECPrivateKey)) { + throw new KeyStoreException("unsupported key type: " + + key.getClass().getName()); + } + + // only support X509Certificate chains + Certificate[] chain = + ((PrivateKeyEntry)entry).getCertificateChain(); + if (!(chain instanceof X509Certificate[])) { + throw new KeyStoreException + (new UnsupportedOperationException + ("unsupported certificate array type: " + + chain.getClass().getName())); + } + + try { + boolean updatedAlias = false; + Set aliases = aliasMap.keySet(); + for (String oldAlias : aliases) { + + // see if there's an existing entry with the same info + + AliasInfo aliasInfo = aliasMap.get(oldAlias); + if (aliasInfo.type == ATTR_CLASS_PKEY && + aliasInfo.cert.getPublicKey().equals + (chain[0].getPublicKey())) { + + // found existing entry - + // caller is renaming entry or updating cert chain + // + // set new CKA_LABEL/CKA_ID + // and update certs if necessary + + updatePkey(alias, + aliasInfo.id, + (X509Certificate[])chain, + !aliasInfo.cert.equals(chain[0])); + updatedAlias = true; + break; + } + } + + if (!updatedAlias) { + // caller adding new entry + engineDeleteEntry(alias); + storePkey(alias, (PrivateKeyEntry)entry); + } + + } catch (PKCS11Exception | CertificateException pe) { + throw new KeyStoreException(pe); + } + + } else if (entry instanceof SecretKeyEntry) { + + SecretKeyEntry ske = (SecretKeyEntry)entry; + SecretKey skey = ske.getSecretKey(); + + try { + // first check if the key already exists + AliasInfo aliasInfo = aliasMap.get(alias); + + if (aliasInfo != null) { + engineDeleteEntry(alias); + } + storeSkey(alias, ske); + + } catch (PKCS11Exception pe) { + throw new KeyStoreException(pe); + } + + } else { + throw new KeyStoreException(new UnsupportedOperationException + ("unsupported entry type: " + entry.getClass().getName())); + } + + try { + + // XXX NSS does not write out the CKA_ID we pass to them + // + // therefore we must re-map labels + // (can not simply update aliasMap) + + mapLabels(); + if (debug != null) { + dumpTokenMap(); + } + } catch (PKCS11Exception | CertificateException pe) { + throw new KeyStoreException(pe); + } + } + + if (debug != null) { + debug.println + ("engineSetEntry added new entry for [" + + alias + + "] to token"); + } + } + + /** + * Determines if the keystore Entry for the specified + * alias is an instance or subclass of the specified + * entryClass. + * + * @param alias the alias name + * @param entryClass the entry class + * + * @return true if the keystore Entry for the specified + * alias is an instance or subclass of the + * specified entryClass, false otherwise + */ + public synchronized boolean engineEntryInstanceOf + (String alias, Class entryClass) { + token.ensureValid(); + return super.engineEntryInstanceOf(alias, entryClass); + } + + private X509Certificate loadCert(Session session, long oHandle) + throws PKCS11Exception, CertificateException { + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] + { new CK_ATTRIBUTE(CKA_VALUE) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + + byte[] bytes = attrs[0].getByteArray(); + if (bytes == null) { + throw new CertificateException + ("unexpectedly retrieved null byte array"); + } + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return (X509Certificate)cf.generateCertificate + (new ByteArrayInputStream(bytes)); + } + + private X509Certificate[] loadChain(Session session, + X509Certificate endCert) + throws PKCS11Exception, CertificateException { + + ArrayList lChain = null; + + if (endCert.getSubjectX500Principal().equals + (endCert.getIssuerX500Principal())) { + // self signed + return new X509Certificate[] { endCert }; + } else { + lChain = new ArrayList(); + lChain.add(endCert); + } + + // try loading remaining certs in chain by following + // issuer->subject links + + X509Certificate next = endCert; + while (true) { + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_CERT, + new CK_ATTRIBUTE(CKA_SUBJECT, + next.getIssuerX500Principal().getEncoded()) }; + long[] ch = findObjects(session, attrs); + + if (ch == null || ch.length == 0) { + // done + break; + } else { + // if more than one found, use first + if (debug != null && ch.length > 1) { + debug.println("engineGetEntry found " + + ch.length + + " certificate entries for subject [" + + next.getIssuerX500Principal().toString() + + "] in token - using first entry"); + } + + next = loadCert(session, ch[0]); + lChain.add(next); + if (next.getSubjectX500Principal().equals + (next.getIssuerX500Principal())) { + // self signed + break; + } + } + } + + return lChain.toArray(new X509Certificate[lChain.size()]); + } + + private SecretKey loadSkey(Session session, long oHandle) + throws PKCS11Exception { + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_KEY_TYPE) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + long kType = attrs[0].getLong(); + + String keyType = null; + int keyLength = -1; + + // XXX NSS mangles the stored key type for secret key token objects + + if (kType == CKK_DES || kType == CKK_DES3) { + if (kType == CKK_DES) { + keyType = "DES"; + keyLength = 64; + } else if (kType == CKK_DES3) { + keyType = "DESede"; + keyLength = 192; + } + } else { + if (kType == CKK_AES) { + keyType = "AES"; + } else if (kType == CKK_BLOWFISH) { + keyType = "Blowfish"; + } else if (kType == CKK_RC4) { + keyType = "ARCFOUR"; + } else { + if (debug != null) { + debug.println("unknown key type [" + + kType + + "] - using 'Generic Secret'"); + } + keyType = "Generic Secret"; + } + + // XXX NSS problem CKR_ATTRIBUTE_TYPE_INVALID? + if (NSS_TEST) { + keyLength = 128; + } else { + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_VALUE_LEN) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + keyLength = (int)attrs[0].getLong(); + } + } + + return P11Key.secretKey(session, oHandle, keyType, keyLength, null); + } + + private PrivateKey loadPkey(Session session, long oHandle) + throws PKCS11Exception, KeyStoreException { + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_KEY_TYPE) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + long kType = attrs[0].getLong(); + String keyType = null; + int keyLength = 0; + + if (kType == CKK_RSA) { + + keyType = "RSA"; + + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_MODULUS) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + BigInteger modulus = attrs[0].getBigInteger(); + keyLength = modulus.bitLength(); + + // This check will combine our "don't care" values here + // with the system-wide min/max values. + try { + RSAKeyFactory.checkKeyLengths(keyLength, null, + -1, Integer.MAX_VALUE); + } catch (InvalidKeyException e) { + throw new KeyStoreException(e.getMessage()); + } + + return P11Key.privateKey(session, + oHandle, + keyType, + keyLength, + null); + + } else if (kType == CKK_DSA) { + + keyType = "DSA"; + + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + BigInteger prime = attrs[0].getBigInteger(); + keyLength = prime.bitLength(); + + return P11Key.privateKey(session, + oHandle, + keyType, + keyLength, + null); + + } else if (kType == CKK_DH) { + + keyType = "DH"; + + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_PRIME) }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + BigInteger prime = attrs[0].getBigInteger(); + keyLength = prime.bitLength(); + + return P11Key.privateKey(session, + oHandle, + keyType, + keyLength, + null); + + } else if (kType == CKK_EC) { + + attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_EC_PARAMS), + }; + token.p11.C_GetAttributeValue(session.id(), oHandle, attrs); + byte[] encodedParams = attrs[0].getByteArray(); + try { + ECParameterSpec params = + ECUtil.getECParameterSpec(null, encodedParams); + keyLength = params.getCurve().getField().getFieldSize(); + } catch (IOException e) { + // we do not want to accept key with unsupported parameters + throw new KeyStoreException("Unsupported parameters", e); + } + + return P11Key.privateKey(session, oHandle, "EC", keyLength, null); + + } else { + if (debug != null) { + debug.println("unknown key type [" + kType + "]"); + } + throw new KeyStoreException("unknown key type"); + } + } + + + /** + * XXX On ibutton, when you C_SetAttribute(CKA_ID) for a private key + * it not only changes the CKA_ID of the private key, + * it changes the CKA_ID of the corresponding cert too. + * And vice versa. + * + * XXX On ibutton, CKR_DEVICE_ERROR if you C_SetAttribute(CKA_ID) + * for a private key, and then try to delete the corresponding cert. + * So this code reverses the order. + * After the cert is first destroyed (if necessary), + * then the CKA_ID of the private key can be changed successfully. + * + * @param replaceCert if true, then caller is updating alias info for + * existing cert (only update CKA_ID/CKA_LABEL). + * if false, then caller is updating cert chain + * (delete old end cert and add new chain). + */ + private void updatePkey(String alias, + byte[] cka_id, + X509Certificate[] chain, + boolean replaceCert) throws + KeyStoreException, CertificateException, PKCS11Exception { + + // XXX + // + // always set replaceCert to true + // + // NSS does not allow resetting of CKA_LABEL on an existing cert + // (C_SetAttribute call succeeds, but is ignored) + + replaceCert = true; + + Session session = null; + try { + session = token.getOpSession(); + + // first get private key object handle and hang onto it + + THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null); + long pKeyHandle; + if (h.type == ATTR_CLASS_PKEY) { + pKeyHandle = h.handle; + } else { + throw new KeyStoreException + ("expected but could not find private key " + + "with CKA_ID " + + getID(cka_id)); + } + + // next find existing end entity cert + + h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); + if (h.type != ATTR_CLASS_CERT) { + throw new KeyStoreException + ("expected but could not find certificate " + + "with CKA_ID " + + getID(cka_id)); + } else { + if (replaceCert) { + // replacing existing cert and chain + destroyChain(cka_id); + } else { + // renaming alias for existing cert + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_LABEL, alias), + new CK_ATTRIBUTE(CKA_ID, alias) }; + token.p11.C_SetAttributeValue + (session.id(), h.handle, attrs); + } + } + + // add new chain + + if (replaceCert) { + // add all certs in chain + storeChain(alias, chain); + } else { + // already updated alias info for existing end cert - + // just update CA certs + storeCaCerts(chain, 1); + } + + // finally update CKA_ID for private key + // + // ibutton may have already done this (that is ok) + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_ID, alias) }; + token.p11.C_SetAttributeValue(session.id(), pKeyHandle, attrs); + + if (debug != null) { + debug.println("updatePkey set new alias [" + + alias + + "] for private key entry"); + } + } finally { + token.releaseSession(session); + } + } + + private void updateP11Pkey(String alias, CK_ATTRIBUTE attribute, P11Key key) + throws PKCS11Exception { + + // if token key, update alias. + // if session key, convert to token key. + + Session session = null; + try { + session = token.getOpSession(); + if (key.tokenObject == true) { + + // token key - set new CKA_ID + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_ID, alias) }; + token.p11.C_SetAttributeValue + (session.id(), key.keyID, attrs); + if (debug != null) { + debug.println("updateP11Pkey set new alias [" + + alias + + "] for key entry"); + } + } else { + + // session key - convert to token key and set CKA_ID + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + new CK_ATTRIBUTE(CKA_ID, alias), + }; + if (attribute != null) { + attrs = addAttribute(attrs, attribute); + } + token.p11.C_CopyObject(session.id(), key.keyID, attrs); + if (debug != null) { + debug.println("updateP11Pkey copied private session key " + + "for [" + + alias + + "] to token entry"); + } + } + } finally { + token.releaseSession(session); + } + } + + private void storeCert(String alias, X509Certificate cert) + throws PKCS11Exception, CertificateException { + + ArrayList attrList = new ArrayList(); + attrList.add(ATTR_TOKEN_TRUE); + attrList.add(ATTR_CLASS_CERT); + attrList.add(ATTR_X509_CERT_TYPE); + attrList.add(new CK_ATTRIBUTE(CKA_SUBJECT, + cert.getSubjectX500Principal().getEncoded())); + attrList.add(new CK_ATTRIBUTE(CKA_ISSUER, + cert.getIssuerX500Principal().getEncoded())); + attrList.add(new CK_ATTRIBUTE(CKA_SERIAL_NUMBER, + cert.getSerialNumber().toByteArray())); + attrList.add(new CK_ATTRIBUTE(CKA_VALUE, cert.getEncoded())); + + if (alias != null) { + attrList.add(new CK_ATTRIBUTE(CKA_LABEL, alias)); + attrList.add(new CK_ATTRIBUTE(CKA_ID, alias)); + } else { + // ibutton requires something to be set + // - alias must be unique + attrList.add(new CK_ATTRIBUTE(CKA_ID, + getID(cert.getSubjectX500Principal().getName + (X500Principal.CANONICAL), cert))); + } + + Session session = null; + try { + session = token.getOpSession(); + token.p11.C_CreateObject(session.id(), + attrList.toArray(new CK_ATTRIBUTE[attrList.size()])); + } finally { + token.releaseSession(session); + } + } + + private void storeChain(String alias, X509Certificate[] chain) + throws PKCS11Exception, CertificateException { + + // add new chain + // + // end cert has CKA_LABEL and CKA_ID set to alias. + // other certs in chain have neither set. + + storeCert(alias, chain[0]); + storeCaCerts(chain, 1); + } + + private void storeCaCerts(X509Certificate[] chain, int start) + throws PKCS11Exception, CertificateException { + + // do not add duplicate CA cert if already in token + // + // XXX ibutton stores duplicate CA certs, NSS does not + + Session session = null; + HashSet cacerts = new HashSet(); + try { + session = token.getOpSession(); + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_CERT }; + long[] handles = findObjects(session, attrs); + + // load certs currently on the token + for (long handle : handles) { + cacerts.add(loadCert(session, handle)); + } + } finally { + token.releaseSession(session); + } + + for (int i = start; i < chain.length; i++) { + if (!cacerts.contains(chain[i])) { + storeCert(null, chain[i]); + } else if (debug != null) { + debug.println("ignoring duplicate CA cert for [" + + chain[i].getSubjectX500Principal() + + "]"); + } + } + } + + private void storeSkey(String alias, SecretKeyEntry ske) + throws PKCS11Exception, KeyStoreException { + + SecretKey skey = ske.getSecretKey(); + // No need to specify CKA_CLASS, CKA_KEY_TYPE, CKA_VALUE since + // they are handled in P11SecretKeyFactory.createKey() method. + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_SKEY_TOKEN_TRUE, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_LABEL, alias), + }; + try { + P11SecretKeyFactory.convertKey(token, skey, null, attrs); + } catch (InvalidKeyException ike) { + // re-throw KeyStoreException to match javadoc + throw new KeyStoreException("Cannot convert to PKCS11 keys", ike); + } + + // update global alias map + aliasMap.put(alias, new AliasInfo(alias)); + + if (debug != null) { + debug.println("storeSkey created token secret key for [" + + alias + "]"); + } + } + + private static CK_ATTRIBUTE[] addAttribute(CK_ATTRIBUTE[] attrs, CK_ATTRIBUTE attr) { + int n = attrs.length; + CK_ATTRIBUTE[] newAttrs = new CK_ATTRIBUTE[n + 1]; + System.arraycopy(attrs, 0, newAttrs, 0, n); + newAttrs[n] = attr; + return newAttrs; + } + + private void storePkey(String alias, PrivateKeyEntry pke) + throws PKCS11Exception, CertificateException, KeyStoreException { + + PrivateKey key = pke.getPrivateKey(); + CK_ATTRIBUTE[] attrs = null; + + // If the key is a token object on this token, update it instead + // of creating a duplicate key object. + // Otherwise, treat a P11Key like any other key, if is is extractable. + if (key instanceof P11Key) { + P11Key p11Key = (P11Key)key; + if (p11Key.tokenObject && (p11Key.token == this.token)) { + updateP11Pkey(alias, null, p11Key); + storeChain(alias, (X509Certificate[])pke.getCertificateChain()); + return; + } + } + + boolean useNDB = token.config.getNssNetscapeDbWorkaround(); + PublicKey publicKey = pke.getCertificate().getPublicKey(); + + if (key instanceof RSAPrivateKey) { + + X509Certificate cert = (X509Certificate)pke.getCertificate(); + attrs = getRsaPrivKeyAttrs + (alias, (RSAPrivateKey)key, cert.getSubjectX500Principal()); + + } else if (key instanceof DSAPrivateKey) { + + DSAPrivateKey dsaKey = (DSAPrivateKey)key; + + CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); + if (idAttrs[0] == null) { + idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); + } + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DSA), + idAttrs[0], + new CK_ATTRIBUTE(CKA_PRIME, dsaKey.getParams().getP()), + new CK_ATTRIBUTE(CKA_SUBPRIME, dsaKey.getParams().getQ()), + new CK_ATTRIBUTE(CKA_BASE, dsaKey.getParams().getG()), + new CK_ATTRIBUTE(CKA_VALUE, dsaKey.getX()), + }; + if (idAttrs[1] != null) { + attrs = addAttribute(attrs, idAttrs[1]); + } + + attrs = token.getAttributes + (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DSA, attrs); + + if (debug != null) { + debug.println("storePkey created DSA template"); + } + + } else if (key instanceof DHPrivateKey) { + + DHPrivateKey dhKey = (DHPrivateKey)key; + + CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); + if (idAttrs[0] == null) { + idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); + } + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_DH), + idAttrs[0], + new CK_ATTRIBUTE(CKA_PRIME, dhKey.getParams().getP()), + new CK_ATTRIBUTE(CKA_BASE, dhKey.getParams().getG()), + new CK_ATTRIBUTE(CKA_VALUE, dhKey.getX()), + }; + if (idAttrs[1] != null) { + attrs = addAttribute(attrs, idAttrs[1]); + } + + attrs = token.getAttributes + (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_DH, attrs); + + } else if (key instanceof ECPrivateKey) { + + ECPrivateKey ecKey = (ECPrivateKey)key; + + CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, useNDB); + if (idAttrs[0] == null) { + idAttrs[0] = new CK_ATTRIBUTE(CKA_ID, alias); + } + + byte[] encodedParams = + ECUtil.encodeECParameterSpec(null, ecKey.getParams()); + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_EC), + idAttrs[0], + new CK_ATTRIBUTE(CKA_VALUE, ecKey.getS()), + new CK_ATTRIBUTE(CKA_EC_PARAMS, encodedParams), + }; + if (idAttrs[1] != null) { + attrs = addAttribute(attrs, idAttrs[1]); + } + + attrs = token.getAttributes + (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_EC, attrs); + + if (debug != null) { + debug.println("storePkey created EC template"); + } + + } else if (key instanceof P11Key) { + // sensitive/non-extractable P11Key + P11Key p11Key = (P11Key)key; + if (p11Key.token != this.token) { + throw new KeyStoreException + ("Cannot move sensitive keys across tokens"); + } + CK_ATTRIBUTE netscapeDB = null; + if (useNDB) { + // Note that this currently fails due to an NSS bug. + // They do not allow the CKA_NETSCAPE_DB attribute to be + // specified during C_CopyObject() and fail with + // CKR_ATTRIBUTE_READ_ONLY. + // But if we did not specify it, they would fail with + // CKA_TEMPLATE_INCOMPLETE, so leave this code in here. + CK_ATTRIBUTE[] idAttrs = getIdAttributes(key, publicKey, false, true); + netscapeDB = idAttrs[1]; + } + // Update the key object. + updateP11Pkey(alias, netscapeDB, p11Key); + storeChain(alias, (X509Certificate[])pke.getCertificateChain()); + return; + + } else { + throw new KeyStoreException("unsupported key type: " + key); + } + + Session session = null; + try { + session = token.getOpSession(); + + // create private key entry + token.p11.C_CreateObject(session.id(), attrs); + if (debug != null) { + debug.println("storePkey created token key for [" + + alias + + "]"); + } + } finally { + token.releaseSession(session); + } + + storeChain(alias, (X509Certificate[])pke.getCertificateChain()); + } + + private CK_ATTRIBUTE[] getRsaPrivKeyAttrs(String alias, + RSAPrivateKey key, + X500Principal subject) throws PKCS11Exception { + + // subject is currently ignored - could be used to set CKA_SUBJECT + + CK_ATTRIBUTE[] attrs = null; + if (key instanceof RSAPrivateCrtKey) { + + if (debug != null) { + debug.println("creating RSAPrivateCrtKey attrs"); + } + + RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key; + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), + new CK_ATTRIBUTE(CKA_ID, alias), + new CK_ATTRIBUTE(CKA_MODULUS, + rsaKey.getModulus()), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, + rsaKey.getPrivateExponent()), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, + rsaKey.getPublicExponent()), + new CK_ATTRIBUTE(CKA_PRIME_1, + rsaKey.getPrimeP()), + new CK_ATTRIBUTE(CKA_PRIME_2, + rsaKey.getPrimeQ()), + new CK_ATTRIBUTE(CKA_EXPONENT_1, + rsaKey.getPrimeExponentP()), + new CK_ATTRIBUTE(CKA_EXPONENT_2, + rsaKey.getPrimeExponentQ()), + new CK_ATTRIBUTE(CKA_COEFFICIENT, + rsaKey.getCrtCoefficient()) }; + attrs = token.getAttributes + (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs); + + } else { + + if (debug != null) { + debug.println("creating RSAPrivateKey attrs"); + } + + RSAPrivateKey rsaKey = key; + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + ATTR_PRIVATE_TRUE, + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), + new CK_ATTRIBUTE(CKA_ID, alias), + new CK_ATTRIBUTE(CKA_MODULUS, + rsaKey.getModulus()), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, + rsaKey.getPrivateExponent()) }; + attrs = token.getAttributes + (TemplateManager.O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attrs); + } + + return attrs; + } + + /** + * Compute the CKA_ID and/or CKA_NETSCAPE_DB attributes that should be + * used for this private key. It uses the same algorithm to calculate the + * values as NSS. The public and private keys MUST match for the result to + * be correct. + * + * It returns a 2 element array with CKA_ID at index 0 and CKA_NETSCAPE_DB + * at index 1. The boolean flags determine what is to be calculated. + * If false or if we could not calculate the value, that element is null. + * + * NOTE that we currently do not use the CKA_ID value calculated by this + * method. + */ + private CK_ATTRIBUTE[] getIdAttributes(PrivateKey privateKey, + PublicKey publicKey, boolean id, boolean netscapeDb) { + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[2]; + if ((id || netscapeDb) == false) { + return attrs; + } + String alg = privateKey.getAlgorithm(); + if (id && alg.equals("RSA") && (publicKey instanceof RSAPublicKey)) { + // CKA_NETSCAPE_DB not needed for RSA public keys + BigInteger n = ((RSAPublicKey)publicKey).getModulus(); + attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(n))); + } else if (alg.equals("DSA") && (publicKey instanceof DSAPublicKey)) { + BigInteger y = ((DSAPublicKey)publicKey).getY(); + if (id) { + attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y))); + } + if (netscapeDb) { + attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y); + } + } else if (alg.equals("DH") && (publicKey instanceof DHPublicKey)) { + BigInteger y = ((DHPublicKey)publicKey).getY(); + if (id) { + attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(getMagnitude(y))); + } + if (netscapeDb) { + attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, y); + } + } else if (alg.equals("EC") && (publicKey instanceof ECPublicKey)) { + ECPublicKey ecPub = (ECPublicKey)publicKey; + ECPoint point = ecPub.getW(); + ECParameterSpec params = ecPub.getParams(); + byte[] encodedPoint = ECUtil.encodePoint(point, params.getCurve()); + if (id) { + attrs[0] = new CK_ATTRIBUTE(CKA_ID, sha1(encodedPoint)); + } + if (netscapeDb) { + attrs[1] = new CK_ATTRIBUTE(CKA_NETSCAPE_DB, encodedPoint); + } + } else { + throw new RuntimeException("Unknown key algorithm " + alg); + } + return attrs; + } + + /** + * return true if cert destroyed + */ + private boolean destroyCert(byte[] cka_id) + throws PKCS11Exception, KeyStoreException { + Session session = null; + try { + session = token.getOpSession(); + THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); + if (h.type != ATTR_CLASS_CERT) { + return false; + } + + token.p11.C_DestroyObject(session.id(), h.handle); + if (debug != null) { + debug.println("destroyCert destroyed cert with CKA_ID [" + + getID(cka_id) + + "]"); + } + return true; + } finally { + token.releaseSession(session); + } + } + + /** + * return true if chain destroyed + */ + private boolean destroyChain(byte[] cka_id) + throws PKCS11Exception, CertificateException, KeyStoreException { + + Session session = null; + try { + session = token.getOpSession(); + + THandle h = getTokenObject(session, ATTR_CLASS_CERT, cka_id, null); + if (h.type != ATTR_CLASS_CERT) { + if (debug != null) { + debug.println("destroyChain could not find " + + "end entity cert with CKA_ID [0x" + + Functions.toHexString(cka_id) + + "]"); + } + return false; + } + + X509Certificate endCert = loadCert(session, h.handle); + token.p11.C_DestroyObject(session.id(), h.handle); + if (debug != null) { + debug.println("destroyChain destroyed end entity cert " + + "with CKA_ID [" + + getID(cka_id) + + "]"); + } + + // build chain following issuer->subject links + + X509Certificate next = endCert; + while (true) { + + if (next.getSubjectX500Principal().equals + (next.getIssuerX500Principal())) { + // self signed - done + break; + } + + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_CERT, + new CK_ATTRIBUTE(CKA_SUBJECT, + next.getIssuerX500Principal().getEncoded()) }; + long[] ch = findObjects(session, attrs); + + if (ch == null || ch.length == 0) { + // done + break; + } else { + // if more than one found, use first + if (debug != null && ch.length > 1) { + debug.println("destroyChain found " + + ch.length + + " certificate entries for subject [" + + next.getIssuerX500Principal() + + "] in token - using first entry"); + } + + next = loadCert(session, ch[0]); + + // only delete if not part of any other chain + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_CERT, + new CK_ATTRIBUTE(CKA_ISSUER, + next.getSubjectX500Principal().getEncoded()) }; + long[] issuers = findObjects(session, attrs); + + boolean destroyIt = false; + if (issuers == null || issuers.length == 0) { + // no other certs with this issuer - + // destroy it + destroyIt = true; + } else if (issuers.length == 1) { + X509Certificate iCert = loadCert(session, issuers[0]); + if (next.equals(iCert)) { + // only cert with issuer is itself (self-signed) - + // destroy it + destroyIt = true; + } + } + + if (destroyIt) { + token.p11.C_DestroyObject(session.id(), ch[0]); + if (debug != null) { + debug.println + ("destroyChain destroyed cert in chain " + + "with subject [" + + next.getSubjectX500Principal() + "]"); + } + } else { + if (debug != null) { + debug.println("destroyChain did not destroy " + + "shared cert in chain with subject [" + + next.getSubjectX500Principal() + "]"); + } + } + } + } + + return true; + + } finally { + token.releaseSession(session); + } + } + + /** + * return true if secret key destroyed + */ + private boolean destroySkey(String alias) + throws PKCS11Exception, KeyStoreException { + Session session = null; + try { + session = token.getOpSession(); + + THandle h = getTokenObject(session, ATTR_CLASS_SKEY, null, alias); + if (h.type != ATTR_CLASS_SKEY) { + if (debug != null) { + debug.println("destroySkey did not find secret key " + + "with CKA_LABEL [" + + alias + + "]"); + } + return false; + } + token.p11.C_DestroyObject(session.id(), h.handle); + return true; + } finally { + token.releaseSession(session); + } + } + + /** + * return true if private key destroyed + */ + private boolean destroyPkey(byte[] cka_id) + throws PKCS11Exception, KeyStoreException { + Session session = null; + try { + session = token.getOpSession(); + + THandle h = getTokenObject(session, ATTR_CLASS_PKEY, cka_id, null); + if (h.type != ATTR_CLASS_PKEY) { + if (debug != null) { + debug.println + ("destroyPkey did not find private key with CKA_ID [" + + getID(cka_id) + + "]"); + } + return false; + } + token.p11.C_DestroyObject(session.id(), h.handle); + return true; + } finally { + token.releaseSession(session); + } + } + + /** + * build [alias + issuer + serialNumber] string from a cert + */ + private String getID(String alias, X509Certificate cert) { + X500Principal issuer = cert.getIssuerX500Principal(); + BigInteger serialNum = cert.getSerialNumber(); + + return alias + + ALIAS_SEP + + issuer.getName(X500Principal.CANONICAL) + + ALIAS_SEP + + serialNum.toString(); + } + + /** + * build CKA_ID string from bytes + */ + private static String getID(byte[] bytes) { + boolean printable = true; + for (int i = 0; i < bytes.length; i++) { + if (!DerValue.isPrintableStringChar((char)bytes[i])) { + printable = false; + break; + } + } + + if (!printable) { + return "0x" + Functions.toHexString(bytes); + } else { + try { + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException uee) { + return "0x" + Functions.toHexString(bytes); + } + } + } + + /** + * find an object on the token + * + * @param type either ATTR_CLASS_CERT, ATTR_CLASS_PKEY, or ATTR_CLASS_SKEY + * @param cka_id the CKA_ID if type is ATTR_CLASS_CERT or ATTR_CLASS_PKEY + * @param cka_label the CKA_LABEL if type is ATTR_CLASS_SKEY + */ + private THandle getTokenObject(Session session, + CK_ATTRIBUTE type, + byte[] cka_id, + String cka_label) + throws PKCS11Exception, KeyStoreException { + + CK_ATTRIBUTE[] attrs; + if (type == ATTR_CLASS_SKEY) { + attrs = new CK_ATTRIBUTE[] { + ATTR_SKEY_TOKEN_TRUE, + new CK_ATTRIBUTE(CKA_LABEL, cka_label), + type }; + } else { + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + new CK_ATTRIBUTE(CKA_ID, cka_id), + type }; + } + long[] h = findObjects(session, attrs); + if (h.length == 0) { + if (debug != null) { + if (type == ATTR_CLASS_SKEY) { + debug.println("getTokenObject did not find secret key " + + "with CKA_LABEL [" + + cka_label + + "]"); + } else if (type == ATTR_CLASS_CERT) { + debug.println + ("getTokenObject did not find cert with CKA_ID [" + + getID(cka_id) + + "]"); + } else { + debug.println("getTokenObject did not find private key " + + "with CKA_ID [" + + getID(cka_id) + + "]"); + } + } + } else if (h.length == 1) { + + // found object handle - return it + return new THandle(h[0], type); + + } else { + + // found multiple object handles - + // see if token ignored CKA_LABEL during search (e.g. NSS) + + if (type == ATTR_CLASS_SKEY) { + + ArrayList list = new ArrayList(h.length); + for (int i = 0; i < h.length; i++) { + + CK_ATTRIBUTE[] label = new CK_ATTRIBUTE[] + { new CK_ATTRIBUTE(CKA_LABEL) }; + token.p11.C_GetAttributeValue(session.id(), h[i], label); + if (label[0].pValue != null && + cka_label.equals(new String(label[0].getCharArray()))) { + list.add(new THandle(h[i], ATTR_CLASS_SKEY)); + } + } + if (list.size() == 1) { + // yes, there was only one CKA_LABEL that matched + return list.get(0); + } else { + throw new KeyStoreException("invalid KeyStore state: " + + "found " + + list.size() + + " secret keys sharing CKA_LABEL [" + + cka_label + + "]"); + } + } else if (type == ATTR_CLASS_CERT) { + throw new KeyStoreException("invalid KeyStore state: " + + "found " + + h.length + + " certificates sharing CKA_ID " + + getID(cka_id)); + } else { + throw new KeyStoreException("invalid KeyStore state: " + + "found " + + h.length + + " private keys sharing CKA_ID " + + getID(cka_id)); + } + } + return new THandle(NO_HANDLE, null); + } + + /** + * Create a mapping of all key pairs, trusted certs, and secret keys + * on the token into logical KeyStore entries unambiguously + * accessible via an alias. + * + * If the token is removed, the map may contain stale values. + * KeyStore.load should be called to re-create the map. + * + * Assume all private keys and matching certs share a unique CKA_ID. + * + * Assume all secret keys have a unique CKA_LABEL. + * + * @return true if multiple certs found sharing the same CKA_LABEL + * (if so, write capabilities are disabled) + */ + private boolean mapLabels() throws + PKCS11Exception, CertificateException, KeyStoreException { + + CK_ATTRIBUTE[] trustedAttr = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TRUSTED) }; + + Session session = null; + try { + session = token.getOpSession(); + + // get all private key CKA_IDs + + ArrayList pkeyIDs = new ArrayList(); + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_PKEY, + }; + long[] handles = findObjects(session, attrs); + + for (long handle : handles) { + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) }; + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + + if (attrs[0].pValue != null) { + pkeyIDs.add(attrs[0].getByteArray()); + } + } + + // Get all certificates + // + // If cert does not have a CKA_LABEL nor CKA_ID, it is ignored. + // + // Get the CKA_LABEL for each cert + // (if the cert does not have a CKA_LABEL, use the CKA_ID). + // + // Map each cert to the its CKA_LABEL + // (multiple certs may be mapped to a single CKA_LABEL) + + HashMap> certMap = + new HashMap>(); + + attrs = new CK_ATTRIBUTE[] { + ATTR_TOKEN_TRUE, + ATTR_CLASS_CERT, + }; + handles = findObjects(session, attrs); + + for (long handle : handles) { + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) }; + + String cka_label = null; + byte[] cka_id = null; + try { + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + if (attrs[0].pValue != null) { + // there is a CKA_LABEL + cka_label = new String(attrs[0].getCharArray()); + } + } catch (PKCS11Exception pe) { + if (pe.getErrorCode() != CKR_ATTRIBUTE_TYPE_INVALID) { + throw pe; + } + + // GetAttributeValue for CKA_LABEL not supported + // + // XXX SCA1000 + } + + // get CKA_ID + + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_ID) }; + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + if (attrs[0].pValue == null) { + if (cka_label == null) { + // no cka_label nor cka_id - ignore + continue; + } + } else { + if (cka_label == null) { + // use CKA_ID as CKA_LABEL + cka_label = getID(attrs[0].getByteArray()); + } + cka_id = attrs[0].getByteArray(); + } + + X509Certificate cert = loadCert(session, handle); + + // get CKA_TRUSTED + + boolean cka_trusted = false; + + if (useSecmodTrust) { + cka_trusted = Secmod.getInstance().isTrusted(cert, nssTrustType); + } else { + if (CKA_TRUSTED_SUPPORTED) { + try { + token.p11.C_GetAttributeValue + (session.id(), handle, trustedAttr); + cka_trusted = trustedAttr[0].getBoolean(); + } catch (PKCS11Exception pe) { + if (pe.getErrorCode() == CKR_ATTRIBUTE_TYPE_INVALID) { + // XXX NSS, ibutton, sca1000 + CKA_TRUSTED_SUPPORTED = false; + if (debug != null) { + debug.println + ("CKA_TRUSTED attribute not supported"); + } + } + } + } + } + + HashSet infoSet = certMap.get(cka_label); + if (infoSet == null) { + infoSet = new HashSet(2); + certMap.put(cka_label, infoSet); + } + + // initially create private key entry AliasInfo entries - + // these entries will get resolved into their true + // entry types later + + infoSet.add(new AliasInfo + (cka_label, + cka_id, + cka_trusted, + cert)); + } + + // create list secret key CKA_LABELS - + // if there are duplicates (either between secret keys, + // or between a secret key and another object), + // throw an exception + HashMap sKeyMap = + new HashMap(); + + attrs = new CK_ATTRIBUTE[] { + ATTR_SKEY_TOKEN_TRUE, + ATTR_CLASS_SKEY, + }; + handles = findObjects(session, attrs); + + for (long handle : handles) { + attrs = new CK_ATTRIBUTE[] { new CK_ATTRIBUTE(CKA_LABEL) }; + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + if (attrs[0].pValue != null) { + + // there is a CKA_LABEL + String cka_label = new String(attrs[0].getCharArray()); + if (sKeyMap.get(cka_label) == null) { + sKeyMap.put(cka_label, new AliasInfo(cka_label)); + } else { + throw new KeyStoreException("invalid KeyStore state: " + + "found multiple secret keys sharing same " + + "CKA_LABEL [" + + cka_label + + "]"); + } + } + } + + // update global aliasMap with alias mappings + ArrayList matchedCerts = + mapPrivateKeys(pkeyIDs, certMap); + boolean sharedLabel = mapCerts(matchedCerts, certMap); + mapSecretKeys(sKeyMap); + + return sharedLabel; + + } finally { + token.releaseSession(session); + } + } + + /** + * for each private key CKA_ID, find corresponding cert with same CKA_ID. + * if found cert, see if cert CKA_LABEL is unique. + * if CKA_LABEL unique, map private key/cert alias to that CKA_LABEL. + * if CKA_LABEL not unique, map private key/cert alias to: + * CKA_LABEL + ALIAS_SEP + ISSUER + ALIAS_SEP + SERIAL + * if cert not found, ignore private key + * (don't support private key entries without a cert chain yet) + * + * @return a list of AliasInfo entries that represents all matches + */ + private ArrayList mapPrivateKeys(ArrayList pkeyIDs, + HashMap> certMap) + throws PKCS11Exception, CertificateException { + + // reset global alias map + aliasMap = new HashMap(); + + // list of matched certs that we will return + ArrayList matchedCerts = new ArrayList(); + + for (byte[] pkeyID : pkeyIDs) { + + // try to find a matching CKA_ID in a certificate + + boolean foundMatch = false; + Set certLabels = certMap.keySet(); + for (String certLabel : certLabels) { + + // get cert CKA_IDs (if present) for each cert + + HashSet infoSet = certMap.get(certLabel); + for (AliasInfo aliasInfo : infoSet) { + if (Arrays.equals(pkeyID, aliasInfo.id)) { + + // found private key with matching cert + + if (infoSet.size() == 1) { + // unique CKA_LABEL - use certLabel as alias + aliasInfo.matched = true; + aliasMap.put(certLabel, aliasInfo); + } else { + // create new alias + aliasInfo.matched = true; + aliasMap.put(getID(certLabel, aliasInfo.cert), + aliasInfo); + } + matchedCerts.add(aliasInfo); + foundMatch = true; + break; + } + } + if (foundMatch) { + break; + } + } + + if (!foundMatch) { + if (debug != null) { + debug.println + ("did not find match for private key with CKA_ID [" + + getID(pkeyID) + + "] (ignoring entry)"); + } + } + } + + return matchedCerts; + } + + /** + * for each cert not matched with a private key but is CKA_TRUSTED: + * if CKA_LABEL unique, map cert to CKA_LABEL. + * if CKA_LABEL not unique, map cert to [label+issuer+serialNum] + * + * if CKA_TRUSTED not supported, treat all certs not part of a chain + * as trusted + * + * @return true if multiple certs found sharing the same CKA_LABEL + */ + private boolean mapCerts(ArrayList matchedCerts, + HashMap> certMap) + throws PKCS11Exception, CertificateException { + + // load all cert chains + for (AliasInfo aliasInfo : matchedCerts) { + Session session = null; + try { + session = token.getOpSession(); + aliasInfo.chain = loadChain(session, aliasInfo.cert); + } finally { + token.releaseSession(session); + } + } + + // find all certs in certMap not part of a cert chain + // - these are trusted + + boolean sharedLabel = false; + + Set certLabels = certMap.keySet(); + for (String certLabel : certLabels) { + HashSet infoSet = certMap.get(certLabel); + for (AliasInfo aliasInfo : infoSet) { + + if (aliasInfo.matched == true) { + // already found a private key match for this cert - + // just continue + aliasInfo.trusted = false; + continue; + } + + // cert in this aliasInfo is not matched yet + // + // if CKA_TRUSTED_SUPPORTED == true, + // then check if cert is trusted + + if (CKA_TRUSTED_SUPPORTED) { + if (aliasInfo.trusted) { + // trusted certificate + if (mapTrustedCert + (certLabel, aliasInfo, infoSet) == true) { + sharedLabel = true; + } + } + continue; + } + + // CKA_TRUSTED_SUPPORTED == false + // + // XXX treat all certs not part of a chain as trusted + // XXX + // XXX Unsupported + // + // boolean partOfChain = false; + // for (AliasInfo matchedInfo : matchedCerts) { + // for (int i = 0; i < matchedInfo.chain.length; i++) { + // if (matchedInfo.chain[i].equals(aliasInfo.cert)) { + // partOfChain = true; + // break; + // } + // } + // if (partOfChain) { + // break; + // } + // } + // + // if (!partOfChain) { + // if (mapTrustedCert(certLabel,aliasInfo,infoSet) == true){ + // sharedLabel = true; + // } + // } else { + // if (debug != null) { + // debug.println("ignoring unmatched/untrusted cert " + + // "that is part of cert chain - cert subject is [" + + // aliasInfo.cert.getSubjectX500Principal().getName + // (X500Principal.CANONICAL) + + // "]"); + // } + // } + } + } + + return sharedLabel; + } + + private boolean mapTrustedCert(String certLabel, + AliasInfo aliasInfo, + HashSet infoSet) { + + boolean sharedLabel = false; + + aliasInfo.type = ATTR_CLASS_CERT; + aliasInfo.trusted = true; + if (infoSet.size() == 1) { + // unique CKA_LABEL - use certLabel as alias + aliasMap.put(certLabel, aliasInfo); + } else { + // create new alias + sharedLabel = true; + aliasMap.put(getID(certLabel, aliasInfo.cert), aliasInfo); + } + + return sharedLabel; + } + + /** + * If the secret key shares a CKA_LABEL with another entry, + * throw an exception + */ + private void mapSecretKeys(HashMap sKeyMap) + throws KeyStoreException { + for (String label : sKeyMap.keySet()) { + if (aliasMap.containsKey(label)) { + throw new KeyStoreException("invalid KeyStore state: " + + "found secret key sharing CKA_LABEL [" + + label + + "] with another token object"); + } + } + aliasMap.putAll(sKeyMap); + } + + private void dumpTokenMap() { + Set aliases = aliasMap.keySet(); + System.out.println("Token Alias Map:"); + if (aliases.isEmpty()) { + System.out.println(" [empty]"); + } else { + for (String s : aliases) { + System.out.println(" " + s + aliasMap.get(s)); + } + } + } + + private void checkWrite() throws KeyStoreException { + if (writeDisabled) { + throw new KeyStoreException + ("This PKCS11KeyStore does not support write capabilities"); + } + } + + private final static long[] LONG0 = new long[0]; + + private static long[] findObjects(Session session, CK_ATTRIBUTE[] attrs) + throws PKCS11Exception { + Token token = session.token; + long[] handles = LONG0; + token.p11.C_FindObjectsInit(session.id(), attrs); + while (true) { + long[] h = token.p11.C_FindObjects(session.id(), FINDOBJECTS_MAX); + if (h.length == 0) { + break; + } + handles = P11Util.concat(handles, h); + } + token.p11.C_FindObjectsFinal(session.id()); + return handles; + } + +} diff --git a/src/sun/security/pkcs11/P11Mac.java b/src/sun/security/pkcs11/P11Mac.java new file mode 100644 index 00000000..e5458c94 --- /dev/null +++ b/src/sun/security/pkcs11/P11Mac.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.MacSpi; + +import sun.nio.ch.DirectBuffer; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * MAC implementation class. This class currently supports HMAC using + * MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512 and the SSL3 MAC + * using MD5 and SHA-1. + * + * Note that unlike other classes (e.g. Signature), this does not + * composite various operations if the token only supports part of the + * required functionality. The MAC implementations in SunJCE already + * do exactly that by implementing an MAC on top of MessageDigests. We + * could not do any better than they. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11Mac extends MacSpi { + + /* unitialized, all fields except session have arbitrary values */ + private final static int S_UNINIT = 1; + + /* session initialized, no data processed yet */ + private final static int S_RESET = 2; + + /* session initialized, data processed */ + private final static int S_UPDATE = 3; + + /* transitional state after doFinal() before we go to S_UNINIT */ + private final static int S_DOFINAL = 4; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private final long mechanism; + + // mechanism object + private final CK_MECHANISM ckMechanism; + + // length of the MAC in bytes + private final int macLength; + + // key instance used, if operation active + private P11Key p11Key; + + // associated session, if any + private Session session; + + // state, one of S_* above + private int state; + + // one byte buffer for the update(byte) method, initialized on demand + private byte[] oneByte; + + P11Mac(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + Long params = null; + switch ((int)mechanism) { + case (int)CKM_MD5_HMAC: + macLength = 16; + break; + case (int)CKM_SHA_1_HMAC: + macLength = 20; + break; + case (int)CKM_SHA224_HMAC: + macLength = 28; + break; + case (int)CKM_SHA256_HMAC: + macLength = 32; + break; + case (int)CKM_SHA384_HMAC: + macLength = 48; + break; + case (int)CKM_SHA512_HMAC: + macLength = 64; + break; + case (int)CKM_SSL3_MD5_MAC: + macLength = 16; + params = Long.valueOf(16); + break; + case (int)CKM_SSL3_SHA1_MAC: + macLength = 20; + params = Long.valueOf(20); + break; + default: + throw new ProviderException("Unknown mechanism: " + mechanism); + } + ckMechanism = new CK_MECHANISM(mechanism, params); + state = S_UNINIT; + initialize(); + } + + private void ensureInitialized() throws PKCS11Exception { + token.ensureValid(); + if (state == S_UNINIT) { + initialize(); + } + } + + private void cancelOperation() { + token.ensureValid(); + if (state == S_UNINIT) { + return; + } + state = S_UNINIT; + if ((session == null) || (token.explicitCancel == false)) { + return; + } + try { + token.p11.C_SignFinal(session.id(), 0); + } catch (PKCS11Exception e) { + throw new ProviderException("Cancel failed", e); + } + } + + private void initialize() throws PKCS11Exception { + if (state == S_RESET) { + return; + } + if (session == null) { + session = token.getOpSession(); + } + if (p11Key != null) { + token.p11.C_SignInit + (session.id(), ckMechanism, p11Key.keyID); + state = S_RESET; + } else { + state = S_UNINIT; + } + } + + // see JCE spec + protected int engineGetMacLength() { + return macLength; + } + + // see JCE spec + protected void engineReset() { + // the framework insists on calling reset() after doFinal(), + // but we prefer to take care of reinitialization ourselves + if (state == S_DOFINAL) { + state = S_UNINIT; + return; + } + cancelOperation(); + try { + initialize(); + } catch (PKCS11Exception e) { + throw new ProviderException("reset() failed, ", e); + } + } + + // see JCE spec + protected void engineInit(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException + ("Parameters not supported"); + } + cancelOperation(); + p11Key = P11SecretKeyFactory.convertKey(token, key, algorithm); + try { + initialize(); + } catch (PKCS11Exception e) { + throw new InvalidKeyException("init() failed", e); + } + } + + // see JCE spec + protected byte[] engineDoFinal() { + try { + ensureInitialized(); + byte[] mac = token.p11.C_SignFinal(session.id(), 0); + state = S_DOFINAL; + return mac; + } catch (PKCS11Exception e) { + throw new ProviderException("doFinal() failed", e); + } finally { + session = token.releaseSession(session); + } + } + + // see JCE spec + protected void engineUpdate(byte input) { + if (oneByte == null) { + oneByte = new byte[1]; + } + oneByte[0] = input; + engineUpdate(oneByte, 0, 1); + } + + // see JCE spec + protected void engineUpdate(byte[] b, int ofs, int len) { + try { + ensureInitialized(); + token.p11.C_SignUpdate(session.id(), 0, b, ofs, len); + state = S_UPDATE; + } catch (PKCS11Exception e) { + throw new ProviderException("update() failed", e); + } + } + + // see JCE spec + protected void engineUpdate(ByteBuffer byteBuffer) { + try { + ensureInitialized(); + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + if (byteBuffer instanceof DirectBuffer == false) { + super.engineUpdate(byteBuffer); + return; + } + long addr = ((DirectBuffer)byteBuffer).address(); + int ofs = byteBuffer.position(); + token.p11.C_SignUpdate(session.id(), addr + ofs, null, 0, len); + byteBuffer.position(ofs + len); + state = S_UPDATE; + } catch (PKCS11Exception e) { + throw new ProviderException("update() failed", e); + } + } +} diff --git a/src/sun/security/pkcs11/P11RSACipher.java b/src/sun/security/pkcs11/P11RSACipher.java new file mode 100644 index 00000000..56ae2984 --- /dev/null +++ b/src/sun/security/pkcs11/P11RSACipher.java @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.*; + +import java.util.Locale; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; +import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; +import sun.security.util.KeyUtil; + +/** + * RSA Cipher implementation class. We currently only support + * PKCS#1 v1.5 padding on top of CKM_RSA_PKCS. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11RSACipher extends CipherSpi { + + // minimum length of PKCS#1 v1.5 padding + private final static int PKCS1_MIN_PADDING_LENGTH = 11; + + // constant byte[] of length 0 + private final static byte[] B0 = new byte[0]; + + // mode constant for public key encryption + private final static int MODE_ENCRYPT = 1; + // mode constant for private key decryption + private final static int MODE_DECRYPT = 2; + // mode constant for private key encryption (signing) + private final static int MODE_SIGN = 3; + // mode constant for public key decryption (verifying) + private final static int MODE_VERIFY = 4; + + // padding type constant for NoPadding + private final static int PAD_NONE = 1; + // padding type constant for PKCS1Padding + private final static int PAD_PKCS1 = 2; + + // token instance + private final Token token; + + // algorithm name (always "RSA") + private final String algorithm; + + // mechanism id + private final long mechanism; + + // associated session, if any + private Session session; + + // mode, one of MODE_* above + private int mode; + + // padding, one of PAD_* above + private int padType; + + private byte[] buffer; + private int bufOfs; + + // key, if init() was called + private P11Key p11Key; + + // flag indicating whether an operation is initialized + private boolean initialized; + + // maximum input data size allowed + // for decryption, this is the length of the key + // for encryption, length of the key minus minimum padding length + private int maxInputSize; + + // maximum output size. this is the length of the key + private int outputSize; + + // cipher parameter for TLS RSA premaster secret + private AlgorithmParameterSpec spec = null; + + // the source of randomness + private SecureRandom random; + + P11RSACipher(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = "RSA"; + this.mechanism = mechanism; + } + + // modes do not make sense for RSA, but allow ECB + // see JCE spec + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + if (mode.equalsIgnoreCase("ECB") == false) { + throw new NoSuchAlgorithmException("Unsupported mode " + mode); + } + } + + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + String lowerPadding = padding.toLowerCase(Locale.ENGLISH); + if (lowerPadding.equals("pkcs1padding")) { + padType = PAD_PKCS1; + } else if (lowerPadding.equals("nopadding")) { + padType = PAD_NONE; + } else { + throw new NoSuchPaddingException("Unsupported padding " + padding); + } + } + + // return 0 as block size, we are not a block cipher + // see JCE spec + protected int engineGetBlockSize() { + return 0; + } + + // return the output size + // see JCE spec + protected int engineGetOutputSize(int inputLen) { + return outputSize; + } + + // no IV, return null + // see JCE spec + protected byte[] engineGetIV() { + return null; + } + + // no parameters, return null + // see JCE spec + protected AlgorithmParameters engineGetParameters() { + return null; + } + + // see JCE spec + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + implInit(opmode, key); + } + + // see JCE spec + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Parameters not supported"); + } + spec = params; + this.random = random; // for TLS RSA premaster secret + } + implInit(opmode, key); + } + + // see JCE spec + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "Parameters not supported"); + } + implInit(opmode, key); + } + + private void implInit(int opmode, Key key) throws InvalidKeyException { + cancelOperation(); + p11Key = P11KeyFactory.convertKey(token, key, algorithm); + boolean encrypt; + if (opmode == Cipher.ENCRYPT_MODE) { + encrypt = true; + } else if (opmode == Cipher.DECRYPT_MODE) { + encrypt = false; + } else if (opmode == Cipher.WRAP_MODE) { + if (p11Key.isPublic() == false) { + throw new InvalidKeyException + ("Wrap has to be used with public keys"); + } + // No further setup needed for C_Wrap(). We'll initialize later if + // we can't use C_Wrap(). + return; + } else if (opmode == Cipher.UNWRAP_MODE) { + if (p11Key.isPrivate() == false) { + throw new InvalidKeyException + ("Unwrap has to be used with private keys"); + } + // No further setup needed for C_Unwrap(). We'll initialize later + // if we can't use C_Unwrap(). + return; + } else { + throw new InvalidKeyException("Unsupported mode: " + opmode); + } + if (p11Key.isPublic()) { + mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY; + } else if (p11Key.isPrivate()) { + mode = encrypt ? MODE_SIGN : MODE_DECRYPT; + } else { + throw new InvalidKeyException("Unknown key type: " + p11Key); + } + int n = (p11Key.length() + 7) >> 3; + outputSize = n; + buffer = new byte[n]; + maxInputSize = ((padType == PAD_PKCS1 && encrypt) ? + (n - PKCS1_MIN_PADDING_LENGTH) : n); + try { + initialize(); + } catch (PKCS11Exception e) { + throw new InvalidKeyException("init() failed", e); + } + } + + private void cancelOperation() { + token.ensureValid(); + if (initialized == false) { + return; + } + initialized = false; + if ((session == null) || (token.explicitCancel == false)) { + return; + } + if (session.hasObjects() == false) { + session = token.killSession(session); + return; + } + try { + PKCS11 p11 = token.p11; + int inLen = maxInputSize; + int outLen = buffer.length; + switch (mode) { + case MODE_ENCRYPT: + p11.C_Encrypt + (session.id(), buffer, 0, inLen, buffer, 0, outLen); + break; + case MODE_DECRYPT: + p11.C_Decrypt + (session.id(), buffer, 0, inLen, buffer, 0, outLen); + break; + case MODE_SIGN: + byte[] tmpBuffer = new byte[maxInputSize]; + p11.C_Sign + (session.id(), tmpBuffer); + break; + case MODE_VERIFY: + p11.C_VerifyRecover + (session.id(), buffer, 0, inLen, buffer, 0, outLen); + break; + default: + throw new ProviderException("internal error"); + } + } catch (PKCS11Exception e) { + // XXX ensure this always works, ignore error + } + } + + private void ensureInitialized() throws PKCS11Exception { + token.ensureValid(); + if (initialized == false) { + initialize(); + } + } + + private void initialize() throws PKCS11Exception { + if (session == null) { + session = token.getOpSession(); + } + PKCS11 p11 = token.p11; + CK_MECHANISM ckMechanism = new CK_MECHANISM(mechanism); + switch (mode) { + case MODE_ENCRYPT: + p11.C_EncryptInit(session.id(), ckMechanism, p11Key.keyID); + break; + case MODE_DECRYPT: + p11.C_DecryptInit(session.id(), ckMechanism, p11Key.keyID); + break; + case MODE_SIGN: + p11.C_SignInit(session.id(), ckMechanism, p11Key.keyID); + break; + case MODE_VERIFY: + p11.C_VerifyRecoverInit(session.id(), ckMechanism, p11Key.keyID); + break; + default: + throw new AssertionError("internal error"); + } + bufOfs = 0; + initialized = true; + } + + private void implUpdate(byte[] in, int inOfs, int inLen) { + try { + ensureInitialized(); + } catch (PKCS11Exception e) { + throw new ProviderException("update() failed", e); + } + if ((inLen == 0) || (in == null)) { + return; + } + if (bufOfs + inLen > maxInputSize) { + bufOfs = maxInputSize + 1; + return; + } + System.arraycopy(in, inOfs, buffer, bufOfs, inLen); + bufOfs += inLen; + } + + private int implDoFinal(byte[] out, int outOfs, int outLen) + throws BadPaddingException, IllegalBlockSizeException { + if (bufOfs > maxInputSize) { + throw new IllegalBlockSizeException("Data must not be longer " + + "than " + maxInputSize + " bytes"); + } + try { + ensureInitialized(); + PKCS11 p11 = token.p11; + int n; + switch (mode) { + case MODE_ENCRYPT: + n = p11.C_Encrypt + (session.id(), buffer, 0, bufOfs, out, outOfs, outLen); + break; + case MODE_DECRYPT: + n = p11.C_Decrypt + (session.id(), buffer, 0, bufOfs, out, outOfs, outLen); + break; + case MODE_SIGN: + byte[] tmpBuffer = new byte[bufOfs]; + System.arraycopy(buffer, 0, tmpBuffer, 0, bufOfs); + tmpBuffer = p11.C_Sign(session.id(), tmpBuffer); + if (tmpBuffer.length > outLen) { + throw new BadPaddingException("Output buffer too small"); + } + System.arraycopy(tmpBuffer, 0, out, outOfs, tmpBuffer.length); + n = tmpBuffer.length; + break; + case MODE_VERIFY: + n = p11.C_VerifyRecover + (session.id(), buffer, 0, bufOfs, out, outOfs, outLen); + break; + default: + throw new ProviderException("internal error"); + } + return n; + } catch (PKCS11Exception e) { + throw (BadPaddingException)new BadPaddingException + ("doFinal() failed").initCause(e); + } finally { + initialized = false; + session = token.releaseSession(session); + } + } + + // see JCE spec + protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { + implUpdate(in, inOfs, inLen); + return B0; + } + + // see JCE spec + protected int engineUpdate(byte[] in, int inOfs, int inLen, + byte[] out, int outOfs) throws ShortBufferException { + implUpdate(in, inOfs, inLen); + return 0; + } + + // see JCE spec + protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) + throws IllegalBlockSizeException, BadPaddingException { + implUpdate(in, inOfs, inLen); + int n = implDoFinal(buffer, 0, buffer.length); + byte[] out = new byte[n]; + System.arraycopy(buffer, 0, out, 0, n); + return out; + } + + // see JCE spec + protected int engineDoFinal(byte[] in, int inOfs, int inLen, + byte[] out, int outOfs) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + implUpdate(in, inOfs, inLen); + return implDoFinal(out, outOfs, out.length - outOfs); + } + + private byte[] doFinal() throws BadPaddingException, + IllegalBlockSizeException { + byte[] t = new byte[2048]; + int n = implDoFinal(t, 0, t.length); + byte[] out = new byte[n]; + System.arraycopy(t, 0, out, 0, n); + return out; + } + + // see JCE spec + protected byte[] engineWrap(Key key) throws InvalidKeyException, + IllegalBlockSizeException { + String keyAlg = key.getAlgorithm(); + P11Key sKey = null; + try { + // The conversion may fail, e.g. trying to wrap an AES key on + // a token that does not support AES, or when the key size is + // not within the range supported by the token. + sKey = P11SecretKeyFactory.convertKey(token, key, keyAlg); + } catch (InvalidKeyException ike) { + byte[] toBeWrappedKey = key.getEncoded(); + if (toBeWrappedKey == null) { + throw new InvalidKeyException + ("wrap() failed, no encoding available", ike); + } + // Directly encrypt the key encoding when key conversion failed + implInit(Cipher.ENCRYPT_MODE, p11Key); + implUpdate(toBeWrappedKey, 0, toBeWrappedKey.length); + try { + return doFinal(); + } catch (BadPaddingException bpe) { + // should not occur + throw new InvalidKeyException("wrap() failed", bpe); + } finally { + // Restore original mode + implInit(Cipher.WRAP_MODE, p11Key); + } + } + Session s = null; + try { + s = token.getOpSession(); + return token.p11.C_WrapKey(s.id(), new CK_MECHANISM(mechanism), + p11Key.keyID, sKey.keyID); + } catch (PKCS11Exception e) { + throw new InvalidKeyException("wrap() failed", e); + } finally { + token.releaseSession(s); + } + } + + // see JCE spec + protected Key engineUnwrap(byte[] wrappedKey, String algorithm, + int type) throws InvalidKeyException, NoSuchAlgorithmException { + + boolean isTlsRsaPremasterSecret = + algorithm.equals("TlsRsaPremasterSecret"); + Exception failover = null; + + SecureRandom secureRandom = random; + if (secureRandom == null && isTlsRsaPremasterSecret) { + secureRandom = new SecureRandom(); + } + + // Should C_Unwrap be preferred for non-TLS RSA premaster secret? + if (token.supportsRawSecretKeyImport()) { + // XXX implement unwrap using C_Unwrap() for all keys + implInit(Cipher.DECRYPT_MODE, p11Key); + if (wrappedKey.length > maxInputSize) { + throw new InvalidKeyException("Key is too long for unwrapping"); + } + + byte[] encoded = null; + implUpdate(wrappedKey, 0, wrappedKey.length); + try { + encoded = doFinal(); + } catch (BadPaddingException e) { + if (isTlsRsaPremasterSecret) { + failover = e; + } else { + throw new InvalidKeyException("Unwrapping failed", e); + } + } catch (IllegalBlockSizeException e) { + // should not occur, handled with length check above + throw new InvalidKeyException("Unwrapping failed", e); + } + + if (isTlsRsaPremasterSecret) { + if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { + throw new IllegalStateException( + "No TlsRsaPremasterSecretParameterSpec specified"); + } + + // polish the TLS premaster secret + TlsRsaPremasterSecretParameterSpec psps = + (TlsRsaPremasterSecretParameterSpec)spec; + encoded = KeyUtil.checkTlsPreMasterSecretKey( + psps.getClientVersion(), psps.getServerVersion(), + secureRandom, encoded, (failover != null)); + } + + return ConstructKeys.constructKey(encoded, algorithm, type); + } else { + Session s = null; + SecretKey secretKey = null; + try { + try { + s = token.getObjSession(); + long keyType = CKK_GENERIC_SECRET; + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), + }; + attributes = token.getAttributes( + O_IMPORT, CKO_SECRET_KEY, keyType, attributes); + long keyID = token.p11.C_UnwrapKey(s.id(), + new CK_MECHANISM(mechanism), p11Key.keyID, + wrappedKey, attributes); + secretKey = P11Key.secretKey(s, keyID, + algorithm, 48 << 3, attributes); + } catch (PKCS11Exception e) { + if (isTlsRsaPremasterSecret) { + failover = e; + } else { + throw new InvalidKeyException("unwrap() failed", e); + } + } + + if (isTlsRsaPremasterSecret) { + byte[] replacer = new byte[48]; + if (failover == null) { + // Does smart compiler dispose this operation? + secureRandom.nextBytes(replacer); + } + + TlsRsaPremasterSecretParameterSpec psps = + (TlsRsaPremasterSecretParameterSpec)spec; + + // Please use the tricky failover and replacer byte array + // as the parameters so that smart compiler won't dispose + // the unused variable . + secretKey = polishPreMasterSecretKey(token, s, + failover, replacer, secretKey, + psps.getClientVersion(), psps.getServerVersion()); + } + + return secretKey; + } finally { + token.releaseSession(s); + } + } + } + + // see JCE spec + protected int engineGetKeySize(Key key) throws InvalidKeyException { + int n = P11KeyFactory.convertKey(token, key, algorithm).length(); + return n; + } + + private static SecretKey polishPreMasterSecretKey( + Token token, Session session, + Exception failover, byte[] replacer, SecretKey secretKey, + int clientVersion, int serverVersion) { + + if (failover != null) { + CK_VERSION version = new CK_VERSION( + (clientVersion >>> 8) & 0xFF, clientVersion & 0xFF); + try { + CK_ATTRIBUTE[] attributes = token.getAttributes( + O_GENERATE, CKO_SECRET_KEY, + CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]); + long keyID = token.p11.C_GenerateKey(session.id(), + // new CK_MECHANISM(CKM_TLS_PRE_MASTER_KEY_GEN, version), + new CK_MECHANISM(CKM_SSL3_PRE_MASTER_KEY_GEN, version), + attributes); + return P11Key.secretKey(session, + keyID, "TlsRsaPremasterSecret", 48 << 3, attributes); + } catch (PKCS11Exception e) { + throw new ProviderException( + "Could not generate premaster secret", e); + } + } + + return secretKey; + } + +} + +final class ConstructKeys { + /** + * Construct a public key from its encoding. + * + * @param encodedKey the encoding of a public key. + * + * @param encodedKeyAlgorithm the algorithm the encodedKey is for. + * + * @return a public key constructed from the encodedKey. + */ + private static final PublicKey constructPublicKey(byte[] encodedKey, + String encodedKeyAlgorithm) + throws InvalidKeyException, NoSuchAlgorithmException { + try { + KeyFactory keyFactory = + KeyFactory.getInstance(encodedKeyAlgorithm); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); + return keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException nsae) { + throw new NoSuchAlgorithmException("No installed providers " + + "can create keys for the " + + encodedKeyAlgorithm + + "algorithm", nsae); + } catch (InvalidKeySpecException ike) { + throw new InvalidKeyException("Cannot construct public key", ike); + } + } + + /** + * Construct a private key from its encoding. + * + * @param encodedKey the encoding of a private key. + * + * @param encodedKeyAlgorithm the algorithm the wrapped key is for. + * + * @return a private key constructed from the encodedKey. + */ + private static final PrivateKey constructPrivateKey(byte[] encodedKey, + String encodedKeyAlgorithm) throws InvalidKeyException, + NoSuchAlgorithmException { + try { + KeyFactory keyFactory = + KeyFactory.getInstance(encodedKeyAlgorithm); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); + return keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException nsae) { + throw new NoSuchAlgorithmException("No installed providers " + + "can create keys for the " + + encodedKeyAlgorithm + + "algorithm", nsae); + } catch (InvalidKeySpecException ike) { + throw new InvalidKeyException("Cannot construct private key", ike); + } + } + + /** + * Construct a secret key from its encoding. + * + * @param encodedKey the encoding of a secret key. + * + * @param encodedKeyAlgorithm the algorithm the secret key is for. + * + * @return a secret key constructed from the encodedKey. + */ + private static final SecretKey constructSecretKey(byte[] encodedKey, + String encodedKeyAlgorithm) { + return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); + } + + static final Key constructKey(byte[] encoding, String keyAlgorithm, + int keyType) throws InvalidKeyException, NoSuchAlgorithmException { + switch (keyType) { + case Cipher.SECRET_KEY: + return constructSecretKey(encoding, keyAlgorithm); + case Cipher.PRIVATE_KEY: + return constructPrivateKey(encoding, keyAlgorithm); + case Cipher.PUBLIC_KEY: + return constructPublicKey(encoding, keyAlgorithm); + default: + throw new InvalidKeyException("Unknown keytype " + keyType); + } + } +} diff --git a/src/sun/security/pkcs11/P11RSAKeyFactory.java b/src/sun/security/pkcs11/P11RSAKeyFactory.java new file mode 100644 index 00000000..586c3164 --- /dev/null +++ b/src/sun/security/pkcs11/P11RSAKeyFactory.java @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.rsa.RSAPublicKeyImpl; +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +import sun.security.rsa.RSAKeyFactory; + +/** + * RSA KeyFactory implementation. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11RSAKeyFactory extends P11KeyFactory { + + P11RSAKeyFactory(Token token, String algorithm) { + super(token, algorithm); + } + + PublicKey implTranslatePublicKey(PublicKey key) throws InvalidKeyException { + try { + if (key instanceof RSAPublicKey) { + RSAPublicKey rsaKey = (RSAPublicKey)key; + return generatePublic( + rsaKey.getModulus(), + rsaKey.getPublicExponent() + ); + } else if ("X.509".equals(key.getFormat())) { + // let SunRsaSign provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + key = RSAPublicKeyImpl.newKey(encoded); + return implTranslatePublicKey(key); + } else { + throw new InvalidKeyException("PublicKey must be instance " + + "of RSAPublicKey or have X.509 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create RSA public key", e); + } + } + + PrivateKey implTranslatePrivateKey(PrivateKey key) + throws InvalidKeyException { + try { + if (key instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key; + return generatePrivate( + rsaKey.getModulus(), + rsaKey.getPublicExponent(), + rsaKey.getPrivateExponent(), + rsaKey.getPrimeP(), + rsaKey.getPrimeQ(), + rsaKey.getPrimeExponentP(), + rsaKey.getPrimeExponentQ(), + rsaKey.getCrtCoefficient() + ); + } else if (key instanceof RSAPrivateKey) { + RSAPrivateKey rsaKey = (RSAPrivateKey)key; + return generatePrivate( + rsaKey.getModulus(), + rsaKey.getPrivateExponent() + ); + } else if ("PKCS#8".equals(key.getFormat())) { + // let SunRsaSign provider parse for us, then recurse + byte[] encoded = key.getEncoded(); + key = sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(encoded); + return implTranslatePrivateKey(key); + } else { + throw new InvalidKeyException("Private key must be instance " + + "of RSAPrivate(Crt)Key or have PKCS#8 encoding"); + } + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create RSA private key", e); + } + } + + // see JCA spec + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof X509EncodedKeySpec) { + try { + byte[] encoded = ((X509EncodedKeySpec)keySpec).getEncoded(); + PublicKey key = RSAPublicKeyImpl.newKey(encoded); + return implTranslatePublicKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException + ("Could not create RSA public key", e); + } + } + if (keySpec instanceof RSAPublicKeySpec == false) { + throw new InvalidKeySpecException("Only RSAPublicKeySpec and " + + "X509EncodedKeySpec supported for RSA public keys"); + } + try { + RSAPublicKeySpec rs = (RSAPublicKeySpec)keySpec; + return generatePublic( + rs.getModulus(), + rs.getPublicExponent() + ); + } catch (PKCS11Exception | InvalidKeyException e) { + throw new InvalidKeySpecException + ("Could not create RSA public key", e); + } + } + + // see JCA spec + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec instanceof PKCS8EncodedKeySpec) { + try { + byte[] encoded = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); + PrivateKey key = + sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(encoded); + return implTranslatePrivateKey(key); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException + ("Could not create RSA private key", e); + } + } + try { + if (keySpec instanceof RSAPrivateCrtKeySpec) { + RSAPrivateCrtKeySpec rs = (RSAPrivateCrtKeySpec)keySpec; + return generatePrivate( + rs.getModulus(), + rs.getPublicExponent(), + rs.getPrivateExponent(), + rs.getPrimeP(), + rs.getPrimeQ(), + rs.getPrimeExponentP(), + rs.getPrimeExponentQ(), + rs.getCrtCoefficient() + ); + } else if (keySpec instanceof RSAPrivateKeySpec) { + RSAPrivateKeySpec rs = (RSAPrivateKeySpec)keySpec; + return generatePrivate( + rs.getModulus(), + rs.getPrivateExponent() + ); + } else { + throw new InvalidKeySpecException("Only RSAPrivate(Crt)KeySpec " + + "and PKCS8EncodedKeySpec supported for RSA private keys"); + } + } catch (PKCS11Exception | InvalidKeyException e) { + throw new InvalidKeySpecException + ("Could not create RSA private key", e); + } + } + + private PublicKey generatePublic(BigInteger n, BigInteger e) + throws PKCS11Exception, InvalidKeyException { + RSAKeyFactory.checkKeyLengths(n.bitLength(), e, -1, 64 * 1024); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PUBLIC_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), + new CK_ATTRIBUTE(CKA_MODULUS, n), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, e), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PUBLIC_KEY, CKK_RSA, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.publicKey + (session, keyID, "RSA", n.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + private PrivateKey generatePrivate(BigInteger n, BigInteger d) + throws PKCS11Exception, InvalidKeyException { + RSAKeyFactory.checkKeyLengths(n.bitLength(), null, -1, 64 * 1024); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), + new CK_ATTRIBUTE(CKA_MODULUS, n), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, d), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.privateKey + (session, keyID, "RSA", n.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + private PrivateKey generatePrivate(BigInteger n, BigInteger e, + BigInteger d, BigInteger p, BigInteger q, BigInteger pe, + BigInteger qe, BigInteger coeff) throws PKCS11Exception, + InvalidKeyException { + RSAKeyFactory.checkKeyLengths(n.bitLength(), e, -1, 64 * 1024); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_PRIVATE_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_RSA), + new CK_ATTRIBUTE(CKA_MODULUS, n), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT, e), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT, d), + new CK_ATTRIBUTE(CKA_PRIME_1, p), + new CK_ATTRIBUTE(CKA_PRIME_2, q), + new CK_ATTRIBUTE(CKA_EXPONENT_1, pe), + new CK_ATTRIBUTE(CKA_EXPONENT_2, qe), + new CK_ATTRIBUTE(CKA_COEFFICIENT, coeff), + }; + attributes = token.getAttributes + (O_IMPORT, CKO_PRIVATE_KEY, CKK_RSA, attributes); + Session session = null; + try { + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + return P11Key.privateKey + (session, keyID, "RSA", n.bitLength(), attributes); + } finally { + token.releaseSession(session); + } + } + + T implGetPublicKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (RSAPublicKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new RSAPublicKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger() + ); + return keySpec.cast(spec); + } else { // X.509 handled in superclass + throw new InvalidKeySpecException("Only RSAPublicKeySpec and " + + "X509EncodedKeySpec supported for RSA public keys"); + } + } + + T implGetPrivateKeySpec(P11Key key, Class keySpec, + Session[] session) throws PKCS11Exception, InvalidKeySpecException { + if (RSAPrivateCrtKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PUBLIC_EXPONENT), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + new CK_ATTRIBUTE(CKA_PRIME_1), + new CK_ATTRIBUTE(CKA_PRIME_2), + new CK_ATTRIBUTE(CKA_EXPONENT_1), + new CK_ATTRIBUTE(CKA_EXPONENT_2), + new CK_ATTRIBUTE(CKA_COEFFICIENT), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new RSAPrivateCrtKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger(), + attributes[2].getBigInteger(), + attributes[3].getBigInteger(), + attributes[4].getBigInteger(), + attributes[5].getBigInteger(), + attributes[6].getBigInteger(), + attributes[7].getBigInteger() + ); + return keySpec.cast(spec); + } else if (RSAPrivateKeySpec.class.isAssignableFrom(keySpec)) { + session[0] = token.getObjSession(); + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_MODULUS), + new CK_ATTRIBUTE(CKA_PRIVATE_EXPONENT), + }; + token.p11.C_GetAttributeValue(session[0].id(), key.keyID, attributes); + KeySpec spec = new RSAPrivateKeySpec( + attributes[0].getBigInteger(), + attributes[1].getBigInteger() + ); + return keySpec.cast(spec); + } else { // PKCS#8 handled in superclass + throw new InvalidKeySpecException("Only RSAPrivate(Crt)KeySpec " + + "and PKCS8EncodedKeySpec supported for RSA private keys"); + } + } + + KeyFactory implGetSoftwareFactory() throws GeneralSecurityException { + return KeyFactory.getInstance("RSA", P11Util.getSunRsaSignProvider()); + } + +} diff --git a/src/sun/security/pkcs11/P11SecretKeyFactory.java b/src/sun/security/pkcs11/P11SecretKeyFactory.java new file mode 100644 index 00000000..e1756205 --- /dev/null +++ b/src/sun/security/pkcs11/P11SecretKeyFactory.java @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.util.*; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_IMPORT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CLASS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_KEY_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_AES; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_BLOWFISH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES3; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_GENERIC_SECRET; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_IDEA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RC2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RC4; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BLOWFISH_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC4_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_SECRET_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_SSLMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_TLSMASTER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_TLSPREMASTER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_TLSRSAPREMASTER; + +/** + * SecretKeyFactory implementation class. This class currently supports + * DES, DESede, AES, ARCFOUR, and Blowfish. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11SecretKeyFactory extends SecretKeyFactorySpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + P11SecretKeyFactory(Token token, String algorithm) { + super(); + this.token = token; + this.algorithm = algorithm; + } + + private static final Map keyTypes; + + static { + keyTypes = new HashMap(); + addKeyType("RC4", CKK_RC4); + addKeyType("ARCFOUR", CKK_RC4); + addKeyType("DES", CKK_DES); + addKeyType("DESede", CKK_DES3); + addKeyType("AES", CKK_AES); + addKeyType("Blowfish", CKK_BLOWFISH); + + // we don't implement RC2 or IDEA, but we want to be able to generate + // keys for those SSL/TLS ciphersuites. + addKeyType("RC2", CKK_RC2); + addKeyType("IDEA", CKK_IDEA); + + addKeyType("TlsPremasterSecret", PCKK_TLSPREMASTER); + addKeyType("TlsRsaPremasterSecret", PCKK_TLSRSAPREMASTER); + addKeyType("TlsMasterSecret", PCKK_TLSMASTER); + addKeyType("Generic", CKK_GENERIC_SECRET); + } + + private static void addKeyType(String name, long id) { + Long l = Long.valueOf(id); + keyTypes.put(name, l); + keyTypes.put(name.toUpperCase(Locale.ENGLISH), l); + } + + static long getKeyType(String algorithm) { + Long l = keyTypes.get(algorithm); + if (l == null) { + algorithm = algorithm.toUpperCase(Locale.ENGLISH); + l = keyTypes.get(algorithm); + if (l == null) { + if (algorithm.startsWith("HMAC")) { + return PCKK_HMAC; + } else if (algorithm.startsWith("SSLMAC")) { + return PCKK_SSLMAC; + } + } + } + return (l != null) ? l.longValue() : -1; + } + + /** + * Convert an arbitrary key of algorithm into a P11Key of provider. + * Used in engineTranslateKey(), P11Cipher.init(), and P11Mac.init(). + */ + static P11Key convertKey(Token token, Key key, String algo) + throws InvalidKeyException { + return convertKey(token, key, algo, null); + } + + /** + * Convert an arbitrary key of algorithm w/ custom attributes into a + * P11Key of provider. + * Used in P11KeyStore.storeSkey. + */ + static P11Key convertKey(Token token, Key key, String algo, + CK_ATTRIBUTE[] extraAttrs) + throws InvalidKeyException { + token.ensureValid(); + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + if (key instanceof SecretKey == false) { + throw new InvalidKeyException("Key must be a SecretKey"); + } + long algoType; + if (algo == null) { + algo = key.getAlgorithm(); + algoType = getKeyType(algo); + } else { + algoType = getKeyType(algo); + long keyAlgorithmType = getKeyType(key.getAlgorithm()); + if (algoType != keyAlgorithmType) { + if ((algoType == PCKK_HMAC) || (algoType == PCKK_SSLMAC)) { + // ignore key algorithm for MACs + } else { + throw new InvalidKeyException + ("Key algorithm must be " + algo); + } + } + } + if (key instanceof P11Key) { + P11Key p11Key = (P11Key)key; + if (p11Key.token == token) { + if (extraAttrs != null) { + Session session = null; + try { + session = token.getObjSession(); + long newKeyID = token.p11.C_CopyObject(session.id(), + p11Key.keyID, extraAttrs); + p11Key = (P11Key) (P11Key.secretKey(session, + newKeyID, p11Key.algorithm, p11Key.keyLength, + extraAttrs)); + } catch (PKCS11Exception p11e) { + throw new InvalidKeyException + ("Cannot duplicate the PKCS11 key", p11e); + } finally { + token.releaseSession(session); + } + } + return p11Key; + } + } + P11Key p11Key = token.secretCache.get(key); + if (p11Key != null) { + return p11Key; + } + if ("RAW".equalsIgnoreCase(key.getFormat()) == false) { + throw new InvalidKeyException("Encoded format must be RAW"); + } + byte[] encoded = key.getEncoded(); + p11Key = createKey(token, encoded, algo, algoType, extraAttrs); + token.secretCache.put(key, p11Key); + return p11Key; + } + + static void fixDESParity(byte[] key, int offset) { + for (int i = 0; i < 8; i++) { + int b = key[offset] & 0xfe; + b |= (Integer.bitCount(b) & 1) ^ 1; + key[offset++] = (byte)b; + } + } + + private static P11Key createKey(Token token, byte[] encoded, + String algorithm, long keyType, CK_ATTRIBUTE[] extraAttrs) + throws InvalidKeyException { + int n = encoded.length << 3; + int keyLength = n; + try { + switch ((int)keyType) { + case (int)CKK_DES: + keyLength = + P11KeyGenerator.checkKeySize(CKM_DES_KEY_GEN, n, token); + fixDESParity(encoded, 0); + break; + case (int)CKK_DES3: + keyLength = + P11KeyGenerator.checkKeySize(CKM_DES3_KEY_GEN, n, token); + fixDESParity(encoded, 0); + fixDESParity(encoded, 8); + if (keyLength == 112) { + keyType = CKK_DES2; + } else { + keyType = CKK_DES3; + fixDESParity(encoded, 16); + } + break; + case (int)CKK_AES: + keyLength = + P11KeyGenerator.checkKeySize(CKM_AES_KEY_GEN, n, token); + break; + case (int)CKK_RC4: + keyLength = + P11KeyGenerator.checkKeySize(CKM_RC4_KEY_GEN, n, token); + break; + case (int)CKK_BLOWFISH: + keyLength = + P11KeyGenerator.checkKeySize(CKM_BLOWFISH_KEY_GEN, n, + token); + break; + case (int)CKK_GENERIC_SECRET: + case (int)PCKK_TLSPREMASTER: + case (int)PCKK_TLSRSAPREMASTER: + case (int)PCKK_TLSMASTER: + keyType = CKK_GENERIC_SECRET; + break; + case (int)PCKK_SSLMAC: + case (int)PCKK_HMAC: + if (n == 0) { + throw new InvalidKeyException + ("MAC keys must not be empty"); + } + keyType = CKK_GENERIC_SECRET; + break; + default: + throw new InvalidKeyException("Unknown algorithm " + + algorithm); + } + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidKeyException("Invalid key for " + algorithm, + iape); + } catch (ProviderException pe) { + throw new InvalidKeyException("Could not create key", pe); + } + Session session = null; + try { + CK_ATTRIBUTE[] attributes; + if (extraAttrs != null) { + attributes = new CK_ATTRIBUTE[3 + extraAttrs.length]; + System.arraycopy(extraAttrs, 0, attributes, 3, + extraAttrs.length); + } else { + attributes = new CK_ATTRIBUTE[3]; + } + attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY); + attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType); + attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded); + attributes = token.getAttributes + (O_IMPORT, CKO_SECRET_KEY, keyType, attributes); + session = token.getObjSession(); + long keyID = token.p11.C_CreateObject(session.id(), attributes); + P11Key p11Key = (P11Key)P11Key.secretKey + (session, keyID, algorithm, keyLength, attributes); + return p11Key; + } catch (PKCS11Exception e) { + throw new InvalidKeyException("Could not create key", e); + } finally { + token.releaseSession(session); + } + } + + // see JCE spec + protected SecretKey engineGenerateSecret(KeySpec keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if (keySpec == null) { + throw new InvalidKeySpecException("KeySpec must not be null"); + } + if (keySpec instanceof SecretKeySpec) { + try { + Key key = convertKey(token, (SecretKey)keySpec, algorithm); + return (SecretKey)key; + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else if (algorithm.equalsIgnoreCase("DES")) { + if (keySpec instanceof DESKeySpec) { + byte[] keyBytes = ((DESKeySpec)keySpec).getKey(); + keySpec = new SecretKeySpec(keyBytes, "DES"); + return engineGenerateSecret(keySpec); + } + } else if (algorithm.equalsIgnoreCase("DESede")) { + if (keySpec instanceof DESedeKeySpec) { + byte[] keyBytes = ((DESedeKeySpec)keySpec).getKey(); + keySpec = new SecretKeySpec(keyBytes, "DESede"); + return engineGenerateSecret(keySpec); + } + } + throw new InvalidKeySpecException + ("Unsupported spec: " + keySpec.getClass().getName()); + } + + private byte[] getKeyBytes(SecretKey key) throws InvalidKeySpecException { + try { + key = engineTranslateKey(key); + if ("RAW".equalsIgnoreCase(key.getFormat()) == false) { + throw new InvalidKeySpecException + ("Could not obtain key bytes"); + } + byte[] k = key.getEncoded(); + return k; + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } + + // see JCE spec + protected KeySpec engineGetKeySpec(SecretKey key, Class keySpec) + throws InvalidKeySpecException { + token.ensureValid(); + if ((key == null) || (keySpec == null)) { + throw new InvalidKeySpecException + ("key and keySpec must not be null"); + } + if (SecretKeySpec.class.isAssignableFrom(keySpec)) { + return new SecretKeySpec(getKeyBytes(key), algorithm); + } else if (algorithm.equalsIgnoreCase("DES")) { + try { + if (DESKeySpec.class.isAssignableFrom(keySpec)) { + return new DESKeySpec(getKeyBytes(key)); + } + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else if (algorithm.equalsIgnoreCase("DESede")) { + try { + if (DESedeKeySpec.class.isAssignableFrom(keySpec)) { + return new DESedeKeySpec(getKeyBytes(key)); + } + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } + throw new InvalidKeySpecException + ("Unsupported spec: " + keySpec.getName()); + } + + // see JCE spec + protected SecretKey engineTranslateKey(SecretKey key) + throws InvalidKeyException { + return (SecretKey)convertKey(token, key, algorithm); + } + +} diff --git a/src/sun/security/pkcs11/P11SecureRandom.java b/src/sun/security/pkcs11/P11SecureRandom.java new file mode 100644 index 00000000..da8524e9 --- /dev/null +++ b/src/sun/security/pkcs11/P11SecureRandom.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import java.security.*; +import sun.security.pkcs11.wrapper.*; + +/** + * SecureRandom implementation class. Some tokens support only + * C_GenerateRandom() and not C_SeedRandom(). In order not to lose an + * application specified seed, we create a SHA1PRNG that we mix with in that + * case. + * + * Note that since SecureRandom is thread safe, we only need one + * instance per PKCS#11 token instance. It is created on demand and cached + * in the SunPKCS11 class. + * + * Also note that we obtain the PKCS#11 session on demand, no need to tie one + * up. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11SecureRandom extends SecureRandomSpi { + + private static final long serialVersionUID = -8939510236124553291L; + + // token instance + private final Token token; + + // PRNG for mixing, non-null if active (i.e. setSeed() has been called) + private volatile SecureRandom mixRandom; + + // buffer, if mixing is used + private byte[] mixBuffer; + + // bytes remaining in mixBuffer, if mixing is used + private int buffered; + + /* + * we buffer data internally for efficiency but limit the lifetime + * to avoid using stale bits. + */ + // lifetime in ms, currently 100 ms (0.1 s) + private static final long MAX_IBUFFER_TIME = 100; + + // size of the internal buffer + private static final int IBUFFER_SIZE = 32; + + // internal buffer for the random bits + private transient byte[] iBuffer = new byte[IBUFFER_SIZE]; + + // number of bytes remain in iBuffer + private transient int ibuffered = 0; + + // time that data was read into iBuffer + private transient long lastRead = 0L; + + P11SecureRandom(Token token) { + this.token = token; + } + + // see JCA spec + @Override + protected synchronized void engineSetSeed(byte[] seed) { + if (seed == null) { + throw new NullPointerException("seed must not be null"); + } + Session session = null; + try { + session = token.getOpSession(); + token.p11.C_SeedRandom(session.id(), seed); + } catch (PKCS11Exception e) { + // cannot set seed + // let a SHA1PRNG use that seed instead + SecureRandom random = mixRandom; + if (random != null) { + random.setSeed(seed); + } else { + try { + mixBuffer = new byte[20]; + random = SecureRandom.getInstance("SHA1PRNG"); + // initialize object before assigning to class field + random.setSeed(seed); + mixRandom = random; + } catch (NoSuchAlgorithmException ee) { + throw new ProviderException(ee); + } + } + } finally { + token.releaseSession(session); + } + } + + // see JCA spec + @Override + protected void engineNextBytes(byte[] bytes) { + if ((bytes == null) || (bytes.length == 0)) { + return; + } + if (bytes.length <= IBUFFER_SIZE) { + int ofs = 0; + synchronized (iBuffer) { + while (ofs < bytes.length) { + long time = System.currentTimeMillis(); + // refill the internal buffer if empty or stale + if ((ibuffered == 0) || + !(time - lastRead < MAX_IBUFFER_TIME)) { + lastRead = time; + implNextBytes(iBuffer); + ibuffered = IBUFFER_SIZE; + } + // copy the buffered bytes into 'bytes' + while ((ofs < bytes.length) && (ibuffered > 0)) { + bytes[ofs++] = iBuffer[IBUFFER_SIZE - ibuffered--]; + } + } + } + } else { + // avoid using the buffer - just fill bytes directly + implNextBytes(bytes); + } + + } + + // see JCA spec + @Override + protected byte[] engineGenerateSeed(int numBytes) { + byte[] b = new byte[numBytes]; + engineNextBytes(b); + return b; + } + + private void mix(byte[] b) { + SecureRandom random = mixRandom; + if (random == null) { + // avoid mixing if setSeed() has never been called + return; + } + synchronized (this) { + int ofs = 0; + int len = b.length; + while (len-- > 0) { + if (buffered == 0) { + random.nextBytes(mixBuffer); + buffered = mixBuffer.length; + } + b[ofs++] ^= mixBuffer[mixBuffer.length - buffered]; + buffered--; + } + } + } + + // fill up the specified buffer with random bytes, and mix them + private void implNextBytes(byte[] bytes) { + Session session = null; + try { + session = token.getOpSession(); + token.p11.C_GenerateRandom(session.id(), bytes); + mix(bytes); + } catch (PKCS11Exception e) { + throw new ProviderException("nextBytes() failed", e); + } finally { + token.releaseSession(session); + } + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + in.defaultReadObject(); + // assign default values to non-null transient fields + iBuffer = new byte[IBUFFER_SIZE]; + ibuffered = 0; + lastRead = 0L; + } +} diff --git a/src/sun/security/pkcs11/P11Signature.java b/src/sun/security/pkcs11/P11Signature.java new file mode 100644 index 00000000..c4e800f6 --- /dev/null +++ b/src/sun/security/pkcs11/P11Signature.java @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.AlgorithmParameterSpec; +import sun.nio.ch.DirectBuffer; + +import sun.security.util.*; +import sun.security.x509.AlgorithmId; + +import sun.security.rsa.RSASignature; +import sun.security.rsa.RSAPadding; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA_SHA1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDSA_SHA1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_X_509; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA1_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKR_DATA_LEN_RANGE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKR_SIGNATURE_INVALID; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKR_SIGNATURE_LEN_RANGE; + +import sun.security.util.KeyUtil; + +/** + * Signature implementation class. This class currently supports the + * following algorithms: + * + * . DSA + * . NONEwithDSA (RawDSA) + * . SHA1withDSA + * . RSA: + * . MD2withRSA + * . MD5withRSA + * . SHA1withRSA + * . SHA224withRSA + * . SHA256withRSA + * . SHA384withRSA + * . SHA512withRSA + * . ECDSA + * . NONEwithECDSA + * . SHA1withECDSA + * . SHA224withECDSA + * . SHA256withECDSA + * . SHA384withECDSA + * . SHA512withECDSA + * + * Note that the underlying PKCS#11 token may support complete signature + * algorithm (e.g. CKM_DSA_SHA1, CKM_MD5_RSA_PKCS), or it may just + * implement the signature algorithm without hashing (e.g. CKM_DSA, CKM_PKCS), + * or it may only implement the raw public key operation (CKM_RSA_X_509). + * This class uses what is available and adds whatever extra processing + * is needed. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class P11Signature extends SignatureSpi { + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // name of the key algorithm, currently either RSA or DSA + private final String keyAlgorithm; + + // mechanism id + private final long mechanism; + + // digest algorithm OID, if we encode RSA signature ourselves + private final ObjectIdentifier digestOID; + + // type, one of T_* below + private final int type; + + // key instance used, if init*() was called + private P11Key p11Key; + + // message digest, if we do the digesting ourselves + private final MessageDigest md; + + // associated session, if any + private Session session; + + // mode, one of M_* below + private int mode; + + // flag indicating whether an operation is initialized + private boolean initialized; + + // buffer, for update(byte) or DSA + private final byte[] buffer; + + // total number of bytes processed in current operation + private int bytesProcessed; + + // constant for signing mode + private final static int M_SIGN = 1; + // constant for verification mode + private final static int M_VERIFY = 2; + + // constant for type digesting, we do the hashing ourselves + private final static int T_DIGEST = 1; + // constant for type update, token does everything + private final static int T_UPDATE = 2; + // constant for type raw, used with RawDSA and NONEwithECDSA only + private final static int T_RAW = 3; + + // XXX PKCS#11 v2.20 says "should not be longer than 1024 bits", + // but this is a little arbitrary + private final static int RAW_ECDSA_MAX = 128; + + P11Signature(Token token, String algorithm, long mechanism) + throws NoSuchAlgorithmException, PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + byte[] buffer = null; + ObjectIdentifier digestOID = null; + MessageDigest md = null; + switch ((int)mechanism) { + case (int)CKM_MD2_RSA_PKCS: + case (int)CKM_MD5_RSA_PKCS: + case (int)CKM_SHA1_RSA_PKCS: + case (int)CKM_SHA224_RSA_PKCS: + case (int)CKM_SHA256_RSA_PKCS: + case (int)CKM_SHA384_RSA_PKCS: + case (int)CKM_SHA512_RSA_PKCS: + keyAlgorithm = "RSA"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_DSA_SHA1: + keyAlgorithm = "DSA"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_ECDSA_SHA1: + keyAlgorithm = "EC"; + type = T_UPDATE; + buffer = new byte[1]; + break; + case (int)CKM_DSA: + keyAlgorithm = "DSA"; + if (algorithm.equals("DSA")) { + type = T_DIGEST; + md = MessageDigest.getInstance("SHA-1"); + } else if (algorithm.equals("RawDSA")) { + type = T_RAW; + buffer = new byte[20]; + } else { + throw new ProviderException(algorithm); + } + break; + case (int)CKM_ECDSA: + keyAlgorithm = "EC"; + if (algorithm.equals("NONEwithECDSA")) { + type = T_RAW; + buffer = new byte[RAW_ECDSA_MAX]; + } else { + String digestAlg; + if (algorithm.equals("SHA1withECDSA")) { + digestAlg = "SHA-1"; + } else if (algorithm.equals("SHA224withECDSA")) { + digestAlg = "SHA-224"; + } else if (algorithm.equals("SHA256withECDSA")) { + digestAlg = "SHA-256"; + } else if (algorithm.equals("SHA384withECDSA")) { + digestAlg = "SHA-384"; + } else if (algorithm.equals("SHA512withECDSA")) { + digestAlg = "SHA-512"; + } else { + throw new ProviderException(algorithm); + } + type = T_DIGEST; + md = MessageDigest.getInstance(digestAlg); + } + break; + case (int)CKM_RSA_PKCS: + case (int)CKM_RSA_X_509: + keyAlgorithm = "RSA"; + type = T_DIGEST; + if (algorithm.equals("MD5withRSA")) { + md = MessageDigest.getInstance("MD5"); + digestOID = AlgorithmId.MD5_oid; + } else if (algorithm.equals("SHA1withRSA")) { + md = MessageDigest.getInstance("SHA-1"); + digestOID = AlgorithmId.SHA_oid; + } else if (algorithm.equals("MD2withRSA")) { + md = MessageDigest.getInstance("MD2"); + digestOID = AlgorithmId.MD2_oid; + } else if (algorithm.equals("SHA224withRSA")) { + md = MessageDigest.getInstance("SHA-224"); + digestOID = AlgorithmId.SHA224_oid; + } else if (algorithm.equals("SHA256withRSA")) { + md = MessageDigest.getInstance("SHA-256"); + digestOID = AlgorithmId.SHA256_oid; + } else if (algorithm.equals("SHA384withRSA")) { + md = MessageDigest.getInstance("SHA-384"); + digestOID = AlgorithmId.SHA384_oid; + } else if (algorithm.equals("SHA512withRSA")) { + md = MessageDigest.getInstance("SHA-512"); + digestOID = AlgorithmId.SHA512_oid; + } else { + throw new ProviderException("Unknown signature: " + algorithm); + } + break; + default: + throw new ProviderException("Unknown mechanism: " + mechanism); + } + this.buffer = buffer; + this.digestOID = digestOID; + this.md = md; + } + + private void ensureInitialized() { + token.ensureValid(); + if (initialized == false) { + initialize(); + } + } + + private void cancelOperation() { + token.ensureValid(); + if (initialized == false) { + return; + } + initialized = false; + if ((session == null) || (token.explicitCancel == false)) { + return; + } + if (session.hasObjects() == false) { + session = token.killSession(session); + return; + } + // "cancel" operation by finishing it + // XXX make sure all this always works correctly + if (mode == M_SIGN) { + try { + if (type == T_UPDATE) { + token.p11.C_SignFinal(session.id(), 0); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + digest = buffer; + } + token.p11.C_Sign(session.id(), digest); + } + } catch (PKCS11Exception e) { + throw new ProviderException("cancel failed", e); + } + } else { // M_VERIFY + try { + byte[] signature; + if (keyAlgorithm.equals("DSA")) { + signature = new byte[40]; + } else { + signature = new byte[(p11Key.length() + 7) >> 3]; + } + if (type == T_UPDATE) { + token.p11.C_VerifyFinal(session.id(), signature); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + digest = buffer; + } + token.p11.C_Verify(session.id(), digest, signature); + } + } catch (PKCS11Exception e) { + // will fail since the signature is incorrect + // XXX check error code + } + } + } + + // assumes current state is initialized == false + private void initialize() { + try { + if (session == null) { + session = token.getOpSession(); + } + if (mode == M_SIGN) { + token.p11.C_SignInit(session.id(), + new CK_MECHANISM(mechanism), p11Key.keyID); + } else { + token.p11.C_VerifyInit(session.id(), + new CK_MECHANISM(mechanism), p11Key.keyID); + } + initialized = true; + } catch (PKCS11Exception e) { + throw new ProviderException("Initialization failed", e); + } + if (bytesProcessed != 0) { + bytesProcessed = 0; + if (md != null) { + md.reset(); + } + } + } + + private void checkKeySize(String keyAlgo, Key key) + throws InvalidKeyException { + CK_MECHANISM_INFO mechInfo = null; + try { + mechInfo = token.getMechanismInfo(mechanism); + } catch (PKCS11Exception e) { + // should not happen, ignore for now. + } + if (mechInfo == null) { + // skip the check if no native info available + return; + } + int minKeySize = (int) mechInfo.ulMinKeySize; + int maxKeySize = (int) mechInfo.ulMaxKeySize; + // need to override the MAX keysize for SHA1withDSA + if (md != null && mechanism == CKM_DSA && maxKeySize > 1024) { + maxKeySize = 1024; + } + int keySize = 0; + if (key instanceof P11Key) { + keySize = ((P11Key) key).length(); + } else { + if (keyAlgo.equals("RSA")) { + keySize = ((RSAKey) key).getModulus().bitLength(); + } else if (keyAlgo.equals("DSA")) { + keySize = ((DSAKey) key).getParams().getP().bitLength(); + } else if (keyAlgo.equals("EC")) { + keySize = ((ECKey) key).getParams().getCurve().getField().getFieldSize(); + } else { + throw new ProviderException("Error: unsupported algo " + keyAlgo); + } + } + if ((minKeySize != -1) && (keySize < minKeySize)) { + throw new InvalidKeyException(keyAlgo + + " key must be at least " + minKeySize + " bits"); + } + if ((maxKeySize != -1) && (keySize > maxKeySize)) { + throw new InvalidKeyException(keyAlgo + + " key must be at most " + maxKeySize + " bits"); + } + if (keyAlgo.equals("RSA")) { + checkRSAKeyLength(keySize); + } + } + + private void checkRSAKeyLength(int len) throws InvalidKeyException { + RSAPadding padding; + try { + padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, (len + 7) >> 3); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidKeyException(iape.getMessage()); + } + int maxDataSize = padding.getMaxDataSize(); + int encodedLength; + if (algorithm.equals("MD5withRSA") || + algorithm.equals("MD2withRSA")) { + encodedLength = 34; + } else if (algorithm.equals("SHA1withRSA")) { + encodedLength = 35; + } else if (algorithm.equals("SHA224withRSA")) { + encodedLength = 47; + } else if (algorithm.equals("SHA256withRSA")) { + encodedLength = 51; + } else if (algorithm.equals("SHA384withRSA")) { + encodedLength = 67; + } else if (algorithm.equals("SHA512withRSA")) { + encodedLength = 83; + } else { + throw new ProviderException("Unknown signature algo: " + algorithm); + } + if (encodedLength > maxDataSize) { + throw new InvalidKeyException + ("Key is too short for this signature algorithm"); + } + } + + // see JCA spec + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + if (publicKey == null) { + throw new InvalidKeyException("Key must not be null"); + } + // Need to check key length whenever a new key is set + if (publicKey != p11Key) { + checkKeySize(keyAlgorithm, publicKey); + } + cancelOperation(); + mode = M_VERIFY; + p11Key = P11KeyFactory.convertKey(token, publicKey, keyAlgorithm); + initialize(); + } + + // see JCA spec + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + if (privateKey == null) { + throw new InvalidKeyException("Key must not be null"); + } + // Need to check RSA key length whenever a new key is set + if (privateKey != p11Key) { + checkKeySize(keyAlgorithm, privateKey); + } + cancelOperation(); + mode = M_SIGN; + p11Key = P11KeyFactory.convertKey(token, privateKey, keyAlgorithm); + initialize(); + } + + // see JCA spec + @Override + protected void engineUpdate(byte b) throws SignatureException { + ensureInitialized(); + switch (type) { + case T_UPDATE: + buffer[0] = b; + engineUpdate(buffer, 0, 1); + break; + case T_DIGEST: + md.update(b); + bytesProcessed++; + break; + case T_RAW: + if (bytesProcessed >= buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + buffer[bytesProcessed++] = b; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + @Override + protected void engineUpdate(byte[] b, int ofs, int len) + throws SignatureException { + ensureInitialized(); + if (len == 0) { + return; + } + switch (type) { + case T_UPDATE: + try { + if (mode == M_SIGN) { + token.p11.C_SignUpdate(session.id(), 0, b, ofs, len); + } else { + token.p11.C_VerifyUpdate(session.id(), 0, b, ofs, len); + } + bytesProcessed += len; + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } + break; + case T_DIGEST: + md.update(b, ofs, len); + bytesProcessed += len; + break; + case T_RAW: + if (bytesProcessed + len > buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + System.arraycopy(b, ofs, buffer, bytesProcessed, len); + bytesProcessed += len; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + @Override + protected void engineUpdate(ByteBuffer byteBuffer) { + ensureInitialized(); + int len = byteBuffer.remaining(); + if (len <= 0) { + return; + } + switch (type) { + case T_UPDATE: + if (byteBuffer instanceof DirectBuffer == false) { + // cannot do better than default impl + super.engineUpdate(byteBuffer); + return; + } + long addr = ((DirectBuffer)byteBuffer).address(); + int ofs = byteBuffer.position(); + try { + if (mode == M_SIGN) { + token.p11.C_SignUpdate + (session.id(), addr + ofs, null, 0, len); + } else { + token.p11.C_VerifyUpdate + (session.id(), addr + ofs, null, 0, len); + } + bytesProcessed += len; + byteBuffer.position(ofs + len); + } catch (PKCS11Exception e) { + throw new ProviderException("Update failed", e); + } + break; + case T_DIGEST: + md.update(byteBuffer); + bytesProcessed += len; + break; + case T_RAW: + if (bytesProcessed + len > buffer.length) { + bytesProcessed = buffer.length + 1; + return; + } + byteBuffer.get(buffer, bytesProcessed, len); + bytesProcessed += len; + break; + default: + throw new ProviderException("Internal error"); + } + } + + // see JCA spec + @Override + protected byte[] engineSign() throws SignatureException { + ensureInitialized(); + try { + byte[] signature; + if (type == T_UPDATE) { + int len = keyAlgorithm.equals("DSA") ? 40 : 0; + signature = token.p11.C_SignFinal(session.id(), len); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + if (mechanism == CKM_DSA) { + if (bytesProcessed != buffer.length) { + throw new SignatureException + ("Data for RawDSA must be exactly 20 bytes long"); + } + digest = buffer; + } else { // CKM_ECDSA + if (bytesProcessed > buffer.length) { + throw new SignatureException("Data for NONEwithECDSA" + + " must be at most " + RAW_ECDSA_MAX + " bytes long"); + } + digest = new byte[bytesProcessed]; + System.arraycopy(buffer, 0, digest, 0, bytesProcessed); + } + } + if (keyAlgorithm.equals("RSA") == false) { + // DSA and ECDSA + signature = token.p11.C_Sign(session.id(), digest); + } else { // RSA + byte[] data = encodeSignature(digest); + if (mechanism == CKM_RSA_X_509) { + data = pkcs1Pad(data); + } + signature = token.p11.C_Sign(session.id(), data); + } + } + if (keyAlgorithm.equals("RSA") == false) { + return dsaToASN1(signature); + } else { + return signature; + } + } catch (PKCS11Exception e) { + throw new ProviderException(e); + } finally { + initialized = false; + session = token.releaseSession(session); + } + } + + // see JCA spec + @Override + protected boolean engineVerify(byte[] signature) throws SignatureException { + ensureInitialized(); + try { + if (keyAlgorithm.equals("DSA")) { + signature = asn1ToDSA(signature); + } else if (keyAlgorithm.equals("EC")) { + signature = asn1ToECDSA(signature); + } + if (type == T_UPDATE) { + token.p11.C_VerifyFinal(session.id(), signature); + } else { + byte[] digest; + if (type == T_DIGEST) { + digest = md.digest(); + } else { // T_RAW + if (mechanism == CKM_DSA) { + if (bytesProcessed != buffer.length) { + throw new SignatureException + ("Data for RawDSA must be exactly 20 bytes long"); + } + digest = buffer; + } else { + if (bytesProcessed > buffer.length) { + throw new SignatureException("Data for NONEwithECDSA" + + " must be at most " + RAW_ECDSA_MAX + " bytes long"); + } + digest = new byte[bytesProcessed]; + System.arraycopy(buffer, 0, digest, 0, bytesProcessed); + } + } + if (keyAlgorithm.equals("RSA") == false) { + // DSA and ECDSA + token.p11.C_Verify(session.id(), digest, signature); + } else { // RSA + byte[] data = encodeSignature(digest); + if (mechanism == CKM_RSA_X_509) { + data = pkcs1Pad(data); + } + token.p11.C_Verify(session.id(), data, signature); + } + } + return true; + } catch (PKCS11Exception e) { + long errorCode = e.getErrorCode(); + if (errorCode == CKR_SIGNATURE_INVALID) { + return false; + } + if (errorCode == CKR_SIGNATURE_LEN_RANGE) { + // return false rather than throwing an exception + return false; + } + // ECF bug? + if (errorCode == CKR_DATA_LEN_RANGE) { + return false; + } + throw new ProviderException(e); + } finally { + // XXX we should not release the session if we abort above + // before calling C_Verify + initialized = false; + session = token.releaseSession(session); + } + } + + private byte[] pkcs1Pad(byte[] data) { + try { + int len = (p11Key.length() + 7) >> 3; + RSAPadding padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, len); + byte[] padded = padding.pad(data); + return padded; + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + private byte[] encodeSignature(byte[] digest) throws SignatureException { + try { + return RSASignature.encodeSignature(digestOID, digest); + } catch (IOException e) { + throw new SignatureException("Invalid encoding", e); + } + } + +// private static byte[] decodeSignature(byte[] signature) throws IOException { +// return RSASignature.decodeSignature(digestOID, signature); +// } + + // For DSA and ECDSA signatures, PKCS#11 represents them as a simple + // byte array that contains the concatenation of r and s. + // For DSA, r and s are always exactly 20 bytes long. + // For ECDSA, r and s are of variable length, but we know that each + // occupies half of the array. + private static byte[] dsaToASN1(byte[] signature) { + int n = signature.length >> 1; + BigInteger r = new BigInteger(1, P11Util.subarray(signature, 0, n)); + BigInteger s = new BigInteger(1, P11Util.subarray(signature, n, n)); + try { + DerOutputStream outseq = new DerOutputStream(100); + outseq.putInteger(r); + outseq.putInteger(s); + DerValue result = new DerValue(DerValue.tag_Sequence, + outseq.toByteArray()); + return result.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("Internal error", e); + } + } + + private static byte[] asn1ToDSA(byte[] signature) throws SignatureException { + try { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getPositiveBigInteger(); + BigInteger s = values[1].getPositiveBigInteger(); + byte[] br = toByteArray(r, 20); + byte[] bs = toByteArray(s, 20); + if ((br == null) || (bs == null)) { + throw new SignatureException("Out of range value for R or S"); + } + return P11Util.concat(br, bs); + } catch (SignatureException e) { + throw e; + } catch (Exception e) { + throw new SignatureException("invalid encoding for signature", e); + } + } + + private byte[] asn1ToECDSA(byte[] signature) throws SignatureException { + try { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + BigInteger r = values[0].getPositiveBigInteger(); + BigInteger s = values[1].getPositiveBigInteger(); + // trim leading zeroes + byte[] br = KeyUtil.trimZeroes(r.toByteArray()); + byte[] bs = KeyUtil.trimZeroes(s.toByteArray()); + int k = Math.max(br.length, bs.length); + // r and s each occupy half the array + byte[] res = new byte[k << 1]; + System.arraycopy(br, 0, res, k - br.length, br.length); + System.arraycopy(bs, 0, res, res.length - bs.length, bs.length); + return res; + } catch (Exception e) { + throw new SignatureException("invalid encoding for signature", e); + } + } + + private static byte[] toByteArray(BigInteger bi, int len) { + byte[] b = bi.toByteArray(); + int n = b.length; + if (n == len) { + return b; + } + if ((n == len + 1) && (b[0] == 0)) { + byte[] t = new byte[len]; + System.arraycopy(b, 1, t, 0, len); + return t; + } + if (n > len) { + return null; + } + // must be smaller + byte[] t = new byte[len]; + System.arraycopy(b, 0, t, (len - n), n); + return t; + } + + // see JCA spec + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + // see JCA spec + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("No parameter accepted"); + } + } + + // see JCA spec + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + // see JCA spec + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } +} diff --git a/src/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java b/src/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java new file mode 100644 index 00000000..690ef7d2 --- /dev/null +++ b/src/sun/security/pkcs11/P11TlsKeyMaterialGenerator.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.internal.spec.*; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyGenerator to calculate the SSL/TLS key material (cipher keys and ivs, + * mac keys) from the master secret. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +public final class P11TlsKeyMaterialGenerator extends KeyGeneratorSpi { + + private final static String MSG = "TlsKeyMaterialGenerator must be " + + "initialized using a TlsKeyMaterialParameterSpec"; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private long mechanism; + + // parameter spec + private TlsKeyMaterialParameterSpec spec; + + // master secret as a P11Key + private P11Key p11Key; + + // version, e.g. 0x0301 + private int version; + + P11TlsKeyMaterialGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + protected void engineInit(SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected void engineInit(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + if (params instanceof TlsKeyMaterialParameterSpec == false) { + throw new InvalidAlgorithmParameterException(MSG); + } + this.spec = (TlsKeyMaterialParameterSpec)params; + try { + p11Key = P11SecretKeyFactory.convertKey + (token, spec.getMasterSecret(), "TlsMasterSecret"); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException("init() failed", e); + } + version = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); + if ((version < 0x0300) && (version > 0x0302)) { + throw new InvalidAlgorithmParameterException + ("Only SSL 3.0, TLS 1.0, and TLS 1.1 are supported"); + } + // we assume the token supports both the CKM_SSL3_* and the CKM_TLS_* + // mechanisms + } + + protected void engineInit(int keysize, SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected SecretKey engineGenerateKey() { + if (spec == null) { + throw new IllegalStateException + ("TlsKeyMaterialGenerator must be initialized"); + } + mechanism = (version == 0x0300) ? CKM_SSL3_KEY_AND_MAC_DERIVE + : CKM_TLS_KEY_AND_MAC_DERIVE; + int macBits = spec.getMacKeyLength() << 3; + int ivBits = spec.getIvLength() << 3; + + int expandedKeyBits = spec.getExpandedCipherKeyLength() << 3; + int keyBits = spec.getCipherKeyLength() << 3; + boolean isExportable; + if (expandedKeyBits != 0) { + isExportable = true; + } else { + isExportable = false; + expandedKeyBits = keyBits; + } + + CK_SSL3_RANDOM_DATA random = new CK_SSL3_RANDOM_DATA + (spec.getClientRandom(), spec.getServerRandom()); + CK_SSL3_KEY_MAT_PARAMS params = new CK_SSL3_KEY_MAT_PARAMS + (macBits, keyBits, ivBits, isExportable, random); + + String cipherAlgorithm = spec.getCipherAlgorithm(); + long keyType = P11SecretKeyFactory.getKeyType(cipherAlgorithm); + if (keyType < 0) { + if (keyBits != 0) { + throw new ProviderException + ("Unknown algorithm: " + spec.getCipherAlgorithm()); + } else { + // NULL encryption ciphersuites + keyType = CKK_GENERIC_SECRET; + } + } + + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes; + if (keyBits != 0) { + attributes = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY), + new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType), + new CK_ATTRIBUTE(CKA_VALUE_LEN, expandedKeyBits >> 3), + }; + } else { + // ciphersuites with NULL ciphers + attributes = new CK_ATTRIBUTE[0]; + } + attributes = token.getAttributes + (O_GENERATE, CKO_SECRET_KEY, keyType, attributes); + // the returned keyID is a dummy, ignore + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, params), p11Key.keyID, attributes); + + CK_SSL3_KEY_MAT_OUT out = params.pReturnedKeyMaterial; + // Note that the MAC keys do not inherit all attributes from the + // template, but they do inherit the sensitive/extractable/token + // flags, which is all P11Key cares about. + SecretKey clientMacKey, serverMacKey; + + // The MAC size may be zero for GCM mode. + // + // PKCS11 does not support GCM mode as the author made the comment, + // so the macBits is unlikely to be zero. It's only a place holder. + if (macBits != 0) { + clientMacKey = P11Key.secretKey + (session, out.hClientMacSecret, "MAC", macBits, attributes); + serverMacKey = P11Key.secretKey + (session, out.hServerMacSecret, "MAC", macBits, attributes); + } else { + clientMacKey = null; + serverMacKey = null; + } + + SecretKey clientCipherKey, serverCipherKey; + if (keyBits != 0) { + clientCipherKey = P11Key.secretKey(session, out.hClientKey, + cipherAlgorithm, expandedKeyBits, attributes); + serverCipherKey = P11Key.secretKey(session, out.hServerKey, + cipherAlgorithm, expandedKeyBits, attributes); + } else { + clientCipherKey = null; + serverCipherKey = null; + } + IvParameterSpec clientIv = (out.pIVClient == null) + ? null : new IvParameterSpec(out.pIVClient); + IvParameterSpec serverIv = (out.pIVServer == null) + ? null : new IvParameterSpec(out.pIVServer); + + return new TlsKeyMaterialSpec(clientMacKey, serverMacKey, + clientCipherKey, clientIv, serverCipherKey, serverIv); + + } catch (Exception e) { + throw new ProviderException("Could not generate key", e); + } finally { + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11TlsMasterSecretGenerator.java b/src/sun/security/pkcs11/P11TlsMasterSecretGenerator.java new file mode 100644 index 00000000..4d007d33 --- /dev/null +++ b/src/sun/security/pkcs11/P11TlsMasterSecretGenerator.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.internal.spec.TlsMasterSecretParameterSpec; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyGenerator for the SSL/TLS master secret. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +public final class P11TlsMasterSecretGenerator extends KeyGeneratorSpi { + + private final static String MSG = "TlsMasterSecretGenerator must be " + + "initialized using a TlsMasterSecretParameterSpec"; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private long mechanism; + + private TlsMasterSecretParameterSpec spec; + private P11Key p11Key; + + int version; + + P11TlsMasterSecretGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + protected void engineInit(SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected void engineInit(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + if (params instanceof TlsMasterSecretParameterSpec == false) { + throw new InvalidAlgorithmParameterException(MSG); + } + this.spec = (TlsMasterSecretParameterSpec)params; + SecretKey key = spec.getPremasterSecret(); + // algorithm should be either TlsRsaPremasterSecret or TlsPremasterSecret, + // but we omit the check + try { + p11Key = P11SecretKeyFactory.convertKey(token, key, null); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException("init() failed", e); + } + version = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); + if ((version < 0x0300) || (version > 0x0302)) { + throw new InvalidAlgorithmParameterException + ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + } + // We assume the token supports the required mechanism. If it does not, + // generateKey() will fail and the failover should take care of us. + } + + protected void engineInit(int keysize, SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected SecretKey engineGenerateKey() { + if (spec == null) { + throw new IllegalStateException + ("TlsMasterSecretGenerator must be initialized"); + } + CK_VERSION ckVersion; + if (p11Key.getAlgorithm().equals("TlsRsaPremasterSecret")) { + mechanism = (version == 0x0300) ? CKM_SSL3_MASTER_KEY_DERIVE + : CKM_TLS_MASTER_KEY_DERIVE; + ckVersion = new CK_VERSION(0, 0); + } else { + // Note: we use DH for all non-RSA premaster secrets. That includes + // Kerberos. That should not be a problem because master secret + // calculation is always a straightforward application of the + // TLS PRF (or the SSL equivalent). + // The only thing special about RSA master secret calculation is + // that it extracts the version numbers from the premaster secret. + mechanism = (version == 0x0300) ? CKM_SSL3_MASTER_KEY_DERIVE_DH + : CKM_TLS_MASTER_KEY_DERIVE_DH; + ckVersion = null; + } + byte[] clientRandom = spec.getClientRandom(); + byte[] serverRandom = spec.getServerRandom(); + CK_SSL3_RANDOM_DATA random = + new CK_SSL3_RANDOM_DATA(clientRandom, serverRandom); + CK_SSL3_MASTER_KEY_DERIVE_PARAMS params = + new CK_SSL3_MASTER_KEY_DERIVE_PARAMS(random, ckVersion); + + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes = token.getAttributes(O_GENERATE, + CKO_SECRET_KEY, CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, params), p11Key.keyID, attributes); + int major, minor; + ckVersion = params.pVersion; + if (ckVersion == null) { + major = -1; + minor = -1; + } else { + major = ckVersion.major; + minor = ckVersion.minor; + } + SecretKey key = P11Key.masterSecretKey(session, keyID, + "TlsMasterSecret", 48 << 3, attributes, major, minor); + return key; + } catch (Exception e) { + throw new ProviderException("Could not generate key", e); + } finally { + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11TlsPrfGenerator.java b/src/sun/security/pkcs11/P11TlsPrfGenerator.java new file mode 100644 index 00000000..018094f2 --- /dev/null +++ b/src/sun/security/pkcs11/P11TlsPrfGenerator.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.internal.spec.TlsPrfParameterSpec; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyGenerator for the TLS PRF. Note that although the PRF is used in a number + * of places during the handshake, this class is usually only used to calculate + * the Finished messages. The reason is that for those other uses more specific + * PKCS#11 mechanisms have been defined (CKM_SSL3_MASTER_KEY_DERIVE, etc.). + * + *

    This class supports the CKM_TLS_PRF mechanism from PKCS#11 v2.20 and + * the older NSS private mechanism. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +final class P11TlsPrfGenerator extends KeyGeneratorSpi { + + private final static String MSG = + "TlsPrfGenerator must be initialized using a TlsPrfParameterSpec"; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private final long mechanism; + + private TlsPrfParameterSpec spec; + + private P11Key p11Key; + + P11TlsPrfGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + protected void engineInit(SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected void engineInit(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + if (params instanceof TlsPrfParameterSpec == false) { + throw new InvalidAlgorithmParameterException(MSG); + } + this.spec = (TlsPrfParameterSpec)params; + SecretKey key = spec.getSecret(); + if (key == null) { + key = NULL_KEY; + } + try { + p11Key = P11SecretKeyFactory.convertKey(token, key, null); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException("init() failed", e); + } + } + + // SecretKeySpec does not allow zero length keys, so we define our + // own class. + // + // As an anonymous class cannot make any guarantees about serialization + // compatibility, it is nonsensical for an anonymous class to define a + // serialVersionUID. Suppress warnings relative to missing serialVersionUID + // field in the anonymous subclass of serializable SecretKey. + @SuppressWarnings("serial") + private static final SecretKey NULL_KEY = new SecretKey() { + public byte[] getEncoded() { + return new byte[0]; + } + public String getFormat() { + return "RAW"; + } + public String getAlgorithm() { + return "Generic"; + } + }; + + protected void engineInit(int keysize, SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected SecretKey engineGenerateKey() { + if (spec == null) { + throw new IllegalStateException("TlsPrfGenerator must be initialized"); + } + byte[] label = P11Util.getBytesUTF8(spec.getLabel()); + byte[] seed = spec.getSeed(); + + if (mechanism == CKM_NSS_TLS_PRF_GENERAL) { + Session session = null; + try { + session = token.getOpSession(); + token.p11.C_SignInit + (session.id(), new CK_MECHANISM(mechanism), p11Key.keyID); + token.p11.C_SignUpdate(session.id(), 0, label, 0, label.length); + token.p11.C_SignUpdate(session.id(), 0, seed, 0, seed.length); + byte[] out = token.p11.C_SignFinal + (session.id(), spec.getOutputLength()); + return new SecretKeySpec(out, "TlsPrf"); + } catch (PKCS11Exception e) { + throw new ProviderException("Could not calculate PRF", e); + } finally { + token.releaseSession(session); + } + } + + // mechanism == CKM_TLS_PRF + + byte[] out = new byte[spec.getOutputLength()]; + CK_TLS_PRF_PARAMS params = new CK_TLS_PRF_PARAMS(seed, label, out); + + Session session = null; + try { + session = token.getOpSession(); + long keyID = token.p11.C_DeriveKey(session.id(), + new CK_MECHANISM(mechanism, params), p11Key.keyID, null); + // ignore keyID, returned PRF bytes are in 'out' + return new SecretKeySpec(out, "TlsPrf"); + } catch (PKCS11Exception e) { + throw new ProviderException("Could not calculate PRF", e); + } finally { + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java b/src/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java new file mode 100644 index 00000000..8b4618a7 --- /dev/null +++ b/src/sun/security/pkcs11/P11TlsRsaPremasterSecretGenerator.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; + +import sun.security.pkcs11.wrapper.*; + +import static sun.security.pkcs11.TemplateManager.O_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * KeyGenerator for the SSL/TLS RSA premaster secret. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +final class P11TlsRsaPremasterSecretGenerator extends KeyGeneratorSpi { + + private final static String MSG = "TlsRsaPremasterSecretGenerator must be " + + "initialized using a TlsRsaPremasterSecretParameterSpec"; + + // token instance + private final Token token; + + // algorithm name + private final String algorithm; + + // mechanism id + private long mechanism; + + private TlsRsaPremasterSecretParameterSpec spec; + + P11TlsRsaPremasterSecretGenerator(Token token, String algorithm, long mechanism) + throws PKCS11Exception { + super(); + this.token = token; + this.algorithm = algorithm; + this.mechanism = mechanism; + } + + protected void engineInit(SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + protected void engineInit(AlgorithmParameterSpec params, + SecureRandom random) throws InvalidAlgorithmParameterException { + if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { + throw new InvalidAlgorithmParameterException(MSG); + } + this.spec = (TlsRsaPremasterSecretParameterSpec)params; + } + + protected void engineInit(int keysize, SecureRandom random) { + throw new InvalidParameterException(MSG); + } + + // Only can be used in client side to generate TLS RSA premaster secret. + protected SecretKey engineGenerateKey() { + if (spec == null) { + throw new IllegalStateException + ("TlsRsaPremasterSecretGenerator must be initialized"); + } + + CK_VERSION version = new CK_VERSION( + spec.getMajorVersion(), spec.getMinorVersion()); + Session session = null; + try { + session = token.getObjSession(); + CK_ATTRIBUTE[] attributes = token.getAttributes( + O_GENERATE, CKO_SECRET_KEY, + CKK_GENERIC_SECRET, new CK_ATTRIBUTE[0]); + long keyID = token.p11.C_GenerateKey(session.id(), + new CK_MECHANISM(mechanism, version), attributes); + SecretKey key = P11Key.secretKey(session, + keyID, "TlsRsaPremasterSecret", 48 << 3, attributes); + return key; + } catch (PKCS11Exception e) { + throw new ProviderException( + "Could not generate premaster secret", e); + } finally { + token.releaseSession(session); + } + } + +} diff --git a/src/sun/security/pkcs11/P11Util.java b/src/sun/security/pkcs11/P11Util.java new file mode 100644 index 00000000..38da9401 --- /dev/null +++ b/src/sun/security/pkcs11/P11Util.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.math.BigInteger; +import java.security.*; + +/** + * Collection of static utility methods. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class P11Util { + + private static Object LOCK = new Object(); + + private static volatile Provider sun, sunRsaSign, sunJce; + + private P11Util() { + // empty + } + + static Provider getSunProvider() { + Provider p = sun; + if (p == null) { + synchronized (LOCK) { + p = getProvider + (sun, "SUN", "sun.security.provider.Sun"); + sun = p; + } + } + return p; + } + + static Provider getSunRsaSignProvider() { + Provider p = sunRsaSign; + if (p == null) { + synchronized (LOCK) { + p = getProvider + (sunRsaSign, "SunRsaSign", "sun.security.rsa.SunRsaSign"); + sunRsaSign = p; + } + } + return p; + } + + static Provider getSunJceProvider() { + Provider p = sunJce; + if (p == null) { + synchronized (LOCK) { + p = getProvider + (sunJce, "SunJCE", "com.sun.crypto.provider.SunJCE"); + sunJce = p; + } + } + return p; + } + + private static Provider getProvider(Provider p, String providerName, + String className) { + if (p != null) { + return p; + } + p = Security.getProvider(providerName); + if (p == null) { + try { + Class clazz = Class.forName(className); + p = (Provider)clazz.newInstance(); + } catch (Exception e) { + throw new ProviderException + ("Could not find provider " + providerName, e); + } + } + return p; + } + + static byte[] convert(byte[] input, int offset, int len) { + if ((offset == 0) && (len == input.length)) { + return input; + } else { + byte[] t = new byte[len]; + System.arraycopy(input, offset, t, 0, len); + return t; + } + } + + static byte[] subarray(byte[] b, int ofs, int len) { + byte[] out = new byte[len]; + System.arraycopy(b, ofs, out, 0, len); + return out; + } + + static byte[] concat(byte[] b1, byte[] b2) { + byte[] b = new byte[b1.length + b2.length]; + System.arraycopy(b1, 0, b, 0, b1.length); + System.arraycopy(b2, 0, b, b1.length, b2.length); + return b; + } + + static long[] concat(long[] b1, long[] b2) { + if (b1.length == 0) { + return b2; + } + long[] b = new long[b1.length + b2.length]; + System.arraycopy(b1, 0, b, 0, b1.length); + System.arraycopy(b2, 0, b, b1.length, b2.length); + return b; + } + + public static byte[] getMagnitude(BigInteger bi) { + byte[] b = bi.toByteArray(); + if ((b.length > 1) && (b[0] == 0)) { + int n = b.length - 1; + byte[] newarray = new byte[n]; + System.arraycopy(b, 1, newarray, 0, n); + b = newarray; + } + return b; + } + + static byte[] getBytesUTF8(String s) { + try { + return s.getBytes("UTF8"); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + static byte[] sha1(byte[] data) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(data); + return md.digest(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + private final static char[] hexDigits = "0123456789abcdef".toCharArray(); + + static String toString(byte[] b) { + if (b == null) { + return "(null)"; + } + StringBuffer sb = new StringBuffer(b.length * 3); + for (int i = 0; i < b.length; i++) { + int k = b[i] & 0xff; + if (i != 0) { + sb.append(':'); + } + sb.append(hexDigits[k >>> 4]); + sb.append(hexDigits[k & 0xf]); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/pkcs11/Secmod.java b/src/sun/security/pkcs11/Secmod.java new file mode 100644 index 00000000..5aac21cc --- /dev/null +++ b/src/sun/security/pkcs11/Secmod.java @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import java.util.*; + +import java.security.*; +import java.security.KeyStore.*; +import java.security.cert.X509Certificate; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + + +/** + * The Secmod class defines the interface to the native NSS + * library and the configuration information it stores in its + * secmod.db file. + * + *

    Example code: + *

    + *   Secmod secmod = Secmod.getInstance();
    + *   if (secmod.isInitialized() == false) {
    + *       secmod.initialize("/home/myself/.mozilla", "/usr/sfw/lib/mozilla");
    + *   }
    + *
    + *   Provider p = secmod.getModule(ModuleType.KEYSTORE).getProvider();
    + *   KeyStore ks = KeyStore.getInstance("PKCS11", p);
    + *   ks.load(null, password);
    + * 
    + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class Secmod { + + private final static boolean DEBUG = false; + + private final static Secmod INSTANCE; + + static { + PKCS11.loadNative(); + INSTANCE = new Secmod(); + } + + private final static String NSS_LIB_NAME = "nss3"; + + private final static String SOFTTOKEN_LIB_NAME = "softokn3"; + + private final static String TRUST_LIB_NAME = "nssckbi"; + + // handle to be passed to the native code, 0 means not initialized + private long nssHandle; + + // whether this is a supported version of NSS + private boolean supported; + + // list of the modules + private List modules; + + private String configDir; + + private String nssLibDir; + + private Secmod() { + // empty + } + + /** + * Return the singleton Secmod instance. + */ + public static Secmod getInstance() { + return INSTANCE; + } + + private boolean isLoaded() { + if (nssHandle == 0) { + nssHandle = nssGetLibraryHandle(System.mapLibraryName(NSS_LIB_NAME)); + if (nssHandle != 0) { + fetchVersions(); + } + } + return (nssHandle != 0); + } + + private void fetchVersions() { + supported = nssVersionCheck(nssHandle, "3.7"); + } + + /** + * Test whether this Secmod has been initialized. Returns true + * if NSS has been initialized using either the initialize() method + * or by directly calling the native NSS APIs. The latter may be + * the case if the current process contains components that use + * NSS directly. + * + * @throws IOException if an incompatible version of NSS + * has been loaded + */ + public synchronized boolean isInitialized() throws IOException { + // NSS does not allow us to check if it is initialized already + // assume that if it is loaded it is also initialized + if (isLoaded() == false) { + return false; + } + if (supported == false) { + throw new IOException + ("An incompatible version of NSS is already loaded, " + + "3.7 or later required"); + } + return true; + } + + String getConfigDir() { + return configDir; + } + + String getLibDir() { + return nssLibDir; + } + + /** + * Initialize this Secmod. + * + * @param configDir the directory containing the NSS configuration + * files such as secmod.db + * @param nssLibDir the directory containing the NSS libraries + * (libnss3.so or nss3.dll) or null if the library is on + * the system default shared library path + * + * @throws IOException if NSS has already been initialized, + * the specified directories are invalid, or initialization + * fails for any other reason + */ + public void initialize(String configDir, String nssLibDir) + throws IOException { + initialize(DbMode.READ_WRITE, configDir, nssLibDir, false); + } + + public void initialize(DbMode dbMode, String configDir, String nssLibDir) + throws IOException { + initialize(dbMode, configDir, nssLibDir, false); + } + + public synchronized void initialize(DbMode dbMode, String configDir, + String nssLibDir, boolean nssOptimizeSpace) throws IOException { + + if (isInitialized()) { + throw new IOException("NSS is already initialized"); + } + + if (dbMode == null) { + throw new NullPointerException(); + } + if ((dbMode != DbMode.NO_DB) && (configDir == null)) { + throw new NullPointerException(); + } + String platformLibName = System.mapLibraryName("nss3"); + String platformPath; + if (nssLibDir == null) { + platformPath = platformLibName; + } else { + File base = new File(nssLibDir); + if (base.isDirectory() == false) { + throw new IOException("nssLibDir must be a directory:" + nssLibDir); + } + File platformFile = new File(base, platformLibName); + if (platformFile.isFile() == false) { + throw new FileNotFoundException(platformFile.getPath()); + } + platformPath = platformFile.getPath(); + } + + if (configDir != null) { + File configBase = new File(configDir); + if (configBase.isDirectory() == false ) { + throw new IOException("configDir must be a directory: " + configDir); + } + File secmodFile = new File(configBase, "secmod.db"); + if (secmodFile.isFile() == false) { + throw new FileNotFoundException(secmodFile.getPath()); + } + } + + if (DEBUG) System.out.println("lib: " + platformPath); + nssHandle = nssLoadLibrary(platformPath); + if (DEBUG) System.out.println("handle: " + nssHandle); + fetchVersions(); + if (supported == false) { + throw new IOException + ("The specified version of NSS is incompatible, " + + "3.7 or later required"); + } + + if (DEBUG) System.out.println("dir: " + configDir); + boolean initok = nssInitialize(dbMode.functionName, nssHandle, + configDir, nssOptimizeSpace); + if (DEBUG) System.out.println("init: " + initok); + if (initok == false) { + throw new IOException("NSS initialization failed"); + } + + this.configDir = configDir; + this.nssLibDir = nssLibDir; + } + + /** + * Return an immutable list of all available modules. + * + * @throws IllegalStateException if this Secmod is misconfigured + * or not initialized + */ + public synchronized List getModules() { + try { + if (isInitialized() == false) { + throw new IllegalStateException("NSS not initialized"); + } + } catch (IOException e) { + // IOException if misconfigured + throw new IllegalStateException(e); + } + if (modules == null) { + @SuppressWarnings("unchecked") + List modules = (List)nssGetModuleList(nssHandle, + nssLibDir); + this.modules = Collections.unmodifiableList(modules); + } + return modules; + } + + private static byte[] getDigest(X509Certificate cert, String algorithm) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + return md.digest(cert.getEncoded()); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + } + + boolean isTrusted(X509Certificate cert, TrustType trustType) { + Bytes bytes = new Bytes(getDigest(cert, "SHA-1")); + TrustAttributes attr = getModuleTrust(ModuleType.KEYSTORE, bytes); + if (attr == null) { + attr = getModuleTrust(ModuleType.FIPS, bytes); + if (attr == null) { + attr = getModuleTrust(ModuleType.TRUSTANCHOR, bytes); + } + } + return (attr == null) ? false : attr.isTrusted(trustType); + } + + private TrustAttributes getModuleTrust(ModuleType type, Bytes bytes) { + Module module = getModule(type); + TrustAttributes t = (module == null) ? null : module.getTrust(bytes); + return t; + } + + /** + * Constants describing the different types of NSS modules. + * For this API, NSS modules are classified as either one + * of the internal modules delivered as part of NSS or + * as an external module provided by a 3rd party. + */ + public static enum ModuleType { + /** + * The NSS Softtoken crypto module. This is the first + * slot of the softtoken object. + * This module provides + * implementations for cryptographic algorithms but no KeyStore. + */ + CRYPTO, + /** + * The NSS Softtoken KeyStore module. This is the second + * slot of the softtoken object. + * This module provides + * implementations for cryptographic algorithms (after login) + * and the KeyStore. + */ + KEYSTORE, + /** + * The NSS Softtoken module in FIPS mode. Note that in FIPS mode the + * softtoken presents only one slot, not separate CRYPTO and KEYSTORE + * slots as in non-FIPS mode. + */ + FIPS, + /** + * The NSS builtin trust anchor module. This is the + * NSSCKBI object. It provides no crypto functions. + */ + TRUSTANCHOR, + /** + * An external module. + */ + EXTERNAL, + } + + /** + * Returns the first module of the specified type. If no such + * module exists, this method returns null. + * + * @throws IllegalStateException if this Secmod is misconfigured + * or not initialized + */ + public Module getModule(ModuleType type) { + for (Module module : getModules()) { + if (module.getType() == type) { + return module; + } + } + return null; + } + + static final String TEMPLATE_EXTERNAL = + "library = %s\n" + + "name = \"%s\"\n" + + "slotListIndex = %d\n"; + + static final String TEMPLATE_TRUSTANCHOR = + "library = %s\n" + + "name = \"NSS Trust Anchors\"\n" + + "slotListIndex = 0\n" + + "enabledMechanisms = { KeyStore }\n" + + "nssUseSecmodTrust = true\n"; + + static final String TEMPLATE_CRYPTO = + "library = %s\n" + + "name = \"NSS SoftToken Crypto\"\n" + + "slotListIndex = 0\n" + + "disabledMechanisms = { KeyStore }\n"; + + static final String TEMPLATE_KEYSTORE = + "library = %s\n" + + "name = \"NSS SoftToken KeyStore\"\n" + + "slotListIndex = 1\n" + + "nssUseSecmodTrust = true\n"; + + static final String TEMPLATE_FIPS = + "library = %s\n" + + "name = \"NSS FIPS SoftToken\"\n" + + "slotListIndex = 0\n" + + "nssUseSecmodTrust = true\n"; + + /** + * A representation of one PKCS#11 slot in a PKCS#11 module. + */ + public static final class Module { + // path of the native library + final String libraryName; + // descriptive name used by NSS + final String commonName; + final int slot; + final ModuleType type; + + private String config; + private SunPKCS11 provider; + + // trust attributes. Used for the KEYSTORE and TRUSTANCHOR modules only + private Map trust; + + Module(String libraryDir, String libraryName, String commonName, + boolean fips, int slot) { + ModuleType type; + + if ((libraryName == null) || (libraryName.length() == 0)) { + // must be softtoken + libraryName = System.mapLibraryName(SOFTTOKEN_LIB_NAME); + if (fips == false) { + type = (slot == 0) ? ModuleType.CRYPTO : ModuleType.KEYSTORE; + } else { + type = ModuleType.FIPS; + if (slot != 0) { + throw new RuntimeException + ("Slot index should be 0 for FIPS slot"); + } + } + } else { + if (libraryName.endsWith(System.mapLibraryName(TRUST_LIB_NAME)) + || commonName.equals("Builtin Roots Module")) { + type = ModuleType.TRUSTANCHOR; + } else { + type = ModuleType.EXTERNAL; + } + if (fips) { + throw new RuntimeException("FIPS flag set for non-internal " + + "module: " + libraryName + ", " + commonName); + } + } + // On Ubuntu the libsoftokn3 library is located in a subdirectory + // of the system libraries directory. (Since Ubuntu 11.04.) + File libraryFile = new File(libraryDir, libraryName); + if (!libraryFile.isFile()) { + File failover = new File(libraryDir, "nss/" + libraryName); + if (failover.isFile()) { + libraryFile = failover; + } + } + this.libraryName = libraryFile.getPath(); + this.commonName = commonName; + this.slot = slot; + this.type = type; + initConfiguration(); + } + + private void initConfiguration() { + switch (type) { + case EXTERNAL: + config = String.format(TEMPLATE_EXTERNAL, libraryName, + commonName + " " + slot, slot); + break; + case CRYPTO: + config = String.format(TEMPLATE_CRYPTO, libraryName); + break; + case KEYSTORE: + config = String.format(TEMPLATE_KEYSTORE, libraryName); + break; + case FIPS: + config = String.format(TEMPLATE_FIPS, libraryName); + break; + case TRUSTANCHOR: + config = String.format(TEMPLATE_TRUSTANCHOR, libraryName); + break; + default: + throw new RuntimeException("Unknown module type: " + type); + } + } + + /** + * Get the configuration for this module. This is a string + * in the SunPKCS11 configuration format. It can be + * customized with additional options and then made + * current using the setConfiguration() method. + */ + @Deprecated + public synchronized String getConfiguration() { + return config; + } + + /** + * Set the configuration for this module. + * + * @throws IllegalStateException if the associated provider + * instance has already been created. + */ + @Deprecated + public synchronized void setConfiguration(String config) { + if (provider != null) { + throw new IllegalStateException("Provider instance already created"); + } + this.config = config; + } + + /** + * Return the pathname of the native library that implements + * this module. For example, /usr/lib/libpkcs11.so. + */ + public String getLibraryName() { + return libraryName; + } + + /** + * Returns the type of this module. + */ + public ModuleType getType() { + return type; + } + + /** + * Returns the provider instance that is associated with this + * module. The first call to this method creates the provider + * instance. + */ + @Deprecated + public synchronized Provider getProvider() { + if (provider == null) { + provider = newProvider(); + } + return provider; + } + + synchronized boolean hasInitializedProvider() { + return provider != null; + } + + void setProvider(SunPKCS11 p) { + if (provider != null) { + throw new ProviderException("Secmod provider already initialized"); + } + provider = p; + } + + private SunPKCS11 newProvider() { + try { + InputStream in = new ByteArrayInputStream(config.getBytes("UTF8")); + return new SunPKCS11(in); + } catch (Exception e) { + // XXX + throw new ProviderException(e); + } + } + + synchronized void setTrust(Token token, X509Certificate cert) { + Bytes bytes = new Bytes(getDigest(cert, "SHA-1")); + TrustAttributes attr = getTrust(bytes); + if (attr == null) { + attr = new TrustAttributes(token, cert, bytes, CKT_NETSCAPE_TRUSTED_DELEGATOR); + trust.put(bytes, attr); + } else { + // does it already have the correct trust settings? + if (attr.isTrusted(TrustType.ALL) == false) { + // XXX not yet implemented + throw new ProviderException("Cannot change existing trust attributes"); + } + } + } + + TrustAttributes getTrust(Bytes hash) { + if (trust == null) { + // If provider is not set, create a temporary provider to + // retrieve the trust information. This can happen if we need + // to get the trust information for the trustanchor module + // because we need to look for user customized settings in the + // keystore module (which may not have a provider created yet). + // Creating a temporary provider and then dropping it on the + // floor immediately is flawed, but it's the best we can do + // for now. + synchronized (this) { + SunPKCS11 p = provider; + if (p == null) { + p = newProvider(); + } + try { + trust = Secmod.getTrust(p); + } catch (PKCS11Exception e) { + throw new RuntimeException(e); + } + } + } + return trust.get(hash); + } + + public String toString() { + return + commonName + " (" + type + ", " + libraryName + ", slot " + slot + ")"; + } + + } + + /** + * Constants representing NSS trust categories. + */ + public static enum TrustType { + /** Trusted for all purposes */ + ALL, + /** Trusted for SSL client authentication */ + CLIENT_AUTH, + /** Trusted for SSL server authentication */ + SERVER_AUTH, + /** Trusted for code signing */ + CODE_SIGNING, + /** Trusted for email protection */ + EMAIL_PROTECTION, + } + + public static enum DbMode { + READ_WRITE("NSS_InitReadWrite"), + READ_ONLY ("NSS_Init"), + NO_DB ("NSS_NoDB_Init"); + + final String functionName; + DbMode(String functionName) { + this.functionName = functionName; + } + } + + /** + * A LoadStoreParameter for use with the NSS Softtoken or + * NSS TrustAnchor KeyStores. + *

    + * It allows the set of trusted certificates that are returned by + * the KeyStore to be specified. + */ + public static final class KeyStoreLoadParameter implements LoadStoreParameter { + final TrustType trustType; + final ProtectionParameter protection; + public KeyStoreLoadParameter(TrustType trustType, char[] password) { + this(trustType, new PasswordProtection(password)); + + } + public KeyStoreLoadParameter(TrustType trustType, ProtectionParameter prot) { + if (trustType == null) { + throw new NullPointerException("trustType must not be null"); + } + this.trustType = trustType; + this.protection = prot; + } + public ProtectionParameter getProtectionParameter() { + return protection; + } + public TrustType getTrustType() { + return trustType; + } + } + + static class TrustAttributes { + final long handle; + final long clientAuth, serverAuth, codeSigning, emailProtection; + final byte[] shaHash; + TrustAttributes(Token token, X509Certificate cert, Bytes bytes, long trustValue) { + Session session = null; + try { + session = token.getOpSession(); + // XXX use KeyStore TrustType settings to determine which + // attributes to set + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_TOKEN, true), + new CK_ATTRIBUTE(CKA_CLASS, CKO_NETSCAPE_TRUST), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_SERVER_AUTH, trustValue), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CODE_SIGNING, trustValue), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_EMAIL_PROTECTION, trustValue), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CLIENT_AUTH, trustValue), + new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_SHA1_HASH, bytes.b), + new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_MD5_HASH, getDigest(cert, "MD5")), + new CK_ATTRIBUTE(CKA_ISSUER, cert.getIssuerX500Principal().getEncoded()), + new CK_ATTRIBUTE(CKA_SERIAL_NUMBER, cert.getSerialNumber().toByteArray()), + // XXX per PKCS#11 spec, the serial number should be in ASN.1 + }; + handle = token.p11.C_CreateObject(session.id(), attrs); + shaHash = bytes.b; + clientAuth = trustValue; + serverAuth = trustValue; + codeSigning = trustValue; + emailProtection = trustValue; + } catch (PKCS11Exception e) { + throw new ProviderException("Could not create trust object", e); + } finally { + token.releaseSession(session); + } + } + TrustAttributes(Token token, Session session, long handle) + throws PKCS11Exception { + this.handle = handle; + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_SERVER_AUTH), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CODE_SIGNING), + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_EMAIL_PROTECTION), + new CK_ATTRIBUTE(CKA_NETSCAPE_CERT_SHA1_HASH), + }; + + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + serverAuth = attrs[0].getLong(); + codeSigning = attrs[1].getLong(); + emailProtection = attrs[2].getLong(); + shaHash = attrs[3].getByteArray(); + + attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_NETSCAPE_TRUST_CLIENT_AUTH), + }; + long c; + try { + token.p11.C_GetAttributeValue(session.id(), handle, attrs); + c = attrs[0].getLong(); + } catch (PKCS11Exception e) { + // trust anchor module does not support this attribute + c = serverAuth; + } + clientAuth = c; + } + Bytes getHash() { + return new Bytes(shaHash); + } + boolean isTrusted(TrustType type) { + switch (type) { + case CLIENT_AUTH: + return isTrusted(clientAuth); + case SERVER_AUTH: + return isTrusted(serverAuth); + case CODE_SIGNING: + return isTrusted(codeSigning); + case EMAIL_PROTECTION: + return isTrusted(emailProtection); + case ALL: + return isTrusted(TrustType.CLIENT_AUTH) + && isTrusted(TrustType.SERVER_AUTH) + && isTrusted(TrustType.CODE_SIGNING) + && isTrusted(TrustType.EMAIL_PROTECTION); + default: + return false; + } + } + + private boolean isTrusted(long l) { + // XXX CKT_TRUSTED? + return (l == CKT_NETSCAPE_TRUSTED_DELEGATOR); + } + + } + + private static class Bytes { + final byte[] b; + Bytes(byte[] b) { + this.b = b; + } + public int hashCode() { + return Arrays.hashCode(b); + } + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof Bytes == false) { + return false; + } + Bytes other = (Bytes)o; + return Arrays.equals(this.b, other.b); + } + } + + private static Map getTrust(SunPKCS11 provider) + throws PKCS11Exception { + Map trustMap = new HashMap(); + Token token = provider.getToken(); + Session session = null; + try { + session = token.getOpSession(); + int MAX_NUM = 8192; + CK_ATTRIBUTE[] attrs = new CK_ATTRIBUTE[] { + new CK_ATTRIBUTE(CKA_CLASS, CKO_NETSCAPE_TRUST), + }; + token.p11.C_FindObjectsInit(session.id(), attrs); + long[] handles = token.p11.C_FindObjects(session.id(), MAX_NUM); + token.p11.C_FindObjectsFinal(session.id()); + if (DEBUG) System.out.println("handles: " + handles.length); + + for (long handle : handles) { + try { + TrustAttributes trust = new TrustAttributes(token, session, handle); + trustMap.put(trust.getHash(), trust); + } catch (PKCS11Exception e) { + // skip put on pkcs11 error + } + } + } finally { + token.releaseSession(session); + } + return trustMap; + } + + private static native long nssGetLibraryHandle(String libraryName); + + private static native long nssLoadLibrary(String name) throws IOException; + + private static native boolean nssVersionCheck(long handle, String minVersion); + + private static native boolean nssInitialize(String functionName, long handle, String configDir, boolean nssOptimizeSpace); + + private static native Object nssGetModuleList(long handle, String libDir); + +} diff --git a/src/sun/security/pkcs11/Session.java b/src/sun/security/pkcs11/Session.java new file mode 100644 index 00000000..99d6da78 --- /dev/null +++ b/src/sun/security/pkcs11/Session.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.lang.ref.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +import java.security.*; + +import sun.security.pkcs11.wrapper.*; + +/** + * A session object. Sessions are obtained via the SessionManager, + * see there for details. Most code will only ever need one method in + * this class, the id() method to obtain the session id. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class Session implements Comparable { + + // time after which to close idle sessions, in milliseconds (3 minutes) + private final static long MAX_IDLE_TIME = 3 * 60 * 1000; + + // token instance + final Token token; + + // session id + private final long id; + + // number of objects created within this session + private final AtomicInteger createdObjects; + + // time this session was last used + // not synchronized/volatile for performance, so may be unreliable + // this could lead to idle sessions being closed early, but that is harmless + private long lastAccess; + + private final SessionRef sessionRef; + + Session(Token token, long id) { + this.token = token; + this.id = id; + createdObjects = new AtomicInteger(); + id(); + sessionRef = new SessionRef(this, id, token); + } + + public int compareTo(Session other) { + if (this.lastAccess == other.lastAccess) { + return 0; + } else { + return (this.lastAccess < other.lastAccess) ? -1 : 1; + } + } + + boolean isLive(long currentTime) { + return currentTime - lastAccess < MAX_IDLE_TIME; + } + + long idInternal() { + return id; + } + + long id() { + if (token.isPresent(this.id) == false) { + throw new ProviderException("Token has been removed"); + } + lastAccess = System.currentTimeMillis(); + return id; + } + + void addObject() { + int n = createdObjects.incrementAndGet(); + // XXX update statistics in session manager if n == 1 + } + + void removeObject() { + int n = createdObjects.decrementAndGet(); + if (n == 0) { + token.sessionManager.demoteObjSession(this); + } else if (n < 0) { + throw new ProviderException("Internal error: objects created " + n); + } + } + + boolean hasObjects() { + return createdObjects.get() != 0; + } + + void close() { + if (hasObjects()) { + throw new ProviderException( + "Internal error: close session with active objects"); + } + sessionRef.dispose(); + } +} + +/* + * NOTE: Use PhantomReference here and not WeakReference + * otherwise the sessions maybe closed before other objects + * which are still being finalized. + */ +final class SessionRef extends PhantomReference + implements Comparable { + + private static ReferenceQueue refQueue = + new ReferenceQueue(); + + private static Set refList = + Collections.synchronizedSortedSet(new TreeSet()); + + static ReferenceQueue referenceQueue() { + return refQueue; + } + + static int totalCount() { + return refList.size(); + } + + private static void drainRefQueueBounded() { + while (true) { + SessionRef next = (SessionRef) refQueue.poll(); + if (next == null) break; + next.dispose(); + } + } + + // handle to the native session + private long id; + private Token token; + + SessionRef(Session session, long id, Token token) { + super(session, refQueue); + this.id = id; + this.token = token; + refList.add(this); + // TBD: run at some interval and not every time? + drainRefQueueBounded(); + } + + void dispose() { + refList.remove(this); + try { + if (token.isPresent(id)) { + token.p11.C_CloseSession(id); + } + } catch (PKCS11Exception e1) { + // ignore + } catch (ProviderException e2) { + // ignore + } finally { + this.clear(); + } + } + + public int compareTo(SessionRef other) { + if (this.id == other.id) { + return 0; + } else { + return (this.id < other.id) ? -1 : 1; + } + } +} diff --git a/src/sun/security/pkcs11/SessionManager.java b/src/sun/security/pkcs11/SessionManager.java new file mode 100644 index 00000000..fe511deb --- /dev/null +++ b/src/sun/security/pkcs11/SessionManager.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.security.ProviderException; + +import sun.security.util.Debug; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Session manager. There is one session manager object per PKCS#11 + * provider. It allows code to checkout a session, release it + * back to the pool, or force it to be closed. + * + * The session manager pools sessions to minimize the number of + * C_OpenSession() and C_CloseSession() that have to be made. It + * maintains two pools: one for "object" sessions and one for + * "operation" sessions. + * + * The reason for this separation is how PKCS#11 deals with session objects. + * It defines that when a session is closed, all objects created within + * that session are destroyed. In other words, we may never close a session + * while a Key created it in is still in use. We would like to keep the + * number of such sessions low. Note that we occasionally want to explicitly + * close a session, see P11Signature. + * + * NOTE that sessions obtained from this class SHOULD be returned using + * either releaseSession() or closeSession() using a finally block when + * not needed anymore. Otherwise, they will be left for cleanup via the + * PhantomReference mechanism when GC kicks in, but it's best not to rely + * on that since GC may not run timely enough since the native PKCS11 library + * is also consuming memory. + * + * Note that sessions are automatically closed when they are not used for a + * period of time, see Session. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class SessionManager { + + private final static int DEFAULT_MAX_SESSIONS = 32; + + private final static Debug debug = Debug.getInstance("pkcs11"); + + // token instance + private final Token token; + + // maximum number of sessions to open with this token + private final int maxSessions; + + // total number of active sessions + private AtomicInteger activeSessions = new AtomicInteger(); + + // pool of available object sessions + private final Pool objSessions; + + // pool of available operation sessions + private final Pool opSessions; + + // maximum number of active sessions during this invocation, for debugging + private int maxActiveSessions; + + // flags to use in the C_OpenSession() call + private final long openSessionFlags; + + SessionManager(Token token) { + long n; + if (token.isWriteProtected()) { + openSessionFlags = CKF_SERIAL_SESSION; + n = token.tokenInfo.ulMaxSessionCount; + } else { + openSessionFlags = CKF_SERIAL_SESSION | CKF_RW_SESSION; + n = token.tokenInfo.ulMaxRwSessionCount; + } + if (n == CK_EFFECTIVELY_INFINITE) { + n = Integer.MAX_VALUE; + } else if ((n == CK_UNAVAILABLE_INFORMATION) || (n < 0)) { + // choose an arbitrary concrete value + n = DEFAULT_MAX_SESSIONS; + } + maxSessions = (int)Math.min(n, Integer.MAX_VALUE); + this.token = token; + this.objSessions = new Pool(this); + this.opSessions = new Pool(this); + } + + // returns whether only a fairly low number of sessions are + // supported by this token. + boolean lowMaxSessions() { + return (maxSessions <= DEFAULT_MAX_SESSIONS); + } + + Session getObjSession() throws PKCS11Exception { + Session session = objSessions.poll(); + if (session != null) { + return ensureValid(session); + } + session = opSessions.poll(); + if (session != null) { + return ensureValid(session); + } + session = openSession(); + return ensureValid(session); + } + + Session getOpSession() throws PKCS11Exception { + Session session = opSessions.poll(); + if (session != null) { + return ensureValid(session); + } + // create a new session rather than re-using an obj session + // that avoids potential expensive cancels() for Signatures & RSACipher + if (maxSessions == Integer.MAX_VALUE || + activeSessions.get() < maxSessions) { + session = openSession(); + return ensureValid(session); + } + session = objSessions.poll(); + if (session != null) { + return ensureValid(session); + } + throw new ProviderException("Could not obtain session"); + } + + private Session ensureValid(Session session) { + session.id(); + return session; + } + + Session killSession(Session session) { + if ((session == null) || (token.isValid() == false)) { + return null; + } + if (debug != null) { + String location = new Exception().getStackTrace()[2].toString(); + System.out.println("Killing session (" + location + ") active: " + + activeSessions.get()); + } + closeSession(session); + return null; + } + + Session releaseSession(Session session) { + if ((session == null) || (token.isValid() == false)) { + return null; + } + + if (session.hasObjects()) { + objSessions.release(session); + } else { + opSessions.release(session); + } + return null; + } + + void demoteObjSession(Session session) { + if (token.isValid() == false) { + return; + } + if (debug != null) { + System.out.println("Demoting session, active: " + + activeSessions.get()); + } + boolean present = objSessions.remove(session); + if (present == false) { + // session is currently in use + // will be added to correct pool on release, nothing to do now + return; + } + opSessions.release(session); + } + + private Session openSession() throws PKCS11Exception { + if ((maxSessions != Integer.MAX_VALUE) && + (activeSessions.get() >= maxSessions)) { + throw new ProviderException("No more sessions available"); + } + + long id = token.p11.C_OpenSession + (token.provider.slotID, openSessionFlags, null, null); + Session session = new Session(token, id); + activeSessions.incrementAndGet(); + if (debug != null) { + synchronized(this) { + if (activeSessions.get() > maxActiveSessions) { + maxActiveSessions = activeSessions.get(); + if (maxActiveSessions % 10 == 0) { + System.out.println("Open sessions: " + maxActiveSessions); + } + } + } + } + return session; + } + + private void closeSession(Session session) { + session.close(); + activeSessions.decrementAndGet(); + } + + public static final class Pool { + + private final SessionManager mgr; + + private final ConcurrentLinkedDeque pool; + + Pool(SessionManager mgr) { + this.mgr = mgr; + pool = new ConcurrentLinkedDeque(); + } + + boolean remove(Session session) { + return pool.remove(session); + } + + Session poll() { + return pool.pollLast(); + } + + void release(Session session) { + pool.offer(session); + if (session.hasObjects()) { + return; + } + + int n = pool.size(); + if (n < 5) { + return; + } + + Session oldestSession; + long time = System.currentTimeMillis(); + int i = 0; + // Check if the session head is too old and continue through queue + // until only one is left. + do { + oldestSession = pool.peek(); + if (oldestSession == null || oldestSession.isLive(time) || + !pool.remove(oldestSession)) { + break; + } + + i++; + mgr.closeSession(oldestSession); + } while ((n - i) > 1); + + if (debug != null) { + System.out.println("Closing " + i + " idle sessions, active: " + + mgr.activeSessions); + } + } + + } + +} diff --git a/src/sun/security/pkcs11/SunPKCS11.java b/src/sun/security/pkcs11/SunPKCS11.java new file mode 100644 index 00000000..50ee6772 --- /dev/null +++ b/src/sun/security/pkcs11/SunPKCS11.java @@ -0,0 +1,1453 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.io.*; +import java.util.*; + +import java.security.*; +import java.security.interfaces.*; + +import javax.crypto.interfaces.*; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.PasswordCallback; + +import sun.security.util.Debug; +import sun.security.util.ResourcesMgr; + +import sun.security.pkcs11.Secmod.*; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * PKCS#11 provider main class. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class SunPKCS11 extends AuthProvider { + + private static final long serialVersionUID = -1354835039035306505L; + + static final Debug debug = Debug.getInstance("sunpkcs11"); + + private static int dummyConfigId; + + // the PKCS11 object through which we make the native calls + final PKCS11 p11; + + // name of the configuration file + private final String configName; + + // configuration information + final Config config; + + // id of the PKCS#11 slot we are using + final long slotID; + + private CallbackHandler pHandler; + private final Object LOCK_HANDLER = new Object(); + + final boolean removable; + + final Module nssModule; + + final boolean nssUseSecmodTrust; + + private volatile Token token; + + private TokenPoller poller; + + Token getToken() { + return token; + } + + public SunPKCS11() { + super("SunPKCS11-Dummy", 1.8d, "SunPKCS11-Dummy"); + throw new ProviderException + ("SunPKCS11 requires configuration file argument"); + } + + public SunPKCS11(String configName) { + this(checkNull(configName), null); + } + + public SunPKCS11(InputStream configStream) { + this(getDummyConfigName(), checkNull(configStream)); + } + + private static T checkNull(T obj) { + if (obj == null) { + throw new NullPointerException(); + } + return obj; + } + + private static synchronized String getDummyConfigName() { + int id = ++dummyConfigId; + return "---DummyConfig-" + id + "---"; + } + + /** + * @deprecated use new SunPKCS11(String) or new SunPKCS11(InputStream) + * instead + */ + @Deprecated + public SunPKCS11(String configName, InputStream configStream) { + super("SunPKCS11-" + + Config.getConfig(configName, configStream).getName(), + 1.8d, Config.getConfig(configName, configStream).getDescription()); + this.configName = configName; + this.config = Config.removeConfig(configName); + + if (debug != null) { + System.out.println("SunPKCS11 loading " + configName); + } + + String library = config.getLibrary(); + String functionList = config.getFunctionList(); + long slotID = config.getSlotID(); + int slotListIndex = config.getSlotListIndex(); + + boolean useSecmod = config.getNssUseSecmod(); + boolean nssUseSecmodTrust = config.getNssUseSecmodTrust(); + Module nssModule = null; + + // + // Initialization via Secmod. The way this works is as follows: + // SunPKCS11 is either in normal mode or in NSS Secmod mode. + // Secmod is activated by specifying one or more of the following + // options in the config file: + // nssUseSecmod, nssSecmodDirectory, nssLibrary, nssModule + // + // XXX add more explanation here + // + // If we are in Secmod mode and configured to use either the + // nssKeyStore or the nssTrustAnchors module, we automatically + // switch to using the NSS trust attributes for trusted certs + // (KeyStore). + // + + if (useSecmod) { + // note: Config ensures library/slot/slotListIndex not specified + // in secmod mode. + Secmod secmod = Secmod.getInstance(); + DbMode nssDbMode = config.getNssDbMode(); + try { + String nssLibraryDirectory = config.getNssLibraryDirectory(); + String nssSecmodDirectory = config.getNssSecmodDirectory(); + boolean nssOptimizeSpace = config.getNssOptimizeSpace(); + + if (secmod.isInitialized()) { + if (nssSecmodDirectory != null) { + String s = secmod.getConfigDir(); + if ((s != null) && + (s.equals(nssSecmodDirectory) == false)) { + throw new ProviderException("Secmod directory " + + nssSecmodDirectory + + " invalid, NSS already initialized with " + + s); + } + } + if (nssLibraryDirectory != null) { + String s = secmod.getLibDir(); + if ((s != null) && + (s.equals(nssLibraryDirectory) == false)) { + throw new ProviderException("NSS library directory " + + nssLibraryDirectory + + " invalid, NSS already initialized with " + + s); + } + } + } else { + if (nssDbMode != DbMode.NO_DB) { + if (nssSecmodDirectory == null) { + throw new ProviderException( + "Secmod not initialized and " + + "nssSecmodDirectory not specified"); + } + } else { + if (nssSecmodDirectory != null) { + throw new ProviderException( + "nssSecmodDirectory must not be " + + "specified in noDb mode"); + } + } + secmod.initialize(nssDbMode, nssSecmodDirectory, + nssLibraryDirectory, nssOptimizeSpace); + } + } catch (IOException e) { + // XXX which exception to throw + throw new ProviderException("Could not initialize NSS", e); + } + List modules = secmod.getModules(); + if (config.getShowInfo()) { + System.out.println("NSS modules: " + modules); + } + + String moduleName = config.getNssModule(); + if (moduleName == null) { + nssModule = secmod.getModule(ModuleType.FIPS); + if (nssModule != null) { + moduleName = "fips"; + } else { + moduleName = (nssDbMode == DbMode.NO_DB) ? + "crypto" : "keystore"; + } + } + if (moduleName.equals("fips")) { + nssModule = secmod.getModule(ModuleType.FIPS); + nssUseSecmodTrust = true; + functionList = "FC_GetFunctionList"; + } else if (moduleName.equals("keystore")) { + nssModule = secmod.getModule(ModuleType.KEYSTORE); + nssUseSecmodTrust = true; + } else if (moduleName.equals("crypto")) { + nssModule = secmod.getModule(ModuleType.CRYPTO); + } else if (moduleName.equals("trustanchors")) { + // XXX should the option be called trustanchor or trustanchors?? + nssModule = secmod.getModule(ModuleType.TRUSTANCHOR); + nssUseSecmodTrust = true; + } else if (moduleName.startsWith("external-")) { + int moduleIndex; + try { + moduleIndex = Integer.parseInt + (moduleName.substring("external-".length())); + } catch (NumberFormatException e) { + moduleIndex = -1; + } + if (moduleIndex < 1) { + throw new ProviderException + ("Invalid external module: " + moduleName); + } + int k = 0; + for (Module module : modules) { + if (module.getType() == ModuleType.EXTERNAL) { + if (++k == moduleIndex) { + nssModule = module; + break; + } + } + } + if (nssModule == null) { + throw new ProviderException("Invalid module " + moduleName + + ": only " + k + " external NSS modules available"); + } + } else { + throw new ProviderException( + "Unknown NSS module: " + moduleName); + } + if (nssModule == null) { + throw new ProviderException( + "NSS module not available: " + moduleName); + } + if (nssModule.hasInitializedProvider()) { + throw new ProviderException("Secmod module already configured"); + } + library = nssModule.libraryName; + slotListIndex = nssModule.slot; + } + this.nssUseSecmodTrust = nssUseSecmodTrust; + this.nssModule = nssModule; + + File libraryFile = new File(library); + // if the filename is a simple filename without path + // (e.g. "libpkcs11.so"), it may refer to a library somewhere on the + // OS library search path. Omit the test for file existance as that + // only looks in the current directory. + if (libraryFile.getName().equals(library) == false) { + if (new File(library).isFile() == false) { + String msg = "Library " + library + " does not exist"; + if (config.getHandleStartupErrors() == Config.ERR_HALT) { + throw new ProviderException(msg); + } else { + throw new UnsupportedOperationException(msg); + } + } + } + + try { + if (debug != null) { + debug.println("Initializing PKCS#11 library " + library); + } + CK_C_INITIALIZE_ARGS initArgs = new CK_C_INITIALIZE_ARGS(); + String nssArgs = config.getNssArgs(); + if (nssArgs != null) { + initArgs.pReserved = nssArgs; + } + // request multithreaded access first + initArgs.flags = CKF_OS_LOCKING_OK; + PKCS11 tmpPKCS11; + try { + tmpPKCS11 = PKCS11.getInstance( + library, functionList, initArgs, + config.getOmitInitialize()); + } catch (PKCS11Exception e) { + if (debug != null) { + debug.println("Multi-threaded initialization failed: " + e); + } + if (config.getAllowSingleThreadedModules() == false) { + throw e; + } + // fall back to single threaded access + if (nssArgs == null) { + // if possible, use null initArgs for better compatibility + initArgs = null; + } else { + initArgs.flags = 0; + } + tmpPKCS11 = PKCS11.getInstance(library, + functionList, initArgs, config.getOmitInitialize()); + } + p11 = tmpPKCS11; + + CK_INFO p11Info = p11.C_GetInfo(); + if (p11Info.cryptokiVersion.major < 2) { + throw new ProviderException("Only PKCS#11 v2.0 and later " + + "supported, library version is v" + p11Info.cryptokiVersion); + } + boolean showInfo = config.getShowInfo(); + if (showInfo) { + System.out.println("Information for provider " + getName()); + System.out.println("Library info:"); + System.out.println(p11Info); + } + + if ((slotID < 0) || showInfo) { + long[] slots = p11.C_GetSlotList(false); + if (showInfo) { + System.out.println("All slots: " + toString(slots)); + slots = p11.C_GetSlotList(true); + System.out.println("Slots with tokens: " + toString(slots)); + } + if (slotID < 0) { + if ((slotListIndex < 0) + || (slotListIndex >= slots.length)) { + throw new ProviderException("slotListIndex is " + + slotListIndex + + " but token only has " + slots.length + " slots"); + } + slotID = slots[slotListIndex]; + } + } + this.slotID = slotID; + CK_SLOT_INFO slotInfo = p11.C_GetSlotInfo(slotID); + removable = (slotInfo.flags & CKF_REMOVABLE_DEVICE) != 0; + initToken(slotInfo); + if (nssModule != null) { + nssModule.setProvider(this); + } + } catch (Exception e) { + if (config.getHandleStartupErrors() == Config.ERR_IGNORE_ALL) { + throw new UnsupportedOperationException + ("Initialization failed", e); + } else { + throw new ProviderException + ("Initialization failed", e); + } + } + } + + private static String toString(long[] longs) { + if (longs.length == 0) { + return "(none)"; + } + StringBuilder sb = new StringBuilder(); + sb.append(longs[0]); + for (int i = 1; i < longs.length; i++) { + sb.append(", "); + sb.append(longs[i]); + } + return sb.toString(); + } + + public boolean equals(Object obj) { + return this == obj; + } + + public int hashCode() { + return System.identityHashCode(this); + } + + private static String[] s(String ...aliases) { + return aliases; + } + + private static final class Descriptor { + final String type; + final String algorithm; + final String className; + final String[] aliases; + final int[] mechanisms; + + private Descriptor(String type, String algorithm, String className, + String[] aliases, int[] mechanisms) { + this.type = type; + this.algorithm = algorithm; + this.className = className; + this.aliases = aliases; + this.mechanisms = mechanisms; + } + private P11Service service(Token token, int mechanism) { + return new P11Service + (token, type, algorithm, className, aliases, mechanism); + } + public String toString() { + return type + "." + algorithm; + } + } + + // Map from mechanism to List of Descriptors that should be + // registered if the mechanism is supported + private final static Map> descriptors = + new HashMap>(); + + private static int[] m(long m1) { + return new int[] {(int)m1}; + } + + private static int[] m(long m1, long m2) { + return new int[] {(int)m1, (int)m2}; + } + + private static int[] m(long m1, long m2, long m3) { + return new int[] {(int)m1, (int)m2, (int)m3}; + } + + private static int[] m(long m1, long m2, long m3, long m4) { + return new int[] {(int)m1, (int)m2, (int)m3, (int)m4}; + } + + private static void d(String type, String algorithm, String className, + int[] m) { + register(new Descriptor(type, algorithm, className, null, m)); + } + + private static void d(String type, String algorithm, String className, + String[] aliases, int[] m) { + register(new Descriptor(type, algorithm, className, aliases, m)); + } + + private static void register(Descriptor d) { + for (int i = 0; i < d.mechanisms.length; i++) { + int m = d.mechanisms[i]; + Integer key = Integer.valueOf(m); + List list = descriptors.get(key); + if (list == null) { + list = new ArrayList(); + descriptors.put(key, list); + } + list.add(d); + } + } + + private final static String MD = "MessageDigest"; + + private final static String SIG = "Signature"; + + private final static String KPG = "KeyPairGenerator"; + + private final static String KG = "KeyGenerator"; + + private final static String AGP = "AlgorithmParameters"; + + private final static String KF = "KeyFactory"; + + private final static String SKF = "SecretKeyFactory"; + + private final static String CIP = "Cipher"; + + private final static String MAC = "Mac"; + + private final static String KA = "KeyAgreement"; + + private final static String KS = "KeyStore"; + + private final static String SR = "SecureRandom"; + + static { + // names of all the implementation classes + // use local variables, only used here + String P11Digest = "sun.security.pkcs11.P11Digest"; + String P11MAC = "sun.security.pkcs11.P11MAC"; + String P11KeyPairGenerator = "sun.security.pkcs11.P11KeyPairGenerator"; + String P11KeyGenerator = "sun.security.pkcs11.P11KeyGenerator"; + String P11RSAKeyFactory = "sun.security.pkcs11.P11RSAKeyFactory"; + String P11DSAKeyFactory = "sun.security.pkcs11.P11DSAKeyFactory"; + String P11DHKeyFactory = "sun.security.pkcs11.P11DHKeyFactory"; + String P11KeyAgreement = "sun.security.pkcs11.P11KeyAgreement"; + String P11SecretKeyFactory = "sun.security.pkcs11.P11SecretKeyFactory"; + String P11Cipher = "sun.security.pkcs11.P11Cipher"; + String P11RSACipher = "sun.security.pkcs11.P11RSACipher"; + String P11Signature = "sun.security.pkcs11.P11Signature"; + + // XXX register all aliases + + d(MD, "MD2", P11Digest, + m(CKM_MD2)); + d(MD, "MD5", P11Digest, + m(CKM_MD5)); + d(MD, "SHA1", P11Digest, + s("SHA", "SHA-1", "1.3.14.3.2.26", "OID.1.3.14.3.2.26"), + m(CKM_SHA_1)); + + d(MD, "SHA-224", P11Digest, + s("2.16.840.1.101.3.4.2.4", "OID.2.16.840.1.101.3.4.2.4"), + m(CKM_SHA224)); + d(MD, "SHA-256", P11Digest, + s("2.16.840.1.101.3.4.2.1", "OID.2.16.840.1.101.3.4.2.1"), + m(CKM_SHA256)); + d(MD, "SHA-384", P11Digest, + s("2.16.840.1.101.3.4.2.2", "OID.2.16.840.1.101.3.4.2.2"), + m(CKM_SHA384)); + d(MD, "SHA-512", P11Digest, + s("2.16.840.1.101.3.4.2.3", "OID.2.16.840.1.101.3.4.2.3"), + m(CKM_SHA512)); + + d(MAC, "HmacMD5", P11MAC, + m(CKM_MD5_HMAC)); + d(MAC, "HmacSHA1", P11MAC, + s("1.2.840.113549.2.7", "OID.1.2.840.113549.2.7"), + m(CKM_SHA_1_HMAC)); + d(MAC, "HmacSHA224", P11MAC, + s("1.2.840.113549.2.8", "OID.1.2.840.113549.2.8"), + m(CKM_SHA224_HMAC)); + d(MAC, "HmacSHA256", P11MAC, + s("1.2.840.113549.2.9", "OID.1.2.840.113549.2.9"), + m(CKM_SHA256_HMAC)); + d(MAC, "HmacSHA384", P11MAC, + s("1.2.840.113549.2.10", "OID.1.2.840.113549.2.10"), + m(CKM_SHA384_HMAC)); + d(MAC, "HmacSHA512", P11MAC, + s("1.2.840.113549.2.11", "OID.1.2.840.113549.2.11"), + m(CKM_SHA512_HMAC)); + d(MAC, "SslMacMD5", P11MAC, + m(CKM_SSL3_MD5_MAC)); + d(MAC, "SslMacSHA1", P11MAC, + m(CKM_SSL3_SHA1_MAC)); + + d(KPG, "RSA", P11KeyPairGenerator, + m(CKM_RSA_PKCS_KEY_PAIR_GEN)); + d(KPG, "DSA", P11KeyPairGenerator, + s("1.3.14.3.2.12", "1.2.840.10040.4.1", "OID.1.2.840.10040.4.1"), + m(CKM_DSA_KEY_PAIR_GEN)); + d(KPG, "DH", P11KeyPairGenerator, s("DiffieHellman"), + m(CKM_DH_PKCS_KEY_PAIR_GEN)); + d(KPG, "EC", P11KeyPairGenerator, + m(CKM_EC_KEY_PAIR_GEN)); + + d(KG, "ARCFOUR", P11KeyGenerator, s("RC4"), + m(CKM_RC4_KEY_GEN)); + d(KG, "DES", P11KeyGenerator, + m(CKM_DES_KEY_GEN)); + d(KG, "DESede", P11KeyGenerator, + m(CKM_DES3_KEY_GEN, CKM_DES2_KEY_GEN)); + d(KG, "AES", P11KeyGenerator, + m(CKM_AES_KEY_GEN)); + d(KG, "Blowfish", P11KeyGenerator, + m(CKM_BLOWFISH_KEY_GEN)); + + // register (Secret)KeyFactories if there are any mechanisms + // for a particular algorithm that we support + d(KF, "RSA", P11RSAKeyFactory, + m(CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(KF, "DSA", P11DSAKeyFactory, + s("1.3.14.3.2.12", "1.2.840.10040.4.1", "OID.1.2.840.10040.4.1"), + m(CKM_DSA_KEY_PAIR_GEN, CKM_DSA, CKM_DSA_SHA1)); + d(KF, "DH", P11DHKeyFactory, s("DiffieHellman"), + m(CKM_DH_PKCS_KEY_PAIR_GEN, CKM_DH_PKCS_DERIVE)); + d(KF, "EC", P11DHKeyFactory, + m(CKM_EC_KEY_PAIR_GEN, CKM_ECDH1_DERIVE, + CKM_ECDSA, CKM_ECDSA_SHA1)); + + // AlgorithmParameters for EC. + // Only needed until we have an EC implementation in the SUN provider. + d(AGP, "EC", "sun.security.ec.ECParameters", + s("1.2.840.10045.2.1"), + m(CKM_EC_KEY_PAIR_GEN, CKM_ECDH1_DERIVE, + CKM_ECDSA, CKM_ECDSA_SHA1)); + + d(KA, "DH", P11KeyAgreement, s("DiffieHellman"), + m(CKM_DH_PKCS_DERIVE)); + d(KA, "ECDH", "sun.security.pkcs11.P11ECDHKeyAgreement", + m(CKM_ECDH1_DERIVE)); + + d(SKF, "ARCFOUR", P11SecretKeyFactory, s("RC4"), + m(CKM_RC4)); + d(SKF, "DES", P11SecretKeyFactory, + m(CKM_DES_CBC)); + d(SKF, "DESede", P11SecretKeyFactory, + m(CKM_DES3_CBC)); + d(SKF, "AES", P11SecretKeyFactory, + s("2.16.840.1.101.3.4.1", "OID.2.16.840.1.101.3.4.1"), + m(CKM_AES_CBC)); + d(SKF, "Blowfish", P11SecretKeyFactory, + m(CKM_BLOWFISH_CBC)); + + // XXX attributes for Ciphers (supported modes, padding) + d(CIP, "ARCFOUR", P11Cipher, s("RC4"), + m(CKM_RC4)); + d(CIP, "DES/CBC/NoPadding", P11Cipher, + m(CKM_DES_CBC)); + d(CIP, "DES/CBC/PKCS5Padding", P11Cipher, + m(CKM_DES_CBC_PAD, CKM_DES_CBC)); + d(CIP, "DES/ECB/NoPadding", P11Cipher, + m(CKM_DES_ECB)); + d(CIP, "DES/ECB/PKCS5Padding", P11Cipher, s("DES"), + m(CKM_DES_ECB)); + + d(CIP, "DESede/CBC/NoPadding", P11Cipher, + m(CKM_DES3_CBC)); + d(CIP, "DESede/CBC/PKCS5Padding", P11Cipher, + m(CKM_DES3_CBC_PAD, CKM_DES3_CBC)); + d(CIP, "DESede/ECB/NoPadding", P11Cipher, + m(CKM_DES3_ECB)); + d(CIP, "DESede/ECB/PKCS5Padding", P11Cipher, s("DESede"), + m(CKM_DES3_ECB)); + d(CIP, "AES/CBC/NoPadding", P11Cipher, + m(CKM_AES_CBC)); + d(CIP, "AES_128/CBC/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.2", "OID.2.16.840.1.101.3.4.1.2"), + m(CKM_AES_CBC)); + d(CIP, "AES_192/CBC/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.22", "OID.2.16.840.1.101.3.4.1.22"), + m(CKM_AES_CBC)); + d(CIP, "AES_256/CBC/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.42", "OID.2.16.840.1.101.3.4.1.42"), + m(CKM_AES_CBC)); + d(CIP, "AES/CBC/PKCS5Padding", P11Cipher, + m(CKM_AES_CBC_PAD, CKM_AES_CBC)); + d(CIP, "AES/ECB/NoPadding", P11Cipher, + m(CKM_AES_ECB)); + d(CIP, "AES_128/ECB/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.1", "OID.2.16.840.1.101.3.4.1.1"), + m(CKM_AES_ECB)); + d(CIP, "AES_192/ECB/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.21", "OID.2.16.840.1.101.3.4.1.21"), + m(CKM_AES_ECB)); + d(CIP, "AES_256/ECB/NoPadding", P11Cipher, + s("2.16.840.1.101.3.4.1.41", "OID.2.16.840.1.101.3.4.1.41"), + m(CKM_AES_ECB)); + d(CIP, "AES/ECB/PKCS5Padding", P11Cipher, s("AES"), + m(CKM_AES_ECB)); + d(CIP, "AES/CTR/NoPadding", P11Cipher, + m(CKM_AES_CTR)); + d(CIP, "Blowfish/CBC/NoPadding", P11Cipher, + m(CKM_BLOWFISH_CBC)); + d(CIP, "Blowfish/CBC/PKCS5Padding", P11Cipher, + m(CKM_BLOWFISH_CBC)); + + // XXX RSA_X_509, RSA_OAEP not yet supported + d(CIP, "RSA/ECB/PKCS1Padding", P11RSACipher, s("RSA"), + m(CKM_RSA_PKCS)); + d(CIP, "RSA/ECB/NoPadding", P11RSACipher, + m(CKM_RSA_X_509)); + + d(SIG, "RawDSA", P11Signature, s("NONEwithDSA"), + m(CKM_DSA)); + d(SIG, "DSA", P11Signature, + s("SHA1withDSA", "1.3.14.3.2.13", "1.3.14.3.2.27", + "1.2.840.10040.4.3", "OID.1.2.840.10040.4.3"), + m(CKM_DSA_SHA1, CKM_DSA)); + d(SIG, "NONEwithECDSA", P11Signature, + m(CKM_ECDSA)); + d(SIG, "SHA1withECDSA", P11Signature, + s("ECDSA", "1.2.840.10045.4.1", "OID.1.2.840.10045.4.1"), + m(CKM_ECDSA_SHA1, CKM_ECDSA)); + d(SIG, "SHA224withECDSA", P11Signature, + s("1.2.840.10045.4.3.1", "OID.1.2.840.10045.4.3.1"), + m(CKM_ECDSA)); + d(SIG, "SHA256withECDSA", P11Signature, + s("1.2.840.10045.4.3.2", "OID.1.2.840.10045.4.3.2"), + m(CKM_ECDSA)); + d(SIG, "SHA384withECDSA", P11Signature, + s("1.2.840.10045.4.3.3", "OID.1.2.840.10045.4.3.3"), + m(CKM_ECDSA)); + d(SIG, "SHA512withECDSA", P11Signature, + s("1.2.840.10045.4.3.4", "OID.1.2.840.10045.4.3.4"), + m(CKM_ECDSA)); + d(SIG, "MD2withRSA", P11Signature, + s("1.2.840.113549.1.1.2", "OID.1.2.840.113549.1.1.2"), + m(CKM_MD2_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "MD5withRSA", P11Signature, + s("1.2.840.113549.1.1.4", "OID.1.2.840.113549.1.1.4"), + m(CKM_MD5_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "SHA1withRSA", P11Signature, + s("1.2.840.113549.1.1.5", "OID.1.2.840.113549.1.1.5", + "1.3.14.3.2.29"), + m(CKM_SHA1_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "SHA224withRSA", P11Signature, + s("1.2.840.113549.1.1.14", "OID.1.2.840.113549.1.1.14"), + m(CKM_SHA224_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "SHA256withRSA", P11Signature, + s("1.2.840.113549.1.1.11", "OID.1.2.840.113549.1.1.11"), + m(CKM_SHA256_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "SHA384withRSA", P11Signature, + s("1.2.840.113549.1.1.12", "OID.1.2.840.113549.1.1.12"), + m(CKM_SHA384_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + d(SIG, "SHA512withRSA", P11Signature, + s("1.2.840.113549.1.1.13", "OID.1.2.840.113549.1.1.13"), + m(CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + + /* + * TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the + * PRF calculations. As of 2010, there is no PKCS11-level + * support for TLS 1.2 PRF calculations, and no known OS's have + * an internal variant we could use. Therefore for TLS 1.2, we + * are updating JSSE to request different provider algorithms + * (e.g. "SunTls12Prf"), and currently only SunJCE has these + * TLS 1.2 algorithms. + * + * If we reused the names such as "SunTlsPrf", the PKCS11 + * providers would need be updated to fail correctly when + * presented with the wrong version number (via + * Provider.Service.supportsParameters()), and we would also + * need to add the appropriate supportsParamters() checks into + * KeyGenerators (not currently there). + * + * In the future, if PKCS11 support is added, we will restructure + * this. + */ + d(KG, "SunTlsRsaPremasterSecret", + "sun.security.pkcs11.P11TlsRsaPremasterSecretGenerator", + m(CKM_SSL3_PRE_MASTER_KEY_GEN, CKM_TLS_PRE_MASTER_KEY_GEN)); + d(KG, "SunTlsMasterSecret", + "sun.security.pkcs11.P11TlsMasterSecretGenerator", + m(CKM_SSL3_MASTER_KEY_DERIVE, CKM_TLS_MASTER_KEY_DERIVE, + CKM_SSL3_MASTER_KEY_DERIVE_DH, + CKM_TLS_MASTER_KEY_DERIVE_DH)); + d(KG, "SunTlsKeyMaterial", + "sun.security.pkcs11.P11TlsKeyMaterialGenerator", + m(CKM_SSL3_KEY_AND_MAC_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE)); + d(KG, "SunTlsPrf", "sun.security.pkcs11.P11TlsPrfGenerator", + m(CKM_TLS_PRF, CKM_NSS_TLS_PRF_GENERAL)); + } + + // background thread that periodically checks for token insertion + // if no token is present. We need to do that in a separate thread because + // the insertion check may block for quite a long time on some tokens. + private static class TokenPoller implements Runnable { + private final SunPKCS11 provider; + private volatile boolean enabled; + private TokenPoller(SunPKCS11 provider) { + this.provider = provider; + enabled = true; + } + public void run() { + int interval = provider.config.getInsertionCheckInterval(); + while (enabled) { + try { + Thread.sleep(interval); + } catch (InterruptedException e) { + break; + } + if (enabled == false) { + break; + } + try { + provider.initToken(null); + } catch (PKCS11Exception e) { + // ignore + } + } + } + void disable() { + enabled = false; + } + } + + // create the poller thread, if not already active + private void createPoller() { + if (poller != null) { + return; + } + TokenPoller poller = new TokenPoller(this); + Thread t = new Thread(poller, "Poller " + getName()); + t.setDaemon(true); + t.setPriority(Thread.MIN_PRIORITY); + t.start(); + this.poller = poller; + } + + // destroy the poller thread, if active + private void destroyPoller() { + if (poller != null) { + poller.disable(); + poller = null; + } + } + + private boolean hasValidToken() { + /* Commented out to work with Solaris softtoken impl which + returns 0-value flags, e.g. both REMOVABLE_DEVICE and + TOKEN_PRESENT are false, when it can't access the token. + if (removable == false) { + return true; + } + */ + Token token = this.token; + return (token != null) && token.isValid(); + } + + // destroy the token. Called if we detect that it has been removed + synchronized void uninitToken(Token token) { + if (this.token != token) { + // mismatch, our token must already be destroyed + return; + } + destroyPoller(); + this.token = null; + // unregister all algorithms + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + clear(); + return null; + } + }); + createPoller(); + } + + // test if a token is present and initialize this provider for it if so. + // does nothing if no token is found + // called from constructor and by poller + private void initToken(CK_SLOT_INFO slotInfo) throws PKCS11Exception { + if (slotInfo == null) { + slotInfo = p11.C_GetSlotInfo(slotID); + } + if (removable && (slotInfo.flags & CKF_TOKEN_PRESENT) == 0) { + createPoller(); + return; + } + destroyPoller(); + boolean showInfo = config.getShowInfo(); + if (showInfo) { + System.out.println("Slot info for slot " + slotID + ":"); + System.out.println(slotInfo); + } + final Token token = new Token(this); + if (showInfo) { + System.out.println + ("Token info for token in slot " + slotID + ":"); + System.out.println(token.tokenInfo); + } + long[] supportedMechanisms = p11.C_GetMechanismList(slotID); + + // Create a map from the various Descriptors to the "most + // preferred" mechanism that was defined during the + // static initialization. For example, DES/CBC/PKCS5Padding + // could be mapped to CKM_DES_CBC_PAD or CKM_DES_CBC. Prefer + // the earliest entry. When asked for "DES/CBC/PKCS5Padding", we + // return a CKM_DES_CBC_PAD. + final Map supportedAlgs = + new HashMap(); + for (int i = 0; i < supportedMechanisms.length; i++) { + long longMech = supportedMechanisms[i]; + boolean isEnabled = config.isEnabled(longMech); + if (showInfo) { + CK_MECHANISM_INFO mechInfo = + p11.C_GetMechanismInfo(slotID, longMech); + System.out.println("Mechanism " + + Functions.getMechanismName(longMech) + ":"); + if (isEnabled == false) { + System.out.println("DISABLED in configuration"); + } + System.out.println(mechInfo); + } + if (isEnabled == false) { + continue; + } + // we do not know of mechs with the upper 32 bits set + if (longMech >>> 32 != 0) { + continue; + } + int mech = (int)longMech; + Integer integerMech = Integer.valueOf(mech); + List ds = descriptors.get(integerMech); + if (ds == null) { + continue; + } + for (Descriptor d : ds) { + Integer oldMech = supportedAlgs.get(d); + if (oldMech == null) { + supportedAlgs.put(d, integerMech); + continue; + } + // See if there is something "more preferred" + // than what we currently have in the supportedAlgs + // map. + int intOldMech = oldMech.intValue(); + for (int j = 0; j < d.mechanisms.length; j++) { + int nextMech = d.mechanisms[j]; + if (mech == nextMech) { + supportedAlgs.put(d, integerMech); + break; + } else if (intOldMech == nextMech) { + break; + } + } + } + + } + + // register algorithms in provider + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + for (Map.Entry entry + : supportedAlgs.entrySet()) { + Descriptor d = entry.getKey(); + int mechanism = entry.getValue().intValue(); + Service s = d.service(token, mechanism); + putService(s); + } + if (((token.tokenInfo.flags & CKF_RNG) != 0) + && config.isEnabled(PCKM_SECURERANDOM) + && !token.sessionManager.lowMaxSessions()) { + // do not register SecureRandom if the token does + // not support many sessions. if we did, we might + // run out of sessions in the middle of a + // nextBytes() call where we cannot fail over. + putService(new P11Service(token, SR, "PKCS11", + "sun.security.pkcs11.P11SecureRandom", null, + PCKM_SECURERANDOM)); + } + if (config.isEnabled(PCKM_KEYSTORE)) { + putService(new P11Service(token, KS, "PKCS11", + "sun.security.pkcs11.P11KeyStore", + s("PKCS11-" + config.getName()), + PCKM_KEYSTORE)); + } + return null; + } + }); + + this.token = token; + } + + private static final class P11Service extends Service { + + private final Token token; + + private final long mechanism; + + P11Service(Token token, String type, String algorithm, + String className, String[] al, long mechanism) { + super(token.provider, type, algorithm, className, toList(al), null); + this.token = token; + this.mechanism = mechanism & 0xFFFFFFFFL; + } + + private static List toList(String[] aliases) { + return (aliases == null) ? null : Arrays.asList(aliases); + } + + public Object newInstance(Object param) + throws NoSuchAlgorithmException { + if (token.isValid() == false) { + throw new NoSuchAlgorithmException("Token has been removed"); + } + try { + return newInstance0(param); + } catch (PKCS11Exception e) { + throw new NoSuchAlgorithmException(e); + } + } + + public Object newInstance0(Object param) throws + PKCS11Exception, NoSuchAlgorithmException { + String algorithm = getAlgorithm(); + String type = getType(); + if (type == MD) { + return new P11Digest(token, algorithm, mechanism); + } else if (type == CIP) { + if (algorithm.startsWith("RSA")) { + return new P11RSACipher(token, algorithm, mechanism); + } else { + return new P11Cipher(token, algorithm, mechanism); + } + } else if (type == SIG) { + return new P11Signature(token, algorithm, mechanism); + } else if (type == MAC) { + return new P11Mac(token, algorithm, mechanism); + } else if (type == KPG) { + return new P11KeyPairGenerator(token, algorithm, mechanism); + } else if (type == KA) { + if (algorithm.equals("ECDH")) { + return new P11ECDHKeyAgreement(token, algorithm, mechanism); + } else { + return new P11KeyAgreement(token, algorithm, mechanism); + } + } else if (type == KF) { + return token.getKeyFactory(algorithm); + } else if (type == SKF) { + return new P11SecretKeyFactory(token, algorithm); + } else if (type == KG) { + // reference equality + if (algorithm == "SunTlsRsaPremasterSecret") { + return new P11TlsRsaPremasterSecretGenerator( + token, algorithm, mechanism); + } else if (algorithm == "SunTlsMasterSecret") { + return new P11TlsMasterSecretGenerator( + token, algorithm, mechanism); + } else if (algorithm == "SunTlsKeyMaterial") { + return new P11TlsKeyMaterialGenerator( + token, algorithm, mechanism); + } else if (algorithm == "SunTlsPrf") { + return new P11TlsPrfGenerator(token, algorithm, mechanism); + } else { + return new P11KeyGenerator(token, algorithm, mechanism); + } + } else if (type == SR) { + return token.getRandom(); + } else if (type == KS) { + return token.getKeyStore(); + } else if (type == AGP) { + return new sun.security.ec.ECParameters(); + } else { + throw new NoSuchAlgorithmException("Unknown type: " + type); + } + } + + public boolean supportsParameter(Object param) { + if ((param == null) || (token.isValid() == false)) { + return false; + } + if (param instanceof Key == false) { + throw new InvalidParameterException("Parameter must be a Key"); + } + String algorithm = getAlgorithm(); + String type = getType(); + Key key = (Key)param; + String keyAlgorithm = key.getAlgorithm(); + // RSA signatures and cipher + if (((type == CIP) && algorithm.startsWith("RSA")) + || (type == SIG) && algorithm.endsWith("RSA")) { + if (keyAlgorithm.equals("RSA") == false) { + return false; + } + return isLocalKey(key) + || (key instanceof RSAPrivateKey) + || (key instanceof RSAPublicKey); + } + // EC + if (((type == KA) && algorithm.equals("ECDH")) + || ((type == SIG) && algorithm.endsWith("ECDSA"))) { + if (keyAlgorithm.equals("EC") == false) { + return false; + } + return isLocalKey(key) + || (key instanceof ECPrivateKey) + || (key instanceof ECPublicKey); + } + // DSA signatures + if ((type == SIG) && algorithm.endsWith("DSA")) { + if (keyAlgorithm.equals("DSA") == false) { + return false; + } + return isLocalKey(key) + || (key instanceof DSAPrivateKey) + || (key instanceof DSAPublicKey); + } + // MACs and symmetric ciphers + if ((type == CIP) || (type == MAC)) { + // do not check algorithm name, mismatch is unlikely anyway + return isLocalKey(key) || "RAW".equals(key.getFormat()); + } + // DH key agreement + if (type == KA) { + if (keyAlgorithm.equals("DH") == false) { + return false; + } + return isLocalKey(key) + || (key instanceof DHPrivateKey) + || (key instanceof DHPublicKey); + } + // should not reach here, + // unknown engine type or algorithm + throw new AssertionError + ("SunPKCS11 error: " + type + ", " + algorithm); + } + + private boolean isLocalKey(Key key) { + return (key instanceof P11Key) && (((P11Key)key).token == token); + } + + public String toString() { + return super.toString() + + " (" + Functions.getMechanismName(mechanism) + ")"; + } + + } + + /** + * Log in to this provider. + * + *

    If the token expects a PIN to be supplied by the caller, + * the handler implementation must support + * a PasswordCallback. + * + *

    To determine if the token supports a protected authentication path, + * the CK_TOKEN_INFO flag, CKF_PROTECTED_AUTHENTICATION_PATH, is consulted. + * + * @param subject this parameter is ignored + * @param handler the CallbackHandler used by + * this provider to communicate with the caller + * + * @exception LoginException if the login operation fails + * @exception SecurityException if the does not pass a security check for + * SecurityPermission("authProvider.name"), + * where name is the value returned by + * this provider's getName method + */ + public void login(Subject subject, CallbackHandler handler) + throws LoginException { + + // security check + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (debug != null) { + debug.println("checking login permission"); + } + sm.checkPermission(new SecurityPermission + ("authProvider." + this.getName())); + } + + if (hasValidToken() == false) { + throw new LoginException("No token present"); + } + + // see if a login is required + + if ((token.tokenInfo.flags & CKF_LOGIN_REQUIRED) == 0) { + if (debug != null) { + debug.println("login operation not required for token - " + + "ignoring login request"); + } + return; + } + + // see if user already logged in + + try { + if (token.isLoggedInNow(null)) { + // user already logged in + if (debug != null) { + debug.println("user already logged in"); + } + return; + } + } catch (PKCS11Exception e) { + // ignore - fall thru and attempt login + } + + // get the pin if necessary + + char[] pin = null; + if ((token.tokenInfo.flags & CKF_PROTECTED_AUTHENTICATION_PATH) == 0) { + + // get password + + CallbackHandler myHandler = getCallbackHandler(handler); + if (myHandler == null) { + // XXX PolicyTool is dependent on this message text + throw new LoginException + ("no password provided, and no callback handler " + + "available for retrieving password"); + } + + java.text.MessageFormat form = new java.text.MessageFormat + (ResourcesMgr.getString + ("PKCS11.Token.providerName.Password.")); + Object[] source = { getName() }; + + PasswordCallback pcall = new PasswordCallback(form.format(source), + false); + Callback[] callbacks = { pcall }; + try { + myHandler.handle(callbacks); + } catch (Exception e) { + LoginException le = new LoginException + ("Unable to perform password callback"); + le.initCause(e); + throw le; + } + + pin = pcall.getPassword(); + pcall.clearPassword(); + if (pin == null) { + if (debug != null) { + debug.println("caller passed NULL pin"); + } + } + } + + // perform token login + + Session session = null; + try { + session = token.getOpSession(); + + // pin is NULL if using CKF_PROTECTED_AUTHENTICATION_PATH + p11.C_Login(session.id(), CKU_USER, pin); + if (debug != null) { + debug.println("login succeeded"); + } + } catch (PKCS11Exception pe) { + if (pe.getErrorCode() == CKR_USER_ALREADY_LOGGED_IN) { + // let this one go + if (debug != null) { + debug.println("user already logged in"); + } + return; + } else if (pe.getErrorCode() == CKR_PIN_INCORRECT) { + FailedLoginException fle = new FailedLoginException(); + fle.initCause(pe); + throw fle; + } else { + LoginException le = new LoginException(); + le.initCause(pe); + throw le; + } + } finally { + token.releaseSession(session); + if (pin != null) { + Arrays.fill(pin, ' '); + } + } + + // we do not store the PIN in the subject for now + } + + /** + * Log out from this provider + * + * @exception LoginException if the logout operation fails + * @exception SecurityException if the does not pass a security check for + * SecurityPermission("authProvider.name"), + * where name is the value returned by + * this provider's getName method + */ + public void logout() throws LoginException { + + // security check + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission + (new SecurityPermission("authProvider." + this.getName())); + } + + if (hasValidToken() == false) { + // app may call logout for cleanup, allow + return; + } + + if ((token.tokenInfo.flags & CKF_LOGIN_REQUIRED) == 0) { + if (debug != null) { + debug.println("logout operation not required for token - " + + "ignoring logout request"); + } + return; + } + + try { + if (token.isLoggedInNow(null) == false) { + if (debug != null) { + debug.println("user not logged in"); + } + return; + } + } catch (PKCS11Exception e) { + // ignore + } + + // perform token logout + + Session session = null; + try { + session = token.getOpSession(); + p11.C_Logout(session.id()); + if (debug != null) { + debug.println("logout succeeded"); + } + } catch (PKCS11Exception pe) { + if (pe.getErrorCode() == CKR_USER_NOT_LOGGED_IN) { + // let this one go + if (debug != null) { + debug.println("user not logged in"); + } + return; + } + LoginException le = new LoginException(); + le.initCause(pe); + throw le; + } finally { + token.releaseSession(session); + } + } + + /** + * Set a CallbackHandler + * + *

    The provider uses this handler if one is not passed to the + * login method. The provider also uses this handler + * if it invokes login on behalf of callers. + * In either case if a handler is not set via this method, + * the provider queries the + * auth.login.defaultCallbackHandler security property + * for the fully qualified class name of a default handler implementation. + * If the security property is not set, + * the provider is assumed to have alternative means + * for obtaining authentication information. + * + * @param handler a CallbackHandler for obtaining + * authentication information, which may be null + * + * @exception SecurityException if the caller does not pass a + * security check for + * SecurityPermission("authProvider.name"), + * where name is the value returned by + * this provider's getName method + */ + public void setCallbackHandler(CallbackHandler handler) { + + // security check + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission + (new SecurityPermission("authProvider." + this.getName())); + } + + synchronized (LOCK_HANDLER) { + pHandler = handler; + } + } + + private CallbackHandler getCallbackHandler(CallbackHandler handler) { + + // get default handler if necessary + + if (handler != null) { + return handler; + } + + if (debug != null) { + debug.println("getting provider callback handler"); + } + + synchronized (LOCK_HANDLER) { + // see if handler was set via setCallbackHandler + if (pHandler != null) { + return pHandler; + } + + try { + if (debug != null) { + debug.println("getting default callback handler"); + } + + CallbackHandler myHandler = AccessController.doPrivileged + (new PrivilegedExceptionAction() { + public CallbackHandler run() throws Exception { + + String defaultHandler = + Security.getProperty + ("auth.login.defaultCallbackHandler"); + + if (defaultHandler == null || + defaultHandler.length() == 0) { + + // ok + if (debug != null) { + debug.println("no default handler set"); + } + return null; + } + + Class c = Class.forName + (defaultHandler, + true, + Thread.currentThread().getContextClassLoader()); + return (CallbackHandler)c.newInstance(); + } + }); + + // save it + pHandler = myHandler; + return myHandler; + + } catch (PrivilegedActionException pae) { + // ok + if (debug != null) { + debug.println("Unable to load default callback handler"); + pae.printStackTrace(); + } + } + } + return null; + } + + private Object writeReplace() throws ObjectStreamException { + return new SunPKCS11Rep(this); + } + + /** + * Serialized representation of the SunPKCS11 provider. + */ + private static class SunPKCS11Rep implements Serializable { + + static final long serialVersionUID = -2896606995897745419L; + + private final String providerName; + + private final String configName; + + SunPKCS11Rep(SunPKCS11 provider) throws NotSerializableException { + providerName = provider.getName(); + configName = provider.configName; + if (Security.getProvider(providerName) != provider) { + throw new NotSerializableException("Only SunPKCS11 providers " + + "installed in java.security.Security can be serialized"); + } + } + + private Object readResolve() throws ObjectStreamException { + SunPKCS11 p = (SunPKCS11)Security.getProvider(providerName); + if ((p == null) || (p.configName.equals(configName) == false)) { + throw new NotSerializableException("Could not find " + + providerName + " in installed providers"); + } + return p; + } + } +} diff --git a/src/sun/security/pkcs11/TemplateManager.java b/src/sun/security/pkcs11/TemplateManager.java new file mode 100644 index 00000000..12dacd35 --- /dev/null +++ b/src/sun/security/pkcs11/TemplateManager.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.util.*; +import java.util.concurrent.*; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * TemplateManager class. + * + * Not all PKCS#11 tokens are created equal. One token may require that one + * value is specified when creating a certain type of object. Another token + * may require a different value. Yet another token may only work if the + * attribute is not specified at all. + * + * In order to allow an application to work unmodified with all those + * different tokens, the SunPKCS11 provider makes the attributes that are + * specified and their value configurable. Hence, only the SunPKCS11 + * configuration file has to be tweaked at deployment time to allow all + * existing applications to be used. + * + * The template manager is responsible for reading the attribute configuration + * information and to make it available to the various internal components + * of the SunPKCS11 provider. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class TemplateManager { + + private final static boolean DEBUG = false; + + // constant for any operation (either O_IMPORT or O_GENERATE) + final static String O_ANY = "*"; + // constant for operation create ("importing" existing key material) + final static String O_IMPORT = "import"; + // constant for operation generate (generating new key material) + final static String O_GENERATE = "generate"; + + private static class KeyAndTemplate { + final TemplateKey key; + final Template template; + + KeyAndTemplate(TemplateKey key, Template template) { + this.key = key; + this.template = template; + } + } + + // primitive templates contains the individual template configuration + // entries from the configuration file + private final List primitiveTemplates; + + // composite templates is a cache of the exact configuration template for + // each specific TemplateKey (no wildcards). the entries are created + // on demand during first use by compositing all applicable + // primitive template entries. the result is then stored in this map + // for performance + private final Map compositeTemplates; + + TemplateManager() { + primitiveTemplates = new ArrayList(); + compositeTemplates = new ConcurrentHashMap(); + } + + // add a template. Called by Config. + void addTemplate(String op, long objectClass, long keyAlgorithm, + CK_ATTRIBUTE[] attrs) { + TemplateKey key = new TemplateKey(op, objectClass, keyAlgorithm); + Template template = new Template(attrs); + if (DEBUG) { + System.out.println("Adding " + key + " -> " + template); + } + primitiveTemplates.add(new KeyAndTemplate(key, template)); + } + + private Template getTemplate(TemplateKey key) { + Template template = compositeTemplates.get(key); + if (template == null) { + template = buildCompositeTemplate(key); + compositeTemplates.put(key, template); + } + return template; + } + + // Get the attributes for the requested op and combine them with attrs. + // This is the method called by the implementation to obtain the + // attributes. + CK_ATTRIBUTE[] getAttributes(String op, long type, long alg, + CK_ATTRIBUTE[] attrs) { + TemplateKey key = new TemplateKey(op, type, alg); + Template template = getTemplate(key); + CK_ATTRIBUTE[] newAttrs = template.getAttributes(attrs); + if (DEBUG) { + System.out.println(key + " -> " + Arrays.asList(newAttrs)); + } + return newAttrs; + } + + // build a composite template for the given key + private Template buildCompositeTemplate(TemplateKey key) { + Template comp = new Template(); + // iterate through primitive templates and add all that apply + for (KeyAndTemplate entry : primitiveTemplates) { + if (entry.key.appliesTo(key)) { + comp.add(entry.template); + } + } + return comp; + } + + /** + * Nested class representing a template identifier. + */ + private static final class TemplateKey { + final String operation; + final long keyType; + final long keyAlgorithm; + TemplateKey(String operation, long keyType, long keyAlgorithm) { + this.operation = operation; + this.keyType = keyType; + this.keyAlgorithm = keyAlgorithm; + } + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof TemplateKey == false) { + return false; + } + TemplateKey other = (TemplateKey)obj; + boolean match = this.operation.equals(other.operation) + && (this.keyType == other.keyType) + && (this.keyAlgorithm == other.keyAlgorithm); + return match; + } + public int hashCode() { + return operation.hashCode() + (int)keyType + (int)keyAlgorithm; + } + boolean appliesTo(TemplateKey key) { + if (operation.equals(O_ANY) || operation.equals(key.operation)) { + if ((keyType == PCKO_ANY) || (keyType == key.keyType)) { + if ((keyAlgorithm == PCKK_ANY) + || (keyAlgorithm == key.keyAlgorithm)) { + return true; + } + } + } + return false; + } + public String toString() { + return "(" + operation + "," + + Functions.getObjectClassName(keyType) + + "," + Functions.getKeyName(keyAlgorithm) + ")"; + } + } + + /** + * Nested class representing template attributes. + */ + private static final class Template { + + private final static CK_ATTRIBUTE[] A0 = new CK_ATTRIBUTE[0]; + + private CK_ATTRIBUTE[] attributes; + + Template() { + attributes = A0; + } + + Template(CK_ATTRIBUTE[] attributes) { + this.attributes = attributes; + } + + void add(Template template) { + attributes = getAttributes(template.attributes); + } + + CK_ATTRIBUTE[] getAttributes(CK_ATTRIBUTE[] attrs) { + return combine(attributes, attrs); + } + + /** + * Combine two sets of attributes. The second set has precedence + * over the first and overrides its settings. + */ + private static CK_ATTRIBUTE[] combine(CK_ATTRIBUTE[] attrs1, + CK_ATTRIBUTE[] attrs2) { + List attrs = new ArrayList(); + for (CK_ATTRIBUTE attr : attrs1) { + if (attr.pValue != null) { + attrs.add(attr); + } + } + for (CK_ATTRIBUTE attr2 : attrs2) { + long type = attr2.type; + for (CK_ATTRIBUTE attr1 : attrs1) { + if (attr1.type == type) { + attrs.remove(attr1); + } + } + if (attr2.pValue != null) { + attrs.add(attr2); + } + } + return attrs.toArray(A0); + } + + public String toString() { + return Arrays.asList(attributes).toString(); + } + + } + +} diff --git a/src/sun/security/pkcs11/Token.java b/src/sun/security/pkcs11/Token.java new file mode 100644 index 00000000..39d301ae --- /dev/null +++ b/src/sun/security/pkcs11/Token.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.io.*; +import java.lang.ref.*; + +import java.security.*; +import javax.security.auth.login.LoginException; + +import sun.security.jca.JCAUtil; + +import sun.security.pkcs11.wrapper.*; +import static sun.security.pkcs11.TemplateManager.*; +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * PKCS#11 token. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +class Token implements Serializable { + + // need to be serializable to allow SecureRandom to be serialized + private static final long serialVersionUID = 2541527649100571747L; + + // how often to check if the token is still present (in ms) + // this is different from checking if a token has been inserted, + // that is done in SunPKCS11. Currently 50 ms. + private final static long CHECK_INTERVAL = 50; + + final SunPKCS11 provider; + + final PKCS11 p11; + + final Config config; + + final CK_TOKEN_INFO tokenInfo; + + // session manager to pool sessions + final SessionManager sessionManager; + + // template manager to customize the attributes used when creating objects + private final TemplateManager templateManager; + + // flag indicating whether we need to explicitly cancel operations + // we started on the token. If false, we assume operations are + // automatically cancelled once we start another one + final boolean explicitCancel; + + // translation cache for secret keys + final KeyCache secretCache; + + // translation cache for asymmetric keys (public and private) + final KeyCache privateCache; + + // cached instances of the various key factories, initialized on demand + private volatile P11KeyFactory rsaFactory, dsaFactory, dhFactory, ecFactory; + + // table which maps mechanisms to the corresponding cached + // MechanismInfo objects + private final Map mechInfoMap; + + // single SecureRandomSpi instance we use per token + // initialized on demand (if supported) + private volatile P11SecureRandom secureRandom; + + // single KeyStoreSpi instance we use per provider + // initialized on demand + private volatile P11KeyStore keyStore; + + // whether this token is a removable token + private final boolean removable; + + // for removable tokens: whether this token is valid or has been removed + private volatile boolean valid; + + // for removable tokens: time last checked for token presence + private long lastPresentCheck; + + // unique token id, used for serialization only + private byte[] tokenId; + + // flag indicating whether the token is write protected + private boolean writeProtected; + + // flag indicating whether we are logged in + private volatile boolean loggedIn; + + // time we last checked login status + private long lastLoginCheck; + + // mutex for token-present-check + private final static Object CHECK_LOCK = new Object(); + + // object for indicating unsupported mechanism in 'mechInfoMap' + private final static CK_MECHANISM_INFO INVALID_MECH = + new CK_MECHANISM_INFO(0, 0, 0); + + // flag indicating whether the token supports raw secret key material import + private Boolean supportsRawSecretKeyImport; + + Token(SunPKCS11 provider) throws PKCS11Exception { + this.provider = provider; + this.removable = provider.removable; + this.valid = true; + p11 = provider.p11; + config = provider.config; + tokenInfo = p11.C_GetTokenInfo(provider.slotID); + writeProtected = (tokenInfo.flags & CKF_WRITE_PROTECTED) != 0; + // create session manager and open a test session + SessionManager sessionManager; + try { + sessionManager = new SessionManager(this); + Session s = sessionManager.getOpSession(); + sessionManager.releaseSession(s); + } catch (PKCS11Exception e) { + if (writeProtected) { + throw e; + } + // token might not permit RW sessions even though + // CKF_WRITE_PROTECTED is not set + writeProtected = true; + sessionManager = new SessionManager(this); + Session s = sessionManager.getOpSession(); + sessionManager.releaseSession(s); + } + this.sessionManager = sessionManager; + secretCache = new KeyCache(); + privateCache = new KeyCache(); + templateManager = config.getTemplateManager(); + explicitCancel = config.getExplicitCancel(); + mechInfoMap = + new ConcurrentHashMap(10); + } + + boolean isWriteProtected() { + return writeProtected; + } + + // return whether the token supports raw secret key material import + boolean supportsRawSecretKeyImport() { + if (supportsRawSecretKeyImport == null) { + SecureRandom random = JCAUtil.getSecureRandom(); + byte[] encoded = new byte[48]; + random.nextBytes(encoded); + + CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[3]; + attributes[0] = new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY); + attributes[1] = new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET); + attributes[2] = new CK_ATTRIBUTE(CKA_VALUE, encoded); + + Session session = null; + try { + attributes = getAttributes(O_IMPORT, + CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes); + session = getObjSession(); + long keyID = p11.C_CreateObject(session.id(), attributes); + + supportsRawSecretKeyImport = Boolean.TRUE; + } catch (PKCS11Exception e) { + supportsRawSecretKeyImport = Boolean.FALSE; + } finally { + releaseSession(session); + } + } + + return supportsRawSecretKeyImport; + } + + // return whether we are logged in + // uses cached result if current. session is optional and may be null + boolean isLoggedIn(Session session) throws PKCS11Exception { + // volatile load first + boolean loggedIn = this.loggedIn; + long time = System.currentTimeMillis(); + if (time - lastLoginCheck > CHECK_INTERVAL) { + loggedIn = isLoggedInNow(session); + lastLoginCheck = time; + } + return loggedIn; + } + + // return whether we are logged in now + // does not use cache + boolean isLoggedInNow(Session session) throws PKCS11Exception { + boolean allocSession = (session == null); + try { + if (allocSession) { + session = getOpSession(); + } + CK_SESSION_INFO info = p11.C_GetSessionInfo(session.id()); + boolean loggedIn = (info.state == CKS_RO_USER_FUNCTIONS) || + (info.state == CKS_RW_USER_FUNCTIONS); + this.loggedIn = loggedIn; + return loggedIn; + } finally { + if (allocSession) { + releaseSession(session); + } + } + } + + // ensure that we are logged in + // call provider.login() if not + void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException { + if (isLoggedIn(session) == false) { + provider.login(null, null); + } + } + + // return whether this token object is valid (i.e. token not removed) + // returns value from last check, does not perform new check + boolean isValid() { + if (removable == false) { + return true; + } + return valid; + } + + void ensureValid() { + if (isValid() == false) { + throw new ProviderException("Token has been removed"); + } + } + + // return whether a token is present (i.e. token not removed) + // returns cached value if current, otherwise performs new check + boolean isPresent(long sessionID) { + if (removable == false) { + return true; + } + if (valid == false) { + return false; + } + long time = System.currentTimeMillis(); + if ((time - lastPresentCheck) >= CHECK_INTERVAL) { + synchronized (CHECK_LOCK) { + if ((time - lastPresentCheck) >= CHECK_INTERVAL) { + boolean ok = false; + try { + // check if token still present + CK_SLOT_INFO slotInfo = + provider.p11.C_GetSlotInfo(provider.slotID); + if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) { + // if the token has been removed and re-inserted, + // the token should return an error + CK_SESSION_INFO sessInfo = + provider.p11.C_GetSessionInfo + (sessionID); + ok = true; + } + } catch (PKCS11Exception e) { + // empty + } + valid = ok; + lastPresentCheck = System.currentTimeMillis(); + if (ok == false) { + destroy(); + } + } + } + } + return valid; + } + + void destroy() { + valid = false; + provider.uninitToken(this); + } + + Session getObjSession() throws PKCS11Exception { + return sessionManager.getObjSession(); + } + + Session getOpSession() throws PKCS11Exception { + return sessionManager.getOpSession(); + } + + Session releaseSession(Session session) { + return sessionManager.releaseSession(session); + } + + Session killSession(Session session) { + return sessionManager.killSession(session); + } + + CK_ATTRIBUTE[] getAttributes(String op, long type, long alg, + CK_ATTRIBUTE[] attrs) throws PKCS11Exception { + CK_ATTRIBUTE[] newAttrs = + templateManager.getAttributes(op, type, alg, attrs); + for (CK_ATTRIBUTE attr : newAttrs) { + if (attr.type == CKA_TOKEN) { + if (attr.getBoolean()) { + try { + ensureLoggedIn(null); + } catch (LoginException e) { + throw new ProviderException("Login failed", e); + } + } + // break once we have found a CKA_TOKEN attribute + break; + } + } + return newAttrs; + } + + P11KeyFactory getKeyFactory(String algorithm) { + P11KeyFactory f; + if (algorithm.equals("RSA")) { + f = rsaFactory; + if (f == null) { + f = new P11RSAKeyFactory(this, algorithm); + rsaFactory = f; + } + } else if (algorithm.equals("DSA")) { + f = dsaFactory; + if (f == null) { + f = new P11DSAKeyFactory(this, algorithm); + dsaFactory = f; + } + } else if (algorithm.equals("DH")) { + f = dhFactory; + if (f == null) { + f = new P11DHKeyFactory(this, algorithm); + dhFactory = f; + } + } else if (algorithm.equals("EC")) { + f = ecFactory; + if (f == null) { + f = new P11ECKeyFactory(this, algorithm); + ecFactory = f; + } + } else { + throw new ProviderException("Unknown algorithm " + algorithm); + } + return f; + } + + P11SecureRandom getRandom() { + if (secureRandom == null) { + secureRandom = new P11SecureRandom(this); + } + return secureRandom; + } + + P11KeyStore getKeyStore() { + if (keyStore == null) { + keyStore = new P11KeyStore(this); + } + return keyStore; + } + + CK_MECHANISM_INFO getMechanismInfo(long mechanism) throws PKCS11Exception { + CK_MECHANISM_INFO result = mechInfoMap.get(mechanism); + if (result == null) { + try { + result = p11.C_GetMechanismInfo(provider.slotID, + mechanism); + mechInfoMap.put(mechanism, result); + } catch (PKCS11Exception e) { + if (e.getErrorCode() != PKCS11Constants.CKR_MECHANISM_INVALID) { + throw e; + } else { + mechInfoMap.put(mechanism, INVALID_MECH); + } + } + } else if (result == INVALID_MECH) { + result = null; + } + return result; + } + + private synchronized byte[] getTokenId() { + if (tokenId == null) { + SecureRandom random = JCAUtil.getSecureRandom(); + tokenId = new byte[20]; + random.nextBytes(tokenId); + serializedTokens.add(new WeakReference(this)); + } + return tokenId; + } + + // list of all tokens that have been serialized within this VM + // NOTE that elements are never removed from this list + // the assumption is that the number of tokens that are serialized + // is relatively small + private static final List> serializedTokens = + new ArrayList>(); + + private Object writeReplace() throws ObjectStreamException { + if (isValid() == false) { + throw new NotSerializableException("Token has been removed"); + } + return new TokenRep(this); + } + + // serialized representation of a token + // tokens can only be de-serialized within the same VM invocation + // and if the token has not been removed in the meantime + private static class TokenRep implements Serializable { + + private static final long serialVersionUID = 3503721168218219807L; + + private final byte[] tokenId; + + TokenRep(Token token) { + tokenId = token.getTokenId(); + } + + private Object readResolve() throws ObjectStreamException { + for (Reference tokenRef : serializedTokens) { + Token token = tokenRef.get(); + if ((token != null) && token.isValid()) { + if (Arrays.equals(token.getTokenId(), tokenId)) { + return token; + } + } + } + throw new NotSerializableException("Could not find token"); + } + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java new file mode 100644 index 00000000..7208f481 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_AES_CTR_PARAMS.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11.wrapper; + +/** + * This class represents the necessary parameters required by + * the CKM_AES_CTR mechanism as defined in CK_AES_CTR_PARAMS structure.

    + * PKCS#11 structure: + *

    + * typedef struct CK_AES_CTR_PARAMS {
    + *   CK_ULONG ulCounterBits;
    + *   CK_BYTE cb[16];
    + * } CK_AES_CTR_PARAMS;
    + * 
    + * + * @author Yu-Ching Valerie Peng + * @since 1.7 + */ +public class CK_AES_CTR_PARAMS { + + private final long ulCounterBits; + private final byte cb[]; + + public CK_AES_CTR_PARAMS(byte[] cb) { + ulCounterBits = 128; + this.cb = cb.clone(); + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("ulCounterBits: "); + buffer.append(ulCounterBits); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("cb: "); + buffer.append(Functions.toHexString(cb)); + + return buffer.toString(); + } +} diff --git a/src/sun/security/pkcs11/wrapper/CK_ATTRIBUTE.java b/src/sun/security/pkcs11/wrapper/CK_ATTRIBUTE.java new file mode 100644 index 00000000..fb94dd5e --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_ATTRIBUTE.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +import java.math.BigInteger; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * class CK_ATTRIBUTE includes the type, value and length of an attribute.

    + * PKCS#11 structure: + *

    + * typedef struct CK_ATTRIBUTE {  
    + *   CK_ATTRIBUTE_TYPE type;  
    + *   CK_VOID_PTR pValue;  
    + *   CK_ULONG ulValueLen;
    + * } CK_ATTRIBUTE;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_ATTRIBUTE { + + // common attributes + // NOTE that CK_ATTRIBUTE is a mutable classes but these attributes + // *MUST NEVER* be modified, e.g. by using them in a + // C_GetAttributeValue() call! + + public final static CK_ATTRIBUTE TOKEN_FALSE = + new CK_ATTRIBUTE(CKA_TOKEN, false); + + public final static CK_ATTRIBUTE SENSITIVE_FALSE = + new CK_ATTRIBUTE(CKA_SENSITIVE, false); + + public final static CK_ATTRIBUTE EXTRACTABLE_TRUE = + new CK_ATTRIBUTE(CKA_EXTRACTABLE, true); + + public final static CK_ATTRIBUTE ENCRYPT_TRUE = + new CK_ATTRIBUTE(CKA_ENCRYPT, true); + + public final static CK_ATTRIBUTE DECRYPT_TRUE = + new CK_ATTRIBUTE(CKA_DECRYPT, true); + + public final static CK_ATTRIBUTE WRAP_TRUE = + new CK_ATTRIBUTE(CKA_WRAP, true); + + public final static CK_ATTRIBUTE UNWRAP_TRUE = + new CK_ATTRIBUTE(CKA_UNWRAP, true); + + public final static CK_ATTRIBUTE SIGN_TRUE = + new CK_ATTRIBUTE(CKA_SIGN, true); + + public final static CK_ATTRIBUTE VERIFY_TRUE = + new CK_ATTRIBUTE(CKA_VERIFY, true); + + public final static CK_ATTRIBUTE SIGN_RECOVER_TRUE = + new CK_ATTRIBUTE(CKA_SIGN_RECOVER, true); + + public final static CK_ATTRIBUTE VERIFY_RECOVER_TRUE = + new CK_ATTRIBUTE(CKA_VERIFY_RECOVER, true); + + public final static CK_ATTRIBUTE DERIVE_TRUE = + new CK_ATTRIBUTE(CKA_DERIVE, true); + + public final static CK_ATTRIBUTE ENCRYPT_NULL = + new CK_ATTRIBUTE(CKA_ENCRYPT); + + public final static CK_ATTRIBUTE DECRYPT_NULL = + new CK_ATTRIBUTE(CKA_DECRYPT); + + public final static CK_ATTRIBUTE WRAP_NULL = + new CK_ATTRIBUTE(CKA_WRAP); + + public final static CK_ATTRIBUTE UNWRAP_NULL = + new CK_ATTRIBUTE(CKA_UNWRAP); + + public CK_ATTRIBUTE() { + // empty + } + + public CK_ATTRIBUTE(long type) { + this.type = type; + } + + public CK_ATTRIBUTE(long type, Object pValue) { + this.type = type; + this.pValue = pValue; + } + + public CK_ATTRIBUTE(long type, boolean value) { + this.type = type; + this.pValue = Boolean.valueOf(value); + } + + public CK_ATTRIBUTE(long type, long value) { + this.type = type; + this.pValue = Long.valueOf(value); + } + + public CK_ATTRIBUTE(long type, BigInteger value) { + this.type = type; + this.pValue = sun.security.pkcs11.P11Util.getMagnitude(value); + } + + public BigInteger getBigInteger() { + if (pValue instanceof byte[] == false) { + throw new RuntimeException("Not a byte[]"); + } + return new BigInteger(1, (byte[])pValue); + } + + public boolean getBoolean() { + if (pValue instanceof Boolean == false) { + throw new RuntimeException + ("Not a Boolean: " + pValue.getClass().getName()); + } + return ((Boolean)pValue).booleanValue(); + } + + public char[] getCharArray() { + if (pValue instanceof char[] == false) { + throw new RuntimeException("Not a char[]"); + } + return (char[])pValue; + } + + public byte[] getByteArray() { + if (pValue instanceof byte[] == false) { + throw new RuntimeException("Not a byte[]"); + } + return (byte[])pValue; + } + + public long getLong() { + if (pValue instanceof Long == false) { + throw new RuntimeException + ("Not a Long: " + pValue.getClass().getName()); + } + return ((Long)pValue).longValue(); + } + + /** + * PKCS#11: + *
    +     *   CK_ATTRIBUTE_TYPE type;
    +     * 
    + */ + public long type; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pValue;
    +     *   CK_ULONG ulValueLen;
    +     * 
    + */ + public Object pValue; + + /** + * Returns the string representation of CK_ATTRIBUTE. + * + * @return the string representation of CK_ATTRIBUTE + */ + public String toString() { + String prefix = Functions.getAttributeName(type) + " = "; + if (type == CKA_CLASS) { + return prefix + Functions.getObjectClassName(getLong()); + } else if (type == CKA_KEY_TYPE) { + return prefix + Functions.getKeyName(getLong()); + } else { + String s; + if (pValue instanceof char[]) { + s = new String((char[])pValue); + } else if (pValue instanceof byte[]) { + s = Functions.toHexString((byte[])pValue); + } else { + s = String.valueOf(pValue); + } + return prefix + s; + } + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_CREATEMUTEX.java b/src/sun/security/pkcs11/wrapper/CK_CREATEMUTEX.java new file mode 100644 index 00000000..29dd0951 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_CREATEMUTEX.java @@ -0,0 +1,69 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * interface CK_CREATEMUTEX.

    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public interface CK_CREATEMUTEX { + + /** + * Method CK_CREATEMUTEX + * + * @param ppMutex + * @return The mutex (lock) object. + * @exception PKCS11Exception + */ + public Object CK_CREATEMUTEX() throws PKCS11Exception; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_C_INITIALIZE_ARGS.java b/src/sun/security/pkcs11/wrapper/CK_C_INITIALIZE_ARGS.java new file mode 100644 index 00000000..3433f955 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_C_INITIALIZE_ARGS.java @@ -0,0 +1,120 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_C_INITIALIZE_ARGS contains the optional arguments for the + * C_Initialize function.

    + * PKCS#11 structure: + *

    + * typedef struct CK_C_INITIALIZE_ARGS {  
    + *   CK_CREATEMUTEX CreateMutex;  
    + *   CK_DESTROYMUTEX DestroyMutex;  
    + *   CK_LOCKMUTEX LockMutex;  
    + *   CK_UNLOCKMUTEX UnlockMutex;  
    + *   CK_FLAGS flags;  
    + *   CK_VOID_PTR pReserved;  
    + * } CK_C_INITIALIZE_ARGS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_C_INITIALIZE_ARGS { + + /** + * PKCS#11: + *
    +     *   CK_CREATEMUTEX CreateMutex;
    +     * 
    + */ + public CK_CREATEMUTEX CreateMutex; + + /** + * PKCS#11: + *
    +     *   CK_DESTROYMUTEX DestroyMutex;
    +     * 
    + */ + public CK_DESTROYMUTEX DestroyMutex; + + /** + * PKCS#11: + *
    +     *   CK_LOCKMUTEX LockMutex;
    +     * 
    + */ + public CK_LOCKMUTEX LockMutex; + + /** + * PKCS#11: + *
    +     *   CK_UNLOCKMUTEX UnlockMutex;
    +     * 
    + */ + public CK_UNLOCKMUTEX UnlockMutex; + + /** + * PKCS#11: + *
    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pReserved;
    +     * 
    + */ + public Object pReserved; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_DATE.java b/src/sun/security/pkcs11/wrapper/CK_DATE.java new file mode 100644 index 00000000..e6b13965 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_DATE.java @@ -0,0 +1,137 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class .

    + * PKCS#11 structure: + *

    + * typedef struct CK_DATE {  
    + *   CK_CHAR year[4];  
    + *   CK_CHAR month[2];  
    + *   CK_CHAR day[2];  
    + * } CK_DATE;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_DATE implements Cloneable { + + /** + * PKCS#11: + *
    +     *   CK_CHAR year[4];   - the year ("1900" - "9999")
    +     * 
    + */ + public char[] year; /* the year ("1900" - "9999") */ + + /** + * PKCS#11: + *
    +     *   CK_CHAR month[2];  - the month ("01" - "12")
    +     * 
    + */ + public char[] month; /* the month ("01" - "12") */ + + /** + * PKCS#11: + *
    +     *   CK_CHAR day[2];    - the day ("01" - "31")
    +     * 
    + */ + public char[] day; /* the day ("01" - "31") */ + + public CK_DATE(char[] year, char[] month, char[] day) { + this.year = year; + this.month = month; + this.day = day; + } + + /** + * Create a (deep) clone of this object. + * + * @return A clone of this object. + */ + public Object clone() { + CK_DATE copy = null; + try { + copy = (CK_DATE) super.clone(); + } catch (CloneNotSupportedException cnse) { + // re-throw as RuntimeException + throw (RuntimeException) + (new RuntimeException("Clone error").initCause(cnse)); + } + copy.year = this.year.clone(); + copy.month = this.month.clone(); + copy.day = this.day.clone(); + + return copy; + } + + /** + * Returns the string representation of CK_DATE. + * + * @return the string representation of CK_DATE + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(new String(day)); + buffer.append('.'); + buffer.append(new String(month)); + buffer.append('.'); + buffer.append(new String(year)); + buffer.append(" (DD.MM.YYYY)"); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_DESTROYMUTEX.java b/src/sun/security/pkcs11/wrapper/CK_DESTROYMUTEX.java new file mode 100644 index 00000000..95a65ffb --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_DESTROYMUTEX.java @@ -0,0 +1,68 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * interface CK_DESTROYMUTEX.

    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public interface CK_DESTROYMUTEX { + + /** + * Method CK_DESTROYMUTEX + * + * @param pMutex The mutex (lock) object. + * @exception PKCS11Exception + */ + public void CK_DESTROYMUTEX(Object pMutex) throws PKCS11Exception; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java new file mode 100644 index 00000000..42d0164f --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_ECDH1_DERIVE_PARAMS.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_ECDH1_DERIVE_PARAMS provides the parameters to the + * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms.

    + * PKCS#11 structure: + *

    + * typedef struct CK_ECDH1_DERIVE_PARAMS {
    + *   CK_EC_KDF_TYPE kdf;
    + *   CK_ULONG ulSharedDataLen;
    + *   CK_BYTE_PTR pSharedData;
    + *   CK_ULONG ulPublicDataLen;
    + *   CK_BYTE_PTR pPublicData;
    + * } CK_ECDH1_DERIVE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + */ +public class CK_ECDH1_DERIVE_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_EC_KDF_TYPE kdf;
    +     * 
    + */ + public long kdf; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulSharedDataLen;
    +     *   CK_BYTE_PTR pSharedData;
    +     * 
    + */ + public byte[] pSharedData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen;
    +     *   CK_BYTE_PTR pPublicData;
    +     * 
    + */ + public byte[] pPublicData; + + public CK_ECDH1_DERIVE_PARAMS(long kdf, byte[] pSharedData, byte[] pPublicData) { + this.kdf = kdf; + this.pSharedData = pSharedData; + this.pPublicData = pPublicData; + } + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("kdf: 0x"); + buffer.append(Functions.toFullHexString(kdf)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSharedDataLen: "); + buffer.append(pSharedData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSharedData: "); + buffer.append(Functions.toHexString(pSharedData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen: "); + buffer.append(pPublicData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData: "); + buffer.append(Functions.toHexString(pPublicData)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_ECDH2_DERIVE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_ECDH2_DERIVE_PARAMS.java new file mode 100644 index 00000000..63cef457 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_ECDH2_DERIVE_PARAMS.java @@ -0,0 +1,181 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_ECDH2_DERIVE_PARAMS provides the parameters to the + * CKM_ECMQV_DERIVE mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_ECDH2_DERIVE_PARAMS {
    + *   CK_EC_KDF_TYPE kdf;
    + *   CK_ULONG ulSharedDataLen;
    + *   CK_BYTE_PTR pSharedData;
    + *   CK_ULONG ulPublicDataLen;
    + *   CK_BYTE_PTR pPublicData;
    + *   CK_ULONG ulPrivateDataLen;
    + *   CK_OBJECT_HANDLE hPrivateData;
    + *   CK_ULONG ulPublicDataLen2;
    + *   CK_BYTE_PTR pPublicData2;
    + * } CK_ECDH2_DERIVE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + */ +public class CK_ECDH2_DERIVE_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_EC_KDF_TYPE kdf;
    +     * 
    + */ + public long kdf; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulSharedDataLen;
    +     *   CK_BYTE_PTR pSharedData;
    +     * 
    + */ + public byte[] pSharedData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen;
    +     *   CK_BYTE_PTR pPublicData;
    +     * 
    + */ + public byte[] pPublicData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPrivateDataLen;
    +     * 
    + */ + public long ulPrivateDataLen; + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hPrivateData;
    +     * 
    + */ + public long hPrivateData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen2;
    +     *   CK_BYTE_PTR pPublicData2;
    +     * 
    + */ + public byte[] pPublicData2; + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("kdf: 0x"); + buffer.append(Functions.toFullHexString(kdf)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSharedDataLen: "); + buffer.append(pSharedData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSharedData: "); + buffer.append(Functions.toHexString(pSharedData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen: "); + buffer.append(pPublicData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData: "); + buffer.append(Functions.toHexString(pPublicData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulPrivateDataLen: "); + buffer.append(ulPrivateDataLen); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hPrivateData: "); + buffer.append(hPrivateData); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen2: "); + buffer.append(pPublicData2.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData2: "); + buffer.append(Functions.toHexString(pPublicData2)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_INFO.java b/src/sun/security/pkcs11/wrapper/CK_INFO.java new file mode 100644 index 00000000..9882c795 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_INFO.java @@ -0,0 +1,164 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_INFO provides general information about Cryptoki.

    + * PKCS#11 structure: + *

    + *  typedef struct CK_INFO {  
    + *    CK_VERSION cryptokiVersion;  
    + *    CK_UTF8CHAR manufacturerID[32];  
    + *    CK_FLAGS flags;  
    + *    CK_UTF8CHAR libraryDescription[32];  
    + *    CK_VERSION libraryVersion;  
    + *  } CK_INFO;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_INFO { + + /** + * Cryptoki interface version number

    + * PKCS#11: + *

    +     *   CK_VERSION cryptokiVersion;
    +     * 
    + */ + public CK_VERSION cryptokiVersion; + + /** + * ID of the Cryptoki library manufacturer. must be blank + * padded - only the first 32 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR manufacturerID[32];
    +     * 
    + */ + public char[] manufacturerID; + + /** + * bit flags reserved for future versions. must be zero

    + * PKCS#11: + *

    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; + + +/* libraryDescription and libraryVersion are new for v2.0 */ + + /** + * must be blank padded - only the first 32 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR libraryDescription[32];
    +     * 
    + */ + public char[] libraryDescription; + + /** + * Cryptoki library version number

    + * PKCS#11: + *

    +     *   CK_VERSION libraryVersion;
    +     * 
    + */ + public CK_VERSION libraryVersion; + + public CK_INFO(CK_VERSION cryptoVer, char[] vendor, long flags, + char[] libDesc, CK_VERSION libVer) { + this.cryptokiVersion = cryptoVer; + this.manufacturerID = vendor; + this.flags = flags; + this.libraryDescription = libDesc; + this.libraryVersion = libVer; + } + + /** + * Returns the string representation of CK_INFO. + * + * @return the string representation of CK_INFO + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("cryptokiVersion: "); + buffer.append(cryptokiVersion.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("manufacturerID: "); + buffer.append(new String(manufacturerID)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("flags: "); + buffer.append(Functions.toBinaryString(flags)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("libraryDescription: "); + buffer.append(new String(libraryDescription)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("libraryVersion: "); + buffer.append(libraryVersion.toString()); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_LOCKMUTEX.java b/src/sun/security/pkcs11/wrapper/CK_LOCKMUTEX.java new file mode 100644 index 00000000..6848a084 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_LOCKMUTEX.java @@ -0,0 +1,68 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * interface CK_LOCKMUTEX

    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public interface CK_LOCKMUTEX { + + /** + * Method CK_LOCKMUTEX + * + * @param pMutex The mutex (lock) object to lock. + * @exception PKCS11Exception + */ + public void CK_LOCKMUTEX(Object pMutex) throws PKCS11Exception; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_MECHANISM.java b/src/sun/security/pkcs11/wrapper/CK_MECHANISM.java new file mode 100644 index 00000000..6d5930e7 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_MECHANISM.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +import java.math.BigInteger; + +/** + * class CK_MECHANISM specifies a particular mechanism and any parameters it + * requires.

    + * PKCS#11 structure: + *

    + *  typedef struct CK_MECHANISM {  
    + *    CK_MECHANISM_TYPE mechanism;  
    + *    CK_VOID_PTR pParameter;  
    + *    CK_ULONG ulParameterLen;  
    + *  } CK_MECHANISM;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_MECHANISM { + + /** + * PKCS#11: + *
    +     *   CK_MECHANISM_TYPE mechanism;
    +     * 
    + */ + public long mechanism; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pParameter;
    +     *   CK_ULONG ulParameterLen;
    +     * 
    + */ + public Object pParameter; + + public CK_MECHANISM() { + // empty + } + + public CK_MECHANISM(long mechanism) { + this.mechanism = mechanism; + } + + // We don't have a (long,Object) constructor to force type checking. + // This makes sure we don't accidentally pass a class that the native + // code cannot handle. + + public CK_MECHANISM(long mechanism, byte[] pParameter) { + init(mechanism, pParameter); + } + + public CK_MECHANISM(long mechanism, BigInteger b) { + init(mechanism, sun.security.pkcs11.P11Util.getMagnitude(b)); + } + + public CK_MECHANISM(long mechanism, CK_VERSION version) { + init(mechanism, version); + } + + public CK_MECHANISM(long mechanism, CK_SSL3_MASTER_KEY_DERIVE_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_SSL3_KEY_MAT_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_TLS_PRF_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_ECDH1_DERIVE_PARAMS params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, Long params) { + init(mechanism, params); + } + + public CK_MECHANISM(long mechanism, CK_AES_CTR_PARAMS params) { + init(mechanism, params); + } + + private void init(long mechanism, Object pParameter) { + this.mechanism = mechanism; + this.pParameter = pParameter; + } + + /** + * Returns the string representation of CK_MECHANISM. + * + * @return the string representation of CK_MECHANISM + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("mechanism: "); + buffer.append(mechanism); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pParameter: "); + buffer.append(pParameter.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulParameterLen: ??"); + //buffer.append(pParameter.length); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_MECHANISM_INFO.java b/src/sun/security/pkcs11/wrapper/CK_MECHANISM_INFO.java new file mode 100644 index 00000000..72018721 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_MECHANISM_INFO.java @@ -0,0 +1,127 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_MECHANISM_INFO provides information about a particular mechanism. + *

    + * PKCS#11 structure: + *

    + * typedef struct CK_MECHANISM_INFO {  
    + *   CK_ULONG ulMinKeySize;  
    + *   CK_ULONG ulMaxKeySize;  
    + *   CK_FLAGS flags;  
    + * } CK_MECHANISM_INFO;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_MECHANISM_INFO { + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMinKeySize;
    +     * 
    + */ + public long ulMinKeySize; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMaxKeySize;
    +     * 
    + */ + public long ulMaxKeySize; + + /** + * PKCS#11: + *
    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; + + public CK_MECHANISM_INFO(long minKeySize, long maxKeySize, + long flags) { + this.ulMinKeySize = minKeySize; + this.ulMaxKeySize = maxKeySize; + this.flags = flags; + } + + /** + * Returns the string representation of CK_MECHANISM_INFO. + * + * @return the string representation of CK_MECHANISM_INFO + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("ulMinKeySize: "); + buffer.append(String.valueOf(ulMinKeySize)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulMaxKeySize: "); + buffer.append(String.valueOf(ulMaxKeySize)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("flags: "); + buffer.append(String.valueOf(flags)); + buffer.append(" = "); + buffer.append(Functions.mechanismInfoFlagsToString(flags)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } +} diff --git a/src/sun/security/pkcs11/wrapper/CK_NOTIFY.java b/src/sun/security/pkcs11/wrapper/CK_NOTIFY.java new file mode 100644 index 00000000..e21882ca --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_NOTIFY.java @@ -0,0 +1,70 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * interface CK_NOTIFY.

    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public interface CK_NOTIFY { + + /** + * Method CK_NOTIFY + * + * @param hSession + * @param event + * @param pApplication + * @exception PKCS11Exception + */ + public void CK_NOTIFY(long hSession, long event, Object pApplication) throws PKCS11Exception; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java new file mode 100644 index 00000000..c58b486b --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_PBE_PARAMS.java @@ -0,0 +1,147 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_PBE_PARAMS provides all of the necessary information required byte + * the CKM_PBE mechanisms and the CKM_PBA_SHA1_WITH_SHA1_HMAC mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_PBE_PARAMS {
    + *   CK_CHAR_PTR pInitVector;
    + *   CK_CHAR_PTR pPassword;
    + *   CK_ULONG ulPasswordLen;
    + *   CK_CHAR_PTR pSalt;
    + *   CK_ULONG ulSaltLen;
    + *   CK_ULONG ulIteration;
    + * } CK_PBE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_PBE_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_CHAR_PTR pInitVector;
    +     * 
    + */ + public char[] pInitVector; + + /** + * PKCS#11: + *
    +     *   CK_CHAR_PTR pPassword;
    +     *   CK_ULONG ulPasswordLen;
    +     * 
    + */ + public char[] pPassword; + + /** + * PKCS#11: + *
    +     *   CK_CHAR_PTR pSalt
    +     *   CK_ULONG ulSaltLen;
    +     * 
    + */ + public char[] pSalt; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulIteration;
    +     * 
    + */ + public long ulIteration; + + /** + * Returns the string representation of CK_PBE_PARAMS. + * + * @return the string representation of CK_PBE_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("pInitVector: "); + buffer.append(pInitVector); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulPasswordLen: "); + buffer.append(pPassword.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPassword: "); + buffer.append(pPassword); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulSaltLen: "); + buffer.append(pSalt.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSalt: "); + buffer.append(pSalt); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulIteration: "); + buffer.append(ulIteration); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java new file mode 100644 index 00000000..ad0fbd3f --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_PKCS5_PBKD2_PARAMS.java @@ -0,0 +1,161 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_PKCS5_PBKD2_PARAMS provides the parameters to the CKM_PKCS5_PBKD2 + * mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_PKCS5_PBKD2_PARAMS {
    + *   CK_PKCS5_PBKD2_SALT_SOURCE_TYPE saltSource;
    + *   CK_VOID_PTR pSaltSourceData;
    + *   CK_ULONG ulSaltSourceDataLen;
    + *   CK_ULONG iterations;
    + *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
    + *   CK_VOID_PTR pPrfData;
    + *   CK_ULONG ulPrfDataLen;
    + * } CK_PKCS5_PBKD2_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_PKCS5_PBKD2_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
    +     * 
    + */ + public long saltSource; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pSaltSourceData;
    +     *   CK_ULONG ulSaltSourceDataLen;
    +     * 
    + */ + public byte[] pSaltSourceData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG iterations;
    +     * 
    + */ + public long iterations; + + /** + * PKCS#11: + *
    +     *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
    +     * 
    + */ + public long prf; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pPrfData;
    +     *   CK_ULONG ulPrfDataLen;
    +     * 
    + */ + public byte[] pPrfData; + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("saltSource: "); + buffer.append(saltSource); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSaltSourceData: "); + buffer.append(Functions.toHexString(pSaltSourceData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulSaltSourceDataLen: "); + buffer.append(pSaltSourceData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("iterations: "); + buffer.append(iterations); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("prf: "); + buffer.append(prf); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPrfData: "); + buffer.append(Functions.toHexString(pPrfData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulPrfDataLen: "); + buffer.append(pPrfData.length); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_OAEP_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_OAEP_PARAMS.java new file mode 100644 index 00000000..12b4cae1 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_OAEP_PARAMS.java @@ -0,0 +1,143 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the + * CKM_RSA_PKCS_OAEP mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_RSA_PKCS_OAEP_PARAMS {
    + *   CK_MECHANISM_TYPE hashAlg;
    + *   CK_RSA_PKCS_OAEP_MGF_TYPE mgf;
    + *   CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
    + *   CK_VOID_PTR pSourceData;
    + *   CK_ULONG ulSourceDataLen;
    + * } CK_RSA_PKCS_OAEP_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_RSA_PKCS_OAEP_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_MECHANISM_TYPE hashAlg;
    +     * 
    + */ + public long hashAlg; + + /** + * PKCS#11: + *
    +     *   CK_RSA_PKCS_OAEP_MGF_TYPE mgf;
    +     * 
    + */ + public long mgf; + + /** + * PKCS#11: + *
    +     *   CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
    +     * 
    + */ + public long source; + + /** + * PKCS#11: + *
    +     *   CK_VOID_PTR pSourceData;
    +     *   CK_ULONG ulSourceDataLen;
    +     * 
    + */ + public byte[] pSourceData; + + //CK_ULONG ulSourceDataLen; + // ulSourceDataLen == pSourceData.length + + /** + * Returns the string representation of CK_RSA_PKCS_OAEP_PARAMS. + * + * @return the string representation of CK_RSA_PKCS_OAEP_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("hashAlg: "); + buffer.append(hashAlg); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("mgf: "); + buffer.append(mgf); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("source: "); + buffer.append(source); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSourceData: "); + buffer.append(pSourceData.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pSourceDataLen: "); + buffer.append(Functions.toHexString(pSourceData)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_PSS_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_PSS_PARAMS.java new file mode 100644 index 00000000..e3601666 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_RSA_PKCS_PSS_PARAMS.java @@ -0,0 +1,118 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_RSA_PKCS_PSS_PARAMS provides the parameters to the CKM_RSA_PKCS_OAEP + * mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_RSA_PKCS_PSS_PARAMS {
    + *   CK_MECHANISM_TYPE hashAlg;
    + *   CK_RSA_PKCS_MGF_TYPE mgf;
    + *   CK_ULONG sLen;
    + * } CK_RSA_PKCS_PSS_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + */ +public class CK_RSA_PKCS_PSS_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_MECHANISM_TYPE hashAlg;
    +     * 
    + */ + public long hashAlg; + + /** + * PKCS#11: + *
    +     *   CK_RSA_PKCS_MGF_TYPE mgf;
    +     * 
    + */ + public long mgf; + + /** + * PKCS#11: + *
    +     *   CK_ULONG sLen;
    +     * 
    + */ + public long sLen; + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("hashAlg: 0x"); + buffer.append(Functions.toFullHexString(hashAlg)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("mgf: 0x"); + buffer.append(Functions.toFullHexString(mgf)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("sLen: "); + buffer.append(sLen); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SESSION_INFO.java b/src/sun/security/pkcs11/wrapper/CK_SESSION_INFO.java new file mode 100644 index 00000000..89cbe752 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SESSION_INFO.java @@ -0,0 +1,142 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SESSION_INFO provides information about a session.

    + * PKCS#11 structure: + *

    + * typedef struct CK_SESSION_INFO {  
    + *   CK_SLOT_ID slotID;  
    + *   CK_STATE state;  
    + *   CK_FLAGS flags;  
    + *   CK_ULONG ulDeviceError;  
    + * } CK_SESSION_INFO;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SESSION_INFO { + + /** + * PKCS#11: + *
    +     *   CK_SLOT_ID slotID;
    +     * 
    + */ + public long slotID; + + /** + * PKCS#11: + *
    +     *   CK_STATE state;
    +     * 
    + */ + public long state; + + /** + * PKCS#11: + *
    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; /* see below */ + + /* ulDeviceError was changed from CK_USHORT to CK_ULONG for + * v2.0 */ + /** + * PKCS#11: + *
    +     *   CK_ULONG ulDeviceError;
    +     * 
    + */ + public long ulDeviceError; /* device-dependent error code */ + + public CK_SESSION_INFO(long slotID, long state, + long flags, long ulDeviceError) { + this.slotID = slotID; + this.state = state; + this.flags = flags; + this.ulDeviceError = ulDeviceError; + } + + /** + * Returns the string representation of CK_SESSION_INFO. + * + * @return the string representation of CK_SESSION_INFO + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("slotID: "); + buffer.append(String.valueOf(slotID)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("state: "); + buffer.append(Functions.sessionStateToString(state)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("flags: "); + buffer.append(Functions.sessionInfoFlagsToString(flags)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulDeviceError: "); + buffer.append(Functions.toHexString(ulDeviceError)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SLOT_INFO.java b/src/sun/security/pkcs11/wrapper/CK_SLOT_INFO.java new file mode 100644 index 00000000..cd6ad512 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SLOT_INFO.java @@ -0,0 +1,163 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SLOT_INFO provides information about a slot.

    + * PKCS#11 structure: + *

    + *  typedef struct CK_SLOT_INFO {  
    + *    CK_UTF8CHAR slotDescription[64];  
    + *    CK_UTF8CHAR manufacturerID[32];  
    + *    CK_FLAGS flags;  
    + *    CK_VERSION hardwareVersion;  
    + *    CK_VERSION firmwareVersion;  
    + *  } CK_SLOT_INFO;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SLOT_INFO { + + /* slotDescription and manufacturerID have been changed from + * CK_CHAR to CK_UTF8CHAR for v2.11. */ + + /** + * must be blank padded and only the first 64 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR slotDescription[64];
    +     * 
    + */ + public char[] slotDescription; + + /** + * must be blank padded and only the first 32 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR manufacturerID[32];
    +     * 
    + */ + public char[] manufacturerID; + + /** + * PKCS#11: + *
    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; + + /* hardwareVersion and firmwareVersion are new for v2.0 */ + /** + * version of hardware

    + * PKCS#11: + *

    +     *   CK_VERSION hardwareVersion;
    +     * 
    + */ + public CK_VERSION hardwareVersion; + + /** + * version of firmware

    + * PKCS#11: + *

    +     *   CK_VERSION firmwareVersion;
    +     * 
    + */ + public CK_VERSION firmwareVersion; + + public CK_SLOT_INFO(char[] slotDesc, char[] vendor, + long flags, CK_VERSION hwVer, CK_VERSION fwVer) { + this.slotDescription = slotDesc; + this.manufacturerID = vendor; + this.flags = flags; + this.hardwareVersion = hwVer; + this.firmwareVersion = fwVer; + } + + /** + * Returns the string representation of CK_SLOT_INFO. + * + * @return the string representation of CK_SLOT_INFO + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("slotDescription: "); + buffer.append(new String(slotDescription)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("manufacturerID: "); + buffer.append(new String(manufacturerID)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("flags: "); + buffer.append(Functions.slotInfoFlagsToString(flags)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hardwareVersion: "); + buffer.append(hardwareVersion.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("firmwareVersion: "); + buffer.append(firmwareVersion.toString()); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_OUT.java b/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_OUT.java new file mode 100644 index 00000000..3bd16800 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_OUT.java @@ -0,0 +1,162 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SSL3_KEY_MAT_OUT contains the resulting key handles and + * initialization vectors after performing a C_DeriveKey function with the + * CKM_SSL3_KEY_AND_MAC_DERIVE mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_SSL3_KEY_MAT_OUT {
    + *   CK_OBJECT_HANDLE hClientMacSecret;
    + *   CK_OBJECT_HANDLE hServerMacSecret;
    + *   CK_OBJECT_HANDLE hClientKey;
    + *   CK_OBJECT_HANDLE hServerKey;
    + *   CK_BYTE_PTR pIVClient;
    + *   CK_BYTE_PTR pIVServer;
    + * } CK_SSL3_KEY_MAT_OUT;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SSL3_KEY_MAT_OUT{ + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hClientMacSecret;
    +     * 
    + */ + public long hClientMacSecret; + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hServerMacSecret;
    +     * 
    + */ + public long hServerMacSecret; + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hClientKey;
    +     * 
    + */ + public long hClientKey; + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hServerKey;
    +     * 
    + */ + public long hServerKey; + + /** + * PKCS#11: + *
    +     *   CK_BYTE_PTR pIVClient;
    +     * 
    + */ + public byte[] pIVClient; + + /** + * PKCS#11: + *
    +     *   CK_BYTE_PTR pIVServer;
    +     * 
    + */ + public byte[] pIVServer; + + /** + * Returns the string representation of CK_SSL3_KEY_MAT_OUT. + * + * @return the string representation of CK_SSL3_KEY_MAT_OUT + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append(Constants.INDENT); + buffer.append("hClientMacSecret: "); + buffer.append(hClientMacSecret); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hServerMacSecret: "); + buffer.append(hServerMacSecret); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hClientKey: "); + buffer.append(hClientKey); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hServerKey: "); + buffer.append(hServerKey); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pIVClient: "); + buffer.append(Functions.toHexString(pIVClient)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pIVServer: "); + buffer.append(Functions.toHexString(pIVServer)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_PARAMS.java new file mode 100644 index 00000000..c2c0cf7d --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SSL3_KEY_MAT_PARAMS.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SSL3_KEY_MAT_PARAMS provides the parameters to the + * CKM_SSL3_KEY_AND_MAC_DERIVE mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_SSL3_KEY_MAT_PARAMS {
    + *   CK_ULONG ulMacSizeInBits;
    + *   CK_ULONG ulKeySizeInBits;
    + *   CK_ULONG ulIVSizeInBits;
    + *   CK_BBOOL bIsExport;
    + *   CK_SSL3_RANDOM_DATA RandomInfo;
    + *   CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
    + * } CK_SSL3_KEY_MAT_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SSL3_KEY_MAT_PARAMS{ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMacSizeInBits;
    +     * 
    + */ + public long ulMacSizeInBits; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulKeySizeInBits;
    +     * 
    + */ + public long ulKeySizeInBits; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulIVSizeInBits;
    +     * 
    + */ + public long ulIVSizeInBits; + + /** + * PKCS#11: + *
    +     *   CK_BBOOL bIsExport;
    +     * 
    + */ + public boolean bIsExport; + + /** + * PKCS#11: + *
    +     *   CK_SSL3_RANDOM_DATA RandomInfo;
    +     * 
    + */ + public CK_SSL3_RANDOM_DATA RandomInfo; + + /** + * PKCS#11: + *
    +     *   CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
    +     * 
    + */ + public CK_SSL3_KEY_MAT_OUT pReturnedKeyMaterial; + + public CK_SSL3_KEY_MAT_PARAMS(int macSize, int keySize, int ivSize, boolean export, CK_SSL3_RANDOM_DATA random) { + ulMacSizeInBits = macSize; + ulKeySizeInBits = keySize; + ulIVSizeInBits = ivSize; + bIsExport = export; + RandomInfo = random; + pReturnedKeyMaterial = new CK_SSL3_KEY_MAT_OUT(); + if (ivSize != 0) { + int n = ivSize >> 3; + pReturnedKeyMaterial.pIVClient = new byte[n]; + pReturnedKeyMaterial.pIVServer = new byte[n]; + } + } + + /** + * Returns the string representation of CK_SSL3_KEY_MAT_PARAMS. + * + * @return the string representation of CK_SSL3_KEY_MAT_PARAMS + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append(Constants.INDENT); + buffer.append("ulMacSizeInBits: "); + buffer.append(ulMacSizeInBits); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulKeySizeInBits: "); + buffer.append(ulKeySizeInBits); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulIVSizeInBits: "); + buffer.append(ulIVSizeInBits); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("bIsExport: "); + buffer.append(bIsExport); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("RandomInfo: "); + buffer.append(RandomInfo); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pReturnedKeyMaterial: "); + buffer.append(pReturnedKeyMaterial); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SSL3_MASTER_KEY_DERIVE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_SSL3_MASTER_KEY_DERIVE_PARAMS.java new file mode 100644 index 00000000..f8e2b382 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SSL3_MASTER_KEY_DERIVE_PARAMS.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SSL3_MASTER_KEY_DERIVE_PARAMS provides the parameters to the + * CKM_SSL3_MASTER_KEY_DERIVE mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
    + *   CK_SSL3_RANDOM_DATA RandomInfo;
    + *   CK_VERSION_PTR pVersion;
    + * } CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SSL3_MASTER_KEY_DERIVE_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_SSL3_RANDOM_DATA RandomInfo;
    +     * 
    + */ + public CK_SSL3_RANDOM_DATA RandomInfo; + + /** + * PKCS#11: + *
    +     *   CK_VERSION_PTR pVersion;
    +     * 
    + */ + public CK_VERSION pVersion; + + public CK_SSL3_MASTER_KEY_DERIVE_PARAMS(CK_SSL3_RANDOM_DATA random, CK_VERSION version) { + RandomInfo = random; + pVersion = version; + } + + /** + * Returns the string representation of CK_SSL3_MASTER_KEY_DERIVE_PARAMS. + * + * @return the string representation of CK_SSL3_MASTER_KEY_DERIVE_PARAMS + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append(Constants.INDENT); + buffer.append("RandomInfo: "); + buffer.append(RandomInfo); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pVersion: "); + buffer.append(pVersion); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_SSL3_RANDOM_DATA.java b/src/sun/security/pkcs11/wrapper/CK_SSL3_RANDOM_DATA.java new file mode 100644 index 00000000..3e9b9575 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_SSL3_RANDOM_DATA.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_SSL3_RANDOM_DATA provides information about the random data of a + * client and a server in an SSL context. This class is used by both the + * CKM_SSL3_MASTER_KEY_DERIVE and the CKM_SSL3_KEY_AND_MAC_DERIVE mechanisms. + *

    + * PKCS#11 structure: + *

    + * typedef struct CK_SSL3_RANDOM_DATA {
    + *   CK_BYTE_PTR pClientRandom;
    + *   CK_ULONG ulClientRandomLen;
    + *   CK_BYTE_PTR pServerRandom;
    + *   CK_ULONG ulServerRandomLen;
    + * } CK_SSL3_RANDOM_DATA;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_SSL3_RANDOM_DATA { + + /** + * PKCS#11: + *
    +     *   CK_BYTE_PTR pClientRandom;
    +     *   CK_ULONG ulClientRandomLen;
    +     * 
    + */ + public byte[] pClientRandom; + + /** + * PKCS#11: + *
    +     *   CK_BYTE_PTR pServerRandom;
    +     *   CK_ULONG ulServerRandomLen;
    +     * 
    + */ + public byte[] pServerRandom; + + public CK_SSL3_RANDOM_DATA(byte[] clientRandom, byte[] serverRandom) { + pClientRandom = clientRandom; + pServerRandom = serverRandom; + } + + /** + * Returns the string representation of CK_SSL3_RANDOM_DATA. + * + * @return the string representation of CK_SSL3_RANDOM_DATA + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append(Constants.INDENT); + buffer.append("pClientRandom: "); + buffer.append(Functions.toHexString(pClientRandom)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulClientRandomLen: "); + buffer.append(pClientRandom.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pServerRandom: "); + buffer.append(Functions.toHexString(pServerRandom)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulServerRandomLen: "); + buffer.append(pServerRandom.length); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_TLS_PRF_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_TLS_PRF_PARAMS.java new file mode 100644 index 00000000..a581702f --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_TLS_PRF_PARAMS.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11.wrapper; + +/** + * CK_TLS_PRF_PARAMS from PKCS#11 v2.20. + * + * @author Andreas Sterbenz + * @since 1.6 + */ +public class CK_TLS_PRF_PARAMS { + + public byte[] pSeed; + public byte[] pLabel; + public byte[] pOutput; + + public CK_TLS_PRF_PARAMS(byte[] pSeed, byte[] pLabel, byte[] pOutput) { + this.pSeed = pSeed; + this.pLabel = pLabel; + this.pOutput = pOutput; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_TOKEN_INFO.java b/src/sun/security/pkcs11/wrapper/CK_TOKEN_INFO.java new file mode 100644 index 00000000..f63433ce --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_TOKEN_INFO.java @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_TOKEN_INFO provides information about a token.

    + * PKCS#11 structure: + *

    + * typedef struct CK_TOKEN_INFO {  
    + *   CK_UTF8CHAR label[32];  
    + *   CK_UTF8CHAR manufacturerID[32];  
    + *   CK_UTF8CHAR model[16];  
    + *   CK_CHAR serialNumber[16];  
    + *   CK_FLAGS flags;  
    + *   CK_ULONG ulMaxSessionCount;  
    + *   CK_ULONG ulSessionCount;  
    + *   CK_ULONG ulMaxRwSessionCount;  
    + *   CK_ULONG ulRwSessionCount;  
    + *   CK_ULONG ulMaxPinLen;  
    + *   CK_ULONG ulMinPinLen;  
    + *   CK_ULONG ulTotalPublicMemory;  
    + *   CK_ULONG ulFreePublicMemory;  
    + *   CK_ULONG ulTotalPrivateMemory;  
    + *   CK_ULONG ulFreePrivateMemory;  
    + *   CK_VERSION hardwareVersion;  
    + *   CK_VERSION firmwareVersion;  
    + *   CK_CHAR utcTime[16];  
    + * } CK_TOKEN_INFO;
    + *   
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_TOKEN_INFO { + + /* label, manufacturerID, and model have been changed from + * CK_CHAR to CK_UTF8CHAR for v2.11. */ + /** + * must be blank padded and only the first 32 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR label[32];
    +     * 
    + */ + public char[] label; /* blank padded */ + + /** + * must be blank padded and only the first 32 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR manufacturerID[32];
    +     * 
    + */ + public char[] manufacturerID; /* blank padded */ + + /** + * must be blank padded and only the first 16 chars will be used

    + * PKCS#11: + *

    +     *   CK_UTF8CHAR model[16];
    +     * 
    + */ + public char[] model; /* blank padded */ + + /** + * must be blank padded and only the first 16 chars will be used

    + * PKCS#11: + *

    +     *   CK_CHAR serialNumber[16];
    +     * 
    + */ + public char[] serialNumber; /* blank padded */ + + /** + * PKCS#11: + *
    +     *   CK_FLAGS flags;
    +     * 
    + */ + public long flags; /* see below */ + + /* ulMaxSessionCount, ulSessionCount, ulMaxRwSessionCount, + * ulRwSessionCount, ulMaxPinLen, and ulMinPinLen have all been + * changed from CK_USHORT to CK_ULONG for v2.0 */ + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMaxSessionCount;
    +     * 
    + */ + public long ulMaxSessionCount; /* max open sessions */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulSessionCount;
    +     * 
    + */ + public long ulSessionCount; /* sess. now open */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMaxRwSessionCount;
    +     * 
    + */ + public long ulMaxRwSessionCount; /* max R/W sessions */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulRwSessionCount;
    +     * 
    + */ + public long ulRwSessionCount; /* R/W sess. now open */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMaxPinLen;
    +     * 
    + */ + public long ulMaxPinLen; /* in bytes */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulMinPinLen;
    +     * 
    + */ + public long ulMinPinLen; /* in bytes */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulTotalPublicMemory;
    +     * 
    + */ + public long ulTotalPublicMemory; /* in bytes */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulFreePublicMemory;
    +     * 
    + */ + public long ulFreePublicMemory; /* in bytes */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulTotalPrivateMemory;
    +     * 
    + */ + public long ulTotalPrivateMemory; /* in bytes */ + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulFreePrivateMemory;
    +     * 
    + */ + public long ulFreePrivateMemory; /* in bytes */ + + /* hardwareVersion, firmwareVersion, and time are new for + * v2.0 */ + /** + * PKCS#11: + *
    +     *   CK_VERSION hardwareVersion;
    +     * 
    + */ + public CK_VERSION hardwareVersion; /* version of hardware */ + + /** + * PKCS#11: + *
    +     *   CK_VERSION firmwareVersion;
    +     * 
    + */ + public CK_VERSION firmwareVersion; /* version of firmware */ + + /** + * only the first 16 chars will be used + * PKCS#11: + *
    +     *   CK_CHAR utcTime[16];
    +     * 
    + */ + public char[] utcTime; /* time */ + + public CK_TOKEN_INFO(char[] label, char[] vendor, char[] model, + char[] serialNo, long flags, + long sessionMax, long session, + long rwSessionMax, long rwSession, + long pinLenMax, long pinLenMin, + long totalPubMem, long freePubMem, + long totalPrivMem, long freePrivMem, + CK_VERSION hwVer, CK_VERSION fwVer, char[] utcTime) { + this.label = label; + this.manufacturerID = vendor; + this.model = model; + this.serialNumber = serialNo; + this.flags = flags; + this.ulMaxSessionCount = sessionMax; + this.ulSessionCount = session; + this.ulMaxRwSessionCount = rwSessionMax; + this.ulRwSessionCount = rwSession; + this.ulMaxPinLen = pinLenMax; + this.ulMinPinLen = pinLenMin; + this.ulTotalPublicMemory = totalPubMem; + this.ulFreePublicMemory = freePubMem; + this.ulTotalPrivateMemory = totalPrivMem; + this.ulFreePrivateMemory = freePrivMem; + this.hardwareVersion = hwVer; + this.firmwareVersion = fwVer; + this.utcTime = utcTime; + } + + /** + * Returns the string representation of CK_TOKEN_INFO. + * + * @return the string representation of CK_TOKEN_INFO + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("label: "); + buffer.append(new String(label)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("manufacturerID: "); + buffer.append(new String(manufacturerID)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("model: "); + buffer.append(new String(model)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("serialNumber: "); + buffer.append(new String(serialNumber)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("flags: "); + buffer.append(Functions.tokenInfoFlagsToString(flags)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulMaxSessionCount: "); + buffer.append((ulMaxSessionCount == PKCS11Constants.CK_EFFECTIVELY_INFINITE) + ? "CK_EFFECTIVELY_INFINITE" + : (ulMaxSessionCount == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulMaxSessionCount)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulSessionCount: "); + buffer.append((ulSessionCount == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulSessionCount)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulMaxRwSessionCount: "); + buffer.append((ulMaxRwSessionCount == PKCS11Constants.CK_EFFECTIVELY_INFINITE) + ? "CK_EFFECTIVELY_INFINITE" + : (ulMaxRwSessionCount == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulMaxRwSessionCount)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulRwSessionCount: "); + buffer.append((ulRwSessionCount == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulRwSessionCount)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulMaxPinLen: "); + buffer.append(String.valueOf(ulMaxPinLen)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulMinPinLen: "); + buffer.append(String.valueOf(ulMinPinLen)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulTotalPublicMemory: "); + buffer.append((ulTotalPublicMemory == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulTotalPublicMemory)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulFreePublicMemory: "); + buffer.append((ulFreePublicMemory == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulFreePublicMemory)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulTotalPrivateMemory: "); + buffer.append((ulTotalPrivateMemory == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulTotalPrivateMemory)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulFreePrivateMemory: "); + buffer.append((ulFreePrivateMemory == PKCS11Constants.CK_UNAVAILABLE_INFORMATION) + ? "CK_UNAVAILABLE_INFORMATION" + : String.valueOf(ulFreePrivateMemory)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hardwareVersion: "); + buffer.append(hardwareVersion.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("firmwareVersion: "); + buffer.append(firmwareVersion.toString()); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("utcTime: "); + buffer.append(new String(utcTime)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString() ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_UNLOCKMUTEX.java b/src/sun/security/pkcs11/wrapper/CK_UNLOCKMUTEX.java new file mode 100644 index 00000000..8c315299 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_UNLOCKMUTEX.java @@ -0,0 +1,68 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * interface CK_UNLOCKMUTEX

    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public interface CK_UNLOCKMUTEX { + + /** + * Method CK_UNLOCKMUTEX + * + * @param pMutex The mutex (lock) object to unlock. + * @exception PKCS11Exception + */ + public void CK_UNLOCKMUTEX(Object pMutex) throws PKCS11Exception; + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_VERSION.java b/src/sun/security/pkcs11/wrapper/CK_VERSION.java new file mode 100644 index 00000000..e47e29dc --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_VERSION.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_VERSION describes the version of a Cryptoki interface, a Cryptoki + * library, or an SSL implementation, or the hardware or firmware version of a + * slot or token.

    + * PKCS#11 structure: + *

    + * typedef struct CK_VERSION {  
    + *   CK_BYTE major;  
    + *   CK_BYTE minor;  
    + * } CK_VERSION;
    + * 
    + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class CK_VERSION { + + /** + * PKCS#11: + *
    +     *   CK_BYTE major;
    +     * 
    + */ + public byte major; /* integer portion of version number */ + + /** + * PKCS#11: + *
    +     *   CK_BYTE minor;
    +     * 
    + */ + public byte minor; /* 1/100ths portion of version number */ + + public CK_VERSION(int major, int minor) { + this.major = (byte)major; + this.minor = (byte)minor; + } + + /** + * Returns the string representation of CK_VERSION. + * + * @return the string representation of CK_VERSION + */ + public String toString() { + StringBuilder buffer = new StringBuilder(); + + buffer.append(major & 0xff); + buffer.append('.'); + int m = minor & 0xff; + if (m < 10) { + buffer.append('0'); + } + buffer.append(m); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java new file mode 100644 index 00000000..c31760b2 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_X9_42_DH1_DERIVE_PARAMS.java @@ -0,0 +1,132 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the + * CKM_X9_42_DH_DERIVE mechanism.

    + * PKCS#11 structure: + *

    + * typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
    + *   CK_X9_42_DH_KDF_TYPE kdf;
    + *   CK_ULONG ulOtherInfoLen;
    + *   CK_BYTE_PTR pOtherInfo;
    + *   CK_ULONG ulPublicDataLen;
    + *   CK_BYTE_PTR pPublicData;
    + * } CK_X9_42_DH1_DERIVE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + */ +public class CK_X9_42_DH1_DERIVE_PARAMS { + + /** + * PKCS#11: + *
    +    *   CK_X9_42_DH_KDF_TYPE kdf;
    +     * 
    + */ + public long kdf; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulOtherInfoLen;
    +     *   CK_BYTE_PTR pOtherInfo;
    +     * 
    + */ + public byte[] pOtherInfo; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen;
    +     *   CK_BYTE_PTR pPublicData;
    +     * 
    + */ + public byte[] pPublicData; + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("kdf: 0x"); + buffer.append(Functions.toFullHexString(kdf)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pOtherInfoLen: "); + buffer.append(pOtherInfo.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pOtherInfo: "); + buffer.append(Functions.toHexString(pOtherInfo)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen: "); + buffer.append(pPublicData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData: "); + buffer.append(Functions.toHexString(pPublicData)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/CK_X9_42_DH2_DERIVE_PARAMS.java b/src/sun/security/pkcs11/wrapper/CK_X9_42_DH2_DERIVE_PARAMS.java new file mode 100644 index 00000000..4e06d99c --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/CK_X9_42_DH2_DERIVE_PARAMS.java @@ -0,0 +1,181 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * class CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the + * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE mechanisms.

    + * PKCS#11 structure: + *

    + * typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
    + *   CK_X9_42_DH_KDF_TYPE kdf;
    + *   CK_ULONG ulOtherInfoLen;
    + *   CK_BYTE_PTR pOtherInfo;
    + *   CK_ULONG ulPublicDataLen;
    + *   CK_BYTE_PTR pPublicData;
    + *   CK_ULONG ulPrivateDataLen;
    + *   CK_OBJECT_HANDLE hPrivateData;
    + *   CK_ULONG ulPublicDataLen2;
    + *   CK_BYTE_PTR pPublicData2;
    + * } CK_X9_42_DH2_DERIVE_PARAMS;
    + * 
    + * + * @author Karl Scheibelhofer + */ +public class CK_X9_42_DH2_DERIVE_PARAMS { + + /** + * PKCS#11: + *
    +     *   CK_X9_42_DH_KDF_TYPE kdf;
    +     * 
    + */ + public long kdf; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulOtherInfoLen;
    +     *   CK_BYTE_PTR pOtherInfo;
    +     * 
    + */ + public byte[] pOtherInfo; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen;
    +     *   CK_BYTE_PTR pPublicData;
    +     * 
    + */ + public byte[] pPublicData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPrivateDataLen;
    +     * 
    + */ + public long ulPrivateDataLen; + + /** + * PKCS#11: + *
    +     *   CK_OBJECT_HANDLE hPrivateData;
    +     * 
    + */ + public long hPrivateData; + + /** + * PKCS#11: + *
    +     *   CK_ULONG ulPublicDataLen2;
    +     *   CK_BYTE_PTR pPublicData2;
    +     * 
    + */ + public byte[] pPublicData2; + + /** + * Returns the string representation of CK_PKCS5_PBKD2_PARAMS. + * + * @return the string representation of CK_PKCS5_PBKD2_PARAMS + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append(Constants.INDENT); + buffer.append("kdf: 0x"); + buffer.append(Functions.toFullHexString(kdf)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pOtherInfoLen: "); + buffer.append(pOtherInfo.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pOtherInfo: "); + buffer.append(Functions.toHexString(pOtherInfo)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen: "); + buffer.append(pPublicData.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData: "); + buffer.append(Functions.toHexString(pPublicData)); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("ulPrivateDataLen: "); + buffer.append(ulPrivateDataLen); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("hPrivateData: "); + buffer.append(hPrivateData); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicDataLen2: "); + buffer.append(pPublicData2.length); + buffer.append(Constants.NEWLINE); + + buffer.append(Constants.INDENT); + buffer.append("pPublicData2: "); + buffer.append(Functions.toHexString(pPublicData2)); + //buffer.append(Constants.NEWLINE); + + return buffer.toString(); + } + +} diff --git a/src/sun/security/pkcs11/wrapper/Constants.java b/src/sun/security/pkcs11/wrapper/Constants.java new file mode 100644 index 00000000..c12b8207 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/Constants.java @@ -0,0 +1,65 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + + +/** + * This class holds only final static member variables that are constants + * in this package. + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class Constants { + + public static final String NEWLINE = System.getProperty("line.separator"); + + public static final String INDENT = " "; + +} diff --git a/src/sun/security/pkcs11/wrapper/Functions.java b/src/sun/security/pkcs11/wrapper/Functions.java new file mode 100644 index 00000000..e661bcbb --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/Functions.java @@ -0,0 +1,1269 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +import java.math.BigInteger; + +import java.util.*; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_AC_ISSUER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ALWAYS_SENSITIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_APPLICATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ATTR_TYPES; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_AUTH_PIN_FLAGS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_BASE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CERTIFICATE_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_CLASS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_COEFFICIENT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_DECRYPT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EC_PARAMS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EC_POINT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ENCRYPT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_END_DATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EXPONENT_1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EXPONENT_2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_EXTRACTABLE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_HAS_RESET; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_HW_FEATURE_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ID; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_ISSUER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_KEY_GEN_MECHANISM; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_KEY_TYPE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_LABEL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_LOCAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_MODIFIABLE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_MODULUS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_MODULUS_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_CERT_MD5_HASH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_CERT_SHA1_HASH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_DB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_TRUST_CLIENT_AUTH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_TRUST_CODE_SIGNING; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_TRUST_EMAIL_PROTECTION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NETSCAPE_TRUST_SERVER_AUTH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_NEVER_EXTRACTABLE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_OBJECT_ID; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_OWNER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME_1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME_2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIME_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIVATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PRIVATE_EXPONENT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_PUBLIC_EXPONENT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_RESET_ON_INIT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SECONDARY_AUTH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SENSITIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SERIAL_NUMBER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SIGN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SIGN_RECOVER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_START_DATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SUBJECT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SUBPRIME; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_SUB_PRIME_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_TOKEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_TRUSTED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_UNWRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE_BITS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VALUE_LEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VENDOR_DEFINED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VERIFY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_VERIFY_RECOVER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKA_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_CLOCK_ON_TOKEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_DECRYPT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_DIGEST; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_DUAL_CRYPTO_OPERATIONS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_COMPRESS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_ECPARAMETERS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_F_2M; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_F_P; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_NAMEDCURVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EC_UNCOMPRESS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_ENCRYPT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_EXTENSION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_GENERATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_GENERATE_KEY_PAIR; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_HW; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_HW_SLOT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_LOGIN_REQUIRED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_PROTECTED_AUTHENTICATION_PATH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_REMOVABLE_DEVICE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_RESTORE_KEY_NOT_NEEDED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_RNG; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_RW_SESSION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SECONDARY_AUTHENTICATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SERIAL_SESSION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SIGN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SIGN_RECOVER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SO_PIN_COUNT_LOW; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SO_PIN_FINAL_TRY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SO_PIN_LOCKED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_SO_PIN_TO_BE_CHANGED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_TOKEN_INITIALIZED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_TOKEN_PRESENT; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_UNWRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_USER_PIN_COUNT_LOW; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_USER_PIN_FINAL_TRY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_USER_PIN_INITIALIZED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_USER_PIN_LOCKED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_USER_PIN_TO_BE_CHANGED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_VERIFY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_VERIFY_RECOVER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKF_WRITE_PROTECTED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_AES; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_BATON; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_BLOWFISH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_CAST; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_CAST128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_CAST3; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_CDMF; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DES3; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_DSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_EC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_GENERIC_SECRET; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_IDEA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_JUNIPER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_KEA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RC2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RC4; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RC5; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_RSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_SKIPJACK; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_VENDOR_DEFINED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKK_X9_42_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_AES_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_CBC128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_COUNTER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_ECB128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_ECB96; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_SHUFFLE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BATON_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BLOWFISH_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_BLOWFISH_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST128_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST3_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CAST_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CDMF_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CONCATENATE_BASE_AND_DATA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CONCATENATE_BASE_AND_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_CONCATENATE_DATA_AND_BASE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES2_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES3_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DES_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DH_PKCS_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DH_PKCS_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DH_PKCS_PARAMETER_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA_PARAMETER_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_DSA_SHA1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDH1_COFACTOR_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDH1_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDSA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECDSA_SHA1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_ECMQV_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_EC_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_EXTRACT_KEY_FROM_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_FASTHASH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_FORTEZZA_TIMESTAMP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_GENERIC_SECRET_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_IDEA_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_CBC128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_COUNTER; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_ECB128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_SHUFFLE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_JUNIPER_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_KEA_KEY_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_KEA_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_KEY_WRAP_LYNKS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_KEY_WRAP_SET_OAEP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD2_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_MD5_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_NSS_TLS_PRF_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBA_SHA1_WITH_SHA1_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_MD2_DES_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_MD5_CAST128_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_MD5_CAST3_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_MD5_CAST_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_MD5_DES_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_CAST128_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_DES2_EDE_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_DES3_EDE_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_RC2_128_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_RC2_40_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_RC4_128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PBE_SHA1_RC4_40; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_PKCS5_PBKD2; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC2_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC4; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC4_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_CBC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_CBC_PAD; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_ECB; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RC5_MAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD128; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD128_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD128_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD128_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD160; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD160_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD160_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RIPEMD160_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_9796; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_PKCS_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_PKCS_OAEP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_PKCS_PSS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_X9_31; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_X9_31_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_RSA_X_509; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA1_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA1_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA1_RSA_PKCS_PSS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA1_RSA_X9_31; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA224_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA256_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA384_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512_KEY_DERIVATION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA512_RSA_PKCS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA_1; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA_1_HMAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SHA_1_HMAC_GENERAL; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_CBC64; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_CFB16; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_CFB32; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_CFB64; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_CFB8; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_ECB64; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_OFB64; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_PRIVATE_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_RELAYX; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SKIPJACK_WRAP; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_KEY_AND_MAC_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_MASTER_KEY_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_MASTER_KEY_DERIVE_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_MD5_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_PRE_MASTER_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_SSL3_SHA1_MAC; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_TLS_KEY_AND_MAC_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_TLS_MASTER_KEY_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_TLS_MASTER_KEY_DERIVE_DH; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_TLS_PRE_MASTER_KEY_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_TLS_PRF; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_VENDOR_DEFINED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_X9_42_DH_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_X9_42_DH_HYBRID_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_X9_42_DH_KEY_PAIR_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_X9_42_DH_PARAMETER_GEN; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_X9_42_MQV_DERIVE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKM_XOR_BASE_AND_DATA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_CERTIFICATE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_DATA; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_DOMAIN_PARAMETERS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_HW_FEATURE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PRIVATE_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_PUBLIC_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_SECRET_KEY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKO_VENDOR_DEFINED; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKS_RO_PUBLIC_SESSION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKS_RO_USER_FUNCTIONS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKS_RW_PUBLIC_SESSION; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKS_RW_SO_FUNCTIONS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.CKS_RW_USER_FUNCTIONS; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKK_ANY; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKM_KEYSTORE; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKM_SECURERANDOM; +import static sun.security.pkcs11.wrapper.PKCS11Constants.PCKO_ANY; + +/** + * This class contains onyl static methods. It is the place for all functions + * that are used by several classes in this package. + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + */ +public class Functions { + + // maps between ids and their names, forward and reverse + // ids are stored as Integers to save space + // since only the lower 32 bits are ever used anyway + + // mechanisms (CKM_*) + private static final Map mechNames = + new HashMap(); + + private static final Map mechIds = + new HashMap(); + + // key types (CKK_*) + private static final Map keyNames = + new HashMap(); + + private static final Map keyIds = + new HashMap(); + + // attributes (CKA_*) + private static final Map attributeNames = + new HashMap(); + + private static final Map attributeIds = + new HashMap(); + + // object classes (CKO_*) + private static final Map objectClassNames = + new HashMap(); + + private static final Map objectClassIds = + new HashMap(); + + + /** + * For converting numbers to their hex presentation. + */ + private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + + /** + * Converts a long value to a hexadecimal String of length 16. Includes + * leading zeros if necessary. + * + * @param value The long value to be converted. + * @return The hexadecimal string representation of the long value. + */ + public static String toFullHexString(long value) { + long currentValue = value; + StringBuffer stringBuffer = new StringBuffer(16); + for(int j = 0; j < 16; j++) { + int currentDigit = (int) currentValue & 0xf; + stringBuffer.append(HEX_DIGITS[currentDigit]); + currentValue >>>= 4; + } + + return stringBuffer.reverse().toString(); + } + + /** + * Converts a int value to a hexadecimal String of length 8. Includes + * leading zeros if necessary. + * + * @param value The int value to be converted. + * @return The hexadecimal string representation of the int value. + */ + public static String toFullHexString(int value) { + int currentValue = value; + StringBuffer stringBuffer = new StringBuffer(8); + for(int i = 0; i < 8; i++) { + int currentDigit = currentValue & 0xf; + stringBuffer.append(HEX_DIGITS[currentDigit]); + currentValue >>>= 4; + } + + return stringBuffer.reverse().toString(); + } + + /** + * converts a long value to a hexadecimal String + * + * @param value the long value to be converted + * @return the hexadecimal string representation of the long value + */ + public static String toHexString(long value) { + return Long.toHexString(value); + } + + /** + * Converts a byte array to a hexadecimal String. Each byte is presented by + * its two digit hex-code; 0x0A -> "0a", 0x00 -> "00". No leading "0x" is + * included in the result. + * + * @param value the byte array to be converted + * @return the hexadecimal string representation of the byte array + */ + public static String toHexString(byte[] value) { + if (value == null) { + return null; + } + + StringBuffer buffer = new StringBuffer(2 * value.length); + int single; + + for (int i = 0; i < value.length; i++) { + single = value[i] & 0xFF; + + if (single < 0x10) { + buffer.append('0'); + } + + buffer.append(Integer.toString(single, 16)); + } + + return buffer.toString(); + } + + /** + * converts a long value to a binary String + * + * @param value the long value to be converted + * @return the binary string representation of the long value + */ + public static String toBinaryString(long value) { + return Long.toString(value, 2); + } + + /** + * converts a byte array to a binary String + * + * @param value the byte array to be converted + * @return the binary string representation of the byte array + */ + public static String toBinaryString(byte[] value) { + BigInteger helpBigInteger = new BigInteger(1, value); + + return helpBigInteger.toString(2); + } + + private static class Flags { + private final long[] flagIds; + private final String[] flagNames; + Flags(long[] flagIds, String[] flagNames) { + if (flagIds.length != flagNames.length) { + throw new AssertionError("Array lengths do not match"); + } + this.flagIds = flagIds; + this.flagNames = flagNames; + } + String toString(long val) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (int i = 0; i < flagIds.length; i++) { + if ((val & flagIds[i]) != 0) { + if (first == false) { + sb.append(" | "); + } + sb.append(flagNames[i]); + first = false; + } + } + return sb.toString(); + } + } + + private static final Flags slotInfoFlags = new Flags(new long[] { + CKF_TOKEN_PRESENT, + CKF_REMOVABLE_DEVICE, + CKF_HW_SLOT, + }, new String[] { + "CKF_TOKEN_PRESENT", + "CKF_REMOVABLE_DEVICE", + "CKF_HW_SLOT", + }); + + /** + * converts the long value flags to a SlotInfoFlag string + * + * @param flags the flags to be converted + * @return the SlotInfoFlag string representation of the flags + */ + public static String slotInfoFlagsToString(long flags) { + return slotInfoFlags.toString(flags); + } + + private static final Flags tokenInfoFlags = new Flags(new long[] { + CKF_RNG, + CKF_WRITE_PROTECTED, + CKF_LOGIN_REQUIRED, + CKF_USER_PIN_INITIALIZED, + CKF_RESTORE_KEY_NOT_NEEDED, + CKF_CLOCK_ON_TOKEN, + CKF_PROTECTED_AUTHENTICATION_PATH, + CKF_DUAL_CRYPTO_OPERATIONS, + CKF_TOKEN_INITIALIZED, + CKF_SECONDARY_AUTHENTICATION, + CKF_USER_PIN_COUNT_LOW, + CKF_USER_PIN_FINAL_TRY, + CKF_USER_PIN_LOCKED, + CKF_USER_PIN_TO_BE_CHANGED, + CKF_SO_PIN_COUNT_LOW, + CKF_SO_PIN_FINAL_TRY, + CKF_SO_PIN_LOCKED, + CKF_SO_PIN_TO_BE_CHANGED, + }, new String[] { + "CKF_RNG", + "CKF_WRITE_PROTECTED", + "CKF_LOGIN_REQUIRED", + "CKF_USER_PIN_INITIALIZED", + "CKF_RESTORE_KEY_NOT_NEEDED", + "CKF_CLOCK_ON_TOKEN", + "CKF_PROTECTED_AUTHENTICATION_PATH", + "CKF_DUAL_CRYPTO_OPERATIONS", + "CKF_TOKEN_INITIALIZED", + "CKF_SECONDARY_AUTHENTICATION", + "CKF_USER_PIN_COUNT_LOW", + "CKF_USER_PIN_FINAL_TRY", + "CKF_USER_PIN_LOCKED", + "CKF_USER_PIN_TO_BE_CHANGED", + "CKF_SO_PIN_COUNT_LOW", + "CKF_SO_PIN_FINAL_TRY", + "CKF_SO_PIN_LOCKED", + "CKF_SO_PIN_TO_BE_CHANGED", + }); + + /** + * converts long value flags to a TokenInfoFlag string + * + * @param flags the flags to be converted + * @return the TokenInfoFlag string representation of the flags + */ + public static String tokenInfoFlagsToString(long flags) { + return tokenInfoFlags.toString(flags); + } + + private static final Flags sessionInfoFlags = new Flags(new long[] { + CKF_RW_SESSION, + CKF_SERIAL_SESSION, + }, new String[] { + "CKF_RW_SESSION", + "CKF_SERIAL_SESSION", + }); + + /** + * converts the long value flags to a SessionInfoFlag string + * + * @param flags the flags to be converted + * @return the SessionInfoFlag string representation of the flags + */ + public static String sessionInfoFlagsToString(long flags) { + return sessionInfoFlags.toString(flags); + } + + /** + * converts the long value state to a SessionState string + * + * @param state the state to be converted + * @return the SessionState string representation of the state + */ + public static String sessionStateToString(long state) { + String name; + + if (state == CKS_RO_PUBLIC_SESSION) { + name = "CKS_RO_PUBLIC_SESSION"; + } else if (state == CKS_RO_USER_FUNCTIONS) { + name = "CKS_RO_USER_FUNCTIONS"; + } else if (state == CKS_RW_PUBLIC_SESSION) { + name = "CKS_RW_PUBLIC_SESSION"; + } else if (state == CKS_RW_USER_FUNCTIONS) { + name = "CKS_RW_USER_FUNCTIONS"; + } else if (state == CKS_RW_SO_FUNCTIONS) { + name = "CKS_RW_SO_FUNCTIONS"; + } else { + name = "ERROR: unknown session state 0x" + toFullHexString(state); + } + + return name; + } + + private static final Flags mechanismInfoFlags = new Flags(new long[] { + CKF_HW, + CKF_ENCRYPT, + CKF_DECRYPT, + CKF_DIGEST, + CKF_SIGN, + CKF_SIGN_RECOVER, + CKF_VERIFY, + CKF_VERIFY_RECOVER, + CKF_GENERATE, + CKF_GENERATE_KEY_PAIR, + CKF_WRAP, + CKF_UNWRAP, + CKF_DERIVE, + CKF_EC_F_P, + CKF_EC_F_2M, + CKF_EC_ECPARAMETERS, + CKF_EC_NAMEDCURVE, + CKF_EC_UNCOMPRESS, + CKF_EC_COMPRESS, + CKF_EXTENSION, + }, new String[] { + "CKF_HW", + "CKF_ENCRYPT", + "CKF_DECRYPT", + "CKF_DIGEST", + "CKF_SIGN", + "CKF_SIGN_RECOVER", + "CKF_VERIFY", + "CKF_VERIFY_RECOVER", + "CKF_GENERATE", + "CKF_GENERATE_KEY_PAIR", + "CKF_WRAP", + "CKF_UNWRAP", + "CKF_DERIVE", + "CKF_EC_F_P", + "CKF_EC_F_2M", + "CKF_EC_ECPARAMETERS", + "CKF_EC_NAMEDCURVE", + "CKF_EC_UNCOMPRESS", + "CKF_EC_COMPRESS", + "CKF_EXTENSION", + }); + + /** + * converts the long value flags to a MechanismInfoFlag string + * + * @param flags the flags to be converted + * @return the MechanismInfoFlag string representation of the flags + */ + public static String mechanismInfoFlagsToString(long flags) { + return mechanismInfoFlags.toString(flags); + } + + private static String getName(Map nameMap, long id) { + String name = null; + if ((id >>> 32) == 0) { + name = nameMap.get(Integer.valueOf((int)id)); + } + if (name == null) { + name = "Unknown 0x" + toFullHexString(id); + } + return name; + } + + public static long getId(Map idMap, String name) { + Integer mech = idMap.get(name); + if (mech == null) { + throw new IllegalArgumentException("Unknown name " + name); + } + return mech.intValue() & 0xffffffffL; + } + + public static String getMechanismName(long id) { + return getName(mechNames, id); + } + + public static long getMechanismId(String name) { + return getId(mechIds, name); + } + + public static String getKeyName(long id) { + return getName(keyNames, id); + } + + public static long getKeyId(String name) { + return getId(keyIds, name); + } + + public static String getAttributeName(long id) { + return getName(attributeNames, id); + } + + public static long getAttributeId(String name) { + return getId(attributeIds, name); + } + + public static String getObjectClassName(long id) { + return getName(objectClassNames, id); + } + + public static long getObjectClassId(String name) { + return getId(objectClassIds, name); + } + + /** + * Check the given arrays for equalitiy. This method considers both arrays as + * equal, if both are null or both have the same length and + * contain exactly the same byte values. + * + * @param array1 The first array. + * @param array2 The second array. + * @return True, if both arrays are null or both have the same + * length and contain exactly the same byte values. False, otherwise. + * @preconditions + * @postconditions + */ + public static boolean equals(byte[] array1, byte[] array2) { + return Arrays.equals(array1, array2); + } + + /** + * Check the given arrays for equalitiy. This method considers both arrays as + * equal, if both are null or both have the same length and + * contain exactly the same char values. + * + * @param array1 The first array. + * @param array2 The second array. + * @return True, if both arrays are null or both have the same + * length and contain exactly the same char values. False, otherwise. + * @preconditions + * @postconditions + */ + public static boolean equals(char[] array1, char[] array2) { + return Arrays.equals(array1, array2); + } + + /** + * Check the given dates for equalitiy. This method considers both dates as + * equal, if both are null or both contain exactly the same char + * values. + * + * @param date1 The first date. + * @param date2 The second date. + * @return True, if both dates are null or both contain the same + * char values. False, otherwise. + * @preconditions + * @postconditions + */ + public static boolean equals(CK_DATE date1, CK_DATE date2) { + boolean equal = false; + + if (date1 == date2) { + equal = true; + } else if ((date1 != null) && (date2 != null)) { + equal = equals(date1.year, date2.year) + && equals(date1.month, date2.month) + && equals(date1.day, date2.day); + } else { + equal = false; + } + + return equal ; + } + + /** + * Calculate a hash code for the given byte array. + * + * @param array The byte array. + * @return A hash code for the given array. + * @preconditions + * @postconditions + */ + public static int hashCode(byte[] array) { + int hash = 0; + + if (array != null) { + for (int i = 0; (i < 4) && (i < array.length); i++) { + hash ^= (0xFF & array[i]) << ((i%4) << 3); + } + } + + return hash ; + } + + /** + * Calculate a hash code for the given char array. + * + * @param array The char array. + * @return A hash code for the given array. + * @preconditions + * @postconditions + */ + public static int hashCode(char[] array) { + int hash = 0; + + if (array != null) { + for (int i = 0; (i < 4) && (i < array.length); i++) { + hash ^= (0xFFFF & array[i]) << ((i%2) << 4); + } + } + + return hash ; + } + + /** + * Calculate a hash code for the given date object. + * + * @param date The date object. + * @return A hash code for the given date. + * @preconditions + * @postconditions + */ + public static int hashCode(CK_DATE date) { + int hash = 0; + + if (date != null) { + if (date.year.length == 4) { + hash ^= (0xFFFF & date.year[0]) << 16; + hash ^= 0xFFFF & date.year[1]; + hash ^= (0xFFFF & date.year[2]) << 16; + hash ^= 0xFFFF & date.year[3]; + } + if (date.month.length == 2) { + hash ^= (0xFFFF & date.month[0]) << 16; + hash ^= 0xFFFF & date.month[1]; + } + if (date.day.length == 2) { + hash ^= (0xFFFF & date.day[0]) << 16; + hash ^= 0xFFFF & date.day[1]; + } + } + + return hash ; + } + + private static void addMapping(Map nameMap, + Map idMap, long id, String name) { + if ((id >>> 32) != 0) { + throw new AssertionError("Id has high bits set: " + id + ", " + name); + } + Integer intId = Integer.valueOf((int)id); + if (nameMap.put(intId, name) != null) { + throw new AssertionError("Duplicate id: " + id + ", " + name); + } + if (idMap.put(name, intId) != null) { + throw new AssertionError("Duplicate name: " + id + ", " + name); + } + } + + private static void addMech(long id, String name) { + addMapping(mechNames, mechIds, id, name); + } + + private static void addKeyType(long id, String name) { + addMapping(keyNames, keyIds, id, name); + } + + private static void addAttribute(long id, String name) { + addMapping(attributeNames, attributeIds, id, name); + } + + private static void addObjectClass(long id, String name) { + addMapping(objectClassNames, objectClassIds, id, name); + } + + static { + addMech(CKM_RSA_PKCS_KEY_PAIR_GEN, "CKM_RSA_PKCS_KEY_PAIR_GEN"); + addMech(CKM_RSA_PKCS, "CKM_RSA_PKCS"); + addMech(CKM_RSA_9796, "CKM_RSA_9796"); + addMech(CKM_RSA_X_509, "CKM_RSA_X_509"); + addMech(CKM_MD2_RSA_PKCS, "CKM_MD2_RSA_PKCS"); + addMech(CKM_MD5_RSA_PKCS, "CKM_MD5_RSA_PKCS"); + addMech(CKM_SHA1_RSA_PKCS, "CKM_SHA1_RSA_PKCS"); + addMech(CKM_RIPEMD128_RSA_PKCS, "CKM_RIPEMD128_RSA_PKCS"); + addMech(CKM_RIPEMD160_RSA_PKCS, "CKM_RIPEMD160_RSA_PKCS"); + addMech(CKM_RSA_PKCS_OAEP, "CKM_RSA_PKCS_OAEP"); + addMech(CKM_RSA_X9_31_KEY_PAIR_GEN, "CKM_RSA_X9_31_KEY_PAIR_GEN"); + addMech(CKM_RSA_X9_31, "CKM_RSA_X9_31"); + addMech(CKM_SHA1_RSA_X9_31, "CKM_SHA1_RSA_X9_31"); + addMech(CKM_RSA_PKCS_PSS, "CKM_RSA_PKCS_PSS"); + addMech(CKM_SHA1_RSA_PKCS_PSS, "CKM_SHA1_RSA_PKCS_PSS"); + addMech(CKM_DSA_KEY_PAIR_GEN, "CKM_DSA_KEY_PAIR_GEN"); + addMech(CKM_DSA, "CKM_DSA"); + addMech(CKM_DSA_SHA1, "CKM_DSA_SHA1"); + addMech(CKM_DH_PKCS_KEY_PAIR_GEN, "CKM_DH_PKCS_KEY_PAIR_GEN"); + addMech(CKM_DH_PKCS_DERIVE, "CKM_DH_PKCS_DERIVE"); + addMech(CKM_X9_42_DH_KEY_PAIR_GEN, "CKM_X9_42_DH_KEY_PAIR_GEN"); + addMech(CKM_X9_42_DH_DERIVE, "CKM_X9_42_DH_DERIVE"); + addMech(CKM_X9_42_DH_HYBRID_DERIVE, "CKM_X9_42_DH_HYBRID_DERIVE"); + addMech(CKM_X9_42_MQV_DERIVE, "CKM_X9_42_MQV_DERIVE"); + addMech(CKM_SHA224_RSA_PKCS, "CKM_SHA224_RSA_PKCS"); + addMech(CKM_SHA256_RSA_PKCS, "CKM_SHA256_RSA_PKCS"); + addMech(CKM_SHA384_RSA_PKCS, "CKM_SHA384_RSA_PKCS"); + addMech(CKM_SHA512_RSA_PKCS, "CKM_SHA512_RSA_PKCS"); + addMech(CKM_RC2_KEY_GEN, "CKM_RC2_KEY_GEN"); + addMech(CKM_RC2_ECB, "CKM_RC2_ECB"); + addMech(CKM_RC2_CBC, "CKM_RC2_CBC"); + addMech(CKM_RC2_MAC, "CKM_RC2_MAC"); + addMech(CKM_RC2_MAC_GENERAL, "CKM_RC2_MAC_GENERAL"); + addMech(CKM_RC2_CBC_PAD, "CKM_RC2_CBC_PAD"); + addMech(CKM_RC4_KEY_GEN, "CKM_RC4_KEY_GEN"); + addMech(CKM_RC4, "CKM_RC4"); + addMech(CKM_DES_KEY_GEN, "CKM_DES_KEY_GEN"); + addMech(CKM_DES_ECB, "CKM_DES_ECB"); + addMech(CKM_DES_CBC, "CKM_DES_CBC"); + addMech(CKM_DES_MAC, "CKM_DES_MAC"); + addMech(CKM_DES_MAC_GENERAL, "CKM_DES_MAC_GENERAL"); + addMech(CKM_DES_CBC_PAD, "CKM_DES_CBC_PAD"); + addMech(CKM_DES2_KEY_GEN, "CKM_DES2_KEY_GEN"); + addMech(CKM_DES3_KEY_GEN, "CKM_DES3_KEY_GEN"); + addMech(CKM_DES3_ECB, "CKM_DES3_ECB"); + addMech(CKM_DES3_CBC, "CKM_DES3_CBC"); + addMech(CKM_DES3_MAC, "CKM_DES3_MAC"); + addMech(CKM_DES3_MAC_GENERAL, "CKM_DES3_MAC_GENERAL"); + addMech(CKM_DES3_CBC_PAD, "CKM_DES3_CBC_PAD"); + addMech(CKM_CDMF_KEY_GEN, "CKM_CDMF_KEY_GEN"); + addMech(CKM_CDMF_ECB, "CKM_CDMF_ECB"); + addMech(CKM_CDMF_CBC, "CKM_CDMF_CBC"); + addMech(CKM_CDMF_MAC, "CKM_CDMF_MAC"); + addMech(CKM_CDMF_MAC_GENERAL, "CKM_CDMF_MAC_GENERAL"); + addMech(CKM_CDMF_CBC_PAD, "CKM_CDMF_CBC_PAD"); + addMech(CKM_MD2, "CKM_MD2"); + addMech(CKM_MD2_HMAC, "CKM_MD2_HMAC"); + addMech(CKM_MD2_HMAC_GENERAL, "CKM_MD2_HMAC_GENERAL"); + addMech(CKM_MD5, "CKM_MD5"); + addMech(CKM_MD5_HMAC, "CKM_MD5_HMAC"); + addMech(CKM_MD5_HMAC_GENERAL, "CKM_MD5_HMAC_GENERAL"); + addMech(CKM_SHA_1, "CKM_SHA_1"); + addMech(CKM_SHA_1_HMAC, "CKM_SHA_1_HMAC"); + addMech(CKM_SHA_1_HMAC_GENERAL, "CKM_SHA_1_HMAC_GENERAL"); + addMech(CKM_RIPEMD128, "CKM_RIPEMD128"); + addMech(CKM_RIPEMD128_HMAC, "CKM_RIPEMD128_HMAC"); + addMech(CKM_RIPEMD128_HMAC_GENERAL, "CKM_RIPEMD128_HMAC_GENERAL"); + addMech(CKM_RIPEMD160, "CKM_RIPEMD160"); + addMech(CKM_RIPEMD160_HMAC, "CKM_RIPEMD160_HMAC"); + addMech(CKM_RIPEMD160_HMAC_GENERAL, "CKM_RIPEMD160_HMAC_GENERAL"); + addMech(CKM_SHA224, "CKM_SHA224"); + addMech(CKM_SHA224_HMAC, "CKM_SHA224_HMAC"); + addMech(CKM_SHA224_HMAC_GENERAL, "CKM_SHA224_HMAC_GENERAL"); + addMech(CKM_SHA256, "CKM_SHA256"); + addMech(CKM_SHA256_HMAC, "CKM_SHA256_HMAC"); + addMech(CKM_SHA256_HMAC_GENERAL, "CKM_SHA256_HMAC_GENERAL"); + addMech(CKM_SHA384, "CKM_SHA384"); + addMech(CKM_SHA384_HMAC, "CKM_SHA384_HMAC"); + addMech(CKM_SHA384_HMAC_GENERAL, "CKM_SHA384_HMAC_GENERAL"); + addMech(CKM_SHA512, "CKM_SHA512"); + addMech(CKM_SHA512_HMAC, "CKM_SHA512_HMAC"); + addMech(CKM_SHA512_HMAC_GENERAL, "CKM_SHA512_HMAC_GENERAL"); + addMech(CKM_CAST_KEY_GEN, "CKM_CAST_KEY_GEN"); + addMech(CKM_CAST_ECB, "CKM_CAST_ECB"); + addMech(CKM_CAST_CBC, "CKM_CAST_CBC"); + addMech(CKM_CAST_MAC, "CKM_CAST_MAC"); + addMech(CKM_CAST_MAC_GENERAL, "CKM_CAST_MAC_GENERAL"); + addMech(CKM_CAST_CBC_PAD, "CKM_CAST_CBC_PAD"); + addMech(CKM_CAST3_KEY_GEN, "CKM_CAST3_KEY_GEN"); + addMech(CKM_CAST3_ECB, "CKM_CAST3_ECB"); + addMech(CKM_CAST3_CBC, "CKM_CAST3_CBC"); + addMech(CKM_CAST3_MAC, "CKM_CAST3_MAC"); + addMech(CKM_CAST3_MAC_GENERAL, "CKM_CAST3_MAC_GENERAL"); + addMech(CKM_CAST3_CBC_PAD, "CKM_CAST3_CBC_PAD"); + addMech(CKM_CAST128_KEY_GEN, "CKM_CAST128_KEY_GEN"); + addMech(CKM_CAST128_ECB, "CKM_CAST128_ECB"); + addMech(CKM_CAST128_CBC, "CKM_CAST128_CBC"); + addMech(CKM_CAST128_MAC, "CKM_CAST128_MAC"); + addMech(CKM_CAST128_MAC_GENERAL, "CKM_CAST128_MAC_GENERAL"); + addMech(CKM_CAST128_CBC_PAD, "CKM_CAST128_CBC_PAD"); + addMech(CKM_RC5_KEY_GEN, "CKM_RC5_KEY_GEN"); + addMech(CKM_RC5_ECB, "CKM_RC5_ECB"); + addMech(CKM_RC5_CBC, "CKM_RC5_CBC"); + addMech(CKM_RC5_MAC, "CKM_RC5_MAC"); + addMech(CKM_RC5_MAC_GENERAL, "CKM_RC5_MAC_GENERAL"); + addMech(CKM_RC5_CBC_PAD, "CKM_RC5_CBC_PAD"); + addMech(CKM_IDEA_KEY_GEN, "CKM_IDEA_KEY_GEN"); + addMech(CKM_IDEA_ECB, "CKM_IDEA_ECB"); + addMech(CKM_IDEA_CBC, "CKM_IDEA_CBC"); + addMech(CKM_IDEA_MAC, "CKM_IDEA_MAC"); + addMech(CKM_IDEA_MAC_GENERAL, "CKM_IDEA_MAC_GENERAL"); + addMech(CKM_IDEA_CBC_PAD, "CKM_IDEA_CBC_PAD"); + addMech(CKM_GENERIC_SECRET_KEY_GEN, "CKM_GENERIC_SECRET_KEY_GEN"); + addMech(CKM_CONCATENATE_BASE_AND_KEY, "CKM_CONCATENATE_BASE_AND_KEY"); + addMech(CKM_CONCATENATE_BASE_AND_DATA, "CKM_CONCATENATE_BASE_AND_DATA"); + addMech(CKM_CONCATENATE_DATA_AND_BASE, "CKM_CONCATENATE_DATA_AND_BASE"); + addMech(CKM_XOR_BASE_AND_DATA, "CKM_XOR_BASE_AND_DATA"); + addMech(CKM_EXTRACT_KEY_FROM_KEY, "CKM_EXTRACT_KEY_FROM_KEY"); + addMech(CKM_SSL3_PRE_MASTER_KEY_GEN, "CKM_SSL3_PRE_MASTER_KEY_GEN"); + addMech(CKM_SSL3_MASTER_KEY_DERIVE, "CKM_SSL3_MASTER_KEY_DERIVE"); + addMech(CKM_SSL3_KEY_AND_MAC_DERIVE, "CKM_SSL3_KEY_AND_MAC_DERIVE"); + addMech(CKM_SSL3_MASTER_KEY_DERIVE_DH, "CKM_SSL3_MASTER_KEY_DERIVE_DH"); + addMech(CKM_TLS_PRE_MASTER_KEY_GEN, "CKM_TLS_PRE_MASTER_KEY_GEN"); + addMech(CKM_TLS_MASTER_KEY_DERIVE, "CKM_TLS_MASTER_KEY_DERIVE"); + addMech(CKM_TLS_KEY_AND_MAC_DERIVE, "CKM_TLS_KEY_AND_MAC_DERIVE"); + addMech(CKM_TLS_MASTER_KEY_DERIVE_DH, "CKM_TLS_MASTER_KEY_DERIVE_DH"); + addMech(CKM_TLS_PRF, "CKM_TLS_PRF"); + addMech(CKM_SSL3_MD5_MAC, "CKM_SSL3_MD5_MAC"); + addMech(CKM_SSL3_SHA1_MAC, "CKM_SSL3_SHA1_MAC"); + addMech(CKM_MD5_KEY_DERIVATION, "CKM_MD5_KEY_DERIVATION"); + addMech(CKM_MD2_KEY_DERIVATION, "CKM_MD2_KEY_DERIVATION"); + addMech(CKM_SHA1_KEY_DERIVATION, "CKM_SHA1_KEY_DERIVATION"); + addMech(CKM_SHA224_KEY_DERIVATION, "CKM_SHA224_KEY_DERIVATION"); + addMech(CKM_SHA256_KEY_DERIVATION, "CKM_SHA256_KEY_DERIVATION"); + addMech(CKM_SHA384_KEY_DERIVATION, "CKM_SHA384_KEY_DERIVATION"); + addMech(CKM_SHA512_KEY_DERIVATION, "CKM_SHA512_KEY_DERIVATION"); + addMech(CKM_PBE_MD2_DES_CBC, "CKM_PBE_MD2_DES_CBC"); + addMech(CKM_PBE_MD5_DES_CBC, "CKM_PBE_MD5_DES_CBC"); + addMech(CKM_PBE_MD5_CAST_CBC, "CKM_PBE_MD5_CAST_CBC"); + addMech(CKM_PBE_MD5_CAST3_CBC, "CKM_PBE_MD5_CAST3_CBC"); + addMech(CKM_PBE_MD5_CAST128_CBC, "CKM_PBE_MD5_CAST128_CBC"); + addMech(CKM_PBE_SHA1_CAST128_CBC, "CKM_PBE_SHA1_CAST128_CBC"); + addMech(CKM_PBE_SHA1_RC4_128, "CKM_PBE_SHA1_RC4_128"); + addMech(CKM_PBE_SHA1_RC4_40, "CKM_PBE_SHA1_RC4_40"); + addMech(CKM_PBE_SHA1_DES3_EDE_CBC, "CKM_PBE_SHA1_DES3_EDE_CBC"); + addMech(CKM_PBE_SHA1_DES2_EDE_CBC, "CKM_PBE_SHA1_DES2_EDE_CBC"); + addMech(CKM_PBE_SHA1_RC2_128_CBC, "CKM_PBE_SHA1_RC2_128_CBC"); + addMech(CKM_PBE_SHA1_RC2_40_CBC, "CKM_PBE_SHA1_RC2_40_CBC"); + addMech(CKM_PKCS5_PBKD2, "CKM_PKCS5_PBKD2"); + addMech(CKM_PBA_SHA1_WITH_SHA1_HMAC, "CKM_PBA_SHA1_WITH_SHA1_HMAC"); + addMech(CKM_KEY_WRAP_LYNKS, "CKM_KEY_WRAP_LYNKS"); + addMech(CKM_KEY_WRAP_SET_OAEP, "CKM_KEY_WRAP_SET_OAEP"); + addMech(CKM_SKIPJACK_KEY_GEN, "CKM_SKIPJACK_KEY_GEN"); + addMech(CKM_SKIPJACK_ECB64, "CKM_SKIPJACK_ECB64"); + addMech(CKM_SKIPJACK_CBC64, "CKM_SKIPJACK_CBC64"); + addMech(CKM_SKIPJACK_OFB64, "CKM_SKIPJACK_OFB64"); + addMech(CKM_SKIPJACK_CFB64, "CKM_SKIPJACK_CFB64"); + addMech(CKM_SKIPJACK_CFB32, "CKM_SKIPJACK_CFB32"); + addMech(CKM_SKIPJACK_CFB16, "CKM_SKIPJACK_CFB16"); + addMech(CKM_SKIPJACK_CFB8, "CKM_SKIPJACK_CFB8"); + addMech(CKM_SKIPJACK_WRAP, "CKM_SKIPJACK_WRAP"); + addMech(CKM_SKIPJACK_PRIVATE_WRAP, "CKM_SKIPJACK_PRIVATE_WRAP"); + addMech(CKM_SKIPJACK_RELAYX, "CKM_SKIPJACK_RELAYX"); + addMech(CKM_KEA_KEY_PAIR_GEN, "CKM_KEA_KEY_PAIR_GEN"); + addMech(CKM_KEA_KEY_DERIVE, "CKM_KEA_KEY_DERIVE"); + addMech(CKM_FORTEZZA_TIMESTAMP, "CKM_FORTEZZA_TIMESTAMP"); + addMech(CKM_BATON_KEY_GEN, "CKM_BATON_KEY_GEN"); + addMech(CKM_BATON_ECB128, "CKM_BATON_ECB128"); + addMech(CKM_BATON_ECB96, "CKM_BATON_ECB96"); + addMech(CKM_BATON_CBC128, "CKM_BATON_CBC128"); + addMech(CKM_BATON_COUNTER, "CKM_BATON_COUNTER"); + addMech(CKM_BATON_SHUFFLE, "CKM_BATON_SHUFFLE"); + addMech(CKM_BATON_WRAP, "CKM_BATON_WRAP"); + addMech(CKM_EC_KEY_PAIR_GEN, "CKM_EC_KEY_PAIR_GEN"); + addMech(CKM_ECDSA, "CKM_ECDSA"); + addMech(CKM_ECDSA_SHA1, "CKM_ECDSA_SHA1"); + addMech(CKM_ECDH1_DERIVE, "CKM_ECDH1_DERIVE"); + addMech(CKM_ECDH1_COFACTOR_DERIVE, "CKM_ECDH1_COFACTOR_DERIVE"); + addMech(CKM_ECMQV_DERIVE, "CKM_ECMQV_DERIVE"); + addMech(CKM_JUNIPER_KEY_GEN, "CKM_JUNIPER_KEY_GEN"); + addMech(CKM_JUNIPER_ECB128, "CKM_JUNIPER_ECB128"); + addMech(CKM_JUNIPER_CBC128, "CKM_JUNIPER_CBC128"); + addMech(CKM_JUNIPER_COUNTER, "CKM_JUNIPER_COUNTER"); + addMech(CKM_JUNIPER_SHUFFLE, "CKM_JUNIPER_SHUFFLE"); + addMech(CKM_JUNIPER_WRAP, "CKM_JUNIPER_WRAP"); + addMech(CKM_FASTHASH, "CKM_FASTHASH"); + addMech(CKM_AES_KEY_GEN, "CKM_AES_KEY_GEN"); + addMech(CKM_AES_ECB, "CKM_AES_ECB"); + addMech(CKM_AES_CBC, "CKM_AES_CBC"); + addMech(CKM_AES_MAC, "CKM_AES_MAC"); + addMech(CKM_AES_MAC_GENERAL, "CKM_AES_MAC_GENERAL"); + addMech(CKM_AES_CBC_PAD, "CKM_AES_CBC_PAD"); + addMech(CKM_BLOWFISH_KEY_GEN, "CKM_BLOWFISH_KEY_GEN"); + addMech(CKM_BLOWFISH_CBC, "CKM_BLOWFISH_CBC"); + addMech(CKM_DSA_PARAMETER_GEN, "CKM_DSA_PARAMETER_GEN"); + addMech(CKM_DH_PKCS_PARAMETER_GEN, "CKM_DH_PKCS_PARAMETER_GEN"); + addMech(CKM_X9_42_DH_PARAMETER_GEN, "CKM_X9_42_DH_PARAMETER_GEN"); + addMech(CKM_VENDOR_DEFINED, "CKM_VENDOR_DEFINED"); + + addMech(CKM_NSS_TLS_PRF_GENERAL, "CKM_NSS_TLS_PRF_GENERAL"); + + addMech(PCKM_SECURERANDOM, "SecureRandom"); + addMech(PCKM_KEYSTORE, "KeyStore"); + + addKeyType(CKK_RSA, "CKK_RSA"); + addKeyType(CKK_DSA, "CKK_DSA"); + addKeyType(CKK_DH, "CKK_DH"); + addKeyType(CKK_EC, "CKK_EC"); + addKeyType(CKK_X9_42_DH, "CKK_X9_42_DH"); + addKeyType(CKK_KEA, "CKK_KEA"); + addKeyType(CKK_GENERIC_SECRET, "CKK_GENERIC_SECRET"); + addKeyType(CKK_RC2, "CKK_RC2"); + addKeyType(CKK_RC4, "CKK_RC4"); + addKeyType(CKK_DES, "CKK_DES"); + addKeyType(CKK_DES2, "CKK_DES2"); + addKeyType(CKK_DES3, "CKK_DES3"); + addKeyType(CKK_CAST, "CKK_CAST"); + addKeyType(CKK_CAST3, "CKK_CAST3"); + addKeyType(CKK_CAST128, "CKK_CAST128"); + addKeyType(CKK_RC5, "CKK_RC5"); + addKeyType(CKK_IDEA, "CKK_IDEA"); + addKeyType(CKK_SKIPJACK, "CKK_SKIPJACK"); + addKeyType(CKK_BATON, "CKK_BATON"); + addKeyType(CKK_JUNIPER, "CKK_JUNIPER"); + addKeyType(CKK_CDMF, "CKK_CDMF"); + addKeyType(CKK_AES, "CKK_AES"); + addKeyType(CKK_BLOWFISH, "CKK_BLOWFISH"); + addKeyType(CKK_VENDOR_DEFINED, "CKK_VENDOR_DEFINED"); + + addKeyType(PCKK_ANY, "*"); + + addAttribute(CKA_CLASS, "CKA_CLASS"); + addAttribute(CKA_TOKEN, "CKA_TOKEN"); + addAttribute(CKA_PRIVATE, "CKA_PRIVATE"); + addAttribute(CKA_LABEL, "CKA_LABEL"); + addAttribute(CKA_APPLICATION, "CKA_APPLICATION"); + addAttribute(CKA_VALUE, "CKA_VALUE"); + addAttribute(CKA_OBJECT_ID, "CKA_OBJECT_ID"); + addAttribute(CKA_CERTIFICATE_TYPE, "CKA_CERTIFICATE_TYPE"); + addAttribute(CKA_ISSUER, "CKA_ISSUER"); + addAttribute(CKA_SERIAL_NUMBER, "CKA_SERIAL_NUMBER"); + addAttribute(CKA_AC_ISSUER, "CKA_AC_ISSUER"); + addAttribute(CKA_OWNER, "CKA_OWNER"); + addAttribute(CKA_ATTR_TYPES, "CKA_ATTR_TYPES"); + addAttribute(CKA_TRUSTED, "CKA_TRUSTED"); + addAttribute(CKA_KEY_TYPE, "CKA_KEY_TYPE"); + addAttribute(CKA_SUBJECT, "CKA_SUBJECT"); + addAttribute(CKA_ID, "CKA_ID"); + addAttribute(CKA_SENSITIVE, "CKA_SENSITIVE"); + addAttribute(CKA_ENCRYPT, "CKA_ENCRYPT"); + addAttribute(CKA_DECRYPT, "CKA_DECRYPT"); + addAttribute(CKA_WRAP, "CKA_WRAP"); + addAttribute(CKA_UNWRAP, "CKA_UNWRAP"); + addAttribute(CKA_SIGN, "CKA_SIGN"); + addAttribute(CKA_SIGN_RECOVER, "CKA_SIGN_RECOVER"); + addAttribute(CKA_VERIFY, "CKA_VERIFY"); + addAttribute(CKA_VERIFY_RECOVER, "CKA_VERIFY_RECOVER"); + addAttribute(CKA_DERIVE, "CKA_DERIVE"); + addAttribute(CKA_START_DATE, "CKA_START_DATE"); + addAttribute(CKA_END_DATE, "CKA_END_DATE"); + addAttribute(CKA_MODULUS, "CKA_MODULUS"); + addAttribute(CKA_MODULUS_BITS, "CKA_MODULUS_BITS"); + addAttribute(CKA_PUBLIC_EXPONENT, "CKA_PUBLIC_EXPONENT"); + addAttribute(CKA_PRIVATE_EXPONENT, "CKA_PRIVATE_EXPONENT"); + addAttribute(CKA_PRIME_1, "CKA_PRIME_1"); + addAttribute(CKA_PRIME_2, "CKA_PRIME_2"); + addAttribute(CKA_EXPONENT_1, "CKA_EXPONENT_1"); + addAttribute(CKA_EXPONENT_2, "CKA_EXPONENT_2"); + addAttribute(CKA_COEFFICIENT, "CKA_COEFFICIENT"); + addAttribute(CKA_PRIME, "CKA_PRIME"); + addAttribute(CKA_SUBPRIME, "CKA_SUBPRIME"); + addAttribute(CKA_BASE, "CKA_BASE"); + addAttribute(CKA_PRIME_BITS, "CKA_PRIME_BITS"); + addAttribute(CKA_SUB_PRIME_BITS, "CKA_SUB_PRIME_BITS"); + addAttribute(CKA_VALUE_BITS, "CKA_VALUE_BITS"); + addAttribute(CKA_VALUE_LEN, "CKA_VALUE_LEN"); + addAttribute(CKA_EXTRACTABLE, "CKA_EXTRACTABLE"); + addAttribute(CKA_LOCAL, "CKA_LOCAL"); + addAttribute(CKA_NEVER_EXTRACTABLE, "CKA_NEVER_EXTRACTABLE"); + addAttribute(CKA_ALWAYS_SENSITIVE, "CKA_ALWAYS_SENSITIVE"); + addAttribute(CKA_KEY_GEN_MECHANISM, "CKA_KEY_GEN_MECHANISM"); + addAttribute(CKA_MODIFIABLE, "CKA_MODIFIABLE"); + addAttribute(CKA_EC_PARAMS, "CKA_EC_PARAMS"); + addAttribute(CKA_EC_POINT, "CKA_EC_POINT"); + addAttribute(CKA_SECONDARY_AUTH, "CKA_SECONDARY_AUTH"); + addAttribute(CKA_AUTH_PIN_FLAGS, "CKA_AUTH_PIN_FLAGS"); + addAttribute(CKA_HW_FEATURE_TYPE, "CKA_HW_FEATURE_TYPE"); + addAttribute(CKA_RESET_ON_INIT, "CKA_RESET_ON_INIT"); + addAttribute(CKA_HAS_RESET, "CKA_HAS_RESET"); + addAttribute(CKA_VENDOR_DEFINED, "CKA_VENDOR_DEFINED"); + addAttribute(CKA_NETSCAPE_DB, "CKA_NETSCAPE_DB"); + + addAttribute(CKA_NETSCAPE_TRUST_SERVER_AUTH, "CKA_NETSCAPE_TRUST_SERVER_AUTH"); + addAttribute(CKA_NETSCAPE_TRUST_CLIENT_AUTH, "CKA_NETSCAPE_TRUST_CLIENT_AUTH"); + addAttribute(CKA_NETSCAPE_TRUST_CODE_SIGNING, "CKA_NETSCAPE_TRUST_CODE_SIGNING"); + addAttribute(CKA_NETSCAPE_TRUST_EMAIL_PROTECTION, "CKA_NETSCAPE_TRUST_EMAIL_PROTECTION"); + addAttribute(CKA_NETSCAPE_CERT_SHA1_HASH, "CKA_NETSCAPE_CERT_SHA1_HASH"); + addAttribute(CKA_NETSCAPE_CERT_MD5_HASH, "CKA_NETSCAPE_CERT_MD5_HASH"); + + addObjectClass(CKO_DATA, "CKO_DATA"); + addObjectClass(CKO_CERTIFICATE, "CKO_CERTIFICATE"); + addObjectClass(CKO_PUBLIC_KEY, "CKO_PUBLIC_KEY"); + addObjectClass(CKO_PRIVATE_KEY, "CKO_PRIVATE_KEY"); + addObjectClass(CKO_SECRET_KEY, "CKO_SECRET_KEY"); + addObjectClass(CKO_HW_FEATURE, "CKO_HW_FEATURE"); + addObjectClass(CKO_DOMAIN_PARAMETERS, "CKO_DOMAIN_PARAMETERS"); + addObjectClass(CKO_VENDOR_DEFINED, "CKO_VENDOR_DEFINED"); + + addObjectClass(PCKO_ANY, "*"); + + } + +} diff --git a/src/sun/security/pkcs11/wrapper/PKCS11.java b/src/sun/security/pkcs11/wrapper/PKCS11.java new file mode 100644 index 00000000..408a0fdc --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/PKCS11.java @@ -0,0 +1,1823 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +import java.io.IOException; +import java.util.*; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import static sun.security.pkcs11.wrapper.PKCS11Constants.*; + +/** + * This is the default implementation of the PKCS11 interface. IT connects to + * the pkcs11wrapper.dll file, which is the native part of this library. + * The strange and awkward looking initialization was chosen to avoid calling + * loadLibrary from a static initialization block, because this would complicate + * the use in applets. + * + * @author Karl Scheibelhofer + * @author Martin Schlaeffer + * @invariants (pkcs11ModulePath_ <> null) + */ +public class PKCS11 { + + /** + * The name of the native part of the wrapper; i.e. the filename without + * the extension (e.g. ".DLL" or ".so"). + */ + private static final String PKCS11_WRAPPER = "j2pkcs11"; + + static { + // cannot use LoadLibraryAction because that would make the native + // library available to the bootclassloader, but we run in the + // extension classloader. + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + System.loadLibrary(PKCS11_WRAPPER); + return null; + } + }); + initializeLibrary(); + } + + public static void loadNative() { + // dummy method that can be called to make sure the native + // portion has been loaded. actual loading happens in the + // static initializer, hence this method is empty. + } + + /** + * The PKCS#11 module to connect to. This is the PKCS#11 driver of the token; + * e.g. pk2priv.dll. + */ + private final String pkcs11ModulePath; + + private long pNativeData; + + /** + * This method does the initialization of the native library. It is called + * exactly once for this class. + * + * @preconditions + * @postconditions + */ + private static native void initializeLibrary(); + + // XXX + /** + * This method does the finalization of the native library. It is called + * exactly once for this class. The library uses this method for a clean-up + * of any resources. + * + * @preconditions + * @postconditions + */ + private static native void finalizeLibrary(); + + private static final Map moduleMap = + new HashMap(); + + /** + * Connects to the PKCS#11 driver given. The filename must contain the + * path, if the driver is not in the system's search path. + * + * @param pkcs11ModulePath the PKCS#11 library path + * @preconditions (pkcs11ModulePath <> null) + * @postconditions + */ + PKCS11(String pkcs11ModulePath, String functionListName) + throws IOException { + connect(pkcs11ModulePath, functionListName); + this.pkcs11ModulePath = pkcs11ModulePath; + } + + public static synchronized PKCS11 getInstance(String pkcs11ModulePath, + String functionList, CK_C_INITIALIZE_ARGS pInitArgs, + boolean omitInitialize) throws IOException, PKCS11Exception { + // we may only call C_Initialize once per native .so/.dll + // so keep a cache using the (non-canonicalized!) path + PKCS11 pkcs11 = moduleMap.get(pkcs11ModulePath); + if (pkcs11 == null) { + if ((pInitArgs != null) + && ((pInitArgs.flags & CKF_OS_LOCKING_OK) != 0)) { + pkcs11 = new PKCS11(pkcs11ModulePath, functionList); + } else { + pkcs11 = new SynchronizedPKCS11(pkcs11ModulePath, functionList); + } + if (omitInitialize == false) { + try { + pkcs11.C_Initialize(pInitArgs); + } catch (PKCS11Exception e) { + // ignore already-initialized error code + // rethrow all other errors + if (e.getErrorCode() != CKR_CRYPTOKI_ALREADY_INITIALIZED) { + throw e; + } + } + } + moduleMap.put(pkcs11ModulePath, pkcs11); + } + return pkcs11; + } + + /** + * Connects this object to the specified PKCS#11 library. This method is for + * internal use only. + * Declared private, because incorrect handling may result in errors in the + * native part. + * + * @param pkcs11ModulePath The PKCS#11 library path. + * @preconditions (pkcs11ModulePath <> null) + * @postconditions + */ + private native void connect(String pkcs11ModulePath, String functionListName) + throws IOException; + + /** + * Disconnects the PKCS#11 library from this object. After calling this + * method, this object is no longer connected to a native PKCS#11 module + * and any subsequent calls to C_ methods will fail. This method is for + * internal use only. + * Declared private, because incorrect handling may result in errors in the + * native part. + * + * @preconditions + * @postconditions + */ + private native void disconnect(); + + + // Implementation of PKCS11 methods delegated to native pkcs11wrapper library + +/* ***************************************************************************** + * General-purpose + ******************************************************************************/ + + /** + * C_Initialize initializes the Cryptoki library. + * (General-purpose) + * + * @param pInitArgs if pInitArgs is not NULL it gets casted to + * CK_C_INITIALIZE_ARGS_PTR and dereferenced + * (PKCS#11 param: CK_VOID_PTR pInitArgs) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + native void C_Initialize(Object pInitArgs) throws PKCS11Exception; + + /** + * C_Finalize indicates that an application is done with the + * Cryptoki library + * (General-purpose) + * + * @param pReserved is reserved. Should be NULL_PTR + * (PKCS#11 param: CK_VOID_PTR pReserved) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pReserved == null) + * @postconditions + */ + public native void C_Finalize(Object pReserved) throws PKCS11Exception; + + + /** + * C_GetInfo returns general information about Cryptoki. + * (General-purpose) + * + * @return the information. + * (PKCS#11 param: CK_INFO_PTR pInfo) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native CK_INFO C_GetInfo() throws PKCS11Exception; + + +/* ***************************************************************************** + * Slot and token management + ******************************************************************************/ + + /** + * C_GetSlotList obtains a list of slots in the system. + * (Slot and token management) + * + * @param tokenPresent if true only Slot IDs with a token are returned + * (PKCS#11 param: CK_BBOOL tokenPresent) + * @return a long array of slot IDs and number of Slot IDs + * (PKCS#11 param: CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native long[] C_GetSlotList(boolean tokenPresent) + throws PKCS11Exception; + + + /** + * C_GetSlotInfo obtains information about a particular slot in + * the system. + * (Slot and token management) + * + * @param slotID the ID of the slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @return the slot information + * (PKCS#11 param: CK_SLOT_INFO_PTR pInfo) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native CK_SLOT_INFO C_GetSlotInfo(long slotID) throws PKCS11Exception; + + + /** + * C_GetTokenInfo obtains information about a particular token + * in the system. + * (Slot and token management) + * + * @param slotID ID of the token's slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @return the token information + * (PKCS#11 param: CK_TOKEN_INFO_PTR pInfo) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native CK_TOKEN_INFO C_GetTokenInfo(long slotID) + throws PKCS11Exception; + + + /** + * C_GetMechanismList obtains a list of mechanism types + * supported by a token. + * (Slot and token management) + * + * @param slotID ID of the token's slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @return a long array of mechanism types and number of mechanism types + * (PKCS#11 param: CK_MECHANISM_TYPE_PTR pMechanismList, + * CK_ULONG_PTR pulCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native long[] C_GetMechanismList(long slotID) throws PKCS11Exception; + + + /** + * C_GetMechanismInfo obtains information about a particular + * mechanism possibly supported by a token. + * (Slot and token management) + * + * @param slotID ID of the token's slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @param type type of mechanism + * (PKCS#11 param: CK_MECHANISM_TYPE type) + * @return the mechanism info + * (PKCS#11 param: CK_MECHANISM_INFO_PTR pInfo) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native CK_MECHANISM_INFO C_GetMechanismInfo(long slotID, long type) + throws PKCS11Exception; + + + /** + * C_InitToken initializes a token. + * (Slot and token management) + * + * @param slotID ID of the token's slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @param pPin the SO's initial PIN and the length in bytes of the PIN + * (PKCS#11 param: CK_CHAR_PTR pPin, CK_ULONG ulPinLen) + * @param pLabel 32-byte token label (blank padded) + * (PKCS#11 param: CK_UTF8CHAR_PTR pLabel) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_InitToken(long slotID, char[] pPin, char[] pLabel) +// throws PKCS11Exception; + + + /** + * C_InitPIN initializes the normal user's PIN. + * (Slot and token management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPin the normal user's PIN and the length in bytes of the PIN + * (PKCS#11 param: CK_CHAR_PTR pPin, CK_ULONG ulPinLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_InitPIN(long hSession, char[] pPin) +// throws PKCS11Exception; + + + /** + * C_SetPIN modifies the PIN of the user who is logged in. + * (Slot and token management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pOldPin the old PIN and the length of the old PIN + * (PKCS#11 param: CK_CHAR_PTR pOldPin, CK_ULONG ulOldLen) + * @param pNewPin the new PIN and the length of the new PIN + * (PKCS#11 param: CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_SetPIN(long hSession, char[] pOldPin, char[] pNewPin) +// throws PKCS11Exception; + + + +/* ***************************************************************************** + * Session management + ******************************************************************************/ + + /** + * C_OpenSession opens a session between an application and a + * token. + * (Session management) + * + * @param slotID the slot's ID + * (PKCS#11 param: CK_SLOT_ID slotID) + * @param flags of CK_SESSION_INFO + * (PKCS#11 param: CK_FLAGS flags) + * @param pApplication passed to callback + * (PKCS#11 param: CK_VOID_PTR pApplication) + * @param Notify the callback function + * (PKCS#11 param: CK_NOTIFY Notify) + * @return the session handle + * (PKCS#11 param: CK_SESSION_HANDLE_PTR phSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native long C_OpenSession(long slotID, long flags, + Object pApplication, CK_NOTIFY Notify) throws PKCS11Exception; + + + /** + * C_CloseSession closes a session between an application and a + * token. + * (Session management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_CloseSession(long hSession) throws PKCS11Exception; + + + /** + * C_CloseAllSessions closes all sessions with a token. + * (Session management) + * + * @param slotID the ID of the token's slot + * (PKCS#11 param: CK_SLOT_ID slotID) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_CloseAllSessions(long slotID) throws PKCS11Exception; + + + /** + * C_GetSessionInfo obtains information about the session. + * (Session management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the session info + * (PKCS#11 param: CK_SESSION_INFO_PTR pInfo) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native CK_SESSION_INFO C_GetSessionInfo(long hSession) + throws PKCS11Exception; + + + /** + * C_GetOperationState obtains the state of the cryptographic operation + * in a session. + * (Session management) + * + * @param hSession session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the state and the state length + * (PKCS#11 param: CK_BYTE_PTR pOperationState, + * CK_ULONG_PTR pulOperationStateLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native byte[] C_GetOperationState(long hSession) + throws PKCS11Exception; + + + /** + * C_SetOperationState restores the state of the cryptographic + * operation in a session. + * (Session management) + * + * @param hSession session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pOperationState the state and the state length + * (PKCS#11 param: CK_BYTE_PTR pOperationState, + * CK_ULONG ulOperationStateLen) + * @param hEncryptionKey en/decryption key + * (PKCS#11 param: CK_OBJECT_HANDLE hEncryptionKey) + * @param hAuthenticationKey sign/verify key + * (PKCS#11 param: CK_OBJECT_HANDLE hAuthenticationKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_SetOperationState(long hSession, byte[] pOperationState, + long hEncryptionKey, long hAuthenticationKey) throws PKCS11Exception; + + + /** + * C_Login logs a user into a token. + * (Session management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param userType the user type + * (PKCS#11 param: CK_USER_TYPE userType) + * @param pPin the user's PIN and the length of the PIN + * (PKCS#11 param: CK_CHAR_PTR pPin, CK_ULONG ulPinLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_Login(long hSession, long userType, char[] pPin) + throws PKCS11Exception; + + + /** + * C_Logout logs a user out from a token. + * (Session management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_Logout(long hSession) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Object management + ******************************************************************************/ + + /** + * C_CreateObject creates a new object. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pTemplate the object's template and number of attributes in + * template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @return the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phObject) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native long C_CreateObject(long hSession, CK_ATTRIBUTE[] pTemplate) + throws PKCS11Exception; + + + /** + * C_CopyObject copies an object, creating a new object for the + * copy. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hObject the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE hObject) + * @param pTemplate the template for the new object and number of attributes + * in template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @return the handle of the copy + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phNewObject) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native long C_CopyObject(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception; + + + /** + * C_DestroyObject destroys an object. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hObject the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE hObject) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_DestroyObject(long hSession, long hObject) + throws PKCS11Exception; + + + /** + * C_GetObjectSize gets the size of an object in bytes. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hObject the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE hObject) + * @return the size of the object + * (PKCS#11 param: CK_ULONG_PTR pulSize) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native long C_GetObjectSize(long hSession, long hObject) +// throws PKCS11Exception; + + + /** + * C_GetAttributeValue obtains the value of one or more object + * attributes. The template attributes also receive the values. + * (Object management) + * note: in PKCS#11 pTemplate and the result template are the same + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hObject the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE hObject) + * @param pTemplate specifies the attributes and number of attributes to get + * The template attributes also receive the values. + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pTemplate <> null) + * @postconditions (result <> null) + */ + public native void C_GetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception; + + + /** + * C_SetAttributeValue modifies the value of one or more object + * attributes + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hObject the object's handle + * (PKCS#11 param: CK_OBJECT_HANDLE hObject) + * @param pTemplate specifies the attributes and values to get; number of + * attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pTemplate <> null) + * @postconditions + */ + public native void C_SetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception; + + + /** + * C_FindObjectsInit initializes a search for token and session + * objects that match a template. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pTemplate the object's attribute values to match and the number of + * attributes in search template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_FindObjectsInit(long hSession, CK_ATTRIBUTE[] pTemplate) + throws PKCS11Exception; + + + /** + * C_FindObjects continues a search for token and session + * objects that match a template, obtaining additional object + * handles. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param ulMaxObjectCount the max. object handles to get + * (PKCS#11 param: CK_ULONG ulMaxObjectCount) + * @return the object's handles and the actual number of objects returned + * (PKCS#11 param: CK_ULONG_PTR pulObjectCount) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native long[] C_FindObjects(long hSession, long ulMaxObjectCount) + throws PKCS11Exception; + + + /** + * C_FindObjectsFinal finishes a search for token and session + * objects. + * (Object management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_FindObjectsFinal(long hSession) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Encryption and decryption + ******************************************************************************/ + + /** + * C_EncryptInit initializes an encryption operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the encryption mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the encryption key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_EncryptInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception; + + + /** + * C_Encrypt encrypts single-part data. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pData the data to get encrypted and the data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG ulDataLen) + * @return the encrypted data and the encrypted data's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedData, + * CK_ULONG_PTR pulEncryptedDataLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pData <> null) + * @postconditions (result <> null) + */ + public native int C_Encrypt(long hSession, byte[] in, int inOfs, int inLen, + byte[] out, int outOfs, int outLen) throws PKCS11Exception; + + + /** + * C_EncryptUpdate continues a multiple-part encryption + * operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the data part to get encrypted and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @return the encrypted data part and the encrypted data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ + public native int C_EncryptUpdate(long hSession, long directIn, byte[] in, + int inOfs, int inLen, long directOut, byte[] out, int outOfs, + int outLen) throws PKCS11Exception; + + + /** + * C_EncryptFinal finishes a multiple-part encryption + * operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the last encrypted data part and the last data part's length + * (PKCS#11 param: CK_BYTE_PTR pLastEncryptedPart, + CK_ULONG_PTR pulLastEncryptedPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native int C_EncryptFinal(long hSession, long directOut, byte[] out, + int outOfs, int outLen) throws PKCS11Exception; + + + /** + * C_DecryptInit initializes a decryption operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the decryption mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the decryption key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_DecryptInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception; + + + /** + * C_Decrypt decrypts encrypted data in a single part. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pEncryptedData the encrypted data to get decrypted and the + * encrypted data's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedData, + * CK_ULONG ulEncryptedDataLen) + * @return the decrypted data and the data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pEncryptedPart <> null) + * @postconditions (result <> null) + */ + public native int C_Decrypt(long hSession, byte[] in, int inOfs, int inLen, + byte[] out, int outOfs, int outLen) throws PKCS11Exception; + + + /** + * C_DecryptUpdate continues a multiple-part decryption + * operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pEncryptedPart the encrypted data part to get decrypted and the + * encrypted data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + * CK_ULONG ulEncryptedPartLen) + * @return the decrypted data part and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pEncryptedPart <> null) + * @postconditions + */ + public native int C_DecryptUpdate(long hSession, long directIn, byte[] in, + int inOfs, int inLen, long directOut, byte[] out, int outOfs, + int outLen) throws PKCS11Exception; + + + /** + * C_DecryptFinal finishes a multiple-part decryption + * operation. + * (Encryption and decryption) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the last decrypted data part and the last data part's length + * (PKCS#11 param: CK_BYTE_PTR pLastPart, + * CK_ULONG_PTR pulLastPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native int C_DecryptFinal(long hSession, long directOut, byte[] out, + int outOfs, int outLen) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Message digesting + ******************************************************************************/ + + /** + * C_DigestInit initializes a message-digesting operation. + * (Message digesting) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the digesting mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_DigestInit(long hSession, CK_MECHANISM pMechanism) + throws PKCS11Exception; + + + // note that C_DigestSingle does not exist in PKCS#11 + // we combined the C_DigestInit and C_Digest into a single function + // to save on Java<->C transitions and save 5-10% on small digests + // this made the C_Digest method redundant, it has been removed + /** + * C_Digest digests data in a single part. + * (Message digesting) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param data the data to get digested and the data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG ulDataLen) + * @return the message digest and the length of the message digest + * (PKCS#11 param: CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (data <> null) + * @postconditions (result <> null) + */ + public native int C_DigestSingle(long hSession, CK_MECHANISM pMechanism, + byte[] in, int inOfs, int inLen, byte[] digest, int digestOfs, + int digestLen) throws PKCS11Exception; + + + /** + * C_DigestUpdate continues a multiple-part message-digesting + * operation. + * (Message digesting) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the data to get digested and the data's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ + public native void C_DigestUpdate(long hSession, long directIn, byte[] in, + int inOfs, int inLen) throws PKCS11Exception; + + + /** + * C_DigestKey continues a multi-part message-digesting + * operation, by digesting the value of a secret key as part of + * the data already digested. + * (Message digesting) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param hKey the handle of the secret key to be digested + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_DigestKey(long hSession, long hKey) + throws PKCS11Exception; + + + /** + * C_DigestFinal finishes a multiple-part message-digesting + * operation. + * (Message digesting) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the message digest and the length of the message digest + * (PKCS#11 param: CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native int C_DigestFinal(long hSession, byte[] pDigest, int digestOfs, + int digestLen) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Signing and MACing + ******************************************************************************/ + + /** + * C_SignInit initializes a signature (private key encryption) + * operation, where the signature is (will be) an appendix to + * the data, and plaintext cannot be recovered from the + * signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the signature mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the signature key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_SignInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception; + + + /** + * C_Sign signs (encrypts with private key) data in a single + * part, where the signature is (will be) an appendix to the + * data, and plaintext cannot be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pData the data to sign and the data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG ulDataLen) + * @return the signature and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, + * CK_ULONG_PTR pulSignatureLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pData <> null) + * @postconditions (result <> null) + */ + public native byte[] C_Sign(long hSession, byte[] pData) + throws PKCS11Exception; + + + /** + * C_SignUpdate continues a multiple-part signature operation, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the data part to sign and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ + public native void C_SignUpdate(long hSession, long directIn, byte[] in, + int inOfs, int inLen) throws PKCS11Exception; + + + /** + * C_SignFinal finishes a multiple-part signature operation, + * returning the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @return the signature and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, + * CK_ULONG_PTR pulSignatureLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native byte[] C_SignFinal(long hSession, int expectedLen) + throws PKCS11Exception; + + + /** + * C_SignRecoverInit initializes a signature operation, where + * the data can be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the signature mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the signature key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_SignRecoverInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception; + + + /** + * C_SignRecover signs data in a single operation, where the + * data can be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pData the data to sign and the data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG ulDataLen) + * @return the signature and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, + * CK_ULONG_PTR pulSignatureLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pData <> null) + * @postconditions (result <> null) + */ + public native int C_SignRecover(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOufs, int outLen) + throws PKCS11Exception; + + + +/* ***************************************************************************** + * Verifying signatures and MACs + ******************************************************************************/ + + /** + * C_VerifyInit initializes a verification operation, where the + * signature is an appendix to the data, and plaintext cannot + * cannot be recovered from the signature (e.g. DSA). + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the verification mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the verification key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_VerifyInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception; + + + /** + * C_Verify verifies a signature in a single-part operation, + * where the signature is an appendix to the data, and plaintext + * cannot be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pData the signed data and the signed data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG ulDataLen) + * @param pSignature the signature to verify and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pData <> null) and (pSignature <> null) + * @postconditions + */ + public native void C_Verify(long hSession, byte[] pData, byte[] pSignature) + throws PKCS11Exception; + + + /** + * C_VerifyUpdate continues a multiple-part verification + * operation, where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the signed data part and the signed data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ + public native void C_VerifyUpdate(long hSession, long directIn, byte[] in, + int inOfs, int inLen) throws PKCS11Exception; + + + /** + * C_VerifyFinal finishes a multiple-part verification + * operation, checking the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pSignature the signature to verify and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pSignature <> null) + * @postconditions + */ + public native void C_VerifyFinal(long hSession, byte[] pSignature) + throws PKCS11Exception; + + + /** + * C_VerifyRecoverInit initializes a signature verification + * operation, where the data is recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the verification mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hKey the handle of the verification key + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native void C_VerifyRecoverInit(long hSession, + CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception; + + + /** + * C_VerifyRecover verifies a signature in a single-part + * operation, where the data is recovered from the signature. + * (Signing and MACing) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pSignature the signature to verify and the signature's length + * (PKCS#11 param: CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) + * @return the recovered data and the recovered data's length + * (PKCS#11 param: CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pSignature <> null) + * @postconditions (result <> null) + */ + public native int C_VerifyRecover(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOufs, int outLen) + throws PKCS11Exception; + + + +/* ***************************************************************************** + * Dual-function cryptographic operations + ******************************************************************************/ + + /** + * C_DigestEncryptUpdate continues a multiple-part digesting + * and encryption operation. + * (Dual-function cryptographic operations) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the data part to digest and to encrypt and the data's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @return the digested and encrypted data part and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + * CK_ULONG_PTR pulEncryptedPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ +// public native byte[] C_DigestEncryptUpdate(long hSession, byte[] pPart) +// throws PKCS11Exception; + + + /** + * C_DecryptDigestUpdate continues a multiple-part decryption and + * digesting operation. + * (Dual-function cryptographic operations) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pEncryptedPart the encrypted data part to decrypt and to digest + * and encrypted data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + * CK_ULONG ulEncryptedPartLen) + * @return the decrypted and digested data part and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pEncryptedPart <> null) + * @postconditions + */ +// public native byte[] C_DecryptDigestUpdate(long hSession, +// byte[] pEncryptedPart) throws PKCS11Exception; + + + /** + * C_SignEncryptUpdate continues a multiple-part signing and + * encryption operation. + * (Dual-function cryptographic operations) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pPart the data part to sign and to encrypt and the data part's + * length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG ulPartLen) + * @return the signed and encrypted data part and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + * CK_ULONG_PTR pulEncryptedPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pPart <> null) + * @postconditions + */ +// public native byte[] C_SignEncryptUpdate(long hSession, byte[] pPart) +// throws PKCS11Exception; + + + /** + * C_DecryptVerifyUpdate continues a multiple-part decryption and + * verify operation. + * (Dual-function cryptographic operations) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pEncryptedPart the encrypted data part to decrypt and to verify + * and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pEncryptedPart, + * CK_ULONG ulEncryptedPartLen) + * @return the decrypted and verified data part and the data part's length + * (PKCS#11 param: CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pEncryptedPart <> null) + * @postconditions + */ +// public native byte[] C_DecryptVerifyUpdate(long hSession, +// byte[] pEncryptedPart) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Key management + ******************************************************************************/ + + /** + * C_GenerateKey generates a secret key, creating a new key + * object. + * (Key management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the key generation mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param pTemplate the template for the new key and the number of + * attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @return the handle of the new key + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native long C_GenerateKey(long hSession, CK_MECHANISM pMechanism, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception; + + + /** + * C_GenerateKeyPair generates a public-key/private-key pair, + * creating new key objects. + * (Key management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the key generation mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param pPublicKeyTemplate the template for the new public key and the + * number of attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pPublicKeyTemplate, + * CK_ULONG ulPublicKeyAttributeCount) + * @param pPrivateKeyTemplate the template for the new private key and the + * number of attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pPrivateKeyTemplate + * CK_ULONG ulPrivateKeyAttributeCount) + * @return a long array with exactly two elements and the public key handle + * as the first element and the private key handle as the second + * element + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phPublicKey, + * CK_OBJECT_HANDLE_PTR phPrivateKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pMechanism <> null) + * @postconditions (result <> null) and (result.length == 2) + */ + public native long[] C_GenerateKeyPair(long hSession, + CK_MECHANISM pMechanism, CK_ATTRIBUTE[] pPublicKeyTemplate, + CK_ATTRIBUTE[] pPrivateKeyTemplate) throws PKCS11Exception; + + + + /** + * C_WrapKey wraps (i.e., encrypts) a key. + * (Key management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the wrapping mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hWrappingKey the handle of the wrapping key + * (PKCS#11 param: CK_OBJECT_HANDLE hWrappingKey) + * @param hKey the handle of the key to be wrapped + * (PKCS#11 param: CK_OBJECT_HANDLE hKey) + * @return the wrapped key and the length of the wrapped key + * (PKCS#11 param: CK_BYTE_PTR pWrappedKey, + * CK_ULONG_PTR pulWrappedKeyLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions (result <> null) + */ + public native byte[] C_WrapKey(long hSession, CK_MECHANISM pMechanism, + long hWrappingKey, long hKey) throws PKCS11Exception; + + + /** + * C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new + * key object. + * (Key management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the unwrapping mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hUnwrappingKey the handle of the unwrapping key + * (PKCS#11 param: CK_OBJECT_HANDLE hUnwrappingKey) + * @param pWrappedKey the wrapped key to unwrap and the wrapped key's length + * (PKCS#11 param: CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen) + * @param pTemplate the template for the new key and the number of + * attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @return the handle of the unwrapped key + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pWrappedKey <> null) + * @postconditions + */ + public native long C_UnwrapKey(long hSession, CK_MECHANISM pMechanism, + long hUnwrappingKey, byte[] pWrappedKey, CK_ATTRIBUTE[] pTemplate) + throws PKCS11Exception; + + + /** + * C_DeriveKey derives a key from a base key, creating a new key + * object. + * (Key management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pMechanism the key derivation mechanism + * (PKCS#11 param: CK_MECHANISM_PTR pMechanism) + * @param hBaseKey the handle of the base key + * (PKCS#11 param: CK_OBJECT_HANDLE hBaseKey) + * @param pTemplate the template for the new key and the number of + * attributes in the template + * (PKCS#11 param: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) + * @return the handle of the derived key + * (PKCS#11 param: CK_OBJECT_HANDLE_PTR phKey) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ + public native long C_DeriveKey(long hSession, CK_MECHANISM pMechanism, + long hBaseKey, CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Random number generation + ******************************************************************************/ + + /** + * C_SeedRandom mixes additional seed material into the token's + * random number generator. + * (Random number generation) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param pSeed the seed material and the seed material's length + * (PKCS#11 param: CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pSeed <> null) + * @postconditions + */ + public native void C_SeedRandom(long hSession, byte[] pSeed) + throws PKCS11Exception; + + + /** + * C_GenerateRandom generates random data. + * (Random number generation) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @param RandomData receives the random data and the length of RandomData + * is the length of random data to be generated + * (PKCS#11 param: CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (randomData <> null) + * @postconditions + */ + public native void C_GenerateRandom(long hSession, byte[] randomData) + throws PKCS11Exception; + + + +/* ***************************************************************************** + * Parallel function management + ******************************************************************************/ + + /** + * C_GetFunctionStatus is a legacy function; it obtains an + * updated status of a function running in parallel with an + * application. + * (Parallel function management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_GetFunctionStatus(long hSession) +// throws PKCS11Exception; + + + /** + * C_CancelFunction is a legacy function; it cancels a function + * running in parallel. + * (Parallel function management) + * + * @param hSession the session's handle + * (PKCS#11 param: CK_SESSION_HANDLE hSession) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions + * @postconditions + */ +// public native void C_CancelFunction(long hSession) throws PKCS11Exception; + + + +/* ***************************************************************************** + * Functions added in for Cryptoki Version 2.01 or later + ******************************************************************************/ + + /** + * C_WaitForSlotEvent waits for a slot event (token insertion, + * removal, etc.) to occur. + * (General-purpose) + * + * @param flags blocking/nonblocking flag + * (PKCS#11 param: CK_FLAGS flags) + * @param pReserved reserved. Should be null + * (PKCS#11 param: CK_VOID_PTR pReserved) + * @return the slot ID where the event occurred + * (PKCS#11 param: CK_SLOT_ID_PTR pSlot) + * @exception PKCS11Exception If function returns other value than CKR_OK. + * @preconditions (pRserved == null) + * @postconditions + */ +// public native long C_WaitForSlotEvent(long flags, Object pRserved) +// throws PKCS11Exception; + + /** + * Returns the string representation of this object. + * + * @return The string representation of object + */ + public String toString() { + return "Module name: " + pkcs11ModulePath; + } + + /** + * Calls disconnect() to cleanup the native part of the wrapper. Once this + * method is called, this object cannot be used any longer. Any subsequent + * call to a C_* method will result in a runtime exception. + * + * @exception Throwable If finalization fails. + */ + protected void finalize() throws Throwable { + disconnect(); + } + +// PKCS11 subclass that has all methods synchronized and delegating to the +// parent. Used for tokens that only support single threaded access +static class SynchronizedPKCS11 extends PKCS11 { + + SynchronizedPKCS11(String pkcs11ModulePath, String functionListName) + throws IOException { + super(pkcs11ModulePath, functionListName); + } + + synchronized void C_Initialize(Object pInitArgs) throws PKCS11Exception { + super.C_Initialize(pInitArgs); + } + + public synchronized void C_Finalize(Object pReserved) + throws PKCS11Exception { + super.C_Finalize(pReserved); + } + + public synchronized CK_INFO C_GetInfo() throws PKCS11Exception { + return super.C_GetInfo(); + } + + public synchronized long[] C_GetSlotList(boolean tokenPresent) + throws PKCS11Exception { + return super.C_GetSlotList(tokenPresent); + } + + public synchronized CK_SLOT_INFO C_GetSlotInfo(long slotID) + throws PKCS11Exception { + return super.C_GetSlotInfo(slotID); + } + + public synchronized CK_TOKEN_INFO C_GetTokenInfo(long slotID) + throws PKCS11Exception { + return super.C_GetTokenInfo(slotID); + } + + public synchronized long[] C_GetMechanismList(long slotID) + throws PKCS11Exception { + return super.C_GetMechanismList(slotID); + } + + public synchronized CK_MECHANISM_INFO C_GetMechanismInfo(long slotID, + long type) throws PKCS11Exception { + return super.C_GetMechanismInfo(slotID, type); + } + + public synchronized long C_OpenSession(long slotID, long flags, + Object pApplication, CK_NOTIFY Notify) throws PKCS11Exception { + return super.C_OpenSession(slotID, flags, pApplication, Notify); + } + + public synchronized void C_CloseSession(long hSession) + throws PKCS11Exception { + super.C_CloseSession(hSession); + } + + public synchronized CK_SESSION_INFO C_GetSessionInfo(long hSession) + throws PKCS11Exception { + return super.C_GetSessionInfo(hSession); + } + + public synchronized void C_Login(long hSession, long userType, char[] pPin) + throws PKCS11Exception { + super.C_Login(hSession, userType, pPin); + } + + public synchronized void C_Logout(long hSession) throws PKCS11Exception { + super.C_Logout(hSession); + } + + public synchronized long C_CreateObject(long hSession, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + return super.C_CreateObject(hSession, pTemplate); + } + + public synchronized long C_CopyObject(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + return super.C_CopyObject(hSession, hObject, pTemplate); + } + + public synchronized void C_DestroyObject(long hSession, long hObject) + throws PKCS11Exception { + super.C_DestroyObject(hSession, hObject); + } + + public synchronized void C_GetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + super.C_GetAttributeValue(hSession, hObject, pTemplate); + } + + public synchronized void C_SetAttributeValue(long hSession, long hObject, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + super.C_SetAttributeValue(hSession, hObject, pTemplate); + } + + public synchronized void C_FindObjectsInit(long hSession, + CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + super.C_FindObjectsInit(hSession, pTemplate); + } + + public synchronized long[] C_FindObjects(long hSession, + long ulMaxObjectCount) throws PKCS11Exception { + return super.C_FindObjects(hSession, ulMaxObjectCount); + } + + public synchronized void C_FindObjectsFinal(long hSession) + throws PKCS11Exception { + super.C_FindObjectsFinal(hSession); + } + + public synchronized void C_EncryptInit(long hSession, + CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception { + super.C_EncryptInit(hSession, pMechanism, hKey); + } + + public synchronized int C_Encrypt(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOfs, int outLen) + throws PKCS11Exception { + return super.C_Encrypt(hSession, in, inOfs, inLen, out, outOfs, outLen); + } + + public synchronized int C_EncryptUpdate(long hSession, long directIn, + byte[] in, int inOfs, int inLen, long directOut, byte[] out, + int outOfs, int outLen) throws PKCS11Exception { + return super.C_EncryptUpdate(hSession, directIn, in, inOfs, inLen, + directOut, out, outOfs, outLen); + } + + public synchronized int C_EncryptFinal(long hSession, long directOut, + byte[] out, int outOfs, int outLen) throws PKCS11Exception { + return super.C_EncryptFinal(hSession, directOut, out, outOfs, outLen); + } + + public synchronized void C_DecryptInit(long hSession, + CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception { + super.C_DecryptInit(hSession, pMechanism, hKey); + } + + public synchronized int C_Decrypt(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOfs, int outLen) + throws PKCS11Exception { + return super.C_Decrypt(hSession, in, inOfs, inLen, out, outOfs, outLen); + } + + public synchronized int C_DecryptUpdate(long hSession, long directIn, + byte[] in, int inOfs, int inLen, long directOut, byte[] out, + int outOfs, int outLen) throws PKCS11Exception { + return super.C_DecryptUpdate(hSession, directIn, in, inOfs, inLen, + directOut, out, outOfs, outLen); + } + + public synchronized int C_DecryptFinal(long hSession, long directOut, + byte[] out, int outOfs, int outLen) throws PKCS11Exception { + return super.C_DecryptFinal(hSession, directOut, out, outOfs, outLen); + } + + public synchronized void C_DigestInit(long hSession, CK_MECHANISM pMechanism) + throws PKCS11Exception { + super.C_DigestInit(hSession, pMechanism); + } + + public synchronized int C_DigestSingle(long hSession, + CK_MECHANISM pMechanism, byte[] in, int inOfs, int inLen, + byte[] digest, int digestOfs, int digestLen) throws PKCS11Exception { + return super.C_DigestSingle(hSession, pMechanism, in, inOfs, inLen, + digest, digestOfs, digestLen); + } + + public synchronized void C_DigestUpdate(long hSession, long directIn, + byte[] in, int inOfs, int inLen) throws PKCS11Exception { + super.C_DigestUpdate(hSession, directIn, in, inOfs, inLen); + } + + public synchronized void C_DigestKey(long hSession, long hKey) + throws PKCS11Exception { + super.C_DigestKey(hSession, hKey); + } + + public synchronized int C_DigestFinal(long hSession, byte[] pDigest, + int digestOfs, int digestLen) throws PKCS11Exception { + return super.C_DigestFinal(hSession, pDigest, digestOfs, digestLen); + } + + public synchronized void C_SignInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception { + super.C_SignInit(hSession, pMechanism, hKey); + } + + public synchronized byte[] C_Sign(long hSession, byte[] pData) + throws PKCS11Exception { + return super.C_Sign(hSession, pData); + } + + public synchronized void C_SignUpdate(long hSession, long directIn, + byte[] in, int inOfs, int inLen) throws PKCS11Exception { + super.C_SignUpdate(hSession, directIn, in, inOfs, inLen); + } + + public synchronized byte[] C_SignFinal(long hSession, int expectedLen) + throws PKCS11Exception { + return super.C_SignFinal(hSession, expectedLen); + } + + public synchronized void C_SignRecoverInit(long hSession, + CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception { + super.C_SignRecoverInit(hSession, pMechanism, hKey); + } + + public synchronized int C_SignRecover(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOufs, int outLen) + throws PKCS11Exception { + return super.C_SignRecover(hSession, in, inOfs, inLen, out, outOufs, + outLen); + } + + public synchronized void C_VerifyInit(long hSession, CK_MECHANISM pMechanism, + long hKey) throws PKCS11Exception { + super.C_VerifyInit(hSession, pMechanism, hKey); + } + + public synchronized void C_Verify(long hSession, byte[] pData, + byte[] pSignature) throws PKCS11Exception { + super.C_Verify(hSession, pData, pSignature); + } + + public synchronized void C_VerifyUpdate(long hSession, long directIn, + byte[] in, int inOfs, int inLen) throws PKCS11Exception { + super.C_VerifyUpdate(hSession, directIn, in, inOfs, inLen); + } + + public synchronized void C_VerifyFinal(long hSession, byte[] pSignature) + throws PKCS11Exception { + super.C_VerifyFinal(hSession, pSignature); + } + + public synchronized void C_VerifyRecoverInit(long hSession, + CK_MECHANISM pMechanism, long hKey) throws PKCS11Exception { + super.C_VerifyRecoverInit(hSession, pMechanism, hKey); + } + + public synchronized int C_VerifyRecover(long hSession, byte[] in, int inOfs, + int inLen, byte[] out, int outOufs, int outLen) + throws PKCS11Exception { + return super.C_VerifyRecover(hSession, in, inOfs, inLen, out, outOufs, + outLen); + } + + public synchronized long C_GenerateKey(long hSession, + CK_MECHANISM pMechanism, CK_ATTRIBUTE[] pTemplate) + throws PKCS11Exception { + return super.C_GenerateKey(hSession, pMechanism, pTemplate); + } + + public synchronized long[] C_GenerateKeyPair(long hSession, + CK_MECHANISM pMechanism, CK_ATTRIBUTE[] pPublicKeyTemplate, + CK_ATTRIBUTE[] pPrivateKeyTemplate) + throws PKCS11Exception { + return super.C_GenerateKeyPair(hSession, pMechanism, pPublicKeyTemplate, + pPrivateKeyTemplate); + } + + public synchronized byte[] C_WrapKey(long hSession, CK_MECHANISM pMechanism, + long hWrappingKey, long hKey) throws PKCS11Exception { + return super.C_WrapKey(hSession, pMechanism, hWrappingKey, hKey); + } + + public synchronized long C_UnwrapKey(long hSession, CK_MECHANISM pMechanism, + long hUnwrappingKey, byte[] pWrappedKey, CK_ATTRIBUTE[] pTemplate) + throws PKCS11Exception { + return super.C_UnwrapKey(hSession, pMechanism, hUnwrappingKey, + pWrappedKey, pTemplate); + } + + public synchronized long C_DeriveKey(long hSession, CK_MECHANISM pMechanism, + long hBaseKey, CK_ATTRIBUTE[] pTemplate) throws PKCS11Exception { + return super.C_DeriveKey(hSession, pMechanism, hBaseKey, pTemplate); + } + + public synchronized void C_SeedRandom(long hSession, byte[] pSeed) + throws PKCS11Exception { + super.C_SeedRandom(hSession, pSeed); + } + + public synchronized void C_GenerateRandom(long hSession, byte[] randomData) + throws PKCS11Exception { + super.C_GenerateRandom(hSession, randomData); + } +} +} diff --git a/src/sun/security/pkcs11/wrapper/PKCS11Constants.java b/src/sun/security/pkcs11/wrapper/PKCS11Constants.java new file mode 100644 index 00000000..98e9fe06 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/PKCS11Constants.java @@ -0,0 +1,966 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +/** + * This interface holds constants of the PKCS#11 v2.11 standard. + * This is mainly the content of the 'pkcs11t.h' header file. + * + * Mapping of primitiv data types to Java types: + *
    + *   TRUE .......................................... true
    + *   FALSE ......................................... false
    + *   CK_BYTE ....................................... byte
    + *   CK_CHAR ....................................... char
    + *   CK_UTF8CHAR ................................... char
    + *   CK_BBOOL ...................................... boolean
    + *   CK_ULONG ...................................... long
    + *   CK_LONG ....................................... long
    + *   CK_FLAGS ...................................... long
    + *   CK_NOTIFICATION ............................... long
    + *   CK_SLOT_ID .................................... long
    + *   CK_SESSION_HANDLE ............................. long
    + *   CK_USER_TYPE .................................. long
    + *   CK_SESSION_HANDLE ............................. long
    + *   CK_STATE ...................................... long
    + *   CK_OBJECT_HANDLE .............................. long
    + *   CK_OBJECT_CLASS ............................... long
    + *   CK_HW_FEATURE_TYPE ............................ long
    + *   CK_KEY_TYPE ................................... long
    + *   CK_CERTIFICATE_TYPE ........................... long
    + *   CK_ATTRIBUTE_TYPE ............................. long
    + *   CK_VOID_PTR ................................... Object[]
    + *   CK_BYTE_PTR ................................... byte[]
    + *   CK_CHAR_PTR ................................... char[]
    + *   CK_UTF8CHAR_PTR ............................... char[]
    + *   CK_MECHANISM_TYPE ............................. long
    + *   CK_RV ......................................... long
    + *   CK_RSA_PKCS_OAEP_MGF_TYPE ..................... long
    + *   CK_RSA_PKCS_OAEP_SOURCE_TYPE .................. long
    + *   CK_RC2_PARAMS ................................. long
    + *   CK_MAC_GENERAL_PARAMS ......................... long
    + *   CK_EXTRACT_PARAMS ............................. long
    + *   CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE .... long
    + *   CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE .............. long
    + *   CK_EC_KDF_TYPE ................................ long
    + *   CK_X9_42_DH_KDF_TYPE .......................... long
    + * 
    + * + * @author Karl Scheibelhofer + * @invariants + */ +public interface PKCS11Constants { + + public static final boolean TRUE = true; + + public static final boolean FALSE = false; + + public static final Object NULL_PTR = null; + + /* some special values for certain CK_ULONG variables */ + + // Cryptoki defines CK_UNAVAILABLE_INFORMATION as (~0UL) + // This means it is 0xffffffff in ILP32/LLP64 but 0xffffffffffffffff in LP64. + // To avoid these differences on the Java side, the native code treats + // CK_UNAVAILABLE_INFORMATION specially and always returns (long)-1 for it. + // See ckULongSpecialToJLong() in pkcs11wrapper.h + public static final long CK_UNAVAILABLE_INFORMATION = -1; + public static final long CK_EFFECTIVELY_INFINITE = 0L; + + /* The following value is always invalid if used as a session */ + /* handle or object handle */ + public static final long CK_INVALID_HANDLE = 0L; + + /* CK_NOTIFICATION enumerates the types of notifications that + * Cryptoki provides to an application */ + /* CK_NOTIFICATION has been changed from an enum to a CK_ULONG + * for v2.0 */ + public static final long CKN_SURRENDER = 0L; + + /* flags: bit flags that provide capabilities of the slot + * Bit Flag Mask Meaning + */ + public static final long CKF_TOKEN_PRESENT = 0x00000001L; + public static final long CKF_REMOVABLE_DEVICE = 0x00000002L; + public static final long CKF_HW_SLOT = 0x00000004L; + + /* The flags parameter is defined as follows: + * Bit Flag Mask Meaning + */ + /* has random # generator */ + public static final long CKF_RNG = 0x00000001L; + + /* token is write-protected */ + public static final long CKF_WRITE_PROTECTED = 0x00000002L; + + /* user must login */ + public static final long CKF_LOGIN_REQUIRED = 0x00000004L; + + /* normal user's PIN is set */ + public static final long CKF_USER_PIN_INITIALIZED = 0x00000008L; + + /* CKF_RESTORE_KEY_NOT_NEEDED is new for v2.0. If it is set, + * that means that *every* time the state of cryptographic + * operations of a session is successfully saved, all keys + * needed to continue those operations are stored in the state */ + public static final long CKF_RESTORE_KEY_NOT_NEEDED = 0x00000020L; + + /* CKF_CLOCK_ON_TOKEN is new for v2.0. If it is set, that means + * that the token has some sort of clock. The time on that + * clock is returned in the token info structure */ + public static final long CKF_CLOCK_ON_TOKEN = 0x00000040L; + + /* CKF_PROTECTED_AUTHENTICATION_PATH is new for v2.0. If it is + * set, that means that there is some way for the user to login + * without sending a PIN through the Cryptoki library itself */ + public static final long CKF_PROTECTED_AUTHENTICATION_PATH = 0x00000100L; + + /* CKF_DUAL_CRYPTO_OPERATIONS is new for v2.0. If it is true, + * that means that a single session with the token can perform + * dual simultaneous cryptographic operations (digest and + * encrypt; decrypt and digest; sign and encrypt; and decrypt + * and sign) */ + public static final long CKF_DUAL_CRYPTO_OPERATIONS = 0x00000200L; + + /* CKF_TOKEN_INITIALIZED if new for v2.10. If it is true, the + * token has been initialized using C_InitializeToken or an + * equivalent mechanism outside the scope of PKCS #11. + * Calling C_InitializeToken when this flag is set will cause + * the token to be reinitialized. */ + public static final long CKF_TOKEN_INITIALIZED = 0x00000400L; + + /* CKF_SECONDARY_AUTHENTICATION if new for v2.10. If it is + * true, the token supports secondary authentication for + * private key objects. */ + public static final long CKF_SECONDARY_AUTHENTICATION = 0x00000800L; + + /* CKF_USER_PIN_COUNT_LOW if new for v2.10. If it is true, an + * incorrect user login PIN has been entered at least once + * since the last successful authentication. */ + public static final long CKF_USER_PIN_COUNT_LOW = 0x00010000L; + + /* CKF_USER_PIN_FINAL_TRY if new for v2.10. If it is true, + * supplying an incorrect user PIN will it to become locked. */ + public static final long CKF_USER_PIN_FINAL_TRY = 0x00020000L; + + /* CKF_USER_PIN_LOCKED if new for v2.10. If it is true, the + * user PIN has been locked. User login to the token is not + * possible. */ + public static final long CKF_USER_PIN_LOCKED = 0x00040000L; + + /* CKF_USER_PIN_TO_BE_CHANGED if new for v2.10. If it is true, + * the user PIN value is the default value set by token + * initialization or manufacturing. */ + public static final long CKF_USER_PIN_TO_BE_CHANGED = 0x00080000L; + + /* CKF_SO_PIN_COUNT_LOW if new for v2.10. If it is true, an + * incorrect SO login PIN has been entered at least once since + * the last successful authentication. */ + public static final long CKF_SO_PIN_COUNT_LOW = 0x00100000L; + + /* CKF_SO_PIN_FINAL_TRY if new for v2.10. If it is true, + * supplying an incorrect SO PIN will it to become locked. */ + public static final long CKF_SO_PIN_FINAL_TRY = 0x00200000L; + + /* CKF_SO_PIN_LOCKED if new for v2.10. If it is true, the SO + * PIN has been locked. SO login to the token is not possible. + */ + public static final long CKF_SO_PIN_LOCKED = 0x00400000L; + + /* CKF_SO_PIN_TO_BE_CHANGED if new for v2.10. If it is true, + * the SO PIN value is the default value set by token + * initialization or manufacturing. */ + public static final long CKF_SO_PIN_TO_BE_CHANGED = 0x00800000L; + + + /* CK_USER_TYPE enumerates the types of Cryptoki users */ + /* CK_USER_TYPE has been changed from an enum to a CK_ULONG for + * v2.0 */ + /* Security Officer */ + public static final long CKU_SO = 0L; + /* Normal user */ + public static final long CKU_USER = 1L; + + /* CK_STATE enumerates the session states */ + /* CK_STATE has been changed from an enum to a CK_ULONG for + * v2.0 */ + public static final long CKS_RO_PUBLIC_SESSION = 0L; + public static final long CKS_RO_USER_FUNCTIONS = 1L; + public static final long CKS_RW_PUBLIC_SESSION = 2L; + public static final long CKS_RW_USER_FUNCTIONS = 3L; + public static final long CKS_RW_SO_FUNCTIONS = 4L; + + + /* The flags are defined in the following table: + * Bit Flag Mask Meaning + */ + /* session is r/w */ + public static final long CKF_RW_SESSION = 0x00000002L; + /* no parallel */ + public static final long CKF_SERIAL_SESSION = 0x00000004L; + + + /* The following classes of objects are defined: */ + /* CKO_HW_FEATURE is new for v2.10 */ + /* CKO_DOMAIN_PARAMETERS is new for v2.11 */ + public static final long CKO_DATA = 0x00000000L; + public static final long CKO_CERTIFICATE = 0x00000001L; + public static final long CKO_PUBLIC_KEY = 0x00000002L; + public static final long CKO_PRIVATE_KEY = 0x00000003L; + public static final long CKO_SECRET_KEY = 0x00000004L; + public static final long CKO_HW_FEATURE = 0x00000005L; + public static final long CKO_DOMAIN_PARAMETERS = 0x00000006L; + public static final long CKO_VENDOR_DEFINED = 0x80000000L; + + // pseudo object class ANY (for template manager) + public static final long PCKO_ANY = 0x7FFFFF23L; + + + /* The following hardware feature types are defined */ + public static final long CKH_MONOTONIC_COUNTER = 0x00000001L; + public static final long CKH_CLOCK = 0x00000002L; + public static final long CKH_VENDOR_DEFINED = 0x80000000L; + + /* the following key types are defined: */ + public static final long CKK_RSA = 0x00000000L; + public static final long CKK_DSA = 0x00000001L; + public static final long CKK_DH = 0x00000002L; + + /* CKK_ECDSA and CKK_KEA are new for v2.0 */ + /* CKK_ECDSA is deprecated in v2.11, CKK_EC is preferred. */ + public static final long CKK_ECDSA = 0x00000003L; + public static final long CKK_EC = 0x00000003L; + public static final long CKK_X9_42_DH = 0x00000004L; + public static final long CKK_KEA = 0x00000005L; + + public static final long CKK_GENERIC_SECRET = 0x00000010L; + public static final long CKK_RC2 = 0x00000011L; + public static final long CKK_RC4 = 0x00000012L; + public static final long CKK_DES = 0x00000013L; + public static final long CKK_DES2 = 0x00000014L; + public static final long CKK_DES3 = 0x00000015L; + + /* all these key types are new for v2.0 */ + public static final long CKK_CAST = 0x00000016L; + public static final long CKK_CAST3 = 0x00000017L; + /* CKK_CAST5 is deprecated in v2.11, CKK_CAST128 is preferred. */ + public static final long CKK_CAST5 = 0x00000018L; + /* CAST128=CAST5 */ + public static final long CKK_CAST128 = 0x00000018L; + public static final long CKK_RC5 = 0x00000019L; + public static final long CKK_IDEA = 0x0000001AL; + public static final long CKK_SKIPJACK = 0x0000001BL; + public static final long CKK_BATON = 0x0000001CL; + public static final long CKK_JUNIPER = 0x0000001DL; + public static final long CKK_CDMF = 0x0000001EL; + public static final long CKK_AES = 0x0000001FL; + // v2.20 + public static final long CKK_BLOWFISH = 0x00000020L; + + public static final long CKK_VENDOR_DEFINED = 0x80000000L; + + // new for v2.20 amendment 3 + //public static final long CKK_CAMELLIA = 0x00000025L; + //public static final long CKK_ARIA = 0x00000026L; + + // pseudo key type ANY (for template manager) + public static final long PCKK_ANY = 0x7FFFFF22L; + + public static final long PCKK_HMAC = 0x7FFFFF23L; + public static final long PCKK_SSLMAC = 0x7FFFFF24L; + public static final long PCKK_TLSPREMASTER = 0x7FFFFF25L; + public static final long PCKK_TLSRSAPREMASTER = 0x7FFFFF26L; + public static final long PCKK_TLSMASTER = 0x7FFFFF27L; + + /* The following certificate types are defined: */ + /* CKC_X_509_ATTR_CERT is new for v2.10 */ + public static final long CKC_X_509 = 0x00000000L; + public static final long CKC_X_509_ATTR_CERT = 0x00000001L; + public static final long CKC_VENDOR_DEFINED = 0x80000000L; + + + /* The following attribute types are defined: */ + public static final long CKA_CLASS = 0x00000000L; + public static final long CKA_TOKEN = 0x00000001L; + public static final long CKA_PRIVATE = 0x00000002L; + public static final long CKA_LABEL = 0x00000003L; + public static final long CKA_APPLICATION = 0x00000010L; + public static final long CKA_VALUE = 0x00000011L; + + /* CKA_OBJECT_ID is new for v2.10 */ + public static final long CKA_OBJECT_ID = 0x00000012L; + + public static final long CKA_CERTIFICATE_TYPE = 0x00000080L; + public static final long CKA_ISSUER = 0x00000081L; + public static final long CKA_SERIAL_NUMBER = 0x00000082L; + + /* CKA_AC_ISSUER, CKA_OWNER, and CKA_ATTR_TYPES are new L; + * for v2.10 */ + public static final long CKA_AC_ISSUER = 0x00000083L; + public static final long CKA_OWNER = 0x00000084L; + public static final long CKA_ATTR_TYPES = 0x00000085L; + + /* CKA_TRUSTED is new for v2.11 */ + public static final long CKA_TRUSTED = 0x00000086L; + + public static final long CKA_KEY_TYPE = 0x00000100L; + public static final long CKA_SUBJECT = 0x00000101L; + public static final long CKA_ID = 0x00000102L; + public static final long CKA_SENSITIVE = 0x00000103L; + public static final long CKA_ENCRYPT = 0x00000104L; + public static final long CKA_DECRYPT = 0x00000105L; + public static final long CKA_WRAP = 0x00000106L; + public static final long CKA_UNWRAP = 0x00000107L; + public static final long CKA_SIGN = 0x00000108L; + public static final long CKA_SIGN_RECOVER = 0x00000109L; + public static final long CKA_VERIFY = 0x0000010AL; + public static final long CKA_VERIFY_RECOVER = 0x0000010BL; + public static final long CKA_DERIVE = 0x0000010CL; + public static final long CKA_START_DATE = 0x00000110L; + public static final long CKA_END_DATE = 0x00000111L; + public static final long CKA_MODULUS = 0x00000120L; + public static final long CKA_MODULUS_BITS = 0x00000121L; + public static final long CKA_PUBLIC_EXPONENT = 0x00000122L; + public static final long CKA_PRIVATE_EXPONENT = 0x00000123L; + public static final long CKA_PRIME_1 = 0x00000124L; + public static final long CKA_PRIME_2 = 0x00000125L; + public static final long CKA_EXPONENT_1 = 0x00000126L; + public static final long CKA_EXPONENT_2 = 0x00000127L; + public static final long CKA_COEFFICIENT = 0x00000128L; + public static final long CKA_PRIME = 0x00000130L; + public static final long CKA_SUBPRIME = 0x00000131L; + public static final long CKA_BASE = 0x00000132L; + + /* CKA_PRIME_BITS and CKA_SUB_PRIME_BITS are new for v2.11 */ + public static final long CKA_PRIME_BITS = 0x00000133L; + public static final long CKA_SUB_PRIME_BITS = 0x00000134L; + + public static final long CKA_VALUE_BITS = 0x00000160L; + public static final long CKA_VALUE_LEN = 0x00000161L; + + /* CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE, + * CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS, + * and CKA_EC_POINT are new for v2.0 */ + public static final long CKA_EXTRACTABLE = 0x00000162L; + public static final long CKA_LOCAL = 0x00000163L; + public static final long CKA_NEVER_EXTRACTABLE = 0x00000164L; + public static final long CKA_ALWAYS_SENSITIVE = 0x00000165L; + + /* CKA_KEY_GEN_MECHANISM is new for v2.11 */ + public static final long CKA_KEY_GEN_MECHANISM = 0x00000166L; + + public static final long CKA_MODIFIABLE = 0x00000170L; + + /* CKA_ECDSA_PARAMS is deprecated in v2.11, + * CKA_EC_PARAMS is preferred. */ + public static final long CKA_ECDSA_PARAMS = 0x00000180L; + public static final long CKA_EC_PARAMS = 0x00000180L; + public static final long CKA_EC_POINT = 0x00000181L; + + /* CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, + * CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, and CKA_HAS_RESET + * are new for v2.10 */ + public static final long CKA_SECONDARY_AUTH = 0x00000200L; + public static final long CKA_AUTH_PIN_FLAGS = 0x00000201L; + public static final long CKA_HW_FEATURE_TYPE = 0x00000300L; + public static final long CKA_RESET_ON_INIT = 0x00000301L; + public static final long CKA_HAS_RESET = 0x00000302L; + + public static final long CKA_VENDOR_DEFINED = 0x80000000L; + + /* the following mechanism types are defined: */ + public static final long CKM_RSA_PKCS_KEY_PAIR_GEN = 0x00000000L; + public static final long CKM_RSA_PKCS = 0x00000001L; + public static final long CKM_RSA_9796 = 0x00000002L; + public static final long CKM_RSA_X_509 = 0x00000003L; + + /* CKM_MD2_RSA_PKCS, CKM_MD5_RSA_PKCS, and CKM_SHA1_RSA_PKCS + * are new for v2.0. They are mechanisms which hash and sign */ + public static final long CKM_MD2_RSA_PKCS = 0x00000004L; + public static final long CKM_MD5_RSA_PKCS = 0x00000005L; + public static final long CKM_SHA1_RSA_PKCS = 0x00000006L; + + /* CKM_RIPEMD128_RSA_PKCS, CKM_RIPEMD160_RSA_PKCS, and + * CKM_RSA_PKCS_OAEP are new for v2.10 */ + public static final long CKM_RIPEMD128_RSA_PKCS = 0x00000007L; + public static final long CKM_RIPEMD160_RSA_PKCS = 0x00000008L; + public static final long CKM_RSA_PKCS_OAEP = 0x00000009L; + + /* CKM_RSA_X9_31_KEY_PAIR_GEN, CKM_RSA_X9_31, CKM_SHA1_RSA_X9_31, + * CKM_RSA_PKCS_PSS, and CKM_SHA1_RSA_PKCS_PSS are new for v2.11 */ + public static final long CKM_RSA_X9_31_KEY_PAIR_GEN = 0x0000000AL; + public static final long CKM_RSA_X9_31 = 0x0000000BL; + public static final long CKM_SHA1_RSA_X9_31 = 0x0000000CL; + public static final long CKM_RSA_PKCS_PSS = 0x0000000DL; + public static final long CKM_SHA1_RSA_PKCS_PSS = 0x0000000EL; + + public static final long CKM_DSA_KEY_PAIR_GEN = 0x00000010L; + public static final long CKM_DSA = 0x00000011L; + public static final long CKM_DSA_SHA1 = 0x00000012L; + public static final long CKM_DH_PKCS_KEY_PAIR_GEN = 0x00000020L; + public static final long CKM_DH_PKCS_DERIVE = 0x00000021L; + + /* CKM_X9_42_DH_KEY_PAIR_GEN, CKM_X9_42_DH_DERIVE, + * CKM_X9_42_DH_HYBRID_DERIVE, and CKM_X9_42_MQV_DERIVE are new for + * v2.11 */ + public static final long CKM_X9_42_DH_KEY_PAIR_GEN = 0x00000030L; + public static final long CKM_X9_42_DH_DERIVE = 0x00000031L; + public static final long CKM_X9_42_DH_HYBRID_DERIVE = 0x00000032L; + public static final long CKM_X9_42_MQV_DERIVE = 0x00000033L; + + // v2.20 + public static final long CKM_SHA256_RSA_PKCS = 0x00000040L; + public static final long CKM_SHA384_RSA_PKCS = 0x00000041L; + public static final long CKM_SHA512_RSA_PKCS = 0x00000042L; + + public static final long CKM_RC2_KEY_GEN = 0x00000100L; + public static final long CKM_RC2_ECB = 0x00000101L; + public static final long CKM_RC2_CBC = 0x00000102L; + public static final long CKM_RC2_MAC = 0x00000103L; + + /* CKM_RC2_MAC_GENERAL and CKM_RC2_CBC_PAD are new for v2.0 */ + public static final long CKM_RC2_MAC_GENERAL = 0x00000104L; + public static final long CKM_RC2_CBC_PAD = 0x00000105L; + + public static final long CKM_RC4_KEY_GEN = 0x00000110L; + public static final long CKM_RC4 = 0x00000111L; + public static final long CKM_DES_KEY_GEN = 0x00000120L; + public static final long CKM_DES_ECB = 0x00000121L; + public static final long CKM_DES_CBC = 0x00000122L; + public static final long CKM_DES_MAC = 0x00000123L; + + /* CKM_DES_MAC_GENERAL and CKM_DES_CBC_PAD are new for v2.0 */ + public static final long CKM_DES_MAC_GENERAL = 0x00000124L; + public static final long CKM_DES_CBC_PAD = 0x00000125L; + + public static final long CKM_DES2_KEY_GEN = 0x00000130L; + public static final long CKM_DES3_KEY_GEN = 0x00000131L; + public static final long CKM_DES3_ECB = 0x00000132L; + public static final long CKM_DES3_CBC = 0x00000133L; + public static final long CKM_DES3_MAC = 0x00000134L; + + /* CKM_DES3_MAC_GENERAL, CKM_DES3_CBC_PAD, CKM_CDMF_KEY_GEN, + * CKM_CDMF_ECB, CKM_CDMF_CBC, CKM_CDMF_MAC, + * CKM_CDMF_MAC_GENERAL, and CKM_CDMF_CBC_PAD are new for v2.0 */ + public static final long CKM_DES3_MAC_GENERAL = 0x00000135L; + public static final long CKM_DES3_CBC_PAD = 0x00000136L; + public static final long CKM_CDMF_KEY_GEN = 0x00000140L; + public static final long CKM_CDMF_ECB = 0x00000141L; + public static final long CKM_CDMF_CBC = 0x00000142L; + public static final long CKM_CDMF_MAC = 0x00000143L; + public static final long CKM_CDMF_MAC_GENERAL = 0x00000144L; + public static final long CKM_CDMF_CBC_PAD = 0x00000145L; + + public static final long CKM_MD2 = 0x00000200L; + + /* CKM_MD2_HMAC and CKM_MD2_HMAC_GENERAL are new for v2.0 */ + public static final long CKM_MD2_HMAC = 0x00000201L; + public static final long CKM_MD2_HMAC_GENERAL = 0x00000202L; + + public static final long CKM_MD5 = 0x00000210L; + + /* CKM_MD5_HMAC and CKM_MD5_HMAC_GENERAL are new for v2.0 */ + public static final long CKM_MD5_HMAC = 0x00000211L; + public static final long CKM_MD5_HMAC_GENERAL = 0x00000212L; + + public static final long CKM_SHA_1 = 0x00000220L; + + /* CKM_SHA_1_HMAC and CKM_SHA_1_HMAC_GENERAL are new for v2.0 */ + public static final long CKM_SHA_1_HMAC = 0x00000221L; + public static final long CKM_SHA_1_HMAC_GENERAL = 0x00000222L; + + /* CKM_RIPEMD128, CKM_RIPEMD128_HMAC, + * CKM_RIPEMD128_HMAC_GENERAL, CKM_RIPEMD160, CKM_RIPEMD160_HMAC, + * and CKM_RIPEMD160_HMAC_GENERAL are new for v2.10 */ + public static final long CKM_RIPEMD128 = 0x00000230L; + public static final long CKM_RIPEMD128_HMAC = 0x00000231L; + public static final long CKM_RIPEMD128_HMAC_GENERAL = 0x00000232L; + public static final long CKM_RIPEMD160 = 0x00000240L; + public static final long CKM_RIPEMD160_HMAC = 0x00000241L; + public static final long CKM_RIPEMD160_HMAC_GENERAL = 0x00000242L; + + // v2.20 + public static final long CKM_SHA256 = 0x00000250L; + public static final long CKM_SHA256_HMAC = 0x00000251L; + public static final long CKM_SHA256_HMAC_GENERAL = 0x00000252L; + + public static final long CKM_SHA384 = 0x00000260L; + public static final long CKM_SHA384_HMAC = 0x00000261L; + public static final long CKM_SHA384_HMAC_GENERAL = 0x00000262L; + + public static final long CKM_SHA512 = 0x00000270L; + public static final long CKM_SHA512_HMAC = 0x00000271L; + public static final long CKM_SHA512_HMAC_GENERAL = 0x00000272L; + + /* All of the following mechanisms are new for v2.0 */ + /* Note that CAST128 and CAST5 are the same algorithm */ + public static final long CKM_CAST_KEY_GEN = 0x00000300L; + public static final long CKM_CAST_ECB = 0x00000301L; + public static final long CKM_CAST_CBC = 0x00000302L; + public static final long CKM_CAST_MAC = 0x00000303L; + public static final long CKM_CAST_MAC_GENERAL = 0x00000304L; + public static final long CKM_CAST_CBC_PAD = 0x00000305L; + public static final long CKM_CAST3_KEY_GEN = 0x00000310L; + public static final long CKM_CAST3_ECB = 0x00000311L; + public static final long CKM_CAST3_CBC = 0x00000312L; + public static final long CKM_CAST3_MAC = 0x00000313L; + public static final long CKM_CAST3_MAC_GENERAL = 0x00000314L; + public static final long CKM_CAST3_CBC_PAD = 0x00000315L; + public static final long CKM_CAST5_KEY_GEN = 0x00000320L; + public static final long CKM_CAST128_KEY_GEN = 0x00000320L; + public static final long CKM_CAST5_ECB = 0x00000321L; + public static final long CKM_CAST128_ECB = 0x00000321L; + public static final long CKM_CAST5_CBC = 0x00000322L; + public static final long CKM_CAST128_CBC = 0x00000322L; + public static final long CKM_CAST5_MAC = 0x00000323L; + public static final long CKM_CAST128_MAC = 0x00000323L; + public static final long CKM_CAST5_MAC_GENERAL = 0x00000324L; + public static final long CKM_CAST128_MAC_GENERAL = 0x00000324L; + public static final long CKM_CAST5_CBC_PAD = 0x00000325L; + public static final long CKM_CAST128_CBC_PAD = 0x00000325L; + public static final long CKM_RC5_KEY_GEN = 0x00000330L; + public static final long CKM_RC5_ECB = 0x00000331L; + public static final long CKM_RC5_CBC = 0x00000332L; + public static final long CKM_RC5_MAC = 0x00000333L; + public static final long CKM_RC5_MAC_GENERAL = 0x00000334L; + public static final long CKM_RC5_CBC_PAD = 0x00000335L; + public static final long CKM_IDEA_KEY_GEN = 0x00000340L; + public static final long CKM_IDEA_ECB = 0x00000341L; + public static final long CKM_IDEA_CBC = 0x00000342L; + public static final long CKM_IDEA_MAC = 0x00000343L; + public static final long CKM_IDEA_MAC_GENERAL = 0x00000344L; + public static final long CKM_IDEA_CBC_PAD = 0x00000345L; + public static final long CKM_GENERIC_SECRET_KEY_GEN = 0x00000350L; + public static final long CKM_CONCATENATE_BASE_AND_KEY = 0x00000360L; + public static final long CKM_CONCATENATE_BASE_AND_DATA = 0x00000362L; + public static final long CKM_CONCATENATE_DATA_AND_BASE = 0x00000363L; + public static final long CKM_XOR_BASE_AND_DATA = 0x00000364L; + public static final long CKM_EXTRACT_KEY_FROM_KEY = 0x00000365L; + public static final long CKM_SSL3_PRE_MASTER_KEY_GEN = 0x00000370L; + public static final long CKM_SSL3_MASTER_KEY_DERIVE = 0x00000371L; + public static final long CKM_SSL3_KEY_AND_MAC_DERIVE = 0x00000372L; + + /* CKM_SSL3_MASTER_KEY_DERIVE_DH, CKM_TLS_PRE_MASTER_KEY_GEN, + * CKM_TLS_MASTER_KEY_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, and + * CKM_TLS_MASTER_KEY_DERIVE_DH are new for v2.11 */ + public static final long CKM_SSL3_MASTER_KEY_DERIVE_DH = 0x00000373L; + public static final long CKM_TLS_PRE_MASTER_KEY_GEN = 0x00000374L; + public static final long CKM_TLS_MASTER_KEY_DERIVE = 0x00000375L; + public static final long CKM_TLS_KEY_AND_MAC_DERIVE = 0x00000376L; + public static final long CKM_TLS_MASTER_KEY_DERIVE_DH = 0x00000377L; + public static final long CKM_TLS_PRF = 0x00000378L; + + public static final long CKM_SSL3_MD5_MAC = 0x00000380L; + public static final long CKM_SSL3_SHA1_MAC = 0x00000381L; + public static final long CKM_MD5_KEY_DERIVATION = 0x00000390L; + public static final long CKM_MD2_KEY_DERIVATION = 0x00000391L; + public static final long CKM_SHA1_KEY_DERIVATION = 0x00000392L; + + // v2.20 + public static final long CKM_SHA256_KEY_DERIVATION = 0x00000393L; + public static final long CKM_SHA384_KEY_DERIVATION = 0x00000394L; + public static final long CKM_SHA512_KEY_DERIVATION = 0x00000395L; + + public static final long CKM_PBE_MD2_DES_CBC = 0x000003A0L; + public static final long CKM_PBE_MD5_DES_CBC = 0x000003A1L; + public static final long CKM_PBE_MD5_CAST_CBC = 0x000003A2L; + public static final long CKM_PBE_MD5_CAST3_CBC = 0x000003A3L; + public static final long CKM_PBE_MD5_CAST5_CBC = 0x000003A4L; + public static final long CKM_PBE_MD5_CAST128_CBC = 0x000003A4L; + public static final long CKM_PBE_SHA1_CAST5_CBC = 0x000003A5L; + public static final long CKM_PBE_SHA1_CAST128_CBC = 0x000003A5L; + public static final long CKM_PBE_SHA1_RC4_128 = 0x000003A6L; + public static final long CKM_PBE_SHA1_RC4_40 = 0x000003A7L; + public static final long CKM_PBE_SHA1_DES3_EDE_CBC = 0x000003A8L; + public static final long CKM_PBE_SHA1_DES2_EDE_CBC = 0x000003A9L; + public static final long CKM_PBE_SHA1_RC2_128_CBC = 0x000003AAL; + public static final long CKM_PBE_SHA1_RC2_40_CBC = 0x000003ABL; + + /* CKM_PKCS5_PBKD2 is new for v2.10 */ + public static final long CKM_PKCS5_PBKD2 = 0x000003B0L; + + public static final long CKM_PBA_SHA1_WITH_SHA1_HMAC = 0x000003C0L; + public static final long CKM_KEY_WRAP_LYNKS = 0x00000400L; + public static final long CKM_KEY_WRAP_SET_OAEP = 0x00000401L; + + /* Fortezza mechanisms */ + public static final long CKM_SKIPJACK_KEY_GEN = 0x00001000L; + public static final long CKM_SKIPJACK_ECB64 = 0x00001001L; + public static final long CKM_SKIPJACK_CBC64 = 0x00001002L; + public static final long CKM_SKIPJACK_OFB64 = 0x00001003L; + public static final long CKM_SKIPJACK_CFB64 = 0x00001004L; + public static final long CKM_SKIPJACK_CFB32 = 0x00001005L; + public static final long CKM_SKIPJACK_CFB16 = 0x00001006L; + public static final long CKM_SKIPJACK_CFB8 = 0x00001007L; + public static final long CKM_SKIPJACK_WRAP = 0x00001008L; + public static final long CKM_SKIPJACK_PRIVATE_WRAP = 0x00001009L; + public static final long CKM_SKIPJACK_RELAYX = 0x0000100AL; + public static final long CKM_KEA_KEY_PAIR_GEN = 0x00001010L; + public static final long CKM_KEA_KEY_DERIVE = 0x00001011L; + public static final long CKM_FORTEZZA_TIMESTAMP = 0x00001020L; + public static final long CKM_BATON_KEY_GEN = 0x00001030L; + public static final long CKM_BATON_ECB128 = 0x00001031L; + public static final long CKM_BATON_ECB96 = 0x00001032L; + public static final long CKM_BATON_CBC128 = 0x00001033L; + public static final long CKM_BATON_COUNTER = 0x00001034L; + public static final long CKM_BATON_SHUFFLE = 0x00001035L; + public static final long CKM_BATON_WRAP = 0x00001036L; + + /* CKM_ECDSA_KEY_PAIR_GEN is deprecated in v2.11, + * CKM_EC_KEY_PAIR_GEN is preferred */ + public static final long CKM_ECDSA_KEY_PAIR_GEN = 0x00001040L; + public static final long CKM_EC_KEY_PAIR_GEN = 0x00001040L; + + public static final long CKM_ECDSA = 0x00001041L; + public static final long CKM_ECDSA_SHA1 = 0x00001042L; + + /* CKM_ECDH1_DERIVE, CKM_ECDH1_COFACTOR_DERIVE, and CKM_ECMQV_DERIVE + * are new for v2.11 */ + public static final long CKM_ECDH1_DERIVE = 0x00001050L; + public static final long CKM_ECDH1_COFACTOR_DERIVE = 0x00001051L; + public static final long CKM_ECMQV_DERIVE = 0x00001052L; + + public static final long CKM_JUNIPER_KEY_GEN = 0x00001060L; + public static final long CKM_JUNIPER_ECB128 = 0x00001061L; + public static final long CKM_JUNIPER_CBC128 = 0x00001062L; + public static final long CKM_JUNIPER_COUNTER = 0x00001063L; + public static final long CKM_JUNIPER_SHUFFLE = 0x00001064L; + public static final long CKM_JUNIPER_WRAP = 0x00001065L; + public static final long CKM_FASTHASH = 0x00001070L; + + /* CKM_AES_KEY_GEN, CKM_AES_ECB, CKM_AES_CBC, CKM_AES_MAC, + * CKM_AES_MAC_GENERAL, CKM_AES_CBC_PAD, CKM_DSA_PARAMETER_GEN, + * CKM_DH_PKCS_PARAMETER_GEN, and CKM_X9_42_DH_PARAMETER_GEN are + * new for v2.11 */ + public static final long CKM_AES_KEY_GEN = 0x00001080L; + public static final long CKM_AES_ECB = 0x00001081L; + public static final long CKM_AES_CBC = 0x00001082L; + public static final long CKM_AES_MAC = 0x00001083L; + public static final long CKM_AES_MAC_GENERAL = 0x00001084L; + public static final long CKM_AES_CBC_PAD = 0x00001085L; + // v2.20 + public static final long CKM_BLOWFISH_KEY_GEN = 0x00001090L; + public static final long CKM_BLOWFISH_CBC = 0x00001091L; + public static final long CKM_DSA_PARAMETER_GEN = 0x00002000L; + public static final long CKM_DH_PKCS_PARAMETER_GEN = 0x00002001L; + public static final long CKM_X9_42_DH_PARAMETER_GEN = 0x00002002L; + + public static final long CKM_VENDOR_DEFINED = 0x80000000L; + + // new for v2.20 amendment 3 + public static final long CKM_SHA224 = 0x00000255L; + public static final long CKM_SHA224_HMAC = 0x00000256L; + public static final long CKM_SHA224_HMAC_GENERAL = 0x00000257L; + public static final long CKM_SHA224_KEY_DERIVATION = 0x00000396L; + public static final long CKM_SHA224_RSA_PKCS = 0x00000046L; + public static final long CKM_SHA224_RSA_PKCS_PSS = 0x00000047L; + public static final long CKM_AES_CTR = 0x00001086L; + /* + public static final long CKM_CAMELLIA_KEY_GEN = 0x00000550L; + public static final long CKM_CAMELLIA_ECB = 0x00000551L; + public static final long CKM_CAMELLIA_CBC = 0x00000552L; + public static final long CKM_CAMELLIA_MAC = 0x00000553L; + public static final long CKM_CAMELLIA_MAC_GENERAL = 0x00000554L; + public static final long CKM_CAMELLIA_CBC_PAD = 0x00000555L; + public static final long CKM_CAMELLIA_ECB_ENCRYPT_DATA = 0x00000556L; + public static final long CKM_CAMELLIA_CBC_ENCRYPT_DATA = 0x00000557L; + public static final long CKM_CAMELLIA_CTR = 0x00000558L; + public static final long CKM_ARIA_KEY_GEN = 0x00000560L; + public static final long CKM_ARIA_ECB = 0x00000561L; + public static final long CKM_ARIA_CBC = 0x00000562L; + public static final long CKM_ARIA_MAC = 0x00000563L; + public static final long CKM_ARIA_MAC_GENERAL = 0x00000564L; + public static final long CKM_ARIA_CBC_PAD = 0x00000565L; + public static final long CKM_ARIA_ECB_ENCRYPT_DATA = 0x00000566L; + public static final long CKM_ARIA_CBC_ENCRYPT_DATA = 0x00000567L; + */ + + // NSS private + public static final long CKM_NSS_TLS_PRF_GENERAL = 0x80000373L; + + // ids for our pseudo mechanisms SecureRandom and KeyStore + public static final long PCKM_SECURERANDOM = 0x7FFFFF20L; + public static final long PCKM_KEYSTORE = 0x7FFFFF21L; + + /* The flags are defined as follows: + * Bit Flag Mask Meaning */ + /* performed by HW */ + public static final long CKF_HW = 0x00000001L; + + /* The flags CKF_ENCRYPT, CKF_DECRYPT, CKF_DIGEST, CKF_SIGN, + * CKG_SIGN_RECOVER, CKF_VERIFY, CKF_VERIFY_RECOVER, + * CKF_GENERATE, CKF_GENERATE_KEY_PAIR, CKF_WRAP, CKF_UNWRAP, + * and CKF_DERIVE are new for v2.0. They specify whether or not + * a mechanism can be used for a particular task */ + public static final long CKF_ENCRYPT = 0x00000100L; + public static final long CKF_DECRYPT = 0x00000200L; + public static final long CKF_DIGEST = 0x00000400L; + public static final long CKF_SIGN = 0x00000800L; + public static final long CKF_SIGN_RECOVER = 0x00001000L; + public static final long CKF_VERIFY = 0x00002000L; + public static final long CKF_VERIFY_RECOVER = 0x00004000L; + public static final long CKF_GENERATE = 0x00008000L; + public static final long CKF_GENERATE_KEY_PAIR = 0x00010000L; + public static final long CKF_WRAP = 0x00020000L; + public static final long CKF_UNWRAP = 0x00040000L; + public static final long CKF_DERIVE = 0x00080000L; + + /* CKF_EC_F_P, CKF_EC_F_2M, CKF_EC_ECPARAMETERS, CKF_EC_NAMEDCURVE, + * CKF_EC_UNCOMPRESS, and CKF_EC_COMPRESS are new for v2.11. They + * describe a token's EC capabilities not available in mechanism + * information. */ + public static final long CKF_EC_F_P = 0x00100000L; + public static final long CKF_EC_F_2M = 0x00200000L; + public static final long CKF_EC_ECPARAMETERS = 0x00400000L; + public static final long CKF_EC_NAMEDCURVE = 0x00800000L; + public static final long CKF_EC_UNCOMPRESS = 0x01000000L; + public static final long CKF_EC_COMPRESS = 0x02000000L; + + /* FALSE for 2.01 */ + public static final long CKF_EXTENSION = 0x80000000L; + + + /* CK_RV is a value that identifies the return value of a + * Cryptoki function */ + /* CK_RV was changed from CK_USHORT to CK_ULONG for v2.0 */ + public static final long CKR_OK = 0x00000000L; + public static final long CKR_CANCEL = 0x00000001L; + public static final long CKR_HOST_MEMORY = 0x00000002L; + public static final long CKR_SLOT_ID_INVALID = 0x00000003L; + + /* CKR_FLAGS_INVALID was removed for v2.0 */ + + /* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */ + public static final long CKR_GENERAL_ERROR = 0x00000005L; + public static final long CKR_FUNCTION_FAILED = 0x00000006L; + + /* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS, + * and CKR_CANT_LOCK are new for v2.01 */ + public static final long CKR_ARGUMENTS_BAD = 0x00000007L; + public static final long CKR_NO_EVENT = 0x00000008L; + public static final long CKR_NEED_TO_CREATE_THREADS = 0x00000009L; + public static final long CKR_CANT_LOCK = 0x0000000AL; + + public static final long CKR_ATTRIBUTE_READ_ONLY = 0x00000010L; + public static final long CKR_ATTRIBUTE_SENSITIVE = 0x00000011L; + public static final long CKR_ATTRIBUTE_TYPE_INVALID = 0x00000012L; + public static final long CKR_ATTRIBUTE_VALUE_INVALID = 0x00000013L; + public static final long CKR_DATA_INVALID = 0x00000020L; + public static final long CKR_DATA_LEN_RANGE = 0x00000021L; + public static final long CKR_DEVICE_ERROR = 0x00000030L; + public static final long CKR_DEVICE_MEMORY = 0x00000031L; + public static final long CKR_DEVICE_REMOVED = 0x00000032L; + public static final long CKR_ENCRYPTED_DATA_INVALID = 0x00000040L; + public static final long CKR_ENCRYPTED_DATA_LEN_RANGE = 0x00000041L; + public static final long CKR_FUNCTION_CANCELED = 0x00000050L; + public static final long CKR_FUNCTION_NOT_PARALLEL = 0x00000051L; + + /* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */ + public static final long CKR_FUNCTION_NOT_SUPPORTED = 0x00000054L; + + public static final long CKR_KEY_HANDLE_INVALID = 0x00000060L; + + /* CKR_KEY_SENSITIVE was removed for v2.0 */ + + public static final long CKR_KEY_SIZE_RANGE = 0x00000062L; + public static final long CKR_KEY_TYPE_INCONSISTENT = 0x00000063L; + + /* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED, + * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED, + * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for + * v2.0 */ + public static final long CKR_KEY_NOT_NEEDED = 0x00000064L; + public static final long CKR_KEY_CHANGED = 0x00000065L; + public static final long CKR_KEY_NEEDED = 0x00000066L; + public static final long CKR_KEY_INDIGESTIBLE = 0x00000067L; + public static final long CKR_KEY_FUNCTION_NOT_PERMITTED = 0x00000068L; + public static final long CKR_KEY_NOT_WRAPPABLE = 0x00000069L; + public static final long CKR_KEY_UNEXTRACTABLE = 0x0000006AL; + + public static final long CKR_MECHANISM_INVALID = 0x00000070L; + public static final long CKR_MECHANISM_PARAM_INVALID = 0x00000071L; + + /* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID + * were removed for v2.0 */ + public static final long CKR_OBJECT_HANDLE_INVALID = 0x00000082L; + public static final long CKR_OPERATION_ACTIVE = 0x00000090L; + public static final long CKR_OPERATION_NOT_INITIALIZED = 0x00000091L; + public static final long CKR_PIN_INCORRECT = 0x000000A0L; + public static final long CKR_PIN_INVALID = 0x000000A1L; + public static final long CKR_PIN_LEN_RANGE = 0x000000A2L; + + /* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */ + public static final long CKR_PIN_EXPIRED = 0x000000A3L; + public static final long CKR_PIN_LOCKED = 0x000000A4L; + + public static final long CKR_SESSION_CLOSED = 0x000000B0L; + public static final long CKR_SESSION_COUNT = 0x000000B1L; + public static final long CKR_SESSION_HANDLE_INVALID = 0x000000B3L; + public static final long CKR_SESSION_PARALLEL_NOT_SUPPORTED = 0x000000B4L; + public static final long CKR_SESSION_READ_ONLY = 0x000000B5L; + public static final long CKR_SESSION_EXISTS = 0x000000B6L; + + /* CKR_SESSION_READ_ONLY_EXISTS and + * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */ + public static final long CKR_SESSION_READ_ONLY_EXISTS = 0x000000B7L; + public static final long CKR_SESSION_READ_WRITE_SO_EXISTS = 0x000000B8L; + + public static final long CKR_SIGNATURE_INVALID = 0x000000C0L; + public static final long CKR_SIGNATURE_LEN_RANGE = 0x000000C1L; + public static final long CKR_TEMPLATE_INCOMPLETE = 0x000000D0L; + public static final long CKR_TEMPLATE_INCONSISTENT = 0x000000D1L; + public static final long CKR_TOKEN_NOT_PRESENT = 0x000000E0L; + public static final long CKR_TOKEN_NOT_RECOGNIZED = 0x000000E1L; + public static final long CKR_TOKEN_WRITE_PROTECTED = 0x000000E2L; + public static final long CKR_UNWRAPPING_KEY_HANDLE_INVALID = 0x000000F0L; + public static final long CKR_UNWRAPPING_KEY_SIZE_RANGE = 0x000000F1L; + public static final long CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT = 0x000000F2L; + public static final long CKR_USER_ALREADY_LOGGED_IN = 0x00000100L; + public static final long CKR_USER_NOT_LOGGED_IN = 0x00000101L; + public static final long CKR_USER_PIN_NOT_INITIALIZED = 0x00000102L; + public static final long CKR_USER_TYPE_INVALID = 0x00000103L; + + /* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES + * are new to v2.01 */ + public static final long CKR_USER_ANOTHER_ALREADY_LOGGED_IN = 0x00000104L; + public static final long CKR_USER_TOO_MANY_TYPES = 0x00000105L; + + public static final long CKR_WRAPPED_KEY_INVALID = 0x00000110L; + public static final long CKR_WRAPPED_KEY_LEN_RANGE = 0x00000112L; + public static final long CKR_WRAPPING_KEY_HANDLE_INVALID = 0x00000113L; + public static final long CKR_WRAPPING_KEY_SIZE_RANGE = 0x00000114L; + public static final long CKR_WRAPPING_KEY_TYPE_INCONSISTENT = 0x00000115L; + public static final long CKR_RANDOM_SEED_NOT_SUPPORTED = 0x00000120L; + + /* These are new to v2.0 */ + public static final long CKR_RANDOM_NO_RNG = 0x00000121L; + + /* These are new to v2.11 */ + public static final long CKR_DOMAIN_PARAMS_INVALID = 0x00000130L; + + /* These are new to v2.0 */ + public static final long CKR_BUFFER_TOO_SMALL = 0x00000150L; + public static final long CKR_SAVED_STATE_INVALID = 0x00000160L; + public static final long CKR_INFORMATION_SENSITIVE = 0x00000170L; + public static final long CKR_STATE_UNSAVEABLE = 0x00000180L; + + /* These are new to v2.01 */ + public static final long CKR_CRYPTOKI_NOT_INITIALIZED = 0x00000190L; + public static final long CKR_CRYPTOKI_ALREADY_INITIALIZED = 0x00000191L; + public static final long CKR_MUTEX_BAD = 0x000001A0L; + public static final long CKR_MUTEX_NOT_LOCKED = 0x000001A1L; + + public static final long CKR_VENDOR_DEFINED = 0x80000000L; + + + /* flags: bit flags that provide capabilities of the slot + * Bit Flag = Mask + */ + public static final long CKF_LIBRARY_CANT_CREATE_OS_THREADS = 0x00000001L; + public static final long CKF_OS_LOCKING_OK = 0x00000002L; + + + /* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */ + public static final long CKF_DONT_BLOCK = 1L; + + + /* The following MGFs are defined */ + public static final long CKG_MGF1_SHA1 = 0x00000001L; + // new for v2.20 amendment 3 + public static final long CKG_MGF1_SHA224 = 0x00000005L; + + /* The following encoding parameter sources are defined */ + public static final long CKZ_DATA_SPECIFIED = 0x00000001L; + + + /* The following PRFs are defined in PKCS #5 v2.0. */ + public static final long CKP_PKCS5_PBKD2_HMAC_SHA1 = 0x00000001L; + + + /* The following salt value sources are defined in PKCS #5 v2.0. */ + public static final long CKZ_SALT_SPECIFIED = 0x00000001L; + + /* the following EC Key Derivation Functions are defined */ + public static final long CKD_NULL = 0x00000001L; + public static final long CKD_SHA1_KDF = 0x00000002L; + + /* the following X9.42 Diffie-Hellman Key Derivation Functions are defined */ + public static final long CKD_SHA1_KDF_ASN1 = 0x00000003L; + public static final long CKD_SHA1_KDF_CONCATENATE = 0x00000004L; + + + // private NSS attribute (for DSA and DH private keys) + public static final long CKA_NETSCAPE_DB = 0xD5A0DB00L; + + // base number of NSS private attributes + public static final long CKA_NETSCAPE_BASE = 0x80000000L + 0x4E534350L; + + // object type for NSS trust + public static final long CKO_NETSCAPE_TRUST = CKA_NETSCAPE_BASE + 3; + + // base number for NSS trust attributes + public static final long CKA_NETSCAPE_TRUST_BASE = CKA_NETSCAPE_BASE + 0x2000; + + // attributes for NSS trust + public static final long CKA_NETSCAPE_TRUST_SERVER_AUTH = CKA_NETSCAPE_TRUST_BASE + 8; + public static final long CKA_NETSCAPE_TRUST_CLIENT_AUTH = CKA_NETSCAPE_TRUST_BASE + 9; + public static final long CKA_NETSCAPE_TRUST_CODE_SIGNING = CKA_NETSCAPE_TRUST_BASE + 10; + public static final long CKA_NETSCAPE_TRUST_EMAIL_PROTECTION = CKA_NETSCAPE_TRUST_BASE + 11; + public static final long CKA_NETSCAPE_CERT_SHA1_HASH = CKA_NETSCAPE_TRUST_BASE + 100; + public static final long CKA_NETSCAPE_CERT_MD5_HASH = CKA_NETSCAPE_TRUST_BASE + 101; + + // trust values for each of the NSS trust attributes + public static final long CKT_NETSCAPE_TRUSTED = CKA_NETSCAPE_BASE + 1; + public static final long CKT_NETSCAPE_TRUSTED_DELEGATOR = CKA_NETSCAPE_BASE + 2; + public static final long CKT_NETSCAPE_UNTRUSTED = CKA_NETSCAPE_BASE + 3; + public static final long CKT_NETSCAPE_MUST_VERIFY = CKA_NETSCAPE_BASE + 4; + public static final long CKT_NETSCAPE_TRUST_UNKNOWN = CKA_NETSCAPE_BASE + 5; /* default */ + public static final long CKT_NETSCAPE_VALID = CKA_NETSCAPE_BASE + 10; + public static final long CKT_NETSCAPE_VALID_DELEGATOR = CKA_NETSCAPE_BASE + 11; + +} diff --git a/src/sun/security/pkcs11/wrapper/PKCS11Exception.java b/src/sun/security/pkcs11/wrapper/PKCS11Exception.java new file mode 100644 index 00000000..68163a04 --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/PKCS11Exception.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + +import java.util.*; + + +/** + * This is the superclass of all checked exceptions used by this package. An + * exception of this class indicates that a function call to the underlying + * PKCS#11 module returned a value not equal to CKR_OK. The application can get + * the returned value by calling getErrorCode(). A return value not equal to + * CKR_OK is the only reason for such an exception to be thrown. + * PKCS#11 defines the meaning of an error-code, which may depend on the + * context in which the error occurs. + * + * @author Karl Scheibelhofer + * @invariants + */ +public class PKCS11Exception extends Exception { + private static final long serialVersionUID = 4877072363729195L; + + /** + * The code of the error which was the reason for this exception. + */ + protected long errorCode_; + + private static final Map errorMap; + + static { + int[] errorCodes = new int[] { + 0x00000000, + 0x00000001, + 0x00000002, + 0x00000003, + 0x00000005, + 0x00000006, + 0x00000007, + 0x00000008, + 0x00000009, + 0x0000000A, + 0x00000010, + 0x00000011, + 0x00000012, + 0x00000013, + 0x00000020, + 0x00000021, + 0x00000030, + 0x00000031, + 0x00000032, + 0x00000040, + 0x00000041, + 0x00000050, + 0x00000051, + 0x00000054, + 0x00000060, + 0x00000062, + 0x00000063, + 0x00000064, + 0x00000065, + 0x00000066, + 0x00000067, + 0x00000068, + 0x00000069, + 0x0000006A, + 0x00000070, + 0x00000071, + 0x00000082, + 0x00000090, + 0x00000091, + 0x000000A0, + 0x000000A1, + 0x000000A2, + 0x000000A3, + 0x000000A4, + 0x000000B0, + 0x000000B1, + 0x000000B3, + 0x000000B4, + 0x000000B5, + 0x000000B6, + 0x000000B7, + 0x000000B8, + 0x000000C0, + 0x000000C1, + 0x000000D0, + 0x000000D1, + 0x000000E0, + 0x000000E1, + 0x000000E2, + 0x000000F0, + 0x000000F1, + 0x000000F2, + 0x00000100, + 0x00000101, + 0x00000102, + 0x00000103, + 0x00000104, + 0x00000105, + 0x00000110, + 0x00000112, + 0x00000113, + 0x00000114, + 0x00000115, + 0x00000120, + 0x00000121, + 0x00000130, + 0x00000150, + 0x00000160, + 0x00000170, + 0x00000180, + 0x00000190, + 0x00000191, + 0x000001A0, + 0x000001A1, + 0x00000200, + 0x80000000, + }; + String[] errorMessages = new String[] { + "CKR_OK", + "CKR_CANCEL", + "CKR_HOST_MEMORY", + "CKR_SLOT_ID_INVALID", + "CKR_GENERAL_ERROR", + "CKR_FUNCTION_FAILED", + "CKR_ARGUMENTS_BAD", + "CKR_NO_EVENT", + "CKR_NEED_TO_CREATE_THREADS", + "CKR_CANT_LOCK", + "CKR_ATTRIBUTE_READ_ONLY", + "CKR_ATTRIBUTE_SENSITIVE", + "CKR_ATTRIBUTE_TYPE_INVALID", + "CKR_ATTRIBUTE_VALUE_INVALID", + "CKR_DATA_INVALID", + "CKR_DATA_LEN_RANGE", + "CKR_DEVICE_ERROR", + "CKR_DEVICE_MEMORY", + "CKR_DEVICE_REMOVED", + "CKR_ENCRYPTED_DATA_INVALID", + "CKR_ENCRYPTED_DATA_LEN_RANGE", + "CKR_FUNCTION_CANCELED", + "CKR_FUNCTION_NOT_PARALLEL", + "CKR_FUNCTION_NOT_SUPPORTED", + "CKR_KEY_HANDLE_INVALID", + "CKR_KEY_SIZE_RANGE", + "CKR_KEY_TYPE_INCONSISTENT", + "CKR_KEY_NOT_NEEDED", + "CKR_KEY_CHANGED", + "CKR_KEY_NEEDED", + "CKR_KEY_INDIGESTIBLE", + "CKR_KEY_FUNCTION_NOT_PERMITTED", + "CKR_KEY_NOT_WRAPPABLE", + "CKR_KEY_UNEXTRACTABLE", + "CKR_MECHANISM_INVALID", + "CKR_MECHANISM_PARAM_INVALID", + "CKR_OBJECT_HANDLE_INVALID", + "CKR_OPERATION_ACTIVE", + "CKR_OPERATION_NOT_INITIALIZED", + "CKR_PIN_INCORRECT", + "CKR_PIN_INVALID", + "CKR_PIN_LEN_RANGE", + "CKR_PIN_EXPIRED", + "CKR_PIN_LOCKED", + "CKR_SESSION_CLOSED", + "CKR_SESSION_COUNT", + "CKR_SESSION_HANDLE_INVALID", + "CKR_SESSION_PARALLEL_NOT_SUPPORTED", + "CKR_SESSION_READ_ONLY", + "CKR_SESSION_EXISTS", + "CKR_SESSION_READ_ONLY_EXISTS", + "CKR_SESSION_READ_WRITE_SO_EXISTS", + "CKR_SIGNATURE_INVALID", + "CKR_SIGNATURE_LEN_RANGE", + "CKR_TEMPLATE_INCOMPLETE", + "CKR_TEMPLATE_INCONSISTENT", + "CKR_TOKEN_NOT_PRESENT", + "CKR_TOKEN_NOT_RECOGNIZED", + "CKR_TOKEN_WRITE_PROTECTED", + "CKR_UNWRAPPING_KEY_HANDLE_INVALID", + "CKR_UNWRAPPING_KEY_SIZE_RANGE", + "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT", + "CKR_USER_ALREADY_LOGGED_IN", + "CKR_USER_NOT_LOGGED_IN", + "CKR_USER_PIN_NOT_INITIALIZED", + "CKR_USER_TYPE_INVALID", + "CKR_USER_ANOTHER_ALREADY_LOGGED_IN", + "CKR_USER_TOO_MANY_TYPES", + "CKR_WRAPPED_KEY_INVALID", + "CKR_WRAPPED_KEY_LEN_RANGE", + "CKR_WRAPPING_KEY_HANDLE_INVALID", + "CKR_WRAPPING_KEY_SIZE_RANGE", + "CKR_WRAPPING_KEY_TYPE_INCONSISTENT", + "CKR_RANDOM_SEED_NOT_SUPPORTED", + "CKR_RANDOM_NO_RNG", + "CKR_DOMAIN_PARAMS_INVALID", + "CKR_BUFFER_TOO_SMALL", + "CKR_SAVED_STATE_INVALID", + "CKR_INFORMATION_SENSITIVE", + "CKR_STATE_UNSAVEABLE", + "CKR_CRYPTOKI_NOT_INITIALIZED", + "CKR_CRYPTOKI_ALREADY_INITIALIZED", + "CKR_MUTEX_BAD", + "CKR_MUTEX_NOT_LOCKED", + "CKR_FUNCTION_REJECTED", + "CKR_VENDOR_DEFINED", + }; + errorMap = new HashMap(); + for (int i = 0; i < errorCodes.length; i++) { + errorMap.put(Long.valueOf(errorCodes[i]), errorMessages[i]); + } + } + + + /** + * Constructor taking the error code as defined for the CKR_* constants + * in PKCS#11. + */ + public PKCS11Exception(long errorCode) { + errorCode_ = errorCode; + } + + /** + * This method gets the corresponding text error message from + * a property file. If this file is not available, it returns the error + * code as a hex-string. + * + * @return The message or the error code; e.g. "CKR_DEVICE_ERROR" or + * "0x00000030". + * @preconditions + * @postconditions (result <> null) + */ + public String getMessage() { + String message = errorMap.get(Long.valueOf(errorCode_)); + if (message == null) { + message = "0x" + Functions.toFullHexString((int)errorCode_); + } + return message; + } + + /** + * Returns the PKCS#11 error code. + * + * @return The error code; e.g. 0x00000030. + * @preconditions + * @postconditions + */ + public long getErrorCode() { + return errorCode_ ; + } + +} diff --git a/src/sun/security/pkcs11/wrapper/PKCS11RuntimeException.java b/src/sun/security/pkcs11/wrapper/PKCS11RuntimeException.java new file mode 100644 index 00000000..538e380f --- /dev/null +++ b/src/sun/security/pkcs11/wrapper/PKCS11RuntimeException.java @@ -0,0 +1,109 @@ +/* + * reserved comment block + * DO NOT REMOVE OR ALTER! + */ +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package sun.security.pkcs11.wrapper; + + +/** + * This is the superclass of all runtime exception used by this library. + * For instance, Runtime exceptions occur, if an internal error in the native + * part of the wrapper occurs. + * + * @author Karl Scheibelhofer + * @invariants + */ +public class PKCS11RuntimeException extends RuntimeException { + private static final long serialVersionUID = 7889842162743590564L; + + /** + * Empty constructor. + * + * @preconditions + * @postconditions + */ + public PKCS11RuntimeException() { + super(); + } + + /** + * Constructor taking a string that describes the reason of the exception + * in more detail. + * + * @param message A descrption of the reason for this exception. + * @preconditions + * @postconditions + */ + public PKCS11RuntimeException(String message) { + super(message); + } + + /** + * Constructor taking an other exception to wrap. + * + * @param encapsulatedException The other exception the wrap into this. + * @preconditions + * @postconditions + */ + public PKCS11RuntimeException(Exception encapsulatedException) { + super(encapsulatedException); + } + + /** + * Constructor taking a message for this exception and an other exception to + * wrap. + * + * @param message The message giving details about the exception to ease + * debugging. + * @param encapsulatedException The other exception the wrap into this. + * @preconditions + * @postconditions + */ + public PKCS11RuntimeException(String message, Exception encapsulatedException) { + super(message, encapsulatedException); + } + +} diff --git a/src/sun/security/pkcs12/MacData.java b/src/sun/security/pkcs12/MacData.java new file mode 100644 index 00000000..93bfef14 --- /dev/null +++ b/src/sun/security/pkcs12/MacData.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs12; + +import java.io.*; +import java.security.*; + +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.x509.AlgorithmId; +import sun.security.pkcs.ParsingException; + + +/** + * A MacData type, as defined in PKCS#12. + * + * @author Sharon Liu + */ + +class MacData { + + private String digestAlgorithmName; + private AlgorithmParameters digestAlgorithmParams; + private byte[] digest; + private byte[] macSalt; + private int iterations; + + // the ASN.1 encoded contents of this class + private byte[] encoded = null; + + /** + * Parses a PKCS#12 MAC data. + */ + MacData(DerInputStream derin) + throws IOException, ParsingException + { + DerValue[] macData = derin.getSequence(2); + + // Parse the digest info + DerInputStream digestIn = new DerInputStream(macData[0].toByteArray()); + DerValue[] digestInfo = digestIn.getSequence(2); + + // Parse the DigestAlgorithmIdentifier. + AlgorithmId digestAlgorithmId = AlgorithmId.parse(digestInfo[0]); + this.digestAlgorithmName = digestAlgorithmId.getName(); + this.digestAlgorithmParams = digestAlgorithmId.getParameters(); + // Get the digest. + this.digest = digestInfo[1].getOctetString(); + + // Get the salt. + this.macSalt = macData[1].getOctetString(); + + // Iterations is optional. The default value is 1. + if (macData.length > 2) { + this.iterations = macData[2].getInteger(); + } else { + this.iterations = 1; + } + } + + MacData(String algName, byte[] digest, byte[] salt, int iterations) + throws NoSuchAlgorithmException + { + if (algName == null) + throw new NullPointerException("the algName parameter " + + "must be non-null"); + + AlgorithmId algid = AlgorithmId.get(algName); + this.digestAlgorithmName = algid.getName(); + this.digestAlgorithmParams = algid.getParameters(); + + if (digest == null) { + throw new NullPointerException("the digest " + + "parameter must be non-null"); + } else if (digest.length == 0) { + throw new IllegalArgumentException("the digest " + + "parameter must not be empty"); + } else { + this.digest = digest.clone(); + } + + this.macSalt = salt; + this.iterations = iterations; + + // delay the generation of ASN.1 encoding until + // getEncoded() is called + this.encoded = null; + + } + + MacData(AlgorithmParameters algParams, byte[] digest, + byte[] salt, int iterations) throws NoSuchAlgorithmException + { + if (algParams == null) + throw new NullPointerException("the algParams parameter " + + "must be non-null"); + + AlgorithmId algid = AlgorithmId.get(algParams); + this.digestAlgorithmName = algid.getName(); + this.digestAlgorithmParams = algid.getParameters(); + + if (digest == null) { + throw new NullPointerException("the digest " + + "parameter must be non-null"); + } else if (digest.length == 0) { + throw new IllegalArgumentException("the digest " + + "parameter must not be empty"); + } else { + this.digest = digest.clone(); + } + + this.macSalt = salt; + this.iterations = iterations; + + // delay the generation of ASN.1 encoding until + // getEncoded() is called + this.encoded = null; + + } + + String getDigestAlgName() { + return digestAlgorithmName; + } + + byte[] getSalt() { + return macSalt; + } + + int getIterations() { + return iterations; + } + + byte[] getDigest() { + return digest; + } + + /** + * Returns the ASN.1 encoding of this object. + * @return the ASN.1 encoding. + * @exception IOException if error occurs when constructing its + * ASN.1 encoding. + */ + public byte[] getEncoded() throws NoSuchAlgorithmException, IOException + { + if (this.encoded != null) + return this.encoded.clone(); + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + DerOutputStream tmp2 = new DerOutputStream(); + // encode encryption algorithm + AlgorithmId algid = AlgorithmId.get(digestAlgorithmName); + algid.encode(tmp2); + + // encode digest data + tmp2.putOctetString(digest); + + tmp.write(DerValue.tag_Sequence, tmp2); + + // encode salt + tmp.putOctetString(macSalt); + + // encode iterations + tmp.putInteger(iterations); + + // wrap everything into a SEQUENCE + out.write(DerValue.tag_Sequence, tmp); + this.encoded = out.toByteArray(); + + return this.encoded.clone(); + } + +} diff --git a/src/sun/security/pkcs12/PKCS12KeyStore.java b/src/sun/security/pkcs12/PKCS12KeyStore.java new file mode 100644 index 00000000..06738e2a --- /dev/null +++ b/src/sun/security/pkcs12/PKCS12KeyStore.java @@ -0,0 +1,2287 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs12; + +import java.io.*; +import java.security.AccessController; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreSpi; +import java.security.KeyStoreException; +import java.security.PKCS12Attribute; +import java.security.PrivateKey; +import java.security.PrivilegedAction; +import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.*; + +import java.security.AlgorithmParameters; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.SecretKeyFactory; +import javax.crypto.SecretKey; +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.security.auth.DestroyFailedException; +import javax.security.auth.x500.X500Principal; + +import sun.security.util.Debug; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; +import sun.security.pkcs.ContentInfo; +import sun.security.x509.AlgorithmId; +import sun.security.pkcs.EncryptedPrivateKeyInfo; + + +/** + * This class provides the keystore implementation referred to as "PKCS12". + * Implements the PKCS#12 PFX protected using the Password privacy mode. + * The contents are protected using Password integrity mode. + * + * Currently we support following PBE algorithms: + * - pbeWithSHAAnd3KeyTripleDESCBC to encrypt private keys + * - pbeWithSHAAnd40BitRC2CBC to encrypt certificates + * + * Supported encryption of various implementations : + * + * Software and mode. Certificate encryption Private key encryption + * --------------------------------------------------------------------- + * MSIE4 (domestic 40 bit RC2. 40 bit RC2 + * and xport versions) + * PKCS#12 export. + * + * MSIE4, 5 (domestic 40 bit RC2, 40 bit RC2, + * and export versions) 3 key triple DES 3 key triple DES + * PKCS#12 import. + * + * MSIE5 40 bit RC2 3 key triple DES, + * PKCS#12 export. with SHA1 (168 bits) + * + * Netscape Communicator 40 bit RC2 3 key triple DES, + * (domestic and export with SHA1 (168 bits) + * versions) PKCS#12 export + * + * Netscape Communicator 40 bit ciphers only All. + * (export version) + * PKCS#12 import. + * + * Netscape Communicator All. All. + * (domestic or fortified + * version) PKCS#12 import. + * + * OpenSSL PKCS#12 code. All. All. + * --------------------------------------------------------------------- + * + * NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry. + * PKCS#12 is mainly used to deliver private keys with their associated + * certificate chain and aliases. In a PKCS12 keystore, entries are + * identified by the alias, and a localKeyId is required to match the + * private key with the certificate. Trusted certificate entries are identified + * by the presence of an trustedKeyUsage attribute. + * + * @author Seema Malkani + * @author Jeff Nisewanger + * @author Jan Luehe + * + * @see KeyProtector + * @see KeyStoreSpi + * @see KeyTool + * + * + */ +public final class PKCS12KeyStore extends KeyStoreSpi { + + public static final int VERSION_3 = 3; + + private static final String[] KEY_PROTECTION_ALGORITHM = { + "keystore.pkcs12.keyProtectionAlgorithm", + "keystore.PKCS12.keyProtectionAlgorithm" + }; + + // friendlyName, localKeyId, trustedKeyUsage + private static final String[] CORE_ATTRIBUTES = { + "1.2.840.113549.1.9.20", + "1.2.840.113549.1.9.21", + "2.16.840.1.113894.746875.1.1" + }; + + private static final Debug debug = Debug.getInstance("pkcs12"); + + private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; + private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; + private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; + + private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; + private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; + + private static final int pkcs9certType[] = {1, 2, 840, 113549, 1, 9, 22, 1}; + + private static final int pbeWithSHAAnd40BitRC2CBC[] = + {1, 2, 840, 113549, 1, 12, 1, 6}; + private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = + {1, 2, 840, 113549, 1, 12, 1, 3}; + private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; + // TODO: temporary Oracle OID + /* + * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) + * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } + */ + private static final int TrustedKeyUsage[] = + {2, 16, 840, 1, 113894, 746875, 1, 1}; + private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; + + private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; + private static ObjectIdentifier CertBag_OID; + private static ObjectIdentifier SecretBag_OID; + private static ObjectIdentifier PKCS9FriendlyName_OID; + private static ObjectIdentifier PKCS9LocalKeyId_OID; + private static ObjectIdentifier PKCS9CertType_OID; + private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID; + private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; + private static ObjectIdentifier pbes2_OID; + private static ObjectIdentifier TrustedKeyUsage_OID; + private static ObjectIdentifier[] AnyUsage; + + private int counter = 0; + private static final int iterationCount = 1024; + private static final int SALT_LEN = 20; + + // private key count + // Note: This is a workaround to allow null localKeyID attribute + // in pkcs12 with one private key entry and associated cert-chain + private int privateKeyCount = 0; + + // secret key count + private int secretKeyCount = 0; + + // certificate count + private int certificateCount = 0; + + // the source of randomness + private SecureRandom random; + + static { + try { + PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag); + CertBag_OID = new ObjectIdentifier(certBag); + SecretBag_OID = new ObjectIdentifier(secretBag); + PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); + PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); + PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); + pbeWithSHAAnd40BitRC2CBC_OID = + new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC); + pbeWithSHAAnd3KeyTripleDESCBC_OID = + new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); + pbes2_OID = new ObjectIdentifier(pbes2); + TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); + AnyUsage = new ObjectIdentifier[]{ + new ObjectIdentifier(AnyExtendedKeyUsage)}; + } catch (IOException ioe) { + // should not happen + } + } + + // A keystore entry and associated attributes + private static class Entry { + Date date; // the creation date of this entry + String alias; + byte[] keyId; + Set attributes; + } + + // A key entry + private static class KeyEntry extends Entry { + } + + // A private key entry and its supporting certificate chain + private static class PrivateKeyEntry extends KeyEntry { + byte[] protectedPrivKey; + Certificate chain[]; + }; + + // A secret key + private static class SecretKeyEntry extends KeyEntry { + byte[] protectedSecretKey; + }; + + // A certificate entry + private static class CertEntry extends Entry { + final X509Certificate cert; + ObjectIdentifier[] trustedKeyUsage; + + CertEntry(X509Certificate cert, byte[] keyId, String alias) { + this(cert, keyId, alias, null, null); + } + + CertEntry(X509Certificate cert, byte[] keyId, String alias, + ObjectIdentifier[] trustedKeyUsage, + Set attributes) { + this.date = new Date(); + this.cert = cert; + this.keyId = keyId; + this.alias = alias; + this.trustedKeyUsage = trustedKeyUsage; + this.attributes = new HashSet<>(); + if (attributes != null) { + this.attributes.addAll(attributes); + } + } + } + + /** + * Private keys and certificates are stored in a map. + * Map entries are keyed by alias names. + */ + private Map entries = + Collections.synchronizedMap(new LinkedHashMap()); + + private ArrayList keyList = new ArrayList(); + private LinkedHashMap certsMap = + new LinkedHashMap(); + private ArrayList certEntries = new ArrayList(); + + /** + * Returns the key associated with the given alias, using the given + * password to recover it. + * + * @param alias the alias name + * @param password the password for recovering the key + * + * @return the requested key, or null if the given alias does not exist + * or does not identify a key entry. + * + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * key cannot be found + * @exception UnrecoverableKeyException if the key cannot be recovered + * (e.g., the given password is wrong). + */ + public Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException + { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + Key key = null; + + if (entry == null || (!(entry instanceof KeyEntry))) { + return null; + } + + // get the encoded private key or secret key + byte[] encrBytes = null; + if (entry instanceof PrivateKeyEntry) { + encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey; + } else if (entry instanceof SecretKeyEntry) { + encrBytes = ((SecretKeyEntry) entry).protectedSecretKey; + } else { + throw new UnrecoverableKeyException("Error locating key"); + } + + byte[] encryptedKey; + AlgorithmParameters algParams; + ObjectIdentifier algOid; + try { + // get the encrypted private key + EncryptedPrivateKeyInfo encrInfo = + new EncryptedPrivateKeyInfo(encrBytes); + encryptedKey = encrInfo.getEncryptedData(); + + // parse Algorithm parameters + DerValue val = new DerValue(encrInfo.getAlgorithm().encode()); + DerInputStream in = val.toDerInputStream(); + algOid = in.getOID(); + algParams = parseAlgParameters(algOid, in); + + } catch (IOException ioe) { + UnrecoverableKeyException uke = + new UnrecoverableKeyException("Private key not stored as " + + "PKCS#8 EncryptedPrivateKeyInfo: " + ioe); + uke.initCause(ioe); + throw uke; + } + + try { + byte[] keyInfo; + while (true) { + try { + // Use JCE + SecretKey skey = getPBEKey(password); + Cipher cipher = Cipher.getInstance( + mapPBEParamsToAlgorithm(algOid, algParams)); + cipher.init(Cipher.DECRYPT_MODE, skey, algParams); + keyInfo = cipher.doFinal(encryptedKey); + break; + } catch (Exception e) { + if (password.length == 0) { + // Retry using an empty password + // without a NULL terminator. + password = new char[1]; + continue; + } + throw e; + } + } + + /* + * Parse the key algorithm and then use a JCA key factory + * to re-create the key. + */ + DerValue val = new DerValue(keyInfo); + DerInputStream in = val.toDerInputStream(); + int i = in.getInteger(); + DerValue[] value = in.getSequence(2); + AlgorithmId algId = new AlgorithmId(value[0].getOID()); + String keyAlgo = algId.getName(); + + // decode private key + if (entry instanceof PrivateKeyEntry) { + KeyFactory kfac = KeyFactory.getInstance(keyAlgo); + PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo); + key = kfac.generatePrivate(kspec); + + if (debug != null) { + debug.println("Retrieved a protected private key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } + + // decode secret key + } else { + SecretKeyFactory sKeyFactory = + SecretKeyFactory.getInstance(keyAlgo); + byte[] keyBytes = in.getOctetString(); + SecretKeySpec secretKeySpec = + new SecretKeySpec(keyBytes, keyAlgo); + + // Special handling required for PBE: needs a PBEKeySpec + if (keyAlgo.startsWith("PBE")) { + KeySpec pbeKeySpec = + sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class); + key = sKeyFactory.generateSecret(pbeKeySpec); + } else { + key = sKeyFactory.generateSecret(secretKeySpec); + } + + if (debug != null) { + debug.println("Retrieved a protected secret key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } + } + } catch (Exception e) { + UnrecoverableKeyException uke = + new UnrecoverableKeyException("Get Key failed: " + + e.getMessage()); + uke.initCause(e); + throw uke; + } + return key; + } + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last), or null if the given alias + * does not exist or does not contain a certificate chain (i.e., the given + * alias identifies either a trusted certificate entry or a + * key entry without a certificate chain). + */ + public Certificate[] engineGetCertificateChain(String alias) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain == null) { + return null; + } else { + + if (debug != null) { + debug.println("Retrieved a " + + ((PrivateKeyEntry) entry).chain.length + + "-certificate chain at alias '" + alias + "'"); + } + + return ((PrivateKeyEntry) entry).chain.clone(); + } + } else { + return null; + } + } + + /** + * Returns the certificate associated with the given alias. + * + *

    If the given alias name identifies a + * trusted certificate entry, the certificate associated with that + * entry is returned. If the given alias name identifies a + * key entry, the first element of the certificate chain of that + * entry is returned, or null if that entry does not have a certificate + * chain. + * + * @param alias the alias name + * + * @return the certificate, or null if the given alias does not exist or + * does not contain a certificate. + */ + public Certificate engineGetCertificate(String alias) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry == null) { + return null; + } + if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + + if (debug != null) { + if (Arrays.equals(AnyUsage, + ((CertEntry) entry).trustedKeyUsage)) { + debug.println("Retrieved a certificate at alias '" + alias + + "' (trusted for any purpose)"); + } else { + debug.println("Retrieved a certificate at alias '" + alias + + "' (trusted for limited purposes)"); + } + } + + return ((CertEntry) entry).cert; + + } else if (entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain == null) { + return null; + } else { + + if (debug != null) { + debug.println("Retrieved a certificate at alias '" + alias + + "'"); + } + + return ((PrivateKeyEntry) entry).chain[0]; + } + + } else { + return null; + } + } + + /** + * Returns the creation date of the entry identified by the given alias. + * + * @param alias the alias name + * + * @return the creation date of this entry, or null if the given alias does + * not exist + */ + public Date engineGetCreationDate(String alias) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null) { + return new Date(entry.date.getTime()); + } else { + return null; + } + } + + /** + * Assigns the given key to the given alias, protecting it with the given + * password. + * + *

    If the given key is of type java.security.PrivateKey, + * it must be accompanied by a certificate chain certifying the + * corresponding public key. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key to be associated with the alias + * @param password the password to protect the key + * @param chain the certificate chain for the corresponding public + * key (only required if the given key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if the given key cannot be protected, or + * this operation fails for some other reason + */ + public synchronized void engineSetKeyEntry(String alias, Key key, + char[] password, Certificate[] chain) + throws KeyStoreException + { + KeyStore.PasswordProtection passwordProtection = + new KeyStore.PasswordProtection(password); + + try { + setKeyEntry(alias, key, passwordProtection, chain, null); + + } finally { + try { + passwordProtection.destroy(); + } catch (DestroyFailedException dfe) { + // ignore + } + } + } + + /* + * Sets a key entry (with attributes, when present) + */ + private void setKeyEntry(String alias, Key key, + KeyStore.PasswordProtection passwordProtection, Certificate[] chain, + Set attributes) + throws KeyStoreException + { + try { + Entry entry; + + if (key instanceof PrivateKey) { + PrivateKeyEntry keyEntry = new PrivateKeyEntry(); + keyEntry.date = new Date(); + + if ((key.getFormat().equals("PKCS#8")) || + (key.getFormat().equals("PKCS8"))) { + + if (debug != null) { + debug.println("Setting a protected private key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } + + // Encrypt the private key + keyEntry.protectedPrivKey = + encryptPrivateKey(key.getEncoded(), passwordProtection); + } else { + throw new KeyStoreException("Private key is not encoded" + + "as PKCS#8"); + } + + // clone the chain + if (chain != null) { + // validate cert-chain + if ((chain.length > 1) && (!validateChain(chain))) + throw new KeyStoreException("Certificate chain is " + + "not valid"); + keyEntry.chain = chain.clone(); + certificateCount += chain.length; + + if (debug != null) { + debug.println("Setting a " + chain.length + + "-certificate chain at alias '" + alias + "'"); + } + } + privateKeyCount++; + entry = keyEntry; + + } else if (key instanceof SecretKey) { + SecretKeyEntry keyEntry = new SecretKeyEntry(); + keyEntry.date = new Date(); + + // Encode secret key in a PKCS#8 + DerOutputStream pkcs8 = new DerOutputStream(); + DerOutputStream secretKeyInfo = new DerOutputStream(); + secretKeyInfo.putInteger(0); + AlgorithmId algId = AlgorithmId.get(key.getAlgorithm()); + algId.encode(secretKeyInfo); + secretKeyInfo.putOctetString(key.getEncoded()); + pkcs8.write(DerValue.tag_Sequence, secretKeyInfo); + + // Encrypt the secret key (using same PBE as for private keys) + keyEntry.protectedSecretKey = + encryptPrivateKey(pkcs8.toByteArray(), passwordProtection); + + if (debug != null) { + debug.println("Setting a protected secret key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } + secretKeyCount++; + entry = keyEntry; + + } else { + throw new KeyStoreException("Unsupported Key type"); + } + + entry.attributes = new HashSet<>(); + if (attributes != null) { + entry.attributes.addAll(attributes); + } + // set the keyId to current date + entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); + // set the alias + entry.alias = alias.toLowerCase(Locale.ENGLISH); + // add the entry + entries.put(alias.toLowerCase(Locale.ENGLISH), entry); + + } catch (Exception nsae) { + throw new KeyStoreException("Key protection " + + " algorithm not found: " + nsae, nsae); + } + } + + /** + * Assigns the given key (that has already been protected) to the given + * alias. + * + *

    If the protected key is of type + * java.security.PrivateKey, it must be accompanied by a + * certificate chain certifying the corresponding public key. If the + * underlying keystore implementation is of type jks, + * key must be encoded as an + * EncryptedPrivateKeyInfo as defined in the PKCS #8 standard. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key (in protected format) to be associated with the alias + * @param chain the certificate chain for the corresponding public + * key (only useful if the protected key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if this operation fails. + */ + public synchronized void engineSetKeyEntry(String alias, byte[] key, + Certificate[] chain) + throws KeyStoreException + { + // Private key must be encoded as EncryptedPrivateKeyInfo + // as defined in PKCS#8 + try { + new EncryptedPrivateKeyInfo(key); + } catch (IOException ioe) { + throw new KeyStoreException("Private key is not stored" + + " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe); + } + + PrivateKeyEntry entry = new PrivateKeyEntry(); + entry.date = new Date(); + + if (debug != null) { + debug.println("Setting a protected private key at alias '" + + alias + "'"); + } + + try { + // set the keyId to current date + entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); + } catch (UnsupportedEncodingException ex) { + // Won't happen + } + // set the alias + entry.alias = alias.toLowerCase(Locale.ENGLISH); + + entry.protectedPrivKey = key.clone(); + if (chain != null) { + entry.chain = chain.clone(); + certificateCount += chain.length; + + if (debug != null) { + debug.println("Setting a " + entry.chain.length + + "-certificate chain at alias '" + alias + "'"); + } + } + + // add the entry + privateKeyCount++; + entries.put(alias.toLowerCase(Locale.ENGLISH), entry); + } + + + /* + * Generate random salt + */ + private byte[] getSalt() + { + // Generate a random salt. + byte[] salt = new byte[SALT_LEN]; + if (random == null) { + random = new SecureRandom(); + } + random.nextBytes(salt); + return salt; + } + + /* + * Generate PBE Algorithm Parameters + */ + private AlgorithmParameters getAlgorithmParameters(String algorithm) + throws IOException + { + AlgorithmParameters algParams = null; + + // create PBE parameters from salt and iteration count + PBEParameterSpec paramSpec = + new PBEParameterSpec(getSalt(), iterationCount); + try { + algParams = AlgorithmParameters.getInstance(algorithm); + algParams.init(paramSpec); + } catch (Exception e) { + throw new IOException("getAlgorithmParameters failed: " + + e.getMessage(), e); + } + return algParams; + } + + /* + * parse Algorithm Parameters + */ + private AlgorithmParameters parseAlgParameters(ObjectIdentifier algorithm, + DerInputStream in) throws IOException + { + AlgorithmParameters algParams = null; + try { + DerValue params; + if (in.available() == 0) { + params = null; + } else { + params = in.getDerValue(); + if (params.tag == DerValue.tag_Null) { + params = null; + } + } + if (params != null) { + if (algorithm.equals((Object)pbes2_OID)) { + algParams = AlgorithmParameters.getInstance("PBES2"); + } else { + algParams = AlgorithmParameters.getInstance("PBE"); + } + algParams.init(params.toByteArray()); + } + } catch (Exception e) { + throw new IOException("parseAlgParameters failed: " + + e.getMessage(), e); + } + return algParams; + } + + /* + * Generate PBE key + */ + private SecretKey getPBEKey(char[] password) throws IOException + { + SecretKey skey = null; + + try { + PBEKeySpec keySpec = new PBEKeySpec(password); + SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); + skey = skFac.generateSecret(keySpec); + keySpec.clearPassword(); + } catch (Exception e) { + throw new IOException("getSecretKey failed: " + + e.getMessage(), e); + } + return skey; + } + + /* + * Encrypt private key using Password-based encryption (PBE) + * as defined in PKCS#5. + * + * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is + * used to derive the key and IV. + * + * @return encrypted private key encoded as EncryptedPrivateKeyInfo + */ + private byte[] encryptPrivateKey(byte[] data, + KeyStore.PasswordProtection passwordProtection) + throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException + { + byte[] key = null; + + try { + String algorithm; + AlgorithmParameters algParams; + AlgorithmId algid; + + // Initialize PBE algorithm and parameters + algorithm = passwordProtection.getProtectionAlgorithm(); + if (algorithm != null) { + AlgorithmParameterSpec algParamSpec = + passwordProtection.getProtectionParameters(); + if (algParamSpec != null) { + algParams = AlgorithmParameters.getInstance(algorithm); + algParams.init(algParamSpec); + } else { + algParams = getAlgorithmParameters(algorithm); + } + } else { + // Check default key protection algorithm for PKCS12 keystores + algorithm = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + String prop = + Security.getProperty( + KEY_PROTECTION_ALGORITHM[0]); + if (prop == null) { + prop = Security.getProperty( + KEY_PROTECTION_ALGORITHM[1]); + } + return prop; + } + }); + if (algorithm == null || algorithm.isEmpty()) { + algorithm = "PBEWithSHA1AndDESede"; + } + algParams = getAlgorithmParameters(algorithm); + } + + ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm); + if (pbeOID == null) { + throw new IOException("PBE algorithm '" + algorithm + + " 'is not supported for key entry protection"); + } + + // Use JCE + SecretKey skey = getPBEKey(passwordProtection.getPassword()); + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); + byte[] encryptedKey = cipher.doFinal(data); + algid = new AlgorithmId(pbeOID, cipher.getParameters()); + + if (debug != null) { + debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + + ")"); + } + + // wrap encrypted private key in EncryptedPrivateKeyInfo + // as defined in PKCS#8 + EncryptedPrivateKeyInfo encrInfo = + new EncryptedPrivateKeyInfo(algid, encryptedKey); + key = encrInfo.getEncoded(); + } catch (Exception e) { + UnrecoverableKeyException uke = + new UnrecoverableKeyException("Encrypt Private Key failed: " + + e.getMessage()); + uke.initCause(e); + throw uke; + } + + return key; + } + + /* + * Map a PBE algorithm name onto its object identifier + */ + private static ObjectIdentifier mapPBEAlgorithmToOID(String algorithm) + throws NoSuchAlgorithmException { + // Check for PBES2 algorithms + if (algorithm.toLowerCase().startsWith("pbewithhmacsha")) { + return pbes2_OID; + } + return AlgorithmId.get(algorithm).getOID(); + } + + /* + * Map a PBE algorithm parameters onto its algorithm name + */ + private static String mapPBEParamsToAlgorithm(ObjectIdentifier algorithm, + AlgorithmParameters algParams) throws NoSuchAlgorithmException { + // Check for PBES2 algorithms + if (algorithm.equals((Object)pbes2_OID) && algParams != null) { + return algParams.toString(); + } + return algorithm.toString(); + } + + /** + * Assigns the given certificate to the given alias. + * + *

    If the given alias already exists in this keystore and identifies a + * trusted certificate entry, the certificate associated with it is + * overridden by the given certificate. + * + * @param alias the alias name + * @param cert the certificate + * + * @exception KeyStoreException if the given alias already exists and does + * not identify a trusted certificate entry, or this operation fails + * for some other reason. + */ + public synchronized void engineSetCertificateEntry(String alias, + Certificate cert) throws KeyStoreException + { + setCertEntry(alias, cert, null); + } + + /* + * Sets a trusted cert entry (with attributes, when present) + */ + private void setCertEntry(String alias, Certificate cert, + Set attributes) throws KeyStoreException { + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof KeyEntry) { + throw new KeyStoreException("Cannot overwrite own certificate"); + } + + CertEntry certEntry = + new CertEntry((X509Certificate) cert, null, alias, AnyUsage, + attributes); + certificateCount++; + entries.put(alias, certEntry); + + if (debug != null) { + debug.println("Setting a trusted certificate at alias '" + alias + + "'"); + } + } + + /** + * Deletes the entry identified by the given alias from this keystore. + * + * @param alias the alias name + * + * @exception KeyStoreException if the entry cannot be removed. + */ + public synchronized void engineDeleteEntry(String alias) + throws KeyStoreException + { + if (debug != null) { + debug.println("Removing entry at alias '" + alias + "'"); + } + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; + if (keyEntry.chain != null) { + certificateCount -= keyEntry.chain.length; + } + privateKeyCount--; + } else if (entry instanceof CertEntry) { + certificateCount--; + } else if (entry instanceof SecretKeyEntry) { + secretKeyCount--; + } + entries.remove(alias.toLowerCase(Locale.ENGLISH)); + } + + /** + * Lists all the alias names of this keystore. + * + * @return enumeration of the alias names + */ + public Enumeration engineAliases() { + return Collections.enumeration(entries.keySet()); + } + + /** + * Checks if the given alias exists in this keystore. + * + * @param alias the alias name + * + * @return true if the alias exists, false otherwise + */ + public boolean engineContainsAlias(String alias) { + return entries.containsKey(alias.toLowerCase(Locale.ENGLISH)); + } + + /** + * Retrieves the number of entries in this keystore. + * + * @return the number of entries in this keystore + */ + public int engineSize() { + return entries.size(); + } + + /** + * Returns true if the entry identified by the given alias is a + * key entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * key entry, false otherwise. + */ + public boolean engineIsKeyEntry(String alias) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof KeyEntry) { + return true; + } else { + return false; + } + } + + /** + * Returns true if the entry identified by the given alias is a + * trusted certificate entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * trusted certificate entry, false otherwise. + */ + public boolean engineIsCertificateEntry(String alias) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + return true; + } else { + return false; + } + } + + /** + * Returns the (alias) name of the first keystore entry whose certificate + * matches the given certificate. + * + *

    This method attempts to match the given certificate with each + * keystore entry. If the entry being considered + * is a trusted certificate entry, the given certificate is + * compared to that entry's certificate. If the entry being considered is + * a key entry, the given certificate is compared to the first + * element of that entry's certificate chain (if a chain exists). + * + * @param cert the certificate to match with. + * + * @return the (alias) name of the first entry with matching certificate, + * or null if no such entry exists in this keystore. + */ + public String engineGetCertificateAlias(Certificate cert) { + Certificate certElem = null; + + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { + String alias = e.nextElement(); + Entry entry = entries.get(alias); + if (entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain != null) { + certElem = ((PrivateKeyEntry) entry).chain[0]; + } + } else if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + certElem = ((CertEntry) entry).cert; + } else { + continue; + } + if (certElem.equals(cert)) { + return alias; + } + } + return null; + } + + /** + * Stores this keystore to the given output stream, and protects its + * integrity with the given password. + * + * @param stream the output stream to which this keystore is written. + * @param password the password to generate the keystore integrity check + * + * @exception IOException if there was an I/O problem with data + * @exception NoSuchAlgorithmException if the appropriate data integrity + * algorithm could not be found + * @exception CertificateException if any of the certificates included in + * the keystore data could not be stored + */ + public synchronized void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + // password is mandatory when storing + if (password == null) { + throw new IllegalArgumentException("password can't be null"); + } + + // -- Create PFX + DerOutputStream pfx = new DerOutputStream(); + + // PFX version (always write the latest version) + DerOutputStream version = new DerOutputStream(); + version.putInteger(VERSION_3); + byte[] pfxVersion = version.toByteArray(); + pfx.write(pfxVersion); + + // -- Create AuthSafe + DerOutputStream authSafe = new DerOutputStream(); + + // -- Create ContentInfos + DerOutputStream authSafeContentInfo = new DerOutputStream(); + + // -- create safeContent Data ContentInfo + if (privateKeyCount > 0 || secretKeyCount > 0) { + + if (debug != null) { + debug.println("Storing " + (privateKeyCount + secretKeyCount) + + " protected key(s) in a PKCS#7 data content-type"); + } + + byte[] safeContentData = createSafeContent(); + ContentInfo dataContentInfo = new ContentInfo(safeContentData); + dataContentInfo.encode(authSafeContentInfo); + } + + // -- create EncryptedContentInfo + if (certificateCount > 0) { + + if (debug != null) { + debug.println("Storing " + certificateCount + + " certificate(s) in a PKCS#7 encryptedData content-type"); + } + + byte[] encrData = createEncryptedData(password); + ContentInfo encrContentInfo = + new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, + new DerValue(encrData)); + encrContentInfo.encode(authSafeContentInfo); + } + + // wrap as SequenceOf ContentInfos + DerOutputStream cInfo = new DerOutputStream(); + cInfo.write(DerValue.tag_SequenceOf, authSafeContentInfo); + byte[] authenticatedSafe = cInfo.toByteArray(); + + // Create Encapsulated ContentInfo + ContentInfo contentInfo = new ContentInfo(authenticatedSafe); + contentInfo.encode(authSafe); + byte[] authSafeData = authSafe.toByteArray(); + pfx.write(authSafeData); + + // -- MAC + byte[] macData = calculateMac(password, authenticatedSafe); + pfx.write(macData); + + // write PFX to output stream + DerOutputStream pfxout = new DerOutputStream(); + pfxout.write(DerValue.tag_Sequence, pfx); + byte[] pfxData = pfxout.toByteArray(); + stream.write(pfxData); + stream.flush(); + } + + /** + * Gets a KeyStore.Entry for the specified alias + * with the specified protection parameter. + * + * @param alias get the KeyStore.Entry for this alias + * @param protParam the ProtectionParameter + * used to protect the Entry, + * which may be null + * + * @return the KeyStore.Entry for the specified alias, + * or null if there is no such entry + * + * @exception KeyStoreException if the operation failed + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * entry cannot be found + * @exception UnrecoverableEntryException if the specified + * protParam were insufficient or invalid + * @exception UnrecoverableKeyException if the entry is a + * PrivateKeyEntry or SecretKeyEntry + * and the specified protParam does not contain + * the information needed to recover the key (e.g. wrong password) + * + * @since 1.5 + */ + @Override + public KeyStore.Entry engineGetEntry(String alias, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableEntryException { + + if (!engineContainsAlias(alias)) { + return null; + } + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (protParam == null) { + if (engineIsCertificateEntry(alias)) { + if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + + if (debug != null) { + debug.println("Retrieved a trusted certificate at " + + "alias '" + alias + "'"); + } + + return new KeyStore.TrustedCertificateEntry( + ((CertEntry)entry).cert, getAttributes(entry)); + } + } else { + throw new UnrecoverableKeyException + ("requested entry requires a password"); + } + } + + if (protParam instanceof KeyStore.PasswordProtection) { + if (engineIsCertificateEntry(alias)) { + throw new UnsupportedOperationException + ("trusted certificate entries are not password-protected"); + } else if (engineIsKeyEntry(alias)) { + KeyStore.PasswordProtection pp = + (KeyStore.PasswordProtection)protParam; + char[] password = pp.getPassword(); + + Key key = engineGetKey(alias, password); + if (key instanceof PrivateKey) { + Certificate[] chain = engineGetCertificateChain(alias); + + return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain, + getAttributes(entry)); + + } else if (key instanceof SecretKey) { + + return new KeyStore.SecretKeyEntry((SecretKey)key, + getAttributes(entry)); + } + } else if (!engineIsKeyEntry(alias)) { + throw new UnsupportedOperationException + ("untrusted certificate entries are not " + + "password-protected"); + } + } + + throw new UnsupportedOperationException(); + } + + /** + * Saves a KeyStore.Entry under the specified alias. + * The specified protection parameter is used to protect the + * Entry. + * + *

    If an entry already exists for the specified alias, + * it is overridden. + * + * @param alias save the KeyStore.Entry under this alias + * @param entry the Entry to save + * @param protParam the ProtectionParameter + * used to protect the Entry, + * which may be null + * + * @exception KeyStoreException if this operation fails + * + * @since 1.5 + */ + @Override + public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, + KeyStore.ProtectionParameter protParam) throws KeyStoreException { + + // get password + if (protParam != null && + !(protParam instanceof KeyStore.PasswordProtection)) { + throw new KeyStoreException("unsupported protection parameter"); + } + KeyStore.PasswordProtection pProtect = null; + if (protParam != null) { + pProtect = (KeyStore.PasswordProtection)protParam; + } + + // set entry + if (entry instanceof KeyStore.TrustedCertificateEntry) { + if (protParam != null && pProtect.getPassword() != null) { + // pre-1.5 style setCertificateEntry did not allow password + throw new KeyStoreException + ("trusted certificate entries are not password-protected"); + } else { + KeyStore.TrustedCertificateEntry tce = + (KeyStore.TrustedCertificateEntry)entry; + setCertEntry(alias, tce.getTrustedCertificate(), + tce.getAttributes()); + + return; + } + } else if (entry instanceof KeyStore.PrivateKeyEntry) { + if (pProtect == null || pProtect.getPassword() == null) { + // pre-1.5 style setKeyEntry required password + throw new KeyStoreException + ("non-null password required to create PrivateKeyEntry"); + } else { + KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry; + setKeyEntry(alias, pke.getPrivateKey(), pProtect, + pke.getCertificateChain(), pke.getAttributes()); + + return; + } + } else if (entry instanceof KeyStore.SecretKeyEntry) { + if (pProtect == null || pProtect.getPassword() == null) { + // pre-1.5 style setKeyEntry required password + throw new KeyStoreException + ("non-null password required to create SecretKeyEntry"); + } else { + KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; + setKeyEntry(alias, ske.getSecretKey(), pProtect, + (Certificate[])null, ske.getAttributes()); + + return; + } + } + + throw new KeyStoreException + ("unsupported entry type: " + entry.getClass().getName()); + } + + /* + * Assemble the entry attributes + */ + private Set getAttributes(Entry entry) { + + if (entry.attributes == null) { + entry.attributes = new HashSet<>(); + } + + // friendlyName + entry.attributes.add(new PKCS12Attribute( + PKCS9FriendlyName_OID.toString(), entry.alias)); + + // localKeyID + byte[] keyIdValue = entry.keyId; + if (keyIdValue != null) { + entry.attributes.add(new PKCS12Attribute( + PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue))); + } + + // trustedKeyUsage + if (entry instanceof CertEntry) { + ObjectIdentifier[] trustedKeyUsageValue = + ((CertEntry) entry).trustedKeyUsage; + if (trustedKeyUsageValue != null) { + if (trustedKeyUsageValue.length == 1) { // omit brackets + entry.attributes.add(new PKCS12Attribute( + TrustedKeyUsage_OID.toString(), + trustedKeyUsageValue[0].toString())); + } else { // multi-valued + entry.attributes.add(new PKCS12Attribute( + TrustedKeyUsage_OID.toString(), + Arrays.toString(trustedKeyUsageValue))); + } + } + } + + return entry.attributes; + } + + /* + * Generate Hash. + */ + private byte[] generateHash(byte[] data) throws IOException + { + byte[] digest = null; + + try { + MessageDigest md = MessageDigest.getInstance("SHA1"); + md.update(data); + digest = md.digest(); + } catch (Exception e) { + throw new IOException("generateHash failed: " + e, e); + } + return digest; + } + + + /* + * Calculate MAC using HMAC algorithm (required for password integrity) + * + * Hash-based MAC algorithm combines secret key with message digest to + * create a message authentication code (MAC) + */ + private byte[] calculateMac(char[] passwd, byte[] data) + throws IOException + { + byte[] mData = null; + String algName = "SHA1"; + + try { + // Generate a random salt. + byte[] salt = getSalt(); + + // generate MAC (MAC key is generated within JCE) + Mac m = Mac.getInstance("HmacPBESHA1"); + PBEParameterSpec params = + new PBEParameterSpec(salt, iterationCount); + SecretKey key = getPBEKey(passwd); + m.init(key, params); + m.update(data); + byte[] macResult = m.doFinal(); + + // encode as MacData + MacData macData = new MacData(algName, macResult, salt, + iterationCount); + DerOutputStream bytes = new DerOutputStream(); + bytes.write(macData.getEncoded()); + mData = bytes.toByteArray(); + } catch (Exception e) { + throw new IOException("calculateMac failed: " + e, e); + } + return mData; + } + + + /* + * Validate Certificate Chain + */ + private boolean validateChain(Certificate[] certChain) + { + for (int i = 0; i < certChain.length-1; i++) { + X500Principal issuerDN = + ((X509Certificate)certChain[i]).getIssuerX500Principal(); + X500Principal subjectDN = + ((X509Certificate)certChain[i+1]).getSubjectX500Principal(); + if (!(issuerDN.equals(subjectDN))) + return false; + } + return true; + } + + + /* + * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage. + * + * Although attributes are optional, they could be required. + * For e.g. localKeyId attribute is required to match the + * private key with the associated end-entity certificate. + * The trustedKeyUsage attribute is used to denote a trusted certificate. + * + * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. + * CertBags may or may not include attributes depending on the type + * of Certificate. In end-entity certificates, localKeyID should be + * unique, and the corresponding private key should have the same + * localKeyID. For trusted CA certs in the cert-chain, localKeyID + * attribute is not required, hence most vendors don't include it. + * NSS/Netscape require it to be unique or null, where as IE/OpenSSL + * ignore it. + * + * Here is a list of pkcs12 attribute values in CertBags. + * + * PKCS12 Attribute NSS/Netscape IE OpenSSL J2SE + * -------------------------------------------------------------- + * LocalKeyId + * (In EE cert only, + * NULL in CA certs) true true true true + * + * friendlyName unique same/ same/ unique + * unique unique/ + * null + * trustedKeyUsage - - - true + * + * Note: OpenSSL adds friendlyName for end-entity cert only, and + * removes the localKeyID and friendlyName for CA certs. + * If the CertBag did not have a friendlyName, most vendors will + * add it, and assign it to the DN of the cert. + */ + private byte[] getBagAttributes(String alias, byte[] keyId, + Set attributes) throws IOException { + return getBagAttributes(alias, keyId, null, attributes); + } + + private byte[] getBagAttributes(String alias, byte[] keyId, + ObjectIdentifier[] trustedUsage, + Set attributes) throws IOException { + + byte[] localKeyID = null; + byte[] friendlyName = null; + byte[] trustedKeyUsage = null; + + // return null if all three attributes are null + if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) { + return null; + } + + // SafeBag Attributes + DerOutputStream bagAttrs = new DerOutputStream(); + + // Encode the friendlyname oid. + if (alias != null) { + DerOutputStream bagAttr1 = new DerOutputStream(); + bagAttr1.putOID(PKCS9FriendlyName_OID); + DerOutputStream bagAttrContent1 = new DerOutputStream(); + DerOutputStream bagAttrValue1 = new DerOutputStream(); + bagAttrContent1.putBMPString(alias); + bagAttr1.write(DerValue.tag_Set, bagAttrContent1); + bagAttrValue1.write(DerValue.tag_Sequence, bagAttr1); + friendlyName = bagAttrValue1.toByteArray(); + } + + // Encode the localkeyId oid. + if (keyId != null) { + DerOutputStream bagAttr2 = new DerOutputStream(); + bagAttr2.putOID(PKCS9LocalKeyId_OID); + DerOutputStream bagAttrContent2 = new DerOutputStream(); + DerOutputStream bagAttrValue2 = new DerOutputStream(); + bagAttrContent2.putOctetString(keyId); + bagAttr2.write(DerValue.tag_Set, bagAttrContent2); + bagAttrValue2.write(DerValue.tag_Sequence, bagAttr2); + localKeyID = bagAttrValue2.toByteArray(); + } + + // Encode the trustedKeyUsage oid. + if (trustedUsage != null) { + DerOutputStream bagAttr3 = new DerOutputStream(); + bagAttr3.putOID(TrustedKeyUsage_OID); + DerOutputStream bagAttrContent3 = new DerOutputStream(); + DerOutputStream bagAttrValue3 = new DerOutputStream(); + for (ObjectIdentifier usage : trustedUsage) { + bagAttrContent3.putOID(usage); + } + bagAttr3.write(DerValue.tag_Set, bagAttrContent3); + bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3); + trustedKeyUsage = bagAttrValue3.toByteArray(); + } + + DerOutputStream attrs = new DerOutputStream(); + if (friendlyName != null) { + attrs.write(friendlyName); + } + if (localKeyID != null) { + attrs.write(localKeyID); + } + if (trustedKeyUsage != null) { + attrs.write(trustedKeyUsage); + } + + if (attributes != null) { + for (KeyStore.Entry.Attribute attribute : attributes) { + String attributeName = attribute.getName(); + // skip friendlyName, localKeyId and trustedKeyUsage + if (CORE_ATTRIBUTES[0].equals(attributeName) || + CORE_ATTRIBUTES[1].equals(attributeName) || + CORE_ATTRIBUTES[2].equals(attributeName)) { + continue; + } + attrs.write(((PKCS12Attribute) attribute).getEncoded()); + } + } + + bagAttrs.write(DerValue.tag_Set, attrs); + return bagAttrs.toByteArray(); + } + + /* + * Create EncryptedData content type, that contains EncryptedContentInfo. + * Includes certificates in individual SafeBags of type CertBag. + * Each CertBag may include pkcs12 attributes + * (see comments in getBagAttributes) + */ + private byte[] createEncryptedData(char[] password) + throws CertificateException, IOException + { + DerOutputStream out = new DerOutputStream(); + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { + + String alias = e.nextElement(); + Entry entry = entries.get(alias); + + // certificate chain + int chainLen = 1; + Certificate[] certs = null; + + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; + if (keyEntry.chain == null) { + chainLen = 0; + } else { + chainLen = keyEntry.chain.length; + } + certs = keyEntry.chain; + + } else if (entry instanceof CertEntry) { + certs = new Certificate[]{((CertEntry) entry).cert}; + } + + for (int i = 0; i < chainLen; i++) { + // create SafeBag of Type CertBag + DerOutputStream safeBag = new DerOutputStream(); + safeBag.putOID(CertBag_OID); + + // create a CertBag + DerOutputStream certBag = new DerOutputStream(); + certBag.putOID(PKCS9CertType_OID); + + // write encoded certs in a context-specific tag + DerOutputStream certValue = new DerOutputStream(); + X509Certificate cert = (X509Certificate) certs[i]; + certValue.putOctetString(cert.getEncoded()); + certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), certValue); + + // wrap CertBag in a Sequence + DerOutputStream certout = new DerOutputStream(); + certout.write(DerValue.tag_Sequence, certBag); + byte[] certBagValue = certout.toByteArray(); + + // Wrap the CertBag encoding in a context-specific tag. + DerOutputStream bagValue = new DerOutputStream(); + bagValue.write(certBagValue); + // write SafeBag Value + safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), bagValue); + + // write SafeBag Attributes + // All Certs should have a unique friendlyName. + // This change is made to meet NSS requirements. + byte[] bagAttrs = null; + if (i == 0) { + // Only End-Entity Cert should have a localKeyId. + if (entry instanceof KeyEntry) { + KeyEntry keyEntry = (KeyEntry) entry; + bagAttrs = + getBagAttributes(keyEntry.alias, keyEntry.keyId, + keyEntry.attributes); + } else { + CertEntry certEntry = (CertEntry) entry; + bagAttrs = + getBagAttributes(certEntry.alias, certEntry.keyId, + certEntry.trustedKeyUsage, + certEntry.attributes); + } + } else { + // Trusted root CA certs and Intermediate CA certs do not + // need to have a localKeyId, and hence localKeyId is null + // This change is made to meet NSS/Netscape requirements. + // NSS pkcs12 library requires trusted CA certs in the + // certificate chain to have unique or null localKeyID. + // However, IE/OpenSSL do not impose this restriction. + bagAttrs = getBagAttributes( + cert.getSubjectX500Principal().getName(), null, + entry.attributes); + } + if (bagAttrs != null) { + safeBag.write(bagAttrs); + } + + // wrap as Sequence + out.write(DerValue.tag_Sequence, safeBag); + } // for cert-chain + } + + // wrap as SequenceOf SafeBag + DerOutputStream safeBagValue = new DerOutputStream(); + safeBagValue.write(DerValue.tag_SequenceOf, out); + byte[] safeBagData = safeBagValue.toByteArray(); + + // encrypt the content (EncryptedContentInfo) + byte[] encrContentInfo = encryptContent(safeBagData, password); + + // -- SEQUENCE of EncryptedData + DerOutputStream encrData = new DerOutputStream(); + DerOutputStream encrDataContent = new DerOutputStream(); + encrData.putInteger(0); + encrData.write(encrContentInfo); + encrDataContent.write(DerValue.tag_Sequence, encrData); + return encrDataContent.toByteArray(); + } + + /* + * Create SafeContent Data content type. + * Includes encrypted secret key in a SafeBag of type SecretBag. + * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. + * Each PKCS8ShroudedKeyBag includes pkcs12 attributes + * (see comments in getBagAttributes) + */ + private byte[] createSafeContent() + throws CertificateException, IOException { + + DerOutputStream out = new DerOutputStream(); + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { + + String alias = e.nextElement(); + Entry entry = entries.get(alias); + if (entry == null || (!(entry instanceof KeyEntry))) { + continue; + } + DerOutputStream safeBag = new DerOutputStream(); + KeyEntry keyEntry = (KeyEntry) entry; + + // DER encode the private key + if (keyEntry instanceof PrivateKeyEntry) { + // Create SafeBag of type pkcs8ShroudedKeyBag + safeBag.putOID(PKCS8ShroudedKeyBag_OID); + + // get the encrypted private key + byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey; + EncryptedPrivateKeyInfo encrInfo = null; + try { + encrInfo = new EncryptedPrivateKeyInfo(encrBytes); + + } catch (IOException ioe) { + throw new IOException("Private key not stored as " + + "PKCS#8 EncryptedPrivateKeyInfo" + + ioe.getMessage()); + } + + // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. + DerOutputStream bagValue = new DerOutputStream(); + bagValue.write(encrInfo.getEncoded()); + safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), bagValue); + + // DER encode the secret key + } else if (keyEntry instanceof SecretKeyEntry) { + // Create SafeBag of type SecretBag + safeBag.putOID(SecretBag_OID); + + // Create a SecretBag + DerOutputStream secretBag = new DerOutputStream(); + secretBag.putOID(PKCS8ShroudedKeyBag_OID); + + // Write secret key in a context-specific tag + DerOutputStream secretKeyValue = new DerOutputStream(); + secretKeyValue.putOctetString( + ((SecretKeyEntry) keyEntry).protectedSecretKey); + secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), secretKeyValue); + + // Wrap SecretBag in a Sequence + DerOutputStream secretBagSeq = new DerOutputStream(); + secretBagSeq.write(DerValue.tag_Sequence, secretBag); + byte[] secretBagValue = secretBagSeq.toByteArray(); + + // Wrap the secret bag in a context-specific tag. + DerOutputStream bagValue = new DerOutputStream(); + bagValue.write(secretBagValue); + + // Write SafeBag value + safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), bagValue); + } else { + continue; // skip this entry + } + + // write SafeBag Attributes + byte[] bagAttrs = + getBagAttributes(alias, entry.keyId, entry.attributes); + safeBag.write(bagAttrs); + + // wrap as Sequence + out.write(DerValue.tag_Sequence, safeBag); + } + + // wrap as Sequence + DerOutputStream safeBagValue = new DerOutputStream(); + safeBagValue.write(DerValue.tag_Sequence, out); + return safeBagValue.toByteArray(); + } + + + /* + * Encrypt the contents using Password-based (PBE) encryption + * as defined in PKCS #5. + * + * NOTE: Currently pbeWithSHAAnd40BiteRC2-CBC algorithmID is used + * to derive the key and IV. + * + * @return encrypted contents encoded as EncryptedContentInfo + */ + private byte[] encryptContent(byte[] data, char[] password) + throws IOException { + + byte[] encryptedData = null; + + // create AlgorithmParameters + AlgorithmParameters algParams = + getAlgorithmParameters("PBEWithSHA1AndRC2_40"); + DerOutputStream bytes = new DerOutputStream(); + AlgorithmId algId = + new AlgorithmId(pbeWithSHAAnd40BitRC2CBC_OID, algParams); + algId.encode(bytes); + byte[] encodedAlgId = bytes.toByteArray(); + + try { + // Use JCE + SecretKey skey = getPBEKey(password); + Cipher cipher = Cipher.getInstance("PBEWithSHA1AndRC2_40"); + cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); + encryptedData = cipher.doFinal(data); + + if (debug != null) { + debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + + ")"); + } + + } catch (Exception e) { + throw new IOException("Failed to encrypt" + + " safe contents entry: " + e, e); + } + + // create EncryptedContentInfo + DerOutputStream bytes2 = new DerOutputStream(); + bytes2.putOID(ContentInfo.DATA_OID); + bytes2.write(encodedAlgId); + + // Wrap encrypted data in a context-specific tag. + DerOutputStream tmpout2 = new DerOutputStream(); + tmpout2.putOctetString(encryptedData); + bytes2.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, (byte)0), tmpout2); + + // wrap EncryptedContentInfo in a Sequence + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, bytes2); + return out.toByteArray(); + } + + /** + * Loads the keystore from the given input stream. + * + *

    If a password is given, it is used to check the integrity of the + * keystore data. Otherwise, the integrity of the keystore is not checked. + * + * @param stream the input stream from which the keystore is loaded + * @param password the (optional) password used to check the integrity of + * the keystore. + * + * @exception IOException if there is an I/O or format problem with the + * keystore data + * @exception NoSuchAlgorithmException if the algorithm used to check + * the integrity of the keystore cannot be found + * @exception CertificateException if any of the certificates in the + * keystore could not be loaded + */ + public synchronized void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + DataInputStream dis; + CertificateFactory cf = null; + ByteArrayInputStream bais = null; + byte[] encoded = null; + + if (stream == null) + return; + + // reset the counter + counter = 0; + + DerValue val = new DerValue(stream); + DerInputStream s = val.toDerInputStream(); + int version = s.getInteger(); + + if (version != VERSION_3) { + throw new IOException("PKCS12 keystore not in version 3 format"); + } + + entries.clear(); + + /* + * Read the authSafe. + */ + byte[] authSafeData; + ContentInfo authSafe = new ContentInfo(s); + ObjectIdentifier contentType = authSafe.getContentType(); + + if (contentType.equals((Object)ContentInfo.DATA_OID)) { + authSafeData = authSafe.getData(); + } else /* signed data */ { + throw new IOException("public key protected PKCS12 not supported"); + } + + DerInputStream as = new DerInputStream(authSafeData); + DerValue[] safeContentsArray = as.getSequence(2); + int count = safeContentsArray.length; + + // reset the counters at the start + privateKeyCount = 0; + secretKeyCount = 0; + certificateCount = 0; + + /* + * Spin over the ContentInfos. + */ + for (int i = 0; i < count; i++) { + byte[] safeContentsData; + ContentInfo safeContents; + DerInputStream sci; + byte[] eAlgId = null; + + sci = new DerInputStream(safeContentsArray[i].toByteArray()); + safeContents = new ContentInfo(sci); + contentType = safeContents.getContentType(); + safeContentsData = null; + if (contentType.equals((Object)ContentInfo.DATA_OID)) { + + if (debug != null) { + debug.println("Loading PKCS#7 data content-type"); + } + + safeContentsData = safeContents.getData(); + } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) { + if (password == null) { + continue; + } + + if (debug != null) { + debug.println("Loading PKCS#7 encryptedData content-type"); + } + + DerInputStream edi = + safeContents.getContent().toDerInputStream(); + int edVersion = edi.getInteger(); + DerValue[] seq = edi.getSequence(2); + ObjectIdentifier edContentType = seq[0].getOID(); + eAlgId = seq[1].toByteArray(); + if (!seq[2].isContextSpecific((byte)0)) { + throw new IOException("encrypted content not present!"); + } + byte newTag = DerValue.tag_OctetString; + if (seq[2].isConstructed()) + newTag |= 0x20; + seq[2].resetTag(newTag); + safeContentsData = seq[2].getOctetString(); + + // parse Algorithm parameters + DerInputStream in = seq[1].toDerInputStream(); + ObjectIdentifier algOid = in.getOID(); + AlgorithmParameters algParams = parseAlgParameters(algOid, in); + + while (true) { + try { + // Use JCE + SecretKey skey = getPBEKey(password); + Cipher cipher = Cipher.getInstance(algOid.toString()); + cipher.init(Cipher.DECRYPT_MODE, skey, algParams); + safeContentsData = cipher.doFinal(safeContentsData); + break; + } catch (Exception e) { + if (password.length == 0) { + // Retry using an empty password + // without a NULL terminator. + password = new char[1]; + continue; + } + throw new IOException( + "failed to decrypt safe contents entry: " + e, e); + } + } + } else { + throw new IOException("public key protected PKCS12" + + " not supported"); + } + DerInputStream sc = new DerInputStream(safeContentsData); + loadSafeContents(sc, password); + } + + // The MacData is optional. + if (password != null && s.available() > 0) { + MacData macData = new MacData(s); + try { + String algName = + macData.getDigestAlgName().toUpperCase(Locale.ENGLISH); + + // Change SHA-1 to SHA1 + algName = algName.replace("-", ""); + + // generate MAC (MAC key is created within JCE) + Mac m = Mac.getInstance("HmacPBE" + algName); + PBEParameterSpec params = + new PBEParameterSpec(macData.getSalt(), + macData.getIterations()); + SecretKey key = getPBEKey(password); + m.init(key, params); + m.update(authSafeData); + byte[] macResult = m.doFinal(); + + if (debug != null) { + debug.println("Checking keystore integrity " + + "(MAC algorithm: " + m.getAlgorithm() + ")"); + } + + if (!Arrays.equals(macData.getDigest(), macResult)) { + throw new SecurityException("Failed PKCS12" + + " integrity checking"); + } + } catch (Exception e) { + throw new IOException("Integrity check failed: " + e, e); + } + } + + /* + * Match up private keys with certificate chains. + */ + PrivateKeyEntry[] list = + keyList.toArray(new PrivateKeyEntry[keyList.size()]); + for (int m = 0; m < list.length; m++) { + PrivateKeyEntry entry = list[m]; + if (entry.keyId != null) { + ArrayList chain = + new ArrayList(); + X509Certificate cert = findMatchedCertificate(entry); + while (cert != null) { + chain.add(cert); + X500Principal issuerDN = cert.getIssuerX500Principal(); + if (issuerDN.equals(cert.getSubjectX500Principal())) { + break; + } + cert = certsMap.get(issuerDN); + } + /* Update existing KeyEntry in entries table */ + if (chain.size() > 0) + entry.chain = chain.toArray(new Certificate[chain.size()]); + } + } + + if (debug != null) { + if (privateKeyCount > 0) { + debug.println("Loaded " + privateKeyCount + + " protected private key(s)"); + } + if (secretKeyCount > 0) { + debug.println("Loaded " + secretKeyCount + + " protected secret key(s)"); + } + if (certificateCount > 0) { + debug.println("Loaded " + certificateCount + + " certificate(s)"); + } + } + + certEntries.clear(); + certsMap.clear(); + keyList.clear(); + } + + /** + * Locates a matched CertEntry from certEntries, and returns its cert. + * @param entry the KeyEntry to match + * @return a certificate, null if not found + */ + private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) { + CertEntry keyIdMatch = null; + CertEntry aliasMatch = null; + for (CertEntry ce: certEntries) { + if (Arrays.equals(entry.keyId, ce.keyId)) { + keyIdMatch = ce; + if (entry.alias.equalsIgnoreCase(ce.alias)) { + // Full match! + return ce.cert; + } + } else if (entry.alias.equalsIgnoreCase(ce.alias)) { + aliasMatch = ce; + } + } + // keyId match first, for compatibility + if (keyIdMatch != null) return keyIdMatch.cert; + else if (aliasMatch != null) return aliasMatch.cert; + else return null; + } + + private void loadSafeContents(DerInputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + DerValue[] safeBags = stream.getSequence(2); + int count = safeBags.length; + + /* + * Spin over the SafeBags. + */ + for (int i = 0; i < count; i++) { + ObjectIdentifier bagId; + DerInputStream sbi; + DerValue bagValue; + Object bagItem = null; + + sbi = safeBags[i].toDerInputStream(); + bagId = sbi.getOID(); + bagValue = sbi.getDerValue(); + if (!bagValue.isContextSpecific((byte)0)) { + throw new IOException("unsupported PKCS12 bag value type " + + bagValue.tag); + } + bagValue = bagValue.data.getDerValue(); + if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) { + PrivateKeyEntry kEntry = new PrivateKeyEntry(); + kEntry.protectedPrivKey = bagValue.toByteArray(); + bagItem = kEntry; + privateKeyCount++; + } else if (bagId.equals((Object)CertBag_OID)) { + DerInputStream cs = new DerInputStream(bagValue.toByteArray()); + DerValue[] certValues = cs.getSequence(2); + ObjectIdentifier certId = certValues[0].getOID(); + if (!certValues[1].isContextSpecific((byte)0)) { + throw new IOException("unsupported PKCS12 cert value type " + + certValues[1].tag); + } + DerValue certValue = certValues[1].data.getDerValue(); + CertificateFactory cf = CertificateFactory.getInstance("X509"); + X509Certificate cert; + cert = (X509Certificate)cf.generateCertificate + (new ByteArrayInputStream(certValue.getOctetString())); + bagItem = cert; + certificateCount++; + } else if (bagId.equals((Object)SecretBag_OID)) { + DerInputStream ss = new DerInputStream(bagValue.toByteArray()); + DerValue[] secretValues = ss.getSequence(2); + ObjectIdentifier secretId = secretValues[0].getOID(); + if (!secretValues[1].isContextSpecific((byte)0)) { + throw new IOException( + "unsupported PKCS12 secret value type " + + secretValues[1].tag); + } + DerValue secretValue = secretValues[1].data.getDerValue(); + SecretKeyEntry kEntry = new SecretKeyEntry(); + kEntry.protectedSecretKey = secretValue.getOctetString(); + bagItem = kEntry; + secretKeyCount++; + } else { + + if (debug != null) { + debug.println("Unsupported PKCS12 bag type: " + bagId); + } + } + + DerValue[] attrSet; + try { + attrSet = sbi.getSet(3); + } catch (IOException e) { + // entry does not have attributes + // Note: CA certs can have no attributes + // OpenSSL generates pkcs12 with no attr for CA certs. + attrSet = null; + } + + String alias = null; + byte[] keyId = null; + ObjectIdentifier[] trustedKeyUsage = null; + Set attributes = new HashSet<>(); + + if (attrSet != null) { + for (int j = 0; j < attrSet.length; j++) { + byte[] encoded = attrSet[j].toByteArray(); + DerInputStream as = new DerInputStream(encoded); + DerValue[] attrSeq = as.getSequence(2); + ObjectIdentifier attrId = attrSeq[0].getOID(); + DerInputStream vs = + new DerInputStream(attrSeq[1].toByteArray()); + DerValue[] valSet; + try { + valSet = vs.getSet(1); + } catch (IOException e) { + throw new IOException("Attribute " + attrId + + " should have a value " + e.getMessage()); + } + if (attrId.equals((Object)PKCS9FriendlyName_OID)) { + alias = valSet[0].getBMPString(); + } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { + keyId = valSet[0].getOctetString(); + } else if + (attrId.equals((Object)TrustedKeyUsage_OID)) { + trustedKeyUsage = new ObjectIdentifier[valSet.length]; + for (int k = 0; k < valSet.length; k++) { + trustedKeyUsage[k] = valSet[k].getOID(); + } + } else { + attributes.add(new PKCS12Attribute(encoded)); + } + } + } + + /* + * As per PKCS12 v1.0 friendlyname (alias) and localKeyId (keyId) + * are optional PKCS12 bagAttributes. But entries in the keyStore + * are identified by their alias. Hence we need to have an + * Unfriendlyname in the alias, if alias is null. The keyId + * attribute is required to match the private key with the + * certificate. If we get a bagItem of type KeyEntry with a + * null keyId, we should skip it entirely. + */ + if (bagItem instanceof KeyEntry) { + KeyEntry entry = (KeyEntry)bagItem; + + if (bagItem instanceof PrivateKeyEntry) { + if (keyId == null) { + // Insert a localKeyID for the privateKey + // Note: This is a workaround to allow null localKeyID + // attribute in pkcs12 with one private key entry and + // associated cert-chain + if (privateKeyCount == 1) { + keyId = "01".getBytes("UTF8"); + } else { + continue; + } + } + } + entry.keyId = keyId; + // restore date if it exists + String keyIdStr = new String(keyId, "UTF8"); + Date date = null; + if (keyIdStr.startsWith("Time ")) { + try { + date = new Date( + Long.parseLong(keyIdStr.substring(5))); + } catch (Exception e) { + date = null; + } + } + if (date == null) { + date = new Date(); + } + entry.date = date; + + if (bagItem instanceof PrivateKeyEntry) { + keyList.add((PrivateKeyEntry) entry); + } + if (entry.attributes == null) { + entry.attributes = new HashSet<>(); + } + entry.attributes.addAll(attributes); + if (alias == null) { + alias = getUnfriendlyName(); + } + entry.alias = alias; + entries.put(alias.toLowerCase(Locale.ENGLISH), entry); + + } else if (bagItem instanceof X509Certificate) { + X509Certificate cert = (X509Certificate)bagItem; + // Insert a localKeyID for the corresponding cert + // Note: This is a workaround to allow null localKeyID + // attribute in pkcs12 with one private key entry and + // associated cert-chain + if ((keyId == null) && (privateKeyCount == 1)) { + // insert localKeyID only for EE cert or self-signed cert + if (i == 0) { + keyId = "01".getBytes("UTF8"); + } + } + // Trusted certificate + if (trustedKeyUsage != null) { + if (alias == null) { + alias = getUnfriendlyName(); + } + CertEntry certEntry = + new CertEntry(cert, keyId, alias, trustedKeyUsage, + attributes); + entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); + } else { + certEntries.add(new CertEntry(cert, keyId, alias)); + } + X500Principal subjectDN = cert.getSubjectX500Principal(); + if (subjectDN != null) { + if (!certsMap.containsKey(subjectDN)) { + certsMap.put(subjectDN, cert); + } + } + } + } + } + + private String getUnfriendlyName() { + counter++; + return (String.valueOf(counter)); + } +} diff --git a/src/sun/security/provider/AuthPolicyFile.java b/src/sun/security/provider/AuthPolicyFile.java new file mode 100644 index 00000000..4e5bf1f8 --- /dev/null +++ b/src/sun/security/provider/AuthPolicyFile.java @@ -0,0 +1,1195 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.lang.reflect.*; +import java.net.URL; +import java.util.*; + +import java.security.AccessController; +import java.security.CodeSource; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.Permission; +import java.security.Permissions; +import java.security.PermissionCollection; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.UnresolvedPermission; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.security.auth.Subject; +import javax.security.auth.PrivateCredentialPermission; + +import sun.security.provider.PolicyParser.GrantEntry; +import sun.security.provider.PolicyParser.PermissionEntry; +import sun.security.provider.PolicyParser.PrincipalEntry; +import sun.security.util.Debug; +import sun.security.util.PolicyUtil; +import sun.security.util.PropertyExpander; + +/** + * See {@code com.sun.security.auth.PolicyFile} for the class description. + * This class is necessary in order to support a default + * {@code javax.security.auth.Policy} implementation on the compact1 and + * compact2 profiles. + * + * @deprecated As of JDK 1.4, replaced by + * {@code sun.security.provider.PolicyFile}. + * This class is entirely deprecated. + */ +@Deprecated +public class AuthPolicyFile extends javax.security.auth.Policy { + + static final ResourceBundle rb = + AccessController.doPrivileged(new PrivilegedAction() { + @Override public ResourceBundle run() { + return (ResourceBundle.getBundle + ("sun.security.util.AuthResources")); + } + }); + + private static final Debug debug = Debug.getInstance("policy", + "\t[Auth Policy]"); + + private static final String AUTH_POLICY = "java.security.auth.policy"; + private static final String SECURITY_MANAGER = "java.security.manager"; + private static final String AUTH_POLICY_URL = "auth.policy.url."; + + private Vector policyEntries; + private Hashtable aliasMapping; + + private boolean initialized = false; + + private boolean expandProperties = true; + private boolean ignoreIdentityScope = true; + + // for use with the reflection API + private static final Class[] PARAMS = { String.class, String.class}; + + /** + * Initializes the Policy object and reads the default policy + * configuration file(s) into the Policy object. + */ + public AuthPolicyFile() { + // initialize Policy if either the AUTH_POLICY or + // SECURITY_MANAGER properties are set + String prop = System.getProperty(AUTH_POLICY); + + if (prop == null) { + prop = System.getProperty(SECURITY_MANAGER); + } + if (prop != null) { + init(); + } + } + + private synchronized void init() { + if (initialized) { + return; + } + + policyEntries = new Vector(); + aliasMapping = new Hashtable(11); + + initPolicyFile(); + initialized = true; + } + + @Override + public synchronized void refresh() { + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new javax.security.auth.AuthPermission + ("refreshPolicy")); + } + + // XXX + // + // 1) if code instantiates PolicyFile directly, then it will need + // all the permissions required for the PolicyFile initialization + // 2) if code calls Policy.getPolicy, then it simply needs + // AuthPermission(getPolicy), and the javax.security.auth.Policy + // implementation instantiates PolicyFile in a doPrivileged block + // 3) if after instantiating a Policy (either via #1 or #2), + // code calls refresh, it simply needs + // AuthPermission(refreshPolicy). then PolicyFile wraps + // the refresh in a doPrivileged block. + initialized = false; + AccessController.doPrivileged(new PrivilegedAction() { + @Override public Void run() { + init(); + return null; + } + }); + } + + private KeyStore initKeyStore(URL policyUrl, String keyStoreName, + String keyStoreType) { + if (keyStoreName != null) { + try { + /* + * location of keystore is specified as absolute URL in policy + * file, or is relative to URL of policy file + */ + URL keyStoreUrl = null; + try { + keyStoreUrl = new URL(keyStoreName); + // absolute URL + } catch (java.net.MalformedURLException e) { + // relative URL + keyStoreUrl = new URL(policyUrl, keyStoreName); + } + + if (debug != null) { + debug.println("reading keystore"+keyStoreUrl); + } + + InputStream inStream = new BufferedInputStream( + PolicyUtil.getInputStream(keyStoreUrl)); + + KeyStore ks; + if (keyStoreType != null) + ks = KeyStore.getInstance(keyStoreType); + else + ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(inStream, null); + inStream.close(); + return ks; + } catch (Exception e) { + // ignore, treat it like we have no keystore + if (debug != null) { + e.printStackTrace(); + } + return null; + } + } + return null; + } + + private void initPolicyFile() { + + String prop = Security.getProperty("policy.expandProperties"); + if (prop != null) { + expandProperties = prop.equalsIgnoreCase("true"); + } + + String iscp = Security.getProperty("policy.ignoreIdentityScope"); + if (iscp != null) { + ignoreIdentityScope = iscp.equalsIgnoreCase("true"); + } + + String allowSys = Security.getProperty("policy.allowSystemProperty"); + if (allowSys != null && allowSys.equalsIgnoreCase("true")) { + String extra_policy = System.getProperty(AUTH_POLICY); + if (extra_policy != null) { + boolean overrideAll = false; + if (extra_policy.startsWith("=")) { + overrideAll = true; + extra_policy = extra_policy.substring(1); + } + try { + extra_policy = PropertyExpander.expand(extra_policy); + URL policyURL; + File policyFile = new File(extra_policy); + if (policyFile.exists()) { + policyURL = + new URL("file:" + policyFile.getCanonicalPath()); + } else { + policyURL = new URL(extra_policy); + } + if (debug != null) { + debug.println("reading " + policyURL); + } + init(policyURL); + } catch (Exception e) { + // ignore. + if (debug != null) { + debug.println("caught exception: " + e); + } + + } + if (overrideAll) { + if (debug != null) { + debug.println("overriding other policies!"); + } + return; + } + } + } + + int n = 1; + boolean loaded_one = false; + String policy_url; + + while ((policy_url = Security.getProperty(AUTH_POLICY_URL+n)) != null) { + try { + policy_url = PropertyExpander.expand(policy_url).replace + (File.separatorChar, '/'); + if (debug != null) { + debug.println("reading " + policy_url); + } + init(new URL(policy_url)); + loaded_one = true; + } catch (Exception e) { + if (debug != null) { + debug.println("error reading policy " + e); + e.printStackTrace(); + } + // ignore that policy + } + n++; + } + + if (loaded_one == false) { + // do not load a static policy + } + } + + /** + * Checks public key. If it is marked as trusted in + * the identity database, add it to the policy + * with the AllPermission. + */ + private boolean checkForTrustedIdentity(final Certificate cert) { + return false; + } + + /** + * Reads a policy configuration into the Policy object using a + * Reader object. + * + * @param policyFile the policy Reader object. + */ + private void init(URL policy) { + PolicyParser pp = new PolicyParser(expandProperties); + try (InputStreamReader isr + = new InputStreamReader(PolicyUtil.getInputStream(policy))) { + pp.read(isr); + KeyStore keyStore = initKeyStore(policy, pp.getKeyStoreUrl(), + pp.getKeyStoreType()); + Enumeration enum_ = pp.grantElements(); + while (enum_.hasMoreElements()) { + GrantEntry ge = enum_.nextElement(); + addGrantEntry(ge, keyStore); + } + } catch (PolicyParser.ParsingException pe) { + System.err.println(AUTH_POLICY + + rb.getString(".error.parsing.") + policy); + System.err.println(AUTH_POLICY + rb.getString("COLON") + + pe.getMessage()); + if (debug != null) { + pe.printStackTrace(); + } + } catch (Exception e) { + if (debug != null) { + debug.println("error parsing " + policy); + debug.println(e.toString()); + e.printStackTrace(); + } + } + } + + /** + * Given a PermissionEntry, create a codeSource. + * + * @return null if signedBy alias is not recognized + */ + CodeSource getCodeSource(GrantEntry ge, KeyStore keyStore) + throws java.net.MalformedURLException + { + Certificate[] certs = null; + if (ge.signedBy != null) { + certs = getCertificates(keyStore, ge.signedBy); + if (certs == null) { + // we don't have a key for this alias, + // just return + if (debug != null) { + debug.println(" no certs for alias " + + ge.signedBy + ", ignoring."); + } + return null; + } + } + + URL location; + if (ge.codeBase != null) { + location = new URL(ge.codeBase); + } else { + location = null; + } + + if (ge.principals == null || ge.principals.size() == 0) { + return (canonicalizeCodebase + (new CodeSource(location, certs), + false)); + } else { + return (canonicalizeCodebase + (new SubjectCodeSource(null, ge.principals, location, certs), + false)); + } + } + + /** + * Add one policy entry to the vector. + */ + private void addGrantEntry(GrantEntry ge, KeyStore keyStore) { + + if (debug != null) { + debug.println("Adding policy entry: "); + debug.println(" signedBy " + ge.signedBy); + debug.println(" codeBase " + ge.codeBase); + if (ge.principals != null) { + for (PrincipalEntry pppe : ge.principals) { + debug.println(" " + pppe.getPrincipalClass() + + " " + pppe.getPrincipalName()); + } + } + debug.println(); + } + + try { + CodeSource codesource = getCodeSource(ge, keyStore); + // skip if signedBy alias was unknown... + if (codesource == null) return; + + PolicyEntry entry = new PolicyEntry(codesource); + Enumeration enum_ = ge.permissionElements(); + while (enum_.hasMoreElements()) { + PermissionEntry pe = enum_.nextElement(); + try { + // XXX special case PrivateCredentialPermission-SELF + Permission perm; + if (pe.permission.equals + ("javax.security.auth.PrivateCredentialPermission") && + pe.name.endsWith(" self")) { + perm = getInstance(pe.permission, + pe.name + " \"self\"", + pe.action); + } else { + perm = getInstance(pe.permission, + pe.name, + pe.action); + } + entry.add(perm); + if (debug != null) { + debug.println(" "+perm); + } + } catch (ClassNotFoundException cnfe) { + Certificate certs[]; + if (pe.signedBy != null) { + certs = getCertificates(keyStore, pe.signedBy); + } else { + certs = null; + } + + // only add if we had no signer or we had a + // a signer and found the keys for it. + if (certs != null || pe.signedBy == null) { + Permission perm = new UnresolvedPermission( + pe.permission, + pe.name, + pe.action, + certs); + entry.add(perm); + if (debug != null) { + debug.println(" "+perm); + } + } + } catch (InvocationTargetException ite) { + System.err.println + (AUTH_POLICY + + rb.getString(".error.adding.Permission.") + + pe.permission + + rb.getString("SPACE") + + ite.getTargetException()); + } catch (Exception e) { + System.err.println + (AUTH_POLICY + + rb.getString(".error.adding.Permission.") + + pe.permission + + rb.getString("SPACE") + + e); + } + } + policyEntries.addElement(entry); + } catch (Exception e) { + System.err.println + (AUTH_POLICY + + rb.getString(".error.adding.Entry.") + + ge + + rb.getString("SPACE") + + e); + } + + if (debug != null) { + debug.println(); + } + } + + /** + * Returns a new Permission object of the given Type. The Permission is + * created by getting the + * Class object using the Class.forName method, and using + * the reflection API to invoke the (String name, String actions) + * constructor on the + * object. + * + * @param type the type of Permission being created. + * @param name the name of the Permission being created. + * @param actions the actions of the Permission being created. + * + * @exception ClassNotFoundException if the particular Permission + * class could not be found. + * + * @exception IllegalAccessException if the class or initializer is + * not accessible. + * + * @exception InstantiationException if getInstance tries to + * instantiate an abstract class or an interface, or if the + * instantiation fails for some other reason. + * + * @exception NoSuchMethodException if the (String, String) constructor + * is not found. + * + * @exception InvocationTargetException if the underlying Permission + * constructor throws an exception. + * + */ + private static final Permission getInstance(String type, + String name, + String actions) + throws ClassNotFoundException, + InstantiationException, + IllegalAccessException, + NoSuchMethodException, + InvocationTargetException + { + //XXX we might want to keep a hash of created factories... + Class pc = Class.forName(type); + Constructor c = pc.getConstructor(PARAMS); + return (Permission) c.newInstance(new Object[] { name, actions }); + } + + /** + * Fetch all certs associated with this alias. + */ + Certificate[] getCertificates(KeyStore keyStore, String aliases) { + + Vector vcerts = null; + + StringTokenizer st = new StringTokenizer(aliases, ","); + int n = 0; + + while (st.hasMoreTokens()) { + String alias = st.nextToken().trim(); + n++; + Certificate cert = null; + // See if this alias's cert has already been cached + cert = (Certificate) aliasMapping.get(alias); + if (cert == null && keyStore != null) { + + try { + cert = keyStore.getCertificate(alias); + } catch (KeyStoreException kse) { + // never happens, because keystore has already been loaded + // when we call this + } + if (cert != null) { + aliasMapping.put(alias, cert); + aliasMapping.put(cert, alias); + } + } + + if (cert != null) { + if (vcerts == null) { + vcerts = new Vector(); + } + vcerts.addElement(cert); + } + } + + // make sure n == vcerts.size, since we are doing a logical *and* + if (vcerts != null && n == vcerts.size()) { + Certificate[] certs = new Certificate[vcerts.size()]; + vcerts.copyInto(certs); + return certs; + } else { + return null; + } + } + + /** + * Enumerate all the entries in the global policy object. + * This method is used by policy admin tools. The tools + * should use the Enumeration methods on the returned object + * to fetch the elements sequentially. + */ + private final synchronized Enumeration elements() { + return policyEntries.elements(); + } + + @Override + public PermissionCollection getPermissions(final Subject subject, + final CodeSource codesource) { + + // 1) if code instantiates PolicyFile directly, then it will need + // all the permissions required for the PolicyFile initialization + // 2) if code calls Policy.getPolicy, then it simply needs + // AuthPermission(getPolicy), and the javax.security.auth.Policy + // implementation instantiates PolicyFile in a doPrivileged block + // 3) if after instantiating a Policy (either via #1 or #2), + // code calls getPermissions, PolicyFile wraps the call + // in a doPrivileged block. + return AccessController.doPrivileged + (new PrivilegedAction() { + @Override public PermissionCollection run() { + SubjectCodeSource scs = new SubjectCodeSource( + subject, null, + codesource == null ? null : codesource.getLocation(), + codesource == null ? null : codesource.getCertificates()); + if (initialized) { + return getPermissions(new Permissions(), scs); + } else { + return new PolicyPermissions(AuthPolicyFile.this, scs); + } + } + }); + } + + /** + * Examines the global policy for the specified CodeSource, and + * creates a PermissionCollection object with + * the set of permissions for that principal's protection domain. + * + * @param CodeSource the codesource associated with the caller. + * This encapsulates the original location of the code (where the code + * came from) and the public key(s) of its signer. + * + * @return the set of permissions according to the policy. + */ + PermissionCollection getPermissions(CodeSource codesource) { + + if (initialized) { + return getPermissions(new Permissions(), codesource); + } else { + return new PolicyPermissions(this, codesource); + } + } + + /** + * Examines the global policy for the specified CodeSource, and + * creates a PermissionCollection object with + * the set of permissions for that principal's protection domain. + * + * @param permissions the permissions to populate + * @param codesource the codesource associated with the caller. + * This encapsulates the original location of the code (where the code + * came from) and the public key(s) of its signer. + * + * @return the set of permissions according to the policy. + */ + Permissions getPermissions(final Permissions perms, + final CodeSource cs) + { + if (!initialized) { + init(); + } + + final CodeSource codesource[] = {null}; + + codesource[0] = canonicalizeCodebase(cs, true); + + if (debug != null) { + debug.println("evaluate(" + codesource[0] + ")\n"); + } + + // needs to be in a begin/endPrivileged block because + // codesource.implies calls URL.equals which does an + // InetAddress lookup + + for (int i = 0; i < policyEntries.size(); i++) { + + PolicyEntry entry = policyEntries.elementAt(i); + + if (debug != null) { + debug.println("PolicyFile CodeSource implies: " + + entry.codesource.toString() + "\n\n" + + "\t" + codesource[0].toString() + "\n\n"); + } + + if (entry.codesource.implies(codesource[0])) { + for (int j = 0; j < entry.permissions.size(); j++) { + Permission p = entry.permissions.elementAt(j); + if (debug != null) { + debug.println(" granting " + p); + } + if (!addSelfPermissions(p, entry.codesource, + codesource[0], perms)) { + // we could check for duplicates + // before adding new permissions, + // but the SubjectDomainCombiner + // already checks for duplicates later + perms.add(p); + } + } + } + } + + // now see if any of the keys are trusted ids. + + if (!ignoreIdentityScope) { + Certificate certs[] = codesource[0].getCertificates(); + if (certs != null) { + for (int k=0; k < certs.length; k++) { + if (aliasMapping.get(certs[k]) == null && + checkForTrustedIdentity(certs[k])) { + // checkForTrustedIdentity added it + // to the policy for us. next time + // around we'll find it. This time + // around we need to add it. + perms.add(new java.security.AllPermission()); + } + } + } + } + return perms; + } + + /** + * Returns true if 'Self' permissions were added to the provided + * 'perms', and false otherwise. + * + *

    + * + * @param p check to see if this Permission is a "SELF" + * PrivateCredentialPermission.

    + * + * @param entryCs the codesource for the Policy entry. + * + * @param accCs the codesource for from the current AccessControlContext. + * + * @param perms the PermissionCollection where the individual + * PrivateCredentialPermissions will be added. + */ + private boolean addSelfPermissions(final Permission p, + CodeSource entryCs, + CodeSource accCs, + Permissions perms) { + + if (!(p instanceof PrivateCredentialPermission)) { + return false; + } + + if (!(entryCs instanceof SubjectCodeSource)) { + return false; + } + + PrivateCredentialPermission pcp = (PrivateCredentialPermission)p; + SubjectCodeSource scs = (SubjectCodeSource)entryCs; + + // see if it is a SELF permission + String[][] pPrincipals = pcp.getPrincipals(); + if (pPrincipals.length <= 0 || + !pPrincipals[0][0].equalsIgnoreCase("self") || + !pPrincipals[0][1].equalsIgnoreCase("self")) { + + // regular PrivateCredentialPermission + return false; + } else { + + // granted a SELF permission - create a + // PrivateCredentialPermission for each + // of the Policy entry's CodeSource Principals + + if (scs.getPrincipals() == null) { + // XXX SubjectCodeSource has no Subject??? + return true; + } + + for (PrincipalEntry principal : scs.getPrincipals()) { + + // if the Policy entry's Principal does not contain a + // WILDCARD for the Principal name, then a + // new PrivateCredentialPermission is created + // for the Principal listed in the Policy entry. + // if the Policy entry's Principal contains a WILDCARD + // for the Principal name, then a new + // PrivateCredentialPermission is created + // for each Principal associated with the Subject + // in the current ACC. + + String[][] principalInfo = getPrincipalInfo(principal, accCs); + + for (int i = 0; i < principalInfo.length; i++) { + + // here's the new PrivateCredentialPermission + + PrivateCredentialPermission newPcp = + new PrivateCredentialPermission + (pcp.getCredentialClass() + + " " + + principalInfo[i][0] + + " " + + "\"" + principalInfo[i][1] + "\"", + "read"); + + if (debug != null) { + debug.println("adding SELF permission: " + + newPcp.toString()); + } + + perms.add(newPcp); + } + } + } + return true; + } + + /** + * return the principal class/name pair in the 2D array. + * array[x][y]: x corresponds to the array length. + * if (y == 0), it's the principal class. + * if (y == 1), it's the principal name. + */ + private String[][] getPrincipalInfo(PrincipalEntry principal, + final CodeSource accCs) { + + // there are 3 possibilities: + // 1) the entry's Principal class and name are not wildcarded + // 2) the entry's Principal name is wildcarded only + // 3) the entry's Principal class and name are wildcarded + + if (!principal.getPrincipalClass().equals + (PrincipalEntry.WILDCARD_CLASS) && + !principal.getPrincipalName().equals + (PrincipalEntry.WILDCARD_NAME)) { + + // build a PrivateCredentialPermission for the principal + // from the Policy entry + String[][] info = new String[1][2]; + info[0][0] = principal.getPrincipalClass(); + info[0][1] = principal.getPrincipalName(); + return info; + + } else if (!principal.getPrincipalClass().equals + (PrincipalEntry.WILDCARD_CLASS) && + principal.getPrincipalName().equals + (PrincipalEntry.WILDCARD_NAME)) { + + // build a PrivateCredentialPermission for all + // the Subject's principals that are instances of principalClass + + // the accCs is guaranteed to be a SubjectCodeSource + // because the earlier CodeSource.implies succeeded + SubjectCodeSource scs = (SubjectCodeSource)accCs; + + Set principalSet = null; + try { + // principal.principalClass should extend Principal + // If it doesn't, we should stop here with a ClassCastException. + @SuppressWarnings("unchecked") + Class pClass = (Class) + Class.forName(principal.getPrincipalClass(), false, + ClassLoader.getSystemClassLoader()); + principalSet = scs.getSubject().getPrincipals(pClass); + } catch (Exception e) { + if (debug != null) { + debug.println("problem finding Principal Class " + + "when expanding SELF permission: " + + e.toString()); + } + } + + if (principalSet == null) { + // error + return new String[0][0]; + } + + String[][] info = new String[principalSet.size()][2]; + + int i = 0; + for (Principal p : principalSet) { + info[i][0] = p.getClass().getName(); + info[i][1] = p.getName(); + i++; + } + return info; + + } else { + + // build a PrivateCredentialPermission for every + // one of the current Subject's principals + + // the accCs is guaranteed to be a SubjectCodeSource + // because the earlier CodeSource.implies succeeded + SubjectCodeSource scs = (SubjectCodeSource)accCs; + Set principalSet = scs.getSubject().getPrincipals(); + + String[][] info = new String[principalSet.size()][2]; + + int i = 0; + for (Principal p : principalSet) { + info[i][0] = p.getClass().getName(); + info[i][1] = p.getName(); + i++; + } + return info; + } + } + + /* + * Returns the signer certificates from the list of certificates associated + * with the given code source. + * + * The signer certificates are those certificates that were used to verify + * signed code originating from the codesource location. + * + * This method assumes that in the given code source, each signer + * certificate is followed by its supporting certificate chain + * (which may be empty), and that the signer certificate and its + * supporting certificate chain are ordered bottom-to-top (i.e., with the + * signer certificate first and the (root) certificate authority last). + */ + Certificate[] getSignerCertificates(CodeSource cs) { + Certificate[] certs = null; + if ((certs = cs.getCertificates()) == null) { + return null; + } + for (int i = 0; i < certs.length; i++) { + if (!(certs[i] instanceof X509Certificate)) + return cs.getCertificates(); + } + + // Do we have to do anything? + int i = 0; + int count = 0; + while (i < certs.length) { + count++; + while (((i+1) < certs.length) + && ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i+1]).getSubjectDN())) { + i++; + } + i++; + } + if (count == certs.length) { + // Done + return certs; + } + + ArrayList userCertList = new ArrayList<>(); + i = 0; + while (i < certs.length) { + userCertList.add(certs[i]); + while (((i+1) < certs.length) + && ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i+1]).getSubjectDN())) { + i++; + } + i++; + } + Certificate[] userCerts = new Certificate[userCertList.size()]; + userCertList.toArray(userCerts); + return userCerts; + } + + private CodeSource canonicalizeCodebase(CodeSource cs, + boolean extractSignerCerts) { + CodeSource canonCs = cs; + if (cs.getLocation() != null && + cs.getLocation().getProtocol().equalsIgnoreCase("file")) { + try { + String path = cs.getLocation().getFile().replace + ('/', + File.separatorChar); + URL csUrl = null; + if (path.endsWith("*")) { + // remove trailing '*' because it causes canonicalization + // to fail on win32 + path = path.substring(0, path.length()-1); + boolean appendFileSep = false; + if (path.endsWith(File.separator)) { + appendFileSep = true; + } + if (path.equals("")) { + path = System.getProperty("user.dir"); + } + File f = new File(path); + path = f.getCanonicalPath(); + StringBuffer sb = new StringBuffer(path); + // reappend '*' to canonicalized filename (note that + // canonicalization may have removed trailing file + // separator, so we have to check for that, too) + if (!path.endsWith(File.separator) && + (appendFileSep || f.isDirectory())) { + sb.append(File.separatorChar); + } + sb.append('*'); + path = sb.toString(); + } else { + path = new File(path).getCanonicalPath(); + } + csUrl = new File(path).toURL(); + + if (cs instanceof SubjectCodeSource) { + SubjectCodeSource scs = (SubjectCodeSource)cs; + if (extractSignerCerts) { + canonCs = new SubjectCodeSource(scs.getSubject(), + scs.getPrincipals(), + csUrl, + getSignerCertificates(scs)); + } else { + canonCs = new SubjectCodeSource(scs.getSubject(), + scs.getPrincipals(), + csUrl, + scs.getCertificates()); + } + } else { + if (extractSignerCerts) { + canonCs = new CodeSource(csUrl, + getSignerCertificates(cs)); + } else { + canonCs = new CodeSource(csUrl, + cs.getCertificates()); + } + } + } catch (IOException ioe) { + // leave codesource as it is, unless we have to extract its + // signer certificates + if (extractSignerCerts) { + if (!(cs instanceof SubjectCodeSource)) { + canonCs = new CodeSource(cs.getLocation(), + getSignerCertificates(cs)); + } else { + SubjectCodeSource scs = (SubjectCodeSource)cs; + canonCs = new SubjectCodeSource(scs.getSubject(), + scs.getPrincipals(), + scs.getLocation(), + getSignerCertificates(scs)); + } + } + } + } else { + if (extractSignerCerts) { + if (!(cs instanceof SubjectCodeSource)) { + canonCs = new CodeSource(cs.getLocation(), + getSignerCertificates(cs)); + } else { + SubjectCodeSource scs = (SubjectCodeSource)cs; + canonCs = new SubjectCodeSource(scs.getSubject(), + scs.getPrincipals(), + scs.getLocation(), + getSignerCertificates(scs)); + } + } + } + return canonCs; + } + + /** + * Each entry in the policy configuration file is represented by a + * PolicyEntry object.

    + * + * A PolicyEntry is a (CodeSource,Permission) pair. The + * CodeSource contains the (URL, PublicKey) that together identify + * where the Java bytecodes come from and who (if anyone) signed + * them. The URL could refer to localhost. The URL could also be + * null, meaning that this policy entry is given to all comers, as + * long as they match the signer field. The signer could be null, + * meaning the code is not signed.

    + * + * The Permission contains the (Type, Name, Action) triplet.

    + * + * For now, the Policy object retrieves the public key from the + * X.509 certificate on disk that corresponds to the signedBy + * alias specified in the Policy config file. For reasons of + * efficiency, the Policy object keeps a hashtable of certs already + * read in. This could be replaced by a secure internal key + * store. + * + *

    + * For example, the entry + *

    +     *          permission java.io.File "/tmp", "read,write",
    +     *          signedBy "Duke";
    +     * 
    + * is represented internally + *
    +     *
    +     * FilePermission f = new FilePermission("/tmp", "read,write");
    +     * PublicKey p = publickeys.get("Duke");
    +     * URL u = InetAddress.getLocalHost();
    +     * CodeBase c = new CodeBase( p, u );
    +     * pe = new PolicyEntry(f, c);
    +     * 
    + * + * @author Marianne Mueller + * @author Roland Schemers + * @see CodeSource + * @see java.security.Policy + * @see Permissions + * @see java.security.ProtectionDomain + */ + private static class PolicyEntry { + + CodeSource codesource; + Vector permissions; + + /** + * Given a Permission and a CodeSource, create a policy entry. + * + * XXX Decide if/how to add validity fields and "purpose" fields to + * XXX policy entries + * + * @param cs the CodeSource, which encapsulates the URL and the public + * key attributes from the policy config file. Validity checks + * are performed on the public key before PolicyEntry is called. + * + */ + PolicyEntry(CodeSource cs) { + this.codesource = cs; + this.permissions = new Vector(); + } + + /** + * add a Permission object to this entry. + */ + void add(Permission p) { + permissions.addElement(p); + } + + /** + * Return the CodeSource for this policy entry + */ + CodeSource getCodeSource() { + return this.codesource; + } + + @Override + public String toString(){ + StringBuffer sb = new StringBuffer(); + sb.append(rb.getString("LPARAM")); + sb.append(getCodeSource()); + sb.append("\n"); + for (int j = 0; j < permissions.size(); j++) { + Permission p = permissions.elementAt(j); + sb.append(rb.getString("SPACE")); + sb.append(rb.getString("SPACE")); + sb.append(p); + sb.append(rb.getString("NEWLINE")); + } + sb.append(rb.getString("RPARAM")); + sb.append(rb.getString("NEWLINE")); + return sb.toString(); + } + + } +} + +@SuppressWarnings("deprecation") +class PolicyPermissions extends PermissionCollection { + + private static final long serialVersionUID = -1954188373270545523L; + + private CodeSource codesource; + private Permissions perms; + private AuthPolicyFile policy; + private boolean notInit; // have we pulled in the policy permissions yet? + private Vector additionalPerms; + + PolicyPermissions(AuthPolicyFile policy, + CodeSource codesource) + { + this.codesource = codesource; + this.policy = policy; + this.perms = null; + this.notInit = true; + this.additionalPerms = null; + } + + @Override + public void add(Permission permission) { + if (isReadOnly()) + throw new SecurityException + (AuthPolicyFile.rb.getString + ("attempt.to.add.a.Permission.to.a.readonly.PermissionCollection")); + + if (perms == null) { + if (additionalPerms == null) { + additionalPerms = new Vector(); + } + additionalPerms.add(permission); + } else { + perms.add(permission); + } + } + + private synchronized void init() { + if (notInit) { + if (perms == null) { + perms = new Permissions(); + } + if (additionalPerms != null) { + Enumeration e = additionalPerms.elements(); + while (e.hasMoreElements()) { + perms.add(e.nextElement()); + } + additionalPerms = null; + } + policy.getPermissions(perms, codesource); + notInit = false; + } + } + + @Override + public boolean implies(Permission permission) { + if (notInit) { + init(); + } + return perms.implies(permission); + } + + @Override + public Enumeration elements() { + if (notInit) { + init(); + } + return perms.elements(); + } + + @Override + public String toString() { + if (notInit) { + init(); + } + return perms.toString(); + } +} diff --git a/src/sun/security/provider/ByteArrayAccess.java b/src/sun/security/provider/ByteArrayAccess.java new file mode 100644 index 00000000..e06832bf --- /dev/null +++ b/src/sun/security/provider/ByteArrayAccess.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import static java.lang.Integer.reverseBytes; +import static java.lang.Long.reverseBytes; + +import java.nio.ByteOrder; + +import sun.misc.Unsafe; + +/** + * Optimized methods for converting between byte[] and int[]/long[], both for + * big endian and little endian byte orders. + * + * Currently, it includes a default code path plus two optimized code paths. + * One is for little endian architectures that support full speed int/long + * access at unaligned addresses (i.e. x86/amd64). The second is for big endian + * architectures (that only support correctly aligned access), such as SPARC. + * These are the only platforms we currently support, but other optimized + * variants could be added as needed. + * + * NOTE that ArrayIndexOutOfBoundsException will be thrown if the bounds checks + * failed. + * + * This class may also be helpful in improving the performance of the + * crypto code in the SunJCE provider. However, for now it is only accessible by + * the message digest implementation in the SUN provider. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ByteArrayAccess { + + private ByteArrayAccess() { + // empty + } + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + // whether to use the optimized path for little endian platforms that + // support full speed unaligned memory access. + private static final boolean littleEndianUnaligned; + + // whether to use the optimzied path for big endian platforms that + // support only correctly aligned full speed memory access. + // (Note that on SPARC unaligned memory access is possible, but it is + // implemented using a software trap and therefore very slow) + private static final boolean bigEndian; + + private final static int byteArrayOfs = unsafe.arrayBaseOffset(byte[].class); + + static { + boolean scaleOK = ((unsafe.arrayIndexScale(byte[].class) == 1) + && (unsafe.arrayIndexScale(int[].class) == 4) + && (unsafe.arrayIndexScale(long[].class) == 8) + && ((byteArrayOfs & 3) == 0)); + + ByteOrder byteOrder = ByteOrder.nativeOrder(); + littleEndianUnaligned = + scaleOK && unaligned() && (byteOrder == ByteOrder.LITTLE_ENDIAN); + bigEndian = + scaleOK && (byteOrder == ByteOrder.BIG_ENDIAN); + } + + // Return whether this platform supports full speed int/long memory access + // at unaligned addresses. + // This code was copied from java.nio.Bits because there is no equivalent + // public API. + private static boolean unaligned() { + String arch = java.security.AccessController.doPrivileged + (new sun.security.action.GetPropertyAction("os.arch", "")); + return arch.equals("i386") || arch.equals("x86") || arch.equals("amd64") + || arch.equals("x86_64"); + } + + /** + * byte[] to int[] conversion, little endian byte order. + */ + static void b2iLittle(byte[] in, int inOfs, int[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len) || + (outOfs < 0) || ((out.length - outOfs) < len/4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = unsafe.getInt(in, (long)inOfs); + inOfs += 4; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs)); + inOfs += 4; + } + } else { + len += inOfs; + while (inOfs < len) { + out[outOfs++] = ((in[inOfs ] & 0xff) ) + | ((in[inOfs + 1] & 0xff) << 8) + | ((in[inOfs + 2] & 0xff) << 16) + | ((in[inOfs + 3] ) << 24); + inOfs += 4; + } + } + } + + // Special optimization of b2iLittle(in, inOfs, out, 0, 64) + static void b2iLittle64(byte[] in, int inOfs, int[] out) { + if ((inOfs < 0) || ((in.length - inOfs) < 64) || + (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[ 0] = unsafe.getInt(in, (long)(inOfs )); + out[ 1] = unsafe.getInt(in, (long)(inOfs + 4)); + out[ 2] = unsafe.getInt(in, (long)(inOfs + 8)); + out[ 3] = unsafe.getInt(in, (long)(inOfs + 12)); + out[ 4] = unsafe.getInt(in, (long)(inOfs + 16)); + out[ 5] = unsafe.getInt(in, (long)(inOfs + 20)); + out[ 6] = unsafe.getInt(in, (long)(inOfs + 24)); + out[ 7] = unsafe.getInt(in, (long)(inOfs + 28)); + out[ 8] = unsafe.getInt(in, (long)(inOfs + 32)); + out[ 9] = unsafe.getInt(in, (long)(inOfs + 36)); + out[10] = unsafe.getInt(in, (long)(inOfs + 40)); + out[11] = unsafe.getInt(in, (long)(inOfs + 44)); + out[12] = unsafe.getInt(in, (long)(inOfs + 48)); + out[13] = unsafe.getInt(in, (long)(inOfs + 52)); + out[14] = unsafe.getInt(in, (long)(inOfs + 56)); + out[15] = unsafe.getInt(in, (long)(inOfs + 60)); + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs ))); + out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 4))); + out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 8))); + out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12))); + out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16))); + out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20))); + out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24))); + out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28))); + out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32))); + out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36))); + out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40))); + out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44))); + out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48))); + out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52))); + out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56))); + out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60))); + } else { + b2iLittle(in, inOfs, out, 0, 64); + } + } + + /** + * int[] to byte[] conversion, little endian byte order. + */ + static void i2bLittle(int[] in, int inOfs, byte[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len/4) || + (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long)outOfs, in[inOfs++]); + outOfs += 4; + } + } else if (bigEndian && ((outOfs & 3) == 0)) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++])); + outOfs += 4; + } + } else { + len += outOfs; + while (outOfs < len) { + int i = in[inOfs++]; + out[outOfs++] = (byte)(i ); + out[outOfs++] = (byte)(i >> 8); + out[outOfs++] = (byte)(i >> 16); + out[outOfs++] = (byte)(i >> 24); + } + } + } + + // Store one 32-bit value into out[outOfs..outOfs+3] in little endian order. + static void i2bLittle4(int val, byte[] out, int outOfs) { + if ((outOfs < 0) || ((out.length - outOfs) < 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val); + } else if (bigEndian && ((outOfs & 3) == 0)) { + unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val)); + } else { + out[outOfs ] = (byte)(val ); + out[outOfs + 1] = (byte)(val >> 8); + out[outOfs + 2] = (byte)(val >> 16); + out[outOfs + 3] = (byte)(val >> 24); + } + } + + /** + * byte[] to int[] conversion, big endian byte order. + */ + static void b2iBig(byte[] in, int inOfs, int[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len) || + (outOfs < 0) || ((out.length - outOfs) < len/4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getInt(in, (long)inOfs)); + inOfs += 4; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = unsafe.getInt(in, (long)inOfs); + inOfs += 4; + } + } else { + len += inOfs; + while (inOfs < len) { + out[outOfs++] = ((in[inOfs + 3] & 0xff) ) + | ((in[inOfs + 2] & 0xff) << 8) + | ((in[inOfs + 1] & 0xff) << 16) + | ((in[inOfs ] ) << 24); + inOfs += 4; + } + } + } + + // Special optimization of b2iBig(in, inOfs, out, 0, 64) + static void b2iBig64(byte[] in, int inOfs, int[] out) { + if ((inOfs < 0) || ((in.length - inOfs) < 64) || + (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[ 0] = reverseBytes(unsafe.getInt(in, (long)(inOfs ))); + out[ 1] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 4))); + out[ 2] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 8))); + out[ 3] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 12))); + out[ 4] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 16))); + out[ 5] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 20))); + out[ 6] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 24))); + out[ 7] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 28))); + out[ 8] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 32))); + out[ 9] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 36))); + out[10] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 40))); + out[11] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 44))); + out[12] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 48))); + out[13] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 52))); + out[14] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 56))); + out[15] = reverseBytes(unsafe.getInt(in, (long)(inOfs + 60))); + } else if (bigEndian && ((inOfs & 3) == 0)) { + inOfs += byteArrayOfs; + out[ 0] = unsafe.getInt(in, (long)(inOfs )); + out[ 1] = unsafe.getInt(in, (long)(inOfs + 4)); + out[ 2] = unsafe.getInt(in, (long)(inOfs + 8)); + out[ 3] = unsafe.getInt(in, (long)(inOfs + 12)); + out[ 4] = unsafe.getInt(in, (long)(inOfs + 16)); + out[ 5] = unsafe.getInt(in, (long)(inOfs + 20)); + out[ 6] = unsafe.getInt(in, (long)(inOfs + 24)); + out[ 7] = unsafe.getInt(in, (long)(inOfs + 28)); + out[ 8] = unsafe.getInt(in, (long)(inOfs + 32)); + out[ 9] = unsafe.getInt(in, (long)(inOfs + 36)); + out[10] = unsafe.getInt(in, (long)(inOfs + 40)); + out[11] = unsafe.getInt(in, (long)(inOfs + 44)); + out[12] = unsafe.getInt(in, (long)(inOfs + 48)); + out[13] = unsafe.getInt(in, (long)(inOfs + 52)); + out[14] = unsafe.getInt(in, (long)(inOfs + 56)); + out[15] = unsafe.getInt(in, (long)(inOfs + 60)); + } else { + b2iBig(in, inOfs, out, 0, 64); + } + } + + /** + * int[] to byte[] conversion, big endian byte order. + */ + static void i2bBig(int[] in, int inOfs, byte[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len/4) || + (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long)outOfs, reverseBytes(in[inOfs++])); + outOfs += 4; + } + } else if (bigEndian && ((outOfs & 3) == 0)) { + outOfs += byteArrayOfs; + len += outOfs; + while (outOfs < len) { + unsafe.putInt(out, (long)outOfs, in[inOfs++]); + outOfs += 4; + } + } else { + len += outOfs; + while (outOfs < len) { + int i = in[inOfs++]; + out[outOfs++] = (byte)(i >> 24); + out[outOfs++] = (byte)(i >> 16); + out[outOfs++] = (byte)(i >> 8); + out[outOfs++] = (byte)(i ); + } + } + } + + // Store one 32-bit value into out[outOfs..outOfs+3] in big endian order. + static void i2bBig4(int val, byte[] out, int outOfs) { + if ((outOfs < 0) || ((out.length - outOfs) < 4)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + unsafe.putInt(out, (long)(byteArrayOfs + outOfs), reverseBytes(val)); + } else if (bigEndian && ((outOfs & 3) == 0)) { + unsafe.putInt(out, (long)(byteArrayOfs + outOfs), val); + } else { + out[outOfs ] = (byte)(val >> 24); + out[outOfs + 1] = (byte)(val >> 16); + out[outOfs + 2] = (byte)(val >> 8); + out[outOfs + 3] = (byte)(val ); + } + } + + /** + * byte[] to long[] conversion, big endian byte order. + */ + static void b2lBig(byte[] in, int inOfs, long[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len) || + (outOfs < 0) || ((out.length - outOfs) < len/8)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = reverseBytes(unsafe.getLong(in, (long)inOfs)); + inOfs += 8; + } + } else if (bigEndian && ((inOfs & 3) == 0)) { + // In the current HotSpot memory layout, the first element of a + // byte[] is only 32-bit aligned, not 64-bit. + // That means we could use getLong() only for offset 4, 12, etc., + // which would rarely occur in practice. Instead, we use an + // optimization that uses getInt() so that it works for offset 0. + inOfs += byteArrayOfs; + len += inOfs; + while (inOfs < len) { + out[outOfs++] = + ((long)unsafe.getInt(in, (long)inOfs) << 32) + | (unsafe.getInt(in, (long)(inOfs + 4)) & 0xffffffffL); + inOfs += 8; + } + } else { + len += inOfs; + while (inOfs < len) { + int i1 = ((in[inOfs + 3] & 0xff) ) + | ((in[inOfs + 2] & 0xff) << 8) + | ((in[inOfs + 1] & 0xff) << 16) + | ((in[inOfs ] ) << 24); + inOfs += 4; + int i2 = ((in[inOfs + 3] & 0xff) ) + | ((in[inOfs + 2] & 0xff) << 8) + | ((in[inOfs + 1] & 0xff) << 16) + | ((in[inOfs ] ) << 24); + out[outOfs++] = ((long)i1 << 32) | (i2 & 0xffffffffL); + inOfs += 4; + } + } + } + + // Special optimization of b2lBig(in, inOfs, out, 0, 128) + static void b2lBig128(byte[] in, int inOfs, long[] out) { + if ((inOfs < 0) || ((in.length - inOfs) < 128) || + (out.length < 16)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (littleEndianUnaligned) { + inOfs += byteArrayOfs; + out[ 0] = reverseBytes(unsafe.getLong(in, (long)(inOfs ))); + out[ 1] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 8))); + out[ 2] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 16))); + out[ 3] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 24))); + out[ 4] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 32))); + out[ 5] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 40))); + out[ 6] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 48))); + out[ 7] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 56))); + out[ 8] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 64))); + out[ 9] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 72))); + out[10] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 80))); + out[11] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 88))); + out[12] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 96))); + out[13] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 104))); + out[14] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 112))); + out[15] = reverseBytes(unsafe.getLong(in, (long)(inOfs + 120))); + } else { + // no optimization for big endian, see comments in b2lBig + b2lBig(in, inOfs, out, 0, 128); + } + } + + /** + * long[] to byte[] conversion, big endian byte order. + */ + static void l2bBig(long[] in, int inOfs, byte[] out, int outOfs, int len) { + if ((inOfs < 0) || ((in.length - inOfs) < len/8) || + (outOfs < 0) || ((out.length - outOfs) < len)) { + throw new ArrayIndexOutOfBoundsException(); + } + len += outOfs; + while (outOfs < len) { + long i = in[inOfs++]; + out[outOfs++] = (byte)(i >> 56); + out[outOfs++] = (byte)(i >> 48); + out[outOfs++] = (byte)(i >> 40); + out[outOfs++] = (byte)(i >> 32); + out[outOfs++] = (byte)(i >> 24); + out[outOfs++] = (byte)(i >> 16); + out[outOfs++] = (byte)(i >> 8); + out[outOfs++] = (byte)(i ); + } + } +} diff --git a/src/sun/security/provider/ConfigFile.java b/src/sun/security/provider/ConfigFile.java new file mode 100644 index 00000000..91ae9a3d --- /dev/null +++ b/src/sun/security/provider/ConfigFile.java @@ -0,0 +1,669 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.Security; +import java.security.URIParameter; +import java.text.MessageFormat; +import java.util.*; +import javax.security.auth.AuthPermission; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.ConfigurationSpi; +import sun.security.util.Debug; +import sun.security.util.PropertyExpander; +import sun.security.util.ResourcesMgr; + +/** + * This class represents a default implementation for + * {@code javax.security.auth.login.Configuration}. + * + *

    This object stores the runtime login configuration representation, + * and is the amalgamation of multiple static login configurations that + * resides in files. The algorithm for locating the login configuration + * file(s) and reading their information into this {@code Configuration} + * object is: + * + *

      + *
    1. + * Loop through the security properties, + * login.config.url.1, login.config.url.2, ..., + * login.config.url.X. + * Each property value specifies a {@code URL} pointing to a + * login configuration file to be loaded. Read in and load + * each configuration. + * + *
    2. + * The {@code java.lang.System} property + * java.security.auth.login.config + * may also be set to a {@code URL} pointing to another + * login configuration file + * (which is the case when a user uses the -D switch at runtime). + * If this property is defined, and its use is allowed by the + * security property file (the Security property, + * policy.allowSystemProperty is set to true), + * also load that login configuration. + * + *
    3. + * If the java.security.auth.login.config property is defined using + * "==" (rather than "="), then ignore all other specified + * login configurations and only load this configuration. + * + *
    4. + * If no system or security properties were set, try to read from the file, + * ${user.home}/.java.login.config, where ${user.home} is the value + * represented by the "user.home" System property. + *
    + * + *

    The configuration syntax supported by this implementation + * is exactly that syntax specified in the + * {@code javax.security.auth.login.Configuration} class. + * + * @see javax.security.auth.login.LoginContext + * @see Security security properties + */ +public final class ConfigFile extends Configuration { + + private final Spi spi; + + public ConfigFile() { + spi = new Spi(); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String appName) { + return spi.engineGetAppConfigurationEntry(appName); + } + + @Override + public synchronized void refresh() { + spi.engineRefresh(); + } + + public final static class Spi extends ConfigurationSpi { + + private URL url; + private boolean expandProp = true; + private Map> configuration; + private int linenum; + private StreamTokenizer st; + private int lookahead; + + private static Debug debugConfig = Debug.getInstance("configfile"); + private static Debug debugParser = Debug.getInstance("configparser"); + + /** + * Creates a new {@code ConfigurationSpi} object. + * + * @throws SecurityException if the {@code ConfigurationSpi} can not be + * initialized + */ + public Spi() { + try { + init(); + } catch (IOException ioe) { + throw new SecurityException(ioe); + } + } + + /** + * Creates a new {@code ConfigurationSpi} object from the specified + * {@code URI}. + * + * @param uri the {@code URI} + * @throws SecurityException if the {@code ConfigurationSpi} can not be + * initialized + * @throws NullPointerException if {@code uri} is null + */ + public Spi(URI uri) { + // only load config from the specified URI + try { + url = uri.toURL(); + init(); + } catch (IOException ioe) { + throw new SecurityException(ioe); + } + } + + public Spi(final Parameters params) throws IOException { + + // call in a doPrivileged + // + // we have already passed the Configuration.getInstance + // security check. also this class is not freely accessible + // (it is in the "sun" package). + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Void run() throws IOException { + if (params == null) { + init(); + } else { + if (!(params instanceof URIParameter)) { + throw new IllegalArgumentException + ("Unrecognized parameter: " + params); + } + URIParameter uriParam = (URIParameter)params; + url = uriParam.getURI().toURL(); + init(); + } + return null; + } + }); + } catch (PrivilegedActionException pae) { + throw (IOException)pae.getException(); + } + + // if init() throws some other RuntimeException, + // let it percolate up naturally. + } + + /** + * Read and initialize the entire login Configuration from the + * configured URL. + * + * @throws IOException if the Configuration can not be initialized + * @throws SecurityException if the caller does not have permission + * to initialize the Configuration + */ + private void init() throws IOException { + + boolean initialized = false; + + // For policy.expandProperties, check if either a security or system + // property is set to false (old code erroneously checked the system + // prop so we must check both to preserve compatibility). + String expand = Security.getProperty("policy.expandProperties"); + if (expand == null) { + expand = System.getProperty("policy.expandProperties"); + } + if ("false".equals(expand)) { + expandProp = false; + } + + // new configuration + Map> newConfig = new HashMap<>(); + + if (url != null) { + /** + * If the caller specified a URI via Configuration.getInstance, + * we only read from that URI + */ + if (debugConfig != null) { + debugConfig.println("reading " + url); + } + init(url, newConfig); + configuration = newConfig; + return; + } + + /** + * Caller did not specify URI via Configuration.getInstance. + * Read from URLs listed in the java.security properties file. + */ + String allowSys = Security.getProperty("policy.allowSystemProperty"); + + if ("true".equalsIgnoreCase(allowSys)) { + String extra_config = System.getProperty + ("java.security.auth.login.config"); + if (extra_config != null) { + boolean overrideAll = false; + if (extra_config.startsWith("=")) { + overrideAll = true; + extra_config = extra_config.substring(1); + } + try { + extra_config = PropertyExpander.expand(extra_config); + } catch (PropertyExpander.ExpandException peee) { + throw ioException("Unable.to.properly.expand.config", + extra_config); + } + + URL configURL = null; + try { + configURL = new URL(extra_config); + } catch (MalformedURLException mue) { + File configFile = new File(extra_config); + if (configFile.exists()) { + configURL = configFile.toURI().toURL(); + } else { + throw ioException( + "extra.config.No.such.file.or.directory.", + extra_config); + } + } + + if (debugConfig != null) { + debugConfig.println("reading "+configURL); + } + init(configURL, newConfig); + initialized = true; + if (overrideAll) { + if (debugConfig != null) { + debugConfig.println("overriding other policies!"); + } + configuration = newConfig; + return; + } + } + } + + int n = 1; + String config_url; + while ((config_url = Security.getProperty + ("login.config.url."+n)) != null) { + try { + config_url = PropertyExpander.expand + (config_url).replace(File.separatorChar, '/'); + if (debugConfig != null) { + debugConfig.println("\tReading config: " + config_url); + } + init(new URL(config_url), newConfig); + initialized = true; + } catch (PropertyExpander.ExpandException peee) { + throw ioException("Unable.to.properly.expand.config", + config_url); + } + n++; + } + + if (initialized == false && n == 1 && config_url == null) { + + // get the config from the user's home directory + if (debugConfig != null) { + debugConfig.println("\tReading Policy " + + "from ~/.java.login.config"); + } + config_url = System.getProperty("user.home"); + String userConfigFile = config_url + File.separatorChar + + ".java.login.config"; + + // No longer throws an exception when there's no config file + // at all. Returns an empty Configuration instead. + if (new File(userConfigFile).exists()) { + init(new File(userConfigFile).toURI().toURL(), newConfig); + } + } + + configuration = newConfig; + } + + private void init(URL config, + Map> newConfig) + throws IOException { + + try (InputStreamReader isr + = new InputStreamReader(getInputStream(config), "UTF-8")) { + readConfig(isr, newConfig); + } catch (FileNotFoundException fnfe) { + if (debugConfig != null) { + debugConfig.println(fnfe.toString()); + } + throw new IOException(ResourcesMgr.getString + ("Configuration.Error.No.such.file.or.directory", + "sun.security.util.AuthResources")); + } + } + + /** + * Retrieve an entry from the Configuration using an application name + * as an index. + * + * @param applicationName the name used to index the Configuration. + * @return an array of AppConfigurationEntries which correspond to + * the stacked configuration of LoginModules for this + * application, or null if this application has no configured + * LoginModules. + */ + @Override + public AppConfigurationEntry[] engineGetAppConfigurationEntry + (String applicationName) { + + List list = null; + synchronized (configuration) { + list = configuration.get(applicationName); + } + + if (list == null || list.size() == 0) { + return null; + } + + AppConfigurationEntry[] entries = + new AppConfigurationEntry[list.size()]; + Iterator iterator = list.iterator(); + for (int i = 0; iterator.hasNext(); i++) { + AppConfigurationEntry e = iterator.next(); + entries[i] = new AppConfigurationEntry(e.getLoginModuleName(), + e.getControlFlag(), + e.getOptions()); + } + return entries; + } + + /** + * Refresh and reload the Configuration by re-reading all of the + * login configurations. + * + * @throws SecurityException if the caller does not have permission + * to refresh the Configuration. + */ + @Override + public synchronized void engineRefresh() { + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission( + new AuthPermission("refreshLoginConfiguration")); + } + + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + try { + init(); + } catch (IOException ioe) { + throw new SecurityException(ioe.getLocalizedMessage(), + ioe); + } + return null; + } + }); + } + + private void readConfig(Reader reader, + Map> newConfig) + throws IOException { + + linenum = 1; + + if (!(reader instanceof BufferedReader)) { + reader = new BufferedReader(reader); + } + + st = new StreamTokenizer(reader); + st.quoteChar('"'); + st.wordChars('$', '$'); + st.wordChars('_', '_'); + st.wordChars('-', '-'); + st.wordChars('*', '*'); + st.lowerCaseMode(false); + st.slashSlashComments(true); + st.slashStarComments(true); + st.eolIsSignificant(true); + + lookahead = nextToken(); + while (lookahead != StreamTokenizer.TT_EOF) { + parseLoginEntry(newConfig); + } + } + + private void parseLoginEntry( + Map> newConfig) + throws IOException { + + List configEntries = new LinkedList<>(); + + // application name + String appName = st.sval; + lookahead = nextToken(); + + if (debugParser != null) { + debugParser.println("\tReading next config entry: " + appName); + } + + match("{"); + + // get the modules + while (peek("}") == false) { + // get the module class name + String moduleClass = match("module class name"); + + // controlFlag (required, optional, etc) + LoginModuleControlFlag controlFlag; + String sflag = match("controlFlag").toUpperCase(); + switch (sflag) { + case "REQUIRED": + controlFlag = LoginModuleControlFlag.REQUIRED; + break; + case "REQUISITE": + controlFlag = LoginModuleControlFlag.REQUISITE; + break; + case "SUFFICIENT": + controlFlag = LoginModuleControlFlag.SUFFICIENT; + break; + case "OPTIONAL": + controlFlag = LoginModuleControlFlag.OPTIONAL; + break; + default: + throw ioException( + "Configuration.Error.Invalid.control.flag.flag", + sflag); + } + + // get the args + Map options = new HashMap<>(); + while (peek(";") == false) { + String key = match("option key"); + match("="); + try { + options.put(key, expand(match("option value"))); + } catch (PropertyExpander.ExpandException peee) { + throw new IOException(peee.getLocalizedMessage()); + } + } + + lookahead = nextToken(); + + // create the new element + if (debugParser != null) { + debugParser.println("\t\t" + moduleClass + ", " + sflag); + for (String key : options.keySet()) { + debugParser.println("\t\t\t" + key + + "=" + options.get(key)); + } + } + configEntries.add(new AppConfigurationEntry(moduleClass, + controlFlag, + options)); + } + + match("}"); + match(";"); + + // add this configuration entry + if (newConfig.containsKey(appName)) { + throw ioException( + "Configuration.Error.Can.not.specify.multiple.entries.for.appName", + appName); + } + newConfig.put(appName, configEntries); + } + + private String match(String expect) throws IOException { + + String value = null; + + switch(lookahead) { + case StreamTokenizer.TT_EOF: + throw ioException( + "Configuration.Error.expected.expect.read.end.of.file.", + expect); + + case '"': + case StreamTokenizer.TT_WORD: + if (expect.equalsIgnoreCase("module class name") || + expect.equalsIgnoreCase("controlFlag") || + expect.equalsIgnoreCase("option key") || + expect.equalsIgnoreCase("option value")) { + value = st.sval; + lookahead = nextToken(); + } else { + throw ioException( + "Configuration.Error.Line.line.expected.expect.found.value.", + new Integer(linenum), expect, st.sval); + } + break; + + case '{': + if (expect.equalsIgnoreCase("{")) { + lookahead = nextToken(); + } else { + throw ioException( + "Configuration.Error.Line.line.expected.expect.", + new Integer(linenum), expect, st.sval); + } + break; + + case ';': + if (expect.equalsIgnoreCase(";")) { + lookahead = nextToken(); + } else { + throw ioException( + "Configuration.Error.Line.line.expected.expect.", + new Integer(linenum), expect, st.sval); + } + break; + + case '}': + if (expect.equalsIgnoreCase("}")) { + lookahead = nextToken(); + } else { + throw ioException( + "Configuration.Error.Line.line.expected.expect.", + new Integer(linenum), expect, st.sval); + } + break; + + case '=': + if (expect.equalsIgnoreCase("=")) { + lookahead = nextToken(); + } else { + throw ioException( + "Configuration.Error.Line.line.expected.expect.", + new Integer(linenum), expect, st.sval); + } + break; + + default: + throw ioException( + "Configuration.Error.Line.line.expected.expect.found.value.", + new Integer(linenum), expect, st.sval); + } + return value; + } + + private boolean peek(String expect) { + switch (lookahead) { + case ',': + return expect.equalsIgnoreCase(","); + case ';': + return expect.equalsIgnoreCase(";"); + case '{': + return expect.equalsIgnoreCase("{"); + case '}': + return expect.equalsIgnoreCase("}"); + default: + return false; + } + } + + private int nextToken() throws IOException { + int tok; + while ((tok = st.nextToken()) == StreamTokenizer.TT_EOL) { + linenum++; + } + return tok; + } + + private InputStream getInputStream(URL url) throws IOException { + if ("file".equalsIgnoreCase(url.getProtocol())) { + // Compatibility notes: + // + // Code changed from + // String path = url.getFile().replace('/', File.separatorChar); + // return new FileInputStream(path); + // + // The original implementation would search for "/tmp/a%20b" + // when url is "file:///tmp/a%20b". This is incorrect. The + // current codes fix this bug and searches for "/tmp/a b". + // For compatibility reasons, when the file "/tmp/a b" does + // not exist, the file named "/tmp/a%20b" will be tried. + // + // This also means that if both file exists, the behavior of + // this method is changed, and the current codes choose the + // correct one. + try { + return url.openStream(); + } catch (Exception e) { + String file = url.getPath(); + if (url.getHost().length() > 0) { // For Windows UNC + file = "//" + url.getHost() + file; + } + if (debugConfig != null) { + debugConfig.println("cannot read " + url + + ", try " + file); + } + return new FileInputStream(file); + } + } else { + return url.openStream(); + } + } + + private String expand(String value) + throws PropertyExpander.ExpandException, IOException { + + if (value.isEmpty()) { + return value; + } + + if (!expandProp) { + return value; + } + String s = PropertyExpander.expand(value); + if (s == null || s.length() == 0) { + throw ioException( + "Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + new Integer(linenum), value); + } + return s; + } + + private IOException ioException(String resourceKey, Object... args) { + MessageFormat form = new MessageFormat(ResourcesMgr.getString + (resourceKey, "sun.security.util.AuthResources")); + return new IOException(form.format(args)); + } + } +} diff --git a/src/sun/security/provider/DSA.java b/src/sun/security/provider/DSA.java new file mode 100644 index 00000000..45e37713 --- /dev/null +++ b/src/sun/security/provider/DSA.java @@ -0,0 +1,738 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.util.*; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.SecureRandom; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.util.Debug; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.jca.JCAUtil; + +/** + * The Digital Signature Standard (using the Digital Signature + * Algorithm), as described in fips186-3 of the National Instute of + * Standards and Technology (NIST), using SHA digest algorithms + * from FIPS180-3. + * + * This file contains both the signature implementation for the + * commonly used SHA1withDSA (DSS), SHA224withDSA, SHA256withDSA, + * as well as RawDSA, used by TLS among others. RawDSA expects + * the 20 byte SHA-1 digest as input via update rather than the + * original data like other signature implementations. + * + * @author Benjamin Renaud + * + * @since 1.1 + * + * @see DSAPublicKey + * @see DSAPrivateKey + */ +abstract class DSA extends SignatureSpi { + + /* Are we debugging? */ + private static final boolean debug = false; + + /* The parameter object */ + private DSAParams params; + + /* algorithm parameters */ + private BigInteger presetP, presetQ, presetG; + + /* The public key, if any */ + private BigInteger presetY; + + /* The private key, if any */ + private BigInteger presetX; + + /* The RNG used to output a seed for generating k */ + private SecureRandom signingRandom; + + /* The message digest object used */ + private final MessageDigest md; + + /** + * Construct a blank DSA object. It must be + * initialized before being usable for signing or verifying. + */ + DSA(MessageDigest md) { + super(); + this.md = md; + } + + /** + * Initialize the DSA object with a DSA private key. + * + * @param privateKey the DSA private key + * + * @exception InvalidKeyException if the key is not a valid DSA private + * key. + */ + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + if (!(privateKey instanceof java.security.interfaces.DSAPrivateKey)) { + throw new InvalidKeyException("not a DSA private key: " + + privateKey); + } + + java.security.interfaces.DSAPrivateKey priv = + (java.security.interfaces.DSAPrivateKey)privateKey; + + // check for algorithm specific constraints before doing initialization + DSAParams params = priv.getParams(); + if (params == null) { + throw new InvalidKeyException("DSA private key lacks parameters"); + } + + this.params = params; + this.presetX = priv.getX(); + this.presetY = null; + this.presetP = params.getP(); + this.presetQ = params.getQ(); + this.presetG = params.getG(); + this.md.reset(); + } + /** + * Initialize the DSA object with a DSA public key. + * + * @param publicKey the DSA public key. + * + * @exception InvalidKeyException if the key is not a valid DSA public + * key. + */ + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + if (!(publicKey instanceof java.security.interfaces.DSAPublicKey)) { + throw new InvalidKeyException("not a DSA public key: " + + publicKey); + } + java.security.interfaces.DSAPublicKey pub = + (java.security.interfaces.DSAPublicKey)publicKey; + + // check for algorithm specific constraints before doing initialization + DSAParams params = pub.getParams(); + if (params == null) { + throw new InvalidKeyException("DSA public key lacks parameters"); + } + + this.params = params; + this.presetY = pub.getY(); + this.presetX = null; + this.presetP = params.getP(); + this.presetQ = params.getQ(); + this.presetG = params.getG(); + this.md.reset(); + } + + /** + * Update a byte to be signed or verified. + */ + protected void engineUpdate(byte b) { + md.update(b); + } + + /** + * Update an array of bytes to be signed or verified. + */ + protected void engineUpdate(byte[] data, int off, int len) { + md.update(data, off, len); + } + + protected void engineUpdate(ByteBuffer b) { + md.update(b); + } + + + /** + * Sign all the data thus far updated. The signature is formatted + * according to the Canonical Encoding Rules, returned as a DER + * sequence of Integer, r and s. + * + * @return a signature block formatted according to the Canonical + * Encoding Rules. + * + * @exception SignatureException if the signature object was not + * properly initialized, or if another exception occurs. + * + * @see sun.security.DSA#engineUpdate + * @see sun.security.DSA#engineVerify + */ + protected byte[] engineSign() throws SignatureException { + BigInteger k = generateK(presetQ); + BigInteger r = generateR(presetP, presetQ, presetG, k); + BigInteger s = generateS(presetX, presetQ, r, k); + + try { + DerOutputStream outseq = new DerOutputStream(100); + outseq.putInteger(r); + outseq.putInteger(s); + DerValue result = new DerValue(DerValue.tag_Sequence, + outseq.toByteArray()); + + return result.toByteArray(); + + } catch (IOException e) { + throw new SignatureException("error encoding signature"); + } + } + + /** + * Verify all the data thus far updated. + * + * @param signature the alledged signature, encoded using the + * Canonical Encoding Rules, as a sequence of integers, r and s. + * + * @exception SignatureException if the signature object was not + * properly initialized, or if another exception occurs. + * + * @see sun.security.DSA#engineUpdate + * @see sun.security.DSA#engineSign + */ + protected boolean engineVerify(byte[] signature) + throws SignatureException { + return engineVerify(signature, 0, signature.length); + } + + /** + * Verify all the data thus far updated. + * + * @param signature the alledged signature, encoded using the + * Canonical Encoding Rules, as a sequence of integers, r and s. + * + * @param offset the offset to start from in the array of bytes. + * + * @param length the number of bytes to use, starting at offset. + * + * @exception SignatureException if the signature object was not + * properly initialized, or if another exception occurs. + * + * @see sun.security.DSA#engineUpdate + * @see sun.security.DSA#engineSign + */ + protected boolean engineVerify(byte[] signature, int offset, int length) + throws SignatureException { + + BigInteger r = null; + BigInteger s = null; + // first decode the signature. + try { + DerInputStream in = new DerInputStream(signature, offset, length); + DerValue[] values = in.getSequence(2); + + r = values[0].getBigInteger(); + s = values[1].getBigInteger(); + + } catch (IOException e) { + throw new SignatureException("invalid encoding for signature"); + } + + // some implementations do not correctly encode values in the ASN.1 + // 2's complement format. force r and s to be positive in order to + // to validate those signatures + if (r.signum() < 0) { + r = new BigInteger(1, r.toByteArray()); + } + if (s.signum() < 0) { + s = new BigInteger(1, s.toByteArray()); + } + + if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) { + BigInteger w = generateW(presetP, presetQ, presetG, s); + BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r); + return v.equals(r); + } else { + throw new SignatureException("invalid signature: out of range values"); + } + } + + @Deprecated + protected void engineSetParameter(String key, Object param) { + throw new InvalidParameterException("No parameter accepted"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "No parameter accepted"); + } + } + + @Deprecated + protected Object engineGetParameter(String key) { + return null; + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + private BigInteger generateR(BigInteger p, BigInteger q, BigInteger g, + BigInteger k) { + BigInteger temp = g.modPow(k, p); + return temp.mod(q); + } + + private BigInteger generateS(BigInteger x, BigInteger q, + BigInteger r, BigInteger k) throws SignatureException { + + byte[] s2; + try { + s2 = md.digest(); + } catch (RuntimeException re) { + // Only for RawDSA due to its 20-byte length restriction + throw new SignatureException(re.getMessage()); + } + // get the leftmost min(N, outLen) bits of the digest value + int nBytes = q.bitLength()/8; + if (nBytes < s2.length) { + s2 = Arrays.copyOfRange(s2, 0, nBytes); + } + BigInteger z = new BigInteger(1, s2); + BigInteger k1 = k.modInverse(q); + + return x.multiply(r).add(z).multiply(k1).mod(q); + } + + private BigInteger generateW(BigInteger p, BigInteger q, + BigInteger g, BigInteger s) { + return s.modInverse(q); + } + + private BigInteger generateV(BigInteger y, BigInteger p, + BigInteger q, BigInteger g, BigInteger w, BigInteger r) + throws SignatureException { + + byte[] s2; + try { + s2 = md.digest(); + } catch (RuntimeException re) { + // Only for RawDSA due to its 20-byte length restriction + throw new SignatureException(re.getMessage()); + } + // get the leftmost min(N, outLen) bits of the digest value + int nBytes = q.bitLength()/8; + if (nBytes < s2.length) { + s2 = Arrays.copyOfRange(s2, 0, nBytes); + } + BigInteger z = new BigInteger(1, s2); + + BigInteger u1 = z.multiply(w).mod(q); + BigInteger u2 = (r.multiply(w)).mod(q); + + BigInteger t1 = g.modPow(u1,p); + BigInteger t2 = y.modPow(u2,p); + BigInteger t3 = t1.multiply(t2); + BigInteger t5 = t3.mod(p); + return t5.mod(q); + } + + // NOTE: This following impl is defined in FIPS 186-3 AppendixB.2.2. + // Original DSS algos such as SHA1withDSA and RawDSA uses a different + // algorithm defined in FIPS 186-1 Sec3.2, and thus need to override this. + protected BigInteger generateK(BigInteger q) { + SecureRandom random = getSigningRandom(); + byte[] kValue = new byte[q.bitLength()/8]; + + while (true) { + random.nextBytes(kValue); + BigInteger k = new BigInteger(1, kValue).mod(q); + if (k.signum() > 0 && k.compareTo(q) < 0) { + return k; + } + } + } + + // Use the application-specified SecureRandom Object if provided. + // Otherwise, use our default SecureRandom Object. + protected SecureRandom getSigningRandom() { + if (signingRandom == null) { + if (appRandom != null) { + signingRandom = appRandom; + } else { + signingRandom = JCAUtil.getSecureRandom(); + } + } + return signingRandom; + } + + /** + * Return a human readable rendition of the engine. + */ + public String toString() { + String printable = "DSA Signature"; + if (presetP != null && presetQ != null && presetG != null) { + printable += "\n\tp: " + Debug.toHexString(presetP); + printable += "\n\tq: " + Debug.toHexString(presetQ); + printable += "\n\tg: " + Debug.toHexString(presetG); + } else { + printable += "\n\t P, Q or G not initialized."; + } + if (presetY != null) { + printable += "\n\ty: " + Debug.toHexString(presetY); + } + if (presetY == null && presetX == null) { + printable += "\n\tUNINIIALIZED"; + } + return printable; + } + + private static void debug(Exception e) { + if (debug) { + e.printStackTrace(); + } + } + + private static void debug(String s) { + if (debug) { + System.err.println(s); + } + } + + /** + * Standard SHA224withDSA implementation as defined in FIPS186-3. + */ + public static final class SHA224withDSA extends DSA { + public SHA224withDSA() throws NoSuchAlgorithmException { + super(MessageDigest.getInstance("SHA-224")); + } + } + + /** + * Standard SHA256withDSA implementation as defined in FIPS186-3. + */ + public static final class SHA256withDSA extends DSA { + public SHA256withDSA() throws NoSuchAlgorithmException { + super(MessageDigest.getInstance("SHA-256")); + } + } + + static class LegacyDSA extends DSA { + /* The random seed used to generate k */ + private int[] kSeed; + /* The random seed used to generate k (specified by application) */ + private byte[] kSeedAsByteArray; + /* + * The random seed used to generate k + * (prevent the same Kseed from being used twice in a row + */ + private int[] kSeedLast; + + public LegacyDSA(MessageDigest md) throws NoSuchAlgorithmException { + super(md); + } + + @Deprecated + protected void engineSetParameter(String key, Object param) { + if (key.equals("KSEED")) { + if (param instanceof byte[]) { + kSeed = byteArray2IntArray((byte[])param); + kSeedAsByteArray = (byte[])param; + } else { + debug("unrecognized param: " + key); + throw new InvalidParameterException("kSeed not a byte array"); + } + } else { + throw new InvalidParameterException("Unsupported parameter"); + } + } + + @Deprecated + protected Object engineGetParameter(String key) { + if (key.equals("KSEED")) { + return kSeedAsByteArray; + } else { + return null; + } + } + + /* + * Please read bug report 4044247 for an alternative, faster, + * NON-FIPS approved method to generate K + */ + @Override + protected BigInteger generateK(BigInteger q) { + BigInteger k = null; + + // The application specified a kSeed for us to use. + // Note: we dis-allow usage of the same Kseed twice in a row + if (kSeed != null && !Arrays.equals(kSeed, kSeedLast)) { + k = generateKUsingKSeed(kSeed, q); + if (k.signum() > 0 && k.compareTo(q) < 0) { + kSeedLast = kSeed.clone(); + return k; + } + } + + // The application did not specify a Kseed for us to use. + // We'll generate a new Kseed by getting random bytes from + // a SecureRandom object. + SecureRandom random = getSigningRandom(); + + while (true) { + int[] seed = new int[5]; + + for (int i = 0; i < 5; i++) seed[i] = random.nextInt(); + + k = generateKUsingKSeed(seed, q); + if (k.signum() > 0 && k.compareTo(q) < 0) { + kSeedLast = seed; + return k; + } + } + } + + /** + * Compute k for the DSA signature as defined in the original DSS, + * i.e. FIPS186. + * + * @param seed the seed for generating k. This seed should be + * secure. This is what is referred to as the KSEED in the DSA + * specification. + * + * @param g the g parameter from the DSA key pair. + */ + private BigInteger generateKUsingKSeed(int[] seed, BigInteger q) { + + // check out t in the spec. + int[] t = { 0xEFCDAB89, 0x98BADCFE, 0x10325476, + 0xC3D2E1F0, 0x67452301 }; + // + int[] tmp = SHA_7(seed, t); + byte[] tmpBytes = new byte[tmp.length * 4]; + for (int i = 0; i < tmp.length; i++) { + int k = tmp[i]; + for (int j = 0; j < 4; j++) { + tmpBytes[(i * 4) + j] = (byte) (k >>> (24 - (j * 8))); + } + } + BigInteger k = new BigInteger(1, tmpBytes).mod(q); + return k; + } + + // Constants for each round + private static final int round1_kt = 0x5a827999; + private static final int round2_kt = 0x6ed9eba1; + private static final int round3_kt = 0x8f1bbcdc; + private static final int round4_kt = 0xca62c1d6; + + /** + * Computes set 1 thru 7 of SHA-1 on m1. */ + static int[] SHA_7(int[] m1, int[] h) { + + int[] W = new int[80]; + System.arraycopy(m1,0,W,0,m1.length); + int temp = 0; + + for (int t = 16; t <= 79; t++){ + temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; + W[t] = ((temp << 1) | (temp >>>(32 - 1))); + } + + int a = h[0],b = h[1],c = h[2], d = h[3], e = h[4]; + for (int i = 0; i < 20; i++) { + temp = ((a<<5) | (a>>>(32-5))) + + ((b&c)|((~b)&d))+ e + W[i] + round1_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 2 + for (int i = 20; i < 40; i++) { + temp = ((a<<5) | (a>>>(32-5))) + + (b ^ c ^ d) + e + W[i] + round2_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 3 + for (int i = 40; i < 60; i++) { + temp = ((a<<5) | (a>>>(32-5))) + + ((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 4 + for (int i = 60; i < 80; i++) { + temp = ((a<<5) | (a>>>(32-5))) + + (b ^ c ^ d) + e + W[i] + round4_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + int[] md = new int[5]; + md[0] = h[0] + a; + md[1] = h[1] + b; + md[2] = h[2] + c; + md[3] = h[3] + d; + md[4] = h[4] + e; + return md; + } + + /* + * Utility routine for converting a byte array into an int array + */ + private int[] byteArray2IntArray(byte[] byteArray) { + + int j = 0; + byte[] newBA; + int mod = byteArray.length % 4; + + // guarantee that the incoming byteArray is a multiple of 4 + // (pad with 0's) + switch (mod) { + case 3: newBA = new byte[byteArray.length + 1]; break; + case 2: newBA = new byte[byteArray.length + 2]; break; + case 1: newBA = new byte[byteArray.length + 3]; break; + default: newBA = new byte[byteArray.length + 0]; break; + } + System.arraycopy(byteArray, 0, newBA, 0, byteArray.length); + + // copy each set of 4 bytes in the byte array into an integer + int[] newSeed = new int[newBA.length / 4]; + for (int i = 0; i < newBA.length; i += 4) { + newSeed[j] = newBA[i + 3] & 0xFF; + newSeed[j] |= (newBA[i + 2] << 8) & 0xFF00; + newSeed[j] |= (newBA[i + 1] << 16) & 0xFF0000; + newSeed[j] |= (newBA[i + 0] << 24) & 0xFF000000; + j++; + } + + return newSeed; + } + } + + public static final class SHA1withDSA extends LegacyDSA { + public SHA1withDSA() throws NoSuchAlgorithmException { + super(MessageDigest.getInstance("SHA-1")); + } + } + + /** + * RawDSA implementation. + * + * RawDSA requires the data to be exactly 20 bytes long. If it is + * not, a SignatureException is thrown when sign()/verify() is called + * per JCA spec. + */ + public static final class RawDSA extends LegacyDSA { + // Internal special-purpose MessageDigest impl for RawDSA + // Only override whatever methods used + // NOTE: no clone support + public static final class NullDigest20 extends MessageDigest { + // 20 byte digest buffer + private final byte[] digestBuffer = new byte[20]; + + // offset into the buffer; use Integer.MAX_VALUE to indicate + // out-of-bound condition + private int ofs = 0; + + protected NullDigest20() { + super("NullDigest20"); + } + protected void engineUpdate(byte input) { + if (ofs == digestBuffer.length) { + ofs = Integer.MAX_VALUE; + } else { + digestBuffer[ofs++] = input; + } + } + protected void engineUpdate(byte[] input, int offset, int len) { + if (ofs + len > digestBuffer.length) { + ofs = Integer.MAX_VALUE; + } else { + System.arraycopy(input, offset, digestBuffer, ofs, len); + ofs += len; + } + } + protected final void engineUpdate(ByteBuffer input) { + int inputLen = input.remaining(); + if (ofs + inputLen > digestBuffer.length) { + ofs = Integer.MAX_VALUE; + } else { + input.get(digestBuffer, ofs, inputLen); + ofs += inputLen; + } + } + protected byte[] engineDigest() throws RuntimeException { + if (ofs != digestBuffer.length) { + throw new RuntimeException + ("Data for RawDSA must be exactly 20 bytes long"); + } + reset(); + return digestBuffer; + } + protected int engineDigest(byte[] buf, int offset, int len) + throws DigestException { + if (ofs != digestBuffer.length) { + throw new DigestException + ("Data for RawDSA must be exactly 20 bytes long"); + } + if (len < digestBuffer.length) { + throw new DigestException + ("Output buffer too small; must be at least 20 bytes"); + } + System.arraycopy(digestBuffer, 0, buf, offset, digestBuffer.length); + reset(); + return digestBuffer.length; + } + + protected void engineReset() { + ofs = 0; + } + protected final int engineGetDigestLength() { + return digestBuffer.length; + } + } + + public RawDSA() throws NoSuchAlgorithmException { + super(new NullDigest20()); + } + } +} diff --git a/src/sun/security/provider/DSAKeyFactory.java b/src/sun/security/provider/DSAKeyFactory.java new file mode 100644 index 00000000..273e5e33 --- /dev/null +++ b/src/sun/security/provider/DSAKeyFactory.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.Key; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.KeyFactorySpi; +import java.security.InvalidKeyException; +import java.security.AccessController; +import java.security.interfaces.DSAParams; +import java.security.spec.DSAPublicKeySpec; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.KeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.security.spec.PKCS8EncodedKeySpec; + +import sun.security.action.GetPropertyAction; + +/** + * This class implements the DSA key factory of the Sun provider. + * + * @author Jan Luehe + * + * + * @since 1.2 + */ + +public class DSAKeyFactory extends KeyFactorySpi { + + // package private for DSAKeyPairGenerator + static final boolean SERIAL_INTEROP; + private static final String SERIAL_PROP = "sun.security.key.serial.interop"; + + static { + + /** + * Check to see if we need to maintain interoperability for serialized + * keys between JDK 5.0 -> JDK 1.4. In other words, determine whether + * a key object serialized in JDK 5.0 must be deserializable in + * JDK 1.4. + * + * If true, then we generate sun.security.provider.DSAPublicKey. + * If false, then we generate sun.security.provider.DSAPublicKeyImpl. + * + * By default this is false. + * This incompatibility was introduced by 4532506. + */ + String prop = AccessController.doPrivileged + (new GetPropertyAction(SERIAL_PROP, null)); + SERIAL_INTEROP = "true".equalsIgnoreCase(prop); + } + + /** + * Generates a public key object from the provided key specification + * (key material). + * + * @param keySpec the specification (key material) of the public key + * + * @return the public key + * + * @exception InvalidKeySpecException if the given key specification + * is inappropriate for this key factory to produce a public key. + */ + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + try { + if (keySpec instanceof DSAPublicKeySpec) { + DSAPublicKeySpec dsaPubKeySpec = (DSAPublicKeySpec)keySpec; + if (SERIAL_INTEROP) { + return new DSAPublicKey(dsaPubKeySpec.getY(), + dsaPubKeySpec.getP(), + dsaPubKeySpec.getQ(), + dsaPubKeySpec.getG()); + } else { + return new DSAPublicKeyImpl(dsaPubKeySpec.getY(), + dsaPubKeySpec.getP(), + dsaPubKeySpec.getQ(), + dsaPubKeySpec.getG()); + } + } else if (keySpec instanceof X509EncodedKeySpec) { + if (SERIAL_INTEROP) { + return new DSAPublicKey + (((X509EncodedKeySpec)keySpec).getEncoded()); + } else { + return new DSAPublicKeyImpl + (((X509EncodedKeySpec)keySpec).getEncoded()); + } + } else { + throw new InvalidKeySpecException + ("Inappropriate key specification"); + } + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException + ("Inappropriate key specification: " + e.getMessage()); + } + } + + /** + * Generates a private key object from the provided key specification + * (key material). + * + * @param keySpec the specification (key material) of the private key + * + * @return the private key + * + * @exception InvalidKeySpecException if the given key specification + * is inappropriate for this key factory to produce a private key. + */ + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + try { + if (keySpec instanceof DSAPrivateKeySpec) { + DSAPrivateKeySpec dsaPrivKeySpec = (DSAPrivateKeySpec)keySpec; + return new DSAPrivateKey(dsaPrivKeySpec.getX(), + dsaPrivKeySpec.getP(), + dsaPrivKeySpec.getQ(), + dsaPrivKeySpec.getG()); + + } else if (keySpec instanceof PKCS8EncodedKeySpec) { + return new DSAPrivateKey + (((PKCS8EncodedKeySpec)keySpec).getEncoded()); + + } else { + throw new InvalidKeySpecException + ("Inappropriate key specification"); + } + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException + ("Inappropriate key specification: " + e.getMessage()); + } + } + + /** + * Returns a specification (key material) of the given key object + * in the requested format. + * + * @param key the key + * + * @param keySpec the requested format in which the key material shall be + * returned + * + * @return the underlying key specification (key material) in the + * requested format + * + * @exception InvalidKeySpecException if the requested key specification is + * inappropriate for the given key, or the given key cannot be processed + * (e.g., the given key has an unrecognized algorithm or format). + */ + protected + T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + + DSAParams params; + + try { + + if (key instanceof java.security.interfaces.DSAPublicKey) { + + // Determine valid key specs + Class dsaPubKeySpec = Class.forName + ("java.security.spec.DSAPublicKeySpec"); + Class x509KeySpec = Class.forName + ("java.security.spec.X509EncodedKeySpec"); + + if (dsaPubKeySpec.isAssignableFrom(keySpec)) { + java.security.interfaces.DSAPublicKey dsaPubKey + = (java.security.interfaces.DSAPublicKey)key; + params = dsaPubKey.getParams(); + return keySpec.cast(new DSAPublicKeySpec(dsaPubKey.getY(), + params.getP(), + params.getQ(), + params.getG())); + + } else if (x509KeySpec.isAssignableFrom(keySpec)) { + return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); + + } else { + throw new InvalidKeySpecException + ("Inappropriate key specification"); + } + + } else if (key instanceof java.security.interfaces.DSAPrivateKey) { + + // Determine valid key specs + Class dsaPrivKeySpec = Class.forName + ("java.security.spec.DSAPrivateKeySpec"); + Class pkcs8KeySpec = Class.forName + ("java.security.spec.PKCS8EncodedKeySpec"); + + if (dsaPrivKeySpec.isAssignableFrom(keySpec)) { + java.security.interfaces.DSAPrivateKey dsaPrivKey + = (java.security.interfaces.DSAPrivateKey)key; + params = dsaPrivKey.getParams(); + return keySpec.cast(new DSAPrivateKeySpec(dsaPrivKey.getX(), + params.getP(), + params.getQ(), + params.getG())); + + } else if (pkcs8KeySpec.isAssignableFrom(keySpec)) { + return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded())); + + } else { + throw new InvalidKeySpecException + ("Inappropriate key specification"); + } + + } else { + throw new InvalidKeySpecException("Inappropriate key type"); + } + + } catch (ClassNotFoundException e) { + throw new InvalidKeySpecException + ("Unsupported key specification: " + e.getMessage()); + } + } + + /** + * Translates a key object, whose provider may be unknown or potentially + * untrusted, into a corresponding key object of this key factory. + * + * @param key the key whose provider is unknown or untrusted + * + * @return the translated key + * + * @exception InvalidKeyException if the given key cannot be processed by + * this key factory. + */ + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + + try { + + if (key instanceof java.security.interfaces.DSAPublicKey) { + // Check if key originates from this factory + if (key instanceof DSAPublicKey) { + return key; + } + // Convert key to spec + DSAPublicKeySpec dsaPubKeySpec + = engineGetKeySpec(key, DSAPublicKeySpec.class); + // Create key from spec, and return it + return engineGeneratePublic(dsaPubKeySpec); + + } else if (key instanceof java.security.interfaces.DSAPrivateKey) { + // Check if key originates from this factory + if (key instanceof DSAPrivateKey) { + return key; + } + // Convert key to spec + DSAPrivateKeySpec dsaPrivKeySpec + = engineGetKeySpec(key, DSAPrivateKeySpec.class); + // Create key from spec, and return it + return engineGeneratePrivate(dsaPrivKeySpec); + + } else { + throw new InvalidKeyException("Wrong algorithm type"); + } + + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Cannot translate key: " + + e.getMessage()); + } + } +} diff --git a/src/sun/security/provider/DSAKeyPairGenerator.java b/src/sun/security/provider/DSAKeyPairGenerator.java new file mode 100644 index 00000000..1f210690 --- /dev/null +++ b/src/sun/security/provider/DSAKeyPairGenerator.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.math.BigInteger; + +import java.security.*; +import java.security.SecureRandom; +import java.security.interfaces.DSAParams; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.DSAParameterSpec; + +import sun.security.jca.JCAUtil; + +/** + * This class generates DSA key parameters and public/private key + * pairs according to the DSS standard NIST FIPS 186. It uses the + * updated version of SHA, SHA-1 as described in FIPS 180-1. + * + * @author Benjamin Renaud + * @author Andreas Sterbenz + * + */ +public class DSAKeyPairGenerator extends KeyPairGenerator +implements java.security.interfaces.DSAKeyPairGenerator { + + /* Length for prime P and subPrime Q in bits */ + private int plen; + private int qlen; + + /* whether to force new parameters to be generated for each KeyPair */ + private boolean forceNewParameters; + + /* preset algorithm parameters. */ + private DSAParameterSpec params; + + /* The source of random bits to use */ + private SecureRandom random; + + public DSAKeyPairGenerator() { + super("DSA"); + initialize(1024, null); + } + + private static void checkStrength(int sizeP, int sizeQ) { + if ((sizeP >= 512) && (sizeP <= 1024) && (sizeP % 64 == 0) + && sizeQ == 160) { + // traditional - allow for backward compatibility + // L=multiples of 64 and between 512 and 1024 (inclusive) + // N=160 + } else if (sizeP == 2048 && (sizeQ == 224 || sizeQ == 256)) { + // L=2048, N=224 or 256 + } else { + throw new InvalidParameterException + ("Unsupported prime and subprime size combination: " + + sizeP + ", " + sizeQ); + } + } + + public void initialize(int modlen, SecureRandom random) { + // generate new parameters when no precomputed ones available. + initialize(modlen, true, random); + this.forceNewParameters = false; + } + + /** + * Initializes the DSA key pair generator. If genParams + * is false, a set of pre-computed parameters is used. + */ + public void initialize(int modlen, boolean genParams, SecureRandom random) { + int subPrimeLen = -1; + if (modlen <= 1024) { + subPrimeLen = 160; + } else if (modlen == 2048) { + subPrimeLen = 224; + } + checkStrength(modlen, subPrimeLen); + if (genParams) { + params = null; + } else { + params = ParameterCache.getCachedDSAParameterSpec(modlen, + subPrimeLen); + if (params == null) { + throw new InvalidParameterException + ("No precomputed parameters for requested modulus size " + + "available"); + } + + } + this.plen = modlen; + this.qlen = subPrimeLen; + this.random = random; + this.forceNewParameters = genParams; + } + + /** + * Initializes the DSA object using a DSA parameter object. + * + * @param params a fully initialized DSA parameter object. + */ + public void initialize(DSAParams params, SecureRandom random) { + if (params == null) { + throw new InvalidParameterException("Params must not be null"); + } + DSAParameterSpec spec = new DSAParameterSpec + (params.getP(), params.getQ(), params.getG()); + initialize0(spec, random); + } + + /** + * Initializes the DSA object using a parameter object. + * + * @param params the parameter set to be used to generate + * the keys. + * @param random the source of randomness for this generator. + * + * @exception InvalidAlgorithmParameterException if the given parameters + * are inappropriate for this key pair generator + */ + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + if (!(params instanceof DSAParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("Inappropriate parameter"); + } + initialize0((DSAParameterSpec)params, random); + } + + private void initialize0(DSAParameterSpec params, SecureRandom random) { + int sizeP = params.getP().bitLength(); + int sizeQ = params.getQ().bitLength(); + checkStrength(sizeP, sizeQ); + this.plen = sizeP; + this.qlen = sizeQ; + this.params = params; + this.random = random; + this.forceNewParameters = false; + } + + /** + * Generates a pair of keys usable by any JavaSecurity compliant + * DSA implementation. + */ + public KeyPair generateKeyPair() { + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + DSAParameterSpec spec; + try { + if (forceNewParameters) { + // generate new parameters each time + spec = ParameterCache.getNewDSAParameterSpec(plen, qlen, random); + } else { + if (params == null) { + params = + ParameterCache.getDSAParameterSpec(plen, qlen, random); + } + spec = params; + } + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + return generateKeyPair(spec.getP(), spec.getQ(), spec.getG(), random); + } + + public KeyPair generateKeyPair(BigInteger p, BigInteger q, BigInteger g, + SecureRandom random) { + + BigInteger x = generateX(random, q); + BigInteger y = generateY(x, p, g); + + try { + + // See the comments in DSAKeyFactory, 4532506, and 6232513. + + DSAPublicKey pub; + if (DSAKeyFactory.SERIAL_INTEROP) { + pub = new DSAPublicKey(y, p, q, g); + } else { + pub = new DSAPublicKeyImpl(y, p, q, g); + } + DSAPrivateKey priv = new DSAPrivateKey(x, p, q, g); + + KeyPair pair = new KeyPair(pub, priv); + return pair; + } catch (InvalidKeyException e) { + throw new ProviderException(e); + } + } + + /** + * Generate the private key component of the key pair using the + * provided source of random bits. This method uses the random but + * source passed to generate a seed and then calls the seed-based + * generateX method. + */ + private BigInteger generateX(SecureRandom random, BigInteger q) { + BigInteger x = null; + byte[] temp = new byte[qlen]; + while (true) { + random.nextBytes(temp); + x = new BigInteger(1, temp).mod(q); + if (x.signum() > 0 && (x.compareTo(q) < 0)) { + return x; + } + } + } + + /** + * Generate the public key component y of the key pair. + * + * @param x the private key component. + * + * @param p the base parameter. + */ + BigInteger generateY(BigInteger x, BigInteger p, BigInteger g) { + BigInteger y = g.modPow(x, p); + return y; + } + +} diff --git a/src/sun/security/provider/DSAParameterGenerator.java b/src/sun/security/provider/DSAParameterGenerator.java new file mode 100644 index 00000000..d0ae1d68 --- /dev/null +++ b/src/sun/security/provider/DSAParameterGenerator.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.math.BigInteger; +import java.security.AlgorithmParameterGeneratorSpi; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.InvalidParameterException; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.DSAParameterSpec; +import java.security.spec.DSAGenParameterSpec; + +/** + * This class generates parameters for the DSA algorithm. It uses a default + * prime modulus size of 1024 bits, which can be overwritten during + * initialization. + * + * @author Jan Luehe + * + * + * @see AlgorithmParameters + * @see AlgorithmParameterSpec + * @see DSAParameters + * + * @since 1.2 + */ + +public class DSAParameterGenerator extends AlgorithmParameterGeneratorSpi { + + // the default parameters + private static final DSAGenParameterSpec DEFAULTS = + new DSAGenParameterSpec(1024, 160, 160); + + // the length of prime P, subPrime Q, and seed in bits + private int valueL = -1; + private int valueN = -1; + private int seedLen = -1; + + // the source of randomness + private SecureRandom random; + + // useful constants + private static final BigInteger ZERO = BigInteger.valueOf(0); + private static final BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger TWO = BigInteger.valueOf(2); + + public DSAParameterGenerator() { + } + + /** + * Initializes this parameter generator for a certain strength + * and source of randomness. + * + * @param strength the strength (size of prime) in bits + * @param random the source of randomness + */ + protected void engineInit(int strength, SecureRandom random) { + if ((strength >= 512) && (strength <= 1024) && (strength % 64 == 0)) { + this.valueN = 160; + } else if (strength == 2048) { + this.valueN = 224; +// } else if (strength == 3072) { +// this.valueN = 256; + } else { + throw new InvalidParameterException + ("Prime size should be 512 - 1024, or 2048"); + } + this.valueL = strength; + this.seedLen = valueN; + this.random = random; + } + + /** + * Initializes this parameter generator with a set of + * algorithm-specific parameter generation values. + * + * @param genParamSpec the set of algorithm-specific parameter generation values + * @param random the source of randomness + * + * @exception InvalidAlgorithmParameterException if the given parameter + * generation values are inappropriate for this parameter generator + */ + protected void engineInit(AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException { + if (!(genParamSpec instanceof DSAGenParameterSpec)) { + throw new InvalidAlgorithmParameterException("Invalid parameter"); + } + DSAGenParameterSpec dsaGenParams = (DSAGenParameterSpec) genParamSpec; + int primePLen = dsaGenParams.getPrimePLength(); + if (primePLen > 2048) { + throw new InvalidParameterException + ("No support for prime size " + primePLen); + } + // directly initialize using the already validated values + this.valueL = primePLen; + this.valueN = dsaGenParams.getSubprimeQLength(); + this.seedLen = dsaGenParams.getSeedLength(); + this.random = random; + } + + /** + * Generates the parameters. + * + * @return the new AlgorithmParameters object + */ + protected AlgorithmParameters engineGenerateParameters() { + AlgorithmParameters algParams = null; + try { + if (this.random == null) { + this.random = new SecureRandom(); + } + if (valueL == -1) { + try { + engineInit(DEFAULTS, this.random); + } catch (InvalidAlgorithmParameterException iape) { + // should never happen + } + } + BigInteger[] pAndQ = generatePandQ(this.random, valueL, + valueN, seedLen); + BigInteger paramP = pAndQ[0]; + BigInteger paramQ = pAndQ[1]; + BigInteger paramG = generateG(paramP, paramQ); + + DSAParameterSpec dsaParamSpec = + new DSAParameterSpec(paramP, paramQ, paramG); + algParams = AlgorithmParameters.getInstance("DSA", "SUN"); + algParams.init(dsaParamSpec); + } catch (InvalidParameterSpecException e) { + // this should never happen + throw new RuntimeException(e.getMessage()); + } catch (NoSuchAlgorithmException e) { + // this should never happen, because we provide it + throw new RuntimeException(e.getMessage()); + } catch (NoSuchProviderException e) { + // this should never happen, because we provide it + throw new RuntimeException(e.getMessage()); + } + + return algParams; + } + + /* + * Generates the prime and subprime parameters for DSA, + * using the provided source of randomness. + * This method will generate new seeds until a suitable + * seed has been found. + * + * @param random the source of randomness to generate the + * seed + * @param valueL the size of p, in bits. + * @param valueN the size of q, in bits. + * @param seedLen the length of seed, in bits. + * + * @return an array of BigInteger, with p at index 0 and + * q at index 1, the seed at index 2, and the counter value + * at index 3. + */ + private static BigInteger[] generatePandQ(SecureRandom random, int valueL, + int valueN, int seedLen) { + String hashAlg = null; + if (valueN == 160) { + hashAlg = "SHA"; + } else if (valueN == 224) { + hashAlg = "SHA-224"; + } else if (valueN == 256) { + hashAlg = "SHA-256"; + } + MessageDigest hashObj = null; + try { + hashObj = MessageDigest.getInstance(hashAlg); + } catch (NoSuchAlgorithmException nsae) { + // should never happen + nsae.printStackTrace(); + } + + /* Step 3, 4: Useful variables */ + int outLen = hashObj.getDigestLength()*8; + int n = (valueL - 1) / outLen; + int b = (valueL - 1) % outLen; + byte[] seedBytes = new byte[seedLen/8]; + BigInteger twoSl = TWO.pow(seedLen); + int primeCertainty = 80; // for 1024-bit prime P + if (valueL == 2048) { + primeCertainty = 112; + //} else if (valueL == 3072) { + // primeCertainty = 128; + } + + BigInteger resultP, resultQ, seed = null; + int counter; + while (true) { + do { + /* Step 5 */ + random.nextBytes(seedBytes); + seed = new BigInteger(1, seedBytes); + + /* Step 6 */ + BigInteger U = new BigInteger(1, hashObj.digest(seedBytes)). + mod(TWO.pow(valueN - 1)); + + /* Step 7 */ + resultQ = TWO.pow(valueN - 1).add(U).add(ONE). subtract(U.mod(TWO)); + } while (!resultQ.isProbablePrime(primeCertainty)); + + /* Step 10 */ + BigInteger offset = ONE; + /* Step 11 */ + for (counter = 0; counter < 4*valueL; counter++) { + BigInteger V[] = new BigInteger[n + 1]; + /* Step 11.1 */ + for (int j = 0; j <= n; j++) { + BigInteger J = BigInteger.valueOf(j); + BigInteger tmp = (seed.add(offset).add(J)).mod(twoSl); + byte[] vjBytes = hashObj.digest(toByteArray(tmp)); + V[j] = new BigInteger(1, vjBytes); + } + /* Step 11.2 */ + BigInteger W = V[0]; + for (int i = 1; i < n; i++) { + W = W.add(V[i].multiply(TWO.pow(i * outLen))); + } + W = W.add((V[n].mod(TWO.pow(b))).multiply(TWO.pow(n * outLen))); + /* Step 11.3 */ + BigInteger twoLm1 = TWO.pow(valueL - 1); + BigInteger X = W.add(twoLm1); + /* Step 11.4, 11.5 */ + BigInteger c = X.mod(resultQ.multiply(TWO)); + resultP = X.subtract(c.subtract(ONE)); + /* Step 11.6, 11.7 */ + if (resultP.compareTo(twoLm1) > -1 + && resultP.isProbablePrime(primeCertainty)) { + /* Step 11.8 */ + BigInteger[] result = {resultP, resultQ, seed, + BigInteger.valueOf(counter)}; + return result; + } + /* Step 11.9 */ + offset = offset.add(BigInteger.valueOf(n)).add(ONE); + } + } + + } + + /* + * Generates the g parameter for DSA. + * + * @param p the prime, p. + * @param q the subprime, q. + * + * @param the g + */ + private static BigInteger generateG(BigInteger p, BigInteger q) { + BigInteger h = ONE; + /* Step 1 */ + BigInteger pMinusOneOverQ = (p.subtract(ONE)).divide(q); + BigInteger resultG = ONE; + while (resultG.compareTo(TWO) < 0) { + /* Step 3 */ + resultG = h.modPow(pMinusOneOverQ, p); + h = h.add(ONE); + } + return resultG; + } + + /* + * Converts the result of a BigInteger.toByteArray call to an exact + * signed magnitude representation for any positive number. + */ + private static byte[] toByteArray(BigInteger bigInt) { + byte[] result = bigInt.toByteArray(); + if (result[0] == 0) { + byte[] tmp = new byte[result.length - 1]; + System.arraycopy(result, 1, tmp, 0, tmp.length); + result = tmp; + } + return result; + } +} diff --git a/src/sun/security/provider/DSAParameters.java b/src/sun/security/provider/DSAParameters.java new file mode 100644 index 00000000..9a71ae1d --- /dev/null +++ b/src/sun/security/provider/DSAParameters.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.math.BigInteger; +import java.security.AlgorithmParametersSpi; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.DSAParameterSpec; +import java.security.spec.InvalidParameterSpecException; + +import sun.security.util.Debug; +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * This class implements the parameter set used by the + * Digital Signature Algorithm as specified in the FIPS 186 + * standard. + * + * @author Jan Luehe + * + * + * @since 1.2 + */ + +public class DSAParameters extends AlgorithmParametersSpi { + + // the prime (p) + protected BigInteger p; + + // the sub-prime (q) + protected BigInteger q; + + // the base (g) + protected BigInteger g; + + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException { + if (!(paramSpec instanceof DSAParameterSpec)) { + throw new InvalidParameterSpecException + ("Inappropriate parameter specification"); + } + this.p = ((DSAParameterSpec)paramSpec).getP(); + this.q = ((DSAParameterSpec)paramSpec).getQ(); + this.g = ((DSAParameterSpec)paramSpec).getG(); + } + + protected void engineInit(byte[] params) throws IOException { + DerValue encodedParams = new DerValue(params); + + if (encodedParams.tag != DerValue.tag_Sequence) { + throw new IOException("DSA params parsing error"); + } + + encodedParams.data.reset(); + + this.p = encodedParams.data.getBigInteger(); + this.q = encodedParams.data.getBigInteger(); + this.g = encodedParams.data.getBigInteger(); + + if (encodedParams.data.available() != 0) { + throw new IOException("encoded params have " + + encodedParams.data.available() + + " extra bytes"); + } + } + + protected void engineInit(byte[] params, String decodingMethod) + throws IOException { + engineInit(params); + } + + protected + T engineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException + { + try { + Class dsaParamSpec = Class.forName + ("java.security.spec.DSAParameterSpec"); + if (dsaParamSpec.isAssignableFrom(paramSpec)) { + return paramSpec.cast( + new DSAParameterSpec(this.p, this.q, this.g)); + } else { + throw new InvalidParameterSpecException + ("Inappropriate parameter Specification"); + } + } catch (ClassNotFoundException e) { + throw new InvalidParameterSpecException + ("Unsupported parameter specification: " + e.getMessage()); + } + } + + protected byte[] engineGetEncoded() throws IOException { + DerOutputStream out = new DerOutputStream(); + DerOutputStream bytes = new DerOutputStream(); + + bytes.putInteger(p); + bytes.putInteger(q); + bytes.putInteger(g); + out.write(DerValue.tag_Sequence, bytes); + return out.toByteArray(); + } + + protected byte[] engineGetEncoded(String encodingMethod) + throws IOException { + return engineGetEncoded(); + } + + /* + * Returns a formatted string describing the parameters. + */ + protected String engineToString() { + return "\n\tp: " + Debug.toHexString(p) + + "\n\tq: " + Debug.toHexString(q) + + "\n\tg: " + Debug.toHexString(g) + + "\n"; + } +} diff --git a/src/sun/security/provider/DSAPrivateKey.java b/src/sun/security/provider/DSAPrivateKey.java new file mode 100644 index 00000000..142a9a9b --- /dev/null +++ b/src/sun/security/provider/DSAPrivateKey.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.AlgorithmParameters; +import java.security.spec.DSAParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.interfaces.DSAParams; + +import sun.security.x509.AlgIdDSA; +import sun.security.pkcs.PKCS8Key; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; + +/** + * A PKCS#8 private key for the Digital Signature Algorithm. + * + * @author Benjamin Renaud + * + * + * @see DSAPublicKey + * @see AlgIdDSA + * @see DSA + */ + +public final class DSAPrivateKey extends PKCS8Key +implements java.security.interfaces.DSAPrivateKey, Serializable { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = -3244453684193605938L; + + /* the private key */ + private BigInteger x; + + /* + * Keep this constructor for backwards compatibility with JDK1.1. + */ + public DSAPrivateKey() { + } + + /** + * Make a DSA private key out of a private key and three parameters. + */ + public DSAPrivateKey(BigInteger x, BigInteger p, + BigInteger q, BigInteger g) + throws InvalidKeyException { + this.x = x; + algid = new AlgIdDSA(p, q, g); + + try { + key = new DerValue(DerValue.tag_Integer, + x.toByteArray()).toByteArray(); + encode(); + } catch (IOException e) { + InvalidKeyException ike = new InvalidKeyException( + "could not DER encode x: " + e.getMessage()); + ike.initCause(e); + throw ike; + } + } + + /** + * Make a DSA private key from its DER encoding (PKCS #8). + */ + public DSAPrivateKey(byte[] encoded) throws InvalidKeyException { + clearOldKey(); + decode(encoded); + } + + /** + * Returns the DSA parameters associated with this key, or null if the + * parameters could not be parsed. + */ + public DSAParams getParams() { + try { + if (algid instanceof DSAParams) { + return (DSAParams)algid; + } else { + DSAParameterSpec paramSpec; + AlgorithmParameters algParams = algid.getParameters(); + if (algParams == null) { + return null; + } + paramSpec = algParams.getParameterSpec(DSAParameterSpec.class); + return (DSAParams)paramSpec; + } + } catch (InvalidParameterSpecException e) { + return null; + } + } + + /** + * Get the raw private key, x, without the parameters. + * + * @see getParameters + */ + public BigInteger getX() { + return x; + } + + private void clearOldKey() { + int i; + if (this.encodedKey != null) { + for (i = 0; i < this.encodedKey.length; i++) { + this.encodedKey[i] = (byte)0x00; + } + } + if (this.key != null) { + for (i = 0; i < this.key.length; i++) { + this.key[i] = (byte)0x00; + } + } + } + + protected void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(key); + x = in.getBigInteger(); + } catch (IOException e) { + InvalidKeyException ike = new InvalidKeyException(e.getMessage()); + ike.initCause(e); + throw ike; + } + } +} diff --git a/src/sun/security/provider/DSAPublicKey.java b/src/sun/security/provider/DSAPublicKey.java new file mode 100644 index 00000000..2ad65430 --- /dev/null +++ b/src/sun/security/provider/DSAPublicKey.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.AlgorithmParameters; +import java.security.spec.DSAParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.interfaces.DSAParams; + +import sun.security.x509.X509Key; +import sun.security.x509.AlgIdDSA; +import sun.security.util.BitArray; +import sun.security.util.Debug; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; + +/** + * An X.509 public key for the Digital Signature Algorithm. + * + * @author Benjamin Renaud + * + * + * @see DSAPrivateKey + * @see AlgIdDSA + * @see DSA + */ + +public class DSAPublicKey extends X509Key +implements java.security.interfaces.DSAPublicKey, Serializable { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = -2994193307391104133L; + + /* the public key */ + private BigInteger y; + + /* + * Keep this constructor for backwards compatibility with JDK1.1. + */ + public DSAPublicKey() { + } + + /** + * Make a DSA public key out of a public key and three parameters. + * The p, q, and g parameters may be null, but if so, parameters will need + * to be supplied from some other source before this key can be used in + * cryptographic operations. PKIX RFC2459bis explicitly allows DSA public + * keys without parameters, where the parameters are provided in the + * issuer's DSA public key. + * + * @param y the actual key bits + * @param p DSA parameter p, may be null if all of p, q, and g are null. + * @param q DSA parameter q, may be null if all of p, q, and g are null. + * @param g DSA parameter g, may be null if all of p, q, and g are null. + */ + public DSAPublicKey(BigInteger y, BigInteger p, BigInteger q, + BigInteger g) + throws InvalidKeyException { + this.y = y; + algid = new AlgIdDSA(p, q, g); + + try { + byte[] keyArray = new DerValue(DerValue.tag_Integer, + y.toByteArray()).toByteArray(); + setKey(new BitArray(keyArray.length*8, keyArray)); + encode(); + } catch (IOException e) { + throw new InvalidKeyException("could not DER encode y: " + + e.getMessage()); + } + } + + /** + * Make a DSA public key from its DER encoding (X.509). + */ + public DSAPublicKey(byte[] encoded) throws InvalidKeyException { + decode(encoded); + } + + /** + * Returns the DSA parameters associated with this key, or null if the + * parameters could not be parsed. + */ + public DSAParams getParams() { + try { + if (algid instanceof DSAParams) { + return (DSAParams)algid; + } else { + DSAParameterSpec paramSpec; + AlgorithmParameters algParams = algid.getParameters(); + if (algParams == null) { + return null; + } + paramSpec = algParams.getParameterSpec(DSAParameterSpec.class); + return (DSAParams)paramSpec; + } + } catch (InvalidParameterSpecException e) { + return null; + } + } + + /** + * Get the raw public value, y, without the parameters. + * + * @see getParameters + */ + public BigInteger getY() { + return y; + } + + public String toString() { + return "Sun DSA Public Key\n Parameters:" + algid + + "\n y:\n" + Debug.toHexString(y) + "\n"; + } + + protected void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(getKey().toByteArray()); + y = in.getBigInteger(); + } catch (IOException e) { + throw new InvalidKeyException("Invalid key: y value\n" + + e.getMessage()); + } + } +} diff --git a/src/sun/security/provider/DSAPublicKeyImpl.java b/src/sun/security/provider/DSAPublicKeyImpl.java new file mode 100644 index 00000000..7ccc1c02 --- /dev/null +++ b/src/sun/security/provider/DSAPublicKeyImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.math.BigInteger; +import java.security.KeyRep; +import java.security.InvalidKeyException; + +/** + * An X.509 public key for the Digital Signature Algorithm. + * + * The difference between DSAPublicKeyImpl and DSAPublicKey is that + * DSAPublicKeyImpl calls writeReplace with KeyRep, and DSAPublicKey + * calls writeObject. + * + * See the comments in DSAKeyFactory, 4532506, and 6232513. + * + */ + +public final class DSAPublicKeyImpl extends DSAPublicKey { + + private static final long serialVersionUID = 7819830118247182730L; + + /** + * Make a DSA public key out of a public key and three parameters. + * The p, q, and g parameters may be null, but if so, parameters will need + * to be supplied from some other source before this key can be used in + * cryptographic operations. PKIX RFC2459bis explicitly allows DSA public + * keys without parameters, where the parameters are provided in the + * issuer's DSA public key. + * + * @param y the actual key bits + * @param p DSA parameter p, may be null if all of p, q, and g are null. + * @param q DSA parameter q, may be null if all of p, q, and g are null. + * @param g DSA parameter g, may be null if all of p, q, and g are null. + */ + public DSAPublicKeyImpl(BigInteger y, BigInteger p, BigInteger q, + BigInteger g) + throws InvalidKeyException { + super(y, p, q, g); + } + + /** + * Make a DSA public key from its DER encoding (X.509). + */ + public DSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { + super(encoded); + } + + protected Object writeReplace() throws java.io.ObjectStreamException { + return new KeyRep(KeyRep.Type.PUBLIC, + getAlgorithm(), + getFormat(), + getEncoded()); + } +} diff --git a/src/sun/security/provider/DigestBase.java b/src/sun/security/provider/DigestBase.java new file mode 100644 index 00000000..98af71af --- /dev/null +++ b/src/sun/security/provider/DigestBase.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.MessageDigestSpi; +import java.security.DigestException; +import java.security.ProviderException; + +/** + * Common base message digest implementation for the Sun provider. + * It implements all the JCA methods as suitable for a Java message digest + * implementation of an algorithm based on a compression function (as all + * commonly used algorithms are). The individual digest subclasses only need to + * implement the following methods: + * + * . abstract void implCompress(byte[] b, int ofs); + * . abstract void implDigest(byte[] out, int ofs); + * . abstract void implReset(); + * + * See the inline documentation for details. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +abstract class DigestBase extends MessageDigestSpi implements Cloneable { + + // one element byte array, temporary storage for update(byte) + private byte[] oneByte; + + // algorithm name to use in the exception message + private final String algorithm; + // length of the message digest in bytes + private final int digestLength; + + // size of the input to the compression function in bytes + private final int blockSize; + // buffer to store partial blocks, blockSize bytes large + // Subclasses should not access this array directly except possibly in their + // implDigest() method. See MD5.java as an example. + byte[] buffer; + // offset into buffer + private int bufOfs; + + // number of bytes processed so far. subclasses should not modify + // this value. + // also used as a flag to indicate reset status + // -1: need to call engineReset() before next call to update() + // 0: is already reset + long bytesProcessed; + + /** + * Main constructor. + */ + DigestBase(String algorithm, int digestLength, int blockSize) { + super(); + this.algorithm = algorithm; + this.digestLength = digestLength; + this.blockSize = blockSize; + buffer = new byte[blockSize]; + } + + // return digest length. See JCA doc. + protected final int engineGetDigestLength() { + return digestLength; + } + + // single byte update. See JCA doc. + protected final void engineUpdate(byte b) { + if (oneByte == null) { + oneByte = new byte[1]; + } + oneByte[0] = b; + engineUpdate(oneByte, 0, 1); + } + + // array update. See JCA doc. + protected final void engineUpdate(byte[] b, int ofs, int len) { + if (len == 0) { + return; + } + if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (bytesProcessed < 0) { + engineReset(); + } + bytesProcessed += len; + // if buffer is not empty, we need to fill it before proceeding + if (bufOfs != 0) { + int n = Math.min(len, blockSize - bufOfs); + System.arraycopy(b, ofs, buffer, bufOfs, n); + bufOfs += n; + ofs += n; + len -= n; + if (bufOfs >= blockSize) { + // compress completed block now + implCompress(buffer, 0); + bufOfs = 0; + } + } + // compress complete blocks + if (len >= blockSize) { + int limit = ofs + len; + ofs = implCompressMultiBlock(b, ofs, limit - blockSize); + len = limit - ofs; + } + // copy remainder to buffer + if (len > 0) { + System.arraycopy(b, ofs, buffer, 0, len); + bufOfs = len; + } + } + + // compress complete blocks + private int implCompressMultiBlock(byte[] b, int ofs, int limit) { + for (; ofs <= limit; ofs += blockSize) { + implCompress(b, ofs); + } + return ofs; + } + + // reset this object. See JCA doc. + protected final void engineReset() { + if (bytesProcessed == 0) { + // already reset, ignore + return; + } + implReset(); + bufOfs = 0; + bytesProcessed = 0; + } + + // return the digest. See JCA doc. + protected final byte[] engineDigest() { + byte[] b = new byte[digestLength]; + try { + engineDigest(b, 0, b.length); + } catch (DigestException e) { + throw (ProviderException) + new ProviderException("Internal error").initCause(e); + } + return b; + } + + // return the digest in the specified array. See JCA doc. + protected final int engineDigest(byte[] out, int ofs, int len) + throws DigestException { + if (len < digestLength) { + throw new DigestException("Length must be at least " + + digestLength + " for " + algorithm + "digests"); + } + if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) { + throw new DigestException("Buffer too short to store digest"); + } + if (bytesProcessed < 0) { + engineReset(); + } + implDigest(out, ofs); + bytesProcessed = -1; + return digestLength; + } + + /** + * Core compression function. Processes blockSize bytes at a time + * and updates the state of this object. + */ + abstract void implCompress(byte[] b, int ofs); + + /** + * Return the digest. Subclasses do not need to reset() themselves, + * DigestBase calls implReset() when necessary. + */ + abstract void implDigest(byte[] out, int ofs); + + /** + * Reset subclass specific state to their initial values. DigestBase + * calls this method when necessary. + */ + abstract void implReset(); + + public Object clone() throws CloneNotSupportedException { + DigestBase copy = (DigestBase) super.clone(); + copy.buffer = copy.buffer.clone(); + return copy; + } + + // padding used for the MD5, and SHA-* message digests + static final byte[] padding; + + static { + // we need 128 byte padding for SHA-384/512 + // and an additional 8 bytes for the high 8 bytes of the 16 + // byte bit counter in SHA-384/512 + padding = new byte[136]; + padding[0] = (byte)0x80; + } +} diff --git a/src/sun/security/provider/DomainKeyStore.java b/src/sun/security/provider/DomainKeyStore.java new file mode 100644 index 00000000..482d8182 --- /dev/null +++ b/src/sun/security/provider/DomainKeyStore.java @@ -0,0 +1,899 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.util.*; + +import sun.security.util.PolicyUtil; + +/** + * This class provides the domain keystore type identified as "DKS". + * DKS presents a collection of separate keystores as a single logical keystore. + * The collection of keystores is specified in a domain configuration file which + * is passed to DKS in a {@link DomainLoadStoreParameter}. + *

    + * The following properties are supported: + *

    + *
    {@code keystoreType=""}
    + *
    The keystore type.
    + *
    {@code keystoreURI=""}
    + *
    The keystore location.
    + *
    {@code keystoreProviderName=""}
    + *
    The name of the keystore's JCE provider.
    + *
    {@code keystorePasswordEnv=""}
    + *
    The environment variable that stores a keystore password. + *
    {@code entryNameSeparator=""}
    + *
    The separator between a keystore name prefix and an entry name. + * When specified, it applies to all the entries in a domain. + * Its default value is a space.
    + *
    + * + * @since 1.8 + */ + +abstract class DomainKeyStore extends KeyStoreSpi { + + // regular DKS + public static final class DKS extends DomainKeyStore { + String convertAlias(String alias) { + return alias.toLowerCase(Locale.ENGLISH); + } + } + + // DKS property names + private static final String ENTRY_NAME_SEPARATOR = "entrynameseparator"; + private static final String KEYSTORE_PROVIDER_NAME = "keystoreprovidername"; + private static final String KEYSTORE_TYPE = "keystoretype"; + private static final String KEYSTORE_URI = "keystoreuri"; + private static final String KEYSTORE_PASSWORD_ENV = "keystorepasswordenv"; + + // RegEx meta characters + private static final String REGEX_META = ".$|()[{^?*+\\"; + + // Default prefix for keystores loaded-by-stream + private static final String DEFAULT_STREAM_PREFIX = "iostream"; + private int streamCounter = 1; + private String entryNameSeparator = " "; + private String entryNameSeparatorRegEx = " "; + + // Default keystore type + private static final String DEFAULT_KEYSTORE_TYPE = + KeyStore.getDefaultType(); + + // Domain keystores + private final Map keystores = new HashMap<>(); + + DomainKeyStore() { + } + + // convert an alias to internal form, overridden in subclasses: + // lower case for regular DKS + abstract String convertAlias(String alias); + + /** + * Returns the key associated with the given alias, using the given + * password to recover it. + * + * @param alias the alias name + * @param password the password for recovering the key + * + * @return the requested key, or null if the given alias does not exist + * or does not identify a key entry. + * + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * key cannot be found + * @exception UnrecoverableKeyException if the key cannot be recovered + * (e.g., the given password is wrong). + */ + public Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException + { + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + Key key = null; + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + key = keystore.getKey(entryAlias, password); + if (key != null) { + break; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return key; + } + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last), or null if the given alias + * does not exist or does not contain a certificate chain (i.e., the given + * alias identifies either a trusted certificate entry or a + * key entry without a certificate chain). + */ + public Certificate[] engineGetCertificateChain(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + Certificate[] chain = null; + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + chain = keystore.getCertificateChain(entryAlias); + if (chain != null) { + break; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return chain; + } + + /** + * Returns the certificate associated with the given alias. + * + *

    If the given alias name identifies a + * trusted certificate entry, the certificate associated with that + * entry is returned. If the given alias name identifies a + * key entry, the first element of the certificate chain of that + * entry is returned, or null if that entry does not have a certificate + * chain. + * + * @param alias the alias name + * + * @return the certificate, or null if the given alias does not exist or + * does not contain a certificate. + */ + public Certificate engineGetCertificate(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + Certificate cert = null; + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + cert = keystore.getCertificate(entryAlias); + if (cert != null) { + break; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return cert; + } + + /** + * Returns the creation date of the entry identified by the given alias. + * + * @param alias the alias name + * + * @return the creation date of this entry, or null if the given alias does + * not exist + */ + public Date engineGetCreationDate(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + Date date = null; + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + date = keystore.getCreationDate(entryAlias); + if (date != null) { + break; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return date; + } + + /** + * Assigns the given private key to the given alias, protecting + * it with the given password as defined in PKCS8. + * + *

    The given java.security.PrivateKey key must + * be accompanied by a certificate chain certifying the + * corresponding public key. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key and certificate + * chain. + * + * @param alias the alias name + * @param key the private key to be associated with the alias + * @param password the password to protect the key + * @param chain the certificate chain for the corresponding public + * key (only required if the given key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if the given key is not a private key, + * cannot be protected, or this operation fails for some other reason + */ + public void engineSetKeyEntry(String alias, Key key, char[] password, + Certificate[] chain) + throws KeyStoreException + { + AbstractMap.SimpleEntry> pair = + getKeystoreForWriting(alias); + + if (pair == null) { + throw new KeyStoreException("Error setting key entry for '" + + alias + "'"); + } + String entryAlias = pair.getKey(); + Map.Entry keystore = pair.getValue(); + keystore.getValue().setKeyEntry(entryAlias, key, password, chain); + } + + /** + * Assigns the given key (that has already been protected) to the given + * alias. + * + *

    If the protected key is of type + * java.security.PrivateKey, it must be accompanied by a + * certificate chain certifying the corresponding public key. If the + * underlying keystore implementation is of type jks, + * key must be encoded as an + * EncryptedPrivateKeyInfo as defined in the PKCS #8 standard. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key (in protected format) to be associated with the alias + * @param chain the certificate chain for the corresponding public + * key (only useful if the protected key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if this operation fails. + */ + public void engineSetKeyEntry(String alias, byte[] key, + Certificate[] chain) + throws KeyStoreException + { + AbstractMap.SimpleEntry> pair = + getKeystoreForWriting(alias); + + if (pair == null) { + throw new KeyStoreException( + "Error setting protected key entry for '" + alias + "'"); + } + String entryAlias = pair.getKey(); + Map.Entry keystore = pair.getValue(); + keystore.getValue().setKeyEntry(entryAlias, key, chain); + } + + /** + * Assigns the given certificate to the given alias. + * + *

    If the given alias already exists in this keystore and identifies a + * trusted certificate entry, the certificate associated with it is + * overridden by the given certificate. + * + * @param alias the alias name + * @param cert the certificate + * + * @exception KeyStoreException if the given alias already exists and does + * not identify a trusted certificate entry, or this operation + * fails for some other reason. + */ + public void engineSetCertificateEntry(String alias, Certificate cert) + throws KeyStoreException + { + AbstractMap.SimpleEntry> pair = + getKeystoreForWriting(alias); + + if (pair == null) { + throw new KeyStoreException("Error setting certificate entry for '" + + alias + "'"); + } + String entryAlias = pair.getKey(); + Map.Entry keystore = pair.getValue(); + keystore.getValue().setCertificateEntry(entryAlias, cert); + } + + /** + * Deletes the entry identified by the given alias from this keystore. + * + * @param alias the alias name + * + * @exception KeyStoreException if the entry cannot be removed. + */ + public void engineDeleteEntry(String alias) throws KeyStoreException + { + AbstractMap.SimpleEntry> pair = + getKeystoreForWriting(alias); + + if (pair == null) { + throw new KeyStoreException("Error deleting entry for '" + alias + + "'"); + } + String entryAlias = pair.getKey(); + Map.Entry keystore = pair.getValue(); + keystore.getValue().deleteEntry(entryAlias); + } + + /** + * Lists all the alias names of this keystore. + * + * @return enumeration of the alias names + */ + public Enumeration engineAliases() { + final Iterator> iterator = + keystores.entrySet().iterator(); + + return new Enumeration() { + private int index = 0; + private Map.Entry keystoresEntry = null; + private String prefix = null; + private Enumeration aliases = null; + + public boolean hasMoreElements() { + try { + if (aliases == null) { + if (iterator.hasNext()) { + keystoresEntry = iterator.next(); + prefix = keystoresEntry.getKey() + + entryNameSeparator; + aliases = keystoresEntry.getValue().aliases(); + } else { + return false; + } + } + if (aliases.hasMoreElements()) { + return true; + } else { + if (iterator.hasNext()) { + keystoresEntry = iterator.next(); + prefix = keystoresEntry.getKey() + + entryNameSeparator; + aliases = keystoresEntry.getValue().aliases(); + } else { + return false; + } + } + } catch (KeyStoreException e) { + return false; + } + + return aliases.hasMoreElements(); + } + + public String nextElement() { + if (hasMoreElements()) { + return prefix + aliases.nextElement(); + } + throw new NoSuchElementException(); + } + }; + } + + /** + * Checks if the given alias exists in this keystore. + * + * @param alias the alias name + * + * @return true if the alias exists, false otherwise + */ + public boolean engineContainsAlias(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + if (keystore.containsAlias(entryAlias)) { + return true; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return false; + } + + /** + * Retrieves the number of entries in this keystore. + * + * @return the number of entries in this keystore + */ + public int engineSize() { + + int size = 0; + try { + for (KeyStore keystore : keystores.values()) { + size += keystore.size(); + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return size; + } + + /** + * Returns true if the entry identified by the given alias is a + * key entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * key entry, false otherwise. + */ + public boolean engineIsKeyEntry(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + if (keystore.isKeyEntry(entryAlias)) { + return true; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return false; + } + + /** + * Returns true if the entry identified by the given alias is a + * trusted certificate entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * trusted certificate entry, false otherwise. + */ + public boolean engineIsCertificateEntry(String alias) { + + AbstractMap.SimpleEntry> pair = + getKeystoresForReading(alias); + + try { + String entryAlias = pair.getKey(); + for (KeyStore keystore : pair.getValue()) { + if (keystore.isCertificateEntry(entryAlias)) { + return true; + } + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + return false; + } + + /* + * Returns a keystore entry alias and a list of target keystores. + * When the supplied alias prefix identifies a keystore then that single + * keystore is returned. When no alias prefix is supplied then all the + * keystores are returned. + */ + private AbstractMap.SimpleEntry> + getKeystoresForReading(String alias) { + + String[] splits = alias.split(this.entryNameSeparatorRegEx, 2); + if (splits.length == 2) { // prefixed alias + KeyStore keystore = keystores.get(splits[0]); + if (keystore != null) { + return new AbstractMap.SimpleEntry<>(splits[1], + (Collection) Collections.singleton(keystore)); + } + } else if (splits.length == 1) { // unprefixed alias + // Check all keystores for the first occurrence of the alias + return new AbstractMap.SimpleEntry<>(alias, keystores.values()); + } + return new AbstractMap.SimpleEntry<>("", + (Collection) Collections.emptyList()); + } + + /* + * Returns a keystore entry alias and a single target keystore. + * An alias prefix must be supplied. + */ + private + AbstractMap.SimpleEntry> + getKeystoreForWriting(String alias) { + + String[] splits = alias.split(this.entryNameSeparator, 2); + if (splits.length == 2) { // prefixed alias + KeyStore keystore = keystores.get(splits[0]); + if (keystore != null) { + return new AbstractMap.SimpleEntry<>(splits[1], + new AbstractMap.SimpleEntry<>(splits[0], keystore)); + } + } + return null; + } + + /** + * Returns the (alias) name of the first keystore entry whose certificate + * matches the given certificate. + * + *

    This method attempts to match the given certificate with each + * keystore entry. If the entry being considered + * is a trusted certificate entry, the given certificate is + * compared to that entry's certificate. If the entry being considered is + * a key entry, the given certificate is compared to the first + * element of that entry's certificate chain (if a chain exists). + * + * @param cert the certificate to match with. + * + * @return the (alias) name of the first entry with matching certificate, + * or null if no such entry exists in this keystore. + */ + public String engineGetCertificateAlias(Certificate cert) { + + try { + + String alias = null; + for (KeyStore keystore : keystores.values()) { + if ((alias = keystore.getCertificateAlias(cert)) != null) { + break; + } + } + return alias; + + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + } + + /** + * Stores this keystore to the given output stream, and protects its + * integrity with the given password. + * + * @param stream the output stream to which this keystore is written. + * @param password the password to generate the keystore integrity check + * + * @exception IOException if there was an I/O problem with data + * @exception NoSuchAlgorithmException if the appropriate data integrity + * algorithm could not be found + * @exception CertificateException if any of the certificates included in + * the keystore data could not be stored + */ + public void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + // Support storing to a stream only when a single keystore has been + // configured + try { + if (keystores.size() == 1) { + keystores.values().iterator().next().store(stream, password); + return; + } + } catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + + throw new UnsupportedOperationException( + "This keystore must be stored using a DomainLoadStoreParameter"); + } + + @Override + public void engineStore(KeyStore.LoadStoreParameter param) + throws IOException, NoSuchAlgorithmException, CertificateException + { + if (param instanceof DomainLoadStoreParameter) { + DomainLoadStoreParameter domainParameter = + (DomainLoadStoreParameter) param; + List builders = getBuilders( + domainParameter.getConfiguration(), + domainParameter.getProtectionParams()); + + for (KeyStoreBuilderComponents builder : builders) { + + try { + + KeyStore.ProtectionParameter pp = builder.protection; + if (!(pp instanceof KeyStore.PasswordProtection)) { + throw new KeyStoreException( + new IllegalArgumentException("ProtectionParameter" + + " must be a KeyStore.PasswordProtection")); + } + char[] password = + ((KeyStore.PasswordProtection) builder.protection) + .getPassword(); + + // Store the keystores + KeyStore keystore = keystores.get(builder.name); + + try (FileOutputStream stream = + new FileOutputStream(builder.file)) { + + keystore.store(stream, password); + } + } catch (KeyStoreException e) { + throw new IOException(e); + } + } + } else { + throw new UnsupportedOperationException( + "This keystore must be stored using a " + + "DomainLoadStoreParameter"); + } + } + + /** + * Loads the keystore from the given input stream. + * + *

    If a password is given, it is used to check the integrity of the + * keystore data. Otherwise, the integrity of the keystore is not checked. + * + * @param stream the input stream from which the keystore is loaded + * @param password the (optional) password used to check the integrity of + * the keystore. + * + * @exception IOException if there is an I/O or format problem with the + * keystore data + * @exception NoSuchAlgorithmException if the algorithm used to check + * the integrity of the keystore cannot be found + * @exception CertificateException if any of the certificates in the + * keystore could not be loaded + */ + public void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + // Support loading from a stream only for a JKS or default type keystore + try { + KeyStore keystore = null; + + try { + keystore = KeyStore.getInstance("JKS"); + keystore.load(stream, password); + + } catch (Exception e) { + // Retry + if (!"JKS".equalsIgnoreCase(DEFAULT_KEYSTORE_TYPE)) { + keystore = KeyStore.getInstance(DEFAULT_KEYSTORE_TYPE); + keystore.load(stream, password); + } else { + throw e; + } + } + String keystoreName = DEFAULT_STREAM_PREFIX + streamCounter++; + keystores.put(keystoreName, keystore); + + } catch (Exception e) { + throw new UnsupportedOperationException( + "This keystore must be loaded using a " + + "DomainLoadStoreParameter"); + } + } + + @Override + public void engineLoad(KeyStore.LoadStoreParameter param) + throws IOException, NoSuchAlgorithmException, CertificateException + { + if (param instanceof DomainLoadStoreParameter) { + DomainLoadStoreParameter domainParameter = + (DomainLoadStoreParameter) param; + List builders = getBuilders( + domainParameter.getConfiguration(), + domainParameter.getProtectionParams()); + + for (KeyStoreBuilderComponents builder : builders) { + + try { + // Load the keystores (file-based and non-file-based) + if (builder.file != null) { + keystores.put(builder.name, + KeyStore.Builder.newInstance(builder.type, + builder.provider, builder.file, + builder.protection) + .getKeyStore()); + } else { + keystores.put(builder.name, + KeyStore.Builder.newInstance(builder.type, + builder.provider, builder.protection) + .getKeyStore()); + } + } catch (KeyStoreException e) { + throw new IOException(e); + } + } + } else { + throw new UnsupportedOperationException( + "This keystore must be loaded using a " + + "DomainLoadStoreParameter"); + } + } + + /* + * Parse a keystore domain configuration file and associated collection + * of keystore passwords to create a collection of KeyStore.Builder. + */ + private List getBuilders(URI configuration, + Map passwords) + throws IOException { + + PolicyParser parser = new PolicyParser(true); // expand properties + Collection domains = null; + List builders = new ArrayList<>(); + String uriDomain = configuration.getFragment(); + + try (InputStreamReader configurationReader = + new InputStreamReader( + PolicyUtil.getInputStream(configuration.toURL()), "UTF-8")) { + parser.read(configurationReader); + domains = parser.getDomainEntries(); + + } catch (MalformedURLException mue) { + throw new IOException(mue); + + } catch (PolicyParser.ParsingException pe) { + throw new IOException(pe); + } + + for (PolicyParser.DomainEntry domain : domains) { + Map domainProperties = domain.getProperties(); + + if (uriDomain != null && + (!uriDomain.equalsIgnoreCase(domain.getName()))) { + continue; // skip this domain + } + + if (domainProperties.containsKey(ENTRY_NAME_SEPARATOR)) { + this.entryNameSeparator = + domainProperties.get(ENTRY_NAME_SEPARATOR); + // escape any regex meta characters + char ch = 0; + StringBuilder s = new StringBuilder(); + for (int i = 0; i < this.entryNameSeparator.length(); i++) { + ch = this.entryNameSeparator.charAt(i); + if (REGEX_META.indexOf(ch) != -1) { + s.append('\\'); + } + s.append(ch); + } + this.entryNameSeparatorRegEx = s.toString(); + } + + Collection keystores = + domain.getEntries(); + for (PolicyParser.KeyStoreEntry keystore : keystores) { + String keystoreName = keystore.getName(); + Map properties = + new HashMap<>(domainProperties); + properties.putAll(keystore.getProperties()); + + String keystoreType = DEFAULT_KEYSTORE_TYPE; + if (properties.containsKey(KEYSTORE_TYPE)) { + keystoreType = properties.get(KEYSTORE_TYPE); + } + + Provider keystoreProvider = null; + if (properties.containsKey(KEYSTORE_PROVIDER_NAME)) { + String keystoreProviderName = + properties.get(KEYSTORE_PROVIDER_NAME); + keystoreProvider = + Security.getProvider(keystoreProviderName); + if (keystoreProvider == null) { + throw new IOException("Error locating JCE provider: " + + keystoreProviderName); + } + } + + File keystoreFile = null; + if (properties.containsKey(KEYSTORE_URI)) { + String uri = properties.get(KEYSTORE_URI); + + try { + if (uri.startsWith("file://")) { + keystoreFile = new File(new URI(uri)); + } else { + keystoreFile = new File(uri); + } + + } catch (URISyntaxException | IllegalArgumentException e) { + throw new IOException( + "Error processing keystore property: " + + "keystoreURI=\"" + uri + "\"", e); + } + } + + KeyStore.ProtectionParameter keystoreProtection = null; + if (passwords.containsKey(keystoreName)) { + keystoreProtection = passwords.get(keystoreName); + + } else if (properties.containsKey(KEYSTORE_PASSWORD_ENV)) { + String env = properties.get(KEYSTORE_PASSWORD_ENV); + String pwd = System.getenv(env); + if (pwd != null) { + keystoreProtection = + new KeyStore.PasswordProtection(pwd.toCharArray()); + } else { + throw new IOException( + "Error processing keystore property: " + + "keystorePasswordEnv=\"" + env + "\""); + } + } else { + keystoreProtection = new KeyStore.PasswordProtection(null); + } + + builders.add(new KeyStoreBuilderComponents(keystoreName, + keystoreType, keystoreProvider, keystoreFile, + keystoreProtection)); + } + break; // skip other domains + } + if (builders.isEmpty()) { + throw new IOException("Error locating domain configuration data " + + "for: " + configuration); + } + + return builders; + } + +/* + * Utility class that holds the components used to construct a KeyStore.Builder + */ +class KeyStoreBuilderComponents { + String name; + String type; + Provider provider; + File file; + KeyStore.ProtectionParameter protection; + + KeyStoreBuilderComponents(String name, String type, Provider provider, + File file, KeyStore.ProtectionParameter protection) { + this.name = name; + this.type = type; + this.provider = provider; + this.file = file; + this.protection = protection; + } +} +} diff --git a/src/sun/security/provider/JavaKeyStore.java b/src/sun/security/provider/JavaKeyStore.java new file mode 100644 index 00000000..e890857c --- /dev/null +++ b/src/sun/security/provider/JavaKeyStore.java @@ -0,0 +1,802 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; +import java.util.*; +import sun.misc.IOUtils; + +import sun.security.pkcs.EncryptedPrivateKeyInfo; + +/** + * This class provides the keystore implementation referred to as "JKS". + * + * @author Jan Luehe + * @author David Brownell + * + * + * @see KeyProtector + * @see KeyStoreSpi + * @see KeyTool + * + * @since 1.2 + */ + +abstract class JavaKeyStore extends KeyStoreSpi { + + // regular JKS + public static final class JKS extends JavaKeyStore { + String convertAlias(String alias) { + return alias.toLowerCase(Locale.ENGLISH); + } + } + + // special JKS that uses case sensitive aliases + public static final class CaseExactJKS extends JavaKeyStore { + String convertAlias(String alias) { + return alias; + } + } + + private static final int MAGIC = 0xfeedfeed; + private static final int VERSION_1 = 0x01; + private static final int VERSION_2 = 0x02; + + // Private keys and their supporting certificate chains + private static class KeyEntry { + Date date; // the creation date of this entry + byte[] protectedPrivKey; + Certificate chain[]; + }; + + // Trusted certificates + private static class TrustedCertEntry { + Date date; // the creation date of this entry + Certificate cert; + }; + + /** + * Private keys and certificates are stored in a hashtable. + * Hash entries are keyed by alias names. + */ + private final Hashtable entries; + + JavaKeyStore() { + entries = new Hashtable(); + } + + // convert an alias to internal form, overridden in subclasses: + // lower case for regular JKS + // original string for CaseExactJKS + abstract String convertAlias(String alias); + + /** + * Returns the key associated with the given alias, using the given + * password to recover it. + * + * @param alias the alias name + * @param password the password for recovering the key + * + * @return the requested key, or null if the given alias does not exist + * or does not identify a key entry. + * + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * key cannot be found + * @exception UnrecoverableKeyException if the key cannot be recovered + * (e.g., the given password is wrong). + */ + public Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException + { + Object entry = entries.get(convertAlias(alias)); + + if (entry == null || !(entry instanceof KeyEntry)) { + return null; + } + if (password == null) { + throw new UnrecoverableKeyException("Password must not be null"); + } + + KeyProtector keyProtector = new KeyProtector(password); + byte[] encrBytes = ((KeyEntry)entry).protectedPrivKey; + EncryptedPrivateKeyInfo encrInfo; + byte[] plain; + try { + encrInfo = new EncryptedPrivateKeyInfo(encrBytes); + } catch (IOException ioe) { + throw new UnrecoverableKeyException("Private key not stored as " + + "PKCS #8 " + + "EncryptedPrivateKeyInfo"); + } + return keyProtector.recover(encrInfo); + } + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last), or null if the given alias + * does not exist or does not contain a certificate chain (i.e., the given + * alias identifies either a trusted certificate entry or a + * key entry without a certificate chain). + */ + public Certificate[] engineGetCertificateChain(String alias) { + Object entry = entries.get(convertAlias(alias)); + + if (entry != null && entry instanceof KeyEntry) { + if (((KeyEntry)entry).chain == null) { + return null; + } else { + return ((KeyEntry)entry).chain.clone(); + } + } else { + return null; + } + } + + /** + * Returns the certificate associated with the given alias. + * + *

    If the given alias name identifies a + * trusted certificate entry, the certificate associated with that + * entry is returned. If the given alias name identifies a + * key entry, the first element of the certificate chain of that + * entry is returned, or null if that entry does not have a certificate + * chain. + * + * @param alias the alias name + * + * @return the certificate, or null if the given alias does not exist or + * does not contain a certificate. + */ + public Certificate engineGetCertificate(String alias) { + Object entry = entries.get(convertAlias(alias)); + + if (entry != null) { + if (entry instanceof TrustedCertEntry) { + return ((TrustedCertEntry)entry).cert; + } else { + if (((KeyEntry)entry).chain == null) { + return null; + } else { + return ((KeyEntry)entry).chain[0]; + } + } + } else { + return null; + } + } + + /** + * Returns the creation date of the entry identified by the given alias. + * + * @param alias the alias name + * + * @return the creation date of this entry, or null if the given alias does + * not exist + */ + public Date engineGetCreationDate(String alias) { + Object entry = entries.get(convertAlias(alias)); + + if (entry != null) { + if (entry instanceof TrustedCertEntry) { + return new Date(((TrustedCertEntry)entry).date.getTime()); + } else { + return new Date(((KeyEntry)entry).date.getTime()); + } + } else { + return null; + } + } + + /** + * Assigns the given private key to the given alias, protecting + * it with the given password as defined in PKCS8. + * + *

    The given java.security.PrivateKey key must + * be accompanied by a certificate chain certifying the + * corresponding public key. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key and certificate + * chain. + * + * @param alias the alias name + * @param key the private key to be associated with the alias + * @param password the password to protect the key + * @param chain the certificate chain for the corresponding public + * key (only required if the given key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if the given key is not a private key, + * cannot be protected, or this operation fails for some other reason + */ + public void engineSetKeyEntry(String alias, Key key, char[] password, + Certificate[] chain) + throws KeyStoreException + { + KeyProtector keyProtector = null; + + if (!(key instanceof PrivateKey)) { + throw new KeyStoreException("Cannot store non-PrivateKeys"); + } + try { + synchronized(entries) { + KeyEntry entry = new KeyEntry(); + entry.date = new Date(); + + // Protect the encoding of the key + keyProtector = new KeyProtector(password); + entry.protectedPrivKey = keyProtector.protect(key); + + // clone the chain + if ((chain != null) && + (chain.length != 0)) { + entry.chain = chain.clone(); + } else { + entry.chain = null; + } + + entries.put(convertAlias(alias), entry); + } + } catch (NoSuchAlgorithmException nsae) { + throw new KeyStoreException("Key protection algorithm not found"); + } finally { + keyProtector = null; + } + } + + /** + * Assigns the given key (that has already been protected) to the given + * alias. + * + *

    If the protected key is of type + * java.security.PrivateKey, it must be accompanied by a + * certificate chain certifying the corresponding public key. If the + * underlying keystore implementation is of type jks, + * key must be encoded as an + * EncryptedPrivateKeyInfo as defined in the PKCS #8 standard. + * + *

    If the given alias already exists, the keystore information + * associated with it is overridden by the given key (and possibly + * certificate chain). + * + * @param alias the alias name + * @param key the key (in protected format) to be associated with the alias + * @param chain the certificate chain for the corresponding public + * key (only useful if the protected key is of type + * java.security.PrivateKey). + * + * @exception KeyStoreException if this operation fails. + */ + public void engineSetKeyEntry(String alias, byte[] key, + Certificate[] chain) + throws KeyStoreException + { + synchronized(entries) { + // key must be encoded as EncryptedPrivateKeyInfo as defined in + // PKCS#8 + try { + new EncryptedPrivateKeyInfo(key); + } catch (IOException ioe) { + throw new KeyStoreException("key is not encoded as " + + "EncryptedPrivateKeyInfo"); + } + + KeyEntry entry = new KeyEntry(); + entry.date = new Date(); + + entry.protectedPrivKey = key.clone(); + if ((chain != null) && + (chain.length != 0)) { + entry.chain = chain.clone(); + } else { + entry.chain = null; + } + + entries.put(convertAlias(alias), entry); + } + } + + /** + * Assigns the given certificate to the given alias. + * + *

    If the given alias already exists in this keystore and identifies a + * trusted certificate entry, the certificate associated with it is + * overridden by the given certificate. + * + * @param alias the alias name + * @param cert the certificate + * + * @exception KeyStoreException if the given alias already exists and does + * not identify a trusted certificate entry, or this operation + * fails for some other reason. + */ + public void engineSetCertificateEntry(String alias, Certificate cert) + throws KeyStoreException + { + synchronized(entries) { + + Object entry = entries.get(convertAlias(alias)); + if ((entry != null) && (entry instanceof KeyEntry)) { + throw new KeyStoreException + ("Cannot overwrite own certificate"); + } + + TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); + trustedCertEntry.cert = cert; + trustedCertEntry.date = new Date(); + entries.put(convertAlias(alias), trustedCertEntry); + } + } + + /** + * Deletes the entry identified by the given alias from this keystore. + * + * @param alias the alias name + * + * @exception KeyStoreException if the entry cannot be removed. + */ + public void engineDeleteEntry(String alias) + throws KeyStoreException + { + synchronized(entries) { + entries.remove(convertAlias(alias)); + } + } + + /** + * Lists all the alias names of this keystore. + * + * @return enumeration of the alias names + */ + public Enumeration engineAliases() { + return entries.keys(); + } + + /** + * Checks if the given alias exists in this keystore. + * + * @param alias the alias name + * + * @return true if the alias exists, false otherwise + */ + public boolean engineContainsAlias(String alias) { + return entries.containsKey(convertAlias(alias)); + } + + /** + * Retrieves the number of entries in this keystore. + * + * @return the number of entries in this keystore + */ + public int engineSize() { + return entries.size(); + } + + /** + * Returns true if the entry identified by the given alias is a + * key entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * key entry, false otherwise. + */ + public boolean engineIsKeyEntry(String alias) { + Object entry = entries.get(convertAlias(alias)); + if ((entry != null) && (entry instanceof KeyEntry)) { + return true; + } else { + return false; + } + } + + /** + * Returns true if the entry identified by the given alias is a + * trusted certificate entry, and false otherwise. + * + * @return true if the entry identified by the given alias is a + * trusted certificate entry, false otherwise. + */ + public boolean engineIsCertificateEntry(String alias) { + Object entry = entries.get(convertAlias(alias)); + if ((entry != null) && (entry instanceof TrustedCertEntry)) { + return true; + } else { + return false; + } + } + + /** + * Returns the (alias) name of the first keystore entry whose certificate + * matches the given certificate. + * + *

    This method attempts to match the given certificate with each + * keystore entry. If the entry being considered + * is a trusted certificate entry, the given certificate is + * compared to that entry's certificate. If the entry being considered is + * a key entry, the given certificate is compared to the first + * element of that entry's certificate chain (if a chain exists). + * + * @param cert the certificate to match with. + * + * @return the (alias) name of the first entry with matching certificate, + * or null if no such entry exists in this keystore. + */ + public String engineGetCertificateAlias(Certificate cert) { + Certificate certElem; + + for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { + String alias = e.nextElement(); + Object entry = entries.get(alias); + if (entry instanceof TrustedCertEntry) { + certElem = ((TrustedCertEntry)entry).cert; + } else if (((KeyEntry)entry).chain != null) { + certElem = ((KeyEntry)entry).chain[0]; + } else { + continue; + } + if (certElem.equals(cert)) { + return alias; + } + } + return null; + } + + /** + * Stores this keystore to the given output stream, and protects its + * integrity with the given password. + * + * @param stream the output stream to which this keystore is written. + * @param password the password to generate the keystore integrity check + * + * @exception IOException if there was an I/O problem with data + * @exception NoSuchAlgorithmException if the appropriate data integrity + * algorithm could not be found + * @exception CertificateException if any of the certificates included in + * the keystore data could not be stored + */ + public void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + synchronized(entries) { + /* + * KEYSTORE FORMAT: + * + * Magic number (big-endian integer), + * Version of this file format (big-endian integer), + * + * Count (big-endian integer), + * followed by "count" instances of either: + * + * { + * tag=1 (big-endian integer), + * alias (UTF string) + * timestamp + * encrypted private-key info according to PKCS #8 + * (integer length followed by encoding) + * cert chain (integer count, then certs; for each cert, + * integer length followed by encoding) + * } + * + * or: + * + * { + * tag=2 (big-endian integer) + * alias (UTF string) + * timestamp + * cert (integer length followed by encoding) + * } + * + * ended by a keyed SHA1 hash (bytes only) of + * { password + whitener + preceding body } + */ + + // password is mandatory when storing + if (password == null) { + throw new IllegalArgumentException("password can't be null"); + } + + byte[] encoded; // the certificate encoding + + MessageDigest md = getPreKeyedHash(password); + DataOutputStream dos + = new DataOutputStream(new DigestOutputStream(stream, md)); + + dos.writeInt(MAGIC); + // always write the latest version + dos.writeInt(VERSION_2); + + dos.writeInt(entries.size()); + + for (Enumeration e = entries.keys(); e.hasMoreElements();) { + + String alias = e.nextElement(); + Object entry = entries.get(alias); + + if (entry instanceof KeyEntry) { + + // Store this entry as a KeyEntry + dos.writeInt(1); + + // Write the alias + dos.writeUTF(alias); + + // Write the (entry creation) date + dos.writeLong(((KeyEntry)entry).date.getTime()); + + // Write the protected private key + dos.writeInt(((KeyEntry)entry).protectedPrivKey.length); + dos.write(((KeyEntry)entry).protectedPrivKey); + + // Write the certificate chain + int chainLen; + if (((KeyEntry)entry).chain == null) { + chainLen = 0; + } else { + chainLen = ((KeyEntry)entry).chain.length; + } + dos.writeInt(chainLen); + for (int i = 0; i < chainLen; i++) { + encoded = ((KeyEntry)entry).chain[i].getEncoded(); + dos.writeUTF(((KeyEntry)entry).chain[i].getType()); + dos.writeInt(encoded.length); + dos.write(encoded); + } + } else { + + // Store this entry as a certificate + dos.writeInt(2); + + // Write the alias + dos.writeUTF(alias); + + // Write the (entry creation) date + dos.writeLong(((TrustedCertEntry)entry).date.getTime()); + + // Write the trusted certificate + encoded = ((TrustedCertEntry)entry).cert.getEncoded(); + dos.writeUTF(((TrustedCertEntry)entry).cert.getType()); + dos.writeInt(encoded.length); + dos.write(encoded); + } + } + + /* + * Write the keyed hash which is used to detect tampering with + * the keystore (such as deleting or modifying key or + * certificate entries). + */ + byte digest[] = md.digest(); + + dos.write(digest); + dos.flush(); + } + } + + /** + * Loads the keystore from the given input stream. + * + *

    If a password is given, it is used to check the integrity of the + * keystore data. Otherwise, the integrity of the keystore is not checked. + * + * @param stream the input stream from which the keystore is loaded + * @param password the (optional) password used to check the integrity of + * the keystore. + * + * @exception IOException if there is an I/O or format problem with the + * keystore data + * @exception NoSuchAlgorithmException if the algorithm used to check + * the integrity of the keystore cannot be found + * @exception CertificateException if any of the certificates in the + * keystore could not be loaded + */ + public void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException + { + synchronized(entries) { + DataInputStream dis; + MessageDigest md = null; + CertificateFactory cf = null; + Hashtable cfs = null; + ByteArrayInputStream bais = null; + byte[] encoded = null; + + if (stream == null) + return; + + if (password != null) { + md = getPreKeyedHash(password); + dis = new DataInputStream(new DigestInputStream(stream, md)); + } else { + dis = new DataInputStream(stream); + } + + // Body format: see store method + + int xMagic = dis.readInt(); + int xVersion = dis.readInt(); + + if (xMagic!=MAGIC || + (xVersion!=VERSION_1 && xVersion!=VERSION_2)) { + throw new IOException("Invalid keystore format"); + } + + if (xVersion == VERSION_1) { + cf = CertificateFactory.getInstance("X509"); + } else { + // version 2 + cfs = new Hashtable(3); + } + + entries.clear(); + int count = dis.readInt(); + + for (int i = 0; i < count; i++) { + int tag; + String alias; + + tag = dis.readInt(); + + if (tag == 1) { // private key entry + + KeyEntry entry = new KeyEntry(); + + // Read the alias + alias = dis.readUTF(); + + // Read the (entry creation) date + entry.date = new Date(dis.readLong()); + + // Read the private key + entry.protectedPrivKey = + IOUtils.readFully(dis, dis.readInt(), true); + + // Read the certificate chain + int numOfCerts = dis.readInt(); + if (numOfCerts > 0) { + List certs = new ArrayList<>( + numOfCerts > 10 ? 10 : numOfCerts); + for (int j = 0; j < numOfCerts; j++) { + if (xVersion == 2) { + // read the certificate type, and instantiate a + // certificate factory of that type (reuse + // existing factory if possible) + String certType = dis.readUTF(); + if (cfs.containsKey(certType)) { + // reuse certificate factory + cf = cfs.get(certType); + } else { + // create new certificate factory + cf = CertificateFactory.getInstance(certType); + // store the certificate factory so we can + // reuse it later + cfs.put(certType, cf); + } + } + // instantiate the certificate + encoded = IOUtils.readFully(dis, dis.readInt(), true); + bais = new ByteArrayInputStream(encoded); + certs.add(cf.generateCertificate(bais)); + bais.close(); + } + // We can be sure now that numOfCerts of certs are read + entry.chain = certs.toArray(new Certificate[numOfCerts]); + } + + // Add the entry to the list + entries.put(alias, entry); + + } else if (tag == 2) { // trusted certificate entry + + TrustedCertEntry entry = new TrustedCertEntry(); + + // Read the alias + alias = dis.readUTF(); + + // Read the (entry creation) date + entry.date = new Date(dis.readLong()); + + // Read the trusted certificate + if (xVersion == 2) { + // read the certificate type, and instantiate a + // certificate factory of that type (reuse + // existing factory if possible) + String certType = dis.readUTF(); + if (cfs.containsKey(certType)) { + // reuse certificate factory + cf = cfs.get(certType); + } else { + // create new certificate factory + cf = CertificateFactory.getInstance(certType); + // store the certificate factory so we can + // reuse it later + cfs.put(certType, cf); + } + } + encoded = IOUtils.readFully(dis, dis.readInt(), true); + bais = new ByteArrayInputStream(encoded); + entry.cert = cf.generateCertificate(bais); + bais.close(); + + // Add the entry to the list + entries.put(alias, entry); + + } else { + throw new IOException("Unrecognized keystore entry"); + } + } + + /* + * If a password has been provided, we check the keyed digest + * at the end. If this check fails, the store has been tampered + * with + */ + if (password != null) { + byte computed[], actual[]; + computed = md.digest(); + actual = new byte[computed.length]; + dis.readFully(actual); + for (int i = 0; i < computed.length; i++) { + if (computed[i] != actual[i]) { + Throwable t = new UnrecoverableKeyException + ("Password verification failed"); + throw (IOException)new IOException + ("Keystore was tampered with, or " + + "password was incorrect").initCause(t); + } + } + } + } + } + + /** + * To guard against tampering with the keystore, we append a keyed + * hash with a bit of whitener. + */ + private MessageDigest getPreKeyedHash(char[] password) + throws NoSuchAlgorithmException, UnsupportedEncodingException + { + int i, j; + + MessageDigest md = MessageDigest.getInstance("SHA"); + byte[] passwdBytes = new byte[password.length * 2]; + for (i=0, j=0; i> 8); + passwdBytes[j++] = (byte)password[i]; + } + md.update(passwdBytes); + for (i=0; iThe password is expected to be in printable ASCII. + * Normal rules for good password selection apply: at least + * seven characters, mixed case, with punctuation encouraged. + * Phrases or words which are easily guessed, for example by + * being found in dictionaries, are bad. + */ + public KeyProtector(char[] password) + throws NoSuchAlgorithmException + { + int i, j; + + if (password == null) { + throw new IllegalArgumentException("password can't be null"); + } + md = MessageDigest.getInstance(DIGEST_ALG); + // Convert password to byte array, so that it can be digested + passwdBytes = new byte[password.length * 2]; + for (i=0, j=0; i> 8); + passwdBytes[j++] = (byte)password[i]; + } + } + + /** + * Ensures that the password bytes of this key protector are + * set to zero when there are no more references to it. + */ + protected void finalize() { + if (passwdBytes != null) { + Arrays.fill(passwdBytes, (byte)0x00); + passwdBytes = null; + } + } + + /* + * Protects the given plaintext key, using the password provided at + * construction time. + */ + public byte[] protect(Key key) throws KeyStoreException + { + int i; + int numRounds; + byte[] digest; + int xorOffset; // offset in xorKey where next digest will be stored + int encrKeyOffset = 0; + + if (key == null) { + throw new IllegalArgumentException("plaintext key can't be null"); + } + + if (!"PKCS#8".equalsIgnoreCase(key.getFormat())) { + throw new KeyStoreException( + "Cannot get key bytes, not PKCS#8 encoded"); + } + + byte[] plainKey = key.getEncoded(); + if (plainKey == null) { + throw new KeyStoreException( + "Cannot get key bytes, encoding not supported"); + } + + // Determine the number of digest rounds + numRounds = plainKey.length / DIGEST_LEN; + if ((plainKey.length % DIGEST_LEN) != 0) + numRounds++; + + // Create a random salt + byte[] salt = new byte[SALT_LEN]; + SecureRandom random = new SecureRandom(); + random.nextBytes(salt); + + // Set up the byte array which will be XORed with "plainKey" + byte[] xorKey = new byte[plainKey.length]; + + // Compute the digests, and store them in "xorKey" + for (i = 0, xorOffset = 0, digest = salt; + i < numRounds; + i++, xorOffset += DIGEST_LEN) { + md.update(passwdBytes); + md.update(digest); + digest = md.digest(); + md.reset(); + // Copy the digest into "xorKey" + if (i < numRounds - 1) { + System.arraycopy(digest, 0, xorKey, xorOffset, + digest.length); + } else { + System.arraycopy(digest, 0, xorKey, xorOffset, + xorKey.length - xorOffset); + } + } + + // XOR "plainKey" with "xorKey", and store the result in "tmpKey" + byte[] tmpKey = new byte[plainKey.length]; + for (i = 0; i < tmpKey.length; i++) { + tmpKey[i] = (byte)(plainKey[i] ^ xorKey[i]); + } + + // Store salt and "tmpKey" in "encrKey" + byte[] encrKey = new byte[salt.length + tmpKey.length + DIGEST_LEN]; + System.arraycopy(salt, 0, encrKey, encrKeyOffset, salt.length); + encrKeyOffset += salt.length; + System.arraycopy(tmpKey, 0, encrKey, encrKeyOffset, tmpKey.length); + encrKeyOffset += tmpKey.length; + + // Append digest(password, plainKey) as an integrity check to "encrKey" + md.update(passwdBytes); + Arrays.fill(passwdBytes, (byte)0x00); + passwdBytes = null; + md.update(plainKey); + digest = md.digest(); + md.reset(); + System.arraycopy(digest, 0, encrKey, encrKeyOffset, digest.length); + + // wrap the protected private key in a PKCS#8-style + // EncryptedPrivateKeyInfo, and returns its encoding + AlgorithmId encrAlg; + try { + encrAlg = new AlgorithmId(new ObjectIdentifier(KEY_PROTECTOR_OID)); + return new EncryptedPrivateKeyInfo(encrAlg,encrKey).getEncoded(); + } catch (IOException ioe) { + throw new KeyStoreException(ioe.getMessage()); + } + } + + /* + * Recovers the plaintext version of the given key (in protected format), + * using the password provided at construction time. + */ + public Key recover(EncryptedPrivateKeyInfo encrInfo) + throws UnrecoverableKeyException + { + int i; + byte[] digest; + int numRounds; + int xorOffset; // offset in xorKey where next digest will be stored + int encrKeyLen; // the length of the encrpyted key + + // do we support the algorithm? + AlgorithmId encrAlg = encrInfo.getAlgorithm(); + if (!(encrAlg.getOID().toString().equals(KEY_PROTECTOR_OID))) { + throw new UnrecoverableKeyException("Unsupported key protection " + + "algorithm"); + } + + byte[] protectedKey = encrInfo.getEncryptedData(); + + /* + * Get the salt associated with this key (the first SALT_LEN bytes of + * protectedKey) + */ + byte[] salt = new byte[SALT_LEN]; + System.arraycopy(protectedKey, 0, salt, 0, SALT_LEN); + + // Determine the number of digest rounds + encrKeyLen = protectedKey.length - SALT_LEN - DIGEST_LEN; + numRounds = encrKeyLen / DIGEST_LEN; + if ((encrKeyLen % DIGEST_LEN) != 0) numRounds++; + + // Get the encrypted key portion and store it in "encrKey" + byte[] encrKey = new byte[encrKeyLen]; + System.arraycopy(protectedKey, SALT_LEN, encrKey, 0, encrKeyLen); + + // Set up the byte array which will be XORed with "encrKey" + byte[] xorKey = new byte[encrKey.length]; + + // Compute the digests, and store them in "xorKey" + for (i = 0, xorOffset = 0, digest = salt; + i < numRounds; + i++, xorOffset += DIGEST_LEN) { + md.update(passwdBytes); + md.update(digest); + digest = md.digest(); + md.reset(); + // Copy the digest into "xorKey" + if (i < numRounds - 1) { + System.arraycopy(digest, 0, xorKey, xorOffset, + digest.length); + } else { + System.arraycopy(digest, 0, xorKey, xorOffset, + xorKey.length - xorOffset); + } + } + + // XOR "encrKey" with "xorKey", and store the result in "plainKey" + byte[] plainKey = new byte[encrKey.length]; + for (i = 0; i < plainKey.length; i++) { + plainKey[i] = (byte)(encrKey[i] ^ xorKey[i]); + } + + /* + * Check the integrity of the recovered key by concatenating it with + * the password, digesting the concatenation, and comparing the + * result of the digest operation with the digest provided at the end + * of protectedKey. If the two digest values are + * different, throw an exception. + */ + md.update(passwdBytes); + Arrays.fill(passwdBytes, (byte)0x00); + passwdBytes = null; + md.update(plainKey); + digest = md.digest(); + md.reset(); + for (i = 0; i < digest.length; i++) { + if (digest[i] != protectedKey[SALT_LEN + encrKeyLen + i]) { + throw new UnrecoverableKeyException("Cannot recover key"); + } + } + + // The parseKey() method of PKCS8Key parses the key + // algorithm and instantiates the appropriate key factory, + // which in turn parses the key material. + try { + return PKCS8Key.parseKey(new DerValue(plainKey)); + } catch (IOException ioe) { + throw new UnrecoverableKeyException(ioe.getMessage()); + } + } +} diff --git a/src/sun/security/provider/MD2.java b/src/sun/security/provider/MD2.java new file mode 100644 index 00000000..b14c1127 --- /dev/null +++ b/src/sun/security/provider/MD2.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.util.Arrays; + +/** + * Implementation for the MD2 algorithm, see RFC1319. It is very slow and + * not particular secure. It is only supported to be able to verify + * RSA/Verisign root certificates signed using MD2withRSA. It should not + * be used for anything else. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class MD2 extends DigestBase { + + // state, 48 ints + private int[] X; + + // checksum, 16 ints. they are really bytes, but byte arithmetic in + // the JVM is much slower that int arithmetic. + private int[] C; + + // temporary store for checksum C during final digest + private byte[] cBytes; + + /** + * Create a new MD2 digest. Called by the JCA framework + */ + public MD2() { + super("MD2", 16, 16); + X = new int[48]; + C = new int[16]; + cBytes = new byte[16]; + } + + public Object clone() throws CloneNotSupportedException { + MD2 copy = (MD2) super.clone(); + copy.X = copy.X.clone(); + copy.C = copy.C.clone(); + copy.cBytes = new byte[16]; + return copy; + } + + // reset state and checksum + void implReset() { + Arrays.fill(X, 0); + Arrays.fill(C, 0); + } + + // finish the digest + void implDigest(byte[] out, int ofs) { + int padValue = 16 - ((int)bytesProcessed & 15); + engineUpdate(PADDING[padValue], 0, padValue); + for (int i = 0; i < 16; i++) { + cBytes[i] = (byte)C[i]; + } + implCompress(cBytes, 0); + for (int i = 0; i < 16; i++) { + out[ofs + i] = (byte)X[i]; + } + } + + // one iteration of the compression function + void implCompress(byte[] b, int ofs) { + for (int i = 0; i < 16; i++) { + int k = b[ofs + i] & 0xff; + X[16 + i] = k; + X[32 + i] = k ^ X[i]; + } + + // update the checksum + int t = C[15]; + for (int i = 0; i < 16; i++) { + t = (C[i] ^= S[X[16 + i] ^ t]); + } + + t = 0; + for (int i = 0; i < 18; i++) { + for (int j = 0; j < 48; j++) { + t = (X[j] ^= S[t]); + } + t = (t + i) & 0xff; + } + } + + // substitution table derived from Pi. Copied from the RFC. + private final static int[] S = new int[] { + 41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, + 19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, + 76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, + 138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, + 245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, + 148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, + 39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, + 181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, + 150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157, + 112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27, + 96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15, + 85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197, + 234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65, + 129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123, + 8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233, + 203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228, + 166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237, + 31, 26, 219, 153, 141, 51, 159, 17, 131, 20, + }; + + // digest padding. 17 element array. + // padding[0] is null + // padding[i] is an array of i time the byte value i (i = 1..16) + private final static byte[][] PADDING; + + static { + PADDING = new byte[17][]; + for (int i = 1; i < 17; i++) { + byte[] b = new byte[i]; + Arrays.fill(b, (byte)i); + PADDING[i] = b; + } + } + +} diff --git a/src/sun/security/provider/MD4.java b/src/sun/security/provider/MD4.java new file mode 100644 index 00000000..346bc9db --- /dev/null +++ b/src/sun/security/provider/MD4.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.*; + +import static sun.security.provider.ByteArrayAccess.*; + +/** + * The MD4 class is used to compute an MD4 message digest over a given + * buffer of bytes. It is an implementation of the RSA Data Security Inc + * MD4 algorithim as described in internet RFC 1320. + * + *

    The MD4 algorithm is very weak and should not be used unless it is + * unavoidable. Therefore, it is not registered in our standard providers. To + * obtain an implementation, call the static getInstance() method in this + * class. + * + * @author Andreas Sterbenz + */ +public final class MD4 extends DigestBase { + + // state of this object + private int[] state; + // temporary buffer, used by implCompress() + private int[] x; + + // rotation constants + private static final int S11 = 3; + private static final int S12 = 7; + private static final int S13 = 11; + private static final int S14 = 19; + private static final int S21 = 3; + private static final int S22 = 5; + private static final int S23 = 9; + private static final int S24 = 13; + private static final int S31 = 3; + private static final int S32 = 9; + private static final int S33 = 11; + private static final int S34 = 15; + + private final static Provider md4Provider; + + static { + md4Provider = new Provider("MD4Provider", 1.8d, "MD4 MessageDigest") { + private static final long serialVersionUID = -8850464997518327965L; + }; + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + md4Provider.put("MessageDigest.MD4", "sun.security.provider.MD4"); + return null; + } + }); + } + + public static MessageDigest getInstance() { + try { + return MessageDigest.getInstance("MD4", md4Provider); + } catch (NoSuchAlgorithmException e) { + // should never occur + throw new ProviderException(e); + } + } + + // Standard constructor, creates a new MD4 instance. + public MD4() { + super("MD4", 16, 64); + state = new int[4]; + x = new int[16]; + implReset(); + } + + // clone this object + public Object clone() throws CloneNotSupportedException { + MD4 copy = (MD4) super.clone(); + copy.state = copy.state.clone(); + copy.x = new int[16]; + return copy; + } + + /** + * Reset the state of this object. + */ + void implReset() { + // Load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + } + + /** + * Perform the final computations, any buffered bytes are added + * to the digest, the count is added to the digest, and the resulting + * digest is stored. + */ + void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x3f; + int padLen = (index < 56) ? (56 - index) : (120 - index); + engineUpdate(padding, 0, padLen); + + i2bLittle4((int)bitsProcessed, buffer, 56); + i2bLittle4((int)(bitsProcessed >>> 32), buffer, 60); + implCompress(buffer, 0); + + i2bLittle(state, 0, out, ofs, 16); + } + + private static int FF(int a, int b, int c, int d, int x, int s) { + a += ((b & c) | ((~b) & d)) + x; + return ((a << s) | (a >>> (32 - s))); + } + + private static int GG(int a, int b, int c, int d, int x, int s) { + a += ((b & c) | (b & d) | (c & d)) + x + 0x5a827999; + return ((a << s) | (a >>> (32 - s))); + } + + private static int HH(int a, int b, int c, int d, int x, int s) { + a += ((b ^ c) ^ d) + x + 0x6ed9eba1; + return ((a << s) | (a >>> (32 - s))); + } + + /** + * This is where the functions come together as the generic MD4 + * transformation operation. It consumes sixteen + * bytes from the buffer, beginning at the specified offset. + */ + void implCompress(byte[] buf, int ofs) { + b2iLittle64(buf, ofs, x); + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + + /* Round 1 */ + a = FF (a, b, c, d, x[ 0], S11); /* 1 */ + d = FF (d, a, b, c, x[ 1], S12); /* 2 */ + c = FF (c, d, a, b, x[ 2], S13); /* 3 */ + b = FF (b, c, d, a, x[ 3], S14); /* 4 */ + a = FF (a, b, c, d, x[ 4], S11); /* 5 */ + d = FF (d, a, b, c, x[ 5], S12); /* 6 */ + c = FF (c, d, a, b, x[ 6], S13); /* 7 */ + b = FF (b, c, d, a, x[ 7], S14); /* 8 */ + a = FF (a, b, c, d, x[ 8], S11); /* 9 */ + d = FF (d, a, b, c, x[ 9], S12); /* 10 */ + c = FF (c, d, a, b, x[10], S13); /* 11 */ + b = FF (b, c, d, a, x[11], S14); /* 12 */ + a = FF (a, b, c, d, x[12], S11); /* 13 */ + d = FF (d, a, b, c, x[13], S12); /* 14 */ + c = FF (c, d, a, b, x[14], S13); /* 15 */ + b = FF (b, c, d, a, x[15], S14); /* 16 */ + + /* Round 2 */ + a = GG (a, b, c, d, x[ 0], S21); /* 17 */ + d = GG (d, a, b, c, x[ 4], S22); /* 18 */ + c = GG (c, d, a, b, x[ 8], S23); /* 19 */ + b = GG (b, c, d, a, x[12], S24); /* 20 */ + a = GG (a, b, c, d, x[ 1], S21); /* 21 */ + d = GG (d, a, b, c, x[ 5], S22); /* 22 */ + c = GG (c, d, a, b, x[ 9], S23); /* 23 */ + b = GG (b, c, d, a, x[13], S24); /* 24 */ + a = GG (a, b, c, d, x[ 2], S21); /* 25 */ + d = GG (d, a, b, c, x[ 6], S22); /* 26 */ + c = GG (c, d, a, b, x[10], S23); /* 27 */ + b = GG (b, c, d, a, x[14], S24); /* 28 */ + a = GG (a, b, c, d, x[ 3], S21); /* 29 */ + d = GG (d, a, b, c, x[ 7], S22); /* 30 */ + c = GG (c, d, a, b, x[11], S23); /* 31 */ + b = GG (b, c, d, a, x[15], S24); /* 32 */ + + /* Round 3 */ + a = HH (a, b, c, d, x[ 0], S31); /* 33 */ + d = HH (d, a, b, c, x[ 8], S32); /* 34 */ + c = HH (c, d, a, b, x[ 4], S33); /* 35 */ + b = HH (b, c, d, a, x[12], S34); /* 36 */ + a = HH (a, b, c, d, x[ 2], S31); /* 37 */ + d = HH (d, a, b, c, x[10], S32); /* 38 */ + c = HH (c, d, a, b, x[ 6], S33); /* 39 */ + b = HH (b, c, d, a, x[14], S34); /* 40 */ + a = HH (a, b, c, d, x[ 1], S31); /* 41 */ + d = HH (d, a, b, c, x[ 9], S32); /* 42 */ + c = HH (c, d, a, b, x[ 5], S33); /* 43 */ + b = HH (b, c, d, a, x[13], S34); /* 44 */ + a = HH (a, b, c, d, x[ 3], S31); /* 45 */ + d = HH (d, a, b, c, x[11], S32); /* 46 */ + c = HH (c, d, a, b, x[ 7], S33); /* 47 */ + b = HH (b, c, d, a, x[15], S34); /* 48 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + } + +} diff --git a/src/sun/security/provider/MD5.java b/src/sun/security/provider/MD5.java new file mode 100644 index 00000000..c9747de8 --- /dev/null +++ b/src/sun/security/provider/MD5.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import static sun.security.provider.ByteArrayAccess.*; + +/** + * The MD5 class is used to compute an MD5 message digest over a given + * buffer of bytes. It is an implementation of the RSA Data Security Inc + * MD5 algorithim as described in internet RFC 1321. + * + * @author Chuck McManis + * @author Benjamin Renaud + * @author Andreas Sterbenz + */ +public final class MD5 extends DigestBase { + + // state of this object + private int[] state; + // temporary buffer, used by implCompress() + private int[] x; + + // rotation constants + private static final int S11 = 7; + private static final int S12 = 12; + private static final int S13 = 17; + private static final int S14 = 22; + private static final int S21 = 5; + private static final int S22 = 9; + private static final int S23 = 14; + private static final int S24 = 20; + private static final int S31 = 4; + private static final int S32 = 11; + private static final int S33 = 16; + private static final int S34 = 23; + private static final int S41 = 6; + private static final int S42 = 10; + private static final int S43 = 15; + private static final int S44 = 21; + + // Standard constructor, creates a new MD5 instance. + public MD5() { + super("MD5", 16, 64); + state = new int[4]; + x = new int[16]; + implReset(); + } + + // clone this object + public Object clone() throws CloneNotSupportedException { + MD5 copy = (MD5) super.clone(); + copy.state = copy.state.clone(); + copy.x = new int[16]; + return copy; + } + + /** + * Reset the state of this object. + */ + void implReset() { + // Load magic initialization constants. + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + } + + /** + * Perform the final computations, any buffered bytes are added + * to the digest, the count is added to the digest, and the resulting + * digest is stored. + */ + void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x3f; + int padLen = (index < 56) ? (56 - index) : (120 - index); + engineUpdate(padding, 0, padLen); + + i2bLittle4((int)bitsProcessed, buffer, 56); + i2bLittle4((int)(bitsProcessed >>> 32), buffer, 60); + implCompress(buffer, 0); + + i2bLittle(state, 0, out, ofs, 16); + } + + /* ********************************************************** + * The MD5 Functions. The results of this + * implementation were checked against the RSADSI version. + * ********************************************************** + */ + + private static int FF(int a, int b, int c, int d, int x, int s, int ac) { + a += ((b & c) | ((~b) & d)) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int GG(int a, int b, int c, int d, int x, int s, int ac) { + a += ((b & d) | (c & (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int HH(int a, int b, int c, int d, int x, int s, int ac) { + a += ((b ^ c) ^ d) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + private static int II(int a, int b, int c, int d, int x, int s, int ac) { + a += (c ^ (b | (~d))) + x + ac; + return ((a << s) | (a >>> (32 - s))) + b; + } + + /** + * This is where the functions come together as the generic MD5 + * transformation operation. It consumes sixteen + * bytes from the buffer, beginning at the specified offset. + */ + void implCompress(byte[] buf, int ofs) { + b2iLittle64(buf, ofs, x); + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + + /* Round 1 */ + a = FF ( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + d = FF ( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + c = FF ( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + b = FF ( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + a = FF ( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + d = FF ( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + c = FF ( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + b = FF ( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + a = FF ( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + d = FF ( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + c = FF ( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + b = FF ( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + a = FF ( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + d = FF ( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + c = FF ( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + b = FF ( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG ( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + d = GG ( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + c = GG ( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + b = GG ( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + a = GG ( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + d = GG ( d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + c = GG ( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + b = GG ( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + a = GG ( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + d = GG ( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + c = GG ( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + b = GG ( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + a = GG ( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + d = GG ( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + c = GG ( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + b = GG ( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH ( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + d = HH ( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + c = HH ( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + b = HH ( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + a = HH ( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + d = HH ( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + c = HH ( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + b = HH ( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + a = HH ( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + d = HH ( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + c = HH ( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + b = HH ( b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + a = HH ( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + d = HH ( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + c = HH ( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + b = HH ( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II ( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + d = II ( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + c = II ( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + b = II ( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + a = II ( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + d = II ( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + c = II ( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + b = II ( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + a = II ( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + d = II ( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + c = II ( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + b = II ( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + a = II ( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + d = II ( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + c = II ( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + b = II ( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + } + +} diff --git a/src/sun/security/provider/ParameterCache.java b/src/sun/security/provider/ParameterCache.java new file mode 100644 index 00000000..0edff492 --- /dev/null +++ b/src/sun/security/provider/ParameterCache.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.math.BigInteger; + +import java.security.*; +import java.security.SecureRandom; +import java.security.spec.*; + +import javax.crypto.spec.DHParameterSpec; + +/** + * Cache for DSA and DH parameter specs. Used by the KeyPairGenerators + * in the Sun, SunJCE, and SunPKCS11 provider if no parameters have been + * explicitly specified by the application. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +public final class ParameterCache { + + private ParameterCache() { + // empty + } + + // cache of DSA parameters + private final static Map dsaCache; + + // cache of DH parameters + private final static Map dhCache; + + /** + * Return cached DSA parameters for the given length combination of + * prime and subprime, or null if none are available in the cache. + */ + public static DSAParameterSpec getCachedDSAParameterSpec(int primeLen, + int subprimeLen) { + // ensure the sum is unique in all cases, i.e. + // case#1: (512 <= p <= 1024) AND q=160 + // case#2: p=2048 AND q=224 + // case#3: p=2048 AND q=256 + // (NOT-YET-SUPPORTED)case#4: p=3072 AND q=256 + return dsaCache.get(Integer.valueOf(primeLen+subprimeLen)); + } + + /** + * Return cached DH parameters for the given keylength, or null if none + * are available in the cache. + */ + public static DHParameterSpec getCachedDHParameterSpec(int keyLength) { + return dhCache.get(Integer.valueOf(keyLength)); + } + + /** + * Return DSA parameters for the given primeLen. Uses cache if + * possible, generates new parameters and adds them to the cache + * otherwise. + */ + public static DSAParameterSpec getDSAParameterSpec(int primeLen, + SecureRandom random) + throws NoSuchAlgorithmException, InvalidParameterSpecException, + InvalidAlgorithmParameterException { + if (primeLen <= 1024) { + return getDSAParameterSpec(primeLen, 160, random); + } else if (primeLen == 2048) { + return getDSAParameterSpec(primeLen, 224, random); + } else { + return null; + } + } + + /** + * Return DSA parameters for the given primeLen and subprimeLen. + * Uses cache if possible, generates new parameters and adds them to the + * cache otherwise. + */ + public static DSAParameterSpec getDSAParameterSpec(int primeLen, + int subprimeLen, SecureRandom random) + throws NoSuchAlgorithmException, InvalidParameterSpecException, + InvalidAlgorithmParameterException { + DSAParameterSpec spec = + getCachedDSAParameterSpec(primeLen, subprimeLen); + if (spec != null) { + return spec; + } + spec = getNewDSAParameterSpec(primeLen, subprimeLen, random); + dsaCache.put(Integer.valueOf(primeLen + subprimeLen), spec); + return spec; + } + + /** + * Return DH parameters for the given keylength. Uses cache if possible, + * generates new parameters and adds them to the cache otherwise. + */ + public static DHParameterSpec getDHParameterSpec(int keyLength, + SecureRandom random) + throws NoSuchAlgorithmException, InvalidParameterSpecException { + DHParameterSpec spec = getCachedDHParameterSpec(keyLength); + if (spec != null) { + return spec; + } + AlgorithmParameterGenerator gen = + AlgorithmParameterGenerator.getInstance("DH"); + gen.init(keyLength, random); + AlgorithmParameters params = gen.generateParameters(); + spec = params.getParameterSpec(DHParameterSpec.class); + dhCache.put(Integer.valueOf(keyLength), spec); + return spec; + } + + /** + * Return new DSA parameters for the given length combination of prime and + * sub prime. Do not lookup in cache and do not cache the newly generated + * parameters. This method really only exists for the legacy method + * DSAKeyPairGenerator.initialize(int, boolean, SecureRandom). + */ + public static DSAParameterSpec getNewDSAParameterSpec(int primeLen, + int subprimeLen, SecureRandom random) + throws NoSuchAlgorithmException, InvalidParameterSpecException, + InvalidAlgorithmParameterException { + AlgorithmParameterGenerator gen = + AlgorithmParameterGenerator.getInstance("DSA"); + // Use init(int size, SecureRandom random) for legacy DSA key sizes + if (primeLen < 1024) { + gen.init(primeLen, random); + } else { + DSAGenParameterSpec genParams = + new DSAGenParameterSpec(primeLen, subprimeLen); + gen.init(genParams, random); + } + AlgorithmParameters params = gen.generateParameters(); + DSAParameterSpec spec = params.getParameterSpec(DSAParameterSpec.class); + return spec; + } + + static { + dhCache = new ConcurrentHashMap(); + dsaCache = new ConcurrentHashMap(); + + /* + * We support precomputed parameter for legacy 512, 768 bit moduli, + * and (L, N) combinations of (1024, 160), (2048, 224), (2048, 256). + * In this file we provide both the seed and counter + * value of the generation process for each of these seeds, + * for validation purposes. We also include the test vectors + * from the DSA specification, FIPS 186, and the FIPS 186 + * Change No 1, which updates the test vector using SHA-1 + * instead of SHA (for both the G function and the message + * hash. + */ + + /* + * L = 512 + * SEED = b869c82b35d70e1b1ff91b28e37a62ecdc34409b + * counter = 123 + */ + BigInteger p512 = + new BigInteger("fca682ce8e12caba26efccf7110e526db078b05edecb" + + "cd1eb4a208f3ae1617ae01f35b91a47e6df63413c5e1" + + "2ed0899bcd132acd50d99151bdc43ee737592e17", 16); + + BigInteger q512 = + new BigInteger("962eddcc369cba8ebb260ee6b6a126d9346e38c5", 16); + + BigInteger g512 = + new BigInteger("678471b27a9cf44ee91a49c5147db1a9aaf244f05a43" + + "4d6486931d2d14271b9e35030b71fd73da179069b32e" + + "2935630e1c2062354d0da20a6c416e50be794ca4", 16); + + /* + * L = 768 + * SEED = 77d0f8c4dad15eb8c4f2f8d6726cefd96d5bb399 + * counter = 263 + */ + BigInteger p768 = + new BigInteger("e9e642599d355f37c97ffd3567120b8e25c9cd43e" + + "927b3a9670fbec5d890141922d2c3b3ad24800937" + + "99869d1e846aab49fab0ad26d2ce6a22219d470bc" + + "e7d777d4a21fbe9c270b57f607002f3cef8393694" + + "cf45ee3688c11a8c56ab127a3daf", 16); + + BigInteger q768 = + new BigInteger("9cdbd84c9f1ac2f38d0f80f42ab952e7338bf511", + 16); + + BigInteger g768 = + new BigInteger("30470ad5a005fb14ce2d9dcd87e38bc7d1b1c5fac" + + "baecbe95f190aa7a31d23c4dbbcbe06174544401a" + + "5b2c020965d8c2bd2171d3668445771f74ba084d2" + + "029d83c1c158547f3a9f1a2715be23d51ae4d3e5a" + + "1f6a7064f316933a346d3f529252", 16); + + + /* + * L = 1024 + * SEED = 8d5155894229d5e689ee01e6018a237e2cae64cd + * counter = 92 + */ + BigInteger p1024 = + new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523c" + + "ef4400c31e3f80b6512669455d402251fb593d8d58" + + "fabfc5f5ba30f6cb9b556cd7813b801d346ff26660" + + "b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c6" + + "1bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554" + + "135a169132f675f3ae2b61d72aeff22203199dd148" + + "01c7", 16); + + BigInteger q1024 = + new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", + 16); + + BigInteger g1024 = + new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa" + + "3aea82f9574c0b3d0782675159578ebad4594fe671" + + "07108180b449167123e84c281613b7cf09328cc8a6" + + "e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f" + + "0bfa213562f1fb627a01243bcca4f1bea8519089a8" + + "83dfe15ae59f06928b665e807b552564014c3bfecf" + + "492a", 16); + + dsaCache.put(Integer.valueOf(512+160), + new DSAParameterSpec(p512, q512, g512)); + dsaCache.put(Integer.valueOf(768+160), + new DSAParameterSpec(p768, q768, g768)); + dsaCache.put(Integer.valueOf(1024+160), + new DSAParameterSpec(p1024, q1024, g1024)); + /* + * L = 2048, N = 224 + * SEED = 584236080cfa43c09b02354135f4cc5198a19efada08bd866d601ba4 + * counter = 2666 + */ + BigInteger p2048_224 = + new BigInteger("8f7935d9b9aae9bfabed887acf4951b6f32ec59e3b" + + "af3718e8eac4961f3efd3606e74351a9c4183339b8" + + "09e7c2ae1c539ba7475b85d011adb8b47987754984" + + "695cac0e8f14b3360828a22ffa27110a3d62a99345" + + "3409a0fe696c4658f84bdd20819c3709a01057b195" + + "adcd00233dba5484b6291f9d648ef883448677979c" + + "ec04b434a6ac2e75e9985de23db0292fc1118c9ffa" + + "9d8181e7338db792b730d7b9e349592f6809987215" + + "3915ea3d6b8b4653c633458f803b32a4c2e0f27290" + + "256e4e3f8a3b0838a1c450e4e18c1a29a37ddf5ea1" + + "43de4b66ff04903ed5cf1623e158d487c608e97f21" + + "1cd81dca23cb6e380765f822e342be484c05763939" + + "601cd667", 16); + + BigInteger q2048_224 = + new BigInteger("baf696a68578f7dfdee7fa67c977c785ef32b233ba" + + "e580c0bcd5695d", 16); + + BigInteger g2048_224 = + new BigInteger("16a65c58204850704e7502a39757040d34da3a3478" + + "c154d4e4a5c02d242ee04f96e61e4bd0904abdac8f" + + "37eeb1e09f3182d23c9043cb642f88004160edf9ca" + + "09b32076a79c32a627f2473e91879ba2c4e744bd20" + + "81544cb55b802c368d1fa83ed489e94e0fa0688e32" + + "428a5c78c478c68d0527b71c9a3abb0b0be12c4468" + + "9639e7d3ce74db101a65aa2b87f64c6826db3ec72f" + + "4b5599834bb4edb02f7c90e9a496d3a55d535bebfc" + + "45d4f619f63f3dedbb873925c2f224e07731296da8" + + "87ec1e4748f87efb5fdeb75484316b2232dee553dd" + + "af02112b0d1f02da30973224fe27aeda8b9d4b2922" + + "d9ba8be39ed9e103a63c52810bc688b7e2ed4316e1" + + "ef17dbde", 16); + dsaCache.put(Integer.valueOf(2048+224), + new DSAParameterSpec(p2048_224, q2048_224, g2048_224)); + + /* + * L = 2048, N = 256 + * SEED = b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536 + * counter = 497 + */ + BigInteger p2048_256 = + new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c721" + + "0313bb45fb4d5bb2e5fe1cbd678cd4bbdd84c9836b" + + "e1f31c0777725aeb6c2fc38b85f48076fa76bcd814" + + "6cc89a6fb2f706dd719898c2083dc8d896f84062e2" + + "c9c94d137b054a8d8096adb8d51952398eeca852a0" + + "af12df83e475aa65d4ec0c38a9560d5661186ff98b" + + "9fc9eb60eee8b030376b236bc73be3acdbd74fd61c" + + "1d2475fa3077b8f080467881ff7e1ca56fee066d79" + + "506ade51edbb5443a563927dbc4ba520086746175c" + + "8885925ebc64c6147906773496990cb714ec667304" + + "e261faee33b3cbdf008e0c3fa90650d97d3909c927" + + "5bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" + + "a406cb0b", 16); + + BigInteger q2048_256 = + new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43" + + "ca0ec572e37e09939a9773", 16); + + BigInteger g2048_256 = + new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b" + + "889a74aaf53174aa0fbe7e3c5b8fcd7a53bef563b0" + + "e98560328960a9517f4014d3325fc7962bf1e04937" + + "0d76d1314a76137e792f3f0db859d095e4a5b93202" + + "4f079ecf2ef09c797452b0770e1350782ed57ddf79" + + "4979dcef23cb96f183061965c4ebc93c9c71c56b92" + + "5955a75f94cccf1449ac43d586d0beee43251b0b22" + + "87349d68de0d144403f13e802f4146d882e057af19" + + "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f" + + "695e347d8d1cf9ac819a26ca9b04cb0eb9b7b03598" + + "8d15bbac65212a55239cfc7e58fae38d7250ab9991" + + "ffbc97134025fe8ce04c4399ad96569be91a546f49" + + "78693c7a", 16); + dsaCache.put(Integer.valueOf(2048+256), + new DSAParameterSpec(p2048_256, q2048_256, g2048_256)); + + // use DSA parameters for DH as well + dhCache.put(Integer.valueOf(512), new DHParameterSpec(p512, g512)); + dhCache.put(Integer.valueOf(768), new DHParameterSpec(p768, g768)); + dhCache.put(Integer.valueOf(1024), new DHParameterSpec(p1024, g1024)); + dhCache.put(Integer.valueOf(2048), new DHParameterSpec(p2048_224, g2048_224)); + } + +} diff --git a/src/sun/security/provider/PolicyFile.java b/src/sun/security/provider/PolicyFile.java new file mode 100644 index 00000000..70741525 --- /dev/null +++ b/src/sun/security/provider/PolicyFile.java @@ -0,0 +1,2345 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.lang.reflect.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URI; +import java.util.*; +import java.text.MessageFormat; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import javax.security.auth.Subject; +import javax.security.auth.x500.X500Principal; +import java.io.FilePermission; +import java.net.SocketPermission; +import java.net.NetPermission; +import java.util.concurrent.atomic.AtomicReference; +import sun.misc.JavaSecurityProtectionDomainAccess; +import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; +import sun.misc.SharedSecrets; +import sun.security.util.PolicyUtil; +import sun.security.util.PropertyExpander; +import sun.security.util.Debug; +import sun.security.util.ResourcesMgr; +import sun.security.util.SecurityConstants; +import sun.net.www.ParseUtil; + +/** + * This class represents a default implementation for + * java.security.Policy. + * + * Note: + * For backward compatibility with JAAS 1.0 it loads + * both java.auth.policy and java.policy. However it + * is recommended that java.auth.policy be not used + * and the java.policy contain all grant entries including + * that contain principal-based entries. + * + * + *

    This object stores the policy for entire Java runtime, + * and is the amalgamation of multiple static policy + * configurations that resides in files. + * The algorithm for locating the policy file(s) and reading their + * information into this Policy object is: + * + *

      + *
    1. + * Loop through the java.security.Security properties, + * policy.url.1, policy.url.2, ..., + * policy.url.X" and + * auth.policy.url.1, auth.policy.url.2, ..., + * auth.policy.url.X". These properties are set + * in the Java security properties file, which is located in the file named + * <JAVA_HOME>/lib/security/java.security. + * <JAVA_HOME> refers to the value of the java.home system property, + * and specifies the directory where the JRE is installed. + * Each property value specifies a URL pointing to a + * policy file to be loaded. Read in and load each policy. + * + * auth.policy.url is supported only for backward compatibility. + * + *
    2. + * The java.lang.System property java.security.policy + * may also be set to a URL pointing to another policy file + * (which is the case when a user uses the -D switch at runtime). + * If this property is defined, and its use is allowed by the + * security property file (the Security property, + * policy.allowSystemProperty is set to true), + * also load that policy. + * + *
    3. + * The java.lang.System property + * java.security.auth.policy may also be set to a + * URL pointing to another policy file + * (which is the case when a user uses the -D switch at runtime). + * If this property is defined, and its use is allowed by the + * security property file (the Security property, + * policy.allowSystemProperty is set to true), + * also load that policy. + * + * java.security.auth.policy is supported only for backward + * compatibility. + * + * If the java.security.policy or + * java.security.auth.policy property is defined using + * "==" (rather than "="), then ignore all other specified + * policies and only load this policy. + *
    + * + * Each policy file consists of one or more grant entries, each of + * which consists of a number of permission entries. + * + *
    + *   grant signedBy "alias", codeBase "URL",
    + *         principal principalClass "principalName",
    + *         principal principalClass "principalName",
    + *         ... {
    + *
    + *     permission Type "name "action",
    + *         signedBy "alias";
    + *     permission Type "name "action",
    + *         signedBy "alias";
    + *     ....
    + *   };
    + * 
    + * + * All non-bold items above must appear as is (although case + * doesn't matter and some are optional, as noted below). + * principal entries are optional and need not be present. + * Italicized items represent variable values. + * + *

    A grant entry must begin with the word grant. + * The signedBy,codeBase and principal + * name/value pairs are optional. + * If they are not present, then any signer (including unsigned code) + * will match, and any codeBase will match. + * Note that the principalClass + * may be set to the wildcard value, *, which allows it to match + * any Principal class. In addition, the principalName + * may also be set to the wildcard value, *, allowing it to match + * any Principal name. When setting the principalName + * to the *, do not surround the * with quotes. + * + *

    A permission entry must begin with the word permission. + * The word Type in the template above is + * a specific permission type, such as java.io.FilePermission + * or java.lang.RuntimePermission. + * + *

    The "action" is required for + * many permission types, such as java.io.FilePermission + * (where it specifies what type of file access that is permitted). + * It is not required for categories such as + * java.lang.RuntimePermission + * where it is not necessary - you either have the + * permission specified by the "name" + * value following the type name or you don't. + * + *

    The signedBy name/value pair for a permission entry + * is optional. If present, it indicates a signed permission. That is, + * the permission class itself must be signed by the given alias in + * order for it to be granted. For example, + * suppose you have the following grant entry: + * + *

    + *   grant principal foo.com.Principal "Duke" {
    + *     permission Foo "foobar", signedBy "FooSoft";
    + *   }
    + * 
    + * + *

    Then this permission of type Foo is granted if the + * Foo.class permission has been signed by the + * "FooSoft" alias, or if XXX Foo.class is a + * system class (i.e., is found on the CLASSPATH). + * + * + *

    Items that appear in an entry must appear in the specified order + * (permission, Type, "name", and + * "action"). An entry is terminated with a semicolon. + * + *

    Case is unimportant for the identifiers (permission, + * signedBy, codeBase, etc.) but is + * significant for the Type + * or for any string that is passed in as a value.

    + * + *

    An example of two entries in a policy configuration file is + *

    + *   // if the code is comes from "foo.com" and is running as "Duke",
    + *   // grant it read/write to all files in /tmp.
    + *
    + *   grant codeBase "foo.com", principal foo.com.Principal "Duke" {
    + *              permission java.io.FilePermission "/tmp/*", "read,write";
    + *   };
    + *
    + *   // grant any code running as "Duke" permission to read
    + *   // the "java.vendor" Property.
    + *
    + *   grant principal foo.com.Principal "Duke" {
    + *         permission java.util.PropertyPermission "java.vendor";
    + *
    + *
    + * 
    + * This Policy implementation supports special handling of any + * permission that contains the string, "${{self}}", as part of + * its target name. When such a permission is evaluated + * (such as during a security check), ${{self}} is replaced + * with one or more Principal class/name pairs. The exact + * replacement performed depends upon the contents of the + * grant clause to which the permission belongs. + *

    + * + * If the grant clause does not contain any principal information, + * the permission will be ignored (permissions containing + * ${{self}} in their target names are only valid in the context + * of a principal-based grant clause). For example, BarPermission + * will always be ignored in the following grant clause: + * + *

    + *    grant codebase "www.foo.com", signedby "duke" {
    + *      permission BarPermission "... ${{self}} ...";
    + *    };
    + *
    + * + * If the grant clause contains principal information, ${{self}} + * will be replaced with that same principal information. + * For example, ${{self}} in BarPermission will be replaced by + * javax.security.auth.x500.X500Principal "cn=Duke" + * in the following grant clause: + * + *
    + *    grant principal javax.security.auth.x500.X500Principal "cn=Duke" {
    + *      permission BarPermission "... ${{self}} ...";
    + *    };
    + *  
    + * + * If there is a comma-separated list of principals in the grant + * clause, then ${{self}} will be replaced by the same + * comma-separated list or principals. + * In the case where both the principal class and name are + * wildcarded in the grant clause, ${{self}} is replaced + * with all the principals associated with the Subject + * in the current AccessControlContext. + * + * + *

    For PrivateCredentialPermissions, you can also use "self" + * instead of "${{self}}". However the use of "self" is + * deprecated in favour of "${{self}}". + * + * @see CodeSource + * @see Permissions + * @see ProtectionDomain + */ +public class PolicyFile extends Policy { + + private static final Debug debug = Debug.getInstance("policy"); + + private static final String NONE = "NONE"; + private static final String P11KEYSTORE = "PKCS11"; + + private static final String SELF = "${{self}}"; + private static final String X500PRINCIPAL = + "javax.security.auth.x500.X500Principal"; + private static final String POLICY = "java.security.policy"; + private static final String SECURITY_MANAGER = "java.security.manager"; + private static final String POLICY_URL = "policy.url."; + private static final String AUTH_POLICY = "java.security.auth.policy"; + private static final String AUTH_POLICY_URL = "auth.policy.url."; + + private static final int DEFAULT_CACHE_SIZE = 1; + + // contains the policy grant entries, PD cache, and alias mapping + private AtomicReference policyInfo = new AtomicReference<>(); + private boolean constructed = false; + + private boolean expandProperties = true; + private boolean ignoreIdentityScope = true; + private boolean allowSystemProperties = true; + private boolean notUtf8 = false; + private URL url; + + // for use with the reflection API + + private static final Class[] PARAMS0 = { }; + private static final Class[] PARAMS1 = { String.class }; + private static final Class[] PARAMS2 = { String.class, String.class }; + + /** + * Initializes the Policy object and reads the default policy + * configuration file(s) into the Policy object. + */ + public PolicyFile() { + init((URL)null); + } + + /** + * Initializes the Policy object and reads the default policy + * from the specified URL only. + */ + public PolicyFile(URL url) { + this.url = url; + init(url); + } + + /** + * Initializes the Policy object and reads the default policy + * configuration file(s) into the Policy object. + * + * The algorithm for locating the policy file(s) and reading their + * information into the Policy object is: + *

    +     *   loop through the Security Properties named "policy.url.1",
    +     *  ""policy.url.2", "auth.policy.url.1",  "auth.policy.url.2" etc, until
    +     *   you don't find one. Each of these specify a policy file.
    +     *
    +     *   if none of these could be loaded, use a builtin static policy
    +     *      equivalent to the default lib/security/java.policy file.
    +     *
    +     *   if the system property "java.policy" or "java.auth.policy" is defined
    +     * (which is the
    +     *      case when the user uses the -D switch at runtime), and
    +     *     its use is allowed by the security property file,
    +     *     also load it.
    +     * 
    + * + * Each policy file consists of one or more grant entries, each of + * which consists of a number of permission entries. + *
    +     *   grant signedBy "alias", codeBase "URL" {
    +     *     permission Type "name", "action",
    +     *         signedBy "alias";
    +     *     ....
    +     *     permission Type "name", "action",
    +     *         signedBy "alias";
    +     *   };
    +     *
    +     * 
    + * + * All non-italicized items above must appear as is (although case + * doesn't matter and some are optional, as noted below). + * Italicized items represent variable values. + * + *

    A grant entry must begin with the word grant. + * The signedBy and codeBase name/value + * pairs are optional. + * If they are not present, then any signer (including unsigned code) + * will match, and any codeBase will match. + * + *

    A permission entry must begin with the word permission. + * The word Type in the template above would actually + * be a specific permission type, such as + * java.io.FilePermission or + * java.lang.RuntimePermission. + * + *

    The "action" is required for + * many permission types, such as java.io.FilePermission + * (where it specifies what type of file access is permitted). + * It is not required for categories such as + * java.lang.RuntimePermission + * where it is not necessary - you either have the + * permission specified by the "name" + * value following the type name or you don't. + * + *

    The signedBy name/value pair for a permission entry + * is optional. If present, it indicates a signed permission. That is, + * the permission class itself must be signed by the given alias in + * order for it to be granted. For example, + * suppose you have the following grant entry: + * + *

    +     *   grant {
    +     *     permission Foo "foobar", signedBy "FooSoft";
    +     *   }
    +     * 
    + * + *

    Then this permission of type Foo is granted if the + * Foo.class permission has been signed by the + * "FooSoft" alias, or if Foo.class is a + * system class (i.e., is found on the CLASSPATH). + * + *

    Items that appear in an entry must appear in the specified order + * (permission, Type, "name", and + * "action"). An entry is terminated with a semicolon. + * + *

    Case is unimportant for the identifiers (permission, + * signedBy, codeBase, etc.) but is + * significant for the Type + * or for any string that is passed in as a value.

    + * + *

    An example of two entries in a policy configuration file is + *

    +     *   //  if the code is signed by "Duke", grant it read/write to all
    +     *   // files in /tmp.
    +     *
    +     *   grant signedBy "Duke" {
    +     *          permission java.io.FilePermission "/tmp/*", "read,write";
    +     *   };
    +     * 

    + * // grant everyone the following permission + * + * grant { + * permission java.util.PropertyPermission "java.vendor"; + * }; + *

    + */ + private void init(URL url) { + // Properties are set once for each init(); ignore changes between + // between diff invocations of initPolicyFile(policy, url, info). + String numCacheStr = + AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + expandProperties = "true".equalsIgnoreCase + (Security.getProperty("policy.expandProperties")); + ignoreIdentityScope = "true".equalsIgnoreCase + (Security.getProperty("policy.ignoreIdentityScope")); + allowSystemProperties = "true".equalsIgnoreCase + (Security.getProperty("policy.allowSystemProperty")); + notUtf8 = "false".equalsIgnoreCase + (System.getProperty("sun.security.policy.utf8")); + return System.getProperty("sun.security.policy.numcaches"); + }}); + + int numCaches; + if (numCacheStr != null) { + try { + numCaches = Integer.parseInt(numCacheStr); + } catch (NumberFormatException e) { + numCaches = DEFAULT_CACHE_SIZE; + } + } else { + numCaches = DEFAULT_CACHE_SIZE; + } + // System.out.println("number caches=" + numCaches); + PolicyInfo newInfo = new PolicyInfo(numCaches); + initPolicyFile(newInfo, url); + policyInfo.set(newInfo); + } + + private void initPolicyFile(final PolicyInfo newInfo, final URL url) { + + if (url != null) { + + /** + * If the caller specified a URL via Policy.getInstance, + * we only read from that URL + */ + + if (debug != null) { + debug.println("reading "+url); + } + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + if (init(url, newInfo) == false) { + // use static policy if all else fails + initStaticPolicy(newInfo); + } + return null; + } + }); + + } else { + + /** + * Caller did not specify URL via Policy.getInstance. + * Read from URLs listed in the java.security properties file. + * + * We call initPolicyFile with POLICY , POLICY_URL and then + * call it with AUTH_POLICY and AUTH_POLICY_URL + * So first we will process the JAVA standard policy + * and then process the JAVA AUTH Policy. + * This is for backward compatibility as well as to handle + * cases where the user has a single unified policyfile + * with both java policy entries and auth entries + */ + + boolean loaded_one = initPolicyFile(POLICY, POLICY_URL, newInfo); + // To maintain strict backward compatibility + // we load the static policy only if POLICY load failed + if (!loaded_one) { + // use static policy if all else fails + initStaticPolicy(newInfo); + } + + initPolicyFile(AUTH_POLICY, AUTH_POLICY_URL, newInfo); + } + } + + private boolean initPolicyFile(final String propname, final String urlname, + final PolicyInfo newInfo) { + Boolean loadedPolicy = + AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + boolean loaded_policy = false; + + if (allowSystemProperties) { + String extra_policy = System.getProperty(propname); + if (extra_policy != null) { + boolean overrideAll = false; + if (extra_policy.startsWith("=")) { + overrideAll = true; + extra_policy = extra_policy.substring(1); + } + try { + extra_policy = + PropertyExpander.expand(extra_policy); + URL policyURL; + + File policyFile = new File(extra_policy); + if (policyFile.exists()) { + policyURL = ParseUtil.fileToEncodedURL + (new File(policyFile.getCanonicalPath())); + } else { + policyURL = new URL(extra_policy); + } + if (debug != null) + debug.println("reading "+policyURL); + if (init(policyURL, newInfo)) + loaded_policy = true; + } catch (Exception e) { + // ignore. + if (debug != null) { + debug.println("caught exception: "+e); + } + } + if (overrideAll) { + if (debug != null) { + debug.println("overriding other policies!"); + } + return Boolean.valueOf(loaded_policy); + } + } + } + + int n = 1; + String policy_uri; + + while ((policy_uri = Security.getProperty(urlname+n)) != null) { + try { + URL policy_url = null; + String expanded_uri = PropertyExpander.expand + (policy_uri).replace(File.separatorChar, '/'); + + if (policy_uri.startsWith("file:${java.home}/") || + policy_uri.startsWith("file:${user.home}/")) { + + // this special case accommodates + // the situation java.home/user.home + // expand to a single slash, resulting in + // a file://foo URI + policy_url = new File + (expanded_uri.substring(5)).toURI().toURL(); + } else { + policy_url = new URI(expanded_uri).toURL(); + } + + if (debug != null) + debug.println("reading "+policy_url); + if (init(policy_url, newInfo)) + loaded_policy = true; + } catch (Exception e) { + if (debug != null) { + debug.println("error reading policy "+e); + e.printStackTrace(); + } + // ignore that policy + } + n++; + } + return Boolean.valueOf(loaded_policy); + } + }); + + return loadedPolicy.booleanValue(); + } + + /** + * Reads a policy configuration into the Policy object using a + * Reader object. + * + * @param policyFile the policy Reader object. + */ + private boolean init(URL policy, PolicyInfo newInfo) { + boolean success = false; + PolicyParser pp = new PolicyParser(expandProperties); + InputStreamReader isr = null; + try { + + // read in policy using UTF-8 by default + // + // check non-standard system property to see if + // the default encoding should be used instead + + if (notUtf8) { + isr = new InputStreamReader + (PolicyUtil.getInputStream(policy)); + } else { + isr = new InputStreamReader + (PolicyUtil.getInputStream(policy), "UTF-8"); + } + + pp.read(isr); + + KeyStore keyStore = null; + try { + keyStore = PolicyUtil.getKeyStore + (policy, + pp.getKeyStoreUrl(), + pp.getKeyStoreType(), + pp.getKeyStoreProvider(), + pp.getStorePassURL(), + debug); + } catch (Exception e) { + // ignore, treat it like we have no keystore + if (debug != null) { + e.printStackTrace(); + } + } + + Enumeration enum_ = pp.grantElements(); + while (enum_.hasMoreElements()) { + PolicyParser.GrantEntry ge = enum_.nextElement(); + addGrantEntry(ge, keyStore, newInfo); + } + } catch (PolicyParser.ParsingException pe) { + MessageFormat form = new MessageFormat(ResourcesMgr.getString + (POLICY + ".error.parsing.policy.message")); + Object[] source = {policy, pe.getLocalizedMessage()}; + System.err.println(form.format(source)); + if (debug != null) + pe.printStackTrace(); + + } catch (Exception e) { + if (debug != null) { + debug.println("error parsing "+policy); + debug.println(e.toString()); + e.printStackTrace(); + } + } finally { + if (isr != null) { + try { + isr.close(); + success = true; + } catch (IOException e) { + // ignore the exception + } + } else { + success = true; + } + } + + return success; + } + + private void initStaticPolicy(final PolicyInfo newInfo) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + PolicyEntry pe = new PolicyEntry(new CodeSource(null, + (Certificate[]) null)); + pe.add(SecurityConstants.LOCAL_LISTEN_PERMISSION); + pe.add(new PropertyPermission("java.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.vendor", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.vendor.url", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.class.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("os.name", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("os.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("os.arch", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("file.separator", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("path.separator", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("line.separator", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.specification.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.specification.vendor", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.specification.name", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.vm.specification.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.vm.specification.vendor", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission + ("java.vm.specification.name", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.vm.version", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.vm.vendor", + SecurityConstants.PROPERTY_READ_ACTION)); + pe.add(new PropertyPermission("java.vm.name", + SecurityConstants.PROPERTY_READ_ACTION)); + + // No need to sync because noone has access to newInfo yet + newInfo.policyEntries.add(pe); + + // Add AllPermissions for standard extensions + String[] extCodebases = PolicyParser.parseExtDirs( + PolicyParser.EXTDIRS_EXPANSION, 0); + if (extCodebases != null && extCodebases.length > 0) { + for (int i = 0; i < extCodebases.length; i++) { + try { + pe = new PolicyEntry(canonicalizeCodebase( + new CodeSource(new URL(extCodebases[i]), + (Certificate[]) null), false )); + pe.add(SecurityConstants.ALL_PERMISSION); + + // No need to sync because noone has access to + // newInfo yet + newInfo.policyEntries.add(pe); + } catch (Exception e) { + // this is probably bad (though not dangerous). + // What should we do? + } + } + } + return null; + } + }); + } + + /** + * Given a GrantEntry, create a codeSource. + * + * @return null if signedBy alias is not recognized + */ + private CodeSource getCodeSource(PolicyParser.GrantEntry ge, KeyStore keyStore, + PolicyInfo newInfo) throws MalformedURLException + { + Certificate[] certs = null; + if (ge.signedBy != null) { + certs = getCertificates(keyStore, ge.signedBy, newInfo); + if (certs == null) { + // we don't have a key for this alias, + // just return + if (debug != null) { + debug.println(" -- No certs for alias '" + + ge.signedBy + "' - ignoring entry"); + } + return null; + } + } + + URL location; + + if (ge.codeBase != null) + location = new URL(ge.codeBase); + else + location = null; + + return (canonicalizeCodebase(new CodeSource(location, certs),false)); + } + + /** + * Add one policy entry to the list. + */ + private void addGrantEntry(PolicyParser.GrantEntry ge, + KeyStore keyStore, PolicyInfo newInfo) { + + if (debug != null) { + debug.println("Adding policy entry: "); + debug.println(" signedBy " + ge.signedBy); + debug.println(" codeBase " + ge.codeBase); + if (ge.principals != null) { + for (PolicyParser.PrincipalEntry pppe : ge.principals) { + debug.println(" " + pppe.toString()); + } + } + } + + try { + CodeSource codesource = getCodeSource(ge, keyStore, newInfo); + // skip if signedBy alias was unknown... + if (codesource == null) return; + + // perform keystore alias principal replacement. + // for example, if alias resolves to X509 certificate, + // replace principal with: + // -- skip if alias is unknown + if (replacePrincipals(ge.principals, keyStore) == false) + return; + PolicyEntry entry = new PolicyEntry(codesource, ge.principals); + Enumeration enum_ = + ge.permissionElements(); + while (enum_.hasMoreElements()) { + PolicyParser.PermissionEntry pe = enum_.nextElement(); + + try { + // perform ${{ ... }} expansions within permission name + expandPermissionName(pe, keyStore); + + // XXX special case PrivateCredentialPermission-SELF + Permission perm; + if (pe.permission.equals + ("javax.security.auth.PrivateCredentialPermission") && + pe.name.endsWith(" self")) { + pe.name = pe.name.substring(0, pe.name.indexOf("self")) + + SELF; + } + // check for self + if (pe.name != null && pe.name.indexOf(SELF) != -1) { + // Create a "SelfPermission" , it could be an + // an unresolved permission which will be resolved + // when implies is called + // Add it to entry + Certificate certs[]; + if (pe.signedBy != null) { + certs = getCertificates(keyStore, + pe.signedBy, + newInfo); + } else { + certs = null; + } + perm = new SelfPermission(pe.permission, + pe.name, + pe.action, + certs); + } else { + perm = getInstance(pe.permission, + pe.name, + pe.action); + } + entry.add(perm); + if (debug != null) { + debug.println(" "+perm); + } + } catch (ClassNotFoundException cnfe) { + Certificate certs[]; + if (pe.signedBy != null) { + certs = getCertificates(keyStore, + pe.signedBy, + newInfo); + } else { + certs = null; + } + + // only add if we had no signer or we had a + // a signer and found the keys for it. + if (certs != null || pe.signedBy == null) { + Permission perm = new UnresolvedPermission( + pe.permission, + pe.name, + pe.action, + certs); + entry.add(perm); + if (debug != null) { + debug.println(" "+perm); + } + } + } catch (InvocationTargetException ite) { + MessageFormat form = new MessageFormat + (ResourcesMgr.getString + (POLICY + + ".error.adding.Permission.perm.message")); + Object[] source = {pe.permission, + ite.getTargetException().toString()}; + System.err.println(form.format(source)); + } catch (Exception e) { + MessageFormat form = new MessageFormat + (ResourcesMgr.getString + (POLICY + + ".error.adding.Permission.perm.message")); + Object[] source = {pe.permission, + e.toString()}; + System.err.println(form.format(source)); + } + } + + // No need to sync because noone has access to newInfo yet + newInfo.policyEntries.add(entry); + } catch (Exception e) { + MessageFormat form = new MessageFormat(ResourcesMgr.getString + (POLICY + + ".error.adding.Entry.message")); + Object[] source = {e.toString()}; + System.err.println(form.format(source)); + } + if (debug != null) + debug.println(); + } + + /** + * Returns a new Permission object of the given Type. The Permission is + * created by getting the + * Class object using the Class.forName method, and using + * the reflection API to invoke the (String name, String actions) + * constructor on the + * object. + * + * @param type the type of Permission being created. + * @param name the name of the Permission being created. + * @param actions the actions of the Permission being created. + * + * @exception ClassNotFoundException if the particular Permission + * class could not be found. + * + * @exception IllegalAccessException if the class or initializer is + * not accessible. + * + * @exception InstantiationException if getInstance tries to + * instantiate an abstract class or an interface, or if the + * instantiation fails for some other reason. + * + * @exception NoSuchMethodException if the (String, String) constructor + * is not found. + * + * @exception InvocationTargetException if the underlying Permission + * constructor throws an exception. + * + */ + + private static final Permission getInstance(String type, + String name, + String actions) + throws ClassNotFoundException, + InstantiationException, + IllegalAccessException, + NoSuchMethodException, + InvocationTargetException + { + //XXX we might want to keep a hash of created factories... + Class pc = Class.forName(type, false, null); + Permission answer = getKnownInstance(pc, name, actions); + if (answer != null) { + return answer; + } + if (!Permission.class.isAssignableFrom(pc)) { + // not the right subtype + throw new ClassCastException(type + " is not a Permission"); + } + + if (name == null && actions == null) { + try { + Constructor c = pc.getConstructor(PARAMS0); + return (Permission) c.newInstance(new Object[] {}); + } catch (NoSuchMethodException ne) { + try { + Constructor c = pc.getConstructor(PARAMS1); + return (Permission) c.newInstance( + new Object[] { name}); + } catch (NoSuchMethodException ne1 ) { + Constructor c = pc.getConstructor(PARAMS2); + return (Permission) c.newInstance( + new Object[] { name, actions }); + } + } + } else { + if (name != null && actions == null) { + try { + Constructor c = pc.getConstructor(PARAMS1); + return (Permission) c.newInstance(new Object[] { name}); + } catch (NoSuchMethodException ne) { + Constructor c = pc.getConstructor(PARAMS2); + return (Permission) c.newInstance( + new Object[] { name, actions }); + } + } else { + Constructor c = pc.getConstructor(PARAMS2); + return (Permission) c.newInstance( + new Object[] { name, actions }); + } + } + } + + /** + * Creates one of the well-known permissions directly instead of + * via reflection. Keep list short to not penalize non-JDK-defined + * permissions. + */ + private static final Permission getKnownInstance(Class claz, + String name, String actions) { + if (claz.equals(FilePermission.class)) { + return new FilePermission(name, actions); + } else if (claz.equals(SocketPermission.class)) { + return new SocketPermission(name, actions); + } else if (claz.equals(RuntimePermission.class)) { + return new RuntimePermission(name, actions); + } else if (claz.equals(PropertyPermission.class)) { + return new PropertyPermission(name, actions); + } else if (claz.equals(NetPermission.class)) { + return new NetPermission(name, actions); + } else if (claz.equals(AllPermission.class)) { + return SecurityConstants.ALL_PERMISSION; + } else { + return null; + } + } + + /** + * Fetch all certs associated with this alias. + */ + private Certificate[] getCertificates + (KeyStore keyStore, String aliases, PolicyInfo newInfo) { + + List vcerts = null; + + StringTokenizer st = new StringTokenizer(aliases, ","); + int n = 0; + + while (st.hasMoreTokens()) { + String alias = st.nextToken().trim(); + n++; + Certificate cert = null; + // See if this alias's cert has already been cached + synchronized (newInfo.aliasMapping) { + cert = (Certificate)newInfo.aliasMapping.get(alias); + + if (cert == null && keyStore != null) { + + try { + cert = keyStore.getCertificate(alias); + } catch (KeyStoreException kse) { + // never happens, because keystore has already been loaded + // when we call this + } + if (cert != null) { + newInfo.aliasMapping.put(alias, cert); + newInfo.aliasMapping.put(cert, alias); + } + } + } + + if (cert != null) { + if (vcerts == null) + vcerts = new ArrayList<>(); + vcerts.add(cert); + } + } + + // make sure n == vcerts.size, since we are doing a logical *and* + if (vcerts != null && n == vcerts.size()) { + Certificate[] certs = new Certificate[vcerts.size()]; + vcerts.toArray(certs); + return certs; + } else { + return null; + } + } + + /** + * Refreshes the policy object by re-reading all the policy files. + */ + @Override public void refresh() { + init(url); + } + + /** + * Evaluates the the global policy for the permissions granted to + * the ProtectionDomain and tests whether the permission is + * granted. + * + * @param domain the ProtectionDomain to test + * @param permission the Permission object to be tested for implication. + * + * @return true if "permission" is a proper subset of a permission + * granted to this ProtectionDomain. + * + * @see ProtectionDomain + */ + @Override + public boolean implies(ProtectionDomain pd, Permission p) { + PolicyInfo pi = policyInfo.get(); + ProtectionDomainCache pdMap = pi.getPdMapping(); + + PermissionCollection pc = pdMap.get(pd); + + if (pc != null) { + return pc.implies(p); + } + + pc = getPermissions(pd); + if (pc == null) { + return false; + } + + // cache mapping of protection domain to its PermissionCollection + pdMap.put(pd, pc); + return pc.implies(p); + } + + /** + * Examines this Policy and returns the permissions granted + * to the specified ProtectionDomain. This includes + * the permissions currently associated with the domain as well + * as the policy permissions granted to the domain's + * CodeSource, ClassLoader, and Principals. + * + *

    Note that this Policy implementation has + * special handling for PrivateCredentialPermissions. + * When this method encounters a PrivateCredentialPermission + * which specifies "self" as the Principal class and name, + * it does not add that Permission to the returned + * PermissionCollection. Instead, it builds + * a new PrivateCredentialPermission + * for each Principal associated with the provided + * Subject. Each new PrivateCredentialPermission + * contains the same Credential class as specified in the + * originally granted permission, as well as the Class and name + * for the respective Principal. + * + *

    + * + * @param domain the Permissions granted to this + * ProtectionDomain are returned. + * + * @return the Permissions granted to the provided + * ProtectionDomain. + */ + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + Permissions perms = new Permissions(); + + if (domain == null) + return perms; + + // first get policy perms + getPermissions(perms, domain); + + // add static perms + // - adding static perms after policy perms is necessary + // to avoid a regression for 4301064 + PermissionCollection pc = domain.getPermissions(); + if (pc != null) { + synchronized (pc) { + Enumeration e = pc.elements(); + while (e.hasMoreElements()) { + perms.add(e.nextElement()); + } + } + } + + return perms; + } + + /** + * Examines this Policy and creates a PermissionCollection object with + * the set of permissions for the specified CodeSource. + * + * @param CodeSource the codesource associated with the caller. + * This encapsulates the original location of the code (where the code + * came from) and the public key(s) of its signer. + * + * @return the set of permissions according to the policy. + */ + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + return getPermissions(new Permissions(), codesource); + } + + /** + * Examines the global policy and returns the provided Permissions + * object with additional permissions granted to the specified + * ProtectionDomain. + * + * @param perm the Permissions to populate + * @param pd the ProtectionDomain associated with the caller. + * + * @return the set of Permissions according to the policy. + */ + private PermissionCollection getPermissions(Permissions perms, + ProtectionDomain pd ) { + if (debug != null) { + debug.println("getPermissions:\n\t" + printPD(pd)); + } + + final CodeSource cs = pd.getCodeSource(); + if (cs == null) + return perms; + + CodeSource canonCodeSource = AccessController.doPrivileged( + new PrivilegedAction(){ + public CodeSource run() { + return canonicalizeCodebase(cs, true); + } + }); + return getPermissions(perms, canonCodeSource, pd.getPrincipals()); + } + + /** + * Examines the global policy and returns the provided Permissions + * object with additional permissions granted to the specified + * CodeSource. + * + * @param permissions the permissions to populate + * @param codesource the codesource associated with the caller. + * This encapsulates the original location of the code (where the code + * came from) and the public key(s) of its signer. + * + * @return the set of permissions according to the policy. + */ + private PermissionCollection getPermissions(Permissions perms, + final CodeSource cs) { + + if (cs == null) + return perms; + + CodeSource canonCodeSource = AccessController.doPrivileged( + new PrivilegedAction(){ + public CodeSource run() { + return canonicalizeCodebase(cs, true); + } + }); + + return getPermissions(perms, canonCodeSource, null); + } + + private Permissions getPermissions(Permissions perms, + final CodeSource cs, + Principal[] principals) { + PolicyInfo pi = policyInfo.get(); + + for (PolicyEntry entry : pi.policyEntries) { + addPermissions(perms, cs, principals, entry); + } + + // Go through policyEntries gotten from identity db; sync required + // because checkForTrustedIdentity (below) might update list + synchronized (pi.identityPolicyEntries) { + for (PolicyEntry entry : pi.identityPolicyEntries) { + addPermissions(perms, cs, principals, entry); + } + } + + // now see if any of the keys are trusted ids. + if (!ignoreIdentityScope) { + Certificate certs[] = cs.getCertificates(); + if (certs != null) { + for (int k=0; k < certs.length; k++) { + Object idMap = pi.aliasMapping.get(certs[k]); + if (idMap == null && + checkForTrustedIdentity(certs[k], pi)) { + // checkForTrustedIdentity added it + // to the policy for us. next time + // around we'll find it. This time + // around we need to add it. + perms.add(SecurityConstants.ALL_PERMISSION); + } + } + } + } + return perms; + } + + private void addPermissions(Permissions perms, + final CodeSource cs, + Principal[] principals, + final PolicyEntry entry) { + + if (debug != null) { + debug.println("evaluate codesources:\n" + + "\tPolicy CodeSource: " + entry.getCodeSource() + "\n" + + "\tActive CodeSource: " + cs); + } + + // check to see if the CodeSource implies + Boolean imp = AccessController.doPrivileged + (new PrivilegedAction() { + public Boolean run() { + return new Boolean(entry.getCodeSource().implies(cs)); + } + }); + if (!imp.booleanValue()) { + if (debug != null) { + debug.println("evaluation (codesource) failed"); + } + + // CodeSource does not imply - return and try next policy entry + return; + } + + // check to see if the Principals imply + + List entryPs = entry.getPrincipals(); + if (debug != null) { + List accPs = new ArrayList<>(); + if (principals != null) { + for (int i = 0; i < principals.length; i++) { + accPs.add(new PolicyParser.PrincipalEntry + (principals[i].getClass().getName(), + principals[i].getName())); + } + } + debug.println("evaluate principals:\n" + + "\tPolicy Principals: " + entryPs + "\n" + + "\tActive Principals: " + accPs); + } + + if (entryPs == null || entryPs.isEmpty()) { + + // policy entry has no principals - + // add perms regardless of principals in current ACC + + addPerms(perms, principals, entry); + if (debug != null) { + debug.println("evaluation (codesource/principals) passed"); + } + return; + + } else if (principals == null || principals.length == 0) { + + // current thread has no principals but this policy entry + // has principals - perms are not added + + if (debug != null) { + debug.println("evaluation (principals) failed"); + } + return; + } + + // current thread has principals and this policy entry + // has principals. see if policy entry principals match + // principals in current ACC + + for (PolicyParser.PrincipalEntry pppe : entryPs) { + + // Check for wildcards + if (pppe.isWildcardClass()) { + // a wildcard class matches all principals in current ACC + continue; + } + + if (pppe.isWildcardName()) { + // a wildcard name matches any principal with the same class + if (wildcardPrincipalNameImplies(pppe.principalClass, + principals)) { + continue; + } + if (debug != null) { + debug.println("evaluation (principal name wildcard) failed"); + } + // policy entry principal not in current ACC - + // immediately return and go to next policy entry + return; + } + + Set pSet = new HashSet<>(Arrays.asList(principals)); + Subject subject = new Subject(true, pSet, + Collections.EMPTY_SET, + Collections.EMPTY_SET); + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class pClass = Class.forName(pppe.principalClass, false, cl); + if (!Principal.class.isAssignableFrom(pClass)) { + // not the right subtype + throw new ClassCastException(pppe.principalClass + + " is not a Principal"); + } + + Constructor c = pClass.getConstructor(PARAMS1); + Principal p = (Principal)c.newInstance(new Object[] { + pppe.principalName }); + + if (debug != null) { + debug.println("found Principal " + p.getClass().getName()); + } + + // check if the Principal implies the current + // thread's principals + if (!p.implies(subject)) { + if (debug != null) { + debug.println("evaluation (principal implies) failed"); + } + + // policy principal does not imply the current Subject - + // immediately return and go to next policy entry + return; + } + } catch (Exception e) { + // fall back to default principal comparison. + // see if policy entry principal is in current ACC + + if (debug != null) { + e.printStackTrace(); + } + + if (!pppe.implies(subject)) { + if (debug != null) { + debug.println("evaluation (default principal implies) failed"); + } + + // policy entry principal not in current ACC - + // immediately return and go to next policy entry + return; + } + } + + // either the principal information matched, + // or the Principal.implies succeeded. + // continue loop and test the next policy principal + } + + // all policy entry principals were found in the current ACC - + // grant the policy permissions + + if (debug != null) { + debug.println("evaluation (codesource/principals) passed"); + } + addPerms(perms, principals, entry); + } + + /** + * Returns true if the array of principals contains at least one + * principal of the specified class. + */ + private static boolean wildcardPrincipalNameImplies(String principalClass, + Principal[] principals) + { + for (Principal p : principals) { + if (principalClass.equals(p.getClass().getName())) { + return true; + } + } + return false; + } + + private void addPerms(Permissions perms, + Principal[] accPs, + PolicyEntry entry) { + for (int i = 0; i < entry.permissions.size(); i++) { + Permission p = entry.permissions.get(i); + if (debug != null) { + debug.println(" granting " + p); + } + + if (p instanceof SelfPermission) { + // handle "SELF" permissions + expandSelf((SelfPermission)p, + entry.getPrincipals(), + accPs, + perms); + } else { + perms.add(p); + } + } + } + + /** + *

    + * + * @param sp the SelfPermission that needs to be expanded

    + * + * @param entryPs list of principals for the Policy entry. + * + * @param pdp Principal array from the current ProtectionDomain. + * + * @param perms the PermissionCollection where the individual + * Permissions will be added after expansion. + */ + + private void expandSelf(SelfPermission sp, + List entryPs, + Principal[] pdp, + Permissions perms) { + + if (entryPs == null || entryPs.isEmpty()) { + // No principals in the grant to substitute + if (debug != null) { + debug.println("Ignoring permission " + + sp.getSelfType() + + " with target name (" + + sp.getSelfName() + "). " + + "No Principal(s) specified " + + "in the grant clause. " + + "SELF-based target names are " + + "only valid in the context " + + "of a Principal-based grant entry." + ); + } + return; + } + int startIndex = 0; + int v; + StringBuilder sb = new StringBuilder(); + while ((v = sp.getSelfName().indexOf(SELF, startIndex)) != -1) { + + // add non-SELF string + sb.append(sp.getSelfName().substring(startIndex, v)); + + // expand SELF + Iterator pli = entryPs.iterator(); + while (pli.hasNext()) { + PolicyParser.PrincipalEntry pppe = pli.next(); + String[][] principalInfo = getPrincipalInfo(pppe,pdp); + for (int i = 0; i < principalInfo.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(principalInfo[i][0] + " " + + "\"" + principalInfo[i][1] + "\""); + } + if (pli.hasNext()) { + sb.append(", "); + } + } + startIndex = v + SELF.length(); + } + // add remaining string (might be the entire string) + sb.append(sp.getSelfName().substring(startIndex)); + + if (debug != null) { + debug.println(" expanded:\n\t" + sp.getSelfName() + + "\n into:\n\t" + sb.toString()); + } + try { + // first try to instantiate the permission + perms.add(getInstance(sp.getSelfType(), + sb.toString(), + sp.getSelfActions())); + } catch (ClassNotFoundException cnfe) { + // ok, the permission is not in the bootclasspath. + // before we add an UnresolvedPermission, check to see + // whether this perm already belongs to the collection. + // if so, use that perm's ClassLoader to create a new + // one. + Class pc = null; + synchronized (perms) { + Enumeration e = perms.elements(); + while (e.hasMoreElements()) { + Permission pElement = e.nextElement(); + if (pElement.getClass().getName().equals(sp.getSelfType())) { + pc = pElement.getClass(); + break; + } + } + } + if (pc == null) { + // create an UnresolvedPermission + perms.add(new UnresolvedPermission(sp.getSelfType(), + sb.toString(), + sp.getSelfActions(), + sp.getCerts())); + } else { + try { + // we found an instantiated permission. + // use its class loader to instantiate a new permission. + Constructor c; + // name parameter can not be null + if (sp.getSelfActions() == null) { + try { + c = pc.getConstructor(PARAMS1); + perms.add((Permission)c.newInstance + (new Object[] {sb.toString()})); + } catch (NoSuchMethodException ne) { + c = pc.getConstructor(PARAMS2); + perms.add((Permission)c.newInstance + (new Object[] {sb.toString(), + sp.getSelfActions() })); + } + } else { + c = pc.getConstructor(PARAMS2); + perms.add((Permission)c.newInstance + (new Object[] {sb.toString(), + sp.getSelfActions()})); + } + } catch (Exception nme) { + if (debug != null) { + debug.println("self entry expansion " + + " instantiation failed: " + + nme.toString()); + } + } + } + } catch (Exception e) { + if (debug != null) { + debug.println(e.toString()); + } + } + } + + /** + * return the principal class/name pair in the 2D array. + * array[x][y]: x corresponds to the array length. + * if (y == 0), it's the principal class. + * if (y == 1), it's the principal name. + */ + private String[][] getPrincipalInfo + (PolicyParser.PrincipalEntry pe, Principal[] pdp) { + + // there are 3 possibilities: + // 1) the entry's Principal class and name are not wildcarded + // 2) the entry's Principal name is wildcarded only + // 3) the entry's Principal class and name are wildcarded + + if (!pe.isWildcardClass() && !pe.isWildcardName()) { + + // build an info array for the principal + // from the Policy entry + String[][] info = new String[1][2]; + info[0][0] = pe.principalClass; + info[0][1] = pe.principalName; + return info; + + } else if (!pe.isWildcardClass() && pe.isWildcardName()) { + + // build an info array for every principal + // in the current domain which has a principal class + // that is equal to policy entry principal class name + List plist = new ArrayList<>(); + for (int i = 0; i < pdp.length; i++) { + if (pe.principalClass.equals(pdp[i].getClass().getName())) + plist.add(pdp[i]); + } + String[][] info = new String[plist.size()][2]; + int i = 0; + for (Principal p : plist) { + info[i][0] = p.getClass().getName(); + info[i][1] = p.getName(); + i++; + } + return info; + + } else { + + // build an info array for every + // one of the current Domain's principals + + String[][] info = new String[pdp.length][2]; + + for (int i = 0; i < pdp.length; i++) { + info[i][0] = pdp[i].getClass().getName(); + info[i][1] = pdp[i].getName(); + } + return info; + } + } + + /* + * Returns the signer certificates from the list of certificates + * associated with the given code source. + * + * The signer certificates are those certificates that were used + * to verifysigned code originating from the codesource location. + * + * This method assumes that in the given code source, each signer + * certificate is followed by its supporting certificate chain + * (which may be empty), and that the signer certificate and its + * supporting certificate chain are ordered bottom-to-top + * (i.e., with the signer certificate first and the (root) certificate + * authority last). + */ + protected Certificate[] getSignerCertificates(CodeSource cs) { + Certificate[] certs = null; + if ((certs = cs.getCertificates()) == null) + return null; + for (int i=0; i userCertList = new ArrayList<>(); + i = 0; + while (i < certs.length) { + userCertList.add(certs[i]); + while (((i+1) < certs.length) + && ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i+1]).getSubjectDN())) { + i++; + } + i++; + } + Certificate[] userCerts = new Certificate[userCertList.size()]; + userCertList.toArray(userCerts); + return userCerts; + } + + private CodeSource canonicalizeCodebase(CodeSource cs, + boolean extractSignerCerts) { + + String path = null; + + CodeSource canonCs = cs; + URL u = cs.getLocation(); + if (u != null) { + if (u.getProtocol().equals("jar")) { + // unwrap url embedded inside jar url + String spec = u.getFile(); + int separator = spec.indexOf("!/"); + if (separator != -1) { + try { + u = new URL(spec.substring(0, separator)); + } catch (MalformedURLException e) { + // Fail silently. In this case, url stays what + // it was above + } + } + } + if (u.getProtocol().equals("file")) { + boolean isLocalFile = false; + String host = u.getHost(); + isLocalFile = (host == null || host.equals("") || + host.equals("~") || host.equalsIgnoreCase("localhost")); + + if (isLocalFile) { + path = u.getFile().replace('/', File.separatorChar); + path = ParseUtil.decode(path); + } + } + } + + if (path != null) { + try { + URL csUrl = null; + path = canonPath(path); + csUrl = ParseUtil.fileToEncodedURL(new File(path)); + + if (extractSignerCerts) { + canonCs = new CodeSource(csUrl, + getSignerCertificates(cs)); + } else { + canonCs = new CodeSource(csUrl, + cs.getCertificates()); + } + } catch (IOException ioe) { + // leave codesource as it is, unless we have to extract its + // signer certificates + if (extractSignerCerts) { + canonCs = new CodeSource(cs.getLocation(), + getSignerCertificates(cs)); + } + } + } else { + if (extractSignerCerts) { + canonCs = new CodeSource(cs.getLocation(), + getSignerCertificates(cs)); + } + } + return canonCs; + } + + // Wrapper to return a canonical path that avoids calling getCanonicalPath() + // with paths that are intended to match all entries in the directory + private static String canonPath(String path) throws IOException { + if (path.endsWith("*")) { + path = path.substring(0, path.length()-1) + "-"; + path = new File(path).getCanonicalPath(); + return path.substring(0, path.length()-1) + "*"; + } else { + return new File(path).getCanonicalPath(); + } + } + + private String printPD(ProtectionDomain pd) { + Principal[] principals = pd.getPrincipals(); + String pals = ""; + if (principals != null && principals.length > 0) { + StringBuilder palBuf = new StringBuilder("(principals "); + for (int i = 0; i < principals.length; i++) { + palBuf.append(principals[i].getClass().getName() + + " \"" + principals[i].getName() + + "\""); + if (i < principals.length-1) + palBuf.append(", "); + else + palBuf.append(")"); + } + pals = palBuf.toString(); + } + return "PD CodeSource: " + + pd.getCodeSource() + +"\n\t" + "PD ClassLoader: " + + pd.getClassLoader() + +"\n\t" + "PD Principals: " + + pals; + } + + /** + * return true if no replacement was performed, + * or if replacement succeeded. + */ + private boolean replacePrincipals( + List principals, KeyStore keystore) { + + if (principals == null || principals.isEmpty() || keystore == null) + return true; + + for (PolicyParser.PrincipalEntry pppe : principals) { + if (pppe.isReplaceName()) { + + // perform replacement + // (only X509 replacement is possible now) + String name; + if ((name = getDN(pppe.principalName, keystore)) == null) { + return false; + } + + if (debug != null) { + debug.println(" Replacing \"" + + pppe.principalName + + "\" with " + + X500PRINCIPAL + "/\"" + + name + + "\""); + } + + pppe.principalClass = X500PRINCIPAL; + pppe.principalName = name; + } + } + // return true if no replacement was performed, + // or if replacement succeeded + return true; + } + + private void expandPermissionName(PolicyParser.PermissionEntry pe, + KeyStore keystore) throws Exception { + // short cut the common case + if (pe.name == null || pe.name.indexOf("${{", 0) == -1) { + return; + } + + int startIndex = 0; + int b, e; + StringBuilder sb = new StringBuilder(); + while ((b = pe.name.indexOf("${{", startIndex)) != -1) { + e = pe.name.indexOf("}}", b); + if (e < 1) { + break; + } + sb.append(pe.name.substring(startIndex, b)); + + // get the value in ${{...}} + String value = pe.name.substring(b+3, e); + + // parse up to the first ':' + int colonIndex; + String prefix = value; + String suffix; + if ((colonIndex = value.indexOf(":")) != -1) { + prefix = value.substring(0, colonIndex); + } + + // handle different prefix possibilities + if (prefix.equalsIgnoreCase("self")) { + // do nothing - handled later + sb.append(pe.name.substring(b, e+2)); + startIndex = e+2; + continue; + } else if (prefix.equalsIgnoreCase("alias")) { + // get the suffix and perform keystore alias replacement + if (colonIndex == -1) { + MessageFormat form = new MessageFormat + (ResourcesMgr.getString + ("alias.name.not.provided.pe.name.")); + Object[] source = {pe.name}; + throw new Exception(form.format(source)); + } + suffix = value.substring(colonIndex+1); + if ((suffix = getDN(suffix, keystore)) == null) { + MessageFormat form = new MessageFormat + (ResourcesMgr.getString + ("unable.to.perform.substitution.on.alias.suffix")); + Object[] source = {value.substring(colonIndex+1)}; + throw new Exception(form.format(source)); + } + + sb.append(X500PRINCIPAL + " \"" + suffix + "\""); + startIndex = e+2; + } else { + MessageFormat form = new MessageFormat + (ResourcesMgr.getString + ("substitution.value.prefix.unsupported")); + Object[] source = {prefix}; + throw new Exception(form.format(source)); + } + } + + // copy the rest of the value + sb.append(pe.name.substring(startIndex)); + + // replace the name with expanded value + if (debug != null) { + debug.println(" Permission name expanded from:\n\t" + + pe.name + "\nto\n\t" + sb.toString()); + } + pe.name = sb.toString(); + } + + private String getDN(String alias, KeyStore keystore) { + Certificate cert = null; + try { + cert = keystore.getCertificate(alias); + } catch (Exception e) { + if (debug != null) { + debug.println(" Error retrieving certificate for '" + + alias + + "': " + + e.toString()); + } + return null; + } + + if (cert == null || !(cert instanceof X509Certificate)) { + if (debug != null) { + debug.println(" -- No certificate for '" + + alias + + "' - ignoring entry"); + } + return null; + } else { + X509Certificate x509Cert = (X509Certificate)cert; + + // 4702543: X500 names with an EmailAddress + // were encoded incorrectly. create new + // X500Principal name with correct encoding + + X500Principal p = new X500Principal + (x509Cert.getSubjectX500Principal().toString()); + return p.getName(); + } + } + + /** + * Checks public key. If it is marked as trusted in + * the identity database, add it to the policy + * with the AllPermission. + */ + private boolean checkForTrustedIdentity(final Certificate cert, + PolicyInfo myInfo) + { + return false; + } + + /** + * Each entry in the policy configuration file is represented by a + * PolicyEntry object.

    + * + * A PolicyEntry is a (CodeSource,Permission) pair. The + * CodeSource contains the (URL, PublicKey) that together identify + * where the Java bytecodes come from and who (if anyone) signed + * them. The URL could refer to localhost. The URL could also be + * null, meaning that this policy entry is given to all comers, as + * long as they match the signer field. The signer could be null, + * meaning the code is not signed.

    + * + * The Permission contains the (Type, Name, Action) triplet.

    + * + * For now, the Policy object retrieves the public key from the + * X.509 certificate on disk that corresponds to the signedBy + * alias specified in the Policy config file. For reasons of + * efficiency, the Policy object keeps a hashtable of certs already + * read in. This could be replaced by a secure internal key + * store. + * + *

    + * For example, the entry + *

    +     *          permission java.io.File "/tmp", "read,write",
    +     *          signedBy "Duke";
    +     * 
    + * is represented internally + *
    +     *
    +     * FilePermission f = new FilePermission("/tmp", "read,write");
    +     * PublicKey p = publickeys.get("Duke");
    +     * URL u = InetAddress.getLocalHost();
    +     * CodeBase c = new CodeBase( p, u );
    +     * pe = new PolicyEntry(f, c);
    +     * 
    + * + * @author Marianne Mueller + * @author Roland Schemers + * @see CodeSource + * @see Policy + * @see Permissions + * @see ProtectionDomain + */ + private static class PolicyEntry { + + private final CodeSource codesource; + final List permissions; + private final List principals; + + /** + * Given a Permission and a CodeSource, create a policy entry. + * + * XXX Decide if/how to add validity fields and "purpose" fields to + * XXX policy entries + * + * @param cs the CodeSource, which encapsulates the URL and the + * public key + * attributes from the policy config file. Validity checks + * are performed on the public key before PolicyEntry is + * called. + * + */ + PolicyEntry(CodeSource cs, List principals) + { + this.codesource = cs; + this.permissions = new ArrayList(); + this.principals = principals; // can be null + } + + PolicyEntry(CodeSource cs) + { + this(cs, null); + } + + List getPrincipals() { + return principals; // can be null + } + + /** + * add a Permission object to this entry. + * No need to sync add op because perms are added to entry only + * while entry is being initialized + */ + void add(Permission p) { + permissions.add(p); + } + + /** + * Return the CodeSource for this policy entry + */ + CodeSource getCodeSource() { + return codesource; + } + + @Override public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(ResourcesMgr.getString("LPARAM")); + sb.append(getCodeSource()); + sb.append("\n"); + for (int j = 0; j < permissions.size(); j++) { + Permission p = permissions.get(j); + sb.append(ResourcesMgr.getString("SPACE")); + sb.append(ResourcesMgr.getString("SPACE")); + sb.append(p); + sb.append(ResourcesMgr.getString("NEWLINE")); + } + sb.append(ResourcesMgr.getString("RPARAM")); + sb.append(ResourcesMgr.getString("NEWLINE")); + return sb.toString(); + } + } + + private static class SelfPermission extends Permission { + + private static final long serialVersionUID = -8315562579967246806L; + + /** + * The class name of the Permission class that will be + * created when this self permission is expanded . + * + * @serial + */ + private String type; + + /** + * The permission name. + * + * @serial + */ + private String name; + + /** + * The actions of the permission. + * + * @serial + */ + private String actions; + + /** + * The certs of the permission. + * + * @serial + */ + private Certificate certs[]; + + /** + * Creates a new SelfPermission containing the permission + * information needed later to expand the self + * @param type the class name of the Permission class that will be + * created when this permission is expanded and if necessary resolved. + * @param name the name of the permission. + * @param actions the actions of the permission. + * @param certs the certificates the permission's class was signed with. + * This is a list of certificate chains, where each chain is composed of + * a signer certificate and optionally its supporting certificate chain. + * Each chain is ordered bottom-to-top (i.e., with the signer + * certificate first and the (root) certificate authority last). + */ + public SelfPermission(String type, String name, String actions, + Certificate certs[]) + { + super(type); + if (type == null) { + throw new NullPointerException + (ResourcesMgr.getString("type.can.t.be.null")); + } + this.type = type; + this.name = name; + this.actions = actions; + if (certs != null) { + // Extract the signer certs from the list of certificates. + for (int i=0; i signerCerts = new ArrayList<>(); + i = 0; + while (i < certs.length) { + signerCerts.add(certs[i]); + while (((i+1) < certs.length) && + ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i+1]).getSubjectDN())) { + i++; + } + i++; + } + this.certs = new Certificate[signerCerts.size()]; + signerCerts.toArray(this.certs); + } + } + } + } + + /** + * This method always returns false for SelfPermission permissions. + * That is, an SelfPermission never considered to + * imply another permission. + * + * @param p the permission to check against. + * + * @return false. + */ + @Override public boolean implies(Permission p) { + return false; + } + + /** + * Checks two SelfPermission objects for equality. + * + * Checks that obj is an SelfPermission, and has + * the same type (class) name, permission name, actions, and + * certificates as this object. + * + * @param obj the object we are testing for equality with this object. + * + * @return true if obj is an SelfPermission, and has the same + * type (class) name, permission name, actions, and + * certificates as this object. + */ + @Override public boolean equals(Object obj) { + if (obj == this) + return true; + + if (! (obj instanceof SelfPermission)) + return false; + SelfPermission that = (SelfPermission) obj; + + if (!(this.type.equals(that.type) && + this.name.equals(that.name) && + this.actions.equals(that.actions))) + return false; + + if (this.certs.length != that.certs.length) + return false; + + int i,j; + boolean match; + + for (i = 0; i < this.certs.length; i++) { + match = false; + for (j = 0; j < that.certs.length; j++) { + if (this.certs[i].equals(that.certs[j])) { + match = true; + break; + } + } + if (!match) return false; + } + + for (i = 0; i < that.certs.length; i++) { + match = false; + for (j = 0; j < this.certs.length; j++) { + if (that.certs[i].equals(this.certs[j])) { + match = true; + break; + } + } + if (!match) return false; + } + return true; + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + @Override public int hashCode() { + int hash = type.hashCode(); + if (name != null) + hash ^= name.hashCode(); + if (actions != null) + hash ^= actions.hashCode(); + return hash; + } + + /** + * Returns the canonical string representation of the actions, + * which currently is the empty string "", since there are no actions + * for an SelfPermission. That is, the actions for the + * permission that will be created when this SelfPermission + * is resolved may be non-null, but an SelfPermission + * itself is never considered to have any actions. + * + * @return the empty string "". + */ + @Override public String getActions() { + return ""; + } + + public String getSelfType() { + return type; + } + + public String getSelfName() { + return name; + } + + public String getSelfActions() { + return actions; + } + + public Certificate[] getCerts() { + return certs; + } + + /** + * Returns a string describing this SelfPermission. The convention + * is to specify the class name, the permission name, and the actions, + * in the following format: '(unresolved "ClassName" "name" "actions")'. + * + * @return information about this SelfPermission. + */ + @Override public String toString() { + return "(SelfPermission " + type + " " + name + " " + actions + ")"; + } + } + + /** + * holds policy information that we need to synch on + */ + private static class PolicyInfo { + private static final boolean verbose = false; + + // Stores grant entries in the policy + final List policyEntries; + + // Stores grant entries gotten from identity database + // Use separate lists to avoid sync on policyEntries + final List identityPolicyEntries; + + // Maps aliases to certs + final Map aliasMapping; + + // Maps ProtectionDomain to PermissionCollection + private final ProtectionDomainCache[] pdMapping; + private Random random; + + PolicyInfo(int numCaches) { + policyEntries = new ArrayList<>(); + identityPolicyEntries = + Collections.synchronizedList(new ArrayList(2)); + aliasMapping = Collections.synchronizedMap(new HashMap<>(11)); + + pdMapping = new ProtectionDomainCache[numCaches]; + JavaSecurityProtectionDomainAccess jspda + = SharedSecrets.getJavaSecurityProtectionDomainAccess(); + for (int i = 0; i < numCaches; i++) { + pdMapping[i] = jspda.getProtectionDomainCache(); + } + if (numCaches > 1) { + random = new Random(); + } + } + ProtectionDomainCache getPdMapping() { + if (pdMapping.length == 1) { + return pdMapping[0]; + } else { + int i = Math.abs(random.nextInt() % pdMapping.length); + return pdMapping[i]; + } + } + } +} diff --git a/src/sun/security/provider/PolicyParser.java b/src/sun/security/provider/PolicyParser.java new file mode 100644 index 00000000..71b796bb --- /dev/null +++ b/src/sun/security/provider/PolicyParser.java @@ -0,0 +1,1432 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.text.MessageFormat; +import java.util.*; +import javax.security.auth.x500.X500Principal; + +import sun.security.util.Debug; +import sun.security.util.PropertyExpander; +import sun.security.util.ResourcesMgr; + +/** + * The policy for a Java runtime (specifying + * which permissions are available for code from various principals) + * is represented as a separate + * persistent configuration. The configuration may be stored as a + * flat ASCII file, as a serialized binary file of + * the Policy class, or as a database.

    + * + *

    The Java runtime creates one global Policy object, which is used to + * represent the static policy configuration file. It is consulted by + * a ProtectionDomain when the protection domain initializes its set of + * permissions.

    + * + *

    The Policy init method parses the policy + * configuration file, and then + * populates the Policy object. The Policy object is agnostic in that + * it is not involved in making policy decisions. It is merely the + * Java runtime representation of the persistent policy configuration + * file.

    + * + *

    When a protection domain needs to initialize its set of + * permissions, it executes code such as the following + * to ask the global Policy object to populate a + * Permissions object with the appropriate permissions: + *

    + *  policy = Policy.getPolicy();
    + *  Permissions perms = policy.getPermissions(protectiondomain)
    + * 
    + * + *

    The protection domain contains a CodeSource + * object, which encapsulates its codebase (URL) and public key attributes. + * It also contains the principals associated with the domain. + * The Policy object evaluates the global policy in light of who the + * principal is and what the code source is and returns an appropriate + * Permissions object. + * + * @author Roland Schemers + * @author Ram Marti + * + * @since 1.2 + */ + +public class PolicyParser { + + private static final String EXTDIRS_PROPERTY = "java.ext.dirs"; + private static final String OLD_EXTDIRS_EXPANSION = + "${" + EXTDIRS_PROPERTY + "}"; + + // package-private: used by PolicyFile for static policy + static final String EXTDIRS_EXPANSION = "${{" + EXTDIRS_PROPERTY + "}}"; + + + private Vector grantEntries; + private Map domainEntries; + + // Convenience variables for parsing + private static final Debug debug = Debug.getInstance("parser", + "\t[Policy Parser]"); + private StreamTokenizer st; + private int lookahead; + private boolean expandProp = false; + private String keyStoreUrlString = null; // unexpanded + private String keyStoreType = null; + private String keyStoreProvider = null; + private String storePassURL = null; + + private String expand(String value) + throws PropertyExpander.ExpandException + { + return expand(value, false); + } + + private String expand(String value, boolean encodeURL) + throws PropertyExpander.ExpandException + { + if (!expandProp) { + return value; + } else { + return PropertyExpander.expand(value, encodeURL); + } + } + + /** + * Creates a PolicyParser object. + */ + + public PolicyParser() { + grantEntries = new Vector(); + } + + + public PolicyParser(boolean expandProp) { + this(); + this.expandProp = expandProp; + } + + /** + * Reads a policy configuration into the Policy object using a + * Reader object.

    + * + * @param policy the policy Reader object. + * + * @exception ParsingException if the policy configuration contains + * a syntax error. + * + * @exception IOException if an error occurs while reading the policy + * configuration. + */ + + public void read(Reader policy) + throws ParsingException, IOException + { + if (!(policy instanceof BufferedReader)) { + policy = new BufferedReader(policy); + } + + /** + * Configure the stream tokenizer: + * Recognize strings between "..." + * Don't convert words to lowercase + * Recognize both C-style and C++-style comments + * Treat end-of-line as white space, not as a token + */ + st = new StreamTokenizer(policy); + + st.resetSyntax(); + st.wordChars('a', 'z'); + st.wordChars('A', 'Z'); + st.wordChars('.', '.'); + st.wordChars('0', '9'); + st.wordChars('_', '_'); + st.wordChars('$', '$'); + st.wordChars(128 + 32, 255); + st.whitespaceChars(0, ' '); + st.commentChar('/'); + st.quoteChar('\''); + st.quoteChar('"'); + st.lowerCaseMode(false); + st.ordinaryChar('/'); + st.slashSlashComments(true); + st.slashStarComments(true); + + /** + * The main parsing loop. The loop is executed once + * for each entry in the config file. The entries + * are delimited by semicolons. Once we've read in + * the information for an entry, go ahead and try to + * add it to the policy vector. + * + */ + + lookahead = st.nextToken(); + GrantEntry ge = null; + while (lookahead != StreamTokenizer.TT_EOF) { + if (peek("grant")) { + ge = parseGrantEntry(); + // could be null if we couldn't expand a property + if (ge != null) + add(ge); + } else if (peek("keystore") && keyStoreUrlString==null) { + // only one keystore entry per policy file, others will be + // ignored + parseKeyStoreEntry(); + } else if (peek("keystorePasswordURL") && storePassURL==null) { + // only one keystore passwordURL per policy file, others will be + // ignored + parseStorePassURL(); + } else if (ge == null && keyStoreUrlString == null && + storePassURL == null && peek("domain")) { + if (domainEntries == null) { + domainEntries = new TreeMap<>(); + } + DomainEntry de = parseDomainEntry(); + if (de != null) { + String domainName = de.getName(); + if (!domainEntries.containsKey(domainName)) { + domainEntries.put(domainName, de); + } else { + MessageFormat form = + new MessageFormat(ResourcesMgr.getString( + "duplicate.keystore.domain.name")); + Object[] source = {domainName}; + throw new ParsingException(form.format(source)); + } + } + } else { + // error? + } + match(";"); + } + + if (keyStoreUrlString == null && storePassURL != null) { + throw new ParsingException(ResourcesMgr.getString + ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore")); + } + } + + public void add(GrantEntry ge) + { + grantEntries.addElement(ge); + } + + public void replace(GrantEntry origGe, GrantEntry newGe) + { + grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe)); + } + + public boolean remove(GrantEntry ge) + { + return grantEntries.removeElement(ge); + } + + /** + * Returns the (possibly expanded) keystore location, or null if the + * expansion fails. + */ + public String getKeyStoreUrl() { + try { + if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) { + return expand(keyStoreUrlString, true).replace + (File.separatorChar, '/'); + } + } catch (PropertyExpander.ExpandException peee) { + if (debug != null) { + debug.println(peee.toString()); + } + return null; + } + return null; + } + + public void setKeyStoreUrl(String url) { + keyStoreUrlString = url; + } + + public String getKeyStoreType() { + return keyStoreType; + } + + public void setKeyStoreType(String type) { + keyStoreType = type; + } + + public String getKeyStoreProvider() { + return keyStoreProvider; + } + + public void setKeyStoreProvider(String provider) { + keyStoreProvider = provider; + } + + public String getStorePassURL() { + try { + if (storePassURL!=null && storePassURL.length()!=0) { + return expand(storePassURL, true).replace + (File.separatorChar, '/'); + } + } catch (PropertyExpander.ExpandException peee) { + if (debug != null) { + debug.println(peee.toString()); + } + return null; + } + return null; + } + + public void setStorePassURL(String storePassURL) { + this.storePassURL = storePassURL; + } + + /** + * Enumerate all the entries in the global policy object. + * This method is used by policy admin tools. The tools + * should use the Enumeration methods on the returned object + * to fetch the elements sequentially. + */ + public Enumeration grantElements(){ + return grantEntries.elements(); + } + + public Collection getDomainEntries() { + return domainEntries.values(); + } + + /** + * write out the policy + */ + + public void write(Writer policy) + { + PrintWriter out = new PrintWriter(new BufferedWriter(policy)); + + Enumeration enum_ = grantElements(); + + out.println("/* AUTOMATICALLY GENERATED ON "+ + (new Date()) + "*/"); + out.println("/* DO NOT EDIT */"); + out.println(); + + // write the (unexpanded) keystore entry as the first entry of the + // policy file + if (keyStoreUrlString != null) { + writeKeyStoreEntry(out); + } + if (storePassURL != null) { + writeStorePassURL(out); + } + + // write "grant" entries + while (enum_.hasMoreElements()) { + GrantEntry ge = enum_.nextElement(); + ge.write(out); + out.println(); + } + out.flush(); + } + + /** + * parses a keystore entry + */ + private void parseKeyStoreEntry() throws ParsingException, IOException { + match("keystore"); + keyStoreUrlString = match("quoted string"); + + // parse keystore type + if (!peek(",")) { + return; // default type + } + match(","); + + if (peek("\"")) { + keyStoreType = match("quoted string"); + } else { + throw new ParsingException(st.lineno(), + ResourcesMgr.getString("expected.keystore.type")); + } + + // parse keystore provider + if (!peek(",")) { + return; // provider optional + } + match(","); + + if (peek("\"")) { + keyStoreProvider = match("quoted string"); + } else { + throw new ParsingException(st.lineno(), + ResourcesMgr.getString("expected.keystore.provider")); + } + } + + private void parseStorePassURL() throws ParsingException, IOException { + match("keyStorePasswordURL"); + storePassURL = match("quoted string"); + } + + /** + * writes the (unexpanded) keystore entry + */ + private void writeKeyStoreEntry(PrintWriter out) { + out.print("keystore \""); + out.print(keyStoreUrlString); + out.print('"'); + if (keyStoreType != null && keyStoreType.length() > 0) + out.print(", \"" + keyStoreType + "\""); + if (keyStoreProvider != null && keyStoreProvider.length() > 0) + out.print(", \"" + keyStoreProvider + "\""); + out.println(";"); + out.println(); + } + + private void writeStorePassURL(PrintWriter out) { + out.print("keystorePasswordURL \""); + out.print(storePassURL); + out.print('"'); + out.println(";"); + out.println(); + } + + /** + * parse a Grant entry + */ + private GrantEntry parseGrantEntry() + throws ParsingException, IOException + { + GrantEntry e = new GrantEntry(); + LinkedList principals = null; + boolean ignoreEntry = false; + + match("grant"); + + while(!peek("{")) { + + if (peekAndMatch("Codebase")) { + if (e.codeBase != null) + throw new ParsingException( + st.lineno(), + ResourcesMgr.getString + ("multiple.Codebase.expressions")); + e.codeBase = match("quoted string"); + peekAndMatch(","); + } else if (peekAndMatch("SignedBy")) { + if (e.signedBy != null) + throw new ParsingException( + st.lineno(), + ResourcesMgr.getString( + "multiple.SignedBy.expressions")); + e.signedBy = match("quoted string"); + + // verify syntax of the aliases + StringTokenizer aliases = new StringTokenizer(e.signedBy, + ",", true); + int actr = 0; + int cctr = 0; + while (aliases.hasMoreTokens()) { + String alias = aliases.nextToken().trim(); + if (alias.equals(",")) + cctr++; + else if (alias.length() > 0) + actr++; + } + if (actr <= cctr) + throw new ParsingException( + st.lineno(), + ResourcesMgr.getString( + "SignedBy.has.empty.alias")); + + peekAndMatch(","); + } else if (peekAndMatch("Principal")) { + if (principals == null) { + principals = new LinkedList<>(); + } + + String principalClass; + String principalName; + + if (peek("\"")) { + // both the principalClass and principalName + // will be replaced later + principalClass = PrincipalEntry.REPLACE_NAME; + principalName = match("principal type"); + } else { + // check for principalClass wildcard + if (peek("*")) { + match("*"); + principalClass = PrincipalEntry.WILDCARD_CLASS; + } else { + principalClass = match("principal type"); + } + + // check for principalName wildcard + if (peek("*")) { + match("*"); + principalName = PrincipalEntry.WILDCARD_NAME; + } else { + principalName = match("quoted string"); + } + + // disallow WILDCARD_CLASS && actual name + if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) && + !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { + if (debug != null) { + debug.println("disallowing principal that " + + "has WILDCARD class but no WILDCARD name"); + } + throw new ParsingException + (st.lineno(), + ResourcesMgr.getString + ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name")); + } + } + + try { + principalName = expand(principalName); + + if (principalClass.equals + ("javax.security.auth.x500.X500Principal") && + !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { + + // 4702543: X500 names with an EmailAddress + // were encoded incorrectly. construct a new + // X500Principal with correct encoding. + + X500Principal p = new X500Principal + ((new X500Principal(principalName)).toString()); + principalName = p.getName(); + } + + principals.add + (new PrincipalEntry(principalClass, principalName)); + } catch (PropertyExpander.ExpandException peee) { + // ignore the entire policy entry + // but continue parsing all the info + // so we can get to the next entry + if (debug != null) { + debug.println("principal name expansion failed: " + + principalName); + } + ignoreEntry = true; + } + peekAndMatch(","); + + } else { + throw new ParsingException(st.lineno(), + ResourcesMgr.getString( + "expected.codeBase.or.SignedBy.or.Principal")); + } + } + + if (principals != null) e.principals = principals; + match("{"); + + while(!peek("}")) { + if (peek("Permission")) { + try { + PermissionEntry pe = parsePermissionEntry(); + e.add(pe); + } catch (PropertyExpander.ExpandException peee) { + // ignore. The add never happened + if (debug != null) { + debug.println(peee.toString()); + } + skipEntry(); // BugId 4219343 + } + match(";"); + } else { + throw new + ParsingException(st.lineno(), + ResourcesMgr.getString( + "expected.permission.entry")); + } + } + match("}"); + + try { + if (e.signedBy != null) e.signedBy = expand(e.signedBy); + if (e.codeBase != null) { + + // For backward compatibility with 1.4 + if (e.codeBase.equals(OLD_EXTDIRS_EXPANSION)) { + e.codeBase = EXTDIRS_EXPANSION; + } + int es; + if ((es=e.codeBase.indexOf(EXTDIRS_EXPANSION)) < 0) { + e.codeBase = expand(e.codeBase, true).replace + (File.separatorChar, '/'); + } else { + // expand the system property "java.ext.dirs", + // parse it into its path components, + // and then create a grant entry for each component + String[] extDirs = parseExtDirs(e.codeBase, es); + if (extDirs != null && extDirs.length > 0) { + for (int i = 0; i < extDirs.length; i++) { + GrantEntry newGe = (GrantEntry)e.clone(); + newGe.codeBase = extDirs[i]; + add(newGe); + + if (debug != null) { + debug.println("creating policy entry for " + + "expanded java.ext.dirs path:\n\t\t" + + extDirs[i]); + } + } + } + ignoreEntry = true; + } + } + } catch (PropertyExpander.ExpandException peee) { + if (debug != null) { + debug.println(peee.toString()); + } + return null; + } + + return (ignoreEntry == true) ? null : e; + } + + /** + * parse a Permission entry + */ + private PermissionEntry parsePermissionEntry() + throws ParsingException, IOException, PropertyExpander.ExpandException + { + PermissionEntry e = new PermissionEntry(); + + // Permission + match("Permission"); + e.permission = match("permission type"); + + if (peek("\"")) { + // Permission name + e.name = expand(match("quoted string")); + } + + if (!peek(",")) { + return e; + } + match(","); + + if (peek("\"")) { + e.action = expand(match("quoted string")); + if (!peek(",")) { + return e; + } + match(","); + } + + if (peekAndMatch("SignedBy")) { + e.signedBy = expand(match("quoted string")); + } + return e; + } + + /** + * parse a domain entry + */ + private DomainEntry parseDomainEntry() + throws ParsingException, IOException + { + boolean ignoreEntry = false; + DomainEntry domainEntry; + String name = null; + Map properties = new HashMap<>(); + + match("domain"); + name = match("domain name"); + + while(!peek("{")) { + // get the domain properties + properties = parseProperties("{"); + } + match("{"); + domainEntry = new DomainEntry(name, properties); + + while(!peek("}")) { + + match("keystore"); + name = match("keystore name"); + // get the keystore properties + if (!peek("}")) { + properties = parseProperties(";"); + } + match(";"); + domainEntry.add(new KeyStoreEntry(name, properties)); + } + match("}"); + + return (ignoreEntry == true) ? null : domainEntry; + } + + /* + * Return a collection of domain properties or keystore properties. + */ + private Map parseProperties(String terminator) + throws ParsingException, IOException { + + Map properties = new HashMap<>(); + String key; + String value; + while (!peek(terminator)) { + key = match("property name"); + match("="); + + try { + value = expand(match("quoted string")); + } catch (PropertyExpander.ExpandException peee) { + throw new IOException(peee.getLocalizedMessage()); + } + properties.put(key.toLowerCase(), value); + } + + return properties; + } + + // package-private: used by PolicyFile for static policy + static String[] parseExtDirs(String codebase, int start) { + + String s = System.getProperty(EXTDIRS_PROPERTY); + String globalPrefix = (start > 0 ? codebase.substring(0, start) : "file:"); + int end = start + EXTDIRS_EXPANSION.length(); + String globalSuffix = (end < codebase.length() ? codebase.substring(end) : + (String) null); + + String[] dirs = null; + String localSuffix; + if (s != null) { + StringTokenizer st = + new StringTokenizer(s, File.pathSeparator); + int count = st.countTokens(); + dirs = new String[count]; + for (int i = 0; i < count; i++) { + File file = new File(st.nextToken()); + dirs[i] = sun.net.www.ParseUtil.encodePath + (file.getAbsolutePath()); + + if (!dirs[i].startsWith("/")) { + dirs[i] = "/" + dirs[i]; + } + + localSuffix = (globalSuffix == null ? + (dirs[i].endsWith("/") ? "*" : "/*") : + globalSuffix); + + dirs[i] = globalPrefix + dirs[i] + localSuffix; + } + } + return dirs; + } + + private boolean peekAndMatch(String expect) + throws ParsingException, IOException + { + if (peek(expect)) { + match(expect); + return true; + } else { + return false; + } + } + + private boolean peek(String expect) { + boolean found = false; + + switch (lookahead) { + + case StreamTokenizer.TT_WORD: + if (expect.equalsIgnoreCase(st.sval)) + found = true; + break; + case ',': + if (expect.equalsIgnoreCase(",")) + found = true; + break; + case '{': + if (expect.equalsIgnoreCase("{")) + found = true; + break; + case '}': + if (expect.equalsIgnoreCase("}")) + found = true; + break; + case '"': + if (expect.equalsIgnoreCase("\"")) + found = true; + break; + case '*': + if (expect.equalsIgnoreCase("*")) + found = true; + break; + case ';': + if (expect.equalsIgnoreCase(";")) + found = true; + break; + default: + + } + return found; + } + + private String match(String expect) + throws ParsingException, IOException + { + String value = null; + + switch (lookahead) { + case StreamTokenizer.TT_NUMBER: + throw new ParsingException(st.lineno(), expect, + ResourcesMgr.getString("number.") + + String.valueOf(st.nval)); + case StreamTokenizer.TT_EOF: + MessageFormat form = new MessageFormat( + ResourcesMgr.getString + ("expected.expect.read.end.of.file.")); + Object[] source = {expect}; + throw new ParsingException(form.format(source)); + case StreamTokenizer.TT_WORD: + if (expect.equalsIgnoreCase(st.sval)) { + lookahead = st.nextToken(); + } else if (expect.equalsIgnoreCase("permission type")) { + value = st.sval; + lookahead = st.nextToken(); + } else if (expect.equalsIgnoreCase("principal type")) { + value = st.sval; + lookahead = st.nextToken(); + } else if (expect.equalsIgnoreCase("domain name") || + expect.equalsIgnoreCase("keystore name") || + expect.equalsIgnoreCase("property name")) { + value = st.sval; + lookahead = st.nextToken(); + } else { + throw new ParsingException(st.lineno(), expect, + st.sval); + } + break; + case '"': + if (expect.equalsIgnoreCase("quoted string")) { + value = st.sval; + lookahead = st.nextToken(); + } else if (expect.equalsIgnoreCase("permission type")) { + value = st.sval; + lookahead = st.nextToken(); + } else if (expect.equalsIgnoreCase("principal type")) { + value = st.sval; + lookahead = st.nextToken(); + } else { + throw new ParsingException(st.lineno(), expect, st.sval); + } + break; + case ',': + if (expect.equalsIgnoreCase(",")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, ","); + break; + case '{': + if (expect.equalsIgnoreCase("{")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, "{"); + break; + case '}': + if (expect.equalsIgnoreCase("}")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, "}"); + break; + case ';': + if (expect.equalsIgnoreCase(";")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, ";"); + break; + case '*': + if (expect.equalsIgnoreCase("*")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, "*"); + break; + case '=': + if (expect.equalsIgnoreCase("=")) + lookahead = st.nextToken(); + else + throw new ParsingException(st.lineno(), expect, "="); + break; + default: + throw new ParsingException(st.lineno(), expect, + new String(new char[] {(char)lookahead})); + } + return value; + } + + /** + * skip all tokens for this entry leaving the delimiter ";" + * in the stream. + */ + private void skipEntry() throws ParsingException, IOException { + while(lookahead != ';') { + switch (lookahead) { + case StreamTokenizer.TT_NUMBER: + throw new ParsingException(st.lineno(), ";", + ResourcesMgr.getString("number.") + + String.valueOf(st.nval)); + case StreamTokenizer.TT_EOF: + throw new ParsingException(ResourcesMgr.getString + ("expected.read.end.of.file.")); + default: + lookahead = st.nextToken(); + } + } + } + + /** + * Each grant entry in the policy configuration file is + * represented by a + * GrantEntry object.

    + * + *

    + * For example, the entry + *

    +     *      grant signedBy "Duke" {
    +     *          permission java.io.FilePermission "/tmp", "read,write";
    +     *      };
    +     *
    +     * 
    + * is represented internally + *
    +     *
    +     * pe = new PermissionEntry("java.io.FilePermission",
    +     *                           "/tmp", "read,write");
    +     *
    +     * ge = new GrantEntry("Duke", null);
    +     *
    +     * ge.add(pe);
    +     *
    +     * 
    + * + * @author Roland Schemers + * + * version 1.19, 05/21/98 + */ + + public static class GrantEntry { + + public String signedBy; + public String codeBase; + public LinkedList principals; + public Vector permissionEntries; + + public GrantEntry() { + principals = new LinkedList(); + permissionEntries = new Vector(); + } + + public GrantEntry(String signedBy, String codeBase) { + this.codeBase = codeBase; + this.signedBy = signedBy; + principals = new LinkedList(); + permissionEntries = new Vector(); + } + + public void add(PermissionEntry pe) + { + permissionEntries.addElement(pe); + } + + public boolean remove(PrincipalEntry pe) + { + return principals.remove(pe); + } + + public boolean remove(PermissionEntry pe) + { + return permissionEntries.removeElement(pe); + } + + public boolean contains(PrincipalEntry pe) + { + return principals.contains(pe); + } + + public boolean contains(PermissionEntry pe) + { + return permissionEntries.contains(pe); + } + + /** + * Enumerate all the permission entries in this GrantEntry. + */ + public Enumeration permissionElements(){ + return permissionEntries.elements(); + } + + + public void write(PrintWriter out) { + out.print("grant"); + if (signedBy != null) { + out.print(" signedBy \""); + out.print(signedBy); + out.print('"'); + if (codeBase != null) + out.print(", "); + } + if (codeBase != null) { + out.print(" codeBase \""); + out.print(codeBase); + out.print('"'); + if (principals != null && principals.size() > 0) + out.print(",\n"); + } + if (principals != null && principals.size() > 0) { + Iterator pli = principals.iterator(); + while (pli.hasNext()) { + out.print(" "); + PrincipalEntry pe = pli.next(); + pe.write(out); + if (pli.hasNext()) + out.print(",\n"); + } + } + out.println(" {"); + Enumeration enum_ = permissionEntries.elements(); + while (enum_.hasMoreElements()) { + PermissionEntry pe = enum_.nextElement(); + out.write(" "); + pe.write(out); + } + out.println("};"); + } + + public Object clone() { + GrantEntry ge = new GrantEntry(); + ge.codeBase = this.codeBase; + ge.signedBy = this.signedBy; + ge.principals = new LinkedList(this.principals); + ge.permissionEntries = + new Vector(this.permissionEntries); + return ge; + } + } + + /** + * Principal info (class and name) in a grant entry + */ + public static class PrincipalEntry implements Principal { + + public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS"; + public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME"; + public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME"; + + String principalClass; + String principalName; + + /** + * A PrincipalEntry consists of the Principal class and Principal name. + * + * @param principalClass the Principal class + * @param principalName the Principal name + * @throws NullPointerException if principalClass or principalName + * are null + */ + public PrincipalEntry(String principalClass, String principalName) { + if (principalClass == null || principalName == null) + throw new NullPointerException(ResourcesMgr.getString( + "null.principalClass.or.principalName")); + this.principalClass = principalClass; + this.principalName = principalName; + } + + boolean isWildcardName() { + return principalName.equals(WILDCARD_NAME); + } + + boolean isWildcardClass() { + return principalClass.equals(WILDCARD_CLASS); + } + + boolean isReplaceName() { + return principalClass.equals(REPLACE_NAME); + } + + public String getPrincipalClass() { + return principalClass; + } + + public String getPrincipalName() { + return principalName; + } + + public String getDisplayClass() { + if (isWildcardClass()) { + return "*"; + } else if (isReplaceName()) { + return ""; + } + else return principalClass; + } + + public String getDisplayName() { + return getDisplayName(false); + } + + public String getDisplayName(boolean addQuote) { + if (isWildcardName()) { + return "*"; + } + else { + if (addQuote) return "\"" + principalName + "\""; + else return principalName; + } + } + + @Override + public String getName() { + return principalName; + } + + @Override + public String toString() { + if (!isReplaceName()) { + return getDisplayClass() + "/" + getDisplayName(); + } else { + return getDisplayName(); + } + } + + /** + * Test for equality between the specified object and this object. + * Two PrincipalEntries are equal if their class and name values + * are equal. + * + * @param obj the object to test for equality with this object + * @return true if the objects are equal, false otherwise + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof PrincipalEntry)) + return false; + + PrincipalEntry that = (PrincipalEntry)obj; + return (principalClass.equals(that.principalClass) && + principalName.equals(that.principalName)); + } + + /** + * Return a hashcode for this PrincipalEntry. + * + * @return a hashcode for this PrincipalEntry + */ + @Override + public int hashCode() { + return principalClass.hashCode(); + } + + public void write(PrintWriter out) { + out.print("principal " + getDisplayClass() + " " + + getDisplayName(true)); + } + } + + /** + * Each permission entry in the policy configuration file is + * represented by a + * PermissionEntry object.

    + * + *

    + * For example, the entry + *

    +     *          permission java.io.FilePermission "/tmp", "read,write";
    +     * 
    + * is represented internally + *
    +     *
    +     * pe = new PermissionEntry("java.io.FilePermission",
    +     *                           "/tmp", "read,write");
    +     * 
    + * + * @author Roland Schemers + * + * version 1.19, 05/21/98 + */ + + public static class PermissionEntry { + + public String permission; + public String name; + public String action; + public String signedBy; + + public PermissionEntry() { + } + + public PermissionEntry(String permission, + String name, + String action) { + this.permission = permission; + this.name = name; + this.action = action; + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + @Override + public int hashCode() { + int retval = permission.hashCode(); + if (name != null) retval ^= name.hashCode(); + if (action != null) retval ^= action.hashCode(); + return retval; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (! (obj instanceof PermissionEntry)) + return false; + + PermissionEntry that = (PermissionEntry) obj; + + if (this.permission == null) { + if (that.permission != null) return false; + } else { + if (!this.permission.equals(that.permission)) return false; + } + + if (this.name == null) { + if (that.name != null) return false; + } else { + if (!this.name.equals(that.name)) return false; + } + + if (this.action == null) { + if (that.action != null) return false; + } else { + if (!this.action.equals(that.action)) return false; + } + + if (this.signedBy == null) { + if (that.signedBy != null) return false; + } else { + if (!this.signedBy.equals(that.signedBy)) return false; + } + + // everything matched -- the 2 objects are equal + return true; + } + + public void write(PrintWriter out) { + out.print("permission "); + out.print(permission); + if (name != null) { + out.print(" \""); + + // ATTENTION: regex with double escaping, + // the normal forms look like: + // $name =~ s/\\/\\\\/g; and + // $name =~ s/\"/\\\"/g; + // and then in a java string, it's escaped again + + out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\"")); + out.print('"'); + } + if (action != null) { + out.print(", \""); + out.print(action); + out.print('"'); + } + if (signedBy != null) { + out.print(", signedBy \""); + out.print(signedBy); + out.print('"'); + } + out.println(";"); + } + } + + /** + * Each domain entry in the keystore domain configuration file is + * represented by a DomainEntry object. + */ + static class DomainEntry { + private final String name; + private final Map properties; + private final Map entries; + + DomainEntry(String name, Map properties) { + this.name = name; + this.properties = properties; + entries = new HashMap<>(); + } + + String getName() { + return name; + } + + Map getProperties() { + return properties; + } + + Collection getEntries() { + return entries.values(); + } + + void add(KeyStoreEntry entry) throws ParsingException { + String keystoreName = entry.getName(); + if (!entries.containsKey(keystoreName)) { + entries.put(keystoreName, entry); + } else { + MessageFormat form = new MessageFormat(ResourcesMgr.getString( + "duplicate.keystore.name")); + Object[] source = {keystoreName}; + throw new ParsingException(form.format(source)); + } + } + + @Override + public String toString() { + StringBuilder s = + new StringBuilder("\ndomain ").append(name); + + if (properties != null) { + for (Map.Entry property : + properties.entrySet()) { + s.append("\n ").append(property.getKey()).append('=') + .append(property.getValue()); + } + } + s.append(" {\n"); + + if (entries != null) { + for (KeyStoreEntry entry : entries.values()) { + s.append(entry).append("\n"); + } + } + s.append("}"); + + return s.toString(); + } + } + + /** + * Each keystore entry in the keystore domain configuration file is + * represented by a KeyStoreEntry object. + */ + + static class KeyStoreEntry { + private final String name; + private final Map properties; + + KeyStoreEntry(String name, Map properties) { + this.name = name; + this.properties = properties; + } + + String getName() { + return name; + } + + Map getProperties() { + return properties; + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder("\n keystore ").append(name); + if (properties != null) { + for (Map.Entry property : + properties.entrySet()) { + s.append("\n ").append(property.getKey()).append('=') + .append(property.getValue()); + } + } + s.append(";"); + + return s.toString(); + } + } + + public static class ParsingException extends GeneralSecurityException { + + private static final long serialVersionUID = -4330692689482574072L; + + private String i18nMessage; + + /** + * Constructs a ParsingException with the specified + * detail message. A detail message is a String that describes + * this particular exception, which may, for example, specify which + * algorithm is not available. + * + * @param msg the detail message. + */ + public ParsingException(String msg) { + super(msg); + i18nMessage = msg; + } + + public ParsingException(int line, String msg) { + super("line " + line + ": " + msg); + MessageFormat form = new MessageFormat + (ResourcesMgr.getString("line.number.msg")); + Object[] source = {new Integer(line), msg}; + i18nMessage = form.format(source); + } + + public ParsingException(int line, String expect, String actual) { + super("line " + line + ": expected [" + expect + + "], found [" + actual + "]"); + MessageFormat form = new MessageFormat(ResourcesMgr.getString + ("line.number.expected.expect.found.actual.")); + Object[] source = {new Integer(line), expect, actual}; + i18nMessage = form.format(source); + } + + @Override + public String getLocalizedMessage() { + return i18nMessage; + } + } + + public static void main(String arg[]) throws Exception { + try (FileReader fr = new FileReader(arg[0]); + FileWriter fw = new FileWriter(arg[1])) { + PolicyParser pp = new PolicyParser(true); + pp.read(fr); + pp.write(fw); + } + } +} diff --git a/src/sun/security/provider/PolicySpiFile.java b/src/sun/security/provider/PolicySpiFile.java new file mode 100644 index 00000000..e32626a6 --- /dev/null +++ b/src/sun/security/provider/PolicySpiFile.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.PolicySpi; +import java.security.ProtectionDomain; +import java.security.URIParameter; + +import java.net.MalformedURLException; + +/** + * This class wraps the PolicyFile subclass implementation of Policy + * inside a PolicySpi implementation that is available from the SUN provider + * via the Policy.getInstance calls. + * + */ +public final class PolicySpiFile extends PolicySpi { + + private PolicyFile pf; + + public PolicySpiFile(Policy.Parameters params) { + + if (params == null) { + pf = new PolicyFile(); + } else { + if (!(params instanceof URIParameter)) { + throw new IllegalArgumentException + ("Unrecognized policy parameter: " + params); + } + URIParameter uriParam = (URIParameter)params; + try { + pf = new PolicyFile(uriParam.getURI().toURL()); + } catch (MalformedURLException mue) { + throw new IllegalArgumentException("Invalid URIParameter", mue); + } + } + } + + protected PermissionCollection engineGetPermissions(CodeSource codesource) { + return pf.getPermissions(codesource); + } + + protected PermissionCollection engineGetPermissions(ProtectionDomain d) { + return pf.getPermissions(d); + } + + protected boolean engineImplies(ProtectionDomain d, Permission p) { + return pf.implies(d, p); + } + + protected void engineRefresh() { + pf.refresh(); + } +} diff --git a/src/sun/security/provider/SHA.java b/src/sun/security/provider/SHA.java new file mode 100644 index 00000000..05a936bb --- /dev/null +++ b/src/sun/security/provider/SHA.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import static sun.security.provider.ByteArrayAccess.*; + +/** + * This class implements the Secure Hash Algorithm (SHA) developed by + * the National Institute of Standards and Technology along with the + * National Security Agency. This is the updated version of SHA + * fip-180 as superseded by fip-180-1. + * + *

    It implement JavaSecurity MessageDigest, and can be used by in + * the Java Security framework, as a pluggable implementation, as a + * filter for the digest stream classes. + * + * @author Roger Riggs + * @author Benjamin Renaud + * @author Andreas Sterbenz + */ +public final class SHA extends DigestBase { + + // Buffer of int's and count of characters accumulated + // 64 bytes are included in each hash block so the low order + // bits of count are used to know how to pack the bytes into ints + // and to know when to compute the block and start the next one. + private int[] W; + + // state of this + private int[] state; + + /** + * Creates a new SHA object. + */ + public SHA() { + super("SHA-1", 20, 64); + state = new int[5]; + W = new int[80]; + implReset(); + } + + /* + * Clones this object. + */ + public Object clone() throws CloneNotSupportedException { + SHA copy = (SHA) super.clone(); + copy.state = copy.state.clone(); + copy.W = new int[80]; + return copy; + } + + /** + * Resets the buffers and hash value to start a new hash. + */ + void implReset() { + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + state[4] = 0xc3d2e1f0; + } + + /** + * Computes the final hash and copies the 20 bytes to the output array. + */ + void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x3f; + int padLen = (index < 56) ? (56 - index) : (120 - index); + engineUpdate(padding, 0, padLen); + + i2bBig4((int)(bitsProcessed >>> 32), buffer, 56); + i2bBig4((int)bitsProcessed, buffer, 60); + implCompress(buffer, 0); + + i2bBig(state, 0, out, ofs, 20); + } + + // Constants for each round + private final static int round1_kt = 0x5a827999; + private final static int round2_kt = 0x6ed9eba1; + private final static int round3_kt = 0x8f1bbcdc; + private final static int round4_kt = 0xca62c1d6; + + /** + * Compute a the hash for the current block. + * + * This is in the same vein as Peter Gutmann's algorithm listed in + * the back of Applied Cryptography, Compact implementation of + * "old" NIST Secure Hash Algorithm. + */ + void implCompress(byte[] buf, int ofs) { + b2iBig64(buf, ofs, W); + + // The first 16 ints have the byte stream, compute the rest of + // the buffer + for (int t = 16; t <= 79; t++) { + int temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]; + W[t] = (temp << 1) | (temp >>> 31); + } + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + int e = state[4]; + + // Round 1 + for (int i = 0; i < 20; i++) { + int temp = ((a<<5) | (a>>>(32-5))) + + ((b&c)|((~b)&d))+ e + W[i] + round1_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 2 + for (int i = 20; i < 40; i++) { + int temp = ((a<<5) | (a>>>(32-5))) + + (b ^ c ^ d) + e + W[i] + round2_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 3 + for (int i = 40; i < 60; i++) { + int temp = ((a<<5) | (a>>>(32-5))) + + ((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + + // Round 4 + for (int i = 60; i < 80; i++) { + int temp = ((a<<5) | (a>>>(32-5))) + + (b ^ c ^ d) + e + W[i] + round4_kt; + e = d; + d = c; + c = ((b<<30) | (b>>>(32-30))); + b = a; + a = temp; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + } + +} diff --git a/src/sun/security/provider/SHA2.java b/src/sun/security/provider/SHA2.java new file mode 100644 index 00000000..23007c96 --- /dev/null +++ b/src/sun/security/provider/SHA2.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import static sun.security.provider.ByteArrayAccess.*; + +/** + * This class implements the Secure Hash Algorithm SHA-256 developed by + * the National Institute of Standards and Technology along with the + * National Security Agency. + * + *

    It implements java.security.MessageDigestSpi, and can be used + * through Java Cryptography Architecture (JCA), as a pluggable + * MessageDigest implementation. + * + * @since 1.4.2 + * @author Valerie Peng + * @author Andreas Sterbenz + */ +abstract class SHA2 extends DigestBase { + + private static final int ITERATION = 64; + // Constants for each round + private static final int[] ROUND_CONSTS = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + // buffer used by implCompress() + private int[] W; + + // state of this object + private int[] state; + + // initial state value. different between SHA-224 and SHA-256 + private final int[] initialHashes; + + /** + * Creates a new SHA object. + */ + SHA2(String name, int digestLength, int[] initialHashes) { + super(name, digestLength, 64); + this.initialHashes = initialHashes; + state = new int[8]; + W = new int[64]; + implReset(); + } + + /** + * Resets the buffers and hash value to start a new hash. + */ + void implReset() { + System.arraycopy(initialHashes, 0, state, 0, state.length); + } + + void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x3f; + int padLen = (index < 56) ? (56 - index) : (120 - index); + engineUpdate(padding, 0, padLen); + + i2bBig4((int)(bitsProcessed >>> 32), buffer, 56); + i2bBig4((int)bitsProcessed, buffer, 60); + implCompress(buffer, 0); + + i2bBig(state, 0, out, ofs, engineGetDigestLength()); + } + + /** + * logical function ch(x,y,z) as defined in spec: + * @return (x and y) xor ((complement x) and z) + * @param x int + * @param y int + * @param z int + */ + private static int lf_ch(int x, int y, int z) { + return (x & y) ^ ((~x) & z); + } + + /** + * logical function maj(x,y,z) as defined in spec: + * @return (x and y) xor (x and z) xor (y and z) + * @param x int + * @param y int + * @param z int + */ + private static int lf_maj(int x, int y, int z) { + return (x & y) ^ (x & z) ^ (y & z); + } + + /** + * logical function R(x,s) - right shift + * @return x right shift for s times + * @param x int + * @param s int + */ + private static int lf_R( int x, int s ) { + return (x >>> s); + } + + /** + * logical function S(x,s) - right rotation + * @return x circular right shift for s times + * @param x int + * @param s int + */ + private static int lf_S(int x, int s) { + return (x >>> s) | (x << (32 - s)); + } + + /** + * logical function sigma0(x) - xor of results of right rotations + * @return S(x,2) xor S(x,13) xor S(x,22) + * @param x int + */ + private static int lf_sigma0(int x) { + return lf_S(x, 2) ^ lf_S(x, 13) ^ lf_S(x, 22); + } + + /** + * logical function sigma1(x) - xor of results of right rotations + * @return S(x,6) xor S(x,11) xor S(x,25) + * @param x int + */ + private static int lf_sigma1(int x) { + return lf_S( x, 6 ) ^ lf_S( x, 11 ) ^ lf_S( x, 25 ); + } + + /** + * logical function delta0(x) - xor of results of right shifts/rotations + * @return int + * @param x int + */ + private static int lf_delta0(int x) { + return lf_S(x, 7) ^ lf_S(x, 18) ^ lf_R(x, 3); + } + + /** + * logical function delta1(x) - xor of results of right shifts/rotations + * @return int + * @param x int + */ + private static int lf_delta1(int x) { + return lf_S(x, 17) ^ lf_S(x, 19) ^ lf_R(x, 10); + } + + /** + * Process the current block to update the state variable state. + */ + void implCompress(byte[] buf, int ofs) { + b2iBig64(buf, ofs, W); + + // The first 16 ints are from the byte stream, compute the rest of + // the W[]'s + for (int t = 16; t < ITERATION; t++) { + W[t] = lf_delta1(W[t-2]) + W[t-7] + lf_delta0(W[t-15]) + + W[t-16]; + } + + int a = state[0]; + int b = state[1]; + int c = state[2]; + int d = state[3]; + int e = state[4]; + int f = state[5]; + int g = state[6]; + int h = state[7]; + + for (int i = 0; i < ITERATION; i++) { + int T1 = h + lf_sigma1(e) + lf_ch(e,f,g) + ROUND_CONSTS[i] + W[i]; + int T2 = lf_sigma0(a) + lf_maj(a,b,c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + } + + public Object clone() throws CloneNotSupportedException { + SHA2 copy = (SHA2) super.clone(); + copy.state = copy.state.clone(); + copy.W = new int[64]; + return copy; + } + + /** + * SHA-224 implementation class. + */ + public static final class SHA224 extends SHA2 { + private static final int[] INITIAL_HASHES = { + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + }; + + public SHA224() { + super("SHA-224", 28, INITIAL_HASHES); + } + } + + /** + * SHA-256 implementation class. + */ + public static final class SHA256 extends SHA2 { + private static final int[] INITIAL_HASHES = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 + }; + + public SHA256() { + super("SHA-256", 32, INITIAL_HASHES); + } + } +} diff --git a/src/sun/security/provider/SHA5.java b/src/sun/security/provider/SHA5.java new file mode 100644 index 00000000..2d0e2cc1 --- /dev/null +++ b/src/sun/security/provider/SHA5.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import static sun.security.provider.ByteArrayAccess.*; + +/** + * This class implements the Secure Hash Algorithm SHA-384 and SHA-512 + * developed by the National Institute of Standards and Technology along + * with the National Security Agency. + * + * The two algorithms are almost identical. This file contains a base + * class SHA5 and two nested static subclasses as the classes to be used + * by the JCA framework. + * + *

    It implements java.security.MessageDigestSpi, and can be used + * through Java Cryptography Architecture (JCA), as a pluggable + * MessageDigest implementation. + * + * @since 1.4.2 + * @author Valerie Peng + * @author Andreas Sterbenz + */ +abstract class SHA5 extends DigestBase { + + private static final int ITERATION = 80; + // Constants for each round/iteration + private static final long[] ROUND_CONSTS = { + 0x428A2F98D728AE22L, 0x7137449123EF65CDL, 0xB5C0FBCFEC4D3B2FL, + 0xE9B5DBA58189DBBCL, 0x3956C25BF348B538L, 0x59F111F1B605D019L, + 0x923F82A4AF194F9BL, 0xAB1C5ED5DA6D8118L, 0xD807AA98A3030242L, + 0x12835B0145706FBEL, 0x243185BE4EE4B28CL, 0x550C7DC3D5FFB4E2L, + 0x72BE5D74F27B896FL, 0x80DEB1FE3B1696B1L, 0x9BDC06A725C71235L, + 0xC19BF174CF692694L, 0xE49B69C19EF14AD2L, 0xEFBE4786384F25E3L, + 0x0FC19DC68B8CD5B5L, 0x240CA1CC77AC9C65L, 0x2DE92C6F592B0275L, + 0x4A7484AA6EA6E483L, 0x5CB0A9DCBD41FBD4L, 0x76F988DA831153B5L, + 0x983E5152EE66DFABL, 0xA831C66D2DB43210L, 0xB00327C898FB213FL, + 0xBF597FC7BEEF0EE4L, 0xC6E00BF33DA88FC2L, 0xD5A79147930AA725L, + 0x06CA6351E003826FL, 0x142929670A0E6E70L, 0x27B70A8546D22FFCL, + 0x2E1B21385C26C926L, 0x4D2C6DFC5AC42AEDL, 0x53380D139D95B3DFL, + 0x650A73548BAF63DEL, 0x766A0ABB3C77B2A8L, 0x81C2C92E47EDAEE6L, + 0x92722C851482353BL, 0xA2BFE8A14CF10364L, 0xA81A664BBC423001L, + 0xC24B8B70D0F89791L, 0xC76C51A30654BE30L, 0xD192E819D6EF5218L, + 0xD69906245565A910L, 0xF40E35855771202AL, 0x106AA07032BBD1B8L, + 0x19A4C116B8D2D0C8L, 0x1E376C085141AB53L, 0x2748774CDF8EEB99L, + 0x34B0BCB5E19B48A8L, 0x391C0CB3C5C95A63L, 0x4ED8AA4AE3418ACBL, + 0x5B9CCA4F7763E373L, 0x682E6FF3D6B2B8A3L, 0x748F82EE5DEFB2FCL, + 0x78A5636F43172F60L, 0x84C87814A1F0AB72L, 0x8CC702081A6439ECL, + 0x90BEFFFA23631E28L, 0xA4506CEBDE82BDE9L, 0xBEF9A3F7B2C67915L, + 0xC67178F2E372532BL, 0xCA273ECEEA26619CL, 0xD186B8C721C0C207L, + 0xEADA7DD6CDE0EB1EL, 0xF57D4F7FEE6ED178L, 0x06F067AA72176FBAL, + 0x0A637DC5A2C898A6L, 0x113F9804BEF90DAEL, 0x1B710B35131C471BL, + 0x28DB77F523047D84L, 0x32CAAB7B40C72493L, 0x3C9EBE0A15C9BEBCL, + 0x431D67C49C100D4CL, 0x4CC5D4BECB3E42B6L, 0x597F299CFC657E2AL, + 0x5FCB6FAB3AD6FAECL, 0x6C44198C4A475817L + }; + + // buffer used by implCompress() + private long[] W; + + // state of this object + private long[] state; + + // initial state value. different between SHA-384 and SHA-512 + private final long[] initialHashes; + + /** + * Creates a new SHA object. + */ + SHA5(String name, int digestLength, long[] initialHashes) { + super(name, digestLength, 128); + this.initialHashes = initialHashes; + state = new long[8]; + W = new long[80]; + implReset(); + } + + final void implReset() { + System.arraycopy(initialHashes, 0, state, 0, state.length); + } + + final void implDigest(byte[] out, int ofs) { + long bitsProcessed = bytesProcessed << 3; + + int index = (int)bytesProcessed & 0x7f; + int padLen = (index < 112) ? (112 - index) : (240 - index); + engineUpdate(padding, 0, padLen + 8); + + i2bBig4((int)(bitsProcessed >>> 32), buffer, 120); + i2bBig4((int)bitsProcessed, buffer, 124); + implCompress(buffer, 0); + + int len = engineGetDigestLength(); + if (len == 28) { + // Special case for SHA-512/224 + l2bBig(state, 0, out, ofs, 24); + i2bBig4((int)(state[3] >> 32), out, ofs + 24); + } else { + l2bBig(state, 0, out, ofs, len); + } + } + + /** + * logical function ch(x,y,z) as defined in spec: + * @return (x and y) xor ((complement x) and z) + * @param x long + * @param y long + * @param z long + */ + private static long lf_ch(long x, long y, long z) { + return (x & y) ^ ((~x) & z); + } + + /** + * logical function maj(x,y,z) as defined in spec: + * @return (x and y) xor (x and z) xor (y and z) + * @param x long + * @param y long + * @param z long + */ + private static long lf_maj(long x, long y, long z) { + return (x & y) ^ (x & z) ^ (y & z); + } + + /** + * logical function R(x,s) - right shift + * @return x right shift for s times + * @param x long + * @param s int + */ + private static long lf_R(long x, int s) { + return (x >>> s); + } + + /** + * logical function S(x,s) - right rotation + * @return x circular right shift for s times + * @param x long + * @param s int + */ + private static long lf_S(long x, int s) { + return (x >>> s) | (x << (64 - s)); + } + + /** + * logical function sigma0(x) - xor of results of right rotations + * @return S(x,28) xor S(x,34) xor S(x,39) + * @param x long + */ + private static long lf_sigma0(long x) { + return lf_S(x, 28) ^ lf_S(x, 34) ^ lf_S(x, 39); + } + + /** + * logical function sigma1(x) - xor of results of right rotations + * @return S(x,14) xor S(x,18) xor S(x,41) + * @param x long + */ + private static long lf_sigma1(long x) { + return lf_S(x, 14) ^ lf_S(x, 18) ^ lf_S(x, 41); + } + + /** + * logical function delta0(x) - xor of results of right shifts/rotations + * @return long + * @param x long + */ + private static long lf_delta0(long x) { + return lf_S(x, 1) ^ lf_S(x, 8) ^ lf_R(x, 7); + } + + /** + * logical function delta1(x) - xor of results of right shifts/rotations + * @return long + * @param x long + */ + private static long lf_delta1(long x) { + return lf_S(x, 19) ^ lf_S(x, 61) ^ lf_R(x, 6); + } + + /** + * Compute the hash for the current block. + * + * This is in the same vein as Peter Gutmann's algorithm listed in + * the back of Applied Cryptography, Compact implementation of + * "old" NIST Secure Hash Algorithm. + */ + final void implCompress(byte[] buf, int ofs) { + b2lBig128(buf, ofs, W); + + // The first 16 longs are from the byte stream, compute the rest of + // the W[]'s + for (int t = 16; t < ITERATION; t++) { + W[t] = lf_delta1(W[t-2]) + W[t-7] + lf_delta0(W[t-15]) + + W[t-16]; + } + + long a = state[0]; + long b = state[1]; + long c = state[2]; + long d = state[3]; + long e = state[4]; + long f = state[5]; + long g = state[6]; + long h = state[7]; + + for (int i = 0; i < ITERATION; i++) { + long T1 = h + lf_sigma1(e) + lf_ch(e,f,g) + ROUND_CONSTS[i] + W[i]; + long T2 = lf_sigma0(a) + lf_maj(a,b,c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + state[5] += f; + state[6] += g; + state[7] += h; + } + + public Object clone() throws CloneNotSupportedException { + SHA5 copy = (SHA5) super.clone(); + copy.state = copy.state.clone(); + copy.W = new long[80]; + return copy; + } + + /** + * SHA-512 implementation class. + */ + public static final class SHA512 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, + 0x3c6ef372fe94f82bL, 0xa54ff53a5f1d36f1L, + 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, + 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L + }; + + public SHA512() { + super("SHA-512", 64, INITIAL_HASHES); + } + } + + /** + * SHA-384 implementation class. + */ + public static final class SHA384 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0xcbbb9d5dc1059ed8L, 0x629a292a367cd507L, + 0x9159015a3070dd17L, 0x152fecd8f70e5939L, + 0x67332667ffc00b31L, 0x8eb44a8768581511L, + 0xdb0c2e0d64f98fa7L, 0x47b5481dbefa4fa4L + }; + + public SHA384() { + super("SHA-384", 48, INITIAL_HASHES); + } + } + public static final class SHA512_224 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0x8C3D37C819544DA2L, 0x73E1996689DCD4D6L, + 0x1DFAB7AE32FF9C82L, 0x679DD514582F9FCFL, + 0x0F6D2B697BD44DA8L, 0x77E36F7304C48942L, + 0x3F9D85A86A1D36C8L, 0x1112E6AD91D692A1L + }; + + public SHA512_224() { + super("SHA-512/224", 28, INITIAL_HASHES); + } + } + + public static final class SHA512_256 extends SHA5 { + + private static final long[] INITIAL_HASHES = { + 0x22312194FC2BF72CL, 0x9F555FA3C84C64C2L, + 0x2393B86B6F53B151L, 0x963877195940EABDL, + 0x96283EE2A88EFFE3L, 0xBE5E1E2553863992L, + 0x2B0199FC2C85B8AAL, 0x0EB72DDC81C52CA2L + }; + + public SHA512_256() { + super("SHA-512/256", 32, INITIAL_HASHES); + } + } +} diff --git a/src/sun/security/provider/SecureRandom.java b/src/sun/security/provider/SecureRandom.java new file mode 100644 index 00000000..4f7d7c3a --- /dev/null +++ b/src/sun/security/provider/SecureRandom.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.SecureRandomSpi; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +/** + *

    This class provides a crytpographically strong pseudo-random number + * generator based on the SHA-1 hash algorithm. + * + *

    Note that if a seed is not provided, we attempt to provide sufficient + * seed bytes to completely randomize the internal state of the generator + * (20 bytes). However, our seed generation algorithm has not been thoroughly + * studied or widely deployed. + * + *

    Also note that when a random object is deserialized, + * engineNextBytes invoked on the + * restored random object will yield the exact same (random) bytes as the + * original object. If this behaviour is not desired, the restored random + * object should be seeded, using + * engineSetSeed. + * + * @author Benjamin Renaud + * @author Josh Bloch + * @author Gadi Guy + */ + +public final class SecureRandom extends SecureRandomSpi +implements java.io.Serializable { + + private static final long serialVersionUID = 3581829991155417889L; + + private static final int DIGEST_SIZE = 20; + private transient MessageDigest digest; + private byte[] state; + private byte[] remainder; + private int remCount; + + /** + * This empty constructor automatically seeds the generator. We attempt + * to provide sufficient seed bytes to completely randomize the internal + * state of the generator (20 bytes). Note, however, that our seed + * generation algorithm has not been thoroughly studied or widely deployed. + * + *

    The first time this constructor is called in a given Virtual Machine, + * it may take several seconds of CPU time to seed the generator, depending + * on the underlying hardware. Successive calls run quickly because they + * rely on the same (internal) pseudo-random number generator for their + * seed bits. + */ + public SecureRandom() { + init(null); + } + + /** + * This constructor is used to instantiate the private seeder object + * with a given seed from the SeedGenerator. + * + * @param seed the seed. + */ + private SecureRandom(byte seed[]) { + init(seed); + } + + /** + * This call, used by the constructors, instantiates the SHA digest + * and sets the seed, if given. + */ + private void init(byte[] seed) { + try { + /* + * Use the local SUN implementation to avoid native + * performance overhead. + */ + digest = MessageDigest.getInstance("SHA", "SUN"); + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { + // Fallback to any available. + try { + digest = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException exc) { + throw new InternalError( + "internal error: SHA-1 not available.", exc); + } + } + + if (seed != null) { + engineSetSeed(seed); + } + } + + /** + * Returns the given number of seed bytes, computed using the seed + * generation algorithm that this class uses to seed itself. This + * call may be used to seed other random number generators. While + * we attempt to return a "truly random" sequence of bytes, we do not + * know exactly how random the bytes returned by this call are. (See + * the empty constructor SecureRandom + * for a brief description of the underlying algorithm.) + * The prudent user will err on the side of caution and get extra + * seed bytes, although it should be noted that seed generation is + * somewhat costly. + * + * @param numBytes the number of seed bytes to generate. + * + * @return the seed bytes. + */ + @Override + public byte[] engineGenerateSeed(int numBytes) { + // Neither of the SeedGenerator implementations require + // locking, so no sync needed here. + byte[] b = new byte[numBytes]; + SeedGenerator.generateSeed(b); + return b; + } + + /** + * Reseeds this random object. The given seed supplements, rather than + * replaces, the existing seed. Thus, repeated calls are guaranteed + * never to reduce randomness. + * + * @param seed the seed. + */ + @Override + synchronized public void engineSetSeed(byte[] seed) { + if (state != null) { + digest.update(state); + for (int i = 0; i < state.length; i++) { + state[i] = 0; + } + } + state = digest.digest(seed); + } + + private static void updateState(byte[] state, byte[] output) { + int last = 1; + int v; + byte t; + boolean zf = false; + + // state(n + 1) = (state(n) + output(n) + 1) % 2^160; + for (int i = 0; i < state.length; i++) { + // Add two bytes + v = (int)state[i] + (int)output[i] + last; + // Result is lower 8 bits + t = (byte)v; + // Store result. Check for state collision. + zf = zf | (state[i] != t); + state[i] = t; + // High 8 bits are carry. Store for next iteration. + last = v >> 8; + } + + // Make sure at least one bit changes! + if (!zf) { + state[0]++; + } + } + + /** + * This static object will be seeded by SeedGenerator, and used + * to seed future instances of SHA1PRNG SecureRandoms. + * + * Bloch, Effective Java Second Edition: Item 71 + */ + private static class SeederHolder { + + private static final SecureRandom seeder; + + static { + /* + * Call to SeedGenerator.generateSeed() to add additional + * seed material (likely from the Native implementation). + */ + seeder = new SecureRandom(SeedGenerator.getSystemEntropy()); + byte [] b = new byte[DIGEST_SIZE]; + SeedGenerator.generateSeed(b); + seeder.engineSetSeed(b); + } + } + + /** + * Generates a user-specified number of random bytes. + * + * @param bytes the array to be filled in with random bytes. + */ + @Override + public synchronized void engineNextBytes(byte[] result) { + int index = 0; + int todo; + byte[] output = remainder; + + if (state == null) { + byte[] seed = new byte[DIGEST_SIZE]; + SeederHolder.seeder.engineNextBytes(seed); + state = digest.digest(seed); + } + + // Use remainder from last time + int r = remCount; + if (r > 0) { + // How many bytes? + todo = (result.length - index) < (DIGEST_SIZE - r) ? + (result.length - index) : (DIGEST_SIZE - r); + // Copy the bytes, zero the buffer + for (int i = 0; i < todo; i++) { + result[i] = output[r]; + output[r++] = 0; + } + remCount += todo; + index += todo; + } + + // If we need more bytes, make them. + while (index < result.length) { + // Step the state + digest.update(state); + output = digest.digest(); + updateState(state, output); + + // How many bytes? + todo = (result.length - index) > DIGEST_SIZE ? + DIGEST_SIZE : result.length - index; + // Copy the bytes, zero the buffer + for (int i = 0; i < todo; i++) { + result[index++] = output[i]; + output[i] = 0; + } + remCount += todo; + } + + // Store remainder for next time + remainder = output; + remCount %= DIGEST_SIZE; + } + + /* + * readObject is called to restore the state of the random object from + * a stream. We have to create a new instance of MessageDigest, because + * it is not included in the stream (it is marked "transient"). + * + * Note that the engineNextBytes() method invoked on the restored random + * object will yield the exact same (random) bytes as the original. + * If you do not want this behaviour, you should re-seed the restored + * random object, using engineSetSeed(). + */ + private void readObject(java.io.ObjectInputStream s) + throws IOException, ClassNotFoundException { + + s.defaultReadObject (); + + try { + /* + * Use the local SUN implementation to avoid native + * performance overhead. + */ + digest = MessageDigest.getInstance("SHA", "SUN"); + } catch (NoSuchProviderException | NoSuchAlgorithmException e) { + // Fallback to any available. + try { + digest = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException exc) { + throw new InternalError( + "internal error: SHA-1 not available.", exc); + } + } + } +} diff --git a/src/sun/security/provider/SeedGenerator.java b/src/sun/security/provider/SeedGenerator.java new file mode 100644 index 00000000..1ccb421e --- /dev/null +++ b/src/sun/security/provider/SeedGenerator.java @@ -0,0 +1,554 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +/** + * This class generates seeds for the SHA1PRNG cryptographically strong + * random number generator. + *

    + * The seed is produced using one of two techniques, via a computation + * of current system activity or from an entropy gathering device. + *

    + * In the default technique the seed is produced by counting the + * number of times the VM manages to loop in a given period. This number + * roughly reflects the machine load at that point in time. + * The samples are translated using a permutation (s-box) + * and then XORed together. This process is non linear and + * should prevent the samples from "averaging out". The s-box + * was designed to have even statistical distribution; it's specific + * values are not crucial for the security of the seed. + * We also create a number of sleeper threads which add entropy + * to the system by keeping the scheduler busy. + * Twenty such samples should give us roughly 160 bits of randomness. + *

    + * These values are gathered in the background by a daemon thread + * thus allowing the system to continue performing it's different + * activites, which in turn add entropy to the random seed. + *

    + * The class also gathers miscellaneous system information, some + * machine dependent, some not. This information is then hashed together + * with the 20 seed bytes. + *

    + * The alternative to the above approach is to acquire seed material + * from an entropy gathering device, such as /dev/random. This can be + * accomplished by setting the value of the {@code securerandom.source} + * Security property to a URL specifying the location of the entropy + * gathering device, or by setting the {@code java.security.egd} System + * property. + *

    + * In the event the specified URL cannot be accessed the default + * threading mechanism is used. + * + * @author Joshua Bloch + * @author Gadi Guy + */ + +import java.security.*; +import java.io.*; +import java.util.Properties; +import java.util.Enumeration; +import java.net.*; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Random; +import sun.security.util.Debug; + +abstract class SeedGenerator { + + // Static instance is created at link time + private static SeedGenerator instance; + + private static final Debug debug = Debug.getInstance("provider"); + + // Static initializer to hook in selected or best performing generator + static { + String egdSource = SunEntries.getSeedSource(); + + /* + * Try the URL specifying the source (e.g. file:/dev/random) + * + * The URLs "file:/dev/random" or "file:/dev/urandom" are used to + * indicate the SeedGenerator should use OS support, if available. + * + * On Windows, this causes the MS CryptoAPI seeder to be used. + * + * On Solaris/Linux/MacOS, this is identical to using + * URLSeedGenerator to read from /dev/[u]random + */ + if (egdSource.equals(SunEntries.URL_DEV_RANDOM) || + egdSource.equals(SunEntries.URL_DEV_URANDOM)) { + try { + instance = new NativeSeedGenerator(egdSource); + if (debug != null) { + debug.println( + "Using operating system seed generator" + egdSource); + } + } catch (IOException e) { + if (debug != null) { + debug.println("Failed to use operating system seed " + + "generator: " + e.toString()); + } + } + } else if (egdSource.length() != 0) { + try { + instance = new URLSeedGenerator(egdSource); + if (debug != null) { + debug.println("Using URL seed generator reading from " + + egdSource); + } + } catch (IOException e) { + if (debug != null) { + debug.println("Failed to create seed generator with " + + egdSource + ": " + e.toString()); + } + } + } + + // Fall back to ThreadedSeedGenerator + if (instance == null) { + if (debug != null) { + debug.println("Using default threaded seed generator"); + } + instance = new ThreadedSeedGenerator(); + } + } + + /** + * Fill result with bytes from the queue. Wait for it if it isn't ready. + */ + static public void generateSeed(byte[] result) { + instance.getSeedBytes(result); + } + + abstract void getSeedBytes(byte[] result); + + /** + * Retrieve some system information, hashed. + */ + static byte[] getSystemEntropy() { + byte[] ba; + final MessageDigest md; + + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException nsae) { + throw new InternalError("internal error: SHA-1 not available." + , nsae); + } + + // The current time in millis + byte b =(byte)System.currentTimeMillis(); + md.update(b); + + AccessController.doPrivileged + (new PrivilegedAction() { + @Override + public Void run() { + try { + // System properties can change from machine to machine + String s; + Properties p = System.getProperties(); + Enumeration e = p.propertyNames(); + while (e.hasMoreElements()) { + s =(String)e.nextElement(); + md.update(s.getBytes()); + md.update(p.getProperty(s).getBytes()); + } + + // Include network adapter names (and a Mac address) + addNetworkAdapterInfo(md); + + // The temporary dir + File f = new File(p.getProperty("java.io.tmpdir")); + int count = 0; + try ( + DirectoryStream stream = + Files.newDirectoryStream(f.toPath())) { + // We use a Random object to choose what file names + // should be used. Otherwise on a machine with too + // many files, the same first 1024 files always get + // used. Any, We make sure the first 512 files are + // always used. + Random r = new Random(); + for (Path entry: stream) { + if (count < 512 || r.nextBoolean()) { + md.update(entry.getFileName() + .toString().getBytes()); + } + if (count++ > 1024) { + break; + } + } + } + } catch (Exception ex) { + md.update((byte)ex.hashCode()); + } + + // get Runtime memory stats + Runtime rt = Runtime.getRuntime(); + byte[] memBytes = longToByteArray(rt.totalMemory()); + md.update(memBytes, 0, memBytes.length); + memBytes = longToByteArray(rt.freeMemory()); + md.update(memBytes, 0, memBytes.length); + + return null; + } + }); + return md.digest(); + } + + /* + * Include network adapter names and, if available, a Mac address + * + * See also java.util.concurrent.ThreadLocalRandom.initialSeed() + */ + private static void addNetworkAdapterInfo(MessageDigest md) { + + try { + Enumeration ifcs = + NetworkInterface.getNetworkInterfaces(); + while (ifcs.hasMoreElements()) { + NetworkInterface ifc = ifcs.nextElement(); + md.update(ifc.toString().getBytes()); + if (!ifc.isVirtual()) { // skip fake addresses + byte[] bs = ifc.getHardwareAddress(); + if (bs != null) { + md.update(bs); + break; + } + } + } + } catch (Exception ignore) { + } + } + + /** + * Helper function to convert a long into a byte array (least significant + * byte first). + */ + private static byte[] longToByteArray(long l) { + byte[] retVal = new byte[8]; + + for (int i=0; i<8; i++) { + retVal[i] = (byte) l; + l >>= 8; + } + + return retVal; + } + + /* + // This method helps the test utility receive unprocessed seed bytes. + public static int genTestSeed() { + return myself.getByte(); + } + */ + + + private static class ThreadedSeedGenerator extends SeedGenerator + implements Runnable { + // Queue is used to collect seed bytes + private byte[] pool; + private int start, end, count; + + // Thread group for our threads + ThreadGroup seedGroup; + + /** + * The constructor is only called once to construct the one + * instance we actually use. It instantiates the message digest + * and starts the thread going. + */ + ThreadedSeedGenerator() { + pool = new byte[20]; + start = end = 0; + + MessageDigest digest; + + try { + digest = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException e) { + throw new InternalError("internal error: SHA-1 not available." + , e); + } + + final ThreadGroup[] finalsg = new ThreadGroup[1]; + Thread t = AccessController.doPrivileged + (new PrivilegedAction() { + @Override + public Thread run() { + ThreadGroup parent, group = + Thread.currentThread().getThreadGroup(); + while ((parent = group.getParent()) != null) { + group = parent; + } + finalsg[0] = new ThreadGroup + (group, "SeedGenerator ThreadGroup"); + Thread newT = new Thread(finalsg[0], + ThreadedSeedGenerator.this, + "SeedGenerator Thread"); + newT.setPriority(Thread.MIN_PRIORITY); + newT.setDaemon(true); + return newT; + } + }); + seedGroup = finalsg[0]; + t.start(); + } + + /** + * This method does the actual work. It collects random bytes and + * pushes them into the queue. + */ + @Override + final public void run() { + try { + while (true) { + // Queue full? Wait till there's room. + synchronized(this) { + while (count >= pool.length) { + wait(); + } + } + + int counter, quanta; + byte v = 0; + + // Spin count must not be under 64000 + for (counter = quanta = 0; + (counter < 64000) && (quanta < 6); quanta++) { + + // Start some noisy threads + try { + BogusThread bt = new BogusThread(); + Thread t = new Thread + (seedGroup, bt, "SeedGenerator Thread"); + t.start(); + } catch (Exception e) { + throw new InternalError("internal error: " + + "SeedGenerator thread creation error.", e); + } + + // We wait 250milli quanta, so the minimum wait time + // cannot be under 250milli. + int latch = 0; + long l = System.currentTimeMillis() + 250; + while (System.currentTimeMillis() < l) { + synchronized(this){}; + latch++; + } + + // Translate the value using the permutation, and xor + // it with previous values gathered. + v ^= rndTab[latch % 255]; + counter += latch; + } + + // Push it into the queue and notify anybody who might + // be waiting for it. + synchronized(this) { + pool[end] = v; + end++; + count++; + if (end >= pool.length) { + end = 0; + } + + notifyAll(); + } + } + } catch (Exception e) { + throw new InternalError("internal error: " + + "SeedGenerator thread generated an exception.", e); + } + } + + @Override + void getSeedBytes(byte[] result) { + for (int i = 0; i < result.length; i++) { + result[i] = getSeedByte(); + } + } + + byte getSeedByte() { + byte b; + + try { + // Wait for it... + synchronized(this) { + while (count <= 0) { + wait(); + } + } + } catch (Exception e) { + if (count <= 0) { + throw new InternalError("internal error: " + + "SeedGenerator thread generated an exception.", e); + } + } + + synchronized(this) { + // Get it from the queue + b = pool[start]; + pool[start] = 0; + start++; + count--; + if (start == pool.length) { + start = 0; + } + + // Notify the daemon thread, just in case it is + // waiting for us to make room in the queue. + notifyAll(); + } + + return b; + } + + // The permutation was calculated by generating 64k of random + // data and using it to mix the trivial permutation. + // It should be evenly distributed. The specific values + // are not crucial to the security of this class. + private static byte[] rndTab = { + 56, 30, -107, -6, -86, 25, -83, 75, -12, -64, + 5, -128, 78, 21, 16, 32, 70, -81, 37, -51, + -43, -46, -108, 87, 29, 17, -55, 22, -11, -111, + -115, 84, -100, 108, -45, -15, -98, 72, -33, -28, + 31, -52, -37, -117, -97, -27, 93, -123, 47, 126, + -80, -62, -93, -79, 61, -96, -65, -5, -47, -119, + 14, 89, 81, -118, -88, 20, 67, -126, -113, 60, + -102, 55, 110, 28, 85, 121, 122, -58, 2, 45, + 43, 24, -9, 103, -13, 102, -68, -54, -101, -104, + 19, 13, -39, -26, -103, 62, 77, 51, 44, 111, + 73, 18, -127, -82, 4, -30, 11, -99, -74, 40, + -89, 42, -76, -77, -94, -35, -69, 35, 120, 76, + 33, -73, -7, 82, -25, -10, 88, 125, -112, 58, + 83, 95, 6, 10, 98, -34, 80, 15, -91, 86, + -19, 52, -17, 117, 49, -63, 118, -90, 36, -116, + -40, -71, 97, -53, -109, -85, 109, -16, -3, 104, + -95, 68, 54, 34, 26, 114, -1, 106, -121, 3, + 66, 0, 100, -84, 57, 107, 119, -42, 112, -61, + 1, 48, 38, 12, -56, -57, 39, -106, -72, 41, + 7, 71, -29, -59, -8, -38, 79, -31, 124, -124, + 8, 91, 116, 99, -4, 9, -36, -78, 63, -49, + -67, -87, 59, 101, -32, 92, 94, 53, -41, 115, + -66, -70, -122, 50, -50, -22, -20, -18, -21, 23, + -2, -48, 96, 65, -105, 123, -14, -110, 69, -24, + -120, -75, 74, 127, -60, 113, 90, -114, 105, 46, + 27, -125, -23, -44, 64 + }; + + /** + * This inner thread causes the thread scheduler to become 'noisy', + * thus adding entropy to the system load. + * At least one instance of this class is generated for every seed byte. + */ + private static class BogusThread implements Runnable { + @Override + final public void run() { + try { + for (int i = 0; i < 5; i++) { + Thread.sleep(50); + } + // System.gc(); + } catch (Exception e) { + } + } + } + } + + static class URLSeedGenerator extends SeedGenerator { + + private String deviceName; + private InputStream seedStream; + + /** + * The constructor is only called once to construct the one + * instance we actually use. It opens the entropy gathering device + * which will supply the randomness. + */ + + URLSeedGenerator(String egdurl) throws IOException { + if (egdurl == null) { + throw new IOException("No random source specified"); + } + deviceName = egdurl; + init(); + } + + private void init() throws IOException { + final URL device = new URL(deviceName); + try { + seedStream = AccessController.doPrivileged + (new PrivilegedExceptionAction() { + @Override + public InputStream run() throws IOException { + /* + * return a FileInputStream for file URLs and + * avoid buffering. The openStream() call wraps + * InputStream in a BufferedInputStream which + * can buffer up to 8K bytes. This read is a + * performance issue for entropy sources which + * can be slow to replenish. + */ + if (device.getProtocol().equalsIgnoreCase("file")) { + File deviceFile = + SunEntries.getDeviceFile(device); + return new FileInputStream(deviceFile); + } else { + return device.openStream(); + } + } + }); + } catch (Exception e) { + throw new IOException( + "Failed to open " + deviceName, e.getCause()); + } + } + + @Override + void getSeedBytes(byte[] result) { + int len = result.length; + int read = 0; + try { + while (read < len) { + int count = seedStream.read(result, read, len - read); + // /dev/random blocks - should never have EOF + if (count < 0) { + throw new InternalError( + "URLSeedGenerator " + deviceName + + " reached end of file"); + } + read += count; + } + } catch (IOException ioe) { + throw new InternalError("URLSeedGenerator " + deviceName + + " generated exception: " + ioe.getMessage(), ioe); + } + } + } +} diff --git a/src/sun/security/provider/SubjectCodeSource.java b/src/sun/security/provider/SubjectCodeSource.java new file mode 100644 index 00000000..2f3ae105 --- /dev/null +++ b/src/sun/security/provider/SubjectCodeSource.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.net.URL; +import java.util.*; +import java.security.CodeSource; +import java.security.Principal; +import java.security.cert.Certificate; +import java.lang.reflect.Constructor; + +import javax.security.auth.Subject; +import sun.security.provider.PolicyParser.PrincipalEntry; + +/** + *

    This SubjectCodeSource class contains + * a URL, signer certificates, and either a Subject + * (that represents the Subject in the current + * AccessControlContext), or a linked list of Principals + * (that represent a "subject" in a Policy). + * + */ +class SubjectCodeSource extends CodeSource implements java.io.Serializable { + + private static final long serialVersionUID = 6039418085604715275L; + + private static final ResourceBundle rb = + java.security.AccessController.doPrivileged + (new java.security.PrivilegedAction() { + public ResourceBundle run() { + return (ResourceBundle.getBundle + ("sun.security.util.AuthResources")); + } + }); + + private Subject subject; + private LinkedList principals; + private static final Class[] PARAMS = { String.class }; + private static final sun.security.util.Debug debug = + sun.security.util.Debug.getInstance("auth", "\t[Auth Access]"); + private ClassLoader sysClassLoader; + + /** + * Creates a new SubjectCodeSource + * with the given Subject, principals, URL, + * and signers (Certificates). The Subject + * represents the Subject associated with the current + * AccessControlContext. + * The Principals are given as a LinkedList + * of PolicyParser.PrincipalEntry objects. + * Typically either a Subject will be provided, + * or a list of principals will be provided + * (not both). + * + *

    + * + * @param subject the Subject associated with this + * SubjectCodeSource

    + * + * @param url the URL associated with this + * SubjectCodeSource

    + * + * @param certs the signers associated with this + * SubjectCodeSource

    + */ + SubjectCodeSource(Subject subject, + LinkedList principals, + URL url, Certificate[] certs) { + + super(url, certs); + this.subject = subject; + this.principals = (principals == null ? + new LinkedList() : + new LinkedList(principals)); + sysClassLoader = java.security.AccessController.doPrivileged + (new java.security.PrivilegedAction() { + public ClassLoader run() { + return ClassLoader.getSystemClassLoader(); + } + }); + } + + /** + * Get the Principals associated with this SubjectCodeSource. + * The Principals are retrieved as a LinkedList + * of PolicyParser.PrincipalEntry objects. + * + *

    + * + * @return the Principals associated with this + * SubjectCodeSource as a LinkedList + * of PolicyParser.PrincipalEntry objects. + */ + LinkedList getPrincipals() { + return principals; + } + + /** + * Get the Subject associated with this + * SubjectCodeSource. The Subject + * represents the Subject associated with the + * current AccessControlContext. + * + *

    + * + * @return the Subject associated with this + * SubjectCodeSource. + */ + Subject getSubject() { + return subject; + } + + /** + * Returns true if this SubjectCodeSource object "implies" + * the specified CodeSource. + * More specifically, this method makes the following checks. + * If any fail, it returns false. If they all succeed, it returns true. + * + *

    + *

      + *
    1. The provided codesource must not be null. + *
    2. codesource must be an instance of SubjectCodeSource. + *
    3. super.implies(codesource) must return true. + *
    4. for each principal in this codesource's principal list: + *
        + *
      1. if the principal is an instanceof + * Principal, then the principal must + * imply the provided codesource's Subject. + *
      2. if the principal is not an instanceof + * Principal, then the provided + * codesource's Subject must have an + * associated Principal, P, where + * P.getClass().getName equals principal.principalClass, + * and P.getName() equals principal.principalName. + *
      + *
    + * + *

    + * + * @param codesource the CodeSource to compare against. + * + * @return true if this SubjectCodeSource implies the + * the specified CodeSource. + */ + public boolean implies(CodeSource codesource) { + + LinkedList subjectList = null; + + if (codesource == null || + !(codesource instanceof SubjectCodeSource) || + !(super.implies(codesource))) { + + if (debug != null) + debug.println("\tSubjectCodeSource.implies: FAILURE 1"); + return false; + } + + SubjectCodeSource that = (SubjectCodeSource)codesource; + + // if the principal list in the policy "implies" + // the Subject associated with the current AccessControlContext, + // then return true + + if (this.principals == null) { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: PASS 1"); + return true; + } + + if (that.getSubject() == null || + that.getSubject().getPrincipals().size() == 0) { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: FAILURE 2"); + return false; + } + + ListIterator li = this.principals.listIterator(0); + while (li.hasNext()) { + PrincipalEntry pppe = li.next(); + try { + + // use new Principal.implies method + + Class pClass = Class.forName(pppe.principalClass, + true, sysClassLoader); + if (!Principal.class.isAssignableFrom(pClass)) { + // not the right subtype + throw new ClassCastException(pppe.principalClass + + " is not a Principal"); + } + Constructor c = pClass.getConstructor(PARAMS); + Principal p = (Principal)c.newInstance(new Object[] { + pppe.principalName }); + + if (!p.implies(that.getSubject())) { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: FAILURE 3"); + return false; + } else { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: PASS 2"); + return true; + } + } catch (Exception e) { + + // simply compare Principals + + if (subjectList == null) { + + if (that.getSubject() == null) { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: " + + "FAILURE 4"); + return false; + } + Iterator i = + that.getSubject().getPrincipals().iterator(); + + subjectList = new LinkedList(); + while (i.hasNext()) { + Principal p = i.next(); + PrincipalEntry spppe = new PrincipalEntry + (p.getClass().getName(), p.getName()); + subjectList.add(spppe); + } + } + + if (!subjectListImpliesPrincipalEntry(subjectList, pppe)) { + if (debug != null) + debug.println("\tSubjectCodeSource.implies: FAILURE 5"); + return false; + } + } + } + + if (debug != null) + debug.println("\tSubjectCodeSource.implies: PASS 3"); + return true; + } + + /** + * This method returns, true, if the provided subjectList + * "contains" the Principal specified + * in the provided pppe argument. + * + * Note that the provided pppe argument may have + * wildcards (*) for the Principal class and name, + * which need to be considered. + * + *

    + * + * @param subjectList a list of PolicyParser.PrincipalEntry objects + * that correspond to all the Principals in the Subject currently + * on this thread's AccessControlContext.

    + * + * @param pppe the Principals specified in a grant entry. + * + * @return true if the provided subjectList "contains" + * the Principal specified in the provided + * pppe argument. + */ + private boolean subjectListImpliesPrincipalEntry( + LinkedList subjectList, PrincipalEntry pppe) { + + ListIterator li = subjectList.listIterator(0); + while (li.hasNext()) { + PrincipalEntry listPppe = li.next(); + + if (pppe.getPrincipalClass().equals + (PrincipalEntry.WILDCARD_CLASS) || + pppe.getPrincipalClass().equals(listPppe.getPrincipalClass())) + { + if (pppe.getPrincipalName().equals + (PrincipalEntry.WILDCARD_NAME) || + pppe.getPrincipalName().equals(listPppe.getPrincipalName())) + return true; + } + } + return false; + } + + /** + * Tests for equality between the specified object and this + * object. Two SubjectCodeSource objects are considered equal + * if their locations are of identical value, if the two sets of + * Certificates are of identical values, and if the + * Subjects are equal, and if the PolicyParser.PrincipalEntry values + * are of identical values. It is not required that + * the Certificates or PolicyParser.PrincipalEntry values + * be in the same order. + * + *

    + * + * @param obj the object to test for equality with this object. + * + * @return true if the objects are considered equal, false otherwise. + */ + public boolean equals(Object obj) { + + if (obj == this) + return true; + + if (super.equals(obj) == false) + return false; + + if (!(obj instanceof SubjectCodeSource)) + return false; + + SubjectCodeSource that = (SubjectCodeSource)obj; + + // the principal lists must match + try { + if (this.getSubject() != that.getSubject()) + return false; + } catch (SecurityException se) { + return false; + } + + if ((this.principals == null && that.principals != null) || + (this.principals != null && that.principals == null)) + return false; + + if (this.principals != null && that.principals != null) { + if (!this.principals.containsAll(that.principals) || + !that.principals.containsAll(this.principals)) + + return false; + } + + return true; + } + + /** + * Return a hashcode for this SubjectCodeSource. + * + *

    + * + * @return a hashcode for this SubjectCodeSource. + */ + public int hashCode() { + return super.hashCode(); + } + + /** + * Return a String representation of this SubjectCodeSource. + * + *

    + * + * @return a String representation of this SubjectCodeSource. + */ + public String toString() { + String returnMe = super.toString(); + if (getSubject() != null) { + if (debug != null) { + final Subject finalSubject = getSubject(); + returnMe = returnMe + "\n" + + java.security.AccessController.doPrivileged + (new java.security.PrivilegedAction() { + public String run() { + return finalSubject.toString(); + } + }); + } else { + returnMe = returnMe + "\n" + getSubject().toString(); + } + } + if (principals != null) { + ListIterator li = principals.listIterator(); + while (li.hasNext()) { + PrincipalEntry pppe = li.next(); + returnMe = returnMe + rb.getString("NEWLINE") + + pppe.getPrincipalClass() + " " + + pppe.getPrincipalName(); + } + } + return returnMe; + } +} diff --git a/src/sun/security/provider/Sun.java b/src/sun/security/provider/Sun.java new file mode 100644 index 00000000..07ef2ff4 --- /dev/null +++ b/src/sun/security/provider/Sun.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.util.*; +import java.security.*; + +import sun.security.action.PutAllAction; + +/** + * The SUN Security Provider. + * + */ +public final class Sun extends Provider { + + private static final long serialVersionUID = 6440182097568097204L; + + private static final String INFO = "SUN " + + "(DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; " + + "SecureRandom; X.509 certificates; JKS & DKS keystores; " + + "PKIX CertPathValidator; " + + "PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; " + + "JavaLoginConfig Configuration)"; + + public Sun() { + /* We are the SUN provider */ + super("SUN", 1.8d, INFO); + + // if there is no security manager installed, put directly into + // the provider. Otherwise, create a temporary map and use a + // doPrivileged() call at the end to transfer the contents + if (System.getSecurityManager() == null) { + SunEntries.putEntries(this); + } else { + // use LinkedHashMap to preserve the order of the PRNGs + Map map = new LinkedHashMap<>(); + SunEntries.putEntries(map); + AccessController.doPrivileged(new PutAllAction(this, map)); + } + } + +} diff --git a/src/sun/security/provider/SunEntries.java b/src/sun/security/provider/SunEntries.java new file mode 100644 index 00000000..b551bebd --- /dev/null +++ b/src/sun/security/provider/SunEntries.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.net.*; +import java.util.Map; +import java.security.*; + +/** + * Defines the entries of the SUN provider. + * + * Algorithms supported, and their names: + * + * - SHA is the message digest scheme described in FIPS 180-1. + * Aliases for SHA are SHA-1 and SHA1. + * + * - SHA1withDSA is the signature scheme described in FIPS 186. + * (SHA used in DSA is SHA-1: FIPS 186 with Change No 1.) + * Aliases for SHA1withDSA are DSA, DSS, SHA/DSA, SHA-1/DSA, SHA1/DSA, + * SHAwithDSA, DSAWithSHA1, and the object + * identifier strings "OID.1.3.14.3.2.13", "OID.1.3.14.3.2.27" and + * "OID.1.2.840.10040.4.3". + * + * - SHA-2 is a set of message digest schemes described in FIPS 180-2. + * SHA-2 family of hash functions includes SHA-224, SHA-256, SHA-384, + * and SHA-512. + * + * - SHA-224withDSA/SHA-256withDSA are the signature schemes + * described in FIPS 186-3. The associated object identifiers are + * "OID.2.16.840.1.101.3.4.3.1", and "OID.2.16.840.1.101.3.4.3.2". + + * - DSA is the key generation scheme as described in FIPS 186. + * Aliases for DSA include the OID strings "OID.1.3.14.3.2.12" + * and "OID.1.2.840.10040.4.1". + * + * - MD5 is the message digest scheme described in RFC 1321. + * There are no aliases for MD5. + * + * - X.509 is the certificate factory type for X.509 certificates + * and CRLs. Aliases for X.509 are X509. + * + * - PKIX is the certification path validation algorithm described + * in RFC 3280. The ValidationAlgorithm attribute notes the + * specification that this provider implements. + * + * - LDAP is the CertStore type for LDAP repositories. The + * LDAPSchema attribute notes the specification defining the + * schema that this provider uses to find certificates and CRLs. + * + * - JavaPolicy is the default file-based Policy type. + * + * - JavaLoginConfig is the default file-based LoginModule Configuration type. + */ + +final class SunEntries { + + private SunEntries() { + // empty + } + + static void putEntries(Map map) { + + /* + * SecureRandom + * + * Register these first to speed up "new SecureRandom()", + * which iterates through the list of algorithms + */ + // register the native PRNG, if available + // if user selected /dev/urandom, we put it before SHA1PRNG, + // otherwise after it + boolean nativeAvailable = NativePRNG.isAvailable(); + boolean useNativePRNG = seedSource.equals(URL_DEV_URANDOM) || + seedSource.equals(URL_DEV_RANDOM); + + if (nativeAvailable && useNativePRNG) { + map.put("SecureRandom.NativePRNG", + "sun.security.provider.NativePRNG"); + } + + map.put("SecureRandom.SHA1PRNG", + "sun.security.provider.SecureRandom"); + if (nativeAvailable && !useNativePRNG) { + map.put("SecureRandom.NativePRNG", + "sun.security.provider.NativePRNG"); + } + + if (NativePRNG.Blocking.isAvailable()) { + map.put("SecureRandom.NativePRNGBlocking", + "sun.security.provider.NativePRNG$Blocking"); + } + + if (NativePRNG.NonBlocking.isAvailable()) { + map.put("SecureRandom.NativePRNGNonBlocking", + "sun.security.provider.NativePRNG$NonBlocking"); + } + + /* + * Signature engines + */ + map.put("Signature.SHA1withDSA", + "sun.security.provider.DSA$SHA1withDSA"); + map.put("Signature.NONEwithDSA", "sun.security.provider.DSA$RawDSA"); + map.put("Alg.Alias.Signature.RawDSA", "NONEwithDSA"); + map.put("Signature.SHA224withDSA", + "sun.security.provider.DSA$SHA224withDSA"); + map.put("Signature.SHA256withDSA", + "sun.security.provider.DSA$SHA256withDSA"); + + String dsaKeyClasses = "java.security.interfaces.DSAPublicKey" + + "|java.security.interfaces.DSAPrivateKey"; + map.put("Signature.SHA1withDSA SupportedKeyClasses", dsaKeyClasses); + map.put("Signature.NONEwithDSA SupportedKeyClasses", dsaKeyClasses); + map.put("Signature.SHA224withDSA SupportedKeyClasses", dsaKeyClasses); + map.put("Signature.SHA256withDSA SupportedKeyClasses", dsaKeyClasses); + + map.put("Alg.Alias.Signature.DSA", "SHA1withDSA"); + map.put("Alg.Alias.Signature.DSS", "SHA1withDSA"); + map.put("Alg.Alias.Signature.SHA/DSA", "SHA1withDSA"); + map.put("Alg.Alias.Signature.SHA-1/DSA", "SHA1withDSA"); + map.put("Alg.Alias.Signature.SHA1/DSA", "SHA1withDSA"); + map.put("Alg.Alias.Signature.SHAwithDSA", "SHA1withDSA"); + map.put("Alg.Alias.Signature.DSAWithSHA1", "SHA1withDSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.10040.4.3", + "SHA1withDSA"); + map.put("Alg.Alias.Signature.1.2.840.10040.4.3", "SHA1withDSA"); + map.put("Alg.Alias.Signature.1.3.14.3.2.13", "SHA1withDSA"); + map.put("Alg.Alias.Signature.1.3.14.3.2.27", "SHA1withDSA"); + map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.1", + "SHA224withDSA"); + map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.1", "SHA224withDSA"); + map.put("Alg.Alias.Signature.OID.2.16.840.1.101.3.4.3.2", + "SHA256withDSA"); + map.put("Alg.Alias.Signature.2.16.840.1.101.3.4.3.2", "SHA256withDSA"); + + /* + * Key Pair Generator engines + */ + map.put("KeyPairGenerator.DSA", + "sun.security.provider.DSAKeyPairGenerator"); + map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.KeyPairGenerator.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.KeyPairGenerator.1.3.14.3.2.12", "DSA"); + + /* + * Digest engines + */ + map.put("MessageDigest.MD2", "sun.security.provider.MD2"); + map.put("MessageDigest.MD5", "sun.security.provider.MD5"); + map.put("MessageDigest.SHA", "sun.security.provider.SHA"); + + map.put("Alg.Alias.MessageDigest.SHA-1", "SHA"); + map.put("Alg.Alias.MessageDigest.SHA1", "SHA"); + map.put("Alg.Alias.MessageDigest.1.3.14.3.2.26", "SHA"); + map.put("Alg.Alias.MessageDigest.OID.1.3.14.3.2.26", "SHA"); + + map.put("MessageDigest.SHA-224", "sun.security.provider.SHA2$SHA224"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.4", "SHA-224"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.4", + "SHA-224"); + + map.put("MessageDigest.SHA-256", "sun.security.provider.SHA2$SHA256"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.1", "SHA-256"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.1", + "SHA-256"); + map.put("MessageDigest.SHA-384", "sun.security.provider.SHA5$SHA384"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.2", "SHA-384"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.2", + "SHA-384"); + map.put("MessageDigest.SHA-512", "sun.security.provider.SHA5$SHA512"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.3", "SHA-512"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.3", + "SHA-512"); + map.put("MessageDigest.SHA-512/224", "sun.security.provider.SHA5$SHA512_224"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.5", "SHA-512/224"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.5", + "SHA-512/224"); + map.put("MessageDigest.SHA-512/256", "sun.security.provider.SHA5$SHA512_256"); + map.put("Alg.Alias.MessageDigest.2.16.840.1.101.3.4.2.6", "SHA-512/256"); + map.put("Alg.Alias.MessageDigest.OID.2.16.840.1.101.3.4.2.6", + "SHA-512/256"); + + /* + * Algorithm Parameter Generator engines + */ + map.put("AlgorithmParameterGenerator.DSA", + "sun.security.provider.DSAParameterGenerator"); + + /* + * Algorithm Parameter engines + */ + map.put("AlgorithmParameters.DSA", + "sun.security.provider.DSAParameters"); + map.put("Alg.Alias.AlgorithmParameters.OID.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.AlgorithmParameters.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.AlgorithmParameters.1.3.14.3.2.12", "DSA"); + + /* + * Key factories + */ + map.put("KeyFactory.DSA", "sun.security.provider.DSAKeyFactory"); + map.put("Alg.Alias.KeyFactory.OID.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.KeyFactory.1.2.840.10040.4.1", "DSA"); + map.put("Alg.Alias.KeyFactory.1.3.14.3.2.12", "DSA"); + + /* + * Certificates + */ + map.put("CertificateFactory.X.509", + "sun.security.provider.X509Factory"); + map.put("Alg.Alias.CertificateFactory.X509", "X.509"); + + /* + * KeyStore + */ + map.put("KeyStore.JKS", "sun.security.provider.JavaKeyStore$JKS"); + map.put("KeyStore.CaseExactJKS", + "sun.security.provider.JavaKeyStore$CaseExactJKS"); + map.put("KeyStore.DKS", "sun.security.provider.DomainKeyStore$DKS"); + + /* + * Policy + */ + map.put("Policy.JavaPolicy", "sun.security.provider.PolicySpiFile"); + + /* + * Configuration + */ + map.put("Configuration.JavaLoginConfig", + "sun.security.provider.ConfigFile$Spi"); + + /* + * CertPathBuilder + */ + map.put("CertPathBuilder.PKIX", + "sun.security.provider.certpath.SunCertPathBuilder"); + map.put("CertPathBuilder.PKIX ValidationAlgorithm", + "RFC3280"); + + /* + * CertPathValidator + */ + map.put("CertPathValidator.PKIX", + "sun.security.provider.certpath.PKIXCertPathValidator"); + map.put("CertPathValidator.PKIX ValidationAlgorithm", + "RFC3280"); + + /* + * CertStores + */ + map.put("CertStore.LDAP", + "sun.security.provider.certpath.ldap.LDAPCertStore"); + map.put("CertStore.LDAP LDAPSchema", "RFC2587"); + map.put("CertStore.Collection", + "sun.security.provider.certpath.CollectionCertStore"); + map.put("CertStore.com.sun.security.IndexedCollection", + "sun.security.provider.certpath.IndexedCollectionCertStore"); + + /* + * KeySize + */ + map.put("Signature.NONEwithDSA KeySize", "1024"); + map.put("Signature.SHA1withDSA KeySize", "1024"); + map.put("Signature.SHA224withDSA KeySize", "2048"); + map.put("Signature.SHA256withDSA KeySize", "2048"); + + map.put("KeyPairGenerator.DSA KeySize", "2048"); + map.put("AlgorithmParameterGenerator.DSA KeySize", "2048"); + + /* + * Implementation type: software or hardware + */ + map.put("Signature.SHA1withDSA ImplementedIn", "Software"); + map.put("KeyPairGenerator.DSA ImplementedIn", "Software"); + map.put("MessageDigest.MD5 ImplementedIn", "Software"); + map.put("MessageDigest.SHA ImplementedIn", "Software"); + map.put("AlgorithmParameterGenerator.DSA ImplementedIn", + "Software"); + map.put("AlgorithmParameters.DSA ImplementedIn", "Software"); + map.put("KeyFactory.DSA ImplementedIn", "Software"); + map.put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); + map.put("CertificateFactory.X.509 ImplementedIn", "Software"); + map.put("KeyStore.JKS ImplementedIn", "Software"); + map.put("CertPathValidator.PKIX ImplementedIn", "Software"); + map.put("CertPathBuilder.PKIX ImplementedIn", "Software"); + map.put("CertStore.LDAP ImplementedIn", "Software"); + map.put("CertStore.Collection ImplementedIn", "Software"); + map.put("CertStore.com.sun.security.IndexedCollection ImplementedIn", + "Software"); + + } + + // name of the *System* property, takes precedence over PROP_RNDSOURCE + private final static String PROP_EGD = "java.security.egd"; + // name of the *Security* property + private final static String PROP_RNDSOURCE = "securerandom.source"; + + final static String URL_DEV_RANDOM = "file:/dev/random"; + final static String URL_DEV_URANDOM = "file:/dev/urandom"; + + private static final String seedSource; + + static { + seedSource = AccessController.doPrivileged( + new PrivilegedAction() { + + @Override + public String run() { + String egdSource = System.getProperty(PROP_EGD, ""); + if (egdSource.length() != 0) { + return egdSource; + } + egdSource = Security.getProperty(PROP_RNDSOURCE); + if (egdSource == null) { + return ""; + } + return egdSource; + } + }); + } + + static String getSeedSource() { + return seedSource; + } + + /* + * Use a URI to access this File. Previous code used a URL + * which is less strict on syntax. If we encounter a + * URISyntaxException we make best efforts for backwards + * compatibility. e.g. space character in deviceName string. + * + * Method called within PrivilegedExceptionAction block. + * + * Moved from SeedGenerator to avoid initialization problems with + * signed providers. + */ + static File getDeviceFile(URL device) throws IOException { + try { + URI deviceURI = device.toURI(); + if(deviceURI.isOpaque()) { + // File constructor does not accept opaque URI + URI localDir = new File( + System.getProperty("user.dir")).toURI(); + String uriPath = localDir.toString() + + deviceURI.toString().substring(5); + return new File(URI.create(uriPath)); + } else { + return new File(deviceURI); + } + } catch (URISyntaxException use) { + /* + * Make best effort to access this File. + * We can try using the URL path. + */ + return new File(device.getPath()); + } + } +} diff --git a/src/sun/security/provider/VerificationProvider.java b/src/sun/security/provider/VerificationProvider.java new file mode 100644 index 00000000..296b0343 --- /dev/null +++ b/src/sun/security/provider/VerificationProvider.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.util.*; +import java.security.*; + +import sun.security.action.PutAllAction; + +import sun.security.rsa.SunRsaSignEntries; + +/** + * Provider used for verification of signed JAR files *if* the Sun and + * SunRsaSign main classes have been removed. Otherwise, this provider is not + * necessary and registers no algorithms. This functionality only exists to + * support a use case required by a specific customer and is not generally + * supported. + * + * @since 1.7 + * @author Andreas Sterbenz + */ +public final class VerificationProvider extends Provider { + + private static final long serialVersionUID = 7482667077568930381L; + + private static final boolean ACTIVE; + + static { + boolean b; + try { + Class.forName("sun.security.provider.Sun"); + Class.forName("sun.security.rsa.SunRsaSign"); + b = false; + } catch (ClassNotFoundException e) { + b = true; + } + ACTIVE = b; + } + + public VerificationProvider() { + super("SunJarVerification", 1.8d, "Jar Verification Provider"); + // register all algorithms normally registered by the Sun and SunRsaSign + // providers, but only if they are missing + if (ACTIVE == false) { + return; + } + + // if there is no security manager installed, put directly into + // the provider. Otherwise, create a temporary map and use a + // doPrivileged() call at the end to transfer the contents + if (System.getSecurityManager() == null) { + SunEntries.putEntries(this); + SunRsaSignEntries.putEntries(this); + } else { + // use LinkedHashMap to preserve the order of the PRNGs + Map map = new LinkedHashMap<>(); + SunEntries.putEntries(map); + SunRsaSignEntries.putEntries(map); + AccessController.doPrivileged(new PutAllAction(this, map)); + } + } + +} diff --git a/src/sun/security/provider/X509Factory.java b/src/sun/security/provider/X509Factory.java new file mode 100644 index 00000000..bc1f7f06 --- /dev/null +++ b/src/sun/security/provider/X509Factory.java @@ -0,0 +1,756 @@ +/* + * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.util.*; +import java.security.cert.*; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CRLImpl; +import sun.security.pkcs.PKCS7; +import sun.security.provider.certpath.X509CertPath; +import sun.security.provider.certpath.X509CertificatePair; +import sun.security.util.DerValue; +import sun.security.util.Cache; +import java.util.Base64; +import sun.security.pkcs.ParsingException; + +/** + * This class defines a certificate factory for X.509 v3 certificates & + * certification paths, and X.509 v2 certificate revocation lists (CRLs). + * + * @author Jan Luehe + * @author Hemma Prafullchandra + * @author Sean Mullan + * + * + * @see CertificateFactorySpi + * @see Certificate + * @see CertPath + * @see CRL + * @see X509Certificate + * @see X509CRL + * @see X509CertImpl + * @see X509CRLImpl + */ + +public class X509Factory extends CertificateFactorySpi { + + public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; + public static final String END_CERT = "-----END CERTIFICATE-----"; + + private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX + + private static final Cache certCache + = Cache.newSoftMemoryCache(750); + private static final Cache crlCache + = Cache.newSoftMemoryCache(750); + + /** + * Generates an X.509 certificate object and initializes it with + * the data read from the input stream is. + * + * @param is an input stream with the certificate data. + * + * @return an X.509 certificate object initialized with the data + * from the input stream. + * + * @exception CertificateException on parsing errors. + */ + @Override + public Certificate engineGenerateCertificate(InputStream is) + throws CertificateException + { + if (is == null) { + // clear the caches (for debugging) + certCache.clear(); + X509CertificatePair.clearCache(); + throw new CertificateException("Missing input stream"); + } + try { + byte[] encoding = readOneBlock(is); + if (encoding != null) { + X509CertImpl cert = getFromCache(certCache, encoding); + if (cert != null) { + return cert; + } + cert = new X509CertImpl(encoding); + addToCache(certCache, cert.getEncodedInternal(), cert); + return cert; + } else { + throw new IOException("Empty input"); + } + } catch (IOException ioe) { + throw new CertificateException("Could not parse certificate: " + + ioe.toString(), ioe); + } + } + + /** + * Read from the stream until length bytes have been read or EOF has + * been reached. Return the number of bytes actually read. + */ + private static int readFully(InputStream in, ByteArrayOutputStream bout, + int length) throws IOException { + int read = 0; + byte[] buffer = new byte[2048]; + while (length > 0) { + int n = in.read(buffer, 0, length<2048?length:2048); + if (n <= 0) { + break; + } + bout.write(buffer, 0, n); + read += n; + length -= n; + } + return read; + } + + /** + * Return an interned X509CertImpl for the given certificate. + * If the given X509Certificate or X509CertImpl is already present + * in the cert cache, the cached object is returned. Otherwise, + * if it is a X509Certificate, it is first converted to a X509CertImpl. + * Then the X509CertImpl is added to the cache and returned. + * + * Note that all certificates created via generateCertificate(InputStream) + * are already interned and this method does not need to be called. + * It is useful for certificates that cannot be created via + * generateCertificate() and for converting other X509Certificate + * implementations to an X509CertImpl. + * + * @param c The source X509Certificate + * @return An X509CertImpl object that is either a cached certificate or a + * newly built X509CertImpl from the provided X509Certificate + * @throws CertificateException if failures occur while obtaining the DER + * encoding for certificate data. + */ + public static synchronized X509CertImpl intern(X509Certificate c) + throws CertificateException { + if (c == null) { + return null; + } + boolean isImpl = c instanceof X509CertImpl; + byte[] encoding; + if (isImpl) { + encoding = ((X509CertImpl)c).getEncodedInternal(); + } else { + encoding = c.getEncoded(); + } + X509CertImpl newC = getFromCache(certCache, encoding); + if (newC != null) { + return newC; + } + if (isImpl) { + newC = (X509CertImpl)c; + } else { + newC = new X509CertImpl(encoding); + encoding = newC.getEncodedInternal(); + } + addToCache(certCache, encoding, newC); + return newC; + } + + /** + * Return an interned X509CRLImpl for the given certificate. + * For more information, see intern(X509Certificate). + * + * @param c The source X509CRL + * @return An X509CRLImpl object that is either a cached CRL or a + * newly built X509CRLImpl from the provided X509CRL + * @throws CRLException if failures occur while obtaining the DER + * encoding for CRL data. + */ + public static synchronized X509CRLImpl intern(X509CRL c) + throws CRLException { + if (c == null) { + return null; + } + boolean isImpl = c instanceof X509CRLImpl; + byte[] encoding; + if (isImpl) { + encoding = ((X509CRLImpl)c).getEncodedInternal(); + } else { + encoding = c.getEncoded(); + } + X509CRLImpl newC = getFromCache(crlCache, encoding); + if (newC != null) { + return newC; + } + if (isImpl) { + newC = (X509CRLImpl)c; + } else { + newC = new X509CRLImpl(encoding); + encoding = newC.getEncodedInternal(); + } + addToCache(crlCache, encoding, newC); + return newC; + } + + /** + * Get the X509CertImpl or X509CRLImpl from the cache. + */ + private static synchronized V getFromCache(Cache cache, + byte[] encoding) { + Object key = new Cache.EqualByteArray(encoding); + return cache.get(key); + } + + /** + * Add the X509CertImpl or X509CRLImpl to the cache. + */ + private static synchronized void addToCache(Cache cache, + byte[] encoding, V value) { + if (encoding.length > ENC_MAX_LENGTH) { + return; + } + Object key = new Cache.EqualByteArray(encoding); + cache.put(key, value); + } + + /** + * Generates a CertPath object and initializes it with + * the data read from the InputStream inStream. The data + * is assumed to be in the default encoding. + * + * @param inStream an InputStream containing the data + * @return a CertPath initialized with the data from the + * InputStream + * @exception CertificateException if an exception occurs while decoding + * @since 1.4 + */ + @Override + public CertPath engineGenerateCertPath(InputStream inStream) + throws CertificateException + { + if (inStream == null) { + throw new CertificateException("Missing input stream"); + } + try { + byte[] encoding = readOneBlock(inStream); + if (encoding != null) { + return new X509CertPath(new ByteArrayInputStream(encoding)); + } else { + throw new IOException("Empty input"); + } + } catch (IOException ioe) { + throw new CertificateException(ioe.getMessage()); + } + } + + /** + * Generates a CertPath object and initializes it with + * the data read from the InputStream inStream. The data + * is assumed to be in the specified encoding. + * + * @param inStream an InputStream containing the data + * @param encoding the encoding used for the data + * @return a CertPath initialized with the data from the + * InputStream + * @exception CertificateException if an exception occurs while decoding or + * the encoding requested is not supported + * @since 1.4 + */ + @Override + public CertPath engineGenerateCertPath(InputStream inStream, + String encoding) throws CertificateException + { + if (inStream == null) { + throw new CertificateException("Missing input stream"); + } + try { + byte[] data = readOneBlock(inStream); + if (data != null) { + return new X509CertPath(new ByteArrayInputStream(data), encoding); + } else { + throw new IOException("Empty input"); + } + } catch (IOException ioe) { + throw new CertificateException(ioe.getMessage()); + } + } + + /** + * Generates a CertPath object and initializes it with + * a List of Certificates. + *

    + * The certificates supplied must be of a type supported by the + * CertificateFactory. They will be copied out of the supplied + * List object. + * + * @param certificates a List of Certificates + * @return a CertPath initialized with the supplied list of + * certificates + * @exception CertificateException if an exception occurs + * @since 1.4 + */ + @Override + public CertPath + engineGenerateCertPath(List certificates) + throws CertificateException + { + return(new X509CertPath(certificates)); + } + + /** + * Returns an iteration of the CertPath encodings supported + * by this certificate factory, with the default encoding first. + *

    + * Attempts to modify the returned Iterator via its + * remove method result in an + * UnsupportedOperationException. + * + * @return an Iterator over the names of the supported + * CertPath encodings (as Strings) + * @since 1.4 + */ + @Override + public Iterator engineGetCertPathEncodings() { + return(X509CertPath.getEncodingsStatic()); + } + + /** + * Returns a (possibly empty) collection view of X.509 certificates read + * from the given input stream is. + * + * @param is the input stream with the certificates. + * + * @return a (possibly empty) collection view of X.509 certificate objects + * initialized with the data from the input stream. + * + * @exception CertificateException on parsing errors. + */ + @Override + public Collection + engineGenerateCertificates(InputStream is) + throws CertificateException { + if (is == null) { + throw new CertificateException("Missing input stream"); + } + try { + return parseX509orPKCS7Cert(is); + } catch (IOException ioe) { + throw new CertificateException(ioe); + } + } + + /** + * Generates an X.509 certificate revocation list (CRL) object and + * initializes it with the data read from the given input stream + * is. + * + * @param is an input stream with the CRL data. + * + * @return an X.509 CRL object initialized with the data + * from the input stream. + * + * @exception CRLException on parsing errors. + */ + @Override + public CRL engineGenerateCRL(InputStream is) + throws CRLException + { + if (is == null) { + // clear the cache (for debugging) + crlCache.clear(); + throw new CRLException("Missing input stream"); + } + try { + byte[] encoding = readOneBlock(is); + if (encoding != null) { + X509CRLImpl crl = getFromCache(crlCache, encoding); + if (crl != null) { + return crl; + } + crl = new X509CRLImpl(encoding); + addToCache(crlCache, crl.getEncodedInternal(), crl); + return crl; + } else { + throw new IOException("Empty input"); + } + } catch (IOException ioe) { + throw new CRLException(ioe.getMessage()); + } + } + + /** + * Returns a (possibly empty) collection view of X.509 CRLs read + * from the given input stream is. + * + * @param is the input stream with the CRLs. + * + * @return a (possibly empty) collection view of X.509 CRL objects + * initialized with the data from the input stream. + * + * @exception CRLException on parsing errors. + */ + @Override + public Collection engineGenerateCRLs( + InputStream is) throws CRLException + { + if (is == null) { + throw new CRLException("Missing input stream"); + } + try { + return parseX509orPKCS7CRL(is); + } catch (IOException ioe) { + throw new CRLException(ioe.getMessage()); + } + } + + /* + * Parses the data in the given input stream as a sequence of DER + * encoded X.509 certificates (in binary or base 64 encoded format) OR + * as a single PKCS#7 encoded blob (in binary or base64 encoded format). + */ + private Collection + parseX509orPKCS7Cert(InputStream is) + throws CertificateException, IOException + { + int peekByte; + byte[] data; + PushbackInputStream pbis = new PushbackInputStream(is); + Collection coll = new ArrayList<>(); + + // Test the InputStream for end-of-stream. If the stream's + // initial state is already at end-of-stream then return + // an empty collection. Otherwise, push the byte back into the + // stream and let readOneBlock look for the first certificate. + peekByte = pbis.read(); + if (peekByte == -1) { + return new ArrayList<>(0); + } else { + pbis.unread(peekByte); + data = readOneBlock(pbis); + } + + // If we end up with a null value after reading the first block + // then we know the end-of-stream has been reached and no certificate + // data has been found. + if (data == null) { + throw new CertificateException("No certificate data found"); + } + + try { + PKCS7 pkcs7 = new PKCS7(data); + X509Certificate[] certs = pkcs7.getCertificates(); + // certs are optional in PKCS #7 + if (certs != null) { + return Arrays.asList(certs); + } else { + // no certificates provided + return new ArrayList<>(0); + } + } catch (ParsingException e) { + while (data != null) { + coll.add(new X509CertImpl(data)); + data = readOneBlock(pbis); + } + } + return coll; + } + + /* + * Parses the data in the given input stream as a sequence of DER encoded + * X.509 CRLs (in binary or base 64 encoded format) OR as a single PKCS#7 + * encoded blob (in binary or base 64 encoded format). + */ + private Collection + parseX509orPKCS7CRL(InputStream is) + throws CRLException, IOException + { + int peekByte; + byte[] data; + PushbackInputStream pbis = new PushbackInputStream(is); + Collection coll = new ArrayList<>(); + + // Test the InputStream for end-of-stream. If the stream's + // initial state is already at end-of-stream then return + // an empty collection. Otherwise, push the byte back into the + // stream and let readOneBlock look for the first CRL. + peekByte = pbis.read(); + if (peekByte == -1) { + return new ArrayList<>(0); + } else { + pbis.unread(peekByte); + data = readOneBlock(pbis); + } + + // If we end up with a null value after reading the first block + // then we know the end-of-stream has been reached and no CRL + // data has been found. + if (data == null) { + throw new CRLException("No CRL data found"); + } + + try { + PKCS7 pkcs7 = new PKCS7(data); + X509CRL[] crls = pkcs7.getCRLs(); + // CRLs are optional in PKCS #7 + if (crls != null) { + return Arrays.asList(crls); + } else { + // no crls provided + return new ArrayList<>(0); + } + } catch (ParsingException e) { + while (data != null) { + coll.add(new X509CRLImpl(data)); + data = readOneBlock(pbis); + } + } + return coll; + } + + /** + * Returns an ASN.1 SEQUENCE from a stream, which might be a BER-encoded + * binary block or a PEM-style BASE64-encoded ASCII data. In the latter + * case, it's de-BASE64'ed before return. + * + * After the reading, the input stream pointer is after the BER block, or + * after the newline character after the -----END SOMETHING----- line. + * + * @param is the InputStream + * @returns byte block or null if end of stream + * @throws IOException If any parsing error + */ + private static byte[] readOneBlock(InputStream is) throws IOException { + + // The first character of a BLOCK. + int c = is.read(); + if (c == -1) { + return null; + } + if (c == DerValue.tag_Sequence) { + ByteArrayOutputStream bout = new ByteArrayOutputStream(2048); + bout.write(c); + readBERInternal(is, bout, c); + return bout.toByteArray(); + } else { + // Read BASE64 encoded data, might skip info at the beginning + char[] data = new char[2048]; + int pos = 0; + + // Step 1: Read until header is found + int hyphen = (c=='-') ? 1: 0; // count of consequent hyphens + int last = (c=='-') ? -1: c; // the char before hyphen + while (true) { + int next = is.read(); + if (next == -1) { + // We accept useless data after the last block, + // say, empty lines. + return null; + } + if (next == '-') { + hyphen++; + } else { + hyphen = 0; + last = next; + } + if (hyphen == 5 && (last == -1 || last == '\r' || last == '\n')) { + break; + } + } + + // Step 2: Read the rest of header, determine the line end + int end; + StringBuilder header = new StringBuilder("-----"); + while (true) { + int next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next == '\n') { + end = '\n'; + break; + } + if (next == '\r') { + next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next == '\n') { + end = '\n'; + } else { + end = '\r'; + data[pos++] = (char)next; + } + break; + } + header.append((char)next); + } + + // Step 3: Read the data + while (true) { + int next = is.read(); + if (next == -1) { + throw new IOException("Incomplete data"); + } + if (next != '-') { + data[pos++] = (char)next; + if (pos >= data.length) { + data = Arrays.copyOf(data, data.length+1024); + } + } else { + break; + } + } + + // Step 4: Consume the footer + StringBuilder footer = new StringBuilder("-"); + while (true) { + int next = is.read(); + // Add next == '\n' for maximum safety, in case endline + // is not consistent. + if (next == -1 || next == end || next == '\n') { + break; + } + if (next != '\r') footer.append((char)next); + } + + checkHeaderFooter(header.toString(), footer.toString()); + + return Base64.getMimeDecoder().decode(new String(data, 0, pos)); + } + } + + private static void checkHeaderFooter(String header, + String footer) throws IOException { + if (header.length() < 16 || !header.startsWith("-----BEGIN ") || + !header.endsWith("-----")) { + throw new IOException("Illegal header: " + header); + } + if (footer.length() < 14 || !footer.startsWith("-----END ") || + !footer.endsWith("-----")) { + throw new IOException("Illegal footer: " + footer); + } + String headerType = header.substring(11, header.length()-5); + String footerType = footer.substring(9, footer.length()-5); + if (!headerType.equals(footerType)) { + throw new IOException("Header and footer do not match: " + + header + " " + footer); + } + } + + /** + * Read one BER data block. This method is aware of indefinite-length BER + * encoding and will read all of the sub-sections in a recursive way + * + * @param is Read from this InputStream + * @param bout Write into this OutputStream + * @param tag Tag already read (-1 mean not read) + * @returns The current tag, used to check EOC in indefinite-length BER + * @throws IOException Any parsing error + */ + private static int readBERInternal(InputStream is, + ByteArrayOutputStream bout, int tag) throws IOException { + + if (tag == -1) { // Not read before the call, read now + tag = is.read(); + if (tag == -1) { + throw new IOException("BER/DER tag info absent"); + } + if ((tag & 0x1f) == 0x1f) { + throw new IOException("Multi octets tag not supported"); + } + bout.write(tag); + } + + int n = is.read(); + if (n == -1) { + throw new IOException("BER/DER length info absent"); + } + bout.write(n); + + int length; + + if (n == 0x80) { // Indefinite-length encoding + if ((tag & 0x20) != 0x20) { + throw new IOException( + "Non constructed encoding must have definite length"); + } + while (true) { + int subTag = readBERInternal(is, bout, -1); + if (subTag == 0) { // EOC, end of indefinite-length section + break; + } + } + } else { + if (n < 0x80) { + length = n; + } else if (n == 0x81) { + length = is.read(); + if (length == -1) { + throw new IOException("Incomplete BER/DER length info"); + } + bout.write(length); + } else if (n == 0x82) { + int highByte = is.read(); + int lowByte = is.read(); + if (lowByte == -1) { + throw new IOException("Incomplete BER/DER length info"); + } + bout.write(highByte); + bout.write(lowByte); + length = (highByte << 8) | lowByte; + } else if (n == 0x83) { + int highByte = is.read(); + int midByte = is.read(); + int lowByte = is.read(); + if (lowByte == -1) { + throw new IOException("Incomplete BER/DER length info"); + } + bout.write(highByte); + bout.write(midByte); + bout.write(lowByte); + length = (highByte << 16) | (midByte << 8) | lowByte; + } else if (n == 0x84) { + int highByte = is.read(); + int nextByte = is.read(); + int midByte = is.read(); + int lowByte = is.read(); + if (lowByte == -1) { + throw new IOException("Incomplete BER/DER length info"); + } + if (highByte > 127) { + throw new IOException("Invalid BER/DER data (a little huge?)"); + } + bout.write(highByte); + bout.write(nextByte); + bout.write(midByte); + bout.write(lowByte); + length = (highByte << 24 ) | (nextByte << 16) | + (midByte << 8) | lowByte; + } else { // ignore longer length forms + throw new IOException("Invalid BER/DER data (too huge?)"); + } + if (readFully(is, bout, length) != length) { + throw new IOException("Incomplete BER/DER data"); + } + } + return tag; + } +} diff --git a/src/sun/security/provider/certpath/AdaptableX509CertSelector.java b/src/sun/security/provider/certpath/AdaptableX509CertSelector.java new file mode 100644 index 00000000..d05d22ff --- /dev/null +++ b/src/sun/security/provider/certpath/AdaptableX509CertSelector.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.X509CertSelector; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.Date; + +import sun.security.util.Debug; +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.x509.SerialNumber; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.AuthorityKeyIdentifierExtension; + +/** + * An adaptable X509 certificate selector for forward certification path + * building. This selector overrides the default X509CertSelector matching + * rules for the subjectKeyIdentifier and serialNumber criteria, and adds + * additional rules for certificate validity. + * + * @since 1.7 + */ +class AdaptableX509CertSelector extends X509CertSelector { + + private static final Debug debug = Debug.getInstance("certpath"); + + // The start date of a validity period. + private Date startDate; + + // The end date of a validity period. + private Date endDate; + + // The subject key identifier + private byte[] ski; + + // The serial number + private BigInteger serial; + + /** + * Sets the criterion of the X509Certificate validity period. + * + * Normally, we may not have to check that a certificate validity period + * must fall within its issuer's certificate validity period. However, + * when we face root CA key updates for version 1 certificates, according + * to scheme of RFC 4210 or 2510, the validity periods should be checked + * to determine the right issuer's certificate. + * + * Conservatively, we will only check the validity periods for version + * 1 and version 2 certificates. For version 3 certificates, we can + * determine the right issuer by authority and subject key identifier + * extensions. + * + * @param startDate the start date of a validity period that must fall + * within the certificate validity period for the X509Certificate + * @param endDate the end date of a validity period that must fall + * within the certificate validity period for the X509Certificate + */ + void setValidityPeriod(Date startDate, Date endDate) { + this.startDate = startDate; + this.endDate = endDate; + } + + /** + * This selector overrides the subjectKeyIdentifier matching rules of + * X509CertSelector, so it throws IllegalArgumentException if this method + * is ever called. + */ + @Override + public void setSubjectKeyIdentifier(byte[] subjectKeyID) { + throw new IllegalArgumentException(); + } + + /** + * This selector overrides the serialNumber matching rules of + * X509CertSelector, so it throws IllegalArgumentException if this method + * is ever called. + */ + @Override + public void setSerialNumber(BigInteger serial) { + throw new IllegalArgumentException(); + } + + /** + * Sets the subjectKeyIdentifier and serialNumber criteria from the + * authority key identifier extension. + * + * The subjectKeyIdentifier criterion is set to the keyIdentifier field + * of the extension, or null if it is empty. The serialNumber criterion + * is set to the authorityCertSerialNumber field, or null if it is empty. + * + * Note that we do not set the subject criterion to the + * authorityCertIssuer field of the extension. The caller MUST set + * the subject criterion before calling match(). + * + * @param ext the authorityKeyIdentifier extension + * @throws IOException if there is an error parsing the extension + */ + void setSkiAndSerialNumber(AuthorityKeyIdentifierExtension ext) + throws IOException { + + ski = null; + serial = null; + + if (ext != null) { + KeyIdentifier akid = (KeyIdentifier)ext.get( + AuthorityKeyIdentifierExtension.KEY_ID); + if (akid != null) { + DerOutputStream derout = new DerOutputStream(); + derout.putOctetString(akid.getIdentifier()); + ski = derout.toByteArray(); + } + SerialNumber asn = (SerialNumber)ext.get( + AuthorityKeyIdentifierExtension.SERIAL_NUMBER); + if (asn != null) { + serial = asn.getNumber(); + } + // the subject criterion should be set by the caller + } + } + + /** + * Decides whether a Certificate should be selected. + * + * This method overrides the matching rules for the subjectKeyIdentifier + * and serialNumber criteria and adds additional rules for certificate + * validity. + * + * For the purpose of compatibility, when a certificate is of + * version 1 and version 2, or the certificate does not include + * a subject key identifier extension, the selection criterion + * of subjectKeyIdentifier will be disabled. + */ + @Override + public boolean match(Certificate cert) { + X509Certificate xcert = (X509Certificate)cert; + + // match subject key identifier + if (!matchSubjectKeyID(xcert)) { + return false; + } + + // In practice, a CA may replace its root certificate and require that + // the existing certificate is still valid, even if the AKID extension + // does not match the replacement root certificate fields. + // + // Conservatively, we only support the replacement for version 1 and + // version 2 certificate. As for version 3, the certificate extension + // may contain sensitive information (for example, policies), the + // AKID need to be respected to seek the exact certificate in case + // of key or certificate abuse. + int version = xcert.getVersion(); + if (serial != null && version > 2) { + if (!serial.equals(xcert.getSerialNumber())) { + return false; + } + } + + // Check the validity period for version 1 and 2 certificate. + if (version < 3) { + if (startDate != null) { + try { + xcert.checkValidity(startDate); + } catch (CertificateException ce) { + return false; + } + } + if (endDate != null) { + try { + xcert.checkValidity(endDate); + } catch (CertificateException ce) { + return false; + } + } + } + + + if (!super.match(cert)) { + return false; + } + + return true; + } + + /* + * Match on subject key identifier extension value. These matching rules + * are identical to X509CertSelector except that if the certificate does + * not have a subject key identifier extension, it returns true. + */ + private boolean matchSubjectKeyID(X509Certificate xcert) { + if (ski == null) { + return true; + } + try { + byte[] extVal = xcert.getExtensionValue("2.5.29.14"); + if (extVal == null) { + if (debug != null) { + debug.println("AdaptableX509CertSelector.match: " + + "no subject key ID extension"); + } + return true; + } + DerInputStream in = new DerInputStream(extVal); + byte[] certSubjectKeyID = in.getOctetString(); + if (certSubjectKeyID == null || + !Arrays.equals(ski, certSubjectKeyID)) { + if (debug != null) { + debug.println("AdaptableX509CertSelector.match: " + + "subject key IDs don't match"); + } + return false; + } + } catch (IOException ex) { + if (debug != null) { + debug.println("AdaptableX509CertSelector.match: " + + "exception in subject key ID check"); + } + return false; + } + return true; + } + + @Override + public Object clone() { + AdaptableX509CertSelector copy = + (AdaptableX509CertSelector)super.clone(); + if (startDate != null) { + copy.startDate = (Date)startDate.clone(); + } + + if (endDate != null) { + copy.endDate = (Date)endDate.clone(); + } + + if (ski != null) { + copy.ski = ski.clone(); + } + return copy; + } +} diff --git a/src/sun/security/provider/certpath/AdjacencyList.java b/src/sun/security/provider/certpath/AdjacencyList.java new file mode 100644 index 00000000..98d1411f --- /dev/null +++ b/src/sun/security/provider/certpath/AdjacencyList.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.provider.certpath; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * An AdjacencyList is used to store the history of certification paths + * attempted in constructing a path from an initiator to a target. The + * AdjacencyList is initialized with a List of + * Lists, where each sub-List contains objects of + * type Vertex. A Vertex describes one possible or + * actual step in the chain building process, and the associated + * Certificate. Specifically, a Vertex object + * contains a Certificate and an index value referencing the + * next sub-list in the process. If the index value is -1 then this + * Vertex doesn't continue the attempted build path. + *

    + * Example: + *

    + * Attempted Paths:

      + *
    • C1->C2->C3 + *
    • C1->C4->C5 + *
    • C1->C4->C6 + *
    • C1->C4->C7 + *
    • C1->C8->C9 + *
    • C1->C10->C11 + *
    + *

    + * AdjacencyList structure:

      + *
    • AL[0] = C1,1 + *
    • AL[1] = C2,2 =>C4,3 =>C8,4 =>C10,5 + *
    • AL[2] = C3,-1 + *
    • AL[3] = C5,-1 =>C6,-1 =>C7,-1 + *
    • AL[4] = C9,-1 + *
    • AL[5] = C11,-1 + *
    + *

    + * The iterator method returns objects of type BuildStep, not + * objects of type Vertex. + * A BuildStep contains a Vertex and a result code, + * accessible via getResult method. There are five result values. + * POSSIBLE denotes that the current step represents a + * Certificate that the builder is considering at this point in + * the build. FOLLOW denotes a Certificate (one of + * those noted as POSSIBLE) that the builder is using to try + * extending the chain. BACK represents that a + * FOLLOW was incorrect, and is being removed from the chain. + * There is exactly one FOLLOW for each BACK. The + * values SUCCEED and FAIL mean that we've come to + * the end of the build process, and there will not be any more entries in + * the list. + *

    + * @see BuildStep + * @see Vertex + *

    + * @author seth proctor + * @since 1.4 + */ +public class AdjacencyList { + + // the actual set of steps the AdjacencyList represents + private ArrayList mStepList; + + // the original list, just for the toString method + private List> mOrigList; + + /** + * Constructs a new AdjacencyList based on the specified + * List. See the example above. + * + * @param list a List of Lists of + * Vertex objects + */ + public AdjacencyList(List> list) { + mStepList = new ArrayList(); + mOrigList = list; + buildList(list, 0, null); + } + + /** + * Gets an Iterator to iterate over the set of + * BuildSteps in build-order. Any attempts to change + * the list through the remove method will fail. + * + * @return an Iterator over the BuildSteps + */ + public Iterator iterator() { + return Collections.unmodifiableList(mStepList).iterator(); + } + + /** + * Recursive, private method which actually builds the step list from + * the given adjacency list. Follow is the parent BuildStep + * that we followed to get here, and if it's null, it means that we're + * at the start. + */ + private boolean buildList(List> theList, int index, + BuildStep follow) { + + // Each time this method is called, we're examining a new list + // from the global list. So, we have to start by getting the list + // that contains the set of Vertexes we're considering. + List l = theList.get(index); + + // we're interested in the case where all indexes are -1... + boolean allNegOne = true; + // ...and in the case where every entry has a Throwable + boolean allXcps = true; + + for (Vertex v : l) { + if (v.getIndex() != -1) { + // count an empty list the same as an index of -1...this + // is to patch a bug somewhere in the builder + if (theList.get(v.getIndex()).size() != 0) + allNegOne = false; + } else { + if (v.getThrowable() == null) + allXcps = false; + } + // every entry, regardless of the final use for it, is always + // entered as a possible step before we take any actions + mStepList.add(new BuildStep(v, BuildStep.POSSIBLE)); + } + + if (allNegOne) { + // There are two cases that we could be looking at here. We + // may need to back up, or the build may have succeeded at + // this point. This is based on whether or not any + // exceptions were found in the list. + if (allXcps) { + // we need to go back...see if this is the last one + if (follow == null) + mStepList.add(new BuildStep(null, BuildStep.FAIL)); + else + mStepList.add(new BuildStep(follow.getVertex(), + BuildStep.BACK)); + + return false; + } else { + // we succeeded...now the only question is which is the + // successful step? If there's only one entry without + // a throwable, then that's the successful step. Otherwise, + // we'll have to make some guesses... + List possibles = new ArrayList<>(); + for (Vertex v : l) { + if (v.getThrowable() == null) + possibles.add(v); + } + + if (possibles.size() == 1) { + // real easy...we've found the final Vertex + mStepList.add(new BuildStep(possibles.get(0), + BuildStep.SUCCEED)); + } else { + // ok...at this point, there is more than one Cert + // which might be the succeed step...how do we know + // which it is? I'm going to assume that our builder + // algorithm is good enough to know which is the + // correct one, and put it first...but a FIXME goes + // here anyway, and we should be comparing to the + // target/initiator Cert... + mStepList.add(new BuildStep(possibles.get(0), + BuildStep.SUCCEED)); + } + + return true; + } + } else { + // There's at least one thing that we can try before we give + // up and go back. Run through the list now, and enter a new + // BuildStep for each path that we try to follow. If none of + // the paths we try produce a successful end, we're going to + // have to back out ourselves. + boolean success = false; + + for (Vertex v : l) { + + // Note that we'll only find a SUCCEED case when we're + // looking at the last possible path, so we don't need to + // consider success in the while loop + + if (v.getIndex() != -1) { + if (theList.get(v.getIndex()).size() != 0) { + // If the entry we're looking at doesn't have an + // index of -1, and doesn't lead to an empty list, + // then it's something we follow! + BuildStep bs = new BuildStep(v, BuildStep.FOLLOW); + mStepList.add(bs); + success = buildList(theList, v.getIndex(), bs); + } + } + } + + if (success) { + // We're already finished! + return true; + } else { + // We failed, and we've exhausted all the paths that we + // could take. The only choice is to back ourselves out. + if (follow == null) + mStepList.add(new BuildStep(null, BuildStep.FAIL)); + else + mStepList.add(new BuildStep(follow.getVertex(), + BuildStep.BACK)); + + return false; + } + } + } + + /** + * Prints out a string representation of this AdjacencyList. + * + * @return String representation + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder("[\n"); + + int i = 0; + for (List l : mOrigList) { + sb.append("LinkedList[").append(i++).append("]:\n"); + + for (Vertex step : l) { + sb.append(step.toString()).append("\n"); + } + } + sb.append("]\n"); + + return sb.toString(); + } +} diff --git a/src/sun/security/provider/certpath/AlgorithmChecker.java b/src/sun/security/provider/certpath/AlgorithmChecker.java new file mode 100644 index 00000000..b2b6553a --- /dev/null +++ b/src/sun/security/provider/certpath/AlgorithmChecker.java @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.EnumSet; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.KeyFactory; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.cert.Certificate; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.TrustAnchor; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.PKIXReason; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CRLImpl; +import sun.security.x509.AlgorithmId; + +/** + * A PKIXCertPathChecker implementation to check whether a + * specified certificate contains the required algorithm constraints. + *

    + * Certificate fields such as the subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to + * the specified algorithm constraints. + * + * @see PKIXCertPathChecker + * @see PKIXParameters + */ +final public class AlgorithmChecker extends PKIXCertPathChecker { + + private final AlgorithmConstraints constraints; + private final PublicKey trustedPubKey; + private PublicKey prevPubKey; + + private final static Set SIGNATURE_PRIMITIVE_SET = + Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); + + private final static DisabledAlgorithmConstraints + certPathDefaultConstraints = new DisabledAlgorithmConstraints( + DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); + + /** + * Create a new AlgorithmChecker with the algorithm + * constraints specified in security property + * "jdk.certpath.disabledAlgorithms". + * + * @param anchor the trust anchor selected to validate the target + * certificate + */ + public AlgorithmChecker(TrustAnchor anchor) { + this(anchor, certPathDefaultConstraints); + } + + /** + * Create a new AlgorithmChecker with the + * given {@code AlgorithmConstraints}. + *

    + * Note that this constructor will be used to check a certification + * path where the trust anchor is unknown, or a certificate list which may + * contain the trust anchor. This constructor is used by SunJSSE. + * + * @param constraints the algorithm constraints (or null) + */ + public AlgorithmChecker(AlgorithmConstraints constraints) { + this.prevPubKey = null; + this.trustedPubKey = null; + this.constraints = constraints; + } + + /** + * Create a new AlgorithmChecker with the + * given TrustAnchor and AlgorithmConstraints. + * + * @param anchor the trust anchor selected to validate the target + * certificate + * @param constraints the algorithm constraints (or null) + * + * @throws IllegalArgumentException if the anchor is null + */ + public AlgorithmChecker(TrustAnchor anchor, + AlgorithmConstraints constraints) { + + if (anchor == null) { + throw new IllegalArgumentException( + "The trust anchor cannot be null"); + } + + if (anchor.getTrustedCert() != null) { + this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); + } else { + this.trustedPubKey = anchor.getCAPublicKey(); + } + + this.prevPubKey = trustedPubKey; + this.constraints = constraints; + } + + @Override + public void init(boolean forward) throws CertPathValidatorException { + // Note that this class does not support forward mode. + if (!forward) { + if (trustedPubKey != null) { + prevPubKey = trustedPubKey; + } else { + prevPubKey = null; + } + } else { + throw new + CertPathValidatorException("forward checking not supported"); + } + } + + @Override + public boolean isForwardCheckingSupported() { + // Note that as this class does not support forward mode, the method + // will always returns false. + return false; + } + + @Override + public Set getSupportedExtensions() { + return null; + } + + @Override + public void check(Certificate cert, + Collection unresolvedCritExts) + throws CertPathValidatorException { + + if (!(cert instanceof X509Certificate) || constraints == null) { + // ignore the check for non-x.509 certificate or null constraints + return; + } + + X509CertImpl x509Cert = null; + try { + x509Cert = X509CertImpl.toImpl((X509Certificate)cert); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + PublicKey currPubKey = x509Cert.getPublicKey(); + String currSigAlg = x509Cert.getSigAlgName(); + + AlgorithmId algorithmId = null; + try { + algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); + + // Check the current signature algorithm + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed: " + currSigAlg, + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + + // check the key usage and key size + boolean[] keyUsage = x509Cert.getKeyUsage(); + if (keyUsage != null && keyUsage.length < 9) { + throw new CertPathValidatorException( + "incorrect KeyUsage extension", + null, null, -1, PKIXReason.INVALID_KEY_USAGE); + } + + if (keyUsage != null) { + Set primitives = + EnumSet.noneOf(CryptoPrimitive.class); + + if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { + // keyUsage[0]: KeyUsage.digitalSignature + // keyUsage[1]: KeyUsage.nonRepudiation + // keyUsage[5]: KeyUsage.keyCertSign + // keyUsage[6]: KeyUsage.cRLSign + primitives.add(CryptoPrimitive.SIGNATURE); + } + + if (keyUsage[2]) { // KeyUsage.keyEncipherment + primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); + } + + if (keyUsage[3]) { // KeyUsage.dataEncipherment + primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); + } + + if (keyUsage[4]) { // KeyUsage.keyAgreement + primitives.add(CryptoPrimitive.KEY_AGREEMENT); + } + + // KeyUsage.encipherOnly and KeyUsage.decipherOnly are + // undefined in the absence of the keyAgreement bit. + + if (!primitives.isEmpty()) { + if (!constraints.permits(primitives, currPubKey)) { + throw new CertPathValidatorException( + "algorithm constraints check failed", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } + } + + // Check with previous cert for signature algorithm and public key + if (prevPubKey != null) { + if (currSigAlg != null) { + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, prevPubKey, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed: " + currSigAlg, + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } + + // Inherit key parameters from previous key + if (PKIX.isDSAPublicKeyWithoutParams(currPubKey)) { + // Inherit DSA parameters from previous key + if (!(prevPubKey instanceof DSAPublicKey)) { + throw new CertPathValidatorException("Input key is not " + + "of a appropriate type for inheriting parameters"); + } + + DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); + if (params == null) { + throw new CertPathValidatorException( + "Key parameters missing"); + } + + try { + BigInteger y = ((DSAPublicKey)currPubKey).getY(); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPublicKeySpec ks = new DSAPublicKeySpec(y, + params.getP(), + params.getQ(), + params.getG()); + currPubKey = kf.generatePublic(ks); + } catch (GeneralSecurityException e) { + throw new CertPathValidatorException("Unable to generate " + + "key with inherited parameters: " + e.getMessage(), e); + } + } + } + + // reset the previous public key + prevPubKey = currPubKey; + + // check the extended key usage, ignore the check now + // List extendedKeyUsages = x509Cert.getExtendedKeyUsage(); + + // DO NOT remove any unresolved critical extensions + } + + /** + * Try to set the trust anchor of the checker. + *

    + * If there is no trust anchor specified and the checker has not started, + * set the trust anchor. + * + * @param anchor the trust anchor selected to validate the target + * certificate + */ + void trySetTrustAnchor(TrustAnchor anchor) { + // Don't bother if the check has started or trust anchor has already + // specified. + if (prevPubKey == null) { + if (anchor == null) { + throw new IllegalArgumentException( + "The trust anchor cannot be null"); + } + + // Don't bother to change the trustedPubKey. + if (anchor.getTrustedCert() != null) { + prevPubKey = anchor.getTrustedCert().getPublicKey(); + } else { + prevPubKey = anchor.getCAPublicKey(); + } + } + } + + /** + * Check the signature algorithm with the specified public key. + * + * @param key the public key to verify the CRL signature + * @param crl the target CRL + */ + static void check(PublicKey key, X509CRL crl) + throws CertPathValidatorException { + + X509CRLImpl x509CRLImpl = null; + try { + x509CRLImpl = X509CRLImpl.toImpl(crl); + } catch (CRLException ce) { + throw new CertPathValidatorException(ce); + } + + AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); + check(key, algorithmId); + } + + /** + * Check the signature algorithm with the specified public key. + * + * @param key the public key to verify the CRL signature + * @param crl the target CRL + */ + static void check(PublicKey key, AlgorithmId algorithmId) + throws CertPathValidatorException { + String sigAlgName = algorithmId.getName(); + AlgorithmParameters sigAlgParams = algorithmId.getParameters(); + + if (!certPathDefaultConstraints.permits( + SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { + throw new CertPathValidatorException( + "algorithm check failed: " + sigAlgName + " is disabled", + null, null, -1, BasicReason.ALGORITHM_CONSTRAINED); + } + } + +} + diff --git a/src/sun/security/provider/certpath/BasicChecker.java b/src/sun/security/provider/certpath/BasicChecker.java new file mode 100644 index 00000000..49a03687 --- /dev/null +++ b/src/sun/security/provider/certpath/BasicChecker.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.X509Certificate; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; +import java.security.cert.TrustAnchor; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPublicKey; +import java.security.spec.DSAPublicKeySpec; +import javax.security.auth.x500.X500Principal; +import sun.security.x509.X500Name; +import sun.security.util.Debug; + +/** + * BasicChecker is a PKIXCertPathChecker that checks the basic information + * on a PKIX certificate, namely the signature, timestamp, and subject/issuer + * name chaining. + * + * @since 1.4 + * @author Yassir Elley + */ +class BasicChecker extends PKIXCertPathChecker { + + private static final Debug debug = Debug.getInstance("certpath"); + private final PublicKey trustedPubKey; + private final X500Principal caName; + private final Date date; + private final String sigProvider; + private final boolean sigOnly; + private X500Principal prevSubject; + private PublicKey prevPubKey; + + /** + * Constructor that initializes the input parameters. + * + * @param anchor the anchor selected to validate the target certificate + * @param testDate the time for which the validity of the certificate + * should be determined + * @param sigProvider the name of the signature provider + * @param sigOnly true if only signature checking is to be done; + * if false, all checks are done + */ + BasicChecker(TrustAnchor anchor, Date date, String sigProvider, + boolean sigOnly) { + if (anchor.getTrustedCert() != null) { + this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); + this.caName = anchor.getTrustedCert().getSubjectX500Principal(); + } else { + this.trustedPubKey = anchor.getCAPublicKey(); + this.caName = anchor.getCA(); + } + this.date = date; + this.sigProvider = sigProvider; + this.sigOnly = sigOnly; + this.prevPubKey = trustedPubKey; + } + + /** + * Initializes the internal state of the checker from parameters + * specified in the constructor. + */ + @Override + public void init(boolean forward) throws CertPathValidatorException { + if (!forward) { + prevPubKey = trustedPubKey; + if (PKIX.isDSAPublicKeyWithoutParams(prevPubKey)) { + // If TrustAnchor is a DSA public key and it has no params, it + // cannot be used to verify the signature of the first cert, + // so throw exception + throw new CertPathValidatorException("Key parameters missing"); + } + prevSubject = caName; + } else { + throw new + CertPathValidatorException("forward checking not supported"); + } + } + + @Override + public boolean isForwardCheckingSupported() { + return false; + } + + @Override + public Set getSupportedExtensions() { + return null; + } + + /** + * Performs the signature, timestamp, and subject/issuer name chaining + * checks on the certificate using its internal state. This method does + * not remove any critical extensions from the Collection. + * + * @param cert the Certificate + * @param unresolvedCritExts a Collection of the unresolved critical + * extensions + * @throws CertPathValidatorException if certificate does not verify + */ + @Override + public void check(Certificate cert, Collection unresolvedCritExts) + throws CertPathValidatorException + { + X509Certificate currCert = (X509Certificate)cert; + + if (!sigOnly) { + verifyTimestamp(currCert); + verifyNameChaining(currCert); + } + verifySignature(currCert); + + updateState(currCert); + } + + /** + * Verifies the signature on the certificate using the previous public key. + * + * @param cert the X509Certificate + * @throws CertPathValidatorException if certificate does not verify + */ + private void verifySignature(X509Certificate cert) + throws CertPathValidatorException + { + String msg = "signature"; + if (debug != null) + debug.println("---checking " + msg + "..."); + + try { + cert.verify(prevPubKey, sigProvider); + } catch (SignatureException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, + BasicReason.INVALID_SIGNATURE); + } catch (GeneralSecurityException e) { + throw new CertPathValidatorException(msg + " check failed", e); + } + + if (debug != null) + debug.println(msg + " verified."); + } + + /** + * Internal method to verify the timestamp on a certificate + */ + private void verifyTimestamp(X509Certificate cert) + throws CertPathValidatorException + { + String msg = "timestamp"; + if (debug != null) + debug.println("---checking " + msg + ":" + date.toString() + "..."); + + try { + cert.checkValidity(date); + } catch (CertificateExpiredException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, BasicReason.EXPIRED); + } catch (CertificateNotYetValidException e) { + throw new CertPathValidatorException + (msg + " check failed", e, null, -1, BasicReason.NOT_YET_VALID); + } + + if (debug != null) + debug.println(msg + " verified."); + } + + /** + * Internal method to check that cert has a valid DN to be next in a chain + */ + private void verifyNameChaining(X509Certificate cert) + throws CertPathValidatorException + { + if (prevSubject != null) { + + String msg = "subject/issuer name chaining"; + if (debug != null) + debug.println("---checking " + msg + "..."); + + X500Principal currIssuer = cert.getIssuerX500Principal(); + + // reject null or empty issuer DNs + if (X500Name.asX500Name(currIssuer).isEmpty()) { + throw new CertPathValidatorException + (msg + " check failed: " + + "empty/null issuer DN in certificate is invalid", null, + null, -1, PKIXReason.NAME_CHAINING); + } + + if (!(currIssuer.equals(prevSubject))) { + throw new CertPathValidatorException + (msg + " check failed", null, null, -1, + PKIXReason.NAME_CHAINING); + } + + if (debug != null) + debug.println(msg + " verified."); + } + } + + /** + * Internal method to manage state information at each iteration + */ + private void updateState(X509Certificate currCert) + throws CertPathValidatorException + { + PublicKey cKey = currCert.getPublicKey(); + if (debug != null) { + debug.println("BasicChecker.updateState issuer: " + + currCert.getIssuerX500Principal().toString() + "; subject: " + + currCert.getSubjectX500Principal() + "; serial#: " + + currCert.getSerialNumber().toString()); + } + if (PKIX.isDSAPublicKeyWithoutParams(cKey)) { + // cKey needs to inherit DSA parameters from prev key + cKey = makeInheritedParamsKey(cKey, prevPubKey); + if (debug != null) debug.println("BasicChecker.updateState Made " + + "key with inherited params"); + } + prevPubKey = cKey; + prevSubject = currCert.getSubjectX500Principal(); + } + + /** + * Internal method to create a new key with inherited key parameters. + * + * @param keyValueKey key from which to obtain key value + * @param keyParamsKey key from which to obtain key parameters + * @return new public key having value and parameters + * @throws CertPathValidatorException if keys are not appropriate types + * for this operation + */ + static PublicKey makeInheritedParamsKey(PublicKey keyValueKey, + PublicKey keyParamsKey) throws CertPathValidatorException + { + if (!(keyValueKey instanceof DSAPublicKey) || + !(keyParamsKey instanceof DSAPublicKey)) + throw new CertPathValidatorException("Input key is not " + + "appropriate type for " + + "inheriting parameters"); + DSAParams params = ((DSAPublicKey)keyParamsKey).getParams(); + if (params == null) + throw new CertPathValidatorException("Key parameters missing"); + try { + BigInteger y = ((DSAPublicKey)keyValueKey).getY(); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPublicKeySpec ks = new DSAPublicKeySpec(y, + params.getP(), + params.getQ(), + params.getG()); + return kf.generatePublic(ks); + } catch (GeneralSecurityException e) { + throw new CertPathValidatorException("Unable to generate key with" + + " inherited parameters: " + + e.getMessage(), e); + } + } + + /** + * return the public key associated with the last certificate processed + * + * @return PublicKey the last public key processed + */ + PublicKey getPublicKey() { + return prevPubKey; + } +} diff --git a/src/sun/security/provider/certpath/BuildStep.java b/src/sun/security/provider/certpath/BuildStep.java new file mode 100644 index 00000000..a50ed187 --- /dev/null +++ b/src/sun/security/provider/certpath/BuildStep.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.cert.X509Certificate; + +/** + * Describes one step of a certification path build, consisting of a + * Vertex state description, a certificate, a possible throwable, + * and a result code. + * + * @author Anne Anderson + * @since 1.4 + * @see Vertex + */ +public class BuildStep { + + private Vertex vertex; + private X509Certificate cert; + private Throwable throwable; + private int result; + + /** + * result code associated with a certificate that may continue a path from + * the current certificate. + */ + public static final int POSSIBLE = 1; + + /** + * result code associated with a certificate that was tried, but that + * represents an unsuccessful path, so the certificate has been backed out + * to allow backtracking to the next possible path. + */ + public static final int BACK = 2; + + /** + * result code associated with a certificate that successfully continues the + * current path, but does not yet reach the target. + */ + public static final int FOLLOW = 3; + + /** + * result code associated with a certificate that represents the end of the + * last possible path, where no path successfully reached the target. + */ + public static final int FAIL = 4; + + /** + * result code associated with a certificate that represents the end of a + * path that successfully reaches the target. + */ + public static final int SUCCEED = 5; + + /** + * construct a BuildStep + * + * @param vtx description of the vertex at this step + * @param res result, where result is one of POSSIBLE, BACK, + * FOLLOW, FAIL, SUCCEED + */ + public BuildStep(Vertex vtx, int res) { + vertex = vtx; + if (vertex != null) { + cert = vertex.getCertificate(); + throwable = vertex.getThrowable(); + } + result = res; + } + + /** + * return vertex description for this build step + * + * @returns Vertex + */ + public Vertex getVertex() { + return vertex; + } + + /** + * return the certificate associated with this build step + * + * @returns X509Certificate + */ + public X509Certificate getCertificate() { + return cert; + } + + /** + * return string form of issuer name from certificate associated with this + * build step + * + * @returns String form of issuer name or null, if no certificate. + */ + public String getIssuerName() { + return getIssuerName(null); + } + + /** + * return string form of issuer name from certificate associated with this + * build step, or a default name if no certificate associated with this + * build step, or if issuer name could not be obtained from the certificate. + * + * @param defaultName name to use as default if unable to return an issuer + * name from the certificate, or if no certificate. + * @returns String form of issuer name or defaultName, if no certificate or + * exception received while trying to extract issuer name from certificate. + */ + public String getIssuerName(String defaultName) { + return (cert == null ? defaultName + : cert.getIssuerX500Principal().toString()); + } + + /** + * return string form of subject name from certificate associated with this + * build step. + * + * @returns String form of subject name or null, if no certificate. + */ + public String getSubjectName() { + return getSubjectName(null); + } + + /** + * return string form of subject name from certificate associated with this + * build step, or a default name if no certificate associated with this + * build step, or if subject name could not be obtained from the + * certificate. + * + * @param defaultName name to use as default if unable to return a subject + * name from the certificate, or if no certificate. + * @returns String form of subject name or defaultName, if no certificate or + * if an exception was received while attempting to extract the subject name + * from the certificate. + */ + public String getSubjectName(String defaultName) { + return (cert == null ? defaultName + : cert.getSubjectX500Principal().toString()); + } + + /** + * return the exception associated with this build step. + * + * @returns Throwable + */ + public Throwable getThrowable() { + return throwable; + } + + /** + * return the result code associated with this build step. The result codes + * are POSSIBLE, FOLLOW, BACK, FAIL, SUCCEED. + * + * @returns int result code + */ + public int getResult() { + return result; + } + + /** + * return a string representing the meaning of the result code associated + * with this build step. + * + * @param res result code + * @returns String string representing meaning of the result code + */ + public String resultToString(int res) { + String resultString = ""; + switch (res) { + case POSSIBLE: + resultString = "Certificate to be tried.\n"; + break; + case BACK: + resultString = "Certificate backed out since path does not " + + "satisfy build requirements.\n"; + break; + case FOLLOW: + resultString = "Certificate satisfies conditions.\n"; + break; + case FAIL: + resultString = "Certificate backed out since path does not " + + "satisfy conditions.\n"; + break; + case SUCCEED: + resultString = "Certificate satisfies conditions.\n"; + break; + default: + resultString = "Internal error: Invalid step result value.\n"; + } + return resultString; + } + + /** + * return a string representation of this build step, showing minimal + * detail. + * + * @returns String + */ + @Override + public String toString() { + String out = "Internal Error\n"; + switch (result) { + case BACK: + case FAIL: + out = resultToString(result); + out = out + vertex.throwableToString(); + break; + case FOLLOW: + case SUCCEED: + case POSSIBLE: + out = resultToString(result); + break; + default: + out = "Internal Error: Invalid step result\n"; + } + return out; + } + + /** + * return a string representation of this build step, showing all detail of + * the vertex state appropriate to the result of this build step, and the + * certificate contents. + * + * @returns String + */ + public String verboseToString() { + String out = resultToString(getResult()); + switch (result) { + case BACK: + case FAIL: + out = out + vertex.throwableToString(); + break; + case FOLLOW: + case SUCCEED: + out = out + vertex.moreToString(); + break; + case POSSIBLE: + break; + default: + break; + } + out = out + "Certificate contains:\n" + vertex.certToString(); + return out; + } + + /** + * return a string representation of this build step, including all possible + * detail of the vertex state, but not including the certificate contents. + * + * @returns String + */ + public String fullToString() { + return resultToString(getResult()) + vertex.toString(); + } +} diff --git a/src/sun/security/provider/certpath/Builder.java b/src/sun/security/provider/certpath/Builder.java new file mode 100644 index 00000000..e053b208 --- /dev/null +++ b/src/sun/security/provider/certpath/Builder.java @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.AccessController; +import java.security.GeneralSecurityException; +import java.security.cert.*; +import java.util.*; + +import sun.security.action.GetBooleanAction; +import sun.security.provider.certpath.PKIX.BuilderParams; +import sun.security.util.Debug; +import sun.security.x509.GeneralNames; +import sun.security.x509.GeneralNameInterface; +import sun.security.x509.GeneralSubtrees; +import sun.security.x509.NameConstraintsExtension; +import sun.security.x509.SubjectAlternativeNameExtension; +import sun.security.x509.X500Name; +import sun.security.x509.X509CertImpl; + +/** + * Abstract class representing a builder, which is able to retrieve + * matching certificates and is able to verify a particular certificate. + * + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ + +public abstract class Builder { + + private static final Debug debug = Debug.getInstance("certpath"); + private Set matchingPolicies; + final BuilderParams buildParams; + final X509CertSelector targetCertConstraints; + + /** + * Flag indicating whether support for the caIssuers field of the + * Authority Information Access extension shall be enabled. Currently + * disabled by default for compatibility reasons. + */ + final static boolean USE_AIA = AccessController.doPrivileged + (new GetBooleanAction("com.sun.security.enableAIAcaIssuers")); + + /** + * Initialize the builder with the input parameters. + * + * @param params the parameter set used to build a certification path + */ + Builder(BuilderParams buildParams) { + this.buildParams = buildParams; + this.targetCertConstraints = + (X509CertSelector)buildParams.targetCertConstraints(); + } + + /** + * Retrieves certificates from the list of certStores using the buildParams + * and the currentState as a filter + * + * @param currentState the current State + * @param certStores list of CertStores + */ + abstract Collection getMatchingCerts + (State currentState, List certStores) + throws CertStoreException, CertificateException, IOException; + + /** + * Verifies the cert against the currentState, using the certPathList + * generated thus far to help with loop detection + * + * @param cert the certificate to be verified + * @param currentState the current state against which the cert is verified + * @param certPathList the certPathList generated thus far + */ + abstract void verifyCert(X509Certificate cert, State currentState, + List certPathList) + throws GeneralSecurityException; + + /** + * Verifies whether the input certificate completes the path. + * When building forward, a trust anchor will complete the path. + * When building reverse, the target certificate will complete the path. + * + * @param cert the certificate to test + * @return a boolean value indicating whether the cert completes the path. + */ + abstract boolean isPathCompleted(X509Certificate cert); + + /** + * Adds the certificate to the certPathList + * + * @param cert the certificate to be added + * @param certPathList the certification path list + */ + abstract void addCertToPath(X509Certificate cert, + LinkedList certPathList); + + /** + * Removes final certificate from the certPathList + * + * @param certPathList the certification path list + */ + abstract void removeFinalCertFromPath + (LinkedList certPathList); + + /** + * get distance of one GeneralName from another + * + * @param base GeneralName at base of subtree + * @param test GeneralName to be tested against base + * @param incomparable the value to return if the names are + * incomparable + * @return distance of test name from base, where 0 + * means exact match, 1 means test is an immediate + * child of base, 2 means test is a grandchild, etc. + * -1 means test is a parent of base, -2 means test + * is a grandparent, etc. + */ + static int distance(GeneralNameInterface base, + GeneralNameInterface test, int incomparable) + { + switch (base.constrains(test)) { + case GeneralNameInterface.NAME_DIFF_TYPE: + if (debug != null) { + debug.println("Builder.distance(): Names are different types"); + } + return incomparable; + case GeneralNameInterface.NAME_SAME_TYPE: + if (debug != null) { + debug.println("Builder.distance(): Names are same type but " + + "in different subtrees"); + } + return incomparable; + case GeneralNameInterface.NAME_MATCH: + return 0; + case GeneralNameInterface.NAME_WIDENS: + break; + case GeneralNameInterface.NAME_NARROWS: + break; + default: // should never occur + return incomparable; + } + + /* names are in same subtree */ + return test.subtreeDepth() - base.subtreeDepth(); + } + + /** + * get hop distance of one GeneralName from another in links where + * the names need not have an ancestor/descendant relationship. + * For example, the hop distance from ou=D,ou=C,o=B,c=US to + * ou=F,ou=E,ou=C,o=B,c=US is 3: D->C, C->E, E->F. The hop distance + * from ou=C,o=B,c=US to ou=D,ou=C,o=B,c=US is -1: C->D + * + * @param base GeneralName + * @param test GeneralName to be tested against base + * @param incomparable the value to return if the names are + * incomparable + * @return distance of test name from base measured in hops in the + * namespace hierarchy, where 0 means exact match. Result + * is positive if path is some number of up hops followed by + * some number of down hops; result is negative if path is + * some number of down hops. + */ + static int hops(GeneralNameInterface base, GeneralNameInterface test, + int incomparable) + { + int baseRtest = base.constrains(test); + switch (baseRtest) { + case GeneralNameInterface.NAME_DIFF_TYPE: + if (debug != null) { + debug.println("Builder.hops(): Names are different types"); + } + return incomparable; + case GeneralNameInterface.NAME_SAME_TYPE: + /* base and test are in different subtrees */ + break; + case GeneralNameInterface.NAME_MATCH: + /* base matches test */ + return 0; + case GeneralNameInterface.NAME_WIDENS: + /* base is ancestor of test */ + return (test.subtreeDepth()-base.subtreeDepth()); + case GeneralNameInterface.NAME_NARROWS: + /* base is descendant of test */ + return (test.subtreeDepth()-base.subtreeDepth()); + default: // should never occur + return incomparable; + } + + /* names are in different subtrees */ + if (base.getType() != GeneralNameInterface.NAME_DIRECTORY) { + if (debug != null) { + debug.println("Builder.hops(): hopDistance not implemented " + + "for this name type"); + } + return incomparable; + } + X500Name baseName = (X500Name)base; + X500Name testName = (X500Name)test; + X500Name commonName = baseName.commonAncestor(testName); + if (commonName == null) { + if (debug != null) { + debug.println("Builder.hops(): Names are in different " + + "namespaces"); + } + return incomparable; + } else { + int commonDistance = commonName.subtreeDepth(); + int baseDistance = baseName.subtreeDepth(); + int testDistance = testName.subtreeDepth(); + return (baseDistance + testDistance - (2 * commonDistance)); + } + } + + /** + * Determine how close a given certificate gets you toward + * a given target. + * + * @param constraints Current NameConstraints; if null, + * then caller must verify NameConstraints + * independently, realizing that this certificate + * may not actually lead to the target at all. + * @param cert Candidate certificate for chain + * @param target GeneralNameInterface name of target + * @return distance from this certificate to target: + *

      + *
    • -1 means certificate could be CA for target, but + * there are no NameConstraints limiting how close + *
    • 0 means certificate subject or subjectAltName + * matches target + *
    • 1 means certificate is permitted to be CA for + * target. + *
    • 2 means certificate is permitted to be CA for + * parent of target. + *
    • >0 in general, means certificate is permitted + * to be a CA for this distance higher in the naming + * hierarchy than the target, plus 1. + *
    + *

    Note that the subject and/or subjectAltName of the + * candidate cert does not have to be an ancestor of the + * target in order to be a CA that can issue a certificate to + * the target. In these cases, the target distance is calculated + * by inspecting the NameConstraints extension in the candidate + * certificate. For example, suppose the target is an X.500 DN with + * a value of "CN=mullan,OU=ireland,O=sun,C=us" and the + * NameConstraints extension in the candidate certificate + * includes a permitted component of "O=sun,C=us", which implies + * that the candidate certificate is allowed to issue certs in + * the "O=sun,C=us" namespace. The target distance is 3 + * ((distance of permitted NC from target) + 1). + * The (+1) is added to distinguish the result from the case + * which returns (0). + * @throws IOException if certificate does not get closer + */ + static int targetDistance(NameConstraintsExtension constraints, + X509Certificate cert, GeneralNameInterface target) + throws IOException + { + /* ensure that certificate satisfies existing name constraints */ + if (constraints != null && !constraints.verify(cert)) { + throw new IOException("certificate does not satisfy existing name " + + "constraints"); + } + + X509CertImpl certImpl; + try { + certImpl = X509CertImpl.toImpl(cert); + } catch (CertificateException e) { + throw new IOException("Invalid certificate", e); + } + /* see if certificate subject matches target */ + X500Name subject = X500Name.asX500Name(certImpl.getSubjectX500Principal()); + if (subject.equals(target)) { + /* match! */ + return 0; + } + + SubjectAlternativeNameExtension altNameExt = + certImpl.getSubjectAlternativeNameExtension(); + if (altNameExt != null) { + GeneralNames altNames = altNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + /* see if any alternative name matches target */ + if (altNames != null) { + for (int j = 0, n = altNames.size(); j < n; j++) { + GeneralNameInterface altName = altNames.get(j).getName(); + if (altName.equals(target)) { + return 0; + } + } + } + } + + + /* no exact match; see if certificate can get us to target */ + + /* first, get NameConstraints out of certificate */ + NameConstraintsExtension ncExt = certImpl.getNameConstraintsExtension(); + if (ncExt == null) { + return -1; + } + + /* merge certificate's NameConstraints with current NameConstraints */ + if (constraints != null) { + constraints.merge(ncExt); + } else { + // Make sure we do a clone here, because we're probably + // going to modify this object later and we don't want to + // be sharing it with a Certificate object! + constraints = (NameConstraintsExtension) ncExt.clone(); + } + + if (debug != null) { + debug.println("Builder.targetDistance() merged constraints: " + + String.valueOf(constraints)); + } + /* reduce permitted by excluded */ + GeneralSubtrees permitted = + constraints.get(NameConstraintsExtension.PERMITTED_SUBTREES); + GeneralSubtrees excluded = + constraints.get(NameConstraintsExtension.EXCLUDED_SUBTREES); + if (permitted != null) { + permitted.reduce(excluded); + } + if (debug != null) { + debug.println("Builder.targetDistance() reduced constraints: " + + permitted); + } + /* see if new merged constraints allow target */ + if (!constraints.verify(target)) { + throw new IOException("New certificate not allowed to sign " + + "certificate for target"); + } + /* find distance to target, if any, in permitted */ + if (permitted == null) { + /* certificate is unconstrained; could sign for anything */ + return -1; + } + for (int i = 0, n = permitted.size(); i < n; i++) { + GeneralNameInterface perName = permitted.get(i).getName().getName(); + int distance = distance(perName, target, -1); + if (distance >= 0) { + return (distance + 1); + } + } + /* no matching type in permitted; cert holder could certify target */ + return -1; + } + + /** + * This method can be used as an optimization to filter out + * certificates that do not have policies which are valid. + * It returns the set of policies (String OIDs) that should exist in + * the certificate policies extension of the certificate that is + * needed by the builder. The logic applied is as follows: + *

    + * 1) If some initial policies have been set *and* policy mappings are + * inhibited, then acceptable certificates are those that include + * the ANY_POLICY OID or with policies that intersect with the + * initial policies. + * 2) If no initial policies have been set *or* policy mappings are + * not inhibited then we don't have much to work with. All we know is + * that a certificate must have *some* policy because if it didn't + * have any policy then the policy tree would become null (and validation + * would fail). + * + * @return the Set of policies any of which must exist in a + * cert's certificate policies extension in order for a cert to be selected. + */ + Set getMatchingPolicies() { + if (matchingPolicies != null) { + Set initialPolicies = buildParams.initialPolicies(); + if ((!initialPolicies.isEmpty()) && + (!initialPolicies.contains(PolicyChecker.ANY_POLICY)) && + (buildParams.policyMappingInhibited())) + { + matchingPolicies = new HashSet<>(initialPolicies); + matchingPolicies.add(PolicyChecker.ANY_POLICY); + } else { + // we just return an empty set to make sure that there is + // at least a certificate policies extension in the cert + matchingPolicies = Collections.emptySet(); + } + } + return matchingPolicies; + } + + /** + * Search the specified CertStores and add all certificates matching + * selector to resultCerts. Self-signed certs are not useful here + * and therefore ignored. + * + * If the targetCert criterion of the selector is set, only that cert + * is examined and the CertStores are not searched. + * + * If checkAll is true, all CertStores are searched for matching certs. + * If false, the method returns as soon as the first CertStore returns + * a matching cert(s). + * + * Returns true iff resultCerts changed (a cert was added to the collection) + */ + boolean addMatchingCerts(X509CertSelector selector, + Collection certStores, + Collection resultCerts, + boolean checkAll) + { + X509Certificate targetCert = selector.getCertificate(); + if (targetCert != null) { + // no need to search CertStores + if (selector.match(targetCert) && !X509CertImpl.isSelfSigned + (targetCert, buildParams.sigProvider())) { + if (debug != null) { + debug.println("Builder.addMatchingCerts: adding target cert"); + } + return resultCerts.add(targetCert); + } + return false; + } + boolean add = false; + for (CertStore store : certStores) { + try { + Collection certs = + store.getCertificates(selector); + for (Certificate cert : certs) { + if (!X509CertImpl.isSelfSigned + ((X509Certificate)cert, buildParams.sigProvider())) { + if (resultCerts.add((X509Certificate)cert)) { + add = true; + } + } + } + if (!checkAll && add) { + return true; + } + } catch (CertStoreException cse) { + // if getCertificates throws a CertStoreException, we ignore + // it and move on to the next CertStore + if (debug != null) { + debug.println("Builder.addMatchingCerts, non-fatal " + + "exception retrieving certs: " + cse); + cse.printStackTrace(); + } + } + } + return add; + } +} diff --git a/src/sun/security/provider/certpath/CertId.java b/src/sun/security/provider/certpath/CertId.java new file mode 100644 index 00000000..ff7be695 --- /dev/null +++ b/src/sun/security/provider/certpath/CertId.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import javax.security.auth.x500.X500Principal; +import sun.misc.HexDumpEncoder; +import sun.security.x509.*; +import sun.security.util.*; + +/** + * This class corresponds to the CertId field in OCSP Request + * and the OCSP Response. The ASN.1 definition for CertID is defined + * in RFC 2560 as: + *

    + *
    + * CertID          ::=     SEQUENCE {
    + *      hashAlgorithm       AlgorithmIdentifier,
    + *      issuerNameHash      OCTET STRING, -- Hash of Issuer's DN
    + *      issuerKeyHash       OCTET STRING, -- Hash of Issuers public key
    + *      serialNumber        CertificateSerialNumber
    + *      }
    + *
    + * 
    + * + * @author Ram Marti + */ + +public class CertId { + + private static final boolean debug = false; + private static final AlgorithmId SHA1_ALGID + = new AlgorithmId(AlgorithmId.SHA_oid); + private final AlgorithmId hashAlgId; + private final byte[] issuerNameHash; + private final byte[] issuerKeyHash; + private final SerialNumber certSerialNumber; + private int myhash = -1; // hashcode for this CertId + + /** + * Creates a CertId. The hash algorithm used is SHA-1. + */ + public CertId(X509Certificate issuerCert, SerialNumber serialNumber) + throws IOException { + + this(issuerCert.getSubjectX500Principal(), + issuerCert.getPublicKey(), serialNumber); + } + + public CertId(X500Principal issuerName, PublicKey issuerKey, + SerialNumber serialNumber) throws IOException { + + // compute issuerNameHash + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException nsae) { + throw new IOException("Unable to create CertId", nsae); + } + hashAlgId = SHA1_ALGID; + md.update(issuerName.getEncoded()); + issuerNameHash = md.digest(); + + // compute issuerKeyHash (remove the tag and length) + byte[] pubKey = issuerKey.getEncoded(); + DerValue val = new DerValue(pubKey); + DerValue[] seq = new DerValue[2]; + seq[0] = val.data.getDerValue(); // AlgorithmID + seq[1] = val.data.getDerValue(); // Key + byte[] keyBytes = seq[1].getBitString(); + md.update(keyBytes); + issuerKeyHash = md.digest(); + certSerialNumber = serialNumber; + + if (debug) { + HexDumpEncoder encoder = new HexDumpEncoder(); + System.out.println("Issuer Name is " + issuerName); + System.out.println("issuerNameHash is " + + encoder.encodeBuffer(issuerNameHash)); + System.out.println("issuerKeyHash is " + + encoder.encodeBuffer(issuerKeyHash)); + System.out.println("SerialNumber is " + serialNumber.getNumber()); + } + } + + /** + * Creates a CertId from its ASN.1 DER encoding. + */ + public CertId(DerInputStream derIn) throws IOException { + hashAlgId = AlgorithmId.parse(derIn.getDerValue()); + issuerNameHash = derIn.getOctetString(); + issuerKeyHash = derIn.getOctetString(); + certSerialNumber = new SerialNumber(derIn); + } + + /** + * Return the hash algorithm identifier. + */ + public AlgorithmId getHashAlgorithm() { + return hashAlgId; + } + + /** + * Return the hash value for the issuer name. + */ + public byte[] getIssuerNameHash() { + return issuerNameHash; + } + + /** + * Return the hash value for the issuer key. + */ + public byte[] getIssuerKeyHash() { + return issuerKeyHash; + } + + /** + * Return the serial number. + */ + public BigInteger getSerialNumber() { + return certSerialNumber.getNumber(); + } + + /** + * Encode the CertId using ASN.1 DER. + * The hash algorithm used is SHA-1. + */ + public void encode(DerOutputStream out) throws IOException { + + DerOutputStream tmp = new DerOutputStream(); + hashAlgId.encode(tmp); + tmp.putOctetString(issuerNameHash); + tmp.putOctetString(issuerKeyHash); + certSerialNumber.encode(tmp); + out.write(DerValue.tag_Sequence, tmp); + + if (debug) { + HexDumpEncoder encoder = new HexDumpEncoder(); + System.out.println("Encoded certId is " + + encoder.encode(out.toByteArray())); + } + } + + /** + * Returns a hashcode value for this CertId. + * + * @return the hashcode value. + */ + @Override public int hashCode() { + if (myhash == -1) { + myhash = hashAlgId.hashCode(); + for (int i = 0; i < issuerNameHash.length; i++) { + myhash += issuerNameHash[i] * i; + } + for (int i = 0; i < issuerKeyHash.length; i++) { + myhash += issuerKeyHash[i] * i; + } + myhash += certSerialNumber.getNumber().hashCode(); + } + return myhash; + } + + /** + * Compares this CertId for equality with the specified + * object. Two CertId objects are considered equal if their hash algorithms, + * their issuer name and issuer key hash values and their serial numbers + * are equal. + * + * @param other the object to test for equality with this object. + * @return true if the objects are considered equal, false otherwise. + */ + @Override public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || (!(other instanceof CertId))) { + return false; + } + + CertId that = (CertId) other; + if (hashAlgId.equals(that.getHashAlgorithm()) && + Arrays.equals(issuerNameHash, that.getIssuerNameHash()) && + Arrays.equals(issuerKeyHash, that.getIssuerKeyHash()) && + certSerialNumber.getNumber().equals(that.getSerialNumber())) { + return true; + } else { + return false; + } + } + + /** + * Create a string representation of the CertId. + */ + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("CertId \n"); + sb.append("Algorithm: " + hashAlgId.toString() +"\n"); + sb.append("issuerNameHash \n"); + HexDumpEncoder encoder = new HexDumpEncoder(); + sb.append(encoder.encode(issuerNameHash)); + sb.append("\nissuerKeyHash: \n"); + sb.append(encoder.encode(issuerKeyHash)); + sb.append("\n" + certSerialNumber.toString()); + return sb.toString(); + } +} diff --git a/src/sun/security/provider/certpath/CertPathHelper.java b/src/sun/security/provider/certpath/CertPathHelper.java new file mode 100644 index 00000000..af6da8a2 --- /dev/null +++ b/src/sun/security/provider/certpath/CertPathHelper.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.util.Date; +import java.util.Set; + +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; + +import sun.security.x509.GeneralNameInterface; + +/** + * Helper class that allows access to Sun specific known-public methods in the + * java.security.cert package. It relies on a subclass in the + * java.security.cert packages that is initialized before any of these methods + * are called (achieved via static initializers). + * + * The methods are made available in this fashion for performance reasons. + * + * @author Andreas Sterbenz + */ +public abstract class CertPathHelper { + + /** + * Object used to tunnel the calls. Initialized by CertPathHelperImpl. + */ + protected static CertPathHelper instance; + + protected CertPathHelper() { + // empty + } + + protected abstract void implSetPathToNames(X509CertSelector sel, + Set names); + + protected abstract void implSetDateAndTime(X509CRLSelector sel, Date date, long skew); + + static void setPathToNames(X509CertSelector sel, + Set names) { + instance.implSetPathToNames(sel, names); + } + + public static void setDateAndTime(X509CRLSelector sel, Date date, long skew) { + instance.implSetDateAndTime(sel, date, skew); + } +} diff --git a/src/sun/security/provider/certpath/CertStoreHelper.java b/src/sun/security/provider/certpath/CertStoreHelper.java new file mode 100644 index 00000000..b8b562ec --- /dev/null +++ b/src/sun/security/provider/certpath/CertStoreHelper.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; +import javax.security.auth.x500.X500Principal; +import java.io.IOException; + +import sun.security.util.Cache; + +/** + * Helper used by URICertStore and others when delegating to another CertStore + * to fetch certs and CRLs. + */ + +public abstract class CertStoreHelper { + + private static final int NUM_TYPES = 2; + private final static Map classMap = new HashMap<>(NUM_TYPES); + static { + classMap.put( + "LDAP", + "sun.security.provider.certpath.ldap.LDAPCertStoreHelper"); + classMap.put( + "SSLServer", + "sun.security.provider.certpath.ssl.SSLServerCertStoreHelper"); + }; + private static Cache cache + = Cache.newSoftMemoryCache(NUM_TYPES); + + public static CertStoreHelper getInstance(final String type) + throws NoSuchAlgorithmException + { + CertStoreHelper helper = cache.get(type); + if (helper != null) { + return helper; + } + final String cl = classMap.get(type); + if (cl == null) { + throw new NoSuchAlgorithmException(type + " not available"); + } + try { + helper = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public CertStoreHelper run() throws ClassNotFoundException { + try { + Class c = Class.forName(cl, true, null); + CertStoreHelper csh + = (CertStoreHelper)c.newInstance(); + cache.put(type, csh); + return csh; + } catch (InstantiationException | + IllegalAccessException e) { + throw new AssertionError(e); + } + } + }); + return helper; + } catch (PrivilegedActionException e) { + throw new NoSuchAlgorithmException(type + " not available", + e.getException()); + } + } + + static boolean isCausedByNetworkIssue(String type, CertStoreException cse) { + switch (type) { + case "LDAP": + case "SSLServer": + try { + CertStoreHelper csh = CertStoreHelper.getInstance(type); + return csh.isCausedByNetworkIssue(cse); + } catch (NoSuchAlgorithmException nsae) { + return false; + } + case "URI": + Throwable t = cse.getCause(); + return (t != null && t instanceof IOException); + default: + // we don't know about any other remote CertStore types + return false; + } + } + + /** + * Returns a CertStore using the given URI as parameters. + */ + public abstract CertStore getCertStore(URI uri) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException; + + /** + * Wraps an existing X509CertSelector when needing to avoid DN matching + * issues. + */ + public abstract X509CertSelector wrap(X509CertSelector selector, + X500Principal certSubject, + String dn) + throws IOException; + + /** + * Wraps an existing X509CRLSelector when needing to avoid DN matching + * issues. + */ + public abstract X509CRLSelector wrap(X509CRLSelector selector, + Collection certIssuers, + String dn) + throws IOException; + + /** + * Returns true if the cause of the CertStoreException is a network + * related issue. + */ + public abstract boolean isCausedByNetworkIssue(CertStoreException e); +} diff --git a/src/sun/security/provider/certpath/CollectionCertStore.java b/src/sun/security/provider/certpath/CollectionCertStore.java new file mode 100644 index 00000000..b5d2e3cd --- /dev/null +++ b/src/sun/security/provider/certpath/CollectionCertStore.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.Certificate; +import java.security.cert.CRL; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashSet; +import java.security.cert.CertSelector; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.CertStoreParameters; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.CRLSelector; +import java.security.cert.CertStoreSpi; + +/** + * A CertStore that retrieves Certificates and + * CRLs from a Collection. + *

    + * Before calling the {@link #engineGetCertificates engineGetCertificates} or + * {@link #engineGetCRLs engineGetCRLs} methods, the + * {@link #CollectionCertStore(CertStoreParameters) + * CollectionCertStore(CertStoreParameters)} constructor is called to + * create the CertStore and establish the + * Collection from which Certificates and + * CRLs will be retrieved. If the specified + * Collection contains an object that is not a + * Certificate or CRL, that object will be + * ignored. + *

    + * Concurrent Access + *

    + * As described in the javadoc for CertStoreSpi, the + * engineGetCertificates and engineGetCRLs methods + * must be thread-safe. That is, multiple threads may concurrently + * invoke these methods on a single CollectionCertStore + * object (or more than one) with no ill effects. + *

    + * This is achieved by requiring that the Collection passed to + * the {@link #CollectionCertStore(CertStoreParameters) + * CollectionCertStore(CertStoreParameters)} constructor (via the + * CollectionCertStoreParameters object) must have fail-fast + * iterators. Simultaneous modifications to the Collection can thus be + * detected and certificate or CRL retrieval can be retried. The fact that + * Certificates and CRLs must be thread-safe is also + * essential. + * + * @see CertStore + * + * @since 1.4 + * @author Steve Hanna + */ +public class CollectionCertStore extends CertStoreSpi { + + private Collection coll; + + /** + * Creates a CertStore with the specified parameters. + * For this class, the parameters object must be an instance of + * CollectionCertStoreParameters. The Collection + * included in the CollectionCertStoreParameters object + * must be thread-safe. + * + * @param params the algorithm parameters + * @exception InvalidAlgorithmParameterException if params is not an + * instance of CollectionCertStoreParameters + */ + public CollectionCertStore(CertStoreParameters params) + throws InvalidAlgorithmParameterException + { + super(params); + if (!(params instanceof CollectionCertStoreParameters)) + throw new InvalidAlgorithmParameterException( + "parameters must be CollectionCertStoreParameters"); + coll = ((CollectionCertStoreParameters) params).getCollection(); + } + + /** + * Returns a Collection of Certificates that + * match the specified selector. If no Certificates + * match the selector, an empty Collection will be returned. + * + * @param selector a CertSelector used to select which + * Certificates should be returned. Specify null + * to return all Certificates. + * @return a Collection of Certificates that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + public Collection engineGetCertificates + (CertSelector selector) throws CertStoreException { + if (coll == null) { + throw new CertStoreException("Collection is null"); + } + // Tolerate a few ConcurrentModificationExceptions + for (int c = 0; c < 10; c++) { + try { + HashSet result = new HashSet<>(); + if (selector != null) { + for (Object o : coll) { + if ((o instanceof Certificate) && + selector.match((Certificate) o)) + result.add((Certificate)o); + } + } else { + for (Object o : coll) { + if (o instanceof Certificate) + result.add((Certificate)o); + } + } + return(result); + } catch (ConcurrentModificationException e) { } + } + throw new ConcurrentModificationException("Too many " + + "ConcurrentModificationExceptions"); + } + + /** + * Returns a Collection of CRLs that + * match the specified selector. If no CRLs + * match the selector, an empty Collection will be returned. + * + * @param selector a CRLSelector used to select which + * CRLs should be returned. Specify null + * to return all CRLs. + * @return a Collection of CRLs that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException + { + if (coll == null) + throw new CertStoreException("Collection is null"); + + // Tolerate a few ConcurrentModificationExceptions + for (int c = 0; c < 10; c++) { + try { + HashSet result = new HashSet<>(); + if (selector != null) { + for (Object o : coll) { + if ((o instanceof CRL) && selector.match((CRL) o)) + result.add((CRL)o); + } + } else { + for (Object o : coll) { + if (o instanceof CRL) + result.add((CRL)o); + } + } + return result; + } catch (ConcurrentModificationException e) { } + } + throw new ConcurrentModificationException("Too many " + + "ConcurrentModificationExceptions"); + } +} diff --git a/src/sun/security/provider/certpath/ConstraintsChecker.java b/src/sun/security/provider/certpath/ConstraintsChecker.java new file mode 100644 index 00000000..a08c990b --- /dev/null +++ b/src/sun/security/provider/certpath/ConstraintsChecker.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import sun.security.util.Debug; +import static sun.security.x509.PKIXExtensions.*; +import sun.security.x509.NameConstraintsExtension; +import sun.security.x509.X509CertImpl; + +/** + * ConstraintsChecker is a PKIXCertPathChecker that checks + * constraints information on a PKIX certificate, namely basic constraints + * and name constraints. + * + * @since 1.4 + * @author Yassir Elley + */ +class ConstraintsChecker extends PKIXCertPathChecker { + + private static final Debug debug = Debug.getInstance("certpath"); + /* length of cert path */ + private final int certPathLength; + /* current maximum path length (as defined in PKIX) */ + private int maxPathLength; + /* current index of cert */ + private int i; + private NameConstraintsExtension prevNC; + + private Set supportedExts; + + /** + * Creates a ConstraintsChecker. + * + * @param certPathLength the length of the certification path + */ + ConstraintsChecker(int certPathLength) { + this.certPathLength = certPathLength; + } + + @Override + public void init(boolean forward) throws CertPathValidatorException { + if (!forward) { + i = 0; + maxPathLength = certPathLength; + prevNC = null; + } else { + throw new CertPathValidatorException + ("forward checking not supported"); + } + } + + @Override + public boolean isForwardCheckingSupported() { + return false; + } + + @Override + public Set getSupportedExtensions() { + if (supportedExts == null) { + supportedExts = new HashSet(2); + supportedExts.add(BasicConstraints_Id.toString()); + supportedExts.add(NameConstraints_Id.toString()); + supportedExts = Collections.unmodifiableSet(supportedExts); + } + return supportedExts; + } + + /** + * Performs the basic constraints and name constraints + * checks on the certificate using its internal state. + * + * @param cert the Certificate to be checked + * @param unresCritExts a Collection of OID strings + * representing the current set of unresolved critical extensions + * @throws CertPathValidatorException if the specified certificate + * does not pass the check + */ + @Override + public void check(Certificate cert, Collection unresCritExts) + throws CertPathValidatorException + { + X509Certificate currCert = (X509Certificate)cert; + + i++; + // MUST run NC check second, since it depends on BC check to + // update remainingCerts + checkBasicConstraints(currCert); + verifyNameConstraints(currCert); + + if (unresCritExts != null && !unresCritExts.isEmpty()) { + unresCritExts.remove(BasicConstraints_Id.toString()); + unresCritExts.remove(NameConstraints_Id.toString()); + } + } + + /** + * Internal method to check the name constraints against a cert + */ + private void verifyNameConstraints(X509Certificate currCert) + throws CertPathValidatorException + { + String msg = "name constraints"; + if (debug != null) { + debug.println("---checking " + msg + "..."); + } + + // check name constraints only if there is a previous name constraint + // and either the currCert is the final cert or the currCert is not + // self-issued + if (prevNC != null && ((i == certPathLength) || + !X509CertImpl.isSelfIssued(currCert))) { + if (debug != null) { + debug.println("prevNC = " + prevNC); + debug.println("currDN = " + currCert.getSubjectX500Principal()); + } + + try { + if (!prevNC.verify(currCert)) { + throw new CertPathValidatorException(msg + " check failed", + null, null, -1, PKIXReason.INVALID_NAME); + } + } catch (IOException ioe) { + throw new CertPathValidatorException(ioe); + } + } + + // merge name constraints regardless of whether cert is self-issued + prevNC = mergeNameConstraints(currCert, prevNC); + + if (debug != null) + debug.println(msg + " verified."); + } + + /** + * Helper to fold sets of name constraints together + */ + static NameConstraintsExtension mergeNameConstraints( + X509Certificate currCert, NameConstraintsExtension prevNC) + throws CertPathValidatorException + { + X509CertImpl currCertImpl; + try { + currCertImpl = X509CertImpl.toImpl(currCert); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + NameConstraintsExtension newConstraints = + currCertImpl.getNameConstraintsExtension(); + + if (debug != null) { + debug.println("prevNC = " + prevNC); + debug.println("newNC = " + String.valueOf(newConstraints)); + } + + // if there are no previous name constraints, we just return the + // new name constraints. + if (prevNC == null) { + if (debug != null) { + debug.println("mergedNC = " + String.valueOf(newConstraints)); + } + if (newConstraints == null) { + return newConstraints; + } else { + // Make sure we do a clone here, because we're probably + // going to modify this object later and we don't want to + // be sharing it with a Certificate object! + return (NameConstraintsExtension)newConstraints.clone(); + } + } else { + try { + // after merge, prevNC should contain the merged constraints + prevNC.merge(newConstraints); + } catch (IOException ioe) { + throw new CertPathValidatorException(ioe); + } + if (debug != null) { + debug.println("mergedNC = " + prevNC); + } + return prevNC; + } + } + + /** + * Internal method to check that a given cert meets basic constraints. + */ + private void checkBasicConstraints(X509Certificate currCert) + throws CertPathValidatorException + { + String msg = "basic constraints"; + if (debug != null) { + debug.println("---checking " + msg + "..."); + debug.println("i = " + i); + debug.println("maxPathLength = " + maxPathLength); + } + + /* check if intermediate cert */ + if (i < certPathLength) { + // RFC5280: If certificate i is a version 3 certificate, verify + // that the basicConstraints extension is present and that cA is + // set to TRUE. (If certificate i is a version 1 or version 2 + // certificate, then the application MUST either verify that + // certificate i is a CA certificate through out-of-band means + // or reject the certificate. Conforming implementations may + // choose to reject all version 1 and version 2 intermediate + // certificates.) + // + // We choose to reject all version 1 and version 2 intermediate + // certificates except that it is self issued by the trust + // anchor in order to support key rollover or changes in + // certificate policies. + int pathLenConstraint = -1; + if (currCert.getVersion() < 3) { // version 1 or version 2 + if (i == 1) { // issued by a trust anchor + if (X509CertImpl.isSelfIssued(currCert)) { + pathLenConstraint = Integer.MAX_VALUE; + } + } + } else { + pathLenConstraint = currCert.getBasicConstraints(); + } + + if (pathLenConstraint == -1) { + throw new CertPathValidatorException + (msg + " check failed: this is not a CA certificate", + null, null, -1, PKIXReason.NOT_CA_CERT); + } + + if (!X509CertImpl.isSelfIssued(currCert)) { + if (maxPathLength <= 0) { + throw new CertPathValidatorException + (msg + " check failed: pathLenConstraint violated - " + + "this cert must be the last cert in the " + + "certification path", null, null, -1, + PKIXReason.PATH_TOO_LONG); + } + maxPathLength--; + } + if (pathLenConstraint < maxPathLength) + maxPathLength = pathLenConstraint; + } + + if (debug != null) { + debug.println("after processing, maxPathLength = " + maxPathLength); + debug.println(msg + " verified."); + } + } + + /** + * Merges the specified maxPathLength with the pathLenConstraint + * obtained from the certificate. + * + * @param cert the X509Certificate + * @param maxPathLength the previous maximum path length + * @return the new maximum path length constraint (-1 means no more + * certificates can follow, Integer.MAX_VALUE means path length is + * unconstrained) + */ + static int mergeBasicConstraints(X509Certificate cert, int maxPathLength) { + + int pathLenConstraint = cert.getBasicConstraints(); + + if (!X509CertImpl.isSelfIssued(cert)) { + maxPathLength--; + } + + if (pathLenConstraint < maxPathLength) { + maxPathLength = pathLenConstraint; + } + + return maxPathLength; + } +} diff --git a/src/sun/security/provider/certpath/DistributionPointFetcher.java b/src/sun/security/provider/certpath/DistributionPointFetcher.java new file mode 100644 index 00000000..176682f6 --- /dev/null +++ b/src/sun/security/provider/certpath/DistributionPointFetcher.java @@ -0,0 +1,773 @@ +/* + * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.*; +import java.net.URI; +import java.security.*; +import java.security.cert.*; +import javax.security.auth.x500.X500Principal; +import java.util.*; + +import sun.security.util.Debug; +import sun.security.util.DerOutputStream; + +import static sun.security.x509.PKIXExtensions.IssuingDistributionPoint_Id; + +import sun.security.x509.*; + +/** + * Class to obtain CRLs via the CRLDistributionPoints extension. + * Note that the functionality of this class must be explicitly enabled + * via a system property, see the USE_CRLDP variable below. + * + * This class uses the URICertStore class to fetch CRLs. The URICertStore + * class also implements CRL caching: see the class description for more + * information. + * + * @author Andreas Sterbenz + * @author Sean Mullan + * @since 1.4.2 + */ +public class DistributionPointFetcher { + + private static final Debug debug = Debug.getInstance("certpath"); + + private static final boolean[] ALL_REASONS = + {true, true, true, true, true, true, true, true, true}; + + /** + * Private instantiation only. + */ + private DistributionPointFetcher() {} + + /** + * Return the X509CRLs matching this selector. The selector must be + * an X509CRLSelector with certificateChecking set. + */ + public static Collection getCRLs(X509CRLSelector selector, + boolean signFlag, + PublicKey prevKey, + String provider, + List certStores, + boolean[] reasonsMask, + Set trustAnchors, + Date validity) + throws CertStoreException + { + return getCRLs(selector, signFlag, prevKey, null, provider, certStores, + reasonsMask, trustAnchors, validity); + } + + /** + * Return the X509CRLs matching this selector. The selector must be + * an X509CRLSelector with certificateChecking set. + */ + public static Collection getCRLs(X509CRLSelector selector, + boolean signFlag, + PublicKey prevKey, + X509Certificate prevCert, + String provider, + List certStores, + boolean[] reasonsMask, + Set trustAnchors, + Date validity) + throws CertStoreException + { + X509Certificate cert = selector.getCertificateChecking(); + if (cert == null) { + return Collections.emptySet(); + } + try { + X509CertImpl certImpl = X509CertImpl.toImpl(cert); + if (debug != null) { + debug.println("DistributionPointFetcher.getCRLs: Checking " + + "CRLDPs for " + certImpl.getSubjectX500Principal()); + } + CRLDistributionPointsExtension ext = + certImpl.getCRLDistributionPointsExtension(); + if (ext == null) { + if (debug != null) { + debug.println("No CRLDP ext"); + } + return Collections.emptySet(); + } + List points = + ext.get(CRLDistributionPointsExtension.POINTS); + Set results = new HashSet<>(); + for (Iterator t = points.iterator(); + t.hasNext() && !Arrays.equals(reasonsMask, ALL_REASONS); ) { + DistributionPoint point = t.next(); + Collection crls = getCRLs(selector, certImpl, + point, reasonsMask, signFlag, prevKey, prevCert, provider, + certStores, trustAnchors, validity); + results.addAll(crls); + } + if (debug != null) { + debug.println("Returning " + results.size() + " CRLs"); + } + return results; + } catch (CertificateException | IOException e) { + return Collections.emptySet(); + } + } + + /** + * Download CRLs from the given distribution point, verify and return them. + * See the top of the class for current limitations. + * + * @throws CertStoreException if there is an error retrieving the CRLs + * from one of the GeneralNames and no other CRLs are retrieved from + * the other GeneralNames. If more than one GeneralName throws an + * exception then the one from the last GeneralName is thrown. + */ + private static Collection getCRLs(X509CRLSelector selector, + X509CertImpl certImpl, DistributionPoint point, boolean[] reasonsMask, + boolean signFlag, PublicKey prevKey, X509Certificate prevCert, + String provider, List certStores, + Set trustAnchors, Date validity) + throws CertStoreException { + + // check for full name + GeneralNames fullName = point.getFullName(); + if (fullName == null) { + // check for relative name + RDN relativeName = point.getRelativeName(); + if (relativeName == null) { + return Collections.emptySet(); + } + try { + GeneralNames crlIssuers = point.getCRLIssuer(); + if (crlIssuers == null) { + fullName = getFullNames + ((X500Name) certImpl.getIssuerDN(), relativeName); + } else { + // should only be one CRL Issuer + if (crlIssuers.size() != 1) { + return Collections.emptySet(); + } else { + fullName = getFullNames + ((X500Name) crlIssuers.get(0).getName(), relativeName); + } + } + } catch (IOException ioe) { + return Collections.emptySet(); + } + } + Collection possibleCRLs = new ArrayList<>(); + CertStoreException savedCSE = null; + for (Iterator t = fullName.iterator(); t.hasNext(); ) { + try { + GeneralName name = t.next(); + if (name.getType() == GeneralNameInterface.NAME_DIRECTORY) { + X500Name x500Name = (X500Name) name.getName(); + possibleCRLs.addAll( + getCRLs(x500Name, certImpl.getIssuerX500Principal(), + certStores)); + } else if (name.getType() == GeneralNameInterface.NAME_URI) { + URIName uriName = (URIName)name.getName(); + X509CRL crl = getCRL(uriName); + if (crl != null) { + possibleCRLs.add(crl); + } + } + } catch (CertStoreException cse) { + savedCSE = cse; + } + } + // only throw CertStoreException if no CRLs are retrieved + if (possibleCRLs.isEmpty() && savedCSE != null) { + throw savedCSE; + } + + Collection crls = new ArrayList<>(2); + for (X509CRL crl : possibleCRLs) { + try { + // make sure issuer is not set + // we check the issuer in verifyCRLs method + selector.setIssuerNames(null); + if (selector.match(crl) && verifyCRL(certImpl, point, crl, + reasonsMask, signFlag, prevKey, prevCert, provider, + trustAnchors, certStores, validity)) { + crls.add(crl); + } + } catch (IOException | CRLException e) { + // don't add the CRL + if (debug != null) { + debug.println("Exception verifying CRL: " + e.getMessage()); + e.printStackTrace(); + } + } + } + return crls; + } + + /** + * Download CRL from given URI. + */ + private static X509CRL getCRL(URIName name) throws CertStoreException { + URI uri = name.getURI(); + if (debug != null) { + debug.println("Trying to fetch CRL from DP " + uri); + } + CertStore ucs = null; + try { + ucs = URICertStore.getInstance + (new URICertStore.URICertStoreParameters(uri)); + } catch (InvalidAlgorithmParameterException | + NoSuchAlgorithmException e) { + if (debug != null) { + debug.println("Can't create URICertStore: " + e.getMessage()); + } + return null; + } + + Collection crls = ucs.getCRLs(null); + if (crls.isEmpty()) { + return null; + } else { + return (X509CRL) crls.iterator().next(); + } + } + + /** + * Fetch CRLs from certStores. + * + * @throws CertStoreException if there is an error retrieving the CRLs from + * one of the CertStores and no other CRLs are retrieved from + * the other CertStores. If more than one CertStore throws an + * exception then the one from the last CertStore is thrown. + */ + private static Collection getCRLs(X500Name name, + X500Principal certIssuer, + List certStores) + throws CertStoreException + { + if (debug != null) { + debug.println("Trying to fetch CRL from DP " + name); + } + X509CRLSelector xcs = new X509CRLSelector(); + xcs.addIssuer(name.asX500Principal()); + xcs.addIssuer(certIssuer); + Collection crls = new ArrayList<>(); + CertStoreException savedCSE = null; + for (CertStore store : certStores) { + try { + for (CRL crl : store.getCRLs(xcs)) { + crls.add((X509CRL)crl); + } + } catch (CertStoreException cse) { + if (debug != null) { + debug.println("Exception while retrieving " + + "CRLs: " + cse); + cse.printStackTrace(); + } + savedCSE = new PKIX.CertStoreTypeException(store.getType(),cse); + } + } + // only throw CertStoreException if no CRLs are retrieved + if (crls.isEmpty() && savedCSE != null) { + throw savedCSE; + } else { + return crls; + } + } + + /** + * Verifies a CRL for the given certificate's Distribution Point to + * ensure it is appropriate for checking the revocation status. + * + * @param certImpl the certificate whose revocation status is being checked + * @param point one of the distribution points of the certificate + * @param crl the CRL + * @param reasonsMask the interim reasons mask + * @param signFlag true if prevKey can be used to verify the CRL + * @param prevKey the public key that verifies the certificate's signature + * @param prevCert the certificate whose public key verifies + * {@code certImpl}'s signature + * @param provider the Signature provider to use + * @param trustAnchors a {@code Set} of {@code TrustAnchor}s + * @param certStores a {@code List} of {@code CertStore}s to be used in + * finding certificates and CRLs + * @param validity the time for which the validity of the CRL issuer's + * certification path should be determined + * @return true if ok, false if not + */ + static boolean verifyCRL(X509CertImpl certImpl, DistributionPoint point, + X509CRL crl, boolean[] reasonsMask, boolean signFlag, + PublicKey prevKey, X509Certificate prevCert, String provider, + Set trustAnchors, List certStores, + Date validity) throws CRLException, IOException { + + boolean indirectCRL = false; + X509CRLImpl crlImpl = X509CRLImpl.toImpl(crl); + IssuingDistributionPointExtension idpExt = + crlImpl.getIssuingDistributionPointExtension(); + X500Name certIssuer = (X500Name) certImpl.getIssuerDN(); + X500Name crlIssuer = (X500Name) crlImpl.getIssuerDN(); + + // if crlIssuer is set, verify that it matches the issuer of the + // CRL and the CRL contains an IDP extension with the indirectCRL + // boolean asserted. Otherwise, verify that the CRL issuer matches the + // certificate issuer. + GeneralNames pointCrlIssuers = point.getCRLIssuer(); + X500Name pointCrlIssuer = null; + if (pointCrlIssuers != null) { + if (idpExt == null || + ((Boolean) idpExt.get + (IssuingDistributionPointExtension.INDIRECT_CRL)).equals + (Boolean.FALSE)) { + return false; + } + boolean match = false; + for (Iterator t = pointCrlIssuers.iterator(); + !match && t.hasNext(); ) { + GeneralNameInterface name = t.next().getName(); + if (crlIssuer.equals(name) == true) { + pointCrlIssuer = (X500Name) name; + match = true; + } + } + if (match == false) { + return false; + } + + // we accept the case that a CRL issuer provide status + // information for itself. + if (issues(certImpl, crlImpl, provider)) { + // reset the public key used to verify the CRL's signature + prevKey = certImpl.getPublicKey(); + } else { + indirectCRL = true; + } + } else if (crlIssuer.equals(certIssuer) == false) { + if (debug != null) { + debug.println("crl issuer does not equal cert issuer"); + } + return false; + } else { + // in case of self-issued indirect CRL issuer. + KeyIdentifier certAKID = certImpl.getAuthKeyId(); + KeyIdentifier crlAKID = crlImpl.getAuthKeyId(); + + if (certAKID == null || crlAKID == null) { + // cannot recognize indirect CRL without AKID + + // we accept the case that a CRL issuer provide status + // information for itself. + if (issues(certImpl, crlImpl, provider)) { + // reset the public key used to verify the CRL's signature + prevKey = certImpl.getPublicKey(); + } + } else if (!certAKID.equals(crlAKID)) { + // we accept the case that a CRL issuer provide status + // information for itself. + if (issues(certImpl, crlImpl, provider)) { + // reset the public key used to verify the CRL's signature + prevKey = certImpl.getPublicKey(); + } else { + indirectCRL = true; + } + } + } + + if (!indirectCRL && !signFlag) { + // cert's key cannot be used to verify the CRL + return false; + } + + if (idpExt != null) { + DistributionPointName idpPoint = (DistributionPointName) + idpExt.get(IssuingDistributionPointExtension.POINT); + if (idpPoint != null) { + GeneralNames idpNames = idpPoint.getFullName(); + if (idpNames == null) { + RDN relativeName = idpPoint.getRelativeName(); + if (relativeName == null) { + if (debug != null) { + debug.println("IDP must be relative or full DN"); + } + return false; + } + if (debug != null) { + debug.println("IDP relativeName:" + relativeName); + } + idpNames = getFullNames(crlIssuer, relativeName); + } + // if the DP name is present in the IDP CRL extension and the + // DP field is present in the DP, then verify that one of the + // names in the IDP matches one of the names in the DP + if (point.getFullName() != null || + point.getRelativeName() != null) { + GeneralNames pointNames = point.getFullName(); + if (pointNames == null) { + RDN relativeName = point.getRelativeName(); + if (relativeName == null) { + if (debug != null) { + debug.println("DP must be relative or full DN"); + } + return false; + } + if (debug != null) { + debug.println("DP relativeName:" + relativeName); + } + if (indirectCRL) { + if (pointCrlIssuers.size() != 1) { + // RFC 3280: there must be only 1 CRL issuer + // name when relativeName is present + if (debug != null) { + debug.println("must only be one CRL " + + "issuer when relative name present"); + } + return false; + } + pointNames = getFullNames + (pointCrlIssuer, relativeName); + } else { + pointNames = getFullNames(certIssuer, relativeName); + } + } + boolean match = false; + for (Iterator i = idpNames.iterator(); + !match && i.hasNext(); ) { + GeneralNameInterface idpName = i.next().getName(); + if (debug != null) { + debug.println("idpName: " + idpName); + } + for (Iterator p = pointNames.iterator(); + !match && p.hasNext(); ) { + GeneralNameInterface pointName = p.next().getName(); + if (debug != null) { + debug.println("pointName: " + pointName); + } + match = idpName.equals(pointName); + } + } + if (!match) { + if (debug != null) { + debug.println("IDP name does not match DP name"); + } + return false; + } + // if the DP name is present in the IDP CRL extension and the + // DP field is absent from the DP, then verify that one of the + // names in the IDP matches one of the names in the crlIssuer + // field of the DP + } else { + // verify that one of the names in the IDP matches one of + // the names in the cRLIssuer of the cert's DP + boolean match = false; + for (Iterator t = pointCrlIssuers.iterator(); + !match && t.hasNext(); ) { + GeneralNameInterface crlIssuerName = t.next().getName(); + for (Iterator i = idpNames.iterator(); + !match && i.hasNext(); ) { + GeneralNameInterface idpName = i.next().getName(); + match = crlIssuerName.equals(idpName); + } + } + if (!match) { + return false; + } + } + } + + // if the onlyContainsUserCerts boolean is asserted, verify that the + // cert is not a CA cert + Boolean b = (Boolean) + idpExt.get(IssuingDistributionPointExtension.ONLY_USER_CERTS); + if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() != -1) { + if (debug != null) { + debug.println("cert must be a EE cert"); + } + return false; + } + + // if the onlyContainsCACerts boolean is asserted, verify that the + // cert is a CA cert + b = (Boolean) + idpExt.get(IssuingDistributionPointExtension.ONLY_CA_CERTS); + if (b.equals(Boolean.TRUE) && certImpl.getBasicConstraints() == -1) { + if (debug != null) { + debug.println("cert must be a CA cert"); + } + return false; + } + + // verify that the onlyContainsAttributeCerts boolean is not + // asserted + b = (Boolean) idpExt.get + (IssuingDistributionPointExtension.ONLY_ATTRIBUTE_CERTS); + if (b.equals(Boolean.TRUE)) { + if (debug != null) { + debug.println("cert must not be an AA cert"); + } + return false; + } + } + + // compute interim reasons mask + boolean[] interimReasonsMask = new boolean[9]; + ReasonFlags reasons = null; + if (idpExt != null) { + reasons = (ReasonFlags) + idpExt.get(IssuingDistributionPointExtension.REASONS); + } + + boolean[] pointReasonFlags = point.getReasonFlags(); + if (reasons != null) { + if (pointReasonFlags != null) { + // set interim reasons mask to the intersection of + // reasons in the DP and onlySomeReasons in the IDP + boolean[] idpReasonFlags = reasons.getFlags(); + for (int i = 0; i < idpReasonFlags.length; i++) { + if (idpReasonFlags[i] && pointReasonFlags[i]) { + interimReasonsMask[i] = true; + } + } + } else { + // set interim reasons mask to the value of + // onlySomeReasons in the IDP (and clone it since we may + // modify it) + interimReasonsMask = reasons.getFlags().clone(); + } + } else if (idpExt == null || reasons == null) { + if (pointReasonFlags != null) { + // set interim reasons mask to the value of DP reasons + interimReasonsMask = pointReasonFlags.clone(); + } else { + // set interim reasons mask to the special value all-reasons + interimReasonsMask = new boolean[9]; + Arrays.fill(interimReasonsMask, true); + } + } + + // verify that interim reasons mask includes one or more reasons + // not included in the reasons mask + boolean oneOrMore = false; + for (int i = 0; i < interimReasonsMask.length && !oneOrMore; i++) { + if (!reasonsMask[i] && interimReasonsMask[i]) { + oneOrMore = true; + } + } + if (!oneOrMore) { + return false; + } + + // Obtain and validate the certification path for the complete + // CRL issuer (if indirect CRL). If a key usage extension is present + // in the CRL issuer's certificate, verify that the cRLSign bit is set. + if (indirectCRL) { + X509CertSelector certSel = new X509CertSelector(); + certSel.setSubject(crlIssuer.asX500Principal()); + boolean[] crlSign = {false,false,false,false,false,false,true}; + certSel.setKeyUsage(crlSign); + + // Currently by default, forward builder does not enable + // subject/authority key identifier identifying for target + // certificate, instead, it only compares the CRL issuer and + // the target certificate subject. If the certificate of the + // delegated CRL issuer is a self-issued certificate, the + // builder is unable to find the proper CRL issuer by issuer + // name only, there is a potential dead loop on finding the + // proper issuer. It is of great help to narrow the target + // scope down to aware of authority key identifiers in the + // selector, for the purposes of breaking the dead loop. + AuthorityKeyIdentifierExtension akidext = + crlImpl.getAuthKeyIdExtension(); + if (akidext != null) { + KeyIdentifier akid = (KeyIdentifier)akidext.get( + AuthorityKeyIdentifierExtension.KEY_ID); + if (akid != null) { + DerOutputStream derout = new DerOutputStream(); + derout.putOctetString(akid.getIdentifier()); + certSel.setSubjectKeyIdentifier(derout.toByteArray()); + } + + SerialNumber asn = (SerialNumber)akidext.get( + AuthorityKeyIdentifierExtension.SERIAL_NUMBER); + if (asn != null) { + certSel.setSerialNumber(asn.getNumber()); + } + // the subject criterion will be set by builder automatically. + } + + // By now, we have validated the previous certificate, so we can + // trust it during the validation of the CRL issuer. + // In addition to the performance improvement, another benefit is to + // break the dead loop while looking for the issuer back and forth + // between the delegated self-issued certificate and its issuer. + Set newTrustAnchors = new HashSet<>(trustAnchors); + + if (prevKey != null) { + // Add the previous certificate as a trust anchor. + // If prevCert is not null, we want to construct a TrustAnchor + // using the cert object because when the certpath for the CRL + // is built later, the CertSelector will make comparisons with + // the TrustAnchor's trustedCert member rather than its pubKey. + TrustAnchor temporary; + if (prevCert != null) { + temporary = new TrustAnchor(prevCert, null); + } else { + X500Principal principal = certImpl.getIssuerX500Principal(); + temporary = new TrustAnchor(principal, prevKey, null); + } + newTrustAnchors.add(temporary); + } + + PKIXBuilderParameters params = null; + try { + params = new PKIXBuilderParameters(newTrustAnchors, certSel); + } catch (InvalidAlgorithmParameterException iape) { + throw new CRLException(iape); + } + params.setCertStores(certStores); + params.setSigProvider(provider); + params.setDate(validity); + try { + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); + PKIXCertPathBuilderResult result = + (PKIXCertPathBuilderResult) builder.build(params); + prevKey = result.getPublicKey(); + } catch (GeneralSecurityException e) { + throw new CRLException(e); + } + } + + // check the crl signature algorithm + try { + AlgorithmChecker.check(prevKey, crl); + } catch (CertPathValidatorException cpve) { + if (debug != null) { + debug.println("CRL signature algorithm check failed: " + cpve); + } + return false; + } + + // validate the signature on the CRL + try { + crl.verify(prevKey, provider); + } catch (GeneralSecurityException e) { + if (debug != null) { + debug.println("CRL signature failed to verify"); + } + return false; + } + + // reject CRL if any unresolved critical extensions remain in the CRL. + Set unresCritExts = crl.getCriticalExtensionOIDs(); + // remove any that we have processed + if (unresCritExts != null) { + unresCritExts.remove(IssuingDistributionPoint_Id.toString()); + if (!unresCritExts.isEmpty()) { + if (debug != null) { + debug.println("Unrecognized critical extension(s) in CRL: " + + unresCritExts); + for (String ext : unresCritExts) { + debug.println(ext); + } + } + return false; + } + } + + // update reasonsMask + for (int i = 0; i < interimReasonsMask.length; i++) { + if (!reasonsMask[i] && interimReasonsMask[i]) { + reasonsMask[i] = true; + } + } + return true; + } + + /** + * Append relative name to the issuer name and return a new + * GeneralNames object. + */ + private static GeneralNames getFullNames(X500Name issuer, RDN rdn) + throws IOException + { + List rdns = new ArrayList<>(issuer.rdns()); + rdns.add(rdn); + X500Name fullName = new X500Name(rdns.toArray(new RDN[0])); + GeneralNames fullNames = new GeneralNames(); + fullNames.add(new GeneralName(fullName)); + return fullNames; + } + + /** + * Verifies whether a CRL is issued by a certain certificate + * + * @param cert the certificate + * @param crl the CRL to be verified + * @param provider the name of the signature provider + */ + private static boolean issues(X509CertImpl cert, X509CRLImpl crl, + String provider) throws IOException + { + boolean matched = false; + + AdaptableX509CertSelector issuerSelector = + new AdaptableX509CertSelector(); + + // check certificate's key usage + boolean[] usages = cert.getKeyUsage(); + if (usages != null) { + usages[6] = true; // cRLSign + issuerSelector.setKeyUsage(usages); + } + + // check certificate's subject + X500Principal crlIssuer = crl.getIssuerX500Principal(); + issuerSelector.setSubject(crlIssuer); + + /* + * Facilitate certification path construction with authority + * key identifier and subject key identifier. + * + * In practice, conforming CAs MUST use the key identifier method, + * and MUST include authority key identifier extension in all CRLs + * issued. [section 5.2.1, RFC 2459] + */ + AuthorityKeyIdentifierExtension crlAKID = crl.getAuthKeyIdExtension(); + issuerSelector.setSkiAndSerialNumber(crlAKID); + + matched = issuerSelector.match(cert); + + // if AKID is unreliable, verify the CRL signature with the cert + if (matched && (crlAKID == null || + cert.getAuthorityKeyIdentifierExtension() == null)) { + try { + crl.verify(cert.getPublicKey(), provider); + matched = true; + } catch (GeneralSecurityException e) { + matched = false; + } + } + + return matched; + } +} diff --git a/src/sun/security/provider/certpath/ForwardBuilder.java b/src/sun/security/provider/certpath/ForwardBuilder.java new file mode 100644 index 00000000..5e3799e1 --- /dev/null +++ b/src/sun/security/provider/certpath/ForwardBuilder.java @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXReason; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.security.cert.X509CertSelector; +import java.util.*; +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.PKIX.BuilderParams; +import sun.security.util.Debug; +import sun.security.x509.AccessDescription; +import sun.security.x509.AuthorityInfoAccessExtension; + +import static sun.security.x509.PKIXExtensions.BasicConstraints_Id; +import static sun.security.x509.PKIXExtensions.CertificatePolicies_Id; +import static sun.security.x509.PKIXExtensions.ExtendedKeyUsage_Id; +import static sun.security.x509.PKIXExtensions.InhibitAnyPolicy_Id; +import static sun.security.x509.PKIXExtensions.KeyUsage_Id; +import static sun.security.x509.PKIXExtensions.NameConstraints_Id; +import static sun.security.x509.PKIXExtensions.PolicyConstraints_Id; +import static sun.security.x509.PKIXExtensions.PolicyMappings_Id; +import static sun.security.x509.PKIXExtensions.SubjectAlternativeName_Id; + +import sun.security.x509.X500Name; +import sun.security.x509.AuthorityKeyIdentifierExtension; + +/** + * This class represents a forward builder, which is able to retrieve + * matching certificates from CertStores and verify a particular certificate + * against a ForwardState. + * + * @since 1.4 + * @author Yassir Elley + * @author Sean Mullan + */ +class ForwardBuilder extends Builder { + + private static final Debug debug = Debug.getInstance("certpath"); + private final Set trustedCerts; + private final Set trustedSubjectDNs; + private final Set trustAnchors; + private X509CertSelector eeSelector; + private AdaptableX509CertSelector caSelector; + private X509CertSelector caTargetSelector; + TrustAnchor trustAnchor; + private Comparator comparator; + private boolean searchAllCertStores = true; + + /** + * Initialize the builder with the input parameters. + * + * @param params the parameter set used to build a certification path + */ + ForwardBuilder(BuilderParams buildParams, boolean searchAllCertStores) { + super(buildParams); + + // populate sets of trusted certificates and subject DNs + trustAnchors = buildParams.trustAnchors(); + trustedCerts = new HashSet(trustAnchors.size()); + trustedSubjectDNs = new HashSet(trustAnchors.size()); + for (TrustAnchor anchor : trustAnchors) { + X509Certificate trustedCert = anchor.getTrustedCert(); + if (trustedCert != null) { + trustedCerts.add(trustedCert); + trustedSubjectDNs.add(trustedCert.getSubjectX500Principal()); + } else { + trustedSubjectDNs.add(anchor.getCA()); + } + } + comparator = new PKIXCertComparator(trustedSubjectDNs); + this.searchAllCertStores = searchAllCertStores; + } + + /** + * Retrieves all certs from the specified CertStores that satisfy the + * requirements specified in the parameters and the current + * PKIX state (name constraints, policy constraints, etc). + * + * @param currentState the current state. + * Must be an instance of ForwardState + * @param certStores list of CertStores + */ + @Override + Collection getMatchingCerts(State currentState, + List certStores) + throws CertStoreException, CertificateException, IOException + { + if (debug != null) { + debug.println("ForwardBuilder.getMatchingCerts()..."); + } + + ForwardState currState = (ForwardState) currentState; + + /* + * We store certs in a Set because we don't want duplicates. + * As each cert is added, it is sorted based on the PKIXCertComparator + * algorithm. + */ + Set certs = new TreeSet<>(comparator); + + /* + * Only look for EE certs if search has just started. + */ + if (currState.isInitial()) { + getMatchingEECerts(currState, certStores, certs); + } + getMatchingCACerts(currState, certStores, certs); + + return certs; + } + + /* + * Retrieves all end-entity certificates which satisfy constraints + * and requirements specified in the parameters and PKIX state. + */ + private void getMatchingEECerts(ForwardState currentState, + List certStores, + Collection eeCerts) + throws IOException + { + if (debug != null) { + debug.println("ForwardBuilder.getMatchingEECerts()..."); + } + /* + * Compose a certificate matching rule to filter out + * certs which don't satisfy constraints + * + * First, retrieve clone of current target cert constraints, + * and then add more selection criteria based on current validation + * state. Since selector never changes, cache local copy & reuse. + */ + if (eeSelector == null) { + eeSelector = (X509CertSelector) targetCertConstraints.clone(); + + /* + * Match on certificate validity date + */ + eeSelector.setCertificateValid(buildParams.date()); + + /* + * Policy processing optimizations + */ + if (buildParams.explicitPolicyRequired()) { + eeSelector.setPolicy(getMatchingPolicies()); + } + /* + * Require EE certs + */ + eeSelector.setBasicConstraints(-2); + } + + /* Retrieve matching EE certs from CertStores */ + addMatchingCerts(eeSelector, certStores, eeCerts, searchAllCertStores); + } + + /** + * Retrieves all CA certificates which satisfy constraints + * and requirements specified in the parameters and PKIX state. + */ + private void getMatchingCACerts(ForwardState currentState, + List certStores, + Collection caCerts) + throws IOException + { + if (debug != null) { + debug.println("ForwardBuilder.getMatchingCACerts()..."); + } + int initialSize = caCerts.size(); + + /* + * Compose a CertSelector to filter out + * certs which do not satisfy requirements. + */ + X509CertSelector sel = null; + + if (currentState.isInitial()) { + if (targetCertConstraints.getBasicConstraints() == -2) { + // no need to continue: this means we never can match a CA cert + return; + } + + /* This means a CA is the target, so match on same stuff as + * getMatchingEECerts + */ + if (debug != null) { + debug.println("ForwardBuilder.getMatchingCACerts(): ca is target"); + } + + if (caTargetSelector == null) { + caTargetSelector = + (X509CertSelector) targetCertConstraints.clone(); + + /* + * Since we don't check the validity period of trusted + * certificates, please don't set the certificate valid + * criterion unless the trusted certificate matching is + * completed. + */ + + /* + * Policy processing optimizations + */ + if (buildParams.explicitPolicyRequired()) + caTargetSelector.setPolicy(getMatchingPolicies()); + } + + sel = caTargetSelector; + } else { + + if (caSelector == null) { + caSelector = new AdaptableX509CertSelector(); + + /* + * Since we don't check the validity period of trusted + * certificates, please don't set the certificate valid + * criterion unless the trusted certificate matching is + * completed. + */ + + /* + * Policy processing optimizations + */ + if (buildParams.explicitPolicyRequired()) + caSelector.setPolicy(getMatchingPolicies()); + } + + /* + * Match on subject (issuer of previous cert) + */ + caSelector.setSubject(currentState.issuerDN); + + /* + * Match on subjectNamesTraversed (both DNs and AltNames) + * (checks that current cert's name constraints permit it + * to certify all the DNs and AltNames that have been traversed) + */ + CertPathHelper.setPathToNames + (caSelector, currentState.subjectNamesTraversed); + + /* + * Facilitate certification path construction with authority + * key identifier and subject key identifier. + */ + AuthorityKeyIdentifierExtension akidext = + currentState.cert.getAuthorityKeyIdentifierExtension(); + caSelector.setSkiAndSerialNumber(akidext); + + /* + * check the validity period + */ + caSelector.setValidityPeriod(currentState.cert.getNotBefore(), + currentState.cert.getNotAfter()); + + sel = caSelector; + } + + /* + * For compatibility, conservatively, we don't check the path + * length constraint of trusted anchors. Please don't set the + * basic constraints criterion unless the trusted certificate + * matching is completed. + */ + sel.setBasicConstraints(-1); + + for (X509Certificate trustedCert : trustedCerts) { + if (sel.match(trustedCert)) { + if (debug != null) { + debug.println("ForwardBuilder.getMatchingCACerts: " + + "found matching trust anchor"); + } + if (caCerts.add(trustedCert) && !searchAllCertStores) { + return; + } + } + } + + /* + * The trusted certificate matching is completed. We need to match + * on certificate validity date. + */ + sel.setCertificateValid(buildParams.date()); + + /* + * Require CA certs with a pathLenConstraint that allows + * at least as many CA certs that have already been traversed + */ + sel.setBasicConstraints(currentState.traversedCACerts); + + /* + * If we have already traversed as many CA certs as the maxPathLength + * will allow us to, then we don't bother looking through these + * certificate pairs. If maxPathLength has a value of -1, this + * means it is unconstrained, so we always look through the + * certificate pairs. + */ + if (currentState.isInitial() || + (buildParams.maxPathLength() == -1) || + (buildParams.maxPathLength() > currentState.traversedCACerts)) + { + if (addMatchingCerts(sel, certStores, + caCerts, searchAllCertStores) + && !searchAllCertStores) { + return; + } + } + + if (!currentState.isInitial() && Builder.USE_AIA) { + // check for AuthorityInformationAccess extension + AuthorityInfoAccessExtension aiaExt = + currentState.cert.getAuthorityInfoAccessExtension(); + if (aiaExt != null) { + getCerts(aiaExt, caCerts); + } + } + + if (debug != null) { + int numCerts = caCerts.size() - initialSize; + debug.println("ForwardBuilder.getMatchingCACerts: found " + + numCerts + " CA certs"); + } + } + + /** + * Download Certificates from the given AIA and add them to the + * specified Collection. + */ + // cs.getCertificates(caSelector) returns a collection of X509Certificate's + // because of the selector, so the cast is safe + @SuppressWarnings("unchecked") + private boolean getCerts(AuthorityInfoAccessExtension aiaExt, + Collection certs) + { + if (Builder.USE_AIA == false) { + return false; + } + List adList = aiaExt.getAccessDescriptions(); + if (adList == null || adList.isEmpty()) { + return false; + } + + boolean add = false; + for (AccessDescription ad : adList) { + CertStore cs = URICertStore.getInstance(ad); + if (cs != null) { + try { + if (certs.addAll((Collection) + cs.getCertificates(caSelector))) { + add = true; + if (!searchAllCertStores) { + return true; + } + } + } catch (CertStoreException cse) { + if (debug != null) { + debug.println("exception getting certs from CertStore:"); + cse.printStackTrace(); + } + } + } + } + return add; + } + + /** + * This inner class compares 2 PKIX certificates according to which + * should be tried first when building a path from the target. + * The preference order is as follows: + * + * Given trusted certificate(s): + * Subject:ou=D,ou=C,o=B,c=A + * + * Preference order for current cert: + * + * 1) Issuer matches a trusted subject + * Issuer: ou=D,ou=C,o=B,c=A + * + * 2) Issuer is a descendant of a trusted subject (in order of + * number of links to the trusted subject) + * a) Issuer: ou=E,ou=D,ou=C,o=B,c=A [links=1] + * b) Issuer: ou=F,ou=E,ou=D,ou=C,ou=B,c=A [links=2] + * + * 3) Issuer is an ancestor of a trusted subject (in order of number of + * links to the trusted subject) + * a) Issuer: ou=C,o=B,c=A [links=1] + * b) Issuer: o=B,c=A [links=2] + * + * 4) Issuer is in the same namespace as a trusted subject (in order of + * number of links to the trusted subject) + * a) Issuer: ou=G,ou=C,o=B,c=A [links=2] + * b) Issuer: ou=H,o=B,c=A [links=3] + * + * 5) Issuer is an ancestor of certificate subject (in order of number + * of links to the certificate subject) + * a) Issuer: ou=K,o=J,c=A + * Subject: ou=L,ou=K,o=J,c=A + * b) Issuer: o=J,c=A + * Subject: ou=L,ou=K,0=J,c=A + * + * 6) Any other certificates + */ + static class PKIXCertComparator implements Comparator { + + final static String METHOD_NME = "PKIXCertComparator.compare()"; + + private final Set trustedSubjectDNs; + + PKIXCertComparator(Set trustedSubjectDNs) { + this.trustedSubjectDNs = trustedSubjectDNs; + } + + /** + * @param oCert1 First X509Certificate to be compared + * @param oCert2 Second X509Certificate to be compared + * @return -1 if oCert1 is preferable to oCert2, or + * if oCert1 and oCert2 are equally preferable (in this + * case it doesn't matter which is preferable, but we don't + * return 0 because the comparator would behave strangely + * when used in a SortedSet). + * 1 if oCert2 is preferable to oCert1 + * 0 if oCert1.equals(oCert2). We only return 0 if the + * certs are equal so that this comparator behaves + * correctly when used in a SortedSet. + * @throws ClassCastException if either argument is not of type + * X509Certificate + */ + @Override + public int compare(X509Certificate oCert1, X509Certificate oCert2) { + + // if certs are the same, return 0 + if (oCert1.equals(oCert2)) return 0; + + X500Principal cIssuer1 = oCert1.getIssuerX500Principal(); + X500Principal cIssuer2 = oCert2.getIssuerX500Principal(); + X500Name cIssuer1Name = X500Name.asX500Name(cIssuer1); + X500Name cIssuer2Name = X500Name.asX500Name(cIssuer2); + + if (debug != null) { + debug.println(METHOD_NME + " o1 Issuer: " + cIssuer1); + debug.println(METHOD_NME + " o2 Issuer: " + cIssuer2); + } + + /* If one cert's issuer matches a trusted subject, then it is + * preferable. + */ + if (debug != null) { + debug.println(METHOD_NME + " MATCH TRUSTED SUBJECT TEST..."); + } + + boolean m1 = trustedSubjectDNs.contains(cIssuer1); + boolean m2 = trustedSubjectDNs.contains(cIssuer2); + if (debug != null) { + debug.println(METHOD_NME + " m1: " + m1); + debug.println(METHOD_NME + " m2: " + m2); + } + if (m1 && m2) { + return -1; + } else if (m1) { + return -1; + } else if (m2) { + return 1; + } + + /* If one cert's issuer is a naming descendant of a trusted subject, + * then it is preferable, in order of increasing naming distance. + */ + if (debug != null) { + debug.println(METHOD_NME + " NAMING DESCENDANT TEST..."); + } + for (X500Principal tSubject : trustedSubjectDNs) { + X500Name tSubjectName = X500Name.asX500Name(tSubject); + int distanceTto1 = + Builder.distance(tSubjectName, cIssuer1Name, -1); + int distanceTto2 = + Builder.distance(tSubjectName, cIssuer2Name, -1); + if (debug != null) { + debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); + debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); + } + if (distanceTto1 > 0 || distanceTto2 > 0) { + if (distanceTto1 == distanceTto2) { + return -1; + } else if (distanceTto1 > 0 && distanceTto2 <= 0) { + return -1; + } else if (distanceTto1 <= 0 && distanceTto2 > 0) { + return 1; + } else if (distanceTto1 < distanceTto2) { + return -1; + } else { // distanceTto1 > distanceTto2 + return 1; + } + } + } + + /* If one cert's issuer is a naming ancestor of a trusted subject, + * then it is preferable, in order of increasing naming distance. + */ + if (debug != null) { + debug.println(METHOD_NME + " NAMING ANCESTOR TEST..."); + } + for (X500Principal tSubject : trustedSubjectDNs) { + X500Name tSubjectName = X500Name.asX500Name(tSubject); + + int distanceTto1 = Builder.distance + (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); + int distanceTto2 = Builder.distance + (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); + if (debug != null) { + debug.println(METHOD_NME +" distanceTto1: " + distanceTto1); + debug.println(METHOD_NME +" distanceTto2: " + distanceTto2); + } + if (distanceTto1 < 0 || distanceTto2 < 0) { + if (distanceTto1 == distanceTto2) { + return -1; + } else if (distanceTto1 < 0 && distanceTto2 >= 0) { + return -1; + } else if (distanceTto1 >= 0 && distanceTto2 < 0) { + return 1; + } else if (distanceTto1 > distanceTto2) { + return -1; + } else { + return 1; + } + } + } + + /* If one cert's issuer is in the same namespace as a trusted + * subject, then it is preferable, in order of increasing naming + * distance. + */ + if (debug != null) { + debug.println(METHOD_NME +" SAME NAMESPACE AS TRUSTED TEST..."); + } + for (X500Principal tSubject : trustedSubjectDNs) { + X500Name tSubjectName = X500Name.asX500Name(tSubject); + X500Name tAo1 = tSubjectName.commonAncestor(cIssuer1Name); + X500Name tAo2 = tSubjectName.commonAncestor(cIssuer2Name); + if (debug != null) { + debug.println(METHOD_NME +" tAo1: " + String.valueOf(tAo1)); + debug.println(METHOD_NME +" tAo2: " + String.valueOf(tAo2)); + } + if (tAo1 != null || tAo2 != null) { + if (tAo1 != null && tAo2 != null) { + int hopsTto1 = Builder.hops + (tSubjectName, cIssuer1Name, Integer.MAX_VALUE); + int hopsTto2 = Builder.hops + (tSubjectName, cIssuer2Name, Integer.MAX_VALUE); + if (debug != null) { + debug.println(METHOD_NME +" hopsTto1: " + hopsTto1); + debug.println(METHOD_NME +" hopsTto2: " + hopsTto2); + } + if (hopsTto1 == hopsTto2) { + } else if (hopsTto1 > hopsTto2) { + return 1; + } else { // hopsTto1 < hopsTto2 + return -1; + } + } else if (tAo1 == null) { + return 1; + } else { + return -1; + } + } + } + + + /* If one cert's issuer is an ancestor of that cert's subject, + * then it is preferable, in order of increasing naming distance. + */ + if (debug != null) { + debug.println(METHOD_NME+" CERT ISSUER/SUBJECT COMPARISON TEST..."); + } + X500Principal cSubject1 = oCert1.getSubjectX500Principal(); + X500Principal cSubject2 = oCert2.getSubjectX500Principal(); + X500Name cSubject1Name = X500Name.asX500Name(cSubject1); + X500Name cSubject2Name = X500Name.asX500Name(cSubject2); + + if (debug != null) { + debug.println(METHOD_NME + " o1 Subject: " + cSubject1); + debug.println(METHOD_NME + " o2 Subject: " + cSubject2); + } + int distanceStoI1 = Builder.distance + (cSubject1Name, cIssuer1Name, Integer.MAX_VALUE); + int distanceStoI2 = Builder.distance + (cSubject2Name, cIssuer2Name, Integer.MAX_VALUE); + if (debug != null) { + debug.println(METHOD_NME + " distanceStoI1: " + distanceStoI1); + debug.println(METHOD_NME + " distanceStoI2: " + distanceStoI2); + } + if (distanceStoI2 > distanceStoI1) { + return -1; + } else if (distanceStoI2 < distanceStoI1) { + return 1; + } + + /* Otherwise, certs are equally preferable. + */ + if (debug != null) { + debug.println(METHOD_NME + " no tests matched; RETURN 0"); + } + return -1; + } + } + + /** + * Verifies a matching certificate. + * + * This method executes the validation steps in the PKIX path + * validation algorithm which were + * not satisfied by the selection criteria used by getCertificates() + * to find the certs and only the steps that can be executed in a + * forward direction (target to trust anchor). Those steps that can + * only be executed in a reverse direction are deferred until the + * complete path has been built. + * + * Trust anchor certs are not validated, but are used to verify the + * signature and revocation status of the previous cert. + * + * If the last certificate is being verified (the one whose subject + * matches the target subject, then steps in 6.1.4 of the PKIX + * Certification Path Validation algorithm are NOT executed, + * regardless of whether or not the last cert is an end-entity + * cert or not. This allows callers to certify CA certs as + * well as EE certs. + * + * @param cert the certificate to be verified + * @param currentState the current state against which the cert is verified + * @param certPathList the certPathList generated thus far + */ + @Override + void verifyCert(X509Certificate cert, State currentState, + List certPathList) + throws GeneralSecurityException + { + if (debug != null) { + debug.println("ForwardBuilder.verifyCert(SN: " + + Debug.toHexString(cert.getSerialNumber()) + + "\n Issuer: " + cert.getIssuerX500Principal() + ")" + + "\n Subject: " + cert.getSubjectX500Principal() + ")"); + } + + ForwardState currState = (ForwardState)currentState; + + // Don't bother to verify untrusted certificate more. + currState.untrustedChecker.check(cert, Collections.emptySet()); + + /* + * check for looping - abort a loop if we encounter the same + * certificate twice + */ + if (certPathList != null) { + for (X509Certificate cpListCert : certPathList) { + if (cert.equals(cpListCert)) { + if (debug != null) { + debug.println("loop detected!!"); + } + throw new CertPathValidatorException("loop detected"); + } + } + } + + /* check if trusted cert */ + boolean isTrustedCert = trustedCerts.contains(cert); + + /* we don't perform any validation of the trusted cert */ + if (!isTrustedCert) { + /* + * Check CRITICAL private extensions for user checkers that + * support forward checking (forwardCheckers) and remove + * ones we know how to check. + */ + Set unresCritExts = cert.getCriticalExtensionOIDs(); + if (unresCritExts == null) { + unresCritExts = Collections.emptySet(); + } + for (PKIXCertPathChecker checker : currState.forwardCheckers) { + checker.check(cert, unresCritExts); + } + + /* + * Remove extensions from user checkers that don't support + * forward checking. After this step, we will have removed + * all extensions that all user checkers are capable of + * processing. + */ + for (PKIXCertPathChecker checker : buildParams.certPathCheckers()) { + if (!checker.isForwardCheckingSupported()) { + Set supportedExts = checker.getSupportedExtensions(); + if (supportedExts != null) { + unresCritExts.removeAll(supportedExts); + } + } + } + + /* + * Look at the remaining extensions and remove any ones we know how + * to check. If there are any left, throw an exception! + */ + if (!unresCritExts.isEmpty()) { + unresCritExts.remove(BasicConstraints_Id.toString()); + unresCritExts.remove(NameConstraints_Id.toString()); + unresCritExts.remove(CertificatePolicies_Id.toString()); + unresCritExts.remove(PolicyMappings_Id.toString()); + unresCritExts.remove(PolicyConstraints_Id.toString()); + unresCritExts.remove(InhibitAnyPolicy_Id.toString()); + unresCritExts.remove(SubjectAlternativeName_Id.toString()); + unresCritExts.remove(KeyUsage_Id.toString()); + unresCritExts.remove(ExtendedKeyUsage_Id.toString()); + + if (!unresCritExts.isEmpty()) + throw new CertPathValidatorException + ("Unrecognized critical extension(s)", null, null, -1, + PKIXReason.UNRECOGNIZED_CRIT_EXT); + } + } + + /* + * if this is the target certificate (init=true), then we are + * not able to do any more verification, so just return + */ + if (currState.isInitial()) { + return; + } + + /* we don't perform any validation of the trusted cert */ + if (!isTrustedCert) { + /* Make sure this is a CA cert */ + if (cert.getBasicConstraints() == -1) { + throw new CertificateException("cert is NOT a CA cert"); + } + + /* + * Check keyUsage extension + */ + KeyChecker.verifyCAKeyUsage(cert); + } + + /* + * the following checks are performed even when the cert + * is a trusted cert, since we are only extracting the + * subjectDN, and publicKey from the cert + * in order to verify a previous cert + */ + + /* + * Check signature only if no key requiring key parameters has been + * encountered. + */ + if (!currState.keyParamsNeeded()) { + (currState.cert).verify(cert.getPublicKey(), + buildParams.sigProvider()); + } + } + + /** + * Verifies whether the input certificate completes the path. + * Checks the cert against each trust anchor that was specified, in order, + * and returns true as soon as it finds a valid anchor. + * Returns true if the cert matches a trust anchor specified as a + * certificate or if the cert verifies with a trust anchor that + * was specified as a trusted {pubkey, caname} pair. Returns false if none + * of the trust anchors are valid for this cert. + * + * @param cert the certificate to test + * @return a boolean value indicating whether the cert completes the path. + */ + @Override + boolean isPathCompleted(X509Certificate cert) { + for (TrustAnchor anchor : trustAnchors) { + if (anchor.getTrustedCert() != null) { + if (cert.equals(anchor.getTrustedCert())) { + this.trustAnchor = anchor; + return true; + } else { + continue; + } + } + X500Principal principal = anchor.getCA(); + PublicKey publicKey = anchor.getCAPublicKey(); + + if (principal != null && publicKey != null && + principal.equals(cert.getSubjectX500Principal())) { + if (publicKey.equals(cert.getPublicKey())) { + // the cert itself is a trust anchor + this.trustAnchor = anchor; + return true; + } + // else, it is a self-issued certificate of the anchor + } + + // Check subject/issuer name chaining + if (principal == null || + !principal.equals(cert.getIssuerX500Principal())) { + continue; + } + + // skip anchor if it contains a DSA key with no DSA params + if (PKIX.isDSAPublicKeyWithoutParams(publicKey)) { + continue; + } + + /* + * Check signature + */ + try { + cert.verify(publicKey, buildParams.sigProvider()); + } catch (InvalidKeyException ike) { + if (debug != null) { + debug.println("ForwardBuilder.isPathCompleted() invalid " + + "DSA key found"); + } + continue; + } catch (GeneralSecurityException e){ + if (debug != null) { + debug.println("ForwardBuilder.isPathCompleted() " + + "unexpected exception"); + e.printStackTrace(); + } + continue; + } + + this.trustAnchor = anchor; + return true; + } + + return false; + } + + /** Adds the certificate to the certPathList + * + * @param cert the certificate to be added + * @param certPathList the certification path list + */ + @Override + void addCertToPath(X509Certificate cert, + LinkedList certPathList) + { + certPathList.addFirst(cert); + } + + /** Removes final certificate from the certPathList + * + * @param certPathList the certification path list + */ + @Override + void removeFinalCertFromPath(LinkedList certPathList) { + certPathList.removeFirst(); + } +} diff --git a/src/sun/security/provider/certpath/ForwardState.java b/src/sun/security/provider/certpath/ForwardState.java new file mode 100644 index 00000000..2dc9e208 --- /dev/null +++ b/src/sun/security/provider/certpath/ForwardState.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import javax.security.auth.x500.X500Principal; + +import sun.security.util.Debug; +import sun.security.x509.SubjectAlternativeNameExtension; +import sun.security.x509.GeneralNames; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNameInterface; +import sun.security.x509.X500Name; +import sun.security.x509.X509CertImpl; + +/** + * A specification of a forward PKIX validation state + * which is initialized by each build and updated each time a + * certificate is added to the current path. + * @since 1.4 + * @author Yassir Elley + */ +class ForwardState implements State { + + private static final Debug debug = Debug.getInstance("certpath"); + + /* The issuer DN of the last cert in the path */ + X500Principal issuerDN; + + /* The last cert in the path */ + X509CertImpl cert; + + /* The set of subjectDNs and subjectAltNames of all certs in the path */ + HashSet subjectNamesTraversed; + + /* + * The number of intermediate CA certs which have been traversed so + * far in the path + */ + int traversedCACerts; + + /* Flag indicating if state is initial (path is just starting) */ + private boolean init = true; + + + /* the untrusted certificates checker */ + UntrustedChecker untrustedChecker; + + /* The list of user-defined checkers that support forward checking */ + ArrayList forwardCheckers; + + /* Flag indicating if key needing to inherit key parameters has been + * encountered. + */ + boolean keyParamsNeededFlag = false; + + /** + * Returns a boolean flag indicating if the state is initial + * (just starting) + * + * @return boolean flag indicating if the state is initial (just starting) + */ + @Override + public boolean isInitial() { + return init; + } + + /** + * Return boolean flag indicating whether a public key that needs to inherit + * key parameters has been encountered. + * + * @return boolean true if key needing to inherit parameters has been + * encountered; false otherwise. + */ + @Override + public boolean keyParamsNeeded() { + return keyParamsNeededFlag; + } + + /** + * Display state for debugging purposes + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("State ["); + sb.append("\n issuerDN of last cert: ").append(issuerDN); + sb.append("\n traversedCACerts: ").append(traversedCACerts); + sb.append("\n init: ").append(String.valueOf(init)); + sb.append("\n keyParamsNeeded: ").append + (String.valueOf(keyParamsNeededFlag)); + sb.append("\n subjectNamesTraversed: \n").append + (subjectNamesTraversed); + sb.append("]\n"); + return sb.toString(); + } + + /** + * Initialize the state. + * + * @param certPathCheckers the list of user-defined PKIXCertPathCheckers + */ + public void initState(List certPathCheckers) + throws CertPathValidatorException + { + subjectNamesTraversed = new HashSet(); + traversedCACerts = 0; + + /* + * Populate forwardCheckers with every user-defined checker + * that supports forward checking and initialize the forwardCheckers + */ + forwardCheckers = new ArrayList(); + for (PKIXCertPathChecker checker : certPathCheckers) { + if (checker.isForwardCheckingSupported()) { + checker.init(true); + forwardCheckers.add(checker); + } + } + + init = true; + } + + /** + * Update the state with the next certificate added to the path. + * + * @param cert the certificate which is used to update the state + */ + @Override + public void updateState(X509Certificate cert) + throws CertificateException, IOException, CertPathValidatorException { + + if (cert == null) + return; + + X509CertImpl icert = X509CertImpl.toImpl(cert); + + /* see if certificate key has null parameters */ + if (PKIX.isDSAPublicKeyWithoutParams(icert.getPublicKey())) { + keyParamsNeededFlag = true; + } + + /* update certificate */ + this.cert = icert; + + /* update issuer DN */ + issuerDN = cert.getIssuerX500Principal(); + + if (!X509CertImpl.isSelfIssued(cert)) { + + /* + * update traversedCACerts only if this is a non-self-issued + * intermediate CA cert + */ + if (!init && cert.getBasicConstraints() != -1) { + traversedCACerts++; + } + } + + /* update subjectNamesTraversed only if this is the EE cert or if + this cert is not self-issued */ + if (init || !X509CertImpl.isSelfIssued(cert)){ + X500Principal subjName = cert.getSubjectX500Principal(); + subjectNamesTraversed.add(X500Name.asX500Name(subjName)); + + try { + SubjectAlternativeNameExtension subjAltNameExt + = icert.getSubjectAlternativeNameExtension(); + if (subjAltNameExt != null) { + GeneralNames gNames = subjAltNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + for (GeneralName gName : gNames.names()) { + subjectNamesTraversed.add(gName.getName()); + } + } + } catch (IOException e) { + if (debug != null) { + debug.println("ForwardState.updateState() unexpected " + + "exception"); + e.printStackTrace(); + } + throw new CertPathValidatorException(e); + } + } + + init = false; + } + + /* + * Clone current state. The state is cloned as each cert is + * added to the path. This is necessary if backtracking occurs, + * and a prior state needs to be restored. + * + * Note that this is a SMART clone. Not all fields are fully copied, + * because some of them will + * not have their contents modified by subsequent calls to updateState. + */ + @Override + @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly + public Object clone() { + try { + ForwardState clonedState = (ForwardState) super.clone(); + + /* clone checkers, if cloneable */ + clonedState.forwardCheckers = (ArrayList) + forwardCheckers.clone(); + ListIterator li = + clonedState.forwardCheckers.listIterator(); + while (li.hasNext()) { + PKIXCertPathChecker checker = li.next(); + if (checker instanceof Cloneable) { + li.set((PKIXCertPathChecker)checker.clone()); + } + } + + /* + * Shallow copy traversed names. There is no need to + * deep copy contents, since the elements of the Set + * are never modified by subsequent calls to updateState(). + */ + clonedState.subjectNamesTraversed + = (HashSet)subjectNamesTraversed.clone(); + return clonedState; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.toString(), e); + } + } +} diff --git a/src/sun/security/provider/certpath/IndexedCollectionCertStore.java b/src/sun/security/provider/certpath/IndexedCollectionCertStore.java new file mode 100644 index 00000000..5d378500 --- /dev/null +++ b/src/sun/security/provider/certpath/IndexedCollectionCertStore.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.util.*; + +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.*; + +import javax.security.auth.x500.X500Principal; + +/** + * A CertStore that retrieves Certificates and + * CRLs from a Collection. + *

    + * This implementation is functionally equivalent to CollectionCertStore + * with two differences: + *

      + *
    1. Upon construction, the elements in the specified Collection are + * partially indexed. X509Certificates are indexed by subject, X509CRLs + * by issuer, non-X509 Certificates and CRLs are copied without indexing, + * other objects are ignored. This increases CertStore construction time + * but allows significant speedups for searches which specify the indexed + * attributes, in particular for large Collections (reduction from linear + * time to effectively constant time). Searches for non-indexed queries + * are as fast (or marginally faster) than for the standard + * CollectionCertStore. Certificate subjects and CRL issuers + * were found to be specified in most searches used internally by the + * CertPath provider. Additional attributes could indexed if there are + * queries that justify the effort. + * + *
    2. Changes to the specified Collection after construction time are + * not detected and ignored. This is because there is no way to efficiently + * detect if a Collection has been modified, a full traversal would be + * required. That would degrade lookup performance to linear time and + * eliminated the benefit of indexing. We may fix this via the introduction + * of new public APIs in the future. + *
    + *

    + * Before calling the {@link #engineGetCertificates engineGetCertificates} or + * {@link #engineGetCRLs engineGetCRLs} methods, the + * {@link #CollectionCertStore(CertStoreParameters) + * CollectionCertStore(CertStoreParameters)} constructor is called to + * create the CertStore and establish the + * Collection from which Certificates and + * CRLs will be retrieved. If the specified + * Collection contains an object that is not a + * Certificate or CRL, that object will be + * ignored. + *

    + * Concurrent Access + *

    + * As described in the javadoc for CertStoreSpi, the + * engineGetCertificates and engineGetCRLs methods + * must be thread-safe. That is, multiple threads may concurrently + * invoke these methods on a single CollectionCertStore + * object (or more than one) with no ill effects. + *

    + * This is achieved by requiring that the Collection passed to + * the {@link #CollectionCertStore(CertStoreParameters) + * CollectionCertStore(CertStoreParameters)} constructor (via the + * CollectionCertStoreParameters object) must have fail-fast + * iterators. Simultaneous modifications to the Collection can thus be + * detected and certificate or CRL retrieval can be retried. The fact that + * Certificates and CRLs must be thread-safe is also + * essential. + * + * @see CertStore + * @see CollectionCertStore + * + * @author Andreas Sterbenz + */ +public class IndexedCollectionCertStore extends CertStoreSpi { + + /** + * Map X500Principal(subject) -> X509Certificate | List of X509Certificate + */ + private Map certSubjects; + /** + * Map X500Principal(issuer) -> X509CRL | List of X509CRL + */ + private Map crlIssuers; + /** + * Sets of non-X509 certificates and CRLs + */ + private Set otherCertificates; + private Set otherCRLs; + + /** + * Creates a CertStore with the specified parameters. + * For this class, the parameters object must be an instance of + * CollectionCertStoreParameters. + * + * @param params the algorithm parameters + * @exception InvalidAlgorithmParameterException if params is not an + * instance of CollectionCertStoreParameters + */ + public IndexedCollectionCertStore(CertStoreParameters params) + throws InvalidAlgorithmParameterException { + super(params); + if (!(params instanceof CollectionCertStoreParameters)) { + throw new InvalidAlgorithmParameterException( + "parameters must be CollectionCertStoreParameters"); + } + Collection coll = ((CollectionCertStoreParameters)params).getCollection(); + if (coll == null) { + throw new InvalidAlgorithmParameterException + ("Collection must not be null"); + } + buildIndex(coll); + } + + /** + * Index the specified Collection copying all references to Certificates + * and CRLs. + */ + private void buildIndex(Collection coll) { + certSubjects = new HashMap(); + crlIssuers = new HashMap(); + otherCertificates = null; + otherCRLs = null; + for (Object obj : coll) { + if (obj instanceof X509Certificate) { + indexCertificate((X509Certificate)obj); + } else if (obj instanceof X509CRL) { + indexCRL((X509CRL)obj); + } else if (obj instanceof Certificate) { + if (otherCertificates == null) { + otherCertificates = new HashSet(); + } + otherCertificates.add((Certificate)obj); + } else if (obj instanceof CRL) { + if (otherCRLs == null) { + otherCRLs = new HashSet(); + } + otherCRLs.add((CRL)obj); + } else { + // ignore + } + } + if (otherCertificates == null) { + otherCertificates = Collections.emptySet(); + } + if (otherCRLs == null) { + otherCRLs = Collections.emptySet(); + } + } + + /** + * Add an X509Certificate to the index. + */ + private void indexCertificate(X509Certificate cert) { + X500Principal subject = cert.getSubjectX500Principal(); + Object oldEntry = certSubjects.put(subject, cert); + if (oldEntry != null) { // assume this is unlikely + if (oldEntry instanceof X509Certificate) { + if (cert.equals(oldEntry)) { + return; + } + List list = new ArrayList<>(2); + list.add(cert); + list.add((X509Certificate)oldEntry); + certSubjects.put(subject, list); + } else { + @SuppressWarnings("unchecked") // See certSubjects javadoc. + List list = (List)oldEntry; + if (list.contains(cert) == false) { + list.add(cert); + } + certSubjects.put(subject, list); + } + } + } + + /** + * Add an X509CRL to the index. + */ + private void indexCRL(X509CRL crl) { + X500Principal issuer = crl.getIssuerX500Principal(); + Object oldEntry = crlIssuers.put(issuer, crl); + if (oldEntry != null) { // assume this is unlikely + if (oldEntry instanceof X509CRL) { + if (crl.equals(oldEntry)) { + return; + } + List list = new ArrayList<>(2); + list.add(crl); + list.add((X509CRL)oldEntry); + crlIssuers.put(issuer, list); + } else { + // See crlIssuers javadoc. + @SuppressWarnings("unchecked") + List list = (List)oldEntry; + if (list.contains(crl) == false) { + list.add(crl); + } + crlIssuers.put(issuer, list); + } + } + } + + /** + * Returns a Collection of Certificates that + * match the specified selector. If no Certificates + * match the selector, an empty Collection will be returned. + * + * @param selector a CertSelector used to select which + * Certificates should be returned. Specify null + * to return all Certificates. + * @return a Collection of Certificates that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + public Collection engineGetCertificates(CertSelector selector) + throws CertStoreException { + + // no selector means match all + if (selector == null) { + Set matches = new HashSet<>(); + matchX509Certs(new X509CertSelector(), matches); + matches.addAll(otherCertificates); + return matches; + } + + if (selector instanceof X509CertSelector == false) { + Set matches = new HashSet<>(); + matchX509Certs(selector, matches); + for (Certificate cert : otherCertificates) { + if (selector.match(cert)) { + matches.add(cert); + } + } + return matches; + } + + if (certSubjects.isEmpty()) { + return Collections.emptySet(); + } + X509CertSelector x509Selector = (X509CertSelector)selector; + // see if the subject is specified + X500Principal subject; + X509Certificate matchCert = x509Selector.getCertificate(); + if (matchCert != null) { + subject = matchCert.getSubjectX500Principal(); + } else { + subject = x509Selector.getSubject(); + } + if (subject != null) { + // yes, narrow down candidates to indexed possibilities + Object entry = certSubjects.get(subject); + if (entry == null) { + return Collections.emptySet(); + } + if (entry instanceof X509Certificate) { + X509Certificate x509Entry = (X509Certificate)entry; + if (x509Selector.match(x509Entry)) { + return Collections.singleton(x509Entry); + } else { + return Collections.emptySet(); + } + } else { + // See certSubjects javadoc. + @SuppressWarnings("unchecked") + List list = (List)entry; + Set matches = new HashSet<>(16); + for (X509Certificate cert : list) { + if (x509Selector.match(cert)) { + matches.add(cert); + } + } + return matches; + } + } + // cannot use index, iterate all + Set matches = new HashSet<>(16); + matchX509Certs(x509Selector, matches); + return matches; + } + + /** + * Iterate through all the X509Certificates and add matches to the + * collection. + */ + private void matchX509Certs(CertSelector selector, + Collection matches) { + + for (Object obj : certSubjects.values()) { + if (obj instanceof X509Certificate) { + X509Certificate cert = (X509Certificate)obj; + if (selector.match(cert)) { + matches.add(cert); + } + } else { + // See certSubjects javadoc. + @SuppressWarnings("unchecked") + List list = (List)obj; + for (X509Certificate cert : list) { + if (selector.match(cert)) { + matches.add(cert); + } + } + } + } + } + + /** + * Returns a Collection of CRLs that + * match the specified selector. If no CRLs + * match the selector, an empty Collection will be returned. + * + * @param selector a CRLSelector used to select which + * CRLs should be returned. Specify null + * to return all CRLs. + * @return a Collection of CRLs that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException { + + if (selector == null) { + Set matches = new HashSet<>(); + matchX509CRLs(new X509CRLSelector(), matches); + matches.addAll(otherCRLs); + return matches; + } + + if (selector instanceof X509CRLSelector == false) { + Set matches = new HashSet<>(); + matchX509CRLs(selector, matches); + for (CRL crl : otherCRLs) { + if (selector.match(crl)) { + matches.add(crl); + } + } + return matches; + } + + if (crlIssuers.isEmpty()) { + return Collections.emptySet(); + } + X509CRLSelector x509Selector = (X509CRLSelector)selector; + // see if the issuer is specified + Collection issuers = x509Selector.getIssuers(); + if (issuers != null) { + HashSet matches = new HashSet<>(16); + for (X500Principal issuer : issuers) { + Object entry = crlIssuers.get(issuer); + if (entry == null) { + // empty + } else if (entry instanceof X509CRL) { + X509CRL crl = (X509CRL)entry; + if (x509Selector.match(crl)) { + matches.add(crl); + } + } else { // List + // See crlIssuers javadoc. + @SuppressWarnings("unchecked") + List list = (List)entry; + for (X509CRL crl : list) { + if (x509Selector.match(crl)) { + matches.add(crl); + } + } + } + } + return matches; + } + // cannot use index, iterate all + Set matches = new HashSet<>(16); + matchX509CRLs(x509Selector, matches); + return matches; + } + + /** + * Iterate through all the X509CRLs and add matches to the + * collection. + */ + private void matchX509CRLs(CRLSelector selector, Collection matches) { + for (Object obj : crlIssuers.values()) { + if (obj instanceof X509CRL) { + X509CRL crl = (X509CRL)obj; + if (selector.match(crl)) { + matches.add(crl); + } + } else { + // See crlIssuers javadoc. + @SuppressWarnings("unchecked") + List list = (List)obj; + for (X509CRL crl : list) { + if (selector.match(crl)) { + matches.add(crl); + } + } + } + } + } + +} diff --git a/src/sun/security/provider/certpath/KeyChecker.java b/src/sun/security/provider/certpath/KeyChecker.java new file mode 100644 index 00000000..2d45d956 --- /dev/null +++ b/src/sun/security/provider/certpath/KeyChecker.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.util.*; +import java.security.cert.*; +import java.security.cert.PKIXReason; + +import sun.security.util.Debug; +import static sun.security.x509.PKIXExtensions.*; + +/** + * KeyChecker is a PKIXCertPathChecker that checks that the + * keyCertSign bit is set in the keyUsage extension in an intermediate CA + * certificate. It also checks whether the final certificate in a + * certification path meets the specified target constraints specified as + * a CertSelector in the PKIXParameters passed to the CertPathValidator. + * + * @since 1.4 + * @author Yassir Elley + */ +class KeyChecker extends PKIXCertPathChecker { + + private static final Debug debug = Debug.getInstance("certpath"); + private final int certPathLen; + private final CertSelector targetConstraints; + private int remainingCerts; + + private Set supportedExts; + + /** + * Creates a KeyChecker. + * + * @param certPathLen allowable cert path length + * @param targetCertSel a CertSelector object specifying the constraints + * on the target certificate + */ + KeyChecker(int certPathLen, CertSelector targetCertSel) { + this.certPathLen = certPathLen; + this.targetConstraints = targetCertSel; + } + + /** + * Initializes the internal state of the checker from parameters + * specified in the constructor + */ + @Override + public void init(boolean forward) throws CertPathValidatorException { + if (!forward) { + remainingCerts = certPathLen; + } else { + throw new CertPathValidatorException + ("forward checking not supported"); + } + } + + @Override + public boolean isForwardCheckingSupported() { + return false; + } + + @Override + public Set getSupportedExtensions() { + if (supportedExts == null) { + supportedExts = new HashSet(3); + supportedExts.add(KeyUsage_Id.toString()); + supportedExts.add(ExtendedKeyUsage_Id.toString()); + supportedExts.add(SubjectAlternativeName_Id.toString()); + supportedExts = Collections.unmodifiableSet(supportedExts); + } + return supportedExts; + } + + /** + * Checks that keyUsage and target constraints are satisfied by + * the specified certificate. + * + * @param cert the Certificate + * @param unresolvedCritExts the unresolved critical extensions + * @throws CertPathValidatorException if certificate does not verify + */ + @Override + public void check(Certificate cert, Collection unresCritExts) + throws CertPathValidatorException + { + X509Certificate currCert = (X509Certificate)cert; + + remainingCerts--; + + // if final certificate, check that target constraints are satisfied + if (remainingCerts == 0) { + if (targetConstraints != null && + targetConstraints.match(currCert) == false) { + throw new CertPathValidatorException("target certificate " + + "constraints check failed"); + } + } else { + // otherwise, verify that keyCertSign bit is set in CA certificate + verifyCAKeyUsage(currCert); + } + + // remove the extensions that we have checked + if (unresCritExts != null && !unresCritExts.isEmpty()) { + unresCritExts.remove(KeyUsage_Id.toString()); + unresCritExts.remove(ExtendedKeyUsage_Id.toString()); + unresCritExts.remove(SubjectAlternativeName_Id.toString()); + } + } + + // the index of keyCertSign in the boolean KeyUsage array + private static final int KEY_CERT_SIGN = 5; + /** + * Verifies the key usage extension in a CA cert. + * The key usage extension, if present, must assert the keyCertSign bit. + * The extended key usage extension is not checked (see CR 4776794 for + * more information). + */ + static void verifyCAKeyUsage(X509Certificate cert) + throws CertPathValidatorException { + String msg = "CA key usage"; + if (debug != null) { + debug.println("KeyChecker.verifyCAKeyUsage() ---checking " + msg + + "..."); + } + + boolean[] keyUsageBits = cert.getKeyUsage(); + + // getKeyUsage returns null if the KeyUsage extension is not present + // in the certificate - in which case there is nothing to check + if (keyUsageBits == null) { + return; + } + + // throw an exception if the keyCertSign bit is not set + if (!keyUsageBits[KEY_CERT_SIGN]) { + throw new CertPathValidatorException + (msg + " check failed: keyCertSign bit is not set", null, + null, -1, PKIXReason.INVALID_KEY_USAGE); + } + + if (debug != null) { + debug.println("KeyChecker.verifyCAKeyUsage() " + msg + + " verified."); + } + } +} diff --git a/src/sun/security/provider/certpath/OCSP.java b/src/sun/security/provider/certpath/OCSP.java new file mode 100644 index 00000000..f58e48f6 --- /dev/null +++ b/src/sun/security/provider/certpath/OCSP.java @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.provider.certpath; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.net.HttpURLConnection; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CRLReason; +import java.security.cert.Extension; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import sun.security.action.GetIntegerAction; +import sun.security.util.Debug; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AccessDescription; +import sun.security.x509.AuthorityInfoAccessExtension; +import sun.security.x509.GeneralName; +import sun.security.x509.GeneralNameInterface; +import sun.security.x509.URIName; +import sun.security.x509.X509CertImpl; + +/** + * This is a class that checks the revocation status of a certificate(s) using + * OCSP. It is not a PKIXCertPathChecker and therefore can be used outside of + * the CertPathValidator framework. It is useful when you want to + * just check the revocation status of a certificate, and you don't want to + * incur the overhead of validating all of the certificates in the + * associated certificate chain. + * + * @author Sean Mullan + */ +public final class OCSP { + + static final ObjectIdentifier NONCE_EXTENSION_OID = + ObjectIdentifier.newInternal(new int[]{ 1, 3, 6, 1, 5, 5, 7, 48, 1, 2}); + + private static final Debug debug = Debug.getInstance("certpath"); + + private static final int DEFAULT_CONNECT_TIMEOUT = 15000; + + /** + * Integer value indicating the timeout length, in seconds, to be + * used for the OCSP check. A timeout of zero is interpreted as + * an infinite timeout. + */ + private static final int CONNECT_TIMEOUT = initializeTimeout(); + + /** + * Initialize the timeout length by getting the OCSP timeout + * system property. If the property has not been set, or if its + * value is negative, set the timeout length to the default. + */ + private static int initializeTimeout() { + Integer tmp = java.security.AccessController.doPrivileged( + new GetIntegerAction("com.sun.security.ocsp.timeout")); + if (tmp == null || tmp < 0) { + return DEFAULT_CONNECT_TIMEOUT; + } + // Convert to milliseconds, as the system property will be + // specified in seconds + return tmp * 1000; + } + + private OCSP() {} + + /** + * Obtains the revocation status of a certificate using OCSP using the most + * common defaults. The OCSP responder URI is retrieved from the + * certificate's AIA extension. The OCSP responder certificate is assumed + * to be the issuer's certificate (or issued by the issuer CA). + * + * @param cert the certificate to be checked + * @param issuerCert the issuer certificate + * @return the RevocationStatus + * @throws IOException if there is an exception connecting to or + * communicating with the OCSP responder + * @throws CertPathValidatorException if an exception occurs while + * encoding the OCSP Request or validating the OCSP Response + */ + public static RevocationStatus check(X509Certificate cert, + X509Certificate issuerCert) + throws IOException, CertPathValidatorException { + CertId certId = null; + URI responderURI = null; + try { + X509CertImpl certImpl = X509CertImpl.toImpl(cert); + responderURI = getResponderURI(certImpl); + if (responderURI == null) { + throw new CertPathValidatorException + ("No OCSP Responder URI in certificate"); + } + certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); + } catch (CertificateException | IOException e) { + throw new CertPathValidatorException + ("Exception while encoding OCSPRequest", e); + } + OCSPResponse ocspResponse = check(Collections.singletonList(certId), + responderURI, issuerCert, null, null, + Collections.emptyList()); + return (RevocationStatus)ocspResponse.getSingleResponse(certId); + } + + /** + * Obtains the revocation status of a certificate using OCSP. + * + * @param cert the certificate to be checked + * @param issuerCert the issuer certificate + * @param responderURI the URI of the OCSP responder + * @param responderCert the OCSP responder's certificate + * @param date the time the validity of the OCSP responder's certificate + * should be checked against. If null, the current time is used. + * @return the RevocationStatus + * @throws IOException if there is an exception connecting to or + * communicating with the OCSP responder + * @throws CertPathValidatorException if an exception occurs while + * encoding the OCSP Request or validating the OCSP Response + */ + public static RevocationStatus check(X509Certificate cert, + X509Certificate issuerCert, + URI responderURI, + X509Certificate responderCert, + Date date) + throws IOException, CertPathValidatorException + { + return check(cert, issuerCert, responderURI, responderCert, date, + Collections.emptyList()); + } + + // Called by com.sun.deploy.security.TrustDecider + public static RevocationStatus check(X509Certificate cert, + X509Certificate issuerCert, + URI responderURI, + X509Certificate responderCert, + Date date, List extensions) + throws IOException, CertPathValidatorException + { + CertId certId = null; + try { + X509CertImpl certImpl = X509CertImpl.toImpl(cert); + certId = new CertId(issuerCert, certImpl.getSerialNumberObject()); + } catch (CertificateException | IOException e) { + throw new CertPathValidatorException + ("Exception while encoding OCSPRequest", e); + } + OCSPResponse ocspResponse = check(Collections.singletonList(certId), + responderURI, issuerCert, responderCert, date, extensions); + return (RevocationStatus) ocspResponse.getSingleResponse(certId); + } + + /** + * Checks the revocation status of a list of certificates using OCSP. + * + * @param certs the CertIds to be checked + * @param responderURI the URI of the OCSP responder + * @param issuerCert the issuer's certificate + * @param responderCert the OCSP responder's certificate + * @param date the time the validity of the OCSP responder's certificate + * should be checked against. If null, the current time is used. + * @return the OCSPResponse + * @throws IOException if there is an exception connecting to or + * communicating with the OCSP responder + * @throws CertPathValidatorException if an exception occurs while + * encoding the OCSP Request or validating the OCSP Response + */ + static OCSPResponse check(List certIds, URI responderURI, + X509Certificate issuerCert, + X509Certificate responderCert, Date date, + List extensions) + throws IOException, CertPathValidatorException + { + byte[] bytes = null; + OCSPRequest request = null; + try { + request = new OCSPRequest(certIds, extensions); + bytes = request.encodeBytes(); + } catch (IOException ioe) { + throw new CertPathValidatorException + ("Exception while encoding OCSPRequest", ioe); + } + + InputStream in = null; + OutputStream out = null; + byte[] response = null; + try { + URL url = responderURI.toURL(); + if (debug != null) { + debug.println("connecting to OCSP service at: " + url); + } + HttpURLConnection con = (HttpURLConnection)url.openConnection(); + con.setConnectTimeout(CONNECT_TIMEOUT); + con.setReadTimeout(CONNECT_TIMEOUT); + con.setDoOutput(true); + con.setDoInput(true); + con.setRequestMethod("POST"); + con.setRequestProperty + ("Content-type", "application/ocsp-request"); + con.setRequestProperty + ("Content-length", String.valueOf(bytes.length)); + out = con.getOutputStream(); + out.write(bytes); + out.flush(); + // Check the response + if (debug != null && + con.getResponseCode() != HttpURLConnection.HTTP_OK) { + debug.println("Received HTTP error: " + con.getResponseCode() + + " - " + con.getResponseMessage()); + } + in = con.getInputStream(); + int contentLength = con.getContentLength(); + if (contentLength == -1) { + contentLength = Integer.MAX_VALUE; + } + response = new byte[contentLength > 2048 ? 2048 : contentLength]; + int total = 0; + while (total < contentLength) { + int count = in.read(response, total, response.length - total); + if (count < 0) + break; + + total += count; + if (total >= response.length && total < contentLength) { + response = Arrays.copyOf(response, total * 2); + } + } + response = Arrays.copyOf(response, total); + } catch (IOException ioe) { + throw new CertPathValidatorException( + "Unable to determine revocation status due to network error", + ioe, null, -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + throw ioe; + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ioe) { + throw ioe; + } + } + } + + OCSPResponse ocspResponse = null; + try { + ocspResponse = new OCSPResponse(response); + } catch (IOException ioe) { + // response decoding exception + throw new CertPathValidatorException(ioe); + } + + // verify the response + ocspResponse.verify(certIds, issuerCert, responderCert, date, + request.getNonce()); + + return ocspResponse; + } + + /** + * Returns the URI of the OCSP Responder as specified in the + * certificate's Authority Information Access extension, or null if + * not specified. + * + * @param cert the certificate + * @return the URI of the OCSP Responder, or null if not specified + */ + // Called by com.sun.deploy.security.TrustDecider + public static URI getResponderURI(X509Certificate cert) { + try { + return getResponderURI(X509CertImpl.toImpl(cert)); + } catch (CertificateException ce) { + // treat this case as if the cert had no extension + return null; + } + } + + static URI getResponderURI(X509CertImpl certImpl) { + + // Examine the certificate's AuthorityInfoAccess extension + AuthorityInfoAccessExtension aia = + certImpl.getAuthorityInfoAccessExtension(); + if (aia == null) { + return null; + } + + List descriptions = aia.getAccessDescriptions(); + for (AccessDescription description : descriptions) { + if (description.getAccessMethod().equals((Object) + AccessDescription.Ad_OCSP_Id)) { + + GeneralName generalName = description.getAccessLocation(); + if (generalName.getType() == GeneralNameInterface.NAME_URI) { + URIName uri = (URIName) generalName.getName(); + return uri.getURI(); + } + } + } + return null; + } + + /** + * The Revocation Status of a certificate. + */ + public static interface RevocationStatus { + public enum CertStatus { GOOD, REVOKED, UNKNOWN }; + + /** + * Returns the revocation status. + */ + CertStatus getCertStatus(); + /** + * Returns the time when the certificate was revoked, or null + * if it has not been revoked. + */ + Date getRevocationTime(); + /** + * Returns the reason the certificate was revoked, or null if it + * has not been revoked. + */ + CRLReason getRevocationReason(); + + /** + * Returns a Map of additional extensions. + */ + Map getSingleExtensions(); + } +} diff --git a/src/sun/security/provider/certpath/OCSPRequest.java b/src/sun/security/provider/certpath/OCSPRequest.java new file mode 100644 index 00000000..6bded972 --- /dev/null +++ b/src/sun/security/provider/certpath/OCSPRequest.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.Extension; +import java.util.Collections; +import java.util.List; + +import sun.misc.HexDumpEncoder; +import sun.security.util.*; + +/** + * This class can be used to generate an OCSP request and send it over + * an outputstream. Currently we do not support signing requests + * The OCSP Request is specified in RFC 2560 and + * the ASN.1 definition is as follows: + *

    + *
    + * OCSPRequest     ::=     SEQUENCE {
    + *      tbsRequest                  TBSRequest,
    + *      optionalSignature   [0]     EXPLICIT Signature OPTIONAL }
    + *
    + *   TBSRequest      ::=     SEQUENCE {
    + *      version             [0]     EXPLICIT Version DEFAULT v1,
    + *      requestorName       [1]     EXPLICIT GeneralName OPTIONAL,
    + *      requestList                 SEQUENCE OF Request,
    + *      requestExtensions   [2]     EXPLICIT Extensions OPTIONAL }
    + *
    + *  Signature       ::=     SEQUENCE {
    + *      signatureAlgorithm      AlgorithmIdentifier,
    + *      signature               BIT STRING,
    + *      certs               [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL
    + *   }
    + *
    + *  Version         ::=             INTEGER  {  v1(0) }
    + *
    + *  Request         ::=     SEQUENCE {
    + *      reqCert                     CertID,
    + *      singleRequestExtensions     [0] EXPLICIT Extensions OPTIONAL }
    + *
    + *  CertID          ::= SEQUENCE {
    + *       hashAlgorithm  AlgorithmIdentifier,
    + *       issuerNameHash OCTET STRING, -- Hash of Issuer's DN
    + *       issuerKeyHash  OCTET STRING, -- Hash of Issuers public key
    + *       serialNumber   CertificateSerialNumber
    + * }
    + *
    + * 
    + * + * @author Ram Marti + */ + +class OCSPRequest { + + private static final Debug debug = Debug.getInstance("certpath"); + private static final boolean dump = debug != null && Debug.isOn("ocsp"); + + // List of request CertIds + private final List certIds; + private final List extensions; + private byte[] nonce; + + /* + * Constructs an OCSPRequest. This constructor is used + * to construct an unsigned OCSP Request for a single user cert. + */ + OCSPRequest(CertId certId) { + this(Collections.singletonList(certId)); + } + + OCSPRequest(List certIds) { + this.certIds = certIds; + this.extensions = Collections.emptyList(); + } + + OCSPRequest(List certIds, List extensions) { + this.certIds = certIds; + this.extensions = extensions; + } + + byte[] encodeBytes() throws IOException { + + // encode tbsRequest + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream requestsOut = new DerOutputStream(); + for (CertId certId : certIds) { + DerOutputStream certIdOut = new DerOutputStream(); + certId.encode(certIdOut); + requestsOut.write(DerValue.tag_Sequence, certIdOut); + } + + tmp.write(DerValue.tag_Sequence, requestsOut); + if (!extensions.isEmpty()) { + DerOutputStream extOut = new DerOutputStream(); + for (Extension ext : extensions) { + ext.encode(extOut); + if (ext.getId().equals(OCSP.NONCE_EXTENSION_OID.toString())) { + nonce = ext.getValue(); + } + } + DerOutputStream extsOut = new DerOutputStream(); + extsOut.write(DerValue.tag_Sequence, extOut); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)2), extsOut); + } + + DerOutputStream tbsRequest = new DerOutputStream(); + tbsRequest.write(DerValue.tag_Sequence, tmp); + + // OCSPRequest without the signature + DerOutputStream ocspRequest = new DerOutputStream(); + ocspRequest.write(DerValue.tag_Sequence, tbsRequest); + + byte[] bytes = ocspRequest.toByteArray(); + + if (dump) { + HexDumpEncoder hexEnc = new HexDumpEncoder(); + debug.println("OCSPRequest bytes...\n\n" + + hexEnc.encode(bytes) + "\n"); + } + + return bytes; + } + + List getCertIds() { + return certIds; + } + + byte[] getNonce() { + return nonce; + } +} diff --git a/src/sun/security/provider/certpath/OCSPResponse.java b/src/sun/security/provider/certpath/OCSPResponse.java new file mode 100644 index 00000000..dfe12af7 --- /dev/null +++ b/src/sun/security/provider/certpath/OCSPResponse.java @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.*; +import java.security.*; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.CRLReason; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.security.auth.x500.X500Principal; + +import sun.misc.HexDumpEncoder; +import sun.security.action.GetIntegerAction; +import sun.security.x509.*; +import sun.security.util.*; + +/** + * This class is used to process an OCSP response. + * The OCSP Response is defined + * in RFC 2560 and the ASN.1 encoding is as follows: + *
    + *
    + *  OCSPResponse ::= SEQUENCE {
    + *      responseStatus         OCSPResponseStatus,
    + *      responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
    + *
    + *   OCSPResponseStatus ::= ENUMERATED {
    + *       successful            (0),  --Response has valid confirmations
    + *       malformedRequest      (1),  --Illegal confirmation request
    + *       internalError         (2),  --Internal error in issuer
    + *       tryLater              (3),  --Try again later
    + *                                   --(4) is not used
    + *       sigRequired           (5),  --Must sign the request
    + *       unauthorized          (6)   --Request unauthorized
    + *   }
    + *
    + *   ResponseBytes ::=       SEQUENCE {
    + *       responseType   OBJECT IDENTIFIER,
    + *       response       OCTET STRING }
    + *
    + *   BasicOCSPResponse       ::= SEQUENCE {
    + *      tbsResponseData      ResponseData,
    + *      signatureAlgorithm   AlgorithmIdentifier,
    + *      signature            BIT STRING,
    + *      certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
    + *
    + *   The value for signature SHALL be computed on the hash of the DER
    + *   encoding ResponseData.
    + *
    + *   ResponseData ::= SEQUENCE {
    + *      version              [0] EXPLICIT Version DEFAULT v1,
    + *      responderID              ResponderID,
    + *      producedAt               GeneralizedTime,
    + *      responses                SEQUENCE OF SingleResponse,
    + *      responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
    + *
    + *   ResponderID ::= CHOICE {
    + *      byName               [1] Name,
    + *      byKey                [2] KeyHash }
    + *
    + *   KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
    + *   (excluding the tag and length fields)
    + *
    + *   SingleResponse ::= SEQUENCE {
    + *      certID                       CertID,
    + *      certStatus                   CertStatus,
    + *      thisUpdate                   GeneralizedTime,
    + *      nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
    + *      singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
    + *
    + *   CertStatus ::= CHOICE {
    + *       good        [0]     IMPLICIT NULL,
    + *       revoked     [1]     IMPLICIT RevokedInfo,
    + *       unknown     [2]     IMPLICIT UnknownInfo }
    + *
    + *   RevokedInfo ::= SEQUENCE {
    + *       revocationTime              GeneralizedTime,
    + *       revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
    + *
    + *   UnknownInfo ::= NULL -- this can be replaced with an enumeration
    + *
    + * 
    + * + * @author Ram Marti + */ + +public final class OCSPResponse { + + public enum ResponseStatus { + SUCCESSFUL, // Response has valid confirmations + MALFORMED_REQUEST, // Illegal request + INTERNAL_ERROR, // Internal error in responder + TRY_LATER, // Try again later + UNUSED, // is not used + SIG_REQUIRED, // Must sign the request + UNAUTHORIZED // Request unauthorized + }; + private static ResponseStatus[] rsvalues = ResponseStatus.values(); + + private static final Debug debug = Debug.getInstance("certpath"); + private static final boolean dump = debug != null && Debug.isOn("ocsp"); + private static final ObjectIdentifier OCSP_BASIC_RESPONSE_OID = + ObjectIdentifier.newInternal(new int[] { 1, 3, 6, 1, 5, 5, 7, 48, 1, 1}); + private static final int CERT_STATUS_GOOD = 0; + private static final int CERT_STATUS_REVOKED = 1; + private static final int CERT_STATUS_UNKNOWN = 2; + + // ResponderID CHOICE tags + private static final int NAME_TAG = 1; + private static final int KEY_TAG = 2; + + // Object identifier for the OCSPSigning key purpose + private static final String KP_OCSP_SIGNING_OID = "1.3.6.1.5.5.7.3.9"; + + // Default maximum clock skew in milliseconds (15 minutes) + // allowed when checking validity of OCSP responses + private static final int DEFAULT_MAX_CLOCK_SKEW = 900000; + + /** + * Integer value indicating the maximum allowable clock skew, in seconds, + * to be used for the OCSP check. + */ + private static final int MAX_CLOCK_SKEW = initializeClockSkew(); + + /** + * Initialize the maximum allowable clock skew by getting the OCSP + * clock skew system property. If the property has not been set, or if its + * value is negative, set the skew to the default. + */ + private static int initializeClockSkew() { + Integer tmp = AccessController.doPrivileged( + new GetIntegerAction("com.sun.security.ocsp.clockSkew")); + if (tmp == null || tmp < 0) { + return DEFAULT_MAX_CLOCK_SKEW; + } + // Convert to milliseconds, as the system property will be + // specified in seconds + return tmp * 1000; + } + + // an array of all of the CRLReasons (used in SingleResponse) + private static CRLReason[] values = CRLReason.values(); + + private final ResponseStatus responseStatus; + private final Map singleResponseMap; + private final AlgorithmId sigAlgId; + private final byte[] signature; + private final byte[] tbsResponseData; + private final byte[] responseNonce; + private List certs; + private X509CertImpl signerCert = null; + private X500Principal responderName = null; + private KeyIdentifier responderKeyId = null; + + /* + * Create an OCSP response from its ASN.1 DER encoding. + */ + OCSPResponse(byte[] bytes) throws IOException { + if (dump) { + HexDumpEncoder hexEnc = new HexDumpEncoder(); + debug.println("OCSPResponse bytes...\n\n" + + hexEnc.encode(bytes) + "\n"); + } + DerValue der = new DerValue(bytes); + if (der.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding in OCSP response: " + + "expected ASN.1 SEQUENCE tag."); + } + DerInputStream derIn = der.getData(); + + // responseStatus + int status = derIn.getEnumerated(); + if (status >= 0 && status < rsvalues.length) { + responseStatus = rsvalues[status]; + } else { + // unspecified responseStatus + throw new IOException("Unknown OCSPResponse status: " + status); + } + if (debug != null) { + debug.println("OCSP response status: " + responseStatus); + } + if (responseStatus != ResponseStatus.SUCCESSFUL) { + // no need to continue, responseBytes are not set. + singleResponseMap = Collections.emptyMap(); + certs = new ArrayList(); + sigAlgId = null; + signature = null; + tbsResponseData = null; + responseNonce = null; + return; + } + + // responseBytes + der = derIn.getDerValue(); + if (!der.isContextSpecific((byte)0)) { + throw new IOException("Bad encoding in responseBytes element " + + "of OCSP response: expected ASN.1 context specific tag 0."); + } + DerValue tmp = der.data.getDerValue(); + if (tmp.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding in responseBytes element " + + "of OCSP response: expected ASN.1 SEQUENCE tag."); + } + + // responseType + derIn = tmp.data; + ObjectIdentifier responseType = derIn.getOID(); + if (responseType.equals((Object)OCSP_BASIC_RESPONSE_OID)) { + if (debug != null) { + debug.println("OCSP response type: basic"); + } + } else { + if (debug != null) { + debug.println("OCSP response type: " + responseType); + } + throw new IOException("Unsupported OCSP response type: " + + responseType); + } + + // BasicOCSPResponse + DerInputStream basicOCSPResponse = + new DerInputStream(derIn.getOctetString()); + + DerValue[] seqTmp = basicOCSPResponse.getSequence(2); + if (seqTmp.length < 3) { + throw new IOException("Unexpected BasicOCSPResponse value"); + } + + DerValue responseData = seqTmp[0]; + + // Need the DER encoded ResponseData to verify the signature later + tbsResponseData = seqTmp[0].toByteArray(); + + // tbsResponseData + if (responseData.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding in tbsResponseData " + + "element of OCSP response: expected ASN.1 SEQUENCE tag."); + } + DerInputStream seqDerIn = responseData.data; + DerValue seq = seqDerIn.getDerValue(); + + // version + if (seq.isContextSpecific((byte)0)) { + // seq[0] is version + if (seq.isConstructed() && seq.isContextSpecific()) { + //System.out.println ("version is available"); + seq = seq.data.getDerValue(); + int version = seq.getInteger(); + if (seq.data.available() != 0) { + throw new IOException("Bad encoding in version " + + " element of OCSP response: bad format"); + } + seq = seqDerIn.getDerValue(); + } + } + + // responderID + short tag = (byte)(seq.tag & 0x1f); + if (tag == NAME_TAG) { + responderName = new X500Principal(seq.getData().toByteArray()); + if (debug != null) { + debug.println("Responder's name: " + responderName); + } + } else if (tag == KEY_TAG) { + responderKeyId = new KeyIdentifier(seq.getData().getOctetString()); + if (debug != null) { + debug.println("Responder's key ID: " + + Debug.toString(responderKeyId.getIdentifier())); + } + } else { + throw new IOException("Bad encoding in responderID element of " + + "OCSP response: expected ASN.1 context specific tag 0 or 1"); + } + + // producedAt + seq = seqDerIn.getDerValue(); + if (debug != null) { + Date producedAtDate = seq.getGeneralizedTime(); + debug.println("OCSP response produced at: " + producedAtDate); + } + + // responses + DerValue[] singleResponseDer = seqDerIn.getSequence(1); + singleResponseMap = new HashMap<>(singleResponseDer.length); + if (debug != null) { + debug.println("OCSP number of SingleResponses: " + + singleResponseDer.length); + } + for (int i = 0; i < singleResponseDer.length; i++) { + SingleResponse singleResponse = + new SingleResponse(singleResponseDer[i]); + singleResponseMap.put(singleResponse.getCertId(), singleResponse); + } + + // responseExtensions + byte[] nonce = null; + if (seqDerIn.available() > 0) { + seq = seqDerIn.getDerValue(); + if (seq.isContextSpecific((byte)1)) { + DerValue[] responseExtDer = seq.data.getSequence(3); + for (int i = 0; i < responseExtDer.length; i++) { + Extension ext = new Extension(responseExtDer[i]); + if (debug != null) { + debug.println("OCSP extension: " + ext); + } + // Only the NONCE extension is recognized + if (ext.getExtensionId().equals((Object) + OCSP.NONCE_EXTENSION_OID)) + { + nonce = ext.getExtensionValue(); + } else if (ext.isCritical()) { + throw new IOException( + "Unsupported OCSP critical extension: " + + ext.getExtensionId()); + } + } + } + } + responseNonce = nonce; + + // signatureAlgorithmId + sigAlgId = AlgorithmId.parse(seqTmp[1]); + + // signature + signature = seqTmp[2].getBitString(); + + // if seq[3] is available , then it is a sequence of certificates + if (seqTmp.length > 3) { + // certs are available + DerValue seqCert = seqTmp[3]; + if (!seqCert.isContextSpecific((byte)0)) { + throw new IOException("Bad encoding in certs element of " + + "OCSP response: expected ASN.1 context specific tag 0."); + } + DerValue[] derCerts = seqCert.getData().getSequence(3); + certs = new ArrayList(derCerts.length); + try { + for (int i = 0; i < derCerts.length; i++) { + X509CertImpl cert = + new X509CertImpl(derCerts[i].toByteArray()); + certs.add(cert); + + if (debug != null) { + debug.println("OCSP response cert #" + (i + 1) + ": " + + cert.getSubjectX500Principal()); + } + } + } catch (CertificateException ce) { + throw new IOException("Bad encoding in X509 Certificate", ce); + } + } else { + certs = new ArrayList(); + } + } + + void verify(List certIds, X509Certificate issuerCert, + X509Certificate responderCert, Date date, byte[] nonce) + throws CertPathValidatorException + { + switch (responseStatus) { + case SUCCESSFUL: + break; + case TRY_LATER: + case INTERNAL_ERROR: + throw new CertPathValidatorException( + "OCSP response error: " + responseStatus, null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); + case UNAUTHORIZED: + default: + throw new CertPathValidatorException("OCSP response error: " + + responseStatus); + } + + // Check that the response includes a response for all of the + // certs that were supplied in the request + for (CertId certId : certIds) { + SingleResponse sr = getSingleResponse(certId); + if (sr == null) { + if (debug != null) { + debug.println("No response found for CertId: " + certId); + } + throw new CertPathValidatorException( + "OCSP response does not include a response for a " + + "certificate supplied in the OCSP request"); + } + if (debug != null) { + debug.println("Status of certificate (with serial number " + + certId.getSerialNumber() + ") is: " + sr.getCertStatus()); + } + } + + // Locate the signer cert + if (signerCert == null) { + // Add the Issuing CA cert and/or Trusted Responder cert to the list + // of certs from the OCSP response + try { + certs.add(X509CertImpl.toImpl(issuerCert)); + if (responderCert != null) { + certs.add(X509CertImpl.toImpl(responderCert)); + } + } catch (CertificateException ce) { + throw new CertPathValidatorException( + "Invalid issuer or trusted responder certificate", ce); + } + + if (responderName != null) { + for (X509CertImpl cert : certs) { + if (cert.getSubjectX500Principal().equals(responderName)) { + signerCert = cert; + break; + } + } + } else if (responderKeyId != null) { + for (X509CertImpl cert : certs) { + // Match responder's key identifier against the cert's SKID + // This will match if the SKID is encoded using the 160-bit + // SHA-1 hash method as defined in RFC 5280. + KeyIdentifier certKeyId = cert.getSubjectKeyId(); + if (certKeyId != null && responderKeyId.equals(certKeyId)) { + signerCert = cert; + break; + } else { + // The certificate does not have a SKID or may have + // been using a different algorithm (ex: see RFC 7093). + // Check if the responder's key identifier matches + // against a newly generated key identifier of the + // cert's public key using the 160-bit SHA-1 method. + try { + certKeyId = new KeyIdentifier(cert.getPublicKey()); + } catch (IOException e) { + // ignore + } + if (responderKeyId.equals(certKeyId)) { + signerCert = cert; + break; + } + } + } + } + } + + // Check whether the signer cert returned by the responder is trusted + if (signerCert != null) { + // Check if the response is signed by the issuing CA + if (signerCert.equals(issuerCert)) { + if (debug != null) { + debug.println("OCSP response is signed by the target's " + + "Issuing CA"); + } + // cert is trusted, now verify the signed response + + // Check if the response is signed by a trusted responder + } else if (signerCert.equals(responderCert)) { + if (debug != null) { + debug.println("OCSP response is signed by a Trusted " + + "Responder"); + } + // cert is trusted, now verify the signed response + + // Check if the response is signed by an authorized responder + } else if (signerCert.getIssuerX500Principal().equals( + issuerCert.getSubjectX500Principal())) { + + // Check for the OCSPSigning key purpose + try { + List keyPurposes = signerCert.getExtendedKeyUsage(); + if (keyPurposes == null || + !keyPurposes.contains(KP_OCSP_SIGNING_OID)) { + throw new CertPathValidatorException( + "Responder's certificate not valid for signing " + + "OCSP responses"); + } + } catch (CertificateParsingException cpe) { + // assume cert is not valid for signing + throw new CertPathValidatorException( + "Responder's certificate not valid for signing " + + "OCSP responses", cpe); + } + + // Check algorithm constraints specified in security property + // "jdk.certpath.disabledAlgorithms". + AlgorithmChecker algChecker = new AlgorithmChecker( + new TrustAnchor(issuerCert, null)); + algChecker.init(false); + algChecker.check(signerCert, Collections.emptySet()); + + // check the validity + try { + if (date == null) { + signerCert.checkValidity(); + } else { + signerCert.checkValidity(date); + } + } catch (CertificateException e) { + throw new CertPathValidatorException( + "Responder's certificate not within the " + + "validity period", e); + } + + // check for revocation + // + // A CA may specify that an OCSP client can trust a + // responder for the lifetime of the responder's + // certificate. The CA does so by including the + // extension id-pkix-ocsp-nocheck. + // + Extension noCheck = + signerCert.getExtension(PKIXExtensions.OCSPNoCheck_Id); + if (noCheck != null) { + if (debug != null) { + debug.println("Responder's certificate includes " + + "the extension id-pkix-ocsp-nocheck."); + } + } else { + // we should do the revocation checking of the + // authorized responder in a future update. + } + + // verify the signature + try { + signerCert.verify(issuerCert.getPublicKey()); + if (debug != null) { + debug.println("OCSP response is signed by an " + + "Authorized Responder"); + } + // cert is trusted, now verify the signed response + + } catch (GeneralSecurityException e) { + signerCert = null; + } + } else { + throw new CertPathValidatorException( + "Responder's certificate is not authorized to sign " + + "OCSP responses"); + } + } + + // Confirm that the signed response was generated using the public + // key from the trusted responder cert + if (signerCert != null) { + // Check algorithm constraints specified in security property + // "jdk.certpath.disabledAlgorithms". + AlgorithmChecker.check(signerCert.getPublicKey(), sigAlgId); + + if (!verifySignature(signerCert)) { + throw new CertPathValidatorException( + "Error verifying OCSP Response's signature"); + } + } else { + // Need responder's cert in order to verify the signature + throw new CertPathValidatorException( + "Unable to verify OCSP Response's signature"); + } + + // Check freshness of OCSPResponse + if (nonce != null) { + if (responseNonce != null && !Arrays.equals(nonce, responseNonce)) { + throw new CertPathValidatorException("Nonces don't match"); + } + } + + long now = (date == null) ? System.currentTimeMillis() : date.getTime(); + Date nowPlusSkew = new Date(now + MAX_CLOCK_SKEW); + Date nowMinusSkew = new Date(now - MAX_CLOCK_SKEW); + for (SingleResponse sr : singleResponseMap.values()) { + if (debug != null) { + String until = ""; + if (sr.nextUpdate != null) { + until = " until " + sr.nextUpdate; + } + debug.println("Response's validity interval is from " + + sr.thisUpdate + until); + } + + // Check that the test date is within the validity interval + if ((sr.thisUpdate != null && nowPlusSkew.before(sr.thisUpdate)) || + (sr.nextUpdate != null && nowMinusSkew.after(sr.nextUpdate))) + { + throw new CertPathValidatorException( + "Response is unreliable: its validity " + + "interval is out-of-date"); + } + } + } + + /** + * Returns the OCSP ResponseStatus. + */ + ResponseStatus getResponseStatus() { + return responseStatus; + } + + /* + * Verify the signature of the OCSP response. + */ + private boolean verifySignature(X509Certificate cert) + throws CertPathValidatorException { + + try { + Signature respSignature = Signature.getInstance(sigAlgId.getName()); + respSignature.initVerify(cert.getPublicKey()); + respSignature.update(tbsResponseData); + + if (respSignature.verify(signature)) { + if (debug != null) { + debug.println("Verified signature of OCSP Response"); + } + return true; + + } else { + if (debug != null) { + debug.println( + "Error verifying signature of OCSP Response"); + } + return false; + } + } catch (InvalidKeyException | NoSuchAlgorithmException | + SignatureException e) + { + throw new CertPathValidatorException(e); + } + } + + /** + * Returns the SingleResponse of the specified CertId, or null if + * there is no response for that CertId. + */ + SingleResponse getSingleResponse(CertId certId) { + return singleResponseMap.get(certId); + } + + /* + * Returns the certificate for the authority that signed the OCSP response. + */ + X509Certificate getSignerCertificate() { + return signerCert; // set in verify() + } + + /* + * A class representing a single OCSP response. + */ + final static class SingleResponse implements OCSP.RevocationStatus { + private final CertId certId; + private final CertStatus certStatus; + private final Date thisUpdate; + private final Date nextUpdate; + private final Date revocationTime; + private final CRLReason revocationReason; + private final Map singleExtensions; + + private SingleResponse(DerValue der) throws IOException { + if (der.tag != DerValue.tag_Sequence) { + throw new IOException("Bad ASN.1 encoding in SingleResponse"); + } + DerInputStream tmp = der.data; + + certId = new CertId(tmp.getDerValue().data); + DerValue derVal = tmp.getDerValue(); + short tag = (byte)(derVal.tag & 0x1f); + if (tag == CERT_STATUS_REVOKED) { + certStatus = CertStatus.REVOKED; + revocationTime = derVal.data.getGeneralizedTime(); + if (derVal.data.available() != 0) { + DerValue dv = derVal.data.getDerValue(); + tag = (byte)(dv.tag & 0x1f); + if (tag == 0) { + int reason = dv.data.getEnumerated(); + // if reason out-of-range just leave as UNSPECIFIED + if (reason >= 0 && reason < values.length) { + revocationReason = values[reason]; + } else { + revocationReason = CRLReason.UNSPECIFIED; + } + } else { + revocationReason = CRLReason.UNSPECIFIED; + } + } else { + revocationReason = CRLReason.UNSPECIFIED; + } + // RevokedInfo + if (debug != null) { + debug.println("Revocation time: " + revocationTime); + debug.println("Revocation reason: " + revocationReason); + } + } else { + revocationTime = null; + revocationReason = CRLReason.UNSPECIFIED; + if (tag == CERT_STATUS_GOOD) { + certStatus = CertStatus.GOOD; + } else if (tag == CERT_STATUS_UNKNOWN) { + certStatus = CertStatus.UNKNOWN; + } else { + throw new IOException("Invalid certificate status"); + } + } + + thisUpdate = tmp.getGeneralizedTime(); + + if (tmp.available() == 0) { + // we are done + nextUpdate = null; + } else { + derVal = tmp.getDerValue(); + tag = (byte)(derVal.tag & 0x1f); + if (tag == 0) { + // next update + nextUpdate = derVal.data.getGeneralizedTime(); + + if (tmp.available() == 0) { + // we are done + } else { + derVal = tmp.getDerValue(); + tag = (byte)(derVal.tag & 0x1f); + } + } else { + nextUpdate = null; + } + } + // singleExtensions + if (tmp.available() > 0) { + derVal = tmp.getDerValue(); + if (derVal.isContextSpecific((byte)1)) { + DerValue[] singleExtDer = derVal.data.getSequence(3); + singleExtensions = + new HashMap + (singleExtDer.length); + for (int i = 0; i < singleExtDer.length; i++) { + Extension ext = new Extension(singleExtDer[i]); + if (debug != null) { + debug.println("OCSP single extension: " + ext); + } + // We don't support any extensions yet. Therefore, if it + // is critical we must throw an exception because we + // don't know how to process it. + if (ext.isCritical()) { + throw new IOException( + "Unsupported OCSP critical extension: " + + ext.getExtensionId()); + } + singleExtensions.put(ext.getId(), ext); + } + } else { + singleExtensions = Collections.emptyMap(); + } + } else { + singleExtensions = Collections.emptyMap(); + } + } + + /* + * Return the certificate's revocation status code + */ + @Override public CertStatus getCertStatus() { + return certStatus; + } + + private CertId getCertId() { + return certId; + } + + @Override public Date getRevocationTime() { + return (Date) revocationTime.clone(); + } + + @Override public CRLReason getRevocationReason() { + return revocationReason; + } + + @Override + public Map getSingleExtensions() { + return Collections.unmodifiableMap(singleExtensions); + } + + /** + * Construct a string representation of a single OCSP response. + */ + @Override public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SingleResponse: \n"); + sb.append(certId); + sb.append("\nCertStatus: "+ certStatus + "\n"); + if (certStatus == CertStatus.REVOKED) { + sb.append("revocationTime is " + revocationTime + "\n"); + sb.append("revocationReason is " + revocationReason + "\n"); + } + sb.append("thisUpdate is " + thisUpdate + "\n"); + if (nextUpdate != null) { + sb.append("nextUpdate is " + nextUpdate + "\n"); + } + return sb.toString(); + } + } +} diff --git a/src/sun/security/provider/certpath/PKIX.java b/src/sun/security/provider/certpath/PKIX.java new file mode 100644 index 00000000..dabfe5c0 --- /dev/null +++ b/src/sun/security/provider/certpath/PKIX.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.provider.certpath; + +import java.security.InvalidAlgorithmParameterException; +import java.security.PublicKey; +import java.security.cert.*; +import java.security.interfaces.DSAPublicKey; +import java.util.*; +import javax.security.auth.x500.X500Principal; + +import sun.security.util.Debug; + +/** + * Common utility methods and classes used by the PKIX CertPathValidator and + * CertPathBuilder implementation. + */ +class PKIX { + + private static final Debug debug = Debug.getInstance("certpath"); + + private PKIX() { } + + static boolean isDSAPublicKeyWithoutParams(PublicKey publicKey) { + return (publicKey instanceof DSAPublicKey && + ((DSAPublicKey)publicKey).getParams() == null); + } + + static ValidatorParams checkParams(CertPath cp, CertPathParameters params) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof PKIXParameters)) { + throw new InvalidAlgorithmParameterException("inappropriate " + + "params, must be an instance of PKIXParameters"); + } + return new ValidatorParams(cp, (PKIXParameters)params); + } + + static BuilderParams checkBuilderParams(CertPathParameters params) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof PKIXBuilderParameters)) { + throw new InvalidAlgorithmParameterException("inappropriate " + + "params, must be an instance of PKIXBuilderParameters"); + } + return new BuilderParams((PKIXBuilderParameters)params); + } + + /** + * PKIXParameters that are shared by the PKIX CertPathValidator + * implementation. Provides additional functionality and avoids + * unnecessary cloning. + */ + static class ValidatorParams { + private final PKIXParameters params; + private CertPath certPath; + private List checkers; + private List stores; + private boolean gotDate; + private Date date; + private Set policies; + private boolean gotConstraints; + private CertSelector constraints; + private Set anchors; + private List certs; + + ValidatorParams(CertPath cp, PKIXParameters params) + throws InvalidAlgorithmParameterException + { + this(params); + if (!cp.getType().equals("X.509") && !cp.getType().equals("X509")) { + throw new InvalidAlgorithmParameterException("inappropriate " + + "CertPath type specified, must be X.509 or X509"); + } + this.certPath = cp; + } + + ValidatorParams(PKIXParameters params) + throws InvalidAlgorithmParameterException + { + this.anchors = params.getTrustAnchors(); + // Make sure that none of the trust anchors include name constraints + // (not supported). + for (TrustAnchor anchor : this.anchors) { + if (anchor.getNameConstraints() != null) { + throw new InvalidAlgorithmParameterException + ("name constraints in trust anchor not supported"); + } + } + this.params = params; + } + + CertPath certPath() { + return certPath; + } + // called by CertPathBuilder after path has been built + void setCertPath(CertPath cp) { + this.certPath = cp; + } + List certificates() { + if (certs == null) { + if (certPath == null) { + certs = Collections.emptyList(); + } else { + // Reverse the ordering for validation so that the target + // cert is the last certificate + @SuppressWarnings("unchecked") + List xc = new ArrayList<> + ((List)certPath.getCertificates()); + Collections.reverse(xc); + certs = xc; + } + } + return certs; + } + List certPathCheckers() { + if (checkers == null) + checkers = params.getCertPathCheckers(); + return checkers; + } + List certStores() { + if (stores == null) + stores = params.getCertStores(); + return stores; + } + Date date() { + if (!gotDate) { + date = params.getDate(); + if (date == null) + date = new Date(); + gotDate = true; + } + return date; + } + Set initialPolicies() { + if (policies == null) + policies = params.getInitialPolicies(); + return policies; + } + CertSelector targetCertConstraints() { + if (!gotConstraints) { + constraints = params.getTargetCertConstraints(); + gotConstraints = true; + } + return constraints; + } + Set trustAnchors() { + return anchors; + } + boolean revocationEnabled() { + return params.isRevocationEnabled(); + } + boolean policyMappingInhibited() { + return params.isPolicyMappingInhibited(); + } + boolean explicitPolicyRequired() { + return params.isExplicitPolicyRequired(); + } + boolean policyQualifiersRejected() { + return params.getPolicyQualifiersRejected(); + } + String sigProvider() { return params.getSigProvider(); } + boolean anyPolicyInhibited() { return params.isAnyPolicyInhibited(); } + + // in rare cases we need access to the original params, for example + // in order to clone CertPathCheckers before building a new chain + PKIXParameters getPKIXParameters() { + return params; + } + } + + static class BuilderParams extends ValidatorParams { + private PKIXBuilderParameters params; + private boolean buildForward = true; + private List stores; + private X500Principal targetSubject; + + BuilderParams(PKIXBuilderParameters params) + throws InvalidAlgorithmParameterException + { + super(params); + checkParams(params); + } + private void checkParams(PKIXBuilderParameters params) + throws InvalidAlgorithmParameterException + { + CertSelector sel = targetCertConstraints(); + if (!(sel instanceof X509CertSelector)) { + throw new InvalidAlgorithmParameterException("the " + + "targetCertConstraints parameter must be an " + + "X509CertSelector"); + } + if (params instanceof SunCertPathBuilderParameters) { + buildForward = + ((SunCertPathBuilderParameters)params).getBuildForward(); + } + this.params = params; + this.targetSubject = getTargetSubject( + certStores(), (X509CertSelector)targetCertConstraints()); + } + @Override List certStores() { + if (stores == null) { + // reorder CertStores so that local CertStores are tried first + stores = new ArrayList<>(params.getCertStores()); + Collections.sort(stores, new CertStoreComparator()); + } + return stores; + } + int maxPathLength() { return params.getMaxPathLength(); } + boolean buildForward() { return buildForward; } + PKIXBuilderParameters params() { return params; } + X500Principal targetSubject() { return targetSubject; } + + /** + * Returns the target subject DN from the first X509Certificate that + * is fetched that matches the specified X509CertSelector. + */ + private static X500Principal getTargetSubject(List stores, + X509CertSelector sel) + throws InvalidAlgorithmParameterException + { + X500Principal subject = sel.getSubject(); + if (subject != null) { + return subject; + } + X509Certificate cert = sel.getCertificate(); + if (cert != null) { + subject = cert.getSubjectX500Principal(); + } + if (subject != null) { + return subject; + } + for (CertStore store : stores) { + try { + Collection certs = + (Collection) + store.getCertificates(sel); + if (!certs.isEmpty()) { + X509Certificate xc = + (X509Certificate)certs.iterator().next(); + return xc.getSubjectX500Principal(); + } + } catch (CertStoreException e) { + // ignore but log it + if (debug != null) { + debug.println("BuilderParams.getTargetSubjectDN: " + + "non-fatal exception retrieving certs: " + e); + e.printStackTrace(); + } + } + } + throw new InvalidAlgorithmParameterException + ("Could not determine unique target subject"); + } + } + + /** + * A CertStoreException with additional information about the type of + * CertStore that generated the exception. + */ + static class CertStoreTypeException extends CertStoreException { + private static final long serialVersionUID = 7463352639238322556L; + + private final String type; + + CertStoreTypeException(String type, CertStoreException cse) { + super(cse.getMessage(), cse.getCause()); + this.type = type; + } + String getType() { + return type; + } + } + + /** + * Comparator that orders CertStores so that local CertStores come before + * remote CertStores. + */ + private static class CertStoreComparator implements Comparator { + @Override + public int compare(CertStore store1, CertStore store2) { + if (store1.getType().equals("Collection") || + store1.getCertStoreParameters() instanceof + CollectionCertStoreParameters) { + return -1; + } else { + return 1; + } + } + } +} diff --git a/src/sun/security/provider/certpath/PKIXCertPathValidator.java b/src/sun/security/provider/certpath/PKIXCertPathValidator.java new file mode 100644 index 00000000..571801fc --- /dev/null +++ b/src/sun/security/provider/certpath/PKIXCertPathValidator.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.*; +import java.util.*; + +import sun.security.provider.certpath.PKIX.ValidatorParams; +import sun.security.x509.X509CertImpl; +import sun.security.util.Debug; + +/** + * This class implements the PKIX validation algorithm for certification + * paths consisting exclusively of X509Certificates. It uses + * the specified input parameter set (which must be a + * PKIXParameters object). + * + * @since 1.4 + * @author Yassir Elley + */ +public final class PKIXCertPathValidator extends CertPathValidatorSpi { + + private static final Debug debug = Debug.getInstance("certpath"); + + /** + * Default constructor. + */ + public PKIXCertPathValidator() {} + + @Override + public CertPathChecker engineGetRevocationChecker() { + return new RevocationChecker(); + } + + /** + * Validates a certification path consisting exclusively of + * X509Certificates using the PKIX validation algorithm, + * which uses the specified input parameter set. + * The input parameter set must be a PKIXParameters object. + * + * @param cp the X509 certification path + * @param params the input PKIX parameter set + * @return the result + * @throws CertPathValidatorException if cert path does not validate. + * @throws InvalidAlgorithmParameterException if the specified + * parameters are inappropriate for this CertPathValidator + */ + @Override + public CertPathValidatorResult engineValidate(CertPath cp, + CertPathParameters params) + throws CertPathValidatorException, InvalidAlgorithmParameterException + { + ValidatorParams valParams = PKIX.checkParams(cp, params); + return validate(valParams); + } + + private static PKIXCertPathValidatorResult validate(ValidatorParams params) + throws CertPathValidatorException + { + if (debug != null) + debug.println("PKIXCertPathValidator.engineValidate()..."); + + // Retrieve the first certificate in the certpath + // (to be used later in pre-screening) + AdaptableX509CertSelector selector = null; + List certList = params.certificates(); + if (!certList.isEmpty()) { + selector = new AdaptableX509CertSelector(); + X509Certificate firstCert = certList.get(0); + // check trusted certificate's subject + selector.setSubject(firstCert.getIssuerX500Principal()); + /* + * Facilitate certification path construction with authority + * key identifier and subject key identifier. + */ + try { + X509CertImpl firstCertImpl = X509CertImpl.toImpl(firstCert); + selector.setSkiAndSerialNumber( + firstCertImpl.getAuthorityKeyIdentifierExtension()); + } catch (CertificateException | IOException e) { + // ignore + } + } + + CertPathValidatorException lastException = null; + + // We iterate through the set of trust anchors until we find + // one that works at which time we stop iterating + for (TrustAnchor anchor : params.trustAnchors()) { + X509Certificate trustedCert = anchor.getTrustedCert(); + if (trustedCert != null) { + // if this trust anchor is not worth trying, + // we move on to the next one + if (selector != null && !selector.match(trustedCert)) { + if (debug != null) { + debug.println("NO - don't try this trustedCert"); + } + continue; + } + + if (debug != null) { + debug.println("YES - try this trustedCert"); + debug.println("anchor.getTrustedCert()." + + "getSubjectX500Principal() = " + + trustedCert.getSubjectX500Principal()); + } + } else { + if (debug != null) { + debug.println("PKIXCertPathValidator.engineValidate(): " + + "anchor.getTrustedCert() == null"); + } + } + + try { + return validate(anchor, params); + } catch (CertPathValidatorException cpe) { + // remember this exception + lastException = cpe; + } + } + + // could not find a trust anchor that verified + // (a) if we did a validation and it failed, use that exception + if (lastException != null) { + throw lastException; + } + // (b) otherwise, generate new exception + throw new CertPathValidatorException + ("Path does not chain with any of the trust anchors", + null, null, -1, PKIXReason.NO_TRUST_ANCHOR); + } + + private static PKIXCertPathValidatorResult validate(TrustAnchor anchor, + ValidatorParams params) + throws CertPathValidatorException + { + int certPathLen = params.certificates().size(); + + // create PKIXCertPathCheckers + List certPathCheckers = new ArrayList<>(); + // add standard checkers that we will be using + certPathCheckers.add(new UntrustedChecker()); + certPathCheckers.add(new AlgorithmChecker(anchor)); + certPathCheckers.add(new KeyChecker(certPathLen, + params.targetCertConstraints())); + certPathCheckers.add(new ConstraintsChecker(certPathLen)); + PolicyNodeImpl rootNode = + new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, false, + Collections.singleton(PolicyChecker.ANY_POLICY), + false); + PolicyChecker pc = new PolicyChecker(params.initialPolicies(), + certPathLen, + params.explicitPolicyRequired(), + params.policyMappingInhibited(), + params.anyPolicyInhibited(), + params.policyQualifiersRejected(), + rootNode); + certPathCheckers.add(pc); + // default value for date is current time + BasicChecker bc = new BasicChecker(anchor, params.date(), + params.sigProvider(), false); + certPathCheckers.add(bc); + + boolean revCheckerAdded = false; + List checkers = params.certPathCheckers(); + for (PKIXCertPathChecker checker : checkers) { + if (checker instanceof PKIXRevocationChecker) { + if (revCheckerAdded) { + throw new CertPathValidatorException( + "Only one PKIXRevocationChecker can be specified"); + } + revCheckerAdded = true; + // if it's our own, initialize it + if (checker instanceof RevocationChecker) { + ((RevocationChecker)checker).init(anchor, params); + } + } + } + // only add a RevocationChecker if revocation is enabled and + // a PKIXRevocationChecker has not already been added + if (params.revocationEnabled() && !revCheckerAdded) { + certPathCheckers.add(new RevocationChecker(anchor, params)); + } + // add user-specified checkers + certPathCheckers.addAll(checkers); + + PKIXMasterCertPathValidator.validate(params.certPath(), + params.certificates(), + certPathCheckers); + + return new PKIXCertPathValidatorResult(anchor, pc.getPolicyTree(), + bc.getPublicKey()); + } +} diff --git a/src/sun/security/provider/certpath/PKIXMasterCertPathValidator.java b/src/sun/security/provider/certpath/PKIXMasterCertPathValidator.java new file mode 100644 index 00000000..db5eddaf --- /dev/null +++ b/src/sun/security/provider/certpath/PKIXMasterCertPathValidator.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import sun.security.util.Debug; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; +import java.security.cert.X509Certificate; + +/** + * This class is initialized with a list of PKIXCertPathCheckers + * and is used to verify the certificates in a CertPath by + * feeding each certificate to each PKIXCertPathChecker. + * + * @since 1.4 + * @author Yassir Elley + */ +class PKIXMasterCertPathValidator { + + private static final Debug debug = Debug.getInstance("certpath"); + + /** + * Validates a certification path consisting exclusively of + * X509Certificates using the specified + * PKIXCertPathCheckers. It is assumed that the + * PKIXCertPathCheckers + * have been initialized with any input parameters they may need. + * + * @param cpOriginal the original X509 CertPath passed in by the user + * @param reversedCertList the reversed X509 CertPath (as a List) + * @param certPathCheckers the PKIXCertPathCheckers + * @throws CertPathValidatorException if cert path does not validate + */ + static void validate(CertPath cpOriginal, + List reversedCertList, + List certPathCheckers) + throws CertPathValidatorException + { + // we actually process reversedCertList, but we keep cpOriginal because + // we need to return the original certPath when we throw an exception. + // we will also need to modify the index appropriately when we + // throw an exception. + + int cpSize = reversedCertList.size(); + + if (debug != null) { + debug.println("--------------------------------------------------" + + "------------"); + debug.println("Executing PKIX certification path validation " + + "algorithm."); + } + + for (int i = 0; i < cpSize; i++) { + + /* The basic loop algorithm is that we get the + * current certificate, we verify the current certificate using + * information from the previous certificate and from the state, + * and we modify the state for the next loop by setting the + * current certificate of this loop to be the previous certificate + * of the next loop. The state is initialized during first loop. + */ + if (debug != null) + debug.println("Checking cert" + (i+1) + " ..."); + + X509Certificate currCert = reversedCertList.get(i); + Set unresCritExts = currCert.getCriticalExtensionOIDs(); + if (unresCritExts == null) { + unresCritExts = Collections.emptySet(); + } + + if (debug != null && !unresCritExts.isEmpty()) { + debug.println("Set of critical extensions:"); + for (String oid : unresCritExts) { + debug.println(oid); + } + } + + for (int j = 0; j < certPathCheckers.size(); j++) { + + PKIXCertPathChecker currChecker = certPathCheckers.get(j); + if (debug != null) { + debug.println("-Using checker" + (j + 1) + " ... [" + + currChecker.getClass().getName() + "]"); + } + + if (i == 0) + currChecker.init(false); + + try { + currChecker.check(currCert, unresCritExts); + + if (debug != null) { + debug.println("-checker" + (j + 1) + + " validation succeeded"); + } + + } catch (CertPathValidatorException cpve) { + throw new CertPathValidatorException(cpve.getMessage(), + cpve.getCause(), cpOriginal, cpSize - (i + 1), + cpve.getReason()); + } + } + + if (!unresCritExts.isEmpty()) { + throw new CertPathValidatorException("unrecognized " + + "critical extension(s)", null, cpOriginal, cpSize-(i+1), + PKIXReason.UNRECOGNIZED_CRIT_EXT); + } + + if (debug != null) + debug.println("\ncert" + (i+1) + " validation succeeded.\n"); + } + + if (debug != null) { + debug.println("Cert path validation succeeded. (PKIX validation " + + "algorithm)"); + debug.println("-------------------------------------------------" + + "-------------"); + } + } +} diff --git a/src/sun/security/provider/certpath/PolicyChecker.java b/src/sun/security/provider/certpath/PolicyChecker.java new file mode 100644 index 00000000..55086742 --- /dev/null +++ b/src/sun/security/provider/certpath/PolicyChecker.java @@ -0,0 +1,928 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; +import java.security.cert.PolicyNode; +import java.security.cert.PolicyQualifierInfo; +import java.security.cert.X509Certificate; +import java.util.*; + +import sun.security.util.Debug; +import sun.security.x509.CertificatePoliciesExtension; +import sun.security.x509.PolicyConstraintsExtension; +import sun.security.x509.PolicyMappingsExtension; +import sun.security.x509.CertificatePolicyMap; + +import static sun.security.x509.PKIXExtensions.CertificatePolicies_Id; +import static sun.security.x509.PKIXExtensions.InhibitAnyPolicy_Id; +import static sun.security.x509.PKIXExtensions.PolicyConstraints_Id; +import static sun.security.x509.PKIXExtensions.PolicyMappings_Id; + +import sun.security.x509.PolicyInformation; +import sun.security.x509.X509CertImpl; +import sun.security.x509.InhibitAnyPolicyExtension; + +/** + * PolicyChecker is a PKIXCertPathChecker that checks policy + * information on a PKIX certificate, namely certificate policies, policy + * mappings, policy constraints and policy qualifiers. + * + * @since 1.4 + * @author Yassir Elley + */ +class PolicyChecker extends PKIXCertPathChecker { + + private final Set initPolicies; + private final int certPathLen; + private final boolean expPolicyRequired; + private final boolean polMappingInhibited; + private final boolean anyPolicyInhibited; + private final boolean rejectPolicyQualifiers; + private PolicyNodeImpl rootNode; + private int explicitPolicy; + private int policyMapping; + private int inhibitAnyPolicy; + private int certIndex; + + private Set supportedExts; + + private static final Debug debug = Debug.getInstance("certpath"); + static final String ANY_POLICY = "2.5.29.32.0"; + + /** + * Constructs a Policy Checker. + * + * @param initialPolicies Set of initial policies + * @param certPathLen length of the certification path to be checked + * @param expPolicyRequired true if explicit policy is required + * @param polMappingInhibited true if policy mapping is inhibited + * @param anyPolicyInhibited true if the ANY_POLICY OID should be inhibited + * @param rejectPolicyQualifiers true if pol qualifiers are to be rejected + * @param rootNode the initial root node of the valid policy tree + */ + PolicyChecker(Set initialPolicies, int certPathLen, + boolean expPolicyRequired, boolean polMappingInhibited, + boolean anyPolicyInhibited, boolean rejectPolicyQualifiers, + PolicyNodeImpl rootNode) + { + if (initialPolicies.isEmpty()) { + // if no initialPolicies are specified by user, set + // initPolicies to be anyPolicy by default + this.initPolicies = new HashSet(1); + this.initPolicies.add(ANY_POLICY); + } else { + this.initPolicies = new HashSet(initialPolicies); + } + this.certPathLen = certPathLen; + this.expPolicyRequired = expPolicyRequired; + this.polMappingInhibited = polMappingInhibited; + this.anyPolicyInhibited = anyPolicyInhibited; + this.rejectPolicyQualifiers = rejectPolicyQualifiers; + this.rootNode = rootNode; + } + + /** + * Initializes the internal state of the checker from parameters + * specified in the constructor + * + * @param forward a boolean indicating whether this checker should be + * initialized capable of building in the forward direction + * @throws CertPathValidatorException if user wants to enable forward + * checking and forward checking is not supported. + */ + @Override + public void init(boolean forward) throws CertPathValidatorException { + if (forward) { + throw new CertPathValidatorException + ("forward checking not supported"); + } + + certIndex = 1; + explicitPolicy = (expPolicyRequired ? 0 : certPathLen + 1); + policyMapping = (polMappingInhibited ? 0 : certPathLen + 1); + inhibitAnyPolicy = (anyPolicyInhibited ? 0 : certPathLen + 1); + } + + /** + * Checks if forward checking is supported. Forward checking refers + * to the ability of the PKIXCertPathChecker to perform its checks + * when presented with certificates in the forward direction (from + * target to anchor). + * + * @return true if forward checking is supported, false otherwise + */ + @Override + public boolean isForwardCheckingSupported() { + return false; + } + + /** + * Gets an immutable Set of the OID strings for the extensions that + * the PKIXCertPathChecker supports (i.e. recognizes, is able to + * process), or null if no extensions are + * supported. All OID strings that a PKIXCertPathChecker might + * possibly be able to process should be included. + * + * @return the Set of extensions supported by this PKIXCertPathChecker, + * or null if no extensions are supported + */ + @Override + public Set getSupportedExtensions() { + if (supportedExts == null) { + supportedExts = new HashSet(4); + supportedExts.add(CertificatePolicies_Id.toString()); + supportedExts.add(PolicyMappings_Id.toString()); + supportedExts.add(PolicyConstraints_Id.toString()); + supportedExts.add(InhibitAnyPolicy_Id.toString()); + supportedExts = Collections.unmodifiableSet(supportedExts); + } + return supportedExts; + } + + /** + * Performs the policy processing checks on the certificate using its + * internal state. + * + * @param cert the Certificate to be processed + * @param unresCritExts the unresolved critical extensions + * @throws CertPathValidatorException if the certificate does not verify + */ + @Override + public void check(Certificate cert, Collection unresCritExts) + throws CertPathValidatorException + { + // now do the policy checks + checkPolicy((X509Certificate) cert); + + if (unresCritExts != null && !unresCritExts.isEmpty()) { + unresCritExts.remove(CertificatePolicies_Id.toString()); + unresCritExts.remove(PolicyMappings_Id.toString()); + unresCritExts.remove(PolicyConstraints_Id.toString()); + unresCritExts.remove(InhibitAnyPolicy_Id.toString()); + } + } + + /** + * Internal method to run through all the checks. + * + * @param currCert the certificate to be processed + * @exception CertPathValidatorException Exception thrown if + * the certificate does not verify + */ + private void checkPolicy(X509Certificate currCert) + throws CertPathValidatorException + { + String msg = "certificate policies"; + if (debug != null) { + debug.println("PolicyChecker.checkPolicy() ---checking " + msg + + "..."); + debug.println("PolicyChecker.checkPolicy() certIndex = " + + certIndex); + debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: " + + "explicitPolicy = " + explicitPolicy); + debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: " + + "policyMapping = " + policyMapping); + debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: " + + "inhibitAnyPolicy = " + inhibitAnyPolicy); + debug.println("PolicyChecker.checkPolicy() BEFORE PROCESSING: " + + "policyTree = " + rootNode); + } + + X509CertImpl currCertImpl = null; + try { + currCertImpl = X509CertImpl.toImpl(currCert); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + boolean finalCert = (certIndex == certPathLen); + + rootNode = processPolicies(certIndex, initPolicies, explicitPolicy, + policyMapping, inhibitAnyPolicy, rejectPolicyQualifiers, rootNode, + currCertImpl, finalCert); + + if (!finalCert) { + explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCertImpl, + finalCert); + policyMapping = mergePolicyMapping(policyMapping, currCertImpl); + inhibitAnyPolicy = mergeInhibitAnyPolicy(inhibitAnyPolicy, + currCertImpl); + } + + certIndex++; + + if (debug != null) { + debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: " + + "explicitPolicy = " + explicitPolicy); + debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: " + + "policyMapping = " + policyMapping); + debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: " + + "inhibitAnyPolicy = " + inhibitAnyPolicy); + debug.println("PolicyChecker.checkPolicy() AFTER PROCESSING: " + + "policyTree = " + rootNode); + debug.println("PolicyChecker.checkPolicy() " + msg + " verified"); + } + } + + /** + * Merges the specified explicitPolicy value with the + * requireExplicitPolicy field of the PolicyConstraints + * extension obtained from the certificate. An explicitPolicy + * value of -1 implies no constraint. + * + * @param explicitPolicy an integer which indicates if a non-null + * valid policy tree is required + * @param currCert the Certificate to be processed + * @param finalCert a boolean indicating whether currCert is + * the final cert in the cert path + * @return returns the new explicitPolicy value + * @exception CertPathValidatorException Exception thrown if an error + * occurs + */ + static int mergeExplicitPolicy(int explicitPolicy, X509CertImpl currCert, + boolean finalCert) throws CertPathValidatorException + { + if ((explicitPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) { + explicitPolicy--; + } + + try { + PolicyConstraintsExtension polConstExt + = currCert.getPolicyConstraintsExtension(); + if (polConstExt == null) + return explicitPolicy; + int require = + polConstExt.get(PolicyConstraintsExtension.REQUIRE).intValue(); + if (debug != null) { + debug.println("PolicyChecker.mergeExplicitPolicy() " + + "require Index from cert = " + require); + } + if (!finalCert) { + if (require != -1) { + if ((explicitPolicy == -1) || (require < explicitPolicy)) { + explicitPolicy = require; + } + } + } else { + if (require == 0) + explicitPolicy = require; + } + } catch (IOException e) { + if (debug != null) { + debug.println("PolicyChecker.mergeExplicitPolicy " + + "unexpected exception"); + e.printStackTrace(); + } + throw new CertPathValidatorException(e); + } + + return explicitPolicy; + } + + /** + * Merges the specified policyMapping value with the + * inhibitPolicyMapping field of the PolicyConstraints + * extension obtained from the certificate. A policyMapping + * value of -1 implies no constraint. + * + * @param policyMapping an integer which indicates if policy mapping + * is inhibited + * @param currCert the Certificate to be processed + * @return returns the new policyMapping value + * @exception CertPathValidatorException Exception thrown if an error + * occurs + */ + static int mergePolicyMapping(int policyMapping, X509CertImpl currCert) + throws CertPathValidatorException + { + if ((policyMapping > 0) && !X509CertImpl.isSelfIssued(currCert)) { + policyMapping--; + } + + try { + PolicyConstraintsExtension polConstExt + = currCert.getPolicyConstraintsExtension(); + if (polConstExt == null) + return policyMapping; + + int inhibit = + polConstExt.get(PolicyConstraintsExtension.INHIBIT).intValue(); + if (debug != null) + debug.println("PolicyChecker.mergePolicyMapping() " + + "inhibit Index from cert = " + inhibit); + + if (inhibit != -1) { + if ((policyMapping == -1) || (inhibit < policyMapping)) { + policyMapping = inhibit; + } + } + } catch (IOException e) { + if (debug != null) { + debug.println("PolicyChecker.mergePolicyMapping " + + "unexpected exception"); + e.printStackTrace(); + } + throw new CertPathValidatorException(e); + } + + return policyMapping; + } + + /** + * Merges the specified inhibitAnyPolicy value with the + * SkipCerts value of the InhibitAnyPolicy + * extension obtained from the certificate. + * + * @param inhibitAnyPolicy an integer which indicates whether + * "any-policy" is considered a match + * @param currCert the Certificate to be processed + * @return returns the new inhibitAnyPolicy value + * @exception CertPathValidatorException Exception thrown if an error + * occurs + */ + static int mergeInhibitAnyPolicy(int inhibitAnyPolicy, + X509CertImpl currCert) throws CertPathValidatorException + { + if ((inhibitAnyPolicy > 0) && !X509CertImpl.isSelfIssued(currCert)) { + inhibitAnyPolicy--; + } + + try { + InhibitAnyPolicyExtension inhAnyPolExt = (InhibitAnyPolicyExtension) + currCert.getExtension(InhibitAnyPolicy_Id); + if (inhAnyPolExt == null) + return inhibitAnyPolicy; + + int skipCerts = + inhAnyPolExt.get(InhibitAnyPolicyExtension.SKIP_CERTS).intValue(); + if (debug != null) + debug.println("PolicyChecker.mergeInhibitAnyPolicy() " + + "skipCerts Index from cert = " + skipCerts); + + if (skipCerts != -1) { + if (skipCerts < inhibitAnyPolicy) { + inhibitAnyPolicy = skipCerts; + } + } + } catch (IOException e) { + if (debug != null) { + debug.println("PolicyChecker.mergeInhibitAnyPolicy " + + "unexpected exception"); + e.printStackTrace(); + } + throw new CertPathValidatorException(e); + } + + return inhibitAnyPolicy; + } + + /** + * Processes certificate policies in the certificate. + * + * @param certIndex the index of the certificate + * @param initPolicies the initial policies required by the user + * @param explicitPolicy an integer which indicates if a non-null + * valid policy tree is required + * @param policyMapping an integer which indicates if policy + * mapping is inhibited + * @param inhibitAnyPolicy an integer which indicates whether + * "any-policy" is considered a match + * @param rejectPolicyQualifiers a boolean indicating whether the + * user wants to reject policies that have qualifiers + * @param origRootNode the root node of the valid policy tree + * @param currCert the Certificate to be processed + * @param finalCert a boolean indicating whether currCert is the final + * cert in the cert path + * @return the root node of the valid policy tree after modification + * @exception CertPathValidatorException Exception thrown if an + * error occurs while processing policies. + */ + static PolicyNodeImpl processPolicies(int certIndex, Set initPolicies, + int explicitPolicy, int policyMapping, int inhibitAnyPolicy, + boolean rejectPolicyQualifiers, PolicyNodeImpl origRootNode, + X509CertImpl currCert, boolean finalCert) + throws CertPathValidatorException + { + boolean policiesCritical = false; + List policyInfo; + PolicyNodeImpl rootNode = null; + Set anyQuals = new HashSet<>(); + + if (origRootNode == null) + rootNode = null; + else + rootNode = origRootNode.copyTree(); + + // retrieve policyOIDs from currCert + CertificatePoliciesExtension currCertPolicies + = currCert.getCertificatePoliciesExtension(); + + // PKIX: Section 6.1.3: Step (d) + if ((currCertPolicies != null) && (rootNode != null)) { + policiesCritical = currCertPolicies.isCritical(); + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "policiesCritical = " + policiesCritical); + + try { + policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES); + } catch (IOException ioe) { + throw new CertPathValidatorException("Exception while " + + "retrieving policyOIDs", ioe); + } + + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "rejectPolicyQualifiers = " + rejectPolicyQualifiers); + + boolean foundAnyPolicy = false; + + // process each policy in cert + for (PolicyInformation curPolInfo : policyInfo) { + String curPolicy = + curPolInfo.getPolicyIdentifier().getIdentifier().toString(); + + if (curPolicy.equals(ANY_POLICY)) { + foundAnyPolicy = true; + anyQuals = curPolInfo.getPolicyQualifiers(); + } else { + // PKIX: Section 6.1.3: Step (d)(1) + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "processing policy: " + curPolicy); + + // retrieve policy qualifiers from cert + Set pQuals = + curPolInfo.getPolicyQualifiers(); + + // reject cert if we find critical policy qualifiers and + // the policyQualifiersRejected flag is set in the params + if (!pQuals.isEmpty() && rejectPolicyQualifiers && + policiesCritical) { + throw new CertPathValidatorException( + "critical policy qualifiers present in certificate", + null, null, -1, PKIXReason.INVALID_POLICY); + } + + // PKIX: Section 6.1.3: Step (d)(1)(i) + boolean foundMatch = processParents(certIndex, + policiesCritical, rejectPolicyQualifiers, rootNode, + curPolicy, pQuals, false); + + if (!foundMatch) { + // PKIX: Section 6.1.3: Step (d)(1)(ii) + processParents(certIndex, policiesCritical, + rejectPolicyQualifiers, rootNode, curPolicy, + pQuals, true); + } + } + } + + // PKIX: Section 6.1.3: Step (d)(2) + if (foundAnyPolicy) { + if ((inhibitAnyPolicy > 0) || + (!finalCert && X509CertImpl.isSelfIssued(currCert))) { + if (debug != null) { + debug.println("PolicyChecker.processPolicies() " + + "processing policy: " + ANY_POLICY); + } + processParents(certIndex, policiesCritical, + rejectPolicyQualifiers, rootNode, ANY_POLICY, anyQuals, + true); + } + } + + // PKIX: Section 6.1.3: Step (d)(3) + rootNode.prune(certIndex); + if (!rootNode.getChildren().hasNext()) { + rootNode = null; + } + } else if (currCertPolicies == null) { + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "no policies present in cert"); + // PKIX: Section 6.1.3: Step (e) + rootNode = null; + } + + // We delay PKIX: Section 6.1.3: Step (f) to the end + // because the code that follows may delete some nodes + // resulting in a null tree + if (rootNode != null) { + if (!finalCert) { + // PKIX: Section 6.1.4: Steps (a)-(b) + rootNode = processPolicyMappings(currCert, certIndex, + policyMapping, rootNode, policiesCritical, anyQuals); + } + } + + // At this point, we optimize the PKIX algorithm by + // removing those nodes which would later have + // been removed by PKIX: Section 6.1.5: Step (g)(iii) + + if ((rootNode != null) && (!initPolicies.contains(ANY_POLICY)) + && (currCertPolicies != null)) { + rootNode = removeInvalidNodes(rootNode, certIndex, + initPolicies, currCertPolicies); + + // PKIX: Section 6.1.5: Step (g)(iii) + if ((rootNode != null) && finalCert) { + // rewrite anyPolicy leaf nodes (see method comments) + rootNode = rewriteLeafNodes(certIndex, initPolicies, rootNode); + } + } + + + if (finalCert) { + // PKIX: Section 6.1.5: Steps (a) and (b) + explicitPolicy = mergeExplicitPolicy(explicitPolicy, currCert, + finalCert); + } + + // PKIX: Section 6.1.3: Step (f) + // verify that either explicit policy is greater than 0 or + // the valid_policy_tree is not equal to NULL + + if ((explicitPolicy == 0) && (rootNode == null)) { + throw new CertPathValidatorException + ("non-null policy tree required and policy tree is null", + null, null, -1, PKIXReason.INVALID_POLICY); + } + + return rootNode; + } + + /** + * Rewrite leaf nodes at the end of validation as described in RFC 3280 + * section 6.1.5: Step (g)(iii). Leaf nodes with anyPolicy are replaced + * by nodes explicitly representing initial policies not already + * represented by leaf nodes. + * + * This method should only be called when processing the final cert + * and if the policy tree is not null and initial policies is not + * anyPolicy. + * + * @param certIndex the depth of the tree + * @param initPolicies Set of user specified initial policies + * @param rootNode the root of the policy tree + */ + private static PolicyNodeImpl rewriteLeafNodes(int certIndex, + Set initPolicies, PolicyNodeImpl rootNode) { + Set anyNodes = + rootNode.getPolicyNodesValid(certIndex, ANY_POLICY); + if (anyNodes.isEmpty()) { + return rootNode; + } + PolicyNodeImpl anyNode = anyNodes.iterator().next(); + PolicyNodeImpl parentNode = (PolicyNodeImpl)anyNode.getParent(); + parentNode.deleteChild(anyNode); + // see if there are any initialPolicies not represented by leaf nodes + Set initial = new HashSet<>(initPolicies); + for (PolicyNodeImpl node : rootNode.getPolicyNodes(certIndex)) { + initial.remove(node.getValidPolicy()); + } + if (initial.isEmpty()) { + // we deleted the anyPolicy node and have nothing to re-add, + // so we need to prune the tree + rootNode.prune(certIndex); + if (rootNode.getChildren().hasNext() == false) { + rootNode = null; + } + } else { + boolean anyCritical = anyNode.isCritical(); + Set anyQualifiers = + anyNode.getPolicyQualifiers(); + for (String policy : initial) { + Set expectedPolicies = Collections.singleton(policy); + PolicyNodeImpl node = new PolicyNodeImpl(parentNode, policy, + anyQualifiers, anyCritical, expectedPolicies, false); + } + } + return rootNode; + } + + /** + * Finds the policy nodes of depth (certIndex-1) where curPolicy + * is in the expected policy set and creates a new child node + * appropriately. If matchAny is true, then a value of ANY_POLICY + * in the expected policy set will match any curPolicy. If matchAny + * is false, then the expected policy set must exactly contain the + * curPolicy to be considered a match. This method returns a boolean + * value indicating whether a match was found. + * + * @param certIndex the index of the certificate whose policy is + * being processed + * @param policiesCritical a boolean indicating whether the certificate + * policies extension is critical + * @param rejectPolicyQualifiers a boolean indicating whether the + * user wants to reject policies that have qualifiers + * @param rootNode the root node of the valid policy tree + * @param curPolicy a String representing the policy being processed + * @param pQuals the policy qualifiers of the policy being processed or an + * empty Set if there are no qualifiers + * @param matchAny a boolean indicating whether a value of ANY_POLICY + * in the expected policy set will be considered a match + * @return a boolean indicating whether a match was found + * @exception CertPathValidatorException Exception thrown if error occurs. + */ + private static boolean processParents(int certIndex, + boolean policiesCritical, boolean rejectPolicyQualifiers, + PolicyNodeImpl rootNode, String curPolicy, + Set pQuals, + boolean matchAny) throws CertPathValidatorException + { + boolean foundMatch = false; + + if (debug != null) + debug.println("PolicyChecker.processParents(): matchAny = " + + matchAny); + + // find matching parents + Set parentNodes = + rootNode.getPolicyNodesExpected(certIndex - 1, + curPolicy, matchAny); + + // for each matching parent, extend policy tree + for (PolicyNodeImpl curParent : parentNodes) { + if (debug != null) + debug.println("PolicyChecker.processParents() " + + "found parent:\n" + curParent.asString()); + + foundMatch = true; + String curParPolicy = curParent.getValidPolicy(); + + PolicyNodeImpl curNode = null; + Set curExpPols = null; + + if (curPolicy.equals(ANY_POLICY)) { + // do step 2 + Set parExpPols = curParent.getExpectedPolicies(); + parentExplicitPolicies: + for (String curParExpPol : parExpPols) { + + Iterator childIter = + curParent.getChildren(); + while (childIter.hasNext()) { + PolicyNodeImpl childNode = childIter.next(); + String childPolicy = childNode.getValidPolicy(); + if (curParExpPol.equals(childPolicy)) { + if (debug != null) + debug.println(childPolicy + " in parent's " + + "expected policy set already appears in " + + "child node"); + continue parentExplicitPolicies; + } + } + + Set expPols = new HashSet<>(); + expPols.add(curParExpPol); + + curNode = new PolicyNodeImpl + (curParent, curParExpPol, pQuals, + policiesCritical, expPols, false); + } + } else { + curExpPols = new HashSet(); + curExpPols.add(curPolicy); + + curNode = new PolicyNodeImpl + (curParent, curPolicy, pQuals, + policiesCritical, curExpPols, false); + } + } + + return foundMatch; + } + + /** + * Processes policy mappings in the certificate. + * + * @param currCert the Certificate to be processed + * @param certIndex the index of the current certificate + * @param policyMapping an integer which indicates if policy + * mapping is inhibited + * @param rootNode the root node of the valid policy tree + * @param policiesCritical a boolean indicating if the certificate policies + * extension is critical + * @param anyQuals the qualifiers associated with ANY-POLICY, or an empty + * Set if there are no qualifiers associated with ANY-POLICY + * @return the root node of the valid policy tree after modification + * @exception CertPathValidatorException exception thrown if an error + * occurs while processing policy mappings + */ + private static PolicyNodeImpl processPolicyMappings(X509CertImpl currCert, + int certIndex, int policyMapping, PolicyNodeImpl rootNode, + boolean policiesCritical, Set anyQuals) + throws CertPathValidatorException + { + PolicyMappingsExtension polMappingsExt + = currCert.getPolicyMappingsExtension(); + + if (polMappingsExt == null) + return rootNode; + + if (debug != null) + debug.println("PolicyChecker.processPolicyMappings() " + + "inside policyMapping check"); + + List maps = null; + try { + maps = polMappingsExt.get(PolicyMappingsExtension.MAP); + } catch (IOException e) { + if (debug != null) { + debug.println("PolicyChecker.processPolicyMappings() " + + "mapping exception"); + e.printStackTrace(); + } + throw new CertPathValidatorException("Exception while checking " + + "mapping", e); + } + + boolean childDeleted = false; + for (CertificatePolicyMap polMap : maps) { + String issuerDomain + = polMap.getIssuerIdentifier().getIdentifier().toString(); + String subjectDomain + = polMap.getSubjectIdentifier().getIdentifier().toString(); + if (debug != null) { + debug.println("PolicyChecker.processPolicyMappings() " + + "issuerDomain = " + issuerDomain); + debug.println("PolicyChecker.processPolicyMappings() " + + "subjectDomain = " + subjectDomain); + } + + if (issuerDomain.equals(ANY_POLICY)) { + throw new CertPathValidatorException + ("encountered an issuerDomainPolicy of ANY_POLICY", + null, null, -1, PKIXReason.INVALID_POLICY); + } + + if (subjectDomain.equals(ANY_POLICY)) { + throw new CertPathValidatorException + ("encountered a subjectDomainPolicy of ANY_POLICY", + null, null, -1, PKIXReason.INVALID_POLICY); + } + + Set validNodes = + rootNode.getPolicyNodesValid(certIndex, issuerDomain); + if (!validNodes.isEmpty()) { + for (PolicyNodeImpl curNode : validNodes) { + if ((policyMapping > 0) || (policyMapping == -1)) { + curNode.addExpectedPolicy(subjectDomain); + } else if (policyMapping == 0) { + PolicyNodeImpl parentNode = + (PolicyNodeImpl) curNode.getParent(); + if (debug != null) + debug.println("PolicyChecker.processPolicyMappings" + + "() before deleting: policy tree = " + + rootNode); + parentNode.deleteChild(curNode); + childDeleted = true; + if (debug != null) + debug.println("PolicyChecker.processPolicyMappings" + + "() after deleting: policy tree = " + + rootNode); + } + } + } else { // no node of depth i has a valid policy + if ((policyMapping > 0) || (policyMapping == -1)) { + Set validAnyNodes = + rootNode.getPolicyNodesValid(certIndex, ANY_POLICY); + for (PolicyNodeImpl curAnyNode : validAnyNodes) { + PolicyNodeImpl curAnyNodeParent = + (PolicyNodeImpl) curAnyNode.getParent(); + + Set expPols = new HashSet<>(); + expPols.add(subjectDomain); + + PolicyNodeImpl curNode = new PolicyNodeImpl + (curAnyNodeParent, issuerDomain, anyQuals, + policiesCritical, expPols, true); + } + } + } + } + + if (childDeleted) { + rootNode.prune(certIndex); + if (!rootNode.getChildren().hasNext()) { + if (debug != null) + debug.println("setting rootNode to null"); + rootNode = null; + } + } + + return rootNode; + } + + /** + * Removes those nodes which do not intersect with the initial policies + * specified by the user. + * + * @param rootNode the root node of the valid policy tree + * @param certIndex the index of the certificate being processed + * @param initPolicies the Set of policies required by the user + * @param currCertPolicies the CertificatePoliciesExtension of the + * certificate being processed + * @returns the root node of the valid policy tree after modification + * @exception CertPathValidatorException Exception thrown if error occurs. + */ + private static PolicyNodeImpl removeInvalidNodes(PolicyNodeImpl rootNode, + int certIndex, Set initPolicies, + CertificatePoliciesExtension currCertPolicies) + throws CertPathValidatorException + { + List policyInfo = null; + try { + policyInfo = currCertPolicies.get(CertificatePoliciesExtension.POLICIES); + } catch (IOException ioe) { + throw new CertPathValidatorException("Exception while " + + "retrieving policyOIDs", ioe); + } + + boolean childDeleted = false; + for (PolicyInformation curPolInfo : policyInfo) { + String curPolicy = + curPolInfo.getPolicyIdentifier().getIdentifier().toString(); + + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "processing policy second time: " + curPolicy); + + Set validNodes = + rootNode.getPolicyNodesValid(certIndex, curPolicy); + for (PolicyNodeImpl curNode : validNodes) { + PolicyNodeImpl parentNode = (PolicyNodeImpl)curNode.getParent(); + if (parentNode.getValidPolicy().equals(ANY_POLICY)) { + if ((!initPolicies.contains(curPolicy)) && + (!curPolicy.equals(ANY_POLICY))) { + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "before deleting: policy tree = " + rootNode); + parentNode.deleteChild(curNode); + childDeleted = true; + if (debug != null) + debug.println("PolicyChecker.processPolicies() " + + "after deleting: policy tree = " + rootNode); + } + } + } + } + + if (childDeleted) { + rootNode.prune(certIndex); + if (!rootNode.getChildren().hasNext()) { + rootNode = null; + } + } + + return rootNode; + } + + /** + * Gets the root node of the valid policy tree, or null if the + * valid policy tree is null. Marks each node of the returned tree + * immutable and thread-safe. + * + * @returns the root node of the valid policy tree, or null if + * the valid policy tree is null + */ + PolicyNode getPolicyTree() { + if (rootNode == null) + return null; + else { + PolicyNodeImpl policyTree = rootNode.copyTree(); + policyTree.setImmutable(); + return policyTree; + } + } +} diff --git a/src/sun/security/provider/certpath/PolicyNodeImpl.java b/src/sun/security/provider/certpath/PolicyNodeImpl.java new file mode 100644 index 00000000..02109d49 --- /dev/null +++ b/src/sun/security/provider/certpath/PolicyNodeImpl.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import java.security.cert.*; + +/** + * Implements the PolicyNode interface. + *

    + * This class provides an implementation of the PolicyNode + * interface, and is used internally to build and search Policy Trees. + * While the implementation is mutable during construction, it is immutable + * before returning to a client and no mutable public or protected methods + * are exposed by this implementation, as per the contract of PolicyNode. + * + * @since 1.4 + * @author Seth Proctor + * @author Sean Mullan + */ +final class PolicyNodeImpl implements PolicyNode { + + /** + * Use to specify the special policy "Any Policy" + */ + private static final String ANY_POLICY = "2.5.29.32.0"; + + // every node has one parent, and zero or more children + private PolicyNodeImpl mParent; + private HashSet mChildren; + + // the 4 fields specified by RFC 3280 + private String mValidPolicy; + private HashSet mQualifierSet; + private boolean mCriticalityIndicator; + private HashSet mExpectedPolicySet; + private boolean mOriginalExpectedPolicySet; + + // the tree depth + private int mDepth; + // immutability flag + private boolean isImmutable = false; + + /** + * Constructor which takes a PolicyNodeImpl representing the + * parent in the Policy Tree to this node. If null, this is the + * root of the tree. The constructor also takes the associated data + * for this node, as found in the certificate. It also takes a boolean + * argument specifying whether this node is being created as a result + * of policy mapping. + * + * @param parent the PolicyNode above this in the tree, or null if this + * node is the tree's root node + * @param validPolicy a String representing this node's valid policy OID + * @param qualifierSet the Set of qualifiers for this policy + * @param criticalityIndicator a boolean representing whether or not the + * extension is critical + * @param expectedPolicySet a Set of expected policies + * @param generatedByPolicyMapping a boolean indicating whether this + * node was generated by a policy mapping + */ + PolicyNodeImpl(PolicyNodeImpl parent, String validPolicy, + Set qualifierSet, + boolean criticalityIndicator, Set expectedPolicySet, + boolean generatedByPolicyMapping) { + mParent = parent; + mChildren = new HashSet(); + + if (validPolicy != null) + mValidPolicy = validPolicy; + else + mValidPolicy = ""; + + if (qualifierSet != null) + mQualifierSet = new HashSet(qualifierSet); + else + mQualifierSet = new HashSet(); + + mCriticalityIndicator = criticalityIndicator; + + if (expectedPolicySet != null) + mExpectedPolicySet = new HashSet(expectedPolicySet); + else + mExpectedPolicySet = new HashSet(); + + mOriginalExpectedPolicySet = !generatedByPolicyMapping; + + // see if we're the root, and act appropriately + if (mParent != null) { + mDepth = mParent.getDepth() + 1; + mParent.addChild(this); + } else { + mDepth = 0; + } + } + + /** + * Alternate constructor which makes a new node with the policy data + * in an existing PolicyNodeImpl. + * + * @param parent a PolicyNode that's the new parent of the node, or + * null if this is the root node + * @param node a PolicyNode containing the policy data to copy + */ + PolicyNodeImpl(PolicyNodeImpl parent, PolicyNodeImpl node) { + this(parent, node.mValidPolicy, node.mQualifierSet, + node.mCriticalityIndicator, node.mExpectedPolicySet, false); + } + + @Override + public PolicyNode getParent() { + return mParent; + } + + @Override + public Iterator getChildren() { + return Collections.unmodifiableSet(mChildren).iterator(); + } + + @Override + public int getDepth() { + return mDepth; + } + + @Override + public String getValidPolicy() { + return mValidPolicy; + } + + @Override + public Set getPolicyQualifiers() { + return Collections.unmodifiableSet(mQualifierSet); + } + + @Override + public Set getExpectedPolicies() { + return Collections.unmodifiableSet(mExpectedPolicySet); + } + + @Override + public boolean isCritical() { + return mCriticalityIndicator; + } + + /** + * Return a printable representation of the PolicyNode. + * Starting at the node on which this method is called, + * it recurses through the tree and prints out each node. + * + * @return a String describing the contents of the Policy Node + */ + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(this.asString()); + + for (PolicyNodeImpl node : mChildren) { + buffer.append(node); + } + return buffer.toString(); + } + + // private methods and package private operations + + boolean isImmutable() { + return isImmutable; + } + + /** + * Sets the immutability flag of this node and all of its children + * to true. + */ + void setImmutable() { + if (isImmutable) + return; + for (PolicyNodeImpl node : mChildren) { + node.setImmutable(); + } + isImmutable = true; + } + + /** + * Private method sets a child node. This is called from the child's + * constructor. + * + * @param child new PolicyNodeImpl child node + */ + private void addChild(PolicyNodeImpl child) { + if (isImmutable) { + throw new IllegalStateException("PolicyNode is immutable"); + } + mChildren.add(child); + } + + /** + * Adds an expectedPolicy to the expected policy set. + * If this is the original expected policy set initialized + * by the constructor, then the expected policy set is cleared + * before the expected policy is added. + * + * @param expectedPolicy a String representing an expected policy. + */ + void addExpectedPolicy(String expectedPolicy) { + if (isImmutable) { + throw new IllegalStateException("PolicyNode is immutable"); + } + if (mOriginalExpectedPolicySet) { + mExpectedPolicySet.clear(); + mOriginalExpectedPolicySet = false; + } + mExpectedPolicySet.add(expectedPolicy); + } + + /** + * Removes all paths which don't reach the specified depth. + * + * @param depth an int representing the desired minimum depth of all paths + */ + void prune(int depth) { + if (isImmutable) + throw new IllegalStateException("PolicyNode is immutable"); + + // if we have no children, we can't prune below us... + if (mChildren.size() == 0) + return; + + Iterator it = mChildren.iterator(); + while (it.hasNext()) { + PolicyNodeImpl node = it.next(); + node.prune(depth); + // now that we've called prune on the child, see if we should + // remove it from the tree + if ((node.mChildren.size() == 0) && (depth > mDepth + 1)) + it.remove(); + } + } + + /** + * Deletes the specified child node of this node, if it exists. + * + * @param childNode the child node to be deleted + */ + void deleteChild(PolicyNode childNode) { + if (isImmutable) { + throw new IllegalStateException("PolicyNode is immutable"); + } + mChildren.remove(childNode); + } + + /** + * Returns a copy of the tree, without copying the policy-related data, + * rooted at the node on which this was called. + * + * @return a copy of the tree + */ + PolicyNodeImpl copyTree() { + return copyTree(null); + } + + private PolicyNodeImpl copyTree(PolicyNodeImpl parent) { + PolicyNodeImpl newNode = new PolicyNodeImpl(parent, this); + + for (PolicyNodeImpl node : mChildren) { + node.copyTree(newNode); + } + + return newNode; + } + + /** + * Returns all nodes at the specified depth in the tree. + * + * @param depth an int representing the depth of the desired nodes + * @return a Set of all nodes at the specified depth + */ + Set getPolicyNodes(int depth) { + Set set = new HashSet<>(); + getPolicyNodes(depth, set); + return set; + } + + /** + * Add all nodes at depth depth to set and return the Set. + * Internal recursion helper. + */ + private void getPolicyNodes(int depth, Set set) { + // if we've reached the desired depth, then return ourself + if (mDepth == depth) { + set.add(this); + } else { + for (PolicyNodeImpl node : mChildren) { + node.getPolicyNodes(depth, set); + } + } + } + + /** + * Finds all nodes at the specified depth whose expected_policy_set + * contains the specified expected OID (if matchAny is false) + * or the special OID "any value" (if matchAny is true). + * + * @param depth an int representing the desired depth + * @param expectedOID a String encoding the valid OID to match + * @param matchAny a boolean indicating whether an expected_policy_set + * containing ANY_POLICY should be considered a match + * @return a Set of matched PolicyNodes + */ + Set getPolicyNodesExpected(int depth, + String expectedOID, boolean matchAny) { + + if (expectedOID.equals(ANY_POLICY)) { + return getPolicyNodes(depth); + } else { + return getPolicyNodesExpectedHelper(depth, expectedOID, matchAny); + } + } + + private Set getPolicyNodesExpectedHelper(int depth, + String expectedOID, boolean matchAny) { + + HashSet set = new HashSet<>(); + + if (mDepth < depth) { + for (PolicyNodeImpl node : mChildren) { + set.addAll(node.getPolicyNodesExpectedHelper(depth, + expectedOID, + matchAny)); + } + } else { + if (matchAny) { + if (mExpectedPolicySet.contains(ANY_POLICY)) + set.add(this); + } else { + if (mExpectedPolicySet.contains(expectedOID)) + set.add(this); + } + } + + return set; + } + + /** + * Finds all nodes at the specified depth that contains the + * specified valid OID + * + * @param depth an int representing the desired depth + * @param validOID a String encoding the valid OID to match + * @return a Set of matched PolicyNodes + */ + Set getPolicyNodesValid(int depth, String validOID) { + HashSet set = new HashSet<>(); + + if (mDepth < depth) { + for (PolicyNodeImpl node : mChildren) { + set.addAll(node.getPolicyNodesValid(depth, validOID)); + } + } else { + if (mValidPolicy.equals(validOID)) + set.add(this); + } + + return set; + } + + private static String policyToString(String oid) { + if (oid.equals(ANY_POLICY)) { + return "anyPolicy"; + } else { + return oid; + } + } + + /** + * Prints out some data on this node. + */ + String asString() { + if (mParent == null) { + return "anyPolicy ROOT\n"; + } else { + StringBuilder sb = new StringBuilder(); + for (int i = 0, n = getDepth(); i < n; i++) { + sb.append(" "); + } + sb.append(policyToString(getValidPolicy())); + sb.append(" CRIT: "); + sb.append(isCritical()); + sb.append(" EP: "); + for (String policy : getExpectedPolicies()) { + sb.append(policyToString(policy)); + sb.append(" "); + } + sb.append(" ("); + sb.append(getDepth()); + sb.append(")\n"); + return sb.toString(); + } + } +} diff --git a/src/sun/security/provider/certpath/ReverseBuilder.java b/src/sun/security/provider/certpath/ReverseBuilder.java new file mode 100644 index 00000000..d9e61433 --- /dev/null +++ b/src/sun/security/provider/certpath/ReverseBuilder.java @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXReason; +import java.security.cert.X509Certificate; +import java.security.cert.X509CertSelector; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.LinkedList; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.PKIX.BuilderParams; +import sun.security.util.Debug; + +import static sun.security.x509.PKIXExtensions.*; +import sun.security.x509.X500Name; +import sun.security.x509.X509CertImpl; +import sun.security.x509.PolicyMappingsExtension; + +/** + * This class represents a reverse builder, which is able to retrieve + * matching certificates from CertStores and verify a particular certificate + * against a ReverseState. + * + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ + +class ReverseBuilder extends Builder { + + private Debug debug = Debug.getInstance("certpath"); + + private final Set initPolicies; + + /** + * Initialize the builder with the input parameters. + * + * @param params the parameter set used to build a certification path + */ + ReverseBuilder(BuilderParams buildParams) { + super(buildParams); + + Set initialPolicies = buildParams.initialPolicies(); + initPolicies = new HashSet(); + if (initialPolicies.isEmpty()) { + // if no initialPolicies are specified by user, set + // initPolicies to be anyPolicy by default + initPolicies.add(PolicyChecker.ANY_POLICY); + } else { + initPolicies.addAll(initialPolicies); + } + } + + /** + * Retrieves all certs from the specified CertStores that satisfy the + * requirements specified in the parameters and the current + * PKIX state (name constraints, policy constraints, etc). + * + * @param currentState the current state. + * Must be an instance of ReverseState + * @param certStores list of CertStores + */ + @Override + Collection getMatchingCerts + (State currState, List certStores) + throws CertStoreException, CertificateException, IOException + { + ReverseState currentState = (ReverseState) currState; + + if (debug != null) + debug.println("In ReverseBuilder.getMatchingCerts."); + + /* + * The last certificate could be an EE or a CA certificate + * (we may be building a partial certification path or + * establishing trust in a CA). + * + * Try the EE certs before the CA certs. It will be more + * common to build a path to an end entity. + */ + Collection certs = + getMatchingEECerts(currentState, certStores); + certs.addAll(getMatchingCACerts(currentState, certStores)); + + return certs; + } + + /* + * Retrieves all end-entity certificates which satisfy constraints + * and requirements specified in the parameters and PKIX state. + */ + private Collection getMatchingEECerts + (ReverseState currentState, List certStores) + throws CertStoreException, CertificateException, IOException { + + /* + * Compose a CertSelector to filter out + * certs which do not satisfy requirements. + * + * First, retrieve clone of current target cert constraints, and + * then add more selection criteria based on current validation state. + */ + X509CertSelector sel = (X509CertSelector) targetCertConstraints.clone(); + + /* + * Match on issuer (subject of previous cert) + */ + sel.setIssuer(currentState.subjectDN); + + /* + * Match on certificate validity date. + */ + sel.setCertificateValid(buildParams.date()); + + /* + * Policy processing optimizations + */ + if (currentState.explicitPolicy == 0) + sel.setPolicy(getMatchingPolicies()); + + /* + * If previous cert has a subject key identifier extension, + * use it to match on authority key identifier extension. + */ + /*if (currentState.subjKeyId != null) { + AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension( + (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID), + null, null); + sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue()); + }*/ + + /* + * Require EE certs + */ + sel.setBasicConstraints(-2); + + /* Retrieve matching certs from CertStores */ + HashSet eeCerts = new HashSet<>(); + addMatchingCerts(sel, certStores, eeCerts, true); + + if (debug != null) { + debug.println("ReverseBuilder.getMatchingEECerts got " + + eeCerts.size() + " certs."); + } + return eeCerts; + } + + /* + * Retrieves all CA certificates which satisfy constraints + * and requirements specified in the parameters and PKIX state. + */ + private Collection getMatchingCACerts + (ReverseState currentState, List certStores) + throws CertificateException, CertStoreException, IOException { + + /* + * Compose a CertSelector to filter out + * certs which do not satisfy requirements. + */ + X509CertSelector sel = new X509CertSelector(); + + /* + * Match on issuer (subject of previous cert) + */ + sel.setIssuer(currentState.subjectDN); + + /* + * Match on certificate validity date. + */ + sel.setCertificateValid(buildParams.date()); + + /* + * Match on target subject name (checks that current cert's + * name constraints permit it to certify target). + * (4 is the integer type for DIRECTORY name). + */ + byte[] subject = targetCertConstraints.getSubjectAsBytes(); + if (subject != null) { + sel.addPathToName(4, subject); + } else { + X509Certificate cert = targetCertConstraints.getCertificate(); + if (cert != null) { + sel.addPathToName(4, + cert.getSubjectX500Principal().getEncoded()); + } + } + + /* + * Policy processing optimizations + */ + if (currentState.explicitPolicy == 0) + sel.setPolicy(getMatchingPolicies()); + + /* + * If previous cert has a subject key identifier extension, + * use it to match on authority key identifier extension. + */ + /*if (currentState.subjKeyId != null) { + AuthorityKeyIdentifierExtension authKeyId = new AuthorityKeyIdentifierExtension( + (KeyIdentifier) currentState.subjKeyId.get(SubjectKeyIdentifierExtension.KEY_ID), + null, null); + sel.setAuthorityKeyIdentifier(authKeyId.getExtensionValue()); + }*/ + + /* + * Require CA certs + */ + sel.setBasicConstraints(0); + + /* Retrieve matching certs from CertStores */ + ArrayList reverseCerts = new ArrayList<>(); + addMatchingCerts(sel, certStores, reverseCerts, true); + + /* Sort remaining certs using name constraints */ + Collections.sort(reverseCerts, new PKIXCertComparator()); + + if (debug != null) + debug.println("ReverseBuilder.getMatchingCACerts got " + + reverseCerts.size() + " certs."); + return reverseCerts; + } + + /* + * This inner class compares 2 PKIX certificates according to which + * should be tried first when building a path to the target. For + * now, the algorithm is to look at name constraints in each cert and those + * which constrain the path closer to the target should be + * ranked higher. Later, we may want to consider other components, + * such as key identifiers. + */ + class PKIXCertComparator implements Comparator { + + private Debug debug = Debug.getInstance("certpath"); + + @Override + public int compare(X509Certificate cert1, X509Certificate cert2) { + + /* + * if either cert certifies the target, always + * put at head of list. + */ + X500Principal targetSubject = buildParams.targetSubject(); + if (cert1.getSubjectX500Principal().equals(targetSubject)) { + return -1; + } + if (cert2.getSubjectX500Principal().equals(targetSubject)) { + return 1; + } + + int targetDist1; + int targetDist2; + try { + X500Name targetSubjectName = X500Name.asX500Name(targetSubject); + targetDist1 = Builder.targetDistance( + null, cert1, targetSubjectName); + targetDist2 = Builder.targetDistance( + null, cert2, targetSubjectName); + } catch (IOException e) { + if (debug != null) { + debug.println("IOException in call to Builder.targetDistance"); + e.printStackTrace(); + } + throw new ClassCastException + ("Invalid target subject distinguished name"); + } + + if (targetDist1 == targetDist2) + return 0; + + if (targetDist1 == -1) + return 1; + + if (targetDist1 < targetDist2) + return -1; + + return 1; + } + } + + /** + * Verifies a matching certificate. + * + * This method executes any of the validation steps in the PKIX path validation + * algorithm which were not satisfied via filtering out non-compliant + * certificates with certificate matching rules. + * + * If the last certificate is being verified (the one whose subject + * matches the target subject, then the steps in Section 6.1.4 of the + * Certification Path Validation algorithm are NOT executed, + * regardless of whether or not the last cert is an end-entity + * cert or not. This allows callers to certify CA certs as + * well as EE certs. + * + * @param cert the certificate to be verified + * @param currentState the current state against which the cert is verified + * @param certPathList the certPathList generated thus far + */ + @Override + void verifyCert(X509Certificate cert, State currState, + List certPathList) + throws GeneralSecurityException + { + if (debug != null) { + debug.println("ReverseBuilder.verifyCert(SN: " + + Debug.toHexString(cert.getSerialNumber()) + + "\n Subject: " + cert.getSubjectX500Principal() + ")"); + } + + ReverseState currentState = (ReverseState) currState; + + /* we don't perform any validation of the trusted cert */ + if (currentState.isInitial()) { + return; + } + + // Don't bother to verify untrusted certificate more. + currentState.untrustedChecker.check(cert, + Collections.emptySet()); + + /* + * check for looping - abort a loop if + * ((we encounter the same certificate twice) AND + * ((policyMappingInhibited = true) OR (no policy mapping + * extensions can be found between the occurrences of the same + * certificate))) + * in order to facilitate the check to see if there are + * any policy mapping extensions found between the occurrences + * of the same certificate, we reverse the certpathlist first + */ + if ((certPathList != null) && (!certPathList.isEmpty())) { + List reverseCertList = new ArrayList<>(); + for (X509Certificate c : certPathList) { + reverseCertList.add(0, c); + } + + boolean policyMappingFound = false; + for (X509Certificate cpListCert : reverseCertList) { + X509CertImpl cpListCertImpl = X509CertImpl.toImpl(cpListCert); + PolicyMappingsExtension policyMappingsExt = + cpListCertImpl.getPolicyMappingsExtension(); + if (policyMappingsExt != null) { + policyMappingFound = true; + } + if (debug != null) + debug.println("policyMappingFound = " + policyMappingFound); + if (cert.equals(cpListCert)) { + if ((buildParams.policyMappingInhibited()) || + (!policyMappingFound)){ + if (debug != null) + debug.println("loop detected!!"); + throw new CertPathValidatorException("loop detected"); + } + } + } + } + + /* check if target cert */ + boolean finalCert = cert.getSubjectX500Principal().equals(buildParams.targetSubject()); + + /* check if CA cert */ + boolean caCert = (cert.getBasicConstraints() != -1 ? true : false); + + /* if there are more certs to follow, verify certain constraints */ + if (!finalCert) { + + /* check if CA cert */ + if (!caCert) + throw new CertPathValidatorException("cert is NOT a CA cert"); + + /* If the certificate was not self-issued, verify that + * remainingCerts is greater than zero + */ + if ((currentState.remainingCACerts <= 0) && !X509CertImpl.isSelfIssued(cert)) { + throw new CertPathValidatorException + ("pathLenConstraint violated, path too long", null, + null, -1, PKIXReason.PATH_TOO_LONG); + } + + /* + * Check keyUsage extension (only if CA cert and not final cert) + */ + KeyChecker.verifyCAKeyUsage(cert); + + } else { + + /* + * If final cert, check that it satisfies specified target + * constraints + */ + if (targetCertConstraints.match(cert) == false) { + throw new CertPathValidatorException("target certificate " + + "constraints check failed"); + } + } + + /* + * Check revocation. + */ + if (buildParams.revocationEnabled() && currentState.revChecker != null) { + currentState.revChecker.check(cert, Collections.emptySet()); + } + + /* Check name constraints if this is not a self-issued cert */ + if (finalCert || !X509CertImpl.isSelfIssued(cert)){ + if (currentState.nc != null) { + try { + if (!currentState.nc.verify(cert)){ + throw new CertPathValidatorException + ("name constraints check failed", null, null, -1, + PKIXReason.INVALID_NAME); + } + } catch (IOException ioe) { + throw new CertPathValidatorException(ioe); + } + } + } + + /* + * Check policy + */ + X509CertImpl certImpl = X509CertImpl.toImpl(cert); + currentState.rootNode = PolicyChecker.processPolicies + (currentState.certIndex, initPolicies, + currentState.explicitPolicy, currentState.policyMapping, + currentState.inhibitAnyPolicy, + buildParams.policyQualifiersRejected(), currentState.rootNode, + certImpl, finalCert); + + /* + * Check CRITICAL private extensions + */ + Set unresolvedCritExts = cert.getCriticalExtensionOIDs(); + if (unresolvedCritExts == null) { + unresolvedCritExts = Collections.emptySet(); + } + + /* + * Check that the signature algorithm is not disabled. + */ + currentState.algorithmChecker.check(cert, unresolvedCritExts); + + for (PKIXCertPathChecker checker : currentState.userCheckers) { + checker.check(cert, unresolvedCritExts); + } + + /* + * Look at the remaining extensions and remove any ones we have + * already checked. If there are any left, throw an exception! + */ + if (!unresolvedCritExts.isEmpty()) { + unresolvedCritExts.remove(BasicConstraints_Id.toString()); + unresolvedCritExts.remove(NameConstraints_Id.toString()); + unresolvedCritExts.remove(CertificatePolicies_Id.toString()); + unresolvedCritExts.remove(PolicyMappings_Id.toString()); + unresolvedCritExts.remove(PolicyConstraints_Id.toString()); + unresolvedCritExts.remove(InhibitAnyPolicy_Id.toString()); + unresolvedCritExts.remove(SubjectAlternativeName_Id.toString()); + unresolvedCritExts.remove(KeyUsage_Id.toString()); + unresolvedCritExts.remove(ExtendedKeyUsage_Id.toString()); + + if (!unresolvedCritExts.isEmpty()) + throw new CertPathValidatorException + ("Unrecognized critical extension(s)", null, null, -1, + PKIXReason.UNRECOGNIZED_CRIT_EXT); + } + + /* + * Check signature. + */ + if (buildParams.sigProvider() != null) { + cert.verify(currentState.pubKey, buildParams.sigProvider()); + } else { + cert.verify(currentState.pubKey); + } + } + + /** + * Verifies whether the input certificate completes the path. + * This checks whether the cert is the target certificate. + * + * @param cert the certificate to test + * @return a boolean value indicating whether the cert completes the path. + */ + @Override + boolean isPathCompleted(X509Certificate cert) { + return cert.getSubjectX500Principal().equals(buildParams.targetSubject()); + } + + /** Adds the certificate to the certPathList + * + * @param cert the certificate to be added + * @param certPathList the certification path list + */ + @Override + void addCertToPath(X509Certificate cert, + LinkedList certPathList) { + certPathList.addLast(cert); + } + + /** Removes final certificate from the certPathList + * + * @param certPathList the certification path list + */ + @Override + void removeFinalCertFromPath(LinkedList certPathList) { + certPathList.removeLast(); + } +} diff --git a/src/sun/security/provider/certpath/ReverseState.java b/src/sun/security/provider/certpath/ReverseState.java new file mode 100644 index 00000000..4a613761 --- /dev/null +++ b/src/sun/security/provider/certpath/ReverseState.java @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.PKIXRevocationChecker; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.ListIterator; +import java.util.Set; +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.PKIX.BuilderParams; +import sun.security.util.Debug; +import sun.security.x509.NameConstraintsExtension; +import sun.security.x509.SubjectKeyIdentifierExtension; +import sun.security.x509.X509CertImpl; + +/** + * A specification of a reverse PKIX validation state + * which is initialized by each build and updated each time a + * certificate is added to the current path. + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ + +class ReverseState implements State { + + private static final Debug debug = Debug.getInstance("certpath"); + + /* The subject DN of the last cert in the path */ + X500Principal subjectDN; + + /* The subject public key of the last cert */ + PublicKey pubKey; + + /* The subject key identifier extension (if any) of the last cert */ + SubjectKeyIdentifierExtension subjKeyId; + + /* The PKIX constrained/excluded subtrees state variable */ + NameConstraintsExtension nc; + + /* The PKIX explicit policy, policy mapping, and inhibit_any-policy + state variables */ + int explicitPolicy; + int policyMapping; + int inhibitAnyPolicy; + int certIndex; + PolicyNodeImpl rootNode; + + /* The number of remaining CA certs which may follow in the path. + * -1: previous cert was an EE cert + * 0: only EE certs may follow. + * >0 and userCheckers; + + /* Flag indicating if state is initial (path is just starting) */ + private boolean init = true; + + /* the checker used for revocation status */ + RevocationChecker revChecker; + + /* the algorithm checker */ + AlgorithmChecker algorithmChecker; + + /* the untrusted certificates checker */ + UntrustedChecker untrustedChecker; + + /* the trust anchor used to validate the path */ + TrustAnchor trustAnchor; + + /* Flag indicating if current cert can vouch for the CRL for + * the next cert + */ + boolean crlSign = true; + + /** + * Returns a boolean flag indicating if the state is initial + * (just starting) + * + * @return boolean flag indicating if the state is initial (just starting) + */ + @Override + public boolean isInitial() { + return init; + } + + /** + * Display state for debugging purposes + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("State ["); + sb.append("\n subjectDN of last cert: ").append(subjectDN); + sb.append("\n subjectKeyIdentifier: ").append + (String.valueOf(subjKeyId)); + sb.append("\n nameConstraints: ").append(String.valueOf(nc)); + sb.append("\n certIndex: ").append(certIndex); + sb.append("\n explicitPolicy: ").append(explicitPolicy); + sb.append("\n policyMapping: ").append(policyMapping); + sb.append("\n inhibitAnyPolicy: ").append(inhibitAnyPolicy); + sb.append("\n rootNode: ").append(rootNode); + sb.append("\n remainingCACerts: ").append(remainingCACerts); + sb.append("\n crlSign: ").append(crlSign); + sb.append("\n init: ").append(init); + sb.append("\n]\n"); + return sb.toString(); + } + + /** + * Initialize the state. + * + * @param buildParams builder parameters + */ + public void initState(BuilderParams buildParams) + throws CertPathValidatorException + { + /* + * Initialize number of remainingCACerts. + * Note that -1 maxPathLen implies unlimited. + * 0 implies only an EE cert is acceptable. + */ + int maxPathLen = buildParams.maxPathLength(); + remainingCACerts = (maxPathLen == -1) ? Integer.MAX_VALUE + : maxPathLen; + + /* Initialize explicit policy state variable */ + if (buildParams.explicitPolicyRequired()) { + explicitPolicy = 0; + } else { + // unconstrained if maxPathLen is -1, + // otherwise, we want to initialize this to the value of the + // longest possible path + 1 (i.e. maxpathlen + finalcert + 1) + explicitPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2; + } + + /* Initialize policy mapping state variable */ + if (buildParams.policyMappingInhibited()) { + policyMapping = 0; + } else { + policyMapping = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2; + } + + /* Initialize inhibit any policy state variable */ + if (buildParams.anyPolicyInhibited()) { + inhibitAnyPolicy = 0; + } else { + inhibitAnyPolicy = (maxPathLen == -1) ? maxPathLen : maxPathLen + 2; + } + + /* Initialize certIndex */ + certIndex = 1; + + /* Initialize policy tree */ + Set initExpPolSet = new HashSet<>(1); + initExpPolSet.add(PolicyChecker.ANY_POLICY); + + rootNode = new PolicyNodeImpl(null, PolicyChecker.ANY_POLICY, null, + false, initExpPolSet, false); + + /* + * Initialize each user-defined checker + * Shallow copy the checkers + */ + userCheckers = new ArrayList<>(buildParams.certPathCheckers()); + /* initialize each checker (just in case) */ + for (PKIXCertPathChecker checker : userCheckers) { + checker.init(false); + } + + /* Start by trusting the cert to sign CRLs */ + crlSign = true; + + init = true; + } + + /** + * Update the state with the specified trust anchor. + * + * @param anchor the most-trusted CA + * @param buildParams builder parameters + */ + public void updateState(TrustAnchor anchor, BuilderParams buildParams) + throws CertificateException, IOException, CertPathValidatorException + { + trustAnchor = anchor; + X509Certificate trustedCert = anchor.getTrustedCert(); + if (trustedCert != null) { + updateState(trustedCert); + } else { + X500Principal caName = anchor.getCA(); + updateState(anchor.getCAPublicKey(), caName); + } + + // The user specified AlgorithmChecker and RevocationChecker may not be + // able to set the trust anchor until now. + boolean revCheckerAdded = false; + for (PKIXCertPathChecker checker : userCheckers) { + if (checker instanceof AlgorithmChecker) { + ((AlgorithmChecker)checker).trySetTrustAnchor(anchor); + } else if (checker instanceof PKIXRevocationChecker) { + if (revCheckerAdded) { + throw new CertPathValidatorException( + "Only one PKIXRevocationChecker can be specified"); + } + // if it's our own, initialize it + if (checker instanceof RevocationChecker) { + ((RevocationChecker)checker).init(anchor, buildParams); + } + ((PKIXRevocationChecker)checker).init(false); + revCheckerAdded = true; + } + } + + // only create a RevocationChecker if revocation is enabled and + // a PKIXRevocationChecker has not already been added + if (buildParams.revocationEnabled() && !revCheckerAdded) { + revChecker = new RevocationChecker(anchor, buildParams); + revChecker.init(false); + } + + init = false; + } + + /** + * Update the state. This method is used when the most-trusted CA is + * a trusted public-key and caName, instead of a trusted cert. + * + * @param pubKey the public key of the trusted CA + * @param subjectDN the subject distinguished name of the trusted CA + */ + private void updateState(PublicKey pubKey, X500Principal subjectDN) { + + /* update subject DN */ + this.subjectDN = subjectDN; + + /* update subject public key */ + this.pubKey = pubKey; + } + + /** + * Update the state with the next certificate added to the path. + * + * @param cert the certificate which is used to update the state + */ + public void updateState(X509Certificate cert) + throws CertificateException, IOException, CertPathValidatorException { + + if (cert == null) { + return; + } + + /* update subject DN */ + subjectDN = cert.getSubjectX500Principal(); + + /* check for key needing to inherit alg parameters */ + X509CertImpl icert = X509CertImpl.toImpl(cert); + PublicKey newKey = cert.getPublicKey(); + if (PKIX.isDSAPublicKeyWithoutParams(newKey)) { + newKey = BasicChecker.makeInheritedParamsKey(newKey, pubKey); + } + + /* update subject public key */ + pubKey = newKey; + + /* + * if this is a trusted cert (init == true), then we + * don't update any of the remaining fields + */ + if (init) { + init = false; + return; + } + + /* update subject key identifier */ + subjKeyId = icert.getSubjectKeyIdentifierExtension(); + + /* update crlSign */ + crlSign = RevocationChecker.certCanSignCrl(cert); + + /* update current name constraints */ + if (nc != null) { + nc.merge(icert.getNameConstraintsExtension()); + } else { + nc = icert.getNameConstraintsExtension(); + if (nc != null) { + // Make sure we do a clone here, because we're probably + // going to modify this object later and we don't want to + // be sharing it with a Certificate object! + nc = (NameConstraintsExtension) nc.clone(); + } + } + + /* update policy state variables */ + explicitPolicy = + PolicyChecker.mergeExplicitPolicy(explicitPolicy, icert, false); + policyMapping = + PolicyChecker.mergePolicyMapping(policyMapping, icert); + inhibitAnyPolicy = + PolicyChecker.mergeInhibitAnyPolicy(inhibitAnyPolicy, icert); + certIndex++; + + /* + * Update remaining CA certs + */ + remainingCACerts = + ConstraintsChecker.mergeBasicConstraints(cert, remainingCACerts); + + init = false; + } + + /** + * Returns a boolean flag indicating if a key lacking necessary key + * algorithm parameters has been encountered. + * + * @return boolean flag indicating if key lacking parameters encountered. + */ + @Override + public boolean keyParamsNeeded() { + /* when building in reverse, we immediately get parameters needed + * or else throw an exception + */ + return false; + } + + /* + * Clone current state. The state is cloned as each cert is + * added to the path. This is necessary if backtracking occurs, + * and a prior state needs to be restored. + * + * Note that this is a SMART clone. Not all fields are fully copied, + * because some of them (e.g., subjKeyId) will + * not have their contents modified by subsequent calls to updateState. + */ + @Override + @SuppressWarnings("unchecked") // Safe casts assuming clone() works correctly + public Object clone() { + try { + ReverseState clonedState = (ReverseState) super.clone(); + + /* clone checkers, if cloneable */ + clonedState.userCheckers = + (ArrayList)userCheckers.clone(); + ListIterator li = + clonedState.userCheckers.listIterator(); + while (li.hasNext()) { + PKIXCertPathChecker checker = li.next(); + if (checker instanceof Cloneable) { + li.set((PKIXCertPathChecker)checker.clone()); + } + } + + /* make copy of name constraints */ + if (nc != null) { + clonedState.nc = (NameConstraintsExtension) nc.clone(); + } + + /* make copy of policy tree */ + if (rootNode != null) { + clonedState.rootNode = rootNode.copyTree(); + } + + return clonedState; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.toString(), e); + } + } +} diff --git a/src/sun/security/provider/certpath/RevocationChecker.java b/src/sun/security/provider/certpath/RevocationChecker.java new file mode 100644 index 00000000..594414f4 --- /dev/null +++ b/src/sun/security/provider/certpath/RevocationChecker.java @@ -0,0 +1,1166 @@ +/* + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.AccessController; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivilegedAction; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.Extension; +import java.security.cert.*; +import java.util.*; +import javax.security.auth.x500.X500Principal; + +import static sun.security.provider.certpath.OCSP.*; +import static sun.security.provider.certpath.PKIX.*; + +import sun.security.x509.*; + +import static sun.security.x509.PKIXExtensions.CertificateIssuer_Id; +import static sun.security.x509.PKIXExtensions.ReasonCode_Id; + +import sun.security.util.Debug; + +class RevocationChecker extends PKIXRevocationChecker { + + private static final Debug debug = Debug.getInstance("certpath"); + + private TrustAnchor anchor; + private ValidatorParams params; + private boolean onlyEE; + private boolean softFail; + private boolean crlDP; + private URI responderURI; + private X509Certificate responderCert; + private List certStores; + private Map ocspResponses; + private List ocspExtensions; + private boolean legacy; + private LinkedList softFailExceptions = + new LinkedList<>(); + + // state variables + private X509Certificate issuerCert; + private PublicKey prevPubKey; + private boolean crlSignFlag; + private int certIndex; + + private enum Mode { PREFER_OCSP, PREFER_CRLS, ONLY_CRLS, ONLY_OCSP }; + private Mode mode = Mode.PREFER_OCSP; + + private static class RevocationProperties { + boolean onlyEE; + boolean ocspEnabled; + boolean crlDPEnabled; + String ocspUrl; + String ocspSubject; + String ocspIssuer; + String ocspSerial; + } + + RevocationChecker() { + legacy = false; + } + + RevocationChecker(TrustAnchor anchor, ValidatorParams params) + throws CertPathValidatorException + { + legacy = true; + init(anchor, params); + } + + void init(TrustAnchor anchor, ValidatorParams params) + throws CertPathValidatorException + { + RevocationProperties rp = getRevocationProperties(); + URI uri = getOcspResponder(); + responderURI = (uri == null) ? toURI(rp.ocspUrl) : uri; + X509Certificate cert = getOcspResponderCert(); + responderCert = (cert == null) + ? getResponderCert(rp, params.trustAnchors(), + params.certStores()) + : cert; + Set

    + * Note that this does not provide support for indirect CRLs, + * only CRLs signed with a different key (but the same issuer + * name) as the certificate being checked. + * + * @param currCert the X509Certificate to be checked + * @param prevKey the PublicKey that failed + * @param signFlag true if that key was trusted to sign CRLs + * @param stackedCerts a Set of X509Certificates> + * whose revocation status depends on the + * non-revoked status of this cert. To avoid + * circular dependencies, we assume they're + * revoked while checking the revocation + * status of this cert. + * @throws CertPathValidatorException if the cert's revocation status + * cannot be verified successfully with another key + */ + private void verifyWithSeparateSigningKey(X509Certificate cert, + PublicKey prevKey, + boolean signFlag, + Set stackedCerts) + throws CertPathValidatorException + { + String msg = "revocation status"; + if (debug != null) { + debug.println( + "RevocationChecker.verifyWithSeparateSigningKey()" + + " ---checking " + msg + "..."); + } + + // reject circular dependencies - RFC 3280 is not explicit on how + // to handle this, so we feel it is safest to reject them until + // the issue is resolved in the PKIX WG. + if ((stackedCerts != null) && stackedCerts.contains(cert)) { + if (debug != null) { + debug.println( + "RevocationChecker.verifyWithSeparateSigningKey()" + + " circular dependency"); + } + throw new CertPathValidatorException + ("Could not determine revocation status", null, null, -1, + BasicReason.UNDETERMINED_REVOCATION_STATUS); + } + + // Try to find another key that might be able to sign + // CRLs vouching for this cert. + // If prevKey wasn't trusted, maybe we just didn't have the right + // path to it. Don't rule that key out. + if (!signFlag) { + buildToNewKey(cert, null, stackedCerts); + } else { + buildToNewKey(cert, prevKey, stackedCerts); + } + } + + /** + * Tries to find a CertPath that establishes a key that can be + * used to verify the revocation status of a given certificate. + * Ignores keys that have previously been tried. Throws a + * CertPathValidatorException if no such key could be found. + * + * @param currCert the X509Certificate to be checked + * @param prevKey the PublicKey of the certificate whose key + * cannot be used to vouch for the CRL and should be ignored + * @param stackedCerts a Set of X509Certificates> + * whose revocation status depends on the + * establishment of this path. + * @throws CertPathValidatorException on failure + */ + private static final boolean [] CRL_SIGN_USAGE = + { false, false, false, false, false, false, true }; + private void buildToNewKey(X509Certificate currCert, + PublicKey prevKey, + Set stackedCerts) + throws CertPathValidatorException + { + + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey()" + + " starting work"); + } + Set badKeys = new HashSet<>(); + if (prevKey != null) { + badKeys.add(prevKey); + } + X509CertSelector certSel = new RejectKeySelector(badKeys); + certSel.setSubject(currCert.getIssuerX500Principal()); + certSel.setKeyUsage(CRL_SIGN_USAGE); + + Set newAnchors = anchor == null ? + params.trustAnchors() : + Collections.singleton(anchor); + + PKIXBuilderParameters builderParams; + try { + builderParams = new PKIXBuilderParameters(newAnchors, certSel); + } catch (InvalidAlgorithmParameterException iape) { + throw new RuntimeException(iape); // should never occur + } + builderParams.setInitialPolicies(params.initialPolicies()); + builderParams.setCertStores(certStores); + builderParams.setExplicitPolicyRequired + (params.explicitPolicyRequired()); + builderParams.setPolicyMappingInhibited + (params.policyMappingInhibited()); + builderParams.setAnyPolicyInhibited(params.anyPolicyInhibited()); + // Policy qualifiers must be rejected, since we don't have + // any way to convey them back to the application. + // That's the default, so no need to write code. + builderParams.setDate(params.date()); + // CertPathCheckers need to be cloned to start from fresh state + builderParams.setCertPathCheckers( + params.getPKIXParameters().getCertPathCheckers()); + builderParams.setSigProvider(params.sigProvider()); + + // Skip revocation during this build to detect circular + // references. But check revocation afterwards, using the + // key (or any other that works). + builderParams.setRevocationEnabled(false); + + // check for AuthorityInformationAccess extension + if (Builder.USE_AIA == true) { + X509CertImpl currCertImpl = null; + try { + currCertImpl = X509CertImpl.toImpl(currCert); + } catch (CertificateException ce) { + // ignore but log it + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey: " + + "error decoding cert: " + ce); + } + } + AuthorityInfoAccessExtension aiaExt = null; + if (currCertImpl != null) { + aiaExt = currCertImpl.getAuthorityInfoAccessExtension(); + } + if (aiaExt != null) { + List adList = aiaExt.getAccessDescriptions(); + if (adList != null) { + for (AccessDescription ad : adList) { + CertStore cs = URICertStore.getInstance(ad); + if (cs != null) { + if (debug != null) { + debug.println("adding AIAext CertStore"); + } + builderParams.addCertStore(cs); + } + } + } + } + } + + CertPathBuilder builder = null; + try { + builder = CertPathBuilder.getInstance("PKIX"); + } catch (NoSuchAlgorithmException nsae) { + throw new CertPathValidatorException(nsae); + } + while (true) { + try { + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey()" + + " about to try build ..."); + } + PKIXCertPathBuilderResult cpbr = + (PKIXCertPathBuilderResult)builder.build(builderParams); + + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey()" + + " about to check revocation ..."); + } + // Now check revocation of all certs in path, assuming that + // the stackedCerts are revoked. + if (stackedCerts == null) { + stackedCerts = new HashSet(); + } + stackedCerts.add(currCert); + TrustAnchor ta = cpbr.getTrustAnchor(); + PublicKey prevKey2 = ta.getCAPublicKey(); + if (prevKey2 == null) { + prevKey2 = ta.getTrustedCert().getPublicKey(); + } + boolean signFlag = true; + List cpList = + cpbr.getCertPath().getCertificates(); + if (cpList.isEmpty()) { + return; + } + try { + for (int i = cpList.size()-1; i >= 0; i-- ) { + X509Certificate cert = (X509Certificate)cpList.get(i); + + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey()" + + " index " + i + " checking " + + cert); + } + checkCRLs(cert, prevKey2, null, signFlag, true, + stackedCerts, newAnchors); + signFlag = certCanSignCrl(cert); + prevKey2 = cert.getPublicKey(); + } + } catch (CertPathValidatorException cpve) { + // ignore it and try to get another key + badKeys.add(cpbr.getPublicKey()); + continue; + } + + if (debug != null) { + debug.println("RevocationChecker.buildToNewKey()" + + " got key " + cpbr.getPublicKey()); + } + // Now check revocation on the current cert using that key and + // the corresponding certificate. + // If it doesn't check out, try to find a different key. + // And if we can't find a key, then return false. + PublicKey newKey = cpbr.getPublicKey(); + try { + checkCRLs(currCert, newKey, (X509Certificate) cpList.get(0), + true, false, null, params.trustAnchors()); + // If that passed, the cert is OK! + return; + } catch (CertPathValidatorException cpve) { + // If it is revoked, rethrow exception + if (cpve.getReason() == BasicReason.REVOKED) { + throw cpve; + } + // Otherwise, ignore the exception and + // try to get another key. + } + badKeys.add(newKey); + } catch (InvalidAlgorithmParameterException iape) { + throw new CertPathValidatorException(iape); + } catch (CertPathBuilderException cpbe) { + throw new CertPathValidatorException + ("Could not determine revocation status", null, null, + -1, BasicReason.UNDETERMINED_REVOCATION_STATUS); + } + } + } + + @Override + public RevocationChecker clone() { + RevocationChecker copy = (RevocationChecker)super.clone(); + // we don't deep-copy the exceptions, but that is ok because they + // are never modified after they are instantiated + copy.softFailExceptions = new LinkedList<>(softFailExceptions); + return copy; + } + + /* + * This inner class extends the X509CertSelector to add an additional + * check to make sure the subject public key isn't on a particular list. + * This class is used by buildToNewKey() to make sure the builder doesn't + * end up with a CertPath to a public key that has already been rejected. + */ + private static class RejectKeySelector extends X509CertSelector { + private final Set badKeySet; + + /** + * Creates a new RejectKeySelector. + * + * @param badPublicKeys a Set of + * PublicKeys that + * should be rejected (or null + * if no such check should be done) + */ + RejectKeySelector(Set badPublicKeys) { + this.badKeySet = badPublicKeys; + } + + /** + * Decides whether a Certificate should be selected. + * + * @param cert the Certificate to be checked + * @return true if the Certificate should be + * selected, false otherwise + */ + @Override + public boolean match(Certificate cert) { + if (!super.match(cert)) + return(false); + + if (badKeySet.contains(cert.getPublicKey())) { + if (debug != null) + debug.println("RejectKeySelector.match: bad key"); + return false; + } + + if (debug != null) + debug.println("RejectKeySelector.match: returning true"); + return true; + } + + /** + * Return a printable representation of the CertSelector. + * + * @return a String describing the contents of the + * CertSelector + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("RejectKeySelector: [\n"); + sb.append(super.toString()); + sb.append(badKeySet); + sb.append("]"); + return sb.toString(); + } + } +} diff --git a/src/sun/security/provider/certpath/State.java b/src/sun/security/provider/certpath/State.java new file mode 100644 index 00000000..93a153fe --- /dev/null +++ b/src/sun/security/provider/certpath/State.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.cert.CertPathValidatorException; + +/** + * A specification of a PKIX validation state + * which is initialized by each build and updated each time a + * certificate is added to the current path. + * + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ + +interface State extends Cloneable { + + /** + * Update the state with the next certificate added to the path. + * + * @param cert the certificate which is used to update the state + */ + public void updateState(X509Certificate cert) + throws CertificateException, IOException, CertPathValidatorException; + + /** + * Creates and returns a copy of this object + */ + public Object clone(); + + /** + * Returns a boolean flag indicating if the state is initial + * (just starting) + * + * @return boolean flag indicating if the state is initial (just starting) + */ + public boolean isInitial(); + + /** + * Returns a boolean flag indicating if a key lacking necessary key + * algorithm parameters has been encountered. + * + * @return boolean flag indicating if key lacking parameters encountered. + */ + public boolean keyParamsNeeded(); +} diff --git a/src/sun/security/provider/certpath/SunCertPathBuilder.java b/src/sun/security/provider/certpath/SunCertPathBuilder.java new file mode 100644 index 00000000..0cde8f22 --- /dev/null +++ b/src/sun/security/provider/certpath/SunCertPathBuilder.java @@ -0,0 +1,813 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.PublicKey; +import java.security.cert.*; +import java.security.cert.CertPathValidatorException.BasicReason; +import java.security.cert.PKIXReason; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.LinkedList; +import java.util.Set; +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.PKIX.BuilderParams; + +import static sun.security.x509.PKIXExtensions.BasicConstraints_Id; +import static sun.security.x509.PKIXExtensions.CertificatePolicies_Id; +import static sun.security.x509.PKIXExtensions.ExtendedKeyUsage_Id; +import static sun.security.x509.PKIXExtensions.InhibitAnyPolicy_Id; +import static sun.security.x509.PKIXExtensions.KeyUsage_Id; +import static sun.security.x509.PKIXExtensions.NameConstraints_Id; +import static sun.security.x509.PKIXExtensions.PolicyConstraints_Id; +import static sun.security.x509.PKIXExtensions.PolicyMappings_Id; +import static sun.security.x509.PKIXExtensions.SubjectAlternativeName_Id; + +import sun.security.util.Debug; + +/** + * This class is able to build certification paths in either the forward + * or reverse directions. + * + *

    If successful, it returns a certification path which has successfully + * satisfied all the constraints and requirements specified in the + * PKIXBuilderParameters object and has been validated according to the PKIX + * path validation algorithm defined in RFC 3280. + * + *

    This implementation uses a depth-first search approach to finding + * certification paths. If it comes to a point in which it cannot find + * any more certificates leading to the target OR the path length is too long + * it backtracks to previous paths until the target has been found or + * all possible paths have been exhausted. + * + *

    This implementation is not thread-safe. + * + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ +public final class SunCertPathBuilder extends CertPathBuilderSpi { + + private static final Debug debug = Debug.getInstance("certpath"); + + /* + * private objects shared by methods + */ + private BuilderParams buildParams; + private CertificateFactory cf; + private boolean pathCompleted = false; + private PolicyNode policyTreeResult; + private TrustAnchor trustAnchor; + private PublicKey finalPublicKey; + + /** + * Create an instance of SunCertPathBuilder. + * + * @throws CertPathBuilderException if an error occurs + */ + public SunCertPathBuilder() throws CertPathBuilderException { + try { + cf = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new CertPathBuilderException(e); + } + } + + @Override + public CertPathChecker engineGetRevocationChecker() { + return new RevocationChecker(); + } + + /** + * Attempts to build a certification path using the Sun build + * algorithm from a trusted anchor(s) to a target subject, which must both + * be specified in the input parameter set. By default, this method will + * attempt to build in the forward direction. In order to build in the + * reverse direction, the caller needs to pass in an instance of + * SunCertPathBuilderParameters with the buildForward flag set to false. + * + *

    The certification path that is constructed is validated + * according to the PKIX specification. + * + * @param params the parameter set for building a path. Must be an instance + * of PKIXBuilderParameters. + * @return a certification path builder result. + * @exception CertPathBuilderException Exception thrown if builder is + * unable to build a complete certification path from the trusted anchor(s) + * to the target subject. + * @throws InvalidAlgorithmParameterException if the given parameters are + * inappropriate for this certification path builder. + */ + @Override + public CertPathBuilderResult engineBuild(CertPathParameters params) + throws CertPathBuilderException, InvalidAlgorithmParameterException { + + if (debug != null) { + debug.println("SunCertPathBuilder.engineBuild(" + params + ")"); + } + + buildParams = PKIX.checkBuilderParams(params); + return build(); + } + + private PKIXCertPathBuilderResult build() throws CertPathBuilderException { + List> adjList = new ArrayList<>(); + PKIXCertPathBuilderResult result = buildCertPath(false, adjList); + if (result == null) { + if (debug != null) { + debug.println("SunCertPathBuilder.engineBuild: 2nd pass"); + } + // try again + adjList.clear(); + result = buildCertPath(true, adjList); + if (result == null) { + throw new SunCertPathBuilderException("unable to find valid " + + "certification path to requested target", + new AdjacencyList(adjList)); + } + } + return result; + } + + private PKIXCertPathBuilderResult buildCertPath(boolean searchAllCertStores, + List> adjList) + throws CertPathBuilderException + { + // Init shared variables and build certification path + pathCompleted = false; + trustAnchor = null; + finalPublicKey = null; + policyTreeResult = null; + LinkedList certPathList = new LinkedList<>(); + try { + if (buildParams.buildForward()) { + buildForward(adjList, certPathList, searchAllCertStores); + } else { + buildReverse(adjList, certPathList); + } + } catch (GeneralSecurityException | IOException e) { + if (debug != null) { + debug.println("SunCertPathBuilder.engineBuild() exception in " + + "build"); + e.printStackTrace(); + } + throw new SunCertPathBuilderException("unable to find valid " + + "certification path to requested target", e, + new AdjacencyList(adjList)); + } + + // construct SunCertPathBuilderResult + try { + if (pathCompleted) { + if (debug != null) + debug.println("SunCertPathBuilder.engineBuild() " + + "pathCompleted"); + + // we must return a certpath which has the target + // as the first cert in the certpath - i.e. reverse + // the certPathList + Collections.reverse(certPathList); + + return new SunCertPathBuilderResult( + cf.generateCertPath(certPathList), trustAnchor, + policyTreeResult, finalPublicKey, + new AdjacencyList(adjList)); + } + } catch (CertificateException e) { + if (debug != null) { + debug.println("SunCertPathBuilder.engineBuild() exception " + + "in wrap-up"); + e.printStackTrace(); + } + throw new SunCertPathBuilderException("unable to find valid " + + "certification path to requested target", e, + new AdjacencyList(adjList)); + } + + return null; + } + + /* + * Private build reverse method. + */ + private void buildReverse(List> adjacencyList, + LinkedList certPathList) + throws GeneralSecurityException, IOException + { + if (debug != null) { + debug.println("SunCertPathBuilder.buildReverse()..."); + debug.println("SunCertPathBuilder.buildReverse() InitialPolicies: " + + buildParams.initialPolicies()); + } + + ReverseState currentState = new ReverseState(); + /* Initialize adjacency list */ + adjacencyList.clear(); + adjacencyList.add(new LinkedList()); + + /* + * Perform a search using each trust anchor, until a valid + * path is found + */ + Iterator iter = buildParams.trustAnchors().iterator(); + while (iter.hasNext()) { + TrustAnchor anchor = iter.next(); + + /* check if anchor satisfies target constraints */ + if (anchorIsTarget(anchor, buildParams.targetCertConstraints())) { + this.trustAnchor = anchor; + this.pathCompleted = true; + this.finalPublicKey = anchor.getTrustedCert().getPublicKey(); + break; + } + + // skip anchor if it contains a DSA key with no DSA params + X509Certificate trustedCert = anchor.getTrustedCert(); + PublicKey pubKey = trustedCert != null ? trustedCert.getPublicKey() + : anchor.getCAPublicKey(); + + if (PKIX.isDSAPublicKeyWithoutParams(pubKey)) { + continue; + } + + /* Initialize current state */ + currentState.initState(buildParams); + currentState.updateState(anchor, buildParams); + + currentState.algorithmChecker = new AlgorithmChecker(anchor); + currentState.untrustedChecker = new UntrustedChecker(); + try { + depthFirstSearchReverse(null, currentState, + new ReverseBuilder(buildParams), + adjacencyList, certPathList); + } catch (GeneralSecurityException | IOException e) { + // continue on error if more anchors to try + if (iter.hasNext()) + continue; + else + throw e; + } + + // break out of loop if search is successful + if (pathCompleted) { + break; + } + } + + if (debug != null) { + debug.println("SunCertPathBuilder.buildReverse() returned from " + + "depthFirstSearchReverse()"); + debug.println("SunCertPathBuilder.buildReverse() " + + "certPathList.size: " + certPathList.size()); + } + } + + /* + * Private build forward method. + */ + private void buildForward(List> adjacencyList, + LinkedList certPathList, + boolean searchAllCertStores) + throws GeneralSecurityException, IOException + { + if (debug != null) { + debug.println("SunCertPathBuilder.buildForward()..."); + } + + /* Initialize current state */ + ForwardState currentState = new ForwardState(); + currentState.initState(buildParams.certPathCheckers()); + + /* Initialize adjacency list */ + adjacencyList.clear(); + adjacencyList.add(new LinkedList()); + + currentState.untrustedChecker = new UntrustedChecker(); + + depthFirstSearchForward(buildParams.targetSubject(), currentState, + new ForwardBuilder(buildParams, + searchAllCertStores), + adjacencyList, certPathList); + } + + /* + * This method performs a depth first search for a certification + * path while building forward which meets the requirements set in + * the parameters object. + * It uses an adjacency list to store all certificates which were + * tried (i.e. at one time added to the path - they may not end up in + * the final path if backtracking occurs). This information can + * be used later to debug or demo the build. + * + * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman" + * for an explanation of the DFS algorithm. + * + * @param dN the distinguished name being currently searched for certs + * @param currentState the current PKIX validation state + */ + private void depthFirstSearchForward(X500Principal dN, + ForwardState currentState, + ForwardBuilder builder, + List> adjList, + LinkedList cpList) + throws GeneralSecurityException, IOException + { + if (debug != null) { + debug.println("SunCertPathBuilder.depthFirstSearchForward(" + dN + + ", " + currentState.toString() + ")"); + } + + /* + * Find all the certificates issued to dN which + * satisfy the PKIX certification path constraints. + */ + Collection certs = + builder.getMatchingCerts(currentState, buildParams.certStores()); + List vertices = addVertices(certs, adjList); + if (debug != null) { + debug.println("SunCertPathBuilder.depthFirstSearchForward(): " + + "certs.size=" + vertices.size()); + } + + /* + * For each cert in the collection, verify anything + * that hasn't been checked yet (signature, revocation, etc) + * and check for loops. Call depthFirstSearchForward() + * recursively for each good cert. + */ + + vertices: + for (Vertex vertex : vertices) { + /** + * Restore state to currentState each time through the loop. + * This is important because some of the user-defined + * checkers modify the state, which MUST be restored if + * the cert eventually fails to lead to the target and + * the next matching cert is tried. + */ + ForwardState nextState = (ForwardState) currentState.clone(); + X509Certificate cert = vertex.getCertificate(); + + try { + builder.verifyCert(cert, nextState, cpList); + } catch (GeneralSecurityException gse) { + if (debug != null) { + debug.println("SunCertPathBuilder.depthFirstSearchForward()" + + ": validation failed: " + gse); + gse.printStackTrace(); + } + vertex.setThrowable(gse); + continue; + } + + /* + * Certificate is good. + * If cert completes the path, + * process userCheckers that don't support forward checking + * and process policies over whole path + * and backtrack appropriately if there is a failure + * else if cert does not complete the path, + * add it to the path + */ + if (builder.isPathCompleted(cert)) { + + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchForward()" + + ": commencing final verification"); + + List appendedCerts = new ArrayList<>(cpList); + + /* + * if the trust anchor selected is specified as a trusted + * public key rather than a trusted cert, then verify this + * cert (which is signed by the trusted public key), but + * don't add it yet to the cpList + */ + if (builder.trustAnchor.getTrustedCert() == null) { + appendedCerts.add(0, cert); + } + + Set initExpPolSet = + Collections.singleton(PolicyChecker.ANY_POLICY); + + PolicyNodeImpl rootNode = new PolicyNodeImpl(null, + PolicyChecker.ANY_POLICY, null, false, initExpPolSet, false); + + List checkers = new ArrayList<>(); + PolicyChecker policyChecker + = new PolicyChecker(buildParams.initialPolicies(), + appendedCerts.size(), + buildParams.explicitPolicyRequired(), + buildParams.policyMappingInhibited(), + buildParams.anyPolicyInhibited(), + buildParams.policyQualifiersRejected(), + rootNode); + checkers.add(policyChecker); + + // add the algorithm checker + checkers.add(new AlgorithmChecker(builder.trustAnchor)); + + BasicChecker basicChecker = null; + if (nextState.keyParamsNeeded()) { + PublicKey rootKey = cert.getPublicKey(); + if (builder.trustAnchor.getTrustedCert() == null) { + rootKey = builder.trustAnchor.getCAPublicKey(); + if (debug != null) + debug.println( + "SunCertPathBuilder.depthFirstSearchForward " + + "using buildParams public key: " + + rootKey.toString()); + } + TrustAnchor anchor = new TrustAnchor + (cert.getSubjectX500Principal(), rootKey, null); + + // add the basic checker + basicChecker = new BasicChecker(anchor, buildParams.date(), + buildParams.sigProvider(), + true); + checkers.add(basicChecker); + } + + buildParams.setCertPath(cf.generateCertPath(appendedCerts)); + + boolean revCheckerAdded = false; + List ckrs = buildParams.certPathCheckers(); + for (PKIXCertPathChecker ckr : ckrs) { + if (ckr instanceof PKIXRevocationChecker) { + if (revCheckerAdded) { + throw new CertPathValidatorException( + "Only one PKIXRevocationChecker can be specified"); + } + revCheckerAdded = true; + // if it's our own, initialize it + if (ckr instanceof RevocationChecker) { + ((RevocationChecker)ckr).init(builder.trustAnchor, + buildParams); + } + } + } + // only add a RevocationChecker if revocation is enabled and + // a PKIXRevocationChecker has not already been added + if (buildParams.revocationEnabled() && !revCheckerAdded) { + checkers.add(new RevocationChecker(builder.trustAnchor, + buildParams)); + } + + checkers.addAll(ckrs); + + // Why we don't need BasicChecker and RevocationChecker + // if nextState.keyParamsNeeded() is false? + + for (int i = 0; i < appendedCerts.size(); i++) { + X509Certificate currCert = appendedCerts.get(i); + if (debug != null) + debug.println("current subject = " + + currCert.getSubjectX500Principal()); + Set unresCritExts = + currCert.getCriticalExtensionOIDs(); + if (unresCritExts == null) { + unresCritExts = Collections.emptySet(); + } + + for (PKIXCertPathChecker currChecker : checkers) { + if (!currChecker.isForwardCheckingSupported()) { + if (i == 0) { + currChecker.init(false); + + // The user specified + // AlgorithmChecker may not be + // able to set the trust anchor until now. + if (currChecker instanceof AlgorithmChecker) { + ((AlgorithmChecker)currChecker). + trySetTrustAnchor(builder.trustAnchor); + } + } + + try { + currChecker.check(currCert, unresCritExts); + } catch (CertPathValidatorException cpve) { + if (debug != null) + debug.println + ("SunCertPathBuilder.depthFirstSearchForward(): " + + "final verification failed: " + cpve); + // If the target cert itself is revoked, we + // cannot trust it. We can bail out here. + if (buildParams.targetCertConstraints().match(currCert) + && cpve.getReason() == BasicReason.REVOKED) { + throw cpve; + } + vertex.setThrowable(cpve); + continue vertices; + } + } + } + + /* + * Remove extensions from user checkers that support + * forward checking. After this step, we will have + * removed all extensions that all user checkers + * are capable of processing. + */ + for (PKIXCertPathChecker checker : + buildParams.certPathCheckers()) + { + if (checker.isForwardCheckingSupported()) { + Set suppExts = + checker.getSupportedExtensions(); + if (suppExts != null) { + unresCritExts.removeAll(suppExts); + } + } + } + + if (!unresCritExts.isEmpty()) { + unresCritExts.remove(BasicConstraints_Id.toString()); + unresCritExts.remove(NameConstraints_Id.toString()); + unresCritExts.remove(CertificatePolicies_Id.toString()); + unresCritExts.remove(PolicyMappings_Id.toString()); + unresCritExts.remove(PolicyConstraints_Id.toString()); + unresCritExts.remove(InhibitAnyPolicy_Id.toString()); + unresCritExts.remove( + SubjectAlternativeName_Id.toString()); + unresCritExts.remove(KeyUsage_Id.toString()); + unresCritExts.remove(ExtendedKeyUsage_Id.toString()); + + if (!unresCritExts.isEmpty()) { + throw new CertPathValidatorException + ("unrecognized critical extension(s)", null, + null, -1, PKIXReason.UNRECOGNIZED_CRIT_EXT); + } + } + } + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchForward()" + + ": final verification succeeded - path completed!"); + pathCompleted = true; + + /* + * if the user specified a trusted public key rather than + * trusted certs, then add this cert (which is signed by + * the trusted public key) to the cpList + */ + if (builder.trustAnchor.getTrustedCert() == null) + builder.addCertToPath(cert, cpList); + // Save the trust anchor + this.trustAnchor = builder.trustAnchor; + + /* + * Extract and save the final target public key + */ + if (basicChecker != null) { + finalPublicKey = basicChecker.getPublicKey(); + } else { + Certificate finalCert; + if (cpList.isEmpty()) { + finalCert = builder.trustAnchor.getTrustedCert(); + } else { + finalCert = cpList.getLast(); + } + finalPublicKey = finalCert.getPublicKey(); + } + + policyTreeResult = policyChecker.getPolicyTree(); + return; + } else { + builder.addCertToPath(cert, cpList); + } + + /* Update the PKIX state */ + nextState.updateState(cert); + + /* + * Append an entry for cert in adjacency list and + * set index for current vertex. + */ + adjList.add(new LinkedList()); + vertex.setIndex(adjList.size() - 1); + + /* recursively search for matching certs at next dN */ + depthFirstSearchForward(cert.getIssuerX500Principal(), nextState, + builder, adjList, cpList); + + /* + * If path has been completed, return ASAP! + */ + if (pathCompleted) { + return; + } else { + /* + * If we get here, it means we have searched all possible + * certs issued by the dN w/o finding any matching certs. + * This means we have to backtrack to the previous cert in + * the path and try some other paths. + */ + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchForward()" + + ": backtracking"); + builder.removeFinalCertFromPath(cpList); + } + } + } + + /* + * This method performs a depth first search for a certification + * path while building reverse which meets the requirements set in + * the parameters object. + * It uses an adjacency list to store all certificates which were + * tried (i.e. at one time added to the path - they may not end up in + * the final path if backtracking occurs). This information can + * be used later to debug or demo the build. + * + * See "Data Structure and Algorithms, by Aho, Hopcroft, and Ullman" + * for an explanation of the DFS algorithm. + * + * @param dN the distinguished name being currently searched for certs + * @param currentState the current PKIX validation state + */ + private void depthFirstSearchReverse(X500Principal dN, + ReverseState currentState, + ReverseBuilder builder, + List> adjList, + LinkedList cpList) + throws GeneralSecurityException, IOException + { + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse(" + dN + + ", " + currentState.toString() + ")"); + + /* + * Find all the certificates issued by dN which + * satisfy the PKIX certification path constraints. + */ + Collection certs = + builder.getMatchingCerts(currentState, buildParams.certStores()); + List vertices = addVertices(certs, adjList); + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse(): " + + "certs.size=" + vertices.size()); + + /* + * For each cert in the collection, verify anything + * that hasn't been checked yet (signature, revocation, etc) + * and check for loops. Call depthFirstSearchReverse() + * recursively for each good cert. + */ + for (Vertex vertex : vertices) { + /** + * Restore state to currentState each time through the loop. + * This is important because some of the user-defined + * checkers modify the state, which MUST be restored if + * the cert eventually fails to lead to the target and + * the next matching cert is tried. + */ + ReverseState nextState = (ReverseState) currentState.clone(); + X509Certificate cert = vertex.getCertificate(); + try { + builder.verifyCert(cert, nextState, cpList); + } catch (GeneralSecurityException gse) { + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse()" + + ": validation failed: " + gse); + vertex.setThrowable(gse); + continue; + } + + /* + * Certificate is good, add it to the path (if it isn't a + * self-signed cert) and update state + */ + if (!currentState.isInitial()) + builder.addCertToPath(cert, cpList); + // save trust anchor + this.trustAnchor = currentState.trustAnchor; + + /* + * Check if path is completed, return ASAP if so. + */ + if (builder.isPathCompleted(cert)) { + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse()" + + ": path completed!"); + pathCompleted = true; + + PolicyNodeImpl rootNode = nextState.rootNode; + + if (rootNode == null) + policyTreeResult = null; + else { + policyTreeResult = rootNode.copyTree(); + ((PolicyNodeImpl)policyTreeResult).setImmutable(); + } + + /* + * Extract and save the final target public key + */ + finalPublicKey = cert.getPublicKey(); + if (PKIX.isDSAPublicKeyWithoutParams(finalPublicKey)) { + finalPublicKey = + BasicChecker.makeInheritedParamsKey + (finalPublicKey, currentState.pubKey); + } + + return; + } + + /* Update the PKIX state */ + nextState.updateState(cert); + + /* + * Append an entry for cert in adjacency list and + * set index for current vertex. + */ + adjList.add(new LinkedList()); + vertex.setIndex(adjList.size() - 1); + + /* recursively search for matching certs at next dN */ + depthFirstSearchReverse(cert.getSubjectX500Principal(), nextState, + builder, adjList, cpList); + + /* + * If path has been completed, return ASAP! + */ + if (pathCompleted) { + return; + } else { + /* + * If we get here, it means we have searched all possible + * certs issued by the dN w/o finding any matching certs. This + * means we have to backtrack to the previous cert in the path + * and try some other paths. + */ + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse()" + + ": backtracking"); + if (!currentState.isInitial()) + builder.removeFinalCertFromPath(cpList); + } + } + if (debug != null) + debug.println("SunCertPathBuilder.depthFirstSearchReverse() all " + + "certs in this adjacency list checked"); + } + + /* + * Adds a collection of matching certificates to the + * adjacency list. + */ + private static List addVertices(Collection certs, + List> adjList) + { + List l = adjList.get(adjList.size() - 1); + + for (X509Certificate cert : certs) { + Vertex v = new Vertex(cert); + l.add(v); + } + + return l; + } + + /** + * Returns true if trust anchor certificate matches specified + * certificate constraints. + */ + private static boolean anchorIsTarget(TrustAnchor anchor, + CertSelector sel) + { + X509Certificate anchorCert = anchor.getTrustedCert(); + if (anchorCert != null) { + return sel.match(anchorCert); + } + return false; + } +} diff --git a/src/sun/security/provider/certpath/SunCertPathBuilderException.java b/src/sun/security/provider/certpath/SunCertPathBuilderException.java new file mode 100644 index 00000000..74a8169e --- /dev/null +++ b/src/sun/security/provider/certpath/SunCertPathBuilderException.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.cert.CertPathBuilderException; + +/** + * This is a subclass of the generic CertPathBuilderException. + * It contains an adjacency list with information regarding the unsuccessful + * paths that the SunCertPathBuilder tried. + * + * @since 1.4 + * @author Sean Mullan + * @see CertPathBuilderException + */ +public class SunCertPathBuilderException extends CertPathBuilderException { + + private static final long serialVersionUID = -7814288414129264709L; + + /** + * @serial + */ + private transient AdjacencyList adjList; + + /** + * Constructs a SunCertPathBuilderException with + * null as its detail message. + */ + public SunCertPathBuilderException() { + super(); + } + + /** + * Constructs a SunCertPathBuilderException with the specified + * detail message. A detail message is a String that + * describes this particular exception. + * + * @param msg the detail message + */ + public SunCertPathBuilderException(String msg) { + super(msg); + } + + /** + * Constructs a SunCertPathBuilderException that wraps the + * specified throwable. This allows any exception to be converted into a + * SunCertPathBuilderException, while retaining information + * about the cause, which may be useful for debugging. The detail message is + * set to (cause==null ? null : cause.toString()) (which + * typically contains the class and detail message of cause). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or unknown.) + * root cause. + */ + public SunCertPathBuilderException(Throwable cause) { + super(cause); + } + + /** + * Creates a SunCertPathBuilderException with the specified + * detail message and cause. + * + * @param msg the detail message + * @param cause the cause + */ + public SunCertPathBuilderException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * Creates a SunCertPathBuilderException withe the specified + * detail message and adjacency list. + * + * @param msg the detail message + * @param adjList the adjacency list + */ + SunCertPathBuilderException(String msg, AdjacencyList adjList) { + this(msg); + this.adjList = adjList; + } + + /** + * Creates a SunCertPathBuilderException with the specified + * detail message, cause, and adjacency list. + * + * @param msg the detail message + * @param cause the throwable that occurred + * @param adjList Adjacency list + */ + SunCertPathBuilderException(String msg, Throwable cause, + AdjacencyList adjList) + { + this(msg, cause); + this.adjList = adjList; + } + + /** + * Returns the adjacency list containing information about the build. + * + * @return the adjacency list containing information about the build + */ + public AdjacencyList getAdjacencyList() { + return adjList; + } +} diff --git a/src/sun/security/provider/certpath/SunCertPathBuilderParameters.java b/src/sun/security/provider/certpath/SunCertPathBuilderParameters.java new file mode 100644 index 00000000..186e2527 --- /dev/null +++ b/src/sun/security/provider/certpath/SunCertPathBuilderParameters.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.*; +import java.util.Set; + +/** + * This class specifies the set of parameters used as input for the Sun + * certification path build algorithm. It is identical to PKIXBuilderParameters + * with the addition of a buildForward parameter which allows + * the caller to specify whether or not the path should be constructed in + * the forward direction. + * + * The default for the buildForward parameter is + * true, which means that the build algorithm should construct paths + * from the target subject back to the trusted anchor. + * + * @since 1.4 + * @author Sean Mullan + * @author Yassir Elley + */ +public class SunCertPathBuilderParameters extends PKIXBuilderParameters { + + private boolean buildForward = true; + + /** + * Creates an instance of SunCertPathBuilderParameters with the + * specified parameter values. + * + * @param trustAnchors a Set of TrustAnchors + * @param targetConstraints a CertSelector specifying the + * constraints on the target certificate + * @throws InvalidAlgorithmParameterException if the specified + * Set is empty (trustAnchors.isEmpty() == true) + * @throws NullPointerException if the specified Set is + * null + * @throws ClassCastException if any of the elements in the Set + * are not of type java.security.cert.TrustAnchor + */ + public SunCertPathBuilderParameters(Set trustAnchors, + CertSelector targetConstraints) throws InvalidAlgorithmParameterException + { + super(trustAnchors, targetConstraints); + setBuildForward(true); + } + + /** + * Creates an instance of SunCertPathBuilderParameters that + * uses the specified KeyStore to populate the set + * of most-trusted CA certificates. + * + * @param keystore A keystore from which the set of most-trusted + * CA certificates will be populated. + * @param targetConstraints a CertSelector specifying the + * constraints on the target certificate + * @throws KeyStoreException if the keystore has not been initialized. + * @throws InvalidAlgorithmParameterException if the keystore does + * not contain at least one trusted certificate entry + * @throws NullPointerException if the keystore is null + */ + public SunCertPathBuilderParameters(KeyStore keystore, + CertSelector targetConstraints) + throws KeyStoreException, InvalidAlgorithmParameterException + { + super(keystore, targetConstraints); + setBuildForward(true); + } + + /** + * Returns the value of the buildForward flag. + * + * @return the value of the buildForward flag + */ + public boolean getBuildForward() { + return this.buildForward; + } + + /** + * Sets the value of the buildForward flag. If true, paths + * are built from the target subject to the trusted anchor. + * If false, paths are built from the trusted anchor to the + * target subject. The default value if not specified is true. + * + * @param buildForward the value of the buildForward flag + */ + public void setBuildForward(boolean buildForward) { + this.buildForward = buildForward; + } + + /** + * Returns a formatted string describing the parameters. + * + * @return a formatted string describing the parameters. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[\n"); + sb.append(super.toString()); + sb.append(" Build Forward Flag: " + String.valueOf(buildForward) + "\n"); + sb.append("]\n"); + return sb.toString(); + } +} diff --git a/src/sun/security/provider/certpath/SunCertPathBuilderResult.java b/src/sun/security/provider/certpath/SunCertPathBuilderResult.java new file mode 100644 index 00000000..59241908 --- /dev/null +++ b/src/sun/security/provider/certpath/SunCertPathBuilderResult.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import sun.security.util.Debug; +import java.security.PublicKey; +import java.security.cert.CertPath; +import java.security.cert.PKIXCertPathBuilderResult; +import java.security.cert.PolicyNode; +import java.security.cert.TrustAnchor; + +/** + * This class represents the result of a SunCertPathBuilder build. + * Since all paths returned by the SunCertPathProvider are PKIX validated + * the result contains the valid policy tree and subject public key returned + * by the algorithm. It also contains the trust anchor and debug information + * represented in the form of an adjacency list. + * + * @see PKIXCertPathBuilderResult + * + * @since 1.4 + * @author Sean Mullan + */ +//@@@ Note: this class is not in public API and access to adjacency list is +//@@@ intended for debugging/replay of Sun PKIX CertPathBuilder implementation. + +public class SunCertPathBuilderResult extends PKIXCertPathBuilderResult { + + private static final Debug debug = Debug.getInstance("certpath"); + + private AdjacencyList adjList; + + /** + * Creates a SunCertPathBuilderResult instance. + * + * @param certPath the validated CertPath + * @param trustAnchor a TrustAnchor describing the CA that + * served as a trust anchor for the certification path + * @param policyTree the valid policy tree, or null + * if there are no valid policies + * @param subjectPublicKey the public key of the subject + * @param adjList an Adjacency list containing debug information + */ + SunCertPathBuilderResult(CertPath certPath, + TrustAnchor trustAnchor, PolicyNode policyTree, + PublicKey subjectPublicKey, AdjacencyList adjList) + { + super(certPath, trustAnchor, policyTree, subjectPublicKey); + this.adjList = adjList; + } + + /** + * Returns the adjacency list containing information about the build. + * + * @return The adjacency list containing information about the build. + */ + public AdjacencyList getAdjacencyList() { + return adjList; + } +} diff --git a/src/sun/security/provider/certpath/URICertStore.java b/src/sun/security/provider/certpath/URICertStore.java new file mode 100644 index 00000000..66677f63 --- /dev/null +++ b/src/sun/security/provider/certpath/URICertStore.java @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.InputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URLConnection; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertSelector; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.CertStoreParameters; +import java.security.cert.CertStoreSpi; +import java.security.cert.CRLException; +import java.security.cert.CRLSelector; +import java.security.cert.X509Certificate; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRL; +import java.security.cert.X509CRLSelector; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import sun.security.action.GetIntegerAction; +import sun.security.x509.AccessDescription; +import sun.security.x509.GeneralNameInterface; +import sun.security.x509.URIName; +import sun.security.util.Cache; +import sun.security.util.Debug; + +/** + * A CertStore that retrieves Certificates or + * CRLs from a URI, for example, as specified in an X.509 + * AuthorityInformationAccess or CRLDistributionPoint extension. + *

    + * For CRLs, this implementation retrieves a single DER encoded CRL per URI. + * For Certificates, this implementation retrieves a single DER encoded CRL or + * a collection of Certificates encoded as a PKCS#7 "certs-only" CMS message. + *

    + * This CertStore also implements Certificate/CRL caching. + * Currently, the cache is shared between all applications in the VM and uses a + * hardcoded policy. The cache has a maximum size of 185 entries, which are held + * by SoftReferences. A request will be satisfied from the cache if we last + * checked for an update within CHECK_INTERVAL (last 30 seconds). Otherwise, + * we open an URLConnection to download the Certificate(s)/CRL using an + * If-Modified-Since request (HTTP) if possible. Note that both positive and + * negative responses are cached, i.e. if we are unable to open the connection + * or the Certificate(s)/CRL cannot be parsed, we remember this result and + * additional calls during the CHECK_INTERVAL period do not try to open another + * connection. + *

    + * The URICertStore is not currently a standard CertStore type. We should + * consider adding a standard "URI" CertStore type. + * + * @author Andreas Sterbenz + * @author Sean Mullan + * @since 7.0 + */ +class URICertStore extends CertStoreSpi { + + private static final Debug debug = Debug.getInstance("certpath"); + + // interval between checks for update of cached Certificates/CRLs + // (30 seconds) + private final static int CHECK_INTERVAL = 30 * 1000; + + // size of the cache (see Cache class for sizing recommendations) + private final static int CACHE_SIZE = 185; + + // X.509 certificate factory instance + private final CertificateFactory factory; + + // cached Collection of X509Certificates (may be empty, never null) + private Collection certs = Collections.emptySet(); + + // cached X509CRL (may be null) + private X509CRL crl; + + // time we last checked for an update + private long lastChecked; + + // time server returned as last modified time stamp + // or 0 if not available + private long lastModified; + + // the URI of this CertStore + private URI uri; + + // true if URI is ldap + private boolean ldap = false; + private CertStoreHelper ldapHelper; + private CertStore ldapCertStore; + private String ldapPath; + + // Default maximum connect timeout in milliseconds (15 seconds) + // allowed when downloading CRLs + private static final int DEFAULT_CRL_CONNECT_TIMEOUT = 15000; + + /** + * Integer value indicating the connect timeout, in seconds, to be + * used for the CRL download. A timeout of zero is interpreted as + * an infinite timeout. + */ + private static final int CRL_CONNECT_TIMEOUT = initializeTimeout(); + + /** + * Initialize the timeout length by getting the CRL timeout + * system property. If the property has not been set, or if its + * value is negative, set the timeout length to the default. + */ + private static int initializeTimeout() { + Integer tmp = java.security.AccessController.doPrivileged( + new GetIntegerAction("com.sun.security.crl.timeout")); + if (tmp == null || tmp < 0) { + return DEFAULT_CRL_CONNECT_TIMEOUT; + } + // Convert to milliseconds, as the system property will be + // specified in seconds + return tmp * 1000; + } + + /** + * Creates a URICertStore. + * + * @param parameters specifying the URI + */ + URICertStore(CertStoreParameters params) + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + super(params); + if (!(params instanceof URICertStoreParameters)) { + throw new InvalidAlgorithmParameterException + ("params must be instanceof URICertStoreParameters"); + } + this.uri = ((URICertStoreParameters) params).uri; + // if ldap URI, use an LDAPCertStore to fetch certs and CRLs + if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals("ldap")) { + ldap = true; + ldapHelper = CertStoreHelper.getInstance("LDAP"); + ldapCertStore = ldapHelper.getCertStore(uri); + ldapPath = uri.getPath(); + // strip off leading '/' + if (ldapPath.charAt(0) == '/') { + ldapPath = ldapPath.substring(1); + } + } + try { + factory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException(); + } + } + + /** + * Returns a URI CertStore. This method consults a cache of + * CertStores (shared per JVM) using the URI as a key. + */ + private static final Cache + certStoreCache = Cache.newSoftMemoryCache(CACHE_SIZE); + static synchronized CertStore getInstance(URICertStoreParameters params) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + if (debug != null) { + debug.println("CertStore URI:" + params.uri); + } + CertStore ucs = certStoreCache.get(params); + if (ucs == null) { + ucs = new UCS(new URICertStore(params), null, "URI", params); + certStoreCache.put(params, ucs); + } else { + if (debug != null) { + debug.println("URICertStore.getInstance: cache hit"); + } + } + return ucs; + } + + /** + * Creates a CertStore from information included in the AccessDescription + * object of a certificate's Authority Information Access Extension. + */ + static CertStore getInstance(AccessDescription ad) { + if (!ad.getAccessMethod().equals((Object) + AccessDescription.Ad_CAISSUERS_Id)) { + return null; + } + GeneralNameInterface gn = ad.getAccessLocation().getName(); + if (!(gn instanceof URIName)) { + return null; + } + URI uri = ((URIName) gn).getURI(); + try { + return URICertStore.getInstance + (new URICertStoreParameters(uri)); + } catch (Exception ex) { + if (debug != null) { + debug.println("exception creating CertStore: " + ex); + ex.printStackTrace(); + } + return null; + } + } + + /** + * Returns a Collection of X509Certificates that + * match the specified selector. If no X509Certificates + * match the selector, an empty Collection will be returned. + * + * @param selector a CertSelector used to select which + * X509Certificates should be returned. Specify + * null to return all X509Certificates. + * @return a Collection of X509Certificates that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + @SuppressWarnings("unchecked") + public synchronized Collection engineGetCertificates + (CertSelector selector) throws CertStoreException { + + // if ldap URI we wrap the CertSelector in an LDAPCertSelector to + // avoid LDAP DN matching issues (see LDAPCertSelector for more info) + if (ldap) { + X509CertSelector xsel = (X509CertSelector) selector; + try { + xsel = ldapHelper.wrap(xsel, xsel.getSubject(), ldapPath); + } catch (IOException ioe) { + throw new CertStoreException(ioe); + } + // Fetch the certificates via LDAP. LDAPCertStore has its own + // caching mechanism, see the class description for more info. + // Safe cast since xsel is an X509 certificate selector. + return (Collection) + ldapCertStore.getCertificates(xsel); + } + + // Return the Certificates for this entry. It returns the cached value + // if it is still current and fetches the Certificates otherwise. + // For the caching details, see the top of this class. + long time = System.currentTimeMillis(); + if (time - lastChecked < CHECK_INTERVAL) { + if (debug != null) { + debug.println("Returning certificates from cache"); + } + return getMatchingCerts(certs, selector); + } + lastChecked = time; + try { + URLConnection connection = uri.toURL().openConnection(); + if (lastModified != 0) { + connection.setIfModifiedSince(lastModified); + } + long oldLastModified = lastModified; + try (InputStream in = connection.getInputStream()) { + lastModified = connection.getLastModified(); + if (oldLastModified != 0) { + if (oldLastModified == lastModified) { + if (debug != null) { + debug.println("Not modified, using cached copy"); + } + return getMatchingCerts(certs, selector); + } else if (connection instanceof HttpURLConnection) { + // some proxy servers omit last modified + HttpURLConnection hconn = (HttpURLConnection)connection; + if (hconn.getResponseCode() + == HttpURLConnection.HTTP_NOT_MODIFIED) { + if (debug != null) { + debug.println("Not modified, using cached copy"); + } + return getMatchingCerts(certs, selector); + } + } + } + if (debug != null) { + debug.println("Downloading new certificates..."); + } + // Safe cast since factory is an X.509 certificate factory + certs = (Collection) + factory.generateCertificates(in); + } + return getMatchingCerts(certs, selector); + } catch (IOException | CertificateException e) { + if (debug != null) { + debug.println("Exception fetching certificates:"); + e.printStackTrace(); + } + } + // exception, forget previous values + lastModified = 0; + certs = Collections.emptySet(); + return certs; + } + + /** + * Iterates over the specified Collection of X509Certificates and + * returns only those that match the criteria specified in the + * CertSelector. + */ + private static Collection getMatchingCerts + (Collection certs, CertSelector selector) { + // if selector not specified, all certs match + if (selector == null) { + return certs; + } + List matchedCerts = new ArrayList<>(certs.size()); + for (X509Certificate cert : certs) { + if (selector.match(cert)) { + matchedCerts.add(cert); + } + } + return matchedCerts; + } + + /** + * Returns a Collection of X509CRLs that + * match the specified selector. If no X509CRLs + * match the selector, an empty Collection will be returned. + * + * @param selector A CRLSelector used to select which + * X509CRLs should be returned. Specify null + * to return all X509CRLs. + * @return A Collection of X509CRLs that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + @Override + @SuppressWarnings("unchecked") + public synchronized Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException { + + // if ldap URI we wrap the CRLSelector in an LDAPCRLSelector to + // avoid LDAP DN matching issues (see LDAPCRLSelector for more info) + if (ldap) { + X509CRLSelector xsel = (X509CRLSelector) selector; + try { + xsel = ldapHelper.wrap(xsel, null, ldapPath); + } catch (IOException ioe) { + throw new CertStoreException(ioe); + } + // Fetch the CRLs via LDAP. LDAPCertStore has its own + // caching mechanism, see the class description for more info. + // Safe cast since xsel is an X509 certificate selector. + try { + return (Collection) ldapCertStore.getCRLs(xsel); + } catch (CertStoreException cse) { + throw new PKIX.CertStoreTypeException("LDAP", cse); + } + } + + // Return the CRLs for this entry. It returns the cached value + // if it is still current and fetches the CRLs otherwise. + // For the caching details, see the top of this class. + long time = System.currentTimeMillis(); + if (time - lastChecked < CHECK_INTERVAL) { + if (debug != null) { + debug.println("Returning CRL from cache"); + } + return getMatchingCRLs(crl, selector); + } + lastChecked = time; + try { + URLConnection connection = uri.toURL().openConnection(); + if (lastModified != 0) { + connection.setIfModifiedSince(lastModified); + } + long oldLastModified = lastModified; + connection.setConnectTimeout(CRL_CONNECT_TIMEOUT); + try (InputStream in = connection.getInputStream()) { + lastModified = connection.getLastModified(); + if (oldLastModified != 0) { + if (oldLastModified == lastModified) { + if (debug != null) { + debug.println("Not modified, using cached copy"); + } + return getMatchingCRLs(crl, selector); + } else if (connection instanceof HttpURLConnection) { + // some proxy servers omit last modified + HttpURLConnection hconn = (HttpURLConnection)connection; + if (hconn.getResponseCode() + == HttpURLConnection.HTTP_NOT_MODIFIED) { + if (debug != null) { + debug.println("Not modified, using cached copy"); + } + return getMatchingCRLs(crl, selector); + } + } + } + if (debug != null) { + debug.println("Downloading new CRL..."); + } + crl = (X509CRL) factory.generateCRL(in); + } + return getMatchingCRLs(crl, selector); + } catch (IOException | CRLException e) { + if (debug != null) { + debug.println("Exception fetching CRL:"); + e.printStackTrace(); + } + // exception, forget previous values + lastModified = 0; + crl = null; + throw new PKIX.CertStoreTypeException("URI", + new CertStoreException(e)); + } + } + + /** + * Checks if the specified X509CRL matches the criteria specified in the + * CRLSelector. + */ + private static Collection getMatchingCRLs + (X509CRL crl, CRLSelector selector) { + if (selector == null || (crl != null && selector.match(crl))) { + return Collections.singletonList(crl); + } else { + return Collections.emptyList(); + } + } + + /** + * CertStoreParameters for the URICertStore. + */ + static class URICertStoreParameters implements CertStoreParameters { + private final URI uri; + private volatile int hashCode = 0; + URICertStoreParameters(URI uri) { + this.uri = uri; + } + @Override public boolean equals(Object obj) { + if (!(obj instanceof URICertStoreParameters)) { + return false; + } + URICertStoreParameters params = (URICertStoreParameters) obj; + return uri.equals(params.uri); + } + @Override public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + uri.hashCode(); + hashCode = result; + } + return hashCode; + } + @Override public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + /* Cannot happen */ + throw new InternalError(e.toString(), e); + } + } + } + + /** + * This class allows the URICertStore to be accessed as a CertStore. + */ + private static class UCS extends CertStore { + protected UCS(CertStoreSpi spi, Provider p, String type, + CertStoreParameters params) { + super(spi, p, type, params); + } + } +} diff --git a/src/sun/security/provider/certpath/UntrustedChecker.java b/src/sun/security/provider/certpath/UntrustedChecker.java new file mode 100644 index 00000000..f52252cd --- /dev/null +++ b/src/sun/security/provider/certpath/UntrustedChecker.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertPathValidatorException; +import java.security.cert.PKIXCertPathChecker; +import java.util.Set; +import java.util.Collection; +import sun.security.util.Debug; +import sun.security.util.UntrustedCertificates; + +/** + * A PKIXCertPathChecker implementation to check whether a + * specified certificate is distrusted. + * + * @see PKIXCertPathChecker + * @see PKIXParameters + */ +final public class UntrustedChecker extends PKIXCertPathChecker { + + private static final Debug debug = Debug.getInstance("certpath"); + + /** + * Default Constructor + */ + public UntrustedChecker() { + // blank + } + + @Override + public void init(boolean forward) throws CertPathValidatorException { + // Note that this class supports both forward and reverse modes. + } + + @Override + public boolean isForwardCheckingSupported() { + // Note that this class supports both forward and reverse modes. + return true; + } + + @Override + public Set getSupportedExtensions() { + return null; + } + + @Override + public void check(Certificate cert, + Collection unresolvedCritExts) + throws CertPathValidatorException { + + X509Certificate currCert = (X509Certificate)cert; + + if (UntrustedCertificates.isUntrusted(currCert)) { + if (debug != null) { + debug.println("UntrustedChecker: untrusted certificate " + + currCert.getSubjectX500Principal()); + } + + throw new CertPathValidatorException( + "Untrusted certificate: " + currCert.getSubjectX500Principal()); + } + } +} + diff --git a/src/sun/security/provider/certpath/Vertex.java b/src/sun/security/provider/certpath/Vertex.java new file mode 100644 index 00000000..e2503f01 --- /dev/null +++ b/src/sun/security/provider/certpath/Vertex.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import sun.security.util.Debug; +import sun.security.x509.AuthorityKeyIdentifierExtension; +import sun.security.x509.KeyIdentifier; +import sun.security.x509.SubjectKeyIdentifierExtension; +import sun.security.x509.X509CertImpl; + +/* + * This class represents a vertex in the adjacency list. A + * vertex in the builder's view is just a distinguished name + * in the directory. The Vertex contains a certificate + * along an attempted certification path, along with a pointer + * to a list of certificates that followed this one in various + * attempted certification paths. + * + * @author Sean Mullan + * @since 1.4 + */ +public class Vertex { + + private static final Debug debug = Debug.getInstance("certpath"); + private X509Certificate cert; + private int index; + private Throwable throwable; + + /** + * Constructor; creates vertex with index of -1 + * Use setIndex method to set another index. + * + * @param cert X509Certificate associated with vertex + */ + Vertex(X509Certificate cert) { + this.cert = cert; + this.index = -1; + } + + /** + * return the certificate for this vertex + * + * @returns X509Certificate + */ + public X509Certificate getCertificate() { + return cert; + } + + /** + * get the index for this vertex, where the index is the row of the + * adjacency list that contains certificates that could follow this + * certificate. + * + * @returns int index for this vertex, or -1 if no following certificates. + */ + public int getIndex() { + return index; + } + + /** + * set the index for this vertex, where the index is the row of the + * adjacency list that contains certificates that could follow this + * certificate. + * + * @param ndx int index for vertex, or -1 if no following certificates. + */ + void setIndex(int ndx) { + index = ndx; + } + + /** + * return the throwable associated with this vertex; + * returns null if none. + * + * @returns Throwable + */ + public Throwable getThrowable() { + return throwable; + } + + /** + * set throwable associated with this vertex; default value is null. + * + * @param throwable Throwable associated with this vertex + * (or null) + */ + void setThrowable(Throwable throwable) { + this.throwable = throwable; + } + + /** + * Return full string representation of vertex + * + * @returns String representation of vertex + */ + @Override + public String toString() { + return certToString() + throwableToString() + indexToString(); + } + + /** + * Return string representation of this vertex's + * certificate information. + * + * @returns String representation of certificate info + */ + public String certToString() { + StringBuilder sb = new StringBuilder(); + + X509CertImpl x509Cert = null; + try { + x509Cert = X509CertImpl.toImpl(cert); + } catch (CertificateException ce) { + if (debug != null) { + debug.println("Vertex.certToString() unexpected exception"); + ce.printStackTrace(); + } + return sb.toString(); + } + + sb.append("Issuer: ").append + (x509Cert.getIssuerX500Principal()).append("\n"); + sb.append("Subject: ").append + (x509Cert.getSubjectX500Principal()).append("\n"); + sb.append("SerialNum: ").append + (x509Cert.getSerialNumber().toString(16)).append("\n"); + sb.append("Expires: ").append + (x509Cert.getNotAfter().toString()).append("\n"); + boolean[] iUID = x509Cert.getIssuerUniqueID(); + if (iUID != null) { + sb.append("IssuerUID: "); + for (boolean b : iUID) { + sb.append(b ? 1 : 0); + } + sb.append("\n"); + } + boolean[] sUID = x509Cert.getSubjectUniqueID(); + if (sUID != null) { + sb.append("SubjectUID: "); + for (boolean b : sUID) { + sb.append(b ? 1 : 0); + } + sb.append("\n"); + } + try { + SubjectKeyIdentifierExtension sKeyID = + x509Cert.getSubjectKeyIdentifierExtension(); + if (sKeyID != null) { + KeyIdentifier keyID = sKeyID.get( + SubjectKeyIdentifierExtension.KEY_ID); + sb.append("SubjKeyID: ").append(keyID.toString()); + } + AuthorityKeyIdentifierExtension aKeyID = + x509Cert.getAuthorityKeyIdentifierExtension(); + if (aKeyID != null) { + KeyIdentifier keyID = (KeyIdentifier)aKeyID.get( + AuthorityKeyIdentifierExtension.KEY_ID); + sb.append("AuthKeyID: ").append(keyID.toString()); + } + } catch (IOException e) { + if (debug != null) { + debug.println("Vertex.certToString() unexpected exception"); + e.printStackTrace(); + } + } + return sb.toString(); + } + + /** + * return Vertex throwable as String compatible with + * the way toString returns other information + * + * @returns String form of exception (or "none") + */ + public String throwableToString() { + StringBuilder sb = new StringBuilder("Exception: "); + if (throwable != null) + sb.append(throwable.toString()); + else + sb.append("null"); + sb.append("\n"); + return sb.toString(); + } + + /** + * return Vertex index as String compatible with + * the way other Vertex.xToString() methods display + * information. + * + * @returns String form of index as "Last cert? [Yes/No] + */ + public String moreToString() { + StringBuilder sb = new StringBuilder("Last cert? "); + sb.append((index == -1) ? "Yes" : "No"); + sb.append("\n"); + return sb.toString(); + } + + /** + * return Vertex index as String compatible with + * the way other Vertex.xToString() methods displays other information. + * + * @returns String form of index as "Index: [numeric index]" + */ + public String indexToString() { + return "Index: " + index + "\n"; + } +} diff --git a/src/sun/security/provider/certpath/X509CertPath.java b/src/sun/security/provider/certpath/X509CertPath.java new file mode 100644 index 00000000..81ccf2c4 --- /dev/null +++ b/src/sun/security/provider/certpath/X509CertPath.java @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.CertificateEncodingException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertPath; +import java.security.cert.X509Certificate; +import java.util.*; + +import sun.security.pkcs.ContentInfo; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; +import sun.security.x509.AlgorithmId; +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; +import sun.security.util.DerInputStream; + +/** + * A {@link CertPath CertPath} (certification path) + * consisting exclusively of + * {@link X509Certificate X509Certificate}s. + *

    + * By convention, X.509 CertPaths are stored from target + * to trust anchor. + * That is, the issuer of one certificate is the subject of the following + * one. However, unvalidated X.509 CertPaths may not follow + * this convention. PKIX CertPathValidators will detect any + * departure from this convention and throw a + * CertPathValidatorException. + * + * @author Yassir Elley + * @since 1.4 + */ +public class X509CertPath extends CertPath { + + private static final long serialVersionUID = 4989800333263052980L; + + /** + * List of certificates in this chain + */ + private List certs; + + /** + * The names of our encodings. PkiPath is the default. + */ + private static final String COUNT_ENCODING = "count"; + private static final String PKCS7_ENCODING = "PKCS7"; + private static final String PKIPATH_ENCODING = "PkiPath"; + + /** + * List of supported encodings + */ + private static final Collection encodingList; + + static { + List list = new ArrayList<>(2); + list.add(PKIPATH_ENCODING); + list.add(PKCS7_ENCODING); + encodingList = Collections.unmodifiableCollection(list); + } + + /** + * Creates an X509CertPath from a List of + * X509Certificates. + *

    + * The certificates are copied out of the supplied List + * object. + * + * @param certs a List of X509Certificates + * @exception CertificateException if certs contains an element + * that is not an X509Certificate + */ + @SuppressWarnings("unchecked") + public X509CertPath(List certs) throws CertificateException { + super("X.509"); + + // Ensure that the List contains only X509Certificates + // + // Note; The certs parameter is not necessarily to be of Certificate + // for some old code. For compatibility, to make sure the exception + // is CertificateException, rather than ClassCastException, please + // don't use + // for (Certificate obj : certs) + for (Object obj : certs) { + if (obj instanceof X509Certificate == false) { + throw new CertificateException + ("List is not all X509Certificates: " + + obj.getClass().getName()); + } + } + + // Assumes that the resulting List is thread-safe. This is true + // because we ensure that it cannot be modified after construction + // and the methods in the Sun JDK 1.4 implementation of ArrayList that + // allow read-only access are thread-safe. + this.certs = Collections.unmodifiableList( + new ArrayList((List)certs)); + } + + /** + * Creates an X509CertPath, reading the encoded form + * from an InputStream. The data is assumed to be in + * the default encoding. + * + * @param is the InputStream to read the data from + * @exception CertificateException if an exception occurs while decoding + */ + public X509CertPath(InputStream is) throws CertificateException { + this(is, PKIPATH_ENCODING); + } + + /** + * Creates an X509CertPath, reading the encoded form + * from an InputStream. The data is assumed to be in the specified + * encoding. + * + * @param is the InputStream to read the data from + * @param encoding the encoding used + * @exception CertificateException if an exception occurs while decoding or + * the encoding requested is not supported + */ + public X509CertPath(InputStream is, String encoding) + throws CertificateException { + super("X.509"); + + switch (encoding) { + case PKIPATH_ENCODING: + certs = parsePKIPATH(is); + break; + case PKCS7_ENCODING: + certs = parsePKCS7(is); + break; + default: + throw new CertificateException("unsupported encoding"); + } + } + + /** + * Parse a PKIPATH format CertPath from an InputStream. Return an + * unmodifiable List of the certificates. + * + * @param is the InputStream to read the data from + * @return an unmodifiable List of the certificates + * @exception CertificateException if an exception occurs + */ + private static List parsePKIPATH(InputStream is) + throws CertificateException { + List certList = null; + CertificateFactory certFac = null; + + if (is == null) { + throw new CertificateException("input stream is null"); + } + + try { + DerInputStream dis = new DerInputStream(readAllBytes(is)); + DerValue[] seq = dis.getSequence(3); + if (seq.length == 0) { + return Collections.emptyList(); + } + + certFac = CertificateFactory.getInstance("X.509"); + certList = new ArrayList(seq.length); + + // append certs in reverse order (target to trust anchor) + for (int i = seq.length-1; i >= 0; i--) { + certList.add((X509Certificate)certFac.generateCertificate + (new ByteArrayInputStream(seq[i].toByteArray()))); + } + + return Collections.unmodifiableList(certList); + + } catch (IOException ioe) { + throw new CertificateException("IOException parsing PkiPath data: " + + ioe, ioe); + } + } + + /** + * Parse a PKCS#7 format CertPath from an InputStream. Return an + * unmodifiable List of the certificates. + * + * @param is the InputStream to read the data from + * @return an unmodifiable List of the certificates + * @exception CertificateException if an exception occurs + */ + private static List parsePKCS7(InputStream is) + throws CertificateException { + List certList; + + if (is == null) { + throw new CertificateException("input stream is null"); + } + + try { + if (is.markSupported() == false) { + // Copy the entire input stream into an InputStream that does + // support mark + is = new ByteArrayInputStream(readAllBytes(is)); + } + PKCS7 pkcs7 = new PKCS7(is); + + X509Certificate[] certArray = pkcs7.getCertificates(); + // certs are optional in PKCS #7 + if (certArray != null) { + certList = Arrays.asList(certArray); + } else { + // no certs provided + certList = new ArrayList(0); + } + } catch (IOException ioe) { + throw new CertificateException("IOException parsing PKCS7 data: " + + ioe); + } + // Assumes that the resulting List is thread-safe. This is true + // because we ensure that it cannot be modified after construction + // and the methods in the Sun JDK 1.4 implementation of ArrayList that + // allow read-only access are thread-safe. + return Collections.unmodifiableList(certList); + } + + /* + * Reads the entire contents of an InputStream into a byte array. + * + * @param is the InputStream to read from + * @return the bytes read from the InputStream + */ + private static byte[] readAllBytes(InputStream is) throws IOException { + byte[] buffer = new byte[8192]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); + int n; + while ((n = is.read(buffer)) != -1) { + baos.write(buffer, 0, n); + } + return baos.toByteArray(); + } + + /** + * Returns the encoded form of this certification path, using the + * default encoding. + * + * @return the encoded bytes + * @exception CertificateEncodingException if an encoding error occurs + */ + @Override + public byte[] getEncoded() throws CertificateEncodingException { + // @@@ Should cache the encoded form + return encodePKIPATH(); + } + + /** + * Encode the CertPath using PKIPATH format. + * + * @return a byte array containing the binary encoding of the PkiPath object + * @exception CertificateEncodingException if an exception occurs + */ + private byte[] encodePKIPATH() throws CertificateEncodingException { + + ListIterator li = certs.listIterator(certs.size()); + try { + DerOutputStream bytes = new DerOutputStream(); + // encode certs in reverse order (trust anchor to target) + // according to PkiPath format + while (li.hasPrevious()) { + X509Certificate cert = li.previous(); + // check for duplicate cert + if (certs.lastIndexOf(cert) != certs.indexOf(cert)) { + throw new CertificateEncodingException + ("Duplicate Certificate"); + } + // get encoded certificates + byte[] encoded = cert.getEncoded(); + bytes.write(encoded); + } + + // Wrap the data in a SEQUENCE + DerOutputStream derout = new DerOutputStream(); + derout.write(DerValue.tag_SequenceOf, bytes); + return derout.toByteArray(); + + } catch (IOException ioe) { + throw new CertificateEncodingException("IOException encoding " + + "PkiPath data: " + ioe, ioe); + } + } + + /** + * Encode the CertPath using PKCS#7 format. + * + * @return a byte array containing the binary encoding of the PKCS#7 object + * @exception CertificateEncodingException if an exception occurs + */ + private byte[] encodePKCS7() throws CertificateEncodingException { + PKCS7 p7 = new PKCS7(new AlgorithmId[0], + new ContentInfo(ContentInfo.DATA_OID, null), + certs.toArray(new X509Certificate[certs.size()]), + new SignerInfo[0]); + DerOutputStream derout = new DerOutputStream(); + try { + p7.encodeSignedData(derout); + } catch (IOException ioe) { + throw new CertificateEncodingException(ioe.getMessage()); + } + return derout.toByteArray(); + } + + /** + * Returns the encoded form of this certification path, using the + * specified encoding. + * + * @param encoding the name of the encoding to use + * @return the encoded bytes + * @exception CertificateEncodingException if an encoding error occurs or + * the encoding requested is not supported + */ + @Override + public byte[] getEncoded(String encoding) + throws CertificateEncodingException { + switch (encoding) { + case PKIPATH_ENCODING: + return encodePKIPATH(); + case PKCS7_ENCODING: + return encodePKCS7(); + default: + throw new CertificateEncodingException("unsupported encoding"); + } + } + + /** + * Returns the encodings supported by this certification path, with the + * default encoding first. + * + * @return an Iterator over the names of the supported + * encodings (as Strings) + */ + public static Iterator getEncodingsStatic() { + return encodingList.iterator(); + } + + /** + * Returns an iteration of the encodings supported by this certification + * path, with the default encoding first. + *

    + * Attempts to modify the returned Iterator via its + * remove method result in an + * UnsupportedOperationException. + * + * @return an Iterator over the names of the supported + * encodings (as Strings) + */ + @Override + public Iterator getEncodings() { + return getEncodingsStatic(); + } + + /** + * Returns the list of certificates in this certification path. + * The List returned must be immutable and thread-safe. + * + * @return an immutable List of X509Certificates + * (may be empty, but not null) + */ + @Override + public List getCertificates() { + return certs; + } +} diff --git a/src/sun/security/provider/certpath/X509CertificatePair.java b/src/sun/security/provider/certpath/X509CertificatePair.java new file mode 100644 index 00000000..ea139dd0 --- /dev/null +++ b/src/sun/security/provider/certpath/X509CertificatePair.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.provider.certpath; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.DSAPublicKey; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.Cache; +import sun.security.x509.X509CertImpl; +import sun.security.provider.X509Factory; + +/** + * This class represents an X.509 Certificate Pair object, which is primarily + * used to hold a pair of cross certificates issued between Certification + * Authorities. The ASN.1 structure is listed below. The forward certificate + * of the CertificatePair contains a certificate issued to this CA by another + * CA. The reverse certificate of the CertificatePair contains a certificate + * issued by this CA to another CA. When both the forward and the reverse + * certificates are present in the CertificatePair, the issuer name in one + * certificate shall match the subject name in the other and vice versa, and + * the subject public key in one certificate shall be capable of verifying the + * digital signature on the other certificate and vice versa. If a subject + * public key in one certificate does not contain required key algorithm + * parameters, then the signature check involving that key is not done.

    + * + * The ASN.1 syntax for this object is: + *

    + * CertificatePair      ::=     SEQUENCE {
    + *      forward [0]     Certificate OPTIONAL,
    + *      reverse [1]     Certificate OPTIONAL
    + *                      -- at least one of the pair shall be present -- }
    + * 

    + * + * This structure uses EXPLICIT tagging. References: Annex A of + * X.509(2000), X.509(1997). + * + * @author Sean Mullan + * @since 1.4 + */ + +public class X509CertificatePair { + + /* ASN.1 explicit tags */ + private static final byte TAG_FORWARD = 0; + private static final byte TAG_REVERSE = 1; + + private X509Certificate forward; + private X509Certificate reverse; + private byte[] encoded; + + private static final Cache cache + = Cache.newSoftMemoryCache(750); + + /** + * Creates an empty instance of X509CertificatePair. + */ + public X509CertificatePair() {} + + /** + * Creates an instance of X509CertificatePair. At least one of + * the pair must be non-null. + * + * @param forward The forward component of the certificate pair + * which represents a certificate issued to this CA by other CAs. + * @param reverse The reverse component of the certificate pair + * which represents a certificate issued by this CA to other CAs. + * @throws CertificateException If an exception occurs. + */ + public X509CertificatePair(X509Certificate forward, X509Certificate reverse) + throws CertificateException { + if (forward == null && reverse == null) { + throw new CertificateException("at least one of certificate pair " + + "must be non-null"); + } + + this.forward = forward; + this.reverse = reverse; + + checkPair(); + } + + /** + * Create a new X509CertificatePair from its encoding. + * + * For internal use only, external code should use generateCertificatePair. + */ + private X509CertificatePair(byte[] encoded) throws CertificateException { + try { + parse(new DerValue(encoded)); + this.encoded = encoded; + } catch (IOException ex) { + throw new CertificateException(ex.toString()); + } + checkPair(); + } + + /** + * Clear the cache for debugging. + */ + public static synchronized void clearCache() { + cache.clear(); + } + + /** + * Create a X509CertificatePair from its encoding. Uses cache lookup + * if possible. + */ + public static synchronized X509CertificatePair generateCertificatePair + (byte[] encoded) throws CertificateException { + Object key = new Cache.EqualByteArray(encoded); + X509CertificatePair pair = cache.get(key); + if (pair != null) { + return pair; + } + pair = new X509CertificatePair(encoded); + key = new Cache.EqualByteArray(pair.encoded); + cache.put(key, pair); + return pair; + } + + /** + * Sets the forward component of the certificate pair. + */ + public void setForward(X509Certificate cert) throws CertificateException { + checkPair(); + forward = cert; + } + + /** + * Sets the reverse component of the certificate pair. + */ + public void setReverse(X509Certificate cert) throws CertificateException { + checkPair(); + reverse = cert; + } + + /** + * Returns the forward component of the certificate pair. + * + * @return The forward certificate, or null if not set. + */ + public X509Certificate getForward() { + return forward; + } + + /** + * Returns the reverse component of the certificate pair. + * + * @return The reverse certificate, or null if not set. + */ + public X509Certificate getReverse() { + return reverse; + } + + /** + * Return the DER encoded form of the certificate pair. + * + * @return The encoded form of the certificate pair. + * @throws CerticateEncodingException If an encoding exception occurs. + */ + public byte[] getEncoded() throws CertificateEncodingException { + try { + if (encoded == null) { + DerOutputStream tmp = new DerOutputStream(); + emit(tmp); + encoded = tmp.toByteArray(); + } + } catch (IOException ex) { + throw new CertificateEncodingException(ex.toString()); + } + return encoded; + } + + /** + * Return a printable representation of the certificate pair. + * + * @return A String describing the contents of the pair. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("X.509 Certificate Pair: [\n"); + if (forward != null) + sb.append(" Forward: ").append(forward).append("\n"); + if (reverse != null) + sb.append(" Reverse: ").append(reverse).append("\n"); + sb.append("]"); + return sb.toString(); + } + + /* Parse the encoded bytes */ + private void parse(DerValue val) + throws IOException, CertificateException + { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException + ("Sequence tag missing for X509CertificatePair"); + } + + while (val.data != null && val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + short tag = (byte) (opt.tag & 0x01f); + switch (tag) { + case TAG_FORWARD: + if (opt.isContextSpecific() && opt.isConstructed()) { + if (forward != null) { + throw new IOException("Duplicate forward " + + "certificate in X509CertificatePair"); + } + opt = opt.data.getDerValue(); + forward = X509Factory.intern + (new X509CertImpl(opt.toByteArray())); + } + break; + case TAG_REVERSE: + if (opt.isContextSpecific() && opt.isConstructed()) { + if (reverse != null) { + throw new IOException("Duplicate reverse " + + "certificate in X509CertificatePair"); + } + opt = opt.data.getDerValue(); + reverse = X509Factory.intern + (new X509CertImpl(opt.toByteArray())); + } + break; + default: + throw new IOException("Invalid encoding of " + + "X509CertificatePair"); + } + } + if (forward == null && reverse == null) { + throw new CertificateException("at least one of certificate pair " + + "must be non-null"); + } + } + + /* Translate to encoded bytes */ + private void emit(DerOutputStream out) + throws IOException, CertificateEncodingException + { + DerOutputStream tagged = new DerOutputStream(); + + if (forward != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putDerValue(new DerValue(forward.getEncoded())); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_FORWARD), tmp); + } + + if (reverse != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putDerValue(new DerValue(reverse.getEncoded())); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_REVERSE), tmp); + } + + out.write(DerValue.tag_Sequence, tagged); + } + + /* + * Check for a valid certificate pair + */ + private void checkPair() throws CertificateException { + + /* if either of pair is missing, return w/o error */ + if (forward == null || reverse == null) { + return; + } + /* + * If both elements of the pair are present, check that they + * are a valid pair. + */ + X500Principal fwSubject = forward.getSubjectX500Principal(); + X500Principal fwIssuer = forward.getIssuerX500Principal(); + X500Principal rvSubject = reverse.getSubjectX500Principal(); + X500Principal rvIssuer = reverse.getIssuerX500Principal(); + if (!fwIssuer.equals(rvSubject) || !rvIssuer.equals(fwSubject)) { + throw new CertificateException("subject and issuer names in " + + "forward and reverse certificates do not match"); + } + + /* check signatures unless key parameters are missing */ + try { + PublicKey pk = reverse.getPublicKey(); + if (!(pk instanceof DSAPublicKey) || + ((DSAPublicKey)pk).getParams() != null) { + forward.verify(pk); + } + pk = forward.getPublicKey(); + if (!(pk instanceof DSAPublicKey) || + ((DSAPublicKey)pk).getParams() != null) { + reverse.verify(pk); + } + } catch (GeneralSecurityException e) { + throw new CertificateException("invalid signature: " + + e.getMessage()); + } + } +} diff --git a/src/sun/security/provider/certpath/ldap/LDAPCertStore.java b/src/sun/security/provider/certpath/ldap/LDAPCertStore.java new file mode 100644 index 00000000..3c4596df --- /dev/null +++ b/src/sun/security/provider/certpath/ldap/LDAPCertStore.java @@ -0,0 +1,1086 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath.ldap; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.util.*; +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.NameNotFoundException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; + +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.*; +import javax.security.auth.x500.X500Principal; + +import sun.misc.HexDumpEncoder; +import sun.security.provider.certpath.X509CertificatePair; +import sun.security.util.Cache; +import sun.security.util.Debug; +import sun.security.x509.X500Name; +import sun.security.action.GetBooleanAction; +import sun.security.action.GetPropertyAction; + +/** + * A CertStore that retrieves Certificates and + * CRLs from an LDAP directory, using the PKIX LDAP V2 Schema + * (RFC 2587): + * + * http://www.ietf.org/rfc/rfc2587.txt. + *

    + * Before calling the {@link #engineGetCertificates engineGetCertificates} or + * {@link #engineGetCRLs engineGetCRLs} methods, the + * {@link #LDAPCertStore(CertStoreParameters) + * LDAPCertStore(CertStoreParameters)} constructor is called to create the + * CertStore and establish the DNS name and port of the LDAP + * server from which Certificates and CRLs will be + * retrieved. + *

    + * Concurrent Access + *

    + * As described in the javadoc for CertStoreSpi, the + * engineGetCertificates and engineGetCRLs methods + * must be thread-safe. That is, multiple threads may concurrently + * invoke these methods on a single LDAPCertStore object + * (or more than one) with no ill effects. This allows a + * CertPathBuilder to search for a CRL while simultaneously + * searching for further certificates, for instance. + *

    + * This is achieved by adding the synchronized keyword to the + * engineGetCertificates and engineGetCRLs methods. + *

    + * This classes uses caching and requests multiple attributes at once to + * minimize LDAP round trips. The cache is associated with the CertStore + * instance. It uses soft references to hold the values to minimize impact + * on footprint and currently has a maximum size of 750 attributes and a + * 30 second default lifetime. + *

    + * We always request CA certificates, cross certificate pairs, and ARLs in + * a single LDAP request when any one of them is needed. The reason is that + * we typically need all of them anyway and requesting them in one go can + * reduce the number of requests to a third. Even if we don't need them, + * these attributes are typically small enough not to cause a noticeable + * overhead. In addition, when the prefetchCRLs flag is true, we also request + * the full CRLs. It is currently false initially but set to true once any + * request for an ARL to the server returns an null value. The reason is + * that CRLs could be rather large but are rarely used. This implementation + * should improve performance in most cases. + * + * @see CertStore + * + * @since 1.4 + * @author Steve Hanna + * @author Andreas Sterbenz + */ +public final class LDAPCertStore extends CertStoreSpi { + + private static final Debug debug = Debug.getInstance("certpath"); + + private final static boolean DEBUG = false; + + /** + * LDAP attribute identifiers. + */ + private static final String USER_CERT = "userCertificate;binary"; + private static final String CA_CERT = "cACertificate;binary"; + private static final String CROSS_CERT = "crossCertificatePair;binary"; + private static final String CRL = "certificateRevocationList;binary"; + private static final String ARL = "authorityRevocationList;binary"; + private static final String DELTA_CRL = "deltaRevocationList;binary"; + + // Constants for various empty values + private final static String[] STRING0 = new String[0]; + + private final static byte[][] BB0 = new byte[0][]; + + private final static Attributes EMPTY_ATTRIBUTES = new BasicAttributes(); + + // cache related constants + private final static int DEFAULT_CACHE_SIZE = 750; + private final static int DEFAULT_CACHE_LIFETIME = 30; + + private final static int LIFETIME; + + private final static String PROP_LIFETIME = + "sun.security.certpath.ldap.cache.lifetime"; + + /* + * Internal system property, that when set to "true", disables the + * JNDI application resource files lookup to prevent recursion issues + * when validating signed JARs with LDAP URLs in certificates. + */ + private final static String PROP_DISABLE_APP_RESOURCE_FILES = + "sun.security.certpath.ldap.disable.app.resource.files"; + + static { + String s = AccessController.doPrivileged( + new GetPropertyAction(PROP_LIFETIME)); + if (s != null) { + LIFETIME = Integer.parseInt(s); // throws NumberFormatException + } else { + LIFETIME = DEFAULT_CACHE_LIFETIME; + } + } + + /** + * The CertificateFactory used to decode certificates from + * their binary stored form. + */ + private CertificateFactory cf; + /** + * The JNDI directory context. + */ + private DirContext ctx; + + /** + * Flag indicating whether we should prefetch CRLs. + */ + private boolean prefetchCRLs = false; + + private final Cache valueCache; + + private int cacheHits = 0; + private int cacheMisses = 0; + private int requests = 0; + + /** + * Creates a CertStore with the specified parameters. + * For this class, the parameters object must be an instance of + * LDAPCertStoreParameters. + * + * @param params the algorithm parameters + * @exception InvalidAlgorithmParameterException if params is not an + * instance of LDAPCertStoreParameters + */ + public LDAPCertStore(CertStoreParameters params) + throws InvalidAlgorithmParameterException { + super(params); + if (!(params instanceof LDAPCertStoreParameters)) + throw new InvalidAlgorithmParameterException( + "parameters must be LDAPCertStoreParameters"); + + LDAPCertStoreParameters lparams = (LDAPCertStoreParameters) params; + + // Create InitialDirContext needed to communicate with the server + createInitialDirContext(lparams.getServerName(), lparams.getPort()); + + // Create CertificateFactory for use later on + try { + cf = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new InvalidAlgorithmParameterException( + "unable to create CertificateFactory for X.509"); + } + if (LIFETIME == 0) { + valueCache = Cache.newNullCache(); + } else if (LIFETIME < 0) { + valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE); + } else { + valueCache = Cache.newSoftMemoryCache(DEFAULT_CACHE_SIZE, LIFETIME); + } + } + + /** + * Returns an LDAP CertStore. This method consults a cache of + * CertStores (shared per JVM) using the LDAP server/port as a key. + */ + private static final Cache + certStoreCache = Cache.newSoftMemoryCache(185); + static synchronized CertStore getInstance(LDAPCertStoreParameters params) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + CertStore lcs = certStoreCache.get(params); + if (lcs == null) { + lcs = CertStore.getInstance("LDAP", params); + certStoreCache.put(params, lcs); + } else { + if (debug != null) { + debug.println("LDAPCertStore.getInstance: cache hit"); + } + } + return lcs; + } + + /** + * Create InitialDirContext. + * + * @param server Server DNS name hosting LDAP service + * @param port Port at which server listens for requests + * @throws InvalidAlgorithmParameterException if creation fails + */ + private void createInitialDirContext(String server, int port) + throws InvalidAlgorithmParameterException { + String url = "ldap://" + server + ":" + port; + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, + "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, url); + + // If property is set to true, disable application resource file lookup. + boolean disableAppResourceFiles = AccessController.doPrivileged( + new GetBooleanAction(PROP_DISABLE_APP_RESOURCE_FILES)); + if (disableAppResourceFiles) { + if (debug != null) { + debug.println("LDAPCertStore disabling app resource files"); + } + env.put("com.sun.naming.disable.app.resource.files", "true"); + } + + try { + ctx = new InitialDirContext(env); + /* + * By default, follow referrals unless application has + * overridden property in an application resource file. + */ + Hashtable currentEnv = ctx.getEnvironment(); + if (currentEnv.get(Context.REFERRAL) == null) { + ctx.addToEnvironment(Context.REFERRAL, "follow"); + } + } catch (NamingException e) { + if (debug != null) { + debug.println("LDAPCertStore.engineInit about to throw " + + "InvalidAlgorithmParameterException"); + e.printStackTrace(); + } + Exception ee = new InvalidAlgorithmParameterException + ("unable to create InitialDirContext using supplied parameters"); + ee.initCause(e); + throw (InvalidAlgorithmParameterException)ee; + } + } + + /** + * Private class encapsulating the actual LDAP operations and cache + * handling. Use: + * + * LDAPRequest request = new LDAPRequest(dn); + * request.addRequestedAttribute(CROSS_CERT); + * request.addRequestedAttribute(CA_CERT); + * byte[][] crossValues = request.getValues(CROSS_CERT); + * byte[][] caValues = request.getValues(CA_CERT); + * + * At most one LDAP request is sent for each instance created. If all + * getValues() calls can be satisfied from the cache, no request + * is sent at all. If a request is sent, all requested attributes + * are always added to the cache irrespective of whether the getValues() + * method is called. + */ + private class LDAPRequest { + + private final String name; + private Map valueMap; + private final List requestedAttributes; + + LDAPRequest(String name) { + this.name = name; + requestedAttributes = new ArrayList<>(5); + } + + String getName() { + return name; + } + + void addRequestedAttribute(String attrId) { + if (valueMap != null) { + throw new IllegalStateException("Request already sent"); + } + requestedAttributes.add(attrId); + } + + /** + * Gets one or more binary values from an attribute. + * + * @param name the location holding the attribute + * @param attrId the attribute identifier + * @return an array of binary values (byte arrays) + * @throws NamingException if a naming exception occurs + */ + byte[][] getValues(String attrId) throws NamingException { + if (DEBUG && ((cacheHits + cacheMisses) % 50 == 0)) { + System.out.println("Cache hits: " + cacheHits + "; misses: " + + cacheMisses); + } + String cacheKey = name + "|" + attrId; + byte[][] values = valueCache.get(cacheKey); + if (values != null) { + cacheHits++; + return values; + } + cacheMisses++; + Map attrs = getValueMap(); + values = attrs.get(attrId); + return values; + } + + /** + * Get a map containing the values for this request. The first time + * this method is called on an object, the LDAP request is sent, + * the results parsed and added to a private map and also to the + * cache of this LDAPCertStore. Subsequent calls return the private + * map immediately. + * + * The map contains an entry for each requested attribute. The + * attribute name is the key, values are byte[][]. If there are no + * values for that attribute, values are byte[0][]. + * + * @return the value Map + * @throws NamingException if a naming exception occurs + */ + private Map getValueMap() throws NamingException { + if (valueMap != null) { + return valueMap; + } + if (DEBUG) { + System.out.println("Request: " + name + ":" + requestedAttributes); + requests++; + if (requests % 5 == 0) { + System.out.println("LDAP requests: " + requests); + } + } + valueMap = new HashMap<>(8); + String[] attrIds = requestedAttributes.toArray(STRING0); + Attributes attrs; + try { + attrs = ctx.getAttributes(name, attrIds); + } catch (NameNotFoundException e) { + // name does not exist on this LDAP server + // treat same as not attributes found + attrs = EMPTY_ATTRIBUTES; + } + for (String attrId : requestedAttributes) { + Attribute attr = attrs.get(attrId); + byte[][] values = getAttributeValues(attr); + cacheAttribute(attrId, values); + valueMap.put(attrId, values); + } + return valueMap; + } + + /** + * Add the values to the cache. + */ + private void cacheAttribute(String attrId, byte[][] values) { + String cacheKey = name + "|" + attrId; + valueCache.put(cacheKey, values); + } + + /** + * Get the values for the given attribute. If the attribute is null + * or does not contain any values, a zero length byte array is + * returned. NOTE that it is assumed that all values are byte arrays. + */ + private byte[][] getAttributeValues(Attribute attr) + throws NamingException { + byte[][] values; + if (attr == null) { + values = BB0; + } else { + values = new byte[attr.size()][]; + int i = 0; + NamingEnumeration enum_ = attr.getAll(); + while (enum_.hasMore()) { + Object obj = enum_.next(); + if (debug != null) { + if (obj instanceof String) { + debug.println("LDAPCertStore.getAttrValues() " + + "enum.next is a string!: " + obj); + } + } + byte[] value = (byte[])obj; + values[i++] = value; + } + } + return values; + } + + } + + /* + * Gets certificates from an attribute id and location in the LDAP + * directory. Returns a Collection containing only the Certificates that + * match the specified CertSelector. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @param sel a CertSelector that the Certificates must match + * @return a Collection of Certificates found + * @throws CertStoreException if an exception occurs + */ + private Collection getCertificates(LDAPRequest request, + String id, X509CertSelector sel) throws CertStoreException { + + /* fetch encoded certs from storage */ + byte[][] encodedCert; + try { + encodedCert = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCert.length; + if (n == 0) { + return Collections.emptySet(); + } + + List certs = new ArrayList<>(n); + /* decode certs and check if they satisfy selector */ + for (int i = 0; i < n; i++) { + ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]); + try { + Certificate cert = cf.generateCertificate(bais); + if (sel.match(cert)) { + certs.add((X509Certificate)cert); + } + } catch (CertificateException e) { + if (debug != null) { + debug.println("LDAPCertStore.getCertificates() encountered " + + "exception while parsing cert, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println( + "[ " + encoder.encodeBuffer(encodedCert[i]) + " ]"); + } + } + } + + return certs; + } + + /* + * Gets certificate pairs from an attribute id and location in the LDAP + * directory. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @return a Collection of X509CertificatePairs found + * @throws CertStoreException if an exception occurs + */ + private Collection getCertPairs( + LDAPRequest request, String id) throws CertStoreException { + + /* fetch the encoded cert pairs from storage */ + byte[][] encodedCertPair; + try { + encodedCertPair = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCertPair.length; + if (n == 0) { + return Collections.emptySet(); + } + + List certPairs = new ArrayList<>(n); + /* decode each cert pair and add it to the Collection */ + for (int i = 0; i < n; i++) { + try { + X509CertificatePair certPair = + X509CertificatePair.generateCertificatePair(encodedCertPair[i]); + certPairs.add(certPair); + } catch (CertificateException e) { + if (debug != null) { + debug.println( + "LDAPCertStore.getCertPairs() encountered exception " + + "while parsing cert, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println( + "[ " + encoder.encodeBuffer(encodedCertPair[i]) + " ]"); + } + } + } + + return certPairs; + } + + /* + * Looks at certificate pairs stored in the crossCertificatePair attribute + * at the specified location in the LDAP directory. Returns a Collection + * containing all Certificates stored in the forward component that match + * the forward CertSelector and all Certificates stored in the reverse + * component that match the reverse CertSelector. + *

    + * If either forward or reverse is null, all certificates from the + * corresponding component will be rejected. + * + * @param name the location to look in + * @param forward the forward CertSelector (or null) + * @param reverse the reverse CertSelector (or null) + * @return a Collection of Certificates found + * @throws CertStoreException if an exception occurs + */ + private Collection getMatchingCrossCerts( + LDAPRequest request, X509CertSelector forward, + X509CertSelector reverse) + throws CertStoreException { + // Get the cert pairs + Collection certPairs = + getCertPairs(request, CROSS_CERT); + + // Find Certificates that match and put them in a list + ArrayList matchingCerts = new ArrayList<>(); + for (X509CertificatePair certPair : certPairs) { + X509Certificate cert; + if (forward != null) { + cert = certPair.getForward(); + if ((cert != null) && forward.match(cert)) { + matchingCerts.add(cert); + } + } + if (reverse != null) { + cert = certPair.getReverse(); + if ((cert != null) && reverse.match(cert)) { + matchingCerts.add(cert); + } + } + } + return matchingCerts; + } + + /** + * Returns a Collection of Certificates that + * match the specified selector. If no Certificates + * match the selector, an empty Collection will be returned. + *

    + * It is not practical to search every entry in the LDAP database for + * matching Certificates. Instead, the CertSelector + * is examined in order to determine where matching Certificates + * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). + * If the subject is specified, its directory entry is searched. If the + * issuer is specified, its directory entry is searched. If neither the + * subject nor the issuer are specified (or the selector is not an + * X509CertSelector), a CertStoreException is + * thrown. + * + * @param selector a CertSelector used to select which + * Certificates should be returned. + * @return a Collection of Certificates that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + public synchronized Collection engineGetCertificates + (CertSelector selector) throws CertStoreException { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() selector: " + + String.valueOf(selector)); + } + + if (selector == null) { + selector = new X509CertSelector(); + } + if (!(selector instanceof X509CertSelector)) { + throw new CertStoreException("LDAPCertStore needs an X509CertSelector " + + "to find certs"); + } + X509CertSelector xsel = (X509CertSelector) selector; + int basicConstraints = xsel.getBasicConstraints(); + String subject = xsel.getSubjectAsString(); + String issuer = xsel.getIssuerAsString(); + HashSet certs = new HashSet<>(); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: " + + basicConstraints); + } + + // basicConstraints: + // -2: only EE certs accepted + // -1: no check is done + // 0: any CA certificate accepted + // >1: certificate's basicConstraints extension pathlen must match + if (subject != null) { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() " + + "subject is not null"); + } + LDAPRequest request = new LDAPRequest(subject); + if (basicConstraints > -2) { + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + } + if (basicConstraints < 0) { + request.addRequestedAttribute(USER_CERT); + } + + if (basicConstraints > -2) { + certs.addAll(getMatchingCrossCerts(request, xsel, null)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getMatchingCrossCerts(subject,xsel,null),certs.size(): " + + certs.size()); + } + certs.addAll(getCertificates(request, CA_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(subject,CA_CERT,xsel),certs.size(): " + + certs.size()); + } + } + if (basicConstraints < 0) { + certs.addAll(getCertificates(request, USER_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(subject,USER_CERT, xsel),certs.size(): " + + certs.size()); + } + } + } else { + if (debug != null) { + debug.println + ("LDAPCertStore.engineGetCertificates() subject is null"); + } + if (basicConstraints == -2) { + throw new CertStoreException("need subject to find EE certs"); + } + if (issuer == null) { + throw new CertStoreException("need subject or issuer to find certs"); + } + } + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() about to " + + "getMatchingCrossCerts..."); + } + if ((issuer != null) && (basicConstraints > -2)) { + LDAPRequest request = new LDAPRequest(issuer); + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + + certs.addAll(getMatchingCrossCerts(request, null, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getMatchingCrossCerts(issuer,null,xsel),certs.size(): " + + certs.size()); + } + certs.addAll(getCertificates(request, CA_CERT, xsel)); + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() after " + + "getCertificates(issuer,CA_CERT,xsel),certs.size(): " + + certs.size()); + } + } + if (debug != null) { + debug.println("LDAPCertStore.engineGetCertificates() returning certs"); + } + return certs; + } + + /* + * Gets CRLs from an attribute id and location in the LDAP directory. + * Returns a Collection containing only the CRLs that match the + * specified CRLSelector. + * + * @param name the location holding the attribute + * @param id the attribute identifier + * @param sel a CRLSelector that the CRLs must match + * @return a Collection of CRLs found + * @throws CertStoreException if an exception occurs + */ + private Collection getCRLs(LDAPRequest request, String id, + X509CRLSelector sel) throws CertStoreException { + + /* fetch the encoded crls from storage */ + byte[][] encodedCRL; + try { + encodedCRL = request.getValues(id); + } catch (NamingException namingEx) { + throw new CertStoreException(namingEx); + } + + int n = encodedCRL.length; + if (n == 0) { + return Collections.emptySet(); + } + + List crls = new ArrayList<>(n); + /* decode each crl and check if it matches selector */ + for (int i = 0; i < n; i++) { + try { + CRL crl = cf.generateCRL(new ByteArrayInputStream(encodedCRL[i])); + if (sel.match(crl)) { + crls.add((X509CRL)crl); + } + } catch (CRLException e) { + if (debug != null) { + debug.println("LDAPCertStore.getCRLs() encountered exception" + + " while parsing CRL, skipping the bad data: "); + HexDumpEncoder encoder = new HexDumpEncoder(); + debug.println("[ " + encoder.encodeBuffer(encodedCRL[i]) + " ]"); + } + } + } + + return crls; + } + + /** + * Returns a Collection of CRLs that + * match the specified selector. If no CRLs + * match the selector, an empty Collection will be returned. + *

    + * It is not practical to search every entry in the LDAP database for + * matching CRLs. Instead, the CRLSelector + * is examined in order to determine where matching CRLs + * are likely to be found (according to the PKIX LDAPv2 schema, RFC 2587). + * If issuerNames or certChecking are specified, the issuer's directory + * entry is searched. If neither issuerNames or certChecking are specified + * (or the selector is not an X509CRLSelector), a + * CertStoreException is thrown. + * + * @param selector A CRLSelector used to select which + * CRLs should be returned. Specify null + * to return all CRLs. + * @return A Collection of CRLs that + * match the specified selector + * @throws CertStoreException if an exception occurs + */ + public synchronized Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCRLs() selector: " + + selector); + } + // Set up selector and collection to hold CRLs + if (selector == null) { + selector = new X509CRLSelector(); + } + if (!(selector instanceof X509CRLSelector)) { + throw new CertStoreException("need X509CRLSelector to find CRLs"); + } + X509CRLSelector xsel = (X509CRLSelector) selector; + HashSet crls = new HashSet<>(); + + // Look in directory entry for issuer of cert we're checking. + Collection issuerNames; + X509Certificate certChecking = xsel.getCertificateChecking(); + if (certChecking != null) { + issuerNames = new HashSet<>(); + X500Principal issuer = certChecking.getIssuerX500Principal(); + issuerNames.add(issuer.getName(X500Principal.RFC2253)); + } else { + // But if we don't know which cert we're checking, try the directory + // entries of all acceptable CRL issuers + issuerNames = xsel.getIssuerNames(); + if (issuerNames == null) { + throw new CertStoreException("need issuerNames or certChecking to " + + "find CRLs"); + } + } + for (Object nameObject : issuerNames) { + String issuerName; + if (nameObject instanceof byte[]) { + try { + X500Principal issuer = new X500Principal((byte[])nameObject); + issuerName = issuer.getName(X500Principal.RFC2253); + } catch (IllegalArgumentException e) { + continue; + } + } else { + issuerName = (String)nameObject; + } + // If all we want is CA certs, try to get the (probably shorter) ARL + Collection entryCRLs = Collections.emptySet(); + if (certChecking == null || certChecking.getBasicConstraints() != -1) { + LDAPRequest request = new LDAPRequest(issuerName); + request.addRequestedAttribute(CROSS_CERT); + request.addRequestedAttribute(CA_CERT); + request.addRequestedAttribute(ARL); + if (prefetchCRLs) { + request.addRequestedAttribute(CRL); + } + try { + entryCRLs = getCRLs(request, ARL, xsel); + if (entryCRLs.isEmpty()) { + // no ARLs found. We assume that means that there are + // no ARLs on this server at all and prefetch the CRLs. + prefetchCRLs = true; + } else { + crls.addAll(entryCRLs); + } + } catch (CertStoreException e) { + if (debug != null) { + debug.println("LDAPCertStore.engineGetCRLs non-fatal error " + + "retrieving ARLs:" + e); + e.printStackTrace(); + } + } + } + // Otherwise, get the CRL + // if certChecking is null, we don't know if we should look in ARL or CRL + // attribute, so check both for matching CRLs. + if (entryCRLs.isEmpty() || certChecking == null) { + LDAPRequest request = new LDAPRequest(issuerName); + request.addRequestedAttribute(CRL); + entryCRLs = getCRLs(request, CRL, xsel); + crls.addAll(entryCRLs); + } + } + return crls; + } + + // converts an LDAP URI into LDAPCertStoreParameters + static LDAPCertStoreParameters getParameters(URI uri) { + String host = uri.getHost(); + if (host == null) { + return new SunLDAPCertStoreParameters(); + } else { + int port = uri.getPort(); + return (port == -1 + ? new SunLDAPCertStoreParameters(host) + : new SunLDAPCertStoreParameters(host, port)); + } + } + + /* + * Subclass of LDAPCertStoreParameters with overridden equals/hashCode + * methods. This is necessary because the parameters are used as + * keys in the LDAPCertStore cache. + */ + private static class SunLDAPCertStoreParameters + extends LDAPCertStoreParameters { + + private volatile int hashCode = 0; + + SunLDAPCertStoreParameters(String serverName, int port) { + super(serverName, port); + } + SunLDAPCertStoreParameters(String serverName) { + super(serverName); + } + SunLDAPCertStoreParameters() { + super(); + } + public boolean equals(Object obj) { + if (!(obj instanceof LDAPCertStoreParameters)) { + return false; + } + LDAPCertStoreParameters params = (LDAPCertStoreParameters) obj; + return (getPort() == params.getPort() && + getServerName().equalsIgnoreCase(params.getServerName())); + } + public int hashCode() { + if (hashCode == 0) { + int result = 17; + result = 37*result + getPort(); + result = 37*result + + getServerName().toLowerCase(Locale.ENGLISH).hashCode(); + hashCode = result; + } + return hashCode; + } + } + + /* + * This inner class wraps an existing X509CertSelector and adds + * additional criteria to match on when the certificate's subject is + * different than the LDAP Distinguished Name entry. The LDAPCertStore + * implementation uses the subject DN as the directory entry for + * looking up certificates. This can be problematic if the certificates + * that you want to fetch have a different subject DN than the entry + * where they are stored. You could set the selector's subject to the + * LDAP DN entry, but then the resulting match would fail to find the + * desired certificates because the subject DNs would not match. This + * class avoids that problem by introducing a certSubject which should + * be set to the certificate's subject DN when it is different than + * the LDAP DN. + */ + static class LDAPCertSelector extends X509CertSelector { + + private X500Principal certSubject; + private X509CertSelector selector; + private X500Principal subject; + + /** + * Creates an LDAPCertSelector. + * + * @param selector the X509CertSelector to wrap + * @param certSubject the subject DN of the certificate that you want + * to retrieve via LDAP + * @param ldapDN the LDAP DN where the certificate is stored + */ + LDAPCertSelector(X509CertSelector selector, X500Principal certSubject, + String ldapDN) throws IOException { + this.selector = selector == null ? new X509CertSelector() : selector; + this.certSubject = certSubject; + this.subject = new X500Name(ldapDN).asX500Principal(); + } + + // we only override the get (accessor methods) since the set methods + // will not be invoked by the code that uses this LDAPCertSelector. + public X509Certificate getCertificate() { + return selector.getCertificate(); + } + public BigInteger getSerialNumber() { + return selector.getSerialNumber(); + } + public X500Principal getIssuer() { + return selector.getIssuer(); + } + public String getIssuerAsString() { + return selector.getIssuerAsString(); + } + public byte[] getIssuerAsBytes() throws IOException { + return selector.getIssuerAsBytes(); + } + public X500Principal getSubject() { + // return the ldap DN + return subject; + } + public String getSubjectAsString() { + // return the ldap DN + return subject.getName(); + } + public byte[] getSubjectAsBytes() throws IOException { + // return the encoded ldap DN + return subject.getEncoded(); + } + public byte[] getSubjectKeyIdentifier() { + return selector.getSubjectKeyIdentifier(); + } + public byte[] getAuthorityKeyIdentifier() { + return selector.getAuthorityKeyIdentifier(); + } + public Date getCertificateValid() { + return selector.getCertificateValid(); + } + public Date getPrivateKeyValid() { + return selector.getPrivateKeyValid(); + } + public String getSubjectPublicKeyAlgID() { + return selector.getSubjectPublicKeyAlgID(); + } + public PublicKey getSubjectPublicKey() { + return selector.getSubjectPublicKey(); + } + public boolean[] getKeyUsage() { + return selector.getKeyUsage(); + } + public Set getExtendedKeyUsage() { + return selector.getExtendedKeyUsage(); + } + public boolean getMatchAllSubjectAltNames() { + return selector.getMatchAllSubjectAltNames(); + } + public Collection> getSubjectAlternativeNames() { + return selector.getSubjectAlternativeNames(); + } + public byte[] getNameConstraints() { + return selector.getNameConstraints(); + } + public int getBasicConstraints() { + return selector.getBasicConstraints(); + } + public Set getPolicy() { + return selector.getPolicy(); + } + public Collection> getPathToNames() { + return selector.getPathToNames(); + } + + public boolean match(Certificate cert) { + // temporarily set the subject criterion to the certSubject + // so that match will not reject the desired certificates + selector.setSubject(certSubject); + boolean match = selector.match(cert); + selector.setSubject(subject); + return match; + } + } + + /** + * This class has the same purpose as LDAPCertSelector except it is for + * X.509 CRLs. + */ + static class LDAPCRLSelector extends X509CRLSelector { + + private X509CRLSelector selector; + private Collection certIssuers; + private Collection issuers; + private HashSet issuerNames; + + /** + * Creates an LDAPCRLSelector. + * + * @param selector the X509CRLSelector to wrap + * @param certIssuers the issuer DNs of the CRLs that you want + * to retrieve via LDAP + * @param ldapDN the LDAP DN where the CRL is stored + */ + LDAPCRLSelector(X509CRLSelector selector, + Collection certIssuers, String ldapDN) + throws IOException { + this.selector = selector == null ? new X509CRLSelector() : selector; + this.certIssuers = certIssuers; + issuerNames = new HashSet<>(); + issuerNames.add(ldapDN); + issuers = new HashSet<>(); + issuers.add(new X500Name(ldapDN).asX500Principal()); + } + // we only override the get (accessor methods) since the set methods + // will not be invoked by the code that uses this LDAPCRLSelector. + public Collection getIssuers() { + // return the ldap DN + return Collections.unmodifiableCollection(issuers); + } + public Collection getIssuerNames() { + // return the ldap DN + return Collections.unmodifiableCollection(issuerNames); + } + public BigInteger getMinCRL() { + return selector.getMinCRL(); + } + public BigInteger getMaxCRL() { + return selector.getMaxCRL(); + } + public Date getDateAndTime() { + return selector.getDateAndTime(); + } + public X509Certificate getCertificateChecking() { + return selector.getCertificateChecking(); + } + public boolean match(CRL crl) { + // temporarily set the issuer criterion to the certIssuers + // so that match will not reject the desired CRL + selector.setIssuers(certIssuers); + boolean match = selector.match(crl); + selector.setIssuers(issuers); + return match; + } + } +} diff --git a/src/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java b/src/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java new file mode 100644 index 00000000..8e6899bc --- /dev/null +++ b/src/sun/security/provider/certpath/ldap/LDAPCertStoreHelper.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath.ldap; + +import java.io.IOException; +import java.net.URI; +import java.util.Collection; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; +import javax.naming.CommunicationException; +import javax.naming.ServiceUnavailableException; +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.CertStoreHelper; + +/** + * LDAP implementation of CertStoreHelper. + */ + +public final class LDAPCertStoreHelper + extends CertStoreHelper +{ + @Override + public CertStore getCertStore(URI uri) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException + { + return LDAPCertStore.getInstance(LDAPCertStore.getParameters(uri)); + } + + @Override + public X509CertSelector wrap(X509CertSelector selector, + X500Principal certSubject, + String ldapDN) + throws IOException + { + return new LDAPCertStore.LDAPCertSelector(selector, certSubject, ldapDN); + } + + @Override + public X509CRLSelector wrap(X509CRLSelector selector, + Collection certIssuers, + String ldapDN) + throws IOException + { + return new LDAPCertStore.LDAPCRLSelector(selector, certIssuers, ldapDN); + } + + @Override + public boolean isCausedByNetworkIssue(CertStoreException e) { + Throwable t = e.getCause(); + return (t != null && (t instanceof ServiceUnavailableException || + t instanceof CommunicationException)); + } +} diff --git a/src/sun/security/provider/certpath/ssl/SSLServerCertStore.java b/src/sun/security/provider/certpath/ssl/SSLServerCertStore.java new file mode 100644 index 00000000..f2a999f5 --- /dev/null +++ b/src/sun/security/provider/certpath/ssl/SSLServerCertStore.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath.ssl; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Provider; +import java.security.cert.CertificateException; +import java.security.cert.CertSelector; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.CertStoreParameters; +import java.security.cert.CertStoreSpi; +import java.security.cert.CRLSelector; +import java.security.cert.X509Certificate; +import java.security.cert.X509CRL; +import java.net.Socket; +import java.net.URLConnection; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedTrustManager; + +/** + * A CertStore that retrieves an SSL server's certificate chain. + */ +public final class SSLServerCertStore extends CertStoreSpi { + + private final URI uri; + private final static GetChainTrustManager trustManager; + private final static SSLSocketFactory socketFactory; + private final static HostnameVerifier hostnameVerifier; + + static { + trustManager = new GetChainTrustManager(); + hostnameVerifier = new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + + SSLSocketFactory tempFactory; + try { + SSLContext context = SSLContext.getInstance("SSL"); + context.init(null, new TrustManager[] { trustManager }, null); + tempFactory = context.getSocketFactory(); + } catch (GeneralSecurityException gse) { + tempFactory = null; + } + + socketFactory = tempFactory; + } + + SSLServerCertStore(URI uri) throws InvalidAlgorithmParameterException { + super(null); + this.uri = uri; + } + + public Collection engineGetCertificates + (CertSelector selector) throws CertStoreException { + + try { + URLConnection urlConn = uri.toURL().openConnection(); + if (urlConn instanceof HttpsURLConnection) { + if (socketFactory == null) { + throw new CertStoreException( + "No initialized SSLSocketFactory"); + } + + HttpsURLConnection https = (HttpsURLConnection)urlConn; + https.setSSLSocketFactory(socketFactory); + https.setHostnameVerifier(hostnameVerifier); + synchronized (trustManager) { + try { + https.connect(); + return getMatchingCerts( + trustManager.serverChain, selector); + } catch (IOException ioe) { + // If the server certificate has already been + // retrieved, don't mind the connection state. + if (trustManager.exchangedServerCerts) { + return getMatchingCerts( + trustManager.serverChain, selector); + } + + // otherwise, rethrow the exception + throw ioe; + } finally { + trustManager.cleanup(); + } + } + } + } catch (IOException ioe) { + throw new CertStoreException(ioe); + } + + return Collections.emptySet(); + } + + private static List getMatchingCerts + (List certs, CertSelector selector) + { + // if selector not specified, all certs match + if (selector == null) { + return certs; + } + List matchedCerts = new ArrayList<>(certs.size()); + for (X509Certificate cert : certs) { + if (selector.match(cert)) { + matchedCerts.add(cert); + } + } + return matchedCerts; + } + + public Collection engineGetCRLs(CRLSelector selector) + throws CertStoreException + { + throw new UnsupportedOperationException(); + } + + static CertStore getInstance(URI uri) + throws InvalidAlgorithmParameterException + { + return new CS(new SSLServerCertStore(uri), null, "SSLServer", null); + } + + /* + * An X509ExtendedTrustManager that ignores the server certificate + * validation. + */ + private static class GetChainTrustManager + extends X509ExtendedTrustManager { + + private List serverChain = + Collections.emptyList(); + private boolean exchangedServerCerts = false; + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + + throw new UnsupportedOperationException(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + + throw new UnsupportedOperationException(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + + throw new UnsupportedOperationException(); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, + String authType) throws CertificateException { + + exchangedServerCerts = true; + this.serverChain = (chain == null) + ? Collections.emptyList() + : Arrays.asList(chain); + + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + + checkServerTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + + checkServerTrusted(chain, authType); + } + + void cleanup() { + exchangedServerCerts = false; + serverChain = Collections.emptyList(); + } + } + + /** + * This class allows the SSLServerCertStore to be accessed as a CertStore. + */ + private static class CS extends CertStore { + protected CS(CertStoreSpi spi, Provider p, String type, + CertStoreParameters params) + { + super(spi, p, type, params); + } + } +} diff --git a/src/sun/security/provider/certpath/ssl/SSLServerCertStoreHelper.java b/src/sun/security/provider/certpath/ssl/SSLServerCertStoreHelper.java new file mode 100644 index 00000000..d19b56d9 --- /dev/null +++ b/src/sun/security/provider/certpath/ssl/SSLServerCertStoreHelper.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider.certpath.ssl; + +import java.io.IOException; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidAlgorithmParameterException; +import java.security.cert.CertStore; +import java.security.cert.CertStoreException; +import java.security.cert.X509CertSelector; +import java.security.cert.X509CRLSelector; +import java.util.Collection; +import javax.security.auth.x500.X500Principal; + +import sun.security.provider.certpath.CertStoreHelper; + +/** + * SSL implementation of CertStoreHelper. + */ +public final class SSLServerCertStoreHelper extends CertStoreHelper { + + @Override + public CertStore getCertStore(URI uri) + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException + { + return SSLServerCertStore.getInstance(uri); + } + + @Override + public X509CertSelector wrap(X509CertSelector selector, + X500Principal certSubject, + String ldapDN) + throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public X509CRLSelector wrap(X509CRLSelector selector, + Collection certIssuers, + String ldapDN) + throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isCausedByNetworkIssue(CertStoreException e) { + Throwable t = e.getCause(); + return (t != null && t instanceof IOException); + } +} diff --git a/src/sun/security/rsa/MGF1.java b/src/sun/security/rsa/MGF1.java new file mode 100644 index 00000000..68f7d485 --- /dev/null +++ b/src/sun/security/rsa/MGF1.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.rsa; + +import java.security.*; + +/** + * This class implements the MGF1 mask generation function defined in PKCS#1 + * v2.2 B.2.1 (https://tools.ietf.org/html/rfc8017#appendix-B.2.1). A mask + * generation function takes an octet string of variable length and a + * desired output length as input and outputs an octet string of the + * desired length. MGF1 is a mask generation function based on a hash + * function, i.e. message digest algorithm. + * + * @since 8 + */ +public final class MGF1 { + + private final MessageDigest md; + + /** + * Construct an instance of MGF1 based on the specified digest algorithm. + */ + MGF1(String mdAlgo) throws NoSuchAlgorithmException { + this.md = MessageDigest.getInstance(mdAlgo); + } + + /** + * Using the specified seed bytes, generate the mask, xor the mask + * with the specified output buffer and store the result into the + * output buffer (essentially replaced in place). + * + * @param seed the buffer holding the seed bytes + * @param seedOfs the index of the seed bytes + * @param seedLen the length of the seed bytes to be used by MGF1 + * @param maskLen the intended length of the generated mask + * @param out the output buffer holding the mask + * @param outOfs the index of the output buffer for the mask + */ + void generateAndXor(byte[] seed, int seedOfs, int seedLen, int maskLen, + byte[] out, int outOfs) throws RuntimeException { + byte[] C = new byte[4]; // 32 bit counter + byte[] digest = new byte[md.getDigestLength()]; + while (maskLen > 0) { + md.update(seed, seedOfs, seedLen); + md.update(C); + try { + md.digest(digest, 0, digest.length); + } catch (DigestException e) { + // should never happen + throw new RuntimeException(e.toString()); + } + for (int i = 0; (i < digest.length) && (maskLen > 0); maskLen--) { + out[outOfs++] ^= digest[i++]; + } + if (maskLen > 0) { + // increment counter + for (int i = C.length - 1; (++C[i] == 0) && (i > 0); i--) { + // empty + } + } + } + } + + /** + * Returns the name of this MGF1 instance, i.e. "MGF1" followed by the + * digest algorithm it based on. + */ + String getName() { + return "MGF1" + md.getAlgorithm(); + } +} diff --git a/src/sun/security/rsa/PSSParameters.java b/src/sun/security/rsa/PSSParameters.java new file mode 100644 index 00000000..25625a89 --- /dev/null +++ b/src/sun/security/rsa/PSSParameters.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.*; +import sun.security.util.*; +import sun.security.x509.*; +import java.security.AlgorithmParametersSpi; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import static java.security.spec.PSSParameterSpec.DEFAULT; + +/** + * This class implements the PSS parameters used with the RSA + * signatures in PSS padding. Here is its ASN.1 definition: + * RSASSA-PSS-params ::= SEQUENCE { + * hashAlgorithm [0] HashAlgorithm DEFAULT sha1, + * maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, + * saltLength [2] INTEGER DEFAULT 20 + * trailerField [3] TrailerField DEFAULT trailerFieldBC + * } + * + * @author Valerie Peng + * + */ + +public final class PSSParameters extends AlgorithmParametersSpi { + + private PSSParameterSpec spec; + + public PSSParameters() { + } + + @Override + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException { + if (!(paramSpec instanceof PSSParameterSpec)) { + throw new InvalidParameterSpecException + ("Inappropriate parameter specification"); + } + PSSParameterSpec spec = (PSSParameterSpec) paramSpec; + + String mgfName = spec.getMGFAlgorithm(); + if (!spec.getMGFAlgorithm().equalsIgnoreCase("MGF1")) { + throw new InvalidParameterSpecException("Unsupported mgf " + + mgfName + "; MGF1 only"); + } + AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); + if (!(mgfSpec instanceof MGF1ParameterSpec)) { + throw new InvalidParameterSpecException("Inappropriate mgf " + + "parameters; non-null MGF1ParameterSpec only"); + } + this.spec = spec; + } + + @Override + protected void engineInit(byte[] encoded) throws IOException { + // first initialize with the DEFAULT values before + // retrieving from the encoding bytes + String mdName = DEFAULT.getDigestAlgorithm(); + MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) DEFAULT.getMGFParameters(); + int saltLength = DEFAULT.getSaltLength(); + int trailerField = DEFAULT.getTrailerField(); + + DerInputStream der = new DerInputStream(encoded); + DerValue[] datum = der.getSequence(4); + + for (DerValue d : datum) { + if (d.isContextSpecific((byte) 0x00)) { + // hash algid + mdName = AlgorithmId.parse + (d.data.getDerValue()).getName(); + } else if (d.isContextSpecific((byte) 0x01)) { + // mgf algid + AlgorithmId val = AlgorithmId.parse(d.data.getDerValue()); + if (!val.getOID().equals(AlgorithmId.mgf1_oid)) { + throw new IOException("Only MGF1 mgf is supported"); + } + AlgorithmId params = AlgorithmId.parse( + new DerValue(val.getEncodedParams())); + String mgfDigestName = params.getName(); + switch (mgfDigestName) { + case "SHA-1": + mgfSpec = MGF1ParameterSpec.SHA1; + break; + case "SHA-224": + mgfSpec = MGF1ParameterSpec.SHA224; + break; + case "SHA-256": + mgfSpec = MGF1ParameterSpec.SHA256; + break; + case "SHA-384": + mgfSpec = MGF1ParameterSpec.SHA384; + break; + case "SHA-512": + mgfSpec = MGF1ParameterSpec.SHA512; + break; + case "SHA-512/224": + mgfSpec = MGF1ParameterSpec.SHA512_224; + break; + case "SHA-512/256": + mgfSpec = MGF1ParameterSpec.SHA512_256; + break; + default: + throw new IOException + ("Unrecognized message digest algorithm " + + mgfDigestName); + } + } else if (d.isContextSpecific((byte) 0x02)) { + // salt length + saltLength = d.data.getDerValue().getInteger(); + if (saltLength < 0) { + throw new IOException("Negative value for saltLength"); + } + } else if (d.isContextSpecific((byte) 0x03)) { + // trailer field + trailerField = d.data.getDerValue().getInteger(); + if (trailerField != 1) { + throw new IOException("Unsupported trailerField value " + + trailerField); + } + } else { + throw new IOException("Invalid encoded PSSParameters"); + } + } + + this.spec = new PSSParameterSpec(mdName, "MGF1", mgfSpec, + saltLength, trailerField); + } + + @Override + protected void engineInit(byte[] encoded, String decodingMethod) + throws IOException { + if ((decodingMethod != null) && + (!decodingMethod.equalsIgnoreCase("ASN.1"))) { + throw new IllegalArgumentException("Only support ASN.1 format"); + } + engineInit(encoded); + } + + @Override + protected + T engineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException { + if (PSSParameterSpec.class.isAssignableFrom(paramSpec)) { + return paramSpec.cast(spec); + } else { + throw new InvalidParameterSpecException + ("Inappropriate parameter specification"); + } + } + + @Override + protected byte[] engineGetEncoded() throws IOException { + return getEncoded(spec); + } + + @Override + protected byte[] engineGetEncoded(String encMethod) throws IOException { + if ((encMethod != null) && + (!encMethod.equalsIgnoreCase("ASN.1"))) { + throw new IllegalArgumentException("Only support ASN.1 format"); + } + return engineGetEncoded(); + } + + @Override + protected String engineToString() { + return spec.toString(); + } + + /** + * Returns the encoding of a {@link PSSParameterSpec} object. This method + * is used in this class and {@link AlgorithmId}. + * + * @param spec a {@code PSSParameterSpec} object + * @return its DER encoding + * @throws IOException if the name of a MessageDigest or MaskGenAlgorithm + * is unsupported + */ + public static byte[] getEncoded(PSSParameterSpec spec) throws IOException { + + AlgorithmParameterSpec mgfSpec = spec.getMGFParameters(); + if (!(mgfSpec instanceof MGF1ParameterSpec)) { + throw new IOException("Cannot encode " + mgfSpec); + } + + MGF1ParameterSpec mgf1Spec = (MGF1ParameterSpec)mgfSpec; + + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream tmp2, tmp3; + + // MD + AlgorithmId mdAlgId; + try { + mdAlgId = AlgorithmId.get(spec.getDigestAlgorithm()); + } catch (NoSuchAlgorithmException nsae) { + throw new IOException("AlgorithmId " + spec.getDigestAlgorithm() + + " impl not found"); + } + if (!mdAlgId.getOID().equals(AlgorithmId.SHA_oid)) { + tmp2 = new DerOutputStream(); + mdAlgId.derEncode(tmp2); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), + tmp2); + } + + // MGF + AlgorithmId mgfDigestId; + try { + mgfDigestId = AlgorithmId.get(mgf1Spec.getDigestAlgorithm()); + } catch (NoSuchAlgorithmException nase) { + throw new IOException("AlgorithmId " + + mgf1Spec.getDigestAlgorithm() + " impl not found"); + } + + if (!mgfDigestId.getOID().equals(AlgorithmId.SHA_oid)) { + tmp2 = new DerOutputStream(); + tmp2.putOID(AlgorithmId.mgf1_oid); + mgfDigestId.encode(tmp2); + tmp3 = new DerOutputStream(); + tmp3.write(DerValue.tag_Sequence, tmp2); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 1), + tmp3); + } + + // SaltLength + if (spec.getSaltLength() != 20) { + tmp2 = new DerOutputStream(); + tmp2.putInteger(spec.getSaltLength()); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 2), + tmp2); + } + + // TrailerField + if (spec.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) { + tmp2 = new DerOutputStream(); + tmp2.putInteger(spec.getTrailerField()); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 3), + tmp2); + } + + // Put all together under a SEQUENCE tag + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, tmp); + return out.toByteArray(); + } +} diff --git a/src/sun/security/rsa/RSACore.java b/src/sun/security/rsa/RSACore.java new file mode 100644 index 00000000..5ea06117 --- /dev/null +++ b/src/sun/security/rsa/RSACore.java @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.math.BigInteger; +import java.util.*; + +import java.security.SecureRandom; +import java.security.interfaces.*; + +import javax.crypto.BadPaddingException; + +import sun.security.jca.JCAUtil; + +/** + * Core of the RSA implementation. Has code to perform public and private key + * RSA operations (with and without CRT for private key ops). Private CRT ops + * also support blinding to twart timing attacks. + * + * The code in this class only does the core RSA operation. Padding and + * unpadding must be done externally. + * + * Note: RSA keys should be at least 512 bits long + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class RSACore { + + // globally enable/disable use of blinding + private final static boolean ENABLE_BLINDING = true; + + // cache for blinding parameters. Map + // use a weak hashmap so that cached values are automatically cleared + // when the modulus is GC'ed + private final static Map + blindingCache = new WeakHashMap<>(); + + private RSACore() { + // empty + } + + /** + * Return the number of bytes required to store the magnitude byte[] of + * this BigInteger. Do not count a 0x00 byte toByteArray() would + * prefix for 2's complement form. + */ + public static int getByteLength(BigInteger b) { + int n = b.bitLength(); + return (n + 7) >> 3; + } + + /** + * Return the number of bytes required to store the modulus of this + * RSA key. + */ + public static int getByteLength(RSAKey key) { + return getByteLength(key.getModulus()); + } + + // temporary, used by RSACipher and RSAPadding. Move this somewhere else + public static byte[] convert(byte[] b, int ofs, int len) { + if ((ofs == 0) && (len == b.length)) { + return b; + } else { + byte[] t = new byte[len]; + System.arraycopy(b, ofs, t, 0, len); + return t; + } + } + + /** + * Perform an RSA public key operation. + */ + public static byte[] rsa(byte[] msg, RSAPublicKey key) + throws BadPaddingException { + return crypt(msg, key.getModulus(), key.getPublicExponent()); + } + + /** + * Perform an RSA private key operation. Uses CRT if the key is a + * CRT key with additional verification check after the signature + * is computed. + */ + @Deprecated + public static byte[] rsa(byte[] msg, RSAPrivateKey key) + throws BadPaddingException { + return rsa(msg, key, true); + } + + /** + * Perform an RSA private key operation. Uses CRT if the key is a + * CRT key. Set 'verify' to true if this function is used for + * generating a signature. + */ + public static byte[] rsa(byte[] msg, RSAPrivateKey key, boolean verify) + throws BadPaddingException { + if (key instanceof RSAPrivateCrtKey) { + return crtCrypt(msg, (RSAPrivateCrtKey)key, verify); + } else { + return priCrypt(msg, key.getModulus(), key.getPrivateExponent()); + } + } + + /** + * RSA public key ops. Simple modPow(). + */ + private static byte[] crypt(byte[] msg, BigInteger n, BigInteger exp) + throws BadPaddingException { + BigInteger m = parseMsg(msg, n); + BigInteger c = m.modPow(exp, n); + return toByteArray(c, getByteLength(n)); + } + + /** + * RSA non-CRT private key operations. + */ + private static byte[] priCrypt(byte[] msg, BigInteger n, BigInteger exp) + throws BadPaddingException { + + BigInteger c = parseMsg(msg, n); + BlindingRandomPair brp = null; + BigInteger m; + if (ENABLE_BLINDING) { + brp = getBlindingRandomPair(null, exp, n); + c = c.multiply(brp.u).mod(n); + m = c.modPow(exp, n); + m = m.multiply(brp.v).mod(n); + } else { + m = c.modPow(exp, n); + } + + return toByteArray(m, getByteLength(n)); + } + + /** + * RSA private key operations with CRT. Algorithm and variable naming + * are taken from PKCS#1 v2.1, section 5.1.2. + */ + private static byte[] crtCrypt(byte[] msg, RSAPrivateCrtKey key, + boolean verify) throws BadPaddingException { + BigInteger n = key.getModulus(); + BigInteger c0 = parseMsg(msg, n); + BigInteger c = c0; + BigInteger p = key.getPrimeP(); + BigInteger q = key.getPrimeQ(); + BigInteger dP = key.getPrimeExponentP(); + BigInteger dQ = key.getPrimeExponentQ(); + BigInteger qInv = key.getCrtCoefficient(); + BigInteger e = key.getPublicExponent(); + BigInteger d = key.getPrivateExponent(); + + BlindingRandomPair brp; + if (ENABLE_BLINDING) { + brp = getBlindingRandomPair(e, d, n); + c = c.multiply(brp.u).mod(n); + } + + // m1 = c ^ dP mod p + BigInteger m1 = c.modPow(dP, p); + // m2 = c ^ dQ mod q + BigInteger m2 = c.modPow(dQ, q); + + // h = (m1 - m2) * qInv mod p + BigInteger mtmp = m1.subtract(m2); + if (mtmp.signum() < 0) { + mtmp = mtmp.add(p); + } + BigInteger h = mtmp.multiply(qInv).mod(p); + + // m = m2 + q * h + BigInteger m = h.multiply(q).add(m2); + + if (ENABLE_BLINDING) { + m = m.multiply(brp.v).mod(n); + } + + if (verify && !c0.equals(m.modPow(e, n))) { + throw new BadPaddingException("RSA private key operation failed"); + } + + return toByteArray(m, getByteLength(n)); + } + + /** + * Parse the msg into a BigInteger and check against the modulus n. + */ + private static BigInteger parseMsg(byte[] msg, BigInteger n) + throws BadPaddingException { + BigInteger m = new BigInteger(1, msg); + if (m.compareTo(n) >= 0) { + throw new BadPaddingException("Message is larger than modulus"); + } + return m; + } + + /** + * Return the encoding of this BigInteger that is exactly len bytes long. + * Prefix/strip off leading 0x00 bytes if necessary. + * Precondition: bi must fit into len bytes + */ + private static byte[] toByteArray(BigInteger bi, int len) { + byte[] b = bi.toByteArray(); + int n = b.length; + if (n == len) { + return b; + } + // BigInteger prefixed a 0x00 byte for 2's complement form, remove it + if ((n == len + 1) && (b[0] == 0)) { + byte[] t = new byte[len]; + System.arraycopy(b, 1, t, 0, len); + return t; + } + // must be smaller + assert (n < len); + byte[] t = new byte[len]; + System.arraycopy(b, 0, t, (len - n), n); + return t; + } + + /** + * Parameters (u,v) for RSA Blinding. This is described in the RSA + * Bulletin#2 (Jan 96) and other places: + * + * ftp://ftp.rsa.com/pub/pdfs/bull-2.pdf + * + * The standard RSA Blinding decryption requires the public key exponent + * (e) and modulus (n), and converts ciphertext (c) to plaintext (p). + * + * Before the modular exponentiation operation, the input message should + * be multiplied by (u (mod n)), and afterward the result is corrected + * by multiplying with (v (mod n)). The system should reject messages + * equal to (0 (mod n)). That is: + * + * 1. Generate r between 0 and n-1, relatively prime to n. + * 2. Compute x = (c*u) mod n + * 3. Compute y = (x^d) mod n + * 4. Compute p = (y*v) mod n + * + * The Java APIs allows for either standard RSAPrivateKey or + * RSAPrivateCrtKey RSA keys. + * + * If the public exponent is available to us (e.g. RSAPrivateCrtKey), + * choose a random r, then let (u, v): + * + * u = r ^ e mod n + * v = r ^ (-1) mod n + * + * The proof follows: + * + * p = (((c * u) ^ d mod n) * v) mod n + * = ((c ^ d) * (u ^ d) * v) mod n + * = ((c ^ d) * (r ^ e) ^ d) * (r ^ (-1))) mod n + * = ((c ^ d) * (r ^ (e * d)) * (r ^ (-1))) mod n + * = ((c ^ d) * (r ^ 1) * (r ^ (-1))) mod n (see below) + * = (c ^ d) mod n + * + * because in RSA cryptosystem, d is the multiplicative inverse of e: + * + * (r^(e * d)) mod n + * = (r ^ 1) mod n + * = r mod n + * + * However, if the public exponent is not available (e.g. RSAPrivateKey), + * we mitigate the timing issue by using a similar random number blinding + * approach using the private key: + * + * u = r + * v = ((r ^ (-1)) ^ d) mod n + * + * This returns the same plaintext because: + * + * p = (((c * u) ^ d mod n) * v) mod n + * = ((c ^ d) * (u ^ d) * v) mod n + * = ((c ^ d) * (u ^ d) * ((u ^ (-1)) ^d)) mod n + * = (c ^ d) mod n + * + * Computing inverses mod n and random number generation is slow, so + * it is often not practical to generate a new random (u, v) pair for + * each new exponentiation. The calculation of parameters might even be + * subject to timing attacks. However, (u, v) pairs should not be + * reused since they themselves might be compromised by timing attacks, + * leaving the private exponent vulnerable. An efficient solution to + * this problem is update u and v before each modular exponentiation + * step by computing: + * + * u = u ^ 2 + * v = v ^ 2 + * + * The total performance cost is small. + */ + private final static class BlindingRandomPair { + final BigInteger u; + final BigInteger v; + + BlindingRandomPair(BigInteger u, BigInteger v) { + this.u = u; + this.v = v; + } + } + + /** + * Set of blinding parameters for a given RSA key. + * + * The RSA modulus is usually unique, so we index by modulus in + * {@code blindingCache}. However, to protect against the unlikely + * case of two keys sharing the same modulus, we also store the public + * or the private exponent. This means we cannot cache blinding + * parameters for multiple keys that share the same modulus, but + * since sharing moduli is fundamentally broken and insecure, this + * does not matter. + */ + private final static class BlindingParameters { + private final static BigInteger BIG_TWO = BigInteger.valueOf(2L); + + // RSA public exponent + private final BigInteger e; + + // hash code of RSA private exponent + private final BigInteger d; + + // r ^ e mod n (CRT), or r mod n (Non-CRT) + private BigInteger u; + + // r ^ (-1) mod n (CRT) , or ((r ^ (-1)) ^ d) mod n (Non-CRT) + private BigInteger v; + + // e: the public exponent + // d: the private exponent + // n: the modulus + BlindingParameters(BigInteger e, BigInteger d, BigInteger n) { + this.u = null; + this.v = null; + this.e = e; + this.d = d; + + int len = n.bitLength(); + SecureRandom random = JCAUtil.getSecureRandom(); + u = new BigInteger(len, random).mod(n); + // Although the possibility is very much limited that u is zero + // or is not relatively prime to n, we still want to be careful + // about the special value. + // + // Secure random generation is expensive, try to use BigInteger.ONE + // this time if this new generated random number is zero or is not + // relatively prime to n. Next time, new generated secure random + // number will be used instead. + if (u.equals(BigInteger.ZERO)) { + u = BigInteger.ONE; // use 1 this time + } + + try { + // The call to BigInteger.modInverse() checks that u is + // relatively prime to n. Otherwise, ArithmeticException is + // thrown. + v = u.modInverse(n); + } catch (ArithmeticException ae) { + // if u is not relatively prime to n, use 1 this time + u = BigInteger.ONE; + v = BigInteger.ONE; + } + + if (e != null) { + u = u.modPow(e, n); // e: the public exponent + // u: random ^ e + // v: random ^ (-1) + } else { + v = v.modPow(d, n); // d: the private exponent + // u: random + // v: random ^ (-d) + } + } + + // return null if need to reset the parameters + BlindingRandomPair getBlindingRandomPair( + BigInteger e, BigInteger d, BigInteger n) { + + if ((this.e != null && this.e.equals(e)) || + (this.d != null && this.d.equals(d))) { + + BlindingRandomPair brp = null; + synchronized (this) { + if (!u.equals(BigInteger.ZERO) && + !v.equals(BigInteger.ZERO)) { + + brp = new BlindingRandomPair(u, v); + if (u.compareTo(BigInteger.ONE) <= 0 || + v.compareTo(BigInteger.ONE) <= 0) { + + // need to reset the random pair next time + u = BigInteger.ZERO; + v = BigInteger.ZERO; + } else { + u = u.modPow(BIG_TWO, n); + v = v.modPow(BIG_TWO, n); + } + } // Otherwise, need to reset the random pair. + } + return brp; + } + + return null; + } + } + + private static BlindingRandomPair getBlindingRandomPair( + BigInteger e, BigInteger d, BigInteger n) { + + BlindingParameters bps = null; + synchronized (blindingCache) { + bps = blindingCache.get(n); + } + + if (bps == null) { + bps = new BlindingParameters(e, d, n); + synchronized (blindingCache) { + blindingCache.putIfAbsent(n, bps); + } + } + + BlindingRandomPair brp = bps.getBlindingRandomPair(e, d, n); + if (brp == null) { + // need to reset the blinding parameters + bps = new BlindingParameters(e, d, n); + synchronized (blindingCache) { + blindingCache.replace(n, bps); + } + brp = bps.getBlindingRandomPair(e, d, n); + } + + return brp; + } + +} diff --git a/src/sun/security/rsa/RSAKeyFactory.java b/src/sun/security/rsa/RSAKeyFactory.java new file mode 100644 index 00000000..17560377 --- /dev/null +++ b/src/sun/security/rsa/RSAKeyFactory.java @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; + +import sun.security.action.GetPropertyAction; + +import static sun.security.rsa.RSAUtil.KeyType; + +/** + * KeyFactory for RSA keys, e.g. "RSA", "RSASSA-PSS". + * Keys must be instances of PublicKey or PrivateKey + * and getAlgorithm() must return a value which matches the type which are + * specified during construction time of the KeyFactory object. + * For such keys, it supports conversion + * between the following: + * + * For public keys: + * . PublicKey with an X.509 encoding + * . RSAPublicKey + * . RSAPublicKeySpec + * . X509EncodedKeySpec + * + * For private keys: + * . PrivateKey with a PKCS#8 encoding + * . RSAPrivateKey + * . RSAPrivateCrtKey + * . RSAPrivateKeySpec + * . RSAPrivateCrtKeySpec + * . PKCS8EncodedKeySpec + * (of course, CRT variants only for CRT keys) + * + * Note: as always, RSA keys should be at least 512 bits long + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public class RSAKeyFactory extends KeyFactorySpi { + + private static final Class RSA_PUB_KEYSPEC_CLS = RSAPublicKeySpec.class; + private static final Class RSA_PRIV_KEYSPEC_CLS = + RSAPrivateKeySpec.class; + private static final Class RSA_PRIVCRT_KEYSPEC_CLS = + RSAPrivateCrtKeySpec.class; + private static final Class X509_KEYSPEC_CLS = X509EncodedKeySpec.class; + private static final Class PKCS8_KEYSPEC_CLS = PKCS8EncodedKeySpec.class; + + public final static int MIN_MODLEN = 512; + public final static int MAX_MODLEN = 16384; + + private final KeyType type; + + /* + * If the modulus length is above this value, restrict the size of + * the exponent to something that can be reasonably computed. We + * could simply hardcode the exp len to something like 64 bits, but + * this approach allows flexibility in case impls would like to use + * larger module and exponent values. + */ + public final static int MAX_MODLEN_RESTRICT_EXP = 3072; + public final static int MAX_RESTRICTED_EXPLEN = 64; + + private static final boolean restrictExpLen = + "true".equalsIgnoreCase(AccessController.doPrivileged( + new GetPropertyAction( + "sun.security.rsa.restrictRSAExponent", "true"))); + + static RSAKeyFactory getInstance(KeyType type) { + return new RSAKeyFactory(type); + } + + // Internal utility method for checking key algorithm + private static void checkKeyAlgo(Key key, String expectedAlg) + throws InvalidKeyException { + String keyAlg = key.getAlgorithm(); + if (keyAlg == null || !(keyAlg.equalsIgnoreCase(expectedAlg))) { + throw new InvalidKeyException("Expected a " + expectedAlg + + " key, but got " + keyAlg); + } + } + + /** + * Static method to convert Key into an instance of RSAPublicKeyImpl + * or RSAPrivate(Crt)KeyImpl. If the key is not an RSA key or cannot be + * used, throw an InvalidKeyException. + * + * Used by RSASignature and RSACipher. + */ + public static RSAKey toRSAKey(Key key) throws InvalidKeyException { + if ((key instanceof RSAPrivateKeyImpl) || + (key instanceof RSAPrivateCrtKeyImpl) || + (key instanceof RSAPublicKeyImpl)) { + return (RSAKey)key; + } else { + try { + KeyType type = KeyType.lookup(key.getAlgorithm()); + RSAKeyFactory kf = RSAKeyFactory.getInstance(type); + return (RSAKey) kf.engineTranslateKey(key); + } catch (ProviderException e) { + throw new InvalidKeyException(e); + } + } + } + + /* + * Single test entry point for all of the mechanisms in the SunRsaSign + * provider (RSA*KeyImpls). All of the tests are the same. + * + * For compatibility, we round up to the nearest byte here: + * some Key impls might pass in a value within a byte of the + * real value. + */ + static void checkRSAProviderKeyLengths(int modulusLen, BigInteger exponent) + throws InvalidKeyException { + checkKeyLengths(((modulusLen + 7) & ~7), exponent, + RSAKeyFactory.MIN_MODLEN, Integer.MAX_VALUE); + } + + /** + * Check the length of an RSA key modulus/exponent to make sure it + * is not too short or long. Some impls have their own min and + * max key sizes that may or may not match with a system defined value. + * + * @param modulusLen the bit length of the RSA modulus. + * @param exponent the RSA exponent + * @param minModulusLen if > 0, check to see if modulusLen is at + * least this long, otherwise unused. + * @param maxModulusLen caller will allow this max number of bits. + * Allow the smaller of the system-defined maximum and this param. + * + * @throws InvalidKeyException if any of the values are unacceptable. + */ + public static void checkKeyLengths(int modulusLen, BigInteger exponent, + int minModulusLen, int maxModulusLen) throws InvalidKeyException { + + if ((minModulusLen > 0) && (modulusLen < (minModulusLen))) { + throw new InvalidKeyException( "RSA keys must be at least " + + minModulusLen + " bits long"); + } + + // Even though our policy file may allow this, we don't want + // either value (mod/exp) to be too big. + + int maxLen = Math.min(maxModulusLen, MAX_MODLEN); + + // If a RSAPrivateKey/RSAPublicKey, make sure the + // modulus len isn't too big. + if (modulusLen > maxLen) { + throw new InvalidKeyException( + "RSA keys must be no longer than " + maxLen + " bits"); + } + + // If a RSAPublicKey, make sure the exponent isn't too big. + if (restrictExpLen && (exponent != null) && + (modulusLen > MAX_MODLEN_RESTRICT_EXP) && + (exponent.bitLength() > MAX_RESTRICTED_EXPLEN)) { + throw new InvalidKeyException( + "RSA exponents can be no longer than " + + MAX_RESTRICTED_EXPLEN + " bits " + + " if modulus is greater than " + + MAX_MODLEN_RESTRICT_EXP + " bits"); + } + } + + // disallowed as KeyType is required + private RSAKeyFactory() { + this.type = KeyType.RSA; + } + + public RSAKeyFactory(KeyType type) { + this.type = type; + } + + /** + * Translate an RSA key into a SunRsaSign RSA key. If conversion is + * not possible, throw an InvalidKeyException. + * See also JCA doc. + */ + protected Key engineTranslateKey(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + // ensure the key algorithm matches the current KeyFactory instance + checkKeyAlgo(key, type.keyAlgo()); + + // no translation needed if the key is already our own impl + if ((key instanceof RSAPrivateKeyImpl) || + (key instanceof RSAPrivateCrtKeyImpl) || + (key instanceof RSAPublicKeyImpl)) { + return key; + } + if (key instanceof PublicKey) { + return translatePublicKey((PublicKey)key); + } else if (key instanceof PrivateKey) { + return translatePrivateKey((PrivateKey)key); + } else { + throw new InvalidKeyException("Neither a public nor a private key"); + } + } + + // see JCA doc + protected PublicKey engineGeneratePublic(KeySpec keySpec) + throws InvalidKeySpecException { + try { + return generatePublic(keySpec); + } catch (InvalidKeySpecException e) { + throw e; + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + // see JCA doc + protected PrivateKey engineGeneratePrivate(KeySpec keySpec) + throws InvalidKeySpecException { + try { + return generatePrivate(keySpec); + } catch (InvalidKeySpecException e) { + throw e; + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + // internal implementation of translateKey() for public keys. See JCA doc + private PublicKey translatePublicKey(PublicKey key) + throws InvalidKeyException { + if (key instanceof RSAPublicKey) { + RSAPublicKey rsaKey = (RSAPublicKey)key; + try { + return new RSAPublicKeyImpl( + RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + rsaKey.getModulus(), + rsaKey.getPublicExponent()); + } catch (ProviderException e) { + // catch providers that incorrectly implement RSAPublicKey + throw new InvalidKeyException("Invalid key", e); + } + } else if ("X.509".equals(key.getFormat())) { + RSAPublicKey translated = new RSAPublicKeyImpl(key.getEncoded()); + // ensure the key algorithm matches the current KeyFactory instance + checkKeyAlgo(translated, type.keyAlgo()); + return translated; + } else { + throw new InvalidKeyException("Public keys must be instance " + + "of RSAPublicKey or have X.509 encoding"); + } + } + + // internal implementation of translateKey() for private keys. See JCA doc + private PrivateKey translatePrivateKey(PrivateKey key) + throws InvalidKeyException { + if (key instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey)key; + try { + return new RSAPrivateCrtKeyImpl( + RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + rsaKey.getModulus(), + rsaKey.getPublicExponent(), + rsaKey.getPrivateExponent(), + rsaKey.getPrimeP(), + rsaKey.getPrimeQ(), + rsaKey.getPrimeExponentP(), + rsaKey.getPrimeExponentQ(), + rsaKey.getCrtCoefficient() + ); + } catch (ProviderException e) { + // catch providers that incorrectly implement RSAPrivateCrtKey + throw new InvalidKeyException("Invalid key", e); + } + } else if (key instanceof RSAPrivateKey) { + RSAPrivateKey rsaKey = (RSAPrivateKey)key; + try { + return new RSAPrivateKeyImpl( + RSAUtil.createAlgorithmId(type, rsaKey.getParams()), + rsaKey.getModulus(), + rsaKey.getPrivateExponent() + ); + } catch (ProviderException e) { + // catch providers that incorrectly implement RSAPrivateKey + throw new InvalidKeyException("Invalid key", e); + } + } else if ("PKCS#8".equals(key.getFormat())) { + RSAPrivateKey translated = + RSAPrivateCrtKeyImpl.newKey(key.getEncoded()); + // ensure the key algorithm matches the current KeyFactory instance + checkKeyAlgo(translated, type.keyAlgo()); + return translated; + } else { + throw new InvalidKeyException("Private keys must be instance " + + "of RSAPrivate(Crt)Key or have PKCS#8 encoding"); + } + } + + // internal implementation of generatePublic. See JCA doc + private PublicKey generatePublic(KeySpec keySpec) + throws GeneralSecurityException { + if (keySpec instanceof X509EncodedKeySpec) { + X509EncodedKeySpec x509Spec = (X509EncodedKeySpec)keySpec; + RSAPublicKey generated = new RSAPublicKeyImpl(x509Spec.getEncoded()); + // ensure the key algorithm matches the current KeyFactory instance + checkKeyAlgo(generated, type.keyAlgo()); + return generated; + } else if (keySpec instanceof RSAPublicKeySpec) { + RSAPublicKeySpec rsaSpec = (RSAPublicKeySpec)keySpec; + try { + return new RSAPublicKeyImpl( + RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + rsaSpec.getModulus(), + rsaSpec.getPublicExponent() + ); + } catch (ProviderException e) { + throw new InvalidKeySpecException(e); + } + } else { + throw new InvalidKeySpecException("Only RSAPublicKeySpec " + + "and X509EncodedKeySpec supported for RSA public keys"); + } + } + + // internal implementation of generatePrivate. See JCA doc + private PrivateKey generatePrivate(KeySpec keySpec) + throws GeneralSecurityException { + if (keySpec instanceof PKCS8EncodedKeySpec) { + PKCS8EncodedKeySpec pkcsSpec = (PKCS8EncodedKeySpec)keySpec; + RSAPrivateKey generated = RSAPrivateCrtKeyImpl.newKey(pkcsSpec.getEncoded()); + // ensure the key algorithm matches the current KeyFactory instance + checkKeyAlgo(generated, type.keyAlgo()); + return generated; + } else if (keySpec instanceof RSAPrivateCrtKeySpec) { + RSAPrivateCrtKeySpec rsaSpec = (RSAPrivateCrtKeySpec)keySpec; + try { + return new RSAPrivateCrtKeyImpl( + RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + rsaSpec.getModulus(), + rsaSpec.getPublicExponent(), + rsaSpec.getPrivateExponent(), + rsaSpec.getPrimeP(), + rsaSpec.getPrimeQ(), + rsaSpec.getPrimeExponentP(), + rsaSpec.getPrimeExponentQ(), + rsaSpec.getCrtCoefficient() + ); + } catch (ProviderException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof RSAPrivateKeySpec) { + RSAPrivateKeySpec rsaSpec = (RSAPrivateKeySpec)keySpec; + try { + return new RSAPrivateKeyImpl( + RSAUtil.createAlgorithmId(type, rsaSpec.getParams()), + rsaSpec.getModulus(), + rsaSpec.getPrivateExponent() + ); + } catch (ProviderException e) { + throw new InvalidKeySpecException(e); + } + } else { + throw new InvalidKeySpecException("Only RSAPrivate(Crt)KeySpec " + + "and PKCS8EncodedKeySpec supported for RSA private keys"); + } + } + + protected T engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException { + try { + // convert key to one of our keys + // this also verifies that the key is a valid RSA key and ensures + // that the encoding is X.509/PKCS#8 for public/private keys + key = engineTranslateKey(key); + } catch (InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + if (key instanceof RSAPublicKey) { + RSAPublicKey rsaKey = (RSAPublicKey)key; + if (RSA_PUB_KEYSPEC_CLS.isAssignableFrom(keySpec)) { + return keySpec.cast(new RSAPublicKeySpec( + rsaKey.getModulus(), + rsaKey.getPublicExponent(), + rsaKey.getParams() + )); + } else if (X509_KEYSPEC_CLS.isAssignableFrom(keySpec)) { + return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); + } else { + throw new InvalidKeySpecException + ("KeySpec must be RSAPublicKeySpec or " + + "X509EncodedKeySpec for RSA public keys"); + } + } else if (key instanceof RSAPrivateKey) { + if (PKCS8_KEYSPEC_CLS.isAssignableFrom(keySpec)) { + return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded())); + } else if (RSA_PRIVCRT_KEYSPEC_CLS.isAssignableFrom(keySpec)) { + if (key instanceof RSAPrivateCrtKey) { + RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey)key; + return keySpec.cast(new RSAPrivateCrtKeySpec( + crtKey.getModulus(), + crtKey.getPublicExponent(), + crtKey.getPrivateExponent(), + crtKey.getPrimeP(), + crtKey.getPrimeQ(), + crtKey.getPrimeExponentP(), + crtKey.getPrimeExponentQ(), + crtKey.getCrtCoefficient(), + crtKey.getParams() + )); + } else { + throw new InvalidKeySpecException + ("RSAPrivateCrtKeySpec can only be used with CRT keys"); + } + } else if (RSA_PRIV_KEYSPEC_CLS.isAssignableFrom(keySpec)) { + RSAPrivateKey rsaKey = (RSAPrivateKey)key; + return keySpec.cast(new RSAPrivateKeySpec( + rsaKey.getModulus(), + rsaKey.getPrivateExponent(), + rsaKey.getParams() + )); + } else { + throw new InvalidKeySpecException + ("KeySpec must be RSAPrivate(Crt)KeySpec or " + + "PKCS8EncodedKeySpec for RSA private keys"); + } + } else { + // should not occur, caught in engineTranslateKey() + throw new InvalidKeySpecException("Neither public nor private key"); + } + } + + public static final class Legacy extends RSAKeyFactory { + public Legacy() { + super(KeyType.RSA); + } + } + + public static final class PSS extends RSAKeyFactory { + public PSS() { + super(KeyType.PSS); + } + } +} diff --git a/src/sun/security/rsa/RSAKeyPairGenerator.java b/src/sun/security/rsa/RSAKeyPairGenerator.java new file mode 100644 index 00000000..3b6dd167 --- /dev/null +++ b/src/sun/security/rsa/RSAKeyPairGenerator.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.RSAKeyGenParameterSpec; + +import sun.security.jca.JCAUtil; +import static sun.security.util.SecurityProviderConstants.DEF_RSA_KEY_SIZE; +import static sun.security.util.SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE; +import sun.security.x509.AlgorithmId; +import static sun.security.rsa.RSAUtil.KeyType; + +/** + * RSA keypair generation. Standard algorithm, minimum key length 512 bit. + * We generate two random primes until we find two where phi is relative + * prime to the public exponent. Default exponent is 65537. It has only bit 0 + * and bit 4 set, which makes it particularly efficient. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public abstract class RSAKeyPairGenerator extends KeyPairGeneratorSpi { + + // public exponent to use + private BigInteger publicExponent; + + // size of the key to generate, >= RSAKeyFactory.MIN_MODLEN + private int keySize; + + private final KeyType type; + private AlgorithmId rsaId; + + // PRNG to use + private SecureRandom random; + + RSAKeyPairGenerator(KeyType type, int defKeySize) { + this.type = type; + // initialize to default in case the app does not call initialize() + initialize(defKeySize, null); + } + + // initialize the generator. See JCA doc + public void initialize(int keySize, SecureRandom random) { + try { + initialize(new RSAKeyGenParameterSpec(keySize, + RSAKeyGenParameterSpec.F4), null); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException(iape.getMessage()); + } + } + + // second initialize method. See JCA doc. + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + + if (params instanceof RSAKeyGenParameterSpec == false) { + throw new InvalidAlgorithmParameterException + ("Params must be instance of RSAKeyGenParameterSpec"); + } + + RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec)params; + int tmpKeySize = rsaSpec.getKeysize(); + BigInteger tmpPublicExponent = rsaSpec.getPublicExponent(); + AlgorithmParameterSpec tmpParams = rsaSpec.getKeyParams(); + + if (tmpPublicExponent == null) { + tmpPublicExponent = RSAKeyGenParameterSpec.F4; + } else { + if (tmpPublicExponent.compareTo(RSAKeyGenParameterSpec.F0) < 0) { + throw new InvalidAlgorithmParameterException + ("Public exponent must be 3 or larger"); + } + if (tmpPublicExponent.bitLength() > tmpKeySize) { + throw new InvalidAlgorithmParameterException + ("Public exponent must be smaller than key size"); + } + } + + // do not allow unreasonably large key sizes, probably user error + try { + RSAKeyFactory.checkKeyLengths(tmpKeySize, tmpPublicExponent, + 512, 64 * 1024); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException( + "Invalid key sizes", e); + } + + try { + this.rsaId = RSAUtil.createAlgorithmId(type, tmpParams); + } catch (ProviderException e) { + throw new InvalidAlgorithmParameterException( + "Invalid key parameters", e); + } + + this.keySize = tmpKeySize; + this.publicExponent = tmpPublicExponent; + this.random = random; + } + + // generate the keypair. See JCA doc + public KeyPair generateKeyPair() { + // accommodate odd key sizes in case anybody wants to use them + int lp = (keySize + 1) >> 1; + int lq = keySize - lp; + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + BigInteger e = publicExponent; + while (true) { + // generate two random primes of size lp/lq + BigInteger p = BigInteger.probablePrime(lp, random); + BigInteger q, n; + do { + q = BigInteger.probablePrime(lq, random); + // convention is for p > q + if (p.compareTo(q) < 0) { + BigInteger tmp = p; + p = q; + q = tmp; + } + // modulus n = p * q + n = p.multiply(q); + // even with correctly sized p and q, there is a chance that + // n will be one bit short. re-generate the smaller prime if so + } while (n.bitLength() < keySize); + + // phi = (p - 1) * (q - 1) must be relative prime to e + // otherwise RSA just won't work ;-) + BigInteger p1 = p.subtract(BigInteger.ONE); + BigInteger q1 = q.subtract(BigInteger.ONE); + BigInteger phi = p1.multiply(q1); + // generate new p and q until they work. typically + // the first try will succeed when using F4 + if (e.gcd(phi).equals(BigInteger.ONE) == false) { + continue; + } + + // private exponent d is the inverse of e mod phi + BigInteger d = e.modInverse(phi); + + // 1st prime exponent pe = d mod (p - 1) + BigInteger pe = d.mod(p1); + // 2nd prime exponent qe = d mod (q - 1) + BigInteger qe = d.mod(q1); + + // crt coefficient coeff is the inverse of q mod p + BigInteger coeff = q.modInverse(p); + + try { + PublicKey publicKey = new RSAPublicKeyImpl(rsaId, n, e); + PrivateKey privateKey = new RSAPrivateCrtKeyImpl( + rsaId, n, e, d, p, q, pe, qe, coeff); + return new KeyPair(publicKey, privateKey); + } catch (InvalidKeyException exc) { + // invalid key exception only thrown for keys < 512 bit, + // will not happen here + throw new RuntimeException(exc); + } + } + } + + public static final class Legacy extends RSAKeyPairGenerator { + public Legacy() { + super(KeyType.RSA, DEF_RSA_KEY_SIZE); + } + } + + public static final class PSS extends RSAKeyPairGenerator { + public PSS() { + super(KeyType.PSS, DEF_RSASSA_PSS_KEY_SIZE); + } + } +} diff --git a/src/sun/security/rsa/RSAPSSSignature.java b/src/sun/security/rsa/RSAPSSSignature.java new file mode 100644 index 00000000..dd63f58c --- /dev/null +++ b/src/sun/security/rsa/RSAPSSSignature.java @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.interfaces.*; + +import java.util.Arrays; +import java.util.Hashtable; + +import sun.security.jca.JCAUtil; + + +/** + * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms. + * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm, + * and salt length values through the required signature PSS parameters. + * We support SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, and + * SHA-512/256 message digest algorithms and MGF1 mask generation function. + * + * @since 8 + */ +public class RSAPSSSignature extends SignatureSpi { + + private static final boolean DEBUG = false; + + // utility method for comparing digest algorithms + // NOTE that first argument is assumed to be standard digest name + private boolean isDigestEqual(String stdAlg, String givenAlg) { + if (stdAlg == null || givenAlg == null) return false; + + if (givenAlg.indexOf("-") != -1) { + return stdAlg.equalsIgnoreCase(givenAlg); + } else { + if (stdAlg.equals("SHA-1")) { + return (givenAlg.equalsIgnoreCase("SHA") + || givenAlg.equalsIgnoreCase("SHA1")); + } else { + StringBuilder sb = new StringBuilder(givenAlg); + // case-insensitive check + if (givenAlg.regionMatches(true, 0, "SHA", 0, 3)) { + givenAlg = sb.insert(3, "-").toString(); + return stdAlg.equalsIgnoreCase(givenAlg); + } else { + throw new ProviderException("Unsupported digest algorithm " + + givenAlg); + } + } + } + } + + private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8]; + + private static final Hashtable DIGEST_LENGTHS = + new Hashtable(); + static { + DIGEST_LENGTHS.put("SHA-1", 20); + DIGEST_LENGTHS.put("SHA", 20); + DIGEST_LENGTHS.put("SHA1", 20); + DIGEST_LENGTHS.put("SHA-224", 28); + DIGEST_LENGTHS.put("SHA224", 28); + DIGEST_LENGTHS.put("SHA-256", 32); + DIGEST_LENGTHS.put("SHA256", 32); + DIGEST_LENGTHS.put("SHA-384", 48); + DIGEST_LENGTHS.put("SHA384", 48); + DIGEST_LENGTHS.put("SHA-512", 64); + DIGEST_LENGTHS.put("SHA512", 64); + DIGEST_LENGTHS.put("SHA-512/224", 28); + DIGEST_LENGTHS.put("SHA512/224", 28); + DIGEST_LENGTHS.put("SHA-512/256", 32); + DIGEST_LENGTHS.put("SHA512/256", 32); + } + + // message digest implementation we use for hashing the data + private MessageDigest md; + // flag indicating whether the digest is reset + private boolean digestReset = true; + + // private key, if initialized for signing + private RSAPrivateKey privKey = null; + // public key, if initialized for verifying + private RSAPublicKey pubKey = null; + // PSS parameters from signatures and keys respectively + private PSSParameterSpec sigParams = null; // required for PSS signatures + + // PRNG used to generate salt bytes if none given + private SecureRandom random; + + /** + * Construct a new RSAPSSSignatur with arbitrary digest algorithm + */ + public RSAPSSSignature() { + this.md = null; + } + + // initialize for verification. See JCA doc + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + if (!(publicKey instanceof RSAPublicKey)) { + throw new InvalidKeyException("key must be RSAPublicKey"); + } + this.pubKey = (RSAPublicKey) isValid((RSAKey)publicKey); + this.privKey = null; + resetDigest(); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + if (!(privateKey instanceof RSAPrivateKey)) { + throw new InvalidKeyException("key must be RSAPrivateKey"); + } + this.privKey = (RSAPrivateKey) isValid((RSAKey)privateKey); + this.pubKey = null; + this.random = + (random == null? JCAUtil.getSecureRandom() : random); + resetDigest(); + } + + /** + * Utility method for checking the key PSS parameters against signature + * PSS parameters. + * Returns false if any of the digest/MGF algorithms and trailerField + * values does not match or if the salt length in key parameters is + * larger than the value in signature parameters. + */ + private static boolean isCompatible(AlgorithmParameterSpec keyParams, + PSSParameterSpec sigParams) { + if (keyParams == null) { + // key with null PSS parameters means no restriction + return true; + } + if (!(keyParams instanceof PSSParameterSpec)) { + return false; + } + // nothing to compare yet, defer the check to when sigParams is set + if (sigParams == null) { + return true; + } + PSSParameterSpec pssKeyParams = (PSSParameterSpec) keyParams; + // first check the salt length requirement + if (pssKeyParams.getSaltLength() > sigParams.getSaltLength()) { + return false; + } + + // compare equality of the rest of fields based on DER encoding + PSSParameterSpec keyParams2 = + new PSSParameterSpec(pssKeyParams.getDigestAlgorithm(), + pssKeyParams.getMGFAlgorithm(), + pssKeyParams.getMGFParameters(), + sigParams.getSaltLength(), + pssKeyParams.getTrailerField()); + PSSParameters ap = new PSSParameters(); + // skip the JCA overhead + try { + ap.engineInit(keyParams2); + byte[] encoded = ap.engineGetEncoded(); + ap.engineInit(sigParams); + byte[] encoded2 = ap.engineGetEncoded(); + return Arrays.equals(encoded, encoded2); + } catch (Exception e) { + if (DEBUG) { + e.printStackTrace(); + } + return false; + } + } + + /** + * Validate the specified RSAKey and its associated parameters against + * internal signature parameters. + */ + private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { + try { + AlgorithmParameterSpec keyParams = rsaKey.getParams(); + // validate key parameters + if (!isCompatible(rsaKey.getParams(), this.sigParams)) { + throw new InvalidKeyException + ("Key contains incompatible PSS parameter values"); + } + // validate key length + if (this.sigParams != null) { + Integer hLen = + DIGEST_LENGTHS.get(this.sigParams.getDigestAlgorithm()); + if (hLen == null) { + throw new ProviderException("Unsupported digest algo: " + + this.sigParams.getDigestAlgorithm()); + } + checkKeyLength(rsaKey, hLen, this.sigParams.getSaltLength()); + } + return rsaKey; + } catch (SignatureException e) { + throw new InvalidKeyException(e); + } + } + + /** + * Validate the specified Signature PSS parameters. + */ + private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p) + throws InvalidAlgorithmParameterException { + if (p == null) { + throw new InvalidAlgorithmParameterException + ("Parameters cannot be null"); + } + if (!(p instanceof PSSParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("parameters must be type PSSParameterSpec"); + } + // no need to validate again if same as current signature parameters + PSSParameterSpec params = (PSSParameterSpec) p; + if (params == this.sigParams) return params; + + RSAKey key = (this.privKey == null? this.pubKey : this.privKey); + // check against keyParams if set + if (key != null) { + if (!isCompatible(key.getParams(), params)) { + throw new InvalidAlgorithmParameterException + ("Signature parameters does not match key parameters"); + } + } + // now sanity check the parameter values + if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) { + throw new InvalidAlgorithmParameterException("Only supports MGF1"); + + } + if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) { + throw new InvalidAlgorithmParameterException + ("Only supports TrailerFieldBC(1)"); + + } + String digestAlgo = params.getDigestAlgorithm(); + // check key length again + if (key != null) { + try { + int hLen = DIGEST_LENGTHS.get(digestAlgo); + checkKeyLength(key, hLen, params.getSaltLength()); + } catch (SignatureException e) { + throw new InvalidAlgorithmParameterException(e); + } + } + return params; + } + + /** + * Ensure the object is initialized with key and parameters and + * reset digest + */ + private void ensureInit() throws SignatureException { + RSAKey key = (this.privKey == null? this.pubKey : this.privKey); + if (key == null) { + throw new SignatureException("Missing key"); + } + if (this.sigParams == null) { + // Parameters are required for signature verification + throw new SignatureException + ("Parameters required for RSASSA-PSS signatures"); + } + } + + /** + * Utility method for checking key length against digest length and + * salt length + */ + private static void checkKeyLength(RSAKey key, int digestLen, + int saltLen) throws SignatureException { + if (key != null) { + int keyLength = getKeyLengthInBits(key) >> 3; + int minLength = Math.addExact(Math.addExact(digestLen, saltLen), 2); + if (keyLength < minLength) { + throw new SignatureException + ("Key is too short, need min " + minLength); + } + } + } + + /** + * Reset the message digest if it is not already reset. + */ + private void resetDigest() { + if (digestReset == false) { + this.md.reset(); + digestReset = true; + } + } + + /** + * Return the message digest value. + */ + private byte[] getDigestValue() { + digestReset = true; + return this.md.digest(); + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte b) throws SignatureException { + ensureInit(); + this.md.update(b); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + ensureInit(); + this.md.update(b, off, len); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(ByteBuffer b) { + try { + ensureInit(); + } catch (SignatureException se) { + // hack for working around API bug + throw new RuntimeException(se.getMessage()); + } + this.md.update(b); + digestReset = false; + } + + // sign the data and return the signature. See JCA doc + @Override + protected byte[] engineSign() throws SignatureException { + ensureInit(); + byte[] mHash = getDigestValue(); + try { + byte[] encoded = encodeSignature(mHash); + byte[] encrypted = RSACore.rsa(encoded, privKey, true); + return encrypted; + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not sign data", e); + } catch (IOException e) { + throw new SignatureException("Could not encode data", e); + } + } + + // verify the data and return the result. See JCA doc + // should be reset to the state after engineInitVerify call. + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + ensureInit(); + try { + if (sigBytes.length != RSACore.getByteLength(this.pubKey)) { + throw new SignatureException + ("Signature length not correct: got " + + sigBytes.length + " but was expecting " + + RSACore.getByteLength(this.pubKey)); + } + byte[] mHash = getDigestValue(); + byte[] decrypted = RSACore.rsa(sigBytes, this.pubKey); + return decodeSignature(mHash, decrypted); + } catch (javax.crypto.BadPaddingException e) { + // occurs if the app has used the wrong RSA public key + // or if sigBytes is invalid + // return false rather than propagating the exception for + // compatibility/ease of use + return false; + } catch (IOException e) { + throw new SignatureException("Signature encoding error", e); + } finally { + resetDigest(); + } + } + + // return the modulus length in bits + private static int getKeyLengthInBits(RSAKey k) { + if (k != null) { + return k.getModulus().bitLength(); + } + return -1; + } + + /** + * Encode the digest 'mHash', return the to-be-signed data. + * Also used by the PKCS#11 provider. + */ + private byte[] encodeSignature(byte[] mHash) + throws IOException, DigestException { + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + String mgfDigestAlgo; + if (mgfParams != null) { + mgfDigestAlgo = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } else { + mgfDigestAlgo = this.md.getAlgorithm(); + } + try { + int emBits = getKeyLengthInBits(this.privKey) - 1; + int emLen =(emBits + 7) >> 3; + int hLen = this.md.getDigestLength(); + int dbLen = emLen - hLen - 1; + int sLen = this.sigParams.getSaltLength(); + + // maps DB into the corresponding region of EM and + // stores its bytes directly into EM + byte[] em = new byte[emLen]; + + // step7 and some of step8 + em[dbLen - sLen - 1] = (byte) 1; // set DB's padding2 into EM + em[em.length - 1] = (byte) 0xBC; // set trailer field of EM + + if (!digestReset) { + throw new ProviderException("Digest should be reset"); + } + // step5: generates M' using padding1, mHash, and salt + this.md.update(EIGHT_BYTES_OF_ZEROS); + digestReset = false; // mark digest as it now has data + this.md.update(mHash); + if (sLen != 0) { + // step4: generate random salt + byte[] salt = new byte[sLen]; + this.random.nextBytes(salt); + this.md.update(salt); + + // step8: set DB's salt into EM + System.arraycopy(salt, 0, em, dbLen - sLen, sLen); + } + // step6: generate H using M' + this.md.digest(em, dbLen, hLen); // set H field of EM + digestReset = true; + + // step7 and 8 are already covered by the code which setting up + // EM as above + + // step9 and 10: feed H into MGF and xor with DB in EM + MGF1 mgf1 = new MGF1(mgfDigestAlgo); + mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); + + // step11: set the leftmost (8emLen - emBits) bits of the leftmost + // octet to 0 + int numZeroBits = (emLen << 3) - emBits; + if (numZeroBits != 0) { + byte MASK = (byte) (0xff >>> numZeroBits); + em[0] = (byte) (em[0] & MASK); + } + + // step12: em should now holds maskedDB || hash h || 0xBC + return em; + } catch (NoSuchAlgorithmException e) { + throw new IOException(e.toString()); + } + } + + /** + * Decode the signature data. Verify that the object identifier matches + * and return the message digest. + */ + private boolean decodeSignature(byte[] mHash, byte[] em) + throws IOException { + int hLen = mHash.length; + int sLen = this.sigParams.getSaltLength(); + int emLen = em.length; + int emBits = getKeyLengthInBits(this.pubKey) - 1; + + // step3 + if (emLen < (hLen + sLen + 2)) { + return false; + } + + // step4 + if (em[emLen - 1] != (byte) 0xBC) { + return false; + } + + // step6: check if the leftmost (8emLen - emBits) bits of the leftmost + // octet are 0 + int numZeroBits = (emLen << 3) - emBits; + if (numZeroBits != 0) { + byte MASK = (byte) (0xff << (8 - numZeroBits)); + if ((em[0] & MASK) != 0) { + return false; + } + } + String mgfDigestAlgo; + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + if (mgfParams != null) { + mgfDigestAlgo = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } else { + mgfDigestAlgo = this.md.getAlgorithm(); + } + // step 7 and 8 + int dbLen = emLen - hLen - 1; + try { + MGF1 mgf1 = new MGF1(mgfDigestAlgo); + mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); + } catch (NoSuchAlgorithmException nsae) { + throw new IOException(nsae.toString()); + } + + // step9: set the leftmost (8emLen - emBits) bits of the leftmost + // octet to 0 + if (numZeroBits != 0) { + byte MASK = (byte) (0xff >>> numZeroBits); + em[0] = (byte) (em[0] & MASK); + } + + // step10 + int i = 0; + for (; i < dbLen - sLen - 1; i++) { + if (em[i] != 0) { + return false; + } + } + if (em[i] != 0x01) { + return false; + } + // step12 and 13 + this.md.update(EIGHT_BYTES_OF_ZEROS); + digestReset = false; + this.md.update(mHash); + if (sLen > 0) { + this.md.update(em, (dbLen - sLen), sLen); + } + byte[] digest2 = this.md.digest(); + digestReset = true; + + // step14 + byte[] digestInEM = Arrays.copyOfRange(em, dbLen, emLen - 1); + return MessageDigest.isEqual(digest2, digestInEM); + } + + // set parameter, not supported. See JCA doc + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + this.sigParams = validateSigParams(params); + // disallow changing parameters when digest has been used + if (!digestReset) { + throw new ProviderException + ("Cannot set parameters during operations"); + } + String newHashAlg = this.sigParams.getDigestAlgorithm(); + // re-allocate md if not yet assigned or algorithm changed + if ((this.md == null) || + !(this.md.getAlgorithm().equalsIgnoreCase(newHashAlg))) { + try { + this.md = MessageDigest.getInstance(newHashAlg); + } catch (NoSuchAlgorithmException nsae) { + // should not happen as we pick default digest algorithm + throw new InvalidAlgorithmParameterException + ("Unsupported digest algorithm " + + newHashAlg, nsae); + } + } + } + + // get parameter, not supported. See JCA doc + @Deprecated + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + AlgorithmParameters ap = null; + if (this.sigParams != null) { + try { + ap = AlgorithmParameters.getInstance("RSASSA-PSS"); + ap.init(this.sigParams); + } catch (GeneralSecurityException gse) { + throw new ProviderException(gse.getMessage()); + } + } + return ap; + } +} diff --git a/src/sun/security/rsa/RSAPadding.java b/src/sun/security/rsa/RSAPadding.java new file mode 100644 index 00000000..0c0708d8 --- /dev/null +++ b/src/sun/security/rsa/RSAPadding.java @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.util.*; + +import java.security.*; +import java.security.spec.*; + +import javax.crypto.BadPaddingException; +import javax.crypto.spec.PSource; +import javax.crypto.spec.OAEPParameterSpec; + +import sun.security.jca.JCAUtil; + +/** + * RSA padding and unpadding. + * + * The various PKCS#1 versions can be found in the IETF RFCs + * tracking the corresponding PKCS#1 standards. + * + * RFC 2313: PKCS#1 v1.5 + * RFC 2437: PKCS#1 v2.0 + * RFC 3447: PKCS#1 v2.1 + * RFC 8017: PKCS#1 v2.2 + * + * The format of PKCS#1 v1.5 padding is: + * + * 0x00 | BT | PS...PS | 0x00 | data...data + * + * where BT is the blocktype (1 or 2). The length of the entire string + * must be the same as the size of the modulus (i.e. 128 byte for a 1024 bit + * key). Per spec, the padding string must be at least 8 bytes long. That + * leaves up to (length of key in bytes) - 11 bytes for the data. + * + * OAEP padding was introduced in PKCS#1 v2.0 and is a bit more complicated + * and has a number of options. We support: + * + * . arbitrary hash functions ('Hash' in the specification), MessageDigest + * implementation must be available + * . MGF1 as the mask generation function + * . the empty string as the default value for label L and whatever + * specified in javax.crypto.spec.OAEPParameterSpec + * + * The algorithms (representations) are forwards-compatible: that is, + * the algorithm described in previous releases are in later releases. + * However, additional comments/checks/clarifications were added to the + * later versions based on real-world experience (e.g. stricter v1.5 + * format checking.) + * + * Note: RSA keys should be at least 512 bits long + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class RSAPadding { + + // NOTE: the constants below are embedded in the JCE RSACipher class + // file. Do not change without coordinating the update + + // PKCS#1 v1.5 padding, blocktype 1 (signing) + public final static int PAD_BLOCKTYPE_1 = 1; + // PKCS#1 v1.5 padding, blocktype 2 (encryption) + public final static int PAD_BLOCKTYPE_2 = 2; + // nopadding. Does not do anything, but allows simpler RSACipher code + public final static int PAD_NONE = 3; + // PKCS#1 v2.1 OAEP padding + public final static int PAD_OAEP_MGF1 = 4; + + // type, one of PAD_* + private final int type; + + // size of the padded block (i.e. size of the modulus) + private final int paddedSize; + + // PRNG used to generate padding bytes (PAD_BLOCKTYPE_2, PAD_OAEP_MGF1) + private SecureRandom random; + + // maximum size of the data + private final int maxDataSize; + + // OAEP: main message digest + private MessageDigest md; + + // OAEP: MGF1 + private MGF1 mgf; + + // OAEP: value of digest of data (user-supplied or zero-length) using md + private byte[] lHash; + + /** + * Get a RSAPadding instance of the specified type. + * Keys used with this padding must be paddedSize bytes long. + */ + public static RSAPadding getInstance(int type, int paddedSize) + throws InvalidKeyException, InvalidAlgorithmParameterException { + return new RSAPadding(type, paddedSize, null, null); + } + + /** + * Get a RSAPadding instance of the specified type. + * Keys used with this padding must be paddedSize bytes long. + */ + public static RSAPadding getInstance(int type, int paddedSize, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + return new RSAPadding(type, paddedSize, random, null); + } + + /** + * Get a RSAPadding instance of the specified type, which must be + * OAEP. Keys used with this padding must be paddedSize bytes long. + */ + public static RSAPadding getInstance(int type, int paddedSize, + SecureRandom random, OAEPParameterSpec spec) + throws InvalidKeyException, InvalidAlgorithmParameterException { + return new RSAPadding(type, paddedSize, random, spec); + } + + // internal constructor + private RSAPadding(int type, int paddedSize, SecureRandom random, + OAEPParameterSpec spec) throws InvalidKeyException, + InvalidAlgorithmParameterException { + this.type = type; + this.paddedSize = paddedSize; + this.random = random; + if (paddedSize < 64) { + // sanity check, already verified in RSASignature/RSACipher + throw new InvalidKeyException("Padded size must be at least 64"); + } + switch (type) { + case PAD_BLOCKTYPE_1: + case PAD_BLOCKTYPE_2: + maxDataSize = paddedSize - 11; + break; + case PAD_NONE: + maxDataSize = paddedSize; + break; + case PAD_OAEP_MGF1: + String mdName = "SHA-1"; + String mgfMdName = mdName; + byte[] digestInput = null; + try { + if (spec != null) { + mdName = spec.getDigestAlgorithm(); + String mgfName = spec.getMGFAlgorithm(); + if (!mgfName.equalsIgnoreCase("MGF1")) { + throw new InvalidAlgorithmParameterException + ("Unsupported MGF algo: " + mgfName); + } + mgfMdName = ((MGF1ParameterSpec)spec.getMGFParameters()) + .getDigestAlgorithm(); + PSource pSrc = spec.getPSource(); + String pSrcAlgo = pSrc.getAlgorithm(); + if (!pSrcAlgo.equalsIgnoreCase("PSpecified")) { + throw new InvalidAlgorithmParameterException + ("Unsupported pSource algo: " + pSrcAlgo); + } + digestInput = ((PSource.PSpecified) pSrc).getValue(); + } + md = MessageDigest.getInstance(mdName); + mgf = new MGF1(mgfMdName); + } catch (NoSuchAlgorithmException e) { + throw new InvalidKeyException("Digest not available", e); + } + lHash = getInitialHash(md, digestInput); + int digestLen = lHash.length; + maxDataSize = paddedSize - 2 - 2 * digestLen; + if (maxDataSize <= 0) { + throw new InvalidKeyException + ("Key is too short for encryption using OAEPPadding" + + " with " + mdName + " and " + mgf.getName()); + } + break; + default: + throw new InvalidKeyException("Invalid padding: " + type); + } + } + + // cache of hashes of zero length data + private static final Map emptyHashes = + Collections.synchronizedMap(new HashMap()); + + /** + * Return the value of the digest using the specified message digest + * md and the digest input digestInput. + * if digestInput is null or 0-length, zero length + * is used to generate the initial digest. + * Note: the md object must be in reset state + */ + private static byte[] getInitialHash(MessageDigest md, + byte[] digestInput) { + byte[] result; + if ((digestInput == null) || (digestInput.length == 0)) { + String digestName = md.getAlgorithm(); + result = emptyHashes.get(digestName); + if (result == null) { + result = md.digest(); + emptyHashes.put(digestName, result); + } + } else { + result = md.digest(digestInput); + } + return result; + } + + /** + * Return the maximum size of the plaintext data that can be processed + * using this object. + */ + public int getMaxDataSize() { + return maxDataSize; + } + + /** + * Pad the data and return the padded block. + */ + public byte[] pad(byte[] data, int ofs, int len) + throws BadPaddingException { + return pad(RSACore.convert(data, ofs, len)); + } + + /** + * Pad the data and return the padded block. + */ + public byte[] pad(byte[] data) throws BadPaddingException { + if (data.length > maxDataSize) { + throw new BadPaddingException("Data must be shorter than " + + (maxDataSize + 1) + " bytes"); + } + switch (type) { + case PAD_NONE: + return data; + case PAD_BLOCKTYPE_1: + case PAD_BLOCKTYPE_2: + return padV15(data); + case PAD_OAEP_MGF1: + return padOAEP(data); + default: + throw new AssertionError(); + } + } + + /** + * Unpad the padded block and return the data. + */ + public byte[] unpad(byte[] padded, int ofs, int len) + throws BadPaddingException { + return unpad(RSACore.convert(padded, ofs, len)); + } + + /** + * Unpad the padded block and return the data. + */ + public byte[] unpad(byte[] padded) throws BadPaddingException { + if (padded.length != paddedSize) { + throw new BadPaddingException("Decryption error"); + } + switch (type) { + case PAD_NONE: + return padded; + case PAD_BLOCKTYPE_1: + case PAD_BLOCKTYPE_2: + return unpadV15(padded); + case PAD_OAEP_MGF1: + return unpadOAEP(padded); + default: + throw new AssertionError(); + } + } + + /** + * PKCS#1 v1.5 padding (blocktype 1 and 2). + */ + private byte[] padV15(byte[] data) throws BadPaddingException { + byte[] padded = new byte[paddedSize]; + System.arraycopy(data, 0, padded, paddedSize - data.length, + data.length); + int psSize = paddedSize - 3 - data.length; + int k = 0; + padded[k++] = 0; + padded[k++] = (byte)type; + if (type == PAD_BLOCKTYPE_1) { + // blocktype 1: all padding bytes are 0xff + while (psSize-- > 0) { + padded[k++] = (byte)0xff; + } + } else { + // blocktype 2: padding bytes are random non-zero bytes + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + // generate non-zero padding bytes + // use a buffer to reduce calls to SecureRandom + byte[] r = new byte[64]; + int i = -1; + while (psSize-- > 0) { + int b; + do { + if (i < 0) { + random.nextBytes(r); + i = r.length - 1; + } + b = r[i--] & 0xff; + } while (b == 0); + padded[k++] = (byte)b; + } + } + return padded; + } + + /** + * PKCS#1 v1.5 unpadding (blocktype 1 (signature) and 2 (encryption)). + * + * Note that we want to make it a constant-time operation + */ + private byte[] unpadV15(byte[] padded) throws BadPaddingException { + int k = 0; + boolean bp = false; + + if (padded[k++] != 0) { + bp = true; + } + if (padded[k++] != type) { + bp = true; + } + int p = 0; + while (k < padded.length) { + int b = padded[k++] & 0xff; + if ((b == 0) && (p == 0)) { + p = k; + } + if ((k == padded.length) && (p == 0)) { + bp = true; + } + if ((type == PAD_BLOCKTYPE_1) && (b != 0xff) && + (p == 0)) { + bp = true; + } + } + int n = padded.length - p; + if (n > maxDataSize) { + bp = true; + } + + // copy useless padding array for a constant-time method + byte[] padding = new byte[p]; + System.arraycopy(padded, 0, padding, 0, p); + + byte[] data = new byte[n]; + System.arraycopy(padded, p, data, 0, n); + + BadPaddingException bpe = new BadPaddingException("Decryption error"); + + if (bp) { + throw bpe; + } else { + return data; + } + } + + /** + * PKCS#1 v2.0 OAEP padding (MGF1). + * Paragraph references refer to PKCS#1 v2.1 (June 14, 2002) + */ + private byte[] padOAEP(byte[] M) throws BadPaddingException { + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + int hLen = lHash.length; + + // 2.d: generate a random octet string seed of length hLen + // if necessary + byte[] seed = new byte[hLen]; + random.nextBytes(seed); + + // buffer for encoded message EM + byte[] EM = new byte[paddedSize]; + + // start and length of seed (as index into EM) + int seedStart = 1; + int seedLen = hLen; + + // copy seed into EM + System.arraycopy(seed, 0, EM, seedStart, seedLen); + + // start and length of data block DB in EM + // we place it inside of EM to reduce copying + int dbStart = hLen + 1; + int dbLen = EM.length - dbStart; + + // start of message M in EM + int mStart = paddedSize - M.length; + + // build DB + // 2.b: Concatenate lHash, PS, a single octet with hexadecimal value + // 0x01, and the message M to form a data block DB of length + // k - hLen -1 octets as DB = lHash || PS || 0x01 || M + // (note that PS is all zeros) + System.arraycopy(lHash, 0, EM, dbStart, hLen); + EM[mStart - 1] = 1; + System.arraycopy(M, 0, EM, mStart, M.length); + + // produce maskedDB + mgf.generateAndXor(EM, seedStart, seedLen, dbLen, EM, dbStart); + + // produce maskSeed + mgf.generateAndXor(EM, dbStart, dbLen, seedLen, EM, seedStart); + + return EM; + } + + /** + * PKCS#1 v2.1 OAEP unpadding (MGF1). + */ + private byte[] unpadOAEP(byte[] padded) throws BadPaddingException { + byte[] EM = padded; + boolean bp = false; + int hLen = lHash.length; + + if (EM[0] != 0) { + bp = true; + } + + int seedStart = 1; + int seedLen = hLen; + + int dbStart = hLen + 1; + int dbLen = EM.length - dbStart; + + mgf.generateAndXor(EM, dbStart, dbLen, seedLen, EM, seedStart); + mgf.generateAndXor(EM, seedStart, seedLen, dbLen, EM, dbStart); + + // verify lHash == lHash' + for (int i = 0; i < hLen; i++) { + if (lHash[i] != EM[dbStart + i]) { + bp = true; + } + } + + int padStart = dbStart + hLen; + int onePos = -1; + + for (int i = padStart; i < EM.length; i++) { + int value = EM[i]; + if (onePos == -1) { + if (value == 0x00) { + // continue; + } else if (value == 0x01) { + onePos = i; + } else { // Anything other than {0,1} is bad. + bp = true; + } + } + } + + // We either ran off the rails or found something other than 0/1. + if (onePos == -1) { + bp = true; + onePos = EM.length - 1; // Don't inadvertently return any data. + } + + int mStart = onePos + 1; + + // copy useless padding array for a constant-time method + byte [] tmp = new byte[mStart - padStart]; + System.arraycopy(EM, padStart, tmp, 0, tmp.length); + + byte [] m = new byte[EM.length - mStart]; + System.arraycopy(EM, mStart, m, 0, m.length); + + BadPaddingException bpe = new BadPaddingException("Decryption error"); + + if (bp) { + throw bpe; + } else { + return m; + } + } +} diff --git a/src/sun/security/rsa/RSAPrivateCrtKeyImpl.java b/src/sun/security/rsa/RSAPrivateCrtKeyImpl.java new file mode 100644 index 00000000..cf9f4550 --- /dev/null +++ b/src/sun/security/rsa/RSAPrivateCrtKeyImpl.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.*; +import java.security.interfaces.*; + +import sun.security.util.*; + +import sun.security.x509.AlgorithmId; +import sun.security.pkcs.PKCS8Key; + +import static sun.security.rsa.RSAUtil.KeyType; + +/** + * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in CRT form. + * For non-CRT private keys, see RSAPrivateKeyImpl. We need separate classes + * to ensure correct behavior in instanceof checks, etc. + * + * Note: RSA keys must be at least 512 bits long + * + * @see RSAPrivateKeyImpl + * @see RSAKeyFactory + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class RSAPrivateCrtKeyImpl + extends PKCS8Key implements RSAPrivateCrtKey { + + private static final long serialVersionUID = -1326088454257084918L; + + private BigInteger n; // modulus + private BigInteger e; // public exponent + private BigInteger d; // private exponent + private BigInteger p; // prime p + private BigInteger q; // prime q + private BigInteger pe; // prime exponent p + private BigInteger qe; // prime exponent q + private BigInteger coeff; // CRT coeffcient + + // Optional parameters associated with this RSA key + // specified in the encoding of its AlgorithmId. + // Must be null for "RSA" keys. + private AlgorithmParameterSpec keyParams; + + /** + * Generate a new key from its encoding. Returns a CRT key if possible + * and a non-CRT key otherwise. Used by RSAKeyFactory. + */ + public static RSAPrivateKey newKey(byte[] encoded) + throws InvalidKeyException { + RSAPrivateCrtKeyImpl key = new RSAPrivateCrtKeyImpl(encoded); + // check all CRT-specific components are available, if any one + // missing, return a non-CRT key instead + if ((key.getPublicExponent().signum() == 0) || + (key.getPrimeExponentP().signum() == 0) || + (key.getPrimeExponentQ().signum() == 0) || + (key.getPrimeP().signum() == 0) || + (key.getPrimeQ().signum() == 0) || + (key.getCrtCoefficient().signum() == 0)) { + return new RSAPrivateKeyImpl( + key.algid, + key.getModulus(), + key.getPrivateExponent() + ); + } else { + return key; + } + } + + /** + * Generate a new key from the specified type and components. + * Returns a CRT key if possible and a non-CRT key otherwise. + * Used by SunPKCS11 provider. + */ + public static RSAPrivateKey newKey(KeyType type, + AlgorithmParameterSpec params, + BigInteger n, BigInteger e, BigInteger d, + BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, + BigInteger coeff) throws InvalidKeyException { + RSAPrivateKey key; + AlgorithmId rsaId = RSAUtil.createAlgorithmId(type, params); + if ((e.signum() == 0) || (p.signum() == 0) || + (q.signum() == 0) || (pe.signum() == 0) || + (qe.signum() == 0) || (coeff.signum() == 0)) { + // if any component is missing, return a non-CRT key + return new RSAPrivateKeyImpl(rsaId, n, d); + } else { + return new RSAPrivateCrtKeyImpl(rsaId, n, e, d, + p, q, pe, qe, coeff); + } + } + + /** + * Construct a key from its encoding. Called from newKey above. + */ + RSAPrivateCrtKeyImpl(byte[] encoded) throws InvalidKeyException { + if (encoded == null || encoded.length == 0) { + throw new InvalidKeyException("Missing key encoding"); + } + + decode(encoded); + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); + try { + // this will check the validity of params + this.keyParams = RSAUtil.getParamSpec(algid); + } catch (ProviderException e) { + throw new InvalidKeyException(e); + } + } + + /** + * Construct a RSA key from its components. Used by the + * RSAKeyFactory and the RSAKeyPairGenerator. + */ + RSAPrivateCrtKeyImpl(AlgorithmId rsaId, + BigInteger n, BigInteger e, BigInteger d, + BigInteger p, BigInteger q, BigInteger pe, BigInteger qe, + BigInteger coeff) throws InvalidKeyException { + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); + + this.n = n; + this.e = e; + this.d = d; + this.p = p; + this.q = q; + this.pe = pe; + this.qe = qe; + this.coeff = coeff; + this.keyParams = RSAUtil.getParamSpec(rsaId); + + // generate the encoding + algid = rsaId; + try { + DerOutputStream out = new DerOutputStream(); + out.putInteger(0); // version must be 0 + out.putInteger(n); + out.putInteger(e); + out.putInteger(d); + out.putInteger(p); + out.putInteger(q); + out.putInteger(pe); + out.putInteger(qe); + out.putInteger(coeff); + DerValue val = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + key = val.toByteArray(); + } catch (IOException exc) { + // should never occur + throw new InvalidKeyException(exc); + } + } + + // see JCA doc + @Override + public String getAlgorithm() { + return algid.getName(); + } + + // see JCA doc + @Override + public BigInteger getModulus() { + return n; + } + + // see JCA doc + @Override + public BigInteger getPublicExponent() { + return e; + } + + // see JCA doc + @Override + public BigInteger getPrivateExponent() { + return d; + } + + // see JCA doc + @Override + public BigInteger getPrimeP() { + return p; + } + + // see JCA doc + @Override + public BigInteger getPrimeQ() { + return q; + } + + // see JCA doc + @Override + public BigInteger getPrimeExponentP() { + return pe; + } + + // see JCA doc + @Override + public BigInteger getPrimeExponentQ() { + return qe; + } + + // see JCA doc + @Override + public BigInteger getCrtCoefficient() { + return coeff; + } + + // see JCA doc + @Override + public AlgorithmParameterSpec getParams() { + return keyParams; + } + + // return a string representation of this key for debugging + @Override + public String toString() { + return "SunRsaSign " + getAlgorithm() + " private CRT key, " + n.bitLength() + + " bits" + "\n params: " + keyParams + "\n modulus: " + n + + "\n private exponent: " + d; + } + + /** + * Parse the key. Called by PKCS8Key. + */ + protected void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(key); + DerValue derValue = in.getDerValue(); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + DerInputStream data = derValue.data; + int version = data.getInteger(); + if (version != 0) { + throw new IOException("Version must be 0"); + } + n = getBigInteger(data); + e = getBigInteger(data); + d = getBigInteger(data); + p = getBigInteger(data); + q = getBigInteger(data); + pe = getBigInteger(data); + qe = getBigInteger(data); + coeff = getBigInteger(data); + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + } catch (IOException e) { + throw new InvalidKeyException("Invalid RSA private key", e); + } + } + + /** + * Read a BigInteger from the DerInputStream. + */ + static BigInteger getBigInteger(DerInputStream data) throws IOException { + BigInteger b = data.getBigInteger(); + + /* + * Some implementations do not correctly encode ASN.1 INTEGER values + * in 2's complement format, resulting in a negative integer when + * decoded. Correct the error by converting it to a positive integer. + * + * See CR 6255949 + */ + if (b.signum() < 0) { + b = new BigInteger(1, b.toByteArray()); + } + return b; + } +} diff --git a/src/sun/security/rsa/RSAPrivateKeyImpl.java b/src/sun/security/rsa/RSAPrivateKeyImpl.java new file mode 100644 index 00000000..c42bd118 --- /dev/null +++ b/src/sun/security/rsa/RSAPrivateKeyImpl.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.interfaces.*; + +import sun.security.util.*; +import sun.security.x509.AlgorithmId; +import sun.security.pkcs.PKCS8Key; + +/** + * RSA private key implementation for "RSA", "RSASSA-PSS" algorithms in non-CRT + * form (modulus, private exponent only). For CRT private keys, see + * RSAPrivateCrtKeyImpl. We need separate classes to ensure correct behavior + * in instanceof checks, etc. + * + * Note: RSA keys must be at least 512 bits long + * + * @see RSAPrivateCrtKeyImpl + * @see RSAKeyFactory + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class RSAPrivateKeyImpl extends PKCS8Key implements RSAPrivateKey { + + private static final long serialVersionUID = -33106691987952810L; + + private final BigInteger n; // modulus + private final BigInteger d; // private exponent + + // optional parameters associated with this RSA key + // specified in the encoding of its AlgorithmId. + // must be null for "RSA" keys. + private final AlgorithmParameterSpec keyParams; + + /** + * Construct a key from its components. Used by the + * RSAKeyFactory and the RSAKeyPairGenerator. + */ + RSAPrivateKeyImpl(AlgorithmId rsaId, BigInteger n, BigInteger d) + throws InvalidKeyException { + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), null); + + this.n = n; + this.d = d; + this.keyParams = RSAUtil.getParamSpec(rsaId); + + // generate the encoding + algid = rsaId; + try { + DerOutputStream out = new DerOutputStream(); + out.putInteger(0); // version must be 0 + out.putInteger(n); + out.putInteger(0); + out.putInteger(d); + out.putInteger(0); + out.putInteger(0); + out.putInteger(0); + out.putInteger(0); + out.putInteger(0); + DerValue val = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + key = val.toByteArray(); + } catch (IOException exc) { + // should never occur + throw new InvalidKeyException(exc); + } + } + + // see JCA doc + @Override + public String getAlgorithm() { + return algid.getName(); + } + + // see JCA doc + @Override + public BigInteger getModulus() { + return n; + } + + // see JCA doc + @Override + public BigInteger getPrivateExponent() { + return d; + } + + // see JCA doc + @Override + public AlgorithmParameterSpec getParams() { + return keyParams; + } + + // return a string representation of this key for debugging + @Override + public String toString() { + return "Sun " + getAlgorithm() + " private key, " + n.bitLength() + + " bits" + "\n params: " + keyParams + "\n modulus: " + n + + "\n private exponent: " + d; + } +} diff --git a/src/sun/security/rsa/RSAPublicKeyImpl.java b/src/sun/security/rsa/RSAPublicKeyImpl.java new file mode 100644 index 00000000..634034bd --- /dev/null +++ b/src/sun/security/rsa/RSAPublicKeyImpl.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.IOException; +import java.math.BigInteger; + +import java.security.*; +import java.security.spec.*; +import java.security.interfaces.*; + +import sun.security.util.*; +import sun.security.x509.X509Key; +import sun.security.x509.AlgorithmId; + +import static sun.security.rsa.RSAUtil.KeyType; + +/** + * RSA public key implementation for "RSA", "RSASSA-PSS" algorithms. + * + * Note: RSA keys must be at least 512 bits long + * + * @see RSAPrivateCrtKeyImpl + * @see RSAPrivateKeyImpl + * @see RSAKeyFactory + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class RSAPublicKeyImpl extends X509Key implements RSAPublicKey { + + private static final long serialVersionUID = 2644735423591199609L; + private static final BigInteger THREE = BigInteger.valueOf(3); + + private BigInteger n; // modulus + private BigInteger e; // public exponent + + // optional parameters associated with this RSA key + // specified in the encoding of its AlgorithmId + // must be null for "RSA" keys. + private AlgorithmParameterSpec keyParams; + + /** + * Generate a new RSAPublicKey from the specified encoding. + * Used by SunPKCS11 provider. + */ + public static RSAPublicKey newKey(byte[] encoded) + throws InvalidKeyException { + return new RSAPublicKeyImpl(encoded); + } + + /** + * Generate a new RSAPublicKey from the specified type and components. + * Used by SunPKCS11 provider. + */ + public static RSAPublicKey newKey(KeyType type, + AlgorithmParameterSpec params, BigInteger n, BigInteger e) + throws InvalidKeyException { + AlgorithmId rsaId = RSAUtil.createAlgorithmId(type, params); + return new RSAPublicKeyImpl(rsaId, n, e); + } + + /** + * Construct a RSA key from AlgorithmId and its components. Used by + * RSAKeyFactory and RSAKeyPairGenerator. + */ + RSAPublicKeyImpl(AlgorithmId rsaId, BigInteger n, BigInteger e) + throws InvalidKeyException { + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); + checkExponentRange(n, e); + + this.n = n; + this.e = e; + this.keyParams = RSAUtil.getParamSpec(rsaId); + + // generate the encoding + algid = rsaId; + try { + DerOutputStream out = new DerOutputStream(); + out.putInteger(n); + out.putInteger(e); + byte[] keyArray = + new DerValue(DerValue.tag_Sequence, + out.toByteArray()).toByteArray(); + setKey(new BitArray(keyArray.length*8, keyArray)); + } catch (IOException exc) { + // should never occur + throw new InvalidKeyException(exc); + } + } + + /** + * Construct a key from its encoding. Used by RSAKeyFactory. + */ + RSAPublicKeyImpl(byte[] encoded) throws InvalidKeyException { + if (encoded == null || encoded.length == 0) { + throw new InvalidKeyException("Missing key encoding"); + } + decode(encoded); // this sets n and e value + RSAKeyFactory.checkRSAProviderKeyLengths(n.bitLength(), e); + checkExponentRange(n, e); + + try { + // this will check the validity of params + this.keyParams = RSAUtil.getParamSpec(algid); + } catch (ProviderException e) { + throw new InvalidKeyException(e); + } + } + + // pkg private utility method for checking RSA modulus and public exponent + static void checkExponentRange(BigInteger mod, BigInteger exp) + throws InvalidKeyException { + // the exponent should be smaller than the modulus + if (exp.compareTo(mod) >= 0) { + throw new InvalidKeyException("exponent is larger than modulus"); + } + + // the exponent should be at least 3 + if (exp.compareTo(THREE) < 0) { + throw new InvalidKeyException("exponent is smaller than 3"); + } + } + + // see JCA doc + @Override + public String getAlgorithm() { + return algid.getName(); + } + + // see JCA doc + @Override + public BigInteger getModulus() { + return n; + } + + // see JCA doc + @Override + public BigInteger getPublicExponent() { + return e; + } + + // see JCA doc + @Override + public AlgorithmParameterSpec getParams() { + return keyParams; + } + + /** + * Parse the key. Called by X509Key. + */ + protected void parseKeyBits() throws InvalidKeyException { + try { + DerInputStream in = new DerInputStream(getKey().toByteArray()); + DerValue derValue = in.getDerValue(); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Not a SEQUENCE"); + } + DerInputStream data = derValue.data; + n = RSAPrivateCrtKeyImpl.getBigInteger(data); + e = RSAPrivateCrtKeyImpl.getBigInteger(data); + if (derValue.data.available() != 0) { + throw new IOException("Extra data available"); + } + } catch (IOException e) { + throw new InvalidKeyException("Invalid RSA public key", e); + } + } + + // return a string representation of this key for debugging + @Override + public String toString() { + return "Sun " + getAlgorithm() + " public key, " + n.bitLength() + + " bits" + "\n params: " + keyParams + "\n modulus: " + n + + "\n public exponent: " + e; + } + + protected Object writeReplace() throws java.io.ObjectStreamException { + return new KeyRep(KeyRep.Type.PUBLIC, + getAlgorithm(), + getFormat(), + getEncoded()); + } +} diff --git a/src/sun/security/rsa/RSASignature.java b/src/sun/security/rsa/RSASignature.java new file mode 100644 index 00000000..bbfd280e --- /dev/null +++ b/src/sun/security/rsa/RSASignature.java @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.AlgorithmParameterSpec; + +import sun.security.rsa.RSAUtil.KeyType; +import sun.security.util.*; +import sun.security.x509.AlgorithmId; + +/** + * PKCS#1 v1.5 RSA signatures with the various message digest algorithms. + * This file contains an abstract base class with all the logic plus + * a nested static class for each of the message digest algorithms + * (see end of the file). We support MD2, MD5, SHA-1, SHA-224, SHA-256, + * SHA-384, SHA-512, SHA-512/224, and SHA-512/256. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public abstract class RSASignature extends SignatureSpi { + + // we sign an ASN.1 SEQUENCE of AlgorithmId and digest + // it has the form 30:xx:30:xx:[digestOID]:05:00:04:xx:[digest] + // this means the encoded length is (8 + digestOID.length + digest.length) + private static final int baseLength = 8; + + // object identifier for the message digest algorithm used + private final ObjectIdentifier digestOID; + + // length of the encoded signature blob + private final int encodedLength; + + // message digest implementation we use + private final MessageDigest md; + // flag indicating whether the digest is reset + private boolean digestReset; + + // private key, if initialized for signing + private RSAPrivateKey privateKey; + // public key, if initialized for verifying + private RSAPublicKey publicKey; + + // padding to use, set when the initSign/initVerify is called + private RSAPadding padding; + + /** + * Construct a new RSASignature. Used by subclasses. + */ + RSASignature(String algorithm, ObjectIdentifier digestOID, int oidLength) { + this.digestOID = digestOID; + try { + md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + digestReset = true; + encodedLength = baseLength + oidLength + md.getDigestLength(); + } + + // initialize for verification. See JCA doc + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + RSAPublicKey rsaKey = (RSAPublicKey)RSAKeyFactory.toRSAKey(publicKey); + this.privateKey = null; + this.publicKey = rsaKey; + initCommon(rsaKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + RSAPrivateKey rsaKey = + (RSAPrivateKey)RSAKeyFactory.toRSAKey(privateKey); + this.privateKey = rsaKey; + this.publicKey = null; + initCommon(rsaKey, random); + } + + /** + * Init code common to sign and verify. + */ + private void initCommon(RSAKey rsaKey, SecureRandom random) + throws InvalidKeyException { + try { + RSAUtil.checkParamsAgainstType(KeyType.RSA, rsaKey.getParams()); + } catch (ProviderException e) { + throw new InvalidKeyException("Invalid key for RSA signatures", e); + } + resetDigest(); + int keySize = RSACore.getByteLength(rsaKey); + try { + padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, keySize, random); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidKeyException(iape.getMessage()); + } + int maxDataSize = padding.getMaxDataSize(); + if (encodedLength > maxDataSize) { + throw new InvalidKeyException + ("Key is too short for this signature algorithm"); + } + } + + /** + * Reset the message digest if it is not already reset. + */ + private void resetDigest() { + if (digestReset == false) { + md.reset(); + digestReset = true; + } + } + + /** + * Return the message digest value. + */ + private byte[] getDigestValue() { + digestReset = true; + return md.digest(); + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte b) throws SignatureException { + md.update(b); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + md.update(b, off, len); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(ByteBuffer b) { + md.update(b); + digestReset = false; + } + + // sign the data and return the signature. See JCA doc + @Override + protected byte[] engineSign() throws SignatureException { + if (privateKey == null) { + throw new SignatureException("Missing private key"); + } + byte[] digest = getDigestValue(); + try { + byte[] encoded = encodeSignature(digestOID, digest); + byte[] padded = padding.pad(encoded); + byte[] encrypted = RSACore.rsa(padded, privateKey, true); + return encrypted; + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not sign data", e); + } catch (IOException e) { + throw new SignatureException("Could not encode data", e); + } + } + + // verify the data and return the result. See JCA doc + // should be reset to the state after engineInitVerify call. + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + if (publicKey == null) { + throw new SignatureException("Missing public key"); + } + try { + if (sigBytes.length != RSACore.getByteLength(publicKey)) { + throw new SignatureException("Signature length not correct: got " + + sigBytes.length + " but was expecting " + + RSACore.getByteLength(publicKey)); + } + byte[] digest = getDigestValue(); + byte[] decrypted = RSACore.rsa(sigBytes, publicKey); + byte[] unpadded = padding.unpad(decrypted); + byte[] decodedDigest = decodeSignature(digestOID, unpadded); + return MessageDigest.isEqual(digest, decodedDigest); + } catch (javax.crypto.BadPaddingException e) { + // occurs if the app has used the wrong RSA public key + // or if sigBytes is invalid + // return false rather than propagating the exception for + // compatibility/ease of use + return false; + } catch (IOException e) { + throw new SignatureException("Signature encoding error", e); + } finally { + resetDigest(); + } + } + + /** + * Encode the digest, return the to-be-signed data. + * Also used by the PKCS#11 provider. + */ + public static byte[] encodeSignature(ObjectIdentifier oid, byte[] digest) + throws IOException { + DerOutputStream out = new DerOutputStream(); + new AlgorithmId(oid).encode(out); + out.putOctetString(digest); + DerValue result = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + return result.toByteArray(); + } + + /** + * Decode the signature data. Verify that the object identifier matches + * and return the message digest. + */ + public static byte[] decodeSignature(ObjectIdentifier oid, byte[] signature) + throws IOException { + DerInputStream in = new DerInputStream(signature); + DerValue[] values = in.getSequence(2); + if ((values.length != 2) || (in.available() != 0)) { + throw new IOException("SEQUENCE length error"); + } + AlgorithmId algId = AlgorithmId.parse(values[0]); + if (algId.getOID().equals((Object)oid) == false) { + throw new IOException("ObjectIdentifier mismatch: " + + algId.getOID()); + } + if (algId.getEncodedParams() != null) { + throw new IOException("Unexpected AlgorithmId parameters"); + } + byte[] digest = values[1].getOctetString(); + return digest; + } + + // set parameter, not supported. See JCA doc + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + // See JCA doc + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("No parameters accepted"); + } + } + + // get parameter, not supported. See JCA doc + @Deprecated + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + // See JCA doc + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + // Nested class for MD2withRSA signatures + public static final class MD2withRSA extends RSASignature { + public MD2withRSA() { + super("MD2", AlgorithmId.MD2_oid, 10); + } + } + + // Nested class for MD5withRSA signatures + public static final class MD5withRSA extends RSASignature { + public MD5withRSA() { + super("MD5", AlgorithmId.MD5_oid, 10); + } + } + + // Nested class for SHA1withRSA signatures + public static final class SHA1withRSA extends RSASignature { + public SHA1withRSA() { + super("SHA-1", AlgorithmId.SHA_oid, 7); + } + } + + // Nested class for SHA224withRSA signatures + public static final class SHA224withRSA extends RSASignature { + public SHA224withRSA() { + super("SHA-224", AlgorithmId.SHA224_oid, 11); + } + } + + // Nested class for SHA256withRSA signatures + public static final class SHA256withRSA extends RSASignature { + public SHA256withRSA() { + super("SHA-256", AlgorithmId.SHA256_oid, 11); + } + } + + // Nested class for SHA384withRSA signatures + public static final class SHA384withRSA extends RSASignature { + public SHA384withRSA() { + super("SHA-384", AlgorithmId.SHA384_oid, 11); + } + } + + // Nested class for SHA512withRSA signatures + public static final class SHA512withRSA extends RSASignature { + public SHA512withRSA() { + super("SHA-512", AlgorithmId.SHA512_oid, 11); + } + } + + // Nested class for SHA512/224withRSA signatures + public static final class SHA512_224withRSA extends RSASignature { + public SHA512_224withRSA() { + super("SHA-512/224", AlgorithmId.SHA512_224_oid, 11); + } + } + + // Nested class for SHA512/256withRSA signatures + public static final class SHA512_256withRSA extends RSASignature { + public SHA512_256withRSA() { + super("SHA-512/256", AlgorithmId.SHA512_256_oid, 11); + } + } +} diff --git a/src/sun/security/rsa/RSAUtil.java b/src/sun/security/rsa/RSAUtil.java new file mode 100644 index 00000000..f1f268af --- /dev/null +++ b/src/sun/security/rsa/RSAUtil.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.security.*; +import java.security.spec.*; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; + +/** + * Utility class for SunRsaSign provider. + * Currently used by RSAKeyPairGenerator and RSAKeyFactory. + * + * @since 8 + */ +public class RSAUtil { + + public enum KeyType { + RSA ("RSA"), + PSS ("RSASSA-PSS") + ; + + private final String algo; + + KeyType(String keyAlgo) { + this.algo = keyAlgo; + } + public String keyAlgo() { + return algo; + } + public static KeyType lookup(String name) + throws InvalidKeyException, ProviderException { + if (name == null) { + throw new InvalidKeyException("Null key algorithm"); + } + for (KeyType kt : KeyType.values()) { + if (kt.keyAlgo().equalsIgnoreCase(name)) { + return kt; + } + } + // no match + throw new ProviderException("Unsupported algorithm " + name); + } + } + + public static void checkParamsAgainstType(KeyType type, + AlgorithmParameterSpec paramSpec) throws ProviderException { + switch (type) { + case RSA: + if (paramSpec != null) { + throw new ProviderException("null params expected for " + + type.keyAlgo()); + } + break; + case PSS: + if ((paramSpec != null) && + !(paramSpec instanceof PSSParameterSpec)) { + throw new ProviderException + ("PSSParmeterSpec expected for " + type.keyAlgo()); + } + break; + default: + throw new ProviderException + ("Unsupported RSA algorithm " + type); + } + } + + public static AlgorithmId createAlgorithmId(KeyType type, + AlgorithmParameterSpec paramSpec) throws ProviderException { + + checkParamsAgainstType(type, paramSpec); + + ObjectIdentifier oid = null; + AlgorithmParameters params = null; + try { + switch (type) { + case RSA: + oid = AlgorithmId.RSAEncryption_oid; + break; + case PSS: + if (paramSpec != null) { + params = AlgorithmParameters.getInstance(type.keyAlgo()); + params.init(paramSpec); + } + oid = AlgorithmId.RSASSA_PSS_oid; + break; + default: + throw new ProviderException + ("Unsupported RSA algorithm " + type); + } + AlgorithmId result; + if (params == null) { + result = new AlgorithmId(oid); + } else { + result = new AlgorithmId(oid, params); + } + return result; + } catch (NoSuchAlgorithmException | InvalidParameterSpecException e) { + // should not happen + throw new ProviderException(e); + } + } + + public static AlgorithmParameterSpec getParamSpec(AlgorithmId algid) + throws ProviderException { + if (algid == null) { + throw new ProviderException("AlgorithmId should not be null"); + } + return getParamSpec(algid.getParameters()); + } + + public static AlgorithmParameterSpec getParamSpec(AlgorithmParameters params) + throws ProviderException { + if (params == null) return null; + + try { + String algName = params.getAlgorithm(); + KeyType type = KeyType.lookup(algName); + Class specCls; + switch (type) { + case RSA: + throw new ProviderException("No params accepted for " + + type.keyAlgo()); + case PSS: + specCls = PSSParameterSpec.class; + break; + default: + throw new ProviderException("Unsupported RSA algorithm: " + algName); + } + return params.getParameterSpec(specCls); + } catch (ProviderException pe) { + // pass it up + throw pe; + } catch (Exception e) { + throw new ProviderException(e); + } + } +} diff --git a/src/sun/security/rsa/SunRsaSign.java b/src/sun/security/rsa/SunRsaSign.java new file mode 100644 index 00000000..65ae02a0 --- /dev/null +++ b/src/sun/security/rsa/SunRsaSign.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.util.*; + +import java.security.*; + +import sun.security.action.PutAllAction; + +/** + * Provider class for the RSA signature provider. Supports RSA keyfactory, + * keypair generation, and RSA signatures. + * + * @since 1.5 + * @author Andreas Sterbenz + */ +public final class SunRsaSign extends Provider { + + private static final long serialVersionUID = 866040293550393045L; + + public SunRsaSign() { + super("SunRsaSign", 1.8d, "Sun RSA signature provider"); + + // if there is no security manager installed, put directly into + // the provider. Otherwise, create a temporary map and use a + // doPrivileged() call at the end to transfer the contents + if (System.getSecurityManager() == null) { + SunRsaSignEntries.putEntries(this); + } else { + // use LinkedHashMap to preserve the order of the PRNGs + Map map = new HashMap<>(); + SunRsaSignEntries.putEntries(map); + AccessController.doPrivileged(new PutAllAction(this, map)); + } + } + +} diff --git a/src/sun/security/rsa/SunRsaSignEntries.java b/src/sun/security/rsa/SunRsaSignEntries.java new file mode 100644 index 00000000..b9dc9720 --- /dev/null +++ b/src/sun/security/rsa/SunRsaSignEntries.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.rsa; + +import java.util.Map; + +/** + * Defines the entries of the SunRsaSign provider. + * + * @author Andreas Sterbenz + */ +public final class SunRsaSignEntries { + + private SunRsaSignEntries() { + // empty + } + + public static void putEntries(Map map) { + + // main algorithms + map.put("KeyFactory.RSA", + "sun.security.rsa.RSAKeyFactory$Legacy"); + map.put("KeyPairGenerator.RSA", + "sun.security.rsa.RSAKeyPairGenerator$Legacy"); + map.put("Signature.MD2withRSA", + "sun.security.rsa.RSASignature$MD2withRSA"); + map.put("Signature.MD5withRSA", + "sun.security.rsa.RSASignature$MD5withRSA"); + map.put("Signature.SHA1withRSA", + "sun.security.rsa.RSASignature$SHA1withRSA"); + map.put("Signature.SHA224withRSA", + "sun.security.rsa.RSASignature$SHA224withRSA"); + map.put("Signature.SHA256withRSA", + "sun.security.rsa.RSASignature$SHA256withRSA"); + map.put("Signature.SHA384withRSA", + "sun.security.rsa.RSASignature$SHA384withRSA"); + map.put("Signature.SHA512withRSA", + "sun.security.rsa.RSASignature$SHA512withRSA"); + map.put("Signature.SHA512/224withRSA", + "sun.security.rsa.RSASignature$SHA512_224withRSA"); + map.put("Signature.SHA512/256withRSA", + "sun.security.rsa.RSASignature$SHA512_256withRSA"); + + map.put("KeyFactory.RSASSA-PSS", + "sun.security.rsa.RSAKeyFactory$PSS"); + map.put("KeyPairGenerator.RSASSA-PSS", + "sun.security.rsa.RSAKeyPairGenerator$PSS"); + map.put("Signature.RSASSA-PSS", + "sun.security.rsa.RSAPSSSignature"); + map.put("AlgorithmParameters.RSASSA-PSS", + "sun.security.rsa.PSSParameters"); + + // attributes for supported key classes + String rsaKeyClasses = "java.security.interfaces.RSAPublicKey" + + "|java.security.interfaces.RSAPrivateKey"; + map.put("Signature.MD2withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.MD5withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA1withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA224withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA256withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA384withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA512withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA512/224withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.SHA512/256withRSA SupportedKeyClasses", rsaKeyClasses); + map.put("Signature.RSASSA-PSS SupportedKeyClasses", rsaKeyClasses); + + // aliases + map.put("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); + map.put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); + + map.put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); + map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", "MD2withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); + map.put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.15", "SHA512/224withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.15", "SHA512/224withRSA"); + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.16", "SHA512/256withRSA"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.16", "SHA512/256withRSA"); + + map.put("Alg.Alias.KeyFactory.1.2.840.113549.1.1.10", "RSASSA-PSS"); + map.put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + map.put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.10", "RSASSA-PSS"); + map.put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + map.put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); + map.put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + map.put("Alg.Alias.AlgorithmParameters.1.2.840.113549.1.1.10", "RSASSA-PSS"); + map.put("Alg.Alias.AlgorithmParameters.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + } +} diff --git a/src/sun/security/smartcardio/CardImpl.java b/src/sun/security/smartcardio/CardImpl.java new file mode 100644 index 00000000..af731222 --- /dev/null +++ b/src/sun/security/smartcardio/CardImpl.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import javax.smartcardio.*; + +import static sun.security.smartcardio.PCSC.SCARD_LEAVE_CARD; +import static sun.security.smartcardio.PCSC.SCARD_RESET_CARD; +import static sun.security.smartcardio.PCSC.SCARD_SHARE_DIRECT; +import static sun.security.smartcardio.PCSC.SCARD_SHARE_SHARED; +import static sun.security.smartcardio.PCSC.SCARD_W_REMOVED_CARD; +import static sun.security.smartcardio.PCSC.SCardBeginTransaction; +import static sun.security.smartcardio.PCSC.SCardConnect; +import static sun.security.smartcardio.PCSC.SCardControl; +import static sun.security.smartcardio.PCSC.SCardDisconnect; +import static sun.security.smartcardio.PCSC.SCardEndTransaction; +import static sun.security.smartcardio.PCSC.SCardStatus; +import static sun.security.smartcardio.PCSC.SCardTransmit; + +/** + * Card implementation. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class CardImpl extends Card { + + private static enum State { OK, REMOVED, DISCONNECTED }; + + // the terminal that created this card + private final TerminalImpl terminal; + + // the native SCARDHANDLE + final long cardId; + + // atr of this card + private final ATR atr; + + // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1 + final int protocol; + + // the basic logical channel (channel 0) + private final ChannelImpl basicChannel; + + // state of this card connection + private volatile State state; + + // thread holding exclusive access to the card, or null + private volatile Thread exclusiveThread; + + // used for platform specific logic + private static final boolean isWindows; + + static { + final String osName = AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty("os.name")); + isWindows = osName.startsWith("Windows"); + } + + CardImpl(TerminalImpl terminal, String protocol) throws PCSCException { + this.terminal = terminal; + int sharingMode = SCARD_SHARE_SHARED; + int connectProtocol; + if (protocol.equals("*")) { + connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; + } else if (protocol.equalsIgnoreCase("T=0")) { + connectProtocol = SCARD_PROTOCOL_T0; + } else if (protocol.equalsIgnoreCase("T=1")) { + connectProtocol = SCARD_PROTOCOL_T1; + } else if (protocol.equalsIgnoreCase("direct")) { + // testing + + // MSDN states that the preferred protocol can be zero, but doesn't + // specify whether other values are allowed. + // pcsc-lite implementation expects the preferred protocol to be non zero. + connectProtocol = isWindows ? 0 : SCARD_PROTOCOL_RAW; + + sharingMode = SCARD_SHARE_DIRECT; + } else { + throw new IllegalArgumentException("Unsupported protocol " + protocol); + } + cardId = SCardConnect(terminal.contextId, terminal.name, + sharingMode, connectProtocol); + byte[] status = new byte[2]; + byte[] atrBytes = SCardStatus(cardId, status); + atr = new ATR(atrBytes); + this.protocol = status[1] & 0xff; + basicChannel = new ChannelImpl(this, 0); + state = State.OK; + } + + void checkState() { + State s = state; + if (s == State.DISCONNECTED) { + throw new IllegalStateException("Card has been disconnected"); + } else if (s == State.REMOVED) { + throw new IllegalStateException("Card has been removed"); + } + } + + boolean isValid() { + if (state != State.OK) { + return false; + } + // ping card via SCardStatus + try { + SCardStatus(cardId, new byte[2]); + return true; + } catch (PCSCException e) { + state = State.REMOVED; + return false; + } + } + + private void checkSecurity(String action) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new CardPermission(terminal.name, action)); + } + } + + void handleError(PCSCException e) { + if (e.code == SCARD_W_REMOVED_CARD) { + state = State.REMOVED; + } + } + + public ATR getATR() { + return atr; + } + + public String getProtocol() { + switch (protocol) { + case SCARD_PROTOCOL_T0: + return "T=0"; + case SCARD_PROTOCOL_T1: + return "T=1"; + default: + // should never occur + return "Unknown protocol " + protocol; + } + } + + public CardChannel getBasicChannel() { + checkSecurity("getBasicChannel"); + checkState(); + return basicChannel; + } + + private static int getSW(byte[] b) { + if (b.length < 2) { + return -1; + } + int sw1 = b[b.length - 2] & 0xff; + int sw2 = b[b.length - 1] & 0xff; + return (sw1 << 8) | sw2; + } + + private static byte[] commandOpenChannel = new byte[] {0, 0x70, 0, 0, 1}; + + public CardChannel openLogicalChannel() throws CardException { + checkSecurity("openLogicalChannel"); + checkState(); + checkExclusive(); + try { + byte[] response = SCardTransmit + (cardId, protocol, commandOpenChannel, 0, commandOpenChannel.length); + if ((response.length != 3) || (getSW(response) != 0x9000)) { + throw new CardException + ("openLogicalChannel() failed, card response: " + + PCSC.toString(response)); + } + return new ChannelImpl(this, response[0]); + } catch (PCSCException e) { + handleError(e); + throw new CardException("openLogicalChannel() failed", e); + } + } + + void checkExclusive() throws CardException { + Thread t = exclusiveThread; + if (t == null) { + return; + } + if (t != Thread.currentThread()) { + throw new CardException("Exclusive access established by another Thread"); + } + } + + public synchronized void beginExclusive() throws CardException { + checkSecurity("exclusive"); + checkState(); + if (exclusiveThread != null) { + throw new CardException + ("Exclusive access has already been assigned to Thread " + + exclusiveThread.getName()); + } + try { + SCardBeginTransaction(cardId); + } catch (PCSCException e) { + handleError(e); + throw new CardException("beginExclusive() failed", e); + } + exclusiveThread = Thread.currentThread(); + } + + public synchronized void endExclusive() throws CardException { + checkState(); + if (exclusiveThread != Thread.currentThread()) { + throw new IllegalStateException + ("Exclusive access not assigned to current Thread"); + } + try { + SCardEndTransaction(cardId, SCARD_LEAVE_CARD); + } catch (PCSCException e) { + handleError(e); + throw new CardException("endExclusive() failed", e); + } finally { + exclusiveThread = null; + } + } + + public byte[] transmitControlCommand(int controlCode, byte[] command) + throws CardException { + checkSecurity("transmitControl"); + checkState(); + checkExclusive(); + if (command == null) { + throw new NullPointerException(); + } + try { + byte[] r = SCardControl(cardId, controlCode, command); + return r; + } catch (PCSCException e) { + handleError(e); + throw new CardException("transmitControlCommand() failed", e); + } + } + + private static final boolean invertReset = + Boolean.parseBoolean( + AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "sun.security.smartcardio.invertCardReset", "false"))); + + public void disconnect(boolean reset) throws CardException { + if (reset) { + checkSecurity("reset"); + } + if (state != State.OK) { + return; + } + checkExclusive(); + // to preserve old behaviour, don't change flag until here + if (invertReset) { + reset = !reset; + } + try { + SCardDisconnect(cardId, (reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD)); + } catch (PCSCException e) { + throw new CardException("disconnect() failed", e); + } finally { + state = State.DISCONNECTED; + exclusiveThread = null; + } + } + + public String toString() { + return "PC/SC card in " + terminal.getName() + + ", protocol " + getProtocol() + ", state " + state; + } + + protected void finalize() throws Throwable { + try { + if (state == State.OK) { + SCardDisconnect(cardId, SCARD_LEAVE_CARD); + } + } finally { + super.finalize(); + } + } + +} diff --git a/src/sun/security/smartcardio/ChannelImpl.java b/src/sun/security/smartcardio/ChannelImpl.java new file mode 100644 index 00000000..b1ede0a0 --- /dev/null +++ b/src/sun/security/smartcardio/ChannelImpl.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import java.nio.*; +import java.security.AccessController; + +import javax.smartcardio.*; + +import static sun.security.smartcardio.PCSC.SCardTransmit; + +import sun.security.action.GetPropertyAction; + +/** + * CardChannel implementation. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ChannelImpl extends CardChannel { + + // the card this channel is associated with + private final CardImpl card; + + // the channel number, 0 for the basic logical channel + private final int channel; + + // whether this channel has been closed. only logical channels can be closed + private volatile boolean isClosed; + + ChannelImpl(CardImpl card, int channel) { + this.card = card; + this.channel = channel; + } + + void checkClosed() { + card.checkState(); + if (isClosed) { + throw new IllegalStateException("Logical channel has been closed"); + } + } + + public Card getCard() { + return card; + } + + public int getChannelNumber() { + checkClosed(); + return channel; + } + + private static void checkManageChannel(byte[] b) { + if (b.length < 4) { + throw new IllegalArgumentException + ("Command APDU must be at least 4 bytes long"); + } + if ((b[0] >= 0) && (b[1] == 0x70)) { + throw new IllegalArgumentException + ("Manage channel command not allowed, use openLogicalChannel()"); + } + } + + public ResponseAPDU transmit(CommandAPDU command) throws CardException { + checkClosed(); + card.checkExclusive(); + byte[] commandBytes = command.getBytes(); + byte[] responseBytes = doTransmit(commandBytes); + return new ResponseAPDU(responseBytes); + } + + public int transmit(ByteBuffer command, ByteBuffer response) throws CardException { + checkClosed(); + card.checkExclusive(); + if ((command == null) || (response == null)) { + throw new NullPointerException(); + } + if (response.isReadOnly()) { + throw new ReadOnlyBufferException(); + } + if (command == response) { + throw new IllegalArgumentException + ("command and response must not be the same object"); + } + if (response.remaining() < 258) { + throw new IllegalArgumentException + ("Insufficient space in response buffer"); + } + byte[] commandBytes = new byte[command.remaining()]; + command.get(commandBytes); + byte[] responseBytes = doTransmit(commandBytes); + response.put(responseBytes); + return responseBytes.length; + } + + private final static boolean t0GetResponse = + getBooleanProperty("sun.security.smartcardio.t0GetResponse", true); + + private final static boolean t1GetResponse = + getBooleanProperty("sun.security.smartcardio.t1GetResponse", true); + + private final static boolean t1StripLe = + getBooleanProperty("sun.security.smartcardio.t1StripLe", false); + + private static boolean getBooleanProperty(String name, boolean def) { + String val = AccessController.doPrivileged(new GetPropertyAction(name)); + if (val == null) { + return def; + } + if (val.equalsIgnoreCase("true")) { + return true; + } else if (val.equalsIgnoreCase("false")) { + return false; + } else { + throw new IllegalArgumentException + (name + " must be either 'true' or 'false'"); + } + } + + private byte[] concat(byte[] b1, byte[] b2, int n2) { + int n1 = b1.length; + if ((n1 == 0) && (n2 == b2.length)) { + return b2; + } + byte[] res = new byte[n1 + n2]; + System.arraycopy(b1, 0, res, 0, n1); + System.arraycopy(b2, 0, res, n1, n2); + return res; + } + + private final static byte[] B0 = new byte[0]; + + private byte[] doTransmit(byte[] command) throws CardException { + // note that we modify the 'command' array in some cases, so it must + // be a copy of the application provided data. + try { + checkManageChannel(command); + setChannel(command); + int n = command.length; + boolean t0 = card.protocol == SCARD_PROTOCOL_T0; + boolean t1 = card.protocol == SCARD_PROTOCOL_T1; + if (t0 && (n >= 7) && (command[4] == 0)) { + throw new CardException + ("Extended length forms not supported for T=0"); + } + if ((t0 || (t1 && t1StripLe)) && (n >= 7)) { + int lc = command[4] & 0xff; + if (lc != 0) { + if (n == lc + 6) { + n--; + } + } else { + lc = ((command[5] & 0xff) << 8) | (command[6] & 0xff); + if (n == lc + 9) { + n -= 2; + } + } + } + boolean getresponse = (t0 && t0GetResponse) || (t1 && t1GetResponse); + int k = 0; + byte[] result = B0; + while (true) { + if (++k >= 32) { + throw new CardException("Could not obtain response"); + } + byte[] response = SCardTransmit + (card.cardId, card.protocol, command, 0, n); + int rn = response.length; + if (getresponse && (rn >= 2)) { + // see ISO 7816/2005, 5.1.3 + if ((rn == 2) && (response[0] == 0x6c)) { + // Resend command using SW2 as short Le field + command[n - 1] = response[1]; + continue; + } + if (response[rn - 2] == 0x61) { + // Issue a GET RESPONSE command with the same CLA + // using SW2 as short Le field + if (rn > 2) { + result = concat(result, response, rn - 2); + } + command[1] = (byte)0xC0; + command[2] = 0; + command[3] = 0; + command[4] = response[rn - 1]; + n = 5; + continue; + } + + } + result = concat(result, response, rn); + break; + } + return result; + } catch (PCSCException e) { + card.handleError(e); + throw new CardException(e); + } + } + + private static int getSW(byte[] res) throws CardException { + if (res.length < 2) { + throw new CardException("Invalid response length: " + res.length); + } + int sw1 = res[res.length - 2] & 0xff; + int sw2 = res[res.length - 1] & 0xff; + return (sw1 << 8) | sw2; + } + + private static boolean isOK(byte[] res) throws CardException { + return (res.length == 2) && (getSW(res) == 0x9000); + } + + private void setChannel(byte[] com) { + int cla = com[0]; + if (cla < 0) { + // proprietary class format, cannot set or check logical channel + // for now, just return + return; + } + // classes 001x xxxx is reserved for future use in ISO, ignore + if ((cla & 0xe0) == 0x20) { + return; + } + // see ISO 7816/2005, table 2 and 3 + if (channel <= 3) { + // mask of bits 7, 1, 0 (channel number) + // 0xbc == 1011 1100 + com[0] &= 0xbc; + com[0] |= channel; + } else if (channel <= 19) { + // mask of bits 7, 3, 2, 1, 0 (channel number) + // 0xbc == 1011 0000 + com[0] &= 0xb0; + com[0] |= 0x40; + com[0] |= (channel - 4); + } else { + throw new RuntimeException("Unsupported channel number: " + channel); + } + } + + public void close() throws CardException { + if (getChannelNumber() == 0) { + throw new IllegalStateException("Cannot close basic logical channel"); + } + if (isClosed) { + return; + } + card.checkExclusive(); + try { + byte[] com = new byte[] {0x00, 0x70, (byte)0x80, 0}; + com[3] = (byte)getChannelNumber(); + setChannel(com); + byte[] res = SCardTransmit(card.cardId, card.protocol, com, 0, com.length); + if (isOK(res) == false) { + throw new CardException("close() failed: " + PCSC.toString(res)); + } + } catch (PCSCException e) { + card.handleError(e); + throw new CardException("Could not close channel", e); + } finally { + isClosed = true; + } + } + + public String toString() { + return "PC/SC channel " + channel; + } + +} diff --git a/src/sun/security/smartcardio/PCSC.java b/src/sun/security/smartcardio/PCSC.java new file mode 100644 index 00000000..4fea4aff --- /dev/null +++ b/src/sun/security/smartcardio/PCSC.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +/** + * Access to native PC/SC functions and definition of PC/SC constants. + * Initialization and platform specific PC/SC constants are handled in + * the platform specific superclass. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSC extends PlatformPCSC { + + private PCSC() { + // no instantiation + } + + static void checkAvailable() throws RuntimeException { + if (initException != null) { + throw new UnsupportedOperationException + ("PC/SC not available on this platform", initException); + } + } + + // returns SCARDCONTEXT (contextId) + static native long SCardEstablishContext + (int scope) + throws PCSCException; + + static native String[] SCardListReaders + (long contextId) + throws PCSCException; + + // returns SCARDHANDLE (cardId) + static native long SCardConnect + (long contextId, String readerName, int shareMode, int preferredProtocols) + throws PCSCException; + + static native byte[] SCardTransmit + (long cardId, int protocol, byte[] buf, int ofs, int len) + throws PCSCException; + + // returns the ATR of the card, updates status[] with reader state and protocol + static native byte[] SCardStatus + (long cardId, byte[] status) + throws PCSCException; + + static native void SCardDisconnect + (long cardId, int disposition) + throws PCSCException; + + // returns dwEventState[] of the same size and order as readerNames[] + static native int[] SCardGetStatusChange + (long contextId, long timeout, int[] currentState, String[] readerNames) + throws PCSCException; + + static native void SCardBeginTransaction + (long cardId) + throws PCSCException; + + static native void SCardEndTransaction + (long cardId, int disposition) + throws PCSCException; + + static native byte[] SCardControl + (long cardId, int controlCode, byte[] sendBuffer) + throws PCSCException; + + // PCSC success/error/failure/warning codes + final static int SCARD_S_SUCCESS = 0x00000000; + final static int SCARD_E_CANCELLED = 0x80100002; + final static int SCARD_E_CANT_DISPOSE = 0x8010000E; + final static int SCARD_E_INSUFFICIENT_BUFFER = 0x80100008; + final static int SCARD_E_INVALID_ATR = 0x80100015; + final static int SCARD_E_INVALID_HANDLE = 0x80100003; + final static int SCARD_E_INVALID_PARAMETER = 0x80100004; + final static int SCARD_E_INVALID_TARGET = 0x80100005; + final static int SCARD_E_INVALID_VALUE = 0x80100011; + final static int SCARD_E_NO_MEMORY = 0x80100006; + final static int SCARD_F_COMM_ERROR = 0x80100013; + final static int SCARD_F_INTERNAL_ERROR = 0x80100001; + final static int SCARD_F_UNKNOWN_ERROR = 0x80100014; + final static int SCARD_F_WAITED_TOO_LONG = 0x80100007; + final static int SCARD_E_UNKNOWN_READER = 0x80100009; + final static int SCARD_E_TIMEOUT = 0x8010000A; + final static int SCARD_E_SHARING_VIOLATION = 0x8010000B; + final static int SCARD_E_NO_SMARTCARD = 0x8010000C; + final static int SCARD_E_UNKNOWN_CARD = 0x8010000D; + final static int SCARD_E_PROTO_MISMATCH = 0x8010000F; + final static int SCARD_E_NOT_READY = 0x80100010; + final static int SCARD_E_SYSTEM_CANCELLED = 0x80100012; + final static int SCARD_E_NOT_TRANSACTED = 0x80100016; + final static int SCARD_E_READER_UNAVAILABLE = 0x80100017; + + final static int SCARD_W_UNSUPPORTED_CARD = 0x80100065; + final static int SCARD_W_UNRESPONSIVE_CARD = 0x80100066; + final static int SCARD_W_UNPOWERED_CARD = 0x80100067; + final static int SCARD_W_RESET_CARD = 0x80100068; + final static int SCARD_W_REMOVED_CARD = 0x80100069; + final static int SCARD_W_INSERTED_CARD = 0x8010006A; + + final static int SCARD_E_UNSUPPORTED_FEATURE = 0x8010001F; + final static int SCARD_E_PCI_TOO_SMALL = 0x80100019; + final static int SCARD_E_READER_UNSUPPORTED = 0x8010001A; + final static int SCARD_E_DUPLICATE_READER = 0x8010001B; + final static int SCARD_E_CARD_UNSUPPORTED = 0x8010001C; + final static int SCARD_E_NO_SERVICE = 0x8010001D; + final static int SCARD_E_SERVICE_STOPPED = 0x8010001E; + + // MS undocumented + final static int SCARD_E_NO_READERS_AVAILABLE = 0x8010002E; + // std. Windows invalid handle return code, used instead of SCARD code + final static int WINDOWS_ERROR_INVALID_HANDLE = 6; + final static int WINDOWS_ERROR_INVALID_PARAMETER = 87; + + // + final static int SCARD_SCOPE_USER = 0x0000; + final static int SCARD_SCOPE_TERMINAL = 0x0001; + final static int SCARD_SCOPE_SYSTEM = 0x0002; + final static int SCARD_SCOPE_GLOBAL = 0x0003; + + final static int SCARD_SHARE_EXCLUSIVE = 0x0001; + final static int SCARD_SHARE_SHARED = 0x0002; + final static int SCARD_SHARE_DIRECT = 0x0003; + + final static int SCARD_LEAVE_CARD = 0x0000; + final static int SCARD_RESET_CARD = 0x0001; + final static int SCARD_UNPOWER_CARD = 0x0002; + final static int SCARD_EJECT_CARD = 0x0003; + + final static int SCARD_STATE_UNAWARE = 0x0000; + final static int SCARD_STATE_IGNORE = 0x0001; + final static int SCARD_STATE_CHANGED = 0x0002; + final static int SCARD_STATE_UNKNOWN = 0x0004; + final static int SCARD_STATE_UNAVAILABLE = 0x0008; + final static int SCARD_STATE_EMPTY = 0x0010; + final static int SCARD_STATE_PRESENT = 0x0020; + final static int SCARD_STATE_ATRMATCH = 0x0040; + final static int SCARD_STATE_EXCLUSIVE = 0x0080; + final static int SCARD_STATE_INUSE = 0x0100; + final static int SCARD_STATE_MUTE = 0x0200; + final static int SCARD_STATE_UNPOWERED = 0x0400; + + final static int TIMEOUT_INFINITE = 0xffffffff; + + private final static char[] hexDigits = "0123456789abcdef".toCharArray(); + + public static String toString(byte[] b) { + StringBuffer sb = new StringBuffer(b.length * 3); + for (int i = 0; i < b.length; i++) { + int k = b[i] & 0xff; + if (i != 0) { + sb.append(':'); + } + sb.append(hexDigits[k >>> 4]); + sb.append(hexDigits[k & 0xf]); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/smartcardio/PCSCException.java b/src/sun/security/smartcardio/PCSCException.java new file mode 100644 index 00000000..caef6452 --- /dev/null +++ b/src/sun/security/smartcardio/PCSCException.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import static sun.security.smartcardio.PCSC.SCARD_E_CANCELLED; +import static sun.security.smartcardio.PCSC.SCARD_E_CANT_DISPOSE; +import static sun.security.smartcardio.PCSC.SCARD_E_CARD_UNSUPPORTED; +import static sun.security.smartcardio.PCSC.SCARD_E_DUPLICATE_READER; +import static sun.security.smartcardio.PCSC.SCARD_E_INSUFFICIENT_BUFFER; +import static sun.security.smartcardio.PCSC.SCARD_E_INVALID_ATR; +import static sun.security.smartcardio.PCSC.SCARD_E_INVALID_HANDLE; +import static sun.security.smartcardio.PCSC.SCARD_E_INVALID_PARAMETER; +import static sun.security.smartcardio.PCSC.SCARD_E_INVALID_TARGET; +import static sun.security.smartcardio.PCSC.SCARD_E_INVALID_VALUE; +import static sun.security.smartcardio.PCSC.SCARD_E_NOT_READY; +import static sun.security.smartcardio.PCSC.SCARD_E_NOT_TRANSACTED; +import static sun.security.smartcardio.PCSC.SCARD_E_NO_MEMORY; +import static sun.security.smartcardio.PCSC.SCARD_E_NO_READERS_AVAILABLE; +import static sun.security.smartcardio.PCSC.SCARD_E_NO_SERVICE; +import static sun.security.smartcardio.PCSC.SCARD_E_NO_SMARTCARD; +import static sun.security.smartcardio.PCSC.SCARD_E_PCI_TOO_SMALL; +import static sun.security.smartcardio.PCSC.SCARD_E_PROTO_MISMATCH; +import static sun.security.smartcardio.PCSC.SCARD_E_READER_UNAVAILABLE; +import static sun.security.smartcardio.PCSC.SCARD_E_READER_UNSUPPORTED; +import static sun.security.smartcardio.PCSC.SCARD_E_SERVICE_STOPPED; +import static sun.security.smartcardio.PCSC.SCARD_E_SHARING_VIOLATION; +import static sun.security.smartcardio.PCSC.SCARD_E_SYSTEM_CANCELLED; +import static sun.security.smartcardio.PCSC.SCARD_E_TIMEOUT; +import static sun.security.smartcardio.PCSC.SCARD_E_UNKNOWN_CARD; +import static sun.security.smartcardio.PCSC.SCARD_E_UNKNOWN_READER; +import static sun.security.smartcardio.PCSC.SCARD_E_UNSUPPORTED_FEATURE; +import static sun.security.smartcardio.PCSC.SCARD_F_COMM_ERROR; +import static sun.security.smartcardio.PCSC.SCARD_F_INTERNAL_ERROR; +import static sun.security.smartcardio.PCSC.SCARD_F_UNKNOWN_ERROR; +import static sun.security.smartcardio.PCSC.SCARD_F_WAITED_TOO_LONG; +import static sun.security.smartcardio.PCSC.SCARD_S_SUCCESS; +import static sun.security.smartcardio.PCSC.SCARD_W_INSERTED_CARD; +import static sun.security.smartcardio.PCSC.SCARD_W_REMOVED_CARD; +import static sun.security.smartcardio.PCSC.SCARD_W_RESET_CARD; +import static sun.security.smartcardio.PCSC.SCARD_W_UNPOWERED_CARD; +import static sun.security.smartcardio.PCSC.SCARD_W_UNRESPONSIVE_CARD; +import static sun.security.smartcardio.PCSC.SCARD_W_UNSUPPORTED_CARD; +import static sun.security.smartcardio.PCSC.WINDOWS_ERROR_INVALID_HANDLE; +import static sun.security.smartcardio.PCSC.WINDOWS_ERROR_INVALID_PARAMETER; + +/** + * Exception for PC/SC errors. The native code portion checks the return value + * of the SCard* functions. If it indicates an error, the native code constructs + * an instance of this exception, throws it, and returns to Java. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSCException extends Exception { + + private static final long serialVersionUID = 4181137171979130432L; + + final int code; + + PCSCException(int code) { + super(toErrorString(code)); + this.code = code; + } + + private static String toErrorString(int code) { + switch (code) { + case SCARD_S_SUCCESS : return "SCARD_S_SUCCESS"; + case SCARD_E_CANCELLED : return "SCARD_E_CANCELLED"; + case SCARD_E_CANT_DISPOSE : return "SCARD_E_CANT_DISPOSE"; + case SCARD_E_INSUFFICIENT_BUFFER : return "SCARD_E_INSUFFICIENT_BUFFER"; + case SCARD_E_INVALID_ATR : return "SCARD_E_INVALID_ATR"; + case SCARD_E_INVALID_HANDLE : return "SCARD_E_INVALID_HANDLE"; + case SCARD_E_INVALID_PARAMETER : return "SCARD_E_INVALID_PARAMETER"; + case SCARD_E_INVALID_TARGET : return "SCARD_E_INVALID_TARGET"; + case SCARD_E_INVALID_VALUE : return "SCARD_E_INVALID_VALUE"; + case SCARD_E_NO_MEMORY : return "SCARD_E_NO_MEMORY"; + case SCARD_F_COMM_ERROR : return "SCARD_F_COMM_ERROR"; + case SCARD_F_INTERNAL_ERROR : return "SCARD_F_INTERNAL_ERROR"; + case SCARD_F_UNKNOWN_ERROR : return "SCARD_F_UNKNOWN_ERROR"; + case SCARD_F_WAITED_TOO_LONG : return "SCARD_F_WAITED_TOO_LONG"; + case SCARD_E_UNKNOWN_READER : return "SCARD_E_UNKNOWN_READER"; + case SCARD_E_TIMEOUT : return "SCARD_E_TIMEOUT"; + case SCARD_E_SHARING_VIOLATION : return "SCARD_E_SHARING_VIOLATION"; + case SCARD_E_NO_SMARTCARD : return "SCARD_E_NO_SMARTCARD"; + case SCARD_E_UNKNOWN_CARD : return "SCARD_E_UNKNOWN_CARD"; + case SCARD_E_PROTO_MISMATCH : return "SCARD_E_PROTO_MISMATCH"; + case SCARD_E_NOT_READY : return "SCARD_E_NOT_READY"; + case SCARD_E_SYSTEM_CANCELLED : return "SCARD_E_SYSTEM_CANCELLED"; + case SCARD_E_NOT_TRANSACTED : return "SCARD_E_NOT_TRANSACTED"; + case SCARD_E_READER_UNAVAILABLE : return "SCARD_E_READER_UNAVAILABLE"; + + case SCARD_W_UNSUPPORTED_CARD : return "SCARD_W_UNSUPPORTED_CARD"; + case SCARD_W_UNRESPONSIVE_CARD : return "SCARD_W_UNRESPONSIVE_CARD"; + case SCARD_W_UNPOWERED_CARD : return "SCARD_W_UNPOWERED_CARD"; + case SCARD_W_RESET_CARD : return "SCARD_W_RESET_CARD"; + case SCARD_W_REMOVED_CARD : return "SCARD_W_REMOVED_CARD"; + case SCARD_W_INSERTED_CARD : return "SCARD_W_INSERTED_CARD"; + + case SCARD_E_UNSUPPORTED_FEATURE : return "SCARD_E_UNSUPPORTED_FEATURE"; + case SCARD_E_PCI_TOO_SMALL : return "SCARD_E_PCI_TOO_SMALL"; + case SCARD_E_READER_UNSUPPORTED : return "SCARD_E_READER_UNSUPPORTED"; + case SCARD_E_DUPLICATE_READER : return "SCARD_E_DUPLICATE_READER"; + case SCARD_E_CARD_UNSUPPORTED : return "SCARD_E_CARD_UNSUPPORTED"; + case SCARD_E_NO_SERVICE : return "SCARD_E_NO_SERVICE"; + case SCARD_E_SERVICE_STOPPED : return "SCARD_E_SERVICE_STOPPED"; + + case SCARD_E_NO_READERS_AVAILABLE: return "SCARD_E_NO_READERS_AVAILABLE"; + case WINDOWS_ERROR_INVALID_HANDLE: return "WINDOWS_ERROR_INVALID_HANDLE"; + case WINDOWS_ERROR_INVALID_PARAMETER: return "WINDOWS_ERROR_INVALID_PARAMETER"; + + default: return "Unknown error 0x" + Integer.toHexString(code); + } + } +} diff --git a/src/sun/security/smartcardio/PCSCTerminals.java b/src/sun/security/smartcardio/PCSCTerminals.java new file mode 100644 index 00000000..8e699f9d --- /dev/null +++ b/src/sun/security/smartcardio/PCSCTerminals.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import java.util.*; +import java.lang.ref.*; + +import javax.smartcardio.*; +import static javax.smartcardio.CardTerminals.State.*; + +import static javax.smartcardio.CardTerminals.State.CARD_ABSENT; +import static javax.smartcardio.CardTerminals.State.CARD_INSERTION; +import static javax.smartcardio.CardTerminals.State.CARD_PRESENT; +import static javax.smartcardio.CardTerminals.State.CARD_REMOVAL; +import static sun.security.smartcardio.PCSC.*; + +/** + * TerminalFactorySpi implementation class. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSCTerminals extends CardTerminals { + + // SCARDCONTEXT, currently shared between all threads/terminals + private static long contextId; + + // terminal state used by waitForCard() + private Map stateMap; + + PCSCTerminals() { + // empty + } + + static synchronized void initContext() throws PCSCException { + if (contextId == 0) { + contextId = SCardEstablishContext(SCARD_SCOPE_USER); + } + } + + private static final Map> terminals + = new HashMap>(); + + private static synchronized TerminalImpl implGetTerminal(String name) { + Reference ref = terminals.get(name); + TerminalImpl terminal = (ref != null) ? ref.get() : null; + if (terminal != null) { + return terminal; + } + terminal = new TerminalImpl(contextId, name); + terminals.put(name, new WeakReference(terminal)); + return terminal; + + } + + public synchronized List list(State state) throws CardException { + if (state == null) { + throw new NullPointerException(); + } + try { + String[] readerNames = SCardListReaders(contextId); + List list = new ArrayList(readerNames.length); + if (stateMap == null) { + // If waitForChange() has never been called, treat event + // queries as status queries. + if (state == CARD_INSERTION) { + state = CARD_PRESENT; + } else if (state == CARD_REMOVAL) { + state = CARD_ABSENT; + } + } + for (String readerName : readerNames) { + CardTerminal terminal = implGetTerminal(readerName); + ReaderState readerState; + switch (state) { + case ALL: + list.add(terminal); + break; + case CARD_PRESENT: + if (terminal.isCardPresent()) { + list.add(terminal); + } + break; + case CARD_ABSENT: + if (terminal.isCardPresent() == false) { + list.add(terminal); + } + break; + case CARD_INSERTION: + readerState = stateMap.get(readerName); + if ((readerState != null) && readerState.isInsertion()) { + list.add(terminal); + } + break; + case CARD_REMOVAL: + readerState = stateMap.get(readerName); + if ((readerState != null) && readerState.isRemoval()) { + list.add(terminal); + } + break; + default: + throw new CardException("Unknown state: " + state); + } + } + return Collections.unmodifiableList(list); + } catch (PCSCException e) { + throw new CardException("list() failed", e); + } + } + + private static class ReaderState { + private int current, previous; + ReaderState() { + current = SCARD_STATE_UNAWARE; + previous = SCARD_STATE_UNAWARE; + } + int get() { + return current; + } + void update(int newState) { + previous = current; + current = newState; + } + boolean isInsertion() { + return !present(previous) && present(current); + } + boolean isRemoval() { + return present(previous) && !present(current); + } + static boolean present(int state) { + return (state & SCARD_STATE_PRESENT) != 0; + } + } + + public synchronized boolean waitForChange(long timeout) throws CardException { + if (timeout < 0) { + throw new IllegalArgumentException + ("Timeout must not be negative: " + timeout); + } + if (stateMap == null) { + // We need to initialize the state database. + // Do that with a recursive call, which will return immediately + // because we pass SCARD_STATE_UNAWARE. + // After that, proceed with the real call. + stateMap = new HashMap(); + waitForChange(0); + } + if (timeout == 0) { + timeout = TIMEOUT_INFINITE; + } + try { + String[] readerNames = SCardListReaders(contextId); + int n = readerNames.length; + if (n == 0) { + throw new IllegalStateException("No terminals available"); + } + int[] status = new int[n]; + ReaderState[] readerStates = new ReaderState[n]; + for (int i = 0; i < readerNames.length; i++) { + String name = readerNames[i]; + ReaderState state = stateMap.get(name); + if (state == null) { + state = new ReaderState(); + } + readerStates[i] = state; + status[i] = state.get(); + } + status = SCardGetStatusChange(contextId, timeout, status, readerNames); + stateMap.clear(); // remove any readers that are no longer available + for (int i = 0; i < n; i++) { + ReaderState state = readerStates[i]; + state.update(status[i]); + stateMap.put(readerNames[i], state); + } + return true; + } catch (PCSCException e) { + if (e.code == SCARD_E_TIMEOUT) { + return false; + } else { + throw new CardException("waitForChange() failed", e); + } + } + } + + static List waitForCards(List terminals, + long timeout, boolean wantPresent) throws CardException { + // the argument sanity checks are performed in + // javax.smartcardio.TerminalFactory or TerminalImpl + + long thisTimeout; + if (timeout == 0) { + timeout = TIMEOUT_INFINITE; + thisTimeout = TIMEOUT_INFINITE; + } else { + // if timeout is not infinite, do the initial call that retrieves + // the status with a 0 timeout. Otherwise, we might get incorrect + // timeout exceptions (seen on Solaris with PC/SC shim) + thisTimeout = 0; + } + + String[] names = new String[terminals.size()]; + int i = 0; + for (CardTerminal terminal : terminals) { + if (terminal instanceof TerminalImpl == false) { + throw new IllegalArgumentException + ("Invalid terminal type: " + terminal.getClass().getName()); + } + TerminalImpl impl = (TerminalImpl)terminal; + names[i++] = impl.name; + } + + int[] status = new int[names.length]; + Arrays.fill(status, SCARD_STATE_UNAWARE); + + try { + while (true) { + // note that we pass "timeout" on each native PC/SC call + // that means that if we end up making multiple (more than 2) + // calls, we might wait too long. + // for now assume that is unlikely and not a problem. + status = SCardGetStatusChange(contextId, thisTimeout, status, names); + thisTimeout = timeout; + + List results = null; + for (i = 0; i < names.length; i++) { + boolean nowPresent = (status[i] & SCARD_STATE_PRESENT) != 0; + if (nowPresent == wantPresent) { + if (results == null) { + results = new ArrayList(); + } + results.add(implGetTerminal(names[i])); + } + } + + if (results != null) { + return Collections.unmodifiableList(results); + } + } + } catch (PCSCException e) { + if (e.code == SCARD_E_TIMEOUT) { + return Collections.emptyList(); + } else { + throw new CardException("waitForCard() failed", e); + } + } + } + +} diff --git a/src/sun/security/smartcardio/SunPCSC.java b/src/sun/security/smartcardio/SunPCSC.java new file mode 100644 index 00000000..c0cb8568 --- /dev/null +++ b/src/sun/security/smartcardio/SunPCSC.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import java.security.*; + +import javax.smartcardio.*; + +/** + * Provider object for PC/SC. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +public final class SunPCSC extends Provider { + + private static final long serialVersionUID = 6168388284028876579L; + + public SunPCSC() { + super("SunPCSC", 1.8d, "Sun PC/SC provider"); + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + put("TerminalFactory.PC/SC", "sun.security.smartcardio.SunPCSC$Factory"); + return null; + } + }); + } + + public static final class Factory extends TerminalFactorySpi { + public Factory(Object obj) throws PCSCException { + if (obj != null) { + throw new IllegalArgumentException + ("SunPCSC factory does not use parameters"); + } + // make sure PCSC is available and that we can obtain a context + PCSC.checkAvailable(); + PCSCTerminals.initContext(); + } + /** + * Returns the available readers. + * This must be a new object for each call. + */ + protected CardTerminals engineTerminals() { + return new PCSCTerminals(); + } + } + +} diff --git a/src/sun/security/smartcardio/TerminalImpl.java b/src/sun/security/smartcardio/TerminalImpl.java new file mode 100644 index 00000000..49465264 --- /dev/null +++ b/src/sun/security/smartcardio/TerminalImpl.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.smartcardio; + +import javax.smartcardio.*; + +import static sun.security.smartcardio.PCSC.*; + +/** + * CardTerminal implementation. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class TerminalImpl extends CardTerminal { + + // native SCARDCONTEXT + final long contextId; + + // the name of this terminal (native PC/SC name) + final String name; + + private CardImpl card; + + TerminalImpl(long contextId, String name) { + this.contextId = contextId; + this.name = name; + } + + public String getName() { + return name; + } + + public synchronized Card connect(String protocol) throws CardException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new CardPermission(name, "connect")); + } + if (card != null) { + if (card.isValid()) { + String cardProto = card.getProtocol(); + if (protocol.equals("*") || protocol.equalsIgnoreCase(cardProto)) { + return card; + } else { + throw new CardException("Cannot connect using " + protocol + + ", connection already established using " + cardProto); + } + } else { + card = null; + } + } + try { + card = new CardImpl(this, protocol); + return card; + } catch (PCSCException e) { + if (e.code == SCARD_W_REMOVED_CARD) { + throw new CardNotPresentException("No card present", e); + } else { + throw new CardException("connect() failed", e); + } + } + } + + public boolean isCardPresent() throws CardException { + try { + int[] status = SCardGetStatusChange(contextId, 0, + new int[] {SCARD_STATE_UNAWARE}, new String[] {name}); + return (status[0] & SCARD_STATE_PRESENT) != 0; + } catch (PCSCException e) { + throw new CardException("isCardPresent() failed", e); + } + } + + private boolean waitForCard(boolean wantPresent, long timeout) throws CardException { + if (timeout < 0) { + throw new IllegalArgumentException("timeout must not be negative"); + } + if (timeout == 0) { + timeout = TIMEOUT_INFINITE; + } + int[] status = new int[] {SCARD_STATE_UNAWARE}; + String[] readers = new String[] {name}; + try { + // check if card status already matches + status = SCardGetStatusChange(contextId, 0, status, readers); + boolean present = (status[0] & SCARD_STATE_PRESENT) != 0; + if (wantPresent == present) { + return true; + } + // no match, wait (until timeout expires) + long end = System.currentTimeMillis() + timeout; + while (wantPresent != present && timeout != 0) { + // set remaining timeout + if (timeout != TIMEOUT_INFINITE) { + timeout = Math.max(end - System.currentTimeMillis(), 0l); + } + status = SCardGetStatusChange(contextId, timeout, status, readers); + present = (status[0] & SCARD_STATE_PRESENT) != 0; + } + return wantPresent == present; + } catch (PCSCException e) { + if (e.code == SCARD_E_TIMEOUT) { + return false; + } else { + throw new CardException("waitForCard() failed", e); + } + } + } + + public boolean waitForCardPresent(long timeout) throws CardException { + return waitForCard(true, timeout); + } + + public boolean waitForCardAbsent(long timeout) throws CardException { + return waitForCard(false, timeout); + } + + public String toString() { + return "PC/SC terminal " + name; + } +} diff --git a/src/sun/security/ssl/ALPNExtension.java b/src/sun/security/ssl/ALPNExtension.java new file mode 100644 index 00000000..4a475e4d --- /dev/null +++ b/src/sun/security/ssl/ALPNExtension.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.charset.*; +import java.util.*; + +import javax.net.ssl.*; + +/* + * [RFC 7301] + * This TLS extension facilitates the negotiation of application-layer protocols + * within the TLS handshake. Clients MAY include an extension of type + * "application_layer_protocol_negotiation" in the (extended) ClientHello + * message. The "extension_data" field of this extension SHALL contain a + * "ProtocolNameList" value: + * + * enum { + * application_layer_protocol_negotiation(16), (65535) + * } ExtensionType; + * + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ +final class ALPNExtension extends HelloExtension { + + final static int ALPN_HEADER_LENGTH = 1; + final static int MAX_APPLICATION_PROTOCOL_LENGTH = 255; + final static int MAX_APPLICATION_PROTOCOL_LIST_LENGTH = 65535; + private int listLength = 0; // ProtocolNameList length + private List protocolNames = null; + + // constructor for ServerHello + ALPNExtension(String protocolName) throws SSLException { + this(new String[]{ protocolName }); + } + + // constructor for ClientHello + ALPNExtension(String[] protocolNames) throws SSLException { + super(ExtensionType.EXT_ALPN); + if (protocolNames.length == 0) { // never null, never empty + throw new IllegalArgumentException( + "The list of application protocols cannot be empty"); + } + this.protocolNames = Arrays.asList(protocolNames); + for (String p : protocolNames) { + int length = p.getBytes(StandardCharsets.UTF_8).length; + if (length == 0) { + throw new SSLProtocolException( + "Application protocol name is empty"); + } + if (length <= MAX_APPLICATION_PROTOCOL_LENGTH) { + listLength += length + ALPN_HEADER_LENGTH; + } else { + throw new SSLProtocolException( + "Application protocol name is too long: " + p); + } + if (listLength > MAX_APPLICATION_PROTOCOL_LIST_LENGTH) { + throw new SSLProtocolException( + "Application protocol name list is too long"); + } + } + } + + // constructor for ServerHello for parsing ALPN extension + ALPNExtension(HandshakeInStream s, int len) throws IOException { + super(ExtensionType.EXT_ALPN); + + if (len >= 2) { + listLength = s.getInt16(); // list length + if (listLength < 2 || listLength + 2 != len) { + throw new SSLProtocolException( + "Invalid " + type + " extension: incorrect list length " + + "(length=" + listLength + ")"); + } + } else { + throw new SSLProtocolException( + "Invalid " + type + " extension: insufficient data " + + "(length=" + len + ")"); + } + + int remaining = listLength; + this.protocolNames = new ArrayList<>(); + while (remaining > 0) { + // opaque ProtocolName<1..2^8-1>; // RFC 7301 + byte[] bytes = s.getBytes8(); + if (bytes.length == 0) { + throw new SSLProtocolException("Invalid " + type + + " extension: empty application protocol name"); + } + String p = + new String(bytes, StandardCharsets.UTF_8); // app protocol + protocolNames.add(p); + remaining -= bytes.length + ALPN_HEADER_LENGTH; + } + + if (remaining != 0) { + throw new SSLProtocolException( + "Invalid " + type + " extension: extra data " + + "(length=" + remaining + ")"); + } + } + + List getPeerAPs() { + return protocolNames; + } + + /* + * Return the length in bytes, including extension type and length fields. + */ + @Override + int length() { + return 6 + listLength; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(listLength + 2); // length of extension_data + s.putInt16(listLength); // length of ProtocolNameList + + for (String p : protocolNames) { + s.putBytes8(p.getBytes(StandardCharsets.UTF_8)); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (protocolNames == null || protocolNames.isEmpty()) { + sb.append(""); + } else { + for (String protocolName : protocolNames) { + sb.append("[" + protocolName + "]"); + } + } + + return "Extension " + type + + ", protocol names: " + sb; + } +} diff --git a/src/sun/security/ssl/Alerts.java b/src/sun/security/ssl/Alerts.java new file mode 100644 index 00000000..1076d674 --- /dev/null +++ b/src/sun/security/ssl/Alerts.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import javax.net.ssl.*; + +/* + * A simple class to congregate alerts, their definitions, and common + * support methods. + */ + +final class Alerts { + + /* + * Alerts are always a fixed two byte format (level/description). + */ + + // warnings and fatal errors are package private facilities/constants + + // Alert levels (enum AlertLevel) + static final byte alert_warning = 1; + static final byte alert_fatal = 2; + + /* + * Alert descriptions (enum AlertDescription) + * + * We may not use them all in our processing, but if someone + * sends us one, we can at least convert it to a string for the + * user. + */ + static final byte alert_close_notify = 0; + static final byte alert_unexpected_message = 10; + static final byte alert_bad_record_mac = 20; + static final byte alert_decryption_failed = 21; + static final byte alert_record_overflow = 22; + static final byte alert_decompression_failure = 30; + static final byte alert_handshake_failure = 40; + static final byte alert_no_certificate = 41; + static final byte alert_bad_certificate = 42; + static final byte alert_unsupported_certificate = 43; + static final byte alert_certificate_revoked = 44; + static final byte alert_certificate_expired = 45; + static final byte alert_certificate_unknown = 46; + static final byte alert_illegal_parameter = 47; + static final byte alert_unknown_ca = 48; + static final byte alert_access_denied = 49; + static final byte alert_decode_error = 50; + static final byte alert_decrypt_error = 51; + static final byte alert_export_restriction = 60; + static final byte alert_protocol_version = 70; + static final byte alert_insufficient_security = 71; + static final byte alert_internal_error = 80; + static final byte alert_user_canceled = 90; + static final byte alert_no_renegotiation = 100; + + // from RFC 3546 (TLS Extensions) + static final byte alert_unsupported_extension = 110; + static final byte alert_certificate_unobtainable = 111; + static final byte alert_unrecognized_name = 112; + static final byte alert_bad_certificate_status_response = 113; + static final byte alert_bad_certificate_hash_value = 114; + + // from RFC 7301 (TLS ALPN Extension) + static final byte alert_no_application_protocol = 120; + + static String alertDescription(byte code) { + switch (code) { + + case alert_close_notify: + return "close_notify"; + case alert_unexpected_message: + return "unexpected_message"; + case alert_bad_record_mac: + return "bad_record_mac"; + case alert_decryption_failed: + return "decryption_failed"; + case alert_record_overflow: + return "record_overflow"; + case alert_decompression_failure: + return "decompression_failure"; + case alert_handshake_failure: + return "handshake_failure"; + case alert_no_certificate: + return "no_certificate"; + case alert_bad_certificate: + return "bad_certificate"; + case alert_unsupported_certificate: + return "unsupported_certificate"; + case alert_certificate_revoked: + return "certificate_revoked"; + case alert_certificate_expired: + return "certificate_expired"; + case alert_certificate_unknown: + return "certificate_unknown"; + case alert_illegal_parameter: + return "illegal_parameter"; + case alert_unknown_ca: + return "unknown_ca"; + case alert_access_denied: + return "access_denied"; + case alert_decode_error: + return "decode_error"; + case alert_decrypt_error: + return "decrypt_error"; + case alert_export_restriction: + return "export_restriction"; + case alert_protocol_version: + return "protocol_version"; + case alert_insufficient_security: + return "insufficient_security"; + case alert_internal_error: + return "internal_error"; + case alert_user_canceled: + return "user_canceled"; + case alert_no_renegotiation: + return "no_renegotiation"; + case alert_unsupported_extension: + return "unsupported_extension"; + case alert_certificate_unobtainable: + return "certificate_unobtainable"; + case alert_unrecognized_name: + return "unrecognized_name"; + case alert_bad_certificate_status_response: + return "bad_certificate_status_response"; + case alert_bad_certificate_hash_value: + return "bad_certificate_hash_value"; + case alert_no_application_protocol: + return "no_application_protocol"; + + default: + return ""; + } + } + + static SSLException getSSLException(byte description, String reason) { + return getSSLException(description, null, reason); + } + + /* + * Try to be a little more specific in our choice of + * exceptions to throw. + */ + static SSLException getSSLException(byte description, Throwable cause, + String reason) { + + SSLException e; + // the SSLException classes do not have a no-args constructor + // make up a message if there is none + if (reason == null) { + if (cause != null) { + reason = cause.toString(); + } else { + reason = ""; + } + } + switch (description) { + case alert_handshake_failure: + case alert_no_certificate: + case alert_bad_certificate: + case alert_unsupported_certificate: + case alert_certificate_revoked: + case alert_certificate_expired: + case alert_certificate_unknown: + case alert_unknown_ca: + case alert_access_denied: + case alert_decrypt_error: + case alert_export_restriction: + case alert_insufficient_security: + case alert_unsupported_extension: + case alert_certificate_unobtainable: + case alert_unrecognized_name: + case alert_bad_certificate_status_response: + case alert_bad_certificate_hash_value: + case alert_no_application_protocol: + e = new SSLHandshakeException(reason); + break; + + case alert_close_notify: + case alert_unexpected_message: + case alert_bad_record_mac: + case alert_decryption_failed: + case alert_record_overflow: + case alert_decompression_failure: + case alert_illegal_parameter: + case alert_decode_error: + case alert_protocol_version: + case alert_internal_error: + case alert_user_canceled: + case alert_no_renegotiation: + default: + e = new SSLException(reason); + break; + } + + if (cause != null) { + e.initCause(cause); + } + return e; + } +} diff --git a/src/sun/security/ssl/AppInputStream.java b/src/sun/security/ssl/AppInputStream.java new file mode 100644 index 00000000..b9fb8fb0 --- /dev/null +++ b/src/sun/security/ssl/AppInputStream.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; + +/** + * InputStream for application data as returned by SSLSocket.getInputStream(). + * It uses an InputRecord as internal buffer that is refilled on demand + * whenever it runs out of data. + * + * @author David Brownell + */ +class AppInputStream extends InputStream { + + // static dummy array we use to implement skip() + private final static byte[] SKIP_ARRAY = new byte[1024]; + + private SSLSocketImpl c; + InputRecord r; + + // One element array used to implement the single byte read() method + private final byte[] oneByte = new byte[1]; + + AppInputStream(SSLSocketImpl conn) { + r = new InputRecord(); + c = conn; + } + + /** + * Return the minimum number of bytes that can be read without blocking. + * Currently not synchronized. + */ + @Override + public int available() throws IOException { + if (c.checkEOF() || (r.isAppDataValid() == false)) { + return 0; + } + return r.available(); + } + + /** + * Read a single byte, returning -1 on non-fault EOF status. + */ + @Override + public synchronized int read() throws IOException { + int n = read(oneByte, 0, 1); + if (n <= 0) { // EOF + return -1; + } + return oneByte[0] & 0xff; + } + + /** + * Read up to "len" bytes into this buffer, starting at "off". + * If the layer above needs more data, it asks for more, so we + * are responsible only for blocking to fill at most one buffer, + * and returning "-1" on non-fault EOF status. + */ + @Override + public synchronized int read(byte b[], int off, int len) + throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + if (c.checkEOF()) { + return -1; + } + try { + /* + * Read data if needed ... notice that the connection guarantees + * that handshake, alert, and change cipher spec data streams are + * handled as they arrive, so we never see them here. + */ + while (r.available() == 0) { + c.readDataRecord(r); + if (c.checkEOF()) { + return -1; + } + } + + int howmany = Math.min(len, r.available()); + howmany = r.read(b, off, howmany); + return howmany; + } catch (Exception e) { + // shutdown and rethrow (wrapped) exception as appropriate + c.handleException(e); + // dummy for compiler + return -1; + } + } + + + /** + * Skip n bytes. This implementation is somewhat less efficient + * than possible, but not badly so (redundant copy). We reuse + * the read() code to keep things simpler. Note that SKIP_ARRAY + * is static and may garbled by concurrent use, but we are not interested + * in the data anyway. + */ + @Override + public synchronized long skip(long n) throws IOException { + long skipped = 0; + while (n > 0) { + int len = (int)Math.min(n, SKIP_ARRAY.length); + int r = read(SKIP_ARRAY, 0, len); + if (r <= 0) { + break; + } + n -= r; + skipped += r; + } + return skipped; + } + + /* + * Socket close is already synchronized, no need to block here. + */ + @Override + public void close() throws IOException { + c.close(); + } + + // inherit default mark/reset behavior (throw Exceptions) from InputStream + +} diff --git a/src/sun/security/ssl/AppOutputStream.java b/src/sun/security/ssl/AppOutputStream.java new file mode 100644 index 00000000..f76d907d --- /dev/null +++ b/src/sun/security/ssl/AppOutputStream.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.OutputStream; +import java.io.IOException; + +/* + * Output stream for application data. This is the kind of stream + * that's handed out via SSLSocket.getOutputStream(). It's all the application + * ever sees. + * + * Once the initial handshake has completed, application data may be + * interleaved with handshake data. That is handled internally and remains + * transparent to the application. + * + * @author David Brownell + */ +class AppOutputStream extends OutputStream { + + private SSLSocketImpl c; + OutputRecord r; + + // One element array used to implement the write(byte) method + private final byte[] oneByte = new byte[1]; + + AppOutputStream(SSLSocketImpl conn) { + r = new OutputRecord(Record.ct_application_data); + c = conn; + } + + /** + * Write the data out, NOW. + */ + @Override + synchronized public void write(byte b[], int off, int len) + throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + + // check if the Socket is invalid (error or closed) + c.checkWrite(); + + /* + * By default, we counter chosen plaintext issues on CBC mode + * ciphersuites in SSLv3/TLS1.0 by sending one byte of application + * data in the first record of every payload, and the rest in + * subsequent record(s). Note that the issues have been solved in + * TLS 1.1 or later. + * + * It is not necessary to split the very first application record of + * a freshly negotiated TLS session, as there is no previous + * application data to guess. To improve compatibility, we will not + * split such records. + * + * This avoids issues in the outbound direction. For a full fix, + * the peer must have similar protections. + */ + boolean isFirstRecordOfThePayload = true; + + // Always flush at the end of each application level record. + // This lets application synchronize read and write streams + // however they like; if we buffered here, they couldn't. + try { + do { + boolean holdRecord = false; + int howmuch; + if (isFirstRecordOfThePayload && c.needToSplitPayload()) { + howmuch = Math.min(0x01, r.availableDataBytes()); + /* + * Nagle's algorithm (TCP_NODELAY) was coming into + * play here when writing short (split) packets. + * Signal to the OutputRecord code to internally + * buffer this small packet until the next outbound + * packet (of any type) is written. + */ + if ((len != 1) && (howmuch == 1)) { + holdRecord = true; + } + } else { + howmuch = Math.min(len, r.availableDataBytes()); + } + + if (isFirstRecordOfThePayload && howmuch != 0) { + isFirstRecordOfThePayload = false; + } + + // NOTE: *must* call c.writeRecord() even for howmuch == 0 + if (howmuch > 0) { + r.write(b, off, howmuch); + off += howmuch; + len -= howmuch; + } + c.writeRecord(r, holdRecord); + c.checkWrite(); + } while (len > 0); + } catch (Exception e) { + // shutdown and rethrow (wrapped) exception as appropriate + c.handleException(e); + } + } + + /** + * Write one byte now. + */ + @Override + synchronized public void write(int i) throws IOException { + oneByte[0] = (byte)i; + write(oneByte, 0, 1); + } + + /* + * Socket close is already synchronized, no need to block here. + */ + @Override + public void close() throws IOException { + c.close(); + } + + // inherit no-op flush() +} diff --git a/src/sun/security/ssl/Authenticator.java b/src/sun/security/ssl/Authenticator.java new file mode 100644 index 00000000..2edd0b7b --- /dev/null +++ b/src/sun/security/ssl/Authenticator.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.Arrays; + +/** + * This class represents an SSL/TLS message authentication token, + * which encapsulates a sequence number and ensures that attempts to + * delete or reorder messages can be detected. + * + * Each SSL/TLS connection state contains a sequence number, which + * is maintained separately for read and write states. The sequence + * number MUST be set to zero whenever a connection state is made the + * active state. Sequence numbers are of type uint64 and may not + * exceed 2^64-1. Sequence numbers do not wrap. If a SSL/TLS + * implementation would need to wrap a sequence number, it must + * renegotiate instead. A sequence number is incremented after each + * record: specifically, the first record transmitted under a + * particular connection state MUST use sequence number 0. + */ +class Authenticator { + + // byte array containing the additional authentication information for + // each record + private final byte[] block; + + // the block size of SSL v3.0: + // sequence number + record type + + record length + private static final int BLOCK_SIZE_SSL = 8 + 1 + 2; + + // the block size of TLS v1.0 and later: + // sequence number + record type + protocol version + record length + private static final int BLOCK_SIZE_TLS = 8 + 1 + 2 + 2; + + /** + * Default construct, no message authentication token is initialized. + * + * Note that this construct can only be called for null MAC + */ + Authenticator() { + block = new byte[0]; + } + + /** + * Constructs the message authentication token for the specified + * SSL/TLS protocol. + */ + Authenticator(ProtocolVersion protocolVersion) { + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + block = new byte[BLOCK_SIZE_TLS]; + block[9] = protocolVersion.major; + block[10] = protocolVersion.minor; + } else { + block = new byte[BLOCK_SIZE_SSL]; + } + } + + /** + * Checks whether the sequence number is close to wrap. + * + * Sequence numbers are of type uint64 and may not exceed 2^64-1. + * Sequence numbers do not wrap. When the sequence number is near + * to wrap, we need to close the connection immediately. + * + * @return true if the sequence number is close to wrap + */ + final boolean seqNumOverflow() { + /* + * Conservatively, we don't allow more records to be generated + * when there are only 2^8 sequence numbers left. + */ + return (block.length != 0 && + block[0] == (byte)0xFF && block[1] == (byte)0xFF && + block[2] == (byte)0xFF && block[3] == (byte)0xFF && + block[4] == (byte)0xFF && block[5] == (byte)0xFF && + block[6] == (byte)0xFF); + } + + /** + * Checks whether the sequence number close to renew. + * + * Sequence numbers are of type uint64 and may not exceed 2^64-1. + * Sequence numbers do not wrap. If a TLS + * implementation would need to wrap a sequence number, it must + * renegotiate instead. + * + * @return true if the sequence number is huge enough to renew + */ + final boolean seqNumIsHuge() { + /* + * Conservatively, we should ask for renegotiation when there are + * only 2^48 sequence numbers left. + */ + return (block.length != 0 && + block[0] == (byte)0xFF && block[1] == (byte)0xFF); + } + + /** + * Gets the current sequence number. + * + * @return the byte array of the current sequence number + */ + final byte[] sequenceNumber() { + return Arrays.copyOf(block, 8); + } + + /** + * Acquires the current message authentication information with the + * specified record type and fragment length, and then increases the + * sequence number. + * + * @param type the record type + * @param length the fragment of the record + * @return the byte array of the current message authentication information + */ + final byte[] acquireAuthenticationBytes(byte type, int length) { + byte[] copy = block.clone(); + + if (block.length != 0) { + copy[8] = type; + copy[copy.length - 2] = (byte)(length >> 8); + copy[copy.length - 1] = (byte)(length); + + /* + * Increase the sequence number in the block array + * it is a 64-bit number stored in big-endian format + */ + int k = 7; + while ((k >= 0) && (++block[k] == 0)) { + k--; + } + } + + return copy; + } + +} diff --git a/src/sun/security/ssl/BaseSSLSocketImpl.java b/src/sun/security/ssl/BaseSSLSocketImpl.java new file mode 100644 index 00000000..557f1edd --- /dev/null +++ b/src/sun/security/ssl/BaseSSLSocketImpl.java @@ -0,0 +1,640 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.nio.channels.SocketChannel; +import java.net.*; + +import javax.net.ssl.*; + +/** + * Abstract base class for SSLSocketImpl. Its purpose is to house code with + * no SSL related logic (or no logic at all). This makes SSLSocketImpl shorter + * and easier to read. It contains a few constants and static methods plus + * overridden java.net.Socket methods. + * + * Methods are defined final to ensure that they are not accidentally + * overridden in SSLSocketImpl. + * + * @see javax.net.ssl.SSLSocket + * @see SSLSocketImpl + * + */ +abstract class BaseSSLSocketImpl extends SSLSocket { + + /* + * Normally "self" is "this" ... but not when this connection is + * layered over a preexisting socket. If we're using an existing + * socket, we delegate some actions to it. Else, we delegate + * instead to "super". This is important to ensure that we don't + * recurse infinitely ... e.g. close() calling itself, or doing + * I/O in terms of our own streams. + */ + final private Socket self; + final private InputStream consumedInput; + + BaseSSLSocketImpl() { + super(); + this.self = this; + this.consumedInput = null; + } + + BaseSSLSocketImpl(Socket socket) { + super(); + this.self = socket; + this.consumedInput = null; + } + + BaseSSLSocketImpl(Socket socket, InputStream consumed) { + super(); + this.self = socket; + this.consumedInput = consumed; + } + + // + // CONSTANTS AND STATIC METHODS + // + + /** + * TLS requires that a close_notify warning alert is sent before the + * connection is closed in order to avoid truncation attacks. Some + * implementations (MS IIS and others) don't do that. The property + * below controls whether we accept that or treat it as an error. + * + * The default is "false", i.e. tolerate the broken behavior. + */ + private final static String PROP_NAME = + "com.sun.net.ssl.requireCloseNotify"; + + final static boolean requireCloseNotify = + Debug.getBooleanProperty(PROP_NAME, false); + + // + // MISC SOCKET METHODS + // + + /** + * Returns the unique {@link java.nio.SocketChannel SocketChannel} object + * associated with this socket, if any. + * @see Socket#getChannel + */ + @Override + public final SocketChannel getChannel() { + if (self == this) { + return super.getChannel(); + } else { + return self.getChannel(); + } + } + + /** + * Binds the address to the socket. + * @see Socket#bind + */ + @Override + public void bind(SocketAddress bindpoint) throws IOException { + /* + * Bind to this socket + */ + if (self == this) { + super.bind(bindpoint); + } else { + // If we're binding on a layered socket... + throw new IOException( + "Underlying socket should already be connected"); + } + } + + /** + * Returns the address of the endpoint this socket is connected to + * @see Socket#getLocalSocketAddress + */ + @Override + public SocketAddress getLocalSocketAddress() { + if (self == this) { + return super.getLocalSocketAddress(); + } else { + return self.getLocalSocketAddress(); + } + } + + /** + * Returns the address of the endpoint this socket is connected to + * @see Socket#getRemoteSocketAddress + */ + @Override + public SocketAddress getRemoteSocketAddress() { + if (self == this) { + return super.getRemoteSocketAddress(); + } else { + return self.getRemoteSocketAddress(); + } + } + + /** + * Connects this socket to the server. + * + * This method is either called on an unconnected SSLSocketImpl by the + * application, or it is called in the constructor of a regular + * SSLSocketImpl. If we are layering on top on another socket, then + * this method should not be called, because we assume that the + * underlying socket is already connected by the time it is passed to + * us. + * + * @param endpoint the SocketAddress + * @throws IOException if an error occurs during the connection + */ + @Override + public final void connect(SocketAddress endpoint) throws IOException { + connect(endpoint, 0); + } + + /** + * Returns the connection state of the socket. + * @see Socket#isConnected + */ + @Override + public final boolean isConnected() { + if (self == this) { + return super.isConnected(); + } else { + return self.isConnected(); + } + } + + /** + * Returns the binding state of the socket. + * @see Socket#isBound + */ + @Override + public final boolean isBound() { + if (self == this) { + return super.isBound(); + } else { + return self.isBound(); + } + } + + // + // CLOSE RELATED METHODS + // + + /** + * The semantics of shutdownInput is not supported in TLS 1.0 + * spec. Thus when the method is called on an SSL socket, an + * UnsupportedOperationException will be thrown. + * + * @throws UnsupportedOperationException + */ + @Override + public final void shutdownInput() throws IOException { + throw new UnsupportedOperationException("The method shutdownInput()" + + " is not supported in SSLSocket"); + } + + /** + * The semantics of shutdownOutput is not supported in TLS 1.0 + * spec. Thus when the method is called on an SSL socket, an + * UnsupportedOperationException will be thrown. + * + * @throws UnsupportedOperationException + */ + @Override + public final void shutdownOutput() throws IOException { + throw new UnsupportedOperationException("The method shutdownOutput()" + + " is not supported in SSLSocket"); + + } + + /** + * Returns the input state of the socket + * @see Socket#isInputShutdown + */ + @Override + public final boolean isInputShutdown() { + if (self == this) { + return super.isInputShutdown(); + } else { + return self.isInputShutdown(); + } + } + + /** + * Returns the output state of the socket + * @see Socket#isOutputShutdown + */ + @Override + public final boolean isOutputShutdown() { + if (self == this) { + return super.isOutputShutdown(); + } else { + return self.isOutputShutdown(); + } + } + + /** + * Ensures that the SSL connection is closed down as cleanly + * as possible, in case the application forgets to do so. + * This allows SSL connections to be implicitly reclaimed, + * rather than forcing them to be explicitly reclaimed at + * the penalty of prematurly killing SSL sessions. + */ + @Override + protected final void finalize() throws Throwable { + try { + close(); + } catch (IOException e1) { + try { + if (self == this) { + super.close(); + } + } catch (IOException e2) { + // ignore + } + } finally { + // We called close on the underlying socket above to + // make doubly sure all resources got released. We + // don't finalize self in the case of overlain sockets, + // that's a different object which the GC will finalize + // separately. + + super.finalize(); + } + } + + // + // GET ADDRESS METHODS + // + + /** + * Returns the address of the remote peer for this connection. + */ + @Override + public final InetAddress getInetAddress() { + if (self == this) { + return super.getInetAddress(); + } else { + return self.getInetAddress(); + } + } + + /** + * Gets the local address to which the socket is bound. + * + * @return the local address to which the socket is bound. + * @since JDK1.1 + */ + @Override + public final InetAddress getLocalAddress() { + if (self == this) { + return super.getLocalAddress(); + } else { + return self.getLocalAddress(); + } + } + + /** + * Returns the number of the remote port that this connection uses. + */ + @Override + public final int getPort() { + if (self == this) { + return super.getPort(); + } else { + return self.getPort(); + } + } + + /** + * Returns the number of the local port that this connection uses. + */ + @Override + public final int getLocalPort() { + if (self == this) { + return super.getLocalPort(); + } else { + return self.getLocalPort(); + } + } + + // + // SOCKET OPTION METHODS + // + + /** + * Enables or disables the Nagle optimization. + * @see Socket#setTcpNoDelay + */ + @Override + public final void setTcpNoDelay(boolean value) throws SocketException { + if (self == this) { + super.setTcpNoDelay(value); + } else { + self.setTcpNoDelay(value); + } + } + + /** + * Returns true if the Nagle optimization is disabled. This + * relates to low-level buffering of TCP traffic, delaying the + * traffic to promote better throughput. + * + * @see Socket#getTcpNoDelay + */ + @Override + public final boolean getTcpNoDelay() throws SocketException { + if (self == this) { + return super.getTcpNoDelay(); + } else { + return self.getTcpNoDelay(); + } + } + + /** + * Assigns the socket's linger timeout. + * @see Socket#setSoLinger + */ + @Override + public final void setSoLinger(boolean flag, int linger) + throws SocketException { + if (self == this) { + super.setSoLinger(flag, linger); + } else { + self.setSoLinger(flag, linger); + } + } + + /** + * Returns the socket's linger timeout. + * @see Socket#getSoLinger + */ + @Override + public final int getSoLinger() throws SocketException { + if (self == this) { + return super.getSoLinger(); + } else { + return self.getSoLinger(); + } + } + + /** + * Send one byte of urgent data on the socket. + * @see Socket#sendUrgentData + * At this point, there seems to be no specific requirement to support + * this for an SSLSocket. An implementation can be provided if a need + * arises in future. + */ + @Override + public final void sendUrgentData(int data) throws SocketException { + throw new SocketException("This method is not supported " + + "by SSLSockets"); + } + + /** + * Enable/disable OOBINLINE (receipt of TCP urgent data) By default, this + * option is disabled and TCP urgent data received on a socket is silently + * discarded. + * @see Socket#setOOBInline + * Setting OOBInline does not have any effect on SSLSocket, + * since currently we don't support sending urgent data. + */ + @Override + public final void setOOBInline(boolean on) throws SocketException { + throw new SocketException("This method is ineffective, since" + + " sending urgent data is not supported by SSLSockets"); + } + + /** + * Tests if OOBINLINE is enabled. + * @see Socket#getOOBInline + */ + @Override + public final boolean getOOBInline() throws SocketException { + throw new SocketException("This method is ineffective, since" + + " sending urgent data is not supported by SSLSockets"); + } + + /** + * Returns the socket timeout. + * @see Socket#getSoTimeout + */ + @Override + public final int getSoTimeout() throws SocketException { + if (self == this) { + return super.getSoTimeout(); + } else { + return self.getSoTimeout(); + } + } + + @Override + public final void setSendBufferSize(int size) throws SocketException { + if (self == this) { + super.setSendBufferSize(size); + } else { + self.setSendBufferSize(size); + } + } + + @Override + public final int getSendBufferSize() throws SocketException { + if (self == this) { + return super.getSendBufferSize(); + } else { + return self.getSendBufferSize(); + } + } + + @Override + public final void setReceiveBufferSize(int size) throws SocketException { + if (self == this) { + super.setReceiveBufferSize(size); + } else { + self.setReceiveBufferSize(size); + } + } + + @Override + public final int getReceiveBufferSize() throws SocketException { + if (self == this) { + return super.getReceiveBufferSize(); + } else { + return self.getReceiveBufferSize(); + } + } + + /** + * Enable/disable SO_KEEPALIVE. + * @see Socket#setKeepAlive + */ + @Override + public final void setKeepAlive(boolean on) throws SocketException { + if (self == this) { + super.setKeepAlive(on); + } else { + self.setKeepAlive(on); + } + } + + /** + * Tests if SO_KEEPALIVE is enabled. + * @see Socket#getKeepAlive + */ + @Override + public final boolean getKeepAlive() throws SocketException { + if (self == this) { + return super.getKeepAlive(); + } else { + return self.getKeepAlive(); + } + } + + /** + * Sets traffic class or type-of-service octet in the IP header for + * packets sent from this Socket. + * @see Socket#setTrafficClass + */ + @Override + public final void setTrafficClass(int tc) throws SocketException { + if (self == this) { + super.setTrafficClass(tc); + } else { + self.setTrafficClass(tc); + } + } + + /** + * Gets traffic class or type-of-service in the IP header for packets + * sent from this Socket. + * @see Socket#getTrafficClass + */ + @Override + public final int getTrafficClass() throws SocketException { + if (self == this) { + return super.getTrafficClass(); + } else { + return self.getTrafficClass(); + } + } + + /** + * Enable/disable SO_REUSEADDR. + * @see Socket#setReuseAddress + */ + @Override + public final void setReuseAddress(boolean on) throws SocketException { + if (self == this) { + super.setReuseAddress(on); + } else { + self.setReuseAddress(on); + } + } + + /** + * Tests if SO_REUSEADDR is enabled. + * @see Socket#getReuseAddress + */ + @Override + public final boolean getReuseAddress() throws SocketException { + if (self == this) { + return super.getReuseAddress(); + } else { + return self.getReuseAddress(); + } + } + + /** + * Sets performance preferences for this socket. + * + * @see Socket#setPerformancePreferences(int, int, int) + */ + @Override + public void setPerformancePreferences(int connectionTime, + int latency, int bandwidth) { + if (self == this) { + super.setPerformancePreferences( + connectionTime, latency, bandwidth); + } else { + self.setPerformancePreferences( + connectionTime, latency, bandwidth); + } + } + + @Override + public String toString() { + if (self == this) { + return super.toString(); + } + + return self.toString(); + } + + @Override + public InputStream getInputStream() throws IOException { + if (self == this) { + return super.getInputStream(); + } + + if (consumedInput != null) { + return new SequenceInputStream(consumedInput, + self.getInputStream()); + } + + return self.getInputStream(); + } + + @Override + public OutputStream getOutputStream() throws IOException { + if (self == this) { + return super.getOutputStream(); + } + + return self.getOutputStream(); + } + + @Override + public synchronized void close() throws IOException { + if (self == this) { + super.close(); + } else { + self.close(); + } + } + + @Override + public synchronized void setSoTimeout(int timeout) throws SocketException { + if (self == this) { + super.setSoTimeout(timeout); + } else { + self.setSoTimeout(timeout); + } + } + + boolean isLayered() { + return (self != this); + } +} diff --git a/src/sun/security/ssl/ByteBufferInputStream.java b/src/sun/security/ssl/ByteBufferInputStream.java new file mode 100644 index 00000000..05107988 --- /dev/null +++ b/src/sun/security/ssl/ByteBufferInputStream.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; + +/** + * A simple InputStream which uses ByteBuffers as it's backing store. + *

    + * The only IOException should come if the InputStream has been closed. + * All other IOException should not occur because all the data is local. + * Data reads on an exhausted ByteBuffer returns a -1. + * + * @author Brad Wetmore + */ +class ByteBufferInputStream extends InputStream { + + ByteBuffer bb; + + ByteBufferInputStream(ByteBuffer bb) { + this.bb = bb; + } + + /** + * Returns a byte from the ByteBuffer. + * + * Increments position(). + */ + @Override + public int read() throws IOException { + + if (bb == null) { + throw new IOException("read on a closed InputStream"); + } + + if (bb.remaining() == 0) { + return -1; + } + + return (bb.get() & 0xFF); // need to be in the range 0 to 255 + } + + /** + * Returns a byte array from the ByteBuffer. + * + * Increments position(). + */ + @Override + public int read(byte b[]) throws IOException { + + if (bb == null) { + throw new IOException("read on a closed InputStream"); + } + + return read(b, 0, b.length); + } + + /** + * Returns a byte array from the ByteBuffer. + * + * Increments position(). + */ + @Override + public int read(byte b[], int off, int len) throws IOException { + + if (bb == null) { + throw new IOException("read on a closed InputStream"); + } + + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + int length = Math.min(bb.remaining(), len); + if (length == 0) { + return -1; + } + + bb.get(b, off, length); + return length; + } + + /** + * Skips over and discards n bytes of data from this input + * stream. + */ + @Override + public long skip(long n) throws IOException { + + if (bb == null) { + throw new IOException("skip on a closed InputStream"); + } + + if (n <= 0) { + return 0; + } + + /* + * ByteBuffers have at most an int, so lose the upper bits. + * The contract allows this. + */ + int nInt = (int) n; + int skip = Math.min(bb.remaining(), nInt); + + bb.position(bb.position() + skip); + + return nInt; + } + + /** + * Returns the number of bytes that can be read (or skipped over) + * from this input stream without blocking by the next caller of a + * method for this input stream. + */ + @Override + public int available() throws IOException { + + if (bb == null) { + throw new IOException("available on a closed InputStream"); + } + + return bb.remaining(); + } + + /** + * Closes this input stream and releases any system resources associated + * with the stream. + * + * @exception IOException if an I/O error occurs. + */ + @Override + public void close() throws IOException { + bb = null; + } + + /** + * Marks the current position in this input stream. + */ + @Override + public synchronized void mark(int readlimit) {} + + /** + * Repositions this stream to the position at the time the + * mark method was last called on this input stream. + */ + @Override + public synchronized void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + /** + * Tests if this input stream supports the mark and + * reset methods. + */ + @Override + public boolean markSupported() { + return false; + } +} diff --git a/src/sun/security/ssl/CipherBox.java b/src/sun/security/ssl/CipherBox.java new file mode 100644 index 00000000..f144a028 --- /dev/null +++ b/src/sun/security/ssl/CipherBox.java @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Arrays; + +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.GCMParameterSpec; + +import java.nio.*; + +import sun.security.ssl.CipherSuite.*; + +import static sun.security.ssl.CipherSuite.B_NULL; +import static sun.security.ssl.CipherSuite.CipherType.AEAD_CIPHER; +import static sun.security.ssl.CipherSuite.CipherType.BLOCK_CIPHER; +import static sun.security.ssl.CipherSuite.CipherType.STREAM_CIPHER; + +import sun.misc.HexDumpEncoder; + + +/** + * This class handles bulk data enciphering/deciphering for each SSLv3 + * message. This provides data confidentiality. Stream ciphers (such + * as RC4) don't need to do padding; block ciphers (e.g. DES) need it. + * + * Individual instances are obtained by calling the static method + * newCipherBox(), which should only be invoked by BulkCipher.newCipher(). + * + * In RFC 2246, with bock ciphers in CBC mode, the Initialization + * Vector (IV) for the first record is generated with the other keys + * and secrets when the security parameters are set. The IV for + * subsequent records is the last ciphertext block from the previous + * record. + * + * In RFC 4346, the implicit Initialization Vector (IV) is replaced + * with an explicit IV to protect against CBC attacks. RFC 4346 + * recommends two algorithms used to generated the per-record IV. + * The implementation uses the algorithm (2)(b), as described at + * section 6.2.3.2 of RFC 4346. + * + * The usage of IV in CBC block cipher can be illustrated in + * the following diagrams. + * + * (random) + * R P1 IV C1 + * | | | | + * SIV---+ |-----+ |-... |----- |------ + * | | | | | | | | + * +----+ | +----+ | +----+ | +----+ | + * | Ek | | + Ek + | | Dk | | | Dk | | + * +----+ | +----+ | +----+ | +----+ | + * | | | | | | | | + * |----| |----| SIV--+ |----| |-... + * | | | | + * IV C1 R P1 + * (discard) + * + * CBC Encryption CBC Decryption + * + * NOTE that any ciphering involved in key exchange (e.g. with RSA) is + * handled separately. + * + * @author David Brownell + * @author Andreas Sterbenz + */ +final class CipherBox { + + // A CipherBox that implements the identity operation + final static CipherBox NULL = new CipherBox(); + + /* Class and subclass dynamic debugging support */ + private static final Debug debug = Debug.getInstance("ssl"); + + // the protocol version this cipher conforms to + private final ProtocolVersion protocolVersion; + + // cipher object + private final Cipher cipher; + + /** + * secure random + */ + private SecureRandom random; + + /** + * fixed IV, the implicit nonce of AEAD cipher suite, only apply to + * AEAD cipher suites + */ + private final byte[] fixedIv; + + /** + * the key, reserved only for AEAD cipher initialization + */ + private final Key key; + + /** + * the operation mode, reserved for AEAD cipher initialization + */ + private final int mode; + + /** + * the authentication tag size, only apply to AEAD cipher suites + */ + private final int tagSize; + + /** + * the record IV length, only apply to AEAD cipher suites + */ + private final int recordIvSize; + + /** + * cipher type + */ + private final CipherType cipherType; + + /** + * Fixed masks of various block size, as the initial decryption IVs + * for TLS 1.1 or later. + * + * For performance, we do not use random IVs. As the initial decryption + * IVs will be discarded by TLS decryption processes, so the fixed masks + * do not hurt cryptographic strength. + */ + private static Hashtable masks; + + /** + * NULL cipherbox. Identity operation, no encryption. + */ + private CipherBox() { + this.protocolVersion = ProtocolVersion.DEFAULT; + this.cipher = null; + this.cipherType = STREAM_CIPHER; + this.fixedIv = new byte[0]; + this.key = null; + this.mode = Cipher.ENCRYPT_MODE; // choose at random + this.random = null; + this.tagSize = 0; + this.recordIvSize = 0; + } + + /** + * Construct a new CipherBox using the cipher transformation. + * + * @exception NoSuchAlgorithmException if no appropriate JCE Cipher + * implementation could be found. + */ + private CipherBox(ProtocolVersion protocolVersion, BulkCipher bulkCipher, + SecretKey key, IvParameterSpec iv, SecureRandom random, + boolean encrypt) throws NoSuchAlgorithmException { + try { + this.protocolVersion = protocolVersion; + this.cipher = JsseJce.getCipher(bulkCipher.transformation); + this.mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + + if (random == null) { + random = JsseJce.getSecureRandom(); + } + this.random = random; + this.cipherType = bulkCipher.cipherType; + + /* + * RFC 4346 recommends two algorithms used to generated the + * per-record IV. The implementation uses the algorithm (2)(b), + * as described at section 6.2.3.2 of RFC 4346. + * + * As we don't care about the initial IV value for TLS 1.1 or + * later, so if the "iv" parameter is null, we use the default + * value generated by Cipher.init() for encryption, and a fixed + * mask for decryption. + */ + if (iv == null && bulkCipher.ivSize != 0 && + mode == Cipher.DECRYPT_MODE && + protocolVersion.v >= ProtocolVersion.TLS11.v) { + iv = getFixedMask(bulkCipher.ivSize); + } + + if (cipherType == AEAD_CIPHER) { + // AEAD must completely initialize the cipher for each packet, + // and so we save initialization parameters for packet + // processing time. + + // Set the tag size for AEAD cipher + tagSize = bulkCipher.tagSize; + + // Reserve the key for AEAD cipher initialization + this.key = key; + + fixedIv = iv.getIV(); + if (fixedIv == null || + fixedIv.length != bulkCipher.fixedIvSize) { + throw new RuntimeException("Improper fixed IV for AEAD"); + } + + // Set the record IV length for AEAD cipher + recordIvSize = bulkCipher.ivSize - bulkCipher.fixedIvSize; + + // DON'T initialize the cipher for AEAD! + } else { + // CBC only requires one initialization during its lifetime + // (future packets/IVs set the proper CBC state), so we can + // initialize now. + + // Zeroize the variables that only apply to AEAD cipher + this.tagSize = 0; + this.fixedIv = new byte[0]; + this.recordIvSize = 0; + this.key = null; + + // Initialize the cipher + cipher.init(mode, key, iv, random); + } + } catch (NoSuchAlgorithmException e) { + throw e; + } catch (Exception e) { + throw new NoSuchAlgorithmException + ("Could not create cipher " + bulkCipher, e); + } catch (ExceptionInInitializerError e) { + throw new NoSuchAlgorithmException + ("Could not create cipher " + bulkCipher, e); + } + } + + /* + * Factory method to obtain a new CipherBox object. + */ + static CipherBox newCipherBox(ProtocolVersion version, BulkCipher cipher, + SecretKey key, IvParameterSpec iv, SecureRandom random, + boolean encrypt) throws NoSuchAlgorithmException { + if (cipher.allowed == false) { + throw new NoSuchAlgorithmException("Unsupported cipher " + cipher); + } + + if (cipher == B_NULL) { + return NULL; + } else { + return new CipherBox(version, cipher, key, iv, random, encrypt); + } + } + + /* + * Get a fixed mask, as the initial decryption IVs for TLS 1.1 or later. + */ + private static IvParameterSpec getFixedMask(int ivSize) { + if (masks == null) { + masks = new Hashtable(5); + } + + IvParameterSpec iv = masks.get(ivSize); + if (iv == null) { + iv = new IvParameterSpec(new byte[ivSize]); + masks.put(ivSize, iv); + } + + return iv; + } + + /* + * Encrypts a block of data, returning the size of the + * resulting block. + */ + int encrypt(byte[] buf, int offset, int len) { + if (cipher == null) { + return len; + } + + try { + int blockSize = cipher.getBlockSize(); + if (cipherType == BLOCK_CIPHER) { + len = addPadding(buf, offset, len, blockSize); + } + + if (debug != null && Debug.isOn("plaintext")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println( + "Padded plaintext before ENCRYPTION: len = " + + len); + hd.encodeBuffer( + new ByteArrayInputStream(buf, offset, len), + System.out); + } catch (IOException e) { } + } + + + if (cipherType == AEAD_CIPHER) { + try { + return cipher.doFinal(buf, offset, len, buf, offset); + } catch (IllegalBlockSizeException | BadPaddingException ibe) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode in JCE provider " + + cipher.getProvider().getName(), ibe); + } + } else { + int newLen = cipher.update(buf, offset, len, buf, offset); + if (newLen != len) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error " + + "in JCE provider " + cipher.getProvider().getName()); + } + return newLen; + } + } catch (ShortBufferException e) { + // unlikely to happen, we should have enough buffer space here + throw new ArrayIndexOutOfBoundsException(e.toString()); + } + } + + /* + * Encrypts a ByteBuffer block of data, returning the size of the + * resulting block. + * + * The byte buffers position and limit initially define the amount + * to encrypt. On return, the position and limit are + * set to last position padded/encrypted. The limit may have changed + * because of the added padding bytes. + */ + int encrypt(ByteBuffer bb, int outLimit) { + + int len = bb.remaining(); + + if (cipher == null) { + bb.position(bb.limit()); + return len; + } + + int pos = bb.position(); + + int blockSize = cipher.getBlockSize(); + if (cipherType == BLOCK_CIPHER) { + // addPadding adjusts pos/limit + len = addPadding(bb, blockSize); + bb.position(pos); + } + + if (debug != null && Debug.isOn("plaintext")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println( + "Padded plaintext before ENCRYPTION: len = " + + len); + hd.encodeBuffer(bb.duplicate(), System.out); + + } catch (IOException e) { } + } + + /* + * Encrypt "in-place". This does not add its own padding. + */ + ByteBuffer dup = bb.duplicate(); + if (cipherType == AEAD_CIPHER) { + try { + int outputSize = cipher.getOutputSize(dup.remaining()); + if (outputSize > bb.remaining()) { + // need to expand the limit of the output buffer for + // the authentication tag. + // + // DON'T worry about the buffer's capacity, we have + // reserved space for the authentication tag. + if (outLimit < pos + outputSize) { + // unlikely to happen + throw new ShortBufferException( + "need more space in output buffer"); + } + bb.limit(pos + outputSize); + } + int newLen = cipher.doFinal(dup, bb); + if (newLen != outputSize) { + throw new RuntimeException( + "Cipher buffering error in JCE provider " + + cipher.getProvider().getName()); + } + return newLen; + } catch (IllegalBlockSizeException | + BadPaddingException | ShortBufferException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode in JCE provider " + + cipher.getProvider().getName(), ibse); + } + } else { + int newLen; + try { + newLen = cipher.update(dup, bb); + } catch (ShortBufferException sbe) { + // unlikely to happen + throw new RuntimeException("Cipher buffering error " + + "in JCE provider " + cipher.getProvider().getName()); + } + + if (bb.position() != dup.position()) { + throw new RuntimeException("bytebuffer padding error"); + } + + if (newLen != len) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error " + + "in JCE provider " + cipher.getProvider().getName()); + } + return newLen; + } + } + + + /* + * Decrypts a block of data, returning the size of the + * resulting block if padding was required. + * + * For SSLv3 and TLSv1.0, with block ciphers in CBC mode the + * Initialization Vector (IV) for the first record is generated by + * the handshake protocol, the IV for subsequent records is the + * last ciphertext block from the previous record. + * + * From TLSv1.1, the implicit IV is replaced with an explicit IV to + * protect against CBC attacks. + * + * Differentiating between bad_record_mac and decryption_failed alerts + * may permit certain attacks against CBC mode. It is preferable to + * uniformly use the bad_record_mac alert to hide the specific type of + * the error. + */ + int decrypt(byte[] buf, int offset, int len, + int tagLen) throws BadPaddingException { + if (cipher == null) { + return len; + } + + try { + int newLen; + if (cipherType == AEAD_CIPHER) { + try { + newLen = cipher.doFinal(buf, offset, len, buf, offset); + } catch (IllegalBlockSizeException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode in JCE provider " + + cipher.getProvider().getName(), ibse); + } + } else { + newLen = cipher.update(buf, offset, len, buf, offset); + if (newLen != len) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error " + + "in JCE provider " + cipher.getProvider().getName()); + } + } + if (debug != null && Debug.isOn("plaintext")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println( + "Padded plaintext after DECRYPTION: len = " + + newLen); + hd.encodeBuffer( + new ByteArrayInputStream(buf, offset, newLen), + System.out); + } catch (IOException e) { } + } + + if (cipherType == BLOCK_CIPHER) { + int blockSize = cipher.getBlockSize(); + newLen = removePadding( + buf, offset, newLen, tagLen, blockSize, protocolVersion); + + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (newLen < blockSize) { + throw new BadPaddingException("invalid explicit IV"); + } + } + } + return newLen; + } catch (ShortBufferException e) { + // unlikely to happen, we should have enough buffer space here + throw new ArrayIndexOutOfBoundsException(e.toString()); + } + } + + + /* + * Decrypts a block of data, returning the size of the + * resulting block if padding was required. position and limit + * point to the end of the decrypted/depadded data. The initial + * limit and new limit may be different, given we may + * have stripped off some padding bytes. + * + * @see decrypt(byte[], int, int) + */ + int decrypt(ByteBuffer bb, int tagLen) throws BadPaddingException { + + int len = bb.remaining(); + + if (cipher == null) { + bb.position(bb.limit()); + return len; + } + + try { + /* + * Decrypt "in-place". + */ + int pos = bb.position(); + ByteBuffer dup = bb.duplicate(); + int newLen; + if (cipherType == AEAD_CIPHER) { + try { + newLen = cipher.doFinal(dup, bb); + } catch (IllegalBlockSizeException ibse) { + // unlikely to happen + throw new RuntimeException( + "Cipher error in AEAD mode \"" + ibse.getMessage() + + " \"in JCE provider " + cipher.getProvider().getName()); + } + } else { + newLen = cipher.update(dup, bb); + if (newLen != len) { + // catch BouncyCastle buffering error + throw new RuntimeException("Cipher buffering error " + + "in JCE provider " + cipher.getProvider().getName()); + } + } + + // reset the limit to the end of the decryted data + bb.limit(pos + newLen); + + if (debug != null && Debug.isOn("plaintext")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println( + "Padded plaintext after DECRYPTION: len = " + + newLen); + + hd.encodeBuffer( + (ByteBuffer)bb.duplicate().position(pos), System.out); + } catch (IOException e) { } + } + + /* + * Remove the block padding. + */ + if (cipherType == BLOCK_CIPHER) { + int blockSize = cipher.getBlockSize(); + bb.position(pos); + newLen = removePadding(bb, tagLen, blockSize, protocolVersion); + + // check the explicit IV of TLS v1.1 or later + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + if (newLen < blockSize) { + throw new BadPaddingException("invalid explicit IV"); + } + + // reset the position to the end of the decrypted data + bb.position(bb.limit()); + } + } + return newLen; + } catch (ShortBufferException e) { + // unlikely to happen, we should have enough buffer space here + throw new ArrayIndexOutOfBoundsException(e.toString()); + } + } + + private static int addPadding(byte[] buf, int offset, int len, + int blockSize) { + int newlen = len + 1; + byte pad; + int i; + + if ((newlen % blockSize) != 0) { + newlen += blockSize - 1; + newlen -= newlen % blockSize; + } + pad = (byte) (newlen - len); + + if (buf.length < (newlen + offset)) { + throw new IllegalArgumentException("no space to pad buffer"); + } + + /* + * TLS version of the padding works for both SSLv3 and TLSv1 + */ + for (i = 0, offset += len; i < pad; i++) { + buf [offset++] = (byte) (pad - 1); + } + return newlen; + } + + /* + * Apply the padding to the buffer. + * + * Limit is advanced to the new buffer length. + * Position is equal to limit. + */ + private static int addPadding(ByteBuffer bb, int blockSize) { + + int len = bb.remaining(); + int offset = bb.position(); + + int newlen = len + 1; + byte pad; + int i; + + if ((newlen % blockSize) != 0) { + newlen += blockSize - 1; + newlen -= newlen % blockSize; + } + pad = (byte) (newlen - len); + + /* + * Update the limit to what will be padded. + */ + bb.limit(newlen + offset); + + /* + * TLS version of the padding works for both SSLv3 and TLSv1 + */ + for (i = 0, offset += len; i < pad; i++) { + bb.put(offset++, (byte) (pad - 1)); + } + + bb.position(offset); + bb.limit(offset); + + return newlen; + } + + /* + * A constant-time check of the padding. + * + * NOTE that we are checking both the padding and the padLen bytes here. + * + * The caller MUST ensure that the len parameter is a positive number. + */ + private static int[] checkPadding( + byte[] buf, int offset, int len, byte pad) { + + if (len <= 0) { + throw new RuntimeException("padding len must be positive"); + } + + // An array of hits is used to prevent Hotspot optimization for + // the purpose of a constant-time check. + int[] results = {0, 0}; // {missed #, matched #} + for (int i = 0; i <= 256;) { + for (int j = 0; j < len && i <= 256; j++, i++) { // j <= i + if (buf[offset + j] != pad) { + results[0]++; // mismatched padding data + } else { + results[1]++; // matched padding data + } + } + } + + return results; + } + + /* + * A constant-time check of the padding. + * + * NOTE that we are checking both the padding and the padLen bytes here. + * + * The caller MUST ensure that the bb parameter has remaining. + */ + private static int[] checkPadding(ByteBuffer bb, byte pad) { + + if (!bb.hasRemaining()) { + throw new RuntimeException("hasRemaining() must be positive"); + } + + // An array of hits is used to prevent Hotspot optimization for + // the purpose of a constant-time check. + int[] results = {0, 0}; // {missed #, matched #} + bb.mark(); + for (int i = 0; i <= 256; bb.reset()) { + for (; bb.hasRemaining() && i <= 256; i++) { + if (bb.get() != pad) { + results[0]++; // mismatched padding data + } else { + results[1]++; // matched padding data + } + } + } + + return results; + } + + /* + * Typical TLS padding format for a 64 bit block cipher is as follows: + * xx xx xx xx xx xx xx 00 + * xx xx xx xx xx xx 01 01 + * ... + * xx 06 06 06 06 06 06 06 + * 07 07 07 07 07 07 07 07 + * TLS also allows any amount of padding from 1 and 256 bytes as long + * as it makes the data a multiple of the block size + */ + private static int removePadding(byte[] buf, int offset, int len, + int tagLen, int blockSize, + ProtocolVersion protocolVersion) throws BadPaddingException { + + // last byte is length byte (i.e. actual padding length - 1) + int padOffset = offset + len - 1; + int padLen = buf[padOffset] & 0xFF; + + int newLen = len - (padLen + 1); + if ((newLen - tagLen) < 0) { + // If the buffer is not long enough to contain the padding plus + // a MAC tag, do a dummy constant-time padding check. + // + // Note that it is a dummy check, so we won't care about what is + // the actual padding data. + checkPadding(buf, offset, len, (byte)(padLen & 0xFF)); + + throw new BadPaddingException("Invalid Padding length: " + padLen); + } + + // The padding data should be filled with the padding length value. + int[] results = checkPadding(buf, offset + newLen, + padLen + 1, (byte)(padLen & 0xFF)); + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (results[0] != 0) { // padding data has invalid bytes + throw new BadPaddingException("Invalid TLS padding data"); + } + } else { // SSLv3 + // SSLv3 requires 0 <= length byte < block size + // some implementations do 1 <= length byte <= block size, + // so accept that as well + // v3 does not require any particular value for the other bytes + if (padLen > blockSize) { + throw new BadPaddingException("Invalid SSLv3 padding"); + } + } + return newLen; + } + + /* + * Position/limit is equal the removed padding. + */ + private static int removePadding(ByteBuffer bb, + int tagLen, int blockSize, + ProtocolVersion protocolVersion) throws BadPaddingException { + + int len = bb.remaining(); + int offset = bb.position(); + + // last byte is length byte (i.e. actual padding length - 1) + int padOffset = offset + len - 1; + int padLen = bb.get(padOffset) & 0xFF; + + int newLen = len - (padLen + 1); + if ((newLen - tagLen) < 0) { + // If the buffer is not long enough to contain the padding plus + // a MAC tag, do a dummy constant-time padding check. + // + // Note that it is a dummy check, so we won't care about what is + // the actual padding data. + checkPadding(bb.duplicate(), (byte)(padLen & 0xFF)); + + throw new BadPaddingException("Invalid Padding length: " + padLen); + } + + // The padding data should be filled with the padding length value. + int[] results = checkPadding( + (ByteBuffer)bb.duplicate().position(offset + newLen), + (byte)(padLen & 0xFF)); + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + if (results[0] != 0) { // padding data has invalid bytes + throw new BadPaddingException("Invalid TLS padding data"); + } + } else { // SSLv3 + // SSLv3 requires 0 <= length byte < block size + // some implementations do 1 <= length byte <= block size, + // so accept that as well + // v3 does not require any particular value for the other bytes + if (padLen > blockSize) { + throw new BadPaddingException("Invalid SSLv3 padding"); + } + } + + /* + * Reset buffer limit to remove padding. + */ + bb.position(offset + newLen); + bb.limit(offset + newLen); + + return newLen; + } + + /* + * Dispose of any intermediate state in the underlying cipher. + * For PKCS11 ciphers, this will release any attached sessions, and + * thus make finalization faster. + */ + void dispose() { + try { + if (cipher != null) { + // ignore return value. + cipher.doFinal(); + } + } catch (Exception e) { + // swallow all types of exceptions. + } + } + + /* + * Does the cipher use CBC mode? + * + * @return true if the cipher use CBC mode, false otherwise. + */ + boolean isCBCMode() { + return cipherType == BLOCK_CIPHER; + } + + /* + * Does the cipher use AEAD mode? + * + * @return true if the cipher use AEAD mode, false otherwise. + */ + boolean isAEADMode() { + return cipherType == AEAD_CIPHER; + } + + /* + * Is the cipher null? + * + * @return true if the cipher is null, false otherwise. + */ + boolean isNullCipher() { + return cipher == null; + } + + /* + * Gets the explicit nonce/IV size of the cipher. + * + * The returned value is the SecurityParameters.record_iv_length in + * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the + * size of explicit nonce for AEAD mode. + * + * @return the explicit nonce size of the cipher. + */ + int getExplicitNonceSize() { + switch (cipherType) { + case BLOCK_CIPHER: + // For block ciphers, the explicit IV length is of length + // SecurityParameters.record_iv_length, which is equal to + // the SecurityParameters.block_size. + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + return cipher.getBlockSize(); + } + break; + case AEAD_CIPHER: + return recordIvSize; + // It is also the length of sequence number, which is + // used as the nonce_explicit for AEAD cipher suites. + } + + return 0; + } + + /* + * Applies the explicit nonce/IV to this cipher. This method is used to + * decrypt an SSL/TLS input record. + * + * The returned value is the SecurityParameters.record_iv_length in + * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the + * size of explicit nonce for AEAD mode. + * + * @param authenticator the authenticator to get the additional + * authentication data + * @param contentType the content type of the input record + * @param bb the byte buffer to get the explicit nonce from + * + * @return the explicit nonce size of the cipher. + */ + int applyExplicitNonce(Authenticator authenticator, byte contentType, + ByteBuffer bb) throws BadPaddingException { + switch (cipherType) { + case BLOCK_CIPHER: + // sanity check length of the ciphertext + int tagLen = (authenticator instanceof MAC) ? + ((MAC)authenticator).MAClen() : 0; + if (tagLen != 0) { + if (!sanityCheck(tagLen, bb.remaining())) { + throw new BadPaddingException( + "ciphertext sanity check failed"); + } + } + + // For block ciphers, the explicit IV length is of length + // SecurityParameters.record_iv_length, which is equal to + // the SecurityParameters.block_size. + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + return cipher.getBlockSize(); + } + break; + case AEAD_CIPHER: + if (bb.remaining() < (recordIvSize + tagSize)) { + throw new BadPaddingException( + "invalid AEAD cipher fragment"); + } + + // initialize the AEAD cipher for the unique IV + byte[] iv = Arrays.copyOf(fixedIv, + fixedIv.length + recordIvSize); + bb.get(iv, fixedIv.length, recordIvSize); + bb.position(bb.position() - recordIvSize); + GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); + try { + cipher.init(mode, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // update the additional authentication data + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, bb.remaining() - recordIvSize - tagSize); + cipher.updateAAD(aad); + + return recordIvSize; + // It is also the length of sequence number, which is + // used as the nonce_explicit for AEAD cipher suites. + } + + return 0; + } + + /* + * Applies the explicit nonce/IV to this cipher. This method is used to + * decrypt an SSL/TLS input record. + * + * The returned value is the SecurityParameters.record_iv_length in + * RFC 4346/5246. It is the size of explicit IV for CBC mode, and the + * size of explicit nonce for AEAD mode. + * + * @param authenticator the authenticator to get the additional + * authentication data + * @param contentType the content type of the input record + * @param buf the byte array to get the explicit nonce from + * @param offset the offset of the byte buffer + * @param cipheredLength the ciphered fragment length of the output + * record, it is the TLSCiphertext.length in RFC 4346/5246. + * + * @return the explicit nonce size of the cipher. + */ + int applyExplicitNonce(Authenticator authenticator, + byte contentType, byte[] buf, int offset, + int cipheredLength) throws BadPaddingException { + + ByteBuffer bb = ByteBuffer.wrap(buf, offset, cipheredLength); + + return applyExplicitNonce(authenticator, contentType, bb); + } + + /* + * Creates the explicit nonce/IV to this cipher. This method is used to + * encrypt an SSL/TLS output record. + * + * The size of the returned array is the SecurityParameters.record_iv_length + * in RFC 4346/5246. It is the size of explicit IV for CBC mode, and the + * size of explicit nonce for AEAD mode. + * + * @param authenticator the authenticator to get the additional + * authentication data + * @param contentType the content type of the input record + * @param fragmentLength the fragment length of the output record, it is + * the TLSCompressed.length in RFC 4346/5246. + * + * @return the explicit nonce of the cipher. + */ + byte[] createExplicitNonce(Authenticator authenticator, + byte contentType, int fragmentLength) { + + byte[] nonce = new byte[0]; + switch (cipherType) { + case BLOCK_CIPHER: + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + // For block ciphers, the explicit IV length is of length + // SecurityParameters.record_iv_length, which is equal to + // the SecurityParameters.block_size. + // + // Generate a random number as the explicit IV parameter. + nonce = new byte[cipher.getBlockSize()]; + random.nextBytes(nonce); + } + break; + case AEAD_CIPHER: + // To be unique and aware of overflow-wrap, sequence number + // is used as the nonce_explicit of AEAD cipher suites. + nonce = authenticator.sequenceNumber(); + + // initialize the AEAD cipher for the unique IV + byte[] iv = Arrays.copyOf(fixedIv, + fixedIv.length + nonce.length); + System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); + GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); + try { + cipher.init(mode, key, spec, random); + } catch (InvalidKeyException | + InvalidAlgorithmParameterException ikae) { + // unlikely to happen + throw new RuntimeException( + "invalid key or spec in GCM mode", ikae); + } + + // update the additional authentication data + byte[] aad = authenticator.acquireAuthenticationBytes( + contentType, fragmentLength); + cipher.updateAAD(aad); + break; + } + + return nonce; + } + + /* + * Is this cipher available? + * + * This method can only be called by CipherSuite.BulkCipher.isAvailable() + * to test the availability of a cipher suites. Please DON'T use it in + * other places, otherwise, the behavior may be unexpected because we may + * initialize AEAD cipher improperly in the method. + */ + Boolean isAvailable() { + // We won't know whether a cipher for a particular key size is + // available until the cipher is successfully initialized. + // + // We do not initialize AEAD cipher in the constructor. Need to + // initialize the cipher to ensure that the AEAD mode for a + // particular key size is supported. + if (cipherType == AEAD_CIPHER) { + try { + Authenticator authenticator = + new Authenticator(protocolVersion); + byte[] nonce = authenticator.sequenceNumber(); + byte[] iv = Arrays.copyOf(fixedIv, + fixedIv.length + nonce.length); + System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length); + GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv); + + cipher.init(mode, key, spec, random); + } catch (Exception e) { + return Boolean.FALSE; + } + } // Otherwise, we have initialized the cipher in the constructor. + + return Boolean.TRUE; + } + + /** + * Sanity check the length of a fragment before decryption. + * + * In CBC mode, check that the fragment length is one or multiple times + * of the block size of the cipher suite, and is at least one (one is the + * smallest size of padding in CBC mode) bigger than the tag size of the + * MAC algorithm except the explicit IV size for TLS 1.1 or later. + * + * In non-CBC mode, check that the fragment length is not less than the + * tag size of the MAC algorithm. + * + * @return true if the length of a fragment matches above requirements + */ + private boolean sanityCheck(int tagLen, int fragmentLen) { + if (!isCBCMode()) { + return fragmentLen >= tagLen; + } + + int blockSize = cipher.getBlockSize(); + if ((fragmentLen % blockSize) == 0) { + int minimal = tagLen + 1; + minimal = (minimal >= blockSize) ? minimal : blockSize; + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + minimal += blockSize; // plus the size of the explicit IV + } + + return (fragmentLen >= minimal); + } + + return false; + } + +} diff --git a/src/sun/security/ssl/CipherSuite.java b/src/sun/security/ssl/CipherSuite.java new file mode 100644 index 00000000..b84ff929 --- /dev/null +++ b/src/sun/security/ssl/CipherSuite.java @@ -0,0 +1,1665 @@ +/* + * Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.util.*; + +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.security.KeyManagementException; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import static sun.security.ssl.CipherSuite.KeyExchange.*; +import static sun.security.ssl.CipherSuite.PRF.*; +import static sun.security.ssl.CipherSuite.CipherType.*; +import static sun.security.ssl.JsseJce.CIPHER_3DES; +import static sun.security.ssl.JsseJce.CIPHER_AES; +import static sun.security.ssl.JsseJce.CIPHER_AES_GCM; +import static sun.security.ssl.JsseJce.CIPHER_DES; +import static sun.security.ssl.JsseJce.CIPHER_RC4; + +/** + * An SSL/TLS CipherSuite. Constants for the standard key exchange, cipher, + * and mac algorithms are also defined in this class. + * + * The CipherSuite class and the inner classes defined in this file roughly + * follow the type safe enum pattern described in Effective Java. This means: + * + * . instances are immutable, classes are final + * + * . there is a unique instance of every value, i.e. there are never two + * instances representing the same CipherSuite, etc. This means equality + * tests can be performed using == instead of equals() (although that works + * as well). [A minor exception are *unsupported* CipherSuites read from a + * handshake message, but this is usually irrelevant] + * + * . instances are obtained using the static valueOf() factory methods. + * + * . properties are defined as final variables and made available as + * package private variables without method accessors + * + * . if the member variable allowed is false, the given algorithm is either + * unavailable or disabled at compile time + * + */ +final class CipherSuite implements Comparable { + + // minimum priority for supported CipherSuites + final static int SUPPORTED_SUITES_PRIORITY = 1; + + // minimum priority for default enabled CipherSuites + final static int DEFAULT_SUITES_PRIORITY = 300; + + // Flag indicating if CipherSuite availability can change dynamically. + // This is the case when we rely on a JCE cipher implementation that + // may not be available in the installed JCE providers. + // It is true because we might not have an ECC implementation. + final static boolean DYNAMIC_AVAILABILITY = true; + + private final static boolean ALLOW_ECC = Debug.getBooleanProperty + ("com.sun.net.ssl.enableECC", true); + + // Map Integer(id) -> CipherSuite + // contains all known CipherSuites + private final static Map idMap; + + // Map String(name) -> CipherSuite + // contains only supported CipherSuites (i.e. allowed == true) + private final static Map nameMap; + + // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5 + // we use TLS_* only for new CipherSuites, still SSL_* for old ones + final String name; + + // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5 + final int id; + + // priority for the internal default preference order. the higher the + // better. Each supported CipherSuite *must* have a unique priority. + // Ciphersuites with priority >= DEFAULT_SUITES_PRIORITY are enabled + // by default + final int priority; + + // key exchange, bulk cipher, mac and prf algorithms. See those + // classes below. + final KeyExchange keyExchange; + final BulkCipher cipher; + final MacAlg macAlg; + final PRF prfAlg; + + // whether a CipherSuite qualifies as exportable under 512/40 bit rules. + // TLS 1.1+ (RFC 4346) must not negotiate to these suites. + final boolean exportable; + + // true iff implemented and enabled at compile time + final boolean allowed; + + // obsoleted since protocol version + final int obsoleted; + + // supported since protocol version + final int supported; + + /** + * Constructor for implemented CipherSuites. + */ + private CipherSuite(String name, int id, int priority, + KeyExchange keyExchange, BulkCipher cipher, + boolean allowed, int obsoleted, int supported, PRF prfAlg) { + this.name = name; + this.id = id; + this.priority = priority; + this.keyExchange = keyExchange; + this.cipher = cipher; + this.exportable = cipher.exportable; + if (cipher.cipherType == CipherType.AEAD_CIPHER) { + macAlg = M_NULL; + } else if (name.endsWith("_MD5")) { + macAlg = M_MD5; + } else if (name.endsWith("_SHA")) { + macAlg = M_SHA; + } else if (name.endsWith("_SHA256")) { + macAlg = M_SHA256; + } else if (name.endsWith("_SHA384")) { + macAlg = M_SHA384; + } else if (name.endsWith("_NULL")) { + macAlg = M_NULL; + } else if (name.endsWith("_SCSV")) { + macAlg = M_NULL; + } else { + throw new IllegalArgumentException + ("Unknown MAC algorithm for ciphersuite " + name); + } + + allowed &= keyExchange.allowed; + allowed &= cipher.allowed; + this.allowed = allowed; + this.obsoleted = obsoleted; + this.supported = supported; + this.prfAlg = prfAlg; + } + + /** + * Constructor for unimplemented CipherSuites. + */ + private CipherSuite(String name, int id) { + this.name = name; + this.id = id; + this.allowed = false; + + this.priority = 0; + this.keyExchange = null; + this.cipher = null; + this.macAlg = null; + this.exportable = false; + this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE; + this.supported = ProtocolVersion.LIMIT_MIN_VALUE; + this.prfAlg = P_NONE; + } + + /** + * Return whether this CipherSuite is available for use. A + * CipherSuite may be unavailable even if it is supported + * (i.e. allowed == true) if the required JCE cipher is not installed. + * In some configuration, this situation may change over time, call + * CipherSuiteList.clearAvailableCache() before this method to obtain + * the most current status. + */ + boolean isAvailable() { + return allowed && keyExchange.isAvailable() && cipher.isAvailable(); + } + + boolean isNegotiable() { + return this != C_SCSV && isAvailable(); + } + + /** + * Compares CipherSuites based on their priority. Has the effect of + * sorting CipherSuites when put in a sorted collection, which is + * used by CipherSuiteList. Follows standard Comparable contract. + * + * Note that for unsupported CipherSuites parsed from a handshake + * message we violate the equals() contract. + */ + @Override + public int compareTo(CipherSuite o) { + return o.priority - priority; + } + + /** + * Returns this.name. + */ + @Override + public String toString() { + return name; + } + + /** + * Return a CipherSuite for the given name. The returned CipherSuite + * is supported by this implementation but may not actually be + * currently useable. See isAvailable(). + * + * @exception IllegalArgumentException if the CipherSuite is unknown or + * unsupported. + */ + static CipherSuite valueOf(String s) { + if (s == null) { + throw new IllegalArgumentException("Name must not be null"); + } + + CipherSuite c = nameMap.get(s); + if ((c == null) || (c.allowed == false)) { + throw new IllegalArgumentException("Unsupported ciphersuite " + s); + } + + return c; + } + + /** + * Return a CipherSuite with the given ID. A temporary object is + * constructed if the ID is unknown. Use isAvailable() to verify that + * the CipherSuite can actually be used. + */ + static CipherSuite valueOf(int id1, int id2) { + id1 &= 0xff; + id2 &= 0xff; + int id = (id1 << 8) | id2; + CipherSuite c = idMap.get(id); + if (c == null) { + String h1 = Integer.toString(id1, 16); + String h2 = Integer.toString(id2, 16); + c = new CipherSuite("Unknown 0x" + h1 + ":0x" + h2, id); + } + return c; + } + + // for use by CipherSuiteList only + static Collection allowedCipherSuites() { + return nameMap.values(); + } + + /* + * Use this method when all of the values need to be specified. + * This is primarily used when defining a new ciphersuite for + * TLS 1.2+ that doesn't use the "default" PRF. + */ + private static void add(String name, int id, int priority, + KeyExchange keyExchange, BulkCipher cipher, + boolean allowed, int obsoleted, int supported, PRF prf) { + + CipherSuite c = new CipherSuite(name, id, priority, keyExchange, + cipher, allowed, obsoleted, supported, prf); + if (idMap.put(id, c) != null) { + throw new RuntimeException("Duplicate ciphersuite definition: " + + id + ", " + name); + } + if (c.allowed) { + if (nameMap.put(name, c) != null) { + throw new RuntimeException("Duplicate ciphersuite definition: " + + id + ", " + name); + } + } + } + + /* + * Use this method when there is no lower protocol limit where this + * suite can be used, and the PRF is P_SHA256. That is, the + * existing ciphersuites. From RFC 5246: + * + * All cipher suites in this document use P_SHA256. + */ + private static void add(String name, int id, int priority, + KeyExchange keyExchange, BulkCipher cipher, + boolean allowed, int obsoleted) { + // If this is an obsoleted suite, then don't let the TLS 1.2 + // protocol have a valid PRF value. + PRF prf = P_SHA256; + if (obsoleted < ProtocolVersion.TLS12.v) { + prf = P_NONE; + } + + add(name, id, priority, keyExchange, cipher, allowed, obsoleted, + ProtocolVersion.LIMIT_MIN_VALUE, prf); + } + + /* + * Use this method when there is no upper protocol limit. That is, + * suites which have not been obsoleted. + */ + private static void add(String name, int id, int priority, + KeyExchange keyExchange, BulkCipher cipher, boolean allowed) { + add(name, id, priority, keyExchange, + cipher, allowed, ProtocolVersion.LIMIT_MAX_VALUE); + } + + /* + * Use this method to define an unimplemented suite. This provides + * a number<->name mapping that can be used for debugging. + */ + private static void add(String name, int id) { + CipherSuite c = new CipherSuite(name, id); + if (idMap.put(id, c) != null) { + throw new RuntimeException("Duplicate ciphersuite definition: " + + id + ", " + name); + } + } + + /** + * An SSL/TLS key exchange algorithm. + */ + static enum KeyExchange { + + // key exchange algorithms + K_NULL ("NULL", false), + K_RSA ("RSA", true), + K_RSA_EXPORT ("RSA_EXPORT", true), + K_DH_RSA ("DH_RSA", false), + K_DH_DSS ("DH_DSS", false), + K_DHE_DSS ("DHE_DSS", true), + K_DHE_RSA ("DHE_RSA", true), + K_DH_ANON ("DH_anon", true), + + K_ECDH_ECDSA ("ECDH_ECDSA", ALLOW_ECC), + K_ECDH_RSA ("ECDH_RSA", ALLOW_ECC), + K_ECDHE_ECDSA("ECDHE_ECDSA", ALLOW_ECC), + K_ECDHE_RSA ("ECDHE_RSA", ALLOW_ECC), + K_ECDH_ANON ("ECDH_anon", ALLOW_ECC), + + // Kerberos cipher suites + K_KRB5 ("KRB5", true), + K_KRB5_EXPORT("KRB5_EXPORT", true), + + // renegotiation protection request signaling cipher suite + K_SCSV ("SCSV", true); + + // name of the key exchange algorithm, e.g. DHE_DSS + final String name; + final boolean allowed; + private final boolean alwaysAvailable; + + KeyExchange(String name, boolean allowed) { + this.name = name; + this.allowed = allowed; + this.alwaysAvailable = allowed && + (!name.startsWith("EC")) && (!name.startsWith("KRB")); + } + + boolean isAvailable() { + if (alwaysAvailable) { + return true; + } + + if (name.startsWith("EC")) { + return (allowed && JsseJce.isEcAvailable()); + } else if (name.startsWith("KRB")) { + return (allowed && JsseJce.isKerberosAvailable()); + } else { + return allowed; + } + } + + @Override + public String toString() { + return name; + } + } + + static enum CipherType { + STREAM_CIPHER, // null or stream cipher + BLOCK_CIPHER, // block cipher in CBC mode + AEAD_CIPHER // AEAD cipher + } + + /** + * An SSL/TLS bulk cipher algorithm. One instance per combination of + * cipher and key length. + * + * Also contains a factory method to obtain in initialized CipherBox + * for this algorithm. + */ + final static class BulkCipher { + + // Map BulkCipher -> Boolean(available) + private final static Map availableCache = + new HashMap<>(8); + + // descriptive name including key size, e.g. AES/128 + final String description; + + // JCE cipher transformation string, e.g. AES/CBC/NoPadding + final String transformation; + + // algorithm name, e.g. AES + final String algorithm; + + // supported and compile time enabled. Also see isAvailable() + final boolean allowed; + + // number of bytes of entropy in the key + final int keySize; + + // length of the actual cipher key in bytes. + // for non-exportable ciphers, this is the same as keySize + final int expandedKeySize; + + // size of the IV + final int ivSize; + + // size of fixed IV + // + // record_iv_length = ivSize - fixedIvSize + final int fixedIvSize; + + // exportable under 512/40 bit rules + final boolean exportable; + + // Is the cipher algorithm of Cipher Block Chaining (CBC) mode? + final CipherType cipherType; + + // size of the authentication tag, only applicable to cipher suites in + // Galois Counter Mode (GCM) + // + // As far as we know, all supported GCM cipher suites use 128-bits + // authentication tags. + final int tagSize = 16; + + // The secure random used to detect the cipher availability. + private final static SecureRandom secureRandom; + + static { + try { + secureRandom = JsseJce.getSecureRandom(); + } catch (KeyManagementException kme) { + throw new RuntimeException(kme); + } + } + + BulkCipher(String transformation, CipherType cipherType, int keySize, + int expandedKeySize, int ivSize, + int fixedIvSize, boolean allowed) { + + this.transformation = transformation; + String[] splits = transformation.split("/"); + this.algorithm = splits[0]; + this.cipherType = cipherType; + this.description = this.algorithm + "/" + (keySize << 3); + this.keySize = keySize; + this.ivSize = ivSize; + this.fixedIvSize = fixedIvSize; + this.allowed = allowed; + + this.expandedKeySize = expandedKeySize; + this.exportable = true; + } + + BulkCipher(String transformation, CipherType cipherType, int keySize, + int ivSize, int fixedIvSize, boolean allowed) { + this.transformation = transformation; + String[] splits = transformation.split("/"); + this.algorithm = splits[0]; + this.cipherType = cipherType; + this.description = this.algorithm + "/" + (keySize << 3); + this.keySize = keySize; + this.ivSize = ivSize; + this.fixedIvSize = fixedIvSize; + this.allowed = allowed; + + this.expandedKeySize = keySize; + this.exportable = false; + } + + /** + * Return an initialized CipherBox for this BulkCipher. + * IV must be null for stream ciphers. + * + * @exception NoSuchAlgorithmException if anything goes wrong + */ + CipherBox newCipher(ProtocolVersion version, SecretKey key, + IvParameterSpec iv, SecureRandom random, + boolean encrypt) throws NoSuchAlgorithmException { + return CipherBox.newCipherBox(version, this, + key, iv, random, encrypt); + } + + /** + * Test if this bulk cipher is available. For use by CipherSuite. + * + * Currently all supported ciphers except AES are always available + * via the JSSE internal implementations. We also assume AES/128 of + * CBC mode is always available since it is shipped with the SunJCE + * provider. However, AES/256 is unavailable when the default JCE + * policy jurisdiction files are installed because of key length + * restrictions, and AEAD is unavailable when the underlying providers + * do not support AEAD/GCM mode. + */ + boolean isAvailable() { + if (allowed == false) { + return false; + } + + if ((this == B_AES_256) || + (this.cipherType == CipherType.AEAD_CIPHER)) { + return isAvailable(this); + } + + // always available + return true; + } + + // for use by CipherSuiteList.clearAvailableCache(); + static synchronized void clearAvailableCache() { + if (DYNAMIC_AVAILABILITY) { + availableCache.clear(); + } + } + + private static synchronized boolean isAvailable(BulkCipher cipher) { + Boolean b = availableCache.get(cipher); + if (b == null) { + int keySizeInBits = cipher.keySize * 8; + if (keySizeInBits > 128) { // need the JCE unlimited + // strength jurisdiction policy + try { + if (Cipher.getMaxAllowedKeyLength( + cipher.transformation) < keySizeInBits) { + b = Boolean.FALSE; + } + } catch (Exception e) { + b = Boolean.FALSE; + } + } + + if (b == null) { + b = Boolean.FALSE; // may be reset to TRUE if + // the cipher is available + CipherBox temporary = null; + try { + SecretKey key = new SecretKeySpec( + new byte[cipher.expandedKeySize], + cipher.algorithm); + IvParameterSpec iv; + if (cipher.cipherType == CipherType.AEAD_CIPHER) { + iv = new IvParameterSpec( + new byte[cipher.fixedIvSize]); + } else { + iv = new IvParameterSpec(new byte[cipher.ivSize]); + } + temporary = cipher.newCipher( + ProtocolVersion.DEFAULT, + key, iv, secureRandom, true); + b = temporary.isAvailable(); + } catch (NoSuchAlgorithmException e) { + // not available + } finally { + if (temporary != null) { + temporary.dispose(); + } + } + } + + availableCache.put(cipher, b); + } + + return b.booleanValue(); + } + + @Override + public String toString() { + return description; + } + } + + /** + * An SSL/TLS key MAC algorithm. + * + * Also contains a factory method to obtain an initialized MAC + * for this algorithm. + */ + final static class MacAlg { + + // descriptive name, e.g. MD5 + final String name; + + // size of the MAC value (and MAC key) in bytes + final int size; + + // block size of the underlying hash algorithm + final int hashBlockSize; + + // minimal padding size of the underlying hash algorithm + final int minimalPaddingSize; + + MacAlg(String name, int size, + int hashBlockSize, int minimalPaddingSize) { + this.name = name; + this.size = size; + this.hashBlockSize = hashBlockSize; + this.minimalPaddingSize = minimalPaddingSize; + } + + /** + * Return an initialized MAC for this MacAlg. ProtocolVersion + * must either be SSL30 (SSLv3 custom MAC) or TLS10 (std. HMAC). + * + * @exception NoSuchAlgorithmException if anything goes wrong + */ + MAC newMac(ProtocolVersion protocolVersion, SecretKey secret) + throws NoSuchAlgorithmException, InvalidKeyException { + return new MAC(this, protocolVersion, secret); + } + + @Override + public String toString() { + return name; + } + } + + // export strength ciphers + final static BulkCipher B_NULL = + new BulkCipher("NULL", STREAM_CIPHER, 0, 0, 0, 0, true); + final static BulkCipher B_RC4_40 = + new BulkCipher(CIPHER_RC4, STREAM_CIPHER, 5, 16, 0, 0, true); + final static BulkCipher B_RC2_40 = + new BulkCipher("RC2", BLOCK_CIPHER, 5, 16, 8, 0, false); + final static BulkCipher B_DES_40 = + new BulkCipher(CIPHER_DES, BLOCK_CIPHER, 5, 8, 8, 0, true); + + // domestic strength ciphers + final static BulkCipher B_RC4_128 = + new BulkCipher(CIPHER_RC4, STREAM_CIPHER, 16, 0, 0, true); + final static BulkCipher B_DES = + new BulkCipher(CIPHER_DES, BLOCK_CIPHER, 8, 8, 0, true); + final static BulkCipher B_3DES = + new BulkCipher(CIPHER_3DES, BLOCK_CIPHER, 24, 8, 0, true); + final static BulkCipher B_IDEA = + new BulkCipher("IDEA", BLOCK_CIPHER, 16, 8, 0, false); + final static BulkCipher B_AES_128 = + new BulkCipher(CIPHER_AES, BLOCK_CIPHER, 16, 16, 0, true); + final static BulkCipher B_AES_256 = + new BulkCipher(CIPHER_AES, BLOCK_CIPHER, 32, 16, 0, true); + final static BulkCipher B_AES_128_GCM = + new BulkCipher(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true); + final static BulkCipher B_AES_256_GCM = + new BulkCipher(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true); + + // MACs + final static MacAlg M_NULL = new MacAlg("NULL", 0, 0, 0); + final static MacAlg M_MD5 = new MacAlg("MD5", 16, 64, 9); + final static MacAlg M_SHA = new MacAlg("SHA", 20, 64, 9); + final static MacAlg M_SHA256 = new MacAlg("SHA256", 32, 64, 9); + final static MacAlg M_SHA384 = new MacAlg("SHA384", 48, 128, 17); + + /** + * PRFs (PseudoRandom Function) from TLS specifications. + * + * TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for generating + * the necessary material. + * + * In TLS 1.2+, all existing/known CipherSuites use SHA256, however + * new Ciphersuites (e.g. RFC 5288) can define specific PRF hash + * algorithms. + */ + static enum PRF { + + // PRF algorithms + P_NONE( "NONE", 0, 0), + P_SHA256("SHA-256", 32, 64), + P_SHA384("SHA-384", 48, 128), + P_SHA512("SHA-512", 64, 128); // not currently used. + + // PRF characteristics + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; + + PRF(String prfHashAlg, int prfHashLength, int prfBlockSize) { + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; + } + + String getPRFHashAlg() { + return prfHashAlg; + } + + int getPRFHashLength() { + return prfHashLength; + } + + int getPRFBlockSize() { + return prfBlockSize; + } + } + + static { + idMap = new HashMap(); + nameMap = new HashMap(); + + final boolean F = false; + final boolean T = true; + // N: ciphersuites only allowed if we are not in FIPS mode + final boolean N = (SunJSSE.isFIPS() == false); + + /* + * TLS Cipher Suite Registry, as of November 2015. + * + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml + * + * Range Registration Procedures Notes + * 000-191 Standards Action Refers to value of first byte + * 192-254 Specification Required Refers to value of first byte + * 255 Reserved for Private Use Refers to value of first byte + * + * Value Description Reference + * 0x00,0x00 TLS_NULL_WITH_NULL_NULL [RFC5246] + * 0x00,0x01 TLS_RSA_WITH_NULL_MD5 [RFC5246] + * 0x00,0x02 TLS_RSA_WITH_NULL_SHA [RFC5246] + * 0x00,0x03 TLS_RSA_EXPORT_WITH_RC4_40_MD5 [RFC4346] + * 0x00,0x04 TLS_RSA_WITH_RC4_128_MD5 [RFC5246] + * 0x00,0x05 TLS_RSA_WITH_RC4_128_SHA [RFC5246] + * 0x00,0x06 TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 [RFC4346] + * 0x00,0x07 TLS_RSA_WITH_IDEA_CBC_SHA [RFC5469] + * 0x00,0x08 TLS_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x09 TLS_RSA_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x0A TLS_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x0B TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x0C TLS_DH_DSS_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x0D TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x0E TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x0F TLS_DH_RSA_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x10 TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x11 TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x12 TLS_DHE_DSS_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x13 TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x14 TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x15 TLS_DHE_RSA_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x16 TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x17 TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 [RFC4346] + * 0x00,0x18 TLS_DH_anon_WITH_RC4_128_MD5 [RFC5246] + * 0x00,0x19 TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA [RFC4346] + * 0x00,0x1A TLS_DH_anon_WITH_DES_CBC_SHA [RFC5469] + * 0x00,0x1B TLS_DH_anon_WITH_3DES_EDE_CBC_SHA [RFC5246] + * 0x00,0x1C-1D Reserved to avoid conflicts with SSLv3 [RFC5246] + * 0x00,0x1E TLS_KRB5_WITH_DES_CBC_SHA [RFC2712] + * 0x00,0x1F TLS_KRB5_WITH_3DES_EDE_CBC_SHA [RFC2712] + * 0x00,0x20 TLS_KRB5_WITH_RC4_128_SHA [RFC2712] + * 0x00,0x21 TLS_KRB5_WITH_IDEA_CBC_SHA [RFC2712] + * 0x00,0x22 TLS_KRB5_WITH_DES_CBC_MD5 [RFC2712] + * 0x00,0x23 TLS_KRB5_WITH_3DES_EDE_CBC_MD5 [RFC2712] + * 0x00,0x24 TLS_KRB5_WITH_RC4_128_MD5 [RFC2712] + * 0x00,0x25 TLS_KRB5_WITH_IDEA_CBC_MD5 [RFC2712] + * 0x00,0x26 TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA [RFC2712] + * 0x00,0x27 TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA [RFC2712] + * 0x00,0x28 TLS_KRB5_EXPORT_WITH_RC4_40_SHA [RFC2712] + * 0x00,0x29 TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 [RFC2712] + * 0x00,0x2A TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 [RFC2712] + * 0x00,0x2B TLS_KRB5_EXPORT_WITH_RC4_40_MD5 [RFC2712] + * 0x00,0x2C TLS_PSK_WITH_NULL_SHA [RFC4785] + * 0x00,0x2D TLS_DHE_PSK_WITH_NULL_SHA [RFC4785] + * 0x00,0x2E TLS_RSA_PSK_WITH_NULL_SHA [RFC4785] + * 0x00,0x2F TLS_RSA_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x30 TLS_DH_DSS_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x31 TLS_DH_RSA_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x32 TLS_DHE_DSS_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x33 TLS_DHE_RSA_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x34 TLS_DH_anon_WITH_AES_128_CBC_SHA [RFC5246] + * 0x00,0x35 TLS_RSA_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x36 TLS_DH_DSS_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x37 TLS_DH_RSA_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x38 TLS_DHE_DSS_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x39 TLS_DHE_RSA_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x3A TLS_DH_anon_WITH_AES_256_CBC_SHA [RFC5246] + * 0x00,0x3B TLS_RSA_WITH_NULL_SHA256 [RFC5246] + * 0x00,0x3C TLS_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x3D TLS_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x3E TLS_DH_DSS_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x3F TLS_DH_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x40 TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x41 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x42 TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x43 TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x44 TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x45 TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x46 TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA [RFC5932] + * 0x00,0x47-4F Reserved to avoid conflicts with + * deployed implementations [Pasi_Eronen] + * 0x00,0x50-58 Reserved to avoid conflicts [Pasi Eronen] + * 0x00,0x59-5C Reserved to avoid conflicts with + * deployed implementations [Pasi_Eronen] + * 0x00,0x5D-5F Unassigned + * 0x00,0x60-66 Reserved to avoid conflicts with widely + * deployed implementations [Pasi_Eronen] + * 0x00,0x67 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x68 TLS_DH_DSS_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x69 TLS_DH_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x6A TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x6B TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x6C TLS_DH_anon_WITH_AES_128_CBC_SHA256 [RFC5246] + * 0x00,0x6D TLS_DH_anon_WITH_AES_256_CBC_SHA256 [RFC5246] + * 0x00,0x6E-83 Unassigned + * 0x00,0x84 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x85 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x86 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x87 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x88 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x89 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA [RFC5932] + * 0x00,0x8A TLS_PSK_WITH_RC4_128_SHA [RFC4279] + * 0x00,0x8B TLS_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] + * 0x00,0x8C TLS_PSK_WITH_AES_128_CBC_SHA [RFC4279] + * 0x00,0x8D TLS_PSK_WITH_AES_256_CBC_SHA [RFC4279] + * 0x00,0x8E TLS_DHE_PSK_WITH_RC4_128_SHA [RFC4279] + * 0x00,0x8F TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] + * 0x00,0x90 TLS_DHE_PSK_WITH_AES_128_CBC_SHA [RFC4279] + * 0x00,0x91 TLS_DHE_PSK_WITH_AES_256_CBC_SHA [RFC4279] + * 0x00,0x92 TLS_RSA_PSK_WITH_RC4_128_SHA [RFC4279] + * 0x00,0x93 TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA [RFC4279] + * 0x00,0x94 TLS_RSA_PSK_WITH_AES_128_CBC_SHA [RFC4279] + * 0x00,0x95 TLS_RSA_PSK_WITH_AES_256_CBC_SHA [RFC4279] + * 0x00,0x96 TLS_RSA_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x97 TLS_DH_DSS_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x98 TLS_DH_RSA_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x99 TLS_DHE_DSS_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x9A TLS_DHE_RSA_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x9B TLS_DH_anon_WITH_SEED_CBC_SHA [RFC4162] + * 0x00,0x9C TLS_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0x9D TLS_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0x9E TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0x9F TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0xA0 TLS_DH_RSA_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0xA1 TLS_DH_RSA_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0xA2 TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0xA3 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0xA4 TLS_DH_DSS_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0xA5 TLS_DH_DSS_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0xA6 TLS_DH_anon_WITH_AES_128_GCM_SHA256 [RFC5288] + * 0x00,0xA7 TLS_DH_anon_WITH_AES_256_GCM_SHA384 [RFC5288] + * 0x00,0xA8 TLS_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] + * 0x00,0xA9 TLS_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] + * 0x00,0xAA TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] + * 0x00,0xAB TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] + * 0x00,0xAC TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 [RFC5487] + * 0x00,0xAD TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 [RFC5487] + * 0x00,0xAE TLS_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] + * 0x00,0xAF TLS_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] + * 0x00,0xB0 TLS_PSK_WITH_NULL_SHA256 [RFC5487] + * 0x00,0xB1 TLS_PSK_WITH_NULL_SHA384 [RFC5487] + * 0x00,0xB2 TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] + * 0x00,0xB3 TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] + * 0x00,0xB4 TLS_DHE_PSK_WITH_NULL_SHA256 [RFC5487] + * 0x00,0xB5 TLS_DHE_PSK_WITH_NULL_SHA384 [RFC5487] + * 0x00,0xB6 TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 [RFC5487] + * 0x00,0xB7 TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 [RFC5487] + * 0x00,0xB8 TLS_RSA_PSK_WITH_NULL_SHA256 [RFC5487] + * 0x00,0xB9 TLS_RSA_PSK_WITH_NULL_SHA384 [RFC5487] + * 0x00,0xBA TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xBB TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xBC TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xBD TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xBE TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xBF TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 [RFC5932] + * 0x00,0xC0 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC1 TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC2 TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC3 TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC4 TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC5 TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 [RFC5932] + * 0x00,0xC6-FE Unassigned + * 0x00,0xFF TLS_EMPTY_RENEGOTIATION_INFO_SCSV [RFC5746] + * 0x01-55,* Unassigned + * 0x56,0x00 TLS_FALLBACK_SCSV [RFC7507] + * 0x56,0x01-0xC0,0x00 Unassigned + * 0xC0,0x01 TLS_ECDH_ECDSA_WITH_NULL_SHA [RFC4492] + * 0xC0,0x02 TLS_ECDH_ECDSA_WITH_RC4_128_SHA [RFC4492] + * 0xC0,0x03 TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA [RFC4492] + * 0xC0,0x04 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA [RFC4492] + * 0xC0,0x05 TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA [RFC4492] + * 0xC0,0x06 TLS_ECDHE_ECDSA_WITH_NULL_SHA [RFC4492] + * 0xC0,0x07 TLS_ECDHE_ECDSA_WITH_RC4_128_SHA [RFC4492] + * 0xC0,0x08 TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA [RFC4492] + * 0xC0,0x09 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA [RFC4492] + * 0xC0,0x0A TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA [RFC4492] + * 0xC0,0x0B TLS_ECDH_RSA_WITH_NULL_SHA [RFC4492] + * 0xC0,0x0C TLS_ECDH_RSA_WITH_RC4_128_SHA [RFC4492] + * 0xC0,0x0D TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA [RFC4492] + * 0xC0,0x0E TLS_ECDH_RSA_WITH_AES_128_CBC_SHA [RFC4492] + * 0xC0,0x0F TLS_ECDH_RSA_WITH_AES_256_CBC_SHA [RFC4492] + * 0xC0,0x10 TLS_ECDHE_RSA_WITH_NULL_SHA [RFC4492] + * 0xC0,0x11 TLS_ECDHE_RSA_WITH_RC4_128_SHA [RFC4492] + * 0xC0,0x12 TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA [RFC4492] + * 0xC0,0x13 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA [RFC4492] + * 0xC0,0x14 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA [RFC4492] + * 0xC0,0x15 TLS_ECDH_anon_WITH_NULL_SHA [RFC4492] + * 0xC0,0x16 TLS_ECDH_anon_WITH_RC4_128_SHA [RFC4492] + * 0xC0,0x17 TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA [RFC4492] + * 0xC0,0x18 TLS_ECDH_anon_WITH_AES_128_CBC_SHA [RFC4492] + * 0xC0,0x19 TLS_ECDH_anon_WITH_AES_256_CBC_SHA [RFC4492] + * 0xC0,0x1A TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA [RFC5054] + * 0xC0,0x1B TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA [RFC5054] + * 0xC0,0x1C TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA [RFC5054] + * 0xC0,0x1D TLS_SRP_SHA_WITH_AES_128_CBC_SHA [RFC5054] + * 0xC0,0x1E TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA [RFC5054] + * 0xC0,0x1F TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA [RFC5054] + * 0xC0,0x20 TLS_SRP_SHA_WITH_AES_256_CBC_SHA [RFC5054] + * 0xC0,0x21 TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA [RFC5054] + * 0xC0,0x22 TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA [RFC5054] + * 0xC0,0x23 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 [RFC5289] + * 0xC0,0x24 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 [RFC5289] + * 0xC0,0x25 TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 [RFC5289] + * 0xC0,0x26 TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 [RFC5289] + * 0xC0,0x27 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 [RFC5289] + * 0xC0,0x28 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 [RFC5289] + * 0xC0,0x29 TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 [RFC5289] + * 0xC0,0x2A TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 [RFC5289] + * 0xC0,0x2B TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 [RFC5289] + * 0xC0,0x2C TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 [RFC5289] + * 0xC0,0x2D TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 [RFC5289] + * 0xC0,0x2E TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 [RFC5289] + * 0xC0,0x2F TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [RFC5289] + * 0xC0,0x30 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 [RFC5289] + * 0xC0,0x31 TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 [RFC5289] + * 0xC0,0x32 TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 [RFC5289] + * 0xC0,0x33 TLS_ECDHE_PSK_WITH_RC4_128_SHA [RFC5489] + * 0xC0,0x34 TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA [RFC5489] + * 0xC0,0x35 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA [RFC5489] + * 0xC0,0x36 TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA [RFC5489] + * 0xC0,0x37 TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 [RFC5489] + * 0xC0,0x38 TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 [RFC5489] + * 0xC0,0x39 TLS_ECDHE_PSK_WITH_NULL_SHA [RFC5489] + * 0xC0,0x3A TLS_ECDHE_PSK_WITH_NULL_SHA256 [RFC5489] + * 0xC0,0x3B TLS_ECDHE_PSK_WITH_NULL_SHA384 [RFC5489] + * 0xC0,0x3C TLS_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x3D TLS_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x3E TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x3F TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x40 TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x41 TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x42 TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x43 TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x44 TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x45 TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x46 TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x47 TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x48 TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x49 TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x4A TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x4B TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x4C TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x4D TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x4E TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x4F TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x50 TLS_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x51 TLS_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x52 TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x53 TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x54 TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x55 TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x56 TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x57 TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x58 TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x59 TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x5A TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x5B TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x5C TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x5D TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x5E TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x5F TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x60 TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x61 TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x62 TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x63 TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x64 TLS_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x65 TLS_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x66 TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x67 TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x68 TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x69 TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x6A TLS_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x6B TLS_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x6C TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x6D TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x6E TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 [RFC6209] + * 0xC0,0x6F TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 [RFC6209] + * 0xC0,0x70 TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 [RFC6209] + * 0xC0,0x71 TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 [RFC6209] + * 0xC0,0x72 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x73 TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x74 TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x75 TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x76 TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x77 TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x78 TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x79 TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x7A TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x7B TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x7C TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x7D TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x7E TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x7F TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x80 TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x81 TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x82 TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x83 TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x84 TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x85 TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x86 TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x87 TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x88 TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x89 TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x8A TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x8B TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x8C TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x8D TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x8E TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x8F TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x90 TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x91 TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x92 TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 [RFC6367] + * 0xC0,0x93 TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 [RFC6367] + * 0xC0,0x94 TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x95 TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x96 TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x97 TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x98 TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x99 TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x9A TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 [RFC6367] + * 0xC0,0x9B TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 [RFC6367] + * 0xC0,0x9C TLS_RSA_WITH_AES_128_CCM [RFC6655] + * 0xC0,0x9D TLS_RSA_WITH_AES_256_CCM [RFC6655] + * 0xC0,0x9E TLS_DHE_RSA_WITH_AES_128_CCM [RFC6655] + * 0xC0,0x9F TLS_DHE_RSA_WITH_AES_256_CCM [RFC6655] + * 0xC0,0xA0 TLS_RSA_WITH_AES_128_CCM_8 [RFC6655] + * 0xC0,0xA1 TLS_RSA_WITH_AES_256_CCM_8 [RFC6655] + * 0xC0,0xA2 TLS_DHE_RSA_WITH_AES_128_CCM_8 [RFC6655] + * 0xC0,0xA3 TLS_DHE_RSA_WITH_AES_256_CCM_8 [RFC6655] + * 0xC0,0xA4 TLS_PSK_WITH_AES_128_CCM [RFC6655] + * 0xC0,0xA5 TLS_PSK_WITH_AES_256_CCM [RFC6655] + * 0xC0,0xA6 TLS_DHE_PSK_WITH_AES_128_CCM [RFC6655] + * 0xC0,0xA7 TLS_DHE_PSK_WITH_AES_256_CCM [RFC6655] + * 0xC0,0xA8 TLS_PSK_WITH_AES_128_CCM_8 [RFC6655] + * 0xC0,0xA9 TLS_PSK_WITH_AES_256_CCM_8 [RFC6655] + * 0xC0,0xAA TLS_PSK_DHE_WITH_AES_128_CCM_8 [RFC6655] + * 0xC0,0xAB TLS_PSK_DHE_WITH_AES_256_CCM_8 [RFC6655] + * 0xC0,0xAC TLS_ECDHE_ECDSA_WITH_AES_128_CCM [RFC7251] + * 0xC0,0xAD TLS_ECDHE_ECDSA_WITH_AES_256_CCM [RFC7251] + * 0xC0,0xAE TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 [RFC7251] + * 0xC0,0xAF TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 [RFC7251] + * 0xC0,0xB0-FF Unassigned + * 0xC1-FD,* Unassigned + * 0xFE,0x00-FD Unassigned + * 0xFE,0xFE-FF Reserved to avoid conflicts with widely + * deployed implementations [Pasi_Eronen] + * 0xFF,0x00-FF Reserved for Private Use [RFC5246] + */ + + add("SSL_NULL_WITH_NULL_NULL", + 0x0000, 1, K_NULL, B_NULL, F); + + /* + * Definition of the CipherSuites that are enabled by default. + * They are listed in preference order, most preferred first, using + * the following criteria: + * 1. Prefer Suite B compliant cipher suites, see RFC6460 (To be + * changed later, see below). + * 2. Prefer the stronger bulk cipher, in the order of AES_256(GCM), + * AES_128(GCM), AES_256, AES_128, 3DES-EDE, RC-4. + * 3. Prefer the stronger MAC algorithm, in the order of SHA384, + * SHA256, SHA, MD5. + * 4. Prefer the better performance of key exchange and digital + * signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, + * RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS. + */ + int p = DEFAULT_SUITES_PRIORITY * 2; + + // shorten names to fit the following table cleanly. + int max = ProtocolVersion.LIMIT_MAX_VALUE; + int tls11 = ProtocolVersion.TLS11.v; + int tls12 = ProtocolVersion.TLS12.v; + + // ID Key Exchange Cipher A obs suprt PRF + // ====== ============ ========= = === ===== ======== + + + // Placeholder for cipher suites in GCM mode. + // + // For better compatibility and interoperability, we decrease the + // priority of cipher suites in GCM mode for a while as GCM + // technologies mature in the industry. Eventually we'll move + // the GCM suites here. + + // AES_256(CBC) + add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + 0xc024, --p, K_ECDHE_ECDSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + 0xc028, --p, K_ECDHE_RSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_RSA_WITH_AES_256_CBC_SHA256", + 0x003d, --p, K_RSA, B_AES_256, T, max, tls12, P_SHA256); + add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", + 0xc026, --p, K_ECDH_ECDSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", + 0xc02a, --p, K_ECDH_RSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", + 0x006b, --p, K_DHE_RSA, B_AES_256, T, max, tls12, P_SHA256); + add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + 0x006a, --p, K_DHE_DSS, B_AES_256, T, max, tls12, P_SHA256); + + add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + 0xC00A, --p, K_ECDHE_ECDSA, B_AES_256, T); + add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + 0xC014, --p, K_ECDHE_RSA, B_AES_256, T); + add("TLS_RSA_WITH_AES_256_CBC_SHA", + 0x0035, --p, K_RSA, B_AES_256, T); + add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", + 0xC005, --p, K_ECDH_ECDSA, B_AES_256, T); + add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", + 0xC00F, --p, K_ECDH_RSA, B_AES_256, T); + add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + 0x0039, --p, K_DHE_RSA, B_AES_256, T); + add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + 0x0038, --p, K_DHE_DSS, B_AES_256, T); + + // AES_128(CBC) + add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + 0xc023, --p, K_ECDHE_ECDSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + 0xc027, --p, K_ECDHE_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_RSA_WITH_AES_128_CBC_SHA256", + 0x003c, --p, K_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", + 0xc025, --p, K_ECDH_ECDSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", + 0xc029, --p, K_ECDH_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", + 0x0067, --p, K_DHE_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", + 0x0040, --p, K_DHE_DSS, B_AES_128, T, max, tls12, P_SHA256); + + add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + 0xC009, --p, K_ECDHE_ECDSA, B_AES_128, T); + add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + 0xC013, --p, K_ECDHE_RSA, B_AES_128, T); + add("TLS_RSA_WITH_AES_128_CBC_SHA", + 0x002f, --p, K_RSA, B_AES_128, T); + add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", + 0xC004, --p, K_ECDH_ECDSA, B_AES_128, T); + add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", + 0xC00E, --p, K_ECDH_RSA, B_AES_128, T); + add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + 0x0033, --p, K_DHE_RSA, B_AES_128, T); + add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + 0x0032, --p, K_DHE_DSS, B_AES_128, T); + + // Cipher suites in GCM mode, see RFC 5288/5289. + // + // We may increase the priority of cipher suites in GCM mode when + // GCM technologies become mature in the industry. + + // Suite B compliant cipher suites, see RFC 6460. + // + // Note that, at present this provider is not Suite B compliant. The + // preference order of the GCM cipher suites does not follow the spec + // of RFC 6460. + add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + 0xc02c, --p, K_ECDHE_ECDSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + 0xc02b, --p, K_ECDHE_ECDSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + + // AES_256(GCM) + add("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + 0xc030, --p, K_ECDHE_RSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_RSA_WITH_AES_256_GCM_SHA384", + 0x009d, --p, K_RSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + 0xc02e, --p, K_ECDH_ECDSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", + 0xc032, --p, K_ECDH_RSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + 0x009f, --p, K_DHE_RSA, B_AES_256_GCM, T, max, tls12, P_SHA384); + add("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", + 0x00a3, --p, K_DHE_DSS, B_AES_256_GCM, T, max, tls12, P_SHA384); + + // AES_128(GCM) + add("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + 0xc02f, --p, K_ECDHE_RSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + add("TLS_RSA_WITH_AES_128_GCM_SHA256", + 0x009c, --p, K_RSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + 0xc02d, --p, K_ECDH_ECDSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + add("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + 0xc031, --p, K_ECDH_RSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + add("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + 0x009e, --p, K_DHE_RSA, B_AES_128_GCM, T, max, tls12, P_SHA256); + add("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", + 0x00a2, --p, K_DHE_DSS, B_AES_128_GCM, T, max, tls12, P_SHA256); + // End of cipher suites in GCM mode. + + // 3DES_EDE + add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + 0xC008, --p, K_ECDHE_ECDSA, B_3DES, T); + add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0xC012, --p, K_ECDHE_RSA, B_3DES, T); + add("SSL_RSA_WITH_3DES_EDE_CBC_SHA", + 0x000a, --p, K_RSA, B_3DES, T); + add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", + 0xC003, --p, K_ECDH_ECDSA, B_3DES, T); + add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", + 0xC00D, --p, K_ECDH_RSA, B_3DES, T); + add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + 0x0016, --p, K_DHE_RSA, B_3DES, T); + add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + 0x0013, --p, K_DHE_DSS, B_3DES, N); + + // RC-4 + add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + 0xC007, --p, K_ECDHE_ECDSA, B_RC4_128, N); + add("TLS_ECDHE_RSA_WITH_RC4_128_SHA", + 0xC011, --p, K_ECDHE_RSA, B_RC4_128, N); + add("SSL_RSA_WITH_RC4_128_SHA", + 0x0005, --p, K_RSA, B_RC4_128, N); + add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", + 0xC002, --p, K_ECDH_ECDSA, B_RC4_128, N); + add("TLS_ECDH_RSA_WITH_RC4_128_SHA", + 0xC00C, --p, K_ECDH_RSA, B_RC4_128, N); + add("SSL_RSA_WITH_RC4_128_MD5", + 0x0004, --p, K_RSA, B_RC4_128, N); + + // Renegotiation protection request Signalling Cipher Suite Value (SCSV) + add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", + 0x00ff, --p, K_SCSV, B_NULL, T); + + /* + * Definition of the CipherSuites that are supported but not enabled + * by default. + * They are listed in preference order, preferred first, using the + * following criteria: + * 1. CipherSuites for KRB5 need additional KRB5 service + * configuration, and these suites are not common in practice, + * so we put KRB5 based cipher suites at the end of the supported + * list. + * 2. If a cipher suite has been obsoleted, we put it at the end of + * the list. + * 3. Prefer the stronger bulk cipher, in the order of AES_256, + * AES_128, 3DES-EDE, RC-4, DES, DES40, RC4_40, NULL. + * 4. Prefer the stronger MAC algorithm, in the order of SHA384, + * SHA256, SHA, MD5. + * 5. Prefer the better performance of key exchange and digital + * signature algorithm, in the order of ECDHE-ECDSA, ECDHE-RSA, + * RSA, ECDH-ECDSA, ECDH-RSA, DHE-RSA, DHE-DSS, anonymous. + */ + p = DEFAULT_SUITES_PRIORITY; + + add("TLS_DH_anon_WITH_AES_256_GCM_SHA384", + 0x00a7, --p, K_DH_ANON, B_AES_256_GCM, N, max, tls12, P_SHA384); + add("TLS_DH_anon_WITH_AES_128_GCM_SHA256", + 0x00a6, --p, K_DH_ANON, B_AES_128_GCM, N, max, tls12, P_SHA256); + + add("TLS_DH_anon_WITH_AES_256_CBC_SHA256", + 0x006d, --p, K_DH_ANON, B_AES_256, N, max, tls12, P_SHA256); + add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", + 0xC019, --p, K_ECDH_ANON, B_AES_256, N); + add("TLS_DH_anon_WITH_AES_256_CBC_SHA", + 0x003a, --p, K_DH_ANON, B_AES_256, N); + + add("TLS_DH_anon_WITH_AES_128_CBC_SHA256", + 0x006c, --p, K_DH_ANON, B_AES_128, N, max, tls12, P_SHA256); + add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", + 0xC018, --p, K_ECDH_ANON, B_AES_128, N); + add("TLS_DH_anon_WITH_AES_128_CBC_SHA", + 0x0034, --p, K_DH_ANON, B_AES_128, N); + + add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", + 0xC017, --p, K_ECDH_ANON, B_3DES, N); + add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", + 0x001b, --p, K_DH_ANON, B_3DES, N); + + add("TLS_ECDH_anon_WITH_RC4_128_SHA", + 0xC016, --p, K_ECDH_ANON, B_RC4_128, N); + add("SSL_DH_anon_WITH_RC4_128_MD5", + 0x0018, --p, K_DH_ANON, B_RC4_128, N); + + // weak cipher suites obsoleted in TLS 1.2 + add("SSL_RSA_WITH_DES_CBC_SHA", + 0x0009, --p, K_RSA, B_DES, N, tls12); + add("SSL_DHE_RSA_WITH_DES_CBC_SHA", + 0x0015, --p, K_DHE_RSA, B_DES, N, tls12); + add("SSL_DHE_DSS_WITH_DES_CBC_SHA", + 0x0012, --p, K_DHE_DSS, B_DES, N, tls12); + add("SSL_DH_anon_WITH_DES_CBC_SHA", + 0x001a, --p, K_DH_ANON, B_DES, N, tls12); + + // weak cipher suites obsoleted in TLS 1.1 + add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0008, --p, K_RSA_EXPORT, B_DES_40, N, tls11); + add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0014, --p, K_DHE_RSA, B_DES_40, N, tls11); + add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + 0x0011, --p, K_DHE_DSS, B_DES_40, N, tls11); + add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + 0x0019, --p, K_DH_ANON, B_DES_40, N, tls11); + + add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", + 0x0003, --p, K_RSA_EXPORT, B_RC4_40, N, tls11); + add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", + 0x0017, --p, K_DH_ANON, B_RC4_40, N, tls11); + + add("TLS_RSA_WITH_NULL_SHA256", + 0x003b, --p, K_RSA, B_NULL, N, max, tls12, P_SHA256); + add("TLS_ECDHE_ECDSA_WITH_NULL_SHA", + 0xC006, --p, K_ECDHE_ECDSA, B_NULL, N); + add("TLS_ECDHE_RSA_WITH_NULL_SHA", + 0xC010, --p, K_ECDHE_RSA, B_NULL, N); + add("SSL_RSA_WITH_NULL_SHA", + 0x0002, --p, K_RSA, B_NULL, N); + add("TLS_ECDH_ECDSA_WITH_NULL_SHA", + 0xC001, --p, K_ECDH_ECDSA, B_NULL, N); + add("TLS_ECDH_RSA_WITH_NULL_SHA", + 0xC00B, --p, K_ECDH_RSA, B_NULL, N); + add("TLS_ECDH_anon_WITH_NULL_SHA", + 0xC015, --p, K_ECDH_ANON, B_NULL, N); + add("SSL_RSA_WITH_NULL_MD5", + 0x0001, --p, K_RSA, B_NULL, N); + + // Supported Kerberos ciphersuites from RFC2712 + add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", + 0x001f, --p, K_KRB5, B_3DES, N); + add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", + 0x0023, --p, K_KRB5, B_3DES, N); + add("TLS_KRB5_WITH_RC4_128_SHA", + 0x0020, --p, K_KRB5, B_RC4_128, N); + add("TLS_KRB5_WITH_RC4_128_MD5", + 0x0024, --p, K_KRB5, B_RC4_128, N); + add("TLS_KRB5_WITH_DES_CBC_SHA", + 0x001e, --p, K_KRB5, B_DES, N, tls12); + add("TLS_KRB5_WITH_DES_CBC_MD5", + 0x0022, --p, K_KRB5, B_DES, N, tls12); + add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", + 0x0026, --p, K_KRB5_EXPORT, B_DES_40, N, tls11); + add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + 0x0029, --p, K_KRB5_EXPORT, B_DES_40, N, tls11); + add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", + 0x0028, --p, K_KRB5_EXPORT, B_RC4_40, N, tls11); + add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", + 0x002b, --p, K_KRB5_EXPORT, B_RC4_40, N, tls11); + + /* + * Other values from the TLS Cipher Suite Registry, as of August 2010. + * + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xml + * + * Range Registration Procedures Notes + * 000-191 Standards Action Refers to value of first byte + * 192-254 Specification Required Refers to value of first byte + * 255 Reserved for Private Use Refers to value of first byte + */ + + // Register the names of a few additional CipherSuites. + // Makes them show up as names instead of numbers in + // the debug output. + + // remaining unsupported ciphersuites defined in RFC2246. + add("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", 0x0006); + add("SSL_RSA_WITH_IDEA_CBC_SHA", 0x0007); + add("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x000b); + add("SSL_DH_DSS_WITH_DES_CBC_SHA", 0x000c); + add("SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA", 0x000d); + add("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x000e); + add("SSL_DH_RSA_WITH_DES_CBC_SHA", 0x000f); + add("SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA", 0x0010); + + // SSL 3.0 Fortezza ciphersuites + add("SSL_FORTEZZA_DMS_WITH_NULL_SHA", 0x001c); + add("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", 0x001d); + + // 1024/56 bit exportable ciphersuites from expired internet draft + add("SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA", 0x0062); + add("SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA", 0x0063); + add("SSL_RSA_EXPORT1024_WITH_RC4_56_SHA", 0x0064); + add("SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", 0x0065); + add("SSL_DHE_DSS_WITH_RC4_128_SHA", 0x0066); + + // Netscape old and new SSL 3.0 FIPS ciphersuites + // see http://www.mozilla.org/projects/security/pki/nss/ssl/fips-ssl-ciphersuites.html + add("NETSCAPE_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xffe0); + add("NETSCAPE_RSA_FIPS_WITH_DES_CBC_SHA", 0xffe1); + add("SSL_RSA_FIPS_WITH_DES_CBC_SHA", 0xfefe); + add("SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", 0xfeff); + + // Unsupported Kerberos cipher suites from RFC 2712 + add("TLS_KRB5_WITH_IDEA_CBC_SHA", 0x0021); + add("TLS_KRB5_WITH_IDEA_CBC_MD5", 0x0025); + add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", 0x0027); + add("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", 0x002a); + + // Unsupported cipher suites from RFC 4162 + add("TLS_RSA_WITH_SEED_CBC_SHA", 0x0096); + add("TLS_DH_DSS_WITH_SEED_CBC_SHA", 0x0097); + add("TLS_DH_RSA_WITH_SEED_CBC_SHA", 0x0098); + add("TLS_DHE_DSS_WITH_SEED_CBC_SHA", 0x0099); + add("TLS_DHE_RSA_WITH_SEED_CBC_SHA", 0x009a); + add("TLS_DH_anon_WITH_SEED_CBC_SHA", 0x009b); + + // Unsupported cipher suites from RFC 4279 + add("TLS_PSK_WITH_RC4_128_SHA", 0x008a); + add("TLS_PSK_WITH_3DES_EDE_CBC_SHA", 0x008b); + add("TLS_PSK_WITH_AES_128_CBC_SHA", 0x008c); + add("TLS_PSK_WITH_AES_256_CBC_SHA", 0x008d); + add("TLS_DHE_PSK_WITH_RC4_128_SHA", 0x008e); + add("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", 0x008f); + add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", 0x0090); + add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", 0x0091); + add("TLS_RSA_PSK_WITH_RC4_128_SHA", 0x0092); + add("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", 0x0093); + add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", 0x0094); + add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", 0x0095); + + // Unsupported cipher suites from RFC 4785 + add("TLS_PSK_WITH_NULL_SHA", 0x002c); + add("TLS_DHE_PSK_WITH_NULL_SHA", 0x002d); + add("TLS_RSA_PSK_WITH_NULL_SHA", 0x002e); + + // Unsupported cipher suites from RFC 5246 + add("TLS_DH_DSS_WITH_AES_128_CBC_SHA", 0x0030); + add("TLS_DH_RSA_WITH_AES_128_CBC_SHA", 0x0031); + add("TLS_DH_DSS_WITH_AES_256_CBC_SHA", 0x0036); + add("TLS_DH_RSA_WITH_AES_256_CBC_SHA", 0x0037); + add("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", 0x003e); + add("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", 0x003f); + add("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", 0x0068); + add("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", 0x0069); + + // Unsupported cipher suites from RFC 5288 + add("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", 0x00a0); + add("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", 0x00a1); + add("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", 0x00a4); + add("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", 0x00a5); + + // Unsupported cipher suites from RFC 5487 + add("TLS_PSK_WITH_AES_128_GCM_SHA256", 0x00a8); + add("TLS_PSK_WITH_AES_256_GCM_SHA384", 0x00a9); + add("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", 0x00aa); + add("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", 0x00ab); + add("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", 0x00ac); + add("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", 0x00ad); + add("TLS_PSK_WITH_AES_128_CBC_SHA256", 0x00ae); + add("TLS_PSK_WITH_AES_256_CBC_SHA384", 0x00af); + add("TLS_PSK_WITH_NULL_SHA256", 0x00b0); + add("TLS_PSK_WITH_NULL_SHA384", 0x00b1); + add("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", 0x00b2); + add("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", 0x00b3); + add("TLS_DHE_PSK_WITH_NULL_SHA256", 0x00b4); + add("TLS_DHE_PSK_WITH_NULL_SHA384", 0x00b5); + add("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", 0x00b6); + add("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", 0x00b7); + add("TLS_RSA_PSK_WITH_NULL_SHA256", 0x00b8); + add("TLS_RSA_PSK_WITH_NULL_SHA384", 0x00b9); + + // Unsupported cipher suites from RFC 5932 + add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0041); + add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0042); + add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0043); + add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0044); + add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0045); + add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", 0x0046); + add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0084); + add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0085); + add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0086); + add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0087); + add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0088); + add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", 0x0089); + add("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00ba); + add("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bb); + add("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00bc); + add("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bd); + add("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00be); + add("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", 0x00bf); + add("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c0); + add("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c1); + add("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c2); + add("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c3); + add("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c4); + add("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", 0x00c5); + + // TLS Fallback Signaling Cipher Suite Value (SCSV) RFC 7507 + add("TLS_FALLBACK_SCSV", 0x5600); + + // Unsupported cipher suites from RFC 5054 + add("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", 0xc01a); + add("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", 0xc01b); + add("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", 0xc01c); + add("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", 0xc01d); + add("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", 0xc01e); + add("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", 0xc01f); + add("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", 0xc020); + add("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", 0xc021); + add("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", 0xc022); + + // Unsupported cipher suites from RFC 5489 + add("TLS_ECDHE_PSK_WITH_RC4_128_SHA", 0xc033); + add("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", 0xc034); + add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", 0xc035); + add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", 0xc036); + add("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", 0xc037); + add("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", 0xc038); + add("TLS_ECDHE_PSK_WITH_NULL_SHA", 0xc039); + add("TLS_ECDHE_PSK_WITH_NULL_SHA256", 0xc03a); + add("TLS_ECDHE_PSK_WITH_NULL_SHA384", 0xc03b); + + // Unsupported cipher suites from RFC 6209 + add("TLS_RSA_WITH_ARIA_128_CBC_SHA256", 0xc03c); + add("TLS_RSA_WITH_ARIA_256_CBC_SHA384", 0xc03d); + add("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", 0xc03e); + add("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", 0xc03f); + add("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc040); + add("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc041); + add("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", 0xc042); + add("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", 0xc043); + add("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc044); + add("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc045); + add("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", 0xc046); + add("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", 0xc047); + add("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc048); + add("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc049); + add("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc04a); + add("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc04b); + add("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04c); + add("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04d); + add("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04e); + add("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04f); + add("TLS_RSA_WITH_ARIA_128_GCM_SHA256", 0xc050); + add("TLS_RSA_WITH_ARIA_256_GCM_SHA384", 0xc051); + add("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc052); + add("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc053); + add("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc054); + add("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc055); + add("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", 0xc056); + add("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", 0xc057); + add("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", 0xc058); + add("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", 0xc059); + add("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", 0xc05a); + add("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", 0xc05b); + add("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05c); + add("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05d); + add("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05e); + add("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05f); + add("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc060); + add("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc061); + add("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc062); + add("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc063); + add("TLS_PSK_WITH_ARIA_128_CBC_SHA256", 0xc064); + add("TLS_PSK_WITH_ARIA_256_CBC_SHA384", 0xc065); + add("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc066); + add("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc067); + add("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", 0xc068); + add("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", 0xc069); + add("TLS_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06a); + add("TLS_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06b); + add("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06c); + add("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06d); + add("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06e); + add("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06f); + add("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc070); + add("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc071); + + // Unsupported cipher suites from RFC 6367 + add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072); + add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073); + add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc074); + add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc075); + add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc076); + add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc077); + add("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc078); + add("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc079); + add("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07a); + add("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07b); + add("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07c); + add("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07d); + add("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07e); + add("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07f); + add("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc080); + add("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc081); + add("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc082); + add("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc083); + add("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", 0xc084); + add("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", 0xc085); + add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086); + add("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087); + add("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc088); + add("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc089); + add("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08a); + add("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08b); + add("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08c); + add("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08d); + add("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc08e); + add("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc08f); + add("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc090); + add("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc091); + add("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc092); + add("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc093); + add("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc094); + add("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc095); + add("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc096); + add("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc097); + add("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc098); + add("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc099); + add("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc09a); + add("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc09b); + + // Unsupported cipher suites from RFC 6655 + add("TLS_RSA_WITH_AES_128_CCM", 0xc09c); + add("TLS_RSA_WITH_AES_256_CCM", 0xc09d); + add("TLS_DHE_RSA_WITH_AES_128_CCM", 0xc09e); + add("TLS_DHE_RSA_WITH_AES_256_CCM", 0xc09f); + add("TLS_RSA_WITH_AES_128_CCM_8", 0xc0A0); + add("TLS_RSA_WITH_AES_256_CCM_8", 0xc0A1); + add("TLS_DHE_RSA_WITH_AES_128_CCM_8", 0xc0A2); + add("TLS_DHE_RSA_WITH_AES_256_CCM_8", 0xc0A3); + add("TLS_PSK_WITH_AES_128_CCM", 0xc0A4); + add("TLS_PSK_WITH_AES_256_CCM", 0xc0A5); + add("TLS_DHE_PSK_WITH_AES_128_CCM", 0xc0A6); + add("TLS_DHE_PSK_WITH_AES_256_CCM", 0xc0A7); + add("TLS_PSK_WITH_AES_128_CCM_8", 0xc0A8); + add("TLS_PSK_WITH_AES_256_CCM_8", 0xc0A9); + add("TLS_PSK_DHE_WITH_AES_128_CCM_8", 0xc0Aa); + add("TLS_PSK_DHE_WITH_AES_256_CCM_8", 0xc0Ab); + + // Unsupported cipher suites from RFC 7251 + add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM", 0xc0Ac); + add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM", 0xc0Ad); + add("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", 0xc0Ae); + add("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", 0xc0Af); + } + + // ciphersuite SSL_NULL_WITH_NULL_NULL + final static CipherSuite C_NULL = CipherSuite.valueOf(0, 0); + + // ciphersuite TLS_EMPTY_RENEGOTIATION_INFO_SCSV + final static CipherSuite C_SCSV = CipherSuite.valueOf(0x00, 0xff); +} diff --git a/src/sun/security/ssl/CipherSuiteList.java b/src/sun/security/ssl/CipherSuiteList.java new file mode 100644 index 00000000..19dc90fa --- /dev/null +++ b/src/sun/security/ssl/CipherSuiteList.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.util.*; + +import javax.net.ssl.SSLException; + +/** + * A list of CipherSuites. Also maintains the lists of supported and + * default ciphersuites and supports I/O from handshake streams. + * + * Instances of this class are immutable. + * + */ +final class CipherSuiteList { + + private final Collection cipherSuites; + private String[] suiteNames; + + // flag indicating whether this list contains any ECC ciphersuites. + // null if not yet checked. + private volatile Boolean containsEC; + + // for use by buildAvailableCache() and + // Handshaker.getKickstartMessage() only + CipherSuiteList(Collection cipherSuites) { + this.cipherSuites = cipherSuites; + } + + /** + * Create a CipherSuiteList with a single element. + */ + CipherSuiteList(CipherSuite suite) { + cipherSuites = new ArrayList(1); + cipherSuites.add(suite); + } + + /** + * Construct a CipherSuiteList from a array of names. We don't bother + * to eliminate duplicates. + * + * @exception IllegalArgumentException if the array or any of its elements + * is null or if the ciphersuite name is unrecognized or unsupported + * using currently installed providers. + */ + CipherSuiteList(String[] names) { + if (names == null) { + throw new IllegalArgumentException("CipherSuites may not be null"); + } + cipherSuites = new ArrayList(names.length); + // refresh available cache once if a CipherSuite is not available + // (maybe new JCE providers have been installed) + boolean refreshed = false; + for (int i = 0; i < names.length; i++) { + String suiteName = names[i]; + CipherSuite suite = CipherSuite.valueOf(suiteName); + if (suite.isAvailable() == false) { + if (refreshed == false) { + // clear the cache so that the isAvailable() call below + // does a full check + clearAvailableCache(); + refreshed = true; + } + // still missing? + if (suite.isAvailable() == false) { + throw new IllegalArgumentException("Cannot support " + + suiteName + " with currently installed providers"); + } + } + cipherSuites.add(suite); + } + } + + /** + * Read a CipherSuiteList from a HandshakeInStream in V3 ClientHello + * format. Does not check if the listed ciphersuites are known or + * supported. + */ + CipherSuiteList(HandshakeInStream in) throws IOException { + byte[] bytes = in.getBytes16(); + if ((bytes.length & 1) != 0) { + throw new SSLException("Invalid ClientHello message"); + } + cipherSuites = new ArrayList(bytes.length >> 1); + for (int i = 0; i < bytes.length; i += 2) { + cipherSuites.add(CipherSuite.valueOf(bytes[i], bytes[i+1])); + } + } + + /** + * Return whether this list contains the given CipherSuite. + */ + boolean contains(CipherSuite suite) { + return cipherSuites.contains(suite); + } + + // Return whether this list contains any ECC ciphersuites + boolean containsEC() { + if (containsEC == null) { + for (CipherSuite c : cipherSuites) { + switch (c.keyExchange) { + case K_ECDH_ECDSA: + case K_ECDH_RSA: + case K_ECDHE_ECDSA: + case K_ECDHE_RSA: + case K_ECDH_ANON: + containsEC = true; + return true; + default: + break; + } + } + containsEC = false; + } + return containsEC; + } + + /** + * Return an Iterator for the CipherSuites in this list. + */ + Iterator iterator() { + return cipherSuites.iterator(); + } + + /** + * Return a reference to the internal Collection of CipherSuites. + * The Collection MUST NOT be modified. + */ + Collection collection() { + return cipherSuites; + } + + /** + * Return the number of CipherSuites in this list. + */ + int size() { + return cipherSuites.size(); + } + + /** + * Return an array with the names of the CipherSuites in this list. + */ + synchronized String[] toStringArray() { + if (suiteNames == null) { + suiteNames = new String[cipherSuites.size()]; + int i = 0; + for (CipherSuite c : cipherSuites) { + suiteNames[i++] = c.name; + } + } + return suiteNames.clone(); + } + + @Override + public String toString() { + return cipherSuites.toString(); + } + + /** + * Write this list to an HandshakeOutStream in V3 ClientHello format. + */ + void send(HandshakeOutStream s) throws IOException { + byte[] suiteBytes = new byte[cipherSuites.size() * 2]; + int i = 0; + for (CipherSuite c : cipherSuites) { + suiteBytes[i] = (byte)(c.id >> 8); + suiteBytes[i+1] = (byte)c.id; + i += 2; + } + s.putBytes16(suiteBytes); + } + + /** + * Clear cache of available ciphersuites. If we support all ciphers + * internally, there is no need to clear the cache and calling this + * method has no effect. + */ + static synchronized void clearAvailableCache() { + if (CipherSuite.DYNAMIC_AVAILABILITY) { + CipherSuite.BulkCipher.clearAvailableCache(); + JsseJce.clearEcAvailable(); + } + } +} diff --git a/src/sun/security/ssl/ClientHandshaker.java b/src/sun/security/ssl/ClientHandshaker.java new file mode 100644 index 00000000..807f4d9c --- /dev/null +++ b/src/sun/security/ssl/ClientHandshaker.java @@ -0,0 +1,1607 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.math.BigInteger; +import java.security.*; +import java.util.*; + +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; + +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import javax.security.auth.x500.X500Principal; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import javax.net.ssl.*; + +import javax.security.auth.Subject; + +import sun.security.ssl.HandshakeMessage.*; +import static sun.security.ssl.CipherSuite.KeyExchange.*; + +/** + * ClientHandshaker does the protocol handshaking from the point + * of view of a client. It is driven asychronously by handshake messages + * as delivered by the parent Handshaker class, and also uses + * common functionality (e.g. key generation) that is provided there. + * + * @author David Brownell + */ +final class ClientHandshaker extends Handshaker { + + // the server's public key from its certificate. + private PublicKey serverKey; + + // the server's ephemeral public key from the server key exchange message + // for ECDHE/ECDH_anon and RSA_EXPORT. + private PublicKey ephemeralServerKey; + + // server's ephemeral public value for DHE/DH_anon key exchanges + private BigInteger serverDH; + + private DHCrypt dh; + + private ECDHCrypt ecdh; + + private CertificateRequest certRequest; + + private boolean serverKeyExchangeReceived; + + /* + * The RSA PreMasterSecret needs to know the version of + * ClientHello that was used on this handshake. This represents + * the "max version" this client is supporting. In the + * case of an initial handshake, it's the max version enabled, + * but in the case of a resumption attempt, it's the version + * of the session we're trying to resume. + */ + private ProtocolVersion maxProtocolVersion; + + // To switch off the SNI extension. + private final static boolean enableSNIExtension = + Debug.getBooleanProperty("jsse.enableSNIExtension", true); + + /* + * Allow unsafe server certificate change? + * + * Server certificate change during SSL/TLS renegotiation may be considered + * unsafe, as described in the Triple Handshake attacks: + * + * https://secure-resumption.com/tlsauth.pdf + * + * Endpoint identification (See + * SSLParameters.getEndpointIdentificationAlgorithm()) is a pretty nice + * guarantee that the server certificate change in renegotiation is legal. + * However, endpoing identification is only enabled for HTTPS and LDAP + * over SSL/TLS by default. It is not enough to protect SSL/TLS + * connections other than HTTPS and LDAP. + * + * The renegotiation indication extension (See RFC 5764) is a pretty + * strong guarantee that the endpoints on both client and server sides + * are identical on the same connection. However, the Triple Handshake + * attacks can bypass this guarantee if there is a session-resumption + * handshake between the initial full handshake and the renegotiation + * full handshake. + * + * Server certificate change may be unsafe and should be restricted if + * endpoint identification is not enabled and the previous handshake is + * a session-resumption abbreviated initial handshake, unless the + * identities represented by both certificates can be regraded as the + * same (See isIdentityEquivalent()). + * + * Considering the compatibility impact and the actual requirements to + * support server certificate change in practice, the system property, + * jdk.tls.allowUnsafeServerCertChange, is used to define whether unsafe + * server certificate change in renegotiation is allowed or not. The + * default value of the system property is "false". To mitigate the + * compactibility impact, applications may want to set the system + * property to "true" at their own risk. + * + * If the value of the system property is "false", server certificate + * change in renegotiation after a session-resumption abbreviated initial + * handshake is restricted (See isIdentityEquivalent()). + * + * If the system property is set to "true" explicitly, the restriction on + * server certificate change in renegotiation is disabled. + */ + private final static boolean allowUnsafeServerCertChange = + Debug.getBooleanProperty("jdk.tls.allowUnsafeServerCertChange", false); + + // Whether an ALPN extension was sent in the ClientHello + private boolean alpnActive = false; + + private List requestedServerNames = + Collections.emptyList(); + + private boolean serverNamesAccepted = false; + + /* + * the reserved server certificate chain in previous handshaking + * + * The server certificate chain is only reserved if the previous + * handshake is a session-resumption abbreviated initial handshake. + */ + private X509Certificate[] reservedServerCerts = null; + + /* + * Constructors + */ + ClientHandshaker(SSLSocketImpl socket, SSLContextImpl context, + ProtocolList enabledProtocols, + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(socket, context, enabledProtocols, true, true, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + } + + ClientHandshaker(SSLEngineImpl engine, SSLContextImpl context, + ProtocolList enabledProtocols, + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(engine, context, enabledProtocols, true, true, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + } + + /* + * This routine handles all the client side handshake messages, one at + * a time. Given the message type (and in some cases the pending cipher + * spec) it parses the type-specific message. Then it calls a function + * that handles that specific message. + * + * It updates the state machine (need to verify it) as each message + * is processed, and writes responses as needed using the connection + * in the constructor. + */ + @Override + void processMessage(byte type, int messageLen) throws IOException { + if (state >= type + && (type != HandshakeMessage.ht_hello_request)) { + throw new SSLProtocolException( + "Handshake message sequence violation, " + type); + } + + switch (type) { + case HandshakeMessage.ht_hello_request: + this.serverHelloRequest(new HelloRequest(input)); + break; + + case HandshakeMessage.ht_server_hello: + this.serverHello(new ServerHello(input, messageLen)); + break; + + case HandshakeMessage.ht_certificate: + if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON + || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { + fatalSE(Alerts.alert_unexpected_message, + "unexpected server cert chain"); + // NOTREACHED + } + this.serverCertificate(new CertificateMsg(input)); + serverKey = + session.getPeerCertificates()[0].getPublicKey(); + break; + + case HandshakeMessage.ht_server_key_exchange: + serverKeyExchangeReceived = true; + switch (keyExchange) { + case K_RSA_EXPORT: + /** + * The server key exchange message is sent by the server only + * when the server certificate message does not contain the + * proper amount of data to allow the client to exchange a + * premaster secret, such as when RSA_EXPORT is used and the + * public key in the server certificate is longer than 512 bits. + */ + if (serverKey == null) { + throw new SSLProtocolException + ("Server did not send certificate message"); + } + + if (!(serverKey instanceof RSAPublicKey)) { + throw new SSLProtocolException("Protocol violation:" + + " the certificate type must be appropriate for the" + + " selected cipher suite's key exchange algorithm"); + } + + if (JsseJce.getRSAKeyLength(serverKey) <= 512) { + throw new SSLProtocolException("Protocol violation:" + + " server sent a server key exchange message for" + + " key exchange " + keyExchange + + " when the public key in the server certificate" + + " is less than or equal to 512 bits in length"); + } + + try { + this.serverKeyExchange(new RSA_ServerKeyExchange(input)); + } catch (GeneralSecurityException e) { + throwSSLException("Server key", e); + } + break; + case K_DH_ANON: + try { + this.serverKeyExchange(new DH_ServerKeyExchange( + input, protocolVersion)); + } catch (GeneralSecurityException e) { + throwSSLException("Server key", e); + } + break; + case K_DHE_DSS: + case K_DHE_RSA: + try { + this.serverKeyExchange(new DH_ServerKeyExchange( + input, serverKey, + clnt_random.random_bytes, svr_random.random_bytes, + messageLen, + localSupportedSignAlgs, protocolVersion)); + } catch (GeneralSecurityException e) { + throwSSLException("Server key", e); + } + break; + case K_ECDHE_ECDSA: + case K_ECDHE_RSA: + case K_ECDH_ANON: + try { + this.serverKeyExchange(new ECDH_ServerKeyExchange + (input, serverKey, clnt_random.random_bytes, + svr_random.random_bytes, + localSupportedSignAlgs, protocolVersion)); + } catch (GeneralSecurityException e) { + throwSSLException("Server key", e); + } + break; + case K_RSA: + case K_DH_RSA: + case K_DH_DSS: + case K_ECDH_ECDSA: + case K_ECDH_RSA: + throw new SSLProtocolException( + "Protocol violation: server sent a server key exchange" + + "message for key exchange " + keyExchange); + case K_KRB5: + case K_KRB5_EXPORT: + throw new SSLProtocolException( + "unexpected receipt of server key exchange algorithm"); + default: + throw new SSLProtocolException( + "unsupported key exchange algorithm = " + + keyExchange); + } + break; + + case HandshakeMessage.ht_certificate_request: + // save for later, it's handled by serverHelloDone + if ((keyExchange == K_DH_ANON) || (keyExchange == K_ECDH_ANON)) { + throw new SSLHandshakeException( + "Client authentication requested for "+ + "anonymous cipher suite."); + } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { + throw new SSLHandshakeException( + "Client certificate requested for "+ + "kerberos cipher suite."); + } + certRequest = new CertificateRequest(input, protocolVersion); + if (debug != null && Debug.isOn("handshake")) { + certRequest.print(System.out); + } + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + Collection peerSignAlgs = + certRequest.getSignAlgorithms(); + if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No peer supported signature algorithms"); + } + + Collection supportedPeerSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + peerSignAlgs); + if (supportedPeerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature and hash algorithm in common"); + } + + setPeerSupportedSignAlgs(supportedPeerSignAlgs); + session.setPeerSupportedSignatureAlgorithms( + supportedPeerSignAlgs); + } + + break; + + case HandshakeMessage.ht_server_hello_done: + this.serverHelloDone(new ServerHelloDone(input)); + break; + + case HandshakeMessage.ht_finished: + // A ChangeCipherSpec record must have been received prior to + // reception of the Finished message (RFC 5246, 7.4.9). + if (!receivedChangeCipherSpec()) { + fatalSE(Alerts.alert_handshake_failure, + "Received Finished message before ChangeCipherSpec"); + } + + this.serverFinished( + new Finished(protocolVersion, input, cipherSuite)); + break; + + default: + throw new SSLProtocolException( + "Illegal client handshake msg, " + type); + } + + // + // Move state machine forward if the message handling + // code didn't already do so + // + if (state < type) { + state = type; + } + } + + /* + * Used by the server to kickstart negotiations -- this requests a + * "client hello" to renegotiate current cipher specs (e.g. maybe lots + * of data has been encrypted with the same keys, or the server needs + * the client to present a certificate). + */ + private void serverHelloRequest(HelloRequest mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // + // Could be (e.g. at connection setup) that we already + // sent the "client hello" but the server's not seen it. + // + if (state < HandshakeMessage.ht_client_hello) { + if (!secureRenegotiation && !allowUnsafeRenegotiation) { + // renegotiation is not allowed. + if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { + // response with a no_renegotiation warning, + warningSE(Alerts.alert_no_renegotiation); + + // invalidate the handshake so that the caller can + // dispose this object. + invalidated = true; + + // If there is still unread block in the handshake + // input stream, it would be truncated with the disposal + // and the next handshake message will become incomplete. + // + // However, according to SSL/TLS specifications, no more + // handshake message should immediately follow ClientHello + // or HelloRequest. So just let it be. + } else { + // For SSLv3, send the handshake_failure fatal error. + // Note that SSLv3 does not define a no_renegotiation + // alert like TLSv1. However we cannot ignore the message + // simply, otherwise the other side was waiting for a + // response that would never come. + fatalSE(Alerts.alert_handshake_failure, + "Renegotiation is not allowed"); + } + } else { + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: continue with insecure renegotiation"); + } + } + kickstart(); + } + } + } + + + /* + * Server chooses session parameters given options created by the + * client -- basically, cipher options, session id, and someday a + * set of compression options. + * + * There are two branches of the state machine, decided by the + * details of this message. One is the "fast" handshake, where we + * can resume the pre-existing session we asked resume. The other + * is a more expensive "full" handshake, with key exchange and + * probably authentication getting done. + */ + private void serverHello(ServerHello mesg) throws IOException { + serverKeyExchangeReceived = false; + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // check if the server selected protocol version is OK for us + ProtocolVersion mesgVersion = mesg.protocolVersion; + if (!isNegotiable(mesgVersion)) { + throw new SSLHandshakeException( + "Server chose " + mesgVersion + + ", but that protocol version is not enabled or not supported " + + "by the client."); + } + + handshakeHash.protocolDetermined(mesgVersion); + + // Set protocolVersion and propagate to SSLSocket and the + // Handshake streams + setVersion(mesgVersion); + + // check the "renegotiation_info" extension + RenegotiationInfoExtension serverHelloRI = (RenegotiationInfoExtension) + mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); + if (serverHelloRI != null) { + if (isInitialHandshake) { + // verify the length of the "renegotiated_connection" field + if (!serverHelloRI.isEmpty()) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info field is not empty"); + } + + secureRenegotiation = true; + } else { + // For a legacy renegotiation, the client MUST verify that + // it does not contain the "renegotiation_info" extension. + if (!secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "Unexpected renegotiation indication extension"); + } + + // verify the client_verify_data and server_verify_data values + byte[] verifyData = + new byte[clientVerifyData.length + serverVerifyData.length]; + System.arraycopy(clientVerifyData, 0, verifyData, + 0, clientVerifyData.length); + System.arraycopy(serverVerifyData, 0, verifyData, + clientVerifyData.length, serverVerifyData.length); + if (!Arrays.equals(verifyData, + serverHelloRI.getRenegotiatedConnection())) { + fatalSE(Alerts.alert_handshake_failure, + "Incorrect verify data in ServerHello " + + "renegotiation_info message"); + } + } + } else { + // no renegotiation indication extension + if (isInitialHandshake) { + if (!allowLegacyHelloMessages) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "Failed to negotiate the use of secure renegotiation"); + } + + secureRenegotiation = false; + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning: No renegotiation " + + "indication extension in ServerHello"); + } + } else { + // For a secure renegotiation, the client must abort the + // handshake if no "renegotiation_info" extension is present. + if (secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "No renegotiation indication extension"); + } + + // we have already allowed unsafe renegotation before request + // the renegotiation. + } + } + + // + // Save server nonce, we always use it to compute connection + // keys and it's also used to create the master secret if we're + // creating a new session (i.e. in the full handshake). + // + svr_random = mesg.svr_random; + + if (isNegotiable(mesg.cipherSuite) == false) { + fatalSE(Alerts.alert_illegal_parameter, + "Server selected improper ciphersuite " + mesg.cipherSuite); + } + + setCipherSuite(mesg.cipherSuite); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); + } + + if (mesg.compression_method != 0) { + fatalSE(Alerts.alert_illegal_parameter, + "compression type not supported, " + + mesg.compression_method); + // NOTREACHED + } + + // so far so good, let's look at the session + if (session != null) { + // we tried to resume, let's see what the server decided + if (session.getSessionId().equals(mesg.sessionId)) { + // server resumed the session, let's make sure everything + // checks out + + // Verify that the session ciphers are unchanged. + CipherSuite sessionSuite = session.getSuite(); + if (cipherSuite != sessionSuite) { + throw new SSLProtocolException + ("Server returned wrong cipher suite for session"); + } + + // verify protocol version match + ProtocolVersion sessionVersion = session.getProtocolVersion(); + if (protocolVersion != sessionVersion) { + throw new SSLProtocolException + ("Server resumed session with wrong protocol version"); + } + + // validate subject identity + if (sessionSuite.keyExchange == K_KRB5 || + sessionSuite.keyExchange == K_KRB5_EXPORT) { + Principal localPrincipal = session.getLocalPrincipal(); + + Subject subject = null; + try { + subject = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Subject run() throws Exception { + return Krb5Helper.getClientSubject(getAccSE()); + }}); + } catch (PrivilegedActionException e) { + subject = null; + if (debug != null && Debug.isOn("session")) { + System.out.println("Attempt to obtain" + + " subject failed!"); + } + } + + if (subject != null) { + // Eliminate dependency on KerberosPrincipal + Set principals = + subject.getPrincipals(Principal.class); + if (!principals.contains(localPrincipal)) { + throw new SSLProtocolException("Server resumed" + + " session with wrong subject identity"); + } else { + if (debug != null && Debug.isOn("session")) + System.out.println("Subject identity is same"); + } + } else { + if (debug != null && Debug.isOn("session")) + System.out.println("Kerberos credentials are not" + + " present in the current Subject; check if " + + " javax.security.auth.useSubjectAsCreds" + + " system property has been set to false"); + throw new SSLProtocolException + ("Server resumed session with no subject"); + } + } + + // looks fine; resume it, and update the state machine. + resumingSession = true; + state = HandshakeMessage.ht_finished - 1; + calculateConnectionKeys(session.getMasterSecret()); + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Server resumed " + session); + } + } else { + // we wanted to resume, but the server refused + session = null; + if (!enableNewSession) { + throw new SSLException("New session creation is disabled"); + } + } + } + + // check the ALPN extension + ALPNExtension serverHelloALPN = + (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN); + + if (serverHelloALPN != null) { + // Check whether an ALPN extension was sent in ClientHello message + if (!alpnActive) { + fatalSE(Alerts.alert_unsupported_extension, + "Server sent " + ExtensionType.EXT_ALPN + + " extension when not requested by client"); + } + + List protocols = serverHelloALPN.getPeerAPs(); + // Only one application protocol name should be present + String p; + if ((protocols.size() == 1) && + !((p = protocols.get(0)).isEmpty())) { + int i; + for (i = 0; i < localApl.length; i++) { + if (localApl[i].equals(p)) { + break; + } + } + if (i == localApl.length) { + fatalSE(Alerts.alert_handshake_failure, + "Server has selected an application protocol name " + + "which was not offered by the client: " + p); + } + applicationProtocol = p; + } else { + fatalSE(Alerts.alert_handshake_failure, + "Incorrect data in ServerHello " + ExtensionType.EXT_ALPN + + " message"); + } + } else { + applicationProtocol = ""; + } + + if (resumingSession && session != null) { + setHandshakeSessionSE(session); + // Reserve the handshake state if this is a session-resumption + // abbreviated initial handshake. + if (isInitialHandshake) { + session.setAsSessionResumption(true); + } + + return; + } + + // check extensions + for (HelloExtension ext : mesg.extensions.list()) { + ExtensionType type = ext.type; + if (type == ExtensionType.EXT_SERVER_NAME) { + serverNamesAccepted = true; + } else if ((type != ExtensionType.EXT_ELLIPTIC_CURVES) + && (type != ExtensionType.EXT_EC_POINT_FORMATS) + && (type != ExtensionType.EXT_SERVER_NAME) + && (type != ExtensionType.EXT_ALPN) + && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) { + fatalSE(Alerts.alert_unsupported_extension, + "Server sent an unsupported extension: " + type); + } + } + + // Create a new session, we need to do the full handshake + session = new SSLSessionImpl(protocolVersion, cipherSuite, + getLocalSupportedSignAlgs(), + mesg.sessionId, getHostSE(), getPortSE()); + session.setRequestedServerNames(requestedServerNames); + setHandshakeSessionSE(session); + if (debug != null && Debug.isOn("handshake")) { + System.out.println("** " + cipherSuite); + } + } + + /* + * Server's own key was either a signing-only key, or was too + * large for export rules ... this message holds an ephemeral + * RSA key to use for key exchange. + */ + private void serverKeyExchange(RSA_ServerKeyExchange mesg) + throws IOException, GeneralSecurityException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + if (!mesg.verify(serverKey, clnt_random, svr_random)) { + fatalSE(Alerts.alert_handshake_failure, + "server key exchange invalid"); + // NOTREACHED + } + ephemeralServerKey = mesg.getPublicKey(); + } + + + /* + * Diffie-Hellman key exchange. We save the server public key and + * our own D-H algorithm object so we can defer key calculations + * until after we've sent the client key exchange message (which + * gives client and server some useful parallelism). + */ + private void serverKeyExchange(DH_ServerKeyExchange mesg) + throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), + sslContext.getSecureRandom()); + serverDH = mesg.getServerPublicKey(); + } + + private void serverKeyExchange(ECDH_ServerKeyExchange mesg) + throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + ECPublicKey key = mesg.getPublicKey(); + ecdh = new ECDHCrypt(key.getParams(), sslContext.getSecureRandom()); + ephemeralServerKey = key; + } + + /* + * The server's "Hello Done" message is the client's sign that + * it's time to do all the hard work. + */ + private void serverHelloDone(ServerHelloDone mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + /* + * Always make sure the input has been digested before we + * start emitting data, to ensure the hashes are correctly + * computed for the Finished and CertificateVerify messages + * which we send (here). + */ + input.digestNow(); + + /* + * FIRST ... if requested, send an appropriate Certificate chain + * to authenticate the client, and remember the associated private + * key to sign the CertificateVerify message. + */ + PrivateKey signingKey = null; + + if (certRequest != null) { + X509ExtendedKeyManager km = sslContext.getX509KeyManager(); + + ArrayList keytypesTmp = new ArrayList<>(4); + + for (int i = 0; i < certRequest.types.length; i++) { + String typeName; + + switch (certRequest.types[i]) { + case CertificateRequest.cct_rsa_sign: + typeName = "RSA"; + break; + + case CertificateRequest.cct_dss_sign: + typeName = "DSA"; + break; + + case CertificateRequest.cct_ecdsa_sign: + // ignore if we do not have EC crypto available + typeName = JsseJce.isEcAvailable() ? "EC" : null; + break; + + // Fixed DH/ECDH client authentication not supported + case CertificateRequest.cct_rsa_fixed_dh: + case CertificateRequest.cct_dss_fixed_dh: + case CertificateRequest.cct_rsa_fixed_ecdh: + case CertificateRequest.cct_ecdsa_fixed_ecdh: + // Any other values (currently not used in TLS) + case CertificateRequest.cct_rsa_ephemeral_dh: + case CertificateRequest.cct_dss_ephemeral_dh: + default: + typeName = null; + break; + } + + if ((typeName != null) && (!keytypesTmp.contains(typeName))) { + keytypesTmp.add(typeName); + } + } + + String alias = null; + int keytypesTmpSize = keytypesTmp.size(); + if (keytypesTmpSize != 0) { + String keytypes[] = + keytypesTmp.toArray(new String[keytypesTmpSize]); + + if (conn != null) { + alias = km.chooseClientAlias(keytypes, + certRequest.getAuthorities(), conn); + } else { + alias = km.chooseEngineClientAlias(keytypes, + certRequest.getAuthorities(), engine); + } + } + + CertificateMsg m1 = null; + if (alias != null) { + X509Certificate[] certs = km.getCertificateChain(alias); + if ((certs != null) && (certs.length != 0)) { + PublicKey publicKey = certs[0].getPublicKey(); + // for EC, make sure we use a supported named curve + if (publicKey instanceof ECPublicKey) { + ECParameterSpec params = + ((ECPublicKey)publicKey).getParams(); + int index = + SupportedEllipticCurvesExtension.getCurveIndex( + params); + if (!SupportedEllipticCurvesExtension.isSupported( + index)) { + publicKey = null; + } + } + if (publicKey != null) { + m1 = new CertificateMsg(certs); + signingKey = km.getPrivateKey(alias); + session.setLocalPrivateKey(signingKey); + session.setLocalCertificates(certs); + } + } + } + if (m1 == null) { + // + // No appropriate cert was found ... report this to the + // server. For SSLv3, send the no_certificate alert; + // TLS uses an empty cert chain instead. + // + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + m1 = new CertificateMsg(new X509Certificate [0]); + } else { + warningSE(Alerts.alert_no_certificate); + } + } + + // + // At last ... send any client certificate chain. + // + if (m1 != null) { + if (debug != null && Debug.isOn("handshake")) { + m1.print(System.out); + } + m1.write(output); + } + } + + /* + * SECOND ... send the client key exchange message. The + * procedure used is a function of the cipher suite selected; + * one is always needed. + */ + HandshakeMessage m2; + + switch (keyExchange) { + + case K_RSA: + case K_RSA_EXPORT: + if (serverKey == null) { + throw new SSLProtocolException + ("Server did not send certificate message"); + } + + if (!(serverKey instanceof RSAPublicKey)) { + throw new SSLProtocolException + ("Server certificate does not include an RSA key"); + } + + /* + * For RSA key exchange, we randomly generate a new + * pre-master secret and encrypt it with the server's + * public key. Then we save that pre-master secret + * so that we can calculate the keying data later; + * it's a performance speedup not to do that until + * the client's waiting for the server response, but + * more of a speedup for the D-H case. + * + * If the RSA_EXPORT scheme is active, when the public + * key in the server certificate is less than or equal + * to 512 bits in length, use the cert's public key, + * otherwise, the ephemeral one. + */ + PublicKey key; + if (keyExchange == K_RSA) { + key = serverKey; + } else { // K_RSA_EXPORT + if (JsseJce.getRSAKeyLength(serverKey) <= 512) { + // extraneous ephemeralServerKey check done + // above in processMessage() + key = serverKey; + } else { + if (ephemeralServerKey == null) { + throw new SSLProtocolException("Server did not send" + + " a RSA_EXPORT Server Key Exchange message"); + } + key = ephemeralServerKey; + } + } + + m2 = new RSAClientKeyExchange(protocolVersion, maxProtocolVersion, + sslContext.getSecureRandom(), key); + break; + case K_DH_RSA: + case K_DH_DSS: + /* + * For DH Key exchange, we only need to make sure the server + * knows our public key, so we calculate the same pre-master + * secret. + * + * For certs that had DH keys in them, we send an empty + * handshake message (no key) ... we flag this case by + * passing a null "dhPublic" value. + * + * Otherwise we send ephemeral DH keys, unsigned. + */ + // if (useDH_RSA || useDH_DSS) + m2 = new DHClientKeyExchange(); + break; + case K_DHE_RSA: + case K_DHE_DSS: + case K_DH_ANON: + if (dh == null) { + throw new SSLProtocolException + ("Server did not send a DH Server Key Exchange message"); + } + m2 = new DHClientKeyExchange(dh.getPublicKey()); + break; + case K_ECDHE_RSA: + case K_ECDHE_ECDSA: + case K_ECDH_ANON: + if (ecdh == null) { + throw new SSLProtocolException + ("Server did not send a ECDH Server Key Exchange message"); + } + m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); + break; + case K_ECDH_RSA: + case K_ECDH_ECDSA: + if (serverKey == null) { + throw new SSLProtocolException + ("Server did not send certificate message"); + } + if (serverKey instanceof ECPublicKey == false) { + throw new SSLProtocolException + ("Server certificate does not include an EC key"); + } + ECParameterSpec params = ((ECPublicKey)serverKey).getParams(); + ecdh = new ECDHCrypt(params, sslContext.getSecureRandom()); + m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); + break; + case K_KRB5: + case K_KRB5_EXPORT: + String sniHostname = null; + for (SNIServerName serverName : requestedServerNames) { + if (serverName instanceof SNIHostName) { + sniHostname = ((SNIHostName) serverName).getAsciiName(); + break; + } + } + + KerberosClientKeyExchange kerberosMsg = null; + if (sniHostname != null) { + // use first requested SNI hostname + try { + kerberosMsg = new KerberosClientKeyExchange( + sniHostname, getAccSE(), protocolVersion, + sslContext.getSecureRandom()); + } catch(IOException e) { + if (serverNamesAccepted) { + // server accepted requested SNI hostname, + // so it must be used + throw e; + } + // fallback to using hostname + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning, cannot use Server Name Indication: " + + e.getMessage()); + } + } + } + + if (kerberosMsg == null) { + String hostname = getHostSE(); + if (hostname == null) { + throw new IOException("Hostname is required" + + " to use Kerberos cipher suites"); + } + kerberosMsg = new KerberosClientKeyExchange( + hostname, getAccSE(), protocolVersion, + sslContext.getSecureRandom()); + } + + // Record the principals involved in exchange + session.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); + session.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); + m2 = kerberosMsg; + break; + default: + // somethings very wrong + throw new RuntimeException + ("Unsupported key exchange: " + keyExchange); + } + if (debug != null && Debug.isOn("handshake")) { + m2.print(System.out); + } + m2.write(output); + + + /* + * THIRD, send a "change_cipher_spec" record followed by the + * "Finished" message. We flush the messages we've queued up, to + * get concurrency between client and server. The concurrency is + * useful as we calculate the master secret, which is needed both + * to compute the "Finished" message, and to compute the keys used + * to protect all records following the change_cipher_spec. + */ + + output.doHashes(); + output.flush(); + + /* + * We deferred calculating the master secret and this connection's + * keying data; we do it now. Deferring this calculation is good + * from a performance point of view, since it lets us do it during + * some time that network delays and the server's own calculations + * would otherwise cause to be "dead" in the critical path. + */ + SecretKey preMasterSecret; + switch (keyExchange) { + case K_RSA: + case K_RSA_EXPORT: + preMasterSecret = ((RSAClientKeyExchange)m2).preMaster; + break; + case K_KRB5: + case K_KRB5_EXPORT: + byte[] secretBytes = + ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); + preMasterSecret = new SecretKeySpec(secretBytes, + "TlsPremasterSecret"); + break; + case K_DHE_RSA: + case K_DHE_DSS: + case K_DH_ANON: + preMasterSecret = dh.getAgreedSecret(serverDH, true); + break; + case K_ECDHE_RSA: + case K_ECDHE_ECDSA: + case K_ECDH_ANON: + preMasterSecret = ecdh.getAgreedSecret(ephemeralServerKey); + break; + case K_ECDH_RSA: + case K_ECDH_ECDSA: + preMasterSecret = ecdh.getAgreedSecret(serverKey); + break; + default: + throw new IOException("Internal error: unknown key exchange " + + keyExchange); + } + + calculateKeys(preMasterSecret, null); + + /* + * FOURTH, if we sent a Certificate, we need to send a signed + * CertificateVerify (unless the key in the client's certificate + * was a Diffie-Hellman key).). + * + * This uses a hash of the previous handshake messages ... either + * a nonfinal one (if the particular implementation supports it) + * or else using the third element in the arrays of hashes being + * computed. + */ + if (signingKey != null) { + CertificateVerify m3; + try { + SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + peerSupportedSignAlgs, signingKey.getAlgorithm(), + signingKey); + + if (preferableSignatureAlgorithm == null) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + String hashAlg = + SignatureAndHashAlgorithm.getHashAlgorithmName( + preferableSignatureAlgorithm); + if (hashAlg == null || hashAlg.length() == 0) { + throw new SSLHandshakeException( + "No supported hash algorithm"); + } + } + + m3 = new CertificateVerify(protocolVersion, handshakeHash, + signingKey, session.getMasterSecret(), + sslContext.getSecureRandom(), + preferableSignatureAlgorithm); + } catch (GeneralSecurityException e) { + fatalSE(Alerts.alert_handshake_failure, + "Error signing certificate verify", e); + // NOTREACHED, make compiler happy + m3 = null; + } + if (debug != null && Debug.isOn("handshake")) { + m3.print(System.out); + } + m3.write(output); + output.doHashes(); + } + + /* + * OK, that's that! + */ + sendChangeCipherAndFinish(false); + } + + + /* + * "Finished" is the last handshake message sent. If we got this + * far, the MAC has been validated post-decryption. We validate + * the two hashes here as an additional sanity check, protecting + * the handshake against various active attacks. + */ + private void serverFinished(Finished mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + boolean verified = mesg.verify(handshakeHash, Finished.SERVER, + session.getMasterSecret()); + + if (!verified) { + fatalSE(Alerts.alert_illegal_parameter, + "server 'finished' message doesn't verify"); + // NOTREACHED + } + + /* + * save server verify data for secure renegotiation + */ + if (secureRenegotiation) { + serverVerifyData = mesg.getVerifyData(); + } + + /* + * Reset the handshake state if this is not an initial handshake. + */ + if (!isInitialHandshake) { + session.setAsSessionResumption(false); + } + + /* + * OK, it verified. If we're doing the fast handshake, add that + * "Finished" message to the hash of handshake messages, then send + * our own change_cipher_spec and Finished message for the server + * to verify in turn. These are the last handshake messages. + * + * In any case, update the session cache. We're done handshaking, + * so there are no threats any more associated with partially + * completed handshakes. + */ + if (resumingSession) { + input.digestNow(); + sendChangeCipherAndFinish(true); + } + session.setLastAccessedTime(System.currentTimeMillis()); + + if (!resumingSession) { + if (session.isRejoinable()) { + ((SSLSessionContextImpl) sslContext + .engineGetClientSessionContext()) + .put(session); + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Cached client session: " + session); + } + } else if (debug != null && Debug.isOn("session")) { + System.out.println( + "%% Didn't cache non-resumable client session: " + + session); + } + } + } + + + /* + * Send my change-cipher-spec and Finished message ... done as the + * last handshake act in either the short or long sequences. In + * the short one, we've already seen the server's Finished; in the + * long one, we wait for it now. + */ + private void sendChangeCipherAndFinish(boolean finishedTag) + throws IOException { + Finished mesg = new Finished(protocolVersion, handshakeHash, + Finished.CLIENT, session.getMasterSecret(), cipherSuite); + + /* + * Send the change_cipher_spec message, then the Finished message + * which we just calculated (and protected using the keys we just + * calculated). Server responds with its Finished message, except + * in the "fast handshake" (resume session) case. + */ + sendChangeCipherSpec(mesg, finishedTag); + + /* + * save client verify data for secure renegotiation + */ + if (secureRenegotiation) { + clientVerifyData = mesg.getVerifyData(); + } + + /* + * Update state machine so server MUST send 'finished' next. + * (In "long" handshake case; in short case, we're responding + * to its message.) + */ + state = HandshakeMessage.ht_finished - 1; + } + + + /* + * Returns a ClientHello message to kickstart renegotiations + */ + @Override + HandshakeMessage getKickstartMessage() throws SSLException { + // session ID of the ClientHello message + SessionId sessionId = SSLSessionImpl.nullSession.getSessionId(); + + // a list of cipher suites sent by the client + CipherSuiteList cipherSuites = getActiveCipherSuites(); + + // set the max protocol version this client is supporting. + maxProtocolVersion = protocolVersion; + + // + // Try to resume an existing session. This might be mandatory, + // given certain API options. + // + session = ((SSLSessionContextImpl)sslContext + .engineGetClientSessionContext()) + .get(getHostSE(), getPortSE()); + if (debug != null && Debug.isOn("session")) { + if (session != null) { + System.out.println("%% Client cached " + + session + + (session.isRejoinable() ? "" : " (not rejoinable)")); + } else { + System.out.println("%% No cached client session"); + } + } + if (session != null) { + // If unsafe server certificate change is not allowed, reserve + // current server certificates if the previous handshake is a + // session-resumption abbreviated initial handshake. + if (!allowUnsafeServerCertChange && session.isSessionResumption()) { + try { + // If existing, peer certificate chain cannot be null. + reservedServerCerts = + (X509Certificate[])session.getPeerCertificates(); + } catch (SSLPeerUnverifiedException puve) { + // Maybe not certificate-based, ignore the exception. + } + } + + if (!session.isRejoinable()) { + session = null; + } + } + + if (session != null) { + CipherSuite sessionSuite = session.getSuite(); + ProtocolVersion sessionVersion = session.getProtocolVersion(); + if (isNegotiable(sessionSuite) == false) { + if (debug != null && Debug.isOn("session")) { + System.out.println("%% can't resume, unavailable cipher"); + } + session = null; + } + + if ((session != null) && !isNegotiable(sessionVersion)) { + if (debug != null && Debug.isOn("session")) { + System.out.println("%% can't resume, protocol disabled"); + } + session = null; + } + + if (session != null) { + if (debug != null) { + if (Debug.isOn("handshake") || Debug.isOn("session")) { + System.out.println("%% Try resuming " + session + + " from port " + getLocalPortSE()); + } + } + + sessionId = session.getSessionId(); + maxProtocolVersion = sessionVersion; + + // Update SSL version number in underlying SSL socket and + // handshake output stream, so that the output records (at the + // record layer) have the correct version + setVersion(sessionVersion); + } + + /* + * Force use of the previous session ciphersuite, and + * add the SCSV if enabled. + */ + if (!enableNewSession) { + if (session == null) { + throw new SSLHandshakeException( + "Can't reuse existing SSL client session"); + } + + Collection cipherList = new ArrayList<>(2); + cipherList.add(sessionSuite); + if (!secureRenegotiation && + cipherSuites.contains(CipherSuite.C_SCSV)) { + cipherList.add(CipherSuite.C_SCSV); + } // otherwise, renegotiation_info extension will be used + + cipherSuites = new CipherSuiteList(cipherList); + } + } + + if (session == null && !enableNewSession) { + throw new SSLHandshakeException("No existing session to resume"); + } + + // exclude SCSV for secure renegotiation + if (secureRenegotiation && cipherSuites.contains(CipherSuite.C_SCSV)) { + Collection cipherList = + new ArrayList<>(cipherSuites.size() - 1); + for (CipherSuite suite : cipherSuites.collection()) { + if (suite != CipherSuite.C_SCSV) { + cipherList.add(suite); + } + } + + cipherSuites = new CipherSuiteList(cipherList); + } + + // make sure there is a negotiable cipher suite. + boolean negotiable = false; + for (CipherSuite suite : cipherSuites.collection()) { + if (isNegotiable(suite)) { + negotiable = true; + break; + } + } + + if (!negotiable) { + throw new SSLHandshakeException("No negotiable cipher suite"); + } + + // Not a TLS1.2+ handshake + // For SSLv2Hello, HandshakeHash.reset() will be called, so we + // cannot call HandshakeHash.protocolDetermined() here. As it does + // not follow the spec that HandshakeHash.reset() can be only be + // called before protocolDetermined. + // if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) { + // handshakeHash.protocolDetermined(maxProtocolVersion); + // } + + // create the ClientHello message + ClientHello clientHelloMessage = new ClientHello( + sslContext.getSecureRandom(), maxProtocolVersion, + sessionId, cipherSuites); + + // add signature_algorithm extension + if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) { + // we will always send the signature_algorithm extension + Collection localSignAlgs = + getLocalSupportedSignAlgs(); + if (localSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs); + } + + // add server_name extension + if (enableSNIExtension) { + if (session != null) { + requestedServerNames = session.getRequestedServerNames(); + } else { + requestedServerNames = serverNames; + } + + if (!requestedServerNames.isEmpty()) { + clientHelloMessage.addSNIExtension(requestedServerNames); + } + } + + // Add ALPN extension + if (localApl != null && localApl.length > 0) { + clientHelloMessage.addALPNExtension(localApl); + alpnActive = true; + } + + // reset the client random cookie + clnt_random = clientHelloMessage.clnt_random; + + /* + * need to set the renegotiation_info extension for: + * 1: secure renegotiation + * 2: initial handshake and no SCSV in the ClientHello + * 3: insecure renegotiation and no SCSV in the ClientHello + */ + if (secureRenegotiation || + !cipherSuites.contains(CipherSuite.C_SCSV)) { + clientHelloMessage.addRenegotiationInfoExtension(clientVerifyData); + } + + return clientHelloMessage; + } + + /* + * Fault detected during handshake. + */ + @Override + void handshakeAlert(byte description) throws SSLProtocolException { + String message = Alerts.alertDescription(description); + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("SSL - handshake alert: " + message); + } + throw new SSLProtocolException("handshake alert: " + message); + } + + /* + * Unless we are using an anonymous ciphersuite, the server always + * sends a certificate message (for the CipherSuites we currently + * support). The trust manager verifies the chain for us. + */ + private void serverCertificate(CertificateMsg mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + X509Certificate[] peerCerts = mesg.getCertificateChain(); + if (peerCerts.length == 0) { + fatalSE(Alerts.alert_bad_certificate, "empty certificate chain"); + } + + // Allow server certificate change in client side during renegotiation + // after a session-resumption abbreviated initial handshake? + // + // DO NOT need to check allowUnsafeServerCertChange here. We only + // reserve server certificates when allowUnsafeServerCertChange is + // flase. + if (reservedServerCerts != null) { + // It is not necessary to check the certificate update if endpoint + // identification is enabled. + String identityAlg = getEndpointIdentificationAlgorithmSE(); + if ((identityAlg == null || identityAlg.length() == 0) && + !isIdentityEquivalent(peerCerts[0], reservedServerCerts[0])) { + + fatalSE(Alerts.alert_bad_certificate, + "server certificate change is restricted " + + "during renegotiation"); + } + } + + // ask the trust manager to verify the chain + X509TrustManager tm = sslContext.getX509TrustManager(); + try { + // find out the key exchange algorithm used + // use "RSA" for non-ephemeral "RSA_EXPORT" + String keyExchangeString; + if (keyExchange == K_RSA_EXPORT && !serverKeyExchangeReceived) { + keyExchangeString = K_RSA.name; + } else { + keyExchangeString = keyExchange.name; + } + + if (tm instanceof X509ExtendedTrustManager) { + if (conn != null) { + ((X509ExtendedTrustManager)tm).checkServerTrusted( + peerCerts.clone(), + keyExchangeString, + conn); + } else { + ((X509ExtendedTrustManager)tm).checkServerTrusted( + peerCerts.clone(), + keyExchangeString, + engine); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + } catch (CertificateException e) { + // This will throw an exception, so include the original error. + fatalSE(Alerts.alert_certificate_unknown, e); + } + session.setPeerCertificates(peerCerts); + } + + /* + * Whether the certificates can represent the same identity? + * + * The certificates can be used to represent the same identity: + * 1. If the subject alternative names of IP address are present in + * both certificates, they should be identical; otherwise, + * 2. if the subject alternative names of DNS name are present in + * both certificates, they should be identical; otherwise, + * 3. if the subject fields are present in both certificates, the + * certificate subjects and issuers should be identical. + */ + private static boolean isIdentityEquivalent(X509Certificate thisCert, + X509Certificate prevCert) { + if (thisCert.equals(prevCert)) { + return true; + } + + // check the iPAddress field in subjectAltName extension + Object thisIPAddress = getSubjectAltName(thisCert, 7); // 7: iPAddress + Object prevIPAddress = getSubjectAltName(prevCert, 7); + if (thisIPAddress != null && prevIPAddress!= null) { + // only allow the exactly match + return Objects.equals(thisIPAddress, prevIPAddress); + } + + // check the dNSName field in subjectAltName extension + Object thisDNSName = getSubjectAltName(thisCert, 2); // 2: dNSName + Object prevDNSName = getSubjectAltName(prevCert, 2); + if (thisDNSName != null && prevDNSName!= null) { + // only allow the exactly match + return Objects.equals(thisDNSName, prevDNSName); + } + + // check the certificate subject and issuer + X500Principal thisSubject = thisCert.getSubjectX500Principal(); + X500Principal prevSubject = prevCert.getSubjectX500Principal(); + X500Principal thisIssuer = thisCert.getIssuerX500Principal(); + X500Principal prevIssuer = prevCert.getIssuerX500Principal(); + if (!thisSubject.getName().isEmpty() && + !prevSubject.getName().isEmpty() && + thisSubject.equals(prevSubject) && + thisIssuer.equals(prevIssuer)) { + return true; + } + + return false; + } + + /* + * Returns the subject alternative name of the specified type in the + * subjectAltNames extension of a certificate. + */ + private static Object getSubjectAltName(X509Certificate cert, int type) { + Collection> subjectAltNames; + + try { + subjectAltNames = cert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Attempt to obtain subjectAltNames extension failed!"); + } + return null; + } + + if (subjectAltNames != null) { + for (List subjectAltName : subjectAltNames) { + int subjectAltNameType = (Integer)subjectAltName.get(0); + if (subjectAltNameType == type) { + return subjectAltName.get(1); + } + } + } + + return null; + } +} diff --git a/src/sun/security/ssl/DHClientKeyExchange.java b/src/sun/security/ssl/DHClientKeyExchange.java new file mode 100644 index 00000000..94426f47 --- /dev/null +++ b/src/sun/security/ssl/DHClientKeyExchange.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.IOException; +import java.io.PrintStream; +import java.math.BigInteger; +import javax.net.ssl.SSLHandshakeException; + +/* + * Message used by clients to send their Diffie-Hellman public + * keys to servers. + * + * @author David Brownell + */ +final class DHClientKeyExchange extends HandshakeMessage { + + @Override + int messageType() { + return ht_client_key_exchange; + } + + /* + * This value may be empty if it was included in the + * client's certificate ... + */ + private byte dh_Yc[]; // 1 to 2^16 -1 bytes + + BigInteger getClientPublicKey() { + return dh_Yc == null ? null : new BigInteger(1, dh_Yc); + } + + /* + * Either pass the client's public key explicitly (because it's + * using DHE or DH_anon), or implicitly (the public key was in the + * certificate). + */ + DHClientKeyExchange(BigInteger publicKey) { + dh_Yc = toByteArray(publicKey); + } + + DHClientKeyExchange() { + dh_Yc = null; + } + + /* + * Get the client's public key either explicitly or implicitly. + * (It's ugly to have an empty record be sent in the latter case, + * but that's what the protocol spec requires.) + */ + DHClientKeyExchange(HandshakeInStream input) throws IOException { + if (input.available() >= 2) { + dh_Yc = input.getBytes16(); + } else { + // currently, we don't support cipher suites that requires + // implicit public key of client. + throw new SSLHandshakeException( + "Unsupported implicit client DiffieHellman public key"); + } + } + + @Override + int messageLength() { + if (dh_Yc == null) { + return 0; + } else { + return dh_Yc.length + 2; + } + } + + @Override + void send(HandshakeOutStream s) throws IOException { + if (dh_Yc != null && dh_Yc.length != 0) { + s.putBytes16(dh_Yc); + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** ClientKeyExchange, DH"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "DH Public key", dh_Yc); + } + } +} diff --git a/src/sun/security/ssl/DHCrypt.java b/src/sun/security/ssl/DHCrypt.java new file mode 100644 index 00000000..4e4f60fd --- /dev/null +++ b/src/sun/security/ssl/DHCrypt.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.math.BigInteger; +import java.security.*; + +import javax.net.ssl.SSLHandshakeException; +import javax.crypto.SecretKey; +import javax.crypto.KeyAgreement; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.*; + +import sun.security.util.KeyUtil; + +/** + * This class implements the Diffie-Hellman key exchange algorithm. + * D-H means combining your private key with your partners public key to + * generate a number. The peer does the same with its private key and our + * public key. Through the magic of Diffie-Hellman we both come up with the + * same number. This number is secret (discounting MITM attacks) and hence + * called the shared secret. It has the same length as the modulus, e.g. 512 + * or 1024 bit. Man-in-the-middle attacks are typically countered by an + * independent authentication step using certificates (RSA, DSA, etc.). + * + * The thing to note is that the shared secret is constant for two partners + * with constant private keys. This is often not what we want, which is why + * it is generally a good idea to create a new private key for each session. + * Generating a private key involves one modular exponentiation assuming + * suitable D-H parameters are available. + * + * General usage of this class (TLS DHE case): + * . if we are server, call DHCrypt(keyLength,random). This generates + * an ephemeral keypair of the request length. + * . if we are client, call DHCrypt(modulus, base, random). This + * generates an ephemeral keypair using the parameters specified by + * the server. + * . send parameters and public value to remote peer + * . receive peers ephemeral public key + * . call getAgreedSecret() to calculate the shared secret + * + * In TLS the server chooses the parameter values itself, the client must use + * those sent to it by the server. + * + * The use of ephemeral keys as described above also achieves what is called + * "forward secrecy". This means that even if the authentication keys are + * broken at a later date, the shared secret remains secure. The session is + * compromised only if the authentication keys are already broken at the + * time the key exchange takes place and an active MITM attack is used. + * This is in contrast to straightforward encrypting RSA key exchanges. + * + * @author David Brownell + */ +final class DHCrypt { + + // group parameters (prime modulus and generator) + private BigInteger modulus; // P (aka N) + private BigInteger base; // G (aka alpha) + + // our private key (including private component x) + private PrivateKey privateKey; + + // public component of our key, X = (g ^ x) mod p + private BigInteger publicValue; // X (aka y) + + // the times to recove from failure if public key validation + private static int MAX_FAILOVER_TIMES = 2; + + /** + * Generate a Diffie-Hellman keypair of the specified size. + */ + DHCrypt(int keyLength, SecureRandom random) { + try { + KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman"); + kpg.initialize(keyLength, random); + + DHPublicKeySpec spec = generateDHPublicKeySpec(kpg); + if (spec == null) { + throw new RuntimeException("Could not generate DH keypair"); + } + + publicValue = spec.getY(); + modulus = spec.getP(); + base = spec.getG(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not generate DH keypair", e); + } + } + + + /** + * Generate a Diffie-Hellman keypair using the specified parameters. + * + * @param modulus the Diffie-Hellman modulus P + * @param base the Diffie-Hellman base G + */ + DHCrypt(BigInteger modulus, BigInteger base, SecureRandom random) { + this.modulus = modulus; + this.base = base; + try { + KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("DiffieHellman"); + DHParameterSpec params = new DHParameterSpec(modulus, base); + kpg.initialize(params, random); + + DHPublicKeySpec spec = generateDHPublicKeySpec(kpg); + if (spec == null) { + throw new RuntimeException("Could not generate DH keypair"); + } + + publicValue = spec.getY(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not generate DH keypair", e); + } + } + + + static DHPublicKeySpec getDHPublicKeySpec(PublicKey key) { + if (key instanceof DHPublicKey) { + DHPublicKey dhKey = (DHPublicKey)key; + DHParameterSpec params = dhKey.getParams(); + return new DHPublicKeySpec(dhKey.getY(), + params.getP(), params.getG()); + } + try { + KeyFactory factory = JsseJce.getKeyFactory("DH"); + return factory.getKeySpec(key, DHPublicKeySpec.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /** Returns the Diffie-Hellman modulus. */ + BigInteger getModulus() { + return modulus; + } + + /** Returns the Diffie-Hellman base (generator). */ + BigInteger getBase() { + return base; + } + + /** + * Gets the public key of this end of the key exchange. + */ + BigInteger getPublicKey() { + return publicValue; + } + + /** + * Get the secret data that has been agreed on through Diffie-Hellman + * key agreement protocol. Note that in the two party protocol, if + * the peer keys are already known, no other data needs to be sent in + * order to agree on a secret. That is, a secured message may be + * sent without any mandatory round-trip overheads. + * + *

    It is illegal to call this member function if the private key + * has not been set (or generated). + * + * @param peerPublicKey the peer's public key. + * @param keyIsValidated whether the {@code peerPublicKey} has beed + * validated + * @return the secret, which is an unsigned big-endian integer + * the same size as the Diffie-Hellman modulus. + */ + SecretKey getAgreedSecret(BigInteger peerPublicValue, + boolean keyIsValidated) throws SSLHandshakeException { + try { + KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); + DHPublicKeySpec spec = + new DHPublicKeySpec(peerPublicValue, modulus, base); + PublicKey publicKey = kf.generatePublic(spec); + KeyAgreement ka = JsseJce.getKeyAgreement("DiffieHellman"); + + // validate the Diffie-Hellman public key + if (!keyIsValidated && + !KeyUtil.isOracleJCEProvider(ka.getProvider().getName())) { + try { + KeyUtil.validate(spec); + } catch (InvalidKeyException ike) { + // prefer handshake_failure alert to internal_error alert + throw new SSLHandshakeException(ike.getMessage()); + } + } + + ka.init(privateKey); + ka.doPhase(publicKey, true); + return ka.generateSecret("TlsPremasterSecret"); + } catch (GeneralSecurityException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(e); + } + } + + // Generate and validate DHPublicKeySpec + private DHPublicKeySpec generateDHPublicKeySpec(KeyPairGenerator kpg) + throws GeneralSecurityException { + + boolean doExtraValiadtion = + (!KeyUtil.isOracleJCEProvider(kpg.getProvider().getName())); + for (int i = 0; i <= MAX_FAILOVER_TIMES; i++) { + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + DHPublicKeySpec spec = getDHPublicKeySpec(kp.getPublic()); + + // validate the Diffie-Hellman public key + if (doExtraValiadtion) { + try { + KeyUtil.validate(spec); + } catch (InvalidKeyException ivke) { + if (i == MAX_FAILOVER_TIMES) { + throw ivke; + } + // otherwise, ignore the exception and try the next one + continue; + } + } + + return spec; + } + + return null; + } +} diff --git a/src/sun/security/ssl/Debug.java b/src/sun/security/ssl/Debug.java new file mode 100644 index 00000000..1d01b6b4 --- /dev/null +++ b/src/sun/security/ssl/Debug.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.PrintStream; +import java.security.AccessController; +import java.util.Locale; + +import sun.security.action.GetPropertyAction; + +/** + * This class has be shamefully lifted from sun.security.util.Debug + * + * @author Gary Ellison + */ +public class Debug { + + private String prefix; + + private static String args; + + static { + args = AccessController.doPrivileged( + new GetPropertyAction("javax.net.debug", "")); + args = args.toLowerCase(Locale.ENGLISH); + if (args.equals("help")) { + Help(); + } + } + + public static void Help() + { + System.err.println(); + System.err.println("all turn on all debugging"); + System.err.println("ssl turn on ssl debugging"); + System.err.println(); + System.err.println("The following can be used with ssl:"); + System.err.println("\trecord enable per-record tracing"); + System.err.println("\thandshake print each handshake message"); + System.err.println("\tkeygen print key generation data"); + System.err.println("\tsession print session activity"); + System.err.println("\tdefaultctx print default SSL initialization"); + System.err.println("\tsslctx print SSLContext tracing"); + System.err.println("\tsessioncache print session cache tracing"); + System.err.println("\tkeymanager print key manager tracing"); + System.err.println("\ttrustmanager print trust manager tracing"); + System.err.println("\tpluggability print pluggability tracing"); + System.err.println(); + System.err.println("\thandshake debugging can be widened with:"); + System.err.println("\tdata hex dump of each handshake message"); + System.err.println("\tverbose verbose handshake message printing"); + System.err.println(); + System.err.println("\trecord debugging can be widened with:"); + System.err.println("\tplaintext hex dump of record plaintext"); + System.err.println("\tpacket print raw SSL/TLS packets"); + System.err.println(); + System.exit(0); + } + + /** + * Get a Debug object corresponding to whether or not the given + * option is set. Set the prefix to be the same as option. + */ + + public static Debug getInstance(String option) + { + return getInstance(option, option); + } + + /** + * Get a Debug object corresponding to whether or not the given + * option is set. Set the prefix to be prefix. + */ + public static Debug getInstance(String option, String prefix) + { + if (isOn(option)) { + Debug d = new Debug(); + d.prefix = prefix; + return d; + } else { + return null; + } + } + + /** + * True if the property "javax.net.debug" contains the + * string "option". + */ + public static boolean isOn(String option) + { + if (args == null) { + return false; + } else { + int n = 0; + option = option.toLowerCase(Locale.ENGLISH); + + if (args.indexOf("all") != -1) { + return true; + } else if ((n = args.indexOf("ssl")) != -1) { + if (args.indexOf("sslctx", n) == -1) { + // don't enable data and plaintext options by default + if (!(option.equals("data") + || option.equals("packet") + || option.equals("plaintext"))) { + return true; + } + } + } + return (args.indexOf(option) != -1); + } + } + + /** + * print a message to stderr that is prefixed with the prefix + * created from the call to getInstance. + */ + + public void println(String message) + { + System.err.println(prefix + ": "+message); + } + + /** + * print a blank line to stderr that is prefixed with the prefix. + */ + + public void println() + { + System.err.println(prefix + ":"); + } + + /** + * print a message to stderr that is prefixed with the prefix. + */ + + public static void println(String prefix, String message) + { + System.err.println(prefix + ": "+message); + } + + public static void println(PrintStream s, String name, byte[] data) { + s.print(name + ": { "); + if (data == null) { + s.print("null"); + } else { + for (int i = 0; i < data.length; i++) { + if (i != 0) s.print(", "); + s.print(data[i] & 0x0ff); + } + } + s.println(" }"); + } + + /** + * Return the value of the boolean System property propName. + * + * Note use of doPrivileged(). Do make accessible to applications. + */ + static boolean getBooleanProperty(String propName, boolean defaultValue) { + // if set, require value of either true or false + String b = AccessController.doPrivileged( + new GetPropertyAction(propName)); + if (b == null) { + return defaultValue; + } else if (b.equalsIgnoreCase("false")) { + return false; + } else if (b.equalsIgnoreCase("true")) { + return true; + } else { + throw new RuntimeException("Value of " + propName + + " must either be 'true' or 'false'"); + } + } + + static String toString(byte[] b) { + return sun.security.util.Debug.toString(b); + } +} diff --git a/src/sun/security/ssl/ECDHClientKeyExchange.java b/src/sun/security/ssl/ECDHClientKeyExchange.java new file mode 100644 index 00000000..8465b01b --- /dev/null +++ b/src/sun/security/ssl/ECDHClientKeyExchange.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.io.PrintStream; + +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.*; + +/** + * ClientKeyExchange message for all ECDH based key exchange methods. It + * contains the client's ephemeral public value. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ECDHClientKeyExchange extends HandshakeMessage { + + @Override + int messageType() { + return ht_client_key_exchange; + } + + private byte[] encodedPoint; + + byte[] getEncodedPoint() { + return encodedPoint; + } + + // Called by the client with its ephemeral public key. + ECDHClientKeyExchange(PublicKey publicKey) { + ECPublicKey ecKey = (ECPublicKey)publicKey; + ECPoint point = ecKey.getW(); + ECParameterSpec params = ecKey.getParams(); + encodedPoint = JsseJce.encodePoint(point, params.getCurve()); + } + + ECDHClientKeyExchange(HandshakeInStream input) throws IOException { + encodedPoint = input.getBytes8(); + } + + @Override + int messageLength() { + return encodedPoint.length + 1; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putBytes8(encodedPoint); + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** ECDHClientKeyExchange"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "ECDH Public value", encodedPoint); + } + } +} diff --git a/src/sun/security/ssl/ECDHCrypt.java b/src/sun/security/ssl/ECDHCrypt.java new file mode 100644 index 00000000..c1ce4e93 --- /dev/null +++ b/src/sun/security/ssl/ECDHCrypt.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.*; +import java.security.interfaces.ECPublicKey; +import java.security.spec.*; + +import javax.crypto.SecretKey; +import javax.crypto.KeyAgreement; +import javax.net.ssl.SSLHandshakeException; + +/** + * Helper class for the ECDH key exchange. It generates the appropriate + * ephemeral keys as necessary and performs the actual shared secret derivation. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class ECDHCrypt { + + // our private key + private PrivateKey privateKey; + + // our public key + private ECPublicKey publicKey; + + // Called by ServerHandshaker for static ECDH + ECDHCrypt(PrivateKey privateKey, PublicKey publicKey) { + this.privateKey = privateKey; + this.publicKey = (ECPublicKey)publicKey; + } + + // Called by ServerHandshaker for ephemeral ECDH + ECDHCrypt(String curveName, SecureRandom random) { + try { + KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC"); + ECGenParameterSpec params = new ECGenParameterSpec(curveName); + kpg.initialize(params, random); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + publicKey = (ECPublicKey)kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not generate DH keypair", e); + } + } + + // Called by ClientHandshaker with params it received from the server + ECDHCrypt(ECParameterSpec params, SecureRandom random) { + try { + KeyPairGenerator kpg = JsseJce.getKeyPairGenerator("EC"); + kpg.initialize(params, random); + KeyPair kp = kpg.generateKeyPair(); + privateKey = kp.getPrivate(); + publicKey = (ECPublicKey)kp.getPublic(); + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not generate DH keypair", e); + } + } + + /** + * Gets the public key of this end of the key exchange. + */ + PublicKey getPublicKey() { + return publicKey; + } + + // called by ClientHandshaker with either the server's static or ephemeral public key + SecretKey getAgreedSecret(PublicKey peerPublicKey) throws SSLHandshakeException { + try { + KeyAgreement ka = JsseJce.getKeyAgreement("ECDH"); + ka.init(privateKey); + ka.doPhase(peerPublicKey, true); + return ka.generateSecret("TlsPremasterSecret"); + } catch (GeneralSecurityException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(e); + } + } + + // called by ServerHandshaker + SecretKey getAgreedSecret(byte[] encodedPoint) throws SSLHandshakeException { + try { + ECParameterSpec params = publicKey.getParams(); + ECPoint point = JsseJce.decodePoint(encodedPoint, params.getCurve()); + KeyFactory kf = JsseJce.getKeyFactory("EC"); + ECPublicKeySpec spec = new ECPublicKeySpec(point, params); + PublicKey peerPublicKey = kf.generatePublic(spec); + return getAgreedSecret(peerPublicKey); + } catch (GeneralSecurityException | java.io.IOException e) { + throw (SSLHandshakeException) new SSLHandshakeException( + "Could not generate secret").initCause(e); + } + } + +} diff --git a/src/sun/security/ssl/EngineArgs.java b/src/sun/security/ssl/EngineArgs.java new file mode 100644 index 00000000..5f89d0c7 --- /dev/null +++ b/src/sun/security/ssl/EngineArgs.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.nio.*; + +/* + * A multi-purpose class which handles all of the SSLEngine arguments. + * It validates arguments, checks for RO conditions, does space + * calculations, performs scatter/gather, etc. + * + * @author Brad R. Wetmore + */ +class EngineArgs { + + /* + * Keep track of the input parameters. + */ + ByteBuffer netData; + ByteBuffer [] appData; + + private int offset; // offset/len for the appData array. + private int len; + + /* + * The initial pos/limit conditions. This is useful because we can + * quickly calculate the amount consumed/produced in successful + * operations, or easily return the buffers to their pre-error + * conditions. + */ + private int netPos; + private int netLim; + + private int [] appPoss; + private int [] appLims; + + /* + * Sum total of the space remaining in all of the appData buffers + */ + private int appRemaining = 0; + + private boolean wrapMethod; + + /* + * Called by the SSLEngine.wrap() method. + */ + EngineArgs(ByteBuffer [] appData, int offset, int len, + ByteBuffer netData) { + this.wrapMethod = true; + init(netData, appData, offset, len); + } + + /* + * Called by the SSLEngine.unwrap() method. + */ + EngineArgs(ByteBuffer netData, ByteBuffer [] appData, int offset, + int len) { + this.wrapMethod = false; + init(netData, appData, offset, len); + } + + /* + * The main initialization method for the arguments. Most + * of them are pretty obvious as to what they do. + * + * Since we're already iterating over appData array for validity + * checking, we also keep track of how much remainging space is + * available. Info is used in both unwrap (to see if there is + * enough space available in the destination), and in wrap (to + * determine how much more we can copy into the outgoing data + * buffer. + */ + private void init(ByteBuffer netData, ByteBuffer [] appData, + int offset, int len) { + + if ((netData == null) || (appData == null)) { + throw new IllegalArgumentException("src/dst is null"); + } + + if ((offset < 0) || (len < 0) || (offset > appData.length - len)) { + throw new IndexOutOfBoundsException(); + } + + if (wrapMethod && netData.isReadOnly()) { + throw new ReadOnlyBufferException(); + } + + netPos = netData.position(); + netLim = netData.limit(); + + appPoss = new int [appData.length]; + appLims = new int [appData.length]; + + for (int i = offset; i < offset + len; i++) { + if (appData[i] == null) { + throw new IllegalArgumentException( + "appData[" + i + "] == null"); + } + + /* + * If we're unwrapping, then check to make sure our + * destination bufffers are writable. + */ + if (!wrapMethod && appData[i].isReadOnly()) { + throw new ReadOnlyBufferException(); + } + + appRemaining += appData[i].remaining(); + + appPoss[i] = appData[i].position(); + appLims[i] = appData[i].limit(); + } + + /* + * Ok, looks like we have a good set of args, let's + * store the rest of this stuff. + */ + this.netData = netData; + this.appData = appData; + this.offset = offset; + this.len = len; + } + + /* + * Given spaceLeft bytes to transfer, gather up that much data + * from the appData buffers (starting at offset in the array), + * and transfer it into the netData buffer. + * + * The user has already ensured there is enough room. + */ + void gather(int spaceLeft) { + for (int i = offset; (i < (offset + len)) && (spaceLeft > 0); i++) { + int amount = Math.min(appData[i].remaining(), spaceLeft); + appData[i].limit(appData[i].position() + amount); + netData.put(appData[i]); + appRemaining -= amount; + spaceLeft -= amount; + } + } + + /* + * Using the supplied buffer, scatter the data into the appData buffers + * (starting at offset in the array). + * + * The user has already ensured there is enough room. + */ + void scatter(ByteBuffer readyData) { + int amountLeft = readyData.remaining(); + + for (int i = offset; (i < (offset + len)) && (amountLeft > 0); + i++) { + int amount = Math.min(appData[i].remaining(), amountLeft); + readyData.limit(readyData.position() + amount); + appData[i].put(readyData); + amountLeft -= amount; + } + assert(readyData.remaining() == 0); + } + + int getAppRemaining() { + return appRemaining; + } + + /* + * Calculate the bytesConsumed/byteProduced. Aren't you glad + * we saved this off earlier? + */ + int deltaNet() { + return (netData.position() - netPos); + } + + /* + * Calculate the bytesConsumed/byteProduced. Aren't you glad + * we saved this off earlier? + */ + int deltaApp() { + int sum = 0; // Only calculating 2^14 here, don't need a long. + + for (int i = offset; i < offset + len; i++) { + sum += appData[i].position() - appPoss[i]; + } + + return sum; + } + + /* + * In the case of Exception, we want to reset the positions + * to appear as though no data has been consumed or produced. + * + * Currently, this method is only called as we are preparing to + * fail out, and thus we don't need to actually recalculate + * appRemaining. If that assumption changes, that variable should + * be updated here. + */ + void resetPos() { + netData.position(netPos); + for (int i = offset; i < offset + len; i++) { + // See comment above about recalculating appRemaining. + appData[i].position(appPoss[i]); + } + } + + /* + * We are doing lots of ByteBuffer manipulations, in which case + * we need to make sure that the limits get set back correctly. + * This is one of the last things to get done before returning to + * the user. + */ + void resetLim() { + netData.limit(netLim); + for (int i = offset; i < offset + len; i++) { + appData[i].limit(appLims[i]); + } + } +} diff --git a/src/sun/security/ssl/EngineInputRecord.java b/src/sun/security/ssl/EngineInputRecord.java new file mode 100644 index 00000000..d980b162 --- /dev/null +++ b/src/sun/security/ssl/EngineInputRecord.java @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; +import javax.net.ssl.*; +import javax.crypto.BadPaddingException; +import sun.misc.HexDumpEncoder; + + +/** + * Wrapper class around InputRecord. + * + * Application data is kept external to the InputRecord, + * but handshake data (alert/change_cipher_spec/handshake) will + * be kept internally in the ByteArrayInputStream. + * + * @author Brad Wetmore + */ +final class EngineInputRecord extends InputRecord { + + private SSLEngineImpl engine; + + /* + * A dummy ByteBuffer we'll pass back even when the data + * is stored internally. It'll never actually be used. + */ + static private ByteBuffer tmpBB = ByteBuffer.allocate(0); + + /* + * Flag to tell whether the last read/parsed data resides + * internal in the ByteArrayInputStream, or in the external + * buffers. + */ + private boolean internalData; + + EngineInputRecord(SSLEngineImpl engine) { + super(); + this.engine = engine; + } + + @Override + byte contentType() { + if (internalData) { + return super.contentType(); + } else { + return ct_application_data; + } + } + + /* + * Check if there is enough inbound data in the ByteBuffer + * to make a inbound packet. Look for both SSLv2 and SSLv3. + * + * @return -1 if there are not enough bytes to tell (small header), + */ + int bytesInCompletePacket(ByteBuffer buf) throws SSLException { + + /* + * SSLv2 length field is in bytes 0/1 + * SSLv3/TLS length field is in bytes 3/4 + */ + if (buf.remaining() < 5) { + return -1; + } + + int pos = buf.position(); + byte byteZero = buf.get(pos); + + int len = 0; + + /* + * If we have already verified previous packets, we can + * ignore the verifications steps, and jump right to the + * determination. Otherwise, try one last hueristic to + * see if it's SSL/TLS. + */ + if (formatVerified || + (byteZero == ct_handshake) || + (byteZero == ct_alert)) { + /* + * Last sanity check that it's not a wild record + */ + ProtocolVersion recordVersion = + ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2)); + + // check the record version + checkRecordVersion(recordVersion, false); + + /* + * Reasonably sure this is a V3, disable further checks. + * We can't do the same in the v2 check below, because + * read still needs to parse/handle the v2 clientHello. + */ + formatVerified = true; + + /* + * One of the SSLv3/TLS message types. + */ + len = ((buf.get(pos + 3) & 0xff) << 8) + + (buf.get(pos + 4) & 0xff) + headerSize; + + } else { + /* + * Must be SSLv2 or something unknown. + * Check if it's short (2 bytes) or + * long (3) header. + * + * Internals can warn about unsupported SSLv2 + */ + boolean isShort = ((byteZero & 0x80) != 0); + + if (isShort && + ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) { + + ProtocolVersion recordVersion = + ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4)); + + // check the record version + checkRecordVersion(recordVersion, true); + + /* + * Client or Server Hello + */ + int mask = (isShort ? 0x7f : 0x3f); + len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) + + (isShort ? 2 : 3); + + } else { + // Gobblygook! + throw new SSLException( + "Unrecognized SSL message, plaintext connection?"); + } + } + + return len; + } + + /* + * Pass the data down if it's internally cached, otherwise + * do it here. + * + * If internal data, data is decrypted internally. + * + * If external data(app), return a new ByteBuffer with data to + * process. + */ + ByteBuffer decrypt(Authenticator authenticator, + CipherBox box, ByteBuffer bb) throws BadPaddingException { + + if (internalData) { + decrypt(authenticator, box); // MAC is checked during decryption + return tmpBB; + } + + BadPaddingException reservedBPE = null; + int tagLen = + (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; + int cipheredLength = bb.remaining(); + + if (!box.isNullCipher()) { + try { + // apply explicit nonce for AEAD/CBC cipher suites if needed + int nonceSize = + box.applyExplicitNonce(authenticator, contentType(), bb); + + // decrypt the content + if (box.isAEADMode()) { + // DON'T encrypt the nonce_explicit for AEAD mode + bb.position(bb.position() + nonceSize); + } // The explicit IV for CBC mode can be decrypted. + + // Note that the CipherBox.decrypt() does not change + // the capacity of the buffer. + box.decrypt(bb, tagLen); + bb.position(nonceSize); // We don't actually remove the nonce. + } catch (BadPaddingException bpe) { + // RFC 2246 states that decryption_failed should be used + // for this purpose. However, that allows certain attacks, + // so we just send bad record MAC. We also need to make + // sure to always check the MAC to avoid a timing attack + // for the same issue. See paper by Vaudenay et al and the + // update in RFC 4346/5246. + // + // Failover to message authentication code checking. + reservedBPE = bpe; + } + } + + // Requires message authentication code for null, stream and block + // cipher suites. + if ((authenticator instanceof MAC) && (tagLen != 0)) { + MAC signer = (MAC)authenticator; + int macOffset = bb.limit() - tagLen; + + // Note that although it is not necessary, we run the same MAC + // computation and comparison on the payload for both stream + // cipher and CBC block cipher. + if (bb.remaining() < tagLen) { + // negative data length, something is wrong + if (reservedBPE == null) { + reservedBPE = new BadPaddingException("bad record"); + } + + // set offset of the dummy MAC + macOffset = cipheredLength - tagLen; + bb.limit(cipheredLength); + } + + // Run MAC computation and comparison on the payload. + if (checkMacTags(contentType(), bb, signer, false)) { + if (reservedBPE == null) { + reservedBPE = new BadPaddingException("bad record MAC"); + } + } + + // Run MAC computation and comparison on the remainder. + // + // It is only necessary for CBC block cipher. It is used to get a + // constant time of MAC computation and comparison on each record. + if (box.isCBCMode()) { + int remainingLen = calculateRemainingLen( + signer, cipheredLength, macOffset); + + // NOTE: here we use the InputRecord.buf because I did not find + // an effective way to work on ByteBuffer when its capacity is + // less than remainingLen. + + // NOTE: remainingLen may be bigger (less than 1 block of the + // hash algorithm of the MAC) than the cipheredLength. However, + // We won't need to worry about it because we always use a + // maximum buffer for every record. We need a change here if + // we use small buffer size in the future. + if (remainingLen > buf.length) { + // unlikely to happen, just a placehold + throw new RuntimeException( + "Internal buffer capacity error"); + } + + // Won't need to worry about the result on the remainder. And + // then we won't need to worry about what's actual data to + // check MAC tag on. We start the check from the header of the + // buffer so that we don't need to construct a new byte buffer. + checkMacTags(contentType(), buf, 0, remainingLen, signer, true); + } + + bb.limit(macOffset); + } + + // Is it a failover? + if (reservedBPE != null) { + throw reservedBPE; + } + + return bb.slice(); + } + + /* + * Run MAC computation and comparison + * + * Please DON'T change the content of the ByteBuffer parameter! + */ + private static boolean checkMacTags(byte contentType, ByteBuffer bb, + MAC signer, boolean isSimulated) { + + int position = bb.position(); + int tagLen = signer.MAClen(); + int lim = bb.limit(); + int macData = lim - tagLen; + + bb.limit(macData); + byte[] hash = signer.compute(contentType, bb, isSimulated); + if (hash == null || tagLen != hash.length) { + // Something is wrong with MAC implementation. + throw new RuntimeException("Internal MAC error"); + } + + bb.position(macData); + bb.limit(lim); + try { + int[] results = compareMacTags(bb, hash); + return (results[0] != 0); + } finally { + // reset to the data + bb.position(position); + bb.limit(macData); + } + } + + /* + * A constant-time comparison of the MAC tags. + * + * Please DON'T change the content of the ByteBuffer parameter! + */ + private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { + + // An array of hits is used to prevent Hotspot optimization for + // the purpose of a constant-time check. + int[] results = {0, 0}; // {missed #, matched #} + + // The caller ensures there are enough bytes available in the buffer. + // So we won't need to check the remaining of the buffer. + for (int i = 0; i < tag.length; i++) { + if (bb.get() != tag[i]) { + results[0]++; // mismatched bytes + } else { + results[1]++; // matched bytes + } + } + + return results; + } + + /* + * Override the actual write below. We do things this way to be + * consistent with InputRecord. InputRecord may try to write out + * data to the peer, and *then* throw an Exception. This forces + * data to be generated/output before the exception is ever + * generated. + */ + @Override + void writeBuffer(OutputStream s, byte [] buf, int off, int len) + throws IOException { + /* + * Copy data out of buffer, it's ready to go. + */ + ByteBuffer netBB = (ByteBuffer) + (ByteBuffer.allocate(len).put(buf, 0, len).flip()); + engine.writer.putOutboundDataSync(netBB); + } + + /* + * Delineate or read a complete packet from src. + * + * If internal data (hs, alert, ccs), the data is read and + * stored internally. + * + * If external data (app), return a new ByteBuffer which points + * to the data to process. + */ + ByteBuffer read(ByteBuffer srcBB) throws IOException { + /* + * Could have a src == null/dst == null check here, + * but that was already checked by SSLEngine.unwrap before + * ever attempting to read. + */ + + /* + * If we have anything besides application data, + * or if we haven't even done the initial v2 verification, + * we send this down to be processed by the underlying + * internal cache. + */ + if (!formatVerified || + (srcBB.get(srcBB.position()) != ct_application_data)) { + internalData = true; + read(new ByteBufferInputStream(srcBB), (OutputStream) null); + return tmpBB; + } + + internalData = false; + + int srcPos = srcBB.position(); + int srcLim = srcBB.limit(); + + ProtocolVersion recordVersion = ProtocolVersion.valueOf( + srcBB.get(srcPos + 1), srcBB.get(srcPos + 2)); + + // check the record version + checkRecordVersion(recordVersion, false); + + /* + * It's really application data. How much to consume? + * Jump over the header. + */ + int len = bytesInCompletePacket(srcBB); + assert(len > 0); + + if (debug != null && Debug.isOn("packet")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + ByteBuffer bb = srcBB.duplicate(); // Use copy of BB + bb.limit(srcPos + len); + + System.out.println("[Raw read (bb)]: length = " + len); + hd.encodeBuffer(bb, System.out); + } catch (IOException e) { } + } + + // Demarcate past header to end of packet. + srcBB.position(srcPos + headerSize); + srcBB.limit(srcPos + len); + + // Protect remainder of buffer, create slice to actually + // operate on. + ByteBuffer bb = srcBB.slice(); + + srcBB.position(srcBB.limit()); + srcBB.limit(srcLim); + + return bb; + } +} diff --git a/src/sun/security/ssl/EngineOutputRecord.java b/src/sun/security/ssl/EngineOutputRecord.java new file mode 100644 index 00000000..686accac --- /dev/null +++ b/src/sun/security/ssl/EngineOutputRecord.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; + +/** + * A OutputRecord class extension which uses external ByteBuffers + * or the internal ByteArrayOutputStream for data manipulations. + *

    + * Instead of rewriting this entire class + * to use ByteBuffers, we leave things intact, so handshake, CCS, + * and alerts will continue to use the internal buffers, but application + * data will use external buffers. + * + * @author Brad Wetmore + */ +final class EngineOutputRecord extends OutputRecord { + + private SSLEngineImpl engine; + private EngineWriter writer; + + private boolean finishedMsg = false; + + /* + * All handshake hashing is done by the superclass + */ + + /* + * Default constructor makes a record supporting the maximum + * SSL record size. It allocates the header bytes directly. + * + * @param type the content type for the record + */ + EngineOutputRecord(byte type, SSLEngineImpl engine) { + super(type, recordSize(type)); + this.engine = engine; + writer = engine.writer; + } + + /** + * Get the size of the buffer we need for records of the specified + * type. + *

    + * Application data buffers will provide their own byte buffers, + * and will not use the internal byte caching. + */ + private static int recordSize(byte type) { + switch (type) { + + case ct_change_cipher_spec: + case ct_alert: + return maxAlertRecordSize; + + case ct_handshake: + return maxRecordSize; + + case ct_application_data: + return 0; + } + + throw new RuntimeException("Unknown record type: " + type); + } + + void setFinishedMsg() { + finishedMsg = true; + } + + @Override + public void flush() throws IOException { + finishedMsg = false; + } + + boolean isFinishedMsg() { + return finishedMsg; + } + + /* + * Override the actual write below. We do things this way to be + * consistent with InputRecord. InputRecord may try to write out + * data to the peer, and *then* throw an Exception. This forces + * data to be generated/output before the exception is ever + * generated. + */ + @Override + void writeBuffer(OutputStream s, byte [] buf, int off, int len, + int debugOffset) throws IOException { + /* + * Copy data out of buffer, it's ready to go. + */ + ByteBuffer netBB = (ByteBuffer) + ByteBuffer.allocate(len).put(buf, off, len).flip(); + + writer.putOutboundData(netBB); + } + + /* + * Main method for writing non-application data. + * We MAC/encrypt, then send down for processing. + */ + void write(Authenticator authenticator, CipherBox writeCipher) + throws IOException { + + /* + * Sanity check. + */ + switch (contentType()) { + case ct_change_cipher_spec: + case ct_alert: + case ct_handshake: + break; + default: + throw new RuntimeException("unexpected byte buffers"); + } + + /* + * Don't bother to really write empty records. We went this + * far to drive the handshake machinery, for correctness; not + * writing empty records improves performance by cutting CPU + * time and network resource usage. Also, some protocol + * implementations are fragile and don't like to see empty + * records, so this increases robustness. + * + * (Even change cipher spec messages have a byte of data!) + */ + if (!isEmpty()) { + // compress(); // eventually + encrypt(authenticator, writeCipher); + + // send down for processing + write((OutputStream)null, false, (ByteArrayOutputStream)null); + } + return; + } + + /** + * Main wrap/write driver. + */ + void write(EngineArgs ea, Authenticator authenticator, + CipherBox writeCipher) throws IOException { + /* + * sanity check to make sure someone didn't inadvertantly + * send us an impossible combination we don't know how + * to process. + */ + assert(contentType() == ct_application_data); + + /* + * Have we set the MAC's yet? If not, we're not ready + * to process application data yet. + */ + if (authenticator == MAC.NULL) { + return; + } + + /* + * Don't bother to really write empty records. We went this + * far to drive the handshake machinery, for correctness; not + * writing empty records improves performance by cutting CPU + * time and network resource usage. Also, some protocol + * implementations are fragile and don't like to see empty + * records, so this increases robustness. + */ + if (ea.getAppRemaining() == 0) { + return; + } + + /* + * By default, we counter chosen plaintext issues on CBC mode + * ciphersuites in SSLv3/TLS1.0 by sending one byte of application + * data in the first record of every payload, and the rest in + * subsequent record(s). Note that the issues have been solved in + * TLS 1.1 or later. + * + * It is not necessary to split the very first application record of + * a freshly negotiated TLS session, as there is no previous + * application data to guess. To improve compatibility, we will not + * split such records. + * + * Because of the compatibility, we'd better produce no more than + * SSLSession.getPacketBufferSize() net data for each wrap. As we + * need a one-byte record at first, the 2nd record size should be + * equal to or less than Record.maxDataSizeMinusOneByteRecord. + * + * This avoids issues in the outbound direction. For a full fix, + * the peer must have similar protections. + */ + int length; + if (engine.needToSplitPayload(writeCipher, protocolVersion)) { + write(ea, authenticator, writeCipher, 0x01); + ea.resetLim(); // reset application data buffer limit + length = Math.min(ea.getAppRemaining(), + maxDataSizeMinusOneByteRecord); + } else { + length = Math.min(ea.getAppRemaining(), maxDataSize); + } + + // Don't bother to really write empty records. + if (length > 0) { + write(ea, authenticator, writeCipher, length); + } + + return; + } + + void write(EngineArgs ea, Authenticator authenticator, + CipherBox writeCipher, int length) throws IOException { + /* + * Copy out existing buffer values. + */ + ByteBuffer dstBB = ea.netData; + int dstPos = dstBB.position(); + int dstLim = dstBB.limit(); + + /* + * Where to put the data. Jump over the header. + * + * Don't need to worry about SSLv2 rewrites, if we're here, + * that's long since done. + */ + int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize(); + dstBB.position(dstData); + + /* + * transfer application data into the network data buffer + */ + ea.gather(length); + dstBB.limit(dstBB.position()); + dstBB.position(dstData); + + /* + * "flip" but skip over header again, add MAC & encrypt + */ + if (authenticator instanceof MAC) { + MAC signer = (MAC)authenticator; + if (signer.MAClen() != 0) { + byte[] hash = signer.compute(contentType(), dstBB, false); + + /* + * position was advanced to limit in compute above. + * + * Mark next area as writable (above layers should have + * established that we have plenty of room), then write + * out the hash. + */ + dstBB.limit(dstBB.limit() + hash.length); + dstBB.put(hash); + + // reset the position and limit + dstBB.limit(dstBB.position()); + dstBB.position(dstData); + } + } + + if (!writeCipher.isNullCipher()) { + /* + * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1 + * or later. + */ + if (protocolVersion.v >= ProtocolVersion.TLS11.v && + (writeCipher.isCBCMode() || writeCipher.isAEADMode())) { + byte[] nonce = writeCipher.createExplicitNonce( + authenticator, contentType(), dstBB.remaining()); + dstBB.position(dstPos + headerSize); + dstBB.put(nonce); + if (!writeCipher.isAEADMode()) { + // The explicit IV in TLS 1.1 and later can be encrypted. + dstBB.position(dstPos + headerSize); + } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode + } + + /* + * Encrypt may pad, so again the limit may have changed. + */ + writeCipher.encrypt(dstBB, dstLim); + + if ((debug != null) && (Debug.isOn("record") || + (Debug.isOn("handshake") && + (contentType() == ct_change_cipher_spec)))) { + System.out.println(Thread.currentThread().getName() + // v3.0/v3.1 ... + + ", WRITE: " + protocolVersion + + " " + InputRecord.contentName(contentType()) + + ", length = " + length); + } + } else { + dstBB.position(dstBB.limit()); + } + + int packetLength = dstBB.limit() - dstPos - headerSize; + + /* + * Finish out the record header. + */ + dstBB.put(dstPos, contentType()); + dstBB.put(dstPos + 1, protocolVersion.major); + dstBB.put(dstPos + 2, protocolVersion.minor); + dstBB.put(dstPos + 3, (byte)(packetLength >> 8)); + dstBB.put(dstPos + 4, (byte)packetLength); + + /* + * Position was already set by encrypt() above. + */ + dstBB.limit(dstLim); + } +} diff --git a/src/sun/security/ssl/EngineWriter.java b/src/sun/security/ssl/EngineWriter.java new file mode 100644 index 00000000..c99d931f --- /dev/null +++ b/src/sun/security/ssl/EngineWriter.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import sun.misc.HexDumpEncoder; + +/** + * A class to help abstract away SSLEngine writing synchronization. + */ +final class EngineWriter { + + /* + * Outgoing handshake Data waiting for a ride is stored here. + * Normal application data is written directly into the outbound + * buffer, but handshake data can be written out at any time, + * so we have buffer it somewhere. + * + * When wrap is called, we first check to see if there is + * any data waiting, then if we're in a data transfer state, + * we try to write app data. + * + * This will contain either ByteBuffers, or the marker + * HandshakeStatus.FINISHED to signify that a handshake just completed. + */ + private LinkedList outboundList; + + private boolean outboundClosed = false; + + /* Class and subclass dynamic debugging support */ + private static final Debug debug = Debug.getInstance("ssl"); + + EngineWriter() { + outboundList = new LinkedList(); + } + + /* + * Upper levels assured us we had room for at least one packet of data. + * As per the SSLEngine spec, we only return one SSL packets worth of + * data. + */ + private HandshakeStatus getOutboundData(ByteBuffer dstBB) { + + Object msg = outboundList.removeFirst(); + assert(msg instanceof ByteBuffer); + + ByteBuffer bbIn = (ByteBuffer) msg; + assert(dstBB.remaining() >= bbIn.remaining()); + + dstBB.put(bbIn); + + /* + * If we have more data in the queue, it's either + * a finished message, or an indication that we need + * to call wrap again. + */ + if (hasOutboundDataInternal()) { + msg = outboundList.getFirst(); + if (msg == HandshakeStatus.FINISHED) { + outboundList.removeFirst(); // consume the message + return HandshakeStatus.FINISHED; + } else { + return HandshakeStatus.NEED_WRAP; + } + } else { + return null; + } + } + + /* + * Properly orders the output of the data written to the wrap call. + * This is only handshake data, application data goes through the + * other writeRecord. + */ + synchronized void writeRecord(EngineOutputRecord outputRecord, + Authenticator authenticator, + CipherBox writeCipher) throws IOException { + + /* + * Only output if we're still open. + */ + if (outboundClosed) { + throw new IOException("writer side was already closed."); + } + + outputRecord.write(authenticator, writeCipher); + + /* + * Did our handshakers notify that we just sent the + * Finished message? + * + * Add an "I'm finished" message to the queue. + */ + if (outputRecord.isFinishedMsg()) { + outboundList.addLast(HandshakeStatus.FINISHED); + } + } + + /* + * Output the packet info. + */ + private void dumpPacket(EngineArgs ea, boolean hsData) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + ByteBuffer bb = ea.netData.duplicate(); + + int pos = bb.position(); + bb.position(pos - ea.deltaNet()); + bb.limit(pos); + + System.out.println("[Raw write" + + (hsData ? "" : " (bb)") + "]: length = " + + bb.remaining()); + hd.encodeBuffer(bb, System.out); + } catch (IOException e) { } + } + + /* + * Properly orders the output of the data written to the wrap call. + * Only app data goes through here, handshake data goes through + * the other writeRecord. + * + * Shouldn't expect to have an IOException here. + * + * Return any determined status. + */ + synchronized HandshakeStatus writeRecord( + EngineOutputRecord outputRecord, EngineArgs ea, + Authenticator authenticator, + CipherBox writeCipher) throws IOException { + + /* + * If we have data ready to go, output this first before + * trying to consume app data. + */ + if (hasOutboundDataInternal()) { + HandshakeStatus hss = getOutboundData(ea.netData); + + if (debug != null && Debug.isOn("packet")) { + /* + * We could have put the dump in + * OutputRecord.write(OutputStream), but let's actually + * output when it's actually output by the SSLEngine. + */ + dumpPacket(ea, true); + } + + return hss; + } + + /* + * If we are closed, no more app data can be output. + * Only existing handshake data (above) can be obtained. + */ + if (outboundClosed) { + throw new IOException("The write side was already closed"); + } + + outputRecord.write(ea, authenticator, writeCipher); + + if (debug != null && Debug.isOn("packet")) { + dumpPacket(ea, false); + } + + /* + * No way new outbound handshake data got here if we're + * locked properly. + * + * We don't have any status we can return. + */ + return null; + } + + /* + * We already hold "this" lock, this is the callback from the + * outputRecord.write() above. We already know this + * writer can accept more data (outboundClosed == false), + * and the closure is sync'd. + */ + void putOutboundData(ByteBuffer bytes) { + outboundList.addLast(bytes); + } + + /* + * This is for the really rare case that someone is writing from + * the *InputRecord* before we know what to do with it. + */ + synchronized void putOutboundDataSync(ByteBuffer bytes) + throws IOException { + + if (outboundClosed) { + throw new IOException("Write side already closed"); + } + + outboundList.addLast(bytes); + } + + /* + * Non-synch'd version of this method, called by internals + */ + private boolean hasOutboundDataInternal() { + return (outboundList.size() != 0); + } + + synchronized boolean hasOutboundData() { + return hasOutboundDataInternal(); + } + + synchronized boolean isOutboundDone() { + return outboundClosed && !hasOutboundDataInternal(); + } + + synchronized void closeOutbound() { + outboundClosed = true; + } + +} diff --git a/src/sun/security/ssl/EphemeralKeyManager.java b/src/sun/security/ssl/EphemeralKeyManager.java new file mode 100644 index 00000000..47dba955 --- /dev/null +++ b/src/sun/security/ssl/EphemeralKeyManager.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.*; + +/** + * The "KeyManager" for ephemeral RSA keys. Ephemeral DH and ECDH keys + * are handled by the DHCrypt and ECDHCrypt classes, respectively. + * + * @author Andreas Sterbenz + */ +final class EphemeralKeyManager { + + // indices for the keys array below + private final static int INDEX_RSA512 = 0; + private final static int INDEX_RSA1024 = 1; + + /* + * Current cached RSA KeyPairs. Elements are never null. + * Indexed via the the constants above. + */ + private final EphemeralKeyPair[] keys = new EphemeralKeyPair[] { + new EphemeralKeyPair(null), + new EphemeralKeyPair(null), + }; + + EphemeralKeyManager() { + // empty + } + + /* + * Get a temporary RSA KeyPair. + */ + KeyPair getRSAKeyPair(boolean export, SecureRandom random) { + int length, index; + if (export) { + length = 512; + index = INDEX_RSA512; + } else { + length = 1024; + index = INDEX_RSA1024; + } + + synchronized (keys) { + KeyPair kp = keys[index].getKeyPair(); + if (kp == null) { + try { + KeyPairGenerator kgen = JsseJce.getKeyPairGenerator("RSA"); + kgen.initialize(length, random); + keys[index] = new EphemeralKeyPair(kgen.genKeyPair()); + kp = keys[index].getKeyPair(); + } catch (Exception e) { + // ignore + } + } + return kp; + } + } + + /** + * Inner class to handle storage of ephemeral KeyPairs. + */ + private static class EphemeralKeyPair { + + // maximum number of times a KeyPair is used + private final static int MAX_USE = 200; + + // maximum time interval in which the keypair is used (1 hour in ms) + private final static long USE_INTERVAL = 3600*1000; + + private KeyPair keyPair; + private int uses; + private long expirationTime; + + private EphemeralKeyPair(KeyPair keyPair) { + this.keyPair = keyPair; + expirationTime = System.currentTimeMillis() + USE_INTERVAL; + } + + /* + * Check if the KeyPair can still be used. + */ + private boolean isValid() { + return (keyPair != null) && (uses < MAX_USE) + && (System.currentTimeMillis() < expirationTime); + } + + /* + * Return the KeyPair or null if it is invalid. + */ + private KeyPair getKeyPair() { + if (isValid() == false) { + keyPair = null; + return null; + } + uses++; + return keyPair; + } + } +} diff --git a/src/sun/security/ssl/ExtensionType.java b/src/sun/security/ssl/ExtensionType.java new file mode 100644 index 00000000..9cf0dcd7 --- /dev/null +++ b/src/sun/security/ssl/ExtensionType.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.ArrayList; +import java.util.List; + +final class ExtensionType { + + final int id; + final String name; + + private ExtensionType(int id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return name; + } + + static List knownExtensions = + new ArrayList(14); + + static ExtensionType get(int id) { + for (ExtensionType ext : knownExtensions) { + if (ext.id == id) { + return ext; + } + } + return new ExtensionType(id, "type_" + id); + } + + private static ExtensionType e(int id, String name) { + ExtensionType ext = new ExtensionType(id, name); + knownExtensions.add(ext); + return ext; + } + + // extensions defined in RFC 3546 + final static ExtensionType EXT_SERVER_NAME = + e(0x0000, "server_name"); // IANA registry value: 0 + final static ExtensionType EXT_MAX_FRAGMENT_LENGTH = + e(0x0001, "max_fragment_length"); // IANA registry value: 1 + final static ExtensionType EXT_CLIENT_CERTIFICATE_URL = + e(0x0002, "client_certificate_url"); // IANA registry value: 2 + final static ExtensionType EXT_TRUSTED_CA_KEYS = + e(0x0003, "trusted_ca_keys"); // IANA registry value: 3 + final static ExtensionType EXT_TRUNCATED_HMAC = + e(0x0004, "truncated_hmac"); // IANA registry value: 4 + final static ExtensionType EXT_STATUS_REQUEST = + e(0x0005, "status_request"); // IANA registry value: 5 + + // extensions defined in RFC 4681 + final static ExtensionType EXT_USER_MAPPING = + e(0x0006, "user_mapping"); // IANA registry value: 6 + + // extensions defined in RFC 5081 + final static ExtensionType EXT_CERT_TYPE = + e(0x0009, "cert_type"); // IANA registry value: 9 + + // extensions defined in RFC 4492 (ECC) + final static ExtensionType EXT_ELLIPTIC_CURVES = + e(0x000A, "elliptic_curves"); // IANA registry value: 10 + final static ExtensionType EXT_EC_POINT_FORMATS = + e(0x000B, "ec_point_formats"); // IANA registry value: 11 + + // extensions defined in RFC 5054 + final static ExtensionType EXT_SRP = + e(0x000C, "srp"); // IANA registry value: 12 + + // extensions defined in RFC 5246 + final static ExtensionType EXT_SIGNATURE_ALGORITHMS = + e(0x000D, "signature_algorithms"); // IANA registry value: 13 + + // extension defined in RFC 7301 (ALPN) + static final ExtensionType EXT_ALPN = + e(0x0010, "application_layer_protocol_negotiation"); + // IANA registry value: 16 + + // extensions defined in RFC 5746 + final static ExtensionType EXT_RENEGOTIATION_INFO = + e(0xff01, "renegotiation_info"); // IANA registry value: 65281 +} diff --git a/src/sun/security/ssl/HandshakeHash.java b/src/sun/security/ssl/HandshakeHash.java new file mode 100644 index 00000000..ad4f6fd1 --- /dev/null +++ b/src/sun/security/ssl/HandshakeHash.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.ByteArrayOutputStream; +import java.security.*; +import java.util.Locale; + +/** + * Abstraction for the SSL/TLS hash of all handshake messages that is + * maintained to verify the integrity of the negotiation. Internally, + * it consists of an MD5 and an SHA1 digest. They are used in the client + * and server finished messages and in certificate verify messages (if sent). + * + * This class transparently deals with cloneable and non-cloneable digests. + * + * This class now supports TLS 1.2 also. The key difference for TLS 1.2 + * is that you cannot determine the hash algorithms for CertificateVerify + * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier) + * that there is no messy MD5+SHA1 digests. + * + * You need to obey these conventions when using this class: + * + * 1. protocolDetermined(version) should be called when the negotiated + * protocol version is determined. + * + * 2. Before protocolDetermined() is called, only update(), and reset() + * and setFinishedAlg() can be called. + * + * 3. After protocolDetermined() is called, reset() cannot be called. + * + * 4. After protocolDetermined() is called, if the version is pre-TLS 1.2, + * getFinishedHash() cannot be called. Otherwise, + * getMD5Clone() and getSHAClone() cannot be called. + * + * 5. getMD5Clone() and getSHAClone() can only be called after + * protocolDetermined() is called and version is pre-TLS 1.2. + * + * 6. getFinishedHash() can only be called after protocolDetermined() + * and setFinishedAlg() have been called and the version is TLS 1.2. + * + * Suggestion: Call protocolDetermined() and setFinishedAlg() + * as early as possible. + * + * Example: + *
    + * HandshakeHash hh = new HandshakeHash(...)
    + * hh.protocolDetermined(ProtocolVersion.TLS12);
    + * hh.update(clientHelloBytes);
    + * hh.setFinishedAlg("SHA-256");
    + * hh.update(serverHelloBytes);
    + * ...
    + * hh.update(CertificateVerifyBytes);
    + * ...
    + * hh.update(finished1);
    + * byte[] finDigest1 = hh.getFinishedHash();
    + * hh.update(finished2);
    + * byte[] finDigest2 = hh.getFinishedHash();
    + * 
    + */ +final class HandshakeHash { + + // Common + + // -1: unknown + // 1: <=TLS 1.1 + // 2: TLS 1.2 + private int version = -1; + private ByteArrayOutputStream data = new ByteArrayOutputStream(); + + // For TLS 1.1 + private MessageDigest md5, sha; + private final int clonesNeeded; // needs to be saved for later use + + // For TLS 1.2 + private MessageDigest finMD; + + /** + * Create a new HandshakeHash. needCertificateVerify indicates whether + * a hash for the certificate verify message is required. + */ + HandshakeHash(boolean needCertificateVerify) { + clonesNeeded = needCertificateVerify ? 3 : 2; + } + + void update(byte[] b, int offset, int len) { + switch (version) { + case 1: + md5.update(b, offset, len); + sha.update(b, offset, len); + break; + default: + if (finMD != null) { + finMD.update(b, offset, len); + } + data.write(b, offset, len); + break; + } + } + + /** + * Reset the remaining digests. Note this does *not* reset the number of + * digest clones that can be obtained. Digests that have already been + * cloned and are gone remain gone. + */ + void reset() { + if (version != -1) { + throw new RuntimeException( + "reset() can be only be called before protocolDetermined"); + } + data.reset(); + } + + + void protocolDetermined(ProtocolVersion pv) { + + // Do not set again, will ignore + if (version != -1) return; + + version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1; + switch (version) { + case 1: + // initiate md5, sha and call update on saved array + try { + md5 = CloneableDigest.getDigest("MD5", clonesNeeded); + sha = CloneableDigest.getDigest("SHA", clonesNeeded); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException + ("Algorithm MD5 or SHA not available", e); + } + byte[] bytes = data.toByteArray(); + update(bytes, 0, bytes.length); + break; + case 2: + break; + } + } + + ///////////////////////////////////////////////////////////// + // Below are old methods for pre-TLS 1.1 + ///////////////////////////////////////////////////////////// + + /** + * Return a new MD5 digest updated with all data hashed so far. + */ + MessageDigest getMD5Clone() { + if (version != 1) { + throw new RuntimeException( + "getMD5Clone() can be only be called for TLS 1.1"); + } + return cloneDigest(md5); + } + + /** + * Return a new SHA digest updated with all data hashed so far. + */ + MessageDigest getSHAClone() { + if (version != 1) { + throw new RuntimeException( + "getSHAClone() can be only be called for TLS 1.1"); + } + return cloneDigest(sha); + } + + private static MessageDigest cloneDigest(MessageDigest digest) { + try { + return (MessageDigest)digest.clone(); + } catch (CloneNotSupportedException e) { + // cannot occur for digests generated via CloneableDigest + throw new RuntimeException("Could not clone digest", e); + } + } + + ///////////////////////////////////////////////////////////// + // Below are new methods for TLS 1.2 + ///////////////////////////////////////////////////////////// + + private static String normalizeAlgName(String alg) { + alg = alg.toUpperCase(Locale.US); + if (alg.startsWith("SHA")) { + if (alg.length() == 3) { + return "SHA-1"; + } + if (alg.charAt(3) != '-') { + return "SHA-" + alg.substring(3); + } + } + return alg; + } + /** + * Specifies the hash algorithm used in Finished. This should be called + * based in info in ServerHello. + * Can be called multiple times. + */ + void setFinishedAlg(String s) { + if (s == null) { + throw new RuntimeException( + "setFinishedAlg's argument cannot be null"); + } + + // Can be called multiple times, but only set once + if (finMD != null) return; + + try { + finMD = CloneableDigest.getDigest(normalizeAlgName(s), 2); + } catch (NoSuchAlgorithmException e) { + throw new Error(e); + } + finMD.update(data.toByteArray()); + } + + byte[] getAllHandshakeMessages() { + return data.toByteArray(); + } + + /** + * Calculates the hash in Finished. Must be called after setFinishedAlg(). + * This method can be called twice, for Finished messages of the server + * side and client side respectively. + */ + byte[] getFinishedHash() { + try { + return cloneDigest(finMD).digest(); + } catch (Exception e) { + throw new Error("BAD"); + } + } +} + +/** + * A wrapper for MessageDigests that simulates cloning of non-cloneable + * digests. It uses the standard MessageDigest API and therefore can be used + * transparently in place of a regular digest. + * + * Note that we extend the MessageDigest class directly rather than + * MessageDigestSpi. This works because MessageDigest was originally designed + * this way in the JDK 1.1 days which allows us to avoid creating an internal + * provider. + * + * It can be "cloned" a limited number of times, which is specified at + * construction time. This is achieved by internally maintaining n digests + * in parallel. Consequently, it is only 1/n-th times as fast as the original + * digest. + * + * Example: + * MessageDigest md = CloneableDigest.getDigest("SHA", 2); + * md.update(data1); + * MessageDigest md2 = (MessageDigest)md.clone(); + * md2.update(data2); + * byte[] d1 = md2.digest(); // digest of data1 || data2 + * md.update(data3); + * byte[] d2 = md.digest(); // digest of data1 || data3 + * + * This class is not thread safe. + * + */ +final class CloneableDigest extends MessageDigest implements Cloneable { + + /** + * The individual MessageDigests. Initially, all elements are non-null. + * When clone() is called, the non-null element with the maximum index is + * returned and the array element set to null. + * + * All non-null element are always in the same state. + */ + private final MessageDigest[] digests; + + private CloneableDigest(MessageDigest digest, int n, String algorithm) + throws NoSuchAlgorithmException { + super(algorithm); + digests = new MessageDigest[n]; + digests[0] = digest; + for (int i = 1; i < n; i++) { + digests[i] = JsseJce.getMessageDigest(algorithm); + } + } + + /** + * Return a MessageDigest for the given algorithm that can be cloned the + * specified number of times. If the default implementation supports + * cloning, it is returned. Otherwise, an instance of this class is + * returned. + */ + static MessageDigest getDigest(String algorithm, int n) + throws NoSuchAlgorithmException { + MessageDigest digest = JsseJce.getMessageDigest(algorithm); + try { + digest.clone(); + // already cloneable, use it + return digest; + } catch (CloneNotSupportedException e) { + return new CloneableDigest(digest, n, algorithm); + } + } + + /** + * Check if this object is still usable. If it has already been cloned the + * maximum number of times, there are no digests left and this object can no + * longer be used. + */ + private void checkState() { + // XXX handshaking currently doesn't stop updating hashes... + // if (digests[0] == null) { + // throw new IllegalStateException("no digests left"); + // } + } + + @Override + protected int engineGetDigestLength() { + checkState(); + return digests[0].getDigestLength(); + } + + @Override + protected void engineUpdate(byte b) { + checkState(); + for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { + digests[i].update(b); + } + } + + @Override + protected void engineUpdate(byte[] b, int offset, int len) { + checkState(); + for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { + digests[i].update(b, offset, len); + } + } + + @Override + protected byte[] engineDigest() { + checkState(); + byte[] digest = digests[0].digest(); + digestReset(); + return digest; + } + + @Override + protected int engineDigest(byte[] buf, int offset, int len) + throws DigestException { + checkState(); + int n = digests[0].digest(buf, offset, len); + digestReset(); + return n; + } + + /** + * Reset all digests after a digest() call. digests[0] has already been + * implicitly reset by the digest() call and does not need to be reset + * again. + */ + private void digestReset() { + for (int i = 1; (i < digests.length) && (digests[i] != null); i++) { + digests[i].reset(); + } + } + + @Override + protected void engineReset() { + checkState(); + for (int i = 0; (i < digests.length) && (digests[i] != null); i++) { + digests[i].reset(); + } + } + + @Override + public Object clone() { + checkState(); + for (int i = digests.length - 1; i >= 0; i--) { + if (digests[i] != null) { + MessageDigest digest = digests[i]; + digests[i] = null; + return digest; + } + } + // cannot occur + throw new InternalError(); + } + +} diff --git a/src/sun/security/ssl/HandshakeInStream.java b/src/sun/security/ssl/HandshakeInStream.java new file mode 100644 index 00000000..4b1ed462 --- /dev/null +++ b/src/sun/security/ssl/HandshakeInStream.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.InputStream; +import java.io.IOException; + +import javax.net.ssl.SSLException; + +/** + * InputStream for handshake data, used internally only. Contains the + * handshake message buffer and methods to parse them. + * + * Once a new handshake record arrives, it is buffered in this class until + * processed by the Handshaker. The buffer may also contain incomplete + * handshake messages in case the message is split across multiple records. + * Handshaker.process_record deals with all that. It may also contain + * handshake messages larger than the default buffer size (e.g. large + * certificate messages). The buffer is grown dynamically to handle that + * (see InputRecord.queueHandshake()). + * + * Note that the InputRecord used as a buffer here is separate from the + * AppInStream.r, which is where data from the socket is initially read + * into. This is because once the initial handshake has been completed, + * handshake and application data messages may be interleaved arbitrarily + * and must be processed independently. + * + * @author David Brownell + */ +public class HandshakeInStream extends InputStream { + + InputRecord r; + + /* + * Construct the stream; we'll be accumulating hashes of the + * input records using two sets of digests. + */ + HandshakeInStream(HandshakeHash handshakeHash) { + r = new InputRecord(); + r.setHandshakeHash(handshakeHash); + } + + + // overridden InputStream methods + + /* + * Return the number of bytes available for read(). + * + * Note that this returns the bytes remaining in the buffer, not + * the bytes remaining in the current handshake message. + */ + @Override + public int available() { + return r.available(); + } + + /* + * Get a byte of handshake data. + */ + @Override + public int read() throws IOException { + int n = r.read(); + if (n == -1) { + throw new SSLException("Unexpected end of handshake data"); + } + return n; + } + + /* + * Get a bunch of bytes of handshake data. + */ + @Override + public int read(byte b [], int off, int len) throws IOException { + // we read from a ByteArrayInputStream, it always returns the + // data in a single read if enough is available + int n = r.read(b, off, len); + if (n != len) { + throw new SSLException("Unexpected end of handshake data"); + } + return n; + } + + /* + * Skip some handshake data. + */ + @Override + public long skip(long n) throws IOException { + return r.skip(n); + } + + /* + * Mark/ reset code, implemented using InputRecord mark/ reset. + * + * Note that it currently provides only a limited mark functionality + * and should be used with care (once a new handshake record has been + * read, data that has already been consumed is lost even if marked). + */ + + @Override + public void mark(int readlimit) { + r.mark(readlimit); + } + + @Override + public void reset() throws IOException { + r.reset(); + } + + @Override + public boolean markSupported() { + return true; + } + + + // handshake management functions + + /* + * Here's an incoming record with handshake data. Queue the contents; + * it might be one or more entire messages, complete a message that's + * partly queued, or both. + */ + void incomingRecord(InputRecord in) throws IOException { + r.queueHandshake(in); + } + + /* + * Hash any data we've consumed but not yet hashed. Useful mostly + * for processing client certificate messages (so we can check the + * immediately following cert verify message) and finished messages + * (so we can compute our own finished message). + */ + void digestNow() { + r.doHashes(); + } + + /* + * Do more than skip that handshake data ... totally ignore it. + * The difference is that the data does not get hashed. + */ + void ignore(int n) { + r.ignore(n); + } + + + // Message parsing methods + + /* + * Read 8, 16, 24, and 32 bit SSL integer data types, encoded + * in standard big-endian form. + */ + + int getInt8() throws IOException { + return read(); + } + + int getInt16() throws IOException { + return (getInt8() << 8) | getInt8(); + } + + int getInt24() throws IOException { + return (getInt8() << 16) | (getInt8() << 8) | getInt8(); + } + + int getInt32() throws IOException { + return (getInt8() << 24) | (getInt8() << 16) + | (getInt8() << 8) | getInt8(); + } + + /* + * Read byte vectors with 8, 16, and 24 bit length encodings. + */ + + byte[] getBytes8() throws IOException { + int len = getInt8(); + verifyLength(len); + byte b[] = new byte[len]; + + read(b, 0, len); + return b; + } + + public byte[] getBytes16() throws IOException { + int len = getInt16(); + verifyLength(len); + byte b[] = new byte[len]; + + read(b, 0, len); + return b; + } + + byte[] getBytes24() throws IOException { + int len = getInt24(); + verifyLength(len); + byte b[] = new byte[len]; + + read(b, 0, len); + return b; + } + + // Is a length greater than available bytes in the record? + private void verifyLength(int len) throws SSLException { + if (len > available()) { + throw new SSLException( + "Not enough data to fill declared vector size"); + } + } + +} diff --git a/src/sun/security/ssl/HandshakeMessage.java b/src/sun/security/ssl/HandshakeMessage.java new file mode 100644 index 00000000..65489e46 --- /dev/null +++ b/src/sun/security/ssl/HandshakeMessage.java @@ -0,0 +1,2051 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import java.lang.reflect.*; + +import javax.security.auth.x500.X500Principal; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.DHPublicKeySpec; + +import javax.net.ssl.*; + +import sun.security.internal.spec.TlsPrfParameterSpec; +import sun.security.ssl.CipherSuite.*; + +import static sun.security.ssl.CipherSuite.PRF.P_NONE; + +import sun.security.util.KeyUtil; + +/** + * Many data structures are involved in the handshake messages. These + * classes are used as structures, with public data members. They are + * not visible outside the SSL package. + * + * Handshake messages all have a common header format, and they are all + * encoded in a "handshake data" SSL record substream. The base class + * here (HandshakeMessage) provides a common framework and records the + * SSL record type of the particular handshake message. + * + * This file contains subclasses for all the basic handshake messages. + * All handshake messages know how to encode and decode themselves on + * SSL streams; this facilitates using the same code on SSL client and + * server sides, although they don't send and receive the same messages. + * + * Messages also know how to print themselves, which is quite handy + * for debugging. They always identify their type, and can optionally + * dump all of their content. + * + * @author David Brownell + */ +public abstract class HandshakeMessage { + + HandshakeMessage() { } + + // enum HandshakeType: + static final byte ht_hello_request = 0; + static final byte ht_client_hello = 1; + static final byte ht_server_hello = 2; + + static final byte ht_certificate = 11; + static final byte ht_server_key_exchange = 12; + static final byte ht_certificate_request = 13; + static final byte ht_server_hello_done = 14; + static final byte ht_certificate_verify = 15; + static final byte ht_client_key_exchange = 16; + + static final byte ht_finished = 20; + + /* Class and subclass dynamic debugging support */ + public static final Debug debug = Debug.getInstance("ssl"); + + /** + * Utility method to convert a BigInteger to a byte array in unsigned + * format as needed in the handshake messages. BigInteger uses + * 2's complement format, i.e. it prepends an extra zero if the MSB + * is set. We remove that. + */ + static byte[] toByteArray(BigInteger bi) { + byte[] b = bi.toByteArray(); + if ((b.length > 1) && (b[0] == 0)) { + int n = b.length - 1; + byte[] newarray = new byte[n]; + System.arraycopy(b, 1, newarray, 0, n); + b = newarray; + } + return b; + } + + /* + * SSL 3.0 MAC padding constants. + * Also used by CertificateVerify and Finished during the handshake. + */ + static final byte[] MD5_pad1 = genPad(0x36, 48); + static final byte[] MD5_pad2 = genPad(0x5c, 48); + + static final byte[] SHA_pad1 = genPad(0x36, 40); + static final byte[] SHA_pad2 = genPad(0x5c, 40); + + private static byte[] genPad(int b, int count) { + byte[] padding = new byte[count]; + Arrays.fill(padding, (byte)b); + return padding; + } + + /* + * Write a handshake message on the (handshake) output stream. + * This is just a four byte header followed by the data. + * + * NOTE that huge messages -- notably, ones with huge cert + * chains -- are handled correctly. + */ + final void write(HandshakeOutStream s) throws IOException { + int len = messageLength(); + if (len >= Record.OVERFLOW_OF_INT24) { + throw new SSLException("Handshake message too big" + + ", type = " + messageType() + ", len = " + len); + } + s.write(messageType()); + s.putInt24(len); + send(s); + } + + /* + * Subclasses implement these methods so those kinds of + * messages can be emitted. Base class delegates to subclass. + */ + abstract int messageType(); + abstract int messageLength(); + abstract void send(HandshakeOutStream s) throws IOException; + + /* + * Write a descriptive message on the output stream; for debugging. + */ + abstract void print(PrintStream p) throws IOException; + +// +// NOTE: the rest of these classes are nested within this one, and are +// imported by other classes in this package. There are a few other +// handshake message classes, not neatly nested here because of current +// licensing requirement for native (RSA) methods. They belong here, +// but those native methods complicate things a lot! +// + + +/* + * HelloRequest ... SERVER --> CLIENT + * + * Server can ask the client to initiate a new handshake, e.g. to change + * session parameters after a connection has been (re)established. + */ +static final class HelloRequest extends HandshakeMessage { + @Override + int messageType() { return ht_hello_request; } + + HelloRequest() { } + + HelloRequest(HandshakeInStream in) throws IOException + { + // nothing in this message + } + + @Override + int messageLength() { return 0; } + + @Override + void send(HandshakeOutStream out) throws IOException + { + // nothing in this messaage + } + + @Override + void print(PrintStream out) throws IOException + { + out.println("*** HelloRequest (empty)"); + } + +} + + +/* + * ClientHello ... CLIENT --> SERVER + * + * Client initiates handshake by telling server what it wants, and what it + * can support (prioritized by what's first in the ciphe suite list). + * + * By RFC2246:7.4.1.2 it's explicitly anticipated that this message + * will have more data added at the end ... e.g. what CAs the client trusts. + * Until we know how to parse it, we will just read what we know + * about, and let our caller handle the jumps over unknown data. + */ +static final class ClientHello extends HandshakeMessage { + + ProtocolVersion protocolVersion; + RandomCookie clnt_random; + SessionId sessionId; + private CipherSuiteList cipherSuites; + byte[] compression_methods; + + HelloExtensions extensions = new HelloExtensions(); + + private final static byte[] NULL_COMPRESSION = new byte[] {0}; + + ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, + SessionId sessionId, CipherSuiteList cipherSuites) { + + this.protocolVersion = protocolVersion; + this.sessionId = sessionId; + this.cipherSuites = cipherSuites; + + if (cipherSuites.containsEC()) { + extensions.add(SupportedEllipticCurvesExtension.DEFAULT); + extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT); + } + + clnt_random = new RandomCookie(generator); + compression_methods = NULL_COMPRESSION; + } + + ClientHello(HandshakeInStream s, int messageLength) throws IOException { + protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); + clnt_random = new RandomCookie(s); + sessionId = new SessionId(s.getBytes8()); + cipherSuites = new CipherSuiteList(s); + compression_methods = s.getBytes8(); + if (messageLength() != messageLength) { + extensions = new HelloExtensions(s); + } + } + + CipherSuiteList getCipherSuites() { + return cipherSuites; + } + + // add renegotiation_info extension + void addRenegotiationInfoExtension(byte[] clientVerifyData) { + HelloExtension renegotiationInfo = new RenegotiationInfoExtension( + clientVerifyData, new byte[0]); + extensions.add(renegotiationInfo); + } + + // add server_name extension + void addSNIExtension(List serverNames) { + try { + extensions.add(new ServerNameExtension(serverNames)); + } catch (IOException ioe) { + // ignore the exception and return + } + } + + // add signature_algorithm extension + void addSignatureAlgorithmsExtension( + Collection algorithms) { + HelloExtension signatureAlgorithm = + new SignatureAlgorithmsExtension(algorithms); + extensions.add(signatureAlgorithm); + } + + // add application_layer_protocol_negotiation extension + void addALPNExtension(String[] applicationProtocols) throws SSLException { + extensions.add(new ALPNExtension(applicationProtocols)); + } + + @Override + int messageType() { return ht_client_hello; } + + @Override + int messageLength() { + /* + * Add fixed size parts of each field... + * version + random + session + cipher + compress + */ + return (2 + 32 + 1 + 2 + 1 + + sessionId.length() /* ... + variable parts */ + + (cipherSuites.size() * 2) + + compression_methods.length) + + extensions.length(); + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt8(protocolVersion.major); + s.putInt8(protocolVersion.minor); + clnt_random.send(s); + s.putBytes8(sessionId.getId()); + cipherSuites.send(s); + s.putBytes8(compression_methods); + extensions.send(s); + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** ClientHello, " + protocolVersion); + + if (debug != null && Debug.isOn("verbose")) { + s.print("RandomCookie: "); + clnt_random.print(s); + + s.print("Session ID: "); + s.println(sessionId); + + s.println("Cipher Suites: " + cipherSuites); + + Debug.println(s, "Compression Methods", compression_methods); + extensions.print(s); + s.println("***"); + } + } +} + +/* + * ServerHello ... SERVER --> CLIENT + * + * Server chooses protocol options from among those it supports and the + * client supports. Then it sends the basic session descriptive parameters + * back to the client. + */ +static final +class ServerHello extends HandshakeMessage +{ + @Override + int messageType() { return ht_server_hello; } + + ProtocolVersion protocolVersion; + RandomCookie svr_random; + SessionId sessionId; + CipherSuite cipherSuite; + byte compression_method; + HelloExtensions extensions = new HelloExtensions(); + + ServerHello() { + // empty + } + + ServerHello(HandshakeInStream input, int messageLength) + throws IOException { + protocolVersion = ProtocolVersion.valueOf(input.getInt8(), + input.getInt8()); + svr_random = new RandomCookie(input); + sessionId = new SessionId(input.getBytes8()); + cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8()); + compression_method = (byte)input.getInt8(); + if (messageLength() != messageLength) { + extensions = new HelloExtensions(input); + } + } + + @Override + int messageLength() + { + // almost fixed size, except session ID and extensions: + // major + minor = 2 + // random = 32 + // session ID len field = 1 + // cipher suite + compression = 3 + // extensions: if present, 2 + length of extensions + return 38 + sessionId.length() + extensions.length(); + } + + @Override + void send(HandshakeOutStream s) throws IOException + { + s.putInt8(protocolVersion.major); + s.putInt8(protocolVersion.minor); + svr_random.send(s); + s.putBytes8(sessionId.getId()); + s.putInt8(cipherSuite.id >> 8); + s.putInt8(cipherSuite.id & 0xff); + s.putInt8(compression_method); + extensions.send(s); + } + + @Override + void print(PrintStream s) throws IOException + { + s.println("*** ServerHello, " + protocolVersion); + + if (debug != null && Debug.isOn("verbose")) { + s.print("RandomCookie: "); + svr_random.print(s); + + s.print("Session ID: "); + s.println(sessionId); + + s.println("Cipher Suite: " + cipherSuite); + s.println("Compression Method: " + compression_method); + extensions.print(s); + s.println("***"); + } + } +} + + +/* + * CertificateMsg ... send by both CLIENT and SERVER + * + * Each end of a connection may need to pass its certificate chain to + * the other end. Such chains are intended to validate an identity with + * reference to some certifying authority. Examples include companies + * like Verisign, or financial institutions. There's some control over + * the certifying authorities which are sent. + * + * NOTE: that these messages might be huge, taking many handshake records. + * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 + * bytes each ... up to 2^32 records sent on the output stream. + */ +static final +class CertificateMsg extends HandshakeMessage +{ + @Override + int messageType() { return ht_certificate; } + + private X509Certificate[] chain; + + private List encodedChain; + + private int messageLength; + + CertificateMsg(X509Certificate[] certs) { + chain = certs; + } + + CertificateMsg(HandshakeInStream input) throws IOException { + int chainLen = input.getInt24(); + List v = new ArrayList<>(4); + + CertificateFactory cf = null; + while (chainLen > 0) { + byte[] cert = input.getBytes24(); + chainLen -= (3 + cert.length); + try { + if (cf == null) { + cf = CertificateFactory.getInstance("X.509"); + } + v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); + } catch (CertificateException e) { + throw (SSLProtocolException)new SSLProtocolException( + e.getMessage()).initCause(e); + } + } + + chain = v.toArray(new X509Certificate[v.size()]); + } + + @Override + int messageLength() { + if (encodedChain == null) { + messageLength = 3; + encodedChain = new ArrayList(chain.length); + try { + for (X509Certificate cert : chain) { + byte[] b = cert.getEncoded(); + encodedChain.add(b); + messageLength += b.length + 3; + } + } catch (CertificateEncodingException e) { + encodedChain = null; + throw new RuntimeException("Could not encode certificates", e); + } + } + return messageLength; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt24(messageLength() - 3); + for (byte[] b : encodedChain) { + s.putBytes24(b); + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** Certificate chain"); + + if (debug != null && Debug.isOn("verbose")) { + for (int i = 0; i < chain.length; i++) + s.println("chain [" + i + "] = " + chain[i]); + s.println("***"); + } + } + + X509Certificate[] getCertificateChain() { + return chain.clone(); + } +} + +/* + * ServerKeyExchange ... SERVER --> CLIENT + * + * The cipher suite selected, when combined with the certificate exchanged, + * implies one of several different kinds of key exchange. Most current + * cipher suites require the server to send more than its certificate. + * + * The primary exceptions are when a server sends an encryption-capable + * RSA public key in its cert, to be used with RSA (or RSA_export) key + * exchange; and when a server sends its Diffie-Hellman cert. Those kinds + * of key exchange do not require a ServerKeyExchange message. + * + * Key exchange can be viewed as having three modes, which are explicit + * for the Diffie-Hellman flavors and poorly specified for RSA ones: + * + * - "Ephemeral" keys. Here, a "temporary" key is allocated by the + * server, and signed. Diffie-Hellman keys signed using RSA or + * DSS are ephemeral (DHE flavor). RSA keys get used to do the same + * thing, to cut the key size down to 512 bits (export restrictions) + * or for signing-only RSA certificates. + * + * - Anonymity. Here no server certificate is sent, only the public + * key of the server. This case is subject to man-in-the-middle + * attacks. This can be done with Diffie-Hellman keys (DH_anon) or + * with RSA keys, but is only used in SSLv3 for DH_anon. + * + * - "Normal" case. Here a server certificate is sent, and the public + * key there is used directly in exchanging the premaster secret. + * For example, Diffie-Hellman "DH" flavor, and any RSA flavor with + * only 512 bit keys. + * + * If a server certificate is sent, there is no anonymity. However, + * when a certificate is sent, ephemeral keys may still be used to + * exchange the premaster secret. That's how RSA_EXPORT often works, + * as well as how the DHE_* flavors work. + */ +static abstract class ServerKeyExchange extends HandshakeMessage +{ + @Override + int messageType() { return ht_server_key_exchange; } +} + + +/* + * Using RSA for Key Exchange: exchange a session key that's not as big + * as the signing-only key. Used for export applications, since exported + * RSA encryption keys can't be bigger than 512 bytes. + * + * This is never used when keys are 512 bits or smaller, and isn't used + * on "US Domestic" ciphers in any case. + */ +static final +class RSA_ServerKeyExchange extends ServerKeyExchange +{ + private byte rsa_modulus[]; // 1 to 2^16 - 1 bytes + private byte rsa_exponent[]; // 1 to 2^16 - 1 bytes + + private Signature signature; + private byte[] signatureBytes; + + /* + * Hash the nonces and the ephemeral RSA public key. + */ + private void updateSignature(byte clntNonce[], byte svrNonce[]) + throws SignatureException { + int tmp; + + signature.update(clntNonce); + signature.update(svrNonce); + + tmp = rsa_modulus.length; + signature.update((byte)(tmp >> 8)); + signature.update((byte)(tmp & 0x0ff)); + signature.update(rsa_modulus); + + tmp = rsa_exponent.length; + signature.update((byte)(tmp >> 8)); + signature.update((byte)(tmp & 0x0ff)); + signature.update(rsa_exponent); + } + + + /* + * Construct an RSA server key exchange message, using data + * known _only_ to the server. + * + * The client knows the public key corresponding to this private + * key, from the Certificate message sent previously. To comply + * with US export regulations we use short RSA keys ... either + * long term ones in the server's X509 cert, or else ephemeral + * ones sent using this message. + */ + RSA_ServerKeyExchange(PublicKey ephemeralKey, PrivateKey privateKey, + RandomCookie clntNonce, RandomCookie svrNonce, SecureRandom sr) + throws GeneralSecurityException { + RSAPublicKeySpec rsaKey = JsseJce.getRSAPublicKeySpec(ephemeralKey); + rsa_modulus = toByteArray(rsaKey.getModulus()); + rsa_exponent = toByteArray(rsaKey.getPublicExponent()); + signature = RSASignature.getInstance(); + signature.initSign(privateKey, sr); + updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); + signatureBytes = signature.sign(); + } + + + /* + * Parse an RSA server key exchange message, using data known + * to the client (and, in some situations, eavesdroppers). + */ + RSA_ServerKeyExchange(HandshakeInStream input) + throws IOException, NoSuchAlgorithmException { + signature = RSASignature.getInstance(); + rsa_modulus = input.getBytes16(); + rsa_exponent = input.getBytes16(); + signatureBytes = input.getBytes16(); + } + + /* + * Get the ephemeral RSA public key that will be used in this + * SSL connection. + */ + PublicKey getPublicKey() { + try { + KeyFactory kfac = JsseJce.getKeyFactory("RSA"); + // modulus and exponent are always positive + RSAPublicKeySpec kspec = new RSAPublicKeySpec( + new BigInteger(1, rsa_modulus), + new BigInteger(1, rsa_exponent)); + return kfac.generatePublic(kspec); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /* + * Verify the signed temporary key using the hashes computed + * from it and the two nonces. This is called by clients + * with "exportable" RSA flavors. + */ + boolean verify(PublicKey certifiedKey, RandomCookie clntNonce, + RandomCookie svrNonce) throws GeneralSecurityException { + signature.initVerify(certifiedKey); + updateSignature(clntNonce.random_bytes, svrNonce.random_bytes); + return signature.verify(signatureBytes); + } + + @Override + int messageLength() { + return 6 + rsa_modulus.length + rsa_exponent.length + + signatureBytes.length; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putBytes16(rsa_modulus); + s.putBytes16(rsa_exponent); + s.putBytes16(signatureBytes); + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** RSA ServerKeyExchange"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "RSA Modulus", rsa_modulus); + Debug.println(s, "RSA Public Exponent", rsa_exponent); + } + } +} + + +/* + * Using Diffie-Hellman algorithm for key exchange. All we really need to + * do is securely get Diffie-Hellman keys (using the same P, G parameters) + * to our peer, then we automatically have a shared secret without need + * to exchange any more data. (D-H only solutions, such as SKIP, could + * eliminate key exchange negotiations and get faster connection setup. + * But they still need a signature algorithm like DSS/DSA to support the + * trusted distribution of keys without relying on unscalable physical + * key distribution systems.) + * + * This class supports several DH-based key exchange algorithms, though + * perhaps eventually each deserves its own class. Notably, this has + * basic support for DH_anon and its DHE_DSS and DHE_RSA signed variants. + */ +static final +class DH_ServerKeyExchange extends ServerKeyExchange +{ + // Fix message encoding, see 4348279 + private final static boolean dhKeyExchangeFix = + Debug.getBooleanProperty("com.sun.net.ssl.dhKeyExchangeFix", true); + + private byte dh_p []; // 1 to 2^16 - 1 bytes + private byte dh_g []; // 1 to 2^16 - 1 bytes + private byte dh_Ys []; // 1 to 2^16 - 1 bytes + + private byte signature []; + + // protocol version being established using this ServerKeyExchange message + ProtocolVersion protocolVersion; + + // the preferable signature algorithm used by this ServerKeyExchange message + private SignatureAndHashAlgorithm preferableSignatureAlgorithm; + + /* + * Construct from initialized DH key object, for DH_anon + * key exchange. + */ + DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + this.preferableSignatureAlgorithm = null; + + // The DH key has been validated in the constructor of DHCrypt. + setValues(obj); + signature = null; + } + + /* + * Construct from initialized DH key object and the key associated + * with the cert chain which was sent ... for DHE_DSS and DHE_RSA + * key exchange. (Constructor called by server.) + */ + DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte clntNonce[], + byte svrNonce[], SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm, + ProtocolVersion protocolVersion) throws GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + // The DH key has been validated in the constructor of DHCrypt. + setValues(obj); + + Signature sig; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + this.preferableSignatureAlgorithm = signAlgorithm; + sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); + } else { + this.preferableSignatureAlgorithm = null; + if (key.getAlgorithm().equals("DSA")) { + sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); + } else { + sig = RSASignature.getInstance(); + } + } + + sig.initSign(key, sr); + updateSignature(sig, clntNonce, svrNonce); + signature = sig.sign(); + } + + /* + * Construct a DH_ServerKeyExchange message from an input + * stream, as if sent from server to client for use with + * DH_anon key exchange + */ + DH_ServerKeyExchange(HandshakeInStream input, + ProtocolVersion protocolVersion) + throws IOException, GeneralSecurityException { + + this.protocolVersion = protocolVersion; + this.preferableSignatureAlgorithm = null; + + dh_p = input.getBytes16(); + dh_g = input.getBytes16(); + dh_Ys = input.getBytes16(); + KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), + new BigInteger(1, dh_p), + new BigInteger(1, dh_g))); + + signature = null; + } + + /* + * Construct a DH_ServerKeyExchange message from an input stream + * and a certificate, as if sent from server to client for use with + * DHE_DSS or DHE_RSA key exchange. (Called by client.) + */ + DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey, + byte clntNonce[], byte svrNonce[], int messageSize, + Collection localSupportedSignAlgs, + ProtocolVersion protocolVersion) + throws IOException, GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + // read params: ServerDHParams + dh_p = input.getBytes16(); + dh_g = input.getBytes16(); + dh_Ys = input.getBytes16(); + KeyUtil.validate(new DHPublicKeySpec(new BigInteger(1, dh_Ys), + new BigInteger(1, dh_p), + new BigInteger(1, dh_g))); + + // read the signature and hash algorithm + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, 0); + + // Is it a local supported signature algorithm? + if (!localSupportedSignAlgs.contains( + preferableSignatureAlgorithm)) { + throw new SSLHandshakeException( + "Unsupported SignatureAndHashAlgorithm in " + + "ServerKeyExchange message"); + } + } else { + this.preferableSignatureAlgorithm = null; + } + + // read the signature + byte signature[]; + if (dhKeyExchangeFix) { + signature = input.getBytes16(); + } else { + messageSize -= (dh_p.length + 2); + messageSize -= (dh_g.length + 2); + messageSize -= (dh_Ys.length + 2); + + signature = new byte[messageSize]; + input.read(signature); + } + + Signature sig; + String algorithm = publicKey.getAlgorithm(); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sig = JsseJce.getSignature( + preferableSignatureAlgorithm.getAlgorithmName()); + } else { + switch (algorithm) { + case "DSA": + sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); + break; + case "RSA": + sig = RSASignature.getInstance(); + break; + default: + throw new SSLKeyException("neither an RSA or a DSA key"); + } + } + + sig.initVerify(publicKey); + updateSignature(sig, clntNonce, svrNonce); + + if (sig.verify(signature) == false ) { + throw new SSLKeyException("Server D-H key verification failed"); + } + } + + /* Return the Diffie-Hellman modulus */ + BigInteger getModulus() { + return new BigInteger(1, dh_p); + } + + /* Return the Diffie-Hellman base/generator */ + BigInteger getBase() { + return new BigInteger(1, dh_g); + } + + /* Return the server's Diffie-Hellman public key */ + BigInteger getServerPublicKey() { + return new BigInteger(1, dh_Ys); + } + + /* + * Update sig with nonces and Diffie-Hellman public key. + */ + private void updateSignature(Signature sig, byte clntNonce[], + byte svrNonce[]) throws SignatureException { + int tmp; + + sig.update(clntNonce); + sig.update(svrNonce); + + tmp = dh_p.length; + sig.update((byte)(tmp >> 8)); + sig.update((byte)(tmp & 0x0ff)); + sig.update(dh_p); + + tmp = dh_g.length; + sig.update((byte)(tmp >> 8)); + sig.update((byte)(tmp & 0x0ff)); + sig.update(dh_g); + + tmp = dh_Ys.length; + sig.update((byte)(tmp >> 8)); + sig.update((byte)(tmp & 0x0ff)); + sig.update(dh_Ys); + } + + private void setValues(DHCrypt obj) { + dh_p = toByteArray(obj.getModulus()); + dh_g = toByteArray(obj.getBase()); + dh_Ys = toByteArray(obj.getPublicKey()); + } + + @Override + int messageLength() { + int temp = 6; // overhead for p, g, y(s) values. + + temp += dh_p.length; + temp += dh_g.length; + temp += dh_Ys.length; + + if (signature != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + temp += SignatureAndHashAlgorithm.sizeInRecord(); + } + + temp += signature.length; + if (dhKeyExchangeFix) { + temp += 2; + } + } + + return temp; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putBytes16(dh_p); + s.putBytes16(dh_g); + s.putBytes16(dh_Ys); + + if (signature != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.putInt8(preferableSignatureAlgorithm.getHashValue()); + s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); + } + + if (dhKeyExchangeFix) { + s.putBytes16(signature); + } else { + s.write(signature); + } + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** Diffie-Hellman ServerKeyExchange"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "DH Modulus", dh_p); + Debug.println(s, "DH Base", dh_g); + Debug.println(s, "Server DH Public Key", dh_Ys); + + if (signature == null) { + s.println("Anonymous"); + } else { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.println("Signature Algorithm " + + preferableSignatureAlgorithm.getAlgorithmName()); + } + + s.println("Signed with a DSA or RSA public key"); + } + } + } +} + +/* + * ECDH server key exchange message. Sent by the server for ECDHE and ECDH_anon + * ciphersuites to communicate its ephemeral public key (including the + * EC domain parameters). + * + * We support named curves only, no explicitly encoded curves. + */ +static final +class ECDH_ServerKeyExchange extends ServerKeyExchange { + + // constants for ECCurveType + private final static int CURVE_EXPLICIT_PRIME = 1; + private final static int CURVE_EXPLICIT_CHAR2 = 2; + private final static int CURVE_NAMED_CURVE = 3; + + // id of the curve we are using + private int curveId; + // encoded public point + private byte[] pointBytes; + + // signature bytes (or null if anonymous) + private byte[] signatureBytes; + + // public key object encapsulated in this message + private ECPublicKey publicKey; + + // protocol version being established using this ServerKeyExchange message + ProtocolVersion protocolVersion; + + // the preferable signature algorithm used by this ServerKeyExchange message + private SignatureAndHashAlgorithm preferableSignatureAlgorithm; + + ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey, + byte[] clntNonce, byte[] svrNonce, SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm, + ProtocolVersion protocolVersion) throws GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + publicKey = (ECPublicKey)obj.getPublicKey(); + ECParameterSpec params = publicKey.getParams(); + ECPoint point = publicKey.getW(); + pointBytes = JsseJce.encodePoint(point, params.getCurve()); + curveId = SupportedEllipticCurvesExtension.getCurveIndex(params); + + if (privateKey == null) { + // ECDH_anon + return; + } + + Signature sig; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + this.preferableSignatureAlgorithm = signAlgorithm; + sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(privateKey.getAlgorithm()); + } + sig.initSign(privateKey); // where is the SecureRandom? + + updateSignature(sig, clntNonce, svrNonce); + signatureBytes = sig.sign(); + } + + /* + * Parse an ECDH server key exchange message. + */ + ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey, + byte[] clntNonce, byte[] svrNonce, + Collection localSupportedSignAlgs, + ProtocolVersion protocolVersion) + throws IOException, GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + // read params: ServerECDHParams + int curveType = input.getInt8(); + ECParameterSpec parameters; + // These parsing errors should never occur as we negotiated + // the supported curves during the exchange of the Hello messages. + if (curveType == CURVE_NAMED_CURVE) { + curveId = input.getInt16(); + if (SupportedEllipticCurvesExtension.isSupported(curveId) + == false) { + throw new SSLHandshakeException( + "Unsupported curveId: " + curveId); + } + String curveOid = + SupportedEllipticCurvesExtension.getCurveOid(curveId); + if (curveOid == null) { + throw new SSLHandshakeException( + "Unknown named curve: " + curveId); + } + parameters = JsseJce.getECParameterSpec(curveOid); + if (parameters == null) { + throw new SSLHandshakeException( + "Unsupported curve: " + curveOid); + } + } else { + throw new SSLHandshakeException( + "Unsupported ECCurveType: " + curveType); + } + pointBytes = input.getBytes8(); + + ECPoint point = JsseJce.decodePoint(pointBytes, parameters.getCurve()); + KeyFactory factory = JsseJce.getKeyFactory("EC"); + publicKey = (ECPublicKey)factory.generatePublic( + new ECPublicKeySpec(point, parameters)); + + if (signingKey == null) { + // ECDH_anon + return; + } + + // read the signature and hash algorithm + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, 0); + + // Is it a local supported signature algorithm? + if (!localSupportedSignAlgs.contains( + preferableSignatureAlgorithm)) { + throw new SSLHandshakeException( + "Unsupported SignatureAndHashAlgorithm in " + + "ServerKeyExchange message"); + } + } + + // read the signature + signatureBytes = input.getBytes16(); + + // verify the signature + Signature sig; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sig = JsseJce.getSignature( + preferableSignatureAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(signingKey.getAlgorithm()); + } + sig.initVerify(signingKey); + + updateSignature(sig, clntNonce, svrNonce); + + if (sig.verify(signatureBytes) == false ) { + throw new SSLKeyException( + "Invalid signature on ECDH server key exchange message"); + } + } + + /* + * Get the ephemeral EC public key encapsulated in this message. + */ + ECPublicKey getPublicKey() { + return publicKey; + } + + private static Signature getSignature(String keyAlgorithm) + throws NoSuchAlgorithmException { + switch (keyAlgorithm) { + case "EC": + return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA); + case "RSA": + return RSASignature.getInstance(); + default: + throw new NoSuchAlgorithmException("neither an RSA or a EC key"); + } + } + + private void updateSignature(Signature sig, byte clntNonce[], + byte svrNonce[]) throws SignatureException { + sig.update(clntNonce); + sig.update(svrNonce); + + sig.update((byte)CURVE_NAMED_CURVE); + sig.update((byte)(curveId >> 8)); + sig.update((byte)curveId); + sig.update((byte)pointBytes.length); + sig.update(pointBytes); + } + + @Override + int messageLength() { + int sigLen = 0; + if (signatureBytes != null) { + sigLen = 2 + signatureBytes.length; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sigLen += SignatureAndHashAlgorithm.sizeInRecord(); + } + } + + return 4 + pointBytes.length + sigLen; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt8(CURVE_NAMED_CURVE); + s.putInt16(curveId); + s.putBytes8(pointBytes); + + if (signatureBytes != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.putInt8(preferableSignatureAlgorithm.getHashValue()); + s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); + } + + s.putBytes16(signatureBytes); + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** ECDH ServerKeyExchange"); + + if (debug != null && Debug.isOn("verbose")) { + if (signatureBytes == null) { + s.println("Anonymous"); + } else { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.println("Signature Algorithm " + + preferableSignatureAlgorithm.getAlgorithmName()); + } + } + + s.println("Server key: " + publicKey); + } + } +} + +static final class DistinguishedName { + + /* + * DER encoded distinguished name. + * TLS requires that its not longer than 65535 bytes. + */ + byte name[]; + + DistinguishedName(HandshakeInStream input) throws IOException { + name = input.getBytes16(); + } + + DistinguishedName(X500Principal dn) { + name = dn.getEncoded(); + } + + X500Principal getX500Principal() throws IOException { + try { + return new X500Principal(name); + } catch (IllegalArgumentException e) { + throw (SSLProtocolException)new SSLProtocolException( + e.getMessage()).initCause(e); + } + } + + int length() { + return 2 + name.length; + } + + void send(HandshakeOutStream output) throws IOException { + output.putBytes16(name); + } + + void print(PrintStream output) throws IOException { + X500Principal principal = new X500Principal(name); + output.println("<" + principal.toString() + ">"); + } +} + +/* + * CertificateRequest ... SERVER --> CLIENT + * + * Authenticated servers may ask clients to authenticate themselves + * in turn, using this message. + * + * Prior to TLS 1.2, the structure of the message is defined as: + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * DistinguishedName certificate_authorities<0..2^16-1>; + * } CertificateRequest; + * + * In TLS 1.2, the structure is changed to: + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2^16-1>; + * DistinguishedName certificate_authorities<0..2^16-1>; + * } CertificateRequest; + * + */ +static final +class CertificateRequest extends HandshakeMessage +{ + // enum ClientCertificateType + static final int cct_rsa_sign = 1; + static final int cct_dss_sign = 2; + static final int cct_rsa_fixed_dh = 3; + static final int cct_dss_fixed_dh = 4; + + // The existance of these two values is a bug in the SSL specification. + // They are never used in the protocol. + static final int cct_rsa_ephemeral_dh = 5; + static final int cct_dss_ephemeral_dh = 6; + + // From RFC 4492 (ECC) + static final int cct_ecdsa_sign = 64; + static final int cct_rsa_fixed_ecdh = 65; + static final int cct_ecdsa_fixed_ecdh = 66; + + private final static byte[] TYPES_NO_ECC = { cct_rsa_sign, cct_dss_sign }; + private final static byte[] TYPES_ECC = + { cct_rsa_sign, cct_dss_sign, cct_ecdsa_sign }; + + byte types []; // 1 to 255 types + DistinguishedName authorities []; // 3 to 2^16 - 1 + // ... "3" because that's the smallest DER-encoded X500 DN + + // protocol version being established using this CertificateRequest message + ProtocolVersion protocolVersion; + + // supported_signature_algorithms for TLS 1.2 or later + private Collection algorithms; + + // length of supported_signature_algorithms + private int algorithmsLen; + + CertificateRequest(X509Certificate ca[], KeyExchange keyExchange, + Collection signAlgs, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + + // always use X500Principal + authorities = new DistinguishedName[ca.length]; + for (int i = 0; i < ca.length; i++) { + X500Principal x500Principal = ca[i].getSubjectX500Principal(); + authorities[i] = new DistinguishedName(x500Principal); + } + // we support RSA, DSS, and ECDSA client authentication and they + // can be used with all ciphersuites. If this changes, the code + // needs to be adapted to take keyExchange into account. + // We only request ECDSA client auth if we have ECC crypto available. + this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; + + // Use supported_signature_algorithms for TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (signAlgs == null || signAlgs.isEmpty()) { + throw new SSLProtocolException( + "No supported signature algorithms"); + } + + algorithms = new ArrayList(signAlgs); + algorithmsLen = + SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); + } else { + algorithms = new ArrayList(); + algorithmsLen = 0; + } + } + + CertificateRequest(HandshakeInStream input, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + + // Read the certificate_types. + types = input.getBytes8(); + + // Read the supported_signature_algorithms for TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + algorithmsLen = input.getInt16(); + if (algorithmsLen < 2) { + throw new SSLProtocolException( + "Invalid supported_signature_algorithms field"); + } + + algorithms = new ArrayList(); + int remains = algorithmsLen; + int sequence = 0; + while (remains > 1) { // needs at least two bytes + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + SignatureAndHashAlgorithm algorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, + ++sequence); + algorithms.add(algorithm); + remains -= 2; // one byte for hash, one byte for signature + } + + if (remains != 0) { + throw new SSLProtocolException( + "Invalid supported_signature_algorithms field"); + } + } else { + algorithms = new ArrayList(); + algorithmsLen = 0; + } + + // read the certificate_authorities + int len = input.getInt16(); + ArrayList v = new ArrayList<>(); + while (len >= 3) { + DistinguishedName dn = new DistinguishedName(input); + v.add(dn); + len -= dn.length(); + } + + if (len != 0) { + throw new SSLProtocolException("Bad CertificateRequest DN length"); + } + + authorities = v.toArray(new DistinguishedName[v.size()]); + } + + X500Principal[] getAuthorities() throws IOException { + X500Principal[] ret = new X500Principal[authorities.length]; + for (int i = 0; i < authorities.length; i++) { + ret[i] = authorities[i].getX500Principal(); + } + return ret; + } + + Collection getSignAlgorithms() { + return algorithms; + } + + @Override + int messageType() { + return ht_certificate_request; + } + + @Override + int messageLength() { + int len = 1 + types.length + 2; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + len += algorithmsLen + 2; + } + + for (int i = 0; i < authorities.length; i++) { + len += authorities[i].length(); + } + + return len; + } + + @Override + void send(HandshakeOutStream output) throws IOException { + // put certificate_types + output.putBytes8(types); + + // put supported_signature_algorithms + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + output.putInt16(algorithmsLen); + for (SignatureAndHashAlgorithm algorithm : algorithms) { + output.putInt8(algorithm.getHashValue()); // hash + output.putInt8(algorithm.getSignatureValue()); // signature + } + } + + // put certificate_authorities + int len = 0; + for (int i = 0; i < authorities.length; i++) { + len += authorities[i].length(); + } + + output.putInt16(len); + for (int i = 0; i < authorities.length; i++) { + authorities[i].send(output); + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** CertificateRequest"); + + if (debug != null && Debug.isOn("verbose")) { + s.print("Cert Types: "); + for (int i = 0; i < types.length; i++) { + switch (types[i]) { + case cct_rsa_sign: + s.print("RSA"); break; + case cct_dss_sign: + s.print("DSS"); break; + case cct_rsa_fixed_dh: + s.print("Fixed DH (RSA sig)"); break; + case cct_dss_fixed_dh: + s.print("Fixed DH (DSS sig)"); break; + case cct_rsa_ephemeral_dh: + s.print("Ephemeral DH (RSA sig)"); break; + case cct_dss_ephemeral_dh: + s.print("Ephemeral DH (DSS sig)"); break; + case cct_ecdsa_sign: + s.print("ECDSA"); break; + case cct_rsa_fixed_ecdh: + s.print("Fixed ECDH (RSA sig)"); break; + case cct_ecdsa_fixed_ecdh: + s.print("Fixed ECDH (ECDSA sig)"); break; + default: + s.print("Type-" + (types[i] & 0xff)); break; + } + if (i != types.length - 1) { + s.print(", "); + } + } + s.println(); + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + StringBuffer buffer = new StringBuffer(); + boolean opened = false; + for (SignatureAndHashAlgorithm signAlg : algorithms) { + if (opened) { + buffer.append(", " + signAlg.getAlgorithmName()); + } else { + buffer.append(signAlg.getAlgorithmName()); + opened = true; + } + } + s.println("Supported Signature Algorithms: " + buffer); + } + + s.println("Cert Authorities:"); + if (authorities.length == 0) { + s.println(""); + } else { + for (int i = 0; i < authorities.length; i++) { + authorities[i].print(s); + } + } + } + } +} + + +/* + * ServerHelloDone ... SERVER --> CLIENT + * + * When server's done sending its messages in response to the client's + * "hello" (e.g. its own hello, certificate, key exchange message, perhaps + * client certificate request) it sends this message to flag that it's + * done that part of the handshake. + */ +static final +class ServerHelloDone extends HandshakeMessage +{ + @Override + int messageType() { return ht_server_hello_done; } + + ServerHelloDone() { } + + ServerHelloDone(HandshakeInStream input) + { + // nothing to do + } + + @Override + int messageLength() + { + return 0; + } + + @Override + void send(HandshakeOutStream s) throws IOException + { + // nothing to send + } + + @Override + void print(PrintStream s) throws IOException + { + s.println("*** ServerHelloDone"); + } +} + + +/* + * CertificateVerify ... CLIENT --> SERVER + * + * Sent after client sends signature-capable certificates (e.g. not + * Diffie-Hellman) to verify. + */ +static final class CertificateVerify extends HandshakeMessage { + + // the signature bytes + private byte[] signature; + + // protocol version being established using this ServerKeyExchange message + ProtocolVersion protocolVersion; + + // the preferable signature algorithm used by this CertificateVerify message + private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; + + /* + * Create an RSA or DSA signed certificate verify message. + */ + CertificateVerify(ProtocolVersion protocolVersion, + HandshakeHash handshakeHash, PrivateKey privateKey, + SecretKey masterSecret, SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm) + throws GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + String algorithm = privateKey.getAlgorithm(); + Signature sig = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + this.preferableSignatureAlgorithm = signAlgorithm; + sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(protocolVersion, algorithm); + } + sig.initSign(privateKey, sr); + updateSignature(sig, protocolVersion, handshakeHash, algorithm, + masterSecret); + signature = sig.sign(); + } + + // + // Unmarshal the signed data from the input stream. + // + CertificateVerify(HandshakeInStream input, + Collection localSupportedSignAlgs, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + + // read the signature and hash algorithm + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + int hashAlg = input.getInt8(); // hash algorithm + int signAlg = input.getInt8(); // signature algorithm + + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0); + + // Is it a local supported signature algorithm? + if (!localSupportedSignAlgs.contains( + preferableSignatureAlgorithm)) { + throw new SSLHandshakeException( + "Unsupported SignatureAndHashAlgorithm in " + + "ServerKeyExchange message"); + } + } + + // read the signature + signature = input.getBytes16(); + } + + /* + * Get the preferable signature algorithm used by this message + */ + SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() { + return preferableSignatureAlgorithm; + } + + /* + * Verify a certificate verify message. Return the result of verification, + * if there is a problem throw a GeneralSecurityException. + */ + boolean verify(ProtocolVersion protocolVersion, + HandshakeHash handshakeHash, PublicKey publicKey, + SecretKey masterSecret) throws GeneralSecurityException { + String algorithm = publicKey.getAlgorithm(); + Signature sig = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sig = JsseJce.getSignature( + preferableSignatureAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(protocolVersion, algorithm); + } + sig.initVerify(publicKey); + updateSignature(sig, protocolVersion, handshakeHash, algorithm, + masterSecret); + return sig.verify(signature); + } + + /* + * Get the Signature object appropriate for verification using the + * given signature algorithm and protocol version. + */ + private static Signature getSignature(ProtocolVersion protocolVersion, + String algorithm) throws GeneralSecurityException { + switch (algorithm) { + case "RSA": + return RSASignature.getInternalInstance(); + case "DSA": + return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); + case "EC": + return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); + default: + throw new SignatureException("Unrecognized algorithm: " + + algorithm); + } + } + + /* + * Update the Signature with the data appropriate for the given + * signature algorithm and protocol version so that the object is + * ready for signing or verifying. + */ + private static void updateSignature(Signature sig, + ProtocolVersion protocolVersion, + HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) + throws SignatureException { + + if (algorithm.equals("RSA")) { + if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- + MessageDigest md5Clone = handshakeHash.getMD5Clone(); + MessageDigest shaClone = handshakeHash.getSHAClone(); + + if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 + updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); + updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); + } + + // The signature must be an instance of RSASignature, need + // to use these hashes directly. + RSASignature.setHashes(sig, md5Clone, shaClone); + } else { // TLS1.2+ + sig.update(handshakeHash.getAllHandshakeMessages()); + } + } else { // DSA, ECDSA + if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- + MessageDigest shaClone = handshakeHash.getSHAClone(); + + if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 + updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); + } + + sig.update(shaClone.digest()); + } else { // TLS1.2+ + sig.update(handshakeHash.getAllHandshakeMessages()); + } + } + } + + /* + * Update the MessageDigest for SSLv3 certificate verify or finished + * message calculation. The digest must already have been updated with + * all preceding handshake messages. + * Used by the Finished class as well. + */ + private static void updateDigest(MessageDigest md, + byte[] pad1, byte[] pad2, + SecretKey masterSecret) { + // Digest the key bytes if available. + // Otherwise (sensitive key), try digesting the key directly. + // That is currently only implemented in SunPKCS11 using a private + // reflection API, so we avoid that if possible. + byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) + ? masterSecret.getEncoded() : null; + if (keyBytes != null) { + md.update(keyBytes); + } else { + digestKey(md, masterSecret); + } + md.update(pad1); + byte[] temp = md.digest(); + + if (keyBytes != null) { + md.update(keyBytes); + } else { + digestKey(md, masterSecret); + } + md.update(pad2); + md.update(temp); + } + + private final static Class delegate; + private final static Field spiField; + + static { + try { + delegate = Class.forName("java.security.MessageDigest$Delegate"); + spiField = delegate.getDeclaredField("digestSpi"); + } catch (Exception e) { + throw new RuntimeException("Reflection failed", e); + } + makeAccessible(spiField); + } + + private static void makeAccessible(final AccessibleObject o) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + o.setAccessible(true); + return null; + } + }); + } + + // ConcurrentHashMap does not allow null values, use this marker object + private final static Object NULL_OBJECT = new Object(); + + // cache Method objects per Spi class + // Note that this will prevent the Spi classes from being GC'd. We assume + // that is not a problem. + private final static Map,Object> methodCache = + new ConcurrentHashMap<>(); + + private static void digestKey(MessageDigest md, SecretKey key) { + try { + // Verify that md is implemented via MessageDigestSpi, not + // via JDK 1.1 style MessageDigest subclassing. + if (md.getClass() != delegate) { + throw new Exception("Digest is not a MessageDigestSpi"); + } + MessageDigestSpi spi = (MessageDigestSpi)spiField.get(md); + Class clazz = spi.getClass(); + Object r = methodCache.get(clazz); + if (r == null) { + try { + r = clazz.getDeclaredMethod("implUpdate", SecretKey.class); + makeAccessible((Method)r); + } catch (NoSuchMethodException e) { + r = NULL_OBJECT; + } + methodCache.put(clazz, r); + } + if (r == NULL_OBJECT) { + throw new Exception( + "Digest does not support implUpdate(SecretKey)"); + } + Method update = (Method)r; + update.invoke(spi, key); + } catch (Exception e) { + throw new RuntimeException( + "Could not obtain encoded key and " + + "MessageDigest cannot digest key", e); + } + } + + @Override + int messageType() { + return ht_certificate_verify; + } + + @Override + int messageLength() { + int temp = 2; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + temp += SignatureAndHashAlgorithm.sizeInRecord(); + } + + return temp + signature.length; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.putInt8(preferableSignatureAlgorithm.getHashValue()); + s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); + } + + s.putBytes16(signature); + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** CertificateVerify"); + + if (debug != null && Debug.isOn("verbose")) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.println("Signature Algorithm " + + preferableSignatureAlgorithm.getAlgorithmName()); + } + } + } +} + + +/* + * FINISHED ... sent by both CLIENT and SERVER + * + * This is the FINISHED message as defined in the SSL and TLS protocols. + * Both protocols define this handshake message slightly differently. + * This class supports both formats. + * + * When handshaking is finished, each side sends a "change_cipher_spec" + * record, then immediately sends a "finished" handshake message prepared + * according to the newly adopted cipher spec. + * + * NOTE that until this is sent, no application data may be passed, unless + * some non-default cipher suite has already been set up on this connection + * connection (e.g. a previous handshake arranged one). + */ +static final class Finished extends HandshakeMessage { + + // constant for a Finished message sent by the client + final static int CLIENT = 1; + + // constant for a Finished message sent by the server + final static int SERVER = 2; + + // enum Sender: "CLNT" and "SRVR" + private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; + private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; + + /* + * Contents of the finished message ("checksum"). For TLS, it + * is 12 bytes long, for SSLv3 36 bytes. + */ + private byte[] verifyData; + + /* + * Current cipher suite we are negotiating. TLS 1.2 has + * ciphersuite-defined PRF algorithms. + */ + private ProtocolVersion protocolVersion; + private CipherSuite cipherSuite; + + /* + * Create a finished message to send to the remote peer. + */ + Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, + int sender, SecretKey master, CipherSuite cipherSuite) { + this.protocolVersion = protocolVersion; + this.cipherSuite = cipherSuite; + verifyData = getFinished(handshakeHash, sender, master); + } + + /* + * Constructor that reads FINISHED message from stream. + */ + Finished(ProtocolVersion protocolVersion, HandshakeInStream input, + CipherSuite cipherSuite) throws IOException { + this.protocolVersion = protocolVersion; + this.cipherSuite = cipherSuite; + int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36; + verifyData = new byte[msgLen]; + input.read(verifyData); + } + + /* + * Verify that the hashes here are what would have been produced + * according to a given set of inputs. This is used to ensure that + * both client and server are fully in sync, and that the handshake + * computations have been successful. + */ + boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) { + byte[] myFinished = getFinished(handshakeHash, sender, master); + return Arrays.equals(myFinished, verifyData); + } + + /* + * Perform the actual finished message calculation. + */ + private byte[] getFinished(HandshakeHash handshakeHash, + int sender, SecretKey masterKey) { + byte[] sslLabel; + String tlsLabel; + if (sender == CLIENT) { + sslLabel = SSL_CLIENT; + tlsLabel = "client finished"; + } else if (sender == SERVER) { + sslLabel = SSL_SERVER; + tlsLabel = "server finished"; + } else { + throw new RuntimeException("Invalid sender: " + sender); + } + + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + // TLS 1.0+ + try { + byte [] seed; + String prfAlg; + PRF prf; + + // Get the KeyGenerator alg and calculate the seed. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + // TLS 1.2 + seed = handshakeHash.getFinishedHash(); + + prfAlg = "SunTls12Prf"; + prf = cipherSuite.prfAlg; + } else { + // TLS 1.0/1.1 + MessageDigest md5Clone = handshakeHash.getMD5Clone(); + MessageDigest shaClone = handshakeHash.getSHAClone(); + seed = new byte[36]; + md5Clone.digest(seed, 0, 16); + shaClone.digest(seed, 16, 20); + + prfAlg = "SunTlsPrf"; + prf = P_NONE; + } + + String prfHashAlg = prf.getPRFHashAlg(); + int prfHashLength = prf.getPRFHashLength(); + int prfBlockSize = prf.getPRFBlockSize(); + + /* + * RFC 5246/7.4.9 says that finished messages can + * be ciphersuite-specific in both length/PRF hash + * algorithm. If we ever run across a different + * length, this call will need to be updated. + */ + TlsPrfParameterSpec spec = new TlsPrfParameterSpec( + masterKey, tlsLabel, seed, 12, + prfHashAlg, prfHashLength, prfBlockSize); + + KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); + kg.init(spec); + SecretKey prfKey = kg.generateKey(); + if ("RAW".equals(prfKey.getFormat()) == false) { + throw new ProviderException( + "Invalid PRF output, format must be RAW"); + } + byte[] finished = prfKey.getEncoded(); + return finished; + } catch (GeneralSecurityException e) { + throw new RuntimeException("PRF failed", e); + } + } else { + // SSLv3 + MessageDigest md5Clone = handshakeHash.getMD5Clone(); + MessageDigest shaClone = handshakeHash.getSHAClone(); + updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey); + updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey); + byte[] finished = new byte[36]; + try { + md5Clone.digest(finished, 0, 16); + shaClone.digest(finished, 16, 20); + } catch (DigestException e) { + // cannot occur + throw new RuntimeException("Digest failed", e); + } + return finished; + } + } + + /* + * Update the MessageDigest for SSLv3 finished message calculation. + * The digest must already have been updated with all preceding handshake + * messages. This operation is almost identical to the certificate verify + * hash, reuse that code. + */ + private static void updateDigest(MessageDigest md, byte[] sender, + byte[] pad1, byte[] pad2, SecretKey masterSecret) { + md.update(sender); + CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); + } + + // get the verify_data of the finished message + byte[] getVerifyData() { + return verifyData; + } + + @Override + int messageType() { return ht_finished; } + + @Override + int messageLength() { + return verifyData.length; + } + + @Override + void send(HandshakeOutStream out) throws IOException { + out.write(verifyData); + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** Finished"); + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "verify_data", verifyData); + s.println("***"); + } + } +} + +// +// END of nested classes +// + +} diff --git a/src/sun/security/ssl/HandshakeOutStream.java b/src/sun/security/ssl/HandshakeOutStream.java new file mode 100644 index 00000000..4c8cd4d7 --- /dev/null +++ b/src/sun/security/ssl/HandshakeOutStream.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * Output stream for handshake data. This is used only internally + * to the SSL classes. + * + * MT note: one thread at a time is presumed be writing handshake + * messages, but (after initial connection setup) it's possible to + * have other threads reading/writing application data. It's the + * SSLSocketImpl class that synchronizes record writes. + * + * @author David Brownell + */ +public class HandshakeOutStream extends OutputStream { + + private SSLSocketImpl socket; + private SSLEngineImpl engine; + + OutputRecord r; + + HandshakeOutStream(ProtocolVersion protocolVersion, + ProtocolVersion helloVersion, HandshakeHash handshakeHash, + SSLSocketImpl socket) { + this.socket = socket; + r = new OutputRecord(Record.ct_handshake); + init(protocolVersion, helloVersion, handshakeHash); + } + + HandshakeOutStream(ProtocolVersion protocolVersion, + ProtocolVersion helloVersion, HandshakeHash handshakeHash, + SSLEngineImpl engine) { + this.engine = engine; + r = new EngineOutputRecord(Record.ct_handshake, engine); + init(protocolVersion, helloVersion, handshakeHash); + } + + private void init(ProtocolVersion protocolVersion, + ProtocolVersion helloVersion, HandshakeHash handshakeHash) { + r.setVersion(protocolVersion); + r.setHelloVersion(helloVersion); + r.setHandshakeHash(handshakeHash); + } + + + /* + * Update the handshake data hashes ... mostly for use after a + * client cert has been sent, so the cert verify message can be + * constructed correctly yet without forcing extra I/O. In all + * other cases, automatic hash calculation suffices. + */ + void doHashes() { + r.doHashes(); + } + + /* + * Write some data out onto the stream ... buffers as much as possible. + * Hashes are updated automatically if something gets flushed to the + * network (e.g. a big cert message etc). + */ + @Override + public void write(byte buf[], int off, int len) throws IOException { + while (len > 0) { + int howmuch = Math.min(len, r.availableDataBytes()); + + if (howmuch == 0) { + flush(); + } else { + r.write(buf, off, howmuch); + off += howmuch; + len -= howmuch; + } + } + } + + /* + * write-a-byte + */ + @Override + public void write(int i) throws IOException { + if (r.availableDataBytes() < 1) { + flush(); + } + r.write(i); + } + + @Override + public void flush() throws IOException { + if (socket != null) { + try { + socket.writeRecord(r); + } catch (IOException e) { + // Had problems writing; check if there was an + // alert from peer. If alert received, waitForClose + // will throw an exception for the alert + socket.waitForClose(true); + + // No alert was received, just rethrow exception + throw e; + } + } else { // engine != null + /* + * Even if record might be empty, flush anyway in case + * there is a finished handshake message that we need + * to queue. + */ + engine.writeRecord((EngineOutputRecord)r); + } + } + + /* + * Tell the OutputRecord that a finished message was + * contained either in this record or the one immeiately + * preceding it. We need to reliably pass back notifications + * that a finish message occurred. + */ + void setFinishedMsg() { + assert(socket == null); + + ((EngineOutputRecord)r).setFinishedMsg(); + } + + /* + * Put integers encoded in standard 8, 16, 24, and 32 bit + * big endian formats. Note that OutputStream.write(int) only + * writes the least significant 8 bits and ignores the rest. + */ + + void putInt8(int i) throws IOException { + checkOverflow(i, Record.OVERFLOW_OF_INT08); + r.write(i); + } + + void putInt16(int i) throws IOException { + checkOverflow(i, Record.OVERFLOW_OF_INT16); + if (r.availableDataBytes() < 2) { + flush(); + } + r.write(i >> 8); + r.write(i); + } + + void putInt24(int i) throws IOException { + checkOverflow(i, Record.OVERFLOW_OF_INT24); + if (r.availableDataBytes() < 3) { + flush(); + } + r.write(i >> 16); + r.write(i >> 8); + r.write(i); + } + + void putInt32(int i) throws IOException { + if (r.availableDataBytes() < 4) { + flush(); + } + r.write(i >> 24); + r.write(i >> 16); + r.write(i >> 8); + r.write(i); + } + + /* + * Put byte arrays with length encoded as 8, 16, 24 bit + * integers in big-endian format. + */ + void putBytes8(byte b[]) throws IOException { + if (b == null) { + putInt8(0); + return; + } else { + checkOverflow(b.length, Record.OVERFLOW_OF_INT08); + } + putInt8(b.length); + write(b, 0, b.length); + } + + public void putBytes16(byte b[]) throws IOException { + if (b == null) { + putInt16(0); + return; + } else { + checkOverflow(b.length, Record.OVERFLOW_OF_INT16); + } + putInt16(b.length); + write(b, 0, b.length); + } + + void putBytes24(byte b[]) throws IOException { + if (b == null) { + putInt24(0); + return; + } else { + checkOverflow(b.length, Record.OVERFLOW_OF_INT24); + } + putInt24(b.length); + write(b, 0, b.length); + } + + private void checkOverflow(int length, int overflow) { + if (length >= overflow) { + // internal_error alert will be triggered + throw new RuntimeException( + "Field length overflow, the field length (" + + length + ") should be less than " + overflow); + } + } +} diff --git a/src/sun/security/ssl/Handshaker.java b/src/sun/security/ssl/Handshaker.java new file mode 100644 index 00000000..c990057e --- /dev/null +++ b/src/sun/security/ssl/Handshaker.java @@ -0,0 +1,1497 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.util.*; +import java.util.function.BiFunction; +import java.security.*; +import java.security.NoSuchAlgorithmException; +import java.security.AccessController; +import java.security.AlgorithmConstraints; +import java.security.AccessControlContext; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; + +import javax.crypto.*; +import javax.crypto.spec.*; + +import javax.net.ssl.*; +import sun.misc.HexDumpEncoder; + +import sun.security.internal.spec.*; + +import sun.security.ssl.HandshakeMessage.*; +import sun.security.ssl.CipherSuite.*; + +import static sun.security.ssl.CipherSuite.PRF.*; +import static sun.security.ssl.CipherSuite.CipherType.*; + +/** + * Handshaker ... processes handshake records from an SSL V3.0 + * data stream, handling all the details of the handshake protocol. + * + * Note that the real protocol work is done in two subclasses, the base + * class just provides the control flow and key generation framework. + * + * @author David Brownell + */ +abstract class Handshaker { + + // protocol version being established using this Handshaker + ProtocolVersion protocolVersion; + + // the currently active protocol version during a renegotiation + ProtocolVersion activeProtocolVersion; + + // security parameters for secure renegotiation. + boolean secureRenegotiation; + byte[] clientVerifyData; + byte[] serverVerifyData; + + // Is it an initial negotiation or a renegotiation? + boolean isInitialHandshake; + + // List of enabled protocols + private ProtocolList enabledProtocols; + + // List of enabled CipherSuites + private CipherSuiteList enabledCipherSuites; + + // The endpoint identification protocol + String identificationProtocol; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + + // Local supported signature and algorithms + Collection localSupportedSignAlgs; + + // Peer supported signature and algorithms + Collection peerSupportedSignAlgs; + + /* + * List of active protocols + * + * Active protocols is a subset of enabled protocols, and will + * contain only those protocols that have vaild cipher suites + * enabled. + */ + private ProtocolList activeProtocols; + + /* + * List of active cipher suites + * + * Active cipher suites is a subset of enabled cipher suites, and will + * contain only those cipher suites available for the active protocols. + */ + private CipherSuiteList activeCipherSuites; + + // The server name indication and matchers + List serverNames = Collections.emptyList(); + Collection sniMatchers = Collections.emptyList(); + + // List of local ApplicationProtocols + String[] localApl = null; + + // Negotiated ALPN value + String applicationProtocol = null; + + // Application protocol callback function (for SSLEngine) + BiFunction,String> + appProtocolSelectorSSLEngine = null; + + // Application protocol callback function (for SSLSocket) + BiFunction,String> + appProtocolSelectorSSLSocket = null; + + private boolean isClient; + private boolean needCertVerify; + + SSLSocketImpl conn = null; + SSLEngineImpl engine = null; + + HandshakeHash handshakeHash; + HandshakeInStream input; + HandshakeOutStream output; + int state; + SSLContextImpl sslContext; + RandomCookie clnt_random, svr_random; + SSLSessionImpl session; + + // Since this is for a Reference Implementation, only backporting a bit of + // the state machine improvement JDK-8074462. + boolean clientHelloDelivered; + + // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL + CipherSuite cipherSuite; + + // current key exchange. Never null, initially K_NULL + KeyExchange keyExchange; + + // True if this session is being resumed (fast handshake) + boolean resumingSession; + + // True if it's OK to start a new SSL session + boolean enableNewSession; + + // True if session keys have been calculated and the caller may receive + // and process a ChangeCipherSpec message + private boolean sessKeysCalculated; + + // Whether local cipher suites preference should be honored during + // handshaking? + // + // Note that in this provider, this option only applies to server side. + // Local cipher suites preference is always honored in client side in + // this provider. + boolean preferLocalCipherSuites = false; + + // Temporary storage for the individual keys. Set by + // calculateConnectionKeys() and cleared once the ciphers are + // activated. + private SecretKey clntWriteKey, svrWriteKey; + private IvParameterSpec clntWriteIV, svrWriteIV; + private SecretKey clntMacSecret, svrMacSecret; + + /* + * Delegated task subsystem data structures. + * + * If thrown is set, we need to propagate this back immediately + * on entry into processMessage(). + * + * Data is protected by the SSLEngine.this lock. + */ + private volatile boolean taskDelegated = false; + private volatile DelegatedTask delegatedTask = null; + private volatile Exception thrown = null; + + // Could probably use a java.util.concurrent.atomic.AtomicReference + // here instead of using this lock. Consider changing. + private Object thrownLock = new Object(); + + // Class and subclass dynamic debugging support + static final Debug debug = Debug.getInstance("ssl"); + + // By default, disable the unsafe legacy session renegotiation + static final boolean allowUnsafeRenegotiation = Debug.getBooleanProperty( + "sun.security.ssl.allowUnsafeRenegotiation", false); + + // For maximum interoperability and backward compatibility, RFC 5746 + // allows server (or client) to accept ClientHello (or ServerHello) + // message without the secure renegotiation_info extension or SCSV. + // + // For maximum security, RFC 5746 also allows server (or client) to + // reject such message with a fatal "handshake_failure" alert. + // + // By default, allow such legacy hello messages. + static final boolean allowLegacyHelloMessages = Debug.getBooleanProperty( + "sun.security.ssl.allowLegacyHelloMessages", true); + + // To prevent the TLS renegotiation issues, by setting system property + // "jdk.tls.rejectClientInitiatedRenegotiation" to true, applications in + // server side can disable all client initiated SSL renegotiations + // regardless of the support of TLS protocols. + // + // By default, allow client initiated renegotiations. + static final boolean rejectClientInitiatedRenego = + Debug.getBooleanProperty( + "jdk.tls.rejectClientInitiatedRenegotiation", false); + + // need to dispose the object when it is invalidated + boolean invalidated; + + Handshaker(SSLSocketImpl c, SSLContextImpl context, + ProtocolList enabledProtocols, boolean needCertVerify, + boolean isClient, ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + this.conn = c; + init(context, enabledProtocols, needCertVerify, isClient, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + } + + Handshaker(SSLEngineImpl engine, SSLContextImpl context, + ProtocolList enabledProtocols, boolean needCertVerify, + boolean isClient, ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + this.engine = engine; + init(context, enabledProtocols, needCertVerify, isClient, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + } + + private void init(SSLContextImpl context, ProtocolList enabledProtocols, + boolean needCertVerify, boolean isClient, + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Allow unsafe renegotiation: " + allowUnsafeRenegotiation + + "\nAllow legacy hello messages: " + allowLegacyHelloMessages + + "\nIs initial handshake: " + isInitialHandshake + + "\nIs secure renegotiation: " + secureRenegotiation); + } + + this.sslContext = context; + this.isClient = isClient; + this.needCertVerify = needCertVerify; + this.activeProtocolVersion = activeProtocolVersion; + this.isInitialHandshake = isInitialHandshake; + this.secureRenegotiation = secureRenegotiation; + this.clientVerifyData = clientVerifyData; + this.serverVerifyData = serverVerifyData; + enableNewSession = true; + invalidated = false; + sessKeysCalculated = false; + clientHelloDelivered = false; + + setCipherSuite(CipherSuite.C_NULL); + setEnabledProtocols(enabledProtocols); + + if (conn != null) { + algorithmConstraints = new SSLAlgorithmConstraints(conn, true); + } else { // engine != null + algorithmConstraints = new SSLAlgorithmConstraints(engine, true); + } + + + // + // In addition to the connection state machine, controlling + // how the connection deals with the different sorts of records + // that get sent (notably handshake transitions!), there's + // also a handshaking state machine that controls message + // sequencing. + // + // It's a convenient artifact of the protocol that this can, + // with only a couple of minor exceptions, be driven by the + // type constant for the last message seen: except for the + // client's cert verify, those constants are in a convenient + // order to drastically simplify state machine checking. + // + state = -2; // initialized but not activated + } + + /* + * Reroutes calls to the SSLSocket or SSLEngine (*SE). + * + * We could have also done it by extra classes + * and letting them override, but this seemed much + * less involved. + */ + void fatalSE(byte b, String diagnostic) throws IOException { + fatalSE(b, diagnostic, null); + } + + void fatalSE(byte b, Throwable cause) throws IOException { + fatalSE(b, null, cause); + } + + void fatalSE(byte b, String diagnostic, Throwable cause) + throws IOException { + if (conn != null) { + conn.fatal(b, diagnostic, cause); + } else { + engine.fatal(b, diagnostic, cause); + } + } + + void warningSE(byte b) { + if (conn != null) { + conn.warning(b); + } else { + engine.warning(b); + } + } + + // ONLY used by ClientHandshaker to setup the peer host in SSLSession. + String getHostSE() { + if (conn != null) { + return conn.getHost(); + } else { + return engine.getPeerHost(); + } + } + + // ONLY used by ServerHandshaker to setup the peer host in SSLSession. + String getHostAddressSE() { + if (conn != null) { + return conn.getInetAddress().getHostAddress(); + } else { + /* + * This is for caching only, doesn't matter that's is really + * a hostname. The main thing is that it doesn't do + * a reverse DNS lookup, potentially slowing things down. + */ + return engine.getPeerHost(); + } + } + + int getPortSE() { + if (conn != null) { + return conn.getPort(); + } else { + return engine.getPeerPort(); + } + } + + int getLocalPortSE() { + if (conn != null) { + return conn.getLocalPort(); + } else { + return -1; + } + } + + AccessControlContext getAccSE() { + if (conn != null) { + return conn.getAcc(); + } else { + return engine.getAcc(); + } + } + + final boolean receivedChangeCipherSpec() { + if (conn != null) { + return conn.receivedChangeCipherSpec(); + } else { + return engine.receivedChangeCipherSpec(); + } + } + + String getEndpointIdentificationAlgorithmSE() { + SSLParameters paras; + if (conn != null) { + paras = conn.getSSLParameters(); + } else { + paras = engine.getSSLParameters(); + } + + return paras.getEndpointIdentificationAlgorithm(); + } + + private void setVersionSE(ProtocolVersion protocolVersion) { + if (conn != null) { + conn.setVersion(protocolVersion); + } else { + engine.setVersion(protocolVersion); + } + } + + /** + * Set the active protocol version and propagate it to the SSLSocket + * and our handshake streams. Called from ClientHandshaker + * and ServerHandshaker with the negotiated protocol version. + */ + void setVersion(ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + setVersionSE(protocolVersion); + + output.r.setVersion(protocolVersion); + } + + /** + * Set the enabled protocols. Called from the constructor or + * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the + * handshake is not yet in progress). + */ + void setEnabledProtocols(ProtocolList enabledProtocols) { + activeCipherSuites = null; + activeProtocols = null; + + this.enabledProtocols = enabledProtocols; + } + + /** + * Set the enabled cipher suites. Called from + * SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites() (if the + * handshake is not yet in progress). + */ + void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) { + activeCipherSuites = null; + activeProtocols = null; + this.enabledCipherSuites = enabledCipherSuites; + } + + /** + * Set the algorithm constraints. Called from the constructor or + * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the + * handshake is not yet in progress). + */ + void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { + activeCipherSuites = null; + activeProtocols = null; + + this.algorithmConstraints = + new SSLAlgorithmConstraints(algorithmConstraints); + this.localSupportedSignAlgs = null; + } + + Collection getLocalSupportedSignAlgs() { + if (localSupportedSignAlgs == null) { + localSupportedSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + algorithmConstraints); + } + + return localSupportedSignAlgs; + } + + void setPeerSupportedSignAlgs( + Collection algorithms) { + peerSupportedSignAlgs = + new ArrayList(algorithms); + } + + Collection getPeerSupportedSignAlgs() { + return peerSupportedSignAlgs; + } + + + /** + * Set the identification protocol. Called from the constructor or + * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the + * handshake is not yet in progress). + */ + void setIdentificationProtocol(String protocol) { + this.identificationProtocol = protocol; + } + + /** + * Sets the server name indication of the handshake. + */ + void setSNIServerNames(List serverNames) { + // The serverNames parameter is unmodifiable. + this.serverNames = serverNames; + } + + /** + * Sets the server name matchers of the handshaking. + */ + void setSNIMatchers(Collection sniMatchers) { + // The sniMatchers parameter is unmodifiable. + this.sniMatchers = sniMatchers; + } + + /** + * Sets the Application Protocol list. + */ + void setApplicationProtocols(String[] apl) { + this.localApl = apl; + } + + /** + * Gets the "negotiated" ALPN value. + */ + String getHandshakeApplicationProtocol() { + return applicationProtocol; + } + + /** + * Sets the Application Protocol selector function for SSLEngine. + */ + void setApplicationProtocolSelectorSSLEngine( + BiFunction,String> selector) { + this.appProtocolSelectorSSLEngine = selector; + } + + /** + * Sets the Application Protocol selector function for SSLSocket. + */ + void setApplicationProtocolSelectorSSLSocket( + BiFunction,String> selector) { + this.appProtocolSelectorSSLSocket = selector; + } + + /** + * Sets the cipher suites preference. + */ + void setUseCipherSuitesOrder(boolean on) { + this.preferLocalCipherSuites = on; + } + + /** + * Prior to handshaking, activate the handshake and initialize the version, + * input stream and output stream. + */ + void activate(ProtocolVersion helloVersion) throws IOException { + if (activeProtocols == null) { + activeProtocols = getActiveProtocols(); + } + + if (activeProtocols.collection().isEmpty() || + activeProtocols.max.v == ProtocolVersion.NONE.v) { + throw new SSLHandshakeException( + "No appropriate protocol (protocol is disabled or " + + "cipher suites are inappropriate)"); + } + + if (activeCipherSuites == null) { + activeCipherSuites = getActiveCipherSuites(); + } + + if (activeCipherSuites.collection().isEmpty()) { + throw new SSLHandshakeException("No appropriate cipher suite"); + } + + // temporary protocol version until the actual protocol version + // is negotiated in the Hello exchange. This affects the record + // version we sent with the ClientHello. + if (!isInitialHandshake) { + protocolVersion = activeProtocolVersion; + } else { + protocolVersion = activeProtocols.max; + } + + if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) { + helloVersion = activeProtocols.helloVersion; + } + + // We accumulate digests of the handshake messages so that + // we can read/write CertificateVerify and Finished messages, + // getting assurance against some particular active attacks. + handshakeHash = new HandshakeHash(needCertVerify); + + // Generate handshake input/output stream. + input = new HandshakeInStream(handshakeHash); + if (conn != null) { + output = new HandshakeOutStream(protocolVersion, helloVersion, + handshakeHash, conn); + conn.getAppInputStream().r.setHandshakeHash(handshakeHash); + conn.getAppInputStream().r.setHelloVersion(helloVersion); + conn.getAppOutputStream().r.setHelloVersion(helloVersion); + } else { + output = new HandshakeOutStream(protocolVersion, helloVersion, + handshakeHash, engine); + engine.inputRecord.setHandshakeHash(handshakeHash); + engine.inputRecord.setHelloVersion(helloVersion); + engine.outputRecord.setHelloVersion(helloVersion); + } + + // move state to activated + state = -1; + } + + /** + * Set cipherSuite and keyExchange to the given CipherSuite. + * Does not perform any verification that this is a valid selection, + * this must be done before calling this method. + */ + void setCipherSuite(CipherSuite s) { + this.cipherSuite = s; + this.keyExchange = s.keyExchange; + } + + /** + * Check if the given ciphersuite is enabled and available within the + * current active cipher suites. + * + * Does not check if the required server certificates are available. + */ + boolean isNegotiable(CipherSuite s) { + if (activeCipherSuites == null) { + activeCipherSuites = getActiveCipherSuites(); + } + + return isNegotiable(activeCipherSuites, s); + } + + /** + * Check if the given ciphersuite is enabled and available within the + * proposed cipher suite list. + * + * Does not check if the required server certificates are available. + */ + final static boolean isNegotiable(CipherSuiteList proposed, CipherSuite s) { + return proposed.contains(s) && s.isNegotiable(); + } + + /** + * Check if the given protocol version is enabled and available. + */ + boolean isNegotiable(ProtocolVersion protocolVersion) { + if (activeProtocols == null) { + activeProtocols = getActiveProtocols(); + } + + return activeProtocols.contains(protocolVersion); + } + + /** + * Select a protocol version from the list. Called from + * ServerHandshaker to negotiate protocol version. + * + * Return the lower of the protocol version suggested in the + * clien hello and the highest supported by the server. + */ + ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { + if (activeProtocols == null) { + activeProtocols = getActiveProtocols(); + } + + return activeProtocols.selectProtocolVersion(protocolVersion); + } + + /** + * Get the active cipher suites. + * + * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, + * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT + * negotiate these cipher suites in TLS 1.1 or later mode. + * + * Therefore, when the active protocols only include TLS 1.1 or later, + * the client cannot request to negotiate those obsoleted cipher + * suites. That is, the obsoleted suites should not be included in the + * client hello. So we need to create a subset of the enabled cipher + * suites, the active cipher suites, which does not contain obsoleted + * cipher suites of the minimum active protocol. + * + * Return empty list instead of null if no active cipher suites. + */ + CipherSuiteList getActiveCipherSuites() { + if (activeCipherSuites == null) { + if (activeProtocols == null) { + activeProtocols = getActiveProtocols(); + } + + ArrayList suites = new ArrayList<>(); + if (!(activeProtocols.collection().isEmpty()) && + activeProtocols.min.v != ProtocolVersion.NONE.v) { + for (CipherSuite suite : enabledCipherSuites.collection()) { + if (suite.obsoleted > activeProtocols.min.v && + suite.supported <= activeProtocols.max.v) { + if (algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + suite.name, null)) { + suites.add(suite); + } + } else if (debug != null && Debug.isOn("verbose")) { + if (suite.obsoleted <= activeProtocols.min.v) { + System.out.println( + "Ignoring obsoleted cipher suite: " + suite); + } else { + System.out.println( + "Ignoring unsupported cipher suite: " + suite); + } + } + } + } + activeCipherSuites = new CipherSuiteList(suites); + } + + return activeCipherSuites; + } + + /* + * Get the active protocol versions. + * + * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, + * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT + * negotiate these cipher suites in TLS 1.1 or later mode. + * + * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the + * only enabled cipher suite, the client cannot request TLS 1.1 or + * later, even though TLS 1.1 or later is enabled. We need to create a + * subset of the enabled protocols, called the active protocols, which + * contains protocols appropriate to the list of enabled Ciphersuites. + * + * Return empty list instead of null if no active protocol versions. + */ + ProtocolList getActiveProtocols() { + if (activeProtocols == null) { + boolean enabledSSL20Hello = false; + ArrayList protocols = new ArrayList<>(4); + for (ProtocolVersion protocol : enabledProtocols.collection()) { + if (!algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + protocol.name, null)) { + if (debug != null && Debug.isOn("verbose")) { + System.out.println( + "Ignoring disabled protocol: " + protocol); + } + + continue; + } + // Need not to check the SSL20Hello protocol. + if (protocol.v == ProtocolVersion.SSL20Hello.v) { + enabledSSL20Hello = true; + continue; + } + + boolean found = false; + for (CipherSuite suite : enabledCipherSuites.collection()) { + if (suite.isAvailable() && suite.obsoleted > protocol.v && + suite.supported <= protocol.v) { + if (algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + suite.name, null)) { + protocols.add(protocol); + found = true; + break; + } else if (debug != null && Debug.isOn("verbose")) { + System.out.println( + "Ignoring disabled cipher suite: " + suite + + " for " + protocol); + } + } else if (debug != null && Debug.isOn("verbose")) { + System.out.println( + "Ignoring unsupported cipher suite: " + suite + + " for " + protocol); + } + } + if (!found && (debug != null) && Debug.isOn("handshake")) { + System.out.println( + "No available cipher suite for " + protocol); + } + } + + if (!protocols.isEmpty() && enabledSSL20Hello) { + protocols.add(ProtocolVersion.SSL20Hello); + } + + activeProtocols = new ProtocolList(protocols); + } + + return activeProtocols; + } + + /** + * As long as handshaking has not activated, we can + * change whether session creations are allowed. + * + * Callers should do their own checking if handshaking + * has activated. + */ + void setEnableSessionCreation(boolean newSessions) { + enableNewSession = newSessions; + } + + /** + * Create a new read cipher and return it to caller. + */ + CipherBox newReadCipher() throws NoSuchAlgorithmException { + BulkCipher cipher = cipherSuite.cipher; + CipherBox box; + if (isClient) { + box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, + sslContext.getSecureRandom(), false); + svrWriteKey = null; + svrWriteIV = null; + } else { + box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, + sslContext.getSecureRandom(), false); + clntWriteKey = null; + clntWriteIV = null; + } + return box; + } + + /** + * Create a new write cipher and return it to caller. + */ + CipherBox newWriteCipher() throws NoSuchAlgorithmException { + BulkCipher cipher = cipherSuite.cipher; + CipherBox box; + if (isClient) { + box = cipher.newCipher(protocolVersion, clntWriteKey, clntWriteIV, + sslContext.getSecureRandom(), true); + clntWriteKey = null; + clntWriteIV = null; + } else { + box = cipher.newCipher(protocolVersion, svrWriteKey, svrWriteIV, + sslContext.getSecureRandom(), true); + svrWriteKey = null; + svrWriteIV = null; + } + return box; + } + + /** + * Create a new read MAC and return it to caller. + */ + Authenticator newReadAuthenticator() + throws NoSuchAlgorithmException, InvalidKeyException { + + Authenticator authenticator = null; + if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { + authenticator = new Authenticator(protocolVersion); + } else { + MacAlg macAlg = cipherSuite.macAlg; + if (isClient) { + authenticator = macAlg.newMac(protocolVersion, svrMacSecret); + svrMacSecret = null; + } else { + authenticator = macAlg.newMac(protocolVersion, clntMacSecret); + clntMacSecret = null; + } + } + + return authenticator; + } + + /** + * Create a new write MAC and return it to caller. + */ + Authenticator newWriteAuthenticator() + throws NoSuchAlgorithmException, InvalidKeyException { + + Authenticator authenticator = null; + if (cipherSuite.cipher.cipherType == AEAD_CIPHER) { + authenticator = new Authenticator(protocolVersion); + } else { + MacAlg macAlg = cipherSuite.macAlg; + if (isClient) { + authenticator = macAlg.newMac(protocolVersion, clntMacSecret); + clntMacSecret = null; + } else { + authenticator = macAlg.newMac(protocolVersion, svrMacSecret); + svrMacSecret = null; + } + } + + return authenticator; + } + + /* + * Returns true iff the handshake sequence is done, so that + * this freshly created session can become the current one. + */ + boolean isDone() { + return state == HandshakeMessage.ht_finished; + } + + + /* + * Returns the session which was created through this + * handshake sequence ... should be called after isDone() + * returns true. + */ + SSLSessionImpl getSession() { + return session; + } + + /* + * Set the handshake session + */ + void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { + if (conn != null) { + conn.setHandshakeSession(handshakeSession); + } else { + engine.setHandshakeSession(handshakeSession); + } + } + + /* + * Returns true if renegotiation is in use for this connection. + */ + boolean isSecureRenegotiation() { + return secureRenegotiation; + } + + /* + * Returns the verify_data from the Finished message sent by the client. + */ + byte[] getClientVerifyData() { + return clientVerifyData; + } + + /* + * Returns the verify_data from the Finished message sent by the server. + */ + byte[] getServerVerifyData() { + return serverVerifyData; + } + + /* + * This routine is fed SSL handshake records when they become available, + * and processes messages found therein. + */ + void process_record(InputRecord r, boolean expectingFinished) + throws IOException { + + checkThrown(); + + /* + * Store the incoming handshake data, then see if we can + * now process any completed handshake messages + */ + input.incomingRecord(r); + + /* + * We don't need to create a separate delegatable task + * for finished messages. + */ + if ((conn != null) || expectingFinished) { + processLoop(); + } else { + delegateTask(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + processLoop(); + return null; + } + }); + } + } + + /* + * On input, we hash messages one at a time since servers may need + * to access an intermediate hash to validate a CertificateVerify + * message. + * + * Note that many handshake messages can come in one record (and often + * do, to reduce network resource utilization), and one message can also + * require multiple records (e.g. very large Certificate messages). + */ + void processLoop() throws IOException { + + // need to read off 4 bytes at least to get the handshake + // message type and length. + while (input.available() >= 4) { + byte messageType; + int messageLen; + + /* + * See if we can read the handshake message header, and + * then the entire handshake message. If not, wait till + * we can read and process an entire message. + */ + input.mark(4); + + messageType = (byte)input.getInt8(); + messageLen = input.getInt24(); + + if (input.available() < messageLen) { + input.reset(); + return; + } + + // Set the flags in the message receiving side. + if (messageType == HandshakeMessage.ht_client_hello) { + clientHelloDelivered = true; + } + + /* + * Process the message. We require + * that processMessage() consumes the entire message. In + * lieu of explicit error checks (how?!) we assume that the + * data will look like garbage on encoding/processing errors, + * and that other protocol code will detect such errors. + * + * Note that digesting is normally deferred till after the + * message has been processed, though to process at least the + * client's Finished message (i.e. send the server's) we need + * to acccelerate that digesting. + * + * Also, note that hello request messages are never hashed; + * that includes the hello request header, too. + */ + if (messageType == HandshakeMessage.ht_hello_request) { + input.reset(); + processMessage(messageType, messageLen); + input.ignore(4 + messageLen); + } else { + input.mark(messageLen); + processMessage(messageType, messageLen); + input.digestNow(); + } + } + } + + + /** + * Returns true iff the handshaker has been activated. + * + * In activated state, the handshaker may not send any messages out. + */ + boolean activated() { + return state >= -1; + } + + /** + * Returns true iff the handshaker has sent any messages. + */ + boolean started() { + return (clientHelloDelivered || + (state >= 0)); // 0: HandshakeMessage.ht_hello_request + // 1: HandshakeMessage.ht_client_hello + } + + + /* + * Used to kickstart the negotiation ... either writing a + * ClientHello or a HelloRequest as appropriate, whichever + * the subclass returns. NOP if handshaking's already started. + */ + void kickstart() throws IOException { + if (state >= 0) { + return; + } + + HandshakeMessage m = getKickstartMessage(); + + if (debug != null && Debug.isOn("handshake")) { + m.print(System.out); + } + m.write(output); + output.flush(); + + state = m.messageType(); + } + + /** + * Both client and server modes can start handshaking; but the + * message they send to do so is different. + */ + abstract HandshakeMessage getKickstartMessage() throws SSLException; + + /* + * Client and Server side protocols are each driven though this + * call, which processes a single message and drives the appropriate + * side of the protocol state machine (depending on the subclass). + */ + abstract void processMessage(byte messageType, int messageLen) + throws IOException; + + /* + * Most alerts in the protocol relate to handshaking problems. + * Alerts are detected as the connection reads data. + */ + abstract void handshakeAlert(byte description) throws SSLProtocolException; + + /* + * Sends a change cipher spec message and updates the write side + * cipher state so that future messages use the just-negotiated spec. + */ + void sendChangeCipherSpec(Finished mesg, boolean lastMessage) + throws IOException { + + output.flush(); // i.e. handshake data + + /* + * The write cipher state is protected by the connection write lock + * so we must grab it while making the change. We also + * make sure no writes occur between sending the ChangeCipherSpec + * message, installing the new cipher state, and sending the + * Finished message. + * + * We already hold SSLEngine/SSLSocket "this" by virtue + * of this being called from the readRecord code. + */ + OutputRecord r; + if (conn != null) { + r = new OutputRecord(Record.ct_change_cipher_spec); + } else { + r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine); + } + + r.setVersion(protocolVersion); + r.write(1); // single byte of data + + if (conn != null) { + conn.writeLock.lock(); + try { + conn.writeRecord(r); + conn.changeWriteCiphers(); + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + mesg.write(output); + output.flush(); + } finally { + conn.writeLock.unlock(); + } + } else { + synchronized (engine.writeLock) { + engine.writeRecord((EngineOutputRecord)r); + engine.changeWriteCiphers(); + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + mesg.write(output); + + if (lastMessage) { + output.setFinishedMsg(); + } + output.flush(); + } + } + } + + /* + * Single access point to key calculation logic. Given the + * pre-master secret and the nonces from client and server, + * produce all the keying material to be used. + */ + void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) { + SecretKey master = calculateMasterSecret(preMasterSecret, version); + session.setMasterSecret(master); + calculateConnectionKeys(master); + } + + + /* + * Calculate the master secret from its various components. This is + * used for key exchange by all cipher suites. + * + * The master secret is the catenation of three MD5 hashes, each + * consisting of the pre-master secret and a SHA1 hash. Those three + * SHA1 hashes are of (different) constant strings, the pre-master + * secret, and the nonces provided by the client and the server. + */ + private SecretKey calculateMasterSecret(SecretKey preMasterSecret, + ProtocolVersion requestedVersion) { + + if (debug != null && Debug.isOn("keygen")) { + HexDumpEncoder dump = new HexDumpEncoder(); + + System.out.println("SESSION KEYGEN:"); + + System.out.println("PreMaster Secret:"); + printHex(dump, preMasterSecret.getEncoded()); + + // Nonces are dumped with connection keygen, no + // benefit to doing it twice + } + + // What algs/params do we need to use? + String masterAlg; + PRF prf; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + masterAlg = "SunTls12MasterSecret"; + prf = cipherSuite.prfAlg; + } else { + masterAlg = "SunTlsMasterSecret"; + prf = P_NONE; + } + + String prfHashAlg = prf.getPRFHashAlg(); + int prfHashLength = prf.getPRFHashLength(); + int prfBlockSize = prf.getPRFBlockSize(); + + TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( + preMasterSecret, protocolVersion.major, protocolVersion.minor, + clnt_random.random_bytes, svr_random.random_bytes, + prfHashAlg, prfHashLength, prfBlockSize); + + try { + KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); + kg.init(spec); + return kg.generateKey(); + } catch (InvalidAlgorithmParameterException | + NoSuchAlgorithmException iae) { + // unlikely to happen, otherwise, must be a provider exception + // + // For RSA premaster secrets, do not signal a protocol error + // due to the Bleichenbacher attack. See comments further down. + if (debug != null && Debug.isOn("handshake")) { + System.out.println("RSA master secret generation error:"); + iae.printStackTrace(System.out); + } + throw new ProviderException(iae); + + } + } + + /* + * Calculate the keys needed for this connection, once the session's + * master secret has been calculated. Uses the master key and nonces; + * the amount of keying material generated is a function of the cipher + * suite that's been negotiated. + * + * This gets called both on the "full handshake" (where we exchanged + * a premaster secret and started a new session) as well as on the + * "fast handshake" (where we just resumed a pre-existing session). + */ + void calculateConnectionKeys(SecretKey masterKey) { + /* + * For both the read and write sides of the protocol, we use the + * master to generate MAC secrets and cipher keying material. Block + * ciphers need initialization vectors, which we also generate. + * + * First we figure out how much keying material is needed. + */ + int hashSize = cipherSuite.macAlg.size; + boolean is_exportable = cipherSuite.exportable; + BulkCipher cipher = cipherSuite.cipher; + int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; + + // Which algs/params do we need to use? + String keyMaterialAlg; + PRF prf; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + keyMaterialAlg = "SunTls12KeyMaterial"; + prf = cipherSuite.prfAlg; + } else { + keyMaterialAlg = "SunTlsKeyMaterial"; + prf = P_NONE; + } + + String prfHashAlg = prf.getPRFHashAlg(); + int prfHashLength = prf.getPRFHashLength(); + int prfBlockSize = prf.getPRFBlockSize(); + + // TLS v1.1 or later uses an explicit IV in CBC cipher suites to + // protect against the CBC attacks. AEAD/GCM cipher suites in TLS + // v1.2 or later use a fixed IV as the implicit part of the partially + // implicit nonce technique described in RFC 5116. + int ivSize = cipher.ivSize; + if (cipher.cipherType == AEAD_CIPHER) { + ivSize = cipher.fixedIvSize; + } else if (protocolVersion.v >= ProtocolVersion.TLS11.v && + cipher.cipherType == BLOCK_CIPHER) { + ivSize = 0; + } + + TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( + masterKey, protocolVersion.major, protocolVersion.minor, + clnt_random.random_bytes, svr_random.random_bytes, + cipher.algorithm, cipher.keySize, expandedKeySize, + ivSize, hashSize, + prfHashAlg, prfHashLength, prfBlockSize); + + try { + KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); + kg.init(spec); + TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec)kg.generateKey(); + + // Return null if cipher keys are not supposed to be generated. + clntWriteKey = keySpec.getClientCipherKey(); + svrWriteKey = keySpec.getServerCipherKey(); + + // Return null if IVs are not supposed to be generated. + clntWriteIV = keySpec.getClientIv(); + svrWriteIV = keySpec.getServerIv(); + + // Return null if MAC keys are not supposed to be generated. + clntMacSecret = keySpec.getClientMacKey(); + svrMacSecret = keySpec.getServerMacKey(); + } catch (GeneralSecurityException e) { + throw new ProviderException(e); + } + + // Mark a flag that allows outside entities (like SSLSocket/SSLEngine) + // determine if a ChangeCipherSpec message could be processed. + sessKeysCalculated = true; + + // + // Dump the connection keys as they're generated. + // + if (debug != null && Debug.isOn("keygen")) { + synchronized (System.out) { + HexDumpEncoder dump = new HexDumpEncoder(); + + System.out.println("CONNECTION KEYGEN:"); + + // Inputs: + System.out.println("Client Nonce:"); + printHex(dump, clnt_random.random_bytes); + System.out.println("Server Nonce:"); + printHex(dump, svr_random.random_bytes); + System.out.println("Master Secret:"); + printHex(dump, masterKey.getEncoded()); + + // Outputs: + if (clntMacSecret != null) { + System.out.println("Client MAC write Secret:"); + printHex(dump, clntMacSecret.getEncoded()); + System.out.println("Server MAC write Secret:"); + printHex(dump, svrMacSecret.getEncoded()); + } else { + System.out.println("... no MAC keys used for this cipher"); + } + + if (clntWriteKey != null) { + System.out.println("Client write key:"); + printHex(dump, clntWriteKey.getEncoded()); + System.out.println("Server write key:"); + printHex(dump, svrWriteKey.getEncoded()); + } else { + System.out.println("... no encryption keys used"); + } + + if (clntWriteIV != null) { + System.out.println("Client write IV:"); + printHex(dump, clntWriteIV.getIV()); + System.out.println("Server write IV:"); + printHex(dump, svrWriteIV.getIV()); + } else { + if (protocolVersion.v >= ProtocolVersion.TLS11.v) { + System.out.println( + "... no IV derived for this protocol"); + } else { + System.out.println("... no IV used for this cipher"); + } + } + System.out.flush(); + } + } + } + + /** + * Return whether or not the Handshaker has derived session keys for + * this handshake. This is used for determining readiness to process + * an incoming ChangeCipherSpec message. + */ + boolean sessionKeysCalculated() { + return sessKeysCalculated; + } + + private static void printHex(HexDumpEncoder dump, byte[] bytes) { + if (bytes == null) { + System.out.println("(key bytes not available)"); + } else { + try { + dump.encodeBuffer(bytes, System.out); + } catch (IOException e) { + // just for debugging, ignore this + } + } + } + + /** + * Throw an SSLException with the specified message and cause. + * Shorthand until a new SSLException constructor is added. + * This method never returns. + */ + static void throwSSLException(String msg, Throwable cause) + throws SSLException { + SSLException e = new SSLException(msg); + e.initCause(cause); + throw e; + } + + + /* + * Implement a simple task delegator. + * + * We are currently implementing this as a single delegator, may + * try for parallel tasks later. Client Authentication could + * benefit from this, where ClientKeyExchange/CertificateVerify + * could be carried out in parallel. + */ + class DelegatedTask implements Runnable { + + private PrivilegedExceptionAction pea; + + DelegatedTask(PrivilegedExceptionAction pea) { + this.pea = pea; + } + + public void run() { + synchronized (engine) { + try { + AccessController.doPrivileged(pea, engine.getAcc()); + } catch (PrivilegedActionException pae) { + thrown = pae.getException(); + } catch (RuntimeException rte) { + thrown = rte; + } + delegatedTask = null; + taskDelegated = false; + } + } + } + + private void delegateTask(PrivilegedExceptionAction pea) { + delegatedTask = new DelegatedTask(pea); + taskDelegated = false; + thrown = null; + } + + DelegatedTask getTask() { + if (!taskDelegated) { + taskDelegated = true; + return delegatedTask; + } else { + return null; + } + } + + /* + * See if there are any tasks which need to be delegated + * + * Locked by SSLEngine.this. + */ + boolean taskOutstanding() { + return (delegatedTask != null); + } + + /* + * The previous caller failed for some reason, report back the + * Exception. We won't worry about Error's. + * + * Locked by SSLEngine.this. + */ + void checkThrown() throws SSLException { + synchronized (thrownLock) { + if (thrown != null) { + + String msg = thrown.getMessage(); + + if (msg == null) { + msg = "Delegated task threw Exception/Error"; + } + + /* + * See what the underlying type of exception is. We should + * throw the same thing. Chain thrown to the new exception. + */ + Exception e = thrown; + thrown = null; + + if (e instanceof RuntimeException) { + throw new RuntimeException(msg, e); + } else if (e instanceof SSLHandshakeException) { + throw (SSLHandshakeException) + new SSLHandshakeException(msg).initCause(e); + } else if (e instanceof SSLKeyException) { + throw (SSLKeyException) + new SSLKeyException(msg).initCause(e); + } else if (e instanceof SSLPeerUnverifiedException) { + throw (SSLPeerUnverifiedException) + new SSLPeerUnverifiedException(msg).initCause(e); + } else if (e instanceof SSLProtocolException) { + throw (SSLProtocolException) + new SSLProtocolException(msg).initCause(e); + } else { + /* + * If it's SSLException or any other Exception, + * we'll wrap it in an SSLException. + */ + throw new SSLException(msg, e); + } + } + } + } +} diff --git a/src/sun/security/ssl/HelloExtension.java b/src/sun/security/ssl/HelloExtension.java new file mode 100644 index 00000000..b36dd859 --- /dev/null +++ b/src/sun/security/ssl/HelloExtension.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +abstract class HelloExtension { + + final ExtensionType type; + + HelloExtension(ExtensionType type) { + this.type = type; + } + + // Length of the encoded extension, including the type and length fields + abstract int length(); + + abstract void send(HandshakeOutStream s) throws IOException; + + @Override + public abstract String toString(); + +} diff --git a/src/sun/security/ssl/HelloExtensions.java b/src/sun/security/ssl/HelloExtensions.java new file mode 100644 index 00000000..f188f74f --- /dev/null +++ b/src/sun/security/ssl/HelloExtensions.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.*; +import javax.net.ssl.*; + +/** + * This file contains all the classes relevant to TLS Extensions for the + * ClientHello and ServerHello messages. The extension mechanism and + * several extensions are defined in RFC 6066. Additional extensions are + * defined in the ECC RFC 4492 and the ALPN extension is defined in RFC 7301. + * + * Currently, only the two ECC extensions are fully supported. + * + * The classes contained in this file are: + * . HelloExtensions: a List of extensions as used in the client hello + * and server hello messages. + * . ExtensionType: an enum style class for the extension type + * . HelloExtension: abstract base class for all extensions. All subclasses + * must be immutable. + * + * . UnknownExtension: used to represent all parsed extensions that we do not + * explicitly support. + * . ServerNameExtension: the server_name extension. + * . SignatureAlgorithmsExtension: the signature_algorithms extension. + * . SupportedEllipticCurvesExtension: the ECC supported curves extension. + * . SupportedEllipticPointFormatsExtension: the ECC supported point formats + * (compressed/uncompressed) extension. + * . ALPNExtension: the application_layer_protocol_negotiation extension. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class HelloExtensions { + + private List extensions; + private int encodedLength; + + HelloExtensions() { + extensions = Collections.emptyList(); + } + + HelloExtensions(HandshakeInStream s) throws IOException { + int len = s.getInt16(); + extensions = new ArrayList(); + encodedLength = len + 2; + while (len > 0) { + int type = s.getInt16(); + int extlen = s.getInt16(); + ExtensionType extType = ExtensionType.get(type); + HelloExtension extension; + if (extType == ExtensionType.EXT_SERVER_NAME) { + extension = new ServerNameExtension(s, extlen); + } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) { + extension = new SignatureAlgorithmsExtension(s, extlen); + } else if (extType == ExtensionType.EXT_ELLIPTIC_CURVES) { + extension = new SupportedEllipticCurvesExtension(s, extlen); + } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) { + extension = + new SupportedEllipticPointFormatsExtension(s, extlen); + } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) { + extension = new RenegotiationInfoExtension(s, extlen); + } else if (extType == ExtensionType.EXT_ALPN) { + extension = new ALPNExtension(s, extlen); + } else { + extension = new UnknownExtension(s, extlen, extType); + } + extensions.add(extension); + len -= extlen + 4; + } + if (len != 0) { + throw new SSLProtocolException( + "Error parsing extensions: extra data"); + } + } + + // Return the List of extensions. Must not be modified by the caller. + List list() { + return extensions; + } + + void add(HelloExtension ext) { + if (extensions.isEmpty()) { + extensions = new ArrayList(); + } + extensions.add(ext); + encodedLength = -1; + } + + HelloExtension get(ExtensionType type) { + for (HelloExtension ext : extensions) { + if (ext.type == type) { + return ext; + } + } + return null; + } + + int length() { + if (encodedLength >= 0) { + return encodedLength; + } + if (extensions.isEmpty()) { + encodedLength = 0; + } else { + encodedLength = 2; + for (HelloExtension ext : extensions) { + encodedLength += ext.length(); + } + } + return encodedLength; + } + + void send(HandshakeOutStream s) throws IOException { + int length = length(); + if (length == 0) { + return; + } + s.putInt16(length - 2); + for (HelloExtension ext : extensions) { + ext.send(s); + } + } + + void print(PrintStream s) throws IOException { + for (HelloExtension ext : extensions) { + s.println(ext.toString()); + } + } +} diff --git a/src/sun/security/ssl/InputRecord.java b/src/sun/security/ssl/InputRecord.java new file mode 100644 index 00000000..b4d97c7f --- /dev/null +++ b/src/sun/security/ssl/InputRecord.java @@ -0,0 +1,872 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; + +import javax.crypto.BadPaddingException; + +import javax.net.ssl.*; + +import sun.misc.HexDumpEncoder; + + +/** + * SSL 3.0 records, as pulled off a TCP stream. Input records are + * basically buffers tied to a particular input stream ... a layer + * above this must map these records into the model of a continuous + * stream of data. + * + * Since this returns SSL 3.0 records, it's the layer that needs to + * map SSL 2.0 style handshake records into SSL 3.0 ones for those + * "old" clients that interop with both V2 and V3 servers. Not as + * pretty as might be desired. + * + * NOTE: During handshaking, each message must be hashed to support + * verification that the handshake process wasn't compromised. + * + * @author David Brownell + */ +class InputRecord extends ByteArrayInputStream implements Record { + + private HandshakeHash handshakeHash; + private int lastHashed; + boolean formatVerified = true; // SSLv2 ruled out? + private boolean isClosed; + private boolean appDataValid; + + // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello + // and the first message we read is a ClientHello in V2 format, we convert + // it to V3. Otherwise we throw an exception when encountering a V2 hello. + private ProtocolVersion helloVersion; + + /* Class and subclass dynamic debugging support */ + static final Debug debug = Debug.getInstance("ssl"); + + /* The existing record length */ + private int exlen; + + /* V2 handshake message */ + private byte v2Buf[]; + + /* + * Construct the record to hold the maximum sized input record. + * Data will be filled in separately. + * + * The structure of the byte buffer looks like: + * + * |--------+---------+---------------------------------| + * | header | IV | content, MAC/TAG, padding, etc. | + * | headerPlusIVSize | + * + * header: the header of an SSL records + * IV: the optional IV/nonce field, it is only required for block + * (TLS 1.1 or later) and AEAD cipher suites. + * + */ + InputRecord() { + super(new byte[maxRecordSize]); + setHelloVersion(ProtocolVersion.DEFAULT_HELLO); + pos = headerSize; + count = headerSize; + lastHashed = count; + exlen = 0; + v2Buf = null; + } + + void setHelloVersion(ProtocolVersion helloVersion) { + this.helloVersion = helloVersion; + } + + ProtocolVersion getHelloVersion() { + return helloVersion; + } + + /* + * Enable format checks if initial handshaking hasn't completed + */ + void enableFormatChecks() { + formatVerified = false; + } + + // return whether the data in this record is valid, decrypted data + boolean isAppDataValid() { + return appDataValid; + } + + void setAppDataValid(boolean value) { + appDataValid = value; + } + + /* + * Return the content type of the record. + */ + byte contentType() { + return buf[0]; + } + + /* + * For handshaking, we need to be able to hash every byte above the + * record marking layer. This is where we're guaranteed to see those + * bytes, so this is where we can hash them ... especially in the + * case of hashing the initial V2 message! + */ + void setHandshakeHash(HandshakeHash handshakeHash) { + this.handshakeHash = handshakeHash; + } + + HandshakeHash getHandshakeHash() { + return handshakeHash; + } + + void decrypt(Authenticator authenticator, + CipherBox box) throws BadPaddingException { + BadPaddingException reservedBPE = null; + int tagLen = + (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; + int cipheredLength = count - headerSize; + + if (!box.isNullCipher()) { + try { + // apply explicit nonce for AEAD/CBC cipher suites if needed + int nonceSize = box.applyExplicitNonce(authenticator, + contentType(), buf, headerSize, cipheredLength); + pos = headerSize + nonceSize; + lastHashed = pos; // don't digest the explicit nonce + + // decrypt the content + int offset = headerSize; + if (box.isAEADMode()) { + // DON'T encrypt the nonce_explicit for AEAD mode + offset += nonceSize; + } // The explicit IV for CBC mode can be decrypted. + + // Note that the CipherBox.decrypt() does not change + // the capacity of the buffer. + count = offset + + box.decrypt(buf, offset, count - offset, tagLen); + + // Note that we don't remove the nonce from the buffer. + } catch (BadPaddingException bpe) { + // RFC 2246 states that decryption_failed should be used + // for this purpose. However, that allows certain attacks, + // so we just send bad record MAC. We also need to make + // sure to always check the MAC to avoid a timing attack + // for the same issue. See paper by Vaudenay et al and the + // update in RFC 4346/5246. + // + // Failover to message authentication code checking. + reservedBPE = bpe; + } + } + + // Requires message authentication code for null, stream and block + // cipher suites. + if (authenticator instanceof MAC && tagLen != 0) { + MAC signer = (MAC)authenticator; + int macOffset = count - tagLen; + int contentLen = macOffset - pos; + + // Note that although it is not necessary, we run the same MAC + // computation and comparison on the payload for both stream + // cipher and CBC block cipher. + if (contentLen < 0) { + // negative data length, something is wrong + if (reservedBPE == null) { + reservedBPE = new BadPaddingException("bad record"); + } + + // set offset of the dummy MAC + macOffset = headerSize + cipheredLength - tagLen; + contentLen = macOffset - headerSize; + } + + count -= tagLen; // Set the count before any MAC checking + // exception occurs, so that the following + // process can read the actual decrypted + // content (minus the MAC) in the fragment + // if necessary. + + // Run MAC computation and comparison on the payload. + if (checkMacTags(contentType(), + buf, pos, contentLen, signer, false)) { + if (reservedBPE == null) { + reservedBPE = new BadPaddingException("bad record MAC"); + } + } + + // Run MAC computation and comparison on the remainder. + // + // It is only necessary for CBC block cipher. It is used to get a + // constant time of MAC computation and comparison on each record. + if (box.isCBCMode()) { + int remainingLen = calculateRemainingLen( + signer, cipheredLength, contentLen); + + // NOTE: remainingLen may be bigger (less than 1 block of the + // hash algorithm of the MAC) than the cipheredLength. However, + // We won't need to worry about it because we always use a + // maximum buffer for every record. We need a change here if + // we use small buffer size in the future. + if (remainingLen > buf.length) { + // unlikely to happen, just a placehold + throw new RuntimeException( + "Internal buffer capacity error"); + } + + // Won't need to worry about the result on the remainder. And + // then we won't need to worry about what's actual data to + // check MAC tag on. We start the check from the header of the + // buffer so that we don't need to construct a new byte buffer. + checkMacTags(contentType(), buf, 0, remainingLen, signer, true); + } + } + + // Is it a failover? + if (reservedBPE != null) { + throw reservedBPE; + } + } + + /* + * Run MAC computation and comparison + * + * Please DON'T change the content of the byte buffer parameter! + */ + static boolean checkMacTags(byte contentType, byte[] buffer, + int offset, int contentLen, MAC signer, boolean isSimulated) { + + int tagLen = signer.MAClen(); + byte[] hash = signer.compute( + contentType, buffer, offset, contentLen, isSimulated); + if (hash == null || tagLen != hash.length) { + // Something is wrong with MAC implementation. + throw new RuntimeException("Internal MAC error"); + } + + int[] results = compareMacTags(buffer, offset + contentLen, hash); + return (results[0] != 0); + } + + /* + * A constant-time comparison of the MAC tags. + * + * Please DON'T change the content of the byte buffer parameter! + */ + private static int[] compareMacTags( + byte[] buffer, int offset, byte[] tag) { + + // An array of hits is used to prevent Hotspot optimization for + // the purpose of a constant-time check. + int[] results = {0, 0}; // {missed #, matched #} + + // The caller ensures there are enough bytes available in the buffer. + // So we won't need to check the length of the buffer. + for (int i = 0; i < tag.length; i++) { + if (buffer[offset + i] != tag[i]) { + results[0]++; // mismatched bytes + } else { + results[1]++; // matched bytes + } + } + + return results; + } + + /* + * Calculate the length of a dummy buffer to run MAC computation + * and comparison on the remainder. + * + * The caller MUST ensure that the fullLen is not less than usedLen. + */ + static int calculateRemainingLen( + MAC signer, int fullLen, int usedLen) { + + int blockLen = signer.hashBlockLen(); + int minimalPaddingLen = signer.minimalPaddingLen(); + + // (blockLen - minimalPaddingLen) is the maximum message size of + // the last block of hash function operation. See FIPS 180-4, or + // MD5 specification. + fullLen += 13 - (blockLen - minimalPaddingLen); + usedLen += 13 - (blockLen - minimalPaddingLen); + + // Note: fullLen is always not less than usedLen, and blockLen + // is always bigger than minimalPaddingLen, so we don't worry + // about negative values. 0x01 is added to the result to ensure + // that the return value is positive. The extra one byte does + // not impact the overall MAC compression function evaluations. + return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) - + Math.ceil(usedLen/(1.0d * blockLen))) * signer.hashBlockLen(); + } + + /* + * Well ... hello_request messages are _never_ hashed since we can't + * know when they'd appear in the sequence. + */ + void ignore(int bytes) { + if (bytes > 0) { + pos += bytes; + lastHashed = pos; + } + } + + /* + * We hash the (plaintext) we've processed, but only on demand. + * + * There is one place where we want to access the hash in the middle + * of a record: client cert message gets hashed, and part of the + * same record is the client cert verify message which uses that hash. + * So we track how much we've read and hashed. + */ + void doHashes() { + int len = pos - lastHashed; + + if (len > 0) { + hashInternal(buf, lastHashed, len); + lastHashed = pos; + } + } + + /* + * Need a helper function so we can hash the V2 hello correctly + */ + private void hashInternal(byte databuf [], int offset, int len) { + if (debug != null && Debug.isOn("data")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println("[read] MD5 and SHA1 hashes: len = " + + len); + hd.encodeBuffer(new ByteArrayInputStream(databuf, offset, len), + System.out); + } catch (IOException e) { } + } + handshakeHash.update(databuf, offset, len); + } + + + /* + * Handshake messages may cross record boundaries. We "queue" + * these in big buffers if we need to cope with this problem. + * This is not anticipated to be a common case; if this turns + * out to be wrong, this can readily be sped up. + */ + void queueHandshake(InputRecord r) throws IOException { + int len; + + /* + * Hash any data that's read but unhashed. + */ + doHashes(); + + /* + * Move any unread data to the front of the buffer, + * flagging it all as unhashed. + */ + if (pos > headerSize) { + len = count - pos; + if (len != 0) { + System.arraycopy(buf, pos, buf, headerSize, len); + } + pos = headerSize; + lastHashed = pos; + count = headerSize + len; + } + + /* + * Grow "buf" if needed + */ + len = r.available() + count; + if (buf.length < len) { + byte newbuf []; + + newbuf = new byte [len]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + /* + * Append the new buffer to this one. + */ + System.arraycopy(r.buf, r.pos, buf, count, len - count); + count = len; + + /* + * Adjust lastHashed; important for now with clients which + * send SSL V2 client hellos. This will go away eventually, + * by buffer code cleanup. + */ + len = r.lastHashed - r.pos; + if (pos == headerSize) { + lastHashed += len; + } else { + throw new SSLProtocolException("?? confused buffer hashing ??"); + } + // we've read the record, advance the pointers + r.pos = r.count; + } + + + /** + * Prevent any more data from being read into this record, + * and flag the record as holding no data. + */ + @Override + public void close() { + appDataValid = false; + isClosed = true; + mark = 0; + pos = 0; + count = 0; + } + + + /* + * We may need to send this SSL v2 "No Cipher" message back, if we + * are faced with an SSLv2 "hello" that's not saying "I talk v3". + * It's the only one documented in the V2 spec as a fatal error. + */ + private static final byte[] v2NoCipher = { + (byte)0x80, (byte)0x03, // unpadded 3 byte record + (byte)0x00, // ... error message + (byte)0x00, (byte)0x01 // ... NO_CIPHER error + }; + + private int readFully(InputStream s, byte b[], int off, int len) + throws IOException { + int n = 0; + while (n < len) { + int readLen = s.read(b, off + n, len - n); + if (readLen < 0) { + return readLen; + } + + if (debug != null && Debug.isOn("packet")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + ByteBuffer bb = ByteBuffer.wrap(b, off + n, readLen); + + System.out.println("[Raw read]: length = " + + bb.remaining()); + hd.encodeBuffer(bb, System.out); + } catch (IOException e) { } + } + + n += readLen; + exlen += readLen; + } + + return n; + } + + /* + * Read the SSL V3 record ... first time around, check to see if it + * really IS a V3 record. Handle SSL V2 clients which can talk V3.0, + * as well as real V3 record format; otherwise report an error. + */ + void read(InputStream s, OutputStream o) throws IOException { + if (isClosed) { + return; + } + + /* + * For SSL it really _is_ an error if the other end went away + * so ungracefully as to not shut down cleanly. + */ + if(exlen < headerSize) { + int really = readFully(s, buf, exlen, headerSize - exlen); + if (really < 0) { + throw new EOFException("SSL peer shut down incorrectly"); + } + + pos = headerSize; + count = headerSize; + lastHashed = pos; + } + + /* + * The first record might use some other record marking convention, + * typically SSL v2 header. (PCT could also be detected here.) + * This case is currently common -- Navigator 3.0 usually works + * this way, as do IE 3.0 and other products. + */ + if (!formatVerified) { + formatVerified = true; + /* + * The first record must either be a handshake record or an + * alert message. If it's not, it is either invalid or an + * SSLv2 message. + */ + if (buf[0] != ct_handshake && buf[0] != ct_alert) { + handleUnknownRecord(s, o); + } else { + readV3Record(s, o); + } + } else { // formatVerified == true + readV3Record(s, o); + } + } + + /** + * Return true if the specified record protocol version is out of the + * range of the possible supported versions. + */ + static void checkRecordVersion(ProtocolVersion version, + boolean allowSSL20Hello) throws SSLException { + // Check if the record version is too old (currently not possible) + // or if the major version does not match. + // + // The actual version negotiation is in the handshaker classes + if ((version.v < ProtocolVersion.MIN.v) || + ((version.major & 0xFF) > (ProtocolVersion.MAX.major & 0xFF))) { + + // if it's not SSLv2, we're out of here. + if (!allowSSL20Hello || + (version.v != ProtocolVersion.SSL20Hello.v)) { + throw new SSLException("Unsupported record version " + version); + } + } + } + + /** + * Read a SSL/TLS record. Throw an IOException if the format is invalid. + */ + private void readV3Record(InputStream s, OutputStream o) + throws IOException { + ProtocolVersion recordVersion = ProtocolVersion.valueOf(buf[1], buf[2]); + + // check the record version + checkRecordVersion(recordVersion, false); + + /* + * Get and check length, then the data. + */ + int contentLen = ((buf[3] & 0x0ff) << 8) + (buf[4] & 0xff); + + /* + * Check for upper bound. + */ + if (contentLen < 0 || contentLen > maxLargeRecordSize - headerSize) { + throw new SSLProtocolException("Bad InputRecord size" + + ", count = " + contentLen + + ", buf.length = " + buf.length); + } + + /* + * Grow "buf" if needed. Since buf is maxRecordSize by default, + * this only occurs when we receive records which violate the + * SSL specification. This is a workaround for a Microsoft SSL bug. + */ + if (contentLen > buf.length - headerSize) { + byte[] newbuf = new byte[contentLen + headerSize]; + System.arraycopy(buf, 0, newbuf, 0, headerSize); + buf = newbuf; + } + + if (exlen < contentLen + headerSize) { + int really = readFully( + s, buf, exlen, contentLen + headerSize - exlen); + if (really < 0) { + throw new SSLException("SSL peer shut down incorrectly"); + } + } + + // now we've got a complete record. + count = contentLen + headerSize; + exlen = 0; + + if (debug != null && Debug.isOn("record")) { + if (count < 0 || count > (maxRecordSize - headerSize)) { + System.out.println(Thread.currentThread().getName() + + ", Bad InputRecord size" + ", count = " + count); + } + System.out.println(Thread.currentThread().getName() + + ", READ: " + recordVersion + " " + + contentName(contentType()) + ", length = " + available()); + } + /* + * then caller decrypts, verifies, and uncompresses + */ + } + + /** + * Deal with unknown records. Called if the first data we read on this + * connection does not look like an SSL/TLS record. It could a SSLv2 + * message, or just garbage. + */ + private void handleUnknownRecord(InputStream s, OutputStream o) + throws IOException { + /* + * No? Oh well; does it look like a V2 "ClientHello"? + * That'd be an unpadded handshake message; we don't + * bother checking length just now. + */ + if (((buf[0] & 0x080) != 0) && buf[2] == 1) { + /* + * if the user has disabled SSLv2Hello (using + * setEnabledProtocol) then throw an + * exception + */ + if (helloVersion != ProtocolVersion.SSL20Hello) { + throw new SSLHandshakeException("SSLv2Hello is disabled"); + } + + ProtocolVersion recordVersion = + ProtocolVersion.valueOf(buf[3], buf[4]); + + if (recordVersion == ProtocolVersion.SSL20Hello) { + /* + * Looks like a V2 client hello, but not one saying + * "let's talk SSLv3". So we send an SSLv2 error + * message, one that's treated as fatal by clients. + * (Otherwise we'll hang.) + */ + try { + writeBuffer(o, v2NoCipher, 0, v2NoCipher.length); + } catch (Exception e) { + /* NOTHING */ + } + throw new SSLException("Unsupported SSL v2.0 ClientHello"); + } + + /* + * If we can map this into a V3 ClientHello, read and + * hash the rest of the V2 handshake, turn it into a + * V3 ClientHello message, and pass it up. + */ + int len = ((buf[0] & 0x7f) << 8) + + (buf[1] & 0xff) - 3; + if (v2Buf == null) { + v2Buf = new byte[len]; + } + if (exlen < len + headerSize) { + int really = readFully( + s, v2Buf, exlen - headerSize, len + headerSize - exlen); + if (really < 0) { + throw new EOFException("SSL peer shut down incorrectly"); + } + } + + // now we've got a complete record. + exlen = 0; + + hashInternal(buf, 2, 3); + hashInternal(v2Buf, 0, len); + V2toV3ClientHello(v2Buf); + v2Buf = null; + lastHashed = count; + + if (debug != null && Debug.isOn("record")) { + System.out.println( + Thread.currentThread().getName() + + ", READ: SSL v2, contentType = " + + contentName(contentType()) + + ", translated length = " + available()); + } + return; + + } else { + /* + * Does it look like a V2 "ServerHello"? + */ + if (((buf [0] & 0x080) != 0) && buf [2] == 4) { + throw new SSLException( + "SSL V2.0 servers are not supported."); + } + + /* + * If this is a V2 NoCipher message then this means + * the other server doesn't support V3. Otherwise, we just + * don't understand what it's saying. + */ + for (int i = 0; i < v2NoCipher.length; i++) { + if (buf[i] != v2NoCipher[i]) { + throw new SSLException( + "Unrecognized SSL message, plaintext connection?"); + } + } + + throw new SSLException("SSL V2.0 servers are not supported."); + } + } + + /* + * Actually do the write here. For SSLEngine's HS data, + * we'll override this method and let it take the appropriate + * action. + */ + void writeBuffer(OutputStream s, byte [] buf, int off, int len) + throws IOException { + s.write(buf, 0, len); + s.flush(); + } + + /* + * Support "old" clients which are capable of SSL V3.0 protocol ... for + * example, Navigator 3.0 clients. The V2 message is in the header and + * the bytes passed as parameter. This routine translates the V2 message + * into an equivalent V3 one. + */ + private void V2toV3ClientHello(byte v2Msg []) throws SSLException + { + int i; + + /* + * Build the first part of the V3 record header from the V2 one + * that's now buffered up. (Lengths are fixed up later). + */ + buf [0] = ct_handshake; + buf [1] = buf [3]; // V3.x + buf[2] = buf[4]; + // header [3..4] for handshake message length + // count = 5; + + /* + * Store the generic V3 handshake header: 4 bytes + */ + buf [5] = 1; // HandshakeMessage.ht_client_hello + // buf [6..8] for length of ClientHello (int24) + // count += 4; + + /* + * ClientHello header starts with SSL version + */ + buf [9] = buf [1]; + buf [10] = buf [2]; + // count += 2; + count = 11; + + /* + * Start parsing the V2 message ... + */ + int cipherSpecLen, sessionIdLen, nonceLen; + + cipherSpecLen = ((v2Msg [0] & 0xff) << 8) + (v2Msg [1] & 0xff); + sessionIdLen = ((v2Msg [2] & 0xff) << 8) + (v2Msg [3] & 0xff); + nonceLen = ((v2Msg [4] & 0xff) << 8) + (v2Msg [5] & 0xff); + + /* + * Copy Random value/nonce ... if less than the 32 bytes of + * a V3 "Random", right justify and zero pad to the left. Else + * just take the last 32 bytes. + */ + int offset = 6 + cipherSpecLen + sessionIdLen; + + if (nonceLen < 32) { + for (i = 0; i < (32 - nonceLen); i++) + buf [count++] = 0; + System.arraycopy(v2Msg, offset, buf, count, nonceLen); + count += nonceLen; + } else { + System.arraycopy(v2Msg, offset + (nonceLen - 32), + buf, count, 32); + count += 32; + } + + /* + * Copy Session ID (only one byte length!) + */ + offset -= sessionIdLen; + buf [count++] = (byte) sessionIdLen; + + System.arraycopy(v2Msg, offset, buf, count, sessionIdLen); + count += sessionIdLen; + + /* + * Copy and translate cipher suites ... V2 specs with first byte zero + * are really V3 specs (in the last 2 bytes), just copy those and drop + * the other ones. Preference order remains unchanged. + * + * Example: Netscape Navigator 3.0 (exportable) says: + * + * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5 + * 0/6, SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 + * + * Microsoft Internet Explorer 3.0 (exportable) supports only + * + * 0/3, SSL_RSA_EXPORT_WITH_RC4_40_MD5 + */ + int j; + + offset -= cipherSpecLen; + j = count + 2; + + for (i = 0; i < cipherSpecLen; i += 3) { + if (v2Msg [offset + i] != 0) + continue; + buf [j++] = v2Msg [offset + i + 1]; + buf [j++] = v2Msg [offset + i + 2]; + } + + j -= count + 2; + buf [count++] = (byte) (j >>> 8); + buf [count++] = (byte) j; + count += j; + + /* + * Append compression methods (default/null only) + */ + buf [count++] = 1; + buf [count++] = 0; // Session.compression_null + + /* + * Fill in lengths of the messages we synthesized (nested: + * V3 handshake message within V3 record) and then return + */ + buf [3] = (byte) (count - headerSize); + buf [4] = (byte) ((count - headerSize) >>> 8); + + buf [headerSize + 1] = 0; + buf [headerSize + 2] = (byte) (((count - headerSize) - 4) >>> 8); + buf [headerSize + 3] = (byte) ((count - headerSize) - 4); + + pos = headerSize; + } + + /** + * Return a description for the given content type. This method should be + * in Record, but since that is an interface this is not possible. + * Called from InputRecord and OutputRecord. + */ + static String contentName(int contentType) { + switch (contentType) { + case ct_change_cipher_spec: + return "Change Cipher Spec"; + case ct_alert: + return "Alert"; + case ct_handshake: + return "Handshake"; + case ct_application_data: + return "Application Data"; + default: + return "contentType = " + contentType; + } + } + +} diff --git a/src/sun/security/ssl/JsseJce.java b/src/sun/security/ssl/JsseJce.java new file mode 100644 index 00000000..4c98772a --- /dev/null +++ b/src/sun/security/ssl/JsseJce.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.*; +import java.math.BigInteger; + +import java.security.*; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.*; + +import javax.crypto.*; + +// explicit import to override the Provider class in this package +import java.security.Provider; + +// need internal Sun classes for FIPS tricks +import sun.security.jca.Providers; +import sun.security.jca.ProviderList; + +import sun.security.util.ECUtil; + +import static sun.security.ssl.SunJSSE.cryptoProvider; + +/** + * This class contains a few static methods for interaction with the JCA/JCE + * to obtain implementations, etc. + * + * @author Andreas Sterbenz + */ +final class JsseJce { + + private final static ProviderList fipsProviderList; + + // Flag indicating whether EC crypto is available. + // If null, then we have not checked yet. + // If yes, then all the EC based crypto we need is available. + private static Boolean ecAvailable; + + // Flag indicating whether Kerberos crypto is available. + // If true, then all the Kerberos-based crypto we need is available. + private final static boolean kerberosAvailable; + static { + boolean temp; + try { + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + // Test for Kerberos using the bootstrap class loader + Class.forName("sun.security.krb5.PrincipalName", true, + null); + return null; + } + }); + temp = true; + + } catch (Exception e) { + temp = false; + } + kerberosAvailable = temp; + } + + static { + // force FIPS flag initialization + // Because isFIPS() is synchronized and cryptoProvider is not modified + // after it completes, this also eliminates the need for any further + // synchronization when accessing cryptoProvider + if (SunJSSE.isFIPS() == false) { + fipsProviderList = null; + } else { + // Setup a ProviderList that can be used by the trust manager + // during certificate chain validation. All the crypto must be + // from the FIPS provider, but we also allow the required + // certificate related services from the SUN provider. + Provider sun = Security.getProvider("SUN"); + if (sun == null) { + throw new RuntimeException + ("FIPS mode: SUN provider must be installed"); + } + Provider sunCerts = new SunCertificates(sun); + fipsProviderList = ProviderList.newList(cryptoProvider, sunCerts); + } + } + + private static final class SunCertificates extends Provider { + private static final long serialVersionUID = -3284138292032213752L; + + SunCertificates(final Provider p) { + super("SunCertificates", 1.8d, "SunJSSE internal"); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + // copy certificate related services from the Sun provider + for (Map.Entry entry : p.entrySet()) { + String key = (String)entry.getKey(); + if (key.startsWith("CertPathValidator.") + || key.startsWith("CertPathBuilder.") + || key.startsWith("CertStore.") + || key.startsWith("CertificateFactory.")) { + put(key, entry.getValue()); + } + } + return null; + } + }); + } + } + + /** + * JCE transformation string for RSA with PKCS#1 v1.5 padding. + * Can be used for encryption, decryption, signing, verifying. + */ + final static String CIPHER_RSA_PKCS1 = "RSA/ECB/PKCS1Padding"; + /** + * JCE transformation string for the stream cipher RC4. + */ + final static String CIPHER_RC4 = "RC4"; + /** + * JCE transformation string for DES in CBC mode without padding. + */ + final static String CIPHER_DES = "DES/CBC/NoPadding"; + /** + * JCE transformation string for (3-key) Triple DES in CBC mode + * without padding. + */ + final static String CIPHER_3DES = "DESede/CBC/NoPadding"; + /** + * JCE transformation string for AES in CBC mode + * without padding. + */ + final static String CIPHER_AES = "AES/CBC/NoPadding"; + /** + * JCE transformation string for AES in GCM mode + * without padding. + */ + final static String CIPHER_AES_GCM = "AES/GCM/NoPadding"; + /** + * JCA identifier string for DSA, i.e. a DSA with SHA-1. + */ + final static String SIGNATURE_DSA = "DSA"; + /** + * JCA identifier string for ECDSA, i.e. a ECDSA with SHA-1. + */ + final static String SIGNATURE_ECDSA = "SHA1withECDSA"; + /** + * JCA identifier string for Raw DSA, i.e. a DSA signature without + * hashing where the application provides the SHA-1 hash of the data. + * Note that the standard name is "NONEwithDSA" but we use "RawDSA" + * for compatibility. + */ + final static String SIGNATURE_RAWDSA = "RawDSA"; + /** + * JCA identifier string for Raw ECDSA, i.e. a DSA signature without + * hashing where the application provides the SHA-1 hash of the data. + */ + final static String SIGNATURE_RAWECDSA = "NONEwithECDSA"; + /** + * JCA identifier string for Raw RSA, i.e. a RSA PKCS#1 v1.5 signature + * without hashing where the application provides the hash of the data. + * Used for RSA client authentication with a 36 byte hash. + */ + final static String SIGNATURE_RAWRSA = "NONEwithRSA"; + /** + * JCA identifier string for the SSL/TLS style RSA Signature. I.e. + * an signature using RSA with PKCS#1 v1.5 padding signing a + * concatenation of an MD5 and SHA-1 digest. + */ + final static String SIGNATURE_SSLRSA = "MD5andSHA1withRSA"; + + private JsseJce() { + // no instantiation of this class + } + + synchronized static boolean isEcAvailable() { + if (ecAvailable == null) { + try { + JsseJce.getSignature(SIGNATURE_ECDSA); + JsseJce.getSignature(SIGNATURE_RAWECDSA); + JsseJce.getKeyAgreement("ECDH"); + JsseJce.getKeyFactory("EC"); + JsseJce.getKeyPairGenerator("EC"); + ecAvailable = true; + } catch (Exception e) { + ecAvailable = false; + } + } + return ecAvailable; + } + + synchronized static void clearEcAvailable() { + ecAvailable = null; + } + + static boolean isKerberosAvailable() { + return kerberosAvailable; + } + + /** + * Return an JCE cipher implementation for the specified algorithm. + */ + static Cipher getCipher(String transformation) + throws NoSuchAlgorithmException { + try { + if (cryptoProvider == null) { + return Cipher.getInstance(transformation); + } else { + return Cipher.getInstance(transformation, cryptoProvider); + } + } catch (NoSuchPaddingException e) { + throw new NoSuchAlgorithmException(e); + } + } + + /** + * Return an JCA signature implementation for the specified algorithm. + * The algorithm string should be one of the constants defined + * in this class. + */ + static Signature getSignature(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return Signature.getInstance(algorithm); + } else { + // reference equality + if (algorithm == SIGNATURE_SSLRSA) { + // The SunPKCS11 provider currently does not support this + // special algorithm. We allow a fallback in this case because + // the SunJSSE implementation does the actual crypto using + // a NONEwithRSA signature obtained from the cryptoProvider. + if (cryptoProvider.getService("Signature", algorithm) == null) { + // Calling Signature.getInstance() and catching the + // exception would be cleaner, but exceptions are a little + // expensive. So we check directly via getService(). + try { + return Signature.getInstance(algorithm, "SunJSSE"); + } catch (NoSuchProviderException e) { + throw new NoSuchAlgorithmException(e); + } + } + } + return Signature.getInstance(algorithm, cryptoProvider); + } + } + + static KeyGenerator getKeyGenerator(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return KeyGenerator.getInstance(algorithm); + } else { + return KeyGenerator.getInstance(algorithm, cryptoProvider); + } + } + + static KeyPairGenerator getKeyPairGenerator(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return KeyPairGenerator.getInstance(algorithm); + } else { + return KeyPairGenerator.getInstance(algorithm, cryptoProvider); + } + } + + static KeyAgreement getKeyAgreement(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return KeyAgreement.getInstance(algorithm); + } else { + return KeyAgreement.getInstance(algorithm, cryptoProvider); + } + } + + static Mac getMac(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return Mac.getInstance(algorithm); + } else { + return Mac.getInstance(algorithm, cryptoProvider); + } + } + + static KeyFactory getKeyFactory(String algorithm) + throws NoSuchAlgorithmException { + if (cryptoProvider == null) { + return KeyFactory.getInstance(algorithm); + } else { + return KeyFactory.getInstance(algorithm, cryptoProvider); + } + } + + static SecureRandom getSecureRandom() throws KeyManagementException { + if (cryptoProvider == null) { + return new SecureRandom(); + } + // Try "PKCS11" first. If that is not supported, iterate through + // the provider and return the first working implementation. + try { + return SecureRandom.getInstance("PKCS11", cryptoProvider); + } catch (NoSuchAlgorithmException e) { + // ignore + } + for (Provider.Service s : cryptoProvider.getServices()) { + if (s.getType().equals("SecureRandom")) { + try { + return SecureRandom.getInstance(s.getAlgorithm(), cryptoProvider); + } catch (NoSuchAlgorithmException ee) { + // ignore + } + } + } + throw new KeyManagementException("FIPS mode: no SecureRandom " + + " implementation found in provider " + cryptoProvider.getName()); + } + + static MessageDigest getMD5() { + return getMessageDigest("MD5"); + } + + static MessageDigest getSHA() { + return getMessageDigest("SHA"); + } + + static MessageDigest getMessageDigest(String algorithm) { + try { + if (cryptoProvider == null) { + return MessageDigest.getInstance(algorithm); + } else { + return MessageDigest.getInstance(algorithm, cryptoProvider); + } + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException + ("Algorithm " + algorithm + " not available", e); + } + } + + static int getRSAKeyLength(PublicKey key) { + BigInteger modulus; + if (key instanceof RSAPublicKey) { + modulus = ((RSAPublicKey)key).getModulus(); + } else { + RSAPublicKeySpec spec = getRSAPublicKeySpec(key); + modulus = spec.getModulus(); + } + return modulus.bitLength(); + } + + static RSAPublicKeySpec getRSAPublicKeySpec(PublicKey key) { + if (key instanceof RSAPublicKey) { + RSAPublicKey rsaKey = (RSAPublicKey)key; + return new RSAPublicKeySpec(rsaKey.getModulus(), + rsaKey.getPublicExponent()); + } + try { + KeyFactory factory = JsseJce.getKeyFactory("RSA"); + return factory.getKeySpec(key, RSAPublicKeySpec.class); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static ECParameterSpec getECParameterSpec(String namedCurveOid) { + return ECUtil.getECParameterSpec(cryptoProvider, namedCurveOid); + } + + static String getNamedCurveOid(ECParameterSpec params) { + return ECUtil.getCurveName(cryptoProvider, params); + } + + static ECPoint decodePoint(byte[] encoded, EllipticCurve curve) + throws java.io.IOException { + return ECUtil.decodePoint(encoded, curve); + } + + static byte[] encodePoint(ECPoint point, EllipticCurve curve) { + return ECUtil.encodePoint(point, curve); + } + + // In FIPS mode, set thread local providers; otherwise a no-op. + // Must be paired with endFipsProvider. + static Object beginFipsProvider() { + if (fipsProviderList == null) { + return null; + } else { + return Providers.beginThreadProviderList(fipsProviderList); + } + } + + static void endFipsProvider(Object o) { + if (fipsProviderList != null) { + Providers.endThreadProviderList((ProviderList)o); + } + } + +} diff --git a/src/sun/security/ssl/KerberosClientKeyExchange.java b/src/sun/security/ssl/KerberosClientKeyExchange.java new file mode 100644 index 00000000..ff393d9a --- /dev/null +++ b/src/sun/security/ssl/KerberosClientKeyExchange.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.io.PrintStream; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.SecureRandom; +import javax.crypto.SecretKey; + +/** + * A helper class that calls the KerberosClientKeyExchange implementation. + */ +public class KerberosClientKeyExchange extends HandshakeMessage { + + private static final String IMPL_CLASS = + "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl"; + + private static final Class implClass = AccessController.doPrivileged( + new PrivilegedAction>() { + @Override + public Class run() { + try { + return Class.forName(IMPL_CLASS, true, null); + } catch (ClassNotFoundException cnf) { + return null; + } + } + } + ); + private final KerberosClientKeyExchange impl = createImpl(); + + private KerberosClientKeyExchange createImpl() { + if (implClass != null && + getClass() == KerberosClientKeyExchange.class) { + try { + return (KerberosClientKeyExchange)implClass.newInstance(); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + return null; + } + + // This constructor will be called when constructing an instance of its + // subclass -- KerberosClientKeyExchangeImpl. Please won't check the + // value of impl variable in this constructor. + protected KerberosClientKeyExchange() { + // please won't check the value of impl variable + } + + public KerberosClientKeyExchange(String serverName, + AccessControlContext acc, ProtocolVersion protocolVersion, + SecureRandom rand) throws IOException { + + if (impl != null) { + init(serverName, acc, protocolVersion, rand); + } else { + throw new IllegalStateException("Kerberos is unavailable"); + } + } + + public KerberosClientKeyExchange(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, SecureRandom rand, + HandshakeInStream input, AccessControlContext acc, + Object serverKeys) throws IOException { + + if (impl != null) { + init(protocolVersion, clientVersion, rand, input, acc, serverKeys); + } else { + throw new IllegalStateException("Kerberos is unavailable"); + } + } + + @Override + int messageType() { + return ht_client_key_exchange; + } + + @Override + public int messageLength() { + return impl.messageLength(); + } + + @Override + public void send(HandshakeOutStream s) throws IOException { + impl.send(s); + } + + @Override + public void print(PrintStream p) throws IOException { + impl.print(p); + } + + public void init(String serverName, + AccessControlContext acc, ProtocolVersion protocolVersion, + SecureRandom rand) throws IOException { + + if (impl != null) { + impl.init(serverName, acc, protocolVersion, rand); + } + } + + public void init(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, SecureRandom rand, + HandshakeInStream input, AccessControlContext acc, + Object ServiceCreds) throws IOException { + + if (impl != null) { + impl.init(protocolVersion, clientVersion, + rand, input, acc, ServiceCreds); + } + } + + public byte[] getUnencryptedPreMasterSecret() { + return impl.getUnencryptedPreMasterSecret(); + } + + public Principal getPeerPrincipal(){ + return impl.getPeerPrincipal(); + } + + public Principal getLocalPrincipal(){ + return impl.getLocalPrincipal(); + } +} diff --git a/src/sun/security/ssl/KeyManagerFactoryImpl.java b/src/sun/security/ssl/KeyManagerFactoryImpl.java new file mode 100644 index 00000000..2b251970 --- /dev/null +++ b/src/sun/security/ssl/KeyManagerFactoryImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.List; +import java.util.Collections; + +import java.security.*; +import java.security.KeyStore.*; + +import javax.net.ssl.*; + +abstract class KeyManagerFactoryImpl extends KeyManagerFactorySpi { + + X509ExtendedKeyManager keyManager; + boolean isInitialized; + + KeyManagerFactoryImpl() { + // empty + } + + /** + * Returns one key manager for each type of key material. + */ + @Override + protected KeyManager[] engineGetKeyManagers() { + if (!isInitialized) { + throw new IllegalStateException( + "KeyManagerFactoryImpl is not initialized"); + } + return new KeyManager[] { keyManager }; + } + + // Factory for the SunX509 keymanager + public static final class SunX509 extends KeyManagerFactoryImpl { + + @Override + protected void engineInit(KeyStore ks, char[] password) throws + KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException { + if ((ks != null) && SunJSSE.isFIPS()) { + if (ks.getProvider() != SunJSSE.cryptoProvider) { + throw new KeyStoreException("FIPS mode: KeyStore must be " + + "from provider " + SunJSSE.cryptoProvider.getName()); + } + } + keyManager = new SunX509KeyManagerImpl(ks, password); + isInitialized = true; + } + + @Override + protected void engineInit(ManagerFactoryParameters spec) throws + InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException( + "SunX509KeyManager does not use ManagerFactoryParameters"); + } + + } + + // Factory for the X509 keymanager + public static final class X509 extends KeyManagerFactoryImpl { + + @Override + protected void engineInit(KeyStore ks, char[] password) throws + KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException { + if (ks == null) { + keyManager = new X509KeyManagerImpl( + Collections.emptyList()); + } else { + if (SunJSSE.isFIPS() && (ks.getProvider() != SunJSSE.cryptoProvider)) { + throw new KeyStoreException("FIPS mode: KeyStore must be " + + "from provider " + SunJSSE.cryptoProvider.getName()); + } + try { + Builder builder = Builder.newInstance(ks, + new PasswordProtection(password)); + keyManager = new X509KeyManagerImpl(builder); + } catch (RuntimeException e) { + throw new KeyStoreException("initialization failed", e); + } + } + isInitialized = true; + } + + @Override + protected void engineInit(ManagerFactoryParameters params) throws + InvalidAlgorithmParameterException { + if (params instanceof KeyStoreBuilderParameters == false) { + throw new InvalidAlgorithmParameterException( + "Parameters must be instance of KeyStoreBuilderParameters"); + } + if (SunJSSE.isFIPS()) { + // XXX should be fixed + throw new InvalidAlgorithmParameterException + ("FIPS mode: KeyStoreBuilderParameters not supported"); + } + List builders = + ((KeyStoreBuilderParameters)params).getParameters(); + keyManager = new X509KeyManagerImpl(builders); + isInitialized = true; + } + + } + +} diff --git a/src/sun/security/ssl/Krb5Helper.java b/src/sun/security/ssl/Krb5Helper.java new file mode 100644 index 00000000..c16eae18 --- /dev/null +++ b/src/sun/security/ssl/Krb5Helper.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.Principal; +import java.security.PrivilegedAction; +import javax.crypto.SecretKey; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +/** + * A helper class for Kerberos APIs. + */ +public final class Krb5Helper { + + private Krb5Helper() { } + + // loads Krb5Proxy implementation class if available + private static final String IMPL_CLASS = + "sun.security.ssl.krb5.Krb5ProxyImpl"; + + private static final Krb5Proxy proxy = + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Krb5Proxy run() { + try { + Class c = Class.forName(IMPL_CLASS, true, null); + return (Krb5Proxy)c.newInstance(); + } catch (ClassNotFoundException cnf) { + return null; + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + }}); + + /** + * Returns true if Kerberos is available. + */ + public static boolean isAvailable() { + return proxy != null; + } + + private static void ensureAvailable() { + if (proxy == null) + throw new AssertionError("Kerberos should have been available"); + } + + /** + * Returns the Subject associated with client-side of the SSL socket. + */ + public static Subject getClientSubject(AccessControlContext acc) + throws LoginException { + ensureAvailable(); + return proxy.getClientSubject(acc); + } + + /** + * Returns the Subject associated with server-side of the SSL socket. + */ + public static Subject getServerSubject(AccessControlContext acc) + throws LoginException { + ensureAvailable(); + return proxy.getServerSubject(acc); + } + + /** + * Returns the KerberosKeys for the default server-side principal. + */ + public static Object getServiceCreds(AccessControlContext acc) + throws LoginException { + ensureAvailable(); + return proxy.getServiceCreds(acc); + } + + /** + * Returns the server-side principal name associated with the KerberosKey. + */ + public static String getServerPrincipalName(Object serviceCreds) { + ensureAvailable(); + return proxy.getServerPrincipalName(serviceCreds); + } + + /** + * Returns the hostname embedded in the principal name. + */ + public static String getPrincipalHostName(Principal principal) { + ensureAvailable(); + return proxy.getPrincipalHostName(principal); + } + + /** + * Returns a ServicePermission for the principal name and action. + */ + public static Permission getServicePermission(String principalName, + String action) { + ensureAvailable(); + return proxy.getServicePermission(principalName, action); + } + + /** + * Determines if the Subject might contain creds for princ. + */ + public static boolean isRelated(Subject subject, Principal princ) { + ensureAvailable(); + return proxy.isRelated(subject, princ); + } +} diff --git a/src/sun/security/ssl/Krb5Proxy.java b/src/sun/security/ssl/Krb5Proxy.java new file mode 100644 index 00000000..8596d86a --- /dev/null +++ b/src/sun/security/ssl/Krb5Proxy.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.AccessControlContext; +import java.security.Permission; +import java.security.Principal; +import javax.crypto.SecretKey; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; + +/** + * An interface to a subset of the Kerberos APIs to avoid a static dependency + * on the types defined by these APIs. + */ +public interface Krb5Proxy { + + /** + * Returns the Subject associated with the client-side of the SSL socket. + */ + Subject getClientSubject(AccessControlContext acc) throws LoginException; + + /** + * Returns the Subject associated with the server-side of the SSL socket. + */ + Subject getServerSubject(AccessControlContext acc) throws LoginException; + + + /** + * Returns the Kerberos ServiceCreds for the default server-side principal. + */ + Object getServiceCreds(AccessControlContext acc) throws LoginException; + + /** + * Returns the server-side principal name associated with the KerberosKey. + */ + String getServerPrincipalName(Object serviceCreds); + + /** + * Returns the hostname embedded in the principal name. + */ + String getPrincipalHostName(Principal principal); + + /** + * Returns a ServicePermission for the principal name and action. + */ + Permission getServicePermission(String principalName, String action); + + /** + * Determines if the Subject might contain creds for princ. + */ + boolean isRelated(Subject subject, Principal princ); +} diff --git a/src/sun/security/ssl/MAC.java b/src/sun/security/ssl/MAC.java new file mode 100644 index 00000000..75dba7e1 --- /dev/null +++ b/src/sun/security/ssl/MAC.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import java.nio.ByteBuffer; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; + +import sun.security.ssl.CipherSuite.MacAlg; +import static sun.security.ssl.CipherSuite.*; + +/** + * This class computes the "Message Authentication Code" (MAC) for each + * SSL stream and block cipher message. This is essentially a shared-secret + * signature, used to provide integrity protection for SSL messages. The + * MAC is actually one of several keyed hashes, as associated with the cipher + * suite and protocol version. (SSL v3.0 uses one construct, TLS uses another.) + * + * @author David Brownell + * @author Andreas Sterbenz + */ +final class MAC extends Authenticator { + + final static MAC NULL = new MAC(); + + // Value of the null MAC is fixed + private static final byte nullMAC[] = new byte[0]; + + // internal identifier for the MAC algorithm + private final MacAlg macAlg; + + // JCE Mac object + private final Mac mac; + + private MAC() { + macAlg = M_NULL; + mac = null; + } + + /** + * Set up, configured for the given SSL/TLS MAC type and version. + */ + MAC(MacAlg macAlg, ProtocolVersion protocolVersion, SecretKey key) + throws NoSuchAlgorithmException, InvalidKeyException { + super(protocolVersion); + this.macAlg = macAlg; + + String algorithm; + boolean tls = (protocolVersion.v >= ProtocolVersion.TLS10.v); + + if (macAlg == M_MD5) { + algorithm = tls ? "HmacMD5" : "SslMacMD5"; + } else if (macAlg == M_SHA) { + algorithm = tls ? "HmacSHA1" : "SslMacSHA1"; + } else if (macAlg == M_SHA256) { + algorithm = "HmacSHA256"; // TLS 1.2+ + } else if (macAlg == M_SHA384) { + algorithm = "HmacSHA384"; // TLS 1.2+ + } else { + throw new RuntimeException("Unknown Mac " + macAlg); + } + + mac = JsseJce.getMac(algorithm); + mac.init(key); + } + + /** + * Returns the length of the MAC. + */ + int MAClen() { + return macAlg.size; + } + + /** + * Returns the hash function block length of the MAC alorithm. + */ + int hashBlockLen() { + return macAlg.hashBlockSize; + } + + /** + * Returns the hash function minimal padding length of the MAC alorithm. + */ + int minimalPaddingLen() { + return macAlg.minimalPaddingSize; + } + + /** + * Computes and returns the MAC for the data in this byte array. + * + * @param type record type + * @param buf compressed record on which the MAC is computed + * @param offset start of compressed record data + * @param len the size of the compressed record + * @param isSimulated if true, simulate the the MAC computation + */ + final byte[] compute(byte type, byte buf[], + int offset, int len, boolean isSimulated) { + if (macAlg.size == 0) { + return nullMAC; + } + + if (!isSimulated) { + byte[] additional = acquireAuthenticationBytes(type, len); + mac.update(additional); + } + mac.update(buf, offset, len); + + return mac.doFinal(); + } + + /** + * Compute and returns the MAC for the remaining data + * in this ByteBuffer. + * + * On return, the bb position == limit, and limit will + * have not changed. + * + * @param type record type + * @param bb a ByteBuffer in which the position and limit + * demarcate the data to be MAC'd. + * @param isSimulated if true, simulate the the MAC computation + */ + final byte[] compute(byte type, ByteBuffer bb, boolean isSimulated) { + if (macAlg.size == 0) { + return nullMAC; + } + + if (!isSimulated) { + byte[] additional = + acquireAuthenticationBytes(type, bb.remaining()); + mac.update(additional); + } + mac.update(bb); + + return mac.doFinal(); + } + +} + diff --git a/src/sun/security/ssl/OutputRecord.java b/src/sun/security/ssl/OutputRecord.java new file mode 100644 index 00000000..5dcdde38 --- /dev/null +++ b/src/sun/security/ssl/OutputRecord.java @@ -0,0 +1,572 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.util.Arrays; + +import javax.net.ssl.SSLException; +import sun.misc.HexDumpEncoder; + + +/** + * SSL 3.0 records, as written to a TCP stream. + * + * Each record has a message area that starts out with data supplied by the + * application. It may grow/shrink due to compression and will be modified + * in place for mac-ing and encryption. + * + * Handshake records have additional needs, notably accumulation of a set + * of hashes which are used to establish that handshaking was done right. + * Handshake records usually have several handshake messages each, and we + * need message-level control over what's hashed. + * + * @author David Brownell + */ +class OutputRecord extends ByteArrayOutputStream implements Record { + + private HandshakeHash handshakeHash; + private int lastHashed; + private boolean firstMessage; + final private byte contentType; + private int headerOffset; + + // current protocol version, sent as record version + ProtocolVersion protocolVersion; + + // version for the ClientHello message. Only relevant if this is a + // client handshake record. If set to ProtocolVersion.SSL20Hello, + // the V3 client hello is converted to V2 format. + private ProtocolVersion helloVersion; + + /* Class and subclass dynamic debugging support */ + static final Debug debug = Debug.getInstance("ssl"); + + /* + * Default constructor makes a record supporting the maximum + * SSL record size. It allocates the header bytes directly. + * + * The structure of the byte buffer looks like: + * + * |---------+--------+-------+---------------------------------| + * | unused | header | IV | content, MAC/TAG, padding, etc. | + * | headerPlusMaxIVSize | + * + * unused: unused part of the buffer of size + * + * headerPlusMaxIVSize - header size - IV size + * + * When this object is created, we don't know the protocol + * version number, IV length, etc., so reserve space in front + * to avoid extra data movement (copies). + * header: the header of an SSL record + * IV: the optional IV/nonce field, it is only required for block + * (TLS 1.1 or later) and AEAD cipher suites. + * + * @param type the content type for the record + */ + OutputRecord(byte type, int size) { + super(size); + this.protocolVersion = ProtocolVersion.DEFAULT; + this.helloVersion = ProtocolVersion.DEFAULT_HELLO; + firstMessage = true; + count = headerPlusMaxIVSize; + contentType = type; + lastHashed = count; + headerOffset = headerPlusMaxIVSize - headerSize; + } + + OutputRecord(byte type) { + this(type, recordSize(type)); + } + + /** + * Get the size of the buffer we need for records of the specified + * type. + */ + private static int recordSize(byte type) { + if ((type == ct_change_cipher_spec) || (type == ct_alert)) { + return maxAlertRecordSize; + } else { + return maxRecordSize; + } + } + + /* + * Updates the SSL version of this record. + */ + synchronized void setVersion(ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + } + + /* + * Updates helloVersion of this record. + */ + synchronized void setHelloVersion(ProtocolVersion helloVersion) { + this.helloVersion = helloVersion; + } + + /* + * Reset the record so that it can be refilled, starting + * immediately after the header. + */ + @Override + public synchronized void reset() { + super.reset(); + count = headerPlusMaxIVSize; + lastHashed = count; + headerOffset = headerPlusMaxIVSize - headerSize; + } + + /* + * For handshaking, we need to be able to hash every byte above the + * record marking layer. This is where we're guaranteed to see those + * bytes, so this is where we can hash them. + */ + void setHandshakeHash(HandshakeHash handshakeHash) { + assert(contentType == ct_handshake); + this.handshakeHash = handshakeHash; + } + + /* + * We hash (the plaintext) on demand. There is one place where + * we want to access the hash in the middle of a record: client + * cert message gets hashed, and part of the same record is the + * client cert verify message which uses that hash. So we track + * how much of each record we've hashed so far. + */ + void doHashes() { + int len = count - lastHashed; + + if (len > 0) { + hashInternal(buf, lastHashed, len); + lastHashed = count; + } + } + + /* + * Need a helper function so we can hash the V2 hello correctly + */ + private void hashInternal(byte buf [], int offset, int len) { + if (debug != null && Debug.isOn("data")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println("[write] MD5 and SHA1 hashes: len = " + + len); + hd.encodeBuffer(new ByteArrayInputStream(buf, + lastHashed, len), System.out); + } catch (IOException e) { } + } + + handshakeHash.update(buf, lastHashed, len); + lastHashed = count; + } + + /* + * Return true iff the record is empty -- to avoid doing the work + * of sending empty records over the network. + */ + boolean isEmpty() { + return count == headerPlusMaxIVSize; + } + + /* + * Return true if the record is of an alert of the given description. + * + * Per SSL/TLS specifications, alert messages convey the severity of the + * message (warning or fatal) and a description of the alert. An alert + * is defined with a two bytes struct, {byte level, byte description}, + * following after the header bytes. + */ + boolean isAlert(byte description) { + if ((count > (headerPlusMaxIVSize + 1)) && (contentType == ct_alert)) { + return buf[headerPlusMaxIVSize + 1] == description; + } + + return false; + } + + /* + * Encrypt ... length may grow due to block cipher padding, or + * message authentication code or tag. + */ + void encrypt(Authenticator authenticator, CipherBox box) + throws IOException { + + // In case we are automatically flushing a handshake stream, make + // sure we have hashed the message first. + // + // when we support compression, hashing can't go here + // since it'll need to be done on the uncompressed data, + // and the MAC applies to the compressed data. + if (contentType == ct_handshake) { + doHashes(); + } + + // Requires message authentication code for stream and block + // cipher suites. + if (authenticator instanceof MAC) { + MAC signer = (MAC)authenticator; + if (signer.MAClen() != 0) { + byte[] hash = signer.compute(contentType, buf, + headerPlusMaxIVSize, count - headerPlusMaxIVSize, false); + write(hash); + } + } + + if (!box.isNullCipher()) { + // Requires explicit IV/nonce for CBC/AEAD cipher suites for + // TLS 1.1 or later. + if ((protocolVersion.v >= ProtocolVersion.TLS11.v) && + (box.isCBCMode() || box.isAEADMode())) { + byte[] nonce = box.createExplicitNonce(authenticator, + contentType, count - headerPlusMaxIVSize); + int offset = headerPlusMaxIVSize - nonce.length; + System.arraycopy(nonce, 0, buf, offset, nonce.length); + headerOffset = offset - headerSize; + } else { + headerOffset = headerPlusMaxIVSize - headerSize; + } + + // encrypt the content + int offset = headerPlusMaxIVSize; + if (!box.isAEADMode()) { + // The explicit IV can be encrypted. + offset = headerOffset + headerSize; + } // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode + + count = offset + box.encrypt(buf, offset, count - offset); + } + } + + /* + * Tell how full the buffer is ... for filling it with application or + * handshake data. + */ + final int availableDataBytes() { + int dataSize = count - headerPlusMaxIVSize; + return maxDataSize - dataSize; + } + + /* + * Increases the capacity if necessary to ensure that it can hold + * at least the number of elements specified by the minimum + * capacity argument. + * + * Note that the increased capacity is only can be used for held + * record buffer. Please DO NOT update the availableDataBytes() + * according to the expended buffer capacity. + * + * @see availableDataBytes() + */ + private void ensureCapacity(int minCapacity) { + // overflow-conscious code + if (minCapacity > buf.length) { + buf = Arrays.copyOf(buf, minCapacity); + } + } + + /* + * Return the type of SSL record that's buffered here. + */ + final byte contentType() { + return contentType; + } + + /* + * Write the record out on the stream. Note that you must have (in + * order) compressed the data, appended the MAC, and encrypted it in + * order for the record to be understood by the other end. (Some of + * those steps will be null early in handshaking.) + * + * Note that this does no locking for the connection, it's required + * that synchronization be done elsewhere. Also, this does its work + * in a single low level write, for efficiency. + */ + void write(OutputStream s, boolean holdRecord, + ByteArrayOutputStream heldRecordBuffer) throws IOException { + + /* + * Don't emit content-free records. (Even change cipher spec + * messages have a byte of data!) + */ + if (count == headerPlusMaxIVSize) { + return; + } + + int length = count - headerOffset - headerSize; + // "should" really never write more than about 14 Kb... + if (length < 0) { + throw new SSLException("output record size too small: " + + length); + } + + if (debug != null + && (Debug.isOn("record") || Debug.isOn("handshake"))) { + if ((debug != null && Debug.isOn("record")) + || contentType() == ct_change_cipher_spec) + System.out.println(Thread.currentThread().getName() + // v3.0/v3.1 ... + + ", WRITE: " + protocolVersion + + " " + InputRecord.contentName(contentType()) + + ", length = " + length); + } + + /* + * If this is the initial ClientHello on this connection and + * we're not trying to resume a (V3) session then send a V2 + * ClientHello instead so we can detect V2 servers cleanly. + */ + if (firstMessage && useV2Hello()) { + byte[] v3Msg = new byte[length - 4]; + System.arraycopy(buf, headerPlusMaxIVSize + 4, + v3Msg, 0, v3Msg.length); + headerOffset = 0; // reset the header offset + V3toV2ClientHello(v3Msg); + handshakeHash.reset(); + lastHashed = 2; + doHashes(); + if (debug != null && Debug.isOn("record")) { + System.out.println( + Thread.currentThread().getName() + + ", WRITE: SSLv2 client hello message" + + ", length = " + (count - 2)); // 2 byte SSLv2 header + } + } else { + /* + * Fill out the header, write it and the message. + */ + buf[headerOffset + 0] = contentType; + buf[headerOffset + 1] = protocolVersion.major; + buf[headerOffset + 2] = protocolVersion.minor; + buf[headerOffset + 3] = (byte)(length >> 8); + buf[headerOffset + 4] = (byte)(length); + } + firstMessage = false; + + /* + * The upper levels may want us to delay sending this packet so + * multiple TLS Records can be sent in one (or more) TCP packets. + * If so, add this packet to the heldRecordBuffer. + * + * NOTE: all writes have been synchronized by upper levels. + */ + int debugOffset = 0; + if (holdRecord) { + /* + * If holdRecord is true, we must have a heldRecordBuffer. + * + * Don't worry about the override of writeBuffer(), because + * when holdRecord is true, the implementation in this class + * will be used. + */ + writeBuffer(heldRecordBuffer, + buf, headerOffset, count - headerOffset, debugOffset); + } else { + // It's time to send, do we have buffered data? + // May or may not have a heldRecordBuffer. + if (heldRecordBuffer != null && heldRecordBuffer.size() > 0) { + int heldLen = heldRecordBuffer.size(); + + // Ensure the capacity of this buffer. + int newCount = count + heldLen - headerOffset; + ensureCapacity(newCount); + + // Slide everything in the buffer to the right. + System.arraycopy(buf, headerOffset, + buf, heldLen, count - headerOffset); + + // Prepend the held record to the buffer. + System.arraycopy( + heldRecordBuffer.toByteArray(), 0, buf, 0, heldLen); + count = newCount; + headerOffset = 0; + + // Clear the held buffer. + heldRecordBuffer.reset(); + + // The held buffer has been dumped, set the debug dump offset. + debugOffset = heldLen; + } + writeBuffer(s, buf, headerOffset, + count - headerOffset, debugOffset); + } + + reset(); + } + + /* + * Actually do the write here. For SSLEngine's HS data, + * we'll override this method and let it take the appropriate + * action. + */ + void writeBuffer(OutputStream s, byte [] buf, int off, int len, + int debugOffset) throws IOException { + s.write(buf, off, len); + s.flush(); + + // Output only the record from the specified debug offset. + if (debug != null && Debug.isOn("packet")) { + try { + HexDumpEncoder hd = new HexDumpEncoder(); + + System.out.println("[Raw write]: length = " + + (len - debugOffset)); + hd.encodeBuffer(new ByteArrayInputStream(buf, + off + debugOffset, len - debugOffset), System.out); + } catch (IOException e) { } + } + } + + /* + * Return whether the buffer contains a ClientHello message that should + * be converted to V2 format. + */ + private boolean useV2Hello() { + return firstMessage + && (helloVersion == ProtocolVersion.SSL20Hello) + && (contentType == ct_handshake) + && (buf[headerOffset + 5] == HandshakeMessage.ht_client_hello) + // 5: recode header size + && (buf[headerPlusMaxIVSize + 4 + 2 + 32] == 0); + // V3 session ID is empty + // 4: handshake header size + // 2: client_version in ClientHello + // 32: random in ClientHello + } + + /* + * Detect "old" servers which are capable of SSL V2.0 protocol ... for + * example, Netscape Commerce 1.0 servers. The V3 message is in the + * header and the bytes passed as parameter. This routine translates + * the V3 message into an equivalent V2 one. + * + * Note that the translation will strip off all hello extensions as + * SSL V2.0 does not support hello extension. + */ + private void V3toV2ClientHello(byte v3Msg []) throws SSLException { + int v3SessionIdLenOffset = 2 + 32; // version + nonce + int v3SessionIdLen = v3Msg[v3SessionIdLenOffset]; + int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen; + int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) + + (v3Msg[v3CipherSpecLenOffset + 1] & 0xff); + int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3 + + /* + * Copy over the cipher specs. We don't care about actually translating + * them for use with an actual V2 server since we only talk V3. + * Therefore, just copy over the V3 cipher spec values with a leading + * 0. + */ + int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length + int v2CipherSpecLen = 0; + count = 11; + boolean containsRenegoInfoSCSV = false; + for (int i = 0; i < cipherSpecs; i++) { + byte byte1, byte2; + + byte1 = v3Msg[v3CipherSpecOffset++]; + byte2 = v3Msg[v3CipherSpecOffset++]; + v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2); + if (!containsRenegoInfoSCSV && + byte1 == (byte)0x00 && byte2 == (byte)0xFF) { + containsRenegoInfoSCSV = true; + } + } + + if (!containsRenegoInfoSCSV) { + v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF); + } + + /* + * Build the first part of the V3 record header from the V2 one + * that's now buffered up. (Lengths are fixed up later). + */ + buf[2] = HandshakeMessage.ht_client_hello; + buf[3] = v3Msg[0]; // major version + buf[4] = v3Msg[1]; // minor version + buf[5] = (byte)(v2CipherSpecLen >>> 8); + buf[6] = (byte)v2CipherSpecLen; + buf[7] = 0; + buf[8] = 0; // always no session + buf[9] = 0; + buf[10] = 32; // nonce length (always 32 in V3) + + /* + * Copy in the nonce. + */ + System.arraycopy(v3Msg, 2, buf, count, 32); + count += 32; + + /* + * Set the length of the message. + */ + count -= 2; // don't include length field itself + buf[0] = (byte)(count >>> 8); + buf[0] |= 0x80; + buf[1] = (byte)(count); + count += 2; + } + + /* + * Mappings from V3 cipher suite encodings to their pure V2 equivalents. + * This is taken from the SSL V3 specification, Appendix E. + */ + private static int[] V3toV2CipherMap1 = + {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07}; + private static int[] V3toV2CipherMap3 = + {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0}; + + /* + * See which matching pure-V2 cipher specs we need to include. + * We are including these not because we are actually prepared + * to talk V2 but because the Oracle Web Server insists on receiving + * at least 1 "pure V2" cipher suite that it supports and returns an + * illegal_parameter alert unless one is present. Rather than mindlessly + * claiming to implement all documented pure V2 cipher suites the code below + * just claims to implement the V2 cipher suite that is "equivalent" + * in terms of cipher algorithm & exportability with the actual V3 cipher + * suite that we do support. + */ + private int V3toV2CipherSuite(byte byte1, byte byte2) { + buf[count++] = 0; + buf[count++] = byte1; + buf[count++] = byte2; + + if (((byte2 & 0xff) > 0xA) || + (V3toV2CipherMap1[byte2] == -1)) { + return 3; + } + + buf[count++] = (byte)V3toV2CipherMap1[byte2]; + buf[count++] = 0; + buf[count++] = (byte)V3toV2CipherMap3[byte2]; + + return 6; + } +} diff --git a/src/sun/security/ssl/ProtocolList.java b/src/sun/security/ssl/ProtocolList.java new file mode 100644 index 00000000..57508070 --- /dev/null +++ b/src/sun/security/ssl/ProtocolList.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.*; + +/** + * A list of ProtocolVersions. Also maintains the list of supported protocols. + * Instances of this class are immutable. Some member variables are final + * and can be accessed directly without method accessors. + * + * @author Andreas Sterbenz + * @since 1.4.1 + */ +final class ProtocolList { + + // the sorted protocol version list + private final ArrayList protocols; + + private String[] protocolNames; + + // the minimum and maximum ProtocolVersions in this list + final ProtocolVersion min, max; + + // the format for the hello version to use + final ProtocolVersion helloVersion; + + ProtocolList(String[] names) { + this(convert(names)); + } + + ProtocolList(ArrayList versions) { + this.protocols = versions; + + if ((protocols.size() == 1) && + protocols.contains(ProtocolVersion.SSL20Hello)) { + throw new IllegalArgumentException("SSLv2Hello cannot be " + + "enabled unless at least one other supported version " + + "is also enabled."); + } + + if (protocols.size() != 0) { + Collections.sort(protocols); + min = protocols.get(0); + max = protocols.get(protocols.size() - 1); + helloVersion = protocols.get(0); + } else { + min = ProtocolVersion.NONE; + max = ProtocolVersion.NONE; + helloVersion = ProtocolVersion.NONE; + } + } + + private static ArrayList convert(String[] names) { + if (names == null) { + throw new IllegalArgumentException("Protocols may not be null"); + } + + ArrayList versions = new ArrayList<>(names.length); + for (int i = 0; i < names.length; i++ ) { + ProtocolVersion version = ProtocolVersion.valueOf(names[i]); + if (versions.contains(version) == false) { + versions.add(version); + } + } + + return versions; + } + + /** + * Return whether this list contains the specified protocol version. + * SSLv2Hello is not a real protocol version we support, we always + * return false for it. + */ + boolean contains(ProtocolVersion protocolVersion) { + if (protocolVersion == ProtocolVersion.SSL20Hello) { + return false; + } + return protocols.contains(protocolVersion); + } + + /** + * Return a reference to the internal Collection of CipherSuites. + * The Collection MUST NOT be modified. + */ + Collection collection() { + return protocols; + } + + /** + * Select a protocol version from the list. + * + * Return the lower of the protocol version of that suggested by + * the protocolVersion and the highest version of this + * protocol list, or null if no protocol version is available. + * + * The method is used by TLS server to negotiated the protocol + * version between client suggested protocol version in the + * client hello and protocol versions supported by the server. + */ + ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) { + ProtocolVersion selectedVersion = null; + for (ProtocolVersion pv : protocols) { + if (pv.v > protocolVersion.v) { + break; // Safe to break here as this.protocols is sorted + } + selectedVersion = pv; + } + + return selectedVersion; + } + + /** + * Return an array with the names of the ProtocolVersions in this list. + */ + synchronized String[] toStringArray() { + if (protocolNames == null) { + protocolNames = new String[protocols.size()]; + int i = 0; + for (ProtocolVersion version : protocols) { + protocolNames[i++] = version.name; + } + } + return protocolNames.clone(); + } + + @Override + public String toString() { + return protocols.toString(); + } +} diff --git a/src/sun/security/ssl/ProtocolVersion.java b/src/sun/security/ssl/ProtocolVersion.java new file mode 100644 index 00000000..879d0f00 --- /dev/null +++ b/src/sun/security/ssl/ProtocolVersion.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.*; +import java.security.CryptoPrimitive; + +/** + * Type safe enum for an SSL/TLS protocol version. Instances are obtained + * using the static factory methods or by referencing the static members + * in this class. Member variables are final and can be accessed without + * accessor methods. + * + * There is only ever one instance per supported protocol version, this + * means == can be used for comparision instead of equals() if desired. + * + * Checks for a particular version number should generally take this form: + * + * if (protocolVersion.v >= ProtocolVersion.TLS10) { + * // TLS 1.0 code goes here + * } else { + * // SSL 3.0 code here + * } + * + * @author Andreas Sterbenz + * @since 1.4.1 + */ +public final class ProtocolVersion implements Comparable { + + // The limit of maximum protocol version + final static int LIMIT_MAX_VALUE = 0xFFFF; + + // The limit of minimum protocol version + final static int LIMIT_MIN_VALUE = 0x0000; + + // Dummy protocol version value for invalid SSLSession + final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE"); + + // If enabled, send/ accept SSLv2 hello messages + final static ProtocolVersion SSL20Hello = new ProtocolVersion(0x0002, + "SSLv2Hello"); + + // SSL 3.0 + final static ProtocolVersion SSL30 = new ProtocolVersion(0x0300, "SSLv3"); + + // TLS 1.0 + final static ProtocolVersion TLS10 = new ProtocolVersion(0x0301, "TLSv1"); + + // TLS 1.1 + final static ProtocolVersion TLS11 = new ProtocolVersion(0x0302, "TLSv1.1"); + + // TLS 1.2 + final static ProtocolVersion TLS12 = new ProtocolVersion(0x0303, "TLSv1.2"); + + private static final boolean FIPS = SunJSSE.isFIPS(); + + // minimum version we implement (SSL 3.0) + final static ProtocolVersion MIN = FIPS ? TLS10 : SSL30; + + // maximum version we implement (TLS 1.2) + final static ProtocolVersion MAX = TLS12; + + // ProtocolVersion to use by default (TLS 1.2) + final static ProtocolVersion DEFAULT = TLS12; + + // Default version for hello messages (SSLv2Hello) + final static ProtocolVersion DEFAULT_HELLO = FIPS ? TLS10 : SSL30; + + // Available protocols + // + // Including all supported protocols except the disabled ones. + final static Set availableProtocols; + + // version in 16 bit MSB format as it appears in records and + // messages, i.e. 0x0301 for TLS 1.0 + public final int v; + + // major and minor version + public final byte major, minor; + + // name used in JSSE (e.g. TLSv1 for TLS 1.0) + final String name; + + // Initialize the available protocols. + static { + Set protocols = new HashSet<>(5); + + ProtocolVersion[] pvs = new ProtocolVersion[] { + SSL20Hello, SSL30, TLS10, TLS11, TLS12}; + for (ProtocolVersion p : pvs) { + if (SSLAlgorithmConstraints.DEFAULT_SSL_ONLY.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + p.name, null)) { + protocols.add(p); + } + } + + availableProtocols = + Collections.unmodifiableSet(protocols); + } + + // private + private ProtocolVersion(int v, String name) { + this.v = v; + this.name = name; + major = (byte)(v >>> 8); + minor = (byte)(v & 0xFF); + } + + // private + private static ProtocolVersion valueOf(int v) { + if (v == SSL30.v) { + return SSL30; + } else if (v == TLS10.v) { + return TLS10; + } else if (v == TLS11.v) { + return TLS11; + } else if (v == TLS12.v) { + return TLS12; + } else if (v == SSL20Hello.v) { + return SSL20Hello; + } else { + int major = (v >>> 8) & 0xFF; + int minor = v & 0xFF; + return new ProtocolVersion(v, "Unknown-" + major + "." + minor); + } + } + + /** + * Return a ProtocolVersion with the specified major and minor version + * numbers. Never throws exceptions. + */ + public static ProtocolVersion valueOf(int major, int minor) { + return valueOf(((major & 0xFF) << 8) | (minor & 0xFF)); + } + + /** + * Return a ProtocolVersion for the given name. + * + * @exception IllegalArgumentException if name is null or does not + * identify a supported protocol + */ + static ProtocolVersion valueOf(String name) { + if (name == null) { + throw new IllegalArgumentException("Protocol cannot be null"); + } + + if (FIPS && (name.equals(SSL30.name) || name.equals(SSL20Hello.name))) { + throw new IllegalArgumentException + ("Only TLS 1.0 or later allowed in FIPS mode"); + } + + if (name.equals(SSL30.name)) { + return SSL30; + } else if (name.equals(TLS10.name)) { + return TLS10; + } else if (name.equals(TLS11.name)) { + return TLS11; + } else if (name.equals(TLS12.name)) { + return TLS12; + } else if (name.equals(SSL20Hello.name)) { + return SSL20Hello; + } else { + throw new IllegalArgumentException(name); + } + } + + @Override + public String toString() { + return name; + } + + /** + * Compares this object with the specified object for order. + */ + @Override + public int compareTo(ProtocolVersion protocolVersion) { + return this.v - protocolVersion.v; + } +} diff --git a/src/sun/security/ssl/RSAClientKeyExchange.java b/src/sun/security/ssl/RSAClientKeyExchange.java new file mode 100644 index 00000000..046dec85 --- /dev/null +++ b/src/sun/security/ssl/RSAClientKeyExchange.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.security.*; + +import javax.crypto.*; + +import javax.net.ssl.*; + +import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; + +/** + * This is the client key exchange message (CLIENT --> SERVER) used with + * all RSA key exchanges; it holds the RSA-encrypted pre-master secret. + * + * The message is encrypted using PKCS #1 block type 02 encryption with the + * server's public key. The padding and resulting message size is a function + * of this server's public key modulus size, but the pre-master secret is + * always exactly 48 bytes. + * + */ +final class RSAClientKeyExchange extends HandshakeMessage { + + /* + * The following field values were encrypted with the server's public + * key (or temp key from server key exchange msg) and are presented + * here in DECRYPTED form. + */ + private ProtocolVersion protocolVersion; // preMaster [0,1] + SecretKey preMaster; + private byte[] encrypted; // same size as public modulus + + /* + * Client randomly creates a pre-master secret and encrypts it + * using the server's RSA public key; only the server can decrypt + * it, using its RSA private key. Result is the same size as the + * server's public key, and uses PKCS #1 block format 02. + */ + RSAClientKeyExchange(ProtocolVersion protocolVersion, + ProtocolVersion maxVersion, + SecureRandom generator, PublicKey publicKey) throws IOException { + if (publicKey.getAlgorithm().equals("RSA") == false) { + throw new SSLKeyException("Public key not of type RSA"); + } + this.protocolVersion = protocolVersion; + + try { + String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ? + "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"); + KeyGenerator kg = JsseJce.getKeyGenerator(s); + kg.init(new TlsRsaPremasterSecretParameterSpec( + maxVersion.v, protocolVersion.v), generator); + preMaster = kg.generateKey(); + + Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1); + cipher.init(Cipher.WRAP_MODE, publicKey, generator); + encrypted = cipher.wrap(preMaster); + } catch (GeneralSecurityException e) { + throw (SSLKeyException)new SSLKeyException + ("RSA premaster secret error").initCause(e); + } + } + + /* + * Server gets the PKCS #1 (block format 02) data, decrypts + * it with its private key. + */ + RSAClientKeyExchange(ProtocolVersion currentVersion, + ProtocolVersion maxVersion, + SecureRandom generator, HandshakeInStream input, + int messageSize, PrivateKey privateKey) throws IOException { + + if (privateKey.getAlgorithm().equals("RSA") == false) { + throw new SSLKeyException("Private key not of type RSA"); + } + + if (currentVersion.v >= ProtocolVersion.TLS10.v) { + encrypted = input.getBytes16(); + } else { + encrypted = new byte [messageSize]; + if (input.read(encrypted) != messageSize) { + throw new SSLProtocolException( + "SSL: read PreMasterSecret: short read"); + } + } + + try { + Cipher cipher = JsseJce.getCipher(JsseJce.CIPHER_RSA_PKCS1); + cipher.init(Cipher.UNWRAP_MODE, privateKey, + new TlsRsaPremasterSecretParameterSpec( + maxVersion.v, currentVersion.v), + generator); + preMaster = (SecretKey)cipher.unwrap(encrypted, + "TlsRsaPremasterSecret", Cipher.SECRET_KEY); + } catch (InvalidKeyException ibk) { + // the message is too big to process with RSA + throw new SSLProtocolException( + "Unable to process PreMasterSecret, may be too big"); + } catch (Exception e) { + // unlikely to happen, otherwise, must be a provider exception + if (debug != null && Debug.isOn("handshake")) { + System.out.println("RSA premaster secret decryption error:"); + e.printStackTrace(System.out); + } + throw new RuntimeException("Could not generate dummy secret", e); + } + } + + @Override + int messageType() { + return ht_client_key_exchange; + } + + @Override + int messageLength() { + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + return encrypted.length + 2; + } else { + return encrypted.length; + } + } + + @Override + void send(HandshakeOutStream s) throws IOException { + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { + s.putBytes16(encrypted); + } else { + s.write(encrypted); + } + } + + @Override + void print(PrintStream s) throws IOException { + s.println("*** ClientKeyExchange, RSA PreMasterSecret, " + + protocolVersion); + } +} diff --git a/src/sun/security/ssl/RSASignature.java b/src/sun/security/ssl/RSASignature.java new file mode 100644 index 00000000..b1824310 --- /dev/null +++ b/src/sun/security/ssl/RSASignature.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; + +/** + * Signature implementation for the SSL/TLS RSA Signature variant with both + * MD5 and SHA-1 MessageDigests. Used for explicit RSA server authentication + * (RSA signed server key exchange for RSA_EXPORT and DHE_RSA) and RSA client + * authentication (RSA signed certificate verify message). + * + * It conforms to the standard JCA Signature API. It is registered in the + * SunJSSE provider to avoid more complicated getInstance() code and + * negative interaction with the JCA mechanisms for hardware providers. + * + * The class should be instantiated via the getInstance() method in this class, + * which returns the implementation from the preferred provider. The internal + * implementation allows the hashes to be explicitly set, which is required + * for RSA client authentication. It can be obtained via the + * getInternalInstance() method. + * + * This class is not thread safe. + * + */ +public final class RSASignature extends SignatureSpi { + + private final Signature rawRsa; + private MessageDigest md5, sha; + + // flag indicating if the MessageDigests are in reset state + private boolean isReset; + + public RSASignature() throws NoSuchAlgorithmException { + super(); + rawRsa = JsseJce.getSignature(JsseJce.SIGNATURE_RAWRSA); + isReset = true; + } + + /** + * Get an implementation for the RSA signature. Follows the standard + * JCA getInstance() model, so it return the implementation from the + * provider with the highest precedence, which may be this class. + */ + static Signature getInstance() throws NoSuchAlgorithmException { + return JsseJce.getSignature(JsseJce.SIGNATURE_SSLRSA); + } + + /** + * Get an internal implementation for the RSA signature. Used for RSA + * client authentication, which needs the ability to set the digests + * to externally provided values via the setHashes() method. + */ + static Signature getInternalInstance() + throws NoSuchAlgorithmException, NoSuchProviderException { + return Signature.getInstance(JsseJce.SIGNATURE_SSLRSA, "SunJSSE"); + } + + /** + * Set the MD5 and SHA hashes to the provided objects. + */ + static void setHashes(Signature sig, MessageDigest md5, MessageDigest sha) { + sig.setParameter("hashes", new MessageDigest[] {md5, sha}); + } + + /** + * Reset the MessageDigests unless they are already reset. + */ + private void reset() { + if (isReset == false) { + md5.reset(); + sha.reset(); + isReset = true; + } + } + + private static void checkNull(Key key) throws InvalidKeyException { + if (key == null) { + throw new InvalidKeyException("Key must not be null"); + } + } + + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + checkNull(publicKey); + reset(); + rawRsa.initVerify(publicKey); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + checkNull(privateKey); + reset(); + rawRsa.initSign(privateKey, random); + } + + // lazily initialize the MessageDigests + private void initDigests() { + if (md5 == null) { + md5 = JsseJce.getMD5(); + sha = JsseJce.getSHA(); + } + } + + @Override + protected void engineUpdate(byte b) { + initDigests(); + isReset = false; + md5.update(b); + sha.update(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) { + initDigests(); + isReset = false; + md5.update(b, off, len); + sha.update(b, off, len); + } + + private byte[] getDigest() throws SignatureException { + try { + initDigests(); + byte[] data = new byte[36]; + md5.digest(data, 0, 16); + sha.digest(data, 16, 20); + isReset = true; + return data; + } catch (DigestException e) { + // should never occur + throw new SignatureException(e); + } + } + + @Override + protected byte[] engineSign() throws SignatureException { + rawRsa.update(getDigest()); + return rawRsa.sign(); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + return engineVerify(sigBytes, 0, sigBytes.length); + } + + @Override + protected boolean engineVerify(byte[] sigBytes, int offset, int length) + throws SignatureException { + rawRsa.update(getDigest()); + return rawRsa.verify(sigBytes, offset, length); + } + + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + if (param.equals("hashes") == false) { + throw new InvalidParameterException + ("Parameter not supported: " + param); + } + if (value instanceof MessageDigest[] == false) { + throw new InvalidParameterException + ("value must be MessageDigest[]"); + } + MessageDigest[] digests = (MessageDigest[])value; + md5 = digests[0]; + sha = digests[1]; + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("No parameters accepted"); + } + } + + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new InvalidParameterException("Parameters not supported"); + } + + + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } +} diff --git a/src/sun/security/ssl/RandomCookie.java b/src/sun/security/ssl/RandomCookie.java new file mode 100644 index 00000000..5f414c40 --- /dev/null +++ b/src/sun/security/ssl/RandomCookie.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.security.SecureRandom; + +/* + * RandomCookie ... SSL hands standard format random cookies (nonces) + * around. These know how to encode/decode themselves on SSL streams, + * and can be created and printed. + * + * @author David Brownell + */ +final class RandomCookie { + + byte random_bytes[]; // exactly 32 bytes + + RandomCookie(SecureRandom generator) { + long temp = System.currentTimeMillis() / 1000; + int gmt_unix_time; + if (temp < Integer.MAX_VALUE) { + gmt_unix_time = (int) temp; + } else { + gmt_unix_time = Integer.MAX_VALUE; // Whoops! + } + + random_bytes = new byte[32]; + generator.nextBytes(random_bytes); + + random_bytes[0] = (byte)(gmt_unix_time >> 24); + random_bytes[1] = (byte)(gmt_unix_time >> 16); + random_bytes[2] = (byte)(gmt_unix_time >> 8); + random_bytes[3] = (byte)gmt_unix_time; + } + + RandomCookie(HandshakeInStream m) throws IOException { + random_bytes = new byte[32]; + m.read(random_bytes, 0, 32); + } + + void send(HandshakeOutStream out) throws IOException { + out.write(random_bytes, 0, 32); + } + + void print(PrintStream s) { + int i, gmt_unix_time; + + gmt_unix_time = random_bytes[0] << 24; + gmt_unix_time += random_bytes[1] << 16; + gmt_unix_time += random_bytes[2] << 8; + gmt_unix_time += random_bytes[3]; + + s.print("GMT: " + gmt_unix_time + " "); + s.print("bytes = { "); + + for (i = 4; i < 32; i++) { + if (i != 4) { + s.print(", "); + } + s.print(random_bytes[i] & 0x0ff); + } + s.println(" }"); + } +} diff --git a/src/sun/security/ssl/Record.java b/src/sun/security/ssl/Record.java new file mode 100644 index 00000000..c3569f6f --- /dev/null +++ b/src/sun/security/ssl/Record.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + + +/** + * SSL/TLS records, as pulled off (and put onto) a TCP stream. This is + * the base interface, which defines common information and interfaces + * used by both Input and Output records. + * + * @author David Brownell + */ +interface Record { + /* + * There are four SSL record types, which are part of the interface + * to this level (along with the maximum record size) + * + * enum { change_cipher_spec(20), alert(21), handshake(22), + * application_data(23), (255) } ContentType; + */ + static final byte ct_change_cipher_spec = 20; + static final byte ct_alert = 21; + static final byte ct_handshake = 22; + static final byte ct_application_data = 23; + + static final int headerSize = 5; // SSLv3 record header + static final int maxExpansion = 1024; // for bad compression + static final int trailerSize = 20; // SHA1 hash size + static final int maxDataSize = 16384; // 2^14 bytes of data + static final int maxPadding = 256; // block cipher padding + static final int maxIVLength = 256; // IV length + + /* + * The size of the header plus the max IV length + */ + static final int headerPlusMaxIVSize = + headerSize // header + + maxIVLength; // iv + + /* + * SSL has a maximum record size. It's header, (compressed) data, + * padding, and a trailer for the message authentication information (MAC + * for block and stream ciphers, and message authentication tag for AEAD + * ciphers). + * + * Some compression algorithms have rare cases where they expand the data. + * As we don't support compression at this time, leave that out. + */ + static final int maxRecordSize = + headerPlusMaxIVSize // header + iv + + maxDataSize // data + + maxPadding // padding + + trailerSize; // MAC or AEAD tag + + static final boolean enableCBCProtection = + Debug.getBooleanProperty("jsse.enableCBCProtection", true); + + /* + * For CBC protection in SSL3/TLS1, we break some plaintext into two + * packets. Max application data size for the second packet. + */ + static final int maxDataSizeMinusOneByteRecord = + maxDataSize // max data size + - ( // max one byte record size + headerPlusMaxIVSize // header + iv + + 1 // one byte data + + maxPadding // padding + + trailerSize // MAC + ); + + /* + * The maximum large record size. + * + * Some SSL/TLS implementations support large fragment upto 2^15 bytes, + * such as Microsoft. We support large incoming fragments. + * + * The maximum large record size is defined as maxRecordSize plus 2^14, + * this is the amount OpenSSL is using. + */ + static final int maxLargeRecordSize = + maxRecordSize // Max size with a conforming implementation + + maxDataSize; // extra 2^14 bytes for large data packets. + + + /* + * Maximum record size for alert and change cipher spec records. + * They only contain 2 and 1 bytes of data, respectively. + * Allocate a smaller array. + */ + static final int maxAlertRecordSize = + headerPlusMaxIVSize // header + iv + + 2 // alert + + maxPadding // padding + + trailerSize; // MAC + + /* + * The overflow values of integers of 8, 16 and 24 bits. + */ + static final int OVERFLOW_OF_INT08 = (1 << 8); + static final int OVERFLOW_OF_INT16 = (1 << 16); + static final int OVERFLOW_OF_INT24 = (1 << 24); +} diff --git a/src/sun/security/ssl/RenegotiationInfoExtension.java b/src/sun/security/ssl/RenegotiationInfoExtension.java new file mode 100644 index 00000000..1233660d --- /dev/null +++ b/src/sun/security/ssl/RenegotiationInfoExtension.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +import javax.net.ssl.SSLProtocolException; + +/* + * For secure renegotiation, RFC5746 defines a new TLS extension, + * "renegotiation_info" (with extension type 0xff01), which contains a + * cryptographic binding to the enclosing TLS connection (if any) for + * which the renegotiation is being performed. The "extension data" + * field of this extension contains a "RenegotiationInfo" structure: + * + * struct { + * opaque renegotiated_connection<0..255>; + * } RenegotiationInfo; + */ +final class RenegotiationInfoExtension extends HelloExtension { + private final byte[] renegotiated_connection; + + RenegotiationInfoExtension(byte[] clientVerifyData, + byte[] serverVerifyData) { + super(ExtensionType.EXT_RENEGOTIATION_INFO); + + if (clientVerifyData.length != 0) { + renegotiated_connection = + new byte[clientVerifyData.length + serverVerifyData.length]; + System.arraycopy(clientVerifyData, 0, renegotiated_connection, + 0, clientVerifyData.length); + + if (serverVerifyData.length != 0) { + System.arraycopy(serverVerifyData, 0, renegotiated_connection, + clientVerifyData.length, serverVerifyData.length); + } + } else { + // ignore both the client and server verify data. + renegotiated_connection = new byte[0]; + } + } + + RenegotiationInfoExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_RENEGOTIATION_INFO); + + // check the extension length + if (len < 1) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + int renegoInfoDataLen = s.getInt8(); + if (renegoInfoDataLen + 1 != len) { // + 1 = the byte we just read + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + renegotiated_connection = new byte[renegoInfoDataLen]; + if (renegoInfoDataLen != 0) { + s.read(renegotiated_connection, 0, renegoInfoDataLen); + } + } + + + // Length of the encoded extension, including the type and length fields + @Override + int length() { + return 5 + renegotiated_connection.length; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(renegotiated_connection.length + 1); + s.putBytes8(renegotiated_connection); + } + + boolean isEmpty() { + return renegotiated_connection.length == 0; + } + + byte[] getRenegotiatedConnection() { + return renegotiated_connection; + } + + @Override + public String toString() { + return "Extension " + type + ", renegotiated_connection: " + + (renegotiated_connection.length == 0 ? "" : + Debug.toString(renegotiated_connection)); + } + +} diff --git a/src/sun/security/ssl/SSLAlgorithmConstraints.java b/src/sun/security/ssl/SSLAlgorithmConstraints.java new file mode 100644 index 00000000..1a8a973e --- /dev/null +++ b/src/sun/security/ssl/SSLAlgorithmConstraints.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.AlgorithmParameters; + +import javax.net.ssl.*; + +import java.security.Key; + +import java.util.Set; +import java.util.HashSet; + +import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.ssl.CipherSuite.*; + +/** + * Algorithm constraints for disabled algorithms property + * + * See the "jdk.certpath.disabledAlgorithms" specification in java.security + * for the syntax of the disabled algorithm string. + */ +final class SSLAlgorithmConstraints implements AlgorithmConstraints { + private final static AlgorithmConstraints tlsDisabledAlgConstraints = + new TLSDisabledAlgConstraints(); + private final static AlgorithmConstraints x509DisabledAlgConstraints = + new X509DisabledAlgConstraints(); + private AlgorithmConstraints userAlgConstraints = null; + private AlgorithmConstraints peerAlgConstraints = null; + + private boolean enabledX509DisabledAlgConstraints = true; + + // the default algorithm constraints + final static AlgorithmConstraints DEFAULT = + new SSLAlgorithmConstraints(null); + + // the default SSL only algorithm constraints + final static AlgorithmConstraints DEFAULT_SSL_ONLY = + new SSLAlgorithmConstraints((SSLSocket)null, false); + + SSLAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { + userAlgConstraints = algorithmConstraints; + } + + SSLAlgorithmConstraints(SSLSocket socket, + boolean withDefaultCertPathConstraints) { + if (socket != null) { + userAlgConstraints = + socket.getSSLParameters().getAlgorithmConstraints(); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLEngine engine, + boolean withDefaultCertPathConstraints) { + if (engine != null) { + userAlgConstraints = + engine.getSSLParameters().getAlgorithmConstraints(); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms, + boolean withDefaultCertPathConstraints) { + if (socket != null) { + userAlgConstraints = + socket.getSSLParameters().getAlgorithmConstraints(); + peerAlgConstraints = + new SupportedSignatureAlgorithmConstraints(supportedAlgorithms); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms, + boolean withDefaultCertPathConstraints) { + if (engine != null) { + userAlgConstraints = + engine.getSSLParameters().getAlgorithmConstraints(); + peerAlgConstraints = + new SupportedSignatureAlgorithmConstraints(supportedAlgorithms); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + @Override + public boolean permits(Set primitives, + String algorithm, AlgorithmParameters parameters) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits( + primitives, algorithm, parameters); + } + + return permitted; + } + + @Override + public boolean permits(Set primitives, Key key) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits(primitives, key); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits(primitives, key); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits(primitives, key); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits(primitives, key); + } + + return permitted; + } + + @Override + public boolean permits(Set primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + return permitted; + } + + + static private class SupportedSignatureAlgorithmConstraints + implements AlgorithmConstraints { + // supported signature algorithms + private String[] supportedAlgorithms; + + SupportedSignatureAlgorithmConstraints(String[] supportedAlgorithms) { + if (supportedAlgorithms != null) { + this.supportedAlgorithms = supportedAlgorithms.clone(); + } else { + this.supportedAlgorithms = null; + } + } + + @Override + public boolean permits(Set primitives, + String algorithm, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException( + "No algorithm name specified"); + } + + if (primitives == null || primitives.isEmpty()) { + throw new IllegalArgumentException( + "No cryptographic primitive specified"); + } + + if (supportedAlgorithms == null || + supportedAlgorithms.length == 0) { + return false; + } + + // trim the MGF part: withand + int position = algorithm.indexOf("and"); + if (position > 0) { + algorithm = algorithm.substring(0, position); + } + + for (String supportedAlgorithm : supportedAlgorithms) { + if (algorithm.equalsIgnoreCase(supportedAlgorithm)) { + return true; + } + } + + return false; + } + + @Override + final public boolean permits(Set primitives, Key key) { + return true; + } + + @Override + final public boolean permits(Set primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException( + "No algorithm name specified"); + } + + return permits(primitives, algorithm, parameters); + } + } + + static private class BasicDisabledAlgConstraints + extends DisabledAlgorithmConstraints { + BasicDisabledAlgConstraints(String propertyName) { + super(propertyName); + } + + protected Set decomposes(KeyExchange keyExchange, + boolean forCertPathOnly) { + Set components = new HashSet<>(); + switch (keyExchange) { + case K_NULL: + if (!forCertPathOnly) { + components.add("NULL"); + } + break; + case K_RSA: + components.add("RSA"); + break; + case K_RSA_EXPORT: + components.add("RSA"); + components.add("RSA_EXPORT"); + break; + case K_DH_RSA: + components.add("RSA"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_RSA"); + break; + case K_DH_DSS: + components.add("DSA"); + components.add("DSS"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_DSS"); + break; + case K_DHE_DSS: + components.add("DSA"); + components.add("DSS"); + components.add("DH"); + components.add("DHE"); + components.add("DiffieHellman"); + components.add("DHE_DSS"); + break; + case K_DHE_RSA: + components.add("RSA"); + components.add("DH"); + components.add("DHE"); + components.add("DiffieHellman"); + components.add("DHE_RSA"); + break; + case K_DH_ANON: + if (!forCertPathOnly) { + components.add("ANON"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_ANON"); + } + break; + case K_ECDH_ECDSA: + components.add("ECDH"); + components.add("ECDSA"); + components.add("ECDH_ECDSA"); + break; + case K_ECDH_RSA: + components.add("ECDH"); + components.add("RSA"); + components.add("ECDH_RSA"); + break; + case K_ECDHE_ECDSA: + components.add("ECDHE"); + components.add("ECDSA"); + components.add("ECDHE_ECDSA"); + break; + case K_ECDHE_RSA: + components.add("ECDHE"); + components.add("RSA"); + components.add("ECDHE_RSA"); + break; + case K_ECDH_ANON: + if (!forCertPathOnly) { + components.add("ECDH"); + components.add("ANON"); + components.add("ECDH_ANON"); + } + break; + case K_KRB5: + if (!forCertPathOnly) { + components.add("KRB5"); + } + break; + case K_KRB5_EXPORT: + if (!forCertPathOnly) { + components.add("KRB5_EXPORT"); + } + break; + default: + // ignore + } + + return components; + } + + protected Set decomposes(BulkCipher bulkCipher) { + Set components = new HashSet<>(); + + if (bulkCipher.transformation != null) { + components.addAll(super.decomposes(bulkCipher.transformation)); + } + + return components; + } + + protected Set decomposes(MacAlg macAlg) { + Set components = new HashSet<>(); + + if (macAlg == CipherSuite.M_MD5) { + components.add("MD5"); + components.add("HmacMD5"); + } else if (macAlg == CipherSuite.M_SHA) { + components.add("SHA1"); + components.add("SHA-1"); + components.add("HmacSHA1"); + } else if (macAlg == CipherSuite.M_SHA256) { + components.add("SHA256"); + components.add("SHA-256"); + components.add("HmacSHA256"); + } else if (macAlg == CipherSuite.M_SHA384) { + components.add("SHA384"); + components.add("SHA-384"); + components.add("HmacSHA384"); + } + + return components; + } + } + + static private class TLSDisabledAlgConstraints + extends BasicDisabledAlgConstraints { + + TLSDisabledAlgConstraints() { + super(DisabledAlgorithmConstraints.PROPERTY_TLS_DISABLED_ALGS); + } + + @Override + protected Set decomposes(String algorithm) { + if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) { + CipherSuite cipherSuite = null; + try { + cipherSuite = CipherSuite.valueOf(algorithm); + } catch (IllegalArgumentException iae) { + // ignore: unknown or unsupported ciphersuite + } + + if (cipherSuite != null) { + Set components = new HashSet<>(); + + if(cipherSuite.keyExchange != null) { + components.addAll( + decomposes(cipherSuite.keyExchange, false)); + } + + if (cipherSuite.cipher != null) { + components.addAll(decomposes(cipherSuite.cipher)); + } + + if (cipherSuite.macAlg != null) { + components.addAll(decomposes(cipherSuite.macAlg)); + } + + return components; + } + } + + return super.decomposes(algorithm); + } + } + + static private class X509DisabledAlgConstraints + extends BasicDisabledAlgConstraints { + + X509DisabledAlgConstraints() { + super(DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); + } + + @Override + protected Set decomposes(String algorithm) { + if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) { + CipherSuite cipherSuite = null; + try { + cipherSuite = CipherSuite.valueOf(algorithm); + } catch (IllegalArgumentException iae) { + // ignore: unknown or unsupported ciphersuite + } + + if (cipherSuite != null) { + Set components = new HashSet<>(); + + if(cipherSuite.keyExchange != null) { + components.addAll( + decomposes(cipherSuite.keyExchange, true)); + } + + // Certification path algorithm constraints do not apply + // to cipherSuite.cipher and cipherSuite.macAlg. + + return components; + } + } + + return super.decomposes(algorithm); + } + } +} + diff --git a/src/sun/security/ssl/SSLContextImpl.java b/src/sun/security/ssl/SSLContextImpl.java new file mode 100644 index 00000000..b00a26d3 --- /dev/null +++ b/src/sun/security/ssl/SSLContextImpl.java @@ -0,0 +1,1285 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.net.Socket; + +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; + +import javax.net.ssl.*; + +import sun.security.provider.certpath.AlgorithmChecker; +import sun.security.action.GetPropertyAction; + +public abstract class SSLContextImpl extends SSLContextSpi { + + private static final Debug debug = Debug.getInstance("ssl"); + + private final EphemeralKeyManager ephemeralKeyManager; + private final SSLSessionContextImpl clientCache; + private final SSLSessionContextImpl serverCache; + + private boolean isInitialized; + + private X509ExtendedKeyManager keyManager; + private X509TrustManager trustManager; + private SecureRandom secureRandom; + + // supported and default protocols + private ProtocolList defaultServerProtocolList; + private ProtocolList defaultClientProtocolList; + private ProtocolList supportedProtocolList; + + // supported and default cipher suites + private CipherSuiteList defaultServerCipherSuiteList; + private CipherSuiteList defaultClientCipherSuiteList; + private CipherSuiteList supportedCipherSuiteList; + + SSLContextImpl() { + ephemeralKeyManager = new EphemeralKeyManager(); + clientCache = new SSLSessionContextImpl(); + serverCache = new SSLSessionContextImpl(); + } + + @Override + protected void engineInit(KeyManager[] km, TrustManager[] tm, + SecureRandom sr) throws KeyManagementException { + isInitialized = false; + keyManager = chooseKeyManager(km); + + if (tm == null) { + try { + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore)null); + tm = tmf.getTrustManagers(); + } catch (Exception e) { + // eat + } + } + trustManager = chooseTrustManager(tm); + + if (sr == null) { + secureRandom = JsseJce.getSecureRandom(); + } else { + if (SunJSSE.isFIPS() && + (sr.getProvider() != SunJSSE.cryptoProvider)) { + throw new KeyManagementException + ("FIPS mode: SecureRandom must be from provider " + + SunJSSE.cryptoProvider.getName()); + } + secureRandom = sr; + } + + /* + * The initial delay of seeding the random number generator + * could be long enough to cause the initial handshake on our + * first connection to timeout and fail. Make sure it is + * primed and ready by getting some initial output from it. + */ + if (debug != null && Debug.isOn("sslctx")) { + System.out.println("trigger seeding of SecureRandom"); + } + secureRandom.nextInt(); + if (debug != null && Debug.isOn("sslctx")) { + System.out.println("done seeding SecureRandom"); + } + isInitialized = true; + } + + private X509TrustManager chooseTrustManager(TrustManager[] tm) + throws KeyManagementException { + // We only use the first instance of X509TrustManager passed to us. + for (int i = 0; tm != null && i < tm.length; i++) { + if (tm[i] instanceof X509TrustManager) { + if (SunJSSE.isFIPS() && + !(tm[i] instanceof X509TrustManagerImpl)) { + throw new KeyManagementException + ("FIPS mode: only SunJSSE TrustManagers may be used"); + } + + if (tm[i] instanceof X509ExtendedTrustManager) { + return (X509TrustManager)tm[i]; + } else { + return new AbstractTrustManagerWrapper( + (X509TrustManager)tm[i]); + } + } + } + + // nothing found, return a dummy X509TrustManager. + return DummyX509TrustManager.INSTANCE; + } + + private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms) + throws KeyManagementException { + for (int i = 0; kms != null && i < kms.length; i++) { + KeyManager km = kms[i]; + if (!(km instanceof X509KeyManager)) { + continue; + } + if (SunJSSE.isFIPS()) { + // In FIPS mode, require that one of SunJSSE's own keymanagers + // is used. Otherwise, we cannot be sure that only keys from + // the FIPS token are used. + if ((km instanceof X509KeyManagerImpl) + || (km instanceof SunX509KeyManagerImpl)) { + return (X509ExtendedKeyManager)km; + } else { + // throw exception, we don't want to silently use the + // dummy keymanager without telling the user. + throw new KeyManagementException + ("FIPS mode: only SunJSSE KeyManagers may be used"); + } + } + if (km instanceof X509ExtendedKeyManager) { + return (X509ExtendedKeyManager)km; + } + if (debug != null && Debug.isOn("sslctx")) { + System.out.println( + "X509KeyManager passed to " + + "SSLContext.init(): need an " + + "X509ExtendedKeyManager for SSLEngine use"); + } + return new AbstractKeyManagerWrapper((X509KeyManager)km); + } + + // nothing found, return a dummy X509ExtendedKeyManager + return DummyX509KeyManager.INSTANCE; + } + + @Override + protected SSLSocketFactory engineGetSocketFactory() { + if (!isInitialized) { + throw new IllegalStateException( + "SSLContextImpl is not initialized"); + } + return new SSLSocketFactoryImpl(this); + } + + @Override + protected SSLServerSocketFactory engineGetServerSocketFactory() { + if (!isInitialized) { + throw new IllegalStateException("SSLContext is not initialized"); + } + return new SSLServerSocketFactoryImpl(this); + } + + @Override + protected SSLEngine engineCreateSSLEngine() { + if (!isInitialized) { + throw new IllegalStateException( + "SSLContextImpl is not initialized"); + } + return new SSLEngineImpl(this); + } + + @Override + protected SSLEngine engineCreateSSLEngine(String host, int port) { + if (!isInitialized) { + throw new IllegalStateException( + "SSLContextImpl is not initialized"); + } + return new SSLEngineImpl(this, host, port); + } + + @Override + protected SSLSessionContext engineGetClientSessionContext() { + return clientCache; + } + + @Override + protected SSLSessionContext engineGetServerSessionContext() { + return serverCache; + } + + SecureRandom getSecureRandom() { + return secureRandom; + } + + X509ExtendedKeyManager getX509KeyManager() { + return keyManager; + } + + X509TrustManager getX509TrustManager() { + return trustManager; + } + + EphemeralKeyManager getEphemeralKeyManager() { + return ephemeralKeyManager; + } + + abstract SSLParameters getDefaultServerSSLParams(); + abstract SSLParameters getDefaultClientSSLParams(); + abstract SSLParameters getSupportedSSLParams(); + + // Get supported ProtocolList. + ProtocolList getSuportedProtocolList() { + if (supportedProtocolList == null) { + supportedProtocolList = + new ProtocolList(getSupportedSSLParams().getProtocols()); + } + + return supportedProtocolList; + } + + // Get default ProtocolList. + ProtocolList getDefaultProtocolList(boolean roleIsServer) { + if (roleIsServer) { + if (defaultServerProtocolList == null) { + defaultServerProtocolList = new ProtocolList( + getDefaultServerSSLParams().getProtocols()); + } + + return defaultServerProtocolList; + } else { + if (defaultClientProtocolList == null) { + defaultClientProtocolList = new ProtocolList( + getDefaultClientSSLParams().getProtocols()); + } + + return defaultClientProtocolList; + } + } + + // Get supported CipherSuiteList. + CipherSuiteList getSupportedCipherSuiteList() { + // The maintenance of cipher suites needs to be synchronized. + synchronized (this) { + // Clear cache of available ciphersuites. + clearAvailableCache(); + + if (supportedCipherSuiteList == null) { + supportedCipherSuiteList = getApplicableCipherSuiteList( + getSuportedProtocolList(), false); + } + + return supportedCipherSuiteList; + } + } + + // Get default CipherSuiteList. + CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { + // The maintenance of cipher suites needs to be synchronized. + synchronized (this) { + // Clear cache of available ciphersuites. + clearAvailableCache(); + + if (roleIsServer) { + if (defaultServerCipherSuiteList == null) { + defaultServerCipherSuiteList = getApplicableCipherSuiteList( + getDefaultProtocolList(true), true); + } + + return defaultServerCipherSuiteList; + } else { + if (defaultClientCipherSuiteList == null) { + defaultClientCipherSuiteList = getApplicableCipherSuiteList( + getDefaultProtocolList(false), true); + } + + return defaultClientCipherSuiteList; + } + } + } + + /** + * Return whether a protocol list is the original default enabled + * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() + */ + boolean isDefaultProtocolList(ProtocolList protocols) { + return (protocols == defaultServerProtocolList) || + (protocols == defaultClientProtocolList); + } + + + /* + * Return the list of all available CipherSuites with a priority of + * minPriority or above. + */ + private CipherSuiteList getApplicableCipherSuiteList( + ProtocolList protocols, boolean onlyEnabled) { + + int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY; + if (onlyEnabled) { + minPriority = CipherSuite.DEFAULT_SUITES_PRIORITY; + } + + Collection allowedCipherSuites = + CipherSuite.allowedCipherSuites(); + + TreeSet suites = new TreeSet<>(); + if (!(protocols.collection().isEmpty()) && + protocols.min.v != ProtocolVersion.NONE.v) { + for (CipherSuite suite : allowedCipherSuites) { + if (!suite.allowed || suite.priority < minPriority) { + continue; + } + + if (suite.isAvailable() && + suite.obsoleted > protocols.min.v && + suite.supported <= protocols.max.v) { + if (SSLAlgorithmConstraints.DEFAULT.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + suite.name, null)) { + suites.add(suite); + } + } else if (debug != null && + Debug.isOn("sslctx") && Debug.isOn("verbose")) { + if (suite.obsoleted <= protocols.min.v) { + System.out.println( + "Ignoring obsoleted cipher suite: " + suite); + } else if (suite.supported > protocols.max.v) { + System.out.println( + "Ignoring unsupported cipher suite: " + suite); + } else { + System.out.println( + "Ignoring unavailable cipher suite: " + suite); + } + } + } + } + + return new CipherSuiteList(suites); + } + + /** + * Clear cache of available ciphersuites. If we support all ciphers + * internally, there is no need to clear the cache and calling this + * method has no effect. + * + * Note that every call to clearAvailableCache() and the maintenance of + * cipher suites need to be synchronized with this instance. + */ + private void clearAvailableCache() { + if (CipherSuite.DYNAMIC_AVAILABILITY) { + supportedCipherSuiteList = null; + defaultServerCipherSuiteList = null; + defaultClientCipherSuiteList = null; + CipherSuite.BulkCipher.clearAvailableCache(); + JsseJce.clearEcAvailable(); + } + } + + /* + * The SSLContext implementation for TLS/SSL algorithm + * + * SSL/TLS protocols specify the forward compatibility and version + * roll-back attack protections, however, a number of SSL/TLS server + * vendors did not implement these aspects properly, and some current + * SSL/TLS servers may refuse to talk to a TLS 1.1 or later client. + * + * Considering above interoperability issues, SunJSSE will not set + * TLS 1.1 and TLS 1.2 as the enabled protocols for client by default. + * + * For SSL/TLS servers, there is no such interoperability issues as + * SSL/TLS clients. In SunJSSE, TLS 1.1 or later version will be the + * enabled protocols for server by default. + * + * We may change the behavior when popular TLS/SSL vendors support TLS + * forward compatibility properly. + * + * SSLv2Hello is no longer necessary. This interoperability option was + * put in place in the late 90's when SSLv3/TLS1.0 were relatively new + * and there were a fair number of SSLv2-only servers deployed. Because + * of the security issues in SSLv2, it is rarely (if ever) used, as + * deployments should now be using SSLv3 and TLSv1. + * + * Considering the issues of SSLv2Hello, we should not enable SSLv2Hello + * by default. Applications still can use it by enabling SSLv2Hello with + * the series of setEnabledProtocols APIs. + */ + + /* + * The base abstract SSLContext implementation. + * + * This abstract class encapsulates supported and the default server + * SSL parameters. + * + * @see SSLContext + */ + private abstract static class AbstractSSLContext extends SSLContextImpl { + // parameters + private static final SSLParameters defaultServerSSLParams; + private static final SSLParameters supportedSSLParams; + + static { + // supported SSL parameters + supportedSSLParams = new SSLParameters(); + + // candidates for available protocols + ProtocolVersion[] candidates; + + if (SunJSSE.isFIPS()) { + supportedSSLParams.setProtocols(new String[] { + ProtocolVersion.TLS10.name, + ProtocolVersion.TLS11.name, + ProtocolVersion.TLS12.name + }); + + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } else { + supportedSSLParams.setProtocols(new String[] { + ProtocolVersion.SSL20Hello.name, + ProtocolVersion.SSL30.name, + ProtocolVersion.TLS10.name, + ProtocolVersion.TLS11.name, + ProtocolVersion.TLS12.name + }); + + candidates = new ProtocolVersion[] { + ProtocolVersion.SSL20Hello, + ProtocolVersion.SSL30, + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } + + defaultServerSSLParams = new SSLParameters(); + defaultServerSSLParams.setProtocols( + getAvailableProtocols(candidates).toArray(new String[0])); + } + + @Override + SSLParameters getDefaultServerSSLParams() { + return defaultServerSSLParams; + } + + @Override + SSLParameters getSupportedSSLParams() { + return supportedSSLParams; + } + + static List getAvailableProtocols( + ProtocolVersion[] protocolCandidates) { + + List availableProtocols = Collections.emptyList(); + if (protocolCandidates != null && protocolCandidates.length != 0) { + availableProtocols = new ArrayList<>(protocolCandidates.length); + for (ProtocolVersion p : protocolCandidates) { + if (ProtocolVersion.availableProtocols.contains(p)) { + availableProtocols.add(p.name); + } + } + } + + return availableProtocols; + } + } + + /* + * The SSLContext implementation for SSLv3 and TLS10 algorithm + * + * @see SSLContext + */ + public static final class TLS10Context extends AbstractSSLContext { + private static final SSLParameters defaultClientSSLParams; + + static { + // candidates for available protocols + ProtocolVersion[] candidates; + if (SunJSSE.isFIPS()) { + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS10 + }; + } else { + candidates = new ProtocolVersion[] { + ProtocolVersion.SSL30, + ProtocolVersion.TLS10 + }; + } + + defaultClientSSLParams = new SSLParameters(); + defaultClientSSLParams.setProtocols( + getAvailableProtocols(candidates).toArray(new String[0])); + } + + @Override + SSLParameters getDefaultClientSSLParams() { + return defaultClientSSLParams; + } + } + + /* + * The SSLContext implementation for TLS11 algorithm + * + * @see SSLContext + */ + public static final class TLS11Context extends AbstractSSLContext { + private static final SSLParameters defaultClientSSLParams; + + static { + // candidates for available protocols + ProtocolVersion[] candidates; + if (SunJSSE.isFIPS()) { + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS10, + ProtocolVersion.TLS11 + }; + } else { + candidates = new ProtocolVersion[] { + ProtocolVersion.SSL30, + ProtocolVersion.TLS10, + ProtocolVersion.TLS11 + }; + } + + defaultClientSSLParams = new SSLParameters(); + defaultClientSSLParams.setProtocols( + getAvailableProtocols(candidates).toArray(new String[0])); + } + + @Override + SSLParameters getDefaultClientSSLParams() { + return defaultClientSSLParams; + } + } + + /* + * The SSLContext implementation for TLS12 algorithm + * + * @see SSLContext + */ + public static final class TLS12Context extends AbstractSSLContext { + private static final SSLParameters defaultClientSSLParams; + + static { + // candidates for available protocols + ProtocolVersion[] candidates; + if (SunJSSE.isFIPS()) { + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } else { + candidates = new ProtocolVersion[] { + ProtocolVersion.SSL30, + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } + + defaultClientSSLParams = new SSLParameters(); + defaultClientSSLParams.setProtocols( + getAvailableProtocols(candidates).toArray(new String[0])); + } + + @Override + SSLParameters getDefaultClientSSLParams() { + return defaultClientSSLParams; + } + } + + /* + * The SSLContext implementation for customized TLS protocols + * + * @see SSLContext + */ + private static class CustomizedSSLContext extends AbstractSSLContext { + private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; + private static final SSLParameters defaultClientSSLParams; + private static IllegalArgumentException reservedException = null; + + // Don't want a java.lang.LinkageError for illegal system property. + // + // Please don't throw exception in this static block. Otherwise, + // java.lang.LinkageError may be thrown during the instantiation of + // the provider service. Instead, let's handle the initialization + // exception in constructor. + static { + // candidates for available protocols + ProtocolVersion[] candidates; + + String property = AccessController.doPrivileged( + new GetPropertyAction(PROPERTY_NAME)); + if (property == null || property.length() == 0) { + // the default enabled client TLS protocols + if (SunJSSE.isFIPS()) { + candidates = new ProtocolVersion[] { + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } else { + candidates = new ProtocolVersion[] { + ProtocolVersion.SSL30, + ProtocolVersion.TLS10, + ProtocolVersion.TLS11, + ProtocolVersion.TLS12 + }; + } + } else { + // remove double quote marks from beginning/end of the property + if (property.length() > 1 && property.charAt(0) == '"' && + property.charAt(property.length() - 1) == '"') { + property = property.substring(1, property.length() - 1); + } + + String[] protocols = null; + if (property != null && property.length() != 0) { + protocols = property.split(","); + } else { + reservedException = new IllegalArgumentException( + "No protocol specified in " + + PROPERTY_NAME + " system property"); + protocols = new String[0]; + } + + candidates = new ProtocolVersion[protocols.length]; + for (int i = 0; i < protocols.length; i++) { + protocols[i] = protocols[i].trim(); + // Is it a supported protocol name? + try { + candidates[i] = ProtocolVersion.valueOf(protocols[i]); + } catch (IllegalArgumentException iae) { + reservedException = new IllegalArgumentException( + PROPERTY_NAME + ": " + protocols[i] + + " is not a standard SSL/TLS protocol name", iae); + break; + } + } + + if ((reservedException == null) && SunJSSE.isFIPS()) { + for (ProtocolVersion protocolVersion : candidates) { + if (ProtocolVersion.SSL20Hello.v == protocolVersion.v || + ProtocolVersion.SSL30.v == protocolVersion.v) { + reservedException = new IllegalArgumentException( + PROPERTY_NAME + ": " + protocolVersion + + " is not FIPS compliant"); + } + } + } + } + + defaultClientSSLParams = new SSLParameters(); + if (reservedException == null) { + defaultClientSSLParams.setProtocols( + getAvailableProtocols(candidates).toArray(new String[0])); + } + } + + protected CustomizedSSLContext() { + if (reservedException != null) { + throw reservedException; + } + } + + @Override + SSLParameters getDefaultClientSSLParams() { + return defaultClientSSLParams; + } + } + + /* + * The SSLContext implementation for default "TLS" algorithm + * + * @see SSLContext + */ + public static final class TLSContext extends CustomizedSSLContext { + // use the default constructor and methods + } + + /* + * The SSLContext implementation for default "Default" algorithm + * + * @see SSLContext + */ + public static final class DefaultSSLContext extends CustomizedSSLContext { + private static final String NONE = "NONE"; + private static final String P11KEYSTORE = "PKCS11"; + + private static volatile SSLContextImpl defaultImpl; + + private static TrustManager[] defaultTrustManagers; + private static KeyManager[] defaultKeyManagers; + + public DefaultSSLContext() throws Exception { + try { + super.engineInit(getDefaultKeyManager(), + getDefaultTrustManager(), null); + } catch (Exception e) { + if (debug != null && Debug.isOn("defaultctx")) { + System.out.println("default context init failed: " + e); + } + throw e; + } + + if (defaultImpl == null) { + defaultImpl = this; + } + } + + @Override + protected void engineInit(KeyManager[] km, TrustManager[] tm, + SecureRandom sr) throws KeyManagementException { + throw new KeyManagementException + ("Default SSLContext is initialized automatically"); + } + + static synchronized SSLContextImpl getDefaultImpl() throws Exception { + if (defaultImpl == null) { + new DefaultSSLContext(); + } + return defaultImpl; + } + + private static synchronized TrustManager[] getDefaultTrustManager() + throws Exception { + if (defaultTrustManagers != null) { + return defaultTrustManagers; + } + + KeyStore ks = + TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx"); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(ks); + defaultTrustManagers = tmf.getTrustManagers(); + return defaultTrustManagers; + } + + private static synchronized KeyManager[] getDefaultKeyManager() + throws Exception { + if (defaultKeyManagers != null) { + return defaultKeyManagers; + } + + final Map props = new HashMap<>(); + AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + props.put("keyStore", System.getProperty( + "javax.net.ssl.keyStore", "")); + props.put("keyStoreType", System.getProperty( + "javax.net.ssl.keyStoreType", + KeyStore.getDefaultType())); + props.put("keyStoreProvider", System.getProperty( + "javax.net.ssl.keyStoreProvider", "")); + props.put("keyStorePasswd", System.getProperty( + "javax.net.ssl.keyStorePassword", "")); + return null; + } + }); + + final String defaultKeyStore = props.get("keyStore"); + String defaultKeyStoreType = props.get("keyStoreType"); + String defaultKeyStoreProvider = props.get("keyStoreProvider"); + if (debug != null && Debug.isOn("defaultctx")) { + System.out.println("keyStore is : " + defaultKeyStore); + System.out.println("keyStore type is : " + + defaultKeyStoreType); + System.out.println("keyStore provider is : " + + defaultKeyStoreProvider); + } + + if (P11KEYSTORE.equals(defaultKeyStoreType) && + !NONE.equals(defaultKeyStore)) { + throw new IllegalArgumentException("if keyStoreType is " + + P11KEYSTORE + ", then keyStore must be " + NONE); + } + + FileInputStream fs = null; + KeyStore ks = null; + char[] passwd = null; + try { + if (defaultKeyStore.length() != 0 && + !NONE.equals(defaultKeyStore)) { + fs = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public FileInputStream run() throws Exception { + return new FileInputStream(defaultKeyStore); + } + }); + } + + String defaultKeyStorePassword = props.get("keyStorePasswd"); + if (defaultKeyStorePassword.length() != 0) { + passwd = defaultKeyStorePassword.toCharArray(); + } + + /** + * Try to initialize key store. + */ + if ((defaultKeyStoreType.length()) != 0) { + if (debug != null && Debug.isOn("defaultctx")) { + System.out.println("init keystore"); + } + if (defaultKeyStoreProvider.length() == 0) { + ks = KeyStore.getInstance(defaultKeyStoreType); + } else { + ks = KeyStore.getInstance(defaultKeyStoreType, + defaultKeyStoreProvider); + } + + // if defaultKeyStore is NONE, fs will be null + ks.load(fs, passwd); + } + } finally { + if (fs != null) { + fs.close(); + fs = null; + } + } + + /* + * Try to initialize key manager. + */ + if (debug != null && Debug.isOn("defaultctx")) { + System.out.println("init keymanager of type " + + KeyManagerFactory.getDefaultAlgorithm()); + } + KeyManagerFactory kmf = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + + if (P11KEYSTORE.equals(defaultKeyStoreType)) { + kmf.init(ks, null); // do not pass key passwd if using token + } else { + kmf.init(ks, passwd); + } + + defaultKeyManagers = kmf.getKeyManagers(); + return defaultKeyManagers; + } + } + +} + + +final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager + implements X509TrustManager { + + // the delegated trust manager + private final X509TrustManager tm; + + AbstractTrustManagerWrapper(X509TrustManager tm) { + this.tm = tm; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + tm.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + tm.checkServerTrusted(chain, authType); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return tm.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + tm.checkClientTrusted(chain, authType); + checkAdditionalTrust(chain, authType, socket, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + tm.checkServerTrusted(chain, authType); + checkAdditionalTrust(chain, authType, socket, false); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + tm.checkClientTrusted(chain, authType); + checkAdditionalTrust(chain, authType, engine, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + tm.checkServerTrusted(chain, authType); + checkAdditionalTrust(chain, authType, engine, false); + } + + private void checkAdditionalTrust(X509Certificate[] chain, String authType, + Socket socket, boolean isClient) throws CertificateException { + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = sslSocket.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + X509TrustManagerImpl.checkIdentity( + hostname, chain[0], identityAlg); + } + + // try the best to check the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + AlgorithmConstraints constraints = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] peerSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + sslSocket, peerSupportedSignAlgs, true); + } else { + constraints = + new SSLAlgorithmConstraints(sslSocket, true); + } + } else { + constraints = new SSLAlgorithmConstraints(sslSocket, true); + } + + checkAlgorithmConstraints(chain, constraints); + } + } + + private void checkAdditionalTrust(X509Certificate[] chain, String authType, + SSLEngine engine, boolean isClient) throws CertificateException { + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = engine.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + X509TrustManagerImpl.checkIdentity( + hostname, chain[0], identityAlg); + } + + // try the best to check the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + AlgorithmConstraints constraints = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] peerSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + engine, peerSupportedSignAlgs, true); + } else { + constraints = + new SSLAlgorithmConstraints(engine, true); + } + } else { + constraints = new SSLAlgorithmConstraints(engine, true); + } + + checkAlgorithmConstraints(chain, constraints); + } + } + + private void checkAlgorithmConstraints(X509Certificate[] chain, + AlgorithmConstraints constraints) throws CertificateException { + + try { + // Does the certificate chain end with a trusted certificate? + int checkedLength = chain.length - 1; + + Collection trustedCerts = new HashSet<>(); + X509Certificate[] certs = tm.getAcceptedIssuers(); + if ((certs != null) && (certs.length > 0)){ + Collections.addAll(trustedCerts, certs); + } + + if (trustedCerts.contains(chain[checkedLength])) { + checkedLength--; + } + + // A forward checker, need to check from trust to target + if (checkedLength >= 0) { + AlgorithmChecker checker = new AlgorithmChecker(constraints); + checker.init(false); + for (int i = checkedLength; i >= 0; i--) { + Certificate cert = chain[i]; + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.emptySet()); + } + } + } catch (CertPathValidatorException cpve) { + throw new CertificateException( + "Certificates does not conform to algorithm constraints"); + } + } +} + +// Dummy X509TrustManager implementation, rejects all peer certificates. +// Used if the application did not specify a proper X509TrustManager. +final class DummyX509TrustManager extends X509ExtendedTrustManager + implements X509TrustManager { + + static final X509TrustManager INSTANCE = new DummyX509TrustManager(); + + private DummyX509TrustManager() { + // empty + } + + /* + * Given the partial or complete certificate chain + * provided by the peer, build a certificate path + * to a trusted root and return if it can be + * validated and is trusted for client SSL authentication. + * If not, it throws an exception. + */ + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation avaiable"); + } + + /* + * Given the partial or complete certificate chain + * provided by the peer, build a certificate path + * to a trusted root and return if it can be + * validated and is trusted for server SSL authentication. + * If not, it throws an exception. + */ + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + /* + * Return an array of issuer certificates which are trusted + * for authenticating peers. + */ + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } +} + +/* + * A wrapper class to turn a X509KeyManager into an X509ExtendedKeyManager + */ +final class AbstractKeyManagerWrapper extends X509ExtendedKeyManager { + + private final X509KeyManager km; + + AbstractKeyManagerWrapper(X509KeyManager km) { + this.km = km; + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return km.getClientAliases(keyType, issuers); + } + + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + return km.chooseClientAlias(keyType, issuers, socket); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return km.getServerAliases(keyType, issuers); + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + return km.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return km.getCertificateChain(alias); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + return km.getPrivateKey(alias); + } + + // Inherit chooseEngineClientAlias() and chooseEngineServerAlias() from + // X509ExtendedKeymanager. It defines them to return null; +} + + +// Dummy X509KeyManager implementation, never returns any certificates/keys. +// Used if the application did not specify a proper X509TrustManager. +final class DummyX509KeyManager extends X509ExtendedKeyManager { + + static final X509ExtendedKeyManager INSTANCE = new DummyX509KeyManager(); + + private DummyX509KeyManager() { + // empty + } + + /* + * Get the matching aliases for authenticating the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return null; + } + + /* + * Choose an alias to authenticate the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseClientAlias(String[] keyTypes, Principal[] issuers, + Socket socket) { + return null; + } + + /* + * Choose an alias to authenticate the client side of an + * engine given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseEngineClientAlias( + String[] keyTypes, Principal[] issuers, SSLEngine engine) { + return null; + } + + /* + * Get the matching aliases for authenticating the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return null; + } + + /* + * Choose an alias to authenticate the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + return null; + } + + /* + * Choose an alias to authenticate the server side of an engine + * given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseEngineServerAlias( + String keyType, Principal[] issuers, SSLEngine engine) { + return null; + } + + /** + * Returns the certificate chain associated with the given alias. + * + * @param alias the alias name + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last) + */ + @Override + public X509Certificate[] getCertificateChain(String alias) { + return null; + } + + /* + * Returns the key associated with the given alias, using the given + * password to recover it. + * + * @param alias the alias name + * + * @return the requested key + */ + @Override + public PrivateKey getPrivateKey(String alias) { + return null; + } +} diff --git a/src/sun/security/ssl/SSLEngineImpl.java b/src/sun/security/ssl/SSLEngineImpl.java new file mode 100644 index 00000000..48913c2e --- /dev/null +++ b/src/sun/security/ssl/SSLEngineImpl.java @@ -0,0 +1,2222 @@ +/* + * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.nio.*; +import java.util.*; +import java.security.*; +import java.util.function.BiFunction; + +import javax.crypto.BadPaddingException; + +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.*; + +/** + * Implementation of an non-blocking SSLEngine. + * + * *Currently*, the SSLEngine code exists in parallel with the current + * SSLSocket. As such, the current implementation is using legacy code + * with many of the same abstractions. However, it varies in many + * areas, most dramatically in the IO handling. + * + * There are three main I/O threads that can be existing in parallel: + * wrap(), unwrap(), and beginHandshake(). We are encouraging users to + * not call multiple instances of wrap or unwrap, because the data could + * appear to flow out of the SSLEngine in a non-sequential order. We + * take all steps we can to at least make sure the ordering remains + * consistent, but once the calls returns, anything can happen. For + * example, thread1 and thread2 both call wrap, thread1 gets the first + * packet, thread2 gets the second packet, but thread2 gets control back + * before thread1, and sends the data. The receiving side would see an + * out-of-order error. + * + * Handshaking is still done the same way as SSLSocket using the normal + * InputStream/OutputStream abstactions. We create + * ClientHandshakers/ServerHandshakers, which produce/consume the + * handshaking data. The transfer of the data is largely handled by the + * HandshakeInStream/HandshakeOutStreams. Lastly, the + * InputRecord/OutputRecords still have the same functionality, except + * that they are overridden with EngineInputRecord/EngineOutputRecord, + * which provide SSLEngine-specific functionality. + * + * Some of the major differences are: + * + * EngineInputRecord/EngineOutputRecord/EngineWriter: + * + * In order to avoid writing whole new control flows for + * handshaking, and to reuse most of the same code, we kept most + * of the actual handshake code the same. As usual, reading + * handshake data may trigger output of more handshake data, so + * what we do is write this data to internal buffers, and wait for + * wrap() to be called to give that data a ride. + * + * All data is routed through + * EngineInputRecord/EngineOutputRecord. However, all handshake + * data (ct_alert/ct_change_cipher_spec/ct_handshake) are passed + * through to the the underlying InputRecord/OutputRecord, and + * the data uses the internal buffers. + * + * Application data is handled slightly different, we copy the data + * directly from the src to the dst buffers, and do all operations + * on those buffers, saving the overhead of multiple copies. + * + * In the case of an inbound record, unwrap passes the inbound + * ByteBuffer to the InputRecord. If the data is handshake data, + * the data is read into the InputRecord's internal buffer. If + * the data is application data, the data is decoded directly into + * the dst buffer. + * + * In the case of an outbound record, when the write to the + * "real" OutputStream's would normally take place, instead we + * call back up to the EngineOutputRecord's version of + * writeBuffer, at which time we capture the resulting output in a + * ByteBuffer, and send that back to the EngineWriter for internal + * storage. + * + * EngineWriter is responsible for "handling" all outbound + * data, be it handshake or app data, and for returning the data + * to wrap() in the proper order. + * + * ClientHandshaker/ServerHandshaker/Handshaker: + * Methods which relied on SSLSocket now have work on either + * SSLSockets or SSLEngines. + * + * @author Brad Wetmore + */ +final public class SSLEngineImpl extends SSLEngine { + + // + // Fields and global comments + // + + /* + * There's a state machine associated with each connection, which + * among other roles serves to negotiate session changes. + * + * - START with constructor, until the TCP connection's around. + * - HANDSHAKE picks session parameters before allowing traffic. + * There are many substates due to sequencing requirements + * for handshake messages. + * - DATA may be transmitted. + * - RENEGOTIATE state allows concurrent data and handshaking + * traffic ("same" substates as HANDSHAKE), and terminates + * in selection of new session (and connection) parameters + * - ERROR state immediately precedes abortive disconnect. + * - CLOSED when one side closes down, used to start the shutdown + * process. SSL connection objects are not reused. + * + * State affects what SSL record types may legally be sent: + * + * - Handshake ... only in HANDSHAKE and RENEGOTIATE states + * - App Data ... only in DATA and RENEGOTIATE states + * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE + * + * Re what may be received: same as what may be sent, except that + * HandshakeRequest handshaking messages can come from servers even + * in the application data state, to request entry to RENEGOTIATE. + * + * The state machine within HANDSHAKE and RENEGOTIATE states controls + * the pending session, not the connection state, until the change + * cipher spec and "Finished" handshake messages are processed and + * make the "new" session become the current one. + * + * NOTE: details of the SMs always need to be nailed down better. + * The text above illustrates the core ideas. + * + * +---->-------+------>--------->-------+ + * | | | + * <-----< ^ ^ <-----< | + *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE | + * v v v | + * | | | | + * +------------+---------------+ | + * | | + * v | + * ERROR>------>----->CLOSED<--------<----+ + * + * ALSO, note that the the purpose of handshaking (renegotiation is + * included) is to assign a different, and perhaps new, session to + * the connection. The SSLv3 spec is a bit confusing on that new + * protocol feature. + */ + private int connectionState; + + private static final int cs_START = 0; + private static final int cs_HANDSHAKE = 1; + private static final int cs_DATA = 2; + private static final int cs_RENEGOTIATE = 3; + private static final int cs_ERROR = 4; + private static final int cs_CLOSED = 6; + + /* + * Once we're in state cs_CLOSED, we can continue to + * wrap/unwrap until we finish sending/receiving the messages + * for close_notify. EngineWriter handles outboundDone. + */ + private boolean inboundDone = false; + + EngineWriter writer; + + /* + * The authentication context holds all information used to establish + * who this end of the connection is (certificate chains, private keys, + * etc) and who is trusted (e.g. as CAs or websites). + */ + private SSLContextImpl sslContext; + + /* + * This connection is one of (potentially) many associated with + * any given session. The output of the handshake protocol is a + * new session ... although all the protocol description talks + * about changing the cipher spec (and it does change), in fact + * that's incidental since it's done by changing everything that + * is associated with a session at the same time. (TLS/IETF may + * change that to add client authentication w/o new key exchg.) + */ + private Handshaker handshaker; + private SSLSessionImpl sess; + private volatile SSLSessionImpl handshakeSession; + + + /* + * Client authentication be off, requested, or required. + * + * This will be used by both this class and SSLSocket's variants. + */ + static final byte clauth_none = 0; + static final byte clauth_requested = 1; + static final byte clauth_required = 2; + + /* + * Flag indicating that the engine has received a ChangeCipherSpec message. + */ + private boolean receivedCCS; + + /* + * Flag indicating if the next record we receive MUST be a Finished + * message. Temporarily set during the handshake to ensure that + * a change cipher spec message is followed by a finished message. + */ + private boolean expectingFinished; + + + /* + * If someone tries to closeInbound() (say at End-Of-Stream) + * our engine having received a close_notify, we need to + * notify the app that we may have a truncation attack underway. + */ + private boolean recvCN; + + /* + * For improved diagnostics, we detail connection closure + * If the engine is closed (connectionState >= cs_ERROR), + * closeReason != null indicates if the engine was closed + * because of an error or because or normal shutdown. + */ + private SSLException closeReason; + + /* + * Per-connection private state that doesn't change when the + * session is changed. + */ + private byte doClientAuth; + private boolean enableSessionCreation = true; + EngineInputRecord inputRecord; + EngineOutputRecord outputRecord; + private AccessControlContext acc; + + // The cipher suites enabled for use on this connection. + private CipherSuiteList enabledCipherSuites; + + // the endpoint identification protocol + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + + // The server name indication and matchers + List serverNames = + Collections.emptyList(); + Collection sniMatchers = + Collections.emptyList(); + + // Configured application protocol values + String[] applicationProtocols = new String[0]; + + // Negotiated application protocol value. + // + // The value under negotiation will be obtained from handshaker. + String applicationProtocol = null; + + + // Callback function that selects the application protocol value during + // the SSL/TLS handshake. + BiFunction, String> applicationProtocolSelector; + + // Have we been told whether we're client or server? + private boolean serverModeSet = false; + private boolean roleIsServer; + + /* + * The protocol versions enabled for use on this connection. + * + * Note: we support a pseudo protocol called SSLv2Hello which when + * set will result in an SSL v2 Hello being sent with SSL (version 3.0) + * or TLS (version 3.1, 3.2, etc.) version info. + */ + private ProtocolList enabledProtocols; + + /* + * The SSL version associated with this connection. + */ + private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT; + + /* + * Crypto state that's reinitialized when the session changes. + */ + private Authenticator readAuthenticator, writeAuthenticator; + private CipherBox readCipher, writeCipher; + // NOTE: compression state would be saved here + + /* + * security parameters for secure renegotiation. + */ + private boolean secureRenegotiation; + private byte[] clientVerifyData; + private byte[] serverVerifyData; + + /* + * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * + * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES. + * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * + * + * There are several locks here. + * + * The primary lock is the per-instance lock used by + * synchronized(this) and the synchronized methods. It controls all + * access to things such as the connection state and variables which + * affect handshaking. If we are inside a synchronized method, we + * can access the state directly, otherwise, we must use the + * synchronized equivalents. + * + * Note that we must never acquire the this lock after + * writeLock or run the risk of deadlock. + * + * Grab some coffee, and be careful with any code changes. + */ + private Object wrapLock; + private Object unwrapLock; + Object writeLock; + + /* + * Is it the first application record to write? + */ + private boolean isFirstAppOutputRecord = true; + + /* + * Whether local cipher suites preference in server side should be + * honored during handshaking? + */ + private boolean preferLocalCipherSuites = false; + + /* + * Class and subclass dynamic debugging support + */ + private static final Debug debug = Debug.getInstance("ssl"); + + // + // Initialization/Constructors + // + + /** + * Constructor for an SSLEngine from SSLContext, without + * host/port hints. This Engine will not be able to cache + * sessions, but must renegotiate everything by hand. + */ + SSLEngineImpl(SSLContextImpl ctx) { + super(); + init(ctx); + } + + /** + * Constructor for an SSLEngine from SSLContext. + */ + SSLEngineImpl(SSLContextImpl ctx, String host, int port) { + super(host, port); + init(ctx); + } + + /** + * Initializes the Engine + */ + private void init(SSLContextImpl ctx) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println("Using SSLEngineImpl."); + } + + sslContext = ctx; + sess = SSLSessionImpl.nullSession; + handshakeSession = null; + + /* + * State is cs_START until we initialize the handshaker. + * + * Apps using SSLEngine are probably going to be server. + * Somewhat arbitrary choice. + */ + roleIsServer = true; + connectionState = cs_START; + receivedCCS = false; + + // default server name indication + serverNames = + Utilities.addToSNIServerNameList(serverNames, getPeerHost()); + + /* + * default read and write side cipher and MAC support + * + * Note: compression support would go here too + */ + readCipher = CipherBox.NULL; + readAuthenticator = MAC.NULL; + writeCipher = CipherBox.NULL; + writeAuthenticator = MAC.NULL; + + // default security parameters for secure renegotiation + secureRenegotiation = false; + clientVerifyData = new byte[0]; + serverVerifyData = new byte[0]; + + enabledCipherSuites = + sslContext.getDefaultCipherSuiteList(roleIsServer); + enabledProtocols = + sslContext.getDefaultProtocolList(roleIsServer); + + wrapLock = new Object(); + unwrapLock = new Object(); + writeLock = new Object(); + + /* + * Save the Access Control Context. This will be used later + * for a couple of things, including providing a context to + * run tasks in, and for determining which credentials + * to use for Subject based (JAAS) decisions + */ + acc = AccessController.getContext(); + + /* + * All outbound application data goes through this OutputRecord, + * other data goes through their respective records created + * elsewhere. All inbound data goes through this one + * input record. + */ + outputRecord = + new EngineOutputRecord(Record.ct_application_data, this); + inputRecord = new EngineInputRecord(this); + inputRecord.enableFormatChecks(); + + writer = new EngineWriter(); + } + + /** + * Initialize the handshaker object. This means: + * + * . if a handshake is already in progress (state is cs_HANDSHAKE + * or cs_RENEGOTIATE), do nothing and return + * + * . if the engine is already closed, throw an Exception (internal error) + * + * . otherwise (cs_START or cs_DATA), create the appropriate handshaker + * object and advance the connection state (to cs_HANDSHAKE or + * cs_RENEGOTIATE, respectively). + * + * This method is called right after a new engine is created, when + * starting renegotiation, or when changing client/server mode of the + * engine. + */ + private void initHandshaker() { + switch (connectionState) { + + // + // Starting a new handshake. + // + case cs_START: + case cs_DATA: + break; + + // + // We're already in the middle of a handshake. + // + case cs_HANDSHAKE: + case cs_RENEGOTIATE: + return; + + // + // Anyone allowed to call this routine is required to + // do so ONLY if the connection state is reasonable... + // + default: + throw new IllegalStateException("Internal error"); + } + + // state is either cs_START or cs_DATA + if (connectionState == cs_START) { + connectionState = cs_HANDSHAKE; + } else { // cs_DATA + connectionState = cs_RENEGOTIATE; + } + if (roleIsServer) { + handshaker = new ServerHandshaker(this, sslContext, + enabledProtocols, doClientAuth, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); + handshaker.setSNIMatchers(sniMatchers); + handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); + } else { + handshaker = new ClientHandshaker(this, sslContext, + enabledProtocols, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); + handshaker.setSNIServerNames(serverNames); + } + handshaker.setEnabledCipherSuites(enabledCipherSuites); + handshaker.setEnableSessionCreation(enableSessionCreation); + handshaker.setApplicationProtocols(applicationProtocols); + handshaker.setApplicationProtocolSelectorSSLEngine( + applicationProtocolSelector); + } + + /* + * Report the current status of the Handshaker + */ + private HandshakeStatus getHSStatus(HandshakeStatus hss) { + + if (hss != null) { + return hss; + } + + synchronized (this) { + if (writer.hasOutboundData()) { + return HandshakeStatus.NEED_WRAP; + } else if (handshaker != null) { + if (handshaker.taskOutstanding()) { + return HandshakeStatus.NEED_TASK; + } else { + return HandshakeStatus.NEED_UNWRAP; + } + } else if (connectionState == cs_CLOSED) { + /* + * Special case where we're closing, but + * still need the close_notify before we + * can officially be closed. + * + * Note isOutboundDone is taken care of by + * hasOutboundData() above. + */ + if (!isInboundDone()) { + return HandshakeStatus.NEED_UNWRAP; + } // else not handshaking + } + + return HandshakeStatus.NOT_HANDSHAKING; + } + } + + synchronized private void checkTaskThrown() throws SSLException { + if (handshaker != null) { + handshaker.checkThrown(); + } + } + + // + // Handshaking and connection state code + // + + /* + * Provides "this" synchronization for connection state. + * Otherwise, you can access it directly. + */ + synchronized private int getConnectionState() { + return connectionState; + } + + synchronized private void setConnectionState(int state) { + connectionState = state; + } + + /* + * Get the Access Control Context. + * + * Used for a known context to + * run tasks in, and for determining which credentials + * to use for Subject-based (JAAS) decisions. + */ + AccessControlContext getAcc() { + return acc; + } + + /* + * Is a handshake currently underway? + */ + @Override + public SSLEngineResult.HandshakeStatus getHandshakeStatus() { + return getHSStatus(null); + } + + /* + * When a connection finishes handshaking by enabling use of a newly + * negotiated session, each end learns about it in two halves (read, + * and write). When both read and write ciphers have changed, and the + * last handshake message has been read, the connection has joined + * (rejoined) the new session. + * + * NOTE: The SSLv3 spec is rather unclear on the concepts here. + * Sessions don't change once they're established (including cipher + * suite and master secret) but connections can join them (and leave + * them). They're created by handshaking, though sometime handshaking + * causes connections to join up with pre-established sessions. + * + * Synchronized on "this" from readRecord. + */ + private void changeReadCiphers() throws SSLException { + if (connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) { + throw new SSLProtocolException( + "State error, change cipher specs"); + } + + // ... create decompressor + + CipherBox oldCipher = readCipher; + + try { + readCipher = handshaker.newReadCipher(); + readAuthenticator = handshaker.newReadAuthenticator(); + } catch (GeneralSecurityException e) { + // "can't happen" + throw new SSLException("Algorithm missing: ", e); + } + + /* + * Dispose of any intermediate state in the underlying cipher. + * For PKCS11 ciphers, this will release any attached sessions, + * and thus make finalization faster. + * + * Since MAC's doFinal() is called for every SSL/TLS packet, it's + * not necessary to do the same with MAC's. + */ + oldCipher.dispose(); + } + + /* + * used by Handshaker to change the active write cipher, follows + * the output of the CCS message. + * + * Also synchronized on "this" from readRecord/delegatedTask. + */ + void changeWriteCiphers() throws SSLException { + if (connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) { + throw new SSLProtocolException( + "State error, change cipher specs"); + } + + // ... create compressor + + CipherBox oldCipher = writeCipher; + + try { + writeCipher = handshaker.newWriteCipher(); + writeAuthenticator = handshaker.newWriteAuthenticator(); + } catch (GeneralSecurityException e) { + // "can't happen" + throw new SSLException("Algorithm missing: ", e); + } + + // See comment above. + oldCipher.dispose(); + + // reset the flag of the first application record + isFirstAppOutputRecord = true; + } + + /* + * Updates the SSL version associated with this connection. + * Called from Handshaker once it has determined the negotiated version. + */ + synchronized void setVersion(ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + outputRecord.setVersion(protocolVersion); + } + + + /** + * Kickstart the handshake if it is not already in progress. + * This means: + * + * . if handshaking is already underway, do nothing and return + * + * . if the engine is not connected or already closed, throw an + * Exception. + * + * . otherwise, call initHandshake() to initialize the handshaker + * object and progress the state. Then, send the initial + * handshaking message if appropriate (always on clients and + * on servers when renegotiating). + */ + private synchronized void kickstartHandshake() throws IOException { + switch (connectionState) { + + case cs_START: + if (!serverModeSet) { + throw new IllegalStateException( + "Client/Server mode not yet set."); + } + initHandshaker(); + break; + + case cs_HANDSHAKE: + // handshaker already setup, proceed + break; + + case cs_DATA: + if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { + throw new SSLHandshakeException( + "Insecure renegotiation is not allowed"); + } + + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: Using insecure renegotiation"); + } + } + + // initialize the handshaker, move to cs_RENEGOTIATE + initHandshaker(); + break; + + case cs_RENEGOTIATE: + // handshaking already in progress, return + return; + + default: + // cs_ERROR/cs_CLOSED + throw new SSLException("SSLEngine is closing/closed"); + } + + // + // Kickstart handshake state machine if we need to ... + // + // Note that handshaker.kickstart() writes the message + // to its HandshakeOutStream, which calls back into + // SSLSocketImpl.writeRecord() to send it. + // + if (!handshaker.activated()) { + // prior to handshaking, activate the handshake + if (connectionState == cs_RENEGOTIATE) { + // don't use SSLv2Hello when renegotiating + handshaker.activate(protocolVersion); + } else { + handshaker.activate(null); + } + + if (handshaker instanceof ClientHandshaker) { + // send client hello + handshaker.kickstart(); + } else { // instanceof ServerHandshaker + if (connectionState == cs_HANDSHAKE) { + // initial handshake, no kickstart message to send + } else { + // we want to renegotiate, send hello request + handshaker.kickstart(); + + // hello request is not included in the handshake + // hashes, reset them + handshaker.handshakeHash.reset(); + } + } + } + } + + /* + * Start a SSLEngine handshake + */ + @Override + public void beginHandshake() throws SSLException { + try { + kickstartHandshake(); + } catch (Exception e) { + fatal(Alerts.alert_handshake_failure, + "Couldn't kickstart handshaking", e); + } + } + + + // + // Read/unwrap side + // + + + /** + * Unwraps a buffer. Does a variety of checks before grabbing + * the unwrapLock, which blocks multiple unwraps from occurring. + */ + @Override + public SSLEngineResult unwrap(ByteBuffer netData, ByteBuffer [] appData, + int offset, int length) throws SSLException { + + EngineArgs ea = new EngineArgs(netData, appData, offset, length); + + try { + synchronized (unwrapLock) { + return readNetRecord(ea); + } + } catch (Exception e) { + /* + * Don't reset position so it looks like we didn't + * consume anything. We did consume something, and it + * got us into this situation, so report that much back. + * Our days of consuming are now over anyway. + */ + fatal(Alerts.alert_internal_error, + "problem unwrapping net record", e); + return null; // make compiler happy + } finally { + /* + * Just in case something failed to reset limits properly. + */ + ea.resetLim(); + } + } + + /* + * Makes additional checks for unwrap, but this time more + * specific to this packet and the current state of the machine. + */ + private SSLEngineResult readNetRecord(EngineArgs ea) throws IOException { + + Status status = null; + HandshakeStatus hsStatus = null; + + /* + * See if the handshaker needs to report back some SSLException. + */ + checkTaskThrown(); + + /* + * Check if we are closing/closed. + */ + if (isInboundDone()) { + return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); + } + + /* + * If we're still in cs_HANDSHAKE, make sure it's been + * started. + */ + synchronized (this) { + if ((connectionState == cs_HANDSHAKE) || + (connectionState == cs_START)) { + kickstartHandshake(); + + /* + * If there's still outbound data to flush, we + * can return without trying to unwrap anything. + */ + hsStatus = getHSStatus(null); + + if (hsStatus == HandshakeStatus.NEED_WRAP) { + return new SSLEngineResult(Status.OK, hsStatus, 0, 0); + } + } + } + + /* + * Grab a copy of this if it doesn't already exist, + * and we can use it several places before anything major + * happens on this side. Races aren't critical + * here. + */ + if (hsStatus == null) { + hsStatus = getHSStatus(null); + } + + /* + * If we have a task outstanding, this *MUST* be done before + * doing any more unwrapping, because we could be in the middle + * of receiving a handshake message, for example, a finished + * message which would change the ciphers. + */ + if (hsStatus == HandshakeStatus.NEED_TASK) { + return new SSLEngineResult( + Status.OK, hsStatus, 0, 0); + } + + /* + * Check the packet to make sure enough is here. + * This will also indirectly check for 0 len packets. + */ + int packetLen = inputRecord.bytesInCompletePacket(ea.netData); + + // Is this packet bigger than SSL/TLS normally allows? + if (packetLen > sess.getPacketBufferSize()) { + if (packetLen > Record.maxLargeRecordSize) { + throw new SSLProtocolException( + "Input SSL/TLS record too big: max = " + + Record.maxLargeRecordSize + + " len = " + packetLen); + } else { + // Expand the expected maximum packet/application buffer + // sizes. + sess.expandBufferSizes(); + } + } + + /* + * Check for OVERFLOW. + * + * To be considered: We could delay enforcing the application buffer + * free space requirement until after the initial handshaking. + */ + if ((packetLen - Record.headerSize) > ea.getAppRemaining()) { + return new SSLEngineResult(Status.BUFFER_OVERFLOW, hsStatus, 0, 0); + } + + // check for UNDERFLOW. + if ((packetLen == -1) || (ea.netData.remaining() < packetLen)) { + return new SSLEngineResult( + Status.BUFFER_UNDERFLOW, hsStatus, 0, 0); + } + + /* + * We're now ready to actually do the read. + * The only result code we really need to be exactly + * right is the HS finished, for signaling to + * HandshakeCompletedListeners. + */ + try { + hsStatus = readRecord(ea); + } catch (SSLException e) { + throw e; + } catch (IOException e) { + throw new SSLException("readRecord", e); + } + + /* + * Check the various condition that we could be reporting. + * + * It's *possible* something might have happened between the + * above and now, but it was better to minimally lock "this" + * during the read process. We'll return the current + * status, which is more representative of the current state. + * + * status above should cover: FINISHED, NEED_TASK + */ + status = (isInboundDone() ? Status.CLOSED : Status.OK); + hsStatus = getHSStatus(hsStatus); + + return new SSLEngineResult(status, hsStatus, + ea.deltaNet(), ea.deltaApp()); + } + + /* + * Actually do the read record processing. + * + * Returns a Status if it can make specific determinations + * of the engine state. In particular, we need to signal + * that a handshake just completed. + * + * It would be nice to be symmetrical with the write side and move + * the majority of this to EngineInputRecord, but there's too much + * SSLEngine state to do that cleanly. It must still live here. + */ + private HandshakeStatus readRecord(EngineArgs ea) throws IOException { + + HandshakeStatus hsStatus = null; + + /* + * The various operations will return new sliced BB's, + * this will avoid having to worry about positions and + * limits in the netBB. + */ + ByteBuffer readBB = null; + ByteBuffer decryptedBB = null; + + if (getConnectionState() != cs_ERROR) { + + /* + * Read a record ... maybe emitting an alert if we get a + * comprehensible but unsupported "hello" message during + * format checking (e.g. V2). + */ + try { + readBB = inputRecord.read(ea.netData); + } catch (IOException e) { + fatal(Alerts.alert_unexpected_message, e); + } + + /* + * The basic SSLv3 record protection involves (optional) + * encryption for privacy, and an integrity check ensuring + * data origin authentication. We do them both here, and + * throw a fatal alert if the integrity check fails. + */ + try { + decryptedBB = inputRecord.decrypt( + readAuthenticator, readCipher, readBB); + } catch (BadPaddingException e) { + byte alertType = (inputRecord.contentType() == + Record.ct_handshake) ? + Alerts.alert_handshake_failure : + Alerts.alert_bad_record_mac; + fatal(alertType, e.getMessage(), e); + } + + // if (!inputRecord.decompress(c)) + // fatal(Alerts.alert_decompression_failure, + // "decompression failure"); + + + /* + * Process the record. + */ + + synchronized (this) { + switch (inputRecord.contentType()) { + case Record.ct_handshake: + /* + * Handshake messages always go to a pending session + * handshaker ... if there isn't one, create one. This + * must work asynchronously, for renegotiation. + * + * NOTE that handshaking will either resume a session + * which was in the cache (and which might have other + * connections in it already), or else will start a new + * session (new keys exchanged) with just this connection + * in it. + */ + initHandshaker(); + if (!handshaker.activated()) { + // prior to handshaking, activate the handshake + if (connectionState == cs_RENEGOTIATE) { + // don't use SSLv2Hello when renegotiating + handshaker.activate(protocolVersion); + } else { + handshaker.activate(null); + } + } + + /* + * process the handshake record ... may contain just + * a partial handshake message or multiple messages. + * + * The handshaker state machine will ensure that it's + * a finished message. + */ + handshaker.process_record(inputRecord, expectingFinished); + expectingFinished = false; + + if (handshaker.invalidated) { + handshaker = null; + receivedCCS = false; + // if state is cs_RENEGOTIATE, revert it to cs_DATA + if (connectionState == cs_RENEGOTIATE) { + connectionState = cs_DATA; + } + } else if (handshaker.isDone()) { + // reset the parameters for secure renegotiation. + secureRenegotiation = + handshaker.isSecureRenegotiation(); + clientVerifyData = handshaker.getClientVerifyData(); + serverVerifyData = handshaker.getServerVerifyData(); + // set connection ALPN value + applicationProtocol = + handshaker.getHandshakeApplicationProtocol(); + + sess = handshaker.getSession(); + handshakeSession = null; + if (!writer.hasOutboundData()) { + hsStatus = HandshakeStatus.FINISHED; + } + handshaker = null; + connectionState = cs_DATA; + receivedCCS = false; + + // No handshakeListeners here. That's a + // SSLSocket thing. + } else if (handshaker.taskOutstanding()) { + hsStatus = HandshakeStatus.NEED_TASK; + } + break; + + case Record.ct_application_data: + // Pass this right back up to the application. + if ((connectionState != cs_DATA) + && (connectionState != cs_RENEGOTIATE) + && (connectionState != cs_CLOSED)) { + throw new SSLProtocolException( + "Data received in non-data state: " + + connectionState); + } + + if (expectingFinished) { + throw new SSLProtocolException + ("Expecting finished message, received data"); + } + + /* + * Don't return data once the inbound side is + * closed. + */ + if (!inboundDone) { + ea.scatter(decryptedBB.slice()); + } + break; + + case Record.ct_alert: + recvAlert(); + break; + + case Record.ct_change_cipher_spec: + if ((connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) + || !handshaker.sessionKeysCalculated() + || receivedCCS) { + // For the CCS message arriving in the wrong state + fatal(Alerts.alert_unexpected_message, + "illegal change cipher spec msg, conn state = " + + connectionState + ", handshake state = " + + handshaker.state); + } else if (inputRecord.available() != 1 + || inputRecord.read() != 1) { + // For structural/content issues with the CCS + fatal(Alerts.alert_unexpected_message, + "Malformed change cipher spec msg"); + } + + // Once we've received CCS, update the flag. + // If the remote endpoint sends it again in this handshake + // we won't process it. + receivedCCS = true; + + // + // The first message after a change_cipher_spec + // record MUST be a "Finished" handshake record, + // else it's a protocol violation. We force this + // to be checked by a minor tweak to the state + // machine. + // + changeReadCiphers(); + // next message MUST be a finished message + expectingFinished = true; + break; + + default: + // + // TLS requires that unrecognized records be ignored. + // + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", Received record type: " + + inputRecord.contentType()); + } + break; + } // switch + + /* + * We only need to check the sequence number state for + * non-handshaking record. + * + * Note that in order to maintain the handshake status + * properly, we check the sequence number after the last + * record reading process. As we request renegotiation + * or close the connection for wrapped sequence number + * when there is enough sequence number space left to + * handle a few more records, so the sequence number + * of the last record cannot be wrapped. + */ + hsStatus = getHSStatus(hsStatus); + if (connectionState < cs_ERROR && !isInboundDone() && + (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { + if (checkSequenceNumber(readAuthenticator, + inputRecord.contentType())) { + hsStatus = getHSStatus(null); + } + } + } // synchronized (this) + } + + return hsStatus; + } + + + // + // write/wrap side + // + + + /** + * Wraps a buffer. Does a variety of checks before grabbing + * the wrapLock, which blocks multiple wraps from occurring. + */ + @Override + public SSLEngineResult wrap(ByteBuffer [] appData, + int offset, int length, ByteBuffer netData) throws SSLException { + + EngineArgs ea = new EngineArgs(appData, offset, length, netData); + + /* + * We can be smarter about using smaller buffer sizes later. + * For now, force it to be large enough to handle any + * valid SSL/TLS record. + */ + if (netData.remaining() < EngineOutputRecord.maxRecordSize) { + return new SSLEngineResult( + Status.BUFFER_OVERFLOW, getHSStatus(null), 0, 0); + } + + try { + synchronized (wrapLock) { + return writeAppRecord(ea); + } + } catch (Exception e) { + ea.resetPos(); + + fatal(Alerts.alert_internal_error, + "problem wrapping app data", e); + return null; // make compiler happy + } finally { + /* + * Just in case something didn't reset limits properly. + */ + ea.resetLim(); + } + } + + /* + * Makes additional checks for unwrap, but this time more + * specific to this packet and the current state of the machine. + */ + private SSLEngineResult writeAppRecord(EngineArgs ea) throws IOException { + + Status status = null; + HandshakeStatus hsStatus = null; + + /* + * See if the handshaker needs to report back some SSLException. + */ + checkTaskThrown(); + + /* + * short circuit if we're closed/closing. + */ + if (writer.isOutboundDone()) { + return new SSLEngineResult(Status.CLOSED, getHSStatus(null), 0, 0); + } + + /* + * If we're still in cs_HANDSHAKE, make sure it's been + * started. + */ + synchronized (this) { + if ((connectionState == cs_HANDSHAKE) || + (connectionState == cs_START)) { + kickstartHandshake(); + + /* + * If there's no HS data available to write, we can return + * without trying to wrap anything. + */ + hsStatus = getHSStatus(null); + + if (hsStatus == HandshakeStatus.NEED_UNWRAP) { + return new SSLEngineResult(Status.OK, hsStatus, 0, 0); + } + } + } + + /* + * Grab a copy of this if it doesn't already exist, + * and we can use it several places before anything major + * happens on this side. Races aren't critical + * here. + */ + if (hsStatus == null) { + hsStatus = getHSStatus(null); + } + + /* + * If we have a task outstanding, this *MUST* be done before + * doing any more wrapping, because we could be in the middle + * of receiving a handshake message, for example, a finished + * message which would change the ciphers. + */ + if (hsStatus == HandshakeStatus.NEED_TASK) { + return new SSLEngineResult( + Status.OK, hsStatus, 0, 0); + } + + /* + * This will obtain any waiting outbound data, or will + * process the outbound appData. + */ + try { + synchronized (writeLock) { + hsStatus = writeRecord(outputRecord, ea); + } + } catch (SSLException e) { + throw e; + } catch (IOException e) { + throw new SSLException("Write problems", e); + } + + /* + * writeRecord might have reported some status. + * Now check for the remaining cases. + * + * status above should cover: NEED_WRAP/FINISHED + */ + status = (isOutboundDone() ? Status.CLOSED : Status.OK); + hsStatus = getHSStatus(hsStatus); + + return new SSLEngineResult(status, hsStatus, + ea.deltaApp(), ea.deltaNet()); + } + + /* + * Central point to write/get all of the outgoing data. + */ + private HandshakeStatus writeRecord(EngineOutputRecord eor, + EngineArgs ea) throws IOException { + + // eventually compress as well. + HandshakeStatus hsStatus = + writer.writeRecord(eor, ea, writeAuthenticator, writeCipher); + + /* + * We only need to check the sequence number state for + * non-handshaking record. + * + * Note that in order to maintain the handshake status + * properly, we check the sequence number after the last + * record writing process. As we request renegotiation + * or close the connection for wrapped sequence number + * when there is enough sequence number space left to + * handle a few more records, so the sequence number + * of the last record cannot be wrapped. + */ + hsStatus = getHSStatus(hsStatus); + if (connectionState < cs_ERROR && !isOutboundDone() && + (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) { + if (checkSequenceNumber(writeAuthenticator, eor.contentType())) { + hsStatus = getHSStatus(null); + } + } + + /* + * turn off the flag of the first application record if we really + * consumed at least byte. + */ + if (isFirstAppOutputRecord && ea.deltaApp() > 0) { + isFirstAppOutputRecord = false; + } + + return hsStatus; + } + + /* + * Need to split the payload except the following cases: + * + * 1. protocol version is TLS 1.1 or later; + * 2. bulk cipher does not use CBC mode, including null bulk cipher suites. + * 3. the payload is the first application record of a freshly + * negotiated TLS session. + * 4. the CBC protection is disabled; + * + * More details, please refer to + * EngineOutputRecord.write(EngineArgs, MAC, CipherBox). + */ + boolean needToSplitPayload(CipherBox cipher, ProtocolVersion protocol) { + return (protocol.v <= ProtocolVersion.TLS10.v) && + cipher.isCBCMode() && !isFirstAppOutputRecord && + Record.enableCBCProtection; + } + + /* + * Non-application OutputRecords go through here. + */ + void writeRecord(EngineOutputRecord eor) throws IOException { + // eventually compress as well. + writer.writeRecord(eor, writeAuthenticator, writeCipher); + + /* + * Check the sequence number state + * + * Note that in order to maintain the connection I/O + * properly, we check the sequence number after the last + * record writing process. As we request renegotiation + * or close the connection for wrapped sequence number + * when there is enough sequence number space left to + * handle a few more records, so the sequence number + * of the last record cannot be wrapped. + */ + if ((connectionState < cs_ERROR) && !isOutboundDone()) { + checkSequenceNumber(writeAuthenticator, eor.contentType()); + } + } + + // + // Close code + // + + /** + * Check the sequence number state + * + * RFC 4346 states that, "Sequence numbers are of type uint64 and + * may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS + * implementation would need to wrap a sequence number, it must + * renegotiate instead." + * + * Return true if the handshake status may be changed. + */ + private boolean checkSequenceNumber(Authenticator authenticator, byte type) + throws IOException { + + /* + * Don't bother to check the sequence number for error or + * closed connections, or NULL MAC + */ + if (connectionState >= cs_ERROR || authenticator == MAC.NULL) { + return false; + } + + /* + * Conservatively, close the connection immediately when the + * sequence number is close to overflow + */ + if (authenticator.seqNumOverflow()) { + /* + * TLS protocols do not define a error alert for sequence + * number overflow. We use handshake_failure error alert + * for handshaking and bad_record_mac for other records. + */ + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", sequence number extremely close to overflow " + + "(2^64-1 packets). Closing connection."); + } + + fatal(Alerts.alert_handshake_failure, "sequence number overflow"); + + return true; // make the compiler happy + } + + /* + * Ask for renegotiation when need to renew sequence number. + * + * Don't bother to kickstart the renegotiation when the local is + * asking for it. + */ + if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", request renegotiation " + + "to avoid sequence number overflow"); + } + + beginHandshake(); + return true; + } + + return false; + } + + /** + * Signals that no more outbound application data will be sent + * on this SSLEngine. + */ + private void closeOutboundInternal() { + + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", closeOutboundInternal()"); + } + + /* + * Already closed, ignore + */ + if (writer.isOutboundDone()) { + return; + } + + switch (connectionState) { + + /* + * If we haven't even started yet, don't bother reading inbound. + */ + case cs_START: + writer.closeOutbound(); + inboundDone = true; + break; + + case cs_ERROR: + case cs_CLOSED: + break; + + /* + * Otherwise we indicate clean termination. + */ + // case cs_HANDSHAKE: + // case cs_DATA: + // case cs_RENEGOTIATE: + default: + warning(Alerts.alert_close_notify); + writer.closeOutbound(); + break; + } + + // See comment in changeReadCiphers() + writeCipher.dispose(); + + connectionState = cs_CLOSED; + } + + @Override + synchronized public void closeOutbound() { + /* + * Dump out a close_notify to the remote side + */ + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called closeOutbound()"); + } + + closeOutboundInternal(); + } + + /** + * Returns the outbound application data closure state + */ + @Override + public boolean isOutboundDone() { + return writer.isOutboundDone(); + } + + /** + * Signals that no more inbound network data will be sent + * to this SSLEngine. + */ + private void closeInboundInternal() { + + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", closeInboundInternal()"); + } + + /* + * Already closed, ignore + */ + if (inboundDone) { + return; + } + + closeOutboundInternal(); + inboundDone = true; + + // See comment in changeReadCiphers() + readCipher.dispose(); + + connectionState = cs_CLOSED; + } + + /* + * Close the inbound side of the connection. We grab the + * lock here, and do the real work in the internal verison. + * We do check for truncation attacks. + */ + @Override + synchronized public void closeInbound() throws SSLException { + /* + * Currently closes the outbound side as well. The IETF TLS + * working group has expressed the opinion that 1/2 open + * connections are not allowed by the spec. May change + * someday in the future. + */ + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called closeInbound()"); + } + + /* + * No need to throw an Exception if we haven't even started yet. + */ + if ((connectionState != cs_START) && !recvCN) { + recvCN = true; // Only receive the Exception once + fatal(Alerts.alert_internal_error, + "Inbound closed before receiving peer's close_notify: " + + "possible truncation attack?"); + } else { + /* + * Currently, this is a no-op, but in case we change + * the close inbound code later. + */ + closeInboundInternal(); + } + } + + /** + * Returns the network inbound data closure state + */ + @Override + synchronized public boolean isInboundDone() { + return inboundDone; + } + + + // + // Misc stuff + // + + + /** + * Returns the current SSLSession for this + * SSLEngine + *

    + * These can be long lived, and frequently correspond to an + * entire login session for some user. + */ + @Override + synchronized public SSLSession getSession() { + return sess; + } + + @Override + synchronized public SSLSession getHandshakeSession() { + return handshakeSession; + } + + synchronized void setHandshakeSession(SSLSessionImpl session) { + handshakeSession = session; + } + + /** + * Returns a delegated Runnable task for + * this SSLEngine. + */ + @Override + synchronized public Runnable getDelegatedTask() { + if (handshaker != null) { + return handshaker.getTask(); + } + return null; + } + + + // + // EXCEPTION AND ALERT HANDLING + // + + /* + * Send a warning alert. + */ + void warning(byte description) { + sendAlert(Alerts.alert_warning, description); + } + + synchronized void fatal(byte description, String diagnostic) + throws SSLException { + fatal(description, diagnostic, null); + } + + synchronized void fatal(byte description, Throwable cause) + throws SSLException { + fatal(description, null, cause); + } + + /* + * We've got a fatal error here, so start the shutdown process. + * + * Because of the way the code was written, we have some code + * calling fatal directly when the "description" is known + * and some throwing Exceptions which are then caught by higher + * levels which then call here. This code needs to determine + * if one of the lower levels has already started the process. + * + * We won't worry about Error's, if we have one of those, + * we're in worse trouble. Note: the networking code doesn't + * deal with Errors either. + */ + synchronized void fatal(byte description, String diagnostic, + Throwable cause) throws SSLException { + + /* + * If we have no further information, make a general-purpose + * message for folks to see. We generally have one or the other. + */ + if (diagnostic == null) { + diagnostic = "General SSLEngine problem"; + } + if (cause == null) { + cause = Alerts.getSSLException(description, cause, diagnostic); + } + + /* + * If we've already shutdown because of an error, + * there is nothing we can do except rethrow the exception. + * + * Most exceptions seen here will be SSLExceptions. + * We may find the occasional Exception which hasn't been + * converted to a SSLException, so we'll do it here. + */ + if (closeReason != null) { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", fatal: engine already closed. Rethrowing " + + cause.toString()); + } + if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else if (cause instanceof SSLException) { + throw (SSLException)cause; + } else if (cause instanceof Exception) { + throw new SSLException("fatal SSLEngine condition", cause); + } + } + + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", fatal error: " + description + + ": " + diagnostic + "\n" + cause.toString()); + } + + /* + * Ok, this engine's going down. + */ + int oldState = connectionState; + connectionState = cs_ERROR; + + inboundDone = true; + + sess.invalidate(); + if (handshakeSession != null) { + handshakeSession.invalidate(); + } + + /* + * If we haven't even started handshaking yet, no need + * to generate the fatal close alert. + */ + if (oldState != cs_START) { + sendAlert(Alerts.alert_fatal, description); + } + + if (cause instanceof SSLException) { // only true if != null + closeReason = (SSLException)cause; + } else { + /* + * Including RuntimeExceptions, but we'll throw those + * down below. The closeReason isn't used again, + * except for null checks. + */ + closeReason = + Alerts.getSSLException(description, cause, diagnostic); + } + + writer.closeOutbound(); + + connectionState = cs_CLOSED; + + // See comment in changeReadCiphers() + readCipher.dispose(); + writeCipher.dispose(); + + if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else { + throw closeReason; + } + } + + /* + * Process an incoming alert ... caller must already have synchronized + * access to "this". + */ + private void recvAlert() throws IOException { + byte level = (byte)inputRecord.read(); + byte description = (byte)inputRecord.read(); + if (description == -1) { // check for short message + fatal(Alerts.alert_illegal_parameter, "Short alert message"); + } + + if (debug != null && (Debug.isOn("record") || + Debug.isOn("handshake"))) { + synchronized (System.out) { + System.out.print(Thread.currentThread().getName()); + System.out.print(", RECV " + protocolVersion + " ALERT: "); + if (level == Alerts.alert_fatal) { + System.out.print("fatal, "); + } else if (level == Alerts.alert_warning) { + System.out.print("warning, "); + } else { + System.out.print(", "); + } + System.out.println(Alerts.alertDescription(description)); + } + } + + if (level == Alerts.alert_warning) { + if (description == Alerts.alert_close_notify) { + if (connectionState == cs_HANDSHAKE) { + fatal(Alerts.alert_unexpected_message, + "Received close_notify during handshake"); + } else { + recvCN = true; + closeInboundInternal(); // reply to close + } + } else { + + // + // The other legal warnings relate to certificates, + // e.g. no_certificate, bad_certificate, etc; these + // are important to the handshaking code, which can + // also handle illegal protocol alerts if needed. + // + if (handshaker != null) { + handshaker.handshakeAlert(description); + } + } + } else { // fatal or unknown level + String reason = "Received fatal alert: " + + Alerts.alertDescription(description); + if (closeReason == null) { + closeReason = Alerts.getSSLException(description, reason); + } + fatal(Alerts.alert_unexpected_message, reason); + } + } + + + /* + * Emit alerts. Caller must have synchronized with "this". + */ + private void sendAlert(byte level, byte description) { + // the connectionState cannot be cs_START + if (connectionState >= cs_CLOSED) { + return; + } + + // For initial handshaking, don't send alert message to peer if + // handshaker has not started. + if (connectionState == cs_HANDSHAKE && + (handshaker == null || !handshaker.started())) { + return; + } + + EngineOutputRecord r = new EngineOutputRecord(Record.ct_alert, this); + r.setVersion(protocolVersion); + + boolean useDebug = debug != null && Debug.isOn("ssl"); + if (useDebug) { + synchronized (System.out) { + System.out.print(Thread.currentThread().getName()); + System.out.print(", SEND " + protocolVersion + " ALERT: "); + if (level == Alerts.alert_fatal) { + System.out.print("fatal, "); + } else if (level == Alerts.alert_warning) { + System.out.print("warning, "); + } else { + System.out.print(", "); + } + System.out.println("description = " + + Alerts.alertDescription(description)); + } + } + + r.write(level); + r.write(description); + try { + writeRecord(r); + } catch (IOException e) { + if (useDebug) { + System.out.println(Thread.currentThread().getName() + + ", Exception sending alert: " + e); + } + } + } + + + // + // VARIOUS OTHER METHODS (COMMON TO SSLSocket) + // + + + /** + * Controls whether new connections may cause creation of new SSL + * sessions. + * + * As long as handshaking has not started, we can change + * whether we enable session creations. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setEnableSessionCreation(boolean flag) { + enableSessionCreation = flag; + + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnableSessionCreation(enableSessionCreation); + } + } + + /** + * Returns true if new connections may cause creation of new SSL + * sessions. + */ + @Override + synchronized public boolean getEnableSessionCreation() { + return enableSessionCreation; + } + + + /** + * Sets the flag controlling whether a server mode engine + * *REQUIRES* SSL client authentication. + * + * As long as handshaking has not started, we can change + * whether client authentication is needed. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setNeedClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); + + if ((handshaker != null) && + (handshaker instanceof ServerHandshaker) && + !handshaker.activated()) { + ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + } + } + + @Override + synchronized public boolean getNeedClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_required); + } + + /** + * Sets the flag controlling whether a server mode engine + * *REQUESTS* SSL client authentication. + * + * As long as handshaking has not started, we can change + * whether client authentication is requested. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setWantClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); + + if ((handshaker != null) && + (handshaker instanceof ServerHandshaker) && + !handshaker.activated()) { + ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + } + } + + @Override + synchronized public boolean getWantClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_requested); + } + + + /** + * Sets the flag controlling whether the engine is in SSL + * client or server mode. Must be called before any SSL + * traffic has started. + */ + @Override + @SuppressWarnings("fallthrough") + synchronized public void setUseClientMode(boolean flag) { + switch (connectionState) { + + case cs_START: + /* + * If we need to change the engine mode and the enabled + * protocols haven't specifically been set by the user, + * change them to the corresponding default ones. + */ + if (roleIsServer != (!flag) && + sslContext.isDefaultProtocolList(enabledProtocols)) { + enabledProtocols = sslContext.getDefaultProtocolList(!flag); + } + + roleIsServer = !flag; + serverModeSet = true; + break; + + case cs_HANDSHAKE: + /* + * If we have a handshaker, but haven't started + * SSL traffic, we can throw away our current + * handshaker, and start from scratch. Don't + * need to call doneConnect() again, we already + * have the streams. + */ + assert(handshaker != null); + if (!handshaker.activated()) { + /* + * If we need to change the engine mode and the enabled + * protocols haven't specifically been set by the user, + * change them to the corresponding default ones. + */ + if (roleIsServer != (!flag) && + sslContext.isDefaultProtocolList(enabledProtocols)) { + enabledProtocols = sslContext.getDefaultProtocolList(!flag); + } + + roleIsServer = !flag; + connectionState = cs_START; + initHandshaker(); + break; + } + + // If handshake has started, that's an error. Fall through... + + default: + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", setUseClientMode() invoked in state = " + + connectionState); + } + + /* + * We can let them continue if they catch this correctly, + * we don't need to shut this down. + */ + throw new IllegalArgumentException( + "Cannot change mode after SSL traffic has started"); + } + } + + @Override + synchronized public boolean getUseClientMode() { + return !roleIsServer; + } + + + /** + * Returns the names of the cipher suites which could be enabled for use + * on an SSL connection. Normally, only a subset of these will actually + * be enabled by default, since this list may include cipher suites which + * do not support the mutual authentication of servers and clients, or + * which do not protect data confidentiality. Servers may also need + * certain kinds of certificates to use certain cipher suites. + * + * @return an array of cipher suite names + */ + @Override + public String[] getSupportedCipherSuites() { + return sslContext.getSupportedCipherSuiteList().toStringArray(); + } + + /** + * Controls which particular cipher suites are enabled for use on + * this connection. The cipher suites must have been listed by + * getCipherSuites() as being supported. Even if a suite has been + * enabled, it might never be used if no peer supports it or the + * requisite certificates (and private keys) are not available. + * + * @param suites Names of all the cipher suites to enable. + */ + @Override + synchronized public void setEnabledCipherSuites(String[] suites) { + enabledCipherSuites = new CipherSuiteList(suites); + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnabledCipherSuites(enabledCipherSuites); + } + } + + /** + * Returns the names of the SSL cipher suites which are currently enabled + * for use on this connection. When an SSL engine is first created, + * all enabled cipher suites (a) protect data confidentiality, + * by traffic encryption, and (b) can mutually authenticate + * both clients and servers. Thus, in some environments, this value + * might be empty. + * + * @return an array of cipher suite names + */ + @Override + synchronized public String[] getEnabledCipherSuites() { + return enabledCipherSuites.toStringArray(); + } + + + /** + * Returns the protocols that are supported by this implementation. + * A subset of the supported protocols may be enabled for this connection + * @return an array of protocol names. + */ + @Override + public String[] getSupportedProtocols() { + return sslContext.getSuportedProtocolList().toStringArray(); + } + + /** + * Controls which protocols are enabled for use on + * this connection. The protocols must have been listed by + * getSupportedProtocols() as being supported. + * + * @param protocols protocols to enable. + * @exception IllegalArgumentException when one of the protocols + * named by the parameter is not supported. + */ + @Override + synchronized public void setEnabledProtocols(String[] protocols) { + enabledProtocols = new ProtocolList(protocols); + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnabledProtocols(enabledProtocols); + } + } + + @Override + synchronized public String[] getEnabledProtocols() { + return enabledProtocols.toStringArray(); + } + + /** + * Returns the SSLParameters in effect for this SSLEngine. + */ + @Override + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + params.setSNIMatchers(sniMatchers); + params.setServerNames(serverNames); + params.setUseCipherSuitesOrder(preferLocalCipherSuites); + params.setApplicationProtocols(applicationProtocols); + + return params; + } + + /** + * Applies SSLParameters to this engine. + */ + @Override + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + preferLocalCipherSuites = params.getUseCipherSuitesOrder(); + + List sniNames = params.getServerNames(); + if (sniNames != null) { + serverNames = sniNames; + } + + Collection matchers = params.getSNIMatchers(); + if (matchers != null) { + sniMatchers = matchers; + } + applicationProtocols = params.getApplicationProtocols(); + + if ((handshaker != null) && !handshaker.started()) { + handshaker.setIdentificationProtocol(identificationProtocol); + handshaker.setAlgorithmConstraints(algorithmConstraints); + applicationProtocols = params.getApplicationProtocols(); + if (roleIsServer) { + handshaker.setSNIMatchers(sniMatchers); + handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); + } else { + handshaker.setSNIServerNames(serverNames); + } + } + } + + /* + * Returns a boolean indicating whether the ChangeCipherSpec message + * has been received for this handshake. + */ + boolean receivedChangeCipherSpec() { + return receivedCCS; + } + + @Override + public synchronized String getApplicationProtocol() { + return applicationProtocol; + } + + @Override + public synchronized String getHandshakeApplicationProtocol() { + if ((handshaker != null) && handshaker.started()) { + return handshaker.getHandshakeApplicationProtocol(); + } + return null; + } + + @Override + public synchronized void setHandshakeApplicationProtocolSelector( + BiFunction, String> selector) { + applicationProtocolSelector = selector; + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setApplicationProtocolSelectorSSLEngine(selector); + } + } + + @Override + public synchronized BiFunction, String> + getHandshakeApplicationProtocolSelector() { + return this.applicationProtocolSelector; + } + + /** + * Returns a printable representation of this end of the connection. + */ + @Override + public String toString() { + StringBuilder retval = new StringBuilder(80); + + retval.append(Integer.toHexString(hashCode())); + retval.append("["); + retval.append("SSLEngine[hostname="); + String host = getPeerHost(); + retval.append((host == null) ? "null" : host); + retval.append(" port="); + retval.append(Integer.toString(getPeerPort())); + retval.append("] "); + retval.append(getSession().getCipherSuite()); + retval.append("]"); + + return retval.toString(); + } +} diff --git a/src/sun/security/ssl/SSLServerSocketFactoryImpl.java b/src/sun/security/ssl/SSLServerSocketFactoryImpl.java new file mode 100644 index 00000000..76bd264a --- /dev/null +++ b/src/sun/security/ssl/SSLServerSocketFactoryImpl.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; + +import javax.net.ssl.SSLServerSocketFactory; + +/** + * This class creates SSL server sockets. + * + * @author David Brownell + */ +final +public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory +{ + private static final int DEFAULT_BACKLOG = 50; + private SSLContextImpl context; + + + /** + * Constructor used to instantiate the default factory. This method is + * only called if the old "ssl.ServerSocketFactory.provider" property in the + * java.security file is set. + */ + public SSLServerSocketFactoryImpl() throws Exception { + this.context = SSLContextImpl.DefaultSSLContext.getDefaultImpl(); + } + + /** + * Called from SSLContextImpl's getSSLServerSocketFactory(). + */ + SSLServerSocketFactoryImpl (SSLContextImpl context) + { + this.context = context; + } + + /** + * Returns an unbound server socket. + * + * @return the unbound socket + * @throws IOException if the socket cannot be created + * @see java.net.Socket#bind(java.net.SocketAddress) + */ + @Override + public ServerSocket createServerSocket() throws IOException { + return new SSLServerSocketImpl(context); + } + + @Override + public ServerSocket createServerSocket (int port) + throws IOException + { + return new SSLServerSocketImpl (port, DEFAULT_BACKLOG, context); + } + + + @Override + public ServerSocket createServerSocket (int port, int backlog) + throws IOException + { + return new SSLServerSocketImpl (port, backlog, context); + } + + @Override + public ServerSocket + createServerSocket (int port, int backlog, InetAddress ifAddress) + throws IOException + { + return new SSLServerSocketImpl (port, backlog, ifAddress, context); + } + + /** + * Returns the subset of the supported cipher suites which are + * enabled by default. These cipher suites all provide a minimum + * quality of service whereby the server authenticates itself + * (preventing person-in-the-middle attacks) and where traffic + * is encrypted to provide confidentiality. + */ + @Override + public String[] getDefaultCipherSuites() { + return context.getDefaultCipherSuiteList(true).toStringArray(); + } + + /** + * Returns the names of the cipher suites which could be enabled for use + * on an SSL connection. Normally, only a subset of these will actually + * be enabled by default, since this list may include cipher suites which + * do not support the mutual authentication of servers and clients, or + * which do not protect data confidentiality. Servers may also need + * certain kinds of certificates to use certain cipher suites. + * + * @return an array of cipher suite names + */ + @Override + public String[] getSupportedCipherSuites() { + return context.getSupportedCipherSuiteList().toStringArray(); + } + +} diff --git a/src/sun/security/ssl/SSLServerSocketImpl.java b/src/sun/security/ssl/SSLServerSocketImpl.java new file mode 100644 index 00000000..d5fb43d5 --- /dev/null +++ b/src/sun/security/ssl/SSLServerSocketImpl.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import java.security.AlgorithmConstraints; + +import java.util.*; + +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SNIMatcher; + + +/** + * This class provides a simple way for servers to support conventional + * use of the Secure Sockets Layer (SSL). Application code uses an + * SSLServerSocketImpl exactly like it uses a regular TCP ServerSocket; the + * difference is that the connections established are secured using SSL. + * + *

    Also, the constructors take an explicit authentication context + * parameter, giving flexibility with respect to how the server socket + * authenticates itself. That policy flexibility is not exposed through + * the standard SSLServerSocketFactory API. + * + *

    System security defaults prevent server sockets from accepting + * connections if they the authentication context has not been given + * a certificate chain and its matching private key. If the clients + * of your application support "anonymous" cipher suites, you may be + * able to configure a server socket to accept those suites. + * + * @see SSLSocketImpl + * @see SSLServerSocketFactoryImpl + * + * @author David Brownell + */ +final +class SSLServerSocketImpl extends SSLServerSocket +{ + private SSLContextImpl sslContext; + + /* Do newly accepted connections require clients to authenticate? */ + private byte doClientAuth = SSLEngineImpl.clauth_none; + + /* Do new connections created here use the "server" mode of SSL? */ + private boolean useServerMode = true; + + /* Can new connections created establish new sessions? */ + private boolean enableSessionCreation = true; + + /* what cipher suites to use by default */ + private CipherSuiteList enabledCipherSuites = null; + + /* which protocol to use by default */ + private ProtocolList enabledProtocols = null; + + // the endpoint identification protocol to use by default + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + + // The server name indication + Collection sniMatchers = + Collections.emptyList(); + + // Configured application protocol values + String[] applicationProtocols = new String[0]; + + /* + * Whether local cipher suites preference in server side should be + * honored during handshaking? + */ + private boolean preferLocalCipherSuites = false; + + /** + * Create an SSL server socket on a port, using a non-default + * authentication context and a specified connection backlog. + * + * @param port the port on which to listen + * @param backlog how many connections may be pending before + * the system should start rejecting new requests + * @param context authentication context for this server + */ + SSLServerSocketImpl(int port, int backlog, SSLContextImpl context) + throws IOException, SSLException + { + super(port, backlog); + initServer(context); + } + + + /** + * Create an SSL server socket on a port, using a specified + * authentication context and a specified backlog of connections + * as well as a particular specified network interface. This + * constructor is used on multihomed hosts, such as those used + * for firewalls or as routers, to control through which interface + * a network service is provided. + * + * @param port the port on which to listen + * @param backlog how many connections may be pending before + * the system should start rejecting new requests + * @param address the address of the network interface through + * which connections will be accepted + * @param context authentication context for this server + */ + SSLServerSocketImpl( + int port, + int backlog, + InetAddress address, + SSLContextImpl context) + throws IOException + { + super(port, backlog, address); + initServer(context); + } + + + /** + * Creates an unbound server socket. + */ + SSLServerSocketImpl(SSLContextImpl context) throws IOException { + super(); + initServer(context); + } + + + /** + * Initializes the server socket. + */ + private void initServer(SSLContextImpl context) throws SSLException { + if (context == null) { + throw new SSLException("No Authentication context given"); + } + sslContext = context; + enabledCipherSuites = sslContext.getDefaultCipherSuiteList(true); + enabledProtocols = sslContext.getDefaultProtocolList(true); + } + + /** + * Returns the names of the cipher suites which could be enabled for use + * on an SSL connection. Normally, only a subset of these will actually + * be enabled by default, since this list may include cipher suites which + * do not support the mutual authentication of servers and clients, or + * which do not protect data confidentiality. Servers may also need + * certain kinds of certificates to use certain cipher suites. + * + * @return an array of cipher suite names + */ + @Override + public String[] getSupportedCipherSuites() { + return sslContext.getSupportedCipherSuiteList().toStringArray(); + } + + /** + * Returns the list of cipher suites which are currently enabled + * for use by newly accepted connections. A null return indicates + * that the system defaults are in effect. + */ + @Override + synchronized public String[] getEnabledCipherSuites() { + return enabledCipherSuites.toStringArray(); + } + + /** + * Controls which particular SSL cipher suites are enabled for use + * by accepted connections. + * + * @param suites Names of all the cipher suites to enable; null + * means to accept system defaults. + */ + @Override + synchronized public void setEnabledCipherSuites(String[] suites) { + enabledCipherSuites = new CipherSuiteList(suites); + } + + @Override + public String[] getSupportedProtocols() { + return sslContext.getSuportedProtocolList().toStringArray(); + } + + /** + * Controls which protocols are enabled for use. + * The protocols must have been listed by + * getSupportedProtocols() as being supported. + * + * @param protocols protocols to enable. + * @exception IllegalArgumentException when one of the protocols + * named by the parameter is not supported. + */ + @Override + synchronized public void setEnabledProtocols(String[] protocols) { + enabledProtocols = new ProtocolList(protocols); + } + + @Override + synchronized public String[] getEnabledProtocols() { + return enabledProtocols.toStringArray(); + } + + /** + * Controls whether the connections which are accepted must include + * client authentication. + */ + @Override + public void setNeedClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); + } + + @Override + public boolean getNeedClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_required); + } + + /** + * Controls whether the connections which are accepted should request + * client authentication. + */ + @Override + public void setWantClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); + } + + @Override + public boolean getWantClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_requested); + } + + /** + * Makes the returned sockets act in SSL "client" mode, not the usual + * server mode. The canonical example of why this is needed is for + * FTP clients, which accept connections from servers and should be + * rejoining the already-negotiated SSL connection. + */ + @Override + public void setUseClientMode(boolean flag) { + /* + * If we need to change the socket mode and the enabled + * protocols haven't specifically been set by the user, + * change them to the corresponding default ones. + */ + if (useServerMode != (!flag) && + sslContext.isDefaultProtocolList(enabledProtocols)) { + enabledProtocols = sslContext.getDefaultProtocolList(!flag); + } + + useServerMode = !flag; + } + + @Override + public boolean getUseClientMode() { + return !useServerMode; + } + + + /** + * Controls whether new connections may cause creation of new SSL + * sessions. + */ + @Override + public void setEnableSessionCreation(boolean flag) { + enableSessionCreation = flag; + } + + /** + * Returns true if new connections may cause creation of new SSL + * sessions. + */ + @Override + public boolean getEnableSessionCreation() { + return enableSessionCreation; + } + + /** + * Returns the SSLParameters in effect for newly accepted connections. + */ + @Override + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + params.setSNIMatchers(sniMatchers); + params.setUseCipherSuitesOrder(preferLocalCipherSuites); + params.setApplicationProtocols(applicationProtocols); + + return params; + } + + /** + * Applies SSLParameters to newly accepted connections. + */ + @Override + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + preferLocalCipherSuites = params.getUseCipherSuitesOrder(); + Collection matchers = params.getSNIMatchers(); + if (matchers != null) { + sniMatchers = params.getSNIMatchers(); + } + applicationProtocols = params.getApplicationProtocols(); + } + + /** + * Accept a new SSL connection. This server identifies itself with + * information provided in the authentication context which was + * presented during construction. + */ + @Override + public Socket accept() throws IOException { + SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode, + enabledCipherSuites, doClientAuth, enableSessionCreation, + enabledProtocols, identificationProtocol, algorithmConstraints, + sniMatchers, preferLocalCipherSuites, applicationProtocols); + + implAccept(s); + s.doneConnect(); + return s; + } + + /** + * Provides a brief description of this SSL socket. + */ + @Override + public String toString() { + return "[SSL: "+ super.toString() + "]"; + } +} diff --git a/src/sun/security/ssl/SSLSessionContextImpl.java b/src/sun/security/ssl/SSLSessionContextImpl.java new file mode 100644 index 00000000..51fac9bc --- /dev/null +++ b/src/sun/security/ssl/SSLSessionContextImpl.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.util.Enumeration; +import java.util.Vector; +import java.util.Locale; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionContext; + +import sun.security.util.Cache; + + +final class SSLSessionContextImpl implements SSLSessionContext { + private Cache sessionCache; + // session cache, session id as key + private Cache sessionHostPortCache; + // session cache, "host:port" as key + private int cacheLimit; // the max cache size + private int timeout; // timeout in seconds + + // package private + SSLSessionContextImpl() { + cacheLimit = getDefaultCacheLimit(); // default cache size + timeout = 86400; // default, 24 hours + + // use soft reference + sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout); + sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout); + } + + /** + * Returns the SSLSession bound to the specified session id. + */ + @Override + public SSLSession getSession(byte[] sessionId) { + if (sessionId == null) { + throw new NullPointerException("session id cannot be null"); + } + + SSLSessionImpl sess = sessionCache.get(new SessionId(sessionId)); + if (!isTimedout(sess)) { + return sess; + } + + return null; + } + + /** + * Returns an enumeration of the active SSL sessions. + */ + @Override + public Enumeration getIds() { + SessionCacheVisitor scVisitor = new SessionCacheVisitor(); + sessionCache.accept(scVisitor); + + return scVisitor.getSessionIds(); + } + + /** + * Sets the timeout limit for cached SSLSession objects + * + * Note that after reset the timeout, the cached session before + * should be timed within the shorter one of the old timeout and the + * new timeout. + */ + @Override + public void setSessionTimeout(int seconds) + throws IllegalArgumentException { + if (seconds < 0) { + throw new IllegalArgumentException(); + } + + if (timeout != seconds) { + sessionCache.setTimeout(seconds); + sessionHostPortCache.setTimeout(seconds); + timeout = seconds; + } + } + + /** + * Gets the timeout limit for cached SSLSession objects + */ + @Override + public int getSessionTimeout() { + return timeout; + } + + /** + * Sets the size of the cache used for storing + * SSLSession objects. + */ + @Override + public void setSessionCacheSize(int size) + throws IllegalArgumentException { + if (size < 0) + throw new IllegalArgumentException(); + + if (cacheLimit != size) { + sessionCache.setCapacity(size); + sessionHostPortCache.setCapacity(size); + cacheLimit = size; + } + } + + /** + * Gets the size of the cache used for storing + * SSLSession objects. + */ + @Override + public int getSessionCacheSize() { + return cacheLimit; + } + + + // package-private method, used ONLY by ServerHandshaker + SSLSessionImpl get(byte[] id) { + return (SSLSessionImpl)getSession(id); + } + + // package-private method, used ONLY by ClientHandshaker + SSLSessionImpl get(String hostname, int port) { + /* + * If no session caching info is available, we won't + * get one, so exit before doing a lookup. + */ + if (hostname == null && port == -1) { + return null; + } + + SSLSessionImpl sess = sessionHostPortCache.get(getKey(hostname, port)); + if (!isTimedout(sess)) { + return sess; + } + + return null; + } + + private String getKey(String hostname, int port) { + return (hostname + ":" + + String.valueOf(port)).toLowerCase(Locale.ENGLISH); + } + + // cache a SSLSession + // + // In SunJSSE implementation, a session is created while getting a + // client hello or a server hello message, and cached while the + // handshaking finished. + // Here we time the session from the time it cached instead of the + // time it created, which is a little longer than the expected. So + // please do check isTimedout() while getting entry from the cache. + void put(SSLSessionImpl s) { + sessionCache.put(s.getSessionId(), s); + + // If no hostname/port info is available, don't add this one. + if ((s.getPeerHost() != null) && (s.getPeerPort() != -1)) { + sessionHostPortCache.put( + getKey(s.getPeerHost(), s.getPeerPort()), s); + } + + s.setContext(this); + } + + // package-private method, remove a cached SSLSession + void remove(SessionId key) { + SSLSessionImpl s = sessionCache.get(key); + if (s != null) { + sessionCache.remove(key); + sessionHostPortCache.remove( + getKey(s.getPeerHost(), s.getPeerPort())); + } + } + + private int getDefaultCacheLimit() { + int cacheLimit = 0; + try { + String s = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + @Override + public String run() { + return System.getProperty( + "javax.net.ssl.sessionCacheSize"); + } + }); + cacheLimit = (s != null) ? Integer.valueOf(s).intValue() : 0; + } catch (Exception e) { + } + + return (cacheLimit > 0) ? cacheLimit : 0; + } + + boolean isTimedout(SSLSession sess) { + if (timeout == 0) { + return false; + } + + if ((sess != null) && ((sess.getCreationTime() + timeout * 1000L) + <= (System.currentTimeMillis()))) { + sess.invalidate(); + return true; + } + + return false; + } + + final class SessionCacheVisitor + implements Cache.CacheVisitor { + Vector ids = null; + + // public void visit(java.util.Map map) {} + @Override + public void visit(java.util.Map map) { + ids = new Vector<>(map.size()); + + for (SessionId key : map.keySet()) { + SSLSessionImpl value = map.get(key); + if (!isTimedout(value)) { + ids.addElement(key.getId()); + } + } + } + + public Enumeration getSessionIds() { + return ids != null ? ids.elements() : + new Vector().elements(); + } + } + +} diff --git a/src/sun/security/ssl/SSLSessionImpl.java b/src/sun/security/ssl/SSLSessionImpl.java new file mode 100644 index 00000000..363d9c7c --- /dev/null +++ b/src/sun/security/ssl/SSLSessionImpl.java @@ -0,0 +1,901 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.net.*; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateEncodingException; + +import javax.crypto.SecretKey; + +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.SSLSessionBindingListener; +import javax.net.ssl.SSLSessionBindingEvent; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLPermission; +import javax.net.ssl.ExtendedSSLSession; +import javax.net.ssl.SNIServerName; + +import static sun.security.ssl.CipherSuite.KeyExchange.*; + +/** + * Implements the SSL session interface, and exposes the session context + * which is maintained by SSL servers. + * + *

    Servers have the ability to manage the sessions associated with + * their authentication context(s). They can do this by enumerating the + * IDs of the sessions which are cached, examining those sessions, and then + * perhaps invalidating a given session so that it can't be used again. + * If servers do not explicitly manage the cache, sessions will linger + * until memory is low enough that the runtime environment purges cache + * entries automatically to reclaim space. + * + *

    The only reason this class is not package-private is that + * there's no other public way to get at the server session context which + * is associated with any given authentication context. + * + * @author David Brownell + */ +final class SSLSessionImpl extends ExtendedSSLSession { + + /* + * we only really need a single null session + */ + static final SSLSessionImpl nullSession = new SSLSessionImpl(); + + // compression methods + private static final byte compression_null = 0; + + /* + * The state of a single session, as described in section 7.1 + * of the SSLv3 spec. + */ + private final ProtocolVersion protocolVersion; + private final SessionId sessionId; + private X509Certificate[] peerCerts; + private byte compressionMethod; + private CipherSuite cipherSuite; + private SecretKey masterSecret; + + /* + * Information not part of the SSLv3 protocol spec, but used + * to support session management policies. + */ + private final long creationTime = System.currentTimeMillis(); + private long lastUsedTime = 0; + private final String host; + private final int port; + private SSLSessionContextImpl context; + private int sessionCount; + private boolean invalidated; + private X509Certificate[] localCerts; + private PrivateKey localPrivateKey; + private String[] localSupportedSignAlgs; + private String[] peerSupportedSignAlgs; + private List requestedServerNames; + + + // Principals for non-certificate based cipher suites + private Principal peerPrincipal; + private Principal localPrincipal; + + /* + * Is the session currently re-established with a session-resumption + * abbreviated initial handshake? + * + * Note that currently we only set this variable in client side. + */ + private boolean isSessionResumption = false; + + /* + * We count session creations, eventually for statistical data but + * also since counters make shorter debugging IDs than the big ones + * we use in the protocol for uniqueness-over-time. + */ + private static volatile int counter = 0; + + /* + * Use of session caches is globally enabled/disabled. + */ + private static boolean defaultRejoinable = true; + + /* Class and subclass dynamic debugging support */ + private static final Debug debug = Debug.getInstance("ssl"); + + /* + * Create a new non-rejoinable session, using the default (null) + * cipher spec. This constructor returns a session which could + * be used either by a client or by a server, as a connection is + * first opened and before handshaking begins. + */ + private SSLSessionImpl() { + this(ProtocolVersion.NONE, CipherSuite.C_NULL, null, + new SessionId(false, null), null, -1); + } + + /* + * Create a new session, using a given cipher spec. This will + * be rejoinable if session caching is enabled; the constructor + * is intended mostly for use by serves. + */ + SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, + Collection algorithms, + SecureRandom generator, String host, int port) { + this(protocolVersion, cipherSuite, algorithms, + new SessionId(defaultRejoinable, generator), host, port); + } + + /* + * Record a new session, using a given cipher spec and session ID. + */ + SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, + Collection algorithms, + SessionId id, String host, int port) { + this.protocolVersion = protocolVersion; + sessionId = id; + peerCerts = null; + compressionMethod = compression_null; + this.cipherSuite = cipherSuite; + masterSecret = null; + this.host = host; + this.port = port; + sessionCount = ++counter; + localSupportedSignAlgs = + SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); + + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Initialized: " + this); + } + } + + void setMasterSecret(SecretKey secret) { + if (masterSecret == null) { + masterSecret = secret; + } else { + throw new RuntimeException("setMasterSecret() error"); + } + } + + /** + * Returns the master secret ... treat with extreme caution! + */ + SecretKey getMasterSecret() { + return masterSecret; + } + + void setPeerCertificates(X509Certificate[] peer) { + if (peerCerts == null) { + peerCerts = peer; + } + } + + void setLocalCertificates(X509Certificate[] local) { + localCerts = local; + } + + void setLocalPrivateKey(PrivateKey privateKey) { + localPrivateKey = privateKey; + } + + void setPeerSupportedSignatureAlgorithms( + Collection algorithms) { + peerSupportedSignAlgs = + SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); + } + + void setRequestedServerNames(List requestedServerNames) { + this.requestedServerNames = new ArrayList<>(requestedServerNames); + } + + /** + * Set the peer principal. + */ + void setPeerPrincipal(Principal principal) { + if (peerPrincipal == null) { + peerPrincipal = principal; + } + } + + /** + * Set the local principal. + */ + void setLocalPrincipal(Principal principal) { + localPrincipal = principal; + } + + /** + * Returns true iff this session may be resumed ... sessions are + * usually resumable. Security policies may suggest otherwise, + * for example sessions that haven't been used for a while (say, + * a working day) won't be resumable, and sessions might have a + * maximum lifetime in any case. + */ + boolean isRejoinable() { + return sessionId != null && sessionId.length() != 0 && + !invalidated && isLocalAuthenticationValid(); + } + + @Override + public synchronized boolean isValid() { + return isRejoinable(); + } + + /** + * Check if the authentication used when establishing this session + * is still valid. Returns true if no authentication was used + */ + boolean isLocalAuthenticationValid() { + if (localPrivateKey != null) { + try { + // if the private key is no longer valid, getAlgorithm() + // should throw an exception + // (e.g. Smartcard has been removed from the reader) + localPrivateKey.getAlgorithm(); + } catch (Exception e) { + invalidate(); + return false; + } + } + return true; + } + + /** + * Returns the ID for this session. The ID is fixed for the + * duration of the session; neither it, nor its value, changes. + */ + @Override + public byte[] getId() { + return sessionId.getId(); + } + + /** + * For server sessions, this returns the set of sessions which + * are currently valid in this process. For client sessions, + * this returns null. + */ + @Override + public SSLSessionContext getSessionContext() { + /* + * An interim security policy until we can do something + * more specific in 1.2. Only allow trusted code (code which + * can set system properties) to get an + * SSLSessionContext. This is to limit the ability of code to + * look up specific sessions or enumerate over them. Otherwise, + * code can only get session objects from successful SSL + * connections which implies that they must have had permission + * to make the network connection in the first place. + */ + SecurityManager sm; + if ((sm = System.getSecurityManager()) != null) { + sm.checkPermission(new SSLPermission("getSSLSessionContext")); + } + + return context; + } + + + SessionId getSessionId() { + return sessionId; + } + + + /** + * Returns the cipher spec in use on this session + */ + CipherSuite getSuite() { + return cipherSuite; + } + + /** + * Resets the cipher spec in use on this session + */ + void setSuite(CipherSuite suite) { + cipherSuite = suite; + + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Negotiating: " + this); + } + } + + /** + * Return true if the session is currently re-established with a + * session-resumption abbreviated initial handshake. + */ + boolean isSessionResumption() { + return isSessionResumption; + } + + /** + * Resets whether the session is re-established with a session-resumption + * abbreviated initial handshake. + */ + void setAsSessionResumption(boolean flag) { + isSessionResumption = flag; + } + + /** + * Returns the name of the cipher suite in use on this session + */ + @Override + public String getCipherSuite() { + return getSuite().name; + } + + ProtocolVersion getProtocolVersion() { + return protocolVersion; + } + + /** + * Returns the standard name of the protocol in use on this session + */ + @Override + public String getProtocol() { + return getProtocolVersion().name; + } + + /** + * Returns the compression technique used in this session + */ + byte getCompression() { + return compressionMethod; + } + + /** + * Returns the hashcode for this session + */ + @Override + public int hashCode() { + return sessionId.hashCode(); + } + + + /** + * Returns true if sessions have same ids, false otherwise. + */ + @Override + public boolean equals(Object obj) { + + if (obj == this) { + return true; + } + + if (obj instanceof SSLSessionImpl) { + SSLSessionImpl sess = (SSLSessionImpl) obj; + return (sessionId != null) && (sessionId.equals( + sess.getSessionId())); + } + + return false; + } + + + /** + * Return the cert chain presented by the peer in the + * java.security.cert format. + * Note: This method can be used only when using certificate-based + * cipher suites; using it with non-certificate-based cipher suites, + * such as Kerberos, will throw an SSLPeerUnverifiedException. + * + * @return array of peer X.509 certs, with the peer's own cert + * first in the chain, and with the "root" CA last. + */ + @Override + public java.security.cert.Certificate[] getPeerCertificates() + throws SSLPeerUnverifiedException { + // + // clone to preserve integrity of session ... caller can't + // change record of peer identity even by accident, much + // less do it intentionally. + // + if ((cipherSuite.keyExchange == K_KRB5) || + (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + throw new SSLPeerUnverifiedException("no certificates expected" + + " for Kerberos cipher suites"); + } + if (peerCerts == null) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + // Certs are immutable objects, therefore we don't clone them. + // But do need to clone the array, so that nothing is inserted + // into peerCerts. + return (java.security.cert.Certificate[])peerCerts.clone(); + } + + /** + * Return the cert chain presented to the peer in the + * java.security.cert format. + * Note: This method is useful only when using certificate-based + * cipher suites. + * + * @return array of peer X.509 certs, with the peer's own cert + * first in the chain, and with the "root" CA last. + */ + @Override + public java.security.cert.Certificate[] getLocalCertificates() { + // + // clone to preserve integrity of session ... caller can't + // change record of peer identity even by accident, much + // less do it intentionally. + return (localCerts == null ? null : + (java.security.cert.Certificate[])localCerts.clone()); + } + + /** + * Return the cert chain presented by the peer in the + * javax.security.cert format. + * Note: This method can be used only when using certificate-based + * cipher suites; using it with non-certificate-based cipher suites, + * such as Kerberos, will throw an SSLPeerUnverifiedException. + * + * @return array of peer X.509 certs, with the peer's own cert + * first in the chain, and with the "root" CA last. + */ + @Override + public javax.security.cert.X509Certificate[] getPeerCertificateChain() + throws SSLPeerUnverifiedException { + // + // clone to preserve integrity of session ... caller can't + // change record of peer identity even by accident, much + // less do it intentionally. + // + if ((cipherSuite.keyExchange == K_KRB5) || + (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + throw new SSLPeerUnverifiedException("no certificates expected" + + " for Kerberos cipher suites"); + } + if (peerCerts == null) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + javax.security.cert.X509Certificate[] certs; + certs = new javax.security.cert.X509Certificate[peerCerts.length]; + for (int i = 0; i < peerCerts.length; i++) { + byte[] der = null; + try { + der = peerCerts[i].getEncoded(); + certs[i] = javax.security.cert.X509Certificate.getInstance(der); + } catch (CertificateEncodingException e) { + throw new SSLPeerUnverifiedException(e.getMessage()); + } catch (javax.security.cert.CertificateException e) { + throw new SSLPeerUnverifiedException(e.getMessage()); + } + } + + return certs; + } + + /** + * Return the cert chain presented by the peer. + * Note: This method can be used only when using certificate-based + * cipher suites; using it with non-certificate-based cipher suites, + * such as Kerberos, will throw an SSLPeerUnverifiedException. + * + * @return array of peer X.509 certs, with the peer's own cert + * first in the chain, and with the "root" CA last. + */ + public X509Certificate[] getCertificateChain() + throws SSLPeerUnverifiedException { + /* + * clone to preserve integrity of session ... caller can't + * change record of peer identity even by accident, much + * less do it intentionally. + */ + if ((cipherSuite.keyExchange == K_KRB5) || + (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + throw new SSLPeerUnverifiedException("no certificates expected" + + " for Kerberos cipher suites"); + } + if (peerCerts != null) { + return peerCerts.clone(); + } else { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + } + + /** + * Returns the identity of the peer which was established as part of + * defining the session. + * + * @return the peer's principal. Returns an X500Principal of the + * end-entity certificate for X509-based cipher suites, and + * Principal for Kerberos cipher suites. + * + * @throws SSLPeerUnverifiedException if the peer's identity has not + * been verified + */ + @Override + public Principal getPeerPrincipal() + throws SSLPeerUnverifiedException + { + if ((cipherSuite.keyExchange == K_KRB5) || + (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + if (peerPrincipal == null) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } else { + // Eliminate dependency on KerberosPrincipal + return peerPrincipal; + } + } + if (peerCerts == null) { + throw new SSLPeerUnverifiedException("peer not authenticated"); + } + return peerCerts[0].getSubjectX500Principal(); + } + + /** + * Returns the principal that was sent to the peer during handshaking. + * + * @return the principal sent to the peer. Returns an X500Principal + * of the end-entity certificate for X509-based cipher suites, and + * Principal for Kerberos cipher suites. If no principal was + * sent, then null is returned. + */ + @Override + public Principal getLocalPrincipal() { + + if ((cipherSuite.keyExchange == K_KRB5) || + (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + // Eliminate dependency on KerberosPrincipal + return (localPrincipal == null ? null : localPrincipal); + } + return (localCerts == null ? null : + localCerts[0].getSubjectX500Principal()); + } + + /** + * Returns the time this session was created. + */ + @Override + public long getCreationTime() { + return creationTime; + } + + /** + * Returns the last time this session was used to initialize + * a connection. + */ + @Override + public long getLastAccessedTime() { + return (lastUsedTime != 0) ? lastUsedTime : creationTime; + } + + void setLastAccessedTime(long time) { + lastUsedTime = time; + } + + + /** + * Returns the network address of the session's peer. This + * implementation does not insist that connections between + * different ports on the same host must necessarily belong + * to different sessions, though that is of course allowed. + */ + public InetAddress getPeerAddress() { + try { + return InetAddress.getByName(host); + } catch (UnknownHostException e) { + return null; + } + } + + @Override + public String getPeerHost() { + return host; + } + + /** + * Need to provide the port info for caching sessions based on + * host and port. Accessed by SSLSessionContextImpl + */ + @Override + public int getPeerPort() { + return port; + } + + void setContext(SSLSessionContextImpl ctx) { + if (context == null) { + context = ctx; + } + } + + /** + * Invalidate a session. Active connections may still exist, but + * no connections will be able to rejoin this session. + */ + @Override + synchronized public void invalidate() { + // + // Can't invalidate the NULL session -- this would be + // attempted when we get a handshaking error on a brand + // new connection, with no "real" session yet. + // + if (this == nullSession) { + return; + } + invalidated = true; + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Invalidated: " + this); + } + if (context != null) { + context.remove(sessionId); + context = null; + } + } + + /* + * Table of application-specific session data indexed by an application + * key and the calling security context. This is important since + * sessions can be shared across different protection domains. + */ + private Hashtable table = new Hashtable<>(); + + /** + * Assigns a session value. Session change events are given if + * appropriate, to any original value as well as the new value. + */ + @Override + public void putValue(String key, Object value) { + if ((key == null) || (value == null)) { + throw new IllegalArgumentException("arguments can not be null"); + } + + SecureKey secureKey = new SecureKey(key); + Object oldValue = table.put(secureKey, value); + + if (oldValue instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent e; + + e = new SSLSessionBindingEvent(this, key); + ((SSLSessionBindingListener)oldValue).valueUnbound(e); + } + if (value instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent e; + + e = new SSLSessionBindingEvent(this, key); + ((SSLSessionBindingListener)value).valueBound(e); + } + } + + + /** + * Returns the specified session value. + */ + @Override + public Object getValue(String key) { + if (key == null) { + throw new IllegalArgumentException("argument can not be null"); + } + + SecureKey secureKey = new SecureKey(key); + return table.get(secureKey); + } + + + /** + * Removes the specified session value, delivering a session changed + * event as appropriate. + */ + @Override + public void removeValue(String key) { + if (key == null) { + throw new IllegalArgumentException("argument can not be null"); + } + + SecureKey secureKey = new SecureKey(key); + Object value = table.remove(secureKey); + + if (value instanceof SSLSessionBindingListener) { + SSLSessionBindingEvent e; + + e = new SSLSessionBindingEvent(this, key); + ((SSLSessionBindingListener)value).valueUnbound(e); + } + } + + + /** + * Lists the names of the session values. + */ + @Override + public String[] getValueNames() { + Enumeration e; + Vector v = new Vector<>(); + SecureKey key; + Object securityCtx = SecureKey.getCurrentSecurityContext(); + + for (e = table.keys(); e.hasMoreElements(); ) { + key = e.nextElement(); + + if (securityCtx.equals(key.getSecurityContext())) { + v.addElement(key.getAppKey()); + } + } + String[] names = new String[v.size()]; + v.copyInto(names); + + return names; + } + + /** + * Use large packet sizes now or follow RFC 2246 packet sizes (2^14) + * until changed. + * + * In the TLS specification (section 6.2.1, RFC2246), it is not + * recommended that the plaintext has more than 2^14 bytes. + * However, some TLS implementations violate the specification. + * This is a workaround for interoperability with these stacks. + * + * Application could accept large fragments up to 2^15 bytes by + * setting the system property jsse.SSLEngine.acceptLargeFragments + * to "true". + */ + private boolean acceptLargeFragments = + Debug.getBooleanProperty("jsse.SSLEngine.acceptLargeFragments", false); + + /** + * Expand the buffer size of both SSL/TLS network packet and + * application data. + */ + protected synchronized void expandBufferSizes() { + acceptLargeFragments = true; + } + + /** + * Gets the current size of the largest SSL/TLS packet that is expected + * when using this session. + */ + @Override + public synchronized int getPacketBufferSize() { + return acceptLargeFragments ? + Record.maxLargeRecordSize : Record.maxRecordSize; + } + + /** + * Gets the current size of the largest application data that is + * expected when using this session. + */ + @Override + public synchronized int getApplicationBufferSize() { + return getPacketBufferSize() - Record.headerSize; + } + + /** + * Gets an array of supported signature algorithms that the local side is + * willing to verify. + */ + @Override + public String[] getLocalSupportedSignatureAlgorithms() { + if (localSupportedSignAlgs != null) { + return localSupportedSignAlgs.clone(); + } + + return new String[0]; + } + + /** + * Gets an array of supported signature algorithms that the peer is + * able to verify. + */ + @Override + public String[] getPeerSupportedSignatureAlgorithms() { + if (peerSupportedSignAlgs != null) { + return peerSupportedSignAlgs.clone(); + } + + return new String[0]; + } + + /** + * Obtains a List containing all {@link SNIServerName}s + * of the requested Server Name Indication (SNI) extension. + */ + @Override + public List getRequestedServerNames() { + if (requestedServerNames != null && !requestedServerNames.isEmpty()) { + return Collections.unmodifiableList( + requestedServerNames); + } + + return Collections.emptyList(); + } + + /** Returns a string representation of this SSL session */ + @Override + public String toString() { + return "[Session-" + sessionCount + + ", " + getCipherSuite() + + "]"; + } + + /** + * When SSL sessions are finalized, all values bound to + * them are removed. + */ + @Override + protected void finalize() throws Throwable { + String[] names = getValueNames(); + for (int i = 0; i < names.length; i++) { + removeValue(names[i]); + } + } +} + + +/** + * This "struct" class serves as a Hash Key that combines an + * application-specific key and a security context. + */ +class SecureKey { + private static Object nullObject = new Object(); + private Object appKey; + private Object securityCtx; + + static Object getCurrentSecurityContext() { + SecurityManager sm = System.getSecurityManager(); + Object context = null; + + if (sm != null) + context = sm.getSecurityContext(); + if (context == null) + context = nullObject; + return context; + } + + SecureKey(Object key) { + this.appKey = key; + this.securityCtx = getCurrentSecurityContext(); + } + + Object getAppKey() { + return appKey; + } + + Object getSecurityContext() { + return securityCtx; + } + + @Override + public int hashCode() { + return appKey.hashCode() ^ securityCtx.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof SecureKey && ((SecureKey)o).appKey.equals(appKey) + && ((SecureKey)o).securityCtx.equals(securityCtx); + } +} diff --git a/src/sun/security/ssl/SSLSocketFactoryImpl.java b/src/sun/security/ssl/SSLSocketFactoryImpl.java new file mode 100644 index 00000000..75cbf730 --- /dev/null +++ b/src/sun/security/ssl/SSLSocketFactoryImpl.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.*; +import java.net.*; +import javax.net.ssl.SSLSocketFactory; + + +/** + * Implementation of an SSL socket factory. This provides the public + * hooks to create SSL sockets, using a "high level" programming + * interface which encapsulates system security policy defaults rather than + * offering application flexibility. In particular, it uses a configurable + * authentication context (and the keys held there) rather than offering + * any flexibility about which keys to use; that context defaults to the + * process-default context, but may be explicitly specified. + * + * @author David Brownell + */ +final public class SSLSocketFactoryImpl extends SSLSocketFactory { + + private SSLContextImpl context; + + /** + * Constructor used to instantiate the default factory. This method is + * only called if the old "ssl.SocketFactory.provider" property in the + * java.security file is set. + */ + public SSLSocketFactoryImpl() throws Exception { + this.context = SSLContextImpl.DefaultSSLContext.getDefaultImpl(); + } + + /** + * Constructs an SSL socket factory. + */ + SSLSocketFactoryImpl(SSLContextImpl context) { + this.context = context; + } + + /** + * Creates an unconnected socket. + * + * @return the unconnected socket + * @see Socket#connect(SocketAddress, int) + */ + @Override + public Socket createSocket() { + return new SSLSocketImpl(context); + } + + /** + * Constructs an SSL connection to a named host at a specified port. + * This acts as the SSL client, and may authenticate itself or rejoin + * existing SSL sessions allowed by the authentication context which + * has been configured. + * + * @param host name of the host with which to connect + * @param port number of the server's port + */ + @Override + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException + { + return new SSLSocketImpl(context, host, port); + } + + /** + * Returns a socket layered over an existing socket to a + * ServerSocket on the named host, at the given port. This + * constructor can be used when tunneling SSL through a proxy. The + * host and port refer to the logical destination server. This + * socket is configured using the socket options established for + * this factory. + * + * @param s the existing socket + * @param host the server host + * @param port the server port + * @param autoClose close the underlying socket when this socket is closed + * + * @exception IOException if the connection can't be established + * @exception UnknownHostException if the host is not known + */ + @Override + public Socket createSocket(Socket s, String host, int port, + boolean autoClose) throws IOException { + return new SSLSocketImpl(context, s, host, port, autoClose); + } + + @Override + public Socket createSocket(Socket s, InputStream consumed, + boolean autoClose) throws IOException { + if (s == null) { + throw new NullPointerException( + "the existing socket cannot be null"); + } + + return new SSLSocketImpl(context, s, consumed, autoClose); + } + + /** + * Constructs an SSL connection to a server at a specified address + * and TCP port. This acts as the SSL client, and may authenticate + * itself or rejoin existing SSL sessions allowed by the authentication + * context which has been configured. + * + * @param address the server's host + * @param port its port + */ + @Override + public Socket createSocket(InetAddress address, int port) + throws IOException + { + return new SSLSocketImpl(context, address, port); + } + + + /** + * Constructs an SSL connection to a named host at a specified port. + * This acts as the SSL client, and may authenticate itself or rejoin + * existing SSL sessions allowed by the authentication context which + * has been configured. The socket will also bind() to the local + * address and port supplied. + */ + @Override + public Socket createSocket(String host, int port, + InetAddress clientAddress, int clientPort) + throws IOException + { + return new SSLSocketImpl(context, host, port, + clientAddress, clientPort); + } + + /** + * Constructs an SSL connection to a server at a specified address + * and TCP port. This acts as the SSL client, and may authenticate + * itself or rejoin existing SSL sessions allowed by the authentication + * context which has been configured. The socket will also bind() to + * the local address and port supplied. + */ + @Override + public Socket createSocket(InetAddress address, int port, + InetAddress clientAddress, int clientPort) + throws IOException + { + return new SSLSocketImpl(context, address, port, + clientAddress, clientPort); + } + + + /** + * Returns the subset of the supported cipher suites which are + * enabled by default. These cipher suites all provide a minimum + * quality of service whereby the server authenticates itself + * (preventing person-in-the-middle attacks) and where traffic + * is encrypted to provide confidentiality. + */ + @Override + public String[] getDefaultCipherSuites() { + return context.getDefaultCipherSuiteList(false).toStringArray(); + } + + /** + * Returns the names of the cipher suites which could be enabled for use + * on an SSL connection. Normally, only a subset of these will actually + * be enabled by default, since this list may include cipher suites which + * do not support the mutual authentication of servers and clients, or + * which do not protect data confidentiality. Servers may also need + * certain kinds of certificates to use certain cipher suites. + */ + @Override + public String[] getSupportedCipherSuites() { + return context.getSupportedCipherSuiteList().toStringArray(); + } +} diff --git a/src/sun/security/ssl/SSLSocketImpl.java b/src/sun/security/ssl/SSLSocketImpl.java new file mode 100644 index 00000000..5e687253 --- /dev/null +++ b/src/sun/security/ssl/SSLSocketImpl.java @@ -0,0 +1,2690 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.net.*; +import java.security.GeneralSecurityException; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.PrivilegedAction; +import java.security.AlgorithmConstraints; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; + +import javax.crypto.BadPaddingException; +import javax.net.ssl.*; + +/** + * Implementation of an SSL socket. This is a normal connection type + * socket, implementing SSL over some lower level socket, such as TCP. + * Because it is layered over some lower level socket, it MUST override + * all default socket methods. + * + *

    This API offers a non-traditional option for establishing SSL + * connections. You may first establish the connection directly, then pass + * that connection to the SSL socket constructor with a flag saying which + * role should be taken in the handshake protocol. (The two ends of the + * connection must not choose the same role!) This allows setup of SSL + * proxying or tunneling, and also allows the kind of "role reversal" + * that is required for most FTP data transfers. + * + * @see javax.net.ssl.SSLSocket + * @see SSLServerSocket + * + * @author David Brownell + */ +final public class SSLSocketImpl extends BaseSSLSocketImpl { + + /* + * ERROR HANDLING GUIDELINES + * (which exceptions to throw and catch and which not to throw and catch) + * + * . if there is an IOException (SocketException) when accessing the + * underlying Socket, pass it through + * + * . do not throw IOExceptions, throw SSLExceptions (or a subclass) + * + * . for internal errors (things that indicate a bug in JSSE or a + * grossly misconfigured J2RE), throw either an SSLException or + * a RuntimeException at your convenience. + * + * . handshaking code (Handshaker or HandshakeMessage) should generally + * pass through exceptions, but can handle them if they know what to + * do. + * + * . exception chaining should be used for all new code. If you happen + * to touch old code that does not use chaining, you should change it. + * + * . there is a top level exception handler that sits at all entry + * points from application code to SSLSocket read/write code. It + * makes sure that all errors are handled (see handleException()). + * + * . JSSE internal code should generally not call close(), call + * closeInternal(). + */ + + /* + * There's a state machine associated with each connection, which + * among other roles serves to negotiate session changes. + * + * - START with constructor, until the TCP connection's around. + * - HANDSHAKE picks session parameters before allowing traffic. + * There are many substates due to sequencing requirements + * for handshake messages. + * - DATA may be transmitted. + * - RENEGOTIATE state allows concurrent data and handshaking + * traffic ("same" substates as HANDSHAKE), and terminates + * in selection of new session (and connection) parameters + * - ERROR state immediately precedes abortive disconnect. + * - SENT_CLOSE sent a close_notify to the peer. For layered, + * non-autoclose socket, must now read close_notify + * from peer before closing the connection. For nonlayered or + * non-autoclose socket, close connection and go onto + * cs_CLOSED state. + * - CLOSED after sending close_notify alert, & socket is closed. + * SSL connection objects are not reused. + * - APP_CLOSED once the application calls close(). Then it behaves like + * a closed socket, e.g.. getInputStream() throws an Exception. + * + * State affects what SSL record types may legally be sent: + * + * - Handshake ... only in HANDSHAKE and RENEGOTIATE states + * - App Data ... only in DATA and RENEGOTIATE states + * - Alert ... in HANDSHAKE, DATA, RENEGOTIATE + * + * Re what may be received: same as what may be sent, except that + * HandshakeRequest handshaking messages can come from servers even + * in the application data state, to request entry to RENEGOTIATE. + * + * The state machine within HANDSHAKE and RENEGOTIATE states controls + * the pending session, not the connection state, until the change + * cipher spec and "Finished" handshake messages are processed and + * make the "new" session become the current one. + * + * NOTE: details of the SMs always need to be nailed down better. + * The text above illustrates the core ideas. + * + * +---->-------+------>--------->-------+ + * | | | + * <-----< ^ ^ <-----< v + *START>----->HANDSHAKE>----->DATA>----->RENEGOTIATE SENT_CLOSE + * v v v | | + * | | | | v + * +------------+---------------+ v ERROR + * | | | + * v | | + * ERROR>------>----->CLOSED<--------<----+-- + + * | + * v + * APP_CLOSED + * + * ALSO, note that the the purpose of handshaking (renegotiation is + * included) is to assign a different, and perhaps new, session to + * the connection. The SSLv3 spec is a bit confusing on that new + * protocol feature. + */ + private static final int cs_START = 0; + private static final int cs_HANDSHAKE = 1; + private static final int cs_DATA = 2; + private static final int cs_RENEGOTIATE = 3; + private static final int cs_ERROR = 4; + private static final int cs_SENT_CLOSE = 5; + private static final int cs_CLOSED = 6; + private static final int cs_APP_CLOSED = 7; + + + /* + * Client authentication be off, requested, or required. + * + * Migrated to SSLEngineImpl: + * clauth_none/cl_auth_requested/clauth_required + */ + + /* + * Drives the protocol state machine. + */ + private volatile int connectionState; + + /* + * Flag indicating that the engine's handshaker has done the necessary + * steps so the engine may process a ChangeCipherSpec message. + */ + private boolean receivedCCS; + + /* + * Flag indicating if the next record we receive MUST be a Finished + * message. Temporarily set during the handshake to ensure that + * a change cipher spec message is followed by a finished message. + */ + private boolean expectingFinished; + + /* + * For improved diagnostics, we detail connection closure + * If the socket is closed (connectionState >= cs_ERROR), + * closeReason != null indicates if the socket was closed + * because of an error or because or normal shutdown. + */ + private SSLException closeReason; + + /* + * Per-connection private state that doesn't change when the + * session is changed. + */ + private byte doClientAuth; + private boolean roleIsServer; + private boolean enableSessionCreation = true; + private String host; + private boolean autoClose = true; + private AccessControlContext acc; + + // The cipher suites enabled for use on this connection. + private CipherSuiteList enabledCipherSuites; + + // The endpoint identification protocol + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + + // The server name indication and matchers + List serverNames = + Collections.emptyList(); + Collection sniMatchers = + Collections.emptyList(); + + // Configured application protocol values + String[] applicationProtocols = new String[0]; + + // Negotiated application protocol value. + // + // The value under negotiation will be obtained from handshaker. + String applicationProtocol = null; + + // Callback function that selects the application protocol value during + // the SSL/TLS handshake. + BiFunction, String> applicationProtocolSelector; + + /* + * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * + * IMPORTANT STUFF TO UNDERSTANDING THE SYNCHRONIZATION ISSUES. + * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * + * + * There are several locks here. + * + * The primary lock is the per-instance lock used by + * synchronized(this) and the synchronized methods. It controls all + * access to things such as the connection state and variables which + * affect handshaking. If we are inside a synchronized method, we + * can access the state directly, otherwise, we must use the + * synchronized equivalents. + * + * The handshakeLock is used to ensure that only one thread performs + * the *complete initial* handshake. If someone is handshaking, any + * stray application or startHandshake() requests who find the + * connection state is cs_HANDSHAKE will stall on handshakeLock + * until handshaking is done. Once the handshake is done, we either + * succeeded or failed, but we can never go back to the cs_HANDSHAKE + * or cs_START state again. + * + * Note that the read/write() calls here in SSLSocketImpl are not + * obviously synchronized. In fact, it's very nonintuitive, and + * requires careful examination of code paths. Grab some coffee, + * and be careful with any code changes. + * + * There can be only three threads active at a time in the I/O + * subsection of this class. + * 1. startHandshake + * 2. AppInputStream + * 3. AppOutputStream + * One thread could call startHandshake(). + * AppInputStream/AppOutputStream read() and write() calls are each + * synchronized on 'this' in their respective classes, so only one + * app. thread will be doing a SSLSocketImpl.read() or .write()'s at + * a time. + * + * If handshaking is required (state cs_HANDSHAKE), and + * getConnectionState() for some/all threads returns cs_HANDSHAKE, + * only one can grab the handshakeLock, and the rest will stall + * either on getConnectionState(), or on the handshakeLock if they + * happen to successfully race through the getConnectionState(). + * + * If a writer is doing the initial handshaking, it must create a + * temporary reader to read the responses from the other side. As a + * side-effect, the writer's reader will have priority over any + * other reader. However, the writer's reader is not allowed to + * consume any application data. When handshakeLock is finally + * released, we either have a cs_DATA connection, or a + * cs_CLOSED/cs_ERROR socket. + * + * The writeLock is held while writing on a socket connection and + * also to protect the MAC and cipher for their direction. The + * writeLock is package private for Handshaker which holds it while + * writing the ChangeCipherSpec message. + * + * To avoid the problem of a thread trying to change operational + * modes on a socket while handshaking is going on, we synchronize + * on 'this'. If handshaking has not started yet, we tell the + * handshaker to change its mode. If handshaking has started, + * we simply store that request until the next pending session + * is created, at which time the new handshaker's state is set. + * + * The readLock is held during readRecord(), which is responsible + * for reading an InputRecord, decrypting it, and processing it. + * The readLock ensures that these three steps are done atomically + * and that once started, no other thread can block on InputRecord.read. + * This is necessary so that processing of close_notify alerts + * from the peer are handled properly. + */ + final private Object handshakeLock = new Object(); + final ReentrantLock writeLock = new ReentrantLock(); + final private Object readLock = new Object(); + + private InputRecord inrec; + + /* + * Crypto state that's reinitialized when the session changes. + */ + private Authenticator readAuthenticator, writeAuthenticator; + private CipherBox readCipher, writeCipher; + // NOTE: compression state would be saved here + + /* + * security parameters for secure renegotiation. + */ + private boolean secureRenegotiation; + private byte[] clientVerifyData; + private byte[] serverVerifyData; + + /* + * The authentication context holds all information used to establish + * who this end of the connection is (certificate chains, private keys, + * etc) and who is trusted (e.g. as CAs or websites). + */ + private SSLContextImpl sslContext; + + + /* + * This connection is one of (potentially) many associated with + * any given session. The output of the handshake protocol is a + * new session ... although all the protocol description talks + * about changing the cipher spec (and it does change), in fact + * that's incidental since it's done by changing everything that + * is associated with a session at the same time. (TLS/IETF may + * change that to add client authentication w/o new key exchg.) + */ + private Handshaker handshaker; + private SSLSessionImpl sess; + private volatile SSLSessionImpl handshakeSession; + + + /* + * If anyone wants to get notified about handshake completions, + * they'll show up on this list. + */ + private HashMap + handshakeListeners; + + /* + * Reuse the same internal input/output streams. + */ + private InputStream sockInput; + private OutputStream sockOutput; + + + /* + * These input and output streams block their data in SSL records, + * and usually arrange integrity and privacy protection for those + * records. The guts of the SSL protocol are wrapped up in these + * streams, and in the handshaking that establishes the details of + * that integrity and privacy protection. + */ + private AppInputStream input; + private AppOutputStream output; + + /* + * The protocol versions enabled for use on this connection. + * + * Note: we support a pseudo protocol called SSLv2Hello which when + * set will result in an SSL v2 Hello being sent with SSL (version 3.0) + * or TLS (version 3.1, 3.2, etc.) version info. + */ + private ProtocolList enabledProtocols; + + /* + * The SSL version associated with this connection. + */ + private ProtocolVersion protocolVersion = ProtocolVersion.DEFAULT; + + /* Class and subclass dynamic debugging support */ + private static final Debug debug = Debug.getInstance("ssl"); + + /* + * Is it the first application record to write? + */ + private boolean isFirstAppOutputRecord = true; + + /* + * If AppOutputStream needs to delay writes of small packets, we + * will use this to store the data until we actually do the write. + */ + private ByteArrayOutputStream heldRecordBuffer = null; + + /* + * Whether local cipher suites preference in server side should be + * honored during handshaking? + */ + private boolean preferLocalCipherSuites = false; + + // + // CONSTRUCTORS AND INITIALIZATION CODE + // + + /** + * Constructs an SSL connection to a named host at a specified port, + * using the authentication context provided. This endpoint acts as + * the client, and may rejoin an existing SSL session if appropriate. + * + * @param context authentication context to use + * @param host name of the host with which to connect + * @param port number of the server's port + */ + SSLSocketImpl(SSLContextImpl context, String host, int port) + throws IOException, UnknownHostException { + super(); + this.host = host; + this.serverNames = + Utilities.addToSNIServerNameList(this.serverNames, this.host); + init(context, false); + SocketAddress socketAddress = + host != null ? new InetSocketAddress(host, port) : + new InetSocketAddress(InetAddress.getByName(null), port); + connect(socketAddress, 0); + } + + + /** + * Constructs an SSL connection to a server at a specified address. + * and TCP port, using the authentication context provided. This + * endpoint acts as the client, and may rejoin an existing SSL session + * if appropriate. + * + * @param context authentication context to use + * @param address the server's host + * @param port its port + */ + SSLSocketImpl(SSLContextImpl context, InetAddress host, int port) + throws IOException { + super(); + init(context, false); + SocketAddress socketAddress = new InetSocketAddress(host, port); + connect(socketAddress, 0); + } + + /** + * Constructs an SSL connection to a named host at a specified port, + * using the authentication context provided. This endpoint acts as + * the client, and may rejoin an existing SSL session if appropriate. + * + * @param context authentication context to use + * @param host name of the host with which to connect + * @param port number of the server's port + * @param localAddr the local address the socket is bound to + * @param localPort the local port the socket is bound to + */ + SSLSocketImpl(SSLContextImpl context, String host, int port, + InetAddress localAddr, int localPort) + throws IOException, UnknownHostException { + super(); + this.host = host; + this.serverNames = + Utilities.addToSNIServerNameList(this.serverNames, this.host); + init(context, false); + bind(new InetSocketAddress(localAddr, localPort)); + SocketAddress socketAddress = + host != null ? new InetSocketAddress(host, port) : + new InetSocketAddress(InetAddress.getByName(null), port); + connect(socketAddress, 0); + } + + + /** + * Constructs an SSL connection to a server at a specified address. + * and TCP port, using the authentication context provided. This + * endpoint acts as the client, and may rejoin an existing SSL session + * if appropriate. + * + * @param context authentication context to use + * @param address the server's host + * @param port its port + * @param localAddr the local address the socket is bound to + * @param localPort the local port the socket is bound to + */ + SSLSocketImpl(SSLContextImpl context, InetAddress host, int port, + InetAddress localAddr, int localPort) + throws IOException { + super(); + init(context, false); + bind(new InetSocketAddress(localAddr, localPort)); + SocketAddress socketAddress = new InetSocketAddress(host, port); + connect(socketAddress, 0); + } + + /* + * Package-private constructor used ONLY by SSLServerSocket. The + * java.net package accepts the TCP connection after this call is + * made. This just initializes handshake state to use "server mode", + * giving control over the use of SSL client authentication. + */ + SSLSocketImpl(SSLContextImpl context, boolean serverMode, + CipherSuiteList suites, byte clientAuth, + boolean sessionCreation, ProtocolList protocols, + String identificationProtocol, + AlgorithmConstraints algorithmConstraints, + Collection sniMatchers, + boolean preferLocalCipherSuites, + String[] applicationProtocols) throws IOException { + + super(); + doClientAuth = clientAuth; + enableSessionCreation = sessionCreation; + this.identificationProtocol = identificationProtocol; + this.algorithmConstraints = algorithmConstraints; + this.sniMatchers = sniMatchers; + this.preferLocalCipherSuites = preferLocalCipherSuites; + this.applicationProtocols = applicationProtocols; + init(context, serverMode); + + /* + * Override what was picked out for us. + */ + enabledCipherSuites = suites; + enabledProtocols = protocols; + } + + + /** + * Package-private constructor used to instantiate an unconnected + * socket. The java.net package will connect it, either when the + * connect() call is made by the application. This instance is + * meant to set handshake state to use "client mode". + */ + SSLSocketImpl(SSLContextImpl context) { + super(); + init(context, false); + } + + + /** + * Layer SSL traffic over an existing connection, rather than creating + * a new connection. The existing connection may be used only for SSL + * traffic (using this SSLSocket) until the SSLSocket.close() call + * returns. However, if a protocol error is detected, that existing + * connection is automatically closed. + * + *

    This particular constructor always uses the socket in the + * role of an SSL client. It may be useful in cases which start + * using SSL after some initial data transfers, for example in some + * SSL tunneling applications or as part of some kinds of application + * protocols which negotiate use of a SSL based security. + * + * @param sock the existing connection + * @param context the authentication context to use + */ + SSLSocketImpl(SSLContextImpl context, Socket sock, String host, + int port, boolean autoClose) throws IOException { + super(sock); + // We always layer over a connected socket + if (!sock.isConnected()) { + throw new SocketException("Underlying socket is not connected"); + } + this.host = host; + this.serverNames = + Utilities.addToSNIServerNameList(this.serverNames, this.host); + init(context, false); + this.autoClose = autoClose; + doneConnect(); + } + + /** + * Creates a server mode {@link Socket} layered over an + * existing connected socket, and is able to read data which has + * already been consumed/removed from the {@link Socket}'s + * underlying {@link InputStream}. + */ + SSLSocketImpl(SSLContextImpl context, Socket sock, + InputStream consumed, boolean autoClose) throws IOException { + super(sock, consumed); + // We always layer over a connected socket + if (!sock.isConnected()) { + throw new SocketException("Underlying socket is not connected"); + } + + // In server mode, it is not necessary to set host and serverNames. + // Otherwise, would require a reverse DNS lookup to get the hostname. + + init(context, true); + this.autoClose = autoClose; + doneConnect(); + } + + /** + * Initializes the client socket. + */ + private void init(SSLContextImpl context, boolean isServer) { + sslContext = context; + sess = SSLSessionImpl.nullSession; + handshakeSession = null; + + /* + * role is as specified, state is START until after + * the low level connection's established. + */ + roleIsServer = isServer; + connectionState = cs_START; + receivedCCS = false; + + /* + * default read and write side cipher and MAC support + * + * Note: compression support would go here too + */ + readCipher = CipherBox.NULL; + readAuthenticator = MAC.NULL; + writeCipher = CipherBox.NULL; + writeAuthenticator = MAC.NULL; + + // initial security parameters for secure renegotiation + secureRenegotiation = false; + clientVerifyData = new byte[0]; + serverVerifyData = new byte[0]; + + enabledCipherSuites = + sslContext.getDefaultCipherSuiteList(roleIsServer); + enabledProtocols = + sslContext.getDefaultProtocolList(roleIsServer); + + inrec = null; + + // save the acc + acc = AccessController.getContext(); + + input = new AppInputStream(this); + output = new AppOutputStream(this); + } + + /** + * Connects this socket to the server with a specified timeout + * value. + * + * This method is either called on an unconnected SSLSocketImpl by the + * application, or it is called in the constructor of a regular + * SSLSocketImpl. If we are layering on top on another socket, then + * this method should not be called, because we assume that the + * underlying socket is already connected by the time it is passed to + * us. + * + * @param endpoint the SocketAddress + * @param timeout the timeout value to be used, 0 is no timeout + * @throws IOException if an error occurs during the connection + * @throws SocketTimeoutException if timeout expires before connecting + */ + @Override + public void connect(SocketAddress endpoint, int timeout) + throws IOException { + + if (isLayered()) { + throw new SocketException("Already connected"); + } + + if (!(endpoint instanceof InetSocketAddress)) { + throw new SocketException( + "Cannot handle non-Inet socket addresses."); + } + + super.connect(endpoint, timeout); + doneConnect(); + } + + /** + * Initialize the handshaker and socket streams. + * + * Called by connect, the layered constructor, and SSLServerSocket. + */ + void doneConnect() throws IOException { + /* + * Save the input and output streams. May be done only after + * java.net actually connects using the socket "self", else + * we get some pretty bizarre failure modes. + */ + sockInput = super.getInputStream(); + sockOutput = super.getOutputStream(); + + /* + * Move to handshaking state, with pending session initialized + * to defaults and the appropriate kind of handshaker set up. + */ + initHandshaker(); + } + + synchronized private int getConnectionState() { + return connectionState; + } + + synchronized private void setConnectionState(int state) { + connectionState = state; + } + + AccessControlContext getAcc() { + return acc; + } + + // + // READING AND WRITING RECORDS + // + + /* + * AppOutputStream calls may need to buffer multiple outbound + * application packets. + * + * All other writeRecord() calls will not buffer, so do not hold + * these records. + */ + void writeRecord(OutputRecord r) throws IOException { + writeRecord(r, false); + } + + /* + * Record Output. Application data can't be sent until the first + * handshake establishes a session. + * + * NOTE: we let empty records be written as a hook to force some + * TCP-level activity, notably handshaking, to occur. + */ + void writeRecord(OutputRecord r, boolean holdRecord) throws IOException { + /* + * The loop is in case of HANDSHAKE --> ERROR transitions, etc + */ + loop: + while (r.contentType() == Record.ct_application_data) { + /* + * Not all states support passing application data. We + * synchronize access to the connection state, so that + * synchronous handshakes can complete cleanly. + */ + switch (getConnectionState()) { + + /* + * We've deferred the initial handshaking till just now, + * when presumably a thread's decided it's OK to block for + * longish periods of time for I/O purposes (as well as + * configured the cipher suites it wants to use). + */ + case cs_HANDSHAKE: + performInitialHandshake(); + break; + + case cs_DATA: + case cs_RENEGOTIATE: + break loop; + + case cs_ERROR: + fatal(Alerts.alert_close_notify, + "error while writing to socket"); + break; // dummy + + case cs_SENT_CLOSE: + case cs_CLOSED: + case cs_APP_CLOSED: + // we should never get here (check in AppOutputStream) + // this is just a fallback + if (closeReason != null) { + throw closeReason; + } else { + throw new SocketException("Socket closed"); + } + + /* + * Else something's goofy in this state machine's use. + */ + default: + throw new SSLProtocolException("State error, send app data"); + } + } + + // + // Don't bother to really write empty records. We went this + // far to drive the handshake machinery, for correctness; not + // writing empty records improves performance by cutting CPU + // time and network resource usage. However, some protocol + // implementations are fragile and don't like to see empty + // records, so this also increases robustness. + // + if (!r.isEmpty()) { + + // If the record is a close notify alert, we need to honor + // socket option SO_LINGER. Note that we will try to send + // the close notify even if the SO_LINGER set to zero. + if (r.isAlert(Alerts.alert_close_notify) && getSoLinger() >= 0) { + + // keep and clear the current thread interruption status. + boolean interrupted = Thread.interrupted(); + try { + if (writeLock.tryLock(getSoLinger(), TimeUnit.SECONDS)) { + try { + writeRecordInternal(r, holdRecord); + } finally { + writeLock.unlock(); + } + } else { + SSLException ssle = new SSLException( + "SO_LINGER timeout," + + " close_notify message cannot be sent."); + + + // For layered, non-autoclose sockets, we are not + // able to bring them into a usable state, so we + // treat it as fatal error. + if (isLayered() && !autoClose) { + // Note that the alert description is + // specified as -1, so no message will be send + // to peer anymore. + fatal((byte)(-1), ssle); + } else if ((debug != null) && Debug.isOn("ssl")) { + System.out.println( + Thread.currentThread().getName() + + ", received Exception: " + ssle); + } + + // RFC2246 requires that the session becomes + // unresumable if any connection is terminated + // without proper close_notify messages with + // level equal to warning. + // + // RFC4346 no longer requires that a session not be + // resumed if failure to properly close a connection. + // + // We choose to make the session unresumable if + // failed to send the close_notify message. + // + sess.invalidate(); + } + } catch (InterruptedException ie) { + // keep interrupted status + interrupted = true; + } + + // restore the interrupted status + if (interrupted) { + Thread.currentThread().interrupt(); + } + } else { + writeLock.lock(); + try { + writeRecordInternal(r, holdRecord); + } finally { + writeLock.unlock(); + } + } + } + } + + private void writeRecordInternal(OutputRecord r, + boolean holdRecord) throws IOException { + + // r.compress(c); + r.encrypt(writeAuthenticator, writeCipher); + + if (holdRecord) { + // If we were requested to delay the record due to possibility + // of Nagle's being active when finally got to writing, and + // it's actually not, we don't really need to delay it. + if (getTcpNoDelay()) { + holdRecord = false; + } else { + // We need to hold the record, so let's provide + // a per-socket place to do it. + if (heldRecordBuffer == null) { + // Likely only need 37 bytes. + heldRecordBuffer = new ByteArrayOutputStream(40); + } + } + } + r.write(sockOutput, holdRecord, heldRecordBuffer); + + /* + * Check the sequence number state + * + * Note that in order to maintain the connection I/O + * properly, we check the sequence number after the last + * record writing process. As we request renegotiation + * or close the connection for wrapped sequence number + * when there is enough sequence number space left to + * handle a few more records, so the sequence number + * of the last record cannot be wrapped. + */ + if (connectionState < cs_ERROR) { + checkSequenceNumber(writeAuthenticator, r.contentType()); + } + + // turn off the flag of the first application record + if (isFirstAppOutputRecord && + r.contentType() == Record.ct_application_data) { + isFirstAppOutputRecord = false; + } + } + + /* + * Need to split the payload except the following cases: + * + * 1. protocol version is TLS 1.1 or later; + * 2. bulk cipher does not use CBC mode, including null bulk cipher suites. + * 3. the payload is the first application record of a freshly + * negotiated TLS session. + * 4. the CBC protection is disabled; + * + * More details, please refer to AppOutputStream.write(byte[], int, int). + */ + boolean needToSplitPayload() { + writeLock.lock(); + try { + return (protocolVersion.v <= ProtocolVersion.TLS10.v) && + writeCipher.isCBCMode() && !isFirstAppOutputRecord && + Record.enableCBCProtection; + } finally { + writeLock.unlock(); + } + } + + /* + * Read an application data record. Alerts and handshake + * messages are handled directly. + */ + void readDataRecord(InputRecord r) throws IOException { + if (getConnectionState() == cs_HANDSHAKE) { + performInitialHandshake(); + } + readRecord(r, true); + } + + + /* + * Clear the pipeline of records from the peer, optionally returning + * application data. Caller is responsible for knowing that it's + * possible to do this kind of clearing, if they don't want app + * data -- e.g. since it's the initial SSL handshake. + * + * Don't synchronize (this) during a blocking read() since it + * protects data which is accessed on the write side as well. + */ + private void readRecord(InputRecord r, boolean needAppData) + throws IOException { + int state; + + // readLock protects reading and processing of an InputRecord. + // It keeps the reading from sockInput and processing of the record + // atomic so that no two threads can be blocked on the + // read from the same input stream at the same time. + // This is required for example when a reader thread is + // blocked on the read and another thread is trying to + // close the socket. For a non-autoclose, layered socket, + // the thread performing the close needs to read the close_notify. + // + // Use readLock instead of 'this' for locking because + // 'this' also protects data accessed during writing. + synchronized (readLock) { + /* + * Read and handle records ... return application data + * ONLY if it's needed. + */ + + while (((state = getConnectionState()) != cs_CLOSED) && + (state != cs_ERROR) && (state != cs_APP_CLOSED)) { + /* + * Read a record ... maybe emitting an alert if we get a + * comprehensible but unsupported "hello" message during + * format checking (e.g. V2). + */ + try { + r.setAppDataValid(false); + r.read(sockInput, sockOutput); + } catch (SSLProtocolException e) { + try { + fatal(Alerts.alert_unexpected_message, e); + } catch (IOException x) { + // discard this exception + } + throw e; + } catch (EOFException eof) { + boolean handshaking = (getConnectionState() <= cs_HANDSHAKE); + boolean rethrow = requireCloseNotify || handshaking; + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", received EOFException: " + + (rethrow ? "error" : "ignored")); + } + if (rethrow) { + SSLException e; + if (handshaking) { + e = new SSLHandshakeException + ("Remote host closed connection during handshake"); + } else { + e = new SSLProtocolException + ("Remote host closed connection incorrectly"); + } + e.initCause(eof); + throw e; + } else { + // treat as if we had received a close_notify + closeInternal(false); + continue; + } + } + + + /* + * The basic SSLv3 record protection involves (optional) + * encryption for privacy, and an integrity check ensuring + * data origin authentication. We do them both here, and + * throw a fatal alert if the integrity check fails. + */ + try { + r.decrypt(readAuthenticator, readCipher); + } catch (BadPaddingException e) { + byte alertType = (r.contentType() == Record.ct_handshake) + ? Alerts.alert_handshake_failure + : Alerts.alert_bad_record_mac; + fatal(alertType, e.getMessage(), e); + } + + // if (!r.decompress(c)) + // fatal(Alerts.alert_decompression_failure, + // "decompression failure"); + + /* + * Process the record. + */ + synchronized (this) { + switch (r.contentType()) { + case Record.ct_handshake: + /* + * Handshake messages always go to a pending session + * handshaker ... if there isn't one, create one. This + * must work asynchronously, for renegotiation. + * + * NOTE that handshaking will either resume a session + * which was in the cache (and which might have other + * connections in it already), or else will start a new + * session (new keys exchanged) with just this connection + * in it. + */ + initHandshaker(); + if (!handshaker.activated()) { + // prior to handshaking, activate the handshake + if (connectionState == cs_RENEGOTIATE) { + // don't use SSLv2Hello when renegotiating + handshaker.activate(protocolVersion); + } else { + handshaker.activate(null); + } + } + + /* + * process the handshake record ... may contain just + * a partial handshake message or multiple messages. + * + * The handshaker state machine will ensure that it's + * a finished message. + */ + handshaker.process_record(r, expectingFinished); + expectingFinished = false; + + if (handshaker.invalidated) { + handshaker = null; + receivedCCS = false; + // if state is cs_RENEGOTIATE, revert it to cs_DATA + if (connectionState == cs_RENEGOTIATE) { + connectionState = cs_DATA; + } + } else if (handshaker.isDone()) { + // reset the parameters for secure renegotiation. + secureRenegotiation = + handshaker.isSecureRenegotiation(); + clientVerifyData = handshaker.getClientVerifyData(); + serverVerifyData = handshaker.getServerVerifyData(); + // set connection ALPN value + applicationProtocol = + handshaker.getHandshakeApplicationProtocol(); + + sess = handshaker.getSession(); + handshakeSession = null; + handshaker = null; + connectionState = cs_DATA; + receivedCCS = false; + + // + // Tell folk about handshake completion, but do + // it in a separate thread. + // + if (handshakeListeners != null) { + HandshakeCompletedEvent event = + new HandshakeCompletedEvent(this, sess); + + Thread t = new NotifyHandshakeThread( + handshakeListeners.entrySet(), event); + t.start(); + } + } + + if (needAppData || connectionState != cs_DATA) { + continue; + } + break; + + case Record.ct_application_data: + // Pass this right back up to the application. + if (connectionState != cs_DATA + && connectionState != cs_RENEGOTIATE + && connectionState != cs_SENT_CLOSE) { + throw new SSLProtocolException( + "Data received in non-data state: " + + connectionState); + } + if (expectingFinished) { + throw new SSLProtocolException + ("Expecting finished message, received data"); + } + if (!needAppData) { + throw new SSLException("Discarding app data"); + } + + r.setAppDataValid(true); + break; + + case Record.ct_alert: + recvAlert(r); + continue; + + case Record.ct_change_cipher_spec: + if ((connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) + || !handshaker.sessionKeysCalculated() + || receivedCCS) { + // For the CCS message arriving in the wrong state + fatal(Alerts.alert_unexpected_message, + "illegal change cipher spec msg, conn state = " + + connectionState + ", handshake state = " + + handshaker.state); + } else if (r.available() != 1 || r.read() != 1) { + // For structural/content issues with the CCS + fatal(Alerts.alert_unexpected_message, + "Malformed change cipher spec msg"); + } + + // Once we've received CCS, update the flag. + // If the remote endpoint sends it again in this handshake + // we won't process it. + receivedCCS = true; + + // + // The first message after a change_cipher_spec + // record MUST be a "Finished" handshake record, + // else it's a protocol violation. We force this + // to be checked by a minor tweak to the state + // machine. + // + changeReadCiphers(); + // next message MUST be a finished message + expectingFinished = true; + continue; + + default: + // + // TLS requires that unrecognized records be ignored. + // + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", Received record type: " + + r.contentType()); + } + continue; + } // switch + + /* + * Check the sequence number state + * + * Note that in order to maintain the connection I/O + * properly, we check the sequence number after the last + * record reading process. As we request renegotiation + * or close the connection for wrapped sequence number + * when there is enough sequence number space left to + * handle a few more records, so the sequence number + * of the last record cannot be wrapped. + */ + if (connectionState < cs_ERROR) { + checkSequenceNumber(readAuthenticator, r.contentType()); + } + + return; + } // synchronized (this) + } + + // + // couldn't read, due to some kind of error + // + r.close(); + return; + } // synchronized (readLock) + } + + /** + * Check the sequence number state + * + * RFC 4346 states that, "Sequence numbers are of type uint64 and + * may not exceed 2^64-1. Sequence numbers do not wrap. If a TLS + * implementation would need to wrap a sequence number, it must + * renegotiate instead." + */ + private void checkSequenceNumber(Authenticator authenticator, byte type) + throws IOException { + + /* + * Don't bother to check the sequence number for error or + * closed connections, or NULL MAC. + */ + if (connectionState >= cs_ERROR || authenticator == MAC.NULL) { + return; + } + + /* + * Conservatively, close the connection immediately when the + * sequence number is close to overflow + */ + if (authenticator.seqNumOverflow()) { + /* + * TLS protocols do not define a error alert for sequence + * number overflow. We use handshake_failure error alert + * for handshaking and bad_record_mac for other records. + */ + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", sequence number extremely close to overflow " + + "(2^64-1 packets). Closing connection."); + + } + + fatal(Alerts.alert_handshake_failure, "sequence number overflow"); + } + + /* + * Ask for renegotiation when need to renew sequence number. + * + * Don't bother to kickstart the renegotiation when the local is + * asking for it. + */ + if ((type != Record.ct_handshake) && authenticator.seqNumIsHuge()) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", request renegotiation " + + "to avoid sequence number overflow"); + } + + startHandshake(); + } + } + + // + // HANDSHAKE RELATED CODE + // + + /** + * Return the AppInputStream. For use by Handshaker only. + */ + AppInputStream getAppInputStream() { + return input; + } + + /** + * Return the AppOutputStream. For use by Handshaker only. + */ + AppOutputStream getAppOutputStream() { + return output; + } + + /** + * Initialize the handshaker object. This means: + * + * . if a handshake is already in progress (state is cs_HANDSHAKE + * or cs_RENEGOTIATE), do nothing and return + * + * . if the socket is already closed, throw an Exception (internal error) + * + * . otherwise (cs_START or cs_DATA), create the appropriate handshaker + * object, and advance the connection state (to cs_HANDSHAKE or + * cs_RENEGOTIATE, respectively). + * + * This method is called right after a new socket is created, when + * starting renegotiation, or when changing client/ server mode of the + * socket. + */ + private void initHandshaker() { + switch (connectionState) { + + // + // Starting a new handshake. + // + case cs_START: + case cs_DATA: + break; + + // + // We're already in the middle of a handshake. + // + case cs_HANDSHAKE: + case cs_RENEGOTIATE: + return; + + // + // Anyone allowed to call this routine is required to + // do so ONLY if the connection state is reasonable... + // + default: + throw new IllegalStateException("Internal error"); + } + + // state is either cs_START or cs_DATA + if (connectionState == cs_START) { + connectionState = cs_HANDSHAKE; + } else { // cs_DATA + connectionState = cs_RENEGOTIATE; + } + if (roleIsServer) { + handshaker = new ServerHandshaker(this, sslContext, + enabledProtocols, doClientAuth, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); + handshaker.setSNIMatchers(sniMatchers); + handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); + } else { + handshaker = new ClientHandshaker(this, sslContext, + enabledProtocols, + protocolVersion, connectionState == cs_HANDSHAKE, + secureRenegotiation, clientVerifyData, serverVerifyData); + handshaker.setSNIServerNames(serverNames); + } + handshaker.setEnabledCipherSuites(enabledCipherSuites); + handshaker.setEnableSessionCreation(enableSessionCreation); + handshaker.setApplicationProtocols(applicationProtocols); + handshaker.setApplicationProtocolSelectorSSLSocket( + applicationProtocolSelector); + } + + /** + * Synchronously perform the initial handshake. + * + * If the handshake is already in progress, this method blocks until it + * is completed. If the initial handshake has already been completed, + * it returns immediately. + */ + private void performInitialHandshake() throws IOException { + // use handshakeLock and the state check to make sure only + // one thread performs the handshake + synchronized (handshakeLock) { + if (getConnectionState() == cs_HANDSHAKE) { + kickstartHandshake(); + + /* + * All initial handshaking goes through this + * InputRecord until we have a valid SSL connection. + * Once initial handshaking is finished, AppInputStream's + * InputRecord can handle any future renegotiation. + * + * Keep this local so that it goes out of scope and is + * eventually GC'd. + */ + if (inrec == null) { + inrec = new InputRecord(); + + /* + * Grab the characteristics already assigned to + * AppInputStream's InputRecord. Enable checking for + * SSLv2 hellos on this first handshake. + */ + inrec.setHandshakeHash(input.r.getHandshakeHash()); + inrec.setHelloVersion(input.r.getHelloVersion()); + inrec.enableFormatChecks(); + } + + readRecord(inrec, false); + inrec = null; + } + } + } + + /** + * Starts an SSL handshake on this connection. + */ + @Override + public void startHandshake() throws IOException { + // start an ssl handshake that could be resumed from timeout exception + startHandshake(true); + } + + /** + * Starts an ssl handshake on this connection. + * + * @param resumable indicates the handshake process is resumable from a + * certain exception. If resumable, the socket will + * be reserved for exceptions like timeout; otherwise, the socket + * will be closed, no further communications could be done. + */ + private void startHandshake(boolean resumable) throws IOException { + checkWrite(); + try { + if (getConnectionState() == cs_HANDSHAKE) { + // do initial handshake + performInitialHandshake(); + } else { + // start renegotiation + kickstartHandshake(); + } + } catch (Exception e) { + // shutdown and rethrow (wrapped) exception as appropriate + handleException(e, resumable); + } + } + + /** + * Kickstart the handshake if it is not already in progress. + * This means: + * + * . if handshaking is already underway, do nothing and return + * + * . if the socket is not connected or already closed, throw an + * Exception. + * + * . otherwise, call initHandshake() to initialize the handshaker + * object and progress the state. Then, send the initial + * handshaking message if appropriate (always on clients and + * on servers when renegotiating). + */ + private synchronized void kickstartHandshake() throws IOException { + + switch (connectionState) { + + case cs_HANDSHAKE: + // handshaker already setup, proceed + break; + + case cs_DATA: + if (!secureRenegotiation && !Handshaker.allowUnsafeRenegotiation) { + throw new SSLHandshakeException( + "Insecure renegotiation is not allowed"); + } + + if (!secureRenegotiation) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: Using insecure renegotiation"); + } + } + + // initialize the handshaker, move to cs_RENEGOTIATE + initHandshaker(); + break; + + case cs_RENEGOTIATE: + // handshaking already in progress, return + return; + + /* + * The only way to get a socket in the state is when + * you have an unconnected socket. + */ + case cs_START: + throw new SocketException( + "handshaking attempted on unconnected socket"); + + default: + throw new SocketException("connection is closed"); + } + + // + // Kickstart handshake state machine if we need to ... + // + // Note that handshaker.kickstart() writes the message + // to its HandshakeOutStream, which calls back into + // SSLSocketImpl.writeRecord() to send it. + // + if (!handshaker.activated()) { + // prior to handshaking, activate the handshake + if (connectionState == cs_RENEGOTIATE) { + // don't use SSLv2Hello when renegotiating + handshaker.activate(protocolVersion); + } else { + handshaker.activate(null); + } + + if (handshaker instanceof ClientHandshaker) { + // send client hello + handshaker.kickstart(); + } else { + if (connectionState == cs_HANDSHAKE) { + // initial handshake, no kickstart message to send + } else { + // we want to renegotiate, send hello request + handshaker.kickstart(); + // hello request is not included in the handshake + // hashes, reset them + handshaker.handshakeHash.reset(); + } + } + } + } + + // + // CLOSURE RELATED CALLS + // + + /** + * Return whether the socket has been explicitly closed by the application. + */ + @Override + public boolean isClosed() { + return connectionState == cs_APP_CLOSED; + } + + /** + * Return whether we have reached end-of-file. + * + * If the socket is not connected, has been shutdown because of an error + * or has been closed, throw an Exception. + */ + boolean checkEOF() throws IOException { + switch (getConnectionState()) { + case cs_START: + throw new SocketException("Socket is not connected"); + + case cs_HANDSHAKE: + case cs_DATA: + case cs_RENEGOTIATE: + case cs_SENT_CLOSE: + return false; + + case cs_APP_CLOSED: + throw new SocketException("Socket is closed"); + + case cs_ERROR: + case cs_CLOSED: + default: + // either closed because of error, or normal EOF + if (closeReason == null) { + return true; + } + IOException e = new SSLException + ("Connection has been shutdown: " + closeReason); + e.initCause(closeReason); + throw e; + + } + } + + /** + * Check if we can write data to this socket. If not, throw an IOException. + */ + void checkWrite() throws IOException { + if (checkEOF() || (getConnectionState() == cs_SENT_CLOSE)) { + // we are at EOF, write must throw Exception + throw new SocketException("Connection closed by remote host"); + } + } + + protected void closeSocket() throws IOException { + + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called closeSocket()"); + } + + super.close(); + } + + private void closeSocket(boolean selfInitiated) throws IOException { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called closeSocket(" + selfInitiated + ")"); + } + if (!isLayered() || autoClose) { + super.close(); + } else if (selfInitiated) { + // layered && non-autoclose + // read close_notify alert to clear input stream + waitForClose(false); + } + } + + /* + * Closing the connection is tricky ... we can't officially close the + * connection until we know the other end is ready to go away too, + * and if ever the connection gets aborted we must forget session + * state (it becomes invalid). + */ + + /** + * Closes the SSL connection. SSL includes an application level + * shutdown handshake; you should close SSL sockets explicitly + * rather than leaving it for finalization, so that your remote + * peer does not experience a protocol error. + */ + @Override + public void close() throws IOException { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called close()"); + } + closeInternal(true); // caller is initiating close + setConnectionState(cs_APP_CLOSED); + } + + /** + * Don't synchronize the whole method because waitForClose() + * (which calls readRecord()) might be called. + * + * @param selfInitiated Indicates which party initiated the close. + * If selfInitiated, this side is initiating a close; for layered and + * non-autoclose socket, wait for close_notify response. + * If !selfInitiated, peer sent close_notify; we reciprocate but + * no need to wait for response. + */ + private void closeInternal(boolean selfInitiated) throws IOException { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", called closeInternal(" + selfInitiated + ")"); + } + + int state = getConnectionState(); + boolean closeSocketCalled = false; + Throwable cachedThrowable = null; + try { + switch (state) { + case cs_START: + // unconnected socket or handshaking has not been initialized + closeSocket(selfInitiated); + break; + + /* + * If we're closing down due to error, we already sent (or else + * received) the fatal alert ... no niceties, blow the connection + * away as quickly as possible (even if we didn't allocate the + * socket ourselves; it's unusable, regardless). + */ + case cs_ERROR: + closeSocket(); + break; + + /* + * Sometimes close() gets called more than once. + */ + case cs_CLOSED: + case cs_APP_CLOSED: + break; + + /* + * Otherwise we indicate clean termination. + */ + // case cs_HANDSHAKE: + // case cs_DATA: + // case cs_RENEGOTIATE: + // case cs_SENT_CLOSE: + default: + synchronized (this) { + if (((state = getConnectionState()) == cs_CLOSED) || + (state == cs_ERROR) || (state == cs_APP_CLOSED)) { + return; // connection was closed while we waited + } + if (state != cs_SENT_CLOSE) { + try { + warning(Alerts.alert_close_notify); + connectionState = cs_SENT_CLOSE; + } catch (Throwable th) { + // we need to ensure socket is closed out + // if we encounter any errors. + connectionState = cs_ERROR; + // cache this for later use + cachedThrowable = th; + closeSocketCalled = true; + closeSocket(selfInitiated); + } + } + } + // If state was cs_SENT_CLOSE before, we don't do the actual + // closing since it is already in progress. + if (state == cs_SENT_CLOSE) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", close invoked again; state = " + + getConnectionState()); + } + if (selfInitiated == false) { + // We were called because a close_notify message was + // received. This may be due to another thread calling + // read() or due to our call to waitForClose() below. + // In either case, just return. + return; + } + // Another thread explicitly called close(). We need to + // wait for the closing to complete before returning. + synchronized (this) { + while (connectionState < cs_CLOSED) { + try { + this.wait(); + } catch (InterruptedException e) { + // ignore + } + } + } + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", after primary close; state = " + + getConnectionState()); + } + return; + } + + if (!closeSocketCalled) { + closeSocketCalled = true; + closeSocket(selfInitiated); + } + + break; + } + } finally { + synchronized (this) { + // Upon exit from this method, the state is always >= cs_CLOSED + connectionState = (connectionState == cs_APP_CLOSED) + ? cs_APP_CLOSED : cs_CLOSED; + // notify any threads waiting for the closing to finish + this.notifyAll(); + } + if (closeSocketCalled) { + // Dispose of ciphers since we've closed socket + disposeCiphers(); + } + if (cachedThrowable != null) { + /* + * Rethrow the error to the calling method + * The Throwable caught can only be an Error or RuntimeException + */ + if (cachedThrowable instanceof Error) + throw (Error) cachedThrowable; + if (cachedThrowable instanceof RuntimeException) + throw (RuntimeException) cachedThrowable; + } + } + } + + /** + * Reads a close_notify or a fatal alert from the input stream. + * Keep reading records until we get a close_notify or until + * the connection is otherwise closed. The close_notify or alert + * might be read by another reader, + * which will then process the close and set the connection state. + */ + void waitForClose(boolean rethrow) throws IOException { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", waiting for close_notify or alert: state " + + getConnectionState()); + } + + try { + int state; + + while (((state = getConnectionState()) != cs_CLOSED) && + (state != cs_ERROR) && (state != cs_APP_CLOSED)) { + // create the InputRecord if it isn't initialized. + if (inrec == null) { + inrec = new InputRecord(); + } + + // Ask for app data and then throw it away + try { + readRecord(inrec, true); + } catch (SocketTimeoutException e) { + // if time out, ignore the exception and continue + } + } + inrec = null; + } catch (IOException e) { + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", Exception while waiting for close " +e); + } + if (rethrow) { + throw e; // pass exception up + } + } + } + + /** + * Called by closeInternal() only. Be sure to consider the + * synchronization locks carefully before calling it elsewhere. + */ + private void disposeCiphers() { + // See comment in changeReadCiphers() + synchronized (readLock) { + readCipher.dispose(); + } + // See comment in changeReadCiphers() + writeLock.lock(); + try { + writeCipher.dispose(); + } finally { + writeLock.unlock(); + } + } + + // + // EXCEPTION AND ALERT HANDLING + // + + /** + * Handle an exception. This method is called by top level exception + * handlers (in read(), write()) to make sure we always shutdown the + * connection correctly and do not pass runtime exception to the + * application. + */ + void handleException(Exception e) throws IOException { + handleException(e, true); + } + + /** + * Handle an exception. This method is called by top level exception + * handlers (in read(), write(), startHandshake()) to make sure we + * always shutdown the connection correctly and do not pass runtime + * exception to the application. + * + * This method never returns normally, it always throws an IOException. + * + * We first check if the socket has already been shutdown because of an + * error. If so, we just rethrow the exception. If the socket has not + * been shutdown, we sent a fatal alert and remember the exception. + * + * @param e the Exception + * @param resumable indicates the caller process is resumable from the + * exception. If resumable, the socket will be + * reserved for exceptions like timeout; otherwise, the socket + * will be closed, no further communications could be done. + */ + synchronized private void handleException(Exception e, boolean resumable) + throws IOException { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", handling exception: " + e.toString()); + } + + // don't close the Socket in case of timeouts or interrupts if + // the process is resumable. + if (e instanceof InterruptedIOException && resumable) { + throw (IOException)e; + } + + // if we've already shutdown because of an error, + // there is nothing to do except rethrow the exception + if (closeReason != null) { + if (e instanceof IOException) { // includes SSLException + throw (IOException)e; + } else { + // this is odd, not an IOException. + // normally, this should not happen + // if closeReason has been already been set + throw Alerts.getSSLException(Alerts.alert_internal_error, e, + "Unexpected exception"); + } + } + + // need to perform error shutdown + boolean isSSLException = (e instanceof SSLException); + if ((isSSLException == false) && (e instanceof IOException)) { + // IOException from the socket + // this means the TCP connection is already dead + // we call fatal just to set the error status + try { + fatal(Alerts.alert_unexpected_message, e); + } catch (IOException ee) { + // ignore (IOException wrapped in SSLException) + } + // rethrow original IOException + throw (IOException)e; + } + + // must be SSLException or RuntimeException + byte alertType; + if (isSSLException) { + if (e instanceof SSLHandshakeException) { + alertType = Alerts.alert_handshake_failure; + } else { + alertType = Alerts.alert_unexpected_message; + } + } else { + alertType = Alerts.alert_internal_error; + } + fatal(alertType, e); + } + + /* + * Send a warning alert. + */ + void warning(byte description) { + sendAlert(Alerts.alert_warning, description); + } + + synchronized void fatal(byte description, String diagnostic) + throws IOException { + fatal(description, diagnostic, null); + } + + synchronized void fatal(byte description, Throwable cause) + throws IOException { + fatal(description, null, cause); + } + + /* + * Send a fatal alert, and throw an exception so that callers will + * need to stand on their heads to accidentally continue processing. + */ + synchronized void fatal(byte description, String diagnostic, + Throwable cause) throws IOException { + if ((input != null) && (input.r != null)) { + input.r.close(); + } + sess.invalidate(); + if (handshakeSession != null) { + handshakeSession.invalidate(); + } + + int oldState = connectionState; + if (connectionState < cs_ERROR) { + connectionState = cs_ERROR; + } + + /* + * Has there been an error received yet? If not, remember it. + * By RFC 2246, we don't bother waiting for a response. + * Fatal errors require immediate shutdown. + */ + if (closeReason == null) { + /* + * Try to clear the kernel buffer to avoid TCP connection resets. + */ + if (oldState == cs_HANDSHAKE) { + sockInput.skip(sockInput.available()); + } + + // If the description equals -1, the alert won't be sent to peer. + if (description != -1) { + sendAlert(Alerts.alert_fatal, description); + } + if (cause instanceof SSLException) { // only true if != null + closeReason = (SSLException)cause; + } else { + closeReason = + Alerts.getSSLException(description, cause, diagnostic); + } + } + + /* + * Clean up our side. + */ + closeSocket(); + // Another thread may have disposed the ciphers during closing + if (connectionState < cs_CLOSED) { + connectionState = (oldState == cs_APP_CLOSED) ? cs_APP_CLOSED + : cs_CLOSED; + + // We should lock readLock and writeLock if no deadlock risks. + // See comment in changeReadCiphers() + readCipher.dispose(); + writeCipher.dispose(); + } + + throw closeReason; + } + + + /* + * Process an incoming alert ... caller must already have synchronized + * access to "this". + */ + private void recvAlert(InputRecord r) throws IOException { + byte level = (byte)r.read(); + byte description = (byte)r.read(); + if (description == -1) { // check for short message + fatal(Alerts.alert_illegal_parameter, "Short alert message"); + } + + if (debug != null && (Debug.isOn("record") || + Debug.isOn("handshake"))) { + synchronized (System.out) { + System.out.print(Thread.currentThread().getName()); + System.out.print(", RECV " + protocolVersion + " ALERT: "); + if (level == Alerts.alert_fatal) { + System.out.print("fatal, "); + } else if (level == Alerts.alert_warning) { + System.out.print("warning, "); + } else { + System.out.print(", "); + } + System.out.println(Alerts.alertDescription(description)); + } + } + + if (level == Alerts.alert_warning) { + if (description == Alerts.alert_close_notify) { + if (connectionState == cs_HANDSHAKE) { + fatal(Alerts.alert_unexpected_message, + "Received close_notify during handshake"); + } else { + closeInternal(false); // reply to close + } + } else { + + // + // The other legal warnings relate to certificates, + // e.g. no_certificate, bad_certificate, etc; these + // are important to the handshaking code, which can + // also handle illegal protocol alerts if needed. + // + if (handshaker != null) { + handshaker.handshakeAlert(description); + } + } + } else { // fatal or unknown level + String reason = "Received fatal alert: " + + Alerts.alertDescription(description); + if (closeReason == null) { + closeReason = Alerts.getSSLException(description, reason); + } + fatal(Alerts.alert_unexpected_message, reason); + } + } + + + /* + * Emit alerts. Caller must have synchronized with "this". + */ + private void sendAlert(byte level, byte description) { + // the connectionState cannot be cs_START + if (connectionState >= cs_SENT_CLOSE) { + return; + } + + // For initial handshaking, don't send alert message to peer if + // handshaker has not started. + if (connectionState == cs_HANDSHAKE && + (handshaker == null || !handshaker.started())) { + return; + } + + OutputRecord r = new OutputRecord(Record.ct_alert); + r.setVersion(protocolVersion); + + boolean useDebug = debug != null && Debug.isOn("ssl"); + if (useDebug) { + synchronized (System.out) { + System.out.print(Thread.currentThread().getName()); + System.out.print(", SEND " + protocolVersion + " ALERT: "); + if (level == Alerts.alert_fatal) { + System.out.print("fatal, "); + } else if (level == Alerts.alert_warning) { + System.out.print("warning, "); + } else { + System.out.print(", "); + } + System.out.println("description = " + + Alerts.alertDescription(description)); + } + } + + r.write(level); + r.write(description); + try { + writeRecord(r); + } catch (IOException e) { + if (useDebug) { + System.out.println(Thread.currentThread().getName() + + ", Exception sending alert: " + e); + } + } + } + + // + // VARIOUS OTHER METHODS + // + + /* + * When a connection finishes handshaking by enabling use of a newly + * negotiated session, each end learns about it in two halves (read, + * and write). When both read and write ciphers have changed, and the + * last handshake message has been read, the connection has joined + * (rejoined) the new session. + * + * NOTE: The SSLv3 spec is rather unclear on the concepts here. + * Sessions don't change once they're established (including cipher + * suite and master secret) but connections can join them (and leave + * them). They're created by handshaking, though sometime handshaking + * causes connections to join up with pre-established sessions. + */ + private void changeReadCiphers() throws SSLException { + if (connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) { + throw new SSLProtocolException( + "State error, change cipher specs"); + } + + // ... create decompressor + + CipherBox oldCipher = readCipher; + + try { + readCipher = handshaker.newReadCipher(); + readAuthenticator = handshaker.newReadAuthenticator(); + } catch (GeneralSecurityException e) { + // "can't happen" + throw new SSLException("Algorithm missing: ", e); + } + + /* + * Dispose of any intermediate state in the underlying cipher. + * For PKCS11 ciphers, this will release any attached sessions, + * and thus make finalization faster. + * + * Since MAC's doFinal() is called for every SSL/TLS packet, it's + * not necessary to do the same with MAC's. + */ + oldCipher.dispose(); + } + + // used by Handshaker + void changeWriteCiphers() throws SSLException { + if (connectionState != cs_HANDSHAKE + && connectionState != cs_RENEGOTIATE) { + throw new SSLProtocolException( + "State error, change cipher specs"); + } + + // ... create compressor + + CipherBox oldCipher = writeCipher; + + try { + writeCipher = handshaker.newWriteCipher(); + writeAuthenticator = handshaker.newWriteAuthenticator(); + } catch (GeneralSecurityException e) { + // "can't happen" + throw new SSLException("Algorithm missing: ", e); + } + + // See comment above. + oldCipher.dispose(); + + // reset the flag of the first application record + isFirstAppOutputRecord = true; + } + + /* + * Updates the SSL version associated with this connection. + * Called from Handshaker once it has determined the negotiated version. + */ + synchronized void setVersion(ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + output.r.setVersion(protocolVersion); + } + + synchronized String getHost() { + // Note that the host may be null or empty for localhost. + if (host == null || host.length() == 0) { + host = getInetAddress().getHostName(); + } + return host; + } + + // ONLY used by HttpsClient to setup the URI specified hostname + // + // Please NOTE that this method MUST be called before calling to + // SSLSocket.setSSLParameters(). Otherwise, the {@code host} parameter + // may override SNIHostName in the customized server name indication. + synchronized public void setHost(String host) { + this.host = host; + this.serverNames = + Utilities.addToSNIServerNameList(this.serverNames, this.host); + } + + /** + * Gets an input stream to read from the peer on the other side. + * Data read from this stream was always integrity protected in + * transit, and will usually have been confidentiality protected. + */ + @Override + synchronized public InputStream getInputStream() throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + /* + * Can't call isConnected() here, because the Handshakers + * do some initialization before we actually connect. + */ + if (connectionState == cs_START) { + throw new SocketException("Socket is not connected"); + } + + return input; + } + + /** + * Gets an output stream to write to the peer on the other side. + * Data written on this stream is always integrity protected, and + * will usually be confidentiality protected. + */ + @Override + synchronized public OutputStream getOutputStream() throws IOException { + if (isClosed()) { + throw new SocketException("Socket is closed"); + } + + /* + * Can't call isConnected() here, because the Handshakers + * do some initialization before we actually connect. + */ + if (connectionState == cs_START) { + throw new SocketException("Socket is not connected"); + } + + return output; + } + + /** + * Returns the the SSL Session in use by this connection. These can + * be long lived, and frequently correspond to an entire login session + * for some user. + */ + @Override + public SSLSession getSession() { + /* + * Force a synchronous handshake, if appropriate. + */ + if (getConnectionState() == cs_HANDSHAKE) { + try { + // start handshaking, if failed, the connection will be closed. + startHandshake(false); + } catch (IOException e) { + // handshake failed. log and return a nullSession + if (debug != null && Debug.isOn("handshake")) { + System.out.println(Thread.currentThread().getName() + + ", IOException in getSession(): " + e); + } + } + } + synchronized (this) { + return sess; + } + } + + @Override + synchronized public SSLSession getHandshakeSession() { + return handshakeSession; + } + + synchronized void setHandshakeSession(SSLSessionImpl session) { + handshakeSession = session; + } + + /** + * Controls whether new connections may cause creation of new SSL + * sessions. + * + * As long as handshaking has not started, we can change + * whether we enable session creations. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setEnableSessionCreation(boolean flag) { + enableSessionCreation = flag; + + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnableSessionCreation(enableSessionCreation); + } + } + + /** + * Returns true if new connections may cause creation of new SSL + * sessions. + */ + @Override + synchronized public boolean getEnableSessionCreation() { + return enableSessionCreation; + } + + + /** + * Sets the flag controlling whether a server mode socket + * *REQUIRES* SSL client authentication. + * + * As long as handshaking has not started, we can change + * whether client authentication is needed. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setNeedClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_required : SSLEngineImpl.clauth_none); + + if ((handshaker != null) && + (handshaker instanceof ServerHandshaker) && + !handshaker.activated()) { + ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + } + } + + @Override + synchronized public boolean getNeedClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_required); + } + + /** + * Sets the flag controlling whether a server mode socket + * *REQUESTS* SSL client authentication. + * + * As long as handshaking has not started, we can change + * whether client authentication is requested. Otherwise, + * we will need to wait for the next handshake. + */ + @Override + synchronized public void setWantClientAuth(boolean flag) { + doClientAuth = (flag ? + SSLEngineImpl.clauth_requested : SSLEngineImpl.clauth_none); + + if ((handshaker != null) && + (handshaker instanceof ServerHandshaker) && + !handshaker.activated()) { + ((ServerHandshaker) handshaker).setClientAuth(doClientAuth); + } + } + + @Override + synchronized public boolean getWantClientAuth() { + return (doClientAuth == SSLEngineImpl.clauth_requested); + } + + + /** + * Sets the flag controlling whether the socket is in SSL + * client or server mode. Must be called before any SSL + * traffic has started. + */ + @Override + @SuppressWarnings("fallthrough") + synchronized public void setUseClientMode(boolean flag) { + switch (connectionState) { + + case cs_START: + /* + * If we need to change the socket mode and the enabled + * protocols haven't specifically been set by the user, + * change them to the corresponding default ones. + */ + if (roleIsServer != (!flag) && + sslContext.isDefaultProtocolList(enabledProtocols)) { + enabledProtocols = sslContext.getDefaultProtocolList(!flag); + } + roleIsServer = !flag; + break; + + case cs_HANDSHAKE: + /* + * If we have a handshaker, but haven't started + * SSL traffic, we can throw away our current + * handshaker, and start from scratch. Don't + * need to call doneConnect() again, we already + * have the streams. + */ + assert(handshaker != null); + if (!handshaker.activated()) { + /* + * If we need to change the socket mode and the enabled + * protocols haven't specifically been set by the user, + * change them to the corresponding default ones. + */ + if (roleIsServer != (!flag) && + sslContext.isDefaultProtocolList(enabledProtocols)) { + enabledProtocols = sslContext.getDefaultProtocolList(!flag); + } + roleIsServer = !flag; + connectionState = cs_START; + initHandshaker(); + break; + } + + // If handshake has started, that's an error. Fall through... + + default: + if (debug != null && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", setUseClientMode() invoked in state = " + + connectionState); + } + throw new IllegalArgumentException( + "Cannot change mode after SSL traffic has started"); + } + } + + @Override + synchronized public boolean getUseClientMode() { + return !roleIsServer; + } + + + /** + * Returns the names of the cipher suites which could be enabled for use + * on an SSL connection. Normally, only a subset of these will actually + * be enabled by default, since this list may include cipher suites which + * do not support the mutual authentication of servers and clients, or + * which do not protect data confidentiality. Servers may also need + * certain kinds of certificates to use certain cipher suites. + * + * @return an array of cipher suite names + */ + @Override + public String[] getSupportedCipherSuites() { + return sslContext.getSupportedCipherSuiteList().toStringArray(); + } + + /** + * Controls which particular cipher suites are enabled for use on + * this connection. The cipher suites must have been listed by + * getCipherSuites() as being supported. Even if a suite has been + * enabled, it might never be used if no peer supports it or the + * requisite certificates (and private keys) are not available. + * + * @param suites Names of all the cipher suites to enable. + */ + @Override + synchronized public void setEnabledCipherSuites(String[] suites) { + enabledCipherSuites = new CipherSuiteList(suites); + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnabledCipherSuites(enabledCipherSuites); + } + } + + /** + * Returns the names of the SSL cipher suites which are currently enabled + * for use on this connection. When an SSL socket is first created, + * all enabled cipher suites (a) protect data confidentiality, + * by traffic encryption, and (b) can mutually authenticate + * both clients and servers. Thus, in some environments, this value + * might be empty. + * + * @return an array of cipher suite names + */ + @Override + synchronized public String[] getEnabledCipherSuites() { + return enabledCipherSuites.toStringArray(); + } + + + /** + * Returns the protocols that are supported by this implementation. + * A subset of the supported protocols may be enabled for this connection + * @return an array of protocol names. + */ + @Override + public String[] getSupportedProtocols() { + return sslContext.getSuportedProtocolList().toStringArray(); + } + + /** + * Controls which protocols are enabled for use on + * this connection. The protocols must have been listed by + * getSupportedProtocols() as being supported. + * + * @param protocols protocols to enable. + * @exception IllegalArgumentException when one of the protocols + * named by the parameter is not supported. + */ + @Override + synchronized public void setEnabledProtocols(String[] protocols) { + enabledProtocols = new ProtocolList(protocols); + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setEnabledProtocols(enabledProtocols); + } + } + + @Override + synchronized public String[] getEnabledProtocols() { + return enabledProtocols.toStringArray(); + } + + /** + * Assigns the socket timeout. + * @see Socket#setSoTimeout + */ + @Override + public void setSoTimeout(int timeout) throws SocketException { + if ((debug != null) && Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", setSoTimeout(" + timeout + ") called"); + } + + super.setSoTimeout(timeout); + } + + /** + * Registers an event listener to receive notifications that an + * SSL handshake has completed on this connection. + */ + @Override + public synchronized void addHandshakeCompletedListener( + HandshakeCompletedListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener is null"); + } + if (handshakeListeners == null) { + handshakeListeners = new + HashMap(4); + } + handshakeListeners.put(listener, AccessController.getContext()); + } + + + /** + * Removes a previously registered handshake completion listener. + */ + @Override + public synchronized void removeHandshakeCompletedListener( + HandshakeCompletedListener listener) { + if (handshakeListeners == null) { + throw new IllegalArgumentException("no listeners"); + } + if (handshakeListeners.remove(listener) == null) { + throw new IllegalArgumentException("listener not registered"); + } + if (handshakeListeners.isEmpty()) { + handshakeListeners = null; + } + } + + /** + * Returns the SSLParameters in effect for this SSLSocket. + */ + @Override + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + params.setSNIMatchers(sniMatchers); + params.setServerNames(serverNames); + params.setUseCipherSuitesOrder(preferLocalCipherSuites); + params.setApplicationProtocols(applicationProtocols); + + return params; + } + + /** + * Applies SSLParameters to this socket. + */ + @Override + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + preferLocalCipherSuites = params.getUseCipherSuitesOrder(); + + List sniNames = params.getServerNames(); + if (sniNames != null) { + serverNames = sniNames; + } + + Collection matchers = params.getSNIMatchers(); + if (matchers != null) { + sniMatchers = matchers; + } + + applicationProtocols = params.getApplicationProtocols(); + + if ((handshaker != null) && !handshaker.started()) { + handshaker.setIdentificationProtocol(identificationProtocol); + handshaker.setAlgorithmConstraints(algorithmConstraints); + handshaker.setApplicationProtocols(applicationProtocols); + if (roleIsServer) { + handshaker.setSNIMatchers(sniMatchers); + handshaker.setUseCipherSuitesOrder(preferLocalCipherSuites); + } else { + handshaker.setSNIServerNames(serverNames); + } + } + } + + /* + * Returns a boolean indicating whether the ChangeCipherSpec message + * has been received for this handshake. + */ + boolean receivedChangeCipherSpec() { + return receivedCCS; + } + + @Override + public synchronized String getApplicationProtocol() { + return applicationProtocol; + } + + @Override + public synchronized String getHandshakeApplicationProtocol() { + if ((handshaker != null) && handshaker.started()) { + return handshaker.getHandshakeApplicationProtocol(); + } + return null; + } + + @Override + public synchronized void setHandshakeApplicationProtocolSelector( + BiFunction, String> selector) { + applicationProtocolSelector = selector; + if ((handshaker != null) && !handshaker.activated()) { + handshaker.setApplicationProtocolSelectorSSLSocket(selector); + } + } + + @Override + public synchronized BiFunction, String> + getHandshakeApplicationProtocolSelector() { + return this.applicationProtocolSelector; + } + + // + // We allocate a separate thread to deliver handshake completion + // events. This ensures that the notifications don't block the + // protocol state machine. + // + private static class NotifyHandshakeThread extends Thread { + + private Set> + targets; // who gets notified + private HandshakeCompletedEvent event; // the notification + + NotifyHandshakeThread( + Set> + entrySet, HandshakeCompletedEvent e) { + + super("HandshakeCompletedNotify-Thread"); + targets = new HashSet<>(entrySet); // clone the entry set + event = e; + } + + @Override + public void run() { + // Don't need to synchronize, as it only runs in one thread. + for (Map.Entry + entry : targets) { + + final HandshakeCompletedListener l = entry.getKey(); + AccessControlContext acc = entry.getValue(); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + l.handshakeCompleted(event); + return null; + } + }, acc); + } + } + } + + /** + * Returns a printable representation of this end of the connection. + */ + @Override + public String toString() { + StringBuffer retval = new StringBuffer(80); + + retval.append(Integer.toHexString(hashCode())); + retval.append("["); + retval.append(sess.getCipherSuite()); + retval.append(": "); + + retval.append(super.toString()); + retval.append("]"); + + return retval.toString(); + } +} diff --git a/src/sun/security/ssl/ServerHandshaker.java b/src/sun/security/ssl/ServerHandshaker.java new file mode 100644 index 00000000..6131099d --- /dev/null +++ b/src/sun/security/ssl/ServerHandshaker.java @@ -0,0 +1,1926 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.io.*; +import java.util.*; +import java.security.*; +import java.security.cert.*; +import java.security.interfaces.*; +import java.security.spec.ECParameterSpec; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import javax.net.ssl.*; + +import javax.security.auth.Subject; + +import sun.security.util.KeyUtil; +import sun.security.action.GetPropertyAction; +import sun.security.ssl.HandshakeMessage.*; +import sun.security.ssl.CipherSuite.*; +import sun.security.ssl.SignatureAndHashAlgorithm.*; +import static sun.security.ssl.CipherSuite.KeyExchange.*; + +/** + * ServerHandshaker does the protocol handshaking from the point + * of view of a server. It is driven asychronously by handshake messages + * as delivered by the parent Handshaker class, and also uses + * common functionality (e.g. key generation) that is provided there. + * + * @author David Brownell + */ +final class ServerHandshaker extends Handshaker { + + // is the server going to require the client to authenticate? + private byte doClientAuth; + + // our authentication info + private X509Certificate[] certs; + private PrivateKey privateKey; + + private Object serviceCreds; + + // flag to check for clientCertificateVerify message + private boolean needClientVerify = false; + + /* + * For exportable ciphersuites using non-exportable key sizes, we use + * ephemeral RSA keys. We could also do anonymous RSA in the same way + * but there are no such ciphersuites currently defined. + */ + private PrivateKey tempPrivateKey; + private PublicKey tempPublicKey; + + /* + * For anonymous and ephemeral Diffie-Hellman key exchange, we use + * ephemeral Diffie-Hellman keys. + */ + private DHCrypt dh; + + // Helper for ECDH based key exchanges + private ECDHCrypt ecdh; + + // version request by the client in its ClientHello + // we remember it for the RSA premaster secret version check + private ProtocolVersion clientRequestedVersion; + + private SupportedEllipticCurvesExtension supportedCurves; + + // the preferable signature algorithm used by ServerKeyExchange message + SignatureAndHashAlgorithm preferableSignatureAlgorithm; + + // Flag to use smart ephemeral DH key which size matches the corresponding + // authentication key + private static final boolean useSmartEphemeralDHKeys; + + // Flag to use legacy ephemeral DH key which size is 512 bits for + // exportable cipher suites, and 768 bits for others + private static final boolean useLegacyEphemeralDHKeys; + + // The customized ephemeral DH key size for non-exportable cipher suites. + private static final int customizedDHKeySize; + + static { + String property = AccessController.doPrivileged( + new GetPropertyAction("jdk.tls.ephemeralDHKeySize")); + if (property == null || property.length() == 0) { + useLegacyEphemeralDHKeys = false; + useSmartEphemeralDHKeys = false; + customizedDHKeySize = -1; + } else if ("matched".equals(property)) { + useLegacyEphemeralDHKeys = false; + useSmartEphemeralDHKeys = true; + customizedDHKeySize = -1; + } else if ("legacy".equals(property)) { + useLegacyEphemeralDHKeys = true; + useSmartEphemeralDHKeys = false; + customizedDHKeySize = -1; + } else { + useLegacyEphemeralDHKeys = false; + useSmartEphemeralDHKeys = false; + + try { + customizedDHKeySize = Integer.parseUnsignedInt(property); + if (customizedDHKeySize < 1024 || customizedDHKeySize > 2048) { + throw new IllegalArgumentException( + "Customized DH key size should be positive integer " + + "between 1024 and 2048 bits, inclusive"); + } + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException( + "Invalid system property jdk.tls.ephemeralDHKeySize"); + } + } + } + + /* + * Constructor ... use the keys found in the auth context. + */ + ServerHandshaker(SSLSocketImpl socket, SSLContextImpl context, + ProtocolList enabledProtocols, byte clientAuth, + ProtocolVersion activeProtocolVersion, boolean isInitialHandshake, + boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(socket, context, enabledProtocols, + (clientAuth != SSLEngineImpl.clauth_none), false, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + doClientAuth = clientAuth; + } + + /* + * Constructor ... use the keys found in the auth context. + */ + ServerHandshaker(SSLEngineImpl engine, SSLContextImpl context, + ProtocolList enabledProtocols, byte clientAuth, + ProtocolVersion activeProtocolVersion, + boolean isInitialHandshake, boolean secureRenegotiation, + byte[] clientVerifyData, byte[] serverVerifyData) { + + super(engine, context, enabledProtocols, + (clientAuth != SSLEngineImpl.clauth_none), false, + activeProtocolVersion, isInitialHandshake, secureRenegotiation, + clientVerifyData, serverVerifyData); + doClientAuth = clientAuth; + } + + /* + * As long as handshaking has not started, we can change + * whether client authentication is required. Otherwise, + * we will need to wait for the next handshake. + */ + void setClientAuth(byte clientAuth) { + doClientAuth = clientAuth; + } + + /* + * This routine handles all the server side handshake messages, one at + * a time. Given the message type (and in some cases the pending cipher + * spec) it parses the type-specific message. Then it calls a function + * that handles that specific message. + * + * It updates the state machine as each message is processed, and writes + * responses as needed using the connection in the constructor. + */ + @Override + void processMessage(byte type, int message_len) + throws IOException { + // + // In SSLv3 and TLS, messages follow strictly increasing + // numerical order _except_ for one annoying special case. + // + if ((state >= type) + && (state != HandshakeMessage.ht_client_key_exchange + && type != HandshakeMessage.ht_certificate_verify)) { + throw new SSLProtocolException( + "Handshake message sequence violation, state = " + state + + ", type = " + type); + } + + switch (type) { + case HandshakeMessage.ht_client_hello: + ClientHello ch = new ClientHello(input, message_len); + /* + * send it off for processing. + */ + this.clientHello(ch); + break; + + case HandshakeMessage.ht_certificate: + if (doClientAuth == SSLEngineImpl.clauth_none) { + fatalSE(Alerts.alert_unexpected_message, + "client sent unsolicited cert chain"); + // NOTREACHED + } + this.clientCertificate(new CertificateMsg(input)); + break; + + case HandshakeMessage.ht_client_key_exchange: + SecretKey preMasterSecret; + switch (keyExchange) { + case K_RSA: + case K_RSA_EXPORT: + /* + * The client's pre-master secret is decrypted using + * either the server's normal private RSA key, or the + * temporary one used for non-export or signing-only + * certificates/keys. + */ + RSAClientKeyExchange pms = new RSAClientKeyExchange( + protocolVersion, clientRequestedVersion, + sslContext.getSecureRandom(), input, + message_len, privateKey); + preMasterSecret = this.clientKeyExchange(pms); + break; + case K_KRB5: + case K_KRB5_EXPORT: + preMasterSecret = this.clientKeyExchange( + new KerberosClientKeyExchange(protocolVersion, + clientRequestedVersion, + sslContext.getSecureRandom(), + input, + this.getAccSE(), + serviceCreds)); + break; + case K_DHE_RSA: + case K_DHE_DSS: + case K_DH_ANON: + /* + * The pre-master secret is derived using the normal + * Diffie-Hellman calculation. Note that the main + * protocol difference in these five flavors is in how + * the ServerKeyExchange message was constructed! + */ + preMasterSecret = this.clientKeyExchange( + new DHClientKeyExchange(input)); + break; + case K_ECDH_RSA: + case K_ECDH_ECDSA: + case K_ECDHE_RSA: + case K_ECDHE_ECDSA: + case K_ECDH_ANON: + preMasterSecret = this.clientKeyExchange + (new ECDHClientKeyExchange(input)); + break; + default: + throw new SSLProtocolException + ("Unrecognized key exchange: " + keyExchange); + } + + // + // All keys are calculated from the premaster secret + // and the exchanged nonces in the same way. + // + calculateKeys(preMasterSecret, clientRequestedVersion); + break; + + case HandshakeMessage.ht_certificate_verify: + this.clientCertificateVerify(new CertificateVerify(input, + localSupportedSignAlgs, protocolVersion)); + break; + + case HandshakeMessage.ht_finished: + // A ChangeCipherSpec record must have been received prior to + // reception of the Finished message (RFC 5246, 7.4.9). + if (!receivedChangeCipherSpec()) { + fatalSE(Alerts.alert_handshake_failure, + "Received Finished message before ChangeCipherSpec"); + } + + this.clientFinished( + new Finished(protocolVersion, input, cipherSuite)); + break; + + default: + throw new SSLProtocolException( + "Illegal server handshake msg, " + type); + } + + // + // Move state machine forward if the message handling + // code didn't already do so + // + if (state < type) { + if(type == HandshakeMessage.ht_certificate_verify) { + state = type + 2; // an annoying special case + } else { + state = type; + } + } + } + + + /* + * ClientHello presents the server with a bunch of options, to which the + * server replies with a ServerHello listing the ones which this session + * will use. If needed, it also writes its Certificate plus in some cases + * a ServerKeyExchange message. It may also write a CertificateRequest, + * to elicit a client certificate. + * + * All these messages are terminated by a ServerHelloDone message. In + * most cases, all this can be sent in a single Record. + */ + private void clientHello(ClientHello mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // Reject client initiated renegotiation? + // + // If server side should reject client-initiated renegotiation, + // send an alert_handshake_failure fatal alert, not a no_renegotiation + // warning alert (no_renegotiation must be a warning: RFC 2246). + // no_renegotiation might seem more natural at first, but warnings + // are not appropriate because the sending party does not know how + // the receiving party will behave. This state must be treated as + // a fatal server condition. + // + // This will not have any impact on server initiated renegotiation. + if (rejectClientInitiatedRenego && !isInitialHandshake && + state != HandshakeMessage.ht_hello_request) { + fatalSE(Alerts.alert_handshake_failure, + "Client initiated renegotiation is not allowed"); + } + + // check the server name indication if required + ServerNameExtension clientHelloSNIExt = (ServerNameExtension) + mesg.extensions.get(ExtensionType.EXT_SERVER_NAME); + if (!sniMatchers.isEmpty()) { + // we do not reject client without SNI extension + if (clientHelloSNIExt != null && + !clientHelloSNIExt.isMatched(sniMatchers)) { + fatalSE(Alerts.alert_unrecognized_name, + "Unrecognized server name indication"); + } + } + + // Does the message include security renegotiation indication? + boolean renegotiationIndicated = false; + + // check the TLS_EMPTY_RENEGOTIATION_INFO_SCSV + CipherSuiteList cipherSuites = mesg.getCipherSuites(); + if (cipherSuites.contains(CipherSuite.C_SCSV)) { + renegotiationIndicated = true; + if (isInitialHandshake) { + secureRenegotiation = true; + } else { + // abort the handshake with a fatal handshake_failure alert + if (secureRenegotiation) { + fatalSE(Alerts.alert_handshake_failure, + "The SCSV is present in a secure renegotiation"); + } else { + fatalSE(Alerts.alert_handshake_failure, + "The SCSV is present in a insecure renegotiation"); + } + } + } + + // check the "renegotiation_info" extension + RenegotiationInfoExtension clientHelloRI = (RenegotiationInfoExtension) + mesg.extensions.get(ExtensionType.EXT_RENEGOTIATION_INFO); + if (clientHelloRI != null) { + renegotiationIndicated = true; + if (isInitialHandshake) { + // verify the length of the "renegotiated_connection" field + if (!clientHelloRI.isEmpty()) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info field is not empty"); + } + + secureRenegotiation = true; + } else { + if (!secureRenegotiation) { + // unexpected RI extension for insecure renegotiation, + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "The renegotiation_info is present in a insecure " + + "renegotiation"); + } + + // verify the client_verify_data value + if (!Arrays.equals(clientVerifyData, + clientHelloRI.getRenegotiatedConnection())) { + fatalSE(Alerts.alert_handshake_failure, + "Incorrect verify data in ClientHello " + + "renegotiation_info message"); + } + } + } else if (!isInitialHandshake && secureRenegotiation) { + // if the connection's "secure_renegotiation" flag is set to TRUE + // and the "renegotiation_info" extension is not present, abort + // the handshake. + fatalSE(Alerts.alert_handshake_failure, + "Inconsistent secure renegotiation indication"); + } + + // if there is no security renegotiation indication or the previous + // handshake is insecure. + if (!renegotiationIndicated || !secureRenegotiation) { + if (isInitialHandshake) { + if (!allowLegacyHelloMessages) { + // abort the handshake with a fatal handshake_failure alert + fatalSE(Alerts.alert_handshake_failure, + "Failed to negotiate the use of secure renegotiation"); + } + + // continue with legacy ClientHello + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning: No renegotiation " + + "indication in ClientHello, allow legacy ClientHello"); + } + } else if (!allowUnsafeRenegotiation) { + // abort the handshake + if (activeProtocolVersion.v >= ProtocolVersion.TLS10.v) { + // respond with a no_renegotiation warning + warningSE(Alerts.alert_no_renegotiation); + + // invalidate the handshake so that the caller can + // dispose this object. + invalidated = true; + + // If there is still unread block in the handshake + // input stream, it would be truncated with the disposal + // and the next handshake message will become incomplete. + // + // However, according to SSL/TLS specifications, no more + // handshake message could immediately follow ClientHello + // or HelloRequest. But in case of any improper messages, + // we'd better check to ensure there is no remaining bytes + // in the handshake input stream. + if (input.available() > 0) { + fatalSE(Alerts.alert_unexpected_message, + "ClientHello followed by an unexpected " + + "handshake message"); + } + + return; + } else { + // For SSLv3, send the handshake_failure fatal error. + // Note that SSLv3 does not define a no_renegotiation + // alert like TLSv1. However we cannot ignore the message + // simply, otherwise the other side was waiting for a + // response that would never come. + fatalSE(Alerts.alert_handshake_failure, + "Renegotiation is not allowed"); + } + } else { // !isInitialHandshake && allowUnsafeRenegotiation + // continue with unsafe renegotiation. + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Warning: continue with insecure renegotiation"); + } + } + } + + // check the ALPN extension + ALPNExtension clientHelloALPN = (ALPNExtension) + mesg.extensions.get(ExtensionType.EXT_ALPN); + + // Use the application protocol callback when provided. + // Otherwise use the local list of application protocols. + boolean hasAPCallback = + ((engine != null && appProtocolSelectorSSLEngine != null) || + (conn != null && appProtocolSelectorSSLSocket != null)); + + if (!hasAPCallback) { + if ((clientHelloALPN != null) && (localApl.length > 0)) { + + // Intersect the requested and the locally supported, + // and save for later. + String negotiatedValue = null; + List protocols = clientHelloALPN.getPeerAPs(); + + // Use server preference order + for (String ap : localApl) { + if (protocols.contains(ap)) { + negotiatedValue = ap; + break; + } + } + + if (negotiatedValue == null) { + fatalSE(Alerts.alert_no_application_protocol, + new SSLHandshakeException( + "No matching ALPN values")); + } + applicationProtocol = negotiatedValue; + + } else { + applicationProtocol = ""; + } + } // Otherwise, applicationProtocol will be set by the callback. + + /* + * Always make sure this entire record has been digested before we + * start emitting output, to ensure correct digesting order. + */ + input.digestNow(); + + /* + * FIRST, construct the ServerHello using the options and priorities + * from the ClientHello. Update the (pending) cipher spec as we do + * so, and save the client's version to protect against rollback + * attacks. + * + * There are a bunch of minor tasks here, and one major one: deciding + * if the short or the full handshake sequence will be used. + */ + ServerHello m1 = new ServerHello(); + + clientRequestedVersion = mesg.protocolVersion; + + // select a proper protocol version. + ProtocolVersion selectedVersion = + selectProtocolVersion(clientRequestedVersion); + if (selectedVersion == null || + selectedVersion.v == ProtocolVersion.SSL20Hello.v) { + fatalSE(Alerts.alert_handshake_failure, + "Client requested protocol " + clientRequestedVersion + + " not enabled or not supported"); + } + + handshakeHash.protocolDetermined(selectedVersion); + setVersion(selectedVersion); + + m1.protocolVersion = protocolVersion; + + // + // random ... save client and server values for later use + // in computing the master secret (from pre-master secret) + // and thence the other crypto keys. + // + // NOTE: this use of three inputs to generating _each_ set + // of ciphers slows things down, but it does increase the + // security since each connection in the session can hold + // its own authenticated (and strong) keys. One could make + // creation of a session a rare thing... + // + clnt_random = mesg.clnt_random; + svr_random = new RandomCookie(sslContext.getSecureRandom()); + m1.svr_random = svr_random; + + session = null; // forget about the current session + // + // Here we go down either of two paths: (a) the fast one, where + // the client's asked to rejoin an existing session, and the server + // permits this; (b) the other one, where a new session is created. + // + if (mesg.sessionId.length() != 0) { + // client is trying to resume a session, let's see... + + SSLSessionImpl previous = ((SSLSessionContextImpl)sslContext + .engineGetServerSessionContext()) + .get(mesg.sessionId.getId()); + // + // Check if we can use the fast path, resuming a session. We + // can do so iff we have a valid record for that session, and + // the cipher suite for that session was on the list which the + // client requested, and if we're not forgetting any needed + // authentication on the part of the client. + // + if (previous != null) { + resumingSession = previous.isRejoinable(); + + if (resumingSession) { + ProtocolVersion oldVersion = previous.getProtocolVersion(); + // cannot resume session with different version + if (oldVersion != protocolVersion) { + resumingSession = false; + } + } + + // cannot resume session with different server name indication + if (resumingSession) { + List oldServerNames = + previous.getRequestedServerNames(); + if (clientHelloSNIExt != null) { + if (!clientHelloSNIExt.isIdentical(oldServerNames)) { + resumingSession = false; + } + } else if (!oldServerNames.isEmpty()) { + resumingSession = false; + } + + if (!resumingSession && + debug != null && Debug.isOn("handshake")) { + System.out.println( + "The requested server name indication " + + "is not identical to the previous one"); + } + } + + if (resumingSession && + (doClientAuth == SSLEngineImpl.clauth_required)) { + try { + previous.getPeerPrincipal(); + } catch (SSLPeerUnverifiedException e) { + resumingSession = false; + } + } + + // validate subject identity + if (resumingSession) { + CipherSuite suite = previous.getSuite(); + if (suite.keyExchange == K_KRB5 || + suite.keyExchange == K_KRB5_EXPORT) { + Principal localPrincipal = previous.getLocalPrincipal(); + + Subject subject = null; + try { + subject = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public Subject run() throws Exception { + return + Krb5Helper.getServerSubject(getAccSE()); + }}); + } catch (PrivilegedActionException e) { + subject = null; + if (debug != null && Debug.isOn("session")) { + System.out.println("Attempt to obtain" + + " subject failed!"); + } + } + + if (subject != null) { + // Eliminate dependency on KerberosPrincipal + if (Krb5Helper.isRelated(subject, localPrincipal)) { + if (debug != null && Debug.isOn("session")) + System.out.println("Subject can" + + " provide creds for princ"); + } else { + resumingSession = false; + if (debug != null && Debug.isOn("session")) + System.out.println("Subject cannot" + + " provide creds for princ"); + } + } else { + resumingSession = false; + if (debug != null && Debug.isOn("session")) + System.out.println("Kerberos credentials are" + + " not present in the current Subject;" + + " check if " + + " javax.security.auth.useSubjectAsCreds" + + " system property has been set to false"); + } + } + } + + if (resumingSession) { + CipherSuite suite = previous.getSuite(); + // verify that the ciphersuite from the cached session + // is in the list of client requested ciphersuites and + // we have it enabled + if ((isNegotiable(suite) == false) || + (mesg.getCipherSuites().contains(suite) == false)) { + resumingSession = false; + } else { + // everything looks ok, set the ciphersuite + // this should be done last when we are sure we + // will resume + setCipherSuite(suite); + } + } + + if (resumingSession) { + session = previous; + if (debug != null && + (Debug.isOn("handshake") || Debug.isOn("session"))) { + System.out.println("%% Resuming " + session); + } + } + } + } // else client did not try to resume + + // + // If client hasn't specified a session we can resume, start a + // new one and choose its cipher suite and compression options. + // Unless new session creation is disabled for this connection! + // + if (session == null) { + if (!enableNewSession) { + throw new SSLException("Client did not resume a session"); + } + + supportedCurves = (SupportedEllipticCurvesExtension) + mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES); + + // We only need to handle the "signature_algorithm" extension + // for full handshakes and TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + SignatureAlgorithmsExtension signAlgs = + (SignatureAlgorithmsExtension)mesg.extensions.get( + ExtensionType.EXT_SIGNATURE_ALGORITHMS); + if (signAlgs != null) { + Collection peerSignAlgs = + signAlgs.getSignAlgorithms(); + if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No peer supported signature algorithms"); + } + + Collection + supportedPeerSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + peerSignAlgs); + if (supportedPeerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature and hash algorithm " + + "in common"); + } + + setPeerSupportedSignAlgs(supportedPeerSignAlgs); + } // else, need to use peer implicit supported signature algs + } + + session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL, + getLocalSupportedSignAlgs(), + sslContext.getSecureRandom(), + getHostAddressSE(), getPortSE()); + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs != null) { + session.setPeerSupportedSignatureAlgorithms( + peerSupportedSignAlgs); + } // else, we will set the implicit peer supported signature + // algorithms in chooseCipherSuite() + } + + // set the server name indication in the session + List clientHelloSNI = + Collections.emptyList(); + if (clientHelloSNIExt != null) { + clientHelloSNI = clientHelloSNIExt.getServerNames(); + } + session.setRequestedServerNames(clientHelloSNI); + + // set the handshake session + setHandshakeSessionSE(session); + + // choose cipher suite and corresponding private key + chooseCipherSuite(mesg); + + session.setSuite(cipherSuite); + session.setLocalPrivateKey(privateKey); + + // chooseCompression(mesg); + } else { + // set the handshake session + setHandshakeSessionSE(session); + } + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); + } + + m1.cipherSuite = cipherSuite; + m1.sessionId = session.getSessionId(); + m1.compression_method = session.getCompression(); + + if (secureRenegotiation) { + // For ServerHellos that are initial handshakes, then the + // "renegotiated_connection" field in "renegotiation_info" + // extension is of zero length. + // + // For ServerHellos that are renegotiating, this field contains + // the concatenation of client_verify_data and server_verify_data. + // + // Note that for initial handshakes, both the clientVerifyData + // variable and serverVerifyData variable are of zero length. + HelloExtension serverHelloRI = new RenegotiationInfoExtension( + clientVerifyData, serverVerifyData); + m1.extensions.add(serverHelloRI); + } + + if (!sniMatchers.isEmpty() && clientHelloSNIExt != null) { + // When resuming a session, the server MUST NOT include a + // server_name extension in the server hello. + if (!resumingSession) { + ServerNameExtension serverHelloSNI = new ServerNameExtension(); + m1.extensions.add(serverHelloSNI); + } + } + + // Prepare the ALPN response + if (clientHelloALPN != null) { + List peerAPs = clientHelloALPN.getPeerAPs(); + + // check for a callback function + if (hasAPCallback) { + if (conn != null) { + applicationProtocol = + appProtocolSelectorSSLSocket.apply(conn, peerAPs); + } else { + applicationProtocol = + appProtocolSelectorSSLEngine.apply(engine, peerAPs); + } + } + + // check for no-match and that the selected name was also proposed + // by the TLS peer + if (applicationProtocol == null || + (!applicationProtocol.isEmpty() && + !peerAPs.contains(applicationProtocol))) { + + fatalSE(Alerts.alert_no_application_protocol, + new SSLHandshakeException( + "No matching ALPN values")); + + } else if (!applicationProtocol.isEmpty()) { + m1.extensions.add(new ALPNExtension(applicationProtocol)); + } + } else { + // Nothing was negotiated, returned at end of the handshake + applicationProtocol = ""; + } + + if (debug != null && Debug.isOn("handshake")) { + m1.print(System.out); + System.out.println("Cipher suite: " + session.getSuite()); + } + m1.write(output); + + // + // If we are resuming a session, we finish writing handshake + // messages right now and then finish. + // + if (resumingSession) { + calculateConnectionKeys(session.getMasterSecret()); + sendChangeCipherAndFinish(false); + return; + } + + + /* + * SECOND, write the server Certificate(s) if we need to. + * + * NOTE: while an "anonymous RSA" mode is explicitly allowed by + * the protocol, we can't support it since all of the SSL flavors + * defined in the protocol spec are explicitly stated to require + * using RSA certificates. + */ + if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { + // Server certificates are omitted for Kerberos ciphers + + } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) { + if (certs == null) { + throw new RuntimeException("no certificates"); + } + + CertificateMsg m2 = new CertificateMsg(certs); + + /* + * Set local certs in the SSLSession, output + * debug info, and then actually write to the client. + */ + session.setLocalCertificates(certs); + if (debug != null && Debug.isOn("handshake")) { + m2.print(System.out); + } + m2.write(output); + + // XXX has some side effects with OS TCP buffering, + // leave it out for now + + // let client verify chain in the meantime... + // output.flush(); + } else { + if (certs != null) { + throw new RuntimeException("anonymous keyexchange with certs"); + } + } + + /* + * THIRD, the ServerKeyExchange message ... iff it's needed. + * + * It's usually needed unless there's an encryption-capable + * RSA cert, or a D-H cert. The notable exception is that + * exportable ciphers used with big RSA keys need to downgrade + * to use short RSA keys, even when the key/cert encrypts OK. + */ + + ServerKeyExchange m3; + switch (keyExchange) { + case K_RSA: + case K_KRB5: + case K_KRB5_EXPORT: + // no server key exchange for RSA or KRB5 ciphersuites + m3 = null; + break; + case K_RSA_EXPORT: + if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { + try { + m3 = new RSA_ServerKeyExchange( + tempPublicKey, privateKey, + clnt_random, svr_random, + sslContext.getSecureRandom()); + privateKey = tempPrivateKey; + } catch (GeneralSecurityException e) { + throwSSLException + ("Error generating RSA server key exchange", e); + m3 = null; // make compiler happy + } + } else { + // RSA_EXPORT with short key, don't need ServerKeyExchange + m3 = null; + } + break; + case K_DHE_RSA: + case K_DHE_DSS: + try { + m3 = new DH_ServerKeyExchange(dh, + privateKey, + clnt_random.random_bytes, + svr_random.random_bytes, + sslContext.getSecureRandom(), + preferableSignatureAlgorithm, + protocolVersion); + } catch (GeneralSecurityException e) { + throwSSLException("Error generating DH server key exchange", e); + m3 = null; // make compiler happy + } + break; + case K_DH_ANON: + m3 = new DH_ServerKeyExchange(dh, protocolVersion); + break; + case K_ECDHE_RSA: + case K_ECDHE_ECDSA: + case K_ECDH_ANON: + try { + m3 = new ECDH_ServerKeyExchange(ecdh, + privateKey, + clnt_random.random_bytes, + svr_random.random_bytes, + sslContext.getSecureRandom(), + preferableSignatureAlgorithm, + protocolVersion); + } catch (GeneralSecurityException e) { + throwSSLException( + "Error generating ECDH server key exchange", e); + m3 = null; // make compiler happy + } + break; + case K_ECDH_RSA: + case K_ECDH_ECDSA: + // ServerKeyExchange not used for fixed ECDH + m3 = null; + break; + default: + throw new RuntimeException("internal error: " + keyExchange); + } + if (m3 != null) { + if (debug != null && Debug.isOn("handshake")) { + m3.print(System.out); + } + m3.write(output); + } + + // + // FOURTH, the CertificateRequest message. The details of + // the message can be affected by the key exchange algorithm + // in use. For example, certs with fixed Diffie-Hellman keys + // are only useful with the DH_DSS and DH_RSA key exchange + // algorithms. + // + // Needed only if server requires client to authenticate self. + // Illegal for anonymous flavors, so we need to check that. + // + // CertificateRequest is omitted for Kerberos ciphers + if (doClientAuth != SSLEngineImpl.clauth_none && + keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON && + keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) { + + CertificateRequest m4; + X509Certificate caCerts[]; + + Collection localSignAlgs = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + // We currently use all local upported signature and hash + // algorithms. However, to minimize the computation cost + // of requested hash algorithms, we may use a restricted + // set of signature algorithms in the future. + localSignAlgs = getLocalSupportedSignAlgs(); + if (localSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + Set localHashAlgs = + SignatureAndHashAlgorithm.getHashAlgorithmNames( + localSignAlgs); + if (localHashAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + } + + caCerts = sslContext.getX509TrustManager().getAcceptedIssuers(); + m4 = new CertificateRequest(caCerts, keyExchange, + localSignAlgs, protocolVersion); + + if (debug != null && Debug.isOn("handshake")) { + m4.print(System.out); + } + m4.write(output); + } + + /* + * FIFTH, say ServerHelloDone. + */ + ServerHelloDone m5 = new ServerHelloDone(); + + if (debug != null && Debug.isOn("handshake")) { + m5.print(System.out); + } + m5.write(output); + + /* + * Flush any buffered messages so the client will see them. + * Ideally, all the messages above go in a single network level + * message to the client. Without big Certificate chains, it's + * going to be the common case. + */ + output.flush(); + } + + /* + * Choose cipher suite from among those supported by client. Sets + * the cipherSuite and keyExchange variables. + */ + private void chooseCipherSuite(ClientHello mesg) throws IOException { + CipherSuiteList prefered; + CipherSuiteList proposed; + if (preferLocalCipherSuites) { + prefered = getActiveCipherSuites(); + proposed = mesg.getCipherSuites(); + } else { + prefered = mesg.getCipherSuites(); + proposed = getActiveCipherSuites(); + } + + for (CipherSuite suite : prefered.collection()) { + if (isNegotiable(proposed, suite) == false) { + continue; + } + + if (doClientAuth == SSLEngineImpl.clauth_required) { + if ((suite.keyExchange == K_DH_ANON) || + (suite.keyExchange == K_ECDH_ANON)) { + continue; + } + } + if (trySetCipherSuite(suite) == false) { + continue; + } + return; + } + fatalSE(Alerts.alert_handshake_failure, "no cipher suites in common"); + } + + /** + * Set the given CipherSuite, if possible. Return the result. + * The call succeeds if the CipherSuite is available and we have + * the necessary certificates to complete the handshake. We don't + * check if the CipherSuite is actually enabled. + * + * If successful, this method also generates ephemeral keys if + * required for this ciphersuite. This may take some time, so this + * method should only be called if you really want to use the + * CipherSuite. + * + * This method is called from chooseCipherSuite() in this class. + */ + boolean trySetCipherSuite(CipherSuite suite) { + /* + * If we're resuming a session we know we can + * support this key exchange algorithm and in fact + * have already cached the result of it in + * the session state. + */ + if (resumingSession) { + return true; + } + + if (suite.isNegotiable() == false) { + return false; + } + + // must not negotiate the obsoleted weak cipher suites. + if (protocolVersion.v >= suite.obsoleted) { + return false; + } + + // must not negotiate unsupported cipher suites. + if (protocolVersion.v < suite.supported) { + return false; + } + + KeyExchange keyExchange = suite.keyExchange; + + // null out any existing references + privateKey = null; + certs = null; + dh = null; + tempPrivateKey = null; + tempPublicKey = null; + + Collection supportedSignAlgs = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs != null) { + supportedSignAlgs = peerSupportedSignAlgs; + } else { + SignatureAndHashAlgorithm algorithm = null; + + // we may optimize the performance + switch (keyExchange) { + // If the negotiated key exchange algorithm is one of + // (RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), + // behave as if client had sent the value {sha1,rsa}. + case K_RSA: + case K_DHE_RSA: + case K_DH_RSA: + // case K_RSA_PSK: + case K_ECDH_RSA: + case K_ECDHE_RSA: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.RSA.value, 0); + break; + // If the negotiated key exchange algorithm is one of + // (DHE_DSS, DH_DSS), behave as if the client had + // sent the value {sha1,dsa}. + case K_DHE_DSS: + case K_DH_DSS: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.DSA.value, 0); + break; + // If the negotiated key exchange algorithm is one of + // (ECDH_ECDSA, ECDHE_ECDSA), behave as if the client + // had sent value {sha1,ecdsa}. + case K_ECDH_ECDSA: + case K_ECDHE_ECDSA: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.ECDSA.value, 0); + break; + default: + // no peer supported signature algorithms + } + + if (algorithm == null) { + supportedSignAlgs = + Collections.emptySet(); + } else { + supportedSignAlgs = + new ArrayList(1); + supportedSignAlgs.add(algorithm); + } + + // Sets the peer supported signature algorithm to use in KM + // temporarily. + session.setPeerSupportedSignatureAlgorithms(supportedSignAlgs); + } + } + + switch (keyExchange) { + case K_RSA: + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + break; + case K_RSA_EXPORT: + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + + try { + if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { + if (!setupEphemeralRSAKeys(suite.exportable)) { + return false; + } + } + } catch (RuntimeException e) { + // could not determine keylength, ignore key + return false; + } + break; + case K_DHE_RSA: + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "RSA", privateKey); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + + setupEphemeralDHKeys(suite.exportable, privateKey); + break; + case K_ECDHE_RSA: + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "RSA", privateKey); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + + if (setupEphemeralECDHKeys() == false) { + return false; + } + break; + case K_DHE_DSS: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "DSA"); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + + // need DSS certs for authentication + if (setupPrivateKeyAndChain("DSA") == false) { + return false; + } + + setupEphemeralDHKeys(suite.exportable, privateKey); + break; + case K_ECDHE_ECDSA: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "ECDSA"); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + + // need EC cert signed using EC + if (setupPrivateKeyAndChain("EC_EC") == false) { + return false; + } + if (setupEphemeralECDHKeys() == false) { + return false; + } + break; + case K_ECDH_RSA: + // need EC cert signed using RSA + if (setupPrivateKeyAndChain("EC_RSA") == false) { + return false; + } + setupStaticECDHKeys(); + break; + case K_ECDH_ECDSA: + // need EC cert signed using EC + if (setupPrivateKeyAndChain("EC_EC") == false) { + return false; + } + setupStaticECDHKeys(); + break; + case K_KRB5: + case K_KRB5_EXPORT: + // need Kerberos Key + if (!setupKerberosKeys()) { + return false; + } + break; + case K_DH_ANON: + // no certs needed for anonymous + setupEphemeralDHKeys(suite.exportable, null); + break; + case K_ECDH_ANON: + // no certs needed for anonymous + if (setupEphemeralECDHKeys() == false) { + return false; + } + break; + default: + // internal error, unknown key exchange + throw new RuntimeException("Unrecognized cipherSuite: " + suite); + } + setCipherSuite(suite); + + // set the peer implicit supported signature algorithms + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs == null) { + setPeerSupportedSignAlgs(supportedSignAlgs); + // we had alreay update the session + } + } + return true; + } + + /* + * Get some "ephemeral" RSA keys for this context. This means + * generating them if it's not already been done. + * + * Note that we currently do not implement any ciphersuites that use + * strong ephemeral RSA. (We do not support the EXPORT1024 ciphersuites + * and standard RSA ciphersuites prohibit ephemeral mode for some reason) + * This means that export is always true and 512 bit keys are generated. + */ + private boolean setupEphemeralRSAKeys(boolean export) { + KeyPair kp = sslContext.getEphemeralKeyManager(). + getRSAKeyPair(export, sslContext.getSecureRandom()); + if (kp == null) { + return false; + } else { + tempPublicKey = kp.getPublic(); + tempPrivateKey = kp.getPrivate(); + return true; + } + } + + /* + * Acquire some "ephemeral" Diffie-Hellman keys for this handshake. + * We don't reuse these, for improved forward secrecy. + */ + private void setupEphemeralDHKeys(boolean export, Key key) { + /* + * 768 bits ephemeral DH private keys were used to be used in + * ServerKeyExchange except that exportable ciphers max out at 512 + * bits modulus values. We still adhere to this behavior in legacy + * mode (system property "jdk.tls.ephemeralDHKeySize" is defined + * as "legacy"). + * + * Old JDK (JDK 7 and previous) releases don't support DH keys bigger + * than 1024 bits. We have to consider the compatibility requirement. + * 1024 bits DH key is always used for non-exportable cipher suites + * in default mode (system property "jdk.tls.ephemeralDHKeySize" + * is not defined). + * + * However, if applications want more stronger strength, setting + * system property "jdk.tls.ephemeralDHKeySize" to "matched" + * is a workaround to use ephemeral DH key which size matches the + * corresponding authentication key. For example, if the public key + * size of an authentication certificate is 2048 bits, then the + * ephemeral DH key size should be 2048 bits accordingly unless + * the cipher suite is exportable. This key sizing scheme keeps + * the cryptographic strength consistent between authentication + * keys and key-exchange keys. + * + * Applications may also want to customize the ephemeral DH key size + * to a fixed length for non-exportable cipher suites. This can be + * approached by setting system property "jdk.tls.ephemeralDHKeySize" + * to a valid positive integer between 1024 and 2048 bits, inclusive. + * + * Note that the minimum acceptable key size is 1024 bits except + * exportable cipher suites or legacy mode. + * + * Note that the maximum acceptable key size is 2048 bits because + * DH keys bigger than 2048 are not always supported by underlying + * JCE providers. + * + * Note that per RFC 2246, the key size limit of DH is 512 bits for + * exportable cipher suites. Because of the weakness, exportable + * cipher suites are deprecated since TLS v1.1 and they are not + * enabled by default in Oracle provider. The legacy behavior is + * reserved and 512 bits DH key is always used for exportable + * cipher suites. + */ + int keySize = export ? 512 : 1024; // default mode + if (!export) { + if (useLegacyEphemeralDHKeys) { // legacy mode + keySize = 768; + } else if (useSmartEphemeralDHKeys) { // matched mode + if (key != null) { + int ks = KeyUtil.getKeySize(key); + // Note that SunJCE provider only supports 2048 bits DH + // keys bigger than 1024. Please DON'T use value other + // than 1024 and 2048 at present. We may improve the + // underlying providers and key size here in the future. + // + // keySize = ks <= 1024 ? 1024 : (ks >= 2048 ? 2048 : ks); + keySize = ks <= 1024 ? 1024 : 2048; + } // Otherwise, anonymous cipher suites, 1024-bit is used. + } else if (customizedDHKeySize > 0) { // customized mode + keySize = customizedDHKeySize; + } + } + + dh = new DHCrypt(keySize, sslContext.getSecureRandom()); + } + + // Setup the ephemeral ECDH parameters. + // If we cannot continue because we do not support any of the curves that + // the client requested, return false. Otherwise (all is well), return true. + private boolean setupEphemeralECDHKeys() { + int index = -1; + if (supportedCurves != null) { + // if the client sent the supported curves extension, pick the + // first one that we support; + for (int curveId : supportedCurves.curveIds()) { + if (SupportedEllipticCurvesExtension.isSupported(curveId)) { + index = curveId; + break; + } + } + if (index < 0) { + // no match found, cannot use this ciphersuite + return false; + } + } else { + // pick our preference + index = SupportedEllipticCurvesExtension.DEFAULT.curveIds()[0]; + } + String oid = SupportedEllipticCurvesExtension.getCurveOid(index); + ecdh = new ECDHCrypt(oid, sslContext.getSecureRandom()); + return true; + } + + private void setupStaticECDHKeys() { + // don't need to check whether the curve is supported, already done + // in setupPrivateKeyAndChain(). + ecdh = new ECDHCrypt(privateKey, certs[0].getPublicKey()); + } + + /** + * Retrieve the server key and certificate for the specified algorithm + * from the KeyManager and set the instance variables. + * + * @return true if successful, false if not available or invalid + */ + private boolean setupPrivateKeyAndChain(String algorithm) { + X509ExtendedKeyManager km = sslContext.getX509KeyManager(); + String alias; + if (conn != null) { + alias = km.chooseServerAlias(algorithm, null, conn); + } else { + alias = km.chooseEngineServerAlias(algorithm, null, engine); + } + if (alias == null) { + return false; + } + PrivateKey tempPrivateKey = km.getPrivateKey(alias); + if (tempPrivateKey == null) { + return false; + } + X509Certificate[] tempCerts = km.getCertificateChain(alias); + if ((tempCerts == null) || (tempCerts.length == 0)) { + return false; + } + String keyAlgorithm = algorithm.split("_")[0]; + PublicKey publicKey = tempCerts[0].getPublicKey(); + if ((tempPrivateKey.getAlgorithm().equals(keyAlgorithm) == false) + || (publicKey.getAlgorithm().equals(keyAlgorithm) == false)) { + return false; + } + // For ECC certs, check whether we support the EC domain parameters. + // If the client sent a SupportedEllipticCurves ClientHello extension, + // check against that too. + if (keyAlgorithm.equals("EC")) { + if (publicKey instanceof ECPublicKey == false) { + return false; + } + ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); + int index = SupportedEllipticCurvesExtension.getCurveIndex(params); + if (SupportedEllipticCurvesExtension.isSupported(index) == false) { + return false; + } + if ((supportedCurves != null) && !supportedCurves.contains(index)) { + return false; + } + } + this.privateKey = tempPrivateKey; + this.certs = tempCerts; + return true; + } + + /** + * Retrieve the Kerberos key for the specified server principal + * from the JAAS configuration file. + * + * @return true if successful, false if not available or invalid + */ + private boolean setupKerberosKeys() { + if (serviceCreds != null) { + return true; + } + try { + final AccessControlContext acc = getAccSE(); + serviceCreds = AccessController.doPrivileged( + // Eliminate dependency on KerberosKey + new PrivilegedExceptionAction() { + @Override + public Object run() throws Exception { + // get kerberos key for the default principal + return Krb5Helper.getServiceCreds(acc); + }}); + + // check permission to access and use the secret key of the + // Kerberized "host" service + if (serviceCreds != null) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Using Kerberos creds"); + } + String serverPrincipal = + Krb5Helper.getServerPrincipalName(serviceCreds); + if (serverPrincipal != null) { + // When service is bound, we check ASAP. Otherwise, + // will check after client request is received + // in in Kerberos ClientKeyExchange + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + // Eliminate dependency on ServicePermission + sm.checkPermission(Krb5Helper.getServicePermission( + serverPrincipal, "accept"), acc); + } + } catch (SecurityException se) { + serviceCreds = null; + // Do not destroy keys. Will affect Subject + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Permission to access Kerberos" + + " secret key denied"); + } + return false; + } + } + } + return serviceCreds != null; + } catch (PrivilegedActionException e) { + // Likely exception here is LoginExceptin + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Attempt to obtain Kerberos key failed: " + + e.toString()); + } + return false; + } + } + + /* + * For Kerberos ciphers, the premaster secret is encrypted using + * the session key. See RFC 2712. + */ + private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg) + throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + // Record the principals involved in exchange + session.setPeerPrincipal(mesg.getPeerPrincipal()); + session.setLocalPrincipal(mesg.getLocalPrincipal()); + + byte[] b = mesg.getUnencryptedPreMasterSecret(); + return new SecretKeySpec(b, "TlsPremasterSecret"); + } + + /* + * Diffie Hellman key exchange is used when the server presented + * D-H parameters in its certificate (signed using RSA or DSS/DSA), + * or else the server presented no certificate but sent D-H params + * in a ServerKeyExchange message. Use of D-H is specified by the + * cipher suite chosen. + * + * The message optionally contains the client's D-H public key (if + * it wasn't not sent in a client certificate). As always with D-H, + * if a client and a server have each other's D-H public keys and + * they use common algorithm parameters, they have a shared key + * that's derived via the D-H calculation. That key becomes the + * pre-master secret. + */ + private SecretKey clientKeyExchange(DHClientKeyExchange mesg) + throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + return dh.getAgreedSecret(mesg.getClientPublicKey(), false); + } + + private SecretKey clientKeyExchange(ECDHClientKeyExchange mesg) + throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + return ecdh.getAgreedSecret(mesg.getEncodedPoint()); + } + + /* + * Client wrote a message to verify the certificate it sent earlier. + * + * Note that this certificate isn't involved in key exchange. Client + * authentication messages are included in the checksums used to + * validate the handshake (e.g. Finished messages). Other than that, + * the _exact_ identity of the client is less fundamental to protocol + * security than its role in selecting keys via the pre-master secret. + */ + private void clientCertificateVerify(CertificateVerify mesg) + throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + SignatureAndHashAlgorithm signAlg = + mesg.getPreferableSignatureAlgorithm(); + if (signAlg == null) { + throw new SSLHandshakeException( + "Illegal CertificateVerify message"); + } + + String hashAlg = + SignatureAndHashAlgorithm.getHashAlgorithmName(signAlg); + if (hashAlg == null || hashAlg.length() == 0) { + throw new SSLHandshakeException( + "No supported hash algorithm"); + } + } + + try { + PublicKey publicKey = + session.getPeerCertificates()[0].getPublicKey(); + + boolean valid = mesg.verify(protocolVersion, handshakeHash, + publicKey, session.getMasterSecret()); + if (valid == false) { + fatalSE(Alerts.alert_bad_certificate, + "certificate verify message signature error"); + } + } catch (GeneralSecurityException e) { + fatalSE(Alerts.alert_bad_certificate, + "certificate verify format error", e); + } + + // reset the flag for clientCertificateVerify message + needClientVerify = false; + } + + + /* + * Client writes "finished" at the end of its handshake, after cipher + * spec is changed. We verify it and then send ours. + * + * When we're resuming a session, we'll have already sent our own + * Finished message so just the verification is needed. + */ + private void clientFinished(Finished mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + /* + * Verify if client did send the certificate when client + * authentication was required, otherwise server should not proceed + */ + if (doClientAuth == SSLEngineImpl.clauth_required) { + // get X500Principal of the end-entity certificate for X509-based + // ciphersuites, or Kerberos principal for Kerberos ciphersuites + session.getPeerPrincipal(); + } + + /* + * Verify if client did send clientCertificateVerify message following + * the client Certificate, otherwise server should not proceed + */ + if (needClientVerify) { + fatalSE(Alerts.alert_handshake_failure, + "client did not send certificate verify message"); + } + + /* + * Verify the client's message with the "before" digest of messages, + * and forget about continuing to use that digest. + */ + boolean verified = mesg.verify(handshakeHash, Finished.CLIENT, + session.getMasterSecret()); + + if (!verified) { + fatalSE(Alerts.alert_handshake_failure, + "client 'finished' message doesn't verify"); + // NOTREACHED + } + + /* + * save client verify data for secure renegotiation + */ + if (secureRenegotiation) { + clientVerifyData = mesg.getVerifyData(); + } + + /* + * OK, it verified. If we're doing the full handshake, add that + * "Finished" message to the hash of handshake messages, then send + * the change_cipher_spec and Finished message. + */ + if (!resumingSession) { + input.digestNow(); + sendChangeCipherAndFinish(true); + } + + /* + * Update the session cache only after the handshake completed, else + * we're open to an attack against a partially completed handshake. + */ + session.setLastAccessedTime(System.currentTimeMillis()); + if (!resumingSession && session.isRejoinable()) { + ((SSLSessionContextImpl)sslContext.engineGetServerSessionContext()) + .put(session); + if (debug != null && Debug.isOn("session")) { + System.out.println( + "%% Cached server session: " + session); + } + } else if (!resumingSession && + debug != null && Debug.isOn("session")) { + System.out.println( + "%% Didn't cache non-resumable server session: " + + session); + } + } + + /* + * Compute finished message with the "server" digest (and then forget + * about that digest, it can't be used again). + */ + private void sendChangeCipherAndFinish(boolean finishedTag) + throws IOException { + + output.flush(); + + Finished mesg = new Finished(protocolVersion, handshakeHash, + Finished.SERVER, session.getMasterSecret(), cipherSuite); + + /* + * Send the change_cipher_spec record; then our Finished handshake + * message will be the last handshake message. Flush, and now we + * are ready for application data!! + */ + sendChangeCipherSpec(mesg, finishedTag); + + /* + * save server verify data for secure renegotiation + */ + if (secureRenegotiation) { + serverVerifyData = mesg.getVerifyData(); + } + + /* + * Update state machine so client MUST send 'finished' next + * The update should only take place if it is not in the fast + * handshake mode since the server has to wait for a finished + * message from the client. + */ + if (finishedTag) { + state = HandshakeMessage.ht_finished; + } + } + + + /* + * Returns a HelloRequest message to kickstart renegotiations + */ + @Override + HandshakeMessage getKickstartMessage() { + return new HelloRequest(); + } + + + /* + * Fault detected during handshake. + */ + @Override + void handshakeAlert(byte description) throws SSLProtocolException { + + String message = Alerts.alertDescription(description); + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("SSL -- handshake alert: " + + message); + } + + /* + * It's ok to get a no_certificate alert from a client of which + * we *requested* authentication information. + * However, if we *required* it, then this is not acceptable. + * + * Anyone calling getPeerCertificates() on the + * session will get an SSLPeerUnverifiedException. + */ + if ((description == Alerts.alert_no_certificate) && + (doClientAuth == SSLEngineImpl.clauth_requested)) { + return; + } + + throw new SSLProtocolException("handshake alert: " + message); + } + + /* + * RSA key exchange is normally used. The client encrypts a "pre-master + * secret" with the server's public key, from the Certificate (or else + * ServerKeyExchange) message that was sent to it by the server. That's + * decrypted using the private key before we get here. + */ + private SecretKey clientKeyExchange(RSAClientKeyExchange mesg) + throws IOException { + + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + return mesg.preMaster; + } + + /* + * Verify the certificate sent by the client. We'll only get one if we + * sent a CertificateRequest to request client authentication. If we + * are in TLS mode, the client may send a message with no certificates + * to indicate it does not have an appropriate chain. (In SSLv3 mode, + * it would send a no certificate alert). + */ + private void clientCertificate(CertificateMsg mesg) throws IOException { + if (debug != null && Debug.isOn("handshake")) { + mesg.print(System.out); + } + + X509Certificate[] peerCerts = mesg.getCertificateChain(); + + if (peerCerts.length == 0) { + /* + * If the client authentication is only *REQUESTED* (e.g. + * not *REQUIRED*, this is an acceptable condition.) + */ + if (doClientAuth == SSLEngineImpl.clauth_requested) { + return; + } else { + fatalSE(Alerts.alert_bad_certificate, + "null cert chain"); + } + } + + // ask the trust manager to verify the chain + X509TrustManager tm = sslContext.getX509TrustManager(); + + try { + // find out the types of client authentication used + PublicKey key = peerCerts[0].getPublicKey(); + String keyAlgorithm = key.getAlgorithm(); + String authType; + if (keyAlgorithm.equals("RSA")) { + authType = "RSA"; + } else if (keyAlgorithm.equals("DSA")) { + authType = "DSA"; + } else if (keyAlgorithm.equals("EC")) { + authType = "EC"; + } else { + // unknown public key type + authType = "UNKNOWN"; + } + + if (tm instanceof X509ExtendedTrustManager) { + if (conn != null) { + ((X509ExtendedTrustManager)tm).checkClientTrusted( + peerCerts.clone(), + authType, + conn); + } else { + ((X509ExtendedTrustManager)tm).checkClientTrusted( + peerCerts.clone(), + authType, + engine); + } + } else { + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); + } + } catch (CertificateException e) { + // This will throw an exception, so include the original error. + fatalSE(Alerts.alert_certificate_unknown, e); + } + // set the flag for clientCertificateVerify message + needClientVerify = true; + + session.setPeerCertificates(peerCerts); + } +} diff --git a/src/sun/security/ssl/ServerNameExtension.java b/src/sun/security/ssl/ServerNameExtension.java new file mode 100644 index 00000000..3707849e --- /dev/null +++ b/src/sun/security/ssl/ServerNameExtension.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIMatcher; +import javax.net.ssl.SNIServerName; +import javax.net.ssl.SSLProtocolException; +import javax.net.ssl.StandardConstants; + +/* + * [RFC 4366/6066] To facilitate secure connections to servers that host + * multiple 'virtual' servers at a single underlying network address, clients + * MAY include an extension of type "server_name" in the (extended) client + * hello. The "extension_data" field of this extension SHALL contain + * "ServerNameList" where: + * + * struct { + * NameType name_type; + * select (name_type) { + * case host_name: HostName; + * } name; + * } ServerName; + * + * enum { + * host_name(0), (255) + * } NameType; + * + * opaque HostName<1..2^16-1>; + * + * struct { + * ServerName server_name_list<1..2^16-1> + * } ServerNameList; + */ +final class ServerNameExtension extends HelloExtension { + + // For backward compatibility, all future data structures associated with + // new NameTypes MUST begin with a 16-bit length field. + final static int NAME_HEADER_LENGTH = 3; // NameType: 1 byte + // Name length: 2 bytes + private Map sniMap; + private int listLength; // ServerNameList length + + // constructor for ServerHello + ServerNameExtension() throws IOException { + super(ExtensionType.EXT_SERVER_NAME); + + listLength = 0; + sniMap = Collections.emptyMap(); + } + + // constructor for ClientHello + ServerNameExtension(List serverNames) + throws IOException { + super(ExtensionType.EXT_SERVER_NAME); + + listLength = 0; + sniMap = new LinkedHashMap<>(); + for (SNIServerName serverName : serverNames) { + // check for duplicated server name type + if (sniMap.put(serverName.getType(), serverName) != null) { + // unlikely to happen, but in case ... + throw new RuntimeException( + "Duplicated server name of type " + serverName.getType()); + } + + listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH; + } + + // This constructor is used for ClientHello only. Empty list is + // not allowed in client mode. + if (listLength == 0) { + throw new RuntimeException("The ServerNameList cannot be empty"); + } + } + + // constructor for ServerHello for parsing SNI extension + ServerNameExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_SERVER_NAME); + + int remains = len; + if (len >= 2) { // "server_name" extension in ClientHello + listLength = s.getInt16(); // ServerNameList length + if (listLength == 0 || listLength + 2 != len) { + throw new SSLProtocolException( + "Invalid " + type + " extension"); + } + + remains -= 2; + sniMap = new LinkedHashMap<>(); + while (remains > 0) { + int code = s.getInt8(); // NameType + + // HostName (length read in getBytes16); + byte[] encoded = s.getBytes16(); + SNIServerName serverName; + switch (code) { + case StandardConstants.SNI_HOST_NAME: + if (encoded.length == 0) { + throw new SSLProtocolException( + "Empty HostName in server name indication"); + } + try { + serverName = new SNIHostName(encoded); + } catch (IllegalArgumentException iae) { + SSLProtocolException spe = new SSLProtocolException( + "Illegal server name, type=host_name(" + + code + "), name=" + + (new String(encoded, StandardCharsets.UTF_8)) + + ", value=" + Debug.toString(encoded)); + spe.initCause(iae); + throw spe; + } + break; + default: + try { + serverName = new UnknownServerName(code, encoded); + } catch (IllegalArgumentException iae) { + SSLProtocolException spe = new SSLProtocolException( + "Illegal server name, type=(" + code + + "), value=" + Debug.toString(encoded)); + spe.initCause(iae); + throw spe; + } + } + // check for duplicated server name type + if (sniMap.put(serverName.getType(), serverName) != null) { + throw new SSLProtocolException( + "Duplicated server name of type " + + serverName.getType()); + } + + remains -= encoded.length + NAME_HEADER_LENGTH; + } + } else if (len == 0) { // "server_name" extension in ServerHello + listLength = 0; + sniMap = Collections.emptyMap(); + } + + if (remains != 0) { + throw new SSLProtocolException("Invalid server_name extension"); + } + } + + List getServerNames() { + if (sniMap != null && !sniMap.isEmpty()) { + return Collections.unmodifiableList( + new ArrayList<>(sniMap.values())); + } + + return Collections.emptyList(); + } + + /* + * Is the extension recognized by the corresponding matcher? + * + * This method is used to check whether the server name indication can + * be recognized by the server name matchers. + * + * Per RFC 6066, if the server understood the ClientHello extension but + * does not recognize the server name, the server SHOULD take one of two + * actions: either abort the handshake by sending a fatal-level + * unrecognized_name(112) alert or continue the handshake. + * + * If there is an instance of SNIMatcher defined for a particular name + * type, it must be used to perform match operations on the server name. + */ + boolean isMatched(Collection matchers) { + if (sniMap != null && !sniMap.isEmpty()) { + for (SNIMatcher matcher : matchers) { + SNIServerName sniName = sniMap.get(matcher.getType()); + if (sniName != null && (!matcher.matches(sniName))) { + return false; + } + } + } + + return true; + } + + /* + * Is the extension is identical to a server name list? + * + * This method is used to check the server name indication during session + * resumption. + * + * Per RFC 6066, when the server is deciding whether or not to accept a + * request to resume a session, the contents of a server_name extension + * MAY be used in the lookup of the session in the session cache. The + * client SHOULD include the same server_name extension in the session + * resumption request as it did in the full handshake that established + * the session. A server that implements this extension MUST NOT accept + * the request to resume the session if the server_name extension contains + * a different name. Instead, it proceeds with a full handshake to + * establish a new session. When resuming a session, the server MUST NOT + * include a server_name extension in the server hello. + */ + boolean isIdentical(List other) { + if (other.size() == sniMap.size()) { + for(SNIServerName sniInOther : other) { + SNIServerName sniName = sniMap.get(sniInOther.getType()); + if (sniName == null || !sniInOther.equals(sniName)) { + return false; + } + } + + return true; + } + + return false; + } + + @Override + int length() { + return listLength == 0 ? 4 : 6 + listLength; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + if (listLength == 0) { + s.putInt16(listLength); // in ServerHello, empty extension_data + } else { + s.putInt16(listLength + 2); // length of extension_data + s.putInt16(listLength); // length of ServerNameList + + for (SNIServerName sniName : sniMap.values()) { + s.putInt8(sniName.getType()); // server name type + s.putBytes16(sniName.getEncoded()); // server name value + } + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + for (SNIServerName sniName : sniMap.values()) { + buffer.append("[" + sniName + "]"); + } + + return "Extension " + type + ", server_name: " + buffer; + } + + private static class UnknownServerName extends SNIServerName { + UnknownServerName(int code, byte[] encoded) { + super(code, encoded); + } + } + +} diff --git a/src/sun/security/ssl/SessionId.java b/src/sun/security/ssl/SessionId.java new file mode 100644 index 00000000..39bdd673 --- /dev/null +++ b/src/sun/security/ssl/SessionId.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.security.SecureRandom; + +/** + * Encapsulates an SSL session ID. SSL Session IDs are not reused by + * servers during the lifetime of any sessions it created. Sessions may + * be used by many connections, either concurrently (for example, two + * connections to a web server at the same time) or sequentially (over as + * long a time period as is allowed by a given server). + * + * @author Satish Dharmaraj + * @author David Brownell + */ +final +class SessionId +{ + private byte sessionId []; // max 32 bytes + + /** Constructs a new session ID ... perhaps for a rejoinable session */ + SessionId (boolean isRejoinable, SecureRandom generator) + { + if (isRejoinable) + // this will be unique, it's a timestamp plus much randomness + sessionId = new RandomCookie (generator).random_bytes; + else + sessionId = new byte [0]; + } + + /** Constructs a session ID from a byte array (max size 32 bytes) */ + SessionId (byte sessionId []) + { this.sessionId = sessionId; } + + /** Returns the length of the ID, in bytes */ + int length () + { return sessionId.length; } + + /** Returns the bytes in the ID. May be an empty array. */ + byte [] getId () + { + return sessionId.clone (); + } + + /** Returns the ID as a string */ + @Override + public String toString () + { + int len = sessionId.length; + StringBuffer s = new StringBuffer (10 + 2 * len); + + s.append ("{"); + for (int i = 0; i < len; i++) { + s.append (0x0ff & sessionId [i]); + if (i != (len - 1)) + s.append (", "); + } + s.append ("}"); + return s.toString (); + } + + + /** Returns a value which is the same for session IDs which are equal */ + @Override + public int hashCode () + { + int retval = 0; + + for (int i = 0; i < sessionId.length; i++) + retval += sessionId [i]; + return retval; + } + + /** Returns true if the parameter is the same session ID */ + @Override + public boolean equals (Object obj) + { + if (!(obj instanceof SessionId)) + return false; + + SessionId s = (SessionId) obj; + byte b [] = s.getId (); + + if (b.length != sessionId.length) + return false; + for (int i = 0; i < sessionId.length; i++) { + if (b [i] != sessionId [i]) + return false; + } + return true; + } +} diff --git a/src/sun/security/ssl/SignatureAlgorithmsExtension.java b/src/sun/security/ssl/SignatureAlgorithmsExtension.java new file mode 100644 index 00000000..f45720e0 --- /dev/null +++ b/src/sun/security/ssl/SignatureAlgorithmsExtension.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +import javax.net.ssl.SSLProtocolException; + +/* + * [RFC5246] The client uses the "signature_algorithms" extension to + * indicate to the server which signature/hash algorithm pairs may be + * used in digital signatures. The "extension_data" field of this + * extension contains a "supported_signature_algorithms" value. + * + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + */ +final class SignatureAlgorithmsExtension extends HelloExtension { + + private Collection algorithms; + private int algorithmsLen; // length of supported_signature_algorithms + + SignatureAlgorithmsExtension( + Collection signAlgs) { + + super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + + algorithms = new ArrayList(signAlgs); + algorithmsLen = + SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); + } + + SignatureAlgorithmsExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + + algorithmsLen = s.getInt16(); + if (algorithmsLen == 0 || algorithmsLen + 2 != len) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + algorithms = new ArrayList(); + int remains = algorithmsLen; + int sequence = 0; + while (remains > 1) { // needs at least two bytes + int hash = s.getInt8(); // hash algorithm + int signature = s.getInt8(); // signature algorithm + + SignatureAndHashAlgorithm algorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence); + algorithms.add(algorithm); + remains -= 2; // one byte for hash, one byte for signature + } + + if (remains != 0) { + throw new SSLProtocolException("Invalid server_name extension"); + } + } + + Collection getSignAlgorithms() { + return algorithms; + } + + @Override + int length() { + return 6 + algorithmsLen; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(algorithmsLen + 2); + s.putInt16(algorithmsLen); + + for (SignatureAndHashAlgorithm algorithm : algorithms) { + s.putInt8(algorithm.getHashValue()); // HashAlgorithm + s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + boolean opened = false; + for (SignatureAndHashAlgorithm signAlg : algorithms) { + if (opened) { + buffer.append(", " + signAlg.getAlgorithmName()); + } else { + buffer.append(signAlg.getAlgorithmName()); + opened = true; + } + } + + return "Extension " + type + ", signature_algorithms: " + buffer; + } +} + diff --git a/src/sun/security/ssl/SignatureAndHashAlgorithm.java b/src/sun/security/ssl/SignatureAndHashAlgorithm.java new file mode 100644 index 00000000..739a1c48 --- /dev/null +++ b/src/sun/security/ssl/SignatureAndHashAlgorithm.java @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.PrivateKey; + +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.EnumSet; +import java.util.TreeMap; +import java.util.Collection; +import java.util.Collections; +import java.util.ArrayList; + +import sun.security.util.KeyUtil; + +/** + * Signature and hash algorithm. + * + * [RFC5246] The client uses the "signature_algorithms" extension to + * indicate to the server which signature/hash algorithm pairs may be + * used in digital signatures. The "extension_data" field of this + * extension contains a "supported_signature_algorithms" value. + * + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ +final class SignatureAndHashAlgorithm { + + // minimum priority for default enabled algorithms + final static int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0; + + // performance optimization + private final static Set SIGNATURE_PRIMITIVE_SET = + Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE)); + + // supported pairs of signature and hash algorithm + private final static Map supportedMap; + private final static Map priorityMap; + + // the hash algorithm + private HashAlgorithm hash; + + // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA + private int id; + + // the standard algorithm name, for example "SHA512withECDSA" + private String algorithm; + + // Priority for the preference order. The lower the better. + // + // If the algorithm is unsupported, its priority should be bigger + // than SUPPORTED_ALG_PRIORITY_MAX_NUM. + private int priority; + + // constructor for supported algorithm + private SignatureAndHashAlgorithm(HashAlgorithm hash, + SignatureAlgorithm signature, String algorithm, int priority) { + this.hash = hash; + this.algorithm = algorithm; + this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF); + this.priority = priority; + } + + // constructor for unsupported algorithm + private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) { + this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF); + this.algorithm = algorithm; + this.id = id; + + // add one more to the sequence number, in case that the number is zero + this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1; + } + + // Note that we do not use the sequence argument for supported algorithms, + // so please don't sort by comparing the objects read from handshake + // messages. + static SignatureAndHashAlgorithm valueOf(int hash, + int signature, int sequence) { + hash &= 0xFF; + signature &= 0xFF; + + int id = (hash << 8) | signature; + SignatureAndHashAlgorithm signAlg = supportedMap.get(id); + if (signAlg == null) { + // unsupported algorithm + signAlg = new SignatureAndHashAlgorithm( + "Unknown (hash:0x" + Integer.toString(hash, 16) + + ", signature:0x" + Integer.toString(signature, 16) + ")", + id, sequence); + } + + return signAlg; + } + + int getHashValue() { + return (id >> 8) & 0xFF; + } + + int getSignatureValue() { + return id & 0xFF; + } + + String getAlgorithmName() { + return algorithm; + } + + // return the size of a SignatureAndHashAlgorithm structure in TLS record + static int sizeInRecord() { + return 2; + } + + // Get local supported algorithm collection complying to + // algorithm constraints + static Collection + getSupportedAlgorithms(AlgorithmConstraints constraints) { + + Collection supported = new ArrayList<>(); + synchronized (priorityMap) { + for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM && + constraints.permits(SIGNATURE_PRIMITIVE_SET, + sigAlg.algorithm, null)) { + supported.add(sigAlg); + } + } + } + + return supported; + } + + // Get supported algorithm collection from an untrusted collection + static Collection getSupportedAlgorithms( + Collection algorithms ) { + Collection supported = new ArrayList<>(); + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) { + supported.add(sigAlg); + } + } + + return supported; + } + + static String[] getAlgorithmNames( + Collection algorithms) { + ArrayList algorithmNames = new ArrayList<>(); + if (algorithms != null) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + algorithmNames.add(sigAlg.algorithm); + } + } + + String[] array = new String[algorithmNames.size()]; + return algorithmNames.toArray(array); + } + + static Set getHashAlgorithmNames( + Collection algorithms) { + Set algorithmNames = new HashSet<>(); + if (algorithms != null) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.hash.value > 0) { + algorithmNames.add(sigAlg.hash.standardName); + } + } + } + + return algorithmNames; + } + + static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) { + return algorithm.hash.standardName; + } + + private static void supports(HashAlgorithm hash, + SignatureAlgorithm signature, String algorithm, int priority) { + + SignatureAndHashAlgorithm pair = + new SignatureAndHashAlgorithm(hash, signature, algorithm, priority); + if (supportedMap.put(pair.id, pair) != null) { + throw new RuntimeException( + "Duplicate SignatureAndHashAlgorithm definition, id: " + + pair.id); + } + if (priorityMap.put(pair.priority, pair) != null) { + throw new RuntimeException( + "Duplicate SignatureAndHashAlgorithm definition, priority: " + + pair.priority); + } + } + + static SignatureAndHashAlgorithm getPreferableAlgorithm( + Collection algorithms, String expected) { + + return SignatureAndHashAlgorithm.getPreferableAlgorithm( + algorithms, expected, null); + } + + static SignatureAndHashAlgorithm getPreferableAlgorithm( + Collection algorithms, + String expected, PrivateKey signingKey) { + + if (expected == null && !algorithms.isEmpty()) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) { + return sigAlg; + } + } + + return null; // no supported algorithm + } + + if (expected == null ) { + return null; // no expected algorithm, no supported algorithm + } + + /* + * Need to check RSA key length to match the length of hash value + */ + int maxDigestLength = Integer.MAX_VALUE; + if (signingKey != null && + "rsa".equalsIgnoreCase(signingKey.getAlgorithm()) && + expected.equalsIgnoreCase("rsa")) { + /* + * RSA keys of 512 bits have been shown to be practically + * breakable, it does not make much sense to use the strong + * hash algorithm for keys whose key size less than 512 bits. + * So it is not necessary to caculate the required max digest + * length exactly. + * + * If key size is greater than or equals to 768, there is no max + * digest length limitation in currect implementation. + * + * If key size is greater than or equals to 512, but less than + * 768, the digest length should be less than or equal to 32 bytes. + * + * If key size is less than 512, the digest length should be + * less than or equal to 20 bytes. + */ + int keySize = KeyUtil.getKeySize(signingKey); + if (keySize >= 768) { + maxDigestLength = HashAlgorithm.SHA512.length; + } else if ((keySize >= 512) && (keySize < 768)) { + maxDigestLength = HashAlgorithm.SHA256.length; + } else if ((keySize > 0) && (keySize < 512)) { + maxDigestLength = HashAlgorithm.SHA1.length; + } // Otherwise, cannot determine the key size, prefer the most + // preferable hash algorithm. + } + + for (SignatureAndHashAlgorithm algorithm : algorithms) { + int signValue = algorithm.id & 0xFF; + if (expected.equalsIgnoreCase("rsa") && + signValue == SignatureAlgorithm.RSA.value) { + if (algorithm.hash.length <= maxDigestLength) { + return algorithm; + } + } else if ( + (expected.equalsIgnoreCase("dsa") && + signValue == SignatureAlgorithm.DSA.value) || + (expected.equalsIgnoreCase("ecdsa") && + signValue == SignatureAlgorithm.ECDSA.value) || + (expected.equalsIgnoreCase("ec") && + signValue == SignatureAlgorithm.ECDSA.value)) { + return algorithm; + } + } + + return null; + } + + static enum HashAlgorithm { + UNDEFINED("undefined", "", -1, -1), + NONE( "none", "NONE", 0, -1), + MD5( "md5", "MD5", 1, 16), + SHA1( "sha1", "SHA-1", 2, 20), + SHA224( "sha224", "SHA-224", 3, 28), + SHA256( "sha256", "SHA-256", 4, 32), + SHA384( "sha384", "SHA-384", 5, 48), + SHA512( "sha512", "SHA-512", 6, 64); + + final String name; // not the standard signature algorithm name + // except the UNDEFINED, other names are defined + // by TLS 1.2 protocol + final String standardName; // the standard MessageDigest algorithm name + final int value; + final int length; // digest length in bytes, -1 means not applicable + + private HashAlgorithm(String name, String standardName, + int value, int length) { + this.name = name; + this.standardName = standardName; + this.value = value; + this.length = length; + } + + static HashAlgorithm valueOf(int value) { + HashAlgorithm algorithm = UNDEFINED; + switch (value) { + case 0: + algorithm = NONE; + break; + case 1: + algorithm = MD5; + break; + case 2: + algorithm = SHA1; + break; + case 3: + algorithm = SHA224; + break; + case 4: + algorithm = SHA256; + break; + case 5: + algorithm = SHA384; + break; + case 6: + algorithm = SHA512; + break; + } + + return algorithm; + } + } + + static enum SignatureAlgorithm { + UNDEFINED("undefined", -1), + ANONYMOUS("anonymous", 0), + RSA( "rsa", 1), + DSA( "dsa", 2), + ECDSA( "ecdsa", 3); + + final String name; // not the standard signature algorithm name + // except the UNDEFINED, other names are defined + // by TLS 1.2 protocol + final int value; + + private SignatureAlgorithm(String name, int value) { + this.name = name; + this.value = value; + } + + static SignatureAlgorithm valueOf(int value) { + SignatureAlgorithm algorithm = UNDEFINED; + switch (value) { + case 0: + algorithm = ANONYMOUS; + break; + case 1: + algorithm = RSA; + break; + case 2: + algorithm = DSA; + break; + case 3: + algorithm = ECDSA; + break; + } + + return algorithm; + } + } + + static { + supportedMap = Collections.synchronizedSortedMap( + new TreeMap()); + priorityMap = Collections.synchronizedSortedMap( + new TreeMap()); + + synchronized (supportedMap) { + int p = SUPPORTED_ALG_PRIORITY_MAX_NUM; + supports(HashAlgorithm.MD5, SignatureAlgorithm.RSA, + "MD5withRSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.DSA, + "SHA1withDSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.RSA, + "SHA1withRSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.ECDSA, + "SHA1withECDSA", --p); + supports(HashAlgorithm.SHA224, SignatureAlgorithm.RSA, + "SHA224withRSA", --p); + supports(HashAlgorithm.SHA224, SignatureAlgorithm.ECDSA, + "SHA224withECDSA", --p); + supports(HashAlgorithm.SHA256, SignatureAlgorithm.RSA, + "SHA256withRSA", --p); + supports(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA, + "SHA256withECDSA", --p); + supports(HashAlgorithm.SHA384, SignatureAlgorithm.RSA, + "SHA384withRSA", --p); + supports(HashAlgorithm.SHA384, SignatureAlgorithm.ECDSA, + "SHA384withECDSA", --p); + supports(HashAlgorithm.SHA512, SignatureAlgorithm.RSA, + "SHA512withRSA", --p); + supports(HashAlgorithm.SHA512, SignatureAlgorithm.ECDSA, + "SHA512withECDSA", --p); + } + } +} + diff --git a/src/sun/security/ssl/SunJSSE.java b/src/sun/security/ssl/SunJSSE.java new file mode 100644 index 00000000..ec40890a --- /dev/null +++ b/src/sun/security/ssl/SunJSSE.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.security.*; + +/** + * The JSSE provider. + * + * The RSA implementation has been removed from JSSE, but we still need to + * register the same algorithms for compatibility. We just point to the RSA + * implementation in the SunRsaSign provider. This works because all classes + * are in the bootclasspath and therefore loaded by the same classloader. + * + * SunJSSE now supports an experimental FIPS compliant mode when used with an + * appropriate FIPS certified crypto provider. In FIPS mode, we: + * . allow only TLS 1.0 or later + * . allow only FIPS approved ciphersuites + * . perform all crypto in the FIPS crypto provider + * + * It is currently not possible to use both FIPS compliant SunJSSE and + * standard JSSE at the same time because of the various static data structures + * we use. + * + * However, we do want to allow FIPS mode to be enabled at runtime and without + * editing the java.security file. That means we need to allow + * Security.removeProvider("SunJSSE") to work, which creates an instance of + * this class in non-FIPS mode. That is why we delay the selection of the mode + * as long as possible. This is until we open an SSL/TLS connection and the + * data structures need to be initialized or until SunJSSE is initialized in + * FIPS mode. + * + */ +public abstract class SunJSSE extends Provider { + + private static final long serialVersionUID = 3231825739635378733L; + + private static String info = "Sun JSSE provider" + + "(PKCS12, SunX509/PKIX key/trust factories, " + + "SSLv3/TLSv1/TLSv1.1/TLSv1.2)"; + + private static String fipsInfo = + "Sun JSSE provider (FIPS mode, crypto provider "; + + // tri-valued flag: + // null := no final decision made + // false := data structures initialized in non-FIPS mode + // true := data structures initialized in FIPS mode + private static Boolean fips; + + // the FIPS certificate crypto provider that we use to perform all crypto + // operations. null in non-FIPS mode + static Provider cryptoProvider; + + protected static synchronized boolean isFIPS() { + if (fips == null) { + fips = false; + } + return fips; + } + + // ensure we can use FIPS mode using the specified crypto provider. + // enable FIPS mode if not already enabled. + private static synchronized void ensureFIPS(Provider p) { + if (fips == null) { + fips = true; + cryptoProvider = p; + } else { + if (fips == false) { + throw new ProviderException + ("SunJSSE already initialized in non-FIPS mode"); + } + if (cryptoProvider != p) { + throw new ProviderException + ("SunJSSE already initialized with FIPS crypto provider " + + cryptoProvider); + } + } + } + + // standard constructor + protected SunJSSE() { + super("SunJSSE", 1.8d, info); + subclassCheck(); + if (Boolean.TRUE.equals(fips)) { + throw new ProviderException + ("SunJSSE is already initialized in FIPS mode"); + } + registerAlgorithms(false); + } + + // preferred constructor to enable FIPS mode at runtime + protected SunJSSE(Provider cryptoProvider){ + this(checkNull(cryptoProvider), cryptoProvider.getName()); + } + + // constructor to enable FIPS mode from java.security file + protected SunJSSE(String cryptoProvider){ + this(null, checkNull(cryptoProvider)); + } + + private static T checkNull(T t) { + if (t == null) { + throw new ProviderException("cryptoProvider must not be null"); + } + return t; + } + + private SunJSSE(Provider cryptoProvider, + String providerName) { + super("SunJSSE", 1.8d, fipsInfo + providerName + ")"); + subclassCheck(); + if (cryptoProvider == null) { + // Calling Security.getProvider() will cause other providers to be + // loaded. That is not good but unavoidable here. + cryptoProvider = Security.getProvider(providerName); + if (cryptoProvider == null) { + throw new ProviderException + ("Crypto provider not installed: " + providerName); + } + } + ensureFIPS(cryptoProvider); + registerAlgorithms(true); + } + + private void registerAlgorithms(final boolean isfips) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + doRegister(isfips); + return null; + } + }); + } + + private void doRegister(boolean isfips) { + if (isfips == false) { + put("KeyFactory.RSA", + "sun.security.rsa.RSAKeyFactory$Legacy"); + put("Alg.Alias.KeyFactory.1.2.840.113549.1.1", "RSA"); + put("Alg.Alias.KeyFactory.OID.1.2.840.113549.1.1", "RSA"); + + put("KeyPairGenerator.RSA", + "sun.security.rsa.RSAKeyPairGenerator$Legacy"); + put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); + put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); + + put("Signature.MD2withRSA", + "sun.security.rsa.RSASignature$MD2withRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.2", "MD2withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.2", + "MD2withRSA"); + + put("Signature.MD5withRSA", + "sun.security.rsa.RSASignature$MD5withRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", + "MD5withRSA"); + + put("Signature.SHA1withRSA", + "sun.security.rsa.RSASignature$SHA1withRSA"); + put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", + "SHA1withRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); + put("Alg.Alias.Signature.OID.1.3.14.3.2.29", "SHA1withRSA"); + + } + put("Signature.MD5andSHA1withRSA", + "sun.security.ssl.RSASignature"); + + put("KeyManagerFactory.SunX509", + "sun.security.ssl.KeyManagerFactoryImpl$SunX509"); + put("KeyManagerFactory.NewSunX509", + "sun.security.ssl.KeyManagerFactoryImpl$X509"); + put("Alg.Alias.KeyManagerFactory.PKIX", "NewSunX509"); + + put("TrustManagerFactory.SunX509", + "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory"); + put("TrustManagerFactory.PKIX", + "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory"); + put("Alg.Alias.TrustManagerFactory.SunPKIX", "PKIX"); + put("Alg.Alias.TrustManagerFactory.X509", "PKIX"); + put("Alg.Alias.TrustManagerFactory.X.509", "PKIX"); + + put("SSLContext.TLSv1", + "sun.security.ssl.SSLContextImpl$TLS10Context"); + put("SSLContext.TLSv1.1", + "sun.security.ssl.SSLContextImpl$TLS11Context"); + put("SSLContext.TLSv1.2", + "sun.security.ssl.SSLContextImpl$TLS12Context"); + put("SSLContext.TLS", + "sun.security.ssl.SSLContextImpl$TLSContext"); + if (isfips == false) { + put("Alg.Alias.SSLContext.SSL", "TLS"); + put("Alg.Alias.SSLContext.SSLv3", "TLSv1"); + } + + put("SSLContext.Default", + "sun.security.ssl.SSLContextImpl$DefaultSSLContext"); + + /* + * KeyStore + */ + put("KeyStore.PKCS12", + "sun.security.pkcs12.PKCS12KeyStore"); + } + + private void subclassCheck() { + if (getClass() != com.sun.net.ssl.internal.ssl.Provider.class) { + throw new AssertionError("Illegal subclass: " + getClass()); + } + } + + @Override + protected final void finalize() throws Throwable { + // empty + super.finalize(); + } + +} diff --git a/src/sun/security/ssl/SunX509KeyManagerImpl.java b/src/sun/security/ssl/SunX509KeyManagerImpl.java new file mode 100644 index 00000000..46e0678f --- /dev/null +++ b/src/sun/security/ssl/SunX509KeyManagerImpl.java @@ -0,0 +1,427 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import javax.net.ssl.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.*; +import java.net.Socket; + +import javax.security.auth.x500.X500Principal; + + +/** + * An implementation of X509KeyManager backed by a KeyStore. + * + * The backing KeyStore is inspected when this object is constructed. + * All key entries containing a PrivateKey and a non-empty chain of + * X509Certificate are then copied into an internal store. This means + * that subsequent modifications of the KeyStore have no effect on the + * X509KeyManagerImpl object. + * + * Note that this class assumes that all keys are protected by the same + * password. + * + * The JSSE handshake code currently calls into this class via + * chooseClientAlias() and chooseServerAlias() to find the certificates to + * use. As implemented here, both always return the first alias returned by + * getClientAliases() and getServerAliases(). In turn, these methods are + * implemented by calling getAliases(), which performs the actual lookup. + * + * Note that this class currently implements no checking of the local + * certificates. In particular, it is *not* guaranteed that: + * . the certificates are within their validity period and not revoked + * . the signatures verify + * . they form a PKIX compliant chain. + * . the certificate extensions allow the certificate to be used for + * the desired purpose. + * + * Chains that fail any of these criteria will probably be rejected by + * the remote peer. + * + */ +final class SunX509KeyManagerImpl extends X509ExtendedKeyManager { + + private static final Debug debug = Debug.getInstance("ssl"); + + private static final String[] STRING0 = new String[0]; + + /* + * The credentials from the KeyStore as + * Map: String(alias) -> X509Credentials(credentials) + */ + private Map credentialsMap; + + /* + * Cached server aliases for the case issuers == null. + * (in the current JSSE implementation, issuers are always null for + * server certs). See chooseServerAlias() for details. + * + * Map: String(keyType) -> String[](alias) + */ + private final Map serverAliasCache; + + /* + * Basic container for credentials implemented as an inner class. + */ + private static class X509Credentials { + PrivateKey privateKey; + X509Certificate[] certificates; + private Set issuerX500Principals; + + X509Credentials(PrivateKey privateKey, X509Certificate[] certificates) { + // assert privateKey and certificates != null + this.privateKey = privateKey; + this.certificates = certificates; + } + + synchronized Set getIssuerX500Principals() { + // lazy initialization + if (issuerX500Principals == null) { + issuerX500Principals = new HashSet(); + for (int i = 0; i < certificates.length; i++) { + issuerX500Principals.add( + certificates[i].getIssuerX500Principal()); + } + } + return issuerX500Principals; + } + } + + SunX509KeyManagerImpl(KeyStore ks, char[] password) + throws KeyStoreException, + NoSuchAlgorithmException, UnrecoverableKeyException { + + credentialsMap = new HashMap(); + serverAliasCache = Collections.synchronizedMap( + new HashMap()); + if (ks == null) { + return; + } + + for (Enumeration aliases = ks.aliases(); + aliases.hasMoreElements(); ) { + String alias = aliases.nextElement(); + if (!ks.isKeyEntry(alias)) { + continue; + } + Key key = ks.getKey(alias, password); + if (key instanceof PrivateKey == false) { + continue; + } + Certificate[] certs = ks.getCertificateChain(alias); + if ((certs == null) || (certs.length == 0) || + !(certs[0] instanceof X509Certificate)) { + continue; + } + if (!(certs instanceof X509Certificate[])) { + Certificate[] tmp = new X509Certificate[certs.length]; + System.arraycopy(certs, 0, tmp, 0, certs.length); + certs = tmp; + } + + X509Credentials cred = new X509Credentials((PrivateKey)key, + (X509Certificate[])certs); + credentialsMap.put(alias, cred); + if (debug != null && Debug.isOn("keymanager")) { + System.out.println("***"); + System.out.println("found key for : " + alias); + for (int i = 0; i < certs.length; i++) { + System.out.println("chain [" + i + "] = " + + certs[i]); + } + System.out.println("***"); + } + } + } + + /* + * Returns the certificate chain associated with the given alias. + * + * @return the certificate chain (ordered with the user's certificate first + * and the root certificate authority last) + */ + @Override + public X509Certificate[] getCertificateChain(String alias) { + if (alias == null) { + return null; + } + X509Credentials cred = credentialsMap.get(alias); + if (cred == null) { + return null; + } else { + return cred.certificates.clone(); + } + } + + /* + * Returns the key associated with the given alias + */ + @Override + public PrivateKey getPrivateKey(String alias) { + if (alias == null) { + return null; + } + X509Credentials cred = credentialsMap.get(alias); + if (cred == null) { + return null; + } else { + return cred.privateKey; + } + } + + /* + * Choose an alias to authenticate the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseClientAlias(String[] keyTypes, Principal[] issuers, + Socket socket) { + /* + * We currently don't do anything with socket, but + * someday we might. It might be a useful hint for + * selecting one of the aliases we get back from + * getClientAliases(). + */ + + if (keyTypes == null) { + return null; + } + + for (int i = 0; i < keyTypes.length; i++) { + String[] aliases = getClientAliases(keyTypes[i], issuers); + if ((aliases != null) && (aliases.length > 0)) { + return aliases[0]; + } + } + return null; + } + + /* + * Choose an alias to authenticate the client side of an + * SSLEngine connection given the public key type + * and the list of certificate issuer authorities recognized by + * the peer (if any). + * + * @since 1.5 + */ + @Override + public String chooseEngineClientAlias(String[] keyType, + Principal[] issuers, SSLEngine engine) { + /* + * If we ever start using socket as a selection criteria, + * we'll need to adjust this. + */ + return chooseClientAlias(keyType, issuers, null); + } + + /* + * Choose an alias to authenticate the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String chooseServerAlias(String keyType, + Principal[] issuers, Socket socket) { + /* + * We currently don't do anything with socket, but + * someday we might. It might be a useful hint for + * selecting one of the aliases we get back from + * getServerAliases(). + */ + if (keyType == null) { + return null; + } + + String[] aliases; + + if (issuers == null || issuers.length == 0) { + aliases = serverAliasCache.get(keyType); + if (aliases == null) { + aliases = getServerAliases(keyType, issuers); + // Cache the result (positive and negative lookups) + if (aliases == null) { + aliases = STRING0; + } + serverAliasCache.put(keyType, aliases); + } + } else { + aliases = getServerAliases(keyType, issuers); + } + if ((aliases != null) && (aliases.length > 0)) { + return aliases[0]; + } + return null; + } + + /* + * Choose an alias to authenticate the server side of an + * SSLEngine connection given the public key type + * and the list of certificate issuer authorities recognized by + * the peer (if any). + * + * @since 1.5 + */ + @Override + public String chooseEngineServerAlias(String keyType, + Principal[] issuers, SSLEngine engine) { + /* + * If we ever start using socket as a selection criteria, + * we'll need to adjust this. + */ + return chooseServerAlias(keyType, issuers, null); + } + + /* + * Get the matching aliases for authenticating the client side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return getAliases(keyType, issuers); + } + + /* + * Get the matching aliases for authenticating the server side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + */ + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return getAliases(keyType, issuers); + } + + /* + * Get the matching aliases for authenticating the either side of a secure + * socket given the public key type and the list of + * certificate issuer authorities recognized by the peer (if any). + * + * Issuers comes to us in the form of X500Principal[]. + */ + private String[] getAliases(String keyType, Principal[] issuers) { + if (keyType == null) { + return null; + } + if (issuers == null) { + issuers = new X500Principal[0]; + } + if (issuers instanceof X500Principal[] == false) { + // normally, this will never happen but try to recover if it does + issuers = convertPrincipals(issuers); + } + String sigType; + if (keyType.contains("_")) { + int k = keyType.indexOf("_"); + sigType = keyType.substring(k + 1); + keyType = keyType.substring(0, k); + } else { + sigType = null; + } + + X500Principal[] x500Issuers = (X500Principal[])issuers; + // the algorithm below does not produce duplicates, so avoid Set + List aliases = new ArrayList<>(); + + for (Map.Entry entry : + credentialsMap.entrySet()) { + + String alias = entry.getKey(); + X509Credentials credentials = entry.getValue(); + X509Certificate[] certs = credentials.certificates; + + if (!keyType.equals(certs[0].getPublicKey().getAlgorithm())) { + continue; + } + if (sigType != null) { + if (certs.length > 1) { + // if possible, check the public key in the issuer cert + if (!sigType.equals( + certs[1].getPublicKey().getAlgorithm())) { + continue; + } + } else { + // Check the signature algorithm of the certificate itself. + // Look for the "withRSA" in "SHA1withRSA", etc. + String sigAlgName = + certs[0].getSigAlgName().toUpperCase(Locale.ENGLISH); + String pattern = "WITH" + + sigType.toUpperCase(Locale.ENGLISH); + if (sigAlgName.contains(pattern) == false) { + continue; + } + } + } + + if (issuers.length == 0) { + // no issuer specified, match all + aliases.add(alias); + if (debug != null && Debug.isOn("keymanager")) { + System.out.println("matching alias: " + alias); + } + } else { + Set certIssuers = + credentials.getIssuerX500Principals(); + for (int i = 0; i < x500Issuers.length; i++) { + if (certIssuers.contains(issuers[i])) { + aliases.add(alias); + if (debug != null && Debug.isOn("keymanager")) { + System.out.println("matching alias: " + alias); + } + break; + } + } + } + } + + String[] aliasStrings = aliases.toArray(STRING0); + return ((aliasStrings.length == 0) ? null : aliasStrings); + } + + /* + * Convert an array of Principals to an array of X500Principals, if + * possible. Principals that cannot be converted are ignored. + */ + private static X500Principal[] convertPrincipals(Principal[] principals) { + List list = new ArrayList<>(principals.length); + for (int i = 0; i < principals.length; i++) { + Principal p = principals[i]; + if (p instanceof X500Principal) { + list.add((X500Principal)p); + } else { + try { + list.add(new X500Principal(p.getName())); + } catch (IllegalArgumentException e) { + // ignore + } + } + } + return list.toArray(new X500Principal[list.size()]); + } +} diff --git a/src/sun/security/ssl/SupportedEllipticCurvesExtension.java b/src/sun/security/ssl/SupportedEllipticCurvesExtension.java new file mode 100644 index 00000000..8933fab1 --- /dev/null +++ b/src/sun/security/ssl/SupportedEllipticCurvesExtension.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.security.spec.ECParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.SSLProtocolException; + +final class SupportedEllipticCurvesExtension extends HelloExtension { + + // the extension value to send in the ClientHello message + static final SupportedEllipticCurvesExtension DEFAULT; + + private static final boolean fips; + + static { + int[] ids; + fips = SunJSSE.isFIPS(); + if (fips == false) { + ids = new int[] { + // NIST curves first + // prefer NIST P-256, rest in order of increasing key length + 23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14, + // non-NIST curves + 15, 16, 17, 2, 18, 4, 5, 20, 8, 22, + }; + } else { + ids = new int[] { + // same as above, but allow only NIST curves in FIPS mode + 23, 1, 3, 19, 21, 6, 7, 9, 10, 24, 11, 12, 25, 13, 14, + }; + } + DEFAULT = new SupportedEllipticCurvesExtension(ids); + } + + private final int[] curveIds; + + private SupportedEllipticCurvesExtension(int[] curveIds) { + super(ExtensionType.EXT_ELLIPTIC_CURVES); + this.curveIds = curveIds; + } + + SupportedEllipticCurvesExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_ELLIPTIC_CURVES); + int k = s.getInt16(); + if (((len & 1) != 0) || (k + 2 != len)) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + curveIds = new int[k >> 1]; + for (int i = 0; i < curveIds.length; i++) { + curveIds[i] = s.getInt16(); + } + } + + boolean contains(int index) { + for (int curveId : curveIds) { + if (index == curveId) { + return true; + } + } + return false; + } + + // Return a reference to the internal curveIds array. + // The caller must NOT modify the contents. + int[] curveIds() { + return curveIds; + } + + @Override + int length() { + return 6 + (curveIds.length << 1); + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + int k = curveIds.length << 1; + s.putInt16(k + 2); + s.putInt16(k); + for (int curveId : curveIds) { + s.putInt16(curveId); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Extension " + type + ", curve names: {"); + boolean first = true; + for (int curveId : curveIds) { + if (first) { + first = false; + } else { + sb.append(", "); + } + // first check if it is a known named curve, then try other cases. + String oid = getCurveOid(curveId); + if (oid != null) { + ECParameterSpec spec = JsseJce.getECParameterSpec(oid); + // this toString() output will look nice for the current + // implementation of the ECParameterSpec class in the Sun + // provider, but may not look good for other implementations. + if (spec != null) { + sb.append(spec.toString().split(" ")[0]); + } else { + sb.append(oid); + } + } else if (curveId == ARBITRARY_PRIME) { + sb.append("arbitrary_explicit_prime_curves"); + } else if (curveId == ARBITRARY_CHAR2) { + sb.append("arbitrary_explicit_char2_curves"); + } else { + sb.append("unknown curve " + curveId); + } + } + sb.append("}"); + return sb.toString(); + } + + // Test whether we support the curve with the given index. + static boolean isSupported(int index) { + if ((index <= 0) || (index >= NAMED_CURVE_OID_TABLE.length)) { + return false; + } + if (fips == false) { + // in non-FIPS mode, we support all valid indices + return true; + } + return DEFAULT.contains(index); + } + + static int getCurveIndex(ECParameterSpec params) { + String oid = JsseJce.getNamedCurveOid(params); + if (oid == null) { + return -1; + } + Integer n = curveIndices.get(oid); + return (n == null) ? -1 : n; + } + + static String getCurveOid(int index) { + if ((index > 0) && (index < NAMED_CURVE_OID_TABLE.length)) { + return NAMED_CURVE_OID_TABLE[index]; + } + return null; + } + + private final static int ARBITRARY_PRIME = 0xff01; + private final static int ARBITRARY_CHAR2 = 0xff02; + + // See sun.security.ec.NamedCurve for the OIDs + private final static String[] NAMED_CURVE_OID_TABLE = new String[] { + null, // (0) unused + "1.3.132.0.1", // (1) sect163k1, NIST K-163 + "1.3.132.0.2", // (2) sect163r1 + "1.3.132.0.15", // (3) sect163r2, NIST B-163 + "1.3.132.0.24", // (4) sect193r1 + "1.3.132.0.25", // (5) sect193r2 + "1.3.132.0.26", // (6) sect233k1, NIST K-233 + "1.3.132.0.27", // (7) sect233r1, NIST B-233 + "1.3.132.0.3", // (8) sect239k1 + "1.3.132.0.16", // (9) sect283k1, NIST K-283 + "1.3.132.0.17", // (10) sect283r1, NIST B-283 + "1.3.132.0.36", // (11) sect409k1, NIST K-409 + "1.3.132.0.37", // (12) sect409r1, NIST B-409 + "1.3.132.0.38", // (13) sect571k1, NIST K-571 + "1.3.132.0.39", // (14) sect571r1, NIST B-571 + "1.3.132.0.9", // (15) secp160k1 + "1.3.132.0.8", // (16) secp160r1 + "1.3.132.0.30", // (17) secp160r2 + "1.3.132.0.31", // (18) secp192k1 + "1.2.840.10045.3.1.1", // (19) secp192r1, NIST P-192 + "1.3.132.0.32", // (20) secp224k1 + "1.3.132.0.33", // (21) secp224r1, NIST P-224 + "1.3.132.0.10", // (22) secp256k1 + "1.2.840.10045.3.1.7", // (23) secp256r1, NIST P-256 + "1.3.132.0.34", // (24) secp384r1, NIST P-384 + "1.3.132.0.35", // (25) secp521r1, NIST P-521 + }; + + private final static Map curveIndices; + + static { + curveIndices = new HashMap(); + for (int i = 1; i < NAMED_CURVE_OID_TABLE.length; i++) { + curveIndices.put(NAMED_CURVE_OID_TABLE[i], i); + } + } + +} diff --git a/src/sun/security/ssl/SupportedEllipticPointFormatsExtension.java b/src/sun/security/ssl/SupportedEllipticPointFormatsExtension.java new file mode 100644 index 00000000..14b84689 --- /dev/null +++ b/src/sun/security/ssl/SupportedEllipticPointFormatsExtension.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.SSLProtocolException; + +final class SupportedEllipticPointFormatsExtension extends HelloExtension { + + final static int FMT_UNCOMPRESSED = 0; + final static int FMT_ANSIX962_COMPRESSED_PRIME = 1; + final static int FMT_ANSIX962_COMPRESSED_CHAR2 = 2; + + static final HelloExtension DEFAULT = + new SupportedEllipticPointFormatsExtension( + new byte[] {FMT_UNCOMPRESSED}); + + private final byte[] formats; + + private SupportedEllipticPointFormatsExtension(byte[] formats) { + super(ExtensionType.EXT_EC_POINT_FORMATS); + this.formats = formats; + } + + SupportedEllipticPointFormatsExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_EC_POINT_FORMATS); + formats = s.getBytes8(); + // RFC 4492 says uncompressed points must always be supported. + // Check just to make sure. + boolean uncompressed = false; + for (int format : formats) { + if (format == FMT_UNCOMPRESSED) { + uncompressed = true; + break; + } + } + if (uncompressed == false) { + throw new SSLProtocolException + ("Peer does not support uncompressed points"); + } + } + + @Override + int length() { + return 5 + formats.length; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(formats.length + 1); + s.putBytes8(formats); + } + + private static String toString(byte format) { + int f = format & 0xff; + switch (f) { + case FMT_UNCOMPRESSED: + return "uncompressed"; + case FMT_ANSIX962_COMPRESSED_PRIME: + return "ansiX962_compressed_prime"; + case FMT_ANSIX962_COMPRESSED_CHAR2: + return "ansiX962_compressed_char2"; + default: + return "unknown-" + f; + } + } + + @Override + public String toString() { + List list = new ArrayList(); + for (byte format : formats) { + list.add(toString(format)); + } + return "Extension " + type + ", formats: " + list; + } +} diff --git a/src/sun/security/ssl/TrustManagerFactoryImpl.java b/src/sun/security/ssl/TrustManagerFactoryImpl.java new file mode 100644 index 00000000..8b598c33 --- /dev/null +++ b/src/sun/security/ssl/TrustManagerFactoryImpl.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.util.*; +import java.io.*; +import java.security.*; +import java.security.cert.*; +import javax.net.ssl.*; + +import sun.security.validator.Validator; + +abstract class TrustManagerFactoryImpl extends TrustManagerFactorySpi { + + private static final Debug debug = Debug.getInstance("ssl"); + private X509TrustManager trustManager = null; + private boolean isInitialized = false; + + TrustManagerFactoryImpl() { + // empty + } + + @Override + protected void engineInit(KeyStore ks) throws KeyStoreException { + if (ks == null) { + try { + ks = getCacertsKeyStore("trustmanager"); + } catch (SecurityException se) { + // eat security exceptions but report other throwables + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println( + "SunX509: skip default keystore: " + se); + } + } catch (Error err) { + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println( + "SunX509: skip default keystore: " + err); + } + throw err; + } catch (RuntimeException re) { + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println( + "SunX509: skip default keystore: " + re); + } + throw re; + } catch (Exception e) { + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println( + "SunX509: skip default keystore: " + e); + } + throw new KeyStoreException( + "problem accessing trust store" + e); + } + } + trustManager = getInstance(ks); + isInitialized = true; + } + + abstract X509TrustManager getInstance(KeyStore ks) throws KeyStoreException; + + abstract X509TrustManager getInstance(ManagerFactoryParameters spec) + throws InvalidAlgorithmParameterException; + + @Override + protected void engineInit(ManagerFactoryParameters spec) throws + InvalidAlgorithmParameterException { + trustManager = getInstance(spec); + isInitialized = true; + } + + /** + * Returns one trust manager for each type of trust material. + */ + @Override + protected TrustManager[] engineGetTrustManagers() { + if (!isInitialized) { + throw new IllegalStateException( + "TrustManagerFactoryImpl is not initialized"); + } + return new TrustManager[] { trustManager }; + } + + /* + * Try to get an InputStream based on the file we pass in. + */ + private static FileInputStream getFileInputStream(final File file) + throws Exception { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public FileInputStream run() throws Exception { + try { + if (file.exists()) { + return new FileInputStream(file); + } else { + return null; + } + } catch (FileNotFoundException e) { + // couldn't find it, oh well. + return null; + } + } + }); + } + + /** + * Returns the keystore with the configured CA certificates. + */ + static KeyStore getCacertsKeyStore(String dbgname) throws Exception + { + String storeFileName = null; + File storeFile = null; + FileInputStream fis = null; + String defaultTrustStoreType; + String defaultTrustStoreProvider; + final HashMap props = new HashMap<>(); + final String sep = File.separator; + KeyStore ks = null; + + AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + props.put("trustStore", System.getProperty( + "javax.net.ssl.trustStore")); + props.put("javaHome", System.getProperty( + "java.home")); + props.put("trustStoreType", System.getProperty( + "javax.net.ssl.trustStoreType", + KeyStore.getDefaultType())); + props.put("trustStoreProvider", System.getProperty( + "javax.net.ssl.trustStoreProvider", "")); + props.put("trustStorePasswd", System.getProperty( + "javax.net.ssl.trustStorePassword", "")); + return null; + } + }); + + /* + * Try: + * javax.net.ssl.trustStore (if this variable exists, stop) + * jssecacerts + * cacerts + * + * If none exists, we use an empty keystore. + */ + + try { + storeFileName = props.get("trustStore"); + if (!"NONE".equals(storeFileName)) { + if (storeFileName != null) { + storeFile = new File(storeFileName); + fis = getFileInputStream(storeFile); + } else { + String javaHome = props.get("javaHome"); + storeFile = new File(javaHome + sep + "lib" + sep + + "security" + sep + + "jssecacerts"); + if ((fis = getFileInputStream(storeFile)) == null) { + storeFile = new File(javaHome + sep + "lib" + sep + + "security" + sep + + "cacerts"); + fis = getFileInputStream(storeFile); + } + } + + if (fis != null) { + storeFileName = storeFile.getPath(); + } else { + storeFileName = "No File Available, using empty keystore."; + } + } + + defaultTrustStoreType = props.get("trustStoreType"); + defaultTrustStoreProvider = props.get("trustStoreProvider"); + if (debug != null && Debug.isOn(dbgname)) { + System.out.println("trustStore is: " + storeFileName); + System.out.println("trustStore type is : " + + defaultTrustStoreType); + System.out.println("trustStore provider is : " + + defaultTrustStoreProvider); + } + + /* + * Try to initialize trust store. + */ + if (defaultTrustStoreType.length() != 0) { + if (debug != null && Debug.isOn(dbgname)) { + System.out.println("init truststore"); + } + if (defaultTrustStoreProvider.length() == 0) { + ks = KeyStore.getInstance(defaultTrustStoreType); + } else { + ks = KeyStore.getInstance(defaultTrustStoreType, + defaultTrustStoreProvider); + } + char[] passwd = null; + String defaultTrustStorePassword = + props.get("trustStorePasswd"); + if (defaultTrustStorePassword.length() != 0) + passwd = defaultTrustStorePassword.toCharArray(); + + // if trustStore is NONE, fis will be null + ks.load(fis, passwd); + + // Zero out the temporary password storage + if (passwd != null) { + for (int i = 0; i < passwd.length; i++) { + passwd[i] = (char)0; + } + } + } + } finally { + if (fis != null) { + fis.close(); + } + } + + return ks; + } + + public static final class SimpleFactory extends TrustManagerFactoryImpl { + @Override + X509TrustManager getInstance(KeyStore ks) throws KeyStoreException { + return new X509TrustManagerImpl(Validator.TYPE_SIMPLE, ks); + } + @Override + X509TrustManager getInstance(ManagerFactoryParameters spec) + throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException + ("SunX509 TrustManagerFactory does not use " + + "ManagerFactoryParameters"); + } + } + + public static final class PKIXFactory extends TrustManagerFactoryImpl { + @Override + X509TrustManager getInstance(KeyStore ks) throws KeyStoreException { + return new X509TrustManagerImpl(Validator.TYPE_PKIX, ks); + } + @Override + X509TrustManager getInstance(ManagerFactoryParameters spec) + throws InvalidAlgorithmParameterException { + if (spec instanceof CertPathTrustManagerParameters == false) { + throw new InvalidAlgorithmParameterException + ("Parameters must be CertPathTrustManagerParameters"); + } + CertPathParameters params = + ((CertPathTrustManagerParameters)spec).getParameters(); + if (params instanceof PKIXBuilderParameters == false) { + throw new InvalidAlgorithmParameterException + ("Encapsulated parameters must be PKIXBuilderParameters"); + } + PKIXBuilderParameters pkixParams = (PKIXBuilderParameters)params; + return new X509TrustManagerImpl(Validator.TYPE_PKIX, pkixParams); + } + } +} diff --git a/src/sun/security/ssl/UnknownExtension.java b/src/sun/security/ssl/UnknownExtension.java new file mode 100644 index 00000000..4411600a --- /dev/null +++ b/src/sun/security/ssl/UnknownExtension.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.io.IOException; + +final class UnknownExtension extends HelloExtension { + + private final byte[] data; + + UnknownExtension(HandshakeInStream s, int len, ExtensionType type) + throws IOException { + super(type); + data = new byte[len]; + // s.read() does not handle 0-length arrays. + if (len != 0) { + s.read(data); + } + } + + @Override + int length() { + return 4 + data.length; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putBytes16(data); + } + + @Override + public String toString() { + return "Unsupported extension " + type + ", data: " + + Debug.toString(data); + } +} diff --git a/src/sun/security/ssl/Utilities.java b/src/sun/security/ssl/Utilities.java new file mode 100644 index 00000000..aefb02c9 --- /dev/null +++ b/src/sun/security/ssl/Utilities.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import javax.net.ssl.*; +import java.util.*; +import sun.net.util.IPAddressUtil; + +/** + * A utility class to share the static methods. + */ +final class Utilities { + /** + * Puts {@code hostname} into the {@code serverNames} list. + *

    + * If the {@code serverNames} does not look like a legal FQDN, it will + * not be put into the returned list. + *

    + * Note that the returned list does not allow duplicated name type. + * + * @return a list of {@link SNIServerName} + */ + static List addToSNIServerNameList( + List serverNames, String hostname) { + + SNIHostName sniHostName = rawToSNIHostName(hostname); + if (sniHostName == null) { + return serverNames; + } + + int size = serverNames.size(); + List sniList = (size != 0) ? + new ArrayList(serverNames) : + new ArrayList(1); + + boolean reset = false; + for (int i = 0; i < size; i++) { + SNIServerName serverName = sniList.get(i); + if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { + sniList.set(i, sniHostName); + if (Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", the previous server name in SNI (" + serverName + + ") was replaced with (" + sniHostName + ")"); + } + reset = true; + break; + } + } + + if (!reset) { + sniList.add(sniHostName); + } + + return Collections.unmodifiableList(sniList); + } + + /** + * Converts string hostname to {@code SNIHostName}. + *

    + * Note that to check whether a hostname is a valid domain name, we cannot + * use the hostname resolved from name services. For virtual hosting, + * multiple hostnames may be bound to the same IP address, so the hostname + * resolved from name services is not always reliable. + * + * @param hostname + * the raw hostname + * @return an instance of {@link SNIHostName}, or null if the hostname does + * not look like a FQDN + */ + private static SNIHostName rawToSNIHostName(String hostname) { + SNIHostName sniHostName = null; + if (hostname != null && hostname.indexOf('.') > 0 && + !hostname.endsWith(".") && + !IPAddressUtil.isIPv4LiteralAddress(hostname) && + !IPAddressUtil.isIPv6LiteralAddress(hostname)) { + + try { + sniHostName = new SNIHostName(hostname); + } catch (IllegalArgumentException iae) { + // don't bother to handle illegal host_name + if (Debug.isOn("ssl")) { + System.out.println(Thread.currentThread().getName() + + ", \"" + hostname + "\" " + + "is not a legal HostName for server name indication"); + } + } + } + + return sniHostName; + } +} diff --git a/src/sun/security/ssl/X509KeyManagerImpl.java b/src/sun/security/ssl/X509KeyManagerImpl.java new file mode 100644 index 00000000..89b49aa5 --- /dev/null +++ b/src/sun/security/ssl/X509KeyManagerImpl.java @@ -0,0 +1,838 @@ +/* + * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl; + +import java.lang.ref.*; +import java.util.*; +import static java.util.Locale.ENGLISH; +import java.util.concurrent.atomic.AtomicLong; +import java.net.Socket; + +import java.security.*; +import java.security.KeyStore.*; +import java.security.cert.*; +import java.security.cert.Certificate; + +import javax.net.ssl.*; + +import sun.security.provider.certpath.AlgorithmChecker; + +/** + * The new X509 key manager implementation. The main differences to the + * old SunX509 key manager are: + * . it is based around the KeyStore.Builder API. This allows it to use + * other forms of KeyStore protection or password input (e.g. a + * CallbackHandler) or to have keys within one KeyStore protected by + * different keys. + * . it can use multiple KeyStores at the same time. + * . it is explicitly designed to accommodate KeyStores that change over + * the lifetime of the process. + * . it makes an effort to choose the key that matches best, i.e. one that + * is not expired and has the appropriate certificate extensions. + * + * Note that this code is not explicitly performance optimzied yet. + * + * @author Andreas Sterbenz + */ +final class X509KeyManagerImpl extends X509ExtendedKeyManager + implements X509KeyManager { + + private static final Debug debug = Debug.getInstance("ssl"); + + private final static boolean useDebug = + (debug != null) && Debug.isOn("keymanager"); + + // for unit testing only, set via privileged reflection + private static Date verificationDate; + + // list of the builders + private final List builders; + + // counter to generate unique ids for the aliases + private final AtomicLong uidCounter; + + // cached entries + private final Map> entryCacheMap; + + X509KeyManagerImpl(Builder builder) { + this(Collections.singletonList(builder)); + } + + X509KeyManagerImpl(List builders) { + this.builders = builders; + uidCounter = new AtomicLong(); + entryCacheMap = Collections.synchronizedMap + (new SizedMap>()); + } + + // LinkedHashMap with a max size of 10 + // see LinkedHashMap JavaDocs + private static class SizedMap extends LinkedHashMap { + private static final long serialVersionUID = -8211222668790986062L; + + @Override protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > 10; + } + } + + // + // public methods + // + + @Override + public X509Certificate[] getCertificateChain(String alias) { + PrivateKeyEntry entry = getEntry(alias); + return entry == null ? null : + (X509Certificate[])entry.getCertificateChain(); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + PrivateKeyEntry entry = getEntry(alias); + return entry == null ? null : entry.getPrivateKey(); + } + + @Override + public String chooseClientAlias(String[] keyTypes, Principal[] issuers, + Socket socket) { + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(socket)); + } + + @Override + public String chooseEngineClientAlias(String[] keyTypes, + Principal[] issuers, SSLEngine engine) { + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(engine)); + } + + @Override + public String chooseServerAlias(String keyType, + Principal[] issuers, Socket socket) { + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(socket), + X509TrustManagerImpl.getRequestedServerNames(socket), + "HTTPS"); // The SNI HostName is a fully qualified domain name. + // The certificate selection scheme for SNI HostName + // is similar to HTTPS endpoint identification scheme + // implemented in this provider. + // + // Using HTTPS endpoint identification scheme to guide + // the selection of an appropriate authentication + // certificate according to requested SNI extension. + // + // It is not a really HTTPS endpoint identification. + } + + @Override + public String chooseEngineServerAlias(String keyType, + Principal[] issuers, SSLEngine engine) { + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(engine), + X509TrustManagerImpl.getRequestedServerNames(engine), + "HTTPS"); // The SNI HostName is a fully qualified domain name. + // The certificate selection scheme for SNI HostName + // is similar to HTTPS endpoint identification scheme + // implemented in this provider. + // + // Using HTTPS endpoint identification scheme to guide + // the selection of an appropriate authentication + // certificate according to requested SNI extension. + // + // It is not a really HTTPS endpoint identification. + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return getAliases(keyType, issuers, CheckType.CLIENT, null); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return getAliases(keyType, issuers, CheckType.SERVER, null); + } + + // + // implementation private methods + // + + // Gets algorithm constraints of the socket. + private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + + if (session != null) { + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return new SSLAlgorithmConstraints( + sslSocket, peerSupportedSignAlgs, true); + } + } + + return new SSLAlgorithmConstraints(sslSocket, true); + } + + return new SSLAlgorithmConstraints((SSLSocket)null, true); + } + + // Gets algorithm constraints of the engine. + private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session != null) { + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return new SSLAlgorithmConstraints( + engine, peerSupportedSignAlgs, true); + } + } + } + + return new SSLAlgorithmConstraints(engine, true); + } + + // we construct the alias we return to JSSE as seen in the code below + // a unique id is included to allow us to reliably cache entries + // between the calls to getCertificateChain() and getPrivateKey() + // even if tokens are inserted or removed + private String makeAlias(EntryStatus entry) { + return uidCounter.incrementAndGet() + "." + entry.builderIndex + "." + + entry.alias; + } + + private PrivateKeyEntry getEntry(String alias) { + // if the alias is null, return immediately + if (alias == null) { + return null; + } + + // try to get the entry from cache + Reference ref = entryCacheMap.get(alias); + PrivateKeyEntry entry = (ref != null) ? ref.get() : null; + if (entry != null) { + return entry; + } + + // parse the alias + int firstDot = alias.indexOf('.'); + int secondDot = alias.indexOf('.', firstDot + 1); + if ((firstDot == -1) || (secondDot == firstDot)) { + // invalid alias + return null; + } + try { + int builderIndex = Integer.parseInt + (alias.substring(firstDot + 1, secondDot)); + String keyStoreAlias = alias.substring(secondDot + 1); + Builder builder = builders.get(builderIndex); + KeyStore ks = builder.getKeyStore(); + Entry newEntry = ks.getEntry + (keyStoreAlias, builder.getProtectionParameter(alias)); + if (newEntry instanceof PrivateKeyEntry == false) { + // unexpected type of entry + return null; + } + entry = (PrivateKeyEntry)newEntry; + entryCacheMap.put(alias, new SoftReference(entry)); + return entry; + } catch (Exception e) { + // ignore + return null; + } + } + + // Class to help verify that the public key algorithm (and optionally + // the signature algorithm) of a certificate matches what we need. + private static class KeyType { + + final String keyAlgorithm; + + // In TLS 1.2, the signature algorithm has been obsoleted by the + // supported_signature_algorithms, and the certificate type no longer + // restricts the algorithm used to sign the certificate. + // However, because we don't support certificate type checking other + // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the + // protocol version here. + final String sigKeyAlgorithm; + + KeyType(String algorithm) { + int k = algorithm.indexOf("_"); + if (k == -1) { + keyAlgorithm = algorithm; + sigKeyAlgorithm = null; + } else { + keyAlgorithm = algorithm.substring(0, k); + sigKeyAlgorithm = algorithm.substring(k + 1); + } + } + + boolean matches(Certificate[] chain) { + if (!chain[0].getPublicKey().getAlgorithm().equals(keyAlgorithm)) { + return false; + } + if (sigKeyAlgorithm == null) { + return true; + } + if (chain.length > 1) { + // if possible, check the public key in the issuer cert + return sigKeyAlgorithm.equals( + chain[1].getPublicKey().getAlgorithm()); + } else { + // Check the signature algorithm of the certificate itself. + // Look for the "withRSA" in "SHA1withRSA", etc. + X509Certificate issuer = (X509Certificate)chain[0]; + String sigAlgName = issuer.getSigAlgName().toUpperCase(ENGLISH); + String pattern = "WITH" + sigKeyAlgorithm.toUpperCase(ENGLISH); + return sigAlgName.contains(pattern); + } + } + } + + private static List getKeyTypes(String ... keyTypes) { + if ((keyTypes == null) || + (keyTypes.length == 0) || (keyTypes[0] == null)) { + return null; + } + List list = new ArrayList<>(keyTypes.length); + for (String keyType : keyTypes) { + list.add(new KeyType(keyType)); + } + return list; + } + + /* + * Return the best alias that fits the given parameters. + * The algorithm we use is: + * . scan through all the aliases in all builders in order + * . as soon as we find a perfect match, return + * (i.e. a match with a cert that has appropriate key usage, + * qualified endpoint identity, and is not expired). + * . if we do not find a perfect match, keep looping and remember + * the imperfect matches + * . at the end, sort the imperfect matches. we prefer expired certs + * with appropriate key usage to certs with the wrong key usage. + * return the first one of them. + */ + private String chooseAlias(List keyTypeList, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints) { + + return chooseAlias(keyTypeList, issuers, + checkType, constraints, null, null); + } + + private String chooseAlias(List keyTypeList, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints, + List requestedServerNames, String idAlgorithm) { + + if (keyTypeList == null || keyTypeList.isEmpty()) { + return null; + } + + Set issuerSet = getIssuerSet(issuers); + List allResults = null; + for (int i = 0, n = builders.size(); i < n; i++) { + try { + List results = getAliases(i, keyTypeList, + issuerSet, false, checkType, constraints, + requestedServerNames, idAlgorithm); + if (results != null) { + // the results will either be a single perfect match + // or 1 or more imperfect matches + // if it's a perfect match, return immediately + EntryStatus status = results.get(0); + if (status.checkResult == CheckResult.OK) { + if (useDebug) { + debug.println("KeyMgr: choosing key: " + status); + } + return makeAlias(status); + } + if (allResults == null) { + allResults = new ArrayList(); + } + allResults.addAll(results); + } + } catch (Exception e) { + // ignore + } + } + if (allResults == null) { + if (useDebug) { + debug.println("KeyMgr: no matching key found"); + } + return null; + } + Collections.sort(allResults); + if (useDebug) { + debug.println("KeyMgr: no good matching key found, " + + "returning best match out of:"); + debug.println(allResults.toString()); + } + return makeAlias(allResults.get(0)); + } + + /* + * Return all aliases that (approximately) fit the parameters. + * These are perfect matches plus imperfect matches (expired certificates + * and certificates with the wrong extensions). + * The perfect matches will be first in the array. + */ + public String[] getAliases(String keyType, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints) { + if (keyType == null) { + return null; + } + + Set issuerSet = getIssuerSet(issuers); + List keyTypeList = getKeyTypes(keyType); + List allResults = null; + for (int i = 0, n = builders.size(); i < n; i++) { + try { + List results = getAliases(i, keyTypeList, + issuerSet, true, checkType, constraints, + null, null); + if (results != null) { + if (allResults == null) { + allResults = new ArrayList(); + } + allResults.addAll(results); + } + } catch (Exception e) { + // ignore + } + } + if (allResults == null || allResults.isEmpty()) { + if (useDebug) { + debug.println("KeyMgr: no matching alias found"); + } + return null; + } + Collections.sort(allResults); + if (useDebug) { + debug.println("KeyMgr: getting aliases: " + allResults); + } + return toAliases(allResults); + } + + // turn candidate entries into unique aliases we can return to JSSE + private String[] toAliases(List results) { + String[] s = new String[results.size()]; + int i = 0; + for (EntryStatus result : results) { + s[i++] = makeAlias(result); + } + return s; + } + + // make a Set out of the array + private Set getIssuerSet(Principal[] issuers) { + if ((issuers != null) && (issuers.length != 0)) { + return new HashSet<>(Arrays.asList(issuers)); + } else { + return null; + } + } + + // a candidate match + // identifies the entry by builder and alias + // and includes the result of the certificate check + private static class EntryStatus implements Comparable { + + final int builderIndex; + final int keyIndex; + final String alias; + final CheckResult checkResult; + + EntryStatus(int builderIndex, int keyIndex, String alias, + Certificate[] chain, CheckResult checkResult) { + this.builderIndex = builderIndex; + this.keyIndex = keyIndex; + this.alias = alias; + this.checkResult = checkResult; + } + + @Override + public int compareTo(EntryStatus other) { + int result = this.checkResult.compareTo(other.checkResult); + return (result == 0) ? (this.keyIndex - other.keyIndex) : result; + } + + @Override + public String toString() { + String s = alias + " (verified: " + checkResult + ")"; + if (builderIndex == 0) { + return s; + } else { + return "Builder #" + builderIndex + ", alias: " + s; + } + } + } + + // enum for the type of certificate check we want to perform + // (client or server) + // also includes the check code itself + private static enum CheckType { + + // enum constant for "no check" (currently not used) + NONE(Collections.emptySet()), + + // enum constant for "tls client" check + // valid EKU for TLS client: any, tls_client + CLIENT(new HashSet(Arrays.asList(new String[] { + "2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))), + + // enum constant for "tls server" check + // valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc + SERVER(new HashSet(Arrays.asList(new String[] { + "2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1", + "1.3.6.1.4.1.311.10.3.3" }))); + + // set of valid EKU values for this type + final Set validEku; + + CheckType(Set validEku) { + this.validEku = validEku; + } + + private static boolean getBit(boolean[] keyUsage, int bit) { + return (bit < keyUsage.length) && keyUsage[bit]; + } + + // check if this certificate is appropriate for this type of use + // first check extensions, if they match, check expiration + // note: we may want to move this code into the sun.security.validator + // package + CheckResult check(X509Certificate cert, Date date, + List serverNames, String idAlgorithm) { + + if (this == NONE) { + return CheckResult.OK; + } + + // check extensions + try { + // check extended key usage + List certEku = cert.getExtendedKeyUsage(); + if ((certEku != null) && + Collections.disjoint(validEku, certEku)) { + // if extension present and it does not contain any of + // the valid EKU OIDs, return extension_mismatch + return CheckResult.EXTENSION_MISMATCH; + } + + // check key usage + boolean[] ku = cert.getKeyUsage(); + if (ku != null) { + String algorithm = cert.getPublicKey().getAlgorithm(); + boolean kuSignature = getBit(ku, 0); + switch (algorithm) { + case "RSA": + // require either signature bit + // or if server also allow key encipherment bit + if (kuSignature == false) { + if ((this == CLIENT) || (getBit(ku, 2) == false)) { + return CheckResult.EXTENSION_MISMATCH; + } + } + break; + case "DSA": + // require signature bit + if (kuSignature == false) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + case "DH": + // require keyagreement bit + if (getBit(ku, 4) == false) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + case "EC": + // require signature bit + if (kuSignature == false) { + return CheckResult.EXTENSION_MISMATCH; + } + // For servers, also require key agreement. + // This is not totally accurate as the keyAgreement + // bit is only necessary for static ECDH key + // exchange and not ephemeral ECDH. We leave it in + // for now until there are signs that this check + // causes problems for real world EC certificates. + if ((this == SERVER) && (getBit(ku, 4) == false)) { + return CheckResult.EXTENSION_MISMATCH; + } + break; + } + } + } catch (CertificateException e) { + // extensions unparseable, return failure + return CheckResult.EXTENSION_MISMATCH; + } + + try { + cert.checkValidity(date); + } catch (CertificateException e) { + return CheckResult.EXPIRED; + } + + if (serverNames != null && !serverNames.isEmpty()) { + for (SNIServerName serverName : serverNames) { + if (serverName.getType() == + StandardConstants.SNI_HOST_NAME) { + if (!(serverName instanceof SNIHostName)) { + try { + serverName = + new SNIHostName(serverName.getEncoded()); + } catch (IllegalArgumentException iae) { + // unlikely to happen, just in case ... + if (useDebug) { + debug.println( + "Illegal server name: " + serverName); + } + + return CheckResult.INSENSITIVE; + } + } + String hostname = + ((SNIHostName)serverName).getAsciiName(); + + try { + X509TrustManagerImpl.checkIdentity(hostname, + cert, idAlgorithm); + } catch (CertificateException e) { + if (useDebug) { + debug.println( + "Certificate identity does not match " + + "Server Name Inidication (SNI): " + + hostname); + } + return CheckResult.INSENSITIVE; + } + + break; + } + } + } + + return CheckResult.OK; + } + } + + // enum for the result of the extension check + // NOTE: the order of the constants is important as they are used + // for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH + private static enum CheckResult { + OK, // ok or not checked + INSENSITIVE, // server name indication insensitive + EXPIRED, // extensions valid but cert expired + EXTENSION_MISMATCH, // extensions invalid (expiration not checked) + } + + /* + * Return a List of all candidate matches in the specified builder + * that fit the parameters. + * We exclude entries in the KeyStore if they are not: + * . private key entries + * . the certificates are not X509 certificates + * . the algorithm of the key in the EE cert doesn't match one of keyTypes + * . none of the certs is issued by a Principal in issuerSet + * Using those entries would not be possible or they would almost + * certainly be rejected by the peer. + * + * In addition to those checks, we also check the extensions in the EE + * cert and its expiration. Even if there is a mismatch, we include + * such certificates because they technically work and might be accepted + * by the peer. This leads to more graceful failure and better error + * messages if the cert expires from one day to the next. + * + * The return values are: + * . null, if there are no matching entries at all + * . if 'findAll' is 'false' and there is a perfect match, a List + * with a single element (early return) + * . if 'findAll' is 'false' and there is NO perfect match, a List + * with all the imperfect matches (expired, wrong extensions) + * . if 'findAll' is 'true', a List with all perfect and imperfect + * matches + */ + private List getAliases(int builderIndex, + List keyTypes, Set issuerSet, + boolean findAll, CheckType checkType, + AlgorithmConstraints constraints, + List requestedServerNames, + String idAlgorithm) throws Exception { + + Builder builder = builders.get(builderIndex); + KeyStore ks = builder.getKeyStore(); + List results = null; + Date date = verificationDate; + boolean preferred = false; + for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { + String alias = e.nextElement(); + // check if it is a key entry (private key or secret key) + if (ks.isKeyEntry(alias) == false) { + continue; + } + + Certificate[] chain = ks.getCertificateChain(alias); + if ((chain == null) || (chain.length == 0)) { + // must be secret key entry, ignore + continue; + } + + boolean incompatible = false; + for (Certificate cert : chain) { + if (cert instanceof X509Certificate == false) { + // not an X509Certificate, ignore this alias + incompatible = true; + break; + } + } + if (incompatible) { + continue; + } + + // check keytype + int keyIndex = -1; + int j = 0; + for (KeyType keyType : keyTypes) { + if (keyType.matches(chain)) { + keyIndex = j; + break; + } + j++; + } + if (keyIndex == -1) { + if (useDebug) { + debug.println("Ignoring alias " + alias + + ": key algorithm does not match"); + } + continue; + } + // check issuers + if (issuerSet != null) { + boolean found = false; + for (Certificate cert : chain) { + X509Certificate xcert = (X509Certificate)cert; + if (issuerSet.contains(xcert.getIssuerX500Principal())) { + found = true; + break; + } + } + if (found == false) { + if (useDebug) { + debug.println("Ignoring alias " + alias + + ": issuers do not match"); + } + continue; + } + } + + // check the algorithm constraints + if (constraints != null && + !conformsToAlgorithmConstraints(constraints, chain)) { + + if (useDebug) { + debug.println("Ignoring alias " + alias + + ": certificate list does not conform to " + + "algorithm constraints"); + } + continue; + } + + if (date == null) { + date = new Date(); + } + CheckResult checkResult = + checkType.check((X509Certificate)chain[0], date, + requestedServerNames, idAlgorithm); + EntryStatus status = + new EntryStatus(builderIndex, keyIndex, + alias, chain, checkResult); + if (!preferred && checkResult == CheckResult.OK && keyIndex == 0) { + preferred = true; + } + if (preferred && (findAll == false)) { + // if we have a good match and do not need all matches, + // return immediately + return Collections.singletonList(status); + } else { + if (results == null) { + results = new ArrayList(); + } + results.add(status); + } + } + return results; + } + + private static boolean conformsToAlgorithmConstraints( + AlgorithmConstraints constraints, Certificate[] chain) { + + AlgorithmChecker checker = new AlgorithmChecker(constraints); + try { + checker.init(false); + } catch (CertPathValidatorException cpve) { + // unlikely to happen + return false; + } + + // It is a forward checker, so we need to check from trust to target. + for (int i = chain.length - 1; i >= 0; i--) { + Certificate cert = chain[i]; + try { + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.emptySet()); + } catch (CertPathValidatorException cpve) { + return false; + } + } + + return true; + } + +} diff --git a/src/sun/security/ssl/X509TrustManagerImpl.java b/src/sun/security/ssl/X509TrustManagerImpl.java new file mode 100644 index 00000000..1137c073 --- /dev/null +++ b/src/sun/security/ssl/X509TrustManagerImpl.java @@ -0,0 +1,467 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.ssl; + +import java.net.Socket; +import javax.net.ssl.SSLSession; + +import java.util.*; +import java.security.*; +import java.security.cert.*; +import javax.net.ssl.*; + +import sun.security.validator.*; +import sun.security.util.HostnameChecker; + +/** + * This class implements the SunJSSE X.509 trust manager using the internal + * validator API in J2SE core. The logic in this class is minimal.

    + *

    + * This class supports both the Simple validation algorithm from previous + * JSSE versions and PKIX validation. Currently, it is not possible for the + * application to specify PKIX parameters other than trust anchors. This will + * be fixed in a future release using new APIs. When that happens, it may also + * make sense to separate the Simple and PKIX trust managers into separate + * classes. + * + * @author Andreas Sterbenz + */ +final class X509TrustManagerImpl extends X509ExtendedTrustManager + implements X509TrustManager { + + private final String validatorType; + + /** + * The Set of trusted X509Certificates. + */ + private final Collection trustedCerts; + + private final PKIXBuilderParameters pkixParams; + + // note that we need separate validator for client and server due to + // the different extension checks. They are initialized lazily on demand. + private volatile Validator clientValidator, serverValidator; + + private static final Debug debug = Debug.getInstance("ssl"); + + X509TrustManagerImpl(String validatorType, KeyStore ks) + throws KeyStoreException { + this.validatorType = validatorType; + this.pkixParams = null; + if (ks == null) { + trustedCerts = Collections.emptySet(); + } else { + trustedCerts = KeyStores.getTrustedCerts(ks); + } + showTrustedCerts(); + } + + X509TrustManagerImpl(String validatorType, PKIXBuilderParameters params) { + this.validatorType = validatorType; + this.pkixParams = params; + // create server validator eagerly so that we can conveniently + // get the trusted certificates + // clients need it anyway eventually, and servers will not mind + // the little extra footprint + Validator v = getValidator(Validator.VAR_TLS_SERVER); + trustedCerts = v.getTrustedCertificates(); + serverValidator = v; + showTrustedCerts(); + } + + @Override + public void checkClientTrusted(X509Certificate chain[], String authType) + throws CertificateException { + checkTrusted(chain, authType, (Socket)null, true); + } + + @Override + public void checkServerTrusted(X509Certificate chain[], String authType) + throws CertificateException { + checkTrusted(chain, authType, (Socket)null, false); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()]; + trustedCerts.toArray(certsArray); + return certsArray; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + checkTrusted(chain, authType, socket, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + checkTrusted(chain, authType, socket, false); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + checkTrusted(chain, authType, engine, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + checkTrusted(chain, authType, engine, false); + } + + private Validator checkTrustedInit(X509Certificate[] chain, + String authType, boolean isClient) { + if (chain == null || chain.length == 0) { + throw new IllegalArgumentException( + "null or zero-length certificate chain"); + } + + if (authType == null || authType.length() == 0) { + throw new IllegalArgumentException( + "null or zero-length authentication type"); + } + + Validator v = null; + if (isClient) { + v = clientValidator; + if (v == null) { + synchronized (this) { + v = clientValidator; + if (v == null) { + v = getValidator(Validator.VAR_TLS_CLIENT); + clientValidator = v; + } + } + } + } else { + // assume double checked locking with a volatile flag works + // (guaranteed under the new Tiger memory model) + v = serverValidator; + if (v == null) { + synchronized (this) { + v = serverValidator; + if (v == null) { + v = getValidator(Validator.VAR_TLS_SERVER); + serverValidator = v; + } + } + } + } + + return v; + } + + + private void checkTrusted(X509Certificate[] chain, String authType, + Socket socket, boolean isClient) throws CertificateException { + Validator v = checkTrustedInit(chain, authType, isClient); + + AlgorithmConstraints constraints = null; + if ((socket != null) && socket.isConnected() && + (socket instanceof SSLSocket)) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = sslSocket.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + checkIdentity(session, chain[0], identityAlg, isClient, + getRequestedServerNames(socket)); + } + + // create the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] localSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + sslSocket, localSupportedSignAlgs, false); + } else { + constraints = + new SSLAlgorithmConstraints(sslSocket, false); + } + } else { + constraints = new SSLAlgorithmConstraints(sslSocket, false); + } + } + + X509Certificate[] trustedChain = null; + if (isClient) { + trustedChain = validate(v, chain, constraints, null); + } else { + trustedChain = validate(v, chain, constraints, authType); + } + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println("Found trusted certificate:"); + System.out.println(trustedChain[trustedChain.length - 1]); + } + } + + private void checkTrusted(X509Certificate[] chain, String authType, + SSLEngine engine, boolean isClient) throws CertificateException { + Validator v = checkTrustedInit(chain, authType, isClient); + + AlgorithmConstraints constraints = null; + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = engine.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + checkIdentity(session, chain[0], identityAlg, isClient, + getRequestedServerNames(engine)); + } + + // create the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] localSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + engine, localSupportedSignAlgs, false); + } else { + constraints = + new SSLAlgorithmConstraints(engine, false); + } + } else { + constraints = new SSLAlgorithmConstraints(engine, false); + } + } + + X509Certificate[] trustedChain = null; + if (isClient) { + trustedChain = validate(v, chain, constraints, null); + } else { + trustedChain = validate(v, chain, constraints, authType); + } + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println("Found trusted certificate:"); + System.out.println(trustedChain[trustedChain.length - 1]); + } + } + + private void showTrustedCerts() { + if (debug != null && Debug.isOn("trustmanager")) { + for (X509Certificate cert : trustedCerts) { + System.out.println("adding as trusted cert:"); + System.out.println(" Subject: " + + cert.getSubjectX500Principal()); + System.out.println(" Issuer: " + + cert.getIssuerX500Principal()); + System.out.println(" Algorithm: " + + cert.getPublicKey().getAlgorithm() + + "; Serial number: 0x" + + cert.getSerialNumber().toString(16)); + System.out.println(" Valid from " + + cert.getNotBefore() + " until " + + cert.getNotAfter()); + System.out.println(); + } + } + } + + private Validator getValidator(String variant) { + Validator v; + if (pkixParams == null) { + v = Validator.getInstance(validatorType, variant, trustedCerts); + } else { + v = Validator.getInstance(validatorType, variant, pkixParams); + } + return v; + } + + private static X509Certificate[] validate(Validator v, + X509Certificate[] chain, AlgorithmConstraints constraints, + String authType) throws CertificateException { + Object o = JsseJce.beginFipsProvider(); + try { + return v.validate(chain, null, constraints, authType); + } finally { + JsseJce.endFipsProvider(o); + } + } + + // Get string representation of HostName from a list of server names. + // + // We are only accepting host_name name type in the list. + private static String getHostNameInSNI(List sniNames) { + + SNIHostName hostname = null; + for (SNIServerName sniName : sniNames) { + if (sniName.getType() != StandardConstants.SNI_HOST_NAME) { + continue; + } + + if (sniName instanceof SNIHostName) { + hostname = (SNIHostName)sniName; + } else { + try { + hostname = new SNIHostName(sniName.getEncoded()); + } catch (IllegalArgumentException iae) { + // unlikely to happen, just in case ... + if ((debug != null) && Debug.isOn("trustmanager")) { + System.out.println("Illegal server name: " + sniName); + } + } + } + + // no more than server name of the same name type + break; + } + + if (hostname != null) { + return hostname.getAsciiName(); + } + + return null; + } + + // Also used by X509KeyManagerImpl + static List getRequestedServerNames(Socket socket) { + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + + if (session != null && (session instanceof ExtendedSSLSession)) { + ExtendedSSLSession extSession = (ExtendedSSLSession)session; + return extSession.getRequestedServerNames(); + } + } + + return Collections.emptyList(); + } + + // Also used by X509KeyManagerImpl + static List getRequestedServerNames(SSLEngine engine) { + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + + if (session != null && (session instanceof ExtendedSSLSession)) { + ExtendedSSLSession extSession = (ExtendedSSLSession)session; + return extSession.getRequestedServerNames(); + } + } + + return Collections.emptyList(); + } + + /* + * Per RFC 6066, if an application negotiates a server name using an + * application protocol and then upgrades to TLS, and if a server_name + * extension is sent, then the extension SHOULD contain the same name + * that was negotiated in the application protocol. If the server_name + * is established in the TLS session handshake, the client SHOULD NOT + * attempt to request a different server name at the application layer. + * + * According to the above spec, we only need to check either the identity + * in server_name extension or the peer host of the connection. Peer host + * is not always a reliable fully qualified domain name. The HostName in + * server_name extension is more reliable than peer host. So we prefer + * the identity checking aginst the server_name extension if present, and + * may failove to peer host checking. + */ + private static void checkIdentity(SSLSession session, + X509Certificate cert, + String algorithm, + boolean isClient, + List sniNames) throws CertificateException { + + boolean identifiable = false; + String peerHost = session.getPeerHost(); + if (isClient) { + String hostname = getHostNameInSNI(sniNames); + if (hostname != null) { + try { + checkIdentity(hostname, cert, algorithm); + identifiable = true; + } catch (CertificateException ce) { + if (hostname.equalsIgnoreCase(peerHost)) { + throw ce; + } + + // otherwisw, failover to check peer host + } + } + } + + if (!identifiable) { + checkIdentity(peerHost, cert, algorithm); + } + } + + /* + * Identify the peer by its certificate and hostname. + * + * Lifted from sun.net.www.protocol.https.HttpsClient. + */ + static void checkIdentity(String hostname, X509Certificate cert, + String algorithm) throws CertificateException { + if (algorithm != null && algorithm.length() != 0) { + // if IPv6 strip off the "[]" + if ((hostname != null) && hostname.startsWith("[") && + hostname.endsWith("]")) { + hostname = hostname.substring(1, hostname.length() - 1); + } + + if (algorithm.equalsIgnoreCase("HTTPS")) { + HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match( + hostname, cert); + } else if (algorithm.equalsIgnoreCase("LDAP") || + algorithm.equalsIgnoreCase("LDAPS")) { + HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match( + hostname, cert); + } else { + throw new CertificateException( + "Unknown identification algorithm: " + algorithm); + } + } + } +} diff --git a/src/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java b/src/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java new file mode 100644 index 00000000..6e1865d1 --- /dev/null +++ b/src/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl.krb5; + +import java.io.IOException; +import java.io.PrintStream; +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.security.SecureRandom; +import java.net.InetAddress; +import java.security.PrivilegedAction; + +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.ServicePermission; +import sun.security.jgss.GSSCaller; + +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.internal.Ticket; +import sun.security.krb5.internal.EncTicketPart; +import sun.security.krb5.internal.crypto.KeyUsage; + +import sun.security.jgss.krb5.Krb5Util; +import sun.security.jgss.krb5.ServiceCreds; +import sun.security.krb5.KrbException; + +import sun.security.ssl.Debug; +import sun.security.ssl.HandshakeInStream; +import sun.security.ssl.HandshakeOutStream; +import sun.security.ssl.Krb5Helper; +import sun.security.ssl.ProtocolVersion; + +/** + * This is Kerberos option in the client key exchange message + * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted + * premaster secret encrypted with the session key sealed in the ticket. + * From RFC 2712: + * struct + * { + * opaque Ticket; + * opaque authenticator; // optional + * opaque EncryptedPreMasterSecret; // encrypted with the session key + * // which is sealed in the ticket + * } KerberosWrapper; + * + * + * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1) + * Encrypted pre-master secret has the same structure as it does for RSA + * except for Kerberos, the encryption key is the session key instead of + * the RSA public key. + * + * XXX authenticator currently ignored + * + */ +public final class KerberosClientKeyExchangeImpl + extends sun.security.ssl.KerberosClientKeyExchange { + + private KerberosPreMasterSecret preMaster; + private byte[] encodedTicket; + private KerberosPrincipal peerPrincipal; + private KerberosPrincipal localPrincipal; + + public KerberosClientKeyExchangeImpl() { + } + + /** + * Creates an instance of KerberosClientKeyExchange consisting of the + * Kerberos service ticket, authenticator and encrypted premaster secret. + * Called by client handshaker. + * + * @param serverName name of server with which to do handshake; + * this is used to get the Kerberos service ticket + * @param protocolVersion Maximum version supported by client (i.e, + * version it requested in client hello) + * @param rand random number generator to use for generating pre-master + * secret + */ + @Override + public void init(String serverName, + AccessControlContext acc, ProtocolVersion protocolVersion, + SecureRandom rand) throws IOException { + + // Get service ticket + KerberosTicket ticket = getServiceTicket(serverName, acc); + encodedTicket = ticket.getEncoded(); + + // Record the Kerberos principals + peerPrincipal = ticket.getServer(); + localPrincipal = ticket.getClient(); + + // Optional authenticator, encrypted using session key, + // currently ignored + + // Generate premaster secret and encrypt it using session key + EncryptionKey sessionKey = new EncryptionKey( + ticket.getSessionKeyType(), + ticket.getSessionKey().getEncoded()); + + preMaster = new KerberosPreMasterSecret(protocolVersion, + rand, sessionKey); + } + + /** + * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding. + * Used by ServerHandshaker to verify and obtain premaster secret. + * + * @param protocolVersion current protocol version + * @param clientVersion version requested by client in its ClientHello; + * used by premaster secret version check + * @param rand random number generator used for generating random + * premaster secret if ticket and/or premaster verification fails + * @param input inputstream from which to get ASN.1-encoded KerberosWrapper + * @param acc the AccessControlContext of the handshaker + * @param serviceCreds server's creds + */ + @Override + public void init(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, + SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds) + throws IOException { + + // Read ticket + encodedTicket = input.getBytes16(); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(System.out, + "encoded Kerberos service ticket", encodedTicket); + } + + EncryptionKey sessionKey = null; + + try { + Ticket t = new Ticket(encodedTicket); + + EncryptedData encPart = t.encPart; + PrincipalName ticketSname = t.sname; + + final ServiceCreds creds = (ServiceCreds)serviceCreds; + final KerberosPrincipal princ = + new KerberosPrincipal(ticketSname.toString()); + + // For bound service, permission already checked at setup + if (creds.getName() == null) { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + // Eliminate dependency on ServicePermission + sm.checkPermission(Krb5Helper.getServicePermission( + ticketSname.toString(), "accept"), acc); + } + } catch (SecurityException se) { + serviceCreds = null; + // Do not destroy keys. Will affect Subject + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Permission to access Kerberos" + + " secret key denied"); + } + throw new IOException("Kerberos service not allowedy"); + } + } + KerberosKey[] serverKeys = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public KerberosKey[] run() { + return creds.getKKeys(princ); + } + }); + if (serverKeys.length == 0) { + throw new IOException("Found no key for " + princ + + (creds.getName() == null ? "" : + (", this keytab is for " + creds.getName() + " only"))); + } + + /* + * permission to access and use the secret key of the Kerberized + * "host" service is done in ServerHandshaker.getKerberosKeys() + * to ensure server has the permission to use the secret key + * before promising the client + */ + + // See if we have the right key to decrypt the ticket to get + // the session key. + int encPartKeyType = encPart.getEType(); + Integer encPartKeyVersion = encPart.getKeyVersionNumber(); + KerberosKey dkey = null; + try { + dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys); + } catch (KrbException ke) { // a kvno mismatch + throw new IOException( + "Cannot find key matching version number", ke); + } + if (dkey == null) { + // %%% Should print string repr of etype + throw new IOException("Cannot find key of appropriate type" + + " to decrypt ticket - need etype " + encPartKeyType); + } + + EncryptionKey secretKey = new EncryptionKey( + encPartKeyType, + dkey.getEncoded()); + + // Decrypt encPart using server's secret key + byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET); + + // Reset data stream after decryption, remove redundant bytes + byte[] temp = encPart.reset(bytes); + EncTicketPart encTicketPart = new EncTicketPart(temp); + + // Record the Kerberos Principals + peerPrincipal = + new KerberosPrincipal(encTicketPart.cname.getName()); + localPrincipal = new KerberosPrincipal(ticketSname.getName()); + + sessionKey = encTicketPart.key; + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("server principal: " + ticketSname); + System.out.println("cname: " + encTicketPart.cname.toString()); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("KerberosWrapper error getting session key," + + " generating random secret (" + e.getMessage() + ")"); + } + sessionKey = null; + } + + input.getBytes16(); // XXX Read and ignore authenticator + + if (sessionKey != null) { + preMaster = new KerberosPreMasterSecret(protocolVersion, + clientVersion, rand, input, sessionKey); + } else { + // Generate bogus premaster secret + preMaster = new KerberosPreMasterSecret(clientVersion, rand); + } + } + + @Override + public int messageLength() { + return (6 + encodedTicket.length + preMaster.getEncrypted().length); + } + + @Override + public void send(HandshakeOutStream s) throws IOException { + s.putBytes16(encodedTicket); + s.putBytes16(null); // XXX no authenticator + s.putBytes16(preMaster.getEncrypted()); + } + + @Override + public void print(PrintStream s) throws IOException { + s.println("*** ClientKeyExchange, Kerberos"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "Kerberos service ticket", encodedTicket); + Debug.println(s, "Random Secret", preMaster.getUnencrypted()); + Debug.println(s, "Encrypted random Secret", + preMaster.getEncrypted()); + } + } + + // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context + private static KerberosTicket getServiceTicket(String serverName, + final AccessControlContext acc) throws IOException { + + if ("localhost".equals(serverName) || + "localhost.localdomain".equals(serverName)) { + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Get the local hostname"); + } + String localHost = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException e) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning," + + " cannot get the local hostname: " + + e.getMessage()); + } + return null; + } + } + }); + if (localHost != null) { + serverName = localHost; + } + } + + // Resolve serverName (possibly in IP addr form) to Kerberos principal + // name for service with hostname + String serviceName = "host/" + serverName; + PrincipalName principal; + try { + principal = new PrincipalName(serviceName, + PrincipalName.KRB_NT_SRV_HST); + } catch (SecurityException se) { + throw se; + } catch (Exception e) { + IOException ioe = new IOException("Invalid service principal" + + " name: " + serviceName); + ioe.initCause(e); + throw ioe; + } + String realm = principal.getRealmAsString(); + + final String serverPrincipal = principal.toString(); + final String tgsPrincipal = "krbtgt/" + realm + "@" + realm; + final String clientPrincipal = null; // use default + + + // check permission to obtain a service ticket to initiate a + // context with the "host" service + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new ServicePermission(serverPrincipal, + "initiate"), acc); + } + + try { + KerberosTicket ticket = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public KerberosTicket run() throws Exception { + return Krb5Util.getTicketFromSubjectAndTgs( + GSSCaller.CALLER_SSL_CLIENT, + clientPrincipal, serverPrincipal, + tgsPrincipal, acc); + }}); + + if (ticket == null) { + throw new IOException("Failed to find any kerberos service" + + " ticket for " + serverPrincipal); + } + return ticket; + } catch (PrivilegedActionException e) { + IOException ioe = new IOException( + "Attempt to obtain kerberos service ticket for " + + serverPrincipal + " failed!"); + ioe.initCause(e); + throw ioe; + } + } + + @Override + public byte[] getUnencryptedPreMasterSecret() { + return preMaster.getUnencrypted(); + } + + @Override + public KerberosPrincipal getPeerPrincipal() { + return peerPrincipal; + } + + @Override + public KerberosPrincipal getLocalPrincipal() { + return localPrincipal; + } + + /** + * Determines if a kvno matches another kvno. Used in the method + * findKey(etype, version, keys). Always returns true if either input + * is null or zero, in case any side does not have kvno info available. + * + * Note: zero is included because N/A is not a legal value for kvno + * in javax.security.auth.kerberos.KerberosKey. Therefore, the info + * that the kvno is N/A might be lost when converting between + * EncryptionKey and KerberosKey. + */ + private static boolean versionMatches(Integer v1, int v2) { + if (v1 == null || v1 == 0 || v2 == 0) { + return true; + } + return v1.equals(v2); + } + + private static KerberosKey findKey(int etype, Integer version, + KerberosKey[] keys) throws KrbException { + int ktype; + boolean etypeFound = false; + + // When no matched kvno is found, returns tke key of the same + // etype with the highest kvno + int kvno_found = 0; + KerberosKey key_found = null; + + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getKeyType(); + if (etype == ktype) { + int kv = keys[i].getVersionNumber(); + etypeFound = true; + if (versionMatches(version, kv)) { + return keys[i]; + } else if (kv > kvno_found) { + key_found = keys[i]; + kvno_found = kv; + } + } + } + // Key not found. + // %%% kludge to allow DES keys to be used for diff etypes + if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || + etype == EncryptedData.ETYPE_DES_CBC_MD5)) { + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getKeyType(); + if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || + ktype == EncryptedData.ETYPE_DES_CBC_MD5) { + int kv = keys[i].getVersionNumber(); + etypeFound = true; + if (versionMatches(version, kv)) { + return new KerberosKey(keys[i].getPrincipal(), + keys[i].getEncoded(), + etype, + kv); + } else if (kv > kvno_found) { + key_found = new KerberosKey(keys[i].getPrincipal(), + keys[i].getEncoded(), + etype, + kv); + kvno_found = kv; + } + } + } + } + if (etypeFound) { + return key_found; + } + return null; + } +} diff --git a/src/sun/security/ssl/krb5/KerberosPreMasterSecret.java b/src/sun/security/ssl/krb5/KerberosPreMasterSecret.java new file mode 100644 index 00000000..84c96a35 --- /dev/null +++ b/src/sun/security/ssl/krb5/KerberosPreMasterSecret.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.ssl.krb5; + +import java.io.*; +import java.security.*; +import java.util.Arrays; + +import javax.net.ssl.*; + +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.KrbException; +import sun.security.krb5.internal.crypto.KeyUsage; + +import sun.security.ssl.Debug; +import sun.security.ssl.HandshakeInStream; +import sun.security.ssl.HandshakeMessage; +import sun.security.ssl.ProtocolVersion; + +/** + * This is the Kerberos premaster secret in the Kerberos client key + * exchange message (CLIENT --> SERVER); it holds the + * Kerberos-encrypted pre-master secret. The secret is encrypted using the + * Kerberos session key. The padding and size of the resulting message + * depends on the session key type, but the pre-master secret is + * always exactly 48 bytes. + * + */ +final class KerberosPreMasterSecret { + + private ProtocolVersion protocolVersion; // preMaster [0,1] + private byte preMaster[]; // 48 bytes + private byte encrypted[]; + + /** + * Constructor used by client to generate premaster secret. + * + * Client randomly creates a pre-master secret and encrypts it + * using the Kerberos session key; only the server can decrypt + * it, using the session key available in the service ticket. + * + * @param protocolVersion used to set preMaster[0,1] + * @param generator random number generator for generating premaster secret + * @param sessionKey Kerberos session key for encrypting premaster secret + */ + KerberosPreMasterSecret(ProtocolVersion protocolVersion, + SecureRandom generator, EncryptionKey sessionKey) throws IOException { + + if (sessionKey.getEType() == + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { + throw new IOException( + "session keys with des3-cbc-hmac-sha1-kd encryption type " + + "are not supported for TLS Kerberos cipher suites"); + } + + this.protocolVersion = protocolVersion; + preMaster = generatePreMaster(generator, protocolVersion); + + // Encrypt premaster secret + try { + EncryptedData eData = new EncryptedData(sessionKey, preMaster, + KeyUsage.KU_UNKNOWN); + encrypted = eData.getBytes(); // not ASN.1 encoded. + + } catch (KrbException e) { + throw (SSLKeyException)new SSLKeyException + ("Kerberos premaster secret error").initCause(e); + } + } + + /* + * Constructor used by server to decrypt encrypted premaster secret. + * The protocol version in preMaster[0,1] must match either currentVersion + * or clientVersion, otherwise, the premaster secret is set to + * a random one to foil possible attack. + * + * @param currentVersion version of protocol being used + * @param clientVersion version requested by client + * @param generator random number generator used to generate + * bogus premaster secret if premaster secret verification fails + * @param input input stream from which to read the encrypted + * premaster secret + * @param sessionKey Kerberos session key to be used for decryption + */ + KerberosPreMasterSecret(ProtocolVersion currentVersion, + ProtocolVersion clientVersion, + SecureRandom generator, HandshakeInStream input, + EncryptionKey sessionKey) throws IOException { + + // Extract encrypted premaster secret from message + encrypted = input.getBytes16(); + + if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { + if (encrypted != null) { + Debug.println(System.out, + "encrypted premaster secret", encrypted); + } + } + + if (sessionKey.getEType() == + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { + throw new IOException( + "session keys with des3-cbc-hmac-sha1-kd encryption type " + + "are not supported for TLS Kerberos cipher suites"); + } + + // Decrypt premaster secret + try { + EncryptedData data = new EncryptedData(sessionKey.getEType(), + null /* optional kvno */, encrypted); + + byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN); + if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { + if (encrypted != null) { + Debug.println(System.out, + "decrypted premaster secret", temp); + } + } + + // Remove padding bytes after decryption. Only DES and DES3 have + // paddings and we don't support DES3 in TLS (see above) + + if (temp.length == 52 && + data.getEType() == EncryptedData.ETYPE_DES_CBC_CRC) { + // For des-cbc-crc, 4 paddings. Value can be 0x04 or 0x00. + if (paddingByteIs(temp, 52, (byte)4) || + paddingByteIs(temp, 52, (byte)0)) { + temp = Arrays.copyOf(temp, 48); + } + } else if (temp.length == 56 && + data.getEType() == EncryptedData.ETYPE_DES_CBC_MD5) { + // For des-cbc-md5, 8 paddings with 0x08, or no padding + if (paddingByteIs(temp, 56, (byte)8)) { + temp = Arrays.copyOf(temp, 48); + } + } + + preMaster = temp; + + protocolVersion = ProtocolVersion.valueOf(preMaster[0], + preMaster[1]); + if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { + System.out.println("Kerberos PreMasterSecret version: " + + protocolVersion); + } + } catch (Exception e) { + // catch exception & process below + preMaster = null; + protocolVersion = currentVersion; + } + + // check if the premaster secret version is ok + // the specification says that it must be the maximum version supported + // by the client from its ClientHello message. However, many + // old implementations send the negotiated version, so accept both + // for SSL v3.0 and TLS v1.0. + // NOTE that we may be comparing two unsupported version numbers in + // the second case, which is why we cannot use object references + // equality in this special case + boolean versionMismatch = (protocolVersion.v != clientVersion.v); + + /* + * we never checked the client_version in server side + * for TLS v1.0 and SSL v3.0. For compatibility, we + * maintain this behavior. + */ + if (versionMismatch && (clientVersion.v <= 0x0301)) { + versionMismatch = (protocolVersion.v != currentVersion.v); + } + + /* + * Bogus decrypted ClientKeyExchange? If so, conjure a + * a random preMaster secret that will fail later during + * Finished message processing. This is a countermeasure against + * the "interactive RSA PKCS#1 encryption envelop attack" reported + * in June 1998. Preserving the executation path will + * mitigate timing attacks and force consistent error handling + * that will prevent an attacking client from differentiating + * different kinds of decrypted ClientKeyExchange bogosities. + */ + if ((preMaster == null) || (preMaster.length != 48) + || versionMismatch) { + if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { + System.out.println("Kerberos PreMasterSecret error, " + + "generating random secret"); + if (preMaster != null) { + Debug.println(System.out, "Invalid secret", preMaster); + } + } + + /* + * Randomize the preMaster secret with the + * ClientHello.client_version, as will produce invalid master + * secret to prevent the attacks. + */ + preMaster = generatePreMaster(generator, clientVersion); + protocolVersion = clientVersion; + } + } + + /** + * Checks if all paddings of data are b + * @param data the block with padding + * @param len length of data, >= 48 + * @param b expected padding byte + */ + private static boolean paddingByteIs(byte[] data, int len, byte b) { + for (int i=48; i= 2) { + hostName = nameParts[1]; + } + } catch (Exception e) { + // ignore + } + return hostName; + } + + + @Override + public Permission getServicePermission(String principalName, + String action) { + return new ServicePermission(principalName, action); + } + + @Override + public boolean isRelated(Subject subject, Principal princ) { + if (princ == null) return false; + Set principals = + subject.getPrincipals(Principal.class); + if (principals.contains(princ)) { + // bound to this principal + return true; + } + for (KeyTab pc: subject.getPrivateCredentials(KeyTab.class)) { + if (!pc.isBound()) { + return true; + } + } + return false; + } +} diff --git a/src/sun/security/timestamp/HttpTimestamper.java b/src/sun/security/timestamp/HttpTimestamper.java new file mode 100644 index 00000000..54cdf6a0 --- /dev/null +++ b/src/sun/security/timestamp/HttpTimestamper.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.timestamp; + +import java.io.BufferedInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.URI; +import java.net.HttpURLConnection; +import java.util.*; + +import sun.misc.IOUtils; +import sun.security.util.Debug; + +/** + * A timestamper that communicates with a Timestamping Authority (TSA) + * over HTTP. + * It supports the Time-Stamp Protocol defined in: + * RFC 3161. + * + * @since 1.5 + * @author Vincent Ryan + */ + +public class HttpTimestamper implements Timestamper { + + private static final int CONNECT_TIMEOUT = 15000; // 15 seconds + + // The MIME type for a timestamp query + private static final String TS_QUERY_MIME_TYPE = + "application/timestamp-query"; + + // The MIME type for a timestamp reply + private static final String TS_REPLY_MIME_TYPE = + "application/timestamp-reply"; + + private static final Debug debug = Debug.getInstance("ts"); + + /* + * HTTP URI identifying the location of the TSA + */ + private URI tsaURI = null; + + /** + * Creates a timestamper that connects to the specified TSA. + * + * @param tsa The location of the TSA. It must be an HTTP or HTTPS URI. + * @throws IllegalArgumentException if tsaURI is not an HTTP or HTTPS URI + */ + public HttpTimestamper(URI tsaURI) { + if (!tsaURI.getScheme().equalsIgnoreCase("http") && + !tsaURI.getScheme().equalsIgnoreCase("https")) { + throw new IllegalArgumentException( + "TSA must be an HTTP or HTTPS URI"); + } + this.tsaURI = tsaURI; + } + + /** + * Connects to the TSA and requests a timestamp. + * + * @param tsQuery The timestamp query. + * @return The result of the timestamp query. + * @throws IOException The exception is thrown if a problem occurs while + * communicating with the TSA. + */ + public TSResponse generateTimestamp(TSRequest tsQuery) throws IOException { + + HttpURLConnection connection = + (HttpURLConnection) tsaURI.toURL().openConnection(); + connection.setDoOutput(true); + connection.setUseCaches(false); // ignore cache + connection.setRequestProperty("Content-Type", TS_QUERY_MIME_TYPE); + connection.setRequestMethod("POST"); + // Avoids the "hang" when a proxy is required but none has been set. + connection.setConnectTimeout(CONNECT_TIMEOUT); + + if (debug != null) { + Set>> headers = + connection.getRequestProperties().entrySet(); + debug.println(connection.getRequestMethod() + " " + tsaURI + + " HTTP/1.1"); + for (Map.Entry> e : headers) { + debug.println(" " + e); + } + debug.println(); + } + connection.connect(); // No HTTP authentication is performed + + // Send the request + DataOutputStream output = null; + try { + output = new DataOutputStream(connection.getOutputStream()); + byte[] request = tsQuery.encode(); + output.write(request, 0, request.length); + output.flush(); + if (debug != null) { + debug.println("sent timestamp query (length=" + + request.length + ")"); + } + } finally { + if (output != null) { + output.close(); + } + } + + // Receive the reply + BufferedInputStream input = null; + byte[] replyBuffer = null; + try { + input = new BufferedInputStream(connection.getInputStream()); + if (debug != null) { + String header = connection.getHeaderField(0); + debug.println(header); + int i = 1; + while ((header = connection.getHeaderField(i)) != null) { + String key = connection.getHeaderFieldKey(i); + debug.println(" " + ((key==null) ? "" : key + ": ") + + header); + i++; + } + debug.println(); + } + verifyMimeType(connection.getContentType()); + + int contentLength = connection.getContentLength(); + replyBuffer = IOUtils.readFully(input, contentLength, false); + + if (debug != null) { + debug.println("received timestamp response (length=" + + replyBuffer.length + ")"); + } + } finally { + if (input != null) { + input.close(); + } + } + return new TSResponse(replyBuffer); + } + + /* + * Checks that the MIME content type is a timestamp reply. + * + * @param contentType The MIME content type to be checked. + * @throws IOException The exception is thrown if a mismatch occurs. + */ + private static void verifyMimeType(String contentType) throws IOException { + if (! TS_REPLY_MIME_TYPE.equalsIgnoreCase(contentType)) { + throw new IOException("MIME Content-Type is not " + + TS_REPLY_MIME_TYPE); + } + } +} diff --git a/src/sun/security/timestamp/TSRequest.java b/src/sun/security/timestamp/TSRequest.java new file mode 100644 index 00000000..086c3b61 --- /dev/null +++ b/src/sun/security/timestamp/TSRequest.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.timestamp; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Extension; +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; + +/** + * This class provides a timestamp request, as defined in + * RFC 3161. + * + * The TimeStampReq ASN.1 type has the following definition: + *

    + *
    + *     TimeStampReq ::= SEQUENCE {
    + *         version           INTEGER { v1(1) },
    + *         messageImprint    MessageImprint
    + *           -- a hash algorithm OID and the hash value of the data to be
    + *           -- time-stamped.
    + *         reqPolicy         TSAPolicyId    OPTIONAL,
    + *         nonce             INTEGER        OPTIONAL,
    + *         certReq           BOOLEAN        DEFAULT FALSE,
    + *         extensions        [0] IMPLICIT Extensions OPTIONAL }
    + *
    + *     MessageImprint ::= SEQUENCE {
    + *         hashAlgorithm     AlgorithmIdentifier,
    + *         hashedMessage     OCTET STRING }
    + *
    + *     TSAPolicyId ::= OBJECT IDENTIFIER
    + *
    + * 
    + * + * @since 1.5 + * @author Vincent Ryan + * @see Timestamper + */ + +public class TSRequest { + + private int version = 1; + + private AlgorithmId hashAlgorithmId = null; + + private byte[] hashValue; + + private String policyId = null; + + private BigInteger nonce = null; + + private boolean returnCertificate = false; + + private X509Extension[] extensions = null; + + /** + * Constructs a timestamp request for the supplied data. + * + * @param toBeTimeStamped The data to be timestamped. + * @param messageDigest The MessageDigest of the hash algorithm to use. + * @throws NoSuchAlgorithmException if the hash algorithm is not supported + */ + public TSRequest(String tSAPolicyID, byte[] toBeTimeStamped, MessageDigest messageDigest) + throws NoSuchAlgorithmException { + + this.policyId = tSAPolicyID; + this.hashAlgorithmId = AlgorithmId.get(messageDigest.getAlgorithm()); + this.hashValue = messageDigest.digest(toBeTimeStamped); + } + + public byte[] getHashedMessage() { + return hashValue.clone(); + } + + /** + * Sets the Time-Stamp Protocol version. + * + * @param version The TSP version. + */ + public void setVersion(int version) { + this.version = version; + } + + /** + * Sets an object identifier for the Time-Stamp Protocol policy. + * + * @param version The policy object identifier. + */ + public void setPolicyId(String policyId) { + this.policyId = policyId; + } + + /** + * Sets a nonce. + * A nonce is a single-use random number. + * + * @param nonce The nonce value. + */ + public void setNonce(BigInteger nonce) { + this.nonce = nonce; + } + + /** + * Request that the TSA include its signing certificate in the response. + * + * @param returnCertificate True if the TSA should return its signing + * certificate. By default it is not returned. + */ + public void requestCertificate(boolean returnCertificate) { + this.returnCertificate = returnCertificate; + } + + /** + * Sets the Time-Stamp Protocol extensions. + * + * @param extensions The protocol extensions. + */ + public void setExtensions(X509Extension[] extensions) { + this.extensions = extensions; + } + + public byte[] encode() throws IOException { + + DerOutputStream request = new DerOutputStream(); + + // encode version + request.putInteger(version); + + // encode messageImprint + DerOutputStream messageImprint = new DerOutputStream(); + hashAlgorithmId.encode(messageImprint); + messageImprint.putOctetString(hashValue); + request.write(DerValue.tag_Sequence, messageImprint); + + // encode optional elements + + if (policyId != null) { + request.putOID(new ObjectIdentifier(policyId)); + } + if (nonce != null) { + request.putInteger(nonce); + } + if (returnCertificate) { + request.putBoolean(true); + } + + DerOutputStream out = new DerOutputStream(); + out.write(DerValue.tag_Sequence, request); + return out.toByteArray(); + } +} diff --git a/src/sun/security/timestamp/TSResponse.java b/src/sun/security/timestamp/TSResponse.java new file mode 100644 index 00000000..0cf223d4 --- /dev/null +++ b/src/sun/security/timestamp/TSResponse.java @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.timestamp; + +import java.io.IOException; +import sun.security.pkcs.PKCS7; +import sun.security.util.Debug; +import sun.security.util.DerValue; + +/** + * This class provides the response corresponding to a timestamp request, + * as defined in + * RFC 3161. + * + * The TimeStampResp ASN.1 type has the following definition: + *
    + *
    + *     TimeStampResp ::= SEQUENCE {
    + *         status            PKIStatusInfo,
    + *         timeStampToken    TimeStampToken OPTIONAL ]
    + *
    + *     PKIStatusInfo ::= SEQUENCE {
    + *         status        PKIStatus,
    + *         statusString  PKIFreeText OPTIONAL,
    + *         failInfo      PKIFailureInfo OPTIONAL }
    + *
    + *     PKIStatus ::= INTEGER {
    + *         granted                (0),
    + *           -- when the PKIStatus contains the value zero a TimeStampToken, as
    + *           -- requested, is present.
    + *         grantedWithMods        (1),
    + *           -- when the PKIStatus contains the value one a TimeStampToken,
    + *           -- with modifications, is present.
    + *         rejection              (2),
    + *         waiting                (3),
    + *         revocationWarning      (4),
    + *           -- this message contains a warning that a revocation is
    + *           -- imminent
    + *         revocationNotification (5)
    + *           -- notification that a revocation has occurred }
    + *
    + *     PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
    + *           -- text encoded as UTF-8 String (note:  each UTF8String SHOULD
    + *           -- include an RFC 1766 language tag to indicate the language
    + *           -- of the contained text)
    + *
    + *     PKIFailureInfo ::= BIT STRING {
    + *         badAlg              (0),
    + *           -- unrecognized or unsupported Algorithm Identifier
    + *         badRequest          (2),
    + *           -- transaction not permitted or supported
    + *         badDataFormat       (5),
    + *           -- the data submitted has the wrong format
    + *         timeNotAvailable    (14),
    + *           -- the TSA's time source is not available
    + *         unacceptedPolicy    (15),
    + *           -- the requested TSA policy is not supported by the TSA
    + *         unacceptedExtension (16),
    + *           -- the requested extension is not supported by the TSA
    + *         addInfoNotAvailable (17)
    + *           -- the additional information requested could not be understood
    + *           -- or is not available
    + *         systemFailure       (25)
    + *           -- the request cannot be handled due to system failure }
    + *
    + *     TimeStampToken ::= ContentInfo
    + *         -- contentType is id-signedData
    + *         -- content is SignedData
    + *         -- eContentType within SignedData is id-ct-TSTInfo
    + *         -- eContent within SignedData is TSTInfo
    + *
    + * 
    + * + * @since 1.5 + * @author Vincent Ryan + * @see Timestamper + */ + +public class TSResponse { + + // Status codes (from RFC 3161) + + /** + * The requested timestamp was granted. + */ + public static final int GRANTED = 0; + + /** + * The requested timestamp was granted with some modifications. + */ + public static final int GRANTED_WITH_MODS = 1; + + /** + * The requested timestamp was not granted. + */ + public static final int REJECTION = 2; + + /** + * The requested timestamp has not yet been processed. + */ + public static final int WAITING = 3; + + /** + * A warning that a certificate revocation is imminent. + */ + public static final int REVOCATION_WARNING = 4; + + /** + * Notification that a certificate revocation has occurred. + */ + public static final int REVOCATION_NOTIFICATION = 5; + + // Failure codes (from RFC 3161) + + /** + * Unrecognized or unsupported algorithm identifier. + */ + public static final int BAD_ALG = 0; + + /** + * The requested transaction is not permitted or supported. + */ + public static final int BAD_REQUEST = 2; + + /** + * The data submitted has the wrong format. + */ + public static final int BAD_DATA_FORMAT = 5; + + /** + * The TSA's time source is not available. + */ + public static final int TIME_NOT_AVAILABLE = 14; + + /** + * The requested TSA policy is not supported by the TSA. + */ + public static final int UNACCEPTED_POLICY = 15; + + /** + * The requested extension is not supported by the TSA. + */ + public static final int UNACCEPTED_EXTENSION = 16; + + /** + * The additional information requested could not be understood or is not + * available. + */ + public static final int ADD_INFO_NOT_AVAILABLE = 17; + + /** + * The request cannot be handled due to system failure. + */ + public static final int SYSTEM_FAILURE = 25; + + private static final Debug debug = Debug.getInstance("ts"); + + private int status; + + private String[] statusString = null; + + private boolean[] failureInfo = null; + + private byte[] encodedTsToken = null; + + private PKCS7 tsToken = null; + + private TimestampToken tstInfo; + + /** + * Constructs an object to store the response to a timestamp request. + * + * @param status A buffer containing the ASN.1 BER encoded response. + * @throws IOException The exception is thrown if a problem is encountered + * parsing the timestamp response. + */ + TSResponse(byte[] tsReply) throws IOException { + parse(tsReply); + } + + /** + * Retrieve the status code returned by the TSA. + */ + public int getStatusCode() { + return status; + } + + /** + * Retrieve the status messages returned by the TSA. + * + * @return If null then no status messages were received. + */ + public String[] getStatusMessages() { + return statusString; + } + + /** + * Retrieve the failure info returned by the TSA. + * + * @return the failure info, or null if no failure code was received. + */ + public boolean[] getFailureInfo() { + return failureInfo; + } + + public String getStatusCodeAsText() { + + switch (status) { + case GRANTED: + return "the timestamp request was granted."; + + case GRANTED_WITH_MODS: + return + "the timestamp request was granted with some modifications."; + + case REJECTION: + return "the timestamp request was rejected."; + + case WAITING: + return "the timestamp request has not yet been processed."; + + case REVOCATION_WARNING: + return "warning: a certificate revocation is imminent."; + + case REVOCATION_NOTIFICATION: + return "notification: a certificate revocation has occurred."; + + default: + return ("unknown status code " + status + "."); + } + } + + private boolean isSet(int position) { + return failureInfo[position]; + } + + public String getFailureCodeAsText() { + + if (failureInfo == null) { + return ""; + } + + try { + if (isSet(BAD_ALG)) + return "Unrecognized or unsupported algorithm identifier."; + if (isSet(BAD_REQUEST)) + return "The requested transaction is not permitted or " + + "supported."; + if (isSet(BAD_DATA_FORMAT)) + return "The data submitted has the wrong format."; + if (isSet(TIME_NOT_AVAILABLE)) + return "The TSA's time source is not available."; + if (isSet(UNACCEPTED_POLICY)) + return "The requested TSA policy is not supported by the TSA."; + if (isSet(UNACCEPTED_EXTENSION)) + return "The requested extension is not supported by the TSA."; + if (isSet(ADD_INFO_NOT_AVAILABLE)) + return "The additional information requested could not be " + + "understood or is not available."; + if (isSet(SYSTEM_FAILURE)) + return "The request cannot be handled due to system failure."; + } catch (ArrayIndexOutOfBoundsException ex) {} + + return ("unknown failure code"); + } + + /** + * Retrieve the timestamp token returned by the TSA. + * + * @return If null then no token was received. + */ + public PKCS7 getToken() { + return tsToken; + } + + public TimestampToken getTimestampToken() { + return tstInfo; + } + + /** + * Retrieve the ASN.1 BER encoded timestamp token returned by the TSA. + * + * @return If null then no token was received. + */ + public byte[] getEncodedToken() { + return encodedTsToken; + } + + /* + * Parses the timestamp response. + * + * @param status A buffer containing the ASN.1 BER encoded response. + * @throws IOException The exception is thrown if a problem is encountered + * parsing the timestamp response. + */ + private void parse(byte[] tsReply) throws IOException { + // Decode TimeStampResp + + DerValue derValue = new DerValue(tsReply); + if (derValue.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding for timestamp response"); + } + + // Parse status + + DerValue statusInfo = derValue.data.getDerValue(); + this.status = statusInfo.data.getInteger(); + if (debug != null) { + debug.println("timestamp response: status=" + this.status); + } + // Parse statusString, if present + if (statusInfo.data.available() > 0) { + byte tag = (byte)statusInfo.data.peekByte(); + if (tag == DerValue.tag_SequenceOf) { + DerValue[] strings = statusInfo.data.getSequence(1); + statusString = new String[strings.length]; + for (int i = 0; i < strings.length; i++) { + statusString[i] = strings[i].getUTF8String(); + if (debug != null) { + debug.println("timestamp response: statusString=" + + statusString[i]); + } + } + } + } + // Parse failInfo, if present + if (statusInfo.data.available() > 0) { + this.failureInfo + = statusInfo.data.getUnalignedBitString().toBooleanArray(); + } + + // Parse timeStampToken, if present + if (derValue.data.available() > 0) { + DerValue timestampToken = derValue.data.getDerValue(); + encodedTsToken = timestampToken.toByteArray(); + tsToken = new PKCS7(encodedTsToken); + tstInfo = new TimestampToken(tsToken.getContentInfo().getData()); + } + + // Check the format of the timestamp response + if (this.status == 0 || this.status == 1) { + if (tsToken == null) { + throw new TimestampException( + "Bad encoding for timestamp response: " + + "expected a timeStampToken element to be present"); + } + } else if (tsToken != null) { + throw new TimestampException( + "Bad encoding for timestamp response: " + + "expected no timeStampToken element to be present"); + } + } + + final static class TimestampException extends IOException { + private static final long serialVersionUID = -1631631794891940953L; + + TimestampException(String message) { + super(message); + } + } +} diff --git a/src/sun/security/timestamp/TimestampToken.java b/src/sun/security/timestamp/TimestampToken.java new file mode 100644 index 00000000..9df8f1c0 --- /dev/null +++ b/src/sun/security/timestamp/TimestampToken.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.timestamp; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Date; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; +import sun.security.x509.AlgorithmId; + +/** + * This class provides the timestamp token info resulting from a successful + * timestamp request, as defined in + * RFC 3161. + * + * The timestampTokenInfo ASN.1 type has the following definition: + *
    + *
    + *     TSTInfo ::= SEQUENCE {
    + *         version                INTEGER  { v1(1) },
    + *         policy                 TSAPolicyId,
    + *         messageImprint         MessageImprint,
    + *           -- MUST have the same value as the similar field in
    + *           -- TimeStampReq
    + *         serialNumber           INTEGER,
    + *          -- Time-Stamping users MUST be ready to accommodate integers
    + *          -- up to 160 bits.
    + *         genTime                GeneralizedTime,
    + *         accuracy               Accuracy                 OPTIONAL,
    + *         ordering               BOOLEAN             DEFAULT FALSE,
    + *         nonce                  INTEGER                  OPTIONAL,
    + *           -- MUST be present if the similar field was present
    + *           -- in TimeStampReq.  In that case it MUST have the same value.
    + *         tsa                    [0] GeneralName          OPTIONAL,
    + *         extensions             [1] IMPLICIT Extensions  OPTIONAL }
    + *
    + *     Accuracy ::= SEQUENCE {
    + *         seconds        INTEGER           OPTIONAL,
    + *         millis     [0] INTEGER  (1..999) OPTIONAL,
    + *         micros     [1] INTEGER  (1..999) OPTIONAL  }
    + *
    + * 
    + * + * @since 1.5 + * @see Timestamper + * @author Vincent Ryan + */ + +public class TimestampToken { + + private int version; + private ObjectIdentifier policy; + private BigInteger serialNumber; + private AlgorithmId hashAlgorithm; + private byte[] hashedMessage; + private Date genTime; + private BigInteger nonce; + + /** + * Constructs an object to store a timestamp token. + * + * @param status A buffer containing the ASN.1 BER encoding of the + * TSTInfo element defined in RFC 3161. + */ + public TimestampToken(byte[] timestampTokenInfo) throws IOException { + if (timestampTokenInfo == null) { + throw new IOException("No timestamp token info"); + } + parse(timestampTokenInfo); + } + + /** + * Extract the date and time from the timestamp token. + * + * @return The date and time when the timestamp was generated. + */ + public Date getDate() { + return genTime; + } + + public AlgorithmId getHashAlgorithm() { + return hashAlgorithm; + } + + // should only be used internally, otherwise return a clone + public byte[] getHashedMessage() { + return hashedMessage; + } + + public BigInteger getNonce() { + return nonce; + } + + public String getPolicyID() { + return policy.toString(); + } + + public BigInteger getSerialNumber() { + return serialNumber; + } + + /* + * Parses the timestamp token info. + * + * @param timestampTokenInfo A buffer containing an ASN.1 BER encoded + * TSTInfo. + * @throws IOException The exception is thrown if a problem is encountered + * while parsing. + */ + private void parse(byte[] timestampTokenInfo) throws IOException { + + DerValue tstInfo = new DerValue(timestampTokenInfo); + if (tstInfo.tag != DerValue.tag_Sequence) { + throw new IOException("Bad encoding for timestamp token info"); + } + // Parse version + version = tstInfo.data.getInteger(); + + // Parse policy + policy = tstInfo.data.getOID(); + + // Parse messageImprint + DerValue messageImprint = tstInfo.data.getDerValue(); + hashAlgorithm = AlgorithmId.parse(messageImprint.data.getDerValue()); + hashedMessage = messageImprint.data.getOctetString(); + + // Parse serialNumber + serialNumber = tstInfo.data.getBigInteger(); + + // Parse genTime + genTime = tstInfo.data.getGeneralizedTime(); + + // Parse optional elements, if present + while (tstInfo.data.available() > 0) { + DerValue d = tstInfo.data.getDerValue(); + if (d.tag == DerValue.tag_Integer) { // must be the nonce + nonce = d.getBigInteger(); + break; + } + + // Additional fields: + // Parse accuracy + // Parse ordering + // Parse tsa + // Parse extensions + } + } +} diff --git a/src/sun/security/timestamp/Timestamper.java b/src/sun/security/timestamp/Timestamper.java new file mode 100644 index 00000000..0e8fc5e7 --- /dev/null +++ b/src/sun/security/timestamp/Timestamper.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.timestamp; + +import java.io.IOException; + +/** + * A timestamping service which conforms to the Time-Stamp Protocol (TSP) + * defined in: + * RFC 3161. + * Individual timestampers may communicate with a Timestamping Authority (TSA) + * over different transport machanisms. TSP permits at least the following + * transports: HTTP, Internet mail, file-based and socket-based. + * + * @author Vincent Ryan + * @see HttpTimestamper + */ +public interface Timestamper { + + /* + * Connects to the TSA and requests a timestamp. + * + * @param tsQuery The timestamp query. + * @return The result of the timestamp query. + * @throws IOException The exception is thrown if a problem occurs while + * communicating with the TSA. + */ + public TSResponse generateTimestamp(TSRequest tsQuery) throws IOException; +} diff --git a/src/sun/security/tools/KeyStoreUtil.java b/src/sun/security/tools/KeyStoreUtil.java new file mode 100644 index 00000000..ecfba5fe --- /dev/null +++ b/src/sun/security/tools/KeyStoreUtil.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +import java.net.URL; + +import java.security.KeyStore; + +import java.text.Collator; + +import java.util.Locale; + +/** + *

    This class provides several utilities to KeyStore. + * + * @since 1.6.0 + */ +public class KeyStoreUtil { + + private KeyStoreUtil() { + // this class is not meant to be instantiated + } + + private static final String JKS = "jks"; + + private static final Collator collator = Collator.getInstance(); + static { + // this is for case insensitive string comparisons + collator.setStrength(Collator.PRIMARY); + }; + + /** + * Returns true if KeyStore has a password. This is true except for + * MSCAPI KeyStores + */ + public static boolean isWindowsKeyStore(String storetype) { + return storetype.equalsIgnoreCase("Windows-MY") + || storetype.equalsIgnoreCase("Windows-ROOT"); + } + + /** + * Returns standard-looking names for storetype + */ + public static String niceStoreTypeName(String storetype) { + if (storetype.equalsIgnoreCase("Windows-MY")) { + return "Windows-MY"; + } else if(storetype.equalsIgnoreCase("Windows-ROOT")) { + return "Windows-ROOT"; + } else { + return storetype.toUpperCase(Locale.ENGLISH); + } + } + + /** + * Returns the keystore with the configured CA certificates. + */ + public static KeyStore getCacertsKeyStore() + throws Exception + { + String sep = File.separator; + File file = new File(System.getProperty("java.home") + sep + + "lib" + sep + "security" + sep + + "cacerts"); + if (!file.exists()) { + return null; + } + KeyStore caks = null; + try (FileInputStream fis = new FileInputStream(file)) { + caks = KeyStore.getInstance(JKS); + caks.load(fis, null); + } + return caks; + } + + public static char[] getPassWithModifier(String modifier, String arg, + java.util.ResourceBundle rb) { + if (modifier == null) { + return arg.toCharArray(); + } else if (collator.compare(modifier, "env") == 0) { + String value = System.getenv(arg); + if (value == null) { + System.err.println(rb.getString( + "Cannot.find.environment.variable.") + arg); + return null; + } else { + return value.toCharArray(); + } + } else if (collator.compare(modifier, "file") == 0) { + try { + URL url = null; + try { + url = new URL(arg); + } catch (java.net.MalformedURLException mue) { + File f = new File(arg); + if (f.exists()) { + url = f.toURI().toURL(); + } else { + System.err.println(rb.getString( + "Cannot.find.file.") + arg); + return null; + } + } + + try (BufferedReader br = + new BufferedReader(new InputStreamReader( + url.openStream()))) { + String value = br.readLine(); + + if (value == null) { + return new char[0]; + } + + return value.toCharArray(); + } + } catch (IOException ioe) { + System.err.println(ioe); + return null; + } + } else { + System.err.println(rb.getString("Unknown.password.type.") + + modifier); + return null; + } + } +} diff --git a/src/sun/security/tools/PathList.java b/src/sun/security/tools/PathList.java new file mode 100644 index 00000000..2911843a --- /dev/null +++ b/src/sun/security/tools/PathList.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools; + +import java.io.File; +import java.io.IOException; +import java.lang.String; +import java.util.StringTokenizer; +import java.net.URL; +import java.net.MalformedURLException; + +/** + * A utility class for handle path list + * + */ +public class PathList { + /** + * Utility method for appending path from pathFrom to pathTo. + * + * @param pathTo the target path + * @param pathSource the path to be appended to pathTo + * @return the resulting path + */ + public static String appendPath(String pathTo, String pathFrom) { + if (pathTo == null || pathTo.length() == 0) { + return pathFrom; + } else if (pathFrom == null || pathFrom.length() == 0) { + return pathTo; + } else { + return pathTo + File.pathSeparator + pathFrom; + } + } + + /** + * Utility method for converting a search path string to an array + * of directory and JAR file URLs. + * + * @param path the search path string + * @return the resulting array of directory and JAR file URLs + */ + public static URL[] pathToURLs(String path) { + StringTokenizer st = new StringTokenizer(path, File.pathSeparator); + URL[] urls = new URL[st.countTokens()]; + int count = 0; + while (st.hasMoreTokens()) { + URL url = fileToURL(new File(st.nextToken())); + if (url != null) { + urls[count++] = url; + } + } + if (urls.length != count) { + URL[] tmp = new URL[count]; + System.arraycopy(urls, 0, tmp, 0, count); + urls = tmp; + } + return urls; + } + + /** + * Returns the directory or JAR file URL corresponding to the specified + * local file name. + * + * @param file the File object + * @return the resulting directory or JAR file URL, or null if unknown + */ + private static URL fileToURL(File file) { + String name; + try { + name = file.getCanonicalPath(); + } catch (IOException e) { + name = file.getAbsolutePath(); + } + name = name.replace(File.separatorChar, '/'); + if (!name.startsWith("/")) { + name = "/" + name; + } + // If the file does not exist, then assume that it's a directory + if (!file.isFile()) { + name = name + "/"; + } + try { + return new URL("file", "", name); + } catch (MalformedURLException e) { + throw new IllegalArgumentException("file"); + } + } +} diff --git a/src/sun/security/tools/jarsigner/Main.java b/src/sun/security/tools/jarsigner/Main.java new file mode 100644 index 00000000..99d71643 --- /dev/null +++ b/src/sun/security/tools/jarsigner/Main.java @@ -0,0 +1,2519 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.jarsigner; + +import java.io.*; +import java.util.*; +import java.util.zip.*; +import java.util.jar.*; +import java.math.BigInteger; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.Collator; +import java.text.MessageFormat; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.*; +import java.lang.reflect.Constructor; + +import com.sun.jarsigner.ContentSigner; +import com.sun.jarsigner.ContentSignerParameters; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.util.Map.Entry; +import sun.security.tools.KeyStoreUtil; +import sun.security.tools.PathList; +import sun.security.x509.*; +import sun.security.util.*; +import java.util.Base64; + + +/** + *

    The jarsigner utility. + * + * The exit codes for the main method are: + * + * 0: success + * 1: any error that the jar cannot be signed or verified, including: + * keystore loading error + * TSP communication error + * jarsigner command line error... + * otherwise: error codes from -strict + * + * @author Roland Schemers + * @author Jan Luehe + */ + +public class Main { + + // for i18n + private static final ResourceBundle rb = + ResourceBundle.getBundle + ("sun.security.tools.jarsigner.Resources"); + private static final Collator collator = Collator.getInstance(); + static { + // this is for case insensitive string comparisions + collator.setStrength(Collator.PRIMARY); + } + + private static final String META_INF = "META-INF/"; + + private static final Class[] PARAM_STRING = { String.class }; + + private static final String NONE = "NONE"; + private static final String P11KEYSTORE = "PKCS11"; + + private static final long SIX_MONTHS = 180*24*60*60*1000L; //milliseconds + + // Attention: + // This is the entry that get launched by the security tool jarsigner. + public static void main(String args[]) throws Exception { + Main js = new Main(); + js.run(args); + } + + static final String VERSION = "1.0"; + + static final int IN_KEYSTORE = 0x01; // signer is in keystore + static final int IN_SCOPE = 0x02; + static final int NOT_ALIAS = 0x04; // alias list is NOT empty and + // signer is not in alias list + static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list + + X509Certificate[] certChain; // signer's cert chain (when composing) + PrivateKey privateKey; // private key + KeyStore store; // the keystore specified by -keystore + // or the default keystore, never null + + String keystore; // key store file + boolean nullStream = false; // null keystore input stream (NONE) + boolean token = false; // token-based keystore + String jarfile; // jar files to sign or verify + String alias; // alias to sign jar with + List ckaliases = new ArrayList<>(); // aliases in -verify + char[] storepass; // keystore password + boolean protectedPath; // protected authentication path + String storetype; // keystore type + String providerName; // provider name + Vector providers = null; // list of providers + // arguments for provider constructors + HashMap providerArgs = new HashMap<>(); + char[] keypass; // private key password + String sigfile; // name of .SF file + String sigalg; // name of signature algorithm + String digestalg = "SHA-256"; // name of digest algorithm + String signedjar; // output filename + String tsaUrl; // location of the Timestamping Authority + String tsaAlias; // alias for the Timestamping Authority's certificate + String altCertChain; // file to read alternative cert chain from + String tSAPolicyID; + boolean verify = false; // verify the jar + String verbose = null; // verbose output when signing/verifying + boolean showcerts = false; // show certs when verifying + boolean debug = false; // debug + boolean signManifest = true; // "sign" the whole manifest + boolean externalSF = true; // leave the .SF out of the PKCS7 block + boolean strict = false; // treat warnings as error + + // read zip entry raw bytes + private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); + private byte[] buffer = new byte[8192]; + private ContentSigner signingMechanism = null; + private String altSignerClass = null; + private String altSignerClasspath = null; + private ZipFile zipFile = null; + + // Informational warnings + private boolean hasExpiringCert = false; + private boolean noTimestamp = false; + private Date expireDate = new Date(0L); // used in noTimestamp warning + + // Severe warnings + private boolean hasExpiredCert = false; + private boolean notYetValidCert = false; + private boolean chainNotValidated = false; + private boolean notSignedByAlias = false; + private boolean aliasNotInStore = false; + private boolean hasUnsignedEntry = false; + private boolean badKeyUsage = false; + private boolean badExtendedKeyUsage = false; + private boolean badNetscapeCertType = false; + + CertificateFactory certificateFactory; + CertPathValidator validator; + PKIXParameters pkixParameters; + + public void run(String args[]) { + try { + parseArgs(args); + + // Try to load and install the specified providers + if (providers != null) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + Enumeration e = providers.elements(); + while (e.hasMoreElements()) { + String provName = e.nextElement(); + Class provClass; + if (cl != null) { + provClass = cl.loadClass(provName); + } else { + provClass = Class.forName(provName); + } + + String provArg = providerArgs.get(provName); + Object obj; + if (provArg == null) { + obj = provClass.newInstance(); + } else { + Constructor c = + provClass.getConstructor(PARAM_STRING); + obj = c.newInstance(provArg); + } + + if (!(obj instanceof Provider)) { + MessageFormat form = new MessageFormat(rb.getString + ("provName.not.a.provider")); + Object[] source = {provName}; + throw new Exception(form.format(source)); + } + Security.addProvider((Provider)obj); + } + } + + if (verify) { + try { + loadKeyStore(keystore, false); + } catch (Exception e) { + if ((keystore != null) || (storepass != null)) { + System.out.println(rb.getString("jarsigner.error.") + + e.getMessage()); + System.exit(1); + } + } + /* if (debug) { + SignatureFileVerifier.setDebug(true); + ManifestEntryVerifier.setDebug(true); + } + */ + verifyJar(jarfile); + } else { + loadKeyStore(keystore, true); + getAliasInfo(alias); + + // load the alternative signing mechanism + if (altSignerClass != null) { + signingMechanism = loadSigningMechanism(altSignerClass, + altSignerClasspath); + } + signJar(jarfile, alias, args); + } + } catch (Exception e) { + System.out.println(rb.getString("jarsigner.error.") + e); + if (debug) { + e.printStackTrace(); + } + System.exit(1); + } finally { + // zero-out private key password + if (keypass != null) { + Arrays.fill(keypass, ' '); + keypass = null; + } + // zero-out keystore password + if (storepass != null) { + Arrays.fill(storepass, ' '); + storepass = null; + } + } + + if (strict) { + int exitCode = 0; + if (chainNotValidated || hasExpiredCert || notYetValidCert) { + exitCode |= 4; + } + if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType) { + exitCode |= 8; + } + if (hasUnsignedEntry) { + exitCode |= 16; + } + if (notSignedByAlias || aliasNotInStore) { + exitCode |= 32; + } + if (exitCode != 0) { + System.exit(exitCode); + } + } + } + + /* + * Parse command line arguments. + */ + void parseArgs(String args[]) { + /* parse flags */ + int n = 0; + + if (args.length == 0) fullusage(); + for (n=0; n < args.length; n++) { + + String flags = args[n]; + String modifier = null; + + if (flags.startsWith("-")) { + int pos = flags.indexOf(':'); + if (pos > 0) { + modifier = flags.substring(pos+1); + flags = flags.substring(0, pos); + } + } + + if (!flags.startsWith("-")) { + if (jarfile == null) { + jarfile = flags; + } else { + alias = flags; + ckaliases.add(alias); + } + } else if (collator.compare(flags, "-keystore") == 0) { + if (++n == args.length) usageNoArg(); + keystore = args[n]; + } else if (collator.compare(flags, "-storepass") ==0) { + if (++n == args.length) usageNoArg(); + storepass = getPass(modifier, args[n]); + } else if (collator.compare(flags, "-storetype") ==0) { + if (++n == args.length) usageNoArg(); + storetype = args[n]; + } else if (collator.compare(flags, "-providerName") ==0) { + if (++n == args.length) usageNoArg(); + providerName = args[n]; + } else if ((collator.compare(flags, "-provider") == 0) || + (collator.compare(flags, "-providerClass") == 0)) { + if (++n == args.length) usageNoArg(); + if (providers == null) { + providers = new Vector(3); + } + providers.add(args[n]); + + if (args.length > (n+1)) { + flags = args[n+1]; + if (collator.compare(flags, "-providerArg") == 0) { + if (args.length == (n+2)) usageNoArg(); + providerArgs.put(args[n], args[n+2]); + n += 2; + } + } + } else if (collator.compare(flags, "-protected") ==0) { + protectedPath = true; + } else if (collator.compare(flags, "-certchain") ==0) { + if (++n == args.length) usageNoArg(); + altCertChain = args[n]; + } else if (collator.compare(flags, "-tsapolicyid") ==0) { + if (++n == args.length) usageNoArg(); + tSAPolicyID = args[n]; + } else if (collator.compare(flags, "-debug") ==0) { + debug = true; + } else if (collator.compare(flags, "-keypass") ==0) { + if (++n == args.length) usageNoArg(); + keypass = getPass(modifier, args[n]); + } else if (collator.compare(flags, "-sigfile") ==0) { + if (++n == args.length) usageNoArg(); + sigfile = args[n]; + } else if (collator.compare(flags, "-signedjar") ==0) { + if (++n == args.length) usageNoArg(); + signedjar = args[n]; + } else if (collator.compare(flags, "-tsa") ==0) { + if (++n == args.length) usageNoArg(); + tsaUrl = args[n]; + } else if (collator.compare(flags, "-tsacert") ==0) { + if (++n == args.length) usageNoArg(); + tsaAlias = args[n]; + } else if (collator.compare(flags, "-altsigner") ==0) { + if (++n == args.length) usageNoArg(); + altSignerClass = args[n]; + } else if (collator.compare(flags, "-altsignerpath") ==0) { + if (++n == args.length) usageNoArg(); + altSignerClasspath = args[n]; + } else if (collator.compare(flags, "-sectionsonly") ==0) { + signManifest = false; + } else if (collator.compare(flags, "-internalsf") ==0) { + externalSF = false; + } else if (collator.compare(flags, "-verify") ==0) { + verify = true; + } else if (collator.compare(flags, "-verbose") ==0) { + verbose = (modifier != null) ? modifier : "all"; + } else if (collator.compare(flags, "-sigalg") ==0) { + if (++n == args.length) usageNoArg(); + sigalg = args[n]; + } else if (collator.compare(flags, "-digestalg") ==0) { + if (++n == args.length) usageNoArg(); + digestalg = args[n]; + } else if (collator.compare(flags, "-certs") ==0) { + showcerts = true; + } else if (collator.compare(flags, "-strict") ==0) { + strict = true; + } else if (collator.compare(flags, "-h") == 0 || + collator.compare(flags, "-help") == 0) { + fullusage(); + } else { + System.err.println( + rb.getString("Illegal.option.") + flags); + usage(); + } + } + + // -certs must always be specified with -verbose + if (verbose == null) showcerts = false; + + if (jarfile == null) { + System.err.println(rb.getString("Please.specify.jarfile.name")); + usage(); + } + if (!verify && alias == null) { + System.err.println(rb.getString("Please.specify.alias.name")); + usage(); + } + if (!verify && ckaliases.size() > 1) { + System.err.println(rb.getString("Only.one.alias.can.be.specified")); + usage(); + } + + if (storetype == null) { + storetype = KeyStore.getDefaultType(); + } + storetype = KeyStoreUtil.niceStoreTypeName(storetype); + + try { + if (signedjar != null && new File(signedjar).getCanonicalPath().equals( + new File(jarfile).getCanonicalPath())) { + signedjar = null; + } + } catch (IOException ioe) { + // File system error? + // Just ignore it. + } + + if (P11KEYSTORE.equalsIgnoreCase(storetype) || + KeyStoreUtil.isWindowsKeyStore(storetype)) { + token = true; + if (keystore == null) { + keystore = NONE; + } + } + + if (NONE.equals(keystore)) { + nullStream = true; + } + + if (token && !nullStream) { + System.err.println(MessageFormat.format(rb.getString + (".keystore.must.be.NONE.if.storetype.is.{0}"), storetype)); + usage(); + } + + if (token && keypass != null) { + System.err.println(MessageFormat.format(rb.getString + (".keypass.can.not.be.specified.if.storetype.is.{0}"), storetype)); + usage(); + } + + if (protectedPath) { + if (storepass != null || keypass != null) { + System.err.println(rb.getString + ("If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified")); + usage(); + } + } + if (KeyStoreUtil.isWindowsKeyStore(storetype)) { + if (storepass != null || keypass != null) { + System.err.println(rb.getString + ("If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified")); + usage(); + } + } + } + + static char[] getPass(String modifier, String arg) { + char[] output = KeyStoreUtil.getPassWithModifier(modifier, arg, rb); + if (output != null) return output; + usage(); + return null; // Useless, usage() already exit + } + + static void usageNoArg() { + System.out.println(rb.getString("Option.lacks.argument")); + usage(); + } + + static void usage() { + System.out.println(); + System.out.println(rb.getString("Please.type.jarsigner.help.for.usage")); + System.exit(1); + } + + static void fullusage() { + System.out.println(rb.getString + ("Usage.jarsigner.options.jar.file.alias")); + System.out.println(rb.getString + (".jarsigner.verify.options.jar.file.alias.")); + System.out.println(); + System.out.println(rb.getString + (".keystore.url.keystore.location")); + System.out.println(); + System.out.println(rb.getString + (".storepass.password.password.for.keystore.integrity")); + System.out.println(); + System.out.println(rb.getString + (".storetype.type.keystore.type")); + System.out.println(); + System.out.println(rb.getString + (".keypass.password.password.for.private.key.if.different.")); + System.out.println(); + System.out.println(rb.getString + (".certchain.file.name.of.alternative.certchain.file")); + System.out.println(); + System.out.println(rb.getString + (".sigfile.file.name.of.SF.DSA.file")); + System.out.println(); + System.out.println(rb.getString + (".signedjar.file.name.of.signed.JAR.file")); + System.out.println(); + System.out.println(rb.getString + (".digestalg.algorithm.name.of.digest.algorithm")); + System.out.println(); + System.out.println(rb.getString + (".sigalg.algorithm.name.of.signature.algorithm")); + System.out.println(); + System.out.println(rb.getString + (".verify.verify.a.signed.JAR.file")); + System.out.println(); + System.out.println(rb.getString + (".verbose.suboptions.verbose.output.when.signing.verifying.")); + System.out.println(rb.getString + (".suboptions.can.be.all.grouped.or.summary")); + System.out.println(); + System.out.println(rb.getString + (".certs.display.certificates.when.verbose.and.verifying")); + System.out.println(); + System.out.println(rb.getString + (".tsa.url.location.of.the.Timestamping.Authority")); + System.out.println(); + System.out.println(rb.getString + (".tsacert.alias.public.key.certificate.for.Timestamping.Authority")); + System.out.println(); + System.out.println(rb.getString + (".tsapolicyid.tsapolicyid.for.Timestamping.Authority")); + System.out.println(); + System.out.println(rb.getString + (".altsigner.class.class.name.of.an.alternative.signing.mechanism")); + System.out.println(); + System.out.println(rb.getString + (".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism")); + System.out.println(); + System.out.println(rb.getString + (".internalsf.include.the.SF.file.inside.the.signature.block")); + System.out.println(); + System.out.println(rb.getString + (".sectionsonly.don.t.compute.hash.of.entire.manifest")); + System.out.println(); + System.out.println(rb.getString + (".protected.keystore.has.protected.authentication.path")); + System.out.println(); + System.out.println(rb.getString + (".providerName.name.provider.name")); + System.out.println(); + System.out.println(rb.getString + (".providerClass.class.name.of.cryptographic.service.provider.s")); + System.out.println(rb.getString + (".providerArg.arg.master.class.file.and.constructor.argument")); + System.out.println(); + System.out.println(rb.getString + (".strict.treat.warnings.as.errors")); + System.out.println(); + + System.exit(0); + } + + void verifyJar(String jarName) + throws Exception + { + boolean anySigned = false; // if there exists entry inside jar signed + JarFile jf = null; + + try { + jf = new JarFile(jarName, true); + Vector entriesVec = new Vector<>(); + byte[] buffer = new byte[8192]; + + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + entriesVec.addElement(je); + InputStream is = null; + try { + is = jf.getInputStream(je); + int n; + while ((n = is.read(buffer, 0, buffer.length)) != -1) { + // we just read. this will throw a SecurityException + // if a signature/digest check fails. + } + } finally { + if (is != null) { + is.close(); + } + } + } + + Manifest man = jf.getManifest(); + + // The map to record display info, only used when -verbose provided + // key: signer info string + // value: the list of files with common key + Map> output = new LinkedHashMap<>(); + + if (man != null) { + if (verbose != null) System.out.println(); + Enumeration e = entriesVec.elements(); + + String tab = rb.getString("6SPACE"); + + while (e.hasMoreElements()) { + JarEntry je = e.nextElement(); + String name = je.getName(); + CodeSigner[] signers = je.getCodeSigners(); + boolean isSigned = (signers != null); + anySigned |= isSigned; + hasUnsignedEntry |= !je.isDirectory() && !isSigned + && !signatureRelated(name); + + int inStoreOrScope = inKeyStore(signers); + + boolean inStore = (inStoreOrScope & IN_KEYSTORE) != 0; + boolean inScope = (inStoreOrScope & IN_SCOPE) != 0; + + notSignedByAlias |= (inStoreOrScope & NOT_ALIAS) != 0; + if (keystore != null) { + aliasNotInStore |= isSigned && (!inStore && !inScope); + } + + // Only used when -verbose provided + StringBuffer sb = null; + if (verbose != null) { + sb = new StringBuffer(); + boolean inManifest = + ((man.getAttributes(name) != null) || + (man.getAttributes("./"+name) != null) || + (man.getAttributes("/"+name) != null)); + sb.append( + (isSigned ? rb.getString("s") : rb.getString("SPACE")) + + (inManifest ? rb.getString("m") : rb.getString("SPACE")) + + (inStore ? rb.getString("k") : rb.getString("SPACE")) + + (inScope ? rb.getString("i") : rb.getString("SPACE")) + + ((inStoreOrScope & NOT_ALIAS) != 0 ?"X":" ") + + rb.getString("SPACE")); + sb.append("|"); + } + + // When -certs provided, display info has extra empty + // lines at the beginning and end. + if (isSigned) { + if (showcerts) sb.append('\n'); + for (CodeSigner signer: signers) { + // signerInfo() must be called even if -verbose + // not provided. The method updates various + // warning flags. + String si = signerInfo(signer, tab); + if (showcerts) { + sb.append(si); + sb.append('\n'); + } + } + } else if (showcerts && !verbose.equals("all")) { + // Print no info for unsigned entries when -verbose:all, + // to be consistent with old behavior. + if (signatureRelated(name)) { + sb.append("\n" + tab + rb.getString( + ".Signature.related.entries.") + "\n\n"); + } else { + sb.append("\n" + tab + rb.getString( + ".Unsigned.entries.") + "\n\n"); + } + } + + if (verbose != null) { + String label = sb.toString(); + if (signatureRelated(name)) { + // Entries inside META-INF and other unsigned + // entries are grouped separately. + label = "-" + label; + } + + // The label finally contains 2 parts separated by '|': + // The legend displayed before the entry names, and + // the cert info (if -certs specified). + + if (!output.containsKey(label)) { + output.put(label, new ArrayList()); + } + + StringBuffer fb = new StringBuffer(); + String s = Long.toString(je.getSize()); + for (int i = 6 - s.length(); i > 0; --i) { + fb.append(' '); + } + fb.append(s).append(' '). + append(new Date(je.getTime()).toString()); + fb.append(' ').append(name); + + output.get(label).add(fb.toString()); + } + } + } + if (verbose != null) { + for (Entry> s: output.entrySet()) { + List files = s.getValue(); + String key = s.getKey(); + if (key.charAt(0) == '-') { // the signature-related group + key = key.substring(1); + } + int pipe = key.indexOf('|'); + if (verbose.equals("all")) { + for (String f: files) { + System.out.println(key.substring(0, pipe) + f); + System.out.printf(key.substring(pipe+1)); + } + } else { + if (verbose.equals("grouped")) { + for (String f: files) { + System.out.println(key.substring(0, pipe) + f); + } + } else if (verbose.equals("summary")) { + System.out.print(key.substring(0, pipe)); + if (files.size() > 1) { + System.out.println(files.get(0) + " " + + String.format(rb.getString( + ".and.d.more."), files.size()-1)); + } else { + System.out.println(files.get(0)); + } + } + System.out.printf(key.substring(pipe+1)); + } + } + System.out.println(); + System.out.println(rb.getString( + ".s.signature.was.verified.")); + System.out.println(rb.getString( + ".m.entry.is.listed.in.manifest")); + System.out.println(rb.getString( + ".k.at.least.one.certificate.was.found.in.keystore")); + System.out.println(rb.getString( + ".i.at.least.one.certificate.was.found.in.identity.scope")); + if (ckaliases.size() > 0) { + System.out.println(rb.getString( + ".X.not.signed.by.specified.alias.es.")); + } + System.out.println(); + } + if (man == null) + System.out.println(rb.getString("no.manifest.")); + + if (!anySigned) { + System.out.println(rb.getString( + "jar.is.unsigned.signatures.missing.or.not.parsable.")); + } else { + boolean warningAppeared = false; + boolean errorAppeared = false; + if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || + notYetValidCert || chainNotValidated || hasExpiredCert || + hasUnsignedEntry || + aliasNotInStore || notSignedByAlias) { + + if (strict) { + System.out.println(rb.getString("jar.verified.with.signer.errors.")); + System.out.println(); + System.out.println(rb.getString("Error.")); + errorAppeared = true; + } else { + System.out.println(rb.getString("jar.verified.")); + System.out.println(); + System.out.println(rb.getString("Warning.")); + warningAppeared = true; + } + + if (badKeyUsage) { + System.out.println( + rb.getString("This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); + } + + if (badExtendedKeyUsage) { + System.out.println( + rb.getString("This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); + } + + if (badNetscapeCertType) { + System.out.println( + rb.getString("This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); + } + + if (hasUnsignedEntry) { + System.out.println(rb.getString( + "This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.")); + } + if (hasExpiredCert) { + System.out.println(rb.getString( + "This.jar.contains.entries.whose.signer.certificate.has.expired.")); + } + if (notYetValidCert) { + System.out.println(rb.getString( + "This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.")); + } + + if (chainNotValidated) { + System.out.println( + rb.getString("This.jar.contains.entries.whose.certificate.chain.is.not.validated.")); + } + + if (notSignedByAlias) { + System.out.println( + rb.getString("This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.")); + } + + if (aliasNotInStore) { + System.out.println(rb.getString("This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.")); + } + } else { + System.out.println(rb.getString("jar.verified.")); + } + if (hasExpiringCert || noTimestamp) { + if (!warningAppeared) { + System.out.println(); + System.out.println(rb.getString("Warning.")); + warningAppeared = true; + } + if (hasExpiringCert) { + System.out.println(rb.getString( + "This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.")); + } + if (noTimestamp) { + System.out.println( + String.format(rb.getString("no.timestamp.verifying"), expireDate)); + } + } + if (warningAppeared || errorAppeared) { + if (! (verbose != null && showcerts)) { + System.out.println(); + System.out.println(rb.getString( + "Re.run.with.the.verbose.and.certs.options.for.more.details.")); + } + } + } + return; + } catch (Exception e) { + System.out.println(rb.getString("jarsigner.") + e); + if (debug) { + e.printStackTrace(); + } + } finally { // close the resource + if (jf != null) { + jf.close(); + } + } + + System.exit(1); + } + + private static MessageFormat validityTimeForm = null; + private static MessageFormat notYetTimeForm = null; + private static MessageFormat expiredTimeForm = null; + private static MessageFormat expiringTimeForm = null; + + /* + * Display some details about a certificate: + * + * [] [", " ] [" (" ")"] + * [ | ] + * + * Note: no newline character at the end + */ + String printCert(String tab, Certificate c, boolean checkValidityPeriod, + Date timestamp, boolean checkUsage) { + + StringBuilder certStr = new StringBuilder(); + String space = rb.getString("SPACE"); + X509Certificate x509Cert = null; + + if (c instanceof X509Certificate) { + x509Cert = (X509Certificate) c; + certStr.append(tab).append(x509Cert.getType()) + .append(rb.getString("COMMA")) + .append(x509Cert.getSubjectDN().getName()); + } else { + certStr.append(tab).append(c.getType()); + } + + String alias = storeHash.get(c); + if (alias != null) { + certStr.append(space).append(alias); + } + + if (checkValidityPeriod && x509Cert != null) { + + certStr.append("\n").append(tab).append("["); + Date notAfter = x509Cert.getNotAfter(); + try { + boolean printValidity = true; + if (timestamp == null) { + if (expireDate.getTime() == 0 || expireDate.after(notAfter)) { + expireDate = notAfter; + } + x509Cert.checkValidity(); + // test if cert will expire within six months + if (notAfter.getTime() < System.currentTimeMillis() + SIX_MONTHS) { + hasExpiringCert = true; + if (expiringTimeForm == null) { + expiringTimeForm = new MessageFormat( + rb.getString("certificate.will.expire.on")); + } + Object[] source = { notAfter }; + certStr.append(expiringTimeForm.format(source)); + printValidity = false; + } + } else { + x509Cert.checkValidity(timestamp); + } + if (printValidity) { + if (validityTimeForm == null) { + validityTimeForm = new MessageFormat( + rb.getString("certificate.is.valid.from")); + } + Object[] source = { x509Cert.getNotBefore(), notAfter }; + certStr.append(validityTimeForm.format(source)); + } + } catch (CertificateExpiredException cee) { + hasExpiredCert = true; + + if (expiredTimeForm == null) { + expiredTimeForm = new MessageFormat( + rb.getString("certificate.expired.on")); + } + Object[] source = { notAfter }; + certStr.append(expiredTimeForm.format(source)); + + } catch (CertificateNotYetValidException cnyve) { + notYetValidCert = true; + + if (notYetTimeForm == null) { + notYetTimeForm = new MessageFormat( + rb.getString("certificate.is.not.valid.until")); + } + Object[] source = { x509Cert.getNotBefore() }; + certStr.append(notYetTimeForm.format(source)); + } + certStr.append("]"); + + if (checkUsage) { + boolean[] bad = new boolean[3]; + checkCertUsage(x509Cert, bad); + if (bad[0] || bad[1] || bad[2]) { + String x = ""; + if (bad[0]) { + x ="KeyUsage"; + } + if (bad[1]) { + if (x.length() > 0) x = x + ", "; + x = x + "ExtendedKeyUsage"; + } + if (bad[2]) { + if (x.length() > 0) x = x + ", "; + x = x + "NetscapeCertType"; + } + certStr.append("\n").append(tab) + .append(MessageFormat.format(rb.getString( + ".{0}.extension.does.not.support.code.signing."), x)); + } + } + } + return certStr.toString(); + } + + private static MessageFormat signTimeForm = null; + + private String printTimestamp(String tab, Timestamp timestamp) { + + if (signTimeForm == null) { + signTimeForm = + new MessageFormat(rb.getString("entry.was.signed.on")); + } + Object[] source = { timestamp.getTimestamp() }; + + return new StringBuilder().append(tab).append("[") + .append(signTimeForm.format(source)).append("]").toString(); + } + + private Map cacheForInKS = new IdentityHashMap<>(); + + private int inKeyStoreForOneSigner(CodeSigner signer) { + if (cacheForInKS.containsKey(signer)) { + return cacheForInKS.get(signer); + } + + boolean found = false; + int result = 0; + List certs = signer.getSignerCertPath().getCertificates(); + for (Certificate c : certs) { + String alias = storeHash.get(c); + if (alias != null) { + if (alias.startsWith("(")) { + result |= IN_KEYSTORE; + } else if (alias.startsWith("[")) { + result |= IN_SCOPE; + } + if (ckaliases.contains(alias.substring(1, alias.length() - 1))) { + result |= SIGNED_BY_ALIAS; + } + } else { + if (store != null) { + try { + alias = store.getCertificateAlias(c); + } catch (KeyStoreException kse) { + // never happens, because keystore has been loaded + } + if (alias != null) { + storeHash.put(c, "(" + alias + ")"); + found = true; + result |= IN_KEYSTORE; + } + } + if (ckaliases.contains(alias)) { + result |= SIGNED_BY_ALIAS; + } + } + } + cacheForInKS.put(signer, result); + return result; + } + + Hashtable storeHash = new Hashtable<>(); + + int inKeyStore(CodeSigner[] signers) { + + if (signers == null) + return 0; + + int output = 0; + + for (CodeSigner signer: signers) { + int result = inKeyStoreForOneSigner(signer); + output |= result; + } + if (ckaliases.size() > 0 && (output & SIGNED_BY_ALIAS) == 0) { + output |= NOT_ALIAS; + } + return output; + } + + void signJar(String jarName, String alias, String[] args) + throws Exception { + boolean aliasUsed = false; + X509Certificate tsaCert = null; + + if (sigfile == null) { + sigfile = alias; + aliasUsed = true; + } + + if (sigfile.length() > 8) { + sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH); + } else { + sigfile = sigfile.toUpperCase(Locale.ENGLISH); + } + + StringBuilder tmpSigFile = new StringBuilder(sigfile.length()); + for (int j = 0; j < sigfile.length(); j++) { + char c = sigfile.charAt(j); + if (! + ((c>= 'A' && c<= 'Z') || + (c>= '0' && c<= '9') || + (c == '-') || + (c == '_'))) { + if (aliasUsed) { + // convert illegal characters from the alias to be _'s + c = '_'; + } else { + throw new + RuntimeException(rb.getString + ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.")); + } + } + tmpSigFile.append(c); + } + + sigfile = tmpSigFile.toString(); + + String tmpJarName; + if (signedjar == null) tmpJarName = jarName+".sig"; + else tmpJarName = signedjar; + + File jarFile = new File(jarName); + File signedJarFile = new File(tmpJarName); + + // Open the jar (zip) file + try { + zipFile = new ZipFile(jarName); + } catch (IOException ioe) { + error(rb.getString("unable.to.open.jar.file.")+jarName, ioe); + } + + FileOutputStream fos = null; + try { + fos = new FileOutputStream(signedJarFile); + } catch (IOException ioe) { + error(rb.getString("unable.to.create.")+tmpJarName, ioe); + } + + PrintStream ps = new PrintStream(fos); + ZipOutputStream zos = new ZipOutputStream(ps); + + /* First guess at what they might be - we don't xclude RSA ones. */ + String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH); + String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH); + + Manifest manifest = new Manifest(); + Map mfEntries = manifest.getEntries(); + + // The Attributes of manifest before updating + Attributes oldAttr = null; + + boolean mfModified = false; + boolean mfCreated = false; + byte[] mfRawBytes = null; + + try { + MessageDigest digests[] = { MessageDigest.getInstance(digestalg) }; + + // Check if manifest exists + ZipEntry mfFile; + if ((mfFile = getManifestFile(zipFile)) != null) { + // Manifest exists. Read its raw bytes. + mfRawBytes = getBytes(zipFile, mfFile); + manifest.read(new ByteArrayInputStream(mfRawBytes)); + oldAttr = (Attributes)(manifest.getMainAttributes().clone()); + } else { + // Create new manifest + Attributes mattr = manifest.getMainAttributes(); + mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(), + "1.0"); + String javaVendor = System.getProperty("java.vendor"); + String jdkVersion = System.getProperty("java.version"); + mattr.putValue("Created-By", jdkVersion + " (" +javaVendor + + ")"); + mfFile = new ZipEntry(JarFile.MANIFEST_NAME); + mfCreated = true; + } + + /* + * For each entry in jar + * (except for signature-related META-INF entries), + * do the following: + * + * - if entry is not contained in manifest, add it to manifest; + * - if entry is contained in manifest, calculate its hash and + * compare it with the one in the manifest; if they are + * different, replace the hash in the manifest with the newly + * generated one. (This may invalidate existing signatures!) + */ + Vector mfFiles = new Vector<>(); + + boolean wasSigned = false; + + for (Enumeration enum_=zipFile.entries(); + enum_.hasMoreElements();) { + ZipEntry ze = enum_.nextElement(); + + if (ze.getName().startsWith(META_INF)) { + // Store META-INF files in vector, so they can be written + // out first + mfFiles.addElement(ze); + + if (SignatureFileVerifier.isBlockOrSF( + ze.getName().toUpperCase(Locale.ENGLISH))) { + wasSigned = true; + } + + if (signatureRelated(ze.getName())) { + // ignore signature-related and manifest files + continue; + } + } + + if (manifest.getAttributes(ze.getName()) != null) { + // jar entry is contained in manifest, check and + // possibly update its digest attributes + if (updateDigests(ze, zipFile, digests, + manifest) == true) { + mfModified = true; + } + } else if (!ze.isDirectory()) { + // Add entry to manifest + Attributes attrs = getDigestAttributes(ze, zipFile, + digests); + mfEntries.put(ze.getName(), attrs); + mfModified = true; + } + } + + // Recalculate the manifest raw bytes if necessary + if (mfModified) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + manifest.write(baos); + if (wasSigned) { + byte[] newBytes = baos.toByteArray(); + if (mfRawBytes != null + && oldAttr.equals(manifest.getMainAttributes())) { + + /* + * Note: + * + * The Attributes object is based on HashMap and can handle + * continuation columns. Therefore, even if the contents are + * not changed (in a Map view), the bytes that it write() + * may be different from the original bytes that it read() + * from. Since the signature on the main attributes is based + * on raw bytes, we must retain the exact bytes. + */ + + int newPos = findHeaderEnd(newBytes); + int oldPos = findHeaderEnd(mfRawBytes); + + if (newPos == oldPos) { + System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos); + } else { + // cat oldHead newTail > newBytes + byte[] lastBytes = new byte[oldPos + + newBytes.length - newPos]; + System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos); + System.arraycopy(newBytes, newPos, lastBytes, oldPos, + newBytes.length - newPos); + newBytes = lastBytes; + } + } + mfRawBytes = newBytes; + } else { + mfRawBytes = baos.toByteArray(); + } + } + + // Write out the manifest + if (mfModified) { + // manifest file has new length + mfFile = new ZipEntry(JarFile.MANIFEST_NAME); + } + if (verbose != null) { + if (mfCreated) { + System.out.println(rb.getString(".adding.") + + mfFile.getName()); + } else if (mfModified) { + System.out.println(rb.getString(".updating.") + + mfFile.getName()); + } + } + zos.putNextEntry(mfFile); + zos.write(mfRawBytes); + + // Calculate SignatureFile (".SF") and SignatureBlockFile + ManifestDigester manDig = new ManifestDigester(mfRawBytes); + SignatureFile sf = new SignatureFile(digests, manifest, manDig, + sigfile, signManifest); + + if (tsaAlias != null) { + tsaCert = getTsaCert(tsaAlias); + } + + if (tsaUrl == null && tsaCert == null) { + noTimestamp = true; + } + + SignatureFile.Block block = null; + + try { + block = + sf.generateBlock(privateKey, sigalg, certChain, + externalSF, tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args, + zipFile); + } catch (SocketTimeoutException e) { + // Provide a helpful message when TSA is beyond a firewall + error(rb.getString("unable.to.sign.jar.") + + rb.getString("no.response.from.the.Timestamping.Authority.") + + "\n -J-Dhttp.proxyHost=" + + "\n -J-Dhttp.proxyPort=\n" + + rb.getString("or") + + "\n -J-Dhttps.proxyHost= " + + "\n -J-Dhttps.proxyPort= ", e); + } + + sfFilename = sf.getMetaName(); + bkFilename = block.getMetaName(); + + ZipEntry sfFile = new ZipEntry(sfFilename); + ZipEntry bkFile = new ZipEntry(bkFilename); + + long time = System.currentTimeMillis(); + sfFile.setTime(time); + bkFile.setTime(time); + + // signature file + zos.putNextEntry(sfFile); + sf.write(zos); + if (verbose != null) { + if (zipFile.getEntry(sfFilename) != null) { + System.out.println(rb.getString(".updating.") + + sfFilename); + } else { + System.out.println(rb.getString(".adding.") + + sfFilename); + } + } + + if (verbose != null) { + if (tsaUrl != null || tsaCert != null) { + System.out.println( + rb.getString("requesting.a.signature.timestamp")); + } + if (tsaUrl != null) { + System.out.println(rb.getString("TSA.location.") + tsaUrl); + } + if (tsaCert != null) { + URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert); + if (tsaURI != null) { + System.out.println(rb.getString("TSA.location.") + + tsaURI); + } + System.out.println(rb.getString("TSA.certificate.") + + printCert("", tsaCert, false, null, false)); + } + if (signingMechanism != null) { + System.out.println( + rb.getString("using.an.alternative.signing.mechanism")); + } + } + + // signature block file + zos.putNextEntry(bkFile); + block.write(zos); + if (verbose != null) { + if (zipFile.getEntry(bkFilename) != null) { + System.out.println(rb.getString(".updating.") + + bkFilename); + } else { + System.out.println(rb.getString(".adding.") + + bkFilename); + } + } + + // Write out all other META-INF files that we stored in the + // vector + for (int i=0; i enum_=zipFile.entries(); + enum_.hasMoreElements();) { + ZipEntry ze = enum_.nextElement(); + + if (!ze.getName().startsWith(META_INF)) { + if (verbose != null) { + if (manifest.getAttributes(ze.getName()) != null) + System.out.println(rb.getString(".signing.") + + ze.getName()); + else + System.out.println(rb.getString(".adding.") + + ze.getName()); + } + writeEntry(zipFile, zos, ze); + } + } + } catch(IOException ioe) { + error(rb.getString("unable.to.sign.jar.")+ioe, ioe); + } finally { + // close the resouces + if (zipFile != null) { + zipFile.close(); + zipFile = null; + } + + if (zos != null) { + zos.close(); + } + } + + // no IOException thrown in the follow try clause, so disable + // the try clause. + // try { + if (signedjar == null) { + // attempt an atomic rename. If that fails, + // rename the original jar file, then the signed + // one, then delete the original. + if (!signedJarFile.renameTo(jarFile)) { + File origJar = new File(jarName+".orig"); + + if (jarFile.renameTo(origJar)) { + if (signedJarFile.renameTo(jarFile)) { + origJar.delete(); + } else { + MessageFormat form = new MessageFormat(rb.getString + ("attempt.to.rename.signedJarFile.to.jarFile.failed")); + Object[] source = {signedJarFile, jarFile}; + error(form.format(source)); + } + } else { + MessageFormat form = new MessageFormat(rb.getString + ("attempt.to.rename.jarFile.to.origJar.failed")); + Object[] source = {jarFile, origJar}; + error(form.format(source)); + } + } + } + + boolean warningAppeared = false; + if (badKeyUsage || badExtendedKeyUsage || badNetscapeCertType || + notYetValidCert || chainNotValidated || hasExpiredCert) { + if (strict) { + System.out.println(rb.getString("jar.signed.with.signer.errors.")); + System.out.println(); + System.out.println(rb.getString("Error.")); + } else { + System.out.println(rb.getString("jar.signed.")); + System.out.println(); + System.out.println(rb.getString("Warning.")); + warningAppeared = true; + } + + if (badKeyUsage) { + System.out.println( + rb.getString("The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.")); + } + + if (badExtendedKeyUsage) { + System.out.println( + rb.getString("The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.")); + } + + if (badNetscapeCertType) { + System.out.println( + rb.getString("The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.")); + } + + if (hasExpiredCert) { + System.out.println( + rb.getString("The.signer.certificate.has.expired.")); + } else if (notYetValidCert) { + System.out.println( + rb.getString("The.signer.certificate.is.not.yet.valid.")); + } + + if (chainNotValidated) { + System.out.println( + rb.getString("The.signer.s.certificate.chain.is.not.validated.")); + } + } else { + System.out.println(rb.getString("jar.signed.")); + } + if (hasExpiringCert || noTimestamp) { + if (!warningAppeared) { + System.out.println(); + System.out.println(rb.getString("Warning.")); + } + + if (hasExpiringCert) { + System.out.println( + rb.getString("The.signer.certificate.will.expire.within.six.months.")); + } + + if (noTimestamp) { + System.out.println( + String.format(rb.getString("no.timestamp.signing"), expireDate)); + } + } + + // no IOException thrown in the above try clause, so disable + // the catch clause. + // } catch(IOException ioe) { + // error(rb.getString("unable.to.sign.jar.")+ioe, ioe); + // } + } + + /** + * Find the length of header inside bs. The header is a multiple (>=0) + * lines of attributes plus an empty line. The empty line is included + * in the header. + */ + @SuppressWarnings("fallthrough") + private int findHeaderEnd(byte[] bs) { + // Initial state true to deal with empty header + boolean newline = true; // just met a newline + int len = bs.length; + for (int i=0; i cacheForSignerInfo = new IdentityHashMap<>(); + + /** + * Returns a string of singer info, with a newline at the end + */ + private String signerInfo(CodeSigner signer, String tab) { + if (cacheForSignerInfo.containsKey(signer)) { + return cacheForSignerInfo.get(signer); + } + StringBuffer s = new StringBuffer(); + List certs = signer.getSignerCertPath().getCertificates(); + // display the signature timestamp, if present + Date timestamp; + Timestamp ts = signer.getTimestamp(); + if (ts != null) { + s.append(printTimestamp(tab, ts)); + s.append('\n'); + timestamp = ts.getTimestamp(); + } else { + timestamp = null; + noTimestamp = true; + } + // display the certificate(s). The first one is end-entity cert and + // its KeyUsage should be checked. + boolean first = true; + for (Certificate c : certs) { + s.append(printCert(tab, c, true, timestamp, first)); + s.append('\n'); + first = false; + } + try { + validateCertChain(certs); + } catch (Exception e) { + if (debug) { + e.printStackTrace(); + } + if (e.getCause() != null && + (e.getCause() instanceof CertificateExpiredException || + e.getCause() instanceof CertificateNotYetValidException)) { + // No more warning, we alreay have hasExpiredCert or notYetValidCert + } else { + chainNotValidated = true; + s.append(tab + rb.getString(".CertPath.not.validated.") + + e.getLocalizedMessage() + "]\n"); // TODO + } + } + String result = s.toString(); + cacheForSignerInfo.put(signer, result); + return result; + } + + private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze) + throws IOException + { + ZipEntry ze2 = new ZipEntry(ze.getName()); + ze2.setMethod(ze.getMethod()); + ze2.setTime(ze.getTime()); + ze2.setComment(ze.getComment()); + ze2.setExtra(ze.getExtra()); + if (ze.getMethod() == ZipEntry.STORED) { + ze2.setSize(ze.getSize()); + ze2.setCrc(ze.getCrc()); + } + os.putNextEntry(ze2); + writeBytes(zf, ze, os); + } + + /** + * Writes all the bytes for a given entry to the specified output stream. + */ + private synchronized void writeBytes + (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException { + int n; + + InputStream is = null; + try { + is = zf.getInputStream(ze); + long left = ze.getSize(); + + while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) { + os.write(buffer, 0, n); + left -= n; + } + } finally { + if (is != null) { + is.close(); + } + } + } + + void loadKeyStore(String keyStoreName, boolean prompt) { + + if (!nullStream && keyStoreName == null) { + keyStoreName = System.getProperty("user.home") + File.separator + + ".keystore"; + } + + try { + + certificateFactory = CertificateFactory.getInstance("X.509"); + validator = CertPathValidator.getInstance("PKIX"); + Set tas = new HashSet<>(); + try { + KeyStore caks = KeyStoreUtil.getCacertsKeyStore(); + if (caks != null) { + Enumeration aliases = caks.aliases(); + while (aliases.hasMoreElements()) { + String a = aliases.nextElement(); + try { + tas.add(new TrustAnchor((X509Certificate)caks.getCertificate(a), null)); + } catch (Exception e2) { + // ignore, when a SecretkeyEntry does not include a cert + } + } + } + } catch (Exception e) { + // Ignore, if cacerts cannot be loaded + } + + if (providerName == null) { + store = KeyStore.getInstance(storetype); + } else { + store = KeyStore.getInstance(storetype, providerName); + } + + // Get pass phrase + // XXX need to disable echo; on UNIX, call getpass(char *prompt)Z + // and on NT call ?? + if (token && storepass == null && !protectedPath + && !KeyStoreUtil.isWindowsKeyStore(storetype)) { + storepass = getPass + (rb.getString("Enter.Passphrase.for.keystore.")); + } else if (!token && storepass == null && prompt) { + storepass = getPass + (rb.getString("Enter.Passphrase.for.keystore.")); + } + + try { + if (nullStream) { + store.load(null, storepass); + } else { + keyStoreName = keyStoreName.replace(File.separatorChar, '/'); + URL url = null; + try { + url = new URL(keyStoreName); + } catch (java.net.MalformedURLException e) { + // try as file + url = new File(keyStoreName).toURI().toURL(); + } + InputStream is = null; + try { + is = url.openStream(); + store.load(is, storepass); + } finally { + if (is != null) { + is.close(); + } + } + } + Enumeration aliases = store.aliases(); + while (aliases.hasMoreElements()) { + String a = aliases.nextElement(); + try { + X509Certificate c = (X509Certificate)store.getCertificate(a); + // Only add TrustedCertificateEntry and self-signed + // PrivateKeyEntry + if (store.isCertificateEntry(a) || + c.getSubjectDN().equals(c.getIssuerDN())) { + tas.add(new TrustAnchor(c, null)); + } + } catch (Exception e2) { + // ignore, when a SecretkeyEntry does not include a cert + } + } + } finally { + try { + pkixParameters = new PKIXParameters(tas); + pkixParameters.setRevocationEnabled(false); + } catch (InvalidAlgorithmParameterException ex) { + // Only if tas is empty + } + } + } catch (IOException ioe) { + throw new RuntimeException(rb.getString("keystore.load.") + + ioe.getMessage()); + } catch (CertificateException ce) { + throw new RuntimeException(rb.getString("certificate.exception.") + + ce.getMessage()); + } catch (NoSuchProviderException pe) { + throw new RuntimeException(rb.getString("keystore.load.") + + pe.getMessage()); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(rb.getString("keystore.load.") + + nsae.getMessage()); + } catch (KeyStoreException kse) { + throw new RuntimeException + (rb.getString("unable.to.instantiate.keystore.class.") + + kse.getMessage()); + } + } + + X509Certificate getTsaCert(String alias) { + + Certificate cs = null; + + try { + cs = store.getCertificate(alias); + } catch (KeyStoreException kse) { + // this never happens, because keystore has been loaded + } + if (cs == null || (!(cs instanceof X509Certificate))) { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the")); + Object[] source = {alias, alias}; + error(form.format(source)); + } + return (X509Certificate) cs; + } + + /** + * Check if userCert is designed to be a code signer + * @param userCert the certificate to be examined + * @param bad 3 booleans to show if the KeyUsage, ExtendedKeyUsage, + * NetscapeCertType has codeSigning flag turned on. + * If null, the class field badKeyUsage, badExtendedKeyUsage, + * badNetscapeCertType will be set. + */ + void checkCertUsage(X509Certificate userCert, boolean[] bad) { + + // Can act as a signer? + // 1. if KeyUsage, then [0:digitalSignature] or + // [1:nonRepudiation] should be true + // 2. if ExtendedKeyUsage, then should contains ANY or CODE_SIGNING + // 3. if NetscapeCertType, then should contains OBJECT_SIGNING + // 1,2,3 must be true + + if (bad != null) { + bad[0] = bad[1] = bad[2] = false; + } + + boolean[] keyUsage = userCert.getKeyUsage(); + if (keyUsage != null) { + keyUsage = Arrays.copyOf(keyUsage, 9); + if (!keyUsage[0] && !keyUsage[1]) { + if (bad != null) { + bad[0] = true; + badKeyUsage = true; + } + } + } + + try { + List xKeyUsage = userCert.getExtendedKeyUsage(); + if (xKeyUsage != null) { + if (!xKeyUsage.contains("2.5.29.37.0") // anyExtendedKeyUsage + && !xKeyUsage.contains("1.3.6.1.5.5.7.3.3")) { // codeSigning + if (bad != null) { + bad[1] = true; + badExtendedKeyUsage = true; + } + } + } + } catch (java.security.cert.CertificateParsingException e) { + // shouldn't happen + } + + try { + // OID_NETSCAPE_CERT_TYPE + byte[] netscapeEx = userCert.getExtensionValue + ("2.16.840.1.113730.1.1"); + if (netscapeEx != null) { + DerInputStream in = new DerInputStream(netscapeEx); + byte[] encoded = in.getOctetString(); + encoded = new DerValue(encoded).getUnalignedBitString() + .toByteArray(); + + NetscapeCertTypeExtension extn = + new NetscapeCertTypeExtension(encoded); + + Boolean val = extn.get(NetscapeCertTypeExtension.OBJECT_SIGNING); + if (!val) { + if (bad != null) { + bad[2] = true; + badNetscapeCertType = true; + } + } + } + } catch (IOException e) { + // + } + } + + void getAliasInfo(String alias) { + + Key key = null; + + try { + Certificate[] cs = null; + if (altCertChain != null) { + try (FileInputStream fis = new FileInputStream(altCertChain)) { + cs = CertificateFactory.getInstance("X.509"). + generateCertificates(fis). + toArray(new Certificate[0]); + } catch (FileNotFoundException ex) { + error(rb.getString("File.specified.by.certchain.does.not.exist")); + } catch (CertificateException | IOException ex) { + error(rb.getString("Cannot.restore.certchain.from.file.specified")); + } + } else { + try { + cs = store.getCertificateChain(alias); + } catch (KeyStoreException kse) { + // this never happens, because keystore has been loaded + } + } + if (cs == null || cs.length == 0) { + if (altCertChain != null) { + error(rb.getString + ("Certificate.chain.not.found.in.the.file.specified.")); + } else { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and")); + Object[] source = {alias, alias}; + error(form.format(source)); + } + } + + certChain = new X509Certificate[cs.length]; + for (int i=0; i certs) throws Exception { + int cpLen = 0; + out: for (; cpLen 0) { + CertPath cp = certificateFactory.generateCertPath( + (cpLen == certs.size())? certs: certs.subList(0, cpLen)); + validator.validate(cp, pkixParameters); + } + } + + char[] getPass(String prompt) + { + System.err.print(prompt); + System.err.flush(); + try { + char[] pass = Password.readPassword(System.in); + + if (pass == null) { + error(rb.getString("you.must.enter.key.password")); + } else { + return pass; + } + } catch (IOException ioe) { + error(rb.getString("unable.to.read.password.")+ioe.getMessage()); + } + // this shouldn't happen + return null; + } + + /* + * Reads all the bytes for a given zip entry. + */ + private synchronized byte[] getBytes(ZipFile zf, + ZipEntry ze) throws IOException { + int n; + + InputStream is = null; + try { + is = zf.getInputStream(ze); + baos.reset(); + long left = ze.getSize(); + + while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) { + baos.write(buffer, 0, n); + left -= n; + } + } finally { + if (is != null) { + is.close(); + } + } + + return baos.toByteArray(); + } + + /* + * Returns manifest entry from given jar file, or null if given jar file + * does not have a manifest entry. + */ + private ZipEntry getManifestFile(ZipFile zf) { + ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME); + if (ze == null) { + // Check all entries for matching name + Enumeration enum_ = zf.entries(); + while (enum_.hasMoreElements() && ze == null) { + ze = enum_.nextElement(); + if (!JarFile.MANIFEST_NAME.equalsIgnoreCase + (ze.getName())) { + ze = null; + } + } + } + return ze; + } + + /* + * Computes the digests of a zip entry, and returns them as an array + * of base64-encoded strings. + */ + private synchronized String[] getDigests(ZipEntry ze, ZipFile zf, + MessageDigest[] digests) + throws IOException { + + int n, i; + InputStream is = null; + try { + is = zf.getInputStream(ze); + long left = ze.getSize(); + while((left > 0) + && (n = is.read(buffer, 0, buffer.length)) != -1) { + for (i=0; i signerClass = appClassLoader.loadClass(signerClassName); + + // Check that it implements ContentSigner + Object signer = signerClass.newInstance(); + if (!(signer instanceof ContentSigner)) { + MessageFormat form = new MessageFormat( + rb.getString("signerClass.is.not.a.signing.mechanism")); + Object[] source = {signerClass.getName()}; + throw new IllegalArgumentException(form.format(source)); + } + return (ContentSigner)signer; + } +} + +class SignatureFile { + + /** SignatureFile */ + Manifest sf; + + /** .SF base name */ + String baseName; + + public SignatureFile(MessageDigest digests[], + Manifest mf, + ManifestDigester md, + String baseName, + boolean signManifest) + + { + this.baseName = baseName; + + String version = System.getProperty("java.version"); + String javaVendor = System.getProperty("java.vendor"); + + sf = new Manifest(); + Attributes mattr = sf.getMainAttributes(); + + mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0"); + mattr.putValue("Created-By", version + " (" + javaVendor + ")"); + + if (signManifest) { + // sign the whole manifest + for (int i=0; i < digests.length; i++) { + mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest", + Base64.getEncoder().encodeToString(md.manifestDigest(digests[i]))); + } + } + + // create digest of the manifest main attributes + ManifestDigester.Entry mde = + md.get(ManifestDigester.MF_MAIN_ATTRS, false); + if (mde != null) { + for (int i=0; i < digests.length; i++) { + mattr.putValue(digests[i].getAlgorithm() + + "-Digest-" + ManifestDigester.MF_MAIN_ATTRS, + Base64.getEncoder().encodeToString(mde.digest(digests[i]))); + } + } else { + throw new IllegalStateException + ("ManifestDigester failed to create " + + "Manifest-Main-Attribute entry"); + } + + /* go through the manifest entries and create the digests */ + + Map entries = sf.getEntries(); + Iterator> mit = + mf.getEntries().entrySet().iterator(); + while(mit.hasNext()) { + Entry e = mit.next(); + String name = e.getKey(); + mde = md.get(name, false); + if (mde != null) { + Attributes attr = new Attributes(); + for (int i=0; i < digests.length; i++) { + attr.putValue(digests[i].getAlgorithm()+"-Digest", + Base64.getEncoder().encodeToString(mde.digest(digests[i]))); + } + entries.put(name, attr); + } + } + } + + /** + * Writes the SignatureFile to the specified OutputStream. + * + * @param out the output stream + * @exception IOException if an I/O error has occurred + */ + + public void write(OutputStream out) throws IOException + { + sf.write(out); + } + + /** + * get .SF file name + */ + public String getMetaName() + { + return "META-INF/"+ baseName + ".SF"; + } + + /** + * get base file name + */ + public String getBaseName() + { + return baseName; + } + + /* + * Generate a signed data block. + * If a URL or a certificate (containing a URL) for a Timestamping + * Authority is supplied then a signature timestamp is generated and + * inserted into the signed data block. + * + * @param sigalg signature algorithm to use, or null to use default + * @param tsaUrl The location of the Timestamping Authority. If null + * then no timestamp is requested. + * @param tsaCert The certificate for the Timestamping Authority. If null + * then no timestamp is requested. + * @param signingMechanism The signing mechanism to use. + * @param args The command-line arguments to jarsigner. + * @param zipFile The original source Zip file. + */ + public Block generateBlock(PrivateKey privateKey, + String sigalg, + X509Certificate[] certChain, + boolean externalSF, String tsaUrl, + X509Certificate tsaCert, + String tSAPolicyID, + ContentSigner signingMechanism, + String[] args, ZipFile zipFile) + throws NoSuchAlgorithmException, InvalidKeyException, IOException, + SignatureException, CertificateException + { + return new Block(this, privateKey, sigalg, certChain, externalSF, + tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args, zipFile); + } + + + public static class Block { + + private byte[] block; + private String blockFileName; + + /* + * Construct a new signature block. + */ + Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, + X509Certificate[] certChain, boolean externalSF, String tsaUrl, + X509Certificate tsaCert, String tSAPolicyID, ContentSigner signingMechanism, + String[] args, ZipFile zipFile) + throws NoSuchAlgorithmException, InvalidKeyException, IOException, + SignatureException, CertificateException { + + Principal issuerName = certChain[0].getIssuerDN(); + if (!(issuerName instanceof X500Name)) { + // must extract the original encoded form of DN for subsequent + // name comparison checks (converting to a String and back to + // an encoded DN could cause the types of String attribute + // values to be changed) + X509CertInfo tbsCert = new + X509CertInfo(certChain[0].getTBSCertificate()); + issuerName = (Principal) + tbsCert.get(X509CertInfo.ISSUER + "." + + X509CertInfo.DN_NAME); + } + BigInteger serial = certChain[0].getSerialNumber(); + + String signatureAlgorithm; + String keyAlgorithm = privateKey.getAlgorithm(); + /* + * If no signature algorithm was specified, we choose a + * default that is compatible with the private key algorithm. + */ + if (sigalg == null) { + + if (keyAlgorithm.equalsIgnoreCase("DSA")) + signatureAlgorithm = "SHA1withDSA"; + else if (keyAlgorithm.equalsIgnoreCase("RSA")) + signatureAlgorithm = "SHA256withRSA"; + else if (keyAlgorithm.equalsIgnoreCase("EC")) + signatureAlgorithm = "SHA256withECDSA"; + else + throw new RuntimeException("private key is not a DSA or " + + "RSA key"); + } else { + signatureAlgorithm = sigalg; + } + + // check common invalid key/signature algorithm combinations + String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH); + if ((sigAlgUpperCase.endsWith("WITHRSA") && + !keyAlgorithm.equalsIgnoreCase("RSA")) || + (sigAlgUpperCase.endsWith("WITHECDSA") && + !keyAlgorithm.equalsIgnoreCase("EC")) || + (sigAlgUpperCase.endsWith("WITHDSA") && + !keyAlgorithm.equalsIgnoreCase("DSA"))) { + throw new SignatureException + ("private key algorithm is not compatible with signature algorithm"); + } + + blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm; + + AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm); + AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm); + + Signature sig = Signature.getInstance(signatureAlgorithm); + sig.initSign(privateKey); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + sfg.write(baos); + + byte[] content = baos.toByteArray(); + + sig.update(content); + byte[] signature = sig.sign(); + + // Timestamp the signature and generate the signature block file + if (signingMechanism == null) { + signingMechanism = new TimestampedSigner(); + } + URI tsaUri = null; + try { + if (tsaUrl != null) { + tsaUri = new URI(tsaUrl); + } + } catch (URISyntaxException e) { + throw new IOException(e); + } + + // Assemble parameters for the signing mechanism + ContentSignerParameters params = + new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID, signature, + signatureAlgorithm, certChain, content, zipFile); + + // Generate the signature block + block = signingMechanism.generateSignedData( + params, externalSF, (tsaUrl != null || tsaCert != null)); + } + + /* + * get block file name. + */ + public String getMetaName() + { + return blockFileName; + } + + /** + * Writes the block file to the specified OutputStream. + * + * @param out the output stream + * @exception IOException if an I/O error has occurred + */ + + public void write(OutputStream out) throws IOException + { + out.write(block); + } + } +} + + +/* + * This object encapsulates the parameters used to perform content signing. + */ +class JarSignerParameters implements ContentSignerParameters { + + private String[] args; + private URI tsa; + private X509Certificate tsaCertificate; + private byte[] signature; + private String signatureAlgorithm; + private X509Certificate[] signerCertificateChain; + private byte[] content; + private ZipFile source; + private String tSAPolicyID; + + /** + * Create a new object. + */ + JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, + String tSAPolicyID, + byte[] signature, String signatureAlgorithm, + X509Certificate[] signerCertificateChain, byte[] content, + ZipFile source) { + + if (signature == null || signatureAlgorithm == null || + signerCertificateChain == null) { + throw new NullPointerException(); + } + this.args = args; + this.tsa = tsa; + this.tsaCertificate = tsaCertificate; + this.tSAPolicyID = tSAPolicyID; + this.signature = signature; + this.signatureAlgorithm = signatureAlgorithm; + this.signerCertificateChain = signerCertificateChain; + this.content = content; + this.source = source; + } + + /** + * Retrieves the command-line arguments. + * + * @return The command-line arguments. May be null. + */ + public String[] getCommandLine() { + return args; + } + + /** + * Retrieves the identifier for a Timestamping Authority (TSA). + * + * @return The TSA identifier. May be null. + */ + public URI getTimestampingAuthority() { + return tsa; + } + + /** + * Retrieves the certificate for a Timestamping Authority (TSA). + * + * @return The TSA certificate. May be null. + */ + public X509Certificate getTimestampingAuthorityCertificate() { + return tsaCertificate; + } + + public String getTSAPolicyID() { + return tSAPolicyID; + } + + /** + * Retrieves the signature. + * + * @return The non-null signature bytes. + */ + public byte[] getSignature() { + return signature; + } + + /** + * Retrieves the name of the signature algorithm. + * + * @return The non-null string name of the signature algorithm. + */ + public String getSignatureAlgorithm() { + return signatureAlgorithm; + } + + /** + * Retrieves the signer's X.509 certificate chain. + * + * @return The non-null array of X.509 public-key certificates. + */ + public X509Certificate[] getSignerCertificateChain() { + return signerCertificateChain; + } + + /** + * Retrieves the content that was signed. + * + * @return The content bytes. May be null. + */ + public byte[] getContent() { + return content; + } + + /** + * Retrieves the original source ZIP file before it was signed. + * + * @return The original ZIP file. May be null. + */ + public ZipFile getSource() { + return source; + } +} diff --git a/src/sun/security/tools/jarsigner/Resources.java b/src/sun/security/tools/jarsigner/Resources.java new file mode 100644 index 00000000..77b06282 --- /dev/null +++ b/src/sun/security/tools/jarsigner/Resources.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.jarsigner; + +/** + *

    This class represents the ResourceBundle + * for JarSigner. + * + */ +public class Resources extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // shared (from jarsigner) + {"SPACE", " "}, + {"2SPACE", " "}, + {"6SPACE", " "}, + {"COMMA", ", "}, + + {"provName.not.a.provider", "{0} not a provider"}, + {"signerClass.is.not.a.signing.mechanism", "{0} is not a signing mechanism"}, + {"jarsigner.error.", "jarsigner error: "}, + {"Illegal.option.", "Illegal option: "}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore must be NONE if -storetype is {0}"}, + {".keypass.can.not.be.specified.if.storetype.is.{0}", + "-keypass can not be specified if -storetype is {0}"}, + {"If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified", + "If -protected is specified, then -storepass and -keypass must not be specified"}, + {"If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified", + "If keystore is not password protected, then -storepass and -keypass must not be specified"}, + {"Usage.jarsigner.options.jar.file.alias", + "Usage: jarsigner [options] jar-file alias"}, + {".jarsigner.verify.options.jar.file.alias.", + " jarsigner -verify [options] jar-file [alias...]"}, + {".keystore.url.keystore.location", + "[-keystore ] keystore location"}, + {".storepass.password.password.for.keystore.integrity", + "[-storepass ] password for keystore integrity"}, + {".storetype.type.keystore.type", + "[-storetype ] keystore type"}, + {".keypass.password.password.for.private.key.if.different.", + "[-keypass ] password for private key (if different)"}, + {".certchain.file.name.of.alternative.certchain.file", + "[-certchain ] name of alternative certchain file"}, + {".sigfile.file.name.of.SF.DSA.file", + "[-sigfile ] name of .SF/.DSA file"}, + {".signedjar.file.name.of.signed.JAR.file", + "[-signedjar ] name of signed JAR file"}, + {".digestalg.algorithm.name.of.digest.algorithm", + "[-digestalg ] name of digest algorithm"}, + {".sigalg.algorithm.name.of.signature.algorithm", + "[-sigalg ] name of signature algorithm"}, + {".verify.verify.a.signed.JAR.file", + "[-verify] verify a signed JAR file"}, + {".verbose.suboptions.verbose.output.when.signing.verifying.", + "[-verbose[:suboptions]] verbose output when signing/verifying."}, + {".suboptions.can.be.all.grouped.or.summary", + " suboptions can be all, grouped or summary"}, + {".certs.display.certificates.when.verbose.and.verifying", + "[-certs] display certificates when verbose and verifying"}, + {".tsa.url.location.of.the.Timestamping.Authority", + "[-tsa ] location of the Timestamping Authority"}, + {".tsacert.alias.public.key.certificate.for.Timestamping.Authority", + "[-tsacert ] public key certificate for Timestamping Authority"}, + {".tsapolicyid.tsapolicyid.for.Timestamping.Authority", + "[-tsapolicyid ] TSAPolicyID for Timestamping Authority"}, + {".altsigner.class.class.name.of.an.alternative.signing.mechanism", + "[-altsigner ] class name of an alternative signing mechanism"}, + {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism", + "[-altsignerpath ] location of an alternative signing mechanism"}, + {".internalsf.include.the.SF.file.inside.the.signature.block", + "[-internalsf] include the .SF file inside the signature block"}, + {".sectionsonly.don.t.compute.hash.of.entire.manifest", + "[-sectionsonly] don't compute hash of entire manifest"}, + {".protected.keystore.has.protected.authentication.path", + "[-protected] keystore has protected authentication path"}, + {".providerName.name.provider.name", + "[-providerName ] provider name"}, + {".providerClass.class.name.of.cryptographic.service.provider.s", + "[-providerClass name of cryptographic service provider's"}, + {".providerArg.arg.master.class.file.and.constructor.argument", + " [-providerArg ]] ... master class file and constructor argument"}, + {".strict.treat.warnings.as.errors", + "[-strict] treat warnings as errors"}, + {"Option.lacks.argument", "Option lacks argument"}, + {"Please.type.jarsigner.help.for.usage", "Please type jarsigner -help for usage"}, + {"Please.specify.jarfile.name", "Please specify jarfile name"}, + {"Please.specify.alias.name", "Please specify alias name"}, + {"Only.one.alias.can.be.specified", "Only one alias can be specified"}, + {"This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.", + "This jar contains signed entries which are not signed by the specified alias(es)."}, + {"This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.", + "This jar contains signed entries that are not signed by alias in this keystore."}, + {"s", "s"}, + {"m", "m"}, + {"k", "k"}, + {"i", "i"}, + {".and.d.more.", "(and %d more)"}, + {".s.signature.was.verified.", + " s = signature was verified "}, + {".m.entry.is.listed.in.manifest", + " m = entry is listed in manifest"}, + {".k.at.least.one.certificate.was.found.in.keystore", + " k = at least one certificate was found in keystore"}, + {".i.at.least.one.certificate.was.found.in.identity.scope", + " i = at least one certificate was found in identity scope"}, + {".X.not.signed.by.specified.alias.es.", + " X = not signed by specified alias(es)"}, + {"no.manifest.", "no manifest."}, + {".Signature.related.entries.","(Signature related entries)"}, + {".Unsigned.entries.", "(Unsigned entries)"}, + {"jar.is.unsigned.signatures.missing.or.not.parsable.", + "jar is unsigned. (signatures missing or not parsable)"}, + {"jar.signed.", "jar signed."}, + {"jar.signed.with.signer.errors.", "jar signed, with signer errors."}, + {"jar.verified.", "jar verified."}, + {"jar.verified.with.signer.errors.", "jar verified, with signer errors."}, + {"jarsigner.", "jarsigner: "}, + {"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.", + "signature filename must consist of the following characters: A-Z, 0-9, _ or -"}, + {"unable.to.open.jar.file.", "unable to open jar file: "}, + {"unable.to.create.", "unable to create: "}, + {".adding.", " adding: "}, + {".updating.", " updating: "}, + {".signing.", " signing: "}, + {"attempt.to.rename.signedJarFile.to.jarFile.failed", + "attempt to rename {0} to {1} failed"}, + {"attempt.to.rename.jarFile.to.origJar.failed", + "attempt to rename {0} to {1} failed"}, + {"unable.to.sign.jar.", "unable to sign jar: "}, + {"Enter.Passphrase.for.keystore.", "Enter Passphrase for keystore: "}, + {"keystore.load.", "keystore load: "}, + {"certificate.exception.", "certificate exception: "}, + {"unable.to.instantiate.keystore.class.", + "unable to instantiate keystore class: "}, + {"Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and", + "Certificate chain not found for: {0}. {1} must reference a valid KeyStore key entry containing a private key and corresponding public key certificate chain."}, + {"File.specified.by.certchain.does.not.exist", + "File specified by -certchain does not exist"}, + {"Cannot.restore.certchain.from.file.specified", + "Cannot restore certchain from file specified"}, + {"Certificate.chain.not.found.in.the.file.specified.", + "Certificate chain not found in the file specified."}, + {"found.non.X.509.certificate.in.signer.s.chain", + "found non-X.509 certificate in signer's chain"}, + {"incomplete.certificate.chain", "incomplete certificate chain"}, + {"Enter.key.password.for.alias.", "Enter key password for {0}: "}, + {"unable.to.recover.key.from.keystore", + "unable to recover key from keystore"}, + {"key.associated.with.alias.not.a.private.key", + "key associated with {0} not a private key"}, + {"you.must.enter.key.password", "you must enter key password"}, + {"unable.to.read.password.", "unable to read password: "}, + {"certificate.is.valid.from", "certificate is valid from {0} to {1}"}, + {"certificate.expired.on", "certificate expired on {0}"}, + {"certificate.is.not.valid.until", + "certificate is not valid until {0}"}, + {"certificate.will.expire.on", "certificate will expire on {0}"}, + {".CertPath.not.validated.", "[CertPath not validated: "}, + {"requesting.a.signature.timestamp", + "requesting a signature timestamp"}, + {"TSA.location.", "TSA location: "}, + {"TSA.certificate.", "TSA certificate: "}, + {"no.response.from.the.Timestamping.Authority.", + "no response from the Timestamping Authority. When connecting" + + " from behind a firewall an HTTP or HTTPS proxy may need to" + + " be specified. Supply the following options to jarsigner:"}, + {"or", "or"}, + {"Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the", + "Certificate not found for: {0}. {1} must reference a valid KeyStore entry containing an X.509 public key certificate for the Timestamping Authority."}, + {"using.an.alternative.signing.mechanism", + "using an alternative signing mechanism"}, + {"entry.was.signed.on", "entry was signed on {0}"}, + {"Warning.", "Warning: "}, + {"Error.", "Error: "}, + {"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.", + "This jar contains unsigned entries which have not been integrity-checked. "}, + {"This.jar.contains.entries.whose.signer.certificate.has.expired.", + "This jar contains entries whose signer certificate has expired. "}, + {"This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.", + "This jar contains entries whose signer certificate will expire within six months. "}, + {"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.", + "This jar contains entries whose signer certificate is not yet valid. "}, + {"Re.run.with.the.verbose.option.for.more.details.", + "Re-run with the -verbose option for more details."}, + {"Re.run.with.the.verbose.and.certs.options.for.more.details.", + "Re-run with the -verbose and -certs options for more details."}, + {"The.signer.certificate.has.expired.", + "The signer certificate has expired."}, + {"The.signer.certificate.will.expire.within.six.months.", + "The signer certificate will expire within six months."}, + {"The.signer.certificate.is.not.yet.valid.", + "The signer certificate is not yet valid."}, + {"The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "The signer certificate's KeyUsage extension doesn't allow code signing."}, + {"The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "The signer certificate's ExtendedKeyUsage extension doesn't allow code signing."}, + {"The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "The signer certificate's NetscapeCertType extension doesn't allow code signing."}, + {"This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "This jar contains entries whose signer certificate's KeyUsage extension doesn't allow code signing."}, + {"This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "This jar contains entries whose signer certificate's ExtendedKeyUsage extension doesn't allow code signing."}, + {"This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "This jar contains entries whose signer certificate's NetscapeCertType extension doesn't allow code signing."}, + {".{0}.extension.does.not.support.code.signing.", + "[{0} extension does not support code signing]"}, + {"The.signer.s.certificate.chain.is.not.validated.", + "The signer's certificate chain is not validated."}, + {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.", + "This jar contains entries whose certificate chain is not validated."}, + {"no.timestamp.signing", + "No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, + {"no.timestamp.verifying", + "This jar contains signatures that does not include a timestamp. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (%1$tY-%1$tm-%1$td) or after any future revocation date."}, + {"Unknown.password.type.", "Unknown password type: "}, + {"Cannot.find.environment.variable.", + "Cannot find environment variable: "}, + {"Cannot.find.file.", "Cannot find file: "}, + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/jarsigner/Resources_ja.java b/src/sun/security/tools/jarsigner/Resources_ja.java new file mode 100644 index 00000000..7588a641 --- /dev/null +++ b/src/sun/security/tools/jarsigner/Resources_ja.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.jarsigner; + +/** + *

    This class represents the ResourceBundle + * for JarSigner. + * + */ +public class Resources_ja extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // shared (from jarsigner) + {"SPACE", " "}, + {"2SPACE", " "}, + {"6SPACE", " "}, + {"COMMA", ", "}, + + {"provName.not.a.provider", "{0}\u306F\u30D7\u30ED\u30D0\u30A4\u30C0\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"signerClass.is.not.a.signing.mechanism", "{0}\u306F\u7F72\u540D\u30E1\u30AB\u30CB\u30BA\u30E0\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"jarsigner.error.", "jarsigner\u30A8\u30E9\u30FC: "}, + {"Illegal.option.", "\u4E0D\u6B63\u306A\u30AA\u30D7\u30B7\u30E7\u30F3: "}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-storetype\u304C{0}\u306E\u5834\u5408\u3001-keystore\u306FNONE\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {".keypass.can.not.be.specified.if.storetype.is.{0}", + "-storetype\u304C{0}\u306E\u5834\u5408\u3001-keypass\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified", + "-protected\u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u306F\u3001-storepass\u304A\u3088\u3073-keypass\u3092\u6307\u5B9A\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044"}, + {"If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u304C\u30D1\u30B9\u30EF\u30FC\u30C9\u3067\u4FDD\u8B77\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u3001-storepass\u304A\u3088\u3073-keypass\u3092\u6307\u5B9A\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044"}, + {"Usage.jarsigner.options.jar.file.alias", + "\u4F7F\u7528\u65B9\u6CD5: jarsigner [options] jar-file alias"}, + {".jarsigner.verify.options.jar.file.alias.", + " jarsigner -verify [options] jar-file [alias...]"}, + {".keystore.url.keystore.location", + "[-keystore ] \u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u4F4D\u7F6E"}, + {".storepass.password.password.for.keystore.integrity", + "[-storepass ] \u30AD\u30FC\u30B9\u30C8\u30A2\u6574\u5408\u6027\u306E\u305F\u3081\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, + {".storetype.type.keystore.type", + "[-storetype ] \u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u578B"}, + {".keypass.password.password.for.private.key.if.different.", + "[-keypass ] \u79D8\u5BC6\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9(\u7570\u306A\u308B\u5834\u5408)"}, + {".certchain.file.name.of.alternative.certchain.file", + "[-certchain ] \u4EE3\u66FF\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u540D\u524D"}, + {".sigfile.file.name.of.SF.DSA.file", + "[-sigfile ] .SF/.DSA\u30D5\u30A1\u30A4\u30EB\u306E\u540D\u524D"}, + {".signedjar.file.name.of.signed.JAR.file", + "[-signedjar ] \u7F72\u540D\u4ED8\u304DJAR\u30D5\u30A1\u30A4\u30EB\u306E\u540D\u524D"}, + {".digestalg.algorithm.name.of.digest.algorithm", + "[-digestalg ] \u30C0\u30A4\u30B8\u30A7\u30B9\u30C8\u30FB\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u306E\u540D\u524D"}, + {".sigalg.algorithm.name.of.signature.algorithm", + "[-sigalg ] \u30B7\u30B0\u30CD\u30C1\u30E3\u30FB\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u306E\u540D\u524D"}, + {".verify.verify.a.signed.JAR.file", + "[-verify] \u7F72\u540D\u4ED8\u304DJAR\u30D5\u30A1\u30A4\u30EB\u306E\u691C\u8A3C"}, + {".verbose.suboptions.verbose.output.when.signing.verifying.", + "[-verbose[:suboptions]] \u7F72\u540D/\u691C\u8A3C\u6642\u306E\u8A73\u7D30\u51FA\u529B\u3002"}, + {".suboptions.can.be.all.grouped.or.summary", + " \u30B5\u30D6\u30AA\u30D7\u30B7\u30E7\u30F3\u3068\u3057\u3066\u3001all\u3001grouped\u307E\u305F\u306Fsummary\u3092\u4F7F\u7528\u3067\u304D\u307E\u3059"}, + {".certs.display.certificates.when.verbose.and.verifying", + "[-certs] \u8A73\u7D30\u51FA\u529B\u304A\u3088\u3073\u691C\u8A3C\u6642\u306B\u8A3C\u660E\u66F8\u3092\u8868\u793A"}, + {".tsa.url.location.of.the.Timestamping.Authority", + "[-tsa ] \u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u5C40\u306E\u5834\u6240"}, + {".tsacert.alias.public.key.certificate.for.Timestamping.Authority", + "[-tsacert ] \u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u5C40\u306E\u516C\u958B\u9375\u8A3C\u660E\u66F8"}, + {".tsapolicyid.tsapolicyid.for.Timestamping.Authority", + "[-tsapolicyid ] \u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u5C40\u306ETSAPolicyID"}, + {".altsigner.class.class.name.of.an.alternative.signing.mechanism", + "[-altsigner ] \u4EE3\u66FF\u7F72\u540D\u30E1\u30AB\u30CB\u30BA\u30E0\u306E\u30AF\u30E9\u30B9\u540D"}, + {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism", + "[-altsignerpath ] \u4EE3\u66FF\u7F72\u540D\u30E1\u30AB\u30CB\u30BA\u30E0\u306E\u5834\u6240"}, + {".internalsf.include.the.SF.file.inside.the.signature.block", + "[-internalsf] \u30B7\u30B0\u30CD\u30C1\u30E3\u30FB\u30D6\u30ED\u30C3\u30AF\u306B.SF\u30D5\u30A1\u30A4\u30EB\u3092\u542B\u3081\u308B"}, + {".sectionsonly.don.t.compute.hash.of.entire.manifest", + "[-sectionsonly] \u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u5168\u4F53\u306E\u30CF\u30C3\u30B7\u30E5\u306F\u8A08\u7B97\u3057\u306A\u3044"}, + {".protected.keystore.has.protected.authentication.path", + "[-protected] \u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u306F\u4FDD\u8B77\u3055\u308C\u305F\u8A8D\u8A3C\u30D1\u30B9\u304C\u3042\u308B"}, + {".providerName.name.provider.name", + "[-providerName ] \u30D7\u30ED\u30D0\u30A4\u30C0\u540D"}, + {".providerClass.class.name.of.cryptographic.service.provider.s", + "[-providerClass \u6697\u53F7\u5316\u30B5\u30FC\u30D3\u30B9\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0\u306E\u540D\u524D"}, + {".providerArg.arg.master.class.file.and.constructor.argument", + " [-providerArg ]] ... \u30DE\u30B9\u30BF\u30FC\u30FB\u30AF\u30E9\u30B9\u30FB\u30D5\u30A1\u30A4\u30EB\u3068\u30B3\u30F3\u30B9\u30C8\u30E9\u30AF\u30BF\u306E\u5F15\u6570"}, + {".strict.treat.warnings.as.errors", + "[-strict] \u8B66\u544A\u3092\u30A8\u30E9\u30FC\u3068\u3057\u3066\u51E6\u7406"}, + {"Option.lacks.argument", "\u30AA\u30D7\u30B7\u30E7\u30F3\u306B\u5F15\u6570\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Please.type.jarsigner.help.for.usage", "\u4F7F\u7528\u65B9\u6CD5\u306B\u3064\u3044\u3066\u306Fjarsigner -help\u3068\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Please.specify.jarfile.name", "jarfile\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Please.specify.alias.name", "\u5225\u540D\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Only.one.alias.can.be.specified", "\u5225\u540D\u306F1\u3064\u306E\u307F\u6307\u5B9A\u3067\u304D\u307E\u3059"}, + {"This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.", + "\u3053\u306Ejar\u306B\u542B\u307E\u308C\u308B\u7F72\u540D\u6E08\u30A8\u30F3\u30C8\u30EA\u306F\u3001\u6307\u5B9A\u3055\u308C\u305F\u5225\u540D\u306B\u3088\u3063\u3066\u7F72\u540D\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.", + "\u3053\u306Ejar\u306B\u542B\u307E\u308C\u308B\u7F72\u540D\u6E08\u30A8\u30F3\u30C8\u30EA\u306F\u3001\u3053\u306E\u30AD\u30FC\u30B9\u30C8\u30A2\u5185\u306E\u5225\u540D\u306B\u3088\u3063\u3066\u7F72\u540D\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"s", "s"}, + {"m", "m"}, + {"k", "k"}, + {"i", "i"}, + {".and.d.more.", "(\u4ED6\u306B\u3082%d\u500B)"}, + {".s.signature.was.verified.", + " s=\u30B7\u30B0\u30CD\u30C1\u30E3\u304C\u691C\u8A3C\u3055\u308C\u307E\u3057\u305F "}, + {".m.entry.is.listed.in.manifest", + " m=\u30A8\u30F3\u30C8\u30EA\u304C\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u5185\u306B\u30EA\u30B9\u30C8\u3055\u308C\u307E\u3059"}, + {".k.at.least.one.certificate.was.found.in.keystore", + " k=1\u3064\u4EE5\u4E0A\u306E\u8A3C\u660E\u66F8\u304C\u30AD\u30FC\u30B9\u30C8\u30A2\u3067\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {".i.at.least.one.certificate.was.found.in.identity.scope", + " i=1\u3064\u4EE5\u4E0A\u306E\u8A3C\u660E\u66F8\u304C\u30A2\u30A4\u30C7\u30F3\u30C6\u30A3\u30C6\u30A3\u30FB\u30B9\u30B3\u30FC\u30D7\u3067\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {".X.not.signed.by.specified.alias.es.", + " X =\u6307\u5B9A\u3057\u305F\u5225\u540D\u3067\u7F72\u540D\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"no.manifest.", "\u30DE\u30CB\u30D5\u30A7\u30B9\u30C8\u306F\u5B58\u5728\u3057\u307E\u305B\u3093\u3002"}, + {".Signature.related.entries.","(\u30B7\u30B0\u30CD\u30C1\u30E3\u95A2\u9023\u30A8\u30F3\u30C8\u30EA)"}, + {".Unsigned.entries.", "(\u672A\u7F72\u540D\u306E\u30A8\u30F3\u30C8\u30EA)"}, + {"jar.is.unsigned.signatures.missing.or.not.parsable.", + "jar\u306F\u7F72\u540D\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002(\u30B7\u30B0\u30CD\u30C1\u30E3\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u3001\u69CB\u6587\u89E3\u6790\u3067\u304D\u307E\u305B\u3093)"}, + {"jar.signed.", "jar\u306F\u7F72\u540D\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"jar.signed.with.signer.errors.", "jar\u306F\u7F72\u540D\u3055\u308C\u307E\u3057\u305F - \u7F72\u540D\u8005\u30A8\u30E9\u30FC\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"jar.verified.", "jar\u304C\u691C\u8A3C\u3055\u308C\u307E\u3057\u305F\u3002"}, + {"jar.verified.with.signer.errors.", "jar\u306F\u691C\u8A3C\u3055\u308C\u307E\u3057\u305F - \u7F72\u540D\u8005\u30A8\u30E9\u30FC\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"jarsigner.", "jarsigner: "}, + {"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.", + "\u30B7\u30B0\u30CD\u30C1\u30E3\u306E\u30D5\u30A1\u30A4\u30EB\u540D\u306B\u4F7F\u7528\u3067\u304D\u308B\u6587\u5B57\u306F\u3001A-Z\u30010-9\u3001_\u3001- \u306E\u307F\u3067\u3059\u3002"}, + {"unable.to.open.jar.file.", "\u6B21\u306Ejar\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F\u3053\u3068\u304C\u3067\u304D\u307E\u305B\u3093: "}, + {"unable.to.create.", "\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093: "}, + {".adding.", " \u8FFD\u52A0\u4E2D: "}, + {".updating.", " \u66F4\u65B0\u4E2D: "}, + {".signing.", " \u7F72\u540D\u4E2D: "}, + {"attempt.to.rename.signedJarFile.to.jarFile.failed", + "{0}\u306E\u540D\u524D\u3092{1}\u306B\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u307E\u3057\u305F\u304C\u5931\u6557\u3057\u307E\u3057\u305F"}, + {"attempt.to.rename.jarFile.to.origJar.failed", + "{0}\u306E\u540D\u524D\u3092{1}\u306B\u5909\u66F4\u3057\u3088\u3046\u3068\u3057\u307E\u3057\u305F\u304C\u5931\u6557\u3057\u307E\u3057\u305F"}, + {"unable.to.sign.jar.", "jar\u306B\u7F72\u540D\u3067\u304D\u307E\u305B\u3093: "}, + {"Enter.Passphrase.for.keystore.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"keystore.load.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30ED\u30FC\u30C9: "}, + {"certificate.exception.", "\u8A3C\u660E\u66F8\u4F8B\u5916: "}, + {"unable.to.instantiate.keystore.class.", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30AF\u30E9\u30B9\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093: "}, + {"Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and", + "\u6B21\u306E\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: {0}\u3002{1}\u306F\u3001\u79D8\u5BC6\u9375\u304A\u3088\u3073\u5BFE\u5FDC\u3059\u308B\u516C\u958B\u9375\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u3092\u542B\u3080\u6709\u52B9\u306AKeyStore\u9375\u30A8\u30F3\u30C8\u30EA\u3092\u53C2\u7167\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"File.specified.by.certchain.does.not.exist", + "-certchain\u3067\u6307\u5B9A\u3055\u308C\u3066\u3044\u308B\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057\u307E\u305B\u3093"}, + {"Cannot.restore.certchain.from.file.specified", + "\u6307\u5B9A\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u304B\u3089\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u3092\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093"}, + {"Certificate.chain.not.found.in.the.file.specified.", + "\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u306F\u6307\u5B9A\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u306B\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002"}, + {"found.non.X.509.certificate.in.signer.s.chain", + "\u7F72\u540D\u8005\u306E\u9023\u9396\u5185\u3067\u975EX.509\u8A3C\u660E\u66F8\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"incomplete.certificate.chain", "\u4E0D\u5B8C\u5168\u306A\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3"}, + {"Enter.key.password.for.alias.", "{0}\u306E\u9375\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"unable.to.recover.key.from.keystore", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u304B\u3089\u9375\u3092\u5FA9\u5143\u3067\u304D\u307E\u305B\u3093"}, + {"key.associated.with.alias.not.a.private.key", + "{0}\u3068\u95A2\u9023\u4ED8\u3051\u3089\u308C\u305F\u9375\u306F\u3001\u79D8\u5BC6\u9375\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"you.must.enter.key.password", "\u9375\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"unable.to.read.password.", "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093: "}, + {"certificate.is.valid.from", "\u8A3C\u660E\u66F8\u306F{0}\u304B\u3089{1}\u307E\u3067\u6709\u52B9\u3067\u3059"}, + {"certificate.expired.on", "\u8A3C\u660E\u66F8\u306F{0}\u306B\u5931\u52B9\u3057\u307E\u3057\u305F"}, + {"certificate.is.not.valid.until", + "\u8A3C\u660E\u66F8\u306F{0}\u307E\u3067\u6709\u52B9\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"certificate.will.expire.on", "\u8A3C\u660E\u66F8\u306F{0}\u306B\u5931\u52B9\u3057\u307E\u3059"}, + {".CertPath.not.validated.", "[CertPath\u304C\u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093: "}, + {"requesting.a.signature.timestamp", + "\u30B7\u30B0\u30CD\u30C1\u30E3\u30FB\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u306E\u30EA\u30AF\u30A8\u30B9\u30C8"}, + {"TSA.location.", "TSA\u306E\u5834\u6240: "}, + {"TSA.certificate.", "TSA\u8A3C\u660E\u66F8: "}, + {"no.response.from.the.Timestamping.Authority.", + "\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u5C40\u304B\u3089\u306E\u30EC\u30B9\u30DD\u30F3\u30B9\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u30D5\u30A1\u30A4\u30A2\u30A6\u30A9\u30FC\u30EB\u3092\u4ECB\u3057\u3066\u63A5\u7D9A\u3059\u308B\u3068\u304D\u306F\u3001\u5FC5\u8981\u306B\u5FDC\u3058\u3066HTTP\u307E\u305F\u306FHTTPS\u30D7\u30ED\u30AD\u30B7\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002jarsigner\u306B\u6B21\u306E\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044:"}, + {"or", "\u307E\u305F\u306F"}, + {"Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the", + "\u8A3C\u660E\u66F8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F: {0}\u3002{1}\u306F\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u5C40\u306EX.509\u516C\u958B\u9375\u8A3C\u660E\u66F8\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u6709\u52B9\u306AKeyStore\u30A8\u30F3\u30C8\u30EA\u3092\u53C2\u7167\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"using.an.alternative.signing.mechanism", + "\u4EE3\u66FF\u7F72\u540D\u30E1\u30AB\u30CB\u30BA\u30E0\u306E\u4F7F\u7528"}, + {"entry.was.signed.on", "\u30A8\u30F3\u30C8\u30EA\u306F{0}\u306B\u7F72\u540D\u3055\u308C\u307E\u3057\u305F"}, + {"Warning.", "\u8B66\u544A: "}, + {"Error.", "\u30A8\u30E9\u30FC: "}, + {"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.", + "\u3053\u306Ejar\u306B\u306F\u3001\u6574\u5408\u6027\u30C1\u30A7\u30C3\u30AF\u3092\u3057\u3066\u3044\u306A\u3044\u672A\u7F72\u540D\u306E\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.has.expired.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u304C\u671F\u9650\u5207\u308C\u306E\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u304C6\u304B\u6708\u4EE5\u5185\u306B\u671F\u9650\u5207\u308C\u3068\u306A\u308B\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u304C\u307E\u3060\u6709\u52B9\u306B\u306A\u3063\u3066\u3044\u306A\u3044\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002 "}, + {"Re.run.with.the.verbose.option.for.more.details.", + "\u8A73\u7D30\u306F\u3001-verbose\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\u3057\u3066\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"Re.run.with.the.verbose.and.certs.options.for.more.details.", + "\u8A73\u7D30\u306F\u3001-verbose\u304A\u3088\u3073-certs\u30AA\u30D7\u30B7\u30E7\u30F3\u3092\u4F7F\u7528\u3057\u3066\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"The.signer.certificate.has.expired.", + "\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u306F\u671F\u9650\u5207\u308C\u3067\u3059\u3002"}, + {"The.signer.certificate.will.expire.within.six.months.", + "\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u306F6\u304B\u6708\u4EE5\u5185\u306B\u671F\u9650\u5207\u308C\u306B\u306A\u308A\u307E\u3059\u3002"}, + {"The.signer.certificate.is.not.yet.valid.", + "\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u306F\u307E\u3060\u6709\u52B9\u306B\u306A\u3063\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306EKeyUsage\u62E1\u5F35\u6A5F\u80FD\u3067\u306F\u3001\u30B3\u30FC\u30C9\u7F72\u540D\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002"}, + {"The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306EExtendedKeyUsage\u62E1\u5F35\u6A5F\u80FD\u3067\u306F\u3001\u30B3\u30FC\u30C9\u7F72\u540D\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002"}, + {"The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306ENetscapeCertType\u62E1\u5F35\u6A5F\u80FD\u3067\u306F\u3001\u30B3\u30FC\u30C9\u7F72\u540D\u306F\u8A31\u53EF\u3055\u308C\u307E\u305B\u3093\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306EKeyUsage\u62E1\u5F35\u6A5F\u80FD\u304C\u30B3\u30FC\u30C9\u7F72\u540D\u3092\u8A31\u53EF\u3057\u306A\u3044\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306EExtendedKeyUsage\u62E1\u5F35\u6A5F\u80FD\u304C\u30B3\u30FC\u30C9\u7F72\u540D\u3092\u8A31\u53EF\u3057\u306A\u3044\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "\u3053\u306Ejar\u306B\u306F\u3001\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306ENetscapeCertType\u62E1\u5F35\u6A5F\u80FD\u304C\u30B3\u30FC\u30C9\u7F72\u540D\u3092\u8A31\u53EF\u3057\u306A\u3044\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002"}, + {".{0}.extension.does.not.support.code.signing.", + "[{0}\u62E1\u5F35\u6A5F\u80FD\u306F\u30B3\u30FC\u30C9\u7F72\u540D\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u3066\u3044\u307E\u305B\u3093]"}, + {"The.signer.s.certificate.chain.is.not.validated.", + "\u7F72\u540D\u8005\u306E\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u304C\u307E\u3060\u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002"}, + {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.", + "\u3053\u306Ejar\u306B\u306F\u3001\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u304C\u307E\u3060\u691C\u8A3C\u3055\u308C\u3066\u3044\u306A\u3044\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002"}, + {"no.timestamp.signing", + "-tsa\u307E\u305F\u306F-tsacert\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u306A\u3044\u305F\u3081\u3001\u3053\u306Ejar\u306B\u306F\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u304C\u4ED8\u52A0\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u304C\u306A\u3044\u3068\u3001\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306E\u6709\u52B9\u671F\u9650(%1$tY-%1$tm-%1$td)\u5F8C\u307E\u305F\u306F\u5C06\u6765\u306E\u5931\u52B9\u65E5\u5F8C\u306B\u3001\u30E6\u30FC\u30B6\u30FC\u306F\u3053\u306Ejar\u3092\u691C\u8A3C\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"no.timestamp.verifying", + "\u3053\u306Ejar\u306B\u306F\u3001\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u304C\u306A\u3044\u7F72\u540D\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7\u304C\u306A\u3044\u3068\u3001\u7F72\u540D\u8005\u8A3C\u660E\u66F8\u306E\u6709\u52B9\u671F\u9650(%1$tY-%1$tm-%1$td)\u5F8C\u307E\u305F\u306F\u5C06\u6765\u306E\u5931\u52B9\u65E5\u5F8C\u306B\u3001\u30E6\u30FC\u30B6\u30FC\u306F\u3053\u306Ejar\u3092\u691C\u8A3C\u3067\u304D\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002"}, + {"Unknown.password.type.", "\u4E0D\u660E\u306A\u30D1\u30B9\u30EF\u30FC\u30C9\u30FB\u30BF\u30A4\u30D7: "}, + {"Cannot.find.environment.variable.", + "\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: "}, + {"Cannot.find.file.", "\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: "}, + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/jarsigner/Resources_zh_CN.java b/src/sun/security/tools/jarsigner/Resources_zh_CN.java new file mode 100644 index 00000000..bdec2e20 --- /dev/null +++ b/src/sun/security/tools/jarsigner/Resources_zh_CN.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.jarsigner; + +/** + *

    This class represents the ResourceBundle + * for JarSigner. + * + */ +public class Resources_zh_CN extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // shared (from jarsigner) + {"SPACE", " "}, + {"2SPACE", " "}, + {"6SPACE", " "}, + {"COMMA", ", "}, + + {"provName.not.a.provider", "{0}\u4E0D\u662F\u63D0\u4F9B\u65B9"}, + {"signerClass.is.not.a.signing.mechanism", "{0}\u4E0D\u662F\u7B7E\u540D\u673A\u5236"}, + {"jarsigner.error.", "jarsigner \u9519\u8BEF: "}, + {"Illegal.option.", "\u975E\u6CD5\u9009\u9879: "}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "\u5982\u679C -storetype \u4E3A {0}, \u5219 -keystore \u5FC5\u987B\u4E3A NONE"}, + {".keypass.can.not.be.specified.if.storetype.is.{0}", + "\u5982\u679C -storetype \u4E3A {0}, \u5219\u4E0D\u80FD\u6307\u5B9A -keypass"}, + {"If.protected.is.specified.then.storepass.and.keypass.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A\u4E86 -protected, \u5219\u4E0D\u80FD\u6307\u5B9A -storepass \u548C -keypass"}, + {"If.keystore.is.not.password.protected.then.storepass.and.keypass.must.not.be.specified", + "\u5982\u679C\u5BC6\u94A5\u5E93\u672A\u53D7\u53E3\u4EE4\u4FDD\u62A4, \u5219\u4E0D\u80FD\u6307\u5B9A -storepass \u548C -keypass"}, + {"Usage.jarsigner.options.jar.file.alias", + "\u7528\u6CD5: jarsigner [\u9009\u9879] jar-file \u522B\u540D"}, + {".jarsigner.verify.options.jar.file.alias.", + " jarsigner -verify [\u9009\u9879] jar-file [\u522B\u540D...]"}, + {".keystore.url.keystore.location", + "[-keystore ] \u5BC6\u94A5\u5E93\u4F4D\u7F6E"}, + {".storepass.password.password.for.keystore.integrity", + "[-storepass <\u53E3\u4EE4>] \u7528\u4E8E\u5BC6\u94A5\u5E93\u5B8C\u6574\u6027\u7684\u53E3\u4EE4"}, + {".storetype.type.keystore.type", + "[-storetype <\u7C7B\u578B>] \u5BC6\u94A5\u5E93\u7C7B\u578B"}, + {".keypass.password.password.for.private.key.if.different.", + "[-keypass <\u53E3\u4EE4>] \u79C1\u6709\u5BC6\u94A5\u7684\u53E3\u4EE4 (\u5982\u679C\u4E0D\u540C)"}, + {".certchain.file.name.of.alternative.certchain.file", + "[-certchain <\u6587\u4EF6>] \u66FF\u4EE3\u8BC1\u4E66\u94FE\u6587\u4EF6\u7684\u540D\u79F0"}, + {".sigfile.file.name.of.SF.DSA.file", + "[-sigfile <\u6587\u4EF6>] .SF/.DSA \u6587\u4EF6\u7684\u540D\u79F0"}, + {".signedjar.file.name.of.signed.JAR.file", + "[-signedjar <\u6587\u4EF6>] \u5DF2\u7B7E\u540D\u7684 JAR \u6587\u4EF6\u7684\u540D\u79F0"}, + {".digestalg.algorithm.name.of.digest.algorithm", + "[-digestalg <\u7B97\u6CD5>] \u6458\u8981\u7B97\u6CD5\u7684\u540D\u79F0"}, + {".sigalg.algorithm.name.of.signature.algorithm", + "[-sigalg <\u7B97\u6CD5>] \u7B7E\u540D\u7B97\u6CD5\u7684\u540D\u79F0"}, + {".verify.verify.a.signed.JAR.file", + "[-verify] \u9A8C\u8BC1\u5DF2\u7B7E\u540D\u7684 JAR \u6587\u4EF6"}, + {".verbose.suboptions.verbose.output.when.signing.verifying.", + "[-verbose[:suboptions]] \u7B7E\u540D/\u9A8C\u8BC1\u65F6\u8F93\u51FA\u8BE6\u7EC6\u4FE1\u606F\u3002"}, + {".suboptions.can.be.all.grouped.or.summary", + " \u5B50\u9009\u9879\u53EF\u4EE5\u662F all, grouped \u6216 summary"}, + {".certs.display.certificates.when.verbose.and.verifying", + "[-certs] \u8F93\u51FA\u8BE6\u7EC6\u4FE1\u606F\u548C\u9A8C\u8BC1\u65F6\u663E\u793A\u8BC1\u4E66"}, + {".tsa.url.location.of.the.Timestamping.Authority", + "[-tsa ] \u65F6\u95F4\u6233\u9881\u53D1\u673A\u6784\u7684\u4F4D\u7F6E"}, + {".tsacert.alias.public.key.certificate.for.Timestamping.Authority", + "[-tsacert <\u522B\u540D>] \u65F6\u95F4\u6233\u9881\u53D1\u673A\u6784\u7684\u516C\u5171\u5BC6\u94A5\u8BC1\u4E66"}, + {".tsapolicyid.tsapolicyid.for.Timestamping.Authority", + "[-tsapolicyid ] \u65F6\u95F4\u6233\u9881\u53D1\u673A\u6784\u7684 TSAPolicyID"}, + {".altsigner.class.class.name.of.an.alternative.signing.mechanism", + "[-altsigner <\u7C7B>] \u66FF\u4EE3\u7684\u7B7E\u540D\u673A\u5236\u7684\u7C7B\u540D"}, + {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism", + "[-altsignerpath <\u8DEF\u5F84\u5217\u8868>] \u66FF\u4EE3\u7684\u7B7E\u540D\u673A\u5236\u7684\u4F4D\u7F6E"}, + {".internalsf.include.the.SF.file.inside.the.signature.block", + "[-internalsf] \u5728\u7B7E\u540D\u5757\u5185\u5305\u542B .SF \u6587\u4EF6"}, + {".sectionsonly.don.t.compute.hash.of.entire.manifest", + "[-sectionsonly] \u4E0D\u8BA1\u7B97\u6574\u4E2A\u6E05\u5355\u7684\u6563\u5217"}, + {".protected.keystore.has.protected.authentication.path", + "[-protected] \u5BC6\u94A5\u5E93\u5177\u6709\u53D7\u4FDD\u62A4\u9A8C\u8BC1\u8DEF\u5F84"}, + {".providerName.name.provider.name", + "[-providerName <\u540D\u79F0>] \u63D0\u4F9B\u65B9\u540D\u79F0"}, + {".providerClass.class.name.of.cryptographic.service.provider.s", + "[-providerClass <\u7C7B> \u52A0\u5BC6\u670D\u52A1\u63D0\u4F9B\u65B9\u7684\u540D\u79F0"}, + {".providerArg.arg.master.class.file.and.constructor.argument", + " [-providerArg <\u53C2\u6570>]]... \u4E3B\u7C7B\u6587\u4EF6\u548C\u6784\u9020\u5668\u53C2\u6570"}, + {".strict.treat.warnings.as.errors", + "[-strict] \u5C06\u8B66\u544A\u89C6\u4E3A\u9519\u8BEF"}, + {"Option.lacks.argument", "\u9009\u9879\u7F3A\u5C11\u53C2\u6570"}, + {"Please.type.jarsigner.help.for.usage", "\u8BF7\u952E\u5165 jarsigner -help \u4EE5\u4E86\u89E3\u7528\u6CD5"}, + {"Please.specify.jarfile.name", "\u8BF7\u6307\u5B9A jarfile \u540D\u79F0"}, + {"Please.specify.alias.name", "\u8BF7\u6307\u5B9A\u522B\u540D"}, + {"Only.one.alias.can.be.specified", "\u53EA\u80FD\u6307\u5B9A\u4E00\u4E2A\u522B\u540D"}, + {"This.jar.contains.signed.entries.which.is.not.signed.by.the.specified.alias.es.", + "\u6B64 jar \u5305\u542B\u672A\u7531\u6307\u5B9A\u522B\u540D\u7B7E\u540D\u7684\u5DF2\u7B7E\u540D\u6761\u76EE\u3002"}, + {"This.jar.contains.signed.entries.that.s.not.signed.by.alias.in.this.keystore.", + "\u6B64 jar \u5305\u542B\u672A\u7531\u6B64\u5BC6\u94A5\u5E93\u4E2D\u7684\u522B\u540D\u7B7E\u540D\u7684\u5DF2\u7B7E\u540D\u6761\u76EE\u3002"}, + {"s", "s"}, + {"m", "m"}, + {"k", "k"}, + {"i", "i"}, + {".and.d.more.", "(%d \u53CA\u4EE5\u4E0A)"}, + {".s.signature.was.verified.", + " s = \u5DF2\u9A8C\u8BC1\u7B7E\u540D "}, + {".m.entry.is.listed.in.manifest", + " m = \u5728\u6E05\u5355\u4E2D\u5217\u51FA\u6761\u76EE"}, + {".k.at.least.one.certificate.was.found.in.keystore", + " k = \u5728\u5BC6\u94A5\u5E93\u4E2D\u81F3\u5C11\u627E\u5230\u4E86\u4E00\u4E2A\u8BC1\u4E66"}, + {".i.at.least.one.certificate.was.found.in.identity.scope", + " i = \u5728\u8EAB\u4EFD\u4F5C\u7528\u57DF\u5185\u81F3\u5C11\u627E\u5230\u4E86\u4E00\u4E2A\u8BC1\u4E66"}, + {".X.not.signed.by.specified.alias.es.", + " X = \u672A\u7531\u6307\u5B9A\u522B\u540D\u7B7E\u540D"}, + {"no.manifest.", "\u6CA1\u6709\u6E05\u5355\u3002"}, + {".Signature.related.entries.","(\u4E0E\u7B7E\u540D\u76F8\u5173\u7684\u6761\u76EE)"}, + {".Unsigned.entries.", "(\u672A\u7B7E\u540D\u6761\u76EE)"}, + {"jar.is.unsigned.signatures.missing.or.not.parsable.", + "jar \u672A\u7B7E\u540D\u3002(\u7F3A\u5C11\u7B7E\u540D\u6216\u65E0\u6CD5\u89E3\u6790\u7B7E\u540D)"}, + {"jar.signed.", "jar \u5DF2\u7B7E\u540D\u3002"}, + {"jar.signed.with.signer.errors.", "jar \u5DF2\u7B7E\u540D, \u4F46\u51FA\u73B0\u7B7E\u540D\u8005\u9519\u8BEF\u3002"}, + {"jar.verified.", "jar \u5DF2\u9A8C\u8BC1\u3002"}, + {"jar.verified.with.signer.errors.", "jar \u5DF2\u9A8C\u8BC1, \u4F46\u51FA\u73B0\u7B7E\u540D\u8005\u9519\u8BEF\u3002"}, + {"jarsigner.", "jarsigner: "}, + {"signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or.", + "\u7B7E\u540D\u6587\u4EF6\u540D\u5FC5\u987B\u5305\u542B\u4EE5\u4E0B\u5B57\u7B26: A-Z, 0-9, _ \u6216 -"}, + {"unable.to.open.jar.file.", "\u65E0\u6CD5\u6253\u5F00 jar \u6587\u4EF6: "}, + {"unable.to.create.", "\u65E0\u6CD5\u521B\u5EFA: "}, + {".adding.", " \u6B63\u5728\u6DFB\u52A0: "}, + {".updating.", " \u6B63\u5728\u66F4\u65B0: "}, + {".signing.", " \u6B63\u5728\u7B7E\u540D: "}, + {"attempt.to.rename.signedJarFile.to.jarFile.failed", + "\u5C1D\u8BD5\u5C06{0}\u91CD\u547D\u540D\u4E3A{1}\u65F6\u5931\u8D25"}, + {"attempt.to.rename.jarFile.to.origJar.failed", + "\u5C1D\u8BD5\u5C06{0}\u91CD\u547D\u540D\u4E3A{1}\u65F6\u5931\u8D25"}, + {"unable.to.sign.jar.", "\u65E0\u6CD5\u5BF9 jar \u8FDB\u884C\u7B7E\u540D: "}, + {"Enter.Passphrase.for.keystore.", "\u8F93\u5165\u5BC6\u94A5\u5E93\u7684\u5BC6\u7801\u77ED\u8BED: "}, + {"keystore.load.", "\u5BC6\u94A5\u5E93\u52A0\u8F7D: "}, + {"certificate.exception.", "\u8BC1\u4E66\u5F02\u5E38\u9519\u8BEF: "}, + {"unable.to.instantiate.keystore.class.", + "\u65E0\u6CD5\u5B9E\u4F8B\u5316\u5BC6\u94A5\u5E93\u7C7B: "}, + {"Certificate.chain.not.found.for.alias.alias.must.reference.a.valid.KeyStore.key.entry.containing.a.private.key.and", + "\u627E\u4E0D\u5230{0}\u7684\u8BC1\u4E66\u94FE\u3002{1}\u5FC5\u987B\u5F15\u7528\u5305\u542B\u79C1\u6709\u5BC6\u94A5\u548C\u76F8\u5E94\u7684\u516C\u5171\u5BC6\u94A5\u8BC1\u4E66\u94FE\u7684\u6709\u6548\u5BC6\u94A5\u5E93\u5BC6\u94A5\u6761\u76EE\u3002"}, + {"File.specified.by.certchain.does.not.exist", + "\u7531 -certchain \u6307\u5B9A\u7684\u6587\u4EF6\u4E0D\u5B58\u5728"}, + {"Cannot.restore.certchain.from.file.specified", + "\u65E0\u6CD5\u4ECE\u6307\u5B9A\u7684\u6587\u4EF6\u8FD8\u539F certchain"}, + {"Certificate.chain.not.found.in.the.file.specified.", + "\u5728\u6307\u5B9A\u7684\u6587\u4EF6\u4E2D\u627E\u4E0D\u5230\u8BC1\u4E66\u94FE\u3002"}, + {"found.non.X.509.certificate.in.signer.s.chain", + "\u5728\u7B7E\u540D\u8005\u7684\u94FE\u4E2D\u627E\u5230\u975E X.509 \u8BC1\u4E66"}, + {"incomplete.certificate.chain", "\u8BC1\u4E66\u94FE\u4E0D\u5B8C\u6574"}, + {"Enter.key.password.for.alias.", "\u8F93\u5165{0}\u7684\u5BC6\u94A5\u53E3\u4EE4: "}, + {"unable.to.recover.key.from.keystore", + "\u65E0\u6CD5\u4ECE\u5BC6\u94A5\u5E93\u4E2D\u6062\u590D\u5BC6\u94A5"}, + {"key.associated.with.alias.not.a.private.key", + "\u4E0E{0}\u5173\u8054\u7684\u5BC6\u94A5\u4E0D\u662F\u79C1\u6709\u5BC6\u94A5"}, + {"you.must.enter.key.password", "\u5FC5\u987B\u8F93\u5165\u5BC6\u94A5\u53E3\u4EE4"}, + {"unable.to.read.password.", "\u65E0\u6CD5\u8BFB\u53D6\u53E3\u4EE4: "}, + {"certificate.is.valid.from", "\u8BC1\u4E66\u7684\u6709\u6548\u671F\u4E3A{0}\u81F3{1}"}, + {"certificate.expired.on", "\u8BC1\u4E66\u5230\u671F\u65E5\u671F\u4E3A {0}"}, + {"certificate.is.not.valid.until", + "\u76F4\u5230{0}, \u8BC1\u4E66\u624D\u6709\u6548"}, + {"certificate.will.expire.on", "\u8BC1\u4E66\u5C06\u5728{0}\u5230\u671F"}, + {".CertPath.not.validated.", "[CertPath \u672A\u9A8C\u8BC1: "}, + {"requesting.a.signature.timestamp", + "\u6B63\u5728\u8BF7\u6C42\u7B7E\u540D\u65F6\u95F4\u6233"}, + {"TSA.location.", "TSA \u4F4D\u7F6E: "}, + {"TSA.certificate.", "TSA \u8BC1\u4E66: "}, + {"no.response.from.the.Timestamping.Authority.", + "\u65F6\u95F4\u6233\u9881\u53D1\u673A\u6784\u6CA1\u6709\u54CD\u5E94\u3002\u5982\u679C\u8981\u4ECE\u9632\u706B\u5899\u540E\u9762\u8FDE\u63A5, \u5219\u53EF\u80FD\u9700\u8981\u6307\u5B9A HTTP \u6216 HTTPS \u4EE3\u7406\u3002\u8BF7\u4E3A jarsigner \u63D0\u4F9B\u4EE5\u4E0B\u9009\u9879: "}, + {"or", "\u6216"}, + {"Certificate.not.found.for.alias.alias.must.reference.a.valid.KeyStore.entry.containing.an.X.509.public.key.certificate.for.the", + "\u627E\u4E0D\u5230{0}\u7684\u8BC1\u4E66\u3002{1}\u5FC5\u987B\u5F15\u7528\u5305\u542B\u65F6\u95F4\u6233\u9881\u53D1\u673A\u6784\u7684 X.509 \u516C\u5171\u5BC6\u94A5\u8BC1\u4E66\u7684\u6709\u6548\u5BC6\u94A5\u5E93\u6761\u76EE\u3002"}, + {"using.an.alternative.signing.mechanism", + "\u6B63\u5728\u4F7F\u7528\u66FF\u4EE3\u7684\u7B7E\u540D\u673A\u5236"}, + {"entry.was.signed.on", "\u6761\u76EE\u7684\u7B7E\u540D\u65E5\u671F\u4E3A {0}"}, + {"Warning.", "\u8B66\u544A: "}, + {"Error.", "\u9519\u8BEF: "}, + {"This.jar.contains.unsigned.entries.which.have.not.been.integrity.checked.", + "\u6B64 jar \u5305\u542B\u5C1A\u672A\u8FDB\u884C\u5B8C\u6574\u6027\u68C0\u67E5\u7684\u672A\u7B7E\u540D\u6761\u76EE\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.has.expired.", + "\u6B64 jar \u5305\u542B\u7B7E\u540D\u8005\u8BC1\u4E66\u5DF2\u8FC7\u671F\u7684\u6761\u76EE\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.will.expire.within.six.months.", + "\u6B64 jar \u5305\u542B\u7B7E\u540D\u8005\u8BC1\u4E66\u5C06\u5728\u516D\u4E2A\u6708\u5185\u8FC7\u671F\u7684\u6761\u76EE\u3002 "}, + {"This.jar.contains.entries.whose.signer.certificate.is.not.yet.valid.", + "\u6B64 jar \u5305\u542B\u7B7E\u540D\u8005\u8BC1\u4E66\u4ECD\u65E0\u6548\u7684\u6761\u76EE\u3002 "}, + {"Re.run.with.the.verbose.option.for.more.details.", + "\u6709\u5173\u8BE6\u7EC6\u4FE1\u606F, \u8BF7\u4F7F\u7528 -verbose \u9009\u9879\u91CD\u65B0\u8FD0\u884C\u3002"}, + {"Re.run.with.the.verbose.and.certs.options.for.more.details.", + "\u6709\u5173\u8BE6\u7EC6\u4FE1\u606F, \u8BF7\u4F7F\u7528 -verbose \u548C -certs \u9009\u9879\u91CD\u65B0\u8FD0\u884C\u3002"}, + {"The.signer.certificate.has.expired.", + "\u7B7E\u540D\u8005\u8BC1\u4E66\u5DF2\u8FC7\u671F\u3002"}, + {"The.signer.certificate.will.expire.within.six.months.", + "\u7B7E\u540D\u8005\u8BC1\u4E66\u5C06\u5728\u516D\u4E2A\u6708\u5185\u8FC7\u671F\u3002"}, + {"The.signer.certificate.is.not.yet.valid.", + "\u7B7E\u540D\u8005\u8BC1\u4E66\u4ECD\u65E0\u6548\u3002"}, + {"The.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 KeyUsage \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u3002"}, + {"The.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 ExtendedKeyUsage \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u3002"}, + {"The.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 NetscapeCertType \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.KeyUsage.extension.doesn.t.allow.code.signing.", + "\u6B64 jar \u5305\u542B\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 KeyUsage \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u7684\u6761\u76EE\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.ExtendedKeyUsage.extension.doesn.t.allow.code.signing.", + "\u6B64 jar \u5305\u542B\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 ExtendedKeyUsage \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u7684\u6761\u76EE\u3002"}, + {"This.jar.contains.entries.whose.signer.certificate.s.NetscapeCertType.extension.doesn.t.allow.code.signing.", + "\u6B64 jar \u5305\u542B\u7531\u4E8E\u7B7E\u540D\u8005\u8BC1\u4E66\u7684 NetscapeCertType \u6269\u5C55\u800C\u65E0\u6CD5\u8FDB\u884C\u4EE3\u7801\u7B7E\u540D\u7684\u6761\u76EE\u3002"}, + {".{0}.extension.does.not.support.code.signing.", + "[{0} \u6269\u5C55\u4E0D\u652F\u6301\u4EE3\u7801\u7B7E\u540D]"}, + {"The.signer.s.certificate.chain.is.not.validated.", + "\u7B7E\u540D\u8005\u7684\u8BC1\u4E66\u94FE\u672A\u9A8C\u8BC1\u3002"}, + {"This.jar.contains.entries.whose.certificate.chain.is.not.validated.", + "\u6B64 jar \u5305\u542B\u8BC1\u4E66\u94FE\u672A\u9A8C\u8BC1\u7684\u6761\u76EE\u3002"}, + {"no.timestamp.signing", + "\u672A\u63D0\u4F9B -tsa \u6216 -tsacert, \u6B64 jar \u6CA1\u6709\u65F6\u95F4\u6233\u3002\u5982\u679C\u6CA1\u6709\u65F6\u95F4\u6233, \u5219\u5728\u7B7E\u540D\u8005\u8BC1\u4E66\u7684\u5230\u671F\u65E5\u671F (%1$tY-%1$tm-%1$td) \u6216\u4EE5\u540E\u7684\u4EFB\u4F55\u64A4\u9500\u65E5\u671F\u4E4B\u540E, \u7528\u6237\u53EF\u80FD\u65E0\u6CD5\u9A8C\u8BC1\u6B64 jar\u3002"}, + {"no.timestamp.verifying", + "\u6B64 jar \u5305\u542B\u7684\u7B7E\u540D\u6CA1\u6709\u65F6\u95F4\u6233\u3002\u5982\u679C\u6CA1\u6709\u65F6\u95F4\u6233, \u5219\u5728\u7B7E\u540D\u8005\u8BC1\u4E66\u7684\u5230\u671F\u65E5\u671F (%1$tY-%1$tm-%1$td) \u6216\u4EE5\u540E\u7684\u4EFB\u4F55\u64A4\u9500\u65E5\u671F\u4E4B\u540E, \u7528\u6237\u53EF\u80FD\u65E0\u6CD5\u9A8C\u8BC1\u6B64 jar\u3002"}, + {"Unknown.password.type.", "\u672A\u77E5\u53E3\u4EE4\u7C7B\u578B: "}, + {"Cannot.find.environment.variable.", + "\u627E\u4E0D\u5230\u73AF\u5883\u53D8\u91CF: "}, + {"Cannot.find.file.", "\u627E\u4E0D\u5230\u6587\u4EF6: "}, + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/jarsigner/TimestampedSigner.java b/src/sun/security/tools/jarsigner/TimestampedSigner.java new file mode 100644 index 00000000..3e504195 --- /dev/null +++ b/src/sun/security/tools/jarsigner/TimestampedSigner.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.jarsigner; + +import java.io.IOException; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import com.sun.jarsigner.*; +import sun.security.pkcs.PKCS7; +import sun.security.util.*; +import sun.security.x509.*; + +/** + * This class implements a content signing service. + * It generates a timestamped signature for a given content according to + * RFC 3161. + * The signature along with a trusted timestamp and the signer's certificate + * are all packaged into a standard PKCS #7 Signed Data message. + * + * @author Vincent Ryan + */ + +public final class TimestampedSigner extends ContentSigner { + + /* + * Object identifier for the subject information access X.509 certificate + * extension. + */ + private static final String SUBJECT_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.11"; + + /* + * Object identifier for the timestamping access descriptors. + */ + private static final ObjectIdentifier AD_TIMESTAMPING_Id; + static { + ObjectIdentifier tmp = null; + try { + tmp = new ObjectIdentifier("1.3.6.1.5.5.7.48.3"); + } catch (IOException e) { + // ignore + } + AD_TIMESTAMPING_Id = tmp; + } + + /** + * Instantiates a content signer that supports timestamped signatures. + */ + public TimestampedSigner() { + } + + /** + * Generates a PKCS #7 signed data message that includes a signature + * timestamp. + * This method is used when a signature has already been generated. + * The signature, a signature timestamp, the signer's certificate chain, + * and optionally the content that was signed, are packaged into a PKCS #7 + * signed data message. + * + * @param params The non-null input parameters. + * @param omitContent true if the content should be omitted from the + * signed data message. Otherwise the content is included. + * @param applyTimestamp true if the signature should be timestamped. + * Otherwise timestamping is not performed. + * @return A PKCS #7 signed data message including a signature timestamp. + * @throws NoSuchAlgorithmException The exception is thrown if the signature + * algorithm is unrecognised. + * @throws CertificateException The exception is thrown if an error occurs + * while processing the signer's certificate or the TSA's + * certificate. + * @throws IOException The exception is thrown if an error occurs while + * generating the signature timestamp or while generating the signed + * data message. + * @throws NullPointerException The exception is thrown if parameters is + * null. + */ + public byte[] generateSignedData(ContentSignerParameters params, + boolean omitContent, boolean applyTimestamp) + throws NoSuchAlgorithmException, CertificateException, IOException { + + if (params == null) { + throw new NullPointerException(); + } + + // Parse the signature algorithm to extract the digest + // algorithm. The expected format is: + // "with" + // or "withand" + String signatureAlgorithm = params.getSignatureAlgorithm(); + + X509Certificate[] signerChain = params.getSignerCertificateChain(); + byte[] signature = params.getSignature(); + + // Include or exclude content + byte[] content = (omitContent == true) ? null : params.getContent(); + + URI tsaURI = null; + if (applyTimestamp) { + tsaURI = params.getTimestampingAuthority(); + if (tsaURI == null) { + // Examine TSA cert + tsaURI = getTimestampingURI( + params.getTimestampingAuthorityCertificate()); + if (tsaURI == null) { + throw new CertificateException( + "Subject Information Access extension not found"); + } + } + } + return PKCS7.generateSignedData(signature, signerChain, content, + params.getSignatureAlgorithm(), tsaURI, + params.getTSAPolicyID()); + } + + /** + * Examine the certificate for a Subject Information Access extension + * (RFC 3280). + * The extension's accessMethod field should contain the object + * identifier defined for timestamping: 1.3.6.1.5.5.7.48.3 and its + * accessLocation field should contain an HTTP or HTTPS URL. + * + * @param tsaCertificate An X.509 certificate for the TSA. + * @return An HTTP or HTTPS URI or null if none was found. + */ + public static URI getTimestampingURI(X509Certificate tsaCertificate) { + + if (tsaCertificate == null) { + return null; + } + // Parse the extensions + try { + byte[] extensionValue = + tsaCertificate.getExtensionValue(SUBJECT_INFO_ACCESS_OID); + if (extensionValue == null) { + return null; + } + DerInputStream der = new DerInputStream(extensionValue); + der = new DerInputStream(der.getOctetString()); + DerValue[] derValue = der.getSequence(5); + AccessDescription description; + GeneralName location; + URIName uri; + for (int i = 0; i < derValue.length; i++) { + description = new AccessDescription(derValue[i]); + if (description.getAccessMethod() + .equals((Object)AD_TIMESTAMPING_Id)) { + location = description.getAccessLocation(); + if (location.getType() == GeneralNameInterface.NAME_URI) { + uri = (URIName) location.getName(); + if (uri.getScheme().equalsIgnoreCase("http") || + uri.getScheme().equalsIgnoreCase("https")) { + return uri.getURI(); + } + } + } + } + } catch (IOException ioe) { + // ignore + } + return null; + } +} diff --git a/src/sun/security/tools/keytool/CertAndKeyGen.java b/src/sun/security/tools/keytool/CertAndKeyGen.java new file mode 100644 index 00000000..713c3fc9 --- /dev/null +++ b/src/sun/security/tools/keytool/CertAndKeyGen.java @@ -0,0 +1,337 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateEncodingException; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Date; + +import sun.security.pkcs10.PKCS10; +import sun.security.x509.*; + +/** + * Generate a pair of keys, and provide access to them. This class is + * provided primarily for ease of use. + * + *

    This provides some simple certificate management functionality. + * Specifically, it allows you to create self-signed X.509 certificates + * as well as PKCS 10 based certificate signing requests. + * + *

    Keys for some public key signature algorithms have algorithm + * parameters, such as DSS/DSA. Some sites' Certificate Authorities + * adopt fixed algorithm parameters, which speeds up some operations + * including key generation and signing. At this time, this interface + * does not provide a way to provide such algorithm parameters, e.g. + * by providing the CA certificate which includes those parameters. + * + *

    Also, note that at this time only signature-capable keys may be + * acquired through this interface. Diffie-Hellman keys, used for secure + * key exchange, may be supported later. + * + * @author David Brownell + * @author Hemma Prafullchandra + * @see PKCS10 + * @see X509CertImpl + */ +public final class CertAndKeyGen { + /** + * Creates a CertAndKeyGen object for a particular key type + * and signature algorithm. + * + * @param keyType type of key, e.g. "RSA", "DSA" + * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", + * "MD2WithRSA", "SHAwithDSA". + * @exception NoSuchAlgorithmException on unrecognized algorithms. + */ + public CertAndKeyGen (String keyType, String sigAlg) + throws NoSuchAlgorithmException + { + keyGen = KeyPairGenerator.getInstance(keyType); + this.sigAlg = sigAlg; + } + + /** + * Creates a CertAndKeyGen object for a particular key type, + * signature algorithm, and provider. + * + * @param keyType type of key, e.g. "RSA", "DSA" + * @param sigAlg name of the signature algorithm, e.g. "MD5WithRSA", + * "MD2WithRSA", "SHAwithDSA". + * @param providerName name of the provider + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception NoSuchProviderException on unrecognized providers. + */ + public CertAndKeyGen (String keyType, String sigAlg, String providerName) + throws NoSuchAlgorithmException, NoSuchProviderException + { + if (providerName == null) { + keyGen = KeyPairGenerator.getInstance(keyType); + } else { + try { + keyGen = KeyPairGenerator.getInstance(keyType, providerName); + } catch (Exception e) { + // try first available provider instead + keyGen = KeyPairGenerator.getInstance(keyType); + } + } + this.sigAlg = sigAlg; + } + + /** + * Sets the source of random numbers used when generating keys. + * If you do not provide one, a system default facility is used. + * You may wish to provide your own source of random numbers + * to get a reproducible sequence of keys and signatures, or + * because you may be able to take advantage of strong sources + * of randomness/entropy in your environment. + */ + public void setRandom (SecureRandom generator) + { + prng = generator; + } + + // want "public void generate (X509Certificate)" ... inherit DSA/D-H param + + /** + * Generates a random public/private key pair, with a given key + * size. Different algorithms provide different degrees of security + * for the same key size, because of the "work factor" involved in + * brute force attacks. As computers become faster, it becomes + * easier to perform such attacks. Small keys are to be avoided. + * + *

    Note that not all values of "keyBits" are valid for all + * algorithms, and not all public key algorithms are currently + * supported for use in X.509 certificates. If the algorithm + * you specified does not produce X.509 compatible keys, an + * invalid key exception is thrown. + * + * @param keyBits the number of bits in the keys. + * @exception InvalidKeyException if the environment does not + * provide X.509 public keys for this signature algorithm. + */ + public void generate (int keyBits) + throws InvalidKeyException + { + KeyPair pair; + + try { + if (prng == null) { + prng = new SecureRandom(); + } + keyGen.initialize(keyBits, prng); + pair = keyGen.generateKeyPair(); + + } catch (Exception e) { + throw new IllegalArgumentException(e.getMessage()); + } + + publicKey = pair.getPublic(); + privateKey = pair.getPrivate(); + + // publicKey's format must be X.509 otherwise + // the whole CertGen part of this class is broken. + if (!"X.509".equalsIgnoreCase(publicKey.getFormat())) { + throw new IllegalArgumentException("publicKey's is not X.509, but " + + publicKey.getFormat()); + } + } + + + /** + * Returns the public key of the generated key pair if it is of type + * X509Key, or null if the public key is of a different type. + * + * XXX Note: This behaviour is needed for backwards compatibility. + * What this method really should return is the public key of the + * generated key pair, regardless of whether or not it is an instance of + * X509Key. Accordingly, the return type of this method + * should be PublicKey. + */ + public X509Key getPublicKey() + { + if (!(publicKey instanceof X509Key)) { + return null; + } + return (X509Key)publicKey; + } + + /** + * Always returns the public key of the generated key pair. Used + * by KeyTool only. + * + * The publicKey is not necessarily to be an instance of + * X509Key in some JCA/JCE providers, for example SunPKCS11. + */ + public PublicKey getPublicKeyAnyway() { + return publicKey; + } + + /** + * Returns the private key of the generated key pair. + * + *

    Be extremely careful when handling private keys. + * When private keys are not kept secret, they lose their ability + * to securely authenticate specific entities ... that is a huge + * security risk! + */ + public PrivateKey getPrivateKey () + { + return privateKey; + } + + /** + * Returns a self-signed X.509v3 certificate for the public key. + * The certificate is immediately valid. No extensions. + * + *

    Such certificates normally are used to identify a "Certificate + * Authority" (CA). Accordingly, they will not always be accepted by + * other parties. However, such certificates are also useful when + * you are bootstrapping your security infrastructure, or deploying + * system prototypes. + * + * @param myname X.500 name of the subject (who is also the issuer) + * @param firstDate the issue time of the certificate + * @param validity how long the certificate should be valid, in seconds + * @exception CertificateException on certificate handling errors. + * @exception InvalidKeyException on key handling errors. + * @exception SignatureException on signature handling errors. + * @exception NoSuchAlgorithmException on unrecognized algorithms. + * @exception NoSuchProviderException on unrecognized providers. + */ + public X509Certificate getSelfCertificate ( + X500Name myname, Date firstDate, long validity) + throws CertificateException, InvalidKeyException, SignatureException, + NoSuchAlgorithmException, NoSuchProviderException + { + return getSelfCertificate(myname, firstDate, validity, null); + } + + // Like above, plus a CertificateExtensions argument, which can be null. + public X509Certificate getSelfCertificate (X500Name myname, Date firstDate, + long validity, CertificateExtensions ext) + throws CertificateException, InvalidKeyException, SignatureException, + NoSuchAlgorithmException, NoSuchProviderException + { + X509CertImpl cert; + Date lastDate; + + try { + lastDate = new Date (); + lastDate.setTime (firstDate.getTime () + validity * 1000); + + CertificateValidity interval = + new CertificateValidity(firstDate,lastDate); + + X509CertInfo info = new X509CertInfo(); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlg, privateKey); + // Add all mandatory attributes + info.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V3)); + info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( + new java.util.Random().nextInt() & 0x7fffffff)); + AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlg, params); + info.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(algID)); + info.set(X509CertInfo.SUBJECT, myname); + info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); + info.set(X509CertInfo.VALIDITY, interval); + info.set(X509CertInfo.ISSUER, myname); + if (ext != null) info.set(X509CertInfo.EXTENSIONS, ext); + + cert = new X509CertImpl(info); + cert.sign(privateKey, + params, + sigAlg, + null); + + return (X509Certificate)cert; + + } catch (IOException e) { + throw new CertificateEncodingException("getSelfCert: " + + e.getMessage()); + } catch (InvalidAlgorithmParameterException e2) { + throw new SignatureException( + "Unsupported PSSParameterSpec: " + e2.getMessage()); + } + } + + // Keep the old method + public X509Certificate getSelfCertificate (X500Name myname, long validity) + throws CertificateException, InvalidKeyException, SignatureException, + NoSuchAlgorithmException, NoSuchProviderException + { + return getSelfCertificate(myname, new Date(), validity); + } + + /** + * Returns a PKCS #10 certificate request. The caller uses either + * PKCS10.print or PKCS10.toByteArray + * operations on the result, to get the request in an appropriate + * transmission format. + * + *

    PKCS #10 certificate requests are sent, along with some proof + * of identity, to Certificate Authorities (CAs) which then issue + * X.509 public key certificates. + * + * @param myname X.500 name of the subject + * @exception InvalidKeyException on key handling errors. + * @exception SignatureException on signature handling errors. + */ + // This method is not used inside JDK. Will not update it. + public PKCS10 getCertRequest (X500Name myname) + throws InvalidKeyException, SignatureException + { + PKCS10 req = new PKCS10 (publicKey); + + try { + Signature signature = Signature.getInstance(sigAlg); + signature.initSign (privateKey); + req.encodeAndSign(myname, signature); + + } catch (CertificateException e) { + throw new SignatureException (sigAlg + " CertificateException"); + + } catch (IOException e) { + throw new SignatureException (sigAlg + " IOException"); + + } catch (NoSuchAlgorithmException e) { + // "can't happen" + throw new SignatureException (sigAlg + " unavailable?"); + } + return req; + } + + private SecureRandom prng; + private String sigAlg; + private KeyPairGenerator keyGen; + private PublicKey publicKey; + private PrivateKey privateKey; +} diff --git a/src/sun/security/tools/keytool/Main.java b/src/sun/security/tools/keytool/Main.java new file mode 100644 index 00000000..7521fb1e --- /dev/null +++ b/src/sun/security/tools/keytool/Main.java @@ -0,0 +1,4200 @@ +/* + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +import java.io.*; +import java.security.CodeSigner; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.Key; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.Security; +import java.security.Signature; +import java.security.Timestamp; +import java.security.UnrecoverableEntryException; +import java.security.UnrecoverableKeyException; +import java.security.Principal; +import java.security.Provider; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertStoreException; +import java.security.cert.CRL; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.spec.AlgorithmParameterSpec; +import java.text.Collator; +import java.text.MessageFormat; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.lang.reflect.Constructor; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.cert.CertStore; + +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.security.cert.X509CRLSelector; +import javax.security.auth.x500.X500Principal; +import java.util.Base64; +import sun.security.util.ObjectIdentifier; +import sun.security.pkcs10.PKCS10; +import sun.security.pkcs10.PKCS10Attribute; +import sun.security.provider.X509Factory; +import sun.security.provider.certpath.CertStoreHelper; +import sun.security.util.Password; +import sun.security.util.SignatureUtil; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import sun.security.pkcs.PKCS9Attribute; +import sun.security.tools.KeyStoreUtil; +import sun.security.tools.PathList; +import sun.security.util.DerValue; +import sun.security.x509.*; + +import static java.security.KeyStore.*; +import static sun.security.tools.keytool.Main.Command.*; +import static sun.security.tools.keytool.Main.Option.*; + +/** + * This tool manages keystores. + * + * @author Jan Luehe + * + * + * @see KeyStore + * @see sun.security.provider.KeyProtector + * @see sun.security.provider.JavaKeyStore + * + * @since 1.2 + */ +public final class Main { + + private boolean debug = false; + private Command command = null; + private String sigAlgName = null; + private String keyAlgName = null; + private boolean verbose = false; + private int keysize = -1; + private boolean rfc = false; + private long validity = (long)90; + private String alias = null; + private String dname = null; + private String dest = null; + private String filename = null; + private String infilename = null; + private String outfilename = null; + private String srcksfname = null; + + // User-specified providers are added before any command is called. + // However, they are not removed before the end of the main() method. + // If you're calling KeyTool.main() directly in your own Java program, + // please programtically add any providers you need and do not specify + // them through the command line. + + private Set> providers = null; + private String storetype = null; + private String srcProviderName = null; + private String providerName = null; + private String pathlist = null; + private char[] storePass = null; + private char[] storePassNew = null; + private char[] keyPass = null; + private char[] keyPassNew = null; + private char[] newPass = null; + private char[] destKeyPass = null; + private char[] srckeyPass = null; + private String ksfname = null; + private File ksfile = null; + private InputStream ksStream = null; // keystore stream + private String sslserver = null; + private String jarfile = null; + private KeyStore keyStore = null; + private boolean token = false; + private boolean nullStream = false; + private boolean kssave = false; + private boolean noprompt = false; + private boolean trustcacerts = false; + private boolean protectedPath = false; + private boolean srcprotectedPath = false; + private CertificateFactory cf = null; + private KeyStore caks = null; // "cacerts" keystore + private char[] srcstorePass = null; + private String srcstoretype = null; + private Set passwords = new HashSet<>(); + private String startDate = null; + + private List ids = new ArrayList<>(); // used in GENCRL + private List v3ext = new ArrayList<>(); + + enum Command { + CERTREQ("Generates.a.certificate.request", + ALIAS, SIGALG, FILEOUT, KEYPASS, KEYSTORE, DNAME, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + CHANGEALIAS("Changes.an.entry.s.alias", + ALIAS, DESTALIAS, KEYPASS, KEYSTORE, STOREPASS, + STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V, PROTECTED), + DELETE("Deletes.an.entry", + ALIAS, KEYSTORE, STOREPASS, STORETYPE, + PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V, PROTECTED), + EXPORTCERT("Exports.certificate", + RFC, ALIAS, FILEOUT, KEYSTORE, STOREPASS, + STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V, PROTECTED), + GENKEYPAIR("Generates.a.key.pair", + ALIAS, KEYALG, KEYSIZE, SIGALG, DESTALIAS, DNAME, + STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + GENSECKEY("Generates.a.secret.key", + ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + GENCERT("Generates.certificate.from.a.certificate.request", + RFC, INFILE, OUTFILE, ALIAS, SIGALG, DNAME, + STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + IMPORTCERT("Imports.a.certificate.or.a.certificate.chain", + NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN, + KEYPASS, KEYSTORE, STOREPASS, STORETYPE, + PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V), + IMPORTPASS("Imports.a.password", + ALIAS, KEYPASS, KEYALG, KEYSIZE, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + IMPORTKEYSTORE("Imports.one.or.all.entries.from.another.keystore", + SRCKEYSTORE, DESTKEYSTORE, SRCSTORETYPE, + DESTSTORETYPE, SRCSTOREPASS, DESTSTOREPASS, + SRCPROTECTED, SRCPROVIDERNAME, DESTPROVIDERNAME, + SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS, + NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, + V), + KEYPASSWD("Changes.the.key.password.of.an.entry", + ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS, + STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V), + LIST("Lists.entries.in.a.keystore", + RFC, ALIAS, KEYSTORE, STOREPASS, STORETYPE, + PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, + PROVIDERPATH, V, PROTECTED), + PRINTCERT("Prints.the.content.of.a.certificate", + RFC, FILEIN, SSLSERVER, JARFILE, V), + PRINTCERTREQ("Prints.the.content.of.a.certificate.request", + FILEIN, V), + PRINTCRL("Prints.the.content.of.a.CRL.file", + FILEIN, V), + STOREPASSWD("Changes.the.store.password.of.a.keystore", + NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, + PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), + + // Undocumented start here, KEYCLONE is used a marker in -help; + + KEYCLONE("Clones.a.key.entry", + ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE, + KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V), + SELFCERT("Generates.a.self.signed.certificate", + ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS, + STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, + PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), + GENCRL("Generates.CRL", + RFC, FILEOUT, ID, + ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + IDENTITYDB("Imports.entries.from.a.JDK.1.1.x.style.identity.database", + FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, + PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V); + + final String description; + final Option[] options; + Command(String d, Option... o) { + description = d; + options = o; + } + @Override + public String toString() { + return "-" + name().toLowerCase(Locale.ENGLISH); + } + }; + + enum Option { + ALIAS("alias", "", "alias.name.of.the.entry.to.process"), + DESTALIAS("destalias", "", "destination.alias"), + DESTKEYPASS("destkeypass", "", "destination.key.password"), + DESTKEYSTORE("destkeystore", "", "destination.keystore.name"), + DESTPROTECTED("destprotected", null, "destination.keystore.password.protected"), + DESTPROVIDERNAME("destprovidername", "", "destination.keystore.provider.name"), + DESTSTOREPASS("deststorepass", "", "destination.keystore.password"), + DESTSTORETYPE("deststoretype", "", "destination.keystore.type"), + DNAME("dname", "", "distinguished.name"), + EXT("ext", "", "X.509.extension"), + FILEOUT("file", "", "output.file.name"), + FILEIN("file", "", "input.file.name"), + ID("id", "", "Serial.ID.of.cert.to.revoke"), + INFILE("infile", "", "input.file.name"), + KEYALG("keyalg", "", "key.algorithm.name"), + KEYPASS("keypass", "", "key.password"), + KEYSIZE("keysize", "", "key.bit.size"), + KEYSTORE("keystore", "", "keystore.name"), + NEW("new", "", "new.password"), + NOPROMPT("noprompt", null, "do.not.prompt"), + OUTFILE("outfile", "", "output.file.name"), + PROTECTED("protected", null, "password.through.protected.mechanism"), + PROVIDERARG("providerarg", "", "provider.argument"), + PROVIDERCLASS("providerclass", "", "provider.class.name"), + PROVIDERNAME("providername", "", "provider.name"), + PROVIDERPATH("providerpath", "", "provider.classpath"), + RFC("rfc", null, "output.in.RFC.style"), + SIGALG("sigalg", "", "signature.algorithm.name"), + SRCALIAS("srcalias", "", "source.alias"), + SRCKEYPASS("srckeypass", "", "source.key.password"), + SRCKEYSTORE("srckeystore", "", "source.keystore.name"), + SRCPROTECTED("srcprotected", null, "source.keystore.password.protected"), + SRCPROVIDERNAME("srcprovidername", "", "source.keystore.provider.name"), + SRCSTOREPASS("srcstorepass", "", "source.keystore.password"), + SRCSTORETYPE("srcstoretype", "", "source.keystore.type"), + SSLSERVER("sslserver", "", "SSL.server.host.and.port"), + JARFILE("jarfile", "", "signed.jar.file"), + STARTDATE("startdate", "", "certificate.validity.start.date.time"), + STOREPASS("storepass", "", "keystore.password"), + STORETYPE("storetype", "", "keystore.type"), + TRUSTCACERTS("trustcacerts", null, "trust.certificates.from.cacerts"), + V("v", null, "verbose.output"), + VALIDITY("validity", "", "validity.number.of.days"); + + final String name, arg, description; + Option(String name, String arg, String description) { + this.name = name; + this.arg = arg; + this.description = description; + } + @Override + public String toString() { + return "-" + name; + } + }; + + private static final Class[] PARAM_STRING = { String.class }; + + private static final String NONE = "NONE"; + private static final String P11KEYSTORE = "PKCS11"; + private static final String P12KEYSTORE = "PKCS12"; + private final String keyAlias = "mykey"; + + // for i18n + private static final ResourceBundle rb = + ResourceBundle.getBundle( + "sun.security.tools.keytool.Resources"); + private static final Collator collator = Collator.getInstance(); + static { + // this is for case insensitive string comparisons + collator.setStrength(Collator.PRIMARY); + }; + + private Main() { } + + public static void main(String[] args) throws Exception { + Main kt = new Main(); + kt.run(args, System.out); + } + + private void run(String[] args, PrintStream out) throws Exception { + try { + parseArgs(args); + if (command != null) { + doCommands(out); + } + } catch (Exception e) { + System.out.println(rb.getString("keytool.error.") + e); + if (verbose) { + e.printStackTrace(System.out); + } + if (!debug) { + System.exit(1); + } else { + throw e; + } + } finally { + for (char[] pass : passwords) { + if (pass != null) { + Arrays.fill(pass, ' '); + pass = null; + } + } + + if (ksStream != null) { + ksStream.close(); + } + } + } + + /** + * Parse command line arguments. + */ + void parseArgs(String[] args) { + + int i=0; + boolean help = args.length == 0; + + for (i=0; (i < args.length) && args[i].startsWith("-"); i++) { + + String flags = args[i]; + + // Check if the last option needs an arg + if (i == args.length - 1) { + for (Option option: Option.values()) { + // Only options with an arg need to be checked + if (collator.compare(flags, option.toString()) == 0) { + if (option.arg != null) errorNeedArgument(flags); + break; + } + } + } + + /* + * Check modifiers + */ + String modifier = null; + int pos = flags.indexOf(':'); + if (pos > 0) { + modifier = flags.substring(pos+1); + flags = flags.substring(0, pos); + } + /* + * command modes + */ + boolean isCommand = false; + for (Command c: Command.values()) { + if (collator.compare(flags, c.toString()) == 0) { + command = c; + isCommand = true; + break; + } + } + + if (isCommand) { + // already recognized as a command + } else if (collator.compare(flags, "-export") == 0) { + command = EXPORTCERT; + } else if (collator.compare(flags, "-genkey") == 0) { + command = GENKEYPAIR; + } else if (collator.compare(flags, "-import") == 0) { + command = IMPORTCERT; + } else if (collator.compare(flags, "-importpassword") == 0) { + command = IMPORTPASS; + } + /* + * Help + */ + else if (collator.compare(flags, "-help") == 0) { + help = true; + } + + /* + * specifiers + */ + else if (collator.compare(flags, "-keystore") == 0 || + collator.compare(flags, "-destkeystore") == 0) { + ksfname = args[++i]; + } else if (collator.compare(flags, "-storepass") == 0 || + collator.compare(flags, "-deststorepass") == 0) { + storePass = getPass(modifier, args[++i]); + passwords.add(storePass); + } else if (collator.compare(flags, "-storetype") == 0 || + collator.compare(flags, "-deststoretype") == 0) { + storetype = args[++i]; + } else if (collator.compare(flags, "-srcstorepass") == 0) { + srcstorePass = getPass(modifier, args[++i]); + passwords.add(srcstorePass); + } else if (collator.compare(flags, "-srcstoretype") == 0) { + srcstoretype = args[++i]; + } else if (collator.compare(flags, "-srckeypass") == 0) { + srckeyPass = getPass(modifier, args[++i]); + passwords.add(srckeyPass); + } else if (collator.compare(flags, "-srcprovidername") == 0) { + srcProviderName = args[++i]; + } else if (collator.compare(flags, "-providername") == 0 || + collator.compare(flags, "-destprovidername") == 0) { + providerName = args[++i]; + } else if (collator.compare(flags, "-providerpath") == 0) { + pathlist = args[++i]; + } else if (collator.compare(flags, "-keypass") == 0) { + keyPass = getPass(modifier, args[++i]); + passwords.add(keyPass); + } else if (collator.compare(flags, "-new") == 0) { + newPass = getPass(modifier, args[++i]); + passwords.add(newPass); + } else if (collator.compare(flags, "-destkeypass") == 0) { + destKeyPass = getPass(modifier, args[++i]); + passwords.add(destKeyPass); + } else if (collator.compare(flags, "-alias") == 0 || + collator.compare(flags, "-srcalias") == 0) { + alias = args[++i]; + } else if (collator.compare(flags, "-dest") == 0 || + collator.compare(flags, "-destalias") == 0) { + dest = args[++i]; + } else if (collator.compare(flags, "-dname") == 0) { + dname = args[++i]; + } else if (collator.compare(flags, "-keysize") == 0) { + keysize = Integer.parseInt(args[++i]); + } else if (collator.compare(flags, "-keyalg") == 0) { + keyAlgName = args[++i]; + } else if (collator.compare(flags, "-sigalg") == 0) { + sigAlgName = args[++i]; + } else if (collator.compare(flags, "-startdate") == 0) { + startDate = args[++i]; + } else if (collator.compare(flags, "-validity") == 0) { + validity = Long.parseLong(args[++i]); + } else if (collator.compare(flags, "-ext") == 0) { + v3ext.add(args[++i]); + } else if (collator.compare(flags, "-id") == 0) { + ids.add(args[++i]); + } else if (collator.compare(flags, "-file") == 0) { + filename = args[++i]; + } else if (collator.compare(flags, "-infile") == 0) { + infilename = args[++i]; + } else if (collator.compare(flags, "-outfile") == 0) { + outfilename = args[++i]; + } else if (collator.compare(flags, "-sslserver") == 0) { + sslserver = args[++i]; + } else if (collator.compare(flags, "-jarfile") == 0) { + jarfile = args[++i]; + } else if (collator.compare(flags, "-srckeystore") == 0) { + srcksfname = args[++i]; + } else if ((collator.compare(flags, "-provider") == 0) || + (collator.compare(flags, "-providerclass") == 0)) { + if (providers == null) { + providers = new HashSet> (3); + } + String providerClass = args[++i]; + String providerArg = null; + + if (args.length > (i+1)) { + flags = args[i+1]; + if (collator.compare(flags, "-providerarg") == 0) { + if (args.length == (i+2)) errorNeedArgument(flags); + providerArg = args[i+2]; + i += 2; + } + } + providers.add( + Pair.of(providerClass, providerArg)); + } + + /* + * options + */ + else if (collator.compare(flags, "-v") == 0) { + verbose = true; + } else if (collator.compare(flags, "-debug") == 0) { + debug = true; + } else if (collator.compare(flags, "-rfc") == 0) { + rfc = true; + } else if (collator.compare(flags, "-noprompt") == 0) { + noprompt = true; + } else if (collator.compare(flags, "-trustcacerts") == 0) { + trustcacerts = true; + } else if (collator.compare(flags, "-protected") == 0 || + collator.compare(flags, "-destprotected") == 0) { + protectedPath = true; + } else if (collator.compare(flags, "-srcprotected") == 0) { + srcprotectedPath = true; + } else { + System.err.println(rb.getString("Illegal.option.") + flags); + tinyHelp(); + } + } + + if (i provider: providers) { + String provName = provider.fst; + Class provClass; + if (cl != null) { + provClass = cl.loadClass(provName); + } else { + provClass = Class.forName(provName); + } + + String provArg = provider.snd; + Object obj; + if (provArg == null) { + obj = provClass.newInstance(); + } else { + Constructor c = provClass.getConstructor(PARAM_STRING); + obj = c.newInstance(provArg); + } + if (!(obj instanceof Provider)) { + MessageFormat form = new MessageFormat + (rb.getString("provName.not.a.provider")); + Object[] source = {provName}; + throw new Exception(form.format(source)); + } + Security.addProvider((Provider)obj); + } + } + + if (command == LIST && verbose && rfc) { + System.err.println(rb.getString + ("Must.not.specify.both.v.and.rfc.with.list.command")); + tinyHelp(); + } + + // Make sure provided passwords are at least 6 characters long + if (command == GENKEYPAIR && keyPass!=null && keyPass.length < 6) { + throw new Exception(rb.getString + ("Key.password.must.be.at.least.6.characters")); + } + if (newPass != null && newPass.length < 6) { + throw new Exception(rb.getString + ("New.password.must.be.at.least.6.characters")); + } + if (destKeyPass != null && destKeyPass.length < 6) { + throw new Exception(rb.getString + ("New.password.must.be.at.least.6.characters")); + } + + // Check if keystore exists. + // If no keystore has been specified at the command line, try to use + // the default, which is located in $HOME/.keystore. + // If the command is "genkey", "identitydb", "import", or "printcert", + // it is OK not to have a keystore. + if (isKeyStoreRelated(command)) { + if (ksfname == null) { + ksfname = System.getProperty("user.home") + File.separator + + ".keystore"; + } + + if (!nullStream) { + try { + ksfile = new File(ksfname); + // Check if keystore file is empty + if (ksfile.exists() && ksfile.length() == 0) { + throw new Exception(rb.getString + ("Keystore.file.exists.but.is.empty.") + ksfname); + } + ksStream = new FileInputStream(ksfile); + } catch (FileNotFoundException e) { + if (command != GENKEYPAIR && + command != GENSECKEY && + command != IDENTITYDB && + command != IMPORTCERT && + command != IMPORTPASS && + command != IMPORTKEYSTORE && + command != PRINTCRL) { + throw new Exception(rb.getString + ("Keystore.file.does.not.exist.") + ksfname); + } + } + } + } + + if ((command == KEYCLONE || command == CHANGEALIAS) + && dest == null) { + dest = getAlias("destination"); + if ("".equals(dest)) { + throw new Exception(rb.getString + ("Must.specify.destination.alias")); + } + } + + if (command == DELETE && alias == null) { + alias = getAlias(null); + if ("".equals(alias)) { + throw new Exception(rb.getString("Must.specify.alias")); + } + } + + // Create new keystore + if (providerName == null) { + keyStore = KeyStore.getInstance(storetype); + } else { + keyStore = KeyStore.getInstance(storetype, providerName); + } + + /* + * Load the keystore data. + * + * At this point, it's OK if no keystore password has been provided. + * We want to make sure that we can load the keystore data, i.e., + * the keystore data has the right format. If we cannot load the + * keystore, why bother asking the user for his or her password? + * Only if we were able to load the keystore, and no keystore + * password has been provided, will we prompt the user for the + * keystore password to verify the keystore integrity. + * This means that the keystore is loaded twice: first load operation + * checks the keystore format, second load operation verifies the + * keystore integrity. + * + * If the keystore password has already been provided (at the + * command line), however, the keystore is loaded only once, and the + * keystore format and integrity are checked "at the same time". + * + * Null stream keystores are loaded later. + */ + if (!nullStream) { + keyStore.load(ksStream, storePass); + if (ksStream != null) { + ksStream.close(); + } + } + + // All commands that create or modify the keystore require a keystore + // password. + + if (nullStream && storePass != null) { + keyStore.load(null, storePass); + } else if (!nullStream && storePass != null) { + // If we are creating a new non nullStream-based keystore, + // insist that the password be at least 6 characters + if (ksStream == null && storePass.length < 6) { + throw new Exception(rb.getString + ("Keystore.password.must.be.at.least.6.characters")); + } + } else if (storePass == null) { + + // only prompt if (protectedPath == false) + + if (!protectedPath && !KeyStoreUtil.isWindowsKeyStore(storetype) && + (command == CERTREQ || + command == DELETE || + command == GENKEYPAIR || + command == GENSECKEY || + command == IMPORTCERT || + command == IMPORTPASS || + command == IMPORTKEYSTORE || + command == KEYCLONE || + command == CHANGEALIAS || + command == SELFCERT || + command == STOREPASSWD || + command == KEYPASSWD || + command == IDENTITYDB)) { + int count = 0; + do { + if (command == IMPORTKEYSTORE) { + System.err.print + (rb.getString("Enter.destination.keystore.password.")); + } else { + System.err.print + (rb.getString("Enter.keystore.password.")); + } + System.err.flush(); + storePass = Password.readPassword(System.in); + passwords.add(storePass); + + // If we are creating a new non nullStream-based keystore, + // insist that the password be at least 6 characters + if (!nullStream && (storePass == null || storePass.length < 6)) { + System.err.println(rb.getString + ("Keystore.password.is.too.short.must.be.at.least.6.characters")); + storePass = null; + } + + // If the keystore file does not exist and needs to be + // created, the storepass should be prompted twice. + if (storePass != null && !nullStream && ksStream == null) { + System.err.print(rb.getString("Re.enter.new.password.")); + char[] storePassAgain = Password.readPassword(System.in); + passwords.add(storePassAgain); + if (!Arrays.equals(storePass, storePassAgain)) { + System.err.println + (rb.getString("They.don.t.match.Try.again")); + storePass = null; + } + } + + count++; + } while ((storePass == null) && count < 3); + + + if (storePass == null) { + System.err.println + (rb.getString("Too.many.failures.try.later")); + return; + } + } else if (!protectedPath + && !KeyStoreUtil.isWindowsKeyStore(storetype) + && isKeyStoreRelated(command)) { + // here we have EXPORTCERT and LIST (info valid until STOREPASSWD) + if (command != PRINTCRL) { + System.err.print(rb.getString("Enter.keystore.password.")); + System.err.flush(); + storePass = Password.readPassword(System.in); + passwords.add(storePass); + } + } + + // Now load a nullStream-based keystore, + // or verify the integrity of an input stream-based keystore + if (nullStream) { + keyStore.load(null, storePass); + } else if (ksStream != null) { + ksStream = new FileInputStream(ksfile); + keyStore.load(ksStream, storePass); + ksStream.close(); + } + } + + if (storePass != null && P12KEYSTORE.equalsIgnoreCase(storetype)) { + MessageFormat form = new MessageFormat(rb.getString( + "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.")); + if (keyPass != null && !Arrays.equals(storePass, keyPass)) { + Object[] source = {"-keypass"}; + System.err.println(form.format(source)); + keyPass = storePass; + } + if (newPass != null && !Arrays.equals(storePass, newPass)) { + Object[] source = {"-new"}; + System.err.println(form.format(source)); + newPass = storePass; + } + if (destKeyPass != null && !Arrays.equals(storePass, destKeyPass)) { + Object[] source = {"-destkeypass"}; + System.err.println(form.format(source)); + destKeyPass = storePass; + } + } + + // Create a certificate factory + if (command == PRINTCERT || command == IMPORTCERT + || command == IDENTITYDB || command == PRINTCRL) { + cf = CertificateFactory.getInstance("X509"); + } + + if (trustcacerts) { + caks = KeyStoreUtil.getCacertsKeyStore(); + } + + // Perform the specified command + if (command == CERTREQ) { + if (filename != null) { + try (PrintStream ps = new PrintStream(new FileOutputStream + (filename))) { + doCertReq(alias, sigAlgName, ps); + } + } else { + doCertReq(alias, sigAlgName, out); + } + if (verbose && filename != null) { + MessageFormat form = new MessageFormat(rb.getString + ("Certification.request.stored.in.file.filename.")); + Object[] source = {filename}; + System.err.println(form.format(source)); + System.err.println(rb.getString("Submit.this.to.your.CA")); + } + } else if (command == DELETE) { + doDeleteEntry(alias); + kssave = true; + } else if (command == EXPORTCERT) { + if (filename != null) { + try (PrintStream ps = new PrintStream(new FileOutputStream + (filename))) { + doExportCert(alias, ps); + } + } else { + doExportCert(alias, out); + } + if (filename != null) { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.stored.in.file.filename.")); + Object[] source = {filename}; + System.err.println(form.format(source)); + } + } else if (command == GENKEYPAIR) { + if (keyAlgName == null) { + keyAlgName = "DSA"; + } + doGenKeyPair(alias, dname, keyAlgName, keysize, sigAlgName); + kssave = true; + } else if (command == GENSECKEY) { + if (keyAlgName == null) { + keyAlgName = "DES"; + } + doGenSecretKey(alias, keyAlgName, keysize); + kssave = true; + } else if (command == IMPORTPASS) { + if (keyAlgName == null) { + keyAlgName = "PBE"; + } + // password is stored as a secret key + doGenSecretKey(alias, keyAlgName, keysize); + kssave = true; + } else if (command == IDENTITYDB) { + if (filename != null) { + try (InputStream inStream = new FileInputStream(filename)) { + doImportIdentityDatabase(inStream); + } + } else { + doImportIdentityDatabase(System.in); + } + } else if (command == IMPORTCERT) { + InputStream inStream = System.in; + if (filename != null) { + inStream = new FileInputStream(filename); + } + String importAlias = (alias!=null)?alias:keyAlias; + try { + if (keyStore.entryInstanceOf( + importAlias, PrivateKeyEntry.class)) { + kssave = installReply(importAlias, inStream); + if (kssave) { + System.err.println(rb.getString + ("Certificate.reply.was.installed.in.keystore")); + } else { + System.err.println(rb.getString + ("Certificate.reply.was.not.installed.in.keystore")); + } + } else if (!keyStore.containsAlias(importAlias) || + keyStore.entryInstanceOf(importAlias, + TrustedCertificateEntry.class)) { + kssave = addTrustedCert(importAlias, inStream); + if (kssave) { + System.err.println(rb.getString + ("Certificate.was.added.to.keystore")); + } else { + System.err.println(rb.getString + ("Certificate.was.not.added.to.keystore")); + } + } + } finally { + if (inStream != System.in) { + inStream.close(); + } + } + } else if (command == IMPORTKEYSTORE) { + doImportKeyStore(); + kssave = true; + } else if (command == KEYCLONE) { + keyPassNew = newPass; + + // added to make sure only key can go thru + if (alias == null) { + alias = keyAlias; + } + if (keyStore.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + if (!keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) { + MessageFormat form = new MessageFormat(rb.getString( + "Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + doCloneEntry(alias, dest, true); // Now everything can be cloned + kssave = true; + } else if (command == CHANGEALIAS) { + if (alias == null) { + alias = keyAlias; + } + doCloneEntry(alias, dest, false); + // in PKCS11, clone a PrivateKeyEntry will delete the old one + if (keyStore.containsAlias(alias)) { + doDeleteEntry(alias); + } + kssave = true; + } else if (command == KEYPASSWD) { + keyPassNew = newPass; + doChangeKeyPasswd(alias); + kssave = true; + } else if (command == LIST) { + if (alias != null) { + doPrintEntry(alias, out, true); + } else { + doPrintEntries(out); + } + } else if (command == PRINTCERT) { + doPrintCert(out); + } else if (command == SELFCERT) { + doSelfCert(alias, dname, sigAlgName); + kssave = true; + } else if (command == STOREPASSWD) { + storePassNew = newPass; + if (storePassNew == null) { + storePassNew = getNewPasswd("keystore password", storePass); + } + kssave = true; + } else if (command == GENCERT) { + if (alias == null) { + alias = keyAlias; + } + InputStream inStream = System.in; + if (infilename != null) { + inStream = new FileInputStream(infilename); + } + PrintStream ps = null; + if (outfilename != null) { + ps = new PrintStream(new FileOutputStream(outfilename)); + out = ps; + } + try { + doGenCert(alias, sigAlgName, inStream, out); + } finally { + if (inStream != System.in) { + inStream.close(); + } + if (ps != null) { + ps.close(); + } + } + } else if (command == GENCRL) { + if (alias == null) { + alias = keyAlias; + } + if (filename != null) { + try (PrintStream ps = + new PrintStream(new FileOutputStream(filename))) { + doGenCRL(ps); + } + } else { + doGenCRL(out); + } + } else if (command == PRINTCERTREQ) { + if (filename != null) { + try (InputStream inStream = new FileInputStream(filename)) { + doPrintCertReq(inStream, out); + } + } else { + doPrintCertReq(System.in, out); + } + } else if (command == PRINTCRL) { + doPrintCRL(filename, out); + } + + // If we need to save the keystore, do so. + if (kssave) { + if (verbose) { + MessageFormat form = new MessageFormat + (rb.getString(".Storing.ksfname.")); + Object[] source = {nullStream ? "keystore" : ksfname}; + System.err.println(form.format(source)); + } + + if (token) { + keyStore.store(null, null); + } else { + char[] pass = (storePassNew!=null) ? storePassNew : storePass; + if (nullStream) { + keyStore.store(null, pass); + } else { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + keyStore.store(bout, pass); + try (FileOutputStream fout = new FileOutputStream(ksfname)) { + fout.write(bout.toByteArray()); + } + } + } + } + } + + /** + * Generate a certificate: Read PKCS10 request from in, and print + * certificate to out. Use alias as CA, sigAlgName as the signature + * type. + */ + private void doGenCert(String alias, String sigAlgName, InputStream in, PrintStream out) + throws Exception { + + + Certificate signerCert = keyStore.getCertificate(alias); + byte[] encoded = signerCert.getEncoded(); + X509CertImpl signerCertImpl = new X509CertImpl(encoded); + X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + X500Name issuer = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." + + X509CertInfo.DN_NAME); + + Date firstDate = getStartDate(startDate); + Date lastDate = new Date(); + lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); + CertificateValidity interval = new CertificateValidity(firstDate, + lastDate); + + PrivateKey privateKey = + (PrivateKey)recoverKey(alias, storePass, keyPass).fst; + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); + } + Signature signature = Signature.getInstance(sigAlgName); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privateKey); + + SignatureUtil.initSignWithParam(signature, privateKey, params, null); + + X509CertInfo info = new X509CertInfo(); + AlgorithmId algID = AlgorithmId.getWithParameterSpec(sigAlgName, params); + info.set(X509CertInfo.VALIDITY, interval); + info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( + new Random().nextInt() & 0x7fffffff)); + info.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V3)); + info.set(X509CertInfo.ALGORITHM_ID, + new CertificateAlgorithmId(algID)); + info.set(X509CertInfo.ISSUER, issuer); + + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + boolean canRead = false; + StringBuffer sb = new StringBuffer(); + while (true) { + String s = reader.readLine(); + if (s == null) break; + // OpenSSL does not use NEW + //if (s.startsWith("-----BEGIN NEW CERTIFICATE REQUEST-----")) { + if (s.startsWith("-----BEGIN") && s.indexOf("REQUEST") >= 0) { + canRead = true; + //} else if (s.startsWith("-----END NEW CERTIFICATE REQUEST-----")) { + } else if (s.startsWith("-----END") && s.indexOf("REQUEST") >= 0) { + break; + } else if (canRead) { + sb.append(s); + } + } + byte[] rawReq = Base64.getMimeDecoder().decode(new String(sb)); + PKCS10 req = new PKCS10(rawReq); + + info.set(X509CertInfo.KEY, new CertificateX509Key(req.getSubjectPublicKeyInfo())); + info.set(X509CertInfo.SUBJECT, + dname==null?req.getSubjectName():new X500Name(dname)); + CertificateExtensions reqex = null; + Iterator attrs = req.getAttributes().getAttributes().iterator(); + while (attrs.hasNext()) { + PKCS10Attribute attr = attrs.next(); + if (attr.getAttributeId().equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) { + reqex = (CertificateExtensions)attr.getAttributeValue(); + } + } + CertificateExtensions ext = createV3Extensions( + reqex, + null, + v3ext, + req.getSubjectPublicKeyInfo(), + signerCert.getPublicKey()); + info.set(X509CertInfo.EXTENSIONS, ext); + X509CertImpl cert = new X509CertImpl(info); + cert.sign(privateKey, params, sigAlgName, null); + dumpCert(cert, out); + for (Certificate ca: keyStore.getCertificateChain(alias)) { + if (ca instanceof X509Certificate) { + X509Certificate xca = (X509Certificate)ca; + if (!isSelfSigned(xca)) { + dumpCert(xca, out); + } + } + } + } + + private void doGenCRL(PrintStream out) + throws Exception { + if (ids == null) { + throw new Exception("Must provide -id when -gencrl"); + } + Certificate signerCert = keyStore.getCertificate(alias); + byte[] encoded = signerCert.getEncoded(); + X509CertImpl signerCertImpl = new X509CertImpl(encoded); + X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." + + X509CertInfo.DN_NAME); + + Date firstDate = getStartDate(startDate); + Date lastDate = (Date) firstDate.clone(); + lastDate.setTime(lastDate.getTime() + validity*1000*24*60*60); + CertificateValidity interval = new CertificateValidity(firstDate, + lastDate); + + + PrivateKey privateKey = + (PrivateKey)recoverKey(alias, storePass, keyPass).fst; + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); + } + + X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()]; + for (int i=0; i= 0) { + CRLExtensions ext = new CRLExtensions(); + ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1)))); + badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)), + firstDate, ext); + } else { + badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate); + } + } + X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts); + crl.sign(privateKey, sigAlgName); + if (rfc) { + out.println("-----BEGIN X509 CRL-----"); + out.println(Base64.getMimeEncoder().encodeToString(crl.getEncodedInternal())); + out.println("-----END X509 CRL-----"); + } else { + out.write(crl.getEncodedInternal()); + } + } + + /** + * Creates a PKCS#10 cert signing request, corresponding to the + * keys (and name) associated with a given alias. + */ + private void doCertReq(String alias, String sigAlgName, PrintStream out) + throws Exception + { + if (alias == null) { + alias = keyAlias; + } + + Pair objs = recoverKey(alias, storePass, keyPass); + PrivateKey privKey = (PrivateKey)objs.fst; + if (keyPass == null) { + keyPass = objs.snd; + } + + Certificate cert = keyStore.getCertificate(alias); + if (cert == null) { + MessageFormat form = new MessageFormat + (rb.getString("alias.has.no.public.key.certificate.")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + PKCS10 request = new PKCS10(cert.getPublicKey()); + CertificateExtensions ext = createV3Extensions(null, null, v3ext, cert.getPublicKey(), null); + // Attribute name is not significant + request.getAttributes().setAttribute(X509CertInfo.EXTENSIONS, + new PKCS10Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext)); + + // Construct a Signature object, so that we can sign the request + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm()); + } + + Signature signature = Signature.getInstance(sigAlgName); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); + SignatureUtil.initSignWithParam(signature, privKey, params, null); + + X500Name subject = dname == null? + new X500Name(((X509Certificate)cert).getSubjectDN().toString()): + new X500Name(dname); + + // Sign the request and base-64 encode it + request.encodeAndSign(subject, signature); + request.print(out); + } + + /** + * Deletes an entry from the keystore. + */ + private void doDeleteEntry(String alias) throws Exception { + if (keyStore.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + keyStore.deleteEntry(alias); + } + + /** + * Exports a certificate from the keystore. + */ + private void doExportCert(String alias, PrintStream out) + throws Exception + { + if (storePass == null + && !KeyStoreUtil.isWindowsKeyStore(storetype)) { + printWarning(); + } + if (alias == null) { + alias = keyAlias; + } + if (keyStore.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + X509Certificate cert = (X509Certificate)keyStore.getCertificate(alias); + if (cert == null) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.has.no.certificate")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + dumpCert(cert, out); + } + + /** + * Prompt the user for a keypass when generating a key entry. + * @param alias the entry we will set password for + * @param orig the original entry of doing a dup, null if generate new + * @param origPass the password to copy from if user press ENTER + */ + private char[] promptForKeyPass(String alias, String orig, char[] origPass) throws Exception{ + if (P12KEYSTORE.equalsIgnoreCase(storetype)) { + return origPass; + } else if (!token && !protectedPath) { + // Prompt for key password + int count; + for (count = 0; count < 3; count++) { + MessageFormat form = new MessageFormat(rb.getString + ("Enter.key.password.for.alias.")); + Object[] source = {alias}; + System.err.println(form.format(source)); + if (orig == null) { + System.err.print(rb.getString + (".RETURN.if.same.as.keystore.password.")); + } else { + form = new MessageFormat(rb.getString + (".RETURN.if.same.as.for.otherAlias.")); + Object[] src = {orig}; + System.err.print(form.format(src)); + } + System.err.flush(); + char[] entered = Password.readPassword(System.in); + passwords.add(entered); + if (entered == null) { + return origPass; + } else if (entered.length >= 6) { + System.err.print(rb.getString("Re.enter.new.password.")); + char[] passAgain = Password.readPassword(System.in); + passwords.add(passAgain); + if (!Arrays.equals(entered, passAgain)) { + System.err.println + (rb.getString("They.don.t.match.Try.again")); + continue; + } + return entered; + } else { + System.err.println(rb.getString + ("Key.password.is.too.short.must.be.at.least.6.characters")); + } + } + if (count == 3) { + if (command == KEYCLONE) { + throw new Exception(rb.getString + ("Too.many.failures.Key.entry.not.cloned")); + } else { + throw new Exception(rb.getString + ("Too.many.failures.key.not.added.to.keystore")); + } + } + } + return null; // PKCS11, MSCAPI, or -protected + } + + /* + * Prompt the user for the password credential to be stored. + */ + private char[] promptForCredential() throws Exception { + // Handle password supplied via stdin + if (System.console() == null) { + char[] importPass = Password.readPassword(System.in); + passwords.add(importPass); + return importPass; + } + + int count; + for (count = 0; count < 3; count++) { + System.err.print( + rb.getString("Enter.the.password.to.be.stored.")); + System.err.flush(); + char[] entered = Password.readPassword(System.in); + passwords.add(entered); + System.err.print(rb.getString("Re.enter.password.")); + char[] passAgain = Password.readPassword(System.in); + passwords.add(passAgain); + if (!Arrays.equals(entered, passAgain)) { + System.err.println(rb.getString("They.don.t.match.Try.again")); + continue; + } + return entered; + } + + if (count == 3) { + throw new Exception(rb.getString + ("Too.many.failures.key.not.added.to.keystore")); + } + + return null; + } + + /** + * Creates a new secret key. + */ + private void doGenSecretKey(String alias, String keyAlgName, + int keysize) + throws Exception + { + if (alias == null) { + alias = keyAlias; + } + if (keyStore.containsAlias(alias)) { + MessageFormat form = new MessageFormat(rb.getString + ("Secret.key.not.generated.alias.alias.already.exists")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + // Use the keystore's default PBE algorithm for entry protection + boolean useDefaultPBEAlgorithm = true; + SecretKey secKey = null; + + if (keyAlgName.toUpperCase().startsWith("PBE")) { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); + + // User is prompted for PBE credential + secKey = + factory.generateSecret(new PBEKeySpec(promptForCredential())); + + // Check whether a specific PBE algorithm was specified + if (!"PBE".equalsIgnoreCase(keyAlgName)) { + useDefaultPBEAlgorithm = false; + } + + if (verbose) { + MessageFormat form = new MessageFormat(rb.getString( + "Generated.keyAlgName.secret.key")); + Object[] source = + {useDefaultPBEAlgorithm ? "PBE" : secKey.getAlgorithm()}; + System.err.println(form.format(source)); + } + } else { + KeyGenerator keygen = KeyGenerator.getInstance(keyAlgName); + if (keysize == -1) { + if ("DES".equalsIgnoreCase(keyAlgName)) { + keysize = 56; + } else if ("DESede".equalsIgnoreCase(keyAlgName)) { + keysize = 168; + } else { + throw new Exception(rb.getString + ("Please.provide.keysize.for.secret.key.generation")); + } + } + keygen.init(keysize); + secKey = keygen.generateKey(); + + if (verbose) { + MessageFormat form = new MessageFormat(rb.getString + ("Generated.keysize.bit.keyAlgName.secret.key")); + Object[] source = {new Integer(keysize), + secKey.getAlgorithm()}; + System.err.println(form.format(source)); + } + } + + if (keyPass == null) { + keyPass = promptForKeyPass(alias, null, storePass); + } + + if (useDefaultPBEAlgorithm) { + keyStore.setKeyEntry(alias, secKey, keyPass, null); + } else { + keyStore.setEntry(alias, new SecretKeyEntry(secKey), + new PasswordProtection(keyPass, keyAlgName, null)); + } + } + + /** + * If no signature algorithm was specified at the command line, + * we choose one that is compatible with the selected private key + */ + private static String getCompatibleSigAlgName(String keyAlgName) + throws Exception { + if ("DSA".equalsIgnoreCase(keyAlgName)) { + return "SHA1WithDSA"; + } else if ("RSA".equalsIgnoreCase(keyAlgName)) { + return "SHA256WithRSA"; + } else if ("EC".equalsIgnoreCase(keyAlgName)) { + return "SHA256withECDSA"; + } else { + throw new Exception(rb.getString + ("Cannot.derive.signature.algorithm")); + } + } + /** + * Creates a new key pair and self-signed certificate. + */ + private void doGenKeyPair(String alias, String dname, String keyAlgName, + int keysize, String sigAlgName) + throws Exception + { + if (keysize == -1) { + if ("EC".equalsIgnoreCase(keyAlgName)) { + keysize = 256; + } else if ("RSA".equalsIgnoreCase(keyAlgName)) { + keysize = 2048; + } else { + keysize = 1024; + } + } + + if (alias == null) { + alias = keyAlias; + } + + if (keyStore.containsAlias(alias)) { + MessageFormat form = new MessageFormat(rb.getString + ("Key.pair.not.generated.alias.alias.already.exists")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(keyAlgName); + } + CertAndKeyGen keypair = + new CertAndKeyGen(keyAlgName, sigAlgName, providerName); + + + // If DN is provided, parse it. Otherwise, prompt the user for it. + X500Name x500Name; + if (dname == null) { + x500Name = getX500Name(); + } else { + x500Name = new X500Name(dname); + } + + keypair.generate(keysize); + PrivateKey privKey = keypair.getPrivateKey(); + + CertificateExtensions ext = createV3Extensions( + null, + null, + v3ext, + keypair.getPublicKeyAnyway(), + null); + + X509Certificate[] chain = new X509Certificate[1]; + chain[0] = keypair.getSelfCertificate( + x500Name, getStartDate(startDate), validity*24L*60L*60L, ext); + + if (verbose) { + MessageFormat form = new MessageFormat(rb.getString + ("Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for")); + Object[] source = {new Integer(keysize), + privKey.getAlgorithm(), + chain[0].getSigAlgName(), + new Long(validity), + x500Name}; + System.err.println(form.format(source)); + } + + if (keyPass == null) { + keyPass = promptForKeyPass(alias, null, storePass); + } + keyStore.setKeyEntry(alias, privKey, keyPass, chain); + } + + /** + * Clones an entry + * @param orig original alias + * @param dest destination alias + * @changePassword if the password can be changed + */ + private void doCloneEntry(String orig, String dest, boolean changePassword) + throws Exception + { + if (orig == null) { + orig = keyAlias; + } + + if (keyStore.containsAlias(dest)) { + MessageFormat form = new MessageFormat + (rb.getString("Destination.alias.dest.already.exists")); + Object[] source = {dest}; + throw new Exception(form.format(source)); + } + + Pair objs = recoverEntry(keyStore, orig, storePass, keyPass); + Entry entry = objs.fst; + keyPass = objs.snd; + + PasswordProtection pp = null; + + if (keyPass != null) { // protected + if (!changePassword || P12KEYSTORE.equalsIgnoreCase(storetype)) { + keyPassNew = keyPass; + } else { + if (keyPassNew == null) { + keyPassNew = promptForKeyPass(dest, orig, keyPass); + } + } + pp = new PasswordProtection(keyPassNew); + } + keyStore.setEntry(dest, entry, pp); + } + + /** + * Changes a key password. + */ + private void doChangeKeyPasswd(String alias) throws Exception + { + + if (alias == null) { + alias = keyAlias; + } + Pair objs = recoverKey(alias, storePass, keyPass); + Key privKey = objs.fst; + if (keyPass == null) { + keyPass = objs.snd; + } + + if (keyPassNew == null) { + MessageFormat form = new MessageFormat + (rb.getString("key.password.for.alias.")); + Object[] source = {alias}; + keyPassNew = getNewPasswd(form.format(source), keyPass); + } + keyStore.setKeyEntry(alias, privKey, keyPassNew, + keyStore.getCertificateChain(alias)); + } + + /** + * Imports a JDK 1.1-style identity database. We can only store one + * certificate per identity, because we use the identity's name as the + * alias (which references a keystore entry), and aliases must be unique. + */ + private void doImportIdentityDatabase(InputStream in) + throws Exception + { + System.err.println(rb.getString + ("No.entries.from.identity.database.added")); + } + + /** + * Prints a single keystore entry. + */ + private void doPrintEntry(String alias, PrintStream out, + boolean printWarning) + throws Exception + { + if (storePass == null && printWarning + && !KeyStoreUtil.isWindowsKeyStore(storetype)) { + printWarning(); + } + + if (keyStore.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + if (verbose || rfc || debug) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.name.alias")); + Object[] source = {alias}; + out.println(form.format(source)); + + if (!token) { + form = new MessageFormat(rb.getString + ("Creation.date.keyStore.getCreationDate.alias.")); + Object[] src = {keyStore.getCreationDate(alias)}; + out.println(form.format(src)); + } + } else { + if (!token) { + MessageFormat form = new MessageFormat + (rb.getString("alias.keyStore.getCreationDate.alias.")); + Object[] source = {alias, keyStore.getCreationDate(alias)}; + out.print(form.format(source)); + } else { + MessageFormat form = new MessageFormat + (rb.getString("alias.")); + Object[] source = {alias}; + out.print(form.format(source)); + } + } + + if (keyStore.entryInstanceOf(alias, SecretKeyEntry.class)) { + if (verbose || rfc || debug) { + Object[] source = {"SecretKeyEntry"}; + out.println(new MessageFormat( + rb.getString("Entry.type.type.")).format(source)); + } else { + out.println("SecretKeyEntry, "); + } + } else if (keyStore.entryInstanceOf(alias, PrivateKeyEntry.class)) { + if (verbose || rfc || debug) { + Object[] source = {"PrivateKeyEntry"}; + out.println(new MessageFormat( + rb.getString("Entry.type.type.")).format(source)); + } else { + out.println("PrivateKeyEntry, "); + } + + // Get the chain + Certificate[] chain = keyStore.getCertificateChain(alias); + if (chain != null) { + if (verbose || rfc || debug) { + out.println(rb.getString + ("Certificate.chain.length.") + chain.length); + for (int i = 0; i < chain.length; i ++) { + MessageFormat form = new MessageFormat + (rb.getString("Certificate.i.1.")); + Object[] source = {new Integer((i + 1))}; + out.println(form.format(source)); + if (verbose && (chain[i] instanceof X509Certificate)) { + printX509Cert((X509Certificate)(chain[i]), out); + } else if (debug) { + out.println(chain[i].toString()); + } else { + dumpCert(chain[i], out); + } + } + } else { + // Print the digest of the user cert only + out.println + (rb.getString("Certificate.fingerprint.SHA1.") + + getCertFingerPrint("SHA1", chain[0])); + } + } + } else if (keyStore.entryInstanceOf(alias, + TrustedCertificateEntry.class)) { + // We have a trusted certificate entry + Certificate cert = keyStore.getCertificate(alias); + Object[] source = {"trustedCertEntry"}; + String mf = new MessageFormat( + rb.getString("Entry.type.type.")).format(source) + "\n"; + if (verbose && (cert instanceof X509Certificate)) { + out.println(mf); + printX509Cert((X509Certificate)cert, out); + } else if (rfc) { + out.println(mf); + dumpCert(cert, out); + } else if (debug) { + out.println(cert.toString()); + } else { + out.println("trustedCertEntry, "); + out.println(rb.getString("Certificate.fingerprint.SHA1.") + + getCertFingerPrint("SHA1", cert)); + } + } else { + out.println(rb.getString("Unknown.Entry.Type")); + } + } + + /** + * Load the srckeystore from a stream, used in -importkeystore + * @returns the src KeyStore + */ + KeyStore loadSourceKeyStore() throws Exception { + boolean isPkcs11 = false; + + InputStream is = null; + + if (P11KEYSTORE.equalsIgnoreCase(srcstoretype) || + KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { + if (!NONE.equals(srcksfname)) { + System.err.println(MessageFormat.format(rb.getString + (".keystore.must.be.NONE.if.storetype.is.{0}"), srcstoretype)); + System.err.println(); + tinyHelp(); + } + isPkcs11 = true; + } else { + if (srcksfname != null) { + File srcksfile = new File(srcksfname); + if (srcksfile.exists() && srcksfile.length() == 0) { + throw new Exception(rb.getString + ("Source.keystore.file.exists.but.is.empty.") + + srcksfname); + } + is = new FileInputStream(srcksfile); + } else { + throw new Exception(rb.getString + ("Please.specify.srckeystore")); + } + } + + KeyStore store; + try { + if (srcProviderName == null) { + store = KeyStore.getInstance(srcstoretype); + } else { + store = KeyStore.getInstance(srcstoretype, srcProviderName); + } + + if (srcstorePass == null + && !srcprotectedPath + && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { + System.err.print(rb.getString("Enter.source.keystore.password.")); + System.err.flush(); + srcstorePass = Password.readPassword(System.in); + passwords.add(srcstorePass); + } + + // always let keypass be storepass when using pkcs12 + if (P12KEYSTORE.equalsIgnoreCase(srcstoretype)) { + if (srckeyPass != null && srcstorePass != null && + !Arrays.equals(srcstorePass, srckeyPass)) { + MessageFormat form = new MessageFormat(rb.getString( + "Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.")); + Object[] source = {"-srckeypass"}; + System.err.println(form.format(source)); + srckeyPass = srcstorePass; + } + } + + store.load(is, srcstorePass); // "is" already null in PKCS11 + } finally { + if (is != null) { + is.close(); + } + } + + if (srcstorePass == null + && !KeyStoreUtil.isWindowsKeyStore(srcstoretype)) { + // anti refactoring, copied from printWarning(), + // but change 2 lines + System.err.println(); + System.err.println(rb.getString + (".WARNING.WARNING.WARNING.")); + System.err.println(rb.getString + (".The.integrity.of.the.information.stored.in.the.srckeystore.")); + System.err.println(rb.getString + (".WARNING.WARNING.WARNING.")); + System.err.println(); + } + + return store; + } + + /** + * import all keys and certs from importkeystore. + * keep alias unchanged if no name conflict, otherwise, prompt. + * keep keypass unchanged for keys + */ + private void doImportKeyStore() throws Exception { + + if (alias != null) { + doImportKeyStoreSingle(loadSourceKeyStore(), alias); + } else { + if (dest != null || srckeyPass != null) { + throw new Exception(rb.getString( + "if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified")); + } + doImportKeyStoreAll(loadSourceKeyStore()); + } + /* + * Information display rule of -importkeystore + * 1. inside single, shows failure + * 2. inside all, shows sucess + * 3. inside all where there is a failure, prompt for continue + * 4. at the final of all, shows summary + */ + } + + /** + * Import a single entry named alias from srckeystore + * @returns 1 if the import action succeed + * 0 if user choose to ignore an alias-dumplicated entry + * 2 if setEntry throws Exception + */ + private int doImportKeyStoreSingle(KeyStore srckeystore, String alias) + throws Exception { + + String newAlias = (dest==null) ? alias : dest; + + if (keyStore.containsAlias(newAlias)) { + Object[] source = {alias}; + if (noprompt) { + System.err.println(new MessageFormat(rb.getString( + "Warning.Overwriting.existing.alias.alias.in.destination.keystore")).format(source)); + } else { + String reply = getYesNoReply(new MessageFormat(rb.getString( + "Existing.entry.alias.alias.exists.overwrite.no.")).format(source)); + if ("NO".equals(reply)) { + newAlias = inputStringFromStdin(rb.getString + ("Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.")); + if ("".equals(newAlias)) { + System.err.println(new MessageFormat(rb.getString( + "Entry.for.alias.alias.not.imported.")).format( + source)); + return 0; + } + } + } + } + + Pair objs = recoverEntry(srckeystore, alias, srcstorePass, srckeyPass); + Entry entry = objs.fst; + + PasswordProtection pp = null; + + // According to keytool.html, "The destination entry will be protected + // using destkeypass. If destkeypass is not provided, the destination + // entry will be protected with the source entry password." + // so always try to protect with destKeyPass. + char[] newPass = null; + if (destKeyPass != null) { + newPass = destKeyPass; + pp = new PasswordProtection(destKeyPass); + } else if (objs.snd != null) { + newPass = objs.snd; + pp = new PasswordProtection(objs.snd); + } + + try { + keyStore.setEntry(newAlias, entry, pp); + // Place the check so that only successful imports are blocked. + // For example, we don't block a failed SecretEntry import. + if (P12KEYSTORE.equalsIgnoreCase(storetype)) { + if (newPass != null && !Arrays.equals(newPass, storePass)) { + throw new Exception(rb.getString( + "The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.")); + } + } + return 1; + } catch (KeyStoreException kse) { + Object[] source2 = {alias, kse.toString()}; + MessageFormat form = new MessageFormat(rb.getString( + "Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.")); + System.err.println(form.format(source2)); + return 2; + } + } + + private void doImportKeyStoreAll(KeyStore srckeystore) throws Exception { + + int ok = 0; + int count = srckeystore.size(); + for (Enumeration e = srckeystore.aliases(); + e.hasMoreElements(); ) { + String alias = e.nextElement(); + int result = doImportKeyStoreSingle(srckeystore, alias); + if (result == 1) { + ok++; + Object[] source = {alias}; + MessageFormat form = new MessageFormat(rb.getString("Entry.for.alias.alias.successfully.imported.")); + System.err.println(form.format(source)); + } else if (result == 2) { + if (!noprompt) { + String reply = getYesNoReply("Do you want to quit the import process? [no]: "); + if ("YES".equals(reply)) { + break; + } + } + } + } + Object[] source = {ok, count-ok}; + MessageFormat form = new MessageFormat(rb.getString( + "Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled")); + System.err.println(form.format(source)); + } + + /** + * Prints all keystore entries. + */ + private void doPrintEntries(PrintStream out) + throws Exception + { + if (storePass == null + && !KeyStoreUtil.isWindowsKeyStore(storetype)) { + printWarning(); + } else { + out.println(); + } + + out.println(rb.getString("Keystore.type.") + keyStore.getType()); + out.println(rb.getString("Keystore.provider.") + + keyStore.getProvider().getName()); + out.println(); + + MessageFormat form; + form = (keyStore.size() == 1) ? + new MessageFormat(rb.getString + ("Your.keystore.contains.keyStore.size.entry")) : + new MessageFormat(rb.getString + ("Your.keystore.contains.keyStore.size.entries")); + Object[] source = {new Integer(keyStore.size())}; + out.println(form.format(source)); + out.println(); + + for (Enumeration e = keyStore.aliases(); + e.hasMoreElements(); ) { + String alias = e.nextElement(); + doPrintEntry(alias, out, false); + if (verbose || rfc) { + out.println(rb.getString("NEWLINE")); + out.println(rb.getString + ("STAR")); + out.println(rb.getString + ("STARNN")); + } + } + } + + private static Iterable e2i(final Enumeration e) { + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + @Override + public boolean hasNext() { + return e.hasMoreElements(); + } + @Override + public T next() { + return e.nextElement(); + } + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + }; + } + }; + } + + /** + * Loads CRLs from a source. This method is also called in JarSigner. + * @param src the source, which means System.in if null, or a URI, + * or a bare file path name + */ + public static Collection loadCRLs(String src) throws Exception { + InputStream in = null; + URI uri = null; + if (src == null) { + in = System.in; + } else { + try { + uri = new URI(src); + if (uri.getScheme().equals("ldap")) { + // No input stream for LDAP + } else { + in = uri.toURL().openStream(); + } + } catch (Exception e) { + try { + in = new FileInputStream(src); + } catch (Exception e2) { + if (uri == null || uri.getScheme() == null) { + throw e2; // More likely a bare file path + } else { + throw e; // More likely a protocol or network problem + } + } + } + } + if (in != null) { + try { + // Read the full stream before feeding to X509Factory, + // otherwise, keytool -gencrl | keytool -printcrl + // might not work properly, since -gencrl is slow + // and there's no data in the pipe at the beginning. + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] b = new byte[4096]; + while (true) { + int len = in.read(b); + if (len < 0) break; + bout.write(b, 0, len); + } + return CertificateFactory.getInstance("X509").generateCRLs( + new ByteArrayInputStream(bout.toByteArray())); + } finally { + if (in != System.in) { + in.close(); + } + } + } else { // must be LDAP, and uri is not null + // Lazily load LDAPCertStoreHelper if present + CertStoreHelper helper = CertStoreHelper.getInstance("LDAP"); + String path = uri.getPath(); + if (path.charAt(0) == '/') path = path.substring(1); + CertStore s = helper.getCertStore(uri); + X509CRLSelector sel = + helper.wrap(new X509CRLSelector(), null, path); + return s.getCRLs(sel); + } + } + + /** + * Returns CRLs described in a X509Certificate's CRLDistributionPoints + * Extension. Only those containing a general name of type URI are read. + */ + public static List readCRLsFromCert(X509Certificate cert) + throws Exception { + List crls = new ArrayList<>(); + CRLDistributionPointsExtension ext = + X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension(); + if (ext == null) return crls; + List distPoints = + ext.get(CRLDistributionPointsExtension.POINTS); + for (DistributionPoint o: distPoints) { + GeneralNames names = o.getFullName(); + if (names != null) { + for (GeneralName name: names.names()) { + if (name.getType() == GeneralNameInterface.NAME_URI) { + URIName uriName = (URIName)name.getName(); + for (CRL crl: loadCRLs(uriName.getName())) { + if (crl instanceof X509CRL) { + crls.add((X509CRL)crl); + } + } + break; // Different name should point to same CRL + } + } + } + } + return crls; + } + + private static String verifyCRL(KeyStore ks, CRL crl) + throws Exception { + X509CRLImpl xcrl = (X509CRLImpl)crl; + X500Principal issuer = xcrl.getIssuerX500Principal(); + for (String s: e2i(ks.aliases())) { + Certificate cert = ks.getCertificate(s); + if (cert instanceof X509Certificate) { + X509Certificate xcert = (X509Certificate)cert; + if (xcert.getSubjectX500Principal().equals(issuer)) { + try { + ((X509CRLImpl)crl).verify(cert.getPublicKey()); + return s; + } catch (Exception e) { + } + } + } + } + return null; + } + + private void doPrintCRL(String src, PrintStream out) + throws Exception { + for (CRL crl: loadCRLs(src)) { + printCRL(crl, out); + String issuer = null; + if (caks != null) { + issuer = verifyCRL(caks, crl); + if (issuer != null) { + out.printf(rb.getString( + "verified.by.s.in.s"), issuer, "cacerts"); + out.println(); + } + } + if (issuer == null && keyStore != null) { + issuer = verifyCRL(keyStore, crl); + if (issuer != null) { + out.printf(rb.getString( + "verified.by.s.in.s"), issuer, "keystore"); + out.println(); + } + } + if (issuer == null) { + out.println(rb.getString + ("STAR")); + out.println(rb.getString + ("warning.not.verified.make.sure.keystore.is.correct")); + out.println(rb.getString + ("STARNN")); + } + } + } + + private void printCRL(CRL crl, PrintStream out) + throws Exception { + if (rfc) { + X509CRL xcrl = (X509CRL)crl; + out.println("-----BEGIN X509 CRL-----"); + out.println(Base64.getMimeEncoder().encodeToString(xcrl.getEncoded())); + out.println("-----END X509 CRL-----"); + } else { + out.println(crl.toString()); + } + } + + private void doPrintCertReq(InputStream in, PrintStream out) + throws Exception { + + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuffer sb = new StringBuffer(); + boolean started = false; + while (true) { + String s = reader.readLine(); + if (s == null) break; + if (!started) { + if (s.startsWith("-----")) { + started = true; + } + } else { + if (s.startsWith("-----")) { + break; + } + sb.append(s); + } + } + PKCS10 req = new PKCS10(Base64.getMimeDecoder().decode(new String(sb))); + + PublicKey pkey = req.getSubjectPublicKeyInfo(); + out.printf(rb.getString("PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key."), + req.getSubjectName(), pkey.getFormat(), pkey.getAlgorithm()); + for (PKCS10Attribute attr: req.getAttributes().getAttributes()) { + ObjectIdentifier oid = attr.getAttributeId(); + if (oid.equals((Object)PKCS9Attribute.EXTENSION_REQUEST_OID)) { + CertificateExtensions exts = (CertificateExtensions)attr.getAttributeValue(); + if (exts != null) { + printExtensions(rb.getString("Extension.Request."), exts, out); + } + } else { + out.println("Attribute: " + attr.getAttributeId()); + PKCS9Attribute pkcs9Attr = + new PKCS9Attribute(attr.getAttributeId(), + attr.getAttributeValue()); + out.print(pkcs9Attr.getName() + ": "); + Object attrVal = attr.getAttributeValue(); + out.println(attrVal instanceof String[] ? + Arrays.toString((String[]) attrVal) : + attrVal); + } + } + if (debug) { + out.println(req); // Just to see more, say, public key length... + } + } + + /** + * Reads a certificate (or certificate chain) and prints its contents in + * a human readable format. + */ + private void printCertFromStream(InputStream in, PrintStream out) + throws Exception + { + Collection c = null; + try { + c = cf.generateCertificates(in); + } catch (CertificateException ce) { + throw new Exception(rb.getString("Failed.to.parse.input"), ce); + } + if (c.isEmpty()) { + throw new Exception(rb.getString("Empty.input")); + } + Certificate[] certs = c.toArray(new Certificate[c.size()]); + for (int i=0; i 1) { + MessageFormat form = new MessageFormat + (rb.getString("Certificate.i.1.")); + Object[] source = {new Integer(i + 1)}; + out.println(form.format(source)); + } + if (rfc) + dumpCert(x509Cert, out); + else + printX509Cert(x509Cert, out); + if (i < (certs.length-1)) { + out.println(); + } + } + } + + private void doPrintCert(final PrintStream out) throws Exception { + if (jarfile != null) { + JarFile jf = new JarFile(jarfile, true); + Enumeration entries = jf.entries(); + Set ss = new HashSet<>(); + byte[] buffer = new byte[8192]; + int pos = 0; + while (entries.hasMoreElements()) { + JarEntry je = entries.nextElement(); + try (InputStream is = jf.getInputStream(je)) { + while (is.read(buffer) != -1) { + // we just read. this will throw a SecurityException + // if a signature/digest check fails. This also + // populate the signers + } + } + CodeSigner[] signers = je.getCodeSigners(); + if (signers != null) { + for (CodeSigner signer: signers) { + if (!ss.contains(signer)) { + ss.add(signer); + out.printf(rb.getString("Signer.d."), ++pos); + out.println(); + out.println(); + out.println(rb.getString("Signature.")); + out.println(); + for (Certificate cert: signer.getSignerCertPath().getCertificates()) { + X509Certificate x = (X509Certificate)cert; + if (rfc) { + out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n"); + dumpCert(x, out); + } else { + printX509Cert(x, out); + } + out.println(); + } + Timestamp ts = signer.getTimestamp(); + if (ts != null) { + out.println(rb.getString("Timestamp.")); + out.println(); + for (Certificate cert: ts.getSignerCertPath().getCertificates()) { + X509Certificate x = (X509Certificate)cert; + if (rfc) { + out.println(rb.getString("Certificate.owner.") + x.getSubjectDN() + "\n"); + dumpCert(x, out); + } else { + printX509Cert(x, out); + } + out.println(); + } + } + } + } + } + } + jf.close(); + if (ss.isEmpty()) { + out.println(rb.getString("Not.a.signed.jar.file")); + } + } else if (sslserver != null) { + // Lazily load SSLCertStoreHelper if present + CertStoreHelper helper = CertStoreHelper.getInstance("SSLServer"); + CertStore cs = helper.getCertStore(new URI("https://" + sslserver)); + Collection chain; + try { + chain = cs.getCertificates(null); + if (chain.isEmpty()) { + // If the certs are not retrieved, we consider it an error + // even if the URL connection is successful. + throw new Exception(rb.getString( + "No.certificate.from.the.SSL.server")); + } + } catch (CertStoreException cse) { + if (cse.getCause() instanceof IOException) { + throw new Exception(rb.getString( + "No.certificate.from.the.SSL.server"), + cse.getCause()); + } else { + throw cse; + } + } + + int i = 0; + for (Certificate cert : chain) { + try { + if (rfc) { + dumpCert(cert, out); + } else { + out.println("Certificate #" + i++); + out.println("===================================="); + printX509Cert((X509Certificate)cert, out); + out.println(); + } + } catch (Exception e) { + if (debug) { + e.printStackTrace(); + } + } + } + } else { + if (filename != null) { + try (FileInputStream inStream = new FileInputStream(filename)) { + printCertFromStream(inStream, out); + } + } else { + printCertFromStream(System.in, out); + } + } + } + /** + * Creates a self-signed certificate, and stores it as a single-element + * certificate chain. + */ + private void doSelfCert(String alias, String dname, String sigAlgName) + throws Exception + { + if (alias == null) { + alias = keyAlias; + } + + Pair objs = recoverKey(alias, storePass, keyPass); + PrivateKey privKey = (PrivateKey)objs.fst; + if (keyPass == null) + keyPass = objs.snd; + + // Determine the signature algorithm + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(privKey.getAlgorithm()); + } + + // Get the old certificate + Certificate oldCert = keyStore.getCertificate(alias); + if (oldCert == null) { + MessageFormat form = new MessageFormat + (rb.getString("alias.has.no.public.key")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + if (!(oldCert instanceof X509Certificate)) { + MessageFormat form = new MessageFormat + (rb.getString("alias.has.no.X.509.certificate")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + // convert to X509CertImpl, so that we can modify selected fields + // (no public APIs available yet) + byte[] encoded = oldCert.getEncoded(); + X509CertImpl certImpl = new X509CertImpl(encoded); + X509CertInfo certInfo = (X509CertInfo)certImpl.get(X509CertImpl.NAME + + "." + + X509CertImpl.INFO); + + // Extend its validity + Date firstDate = getStartDate(startDate); + Date lastDate = new Date(); + lastDate.setTime(firstDate.getTime() + validity*1000L*24L*60L*60L); + CertificateValidity interval = new CertificateValidity(firstDate, + lastDate); + certInfo.set(X509CertInfo.VALIDITY, interval); + + // Make new serial number + certInfo.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( + new Random().nextInt() & 0x7fffffff)); + + // Set owner and issuer fields + X500Name owner; + if (dname == null) { + // Get the owner name from the certificate + owner = (X500Name)certInfo.get(X509CertInfo.SUBJECT + "." + + X509CertInfo.DN_NAME); + } else { + // Use the owner name specified at the command line + owner = new X500Name(dname); + certInfo.set(X509CertInfo.SUBJECT + "." + + X509CertInfo.DN_NAME, owner); + } + // Make issuer same as owner (self-signed!) + certInfo.set(X509CertInfo.ISSUER + "." + + X509CertInfo.DN_NAME, owner); + + // The inner and outer signature algorithms have to match. + // The way we achieve that is really ugly, but there seems to be no + // other solution: We first sign the cert, then retrieve the + // outer sigalg and use it to set the inner sigalg + X509CertImpl newCert = new X509CertImpl(certInfo); + AlgorithmParameterSpec params = AlgorithmId + .getDefaultAlgorithmParameterSpec(sigAlgName, privKey); + newCert.sign(privKey, params, sigAlgName, null); + AlgorithmId sigAlgid = (AlgorithmId)newCert.get(X509CertImpl.SIG_ALG); + certInfo.set(CertificateAlgorithmId.NAME + "." + + CertificateAlgorithmId.ALGORITHM, sigAlgid); + + certInfo.set(X509CertInfo.VERSION, + new CertificateVersion(CertificateVersion.V3)); + + CertificateExtensions ext = createV3Extensions( + null, + (CertificateExtensions)certInfo.get(X509CertInfo.EXTENSIONS), + v3ext, + oldCert.getPublicKey(), + null); + certInfo.set(X509CertInfo.EXTENSIONS, ext); + // Sign the new certificate + newCert = new X509CertImpl(certInfo); + newCert.sign(privKey, params, sigAlgName, null); + + // Store the new certificate as a single-element certificate chain + keyStore.setKeyEntry(alias, privKey, + (keyPass != null) ? keyPass : storePass, + new Certificate[] { newCert } ); + + if (verbose) { + System.err.println(rb.getString("New.certificate.self.signed.")); + System.err.print(newCert.toString()); + System.err.println(); + } + } + + /** + * Processes a certificate reply from a certificate authority. + * + *

    Builds a certificate chain on top of the certificate reply, + * using trusted certificates from the keystore. The chain is complete + * after a self-signed certificate has been encountered. The self-signed + * certificate is considered a root certificate authority, and is stored + * at the end of the chain. + * + *

    The newly generated chain replaces the old chain associated with the + * key entry. + * + * @return true if the certificate reply was installed, otherwise false. + */ + private boolean installReply(String alias, InputStream in) + throws Exception + { + if (alias == null) { + alias = keyAlias; + } + + Pair objs = recoverKey(alias, storePass, keyPass); + PrivateKey privKey = (PrivateKey)objs.fst; + if (keyPass == null) { + keyPass = objs.snd; + } + + Certificate userCert = keyStore.getCertificate(alias); + if (userCert == null) { + MessageFormat form = new MessageFormat + (rb.getString("alias.has.no.public.key.certificate.")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + // Read the certificates in the reply + Collection c = cf.generateCertificates(in); + if (c.isEmpty()) { + throw new Exception(rb.getString("Reply.has.no.certificates")); + } + Certificate[] replyCerts = c.toArray(new Certificate[c.size()]); + Certificate[] newChain; + if (replyCerts.length == 1) { + // single-cert reply + newChain = establishCertChain(userCert, replyCerts[0]); + } else { + // cert-chain reply (e.g., PKCS#7) + newChain = validateReply(alias, userCert, replyCerts); + } + + // Now store the newly established chain in the keystore. The new + // chain replaces the old one. + if (newChain != null) { + keyStore.setKeyEntry(alias, privKey, + (keyPass != null) ? keyPass : storePass, + newChain); + return true; + } else { + return false; + } + } + + /** + * Imports a certificate and adds it to the list of trusted certificates. + * + * @return true if the certificate was added, otherwise false. + */ + private boolean addTrustedCert(String alias, InputStream in) + throws Exception + { + if (alias == null) { + throw new Exception(rb.getString("Must.specify.alias")); + } + if (keyStore.containsAlias(alias)) { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.not.imported.alias.alias.already.exists")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + // Read the certificate + X509Certificate cert = null; + try { + cert = (X509Certificate)cf.generateCertificate(in); + } catch (ClassCastException | CertificateException ce) { + throw new Exception(rb.getString("Input.not.an.X.509.certificate")); + } + + // if certificate is self-signed, make sure it verifies + boolean selfSigned = false; + if (isSelfSigned(cert)) { + cert.verify(cert.getPublicKey()); + selfSigned = true; + } + + if (noprompt) { + keyStore.setCertificateEntry(alias, cert); + return true; + } + + // check if cert already exists in keystore + String reply = null; + String trustalias = keyStore.getCertificateAlias(cert); + if (trustalias != null) { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.already.exists.in.keystore.under.alias.trustalias.")); + Object[] source = {trustalias}; + System.err.println(form.format(source)); + reply = getYesNoReply + (rb.getString("Do.you.still.want.to.add.it.no.")); + } else if (selfSigned) { + if (trustcacerts && (caks != null) && + ((trustalias=caks.getCertificateAlias(cert)) != null)) { + MessageFormat form = new MessageFormat(rb.getString + ("Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.")); + Object[] source = {trustalias}; + System.err.println(form.format(source)); + reply = getYesNoReply + (rb.getString("Do.you.still.want.to.add.it.to.your.own.keystore.no.")); + } + if (trustalias == null) { + // Print the cert and ask user if they really want to add + // it to their keystore + printX509Cert(cert, System.out); + reply = getYesNoReply + (rb.getString("Trust.this.certificate.no.")); + } + } + if (reply != null) { + if ("YES".equals(reply)) { + keyStore.setCertificateEntry(alias, cert); + return true; + } else { + return false; + } + } + + // Try to establish trust chain + try { + Certificate[] chain = establishCertChain(null, cert); + if (chain != null) { + keyStore.setCertificateEntry(alias, cert); + return true; + } + } catch (Exception e) { + // Print the cert and ask user if they really want to add it to + // their keystore + printX509Cert(cert, System.out); + reply = getYesNoReply + (rb.getString("Trust.this.certificate.no.")); + if ("YES".equals(reply)) { + keyStore.setCertificateEntry(alias, cert); + return true; + } else { + return false; + } + } + + return false; + } + + /** + * Prompts user for new password. New password must be different from + * old one. + * + * @param prompt the message that gets prompted on the screen + * @param oldPasswd the current (i.e., old) password + */ + private char[] getNewPasswd(String prompt, char[] oldPasswd) + throws Exception + { + char[] entered = null; + char[] reentered = null; + + for (int count = 0; count < 3; count++) { + MessageFormat form = new MessageFormat + (rb.getString("New.prompt.")); + Object[] source = {prompt}; + System.err.print(form.format(source)); + entered = Password.readPassword(System.in); + passwords.add(entered); + if (entered == null || entered.length < 6) { + System.err.println(rb.getString + ("Password.is.too.short.must.be.at.least.6.characters")); + } else if (Arrays.equals(entered, oldPasswd)) { + System.err.println(rb.getString("Passwords.must.differ")); + } else { + form = new MessageFormat + (rb.getString("Re.enter.new.prompt.")); + Object[] src = {prompt}; + System.err.print(form.format(src)); + reentered = Password.readPassword(System.in); + passwords.add(reentered); + if (!Arrays.equals(entered, reentered)) { + System.err.println + (rb.getString("They.don.t.match.Try.again")); + } else { + Arrays.fill(reentered, ' '); + return entered; + } + } + if (entered != null) { + Arrays.fill(entered, ' '); + entered = null; + } + if (reentered != null) { + Arrays.fill(reentered, ' '); + reentered = null; + } + } + throw new Exception(rb.getString("Too.many.failures.try.later")); + } + + /** + * Prompts user for alias name. + * @param prompt the {0} of "Enter {0} alias name: " in prompt line + * @returns the string entered by the user, without the \n at the end + */ + private String getAlias(String prompt) throws Exception { + if (prompt != null) { + MessageFormat form = new MessageFormat + (rb.getString("Enter.prompt.alias.name.")); + Object[] source = {prompt}; + System.err.print(form.format(source)); + } else { + System.err.print(rb.getString("Enter.alias.name.")); + } + return (new BufferedReader(new InputStreamReader( + System.in))).readLine(); + } + + /** + * Prompts user for an input string from the command line (System.in) + * @prompt the prompt string printed + * @returns the string entered by the user, without the \n at the end + */ + private String inputStringFromStdin(String prompt) throws Exception { + System.err.print(prompt); + return (new BufferedReader(new InputStreamReader( + System.in))).readLine(); + } + + /** + * Prompts user for key password. User may select to choose the same + * password (otherKeyPass) as for otherAlias. + */ + private char[] getKeyPasswd(String alias, String otherAlias, + char[] otherKeyPass) + throws Exception + { + int count = 0; + char[] keyPass = null; + + do { + if (otherKeyPass != null) { + MessageFormat form = new MessageFormat(rb.getString + ("Enter.key.password.for.alias.")); + Object[] source = {alias}; + System.err.println(form.format(source)); + + form = new MessageFormat(rb.getString + (".RETURN.if.same.as.for.otherAlias.")); + Object[] src = {otherAlias}; + System.err.print(form.format(src)); + } else { + MessageFormat form = new MessageFormat(rb.getString + ("Enter.key.password.for.alias.")); + Object[] source = {alias}; + System.err.print(form.format(source)); + } + System.err.flush(); + keyPass = Password.readPassword(System.in); + passwords.add(keyPass); + if (keyPass == null) { + keyPass = otherKeyPass; + } + count++; + } while ((keyPass == null) && count < 3); + + if (keyPass == null) { + throw new Exception(rb.getString("Too.many.failures.try.later")); + } + + return keyPass; + } + + /** + * Prints a certificate in a human readable format. + */ + private void printX509Cert(X509Certificate cert, PrintStream out) + throws Exception + { + /* + out.println("Owner: " + + cert.getSubjectDN().toString() + + "\n" + + "Issuer: " + + cert.getIssuerDN().toString() + + "\n" + + "Serial number: " + cert.getSerialNumber().toString(16) + + "\n" + + "Valid from: " + cert.getNotBefore().toString() + + " until: " + cert.getNotAfter().toString() + + "\n" + + "Certificate fingerprints:\n" + + "\t MD5: " + getCertFingerPrint("MD5", cert) + + "\n" + + "\t SHA1: " + getCertFingerPrint("SHA1", cert)); + */ + + MessageFormat form = new MessageFormat + (rb.getString(".PATTERN.printX509Cert")); + Object[] source = {cert.getSubjectDN().toString(), + cert.getIssuerDN().toString(), + cert.getSerialNumber().toString(16), + cert.getNotBefore().toString(), + cert.getNotAfter().toString(), + getCertFingerPrint("MD5", cert), + getCertFingerPrint("SHA1", cert), + getCertFingerPrint("SHA-256", cert), + cert.getSigAlgName(), + cert.getVersion() + }; + out.println(form.format(source)); + + if (cert instanceof X509CertImpl) { + X509CertImpl impl = (X509CertImpl)cert; + X509CertInfo certInfo = (X509CertInfo)impl.get(X509CertImpl.NAME + + "." + + X509CertImpl.INFO); + CertificateExtensions exts = (CertificateExtensions) + certInfo.get(X509CertInfo.EXTENSIONS); + if (exts != null) { + printExtensions(rb.getString("Extensions."), exts, out); + } + } + } + + private static void printExtensions(String title, CertificateExtensions exts, PrintStream out) + throws Exception { + int extnum = 0; + Iterator i1 = exts.getAllExtensions().iterator(); + Iterator i2 = exts.getUnparseableExtensions().values().iterator(); + while (i1.hasNext() || i2.hasNext()) { + Extension ext = i1.hasNext()?i1.next():i2.next(); + if (extnum == 0) { + out.println(); + out.println(title); + out.println(); + } + out.print("#"+(++extnum)+": "+ ext); + if (ext.getClass() == Extension.class) { + byte[] v = ext.getExtensionValue(); + if (v.length == 0) { + out.println(rb.getString(".Empty.value.")); + } else { + new sun.misc.HexDumpEncoder().encodeBuffer(ext.getExtensionValue(), out); + out.println(); + } + } + out.println(); + } + } + + /** + * Returns true if the certificate is self-signed, false otherwise. + */ + private boolean isSelfSigned(X509Certificate cert) { + return signedBy(cert, cert); + } + + private boolean signedBy(X509Certificate end, X509Certificate ca) { + if (!ca.getSubjectDN().equals(end.getIssuerDN())) { + return false; + } + try { + end.verify(ca.getPublicKey()); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * Locates a signer for a given certificate from a given keystore and + * returns the signer's certificate. + * @param cert the certificate whose signer is searched, not null + * @param ks the keystore to search with, not null + * @return cert itself if it's already inside ks, + * or a certificate inside ks who signs cert, + * or null otherwise. + */ + private static Certificate getTrustedSigner(Certificate cert, KeyStore ks) + throws Exception { + if (ks.getCertificateAlias(cert) != null) { + return cert; + } + for (Enumeration aliases = ks.aliases(); + aliases.hasMoreElements(); ) { + String name = aliases.nextElement(); + Certificate trustedCert = ks.getCertificate(name); + if (trustedCert != null) { + try { + cert.verify(trustedCert.getPublicKey()); + return trustedCert; + } catch (Exception e) { + // Not verified, skip to the next one + } + } + } + return null; + } + + /** + * Gets an X.500 name suitable for inclusion in a certification request. + */ + private X500Name getX500Name() throws IOException { + BufferedReader in; + in = new BufferedReader(new InputStreamReader(System.in)); + String commonName = "Unknown"; + String organizationalUnit = "Unknown"; + String organization = "Unknown"; + String city = "Unknown"; + String state = "Unknown"; + String country = "Unknown"; + X500Name name; + String userInput = null; + + int maxRetry = 20; + do { + if (maxRetry-- < 0) { + throw new RuntimeException(rb.getString( + "Too.many.retries.program.terminated")); + } + commonName = inputString(in, + rb.getString("What.is.your.first.and.last.name."), + commonName); + organizationalUnit = inputString(in, + rb.getString + ("What.is.the.name.of.your.organizational.unit."), + organizationalUnit); + organization = inputString(in, + rb.getString("What.is.the.name.of.your.organization."), + organization); + city = inputString(in, + rb.getString("What.is.the.name.of.your.City.or.Locality."), + city); + state = inputString(in, + rb.getString("What.is.the.name.of.your.State.or.Province."), + state); + country = inputString(in, + rb.getString + ("What.is.the.two.letter.country.code.for.this.unit."), + country); + name = new X500Name(commonName, organizationalUnit, organization, + city, state, country); + MessageFormat form = new MessageFormat + (rb.getString("Is.name.correct.")); + Object[] source = {name}; + userInput = inputString + (in, form.format(source), rb.getString("no")); + } while (collator.compare(userInput, rb.getString("yes")) != 0 && + collator.compare(userInput, rb.getString("y")) != 0); + + System.err.println(); + return name; + } + + private String inputString(BufferedReader in, String prompt, + String defaultValue) + throws IOException + { + System.err.println(prompt); + MessageFormat form = new MessageFormat + (rb.getString(".defaultValue.")); + Object[] source = {defaultValue}; + System.err.print(form.format(source)); + System.err.flush(); + + String value = in.readLine(); + if (value == null || collator.compare(value, "") == 0) { + value = defaultValue; + } + return value; + } + + /** + * Writes an X.509 certificate in base64 or binary encoding to an output + * stream. + */ + private void dumpCert(Certificate cert, PrintStream out) + throws IOException, CertificateException + { + if (rfc) { + out.println(X509Factory.BEGIN_CERT); + out.println(Base64.getMimeEncoder().encodeToString(cert.getEncoded())); + out.println(X509Factory.END_CERT); + } else { + out.write(cert.getEncoded()); // binary + } + } + + /** + * Converts a byte to hex digit and writes to the supplied buffer + */ + private void byte2hex(byte b, StringBuffer buf) { + char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + int high = ((b & 0xf0) >> 4); + int low = (b & 0x0f); + buf.append(hexChars[high]); + buf.append(hexChars[low]); + } + + /** + * Converts a byte array to hex string + */ + private String toHexString(byte[] block) { + StringBuffer buf = new StringBuffer(); + int len = block.length; + for (int i = 0; i < len; i++) { + byte2hex(block[i], buf); + if (i < len-1) { + buf.append(":"); + } + } + return buf.toString(); + } + + /** + * Recovers (private) key associated with given alias. + * + * @return an array of objects, where the 1st element in the array is the + * recovered private key, and the 2nd element is the password used to + * recover it. + */ + private Pair recoverKey(String alias, char[] storePass, + char[] keyPass) + throws Exception + { + Key key = null; + + if (keyStore.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + if (!keyStore.entryInstanceOf(alias, PrivateKeyEntry.class) && + !keyStore.entryInstanceOf(alias, SecretKeyEntry.class)) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.has.no.key")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + if (keyPass == null) { + // Try to recover the key using the keystore password + try { + key = keyStore.getKey(alias, storePass); + + keyPass = storePass; + passwords.add(keyPass); + } catch (UnrecoverableKeyException e) { + // Did not work out, so prompt user for key password + if (!token) { + keyPass = getKeyPasswd(alias, null, null); + key = keyStore.getKey(alias, keyPass); + } else { + throw e; + } + } + } else { + key = keyStore.getKey(alias, keyPass); + } + + return Pair.of(key, keyPass); + } + + /** + * Recovers entry associated with given alias. + * + * @return an array of objects, where the 1st element in the array is the + * recovered entry, and the 2nd element is the password used to + * recover it (null if no password). + */ + private Pair recoverEntry(KeyStore ks, + String alias, + char[] pstore, + char[] pkey) throws Exception { + + if (ks.containsAlias(alias) == false) { + MessageFormat form = new MessageFormat + (rb.getString("Alias.alias.does.not.exist")); + Object[] source = {alias}; + throw new Exception(form.format(source)); + } + + PasswordProtection pp = null; + Entry entry; + + try { + // First attempt to access entry without key password + // (PKCS11 entry or trusted certificate entry, for example) + + entry = ks.getEntry(alias, pp); + pkey = null; + } catch (UnrecoverableEntryException une) { + + if(P11KEYSTORE.equalsIgnoreCase(ks.getType()) || + KeyStoreUtil.isWindowsKeyStore(ks.getType())) { + // should not happen, but a possibility + throw une; + } + + // entry is protected + + if (pkey != null) { + + // try provided key password + + pp = new PasswordProtection(pkey); + entry = ks.getEntry(alias, pp); + + } else { + + // try store pass + + try { + pp = new PasswordProtection(pstore); + entry = ks.getEntry(alias, pp); + pkey = pstore; + } catch (UnrecoverableEntryException une2) { + if (P12KEYSTORE.equalsIgnoreCase(ks.getType())) { + + // P12 keystore currently does not support separate + // store and entry passwords + + throw une2; + } else { + + // prompt for entry password + + pkey = getKeyPasswd(alias, null, null); + pp = new PasswordProtection(pkey); + entry = ks.getEntry(alias, pp); + } + } + } + } + + return Pair.of(entry, pkey); + } + /** + * Gets the requested finger print of the certificate. + */ + private String getCertFingerPrint(String mdAlg, Certificate cert) + throws Exception + { + byte[] encCertInfo = cert.getEncoded(); + MessageDigest md = MessageDigest.getInstance(mdAlg); + byte[] digest = md.digest(encCertInfo); + return toHexString(digest); + } + + /** + * Prints warning about missing integrity check. + */ + private void printWarning() { + System.err.println(); + System.err.println(rb.getString + (".WARNING.WARNING.WARNING.")); + System.err.println(rb.getString + (".The.integrity.of.the.information.stored.in.your.keystore.")); + System.err.println(rb.getString + (".WARNING.WARNING.WARNING.")); + System.err.println(); + } + + /** + * Validates chain in certification reply, and returns the ordered + * elements of the chain (with user certificate first, and root + * certificate last in the array). + * + * @param alias the alias name + * @param userCert the user certificate of the alias + * @param replyCerts the chain provided in the reply + */ + private Certificate[] validateReply(String alias, + Certificate userCert, + Certificate[] replyCerts) + throws Exception + { + // order the certs in the reply (bottom-up). + // we know that all certs in the reply are of type X.509, because + // we parsed them using an X.509 certificate factory + int i; + PublicKey userPubKey = userCert.getPublicKey(); + for (i=0; i> certs = null; + if (keyStore.size() > 0) { + certs = new Hashtable>(11); + keystorecerts2Hashtable(keyStore, certs); + } + if (trustcacerts) { + if (caks!=null && caks.size()>0) { + if (certs == null) { + certs = new Hashtable>(11); + } + keystorecerts2Hashtable(caks, certs); + } + } + + // start building chain + Vector chain = new Vector<>(2); + if (buildChain((X509Certificate)certToVerify, chain, certs)) { + Certificate[] newChain = new Certificate[chain.size()]; + // buildChain() returns chain with self-signed root-cert first and + // user-cert last, so we need to invert the chain before we store + // it + int j=0; + for (int i=chain.size()-1; i>=0; i--) { + newChain[j] = chain.elementAt(i); + j++; + } + return newChain; + } else { + throw new Exception + (rb.getString("Failed.to.establish.chain.from.reply")); + } + } + + /** + * Recursively tries to establish chain from pool of trusted certs. + * + * @param certToVerify the cert that needs to be verified. + * @param chain the chain that's being built. + * @param certs the pool of trusted certs + * + * @return true if successful, false otherwise. + */ + private boolean buildChain(X509Certificate certToVerify, + Vector chain, + Hashtable> certs) { + Principal issuer = certToVerify.getIssuerDN(); + if (isSelfSigned(certToVerify)) { + // reached self-signed root cert; + // no verification needed because it's trusted. + chain.addElement(certToVerify); + return true; + } + + // Get the issuer's certificate(s) + Vector vec = certs.get(issuer); + if (vec == null) { + return false; + } + + // Try out each certificate in the vector, until we find one + // whose public key verifies the signature of the certificate + // in question. + for (Enumeration issuerCerts = vec.elements(); + issuerCerts.hasMoreElements(); ) { + X509Certificate issuerCert + = (X509Certificate)issuerCerts.nextElement(); + PublicKey issuerPubKey = issuerCert.getPublicKey(); + try { + certToVerify.verify(issuerPubKey); + } catch (Exception e) { + continue; + } + if (buildChain(issuerCert, chain, certs)) { + chain.addElement(certToVerify); + return true; + } + } + return false; + } + + /** + * Prompts user for yes/no decision. + * + * @return the user's decision, can only be "YES" or "NO" + */ + private String getYesNoReply(String prompt) + throws IOException + { + String reply = null; + int maxRetry = 20; + do { + if (maxRetry-- < 0) { + throw new RuntimeException(rb.getString( + "Too.many.retries.program.terminated")); + } + System.err.print(prompt); + System.err.flush(); + reply = (new BufferedReader(new InputStreamReader + (System.in))).readLine(); + if (collator.compare(reply, "") == 0 || + collator.compare(reply, rb.getString("n")) == 0 || + collator.compare(reply, rb.getString("no")) == 0) { + reply = "NO"; + } else if (collator.compare(reply, rb.getString("y")) == 0 || + collator.compare(reply, rb.getString("yes")) == 0) { + reply = "YES"; + } else { + System.err.println(rb.getString("Wrong.answer.try.again")); + reply = null; + } + } while (reply == null); + return reply; + } + + /** + * Stores the (leaf) certificates of a keystore in a hashtable. + * All certs belonging to the same CA are stored in a vector that + * in turn is stored in the hashtable, keyed by the CA's subject DN + */ + private void keystorecerts2Hashtable(KeyStore ks, + Hashtable> hash) + throws Exception { + + for (Enumeration aliases = ks.aliases(); + aliases.hasMoreElements(); ) { + String alias = aliases.nextElement(); + Certificate cert = ks.getCertificate(alias); + if (cert != null) { + Principal subjectDN = ((X509Certificate)cert).getSubjectDN(); + Vector vec = hash.get(subjectDN); + if (vec == null) { + vec = new Vector(); + vec.addElement(cert); + } else { + if (!vec.contains(cert)) { + vec.addElement(cert); + } + } + hash.put(subjectDN, vec); + } + } + } + + /** + * Returns the issue time that's specified the -startdate option + * @param s the value of -startdate option + */ + private static Date getStartDate(String s) throws IOException { + Calendar c = new GregorianCalendar(); + if (s != null) { + IOException ioe = new IOException( + rb.getString("Illegal.startdate.value")); + int len = s.length(); + if (len == 0) { + throw ioe; + } + if (s.charAt(0) == '-' || s.charAt(0) == '+') { + // Form 1: ([+-]nnn[ymdHMS])+ + int start = 0; + while (start < len) { + int sign = 0; + switch (s.charAt(start)) { + case '+': sign = 1; break; + case '-': sign = -1; break; + default: throw ioe; + } + int i = start+1; + for (; i '9') break; + } + if (i == start+1) throw ioe; + int number = Integer.parseInt(s.substring(start+1, i)); + if (i >= len) throw ioe; + int unit = 0; + switch (s.charAt(i)) { + case 'y': unit = Calendar.YEAR; break; + case 'm': unit = Calendar.MONTH; break; + case 'd': unit = Calendar.DATE; break; + case 'H': unit = Calendar.HOUR; break; + case 'M': unit = Calendar.MINUTE; break; + case 'S': unit = Calendar.SECOND; break; + default: throw ioe; + } + c.add(unit, sign * number); + start = i + 1; + } + } else { + // Form 2: [yyyy/mm/dd] [HH:MM:SS] + String date = null, time = null; + if (len == 19) { + date = s.substring(0, 10); + time = s.substring(11); + if (s.charAt(10) != ' ') + throw ioe; + } else if (len == 10) { + date = s; + } else if (len == 8) { + time = s; + } else { + throw ioe; + } + if (date != null) { + if (date.matches("\\d\\d\\d\\d\\/\\d\\d\\/\\d\\d")) { + c.set(Integer.valueOf(date.substring(0, 4)), + Integer.valueOf(date.substring(5, 7))-1, + Integer.valueOf(date.substring(8, 10))); + } else { + throw ioe; + } + } + if (time != null) { + if (time.matches("\\d\\d:\\d\\d:\\d\\d")) { + c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time.substring(0, 2))); + c.set(Calendar.MINUTE, Integer.valueOf(time.substring(0, 2))); + c.set(Calendar.SECOND, Integer.valueOf(time.substring(0, 2))); + c.set(Calendar.MILLISECOND, 0); + } else { + throw ioe; + } + } + } + } + return c.getTime(); + } + + /** + * Match a command (may be abbreviated) with a command set. + * @param s the command provided + * @param list the legal command set. If there is a null, commands after it + * are regarded experimental, which means they are supported but their + * existence should not be revealed to user. + * @return the position of a single match, or -1 if none matched + * @throws Exception if s is ambiguous + */ + private static int oneOf(String s, String... list) throws Exception { + int[] match = new int[list.length]; + int nmatch = 0; + int experiment = Integer.MAX_VALUE; + for (int i = 0; i experiment) { + return match[0]; + } + StringBuffer sb = new StringBuffer(); + MessageFormat form = new MessageFormat(rb.getString + ("command.{0}.is.ambiguous.")); + Object[] source = {s}; + sb.append(form.format(source)); + sb.append("\n "); + for (int i=0; iextstr argument. + * + * @param reqex the requested extensions, can be null, used for -gencert + * @param ext the original extensions, can be null, used for -selfcert + * @param extstrs -ext values, Read keytool doc + * @param pkey the public key for the certificate + * @param akey the public key for the authority (issuer) + * @return the created CertificateExtensions + */ + private CertificateExtensions createV3Extensions( + CertificateExtensions reqex, + CertificateExtensions ext, + List extstrs, + PublicKey pkey, + PublicKey akey) throws Exception { + + if (ext != null && reqex != null) { + // This should not happen + throw new Exception("One of request and original should be null."); + } + if (ext == null) ext = new CertificateExtensions(); + try { + // name{:critical}{=value} + // Honoring requested extensions + if (reqex != null) { + for(String extstr: extstrs) { + if (extstr.toLowerCase(Locale.ENGLISH).startsWith("honored=")) { + List list = Arrays.asList( + extstr.toLowerCase(Locale.ENGLISH).substring(8).split(",")); + // First check existence of "all" + if (list.contains("all")) { + ext = reqex; // we know ext was null + } + // one by one for others + for (String item: list) { + if (item.equals("all")) continue; + + // add or remove + boolean add = true; + // -1, unchanged, 0 crtical, 1 non-critical + int action = -1; + String type = null; + if (item.startsWith("-")) { + add = false; + type = item.substring(1); + } else { + int colonpos = item.indexOf(':'); + if (colonpos >= 0) { + type = item.substring(0, colonpos); + action = oneOf(item.substring(colonpos+1), + "critical", "non-critical"); + if (action == -1) { + throw new Exception(rb.getString + ("Illegal.value.") + item); + } + } + } + String n = reqex.getNameByOid(findOidForExtName(type)); + if (add) { + Extension e = reqex.get(n); + if (!e.isCritical() && action == 0 + || e.isCritical() && action == 1) { + e = Extension.newExtension( + e.getExtensionId(), + !e.isCritical(), + e.getExtensionValue()); + ext.set(n, e); + } + } else { + ext.delete(n); + } + } + break; + } + } + } + for(String extstr: extstrs) { + String name, value; + boolean isCritical = false; + + int eqpos = extstr.indexOf('='); + if (eqpos >= 0) { + name = extstr.substring(0, eqpos); + value = extstr.substring(eqpos+1); + } else { + name = extstr; + value = null; + } + + int colonpos = name.indexOf(':'); + if (colonpos >= 0) { + if (oneOf(name.substring(colonpos+1), "critical") == 0) { + isCritical = true; + } + name = name.substring(0, colonpos); + } + + if (name.equalsIgnoreCase("honored")) { + continue; + } + int exttype = oneOf(name, extSupported); + switch (exttype) { + case 0: // BC + int pathLen = -1; + boolean isCA = false; + if (value == null) { + isCA = true; + } else { + try { // the abbr format + pathLen = Integer.parseInt(value); + isCA = true; + } catch (NumberFormatException ufe) { + // ca:true,pathlen:1 + for (String part: value.split(",")) { + String[] nv = part.split(":"); + if (nv.length != 2) { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } else { + if (nv[0].equalsIgnoreCase("ca")) { + isCA = Boolean.parseBoolean(nv[1]); + } else if (nv[0].equalsIgnoreCase("pathlen")) { + pathLen = Integer.parseInt(nv[1]); + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + } + } + } + } + ext.set(BasicConstraintsExtension.NAME, + new BasicConstraintsExtension(isCritical, isCA, + pathLen)); + break; + case 1: // KU + if(value != null) { + boolean[] ok = new boolean[9]; + for (String s: value.split(",")) { + int p = oneOf(s, + "digitalSignature", // (0), + "nonRepudiation", // (1) + "keyEncipherment", // (2), + "dataEncipherment", // (3), + "keyAgreement", // (4), + "keyCertSign", // (5), + "cRLSign", // (6), + "encipherOnly", // (7), + "decipherOnly", // (8) + "contentCommitment" // also (1) + ); + if (p < 0) { + throw new Exception(rb.getString("Unknown.keyUsage.type.") + s); + } + if (p == 9) p = 1; + ok[p] = true; + } + KeyUsageExtension kue = new KeyUsageExtension(ok); + // The above KeyUsageExtension constructor does not + // allow isCritical value, so... + ext.set(KeyUsageExtension.NAME, Extension.newExtension( + kue.getExtensionId(), + isCritical, + kue.getExtensionValue())); + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + break; + case 2: // EKU + if(value != null) { + Vector v = new Vector<>(); + for (String s: value.split(",")) { + int p = oneOf(s, + "anyExtendedKeyUsage", + "serverAuth", //1 + "clientAuth", //2 + "codeSigning", //3 + "emailProtection", //4 + "", //5 + "", //6 + "", //7 + "timeStamping", //8 + "OCSPSigning" //9 + ); + if (p < 0) { + try { + v.add(new ObjectIdentifier(s)); + } catch (Exception e) { + throw new Exception(rb.getString( + "Unknown.extendedkeyUsage.type.") + s); + } + } else if (p == 0) { + v.add(new ObjectIdentifier("2.5.29.37.0")); + } else { + v.add(new ObjectIdentifier("1.3.6.1.5.5.7.3." + p)); + } + } + ext.set(ExtendedKeyUsageExtension.NAME, + new ExtendedKeyUsageExtension(isCritical, v)); + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + break; + case 3: // SAN + case 4: // IAN + if(value != null) { + String[] ps = value.split(","); + GeneralNames gnames = new GeneralNames(); + for(String item: ps) { + colonpos = item.indexOf(':'); + if (colonpos < 0) { + throw new Exception("Illegal item " + item + " in " + extstr); + } + String t = item.substring(0, colonpos); + String v = item.substring(colonpos+1); + gnames.add(createGeneralName(t, v)); + } + if (exttype == 3) { + ext.set(SubjectAlternativeNameExtension.NAME, + new SubjectAlternativeNameExtension( + isCritical, gnames)); + } else { + ext.set(IssuerAlternativeNameExtension.NAME, + new IssuerAlternativeNameExtension( + isCritical, gnames)); + } + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + break; + case 5: // SIA, always non-critical + case 6: // AIA, always non-critical + if (isCritical) { + throw new Exception(rb.getString( + "This.extension.cannot.be.marked.as.critical.") + extstr); + } + if(value != null) { + List accessDescriptions = + new ArrayList<>(); + String[] ps = value.split(","); + for(String item: ps) { + colonpos = item.indexOf(':'); + int colonpos2 = item.indexOf(':', colonpos+1); + if (colonpos < 0 || colonpos2 < 0) { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + String m = item.substring(0, colonpos); + String t = item.substring(colonpos+1, colonpos2); + String v = item.substring(colonpos2+1); + int p = oneOf(m, + "", + "ocsp", //1 + "caIssuers", //2 + "timeStamping", //3 + "", + "caRepository" //5 + ); + ObjectIdentifier oid; + if (p < 0) { + try { + oid = new ObjectIdentifier(m); + } catch (Exception e) { + throw new Exception(rb.getString( + "Unknown.AccessDescription.type.") + m); + } + } else { + oid = new ObjectIdentifier("1.3.6.1.5.5.7.48." + p); + } + accessDescriptions.add(new AccessDescription( + oid, createGeneralName(t, v))); + } + if (exttype == 5) { + ext.set(SubjectInfoAccessExtension.NAME, + new SubjectInfoAccessExtension(accessDescriptions)); + } else { + ext.set(AuthorityInfoAccessExtension.NAME, + new AuthorityInfoAccessExtension(accessDescriptions)); + } + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + break; + case 8: // CRL, experimental, only support 1 distributionpoint + if(value != null) { + String[] ps = value.split(","); + GeneralNames gnames = new GeneralNames(); + for(String item: ps) { + colonpos = item.indexOf(':'); + if (colonpos < 0) { + throw new Exception("Illegal item " + item + " in " + extstr); + } + String t = item.substring(0, colonpos); + String v = item.substring(colonpos+1); + gnames.add(createGeneralName(t, v)); + } + ext.set(CRLDistributionPointsExtension.NAME, + new CRLDistributionPointsExtension( + isCritical, Collections.singletonList( + new DistributionPoint(gnames, null, null)))); + } else { + throw new Exception(rb.getString + ("Illegal.value.") + extstr); + } + break; + case -1: + ObjectIdentifier oid = new ObjectIdentifier(name); + byte[] data = null; + if (value != null) { + data = new byte[value.length() / 2 + 1]; + int pos = 0; + for (char c: value.toCharArray()) { + int hex; + if (c >= '0' && c <= '9') { + hex = c - '0' ; + } else if (c >= 'A' && c <= 'F') { + hex = c - 'A' + 10; + } else if (c >= 'a' && c <= 'f') { + hex = c - 'a' + 10; + } else { + continue; + } + if (pos % 2 == 0) { + data[pos/2] = (byte)(hex << 4); + } else { + data[pos/2] += hex; + } + pos++; + } + if (pos % 2 != 0) { + throw new Exception(rb.getString( + "Odd.number.of.hex.digits.found.") + extstr); + } + data = Arrays.copyOf(data, pos/2); + } else { + data = new byte[0]; + } + ext.set(oid.toString(), new Extension(oid, isCritical, + new DerValue(DerValue.tag_OctetString, data) + .toByteArray())); + break; + default: + throw new Exception(rb.getString( + "Unknown.extension.type.") + extstr); + } + } + // always non-critical + ext.set(SubjectKeyIdentifierExtension.NAME, + new SubjectKeyIdentifierExtension( + new KeyIdentifier(pkey).getIdentifier())); + if (akey != null && !pkey.equals(akey)) { + ext.set(AuthorityKeyIdentifierExtension.NAME, + new AuthorityKeyIdentifierExtension( + new KeyIdentifier(akey), null, null)); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + return ext; + } + + /** + * Prints the usage of this tool. + */ + private void usage() { + if (command != null) { + System.err.println("keytool " + command + + rb.getString(".OPTION.")); + System.err.println(); + System.err.println(rb.getString(command.description)); + System.err.println(); + System.err.println(rb.getString("Options.")); + System.err.println(); + + // Left and right sides of the options list + String[] left = new String[command.options.length]; + String[] right = new String[command.options.length]; + + // Check if there's an unknown option + boolean found = false; + + // Length of left side of options list + int lenLeft = 0; + for (int j=0; j lenLeft) { + lenLeft = left[j].length(); + } + right[j] = rb.getString(opt.description); + } + for (int j=0; j { + + public final A fst; + public final B snd; + + public Pair(A fst, B snd) { + this.fst = fst; + this.snd = snd; + } + + public String toString() { + return "Pair[" + fst + "," + snd + "]"; + } + + public boolean equals(Object other) { + return + other instanceof Pair && + Objects.equals(fst, ((Pair)other).fst) && + Objects.equals(snd, ((Pair)other).snd); + } + + public int hashCode() { + if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1; + else if (snd == null) return fst.hashCode() + 2; + else return fst.hashCode() * 17 + snd.hashCode(); + } + + public static Pair of(A a, B b) { + return new Pair<>(a,b); + } +} + diff --git a/src/sun/security/tools/keytool/Resources.java b/src/sun/security/tools/keytool/Resources.java new file mode 100644 index 00000000..73eac266 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources.java @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Options:"}, + {"Use.keytool.help.for.all.available.commands", + "Use \"keytool -help\" for all available commands"}, + {"Key.and.Certificate.Management.Tool", + "Key and Certificate Management Tool"}, + {"Commands.", "Commands:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "Use \"keytool -command_name -help\" for usage of command_name"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Generates a certificate request"}, //-certreq + {"Changes.an.entry.s.alias", + "Changes an entry's alias"}, //-changealias + {"Deletes.an.entry", + "Deletes an entry"}, //-delete + {"Exports.certificate", + "Exports certificate"}, //-exportcert + {"Generates.a.key.pair", + "Generates a key pair"}, //-genkeypair + {"Generates.a.secret.key", + "Generates a secret key"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Generates certificate from a certificate request"}, //-gencert + {"Generates.CRL", "Generates CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Generated {0} secret key"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Generated {0}-bit {1} secret key"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Imports entries from a JDK 1.1.x-style identity database"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Imports a certificate or a certificate chain"}, //-importcert + {"Imports.a.password", + "Imports a password"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Imports one or all entries from another keystore"}, //-importkeystore + {"Clones.a.key.entry", + "Clones a key entry"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "Changes the key password of an entry"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Lists entries in a keystore"}, //-list + {"Prints.the.content.of.a.certificate", + "Prints the content of a certificate"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Prints the content of a certificate request"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Prints the content of a CRL file"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Generates a self-signed certificate"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "Changes the store password of a keystore"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "alias name of the entry to process"}, //-alias + {"destination.alias", + "destination alias"}, //-destalias + {"destination.key.password", + "destination key password"}, //-destkeypass + {"destination.keystore.name", + "destination keystore name"}, //-destkeystore + {"destination.keystore.password.protected", + "destination keystore password protected"}, //-destprotected + {"destination.keystore.provider.name", + "destination keystore provider name"}, //-destprovidername + {"destination.keystore.password", + "destination keystore password"}, //-deststorepass + {"destination.keystore.type", + "destination keystore type"}, //-deststoretype + {"distinguished.name", + "distinguished name"}, //-dname + {"X.509.extension", + "X.509 extension"}, //-ext + {"output.file.name", + "output file name"}, //-file and -outfile + {"input.file.name", + "input file name"}, //-file and -infile + {"key.algorithm.name", + "key algorithm name"}, //-keyalg + {"key.password", + "key password"}, //-keypass + {"key.bit.size", + "key bit size"}, //-keysize + {"keystore.name", + "keystore name"}, //-keystore + {"new.password", + "new password"}, //-new + {"do.not.prompt", + "do not prompt"}, //-noprompt + {"password.through.protected.mechanism", + "password through protected mechanism"}, //-protected + {"provider.argument", + "provider argument"}, //-providerarg + {"provider.class.name", + "provider class name"}, //-providerclass + {"provider.name", + "provider name"}, //-providername + {"provider.classpath", + "provider classpath"}, //-providerpath + {"output.in.RFC.style", + "output in RFC style"}, //-rfc + {"signature.algorithm.name", + "signature algorithm name"}, //-sigalg + {"source.alias", + "source alias"}, //-srcalias + {"source.key.password", + "source key password"}, //-srckeypass + {"source.keystore.name", + "source keystore name"}, //-srckeystore + {"source.keystore.password.protected", + "source keystore password protected"}, //-srcprotected + {"source.keystore.provider.name", + "source keystore provider name"}, //-srcprovidername + {"source.keystore.password", + "source keystore password"}, //-srcstorepass + {"source.keystore.type", + "source keystore type"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL server host and port"}, //-sslserver + {"signed.jar.file", + "signed jar file"}, //=jarfile + {"certificate.validity.start.date.time", + "certificate validity start date/time"}, //-startdate + {"keystore.password", + "keystore password"}, //-storepass + {"keystore.type", + "keystore type"}, //-storetype + {"trust.certificates.from.cacerts", + "trust certificates from cacerts"}, //-trustcacerts + {"verbose.output", + "verbose output"}, //-v + {"validity.number.of.days", + "validity number of days"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "Serial ID of cert to revoke"}, //-id + // keytool: Running part + {"keytool.error.", "keytool error: "}, + {"Illegal.option.", "Illegal option: "}, + {"Illegal.value.", "Illegal value: "}, + {"Unknown.password.type.", "Unknown password type: "}, + {"Cannot.find.environment.variable.", + "Cannot find environment variable: "}, + {"Cannot.find.file.", "Cannot find file: "}, + {"Command.option.flag.needs.an.argument.", "Command option {0} needs an argument."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Warning: Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified {0} value."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore must be NONE if -storetype is {0}"}, + {"Too.many.retries.program.terminated", + "Too many retries, program terminated"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "-storepasswd and -keypasswd commands not supported if -storetype is {0}"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "-keypasswd commands not supported if -storetype is PKCS12"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-keypass and -new can not be specified if -storetype is {0}"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "if -protected is specified, then -storepass, -keypass, and -new must not be specified"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "if -srcprotected is specified, then -srcstorepass and -srckeypass must not be specified"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "if keystore is not password protected, then -storepass, -keypass, and -new must not be specified"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "if source keystore is not password protected, then -srcstorepass and -srckeypass must not be specified"}, + {"Illegal.startdate.value", "Illegal startdate value"}, + {"Validity.must.be.greater.than.zero", + "Validity must be greater than zero"}, + {"provName.not.a.provider", "{0} not a provider"}, + {"Usage.error.no.command.provided", "Usage error: no command provided"}, + {"Source.keystore.file.exists.but.is.empty.", "Source keystore file exists, but is empty: "}, + {"Please.specify.srckeystore", "Please specify -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "Must not specify both -v and -rfc with 'list' command"}, + {"Key.password.must.be.at.least.6.characters", + "Key password must be at least 6 characters"}, + {"New.password.must.be.at.least.6.characters", + "New password must be at least 6 characters"}, + {"Keystore.file.exists.but.is.empty.", + "Keystore file exists, but is empty: "}, + {"Keystore.file.does.not.exist.", + "Keystore file does not exist: "}, + {"Must.specify.destination.alias", "Must specify destination alias"}, + {"Must.specify.alias", "Must specify alias"}, + {"Keystore.password.must.be.at.least.6.characters", + "Keystore password must be at least 6 characters"}, + {"Enter.the.password.to.be.stored.", + "Enter the password to be stored: "}, + {"Enter.keystore.password.", "Enter keystore password: "}, + {"Enter.source.keystore.password.", "Enter source keystore password: "}, + {"Enter.destination.keystore.password.", "Enter destination keystore password: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "Keystore password is too short - must be at least 6 characters"}, + {"Unknown.Entry.Type", "Unknown Entry Type"}, + {"Too.many.failures.Alias.not.changed", "Too many failures. Alias not changed"}, + {"Entry.for.alias.alias.successfully.imported.", + "Entry for alias {0} successfully imported."}, + {"Entry.for.alias.alias.not.imported.", "Entry for alias {0} not imported."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Problem importing entry for alias {0}: {1}.\nEntry for alias {0} not imported."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Import command completed: {0} entries successfully imported, {1} entries failed or cancelled"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Warning: Overwriting existing alias {0} in destination keystore"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "Existing entry alias {0} exists, overwrite? [no]: "}, + {"Too.many.failures.try.later", "Too many failures - try later"}, + {"Certification.request.stored.in.file.filename.", + "Certification request stored in file <{0}>"}, + {"Submit.this.to.your.CA", "Submit this to your CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "if alias not specified, destalias and srckeypass must not be specified"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "The destination pkcs12 keystore has different storepass and keypass. Please retry with -destkeypass specified."}, + {"Certificate.stored.in.file.filename.", + "Certificate stored in file <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "Certificate reply was installed in keystore"}, + {"Certificate.reply.was.not.installed.in.keystore", + "Certificate reply was not installed in keystore"}, + {"Certificate.was.added.to.keystore", + "Certificate was added to keystore"}, + {"Certificate.was.not.added.to.keystore", + "Certificate was not added to keystore"}, + {".Storing.ksfname.", "[Storing {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} has no public key (certificate)"}, + {"Cannot.derive.signature.algorithm", + "Cannot derive signature algorithm"}, + {"Alias.alias.does.not.exist", + "Alias <{0}> does not exist"}, + {"Alias.alias.has.no.certificate", + "Alias <{0}> has no certificate"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Key pair not generated, alias <{0}> already exists"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Generating {0} bit {1} key pair and self-signed certificate ({2}) with a validity of {3} days\n\tfor: {4}"}, + {"Enter.key.password.for.alias.", "Enter key password for <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN if same as keystore password): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "Key password is too short - must be at least 6 characters"}, + {"Too.many.failures.key.not.added.to.keystore", + "Too many failures - key not added to keystore"}, + {"Destination.alias.dest.already.exists", + "Destination alias <{0}> already exists"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "Password is too short - must be at least 6 characters"}, + {"Too.many.failures.Key.entry.not.cloned", + "Too many failures. Key entry not cloned"}, + {"key.password.for.alias.", "key password for <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "Keystore entry for <{0}> already exists"}, + {"Creating.keystore.entry.for.id.getName.", + "Creating keystore entry for <{0}> ..."}, + {"No.entries.from.identity.database.added", + "No entries from identity database added"}, + {"Alias.name.alias", "Alias name: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Creation date: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Entry type: {0}"}, + {"Certificate.chain.length.", "Certificate chain length: "}, + {"Certificate.i.1.", "Certificate[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Certificate fingerprint (SHA1): "}, + {"Keystore.type.", "Keystore type: "}, + {"Keystore.provider.", "Keystore provider: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Your keystore contains {0,number,integer} entry"}, + {"Your.keystore.contains.keyStore.size.entries", + "Your keystore contains {0,number,integer} entries"}, + {"Failed.to.parse.input", "Failed to parse input"}, + {"Empty.input", "Empty input"}, + {"Not.X.509.certificate", "Not X.509 certificate"}, + {"alias.has.no.public.key", "{0} has no public key"}, + {"alias.has.no.X.509.certificate", "{0} has no X.509 certificate"}, + {"New.certificate.self.signed.", "New certificate (self-signed):"}, + {"Reply.has.no.certificates", "Reply has no certificates"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Certificate not imported, alias <{0}> already exists"}, + {"Input.not.an.X.509.certificate", "Input not an X.509 certificate"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "Certificate already exists in keystore under alias <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "Do you still want to add it? [no]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "Certificate already exists in system-wide CA keystore under alias <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "Do you still want to add it to your own keystore? [no]: "}, + {"Trust.this.certificate.no.", "Trust this certificate? [no]: "}, + {"YES", "YES"}, + {"New.prompt.", "New {0}: "}, + {"Passwords.must.differ", "Passwords must differ"}, + {"Re.enter.new.prompt.", "Re-enter new {0}: "}, + {"Re.enter.password.", "Re-enter password: "}, + {"Re.enter.new.password.", "Re-enter new password: "}, + {"They.don.t.match.Try.again", "They don't match. Try again"}, + {"Enter.prompt.alias.name.", "Enter {0} alias name: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Enter new alias name\t(RETURN to cancel import for this entry): "}, + {"Enter.alias.name.", "Enter alias name: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN if same as for <{0}>)"}, + {".PATTERN.printX509Cert", + "Owner: {0}\nIssuer: {1}\nSerial number: {2}\nValid from: {3} until: {4}\nCertificate fingerprints:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Signature algorithm name: {8}\n\t Version: {9}"}, + {"What.is.your.first.and.last.name.", + "What is your first and last name?"}, + {"What.is.the.name.of.your.organizational.unit.", + "What is the name of your organizational unit?"}, + {"What.is.the.name.of.your.organization.", + "What is the name of your organization?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "What is the name of your City or Locality?"}, + {"What.is.the.name.of.your.State.or.Province.", + "What is the name of your State or Province?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "What is the two-letter country code for this unit?"}, + {"Is.name.correct.", "Is {0} correct?"}, + {"no", "no"}, + {"yes", "yes"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "Alias <{0}> has no key"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "Alias <{0}> references an entry type that is not a private key entry. The -keyclone command only supports cloning of private key entries"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Signer #%d:"}, + {"Timestamp.", "Timestamp:"}, + {"Signature.", "Signature:"}, + {"CRLs.", "CRLs:"}, + {"Certificate.owner.", "Certificate owner: "}, + {"Not.a.signed.jar.file", "Not a signed jar file"}, + {"No.certificate.from.the.SSL.server", + "No certificate from the SSL server"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* The integrity of the information stored in your keystore *\n" + + "* has NOT been verified! In order to verify its integrity, *\n" + + "* you must provide your keystore password. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* The integrity of the information stored in the srckeystore*\n" + + "* has NOT been verified! In order to verify its integrity, *\n" + + "* you must provide the srckeystore password. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "Certificate reply does not contain public key for <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Incomplete certificate chain in reply"}, + {"Certificate.chain.in.reply.does.not.verify.", + "Certificate chain in reply does not verify: "}, + {"Top.level.certificate.in.reply.", + "Top-level certificate in reply:\n"}, + {".is.not.trusted.", "... is not trusted. "}, + {"Install.reply.anyway.no.", "Install reply anyway? [no]: "}, + {"NO", "NO"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "Public keys in reply and keystore don't match"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "Certificate reply and certificate in keystore are identical"}, + {"Failed.to.establish.chain.from.reply", + "Failed to establish chain from reply"}, + {"n", "n"}, + {"Wrong.answer.try.again", "Wrong answer, try again"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "Secret Key not generated, alias <{0}> already exists"}, + {"Please.provide.keysize.for.secret.key.generation", + "Please provide -keysize for secret key generation"}, + + {"verified.by.s.in.s", "Verified by %s in %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "WARNING: not verified. Make sure -keystore is correct."}, + + {"Extensions.", "Extensions: "}, + {".Empty.value.", "(Empty value)"}, + {"Extension.Request.", "Extension Request:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 Certificate Request (Version 1.0)\n" + + "Subject: %s\nPublic Key: %s format %s key\n"}, + {"Unknown.keyUsage.type.", "Unknown keyUsage type: "}, + {"Unknown.extendedkeyUsage.type.", "Unknown extendedkeyUsage type: "}, + {"Unknown.AccessDescription.type.", "Unknown AccessDescription type: "}, + {"Unrecognized.GeneralName.type.", "Unrecognized GeneralName type: "}, + {"This.extension.cannot.be.marked.as.critical.", + "This extension cannot be marked as critical. "}, + {"Odd.number.of.hex.digits.found.", "Odd number of hex digits found: "}, + {"Unknown.extension.type.", "Unknown extension type: "}, + {"command.{0}.is.ambiguous.", "command {0} is ambiguous:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_de.java b/src/sun/security/tools/keytool/Resources_de.java new file mode 100644 index 00000000..a024795d --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_de.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_de extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Optionen:"}, + {"Use.keytool.help.for.all.available.commands", + "\"keytool -help\" f\u00FCr alle verf\u00FCgbaren Befehle verwenden"}, + {"Key.and.Certificate.Management.Tool", + "Schl\u00FCssel- und Zertifikatsverwaltungstool"}, + {"Commands.", "Befehle:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "\"keytool -command_name -help\" f\u00FCr Verwendung von command_name verwenden"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Generiert eine Zertifikatanforderung"}, //-certreq + {"Changes.an.entry.s.alias", + "\u00C4ndert den Alias eines Eintrags"}, //-changealias + {"Deletes.an.entry", + "L\u00F6scht einen Eintrag"}, //-delete + {"Exports.certificate", + "Exportiert ein Zertifikat"}, //-exportcert + {"Generates.a.key.pair", + "Generiert ein Schl\u00FCsselpaar"}, //-genkeypair + {"Generates.a.secret.key", + "Generiert einen Secret Key"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Generiert ein Zertifikat aus einer Zertifikatanforderung"}, //-gencert + {"Generates.CRL", "Generiert eine CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "{0} Secret Key generiert"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "{0}-Bit {1} Secret Key generiert"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importiert Eintr\u00E4ge aus einer Identity-Datenbank im JDK 1.1.x-Stil"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importiert ein Zertifikat oder eine Zertifikatskette"}, //-importcert + {"Imports.a.password", + "Importiert ein Kennwort"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importiert einen oder alle Eintr\u00E4ge aus einem anderen Keystore"}, //-importkeystore + {"Clones.a.key.entry", + "Clont einen Schl\u00FCsseleintrag"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u00C4ndert das Schl\u00FCsselkennwort eines Eintrags"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Listet die Eintr\u00E4ge in einem Keystore auf"}, //-list + {"Prints.the.content.of.a.certificate", + "Druckt den Content eines Zertifikats"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Druckt den Content einer Zertifikatanforderung"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Druckt den Content einer CRL-Datei"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Generiert ein selbst signiertes Zertifikat"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u00C4ndert das Speicherkennwort eines Keystores"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "Aliasname des zu verarbeitenden Eintrags"}, //-alias + {"destination.alias", + "Zielalias"}, //-destalias + {"destination.key.password", + "Zielschl\u00FCssel-Kennwort"}, //-destkeypass + {"destination.keystore.name", + "Ziel-Keystore-Name"}, //-destkeystore + {"destination.keystore.password.protected", + "Ziel-Keystore kennwortgesch\u00FCtzt"}, //-destprotected + {"destination.keystore.provider.name", + "Ziel-Keystore-Providername"}, //-destprovidername + {"destination.keystore.password", + "Ziel-Keystore-Kennwort"}, //-deststorepass + {"destination.keystore.type", + "Ziel-Keystore-Typ"}, //-deststoretype + {"distinguished.name", + "Distinguished Name"}, //-dname + {"X.509.extension", + "X.509-Erweiterung"}, //-ext + {"output.file.name", + "Ausgabedateiname"}, //-file and -outfile + {"input.file.name", + "Eingabedateiname"}, //-file and -infile + {"key.algorithm.name", + "Schl\u00FCsselalgorithmusname"}, //-keyalg + {"key.password", + "Schl\u00FCsselkennwort"}, //-keypass + {"key.bit.size", + "Schl\u00FCsselbitgr\u00F6\u00DFe"}, //-keysize + {"keystore.name", + "Keystore-Name"}, //-keystore + {"new.password", + "Neues Kennwort"}, //-new + {"do.not.prompt", + "Kein Prompt"}, //-noprompt + {"password.through.protected.mechanism", + "Kennwort \u00FCber gesch\u00FCtzten Mechanismus"}, //-protected + {"provider.argument", + "Providerargument"}, //-providerarg + {"provider.class.name", + "Providerklassenname"}, //-providerclass + {"provider.name", + "Providername"}, //-providername + {"provider.classpath", + "Provider-Classpath"}, //-providerpath + {"output.in.RFC.style", + "Ausgabe in RFC-Stil"}, //-rfc + {"signature.algorithm.name", + "Signaturalgorithmusname"}, //-sigalg + {"source.alias", + "Quellalias"}, //-srcalias + {"source.key.password", + "Quellschl\u00FCssel-Kennwort"}, //-srckeypass + {"source.keystore.name", + "Quell-Keystore-Name"}, //-srckeystore + {"source.keystore.password.protected", + "Quell-Keystore kennwortgesch\u00FCtzt"}, //-srcprotected + {"source.keystore.provider.name", + "Quell-Keystore-Providername"}, //-srcprovidername + {"source.keystore.password", + "Quell-Keystore-Kennwort"}, //-srcstorepass + {"source.keystore.type", + "Quell-Keystore-Typ"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL-Serverhost und -port"}, //-sslserver + {"signed.jar.file", + "Signierte JAR-Datei"}, //=jarfile + {"certificate.validity.start.date.time", + "Anfangsdatum/-zeit f\u00FCr Zertifikatsg\u00FCltigkeit"}, //-startdate + {"keystore.password", + "Keystore-Kennwort"}, //-storepass + {"keystore.type", + "Keystore-Typ"}, //-storetype + {"trust.certificates.from.cacerts", + "Zertifikaten aus cacerts vertrauen"}, //-trustcacerts + {"verbose.output", + "Verbose-Ausgabe"}, //-v + {"validity.number.of.days", + "G\u00FCltigkeitsdauer (Tage)"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "Serielle ID des zu entziehenden Certs"}, //-id + // keytool: Running part + {"keytool.error.", "Keytool-Fehler: "}, + {"Illegal.option.", "Ung\u00FCltige Option: "}, + {"Illegal.value.", "Ung\u00FCltiger Wert: "}, + {"Unknown.password.type.", "Unbekannter Kennworttyp: "}, + {"Cannot.find.environment.variable.", + "Umgebungsvariable kann nicht gefunden werden: "}, + {"Cannot.find.file.", "Datei kann nicht gefunden werden: "}, + {"Command.option.flag.needs.an.argument.", "Befehlsoption {0} ben\u00F6tigt ein Argument."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Warnung: Keine Unterst\u00FCtzung f\u00FCr unterschiedliche Speicher- und Schl\u00FCsselkennw\u00F6rter bei PKCS12 KeyStores. Der benutzerdefinierte Wert {0} wird ignoriert."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore muss NONE sein, wenn -storetype {0} ist"}, + {"Too.many.retries.program.terminated", + "Zu viele erneute Versuche. Programm wird beendet"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "Befehle -storepasswd und -keypasswd werden nicht unterst\u00FCtzt, wenn -storetype {0} ist"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "Befehle des Typs -keypasswd werden nicht unterst\u00FCtzt, wenn -storetype PKCS12 ist"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-keypass und -new k\u00F6nnen nicht angegeben werden, wenn -storetype {0} ist"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "Wenn -protected angegeben ist, d\u00FCrfen -storepass, -keypass und -new nicht angegeben werden"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Wenn -srcprotected angegeben ist, d\u00FCrfen -srcstorepass und -srckeypass nicht angegeben werden"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "Wenn der Keystore nicht kennwortgesch\u00FCtzt ist, d\u00FCrfen -storepass, -keypass und -new nicht angegeben werden"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Wenn der Quell-Keystore nicht kennwortgesch\u00FCtzt ist, d\u00FCrfen -srcstorepass und -srckeypass nicht angegeben werden"}, + {"Illegal.startdate.value", "Ung\u00FCltiger Wert f\u00FCr Anfangsdatum"}, + {"Validity.must.be.greater.than.zero", + "G\u00FCltigkeit muss gr\u00F6\u00DFer als null sein"}, + {"provName.not.a.provider", "{0} kein Provider"}, + {"Usage.error.no.command.provided", "Verwendungsfehler: Kein Befehl angegeben"}, + {"Source.keystore.file.exists.but.is.empty.", "Quell-Keystore-Datei ist zwar vorhanden, ist aber leer: "}, + {"Please.specify.srckeystore", "Geben Sie -srckeystore an"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "-v und -rfc d\u00FCrfen bei Befehl \"list\" nicht beide angegeben werden"}, + {"Key.password.must.be.at.least.6.characters", + "Schl\u00FCsselkennwort muss mindestens sechs Zeichen lang sein"}, + {"New.password.must.be.at.least.6.characters", + "Neues Kennwort muss mindestens sechs Zeichen lang sein"}, + {"Keystore.file.exists.but.is.empty.", + "Keystore-Datei ist vorhanden, ist aber leer: "}, + {"Keystore.file.does.not.exist.", + "Keystore-Datei ist nicht vorhanden: "}, + {"Must.specify.destination.alias", "Sie m\u00FCssen einen Zielalias angeben"}, + {"Must.specify.alias", "Sie m\u00FCssen einen Alias angeben"}, + {"Keystore.password.must.be.at.least.6.characters", + "Keystore-Kennwort muss mindestens sechs Zeichen lang sein"}, + {"Enter.the.password.to.be.stored.", + "Geben Sie das Kennwort ein, das gespeichert werden soll: "}, + {"Enter.keystore.password.", "Keystore-Kennwort eingeben: "}, + {"Enter.source.keystore.password.", "Quell-Keystore-Kennwort eingeben: "}, + {"Enter.destination.keystore.password.", "Ziel-Keystore-Kennwort eingeben: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "Keystore-Kennwort ist zu kurz. Es muss mindestens sechs Zeichen lang sein"}, + {"Unknown.Entry.Type", "Unbekannter Eintragstyp"}, + {"Too.many.failures.Alias.not.changed", "Zu viele Fehler. Alias nicht ge\u00E4ndert"}, + {"Entry.for.alias.alias.successfully.imported.", + "Eintrag f\u00FCr Alias {0} erfolgreich importiert."}, + {"Entry.for.alias.alias.not.imported.", "Eintrag f\u00FCr Alias {0} nicht importiert."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Problem beim Importieren des Eintrags f\u00FCr Alias {0}: {1}.\nEintrag f\u00FCr Alias {0} nicht importiert."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Importbefehl abgeschlossen: {0} Eintr\u00E4ge erfolgreich importiert, {1} Eintr\u00E4ge nicht erfolgreich oder abgebrochen"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Warnung: Vorhandener Alias {0} in Ziel-Keystore wird \u00FCberschrieben"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "Eintragsalias {0} ist bereits vorhanden. \u00DCberschreiben? [Nein]: "}, + {"Too.many.failures.try.later", "Zu viele Fehler. Versuchen Sie es sp\u00E4ter erneut"}, + {"Certification.request.stored.in.file.filename.", + "Zertifizierungsanforderung in Datei <{0}> gespeichert"}, + {"Submit.this.to.your.CA", "Leiten Sie dies an die CA weiter"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "Wenn kein Alias angegeben ist, d\u00FCrfen destalias und srckeypass nicht angegeben werden"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "Der Ziel-Keystore pkcs12 hat unterschiedliche Kennw\u00F6rter f\u00FCr storepass und keypass. Wiederholen Sie den Vorgang, indem Sie -destkeypass angeben."}, + {"Certificate.stored.in.file.filename.", + "Zertifikat in Datei <{0}> gespeichert"}, + {"Certificate.reply.was.installed.in.keystore", + "Zertifikatantwort wurde in Keystore installiert"}, + {"Certificate.reply.was.not.installed.in.keystore", + "Zertifikatantwort wurde nicht in Keystore installiert"}, + {"Certificate.was.added.to.keystore", + "Zertifikat wurde Keystore hinzugef\u00FCgt"}, + {"Certificate.was.not.added.to.keystore", + "Zertifikat wurde nicht zu Keystore hinzugef\u00FCgt"}, + {".Storing.ksfname.", "[{0} wird gesichert]"}, + {"alias.has.no.public.key.certificate.", + "{0} hat keinen Public Key (Zertifikat)"}, + {"Cannot.derive.signature.algorithm", + "Signaturalgorithmus kann nicht abgeleitet werden"}, + {"Alias.alias.does.not.exist", + "Alias <{0}> ist nicht vorhanden"}, + {"Alias.alias.has.no.certificate", + "Alias <{0}> hat kein Zertifikat"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Schl\u00FCsselpaar wurde nicht generiert. Alias <{0}> ist bereits vorhanden"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Generieren von Schl\u00FCsselpaar (Typ {1}, {0} Bit) und selbst signiertem Zertifikat ({2}) mit einer G\u00FCltigkeit von {3} Tagen\n\tf\u00FCr: {4}"}, + {"Enter.key.password.for.alias.", "Schl\u00FCsselkennwort f\u00FCr <{0}> eingeben"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN, wenn identisch mit Keystore-Kennwort): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "Schl\u00FCsselkennwort ist zu kurz. Es muss mindestens sechs Zeichen lang sein"}, + {"Too.many.failures.key.not.added.to.keystore", + "Zu viele Fehler. Schl\u00FCssel wurde nicht zu Keystore hinzugef\u00FCgt"}, + {"Destination.alias.dest.already.exists", + "Zielalias <{0}> bereits vorhanden"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "Kennwort ist zu kurz. Es muss mindestens sechs Zeichen lang sein"}, + {"Too.many.failures.Key.entry.not.cloned", + "Zu viele Fehler. Schl\u00FCsseleintrag wurde nicht geclont"}, + {"key.password.for.alias.", "Schl\u00FCsselkennwort f\u00FCr <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "Keystore-Eintrag f\u00FCr <{0}> bereits vorhanden"}, + {"Creating.keystore.entry.for.id.getName.", + "Keystore-Eintrag f\u00FCr <{0}> wird erstellt..."}, + {"No.entries.from.identity.database.added", + "Keine Eintr\u00E4ge aus Identity-Datenbank hinzugef\u00FCgt"}, + {"Alias.name.alias", "Aliasname: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Erstellungsdatum: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Eintragstyp: {0}"}, + {"Certificate.chain.length.", "Zertifikatskettenl\u00E4nge: "}, + {"Certificate.i.1.", "Zertifikat[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Zertifikat-Fingerprint (SHA1): "}, + {"Keystore.type.", "Keystore-Typ: "}, + {"Keystore.provider.", "Keystore-Provider: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Keystore enth\u00E4lt {0,number,integer} Eintrag"}, + {"Your.keystore.contains.keyStore.size.entries", + "Keystore enth\u00E4lt {0,number,integer} Eintr\u00E4ge"}, + {"Failed.to.parse.input", "Eingabe konnte nicht geparst werden"}, + {"Empty.input", "Leere Eingabe"}, + {"Not.X.509.certificate", "Kein X.509-Zertifikat"}, + {"alias.has.no.public.key", "{0} hat keinen Public Key"}, + {"alias.has.no.X.509.certificate", "{0} hat kein X.509-Zertifikat"}, + {"New.certificate.self.signed.", "Neues Zertifikat (selbst signiert):"}, + {"Reply.has.no.certificates", "Antwort hat keine Zertifikate"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Zertifikat nicht importiert. Alias <{0}> ist bereits vorhanden"}, + {"Input.not.an.X.509.certificate", "Eingabe kein X.509-Zertifikat"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "Zertifikat ist bereits unter Alias <{0}> im Keystore vorhanden"}, + {"Do.you.still.want.to.add.it.no.", + "M\u00F6chten Sie es trotzdem hinzuf\u00FCgen? [Nein]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "Zertifikat ist bereits unter Alias <{0}> im systemweiten CA-Keystore vorhanden"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "M\u00F6chten Sie es trotzdem zu Ihrem eigenen Keystore hinzuf\u00FCgen? [Nein]: "}, + {"Trust.this.certificate.no.", "Diesem Zertifikat vertrauen? [Nein]: "}, + {"YES", "JA"}, + {"New.prompt.", "Neues {0}: "}, + {"Passwords.must.differ", "Kennw\u00F6rter m\u00FCssen sich unterscheiden"}, + {"Re.enter.new.prompt.", "Neues {0} erneut eingeben: "}, + {"Re.enter.password.", "Geben Sie das Kennwort erneut ein: "}, + {"Re.enter.new.password.", "Neues Kennwort erneut eingeben: "}, + {"They.don.t.match.Try.again", "Keine \u00DCbereinstimmung. Wiederholen Sie den Vorgang"}, + {"Enter.prompt.alias.name.", "{0}-Aliasnamen eingeben: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Geben Sie einen neuen Aliasnamen ein\t(RETURN, um den Import dieses Eintrags abzubrechen): "}, + {"Enter.alias.name.", "Aliasnamen eingeben: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN, wenn identisch mit <{0}>)"}, + {".PATTERN.printX509Cert", + "Eigent\u00FCmer: {0}\nAussteller: {1}\nSeriennummer: {2}\nG\u00FCltig von: {3} bis: {4}\nZertifikat-Fingerprints:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Signaturalgorithmusname: {8}\n\t Version: {9}"}, + {"What.is.your.first.and.last.name.", + "Wie lautet Ihr Vor- und Nachname?"}, + {"What.is.the.name.of.your.organizational.unit.", + "Wie lautet der Name Ihrer organisatorischen Einheit?"}, + {"What.is.the.name.of.your.organization.", + "Wie lautet der Name Ihrer Organisation?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "Wie lautet der Name Ihrer Stadt oder Gemeinde?"}, + {"What.is.the.name.of.your.State.or.Province.", + "Wie lautet der Name Ihres Bundeslands?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "Wie lautet der L\u00E4ndercode (zwei Buchstaben) f\u00FCr diese Einheit?"}, + {"Is.name.correct.", "Ist {0} richtig?"}, + {"no", "Nein"}, + {"yes", "Ja"}, + {"y", "J"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "Alias <{0}> verf\u00FCgt \u00FCber keinen Schl\u00FCssel"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "Alias <{0}> verweist auf einen Eintragstyp, der kein Private Key-Eintrag ist. Der Befehl -keyclone unterst\u00FCtzt nur das Clonen von Private Key-Eintr\u00E4gen"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Signaturgeber #%d:"}, + {"Timestamp.", "Zeitstempel:"}, + {"Signature.", "Signatur:"}, + {"CRLs.", "CRLs:"}, + {"Certificate.owner.", "Zertifikateigent\u00FCmer: "}, + {"Not.a.signed.jar.file", "Keine signierte JAR-Datei"}, + {"No.certificate.from.the.SSL.server", + "Kein Zertifikat vom SSL-Server"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* Die Integrit\u00E4t der Informationen, die in Ihrem Keystore gespeichert sind, *\n* wurde NICHT gepr\u00FCft. Um die Integrit\u00E4t zu pr\u00FCfen, *\n* m\u00FCssen Sie Ihr Keystore-Kennwort angeben. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* Die Integrit\u00E4t der Informationen, die in Ihrem Srckeystore gespeichert sind, *\n* wurde NICHT gepr\u00FCft. Um die Integrit\u00E4t zu pr\u00FCfen, *\n* m\u00FCssen Sie Ihr Srckeystore-Kennwort angeben. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "Zertifikatantwort enth\u00E4lt keinen Public Key f\u00FCr <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Unvollst\u00E4ndige Zertifikatskette in Antwort"}, + {"Certificate.chain.in.reply.does.not.verify.", + "Zertifikatskette in Antwort verifiziert nicht: "}, + {"Top.level.certificate.in.reply.", + "Zertifikat der obersten Ebene in Antwort:\n"}, + {".is.not.trusted.", "... ist nicht vertrauensw\u00FCrdig. "}, + {"Install.reply.anyway.no.", "Antwort trotzdem installieren? [Nein]: "}, + {"NO", "NEIN"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "Public Keys in Antwort und Keystore stimmen nicht \u00FCberein"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "Zertifikatantwort und Zertifikat in Keystore sind identisch"}, + {"Failed.to.establish.chain.from.reply", + "Kette konnte der Antwort nicht entnommen werden"}, + {"n", "N"}, + {"Wrong.answer.try.again", "Falsche Antwort. Wiederholen Sie den Vorgang"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "Secret Key wurde nicht generiert. Alias <{0}> ist bereits vorhanden"}, + {"Please.provide.keysize.for.secret.key.generation", + "Geben Sie -keysize zum Erstellen eines Secret Keys an"}, + + {"verified.by.s.in.s", "Gepr\u00FCft von %s in %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "WARNUNG: Nicht gepr\u00FCft. Stellen Sie sicher, dass -keystore korrekt ist."}, + + {"Extensions.", "Erweiterungen: "}, + {".Empty.value.", "(Leerer Wert)"}, + {"Extension.Request.", "Erweiterungsanforderung:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10-Zertifikatanforderung (Version 1.0)\nSubjekt: %s\nPublic Key: %s Format %s Schl\u00FCssel\n"}, + {"Unknown.keyUsage.type.", "Unbekannter keyUsage-Typ: "}, + {"Unknown.extendedkeyUsage.type.", "Unbekannter extendedkeyUsage-Typ: "}, + {"Unknown.AccessDescription.type.", "Unbekannter AccessDescription-Typ: "}, + {"Unrecognized.GeneralName.type.", "Unbekannter GeneralName-Typ: "}, + {"This.extension.cannot.be.marked.as.critical.", + "Erweiterung kann nicht als \"Kritisch\" markiert werden. "}, + {"Odd.number.of.hex.digits.found.", "Ungerade Anzahl hexadezimaler Ziffern gefunden: "}, + {"Unknown.extension.type.", "Unbekannter Erweiterungstyp: "}, + {"command.{0}.is.ambiguous.", "Befehl {0} ist mehrdeutig:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_es.java b/src/sun/security/tools/keytool/Resources_es.java new file mode 100644 index 00000000..5348d1a1 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_es.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_es extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Opciones:"}, + {"Use.keytool.help.for.all.available.commands", + "Utilice\"keytool -help\" para todos los comandos disponibles"}, + {"Key.and.Certificate.Management.Tool", + "Herramienta de Gesti\u00F3n de Certificados y Claves"}, + {"Commands.", "Comandos:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "Utilice \"keytool -command_name -help\" para la sintaxis de nombre_comando"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Genera una solicitud de certificado"}, //-certreq + {"Changes.an.entry.s.alias", + "Cambia un alias de entrada"}, //-changealias + {"Deletes.an.entry", + "Suprime una entrada"}, //-delete + {"Exports.certificate", + "Exporta el certificado"}, //-exportcert + {"Generates.a.key.pair", + "Genera un par de claves"}, //-genkeypair + {"Generates.a.secret.key", + "Genera un clave secreta"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Genera un certificado a partir de una solicitud de certificado"}, //-gencert + {"Generates.CRL", "Genera CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Clave secreta {0} generada"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Clave secreta {1} de {0} bits generada"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importa entradas desde una base de datos de identidades JDK 1.1.x-style"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importa un certificado o una cadena de certificados"}, //-importcert + {"Imports.a.password", + "Importa una contrase\u00F1a"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importa una o todas las entradas desde otro almac\u00E9n de claves"}, //-importkeystore + {"Clones.a.key.entry", + "Clona una entrada de clave"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "Cambia la contrase\u00F1a de clave de una entrada"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Enumera las entradas de un almac\u00E9n de claves"}, //-list + {"Prints.the.content.of.a.certificate", + "Imprime el contenido de un certificado"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Imprime el contenido de una solicitud de certificado"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Imprime el contenido de un archivo CRL"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Genera un certificado autofirmado"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "Cambia la contrase\u00F1a de almac\u00E9n de un almac\u00E9n de claves"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "nombre de alias de la entrada que se va a procesar"}, //-alias + {"destination.alias", + "alias de destino"}, //-destalias + {"destination.key.password", + "contrase\u00F1a de clave de destino"}, //-destkeypass + {"destination.keystore.name", + "nombre de almac\u00E9n de claves de destino"}, //-destkeystore + {"destination.keystore.password.protected", + "almac\u00E9n de claves de destino protegido por contrase\u00F1a"}, //-destprotected + {"destination.keystore.provider.name", + "nombre de proveedor de almac\u00E9n de claves de destino"}, //-destprovidername + {"destination.keystore.password", + "contrase\u00F1a de almac\u00E9n de claves de destino"}, //-deststorepass + {"destination.keystore.type", + "tipo de almac\u00E9n de claves de destino"}, //-deststoretype + {"distinguished.name", + "nombre distintivo"}, //-dname + {"X.509.extension", + "extensi\u00F3n X.509"}, //-ext + {"output.file.name", + "nombre de archivo de salida"}, //-file and -outfile + {"input.file.name", + "nombre de archivo de entrada"}, //-file and -infile + {"key.algorithm.name", + "nombre de algoritmo de clave"}, //-keyalg + {"key.password", + "contrase\u00F1a de clave"}, //-keypass + {"key.bit.size", + "tama\u00F1o de bit de clave"}, //-keysize + {"keystore.name", + "nombre de almac\u00E9n de claves"}, //-keystore + {"new.password", + "nueva contrase\u00F1a"}, //-new + {"do.not.prompt", + "no solicitar"}, //-noprompt + {"password.through.protected.mechanism", + "contrase\u00F1a a trav\u00E9s de mecanismo protegido"}, //-protected + {"provider.argument", + "argumento del proveedor"}, //-providerarg + {"provider.class.name", + "nombre de clase del proveedor"}, //-providerclass + {"provider.name", + "nombre del proveedor"}, //-providername + {"provider.classpath", + "classpath de proveedor"}, //-providerpath + {"output.in.RFC.style", + "salida en estilo RFC"}, //-rfc + {"signature.algorithm.name", + "nombre de algoritmo de firma"}, //-sigalg + {"source.alias", + "alias de origen"}, //-srcalias + {"source.key.password", + "contrase\u00F1a de clave de origen"}, //-srckeypass + {"source.keystore.name", + "nombre de almac\u00E9n de claves de origen"}, //-srckeystore + {"source.keystore.password.protected", + "almac\u00E9n de claves de origen protegido por contrase\u00F1a"}, //-srcprotected + {"source.keystore.provider.name", + "nombre de proveedor de almac\u00E9n de claves de origen"}, //-srcprovidername + {"source.keystore.password", + "contrase\u00F1a de almac\u00E9n de claves de origen"}, //-srcstorepass + {"source.keystore.type", + "tipo de almac\u00E9n de claves de origen"}, //-srcstoretype + {"SSL.server.host.and.port", + "puerto y host del servidor SSL"}, //-sslserver + {"signed.jar.file", + "archivo jar firmado"}, //=jarfile + {"certificate.validity.start.date.time", + "fecha/hora de inicio de validez del certificado"}, //-startdate + {"keystore.password", + "contrase\u00F1a de almac\u00E9n de claves"}, //-storepass + {"keystore.type", + "tipo de almac\u00E9n de claves"}, //-storetype + {"trust.certificates.from.cacerts", + "certificados de protecci\u00F3n de cacerts"}, //-trustcacerts + {"verbose.output", + "salida detallada"}, //-v + {"validity.number.of.days", + "n\u00FAmero de validez de d\u00EDas"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "identificador de serie del certificado que se va a revocar"}, //-id + // keytool: Running part + {"keytool.error.", "error de herramienta de claves: "}, + {"Illegal.option.", "Opci\u00F3n no permitida: "}, + {"Illegal.value.", "Valor no permitido: "}, + {"Unknown.password.type.", "Tipo de contrase\u00F1a desconocido: "}, + {"Cannot.find.environment.variable.", + "No se ha encontrado la variable del entorno: "}, + {"Cannot.find.file.", "No se ha encontrado el archivo: "}, + {"Command.option.flag.needs.an.argument.", "La opci\u00F3n de comando {0} necesita un argumento."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Advertencia: los almacenes de claves en formato PKCS12 no admiten contrase\u00F1as de clave y almacenamiento distintas. Se ignorar\u00E1 el valor especificado por el usuario, {0}."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore debe ser NONE si -storetype es {0}"}, + {"Too.many.retries.program.terminated", + "Ha habido demasiados intentos, se ha cerrado el programa"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "Los comandos -storepasswd y -keypasswd no est\u00E1n soportados si -storetype es {0}"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "Los comandos -keypasswd no est\u00E1n soportados si -storetype es PKCS12"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-keypass y -new no se pueden especificar si -storetype es {0}"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "si se especifica -protected, no deben especificarse -storepass, -keypass ni -new"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Si se especifica -srcprotected, no se puede especificar -srcstorepass ni -srckeypass"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "Si keystore no est\u00E1 protegido por contrase\u00F1a, no se deben especificar -storepass, -keypass ni -new"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Si el almac\u00E9n de claves de origen no est\u00E1 protegido por contrase\u00F1a, no se deben especificar -srcstorepass ni -srckeypass"}, + {"Illegal.startdate.value", "Valor de fecha de inicio no permitido"}, + {"Validity.must.be.greater.than.zero", + "La validez debe ser mayor que cero"}, + {"provName.not.a.provider", "{0} no es un proveedor"}, + {"Usage.error.no.command.provided", "Error de sintaxis: no se ha proporcionado ning\u00FAn comando"}, + {"Source.keystore.file.exists.but.is.empty.", "El archivo de almac\u00E9n de claves de origen existe, pero est\u00E1 vac\u00EDo: "}, + {"Please.specify.srckeystore", "Especifique -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "No se deben especificar -v y -rfc simult\u00E1neamente con el comando 'list'"}, + {"Key.password.must.be.at.least.6.characters", + "La contrase\u00F1a de clave debe tener al menos 6 caracteres"}, + {"New.password.must.be.at.least.6.characters", + "La nueva contrase\u00F1a debe tener al menos 6 caracteres"}, + {"Keystore.file.exists.but.is.empty.", + "El archivo de almac\u00E9n de claves existe, pero est\u00E1 vac\u00EDo: "}, + {"Keystore.file.does.not.exist.", + "El archivo de almac\u00E9n de claves no existe: "}, + {"Must.specify.destination.alias", "Se debe especificar un alias de destino"}, + {"Must.specify.alias", "Se debe especificar un alias"}, + {"Keystore.password.must.be.at.least.6.characters", + "La contrase\u00F1a del almac\u00E9n de claves debe tener al menos 6 caracteres"}, + {"Enter.the.password.to.be.stored.", + "Introduzca la contrase\u00F1a que se va a almacenar: "}, + {"Enter.keystore.password.", "Introduzca la contrase\u00F1a del almac\u00E9n de claves: "}, + {"Enter.source.keystore.password.", "Introduzca la contrase\u00F1a de almac\u00E9n de claves de origen: "}, + {"Enter.destination.keystore.password.", "Introduzca la contrase\u00F1a de almac\u00E9n de claves de destino: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "La contrase\u00F1a del almac\u00E9n de claves es demasiado corta, debe tener al menos 6 caracteres"}, + {"Unknown.Entry.Type", "Tipo de Entrada Desconocido"}, + {"Too.many.failures.Alias.not.changed", "Demasiados fallos. No se ha cambiado el alias"}, + {"Entry.for.alias.alias.successfully.imported.", + "La entrada del alias {0} se ha importado correctamente."}, + {"Entry.for.alias.alias.not.imported.", "La entrada del alias {0} no se ha importado."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Problema al importar la entrada del alias {0}: {1}.\nNo se ha importado la entrada del alias {0}."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Comando de importaci\u00F3n completado: {0} entradas importadas correctamente, {1} entradas incorrectas o canceladas"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Advertencia: se sobrescribir\u00E1 el alias {0} en el almac\u00E9n de claves de destino"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "El alias de entrada existente {0} ya existe, \u00BFdesea sobrescribirlo? [no]: "}, + {"Too.many.failures.try.later", "Demasiados fallos; int\u00E9ntelo m\u00E1s adelante"}, + {"Certification.request.stored.in.file.filename.", + "Solicitud de certificaci\u00F3n almacenada en el archivo <{0}>"}, + {"Submit.this.to.your.CA", "Enviar a la CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "si no se especifica el alias, no se debe especificar destalias ni srckeypass"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "El almac\u00E9n de claves pkcs12 de destino tiene storepass y keypass diferentes. Vuelva a intentarlo con -destkeypass especificado."}, + {"Certificate.stored.in.file.filename.", + "Certificado almacenado en el archivo <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "Se ha instalado la respuesta del certificado en el almac\u00E9n de claves"}, + {"Certificate.reply.was.not.installed.in.keystore", + "No se ha instalado la respuesta del certificado en el almac\u00E9n de claves"}, + {"Certificate.was.added.to.keystore", + "Se ha agregado el certificado al almac\u00E9n de claves"}, + {"Certificate.was.not.added.to.keystore", + "No se ha agregado el certificado al almac\u00E9n de claves"}, + {".Storing.ksfname.", "[Almacenando {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} no tiene clave p\u00FAblica (certificado)"}, + {"Cannot.derive.signature.algorithm", + "No se puede derivar el algoritmo de firma"}, + {"Alias.alias.does.not.exist", + "El alias <{0}> no existe"}, + {"Alias.alias.has.no.certificate", + "El alias <{0}> no tiene certificado"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "No se ha generado el par de claves, el alias <{0}> ya existe"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Generando par de claves {1} de {0} bits para certificado autofirmado ({2}) con una validez de {3} d\u00EDas\n\tpara: {4}"}, + {"Enter.key.password.for.alias.", "Introduzca la contrase\u00F1a de clave para <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(INTRO si es la misma contrase\u00F1a que la del almac\u00E9n de claves): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "La contrase\u00F1a de clave es demasiado corta; debe tener al menos 6 caracteres"}, + {"Too.many.failures.key.not.added.to.keystore", + "Demasiados fallos; no se ha agregado la clave al almac\u00E9n de claves"}, + {"Destination.alias.dest.already.exists", + "El alias de destino <{0}> ya existe"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "La contrase\u00F1a es demasiado corta; debe tener al menos 6 caracteres"}, + {"Too.many.failures.Key.entry.not.cloned", + "Demasiados fallos. No se ha clonado la entrada de clave"}, + {"key.password.for.alias.", "contrase\u00F1a de clave para <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "La entrada de almac\u00E9n de claves para <{0}> ya existe"}, + {"Creating.keystore.entry.for.id.getName.", + "Creando entrada de almac\u00E9n de claves para <{0}> ..."}, + {"No.entries.from.identity.database.added", + "No se han agregado entradas de la base de datos de identidades"}, + {"Alias.name.alias", "Nombre de Alias: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Fecha de Creaci\u00F3n: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Tipo de Entrada: {0}"}, + {"Certificate.chain.length.", "Longitud de la Cadena de Certificado: "}, + {"Certificate.i.1.", "Certificado[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Huella Digital de Certificado (SHA1): "}, + {"Keystore.type.", "Tipo de Almac\u00E9n de Claves: "}, + {"Keystore.provider.", "Proveedor de Almac\u00E9n de Claves: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Su almac\u00E9n de claves contiene {0,number,integer} entrada"}, + {"Your.keystore.contains.keyStore.size.entries", + "Su almac\u00E9n de claves contiene {0,number,integer} entradas"}, + {"Failed.to.parse.input", "Fallo al analizar la entrada"}, + {"Empty.input", "Entrada vac\u00EDa"}, + {"Not.X.509.certificate", "No es un certificado X.509"}, + {"alias.has.no.public.key", "{0} no tiene clave p\u00FAblica"}, + {"alias.has.no.X.509.certificate", "{0} no tiene certificado X.509"}, + {"New.certificate.self.signed.", "Nuevo Certificado (Autofirmado):"}, + {"Reply.has.no.certificates", "La respuesta no tiene certificados"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Certificado no importado, el alias <{0}> ya existe"}, + {"Input.not.an.X.509.certificate", "La entrada no es un certificado X.509"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "El certificado ya existe en el almac\u00E9n de claves con el alias <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "\u00BFA\u00FAn desea agregarlo? [no]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "El certificado ya existe en el almac\u00E9n de claves de la CA del sistema, con el alias <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\u00BFA\u00FAn desea agregarlo a su propio almac\u00E9n de claves? [no]: "}, + {"Trust.this.certificate.no.", "\u00BFConfiar en este certificado? [no]: "}, + {"YES", "S\u00CD"}, + {"New.prompt.", "Nuevo {0}: "}, + {"Passwords.must.differ", "Las contrase\u00F1as deben ser distintas"}, + {"Re.enter.new.prompt.", "Vuelva a escribir el nuevo {0}: "}, + {"Re.enter.password.", "Vuelva a introducir la contrase\u00F1a: "}, + {"Re.enter.new.password.", "Volver a escribir la contrase\u00F1a nueva: "}, + {"They.don.t.match.Try.again", "No coinciden. Int\u00E9ntelo de nuevo"}, + {"Enter.prompt.alias.name.", "Escriba el nombre de alias de {0}: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Indique el nuevo nombre de alias\t(INTRO para cancelar la importaci\u00F3n de esta entrada): "}, + {"Enter.alias.name.", "Introduzca el nombre de alias: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(INTRO si es el mismo que para <{0}>)"}, + {".PATTERN.printX509Cert", + "Propietario: {0}\nEmisor: {1}\nN\u00FAmero de serie: {2}\nV\u00E1lido desde: {3} hasta: {4}\nHuellas digitales del Certificado:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Nombre del Algoritmo de Firma: {8}\n\t Versi\u00F3n: {9}"}, + {"What.is.your.first.and.last.name.", + "\u00BFCu\u00E1les son su nombre y su apellido?"}, + {"What.is.the.name.of.your.organizational.unit.", + "\u00BFCu\u00E1l es el nombre de su unidad de organizaci\u00F3n?"}, + {"What.is.the.name.of.your.organization.", + "\u00BFCu\u00E1l es el nombre de su organizaci\u00F3n?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "\u00BFCu\u00E1l es el nombre de su ciudad o localidad?"}, + {"What.is.the.name.of.your.State.or.Province.", + "\u00BFCu\u00E1l es el nombre de su estado o provincia?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\u00BFCu\u00E1l es el c\u00F3digo de pa\u00EDs de dos letras de la unidad?"}, + {"Is.name.correct.", "\u00BFEs correcto {0}?"}, + {"no", "no"}, + {"yes", "s\u00ED"}, + {"y", "s"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "El alias <{0}> no tiene clave"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "El alias <{0}> hace referencia a un tipo de entrada que no es una clave privada. El comando -keyclone s\u00F3lo permite la clonaci\u00F3n de entradas de claves privadas"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "#%d de Firmante:"}, + {"Timestamp.", "Registro de Hora:"}, + {"Signature.", "Firma:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "Propietario del Certificado: "}, + {"Not.a.signed.jar.file", "No es un archivo jar firmado"}, + {"No.certificate.from.the.SSL.server", + "Ning\u00FAn certificado del servidor SSL"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* La integridad de la informaci\u00F3n almacenada en el almac\u00E9n de claves *\n* NO se ha comprobado. Para comprobar dicha integridad, *\n* debe proporcionar la contrase\u00F1a del almac\u00E9n de claves. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* La integridad de la informaci\u00F3n almacenada en srckeystore*\n* NO se ha comprobado. Para comprobar dicha integridad, *\n* debe proporcionar la contrase\u00F1a de srckeystore. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "La respuesta de certificado no contiene una clave p\u00FAblica para <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Cadena de certificado incompleta en la respuesta"}, + {"Certificate.chain.in.reply.does.not.verify.", + "La cadena de certificado de la respuesta no verifica: "}, + {"Top.level.certificate.in.reply.", + "Certificado de nivel superior en la respuesta:\n"}, + {".is.not.trusted.", "... no es de confianza. "}, + {"Install.reply.anyway.no.", "\u00BFInstalar respuesta de todos modos? [no]: "}, + {"NO", "NO"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "Las claves p\u00FAblicas en la respuesta y en el almac\u00E9n de claves no coinciden"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "La respuesta del certificado y el certificado en el almac\u00E9n de claves son id\u00E9nticos"}, + {"Failed.to.establish.chain.from.reply", + "No se ha podido definir una cadena a partir de la respuesta"}, + {"n", "n"}, + {"Wrong.answer.try.again", "Respuesta incorrecta, vuelva a intentarlo"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "No se ha generado la clave secreta, el alias <{0}> ya existe"}, + {"Please.provide.keysize.for.secret.key.generation", + "Proporcione el valor de -keysize para la generaci\u00F3n de claves secretas"}, + + {"verified.by.s.in.s", "Verificado por %s en %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "ADVERTENCIA: no se ha verificado. Aseg\u00FArese de que el valor de -keystore es correcto."}, + + {"Extensions.", "Extensiones: "}, + {".Empty.value.", "(Valor vac\u00EDo)"}, + {"Extension.Request.", "Solicitud de Extensi\u00F3n:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "Solicitud de Certificado PKCS #10 (Versi\u00F3n 1.0)\nAsunto: %s\nClave P\u00FAblica: %s formato %s clave\n"}, + {"Unknown.keyUsage.type.", "Tipo de uso de clave desconocido: "}, + {"Unknown.extendedkeyUsage.type.", "Tipo de uso de clave extendida desconocido: "}, + {"Unknown.AccessDescription.type.", "Tipo de descripci\u00F3n de acceso desconocido: "}, + {"Unrecognized.GeneralName.type.", "Tipo de nombre general no reconocido: "}, + {"This.extension.cannot.be.marked.as.critical.", + "Esta extensi\u00F3n no se puede marcar como cr\u00EDtica. "}, + {"Odd.number.of.hex.digits.found.", "Se ha encontrado un n\u00FAmero impar de d\u00EDgitos hexadecimales: "}, + {"Unknown.extension.type.", "Tipo de extensi\u00F3n desconocida: "}, + {"command.{0}.is.ambiguous.", "El comando {0} es ambiguo:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_fr.java b/src/sun/security/tools/keytool/Resources_fr.java new file mode 100644 index 00000000..6b22844f --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_fr.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_fr extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Options :"}, + {"Use.keytool.help.for.all.available.commands", + "Utiliser \"keytool -help\" pour toutes les commandes disponibles"}, + {"Key.and.Certificate.Management.Tool", + "Outil de gestion de certificats et de cl\u00E9s"}, + {"Commands.", "Commandes :"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "Utiliser \"keytool -command_name -help\" pour la syntaxe de command_name"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "G\u00E9n\u00E8re une demande de certificat"}, //-certreq + {"Changes.an.entry.s.alias", + "Modifie l'alias d'une entr\u00E9e"}, //-changealias + {"Deletes.an.entry", + "Supprime une entr\u00E9e"}, //-delete + {"Exports.certificate", + "Exporte le certificat"}, //-exportcert + {"Generates.a.key.pair", + "G\u00E9n\u00E8re une paire de cl\u00E9s"}, //-genkeypair + {"Generates.a.secret.key", + "G\u00E9n\u00E8re une cl\u00E9 secr\u00E8te"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "G\u00E9n\u00E8re le certificat \u00E0 partir d'une demande de certificat"}, //-gencert + {"Generates.CRL", "G\u00E9n\u00E8re la liste des certificats r\u00E9voqu\u00E9s (CRL)"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Cl\u00E9 secr\u00E8te {0} g\u00E9n\u00E9r\u00E9e"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Cl\u00E9 secr\u00E8te {0} bits {1} g\u00E9n\u00E9r\u00E9e"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importe les entr\u00E9es \u00E0 partir d'une base de donn\u00E9es d'identit\u00E9s de type JDK 1.1.x"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importe un certificat ou une cha\u00EEne de certificat"}, //-importcert + {"Imports.a.password", + "Importe un mot de passe"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importe une entr\u00E9e ou la totalit\u00E9 des entr\u00E9es depuis un autre fichier de cl\u00E9s"}, //-importkeystore + {"Clones.a.key.entry", + "Clone une entr\u00E9e de cl\u00E9"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "Modifie le mot de passe de cl\u00E9 d'une entr\u00E9e"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "R\u00E9pertorie les entr\u00E9es d'un fichier de cl\u00E9s"}, //-list + {"Prints.the.content.of.a.certificate", + "Imprime le contenu d'un certificat"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Imprime le contenu d'une demande de certificat"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Imprime le contenu d'un fichier de liste des certificats r\u00E9voqu\u00E9s (CRL)"}, //-printcrl + {"Generates.a.self.signed.certificate", + "G\u00E9n\u00E8re un certificat auto-sign\u00E9"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "Modifie le mot de passe de banque d'un fichier de cl\u00E9s"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "nom d'alias de l'entr\u00E9e \u00E0 traiter"}, //-alias + {"destination.alias", + "alias de destination"}, //-destalias + {"destination.key.password", + "mot de passe de la cl\u00E9 de destination"}, //-destkeypass + {"destination.keystore.name", + "nom du fichier de cl\u00E9s de destination"}, //-destkeystore + {"destination.keystore.password.protected", + "mot de passe du fichier de cl\u00E9s de destination prot\u00E9g\u00E9"}, //-destprotected + {"destination.keystore.provider.name", + "nom du fournisseur du fichier de cl\u00E9s de destination"}, //-destprovidername + {"destination.keystore.password", + "mot de passe du fichier de cl\u00E9s de destination"}, //-deststorepass + {"destination.keystore.type", + "type du fichier de cl\u00E9s de destination"}, //-deststoretype + {"distinguished.name", + "nom distinctif"}, //-dname + {"X.509.extension", + "extension X.509"}, //-ext + {"output.file.name", + "nom du fichier de sortie"}, //-file and -outfile + {"input.file.name", + "nom du fichier d'entr\u00E9e"}, //-file and -infile + {"key.algorithm.name", + "nom de l'algorithme de cl\u00E9"}, //-keyalg + {"key.password", + "mot de passe de la cl\u00E9"}, //-keypass + {"key.bit.size", + "taille en bits de la cl\u00E9"}, //-keysize + {"keystore.name", + "nom du fichier de cl\u00E9s"}, //-keystore + {"new.password", + "nouveau mot de passe"}, //-new + {"do.not.prompt", + "ne pas inviter"}, //-noprompt + {"password.through.protected.mechanism", + "mot de passe via m\u00E9canisme prot\u00E9g\u00E9"}, //-protected + {"provider.argument", + "argument du fournisseur"}, //-providerarg + {"provider.class.name", + "nom de la classe de fournisseur"}, //-providerclass + {"provider.name", + "nom du fournisseur"}, //-providername + {"provider.classpath", + "variable d'environnement CLASSPATH du fournisseur"}, //-providerpath + {"output.in.RFC.style", + "sortie au style RFC"}, //-rfc + {"signature.algorithm.name", + "nom de l'algorithme de signature"}, //-sigalg + {"source.alias", + "alias source"}, //-srcalias + {"source.key.password", + "mot de passe de la cl\u00E9 source"}, //-srckeypass + {"source.keystore.name", + "nom du fichier de cl\u00E9s source"}, //-srckeystore + {"source.keystore.password.protected", + "mot de passe du fichier de cl\u00E9s source prot\u00E9g\u00E9"}, //-srcprotected + {"source.keystore.provider.name", + "nom du fournisseur du fichier de cl\u00E9s source"}, //-srcprovidername + {"source.keystore.password", + "mot de passe du fichier de cl\u00E9s source"}, //-srcstorepass + {"source.keystore.type", + "type du fichier de cl\u00E9s source"}, //-srcstoretype + {"SSL.server.host.and.port", + "Port et h\u00F4te du serveur SSL"}, //-sslserver + {"signed.jar.file", + "fichier JAR sign\u00E9"}, //=jarfile + {"certificate.validity.start.date.time", + "date/heure de d\u00E9but de validit\u00E9 du certificat"}, //-startdate + {"keystore.password", + "mot de passe du fichier de cl\u00E9s"}, //-storepass + {"keystore.type", + "type du fichier de cl\u00E9s"}, //-storetype + {"trust.certificates.from.cacerts", + "certificats s\u00E9curis\u00E9s issus de certificats CA"}, //-trustcacerts + {"verbose.output", + "sortie en mode verbose"}, //-v + {"validity.number.of.days", + "nombre de jours de validit\u00E9"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "ID de s\u00E9rie du certificat \u00E0 r\u00E9voquer"}, //-id + // keytool: Running part + {"keytool.error.", "erreur keytool : "}, + {"Illegal.option.", "Option non admise : "}, + {"Illegal.value.", "Valeur non admise : "}, + {"Unknown.password.type.", "Type de mot de passe inconnu : "}, + {"Cannot.find.environment.variable.", + "Variable d'environnement introuvable : "}, + {"Cannot.find.file.", "Fichier introuvable : "}, + {"Command.option.flag.needs.an.argument.", "L''option de commande {0} requiert un argument."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Avertissement\u00A0: les mots de passe de cl\u00E9 et de banque distincts ne sont pas pris en charge pour les fichiers de cl\u00E9s d''acc\u00E8s PKCS12. La valeur {0} sp\u00E9cifi\u00E9e par l''utilisateur est ignor\u00E9e."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore doit \u00EAtre d\u00E9fini sur NONE si -storetype est {0}"}, + {"Too.many.retries.program.terminated", + "Trop de tentatives, fin du programme"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "Les commandes -storepasswd et -keypasswd ne sont pas prises en charge si -storetype est d\u00E9fini sur {0}"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "Les commandes -keypasswd ne sont pas prises en charge si -storetype est d\u00E9fini sur PKCS12"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "Les commandes -keypass et -new ne peuvent pas \u00EAtre sp\u00E9cifi\u00E9es si -storetype est d\u00E9fini sur {0}"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "si -protected est sp\u00E9cifi\u00E9, -storepass, -keypass et -new ne doivent pas \u00EAtre indiqu\u00E9s"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Si -srcprotected est indiqu\u00E9, les commandes -srcstorepass et -srckeypass ne doivent pas \u00EAtre sp\u00E9cifi\u00E9es"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "Si le fichier de cl\u00E9s n'est pas prot\u00E9g\u00E9 par un mot de passe, les commandes -storepass, -keypass et -new ne doivent pas \u00EAtre sp\u00E9cifi\u00E9es"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Si le fichier de cl\u00E9s source n'est pas prot\u00E9g\u00E9 par un mot de passe, les commandes -srcstorepass et -srckeypass ne doivent pas \u00EAtre sp\u00E9cifi\u00E9es"}, + {"Illegal.startdate.value", "Valeur de date de d\u00E9but non admise"}, + {"Validity.must.be.greater.than.zero", + "La validit\u00E9 doit \u00EAtre sup\u00E9rieure \u00E0 z\u00E9ro"}, + {"provName.not.a.provider", "{0} n''est pas un fournisseur"}, + {"Usage.error.no.command.provided", "Erreur de syntaxe\u00A0: aucune commande fournie"}, + {"Source.keystore.file.exists.but.is.empty.", "Le fichier de cl\u00E9s source existe mais il est vide : "}, + {"Please.specify.srckeystore", "Indiquez -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "-v et -rfc ne doivent pas \u00EAtre sp\u00E9cifi\u00E9s avec la commande 'list'"}, + {"Key.password.must.be.at.least.6.characters", + "Un mot de passe de cl\u00E9 doit comporter au moins 6 caract\u00E8res"}, + {"New.password.must.be.at.least.6.characters", + "Le nouveau mot de passe doit comporter au moins 6 caract\u00E8res"}, + {"Keystore.file.exists.but.is.empty.", + "Fichier de cl\u00E9s existant mais vide : "}, + {"Keystore.file.does.not.exist.", + "Le fichier de cl\u00E9s n'existe pas : "}, + {"Must.specify.destination.alias", "L'alias de destination doit \u00EAtre sp\u00E9cifi\u00E9"}, + {"Must.specify.alias", "L'alias doit \u00EAtre sp\u00E9cifi\u00E9"}, + {"Keystore.password.must.be.at.least.6.characters", + "Un mot de passe de fichier de cl\u00E9s doit comporter au moins 6 caract\u00E8res"}, + {"Enter.the.password.to.be.stored.", + "Saisissez le mot de passe \u00E0 stocker : "}, + {"Enter.keystore.password.", "Entrez le mot de passe du fichier de cl\u00E9s : "}, + {"Enter.source.keystore.password.", "Entrez le mot de passe du fichier de cl\u00E9s source\u00A0: "}, + {"Enter.destination.keystore.password.", "Entrez le mot de passe du fichier de cl\u00E9s de destination\u00A0: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "Le mot de passe du fichier de cl\u00E9s est trop court : il doit comporter au moins 6 caract\u00E8res"}, + {"Unknown.Entry.Type", "Type d'entr\u00E9e inconnu"}, + {"Too.many.failures.Alias.not.changed", "Trop d'erreurs. Alias non modifi\u00E9"}, + {"Entry.for.alias.alias.successfully.imported.", + "L''entr\u00E9e de l''alias {0} a \u00E9t\u00E9 import\u00E9e."}, + {"Entry.for.alias.alias.not.imported.", "L''entr\u00E9e de l''alias {0} n''a pas \u00E9t\u00E9 import\u00E9e."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Probl\u00E8me lors de l''import de l''entr\u00E9e de l''alias {0}\u00A0: {1}.\nL''entr\u00E9e de l''alias {0} n''a pas \u00E9t\u00E9 import\u00E9e."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Commande d''import ex\u00E9cut\u00E9e\u00A0: {0} entr\u00E9es import\u00E9es, \u00E9chec ou annulation de {1} entr\u00E9es"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Avertissement\u00A0: l''alias {0} existant sera remplac\u00E9 dans le fichier de cl\u00E9s d''acc\u00E8s de destination"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "L''alias d''entr\u00E9e {0} existe d\u00E9j\u00E0. Voulez-vous le remplacer ? [non]\u00A0: "}, + {"Too.many.failures.try.later", "Trop d'erreurs. R\u00E9essayez plus tard"}, + {"Certification.request.stored.in.file.filename.", + "Demande de certification stock\u00E9e dans le fichier <{0}>"}, + {"Submit.this.to.your.CA", "Soumettre \u00E0 votre CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "si l'alias n'est pas sp\u00E9cifi\u00E9, destalias et srckeypass ne doivent pas \u00EAtre sp\u00E9cifi\u00E9s"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "Le fichier de cl\u00E9s pkcs12 de destination contient un mot de passe de fichier de cl\u00E9s et un mot de passe de cl\u00E9 diff\u00E9rents. R\u00E9essayez en sp\u00E9cifiant -destkeypass."}, + {"Certificate.stored.in.file.filename.", + "Certificat stock\u00E9 dans le fichier <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "R\u00E9ponse de certificat install\u00E9e dans le fichier de cl\u00E9s"}, + {"Certificate.reply.was.not.installed.in.keystore", + "R\u00E9ponse de certificat non install\u00E9e dans le fichier de cl\u00E9s"}, + {"Certificate.was.added.to.keystore", + "Certificat ajout\u00E9 au fichier de cl\u00E9s"}, + {"Certificate.was.not.added.to.keystore", + "Certificat non ajout\u00E9 au fichier de cl\u00E9s"}, + {".Storing.ksfname.", "[Stockage de {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} ne poss\u00E8de pas de cl\u00E9 publique (certificat)"}, + {"Cannot.derive.signature.algorithm", + "Impossible de d\u00E9duire l'algorithme de signature"}, + {"Alias.alias.does.not.exist", + "L''alias <{0}> n''existe pas"}, + {"Alias.alias.has.no.certificate", + "L''alias <{0}> ne poss\u00E8de pas de certificat"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Paire de cl\u00E9s non g\u00E9n\u00E9r\u00E9e, l''alias <{0}> existe d\u00E9j\u00E0"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "G\u00E9n\u00E9ration d''une paire de cl\u00E9s {1} de {0} bits et d''un certificat auto-sign\u00E9 ({2}) d''une validit\u00E9 de {3} jours\n\tpour : {4}"}, + {"Enter.key.password.for.alias.", "Entrez le mot de passe de la cl\u00E9 pour <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(appuyez sur Entr\u00E9e s'il s'agit du mot de passe du fichier de cl\u00E9s) : "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "Le mot de passe de la cl\u00E9 est trop court : il doit comporter au moins 6 caract\u00E8res"}, + {"Too.many.failures.key.not.added.to.keystore", + "Trop d'erreurs. Cl\u00E9 non ajout\u00E9e au fichier de cl\u00E9s"}, + {"Destination.alias.dest.already.exists", + "L''alias de la destination <{0}> existe d\u00E9j\u00E0"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "Le mot de passe est trop court : il doit comporter au moins 6 caract\u00E8res"}, + {"Too.many.failures.Key.entry.not.cloned", + "Trop d'erreurs. Entr\u00E9e de cl\u00E9 non clon\u00E9e"}, + {"key.password.for.alias.", "mot de passe de cl\u00E9 pour <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "L''entr\u00E9e de fichier de cl\u00E9s d''acc\u00E8s pour <{0}> existe d\u00E9j\u00E0"}, + {"Creating.keystore.entry.for.id.getName.", + "Cr\u00E9ation d''une entr\u00E9e de fichier de cl\u00E9s d''acc\u00E8s pour <{0}>..."}, + {"No.entries.from.identity.database.added", + "Aucune entr\u00E9e ajout\u00E9e \u00E0 partir de la base de donn\u00E9es d'identit\u00E9s"}, + {"Alias.name.alias", "Nom d''alias : {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Date de cr\u00E9ation : {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Type d''entr\u00E9e\u00A0: {0}"}, + {"Certificate.chain.length.", "Longueur de cha\u00EEne du certificat : "}, + {"Certificate.i.1.", "Certificat[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Empreinte du certificat (SHA1) : "}, + {"Keystore.type.", "Type de fichier de cl\u00E9s : "}, + {"Keystore.provider.", "Fournisseur de fichier de cl\u00E9s : "}, + {"Your.keystore.contains.keyStore.size.entry", + "Votre fichier de cl\u00E9s d''acc\u00E8s contient {0,number,integer} entr\u00E9e"}, + {"Your.keystore.contains.keyStore.size.entries", + "Votre fichier de cl\u00E9s d''acc\u00E8s contient {0,number,integer} entr\u00E9es"}, + {"Failed.to.parse.input", "L'analyse de l'entr\u00E9e a \u00E9chou\u00E9"}, + {"Empty.input", "Entr\u00E9e vide"}, + {"Not.X.509.certificate", "Pas un certificat X.509"}, + {"alias.has.no.public.key", "{0} ne poss\u00E8de pas de cl\u00E9 publique"}, + {"alias.has.no.X.509.certificate", "{0} ne poss\u00E8de pas de certificat X.509"}, + {"New.certificate.self.signed.", "Nouveau certificat (auto-sign\u00E9) :"}, + {"Reply.has.no.certificates", "La r\u00E9ponse n'a pas de certificat"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Certificat non import\u00E9, l''alias <{0}> existe d\u00E9j\u00E0"}, + {"Input.not.an.X.509.certificate", "L'entr\u00E9e n'est pas un certificat X.509"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "Le certificat existe d\u00E9j\u00E0 dans le fichier de cl\u00E9s d''acc\u00E8s sous l''alias <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "Voulez-vous toujours l'ajouter ? [non] : "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "Le certificat existe d\u00E9j\u00E0 dans le fichier de cl\u00E9s d''acc\u00E8s CA syst\u00E8me sous l''alias <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "Voulez-vous toujours l'ajouter \u00E0 votre fichier de cl\u00E9s ? [non] : "}, + {"Trust.this.certificate.no.", "Faire confiance \u00E0 ce certificat ? [non] : "}, + {"YES", "OUI"}, + {"New.prompt.", "Nouveau {0} : "}, + {"Passwords.must.differ", "Les mots de passe doivent diff\u00E9rer"}, + {"Re.enter.new.prompt.", "Indiquez encore le nouveau {0} : "}, + {"Re.enter.password.", "R\u00E9p\u00E9tez le mot de passe : "}, + {"Re.enter.new.password.", "Ressaisissez le nouveau mot de passe : "}, + {"They.don.t.match.Try.again", "Ils sont diff\u00E9rents. R\u00E9essayez."}, + {"Enter.prompt.alias.name.", "Indiquez le nom d''alias {0} : "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Saisissez le nom du nouvel alias\t(ou appuyez sur Entr\u00E9e pour annuler l'import de cette entr\u00E9e)\u00A0: "}, + {"Enter.alias.name.", "Indiquez le nom d'alias : "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(appuyez sur Entr\u00E9e si le r\u00E9sultat est identique \u00E0 <{0}>)"}, + {".PATTERN.printX509Cert", + "Propri\u00E9taire : {0}\nEmetteur : {1}\nNum\u00E9ro de s\u00E9rie : {2}\nValide du : {3} au : {4}\nEmpreintes du certificat :\n\t MD5: {5}\n\t SHA1 : {6}\n\t SHA256 : {7}\n\t Nom de l''algorithme de signature : {8}\n\t Version : {9}"}, + {"What.is.your.first.and.last.name.", + "Quels sont vos nom et pr\u00E9nom ?"}, + {"What.is.the.name.of.your.organizational.unit.", + "Quel est le nom de votre unit\u00E9 organisationnelle ?"}, + {"What.is.the.name.of.your.organization.", + "Quel est le nom de votre entreprise ?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "Quel est le nom de votre ville de r\u00E9sidence ?"}, + {"What.is.the.name.of.your.State.or.Province.", + "Quel est le nom de votre \u00E9tat ou province ?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "Quel est le code pays \u00E0 deux lettres pour cette unit\u00E9 ?"}, + {"Is.name.correct.", "Est-ce {0} ?"}, + {"no", "non"}, + {"yes", "oui"}, + {"y", "o"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "L''alias <{0}> n''est associ\u00E9 \u00E0 aucune cl\u00E9"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "L''entr\u00E9e \u00E0 laquelle l''alias <{0}> fait r\u00E9f\u00E9rence n''est pas une entr\u00E9e de type cl\u00E9 priv\u00E9e. La commande -keyclone prend uniquement en charge le clonage des cl\u00E9s priv\u00E9es"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Signataire n\u00B0%d :"}, + {"Timestamp.", "Horodatage :"}, + {"Signature.", "Signature :"}, + {"CRLs.", "Listes des certificats r\u00E9voqu\u00E9s (CRL) :"}, + {"Certificate.owner.", "Propri\u00E9taire du certificat : "}, + {"Not.a.signed.jar.file", "Fichier JAR non sign\u00E9"}, + {"No.certificate.from.the.SSL.server", + "Aucun certificat du serveur SSL"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* L'int\u00E9grit\u00E9 des informations stock\u00E9es dans votre fichier de cl\u00E9s *\n* n'a PAS \u00E9t\u00E9 v\u00E9rifi\u00E9e. Pour cela, *\n* vous devez fournir le mot de passe de votre fichier de cl\u00E9s. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* L'int\u00E9grit\u00E9 des informations stock\u00E9es dans le fichier de cl\u00E9s source *\n* n'a PAS \u00E9t\u00E9 v\u00E9rifi\u00E9e. Pour cela, *\n* vous devez fournir le mot de passe de votre fichier de cl\u00E9s source. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "La r\u00E9ponse au certificat ne contient pas de cl\u00E9 publique pour <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Cha\u00EEne de certificat incompl\u00E8te dans la r\u00E9ponse"}, + {"Certificate.chain.in.reply.does.not.verify.", + "La cha\u00EEne de certificat de la r\u00E9ponse ne concorde pas : "}, + {"Top.level.certificate.in.reply.", + "Certificat de niveau sup\u00E9rieur dans la r\u00E9ponse :\n"}, + {".is.not.trusted.", "... non s\u00E9curis\u00E9. "}, + {"Install.reply.anyway.no.", "Installer la r\u00E9ponse quand m\u00EAme ? [non] : "}, + {"NO", "NON"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "Les cl\u00E9s publiques de la r\u00E9ponse et du fichier de cl\u00E9s ne concordent pas"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "La r\u00E9ponse au certificat et le certificat du fichier de cl\u00E9s sont identiques"}, + {"Failed.to.establish.chain.from.reply", + "Impossible de cr\u00E9er une cha\u00EEne \u00E0 partir de la r\u00E9ponse"}, + {"n", "n"}, + {"Wrong.answer.try.again", "R\u00E9ponse incorrecte, recommencez"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "Cl\u00E9 secr\u00E8te non g\u00E9n\u00E9r\u00E9e, l''alias <{0}> existe d\u00E9j\u00E0"}, + {"Please.provide.keysize.for.secret.key.generation", + "Indiquez -keysize pour la g\u00E9n\u00E9ration de la cl\u00E9 secr\u00E8te"}, + + {"verified.by.s.in.s", "V\u00E9rifi\u00E9 par %s dans %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "AVERTISSEMENT : non v\u00E9rifi\u00E9. Assurez-vous que -keystore est correct."}, + + {"Extensions.", "Extensions\u00A0: "}, + {".Empty.value.", "(Valeur vide)"}, + {"Extension.Request.", "Demande d'extension :"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "Demande de certificat PKCS #10 (version 1.0)\nSujet : %s\nCl\u00E9 publique : format %s pour la cl\u00E9 %s\n"}, + {"Unknown.keyUsage.type.", "Type keyUsage inconnu : "}, + {"Unknown.extendedkeyUsage.type.", "Type extendedkeyUsage inconnu : "}, + {"Unknown.AccessDescription.type.", "Type AccessDescription inconnu : "}, + {"Unrecognized.GeneralName.type.", "Type GeneralName non reconnu : "}, + {"This.extension.cannot.be.marked.as.critical.", + "Cette extension ne peut pas \u00EAtre marqu\u00E9e comme critique. "}, + {"Odd.number.of.hex.digits.found.", "Nombre impair de chiffres hexad\u00E9cimaux trouv\u00E9 : "}, + {"Unknown.extension.type.", "Type d'extension inconnu : "}, + {"command.{0}.is.ambiguous.", "commande {0} ambigu\u00EB :"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_it.java b/src/sun/security/tools/keytool/Resources_it.java new file mode 100644 index 00000000..2a49fe60 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_it.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_it extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Opzioni:"}, + {"Use.keytool.help.for.all.available.commands", + "Utilizzare \"keytool -help\" per visualizzare tutti i comandi disponibili"}, + {"Key.and.Certificate.Management.Tool", + "Strumento di gestione di chiavi e certificati"}, + {"Commands.", "Comandi:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "Utilizzare \"keytool -command_name -help\" per informazioni sull'uso di command_name"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Genera una richiesta di certificato"}, //-certreq + {"Changes.an.entry.s.alias", + "Modifica l'alias di una voce"}, //-changealias + {"Deletes.an.entry", + "Elimina una voce"}, //-delete + {"Exports.certificate", + "Esporta il certificato"}, //-exportcert + {"Generates.a.key.pair", + "Genera una coppia di chiavi"}, //-genkeypair + {"Generates.a.secret.key", + "Genera una chiave segreta"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Genera un certificato da una richiesta di certificato"}, //-gencert + {"Generates.CRL", "Genera CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Generata chiave segreta {0}"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Generata chiave segreta {1} a {0} bit"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importa le voci da un database delle identit\u00E0 di tipo JDK 1.1.x"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importa un certificato o una catena di certificati"}, //-importcert + {"Imports.a.password", + "Importa una password"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importa una o tutte le voci da un altro keystore"}, //-importkeystore + {"Clones.a.key.entry", + "Duplica una voce di chiave"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "Modifica la password della chiave per una voce"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Elenca le voci in un keystore"}, //-list + {"Prints.the.content.of.a.certificate", + "Visualizza i contenuti di un certificato"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Visualizza i contenuti di una richiesta di certificato"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Visualizza i contenuti di un file CRL"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Genera certificato con firma automatica"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "Modifica la password di area di memorizzazione di un keystore"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "nome alias della voce da elaborare"}, //-alias + {"destination.alias", + "alias di destinazione"}, //-destalias + {"destination.key.password", + "password chiave di destinazione"}, //-destkeypass + {"destination.keystore.name", + "nome keystore di destinazione"}, //-destkeystore + {"destination.keystore.password.protected", + "password keystore di destinazione protetta"}, //-destprotected + {"destination.keystore.provider.name", + "nome provider keystore di destinazione"}, //-destprovidername + {"destination.keystore.password", + "password keystore di destinazione"}, //-deststorepass + {"destination.keystore.type", + "tipo keystore di destinazione"}, //-deststoretype + {"distinguished.name", + "nome distinto"}, //-dname + {"X.509.extension", + "estensione X.509"}, //-ext + {"output.file.name", + "nome file di output"}, //-file and -outfile + {"input.file.name", + "nome file di input"}, //-file and -infile + {"key.algorithm.name", + "nome algoritmo chiave"}, //-keyalg + {"key.password", + "password chiave"}, //-keypass + {"key.bit.size", + "dimensione bit chiave"}, //-keysize + {"keystore.name", + "nome keystore"}, //-keystore + {"new.password", + "nuova password"}, //-new + {"do.not.prompt", + "non richiedere"}, //-noprompt + {"password.through.protected.mechanism", + "password mediante meccanismo protetto"}, //-protected + {"provider.argument", + "argomento provider"}, //-providerarg + {"provider.class.name", + "nome classe provider"}, //-providerclass + {"provider.name", + "nome provider"}, //-providername + {"provider.classpath", + "classpath provider"}, //-providerpath + {"output.in.RFC.style", + "output in stile RFC"}, //-rfc + {"signature.algorithm.name", + "nome algoritmo firma"}, //-sigalg + {"source.alias", + "alias origine"}, //-srcalias + {"source.key.password", + "password chiave di origine"}, //-srckeypass + {"source.keystore.name", + "nome keystore di origine"}, //-srckeystore + {"source.keystore.password.protected", + "password keystore di origine protetta"}, //-srcprotected + {"source.keystore.provider.name", + "nome provider keystore di origine"}, //-srcprovidername + {"source.keystore.password", + "password keystore di origine"}, //-srcstorepass + {"source.keystore.type", + "tipo keystore di origine"}, //-srcstoretype + {"SSL.server.host.and.port", + "host e porta server SSL"}, //-sslserver + {"signed.jar.file", + "file jar firmato"}, //=jarfile + {"certificate.validity.start.date.time", + "data/ora di inizio validit\u00E0 certificato"}, //-startdate + {"keystore.password", + "password keystore"}, //-storepass + {"keystore.type", + "tipo keystore"}, //-storetype + {"trust.certificates.from.cacerts", + "considera sicuri i certificati da cacerts"}, //-trustcacerts + {"verbose.output", + "output descrittivo"}, //-v + {"validity.number.of.days", + "numero di giorni di validit\u00E0"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "ID seriale del certificato da revocare"}, //-id + // keytool: Running part + {"keytool.error.", "Errore keytool: "}, + {"Illegal.option.", "Opzione non valida: "}, + {"Illegal.value.", "Valore non valido: "}, + {"Unknown.password.type.", "Tipo di password sconosciuto: "}, + {"Cannot.find.environment.variable.", + "Impossibile trovare la variabile di ambiente: "}, + {"Cannot.find.file.", "Impossibile trovare il file: "}, + {"Command.option.flag.needs.an.argument.", "\u00C8 necessario specificare un argomento per l''opzione di comando {0}."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Avvertenza: non sono supportate password diverse di chiave e di archivio per i keystore PKCS12. Il valore {0} specificato dall''utente verr\u00E0 ignorato."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "Se -storetype \u00E8 impostato su {0}, -keystore deve essere impostato su NONE"}, + {"Too.many.retries.program.terminated", + "Il numero dei tentativi consentiti \u00E8 stato superato. Il programma verr\u00E0 terminato."}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "Se -storetype \u00E8 impostato su {0}, i comandi -storepasswd e -keypasswd non sono supportati"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "Se -storetype \u00E8 impostato su PKCS12 i comandi -keypasswd non vengono supportati"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "Se -storetype \u00E8 impostato su {0}, non \u00E8 possibile specificare un valore per -keypass e -new"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "Se \u00E8 specificata l'opzione -protected, le opzioni -storepass, -keypass e -new non possono essere specificate"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Se viene specificato -srcprotected, -srcstorepass e -srckeypass non dovranno essere specificati"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "Se il file keystore non \u00E8 protetto da password, non deve essere specificato alcun valore per -storepass, -keypass e -new"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "Se il file keystore non \u00E8 protetto da password, non deve essere specificato alcun valore per -srcstorepass e -srckeypass"}, + {"Illegal.startdate.value", "Valore di data di inizio non valido"}, + {"Validity.must.be.greater.than.zero", + "La validit\u00E0 deve essere maggiore di zero"}, + {"provName.not.a.provider", "{0} non \u00E8 un provider"}, + {"Usage.error.no.command.provided", "Errore di utilizzo: nessun comando specificato"}, + {"Source.keystore.file.exists.but.is.empty.", "Il file keystore di origine esiste, ma \u00E8 vuoto: "}, + {"Please.specify.srckeystore", "Specificare -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "Impossibile specificare sia -v sia -rfc con il comando 'list'"}, + {"Key.password.must.be.at.least.6.characters", + "La password della chiave deve contenere almeno 6 caratteri"}, + {"New.password.must.be.at.least.6.characters", + "La nuova password deve contenere almeno 6 caratteri"}, + {"Keystore.file.exists.but.is.empty.", + "Il file keystore esiste ma \u00E8 vuoto: "}, + {"Keystore.file.does.not.exist.", + "Il file keystore non esiste: "}, + {"Must.specify.destination.alias", "\u00C8 necessario specificare l'alias di destinazione"}, + {"Must.specify.alias", "\u00C8 necessario specificare l'alias"}, + {"Keystore.password.must.be.at.least.6.characters", + "La password del keystore deve contenere almeno 6 caratteri"}, + {"Enter.the.password.to.be.stored.", + "Immettere la password da memorizzare: "}, + {"Enter.keystore.password.", "Immettere la password del keystore: "}, + {"Enter.source.keystore.password.", "Immettere la password del keystore di origine: "}, + {"Enter.destination.keystore.password.", "Immettere la password del keystore di destinazione: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "La password del keystore \u00E8 troppo corta - deve contenere almeno 6 caratteri"}, + {"Unknown.Entry.Type", "Tipo di voce sconosciuto"}, + {"Too.many.failures.Alias.not.changed", "Numero eccessivo di errori. L'alias non \u00E8 stato modificato."}, + {"Entry.for.alias.alias.successfully.imported.", + "La voce dell''alias {0} \u00E8 stata importata."}, + {"Entry.for.alias.alias.not.imported.", "La voce dell''alias {0} non \u00E8 stata importata."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Si \u00E8 verificato un problema durante l''importazione della voce dell''alias {0}: {1}.\nLa voce dell''alias {0} non \u00E8 stata importata."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Comando di importazione completato: {0} voce/i importata/e, {1} voce/i non importata/e o annullata/e"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Avvertenza: sovrascrittura in corso dell''alias {0} nel file keystore di destinazione"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "La voce dell''alias {0} esiste gi\u00E0. Sovrascrivere? [no]: "}, + {"Too.many.failures.try.later", "Troppi errori - riprovare"}, + {"Certification.request.stored.in.file.filename.", + "La richiesta di certificazione \u00E8 memorizzata nel file <{0}>"}, + {"Submit.this.to.your.CA", "Sottomettere alla propria CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "Se l'alias non \u00E8 specificato, destalias e srckeypass non dovranno essere specificati"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "Keystore pkcs12 di destinazione con storepass e keypass differenti. Riprovare con -destkeypass specificato."}, + {"Certificate.stored.in.file.filename.", + "Il certificato \u00E8 memorizzato nel file <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "La risposta del certificato \u00E8 stata installata nel keystore"}, + {"Certificate.reply.was.not.installed.in.keystore", + "La risposta del certificato non \u00E8 stata installata nel keystore"}, + {"Certificate.was.added.to.keystore", + "Il certificato \u00E8 stato aggiunto al keystore"}, + {"Certificate.was.not.added.to.keystore", + "Il certificato non \u00E8 stato aggiunto al keystore"}, + {".Storing.ksfname.", "[Memorizzazione di {0}] in corso"}, + {"alias.has.no.public.key.certificate.", + "{0} non dispone di chiave pubblica (certificato)"}, + {"Cannot.derive.signature.algorithm", + "Impossibile derivare l'algoritmo di firma"}, + {"Alias.alias.does.not.exist", + "L''alias <{0}> non esiste"}, + {"Alias.alias.has.no.certificate", + "L''alias <{0}> non dispone di certificato"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Non \u00E8 stata generata la coppia di chiavi, l''alias <{0}> \u00E8 gi\u00E0 esistente"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Generazione in corso di una coppia di chiavi {1} da {0} bit e di un certificato autofirmato ({2}) con una validit\u00E0 di {3} giorni\n\tper: {4}"}, + {"Enter.key.password.for.alias.", "Immettere la password della chiave per <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(INVIO se corrisponde alla password del keystore): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "La password della chiave \u00E8 troppo corta - deve contenere almeno 6 caratteri"}, + {"Too.many.failures.key.not.added.to.keystore", + "Troppi errori - la chiave non \u00E8 stata aggiunta al keystore"}, + {"Destination.alias.dest.already.exists", + "L''alias di destinazione <{0}> \u00E8 gi\u00E0 esistente"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "La password \u00E8 troppo corta - deve contenere almeno 6 caratteri"}, + {"Too.many.failures.Key.entry.not.cloned", + "Numero eccessivo di errori. Il valore della chiave non \u00E8 stato copiato."}, + {"key.password.for.alias.", "password della chiave per <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "La voce del keystore per <{0}> esiste gi\u00E0"}, + {"Creating.keystore.entry.for.id.getName.", + "Creazione della voce del keystore per <{0}> in corso..."}, + {"No.entries.from.identity.database.added", + "Nessuna voce aggiunta dal database delle identit\u00E0"}, + {"Alias.name.alias", "Nome alias: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Data di creazione: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Tipo di voce: {0}"}, + {"Certificate.chain.length.", "Lunghezza catena certificati: "}, + {"Certificate.i.1.", "Certificato[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Impronta digitale certificato (SHA1): "}, + {"Keystore.type.", "Tipo keystore: "}, + {"Keystore.provider.", "Provider keystore: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Il keystore contiene {0,number,integer} voce"}, + {"Your.keystore.contains.keyStore.size.entries", + "Il keystore contiene {0,number,integer} voci"}, + {"Failed.to.parse.input", "Impossibile analizzare l'input"}, + {"Empty.input", "Input vuoto"}, + {"Not.X.509.certificate", "Il certificato non \u00E8 X.509"}, + {"alias.has.no.public.key", "{0} non dispone di chiave pubblica"}, + {"alias.has.no.X.509.certificate", "{0} non dispone di certificato X.509"}, + {"New.certificate.self.signed.", "Nuovo certificato (autofirmato):"}, + {"Reply.has.no.certificates", "La risposta non dispone di certificati"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Impossibile importare il certificato, l''alias <{0}> \u00E8 gi\u00E0 esistente"}, + {"Input.not.an.X.509.certificate", "L'input non \u00E8 un certificato X.509"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "Il certificato esiste gi\u00E0 nel keystore con alias <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "Aggiungerlo ugualmente? [no]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "Il certificato esiste gi\u00E0 nel keystore CA con alias <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "Aggiungerlo al proprio keystore? [no]: "}, + {"Trust.this.certificate.no.", "Considerare sicuro questo certificato? [no]: "}, + {"YES", "S\u00EC"}, + {"New.prompt.", "Nuova {0}: "}, + {"Passwords.must.differ", "Le password non devono coincidere"}, + {"Re.enter.new.prompt.", "Reimmettere un nuovo valore per {0}: "}, + {"Re.enter.password.", "Reimmettere la password: "}, + {"Re.enter.new.password.", "Immettere nuovamente la nuova password: "}, + {"They.don.t.match.Try.again", "Non corrispondono. Riprovare."}, + {"Enter.prompt.alias.name.", "Immettere nome alias {0}: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Immettere un nuovo nome alias\t(premere INVIO per annullare l'importazione della voce): "}, + {"Enter.alias.name.", "Immettere nome alias: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(INVIO se corrisponde al nome di <{0}>)"}, + {".PATTERN.printX509Cert", + "Proprietario: {0}\nAutorit\u00E0 emittente: {1}\nNumero di serie: {2}\nValido da: {3} a: {4}\nImpronte digitali certificato:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Nome algoritmo firma: {8}\n\t Versione: {9}"}, + {"What.is.your.first.and.last.name.", + "Specificare nome e cognome"}, + {"What.is.the.name.of.your.organizational.unit.", + "Specificare il nome dell'unit\u00E0 organizzativa"}, + {"What.is.the.name.of.your.organization.", + "Specificare il nome dell'organizzazione"}, + {"What.is.the.name.of.your.City.or.Locality.", + "Specificare la localit\u00E0"}, + {"What.is.the.name.of.your.State.or.Province.", + "Specificare la provincia"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "Specificare il codice a due lettere del paese in cui si trova l'unit\u00E0"}, + {"Is.name.correct.", "Il dato {0} \u00E8 corretto?"}, + {"no", "no"}, + {"yes", "s\u00EC"}, + {"y", "s"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "All''alias <{0}> non \u00E8 associata alcuna chiave"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "L''alias <{0}> fa riferimento a un tipo di voce che non \u00E8 una voce di chiave privata. Il comando -keyclone supporta solo la copia delle voci di chiave private."}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Firmatario #%d:"}, + {"Timestamp.", "Indicatore orario:"}, + {"Signature.", "Firma:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "Proprietario certificato: "}, + {"Not.a.signed.jar.file", "Non \u00E8 un file jar firmato"}, + {"No.certificate.from.the.SSL.server", + "Nessun certificato dal server SSL"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* L'integrit\u00E0 delle informazioni memorizzate nel keystore *\n* NON \u00E8 stata verificata. Per verificarne l'integrit\u00E0 *\n* \u00E8 necessario fornire la password del keystore. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* L'integrit\u00E0 delle informazioni memorizzate nel srckeystore *\n* NON \u00E8 stata verificata. Per verificarne l'integrit\u00E0 *\n* \u00E8 necessario fornire la password del srckeystore. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "La risposta del certificato non contiene la chiave pubblica per <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Catena dei certificati incompleta nella risposta"}, + {"Certificate.chain.in.reply.does.not.verify.", + "La catena dei certificati nella risposta non verifica: "}, + {"Top.level.certificate.in.reply.", + "Certificato di primo livello nella risposta:\n"}, + {".is.not.trusted.", "...non \u00E8 considerato sicuro. "}, + {"Install.reply.anyway.no.", "Installare la risposta? [no]: "}, + {"NO", "NO"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "Le chiavi pubbliche nella risposta e nel keystore non corrispondono"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "La risposta del certificato e il certificato nel keystore sono identici"}, + {"Failed.to.establish.chain.from.reply", + "Impossibile stabilire la catena dalla risposta"}, + {"n", "n"}, + {"Wrong.answer.try.again", "Risposta errata, riprovare"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "La chiave segreta non \u00E8 stata generata; l''alias <{0}> esiste gi\u00E0"}, + {"Please.provide.keysize.for.secret.key.generation", + "Specificare il valore -keysize per la generazione della chiave segreta"}, + + {"verified.by.s.in.s", "Verificato da %s in %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "AVVERTENZA: non verificato. Assicurarsi che -keystore sia corretto."}, + + {"Extensions.", "Estensioni: "}, + {".Empty.value.", "(valore vuoto)"}, + {"Extension.Request.", "Richiesta di estensione:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "Richiesta di certificato PKCS #10 (versione 1.0)\nOggetto: %s\nChiave pubblica: %s formato %s chiave\n"}, + {"Unknown.keyUsage.type.", "Tipo keyUsage sconosciuto: "}, + {"Unknown.extendedkeyUsage.type.", "Tipo extendedkeyUsage sconosciuto: "}, + {"Unknown.AccessDescription.type.", "Tipo AccessDescription sconosciuto: "}, + {"Unrecognized.GeneralName.type.", "Tipo GeneralName non riconosciuto: "}, + {"This.extension.cannot.be.marked.as.critical.", + "Impossibile contrassegnare questa estensione come critica. "}, + {"Odd.number.of.hex.digits.found.", "\u00C8 stato trovato un numero dispari di cifre esadecimali: "}, + {"Unknown.extension.type.", "Tipo di estensione sconosciuto: "}, + {"command.{0}.is.ambiguous.", "il comando {0} \u00E8 ambiguo:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_ja.java b/src/sun/security/tools/keytool/Resources_ja.java new file mode 100644 index 00000000..d9102036 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_ja.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_ja extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "\u30AA\u30D7\u30B7\u30E7\u30F3:"}, + {"Use.keytool.help.for.all.available.commands", + "\u4F7F\u7528\u53EF\u80FD\u306A\u3059\u3079\u3066\u306E\u30B3\u30DE\u30F3\u30C9\u306B\u3064\u3044\u3066\u306F\"keytool -help\"\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Key.and.Certificate.Management.Tool", + "\u30AD\u30FC\u304A\u3088\u3073\u8A3C\u660E\u66F8\u7BA1\u7406\u30C4\u30FC\u30EB"}, + {"Commands.", "\u30B3\u30DE\u30F3\u30C9:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "command_name\u306E\u4F7F\u7528\u65B9\u6CD5\u306B\u3064\u3044\u3066\u306F\"keytool -command_name -help\"\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "\u8A3C\u660E\u66F8\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u751F\u6210\u3057\u307E\u3059"}, //-certreq + {"Changes.an.entry.s.alias", + "\u30A8\u30F3\u30C8\u30EA\u306E\u5225\u540D\u3092\u5909\u66F4\u3057\u307E\u3059"}, //-changealias + {"Deletes.an.entry", + "\u30A8\u30F3\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3059"}, //-delete + {"Exports.certificate", + "\u8A3C\u660E\u66F8\u3092\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, //-exportcert + {"Generates.a.key.pair", + "\u9375\u30DA\u30A2\u3092\u751F\u6210\u3057\u307E\u3059"}, //-genkeypair + {"Generates.a.secret.key", + "\u79D8\u5BC6\u9375\u3092\u751F\u6210\u3057\u307E\u3059"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "\u8A3C\u660E\u66F8\u30EA\u30AF\u30A8\u30B9\u30C8\u304B\u3089\u8A3C\u660E\u66F8\u3092\u751F\u6210\u3057\u307E\u3059"}, //-gencert + {"Generates.CRL", "CRL\u3092\u751F\u6210\u3057\u307E\u3059"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "{0}\u79D8\u5BC6\u9375\u3092\u751F\u6210\u3057\u307E\u3057\u305F"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "{0}\u30D3\u30C3\u30C8{1}\u79D8\u5BC6\u9375\u3092\u751F\u6210\u3057\u307E\u3057\u305F"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "JDK 1.1.x-style\u30A2\u30A4\u30C7\u30F3\u30C6\u30A3\u30C6\u30A3\u30FB\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304B\u3089\u30A8\u30F3\u30C8\u30EA\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "\u8A3C\u660E\u66F8\u307E\u305F\u306F\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, //-importcert + {"Imports.a.password", + "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "\u5225\u306E\u30AD\u30FC\u30B9\u30C8\u30A2\u304B\u30891\u3064\u307E\u305F\u306F\u3059\u3079\u3066\u306E\u30A8\u30F3\u30C8\u30EA\u3092\u30A4\u30F3\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, //-importkeystore + {"Clones.a.key.entry", + "\u9375\u30A8\u30F3\u30C8\u30EA\u306E\u30AF\u30ED\u30FC\u30F3\u3092\u4F5C\u6210\u3057\u307E\u3059"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u30A8\u30F3\u30C8\u30EA\u306E\u9375\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5909\u66F4\u3057\u307E\u3059"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u5185\u306E\u30A8\u30F3\u30C8\u30EA\u3092\u30EA\u30B9\u30C8\u3057\u307E\u3059"}, //-list + {"Prints.the.content.of.a.certificate", + "\u8A3C\u660E\u66F8\u306E\u5185\u5BB9\u3092\u51FA\u529B\u3057\u307E\u3059"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "\u8A3C\u660E\u66F8\u30EA\u30AF\u30A8\u30B9\u30C8\u306E\u5185\u5BB9\u3092\u51FA\u529B\u3057\u307E\u3059"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "CRL\u30D5\u30A1\u30A4\u30EB\u306E\u5185\u5BB9\u3092\u51FA\u529B\u3057\u307E\u3059"}, //-printcrl + {"Generates.a.self.signed.certificate", + "\u81EA\u5DF1\u7F72\u540D\u578B\u8A3C\u660E\u66F8\u3092\u751F\u6210\u3057\u307E\u3059"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30B9\u30C8\u30A2\u30FB\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5909\u66F4\u3057\u307E\u3059"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "\u51E6\u7406\u3059\u308B\u30A8\u30F3\u30C8\u30EA\u306E\u5225\u540D"}, //-alias + {"destination.alias", + "\u51FA\u529B\u5148\u306E\u5225\u540D"}, //-destalias + {"destination.key.password", + "\u51FA\u529B\u5148\u30AD\u30FC\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-destkeypass + {"destination.keystore.name", + "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u540D"}, //-destkeystore + {"destination.keystore.password.protected", + "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u4FDD\u8B77\u5BFE\u8C61\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-destprotected + {"destination.keystore.provider.name", + "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0\u540D"}, //-destprovidername + {"destination.keystore.password", + "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-deststorepass + {"destination.keystore.type", + "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30BF\u30A4\u30D7"}, //-deststoretype + {"distinguished.name", + "\u8B58\u5225\u540D"}, //-dname + {"X.509.extension", + "X.509\u62E1\u5F35"}, //-ext + {"output.file.name", + "\u51FA\u529B\u30D5\u30A1\u30A4\u30EB\u540D"}, //-file and -outfile + {"input.file.name", + "\u5165\u529B\u30D5\u30A1\u30A4\u30EB\u540D"}, //-file and -infile + {"key.algorithm.name", + "\u9375\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u540D"}, //-keyalg + {"key.password", + "\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-keypass + {"key.bit.size", + "\u9375\u306E\u30D3\u30C3\u30C8\u30FB\u30B5\u30A4\u30BA"}, //-keysize + {"keystore.name", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u540D"}, //-keystore + {"new.password", + "\u65B0\u898F\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-new + {"do.not.prompt", + "\u30D7\u30ED\u30F3\u30D7\u30C8\u3092\u8868\u793A\u3057\u306A\u3044"}, //-noprompt + {"password.through.protected.mechanism", + "\u4FDD\u8B77\u30E1\u30AB\u30CB\u30BA\u30E0\u306B\u3088\u308B\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-protected + {"provider.argument", + "\u30D7\u30ED\u30D0\u30A4\u30C0\u5F15\u6570"}, //-providerarg + {"provider.class.name", + "\u30D7\u30ED\u30D0\u30A4\u30C0\u30FB\u30AF\u30E9\u30B9\u540D"}, //-providerclass + {"provider.name", + "\u30D7\u30ED\u30D0\u30A4\u30C0\u540D"}, //-providername + {"provider.classpath", + "\u30D7\u30ED\u30D0\u30A4\u30C0\u30FB\u30AF\u30E9\u30B9\u30D1\u30B9"}, //-providerpath + {"output.in.RFC.style", + "RFC\u30B9\u30BF\u30A4\u30EB\u306E\u51FA\u529B"}, //-rfc + {"signature.algorithm.name", + "\u7F72\u540D\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u540D"}, //-sigalg + {"source.alias", + "\u30BD\u30FC\u30B9\u5225\u540D"}, //-srcalias + {"source.key.password", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-srckeypass + {"source.keystore.name", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u540D"}, //-srckeystore + {"source.keystore.password.protected", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u4FDD\u8B77\u5BFE\u8C61\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-srcprotected + {"source.keystore.provider.name", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0\u540D"}, //-srcprovidername + {"source.keystore.password", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-srcstorepass + {"source.keystore.type", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30BF\u30A4\u30D7"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL\u30B5\u30FC\u30D0\u30FC\u306E\u30DB\u30B9\u30C8\u3068\u30DD\u30FC\u30C8"}, //-sslserver + {"signed.jar.file", + "\u7F72\u540D\u4ED8\u304DJAR\u30D5\u30A1\u30A4\u30EB"}, //=jarfile + {"certificate.validity.start.date.time", + "\u8A3C\u660E\u66F8\u306E\u6709\u52B9\u958B\u59CB\u65E5\u6642"}, //-startdate + {"keystore.password", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, //-storepass + {"keystore.type", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30BF\u30A4\u30D7"}, //-storetype + {"trust.certificates.from.cacerts", + "cacerts\u304B\u3089\u306E\u8A3C\u660E\u66F8\u3092\u4FE1\u983C\u3059\u308B"}, //-trustcacerts + {"verbose.output", + "\u8A73\u7D30\u51FA\u529B"}, //-v + {"validity.number.of.days", + "\u59A5\u5F53\u6027\u65E5\u6570"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "\u5931\u52B9\u3059\u308B\u8A3C\u660E\u66F8\u306E\u30B7\u30EA\u30A2\u30EBID"}, //-id + // keytool: Running part + {"keytool.error.", "keytool\u30A8\u30E9\u30FC: "}, + {"Illegal.option.", "\u4E0D\u6B63\u306A\u30AA\u30D7\u30B7\u30E7\u30F3: "}, + {"Illegal.value.", "\u4E0D\u6B63\u306A\u5024: "}, + {"Unknown.password.type.", "\u4E0D\u660E\u306A\u30D1\u30B9\u30EF\u30FC\u30C9\u30FB\u30BF\u30A4\u30D7: "}, + {"Cannot.find.environment.variable.", + "\u74B0\u5883\u5909\u6570\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: "}, + {"Cannot.find.file.", "\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: "}, + {"Command.option.flag.needs.an.argument.", "\u30B3\u30DE\u30F3\u30C9\u30FB\u30AA\u30D7\u30B7\u30E7\u30F3{0}\u306B\u306F\u5F15\u6570\u304C\u5FC5\u8981\u3067\u3059\u3002"}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "\u8B66\u544A: PKCS12\u30AD\u30FC\u30B9\u30C8\u30A2\u3067\u306F\u3001\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3068\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u7570\u306A\u308B\u72B6\u6CC1\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093\u3002\u30E6\u30FC\u30B6\u30FC\u304C\u6307\u5B9A\u3057\u305F{0}\u306E\u5024\u306F\u7121\u8996\u3057\u307E\u3059\u3002"}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-storetype\u304C{0}\u306E\u5834\u5408\u3001-keystore\u306FNONE\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Too.many.retries.program.terminated", + "\u518D\u8A66\u884C\u304C\u591A\u3059\u304E\u307E\u3059\u3002\u30D7\u30ED\u30B0\u30E9\u30E0\u304C\u7D42\u4E86\u3057\u307E\u3057\u305F"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "-storetype\u304C{0}\u306E\u5834\u5408\u3001-storepasswd\u30B3\u30DE\u30F3\u30C9\u304A\u3088\u3073-keypasswd\u30B3\u30DE\u30F3\u30C9\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "-storetype\u304CPKCS12\u306E\u5834\u5408\u3001-keypasswd\u30B3\u30DE\u30F3\u30C9\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-storetype\u304C{0}\u306E\u5834\u5408\u3001-keypass\u3068-new\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "-protected\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3001-storepass\u3001-keypass\u304A\u3088\u3073-new\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "-srcprotected\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u308B\u5834\u5408\u3001-srcstorepass\u304A\u3088\u3073-srckeypass\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u304C\u30D1\u30B9\u30EF\u30FC\u30C9\u3067\u4FDD\u8B77\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u3001-storepass\u3001-keypass\u304A\u3088\u3073-new\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u304C\u30D1\u30B9\u30EF\u30FC\u30C9\u3067\u4FDD\u8B77\u3055\u308C\u3066\u3044\u306A\u3044\u5834\u5408\u3001-srcstorepass\u304A\u3088\u3073-srckeypass\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"Illegal.startdate.value", "startdate\u5024\u304C\u7121\u52B9\u3067\u3059"}, + {"Validity.must.be.greater.than.zero", + "\u59A5\u5F53\u6027\u306F\u30BC\u30ED\u3088\u308A\u5927\u304D\u3044\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"provName.not.a.provider", "{0}\u306F\u30D7\u30ED\u30D0\u30A4\u30C0\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"Usage.error.no.command.provided", "\u4F7F\u7528\u30A8\u30E9\u30FC: \u30B3\u30DE\u30F3\u30C9\u304C\u6307\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"Source.keystore.file.exists.but.is.empty.", "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D5\u30A1\u30A4\u30EB\u306F\u3001\u5B58\u5728\u3057\u307E\u3059\u304C\u7A7A\u3067\u3059: "}, + {"Please.specify.srckeystore", "-srckeystore\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "'list'\u30B3\u30DE\u30F3\u30C9\u306B-v\u3068-rfc\u306E\u4E21\u65B9\u3092\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"Key.password.must.be.at.least.6.characters", + "\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u306F6\u6587\u5B57\u4EE5\u4E0A\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"New.password.must.be.at.least.6.characters", + "\u65B0\u898F\u30D1\u30B9\u30EF\u30FC\u30C9\u306F6\u6587\u5B57\u4EE5\u4E0A\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Keystore.file.exists.but.is.empty.", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057\u307E\u3059\u304C\u3001\u7A7A\u3067\u3059: "}, + {"Keystore.file.does.not.exist.", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057\u307E\u305B\u3093: "}, + {"Must.specify.destination.alias", "\u51FA\u529B\u5148\u306E\u5225\u540D\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Must.specify.alias", "\u5225\u540D\u3092\u6307\u5B9A\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Keystore.password.must.be.at.least.6.characters", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u306F6\u6587\u5B57\u4EE5\u4E0A\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Enter.the.password.to.be.stored.", + "\u4FDD\u5B58\u3059\u308B\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Enter.keystore.password.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Enter.source.keystore.password.", "\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Enter.destination.keystore.password.", "\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u77ED\u3059\u304E\u307E\u3059 - 6\u6587\u5B57\u4EE5\u4E0A\u306B\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Unknown.Entry.Type", "\u4E0D\u660E\u306A\u30A8\u30F3\u30C8\u30EA\u30FB\u30BF\u30A4\u30D7"}, + {"Too.many.failures.Alias.not.changed", "\u969C\u5BB3\u304C\u591A\u3059\u304E\u307E\u3059\u3002\u5225\u540D\u306F\u5909\u66F4\u3055\u308C\u307E\u305B\u3093"}, + {"Entry.for.alias.alias.successfully.imported.", + "\u5225\u540D{0}\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u306B\u6210\u529F\u3057\u307E\u3057\u305F\u3002"}, + {"Entry.for.alias.alias.not.imported.", "\u5225\u540D{0}\u306E\u30A8\u30F3\u30C8\u30EA\u306F\u30A4\u30F3\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002"}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "\u5225\u540D{0}\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u4E2D\u306B\u554F\u984C\u304C\u767A\u751F\u3057\u307E\u3057\u305F: {1}\u3002\n\u5225\u540D{0}\u306E\u30A8\u30F3\u30C8\u30EA\u306F\u30A4\u30F3\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002"}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "\u30A4\u30F3\u30DD\u30FC\u30C8\u30FB\u30B3\u30DE\u30F3\u30C9\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F: {0}\u4EF6\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u304C\u6210\u529F\u3057\u307E\u3057\u305F\u3002{1}\u4EF6\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u304C\u5931\u6557\u3057\u305F\u304B\u53D6\u308A\u6D88\u3055\u308C\u307E\u3057\u305F"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "\u8B66\u544A: \u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u5185\u306E\u65E2\u5B58\u306E\u5225\u540D{0}\u3092\u4E0A\u66F8\u304D\u3057\u3066\u3044\u307E\u3059"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "\u65E2\u5B58\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u5225\u540D{0}\u304C\u5B58\u5728\u3057\u3066\u3044\u307E\u3059\u3002\u4E0A\u66F8\u304D\u3057\u307E\u3059\u304B\u3002[\u3044\u3044\u3048]: "}, + {"Too.many.failures.try.later", "\u969C\u5BB3\u304C\u591A\u3059\u304E\u307E\u3059 - \u5F8C\u3067\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Certification.request.stored.in.file.filename.", + "\u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30D5\u30A1\u30A4\u30EB<{0}>\u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F"}, + {"Submit.this.to.your.CA", "\u3053\u308C\u3092CA\u306B\u63D0\u51FA\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "\u5225\u540D\u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u3001\u51FA\u529B\u5148\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u5225\u540D\u304A\u3088\u3073\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "\u51FA\u529B\u5148pkcs12\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u3001\u7570\u306A\u308Bstorepass\u304A\u3088\u3073keypass\u304C\u3042\u308A\u307E\u3059\u3002-destkeypass\u3092\u6307\u5B9A\u3057\u3066\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"Certificate.stored.in.file.filename.", + "\u8A3C\u660E\u66F8\u304C\u30D5\u30A1\u30A4\u30EB<{0}>\u306B\u4FDD\u5B58\u3055\u308C\u307E\u3057\u305F"}, + {"Certificate.reply.was.installed.in.keystore", + "\u8A3C\u660E\u66F8\u5FDC\u7B54\u304C\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u307E\u3057\u305F"}, + {"Certificate.reply.was.not.installed.in.keystore", + "\u8A3C\u660E\u66F8\u5FDC\u7B54\u304C\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"}, + {"Certificate.was.added.to.keystore", + "\u8A3C\u660E\u66F8\u304C\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u8FFD\u52A0\u3055\u308C\u307E\u3057\u305F"}, + {"Certificate.was.not.added.to.keystore", + "\u8A3C\u660E\u66F8\u304C\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u8FFD\u52A0\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"}, + {".Storing.ksfname.", "[{0}\u3092\u683C\u7D0D\u4E2D]"}, + {"alias.has.no.public.key.certificate.", + "{0}\u306B\u306F\u516C\u958B\u9375(\u8A3C\u660E\u66F8)\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Cannot.derive.signature.algorithm", + "\u7F72\u540D\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093"}, + {"Alias.alias.does.not.exist", + "\u5225\u540D<{0}>\u306F\u5B58\u5728\u3057\u307E\u305B\u3093"}, + {"Alias.alias.has.no.certificate", + "\u5225\u540D<{0}>\u306B\u306F\u8A3C\u660E\u66F8\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "\u9375\u30DA\u30A2\u306F\u751F\u6210\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5225\u540D<{0}>\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "{3}\u65E5\u9593\u6709\u52B9\u306A{0}\u30D3\u30C3\u30C8\u306E{1}\u306E\u9375\u30DA\u30A2\u3068\u81EA\u5DF1\u7F72\u540D\u578B\u8A3C\u660E\u66F8({2})\u3092\u751F\u6210\u3057\u3066\u3044\u307E\u3059\n\t\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u540D: {4}"}, + {"Enter.key.password.for.alias.", "<{0}>\u306E\u9375\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044"}, + {".RETURN.if.same.as.keystore.password.", + "\t(\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3068\u540C\u3058\u5834\u5408\u306FRETURN\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u77ED\u3059\u304E\u307E\u3059 - 6\u6587\u5B57\u4EE5\u4E0A\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Too.many.failures.key.not.added.to.keystore", + "\u969C\u5BB3\u304C\u591A\u3059\u304E\u307E\u3059 - \u9375\u306F\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u8FFD\u52A0\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"}, + {"Destination.alias.dest.already.exists", + "\u51FA\u529B\u5148\u306E\u5225\u540D<{0}>\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u77ED\u3059\u304E\u307E\u3059 - 6\u6587\u5B57\u4EE5\u4E0A\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Too.many.failures.Key.entry.not.cloned", + "\u969C\u5BB3\u304C\u591A\u3059\u304E\u307E\u3059\u3002\u9375\u30A8\u30F3\u30C8\u30EA\u306E\u30AF\u30ED\u30FC\u30F3\u306F\u4F5C\u6210\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"}, + {"key.password.for.alias.", "<{0}>\u306E\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9"}, + {"Keystore.entry.for.id.getName.already.exists", + "<{0}>\u306E\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30A8\u30F3\u30C8\u30EA\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Creating.keystore.entry.for.id.getName.", + "<{0}>\u306E\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30A8\u30F3\u30C8\u30EA\u3092\u4F5C\u6210\u4E2D..."}, + {"No.entries.from.identity.database.added", + "\u30A2\u30A4\u30C7\u30F3\u30C6\u30A3\u30C6\u30A3\u30FB\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304B\u3089\u8FFD\u52A0\u3055\u308C\u305F\u30A8\u30F3\u30C8\u30EA\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"Alias.name.alias", "\u5225\u540D: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "\u4F5C\u6210\u65E5: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0},{1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "\u30A8\u30F3\u30C8\u30EA\u30FB\u30BF\u30A4\u30D7: {0}"}, + {"Certificate.chain.length.", "\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u306E\u9577\u3055: "}, + {"Certificate.i.1.", "\u8A3C\u660E\u66F8[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "\u8A3C\u660E\u66F8\u306E\u30D5\u30A3\u30F3\u30AC\u30D7\u30EA\u30F3\u30C8(SHA1): "}, + {"Keystore.type.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30BF\u30A4\u30D7: "}, + {"Keystore.provider.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0: "}, + {"Your.keystore.contains.keyStore.size.entry", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u306F{0,number,integer}\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u307E\u3059"}, + {"Your.keystore.contains.keyStore.size.entries", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u306F{0,number,integer}\u30A8\u30F3\u30C8\u30EA\u304C\u542B\u307E\u308C\u307E\u3059"}, + {"Failed.to.parse.input", "\u5165\u529B\u306E\u69CB\u6587\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F"}, + {"Empty.input", "\u5165\u529B\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Not.X.509.certificate", "X.509\u8A3C\u660E\u66F8\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"alias.has.no.public.key", "{0}\u306B\u306F\u516C\u958B\u9375\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"alias.has.no.X.509.certificate", "{0}\u306B\u306FX.509\u8A3C\u660E\u66F8\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"New.certificate.self.signed.", "\u65B0\u3057\u3044\u8A3C\u660E\u66F8(\u81EA\u5DF1\u7F72\u540D\u578B):"}, + {"Reply.has.no.certificates", "\u5FDC\u7B54\u306B\u306F\u8A3C\u660E\u66F8\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Certificate.not.imported.alias.alias.already.exists", + "\u8A3C\u660E\u66F8\u306F\u30A4\u30F3\u30DD\u30FC\u30C8\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5225\u540D<{0}>\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Input.not.an.X.509.certificate", "\u5165\u529B\u306FX.509\u8A3C\u660E\u66F8\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "\u8A3C\u660E\u66F8\u306F\u3001\u5225\u540D<{0}>\u306E\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Do.you.still.want.to.add.it.no.", + "\u8FFD\u52A0\u3057\u307E\u3059\u304B\u3002[\u3044\u3044\u3048]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "\u8A3C\u660E\u66F8\u306F\u3001\u5225\u540D<{0}>\u306E\u30B7\u30B9\u30C6\u30E0\u898F\u6A21\u306ECA\u30AD\u30FC\u30B9\u30C8\u30A2\u5185\u306B\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u8FFD\u52A0\u3057\u307E\u3059\u304B\u3002 [\u3044\u3044\u3048]: "}, + {"Trust.this.certificate.no.", "\u3053\u306E\u8A3C\u660E\u66F8\u3092\u4FE1\u983C\u3057\u307E\u3059\u304B\u3002 [\u3044\u3044\u3048]: "}, + {"YES", "\u306F\u3044"}, + {"New.prompt.", "\u65B0\u898F{0}: "}, + {"Passwords.must.differ", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u7570\u306A\u3063\u3066\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Re.enter.new.prompt.", "\u65B0\u898F{0}\u3092\u518D\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Re.enter.password.", "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u518D\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Re.enter.new.password.", "\u65B0\u898F\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u518D\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"They.don.t.match.Try.again", "\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002\u3082\u3046\u4E00\u5EA6\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Enter.prompt.alias.name.", "{0}\u306E\u5225\u540D\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "\u65B0\u3057\u3044\u5225\u540D\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\t(\u3053\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u3092\u53D6\u308A\u6D88\u3059\u5834\u5408\u306FRETURN\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044): "}, + {"Enter.alias.name.", "\u5225\u540D\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(<{0}>\u3068\u540C\u3058\u5834\u5408\u306FRETURN\u3092\u62BC\u3057\u3066\u304F\u3060\u3055\u3044)"}, + {".PATTERN.printX509Cert", + "\u6240\u6709\u8005: {0}\n\u767A\u884C\u8005: {1}\n\u30B7\u30EA\u30A2\u30EB\u756A\u53F7: {2}\n\u6709\u52B9\u671F\u9593\u306E\u958B\u59CB\u65E5: {3}\u7D42\u4E86\u65E5: {4}\n\u8A3C\u660E\u66F8\u306E\u30D5\u30A3\u30F3\u30AC\u30D7\u30EA\u30F3\u30C8:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t \u7F72\u540D\u30A2\u30EB\u30B4\u30EA\u30BA\u30E0\u540D: {8}\n\t \u30D0\u30FC\u30B8\u30E7\u30F3: {9}"}, + {"What.is.your.first.and.last.name.", + "\u59D3\u540D\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"What.is.the.name.of.your.organizational.unit.", + "\u7D44\u7E54\u5358\u4F4D\u540D\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"What.is.the.name.of.your.organization.", + "\u7D44\u7E54\u540D\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"What.is.the.name.of.your.City.or.Locality.", + "\u90FD\u5E02\u540D\u307E\u305F\u306F\u5730\u57DF\u540D\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"What.is.the.name.of.your.State.or.Province.", + "\u90FD\u9053\u5E9C\u770C\u540D\u307E\u305F\u306F\u5DDE\u540D\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\u3053\u306E\u5358\u4F4D\u306B\u8A72\u5F53\u3059\u308B2\u6587\u5B57\u306E\u56FD\u30B3\u30FC\u30C9\u306F\u4F55\u3067\u3059\u304B\u3002"}, + {"Is.name.correct.", "{0}\u3067\u3088\u308D\u3057\u3044\u3067\u3059\u304B\u3002"}, + {"no", "\u3044\u3044\u3048"}, + {"yes", "\u306F\u3044"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "\u5225\u540D<{0}>\u306B\u306F\u9375\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "\u5225\u540D<{0}>\u304C\u53C2\u7167\u3057\u3066\u3044\u308B\u30A8\u30F3\u30C8\u30EA\u30FB\u30BF\u30A4\u30D7\u306F\u79D8\u5BC6\u9375\u30A8\u30F3\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002-keyclone\u30B3\u30DE\u30F3\u30C9\u306F\u79D8\u5BC6\u9375\u30A8\u30F3\u30C8\u30EA\u306E\u30AF\u30ED\u30FC\u30F3\u4F5C\u6210\u306E\u307F\u3092\u30B5\u30DD\u30FC\u30C8\u3057\u307E\u3059"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "\u7F72\u540D\u8005\u756A\u53F7%d:"}, + {"Timestamp.", "\u30BF\u30A4\u30E0\u30B9\u30BF\u30F3\u30D7:"}, + {"Signature.", "\u7F72\u540D:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "\u8A3C\u660E\u66F8\u306E\u6240\u6709\u8005: "}, + {"Not.a.signed.jar.file", "\u7F72\u540D\u4ED8\u304DJAR\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"No.certificate.from.the.SSL.server", + "SSL\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u306E\u8A3C\u660E\u66F8\u304C\u3042\u308A\u307E\u305B\u3093"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "*\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u4FDD\u5B58\u3055\u308C\u305F\u60C5\u5831\u306E\u6574\u5408\u6027\u306F*\n*\u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u6574\u5408\u6027\u3092\u691C\u8A3C\u3059\u308B\u306B\u306F*\n*\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002*"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "*\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306B\u4FDD\u5B58\u3055\u308C\u305F\u60C5\u5831\u306E\u6574\u5408\u6027\u306F*\n*\u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u6574\u5408\u6027\u3092\u691C\u8A3C\u3059\u308B\u306B\u306F*\n*\u30BD\u30FC\u30B9\u30FB\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u5165\u529B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002*"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "\u8A3C\u660E\u66F8\u5FDC\u7B54\u306B\u306F\u3001<{0}>\u306E\u516C\u958B\u9375\u306F\u542B\u307E\u308C\u307E\u305B\u3093"}, + {"Incomplete.certificate.chain.in.reply", + "\u5FDC\u7B54\u3057\u305F\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u306F\u4E0D\u5B8C\u5168\u3067\u3059"}, + {"Certificate.chain.in.reply.does.not.verify.", + "\u5FDC\u7B54\u3057\u305F\u8A3C\u660E\u66F8\u30C1\u30A7\u30FC\u30F3\u306F\u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093: "}, + {"Top.level.certificate.in.reply.", + "\u5FDC\u7B54\u3057\u305F\u30C8\u30C3\u30D7\u30EC\u30D9\u30EB\u306E\u8A3C\u660E\u66F8:\n"}, + {".is.not.trusted.", "... \u306F\u4FE1\u983C\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002 "}, + {"Install.reply.anyway.no.", "\u5FDC\u7B54\u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\u307E\u3059\u304B\u3002[\u3044\u3044\u3048]: "}, + {"NO", "\u3044\u3044\u3048"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "\u5FDC\u7B54\u3057\u305F\u516C\u958B\u9375\u3068\u30AD\u30FC\u30B9\u30C8\u30A2\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "\u8A3C\u660E\u66F8\u5FDC\u7B54\u3068\u30AD\u30FC\u30B9\u30C8\u30A2\u5185\u306E\u8A3C\u660E\u66F8\u304C\u540C\u3058\u3067\u3059"}, + {"Failed.to.establish.chain.from.reply", + "\u5FDC\u7B54\u304B\u3089\u9023\u9396\u3092\u78BA\u7ACB\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F"}, + {"n", "n"}, + {"Wrong.answer.try.again", "\u5FDC\u7B54\u304C\u9593\u9055\u3063\u3066\u3044\u307E\u3059\u3002\u3082\u3046\u4E00\u5EA6\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "\u79D8\u5BC6\u9375\u306F\u751F\u6210\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u5225\u540D<{0}>\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059"}, + {"Please.provide.keysize.for.secret.key.generation", + "\u79D8\u5BC6\u9375\u306E\u751F\u6210\u6642\u306B\u306F -keysize\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"}, + + {"verified.by.s.in.s", "%s(%s\u5185)\u306B\u3088\u308A\u691C\u8A3C\u3055\u308C\u307E\u3057\u305F"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "\u8B66\u544A: \u691C\u8A3C\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002-keystore\u304C\u6B63\u3057\u3044\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + + {"Extensions.", "\u62E1\u5F35: "}, + {".Empty.value.", "(\u7A7A\u306E\u5024)"}, + {"Extension.Request.", "\u62E1\u5F35\u30EA\u30AF\u30A8\u30B9\u30C8:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10\u8A3C\u660E\u66F8\u30EA\u30AF\u30A8\u30B9\u30C8(\u30D0\u30FC\u30B8\u30E7\u30F31.0)\n\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8: %s\n\u516C\u958B\u9375: %s \u30D5\u30A9\u30FC\u30DE\u30C3\u30C8 %s \u30AD\u30FC\n"}, + {"Unknown.keyUsage.type.", "\u4E0D\u660E\u306AkeyUsage\u30BF\u30A4\u30D7: "}, + {"Unknown.extendedkeyUsage.type.", "\u4E0D\u660E\u306AextendedkeyUsage\u30BF\u30A4\u30D7: "}, + {"Unknown.AccessDescription.type.", "\u4E0D\u660E\u306AAccessDescription\u30BF\u30A4\u30D7: "}, + {"Unrecognized.GeneralName.type.", "\u8A8D\u8B58\u3055\u308C\u306A\u3044GeneralName\u30BF\u30A4\u30D7: "}, + {"This.extension.cannot.be.marked.as.critical.", + "\u3053\u306E\u62E1\u5F35\u306F\u30AF\u30EA\u30C6\u30A3\u30AB\u30EB\u3068\u3057\u3066\u30DE\u30FC\u30AF\u4ED8\u3051\u3067\u304D\u307E\u305B\u3093\u3002 "}, + {"Odd.number.of.hex.digits.found.", "\u5947\u6570\u306E16\u9032\u6570\u304C\u898B\u3064\u304B\u308A\u307E\u3057\u305F: "}, + {"Unknown.extension.type.", "\u4E0D\u660E\u306A\u62E1\u5F35\u30BF\u30A4\u30D7: "}, + {"command.{0}.is.ambiguous.", "\u30B3\u30DE\u30F3\u30C9{0}\u306F\u3042\u3044\u307E\u3044\u3067\u3059:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_ko.java b/src/sun/security/tools/keytool/Resources_ko.java new file mode 100644 index 00000000..40ede297 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_ko.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_ko extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "\uC635\uC158:"}, + {"Use.keytool.help.for.all.available.commands", + "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBAA8\uB4E0 \uBA85\uB839\uC5D0 \"keytool -help\" \uC0AC\uC6A9"}, + {"Key.and.Certificate.Management.Tool", + "\uD0A4 \uBC0F \uC778\uC99D\uC11C \uAD00\uB9AC \uD234"}, + {"Commands.", "\uBA85\uB839:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "command_name \uC0AC\uC6A9\uBC95\uC5D0 \"keytool -command_name -help\" \uC0AC\uC6A9"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "\uC778\uC99D\uC11C \uC694\uCCAD\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-certreq + {"Changes.an.entry.s.alias", + "\uD56D\uBAA9\uC758 \uBCC4\uCE6D\uC744 \uBCC0\uACBD\uD569\uB2C8\uB2E4."}, //-changealias + {"Deletes.an.entry", + "\uD56D\uBAA9\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4."}, //-delete + {"Exports.certificate", + "\uC778\uC99D\uC11C\uB97C \uC775\uC2A4\uD3EC\uD2B8\uD569\uB2C8\uB2E4."}, //-exportcert + {"Generates.a.key.pair", + "\uD0A4 \uC30D\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-genkeypair + {"Generates.a.secret.key", + "\uBCF4\uC548 \uD0A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "\uC778\uC99D\uC11C \uC694\uCCAD\uC5D0\uC11C \uC778\uC99D\uC11C\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-gencert + {"Generates.CRL", "CRL\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-gencrl + {"Generated.keyAlgName.secret.key", + "{0} \uBCF4\uC548 \uD0A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "{0}\uBE44\uD2B8 {1} \uBCF4\uC548 \uD0A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "JDK 1.1.x \uC2A4\uD0C0\uC77C ID \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC5D0\uC11C \uD56D\uBAA9\uC744 \uC784\uD3EC\uD2B8\uD569\uB2C8\uB2E4."}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "\uC778\uC99D\uC11C \uB610\uB294 \uC778\uC99D\uC11C \uCCB4\uC778\uC744 \uC784\uD3EC\uD2B8\uD569\uB2C8\uB2E4."}, //-importcert + {"Imports.a.password", + "\uBE44\uBC00\uBC88\uD638\uB97C \uC784\uD3EC\uD2B8\uD569\uB2C8\uB2E4."}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "\uB2E4\uB978 \uD0A4 \uC800\uC7A5\uC18C\uC5D0\uC11C \uD558\uB098 \uB610\uB294 \uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uC784\uD3EC\uD2B8\uD569\uB2C8\uB2E4."}, //-importkeystore + {"Clones.a.key.entry", + "\uD0A4 \uD56D\uBAA9\uC744 \uBCF5\uC81C\uD569\uB2C8\uB2E4."}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\uD56D\uBAA9\uC758 \uD0A4 \uBE44\uBC00\uBC88\uD638\uB97C \uBCC0\uACBD\uD569\uB2C8\uB2E4."}, //-keypasswd + {"Lists.entries.in.a.keystore", + "\uD0A4 \uC800\uC7A5\uC18C\uC758 \uD56D\uBAA9\uC744 \uB098\uC5F4\uD569\uB2C8\uB2E4."}, //-list + {"Prints.the.content.of.a.certificate", + "\uC778\uC99D\uC11C\uC758 \uCF58\uD150\uCE20\uB97C \uC778\uC1C4\uD569\uB2C8\uB2E4."}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "\uC778\uC99D\uC11C \uC694\uCCAD\uC758 \uCF58\uD150\uCE20\uB97C \uC778\uC1C4\uD569\uB2C8\uB2E4."}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "CRL \uD30C\uC77C\uC758 \uCF58\uD150\uCE20\uB97C \uC778\uC1C4\uD569\uB2C8\uB2E4."}, //-printcrl + {"Generates.a.self.signed.certificate", + "\uC790\uCCB4 \uC11C\uBA85\uB41C \uC778\uC99D\uC11C\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4."}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\uD0A4 \uC800\uC7A5\uC18C\uC758 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uB97C \uBCC0\uACBD\uD569\uB2C8\uB2E4."}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "\uCC98\uB9AC\uD560 \uD56D\uBAA9\uC758 \uBCC4\uCE6D \uC774\uB984"}, //-alias + {"destination.alias", + "\uB300\uC0C1 \uBCC4\uCE6D"}, //-destalias + {"destination.key.password", + "\uB300\uC0C1 \uD0A4 \uBE44\uBC00\uBC88\uD638"}, //-destkeypass + {"destination.keystore.name", + "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uC774\uB984"}, //-destkeystore + {"destination.keystore.password.protected", + "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uB85C \uBCF4\uD638\uB428"}, //-destprotected + {"destination.keystore.provider.name", + "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uC81C\uACF5\uC790 \uC774\uB984"}, //-destprovidername + {"destination.keystore.password", + "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638"}, //-deststorepass + {"destination.keystore.type", + "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uC720\uD615"}, //-deststoretype + {"distinguished.name", + "\uC2DD\uBCC4 \uC774\uB984"}, //-dname + {"X.509.extension", + "X.509 \uD655\uC7A5"}, //-ext + {"output.file.name", + "\uCD9C\uB825 \uD30C\uC77C \uC774\uB984"}, //-file and -outfile + {"input.file.name", + "\uC785\uB825 \uD30C\uC77C \uC774\uB984"}, //-file and -infile + {"key.algorithm.name", + "\uD0A4 \uC54C\uACE0\uB9AC\uC998 \uC774\uB984"}, //-keyalg + {"key.password", + "\uD0A4 \uBE44\uBC00\uBC88\uD638"}, //-keypass + {"key.bit.size", + "\uD0A4 \uBE44\uD2B8 \uD06C\uAE30"}, //-keysize + {"keystore.name", + "\uD0A4 \uC800\uC7A5\uC18C \uC774\uB984"}, //-keystore + {"new.password", + "\uC0C8 \uBE44\uBC00\uBC88\uD638"}, //-new + {"do.not.prompt", + "\uD655\uC778\uD558\uC9C0 \uC54A\uC74C"}, //-noprompt + {"password.through.protected.mechanism", + "\uBCF4\uD638\uB418\uB294 \uBA54\uCEE4\uB2C8\uC998\uC744 \uD1B5\uD55C \uBE44\uBC00\uBC88\uD638"}, //-protected + {"provider.argument", + "\uC81C\uACF5\uC790 \uC778\uC218"}, //-providerarg + {"provider.class.name", + "\uC81C\uACF5\uC790 \uD074\uB798\uC2A4 \uC774\uB984"}, //-providerclass + {"provider.name", + "\uC81C\uACF5\uC790 \uC774\uB984"}, //-providername + {"provider.classpath", + "\uC81C\uACF5\uC790 \uD074\uB798\uC2A4 \uACBD\uB85C"}, //-providerpath + {"output.in.RFC.style", + "RFC \uC2A4\uD0C0\uC77C\uC758 \uCD9C\uB825"}, //-rfc + {"signature.algorithm.name", + "\uC11C\uBA85 \uC54C\uACE0\uB9AC\uC998 \uC774\uB984"}, //-sigalg + {"source.alias", + "\uC18C\uC2A4 \uBCC4\uCE6D"}, //-srcalias + {"source.key.password", + "\uC18C\uC2A4 \uD0A4 \uBE44\uBC00\uBC88\uD638"}, //-srckeypass + {"source.keystore.name", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uC774\uB984"}, //-srckeystore + {"source.keystore.password.protected", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uB85C \uBCF4\uD638\uB428"}, //-srcprotected + {"source.keystore.provider.name", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uC81C\uACF5\uC790 \uC774\uB984"}, //-srcprovidername + {"source.keystore.password", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638"}, //-srcstorepass + {"source.keystore.type", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uC720\uD615"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL \uC11C\uBC84 \uD638\uC2A4\uD2B8 \uBC0F \uD3EC\uD2B8"}, //-sslserver + {"signed.jar.file", + "\uC11C\uBA85\uB41C jar \uD30C\uC77C"}, //=jarfile + {"certificate.validity.start.date.time", + "\uC778\uC99D\uC11C \uC720\uD6A8 \uAE30\uAC04 \uC2DC\uC791 \uB0A0\uC9DC/\uC2DC\uAC04"}, //-startdate + {"keystore.password", + "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638"}, //-storepass + {"keystore.type", + "\uD0A4 \uC800\uC7A5\uC18C \uC720\uD615"}, //-storetype + {"trust.certificates.from.cacerts", + "cacerts\uC758 \uBCF4\uC548 \uC778\uC99D\uC11C"}, //-trustcacerts + {"verbose.output", + "\uC0C1\uC138 \uC815\uBCF4 \uCD9C\uB825"}, //-v + {"validity.number.of.days", + "\uC720\uD6A8 \uAE30\uAC04 \uC77C \uC218"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "\uCCA0\uD68C\uD560 \uC778\uC99D\uC11C\uC758 \uC77C\uB828 ID"}, //-id + // keytool: Running part + {"keytool.error.", "keytool \uC624\uB958: "}, + {"Illegal.option.", "\uC798\uBABB\uB41C \uC635\uC158: "}, + {"Illegal.value.", "\uC798\uBABB\uB41C \uAC12: "}, + {"Unknown.password.type.", "\uC54C \uC218 \uC5C6\uB294 \uBE44\uBC00\uBC88\uD638 \uC720\uD615: "}, + {"Cannot.find.environment.variable.", + "\uD658\uACBD \uBCC0\uC218\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC74C: "}, + {"Cannot.find.file.", "\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC74C: "}, + {"Command.option.flag.needs.an.argument.", "\uBA85\uB839 \uC635\uC158 {0}\uC5D0 \uC778\uC218\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "\uACBD\uACE0: \uB2E4\uB978 \uC800\uC7A5\uC18C \uBC0F \uD0A4 \uBE44\uBC00\uBC88\uD638\uB294 PKCS12 KeyStores\uC5D0 \uB300\uD574 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uC9C0\uC815\uD55C {0} \uAC12\uC744 \uBB34\uC2DC\uD558\uB294 \uC911\uC785\uB2C8\uB2E4."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-storetype\uC774 {0}\uC778 \uACBD\uC6B0 -keystore\uB294 NONE\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Too.many.retries.program.terminated", + "\uC7AC\uC2DC\uB3C4 \uD69F\uC218\uAC00 \uB108\uBB34 \uB9CE\uC544 \uD504\uB85C\uADF8\uB7A8\uC774 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "-storetype\uC774 {0}\uC778 \uACBD\uC6B0 -storepasswd \uBC0F -keypasswd \uBA85\uB839\uC774 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "-storetype\uC774 PKCS12\uC778 \uACBD\uC6B0 -keypasswd \uBA85\uB839\uC774 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-storetype\uC774 {0}\uC778 \uACBD\uC6B0 -keypass \uBC0F -new\uB97C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "-protected\uB97C \uC9C0\uC815\uD55C \uACBD\uC6B0 -storepass, -keypass \uBC0F -new\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "-srcprotected\uB97C \uC9C0\uC815\uD55C \uACBD\uC6B0 -srcstorepass \uBC0F -srckeypass\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "\uD0A4 \uC800\uC7A5\uC18C\uAC00 \uBE44\uBC00\uBC88\uD638\uB85C \uBCF4\uD638\uB418\uC9C0 \uC54A\uB294 \uACBD\uC6B0 -storepass, -keypass \uBC0F -new\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C\uAC00 \uBE44\uBC00\uBC88\uD638\uB85C \uBCF4\uD638\uB418\uC9C0 \uC54A\uB294 \uACBD\uC6B0 -srcstorepass \uBC0F -srckeypass\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"Illegal.startdate.value", "startdate \uAC12\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Validity.must.be.greater.than.zero", + "\uC720\uD6A8 \uAE30\uAC04\uC740 0\uBCF4\uB2E4 \uCEE4\uC57C \uD569\uB2C8\uB2E4."}, + {"provName.not.a.provider", "{0}\uC740(\uB294) \uC81C\uACF5\uC790\uAC00 \uC544\uB2D9\uB2C8\uB2E4."}, + {"Usage.error.no.command.provided", "\uC0AC\uC6A9\uBC95 \uC624\uB958: \uBA85\uB839\uC744 \uC785\uB825\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Source.keystore.file.exists.but.is.empty.", "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0\uB9CC \uBE44\uC5B4 \uC788\uC74C: "}, + {"Please.specify.srckeystore", "-srckeystore\uB97C \uC9C0\uC815\uD558\uC2ED\uC2DC\uC624."}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "'list' \uBA85\uB839\uC5D0 -v\uC640 -rfc\uB97C \uD568\uAED8 \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"Key.password.must.be.at.least.6.characters", + "\uD0A4 \uBE44\uBC00\uBC88\uD638\uB294 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"New.password.must.be.at.least.6.characters", + "\uC0C8 \uBE44\uBC00\uBC88\uD638\uB294 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Keystore.file.exists.but.is.empty.", + "\uD0A4 \uC800\uC7A5\uC18C \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0\uB9CC \uBE44\uC5B4 \uC788\uC74C: "}, + {"Keystore.file.does.not.exist.", + "\uD0A4 \uC800\uC7A5\uC18C \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC74C: "}, + {"Must.specify.destination.alias", "\uB300\uC0C1 \uBCC4\uCE6D\uC744 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."}, + {"Must.specify.alias", "\uBCC4\uCE6D\uC744 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."}, + {"Keystore.password.must.be.at.least.6.characters", + "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uB294 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Enter.the.password.to.be.stored.", + "\uC800\uC7A5\uD560 \uBE44\uBC00\uBC88\uD638 \uC785\uB825: "}, + {"Enter.keystore.password.", "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638 \uC785\uB825: "}, + {"Enter.source.keystore.password.", "\uC18C\uC2A4 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638 \uC785\uB825: "}, + {"Enter.destination.keystore.password.", "\uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638 \uC785\uB825: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uAC00 \uB108\uBB34 \uC9E7\uC74C - 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Unknown.Entry.Type", "\uC54C \uC218 \uC5C6\uB294 \uD56D\uBAA9 \uC720\uD615"}, + {"Too.many.failures.Alias.not.changed", "\uC624\uB958\uAC00 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4. \uBCC4\uCE6D\uC774 \uBCC0\uACBD\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Entry.for.alias.alias.successfully.imported.", + "{0} \uBCC4\uCE6D\uC5D0 \uB300\uD55C \uD56D\uBAA9\uC774 \uC131\uACF5\uC801\uC73C\uB85C \uC784\uD3EC\uD2B8\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Entry.for.alias.alias.not.imported.", "{0} \uBCC4\uCE6D\uC5D0 \uB300\uD55C \uD56D\uBAA9\uC774 \uC784\uD3EC\uD2B8\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "{0} \uBCC4\uCE6D\uC5D0 \uB300\uD55C \uD56D\uBAA9\uC744 \uC784\uD3EC\uD2B8\uD558\uB294 \uC911 \uBB38\uC81C \uBC1C\uC0DD: {1}.\n{0} \uBCC4\uCE6D\uC5D0 \uB300\uD55C \uD56D\uBAA9\uC774 \uC784\uD3EC\uD2B8\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "\uC784\uD3EC\uD2B8 \uBA85\uB839 \uC644\uB8CC: \uC131\uACF5\uC801\uC73C\uB85C \uC784\uD3EC\uD2B8\uB41C \uD56D\uBAA9\uC740 {0}\uAC1C, \uC2E4\uD328\uD558\uAC70\uB098 \uCDE8\uC18C\uB41C \uD56D\uBAA9\uC740 {1}\uAC1C\uC785\uB2C8\uB2E4."}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "\uACBD\uACE0: \uB300\uC0C1 \uD0A4 \uC800\uC7A5\uC18C\uC5D0\uC11C \uAE30\uC874 \uBCC4\uCE6D {0}\uC744(\uB97C) \uACB9\uCCD0 \uC4F0\uB294 \uC911"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "\uAE30\uC874 \uD56D\uBAA9 \uBCC4\uCE6D {0}\uC774(\uAC00) \uC874\uC7AC\uD569\uB2C8\uB2E4. \uACB9\uCCD0 \uC4F0\uACA0\uC2B5\uB2C8\uAE4C? [\uC544\uB2C8\uC624]: "}, + {"Too.many.failures.try.later", "\uC624\uB958\uAC00 \uB108\uBB34 \uB9CE\uC74C - \uB098\uC911\uC5D0 \uC2DC\uB3C4\uD558\uC2ED\uC2DC\uC624."}, + {"Certification.request.stored.in.file.filename.", + "\uC778\uC99D \uC694\uCCAD\uC774 <{0}> \uD30C\uC77C\uC5D0 \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Submit.this.to.your.CA", "CA\uC5D0\uAC8C \uC81C\uCD9C\uD558\uC2ED\uC2DC\uC624."}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "\uBCC4\uCE6D\uC744 \uC9C0\uC815\uD558\uC9C0 \uC54A\uC740 \uACBD\uC6B0 destalias \uBC0F srckeypass\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC544\uC57C \uD569\uB2C8\uB2E4."}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "\uB300\uC0C1 pkcs12 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uB2E4\uB978 storepass \uBC0F keypass\uAC00 \uC788\uC2B5\uB2C8\uB2E4. \uC9C0\uC815\uB41C -destkeypass\uB85C \uC7AC\uC2DC\uB3C4\uD558\uC2ED\uC2DC\uC624."}, + {"Certificate.stored.in.file.filename.", + "\uC778\uC99D\uC11C\uAC00 <{0}> \uD30C\uC77C\uC5D0 \uC800\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Certificate.reply.was.installed.in.keystore", + "\uC778\uC99D\uC11C \uD68C\uC2E0\uC774 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC124\uCE58\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Certificate.reply.was.not.installed.in.keystore", + "\uC778\uC99D\uC11C \uD68C\uC2E0\uC774 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC124\uCE58\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Certificate.was.added.to.keystore", + "\uC778\uC99D\uC11C\uAC00 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Certificate.was.not.added.to.keystore", + "\uC778\uC99D\uC11C\uAC00 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uCD94\uAC00\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {".Storing.ksfname.", "[{0}\uC744(\uB97C) \uC800\uC7A5\uD558\uB294 \uC911]"}, + {"alias.has.no.public.key.certificate.", + "{0}\uC5D0 \uACF5\uC6A9 \uD0A4(\uC778\uC99D\uC11C)\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Cannot.derive.signature.algorithm", + "\uC11C\uBA85 \uC54C\uACE0\uB9AC\uC998\uC744 \uD30C\uC0DD\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Alias.alias.does.not.exist", + "<{0}> \uBCC4\uCE6D\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"Alias.alias.has.no.certificate", + "<{0}> \uBCC4\uCE6D\uC5D0 \uC778\uC99D\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Key.pair.not.generated.alias.alias.already.exists", + "\uD0A4 \uC30D\uC774 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC558\uC73C\uBA70 <{0}> \uBCC4\uCE6D\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "\uB2E4\uC74C\uC5D0 \uB300\uD574 \uC720\uD6A8 \uAE30\uAC04\uC774 {3}\uC77C\uC778 {0}\uBE44\uD2B8 {1} \uD0A4 \uC30D \uBC0F \uC790\uCCB4 \uC11C\uBA85\uB41C \uC778\uC99D\uC11C({2})\uB97C \uC0DD\uC131\uD558\uB294 \uC911\n\t: {4}"}, + {"Enter.key.password.for.alias.", "<{0}>\uC5D0 \uB300\uD55C \uD0A4 \uBE44\uBC00\uBC88\uD638\uB97C \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {".RETURN.if.same.as.keystore.password.", + "\t(\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uC640 \uB3D9\uC77C\uD55C \uACBD\uC6B0 Enter \uD0A4\uB97C \uB204\uB984): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "\uD0A4 \uBE44\uBC00\uBC88\uD638\uAC00 \uB108\uBB34 \uC9E7\uC74C - 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Too.many.failures.key.not.added.to.keystore", + "\uC624\uB958\uAC00 \uB108\uBB34 \uB9CE\uC74C - \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uD0A4\uAC00 \uCD94\uAC00\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"Destination.alias.dest.already.exists", + "\uB300\uC0C1 \uBCC4\uCE6D <{0}>\uC774(\uAC00) \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Password.is.too.short.must.be.at.least.6.characters", + "\uBE44\uBC00\uBC88\uD638\uAC00 \uB108\uBB34 \uC9E7\uC74C - 6\uC790 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Too.many.failures.Key.entry.not.cloned", + "\uC624\uB958\uAC00 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4. \uD0A4 \uD56D\uBAA9\uC774 \uBCF5\uC81C\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."}, + {"key.password.for.alias.", "<{0}>\uC5D0 \uB300\uD55C \uD0A4 \uBE44\uBC00\uBC88\uD638"}, + {"Keystore.entry.for.id.getName.already.exists", + "<{0}>\uC5D0 \uB300\uD55C \uD0A4 \uC800\uC7A5\uC18C \uD56D\uBAA9\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Creating.keystore.entry.for.id.getName.", + "<{0}>\uC5D0 \uB300\uD55C \uD0A4 \uC800\uC7A5\uC18C \uD56D\uBAA9\uC744 \uC0DD\uC131\uD558\uB294 \uC911..."}, + {"No.entries.from.identity.database.added", + "ID \uB370\uC774\uD130\uBCA0\uC774\uC2A4\uC5D0\uC11C \uCD94\uAC00\uB41C \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Alias.name.alias", "\uBCC4\uCE6D \uC774\uB984: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "\uC0DD\uC131 \uB0A0\uC9DC: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "\uD56D\uBAA9 \uC720\uD615: {0}"}, + {"Certificate.chain.length.", "\uC778\uC99D\uC11C \uCCB4\uC778 \uAE38\uC774: "}, + {"Certificate.i.1.", "\uC778\uC99D\uC11C[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "\uC778\uC99D\uC11C \uC9C0\uBB38(SHA1): "}, + {"Keystore.type.", "\uD0A4 \uC800\uC7A5\uC18C \uC720\uD615: "}, + {"Keystore.provider.", "\uD0A4 \uC800\uC7A5\uC18C \uC81C\uACF5\uC790: "}, + {"Your.keystore.contains.keyStore.size.entry", + "\uD0A4 \uC800\uC7A5\uC18C\uC5D0 {0,number,integer}\uAC1C\uC758 \uD56D\uBAA9\uC774 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."}, + {"Your.keystore.contains.keyStore.size.entries", + "\uD0A4 \uC800\uC7A5\uC18C\uC5D0 {0,number,integer}\uAC1C\uC758 \uD56D\uBAA9\uC774 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."}, + {"Failed.to.parse.input", "\uC785\uB825\uAC12\uC758 \uAD6C\uBB38 \uBD84\uC11D\uC744 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."}, + {"Empty.input", "\uC785\uB825\uAC12\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."}, + {"Not.X.509.certificate", "X.509 \uC778\uC99D\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4."}, + {"alias.has.no.public.key", "{0}\uC5D0 \uACF5\uC6A9 \uD0A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"alias.has.no.X.509.certificate", "{0}\uC5D0 X.509 \uC778\uC99D\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"New.certificate.self.signed.", "\uC0C8 \uC778\uC99D\uC11C(\uC790\uCCB4 \uC11C\uBA85):"}, + {"Reply.has.no.certificates", "\uD68C\uC2E0\uC5D0 \uC778\uC99D\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Certificate.not.imported.alias.alias.already.exists", + "\uC778\uC99D\uC11C\uAC00 \uC784\uD3EC\uD2B8\uB418\uC9C0 \uC54A\uC558\uC73C\uBA70 <{0}> \uBCC4\uCE6D\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Input.not.an.X.509.certificate", "\uC785\uB825\uC774 X.509 \uC778\uC99D\uC11C\uAC00 \uC544\uB2D9\uB2C8\uB2E4."}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "\uC778\uC99D\uC11C\uAC00 <{0}> \uBCC4\uCE6D \uC544\uB798\uC758 \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Do.you.still.want.to.add.it.no.", + "\uCD94\uAC00\uD558\uACA0\uC2B5\uB2C8\uAE4C? [\uC544\uB2C8\uC624]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "\uC778\uC99D\uC11C\uAC00 <{0}> \uBCC4\uCE6D \uC544\uB798\uC5D0 \uC788\uB294 \uC2DC\uC2A4\uD15C \uCC28\uC6D0\uC758 CA \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\uACE0\uC720\uD55C \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uCD94\uAC00\uD558\uACA0\uC2B5\uB2C8\uAE4C? [\uC544\uB2C8\uC624]: "}, + {"Trust.this.certificate.no.", "\uC774 \uC778\uC99D\uC11C\uB97C \uC2E0\uB8B0\uD569\uB2C8\uAE4C? [\uC544\uB2C8\uC624]: "}, + {"YES", "\uC608"}, + {"New.prompt.", "\uC0C8 {0}: "}, + {"Passwords.must.differ", "\uBE44\uBC00\uBC88\uD638\uB294 \uB2EC\uB77C\uC57C \uD569\uB2C8\uB2E4."}, + {"Re.enter.new.prompt.", "\uC0C8 {0} \uB2E4\uC2DC \uC785\uB825: "}, + {"Re.enter.password.", "\uBE44\uBC00\uBC88\uD638 \uB2E4\uC2DC \uC785\uB825: "}, + {"Re.enter.new.password.", "\uC0C8 \uBE44\uBC00\uBC88\uD638 \uB2E4\uC2DC \uC785\uB825: "}, + {"They.don.t.match.Try.again", "\uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC2ED\uC2DC\uC624."}, + {"Enter.prompt.alias.name.", "{0} \uBCC4\uCE6D \uC774\uB984 \uC785\uB825: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "\uC0C8 \uBCC4\uCE6D \uC774\uB984 \uC785\uB825\t(\uC774 \uD56D\uBAA9\uC5D0 \uB300\uD55C \uC784\uD3EC\uD2B8\uB97C \uCDE8\uC18C\uD558\uB824\uBA74 Enter \uD0A4\uB97C \uB204\uB984): "}, + {"Enter.alias.name.", "\uBCC4\uCE6D \uC774\uB984 \uC785\uB825: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(<{0}>\uACFC(\uC640) \uB3D9\uC77C\uD55C \uACBD\uC6B0 Enter \uD0A4\uB97C \uB204\uB984)"}, + {".PATTERN.printX509Cert", + "\uC18C\uC720\uC790: {0}\n\uBC1C\uD589\uC790: {1}\n\uC77C\uB828 \uBC88\uD638: {2}\n\uC801\uD569\uD55C \uC2DC\uC791 \uB0A0\uC9DC: {3}, \uC885\uB8CC \uB0A0\uC9DC: {4}\n\uC778\uC99D\uC11C \uC9C0\uBB38:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t \uC11C\uBA85 \uC54C\uACE0\uB9AC\uC998 \uC774\uB984: {8}\n\t \uBC84\uC804: {9}"}, + {"What.is.your.first.and.last.name.", + "\uC774\uB984\uACFC \uC131\uC744 \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"What.is.the.name.of.your.organizational.unit.", + "\uC870\uC9C1 \uB2E8\uC704 \uC774\uB984\uC744 \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"What.is.the.name.of.your.organization.", + "\uC870\uC9C1 \uC774\uB984\uC744 \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"What.is.the.name.of.your.City.or.Locality.", + "\uAD6C/\uAD70/\uC2DC \uC774\uB984\uC744 \uC785\uB825\uD558\uC2ED\uC2DC\uC624?"}, + {"What.is.the.name.of.your.State.or.Province.", + "\uC2DC/\uB3C4 \uC774\uB984\uC744 \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\uC774 \uC870\uC9C1\uC758 \uB450 \uC790\uB9AC \uAD6D\uAC00 \uCF54\uB4DC\uB97C \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"Is.name.correct.", "{0}\uC774(\uAC00) \uB9DE\uC2B5\uB2C8\uAE4C?"}, + {"no", "\uC544\uB2C8\uC624"}, + {"yes", "\uC608"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "<{0}> \uBCC4\uCE6D\uC5D0 \uD0A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "<{0}> \uBCC4\uCE6D\uC740 \uC804\uC6A9 \uD0A4 \uD56D\uBAA9\uC774 \uC544\uB2CC \uD56D\uBAA9 \uC720\uD615\uC744 \uCC38\uC870\uD569\uB2C8\uB2E4. -keyclone \uBA85\uB839\uC740 \uC804\uC6A9 \uD0A4 \uD56D\uBAA9\uC758 \uBCF5\uC81C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4."}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "\uC11C\uBA85\uC790 #%d:"}, + {"Timestamp.", "\uC2DC\uAC04 \uAE30\uB85D:"}, + {"Signature.", "\uC11C\uBA85:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "\uC778\uC99D\uC11C \uC18C\uC720\uC790: "}, + {"Not.a.signed.jar.file", "\uC11C\uBA85\uB41C jar \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4."}, + {"No.certificate.from.the.SSL.server", + "SSL \uC11C\uBC84\uC5D0\uC11C \uAC00\uC838\uC628 \uC778\uC99D\uC11C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* \uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC800\uC7A5\uB41C \uC815\uBCF4\uC758 \uBB34\uACB0\uC131\uC774 *\n* \uD655\uC778\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4! \uBB34\uACB0\uC131\uC744 \uD655\uC778\uD558\uB824\uBA74, *\n* \uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638\uB97C \uC81C\uACF5\uD574\uC57C \uD569\uB2C8\uB2E4. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* srckeystore\uC5D0 \uC800\uC7A5\uB41C \uC815\uBCF4\uC758 \uBB34\uACB0\uC131\uC774 *\n* \uD655\uC778\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4! \uBB34\uACB0\uC131\uC744 \uD655\uC778\uD558\uB824\uBA74, *\n* srckeystore \uBE44\uBC00\uBC88\uD638\uB97C \uC81C\uACF5\uD574\uC57C \uD569\uB2C8\uB2E4. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "\uC778\uC99D\uC11C \uD68C\uC2E0\uC5D0 <{0}>\uC5D0 \uB300\uD55C \uACF5\uC6A9 \uD0A4\uAC00 \uD3EC\uD568\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"Incomplete.certificate.chain.in.reply", + "\uD68C\uC2E0\uC5D0 \uBD88\uC644\uC804\uD55C \uC778\uC99D\uC11C \uCCB4\uC778\uC774 \uC788\uC2B5\uB2C8\uB2E4."}, + {"Certificate.chain.in.reply.does.not.verify.", + "\uD68C\uC2E0\uC758 \uC778\uC99D\uC11C \uCCB4\uC778\uC774 \uD655\uC778\uB418\uC9C0 \uC54A\uC74C: "}, + {"Top.level.certificate.in.reply.", + "\uD68C\uC2E0\uC5D0 \uCD5C\uC0C1\uC704 \uB808\uBCA8 \uC778\uC99D\uC11C\uAC00 \uC788\uC74C:\n"}, + {".is.not.trusted.", "...\uC744(\uB97C) \uC2E0\uB8B0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. "}, + {"Install.reply.anyway.no.", "\uD68C\uC2E0\uC744 \uC124\uCE58\uD558\uACA0\uC2B5\uB2C8\uAE4C? [\uC544\uB2C8\uC624]: "}, + {"NO", "\uC544\uB2C8\uC624"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "\uD68C\uC2E0\uACFC \uD0A4 \uC800\uC7A5\uC18C\uC758 \uACF5\uC6A9 \uD0A4\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "\uD68C\uC2E0\uACFC \uD0A4 \uC800\uC7A5\uC18C\uC758 \uC778\uC99D\uC11C\uAC00 \uB3D9\uC77C\uD569\uB2C8\uB2E4."}, + {"Failed.to.establish.chain.from.reply", + "\uD68C\uC2E0\uC758 \uCCB4\uC778 \uC124\uC815\uC744 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."}, + {"n", "n"}, + {"Wrong.answer.try.again", "\uC798\uBABB\uB41C \uC751\uB2F5\uC785\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC2ED\uC2DC\uC624."}, + {"Secret.key.not.generated.alias.alias.already.exists", + "\uBCF4\uC548 \uD0A4\uAC00 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC558\uC73C\uBA70 <{0}> \uBCC4\uCE6D\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4."}, + {"Please.provide.keysize.for.secret.key.generation", + "\uBCF4\uC548 \uD0A4\uB97C \uC0DD\uC131\uD558\uB824\uBA74 -keysize\uB97C \uC81C\uACF5\uD558\uC2ED\uC2DC\uC624."}, + + {"verified.by.s.in.s", "%s(%s)\uC5D0 \uC758\uD574 \uD655\uC778\uB428"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "\uACBD\uACE0: \uD655\uC778\uB418\uC9C0 \uC54A\uC74C. -keystore\uAC00 \uC62C\uBC14\uB978\uC9C0 \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + + {"Extensions.", "\uD655\uC7A5: "}, + {".Empty.value.", "(\uBE44\uC5B4 \uC788\uB294 \uAC12)"}, + {"Extension.Request.", "\uD655\uC7A5 \uC694\uCCAD:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 \uC778\uC99D\uC11C \uC694\uCCAD(1.0 \uBC84\uC804)\n\uC81C\uBAA9: %s\n\uACF5\uC6A9 \uD0A4: %s \uD615\uC2DD %s \uD0A4\n"}, + {"Unknown.keyUsage.type.", "\uC54C \uC218 \uC5C6\uB294 keyUsage \uC720\uD615: "}, + {"Unknown.extendedkeyUsage.type.", "\uC54C \uC218 \uC5C6\uB294 extendedkeyUsage \uC720\uD615: "}, + {"Unknown.AccessDescription.type.", "\uC54C \uC218 \uC5C6\uB294 AccessDescription \uC720\uD615: "}, + {"Unrecognized.GeneralName.type.", "\uC54C \uC218 \uC5C6\uB294 GeneralName \uC720\uD615: "}, + {"This.extension.cannot.be.marked.as.critical.", + "\uC774 \uD655\uC7A5\uC740 \uC911\uC694\uD55C \uAC83\uC73C\uB85C \uD45C\uC2DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. "}, + {"Odd.number.of.hex.digits.found.", "\uD640\uC218 \uAC1C\uC758 16\uC9C4\uC218\uAC00 \uBC1C\uACAC\uB428: "}, + {"Unknown.extension.type.", "\uC54C \uC218 \uC5C6\uB294 \uD655\uC7A5 \uC720\uD615: "}, + {"command.{0}.is.ambiguous.", "{0} \uBA85\uB839\uC774 \uBAA8\uD638\uD568:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_pt_BR.java b/src/sun/security/tools/keytool/Resources_pt_BR.java new file mode 100644 index 00000000..0b976a35 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_pt_BR.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_pt_BR extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Op\u00E7\u00F5es:"}, + {"Use.keytool.help.for.all.available.commands", + "Use \"keytool -help\" para todos os comandos dispon\u00EDveis"}, + {"Key.and.Certificate.Management.Tool", + "Ferramenta de Gerenciamento de Chave e Certificado"}, + {"Commands.", "Comandos:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "Use \"keytool -command_name -help\" para uso de command_name"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Gera uma solicita\u00E7\u00E3o de certificado"}, //-certreq + {"Changes.an.entry.s.alias", + "Altera um alias de entrada"}, //-changealias + {"Deletes.an.entry", + "Exclui uma entrada"}, //-delete + {"Exports.certificate", + "Exporta o certificado"}, //-exportcert + {"Generates.a.key.pair", + "Gera um par de chaves"}, //-genkeypair + {"Generates.a.secret.key", + "Gera uma chave secreta"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Gera um certificado de uma solicita\u00E7\u00E3o de certificado"}, //-gencert + {"Generates.CRL", "Gera CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Chave secreta {0} gerada"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Chave secreta {1} de {0} bits gerada"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importa entradas de um banco de dados de identidade JDK 1.1.x-style"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importa um certificado ou uma cadeia de certificados"}, //-importcert + {"Imports.a.password", + "Importa uma senha"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importa uma ou todas as entradas de outra \u00E1rea de armazenamento de chaves"}, //-importkeystore + {"Clones.a.key.entry", + "Clona uma entrada de chave"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "Altera a senha da chave de uma entrada"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Lista entradas em uma \u00E1rea de armazenamento de chaves"}, //-list + {"Prints.the.content.of.a.certificate", + "Imprime o conte\u00FAdo de um certificado"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Imprime o conte\u00FAdo de uma solicita\u00E7\u00E3o de certificado"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Imprime o conte\u00FAdo de um arquivo CRL"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Gera um certificado autoassinado"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "Altera a senha de armazenamento de uma \u00E1rea de armazenamento de chaves"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "nome do alias da entrada a ser processada"}, //-alias + {"destination.alias", + "alias de destino"}, //-destalias + {"destination.key.password", + "senha da chave de destino"}, //-destkeypass + {"destination.keystore.name", + "nome da \u00E1rea de armazenamento de chaves de destino"}, //-destkeystore + {"destination.keystore.password.protected", + "senha protegida da \u00E1rea de armazenamento de chaves de destino"}, //-destprotected + {"destination.keystore.provider.name", + "nome do fornecedor da \u00E1rea de armazenamento de chaves de destino"}, //-destprovidername + {"destination.keystore.password", + "senha da \u00E1rea de armazenamento de chaves de destino"}, //-deststorepass + {"destination.keystore.type", + "tipo de \u00E1rea de armazenamento de chaves de destino"}, //-deststoretype + {"distinguished.name", + "nome distinto"}, //-dname + {"X.509.extension", + "extens\u00E3o X.509"}, //-ext + {"output.file.name", + "nome do arquivo de sa\u00EDda"}, //-file and -outfile + {"input.file.name", + "nome do arquivo de entrada"}, //-file and -infile + {"key.algorithm.name", + "nome do algoritmo da chave"}, //-keyalg + {"key.password", + "senha da chave"}, //-keypass + {"key.bit.size", + "tamanho do bit da chave"}, //-keysize + {"keystore.name", + "nome da \u00E1rea de armazenamento de chaves"}, //-keystore + {"new.password", + "nova senha"}, //-new + {"do.not.prompt", + "n\u00E3o perguntar"}, //-noprompt + {"password.through.protected.mechanism", + "senha por meio de mecanismo protegido"}, //-protected + {"provider.argument", + "argumento do fornecedor"}, //-providerarg + {"provider.class.name", + "nome da classe do fornecedor"}, //-providerclass + {"provider.name", + "nome do fornecedor"}, //-providername + {"provider.classpath", + "classpath do fornecedor"}, //-providerpath + {"output.in.RFC.style", + "sa\u00EDda no estilo RFC"}, //-rfc + {"signature.algorithm.name", + "nome do algoritmo de assinatura"}, //-sigalg + {"source.alias", + "alias de origem"}, //-srcalias + {"source.key.password", + "senha da chave de origem"}, //-srckeypass + {"source.keystore.name", + "nome da \u00E1rea de armazenamento de chaves de origem"}, //-srckeystore + {"source.keystore.password.protected", + "senha protegida da \u00E1rea de armazenamento de chaves de origem"}, //-srcprotected + {"source.keystore.provider.name", + "nome do fornecedor da \u00E1rea de armazenamento de chaves de origem"}, //-srcprovidername + {"source.keystore.password", + "senha da \u00E1rea de armazenamento de chaves de origem"}, //-srcstorepass + {"source.keystore.type", + "tipo de \u00E1rea de armazenamento de chaves de origem"}, //-srcstoretype + {"SSL.server.host.and.port", + "porta e host do servidor SSL"}, //-sslserver + {"signed.jar.file", + "arquivo jar assinado"}, //=jarfile + {"certificate.validity.start.date.time", + "data/hora inicial de validade do certificado"}, //-startdate + {"keystore.password", + "senha da \u00E1rea de armazenamento de chaves"}, //-storepass + {"keystore.type", + "tipo de \u00E1rea de armazenamento de chaves"}, //-storetype + {"trust.certificates.from.cacerts", + "certificados confi\u00E1veis do cacerts"}, //-trustcacerts + {"verbose.output", + "sa\u00EDda detalhada"}, //-v + {"validity.number.of.days", + "n\u00FAmero de dias da validade"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "ID de s\u00E9rie do certificado a ser revogado"}, //-id + // keytool: Running part + {"keytool.error.", "erro de keytool: "}, + {"Illegal.option.", "Op\u00E7\u00E3o inv\u00E1lida: "}, + {"Illegal.value.", "Valor inv\u00E1lido: "}, + {"Unknown.password.type.", "Tipo de senha desconhecido: "}, + {"Cannot.find.environment.variable.", + "N\u00E3o \u00E9 poss\u00EDvel localizar a vari\u00E1vel do ambiente: "}, + {"Cannot.find.file.", "N\u00E3o \u00E9 poss\u00EDvel localizar o arquivo: "}, + {"Command.option.flag.needs.an.argument.", "A op\u00E7\u00E3o de comando {0} precisa de um argumento."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Advert\u00EAncia: Senhas de chave e de armazenamento diferentes n\u00E3o suportadas para KeyStores PKCS12. Ignorando valor {0} especificado pelo usu\u00E1rio."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore deve ser NONE se -storetype for {0}"}, + {"Too.many.retries.program.terminated", + "Excesso de tentativas de repeti\u00E7\u00E3o; programa finalizado"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "comandos -storepasswd e -keypasswd n\u00E3o suportados se -storetype for {0}"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "comandos -keypasswd n\u00E3o suportados se -storetype for PKCS12"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-keypass e -new n\u00E3o podem ser especificados se -storetype for {0}"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "se -protected for especificado, ent\u00E3o -storepass, -keypass e -new n\u00E3o dever\u00E3o ser especificados"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "se -srcprotected for especificado, ent\u00E3o -srcstorepass e -srckeypass n\u00E3o dever\u00E3o ser especificados"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "se a \u00E1rea de armazenamento de chaves n\u00E3o estiver protegida por senha, ent\u00E3o -storepass, -keypass e -new n\u00E3o dever\u00E3o ser especificados"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "se a \u00E1rea de armazenamento de chaves de origem n\u00E3o estiver protegida por senha, ent\u00E3o -srcstorepass e -srckeypass n\u00E3o dever\u00E3o ser especificados"}, + {"Illegal.startdate.value", "valor da data inicial inv\u00E1lido"}, + {"Validity.must.be.greater.than.zero", + "A validade deve ser maior do que zero"}, + {"provName.not.a.provider", "{0} n\u00E3o \u00E9 um fornecedor"}, + {"Usage.error.no.command.provided", "Erro de uso: nenhum comando fornecido"}, + {"Source.keystore.file.exists.but.is.empty.", "O arquivo da \u00E1rea de armazenamento de chaves de origem existe, mas est\u00E1 vazio: "}, + {"Please.specify.srckeystore", "Especifique -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "N\u00E3o devem ser especificados -v e -rfc com o comando 'list'"}, + {"Key.password.must.be.at.least.6.characters", + "A senha da chave deve ter, no m\u00EDnimo, 6 caracteres"}, + {"New.password.must.be.at.least.6.characters", + "A nova senha deve ter, no m\u00EDnimo, 6 caracteres"}, + {"Keystore.file.exists.but.is.empty.", + "O arquivo da \u00E1rea de armazenamento de chaves existe, mas est\u00E1 vazio: "}, + {"Keystore.file.does.not.exist.", + "O arquivo da \u00E1rea de armazenamento de chaves n\u00E3o existe. "}, + {"Must.specify.destination.alias", "Deve ser especificado um alias de destino"}, + {"Must.specify.alias", "Deve ser especificado um alias"}, + {"Keystore.password.must.be.at.least.6.characters", + "A senha da \u00E1rea de armazenamento de chaves deve ter, no m\u00EDnimo, 6 caracteres"}, + {"Enter.the.password.to.be.stored.", + "Digite a senha a ser armazenada: "}, + {"Enter.keystore.password.", "Informe a senha da \u00E1rea de armazenamento de chaves: "}, + {"Enter.source.keystore.password.", "Informe a senha da \u00E1rea de armazenamento de chaves de origem: "}, + {"Enter.destination.keystore.password.", "Informe a senha da \u00E1rea de armazenamento de chaves de destino: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "A senha da \u00E1rea de armazenamento de chaves \u00E9 muito curta - ela deve ter, no m\u00EDnimo, 6 caracteres"}, + {"Unknown.Entry.Type", "Tipo de Entrada Desconhecido"}, + {"Too.many.failures.Alias.not.changed", "Excesso de falhas. Alias n\u00E3o alterado"}, + {"Entry.for.alias.alias.successfully.imported.", + "Entrada do alias {0} importada com \u00EAxito."}, + {"Entry.for.alias.alias.not.imported.", "Entrada do alias {0} n\u00E3o importada."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Problema ao importar a entrada do alias {0}: {1}.\nEntrada do alias {0} n\u00E3o importada."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Comando de importa\u00E7\u00E3o conclu\u00EDdo: {0} entradas importadas com \u00EAxito, {1} entradas falharam ou foram canceladas"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Advert\u00EAncia: Substitui\u00E7\u00E3o do alias {0} existente na \u00E1rea de armazenamento de chaves de destino"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "Entrada j\u00E1 existente no alias {0}, substituir? [n\u00E3o]: "}, + {"Too.many.failures.try.later", "Excesso de falhas - tente mais tarde"}, + {"Certification.request.stored.in.file.filename.", + "Solicita\u00E7\u00E3o de certificado armazenada no arquivo <{0}>"}, + {"Submit.this.to.your.CA", "Submeter \u00E0 CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "se o alias n\u00E3o estiver especificado, destalias e srckeypass n\u00E3o dever\u00E3o ser especificados"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "O armazenamento de chaves pkcs12 de destino tem storepass e keypass diferentes. Tente novamente especificando -destkeypass."}, + {"Certificate.stored.in.file.filename.", + "Certificado armazenado no arquivo <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "A resposta do certificado foi instalada na \u00E1rea de armazenamento de chaves"}, + {"Certificate.reply.was.not.installed.in.keystore", + "A resposta do certificado n\u00E3o foi instalada na \u00E1rea de armazenamento de chaves"}, + {"Certificate.was.added.to.keystore", + "O certificado foi adicionado \u00E0 \u00E1rea de armazenamento de chaves"}, + {"Certificate.was.not.added.to.keystore", + "O certificado n\u00E3o foi adicionado \u00E0 \u00E1rea de armazenamento de chaves"}, + {".Storing.ksfname.", "[Armazenando {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} n\u00E3o tem chave p\u00FAblica (certificado)"}, + {"Cannot.derive.signature.algorithm", + "N\u00E3o \u00E9 poss\u00EDvel obter um algoritmo de assinatura"}, + {"Alias.alias.does.not.exist", + "O alias <{0}> n\u00E3o existe"}, + {"Alias.alias.has.no.certificate", + "O alias <{0}> n\u00E3o tem certificado"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Par de chaves n\u00E3o gerado; o alias <{0}> j\u00E1 existe"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Gerando o par de chaves {1} de {0} bit e o certificado autoassinado ({2}) com uma validade de {3} dias\n\tpara: {4}"}, + {"Enter.key.password.for.alias.", "Informar a senha da chave de <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN se for igual \u00E0 senha da \u00E1rea do armazenamento de chaves): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "A senha da chave \u00E9 muito curta - deve ter, no m\u00EDnimo, 6 caracteres"}, + {"Too.many.failures.key.not.added.to.keystore", + "Excesso de falhas - chave n\u00E3o adicionada a \u00E1rea de armazenamento de chaves"}, + {"Destination.alias.dest.already.exists", + "O alias de destino <{0}> j\u00E1 existe"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "A senha \u00E9 muito curta - deve ter, no m\u00EDnimo, 6 caracteres"}, + {"Too.many.failures.Key.entry.not.cloned", + "Excesso de falhas. Entrada da chave n\u00E3o clonada"}, + {"key.password.for.alias.", "senha da chave de <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "A entrada da \u00E1rea do armazenamento de chaves de <{0}> j\u00E1 existe"}, + {"Creating.keystore.entry.for.id.getName.", + "Criando entrada da \u00E1rea do armazenamento de chaves para <{0}> ..."}, + {"No.entries.from.identity.database.added", + "Nenhuma entrada adicionada do banco de dados de identidades"}, + {"Alias.name.alias", "Nome do alias: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Data de cria\u00E7\u00E3o: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Tipo de entrada: {0}"}, + {"Certificate.chain.length.", "Comprimento da cadeia de certificados: "}, + {"Certificate.i.1.", "Certificado[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Fingerprint (SHA1) do certificado: "}, + {"Keystore.type.", "Tipo de \u00E1rea de armazenamento de chaves: "}, + {"Keystore.provider.", "Fornecedor da \u00E1rea de armazenamento de chaves: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Sua \u00E1rea de armazenamento de chaves cont\u00E9m {0,number,integer} entrada"}, + {"Your.keystore.contains.keyStore.size.entries", + "Sua \u00E1rea de armazenamento de chaves cont\u00E9m {0,number,integer} entradas"}, + {"Failed.to.parse.input", "Falha durante o parsing da entrada"}, + {"Empty.input", "Entrada vazia"}, + {"Not.X.509.certificate", "N\u00E3o \u00E9 um certificado X.509"}, + {"alias.has.no.public.key", "{0} n\u00E3o tem chave p\u00FAblica"}, + {"alias.has.no.X.509.certificate", "{0} n\u00E3o tem certificado X.509"}, + {"New.certificate.self.signed.", "Novo certificado (autoassinado):"}, + {"Reply.has.no.certificates", "A resposta n\u00E3o tem certificado"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Certificado n\u00E3o importado, o alias <{0}> j\u00E1 existe"}, + {"Input.not.an.X.509.certificate", "A entrada n\u00E3o \u00E9 um certificado X.509"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "O certificado j\u00E1 existe no armazenamento de chaves no alias <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "Ainda deseja adicion\u00E1-lo? [n\u00E3o]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "O certificado j\u00E1 existe na \u00E1rea de armazenamento de chaves da CA em todo o sistema no alias <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "Ainda deseja adicion\u00E1-lo \u00E0 sua \u00E1rea de armazenamento de chaves? [n\u00E3o]: "}, + {"Trust.this.certificate.no.", "Confiar neste certificado? [n\u00E3o]: "}, + {"YES", "SIM"}, + {"New.prompt.", "Nova {0}: "}, + {"Passwords.must.differ", "As senhas devem ser diferentes"}, + {"Re.enter.new.prompt.", "Informe novamente a nova {0}: "}, + {"Re.enter.password.", "Redigite a senha: "}, + {"Re.enter.new.password.", "Informe novamente a nova senha: "}, + {"They.don.t.match.Try.again", "Elas n\u00E3o correspondem. Tente novamente"}, + {"Enter.prompt.alias.name.", "Informe o nome do alias {0}: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Informe o novo nome do alias\t(RETURN para cancelar a importa\u00E7\u00E3o desta entrada): "}, + {"Enter.alias.name.", "Informe o nome do alias: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN se for igual ao de <{0}>)"}, + {".PATTERN.printX509Cert", + "Propriet\u00E1rio: {0}\nEmissor: {1}\nN\u00FAmero de s\u00E9rie: {2}\nV\u00E1lido de: {3} a: {4}\nFingerprints do certificado:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Nome do algoritmo de assinatura: {8}\n\t Vers\u00E3o: {9}"}, + {"What.is.your.first.and.last.name.", + "Qual \u00E9 o seu nome e o seu sobrenome?"}, + {"What.is.the.name.of.your.organizational.unit.", + "Qual \u00E9 o nome da sua unidade organizacional?"}, + {"What.is.the.name.of.your.organization.", + "Qual \u00E9 o nome da sua empresa?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "Qual \u00E9 o nome da sua Cidade ou Localidade?"}, + {"What.is.the.name.of.your.State.or.Province.", + "Qual \u00E9 o nome do seu Estado ou Munic\u00EDpio?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "Quais s\u00E3o as duas letras do c\u00F3digo do pa\u00EDs desta unidade?"}, + {"Is.name.correct.", "{0} Est\u00E1 correto?"}, + {"no", "n\u00E3o"}, + {"yes", "sim"}, + {"y", "s"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "O alias <{0}> n\u00E3o tem chave"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "O alias <{0}> faz refer\u00EAncia a um tipo de entrada que n\u00E3o \u00E9 uma entrada de chave privada. O comando -keyclone oferece suporte somente \u00E0 clonagem de entradas de chave privada"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Signat\u00E1rio #%d:"}, + {"Timestamp.", "Timestamp:"}, + {"Signature.", "Assinatura:"}, + {"CRLs.", "CRLs:"}, + {"Certificate.owner.", "Propriet\u00E1rio do certificado: "}, + {"Not.a.signed.jar.file", "N\u00E3o \u00E9 um arquivo jar assinado"}, + {"No.certificate.from.the.SSL.server", + "N\u00E3o \u00E9 um certificado do servidor SSL"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* A integridade das informa\u00E7\u00F5es armazenadas na sua \u00E1rea de armazenamento de chaves *\n* N\u00C3O foi verificada! Para que seja poss\u00EDvel verificar sua integridade, *\n* voc\u00EA deve fornecer a senha da \u00E1rea de armazenamento de chaves. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* A integridade das informa\u00E7\u00F5es armazenadas no srckeystore *\n* N\u00C3O foi verificada! Para que seja poss\u00EDvel verificar sua integridade, *\n* voc\u00EA deve fornecer a senha do srckeystore. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "A resposta do certificado n\u00E3o cont\u00E9m a chave p\u00FAblica de <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Cadeia de certificados incompleta na resposta"}, + {"Certificate.chain.in.reply.does.not.verify.", + "A cadeia de certificados da resposta n\u00E3o verifica: "}, + {"Top.level.certificate.in.reply.", + "Certificado de n\u00EDvel superior na resposta:\n"}, + {".is.not.trusted.", "... n\u00E3o \u00E9 confi\u00E1vel. "}, + {"Install.reply.anyway.no.", "Instalar resposta assim mesmo? [n\u00E3o]: "}, + {"NO", "N\u00C3O"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "As chaves p\u00FAblicas da resposta e da \u00E1rea de armazenamento de chaves n\u00E3o correspondem"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "O certificado da resposta e o certificado da \u00E1rea de armazenamento de chaves s\u00E3o id\u00EAnticos"}, + {"Failed.to.establish.chain.from.reply", + "Falha ao estabelecer a cadeia a partir da resposta"}, + {"n", "n"}, + {"Wrong.answer.try.again", "Resposta errada; tente novamente"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "Chave secreta n\u00E3o gerada; o alias <{0}> j\u00E1 existe"}, + {"Please.provide.keysize.for.secret.key.generation", + "Forne\u00E7a o -keysize para a gera\u00E7\u00E3o da chave secreta"}, + + {"verified.by.s.in.s", "Verificado por %s em %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "ADVERT\u00CANCIA: n\u00E3o verificado. Certifique-se que -keystore esteja correto."}, + + {"Extensions.", "Extens\u00F5es: "}, + {".Empty.value.", "(Valor vazio)"}, + {"Extension.Request.", "Solicita\u00E7\u00E3o de Extens\u00E3o:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "Solicita\u00E7\u00E3o do Certificado PKCS #10 (Vers\u00E3o 1.0)\nAssunto: %s\nChave P\u00FAblica: %s formato %s chave\n"}, + {"Unknown.keyUsage.type.", "Tipo de keyUsage desconhecido: "}, + {"Unknown.extendedkeyUsage.type.", "Tipo de extendedkeyUsage desconhecido: "}, + {"Unknown.AccessDescription.type.", "Tipo de AccessDescription desconhecido: "}, + {"Unrecognized.GeneralName.type.", "Tipo de GeneralName n\u00E3o reconhecido: "}, + {"This.extension.cannot.be.marked.as.critical.", + "Esta extens\u00E3o n\u00E3o pode ser marcada como cr\u00EDtica. "}, + {"Odd.number.of.hex.digits.found.", "Encontrado n\u00FAmero \u00EDmpar de seis d\u00EDgitos: "}, + {"Unknown.extension.type.", "Tipo de extens\u00E3o desconhecido: "}, + {"command.{0}.is.ambiguous.", "o comando {0} \u00E9 amb\u00EDguo:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_sv.java b/src/sun/security/tools/keytool/Resources_sv.java new file mode 100644 index 00000000..c6b4a849 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_sv.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_sv extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "Alternativ:"}, + {"Use.keytool.help.for.all.available.commands", + "L\u00E4s \"Hj\u00E4lp - Nyckelverktyg\" f\u00F6r alla tillg\u00E4ngliga kommandon"}, + {"Key.and.Certificate.Management.Tool", + "Hanteringsverktyg f\u00F6r nycklar och certifikat"}, + {"Commands.", "Kommandon:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "L\u00E4s \"Hj\u00E4lp - Nyckelverktyg - command_name\" om anv\u00E4ndning av command_name"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "Genererar certifikatbeg\u00E4ran"}, //-certreq + {"Changes.an.entry.s.alias", + "\u00C4ndrar postalias"}, //-changealias + {"Deletes.an.entry", + "Tar bort post"}, //-delete + {"Exports.certificate", + "Exporterar certifikat"}, //-exportcert + {"Generates.a.key.pair", + "Genererar nyckelpar"}, //-genkeypair + {"Generates.a.secret.key", + "Genererar hemlig nyckel"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "Genererar certifikat fr\u00E5n certifikatbeg\u00E4ran"}, //-gencert + {"Generates.CRL", "Genererar CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "Genererade {0} hemlig nyckel"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "Genererade {0}-bitars {1} hemlig nyckel"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "Importerar poster fr\u00E5n identitetsdatabas i JDK 1.1.x-format"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "Importerar ett certifikat eller en certifikatkedja"}, //-importcert + {"Imports.a.password", + "Importerar ett l\u00F6senord"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "Importerar en eller alla poster fr\u00E5n annat nyckellager"}, //-importkeystore + {"Clones.a.key.entry", + "Klonar en nyckelpost"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u00C4ndrar nyckell\u00F6senordet f\u00F6r en post"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "Visar lista \u00F6ver poster i nyckellager"}, //-list + {"Prints.the.content.of.a.certificate", + "Skriver ut inneh\u00E5llet i ett certifikat"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "Skriver ut inneh\u00E5llet i en certifikatbeg\u00E4ran"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "Skriver ut inneh\u00E5llet i en CRL-fil"}, //-printcrl + {"Generates.a.self.signed.certificate", + "Genererar ett sj\u00E4lvsignerat certifikat"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u00C4ndrar lagerl\u00F6senordet f\u00F6r ett nyckellager"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "aliasnamn f\u00F6r post som ska bearbetas"}, //-alias + {"destination.alias", + "destinationsalias"}, //-destalias + {"destination.key.password", + "l\u00F6senord f\u00F6r destinationsnyckel"}, //-destkeypass + {"destination.keystore.name", + "namn p\u00E5 destinationsnyckellager"}, //-destkeystore + {"destination.keystore.password.protected", + "skyddat l\u00F6senord f\u00F6r destinationsnyckellager"}, //-destprotected + {"destination.keystore.provider.name", + "leverant\u00F6rsnamn f\u00F6r destinationsnyckellager"}, //-destprovidername + {"destination.keystore.password", + "l\u00F6senord f\u00F6r destinationsnyckellager"}, //-deststorepass + {"destination.keystore.type", + "typ av destinationsnyckellager"}, //-deststoretype + {"distinguished.name", + "unikt namn"}, //-dname + {"X.509.extension", + "X.509-till\u00E4gg"}, //-ext + {"output.file.name", + "namn p\u00E5 utdatafil"}, //-file and -outfile + {"input.file.name", + "namn p\u00E5 indatafil"}, //-file and -infile + {"key.algorithm.name", + "namn p\u00E5 nyckelalgoritm"}, //-keyalg + {"key.password", + "nyckell\u00F6senord"}, //-keypass + {"key.bit.size", + "nyckelbitstorlek"}, //-keysize + {"keystore.name", + "namn p\u00E5 nyckellager"}, //-keystore + {"new.password", + "nytt l\u00F6senord"}, //-new + {"do.not.prompt", + "fr\u00E5ga inte"}, //-noprompt + {"password.through.protected.mechanism", + "l\u00F6senord med skyddad mekanism"}, //-protected + {"provider.argument", + "leverant\u00F6rsargument"}, //-providerarg + {"provider.class.name", + "namn p\u00E5 leverant\u00F6rsklass"}, //-providerclass + {"provider.name", + "leverant\u00F6rsnamn"}, //-providername + {"provider.classpath", + "leverant\u00F6rsklass\u00F6kv\u00E4g"}, //-providerpath + {"output.in.RFC.style", + "utdata i RFC-format"}, //-rfc + {"signature.algorithm.name", + "namn p\u00E5 signaturalgoritm"}, //-sigalg + {"source.alias", + "k\u00E4llalias"}, //-srcalias + {"source.key.password", + "l\u00F6senord f\u00F6r k\u00E4llnyckel"}, //-srckeypass + {"source.keystore.name", + "namn p\u00E5 k\u00E4llnyckellager"}, //-srckeystore + {"source.keystore.password.protected", + "skyddat l\u00F6senord f\u00F6r k\u00E4llnyckellager"}, //-srcprotected + {"source.keystore.provider.name", + "leverant\u00F6rsnamn f\u00F6r k\u00E4llnyckellager"}, //-srcprovidername + {"source.keystore.password", + "l\u00F6senord f\u00F6r k\u00E4llnyckellager"}, //-srcstorepass + {"source.keystore.type", + "typ av k\u00E4llnyckellager"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL-serverv\u00E4rd och -port"}, //-sslserver + {"signed.jar.file", + "signerad jar-fil"}, //=jarfile + {"certificate.validity.start.date.time", + "startdatum/-tid f\u00F6r certifikatets giltighet"}, //-startdate + {"keystore.password", + "l\u00F6senord f\u00F6r nyckellager"}, //-storepass + {"keystore.type", + "nyckellagertyp"}, //-storetype + {"trust.certificates.from.cacerts", + "tillf\u00F6rlitliga certifikat fr\u00E5n cacerts"}, //-trustcacerts + {"verbose.output", + "utf\u00F6rliga utdata"}, //-v + {"validity.number.of.days", + "antal dagar f\u00F6r giltighet"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "Seriellt ID f\u00F6r certifikat som ska \u00E5terkallas"}, //-id + // keytool: Running part + {"keytool.error.", "nyckelverktygsfel: "}, + {"Illegal.option.", "Otill\u00E5tet alternativ: "}, + {"Illegal.value.", "Otill\u00E5tet v\u00E4rde: "}, + {"Unknown.password.type.", "Ok\u00E4nd l\u00F6senordstyp: "}, + {"Cannot.find.environment.variable.", + "Hittar inte milj\u00F6variabel: "}, + {"Cannot.find.file.", "Hittar inte fil: "}, + {"Command.option.flag.needs.an.argument.", "Kommandoalternativet {0} beh\u00F6ver ett argument."}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "Varning! PKCS12-nyckellager har inte st\u00F6d f\u00F6r olika l\u00F6senord f\u00F6r lagret och nyckeln. Det anv\u00E4ndarspecificerade {0}-v\u00E4rdet ignoreras."}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "-keystore m\u00E5ste vara NONE om -storetype \u00E4r {0}"}, + {"Too.many.retries.program.terminated", + "F\u00F6r m\u00E5nga f\u00F6rs\u00F6k. Programmet avslutas"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "-storepasswd- och -keypasswd-kommandon st\u00F6ds inte om -storetype \u00E4r {0}"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "-keypasswd-kommandon st\u00F6ds inte om -storetype \u00E4r PKCS12"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "-keypass och -new kan inte anges om -storetype \u00E4r {0}"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "om -protected har angetts f\u00E5r inte -storepass, -keypass och -new anges"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "om -srcprotected anges f\u00E5r -srcstorepass och -srckeypass inte anges"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "om nyckellagret inte \u00E4r l\u00F6senordsskyddat f\u00E5r -storepass, -keypass och -new inte anges"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "om k\u00E4llnyckellagret inte \u00E4r l\u00F6senordsskyddat f\u00E5r -srcstorepass och -srckeypass inte anges"}, + {"Illegal.startdate.value", "Otill\u00E5tet v\u00E4rde f\u00F6r startdatum"}, + {"Validity.must.be.greater.than.zero", + "Giltigheten m\u00E5ste vara st\u00F6rre \u00E4n noll"}, + {"provName.not.a.provider", "{0} \u00E4r inte en leverant\u00F6r"}, + {"Usage.error.no.command.provided", "Syntaxfel: inget kommando angivet"}, + {"Source.keystore.file.exists.but.is.empty.", "Nyckellagrets k\u00E4llfil finns, men \u00E4r tom: "}, + {"Please.specify.srckeystore", "Ange -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "Kan inte specificera b\u00E5de -v och -rfc med 'list'-kommandot"}, + {"Key.password.must.be.at.least.6.characters", + "Nyckell\u00F6senordet m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"New.password.must.be.at.least.6.characters", + "Det nya l\u00F6senordet m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"Keystore.file.exists.but.is.empty.", + "Nyckellagerfilen finns, men \u00E4r tom: "}, + {"Keystore.file.does.not.exist.", + "Nyckellagerfilen finns inte: "}, + {"Must.specify.destination.alias", "Du m\u00E5ste ange destinationsalias"}, + {"Must.specify.alias", "Du m\u00E5ste ange alias"}, + {"Keystore.password.must.be.at.least.6.characters", + "Nyckellagerl\u00F6senordet m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"Enter.the.password.to.be.stored.", + "Ange det l\u00F6senord som ska lagras: "}, + {"Enter.keystore.password.", "Ange nyckellagerl\u00F6senord: "}, + {"Enter.source.keystore.password.", "Ange l\u00F6senord f\u00F6r k\u00E4llnyckellagret: "}, + {"Enter.destination.keystore.password.", "Ange nyckellagerl\u00F6senord f\u00F6r destination: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "Nyckellagerl\u00F6senordet \u00E4r f\u00F6r kort - det m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"Unknown.Entry.Type", "Ok\u00E4nd posttyp"}, + {"Too.many.failures.Alias.not.changed", "F\u00F6r m\u00E5nga fel. Alias har inte \u00E4ndrats"}, + {"Entry.for.alias.alias.successfully.imported.", + "Posten f\u00F6r alias {0} har importerats."}, + {"Entry.for.alias.alias.not.imported.", "Posten f\u00F6r alias {0} har inte importerats."}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "Ett problem uppstod vid importen av posten f\u00F6r alias {0}: {1}.\nPosten {0} har inte importerats."}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "Kommandoimporten slutf\u00F6rd: {0} poster har importerats, {1} poster var felaktiga eller annullerades"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "Varning! Det befintliga aliaset {0} i destinationsnyckellagret skrivs \u00F6ver"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "Aliaset {0} finns redan. Vill du skriva \u00F6ver det? [nej]: "}, + {"Too.many.failures.try.later", "F\u00F6r m\u00E5nga fel - f\u00F6rs\u00F6k igen senare"}, + {"Certification.request.stored.in.file.filename.", + "Certifikatbeg\u00E4ran har lagrats i filen <{0}>"}, + {"Submit.this.to.your.CA", "Skicka detta till certifikatutf\u00E4rdaren"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "om alias inte angivits ska inte heller destalias och srckeypass anges"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "Destinationsnyckellagret pkcs12 har olika storepass och keypass. F\u00F6rs\u00F6k igen med -destkeypass angivet."}, + {"Certificate.stored.in.file.filename.", + "Certifikatet har lagrats i filen <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "Certifikatsvaret har installerats i nyckellagret"}, + {"Certificate.reply.was.not.installed.in.keystore", + "Certifikatsvaret har inte installerats i nyckellagret"}, + {"Certificate.was.added.to.keystore", + "Certifikatet har lagts till i nyckellagret"}, + {"Certificate.was.not.added.to.keystore", + "Certifikatet har inte lagts till i nyckellagret"}, + {".Storing.ksfname.", "[Lagrar {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} saknar offentlig nyckel (certifikat)"}, + {"Cannot.derive.signature.algorithm", + "Kan inte h\u00E4rleda signaturalgoritm"}, + {"Alias.alias.does.not.exist", + "Aliaset <{0}> finns inte"}, + {"Alias.alias.has.no.certificate", + "Aliaset <{0}> saknar certifikat"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "Nyckelparet genererades inte. Aliaset <{0}> finns redan"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "Genererar {0} bitars {1}-nyckelpar och sj\u00E4lvsignerat certifikat ({2}) med en giltighet p\u00E5 {3} dagar\n\tf\u00F6r: {4}"}, + {"Enter.key.password.for.alias.", "Ange nyckell\u00F6senord f\u00F6r <{0}>"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN om det \u00E4r identiskt med nyckellagerl\u00F6senordet): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "Nyckell\u00F6senordet \u00E4r f\u00F6r kort - det m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"Too.many.failures.key.not.added.to.keystore", + "F\u00F6r m\u00E5nga fel - nyckeln lades inte till i nyckellagret"}, + {"Destination.alias.dest.already.exists", + "Destinationsaliaset <{0}> finns redan"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "L\u00F6senordet \u00E4r f\u00F6r kort - det m\u00E5ste inneh\u00E5lla minst 6 tecken"}, + {"Too.many.failures.Key.entry.not.cloned", + "F\u00F6r m\u00E5nga fel. Nyckelposten har inte klonats"}, + {"key.password.for.alias.", "nyckell\u00F6senord f\u00F6r <{0}>"}, + {"Keystore.entry.for.id.getName.already.exists", + "Nyckellagerpost f\u00F6r <{0}> finns redan"}, + {"Creating.keystore.entry.for.id.getName.", + "Skapar nyckellagerpost f\u00F6r <{0}> ..."}, + {"No.entries.from.identity.database.added", + "Inga poster fr\u00E5n identitetsdatabasen har lagts till"}, + {"Alias.name.alias", "Aliasnamn: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "Skapat den: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "Posttyp: {0}"}, + {"Certificate.chain.length.", "L\u00E4ngd p\u00E5 certifikatskedja: "}, + {"Certificate.i.1.", "Certifikat[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "Certifikatets fingeravtryck (SHA1): "}, + {"Keystore.type.", "Nyckellagertyp: "}, + {"Keystore.provider.", "Nyckellagerleverant\u00F6r: "}, + {"Your.keystore.contains.keyStore.size.entry", + "Nyckellagret inneh\u00E5ller {0,number,integer} post"}, + {"Your.keystore.contains.keyStore.size.entries", + "Nyckellagret inneh\u00E5ller {0,number,integer} poster"}, + {"Failed.to.parse.input", "Kunde inte tolka indata"}, + {"Empty.input", "Inga indata"}, + {"Not.X.509.certificate", "Inte ett X.509-certifikat"}, + {"alias.has.no.public.key", "{0} saknar offentlig nyckel"}, + {"alias.has.no.X.509.certificate", "{0} saknar X.509-certifikat"}, + {"New.certificate.self.signed.", "Nytt certifikat (sj\u00E4lvsignerat):"}, + {"Reply.has.no.certificates", "Svaret saknar certifikat"}, + {"Certificate.not.imported.alias.alias.already.exists", + "Certifikatet importerades inte. Aliaset <{0}> finns redan"}, + {"Input.not.an.X.509.certificate", "Indata \u00E4r inte ett X.509-certifikat"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "Certifikatet finns redan i nyckellagerfilen under aliaset <{0}>"}, + {"Do.you.still.want.to.add.it.no.", + "Vill du fortfarande l\u00E4gga till det? [nej]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "Certifikatet finns redan i den systemomsp\u00E4nnande CA-nyckellagerfilen under aliaset <{0}>"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "Vill du fortfarande l\u00E4gga till det i ditt eget nyckellagret? [nej]: "}, + {"Trust.this.certificate.no.", "Litar du p\u00E5 det h\u00E4r certifikatet? [nej]: "}, + {"YES", "JA"}, + {"New.prompt.", "Nytt {0}: "}, + {"Passwords.must.differ", "L\u00F6senorden m\u00E5ste vara olika"}, + {"Re.enter.new.prompt.", "Ange nytt {0} igen: "}, + {"Re.enter.password.", "Ange l\u00F6senord igen: "}, + {"Re.enter.new.password.", "Ange det nya l\u00F6senordet igen: "}, + {"They.don.t.match.Try.again", "De matchar inte. F\u00F6rs\u00F6k igen"}, + {"Enter.prompt.alias.name.", "Ange aliasnamn f\u00F6r {0}: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "Ange ett nytt aliasnamn\t(skriv RETURN f\u00F6r att avbryta importen av denna post): "}, + {"Enter.alias.name.", "Ange aliasnamn: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN om det \u00E4r det samma som f\u00F6r <{0}>)"}, + {".PATTERN.printX509Cert", + "\u00C4gare: {0}\nUtf\u00E4rdare: {1}\nSerienummer: {2}\nGiltigt fr\u00E5n den: {3} till: {4}\nCertifikatets fingeravtryck:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t Namn p\u00E5 signaturalgoritm: {8}\n\t Version: {9}"}, + {"What.is.your.first.and.last.name.", + "Vad heter du i f\u00F6r- och efternamn?"}, + {"What.is.the.name.of.your.organizational.unit.", + "Vad heter din avdelning inom organisationen?"}, + {"What.is.the.name.of.your.organization.", + "Vad heter din organisation?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "Vad heter din ort eller plats?"}, + {"What.is.the.name.of.your.State.or.Province.", + "Vad heter ditt land eller din provins?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "Vilken \u00E4r den tv\u00E5st\u00E4lliga landskoden?"}, + {"Is.name.correct.", "\u00C4r {0} korrekt?"}, + {"no", "nej"}, + {"yes", "ja"}, + {"y", "j"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "Aliaset <{0}> saknar nyckel"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "Aliaset <{0}> refererar till en posttyp som inte \u00E4r n\u00E5gon privat nyckelpost. Kommandot -keyclone har endast st\u00F6d f\u00F6r kloning av privata nyckelposter"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "Signerare #%d:"}, + {"Timestamp.", "Tidsst\u00E4mpel:"}, + {"Signature.", "Underskrift:"}, + {"CRLs.", "CRL:er:"}, + {"Certificate.owner.", "Certifikat\u00E4gare: "}, + {"Not.a.signed.jar.file", "Ingen signerad jar-fil"}, + {"No.certificate.from.the.SSL.server", + "Inget certifikat fr\u00E5n SSL-servern"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* Integriteten f\u00F6r den information som lagras i nyckellagerfilen *\n* har INTE verifierats! Om du vill verifiera dess integritet *\n* m\u00E5ste du ange l\u00F6senordet f\u00F6r nyckellagret. *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* Integriteten f\u00F6r den information som lagras i srckeystore*\n* har INTE verifierats! Om du vill verifiera dess integritet *\n* m\u00E5ste du ange l\u00F6senordet f\u00F6r srckeystore. *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "Certifikatsvaret inneh\u00E5ller inte n\u00E5gon offentlig nyckel f\u00F6r <{0}>"}, + {"Incomplete.certificate.chain.in.reply", + "Ofullst\u00E4ndig certifikatskedja i svaret"}, + {"Certificate.chain.in.reply.does.not.verify.", + "Certifikatskedjan i svaret g\u00E5r inte att verifiera: "}, + {"Top.level.certificate.in.reply.", + "Toppniv\u00E5certifikatet i svaret:\n"}, + {".is.not.trusted.", "... \u00E4r inte betrott. "}, + {"Install.reply.anyway.no.", "Vill du installera svaret \u00E4nd\u00E5? [nej]: "}, + {"NO", "NEJ"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "De offentliga nycklarna i svaret och nyckellagret matchar inte varandra"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "Certifikatsvaret och certifikatet i nyckellagret \u00E4r identiska"}, + {"Failed.to.establish.chain.from.reply", + "Kunde inte uppr\u00E4tta kedja fr\u00E5n svaret"}, + {"n", "n"}, + {"Wrong.answer.try.again", "Fel svar. F\u00F6rs\u00F6k p\u00E5 nytt."}, + {"Secret.key.not.generated.alias.alias.already.exists", + "Den hemliga nyckeln har inte genererats eftersom aliaset <{0}> redan finns"}, + {"Please.provide.keysize.for.secret.key.generation", + "Ange -keysize f\u00F6r att skapa hemlig nyckel"}, + + {"verified.by.s.in.s", "Verifierad av %s i %s"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "VARNING: ej verifierad. Se till att -nyckellager \u00E4r korrekt."}, + + {"Extensions.", "Till\u00E4gg: "}, + {".Empty.value.", "(Tomt v\u00E4rde)"}, + {"Extension.Request.", "Till\u00E4ggsbeg\u00E4ran:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 certifikatbeg\u00E4ran (version 1.0)\n\u00C4mne: %s\nAllm\u00E4n nyckel: %s-format %s-nyckel\n"}, + {"Unknown.keyUsage.type.", "Ok\u00E4nd keyUsage-typ: "}, + {"Unknown.extendedkeyUsage.type.", "Ok\u00E4nd extendedkeyUsage-typ: "}, + {"Unknown.AccessDescription.type.", "Ok\u00E4nd AccessDescription-typ: "}, + {"Unrecognized.GeneralName.type.", "Ok\u00E4nd GeneralName-typ: "}, + {"This.extension.cannot.be.marked.as.critical.", + "Detta till\u00E4gg kan inte markeras som kritiskt. "}, + {"Odd.number.of.hex.digits.found.", "Udda antal hex-siffror p\u00E5tr\u00E4ffades: "}, + {"Unknown.extension.type.", "Ok\u00E4nd till\u00E4ggstyp: "}, + {"command.{0}.is.ambiguous.", "kommandot {0} \u00E4r tvetydigt:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_zh_CN.java b/src/sun/security/tools/keytool/Resources_zh_CN.java new file mode 100644 index 00000000..6ae574e4 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_zh_CN.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_zh_CN extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "\u9009\u9879:"}, + {"Use.keytool.help.for.all.available.commands", + "\u4F7F\u7528 \"keytool -help\" \u83B7\u53D6\u6240\u6709\u53EF\u7528\u547D\u4EE4"}, + {"Key.and.Certificate.Management.Tool", + "\u5BC6\u94A5\u548C\u8BC1\u4E66\u7BA1\u7406\u5DE5\u5177"}, + {"Commands.", "\u547D\u4EE4:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "\u4F7F\u7528 \"keytool -command_name -help\" \u83B7\u53D6 command_name \u7684\u7528\u6CD5"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "\u751F\u6210\u8BC1\u4E66\u8BF7\u6C42"}, //-certreq + {"Changes.an.entry.s.alias", + "\u66F4\u6539\u6761\u76EE\u7684\u522B\u540D"}, //-changealias + {"Deletes.an.entry", + "\u5220\u9664\u6761\u76EE"}, //-delete + {"Exports.certificate", + "\u5BFC\u51FA\u8BC1\u4E66"}, //-exportcert + {"Generates.a.key.pair", + "\u751F\u6210\u5BC6\u94A5\u5BF9"}, //-genkeypair + {"Generates.a.secret.key", + "\u751F\u6210\u5BC6\u94A5"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "\u6839\u636E\u8BC1\u4E66\u8BF7\u6C42\u751F\u6210\u8BC1\u4E66"}, //-gencert + {"Generates.CRL", "\u751F\u6210 CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "\u5DF2\u751F\u6210{0}\u5BC6\u94A5"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "\u5DF2\u751F\u6210 {0} \u4F4D{1}\u5BC6\u94A5"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "\u4ECE JDK 1.1.x \u6837\u5F0F\u7684\u8EAB\u4EFD\u6570\u636E\u5E93\u5BFC\u5165\u6761\u76EE"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "\u5BFC\u5165\u8BC1\u4E66\u6216\u8BC1\u4E66\u94FE"}, //-importcert + {"Imports.a.password", + "\u5BFC\u5165\u53E3\u4EE4"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "\u4ECE\u5176\u4ED6\u5BC6\u94A5\u5E93\u5BFC\u5165\u4E00\u4E2A\u6216\u6240\u6709\u6761\u76EE"}, //-importkeystore + {"Clones.a.key.entry", + "\u514B\u9686\u5BC6\u94A5\u6761\u76EE"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u66F4\u6539\u6761\u76EE\u7684\u5BC6\u94A5\u53E3\u4EE4"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "\u5217\u51FA\u5BC6\u94A5\u5E93\u4E2D\u7684\u6761\u76EE"}, //-list + {"Prints.the.content.of.a.certificate", + "\u6253\u5370\u8BC1\u4E66\u5185\u5BB9"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "\u6253\u5370\u8BC1\u4E66\u8BF7\u6C42\u7684\u5185\u5BB9"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "\u6253\u5370 CRL \u6587\u4EF6\u7684\u5185\u5BB9"}, //-printcrl + {"Generates.a.self.signed.certificate", + "\u751F\u6210\u81EA\u7B7E\u540D\u8BC1\u4E66"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u66F4\u6539\u5BC6\u94A5\u5E93\u7684\u5B58\u50A8\u53E3\u4EE4"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "\u8981\u5904\u7406\u7684\u6761\u76EE\u7684\u522B\u540D"}, //-alias + {"destination.alias", + "\u76EE\u6807\u522B\u540D"}, //-destalias + {"destination.key.password", + "\u76EE\u6807\u5BC6\u94A5\u53E3\u4EE4"}, //-destkeypass + {"destination.keystore.name", + "\u76EE\u6807\u5BC6\u94A5\u5E93\u540D\u79F0"}, //-destkeystore + {"destination.keystore.password.protected", + "\u53D7\u4FDD\u62A4\u7684\u76EE\u6807\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, //-destprotected + {"destination.keystore.provider.name", + "\u76EE\u6807\u5BC6\u94A5\u5E93\u63D0\u4F9B\u65B9\u540D\u79F0"}, //-destprovidername + {"destination.keystore.password", + "\u76EE\u6807\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, //-deststorepass + {"destination.keystore.type", + "\u76EE\u6807\u5BC6\u94A5\u5E93\u7C7B\u578B"}, //-deststoretype + {"distinguished.name", + "\u552F\u4E00\u5224\u522B\u540D"}, //-dname + {"X.509.extension", + "X.509 \u6269\u5C55"}, //-ext + {"output.file.name", + "\u8F93\u51FA\u6587\u4EF6\u540D"}, //-file and -outfile + {"input.file.name", + "\u8F93\u5165\u6587\u4EF6\u540D"}, //-file and -infile + {"key.algorithm.name", + "\u5BC6\u94A5\u7B97\u6CD5\u540D\u79F0"}, //-keyalg + {"key.password", + "\u5BC6\u94A5\u53E3\u4EE4"}, //-keypass + {"key.bit.size", + "\u5BC6\u94A5\u4F4D\u5927\u5C0F"}, //-keysize + {"keystore.name", + "\u5BC6\u94A5\u5E93\u540D\u79F0"}, //-keystore + {"new.password", + "\u65B0\u53E3\u4EE4"}, //-new + {"do.not.prompt", + "\u4E0D\u63D0\u793A"}, //-noprompt + {"password.through.protected.mechanism", + "\u901A\u8FC7\u53D7\u4FDD\u62A4\u7684\u673A\u5236\u7684\u53E3\u4EE4"}, //-protected + {"provider.argument", + "\u63D0\u4F9B\u65B9\u53C2\u6570"}, //-providerarg + {"provider.class.name", + "\u63D0\u4F9B\u65B9\u7C7B\u540D"}, //-providerclass + {"provider.name", + "\u63D0\u4F9B\u65B9\u540D\u79F0"}, //-providername + {"provider.classpath", + "\u63D0\u4F9B\u65B9\u7C7B\u8DEF\u5F84"}, //-providerpath + {"output.in.RFC.style", + "\u4EE5 RFC \u6837\u5F0F\u8F93\u51FA"}, //-rfc + {"signature.algorithm.name", + "\u7B7E\u540D\u7B97\u6CD5\u540D\u79F0"}, //-sigalg + {"source.alias", + "\u6E90\u522B\u540D"}, //-srcalias + {"source.key.password", + "\u6E90\u5BC6\u94A5\u53E3\u4EE4"}, //-srckeypass + {"source.keystore.name", + "\u6E90\u5BC6\u94A5\u5E93\u540D\u79F0"}, //-srckeystore + {"source.keystore.password.protected", + "\u53D7\u4FDD\u62A4\u7684\u6E90\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, //-srcprotected + {"source.keystore.provider.name", + "\u6E90\u5BC6\u94A5\u5E93\u63D0\u4F9B\u65B9\u540D\u79F0"}, //-srcprovidername + {"source.keystore.password", + "\u6E90\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, //-srcstorepass + {"source.keystore.type", + "\u6E90\u5BC6\u94A5\u5E93\u7C7B\u578B"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL \u670D\u52A1\u5668\u4E3B\u673A\u548C\u7AEF\u53E3"}, //-sslserver + {"signed.jar.file", + "\u5DF2\u7B7E\u540D\u7684 jar \u6587\u4EF6"}, //=jarfile + {"certificate.validity.start.date.time", + "\u8BC1\u4E66\u6709\u6548\u671F\u5F00\u59CB\u65E5\u671F/\u65F6\u95F4"}, //-startdate + {"keystore.password", + "\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, //-storepass + {"keystore.type", + "\u5BC6\u94A5\u5E93\u7C7B\u578B"}, //-storetype + {"trust.certificates.from.cacerts", + "\u4FE1\u4EFB\u6765\u81EA cacerts \u7684\u8BC1\u4E66"}, //-trustcacerts + {"verbose.output", + "\u8BE6\u7EC6\u8F93\u51FA"}, //-v + {"validity.number.of.days", + "\u6709\u6548\u5929\u6570"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "\u8981\u64A4\u9500\u7684\u8BC1\u4E66\u7684\u5E8F\u5217 ID"}, //-id + // keytool: Running part + {"keytool.error.", "keytool \u9519\u8BEF: "}, + {"Illegal.option.", "\u975E\u6CD5\u9009\u9879: "}, + {"Illegal.value.", "\u975E\u6CD5\u503C: "}, + {"Unknown.password.type.", "\u672A\u77E5\u53E3\u4EE4\u7C7B\u578B: "}, + {"Cannot.find.environment.variable.", + "\u627E\u4E0D\u5230\u73AF\u5883\u53D8\u91CF: "}, + {"Cannot.find.file.", "\u627E\u4E0D\u5230\u6587\u4EF6: "}, + {"Command.option.flag.needs.an.argument.", "\u547D\u4EE4\u9009\u9879{0}\u9700\u8981\u4E00\u4E2A\u53C2\u6570\u3002"}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "\u8B66\u544A: PKCS12 \u5BC6\u94A5\u5E93\u4E0D\u652F\u6301\u5176\u4ED6\u5B58\u50A8\u548C\u5BC6\u94A5\u53E3\u4EE4\u3002\u6B63\u5728\u5FFD\u7565\u7528\u6237\u6307\u5B9A\u7684{0}\u503C\u3002"}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "\u5982\u679C -storetype \u4E3A {0}, \u5219 -keystore \u5FC5\u987B\u4E3A NONE"}, + {"Too.many.retries.program.terminated", + "\u91CD\u8BD5\u6B21\u6570\u8FC7\u591A, \u7A0B\u5E8F\u5DF2\u7EC8\u6B62"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "\u5982\u679C -storetype \u4E3A {0}, \u5219\u4E0D\u652F\u6301 -storepasswd \u548C -keypasswd \u547D\u4EE4"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "\u5982\u679C -storetype \u4E3A PKCS12, \u5219\u4E0D\u652F\u6301 -keypasswd \u547D\u4EE4"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "\u5982\u679C -storetype \u4E3A {0}, \u5219\u4E0D\u80FD\u6307\u5B9A -keypass \u548C -new"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A\u4E86 -protected, \u5219\u4E0D\u80FD\u6307\u5B9A -storepass, -keypass \u548C -new"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A\u4E86 -srcprotected, \u5219\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u5BC6\u94A5\u5E93\u672A\u53D7\u53E3\u4EE4\u4FDD\u62A4, \u5219\u4E0D\u80FD\u6307\u5B9A -storepass, -keypass \u548C -new"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u6E90\u5BC6\u94A5\u5E93\u672A\u53D7\u53E3\u4EE4\u4FDD\u62A4, \u5219\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"Illegal.startdate.value", "\u975E\u6CD5\u5F00\u59CB\u65E5\u671F\u503C"}, + {"Validity.must.be.greater.than.zero", + "\u6709\u6548\u6027\u5FC5\u987B\u5927\u4E8E\u96F6"}, + {"provName.not.a.provider", "{0}\u4E0D\u662F\u63D0\u4F9B\u65B9"}, + {"Usage.error.no.command.provided", "\u7528\u6CD5\u9519\u8BEF: \u6CA1\u6709\u63D0\u4F9B\u547D\u4EE4"}, + {"Source.keystore.file.exists.but.is.empty.", "\u6E90\u5BC6\u94A5\u5E93\u6587\u4EF6\u5B58\u5728, \u4F46\u4E3A\u7A7A: "}, + {"Please.specify.srckeystore", "\u8BF7\u6307\u5B9A -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + "\u4E0D\u80FD\u4F7F\u7528 'list' \u547D\u4EE4\u6765\u6307\u5B9A -v \u53CA -rfc"}, + {"Key.password.must.be.at.least.6.characters", + "\u5BC6\u94A5\u53E3\u4EE4\u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"New.password.must.be.at.least.6.characters", + "\u65B0\u53E3\u4EE4\u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"Keystore.file.exists.but.is.empty.", + "\u5BC6\u94A5\u5E93\u6587\u4EF6\u5B58\u5728, \u4F46\u4E3A\u7A7A: "}, + {"Keystore.file.does.not.exist.", + "\u5BC6\u94A5\u5E93\u6587\u4EF6\u4E0D\u5B58\u5728: "}, + {"Must.specify.destination.alias", "\u5FC5\u987B\u6307\u5B9A\u76EE\u6807\u522B\u540D"}, + {"Must.specify.alias", "\u5FC5\u987B\u6307\u5B9A\u522B\u540D"}, + {"Keystore.password.must.be.at.least.6.characters", + "\u5BC6\u94A5\u5E93\u53E3\u4EE4\u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"Enter.the.password.to.be.stored.", + "\u8F93\u5165\u8981\u5B58\u50A8\u7684\u53E3\u4EE4: "}, + {"Enter.keystore.password.", "\u8F93\u5165\u5BC6\u94A5\u5E93\u53E3\u4EE4: "}, + {"Enter.source.keystore.password.", "\u8F93\u5165\u6E90\u5BC6\u94A5\u5E93\u53E3\u4EE4: "}, + {"Enter.destination.keystore.password.", "\u8F93\u5165\u76EE\u6807\u5BC6\u94A5\u5E93\u53E3\u4EE4: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "\u5BC6\u94A5\u5E93\u53E3\u4EE4\u592A\u77ED - \u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"Unknown.Entry.Type", "\u672A\u77E5\u6761\u76EE\u7C7B\u578B"}, + {"Too.many.failures.Alias.not.changed", "\u6545\u969C\u592A\u591A\u3002\u672A\u66F4\u6539\u522B\u540D"}, + {"Entry.for.alias.alias.successfully.imported.", + "\u5DF2\u6210\u529F\u5BFC\u5165\u522B\u540D {0} \u7684\u6761\u76EE\u3002"}, + {"Entry.for.alias.alias.not.imported.", "\u672A\u5BFC\u5165\u522B\u540D {0} \u7684\u6761\u76EE\u3002"}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "\u5BFC\u5165\u522B\u540D {0} \u7684\u6761\u76EE\u65F6\u51FA\u73B0\u95EE\u9898: {1}\u3002\n\u672A\u5BFC\u5165\u522B\u540D {0} \u7684\u6761\u76EE\u3002"}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "\u5DF2\u5B8C\u6210\u5BFC\u5165\u547D\u4EE4: {0} \u4E2A\u6761\u76EE\u6210\u529F\u5BFC\u5165, {1} \u4E2A\u6761\u76EE\u5931\u8D25\u6216\u53D6\u6D88"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "\u8B66\u544A: \u6B63\u5728\u8986\u76D6\u76EE\u6807\u5BC6\u94A5\u5E93\u4E2D\u7684\u73B0\u6709\u522B\u540D {0}"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "\u5B58\u5728\u73B0\u6709\u6761\u76EE\u522B\u540D {0}, \u662F\u5426\u8986\u76D6? [\u5426]: "}, + {"Too.many.failures.try.later", "\u6545\u969C\u592A\u591A - \u8BF7\u7A0D\u540E\u518D\u8BD5"}, + {"Certification.request.stored.in.file.filename.", + "\u5B58\u50A8\u5728\u6587\u4EF6 <{0}> \u4E2D\u7684\u8BA4\u8BC1\u8BF7\u6C42"}, + {"Submit.this.to.your.CA", "\u5C06\u6B64\u63D0\u4EA4\u7ED9\u60A8\u7684 CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u6CA1\u6709\u6307\u5B9A\u522B\u540D, \u5219\u4E0D\u80FD\u6307\u5B9A\u76EE\u6807\u522B\u540D\u548C\u6E90\u5BC6\u94A5\u5E93\u53E3\u4EE4"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "\u76EE\u6807 pkcs12 \u5BC6\u94A5\u5E93\u5177\u6709\u4E0D\u540C\u7684 storepass \u548C keypass\u3002\u8BF7\u5728\u6307\u5B9A\u4E86 -destkeypass \u65F6\u91CD\u8BD5\u3002"}, + {"Certificate.stored.in.file.filename.", + "\u5B58\u50A8\u5728\u6587\u4EF6 <{0}> \u4E2D\u7684\u8BC1\u4E66"}, + {"Certificate.reply.was.installed.in.keystore", + "\u8BC1\u4E66\u56DE\u590D\u5DF2\u5B89\u88C5\u5728\u5BC6\u94A5\u5E93\u4E2D"}, + {"Certificate.reply.was.not.installed.in.keystore", + "\u8BC1\u4E66\u56DE\u590D\u672A\u5B89\u88C5\u5728\u5BC6\u94A5\u5E93\u4E2D"}, + {"Certificate.was.added.to.keystore", + "\u8BC1\u4E66\u5DF2\u6DFB\u52A0\u5230\u5BC6\u94A5\u5E93\u4E2D"}, + {"Certificate.was.not.added.to.keystore", + "\u8BC1\u4E66\u672A\u6DFB\u52A0\u5230\u5BC6\u94A5\u5E93\u4E2D"}, + {".Storing.ksfname.", "[\u6B63\u5728\u5B58\u50A8{0}]"}, + {"alias.has.no.public.key.certificate.", + "{0}\u6CA1\u6709\u516C\u5171\u5BC6\u94A5 (\u8BC1\u4E66)"}, + {"Cannot.derive.signature.algorithm", + "\u65E0\u6CD5\u6D3E\u751F\u7B7E\u540D\u7B97\u6CD5"}, + {"Alias.alias.does.not.exist", + "\u522B\u540D <{0}> \u4E0D\u5B58\u5728"}, + {"Alias.alias.has.no.certificate", + "\u522B\u540D <{0}> \u6CA1\u6709\u8BC1\u4E66"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "\u672A\u751F\u6210\u5BC6\u94A5\u5BF9, \u522B\u540D <{0}> \u5DF2\u7ECF\u5B58\u5728"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "\u6B63\u5728\u4E3A\u4EE5\u4E0B\u5BF9\u8C61\u751F\u6210 {0} \u4F4D{1}\u5BC6\u94A5\u5BF9\u548C\u81EA\u7B7E\u540D\u8BC1\u4E66 ({2}) (\u6709\u6548\u671F\u4E3A {3} \u5929):\n\t {4}"}, + {"Enter.key.password.for.alias.", "\u8F93\u5165 <{0}> \u7684\u5BC6\u94A5\u53E3\u4EE4"}, + {".RETURN.if.same.as.keystore.password.", + "\t(\u5982\u679C\u548C\u5BC6\u94A5\u5E93\u53E3\u4EE4\u76F8\u540C, \u6309\u56DE\u8F66): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "\u5BC6\u94A5\u53E3\u4EE4\u592A\u77ED - \u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"Too.many.failures.key.not.added.to.keystore", + "\u6545\u969C\u592A\u591A - \u5BC6\u94A5\u672A\u6DFB\u52A0\u5230\u5BC6\u94A5\u5E93\u4E2D"}, + {"Destination.alias.dest.already.exists", + "\u76EE\u6807\u522B\u540D <{0}> \u5DF2\u7ECF\u5B58\u5728"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "\u53E3\u4EE4\u592A\u77ED - \u81F3\u5C11\u5FC5\u987B\u4E3A 6 \u4E2A\u5B57\u7B26"}, + {"Too.many.failures.Key.entry.not.cloned", + "\u6545\u969C\u592A\u591A\u3002\u672A\u514B\u9686\u5BC6\u94A5\u6761\u76EE"}, + {"key.password.for.alias.", "<{0}> \u7684\u5BC6\u94A5\u53E3\u4EE4"}, + {"Keystore.entry.for.id.getName.already.exists", + "<{0}> \u7684\u5BC6\u94A5\u5E93\u6761\u76EE\u5DF2\u7ECF\u5B58\u5728"}, + {"Creating.keystore.entry.for.id.getName.", + "\u6B63\u5728\u521B\u5EFA <{0}> \u7684\u5BC6\u94A5\u5E93\u6761\u76EE..."}, + {"No.entries.from.identity.database.added", + "\u672A\u4ECE\u8EAB\u4EFD\u6570\u636E\u5E93\u4E2D\u6DFB\u52A0\u4EFB\u4F55\u6761\u76EE"}, + {"Alias.name.alias", "\u522B\u540D: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "\u521B\u5EFA\u65E5\u671F: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "\u6761\u76EE\u7C7B\u578B: {0}"}, + {"Certificate.chain.length.", "\u8BC1\u4E66\u94FE\u957F\u5EA6: "}, + {"Certificate.i.1.", "\u8BC1\u4E66[{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "\u8BC1\u4E66\u6307\u7EB9 (SHA1): "}, + {"Keystore.type.", "\u5BC6\u94A5\u5E93\u7C7B\u578B: "}, + {"Keystore.provider.", "\u5BC6\u94A5\u5E93\u63D0\u4F9B\u65B9: "}, + {"Your.keystore.contains.keyStore.size.entry", + "\u60A8\u7684\u5BC6\u94A5\u5E93\u5305\u542B {0,number,integer} \u4E2A\u6761\u76EE"}, + {"Your.keystore.contains.keyStore.size.entries", + "\u60A8\u7684\u5BC6\u94A5\u5E93\u5305\u542B {0,number,integer} \u4E2A\u6761\u76EE"}, + {"Failed.to.parse.input", "\u65E0\u6CD5\u89E3\u6790\u8F93\u5165"}, + {"Empty.input", "\u7A7A\u8F93\u5165"}, + {"Not.X.509.certificate", "\u975E X.509 \u8BC1\u4E66"}, + {"alias.has.no.public.key", "{0}\u6CA1\u6709\u516C\u5171\u5BC6\u94A5"}, + {"alias.has.no.X.509.certificate", "{0}\u6CA1\u6709 X.509 \u8BC1\u4E66"}, + {"New.certificate.self.signed.", "\u65B0\u8BC1\u4E66 (\u81EA\u7B7E\u540D):"}, + {"Reply.has.no.certificates", "\u56DE\u590D\u4E2D\u6CA1\u6709\u8BC1\u4E66"}, + {"Certificate.not.imported.alias.alias.already.exists", + "\u8BC1\u4E66\u672A\u5BFC\u5165, \u522B\u540D <{0}> \u5DF2\u7ECF\u5B58\u5728"}, + {"Input.not.an.X.509.certificate", "\u6240\u8F93\u5165\u7684\u4E0D\u662F X.509 \u8BC1\u4E66"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "\u5728\u522B\u540D <{0}> \u4E4B\u4E0B, \u8BC1\u4E66\u5DF2\u7ECF\u5B58\u5728\u4E8E\u5BC6\u94A5\u5E93\u4E2D"}, + {"Do.you.still.want.to.add.it.no.", + "\u662F\u5426\u4ECD\u8981\u6DFB\u52A0? [\u5426]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "\u5728\u522B\u540D <{0}> \u4E4B\u4E0B, \u8BC1\u4E66\u5DF2\u7ECF\u5B58\u5728\u4E8E\u7CFB\u7EDF\u8303\u56F4\u7684 CA \u5BC6\u94A5\u5E93\u4E2D"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\u662F\u5426\u4ECD\u8981\u5C06\u5B83\u6DFB\u52A0\u5230\u81EA\u5DF1\u7684\u5BC6\u94A5\u5E93? [\u5426]: "}, + {"Trust.this.certificate.no.", "\u662F\u5426\u4FE1\u4EFB\u6B64\u8BC1\u4E66? [\u5426]: "}, + {"YES", "YES"}, + {"New.prompt.", "\u65B0{0}: "}, + {"Passwords.must.differ", "\u53E3\u4EE4\u4E0D\u80FD\u76F8\u540C"}, + {"Re.enter.new.prompt.", "\u91CD\u65B0\u8F93\u5165\u65B0{0}: "}, + {"Re.enter.password.", "\u518D\u6B21\u8F93\u5165\u53E3\u4EE4: "}, + {"Re.enter.new.password.", "\u518D\u6B21\u8F93\u5165\u65B0\u53E3\u4EE4: "}, + {"They.don.t.match.Try.again", "\u5B83\u4EEC\u4E0D\u5339\u914D\u3002\u8BF7\u91CD\u8BD5"}, + {"Enter.prompt.alias.name.", "\u8F93\u5165{0}\u522B\u540D: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "\u5BFC\u5165\u65B0\u7684\u522B\u540D\t(\u6309\u56DE\u8F66\u4EE5\u53D6\u6D88\u5BF9\u6B64\u6761\u76EE\u7684\u5BFC\u5165): "}, + {"Enter.alias.name.", "\u8F93\u5165\u522B\u540D: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(\u5982\u679C\u548C <{0}> \u76F8\u540C, \u5219\u6309\u56DE\u8F66)"}, + {".PATTERN.printX509Cert", + "\u6240\u6709\u8005: {0}\n\u53D1\u5E03\u8005: {1}\n\u5E8F\u5217\u53F7: {2}\n\u6709\u6548\u671F\u5F00\u59CB\u65E5\u671F: {3}, \u622A\u6B62\u65E5\u671F: {4}\n\u8BC1\u4E66\u6307\u7EB9:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t \u7B7E\u540D\u7B97\u6CD5\u540D\u79F0: {8}\n\t \u7248\u672C: {9}"}, + {"What.is.your.first.and.last.name.", + "\u60A8\u7684\u540D\u5B57\u4E0E\u59D3\u6C0F\u662F\u4EC0\u4E48?"}, + {"What.is.the.name.of.your.organizational.unit.", + "\u60A8\u7684\u7EC4\u7EC7\u5355\u4F4D\u540D\u79F0\u662F\u4EC0\u4E48?"}, + {"What.is.the.name.of.your.organization.", + "\u60A8\u7684\u7EC4\u7EC7\u540D\u79F0\u662F\u4EC0\u4E48?"}, + {"What.is.the.name.of.your.City.or.Locality.", + "\u60A8\u6240\u5728\u7684\u57CE\u5E02\u6216\u533A\u57DF\u540D\u79F0\u662F\u4EC0\u4E48?"}, + {"What.is.the.name.of.your.State.or.Province.", + "\u60A8\u6240\u5728\u7684\u7701/\u5E02/\u81EA\u6CBB\u533A\u540D\u79F0\u662F\u4EC0\u4E48?"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\u8BE5\u5355\u4F4D\u7684\u53CC\u5B57\u6BCD\u56FD\u5BB6/\u5730\u533A\u4EE3\u7801\u662F\u4EC0\u4E48?"}, + {"Is.name.correct.", "{0}\u662F\u5426\u6B63\u786E?"}, + {"no", "\u5426"}, + {"yes", "\u662F"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "\u522B\u540D <{0}> \u6CA1\u6709\u5BC6\u94A5"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "\u522B\u540D <{0}> \u5F15\u7528\u4E86\u4E0D\u5C5E\u4E8E\u79C1\u6709\u5BC6\u94A5\u6761\u76EE\u7684\u6761\u76EE\u7C7B\u578B\u3002-keyclone \u547D\u4EE4\u4EC5\u652F\u6301\u5BF9\u79C1\u6709\u5BC6\u94A5\u6761\u76EE\u7684\u514B\u9686"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "\u7B7E\u540D\u8005 #%d:"}, + {"Timestamp.", "\u65F6\u95F4\u6233:"}, + {"Signature.", "\u7B7E\u540D:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "\u8BC1\u4E66\u6240\u6709\u8005: "}, + {"Not.a.signed.jar.file", "\u4E0D\u662F\u5DF2\u7B7E\u540D\u7684 jar \u6587\u4EF6"}, + {"No.certificate.from.the.SSL.server", + "\u6CA1\u6709\u6765\u81EA SSL \u670D\u52A1\u5668\u7684\u8BC1\u4E66"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* \u5B58\u50A8\u5728\u60A8\u7684\u5BC6\u94A5\u5E93\u4E2D\u7684\u4FE1\u606F\u7684\u5B8C\u6574\u6027 *\n* \u5C1A\u672A\u7ECF\u8FC7\u9A8C\u8BC1! \u4E3A\u4E86\u9A8C\u8BC1\u5176\u5B8C\u6574\u6027, *\n* \u5FC5\u987B\u63D0\u4F9B\u5BC6\u94A5\u5E93\u53E3\u4EE4\u3002 *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* \u5B58\u50A8\u5728 srckeystore \u4E2D\u7684\u4FE1\u606F\u7684\u5B8C\u6574\u6027*\n* \u5C1A\u672A\u7ECF\u8FC7\u9A8C\u8BC1! \u4E3A\u4E86\u9A8C\u8BC1\u5176\u5B8C\u6574\u6027, *\n* \u5FC5\u987B\u63D0\u4F9B\u6E90\u5BC6\u94A5\u5E93\u53E3\u4EE4\u3002 *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "\u8BC1\u4E66\u56DE\u590D\u4E2D\u4E0D\u5305\u542B <{0}> \u7684\u516C\u5171\u5BC6\u94A5"}, + {"Incomplete.certificate.chain.in.reply", + "\u56DE\u590D\u4E2D\u7684\u8BC1\u4E66\u94FE\u4E0D\u5B8C\u6574"}, + {"Certificate.chain.in.reply.does.not.verify.", + "\u56DE\u590D\u4E2D\u7684\u8BC1\u4E66\u94FE\u672A\u9A8C\u8BC1: "}, + {"Top.level.certificate.in.reply.", + "\u56DE\u590D\u4E2D\u7684\u9876\u7EA7\u8BC1\u4E66:\n"}, + {".is.not.trusted.", "... \u662F\u4E0D\u53EF\u4FE1\u7684\u3002"}, + {"Install.reply.anyway.no.", "\u662F\u5426\u4ECD\u8981\u5B89\u88C5\u56DE\u590D? [\u5426]: "}, + {"NO", "NO"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "\u56DE\u590D\u4E2D\u7684\u516C\u5171\u5BC6\u94A5\u4E0E\u5BC6\u94A5\u5E93\u4E0D\u5339\u914D"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "\u8BC1\u4E66\u56DE\u590D\u4E0E\u5BC6\u94A5\u5E93\u4E2D\u7684\u8BC1\u4E66\u662F\u76F8\u540C\u7684"}, + {"Failed.to.establish.chain.from.reply", + "\u65E0\u6CD5\u4ECE\u56DE\u590D\u4E2D\u5EFA\u7ACB\u94FE"}, + {"n", "n"}, + {"Wrong.answer.try.again", "\u9519\u8BEF\u7684\u7B54\u6848, \u8BF7\u518D\u8BD5\u4E00\u6B21"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "\u6CA1\u6709\u751F\u6210\u5BC6\u94A5, \u522B\u540D <{0}> \u5DF2\u7ECF\u5B58\u5728"}, + {"Please.provide.keysize.for.secret.key.generation", + "\u8BF7\u63D0\u4F9B -keysize \u4EE5\u751F\u6210\u5BC6\u94A5"}, + + {"verified.by.s.in.s", "\u7531 %s \u9A8C\u8BC1(\u5728 %s \u4E2D)"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "\u8B66\u544A: \u672A\u9A8C\u8BC1\u3002\u8BF7\u786E\u4FDD\u5BC6\u94A5\u5E93\u662F\u6B63\u786E\u7684\u3002"}, + + {"Extensions.", "\u6269\u5C55: "}, + {".Empty.value.", "(\u7A7A\u503C)"}, + {"Extension.Request.", "\u6269\u5C55\u8BF7\u6C42:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 \u8BC1\u4E66\u8BF7\u6C42 (\u7248\u672C 1.0)\n\u4E3B\u9898: %s\n\u516C\u5171\u5BC6\u94A5: %s \u683C\u5F0F %s \u5BC6\u94A5\n"}, + {"Unknown.keyUsage.type.", "\u672A\u77E5 keyUsage \u7C7B\u578B: "}, + {"Unknown.extendedkeyUsage.type.", "\u672A\u77E5 extendedkeyUsage \u7C7B\u578B: "}, + {"Unknown.AccessDescription.type.", "\u672A\u77E5 AccessDescription \u7C7B\u578B: "}, + {"Unrecognized.GeneralName.type.", "\u65E0\u6CD5\u8BC6\u522B\u7684 GeneralName \u7C7B\u578B: "}, + {"This.extension.cannot.be.marked.as.critical.", + "\u65E0\u6CD5\u5C06\u6B64\u6269\u5C55\u6807\u8BB0\u4E3A\u201C\u4E25\u91CD\u201D\u3002"}, + {"Odd.number.of.hex.digits.found.", "\u627E\u5230\u5947\u6570\u4E2A\u5341\u516D\u8FDB\u5236\u6570\u5B57: "}, + {"Unknown.extension.type.", "\u672A\u77E5\u6269\u5C55\u7C7B\u578B: "}, + {"command.{0}.is.ambiguous.", "\u547D\u4EE4{0}\u4E0D\u660E\u786E:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_zh_HK.java b/src/sun/security/tools/keytool/Resources_zh_HK.java new file mode 100644 index 00000000..90cb13f8 --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_zh_HK.java @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_zh_HK extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part +// "Option" should be translated. + {".OPTION.", " [\u9078\u9805]..."}, + {"Options.", "\u9078\u9805:"}, + {"Use.keytool.help.for.all.available.commands", + "\u4F7F\u7528 \"keytool -help\" \u53D6\u5F97\u6240\u6709\u53EF\u7528\u7684\u547D\u4EE4"}, + {"Key.and.Certificate.Management.Tool", + "\u91D1\u9470\u8207\u6191\u8B49\u7BA1\u7406\u5DE5\u5177"}, + {"Commands.", "\u547D\u4EE4:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "\u4F7F\u7528 \"keytool -command_name -help\" \u53D6\u5F97 command_name \u7684\u7528\u6CD5"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "\u7522\u751F\u6191\u8B49\u8981\u6C42"}, //-certreq + {"Changes.an.entry.s.alias", + "\u8B8A\u66F4\u9805\u76EE\u7684\u5225\u540D"}, //-changealias + {"Deletes.an.entry", + "\u522A\u9664\u9805\u76EE"}, //-delete + {"Exports.certificate", + "\u532F\u51FA\u6191\u8B49"}, //-exportcert + {"Generates.a.key.pair", + "\u7522\u751F\u91D1\u9470\u7D44"}, //-genkeypair +// translation of "secret" key should be different to "private" key. + {"Generates.a.secret.key", + "\u7522\u751F\u79D8\u5BC6\u91D1\u9470"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "\u5F9E\u6191\u8B49\u8981\u6C42\u7522\u751F\u6191\u8B49"}, //-gencert + {"Generates.CRL", "\u7522\u751F CRL"}, //-gencrl + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "\u5F9E JDK 1.1.x-style \u8B58\u5225\u8CC7\u6599\u5EAB\u532F\u5165\u9805\u76EE"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "\u532F\u5165\u6191\u8B49\u6216\u6191\u8B49\u93C8"}, //-importcert + {"Imports.one.or.all.entries.from.another.keystore", + "\u5F9E\u5176\u4ED6\u91D1\u9470\u5132\u5B58\u5EAB\u532F\u5165\u4E00\u500B\u6216\u5168\u90E8\u9805\u76EE"}, //-importkeystore + {"Clones.a.key.entry", + "\u8907\u88FD\u91D1\u9470\u9805\u76EE"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u8B8A\u66F4\u9805\u76EE\u7684\u91D1\u9470\u5BC6\u78BC"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "\u5217\u793A\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u9805\u76EE"}, //-list + {"Prints.the.content.of.a.certificate", + "\u5217\u5370\u6191\u8B49\u7684\u5167\u5BB9"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "\u5217\u5370\u6191\u8B49\u8981\u6C42\u7684\u5167\u5BB9"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "\u5217\u5370 CRL \u6A94\u6848\u7684\u5167\u5BB9"}, //-printcrl + {"Generates.a.self.signed.certificate", + "\u7522\u751F\u81EA\u884C\u7C3D\u7F72\u7684\u6191\u8B49"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u8B8A\u66F4\u91D1\u9470\u5132\u5B58\u5EAB\u7684\u5132\u5B58\u5BC6\u78BC"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "\u8981\u8655\u7406\u9805\u76EE\u7684\u5225\u540D\u540D\u7A31"}, //-alias + {"destination.alias", + "\u76EE\u7684\u5730\u5225\u540D"}, //-destalias + {"destination.key.password", + "\u76EE\u7684\u5730\u91D1\u9470\u5BC6\u78BC"}, //-destkeypass + {"destination.keystore.name", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-destkeystore + {"destination.keystore.password.protected", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u4FDD\u8B77"}, //-destprotected + {"destination.keystore.provider.name", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005\u540D\u7A31"}, //-destprovidername + {"destination.keystore.password", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-deststorepass + {"destination.keystore.type", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-deststoretype + {"distinguished.name", + "\u8FA8\u5225\u540D\u7A31"}, //-dname + {"X.509.extension", + "X.509 \u64F4\u5145\u5957\u4EF6"}, //-ext + {"output.file.name", + "\u8F38\u51FA\u6A94\u6848\u540D\u7A31"}, //-file and -outfile + {"input.file.name", + "\u8F38\u5165\u6A94\u6848\u540D\u7A31"}, //-file and -infile + {"key.algorithm.name", + "\u91D1\u9470\u6F14\u7B97\u6CD5\u540D\u7A31"}, //-keyalg + {"key.password", + "\u91D1\u9470\u5BC6\u78BC"}, //-keypass + {"key.bit.size", + "\u91D1\u9470\u4F4D\u5143\u5927\u5C0F"}, //-keysize + {"keystore.name", + "\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-keystore + {"new.password", + "\u65B0\u5BC6\u78BC"}, //-new + {"do.not.prompt", + "\u4E0D\u8981\u63D0\u793A"}, //-noprompt + {"password.through.protected.mechanism", + "\u7D93\u7531\u4FDD\u8B77\u6A5F\u5236\u7684\u5BC6\u78BC"}, //-protected + {"provider.argument", + "\u63D0\u4F9B\u8005\u5F15\u6578"}, //-providerarg + {"provider.class.name", + "\u63D0\u4F9B\u8005\u985E\u5225\u540D\u7A31"}, //-providerclass + {"provider.name", + "\u63D0\u4F9B\u8005\u540D\u7A31"}, //-providername + {"provider.classpath", + "\u63D0\u4F9B\u8005\u985E\u5225\u8DEF\u5F91"}, //-providerpath + {"output.in.RFC.style", + "\u4EE5 RFC \u6A23\u5F0F\u8F38\u51FA"}, //-rfc + {"signature.algorithm.name", + "\u7C3D\u7AE0\u6F14\u7B97\u6CD5\u540D\u7A31"}, //-sigalg + {"source.alias", + "\u4F86\u6E90\u5225\u540D"}, //-srcalias + {"source.key.password", + "\u4F86\u6E90\u91D1\u9470\u5BC6\u78BC"}, //-srckeypass + {"source.keystore.name", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-srckeystore + {"source.keystore.password.protected", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u4FDD\u8B77"}, //-srcprotected + {"source.keystore.provider.name", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005\u540D\u7A31"}, //-srcprovidername + {"source.keystore.password", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-srcstorepass + {"source.keystore.type", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL \u4F3A\u670D\u5668\u4E3B\u6A5F\u8207\u9023\u63A5\u57E0"}, //-sslserver + {"signed.jar.file", + "\u7C3D\u7F72\u7684 jar \u6A94\u6848"}, //=jarfile + {"certificate.validity.start.date.time", + "\u6191\u8B49\u6709\u6548\u6027\u958B\u59CB\u65E5\u671F/\u6642\u9593"}, //-startdate + {"keystore.password", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-storepass + {"keystore.type", + "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-storetype + {"trust.certificates.from.cacerts", + "\u4F86\u81EA cacerts \u7684\u4FE1\u4EFB\u6191\u8B49"}, //-trustcacerts + {"verbose.output", + "\u8A73\u7D30\u8CC7\u8A0A\u8F38\u51FA"}, //-v + {"validity.number.of.days", + "\u6709\u6548\u6027\u65E5\u6578"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "\u8981\u64A4\u92B7\u6191\u8B49\u7684\u5E8F\u5217 ID"}, //-id + // keytool: Running part + {"keytool.error.", "\u91D1\u9470\u5DE5\u5177\u932F\u8AA4: "}, + {"Illegal.option.", "\u7121\u6548\u7684\u9078\u9805:"}, + {"Illegal.value.", "\u7121\u6548\u503C: "}, + {"Unknown.password.type.", "\u4E0D\u660E\u7684\u5BC6\u78BC\u985E\u578B: "}, + {"Cannot.find.environment.variable.", + "\u627E\u4E0D\u5230\u74B0\u5883\u8B8A\u6578: "}, + {"Cannot.find.file.", "\u627E\u4E0D\u5230\u6A94\u6848: "}, + {"Command.option.flag.needs.an.argument.", "\u547D\u4EE4\u9078\u9805 {0} \u9700\u8981\u5F15\u6578\u3002"}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "\u8B66\u544A: PKCS12 \u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u652F\u63F4\u4E0D\u540C\u7684\u5132\u5B58\u5EAB\u548C\u91D1\u9470\u5BC6\u78BC\u3002\u5FFD\u7565\u4F7F\u7528\u8005\u6307\u5B9A\u7684 {0} \u503C\u3002"}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247 -keystore \u5FC5\u9808\u70BA NONE"}, + {"Too.many.retries.program.terminated", + "\u91CD\u8A66\u6B21\u6578\u592A\u591A\uFF0C\u7A0B\u5F0F\u5DF2\u7D42\u6B62"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247\u4E0D\u652F\u63F4 -storepasswd \u548C -keypasswd \u547D\u4EE4"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "\u5982\u679C -storetype \u70BA PKCS12\uFF0C\u5247\u4E0D\u652F\u63F4 -keypasswd \u547D\u4EE4"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -keypass \u548C -new"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A -protected\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -storepass\u3001-keypass \u548C -new"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A -srcprotected\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u53D7\u5BC6\u78BC\u4FDD\u8B77\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -storepass\u3001-keypass \u548C -new"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u53D7\u5BC6\u78BC\u4FDD\u8B77\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"Illegal.startdate.value", "\u7121\u6548\u7684 startdate \u503C"}, + {"Validity.must.be.greater.than.zero", + "\u6709\u6548\u6027\u5FC5\u9808\u5927\u65BC\u96F6"}, + {"provName.not.a.provider", "{0} \u4E0D\u662F\u4E00\u500B\u63D0\u4F9B\u8005"}, + {"Usage.error.no.command.provided", "\u7528\u6CD5\u932F\u8AA4: \u672A\u63D0\u4F9B\u547D\u4EE4"}, + {"Source.keystore.file.exists.but.is.empty.", "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u5B58\u5728\uFF0C\u4F46\u70BA\u7A7A: "}, + {"Please.specify.srckeystore", "\u8ACB\u6307\u5B9A -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + " 'list' \u547D\u4EE4\u4E0D\u80FD\u540C\u6642\u6307\u5B9A -v \u53CA -rfc"}, + {"Key.password.must.be.at.least.6.characters", + "\u91D1\u9470\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"New.password.must.be.at.least.6.characters", + "\u65B0\u7684\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Keystore.file.exists.but.is.empty.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u5B58\u5728\uFF0C\u4F46\u70BA\u7A7A\u767D: "}, + {"Keystore.file.does.not.exist.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u4E0D\u5B58\u5728: "}, + {"Must.specify.destination.alias", "\u5FC5\u9808\u6307\u5B9A\u76EE\u7684\u5730\u5225\u540D"}, + {"Must.specify.alias", "\u5FC5\u9808\u6307\u5B9A\u5225\u540D"}, + {"Keystore.password.must.be.at.least.6.characters", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Enter.keystore.password.", "\u8F38\u5165\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Enter.source.keystore.password.", "\u8ACB\u8F38\u5165\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Enter.destination.keystore.password.", "\u8ACB\u8F38\u5165\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Unknown.Entry.Type", "\u4E0D\u660E\u7684\u9805\u76EE\u985E\u578B"}, + {"Too.many.failures.Alias.not.changed", "\u592A\u591A\u932F\u8AA4\u3002\u672A\u8B8A\u66F4\u5225\u540D"}, + {"Entry.for.alias.alias.successfully.imported.", + "\u5DF2\u6210\u529F\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Entry.for.alias.alias.not.imported.", "\u672A\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u6642\u51FA\u73FE\u554F\u984C: {1}\u3002\n\u672A\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "\u5DF2\u5B8C\u6210\u532F\u5165\u547D\u4EE4: \u6210\u529F\u532F\u5165 {0} \u500B\u9805\u76EE\uFF0C{1} \u500B\u9805\u76EE\u5931\u6557\u6216\u5DF2\u53D6\u6D88"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "\u8B66\u544A: \u6B63\u5728\u8986\u5BEB\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u73FE\u6709\u5225\u540D {0}"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "\u73FE\u6709\u9805\u76EE\u5225\u540D {0} \u5B58\u5728\uFF0C\u662F\u5426\u8986\u5BEB\uFF1F[\u5426]: "}, + {"Too.many.failures.try.later", "\u592A\u591A\u932F\u8AA4 - \u8ACB\u7A0D\u5F8C\u518D\u8A66"}, + {"Certification.request.stored.in.file.filename.", + "\u8A8D\u8B49\u8981\u6C42\u5132\u5B58\u5728\u6A94\u6848 <{0}>"}, + {"Submit.this.to.your.CA", "\u5C07\u6B64\u9001\u51FA\u81F3\u60A8\u7684 CA"}, + {"if.alias.not.specified.destalias.srckeypass.and.destkeypass.must.not.be.specified", + "\u5982\u679C\u672A\u6307\u5B9A\u5225\u540D\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A destalias\u3001srckeypass \u53CA destkeypass"}, + {"Certificate.stored.in.file.filename.", + "\u6191\u8B49\u5132\u5B58\u5728\u6A94\u6848 <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "\u6191\u8B49\u56DE\u8986\u5DF2\u5B89\u88DD\u5728\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.reply.was.not.installed.in.keystore", + "\u6191\u8B49\u56DE\u8986\u672A\u5B89\u88DD\u5728\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.was.added.to.keystore", + "\u6191\u8B49\u5DF2\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.was.not.added.to.keystore", + "\u6191\u8B49\u672A\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {".Storing.ksfname.", "[\u5132\u5B58 {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} \u6C92\u6709\u516C\u958B\u91D1\u9470 (\u6191\u8B49)"}, + {"Cannot.derive.signature.algorithm", + "\u7121\u6CD5\u53D6\u5F97\u7C3D\u7AE0\u6F14\u7B97\u6CD5"}, + {"Alias.alias.does.not.exist", + "\u5225\u540D <{0}> \u4E0D\u5B58\u5728"}, + {"Alias.alias.has.no.certificate", + "\u5225\u540D <{0}> \u6C92\u6709\u6191\u8B49"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "\u6C92\u6709\u5EFA\u7ACB\u91D1\u9470\u7D44\uFF0C\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "\u91DD\u5C0D {4} \u7522\u751F\u6709\u6548\u671F {3} \u5929\u7684 {0} \u4F4D\u5143 {1} \u91D1\u9470\u7D44\u4EE5\u53CA\u81EA\u6211\u7C3D\u7F72\u6191\u8B49 ({2})\n\t"}, + {"Enter.key.password.for.alias.", "\u8F38\u5165 <{0}> \u7684\u91D1\u9470\u5BC6\u78BC"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN \u5982\u679C\u548C\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u76F8\u540C): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "\u91D1\u9470\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Too.many.failures.key.not.added.to.keystore", + "\u592A\u591A\u932F\u8AA4 - \u91D1\u9470\u672A\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB"}, + {"Destination.alias.dest.already.exists", + "\u76EE\u7684\u5730\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Too.many.failures.Key.entry.not.cloned", + "\u592A\u591A\u932F\u8AA4\u3002\u672A\u8907\u88FD\u91D1\u9470\u9805\u76EE"}, + {"key.password.for.alias.", "<{0}> \u7684\u91D1\u9470\u5BC6\u78BC"}, + {"Keystore.entry.for.id.getName.already.exists", + "<{0}> \u7684\u91D1\u9470\u5132\u5B58\u5EAB\u9805\u76EE\u5DF2\u7D93\u5B58\u5728"}, + {"Creating.keystore.entry.for.id.getName.", + "\u5EFA\u7ACB <{0}> \u7684\u91D1\u9470\u5132\u5B58\u5EAB\u9805\u76EE..."}, + {"No.entries.from.identity.database.added", + "\u6C92\u6709\u65B0\u589E\u4F86\u81EA\u8B58\u5225\u8CC7\u6599\u5EAB\u7684\u9805\u76EE"}, + {"Alias.name.alias", "\u5225\u540D\u540D\u7A31: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "\u5EFA\u7ACB\u65E5\u671F: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "\u9805\u76EE\u985E\u578B: {0}"}, + {"Certificate.chain.length.", "\u6191\u8B49\u93C8\u9577\u5EA6: "}, + {"Certificate.i.1.", "\u6191\u8B49 [{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "\u6191\u8B49\u6307\u7D0B (SHA1): "}, + {"Keystore.type.", "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B: "}, + {"Keystore.provider.", "\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005: "}, + {"Your.keystore.contains.keyStore.size.entry", + "\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5305\u542B {0,number,integer} \u9805\u76EE"}, + {"Your.keystore.contains.keyStore.size.entries", + "\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5305\u542B {0,number,integer} \u9805\u76EE"}, + {"Failed.to.parse.input", "\u7121\u6CD5\u5256\u6790\u8F38\u5165"}, + {"Empty.input", "\u7A7A\u8F38\u5165"}, + {"Not.X.509.certificate", "\u975E X.509 \u6191\u8B49"}, + {"alias.has.no.public.key", "{0} \u7121\u516C\u958B\u91D1\u9470"}, + {"alias.has.no.X.509.certificate", "{0} \u7121 X.509 \u6191\u8B49"}, + {"New.certificate.self.signed.", "\u65B0\u6191\u8B49 (\u81EA\u6211\u7C3D\u7F72): "}, + {"Reply.has.no.certificates", "\u56DE\u8986\u4E0D\u542B\u6191\u8B49"}, + {"Certificate.not.imported.alias.alias.already.exists", + "\u6191\u8B49\u672A\u8F38\u5165\uFF0C\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Input.not.an.X.509.certificate", "\u8F38\u5165\u7684\u4E0D\u662F X.509 \u6191\u8B49"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684 <{0}> \u5225\u540D\u4E4B\u4E0B\uFF0C\u6191\u8B49\u5DF2\u7D93\u5B58\u5728"}, + {"Do.you.still.want.to.add.it.no.", + "\u60A8\u4ECD\u7136\u60F3\u8981\u5C07\u4E4B\u65B0\u589E\u55CE\uFF1F [\u5426]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "\u6574\u500B\u7CFB\u7D71 CA \u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684 <{0}> \u5225\u540D\u4E4B\u4E0B\uFF0C\u6191\u8B49\u5DF2\u7D93\u5B58\u5728"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\u60A8\u4ECD\u7136\u60F3\u8981\u5C07\u4E4B\u65B0\u589E\u81F3\u81EA\u5DF1\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u55CE\uFF1F [\u5426]: "}, + {"Trust.this.certificate.no.", "\u4FE1\u4EFB\u9019\u500B\u6191\u8B49\uFF1F [\u5426]: "}, + {"YES", "\u662F"}, + {"New.prompt.", "\u65B0 {0}: "}, + {"Passwords.must.differ", "\u5FC5\u9808\u662F\u4E0D\u540C\u7684\u5BC6\u78BC"}, + {"Re.enter.new.prompt.", "\u91CD\u65B0\u8F38\u5165\u65B0 {0}: "}, + {"Re.enter.new.password.", "\u91CD\u65B0\u8F38\u5165\u65B0\u5BC6\u78BC: "}, + {"They.don.t.match.Try.again", "\u5B83\u5011\u4E0D\u76F8\u7B26\u3002\u8ACB\u91CD\u8A66"}, + {"Enter.prompt.alias.name.", "\u8F38\u5165 {0} \u5225\u540D\u540D\u7A31: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "\u8ACB\u8F38\u5165\u65B0\u7684\u5225\u540D\u540D\u7A31\t(RETURN \u4EE5\u53D6\u6D88\u532F\u5165\u6B64\u9805\u76EE):"}, + {"Enter.alias.name.", "\u8F38\u5165\u5225\u540D\u540D\u7A31: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN \u5982\u679C\u548C <{0}> \u7684\u76F8\u540C)"}, + {".PATTERN.printX509Cert", + "\u64C1\u6709\u8005: {0}\n\u767C\u51FA\u8005: {1}\n\u5E8F\u865F: {2}\n\u6709\u6548\u671F\u81EA: {3} \u5230: {4}\n\u6191\u8B49\u6307\u7D0B:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t \u7C3D\u7AE0\u6F14\u7B97\u6CD5\u540D\u7A31: {8}\n\t \u7248\u672C: {9}"}, + {"What.is.your.first.and.last.name.", + "\u60A8\u7684\u540D\u5B57\u8207\u59D3\u6C0F\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.organizational.unit.", + "\u60A8\u7684\u7D44\u7E54\u55AE\u4F4D\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.organization.", + "\u60A8\u7684\u7D44\u7E54\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.City.or.Locality.", + "\u60A8\u6240\u5728\u7684\u57CE\u5E02\u6216\u5730\u5340\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.State.or.Province.", + "\u60A8\u6240\u5728\u7684\u5DDE\u53CA\u7701\u4EFD\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\u6B64\u55AE\u4F4D\u7684\u5169\u500B\u5B57\u6BCD\u570B\u5225\u4EE3\u78BC\u70BA\u4F55\uFF1F"}, + {"Is.name.correct.", "{0} \u6B63\u78BA\u55CE\uFF1F"}, + {"no", "\u5426"}, + {"yes", "\u662F"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "\u5225\u540D <{0}> \u6C92\u6709\u91D1\u9470"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "\u5225\u540D <{0}> \u6240\u53C3\u7167\u7684\u9805\u76EE\u4E0D\u662F\u79C1\u5BC6\u91D1\u9470\u985E\u578B\u3002-keyclone \u547D\u4EE4\u50C5\u652F\u63F4\u79C1\u5BC6\u91D1\u9470\u9805\u76EE\u7684\u8907\u88FD"}, + + {".WARNING.WARNING.WARNING.", + "***************** \u8B66\u544A \u8B66\u544A \u8B66\u544A *****************"}, + {"Signer.d.", "\u7C3D\u7F72\u8005 #%d:"}, + {"Timestamp.", "\u6642\u6233:"}, + {"Signature.", "\u7C3D\u7AE0:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "\u6191\u8B49\u64C1\u6709\u8005: "}, + {"Not.a.signed.jar.file", "\u4E0D\u662F\u7C3D\u7F72\u7684 jar \u6A94\u6848"}, + {"No.certificate.from.the.SSL.server", + "\u6C92\u6709\u4F86\u81EA SSL \u4F3A\u670D\u5668\u7684\u6191\u8B49"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* \u5C1A\u672A\u9A57\u8B49\u5132\u5B58\u65BC\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u8CC7\u8A0A *\n* \u7684\u5B8C\u6574\u6027\uFF01\u82E5\u8981\u9A57\u8B49\u5176\u5B8C\u6574\u6027\uFF0C*\n* \u60A8\u5FC5\u9808\u63D0\u4F9B\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u3002 *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* \u5C1A\u672A\u9A57\u8B49\u5132\u5B58\u65BC srckeystore \u4E2D\u8CC7\u8A0A*\n* \u7684\u5B8C\u6574\u6027\uFF01\u82E5\u8981\u9A57\u8B49\u5176\u5B8C\u6574\u6027\uFF0C\u60A8\u5FC5\u9808 *\n* \u63D0\u4F9B srckeystore \u5BC6\u78BC\u3002 *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "\u6191\u8B49\u56DE\u8986\u4E26\u672A\u5305\u542B <{0}> \u7684\u516C\u958B\u91D1\u9470"}, + {"Incomplete.certificate.chain.in.reply", + "\u56DE\u8986\u6642\u7684\u6191\u8B49\u93C8\u4E0D\u5B8C\u6574"}, + {"Certificate.chain.in.reply.does.not.verify.", + "\u56DE\u8986\u6642\u7684\u6191\u8B49\u93C8\u672A\u9A57\u8B49: "}, + {"Top.level.certificate.in.reply.", + "\u56DE\u8986\u6642\u7684\u6700\u9AD8\u7D1A\u6191\u8B49:\\n"}, + {".is.not.trusted.", "... \u662F\u4E0D\u88AB\u4FE1\u4EFB\u7684\u3002"}, + {"Install.reply.anyway.no.", "\u9084\u662F\u8981\u5B89\u88DD\u56DE\u8986\uFF1F [\u5426]: "}, + {"NO", "\u5426"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "\u56DE\u8986\u6642\u7684\u516C\u958B\u91D1\u9470\u8207\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u7B26"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "\u6191\u8B49\u56DE\u8986\u8207\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u6191\u8B49\u662F\u76F8\u540C\u7684"}, + {"Failed.to.establish.chain.from.reply", + "\u7121\u6CD5\u5F9E\u56DE\u8986\u4E2D\u5C07\u93C8\u5EFA\u7ACB\u8D77\u4F86"}, + {"n", "n"}, + {"Wrong.answer.try.again", "\u932F\u8AA4\u7684\u7B54\u6848\uFF0C\u8ACB\u518D\u8A66\u4E00\u6B21"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "\u672A\u7522\u751F\u79D8\u5BC6\u91D1\u9470\uFF0C\u5225\u540D <{0}> \u5DF2\u5B58\u5728"}, + {"Please.provide.keysize.for.secret.key.generation", + "\u8ACB\u63D0\u4F9B -keysize \u4EE5\u7522\u751F\u79D8\u5BC6\u91D1\u9470"}, + + {"Extensions.", "\u64F4\u5145\u5957\u4EF6: "}, + {".Empty.value.", "(\u7A7A\u767D\u503C)"}, + {"Extension.Request.", "\u64F4\u5145\u5957\u4EF6\u8981\u6C42:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 \u6191\u8B49\u8981\u6C42 (\u7248\u672C 1.0)\n\u4E3B\u9AD4: %s\n\u516C\u7528\u91D1\u9470: %s \u683C\u5F0F %s \u91D1\u9470\n"}, + {"Unknown.keyUsage.type.", "\u4E0D\u660E\u7684 keyUsage \u985E\u578B: "}, + {"Unknown.extendedkeyUsage.type.", "\u4E0D\u660E\u7684 extendedkeyUsage \u985E\u578B: "}, + {"Unknown.AccessDescription.type.", "\u4E0D\u660E\u7684 AccessDescription \u985E\u578B: "}, + {"Unrecognized.GeneralName.type.", "\u7121\u6CD5\u8FA8\u8B58\u7684 GeneralName \u985E\u578B: "}, + {"This.extension.cannot.be.marked.as.critical.", + "\u6B64\u64F4\u5145\u5957\u4EF6\u7121\u6CD5\u6A19\u793A\u70BA\u95DC\u9375\u3002"}, + {"Odd.number.of.hex.digits.found.", "\u627E\u5230\u5341\u516D\u9032\u4F4D\u6578\u5B57\u7684\u5947\u6578: "}, + {"Unknown.extension.type.", "\u4E0D\u660E\u7684\u64F4\u5145\u5957\u4EF6\u985E\u578B: "}, + {"command.{0}.is.ambiguous.", "\u547D\u4EE4 {0} \u4E0D\u660E\u78BA:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/keytool/Resources_zh_TW.java b/src/sun/security/tools/keytool/Resources_zh_TW.java new file mode 100644 index 00000000..1f0bb68b --- /dev/null +++ b/src/sun/security/tools/keytool/Resources_zh_TW.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.keytool; + +/** + *

    This class represents the ResourceBundle + * for the keytool. + * + */ +public class Resources_zh_TW extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"STAR", + "*******************************************"}, + {"STARNN", + "*******************************************\n\n"}, + + // keytool: Help part + {".OPTION.", " [OPTION]..."}, + {"Options.", "\u9078\u9805:"}, + {"Use.keytool.help.for.all.available.commands", + "\u4F7F\u7528 \"keytool -help\" \u53D6\u5F97\u6240\u6709\u53EF\u7528\u7684\u547D\u4EE4"}, + {"Key.and.Certificate.Management.Tool", + "\u91D1\u9470\u8207\u6191\u8B49\u7BA1\u7406\u5DE5\u5177"}, + {"Commands.", "\u547D\u4EE4:"}, + {"Use.keytool.command.name.help.for.usage.of.command.name", + "\u4F7F\u7528 \"keytool -command_name -help\" \u53D6\u5F97 command_name \u7684\u7528\u6CD5"}, + // keytool: help: commands + {"Generates.a.certificate.request", + "\u7522\u751F\u6191\u8B49\u8981\u6C42"}, //-certreq + {"Changes.an.entry.s.alias", + "\u8B8A\u66F4\u9805\u76EE\u7684\u5225\u540D"}, //-changealias + {"Deletes.an.entry", + "\u522A\u9664\u9805\u76EE"}, //-delete + {"Exports.certificate", + "\u532F\u51FA\u6191\u8B49"}, //-exportcert + {"Generates.a.key.pair", + "\u7522\u751F\u91D1\u9470\u7D44"}, //-genkeypair + {"Generates.a.secret.key", + "\u7522\u751F\u79D8\u5BC6\u91D1\u9470"}, //-genseckey + {"Generates.certificate.from.a.certificate.request", + "\u5F9E\u6191\u8B49\u8981\u6C42\u7522\u751F\u6191\u8B49"}, //-gencert + {"Generates.CRL", "\u7522\u751F CRL"}, //-gencrl + {"Generated.keyAlgName.secret.key", + "\u5DF2\u7522\u751F {0} \u79D8\u5BC6\u91D1\u9470"}, //-genseckey + {"Generated.keysize.bit.keyAlgName.secret.key", + "\u5DF2\u7522\u751F {0} \u4F4D\u5143 {1} \u79D8\u5BC6\u91D1\u9470"}, //-genseckey + {"Imports.entries.from.a.JDK.1.1.x.style.identity.database", + "\u5F9E JDK 1.1.x-style \u8B58\u5225\u8CC7\u6599\u5EAB\u532F\u5165\u9805\u76EE"}, //-identitydb + {"Imports.a.certificate.or.a.certificate.chain", + "\u532F\u5165\u6191\u8B49\u6216\u6191\u8B49\u93C8"}, //-importcert + {"Imports.a.password", + "\u532F\u5165\u5BC6\u78BC"}, //-importpass + {"Imports.one.or.all.entries.from.another.keystore", + "\u5F9E\u5176\u4ED6\u91D1\u9470\u5132\u5B58\u5EAB\u532F\u5165\u4E00\u500B\u6216\u5168\u90E8\u9805\u76EE"}, //-importkeystore + {"Clones.a.key.entry", + "\u8907\u88FD\u91D1\u9470\u9805\u76EE"}, //-keyclone + {"Changes.the.key.password.of.an.entry", + "\u8B8A\u66F4\u9805\u76EE\u7684\u91D1\u9470\u5BC6\u78BC"}, //-keypasswd + {"Lists.entries.in.a.keystore", + "\u5217\u793A\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u9805\u76EE"}, //-list + {"Prints.the.content.of.a.certificate", + "\u5217\u5370\u6191\u8B49\u7684\u5167\u5BB9"}, //-printcert + {"Prints.the.content.of.a.certificate.request", + "\u5217\u5370\u6191\u8B49\u8981\u6C42\u7684\u5167\u5BB9"}, //-printcertreq + {"Prints.the.content.of.a.CRL.file", + "\u5217\u5370 CRL \u6A94\u6848\u7684\u5167\u5BB9"}, //-printcrl + {"Generates.a.self.signed.certificate", + "\u7522\u751F\u81EA\u884C\u7C3D\u7F72\u7684\u6191\u8B49"}, //-selfcert + {"Changes.the.store.password.of.a.keystore", + "\u8B8A\u66F4\u91D1\u9470\u5132\u5B58\u5EAB\u7684\u5132\u5B58\u5BC6\u78BC"}, //-storepasswd + // keytool: help: options + {"alias.name.of.the.entry.to.process", + "\u8981\u8655\u7406\u9805\u76EE\u7684\u5225\u540D\u540D\u7A31"}, //-alias + {"destination.alias", + "\u76EE\u7684\u5730\u5225\u540D"}, //-destalias + {"destination.key.password", + "\u76EE\u7684\u5730\u91D1\u9470\u5BC6\u78BC"}, //-destkeypass + {"destination.keystore.name", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-destkeystore + {"destination.keystore.password.protected", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u4FDD\u8B77"}, //-destprotected + {"destination.keystore.provider.name", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005\u540D\u7A31"}, //-destprovidername + {"destination.keystore.password", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-deststorepass + {"destination.keystore.type", + "\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-deststoretype + {"distinguished.name", + "\u8FA8\u5225\u540D\u7A31"}, //-dname + {"X.509.extension", + "X.509 \u64F4\u5145\u5957\u4EF6"}, //-ext + {"output.file.name", + "\u8F38\u51FA\u6A94\u6848\u540D\u7A31"}, //-file and -outfile + {"input.file.name", + "\u8F38\u5165\u6A94\u6848\u540D\u7A31"}, //-file and -infile + {"key.algorithm.name", + "\u91D1\u9470\u6F14\u7B97\u6CD5\u540D\u7A31"}, //-keyalg + {"key.password", + "\u91D1\u9470\u5BC6\u78BC"}, //-keypass + {"key.bit.size", + "\u91D1\u9470\u4F4D\u5143\u5927\u5C0F"}, //-keysize + {"keystore.name", + "\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-keystore + {"new.password", + "\u65B0\u5BC6\u78BC"}, //-new + {"do.not.prompt", + "\u4E0D\u8981\u63D0\u793A"}, //-noprompt + {"password.through.protected.mechanism", + "\u7D93\u7531\u4FDD\u8B77\u6A5F\u5236\u7684\u5BC6\u78BC"}, //-protected + {"provider.argument", + "\u63D0\u4F9B\u8005\u5F15\u6578"}, //-providerarg + {"provider.class.name", + "\u63D0\u4F9B\u8005\u985E\u5225\u540D\u7A31"}, //-providerclass + {"provider.name", + "\u63D0\u4F9B\u8005\u540D\u7A31"}, //-providername + {"provider.classpath", + "\u63D0\u4F9B\u8005\u985E\u5225\u8DEF\u5F91"}, //-providerpath + {"output.in.RFC.style", + "\u4EE5 RFC \u6A23\u5F0F\u8F38\u51FA"}, //-rfc + {"signature.algorithm.name", + "\u7C3D\u7AE0\u6F14\u7B97\u6CD5\u540D\u7A31"}, //-sigalg + {"source.alias", + "\u4F86\u6E90\u5225\u540D"}, //-srcalias + {"source.key.password", + "\u4F86\u6E90\u91D1\u9470\u5BC6\u78BC"}, //-srckeypass + {"source.keystore.name", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31"}, //-srckeystore + {"source.keystore.password.protected", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u4FDD\u8B77"}, //-srcprotected + {"source.keystore.provider.name", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005\u540D\u7A31"}, //-srcprovidername + {"source.keystore.password", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-srcstorepass + {"source.keystore.type", + "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-srcstoretype + {"SSL.server.host.and.port", + "SSL \u4F3A\u670D\u5668\u4E3B\u6A5F\u8207\u9023\u63A5\u57E0"}, //-sslserver + {"signed.jar.file", + "\u7C3D\u7F72\u7684 jar \u6A94\u6848"}, //=jarfile + {"certificate.validity.start.date.time", + "\u6191\u8B49\u6709\u6548\u6027\u958B\u59CB\u65E5\u671F/\u6642\u9593"}, //-startdate + {"keystore.password", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC"}, //-storepass + {"keystore.type", + "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, //-storetype + {"trust.certificates.from.cacerts", + "\u4F86\u81EA cacerts \u7684\u4FE1\u4EFB\u6191\u8B49"}, //-trustcacerts + {"verbose.output", + "\u8A73\u7D30\u8CC7\u8A0A\u8F38\u51FA"}, //-v + {"validity.number.of.days", + "\u6709\u6548\u6027\u65E5\u6578"}, //-validity + {"Serial.ID.of.cert.to.revoke", + "\u8981\u64A4\u92B7\u6191\u8B49\u7684\u5E8F\u5217 ID"}, //-id + // keytool: Running part + {"keytool.error.", "\u91D1\u9470\u5DE5\u5177\u932F\u8AA4: "}, + {"Illegal.option.", "\u7121\u6548\u7684\u9078\u9805:"}, + {"Illegal.value.", "\u7121\u6548\u503C: "}, + {"Unknown.password.type.", "\u4E0D\u660E\u7684\u5BC6\u78BC\u985E\u578B: "}, + {"Cannot.find.environment.variable.", + "\u627E\u4E0D\u5230\u74B0\u5883\u8B8A\u6578: "}, + {"Cannot.find.file.", "\u627E\u4E0D\u5230\u6A94\u6848: "}, + {"Command.option.flag.needs.an.argument.", "\u547D\u4EE4\u9078\u9805 {0} \u9700\u8981\u5F15\u6578\u3002"}, + {"Warning.Different.store.and.key.passwords.not.supported.for.PKCS12.KeyStores.Ignoring.user.specified.command.value.", + "\u8B66\u544A: PKCS12 \u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u652F\u63F4\u4E0D\u540C\u7684\u5132\u5B58\u5EAB\u548C\u91D1\u9470\u5BC6\u78BC\u3002\u5FFD\u7565\u4F7F\u7528\u8005\u6307\u5B9A\u7684 {0} \u503C\u3002"}, + {".keystore.must.be.NONE.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247 -keystore \u5FC5\u9808\u70BA NONE"}, + {"Too.many.retries.program.terminated", + "\u91CD\u8A66\u6B21\u6578\u592A\u591A\uFF0C\u7A0B\u5F0F\u5DF2\u7D42\u6B62"}, + {".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247\u4E0D\u652F\u63F4 -storepasswd \u548C -keypasswd \u547D\u4EE4"}, + {".keypasswd.commands.not.supported.if.storetype.is.PKCS12", + "\u5982\u679C -storetype \u70BA PKCS12\uFF0C\u5247\u4E0D\u652F\u63F4 -keypasswd \u547D\u4EE4"}, + {".keypass.and.new.can.not.be.specified.if.storetype.is.{0}", + "\u5982\u679C -storetype \u70BA {0}\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -keypass \u548C -new"}, + {"if.protected.is.specified.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A -protected\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -storepass\u3001-keypass \u548C -new"}, + {"if.srcprotected.is.specified.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u6307\u5B9A -srcprotected\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"if.keystore.is.not.password.protected.then.storepass.keypass.and.new.must.not.be.specified", + "\u5982\u679C\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u53D7\u5BC6\u78BC\u4FDD\u8B77\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -storepass\u3001-keypass \u548C -new"}, + {"if.source.keystore.is.not.password.protected.then.srcstorepass.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u53D7\u5BC6\u78BC\u4FDD\u8B77\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A -srcstorepass \u548C -srckeypass"}, + {"Illegal.startdate.value", "\u7121\u6548\u7684 startdate \u503C"}, + {"Validity.must.be.greater.than.zero", + "\u6709\u6548\u6027\u5FC5\u9808\u5927\u65BC\u96F6"}, + {"provName.not.a.provider", "{0} \u4E0D\u662F\u4E00\u500B\u63D0\u4F9B\u8005"}, + {"Usage.error.no.command.provided", "\u7528\u6CD5\u932F\u8AA4: \u672A\u63D0\u4F9B\u547D\u4EE4"}, + {"Source.keystore.file.exists.but.is.empty.", "\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u5B58\u5728\uFF0C\u4F46\u70BA\u7A7A: "}, + {"Please.specify.srckeystore", "\u8ACB\u6307\u5B9A -srckeystore"}, + {"Must.not.specify.both.v.and.rfc.with.list.command", + " 'list' \u547D\u4EE4\u4E0D\u80FD\u540C\u6642\u6307\u5B9A -v \u53CA -rfc"}, + {"Key.password.must.be.at.least.6.characters", + "\u91D1\u9470\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"New.password.must.be.at.least.6.characters", + "\u65B0\u7684\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Keystore.file.exists.but.is.empty.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u5B58\u5728\uFF0C\u4F46\u70BA\u7A7A\u767D: "}, + {"Keystore.file.does.not.exist.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u6A94\u6848\u4E0D\u5B58\u5728: "}, + {"Must.specify.destination.alias", "\u5FC5\u9808\u6307\u5B9A\u76EE\u7684\u5730\u5225\u540D"}, + {"Must.specify.alias", "\u5FC5\u9808\u6307\u5B9A\u5225\u540D"}, + {"Keystore.password.must.be.at.least.6.characters", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Enter.the.password.to.be.stored.", + "\u8F38\u5165\u8981\u5132\u5B58\u7684\u5BC6\u78BC: "}, + {"Enter.keystore.password.", "\u8F38\u5165\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Enter.source.keystore.password.", "\u8ACB\u8F38\u5165\u4F86\u6E90\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Enter.destination.keystore.password.", "\u8ACB\u8F38\u5165\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Keystore.password.is.too.short.must.be.at.least.6.characters", + "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Unknown.Entry.Type", "\u4E0D\u660E\u7684\u9805\u76EE\u985E\u578B"}, + {"Too.many.failures.Alias.not.changed", "\u592A\u591A\u932F\u8AA4\u3002\u672A\u8B8A\u66F4\u5225\u540D"}, + {"Entry.for.alias.alias.successfully.imported.", + "\u5DF2\u6210\u529F\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Entry.for.alias.alias.not.imported.", "\u672A\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Problem.importing.entry.for.alias.alias.exception.Entry.for.alias.alias.not.imported.", + "\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u6642\u51FA\u73FE\u554F\u984C: {1}\u3002\n\u672A\u532F\u5165\u5225\u540D {0} \u7684\u9805\u76EE\u3002"}, + {"Import.command.completed.ok.entries.successfully.imported.fail.entries.failed.or.cancelled", + "\u5DF2\u5B8C\u6210\u532F\u5165\u547D\u4EE4: \u6210\u529F\u532F\u5165 {0} \u500B\u9805\u76EE\uFF0C{1} \u500B\u9805\u76EE\u5931\u6557\u6216\u5DF2\u53D6\u6D88"}, + {"Warning.Overwriting.existing.alias.alias.in.destination.keystore", + "\u8B66\u544A: \u6B63\u5728\u8986\u5BEB\u76EE\u7684\u5730\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u73FE\u6709\u5225\u540D {0}"}, + {"Existing.entry.alias.alias.exists.overwrite.no.", + "\u73FE\u6709\u9805\u76EE\u5225\u540D {0} \u5B58\u5728\uFF0C\u662F\u5426\u8986\u5BEB\uFF1F[\u5426]: "}, + {"Too.many.failures.try.later", "\u592A\u591A\u932F\u8AA4 - \u8ACB\u7A0D\u5F8C\u518D\u8A66"}, + {"Certification.request.stored.in.file.filename.", + "\u8A8D\u8B49\u8981\u6C42\u5132\u5B58\u5728\u6A94\u6848 <{0}>"}, + {"Submit.this.to.your.CA", "\u5C07\u6B64\u9001\u51FA\u81F3\u60A8\u7684 CA"}, + {"if.alias.not.specified.destalias.and.srckeypass.must.not.be.specified", + "\u5982\u679C\u672A\u6307\u5B9A\u5225\u540D\uFF0C\u5247\u4E0D\u80FD\u6307\u5B9A destalias \u548C srckeypass"}, + {"The.destination.pkcs12.keystore.has.different.storepass.and.keypass.Please.retry.with.destkeypass.specified.", + "\u76EE\u7684\u5730 pkcs12 \u91D1\u9470\u5132\u5B58\u5EAB\u7684 storepass \u548C keypass \u4E0D\u540C\u3002\u8ACB\u91CD\u65B0\u4EE5 -destkeypass \u6307\u5B9A\u3002"}, + {"Certificate.stored.in.file.filename.", + "\u6191\u8B49\u5132\u5B58\u5728\u6A94\u6848 <{0}>"}, + {"Certificate.reply.was.installed.in.keystore", + "\u6191\u8B49\u56DE\u8986\u5DF2\u5B89\u88DD\u5728\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.reply.was.not.installed.in.keystore", + "\u6191\u8B49\u56DE\u8986\u672A\u5B89\u88DD\u5728\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.was.added.to.keystore", + "\u6191\u8B49\u5DF2\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {"Certificate.was.not.added.to.keystore", + "\u6191\u8B49\u672A\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D"}, + {".Storing.ksfname.", "[\u5132\u5B58 {0}]"}, + {"alias.has.no.public.key.certificate.", + "{0} \u6C92\u6709\u516C\u958B\u91D1\u9470 (\u6191\u8B49)"}, + {"Cannot.derive.signature.algorithm", + "\u7121\u6CD5\u53D6\u5F97\u7C3D\u7AE0\u6F14\u7B97\u6CD5"}, + {"Alias.alias.does.not.exist", + "\u5225\u540D <{0}> \u4E0D\u5B58\u5728"}, + {"Alias.alias.has.no.certificate", + "\u5225\u540D <{0}> \u6C92\u6709\u6191\u8B49"}, + {"Key.pair.not.generated.alias.alias.already.exists", + "\u6C92\u6709\u5EFA\u7ACB\u91D1\u9470\u7D44\uFF0C\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Generating.keysize.bit.keyAlgName.key.pair.and.self.signed.certificate.sigAlgName.with.a.validity.of.validality.days.for", + "\u91DD\u5C0D {4} \u7522\u751F\u6709\u6548\u671F {3} \u5929\u7684 {0} \u4F4D\u5143 {1} \u91D1\u9470\u7D44\u4EE5\u53CA\u81EA\u6211\u7C3D\u7F72\u6191\u8B49 ({2})\n\t"}, + {"Enter.key.password.for.alias.", "\u8F38\u5165 <{0}> \u7684\u91D1\u9470\u5BC6\u78BC"}, + {".RETURN.if.same.as.keystore.password.", + "\t(RETURN \u5982\u679C\u548C\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u76F8\u540C): "}, + {"Key.password.is.too.short.must.be.at.least.6.characters", + "\u91D1\u9470\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Too.many.failures.key.not.added.to.keystore", + "\u592A\u591A\u932F\u8AA4 - \u91D1\u9470\u672A\u65B0\u589E\u81F3\u91D1\u9470\u5132\u5B58\u5EAB"}, + {"Destination.alias.dest.already.exists", + "\u76EE\u7684\u5730\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Password.is.too.short.must.be.at.least.6.characters", + "\u5BC6\u78BC\u592A\u77ED - \u5FC5\u9808\u81F3\u5C11\u70BA 6 \u500B\u5B57\u5143"}, + {"Too.many.failures.Key.entry.not.cloned", + "\u592A\u591A\u932F\u8AA4\u3002\u672A\u8907\u88FD\u91D1\u9470\u9805\u76EE"}, + {"key.password.for.alias.", "<{0}> \u7684\u91D1\u9470\u5BC6\u78BC"}, + {"Keystore.entry.for.id.getName.already.exists", + "<{0}> \u7684\u91D1\u9470\u5132\u5B58\u5EAB\u9805\u76EE\u5DF2\u7D93\u5B58\u5728"}, + {"Creating.keystore.entry.for.id.getName.", + "\u5EFA\u7ACB <{0}> \u7684\u91D1\u9470\u5132\u5B58\u5EAB\u9805\u76EE..."}, + {"No.entries.from.identity.database.added", + "\u6C92\u6709\u65B0\u589E\u4F86\u81EA\u8B58\u5225\u8CC7\u6599\u5EAB\u7684\u9805\u76EE"}, + {"Alias.name.alias", "\u5225\u540D\u540D\u7A31: {0}"}, + {"Creation.date.keyStore.getCreationDate.alias.", + "\u5EFA\u7ACB\u65E5\u671F: {0,date}"}, + {"alias.keyStore.getCreationDate.alias.", + "{0}, {1,date}, "}, + {"alias.", "{0}, "}, + {"Entry.type.type.", "\u9805\u76EE\u985E\u578B: {0}"}, + {"Certificate.chain.length.", "\u6191\u8B49\u93C8\u9577\u5EA6: "}, + {"Certificate.i.1.", "\u6191\u8B49 [{0,number,integer}]:"}, + {"Certificate.fingerprint.SHA1.", "\u6191\u8B49\u6307\u7D0B (SHA1): "}, + {"Keystore.type.", "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B: "}, + {"Keystore.provider.", "\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005: "}, + {"Your.keystore.contains.keyStore.size.entry", + "\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5305\u542B {0,number,integer} \u9805\u76EE"}, + {"Your.keystore.contains.keyStore.size.entries", + "\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5305\u542B {0,number,integer} \u9805\u76EE"}, + {"Failed.to.parse.input", "\u7121\u6CD5\u5256\u6790\u8F38\u5165"}, + {"Empty.input", "\u7A7A\u8F38\u5165"}, + {"Not.X.509.certificate", "\u975E X.509 \u6191\u8B49"}, + {"alias.has.no.public.key", "{0} \u7121\u516C\u958B\u91D1\u9470"}, + {"alias.has.no.X.509.certificate", "{0} \u7121 X.509 \u6191\u8B49"}, + {"New.certificate.self.signed.", "\u65B0\u6191\u8B49 (\u81EA\u6211\u7C3D\u7F72): "}, + {"Reply.has.no.certificates", "\u56DE\u8986\u4E0D\u542B\u6191\u8B49"}, + {"Certificate.not.imported.alias.alias.already.exists", + "\u6191\u8B49\u672A\u8F38\u5165\uFF0C\u5225\u540D <{0}> \u5DF2\u7D93\u5B58\u5728"}, + {"Input.not.an.X.509.certificate", "\u8F38\u5165\u7684\u4E0D\u662F X.509 \u6191\u8B49"}, + {"Certificate.already.exists.in.keystore.under.alias.trustalias.", + "\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684 <{0}> \u5225\u540D\u4E4B\u4E0B\uFF0C\u6191\u8B49\u5DF2\u7D93\u5B58\u5728"}, + {"Do.you.still.want.to.add.it.no.", + "\u60A8\u4ECD\u7136\u60F3\u8981\u5C07\u4E4B\u65B0\u589E\u55CE\uFF1F [\u5426]: "}, + {"Certificate.already.exists.in.system.wide.CA.keystore.under.alias.trustalias.", + "\u6574\u500B\u7CFB\u7D71 CA \u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684 <{0}> \u5225\u540D\u4E4B\u4E0B\uFF0C\u6191\u8B49\u5DF2\u7D93\u5B58\u5728"}, + {"Do.you.still.want.to.add.it.to.your.own.keystore.no.", + "\u60A8\u4ECD\u7136\u60F3\u8981\u5C07\u4E4B\u65B0\u589E\u81F3\u81EA\u5DF1\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u55CE\uFF1F [\u5426]: "}, + {"Trust.this.certificate.no.", "\u4FE1\u4EFB\u9019\u500B\u6191\u8B49\uFF1F [\u5426]: "}, + {"YES", "\u662F"}, + {"New.prompt.", "\u65B0 {0}: "}, + {"Passwords.must.differ", "\u5FC5\u9808\u662F\u4E0D\u540C\u7684\u5BC6\u78BC"}, + {"Re.enter.new.prompt.", "\u91CD\u65B0\u8F38\u5165\u65B0 {0}: "}, + {"Re.enter.password.", "\u91CD\u65B0\u8F38\u5165\u5BC6\u78BC:"}, + {"Re.enter.new.password.", "\u91CD\u65B0\u8F38\u5165\u65B0\u5BC6\u78BC: "}, + {"They.don.t.match.Try.again", "\u5B83\u5011\u4E0D\u76F8\u7B26\u3002\u8ACB\u91CD\u8A66"}, + {"Enter.prompt.alias.name.", "\u8F38\u5165 {0} \u5225\u540D\u540D\u7A31: "}, + {"Enter.new.alias.name.RETURN.to.cancel.import.for.this.entry.", + "\u8ACB\u8F38\u5165\u65B0\u7684\u5225\u540D\u540D\u7A31\t(RETURN \u4EE5\u53D6\u6D88\u532F\u5165\u6B64\u9805\u76EE):"}, + {"Enter.alias.name.", "\u8F38\u5165\u5225\u540D\u540D\u7A31: "}, + {".RETURN.if.same.as.for.otherAlias.", + "\t(RETURN \u5982\u679C\u548C <{0}> \u7684\u76F8\u540C)"}, + {".PATTERN.printX509Cert", + "\u64C1\u6709\u8005: {0}\n\u767C\u51FA\u8005: {1}\n\u5E8F\u865F: {2}\n\u6709\u6548\u671F\u81EA: {3} \u5230: {4}\n\u6191\u8B49\u6307\u7D0B:\n\t MD5: {5}\n\t SHA1: {6}\n\t SHA256: {7}\n\t \u7C3D\u7AE0\u6F14\u7B97\u6CD5\u540D\u7A31: {8}\n\t \u7248\u672C: {9}"}, + {"What.is.your.first.and.last.name.", + "\u60A8\u7684\u540D\u5B57\u8207\u59D3\u6C0F\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.organizational.unit.", + "\u60A8\u7684\u7D44\u7E54\u55AE\u4F4D\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.organization.", + "\u60A8\u7684\u7D44\u7E54\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.City.or.Locality.", + "\u60A8\u6240\u5728\u7684\u57CE\u5E02\u6216\u5730\u5340\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.name.of.your.State.or.Province.", + "\u60A8\u6240\u5728\u7684\u5DDE\u53CA\u7701\u4EFD\u540D\u7A31\u70BA\u4F55\uFF1F"}, + {"What.is.the.two.letter.country.code.for.this.unit.", + "\u6B64\u55AE\u4F4D\u7684\u5169\u500B\u5B57\u6BCD\u570B\u5225\u4EE3\u78BC\u70BA\u4F55\uFF1F"}, + {"Is.name.correct.", "{0} \u6B63\u78BA\u55CE\uFF1F"}, + {"no", "\u5426"}, + {"yes", "\u662F"}, + {"y", "y"}, + {".defaultValue.", " [{0}]: "}, + {"Alias.alias.has.no.key", + "\u5225\u540D <{0}> \u6C92\u6709\u91D1\u9470"}, + {"Alias.alias.references.an.entry.type.that.is.not.a.private.key.entry.The.keyclone.command.only.supports.cloning.of.private.key", + "\u5225\u540D <{0}> \u6240\u53C3\u7167\u7684\u9805\u76EE\u4E0D\u662F\u79C1\u5BC6\u91D1\u9470\u985E\u578B\u3002-keyclone \u547D\u4EE4\u50C5\u652F\u63F4\u79C1\u5BC6\u91D1\u9470\u9805\u76EE\u7684\u8907\u88FD"}, + + {".WARNING.WARNING.WARNING.", + "***************** WARNING WARNING WARNING *****************"}, + {"Signer.d.", "\u7C3D\u7F72\u8005 #%d:"}, + {"Timestamp.", "\u6642\u6233:"}, + {"Signature.", "\u7C3D\u7AE0:"}, + {"CRLs.", "CRL:"}, + {"Certificate.owner.", "\u6191\u8B49\u64C1\u6709\u8005: "}, + {"Not.a.signed.jar.file", "\u4E0D\u662F\u7C3D\u7F72\u7684 jar \u6A94\u6848"}, + {"No.certificate.from.the.SSL.server", + "\u6C92\u6709\u4F86\u81EA SSL \u4F3A\u670D\u5668\u7684\u6191\u8B49"}, + + {".The.integrity.of.the.information.stored.in.your.keystore.", + "* \u5C1A\u672A\u9A57\u8B49\u5132\u5B58\u65BC\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u8CC7\u8A0A *\n* \u7684\u5B8C\u6574\u6027\uFF01\u82E5\u8981\u9A57\u8B49\u5176\u5B8C\u6574\u6027\uFF0C*\n* \u60A8\u5FC5\u9808\u63D0\u4F9B\u60A8\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC\u3002 *"}, + {".The.integrity.of.the.information.stored.in.the.srckeystore.", + "* \u5C1A\u672A\u9A57\u8B49\u5132\u5B58\u65BC srckeystore \u4E2D\u8CC7\u8A0A*\n* \u7684\u5B8C\u6574\u6027\uFF01\u82E5\u8981\u9A57\u8B49\u5176\u5B8C\u6574\u6027\uFF0C\u60A8\u5FC5\u9808 *\n* \u63D0\u4F9B srckeystore \u5BC6\u78BC\u3002 *"}, + + {"Certificate.reply.does.not.contain.public.key.for.alias.", + "\u6191\u8B49\u56DE\u8986\u4E26\u672A\u5305\u542B <{0}> \u7684\u516C\u958B\u91D1\u9470"}, + {"Incomplete.certificate.chain.in.reply", + "\u56DE\u8986\u6642\u7684\u6191\u8B49\u93C8\u4E0D\u5B8C\u6574"}, + {"Certificate.chain.in.reply.does.not.verify.", + "\u56DE\u8986\u6642\u7684\u6191\u8B49\u93C8\u672A\u9A57\u8B49: "}, + {"Top.level.certificate.in.reply.", + "\u56DE\u8986\u6642\u7684\u6700\u9AD8\u7D1A\u6191\u8B49:\n"}, + {".is.not.trusted.", "... \u662F\u4E0D\u88AB\u4FE1\u4EFB\u7684\u3002"}, + {"Install.reply.anyway.no.", "\u9084\u662F\u8981\u5B89\u88DD\u56DE\u8986\uFF1F [\u5426]: "}, + {"NO", "\u5426"}, + {"Public.keys.in.reply.and.keystore.don.t.match", + "\u56DE\u8986\u6642\u7684\u516C\u958B\u91D1\u9470\u8207\u91D1\u9470\u5132\u5B58\u5EAB\u4E0D\u7B26"}, + {"Certificate.reply.and.certificate.in.keystore.are.identical", + "\u6191\u8B49\u56DE\u8986\u8207\u91D1\u9470\u5132\u5B58\u5EAB\u4E2D\u7684\u6191\u8B49\u662F\u76F8\u540C\u7684"}, + {"Failed.to.establish.chain.from.reply", + "\u7121\u6CD5\u5F9E\u56DE\u8986\u4E2D\u5C07\u93C8\u5EFA\u7ACB\u8D77\u4F86"}, + {"n", "n"}, + {"Wrong.answer.try.again", "\u932F\u8AA4\u7684\u7B54\u6848\uFF0C\u8ACB\u518D\u8A66\u4E00\u6B21"}, + {"Secret.key.not.generated.alias.alias.already.exists", + "\u672A\u7522\u751F\u79D8\u5BC6\u91D1\u9470\uFF0C\u5225\u540D <{0}> \u5DF2\u5B58\u5728"}, + {"Please.provide.keysize.for.secret.key.generation", + "\u8ACB\u63D0\u4F9B -keysize \u4EE5\u7522\u751F\u79D8\u5BC6\u91D1\u9470"}, + + {"verified.by.s.in.s", "\u7531 %s \u9A57\u8B49 (\u5728 %s \u4E2D)"}, + {"warning.not.verified.make.sure.keystore.is.correct", + "\u8B66\u544A: \u672A\u9A57\u8B49\u3002\u8ACB\u78BA\u5B9A -keystore \u6B63\u78BA\u3002"}, + + {"Extensions.", "\u64F4\u5145\u5957\u4EF6: "}, + {".Empty.value.", "(\u7A7A\u767D\u503C)"}, + {"Extension.Request.", "\u64F4\u5145\u5957\u4EF6\u8981\u6C42:"}, + {"PKCS.10.Certificate.Request.Version.1.0.Subject.s.Public.Key.s.format.s.key.", + "PKCS #10 \u6191\u8B49\u8981\u6C42 (\u7248\u672C 1.0)\n\u4E3B\u9AD4: %s\n\u516C\u7528\u91D1\u9470: %s \u683C\u5F0F %s \u91D1\u9470\n"}, + {"Unknown.keyUsage.type.", "\u4E0D\u660E\u7684 keyUsage \u985E\u578B: "}, + {"Unknown.extendedkeyUsage.type.", "\u4E0D\u660E\u7684 extendedkeyUsage \u985E\u578B: "}, + {"Unknown.AccessDescription.type.", "\u4E0D\u660E\u7684 AccessDescription \u985E\u578B: "}, + {"Unrecognized.GeneralName.type.", "\u7121\u6CD5\u8FA8\u8B58\u7684 GeneralName \u985E\u578B: "}, + {"This.extension.cannot.be.marked.as.critical.", + "\u6B64\u64F4\u5145\u5957\u4EF6\u7121\u6CD5\u6A19\u793A\u70BA\u95DC\u9375\u3002"}, + {"Odd.number.of.hex.digits.found.", "\u627E\u5230\u5341\u516D\u9032\u4F4D\u6578\u5B57\u7684\u5947\u6578: "}, + {"Unknown.extension.type.", "\u4E0D\u660E\u7684\u64F4\u5145\u5957\u4EF6\u985E\u578B: "}, + {"command.{0}.is.ambiguous.", "\u547D\u4EE4 {0} \u4E0D\u660E\u78BA:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/PolicyTool.java b/src/sun/security/tools/policytool/PolicyTool.java new file mode 100644 index 00000000..2a63d454 --- /dev/null +++ b/src/sun/security/tools/policytool/PolicyTool.java @@ -0,0 +1,4518 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +import java.io.*; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Vector; +import java.util.Enumeration; +import java.net.URL; +import java.net.MalformedURLException; +import java.lang.reflect.*; +import java.text.Collator; +import java.text.MessageFormat; +import sun.security.util.PropertyExpander; +import sun.security.util.PropertyExpander.ExpandException; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FileDialog; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.*; +import sun.security.provider.*; +import sun.security.util.PolicyUtil; +import javax.security.auth.x500.X500Principal; +import javax.swing.*; +import javax.swing.border.EmptyBorder; + +/** + * PolicyTool may be used by users and administrators to configure the + * overall java security policy (currently stored in the policy file). + * Using PolicyTool administrators may add and remove policies from + * the policy file.

    + * + * @see Policy + * @since 1.2 + */ + +public class PolicyTool { + + // for i18n + static final java.util.ResourceBundle rb = + java.util.ResourceBundle.getBundle( + "sun.security.tools.policytool.Resources"); + static final Collator collator = Collator.getInstance(); + static { + // this is for case insensitive string comparisons + collator.setStrength(Collator.PRIMARY); + + // Support for Apple menu bar + if (System.getProperty("apple.laf.useScreenMenuBar") == null) { + System.setProperty("apple.laf.useScreenMenuBar", "true"); + } + System.setProperty("apple.awt.application.name", getMessage("Policy.Tool")); + + // Apply the system L&F if not specified with a system property. + if (System.getProperty("swing.defaultlaf") == null) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + // ignore + } + } + } + + // anyone can add warnings + Vector warnings; + boolean newWarning = false; + + // set to true if policy modified. + // this way upon exit we know if to ask the user to save changes + boolean modified = false; + + private static final boolean testing = false; + private static final Class[] TWOPARAMS = { String.class, String.class }; + private static final Class[] ONEPARAMS = { String.class }; + private static final Class[] NOPARAMS = {}; + /* + * All of the policy entries are read in from the + * policy file and stored here. Updates to the policy entries + * using addEntry() and removeEntry() are made here. To ultimately save + * the policy entries back to the policy file, the SavePolicy button + * must be clicked. + **/ + private static String policyFileName = null; + private Vector policyEntries = null; + private PolicyParser parser = null; + + /* The public key alias information is stored here. */ + private KeyStore keyStore = null; + private String keyStoreName = " "; + private String keyStoreType = " "; + private String keyStoreProvider = " "; + private String keyStorePwdURL = " "; + + /* standard PKCS11 KeyStore type */ + private static final String P11KEYSTORE = "PKCS11"; + + /* reserved word for PKCS11 KeyStores */ + private static final String NONE = "NONE"; + + /** + * default constructor + */ + private PolicyTool() { + policyEntries = new Vector(); + parser = new PolicyParser(); + warnings = new Vector(); + } + + /** + * get the PolicyFileName + */ + String getPolicyFileName() { + return policyFileName; + } + + /** + * set the PolicyFileName + */ + void setPolicyFileName(String policyFileName) { + PolicyTool.policyFileName = policyFileName; + } + + /** + * clear keyStore info + */ + void clearKeyStoreInfo() { + this.keyStoreName = null; + this.keyStoreType = null; + this.keyStoreProvider = null; + this.keyStorePwdURL = null; + + this.keyStore = null; + } + + /** + * get the keyStore URL name + */ + String getKeyStoreName() { + return keyStoreName; + } + + /** + * get the keyStore Type + */ + String getKeyStoreType() { + return keyStoreType; + } + + /** + * get the keyStore Provider + */ + String getKeyStoreProvider() { + return keyStoreProvider; + } + + /** + * get the keyStore password URL + */ + String getKeyStorePwdURL() { + return keyStorePwdURL; + } + + /** + * Open and read a policy file + */ + void openPolicy(String filename) throws FileNotFoundException, + PolicyParser.ParsingException, + KeyStoreException, + CertificateException, + InstantiationException, + MalformedURLException, + IOException, + NoSuchAlgorithmException, + IllegalAccessException, + NoSuchMethodException, + UnrecoverableKeyException, + NoSuchProviderException, + ClassNotFoundException, + ExpandException, + InvocationTargetException { + + newWarning = false; + + // start fresh - blow away the current state + policyEntries = new Vector(); + parser = new PolicyParser(); + warnings = new Vector(); + setPolicyFileName(null); + clearKeyStoreInfo(); + + // see if user is opening a NEW policy file + if (filename == null) { + modified = false; + return; + } + + // Read in the policy entries from the file and + // populate the parser vector table. The parser vector + // table only holds the entries as strings, so it only + // guarantees that the policies are syntactically + // correct. + setPolicyFileName(filename); + parser.read(new FileReader(filename)); + + // open the keystore + openKeyStore(parser.getKeyStoreUrl(), parser.getKeyStoreType(), + parser.getKeyStoreProvider(), parser.getStorePassURL()); + + // Update the local vector with the same policy entries. + // This guarantees that the policy entries are not only + // syntactically correct, but semantically valid as well. + Enumeration enum_ = parser.grantElements(); + while (enum_.hasMoreElements()) { + PolicyParser.GrantEntry ge = enum_.nextElement(); + + // see if all the signers have public keys + if (ge.signedBy != null) { + + String signers[] = parseSigners(ge.signedBy); + for (int i = 0; i < signers.length; i++) { + PublicKey pubKey = getPublicKeyAlias(signers[i]); + if (pubKey == null) { + newWarning = true; + MessageFormat form = new MessageFormat(getMessage + ("Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.")); + Object[] source = {signers[i]}; + warnings.addElement(form.format(source)); + } + } + } + + // check to see if the Principals are valid + ListIterator prinList = + ge.principals.listIterator(0); + while (prinList.hasNext()) { + PolicyParser.PrincipalEntry pe = prinList.next(); + try { + verifyPrincipal(pe.getPrincipalClass(), + pe.getPrincipalName()); + } catch (ClassNotFoundException fnfe) { + newWarning = true; + MessageFormat form = new MessageFormat(getMessage + ("Warning.Class.not.found.class")); + Object[] source = {pe.getPrincipalClass()}; + warnings.addElement(form.format(source)); + } + } + + // check to see if the Permissions are valid + Enumeration perms = + ge.permissionElements(); + while (perms.hasMoreElements()) { + PolicyParser.PermissionEntry pe = perms.nextElement(); + try { + verifyPermission(pe.permission, pe.name, pe.action); + } catch (ClassNotFoundException fnfe) { + newWarning = true; + MessageFormat form = new MessageFormat(getMessage + ("Warning.Class.not.found.class")); + Object[] source = {pe.permission}; + warnings.addElement(form.format(source)); + } catch (InvocationTargetException ite) { + newWarning = true; + MessageFormat form = new MessageFormat(getMessage + ("Warning.Invalid.argument.s.for.constructor.arg")); + Object[] source = {pe.permission}; + warnings.addElement(form.format(source)); + } + + // see if all the permission signers have public keys + if (pe.signedBy != null) { + + String signers[] = parseSigners(pe.signedBy); + + for (int i = 0; i < signers.length; i++) { + PublicKey pubKey = getPublicKeyAlias(signers[i]); + if (pubKey == null) { + newWarning = true; + MessageFormat form = new MessageFormat(getMessage + ("Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.")); + Object[] source = {signers[i]}; + warnings.addElement(form.format(source)); + } + } + } + } + PolicyEntry pEntry = new PolicyEntry(this, ge); + policyEntries.addElement(pEntry); + } + + // just read in the policy -- nothing has been modified yet + modified = false; + } + + + /** + * Save a policy to a file + */ + void savePolicy(String filename) + throws FileNotFoundException, IOException { + // save the policy entries to a file + parser.setKeyStoreUrl(keyStoreName); + parser.setKeyStoreType(keyStoreType); + parser.setKeyStoreProvider(keyStoreProvider); + parser.setStorePassURL(keyStorePwdURL); + parser.write(new FileWriter(filename)); + modified = false; + } + + /** + * Open the KeyStore + */ + void openKeyStore(String name, + String type, + String provider, + String pwdURL) throws KeyStoreException, + NoSuchAlgorithmException, + UnrecoverableKeyException, + IOException, + CertificateException, + NoSuchProviderException, + ExpandException { + + if (name == null && type == null && + provider == null && pwdURL == null) { + + // policy did not specify a keystore during open + // or use wants to reset keystore values + + this.keyStoreName = null; + this.keyStoreType = null; + this.keyStoreProvider = null; + this.keyStorePwdURL = null; + + // caller will set (tool.modified = true) if appropriate + + return; + } + + URL policyURL = null; + if (policyFileName != null) { + File pfile = new File(policyFileName); + policyURL = new URL("file:" + pfile.getCanonicalPath()); + } + + // although PolicyUtil.getKeyStore may properly handle + // defaults and property expansion, we do it here so that + // if the call is successful, we can set the proper values + // (PolicyUtil.getKeyStore does not return expanded values) + + if (name != null && name.length() > 0) { + name = PropertyExpander.expand(name).replace + (File.separatorChar, '/'); + } + if (type == null || type.length() == 0) { + type = KeyStore.getDefaultType(); + } + if (pwdURL != null && pwdURL.length() > 0) { + pwdURL = PropertyExpander.expand(pwdURL).replace + (File.separatorChar, '/'); + } + + try { + this.keyStore = PolicyUtil.getKeyStore(policyURL, + name, + type, + provider, + pwdURL, + null); + } catch (IOException ioe) { + + // copied from sun.security.pkcs11.SunPKCS11 + String MSG = "no password provided, and no callback handler " + + "available for retrieving password"; + + Throwable cause = ioe.getCause(); + if (cause != null && + cause instanceof javax.security.auth.login.LoginException && + MSG.equals(cause.getMessage())) { + + // throw a more friendly exception message + throw new IOException(MSG); + } else { + throw ioe; + } + } + + this.keyStoreName = name; + this.keyStoreType = type; + this.keyStoreProvider = provider; + this.keyStorePwdURL = pwdURL; + + // caller will set (tool.modified = true) + } + + /** + * Add a Grant entry to the overall policy at the specified index. + * A policy entry consists of a CodeSource. + */ + boolean addEntry(PolicyEntry pe, int index) { + + if (index < 0) { + // new entry -- just add it to the end + policyEntries.addElement(pe); + parser.add(pe.getGrantEntry()); + } else { + // existing entry -- replace old one + PolicyEntry origPe = policyEntries.elementAt(index); + parser.replace(origPe.getGrantEntry(), pe.getGrantEntry()); + policyEntries.setElementAt(pe, index); + } + return true; + } + + /** + * Add a Principal entry to an existing PolicyEntry at the specified index. + * A Principal entry consists of a class, and name. + * + * If the principal already exists, it is not added again. + */ + boolean addPrinEntry(PolicyEntry pe, + PolicyParser.PrincipalEntry newPrin, + int index) { + + // first add the principal to the Policy Parser entry + PolicyParser.GrantEntry grantEntry = pe.getGrantEntry(); + if (grantEntry.contains(newPrin) == true) + return false; + + LinkedList prinList = + grantEntry.principals; + if (index != -1) + prinList.set(index, newPrin); + else + prinList.add(newPrin); + + modified = true; + return true; + } + + /** + * Add a Permission entry to an existing PolicyEntry at the specified index. + * A Permission entry consists of a permission, name, and actions. + * + * If the permission already exists, it is not added again. + */ + boolean addPermEntry(PolicyEntry pe, + PolicyParser.PermissionEntry newPerm, + int index) { + + // first add the permission to the Policy Parser Vector + PolicyParser.GrantEntry grantEntry = pe.getGrantEntry(); + if (grantEntry.contains(newPerm) == true) + return false; + + Vector permList = + grantEntry.permissionEntries; + if (index != -1) + permList.setElementAt(newPerm, index); + else + permList.addElement(newPerm); + + modified = true; + return true; + } + + /** + * Remove a Permission entry from an existing PolicyEntry. + */ + boolean removePermEntry(PolicyEntry pe, + PolicyParser.PermissionEntry perm) { + + // remove the Permission from the GrantEntry + PolicyParser.GrantEntry ppge = pe.getGrantEntry(); + modified = ppge.remove(perm); + return modified; + } + + /** + * remove an entry from the overall policy + */ + boolean removeEntry(PolicyEntry pe) { + + parser.remove(pe.getGrantEntry()); + modified = true; + return (policyEntries.removeElement(pe)); + } + + /** + * retrieve all Policy Entries + */ + PolicyEntry[] getEntry() { + + if (policyEntries.size() > 0) { + PolicyEntry entries[] = new PolicyEntry[policyEntries.size()]; + for (int i = 0; i < policyEntries.size(); i++) + entries[i] = policyEntries.elementAt(i); + return entries; + } + return null; + } + + /** + * Retrieve the public key mapped to a particular name. + * If the key has expired, a KeyException is thrown. + */ + PublicKey getPublicKeyAlias(String name) throws KeyStoreException { + if (keyStore == null) { + return null; + } + + Certificate cert = keyStore.getCertificate(name); + if (cert == null) { + return null; + } + PublicKey pubKey = cert.getPublicKey(); + return pubKey; + } + + /** + * Retrieve all the alias names stored in the certificate database + */ + String[] getPublicKeyAlias() throws KeyStoreException { + + int numAliases = 0; + String aliases[] = null; + + if (keyStore == null) { + return null; + } + Enumeration enum_ = keyStore.aliases(); + + // first count the number of elements + while (enum_.hasMoreElements()) { + enum_.nextElement(); + numAliases++; + } + + if (numAliases > 0) { + // now copy them into an array + aliases = new String[numAliases]; + numAliases = 0; + enum_ = keyStore.aliases(); + while (enum_.hasMoreElements()) { + aliases[numAliases] = new String(enum_.nextElement()); + numAliases++; + } + } + return aliases; + } + + /** + * This method parses a single string of signers separated by commas + * ("jordan, duke, pippen") into an array of individual strings. + */ + String[] parseSigners(String signedBy) { + + String signers[] = null; + int numSigners = 1; + int signedByIndex = 0; + int commaIndex = 0; + int signerNum = 0; + + // first pass thru "signedBy" counts the number of signers + while (commaIndex >= 0) { + commaIndex = signedBy.indexOf(',', signedByIndex); + if (commaIndex >= 0) { + numSigners++; + signedByIndex = commaIndex + 1; + } + } + signers = new String[numSigners]; + + // second pass thru "signedBy" transfers signers to array + commaIndex = 0; + signedByIndex = 0; + while (commaIndex >= 0) { + if ((commaIndex = signedBy.indexOf(',', signedByIndex)) >= 0) { + // transfer signer and ignore trailing part of the string + signers[signerNum] = + signedBy.substring(signedByIndex, commaIndex).trim(); + signerNum++; + signedByIndex = commaIndex + 1; + } else { + // we are at the end of the string -- transfer signer + signers[signerNum] = signedBy.substring(signedByIndex).trim(); + } + } + return signers; + } + + /** + * Check to see if the Principal contents are OK + */ + void verifyPrincipal(String type, String name) + throws ClassNotFoundException, + InstantiationException + { + if (type.equals(PolicyParser.PrincipalEntry.WILDCARD_CLASS) || + type.equals(PolicyParser.PrincipalEntry.REPLACE_NAME)) { + return; + } + Class PRIN = Class.forName("java.security.Principal"); + Class pc = Class.forName(type, true, + Thread.currentThread().getContextClassLoader()); + if (!PRIN.isAssignableFrom(pc)) { + MessageFormat form = new MessageFormat(getMessage + ("Illegal.Principal.Type.type")); + Object[] source = {type}; + throw new InstantiationException(form.format(source)); + } + + if (ToolDialog.X500_PRIN_CLASS.equals(pc.getName())) { + // PolicyParser checks validity of X500Principal name + // - PolicyTool needs to as well so that it doesn't store + // an invalid name that can't be read in later + // + // this can throw an IllegalArgumentException + X500Principal newP = new X500Principal(name); + } + } + + /** + * Check to see if the Permission contents are OK + */ + @SuppressWarnings("fallthrough") + void verifyPermission(String type, + String name, + String actions) + throws ClassNotFoundException, + InstantiationException, + IllegalAccessException, + NoSuchMethodException, + InvocationTargetException + { + + //XXX we might want to keep a hash of created factories... + Class pc = Class.forName(type, true, + Thread.currentThread().getContextClassLoader()); + Constructor c = null; + Vector objects = new Vector<>(2); + if (name != null) objects.add(name); + if (actions != null) objects.add(actions); + switch (objects.size()) { + case 0: + try { + c = pc.getConstructor(NOPARAMS); + break; + } catch (NoSuchMethodException ex) { + // proceed to the one-param constructor + objects.add(null); + } + /* fall through */ + case 1: + try { + c = pc.getConstructor(ONEPARAMS); + break; + } catch (NoSuchMethodException ex) { + // proceed to the two-param constructor + objects.add(null); + } + /* fall through */ + case 2: + c = pc.getConstructor(TWOPARAMS); + break; + } + Object parameters[] = objects.toArray(); + Permission p = (Permission)c.newInstance(parameters); + } + + /* + * Parse command line arguments. + */ + static void parseArgs(String args[]) { + /* parse flags */ + int n = 0; + + for (n=0; (n < args.length) && args[n].startsWith("-"); n++) { + + String flags = args[n]; + + if (collator.compare(flags, "-file") == 0) { + if (++n == args.length) usage(); + policyFileName = args[n]; + } else { + MessageFormat form = new MessageFormat(getMessage + ("Illegal.option.option")); + Object[] source = { flags }; + System.err.println(form.format(source)); + usage(); + } + } + } + + static void usage() { + System.out.println(getMessage("Usage.policytool.options.")); + System.out.println(); + System.out.println(getMessage + (".file.file.policy.file.location")); + System.out.println(); + + System.exit(1); + } + + /** + * run the PolicyTool + */ + public static void main(String args[]) { + parseArgs(args); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ToolWindow tw = new ToolWindow(new PolicyTool()); + tw.displayToolWindow(args); + } + }); + } + + // split instr to words according to capitalization, + // like, AWTControl -> A W T Control + // this method is for easy pronounciation + static String splitToWords(String instr) { + return instr.replaceAll("([A-Z])", " $1"); + } + + /** + * Returns the message corresponding to the key in the bundle. + * This is preferred over {@link #getString} because it removes + * any mnemonic '&' character in the string. + * + * @param key the key + * + * @return the message + */ + static String getMessage(String key) { + return removeMnemonicAmpersand(rb.getString(key)); + } + + + /** + * Returns the mnemonic for a message. + * + * @param key the key + * + * @return the mnemonic int + */ + static int getMnemonicInt(String key) { + String message = rb.getString(key); + return (findMnemonicInt(message)); + } + + /** + * Returns the mnemonic display index for a message. + * + * @param key the key + * + * @return the mnemonic display index + */ + static int getDisplayedMnemonicIndex(String key) { + String message = rb.getString(key); + return (findMnemonicIndex(message)); + } + + /** + * Finds the mnemonic character in a message. + * + * The mnemonic character is the first character followed by the first + * & that is not followed by another &. + * + * @return the mnemonic as an int, or 0 if it + * can't be found. + */ + private static int findMnemonicInt(String s) { + for (int i = 0; i < s.length() - 1; i++) { + if (s.charAt(i) == '&') { + if (s.charAt(i + 1) != '&') { + return KeyEvent.getExtendedKeyCodeForChar(s.charAt(i + 1)); + } else { + i++; + } + } + } + return 0; + } + + /** + * Finds the index of the mnemonic character in a message. + * + * The mnemonic character is the first character followed by the first + * & that is not followed by another &. + * + * @return the mnemonic character index as an int, or -1 if it + * can't be found. + */ + private static int findMnemonicIndex(String s) { + for (int i = 0; i < s.length() - 1; i++) { + if (s.charAt(i) == '&') { + if (s.charAt(i + 1) != '&') { + // Return the index of the '&' since it will be removed + return i; + } else { + i++; + } + } + } + return -1; + } + + /** + * Removes the mnemonic identifier (&) from a string unless + * it's escaped by && or placed at the end. + * + * @param message the message + * + * @return a message with the mnemonic identifier removed + */ + private static String removeMnemonicAmpersand(String message) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < message.length(); i++) { + char current = message.charAt(i); + if (current != '&' || i == message.length() - 1 + || message.charAt(i + 1) == '&') { + s.append(current); + } + } + return s.toString(); + } +} + +/** + * Each entry in the policy configuration file is represented by a + * PolicyEntry object. + * + * A PolicyEntry is a (CodeSource,Permission) pair. The + * CodeSource contains the (URL, PublicKey) that together identify + * where the Java bytecodes come from and who (if anyone) signed + * them. The URL could refer to localhost. The URL could also be + * null, meaning that this policy entry is given to all comers, as + * long as they match the signer field. The signer could be null, + * meaning the code is not signed. + * + * The Permission contains the (Type, Name, Action) triplet. + * + */ +class PolicyEntry { + + private CodeSource codesource; + private PolicyTool tool; + private PolicyParser.GrantEntry grantEntry; + private boolean testing = false; + + /** + * Create a PolicyEntry object from the information read in + * from a policy file. + */ + PolicyEntry(PolicyTool tool, PolicyParser.GrantEntry ge) + throws MalformedURLException, NoSuchMethodException, + ClassNotFoundException, InstantiationException, IllegalAccessException, + InvocationTargetException, CertificateException, + IOException, NoSuchAlgorithmException, UnrecoverableKeyException { + + this.tool = tool; + + URL location = null; + + // construct the CodeSource + if (ge.codeBase != null) + location = new URL(ge.codeBase); + this.codesource = new CodeSource(location, + (Certificate[]) null); + + if (testing) { + System.out.println("Adding Policy Entry:"); + System.out.println(" CodeBase = " + location); + System.out.println(" Signers = " + ge.signedBy); + System.out.println(" with " + ge.principals.size() + + " Principals"); + } + + this.grantEntry = ge; + } + + /** + * get the codesource associated with this PolicyEntry + */ + CodeSource getCodeSource() { + return codesource; + } + + /** + * get the GrantEntry associated with this PolicyEntry + */ + PolicyParser.GrantEntry getGrantEntry() { + return grantEntry; + } + + /** + * convert the header portion, i.e. codebase, signer, principals, of + * this policy entry into a string + */ + String headerToString() { + String pString = principalsToString(); + if (pString.length() == 0) { + return codebaseToString(); + } else { + return codebaseToString() + ", " + pString; + } + } + + /** + * convert the Codebase/signer portion of this policy entry into a string + */ + String codebaseToString() { + + String stringEntry = new String(); + + if (grantEntry.codeBase != null && + grantEntry.codeBase.equals("") == false) + stringEntry = stringEntry.concat + ("CodeBase \"" + + grantEntry.codeBase + + "\""); + + if (grantEntry.signedBy != null && + grantEntry.signedBy.equals("") == false) + stringEntry = ((stringEntry.length() > 0) ? + stringEntry.concat(", SignedBy \"" + + grantEntry.signedBy + + "\"") : + stringEntry.concat("SignedBy \"" + + grantEntry.signedBy + + "\"")); + + if (stringEntry.length() == 0) + return new String("CodeBase "); + return stringEntry; + } + + /** + * convert the Principals portion of this policy entry into a string + */ + String principalsToString() { + String result = ""; + if ((grantEntry.principals != null) && + (!grantEntry.principals.isEmpty())) { + StringBuffer buffer = new StringBuffer(200); + ListIterator list = + grantEntry.principals.listIterator(); + while (list.hasNext()) { + PolicyParser.PrincipalEntry pppe = list.next(); + buffer.append(" Principal " + pppe.getDisplayClass() + " " + + pppe.getDisplayName(true)); + if (list.hasNext()) buffer.append(", "); + } + result = buffer.toString(); + } + return result; + } + + /** + * convert this policy entry into a PolicyParser.PermissionEntry + */ + PolicyParser.PermissionEntry toPermissionEntry(Permission perm) { + + String actions = null; + + // get the actions + if (perm.getActions() != null && + perm.getActions().trim() != "") + actions = perm.getActions(); + + PolicyParser.PermissionEntry pe = new PolicyParser.PermissionEntry + (perm.getClass().getName(), + perm.getName(), + actions); + return pe; + } +} + +/** + * The main window for the PolicyTool + */ +class ToolWindow extends JFrame { + // use serialVersionUID from JDK 1.2.2 for interoperability + private static final long serialVersionUID = 5682568601210376777L; + + /* ESCAPE key */ + static final KeyStroke escKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + /* external paddings */ + public static final Insets TOP_PADDING = new Insets(25,0,0,0); + public static final Insets BOTTOM_PADDING = new Insets(0,0,25,0); + public static final Insets LITE_BOTTOM_PADDING = new Insets(0,0,10,0); + public static final Insets LR_PADDING = new Insets(0,10,0,10); + public static final Insets TOP_BOTTOM_PADDING = new Insets(15, 0, 15, 0); + public static final Insets L_TOP_BOTTOM_PADDING = new Insets(5,10,15,0); + public static final Insets LR_TOP_BOTTOM_PADDING = new Insets(15, 4, 15, 4); + public static final Insets LR_BOTTOM_PADDING = new Insets(0,10,5,10); + public static final Insets L_BOTTOM_PADDING = new Insets(0,10,5,0); + public static final Insets R_BOTTOM_PADDING = new Insets(0, 0, 25, 5); + public static final Insets R_PADDING = new Insets(0, 0, 0, 5); + + /* buttons and menus */ + public static final String NEW_POLICY_FILE = "New"; + public static final String OPEN_POLICY_FILE = "Open"; + public static final String SAVE_POLICY_FILE = "Save"; + public static final String SAVE_AS_POLICY_FILE = "Save.As"; + public static final String VIEW_WARNINGS = "View.Warning.Log"; + public static final String QUIT = "Exit"; + public static final String ADD_POLICY_ENTRY = "Add.Policy.Entry"; + public static final String EDIT_POLICY_ENTRY = "Edit.Policy.Entry"; + public static final String REMOVE_POLICY_ENTRY = "Remove.Policy.Entry"; + public static final String EDIT_KEYSTORE = "Edit"; + public static final String ADD_PUBKEY_ALIAS = "Add.Public.Key.Alias"; + public static final String REMOVE_PUBKEY_ALIAS = "Remove.Public.Key.Alias"; + + /* gridbag index for components in the main window (MW) */ + public static final int MW_FILENAME_LABEL = 0; + public static final int MW_FILENAME_TEXTFIELD = 1; + public static final int MW_PANEL = 2; + public static final int MW_ADD_BUTTON = 0; + public static final int MW_EDIT_BUTTON = 1; + public static final int MW_REMOVE_BUTTON = 2; + public static final int MW_POLICY_LIST = 3; // follows MW_PANEL + + /* The preferred height of JTextField should match JComboBox. */ + static final int TEXTFIELD_HEIGHT = new JComboBox().getPreferredSize().height; + + private PolicyTool tool; + + /** + * Constructor + */ + ToolWindow(PolicyTool tool) { + this.tool = tool; + } + + /** + * Don't call getComponent directly on the window + */ + public Component getComponent(int n) { + Component c = getContentPane().getComponent(n); + if (c instanceof JScrollPane) { + c = ((JScrollPane)c).getViewport().getView(); + } + return c; + } + + /** + * Initialize the PolicyTool window with the necessary components + */ + private void initWindow() { + // The ToolWindowListener will handle closing the window. + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + // create the top menu bar + JMenuBar menuBar = new JMenuBar(); + + // create a File menu + JMenu menu = new JMenu(); + configureButton(menu, "File"); + ActionListener actionListener = new FileMenuListener(tool, this); + addMenuItem(menu, NEW_POLICY_FILE, actionListener, "N"); + addMenuItem(menu, OPEN_POLICY_FILE, actionListener, "O"); + addMenuItem(menu, SAVE_POLICY_FILE, actionListener, "S"); + addMenuItem(menu, SAVE_AS_POLICY_FILE, actionListener, null); + addMenuItem(menu, VIEW_WARNINGS, actionListener, null); + addMenuItem(menu, QUIT, actionListener, null); + menuBar.add(menu); + + // create a KeyStore menu + menu = new JMenu(); + configureButton(menu, "KeyStore"); + actionListener = new MainWindowListener(tool, this); + addMenuItem(menu, EDIT_KEYSTORE, actionListener, null); + menuBar.add(menu); + setJMenuBar(menuBar); + + // Create some space around components + ((JPanel)getContentPane()).setBorder(new EmptyBorder(6, 6, 6, 6)); + + // policy entry listing + JLabel label = new JLabel(PolicyTool.getMessage("Policy.File.")); + addNewComponent(this, label, MW_FILENAME_LABEL, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + LR_TOP_BOTTOM_PADDING); + JTextField tf = new JTextField(50); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Policy.File.")); + tf.setEditable(false); + addNewComponent(this, tf, MW_FILENAME_TEXTFIELD, + 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + LR_TOP_BOTTOM_PADDING); + + + // add ADD/REMOVE/EDIT buttons in a new panel + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + JButton button = new JButton(); + configureButton(button, ADD_POLICY_ENTRY); + button.addActionListener(new MainWindowListener(tool, this)); + addNewComponent(panel, button, MW_ADD_BUTTON, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + LR_PADDING); + + button = new JButton(); + configureButton(button, EDIT_POLICY_ENTRY); + button.addActionListener(new MainWindowListener(tool, this)); + addNewComponent(panel, button, MW_EDIT_BUTTON, + 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + LR_PADDING); + + button = new JButton(); + configureButton(button, REMOVE_POLICY_ENTRY); + button.addActionListener(new MainWindowListener(tool, this)); + addNewComponent(panel, button, MW_REMOVE_BUTTON, + 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + LR_PADDING); + + addNewComponent(this, panel, MW_PANEL, + 0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.BOTH, + BOTTOM_PADDING); + + + String policyFile = tool.getPolicyFileName(); + if (policyFile == null) { + String userHome; + userHome = AccessController.doPrivileged( + new sun.security.action.GetPropertyAction("user.home")); + policyFile = userHome + File.separatorChar + ".java.policy"; + } + + try { + // open the policy file + tool.openPolicy(policyFile); + + // display the policy entries via the policy list textarea + DefaultListModel listModel = new DefaultListModel(); + JList list = new JList(listModel); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, this)); + PolicyEntry entries[] = tool.getEntry(); + if (entries != null) { + for (int i = 0; i < entries.length; i++) { + listModel.addElement(entries[i].headerToString()); + } + } + JTextField newFilename = (JTextField) + getComponent(MW_FILENAME_TEXTFIELD); + newFilename.setText(policyFile); + initPolicyList(list); + + } catch (FileNotFoundException fnfe) { + // add blank policy listing + JList list = new JList(new DefaultListModel()); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, this)); + initPolicyList(list); + tool.setPolicyFileName(null); + tool.modified = false; + + // just add warning + tool.warnings.addElement(fnfe.toString()); + + } catch (Exception e) { + // add blank policy listing + JList list = new JList(new DefaultListModel()); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, this)); + initPolicyList(list); + tool.setPolicyFileName(null); + tool.modified = false; + + // display the error + MessageFormat form = new MessageFormat(PolicyTool.getMessage + ("Could.not.open.policy.file.policyFile.e.toString.")); + Object[] source = {policyFile, e.toString()}; + displayErrorDialog(null, form.format(source)); + } + } + + + // Platform specific modifier (control / command). + private int shortCutModifier = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + private void addMenuItem(JMenu menu, String key, ActionListener actionListener, String accelerator) { + JMenuItem menuItem = new JMenuItem(); + configureButton(menuItem, key); + + if (PolicyTool.rb.containsKey(key + ".accelerator")) { + // Accelerator from resources takes precedence + accelerator = PolicyTool.getMessage(key + ".accelerator"); + } + + if (accelerator != null && !accelerator.isEmpty()) { + KeyStroke keyStroke; + if (accelerator.length() == 1) { + keyStroke = KeyStroke.getKeyStroke(KeyEvent.getExtendedKeyCodeForChar(accelerator.charAt(0)), + shortCutModifier); + } else { + keyStroke = KeyStroke.getKeyStroke(accelerator); + } + menuItem.setAccelerator(keyStroke); + } + + menuItem.addActionListener(actionListener); + menu.add(menuItem); + } + + static void configureButton(AbstractButton button, String key) { + button.setText(PolicyTool.getMessage(key)); + button.setActionCommand(key); + + int mnemonicInt = PolicyTool.getMnemonicInt(key); + if (mnemonicInt > 0) { + button.setMnemonic(mnemonicInt); + button.setDisplayedMnemonicIndex(PolicyTool.getDisplayedMnemonicIndex(key)); + } + } + + static void configureLabelFor(JLabel label, JComponent component, String key) { + label.setText(PolicyTool.getMessage(key)); + label.setLabelFor(component); + + int mnemonicInt = PolicyTool.getMnemonicInt(key); + if (mnemonicInt > 0) { + label.setDisplayedMnemonic(mnemonicInt); + label.setDisplayedMnemonicIndex(PolicyTool.getDisplayedMnemonicIndex(key)); + } + } + + + /** + * Add a component to the PolicyTool window + */ + void addNewComponent(Container container, JComponent component, + int index, int gridx, int gridy, int gridwidth, int gridheight, + double weightx, double weighty, int fill, Insets is) { + + if (container instanceof JFrame) { + container = ((JFrame)container).getContentPane(); + } else if (container instanceof JDialog) { + container = ((JDialog)container).getContentPane(); + } + + // add the component at the specified gridbag index + container.add(component, index); + + // set the constraints + GridBagLayout gbl = (GridBagLayout)container.getLayout(); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = gridx; + gbc.gridy = gridy; + gbc.gridwidth = gridwidth; + gbc.gridheight = gridheight; + gbc.weightx = weightx; + gbc.weighty = weighty; + gbc.fill = fill; + if (is != null) gbc.insets = is; + gbl.setConstraints(component, gbc); + } + + + /** + * Add a component to the PolicyTool window without external padding + */ + void addNewComponent(Container container, JComponent component, + int index, int gridx, int gridy, int gridwidth, int gridheight, + double weightx, double weighty, int fill) { + + // delegate with "null" external padding + addNewComponent(container, component, index, gridx, gridy, + gridwidth, gridheight, weightx, weighty, + fill, null); + } + + + /** + * Init the policy_entry_list TEXTAREA component in the + * PolicyTool window + */ + void initPolicyList(JList policyList) { + + // add the policy list to the window + //policyList.setPreferredSize(new Dimension(500, 350)); + JScrollPane scrollPane = new JScrollPane(policyList); + addNewComponent(this, scrollPane, MW_POLICY_LIST, + 0, 3, 2, 1, 1.0, 1.0, GridBagConstraints.BOTH); + } + + /** + * Replace the policy_entry_list TEXTAREA component in the + * PolicyTool window with an updated one. + */ + void replacePolicyList(JList policyList) { + + // remove the original list of Policy Entries + // and add the new list of entries + JList list = (JList)getComponent(MW_POLICY_LIST); + list.setModel(policyList.getModel()); + } + + /** + * display the main PolicyTool window + */ + void displayToolWindow(String args[]) { + + setTitle(PolicyTool.getMessage("Policy.Tool")); + setResizable(true); + addWindowListener(new ToolWindowListener(tool, this)); + //setBounds(135, 80, 500, 500); + getContentPane().setLayout(new GridBagLayout()); + + initWindow(); + pack(); + setLocationRelativeTo(null); + + // display it + setVisible(true); + + if (tool.newWarning == true) { + displayStatusDialog(this, PolicyTool.getMessage + ("Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.")); + } + } + + /** + * displays a dialog box describing an error which occurred. + */ + void displayErrorDialog(Window w, String error) { + ToolDialog ed = new ToolDialog + (PolicyTool.getMessage("Error"), tool, this, true); + + // find where the PolicyTool gui is + Point location = ((w == null) ? + getLocationOnScreen() : w.getLocationOnScreen()); + //ed.setBounds(location.x + 50, location.y + 50, 600, 100); + ed.setLayout(new GridBagLayout()); + + JLabel label = new JLabel(error); + addNewComponent(ed, label, 0, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH); + + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + ActionListener okListener = new ErrorOKButtonListener(ed); + okButton.addActionListener(okListener); + addNewComponent(ed, okButton, 1, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + ed.getRootPane().setDefaultButton(okButton); + ed.getRootPane().registerKeyboardAction(okListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + ed.pack(); + ed.setLocationRelativeTo(w); + ed.setVisible(true); + } + + /** + * displays a dialog box describing an error which occurred. + */ + void displayErrorDialog(Window w, Throwable t) { + if (t instanceof NoDisplayException) { + return; + } + displayErrorDialog(w, t.toString()); + } + + /** + * displays a dialog box describing the status of an event + */ + void displayStatusDialog(Window w, String status) { + ToolDialog sd = new ToolDialog + (PolicyTool.getMessage("Status"), tool, this, true); + + // find the location of the PolicyTool gui + Point location = ((w == null) ? + getLocationOnScreen() : w.getLocationOnScreen()); + //sd.setBounds(location.x + 50, location.y + 50, 500, 100); + sd.setLayout(new GridBagLayout()); + + JLabel label = new JLabel(status); + addNewComponent(sd, label, 0, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH); + + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + ActionListener okListener = new StatusOKButtonListener(sd); + okButton.addActionListener(okListener); + addNewComponent(sd, okButton, 1, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + sd.getRootPane().setDefaultButton(okButton); + sd.getRootPane().registerKeyboardAction(okListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + sd.pack(); + sd.setLocationRelativeTo(w); + sd.setVisible(true); + } + + /** + * display the warning log + */ + void displayWarningLog(Window w) { + + ToolDialog wd = new ToolDialog + (PolicyTool.getMessage("Warning"), tool, this, true); + + // find the location of the PolicyTool gui + Point location = ((w == null) ? + getLocationOnScreen() : w.getLocationOnScreen()); + //wd.setBounds(location.x + 50, location.y + 50, 500, 100); + wd.setLayout(new GridBagLayout()); + + JTextArea ta = new JTextArea(); + ta.setEditable(false); + for (int i = 0; i < tool.warnings.size(); i++) { + ta.append(tool.warnings.elementAt(i)); + ta.append(PolicyTool.getMessage("NEWLINE")); + } + addNewComponent(wd, ta, 0, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + BOTTOM_PADDING); + ta.setFocusable(false); + + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + ActionListener okListener = new CancelButtonListener(wd); + okButton.addActionListener(okListener); + addNewComponent(wd, okButton, 1, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + LR_PADDING); + + wd.getRootPane().setDefaultButton(okButton); + wd.getRootPane().registerKeyboardAction(okListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + wd.pack(); + wd.setLocationRelativeTo(w); + wd.setVisible(true); + } + + char displayYesNoDialog(Window w, String title, String prompt, String yes, String no) { + + final ToolDialog tw = new ToolDialog + (title, tool, this, true); + Point location = ((w == null) ? + getLocationOnScreen() : w.getLocationOnScreen()); + //tw.setBounds(location.x + 75, location.y + 100, 400, 150); + tw.setLayout(new GridBagLayout()); + + JTextArea ta = new JTextArea(prompt, 10, 50); + ta.setEditable(false); + ta.setLineWrap(true); + ta.setWrapStyleWord(true); + JScrollPane scrollPane = new JScrollPane(ta, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + addNewComponent(tw, scrollPane, 0, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH); + ta.setFocusable(false); + + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + // StringBuffer to store button press. Must be final. + final StringBuffer chooseResult = new StringBuffer(); + + JButton button = new JButton(yes); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + chooseResult.append('Y'); + tw.setVisible(false); + tw.dispose(); + } + }); + addNewComponent(panel, button, 0, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + LR_PADDING); + + button = new JButton(no); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + chooseResult.append('N'); + tw.setVisible(false); + tw.dispose(); + } + }); + addNewComponent(panel, button, 1, + 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + LR_PADDING); + + addNewComponent(tw, panel, 1, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + tw.pack(); + tw.setLocationRelativeTo(w); + tw.setVisible(true); + if (chooseResult.length() > 0) { + return chooseResult.charAt(0); + } else { + // I did encounter this once, don't why. + return 'N'; + } + } + +} + +/** + * General dialog window + */ +class ToolDialog extends JDialog { + // use serialVersionUID from JDK 1.2.2 for interoperability + private static final long serialVersionUID = -372244357011301190L; + + /* ESCAPE key */ + static final KeyStroke escKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + + /* necessary constants */ + public static final int NOACTION = 0; + public static final int QUIT = 1; + public static final int NEW = 2; + public static final int OPEN = 3; + + public static final String ALL_PERM_CLASS = + "java.security.AllPermission"; + public static final String FILE_PERM_CLASS = + "java.io.FilePermission"; + + public static final String X500_PRIN_CLASS = + "javax.security.auth.x500.X500Principal"; + + /* popup menus */ + public static final String PERM = + PolicyTool.getMessage + ("Permission."); + + public static final String PRIN_TYPE = + PolicyTool.getMessage("Principal.Type."); + public static final String PRIN_NAME = + PolicyTool.getMessage("Principal.Name."); + + /* more popu menus */ + public static final String PERM_NAME = + PolicyTool.getMessage + ("Target.Name."); + + /* and more popup menus */ + public static final String PERM_ACTIONS = + PolicyTool.getMessage + ("Actions."); + + /* gridbag index for display PolicyEntry (PE) components */ + public static final int PE_CODEBASE_LABEL = 0; + public static final int PE_CODEBASE_TEXTFIELD = 1; + public static final int PE_SIGNEDBY_LABEL = 2; + public static final int PE_SIGNEDBY_TEXTFIELD = 3; + + public static final int PE_PANEL0 = 4; + public static final int PE_ADD_PRIN_BUTTON = 0; + public static final int PE_EDIT_PRIN_BUTTON = 1; + public static final int PE_REMOVE_PRIN_BUTTON = 2; + + public static final int PE_PRIN_LABEL = 5; + public static final int PE_PRIN_LIST = 6; + + public static final int PE_PANEL1 = 7; + public static final int PE_ADD_PERM_BUTTON = 0; + public static final int PE_EDIT_PERM_BUTTON = 1; + public static final int PE_REMOVE_PERM_BUTTON = 2; + + public static final int PE_PERM_LIST = 8; + + public static final int PE_PANEL2 = 9; + public static final int PE_CANCEL_BUTTON = 1; + public static final int PE_DONE_BUTTON = 0; + + /* the gridbag index for components in the Principal Dialog (PRD) */ + public static final int PRD_DESC_LABEL = 0; + public static final int PRD_PRIN_CHOICE = 1; + public static final int PRD_PRIN_TEXTFIELD = 2; + public static final int PRD_NAME_LABEL = 3; + public static final int PRD_NAME_TEXTFIELD = 4; + public static final int PRD_CANCEL_BUTTON = 6; + public static final int PRD_OK_BUTTON = 5; + + /* the gridbag index for components in the Permission Dialog (PD) */ + public static final int PD_DESC_LABEL = 0; + public static final int PD_PERM_CHOICE = 1; + public static final int PD_PERM_TEXTFIELD = 2; + public static final int PD_NAME_CHOICE = 3; + public static final int PD_NAME_TEXTFIELD = 4; + public static final int PD_ACTIONS_CHOICE = 5; + public static final int PD_ACTIONS_TEXTFIELD = 6; + public static final int PD_SIGNEDBY_LABEL = 7; + public static final int PD_SIGNEDBY_TEXTFIELD = 8; + public static final int PD_CANCEL_BUTTON = 10; + public static final int PD_OK_BUTTON = 9; + + /* modes for KeyStore */ + public static final int EDIT_KEYSTORE = 0; + + /* the gridbag index for components in the Change KeyStore Dialog (KSD) */ + public static final int KSD_NAME_LABEL = 0; + public static final int KSD_NAME_TEXTFIELD = 1; + public static final int KSD_TYPE_LABEL = 2; + public static final int KSD_TYPE_TEXTFIELD = 3; + public static final int KSD_PROVIDER_LABEL = 4; + public static final int KSD_PROVIDER_TEXTFIELD = 5; + public static final int KSD_PWD_URL_LABEL = 6; + public static final int KSD_PWD_URL_TEXTFIELD = 7; + public static final int KSD_CANCEL_BUTTON = 9; + public static final int KSD_OK_BUTTON = 8; + + /* the gridbag index for components in the User Save Changes Dialog (USC) */ + public static final int USC_LABEL = 0; + public static final int USC_PANEL = 1; + public static final int USC_YES_BUTTON = 0; + public static final int USC_NO_BUTTON = 1; + public static final int USC_CANCEL_BUTTON = 2; + + /* gridbag index for the ConfirmRemovePolicyEntryDialog (CRPE) */ + public static final int CRPE_LABEL1 = 0; + public static final int CRPE_LABEL2 = 1; + public static final int CRPE_PANEL = 2; + public static final int CRPE_PANEL_OK = 0; + public static final int CRPE_PANEL_CANCEL = 1; + + /* some private static finals */ + private static final int PERMISSION = 0; + private static final int PERMISSION_NAME = 1; + private static final int PERMISSION_ACTIONS = 2; + private static final int PERMISSION_SIGNEDBY = 3; + private static final int PRINCIPAL_TYPE = 4; + private static final int PRINCIPAL_NAME = 5; + + /* The preferred height of JTextField should match JComboBox. */ + static final int TEXTFIELD_HEIGHT = new JComboBox().getPreferredSize().height; + + public static java.util.ArrayList PERM_ARRAY; + public static java.util.ArrayList PRIN_ARRAY; + PolicyTool tool; + ToolWindow tw; + + static { + + // set up permission objects + + PERM_ARRAY = new java.util.ArrayList(); + PERM_ARRAY.add(new AllPerm()); + PERM_ARRAY.add(new AudioPerm()); + PERM_ARRAY.add(new AuthPerm()); + PERM_ARRAY.add(new AWTPerm()); + PERM_ARRAY.add(new DelegationPerm()); + PERM_ARRAY.add(new FilePerm()); + PERM_ARRAY.add(new URLPerm()); + PERM_ARRAY.add(new InqSecContextPerm()); + PERM_ARRAY.add(new LogPerm()); + PERM_ARRAY.add(new MgmtPerm()); + PERM_ARRAY.add(new MBeanPerm()); + PERM_ARRAY.add(new MBeanSvrPerm()); + PERM_ARRAY.add(new MBeanTrustPerm()); + PERM_ARRAY.add(new NetPerm()); + PERM_ARRAY.add(new PrivCredPerm()); + PERM_ARRAY.add(new PropPerm()); + PERM_ARRAY.add(new ReflectPerm()); + PERM_ARRAY.add(new RuntimePerm()); + PERM_ARRAY.add(new SecurityPerm()); + PERM_ARRAY.add(new SerialPerm()); + PERM_ARRAY.add(new ServicePerm()); + PERM_ARRAY.add(new SocketPerm()); + PERM_ARRAY.add(new SQLPerm()); + PERM_ARRAY.add(new SSLPerm()); + PERM_ARRAY.add(new SubjDelegPerm()); + + // set up principal objects + + PRIN_ARRAY = new java.util.ArrayList(); + PRIN_ARRAY.add(new KrbPrin()); + PRIN_ARRAY.add(new X500Prin()); + } + + ToolDialog(String title, PolicyTool tool, ToolWindow tw, boolean modal) { + super(tw, modal); + setTitle(title); + this.tool = tool; + this.tw = tw; + addWindowListener(new ChildWindowListener(this)); + + // Create some space around components + ((JPanel)getContentPane()).setBorder(new EmptyBorder(6, 6, 6, 6)); + } + + /** + * Don't call getComponent directly on the window + */ + public Component getComponent(int n) { + Component c = getContentPane().getComponent(n); + if (c instanceof JScrollPane) { + c = ((JScrollPane)c).getViewport().getView(); + } + return c; + } + + /** + * get the Perm instance based on either the (shortened) class name + * or the fully qualified class name + */ + static Perm getPerm(String clazz, boolean fullClassName) { + for (int i = 0; i < PERM_ARRAY.size(); i++) { + Perm next = PERM_ARRAY.get(i); + if (fullClassName) { + if (next.FULL_CLASS.equals(clazz)) { + return next; + } + } else { + if (next.CLASS.equals(clazz)) { + return next; + } + } + } + return null; + } + + /** + * get the Prin instance based on either the (shortened) class name + * or the fully qualified class name + */ + static Prin getPrin(String clazz, boolean fullClassName) { + for (int i = 0; i < PRIN_ARRAY.size(); i++) { + Prin next = PRIN_ARRAY.get(i); + if (fullClassName) { + if (next.FULL_CLASS.equals(clazz)) { + return next; + } + } else { + if (next.CLASS.equals(clazz)) { + return next; + } + } + } + return null; + } + + /** + * pop up a dialog so the user can enter info to add a new PolicyEntry + * - if edit is TRUE, then the user is editing an existing entry + * and we should display the original info as well. + * + * - the other reason we need the 'edit' boolean is we need to know + * when we are adding a NEW policy entry. in this case, we can + * not simply update the existing entry, because it doesn't exist. + * we ONLY update the GUI listing/info, and then when the user + * finally clicks 'OK' or 'DONE', then we can collect that info + * and add it to the policy. + */ + void displayPolicyEntryDialog(boolean edit) { + + int listIndex = 0; + PolicyEntry entries[] = null; + TaggedList prinList = new TaggedList(3, false); + prinList.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Principal.List")); + prinList.addMouseListener + (new EditPrinButtonListener(tool, tw, this, edit)); + TaggedList permList = new TaggedList(10, false); + permList.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Permission.List")); + permList.addMouseListener + (new EditPermButtonListener(tool, tw, this, edit)); + + // find where the PolicyTool gui is + Point location = tw.getLocationOnScreen(); + //setBounds(location.x + 75, location.y + 200, 650, 500); + setLayout(new GridBagLayout()); + setResizable(true); + + if (edit) { + // get the selected item + entries = tool.getEntry(); + JList policyList = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + listIndex = policyList.getSelectedIndex(); + + // get principal list + LinkedList principals = + entries[listIndex].getGrantEntry().principals; + for (int i = 0; i < principals.size(); i++) { + String prinString = null; + PolicyParser.PrincipalEntry nextPrin = principals.get(i); + prinList.addTaggedItem(PrincipalEntryToUserFriendlyString(nextPrin), nextPrin); + } + + // get permission list + Vector permissions = + entries[listIndex].getGrantEntry().permissionEntries; + for (int i = 0; i < permissions.size(); i++) { + String permString = null; + PolicyParser.PermissionEntry nextPerm = + permissions.elementAt(i); + permList.addTaggedItem(ToolDialog.PermissionEntryToUserFriendlyString(nextPerm), nextPerm); + } + } + + // codebase label and textfield + JLabel label = new JLabel(); + tw.addNewComponent(this, label, PE_CODEBASE_LABEL, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_PADDING); + JTextField tf; + tf = (edit ? + new JTextField(entries[listIndex].getGrantEntry().codeBase) : + new JTextField()); + ToolWindow.configureLabelFor(label, tf, "CodeBase."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Code.Base")); + tw.addNewComponent(this, tf, PE_CODEBASE_TEXTFIELD, + 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH); + + // signedby label and textfield + label = new JLabel(); + tw.addNewComponent(this, label, PE_SIGNEDBY_LABEL, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_PADDING); + tf = (edit ? + new JTextField(entries[listIndex].getGrantEntry().signedBy) : + new JTextField()); + ToolWindow.configureLabelFor(label, tf, "SignedBy."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Signed.By.")); + tw.addNewComponent(this, tf, PE_SIGNEDBY_TEXTFIELD, + 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH); + + // panel for principal buttons + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + JButton button = new JButton(); + ToolWindow.configureButton(button, "Add.Principal"); + button.addActionListener + (new AddPrinButtonListener(tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_ADD_PRIN_BUTTON, + 0, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + button = new JButton(); + ToolWindow.configureButton(button, "Edit.Principal"); + button.addActionListener(new EditPrinButtonListener + (tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_EDIT_PRIN_BUTTON, + 1, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + button = new JButton(); + ToolWindow.configureButton(button, "Remove.Principal"); + button.addActionListener(new RemovePrinButtonListener + (tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_REMOVE_PRIN_BUTTON, + 2, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + tw.addNewComponent(this, panel, PE_PANEL0, + 1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.HORIZONTAL, + ToolWindow.LITE_BOTTOM_PADDING); + + // principal label and list + label = new JLabel(); + tw.addNewComponent(this, label, PE_PRIN_LABEL, + 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_BOTTOM_PADDING); + JScrollPane scrollPane = new JScrollPane(prinList); + ToolWindow.configureLabelFor(label, scrollPane, "Principals."); + tw.addNewComponent(this, scrollPane, PE_PRIN_LIST, + 1, 3, 3, 1, 0.0, prinList.getVisibleRowCount(), GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // panel for permission buttons + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + button = new JButton(); + ToolWindow.configureButton(button, ".Add.Permission"); + button.addActionListener(new AddPermButtonListener + (tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_ADD_PERM_BUTTON, + 0, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + button = new JButton(); + ToolWindow.configureButton(button, ".Edit.Permission"); + button.addActionListener(new EditPermButtonListener + (tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_EDIT_PERM_BUTTON, + 1, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + + button = new JButton(); + ToolWindow.configureButton(button, "Remove.Permission"); + button.addActionListener(new RemovePermButtonListener + (tool, tw, this, edit)); + tw.addNewComponent(panel, button, PE_REMOVE_PERM_BUTTON, + 2, 0, 1, 1, 100.0, 0.0, GridBagConstraints.HORIZONTAL); + + tw.addNewComponent(this, panel, PE_PANEL1, + 0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.HORIZONTAL, + ToolWindow.LITE_BOTTOM_PADDING); + + // permission list + scrollPane = new JScrollPane(permList); + tw.addNewComponent(this, scrollPane, PE_PERM_LIST, + 0, 5, 3, 1, 0.0, permList.getVisibleRowCount(), GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + + // panel for Done and Cancel buttons + panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + // Done Button + JButton okButton = new JButton(PolicyTool.getMessage("Done")); + okButton.addActionListener + (new AddEntryDoneButtonListener(tool, tw, this, edit)); + tw.addNewComponent(panel, okButton, PE_DONE_BUTTON, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.LR_PADDING); + + // Cancel Button + JButton cancelButton = new JButton(PolicyTool.getMessage("Cancel")); + ActionListener cancelListener = new CancelButtonListener(this); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(panel, cancelButton, PE_CANCEL_BUTTON, + 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.LR_PADDING); + + // add the panel + tw.addNewComponent(this, panel, PE_PANEL2, + 0, 6, 2, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + getRootPane().setDefaultButton(okButton); + getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + pack(); + setLocationRelativeTo(tw); + setVisible(true); + } + + /** + * Read all the Policy information data in the dialog box + * and construct a PolicyEntry object with it. + */ + PolicyEntry getPolicyEntryFromDialog() + throws InvalidParameterException, MalformedURLException, + NoSuchMethodException, ClassNotFoundException, InstantiationException, + IllegalAccessException, InvocationTargetException, + CertificateException, IOException, Exception { + + // get the Codebase + JTextField tf = (JTextField)getComponent(PE_CODEBASE_TEXTFIELD); + String codebase = null; + if (tf.getText().trim().equals("") == false) + codebase = new String(tf.getText().trim()); + + // get the SignedBy + tf = (JTextField)getComponent(PE_SIGNEDBY_TEXTFIELD); + String signedby = null; + if (tf.getText().trim().equals("") == false) + signedby = new String(tf.getText().trim()); + + // construct a new GrantEntry + PolicyParser.GrantEntry ge = + new PolicyParser.GrantEntry(signedby, codebase); + + // get the new Principals + LinkedList prins = new LinkedList<>(); + TaggedList prinList = (TaggedList)getComponent(PE_PRIN_LIST); + for (int i = 0; i < prinList.getModel().getSize(); i++) { + prins.add((PolicyParser.PrincipalEntry)prinList.getObject(i)); + } + ge.principals = prins; + + // get the new Permissions + Vector perms = new Vector<>(); + TaggedList permList = (TaggedList)getComponent(PE_PERM_LIST); + for (int i = 0; i < permList.getModel().getSize(); i++) { + perms.addElement((PolicyParser.PermissionEntry)permList.getObject(i)); + } + ge.permissionEntries = perms; + + // construct a new PolicyEntry object + PolicyEntry entry = new PolicyEntry(tool, ge); + + return entry; + } + + /** + * display a dialog box for the user to enter KeyStore information + */ + void keyStoreDialog(int mode) { + + // find where the PolicyTool gui is + Point location = tw.getLocationOnScreen(); + //setBounds(location.x + 25, location.y + 100, 500, 300); + setLayout(new GridBagLayout()); + + if (mode == EDIT_KEYSTORE) { + + // KeyStore label and textfield + JLabel label = new JLabel(); + tw.addNewComponent(this, label, KSD_NAME_LABEL, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_BOTTOM_PADDING); + JTextField tf = new JTextField(tool.getKeyStoreName(), 30); + ToolWindow.configureLabelFor(label, tf, "KeyStore.URL."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + + // URL to U R L, so that accessibility reader will pronounce well + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("KeyStore.U.R.L.")); + tw.addNewComponent(this, tf, KSD_NAME_TEXTFIELD, + 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // KeyStore type and textfield + label = new JLabel(); + tw.addNewComponent(this, label, KSD_TYPE_LABEL, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_BOTTOM_PADDING); + tf = new JTextField(tool.getKeyStoreType(), 30); + ToolWindow.configureLabelFor(label, tf, "KeyStore.Type."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("KeyStore.Type.")); + tw.addNewComponent(this, tf, KSD_TYPE_TEXTFIELD, + 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // KeyStore provider and textfield + label = new JLabel(); + tw.addNewComponent(this, label, KSD_PROVIDER_LABEL, + 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_BOTTOM_PADDING); + tf = new JTextField(tool.getKeyStoreProvider(), 30); + ToolWindow.configureLabelFor(label, tf, "KeyStore.Provider."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("KeyStore.Provider.")); + tw.addNewComponent(this, tf, KSD_PROVIDER_TEXTFIELD, + 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // KeyStore password URL and textfield + label = new JLabel(); + tw.addNewComponent(this, label, KSD_PWD_URL_LABEL, + 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.R_BOTTOM_PADDING); + tf = new JTextField(tool.getKeyStorePwdURL(), 30); + ToolWindow.configureLabelFor(label, tf, "KeyStore.Password.URL."); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("KeyStore.Password.U.R.L.")); + tw.addNewComponent(this, tf, KSD_PWD_URL_TEXTFIELD, + 1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // OK button + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + okButton.addActionListener + (new ChangeKeyStoreOKButtonListener(tool, tw, this)); + tw.addNewComponent(this, okButton, KSD_OK_BUTTON, + 0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + // cancel button + JButton cancelButton = new JButton(PolicyTool.getMessage("Cancel")); + ActionListener cancelListener = new CancelButtonListener(this); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(this, cancelButton, KSD_CANCEL_BUTTON, + 1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL); + + getRootPane().setDefaultButton(okButton); + getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + } + + pack(); + setLocationRelativeTo(tw); + setVisible(true); + } + + /** + * display a dialog box for the user to input Principal info + * + * if editPolicyEntry is false, then we are adding Principals to + * a new PolicyEntry, and we only update the GUI listing + * with the new Principal. + * + * if edit is true, then we are editing an existing Policy entry. + */ + void displayPrincipalDialog(boolean editPolicyEntry, boolean edit) { + + PolicyParser.PrincipalEntry editMe = null; + + // get the Principal selected from the Principal List + TaggedList prinList = (TaggedList)getComponent(PE_PRIN_LIST); + int prinIndex = prinList.getSelectedIndex(); + + if (edit) { + editMe = (PolicyParser.PrincipalEntry)prinList.getObject(prinIndex); + } + + ToolDialog newTD = new ToolDialog + (PolicyTool.getMessage("Principals"), tool, tw, true); + newTD.addWindowListener(new ChildWindowListener(newTD)); + + // find where the PolicyTool gui is + Point location = getLocationOnScreen(); + //newTD.setBounds(location.x + 50, location.y + 100, 650, 190); + newTD.setLayout(new GridBagLayout()); + newTD.setResizable(true); + + // description label + JLabel label = (edit ? + new JLabel(PolicyTool.getMessage(".Edit.Principal.")) : + new JLabel(PolicyTool.getMessage(".Add.New.Principal."))); + tw.addNewComponent(newTD, label, PRD_DESC_LABEL, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.TOP_BOTTOM_PADDING); + + // principal choice + JComboBox choice = new JComboBox(); + choice.addItem(PRIN_TYPE); + choice.getAccessibleContext().setAccessibleName(PRIN_TYPE); + for (int i = 0; i < PRIN_ARRAY.size(); i++) { + Prin next = PRIN_ARRAY.get(i); + choice.addItem(next.CLASS); + } + + if (edit) { + if (PolicyParser.PrincipalEntry.WILDCARD_CLASS.equals + (editMe.getPrincipalClass())) { + choice.setSelectedItem(PRIN_TYPE); + } else { + Prin inputPrin = getPrin(editMe.getPrincipalClass(), true); + if (inputPrin != null) { + choice.setSelectedItem(inputPrin.CLASS); + } + } + } + // Add listener after selected item is set + choice.addItemListener(new PrincipalTypeMenuListener(newTD)); + + tw.addNewComponent(newTD, choice, PRD_PRIN_CHOICE, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_PADDING); + + // principal textfield + JTextField tf; + tf = (edit ? + new JTextField(editMe.getDisplayClass(), 30) : + new JTextField(30)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName(PRIN_TYPE); + tw.addNewComponent(newTD, tf, PRD_PRIN_TEXTFIELD, + 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_PADDING); + + // name label and textfield + label = new JLabel(PRIN_NAME); + tf = (edit ? + new JTextField(editMe.getDisplayName(), 40) : + new JTextField(40)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName(PRIN_NAME); + + tw.addNewComponent(newTD, label, PRD_NAME_LABEL, + 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_PADDING); + tw.addNewComponent(newTD, tf, PRD_NAME_TEXTFIELD, + 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_PADDING); + + // OK button + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + okButton.addActionListener( + new NewPolicyPrinOKButtonListener + (tool, tw, this, newTD, edit)); + tw.addNewComponent(newTD, okButton, PRD_OK_BUTTON, + 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.TOP_BOTTOM_PADDING); + // cancel button + JButton cancelButton = new JButton(PolicyTool.getMessage("Cancel")); + ActionListener cancelListener = new CancelButtonListener(newTD); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(newTD, cancelButton, PRD_CANCEL_BUTTON, + 1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.TOP_BOTTOM_PADDING); + + newTD.getRootPane().setDefaultButton(okButton); + newTD.getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + newTD.pack(); + newTD.setLocationRelativeTo(tw); + newTD.setVisible(true); + } + + /** + * display a dialog box for the user to input Permission info + * + * if editPolicyEntry is false, then we are adding Permissions to + * a new PolicyEntry, and we only update the GUI listing + * with the new Permission. + * + * if edit is true, then we are editing an existing Permission entry. + */ + void displayPermissionDialog(boolean editPolicyEntry, boolean edit) { + + PolicyParser.PermissionEntry editMe = null; + + // get the Permission selected from the Permission List + TaggedList permList = (TaggedList)getComponent(PE_PERM_LIST); + int permIndex = permList.getSelectedIndex(); + + if (edit) { + editMe = (PolicyParser.PermissionEntry)permList.getObject(permIndex); + } + + ToolDialog newTD = new ToolDialog + (PolicyTool.getMessage("Permissions"), tool, tw, true); + newTD.addWindowListener(new ChildWindowListener(newTD)); + + // find where the PolicyTool gui is + Point location = getLocationOnScreen(); + //newTD.setBounds(location.x + 50, location.y + 100, 700, 250); + newTD.setLayout(new GridBagLayout()); + newTD.setResizable(true); + + // description label + JLabel label = (edit ? + new JLabel(PolicyTool.getMessage(".Edit.Permission.")) : + new JLabel(PolicyTool.getMessage(".Add.New.Permission."))); + tw.addNewComponent(newTD, label, PD_DESC_LABEL, + 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.TOP_BOTTOM_PADDING); + + // permission choice (added in alphabetical order) + JComboBox choice = new JComboBox(); + choice.addItem(PERM); + choice.getAccessibleContext().setAccessibleName(PERM); + for (int i = 0; i < PERM_ARRAY.size(); i++) { + Perm next = PERM_ARRAY.get(i); + choice.addItem(next.CLASS); + } + tw.addNewComponent(newTD, choice, PD_PERM_CHOICE, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + + // permission textfield + JTextField tf; + tf = (edit ? new JTextField(editMe.permission, 30) : new JTextField(30)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName(PERM); + if (edit) { + Perm inputPerm = getPerm(editMe.permission, true); + if (inputPerm != null) { + choice.setSelectedItem(inputPerm.CLASS); + } + } + tw.addNewComponent(newTD, tf, PD_PERM_TEXTFIELD, + 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + choice.addItemListener(new PermissionMenuListener(newTD)); + + // name label and textfield + choice = new JComboBox(); + choice.addItem(PERM_NAME); + choice.getAccessibleContext().setAccessibleName(PERM_NAME); + tf = (edit ? new JTextField(editMe.name, 40) : new JTextField(40)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName(PERM_NAME); + if (edit) { + setPermissionNames(getPerm(editMe.permission, true), choice, tf); + } + tw.addNewComponent(newTD, choice, PD_NAME_CHOICE, + 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + tw.addNewComponent(newTD, tf, PD_NAME_TEXTFIELD, + 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + choice.addItemListener(new PermissionNameMenuListener(newTD)); + + // actions label and textfield + choice = new JComboBox(); + choice.addItem(PERM_ACTIONS); + choice.getAccessibleContext().setAccessibleName(PERM_ACTIONS); + tf = (edit ? new JTextField(editMe.action, 40) : new JTextField(40)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName(PERM_ACTIONS); + if (edit) { + setPermissionActions(getPerm(editMe.permission, true), choice, tf); + } + tw.addNewComponent(newTD, choice, PD_ACTIONS_CHOICE, + 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + tw.addNewComponent(newTD, tf, PD_ACTIONS_TEXTFIELD, + 1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + choice.addItemListener(new PermissionActionsMenuListener(newTD)); + + // signedby label and textfield + label = new JLabel(PolicyTool.getMessage("Signed.By.")); + tw.addNewComponent(newTD, label, PD_SIGNEDBY_LABEL, + 0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + tf = (edit ? new JTextField(editMe.signedBy, 40) : new JTextField(40)); + tf.setPreferredSize(new Dimension(tf.getPreferredSize().width, TEXTFIELD_HEIGHT)); + tf.getAccessibleContext().setAccessibleName( + PolicyTool.getMessage("Signed.By.")); + tw.addNewComponent(newTD, tf, PD_SIGNEDBY_TEXTFIELD, + 1, 4, 1, 1, 1.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.LR_BOTTOM_PADDING); + + // OK button + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + okButton.addActionListener( + new NewPolicyPermOKButtonListener + (tool, tw, this, newTD, edit)); + tw.addNewComponent(newTD, okButton, PD_OK_BUTTON, + 0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.TOP_BOTTOM_PADDING); + + // cancel button + JButton cancelButton = new JButton(PolicyTool.getMessage("Cancel")); + ActionListener cancelListener = new CancelButtonListener(newTD); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(newTD, cancelButton, PD_CANCEL_BUTTON, + 1, 5, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL, + ToolWindow.TOP_BOTTOM_PADDING); + + newTD.getRootPane().setDefaultButton(okButton); + newTD.getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + newTD.pack(); + newTD.setLocationRelativeTo(tw); + newTD.setVisible(true); + } + + /** + * construct a Principal object from the Principal Info Dialog Box + */ + PolicyParser.PrincipalEntry getPrinFromDialog() throws Exception { + + JTextField tf = (JTextField)getComponent(PRD_PRIN_TEXTFIELD); + String pclass = new String(tf.getText().trim()); + tf = (JTextField)getComponent(PRD_NAME_TEXTFIELD); + String pname = new String(tf.getText().trim()); + if (pclass.equals("*")) { + pclass = PolicyParser.PrincipalEntry.WILDCARD_CLASS; + } + if (pname.equals("*")) { + pname = PolicyParser.PrincipalEntry.WILDCARD_NAME; + } + + PolicyParser.PrincipalEntry pppe = null; + + if ((pclass.equals(PolicyParser.PrincipalEntry.WILDCARD_CLASS)) && + (!pname.equals(PolicyParser.PrincipalEntry.WILDCARD_NAME))) { + throw new Exception + (PolicyTool.getMessage("Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name")); + } else if (pname.equals("")) { + throw new Exception + (PolicyTool.getMessage("Cannot.Specify.Principal.without.a.Name")); + } else if (pclass.equals("")) { + // make this consistent with what PolicyParser does + // when it sees an empty principal class + pclass = PolicyParser.PrincipalEntry.REPLACE_NAME; + tool.warnings.addElement( + "Warning: Principal name '" + pname + + "' specified without a Principal class.\n" + + "\t'" + pname + "' will be interpreted " + + "as a key store alias.\n" + + "\tThe final principal class will be " + + ToolDialog.X500_PRIN_CLASS + ".\n" + + "\tThe final principal name will be " + + "determined by the following:\n" + + "\n" + + "\tIf the key store entry identified by '" + + pname + "'\n" + + "\tis a key entry, then the principal name will be\n" + + "\tthe subject distinguished name from the first\n" + + "\tcertificate in the entry's certificate chain.\n" + + "\n" + + "\tIf the key store entry identified by '" + + pname + "'\n" + + "\tis a trusted certificate entry, then the\n" + + "\tprincipal name will be the subject distinguished\n" + + "\tname from the trusted public key certificate."); + tw.displayStatusDialog(this, + "'" + pname + "' will be interpreted as a key " + + "store alias. View Warning Log for details."); + } + return new PolicyParser.PrincipalEntry(pclass, pname); + } + + + /** + * construct a Permission object from the Permission Info Dialog Box + */ + PolicyParser.PermissionEntry getPermFromDialog() { + + JTextField tf = (JTextField)getComponent(PD_PERM_TEXTFIELD); + String permission = new String(tf.getText().trim()); + tf = (JTextField)getComponent(PD_NAME_TEXTFIELD); + String name = null; + if (tf.getText().trim().equals("") == false) + name = new String(tf.getText().trim()); + if (permission.equals("") || + (!permission.equals(ALL_PERM_CLASS) && name == null)) { + throw new InvalidParameterException(PolicyTool.getMessage + ("Permission.and.Target.Name.must.have.a.value")); + } + + // When the permission is FilePermission, we need to check the name + // to make sure it's not escaped. We believe -- + // + // String name.lastIndexOf("\\\\") + // ---------------- ------------------------ + // c:\foo\bar -1, legal + // c:\\foo\\bar 2, illegal + // \\server\share 0, legal + // \\\\server\share 2, illegal + + if (permission.equals(FILE_PERM_CLASS) && name.lastIndexOf("\\\\") > 0) { + char result = tw.displayYesNoDialog(this, + PolicyTool.getMessage("Warning"), + PolicyTool.getMessage( + "Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes"), + PolicyTool.getMessage("Retain"), + PolicyTool.getMessage("Edit") + ); + if (result != 'Y') { + // an invisible exception + throw new NoDisplayException(); + } + } + // get the Actions + tf = (JTextField)getComponent(PD_ACTIONS_TEXTFIELD); + String actions = null; + if (tf.getText().trim().equals("") == false) + actions = new String(tf.getText().trim()); + + // get the Signed By + tf = (JTextField)getComponent(PD_SIGNEDBY_TEXTFIELD); + String signedBy = null; + if (tf.getText().trim().equals("") == false) + signedBy = new String(tf.getText().trim()); + + PolicyParser.PermissionEntry pppe = new PolicyParser.PermissionEntry + (permission, name, actions); + pppe.signedBy = signedBy; + + // see if the signers have public keys + if (signedBy != null) { + String signers[] = tool.parseSigners(pppe.signedBy); + for (int i = 0; i < signers.length; i++) { + try { + PublicKey pubKey = tool.getPublicKeyAlias(signers[i]); + if (pubKey == null) { + MessageFormat form = new MessageFormat + (PolicyTool.getMessage + ("Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.")); + Object[] source = {signers[i]}; + tool.warnings.addElement(form.format(source)); + tw.displayStatusDialog(this, form.format(source)); + } + } catch (Exception e) { + tw.displayErrorDialog(this, e); + } + } + } + return pppe; + } + + /** + * confirm that the user REALLY wants to remove the Policy Entry + */ + void displayConfirmRemovePolicyEntry() { + + // find the entry to be removed + JList list = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + int index = list.getSelectedIndex(); + PolicyEntry entries[] = tool.getEntry(); + + // find where the PolicyTool gui is + Point location = tw.getLocationOnScreen(); + //setBounds(location.x + 25, location.y + 100, 600, 400); + setLayout(new GridBagLayout()); + + // ask the user do they really want to do this? + JLabel label = new JLabel + (PolicyTool.getMessage("Remove.this.Policy.Entry.")); + tw.addNewComponent(this, label, CRPE_LABEL1, + 0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + + // display the policy entry + label = new JLabel(entries[index].codebaseToString()); + tw.addNewComponent(this, label, CRPE_LABEL2, + 0, 1, 2, 1, 0.0, 0.0, GridBagConstraints.BOTH); + label = new JLabel(entries[index].principalsToString().trim()); + tw.addNewComponent(this, label, CRPE_LABEL2+1, + 0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.BOTH); + Vector perms = + entries[index].getGrantEntry().permissionEntries; + for (int i = 0; i < perms.size(); i++) { + PolicyParser.PermissionEntry nextPerm = perms.elementAt(i); + String permString = ToolDialog.PermissionEntryToUserFriendlyString(nextPerm); + label = new JLabel(" " + permString); + if (i == (perms.size()-1)) { + tw.addNewComponent(this, label, CRPE_LABEL2 + 2 + i, + 1, 3 + i, 1, 1, 0.0, 0.0, + GridBagConstraints.BOTH, + ToolWindow.BOTTOM_PADDING); + } else { + tw.addNewComponent(this, label, CRPE_LABEL2 + 2 + i, + 1, 3 + i, 1, 1, 0.0, 0.0, + GridBagConstraints.BOTH); + } + } + + + // add OK/CANCEL buttons in a new panel + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + // OK button + JButton okButton = new JButton(PolicyTool.getMessage("OK")); + okButton.addActionListener + (new ConfirmRemovePolicyEntryOKButtonListener(tool, tw, this)); + tw.addNewComponent(panel, okButton, CRPE_PANEL_OK, + 0, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, ToolWindow.LR_PADDING); + + // cancel button + JButton cancelButton = new JButton(PolicyTool.getMessage("Cancel")); + ActionListener cancelListener = new CancelButtonListener(this); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(panel, cancelButton, CRPE_PANEL_CANCEL, + 1, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, ToolWindow.LR_PADDING); + + tw.addNewComponent(this, panel, CRPE_LABEL2 + 2 + perms.size(), + 0, 3 + perms.size(), 2, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, ToolWindow.TOP_BOTTOM_PADDING); + + getRootPane().setDefaultButton(okButton); + getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + pack(); + setLocationRelativeTo(tw); + setVisible(true); + } + + /** + * perform SAVE AS + */ + void displaySaveAsDialog(int nextEvent) { + + // pop up a dialog box for the user to enter a filename. + FileDialog fd = new FileDialog + (tw, PolicyTool.getMessage("Save.As"), FileDialog.SAVE); + fd.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + e.getWindow().setVisible(false); + } + }); + fd.setVisible(true); + + // see if the user hit cancel + if (fd.getFile() == null || + fd.getFile().equals("")) + return; + + // get the entered filename + File saveAsFile = new File(fd.getDirectory(), fd.getFile()); + String filename = saveAsFile.getPath(); + fd.dispose(); + + try { + // save the policy entries to a file + tool.savePolicy(filename); + + // display status + MessageFormat form = new MessageFormat(PolicyTool.getMessage + ("Policy.successfully.written.to.filename")); + Object[] source = {filename}; + tw.displayStatusDialog(null, form.format(source)); + + // display the new policy filename + JTextField newFilename = (JTextField)tw.getComponent + (ToolWindow.MW_FILENAME_TEXTFIELD); + newFilename.setText(filename); + tw.setVisible(true); + + // now continue with the originally requested command + // (QUIT, NEW, or OPEN) + userSaveContinue(tool, tw, this, nextEvent); + + } catch (FileNotFoundException fnfe) { + if (filename == null || filename.equals("")) { + tw.displayErrorDialog(null, new FileNotFoundException + (PolicyTool.getMessage("null.filename"))); + } else { + tw.displayErrorDialog(null, fnfe); + } + } catch (Exception ee) { + tw.displayErrorDialog(null, ee); + } + } + + /** + * ask user if they want to save changes + */ + void displayUserSave(int select) { + + if (tool.modified == true) { + + // find where the PolicyTool gui is + Point location = tw.getLocationOnScreen(); + //setBounds(location.x + 75, location.y + 100, 400, 150); + setLayout(new GridBagLayout()); + + JLabel label = new JLabel + (PolicyTool.getMessage("Save.changes.")); + tw.addNewComponent(this, label, USC_LABEL, + 0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.BOTH, + ToolWindow.L_TOP_BOTTOM_PADDING); + + JPanel panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + JButton yesButton = new JButton(); + ToolWindow.configureButton(yesButton, "Yes"); + yesButton.addActionListener + (new UserSaveYesButtonListener(this, tool, tw, select)); + tw.addNewComponent(panel, yesButton, USC_YES_BUTTON, + 0, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, + ToolWindow.LR_BOTTOM_PADDING); + JButton noButton = new JButton(); + ToolWindow.configureButton(noButton, "No"); + noButton.addActionListener + (new UserSaveNoButtonListener(this, tool, tw, select)); + tw.addNewComponent(panel, noButton, USC_NO_BUTTON, + 1, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, + ToolWindow.LR_BOTTOM_PADDING); + JButton cancelButton = new JButton(); + ToolWindow.configureButton(cancelButton, "Cancel"); + ActionListener cancelListener = new CancelButtonListener(this); + cancelButton.addActionListener(cancelListener); + tw.addNewComponent(panel, cancelButton, USC_CANCEL_BUTTON, + 2, 0, 1, 1, 0.0, 0.0, + GridBagConstraints.VERTICAL, + ToolWindow.LR_BOTTOM_PADDING); + + tw.addNewComponent(this, panel, USC_PANEL, + 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH); + + getRootPane().registerKeyboardAction(cancelListener, escKey, JComponent.WHEN_IN_FOCUSED_WINDOW); + + pack(); + setLocationRelativeTo(tw); + setVisible(true); + } else { + // just do the original request (QUIT, NEW, or OPEN) + userSaveContinue(tool, tw, this, select); + } + } + + /** + * when the user sees the 'YES', 'NO', 'CANCEL' buttons on the + * displayUserSave dialog, and the click on one of them, + * we need to continue the originally requested action + * (either QUITting, opening NEW policy file, or OPENing an existing + * policy file. do that now. + */ + @SuppressWarnings("fallthrough") + void userSaveContinue(PolicyTool tool, ToolWindow tw, + ToolDialog us, int select) { + + // now either QUIT, open a NEW policy file, or OPEN an existing policy + switch(select) { + case ToolDialog.QUIT: + + tw.setVisible(false); + tw.dispose(); + System.exit(0); + + case ToolDialog.NEW: + + try { + tool.openPolicy(null); + } catch (Exception ee) { + tool.modified = false; + tw.displayErrorDialog(null, ee); + } + + // display the policy entries via the policy list textarea + JList list = new JList(new DefaultListModel()); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, tw)); + tw.replacePolicyList(list); + + // display null policy filename and keystore + JTextField newFilename = (JTextField)tw.getComponent( + ToolWindow.MW_FILENAME_TEXTFIELD); + newFilename.setText(""); + tw.setVisible(true); + break; + + case ToolDialog.OPEN: + + // pop up a dialog box for the user to enter a filename. + FileDialog fd = new FileDialog + (tw, PolicyTool.getMessage("Open"), FileDialog.LOAD); + fd.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + e.getWindow().setVisible(false); + } + }); + fd.setVisible(true); + + // see if the user hit 'cancel' + if (fd.getFile() == null || + fd.getFile().equals("")) + return; + + // get the entered filename + String policyFile = new File(fd.getDirectory(), fd.getFile()).getPath(); + + try { + // open the policy file + tool.openPolicy(policyFile); + + // display the policy entries via the policy list textarea + DefaultListModel listModel = new DefaultListModel(); + list = new JList(listModel); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, tw)); + PolicyEntry entries[] = tool.getEntry(); + if (entries != null) { + for (int i = 0; i < entries.length; i++) { + listModel.addElement(entries[i].headerToString()); + } + } + tw.replacePolicyList(list); + tool.modified = false; + + // display the new policy filename + newFilename = (JTextField)tw.getComponent( + ToolWindow.MW_FILENAME_TEXTFIELD); + newFilename.setText(policyFile); + tw.setVisible(true); + + // inform user of warnings + if (tool.newWarning == true) { + tw.displayStatusDialog(null, PolicyTool.getMessage + ("Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.")); + } + + } catch (Exception e) { + // add blank policy listing + list = new JList(new DefaultListModel()); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, tw)); + tw.replacePolicyList(list); + tool.setPolicyFileName(null); + tool.modified = false; + + // display a null policy filename + newFilename = (JTextField)tw.getComponent( + ToolWindow.MW_FILENAME_TEXTFIELD); + newFilename.setText(""); + tw.setVisible(true); + + // display the error + MessageFormat form = new MessageFormat(PolicyTool.getMessage + ("Could.not.open.policy.file.policyFile.e.toString.")); + Object[] source = {policyFile, e.toString()}; + tw.displayErrorDialog(null, form.format(source)); + } + break; + } + } + + /** + * Return a Menu list of names for a given permission + * + * If inputPerm's TARGETS are null, then this means TARGETS are + * not allowed to be entered (and the TextField is set to be + * non-editable). + * + * If TARGETS are valid but there are no standard ones + * (user must enter them by hand) then the TARGETS array may be empty + * (and of course non-null). + */ + void setPermissionNames(Perm inputPerm, JComboBox names, JTextField field) { + names.removeAllItems(); + names.addItem(PERM_NAME); + + if (inputPerm == null) { + // custom permission + field.setEditable(true); + } else if (inputPerm.TARGETS == null) { + // standard permission with no targets + field.setEditable(false); + } else { + // standard permission with standard targets + field.setEditable(true); + for (int i = 0; i < inputPerm.TARGETS.length; i++) { + names.addItem(inputPerm.TARGETS[i]); + } + } + } + + /** + * Return a Menu list of actions for a given permission + * + * If inputPerm's ACTIONS are null, then this means ACTIONS are + * not allowed to be entered (and the TextField is set to be + * non-editable). This is typically true for BasicPermissions. + * + * If ACTIONS are valid but there are no standard ones + * (user must enter them by hand) then the ACTIONS array may be empty + * (and of course non-null). + */ + void setPermissionActions(Perm inputPerm, JComboBox actions, JTextField field) { + actions.removeAllItems(); + actions.addItem(PERM_ACTIONS); + + if (inputPerm == null) { + // custom permission + field.setEditable(true); + } else if (inputPerm.ACTIONS == null) { + // standard permission with no actions + field.setEditable(false); + } else { + // standard permission with standard actions + field.setEditable(true); + for (int i = 0; i < inputPerm.ACTIONS.length; i++) { + actions.addItem(inputPerm.ACTIONS[i]); + } + } + } + + static String PermissionEntryToUserFriendlyString(PolicyParser.PermissionEntry pppe) { + String result = pppe.permission; + if (pppe.name != null) { + result += " " + pppe.name; + } + if (pppe.action != null) { + result += ", \"" + pppe.action + "\""; + } + if (pppe.signedBy != null) { + result += ", signedBy " + pppe.signedBy; + } + return result; + } + + static String PrincipalEntryToUserFriendlyString(PolicyParser.PrincipalEntry pppe) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pppe.write(pw); + return sw.toString(); + } +} + +/** + * Event handler for the PolicyTool window + */ +class ToolWindowListener implements WindowListener { + + private PolicyTool tool; + private ToolWindow tw; + + ToolWindowListener(PolicyTool tool, ToolWindow tw) { + this.tool = tool; + this.tw = tw; + } + + public void windowOpened(WindowEvent we) { + } + + public void windowClosing(WindowEvent we) { + // Closing the window acts the same as choosing Menu->Exit. + + // ask user if they want to save changes + ToolDialog td = new ToolDialog(PolicyTool.getMessage("Save.Changes"), tool, tw, true); + td.displayUserSave(ToolDialog.QUIT); + + // the above method will perform the QUIT as long as the + // user does not CANCEL the request + } + + public void windowClosed(WindowEvent we) { + System.exit(0); + } + + public void windowIconified(WindowEvent we) { + } + + public void windowDeiconified(WindowEvent we) { + } + + public void windowActivated(WindowEvent we) { + } + + public void windowDeactivated(WindowEvent we) { + } +} + +/** + * Event handler for the Policy List + */ +class PolicyListListener extends MouseAdapter implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + + PolicyListListener(PolicyTool tool, ToolWindow tw) { + this.tool = tool; + this.tw = tw; + + } + + public void actionPerformed(ActionEvent e) { + + // display the permission list for a policy entry + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Policy.Entry"), tool, tw, true); + td.displayPolicyEntryDialog(true); + } + + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + actionPerformed(null); + } + } +} + +/** + * Event handler for the File Menu + */ +class FileMenuListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + + FileMenuListener(PolicyTool tool, ToolWindow tw) { + this.tool = tool; + this.tw = tw; + } + + public void actionPerformed(ActionEvent e) { + + if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.QUIT) == 0) { + + // ask user if they want to save changes + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Save.Changes"), tool, tw, true); + td.displayUserSave(ToolDialog.QUIT); + + // the above method will perform the QUIT as long as the + // user does not CANCEL the request + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.NEW_POLICY_FILE) == 0) { + + // ask user if they want to save changes + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Save.Changes"), tool, tw, true); + td.displayUserSave(ToolDialog.NEW); + + // the above method will perform the NEW as long as the + // user does not CANCEL the request + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.OPEN_POLICY_FILE) == 0) { + + // ask user if they want to save changes + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Save.Changes"), tool, tw, true); + td.displayUserSave(ToolDialog.OPEN); + + // the above method will perform the OPEN as long as the + // user does not CANCEL the request + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.SAVE_POLICY_FILE) == 0) { + + // get the previously entered filename + String filename = ((JTextField)tw.getComponent( + ToolWindow.MW_FILENAME_TEXTFIELD)).getText(); + + // if there is no filename, do a SAVE_AS + if (filename == null || filename.length() == 0) { + // user wants to SAVE AS + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Save.As"), tool, tw, true); + td.displaySaveAsDialog(ToolDialog.NOACTION); + } else { + try { + // save the policy entries to a file + tool.savePolicy(filename); + + // display status + MessageFormat form = new MessageFormat + (PolicyTool.getMessage + ("Policy.successfully.written.to.filename")); + Object[] source = {filename}; + tw.displayStatusDialog(null, form.format(source)); + } catch (FileNotFoundException fnfe) { + if (filename == null || filename.equals("")) { + tw.displayErrorDialog(null, new FileNotFoundException + (PolicyTool.getMessage("null.filename"))); + } else { + tw.displayErrorDialog(null, fnfe); + } + } catch (Exception ee) { + tw.displayErrorDialog(null, ee); + } + } + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.SAVE_AS_POLICY_FILE) == 0) { + + // user wants to SAVE AS + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Save.As"), tool, tw, true); + td.displaySaveAsDialog(ToolDialog.NOACTION); + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.VIEW_WARNINGS) == 0) { + tw.displayWarningLog(null); + } + } +} + +/** + * Event handler for the main window buttons and Edit Menu + */ +class MainWindowListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + + MainWindowListener(PolicyTool tool, ToolWindow tw) { + this.tool = tool; + this.tw = tw; + } + + public void actionPerformed(ActionEvent e) { + + if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.ADD_POLICY_ENTRY) == 0) { + + // display a dialog box for the user to enter policy info + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Policy.Entry"), tool, tw, true); + td.displayPolicyEntryDialog(false); + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.REMOVE_POLICY_ENTRY) == 0) { + + // get the selected entry + JList list = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + int index = list.getSelectedIndex(); + if (index < 0) { + tw.displayErrorDialog(null, new Exception + (PolicyTool.getMessage("No.Policy.Entry.selected"))); + return; + } + + // ask the user if they really want to remove the policy entry + ToolDialog td = new ToolDialog(PolicyTool.getMessage + ("Remove.Policy.Entry"), tool, tw, true); + td.displayConfirmRemovePolicyEntry(); + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.EDIT_POLICY_ENTRY) == 0) { + + // get the selected entry + JList list = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + int index = list.getSelectedIndex(); + if (index < 0) { + tw.displayErrorDialog(null, new Exception + (PolicyTool.getMessage("No.Policy.Entry.selected"))); + return; + } + + // display the permission list for a policy entry + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("Policy.Entry"), tool, tw, true); + td.displayPolicyEntryDialog(true); + + } else if (PolicyTool.collator.compare(e.getActionCommand(), + ToolWindow.EDIT_KEYSTORE) == 0) { + + // display a dialog box for the user to enter keystore info + ToolDialog td = new ToolDialog + (PolicyTool.getMessage("KeyStore"), tool, tw, true); + td.keyStoreDialog(ToolDialog.EDIT_KEYSTORE); + } + } +} + +/** + * Event handler for AddEntryDoneButton button + * + * -- if edit is TRUE, then we are EDITing an existing PolicyEntry + * and we need to update both the policy and the GUI listing. + * if edit is FALSE, then we are ADDing a new PolicyEntry, + * so we only need to update the GUI listing. + */ +class AddEntryDoneButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean edit; + + AddEntryDoneButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean edit) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.edit = edit; + } + + public void actionPerformed(ActionEvent e) { + + try { + // get a PolicyEntry object from the dialog policy info + PolicyEntry newEntry = td.getPolicyEntryFromDialog(); + PolicyParser.GrantEntry newGe = newEntry.getGrantEntry(); + + // see if all the signers have public keys + if (newGe.signedBy != null) { + String signers[] = tool.parseSigners(newGe.signedBy); + for (int i = 0; i < signers.length; i++) { + PublicKey pubKey = tool.getPublicKeyAlias(signers[i]); + if (pubKey == null) { + MessageFormat form = new MessageFormat + (PolicyTool.getMessage + ("Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.")); + Object[] source = {signers[i]}; + tool.warnings.addElement(form.format(source)); + tw.displayStatusDialog(td, form.format(source)); + } + } + } + + // add the entry + JList policyList = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + if (edit) { + int listIndex = policyList.getSelectedIndex(); + tool.addEntry(newEntry, listIndex); + String newCodeBaseStr = newEntry.headerToString(); + if (PolicyTool.collator.compare + (newCodeBaseStr, policyList.getModel().getElementAt(listIndex)) != 0) + tool.modified = true; + ((DefaultListModel)policyList.getModel()).set(listIndex, newCodeBaseStr); + } else { + tool.addEntry(newEntry, -1); + ((DefaultListModel)policyList.getModel()).addElement(newEntry.headerToString()); + tool.modified = true; + } + td.setVisible(false); + td.dispose(); + + } catch (Exception eee) { + tw.displayErrorDialog(td, eee); + } + } +} + +/** + * Event handler for ChangeKeyStoreOKButton button + */ +class ChangeKeyStoreOKButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + + ChangeKeyStoreOKButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td) { + this.tool = tool; + this.tw = tw; + this.td = td; + } + + public void actionPerformed(ActionEvent e) { + + String URLString = ((JTextField)td.getComponent( + ToolDialog.KSD_NAME_TEXTFIELD)).getText().trim(); + String type = ((JTextField)td.getComponent( + ToolDialog.KSD_TYPE_TEXTFIELD)).getText().trim(); + String provider = ((JTextField)td.getComponent( + ToolDialog.KSD_PROVIDER_TEXTFIELD)).getText().trim(); + String pwdURL = ((JTextField)td.getComponent( + ToolDialog.KSD_PWD_URL_TEXTFIELD)).getText().trim(); + + try { + tool.openKeyStore + ((URLString.length() == 0 ? null : URLString), + (type.length() == 0 ? null : type), + (provider.length() == 0 ? null : provider), + (pwdURL.length() == 0 ? null : pwdURL)); + tool.modified = true; + } catch (Exception ex) { + MessageFormat form = new MessageFormat(PolicyTool.getMessage + ("Unable.to.open.KeyStore.ex.toString.")); + Object[] source = {ex.toString()}; + tw.displayErrorDialog(td, form.format(source)); + return; + } + + td.dispose(); + } +} + +/** + * Event handler for AddPrinButton button + */ +class AddPrinButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean editPolicyEntry; + + AddPrinButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean editPolicyEntry) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.editPolicyEntry = editPolicyEntry; + } + + public void actionPerformed(ActionEvent e) { + + // display a dialog box for the user to enter principal info + td.displayPrincipalDialog(editPolicyEntry, false); + } +} + +/** + * Event handler for AddPermButton button + */ +class AddPermButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean editPolicyEntry; + + AddPermButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean editPolicyEntry) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.editPolicyEntry = editPolicyEntry; + } + + public void actionPerformed(ActionEvent e) { + + // display a dialog box for the user to enter permission info + td.displayPermissionDialog(editPolicyEntry, false); + } +} + +/** + * Event handler for AddPrinOKButton button + */ +class NewPolicyPrinOKButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog listDialog; + private ToolDialog infoDialog; + private boolean edit; + + NewPolicyPrinOKButtonListener(PolicyTool tool, + ToolWindow tw, + ToolDialog listDialog, + ToolDialog infoDialog, + boolean edit) { + this.tool = tool; + this.tw = tw; + this.listDialog = listDialog; + this.infoDialog = infoDialog; + this.edit = edit; + } + + public void actionPerformed(ActionEvent e) { + + try { + // read in the new principal info from Dialog Box + PolicyParser.PrincipalEntry pppe = + infoDialog.getPrinFromDialog(); + if (pppe != null) { + try { + tool.verifyPrincipal(pppe.getPrincipalClass(), + pppe.getPrincipalName()); + } catch (ClassNotFoundException cnfe) { + MessageFormat form = new MessageFormat + (PolicyTool.getMessage + ("Warning.Class.not.found.class")); + Object[] source = {pppe.getPrincipalClass()}; + tool.warnings.addElement(form.format(source)); + tw.displayStatusDialog(infoDialog, form.format(source)); + } + + // add the principal to the GUI principal list + TaggedList prinList = + (TaggedList)listDialog.getComponent(ToolDialog.PE_PRIN_LIST); + + String prinString = ToolDialog.PrincipalEntryToUserFriendlyString(pppe); + if (edit) { + // if editing, replace the original principal + int index = prinList.getSelectedIndex(); + prinList.replaceTaggedItem(prinString, pppe, index); + } else { + // if adding, just add it to the end + prinList.addTaggedItem(prinString, pppe); + } + } + infoDialog.dispose(); + } catch (Exception ee) { + tw.displayErrorDialog(infoDialog, ee); + } + } +} + +/** + * Event handler for AddPermOKButton button + */ +class NewPolicyPermOKButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog listDialog; + private ToolDialog infoDialog; + private boolean edit; + + NewPolicyPermOKButtonListener(PolicyTool tool, + ToolWindow tw, + ToolDialog listDialog, + ToolDialog infoDialog, + boolean edit) { + this.tool = tool; + this.tw = tw; + this.listDialog = listDialog; + this.infoDialog = infoDialog; + this.edit = edit; + } + + public void actionPerformed(ActionEvent e) { + + try { + // read in the new permission info from Dialog Box + PolicyParser.PermissionEntry pppe = + infoDialog.getPermFromDialog(); + + try { + tool.verifyPermission(pppe.permission, pppe.name, pppe.action); + } catch (ClassNotFoundException cnfe) { + MessageFormat form = new MessageFormat(PolicyTool.getMessage + ("Warning.Class.not.found.class")); + Object[] source = {pppe.permission}; + tool.warnings.addElement(form.format(source)); + tw.displayStatusDialog(infoDialog, form.format(source)); + } + + // add the permission to the GUI permission list + TaggedList permList = + (TaggedList)listDialog.getComponent(ToolDialog.PE_PERM_LIST); + + String permString = ToolDialog.PermissionEntryToUserFriendlyString(pppe); + if (edit) { + // if editing, replace the original permission + int which = permList.getSelectedIndex(); + permList.replaceTaggedItem(permString, pppe, which); + } else { + // if adding, just add it to the end + permList.addTaggedItem(permString, pppe); + } + infoDialog.dispose(); + + } catch (InvocationTargetException ite) { + tw.displayErrorDialog(infoDialog, ite.getTargetException()); + } catch (Exception ee) { + tw.displayErrorDialog(infoDialog, ee); + } + } +} + +/** + * Event handler for RemovePrinButton button + */ +class RemovePrinButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean edit; + + RemovePrinButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean edit) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.edit = edit; + } + + public void actionPerformed(ActionEvent e) { + + // get the Principal selected from the Principal List + TaggedList prinList = (TaggedList)td.getComponent( + ToolDialog.PE_PRIN_LIST); + int prinIndex = prinList.getSelectedIndex(); + + if (prinIndex < 0) { + tw.displayErrorDialog(td, new Exception + (PolicyTool.getMessage("No.principal.selected"))); + return; + } + // remove the principal from the display + prinList.removeTaggedItem(prinIndex); + } +} + +/** + * Event handler for RemovePermButton button + */ +class RemovePermButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean edit; + + RemovePermButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean edit) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.edit = edit; + } + + public void actionPerformed(ActionEvent e) { + + // get the Permission selected from the Permission List + TaggedList permList = (TaggedList)td.getComponent( + ToolDialog.PE_PERM_LIST); + int permIndex = permList.getSelectedIndex(); + + if (permIndex < 0) { + tw.displayErrorDialog(td, new Exception + (PolicyTool.getMessage("No.permission.selected"))); + return; + } + // remove the permission from the display + permList.removeTaggedItem(permIndex); + + } +} + +/** + * Event handler for Edit Principal button + * + * We need the editPolicyEntry boolean to tell us if the user is + * adding a new PolicyEntry at this time, or editing an existing entry. + * If the user is adding a new PolicyEntry, we ONLY update the + * GUI listing. If the user is editing an existing PolicyEntry, we + * update both the GUI listing and the actual PolicyEntry. + */ +class EditPrinButtonListener extends MouseAdapter implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean editPolicyEntry; + + EditPrinButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean editPolicyEntry) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.editPolicyEntry = editPolicyEntry; + } + + public void actionPerformed(ActionEvent e) { + + // get the Principal selected from the Principal List + TaggedList list = (TaggedList)td.getComponent( + ToolDialog.PE_PRIN_LIST); + int prinIndex = list.getSelectedIndex(); + + if (prinIndex < 0) { + tw.displayErrorDialog(td, new Exception + (PolicyTool.getMessage("No.principal.selected"))); + return; + } + td.displayPrincipalDialog(editPolicyEntry, true); + } + + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + actionPerformed(null); + } + } +} + +/** + * Event handler for Edit Permission button + * + * We need the editPolicyEntry boolean to tell us if the user is + * adding a new PolicyEntry at this time, or editing an existing entry. + * If the user is adding a new PolicyEntry, we ONLY update the + * GUI listing. If the user is editing an existing PolicyEntry, we + * update both the GUI listing and the actual PolicyEntry. + */ +class EditPermButtonListener extends MouseAdapter implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog td; + private boolean editPolicyEntry; + + EditPermButtonListener(PolicyTool tool, ToolWindow tw, + ToolDialog td, boolean editPolicyEntry) { + this.tool = tool; + this.tw = tw; + this.td = td; + this.editPolicyEntry = editPolicyEntry; + } + + public void actionPerformed(ActionEvent e) { + + // get the Permission selected from the Permission List + JList list = (JList)td.getComponent(ToolDialog.PE_PERM_LIST); + int permIndex = list.getSelectedIndex(); + + if (permIndex < 0) { + tw.displayErrorDialog(td, new Exception + (PolicyTool.getMessage("No.permission.selected"))); + return; + } + td.displayPermissionDialog(editPolicyEntry, true); + } + + public void mouseClicked(MouseEvent evt) { + if (evt.getClickCount() == 2) { + actionPerformed(null); + } + } +} + +/** + * Event handler for Principal Popup Menu + */ +class PrincipalTypeMenuListener implements ItemListener { + + private ToolDialog td; + + PrincipalTypeMenuListener(ToolDialog td) { + this.td = td; + } + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) { + // We're only interested in SELECTED events + return; + } + + JComboBox prin = (JComboBox)td.getComponent(ToolDialog.PRD_PRIN_CHOICE); + JTextField prinField = (JTextField)td.getComponent( + ToolDialog.PRD_PRIN_TEXTFIELD); + JTextField nameField = (JTextField)td.getComponent( + ToolDialog.PRD_NAME_TEXTFIELD); + + prin.getAccessibleContext().setAccessibleName( + PolicyTool.splitToWords((String)e.getItem())); + if (((String)e.getItem()).equals(ToolDialog.PRIN_TYPE)) { + // ignore if they choose "Principal Type:" item + if (prinField.getText() != null && + prinField.getText().length() > 0) { + Prin inputPrin = ToolDialog.getPrin(prinField.getText(), true); + prin.setSelectedItem(inputPrin.CLASS); + } + return; + } + + // if you change the principal, clear the name + if (prinField.getText().indexOf((String)e.getItem()) == -1) { + nameField.setText(""); + } + + // set the text in the textfield and also modify the + // pull-down choice menus to reflect the correct possible + // set of names and actions + Prin inputPrin = ToolDialog.getPrin((String)e.getItem(), false); + if (inputPrin != null) { + prinField.setText(inputPrin.FULL_CLASS); + } + } +} + +/** + * Event handler for Permission Popup Menu + */ +class PermissionMenuListener implements ItemListener { + + private ToolDialog td; + + PermissionMenuListener(ToolDialog td) { + this.td = td; + } + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) { + // We're only interested in SELECTED events + return; + } + + JComboBox perms = (JComboBox)td.getComponent( + ToolDialog.PD_PERM_CHOICE); + JComboBox names = (JComboBox)td.getComponent( + ToolDialog.PD_NAME_CHOICE); + JComboBox actions = (JComboBox)td.getComponent( + ToolDialog.PD_ACTIONS_CHOICE); + JTextField nameField = (JTextField)td.getComponent( + ToolDialog.PD_NAME_TEXTFIELD); + JTextField actionsField = (JTextField)td.getComponent( + ToolDialog.PD_ACTIONS_TEXTFIELD); + JTextField permField = (JTextField)td.getComponent( + ToolDialog.PD_PERM_TEXTFIELD); + JTextField signedbyField = (JTextField)td.getComponent( + ToolDialog.PD_SIGNEDBY_TEXTFIELD); + + perms.getAccessibleContext().setAccessibleName( + PolicyTool.splitToWords((String)e.getItem())); + + // ignore if they choose the 'Permission:' item + if (PolicyTool.collator.compare((String)e.getItem(), + ToolDialog.PERM) == 0) { + if (permField.getText() != null && + permField.getText().length() > 0) { + + Perm inputPerm = ToolDialog.getPerm(permField.getText(), true); + if (inputPerm != null) { + perms.setSelectedItem(inputPerm.CLASS); + } + } + return; + } + + // if you change the permission, clear the name, actions, and signedBy + if (permField.getText().indexOf((String)e.getItem()) == -1) { + nameField.setText(""); + actionsField.setText(""); + signedbyField.setText(""); + } + + // set the text in the textfield and also modify the + // pull-down choice menus to reflect the correct possible + // set of names and actions + + Perm inputPerm = ToolDialog.getPerm((String)e.getItem(), false); + if (inputPerm == null) { + permField.setText(""); + } else { + permField.setText(inputPerm.FULL_CLASS); + } + td.setPermissionNames(inputPerm, names, nameField); + td.setPermissionActions(inputPerm, actions, actionsField); + } +} + +/** + * Event handler for Permission Name Popup Menu + */ +class PermissionNameMenuListener implements ItemListener { + + private ToolDialog td; + + PermissionNameMenuListener(ToolDialog td) { + this.td = td; + } + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) { + // We're only interested in SELECTED events + return; + } + + JComboBox names = (JComboBox)td.getComponent(ToolDialog.PD_NAME_CHOICE); + names.getAccessibleContext().setAccessibleName( + PolicyTool.splitToWords((String)e.getItem())); + + if (((String)e.getItem()).indexOf(ToolDialog.PERM_NAME) != -1) + return; + + JTextField tf = (JTextField)td.getComponent(ToolDialog.PD_NAME_TEXTFIELD); + tf.setText((String)e.getItem()); + } +} + +/** + * Event handler for Permission Actions Popup Menu + */ +class PermissionActionsMenuListener implements ItemListener { + + private ToolDialog td; + + PermissionActionsMenuListener(ToolDialog td) { + this.td = td; + } + + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) { + // We're only interested in SELECTED events + return; + } + + JComboBox actions = (JComboBox)td.getComponent( + ToolDialog.PD_ACTIONS_CHOICE); + actions.getAccessibleContext().setAccessibleName((String)e.getItem()); + + if (((String)e.getItem()).indexOf(ToolDialog.PERM_ACTIONS) != -1) + return; + + JTextField tf = (JTextField)td.getComponent( + ToolDialog.PD_ACTIONS_TEXTFIELD); + if (tf.getText() == null || tf.getText().equals("")) { + tf.setText((String)e.getItem()); + } else { + if (tf.getText().indexOf((String)e.getItem()) == -1) + tf.setText(tf.getText() + ", " + (String)e.getItem()); + } + } +} + +/** + * Event handler for all the children dialogs/windows + */ +class ChildWindowListener implements WindowListener { + + private ToolDialog td; + + ChildWindowListener(ToolDialog td) { + this.td = td; + } + + public void windowOpened(WindowEvent we) { + } + + public void windowClosing(WindowEvent we) { + // same as pressing the "cancel" button + td.setVisible(false); + td.dispose(); + } + + public void windowClosed(WindowEvent we) { + } + + public void windowIconified(WindowEvent we) { + } + + public void windowDeiconified(WindowEvent we) { + } + + public void windowActivated(WindowEvent we) { + } + + public void windowDeactivated(WindowEvent we) { + } +} + +/** + * Event handler for CancelButton button + */ +class CancelButtonListener implements ActionListener { + + private ToolDialog td; + + CancelButtonListener(ToolDialog td) { + this.td = td; + } + + public void actionPerformed(ActionEvent e) { + td.setVisible(false); + td.dispose(); + } +} + +/** + * Event handler for ErrorOKButton button + */ +class ErrorOKButtonListener implements ActionListener { + + private ToolDialog ed; + + ErrorOKButtonListener(ToolDialog ed) { + this.ed = ed; + } + + public void actionPerformed(ActionEvent e) { + ed.setVisible(false); + ed.dispose(); + } +} + +/** + * Event handler for StatusOKButton button + */ +class StatusOKButtonListener implements ActionListener { + + private ToolDialog sd; + + StatusOKButtonListener(ToolDialog sd) { + this.sd = sd; + } + + public void actionPerformed(ActionEvent e) { + sd.setVisible(false); + sd.dispose(); + } +} + +/** + * Event handler for UserSaveYes button + */ +class UserSaveYesButtonListener implements ActionListener { + + private ToolDialog us; + private PolicyTool tool; + private ToolWindow tw; + private int select; + + UserSaveYesButtonListener(ToolDialog us, PolicyTool tool, + ToolWindow tw, int select) { + this.us = us; + this.tool = tool; + this.tw = tw; + this.select = select; + } + + public void actionPerformed(ActionEvent e) { + + // first get rid of the window + us.setVisible(false); + us.dispose(); + + try { + String filename = ((JTextField)tw.getComponent( + ToolWindow.MW_FILENAME_TEXTFIELD)).getText(); + if (filename == null || filename.equals("")) { + us.displaySaveAsDialog(select); + + // the above dialog will continue with the originally + // requested command if necessary + } else { + // save the policy entries to a file + tool.savePolicy(filename); + + // display status + MessageFormat form = new MessageFormat + (PolicyTool.getMessage + ("Policy.successfully.written.to.filename")); + Object[] source = {filename}; + tw.displayStatusDialog(null, form.format(source)); + + // now continue with the originally requested command + // (QUIT, NEW, or OPEN) + us.userSaveContinue(tool, tw, us, select); + } + } catch (Exception ee) { + // error -- just report it and bail + tw.displayErrorDialog(null, ee); + } + } +} + +/** + * Event handler for UserSaveNoButton + */ +class UserSaveNoButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog us; + private int select; + + UserSaveNoButtonListener(ToolDialog us, PolicyTool tool, + ToolWindow tw, int select) { + this.us = us; + this.tool = tool; + this.tw = tw; + this.select = select; + } + + public void actionPerformed(ActionEvent e) { + us.setVisible(false); + us.dispose(); + + // now continue with the originally requested command + // (QUIT, NEW, or OPEN) + us.userSaveContinue(tool, tw, us, select); + } +} + +/** + * Event handler for UserSaveCancelButton + */ +class UserSaveCancelButtonListener implements ActionListener { + + private ToolDialog us; + + UserSaveCancelButtonListener(ToolDialog us) { + this.us = us; + } + + public void actionPerformed(ActionEvent e) { + us.setVisible(false); + us.dispose(); + + // do NOT continue with the originally requested command + // (QUIT, NEW, or OPEN) + } +} + +/** + * Event handler for ConfirmRemovePolicyEntryOKButtonListener + */ +class ConfirmRemovePolicyEntryOKButtonListener implements ActionListener { + + private PolicyTool tool; + private ToolWindow tw; + private ToolDialog us; + + ConfirmRemovePolicyEntryOKButtonListener(PolicyTool tool, + ToolWindow tw, ToolDialog us) { + this.tool = tool; + this.tw = tw; + this.us = us; + } + + public void actionPerformed(ActionEvent e) { + // remove the entry + JList list = (JList)tw.getComponent(ToolWindow.MW_POLICY_LIST); + int index = list.getSelectedIndex(); + PolicyEntry entries[] = tool.getEntry(); + tool.removeEntry(entries[index]); + + // redraw the window listing + DefaultListModel listModel = new DefaultListModel(); + list = new JList(listModel); + list.setVisibleRowCount(15); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addMouseListener(new PolicyListListener(tool, tw)); + entries = tool.getEntry(); + if (entries != null) { + for (int i = 0; i < entries.length; i++) { + listModel.addElement(entries[i].headerToString()); + } + } + tw.replacePolicyList(list); + us.setVisible(false); + us.dispose(); + } +} + +/** + * Just a special name, so that the codes dealing with this exception knows + * it's special, and does not pop out a warning box. + */ +class NoDisplayException extends RuntimeException { + private static final long serialVersionUID = -4611761427108719794L; +} + +/** + * This is a java.awt.List that bind an Object to each String it holds. + */ +class TaggedList extends JList { + private static final long serialVersionUID = -5676238110427785853L; + + private java.util.List data = new LinkedList<>(); + public TaggedList(int i, boolean b) { + super(new DefaultListModel()); + setVisibleRowCount(i); + setSelectionMode(b ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION); + } + + public Object getObject(int index) { + return data.get(index); + } + + public void addTaggedItem(String string, Object object) { + ((DefaultListModel)getModel()).addElement(string); + data.add(object); + } + + public void replaceTaggedItem(String string, Object object, int index) { + ((DefaultListModel)getModel()).set(index, string); + data.set(index, object); + } + + public void removeTaggedItem(int index) { + ((DefaultListModel)getModel()).remove(index); + data.remove(index); + } +} + +/** + * Convenience Principal Classes + */ + +class Prin { + public final String CLASS; + public final String FULL_CLASS; + + public Prin(String clazz, String fullClass) { + this.CLASS = clazz; + this.FULL_CLASS = fullClass; + } +} + +class KrbPrin extends Prin { + public KrbPrin() { + super("KerberosPrincipal", + "javax.security.auth.kerberos.KerberosPrincipal"); + } +} + +class X500Prin extends Prin { + public X500Prin() { + super("X500Principal", + "javax.security.auth.x500.X500Principal"); + } +} + +/** + * Convenience Permission Classes + */ + +class Perm { + public final String CLASS; + public final String FULL_CLASS; + public final String[] TARGETS; + public final String[] ACTIONS; + + public Perm(String clazz, String fullClass, + String[] targets, String[] actions) { + + this.CLASS = clazz; + this.FULL_CLASS = fullClass; + this.TARGETS = targets; + this.ACTIONS = actions; + } +} + +class AllPerm extends Perm { + public AllPerm() { + super("AllPermission", "java.security.AllPermission", null, null); + } +} + +class AudioPerm extends Perm { + public AudioPerm() { + super("AudioPermission", + "javax.sound.sampled.AudioPermission", + new String[] { + "play", + "record" + }, + null); + } +} + +class AuthPerm extends Perm { + public AuthPerm() { + super("AuthPermission", + "javax.security.auth.AuthPermission", + new String[] { + "doAs", + "doAsPrivileged", + "getSubject", + "getSubjectFromDomainCombiner", + "setReadOnly", + "modifyPrincipals", + "modifyPublicCredentials", + "modifyPrivateCredentials", + "refreshCredential", + "destroyCredential", + "createLoginContext.<" + PolicyTool.getMessage("name") + ">", + "getLoginConfiguration", + "setLoginConfiguration", + "createLoginConfiguration.<" + + PolicyTool.getMessage("configuration.type") + ">", + "refreshLoginConfiguration" + }, + null); + } +} + +class AWTPerm extends Perm { + public AWTPerm() { + super("AWTPermission", + "java.awt.AWTPermission", + new String[] { + "accessClipboard", + "accessEventQueue", + "accessSystemTray", + "createRobot", + "fullScreenExclusive", + "listenToAllAWTEvents", + "readDisplayPixels", + "replaceKeyboardFocusManager", + "setAppletStub", + "setWindowAlwaysOnTop", + "showWindowWithoutWarningBanner", + "toolkitModality", + "watchMousePointer" + }, + null); + } +} + +class DelegationPerm extends Perm { + public DelegationPerm() { + super("DelegationPermission", + "javax.security.auth.kerberos.DelegationPermission", + new String[] { + // allow user input + }, + null); + } +} + +class FilePerm extends Perm { + public FilePerm() { + super("FilePermission", + "java.io.FilePermission", + new String[] { + "<>" + }, + new String[] { + "read", + "write", + "delete", + "execute" + }); + } +} + +class URLPerm extends Perm { + public URLPerm() { + super("URLPermission", + "java.net.URLPermission", + new String[] { + "<"+ PolicyTool.getMessage("url") + ">", + }, + new String[] { + "<" + PolicyTool.getMessage("method.list") + ">:<" + + PolicyTool.getMessage("request.headers.list") + ">", + }); + } +} + +class InqSecContextPerm extends Perm { + public InqSecContextPerm() { + super("InquireSecContextPermission", + "com.sun.security.jgss.InquireSecContextPermission", + new String[] { + "KRB5_GET_SESSION_KEY", + "KRB5_GET_TKT_FLAGS", + "KRB5_GET_AUTHZ_DATA", + "KRB5_GET_AUTHTIME" + }, + null); + } +} + +class LogPerm extends Perm { + public LogPerm() { + super("LoggingPermission", + "java.util.logging.LoggingPermission", + new String[] { + "control" + }, + null); + } +} + +class MgmtPerm extends Perm { + public MgmtPerm() { + super("ManagementPermission", + "java.lang.management.ManagementPermission", + new String[] { + "control", + "monitor" + }, + null); + } +} + +class MBeanPerm extends Perm { + public MBeanPerm() { + super("MBeanPermission", + "javax.management.MBeanPermission", + new String[] { + // allow user input + }, + new String[] { + "addNotificationListener", + "getAttribute", + "getClassLoader", + "getClassLoaderFor", + "getClassLoaderRepository", + "getDomains", + "getMBeanInfo", + "getObjectInstance", + "instantiate", + "invoke", + "isInstanceOf", + "queryMBeans", + "queryNames", + "registerMBean", + "removeNotificationListener", + "setAttribute", + "unregisterMBean" + }); + } +} + +class MBeanSvrPerm extends Perm { + public MBeanSvrPerm() { + super("MBeanServerPermission", + "javax.management.MBeanServerPermission", + new String[] { + "createMBeanServer", + "findMBeanServer", + "newMBeanServer", + "releaseMBeanServer" + }, + null); + } +} + +class MBeanTrustPerm extends Perm { + public MBeanTrustPerm() { + super("MBeanTrustPermission", + "javax.management.MBeanTrustPermission", + new String[] { + "register" + }, + null); + } +} + +class NetPerm extends Perm { + public NetPerm() { + super("NetPermission", + "java.net.NetPermission", + new String[] { + "setDefaultAuthenticator", + "requestPasswordAuthentication", + "specifyStreamHandler", + "setProxySelector", + "getProxySelector", + "setCookieHandler", + "getCookieHandler", + "setResponseCache", + "getResponseCache" + }, + null); + } +} + +class PrivCredPerm extends Perm { + public PrivCredPerm() { + super("PrivateCredentialPermission", + "javax.security.auth.PrivateCredentialPermission", + new String[] { + // allow user input + }, + new String[] { + "read" + }); + } +} + +class PropPerm extends Perm { + public PropPerm() { + super("PropertyPermission", + "java.util.PropertyPermission", + new String[] { + // allow user input + }, + new String[] { + "read", + "write" + }); + } +} + +class ReflectPerm extends Perm { + public ReflectPerm() { + super("ReflectPermission", + "java.lang.reflect.ReflectPermission", + new String[] { + "suppressAccessChecks" + }, + null); + } +} + +class RuntimePerm extends Perm { + public RuntimePerm() { + super("RuntimePermission", + "java.lang.RuntimePermission", + new String[] { + "createClassLoader", + "getClassLoader", + "setContextClassLoader", + "enableContextClassLoaderOverride", + "setSecurityManager", + "createSecurityManager", + "getenv.<" + + PolicyTool.getMessage("environment.variable.name") + ">", + "exitVM", + "shutdownHooks", + "setFactory", + "setIO", + "modifyThread", + "stopThread", + "modifyThreadGroup", + "getProtectionDomain", + "readFileDescriptor", + "writeFileDescriptor", + "loadLibrary.<" + + PolicyTool.getMessage("library.name") + ">", + "accessClassInPackage.<" + + PolicyTool.getMessage("package.name")+">", + "defineClassInPackage.<" + + PolicyTool.getMessage("package.name")+">", + "accessDeclaredMembers", + "queuePrintJob", + "getStackTrace", + "setDefaultUncaughtExceptionHandler", + "preferences", + "usePolicy", + // "inheritedChannel" + }, + null); + } +} + +class SecurityPerm extends Perm { + public SecurityPerm() { + super("SecurityPermission", + "java.security.SecurityPermission", + new String[] { + "createAccessControlContext", + "getDomainCombiner", + "getPolicy", + "setPolicy", + "createPolicy.<" + + PolicyTool.getMessage("policy.type") + ">", + "getProperty.<" + + PolicyTool.getMessage("property.name") + ">", + "setProperty.<" + + PolicyTool.getMessage("property.name") + ">", + "insertProvider.<" + + PolicyTool.getMessage("provider.name") + ">", + "removeProvider.<" + + PolicyTool.getMessage("provider.name") + ">", + //"setSystemScope", + //"setIdentityPublicKey", + //"setIdentityInfo", + //"addIdentityCertificate", + //"removeIdentityCertificate", + //"printIdentity", + "clearProviderProperties.<" + + PolicyTool.getMessage("provider.name") + ">", + "putProviderProperty.<" + + PolicyTool.getMessage("provider.name") + ">", + "removeProviderProperty.<" + + PolicyTool.getMessage("provider.name") + ">", + //"getSignerPrivateKey", + //"setSignerKeyPair" + }, + null); + } +} + +class SerialPerm extends Perm { + public SerialPerm() { + super("SerializablePermission", + "java.io.SerializablePermission", + new String[] { + "enableSubclassImplementation", + "enableSubstitution" + }, + null); + } +} + +class ServicePerm extends Perm { + public ServicePerm() { + super("ServicePermission", + "javax.security.auth.kerberos.ServicePermission", + new String[] { + // allow user input + }, + new String[] { + "initiate", + "accept" + }); + } +} + +class SocketPerm extends Perm { + public SocketPerm() { + super("SocketPermission", + "java.net.SocketPermission", + new String[] { + // allow user input + }, + new String[] { + "accept", + "connect", + "listen", + "resolve" + }); + } +} + +class SQLPerm extends Perm { + public SQLPerm() { + super("SQLPermission", + "java.sql.SQLPermission", + new String[] { + "setLog", + "callAbort", + "setSyncFactory", + "setNetworkTimeout", + }, + null); + } +} + +class SSLPerm extends Perm { + public SSLPerm() { + super("SSLPermission", + "javax.net.ssl.SSLPermission", + new String[] { + "setHostnameVerifier", + "getSSLSessionContext" + }, + null); + } +} + +class SubjDelegPerm extends Perm { + public SubjDelegPerm() { + super("SubjectDelegationPermission", + "javax.management.remote.SubjectDelegationPermission", + new String[] { + // allow user input + }, + null); + } +} diff --git a/src/sun/security/tools/policytool/Resources.java b/src/sun/security/tools/policytool/Resources.java new file mode 100644 index 00000000..c666ae4b --- /dev/null +++ b/src/sun/security/tools/policytool/Resources.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Warning: A public key for alias {0} does not exist. Make sure a KeyStore is properly configured."}, + {"Warning.Class.not.found.class", "Warning: Class not found: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Warning: Invalid argument(s) for constructor: {0}"}, + {"Illegal.Principal.Type.type", "Illegal Principal Type: {0}"}, + {"Illegal.option.option", "Illegal option: {0}"}, + {"Usage.policytool.options.", "Usage: policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] policy file location"}, + {"New", "&New"}, + {"Open", "&Open..."}, + {"Save", "&Save"}, + {"Save.As", "Save &As..."}, + {"View.Warning.Log", "View &Warning Log"}, + {"Exit", "E&xit"}, + {"Add.Policy.Entry", "&Add Policy Entry"}, + {"Edit.Policy.Entry", "&Edit Policy Entry"}, + {"Remove.Policy.Entry", "&Remove Policy Entry"}, + {"Edit", "&Edit"}, + {"Retain", "Retain"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Warning: File name may include escaped backslash characters. " + + "It is not necessary to escape backslash characters " + + "(the tool escapes characters as necessary when writing " + + "the policy contents to the persistent store).\n\n" + + "Click on Retain to retain the entered name, or click on " + + "Edit to edit the name."}, + + {"Add.Public.Key.Alias", "Add Public Key Alias"}, + {"Remove.Public.Key.Alias", "Remove Public Key Alias"}, + {"File", "&File"}, + {"KeyStore", "&KeyStore"}, + {"Policy.File.", "Policy File:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "Could not open policy file: {0}: {1}"}, + {"Policy.Tool", "Policy Tool"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Errors have occurred while opening the policy configuration. View the Warning Log for more information."}, + {"Error", "Error"}, + {"OK", "OK"}, + {"Status", "Status"}, + {"Warning", "Warning"}, + {"Permission.", + "Permission: "}, + {"Principal.Type.", "Principal Type:"}, + {"Principal.Name.", "Principal Name:"}, + {"Target.Name.", + "Target Name: "}, + {"Actions.", + "Actions: "}, + {"OK.to.overwrite.existing.file.filename.", + "OK to overwrite existing file {0}?"}, + {"Cancel", "Cancel"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "&Add Principal"}, + {"Edit.Principal", "&Edit Principal"}, + {"Remove.Principal", "&Remove Principal"}, + {"Principals.", "&Principals:"}, + {".Add.Permission", " A&dd Permission"}, + {".Edit.Permission", " Ed&it Permission"}, + {"Remove.Permission", "Re&move Permission"}, + {"Done", "Done"}, + {"KeyStore.URL.", "KeyStore &URL:"}, + {"KeyStore.Type.", "KeyStore &Type:"}, + {"KeyStore.Provider.", "KeyStore &Provider:"}, + {"KeyStore.Password.URL.", "KeyStore Pass&word URL:"}, + {"Principals", "Principals"}, + {".Edit.Principal.", " Edit Principal:"}, + {".Add.New.Principal.", " Add New Principal:"}, + {"Permissions", "Permissions"}, + {".Edit.Permission.", " Edit Permission:"}, + {".Add.New.Permission.", " Add New Permission:"}, + {"Signed.By.", "Signed By:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "Cannot Specify Principal with a Wildcard Class without a Wildcard Name"}, + {"Cannot.Specify.Principal.without.a.Name", + "Cannot Specify Principal without a Name"}, + {"Permission.and.Target.Name.must.have.a.value", + "Permission and Target Name must have a value"}, + {"Remove.this.Policy.Entry.", "Remove this Policy Entry?"}, + {"Overwrite.File", "Overwrite File"}, + {"Policy.successfully.written.to.filename", + "Policy successfully written to {0}"}, + {"null.filename", "null filename"}, + {"Save.changes.", "Save changes?"}, + {"Yes", "&Yes"}, + {"No", "&No"}, + {"Policy.Entry", "Policy Entry"}, + {"Save.Changes", "Save Changes"}, + {"No.Policy.Entry.selected", "No Policy Entry selected"}, + {"Unable.to.open.KeyStore.ex.toString.", + "Unable to open KeyStore: {0}"}, + {"No.principal.selected", "No principal selected"}, + {"No.permission.selected", "No permission selected"}, + {"name", "name"}, + {"configuration.type", "configuration type"}, + {"environment.variable.name", "environment variable name"}, + {"library.name", "library name"}, + {"package.name", "package name"}, + {"policy.type", "policy type"}, + {"property.name", "property name"}, + {"provider.name", "provider name"}, + {"url", "url"}, + {"method.list", "method list"}, + {"request.headers.list", "request headers list"}, + {"Principal.List", "Principal List"}, + {"Permission.List", "Permission List"}, + {"Code.Base", "Code Base"}, + {"KeyStore.U.R.L.", "KeyStore U R L:"}, + {"KeyStore.Password.U.R.L.", "KeyStore Password U R L:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_de.java b/src/sun/security/tools/policytool/Resources_de.java new file mode 100644 index 00000000..4e5c923b --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_de.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_de extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Warnung: Kein Public Key f\u00FCr Alias {0} vorhanden. Vergewissern Sie sich, dass der KeyStore ordnungsgem\u00E4\u00DF konfiguriert ist."}, + {"Warning.Class.not.found.class", "Warnung: Klasse nicht gefunden: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Warnung: Ung\u00FCltige(s) Argument(e) f\u00FCr Constructor: {0}"}, + {"Illegal.Principal.Type.type", "Ung\u00FCltiger Principal-Typ: {0}"}, + {"Illegal.option.option", "Ung\u00FCltige Option: {0}"}, + {"Usage.policytool.options.", "Verwendung: policytool [Optionen]"}, + {".file.file.policy.file.location", + " [-file ] Policy-Dateiverzeichnis"}, + {"New", "&Neu"}, + {"Open", "&\u00D6ffnen..."}, + {"Save", "&Speichern"}, + {"Save.As", "Speichern &unter..."}, + {"View.Warning.Log", "&Warnungslog anzeigen"}, + {"Exit", "B&eenden"}, + {"Add.Policy.Entry", "Policy-Eintrag &hinzuf\u00FCgen"}, + {"Edit.Policy.Entry", "Policy-Eintrag &bearbeiten"}, + {"Remove.Policy.Entry", "Policy-Eintrag &entfernen"}, + {"Edit", "&Bearbeiten"}, + {"Retain", "Beibehalten"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Warnung: M\u00F6glicherweise enth\u00E4lt der Dateiname Escapezeichen mit Backslash. Es ist nicht notwendig, Backslash-Zeichen zu escapen (das Tool f\u00FChrt dies automatisch beim Schreiben des Policy-Contents in den persistenten Speicher aus).\n\nKlicken Sie auf \"Beibehalten\", um den eingegebenen Namen beizubehalten oder auf \"Bearbeiten\", um den Namen zu bearbeiten."}, + + {"Add.Public.Key.Alias", "Public Key-Alias hinzuf\u00FCgen"}, + {"Remove.Public.Key.Alias", "Public Key-Alias entfernen"}, + {"File", "&Datei"}, + {"KeyStore", "&KeyStore"}, + {"Policy.File.", "Policy-Datei:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "Policy-Datei konnte nicht ge\u00F6ffnet werden: {0}: {1}"}, + {"Policy.Tool", "Policy-Tool"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Beim \u00D6ffnen der Policy-Konfiguration sind Fehler aufgetreten. Weitere Informationen finden Sie im Warnungslog."}, + {"Error", "Fehler"}, + {"OK", "OK"}, + {"Status", "Status"}, + {"Warning", "Warnung"}, + {"Permission.", + "Berechtigung: "}, + {"Principal.Type.", "Principal-Typ:"}, + {"Principal.Name.", "Principal-Name:"}, + {"Target.Name.", + "Zielname: "}, + {"Actions.", + "Aktionen: "}, + {"OK.to.overwrite.existing.file.filename.", + "Vorhandene Datei {0} \u00FCberschreiben?"}, + {"Cancel", "Abbrechen"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "Principal &hinzuf\u00FCgen"}, + {"Edit.Principal", "Principal &bearbeiten"}, + {"Remove.Principal", "Principal ent&fernen"}, + {"Principals.", "&Principals:"}, + {".Add.Permission", " B&erechtigung hinzuf\u00FCgen"}, + {".Edit.Permission", " Be&rechtigung bearbeiten"}, + {"Remove.Permission", "Berec&htigung entfernen"}, + {"Done", "Fertig"}, + {"KeyStore.URL.", "KeyStore-&URL:"}, + {"KeyStore.Type.", "KeyStore-&Typ:"}, + {"KeyStore.Provider.", "KeyStore-&Provider:"}, + {"KeyStore.Password.URL.", "KeyStore-Kenn&wort-URL:"}, + {"Principals", "Principals"}, + {".Edit.Principal.", " Principal bearbeiten:"}, + {".Add.New.Principal.", " Neuen Principal hinzuf\u00FCgen:"}, + {"Permissions", "Berechtigungen"}, + {".Edit.Permission.", " Berechtigung bearbeiten:"}, + {".Add.New.Permission.", " Neue Berechtigung hinzuf\u00FCgen:"}, + {"Signed.By.", "Signiert von:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "Principal kann nicht mit einer Platzhalterklasse ohne Platzhalternamen angegeben werden"}, + {"Cannot.Specify.Principal.without.a.Name", + "Principal kann nicht ohne einen Namen angegeben werden"}, + {"Permission.and.Target.Name.must.have.a.value", + "Berechtigung und Zielname m\u00FCssen einen Wert haben"}, + {"Remove.this.Policy.Entry.", "Diesen Policy-Eintrag entfernen?"}, + {"Overwrite.File", "Datei \u00FCberschreiben"}, + {"Policy.successfully.written.to.filename", + "Policy erfolgreich in {0} geschrieben"}, + {"null.filename", "Null-Dateiname"}, + {"Save.changes.", "\u00C4nderungen speichern?"}, + {"Yes", "&Ja"}, + {"No", "&Nein"}, + {"Policy.Entry", "Policy-Eintrag"}, + {"Save.Changes", "\u00C4nderungen speichern"}, + {"No.Policy.Entry.selected", "Kein Policy-Eintrag ausgew\u00E4hlt"}, + {"Unable.to.open.KeyStore.ex.toString.", + "KeyStore kann nicht ge\u00F6ffnet werden: {0}"}, + {"No.principal.selected", "Kein Principal ausgew\u00E4hlt"}, + {"No.permission.selected", "Keine Berechtigung ausgew\u00E4hlt"}, + {"name", "Name"}, + {"configuration.type", "Konfigurationstyp"}, + {"environment.variable.name", "Umgebungsvariablenname"}, + {"library.name", "Library-Name"}, + {"package.name", "Packagename"}, + {"policy.type", "Policy-Typ"}, + {"property.name", "Eigenschaftsname"}, + {"provider.name", "Providername"}, + {"url", "URL"}, + {"method.list", "Methodenliste"}, + {"request.headers.list", "Headerliste anfordern"}, + {"Principal.List", "Principal-Liste"}, + {"Permission.List", "Berechtigungsliste"}, + {"Code.Base", "Codebase"}, + {"KeyStore.U.R.L.", "KeyStore-URL:"}, + {"KeyStore.Password.U.R.L.", "KeyStore-Kennwort-URL:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_es.java b/src/sun/security/tools/policytool/Resources_es.java new file mode 100644 index 00000000..2ecd3b59 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_es.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_es extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Advertencia: no hay clave p\u00FAblica para el alias {0}. Aseg\u00FArese de que se ha configurado correctamente un almac\u00E9n de claves."}, + {"Warning.Class.not.found.class", "Advertencia: no se ha encontrado la clase: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Advertencia: argumento(s) no v\u00E1lido(s) para el constructor: {0}"}, + {"Illegal.Principal.Type.type", "Tipo de principal no permitido: {0}"}, + {"Illegal.option.option", "Opci\u00F3n no permitida: {0}"}, + {"Usage.policytool.options.", "Sintaxis: policytool [opciones]"}, + {".file.file.policy.file.location", + " [-file ] ubicaci\u00F3n del archivo de normas"}, + {"New", "&Nueva"}, + {"Open", "&Abrir..."}, + {"Save", "&Guardar"}, + {"Save.As", "Guardar &como..."}, + {"View.Warning.Log", "Ver &log de advertencias"}, + {"Exit", "&Salir"}, + {"Add.Policy.Entry", "Agregar &entrada de pol\u00EDtica"}, + {"Edit.Policy.Entry", "E&ditar entrada de pol\u00EDtica"}, + {"Remove.Policy.Entry", "&Eliminar entrada de pol\u00EDtica"}, + {"Edit", "&Editar"}, + {"Retain", "Mantener"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Advertencia: el nombre del archivo puede contener caracteres de barra invertida de escape. No es necesario utilizar barras invertidas de escape (la herramienta aplica caracteres de escape seg\u00FAn sea necesario al escribir el contenido de las pol\u00EDticas en el almac\u00E9n persistente).\n\nHaga clic en Mantener para conservar el nombre introducido o en Editar para modificarlo."}, + + {"Add.Public.Key.Alias", "Agregar Alias de Clave P\u00FAblico"}, + {"Remove.Public.Key.Alias", "Eliminar Alias de Clave P\u00FAblico"}, + {"File", "&Archivo"}, + {"KeyStore", "Al&mac\u00E9n de claves"}, + {"Policy.File.", "Archivo de Pol\u00EDtica:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "No se ha podido abrir el archivo de pol\u00EDtica: {0}: {1}"}, + {"Policy.Tool", "Herramienta de Pol\u00EDticas"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Ha habido errores al abrir la configuraci\u00F3n de pol\u00EDticas. V\u00E9ase el log de advertencias para obtener m\u00E1s informaci\u00F3n."}, + {"Error", "Error"}, + {"OK", "Aceptar"}, + {"Status", "Estado"}, + {"Warning", "Advertencia"}, + {"Permission.", + "Permiso: "}, + {"Principal.Type.", "Tipo de Principal:"}, + {"Principal.Name.", "Nombre de Principal:"}, + {"Target.Name.", + "Nombre de Destino: "}, + {"Actions.", + "Acciones: "}, + {"OK.to.overwrite.existing.file.filename.", + "\u00BFSobrescribir el archivo existente {0}?"}, + {"Cancel", "Cancelar"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "&Agregar principal"}, + {"Edit.Principal", "&Editar principal"}, + {"Remove.Principal", "Elimina&r principal"}, + {"Principals.", "&Principales:"}, + {".Add.Permission", "A&gregar permiso"}, + {".Edit.Permission", "Ed&itar permiso"}, + {"Remove.Permission", "Eli&minar permiso"}, + {"Done", "Listo"}, + {"KeyStore.URL.", "&URL de almac\u00E9n de claves:"}, + {"KeyStore.Type.", "&Tipo de almac\u00E9n de claves:"}, + {"KeyStore.Provider.", "&Proveedor de almac\u00E9n de claves:"}, + {"KeyStore.Password.URL.", "URL de Contra&se\u00F1a de almac\u00E9n de claves:"}, + {"Principals", "Principales"}, + {".Edit.Principal.", " Editar Principal:"}, + {".Add.New.Principal.", " Agregar Nuevo Principal:"}, + {"Permissions", "Permisos"}, + {".Edit.Permission.", " Editar Permiso:"}, + {".Add.New.Permission.", " Agregar Permiso Nuevo:"}, + {"Signed.By.", "Firmado Por:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "No se puede especificar un principal con una clase de comod\u00EDn sin un nombre de comod\u00EDn"}, + {"Cannot.Specify.Principal.without.a.Name", + "No se puede especificar el principal sin un nombre"}, + {"Permission.and.Target.Name.must.have.a.value", + "Permiso y Nombre de Destino deben tener un valor"}, + {"Remove.this.Policy.Entry.", "\u00BFEliminar esta entrada de pol\u00EDtica?"}, + {"Overwrite.File", "Sobrescribir Archivo"}, + {"Policy.successfully.written.to.filename", + "Pol\u00EDtica escrita correctamente en {0}"}, + {"null.filename", "nombre de archivo nulo"}, + {"Save.changes.", "\u00BFGuardar los cambios?"}, + {"Yes", "&S\u00ED"}, + {"No", "&No"}, + {"Policy.Entry", "Entrada de Pol\u00EDtica"}, + {"Save.Changes", "Guardar Cambios"}, + {"No.Policy.Entry.selected", "No se ha seleccionado la entrada de pol\u00EDtica"}, + {"Unable.to.open.KeyStore.ex.toString.", + "No se ha podido abrir el almac\u00E9n de claves: {0}"}, + {"No.principal.selected", "No se ha seleccionado un principal"}, + {"No.permission.selected", "No se ha seleccionado un permiso"}, + {"name", "nombre"}, + {"configuration.type", "tipo de configuraci\u00F3n"}, + {"environment.variable.name", "nombre de variable de entorno"}, + {"library.name", "nombre de la biblioteca"}, + {"package.name", "nombre del paquete"}, + {"policy.type", "tipo de pol\u00EDtica"}, + {"property.name", "nombre de la propiedad"}, + {"provider.name", "nombre del proveedor"}, + {"url", "url"}, + {"method.list", "lista de m\u00E9todos"}, + {"request.headers.list", "lista de cabeceras de solicitudes"}, + {"Principal.List", "Lista de Principales"}, + {"Permission.List", "Lista de Permisos"}, + {"Code.Base", "Base de C\u00F3digo"}, + {"KeyStore.U.R.L.", "URL de Almac\u00E9n de Claves:"}, + {"KeyStore.Password.U.R.L.", "URL de Contrase\u00F1a de Almac\u00E9n de Claves:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_fr.java b/src/sun/security/tools/policytool/Resources_fr.java new file mode 100644 index 00000000..158aa513 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_fr.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_fr extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Avertissement\u00A0: il n''existe pas de cl\u00E9 publique pour l''alias {0}. V\u00E9rifiez que le fichier de cl\u00E9s d''acc\u00E8s est correctement configur\u00E9."}, + {"Warning.Class.not.found.class", "Avertissement : classe introuvable - {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Avertissement\u00A0: arguments non valides pour le constructeur\u00A0- {0}"}, + {"Illegal.Principal.Type.type", "Type de principal non admis : {0}"}, + {"Illegal.option.option", "Option non admise : {0}"}, + {"Usage.policytool.options.", "Syntaxe : policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] emplacement du fichier de r\u00E8gles"}, + {"New", "&Nouveau"}, + {"Open", "&Ouvrir..."}, + {"Save", "Enregi&strer"}, + {"Save.As", "Enregistrer so&us..."}, + {"View.Warning.Log", "Affic&her le journal des avertissements"}, + {"Exit", "&Quitter"}, + {"Add.Policy.Entry", "&Ajouter une r\u00E8gle"}, + {"Edit.Policy.Entry", "Modifi&er une r\u00E8gle"}, + {"Remove.Policy.Entry", "Enlever une &r\u00E8gle"}, + {"Edit", "Modifi&er"}, + {"Retain", "Conserver"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Avertissement : il se peut que le nom de fichier contienne des barres obliques inverses avec caract\u00E8re d'\u00E9chappement. Il n'est pas n\u00E9cessaire d'ajouter un caract\u00E8re d'\u00E9chappement aux barres obliques inverses. L'outil proc\u00E8de \u00E0 l'\u00E9chappement si n\u00E9cessaire lorsqu'il \u00E9crit le contenu des r\u00E8gles dans la zone de stockage persistant.\n\nCliquez sur Conserver pour garder le nom saisi ou sur Modifier pour le remplacer."}, + + {"Add.Public.Key.Alias", "Ajouter un alias de cl\u00E9 publique"}, + {"Remove.Public.Key.Alias", "Enlever un alias de cl\u00E9 publique"}, + {"File", "&Fichier"}, + {"KeyStore", "Fichier &de cl\u00E9s"}, + {"Policy.File.", "Fichier de r\u00E8gles :"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "Impossible d''ouvrir le fichier de r\u00E8gles\u00A0: {0}: {1}"}, + {"Policy.Tool", "Policy Tool"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Des erreurs se sont produites \u00E0 l'ouverture de la configuration de r\u00E8gles. Pour plus d'informations, consultez le journal des avertissements."}, + {"Error", "Erreur"}, + {"OK", "OK"}, + {"Status", "Statut"}, + {"Warning", "Avertissement"}, + {"Permission.", + "Droit : "}, + {"Principal.Type.", "Type de principal :"}, + {"Principal.Name.", "Nom de principal :"}, + {"Target.Name.", + "Nom de cible : "}, + {"Actions.", + "Actions : "}, + {"OK.to.overwrite.existing.file.filename.", + "Remplacer le fichier existant {0} ?"}, + {"Cancel", "Annuler"}, + {"CodeBase.", "Base de &code :"}, + {"SignedBy.", "&Sign\u00E9 par :"}, + {"Add.Principal", "&Ajouter un principal"}, + {"Edit.Principal", "Modifi&er un principal"}, + {"Remove.Principal", "Enleve&r un principal"}, + {"Principals.", "&Principaux :"}, + {".Add.Permission", " Ajouter un &droit"}, + {".Edit.Permission", " Mod&ifier un droit"}, + {"Remove.Permission", "Enlever un dr&oit"}, + {"Done", "Termin\u00E9"}, + {"KeyStore.URL.", "&URL du fichier de cl\u00E9s :"}, + {"KeyStore.Type.", "&Type du fichier de cl\u00E9s :"}, + {"KeyStore.Provider.", "Four&nisseur du fichier de cl\u00E9s :"}, + {"KeyStore.Password.URL.", "UR&L du mot de passe du fichier de cl\u00E9s :"}, + {"Principals", "Principaux"}, + {".Edit.Principal.", " Modifier un principal :"}, + {".Add.New.Principal.", " Ajouter un principal :"}, + {"Permissions", "Droits"}, + {".Edit.Permission.", " Modifier un droit :"}, + {".Add.New.Permission.", " Ajouter un droit :"}, + {"Signed.By.", "Sign\u00E9 par :"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "Impossible de sp\u00E9cifier un principal avec une classe g\u00E9n\u00E9rique sans nom g\u00E9n\u00E9rique"}, + {"Cannot.Specify.Principal.without.a.Name", + "Impossible de sp\u00E9cifier un principal sans nom"}, + {"Permission.and.Target.Name.must.have.a.value", + "Le droit et le nom de cible doivent avoir une valeur"}, + {"Remove.this.Policy.Entry.", "Enlever cette r\u00E8gle ?"}, + {"Overwrite.File", "Remplacer le fichier"}, + {"Policy.successfully.written.to.filename", + "R\u00E8gle \u00E9crite dans {0}"}, + {"null.filename", "nom de fichier NULL"}, + {"Save.changes.", "Enregistrer les modifications ?"}, + {"Yes", "&Oui"}, + {"No", "&Non"}, + {"Policy.Entry", "R\u00E8gle"}, + {"Save.Changes", "Enregistrer les modifications"}, + {"No.Policy.Entry.selected", "Aucune r\u00E8gle s\u00E9lectionn\u00E9e"}, + {"Unable.to.open.KeyStore.ex.toString.", + "Impossible d''ouvrir le fichier de cl\u00E9s d''acc\u00E8s : {0}"}, + {"No.principal.selected", "Aucun principal s\u00E9lectionn\u00E9"}, + {"No.permission.selected", "Aucun droit s\u00E9lectionn\u00E9"}, + {"name", "nom"}, + {"configuration.type", "type de configuration"}, + {"environment.variable.name", "Nom de variable d'environnement"}, + {"library.name", "nom de biblioth\u00E8que"}, + {"package.name", "nom de package"}, + {"policy.type", "type de r\u00E8gle"}, + {"property.name", "nom de propri\u00E9t\u00E9"}, + {"provider.name", "nom du fournisseur"}, + {"url", "url"}, + {"method.list", "liste des m\u00E9thodes"}, + {"request.headers.list", "liste des en-t\u00EAtes de demande"}, + {"Principal.List", "Liste de principaux"}, + {"Permission.List", "Liste de droits"}, + {"Code.Base", "Base de code"}, + {"KeyStore.U.R.L.", "URL du fichier de cl\u00E9s :"}, + {"KeyStore.Password.U.R.L.", "URL du mot de passe du fichier de cl\u00E9s :"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_it.java b/src/sun/security/tools/policytool/Resources_it.java new file mode 100644 index 00000000..12d7ce27 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_it.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_it extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Avvertenza: non esiste una chiave pubblica per l''alias {0}. Verificare che il keystore sia configurato correttamente."}, + {"Warning.Class.not.found.class", "Avvertenza: classe non trovata: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Avvertenza: argomento o argomenti non validi per il costruttore {0}"}, + {"Illegal.Principal.Type.type", "Tipo principal non valido: {0}"}, + {"Illegal.option.option", "Opzione non valida: {0}"}, + {"Usage.policytool.options.", "Uso: policytool [opzioni]"}, + {".file.file.policy.file.location", + " [-file ] posizione del file dei criteri"}, + {"New", "&Nuovo"}, + {"Open", "&Apri..."}, + {"Save", "&Salva"}, + {"Save.As", "Sal&va con nome..."}, + {"View.Warning.Log", "Visuali&zza registro avvertenze"}, + {"Exit", "&Esci"}, + {"Add.Policy.Entry", "Aggi&ungi voce criteri"}, + {"Edit.Policy.Entry", "&Modifica voce criteri"}, + {"Remove.Policy.Entry", "&Rimuovi voce criteri"}, + {"Edit", "&Modifica"}, + {"Retain", "Mantieni"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Avvertenza: il nome file pu\u00F2 includere barre rovesciate con escape. Non \u00E8 necessario eseguire l'escape delle barre rovesciate (se necessario lo strumento esegue l'escape dei caratteri al momento della scrittura del contenuto dei criteri nell'area di memorizzazione persistente).\n\nFare click su Mantieni per conservare il nome immesso, oppure su Modifica per modificare il nome."}, + + {"Add.Public.Key.Alias", "Aggiungi alias chiave pubblica"}, + {"Remove.Public.Key.Alias", "Rimuovi alias chiave pubblica"}, + {"File", "&File"}, + {"KeyStore", "&Keystore"}, + {"Policy.File.", "File dei criteri:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "Impossibile aprire il file di criteri {0}: {1}"}, + {"Policy.Tool", "Strumento criteri"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Si sono verificati errori durante l'apertura della configurazione dei criteri. Consultare il registro delle avvertenze per ulteriori informazioni."}, + {"Error", "Errore"}, + {"OK", "OK"}, + {"Status", "Stato"}, + {"Warning", "Avvertenza"}, + {"Permission.", + "Autorizzazione: "}, + {"Principal.Type.", "Tipo principal:"}, + {"Principal.Name.", "Nome principal:"}, + {"Target.Name.", + "Nome destinazione: "}, + {"Actions.", + "Azioni: "}, + {"OK.to.overwrite.existing.file.filename.", + "OK per sovrascrivere il file {0}?"}, + {"Cancel", "Annulla"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "&Aggiungi principal"}, + {"Edit.Principal", "&Modifica principal"}, + {"Remove.Principal", "&Rimuovi principal"}, + {"Principals.", "&Principal:"}, + {".Add.Permission", " Aggiu&ngi autorizzazione"}, + {".Edit.Permission", " Mo&difica autorizzazione"}, + {"Remove.Permission", "R&imuovi autorizzazione"}, + {"Done", "Fine"}, + {"KeyStore.URL.", "&URL keystore:"}, + {"KeyStore.Type.", "&Tipo keystore:"}, + {"KeyStore.Provider.", "Pro&vider keystore:"}, + {"KeyStore.Password.URL.", "URL pass&word keystore:"}, + {"Principals", "Principal:"}, + {".Edit.Principal.", " Modifica principal:"}, + {".Add.New.Principal.", " Aggiungi nuovo principal:"}, + {"Permissions", "Autorizzazioni"}, + {".Edit.Permission.", " Modifica autorizzazione:"}, + {".Add.New.Permission.", " Aggiungi nuova autorizzazione:"}, + {"Signed.By.", "Firmato da:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "Impossibile specificare principal con una classe carattere jolly senza un nome carattere jolly"}, + {"Cannot.Specify.Principal.without.a.Name", + "Impossibile specificare principal senza un nome"}, + {"Permission.and.Target.Name.must.have.a.value", + "L'autorizzazione e il nome destinazione non possono essere nulli"}, + {"Remove.this.Policy.Entry.", "Rimuovere questa voce dei criteri?"}, + {"Overwrite.File", "Sovrascrivi file"}, + {"Policy.successfully.written.to.filename", + "I criteri sono stati scritti in {0}"}, + {"null.filename", "nome file nullo"}, + {"Save.changes.", "Salvare le modifiche?"}, + {"Yes", "&S\u00EC"}, + {"No", "&No"}, + {"Policy.Entry", "Voce dei criteri"}, + {"Save.Changes", "Salva le modifiche"}, + {"No.Policy.Entry.selected", "Nessuna voce dei criteri selezionata"}, + {"Unable.to.open.KeyStore.ex.toString.", + "Impossibile aprire il keystore: {0}"}, + {"No.principal.selected", "Nessun principal selezionato"}, + {"No.permission.selected", "Nessuna autorizzazione selezionata"}, + {"name", "nome"}, + {"configuration.type", "tipo di configurazione"}, + {"environment.variable.name", "nome variabile ambiente"}, + {"library.name", "nome libreria"}, + {"package.name", "nome package"}, + {"policy.type", "tipo di criteri"}, + {"property.name", "nome propriet\u00E0"}, + {"provider.name", "nome provider"}, + {"url", "url"}, + {"method.list", "lista metodi"}, + {"request.headers.list", "lista intestazioni di richiesta"}, + {"Principal.List", "Lista principal"}, + {"Permission.List", "Lista autorizzazioni"}, + {"Code.Base", "Codebase"}, + {"KeyStore.U.R.L.", "URL keystore:"}, + {"KeyStore.Password.U.R.L.", "URL password keystore:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_ja.java b/src/sun/security/tools/policytool/Resources_ja.java new file mode 100644 index 00000000..479c08ac --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_ja.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_ja extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "\u8B66\u544A: \u5225\u540D{0}\u306E\u516C\u958B\u9375\u304C\u5B58\u5728\u3057\u307E\u305B\u3093\u3002\u30AD\u30FC\u30B9\u30C8\u30A2\u304C\u6B63\u3057\u304F\u69CB\u6210\u3055\u308C\u3066\u3044\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"Warning.Class.not.found.class", "\u8B66\u544A: \u30AF\u30E9\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "\u8B66\u544A: \u30B3\u30F3\u30B9\u30C8\u30E9\u30AF\u30BF\u306E\u5F15\u6570\u304C\u7121\u52B9\u3067\u3059: {0}"}, + {"Illegal.Principal.Type.type", "\u4E0D\u6B63\u306A\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u30BF\u30A4\u30D7: {0}"}, + {"Illegal.option.option", "\u4E0D\u6B63\u306A\u30AA\u30D7\u30B7\u30E7\u30F3: {0}"}, + {"Usage.policytool.options.", "\u4F7F\u7528\u65B9\u6CD5: policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] \u30DD\u30EA\u30B7\u30FC\u30FB\u30D5\u30A1\u30A4\u30EB\u306E\u5834\u6240"}, + {"New", "\u65B0\u898F(&N)"}, + {"Open", "\u958B\u304F(&O)..."}, + {"Save", "\u4FDD\u5B58(&S)"}, + {"Save.As", "\u5225\u540D\u4FDD\u5B58(&A)..."}, + {"View.Warning.Log", "\u8B66\u544A\u30ED\u30B0\u306E\u8868\u793A(&W)"}, + {"Exit", "\u7D42\u4E86(&X)"}, + {"Add.Policy.Entry", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA\u306E\u8FFD\u52A0(&A)"}, + {"Edit.Policy.Entry", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA\u306E\u7DE8\u96C6(&E)"}, + {"Remove.Policy.Entry", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA\u306E\u524A\u9664(&R)"}, + {"Edit", "\u7DE8\u96C6(&E)"}, + {"Retain", "\u4FDD\u6301"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "\u8B66\u544A: \u30D5\u30A1\u30A4\u30EB\u540D\u306B\u30A8\u30B9\u30B1\u30FC\u30D7\u3055\u308C\u305F\u30D0\u30C3\u30AF\u30B9\u30E9\u30C3\u30B7\u30E5\u6587\u5B57\u304C\u542B\u307E\u308C\u3066\u3044\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u30D0\u30C3\u30AF\u30B9\u30E9\u30C3\u30B7\u30E5\u6587\u5B57\u3092\u30A8\u30B9\u30B1\u30FC\u30D7\u3059\u308B\u5FC5\u8981\u306F\u3042\u308A\u307E\u305B\u3093(\u30C4\u30FC\u30EB\u306F\u30DD\u30EA\u30B7\u30FC\u5185\u5BB9\u3092\u6C38\u7D9A\u30B9\u30C8\u30A2\u306B\u66F8\u304D\u8FBC\u3080\u3068\u304D\u306B\u3001\u5FC5\u8981\u306B\u5FDC\u3058\u3066\u6587\u5B57\u3092\u30A8\u30B9\u30B1\u30FC\u30D7\u3057\u307E\u3059)\u3002\n\n\u5165\u529B\u6E08\u306E\u540D\u524D\u3092\u4FDD\u6301\u3059\u308B\u306B\u306F\u300C\u4FDD\u6301\u300D\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3001\u540D\u524D\u3092\u7DE8\u96C6\u3059\u308B\u306B\u306F\u300C\u7DE8\u96C6\u300D\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + + {"Add.Public.Key.Alias", "\u516C\u958B\u9375\u306E\u5225\u540D\u306E\u8FFD\u52A0"}, + {"Remove.Public.Key.Alias", "\u516C\u958B\u9375\u306E\u5225\u540D\u3092\u524A\u9664"}, + {"File", "\u30D5\u30A1\u30A4\u30EB(&F)"}, + {"KeyStore", "\u30AD\u30FC\u30B9\u30C8\u30A2(&K)"}, + {"Policy.File.", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30D5\u30A1\u30A4\u30EB:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "\u30DD\u30EA\u30B7\u30FC\u30FB\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u3051\u307E\u305B\u3093\u3067\u3057\u305F: {0}: {1}"}, + {"Policy.Tool", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30C4\u30FC\u30EB"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "\u30DD\u30EA\u30B7\u30FC\u69CB\u6210\u3092\u958B\u304F\u3068\u304D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002\u8A73\u7D30\u306F\u8B66\u544A\u30ED\u30B0\u3092\u53C2\u7167\u3057\u3066\u304F\u3060\u3055\u3044\u3002"}, + {"Error", "\u30A8\u30E9\u30FC"}, + {"OK", "OK"}, + {"Status", "\u72B6\u614B"}, + {"Warning", "\u8B66\u544A"}, + {"Permission.", + "\u30A2\u30AF\u30BB\u30B9\u6A29: "}, + {"Principal.Type.", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u30BF\u30A4\u30D7:"}, + {"Principal.Name.", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u540D\u524D:"}, + {"Target.Name.", + "\u30BF\u30FC\u30B2\u30C3\u30C8\u540D: "}, + {"Actions.", + "\u30A2\u30AF\u30B7\u30E7\u30F3: "}, + {"OK.to.overwrite.existing.file.filename.", + "\u65E2\u5B58\u306E\u30D5\u30A1\u30A4\u30EB{0}\u306B\u4E0A\u66F8\u304D\u3057\u307E\u3059\u304B\u3002"}, + {"Cancel", "\u53D6\u6D88"}, + {"CodeBase.", "CodeBase(&C):"}, + {"SignedBy.", "SignedBy(&S):"}, + {"Add.Principal", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u8FFD\u52A0(&A)"}, + {"Edit.Principal", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u7DE8\u96C6(&E)"}, + {"Remove.Principal", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u524A\u9664(&R)"}, + {"Principals.", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB(&P):"}, + {".Add.Permission", " \u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u8FFD\u52A0(&D)"}, + {".Edit.Permission", " \u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u7DE8\u96C6(&I)"}, + {"Remove.Permission", "\u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u524A\u9664(&M)"}, + {"Done", "\u5B8C\u4E86"}, + {"KeyStore.URL.", "\u30AD\u30FC\u30B9\u30C8\u30A2URL(&U):"}, + {"KeyStore.Type.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30BF\u30A4\u30D7(&T):"}, + {"KeyStore.Provider.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0(&P):"}, + {"KeyStore.Password.URL.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D1\u30B9\u30EF\u30FC\u30C9URL(&W):"}, + {"Principals", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB"}, + {".Edit.Principal.", " \u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u7DE8\u96C6:"}, + {".Add.New.Principal.", " \u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u65B0\u898F\u8FFD\u52A0:"}, + {"Permissions", "\u30A2\u30AF\u30BB\u30B9\u6A29"}, + {".Edit.Permission.", " \u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u7DE8\u96C6:"}, + {".Add.New.Permission.", " \u65B0\u898F\u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u8FFD\u52A0:"}, + {"Signed.By.", "\u7F72\u540D\u8005:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u540D\u306E\u306A\u3044\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u30FB\u30AF\u30E9\u30B9\u3092\u4F7F\u7528\u3057\u3066\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u3092\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"Cannot.Specify.Principal.without.a.Name", + "\u540D\u524D\u3092\u4F7F\u7528\u305B\u305A\u306B\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u3092\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"Permission.and.Target.Name.must.have.a.value", + "\u30A2\u30AF\u30BB\u30B9\u6A29\u3068\u30BF\u30FC\u30B2\u30C3\u30C8\u540D\u306F\u3001\u5024\u3092\u4FDD\u6301\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Remove.this.Policy.Entry.", "\u3053\u306E\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3059\u304B\u3002"}, + {"Overwrite.File", "\u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D\u3057\u307E\u3059"}, + {"Policy.successfully.written.to.filename", + "\u30DD\u30EA\u30B7\u30FC\u306E{0}\u3078\u306E\u66F8\u8FBC\u307F\u306B\u6210\u529F\u3057\u307E\u3057\u305F"}, + {"null.filename", "\u30D5\u30A1\u30A4\u30EB\u540D\u304Cnull\u3067\u3059"}, + {"Save.changes.", "\u5909\u66F4\u3092\u4FDD\u5B58\u3057\u307E\u3059\u304B\u3002"}, + {"Yes", "\u306F\u3044(&Y)"}, + {"No", "\u3044\u3044\u3048(&N)"}, + {"Policy.Entry", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA"}, + {"Save.Changes", "\u5909\u66F4\u3092\u4FDD\u5B58\u3057\u307E\u3059"}, + {"No.Policy.Entry.selected", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30A8\u30F3\u30C8\u30EA\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"Unable.to.open.KeyStore.ex.toString.", + "\u30AD\u30FC\u30B9\u30C8\u30A2{0}\u3092\u958B\u3051\u307E\u305B\u3093"}, + {"No.principal.selected", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"No.permission.selected", "\u30A2\u30AF\u30BB\u30B9\u6A29\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"name", "\u540D\u524D"}, + {"configuration.type", "\u69CB\u6210\u30BF\u30A4\u30D7"}, + {"environment.variable.name", "\u74B0\u5883\u5909\u6570\u540D"}, + {"library.name", "\u30E9\u30A4\u30D6\u30E9\u30EA\u540D"}, + {"package.name", "\u30D1\u30C3\u30B1\u30FC\u30B8\u540D"}, + {"policy.type", "\u30DD\u30EA\u30B7\u30FC\u30FB\u30BF\u30A4\u30D7"}, + {"property.name", "\u30D7\u30ED\u30D1\u30C6\u30A3\u540D"}, + {"provider.name", "\u30D7\u30ED\u30D0\u30A4\u30C0\u540D"}, + {"url", "URL"}, + {"method.list", "\u30E1\u30BD\u30C3\u30C9\u30FB\u30EA\u30B9\u30C8"}, + {"request.headers.list", "\u30EA\u30AF\u30A8\u30B9\u30C8\u30FB\u30D8\u30C3\u30C0\u30FC\u30FB\u30EA\u30B9\u30C8"}, + {"Principal.List", "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u306E\u30EA\u30B9\u30C8"}, + {"Permission.List", "\u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u30EA\u30B9\u30C8"}, + {"Code.Base", "\u30B3\u30FC\u30C9\u30FB\u30D9\u30FC\u30B9"}, + {"KeyStore.U.R.L.", "\u30AD\u30FC\u30B9\u30C8\u30A2U R L:"}, + {"KeyStore.Password.U.R.L.", "\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D1\u30B9\u30EF\u30FC\u30C9U R L:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_ko.java b/src/sun/security/tools/policytool/Resources_ko.java new file mode 100644 index 00000000..6e952870 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_ko.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_ko extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "\uACBD\uACE0: {0} \uBCC4\uCE6D\uC5D0 \uB300\uD55C \uACF5\uC6A9 \uD0A4\uAC00 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD0A4 \uC800\uC7A5\uC18C\uAC00 \uC81C\uB300\uB85C \uAD6C\uC131\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + {"Warning.Class.not.found.class", "\uACBD\uACE0: \uD074\uB798\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC74C: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "\uACBD\uACE0: \uC0DD\uC131\uC790\uC5D0 \uB300\uD574 \uBD80\uC801\uD569\uD55C \uC778\uC218: {0}"}, + {"Illegal.Principal.Type.type", "\uC798\uBABB\uB41C \uC8FC\uCCB4 \uC720\uD615: {0}"}, + {"Illegal.option.option", "\uC798\uBABB\uB41C \uC635\uC158: {0}"}, + {"Usage.policytool.options.", "\uC0AC\uC6A9\uBC95: policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] \uC815\uCC45 \uD30C\uC77C \uC704\uCE58"}, + {"New", "\uC0C8\uB85C \uB9CC\uB4E4\uAE30(&N)"}, + {"Open", "\uC5F4\uAE30(&O)..."}, + {"Save", "\uC800\uC7A5(&S)"}, + {"Save.As", "\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5(&A)..."}, + {"View.Warning.Log", "\uACBD\uACE0 \uB85C\uADF8 \uBCF4\uAE30(&W)"}, + {"Exit", "\uC885\uB8CC(&X)"}, + {"Add.Policy.Entry", "\uC815\uCC45 \uD56D\uBAA9 \uCD94\uAC00(&A)"}, + {"Edit.Policy.Entry", "\uC815\uCC45 \uD56D\uBAA9 \uD3B8\uC9D1(&E)"}, + {"Remove.Policy.Entry", "\uC815\uCC45 \uD56D\uBAA9 \uC81C\uAC70(&R)"}, + {"Edit", "\uD3B8\uC9D1(&E)"}, + {"Retain", "\uC720\uC9C0"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "\uACBD\uACE0: \uD30C\uC77C \uC774\uB984\uC5D0 \uC774\uC2A4\uCF00\uC774\uD504\uB41C \uBC31\uC2AC\uB798\uC2DC \uBB38\uC790\uAC00 \uD3EC\uD568\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uBC31\uC2AC\uB798\uC2DC \uBB38\uC790\uB294 \uC774\uC2A4\uCF00\uC774\uD504\uD560 \uD544\uC694\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uC601\uAD6C \uC800\uC7A5\uC18C\uC5D0 \uC815\uCC45 \uCF58\uD150\uCE20\uB97C \uC4F8 \uB54C \uD544\uC694\uC5D0 \uB530\uB77C \uC790\uB3D9\uC73C\uB85C \uBB38\uC790\uAC00 \uC774\uC2A4\uCF00\uC774\uD504\uB429\uB2C8\uB2E4.\n\n\uC785\uB825\uB41C \uC774\uB984\uC744 \uADF8\uB300\uB85C \uC720\uC9C0\uD558\uB824\uBA74 [\uC720\uC9C0]\uB97C \uB204\uB974\uACE0, \uC774\uB984\uC744 \uD3B8\uC9D1\uD558\uB824\uBA74 [\uD3B8\uC9D1]\uC744 \uB204\uB974\uC2ED\uC2DC\uC624."}, + + {"Add.Public.Key.Alias", "\uACF5\uC6A9 \uD0A4 \uBCC4\uCE6D \uCD94\uAC00"}, + {"Remove.Public.Key.Alias", "\uACF5\uC6A9 \uD0A4 \uBCC4\uCE6D \uC81C\uAC70"}, + {"File", "\uD30C\uC77C(&F)"}, + {"KeyStore", "\uD0A4 \uC800\uC7A5\uC18C(&K)"}, + {"Policy.File.", "\uC815\uCC45 \uD30C\uC77C:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "\uC815\uCC45 \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC74C: {0}: {1}"}, + {"Policy.Tool", "\uC815\uCC45 \uD234"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "\uC815\uCC45 \uAD6C\uC131\uC744 \uC5EC\uB294 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uC790\uC138\uD55C \uB0B4\uC6A9\uC740 \uACBD\uACE0 \uB85C\uADF8\uB97C \uD655\uC778\uD558\uC2ED\uC2DC\uC624."}, + {"Error", "\uC624\uB958"}, + {"OK", "\uD655\uC778"}, + {"Status", "\uC0C1\uD0DC"}, + {"Warning", "\uACBD\uACE0"}, + {"Permission.", + "\uAD8C\uD55C: "}, + {"Principal.Type.", "\uC8FC\uCCB4 \uC720\uD615:"}, + {"Principal.Name.", "\uC8FC\uCCB4 \uC774\uB984:"}, + {"Target.Name.", + "\uB300\uC0C1 \uC774\uB984: "}, + {"Actions.", + "\uC791\uC5C5: "}, + {"OK.to.overwrite.existing.file.filename.", + "\uAE30\uC874 \uD30C\uC77C {0}\uC744(\uB97C) \uACB9\uCCD0 \uC4F0\uACA0\uC2B5\uB2C8\uAE4C?"}, + {"Cancel", "\uCDE8\uC18C"}, + {"CodeBase.", "CodeBase(&C)"}, + {"SignedBy.", "SignedBy(&S):"}, + {"Add.Principal", "\uC8FC\uCCB4 \uCD94\uAC00(&A)"}, + {"Edit.Principal", "\uC8FC\uCCB4 \uD3B8\uC9D1(&E)"}, + {"Remove.Principal", "\uC8FC\uCCB4 \uC81C\uAC70(&R)"}, + {"Principals.", "\uC8FC\uCCB4(&P):"}, + {".Add.Permission", " \uAD8C\uD55C \uCD94\uAC00(&D)"}, + {".Edit.Permission", " \uAD8C\uD55C \uD3B8\uC9D1(&I)"}, + {"Remove.Permission", "\uAD8C\uD55C \uC81C\uAC70(&M)"}, + {"Done", "\uC644\uB8CC"}, + {"KeyStore.URL.", "\uD0A4 \uC800\uC7A5\uC18C URL(&U):"}, + {"KeyStore.Type.", "\uD0A4 \uC800\uC7A5\uC18C \uC720\uD615(&T):"}, + {"KeyStore.Provider.", "\uD0A4 \uC800\uC7A5\uC18C \uC81C\uACF5\uC790(&P):"}, + {"KeyStore.Password.URL.", "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638 URL(&W):"}, + {"Principals", "\uC8FC\uCCB4"}, + {".Edit.Principal.", " \uC8FC\uCCB4 \uD3B8\uC9D1:"}, + {".Add.New.Principal.", " \uC0C8 \uC8FC\uCCB4 \uCD94\uAC00:"}, + {"Permissions", "\uAD8C\uD55C"}, + {".Edit.Permission.", " \uAD8C\uD55C \uD3B8\uC9D1:"}, + {".Add.New.Permission.", " \uC0C8 \uAD8C\uD55C \uCD94\uAC00:"}, + {"Signed.By.", "\uC11C\uBA85\uC790:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "\uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uC774\uB984 \uC5C6\uC774 \uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uD074\uB798\uC2A4\uB97C \uC0AC\uC6A9\uD558\uB294 \uC8FC\uCCB4\uB97C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Cannot.Specify.Principal.without.a.Name", + "\uC774\uB984 \uC5C6\uC774 \uC8FC\uCCB4\uB97C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Permission.and.Target.Name.must.have.a.value", + "\uAD8C\uD55C\uACFC \uB300\uC0C1 \uC774\uB984\uC758 \uAC12\uC774 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Remove.this.Policy.Entry.", "\uC774 \uC815\uCC45 \uD56D\uBAA9\uC744 \uC81C\uAC70\uD558\uACA0\uC2B5\uB2C8\uAE4C?"}, + {"Overwrite.File", "\uD30C\uC77C \uACB9\uCCD0\uC4F0\uAE30"}, + {"Policy.successfully.written.to.filename", + "{0}\uC5D0 \uC131\uACF5\uC801\uC73C\uB85C \uC815\uCC45\uC744 \uC37C\uC2B5\uB2C8\uB2E4."}, + {"null.filename", "\uB110 \uD30C\uC77C \uC774\uB984"}, + {"Save.changes.", "\uBCC0\uACBD \uC0AC\uD56D\uC744 \uC800\uC7A5\uD558\uACA0\uC2B5\uB2C8\uAE4C?"}, + {"Yes", "\uC608(&Y)"}, + {"No", "\uC544\uB2C8\uC624(&N)"}, + {"Policy.Entry", "\uC815\uCC45 \uD56D\uBAA9"}, + {"Save.Changes", "\uBCC0\uACBD \uC0AC\uD56D \uC800\uC7A5"}, + {"No.Policy.Entry.selected", "\uC120\uD0DD\uB41C \uC815\uCC45 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Unable.to.open.KeyStore.ex.toString.", + "\uD0A4 \uC800\uC7A5\uC18C\uB97C \uC5F4 \uC218 \uC5C6\uC74C: {0}"}, + {"No.principal.selected", "\uC120\uD0DD\uB41C \uC8FC\uCCB4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"No.permission.selected", "\uC120\uD0DD\uB41C \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"name", "\uC774\uB984"}, + {"configuration.type", "\uAD6C\uC131 \uC720\uD615"}, + {"environment.variable.name", "\uD658\uACBD \uBCC0\uC218 \uC774\uB984"}, + {"library.name", "\uB77C\uC774\uBE0C\uB7EC\uB9AC \uC774\uB984"}, + {"package.name", "\uD328\uD0A4\uC9C0 \uC774\uB984"}, + {"policy.type", "\uC815\uCC45 \uC720\uD615"}, + {"property.name", "\uC18D\uC131 \uC774\uB984"}, + {"provider.name", "\uC81C\uACF5\uC790 \uC774\uB984"}, + {"url", "URL"}, + {"method.list", "\uBA54\uC18C\uB4DC \uBAA9\uB85D"}, + {"request.headers.list", "\uC694\uCCAD \uD5E4\uB354 \uBAA9\uB85D"}, + {"Principal.List", "\uC8FC\uCCB4 \uBAA9\uB85D"}, + {"Permission.List", "\uAD8C\uD55C \uBAA9\uB85D"}, + {"Code.Base", "\uCF54\uB4DC \uBCA0\uC774\uC2A4"}, + {"KeyStore.U.R.L.", "\uD0A4 \uC800\uC7A5\uC18C URL:"}, + {"KeyStore.Password.U.R.L.", "\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638 URL:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_pt_BR.java b/src/sun/security/tools/policytool/Resources_pt_BR.java new file mode 100644 index 00000000..3d683610 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_pt_BR.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_pt_BR extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Advert\u00EAncia: N\u00E3o existe uma chave p\u00FAblica para o alias {0}. Certifique-se de que um KeyStore esteja configurado adequadamente."}, + {"Warning.Class.not.found.class", "Advert\u00EAncia: Classe n\u00E3o encontrada: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Advert\u00EAncia: Argumento(s) inv\u00E1lido(s) para o construtor: {0}"}, + {"Illegal.Principal.Type.type", "Tipo Principal Inv\u00E1lido: {0}"}, + {"Illegal.option.option", "Op\u00E7\u00E3o inv\u00E1lida: {0}"}, + {"Usage.policytool.options.", "Uso: policytool [op\u00E7\u00F5es]"}, + {".file.file.policy.file.location", + " [-file ] localiza\u00E7\u00E3o do arquivo de pol\u00EDtica"}, + {"New", "&Novo"}, + {"Open", "&Abrir..."}, + {"Save", "&Salvar"}, + {"Save.As", "Salvar c&omo..."}, + {"View.Warning.Log", "E&xibir Log de Advert\u00EAncias"}, + {"Exit", "S&air"}, + {"Add.Policy.Entry", "&Adicionar Entrada de Pol\u00EDtica"}, + {"Edit.Policy.Entry", "&Editar Entrada de Pol\u00EDtica"}, + {"Remove.Policy.Entry", "&Remover Entrada de Pol\u00EDtica"}, + {"Edit", "&Editar"}, + {"Retain", "Reter"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Advert\u00EAncia: O nome do arquivo pode conter caracteres de escape barra invertida. N\u00E3o \u00E9 necess\u00E1rio fazer o escape dos caracteres de barra invertida (a ferramenta faz o escape dos caracteres conforme necess\u00E1rio ao gravar o conte\u00FAdo da pol\u00EDtica no armazenamento persistente).\n\nClique em Reter para reter o nome da entrada ou clique em Editar para edit\u00E1-lo."}, + + {"Add.Public.Key.Alias", "Adicionar Alias de Chave P\u00FAblica"}, + {"Remove.Public.Key.Alias", "Remover Alias de Chave P\u00FAblica"}, + {"File", "&Arquivo"}, + {"KeyStore", "&KeyStore"}, + {"Policy.File.", "Arquivo de Pol\u00EDtica:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "N\u00E3o foi poss\u00EDvel abrir o arquivo de pol\u00EDtica: {0}: {1}"}, + {"Policy.Tool", "Ferramenta de Pol\u00EDtica"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Erros durante a abertura da configura\u00E7\u00E3o da pol\u00EDtica. Consulte o Log de Advert\u00EAncias para obter mais informa\u00E7\u00F5es."}, + {"Error", "Erro"}, + {"OK", "OK"}, + {"Status", "Status"}, + {"Warning", "Advert\u00EAncia"}, + {"Permission.", + "Permiss\u00E3o: "}, + {"Principal.Type.", "Tipo do Principal:"}, + {"Principal.Name.", "Nome do Principal:"}, + {"Target.Name.", + "Nome do Alvo: "}, + {"Actions.", + "A\u00E7\u00F5es: "}, + {"OK.to.overwrite.existing.file.filename.", + "Est\u00E1 correto substituir o arquivo existente {0}?"}, + {"Cancel", "Cancelar"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "&Adicionar Principal"}, + {"Edit.Principal", "&Editar Principal"}, + {"Remove.Principal", "&Remover Principal"}, + {"Principals.", "&Principais:"}, + {".Add.Permission", " A&dicionar Permiss\u00E3o"}, + {".Edit.Permission", " Ed&itar Permiss\u00E3o"}, + {"Remove.Permission", "Re&mover Permiss\u00E3o"}, + {"Done", "Conclu\u00EDdo"}, + {"KeyStore.URL.", "&URL do KeyStore:"}, + {"KeyStore.Type.", "&Tipo de KeyStore:"}, + {"KeyStore.Provider.", "&Fornecedor de KeyStore:"}, + {"KeyStore.Password.URL.", "URL da Senha do Ke&yStore:"}, + {"Principals", "Principais"}, + {".Edit.Principal.", " Editar Principal:"}, + {".Add.New.Principal.", " Adicionar Novo Principal:"}, + {"Permissions", "Permiss\u00F5es"}, + {".Edit.Permission.", " Editar Permiss\u00E3o:"}, + {".Add.New.Permission.", " Adicionar Nova Permiss\u00E3o:"}, + {"Signed.By.", "Assinado por:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "N\u00E3o \u00E9 Poss\u00EDvel Especificar um Principal com uma Classe de Curinga sem um Nome de Curinga"}, + {"Cannot.Specify.Principal.without.a.Name", + "N\u00E3o \u00E9 Poss\u00EDvel Especificar um Principal sem um Nome"}, + {"Permission.and.Target.Name.must.have.a.value", + "O Nome de Destino e a Permiss\u00E3o devem ter um Valor"}, + {"Remove.this.Policy.Entry.", "Remover esta Entrada de Pol\u00EDtica?"}, + {"Overwrite.File", "Substituir Arquivo"}, + {"Policy.successfully.written.to.filename", + "Pol\u00EDtica gravada com \u00EAxito em {0}"}, + {"null.filename", "nome de arquivo nulo"}, + {"Save.changes.", "Salvar altera\u00E7\u00F5es?"}, + {"Yes", "&Sim"}, + {"No", "&N\u00E3o"}, + {"Policy.Entry", "Entrada de Pol\u00EDtica"}, + {"Save.Changes", "Salvar Altera\u00E7\u00F5es"}, + {"No.Policy.Entry.selected", "Nenhuma Entrada de Pol\u00EDtica Selecionada"}, + {"Unable.to.open.KeyStore.ex.toString.", + "N\u00E3o \u00E9 poss\u00EDvel abrir a KeyStore: {0}"}, + {"No.principal.selected", "Nenhum principal selecionado"}, + {"No.permission.selected", "Nenhuma permiss\u00E3o selecionada"}, + {"name", "nome"}, + {"configuration.type", "tipo de configura\u00E7\u00E3o"}, + {"environment.variable.name", "nome da vari\u00E1vel de ambiente"}, + {"library.name", "nome da biblioteca"}, + {"package.name", "nome do pacote"}, + {"policy.type", "tipo de pol\u00EDtica"}, + {"property.name", "nome da propriedade"}, + {"provider.name", "nome do fornecedor"}, + {"url", "url"}, + {"method.list", "lista de m\u00E9todos"}, + {"request.headers.list", "solicitar lista de cabe\u00E7alhos"}, + {"Principal.List", "Lista de Principais"}, + {"Permission.List", "Lista de Permiss\u00F5es"}, + {"Code.Base", "Base de C\u00F3digo"}, + {"KeyStore.U.R.L.", "U R L da KeyStore:"}, + {"KeyStore.Password.U.R.L.", "U R L da Senha do KeyStore:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_sv.java b/src/sun/security/tools/policytool/Resources_sv.java new file mode 100644 index 00000000..26eb8863 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_sv.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_sv extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "Varning! Det finns ingen offentlig nyckel f\u00F6r aliaset {0}. Kontrollera att det aktuella nyckellagret \u00E4r korrekt konfigurerat."}, + {"Warning.Class.not.found.class", "Varning! Klassen hittades inte: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "Varning! Ogiltiga argument f\u00F6r konstruktor: {0}"}, + {"Illegal.Principal.Type.type", "Otill\u00E5ten identitetshavaretyp: {0}"}, + {"Illegal.option.option", "Otill\u00E5tet alternativ: {0}"}, + {"Usage.policytool.options.", "Syntax: policytool [alternativ]"}, + {".file.file.policy.file.location", + " [-file ] policyfilens plats"}, + {"New", "&Nytt"}, + {"Open", "&\u00D6ppna..."}, + {"Save", "S¶"}, + {"Save.As", "Spara &som..."}, + {"View.Warning.Log", "Visa varningslo&gg"}, + {"Exit", "A&vsluta"}, + {"Add.Policy.Entry", "&L\u00E4gg till policypost"}, + {"Edit.Policy.Entry", "&Redigera policypost"}, + {"Remove.Policy.Entry", "&Ta bort policypost"}, + {"Edit", "&Redigera"}, + {"Retain", "Beh\u00E5ll"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "Varning! Filnamnet kan inneh\u00E5lla omv\u00E4nda snedstreck inom citattecken. Citattecken kr\u00E4vs inte f\u00F6r omv\u00E4nda snedstreck (verktyget hanterar detta n\u00E4r policyinneh\u00E5llet skrivs till det best\u00E4ndiga lagret).\n\nKlicka p\u00E5 Beh\u00E5ll f\u00F6r att beh\u00E5lla det angivna namnet, eller klicka p\u00E5 Redigera f\u00F6r att \u00E4ndra det."}, + + {"Add.Public.Key.Alias", "L\u00E4gg till offentligt nyckelalias"}, + {"Remove.Public.Key.Alias", "Ta bort offentligt nyckelalias"}, + {"File", "&Arkiv"}, + {"KeyStore", "&KeyStore"}, + {"Policy.File.", "Policyfil:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "Kan inte \u00F6ppna policyfilen: {0}: {1}"}, + {"Policy.Tool", "Policyverktyg"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "Det uppstod ett fel n\u00E4r policykonfigurationen skulle \u00F6ppnas. Se varningsloggen f\u00F6r mer information."}, + {"Error", "Fel"}, + {"OK", "OK"}, + {"Status", "Status"}, + {"Warning", "Varning"}, + {"Permission.", + "Beh\u00F6righet: "}, + {"Principal.Type.", "Identitetshavaretyp:"}, + {"Principal.Name.", "Identitetshavare:"}, + {"Target.Name.", + "M\u00E5l: "}, + {"Actions.", + "Funktioner: "}, + {"OK.to.overwrite.existing.file.filename.", + "Ska den befintliga filen {0} skrivas \u00F6ver?"}, + {"Cancel", "Avbryt"}, + {"CodeBase.", "&CodeBase:"}, + {"SignedBy.", "&SignedBy:"}, + {"Add.Principal", "&L\u00E4gg till identitetshavare"}, + {"Edit.Principal", "&Redigera identitetshavare"}, + {"Remove.Principal", "&Ta bort identitetshavare"}, + {"Principals.", "Identitetshavare:"}, + {".Add.Permission", " L&\u00E4gg till beh\u00F6righet"}, + {".Edit.Permission", " Re&digera beh\u00F6righet"}, + {"Remove.Permission", "Ta &bort beh\u00F6righet"}, + {"Done", "Utf\u00F6rd"}, + {"KeyStore.URL.", "KeyStore-&URL:"}, + {"KeyStore.Type.", "KeyStore-&typ:"}, + {"KeyStore.Provider.", "KeyStore-&leverant\u00F6r:"}, + {"KeyStore.Password.URL.", "KeyStore-l\u00F6sen&ords-URL:"}, + {"Principals", "Identitetshavare"}, + {".Edit.Principal.", " Redigera identitetshavare:"}, + {".Add.New.Principal.", " L\u00E4gg till ny identitetshavare:"}, + {"Permissions", "Beh\u00F6righet"}, + {".Edit.Permission.", " Redigera beh\u00F6righet:"}, + {".Add.New.Permission.", " L\u00E4gg till ny beh\u00F6righet:"}, + {"Signed.By.", "Signerad av:"}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "Kan inte specificera identitetshavare med jokerteckenklass utan jokerteckennamn"}, + {"Cannot.Specify.Principal.without.a.Name", + "Kan inte specificera identitetshavare utan namn"}, + {"Permission.and.Target.Name.must.have.a.value", + "Beh\u00F6righet och m\u00E5lnamn m\u00E5ste ha ett v\u00E4rde"}, + {"Remove.this.Policy.Entry.", "Vill du ta bort den h\u00E4r policyposten?"}, + {"Overwrite.File", "Skriv \u00F6ver fil"}, + {"Policy.successfully.written.to.filename", + "Policy har skrivits till {0}"}, + {"null.filename", "nullfilnamn"}, + {"Save.changes.", "Vill du spara \u00E4ndringarna?"}, + {"Yes", "&Ja"}, + {"No", "&Nej"}, + {"Policy.Entry", "Policyfel"}, + {"Save.Changes", "Spara \u00E4ndringar"}, + {"No.Policy.Entry.selected", "Ingen policypost har valts"}, + {"Unable.to.open.KeyStore.ex.toString.", + "Kan inte \u00F6ppna nyckellagret: {0}"}, + {"No.principal.selected", "Ingen identitetshavare har valts"}, + {"No.permission.selected", "Ingen beh\u00F6righet har valts"}, + {"name", "namn"}, + {"configuration.type", "konfigurationstyp"}, + {"environment.variable.name", "variabelnamn f\u00F6r milj\u00F6"}, + {"library.name", "biblioteksnamn"}, + {"package.name", "paketnamn"}, + {"policy.type", "policytyp"}, + {"property.name", "egenskapsnamn"}, + {"provider.name", "leverant\u00F6rsnamn"}, + {"url", "url"}, + {"method.list", "metodlista"}, + {"request.headers.list", "beg\u00E4ranrubriklista"}, + {"Principal.List", "Lista \u00F6ver identitetshavare"}, + {"Permission.List", "Beh\u00F6righetslista"}, + {"Code.Base", "Kodbas"}, + {"KeyStore.U.R.L.", "URL f\u00F6r nyckellager:"}, + {"KeyStore.Password.U.R.L.", "URL f\u00F6r l\u00F6senord till nyckellager:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_zh_CN.java b/src/sun/security/tools/policytool/Resources_zh_CN.java new file mode 100644 index 00000000..25003b86 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_zh_CN.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_zh_CN extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "\u8B66\u544A: \u522B\u540D {0} \u7684\u516C\u5171\u5BC6\u94A5\u4E0D\u5B58\u5728\u3002\u8BF7\u786E\u4FDD\u5DF2\u6B63\u786E\u914D\u7F6E\u5BC6\u94A5\u5E93\u3002"}, + {"Warning.Class.not.found.class", "\u8B66\u544A: \u627E\u4E0D\u5230\u7C7B: {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "\u8B66\u544A: \u6784\u9020\u5668\u7684\u53C2\u6570\u65E0\u6548: {0}"}, + {"Illegal.Principal.Type.type", "\u975E\u6CD5\u7684\u4E3B\u7528\u6237\u7C7B\u578B: {0}"}, + {"Illegal.option.option", "\u975E\u6CD5\u9009\u9879: {0}"}, + {"Usage.policytool.options.", "\u7528\u6CD5: policytool [\u9009\u9879]"}, + {".file.file.policy.file.location", + " [-file ] \u7B56\u7565\u6587\u4EF6\u4F4D\u7F6E"}, + {"New", "\u65B0\u5EFA(&N)"}, + {"Open", "\u6253\u5F00(&O)..."}, + {"Save", "\u4FDD\u5B58(&S)"}, + {"Save.As", "\u53E6\u5B58\u4E3A(&A)..."}, + {"View.Warning.Log", "\u67E5\u770B\u8B66\u544A\u65E5\u5FD7(&W)"}, + {"Exit", "\u9000\u51FA(&X)"}, + {"Add.Policy.Entry", "\u6DFB\u52A0\u7B56\u7565\u6761\u76EE(&A)"}, + {"Edit.Policy.Entry", "\u7F16\u8F91\u7B56\u7565\u6761\u76EE(&E)"}, + {"Remove.Policy.Entry", "\u5220\u9664\u7B56\u7565\u6761\u76EE(&R)"}, + {"Edit", "\u7F16\u8F91(&E)"}, + {"Retain", "\u4FDD\u7559"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "\u8B66\u544A: \u6587\u4EF6\u540D\u5305\u542B\u8F6C\u4E49\u7684\u53CD\u659C\u6760\u5B57\u7B26\u3002\u4E0D\u9700\u8981\u5BF9\u53CD\u659C\u6760\u5B57\u7B26\u8FDB\u884C\u8F6C\u4E49 (\u8BE5\u5DE5\u5177\u5728\u5C06\u7B56\u7565\u5185\u5BB9\u5199\u5165\u6C38\u4E45\u5B58\u50A8\u65F6\u4F1A\u6839\u636E\u9700\u8981\u5BF9\u5B57\u7B26\u8FDB\u884C\u8F6C\u4E49)\u3002\n\n\u5355\u51FB\u201C\u4FDD\u7559\u201D\u53EF\u4FDD\u7559\u8F93\u5165\u7684\u540D\u79F0, \u6216\u8005\u5355\u51FB\u201C\u7F16\u8F91\u201D\u53EF\u7F16\u8F91\u8BE5\u540D\u79F0\u3002"}, + + {"Add.Public.Key.Alias", "\u6DFB\u52A0\u516C\u5171\u5BC6\u94A5\u522B\u540D"}, + {"Remove.Public.Key.Alias", "\u5220\u9664\u516C\u5171\u5BC6\u94A5\u522B\u540D"}, + {"File", "\u6587\u4EF6(&F)"}, + {"KeyStore", "\u5BC6\u94A5\u5E93(&K)"}, + {"Policy.File.", "\u7B56\u7565\u6587\u4EF6:"}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "\u65E0\u6CD5\u6253\u5F00\u7B56\u7565\u6587\u4EF6: {0}: {1}"}, + {"Policy.Tool", "\u7B56\u7565\u5DE5\u5177"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "\u6253\u5F00\u7B56\u7565\u914D\u7F6E\u65F6\u51FA\u9519\u3002\u6709\u5173\u8BE6\u7EC6\u4FE1\u606F, \u8BF7\u67E5\u770B\u8B66\u544A\u65E5\u5FD7\u3002"}, + {"Error", "\u9519\u8BEF"}, + {"OK", "\u786E\u5B9A"}, + {"Status", "\u72B6\u6001"}, + {"Warning", "\u8B66\u544A"}, + {"Permission.", + "\u6743\u9650: "}, + {"Principal.Type.", "\u4E3B\u7528\u6237\u7C7B\u578B:"}, + {"Principal.Name.", "\u4E3B\u7528\u6237\u540D\u79F0:"}, + {"Target.Name.", + "\u76EE\u6807\u540D\u79F0: "}, + {"Actions.", + "\u64CD\u4F5C: "}, + {"OK.to.overwrite.existing.file.filename.", + "\u786E\u8BA4\u8986\u76D6\u73B0\u6709\u7684\u6587\u4EF6{0}?"}, + {"Cancel", "\u53D6\u6D88"}, + {"CodeBase.", "CodeBase(&C):"}, + {"SignedBy.", "SignedBy(&S):"}, + {"Add.Principal", "\u6DFB\u52A0\u4E3B\u7528\u6237(&A)"}, + {"Edit.Principal", "\u7F16\u8F91\u4E3B\u7528\u6237(&E)"}, + {"Remove.Principal", "\u5220\u9664\u4E3B\u7528\u6237(&R)"}, + {"Principals.", "\u4E3B\u7528\u6237(&P):"}, + {".Add.Permission", " \u6DFB\u52A0\u6743\u9650(&D)"}, + {".Edit.Permission", " \u7F16\u8F91\u6743\u9650(&I)"}, + {"Remove.Permission", "\u5220\u9664\u6743\u9650(&M)"}, + {"Done", "\u5B8C\u6210"}, + {"KeyStore.URL.", "\u5BC6\u94A5\u5E93 URL(&U):"}, + {"KeyStore.Type.", "\u5BC6\u94A5\u5E93\u7C7B\u578B(&T):"}, + {"KeyStore.Provider.", "\u5BC6\u94A5\u5E93\u63D0\u4F9B\u65B9(&P):"}, + {"KeyStore.Password.URL.", "\u5BC6\u94A5\u5E93\u53E3\u4EE4 URL(&W):"}, + {"Principals", "\u4E3B\u7528\u6237"}, + {".Edit.Principal.", " \u7F16\u8F91\u4E3B\u7528\u6237:"}, + {".Add.New.Principal.", " \u6DFB\u52A0\u65B0\u4E3B\u7528\u6237:"}, + {"Permissions", "\u6743\u9650"}, + {".Edit.Permission.", " \u7F16\u8F91\u6743\u9650:"}, + {".Add.New.Permission.", " \u52A0\u5165\u65B0\u7684\u6743\u9650:"}, + {"Signed.By.", "\u7B7E\u7F72\u4EBA: "}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "\u6CA1\u6709\u901A\u914D\u7B26\u540D\u79F0, \u65E0\u6CD5\u4F7F\u7528\u901A\u914D\u7B26\u7C7B\u6307\u5B9A\u4E3B\u7528\u6237"}, + {"Cannot.Specify.Principal.without.a.Name", + "\u6CA1\u6709\u540D\u79F0, \u65E0\u6CD5\u6307\u5B9A\u4E3B\u7528\u6237"}, + {"Permission.and.Target.Name.must.have.a.value", + "\u6743\u9650\u53CA\u76EE\u6807\u540D\u5FC5\u987B\u6709\u4E00\u4E2A\u503C"}, + {"Remove.this.Policy.Entry.", "\u662F\u5426\u5220\u9664\u6B64\u7B56\u7565\u6761\u76EE?"}, + {"Overwrite.File", "\u8986\u76D6\u6587\u4EF6"}, + {"Policy.successfully.written.to.filename", + "\u7B56\u7565\u5DF2\u6210\u529F\u5199\u5165\u5230{0}"}, + {"null.filename", "\u7A7A\u6587\u4EF6\u540D"}, + {"Save.changes.", "\u662F\u5426\u4FDD\u5B58\u6240\u505A\u7684\u66F4\u6539?"}, + {"Yes", "\u662F(&Y)"}, + {"No", "\u5426(&N)"}, + {"Policy.Entry", "\u7B56\u7565\u6761\u76EE"}, + {"Save.Changes", "\u4FDD\u5B58\u66F4\u6539"}, + {"No.Policy.Entry.selected", "\u6CA1\u6709\u9009\u62E9\u7B56\u7565\u6761\u76EE"}, + {"Unable.to.open.KeyStore.ex.toString.", + "\u65E0\u6CD5\u6253\u5F00\u5BC6\u94A5\u5E93: {0}"}, + {"No.principal.selected", "\u672A\u9009\u62E9\u4E3B\u7528\u6237"}, + {"No.permission.selected", "\u6CA1\u6709\u9009\u62E9\u6743\u9650"}, + {"name", "\u540D\u79F0"}, + {"configuration.type", "\u914D\u7F6E\u7C7B\u578B"}, + {"environment.variable.name", "\u73AF\u5883\u53D8\u91CF\u540D"}, + {"library.name", "\u5E93\u540D\u79F0"}, + {"package.name", "\u7A0B\u5E8F\u5305\u540D\u79F0"}, + {"policy.type", "\u7B56\u7565\u7C7B\u578B"}, + {"property.name", "\u5C5E\u6027\u540D\u79F0"}, + {"provider.name", "\u63D0\u4F9B\u65B9\u540D\u79F0"}, + {"url", "URL"}, + {"method.list", "\u65B9\u6CD5\u5217\u8868"}, + {"request.headers.list", "\u8BF7\u6C42\u6807\u5934\u5217\u8868"}, + {"Principal.List", "\u4E3B\u7528\u6237\u5217\u8868"}, + {"Permission.List", "\u6743\u9650\u5217\u8868"}, + {"Code.Base", "\u4EE3\u7801\u5E93"}, + {"KeyStore.U.R.L.", "\u5BC6\u94A5\u5E93 URL:"}, + {"KeyStore.Password.U.R.L.", "\u5BC6\u94A5\u5E93\u53E3\u4EE4 URL:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_zh_HK.java b/src/sun/security/tools/policytool/Resources_zh_HK.java new file mode 100644 index 00000000..a67441a4 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_zh_HK.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_zh_HK extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "\u8B66\u544A: \u5225\u540D {0} \u7684\u516C\u958B\u91D1\u9470\u4E0D\u5B58\u5728\u3002\u8ACB\u78BA\u5B9A\u91D1\u9470\u5132\u5B58\u5EAB\u914D\u7F6E\u6B63\u78BA\u3002"}, + {"Warning.Class.not.found.class", "\u8B66\u544A: \u627E\u4E0D\u5230\u985E\u5225 {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "\u8B66\u544A: \u7121\u6548\u7684\u5EFA\u69CB\u5B50\u5F15\u6578: {0}"}, + {"Illegal.Principal.Type.type", "\u7121\u6548\u7684 Principal \u985E\u578B: {0}"}, + {"Illegal.option.option", "\u7121\u6548\u7684\u9078\u9805: {0}"}, + {"Usage.policytool.options.", "\u7528\u6CD5: policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] \u539F\u5247\u6A94\u6848\u4F4D\u7F6E"}, + {"New", "\u65B0\u589E"}, + {"Open", "\u958B\u555F"}, + {"Save", "\u5132\u5B58"}, + {"Save.As", "\u53E6\u5B58\u65B0\u6A94"}, + {"View.Warning.Log", "\u6AA2\u8996\u8B66\u544A\u8A18\u9304"}, + {"Exit", "\u7D50\u675F"}, + {"Add.Policy.Entry", "\u65B0\u589E\u539F\u5247\u9805\u76EE"}, + {"Edit.Policy.Entry", "\u7DE8\u8F2F\u539F\u5247\u9805\u76EE"}, + {"Remove.Policy.Entry", "\u79FB\u9664\u539F\u5247\u9805\u76EE"}, + {"Edit", "\u7DE8\u8F2F"}, + {"Retain", "\u4FDD\u7559"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "\u8B66\u544A: \u6A94\u6848\u540D\u7A31\u5305\u542B\u9041\u96E2\u53CD\u659C\u7DDA\u5B57\u5143\u3002\u4E0D\u9700\u8981\u9041\u96E2\u53CD\u659C\u7DDA\u5B57\u5143 (\u64B0\u5BEB\u539F\u5247\u5167\u5BB9\u81F3\u6C38\u4E45\u5B58\u653E\u5340\u6642\u9700\u8981\u5DE5\u5177\u9041\u96E2\u5B57\u5143)\u3002\n\n\u6309\u4E00\u4E0B\u300C\u4FDD\u7559\u300D\u4EE5\u4FDD\u7559\u8F38\u5165\u7684\u540D\u7A31\uFF0C\u6216\u6309\u4E00\u4E0B\u300C\u7DE8\u8F2F\u300D\u4EE5\u7DE8\u8F2F\u540D\u7A31\u3002"}, + + {"Add.Public.Key.Alias", "\u65B0\u589E\u516C\u958B\u91D1\u9470\u5225\u540D"}, + {"Remove.Public.Key.Alias", "\u79FB\u9664\u516C\u958B\u91D1\u9470\u5225\u540D"}, + {"File", "\u6A94\u6848"}, + {"KeyStore", "\u91D1\u9470\u5132\u5B58\u5EAB"}, + {"Policy.File.", "\u539F\u5247\u6A94\u6848: "}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "\u7121\u6CD5\u958B\u555F\u539F\u5247\u6A94\u6848: {0}: {1}"}, + {"Policy.Tool", "\u539F\u5247\u5DE5\u5177"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "\u958B\u555F\u539F\u5247\u8A18\u7F6E\u6642\u767C\u751F\u932F\u8AA4\u3002\u8ACB\u6AA2\u8996\u8B66\u544A\u8A18\u9304\u4EE5\u53D6\u5F97\u66F4\u591A\u7684\u8CC7\u8A0A"}, + {"Error", "\u932F\u8AA4"}, + {"OK", "\u78BA\u5B9A"}, + {"Status", "\u72C0\u614B"}, + {"Warning", "\u8B66\u544A"}, + {"Permission.", + "\u6B0A\u9650: "}, + {"Principal.Type.", "Principal \u985E\u578B: "}, + {"Principal.Name.", "Principal \u540D\u7A31: "}, + {"Target.Name.", + "\u76EE\u6A19\u540D\u7A31: "}, + {"Actions.", + "\u52D5\u4F5C: "}, + {"OK.to.overwrite.existing.file.filename.", + "\u78BA\u8A8D\u8986\u5BEB\u73FE\u5B58\u7684\u6A94\u6848 {0}\uFF1F"}, + {"Cancel", "\u53D6\u6D88"}, + {"CodeBase.", "CodeBase:"}, + {"SignedBy.", "SignedBy:"}, + {"Add.Principal", "\u65B0\u589E Principal"}, + {"Edit.Principal", "\u7DE8\u8F2F Principal"}, + {"Remove.Principal", "\u79FB\u9664 Principal"}, + {"Principals.", "Principal:"}, + {".Add.Permission", " \u65B0\u589E\u6B0A\u9650"}, + {".Edit.Permission", " \u7DE8\u8F2F\u6B0A\u9650"}, + {"Remove.Permission", "\u79FB\u9664\u6B0A\u9650"}, + {"Done", "\u5B8C\u6210"}, + {"KeyStore.URL.", "\u91D1\u9470\u5132\u5B58\u5EAB URL: "}, + {"KeyStore.Type.", "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B:"}, + {"KeyStore.Provider.", "\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005:"}, + {"KeyStore.Password.URL.", "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC URL: "}, + {"Principals", "Principal"}, + {".Edit.Principal.", " \u7DE8\u8F2F Principal: "}, + {".Add.New.Principal.", " \u65B0\u589E Principal: "}, + {"Permissions", "\u6B0A\u9650"}, + {".Edit.Permission.", " \u7DE8\u8F2F\u6B0A\u9650:"}, + {".Add.New.Permission.", " \u65B0\u589E\u6B0A\u9650:"}, + {"Signed.By.", "\u7C3D\u7F72\u4EBA: "}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "\u6C92\u6709\u842C\u7528\u5B57\u5143\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A\u542B\u6709\u842C\u7528\u5B57\u5143\u985E\u5225\u7684 Principal"}, + {"Cannot.Specify.Principal.without.a.Name", + "\u6C92\u6709\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A Principal"}, + {"Permission.and.Target.Name.must.have.a.value", + "\u6B0A\u9650\u53CA\u76EE\u6A19\u540D\u7A31\u5FC5\u9808\u6709\u4E00\u500B\u503C\u3002"}, + {"Remove.this.Policy.Entry.", "\u79FB\u9664\u9019\u500B\u539F\u5247\u9805\u76EE\uFF1F"}, + {"Overwrite.File", "\u8986\u5BEB\u6A94\u6848"}, + {"Policy.successfully.written.to.filename", + "\u539F\u5247\u6210\u529F\u5BEB\u5165\u81F3 {0}"}, + {"null.filename", "\u7A7A\u503C\u6A94\u540D"}, + {"Save.changes.", "\u5132\u5B58\u8B8A\u66F4\uFF1F"}, + {"Yes", "\u662F"}, + {"No", "\u5426"}, + {"Policy.Entry", "\u539F\u5247\u9805\u76EE"}, + {"Save.Changes", "\u5132\u5B58\u8B8A\u66F4"}, + {"No.Policy.Entry.selected", "\u6C92\u6709\u9078\u53D6\u539F\u5247\u9805\u76EE"}, + {"Unable.to.open.KeyStore.ex.toString.", + "\u7121\u6CD5\u958B\u555F\u91D1\u9470\u5132\u5B58\u5EAB: {0}"}, + {"No.principal.selected", "\u672A\u9078\u53D6 Principal"}, + {"No.permission.selected", "\u6C92\u6709\u9078\u53D6\u6B0A\u9650"}, + {"name", "\u540D\u7A31"}, + {"configuration.type", "\u7D44\u614B\u985E\u578B"}, + {"environment.variable.name", "\u74B0\u5883\u8B8A\u6578\u540D\u7A31"}, + {"library.name", "\u7A0B\u5F0F\u5EAB\u540D\u7A31"}, + {"package.name", "\u5957\u88DD\u7A0B\u5F0F\u540D\u7A31"}, + {"policy.type", "\u539F\u5247\u985E\u578B"}, + {"property.name", "\u5C6C\u6027\u540D\u7A31"}, + {"provider.name", "\u63D0\u4F9B\u8005\u540D\u7A31"}, + {"Principal.List", "Principal \u6E05\u55AE"}, + {"Permission.List", "\u6B0A\u9650\u6E05\u55AE"}, + {"Code.Base", "\u4EE3\u78BC\u57FA\u6E96"}, + {"KeyStore.U.R.L.", "\u91D1\u9470\u5132\u5B58\u5EAB URL:"}, + {"KeyStore.Password.U.R.L.", "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC URL:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/tools/policytool/Resources_zh_TW.java b/src/sun/security/tools/policytool/Resources_zh_TW.java new file mode 100644 index 00000000..a0c60ca8 --- /dev/null +++ b/src/sun/security/tools/policytool/Resources_zh_TW.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.tools.policytool; + +/** + *

    This class represents the ResourceBundle + * for the policytool. + * + */ +public class Resources_zh_TW extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + {"NEWLINE", "\n"}, + {"Warning.A.public.key.for.alias.signers.i.does.not.exist.Make.sure.a.KeyStore.is.properly.configured.", + "\u8B66\u544A: \u5225\u540D {0} \u7684\u516C\u958B\u91D1\u9470\u4E0D\u5B58\u5728\u3002\u8ACB\u78BA\u5B9A\u91D1\u9470\u5132\u5B58\u5EAB\u914D\u7F6E\u6B63\u78BA\u3002"}, + {"Warning.Class.not.found.class", "\u8B66\u544A: \u627E\u4E0D\u5230\u985E\u5225 {0}"}, + {"Warning.Invalid.argument.s.for.constructor.arg", + "\u8B66\u544A: \u7121\u6548\u7684\u5EFA\u69CB\u5B50\u5F15\u6578: {0}"}, + {"Illegal.Principal.Type.type", "\u7121\u6548\u7684 Principal \u985E\u578B: {0}"}, + {"Illegal.option.option", "\u7121\u6548\u7684\u9078\u9805: {0}"}, + {"Usage.policytool.options.", "\u7528\u6CD5: policytool [options]"}, + {".file.file.policy.file.location", + " [-file ] \u539F\u5247\u6A94\u6848\u4F4D\u7F6E"}, + {"New", "\u65B0\u5EFA(&N)"}, + {"Open", "\u958B\u555F(&O)..."}, + {"Save", "\u5132\u5B58(&S)"}, + {"Save.As", "\u53E6\u5B58\u65B0\u6A94(&A)..."}, + {"View.Warning.Log", "\u6AA2\u8996\u8B66\u544A\u8A18\u9304(&W)"}, + {"Exit", "\u7D50\u675F(&X)"}, + {"Add.Policy.Entry", "\u65B0\u589E\u539F\u5247\u9805\u76EE(&A)"}, + {"Edit.Policy.Entry", "\u7DE8\u8F2F\u539F\u5247\u9805\u76EE(&E)"}, + {"Remove.Policy.Entry", "\u79FB\u9664\u539F\u5247\u9805\u76EE(&R)"}, + {"Edit", "\u7DE8\u8F2F(&E)"}, + {"Retain", "\u4FDD\u7559"}, + + {"Warning.File.name.may.include.escaped.backslash.characters.It.is.not.necessary.to.escape.backslash.characters.the.tool.escapes", + "\u8B66\u544A: \u6A94\u6848\u540D\u7A31\u5305\u542B\u9041\u96E2\u53CD\u659C\u7DDA\u5B57\u5143\u3002\u4E0D\u9700\u8981\u9041\u96E2\u53CD\u659C\u7DDA\u5B57\u5143 (\u64B0\u5BEB\u539F\u5247\u5167\u5BB9\u81F3\u6C38\u4E45\u5B58\u653E\u5340\u6642\u9700\u8981\u5DE5\u5177\u9041\u96E2\u5B57\u5143)\u3002\n\n\u6309\u4E00\u4E0B\u300C\u4FDD\u7559\u300D\u4EE5\u4FDD\u7559\u8F38\u5165\u7684\u540D\u7A31\uFF0C\u6216\u6309\u4E00\u4E0B\u300C\u7DE8\u8F2F\u300D\u4EE5\u7DE8\u8F2F\u540D\u7A31\u3002"}, + + {"Add.Public.Key.Alias", "\u65B0\u589E\u516C\u958B\u91D1\u9470\u5225\u540D"}, + {"Remove.Public.Key.Alias", "\u79FB\u9664\u516C\u958B\u91D1\u9470\u5225\u540D"}, + {"File", "\u6A94\u6848(&F)"}, + {"KeyStore", "\u91D1\u9470\u5132\u5B58\u5EAB(&K)"}, + {"Policy.File.", "\u539F\u5247\u6A94\u6848: "}, + {"Could.not.open.policy.file.policyFile.e.toString.", + "\u7121\u6CD5\u958B\u555F\u539F\u5247\u6A94\u6848: {0}: {1}"}, + {"Policy.Tool", "\u539F\u5247\u5DE5\u5177"}, + {"Errors.have.occurred.while.opening.the.policy.configuration.View.the.Warning.Log.for.more.information.", + "\u958B\u555F\u539F\u5247\u8A18\u7F6E\u6642\u767C\u751F\u932F\u8AA4\u3002\u8ACB\u6AA2\u8996\u8B66\u544A\u8A18\u9304\u4EE5\u53D6\u5F97\u66F4\u591A\u7684\u8CC7\u8A0A"}, + {"Error", "\u932F\u8AA4"}, + {"OK", "\u78BA\u5B9A"}, + {"Status", "\u72C0\u614B"}, + {"Warning", "\u8B66\u544A"}, + {"Permission.", + "\u6B0A\u9650: "}, + {"Principal.Type.", "Principal \u985E\u578B: "}, + {"Principal.Name.", "Principal \u540D\u7A31: "}, + {"Target.Name.", + "\u76EE\u6A19\u540D\u7A31: "}, + {"Actions.", + "\u52D5\u4F5C: "}, + {"OK.to.overwrite.existing.file.filename.", + "\u78BA\u8A8D\u8986\u5BEB\u73FE\u5B58\u7684\u6A94\u6848 {0}\uFF1F"}, + {"Cancel", "\u53D6\u6D88"}, + {"CodeBase.", "CodeBase(&C):"}, + {"SignedBy.", "SignedBy(&S):"}, + {"Add.Principal", "\u65B0\u589E Principal(&A)"}, + {"Edit.Principal", "\u7DE8\u8F2F Principal(&E)"}, + {"Remove.Principal", "\u79FB\u9664 Principal(&R)"}, + {"Principals.", "Principal(&P):"}, + {".Add.Permission", " \u65B0\u589E\u6B0A\u9650(&D)"}, + {".Edit.Permission", " \u7DE8\u8F2F\u6B0A\u9650(&I)"}, + {"Remove.Permission", "\u79FB\u9664\u6B0A\u9650(&M)"}, + {"Done", "\u5B8C\u6210"}, + {"KeyStore.URL.", "\u91D1\u9470\u5132\u5B58\u5EAB URL(&U): "}, + {"KeyStore.Type.", "\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B(&T):"}, + {"KeyStore.Provider.", "\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005(&P):"}, + {"KeyStore.Password.URL.", "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC URL(&W): "}, + {"Principals", "Principal"}, + {".Edit.Principal.", " \u7DE8\u8F2F Principal: "}, + {".Add.New.Principal.", " \u65B0\u589E Principal: "}, + {"Permissions", "\u6B0A\u9650"}, + {".Edit.Permission.", " \u7DE8\u8F2F\u6B0A\u9650:"}, + {".Add.New.Permission.", " \u65B0\u589E\u6B0A\u9650:"}, + {"Signed.By.", "\u7C3D\u7F72\u4EBA: "}, + {"Cannot.Specify.Principal.with.a.Wildcard.Class.without.a.Wildcard.Name", + "\u6C92\u6709\u842C\u7528\u5B57\u5143\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A\u542B\u6709\u842C\u7528\u5B57\u5143\u985E\u5225\u7684 Principal"}, + {"Cannot.Specify.Principal.without.a.Name", + "\u6C92\u6709\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A Principal"}, + {"Permission.and.Target.Name.must.have.a.value", + "\u6B0A\u9650\u53CA\u76EE\u6A19\u540D\u7A31\u5FC5\u9808\u6709\u4E00\u500B\u503C\u3002"}, + {"Remove.this.Policy.Entry.", "\u79FB\u9664\u9019\u500B\u539F\u5247\u9805\u76EE\uFF1F"}, + {"Overwrite.File", "\u8986\u5BEB\u6A94\u6848"}, + {"Policy.successfully.written.to.filename", + "\u539F\u5247\u6210\u529F\u5BEB\u5165\u81F3 {0}"}, + {"null.filename", "\u7A7A\u503C\u6A94\u540D"}, + {"Save.changes.", "\u5132\u5B58\u8B8A\u66F4\uFF1F"}, + {"Yes", "\u662F(&Y)"}, + {"No", "\u5426(&N)"}, + {"Policy.Entry", "\u539F\u5247\u9805\u76EE"}, + {"Save.Changes", "\u5132\u5B58\u8B8A\u66F4"}, + {"No.Policy.Entry.selected", "\u6C92\u6709\u9078\u53D6\u539F\u5247\u9805\u76EE"}, + {"Unable.to.open.KeyStore.ex.toString.", + "\u7121\u6CD5\u958B\u555F\u91D1\u9470\u5132\u5B58\u5EAB: {0}"}, + {"No.principal.selected", "\u672A\u9078\u53D6 Principal"}, + {"No.permission.selected", "\u6C92\u6709\u9078\u53D6\u6B0A\u9650"}, + {"name", "\u540D\u7A31"}, + {"configuration.type", "\u7D44\u614B\u985E\u578B"}, + {"environment.variable.name", "\u74B0\u5883\u8B8A\u6578\u540D\u7A31"}, + {"library.name", "\u7A0B\u5F0F\u5EAB\u540D\u7A31"}, + {"package.name", "\u5957\u88DD\u7A0B\u5F0F\u540D\u7A31"}, + {"policy.type", "\u539F\u5247\u985E\u578B"}, + {"property.name", "\u5C6C\u6027\u540D\u7A31"}, + {"provider.name", "\u63D0\u4F9B\u8005\u540D\u7A31"}, + {"url", "URL"}, + {"method.list", "\u65B9\u6CD5\u6E05\u55AE"}, + {"request.headers.list", "\u8981\u6C42\u6A19\u982D\u6E05\u55AE"}, + {"Principal.List", "Principal \u6E05\u55AE"}, + {"Permission.List", "\u6B0A\u9650\u6E05\u55AE"}, + {"Code.Base", "\u4EE3\u78BC\u57FA\u6E96"}, + {"KeyStore.U.R.L.", "\u91D1\u9470\u5132\u5B58\u5EAB URL:"}, + {"KeyStore.Password.U.R.L.", "\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC URL:"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources.java b/src/sun/security/util/AuthResources.java new file mode 100644 index 00000000..8821b879 --- /dev/null +++ b/src/sun/security/util/AuthResources.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "invalid null input: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Invalid NTSid value"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [Primary Group]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [Supplementary Group]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "Unable to properly expand {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (No such file or directory)"}, + {"Configuration.Error.No.such.file.or.directory", + "Configuration Error:\n\tNo such file or directory"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Configuration Error:\n\tInvalid control flag, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Configuration Error:\n\tCan not specify multiple entries for {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Configuration Error:\n\texpected [{0}], read [end of file]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Configuration Error:\n\tLine {0}: expected [{1}], found [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Configuration Error:\n\tLine {0}: expected [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Configuration Error:\n\tLine {0}: system property [{1}] expanded to empty value"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","username: "}, + {"password.","password: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Please enter keystore information"}, + {"Keystore.alias.","Keystore alias: "}, + {"Keystore.password.","Keystore password: "}, + {"Private.key.password.optional.", + "Private key password (optional): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos username [{0}]: "}, + {"Kerberos.password.for.username.", + "Kerberos password for {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": error parsing "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": error adding Permission "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": error adding Entry "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "attempt to add a Permission to a readonly PermissionCollection"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "expected keystore type"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "can not specify Principal with a wildcard class without a wildcard name"}, + {"expected.codeBase.or.SignedBy", "expected codeBase or SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "only Principal-based grant entries permitted"}, + {"expected.permission.entry", "expected permission entry"}, + {"number.", "number "}, + {"expected.expect.read.end.of.file.", + "expected {0}, read end of file"}, + {"expected.read.end.of.file", "expected ';', read end of file"}, + {"line.", "line "}, + {".expected.", ": expected '"}, + {".found.", "', found '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [Primary Group]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [Supplementary Group]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "provided null name"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_de.java b/src/sun/security/util/AuthResources_de.java new file mode 100644 index 00000000..19173956 --- /dev/null +++ b/src/sun/security/util/AuthResources_de.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_de extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "Ung\u00FCltige Nulleingabe: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Ung\u00FCltiger NTSid-Wert"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [Prim\u00E4rgruppe]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [Zusatzgruppe]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "{0} kann nicht ordnungsgem\u00E4\u00DF erweitert werden"}, + {"extra.config.No.such.file.or.directory.", + "{0} (Datei oder Verzeichnis nicht vorhanden)"}, + {"Configuration.Error.No.such.file.or.directory", + "Konfigurationsfehler:\n\tDatei oder Verzeichnis nicht vorhanden"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Konfigurationsfehler:\n\tUng\u00FCltiges Steuerkennzeichen {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Konfigurationsfehler:\n\tEs k\u00F6nnen nicht mehrere Angaben f\u00FCr {0} gemacht werden."}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Konfigurationsfehler:\n\t[{0}] erwartet, [Dateiende] gelesen"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Konfigurationsfehler:\n\tZeile {0}: [{1}] erwartet, [{2}] gefunden"}, + {"Configuration.Error.Line.line.expected.expect.", + "Konfigurationsfehler:\n\tZeile {0}: [{1}] erwartet"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Konfigurationsfehler:\n\tZeile {0}: Systemeigenschaft [{1}] auf leeren Wert erweitert"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","Benutzername: "}, + {"password.","Kennwort: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Geben Sie die Keystore-Informationen ein"}, + {"Keystore.alias.","Keystore-Alias: "}, + {"Keystore.password.","Keystore-Kennwort: "}, + {"Private.key.password.optional.", + "Private Key-Kennwort (optional): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos-Benutzername [{0}]: "}, + {"Kerberos.password.for.username.", + "Kerberos-Kennwort f\u00FCr {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": Parsefehler "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": Fehler beim Hinzuf\u00FCgen der Berechtigung "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": Fehler beim Hinzuf\u00FCgen des Eintrags "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "Es wurde versucht, eine Berechtigung zu einer schreibgesch\u00FCtzten PermissionCollection hinzuzuf\u00FCgen"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "Keystore-Typ erwartet"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "Principal kann nicht mit einer Platzhalterklasse ohne Platzhalternamen angegeben werden"}, + {"expected.codeBase.or.SignedBy", "codeBase oder SignedBy erwartet"}, + {"only.Principal.based.grant.entries.permitted", + "Nur Principal-basierte Berechtigungseintr\u00E4ge zul\u00E4ssig"}, + {"expected.permission.entry", "Berechtigungseintrag erwartet"}, + {"number.", "Nummer "}, + {"expected.expect.read.end.of.file.", + "{0} erwartet, Dateiende gelesen"}, + {"expected.read.end.of.file", "\";\" erwartet, Dateiende gelesen"}, + {"line.", "Zeile "}, + {".expected.", ": erwartet: \""}, + {".found.", "\", gefunden: \""}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [Prim\u00E4rgruppe]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [Zusatzgruppe]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "Nullname angegeben"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_es.java b/src/sun/security/util/AuthResources_es.java new file mode 100644 index 00000000..a18bbfe2 --- /dev/null +++ b/src/sun/security/util/AuthResources_es.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_es extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "entrada nula no v\u00E1lida: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Valor de NTSid no v\u00E1lido"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [Grupo Principal] {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [Grupo Adicional] {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "No se ha podido ampliar correctamente {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (No existe tal archivo o directorio)"}, + {"Configuration.Error.No.such.file.or.directory", + "Error de Configuraci\u00F3n:\n\tNo existe tal archivo o directorio"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Error de Configuraci\u00F3n:\n\tIndicador de control no v\u00E1lido, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Error de Configuraci\u00F3n:\n\tNo se pueden especificar varias entradas para {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Error de configuraci\u00F3n:\n\tse esperaba [{0}], se ha le\u00EDdo [final de archivo]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Error de configuraci\u00F3n:\n\tL\u00EDnea {0}: se esperaba [{1}], se ha encontrado [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Error de configuraci\u00F3n:\n\tL\u00EDnea {0}: se esperaba [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Error de configuraci\u00F3n:\n\tL\u00EDnea {0}: propiedad de sistema [{1}] ampliada a valor vac\u00EDo"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","nombre de usuario: "}, + {"password.","contrase\u00F1a: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Introduzca la informaci\u00F3n del almac\u00E9n de claves"}, + {"Keystore.alias.","Alias de Almac\u00E9n de Claves: "}, + {"Keystore.password.","Contrase\u00F1a de Almac\u00E9n de Claves: "}, + {"Private.key.password.optional.", + "Contrase\u00F1a de Clave Privada (opcional): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Nombre de usuario de Kerberos [{0}]: "}, + {"Kerberos.password.for.username.", + "Contrase\u00F1a de Kerberos de {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": error de an\u00E1lisis "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": error al agregar el permiso "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": error al agregar la entrada "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "se ha intentado agregar un permiso a una recopilaci\u00F3n de permisos de s\u00F3lo lectura"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "se esperaba un tipo de almac\u00E9n de claves"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "no se puede especificar Principal con una clase de comod\u00EDn sin un nombre de comod\u00EDn"}, + {"expected.codeBase.or.SignedBy", "se esperaba codeBase o SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "s\u00F3lo se permite otorgar entradas basadas en Principal"}, + {"expected.permission.entry", "se esperaba un permiso de entrada"}, + {"number.", "n\u00FAmero "}, + {"expected.expect.read.end.of.file.", + "se esperaba [{0}], se ha le\u00EDdo final de archivo"}, + {"expected.read.end.of.file", "se esperaba ';', se ha le\u00EDdo el final de archivo"}, + {"line.", "l\u00EDnea "}, + {".expected.", ": se esperaba '"}, + {".found.", "', se ha encontrado '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [Grupo Principal]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [Grupo Adicional]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "se ha proporcionado un nombre nulo"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_fr.java b/src/sun/security/util/AuthResources_fr.java new file mode 100644 index 00000000..ac92ae2f --- /dev/null +++ b/src/sun/security/util/AuthResources_fr.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_fr extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "entr\u00E9e NULL non valide : {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal : {0}"}, + {"NTNumericCredential.name", "NTNumericCredential : {0}"}, + {"Invalid.NTSid.value", "Valeur de NTSid non valide"}, + {"NTSid.name", "NTSid : {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal : {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal : {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal : {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal : {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal : {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [groupe principal] : {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [groupe suppl\u00E9mentaire] : {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal : {0}"}, + {"UnixPrincipal.name", "UnixPrincipal : {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "Impossible de d\u00E9velopper {0} correctement"}, + {"extra.config.No.such.file.or.directory.", + "{0} (fichier ou r\u00E9pertoire inexistant)"}, + {"Configuration.Error.No.such.file.or.directory", + "Erreur de configuration :\n\tCe fichier ou r\u00E9pertoire n'existe pas"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Erreur de configuration :\n\tIndicateur de contr\u00F4le non valide, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Erreur de configuration :\n\tImpossible de sp\u00E9cifier des entr\u00E9es multiples pour {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Erreur de configuration :\n\tAttendu : [{0}], lu : [fin de fichier]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Erreur de configuration :\n\tLigne {0} : attendu [{1}], trouv\u00E9 [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Erreur de configuration :\n\tLigne {0} : attendu [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Erreur de configuration :\n\tLigne {0} : propri\u00E9t\u00E9 syst\u00E8me [{1}] d\u00E9velopp\u00E9e en valeur vide"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","nom utilisateur : "}, + {"password.","mot de passe : "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Entrez les informations du fichier de cl\u00E9s"}, + {"Keystore.alias.","Alias du fichier de cl\u00E9s : "}, + {"Keystore.password.","Mot de passe pour fichier de cl\u00E9s : "}, + {"Private.key.password.optional.", + "Mot de passe de la cl\u00E9 priv\u00E9e (facultatif) : "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Nom utilisateur Kerberos [{0}] : "}, + {"Kerberos.password.for.username.", + "Mot de passe Kerberos pour {0} : "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": erreur d'analyse "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": erreur d'ajout de droit "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": erreur d'ajout d'entr\u00E9e "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "tentative d'ajout de droit \u00E0 un ensemble de droits en lecture seule"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "type de fichier de cl\u00E9s attendu"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "impossible de sp\u00E9cifier le principal avec une classe g\u00E9n\u00E9rique sans nom g\u00E9n\u00E9rique"}, + {"expected.codeBase.or.SignedBy", "codeBase ou SignedBy attendu"}, + {"only.Principal.based.grant.entries.permitted", + "seules les entr\u00E9es bas\u00E9es sur Principal sont autoris\u00E9es"}, + {"expected.permission.entry", "entr\u00E9e de droit attendue"}, + {"number.", "nombre "}, + {"expected.expect.read.end.of.file.", + "attendu {0}, lecture de fin de fichier"}, + {"expected.read.end.of.file", "attendu ';', lecture de fin de fichier"}, + {"line.", "ligne "}, + {".expected.", ": attendu '"}, + {".found.", "', trouv\u00E9 '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [groupe principal] : "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [groupe suppl\u00E9mentaire] : "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal : "}, + {"SolarisPrincipal.", "SolarisPrincipal : "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "nom NULL fourni"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_it.java b/src/sun/security/util/AuthResources_it.java new file mode 100644 index 00000000..ec5332e9 --- /dev/null +++ b/src/sun/security/util/AuthResources_it.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_it extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "input nullo non valido: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Valore NTSid non valido"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [gruppo primario]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [gruppo supplementare]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "Impossibile espandere correttamente {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (file o directory inesistente)"}, + {"Configuration.Error.No.such.file.or.directory", + "Errore di configurazione:\n\tFile o directory inesistente"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Errore di configurazione:\n\tflag di controllo non valido, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Errore di configurazione:\n\timpossibile specificare pi\u00F9 valori per {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Errore di configurazione:\n\tprevisto [{0}], letto [end of file]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Errore di configurazione:\n\triga {0}: previsto [{1}], trovato [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Errore di configurazione:\n\triga {0}: previsto [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Errore di configurazione:\n\triga {0}: propriet\u00E0 di sistema [{1}] espansa a valore vuoto"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","Nome utente: "}, + {"password.","Password: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Immettere le informazioni per il keystore"}, + {"Keystore.alias.","Alias keystore: "}, + {"Keystore.password.","Password keystore: "}, + {"Private.key.password.optional.", + "Password chiave privata (opzionale): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Nome utente Kerberos [{0}]: "}, + {"Kerberos.password.for.username.", + "Password Kerberos per {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": errore durante l'analisi "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": errore durante l'aggiunta dell'autorizzazione "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": errore durante l'aggiunta della voce "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "tentativo di aggiungere un'autorizzazione a una PermissionCollection di sola lettura"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "tipo keystore previsto"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "impossibile specificare un principal con una classe carattere jolly senza un nome carattere jolly"}, + {"expected.codeBase.or.SignedBy", "previsto codeBase o SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "sono consentiti solo valori garantiti basati sul principal"}, + {"expected.permission.entry", "prevista voce di autorizzazione"}, + {"number.", "numero "}, + {"expected.expect.read.end.of.file.", + "previsto {0}, letto end of file"}, + {"expected.read.end.of.file", "previsto ';', letto end of file"}, + {"line.", "riga "}, + {".expected.", ": previsto '"}, + {".found.", "', trovato '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [gruppo primario]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [gruppo supplementare]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "il nome fornito \u00E8 nullo"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_ja.java b/src/sun/security/util/AuthResources_ja.java new file mode 100644 index 00000000..24818ff2 --- /dev/null +++ b/src/sun/security/util/AuthResources_ja.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_ja extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "\u7121\u52B9\u306Anull\u306E\u5165\u529B: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "\u7121\u52B9\u306ANTSid\u5024"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [\u4E3B\u30B0\u30EB\u30FC\u30D7]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [\u88DC\u52A9\u30B0\u30EB\u30FC\u30D7]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "{0}\u3092\u6B63\u3057\u304F\u5C55\u958B\u3067\u304D\u307E\u305B\u3093"}, + {"extra.config.No.such.file.or.directory.", + "{0}(\u6307\u5B9A\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u5B58\u5728\u3057\u307E\u305B\u3093)"}, + {"Configuration.Error.No.such.file.or.directory", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t\u6307\u5B9A\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u5B58\u5728\u3057\u307E\u305B\u3093"}, + {"Configuration.Error.Invalid.control.flag.flag", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t\u7121\u52B9\u306A\u5236\u5FA1\u30D5\u30E9\u30B0: {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t{0}\u306B\u8907\u6570\u306E\u30A8\u30F3\u30C8\u30EA\u3092\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t[{0}]\u3067\u306F\u306A\u304F\u3001[\u30D5\u30A1\u30A4\u30EB\u306E\u7D42\u308F\u308A]\u304C\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3057\u305F"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t\u884C{0}: [{1}]\u3067\u306F\u306A\u304F\u3001[{2}]\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"Configuration.Error.Line.line.expected.expect.", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t\u884C{0}: [{1}]\u304C\u8981\u6C42\u3055\u308C\u307E\u3057\u305F"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "\u69CB\u6210\u30A8\u30E9\u30FC:\n\t\u884C{0}: \u30B7\u30B9\u30C6\u30E0\u30FB\u30D7\u30ED\u30D1\u30C6\u30A3[{1}]\u304C\u7A7A\u306E\u5024\u306B\u5C55\u958B\u3055\u308C\u307E\u3057\u305F"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","\u30E6\u30FC\u30B6\u30FC\u540D: "}, + {"password.","\u30D1\u30B9\u30EF\u30FC\u30C9: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u60C5\u5831\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044"}, + {"Keystore.alias.","\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u5225\u540D: "}, + {"Keystore.password.","\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u30D1\u30B9\u30EF\u30FC\u30C9: "}, + {"Private.key.password.optional.", + "\u79D8\u5BC6\u9375\u306E\u30D1\u30B9\u30EF\u30FC\u30C9(\u30AA\u30D7\u30B7\u30E7\u30F3): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos\u30E6\u30FC\u30B6\u30FC\u540D[{0}]: "}, + {"Kerberos.password.for.username.", + "{0}\u306EKerberos\u30D1\u30B9\u30EF\u30FC\u30C9: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": \u89E3\u6790\u30A8\u30E9\u30FC "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": \u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u8FFD\u52A0\u30A8\u30E9\u30FC "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": \u30A8\u30F3\u30C8\u30EA\u306E\u8FFD\u52A0\u30A8\u30E9\u30FC "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "\u8AAD\u53D6\u308A\u5C02\u7528\u306EPermissionCollection\u306B\u30A2\u30AF\u30BB\u30B9\u6A29\u306E\u8FFD\u52A0\u304C\u8A66\u884C\u3055\u308C\u307E\u3057\u305F"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "\u4E88\u60F3\u3055\u308C\u305F\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30BF\u30A4\u30D7"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u540D\u306E\u306A\u3044\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u30FB\u30AF\u30E9\u30B9\u3092\u4F7F\u7528\u3057\u3066\u3001\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u3092\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"expected.codeBase.or.SignedBy", "\u4E88\u60F3\u3055\u308C\u305FcodeBase\u307E\u305F\u306FSignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u30FB\u30D9\u30FC\u30B9\u306E\u30A8\u30F3\u30C8\u30EA\u306E\u307F\u304C\u8A31\u53EF\u3055\u308C\u307E\u3059\u3002"}, + {"expected.permission.entry", "\u4E88\u60F3\u3055\u308C\u305F\u30A2\u30AF\u30BB\u30B9\u6A29\u30A8\u30F3\u30C8\u30EA"}, + {"number.", "\u6570 "}, + {"expected.expect.read.end.of.file.", + "{0}\u3067\u306F\u306A\u304F\u30D5\u30A1\u30A4\u30EB\u306E\u7D42\u308F\u308A\u304C\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3057\u305F"}, + {"expected.read.end.of.file", "\u4E88\u60F3\u5024\u306F';'\u3067\u3059\u304C\u3001\u30D5\u30A1\u30A4\u30EB\u306E\u7D42\u308F\u308A\u304C\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3057\u305F"}, + {"line.", "\u884C\u756A\u53F7 "}, + {".expected.", ": \u4E88\u60F3\u5024'"}, + {".found.", "',\u691C\u51FA\u5024'"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [\u4E3B\u30B0\u30EB\u30FC\u30D7]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [\u88DC\u52A9\u30B0\u30EB\u30FC\u30D7]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "null\u306E\u540D\u524D\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_ko.java b/src/sun/security/util/AuthResources_ko.java new file mode 100644 index 00000000..a7537fea --- /dev/null +++ b/src/sun/security/util/AuthResources_ko.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_ko extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "\uBD80\uC801\uD569\uD55C \uB110 \uC785\uB825: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "NTSid \uAC12\uC774 \uBD80\uC801\uD569\uD569\uB2C8\uB2E4."}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [\uAE30\uBCF8 \uADF8\uB8F9]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [\uBCF4\uC870 \uADF8\uB8F9]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "{0}\uC744(\uB97C) \uC81C\uB300\uB85C \uD655\uC7A5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"extra.config.No.such.file.or.directory.", + "{0}(\uD574\uB2F9 \uD30C\uC77C \uB610\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.)"}, + {"Configuration.Error.No.such.file.or.directory", + "\uAD6C\uC131 \uC624\uB958:\n\t\uD574\uB2F9 \uD30C\uC77C \uB610\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Configuration.Error.Invalid.control.flag.flag", + "\uAD6C\uC131 \uC624\uB958:\n\t\uC81C\uC5B4 \uD50C\uB798\uADF8\uAC00 \uBD80\uC801\uD569\uD568, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "\uAD6C\uC131 \uC624\uB958:\n\t{0}\uC5D0 \uB300\uD574 \uC5EC\uB7EC \uD56D\uBAA9\uC744 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "\uAD6C\uC131 \uC624\uB958:\n\t[{0}]\uC774(\uAC00) \uD544\uC694\uD558\uC9C0\uB9CC [\uD30C\uC77C\uC758 \uB05D]\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4."}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "\uAD6C\uC131 \uC624\uB958:\n\t{0} \uD589: [{1}]\uC774(\uAC00) \uD544\uC694\uD558\uC9C0\uB9CC [{2}]\uC774(\uAC00) \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Configuration.Error.Line.line.expected.expect.", + "\uAD6C\uC131 \uC624\uB958:\n\t{0} \uD589: [{1}]\uC774(\uAC00) \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "\uAD6C\uC131 \uC624\uB958:\n\t{0} \uD589: \uC2DC\uC2A4\uD15C \uC18D\uC131 [{1}]\uC774(\uAC00) \uBE48 \uAC12\uC73C\uB85C \uD655\uC7A5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","\uC0AC\uC6A9\uC790 \uC774\uB984: "}, + {"password.","\uBE44\uBC00\uBC88\uD638: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "\uD0A4 \uC800\uC7A5\uC18C \uC815\uBCF4\uB97C \uC785\uB825\uD558\uC2ED\uC2DC\uC624."}, + {"Keystore.alias.","\uD0A4 \uC800\uC7A5\uC18C \uBCC4\uCE6D: "}, + {"Keystore.password.","\uD0A4 \uC800\uC7A5\uC18C \uBE44\uBC00\uBC88\uD638: "}, + {"Private.key.password.optional.", + "\uC804\uC6A9 \uD0A4 \uBE44\uBC00\uBC88\uD638(\uC120\uD0DD \uC0AC\uD56D): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos \uC0AC\uC6A9\uC790 \uC774\uB984 [{0}]: "}, + {"Kerberos.password.for.username.", + "{0}\uC758 Kerberos \uBE44\uBC00\uBC88\uD638: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": \uAD6C\uBB38 \uBD84\uC11D \uC624\uB958 "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": \uAD8C\uD55C \uCD94\uAC00 \uC624\uB958 "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": \uD56D\uBAA9 \uCD94\uAC00 \uC624\uB958 "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "\uC77D\uAE30 \uC804\uC6A9 PermissionCollection\uC5D0 \uAD8C\uD55C\uC744 \uCD94\uAC00\uD558\uB824\uACE0 \uC2DC\uB3C4\uD588\uC2B5\uB2C8\uB2E4."}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "\uD0A4 \uC800\uC7A5\uC18C \uC720\uD615\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uC774\uB984 \uC5C6\uC774 \uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uD074\uB798\uC2A4\uB97C \uC0AC\uC6A9\uD558\uB294 \uC8FC\uCCB4\uB97C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"expected.codeBase.or.SignedBy", "codeBase \uB610\uB294 SignedBy\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"only.Principal.based.grant.entries.permitted", + "\uC8FC\uCCB4 \uAE30\uBC18 \uAD8C\uD55C \uBD80\uC5EC \uD56D\uBAA9\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4."}, + {"expected.permission.entry", "\uAD8C\uD55C \uD56D\uBAA9\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"number.", "\uC22B\uC790 "}, + {"expected.expect.read.end.of.file.", + "{0}\uC774(\uAC00) \uD544\uC694\uD558\uC9C0\uB9CC \uD30C\uC77C\uC758 \uB05D\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4."}, + {"expected.read.end.of.file", "';'\uC774 \uD544\uC694\uD558\uC9C0\uB9CC \uD30C\uC77C\uC758 \uB05D\uC5D0 \uB3C4\uB2EC\uD588\uC2B5\uB2C8\uB2E4."}, + {"line.", "\uD589 "}, + {".expected.", ": \uD544\uC694\uD55C \uD56D\uBAA9: '"}, + {".found.", "', \uBC1C\uACAC\uB41C \uD56D\uBAA9: '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [\uAE30\uBCF8 \uADF8\uB8F9]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [\uBCF4\uC870 \uADF8\uB8F9]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "\uB110 \uC774\uB984\uC744 \uC81C\uACF5\uD588\uC2B5\uB2C8\uB2E4."} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_pt_BR.java b/src/sun/security/util/AuthResources_pt_BR.java new file mode 100644 index 00000000..1c251f70 --- /dev/null +++ b/src/sun/security/util/AuthResources_pt_BR.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_pt_BR extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "entrada nula inv\u00E1lida: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Valor de NTSid inv\u00E1lido"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [Grupo Principal]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [Grupo Complementar]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "N\u00E3o \u00E9 poss\u00EDvel expandir corretamente {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (tal arquivo ou diret\u00F3rio n\u00E3o existe)"}, + {"Configuration.Error.No.such.file.or.directory", + "Erro de Configura\u00E7\u00E3o:\n\tN\u00E3o h\u00E1 tal arquivo ou diret\u00F3rio"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Erro de Configura\u00E7\u00E3o:\n\tFlag de controle inv\u00E1lido, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Erro de Configura\u00E7\u00E3o:\n\tN\u00E3o \u00E9 poss\u00EDvel especificar v\u00E1rias entradas para {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Erro de Configura\u00E7\u00E3o:\n\tesperado [{0}], lido [fim do arquivo]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Erro de Configura\u00E7\u00E3o:\n\tLinha {0}: esperada [{1}], encontrada [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Erro de Configura\u00E7\u00E3o:\n\tLinha {0}: esperada [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Erro de Configura\u00E7\u00E3o:\n\tLinha {0}: propriedade do sistema [{1}] expandida para valor vazio"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","nome do usu\u00E1rio: "}, + {"password.","senha: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Especifique as informa\u00E7\u00F5es do armazenamento de chaves"}, + {"Keystore.alias.","Alias do armazenamento de chaves: "}, + {"Keystore.password.","Senha do armazenamento de chaves: "}, + {"Private.key.password.optional.", + "Senha da chave privada (opcional): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Nome do usu\u00E1rio de Kerberos [{0}]: "}, + {"Kerberos.password.for.username.", + "Senha de Kerberos de {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": erro de parsing "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": erro ao adicionar a Permiss\u00E3o "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": erro ao adicionar a Entrada "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "tentativa de adicionar uma Permiss\u00E3o a um PermissionCollection somente para leitura"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "tipo de armazenamento de chaves esperado"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "n\u00E3o \u00E9 poss\u00EDvel especificar um principal com uma classe curinga sem um nome curinga"}, + {"expected.codeBase.or.SignedBy", "CodeBase ou SignedBy esperado"}, + {"only.Principal.based.grant.entries.permitted", + "somente \u00E9 permitido conceder entradas com base no Principal"}, + {"expected.permission.entry", "entrada de permiss\u00E3o esperada"}, + {"number.", "n\u00FAmero "}, + {"expected.expect.read.end.of.file.", + "esperado {0}, ler fim do arquivo"}, + {"expected.read.end.of.file", "esperado ';', fim de arquivo lido"}, + {"line.", "linha "}, + {".expected.", ": esperado '"}, + {".found.", "', encontrado '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [Grupo Principal]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [Grupo Complementar]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "nome nulo fornecido"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_sv.java b/src/sun/security/util/AuthResources_sv.java new file mode 100644 index 00000000..f2e82c7b --- /dev/null +++ b/src/sun/security/util/AuthResources_sv.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_sv extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "ogiltiga null-indata: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "Ogiltigt NTSid-v\u00E4rde"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [prim\u00E4r grupp]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [till\u00E4ggsgrupp]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "Kan inte ut\u00F6ka korrekt {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (det finns ingen s\u00E5dan fil eller katalog)"}, + {"Configuration.Error.No.such.file.or.directory", + "Konfigurationsfel:\n\tFilen eller katalogen finns inte"}, + {"Configuration.Error.Invalid.control.flag.flag", + "Konfigurationsfel:\n\tOgiltig kontrollflagga, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "Konfigurationsfel:\n\tKan inte ange flera poster f\u00F6r {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "Konfigurationsfel:\n\tf\u00F6rv\u00E4ntade [{0}], l\u00E4ste [filslut]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "Konfigurationsfel:\n\tRad {0}: f\u00F6rv\u00E4ntade [{1}], hittade [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "Konfigurationsfel:\n\tRad {0}: f\u00F6rv\u00E4ntade [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "Konfigurationsfel:\n\tRad {0}: systemegenskapen [{1}] ut\u00F6kad till tomt v\u00E4rde"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","anv\u00E4ndarnamn: "}, + {"password.","l\u00F6senord: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "Ange nyckellagerinformation"}, + {"Keystore.alias.","Nyckellageralias: "}, + {"Keystore.password.","Nyckellagerl\u00F6senord: "}, + {"Private.key.password.optional.", + "L\u00F6senord f\u00F6r personlig nyckel (valfritt): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos-anv\u00E4ndarnamn [{0}]: "}, + {"Kerberos.password.for.username.", + "Kerberos-l\u00F6senord f\u00F6r {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": tolkningsfel "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": fel vid till\u00E4gg av beh\u00F6righet "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": fel vid till\u00E4gg av post "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "f\u00F6rs\u00F6k att l\u00E4gga till beh\u00F6righet till skrivskyddad PermissionCollection"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "f\u00F6rv\u00E4ntad nyckellagertyp"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "kan inte ange identitetshavare med en jokerteckenklass utan ett jokerteckennamn"}, + {"expected.codeBase.or.SignedBy", "f\u00F6rv\u00E4ntade codeBase eller SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "endast identitetshavarbaserade poster till\u00E5ts"}, + {"expected.permission.entry", "f\u00F6rv\u00E4ntade beh\u00F6righetspost"}, + {"number.", "antal "}, + {"expected.expect.read.end.of.file.", + "f\u00F6rv\u00E4ntade {0}, l\u00E4ste filslut"}, + {"expected.read.end.of.file", "f\u00F6rv\u00E4ntade ';', l\u00E4ste filslut"}, + {"line.", "rad "}, + {".expected.", ": f\u00F6rv\u00E4ntade '"}, + {".found.", "', hittade '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [prim\u00E4r grupp]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [till\u00E4ggsgrupp]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "angav null-namn"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_zh_CN.java b/src/sun/security/util/AuthResources_zh_CN.java new file mode 100644 index 00000000..eab8f671 --- /dev/null +++ b/src/sun/security/util/AuthResources_zh_CN.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_zh_CN extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "\u65E0\u6548\u7684\u7A7A\u8F93\u5165: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "\u65E0\u6548\u7684 NTSid \u503C"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [\u4E3B\u7EC4]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [\u8865\u5145\u7EC4]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "\u65E0\u6CD5\u6B63\u786E\u6269\u5C55{0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (\u6CA1\u6709\u8FD9\u6837\u7684\u6587\u4EF6\u6216\u76EE\u5F55)"}, + {"Configuration.Error.No.such.file.or.directory", + "\u914D\u7F6E\u9519\u8BEF:\n\t\u6CA1\u6709\u6B64\u6587\u4EF6\u6216\u76EE\u5F55"}, + {"Configuration.Error.Invalid.control.flag.flag", + "\u914D\u7F6E\u9519\u8BEF: \n\t\u65E0\u6548\u7684\u63A7\u5236\u6807\u8BB0, {0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "\u914D\u7F6E\u9519\u8BEF:\n\t\u65E0\u6CD5\u6307\u5B9A{0}\u7684\u591A\u4E2A\u6761\u76EE"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "\u914D\u7F6E\u9519\u8BEF: \n\t\u5E94\u4E3A [{0}], \u8BFB\u53D6\u7684\u662F [\u6587\u4EF6\u7ED3\u5C3E]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "\u914D\u7F6E\u9519\u8BEF: \n\t\u884C {0}: \u5E94\u4E3A [{1}], \u627E\u5230 [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "\u914D\u7F6E\u9519\u8BEF: \n\t\u884C {0}: \u5E94\u4E3A [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "\u914D\u7F6E\u9519\u8BEF: \n\t\u884C {0}: \u7CFB\u7EDF\u5C5E\u6027 [{1}] \u6269\u5C55\u5230\u7A7A\u503C"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","\u7528\u6237\u540D: "}, + {"password.","\u53E3\u4EE4: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "\u8BF7\u8F93\u5165\u5BC6\u94A5\u5E93\u4FE1\u606F"}, + {"Keystore.alias.","\u5BC6\u94A5\u5E93\u522B\u540D: "}, + {"Keystore.password.","\u5BC6\u94A5\u5E93\u53E3\u4EE4: "}, + {"Private.key.password.optional.", + "\u79C1\u6709\u5BC6\u94A5\u53E3\u4EE4 (\u53EF\u9009): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos \u7528\u6237\u540D [{0}]: "}, + {"Kerberos.password.for.username.", + "{0}\u7684 Kerberos \u53E3\u4EE4: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": \u89E3\u6790\u65F6\u51FA\u9519 "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": \u6DFB\u52A0\u6743\u9650\u65F6\u51FA\u9519 "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": \u6DFB\u52A0\u6761\u76EE\u65F6\u51FA\u9519 "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "\u5C1D\u8BD5\u5C06\u6743\u9650\u6DFB\u52A0\u81F3\u53EA\u8BFB\u7684 PermissionCollection"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "\u5E94\u4E3A\u5BC6\u94A5\u5E93\u7C7B\u578B"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u6CA1\u6709\u901A\u914D\u7B26\u540D\u79F0, \u65E0\u6CD5\u4F7F\u7528\u901A\u914D\u7B26\u7C7B\u6307\u5B9A\u4E3B\u7528\u6237"}, + {"expected.codeBase.or.SignedBy", "\u5E94\u4E3A codeBase \u6216 SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "\u53EA\u5141\u8BB8\u57FA\u4E8E\u4E3B\u7528\u6237\u7684\u6388\u6743\u6761\u76EE"}, + {"expected.permission.entry", "\u5E94\u4E3A\u6743\u9650\u6761\u76EE"}, + {"number.", "\u7F16\u53F7 "}, + {"expected.expect.read.end.of.file.", + "\u5E94\u4E3A{0}, \u8BFB\u53D6\u7684\u662F\u6587\u4EF6\u7ED3\u5C3E"}, + {"expected.read.end.of.file", "\u5E94\u4E3A ';', \u8BFB\u53D6\u7684\u662F\u6587\u4EF6\u7ED3\u5C3E"}, + {"line.", "\u884C "}, + {".expected.", ": \u5E94\u4E3A '"}, + {".found.", "', \u627E\u5230 '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [\u4E3B\u7EC4]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [\u8865\u5145\u7EC4]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "\u63D0\u4F9B\u7684\u540D\u79F0\u4E3A\u7A7A\u503C"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/AuthResources_zh_TW.java b/src/sun/security/util/AuthResources_zh_TW.java new file mode 100644 index 00000000..450ca22d --- /dev/null +++ b/src/sun/security/util/AuthResources_zh_TW.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for the following packages: + * + *

      + *
    1. com.sun.security.auth + *
    2. com.sun.security.auth.login + *
    + * + */ +public class AuthResources_zh_TW extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // NT principals + {"invalid.null.input.value", "\u7121\u6548\u7A7A\u503C\u8F38\u5165: {0}"}, + {"NTDomainPrincipal.name", "NTDomainPrincipal: {0}"}, + {"NTNumericCredential.name", "NTNumericCredential: {0}"}, + {"Invalid.NTSid.value", "\u7121\u6548 NTSid \u503C"}, + {"NTSid.name", "NTSid: {0}"}, + {"NTSidDomainPrincipal.name", "NTSidDomainPrincipal: {0}"}, + {"NTSidGroupPrincipal.name", "NTSidGroupPrincipal: {0}"}, + {"NTSidPrimaryGroupPrincipal.name", "NTSidPrimaryGroupPrincipal: {0}"}, + {"NTSidUserPrincipal.name", "NTSidUserPrincipal: {0}"}, + {"NTUserPrincipal.name", "NTUserPrincipal: {0}"}, + + // UnixPrincipals + {"UnixNumericGroupPrincipal.Primary.Group.name", + "UnixNumericGroupPrincipal [\u4E3B\u7FA4\u7D44]: {0}"}, + {"UnixNumericGroupPrincipal.Supplementary.Group.name", + "UnixNumericGroupPrincipal [\u9644\u52A0\u7FA4\u7D44]: {0}"}, + {"UnixNumericUserPrincipal.name", "UnixNumericUserPrincipal: {0}"}, + {"UnixPrincipal.name", "UnixPrincipal: {0}"}, + + // com.sun.security.auth.login.ConfigFile + {"Unable.to.properly.expand.config", "\u7121\u6CD5\u9069\u7576\u5730\u64F4\u5145 {0}"}, + {"extra.config.No.such.file.or.directory.", + "{0} (\u6C92\u6709\u6B64\u6A94\u6848\u6216\u76EE\u9304)"}, + {"Configuration.Error.No.such.file.or.directory", + "\u7D44\u614B\u932F\u8AA4:\n\t\u7121\u6B64\u6A94\u6848\u6216\u76EE\u9304"}, + {"Configuration.Error.Invalid.control.flag.flag", + "\u7D44\u614B\u932F\u8AA4:\n\t\u7121\u6548\u7684\u63A7\u5236\u65D7\u6A19\uFF0C{0}"}, + {"Configuration.Error.Can.not.specify.multiple.entries.for.appName", + "\u7D44\u614B\u932F\u8AA4: \n\t\u7121\u6CD5\u6307\u5B9A\u591A\u91CD\u9805\u76EE {0}"}, + {"Configuration.Error.expected.expect.read.end.of.file.", + "\u7D44\u614B\u932F\u8AA4: \n\t\u9810\u671F\u7684 [{0}], \u8B80\u53D6 [end of file]"}, + {"Configuration.Error.Line.line.expected.expect.found.value.", + "\u7D44\u614B\u932F\u8AA4: \n\t\u884C {0}: \u9810\u671F\u7684 [{1}], \u767C\u73FE [{2}]"}, + {"Configuration.Error.Line.line.expected.expect.", + "\u7D44\u614B\u932F\u8AA4: \n\t\u884C {0}: \u9810\u671F\u7684 [{1}]"}, + {"Configuration.Error.Line.line.system.property.value.expanded.to.empty.value", + "\u7D44\u614B\u932F\u8AA4: \n\t\u884C {0}: \u7CFB\u7D71\u5C6C\u6027 [{1}] \u64F4\u5145\u81F3\u7A7A\u503C"}, + + // com.sun.security.auth.module.JndiLoginModule + {"username.","\u4F7F\u7528\u8005\u540D\u7A31: "}, + {"password.","\u5BC6\u78BC: "}, + + // com.sun.security.auth.module.KeyStoreLoginModule + {"Please.enter.keystore.information", + "\u8ACB\u8F38\u5165\u91D1\u9470\u5132\u5B58\u5EAB\u8CC7\u8A0A"}, + {"Keystore.alias.","\u91D1\u9470\u5132\u5B58\u5EAB\u5225\u540D: "}, + {"Keystore.password.","\u91D1\u9470\u5132\u5B58\u5EAB\u5BC6\u78BC: "}, + {"Private.key.password.optional.", + "\u79C1\u4EBA\u91D1\u9470\u5BC6\u78BC (\u9078\u64C7\u6027\u7684): "}, + + // com.sun.security.auth.module.Krb5LoginModule + {"Kerberos.username.defUsername.", + "Kerberos \u4F7F\u7528\u8005\u540D\u7A31 [{0}]: "}, + {"Kerberos.password.for.username.", + "Kerberos \u5BC6\u78BC {0}: "}, + + /*** EVERYTHING BELOW IS DEPRECATED ***/ + + // com.sun.security.auth.PolicyFile + {".error.parsing.", ": \u5256\u6790\u932F\u8AA4 "}, + {"COLON", ": "}, + {".error.adding.Permission.", ": \u65B0\u589E\u6B0A\u9650\u932F\u8AA4 "}, + {"SPACE", " "}, + {".error.adding.Entry.", ": \u65B0\u589E\u8F38\u5165\u932F\u8AA4 "}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"attempt.to.add.a.Permission.to.a.readonly.PermissionCollection", + "\u8A66\u8457\u65B0\u589E\u6B0A\u9650\u81F3\u552F\u8B80\u7684 PermissionCollection"}, + + // com.sun.security.auth.PolicyParser + {"expected.keystore.type", "\u9810\u671F\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u6C92\u6709\u842C\u7528\u5B57\u5143\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A\u542B\u6709\u842C\u7528\u5B57\u5143\u985E\u5225\u7684 Principal"}, + {"expected.codeBase.or.SignedBy", "\u9810\u671F\u7684 codeBase \u6216 SignedBy"}, + {"only.Principal.based.grant.entries.permitted", + "\u53EA\u5141\u8A31\u4EE5 Principal \u70BA\u57FA\u790E\u7684\u6388\u6B0A\u9805\u76EE"}, + {"expected.permission.entry", "\u9810\u671F\u7684\u6B0A\u9650\u9805\u76EE"}, + {"number.", "\u865F\u78BC "}, + {"expected.expect.read.end.of.file.", + "\u9810\u671F\u70BA {0}, \u8B80\u53D6\u6A94\u6848\u7D50\u5C3E"}, + {"expected.read.end.of.file", "\u9810\u671F\u7684 ';'\uFF0C\u8B80\u53D6\u6A94\u6848\u7D50\u5C3E"}, + {"line.", "\u884C "}, + {".expected.", ": \u9810\u671F '"}, + {".found.", "'\uFF0C\u767C\u73FE '"}, + {"QUOTE", "'"}, + + // SolarisPrincipals + {"SolarisNumericGroupPrincipal.Primary.Group.", + "SolarisNumericGroupPrincipal [\u4E3B\u7FA4\u7D44]: "}, + {"SolarisNumericGroupPrincipal.Supplementary.Group.", + "SolarisNumericGroupPrincipal [\u9644\u52A0\u7FA4\u7D44]: "}, + {"SolarisNumericUserPrincipal.", + "SolarisNumericUserPrincipal: "}, + {"SolarisPrincipal.", "SolarisPrincipal: "}, + // provided.null.name is the NullPointerException message when a + // developer incorrectly passes a null name to the constructor of + // subclasses of java.security.Principal + {"provided.null.name", "\u63D0\u4F9B\u7A7A\u503C\u540D\u7A31"} + + }; + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + public Object[][] getContents() { + return contents; + } +} diff --git a/src/sun/security/util/BitArray.java b/src/sun/security/util/BitArray.java new file mode 100644 index 00000000..ad5ee5f6 --- /dev/null +++ b/src/sun/security/util/BitArray.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +/** + * A packed array of booleans. + * + * @author Joshua Bloch + * @author Douglas Hoover + */ + +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 (repLength > 0) { + repn[repLength - 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 = 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 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()); + + } + + public BitArray truncate() { + for (int i=length-1; i>=0; i--) { + if (get(i)) { + return new BitArray(i+1, Arrays.copyOf(repn, (i + BITS_PER_UNIT)/BITS_PER_UNIT)); + } + } + return new BitArray(1); + } + +} diff --git a/src/sun/security/util/ByteArrayLexOrder.java b/src/sun/security/util/ByteArrayLexOrder.java new file mode 100644 index 00000000..f943bc93 --- /dev/null +++ b/src/sun/security/util/ByteArrayLexOrder.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package sun.security.util; + +import java.util.Comparator; + +/** + * Compare two byte arrays in lexicographical order. + * + * @author D. N. Hoover + */ +public class ByteArrayLexOrder implements Comparator { + + /** + * Perform lexicographical comparison of two byte arrays, + * regarding each byte as unsigned. That is, compare array entries + * in order until they differ--the array with the smaller entry + * is "smaller". If array entries are + * equal till one array ends, then the longer array is "bigger". + * + * @param bytes1 first byte array to compare. + * @param bytes2 second byte array to compare. + * @return negative number if bytes1 < bytes2, 0 if bytes1 == bytes2, + * positive number if bytes1 > bytes2. + * + * @exception ClassCastException + * 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/src/sun/security/util/ByteArrayTagOrder.java b/src/sun/security/util/ByteArrayTagOrder.java new file mode 100644 index 00000000..6ce51efa --- /dev/null +++ b/src/sun/security/util/ByteArrayTagOrder.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/** + * ByteArrayTagOrder: a class for comparing two DER encodings by the + * order of their tags. + * + * @author D. N. Hoover + */ + +package sun.security.util; + +import java.util.Comparator; + +public class ByteArrayTagOrder implements Comparator { + + /** + * Compare two byte arrays, by the order of their tags, + * as defined in ITU-T X.680, sec. 6.4. (First compare + * tag classes, then tag numbers, ignoring the constructivity bit.) + * + * @param bytes1 first byte array to compare. + * @param bytes2 second byte array to compare. + * @return negative number if bytes1 < bytes2, 0 if bytes1 == bytes2, + * positive number if bytes1 > bytes2. + * + * @exception ClassCastException + * 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/src/sun/security/util/Cache.java b/src/sun/security/util/Cache.java new file mode 100644 index 00000000..80373249 --- /dev/null +++ b/src/sun/security/util/Cache.java @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.util.*; +import java.lang.ref.*; + +/** + * Abstract base class and factory for caches. A cache is a key-value mapping. + * It has properties that make it more suitable for caching than a Map. + * + * The factory methods can be used to obtain two different implementations. + * They have the following properties: + * + * . keys and values reside in memory + * + * . keys and values must be non-null + * + * . maximum size. Replacements are made in LRU order. + * + * . optional lifetime, specified in seconds. + * + * . safe for concurrent use by multiple threads + * + * . values are held by either standard references or via SoftReferences. + * SoftReferences have the advantage that they are automatically cleared + * by the garbage collector in response to memory demand. This makes it + * possible to simple set the maximum size to a very large value and let + * the GC automatically size the cache dynamically depending on the + * amount of available memory. + * + * However, note that because of the way SoftReferences are implemented in + * HotSpot at the moment, this may not work perfectly as it clears them fairly + * eagerly. Performance may be improved if the Java heap size is set to larger + * value using e.g. java -ms64M -mx128M foo.Test + * + * Cache sizing: the memory cache is implemented on top of a LinkedHashMap. + * In its current implementation, the number of buckets (NOT entries) in + * (Linked)HashMaps is always a power of two. It is recommended to set the + * maximum cache size to value that uses those buckets fully. For example, + * if a cache with somewhere between 500 and 1000 entries is desired, a + * maximum size of 750 would be a good choice: try 1024 buckets, with a + * load factor of 0.75f, the number of entries can be calculated as + * buckets / 4 * 3. As mentioned above, with a SoftReference cache, it is + * generally reasonable to set the size to a fairly large value. + * + * @author Andreas Sterbenz + */ +public abstract class Cache { + + protected Cache() { + // empty + } + + /** + * Return the number of currently valid entries in the cache. + */ + public abstract int size(); + + /** + * Remove all entries from the cache. + */ + public abstract void clear(); + + /** + * Add an entry to the cache. + */ + public abstract void put(K key, V value); + + /** + * Get a value from the cache. + */ + public abstract V get(Object key); + + /** + * Remove an entry from the cache. + */ + public abstract void remove(Object key); + + /** + * Set the maximum size. + */ + public abstract void setCapacity(int size); + + /** + * Set the timeout(in seconds). + */ + public abstract void setTimeout(int timeout); + + /** + * accept a visitor + */ + public abstract void accept(CacheVisitor visitor); + + /** + * Return a new memory cache with the specified maximum size, unlimited + * lifetime for entries, with the values held by SoftReferences. + */ + public static Cache newSoftMemoryCache(int size) { + return new MemoryCache<>(true, size); + } + + /** + * Return a new memory cache with the specified maximum size, the + * specified maximum lifetime (in seconds), with the values held + * by SoftReferences. + */ + public static Cache newSoftMemoryCache(int size, int timeout) { + return new MemoryCache<>(true, size, timeout); + } + + /** + * Return a new memory cache with the specified maximum size, unlimited + * lifetime for entries, with the values held by standard references. + */ + public static Cache newHardMemoryCache(int size) { + return new MemoryCache<>(false, size); + } + + /** + * Return a dummy cache that does nothing. + */ + @SuppressWarnings("unchecked") + public static Cache newNullCache() { + return (Cache) NullCache.INSTANCE; + } + + /** + * Return a new memory cache with the specified maximum size, the + * specified maximum lifetime (in seconds), with the values held + * by standard references. + */ + public static Cache newHardMemoryCache(int size, int timeout) { + return new MemoryCache<>(false, size, timeout); + } + + /** + * Utility class that wraps a byte array and implements the equals() + * and hashCode() contract in a way suitable for Maps and caches. + */ + public static class EqualByteArray { + + private final byte[] b; + private volatile int hash; + + public EqualByteArray(byte[] b) { + this.b = b; + } + + public int hashCode() { + int h = hash; + if (h == 0) { + h = b.length + 1; + for (int i = 0; i < b.length; i++) { + h += (b[i] & 0xff) * 37; + } + hash = h; + } + return h; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof EqualByteArray == false) { + return false; + } + EqualByteArray other = (EqualByteArray)obj; + return Arrays.equals(this.b, other.b); + } + } + + public interface CacheVisitor { + public void visit(Map map); + } + +} + +class NullCache extends Cache { + + final static Cache INSTANCE = new NullCache<>(); + + private NullCache() { + // empty + } + + public int size() { + return 0; + } + + public void clear() { + // empty + } + + public void put(K key, V value) { + // empty + } + + public V get(Object key) { + return null; + } + + public void remove(Object key) { + // empty + } + + public void setCapacity(int size) { + // empty + } + + public void setTimeout(int timeout) { + // empty + } + + public void accept(CacheVisitor visitor) { + // empty + } + +} + +class MemoryCache extends Cache { + + private final static float LOAD_FACTOR = 0.75f; + + // XXXX + private final static boolean DEBUG = false; + + private final Map> cacheMap; + private int maxSize; + private long lifetime; + + // ReferenceQueue is of type V instead of Cache + // to allow SoftCacheEntry to extend SoftReference + private final ReferenceQueue queue; + + public MemoryCache(boolean soft, int maxSize) { + this(soft, maxSize, 0); + } + + public MemoryCache(boolean soft, int maxSize, int lifetime) { + this.maxSize = maxSize; + this.lifetime = lifetime * 1000; + if (soft) + this.queue = new ReferenceQueue<>(); + else + this.queue = null; + + int buckets = (int)(maxSize / LOAD_FACTOR) + 1; + cacheMap = new LinkedHashMap<>(buckets, LOAD_FACTOR, true); + } + + /** + * Empty the reference queue and remove all corresponding entries + * from the cache. + * + * This method should be called at the beginning of each public + * method. + */ + private void emptyQueue() { + if (queue == null) { + return; + } + int startSize = cacheMap.size(); + while (true) { + @SuppressWarnings("unchecked") + CacheEntry entry = (CacheEntry)queue.poll(); + if (entry == null) { + break; + } + K key = entry.getKey(); + if (key == null) { + // key is null, entry has already been removed + continue; + } + CacheEntry currentEntry = cacheMap.remove(key); + // check if the entry in the map corresponds to the expired + // entry. If not, readd the entry + if ((currentEntry != null) && (entry != currentEntry)) { + cacheMap.put(key, currentEntry); + } + } + if (DEBUG) { + int endSize = cacheMap.size(); + if (startSize != endSize) { + System.out.println("*** Expunged " + (startSize - endSize) + + " entries, " + endSize + " entries left"); + } + } + } + + /** + * Scan all entries and remove all expired ones. + */ + private void expungeExpiredEntries() { + emptyQueue(); + if (lifetime == 0) { + return; + } + int cnt = 0; + long time = System.currentTimeMillis(); + for (Iterator> t = cacheMap.values().iterator(); + t.hasNext(); ) { + CacheEntry entry = t.next(); + if (entry.isValid(time) == false) { + t.remove(); + cnt++; + } + } + if (DEBUG) { + if (cnt != 0) { + System.out.println("Removed " + cnt + + " expired entries, remaining " + cacheMap.size()); + } + } + } + + public synchronized int size() { + expungeExpiredEntries(); + return cacheMap.size(); + } + + public synchronized void clear() { + if (queue != null) { + // if this is a SoftReference cache, first invalidate() all + // entries so that GC does not have to enqueue them + for (CacheEntry entry : cacheMap.values()) { + entry.invalidate(); + } + while (queue.poll() != null) { + // empty + } + } + cacheMap.clear(); + } + + public synchronized void put(K key, V value) { + emptyQueue(); + long expirationTime = (lifetime == 0) ? 0 : + System.currentTimeMillis() + lifetime; + CacheEntry newEntry = newEntry(key, value, expirationTime, queue); + CacheEntry oldEntry = cacheMap.put(key, newEntry); + if (oldEntry != null) { + oldEntry.invalidate(); + return; + } + if (maxSize > 0 && cacheMap.size() > maxSize) { + expungeExpiredEntries(); + if (cacheMap.size() > maxSize) { // still too large? + Iterator> t = cacheMap.values().iterator(); + CacheEntry lruEntry = t.next(); + if (DEBUG) { + System.out.println("** Overflow removal " + + lruEntry.getKey() + " | " + lruEntry.getValue()); + } + t.remove(); + lruEntry.invalidate(); + } + } + } + + public synchronized V get(Object key) { + emptyQueue(); + CacheEntry entry = cacheMap.get(key); + if (entry == null) { + return null; + } + long time = (lifetime == 0) ? 0 : System.currentTimeMillis(); + if (entry.isValid(time) == false) { + if (DEBUG) { + System.out.println("Ignoring expired entry"); + } + cacheMap.remove(key); + return null; + } + return entry.getValue(); + } + + public synchronized void remove(Object key) { + emptyQueue(); + CacheEntry entry = cacheMap.remove(key); + if (entry != null) { + entry.invalidate(); + } + } + + public synchronized void setCapacity(int size) { + expungeExpiredEntries(); + if (size > 0 && cacheMap.size() > size) { + Iterator> t = cacheMap.values().iterator(); + for (int i = cacheMap.size() - size; i > 0; i--) { + CacheEntry lruEntry = t.next(); + if (DEBUG) { + System.out.println("** capacity reset removal " + + lruEntry.getKey() + " | " + lruEntry.getValue()); + } + t.remove(); + lruEntry.invalidate(); + } + } + + maxSize = size > 0 ? size : 0; + + if (DEBUG) { + System.out.println("** capacity reset to " + size); + } + } + + public synchronized void setTimeout(int timeout) { + emptyQueue(); + lifetime = timeout > 0 ? timeout * 1000L : 0L; + + if (DEBUG) { + System.out.println("** lifetime reset to " + timeout); + } + } + + // it is a heavyweight method. + public synchronized void accept(CacheVisitor visitor) { + expungeExpiredEntries(); + Map cached = getCachedEntries(); + + visitor.visit(cached); + } + + private Map getCachedEntries() { + Map kvmap = new HashMap<>(cacheMap.size()); + + for (CacheEntry entry : cacheMap.values()) { + kvmap.put(entry.getKey(), entry.getValue()); + } + + return kvmap; + } + + protected CacheEntry newEntry(K key, V value, + long expirationTime, ReferenceQueue queue) { + if (queue != null) { + return new SoftCacheEntry<>(key, value, expirationTime, queue); + } else { + return new HardCacheEntry<>(key, value, expirationTime); + } + } + + private static interface CacheEntry { + + boolean isValid(long currentTime); + + void invalidate(); + + K getKey(); + + V getValue(); + + } + + private static class HardCacheEntry implements CacheEntry { + + private K key; + private V value; + private long expirationTime; + + HardCacheEntry(K key, V value, long expirationTime) { + this.key = key; + this.value = value; + this.expirationTime = expirationTime; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public boolean isValid(long currentTime) { + boolean valid = (currentTime <= expirationTime); + if (valid == false) { + invalidate(); + } + return valid; + } + + public void invalidate() { + key = null; + value = null; + expirationTime = -1; + } + } + + private static class SoftCacheEntry + extends SoftReference + implements CacheEntry { + + private K key; + private long expirationTime; + + SoftCacheEntry(K key, V value, long expirationTime, + ReferenceQueue queue) { + super(value, queue); + this.key = key; + this.expirationTime = expirationTime; + } + + public K getKey() { + return key; + } + + public V getValue() { + return get(); + } + + public boolean isValid(long currentTime) { + boolean valid = (currentTime <= expirationTime) && (get() != null); + if (valid == false) { + invalidate(); + } + return valid; + } + + public void invalidate() { + clear(); + key = null; + expirationTime = -1; + } + } + +} diff --git a/src/sun/security/util/Debug.java b/src/sun/security/util/Debug.java new file mode 100644 index 00000000..8dd0c3b1 --- /dev/null +++ b/src/sun/security/util/Debug.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.math.BigInteger; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.Locale; + +/** + * A utility class for debuging. + * + * @author Roland Schemers + */ +public class Debug { + + private String prefix; + + private static String args; + + static { + args = java.security.AccessController.doPrivileged + (new sun.security.action.GetPropertyAction + ("java.security.debug")); + + String args2 = java.security.AccessController.doPrivileged + (new sun.security.action.GetPropertyAction + ("java.security.auth.debug")); + + if (args == null) { + args = args2; + } else { + if (args2 != null) + args = args + "," + args2; + } + + if (args != null) { + args = marshal(args); + if (args.equals("help")) { + Help(); + } + } + } + + public static void Help() + { + System.err.println(); + System.err.println("all turn on all debugging"); + System.err.println("access print all checkPermission results"); + System.err.println("certpath PKIX CertPathBuilder and"); + System.err.println(" CertPathValidator debugging"); + System.err.println("combiner SubjectDomainCombiner debugging"); + System.err.println("gssloginconfig"); + System.err.println(" GSS LoginConfigImpl debugging"); + System.err.println("configfile JAAS ConfigFile loading"); + System.err.println("configparser JAAS ConfigFile parsing"); + System.err.println("jar jar verification"); + System.err.println("logincontext login context results"); + System.err.println("jca JCA engine class debugging"); + System.err.println("policy loading and granting"); + System.err.println("provider security provider debugging"); + System.err.println("pkcs11 PKCS11 session manager debugging"); + System.err.println("pkcs11keystore"); + System.err.println(" PKCS11 KeyStore debugging"); + System.err.println("sunpkcs11 SunPKCS11 provider debugging"); + System.err.println("scl permissions SecureClassLoader assigns"); + System.err.println("ts timestamping"); + System.err.println(); + System.err.println("The following can be used with access:"); + System.err.println(); + System.err.println("stack include stack trace"); + System.err.println("domain dump all domains in context"); + System.err.println("failure before throwing exception, dump stack"); + System.err.println(" and domain that didn't have permission"); + System.err.println(); + System.err.println("The following can be used with stack and domain:"); + System.err.println(); + System.err.println("permission="); + System.err.println(" only dump output if specified permission"); + System.err.println(" is being checked"); + System.err.println("codebase="); + System.err.println(" only dump output if specified codebase"); + System.err.println(" is being checked"); + System.err.println(); + System.err.println("The following can be used with provider:"); + System.err.println(); + System.err.println("engine="); + System.err.println(" only dump output for the specified list"); + System.err.println(" of JCA engines. Supported values:"); + System.err.println(" Cipher, KeyAgreement, KeyGenerator,"); + System.err.println(" KeyPairGenerator, KeyStore, Mac,"); + System.err.println(" MessageDigest, SecureRandom, Signature."); + System.err.println(); + System.err.println("Note: Separate multiple options with a comma"); + System.exit(0); + } + + + /** + * Get a Debug object corresponding to whether or not the given + * option is set. Set the prefix to be the same as option. + */ + + public static Debug getInstance(String option) + { + return getInstance(option, option); + } + + /** + * Get a Debug object corresponding to whether or not the given + * option is set. Set the prefix to be prefix. + */ + public static Debug getInstance(String option, String prefix) + { + if (isOn(option)) { + Debug d = new Debug(); + d.prefix = prefix; + return d; + } else { + return null; + } + } + + /** + * True if the system property "security.debug" contains the + * string "option". + */ + public static boolean isOn(String option) + { + if (args == null) + return false; + else { + if (args.indexOf("all") != -1) + return true; + else + return (args.indexOf(option) != -1); + } + } + + /** + * print a message to stderr that is prefixed with the prefix + * created from the call to getInstance. + */ + + public void println(String message) + { + System.err.println(prefix + ": "+message); + } + + /** + * print a blank line to stderr that is prefixed with the prefix. + */ + + public void println() + { + System.err.println(prefix + ":"); + } + + /** + * print a message to stderr that is prefixed with the prefix. + */ + + public static void println(String prefix, String message) + { + System.err.println(prefix + ": "+message); + } + + /** + * return a hexadecimal printed representation of the specified + * BigInteger object. 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 static String toHexString(BigInteger b) { + String hexValue = b.toString(16); + StringBuffer buf = new StringBuffer(hexValue.length()*2); + + if (hexValue.startsWith("-")) { + buf.append(" -"); + hexValue = hexValue.substring(1); + } else { + buf.append(" "); // four spaces + } + if ((hexValue.length()%2) != 0) { + // add back the leading 0 + hexValue = "0" + hexValue; + } + int i=0; + while (i < hexValue.length()) { + // one byte at a time + buf.append(hexValue.substring(i, i+2)); + i+=2; + if (i!= hexValue.length()) { + if ((i%64) == 0) { + buf.append("\n "); // line after eight words + } else if (i%8 == 0) { + buf.append(" "); // space between words + } + } + } + return buf.toString(); + } + + /** + * change a string into lower case except permission classes and URLs. + */ + private static String marshal(String args) { + if (args != null) { + StringBuffer target = new StringBuffer(); + StringBuffer source = new StringBuffer(args); + + // obtain the "permission=" options + // the syntax of classname: IDENTIFIER.IDENTIFIER + // the regular express to match a class name: + // "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*" + String keyReg = "[Pp][Ee][Rr][Mm][Ii][Ss][Ss][Ii][Oo][Nn]="; + String keyStr = "permission="; + String reg = keyReg + + "[a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*"; + Pattern pattern = Pattern.compile(reg); + Matcher matcher = pattern.matcher(source); + StringBuffer left = new StringBuffer(); + while (matcher.find()) { + String matched = matcher.group(); + target.append(matched.replaceFirst(keyReg, keyStr)); + target.append(" "); + + // delete the matched sequence + matcher.appendReplacement(left, ""); + } + matcher.appendTail(left); + source = left; + + // obtain the "codebase=" options + // the syntax of URL is too flexible, and here assumes that the + // URL contains no space, comma(','), and semicolon(';'). That + // also means those characters also could be used as separator + // after codebase option. + // However, the assumption is incorrect in some special situation + // when the URL contains comma or semicolon + keyReg = "[Cc][Oo][Dd][Ee][Bb][Aa][Ss][Ee]="; + keyStr = "codebase="; + reg = keyReg + "[^, ;]*"; + pattern = Pattern.compile(reg); + matcher = pattern.matcher(source); + left = new StringBuffer(); + while (matcher.find()) { + String matched = matcher.group(); + target.append(matched.replaceFirst(keyReg, keyStr)); + target.append(" "); + + // delete the matched sequence + matcher.appendReplacement(left, ""); + } + matcher.appendTail(left); + source = left; + + // convert the rest to lower-case characters + target.append(source.toString().toLowerCase(Locale.ENGLISH)); + + return target.toString(); + } + + return null; + } + + private final static char[] hexDigits = "0123456789abcdef".toCharArray(); + + public static String toString(byte[] b) { + if (b == null) { + return "(null)"; + } + StringBuilder sb = new StringBuilder(b.length * 3); + for (int i = 0; i < b.length; i++) { + int k = b[i] & 0xff; + if (i != 0) { + sb.append(':'); + } + sb.append(hexDigits[k >>> 4]); + sb.append(hexDigits[k & 0xf]); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/util/DerEncoder.java b/src/sun/security/util/DerEncoder.java new file mode 100644 index 00000000..fadad5ff --- /dev/null +++ b/src/sun/security/util/DerEncoder.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.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. + * + * @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/src/sun/security/util/DerIndefLenConverter.java b/src/sun/security/util/DerIndefLenConverter.java new file mode 100644 index 00000000..cbd5ecc0 --- /dev/null +++ b/src/sun/security/util/DerIndefLenConverter.java @@ -0,0 +1,357 @@ +/* + * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * A package private utility class to convert indefinite length DER + * encoded byte arrays to definite length DER encoded byte arrays. + * + * This assumes that the basic data structure is "tag, length, value" + * triplet. In the case where the length is "indefinite", terminating + * end-of-contents bytes are expected. + * + * @author Hemma Prafullchandra + */ +class DerIndefLenConverter { + + private static final int TAG_MASK = 0x1f; // bits 5-1 + private static final int FORM_MASK = 0x20; // bits 6 + private static final int CLASS_MASK = 0xC0; // bits 8 and 7 + + private static final int LEN_LONG = 0x80; // bit 8 set + private static final int LEN_MASK = 0x7f; // bits 7 - 1 + private static final int SKIP_EOC_BYTES = 2; + + private byte[] data, newData; + private int newDataPos, dataPos, dataSize, index; + private int unresolved = 0; + + private ArrayList ndefsList = new ArrayList(); + + private int numOfTotalLenBytes = 0; + + private boolean isEOC(int tag) { + return (((tag & TAG_MASK) == 0x00) && // EOC + ((tag & FORM_MASK) == 0x00) && // primitive + ((tag & CLASS_MASK) == 0x00)); // universal + } + + // if bit 8 is set then it implies either indefinite length or long form + static boolean isLongForm(int lengthByte) { + return ((lengthByte & LEN_LONG) == LEN_LONG); + } + + /* + * Default package private constructor + */ + DerIndefLenConverter() { } + + /** + * Checks whether the given length byte is of the form + * Indefinite. + * + * @param lengthByte the length byte from a DER encoded + * object. + * @return true if the byte is of Indefinite form otherwise + * returns false. + */ + static boolean isIndefinite(int lengthByte) { + return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0)); + } + + /** + * Parse the tag and if it is an end-of-contents tag then + * add the current position to the eocList vector. + */ + private void parseTag() throws IOException { + if (dataPos == dataSize) + return; + if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) { + int numOfEncapsulatedLenBytes = 0; + Object elem = null; + int index; + for (index = ndefsList.size()-1; index >= 0; index--) { + // Determine the first element in the vector that does not + // have a matching EOC + elem = ndefsList.get(index); + if (elem instanceof Integer) { + break; + } else { + numOfEncapsulatedLenBytes += ((byte[])elem).length - 3; + } + } + if (index < 0) { + throw new IOException("EOC does not have matching " + + "indefinite-length tag"); + } + int sectionLen = dataPos - ((Integer)elem).intValue() + + numOfEncapsulatedLenBytes; + byte[] sectionLenBytes = getLengthBytes(sectionLen); + ndefsList.set(index, sectionLenBytes); + unresolved--; + + // Add the number of bytes required to represent this section + // to the total number of length bytes, + // and subtract the indefinite-length tag (1 byte) and + // EOC bytes (2 bytes) for this section + numOfTotalLenBytes += (sectionLenBytes.length - 3); + } + dataPos++; + } + + /** + * Write the tag and if it is an end-of-contents tag + * then skip the tag and its 1 byte length of zero. + */ + private void writeTag() { + if (dataPos == dataSize) + return; + int tag = data[dataPos++]; + if (isEOC(tag) && (data[dataPos] == 0)) { + dataPos++; // skip length + writeTag(); + } else + newData[newDataPos++] = (byte)tag; + } + + /** + * Parse the length and if it is an indefinite length then add + * the current position to the ndefsList vector. + */ + private int parseLength() throws IOException { + int curLen = 0; + if (dataPos == dataSize) + return curLen; + int lenByte = data[dataPos++] & 0xff; + if (isIndefinite(lenByte)) { + ndefsList.add(new Integer(dataPos)); + unresolved++; + return curLen; + } + if (isLongForm(lenByte)) { + lenByte &= LEN_MASK; + if (lenByte > 4) { + throw new IOException("Too much data"); + } + if ((dataSize - dataPos) < (lenByte + 1)) { + throw new IOException("Too little data"); + } + for (int i = 0; i < lenByte; i++) { + curLen = (curLen << 8) + (data[dataPos++] & 0xff); + } + if (curLen < 0) { + throw new IOException("Invalid length bytes"); + } + } else { + curLen = (lenByte & LEN_MASK); + } + return curLen; + } + + /** + * Write the length and if it is an indefinite length + * then calculate the definite length from the positions + * of the indefinite length and its matching EOC terminator. + * Then, write the value. + */ + private void writeLengthAndValue() throws IOException { + if (dataPos == dataSize) + return; + int curLen = 0; + int lenByte = data[dataPos++] & 0xff; + if (isIndefinite(lenByte)) { + byte[] lenBytes = (byte[])ndefsList.get(index++); + System.arraycopy(lenBytes, 0, newData, newDataPos, + lenBytes.length); + newDataPos += lenBytes.length; + return; + } + if (isLongForm(lenByte)) { + lenByte &= LEN_MASK; + for (int i = 0; i < lenByte; i++) { + curLen = (curLen << 8) + (data[dataPos++] & 0xff); + } + if (curLen < 0) { + throw new IOException("Invalid length bytes"); + } + } else { + curLen = (lenByte & LEN_MASK); + } + writeLength(curLen); + writeValue(curLen); + } + + private void writeLength(int curLen) { + if (curLen < 128) { + newData[newDataPos++] = (byte)curLen; + + } else if (curLen < (1 << 8)) { + newData[newDataPos++] = (byte)0x81; + newData[newDataPos++] = (byte)curLen; + + } else if (curLen < (1 << 16)) { + newData[newDataPos++] = (byte)0x82; + newData[newDataPos++] = (byte)(curLen >> 8); + newData[newDataPos++] = (byte)curLen; + + } else if (curLen < (1 << 24)) { + newData[newDataPos++] = (byte)0x83; + newData[newDataPos++] = (byte)(curLen >> 16); + newData[newDataPos++] = (byte)(curLen >> 8); + newData[newDataPos++] = (byte)curLen; + + } else { + newData[newDataPos++] = (byte)0x84; + newData[newDataPos++] = (byte)(curLen >> 24); + newData[newDataPos++] = (byte)(curLen >> 16); + newData[newDataPos++] = (byte)(curLen >> 8); + newData[newDataPos++] = (byte)curLen; + } + } + + private byte[] getLengthBytes(int curLen) { + byte[] lenBytes; + int index = 0; + + if (curLen < 128) { + lenBytes = new byte[1]; + lenBytes[index++] = (byte)curLen; + + } else if (curLen < (1 << 8)) { + lenBytes = new byte[2]; + lenBytes[index++] = (byte)0x81; + lenBytes[index++] = (byte)curLen; + + } else if (curLen < (1 << 16)) { + lenBytes = new byte[3]; + lenBytes[index++] = (byte)0x82; + lenBytes[index++] = (byte)(curLen >> 8); + lenBytes[index++] = (byte)curLen; + + } else if (curLen < (1 << 24)) { + lenBytes = new byte[4]; + lenBytes[index++] = (byte)0x83; + lenBytes[index++] = (byte)(curLen >> 16); + lenBytes[index++] = (byte)(curLen >> 8); + lenBytes[index++] = (byte)curLen; + + } else { + lenBytes = new byte[5]; + lenBytes[index++] = (byte)0x84; + lenBytes[index++] = (byte)(curLen >> 24); + lenBytes[index++] = (byte)(curLen >> 16); + lenBytes[index++] = (byte)(curLen >> 8); + lenBytes[index++] = (byte)curLen; + } + + return lenBytes; + } + + // Returns the number of bytes needed to represent the given length + // in ASN.1 notation + private int getNumOfLenBytes(int len) { + int numOfLenBytes = 0; + + if (len < 128) { + numOfLenBytes = 1; + } else if (len < (1 << 8)) { + numOfLenBytes = 2; + } else if (len < (1 << 16)) { + numOfLenBytes = 3; + } else if (len < (1 << 24)) { + numOfLenBytes = 4; + } else { + numOfLenBytes = 5; + } + return numOfLenBytes; + } + + /** + * Parse the value; + */ + private void parseValue(int curLen) { + dataPos += curLen; + } + + /** + * Write the value; + */ + private void writeValue(int curLen) { + for (int i=0; i < curLen; i++) + newData[newDataPos++] = data[dataPos++]; + } + + /** + * Converts a indefinite length DER encoded byte array to + * a definte length DER encoding. + * + * @param indefData the byte array holding the indefinite + * length encoding. + * @return the byte array containing the definite length + * DER encoding. + * @exception IOException on parsing or re-writing errors. + */ + byte[] convert(byte[] indefData) throws IOException { + data = indefData; + dataPos=0; index=0; + dataSize = data.length; + int len=0; + int unused = 0; + + // parse and set up the vectors of all the indefinite-lengths + while (dataPos < dataSize) { + parseTag(); + len = parseLength(); + parseValue(len); + if (unresolved == 0) { + unused = dataSize - dataPos; + dataSize = dataPos; + break; + } + } + + if (unresolved != 0) { + throw new IOException("not all indef len BER resolved"); + } + + newData = new byte[dataSize + numOfTotalLenBytes + unused]; + dataPos=0; newDataPos=0; index=0; + + // write out the new byte array replacing all the indefinite-lengths + // and EOCs + while (dataPos < dataSize) { + writeTag(); + writeLengthAndValue(); + } + System.arraycopy(indefData, dataSize, + newData, dataSize + numOfTotalLenBytes, unused); + + return newData; + } +} diff --git a/src/sun/security/util/DerInputBuffer.java b/src/sun/security/util/DerInputBuffer.java new file mode 100644 index 00000000..034d92c0 --- /dev/null +++ b/src/sun/security/util/DerInputBuffer.java @@ -0,0 +1,432 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.util.Date; +import sun.util.calendar.CalendarDate; +import sun.util.calendar.CalendarSystem; + +/** + * 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 + * anyone who could violate the "typed value stream" DER model and hence + * corrupt the input stream of DER values. + * + * @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; + } + + /** + * Returns a hashcode for this DerInputBuffer. + * + * @return a hashcode for this DerInputBuffer. + */ + public int hashCode() { + int retval = 0; + + int len = available(); + int p = pos; + + for (int i = 0; i < len; i++) + retval += buf[p + i] * i; + return retval; + } + + void truncate(int len) throws IOException { + if (len > available()) + throw new IOException("insufficient data"); + count = pos + len; + } + + /** + * Returns the integer which takes up the specified number + * of bytes in this buffer as a BigInteger. + * @param len the number of bytes to use. + * @param makePositive whether to always return a positive value, + * irrespective of actual encoding + * @return the integer as a BigInteger. + */ + BigInteger getBigInteger(int len, boolean makePositive) throws IOException { + if (len > available()) + throw new IOException("short read of integer"); + + if (len == 0) { + throw new IOException("Invalid encoding: zero length Int value"); + } + + byte[] bytes = new byte[len]; + + System.arraycopy(buf, pos, bytes, 0, len); + skip(len); + + if (makePositive) { + return new BigInteger(1, bytes); + } else { + return new BigInteger(bytes); + } + } + + /** + * Returns the integer which takes up the specified number + * of bytes in this buffer. + * @throws IOException if the result is not within the valid + * range for integer, i.e. between Integer.MIN_VALUE and + * Integer.MAX_VALUE. + * @param len the number of bytes to use. + * @return the integer. + */ + public int getInteger(int len) throws IOException { + + BigInteger result = getBigInteger(len, false); + if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) { + throw new IOException("Integer below minimum valid value"); + } + if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) { + throw new IOException("Integer exceeds maximum valid value"); + } + return result.intValue(); + } + + /** + * Returns the bit string which takes up the specified + * number of bytes in this buffer. + */ + public byte[] getBitString(int len) throws IOException { + if (len > available()) + throw new IOException("short read of bit string"); + + if (len == 0) { + throw new IOException("Invalid encoding: zero length bit string"); + } + + int numOfPadBits = buf[pos]; + if ((numOfPadBits < 0) || (numOfPadBits > 7)) { + throw new IOException("Invalid number of padding bits"); + } + // minus the first byte which indicates the number of padding bits + byte[] retval = new byte[len - 1]; + System.arraycopy(buf, pos + 1, retval, 0, len - 1); + if (numOfPadBits != 0) { + // get rid of the padding bits + retval[len - 2] &= (0xff << numOfPadBits); + } + skip(len); + return retval; + } + + /** + * Returns the bit string which takes up the rest of this buffer. + */ + byte[] getBitString() throws IOException { + return getBitString(available()); + } + + /** + * Returns the bit string which takes up the rest of this buffer. + * The bit string need not be byte-aligned. + */ + BitArray getUnalignedBitString() throws IOException { + 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(); + int unusedBits = buf[pos] & 0xff; + if (unusedBits > 7 ) { + throw new IOException("Invalid value for unused bits: " + unusedBits); + } + byte[] bits = new byte[len - 1]; + // number of valid bits + int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits; + + System.arraycopy(buf, pos + 1, bits, 0, len - 1); + + BitArray bitArray = new BitArray(length, bits); + pos = count; + return bitArray; + } + + /** + * Returns the UTC Time value that takes up the specified number + * of bytes in this buffer. + * @param len the number of bytes to use + */ + public Date getUTCTime(int len) throws IOException { + if (len > available()) + throw new IOException("short read of DER UTC Time"); + + if (len < 11 || len > 17) + throw new IOException("DER UTC Time length error"); + + return getTime(len, false); + } + + /** + * Returns the Generalized Time value that takes up the specified + * number of bytes in this buffer. + * @param len the number of bytes to use + */ + public Date getGeneralizedTime(int len) throws IOException { + if (len > available()) + throw new IOException("short read of DER Generalized Time"); + + if (len < 13 || len > 23) + throw new IOException("DER Generalized Time length error"); + + return getTime(len, true); + + } + + /** + * Private helper routine to extract time from the der value. + * @param len the number of bytes to use + * @param generalized true if Generalized Time is to be read, false + * if UTC Time is to be read. + */ + private Date getTime(int len, boolean generalized) throws IOException { + + /* + * UTC time encoded as ASCII chars: + * YYMMDDhhmmZ + * YYMMDDhhmmssZ + * YYMMDDhhmm+hhmm + * YYMMDDhhmm-hhmm + * YYMMDDhhmmss+hhmm + * YYMMDDhhmmss-hhmm + * UTC Time is broken in storing only two digits of year. + * If YY < 50, we assume 20YY; + * if YY >= 50, we assume 19YY, as per RFC 3280. + * + * Generalized time has a four-digit year and allows any + * precision specified in ISO 8601. However, for our purposes, + * we will only allow the same format as UTC time, except that + * fractional seconds (millisecond precision) are supported. + */ + + int year, month, day, hour, minute, second, millis; + String type = null; + + if (generalized) { + type = "Generalized"; + year = 1000 * Character.digit((char)buf[pos++], 10); + year += 100 * Character.digit((char)buf[pos++], 10); + year += 10 * Character.digit((char)buf[pos++], 10); + year += Character.digit((char)buf[pos++], 10); + len -= 2; // For the two extra YY + } else { + type = "UTC"; + year = 10 * Character.digit((char)buf[pos++], 10); + year += Character.digit((char)buf[pos++], 10); + + if (year < 50) // origin 2000 + year += 2000; + else + year += 1900; // origin 1900 + } + + month = 10 * Character.digit((char)buf[pos++], 10); + month += Character.digit((char)buf[pos++], 10); + + day = 10 * Character.digit((char)buf[pos++], 10); + day += Character.digit((char)buf[pos++], 10); + + hour = 10 * Character.digit((char)buf[pos++], 10); + hour += Character.digit((char)buf[pos++], 10); + + minute = 10 * Character.digit((char)buf[pos++], 10); + minute += Character.digit((char)buf[pos++], 10); + + len -= 10; // YYMMDDhhmm + + /* + * 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. + */ + + millis = 0; + if (len > 2 && len < 12) { + second = 10 * Character.digit((char)buf[pos++], 10); + second += Character.digit((char)buf[pos++], 10); + len -= 2; + // handle fractional seconds (if present) + if (buf[pos] == '.' || buf[pos] == ',') { + len --; + pos++; + // handle upto milisecond precision only + int precision = 0; + int peek = pos; + while (buf[peek] != 'Z' && + buf[peek] != '+' && + buf[peek] != '-') { + peek++; + precision++; + } + switch (precision) { + case 3: + millis += 100 * Character.digit((char)buf[pos++], 10); + millis += 10 * Character.digit((char)buf[pos++], 10); + millis += Character.digit((char)buf[pos++], 10); + break; + case 2: + millis += 100 * Character.digit((char)buf[pos++], 10); + millis += 10 * Character.digit((char)buf[pos++], 10); + break; + case 1: + millis += 100 * Character.digit((char)buf[pos++], 10); + break; + default: + throw new IOException("Parse " + type + + " time, unsupported precision for seconds value"); + } + len -= precision; + } + } else + second = 0; + + if (month == 0 || day == 0 + || month > 12 || day > 31 + || hour >= 24 || minute >= 60 || second >= 60) + throw new IOException("Parse " + type + " time, invalid format"); + + /* + * Generalized time can theoretically allow any precision, + * but we're not supporting that. + */ + CalendarSystem gcal = CalendarSystem.getGregorianCalendar(); + CalendarDate date = gcal.newCalendarDate(null); // no time zone + date.setDate(year, month, day); + date.setTimeOfDay(hour, minute, second, millis); + long time = gcal.getTime(date); + + /* + * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm + */ + if (! (len == 1 || len == 5)) + throw new IOException("Parse " + type + " time, invalid offset"); + + int hr, min; + + switch (buf[pos++]) { + case '+': + hr = 10 * Character.digit((char)buf[pos++], 10); + hr += Character.digit((char)buf[pos++], 10); + min = 10 * Character.digit((char)buf[pos++], 10); + min += Character.digit((char)buf[pos++], 10); + + if (hr >= 24 || min >= 60) + throw new IOException("Parse " + type + " time, +hhmm"); + + time -= ((hr * 60) + min) * 60 * 1000; + break; + + case '-': + hr = 10 * Character.digit((char)buf[pos++], 10); + hr += Character.digit((char)buf[pos++], 10); + min = 10 * Character.digit((char)buf[pos++], 10); + min += Character.digit((char)buf[pos++], 10); + + if (hr >= 24 || min >= 60) + throw new IOException("Parse " + type + " time, -hhmm"); + + time += ((hr * 60) + min) * 60 * 1000; + break; + + case 'Z': + break; + + default: + throw new IOException("Parse " + type + " time, garbage offset"); + } + return new Date(time); + } +} diff --git a/src/sun/security/util/DerInputStream.java b/src/sun/security/util/DerInputStream.java new file mode 100644 index 00000000..be3b95e0 --- /dev/null +++ b/src/sun/security/util/DerInputStream.java @@ -0,0 +1,597 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Date; +import java.util.Vector; +import java.math.BigInteger; +import java.io.DataInputStream; + +/** + * A DER input stream, used for parsing ASN.1 DER-encoded data such as + * that found in X.509 certificates. DER is a subset of BER/1, which has + * the advantage that it allows only a single encoding of primitive data. + * (High level data such as dates still support many encodings.) That is, + * it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER). + * + *

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

    At this time, this class supports only a subset of the types of DER + * data encodings which are defined. That subset is sufficient for parsing + * most X.509 certificates. + * + * + * @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; + + /** The DER tag of the value; one of the tag_ constants. */ + public byte tag; + + /** + * 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) throws IOException { + init(data, 0, data.length); + } + + /** + * Create a DER input stream from part of a data buffer. + * The buffer is not copied, it is shared. Accordingly, the + * buffer should be treated as read-only. + * + * @param data the buffer from which to create the string (CONSUMED) + * @param offset the first index of data which will + * be read as DER input in the new stream + * @param len how long a chunk of the buffer to use, + * starting at "offset" + */ + public DerInputStream(byte[] data, int offset, int len) throws IOException { + init(data, offset, len); + } + + /* + * private helper routine + */ + private void init(byte[] data, int offset, int len) throws IOException { + if ((offset+2 > data.length) || (offset+len > data.length)) { + throw new IOException("Encoding bytes too short"); + } + // check for indefinite length encoding + if (DerIndefLenConverter.isIndefinite(data[offset+1])) { + byte[] inData = new byte[len]; + System.arraycopy(data, offset, inData, 0, len); + + DerIndefLenConverter derIn = new DerIndefLenConverter(); + buffer = new DerInputBuffer(derIn.convert(inData)); + } else + 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, ENUMERATED, BIT STRING, OCTET STRING, NULL + * OBJECT IDENTIFIER, SEQUENCE (OF), SET (OF) + * UTF8String, PrintableString, T61String, IA5String, UTCTime, + * GeneralizedTime, BMPString. + * Note: UniversalString not supported till encoder is available. + */ + + /** + * Get an integer from the input stream as an integer. + * + * @return the integer held in this DER input stream. + */ + public int getInteger() throws IOException { + if (buffer.read() != DerValue.tag_Integer) { + throw new IOException("DER input, Integer tag error"); + } + return buffer.getInteger(getLength(buffer)); + } + + /** + * Get a integer from the input stream as a BigInteger object. + * + * @return the integer held in this DER input stream. + */ + public BigInteger getBigInteger() throws IOException { + if (buffer.read() != DerValue.tag_Integer) { + throw new IOException("DER input, Integer tag error"); + } + return buffer.getBigInteger(getLength(buffer), false); + } + + /** + * Returns an ASN.1 INTEGER value as a positive BigInteger. + * This is just to deal with implementations that incorrectly encode + * some values as negative. + * + * @return the integer held in this DER value as a BigInteger. + */ + public BigInteger getPositiveBigInteger() throws IOException { + if (buffer.read() != DerValue.tag_Integer) { + throw new IOException("DER input, Integer tag error"); + } + return buffer.getBigInteger(getLength(buffer), true); + } + + /** + * Get an enumerated from the input stream. + * + * @return the integer held in this DER input stream. + */ + public int getEnumerated() throws IOException { + if (buffer.read() != DerValue.tag_Enumerated) { + throw new IOException("DER input, Enumerated tag error"); + } + return buffer.getInteger(getLength(buffer)); + } + + /** + * Get a bit string from the input stream. Padded bits (if any) + * will be stripped off before the bit string is returned. + */ + public byte[] getBitString() throws IOException { + if (buffer.read() != DerValue.tag_BitString) + throw new IOException("DER input not an bit string"); + + return buffer.getBitString(getLength(buffer)); + } + + /** + * 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 ((length != 0) && (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 ((length != 0) && (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) && (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 { + tag = (byte)buffer.read(); + if (tag != DerValue.tag_Sequence) + throw new IOException("Sequence 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) + * @return array of the values in the sequence + */ + public DerValue[] getSet(int startLen) throws IOException { + tag = (byte)buffer.read(); + if (tag != 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 { + tag = (byte)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 { + DerInputStream newstr; + + byte lenByte = (byte)buffer.read(); + int len = getLength((lenByte & 0xff), buffer); + + if (len == -1) { + // indefinite length encoding found + int readLen = buffer.available(); + int offset = 2; // for tag and length bytes + byte[] indefData = new byte[readLen + offset]; + indefData[0] = tag; + indefData[1] = lenByte; + DataInputStream dis = new DataInputStream(buffer); + dis.readFully(indefData, offset, readLen); + dis.close(); + DerIndefLenConverter derIn = new DerIndefLenConverter(); + buffer = new DerInputBuffer(derIn.convert(indefData)); + if (tag != buffer.read()) + throw new IOException("Indefinite length encoding" + + " not supported"); + len = DerInputStream.getLength(buffer); + } + + if (len == 0) + // return empty array instead of null, which should be + // used only for missing optionals + return new DerValue[0]; + + /* + * Create a temporary stream from which to read the data, + * unless it's not really needed. + */ + if (buffer.available() == len) + newstr = this; + else + newstr = subStream(len, true); + + /* + * Pull values out of the stream. + */ + Vector vec = new Vector(startLen); + DerValue value; + + do { + value = new DerValue(newstr.buffer); + vec.addElement(value); + } while (newstr.available() > 0); + + if (newstr.available() != 0) + throw new IOException("extra data at end of vector"); + + /* + * Now stick them into the array we're returning. + */ + int i, max = vec.size(); + DerValue[] retval = new DerValue[max]; + + for (i = 0; i < max; i++) + retval[i] = 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); + } + + /** + * Read a string that was encoded as a UTF8String DER value. + */ + public String getUTF8String() throws IOException { + return readString(DerValue.tag_UTF8String, "UTF-8", "UTF8"); + } + + /** + * Read a string that was encoded as a PrintableString DER value. + */ + public String getPrintableString() throws IOException { + return readString(DerValue.tag_PrintableString, "Printable", + "ASCII"); + } + + /** + * Read a string that was encoded as a T61String DER value. + */ + public String getT61String() throws IOException { + /* + * Works for common characters between T61 and ASCII. + */ + return readString(DerValue.tag_T61String, "T61", "ISO-8859-1"); + } + + /** + * Read a string that was encoded as a IA5tring DER value. + */ + public String getIA5String() throws IOException { + return readString(DerValue.tag_IA5String, "IA5", "ASCII"); + } + + /** + * Read a string that was encoded as a BMPString DER value. + */ + public String getBMPString() throws IOException { + return readString(DerValue.tag_BMPString, "BMP", + "UnicodeBigUnmarked"); + } + + /** + * Read a string that was encoded as a GeneralString DER value. + */ + public String getGeneralString() throws IOException { + return readString(DerValue.tag_GeneralString, "General", + "ASCII"); + } + + /** + * Private helper routine to read an encoded string from the input + * stream. + * @param stringTag the tag for the type of string to read + * @param stringName a name to display in error messages + * @param enc the encoder to use to interpret the data. Should + * correspond to the stringTag above. + */ + private String readString(byte stringTag, String stringName, + String enc) throws IOException { + + if (buffer.read() != stringTag) + throw new IOException("DER input not a " + + stringName + " string"); + + int length = getLength(buffer); + byte[] retval = new byte[length]; + if ((length != 0) && (buffer.read(retval) != length)) + throw new IOException("short read of DER " + + stringName + " string"); + + return new String(retval, enc); + } + + /** + * 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 "); + return buffer.getUTCTime(getLength(buffer)); + } + + /** + * 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 "); + return buffer.getGeneralizedTime(getLength(buffer)); + } + + /* + * 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!) + * + * @return the length or -1 if indefinite length found. + * @exception IOException on parsing error or unsupported lengths. + */ + static int getLength(InputStream in) throws IOException { + return getLength(in.read(), in); + } + + /* + * 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!) + * + * @return the length or -1 if indefinite length found. + * @exception IOException on parsing error or unsupported lengths. + */ + static int getLength(int lenByte, InputStream in) throws IOException { + int value, tmp; + + tmp = lenByte; + if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum + value = tmp; + } else { // long form or indefinite + tmp &= 0x07f; + + /* + * NOTE: tmp == 0 indicates indefinite length encoded data. + * tmp > 4 indicates more than 4Gb of data. + */ + if (tmp == 0) + return -1; + if (tmp < 0 || tmp > 4) + throw new IOException("DerInputStream.getLength(): lengthTag=" + + tmp + ", " + + ((tmp < 0) ? "incorrect DER encoding." : "too big.")); + + for (value = 0; tmp > 0; tmp --) { + value <<= 8; + value += 0x0ff & in.read(); + } + if (value < 0) { + throw new IOException("DerInputStream.getLength(): " + + "Invalid length bytes"); + } + } + return value; + } + + /** + * Mark the current position in the buffer, so that + * a later call to reset will return here. + */ + public void mark(int value) { buffer.mark(value); } + + + /** + * Return to the position of the last mark + * call. A mark is implicitly set at the beginning of + * the stream when it is created. + */ + public void reset() { buffer.reset(); } + + + /** + * Returns the number of bytes available for reading. + * This is most useful for testing whether the stream is + * empty. + */ + public int available() { return buffer.available(); } +} diff --git a/src/sun/security/util/DerOutputStream.java b/src/sun/security/util/DerOutputStream.java new file mode 100644 index 00000000..c517df8a --- /dev/null +++ b/src/sun/security/util/DerOutputStream.java @@ -0,0 +1,576 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.Comparator; +import java.util.Arrays; +import java.math.BigInteger; +import java.util.Locale; + + +/** + * Output stream marshaling DER-encoded data. This is eventually provided + * in the form of a byte array; there is no advance limit on the size of + * that byte array. + * + *

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

    +     *            [N] IMPLICIT 
    +     * 
    + * For example, FooLength [1] IMPLICIT INTEGER, with value=4; + * would be encoded as "81 01 04" whereas in explicit + * tagging it would be encoded as "A1 03 02 01 04". + * Notice that the tag is A1 and not 81, this is because with + * explicit tagging the form is always constructed. + * @param value original value being implicitly tagged + */ + public void writeImplicit(byte tag, DerOutputStream value) + throws IOException { + write(tag); + write(value.buf, 1, value.count-1); + } + + /** + * Marshals pre-encoded DER value onto the output stream. + */ + public void putDerValue(DerValue val) throws IOException { + val.encode(this); + } + + /* + * PRIMITIVES -- these are "universal" ASN.1 simple types. + * + * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL + * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF) + * PrintableString, T61String, IA5String, UTCTime + */ + + /** + * Marshals a DER boolean on the output stream. + */ + public void putBoolean(boolean val) throws IOException { + write(DerValue.tag_Boolean); + putLength(1); + if (val) { + write(0xff); + } else { + write(0); + } + } + + /** + * Marshals a DER enumerated on the output stream. + * @param i the enumerated value. + */ + public void putEnumerated(int i) throws IOException { + write(DerValue.tag_Enumerated); + putIntegerContents(i); + } + + /** + * Marshals a DER integer on the output stream. + * + * @param i the integer in the form of a BigInteger. + */ + public void putInteger(BigInteger i) throws IOException { + write(DerValue.tag_Integer); + byte[] buf = i.toByteArray(); // least number of bytes + putLength(buf.length); + write(buf, 0, buf.length); + } + + /** + * Marshals a DER integer on the output stream. + * @param i the integer in the form of an Integer. + */ + public void putInteger(Integer i) throws IOException { + putInteger(i.intValue()); + } + + /** + * Marshals a DER integer on the output stream. + * @param i the integer. + */ + public void putInteger(int i) throws IOException { + write(DerValue.tag_Integer); + putIntegerContents(i); + } + + private void putIntegerContents(int i) throws IOException { + + byte[] bytes = new byte[4]; + int start = 0; + + // Obtain the four bytes of the int + + bytes[3] = (byte) (i & 0xff); + bytes[2] = (byte)((i & 0xff00) >>> 8); + bytes[1] = (byte)((i & 0xff0000) >>> 16); + bytes[0] = (byte)((i & 0xff000000) >>> 24); + + // Reduce them to the least number of bytes needed to + // represent this int + + if (bytes[0] == (byte)0xff) { + + // Eliminate redundant 0xff + + for (int j = 0; j < 3; j++) { + if ((bytes[j] == (byte)0xff) && + ((bytes[j+1] & 0x80) == 0x80)) + start++; + else + break; + } + } else if (bytes[0] == 0x00) { + + // Eliminate redundant 0x00 + + for (int j = 0; j < 3; j++) { + if ((bytes[j] == 0x00) && + ((bytes[j+1] & 0x80) == 0)) + start++; + else + break; + } + } + + putLength(4 - start); + for (int k = start; k < 4; k++) + write(bytes[k]); + } + + /** + * 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); + } + + /** + * 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 truncated DER bit string on the output stream. + * The bit strings need not be byte-aligned. + * + * @param bits the bit string, MSB first + */ + public void putTruncatedUnalignedBitString(BitArray ba) throws IOException { + putUnalignedBitString(ba.truncate()); + } + + /** + * 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); + } + + /** + * Marshals the contents of a set on the output stream. Sets + * are semantically unordered, but DER requires that encodings of + * set elements be sorted into ascending lexicographical order + * before being output. Hence sets with the same tags and + * elements have the same DER encoding. + * + * This method supports the ASN.1 "SET OF" construct, but not + * "SET", which uses a different order. + */ + public void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException { + putOrderedSet(tag, set, lexOrder); + } + + /** + * Marshals the contents of a set on the output stream. Sets + * are semantically unordered, but DER requires that encodings of + * set elements be sorted into ascending tag order + * before being output. Hence sets with the same tags and + * elements have the same DER encoding. + * + * This method supports the ASN.1 "SET" construct, but not + * "SET OF", which uses a different order. + */ + public void putOrderedSet(byte tag, DerEncoder[] set) throws IOException { + putOrderedSet(tag, set, tagOrder); + } + + /** + * Lexicographical order comparison on byte arrays, for ordering + * elements of a SET OF objects in DER encoding. + */ + private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder(); + + /** + * Tag order comparison on byte arrays, for ordering elements of + * SET objects in DER encoding. + */ + private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder(); + + /** + * Marshals a the contents of a set on the output stream with the + * encodings of its sorted in increasing order. + * + * @param order the order to use when sorting encodings of components. + */ + private void putOrderedSet(byte tag, DerEncoder[] set, + Comparator order) throws IOException { + DerOutputStream[] streams = new DerOutputStream[set.length]; + + for (int i = 0; i < set.length; i++) { + streams[i] = new DerOutputStream(); + set[i].derEncode(streams[i]); + } + + // order the element encodings + byte[][] bufs = new byte[streams.length][]; + for (int i = 0; i < streams.length; i++) { + bufs[i] = streams[i].toByteArray(); + } + Arrays.sort(bufs, order); + + DerOutputStream bytes = new DerOutputStream(); + for (int i = 0; i < streams.length; i++) { + bytes.write(bufs[i]); + } + write(tag, bytes); + + } + + /** + * Marshals a string as a DER encoded UTF8String. + */ + public void putUTF8String(String s) throws IOException { + writeString(s, DerValue.tag_UTF8String, "UTF8"); + } + + /** + * Marshals a string as a DER encoded PrintableString. + */ + public void putPrintableString(String s) throws IOException { + writeString(s, DerValue.tag_PrintableString, "ASCII"); + } + + /** + * Marshals a string as a DER encoded T61String. + */ + public void putT61String(String s) throws IOException { + /* + * Works for characters that are defined in both ASCII and + * T61. + */ + writeString(s, DerValue.tag_T61String, "ISO-8859-1"); + } + + /** + * Marshals a string as a DER encoded IA5String. + */ + public void putIA5String(String s) throws IOException { + writeString(s, DerValue.tag_IA5String, "ASCII"); + } + + /** + * Marshals a string as a DER encoded BMPString. + */ + public void putBMPString(String s) throws IOException { + writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked"); + } + + /** + * Marshals a string as a DER encoded GeneralString. + */ + public void putGeneralString(String s) throws IOException { + writeString(s, DerValue.tag_GeneralString, "ASCII"); + } + + /** + * Private helper routine for writing DER encoded string values. + * @param s the string to write + * @param stringTag one of the DER string tags that indicate which + * encoding should be used to write the string out. + * @param enc the name of the encoder that should be used corresponding + * to the above tag. + */ + private void writeString(String s, byte stringTag, String enc) + throws IOException { + + byte[] data = s.getBytes(enc); + write(stringTag); + putLength(data.length); + write(data); + } + + /** + * Marshals a DER UTC time/date value. + * + *

    YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time + * and with seconds (even if seconds=0) as per RFC 3280. + */ + public void putUTCTime(Date d) throws IOException { + putTime(d, DerValue.tag_UtcTime); + } + + /** + * Marshals a DER Generalized Time/date value. + * + *

    YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time + * and with seconds (even if seconds=0) as per RFC 3280. + */ + public void putGeneralizedTime(Date d) throws IOException { + putTime(d, DerValue.tag_GeneralizedTime); + } + + /** + * Private helper routine for marshalling a DER UTC/Generalized + * time/date value. If the tag specified is not that for UTC Time + * then it defaults to Generalized Time. + * @param d the date to be marshalled + * @param tag the tag for UTC Time or Generalized Time + */ + private void putTime(Date d, byte tag) throws IOException { + + /* + * Format the date. + */ + + TimeZone tz = TimeZone.getTimeZone("GMT"); + String pattern = null; + + if (tag == DerValue.tag_UtcTime) { + pattern = "yyMMddHHmmss'Z'"; + } else { + tag = DerValue.tag_GeneralizedTime; + pattern = "yyyyMMddHHmmss'Z'"; + } + + SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.US); + sdf.setTimeZone(tz); + byte[] time = (sdf.format(d)).getBytes("ISO-8859-1"); + + /* + * Write the formatted date. + */ + + write(tag); + putLength(time.length); + write(time); + } + + /** + * Put the encoding of the length in the stream. + * + * @params 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. + * + * @params class the tag class type, one of UNIVERSAL, CONTEXT, + * APPLICATION or PRIVATE + * @params form if true, the value is constructed, otherwise it is + * primitive. + * @params val the tag value + */ + public void putTag(byte tagClass, boolean form, byte val) { + byte tag = (byte)(tagClass | val); + if (form) { + tag |= (byte)0x20; + } + write(tag); + } + + /** + * Write the current contents of this DerOutputStream + * to an OutputStream. + * + * @exception IOException on output error. + */ + public void derEncode(OutputStream out) throws IOException { + out.write(toByteArray()); + } +} diff --git a/src/sun/security/util/DerValue.java b/src/sun/security/util/DerValue.java new file mode 100644 index 00000000..beb51c19 --- /dev/null +++ b/src/sun/security/util/DerValue.java @@ -0,0 +1,934 @@ +/* + * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.*; +import java.math.BigInteger; +import java.util.Date; +import sun.misc.IOUtils; + +/** + * Represents a single DER-encoded value. DER encoding rules are a subset + * of the "Basic" Encoding Rules (BER), but they only support a single way + * ("Definite" encoding) to encode any given value. + * + *

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

    At this time, this class supports only a subset of the types of DER + * data encodings which are defined. That subset is sufficient for parsing + * most X.509 certificates, and working with selected additional formats + * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data). + * + * A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3 + * and RFC 3280, section 4.1.2.4., we assume that this kind of string will + * contain ISO-8859-1 characters only. + * + * + * @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, never null + */ + public final 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 indicating an ASN.1 "UTF8String" value. */ + public final static byte tag_UTF8String = 0x0C; + + /** Tag value including a "printable" string */ + public final static byte tag_PrintableString = 0x13; + + /** 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 "GenerallString" value. */ + public final static byte tag_GeneralString = 0x1B; + + /** Tag value indicating an ASN.1 "UniversalString" value. */ + public final static byte tag_UniversalString = 0x1C; + + /** Tag value indicating an ASN.1 "BMPString" value. */ + public final static byte tag_BMPString = 0x1E; + + // 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. + */ + + /** + * Returns true if the tag class is UNIVERSAL. + */ + public boolean isUniversal() { return ((tag & 0x0c0) == 0x000); } + + /** + * Returns true if the tag class is APPLICATION. + */ + public 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); } + + /** + * Returns true iff the CONSTRUCTED TAG matches the passed tag. + */ + public boolean isConstructed(byte constructedTag) { + if (!isConstructed()) { + return false; + } + return ((tag & 0x01f) == constructedTag); + } + + /** + * Creates a PrintableString or UTF8string DER value from a string + */ + public DerValue(String value) throws IOException { + boolean isPrintableString = true; + for (int i = 0; i < value.length(); i++) { + if (!isPrintableStringChar(value.charAt(i))) { + isPrintableString = false; + break; + } + } + + data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value); + } + + /** + * Creates a string type DER value from a String object + * @param stringTag the tag for the DER value to create + * @param value the String object to use for the DER value + */ + public DerValue(byte stringTag, String value) throws IOException { + data = init(stringTag, 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); + } + + /* + * package private + */ + DerValue(DerInputBuffer in) throws IOException { + // XXX must also parse BER-encoded constructed + // values such as sequences, sets... + + tag = (byte)in.read(); + byte lenByte = (byte)in.read(); + length = DerInputStream.getLength((lenByte & 0xff), in); + if (length == -1) { // indefinite length encoding found + DerInputBuffer inbuf = in.dup(); + int readLen = inbuf.available(); + int offset = 2; // for tag and length bytes + byte[] indefData = new byte[readLen + offset]; + indefData[0] = tag; + indefData[1] = lenByte; + DataInputStream dis = new DataInputStream(inbuf); + dis.readFully(indefData, offset, readLen); + dis.close(); + DerIndefLenConverter derIn = new DerIndefLenConverter(); + inbuf = new DerInputBuffer(derIn.convert(indefData)); + if (tag != inbuf.read()) + throw new IOException + ("Indefinite length encoding not supported"); + length = DerInputStream.getLength(inbuf); + buffer = inbuf.dup(); + buffer.truncate(length); + data = new DerInputStream(buffer); + // indefinite form is encoded by sending a length field with a + // length of 0. - i.e. [1000|0000]. + // the object is ended by sending two zero bytes. + in.skip(length + offset); + } else { + + 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 { + data = 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 { + data = 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. + * In case of indefinite length encoded datum, the input stream + * must hold only one datum. + * + * @param in the input stream holding a single DER datum, + * which may be followed by additional data + */ + public DerValue(InputStream in) throws IOException { + data = init(false, in); + } + + private DerInputStream init(byte stringTag, String value) throws IOException { + String enc = null; + + tag = stringTag; + + switch (stringTag) { + case tag_PrintableString: + case tag_IA5String: + case tag_GeneralString: + enc = "ASCII"; + break; + case tag_T61String: + enc = "ISO-8859-1"; + break; + case tag_BMPString: + enc = "UnicodeBigUnmarked"; + break; + case tag_UTF8String: + enc = "UTF8"; + break; + // TBD: Need encoder for UniversalString before it can + // be handled. + default: + throw new IllegalArgumentException("Unsupported DER string type"); + } + + byte[] buf = value.getBytes(enc); + length = buf.length; + buffer = new DerInputBuffer(buf); + DerInputStream result = new DerInputStream(buffer); + result.mark(Integer.MAX_VALUE); + return result; + } + + /* + * helper routine + */ + private DerInputStream init(boolean fullyBuffered, InputStream in) + throws IOException { + + tag = (byte)in.read(); + byte lenByte = (byte)in.read(); + length = DerInputStream.getLength((lenByte & 0xff), in); + if (length == -1) { // indefinite length encoding found + int readLen = in.available(); + int offset = 2; // for tag and length bytes + byte[] indefData = new byte[readLen + offset]; + indefData[0] = tag; + indefData[1] = lenByte; + DataInputStream dis = new DataInputStream(in); + dis.readFully(indefData, offset, readLen); + dis.close(); + DerIndefLenConverter derIn = new DerIndefLenConverter(); + in = new ByteArrayInputStream(derIn.convert(indefData)); + if (tag != in.read()) + throw new IOException + ("Indefinite length encoding not supported"); + length = DerInputStream.getLength(in); + } + + if (fullyBuffered && in.available() != length) + throw new IOException("extra data given to DerValue constructor"); + + byte[] bytes = IOUtils.readFully(in, length, true); + + buffer = new DerInputBuffer(bytes); + return 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); + // XXX yeech, excess copies ... DerInputBuffer.write(OutStream) + if (length > 0) { + byte[] value = new byte[length]; + // always synchronized on data + synchronized (data) { + buffer.reset(); + if (buffer.read(value) != length) { + throw new IOException("short DER value read (encode)"); + } + out.write(value); + } + } + } + + public final DerInputStream getData() { + return data; + } + + public final byte getTag() { + return tag; + } + + /** + * 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); + } + + private byte[] append(byte[] a, byte[] b) { + if (a == null) + return b; + + byte[] ret = new byte[a.length + b.length]; + System.arraycopy(a, 0, ret, 0, a.length); + System.arraycopy(b, 0, ret, a.length, b.length); + + return ret; + } + + /** + * Returns an ASN.1 OCTET STRING + * + * @return the octet string held in this DER value + */ + public byte[] getOctetString() throws IOException { + byte[] bytes; + + if (tag != tag_OctetString && !isConstructed(tag_OctetString)) { + throw new IOException( + "DerValue.getOctetString, not an Octet String: " + tag); + } + bytes = new byte[length]; + // Note: do not tempt to call buffer.read(bytes) at all. There's a + // known bug that it returns -1 instead of 0. + if (length == 0) { + return bytes; + } + if (buffer.read(bytes) != length) + throw new IOException("short read on DerValue buffer"); + if (isConstructed()) { + DerInputStream in = new DerInputStream(bytes); + bytes = null; + while (in.available() != 0) { + bytes = append(bytes, in.getOctetString()); + } + } + return bytes; + } + + /** + * Returns an ASN.1 INTEGER value as an integer. + * + * @return the integer held in this DER value. + */ + public int getInteger() throws IOException { + if (tag != tag_Integer) { + throw new IOException("DerValue.getInteger, not an int " + tag); + } + return buffer.getInteger(data.available()); + } + + /** + * Returns an ASN.1 INTEGER value as a BigInteger. + * + * @return the integer held in this DER value as a BigInteger. + */ + public BigInteger getBigInteger() throws IOException { + if (tag != tag_Integer) + throw new IOException("DerValue.getBigInteger, not an int " + tag); + return buffer.getBigInteger(data.available(), false); + } + + /** + * Returns an ASN.1 INTEGER value as a positive BigInteger. + * This is just to deal with implementations that incorrectly encode + * some values as negative. + * + * @return the integer held in this DER value as a BigInteger. + */ + public BigInteger getPositiveBigInteger() throws IOException { + if (tag != tag_Integer) + throw new IOException("DerValue.getBigInteger, not an int " + tag); + return buffer.getBigInteger(data.available(), true); + } + + /** + * Returns an ASN.1 ENUMERATED value. + * + * @return the integer held in this DER value. + */ + public int getEnumerated() throws IOException { + if (tag != tag_Enumerated) { + throw new IOException("DerValue.getEnumerated, incorrect tag: " + + tag); + } + return buffer.getInteger(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, IA5, BMP, UTF8). + */ + // TBD: Need encoder for UniversalString before it can be handled. + public String getAsString() throws IOException { + if (tag == tag_UTF8String) + return getUTF8String(); + else if (tag == tag_PrintableString) + return getPrintableString(); + else if (tag == tag_T61String) + return getT61String(); + else if (tag == tag_IA5String) + return getIA5String(); + /* + else if (tag == tag_UniversalString) + return getUniversalString(); + */ + else if (tag == tag_BMPString) + return getBMPString(); + else if (tag == tag_GeneralString) + return getGeneralString(); + else + return null; + } + + /** + * Returns an ASN.1 BIT STRING value, with the tag assumed implicit + * based on the parameter. The bit string must be byte-aligned. + * + * @params 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. + * + * @params 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(); + } + + /** + * Helper routine to return all the bytes contained in the + * DerInputStream associated with this object. + */ + public byte[] getDataBytes() throws IOException { + byte[] retVal = new byte[length]; + synchronized (data) { + data.reset(); + data.getBytes(retVal); + } + return retVal; + } + + /** + * 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 new String(getDataBytes(), "ASCII"); + } + + /** + * 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 new String(getDataBytes(), "ISO-8859-1"); + } + + /** + * 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 new String(getDataBytes(), "ASCII"); + } + + /** + * Returns the ASN.1 BMP (Unicode) STRING value as a Java string. + * + * @return a string corresponding to the encoded BMPString held in + * this value + */ + public String getBMPString() throws IOException { + if (tag != tag_BMPString) + throw new IOException( + "DerValue.getBMPString, not BMP " + tag); + + // BMPString is the same as Unicode in big endian, unmarked + // format. + return new String(getDataBytes(), "UnicodeBigUnmarked"); + } + + /** + * Returns the ASN.1 UTF-8 STRING value as a Java String. + * + * @return a string corresponding to the encoded UTF8String held in + * this value + */ + public String getUTF8String() throws IOException { + if (tag != tag_UTF8String) + throw new IOException( + "DerValue.getUTF8String, not UTF-8 " + tag); + + return new String(getDataBytes(), "UTF8"); + } + + /** + * Returns the ASN.1 GENERAL STRING value as a Java String. + * + * @return a string corresponding to the encoded GeneralString held in + * this value + */ + public String getGeneralString() throws IOException { + if (tag != tag_GeneralString) + throw new IOException( + "DerValue.getGeneralString, not GeneralString " + tag); + + return new String(getDataBytes(), "ASCII"); + } + + /** + * Returns a Date if the DerValue is UtcTime. + * + * @return the Date held in this DER value + */ + public Date getUTCTime() throws IOException { + if (tag != tag_UtcTime) { + throw new IOException("DerValue.getUTCTime, not a UtcTime: " + tag); + } + return buffer.getUTCTime(data.available()); + } + + /** + * Returns a Date if the DerValue is GeneralizedTime. + * + * @return the Date held in this DER value + */ + public Date getGeneralizedTime() throws IOException { + if (tag != tag_GeneralizedTime) { + throw new IOException( + "DerValue.getGeneralizedTime, not a GeneralizedTime: " + tag); + } + return buffer.getGeneralizedTime(data.available()); + } + + /** + * 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) { + if (this == other) { + return true; + } + if (tag != other.tag) { + return false; + } + if (data == other.data) { + return true; + } + + // make sure the order of lock is always consistent to avoid a deadlock + return (System.identityHashCode(this.data) + > System.identityHashCode(other.data)) ? + doEquals(this, other): + doEquals(other, this); + } + + /** + * Helper for public method equals() + */ + private static boolean doEquals(DerValue d1, DerValue d2) { + synchronized (d1.data) { + synchronized (d2.data) { + d1.data.reset(); + d2.data.reset(); + return d1.buffer.equals(d2.buffer); + } + } + } + + /** + * Returns a printable representation of the value. + * + * @return printable representation of the value + */ + public String toString() { + try { + + String str = getAsString(); + if (str != null) + return "\"" + str + "\""; + 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; + } + + /** + * Determine if a character is one of the permissible characters for + * PrintableString: + * A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses, + * plus sign, comma, hyphen, period, slash, colon, equals sign, + * and question mark. + * + * Characters that are *not* allowed in PrintableString include + * exclamation point, quotation mark, number sign, dollar sign, + * percent sign, ampersand, asterisk, semicolon, less than sign, + * greater than sign, at sign, left and right square brackets, + * backslash, circumflex (94), underscore, back quote (96), + * left and right curly brackets, vertical line, tilde, + * and the control codes (0-31 and 127). + * + * This list is based on X.680 (the ASN.1 spec). + */ + public static boolean isPrintableStringChar(char ch) { + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9')) { + return true; + } else { + switch (ch) { + case ' ': /* space */ + case '\'': /* apostrophe */ + case '(': /* left paren */ + case ')': /* right paren */ + case '+': /* plus */ + case ',': /* comma */ + case '-': /* hyphen */ + case '.': /* period */ + case '/': /* slash */ + case ':': /* colon */ + case '=': /* equals */ + case '?': /* question mark */ + return true; + default: + return false; + } + } + } + + /** + * Create the tag of the attribute. + * + * @params class the tag class type, one of UNIVERSAL, CONTEXT, + * APPLICATION or PRIVATE + * @params form if true, the value is constructed, otherwise it + * is primitive. + * @params 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. + * + * @params tag the tag value + */ + public void resetTag(byte tag) { + this.tag = tag; + } + + /** + * Returns a hashcode for this DerValue. + * + * @return a hashcode for this DerValue. + */ + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/src/sun/security/util/DisabledAlgorithmConstraints.java b/src/sun/security/util/DisabledAlgorithmConstraints.java new file mode 100644 index 00000000..17b56974 --- /dev/null +++ b/src/sun/security/util/DisabledAlgorithmConstraints.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.AlgorithmParameters; + +import java.security.Key; +import java.security.Security; +import java.security.PrivilegedAction; +import java.security.AccessController; + +import java.util.Locale; +import java.util.Set; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * Algorithm constraints for disabled algorithms property + * + * See the "jdk.certpath.disabledAlgorithms" specification in java.security + * for the syntax of the disabled algorithm string. + */ +public class DisabledAlgorithmConstraints implements AlgorithmConstraints { + + // the known security property, jdk.certpath.disabledAlgorithms + public final static String PROPERTY_CERTPATH_DISABLED_ALGS = + "jdk.certpath.disabledAlgorithms"; + + // the known security property, jdk.tls.disabledAlgorithms + public final static String PROPERTY_TLS_DISABLED_ALGS = + "jdk.tls.disabledAlgorithms"; + + private final static Map disabledAlgorithmsMap = + new HashMap<>(); + private final static Map keySizeConstraintsMap = + new HashMap<>(); + + private String[] disabledAlgorithms; + private KeySizeConstraints keySizeConstraints; + + /** + * Initialize algorithm constraints with the specified security property. + * + * @param propertyName the security property name that define the disabled + * algorithm constraints + */ + public DisabledAlgorithmConstraints(String propertyName) { + // Both disabledAlgorithmsMap and keySizeConstraintsMap are + // synchronized with the lock of disabledAlgorithmsMap. + synchronized (disabledAlgorithmsMap) { + if(!disabledAlgorithmsMap.containsKey(propertyName)) { + loadDisabledAlgorithmsMap(propertyName); + } + + disabledAlgorithms = disabledAlgorithmsMap.get(propertyName); + keySizeConstraints = keySizeConstraintsMap.get(propertyName); + } + } + + @Override + final public boolean permits(Set primitives, + String algorithm, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException("No algorithm name specified"); + } + + if (primitives == null || primitives.isEmpty()) { + throw new IllegalArgumentException( + "No cryptographic primitive specified"); + } + + Set elements = null; + for (String disabled : disabledAlgorithms) { + if (disabled == null || disabled.isEmpty()) { + continue; + } + + // check the full name + if (disabled.equalsIgnoreCase(algorithm)) { + return false; + } + + // decompose the algorithm into sub-elements + if (elements == null) { + elements = decomposes(algorithm); + } + + // check the items of the algorithm + for (String element : elements) { + if (disabled.equalsIgnoreCase(element)) { + return false; + } + } + } + + return true; + } + + @Override + final public boolean permits(Set primitives, Key key) { + return checkConstraints(primitives, "", key, null); + } + + @Override + final public boolean permits(Set primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException("No algorithm name specified"); + } + + return checkConstraints(primitives, algorithm, key, parameters); + } + + /** + * Decompose the standard algorithm name into sub-elements. + *

    + * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA" + * so that we can check the "SHA1" and "RSA" algorithm constraints + * separately. + *

    + * Please override the method if need to support more name pattern. + */ + protected Set decomposes(String algorithm) { + if (algorithm == null || algorithm.length() == 0) { + return new HashSet(); + } + + // algorithm/mode/padding + Pattern transPattern = Pattern.compile("/"); + String[] transTockens = transPattern.split(algorithm); + + Set elements = new HashSet(); + for (String transTocken : transTockens) { + if (transTocken == null || transTocken.length() == 0) { + continue; + } + + // PBEWithAnd + // PBEWithAnd + // OAEPWithAndPadding + // with + // withand + Pattern pattern = + Pattern.compile("with|and", Pattern.CASE_INSENSITIVE); + String[] tokens = pattern.split(transTocken); + + for (String token : tokens) { + if (token == null || token.length() == 0) { + continue; + } + + elements.add(token); + } + } + + // In Java standard algorithm name specification, for different + // purpose, the SHA-1 and SHA-2 algorithm names are different. For + // example, for MessageDigest, the standard name is "SHA-256", while + // for Signature, the digest algorithm component is "SHA256" for + // signature algorithm "SHA256withRSA". So we need to check both + // "SHA-256" and "SHA256" to make the right constraint checking. + + // handle special name: SHA-1 and SHA1 + if (elements.contains("SHA1") && !elements.contains("SHA-1")) { + elements.add("SHA-1"); + } + if (elements.contains("SHA-1") && !elements.contains("SHA1")) { + elements.add("SHA1"); + } + + // handle special name: SHA-224 and SHA224 + if (elements.contains("SHA224") && !elements.contains("SHA-224")) { + elements.add("SHA-224"); + } + if (elements.contains("SHA-224") && !elements.contains("SHA224")) { + elements.add("SHA224"); + } + + // handle special name: SHA-256 and SHA256 + if (elements.contains("SHA256") && !elements.contains("SHA-256")) { + elements.add("SHA-256"); + } + if (elements.contains("SHA-256") && !elements.contains("SHA256")) { + elements.add("SHA256"); + } + + // handle special name: SHA-384 and SHA384 + if (elements.contains("SHA384") && !elements.contains("SHA-384")) { + elements.add("SHA-384"); + } + if (elements.contains("SHA-384") && !elements.contains("SHA384")) { + elements.add("SHA384"); + } + + // handle special name: SHA-512 and SHA512 + if (elements.contains("SHA512") && !elements.contains("SHA-512")) { + elements.add("SHA-512"); + } + if (elements.contains("SHA-512") && !elements.contains("SHA512")) { + elements.add("SHA512"); + } + + return elements; + } + + // Check algorithm constraints + private boolean checkConstraints(Set primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + // check the key parameter, it cannot be null. + if (key == null) { + throw new IllegalArgumentException("The key cannot be null"); + } + + // check the target algorithm + if (algorithm != null && algorithm.length() != 0) { + if (!permits(primitives, algorithm, parameters)) { + return false; + } + } + + // check the key algorithm + if (!permits(primitives, key.getAlgorithm(), null)) { + return false; + } + + // check the key constraints + if (keySizeConstraints.disables(key)) { + return false; + } + + return true; + } + + // Get disabled algorithm constraints from the specified security property. + private static void loadDisabledAlgorithmsMap( + final String propertyName) { + + String property = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return Security.getProperty(propertyName); + } + }); + + String[] algorithmsInProperty = null; + + if (property != null && !property.isEmpty()) { + + // remove double quote marks from beginning/end of the property + if (property.charAt(0) == '"' && + property.charAt(property.length() - 1) == '"') { + property = property.substring(1, property.length() - 1); + } + + algorithmsInProperty = property.split(","); + for (int i = 0; i < algorithmsInProperty.length; i++) { + algorithmsInProperty[i] = algorithmsInProperty[i].trim(); + } + } + + // map the disabled algorithms + if (algorithmsInProperty == null) { + algorithmsInProperty = new String[0]; + } + disabledAlgorithmsMap.put(propertyName, algorithmsInProperty); + + // map the key constraints + KeySizeConstraints keySizeConstraints = + new KeySizeConstraints(algorithmsInProperty); + keySizeConstraintsMap.put(propertyName, keySizeConstraints); + } + + /** + * key constraints + */ + private static class KeySizeConstraints { + private static final Pattern pattern = Pattern.compile( + "(\\S+)\\s+keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); + + private Map> constraintsMap = + Collections.synchronizedMap( + new HashMap>()); + + public KeySizeConstraints(String[] restrictions) { + for (String restriction : restrictions) { + if (restriction == null || restriction.isEmpty()) { + continue; + } + + Matcher matcher = pattern.matcher(restriction); + if (matcher.matches()) { + String algorithm = matcher.group(1); + + KeySizeConstraint.Operator operator = + KeySizeConstraint.Operator.of(matcher.group(2)); + int length = Integer.parseInt(matcher.group(3)); + + algorithm = algorithm.toLowerCase(Locale.ENGLISH); + + synchronized (constraintsMap) { + if (!constraintsMap.containsKey(algorithm)) { + constraintsMap.put(algorithm, + new HashSet()); + } + + Set constraintSet = + constraintsMap.get(algorithm); + KeySizeConstraint constraint = + new KeySizeConstraint(operator, length); + constraintSet.add(constraint); + } + } + } + } + + // Does this KeySizeConstraints disable the specified key? + public boolean disables(Key key) { + String algorithm = key.getAlgorithm().toLowerCase(Locale.ENGLISH); + synchronized (constraintsMap) { + if (constraintsMap.containsKey(algorithm)) { + Set constraintSet = + constraintsMap.get(algorithm); + for (KeySizeConstraint constraint : constraintSet) { + if (constraint.disables(key)) { + return true; + } + } + } + } + + return false; + } + } + + /** + * Key size constraint. + * + * e.g. "keysize <= 1024" + */ + private static class KeySizeConstraint { + // operator + static enum Operator { + EQ, // "==" + NE, // "!=" + LT, // "<" + LE, // "<=" + GT, // ">" + GE; // ">=" + + static Operator of(String s) { + switch (s) { + case "==": + return EQ; + case "!=": + return NE; + case "<": + return LT; + case "<=": + return LE; + case ">": + return GT; + case ">=": + return GE; + } + + throw new IllegalArgumentException( + s + " is not a legal Operator"); + } + } + + private int minSize; // the minimal available key size + private int maxSize; // the maximal available key size + private int prohibitedSize = -1; // unavailable key sizes + + public KeySizeConstraint(Operator operator, int length) { + switch (operator) { + case EQ: // an unavailable key size + this.minSize = 0; + this.maxSize = Integer.MAX_VALUE; + prohibitedSize = length; + break; + case NE: + this.minSize = length; + this.maxSize = length; + break; + case LT: + this.minSize = length; + this.maxSize = Integer.MAX_VALUE; + break; + case LE: + this.minSize = length + 1; + this.maxSize = Integer.MAX_VALUE; + break; + case GT: + this.minSize = 0; + this.maxSize = length; + break; + case GE: + this.minSize = 0; + this.maxSize = length > 1 ? (length - 1) : 0; + break; + default: + // unlikely to happen + this.minSize = Integer.MAX_VALUE; + this.maxSize = -1; + } + } + + // Does this key constraint disable the specified key? + public boolean disables(Key key) { + int size = KeyUtil.getKeySize(key); + + if (size == 0) { + return true; // we don't allow any key of size 0. + } else if (size > 0) { + return ((size < minSize) || (size > maxSize) || + (prohibitedSize == size)); + } // Otherwise, the key size is not accessible. Conservatively, + // please don't disable such keys. + + return false; + } + } + +} + diff --git a/src/sun/security/util/ECKeySizeParameterSpec.java b/src/sun/security/util/ECKeySizeParameterSpec.java new file mode 100644 index 00000000..d9c449bd --- /dev/null +++ b/src/sun/security/util/ECKeySizeParameterSpec.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.util; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * This immutable class is used when randomly generating a key pair and the + * consumer only specifies the length of the key and therefore a curve for that + * key size must be picked from a the list of supported curves using this spec. + * + * @see AlgorithmParameterSpec + * @see ECGenParameterSpec + */ +public class ECKeySizeParameterSpec implements AlgorithmParameterSpec { + + private int keySize; + + /** + * Creates a parameter specification for EC curve + * generation using a standard (or predefined) key size + * keySize in order to generate the corresponding + * (precomputed) elliptic curve. + *

    + * Note, if the curve of the specified length is not supported, + * AlgorithmParameters.init will throw an exception. + * + * @param keySize the key size of the curve to lookup + */ + public ECKeySizeParameterSpec(int keySize) { + this.keySize = keySize; + } + + /** + * Returns the key size of this spec. + * + * @return the standard or predefined key size. + */ + public int getKeySize() { + return keySize; + } +} diff --git a/src/sun/security/util/ECUtil.java b/src/sun/security/util/ECUtil.java new file mode 100644 index 00000000..e78ad431 --- /dev/null +++ b/src/sun/security/util/ECUtil.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.IOException; + +import java.math.BigInteger; + +import java.security.*; + +import java.security.interfaces.*; + +import java.security.spec.*; + +import java.util.Arrays; + +import sun.security.x509.X509Key; + +public final class ECUtil { + + // Used by SunPKCS11 and SunJSSE. + public static ECPoint decodePoint(byte[] data, EllipticCurve curve) + throws IOException { + if ((data.length == 0) || (data[0] != 4)) { + throw new IOException("Only uncompressed point format supported"); + } + // Per ANSI X9.62, an encoded point is a 1 byte type followed by + // ceiling(log base 2 field-size / 8) bytes of x and the same of y. + int n = (data.length - 1) / 2; + if (n != ((curve.getField().getFieldSize() + 7 ) >> 3)) { + throw new IOException("Point does not match field size"); + } + + byte[] xb = Arrays.copyOfRange(data, 1, 1 + n); + byte[] yb = Arrays.copyOfRange(data, n + 1, n + 1 + n); + + return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb)); + } + + // Used by SunPKCS11 and SunJSSE. + public static byte[] encodePoint(ECPoint point, EllipticCurve curve) { + // get field size in bytes (rounding up) + int n = (curve.getField().getFieldSize() + 7) >> 3; + byte[] xb = trimZeroes(point.getAffineX().toByteArray()); + byte[] yb = trimZeroes(point.getAffineY().toByteArray()); + if ((xb.length > n) || (yb.length > n)) { + throw new RuntimeException + ("Point coordinates do not match field size"); + } + byte[] b = new byte[1 + (n << 1)]; + b[0] = 4; // uncompressed + System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length); + System.arraycopy(yb, 0, b, b.length - yb.length, yb.length); + return b; + } + + public static byte[] trimZeroes(byte[] b) { + int i = 0; + while ((i < b.length - 1) && (b[i] == 0)) { + i++; + } + if (i == 0) { + return b; + } + + return Arrays.copyOfRange(b, i, b.length); + } + + private static KeyFactory getKeyFactory() { + try { + return KeyFactory.getInstance("EC", "SunEC"); + } catch (NoSuchAlgorithmException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + } + + public static ECPublicKey decodeX509ECPublicKey(byte[] encoded) + throws InvalidKeySpecException { + KeyFactory keyFactory = getKeyFactory(); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded); + + return (ECPublicKey)keyFactory.generatePublic(keySpec); + } + + public static byte[] x509EncodeECPublicKey(ECPoint w, + ECParameterSpec params) throws InvalidKeySpecException { + KeyFactory keyFactory = getKeyFactory(); + ECPublicKeySpec keySpec = new ECPublicKeySpec(w, params); + X509Key key = (X509Key)keyFactory.generatePublic(keySpec); + + return key.getEncoded(); + } + + public static ECPrivateKey decodePKCS8ECPrivateKey(byte[] encoded) + throws InvalidKeySpecException { + KeyFactory keyFactory = getKeyFactory(); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); + + return (ECPrivateKey)keyFactory.generatePrivate(keySpec); + } + + public static ECPrivateKey generateECPrivateKey(BigInteger s, + ECParameterSpec params) throws InvalidKeySpecException { + KeyFactory keyFactory = getKeyFactory(); + ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, params); + + return (ECPrivateKey)keyFactory.generatePrivate(keySpec); + } + + private static AlgorithmParameters getECParameters(Provider p) { + try { + if (p != null) { + return AlgorithmParameters.getInstance("EC", p); + } + + return AlgorithmParameters.getInstance("EC"); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException(nsae); + } + } + + public static byte[] encodeECParameterSpec(Provider p, + ECParameterSpec spec) { + AlgorithmParameters parameters = getECParameters(p); + + try { + parameters.init(spec); + } catch (InvalidParameterSpecException ipse) { + throw new RuntimeException("Not a known named curve: " + spec); + } + + try { + return parameters.getEncoded(); + } catch (IOException ioe) { + // it is a bug if this should happen + throw new RuntimeException(ioe); + } + } + + public static ECParameterSpec getECParameterSpec(Provider p, + ECParameterSpec spec) { + AlgorithmParameters parameters = getECParameters(p); + + try { + parameters.init(spec); + return parameters.getParameterSpec(ECParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + return null; + } + } + + public static ECParameterSpec getECParameterSpec(Provider p, + byte[] params) + throws IOException { + AlgorithmParameters parameters = getECParameters(p); + + parameters.init(params); + + try { + return parameters.getParameterSpec(ECParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + return null; + } + } + + public static ECParameterSpec getECParameterSpec(Provider p, String name) { + AlgorithmParameters parameters = getECParameters(p); + + try { + parameters.init(new ECGenParameterSpec(name)); + return parameters.getParameterSpec(ECParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + return null; + } + } + + public static ECParameterSpec getECParameterSpec(Provider p, int keySize) { + AlgorithmParameters parameters = getECParameters(p); + + try { + parameters.init(new ECKeySizeParameterSpec(keySize)); + return parameters.getParameterSpec(ECParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + return null; + } + + } + + public static String getCurveName(Provider p, ECParameterSpec spec) { + ECGenParameterSpec nameSpec; + AlgorithmParameters parameters = getECParameters(p); + + try { + parameters.init(spec); + nameSpec = parameters.getParameterSpec(ECGenParameterSpec.class); + } catch (InvalidParameterSpecException ipse) { + return null; + } + + if (nameSpec == null) { + return null; + } + + return nameSpec.getName(); + } + + public static boolean equals(ECParameterSpec spec1, ECParameterSpec spec2) { + if (spec1 == spec2) { + return true; + } + + if (spec1 == null || spec2 == null) { + return false; + } + return (spec1.getCofactor() == spec2.getCofactor() && + spec1.getOrder().equals(spec2.getOrder()) && + spec1.getCurve().equals(spec2.getCurve()) && + spec1.getGenerator().equals(spec2.getGenerator())); + } + + private ECUtil() {} +} diff --git a/src/sun/security/util/HostnameChecker.java b/src/sun/security/util/HostnameChecker.java new file mode 100644 index 00000000..77f17f71 --- /dev/null +++ b/src/sun/security/util/HostnameChecker.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.IOException; +import java.util.*; + +import java.security.Principal; +import java.security.cert.*; + +import javax.security.auth.x500.X500Principal; + +import sun.security.ssl.Krb5Helper; +import sun.security.x509.X500Name; + +import sun.net.util.IPAddressUtil; + +/** + * Class to check hostnames against the names specified in a certificate as + * required for TLS and LDAP. + * + */ +public class HostnameChecker { + + // Constant for a HostnameChecker for TLS + public final static byte TYPE_TLS = 1; + private final static HostnameChecker INSTANCE_TLS = + new HostnameChecker(TYPE_TLS); + + // Constant for a HostnameChecker for LDAP + public final static byte TYPE_LDAP = 2; + private final static HostnameChecker INSTANCE_LDAP = + new HostnameChecker(TYPE_LDAP); + + // constants for subject alt names of type DNS and IP + private final static int ALTNAME_DNS = 2; + private final static int ALTNAME_IP = 7; + + // the algorithm to follow to perform the check. Currently unused. + private final byte checkType; + + private HostnameChecker(byte checkType) { + this.checkType = checkType; + } + + /** + * Get a HostnameChecker instance. checkType should be one of the + * TYPE_* constants defined in this class. + */ + public static HostnameChecker getInstance(byte checkType) { + if (checkType == TYPE_TLS) { + return INSTANCE_TLS; + } else if (checkType == TYPE_LDAP) { + return INSTANCE_LDAP; + } + throw new IllegalArgumentException("Unknown check type: " + checkType); + } + + /** + * Perform the check. + * + * @exception CertificateException if the name does not match any of + * the names specified in the certificate + */ + public void match(String expectedName, X509Certificate cert) + throws CertificateException { + if (isIpAddress(expectedName)) { + matchIP(expectedName, cert); + } else { + matchDNS(expectedName, cert); + } + } + + /** + * Perform the check for Kerberos. + */ + public static boolean match(String expectedName, Principal principal) { + String hostName = getServerName(principal); + return (expectedName.equalsIgnoreCase(hostName)); + } + + /** + * Return the Server name from Kerberos principal. + */ + public static String getServerName(Principal principal) { + return Krb5Helper.getPrincipalHostName(principal); + } + + /** + * Test whether the given hostname looks like a literal IPv4 or IPv6 + * address. The hostname does not need to be a fully qualified name. + * + * This is not a strict check that performs full input validation. + * That means if the method returns true, name need not be a correct + * IP address, rather that it does not represent a valid DNS hostname. + * Likewise for IP addresses when it returns false. + */ + private static boolean isIpAddress(String name) { + if (IPAddressUtil.isIPv4LiteralAddress(name) || + IPAddressUtil.isIPv6LiteralAddress(name)) { + return true; + } else { + return false; + } + } + + /** + * Check if the certificate allows use of the given IP address. + * + * From RFC2818: + * In some cases, the URI is specified as an IP address rather than a + * hostname. In this case, the iPAddress subjectAltName must be present + * in the certificate and must exactly match the IP in the URI. + */ + private static void matchIP(String expectedIP, X509Certificate cert) + throws CertificateException { + Collection> subjAltNames = cert.getSubjectAlternativeNames(); + if (subjAltNames == null) { + throw new CertificateException + ("No subject alternative names present"); + } + for (List next : subjAltNames) { + // For IP address, it needs to be exact match + if (((Integer)next.get(0)).intValue() == ALTNAME_IP) { + String ipAddress = (String)next.get(1); + if (expectedIP.equalsIgnoreCase(ipAddress)) { + return; + } + } + } + throw new CertificateException("No subject alternative " + + "names matching " + "IP address " + + expectedIP + " found"); + } + + /** + * Check if the certificate allows use of the given DNS name. + * + * From RFC2818: + * If a subjectAltName extension of type dNSName is present, that MUST + * be used as the identity. Otherwise, the (most specific) Common Name + * field in the Subject field of the certificate MUST be used. Although + * the use of the Common Name is existing practice, it is deprecated and + * Certification Authorities are encouraged to use the dNSName instead. + * + * Matching is performed using the matching rules specified by + * [RFC2459]. If more than one identity of a given type is present in + * the certificate (e.g., more than one dNSName name, a match in any one + * of the set is considered acceptable.) + */ + private void matchDNS(String expectedName, X509Certificate cert) + throws CertificateException { + Collection> subjAltNames = cert.getSubjectAlternativeNames(); + if (subjAltNames != null) { + boolean foundDNS = false; + for ( List next : subjAltNames) { + if (((Integer)next.get(0)).intValue() == ALTNAME_DNS) { + foundDNS = true; + String dnsName = (String)next.get(1); + if (isMatched(expectedName, dnsName)) { + return; + } + } + } + if (foundDNS) { + // if certificate contains any subject alt names of type DNS + // but none match, reject + throw new CertificateException("No subject alternative DNS " + + "name matching " + expectedName + " found."); + } + } + X500Name subjectName = getSubjectX500Name(cert); + DerValue derValue = subjectName.findMostSpecificAttribute + (X500Name.commonName_oid); + if (derValue != null) { + try { + if (isMatched(expectedName, derValue.getAsString())) { + return; + } + } catch (IOException e) { + // ignore + } + } + String msg = "No name matching " + expectedName + " found"; + throw new CertificateException(msg); + } + + + /** + * Return the subject of a certificate as X500Name, by reparsing if + * necessary. X500Name should only be used if access to name components + * is required, in other cases X500Principal is to be preferred. + * + * This method is currently used from within JSSE, do not remove. + */ + public static X500Name getSubjectX500Name(X509Certificate cert) + throws CertificateParsingException { + try { + Principal subjectDN = cert.getSubjectDN(); + if (subjectDN instanceof X500Name) { + return (X500Name)subjectDN; + } else { + X500Principal subjectX500 = cert.getSubjectX500Principal(); + return new X500Name(subjectX500.getEncoded()); + } + } catch (IOException e) { + throw(CertificateParsingException) + new CertificateParsingException().initCause(e); + } + } + + + /** + * Returns true if name matches against template.

    + * + * The matching is performed as per RFC 2818 rules for TLS and + * RFC 2830 rules for LDAP.

    + * + * The name parameter should represent a DNS name. + * The template parameter + * may contain the wildcard character * + */ + private boolean isMatched(String name, String template) { + if (checkType == TYPE_TLS) { + return matchAllWildcards(name, template); + } else if (checkType == TYPE_LDAP) { + return matchLeftmostWildcard(name, template); + } else { + return false; + } + } + + + /** + * Returns true if name matches against template.

    + * + * According to RFC 2818, section 3.1 - + * Names may contain the wildcard character * which is + * considered to match any single domain name component + * or component fragment. + * E.g., *.a.com matches foo.a.com but not + * bar.foo.a.com. f*.com matches foo.com but not bar.com. + */ + private static boolean matchAllWildcards(String name, + String template) { + name = name.toLowerCase(Locale.ENGLISH); + template = template.toLowerCase(Locale.ENGLISH); + StringTokenizer nameSt = new StringTokenizer(name, "."); + StringTokenizer templateSt = new StringTokenizer(template, "."); + + if (nameSt.countTokens() != templateSt.countTokens()) { + return false; + } + + while (nameSt.hasMoreTokens()) { + if (!matchWildCards(nameSt.nextToken(), + templateSt.nextToken())) { + return false; + } + } + return true; + } + + + /** + * Returns true if name matches against template.

    + * + * As per RFC 2830, section 3.6 - + * The "*" wildcard character is allowed. If present, it applies only + * to the left-most name component. + * E.g. *.bar.com would match a.bar.com, b.bar.com, etc. but not + * bar.com. + */ + private static boolean matchLeftmostWildcard(String name, + String template) { + name = name.toLowerCase(Locale.ENGLISH); + template = template.toLowerCase(Locale.ENGLISH); + + // Retreive leftmost component + int templateIdx = template.indexOf("."); + int nameIdx = name.indexOf("."); + + if (templateIdx == -1) + templateIdx = template.length(); + if (nameIdx == -1) + nameIdx = name.length(); + + if (matchWildCards(name.substring(0, nameIdx), + template.substring(0, templateIdx))) { + + // match rest of the name + return template.substring(templateIdx).equals( + name.substring(nameIdx)); + } else { + return false; + } + } + + + /** + * Returns true if the name matches against the template that may + * contain wildcard char *

    + */ + private static boolean matchWildCards(String name, String template) { + + int wildcardIdx = template.indexOf("*"); + if (wildcardIdx == -1) + return name.equals(template); + + boolean isBeginning = true; + String beforeWildcard = ""; + String afterWildcard = template; + + while (wildcardIdx != -1) { + + // match in sequence the non-wildcard chars in the template. + beforeWildcard = afterWildcard.substring(0, wildcardIdx); + afterWildcard = afterWildcard.substring(wildcardIdx + 1); + + int beforeStartIdx = name.indexOf(beforeWildcard); + if ((beforeStartIdx == -1) || + (isBeginning && beforeStartIdx != 0)) { + return false; + } + isBeginning = false; + + // update the match scope + name = name.substring(beforeStartIdx + beforeWildcard.length()); + wildcardIdx = afterWildcard.indexOf("*"); + } + return name.endsWith(afterWildcard); + } +} diff --git a/src/sun/security/util/KeyUtil.java b/src/sun/security/util/KeyUtil.java new file mode 100644 index 00000000..47208b2e --- /dev/null +++ b/src/sun/security/util/KeyUtil.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.Key; +import java.security.InvalidKeyException; +import java.security.interfaces.ECKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.DSAKey; +import java.security.SecureRandom; +import java.security.spec.KeySpec; +import javax.crypto.SecretKey; +import javax.crypto.interfaces.DHKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; +import java.math.BigInteger; + +/** + * A utility class to get key length, valiate keys, etc. + */ +public final class KeyUtil { + + /** + * Returns the key size of the given key object in bits. + * + * @param key the key object, cannot be null + * @return the key size of the given key object in bits, or -1 if the + * key size is not accessible + */ + public static final int getKeySize(Key key) { + int size = -1; + + if (key instanceof Length) { + try { + Length ruler = (Length)key; + size = ruler.length(); + } catch (UnsupportedOperationException usoe) { + // ignore the exception + } + + if (size >= 0) { + return size; + } + } + + // try to parse the length from key specification + if (key instanceof SecretKey) { + SecretKey sk = (SecretKey)key; + String format = sk.getFormat(); + if ("RAW".equals(format) && sk.getEncoded() != null) { + size = (sk.getEncoded().length * 8); + } // Otherwise, it may be a unextractable key of PKCS#11, or + // a key we are not able to handle. + } else if (key instanceof RSAKey) { + RSAKey pubk = (RSAKey)key; + size = pubk.getModulus().bitLength(); + } else if (key instanceof ECKey) { + ECKey pubk = (ECKey)key; + size = pubk.getParams().getOrder().bitLength(); + } else if (key instanceof DSAKey) { + DSAKey pubk = (DSAKey)key; + size = pubk.getParams().getP().bitLength(); + } else if (key instanceof DHKey) { + DHKey pubk = (DHKey)key; + size = pubk.getParams().getP().bitLength(); + } // Otherwise, it may be a unextractable key of PKCS#11, or + // a key we are not able to handle. + + return size; + } + + /** + * Returns whether the key is valid or not. + *

    + * Note that this method is only apply to DHPublicKey at present. + * + * @param publicKey + * the key object, cannot be null + * + * @throws NullPointerException if {@code publicKey} is null + * @throws InvalidKeyException if {@code publicKey} is invalid + */ + public static final void validate(Key key) + throws InvalidKeyException { + if (key == null) { + throw new NullPointerException( + "The key to be validated cannot be null"); + } + + if (key instanceof DHPublicKey) { + validateDHPublicKey((DHPublicKey)key); + } + } + + + /** + * Returns whether the key spec is valid or not. + *

    + * Note that this method is only apply to DHPublicKeySpec at present. + * + * @param keySpec + * the key spec object, cannot be null + * + * @throws NullPointerException if {@code keySpec} is null + * @throws InvalidKeyException if {@code keySpec} is invalid + */ + public static final void validate(KeySpec keySpec) + throws InvalidKeyException { + if (keySpec == null) { + throw new NullPointerException( + "The key spec to be validated cannot be null"); + } + + if (keySpec instanceof DHPublicKeySpec) { + validateDHPublicKey((DHPublicKeySpec)keySpec); + } + } + + /** + * Returns whether the specified provider is Oracle provider or not. + *

    + * Note that this method is only apply to SunJCE and SunPKCS11 at present. + * + * @param providerName + * the provider name + * @return true if, and only if, the provider of the specified + * {@code providerName} is Oracle provider + */ + public static final boolean isOracleJCEProvider(String providerName) { + return providerName != null && (providerName.equals("SunJCE") || + providerName.startsWith("SunPKCS11")); + } + + /** + * Check the format of TLS PreMasterSecret. + *

    + * To avoid vulnerabilities described by section 7.4.7.1, RFC 5246, + * treating incorrectly formatted message blocks and/or mismatched + * version numbers in a manner indistinguishable from correctly + * formatted RSA blocks. + * + * RFC 5246 describes the approach as : + * + * 1. Generate a string R of 48 random bytes + * + * 2. Decrypt the message to recover the plaintext M + * + * 3. If the PKCS#1 padding is not correct, or the length of message + * M is not exactly 48 bytes: + * pre_master_secret = R + * else If ClientHello.client_version <= TLS 1.0, and version + * number check is explicitly disabled: + * premaster secret = M + * else If M[0..1] != ClientHello.client_version: + * premaster secret = R + * else: + * premaster secret = M + * + * Note that #2 should have completed before the call to this method. + * + * @param clientVersion the version of the TLS protocol by which the + * client wishes to communicate during this session + * @param serverVersion the negotiated version of the TLS protocol which + * contains the lower of that suggested by the client in the client + * hello and the highest supported by the server. + * @param encoded the encoded key in its "RAW" encoding format + * @param isFailover whether or not the previous decryption of the + * encrypted PreMasterSecret message run into problem + * @return the polished PreMasterSecret key in its "RAW" encoding format + */ + public static byte[] checkTlsPreMasterSecretKey( + int clientVersion, int serverVersion, SecureRandom random, + byte[] encoded, boolean isFailOver) { + + if (random == null) { + random = new SecureRandom(); + } + byte[] replacer = new byte[48]; + random.nextBytes(replacer); + + if (!isFailOver && (encoded != null)) { + // check the length + if (encoded.length != 48) { + // private, don't need to clone the byte array. + return replacer; + } + + int encodedVersion = + ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF); + if (clientVersion != encodedVersion) { + if (clientVersion > 0x0301 || // 0x0301: TLSv1 + serverVersion != encodedVersion) { + encoded = replacer; + } // Otherwise, For compatibility, we maintain the behavior + // that the version in pre_master_secret can be the + // negotiated version for TLS v1.0 and SSL v3.0. + } + + // private, don't need to clone the byte array. + return encoded; + } + + // private, don't need to clone the byte array. + return replacer; + } + + /** + * Returns whether the Diffie-Hellman public key is valid or not. + * + * Per RFC 2631 and NIST SP800-56A, the following algorithm is used to + * validate Diffie-Hellman public keys: + * 1. Verify that y lies within the interval [2,p-1]. If it does not, + * the key is invalid. + * 2. Compute y^q mod p. If the result == 1, the key is valid. + * Otherwise the key is invalid. + */ + private static void validateDHPublicKey(DHPublicKey publicKey) + throws InvalidKeyException { + DHParameterSpec paramSpec = publicKey.getParams(); + + BigInteger p = paramSpec.getP(); + BigInteger g = paramSpec.getG(); + BigInteger y = publicKey.getY(); + + validateDHPublicKey(p, g, y); + } + + private static void validateDHPublicKey(DHPublicKeySpec publicKeySpec) + throws InvalidKeyException { + validateDHPublicKey(publicKeySpec.getP(), + publicKeySpec.getG(), publicKeySpec.getY()); + } + + private static void validateDHPublicKey(BigInteger p, + BigInteger g, BigInteger y) throws InvalidKeyException { + + // For better interoperability, the interval is limited to [2, p-2]. + BigInteger leftOpen = BigInteger.ONE; + BigInteger rightOpen = p.subtract(BigInteger.ONE); + if (y.compareTo(leftOpen) <= 0) { + throw new InvalidKeyException( + "Diffie-Hellman public key is too small"); + } + if (y.compareTo(rightOpen) >= 0) { + throw new InvalidKeyException( + "Diffie-Hellman public key is too large"); + } + + // y^q mod p == 1? + // Unable to perform this check as q is unknown in this circumstance. + + // p is expected to be prime. However, it is too expensive to check + // that p is prime. Instead, in order to mitigate the impact of + // non-prime values, we check that y is not a factor of p. + BigInteger r = p.remainder(y); + if (r.equals(BigInteger.ZERO)) { + throw new InvalidKeyException("Invalid Diffie-Hellman parameters"); + } + } + + /** + * Trim leading (most significant) zeroes from the result. + * + * @throws NullPointerException if {@code b} is null + */ + public static byte[] trimZeroes(byte[] b) { + int i = 0; + while ((i < b.length - 1) && (b[i] == 0)) { + i++; + } + if (i == 0) { + return b; + } + byte[] t = new byte[b.length - i]; + System.arraycopy(b, i, t, 0, t.length); + return t; + } + +} + diff --git a/src/sun/security/util/Length.java b/src/sun/security/util/Length.java new file mode 100644 index 00000000..1207e577 --- /dev/null +++ b/src/sun/security/util/Length.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + * The Length interface defines the length of an object + */ +public interface Length { + + /** + * Gets the length of this object + *

    + * Note that if a class of java.security.Key implements this interfaces, + * the length should be measured in bits. + * + * @return the length of this object + * @throws UnsupportedOperationException if the operation is not supported + */ + public int length(); +} diff --git a/src/sun/security/util/ManifestDigester.java b/src/sun/security/util/ManifestDigester.java new file mode 100644 index 00000000..fc9ccd3d --- /dev/null +++ b/src/sun/security/util/ManifestDigester.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.*; +import java.util.HashMap; +import java.io.ByteArrayOutputStream; + +/** + * This class is used to compute digests on sections of the Manifest. + */ +public class ManifestDigester { + + public static final String MF_MAIN_ATTRS = "Manifest-Main-Attributes"; + + /** the raw bytes of the manifest */ + private byte rawBytes[]; + + /** the offset/length pair for a section */ + private HashMap entries; // key is a UTF-8 string + + /** state returned by findSection */ + static class Position { + int endOfFirstLine; // not including newline character + + int endOfSection; // end of section, not including the blank line + // between sections + int startOfNext; // the start of the next section + } + + /** + * find a section in the manifest. + * + * @param offset should point to the starting offset with in the + * raw bytes of the next section. + * + * @pos set by + * + * @returns false if end of bytes has been reached, otherwise returns + * true + */ + @SuppressWarnings("fallthrough") + private boolean findSection(int offset, Position pos) + { + int i = offset, len = rawBytes.length; + int last = offset; + int next; + boolean allBlank = true; + + pos.endOfFirstLine = -1; + + while (i < len) { + byte b = rawBytes[i]; + switch(b) { + case '\r': + if (pos.endOfFirstLine == -1) + pos.endOfFirstLine = i-1; + if ((i < len) && (rawBytes[i+1] == '\n')) + i++; + /* fall through */ + case '\n': + if (pos.endOfFirstLine == -1) + pos.endOfFirstLine = i-1; + if (allBlank || (i == len-1)) { + if (i == len-1) + pos.endOfSection = i; + else + pos.endOfSection = last; + pos.startOfNext = i+1; + return true; + } + else { + // start of a new line + last = i; + allBlank = true; + } + break; + default: + allBlank = false; + break; + } + i++; + } + return false; + } + + public ManifestDigester(byte bytes[]) + { + rawBytes = bytes; + entries = new HashMap(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + Position pos = new Position(); + + if (!findSection(0, pos)) + return; // XXX: exception? + + // create an entry for main attributes + entries.put(MF_MAIN_ATTRS, + new Entry(0, pos.endOfSection + 1, pos.startOfNext, rawBytes)); + + int start = pos.startOfNext; + while(findSection(start, pos)) { + int len = pos.endOfFirstLine-start+1; + int sectionLen = pos.endOfSection-start+1; + int sectionLenWithBlank = pos.startOfNext-start; + + if (len > 6) { + if (isNameAttr(bytes, start)) { + StringBuilder nameBuf = new StringBuilder(sectionLen); + + try { + nameBuf.append( + new String(bytes, start+6, len-6, "UTF8")); + + int i = start + len; + if ((i-start) < sectionLen) { + if (bytes[i] == '\r') { + i += 2; + } else { + i += 1; + } + } + + while ((i-start) < sectionLen) { + if (bytes[i++] == ' ') { + // name is wrapped + int wrapStart = i; + while (((i-start) < sectionLen) + && (bytes[i++] != '\n')); + if (bytes[i-1] != '\n') + return; // XXX: exception? + int wrapLen; + if (bytes[i-2] == '\r') + wrapLen = i-wrapStart-2; + else + wrapLen = i-wrapStart-1; + + nameBuf.append(new String(bytes, wrapStart, + wrapLen, "UTF8")); + } else { + break; + } + } + + entries.put(nameBuf.toString(), + new Entry(start, sectionLen, sectionLenWithBlank, + rawBytes)); + + } catch (java.io.UnsupportedEncodingException uee) { + throw new IllegalStateException( + "UTF8 not available on platform"); + } + } + } + start = pos.startOfNext; + } + } + + private boolean isNameAttr(byte bytes[], int start) + { + return ((bytes[start] == 'N') || (bytes[start] == 'n')) && + ((bytes[start+1] == 'a') || (bytes[start+1] == 'A')) && + ((bytes[start+2] == 'm') || (bytes[start+2] == 'M')) && + ((bytes[start+3] == 'e') || (bytes[start+3] == 'E')) && + (bytes[start+4] == ':') && + (bytes[start+5] == ' '); + } + + public static class Entry { + int offset; + int length; + int lengthWithBlankLine; + byte[] rawBytes; + boolean oldStyle; + + public Entry(int offset, int length, + int lengthWithBlankLine, byte[] rawBytes) + { + this.offset = offset; + this.length = length; + this.lengthWithBlankLine = lengthWithBlankLine; + this.rawBytes = rawBytes; + } + + public byte[] digest(MessageDigest md) + { + md.reset(); + if (oldStyle) { + doOldStyle(md,rawBytes, offset, lengthWithBlankLine); + } else { + md.update(rawBytes, offset, lengthWithBlankLine); + } + return md.digest(); + } + + private void doOldStyle(MessageDigest md, + byte[] bytes, + int offset, + int length) + { + // this is too gross to even document, but here goes + // the 1.1 jar verification code ignored spaces at the + // end of lines when calculating digests, so that is + // what this code does. It only gets called if we + // are parsing a 1.1 signed signature file + int i = offset; + int start = offset; + int max = offset + length; + int prev = -1; + while(i createdDigests; + + /** the digests in use for a given entry*/ + ArrayList digests; + + /** the manifest hashes for the digests in use */ + ArrayList manifestHashes; + + private String name = null; + private Manifest man; + + private boolean skip = true; + + private JarEntry entry; + + private CodeSigner[] signers = null; + + /** + * Create a new ManifestEntryVerifier object. + */ + public ManifestEntryVerifier(Manifest man) + { + createdDigests = new HashMap(11); + digests = new ArrayList(); + manifestHashes = new ArrayList(); + this.man = man; + } + + /** + * Find the hashes in the + * manifest for this entry, save them, and set the MessageDigest + * objects to calculate the hashes on the fly. If name is + * null it signifies that update/verify should ignore this entry. + */ + public void setEntry(String name, JarEntry entry) + throws IOException + { + digests.clear(); + manifestHashes.clear(); + this.name = name; + this.entry = entry; + + skip = true; + signers = null; + + if (man == null || name == null) { + return; + } + + /* get the headers from the manifest for this entry */ + /* if there aren't any, we can't verify any digests for this entry */ + + Attributes attr = man.getAttributes(name); + if (attr == null) { + // ugh. we should be able to remove this at some point. + // there are broken jars floating around with ./name and /name + // in the manifest, and "name" in the zip/jar file. + attr = man.getAttributes("./"+name); + if (attr == null) { + attr = man.getAttributes("/"+name); + if (attr == null) + return; + } + } + + for (Map.Entry se : attr.entrySet()) { + String key = se.getKey().toString(); + + if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { + // 7 is length of "-Digest" + String algorithm = key.substring(0, key.length()-7); + + MessageDigest digest = createdDigests.get(algorithm); + + if (digest == null) { + try { + + digest = MessageDigest.getInstance + (algorithm, SunProviderHolder.instance); + createdDigests.put(algorithm, digest); + } catch (NoSuchAlgorithmException nsae) { + // ignore + } + } + + if (digest != null) { + skip = false; + digest.reset(); + digests.add(digest); + manifestHashes.add( + Base64.getMimeDecoder().decode((String)se.getValue())); + } + } + } + } + + /** + * update the digests for the digests we are interested in + */ + public void update(byte buffer) { + if (skip) return; + + for (int i=0; i < digests.size(); i++) { + digests.get(i).update(buffer); + } + } + + /** + * update the digests for the digests we are interested in + */ + public void update(byte buffer[], int off, int len) { + if (skip) return; + + for (int i=0; i < digests.size(); i++) { + digests.get(i).update(buffer, off, len); + } + } + + /** + * get the JarEntry for this object + */ + public JarEntry getEntry() + { + return entry; + } + + /** + * go through all the digests, calculating the final digest + * and comparing it to the one in the manifest. If this is + * the first time we have verified this object, remove its + * code signers from sigFileSigners and place in verifiedSigners. + * + * + */ + public CodeSigner[] verify(Hashtable verifiedSigners, + Hashtable sigFileSigners) + throws JarException + { + if (skip) { + return null; + } + + if (signers != null) + return signers; + + for (int i=0; i < digests.size(); i++) { + + MessageDigest digest = digests.get(i); + byte [] manHash = manifestHashes.get(i); + byte [] theHash = digest.digest(); + + if (debug != null) { + debug.println("Manifest Entry: " + + name + " digest=" + digest.getAlgorithm()); + debug.println(" manifest " + toHex(manHash)); + debug.println(" computed " + toHex(theHash)); + debug.println(); + } + + if (!MessageDigest.isEqual(theHash, manHash)) + throw new SecurityException(digest.getAlgorithm()+ + " digest error for "+name); + } + + // take it out of sigFileSigners and put it in verifiedSigners... + signers = sigFileSigners.remove(name); + if (signers != null) { + verifiedSigners.put(name, signers); + } + return signers; + } + + // for the toHex function + private static final char[] hexc = + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + /** + * convert a byte array to a hex string for debugging purposes + * @param data the binary data to be converted to a hex string + * @return an ASCII hex string + */ + + static String toHex(byte[] data) { + + StringBuffer sb = new StringBuffer(data.length*2); + + for (int i=0; i>4) & 0x0f]); + sb.append(hexc[data[i] & 0x0f]); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/util/ObjectIdentifier.java b/src/sun/security/util/ObjectIdentifier.java new file mode 100644 index 00000000..66038a37 --- /dev/null +++ b/src/sun/security/util/ObjectIdentifier.java @@ -0,0 +1,662 @@ +/* + * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.*; +import java.math.BigInteger; +import java.util.Arrays; + +/** + * Represent an ISO Object Identifier. + * + *

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

    So for example, JavaSoft has the sole authority to assign the + * meaning to identifiers below the 1.3.6.1.4.1.42.2.17 node in the + * hierarchy, and other organizations can easily acquire the ability + * to assign such unique identifiers. + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ + +final public +class ObjectIdentifier implements Serializable +{ + /** + * We use the DER value (no tag, no length) as the internal format + * @serial + */ + private byte[] encoding = null; + + private transient volatile String stringForm; + + /* + * IMPORTANT NOTES FOR CODE CHANGES (bug 4811968) IN JDK 1.7.0 + * =========================================================== + * + * (Almost) serialization compatibility with old versions: + * + * serialVersionUID is unchanged. Old field "component" is changed to + * type Object so that "poison" (unknown object type for old versions) + * can be put inside if there are huge components that cannot be saved + * as integers. + * + * New version use the new filed "encoding" only. + * + * Below are all 4 cases in a serialization/deserialization process: + * + * 1. old -> old: Not covered here + * 2. old -> new: There's no "encoding" field, new readObject() reads + * "components" and "componentLen" instead and inits correctly. + * 3. new -> new: "encoding" field exists, new readObject() uses it + * (ignoring the other 2 fields) and inits correctly. + * 4. new -> old: old readObject() only recognizes "components" and + * "componentLen" fields. If no huge components are involved, they + * are serialized as legal values and old object can init correctly. + * Otherwise, old object cannot recognize the form (component not int[]) + * and throw a ClassNotFoundException at deserialization time. + * + * Therfore, for the first 3 cases, exact compatibility is preserved. In + * the 4th case, non-huge OID is still supportable in old versions, while + * huge OID is not. + */ + private static final long serialVersionUID = 8697030238860181294L; + + /** + * Changed to Object + * @serial + */ + private Object components = null; // path from root + /** + * @serial + */ + private int componentLen = -1; // how much is used. + + // Is the components field calculated? + transient private boolean componentsCalculated = false; + + private void readObject(ObjectInputStream is) + throws IOException, ClassNotFoundException { + is.defaultReadObject(); + + if (encoding == null) { // from an old version + init((int[])components, componentLen); + } + } + + private void writeObject(ObjectOutputStream os) + throws IOException { + if (!componentsCalculated) { + int[] comps = toIntArray(); + if (comps != null) { // every one understands this + components = comps; + componentLen = comps.length; + } else { + components = HugeOidNotSupportedByOldJDK.theOne; + } + componentsCalculated = true; + } + os.defaultWriteObject(); + } + + static class HugeOidNotSupportedByOldJDK implements Serializable { + private static final long serialVersionUID = 1L; + static HugeOidNotSupportedByOldJDK theOne = new HugeOidNotSupportedByOldJDK(); + } + + /** + * Constructs, from a string. This string should be of the form 1.23.56. + * Validity check included. + */ + public ObjectIdentifier (String oid) throws IOException + { + int ch = '.'; + int start = 0; + int end = 0; + + int pos = 0; + byte[] tmp = new byte[oid.length()]; + int first = 0, second; + int count = 0; + + try { + String comp = null; + do { + int length = 0; // length of one section + end = oid.indexOf(ch,start); + if (end == -1) { + comp = oid.substring(start); + length = oid.length() - start; + } else { + comp = oid.substring(start,end); + length = end - start; + } + + if (length > 9) { + BigInteger bignum = new BigInteger(comp); + if (count == 0) { + checkFirstComponent(bignum); + first = bignum.intValue(); + } else { + if (count == 1) { + checkSecondComponent(first, bignum); + bignum = bignum.add(BigInteger.valueOf(40*first)); + } else { + checkOtherComponent(count, bignum); + } + pos += pack7Oid(bignum, tmp, pos); + } + } else { + int num = Integer.parseInt(comp); + if (count == 0) { + checkFirstComponent(num); + first = num; + } else { + if (count == 1) { + checkSecondComponent(first, num); + num += 40 * first; + } else { + checkOtherComponent(count, num); + } + pos += pack7Oid(num, tmp, pos); + } + } + start = end + 1; + count++; + } while (end != -1); + + checkCount(count); + encoding = new byte[pos]; + System.arraycopy(tmp, 0, encoding, 0, pos); + this.stringForm = oid; + } catch (IOException ioe) { // already detected by checkXXX + throw ioe; + } catch (Exception e) { + throw new IOException("ObjectIdentifier() -- Invalid format: " + + e.toString(), e); + } + } + + /** + * Constructor, from an array of integers. + * Validity check included. + */ + public ObjectIdentifier (int values []) throws IOException + { + checkCount(values.length); + checkFirstComponent(values[0]); + checkSecondComponent(values[0], values[1]); + for (int i=2; iNOTE: 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 ( + "ObjectIdentifier() -- data isn't an object ID" + + " (tag = " + type_id + ")" + ); + + encoding = new byte[in.getLength()]; + in.getBytes(encoding); + check(encoding); + } + + /* + * Constructor, from the rest of a DER input buffer; + * the tag and length have been removed/verified + * Validity check NOT included. + */ + ObjectIdentifier (DerInputBuffer buf) throws IOException + { + DerInputStream in = new DerInputStream(buf); + encoding = new byte[in.available()]; + in.getBytes(encoding); + check(encoding); + } + + private void init(int[] components, int length) { + int pos = 0; + byte[] tmp = new byte[length*5+1]; // +1 for empty input + + if (components[1] < Integer.MAX_VALUE - components[0]*40) + pos += pack7Oid(components[0]*40+components[1], tmp, pos); + else { + BigInteger big = BigInteger.valueOf(components[1]); + big = big.add(BigInteger.valueOf(components[0]*40)); + pos += pack7Oid(big, tmp, pos); + } + + for (int i=2; i 4) { + BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8)); + if (fromPos == 0) { + result[which++] = 2; + BigInteger second = big.subtract(BigInteger.valueOf(80)); + if (second.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { + return null; + } else { + result[which++] = second.intValue(); + } + } else { + if (big.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) { + return null; + } else { + result[which++] = big.intValue(); + } + } + } else { + int retval = 0; + for (int j = fromPos; j <= i; j++) { + retval <<= 7; + byte tmp = encoding[j]; + retval |= (tmp & 0x07f); + } + if (fromPos == 0) { + if (retval < 80) { + result[which++] = retval / 40; + result[which++] = retval % 40; + } else { + result[which++] = 2; + result[which++] = retval - 80; + } + } else { + result[which++] = retval; + } + } + fromPos = i+1; + } + if (which >= result.length) { + result = Arrays.copyOf(result, which + 10); + } + } + return Arrays.copyOf(result, which); + } + + /** + * 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. + */ + @Override + public String toString() { + String s = stringForm; + if (s == null) { + int length = encoding.length; + StringBuffer sb = new StringBuffer(length * 4); + + int fromPos = 0; + for (int i = 0; i < length; i++) { + if ((encoding[i] & 0x80) == 0) { + // one section [fromPos..i] + if (fromPos != 0) { // not the first segment + sb.append('.'); + } + if (i - fromPos + 1 > 4) { // maybe big integer + BigInteger big = new BigInteger(pack(encoding, fromPos, i-fromPos+1, 7, 8)); + if (fromPos == 0) { + // first section encoded with more than 4 bytes, + // must be 2.something + sb.append("2."); + sb.append(big.subtract(BigInteger.valueOf(80))); + } else { + sb.append(big); + } + } else { // small integer + int retval = 0; + for (int j = fromPos; j <= i; j++) { + retval <<= 7; + byte tmp = encoding[j]; + retval |= (tmp & 0x07f); + } + if (fromPos == 0) { + if (retval < 80) { + sb.append(retval/40); + sb.append('.'); + sb.append(retval%40); + } else { + sb.append("2."); + sb.append(retval - 80); + } + } else { + sb.append(retval); + } + } + fromPos = i+1; + } + } + s = sb.toString(); + stringForm = s; + } + return s; + } + + /** + * Repack all bits from input to output. On the both sides, only a portion + * (from the least significant bit) of the 8 bits in a byte is used. This + * number is defined as the number of useful bits (NUB) for the array. All the + * used bits from the input byte array and repacked into the output in the + * exactly same order. The output bits are aligned so that the final bit of + * the input (the least significant bit in the last byte), when repacked as + * the final bit of the output, is still at the least significant position. + * Zeroes will be padded on the left side of the first output byte if + * necessary. All unused bits in the output are also zeroed. + * + * For example: if the input is 01001100 with NUB 8, the output which + * has a NUB 6 will look like: + * 00000001 00001100 + * The first 2 bits of the output bytes are unused bits. The other bits + * turn out to be 000001 001100. While the 8 bits on the right are from + * the input, the left 4 zeroes are padded to fill the 6 bits space. + * + * @param in the input byte array + * @param ioffset start point inside in + * @param ilength number of bytes to repack + * @param iw NUB for input + * @param ow NUB for output + * @return the repacked bytes + */ + private static byte[] pack(byte[] in, int ioffset, int ilength, int iw, int ow) { + assert (iw > 0 && iw <= 8): "input NUB must be between 1 and 8"; + assert (ow > 0 && ow <= 8): "output NUB must be between 1 and 8"; + + if (iw == ow) { + return in.clone(); + } + + int bits = ilength * iw; // number of all used bits + byte[] out = new byte[(bits+ow-1)/ow]; + + // starting from the 0th bit in the input + int ipos = 0; + + // the number of padding 0's needed in the output, skip them + int opos = (bits+ow-1)/ow*ow-bits; + + while(ipos < bits) { + int count = iw - ipos%iw; // unpacked bits in current input byte + if (count > ow - opos%ow) { // free space available in output byte + count = ow - opos%ow; // choose the smaller number + } + // and move them! + out[opos/ow] |= // paste! + (((in[ioffset+ipos/iw]+256) // locate the byte (+256 so that it's never negative) + >> (iw-ipos%iw-count)) // move to the end of a byte + & ((1 << (count))-1)) // zero out all other bits + << (ow-opos%ow-count); // move to the output position + ipos += count; // advance + opos += count; // advance + } + return out; + } + + /** + * Repack from NUB 8 to a NUB 7 OID sub-identifier, remove all + * unnecessary 0 headings, set the first bit of all non-tail + * output bytes to 1 (as ITU-T Rec. X.690 8.19.2 says), and + * paste it into an existing byte array. + * @param out the existing array to be pasted into + * @param ooffset the starting position to paste + * @return the number of bytes pasted + */ + private static int pack7Oid(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { + byte[] pack = pack(in, ioffset, ilength, 8, 7); + int firstNonZero = pack.length-1; // paste at least one byte + for (int i=pack.length-2; i>=0; i--) { + if (pack[i] != 0) { + firstNonZero = i; + } + pack[i] |= 0x80; + } + System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); + return pack.length-firstNonZero; + } + + /** + * Repack from NUB 7 to NUB 8, remove all unnecessary 0 + * headings, and paste it into an existing byte array. + * @param out the existing array to be pasted into + * @param ooffset the starting position to paste + * @return the number of bytes pasted + */ + private static int pack8(byte[] in, int ioffset, int ilength, byte[] out, int ooffset) { + byte[] pack = pack(in, ioffset, ilength, 7, 8); + int firstNonZero = pack.length-1; // paste at least one byte + for (int i=pack.length-2; i>=0; i--) { + if (pack[i] != 0) { + firstNonZero = i; + } + } + System.arraycopy(pack, firstNonZero, out, ooffset, pack.length-firstNonZero); + return pack.length-firstNonZero; + } + + /** + * Pack the int into a OID sub-identifier DER encoding + */ + private static int pack7Oid(int input, byte[] out, int ooffset) { + byte[] b = new byte[4]; + b[0] = (byte)(input >> 24); + b[1] = (byte)(input >> 16); + b[2] = (byte)(input >> 8); + b[3] = (byte)(input); + return pack7Oid(b, 0, 4, out, ooffset); + } + + /** + * Pack the BigInteger into a OID subidentifier DER encoding + */ + private static int pack7Oid(BigInteger input, byte[] out, int ooffset) { + byte[] b = input.toByteArray(); + return pack7Oid(b, 0, b.length, out, ooffset); + } + + /** + * Private methods to check validity of OID. They must be -- + * 1. at least 2 components + * 2. all components must be non-negative + * 3. the first must be 0, 1 or 2 + * 4. if the first is 0 or 1, the second must be <40 + */ + + /** + * Check the DER encoding. Since DER encoding defines that the integer bits + * are unsigned, so there's no need to check the MSB. + */ + private static void check(byte[] encoding) throws IOException { + int length = encoding.length; + if (length < 1 || // too short + (encoding[length - 1] & 0x80) != 0) { // not ended + throw new IOException("ObjectIdentifier() -- " + + "Invalid DER encoding, not ended"); + } + for (int i=0; i 2) { + throw new IOException("ObjectIdentifier() -- " + + "First oid component is invalid "); + } + } + private static void checkFirstComponent(BigInteger first) throws IOException { + if (first.signum() == -1 || + first.compareTo(BigInteger.valueOf(2)) == 1) { + throw new IOException("ObjectIdentifier() -- " + + "First oid component is invalid "); + } + } + private static void checkSecondComponent(int first, int second) throws IOException { + if (second < 0 || first != 2 && second > 39) { + throw new IOException("ObjectIdentifier() -- " + + "Second oid component is invalid "); + } + } + private static void checkSecondComponent(int first, BigInteger second) throws IOException { + if (second.signum() == -1 || + first != 2 && + second.compareTo(BigInteger.valueOf(39)) == 1) { + throw new IOException("ObjectIdentifier() -- " + + "Second oid component is invalid "); + } + } + private static void checkOtherComponent(int i, int num) throws IOException { + if (num < 0) { + throw new IOException("ObjectIdentifier() -- " + + "oid component #" + (i+1) + " must be non-negative "); + } + } + private static void checkOtherComponent(int i, BigInteger num) throws IOException { + if (num.signum() == -1) { + throw new IOException("ObjectIdentifier() -- " + + "oid component #" + (i+1) + " must be non-negative "); + } + } +} diff --git a/src/sun/security/util/Password.java b/src/sun/security/util/Password.java new file mode 100644 index 00000000..619011f7 --- /dev/null +++ b/src/sun/security/util/Password.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.*; +import java.nio.*; +import java.nio.charset.*; +import java.util.Arrays; + +/** + * A utility class for reading passwords + * + */ +public class Password { + /** Reads user password from given input stream. */ + public static char[] readPassword(InputStream in) throws IOException { + return readPassword(in, false); + } + + /** Reads user password from given input stream. + * @param isEchoOn true if the password should be echoed on the screen + */ + @SuppressWarnings("fallthrough") + public static char[] readPassword(InputStream in, boolean isEchoOn) + throws IOException { + + char[] consoleEntered = null; + byte[] consoleBytes = null; + + try { + // Use the new java.io.Console class + Console con = null; + if (!isEchoOn && in == System.in && ((con = System.console()) != null)) { + consoleEntered = con.readPassword(); + // readPassword returns "" if you just print ENTER, + // to be compatible with old Password class, change to null + if (consoleEntered != null && consoleEntered.length == 0) { + return null; + } + consoleBytes = convertToBytes(consoleEntered); + in = new ByteArrayInputStream(consoleBytes); + } + + // Rest of the lines still necessary for KeyStoreLoginModule + // and when there is no console. + + char[] lineBuffer; + char[] buf; + int i; + + buf = lineBuffer = new char[128]; + + int room = buf.length; + int offset = 0; + int c; + + boolean done = false; + while (!done) { + switch (c = in.read()) { + case -1: + case '\n': + done = true; + break; + + case '\r': + int c2 = in.read(); + if ((c2 != '\n') && (c2 != -1)) { + if (!(in instanceof PushbackInputStream)) { + in = new PushbackInputStream(in); + } + ((PushbackInputStream)in).unread(c2); + } else { + done = true; + break; + } + /* fall through */ + default: + if (--room < 0) { + buf = new char[offset + 128]; + room = buf.length - offset - 1; + System.arraycopy(lineBuffer, 0, buf, 0, offset); + Arrays.fill(lineBuffer, ' '); + lineBuffer = buf; + } + buf[offset++] = (char) c; + break; + } + } + + if (offset == 0) { + return null; + } + + char[] ret = new char[offset]; + System.arraycopy(buf, 0, ret, 0, offset); + Arrays.fill(buf, ' '); + + return ret; + } finally { + if (consoleEntered != null) { + Arrays.fill(consoleEntered, ' '); + } + if (consoleBytes != null) { + Arrays.fill(consoleBytes, (byte)0); + } + } + } + + /** + * Change a password read from Console.readPassword() into + * its original bytes. + * + * @param pass a char[] + * @return its byte[] format, similar to new String(pass).getBytes() + */ + private static byte[] convertToBytes(char[] pass) { + if (enc == null) { + synchronized (Password.class) { + enc = sun.misc.SharedSecrets.getJavaIOAccess() + .charset() + .newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + } + } + byte[] ba = new byte[(int)(enc.maxBytesPerChar() * pass.length)]; + ByteBuffer bb = ByteBuffer.wrap(ba); + synchronized (enc) { + enc.reset().encode(CharBuffer.wrap(pass), bb, true); + } + if (bb.position() < ba.length) { + ba[bb.position()] = '\n'; + } + return ba; + } + private static volatile CharsetEncoder enc; +} diff --git a/src/sun/security/util/PendingException.java b/src/sun/security/util/PendingException.java new file mode 100644 index 00000000..3997f25d --- /dev/null +++ b/src/sun/security/util/PendingException.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + * An exception that denotes that an operation is pending. + * Currently used by LoginContext. + * + */ +public class PendingException extends RuntimeException { + + private static final long serialVersionUID = -5201837247928788640L; + + /** + * Constructs a PendingException with no detail message. A detail + * message is a String that describes this particular exception. + */ + public PendingException() { + super(); + } + + /** + * Constructs a PendingException with the specified detail message. + * A detail message is a String that describes this particular + * exception. + * + *

    + * + * @param msg the detail message. + */ + public PendingException(String msg) { + super(msg); + } +} diff --git a/src/sun/security/util/PermissionFactory.java b/src/sun/security/util/PermissionFactory.java new file mode 100644 index 00000000..c6457fff --- /dev/null +++ b/src/sun/security/util/PermissionFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.Permission; + +/** + * A factory object that creates Permission objects. + */ + +public interface PermissionFactory { + T newPermission(String name); +} diff --git a/src/sun/security/util/PolicyUtil.java b/src/sun/security/util/PolicyUtil.java new file mode 100644 index 00000000..128c6883 --- /dev/null +++ b/src/sun/security/util/PolicyUtil.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.util.Arrays; + +import sun.net.www.ParseUtil; + + +/** + * A utility class for getting a KeyStore instance from policy information. + * In addition, a supporting getInputStream method. + * + */ +public class PolicyUtil { + + // standard PKCS11 KeyStore type + private static final String P11KEYSTORE = "PKCS11"; + + // reserved word + private static final String NONE = "NONE"; + + /* + * Fast path reading from file urls in order to avoid calling + * FileURLConnection.connect() which can be quite slow the first time + * it is called. We really should clean up FileURLConnection so that + * this is not a problem but in the meantime this fix helps reduce + * start up time noticeably for the new launcher. -- DAC + */ + public static InputStream getInputStream(URL url) throws IOException { + if ("file".equals(url.getProtocol())) { + String path = url.getFile().replace('/', File.separatorChar); + path = ParseUtil.decode(path); + return new FileInputStream(path); + } else { + return url.openStream(); + } + } + + /** + * this is intended for use by policytool and the policy parser to + * instantiate a KeyStore from the information in the GUI/policy file + */ + public static KeyStore getKeyStore + (URL policyUrl, // URL of policy file + String keyStoreName, // input: keyStore URL + String keyStoreType, // input: keyStore type + String keyStoreProvider, // input: keyStore provider + String storePassURL, // input: keyStore password + Debug debug) + throws KeyStoreException, MalformedURLException, IOException, + NoSuchProviderException, NoSuchAlgorithmException, + java.security.cert.CertificateException { + + if (keyStoreName == null) { + throw new IllegalArgumentException("null KeyStore name"); + } + + char[] keyStorePassword = null; + try { + KeyStore ks; + if (keyStoreType == null) { + keyStoreType = KeyStore.getDefaultType(); + } + + if (P11KEYSTORE.equalsIgnoreCase(keyStoreType) && + !NONE.equals(keyStoreName)) { + throw new IllegalArgumentException + ("Invalid value (" + + keyStoreName + + ") for keystore URL. If the keystore type is \"" + + P11KEYSTORE + + "\", the keystore url must be \"" + + NONE + + "\""); + } + + if (keyStoreProvider != null) { + ks = KeyStore.getInstance(keyStoreType, keyStoreProvider); + } else { + ks = KeyStore.getInstance(keyStoreType); + } + + if (storePassURL != null) { + URL passURL; + try { + passURL = new URL(storePassURL); + // absolute URL + } catch (MalformedURLException e) { + // relative URL + if (policyUrl == null) { + throw e; + } + passURL = new URL(policyUrl, storePassURL); + } + + if (debug != null) { + debug.println("reading password"+passURL); + } + + InputStream in = null; + try { + in = passURL.openStream(); + keyStorePassword = Password.readPassword(in); + } finally { + if (in != null) { + in.close(); + } + } + } + + if (NONE.equals(keyStoreName)) { + ks.load(null, keyStorePassword); + return ks; + } else { + /* + * location of keystore is specified as absolute URL in policy + * file, or is relative to URL of policy file + */ + URL keyStoreUrl = null; + try { + keyStoreUrl = new URL(keyStoreName); + // absolute URL + } catch (MalformedURLException e) { + // relative URL + if (policyUrl == null) { + throw e; + } + keyStoreUrl = new URL(policyUrl, keyStoreName); + } + + if (debug != null) { + debug.println("reading keystore"+keyStoreUrl); + } + + InputStream inStream = null; + try { + inStream = + new BufferedInputStream(getInputStream(keyStoreUrl)); + ks.load(inStream, keyStorePassword); + } finally { + inStream.close(); + } + return ks; + } + } finally { + if (keyStorePassword != null) { + Arrays.fill(keyStorePassword, ' '); + } + } + } +} diff --git a/src/sun/security/util/PropertyExpander.java b/src/sun/security/util/PropertyExpander.java new file mode 100644 index 00000000..05bef522 --- /dev/null +++ b/src/sun/security/util/PropertyExpander.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.net.URI; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; + +/** + * A utility class to expand properties embedded in a string. + * Strings of the form ${some.property.name} are expanded to + * be the value of the property. Also, the special ${/} property + * is expanded to be the same as file.separator. If a property + * is not set, a GeneralSecurityException will be thrown. + * + * @author Roland Schemers + */ +public class PropertyExpander { + + + public static class ExpandException extends GeneralSecurityException { + + private static final long serialVersionUID = -7941948581406161702L; + + public ExpandException(String msg) { + super(msg); + } + } + + public static String expand(String value) + throws ExpandException + { + return expand(value, false); + } + + public static String expand(String value, boolean encodeURL) + throws ExpandException + { + if (value == null) + return null; + + int p = value.indexOf("${", 0); + + // no special characters + if (p == -1) return value; + + StringBuffer sb = new StringBuffer(value.length()); + int max = value.length(); + int i = 0; // index of last character we copied + + scanner: + while (p < max) { + if (p > i) { + // copy in anything before the special stuff + sb.append(value.substring(i, p)); + i = p; + } + int pe = p+2; + + // do not expand ${{ ... }} + if (pe < max && value.charAt(pe) == '{') { + pe = value.indexOf("}}", pe); + if (pe == -1 || pe+2 == max) { + // append remaining chars + sb.append(value.substring(p)); + break scanner; + } else { + // append as normal text + pe++; + sb.append(value.substring(p, pe+1)); + } + } else { + while ((pe < max) && (value.charAt(pe) != '}')) { + pe++; + } + if (pe == max) { + // no matching '}' found, just add in as normal text + sb.append(value.substring(p, pe)); + break scanner; + } + String prop = value.substring(p+2, pe); + if (prop.equals("/")) { + sb.append(java.io.File.separatorChar); + } else { + String val = System.getProperty(prop); + if (val != null) { + if (encodeURL) { + // encode 'val' unless it's an absolute URI + // at the beginning of the string buffer + try { + if (sb.length() > 0 || + !(new URI(val)).isAbsolute()) { + val = sun.net.www.ParseUtil.encodePath(val); + } + } catch (URISyntaxException use) { + val = sun.net.www.ParseUtil.encodePath(val); + } + } + sb.append(val); + } else { + throw new ExpandException( + "unable to expand property " + + prop); + } + } + } + i = pe+1; + p = value.indexOf("${", i); + if (p == -1) { + // no more to expand. copy in any extra + if (i < max) { + sb.append(value.substring(i, max)); + } + // break out of loop + break scanner; + } + } + return sb.toString(); + } +} diff --git a/src/sun/security/util/Resources.java b/src/sun/security/util/Resources.java new file mode 100644 index 00000000..286acc99 --- /dev/null +++ b/src/sun/security/util/Resources.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "invalid null input(s)"}, + {"actions.can.only.be.read.", "actions can only be 'read'"}, + {"permission.name.name.syntax.invalid.", + "permission name [{0}] syntax invalid: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Credential Class not followed by a Principal Class and Name"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Principal Class not followed by a Principal Name"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "Principal Name must be surrounded by quotes"}, + {"Principal.Name.missing.end.quote", + "Principal Name missing end quote"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "PrivateCredentialPermission Principal Class can not be a wildcard (*) value if Principal Name is not a wildcard (*) value"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tPrincipal Class = {0}\n\tPrincipal Name = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "provided null name"}, + {"provided.null.keyword.map", "provided null keyword map"}, + {"provided.null.OID.map", "provided null OID map"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "invalid null AccessControlContext provided"}, + {"invalid.null.action.provided", "invalid null action provided"}, + {"invalid.null.Class.provided", "invalid null Class provided"}, + {"Subject.", "Subject:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\tPublic Credential: "}, + {".Private.Credentials.inaccessible.", + "\tPrivate Credentials inaccessible\n"}, + {".Private.Credential.", "\tPrivate Credential: "}, + {".Private.Credential.inaccessible.", + "\tPrivate Credential inaccessible\n"}, + {"Subject.is.read.only", "Subject is read-only"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "attempting to add an object which is not an instance of java.security.Principal to a Subject's Principal Set"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "attempting to add an object which is not an instance of {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Invalid null input: name"}, + {"No.LoginModules.configured.for.name", + "No LoginModules configured for {0}"}, + {"invalid.null.Subject.provided", "invalid null Subject provided"}, + {"invalid.null.CallbackHandler.provided", + "invalid null CallbackHandler provided"}, + {"null.subject.logout.called.before.login", + "null subject - logout called before login"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "unable to instantiate LoginModule, {0}, because it does not provide a no-argument constructor"}, + {"unable.to.instantiate.LoginModule", + "unable to instantiate LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "unable to instantiate LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "unable to find LoginModule class: "}, + {"unable.to.access.LoginModule.", + "unable to access LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "Login Failure: all modules ignored"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: error parsing {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: error adding Permission, {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: error adding Entry:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "alias name not provided ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "unable to perform substitution on alias, {0}"}, + {"substitution.value.prefix.unsupported", + "substitution value, {0}, unsupported"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","type can't be null"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "keystorePasswordURL can not be specified without also specifying keystore"}, + {"expected.keystore.type", "expected keystore type"}, + {"expected.keystore.provider", "expected keystore provider"}, + {"multiple.Codebase.expressions", + "multiple Codebase expressions"}, + {"multiple.SignedBy.expressions","multiple SignedBy expressions"}, + {"duplicate.keystore.domain.name","duplicate keystore domain name: {0}"}, + {"duplicate.keystore.name","duplicate keystore name: {0}"}, + {"SignedBy.has.empty.alias","SignedBy has empty alias"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "can not specify Principal with a wildcard class without a wildcard name"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "expected codeBase or SignedBy or Principal"}, + {"expected.permission.entry", "expected permission entry"}, + {"number.", "number "}, + {"expected.expect.read.end.of.file.", + "expected [{0}], read [end of file]"}, + {"expected.read.end.of.file.", + "expected [;], read [end of file]"}, + {"line.number.msg", "line {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "line {0}: expected [{1}], found [{2}]"}, + {"null.principalClass.or.principalName", + "null principalClass or principalName"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11 Token [{0}] Password: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "unable to instantiate Subject-based policy"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/ResourcesMgr.java b/src/sun/security/util/ResourcesMgr.java new file mode 100644 index 00000000..091f2ea8 --- /dev/null +++ b/src/sun/security/util/ResourcesMgr.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + */ +public class ResourcesMgr { + + // intended for java.security, javax.security and sun.security resources + private static java.util.ResourceBundle bundle; + + // intended for com.sun.security resources + private static java.util.ResourceBundle altBundle; + + public static String getString(String s) { + + if (bundle == null) { + + // only load if/when needed + bundle = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public java.util.ResourceBundle run() { + return (java.util.ResourceBundle.getBundle + ("sun.security.util.Resources")); + } + }); + } + + return bundle.getString(s); + } + + public static String getString(String s, final String altBundleName) { + + if (altBundle == null) { + + // only load if/when needed + altBundle = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public java.util.ResourceBundle run() { + return (java.util.ResourceBundle.getBundle(altBundleName)); + } + }); + } + + return altBundle.getString(s); + } +} diff --git a/src/sun/security/util/Resources_de.java b/src/sun/security/util/Resources_de.java new file mode 100644 index 00000000..5f988201 --- /dev/null +++ b/src/sun/security/util/Resources_de.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_de extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "Ung\u00FCltige Nulleingabe(n)"}, + {"actions.can.only.be.read.", "Aktionen k\u00F6nnen nur \"lesen\" sein"}, + {"permission.name.name.syntax.invalid.", + "Syntax f\u00FCr Berechtigungsnamen [{0}] ung\u00FCltig: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Nach Zugangsdatenklasse folgt keine Principal-Klasse und kein Name"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Nach Principal-Klasse folgt kein Principal-Name"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "Principal-Name muss in Anf\u00FChrungszeichen stehen"}, + {"Principal.Name.missing.end.quote", + "Abschlie\u00DFendes Anf\u00FChrungszeichen f\u00FCr Principal-Name fehlt"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "Principal-Klasse PrivateCredentialPermission kann kein Platzhalterwert (*) sein, wenn der Principal-Name kein Platzhalterwert (*) ist"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tPrincipal-Klasse = {0}\n\tPrincipal-Name = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "Nullname angegeben"}, + {"provided.null.keyword.map", "Null-Schl\u00FCsselwortzuordnung angegeben"}, + {"provided.null.OID.map", "Null-OID-Zuordnung angegeben"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "Ung\u00FCltiger Nullwert f\u00FCr AccessControlContext angegeben"}, + {"invalid.null.action.provided", "Ung\u00FCltige Nullaktion angegeben"}, + {"invalid.null.Class.provided", "Ung\u00FCltige Nullklasse angegeben"}, + {"Subject.", "Subjekt:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\t\u00D6ffentliche Zugangsdaten: "}, + {".Private.Credentials.inaccessible.", + "\tKein Zugriff auf private Zugangsdaten\n"}, + {".Private.Credential.", "\tPrivate Zugangsdaten: "}, + {".Private.Credential.inaccessible.", + "\tKein Zugriff auf private Zugangsdaten\n"}, + {"Subject.is.read.only", "Subjekt ist schreibgesch\u00FCtzt"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "Es wird versucht, ein Objekt hinzuzuf\u00FCgen, das keine Instanz von java.security.Principal f\u00FCr eine Principal-Gruppe eines Subjekts ist"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "Es wird versucht, ein Objekt hinzuzuf\u00FCgen, das keine Instanz von {0} ist"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Ung\u00FCltige Nulleingabe: Name"}, + {"No.LoginModules.configured.for.name", + "F\u00FCr {0} sind keine LoginModules konfiguriert"}, + {"invalid.null.Subject.provided", "Ung\u00FCltiges Nullsubjekt angegeben"}, + {"invalid.null.CallbackHandler.provided", + "Ung\u00FCltiger Nullwert f\u00FCr CallbackHandler angegeben"}, + {"null.subject.logout.called.before.login", + "Nullsubjekt - Abmeldung vor Anmeldung aufgerufen"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "LoginModule {0} kann nicht instanziiert werden, da es keinen argumentlosen Constructor angibt"}, + {"unable.to.instantiate.LoginModule", + "LoginModule kann nicht instanziiert werden"}, + {"unable.to.instantiate.LoginModule.", + "LoginModule kann nicht instanziiert werden: "}, + {"unable.to.find.LoginModule.class.", + "LoginModule-Klasse kann nicht gefunden werden: "}, + {"unable.to.access.LoginModule.", + "Kein Zugriff auf LoginModule m\u00F6glich: "}, + {"Login.Failure.all.modules.ignored", + "Anmeldefehler: Alle Module werden ignoriert"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: Fehler beim Parsen von {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: Fehler beim Hinzuf\u00FCgen von Berechtigung, {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: Fehler beim Hinzuf\u00FCgen von Eintrag:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "Aliasname nicht angegeben ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "Substitution f\u00FCr Alias {0} kann nicht ausgef\u00FChrt werden"}, + {"substitution.value.prefix.unsupported", + "Substitutionswert {0} nicht unterst\u00FCtzt"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","Typ kann nicht null sein"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "keystorePasswordURL kann nicht ohne Keystore angegeben werden"}, + {"expected.keystore.type", "Keystore-Typ erwartet"}, + {"expected.keystore.provider", "Keystore-Provider erwartet"}, + {"multiple.Codebase.expressions", + "mehrere Codebase-Ausdr\u00FCcke"}, + {"multiple.SignedBy.expressions","mehrere SignedBy-Ausdr\u00FCcke"}, + {"duplicate.keystore.domain.name","Keystore-Domainname doppelt vorhanden: {0}"}, + {"duplicate.keystore.name","Keystore-Name doppelt vorhanden: {0}"}, + {"SignedBy.has.empty.alias","Leerer Alias in SignedBy"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "Principal kann nicht mit einer Platzhalterklasse ohne Platzhalternamen angegeben werden"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "codeBase oder SignedBy oder Principal erwartet"}, + {"expected.permission.entry", "Berechtigungseintrag erwartet"}, + {"number.", "Nummer "}, + {"expected.expect.read.end.of.file.", + "[{0}] erwartet, [Dateiende] gelesen"}, + {"expected.read.end.of.file.", + "[;] erwartet, [Dateiende] gelesen"}, + {"line.number.msg", "Zeile {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "Zeile {0}: [{1}] erwartet, [{2}] gefunden"}, + {"null.principalClass.or.principalName", + "principalClass oder principalName null"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "Kennwort f\u00FCr PKCS11-Token [{0}]: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "Subjektbasierte Policy kann nicht instanziiert werden"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_es.java b/src/sun/security/util/Resources_es.java new file mode 100644 index 00000000..783f072f --- /dev/null +++ b/src/sun/security/util/Resources_es.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_es extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "entradas nulas no v\u00E1lidas"}, + {"actions.can.only.be.read.", "las acciones s\u00F3lo pueden 'leerse'"}, + {"permission.name.name.syntax.invalid.", + "sintaxis de nombre de permiso [{0}] no v\u00E1lida: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "La clase de credencial no va seguida de una clase y nombre de principal"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "La clase de principal no va seguida de un nombre de principal"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "El nombre de principal debe ir entre comillas"}, + {"Principal.Name.missing.end.quote", + "Faltan las comillas finales en el nombre de principal"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "La clase de principal PrivateCredentialPermission no puede ser un valor comod\u00EDn (*) si el nombre de principal no lo es tambi\u00E9n"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tClase de Principal = {0}\n\tNombre de Principal = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "se ha proporcionado un nombre nulo"}, + {"provided.null.keyword.map", "mapa de palabras clave proporcionado nulo"}, + {"provided.null.OID.map", "mapa de OID proporcionado nulo"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "se ha proporcionado un AccessControlContext nulo no v\u00E1lido"}, + {"invalid.null.action.provided", "se ha proporcionado una acci\u00F3n nula no v\u00E1lida"}, + {"invalid.null.Class.provided", "se ha proporcionado una clase nula no v\u00E1lida"}, + {"Subject.", "Asunto:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\tCredencial P\u00FAblica: "}, + {".Private.Credentials.inaccessible.", + "\tCredenciales Privadas Inaccesibles\n"}, + {".Private.Credential.", "\tCredencial Privada: "}, + {".Private.Credential.inaccessible.", + "\tCredencial Privada Inaccesible\n"}, + {"Subject.is.read.only", "El asunto es de s\u00F3lo lectura"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "intentando agregar un objeto que no es una instancia de java.security.Principal al juego principal de un asunto"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "intentando agregar un objeto que no es una instancia de {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Entrada nula no v\u00E1lida: nombre"}, + {"No.LoginModules.configured.for.name", + "No se han configurado LoginModules para {0}"}, + {"invalid.null.Subject.provided", "se ha proporcionado un asunto nulo no v\u00E1lido"}, + {"invalid.null.CallbackHandler.provided", + "se ha proporcionado CallbackHandler nulo no v\u00E1lido"}, + {"null.subject.logout.called.before.login", + "asunto nulo - se ha llamado al cierre de sesi\u00F3n antes del inicio de sesi\u00F3n"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "no se ha podido instanciar LoginModule, {0}, porque no incluye un constructor sin argumentos"}, + {"unable.to.instantiate.LoginModule", + "no se ha podido instanciar LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "no se ha podido instanciar LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "no se ha encontrado la clase LoginModule: "}, + {"unable.to.access.LoginModule.", + "no se ha podido acceder a LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "Fallo en inicio de sesi\u00F3n: se han ignorado todos los m\u00F3dulos"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: error de an\u00E1lisis de {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: error al agregar un permiso, {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: error al agregar una entrada:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "no se ha proporcionado el nombre de alias ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "no se puede realizar la sustituci\u00F3n en el alias, {0}"}, + {"substitution.value.prefix.unsupported", + "valor de sustituci\u00F3n, {0}, no soportado"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","el tipo no puede ser nulo"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "keystorePasswordURL no puede especificarse sin especificar tambi\u00E9n el almac\u00E9n de claves"}, + {"expected.keystore.type", "se esperaba un tipo de almac\u00E9n de claves"}, + {"expected.keystore.provider", "se esperaba un proveedor de almac\u00E9n de claves"}, + {"multiple.Codebase.expressions", + "expresiones m\u00FAltiples de CodeBase"}, + {"multiple.SignedBy.expressions","expresiones m\u00FAltiples de SignedBy"}, + {"duplicate.keystore.domain.name","nombre de dominio de almac\u00E9n de claves duplicado: {0}"}, + {"duplicate.keystore.name","nombre de almac\u00E9n de claves duplicado: {0}"}, + {"SignedBy.has.empty.alias","SignedBy tiene un alias vac\u00EDo"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "no se puede especificar Principal con una clase de comod\u00EDn sin un nombre de comod\u00EDn"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "se esperaba codeBase o SignedBy o Principal"}, + {"expected.permission.entry", "se esperaba una entrada de permiso"}, + {"number.", "n\u00FAmero "}, + {"expected.expect.read.end.of.file.", + "se esperaba [{0}], se ha le\u00EDdo [final de archivo]"}, + {"expected.read.end.of.file.", + "se esperaba [;], se ha le\u00EDdo [final de archivo]"}, + {"line.number.msg", "l\u00EDnea {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "l\u00EDnea {0}: se esperaba [{1}], se ha encontrado [{2}]"}, + {"null.principalClass.or.principalName", + "principalClass o principalName nulos"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "Contrase\u00F1a del Token PKCS11 [{0}]: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "no se ha podido instanciar una pol\u00EDtica basada en asunto"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_fr.java b/src/sun/security/util/Resources_fr.java new file mode 100644 index 00000000..a2053cf6 --- /dev/null +++ b/src/sun/security/util/Resources_fr.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_fr extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "entr\u00E9es NULL non valides"}, + {"actions.can.only.be.read.", "les actions sont accessibles en lecture uniquement"}, + {"permission.name.name.syntax.invalid.", + "syntaxe de nom de droit [{0}] non valide : "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Classe Credential non suivie d'une classe et d'un nom de principal"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Classe de principal non suivie d'un nom de principal"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "Le nom de principal doit \u00EAtre indiqu\u00E9 entre guillemets"}, + {"Principal.Name.missing.end.quote", + "Guillemet fermant manquant pour le nom de principal"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "La classe de principal PrivateCredentialPermission ne peut pas \u00EAtre une valeur g\u00E9n\u00E9rique (*) si le nom de principal n'est pas une valeur g\u00E9n\u00E9rique (*)"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner :\n\tClasse de principal = {0}\n\tNom de principal = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "nom NULL fourni"}, + {"provided.null.keyword.map", "mappage de mots-cl\u00E9s NULL fourni"}, + {"provided.null.OID.map", "mappage OID NULL fourni"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "AccessControlContext NULL fourni non valide"}, + {"invalid.null.action.provided", "action NULL fournie non valide"}, + {"invalid.null.Class.provided", "classe NULL fournie non valide"}, + {"Subject.", "Objet :\n"}, + {".Principal.", "\tPrincipal : "}, + {".Public.Credential.", "\tInformations d'identification publiques : "}, + {".Private.Credentials.inaccessible.", + "\tInformations d'identification priv\u00E9es inaccessibles\n"}, + {".Private.Credential.", "\tInformations d'identification priv\u00E9es : "}, + {".Private.Credential.inaccessible.", + "\tInformations d'identification priv\u00E9es inaccessibles\n"}, + {"Subject.is.read.only", "Sujet en lecture seule"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "tentative d'ajout d'un objet qui n'est pas une instance de java.security.Principal dans un ensemble de principaux du sujet"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "tentative d''ajout d''un objet qui n''est pas une instance de {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag : "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Entr\u00E9e NULL non valide : nom"}, + {"No.LoginModules.configured.for.name", + "Aucun LoginModule configur\u00E9 pour {0}"}, + {"invalid.null.Subject.provided", "sujet NULL fourni non valide"}, + {"invalid.null.CallbackHandler.provided", + "CallbackHandler NULL fourni non valide"}, + {"null.subject.logout.called.before.login", + "sujet NULL - Tentative de d\u00E9connexion avant la connexion"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "impossible d''instancier LoginModule {0} car il ne fournit pas de constructeur sans argument"}, + {"unable.to.instantiate.LoginModule", + "impossible d'instancier LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "impossible d'instancier LoginModule\u00A0: "}, + {"unable.to.find.LoginModule.class.", + "classe LoginModule introuvable : "}, + {"unable.to.access.LoginModule.", + "impossible d'acc\u00E9der \u00E0 LoginModule : "}, + {"Login.Failure.all.modules.ignored", + "Echec de connexion : tous les modules ont \u00E9t\u00E9 ignor\u00E9s"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy : erreur d''analyse de {0} :\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy : erreur d''ajout de droit, {0} :\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy : erreur d''ajout d''entr\u00E9e :\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "nom d''alias non fourni ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "impossible d''effectuer une substitution pour l''alias, {0}"}, + {"substitution.value.prefix.unsupported", + "valeur de substitution, {0}, non prise en charge"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","le type ne peut \u00EAtre NULL"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "Impossible de sp\u00E9cifier keystorePasswordURL sans indiquer aussi le fichier de cl\u00E9s"}, + {"expected.keystore.type", "type de fichier de cl\u00E9s attendu"}, + {"expected.keystore.provider", "fournisseur de fichier de cl\u00E9s attendu"}, + {"multiple.Codebase.expressions", + "expressions Codebase multiples"}, + {"multiple.SignedBy.expressions","expressions SignedBy multiples"}, + {"duplicate.keystore.domain.name","nom de domaine de fichier de cl\u00E9s en double : {0}"}, + {"duplicate.keystore.name","nom de fichier de cl\u00E9s en double : {0}"}, + {"SignedBy.has.empty.alias","SignedBy poss\u00E8de un alias vide"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "impossible de sp\u00E9cifier le principal avec une classe g\u00E9n\u00E9rique sans nom g\u00E9n\u00E9rique"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "codeBase, SignedBy ou Principal attendu"}, + {"expected.permission.entry", "entr\u00E9e de droit attendue"}, + {"number.", "nombre "}, + {"expected.expect.read.end.of.file.", + "attendu [{0}], lu [fin de fichier]"}, + {"expected.read.end.of.file.", + "attendu [;], lu [fin de fichier]"}, + {"line.number.msg", "ligne {0} : {1}"}, + {"line.number.expected.expect.found.actual.", + "ligne {0} : attendu [{1}], trouv\u00E9 [{2}]"}, + {"null.principalClass.or.principalName", + "principalClass ou principalName NULL"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "Mot de passe PKCS11 Token [{0}] : "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "impossible d'instancier les r\u00E8gles bas\u00E9es sur le sujet"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_it.java b/src/sun/security/util/Resources_it.java new file mode 100644 index 00000000..28ceead5 --- /dev/null +++ b/src/sun/security/util/Resources_it.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_it extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "input nullo/i non valido/i"}, + {"actions.can.only.be.read.", "le azioni possono essere solamente 'lette'"}, + {"permission.name.name.syntax.invalid.", + "sintassi [{0}] non valida per il nome autorizzazione: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "la classe di credenziali non \u00E8 seguita da un nome e una classe di principal"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "la classe di principal non \u00E8 seguita da un nome principal"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "il nome principal deve essere compreso tra apici"}, + {"Principal.Name.missing.end.quote", + "apice di chiusura del nome principal mancante"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "la classe principal PrivateCredentialPermission non pu\u00F2 essere un valore carattere jolly (*) se il nome principal a sua volta non \u00E8 un valore carattere jolly (*)"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tclasse Principal = {0}\n\tNome Principal = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "il nome fornito \u00E8 nullo"}, + {"provided.null.keyword.map", "specificata mappa parole chiave null"}, + {"provided.null.OID.map", "specificata mappa OID null"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "fornito un valore nullo non valido per AccessControlContext"}, + {"invalid.null.action.provided", "fornita un'azione nulla non valida"}, + {"invalid.null.Class.provided", "fornita una classe nulla non valida"}, + {"Subject.", "Oggetto:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\tCredenziale pubblica: "}, + {".Private.Credentials.inaccessible.", + "\tImpossibile accedere alle credenziali private\n"}, + {".Private.Credential.", "\tCredenziale privata: "}, + {".Private.Credential.inaccessible.", + "\tImpossibile accedere alla credenziale privata\n"}, + {"Subject.is.read.only", "L'oggetto \u00E8 di sola lettura"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "si \u00E8 tentato di aggiungere un oggetto che non \u00E8 un'istanza di java.security.Principal a un set principal dell'oggetto"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "si \u00E8 tentato di aggiungere un oggetto che non \u00E8 un''istanza di {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Input nullo non valido: nome"}, + {"No.LoginModules.configured.for.name", + "Nessun LoginModules configurato per {0}"}, + {"invalid.null.Subject.provided", "fornito un valore nullo non valido per l'oggetto"}, + {"invalid.null.CallbackHandler.provided", + "fornito un valore nullo non valido per CallbackHandler"}, + {"null.subject.logout.called.before.login", + "oggetto nullo - il logout \u00E8 stato richiamato prima del login"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "impossibile creare un''istanza di LoginModule {0} in quanto non restituisce un argomento vuoto per il costruttore"}, + {"unable.to.instantiate.LoginModule", + "impossibile creare un'istanza di LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "impossibile creare un'istanza di LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "impossibile trovare la classe LoginModule: "}, + {"unable.to.access.LoginModule.", + "impossibile accedere a LoginModule "}, + {"Login.Failure.all.modules.ignored", + "Errore di login: tutti i moduli sono stati ignorati"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: errore durante l''analisi di {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: errore durante l''aggiunta dell''autorizzazione {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: errore durante l''aggiunta della voce:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "impossibile fornire nome alias ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "impossibile eseguire una sostituzione sull''alias, {0}"}, + {"substitution.value.prefix.unsupported", + "valore sostituzione, {0}, non supportato"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","il tipo non pu\u00F2 essere nullo"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "Impossibile specificare keystorePasswordURL senza specificare anche il keystore"}, + {"expected.keystore.type", "tipo keystore previsto"}, + {"expected.keystore.provider", "provider di keystore previsto"}, + {"multiple.Codebase.expressions", + "espressioni Codebase multiple"}, + {"multiple.SignedBy.expressions","espressioni SignedBy multiple"}, + {"duplicate.keystore.domain.name","nome dominio keystore duplicato: {0}"}, + {"duplicate.keystore.name","nome keystore duplicato: {0}"}, + {"SignedBy.has.empty.alias","SignedBy presenta un alias vuoto"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "impossibile specificare un principal con una classe carattere jolly senza un nome carattere jolly"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "previsto codeBase o SignedBy o principal"}, + {"expected.permission.entry", "prevista voce di autorizzazione"}, + {"number.", "numero "}, + {"expected.expect.read.end.of.file.", + "previsto [{0}], letto [end of file]"}, + {"expected.read.end.of.file.", + "previsto [;], letto [end of file]"}, + {"line.number.msg", "riga {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "riga {0}: previsto [{1}], trovato [{2}]"}, + {"null.principalClass.or.principalName", + "principalClass o principalName nullo"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "Password per token PKCS11 [{0}]: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "impossibile creare un'istanza dei criteri basati sull'oggetto"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_ja.java b/src/sun/security/util/Resources_ja.java new file mode 100644 index 00000000..474d2647 --- /dev/null +++ b/src/sun/security/util/Resources_ja.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_ja extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "null\u306E\u5165\u529B\u306F\u7121\u52B9\u3067\u3059"}, + {"actions.can.only.be.read.", "\u30A2\u30AF\u30B7\u30E7\u30F3\u306F'\u8AAD\u8FBC\u307F'\u306E\u307F\u53EF\u80FD\u3067\u3059"}, + {"permission.name.name.syntax.invalid.", + "\u30A2\u30AF\u30BB\u30B9\u6A29\u540D[{0}]\u306E\u69CB\u6587\u304C\u7121\u52B9\u3067\u3059: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Credential\u30AF\u30E9\u30B9\u306E\u6B21\u306BPrincipal\u30AF\u30E9\u30B9\u304A\u3088\u3073\u540D\u524D\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Principal\u30AF\u30E9\u30B9\u306E\u6B21\u306B\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u540D\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u540D\u306F\u5F15\u7528\u7B26\u3067\u56F2\u3080\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059"}, + {"Principal.Name.missing.end.quote", + "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u540D\u306E\u6700\u5F8C\u306B\u5F15\u7528\u7B26\u304C\u3042\u308A\u307E\u305B\u3093"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u540D\u304C\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9(*)\u5024\u3067\u306A\u3044\u5834\u5408\u3001PrivateCredentialPermission\u306EPrincipal\u30AF\u30E9\u30B9\u3092\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9(*)\u5024\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tPrincipal\u30AF\u30E9\u30B9={0}\n\t\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u540D={1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "null\u306E\u540D\u524D\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"provided.null.keyword.map", "null\u306E\u30AD\u30FC\u30EF\u30FC\u30C9\u30FB\u30DE\u30C3\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"provided.null.OID.map", "null\u306EOID\u30DE\u30C3\u30D7\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "\u7121\u52B9\u306Anull AccessControlContext\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"invalid.null.action.provided", "\u7121\u52B9\u306Anull\u30A2\u30AF\u30B7\u30E7\u30F3\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"invalid.null.Class.provided", "\u7121\u52B9\u306Anull\u30AF\u30E9\u30B9\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"Subject.", "\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8:\n"}, + {".Principal.", "\t\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB: "}, + {".Public.Credential.", "\t\u516C\u958B\u8CC7\u683C: "}, + {".Private.Credentials.inaccessible.", + "\t\u975E\u516C\u958B\u8CC7\u683C\u306B\u306F\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093\n"}, + {".Private.Credential.", "\t\u975E\u516C\u958B\u8CC7\u683C: "}, + {".Private.Credential.inaccessible.", + "\t\u975E\u516C\u958B\u8CC7\u683C\u306B\u306F\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093\n"}, + {"Subject.is.read.only", "\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8\u306F\u8AAD\u53D6\u308A\u5C02\u7528\u3067\u3059"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "java.security.Principal\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3067\u306F\u306A\u3044\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u3001\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u30FB\u30BB\u30C3\u30C8\u306B\u8FFD\u52A0\u3057\u3088\u3046\u3068\u3057\u307E\u3057\u305F"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "{0}\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3067\u306F\u306A\u3044\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3092\u8FFD\u52A0\u3057\u3088\u3046\u3068\u3057\u307E\u3057\u305F"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "\u7121\u52B9\u306Anull\u5165\u529B: \u540D\u524D"}, + {"No.LoginModules.configured.for.name", + "{0}\u7528\u306B\u69CB\u6210\u3055\u308C\u305FLoginModules\u306F\u3042\u308A\u307E\u305B\u3093"}, + {"invalid.null.Subject.provided", "\u7121\u52B9\u306Anull\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"invalid.null.CallbackHandler.provided", + "\u7121\u52B9\u306Anull CallbackHandler\u304C\u6307\u5B9A\u3055\u308C\u307E\u3057\u305F"}, + {"null.subject.logout.called.before.login", + "null\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8 - \u30ED\u30B0\u30A4\u30F3\u3059\u308B\u524D\u306B\u30ED\u30B0\u30A2\u30A6\u30C8\u304C\u547C\u3073\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "LoginModule {0}\u306F\u5F15\u6570\u3092\u53D6\u3089\u306A\u3044\u30B3\u30F3\u30B9\u30C8\u30E9\u30AF\u30BF\u3092\u6307\u5B9A\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093"}, + {"unable.to.instantiate.LoginModule", + "LoginModule\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093"}, + {"unable.to.instantiate.LoginModule.", + "LoginModule\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093: "}, + {"unable.to.find.LoginModule.class.", + "LoginModule\u30AF\u30E9\u30B9\u3092\u691C\u51FA\u3067\u304D\u307E\u305B\u3093: "}, + {"unable.to.access.LoginModule.", + "LoginModule\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u307E\u305B\u3093: "}, + {"Login.Failure.all.modules.ignored", + "\u30ED\u30B0\u30A4\u30F3\u5931\u6557: \u3059\u3079\u3066\u306E\u30E2\u30B8\u30E5\u30FC\u30EB\u306F\u7121\u8996\u3055\u308C\u307E\u3059"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: {0}\u306E\u69CB\u6587\u89E3\u6790\u30A8\u30E9\u30FC:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: \u30A2\u30AF\u30BB\u30B9\u6A29{0}\u306E\u8FFD\u52A0\u30A8\u30E9\u30FC:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: \u30A8\u30F3\u30C8\u30EA\u306E\u8FFD\u52A0\u30A8\u30E9\u30FC:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "\u5225\u540D\u306E\u6307\u5B9A\u304C\u3042\u308A\u307E\u305B\u3093({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "\u5225\u540D{0}\u306B\u5BFE\u3057\u3066\u7F6E\u63DB\u64CD\u4F5C\u304C\u3067\u304D\u307E\u305B\u3093"}, + {"substitution.value.prefix.unsupported", + "\u7F6E\u63DB\u5024{0}\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","\u5165\u529B\u3092null\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "\u30AD\u30FC\u30B9\u30C8\u30A2\u3092\u6307\u5B9A\u3057\u306A\u3044\u5834\u5408\u3001keystorePasswordURL\u306F\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093"}, + {"expected.keystore.type", "\u4E88\u60F3\u3055\u308C\u305F\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30BF\u30A4\u30D7"}, + {"expected.keystore.provider", "\u4E88\u60F3\u3055\u308C\u305F\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30D7\u30ED\u30D0\u30A4\u30C0"}, + {"multiple.Codebase.expressions", + "\u8907\u6570\u306ECodebase\u5F0F"}, + {"multiple.SignedBy.expressions","\u8907\u6570\u306ESignedBy\u5F0F"}, + {"duplicate.keystore.domain.name","\u91CD\u8907\u3059\u308B\u30AD\u30FC\u30B9\u30C8\u30A2\u30FB\u30C9\u30E1\u30A4\u30F3\u540D: {0}"}, + {"duplicate.keystore.name","\u91CD\u8907\u3059\u308B\u30AD\u30FC\u30B9\u30C8\u30A2\u540D: {0}"}, + {"SignedBy.has.empty.alias","SignedBy\u306F\u7A7A\u306E\u5225\u540D\u3092\u4FDD\u6301\u3057\u307E\u3059"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u540D\u306E\u306A\u3044\u30EF\u30A4\u30EB\u30C9\u30AB\u30FC\u30C9\u30FB\u30AF\u30E9\u30B9\u3092\u4F7F\u7528\u3057\u3066\u3001\u30D7\u30EA\u30F3\u30B7\u30D1\u30EB\u3092\u6307\u5B9A\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "\u4E88\u60F3\u3055\u308C\u305FcodeBase\u3001SignedBy\u307E\u305F\u306FPrincipal"}, + {"expected.permission.entry", "\u4E88\u60F3\u3055\u308C\u305F\u30A2\u30AF\u30BB\u30B9\u6A29\u30A8\u30F3\u30C8\u30EA"}, + {"number.", "\u6570 "}, + {"expected.expect.read.end.of.file.", + "[{0}]\u3067\u306F\u306A\u304F[\u30D5\u30A1\u30A4\u30EB\u306E\u7D42\u308F\u308A]\u304C\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3057\u305F"}, + {"expected.read.end.of.file.", + "[;]\u3067\u306F\u306A\u304F[\u30D5\u30A1\u30A4\u30EB\u306E\u7D42\u308F\u308A]\u304C\u8AAD\u307F\u8FBC\u307E\u308C\u307E\u3057\u305F"}, + {"line.number.msg", "\u884C{0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "\u884C{0}: [{1}]\u3067\u306F\u306A\u304F[{2}]\u304C\u691C\u51FA\u3055\u308C\u307E\u3057\u305F"}, + {"null.principalClass.or.principalName", + "null\u306EprincipalClass\u307E\u305F\u306FprincipalName"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11\u30C8\u30FC\u30AF\u30F3[{0}]\u30D1\u30B9\u30EF\u30FC\u30C9: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "\u30B5\u30D6\u30B8\u30A7\u30AF\u30C8\u30FB\u30D9\u30FC\u30B9\u306E\u30DD\u30EA\u30B7\u30FC\u306E\u30A4\u30F3\u30B9\u30BF\u30F3\u30B9\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_ko.java b/src/sun/security/util/Resources_ko.java new file mode 100644 index 00000000..8dfca86c --- /dev/null +++ b/src/sun/security/util/Resources_ko.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_ko extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "\uB110 \uC785\uB825\uAC12\uC774 \uBD80\uC801\uD569\uD569\uB2C8\uB2E4."}, + {"actions.can.only.be.read.", "\uC791\uC5C5\uC740 '\uC77D\uAE30' \uC804\uC6A9\uC785\uB2C8\uB2E4."}, + {"permission.name.name.syntax.invalid.", + "\uAD8C\uD55C \uC774\uB984 [{0}] \uAD6C\uBB38\uC774 \uBD80\uC801\uD569\uD568: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "\uC778\uC99D\uC11C \uD074\uB798\uC2A4 \uB2E4\uC74C\uC5D0 \uC8FC\uCCB4 \uD074\uB798\uC2A4\uC640 \uC774\uB984\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "\uC8FC\uCCB4 \uD074\uB798\uC2A4 \uB2E4\uC74C\uC5D0 \uC8FC\uCCB4 \uC774\uB984\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"Principal.Name.must.be.surrounded.by.quotes", + "\uC8FC\uCCB4 \uC774\uB984\uC740 \uB530\uC634\uD45C\uB85C \uBB36\uC5B4\uC57C \uD569\uB2C8\uB2E4."}, + {"Principal.Name.missing.end.quote", + "\uC8FC\uCCB4 \uC774\uB984\uC5D0 \uB2EB\uB294 \uB530\uC634\uD45C\uAC00 \uB204\uB77D\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "\uC8FC\uCCB4 \uC774\uB984\uC774 \uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790(*) \uAC12\uC774 \uC544\uB2CC \uACBD\uC6B0 PrivateCredentialPermission \uC8FC\uCCB4 \uD074\uB798\uC2A4\uB294 \uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790(*) \uAC12\uC77C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\t\uC8FC\uCCB4 \uD074\uB798\uC2A4 = {0}\n\t\uC8FC\uCCB4 \uC774\uB984 = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "\uB110 \uC774\uB984\uC744 \uC81C\uACF5\uD588\uC2B5\uB2C8\uB2E4."}, + {"provided.null.keyword.map", "\uB110 \uD0A4\uC6CC\uB4DC \uB9F5\uC744 \uC81C\uACF5\uD588\uC2B5\uB2C8\uB2E4."}, + {"provided.null.OID.map", "\uB110 OID \uB9F5\uC744 \uC81C\uACF5\uD588\uC2B5\uB2C8\uB2E4."}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "\uBD80\uC801\uD569\uD55C \uB110 AccessControlContext\uAC00 \uC81C\uACF5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"invalid.null.action.provided", "\uBD80\uC801\uD569\uD55C \uB110 \uC791\uC5C5\uC774 \uC81C\uACF5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"invalid.null.Class.provided", "\uBD80\uC801\uD569\uD55C \uB110 \uD074\uB798\uC2A4\uAC00 \uC81C\uACF5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"Subject.", "\uC81C\uBAA9:\n"}, + {".Principal.", "\\\uC8FC\uCCB4: "}, + {".Public.Credential.", "\t\uACF5\uC6A9 \uC778\uC99D\uC11C: "}, + {".Private.Credentials.inaccessible.", + "\t\uC804\uC6A9 \uC778\uC99D\uC11C\uC5D0 \uC561\uC138\uC2A4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n"}, + {".Private.Credential.", "\t\uC804\uC6A9 \uC778\uC99D\uC11C: "}, + {".Private.Credential.inaccessible.", + "\t\uC804\uC6A9 \uC778\uC99D\uC11C\uC5D0 \uC561\uC138\uC2A4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n"}, + {"Subject.is.read.only", "\uC81C\uBAA9\uC774 \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4."}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "java.security.Principal\uC758 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC544\uB2CC \uAC1D\uCCB4\uB97C \uC81C\uBAA9\uC758 \uC8FC\uCCB4 \uC9D1\uD569\uC5D0 \uCD94\uAC00\uD558\uB824\uACE0 \uC2DC\uB3C4\uD558\uB294 \uC911"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "{0}\uC758 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC544\uB2CC \uAC1D\uCCB4\uB97C \uCD94\uAC00\uD558\uB824\uACE0 \uC2DC\uB3C4\uD558\uB294 \uC911"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "\uBD80\uC801\uD569\uD55C \uB110 \uC785\uB825\uAC12: \uC774\uB984"}, + {"No.LoginModules.configured.for.name", + "{0}\uC5D0 \uB300\uD574 \uAD6C\uC131\uB41C LoginModules\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"invalid.null.Subject.provided", "\uBD80\uC801\uD569\uD55C \uB110 \uC81C\uBAA9\uC774 \uC81C\uACF5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"invalid.null.CallbackHandler.provided", + "\uBD80\uC801\uD569\uD55C \uB110 CallbackHandler\uAC00 \uC81C\uACF5\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"null.subject.logout.called.before.login", + "\uB110 \uC81C\uBAA9 - \uB85C\uADF8\uC778 \uC804\uC5D0 \uB85C\uADF8\uC544\uC6C3\uC774 \uD638\uCD9C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "\uC778\uC218\uAC00 \uC5C6\uB294 \uC0DD\uC131\uC790\uB97C \uC81C\uACF5\uD558\uC9C0 \uC54A\uC544 LoginModule {0}\uC744(\uB97C) \uC778\uC2A4\uD134\uC2A4\uD654\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"unable.to.instantiate.LoginModule", + "LoginModule\uC744 \uC778\uC2A4\uD134\uC2A4\uD654\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"unable.to.instantiate.LoginModule.", + "LoginModule\uC744 \uC778\uC2A4\uD134\uC2A4\uD654\uD560 \uC218 \uC5C6\uC74C: "}, + {"unable.to.find.LoginModule.class.", + "LoginModule \uD074\uB798\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC74C: "}, + {"unable.to.access.LoginModule.", + "LoginModule\uC5D0 \uC561\uC138\uC2A4\uD560 \uC218 \uC5C6\uC74C: "}, + {"Login.Failure.all.modules.ignored", + "\uB85C\uADF8\uC778 \uC2E4\uD328: \uBAA8\uB4E0 \uBAA8\uB4C8\uC774 \uBB34\uC2DC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: {0}\uC758 \uAD6C\uBB38\uC744 \uBD84\uC11D\uD558\uB294 \uC911 \uC624\uB958 \uBC1C\uC0DD:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: {0} \uAD8C\uD55C\uC744 \uCD94\uAC00\uD558\uB294 \uC911 \uC624\uB958 \uBC1C\uC0DD:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: \uD56D\uBAA9\uC744 \uCD94\uAC00\uD558\uB294 \uC911 \uC624\uB958 \uBC1C\uC0DD:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "\uBCC4\uCE6D \uC774\uB984\uC774 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4({0})."}, + {"unable.to.perform.substitution.on.alias.suffix", + "{0} \uBCC4\uCE6D\uC744 \uB300\uCCB4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"substitution.value.prefix.unsupported", + "\uB300\uCCB4 \uAC12 {0}\uC740(\uB294) \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","\uC720\uD615\uC740 \uB110\uC77C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "\uD0A4 \uC800\uC7A5\uC18C\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uACE0 keystorePasswordURL\uC744 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"expected.keystore.type", "\uD0A4 \uC800\uC7A5\uC18C \uC720\uD615\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"expected.keystore.provider", "\uD0A4 \uC800\uC7A5\uC18C \uC81C\uACF5\uC790\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"multiple.Codebase.expressions", + "Codebase \uD45C\uD604\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uC785\uB2C8\uB2E4."}, + {"multiple.SignedBy.expressions","SignedBy \uD45C\uD604\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uC785\uB2C8\uB2E4."}, + {"duplicate.keystore.domain.name","\uC911\uBCF5\uB41C \uD0A4 \uC800\uC7A5\uC18C \uB3C4\uBA54\uC778 \uC774\uB984: {0}"}, + {"duplicate.keystore.name","\uC911\uBCF5\uB41C \uD0A4 \uC800\uC7A5\uC18C \uC774\uB984: {0}"}, + {"SignedBy.has.empty.alias","SignedBy\uC758 \uBCC4\uCE6D\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uC774\uB984 \uC5C6\uC774 \uC640\uC77C\uB4DC \uCE74\uB4DC \uBB38\uC790 \uD074\uB798\uC2A4\uB97C \uC0AC\uC6A9\uD558\uB294 \uC8FC\uCCB4\uB97C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."}, + {"expected.codeBase.or.SignedBy.or.Principal", + "codeBase, SignedBy \uB610\uB294 \uC8FC\uCCB4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"expected.permission.entry", "\uAD8C\uD55C \uD56D\uBAA9\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."}, + {"number.", "\uC22B\uC790 "}, + {"expected.expect.read.end.of.file.", + "[{0}]\uC774(\uAC00) \uD544\uC694\uD558\uC9C0\uB9CC [\uD30C\uC77C\uC758 \uB05D]\uAE4C\uC9C0 \uC77D\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"expected.read.end.of.file.", + "[;]\uC774 \uD544\uC694\uD558\uC9C0\uB9CC [\uD30C\uC77C\uC758 \uB05D]\uAE4C\uC9C0 \uC77D\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"line.number.msg", "{0} \uD589: {1}"}, + {"line.number.expected.expect.found.actual.", + "{0} \uD589: [{1}]\uC774(\uAC00) \uD544\uC694\uD558\uC9C0\uB9CC [{2}]\uC774(\uAC00) \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."}, + {"null.principalClass.or.principalName", + "principalClass \uB610\uB294 principalName\uC774 \uB110\uC785\uB2C8\uB2E4."}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11 \uD1A0\uD070 [{0}] \uBE44\uBC00\uBC88\uD638: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "\uC81C\uBAA9 \uAE30\uBC18 \uC815\uCC45\uC744 \uC778\uC2A4\uD134\uC2A4\uD654\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_pt_BR.java b/src/sun/security/util/Resources_pt_BR.java new file mode 100644 index 00000000..ba17abf6 --- /dev/null +++ b/src/sun/security/util/Resources_pt_BR.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_pt_BR extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "entrada(s) nula(s) inv\u00E1lida(s)"}, + {"actions.can.only.be.read.", "as a\u00E7\u00F5es s\u00F3 podem ser 'lidas'"}, + {"permission.name.name.syntax.invalid.", + "sintaxe inv\u00E1lida do nome da permiss\u00E3o [{0}]: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Classe da Credencial n\u00E3o seguida por um Nome e uma Classe do Principal"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Classe do Principal n\u00E3o seguida por um Nome do Principal"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "O Nome do Principal deve estar entre aspas"}, + {"Principal.Name.missing.end.quote", + "Faltam as aspas finais no Nome do Principal"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "A Classe do Principal PrivateCredentialPermission n\u00E3o pode ser um valor curinga (*) se o Nome do Principal n\u00E3o for um valor curinga (*)"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tClasse do Principal = {0}\n\tNome do Principal = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "nome nulo fornecido"}, + {"provided.null.keyword.map", "mapa de palavra-chave nulo fornecido"}, + {"provided.null.OID.map", "mapa OID nulo fornecido"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "AccessControlContext nulo inv\u00E1lido fornecido"}, + {"invalid.null.action.provided", "a\u00E7\u00E3o nula inv\u00E1lida fornecida"}, + {"invalid.null.Class.provided", "Classe nula inv\u00E1lida fornecida"}, + {"Subject.", "Assunto:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\tCredencial P\u00FAblica: "}, + {".Private.Credentials.inaccessible.", + "\tCredenciais Privadas inacess\u00EDveis\n"}, + {".Private.Credential.", "\tCredencial Privada: "}, + {".Private.Credential.inaccessible.", + "\tCredencial Privada inacess\u00EDvel\n"}, + {"Subject.is.read.only", "O Assunto \u00E9 somente para leitura"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "tentativa de adicionar um objeto que n\u00E3o \u00E9 uma inst\u00E2ncia de java.security.Principal a um conjunto de principais do Subject"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "tentativa de adicionar um objeto que n\u00E3o \u00E9 uma inst\u00E2ncia de {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Entrada nula inv\u00E1lida: nome"}, + {"No.LoginModules.configured.for.name", + "Nenhum LoginModule configurado para {0}"}, + {"invalid.null.Subject.provided", "Subject nulo inv\u00E1lido fornecido"}, + {"invalid.null.CallbackHandler.provided", + "CallbackHandler nulo inv\u00E1lido fornecido"}, + {"null.subject.logout.called.before.login", + "Subject nulo - log-out chamado antes do log-in"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "n\u00E3o \u00E9 poss\u00EDvel instanciar LoginModule, {0}, porque ele n\u00E3o fornece um construtor sem argumento"}, + {"unable.to.instantiate.LoginModule", + "n\u00E3o \u00E9 poss\u00EDvel instanciar LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "n\u00E3o \u00E9 poss\u00EDvel instanciar LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "n\u00E3o \u00E9 poss\u00EDvel localizar a classe LoginModule: "}, + {"unable.to.access.LoginModule.", + "n\u00E3o \u00E9 poss\u00EDvel acessar LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "Falha de Log-in: todos os m\u00F3dulos ignorados"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: erro durante o parsing de {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: erro ao adicionar a permiss\u00E3o, {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: erro ao adicionar a Entrada:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "nome de alias n\u00E3o fornecido ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "n\u00E3o \u00E9 poss\u00EDvel realizar a substitui\u00E7\u00E3o no alias, {0}"}, + {"substitution.value.prefix.unsupported", + "valor da substitui\u00E7\u00E3o, {0}, n\u00E3o suportado"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","o tipo n\u00E3o pode ser nulo"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "keystorePasswordURL n\u00E3o pode ser especificado sem que a \u00E1rea de armazenamento de chaves tamb\u00E9m seja especificada"}, + {"expected.keystore.type", "tipo de armazenamento de chaves esperado"}, + {"expected.keystore.provider", "fornecedor da \u00E1rea de armazenamento de chaves esperado"}, + {"multiple.Codebase.expressions", + "v\u00E1rias express\u00F5es CodeBase"}, + {"multiple.SignedBy.expressions","v\u00E1rias express\u00F5es SignedBy"}, + {"duplicate.keystore.domain.name","nome do dom\u00EDnio da \u00E1rea de armazenamento de teclas duplicado: {0}"}, + {"duplicate.keystore.name","nome da \u00E1rea de armazenamento de chaves duplicado: {0}"}, + {"SignedBy.has.empty.alias","SignedBy tem alias vazio"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "n\u00E3o \u00E9 poss\u00EDvel especificar um principal com uma classe curinga sem um nome curinga"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "CodeBase ou SignedBy ou Principal esperado"}, + {"expected.permission.entry", "entrada de permiss\u00E3o esperada"}, + {"number.", "n\u00FAmero "}, + {"expected.expect.read.end.of.file.", + "esperado [{0}], lido [fim do arquivo]"}, + {"expected.read.end.of.file.", + "esperado [;], lido [fim do arquivo]"}, + {"line.number.msg", "linha {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "linha {0}: esperada [{1}], encontrada [{2}]"}, + {"null.principalClass.or.principalName", + "principalClass ou principalName nulo"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "Senha PKCS11 de Token [{0}]: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "n\u00E3o \u00E9 poss\u00EDvel instanciar a pol\u00EDtica com base em Subject"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_sv.java b/src/sun/security/util/Resources_sv.java new file mode 100644 index 00000000..000c36dd --- /dev/null +++ b/src/sun/security/util/Resources_sv.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_sv extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "ogiltiga null-indata"}, + {"actions.can.only.be.read.", "funktioner kan endast 'l\u00E4sas'"}, + {"permission.name.name.syntax.invalid.", + "syntaxen f\u00F6r beh\u00F6righetsnamnet [{0}] \u00E4r ogiltig: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Inloggningsuppgiftsklassen f\u00F6ljs inte av klass eller namn f\u00F6r identitetshavare"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Identitetshavareklassen f\u00F6ljs inte av n\u00E5got identitetshavarenamn"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "Identitetshavarenamnet m\u00E5ste anges inom citattecken"}, + {"Principal.Name.missing.end.quote", + "Identitetshavarenamnet saknar avslutande citattecken"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "Identitetshavareklassen PrivateCredentialPermission kan inte ha n\u00E5got jokertecken (*) om inte namnet p\u00E5 identitetshavaren anges med jokertecken (*)"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tIdentitetshavareklass = {0}\n\tIdentitetshavarenamn = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "angav null-namn"}, + {"provided.null.keyword.map", "nullnyckelordsmappning tillhandah\u00F6lls"}, + {"provided.null.OID.map", "null-OID-mappning tillhandah\u00F6lls"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "ogiltigt null-AccessControlContext"}, + {"invalid.null.action.provided", "ogiltig null-funktion"}, + {"invalid.null.Class.provided", "ogiltig null-klass"}, + {"Subject.", "Innehavare:\n"}, + {".Principal.", "\tIdentitetshavare: "}, + {".Public.Credential.", "\tOffentlig inloggning: "}, + {".Private.Credentials.inaccessible.", + "\tPrivat inloggning \u00E4r inte tillg\u00E4nglig\n"}, + {".Private.Credential.", "\tPrivat inloggning: "}, + {".Private.Credential.inaccessible.", + "\tPrivat inloggning \u00E4r inte tillg\u00E4nglig\n"}, + {"Subject.is.read.only", "Innehavare \u00E4r skrivskyddad"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "f\u00F6rs\u00F6k att l\u00E4gga till ett objekt som inte \u00E4r en f\u00F6rekomst av java.security.Principal till en upps\u00E4ttning av identitetshavare"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "f\u00F6rs\u00F6ker l\u00E4gga till ett objekt som inte \u00E4r en instans av {0}"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "Ogiltiga null-indata: namn"}, + {"No.LoginModules.configured.for.name", + "Inga inloggningsmoduler har konfigurerats f\u00F6r {0}"}, + {"invalid.null.Subject.provided", "ogiltig null-innehavare"}, + {"invalid.null.CallbackHandler.provided", + "ogiltig null-CallbackHandler"}, + {"null.subject.logout.called.before.login", + "null-innehavare - utloggning anropades f\u00F6re inloggning"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "kan inte instansiera LoginModule, {0}, eftersom den inte tillhandah\u00E5ller n\u00E5gon icke-argumentskonstruktor"}, + {"unable.to.instantiate.LoginModule", + "kan inte instansiera LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "kan inte instansiera LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "hittar inte LoginModule-klassen: "}, + {"unable.to.access.LoginModule.", + "ingen \u00E5tkomst till LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "Inloggningsfel: alla moduler ignoreras"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: fel vid tolkning av {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: fel vid till\u00E4gg av beh\u00F6righet, {0}:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: fel vid till\u00E4gg av post:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "aliasnamn ej angivet ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "kan ej ers\u00E4tta alias, {0}"}, + {"substitution.value.prefix.unsupported", + "ers\u00E4ttningsv\u00E4rde, {0}, st\u00F6ds ej"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","typen kan inte vara null"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "kan inte ange keystorePasswordURL utan att ange nyckellager"}, + {"expected.keystore.type", "f\u00F6rv\u00E4ntad nyckellagertyp"}, + {"expected.keystore.provider", "nyckellagerleverant\u00F6r f\u00F6rv\u00E4ntades"}, + {"multiple.Codebase.expressions", + "flera CodeBase-uttryck"}, + {"multiple.SignedBy.expressions","flera SignedBy-uttryck"}, + {"duplicate.keystore.domain.name","dom\u00E4nnamn f\u00F6r dubbelt nyckellager: {0}"}, + {"duplicate.keystore.name","namn f\u00F6r dubbelt nyckellager: {0}"}, + {"SignedBy.has.empty.alias","SignedBy har ett tomt alias"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "kan inte ange identitetshavare med en jokerteckenklass utan ett jokerteckennamn"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "f\u00F6rv\u00E4ntad codeBase eller SignedBy eller identitetshavare"}, + {"expected.permission.entry", "f\u00F6rv\u00E4ntade beh\u00F6righetspost"}, + {"number.", "antal "}, + {"expected.expect.read.end.of.file.", + "f\u00F6rv\u00E4ntade [{0}], l\u00E4ste [filslut]"}, + {"expected.read.end.of.file.", + "f\u00F6rv\u00E4ntade [;], l\u00E4ste [filslut]"}, + {"line.number.msg", "rad {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "rad {0}: f\u00F6rv\u00E4ntade [{1}], hittade [{2}]"}, + {"null.principalClass.or.principalName", + "null-principalClass eller -principalName"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11-tecken [{0}] L\u00F6senord: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "den innehavarbaserade policyn kan inte skapas"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_zh_CN.java b/src/sun/security/util/Resources_zh_CN.java new file mode 100644 index 00000000..536fd09e --- /dev/null +++ b/src/sun/security/util/Resources_zh_CN.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_zh_CN extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "\u65E0\u6548\u7684\u7A7A\u8F93\u5165"}, + {"actions.can.only.be.read.", "\u64CD\u4F5C\u53EA\u80FD\u4E3A '\u8BFB\u53D6'"}, + {"permission.name.name.syntax.invalid.", + "\u6743\u9650\u540D\u79F0 [{0}] \u8BED\u6CD5\u65E0\u6548: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "\u8EAB\u4EFD\u8BC1\u660E\u7C7B\u540E\u9762\u672A\u8DDF\u968F\u4E3B\u7528\u6237\u7C7B\u53CA\u540D\u79F0"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "\u4E3B\u7528\u6237\u7C7B\u540E\u9762\u672A\u8DDF\u968F\u4E3B\u7528\u6237\u540D\u79F0"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "\u4E3B\u7528\u6237\u540D\u79F0\u5FC5\u987B\u653E\u5728\u5F15\u53F7\u5185"}, + {"Principal.Name.missing.end.quote", + "\u4E3B\u7528\u6237\u540D\u79F0\u7F3A\u5C11\u53F3\u5F15\u53F7"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "\u5982\u679C\u4E3B\u7528\u6237\u540D\u79F0\u4E0D\u662F\u901A\u914D\u7B26 (*) \u503C, \u90A3\u4E48 PrivateCredentialPermission \u4E3B\u7528\u6237\u7C7B\u4E0D\u80FD\u662F\u901A\u914D\u7B26 (*) \u503C"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\t\u4E3B\u7528\u6237\u7C7B = {0}\n\t\u4E3B\u7528\u6237\u540D\u79F0 = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "\u63D0\u4F9B\u7684\u540D\u79F0\u4E3A\u7A7A\u503C"}, + {"provided.null.keyword.map", "\u63D0\u4F9B\u7684\u5173\u952E\u5B57\u6620\u5C04\u4E3A\u7A7A\u503C"}, + {"provided.null.OID.map", "\u63D0\u4F9B\u7684 OID \u6620\u5C04\u4E3A\u7A7A\u503C"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "\u63D0\u4F9B\u4E86\u65E0\u6548\u7684\u7A7A AccessControlContext"}, + {"invalid.null.action.provided", "\u63D0\u4F9B\u4E86\u65E0\u6548\u7684\u7A7A\u64CD\u4F5C"}, + {"invalid.null.Class.provided", "\u63D0\u4F9B\u4E86\u65E0\u6548\u7684\u7A7A\u7C7B"}, + {"Subject.", "\u4E3B\u9898: \n"}, + {".Principal.", "\t\u4E3B\u7528\u6237: "}, + {".Public.Credential.", "\t\u516C\u5171\u8EAB\u4EFD\u8BC1\u660E: "}, + {".Private.Credentials.inaccessible.", + "\t\u65E0\u6CD5\u8BBF\u95EE\u4E13\u7528\u8EAB\u4EFD\u8BC1\u660E\n"}, + {".Private.Credential.", "\t\u4E13\u7528\u8EAB\u4EFD\u8BC1\u660E: "}, + {".Private.Credential.inaccessible.", + "\t\u65E0\u6CD5\u8BBF\u95EE\u4E13\u7528\u8EAB\u4EFD\u8BC1\u660E\n"}, + {"Subject.is.read.only", "\u4E3B\u9898\u4E3A\u53EA\u8BFB"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "\u6B63\u5728\u5C1D\u8BD5\u5C06\u4E00\u4E2A\u975E java.security.Principal \u5B9E\u4F8B\u7684\u5BF9\u8C61\u6DFB\u52A0\u5230\u4E3B\u9898\u7684\u4E3B\u7528\u6237\u96C6\u4E2D"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "\u6B63\u5728\u5C1D\u8BD5\u6DFB\u52A0\u4E00\u4E2A\u975E{0}\u5B9E\u4F8B\u7684\u5BF9\u8C61"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "\u65E0\u6548\u7A7A\u8F93\u5165: \u540D\u79F0"}, + {"No.LoginModules.configured.for.name", + "\u6CA1\u6709\u4E3A{0}\u914D\u7F6E LoginModules"}, + {"invalid.null.Subject.provided", "\u63D0\u4F9B\u4E86\u65E0\u6548\u7684\u7A7A\u4E3B\u9898"}, + {"invalid.null.CallbackHandler.provided", + "\u63D0\u4F9B\u4E86\u65E0\u6548\u7684\u7A7A CallbackHandler"}, + {"null.subject.logout.called.before.login", + "\u7A7A\u4E3B\u9898 - \u5728\u767B\u5F55\u4E4B\u524D\u8C03\u7528\u4E86\u6CE8\u9500"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "\u65E0\u6CD5\u5B9E\u4F8B\u5316 LoginModule, {0}, \u56E0\u4E3A\u5B83\u672A\u63D0\u4F9B\u4E00\u4E2A\u65E0\u53C2\u6570\u6784\u9020\u5668"}, + {"unable.to.instantiate.LoginModule", + "\u65E0\u6CD5\u5B9E\u4F8B\u5316 LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "\u65E0\u6CD5\u5B9E\u4F8B\u5316 LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "\u65E0\u6CD5\u627E\u5230 LoginModule \u7C7B: "}, + {"unable.to.access.LoginModule.", + "\u65E0\u6CD5\u8BBF\u95EE LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "\u767B\u5F55\u5931\u8D25: \u5FFD\u7565\u6240\u6709\u6A21\u5757"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: \u89E3\u6790{0}\u65F6\u51FA\u9519:\n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: \u6DFB\u52A0\u6743\u9650{0}\u65F6\u51FA\u9519:\n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: \u6DFB\u52A0\u6761\u76EE\u65F6\u51FA\u9519:\n\t{0}"}, + {"alias.name.not.provided.pe.name.", "\u672A\u63D0\u4F9B\u522B\u540D ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "\u65E0\u6CD5\u5728\u522B\u540D {0} \u4E0A\u6267\u884C\u66FF\u4EE3"}, + {"substitution.value.prefix.unsupported", + "\u66FF\u4EE3\u503C{0}\u4E0D\u53D7\u652F\u6301"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A\u503C"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "\u4E0D\u6307\u5B9A\u5BC6\u94A5\u5E93\u65F6\u65E0\u6CD5\u6307\u5B9A keystorePasswordURL"}, + {"expected.keystore.type", "\u5E94\u4E3A\u5BC6\u94A5\u5E93\u7C7B\u578B"}, + {"expected.keystore.provider", "\u5E94\u4E3A\u5BC6\u94A5\u5E93\u63D0\u4F9B\u65B9"}, + {"multiple.Codebase.expressions", + "\u591A\u4E2A\u4EE3\u7801\u5E93\u8868\u8FBE\u5F0F"}, + {"multiple.SignedBy.expressions","\u591A\u4E2A SignedBy \u8868\u8FBE\u5F0F"}, + {"duplicate.keystore.domain.name","\u5BC6\u94A5\u5E93\u57DF\u540D\u91CD\u590D: {0}"}, + {"duplicate.keystore.name","\u5BC6\u94A5\u5E93\u540D\u79F0\u91CD\u590D: {0}"}, + {"SignedBy.has.empty.alias","SignedBy \u6709\u7A7A\u522B\u540D"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u6CA1\u6709\u901A\u914D\u7B26\u540D\u79F0, \u65E0\u6CD5\u4F7F\u7528\u901A\u914D\u7B26\u7C7B\u6307\u5B9A\u4E3B\u7528\u6237"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "\u5E94\u4E3A codeBase, SignedBy \u6216\u4E3B\u7528\u6237"}, + {"expected.permission.entry", "\u5E94\u4E3A\u6743\u9650\u6761\u76EE"}, + {"number.", "\u7F16\u53F7 "}, + {"expected.expect.read.end.of.file.", + "\u5E94\u4E3A [{0}], \u8BFB\u53D6\u7684\u662F [\u6587\u4EF6\u7ED3\u5C3E]"}, + {"expected.read.end.of.file.", + "\u5E94\u4E3A [;], \u8BFB\u53D6\u7684\u662F [\u6587\u4EF6\u7ED3\u5C3E]"}, + {"line.number.msg", "\u5217{0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "\u884C\u53F7 {0}: \u5E94\u4E3A [{1}], \u627E\u5230 [{2}]"}, + {"null.principalClass.or.principalName", + "principalClass \u6216 principalName \u4E3A\u7A7A\u503C"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11 \u6807\u8BB0 [{0}] \u53E3\u4EE4: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "\u65E0\u6CD5\u5B9E\u4F8B\u5316\u57FA\u4E8E\u4E3B\u9898\u7684\u7B56\u7565"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/Resources_zh_TW.java b/src/sun/security/util/Resources_zh_TW.java new file mode 100644 index 00000000..a1de7183 --- /dev/null +++ b/src/sun/security/util/Resources_zh_TW.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +/** + *

    This class represents the ResourceBundle + * for javax.security.auth and sun.security. + * + */ +public class Resources_zh_TW extends java.util.ListResourceBundle { + + private static final Object[][] contents = { + + // javax.security.auth.PrivateCredentialPermission + {"invalid.null.input.s.", "\u7121\u6548\u7A7A\u503C\u8F38\u5165"}, + {"actions.can.only.be.read.", "\u52D5\u4F5C\u53EA\u80FD\u88AB\u300C\u8B80\u53D6\u300D"}, + {"permission.name.name.syntax.invalid.", + "\u6B0A\u9650\u540D\u7A31 [{0}] \u662F\u7121\u6548\u7684\u8A9E\u6CD5: "}, + {"Credential.Class.not.followed.by.a.Principal.Class.and.Name", + "Credential \u985E\u5225\u5F8C\u9762\u4E0D\u662F Principal \u985E\u5225\u53CA\u540D\u7A31"}, + {"Principal.Class.not.followed.by.a.Principal.Name", + "Principal \u985E\u5225\u5F8C\u9762\u4E0D\u662F Principal \u540D\u7A31"}, + {"Principal.Name.must.be.surrounded.by.quotes", + "Principal \u540D\u7A31\u5FC5\u9808\u4EE5\u5F15\u865F\u5708\u4F4F"}, + {"Principal.Name.missing.end.quote", + "Principal \u540D\u7A31\u7F3A\u5C11\u4E0B\u5F15\u865F"}, + {"PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value", + "\u5982\u679C Principal \u540D\u7A31\u4E0D\u662F\u4E00\u500B\u842C\u7528\u5B57\u5143 (*) \u503C\uFF0C\u90A3\u9EBC PrivateCredentialPermission Principal \u985E\u5225\u5C31\u4E0D\u80FD\u662F\u842C\u7528\u5B57\u5143 (*) \u503C"}, + {"CredOwner.Principal.Class.class.Principal.Name.name", + "CredOwner:\n\tPrincipal \u985E\u5225 = {0}\n\tPrincipal \u540D\u7A31 = {1}"}, + + // javax.security.auth.x500 + {"provided.null.name", "\u63D0\u4F9B\u7A7A\u503C\u540D\u7A31"}, + {"provided.null.keyword.map", "\u63D0\u4F9B\u7A7A\u503C\u95DC\u9375\u5B57\u5C0D\u6620"}, + {"provided.null.OID.map", "\u63D0\u4F9B\u7A7A\u503C OID \u5C0D\u6620"}, + + // javax.security.auth.Subject + {"NEWLINE", "\n"}, + {"invalid.null.AccessControlContext.provided", + "\u63D0\u4F9B\u7121\u6548\u7684\u7A7A\u503C AccessControlContext"}, + {"invalid.null.action.provided", "\u63D0\u4F9B\u7121\u6548\u7684\u7A7A\u503C\u52D5\u4F5C"}, + {"invalid.null.Class.provided", "\u63D0\u4F9B\u7121\u6548\u7684\u7A7A\u503C\u985E\u5225"}, + {"Subject.", "\u4E3B\u984C:\n"}, + {".Principal.", "\tPrincipal: "}, + {".Public.Credential.", "\t\u516C\u7528\u8B49\u660E\u8CC7\u6599: "}, + {".Private.Credentials.inaccessible.", + "\t\u79C1\u4EBA\u8B49\u660E\u8CC7\u6599\u7121\u6CD5\u5B58\u53D6\n"}, + {".Private.Credential.", "\t\u79C1\u4EBA\u8B49\u660E\u8CC7\u6599: "}, + {".Private.Credential.inaccessible.", + "\t\u79C1\u4EBA\u8B49\u660E\u8CC7\u6599\u7121\u6CD5\u5B58\u53D6\n"}, + {"Subject.is.read.only", "\u4E3B\u984C\u70BA\u552F\u8B80"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set", + "\u8A66\u5716\u65B0\u589E\u4E00\u500B\u975E java.security.Principal \u57F7\u884C\u8655\u7406\u7684\u7269\u4EF6\u81F3\u4E3B\u984C\u7684 Principal \u7FA4\u4E2D"}, + {"attempting.to.add.an.object.which.is.not.an.instance.of.class", + "\u8A66\u5716\u65B0\u589E\u4E00\u500B\u975E {0} \u57F7\u884C\u8655\u7406\u7684\u7269\u4EF6"}, + + // javax.security.auth.login.AppConfigurationEntry + {"LoginModuleControlFlag.", "LoginModuleControlFlag: "}, + + // javax.security.auth.login.LoginContext + {"Invalid.null.input.name", "\u7121\u6548\u7A7A\u503C\u8F38\u5165: \u540D\u7A31"}, + {"No.LoginModules.configured.for.name", + "\u7121\u91DD\u5C0D {0} \u914D\u7F6E\u7684 LoginModules"}, + {"invalid.null.Subject.provided", "\u63D0\u4F9B\u7121\u6548\u7A7A\u503C\u4E3B\u984C"}, + {"invalid.null.CallbackHandler.provided", + "\u63D0\u4F9B\u7121\u6548\u7A7A\u503C CallbackHandler"}, + {"null.subject.logout.called.before.login", + "\u7A7A\u503C\u4E3B\u984C - \u5728\u767B\u5165\u4E4B\u524D\u5373\u547C\u53EB\u767B\u51FA"}, + {"unable.to.instantiate.LoginModule.module.because.it.does.not.provide.a.no.argument.constructor", + "\u7121\u6CD5\u5275\u8A2D LoginModule\uFF0C{0}\uFF0C\u56E0\u70BA\u5B83\u4E26\u672A\u63D0\u4F9B\u975E\u5F15\u6578\u7684\u5EFA\u69CB\u5B50"}, + {"unable.to.instantiate.LoginModule", + "\u7121\u6CD5\u5EFA\u7ACB LoginModule"}, + {"unable.to.instantiate.LoginModule.", + "\u7121\u6CD5\u5EFA\u7ACB LoginModule: "}, + {"unable.to.find.LoginModule.class.", + "\u627E\u4E0D\u5230 LoginModule \u985E\u5225: "}, + {"unable.to.access.LoginModule.", + "\u7121\u6CD5\u5B58\u53D6 LoginModule: "}, + {"Login.Failure.all.modules.ignored", + "\u767B\u5165\u5931\u6557: \u5FFD\u7565\u6240\u6709\u6A21\u7D44"}, + + // sun.security.provider.PolicyFile + + {"java.security.policy.error.parsing.policy.message", + "java.security.policy: \u5256\u6790\u932F\u8AA4 {0}: \n\t{1}"}, + {"java.security.policy.error.adding.Permission.perm.message", + "java.security.policy: \u65B0\u589E\u6B0A\u9650\u932F\u8AA4 {0}: \n\t{1}"}, + {"java.security.policy.error.adding.Entry.message", + "java.security.policy: \u65B0\u589E\u9805\u76EE\u932F\u8AA4: \n\t{0}"}, + {"alias.name.not.provided.pe.name.", "\u672A\u63D0\u4F9B\u5225\u540D\u540D\u7A31 ({0})"}, + {"unable.to.perform.substitution.on.alias.suffix", + "\u7121\u6CD5\u5C0D\u5225\u540D\u57F7\u884C\u66FF\u63DB\uFF0C{0}"}, + {"substitution.value.prefix.unsupported", + "\u4E0D\u652F\u63F4\u7684\u66FF\u63DB\u503C\uFF0C{0}"}, + {"LPARAM", "("}, + {"RPARAM", ")"}, + {"type.can.t.be.null","\u8F38\u5165\u4E0D\u80FD\u70BA\u7A7A\u503C"}, + + // sun.security.provider.PolicyParser + {"keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore", + "\u6307\u5B9A keystorePasswordURL \u9700\u8981\u540C\u6642\u6307\u5B9A\u91D1\u9470\u5132\u5B58\u5EAB"}, + {"expected.keystore.type", "\u9810\u671F\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u985E\u578B"}, + {"expected.keystore.provider", "\u9810\u671F\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u63D0\u4F9B\u8005"}, + {"multiple.Codebase.expressions", + "\u591A\u91CD Codebase \u8868\u793A\u5F0F"}, + {"multiple.SignedBy.expressions","\u591A\u91CD SignedBy \u8868\u793A\u5F0F"}, + {"duplicate.keystore.domain.name","\u91CD\u8907\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u7DB2\u57DF\u540D\u7A31: {0}"}, + {"duplicate.keystore.name","\u91CD\u8907\u7684\u91D1\u9470\u5132\u5B58\u5EAB\u540D\u7A31: {0}"}, + {"SignedBy.has.empty.alias","SignedBy \u6709\u7A7A\u5225\u540D"}, + {"can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name", + "\u6C92\u6709\u842C\u7528\u5B57\u5143\u540D\u7A31\uFF0C\u7121\u6CD5\u6307\u5B9A\u542B\u6709\u842C\u7528\u5B57\u5143\u985E\u5225\u7684 Principal"}, + {"expected.codeBase.or.SignedBy.or.Principal", + "\u9810\u671F\u7684 codeBase \u6216 SignedBy \u6216 Principal"}, + {"expected.permission.entry", "\u9810\u671F\u7684\u6B0A\u9650\u9805\u76EE"}, + {"number.", "\u865F\u78BC "}, + {"expected.expect.read.end.of.file.", + "\u9810\u671F\u7684 [{0}], \u8B80\u53D6 [end of file]"}, + {"expected.read.end.of.file.", + "\u9810\u671F\u7684 [;], \u8B80\u53D6 [end of file]"}, + {"line.number.msg", "\u884C {0}: {1}"}, + {"line.number.expected.expect.found.actual.", + "\u884C {0}: \u9810\u671F\u7684 [{1}]\uFF0C\u767C\u73FE [{2}]"}, + {"null.principalClass.or.principalName", + "\u7A7A\u503C principalClass \u6216 principalName"}, + + // sun.security.pkcs11.SunPKCS11 + {"PKCS11.Token.providerName.Password.", + "PKCS11 \u8A18\u865F [{0}] \u5BC6\u78BC: "}, + + /* --- DEPRECATED --- */ + // javax.security.auth.Policy + {"unable.to.instantiate.Subject.based.policy", + "\u7121\u6CD5\u5EFA\u7ACB\u4E3B\u984C\u5F0F\u7684\u539F\u5247"} + }; + + + /** + * Returns the contents of this ResourceBundle. + * + *

    + * + * @return the contents of this ResourceBundle. + */ + @Override + public Object[][] getContents() { + return contents; + } +} + diff --git a/src/sun/security/util/SecurityConstants.java b/src/sun/security/util/SecurityConstants.java new file mode 100644 index 00000000..84df2945 --- /dev/null +++ b/src/sun/security/util/SecurityConstants.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.net.SocketPermission; +import java.net.NetPermission; +import java.security.Permission; +import java.security.SecurityPermission; +import java.security.AllPermission; + +/** + * Permission constants and string constants used to create permissions + * used throughout the JDK. + */ +public final class SecurityConstants { + // Cannot create one of these + private SecurityConstants () { + } + + // Commonly used string constants for permission actions used by + // SecurityManager. Declare here for shortcut when checking permissions + // in FilePermission, SocketPermission, and PropertyPermission. + + public static final String FILE_DELETE_ACTION = "delete"; + public static final String FILE_EXECUTE_ACTION = "execute"; + public static final String FILE_READ_ACTION = "read"; + public static final String FILE_WRITE_ACTION = "write"; + public static final String FILE_READLINK_ACTION = "readlink"; + + public static final String SOCKET_RESOLVE_ACTION = "resolve"; + public static final String SOCKET_CONNECT_ACTION = "connect"; + public static final String SOCKET_LISTEN_ACTION = "listen"; + public static final String SOCKET_ACCEPT_ACTION = "accept"; + public static final String SOCKET_CONNECT_ACCEPT_ACTION = "connect,accept"; + + public static final String PROPERTY_RW_ACTION = "read,write"; + public static final String PROPERTY_READ_ACTION = "read"; + public static final String PROPERTY_WRITE_ACTION = "write"; + + // Permission constants used in the various checkPermission() calls in JDK. + + // java.lang.Class, java.lang.SecurityManager, java.lang.System, + // java.net.URLConnection, java.security.AllPermission, java.security.Policy, + // sun.security.provider.PolicyFile + public static final AllPermission ALL_PERMISSION = new AllPermission(); + + /** + * AWT Permissions used in the JDK. + */ + public static class AWT { + private AWT() { } + + /** + * The class name of the factory to create java.awt.AWTPermission objects. + */ + private static final String AWTFactory = "sun.awt.AWTPermissionFactory"; + + /** + * The PermissionFactory to create AWT permissions (or null if AWT is + * not present) + */ + private static final PermissionFactory factory = permissionFactory(); + + private static PermissionFactory permissionFactory() { + Class c; + try { + c = Class.forName(AWTFactory, false, AWT.class.getClassLoader()); + } catch (ClassNotFoundException e) { + // not available + return null; + } + // AWT present + try { + return (PermissionFactory)c.newInstance(); + } catch (ReflectiveOperationException x) { + throw new InternalError(x); + } + } + + private static Permission newAWTPermission(String name) { + return (factory == null) ? null : factory.newPermission(name); + } + + // java.lang.SecurityManager + public static final Permission TOPLEVEL_WINDOW_PERMISSION = + newAWTPermission("showWindowWithoutWarningBanner"); + + // java.lang.SecurityManager + public static final Permission ACCESS_CLIPBOARD_PERMISSION = + newAWTPermission("accessClipboard"); + + // java.lang.SecurityManager + public static final Permission CHECK_AWT_EVENTQUEUE_PERMISSION = + newAWTPermission("accessEventQueue"); + + // java.awt.Dialog + public static final Permission TOOLKIT_MODALITY_PERMISSION = + newAWTPermission("toolkitModality"); + + // java.awt.Robot + public static final Permission READ_DISPLAY_PIXELS_PERMISSION = + newAWTPermission("readDisplayPixels"); + + // java.awt.Robot + public static final Permission CREATE_ROBOT_PERMISSION = + newAWTPermission("createRobot"); + + // java.awt.MouseInfo + public static final Permission WATCH_MOUSE_PERMISSION = + newAWTPermission("watchMousePointer"); + + // java.awt.Window + public static final Permission SET_WINDOW_ALWAYS_ON_TOP_PERMISSION = + newAWTPermission("setWindowAlwaysOnTop"); + + // java.awt.Toolkit + public static final Permission ALL_AWT_EVENTS_PERMISSION = + newAWTPermission("listenToAllAWTEvents"); + + // java.awt.SystemTray + public static final Permission ACCESS_SYSTEM_TRAY_PERMISSION = + newAWTPermission("accessSystemTray"); + } + + // java.net.URL + public static final NetPermission SPECIFY_HANDLER_PERMISSION = + new NetPermission("specifyStreamHandler"); + + // java.net.ProxySelector + public static final NetPermission SET_PROXYSELECTOR_PERMISSION = + new NetPermission("setProxySelector"); + + // java.net.ProxySelector + public static final NetPermission GET_PROXYSELECTOR_PERMISSION = + new NetPermission("getProxySelector"); + + // java.net.CookieHandler + public static final NetPermission SET_COOKIEHANDLER_PERMISSION = + new NetPermission("setCookieHandler"); + + // java.net.CookieHandler + public static final NetPermission GET_COOKIEHANDLER_PERMISSION = + new NetPermission("getCookieHandler"); + + // java.net.ResponseCache + public static final NetPermission SET_RESPONSECACHE_PERMISSION = + new NetPermission("setResponseCache"); + + // java.net.ResponseCache + public static final NetPermission GET_RESPONSECACHE_PERMISSION = + new NetPermission("getResponseCache"); + + // java.lang.SecurityManager, sun.applet.AppletPanel, sun.misc.Launcher + public static final RuntimePermission CREATE_CLASSLOADER_PERMISSION = + new RuntimePermission("createClassLoader"); + + // java.lang.SecurityManager + public static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = + new RuntimePermission("accessDeclaredMembers"); + + // java.lang.SecurityManager, sun.applet.AppletSecurity + public static final RuntimePermission MODIFY_THREAD_PERMISSION = + new RuntimePermission("modifyThread"); + + // java.lang.SecurityManager, sun.applet.AppletSecurity + public static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = + new RuntimePermission("modifyThreadGroup"); + + // java.lang.Class + public static final RuntimePermission GET_PD_PERMISSION = + new RuntimePermission("getProtectionDomain"); + + // java.lang.Class, java.lang.ClassLoader, java.lang.Thread + public static final RuntimePermission GET_CLASSLOADER_PERMISSION = + new RuntimePermission("getClassLoader"); + + // java.lang.Thread + public static final RuntimePermission STOP_THREAD_PERMISSION = + new RuntimePermission("stopThread"); + + // java.lang.Thread + public static final RuntimePermission GET_STACK_TRACE_PERMISSION = + new RuntimePermission("getStackTrace"); + + // java.security.AccessControlContext + public static final SecurityPermission CREATE_ACC_PERMISSION = + new SecurityPermission("createAccessControlContext"); + + // java.security.AccessControlContext + public static final SecurityPermission GET_COMBINER_PERMISSION = + new SecurityPermission("getDomainCombiner"); + + // java.security.Policy, java.security.ProtectionDomain + public static final SecurityPermission GET_POLICY_PERMISSION = + new SecurityPermission ("getPolicy"); + + // java.lang.SecurityManager + public static final SocketPermission LOCAL_LISTEN_PERMISSION = + new SocketPermission("localhost:0", SOCKET_LISTEN_ACTION); +} diff --git a/src/sun/security/util/SecurityProviderConstants.java b/src/sun/security/util/SecurityProviderConstants.java new file mode 100644 index 00000000..2054e2da --- /dev/null +++ b/src/sun/security/util/SecurityProviderConstants.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.util.regex.PatternSyntaxException; +import java.security.InvalidParameterException; +import sun.security.action.GetPropertyAction; + +/** + * Various constants such as version number, default key length, used by + * the JDK security/crypto providers. + */ +public final class SecurityProviderConstants { + private static final Debug debug = + Debug.getInstance("jca", "ProviderConfig"); + + // Cannot create one of these + private SecurityProviderConstants () { + } + + public static final int getDefDSASubprimeSize(int primeSize) { + if (primeSize <= 1024) { + return 160; + } else if (primeSize == 2048) { + return 224; + } else if (primeSize == 3072) { + return 256; + } else { + throw new InvalidParameterException("Invalid DSA Prime Size: " + + primeSize); + } + } + + public static final int DEF_DSA_KEY_SIZE; + public static final int DEF_RSA_KEY_SIZE; + public static final int DEF_RSASSA_PSS_KEY_SIZE; + public static final int DEF_DH_KEY_SIZE; + public static final int DEF_EC_KEY_SIZE; + + private static final String KEY_LENGTH_PROP = + "jdk.security.defaultKeySize"; + static { + String keyLengthStr = GetPropertyAction.privilegedGetProperty + (KEY_LENGTH_PROP); + int dsaKeySize = 2048; + int rsaKeySize = 1024; + int rsaSsaPssKeySize = 2048; + int dhKeySize = 2048; + int ecKeySize = 256; + + if (keyLengthStr != null) { + try { + String[] pairs = keyLengthStr.split(","); + for (String p : pairs) { + String[] algoAndValue = p.split(":"); + if (algoAndValue.length != 2) { + // invalid pair, skip to next pair + if (debug != null) { + debug.println("Ignoring invalid pair in " + + KEY_LENGTH_PROP + " property: " + p); + } + continue; + } + String algoName = algoAndValue[0].trim().toUpperCase(); + int value = -1; + try { + value = Integer.parseInt(algoAndValue[1].trim()); + } catch (NumberFormatException nfe) { + // invalid value, skip to next pair + if (debug != null) { + debug.println("Ignoring invalid value in " + + KEY_LENGTH_PROP + " property: " + p); + } + continue; + } + if (algoName.equals("DSA")) { + dsaKeySize = value; + } else if (algoName.equals("RSA")) { + rsaKeySize = value; + } else if (algoName.equals("RSASSA-PSS")) { + rsaSsaPssKeySize = value; + } else if (algoName.equals("DH")) { + dhKeySize = value; + } else if (algoName.equals("EC")) { + ecKeySize = value; + } else { + if (debug != null) { + debug.println("Ignoring unsupported algo in " + + KEY_LENGTH_PROP + " property: " + p); + } + continue; + } + if (debug != null) { + debug.println("Overriding default " + algoName + + " keysize with value from " + + KEY_LENGTH_PROP + " property: " + value); + } + } + } catch (PatternSyntaxException pse) { + // if property syntax is not followed correctly + if (debug != null) { + debug.println("Unexpected exception while parsing " + + KEY_LENGTH_PROP + " property: " + pse); + } + } + } + DEF_DSA_KEY_SIZE = dsaKeySize; + DEF_RSA_KEY_SIZE = rsaKeySize; + DEF_RSASSA_PSS_KEY_SIZE = rsaSsaPssKeySize; + DEF_DH_KEY_SIZE = dhKeySize; + DEF_EC_KEY_SIZE = ecKeySize; + } +} diff --git a/src/sun/security/util/SignatureFileVerifier.java b/src/sun/security/util/SignatureFileVerifier.java new file mode 100644 index 00000000..fa0f5301 --- /dev/null +++ b/src/sun/security/util/SignatureFileVerifier.java @@ -0,0 +1,656 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.security.cert.CertPath; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.*; +import java.io.*; +import java.util.*; +import java.util.jar.*; + +import sun.security.pkcs.*; +import java.util.Base64; + +import sun.security.jca.Providers; + +public class SignatureFileVerifier { + + /* Are we debugging ? */ + private static final Debug debug = Debug.getInstance("jar"); + + /* cache of CodeSigner objects */ + private ArrayList signerCache; + + private static final String ATTR_DIGEST = + ("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase + (Locale.ENGLISH); + + /** the PKCS7 block for this .DSA/.RSA/.EC file */ + private PKCS7 block; + + /** the raw bytes of the .SF file */ + private byte sfBytes[]; + + /** the name of the signature block file, uppercased and without + * the extension (.DSA/.RSA/.EC) + */ + private String name; + + /** the ManifestDigester */ + private ManifestDigester md; + + /** cache of created MessageDigest objects */ + private HashMap createdDigests; + + /* workaround for parsing Netscape jars */ + private boolean workaround = false; + + /* for generating certpath objects */ + private CertificateFactory certificateFactory = null; + + /** + * Create the named SignatureFileVerifier. + * + * @param name the name of the signature block file (.DSA/.RSA/.EC) + * + * @param rawBytes the raw bytes of the signature block file + */ + public SignatureFileVerifier(ArrayList signerCache, + ManifestDigester md, + String name, + byte rawBytes[]) + throws IOException, CertificateException + { + // new PKCS7() calls CertificateFactory.getInstance() + // need to use local providers here, see Providers class + Object obj = null; + try { + obj = Providers.startJarVerification(); + block = new PKCS7(rawBytes); + sfBytes = block.getContentInfo().getData(); + certificateFactory = CertificateFactory.getInstance("X509"); + } finally { + Providers.stopJarVerification(obj); + } + this.name = name.substring(0, name.lastIndexOf(".")) + .toUpperCase(Locale.ENGLISH); + this.md = md; + this.signerCache = signerCache; + } + + /** + * returns true if we need the .SF file + */ + public boolean needSignatureFileBytes() + { + + return sfBytes == null; + } + + + /** + * returns true if we need this .SF file. + * + * @param name the name of the .SF file without the extension + * + */ + public boolean needSignatureFile(String name) + { + return this.name.equalsIgnoreCase(name); + } + + /** + * used to set the raw bytes of the .SF file when it + * is external to the signature block file. + */ + public void setSignatureFile(byte sfBytes[]) + { + this.sfBytes = sfBytes; + } + + /** + * Utility method used by JarVerifier and JarSigner + * to determine the signature file names and PKCS7 block + * files names that are supported + * + * @param s file name + * @return true if the input file name is a supported + * Signature File or PKCS7 block file name + */ + public static boolean isBlockOrSF(String s) { + // we currently only support DSA and RSA PKCS7 blocks + if (s.endsWith(".SF") || s.endsWith(".DSA") || + s.endsWith(".RSA") || s.endsWith(".EC")) { + return true; + } + return false; + } + + /** + * Yet another utility method used by JarVerifier and JarSigner + * to determine what files are signature related, which includes + * the MANIFEST, SF files, known signature block files, and other + * unknown signature related files (those starting with SIG- with + * an optional [A-Z0-9]{1,3} extension right inside META-INF). + * + * @param s file name + * @return true if the input file name is signature related + */ + public static boolean isSigningRelated(String name) { + name = name.toUpperCase(Locale.ENGLISH); + if (!name.startsWith("META-INF/")) { + return false; + } + name = name.substring(9); + if (name.indexOf('/') != -1) { + return false; + } + if (isBlockOrSF(name) || name.equals("MANIFEST.MF")) { + return true; + } else if (name.startsWith("SIG-")) { + // check filename extension + // see https://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html#Digital_Signatures + // for what filename extensions are legal + int extIndex = name.lastIndexOf('.'); + if (extIndex != -1) { + String ext = name.substring(extIndex + 1); + // validate length first + if (ext.length() > 3 || ext.length() < 1) { + return false; + } + // then check chars, must be in [a-zA-Z0-9] per the jar spec + for (int index = 0; index < ext.length(); index++) { + char cc = ext.charAt(index); + // chars are promoted to uppercase so skip lowercase checks + if ((cc < 'A' || cc > 'Z') && (cc < '0' || cc > '9')) { + return false; + } + } + } + return true; // no extension is OK + } + return false; + } + + /** get digest from cache */ + + private MessageDigest getDigest(String algorithm) + { + if (createdDigests == null) + createdDigests = new HashMap(); + + MessageDigest digest = createdDigests.get(algorithm); + + if (digest == null) { + try { + digest = MessageDigest.getInstance(algorithm); + createdDigests.put(algorithm, digest); + } catch (NoSuchAlgorithmException nsae) { + // ignore + } + } + return digest; + } + + /** + * process the signature block file. Goes through the .SF file + * and adds code signers for each section where the .SF section + * hash was verified against the Manifest section. + * + * + */ + public void process(Hashtable signers, + List manifestDigests) + throws IOException, SignatureException, NoSuchAlgorithmException, + JarException, CertificateException + { + // calls Signature.getInstance() and MessageDigest.getInstance() + // need to use local providers here, see Providers class + Object obj = null; + try { + obj = Providers.startJarVerification(); + processImpl(signers, manifestDigests); + } finally { + Providers.stopJarVerification(obj); + } + + } + + private void processImpl(Hashtable signers, + List manifestDigests) + throws IOException, SignatureException, NoSuchAlgorithmException, + JarException, CertificateException + { + Manifest sf = new Manifest(); + sf.read(new ByteArrayInputStream(sfBytes)); + + String version = + sf.getMainAttributes().getValue(Attributes.Name.SIGNATURE_VERSION); + + if ((version == null) || !(version.equalsIgnoreCase("1.0"))) { + // XXX: should this be an exception? + // for now we just ignore this signature file + return; + } + + SignerInfo[] infos = block.verify(sfBytes); + + if (infos == null) { + throw new SecurityException("cannot verify signature block file " + + name); + } + + + CodeSigner[] newSigners = getSigners(infos, block); + + // make sure we have something to do all this work for... + if (newSigners == null) + return; + + Iterator> entries = + sf.getEntries().entrySet().iterator(); + + // see if we can verify the whole manifest first + boolean manifestSigned = verifyManifestHash(sf, md, manifestDigests); + + // verify manifest main attributes + if (!manifestSigned && !verifyManifestMainAttrs(sf, md)) { + throw new SecurityException + ("Invalid signature file digest for Manifest main attributes"); + } + + // go through each section in the signature file + while(entries.hasNext()) { + + Map.Entry e = entries.next(); + String name = e.getKey(); + + if (manifestSigned || + (verifySection(e.getValue(), name, md))) { + + if (name.startsWith("./")) + name = name.substring(2); + + if (name.startsWith("/")) + name = name.substring(1); + + updateSigners(newSigners, signers, name); + + if (debug != null) { + debug.println("processSignature signed name = "+name); + } + + } else if (debug != null) { + debug.println("processSignature unsigned name = "+name); + } + } + + // MANIFEST.MF is always regarded as signed + updateSigners(newSigners, signers, JarFile.MANIFEST_NAME); + } + + /** + * See if the whole manifest was signed. + */ + private boolean verifyManifestHash(Manifest sf, + ManifestDigester md, + List manifestDigests) + throws IOException + { + Attributes mattr = sf.getMainAttributes(); + boolean manifestSigned = false; + + // go through all the attributes and process *-Digest-Manifest entries + for (Map.Entry se : mattr.entrySet()) { + + String key = se.getKey().toString(); + + if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) { + // 16 is length of "-Digest-Manifest" + String algorithm = key.substring(0, key.length()-16); + + manifestDigests.add(key); + manifestDigests.add(se.getValue()); + MessageDigest digest = getDigest(algorithm); + if (digest != null) { + byte[] computedHash = md.manifestDigest(digest); + byte[] expectedHash = + Base64.getMimeDecoder().decode((String)se.getValue()); + + if (debug != null) { + debug.println("Signature File: Manifest digest " + + digest.getAlgorithm()); + debug.println( " sigfile " + toHex(expectedHash)); + debug.println( " computed " + toHex(computedHash)); + debug.println(); + } + + if (MessageDigest.isEqual(computedHash, + expectedHash)) { + manifestSigned = true; + } else { + //XXX: we will continue and verify each section + } + } + } + } + return manifestSigned; + } + + private boolean verifyManifestMainAttrs(Manifest sf, + ManifestDigester md) + throws IOException + { + Attributes mattr = sf.getMainAttributes(); + boolean attrsVerified = true; + + // go through all the attributes and process + // digest entries for the manifest main attributes + for (Map.Entry se : mattr.entrySet()) { + String key = se.getKey().toString(); + + if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) { + String algorithm = + key.substring(0, key.length() - ATTR_DIGEST.length()); + + MessageDigest digest = getDigest(algorithm); + if (digest != null) { + ManifestDigester.Entry mde = + md.get(ManifestDigester.MF_MAIN_ATTRS, false); + byte[] computedHash = mde.digest(digest); + byte[] expectedHash = + Base64.getMimeDecoder().decode((String)se.getValue()); + + if (debug != null) { + debug.println("Signature File: " + + "Manifest Main Attributes digest " + + digest.getAlgorithm()); + debug.println( " sigfile " + toHex(expectedHash)); + debug.println( " computed " + toHex(computedHash)); + debug.println(); + } + + if (MessageDigest.isEqual(computedHash, + expectedHash)) { + // good + } else { + // we will *not* continue and verify each section + attrsVerified = false; + if (debug != null) { + debug.println("Verification of " + + "Manifest main attributes failed"); + debug.println(); + } + break; + } + } + } + } + + // this method returns 'true' if either: + // . manifest main attributes were not signed, or + // . manifest main attributes were signed and verified + return attrsVerified; + } + + /** + * given the .SF digest header, and the data from the + * section in the manifest, see if the hashes match. + * if not, throw a SecurityException. + * + * @return true if all the -Digest headers verified + * @exception SecurityException if the hash was not equal + */ + + private boolean verifySection(Attributes sfAttr, + String name, + ManifestDigester md) + throws IOException + { + boolean oneDigestVerified = false; + ManifestDigester.Entry mde = md.get(name,block.isOldStyle()); + + if (mde == null) { + throw new SecurityException( + "no manifiest section for signature file entry "+name); + } + + if (sfAttr != null) { + + //sun.misc.HexDumpEncoder hex = new sun.misc.HexDumpEncoder(); + //hex.encodeBuffer(data, System.out); + + // go through all the attributes and process *-Digest entries + for (Map.Entry se : sfAttr.entrySet()) { + String key = se.getKey().toString(); + + if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) { + // 7 is length of "-Digest" + String algorithm = key.substring(0, key.length()-7); + + MessageDigest digest = getDigest(algorithm); + + if (digest != null) { + boolean ok = false; + + byte[] expected = + Base64.getMimeDecoder().decode((String)se.getValue()); + byte[] computed; + if (workaround) { + computed = mde.digestWorkaround(digest); + } else { + computed = mde.digest(digest); + } + + if (debug != null) { + debug.println("Signature Block File: " + + name + " digest=" + digest.getAlgorithm()); + debug.println(" expected " + toHex(expected)); + debug.println(" computed " + toHex(computed)); + debug.println(); + } + + if (MessageDigest.isEqual(computed, expected)) { + oneDigestVerified = true; + ok = true; + } else { + // attempt to fallback to the workaround + if (!workaround) { + computed = mde.digestWorkaround(digest); + if (MessageDigest.isEqual(computed, expected)) { + if (debug != null) { + debug.println(" re-computed " + toHex(computed)); + debug.println(); + } + workaround = true; + oneDigestVerified = true; + ok = true; + } + } + } + if (!ok){ + throw new SecurityException("invalid " + + digest.getAlgorithm() + + " signature file digest for " + name); + } + } + } + } + } + return oneDigestVerified; + } + + /** + * Given the PKCS7 block and SignerInfo[], create an array of + * CodeSigner objects. We do this only *once* for a given + * signature block file. + */ + private CodeSigner[] getSigners(SignerInfo infos[], PKCS7 block) + throws IOException, NoSuchAlgorithmException, SignatureException, + CertificateException { + + ArrayList signers = null; + + for (int i = 0; i < infos.length; i++) { + + SignerInfo info = infos[i]; + ArrayList chain = info.getCertificateChain(block); + CertPath certChain = certificateFactory.generateCertPath(chain); + if (signers == null) { + signers = new ArrayList(); + } + // Append the new code signer + signers.add(new CodeSigner(certChain, info.getTimestamp())); + + if (debug != null) { + debug.println("Signature Block Certificate: " + + chain.get(0)); + } + } + + if (signers != null) { + return signers.toArray(new CodeSigner[signers.size()]); + } else { + return null; + } + } + + // for the toHex function + private static final char[] hexc = + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + /** + * convert a byte array to a hex string for debugging purposes + * @param data the binary data to be converted to a hex string + * @return an ASCII hex string + */ + + static String toHex(byte[] data) { + + StringBuffer sb = new StringBuffer(data.length*2); + + for (int i=0; i>4) & 0x0f]); + sb.append(hexc[data[i] & 0x0f]); + } + return sb.toString(); + } + + // returns true if set contains signer + static boolean contains(CodeSigner[] set, CodeSigner signer) + { + for (int i = 0; i < set.length; i++) { + if (set[i].equals(signer)) + return true; + } + return false; + } + + // returns true if subset is a subset of set + static boolean isSubSet(CodeSigner[] subset, CodeSigner[] set) + { + // check for the same object + if (set == subset) + return true; + + boolean match; + for (int i = 0; i < subset.length; i++) { + if (!contains(set, subset[i])) + return false; + } + return true; + } + + /** + * returns true if signer contains exactly the same code signers as + * oldSigner and newSigner, false otherwise. oldSigner + * is allowed to be null. + */ + static boolean matches(CodeSigner[] signers, CodeSigner[] oldSigners, + CodeSigner[] newSigners) { + + // special case + if ((oldSigners == null) && (signers == newSigners)) + return true; + + boolean match; + + // make sure all oldSigners are in signers + if ((oldSigners != null) && !isSubSet(oldSigners, signers)) + return false; + + // make sure all newSigners are in signers + if (!isSubSet(newSigners, signers)) { + return false; + } + + // now make sure all the code signers in signers are + // also in oldSigners or newSigners + + for (int i = 0; i < signers.length; i++) { + boolean found = + ((oldSigners != null) && contains(oldSigners, signers[i])) || + contains(newSigners, signers[i]); + if (!found) + return false; + } + return true; + } + + void updateSigners(CodeSigner[] newSigners, + Hashtable signers, String name) { + + CodeSigner[] oldSigners = signers.get(name); + + // search through the cache for a match, go in reverse order + // as we are more likely to find a match with the last one + // added to the cache + + CodeSigner[] cachedSigners; + for (int i = signerCache.size() - 1; i != -1; i--) { + cachedSigners = signerCache.get(i); + if (matches(cachedSigners, oldSigners, newSigners)) { + signers.put(name, cachedSigners); + return; + } + } + + if (oldSigners == null) { + cachedSigners = newSigners; + } else { + cachedSigners = + new CodeSigner[oldSigners.length + newSigners.length]; + System.arraycopy(oldSigners, 0, cachedSigners, 0, + oldSigners.length); + System.arraycopy(newSigners, 0, cachedSigners, oldSigners.length, + newSigners.length); + } + signerCache.add(cachedSigners); + signers.put(name, cachedSigners); + } +} diff --git a/src/sun/security/util/SignatureUtil.java b/src/sun/security/util/SignatureUtil.java new file mode 100644 index 00000000..85cf55ad --- /dev/null +++ b/src/sun/security/util/SignatureUtil.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.util; + +import java.io.IOException; +import java.security.*; +import java.security.spec.*; +import java.util.Locale; +import sun.security.rsa.RSAUtil; +import sun.misc.SharedSecrets; + +/** + * Utility class for Signature related operations. Currently used by various + * internal PKI classes such as sun.security.x509.X509CertImpl, + * sun.security.pkcs.SignerInfo, for setting signature parameters. + * + * @since 8 + */ +public class SignatureUtil { + + private static String checkName(String algName) throws ProviderException { + if (algName.indexOf(".") == -1) { + return algName; + } + // convert oid to String + try { + return Signature.getInstance(algName).getAlgorithm(); + } catch (Exception e) { + throw new ProviderException("Error mapping algorithm name", e); + } + } + + // Utility method of creating an AlgorithmParameters object with + // the specified algorithm name and encoding + private static AlgorithmParameters createAlgorithmParameters(String algName, + byte[] paramBytes) throws ProviderException { + + try { + algName = checkName(algName); + AlgorithmParameters result = + AlgorithmParameters.getInstance(algName); + result.init(paramBytes); + return result; + } catch (NoSuchAlgorithmException | IOException e) { + throw new ProviderException(e); + } + } + + // Utility method for converting the specified AlgorithmParameters object + // into an AlgorithmParameterSpec object. + public static AlgorithmParameterSpec getParamSpec(String sigName, + AlgorithmParameters params) + throws ProviderException { + + sigName = checkName(sigName).toUpperCase(Locale.ENGLISH); + AlgorithmParameterSpec paramSpec = null; + if (params != null) { + // AlgorithmParameters.getAlgorithm() may returns oid if it's + // created during DER decoding. Convert to use the standard name + // before passing it to RSAUtil + if (params.getAlgorithm().indexOf(".") != -1) { + try { + params = createAlgorithmParameters(sigName, + params.getEncoded()); + } catch (IOException e) { + throw new ProviderException(e); + } + } + + if (sigName.indexOf("RSA") != -1) { + paramSpec = RSAUtil.getParamSpec(params); + } else if (sigName.indexOf("ECDSA") != -1) { + try { + paramSpec = params.getParameterSpec(ECParameterSpec.class); + } catch (Exception e) { + throw new ProviderException("Error handling EC parameters", e); + } + } else { + throw new ProviderException + ("Unrecognized algorithm for signature parameters " + + sigName); + } + } + return paramSpec; + } + + // Utility method for converting the specified parameter bytes into an + // AlgorithmParameterSpec object. + public static AlgorithmParameterSpec getParamSpec(String sigName, + byte[] paramBytes) + throws ProviderException { + sigName = checkName(sigName).toUpperCase(Locale.ENGLISH); + AlgorithmParameterSpec paramSpec = null; + + if (paramBytes != null) { + if (sigName.indexOf("RSA") != -1) { + AlgorithmParameters params = + createAlgorithmParameters(sigName, paramBytes); + paramSpec = RSAUtil.getParamSpec(params); + } else if (sigName.indexOf("ECDSA") != -1) { + try { + Provider p = Signature.getInstance(sigName).getProvider(); + paramSpec = ECUtil.getECParameterSpec(p, paramBytes); + } catch (Exception e) { + throw new ProviderException("Error handling EC parameters", e); + } + // ECUtil discards exception and returns null, so we need to check + // the returned value + if (paramSpec == null) { + throw new ProviderException("Error handling EC parameters"); + } + } else { + throw new ProviderException + ("Unrecognized algorithm for signature parameters " + + sigName); + } + } + return paramSpec; + } + + // Utility method for initializing the specified Signature object + // for verification with the specified key and params (may be null) + public static void initVerifyWithParam(Signature s, PublicKey key, + AlgorithmParameterSpec params) + throws ProviderException, InvalidAlgorithmParameterException, + InvalidKeyException { + SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, key, params); + } + + // Utility method for initializing the specified Signature object + // for verification with the specified Certificate and params (may be null) + public static void initVerifyWithParam(Signature s, + java.security.cert.Certificate cert, + AlgorithmParameterSpec params) + throws ProviderException, InvalidAlgorithmParameterException, + InvalidKeyException { + SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, cert, params); + } + + // Utility method for initializing the specified Signature object + // for signing with the specified key and params (may be null) + public static void initSignWithParam(Signature s, PrivateKey key, + AlgorithmParameterSpec params, SecureRandom sr) + throws ProviderException, InvalidAlgorithmParameterException, + InvalidKeyException { + SharedSecrets.getJavaSecuritySignatureAccess().initSign(s, key, params, sr); + } +} diff --git a/src/sun/security/util/UntrustedCertificates.java b/src/sun/security/util/UntrustedCertificates.java new file mode 100644 index 00000000..3ccc48d8 --- /dev/null +++ b/src/sun/security/util/UntrustedCertificates.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.util; + +import java.io.*; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; +import java.util.*; +import sun.security.x509.X509CertImpl; + +/** + * A utility class to check if a certificate is untrusted. This is an internal + * mechanism that explicitly marks a certificate as untrusted, normally in the + * case that a certificate is known to be used for malicious reasons. + * + * Attention: This check is NOT meant to replace the standard PKI-defined + * validation check, neither is it used as an alternative to CRL. + */ +public final class UntrustedCertificates { + + private static final Debug debug = Debug.getInstance("certpath"); + private static final String ALGORITHM_KEY = "Algorithm"; + + private static final Properties props = new Properties(); + private static final String algorithm; + + static { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + File f = new File(System.getProperty("java.home"), + "lib/security/blacklisted.certs"); + try (FileInputStream fin = new FileInputStream(f)) { + props.load(fin); + // It's said that the fingerprint could contain colons + for (Map.Entry e: props.entrySet()) { + e.setValue(stripColons(e.getValue())); + } + } catch (IOException fnfe) { + if (debug != null) { + debug.println("Error parsing blacklisted.certs"); + } + } + return null; + } + }); + algorithm = props.getProperty(ALGORITHM_KEY); + } + + private static String stripColons(Object input) { + String s = (String)input; + char[] letters = s.toCharArray(); + int pos = 0; + for (int i = 0; i < letters.length; i++) { + if (letters[i] != ':') { + if (i != pos) { + letters[pos] = letters[i]; + } + pos++; + } + } + if (pos == letters.length) return s; + else return new String(letters, 0, pos); + } + /** + * Checks if a certificate is untrusted. + * + * @param cert the certificate to check + * @return true if the certificate is untrusted. + */ + public static boolean isUntrusted(X509Certificate cert) { + if (algorithm == null) { + return false; + } + String key; + if (cert instanceof X509CertImpl) { + key = ((X509CertImpl)cert).getFingerprint(algorithm); + } else { + try { + key = new X509CertImpl(cert.getEncoded()).getFingerprint(algorithm); + } catch (CertificateException cee) { + return false; + } + } + return props.containsKey(key); + } + + private UntrustedCertificates() {} +} diff --git a/src/sun/security/validator/EndEntityChecker.java b/src/sun/security/validator/EndEntityChecker.java new file mode 100644 index 00000000..4ca79f08 --- /dev/null +++ b/src/sun/security/validator/EndEntityChecker.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.util.*; + +import java.security.cert.*; + +import sun.security.x509.NetscapeCertTypeExtension; + +/** + * Class to check if an end entity cert is suitable for use in some + * context.

    + * + * This class is used internally by the validator. Currently, seven variants + * are supported defined as VAR_XXX constants in the Validator class: + *

      + *
    • Generic. No additional requirements, all certificates are ok. + * + *
    • TLS server. Requires that a String parameter is passed to + * validate that specifies the name of the TLS key exchange algorithm + * in use. See the JSSE X509TrustManager spec for details. + * + *
    • TLS client. + * + *
    • Code signing. + * + *
    • JCE code signing. Some early JCE code signing certs issued to + * providers had incorrect extensions. In this mode the checks + * are relaxed compared to standard code signing checks in order to + * allow these certificates to pass. + * + *
    • Plugin code signing. WebStart and Plugin require their own variant + * which is equivalent to VAR_CODE_SIGNING with additional checks for + * compatibility/special cases. See also PKIXValidator. + * + *
    • TSA Server (see RFC 3161, section 2.3). + * + *
    + * + * @author Andreas Sterbenz + */ +class EndEntityChecker { + + // extended key usage OIDs for TLS server, TLS client, code signing + // and any usage + + private final static String OID_EXTENDED_KEY_USAGE = + SimpleValidator.OID_EXTENDED_KEY_USAGE; + + private final static String OID_EKU_TLS_SERVER = "1.3.6.1.5.5.7.3.1"; + + private final static String OID_EKU_TLS_CLIENT = "1.3.6.1.5.5.7.3.2"; + + private final static String OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3"; + + private final static String OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8"; + + private final static String OID_EKU_ANY_USAGE = "2.5.29.37.0"; + + // the Netscape Server-Gated-Cryptography EKU extension OID + private final static String OID_EKU_NS_SGC = "2.16.840.1.113730.4.1"; + + // the Microsoft Server-Gated-Cryptography EKU extension OID + private final static String OID_EKU_MS_SGC = "1.3.6.1.4.1.311.10.3.3"; + + // the recognized extension OIDs + private final static String OID_SUBJECT_ALT_NAME = "2.5.29.17"; + + private final static String NSCT_SSL_CLIENT = + NetscapeCertTypeExtension.SSL_CLIENT; + + private final static String NSCT_SSL_SERVER = + NetscapeCertTypeExtension.SSL_SERVER; + + private final static String NSCT_CODE_SIGNING = + NetscapeCertTypeExtension.OBJECT_SIGNING; + + // bit numbers in the key usage extension + private final static int KU_SIGNATURE = 0; + private final static int KU_KEY_ENCIPHERMENT = 2; + private final static int KU_KEY_AGREEMENT = 4; + + // TLS key exchange algorithms requiring digitalSignature key usage + private final static Collection KU_SERVER_SIGNATURE = + Arrays.asList("DHE_DSS", "DHE_RSA", "ECDHE_ECDSA", "ECDHE_RSA", + "RSA_EXPORT", "UNKNOWN"); + + // TLS key exchange algorithms requiring keyEncipherment key usage + private final static Collection KU_SERVER_ENCRYPTION = + Arrays.asList("RSA"); + + // TLS key exchange algorithms requiring keyAgreement key usage + private final static Collection KU_SERVER_KEY_AGREEMENT = + Arrays.asList("DH_DSS", "DH_RSA", "ECDH_ECDSA", "ECDH_RSA"); + + // variant of this end entity cert checker + private final String variant; + + // type of the validator this checker belongs to + private final String type; + + private EndEntityChecker(String type, String variant) { + this.type = type; + this.variant = variant; + } + + static EndEntityChecker getInstance(String type, String variant) { + return new EndEntityChecker(type, variant); + } + + void check(X509Certificate cert, Object parameter) + throws CertificateException { + if (variant.equals(Validator.VAR_GENERIC)) { + // no checks + return; + } else if (variant.equals(Validator.VAR_TLS_SERVER)) { + checkTLSServer(cert, (String)parameter); + } else if (variant.equals(Validator.VAR_TLS_CLIENT)) { + checkTLSClient(cert); + } else if (variant.equals(Validator.VAR_CODE_SIGNING)) { + checkCodeSigning(cert); + } else if (variant.equals(Validator.VAR_JCE_SIGNING)) { + checkCodeSigning(cert); + } else if (variant.equals(Validator.VAR_PLUGIN_CODE_SIGNING)) { + checkCodeSigning(cert); + } else if (variant.equals(Validator.VAR_TSA_SERVER)) { + checkTSAServer(cert); + } else { + throw new CertificateException("Unknown variant: " + variant); + } + } + + /** + * Utility method returning the Set of critical extensions for + * certificate cert (never null). + */ + private Set getCriticalExtensions(X509Certificate cert) { + Set exts = cert.getCriticalExtensionOIDs(); + if (exts == null) { + exts = Collections.emptySet(); + } + return exts; + } + + /** + * Utility method checking if there are any unresolved critical extensions. + * @throws CertificateException if so. + */ + private void checkRemainingExtensions(Set exts) + throws CertificateException { + // basic constraints irrelevant in EE certs + exts.remove(SimpleValidator.OID_BASIC_CONSTRAINTS); + + // If the subject field contains an empty sequence, the subjectAltName + // extension MUST be marked critical. + // We do not check the validity of the critical extension, just mark + // it recognizable here. + exts.remove(OID_SUBJECT_ALT_NAME); + + if (!exts.isEmpty()) { + throw new CertificateException("Certificate contains unsupported " + + "critical extensions: " + exts); + } + } + + /** + * Utility method checking if the extended key usage extension in + * certificate cert allows use for expectedEKU. + */ + private boolean checkEKU(X509Certificate cert, Set exts, + String expectedEKU) throws CertificateException { + List eku = cert.getExtendedKeyUsage(); + if (eku == null) { + return true; + } + return eku.contains(expectedEKU) || eku.contains(OID_EKU_ANY_USAGE); + } + + /** + * Utility method checking if bit 'bit' is set in this certificates + * key usage extension. + * @throws CertificateException if not + */ + private boolean checkKeyUsage(X509Certificate cert, int bit) + throws CertificateException { + boolean[] keyUsage = cert.getKeyUsage(); + if (keyUsage == null) { + return true; + } + return (keyUsage.length > bit) && keyUsage[bit]; + } + + /** + * Check whether this certificate can be used for TLS client + * authentication. + * @throws CertificateException if not. + */ + private void checkTLSClient(X509Certificate cert) + throws CertificateException { + Set exts = getCriticalExtensions(cert); + + if (checkKeyUsage(cert, KU_SIGNATURE) == false) { + throw new ValidatorException + ("KeyUsage does not allow digital signatures", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + if (checkEKU(cert, exts, OID_EKU_TLS_CLIENT) == false) { + throw new ValidatorException("Extended key usage does not " + + "permit use for TLS client authentication", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_CLIENT)) { + throw new ValidatorException + ("Netscape cert type does not permit use for SSL client", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + // remove extensions we checked + exts.remove(SimpleValidator.OID_KEY_USAGE); + exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); + exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); + + checkRemainingExtensions(exts); + } + + /** + * Check whether this certificate can be used for TLS server authentication + * using the specified authentication type parameter. See X509TrustManager + * specification for details. + * @throws CertificateException if not. + */ + private void checkTLSServer(X509Certificate cert, String parameter) + throws CertificateException { + Set exts = getCriticalExtensions(cert); + + if (KU_SERVER_ENCRYPTION.contains(parameter)) { + if (checkKeyUsage(cert, KU_KEY_ENCIPHERMENT) == false) { + throw new ValidatorException + ("KeyUsage does not allow key encipherment", + ValidatorException.T_EE_EXTENSIONS, cert); + } + } else if (KU_SERVER_SIGNATURE.contains(parameter)) { + if (checkKeyUsage(cert, KU_SIGNATURE) == false) { + throw new ValidatorException + ("KeyUsage does not allow digital signatures", + ValidatorException.T_EE_EXTENSIONS, cert); + } + } else if (KU_SERVER_KEY_AGREEMENT.contains(parameter)) { + if (checkKeyUsage(cert, KU_KEY_AGREEMENT) == false) { + throw new ValidatorException + ("KeyUsage does not allow key agreement", + ValidatorException.T_EE_EXTENSIONS, cert); + } + } else { + throw new CertificateException("Unknown authType: " + parameter); + } + + if (checkEKU(cert, exts, OID_EKU_TLS_SERVER) == false) { + // check for equivalent but now obsolete Server-Gated-Cryptography + // (aka Step-Up, 128 bit) EKU OIDs + if ((checkEKU(cert, exts, OID_EKU_MS_SGC) == false) && + (checkEKU(cert, exts, OID_EKU_NS_SGC) == false)) { + throw new ValidatorException + ("Extended key usage does not permit use for TLS " + + "server authentication", + ValidatorException.T_EE_EXTENSIONS, cert); + } + } + + if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_SSL_SERVER)) { + throw new ValidatorException + ("Netscape cert type does not permit use for SSL server", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + // remove extensions we checked + exts.remove(SimpleValidator.OID_KEY_USAGE); + exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); + exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); + + checkRemainingExtensions(exts); + } + + /** + * Check whether this certificate can be used for code signing. + * @throws CertificateException if not. + */ + private void checkCodeSigning(X509Certificate cert) + throws CertificateException { + Set exts = getCriticalExtensions(cert); + + if (checkKeyUsage(cert, KU_SIGNATURE) == false) { + throw new ValidatorException + ("KeyUsage does not allow digital signatures", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + if (checkEKU(cert, exts, OID_EKU_CODE_SIGNING) == false) { + throw new ValidatorException + ("Extended key usage does not permit use for code signing", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + // do not check Netscape cert type for JCE code signing checks + // (some certs were issued with incorrect extensions) + if (variant.equals(Validator.VAR_JCE_SIGNING) == false) { + if (!SimpleValidator.getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING)) { + throw new ValidatorException + ("Netscape cert type does not permit use for code signing", + ValidatorException.T_EE_EXTENSIONS, cert); + } + exts.remove(SimpleValidator.OID_NETSCAPE_CERT_TYPE); + } + + // remove extensions we checked + exts.remove(SimpleValidator.OID_KEY_USAGE); + exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); + + checkRemainingExtensions(exts); + } + + /** + * Check whether this certificate can be used by a time stamping authority + * server (see RFC 3161, section 2.3). + * @throws CertificateException if not. + */ + private void checkTSAServer(X509Certificate cert) + throws CertificateException { + Set exts = getCriticalExtensions(cert); + + if (checkKeyUsage(cert, KU_SIGNATURE) == false) { + throw new ValidatorException + ("KeyUsage does not allow digital signatures", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + if (cert.getExtendedKeyUsage() == null) { + throw new ValidatorException + ("Certificate does not contain an extended key usage " + + "extension required for a TSA server", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + if (checkEKU(cert, exts, OID_EKU_TIME_STAMPING) == false) { + throw new ValidatorException + ("Extended key usage does not permit use for TSA server", + ValidatorException.T_EE_EXTENSIONS, cert); + } + + // remove extensions we checked + exts.remove(SimpleValidator.OID_KEY_USAGE); + exts.remove(SimpleValidator.OID_EXTENDED_KEY_USAGE); + + checkRemainingExtensions(exts); + } +} diff --git a/src/sun/security/validator/KeyStores.java b/src/sun/security/validator/KeyStores.java new file mode 100644 index 00000000..71c3dce7 --- /dev/null +++ b/src/sun/security/validator/KeyStores.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.util.*; + +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; + +/** + * Collection of static utility methods related to KeyStores. + * + * @author Andreas Sterbenz + */ +public class KeyStores { + + private KeyStores() { + // empty + } + + // in the future, all accesses to the system cacerts keystore should + // go through this class. but not right now. +/* + private final static String javaHome = + (String)AccessController.doPrivileged(new GetPropertyAction("java.home")); + + private final static char SEP = File.separatorChar; + + private static KeyStore caCerts; + + private static KeyStore getKeyStore(String type, String name, + char[] password) throws IOException { + if (type == null) { + type = "JKS"; + } + try { + KeyStore ks = KeyStore.getInstance(type); + FileInputStream in = (FileInputStream)AccessController.doPrivileged + (new OpenFileInputStreamAction(name)); + ks.load(in, password); + return ks; + } catch (GeneralSecurityException e) { + // XXX + throw new IOException(); + } catch (PrivilegedActionException e) { + throw (IOException)e.getCause(); + } + } + + /** + * Return a KeyStore with the contents of the lib/security/cacerts file. + * The file is only opened once per JVM invocation and the contents + * cached subsequently. + * + public synchronized static KeyStore getCaCerts() throws IOException { + if (caCerts != null) { + return caCerts; + } + String name = javaHome + SEP + "lib" + SEP + "security" + SEP + "cacerts"; + caCerts = getKeyStore(null, name, null); + return caCerts; + } +*/ + + /** + * Return a Set with all trusted X509Certificates contained in + * this KeyStore. + */ + public static Set getTrustedCerts(KeyStore ks) { + Set set = new HashSet(); + try { + for (Enumeration e = ks.aliases(); e.hasMoreElements(); ) { + String alias = e.nextElement(); + if (ks.isCertificateEntry(alias)) { + Certificate cert = ks.getCertificate(alias); + if (cert instanceof X509Certificate) { + set.add((X509Certificate)cert); + } + } else if (ks.isKeyEntry(alias)) { + Certificate[] certs = ks.getCertificateChain(alias); + if ((certs != null) && (certs.length > 0) && + (certs[0] instanceof X509Certificate)) { + set.add((X509Certificate)certs[0]); + } + } + } + } catch (KeyStoreException e) { + // ignore + } + return set; + } + +} diff --git a/src/sun/security/validator/PKIXValidator.java b/src/sun/security/validator/PKIXValidator.java new file mode 100644 index 00000000..51761e1b --- /dev/null +++ b/src/sun/security/validator/PKIXValidator.java @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.util.*; + +import java.security.*; +import java.security.cert.*; + +import javax.security.auth.x500.X500Principal; +import sun.security.action.GetBooleanAction; +import sun.security.provider.certpath.AlgorithmChecker; + +/** + * Validator implementation built on the PKIX CertPath API. This + * implementation will be emphasized going forward.

    + *

    + * Note that the validate() implementation tries to use a PKIX validator + * if that appears possible and a PKIX builder otherwise. This increases + * performance and currently also leads to better exception messages + * in case of failures. + *

    + * {@code PKIXValidator} objects are immutable once they have been created. + * Please DO NOT add methods that can change the state of an instance once + * it has been created. + * + * @author Andreas Sterbenz + */ +public final class PKIXValidator extends Validator { + + /** + * Flag indicating whether to enable revocation check for the PKIX trust + * manager. Typically, this will only work if the PKIX implementation + * supports CRL distribution points as we do not manually setup CertStores. + */ + private final static boolean checkTLSRevocation = + AccessController.doPrivileged + (new GetBooleanAction("com.sun.net.ssl.checkRevocation")); + + // enable use of the validator if possible + private final static boolean TRY_VALIDATOR = true; + + private final Set trustedCerts; + private final PKIXBuilderParameters parameterTemplate; + private int certPathLength = -1; + + // needed only for the validator + private final Map> trustedSubjects; + private final CertificateFactory factory; + + private final boolean plugin; + + PKIXValidator(String variant, Collection trustedCerts) { + super(TYPE_PKIX, variant); + if (trustedCerts instanceof Set) { + this.trustedCerts = (Set)trustedCerts; + } else { + this.trustedCerts = new HashSet(trustedCerts); + } + Set trustAnchors = new HashSet(); + for (X509Certificate cert : trustedCerts) { + trustAnchors.add(new TrustAnchor(cert, null)); + } + try { + parameterTemplate = new PKIXBuilderParameters(trustAnchors, null); + } catch (InvalidAlgorithmParameterException e) { + throw new RuntimeException("Unexpected error: " + e.toString(), e); + } + setDefaultParameters(variant); + + // initCommon(); + if (TRY_VALIDATOR) { + if (TRY_VALIDATOR == false) { + return; + } + trustedSubjects = new HashMap>(); + for (X509Certificate cert : trustedCerts) { + X500Principal dn = cert.getSubjectX500Principal(); + List keys; + if (trustedSubjects.containsKey(dn)) { + keys = trustedSubjects.get(dn); + } else { + keys = new ArrayList(); + trustedSubjects.put(dn, keys); + } + keys.add(cert.getPublicKey()); + } + try { + factory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Internal error", e); + } + plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); + } else { + plugin = false; + } + } + + PKIXValidator(String variant, PKIXBuilderParameters params) { + super(TYPE_PKIX, variant); + trustedCerts = new HashSet(); + for (TrustAnchor anchor : params.getTrustAnchors()) { + X509Certificate cert = anchor.getTrustedCert(); + if (cert != null) { + trustedCerts.add(cert); + } + } + parameterTemplate = params; + + // initCommon(); + if (TRY_VALIDATOR) { + if (TRY_VALIDATOR == false) { + return; + } + trustedSubjects = new HashMap>(); + for (X509Certificate cert : trustedCerts) { + X500Principal dn = cert.getSubjectX500Principal(); + List keys; + if (trustedSubjects.containsKey(dn)) { + keys = trustedSubjects.get(dn); + } else { + keys = new ArrayList(); + trustedSubjects.put(dn, keys); + } + keys.add(cert.getPublicKey()); + } + try { + factory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Internal error", e); + } + plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); + } else { + plugin = false; + } + } + + public Collection getTrustedCertificates() { + return trustedCerts; + } + + /** + * Returns the length of the last certification path that is validated by + * CertPathValidator. This is intended primarily as a callback mechanism + * for PKIXCertPathCheckers to determine the length of the certification + * path that is being validated. It is necessary since engineValidate() + * may modify the length of the path. + * + * @return the length of the last certification path passed to + * CertPathValidator.validate, or -1 if it has not been invoked yet + */ + public int getCertPathLength() { // mutable, should be private + return certPathLength; + } + + /** + * Set J2SE global default PKIX parameters. Currently, hardcoded to disable + * revocation checking. In the future, this should be configurable. + */ + private void setDefaultParameters(String variant) { + if ((variant == Validator.VAR_TLS_SERVER) || + (variant == Validator.VAR_TLS_CLIENT)) { + parameterTemplate.setRevocationEnabled(checkTLSRevocation); + } else { + parameterTemplate.setRevocationEnabled(false); + } + } + + /** + * Return the PKIX parameters used by this instance. An application may + * modify the parameters but must make sure not to perform any concurrent + * validations. + */ + public PKIXBuilderParameters getParameters() { // mutable, should be private + return parameterTemplate; + } + + @Override + X509Certificate[] engineValidate(X509Certificate[] chain, + Collection otherCerts, + AlgorithmConstraints constraints, + Object parameter) throws CertificateException { + if ((chain == null) || (chain.length == 0)) { + throw new CertificateException + ("null or zero-length certificate chain"); + } + + // add new algorithm constraints checker + PKIXBuilderParameters pkixParameters = + (PKIXBuilderParameters) parameterTemplate.clone(); + AlgorithmChecker algorithmChecker = null; + if (constraints != null) { + algorithmChecker = new AlgorithmChecker(constraints); + pkixParameters.addCertPathChecker(algorithmChecker); + } + + if (TRY_VALIDATOR) { + // check that chain is in correct order and check if chain contains + // trust anchor + X500Principal prevIssuer = null; + for (int i = 0; i < chain.length; i++) { + X509Certificate cert = chain[i]; + X500Principal dn = cert.getSubjectX500Principal(); + if (i != 0 && + !dn.equals(prevIssuer)) { + // chain is not ordered correctly, call builder instead + return doBuild(chain, otherCerts, pkixParameters); + } + + // Check if chain[i] is already trusted. It may be inside + // trustedCerts, or has the same dn and public key as a cert + // inside trustedCerts. The latter happens when a CA has + // updated its cert with a stronger signature algorithm in JRE + // but the weak one is still in circulation. + + if (trustedCerts.contains(cert) || // trusted cert + (trustedSubjects.containsKey(dn) && // replacing ... + trustedSubjects.get(dn).contains( // ... weak cert + cert.getPublicKey()))) { + if (i == 0) { + return new X509Certificate[] {chain[0]}; + } + // Remove and call validator on partial chain [0 .. i-1] + X509Certificate[] newChain = new X509Certificate[i]; + System.arraycopy(chain, 0, newChain, 0, i); + return doValidate(newChain, pkixParameters); + } + prevIssuer = cert.getIssuerX500Principal(); + } + + // apparently issued by trust anchor? + X509Certificate last = chain[chain.length - 1]; + X500Principal issuer = last.getIssuerX500Principal(); + X500Principal subject = last.getSubjectX500Principal(); + if (trustedSubjects.containsKey(issuer) && + isSignatureValid(trustedSubjects.get(issuer), last)) { + return doValidate(chain, pkixParameters); + } + + // don't fallback to builder if called from plugin/webstart + if (plugin) { + // Validate chain even if no trust anchor is found. This + // allows plugin/webstart to make sure the chain is + // otherwise valid + if (chain.length > 1) { + X509Certificate[] newChain = + new X509Certificate[chain.length-1]; + System.arraycopy(chain, 0, newChain, 0, newChain.length); + + // temporarily set last cert as sole trust anchor + try { + pkixParameters.setTrustAnchors + (Collections.singleton(new TrustAnchor + (chain[chain.length-1], null))); + } catch (InvalidAlgorithmParameterException iape) { + // should never occur, but ... + throw new CertificateException(iape); + } + doValidate(newChain, pkixParameters); + } + // if the rest of the chain is valid, throw exception + // indicating no trust anchor was found + throw new ValidatorException + (ValidatorException.T_NO_TRUST_ANCHOR); + } + // otherwise, fall back to builder + } + + return doBuild(chain, otherCerts, pkixParameters); + } + + private boolean isSignatureValid(List keys, + X509Certificate sub) { + if (plugin) { + for (PublicKey key: keys) { + try { + sub.verify(key); + return true; + } catch (Exception ex) { + continue; + } + } + return false; + } + return true; // only check if PLUGIN is set + } + + private static X509Certificate[] toArray(CertPath path, TrustAnchor anchor) + throws CertificateException { + List list = + path.getCertificates(); + X509Certificate[] chain = new X509Certificate[list.size() + 1]; + list.toArray(chain); + X509Certificate trustedCert = anchor.getTrustedCert(); + if (trustedCert == null) { + throw new ValidatorException + ("TrustAnchor must be specified as certificate"); + } + chain[chain.length - 1] = trustedCert; + return chain; + } + + /** + * Set the check date (for debugging). + */ + private void setDate(PKIXBuilderParameters params) { + @SuppressWarnings("deprecation") + Date date = validationDate; + if (date != null) { + params.setDate(date); + } + } + + private X509Certificate[] doValidate(X509Certificate[] chain, + PKIXBuilderParameters params) throws CertificateException { + try { + setDate(params); + + // do the validation + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + CertPath path = factory.generateCertPath(Arrays.asList(chain)); + certPathLength = chain.length; + PKIXCertPathValidatorResult result = + (PKIXCertPathValidatorResult)validator.validate(path, params); + + return toArray(path, result.getTrustAnchor()); + } catch (GeneralSecurityException e) { + throw new ValidatorException + ("PKIX path validation failed: " + e.toString(), e); + } + } + + private X509Certificate[] doBuild(X509Certificate[] chain, + Collection otherCerts, + PKIXBuilderParameters params) throws CertificateException { + + try { + setDate(params); + + // setup target constraints + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(chain[0]); + params.setTargetCertConstraints(selector); + + // setup CertStores + Collection certs = + new ArrayList(); + certs.addAll(Arrays.asList(chain)); + if (otherCerts != null) { + certs.addAll(otherCerts); + } + CertStore store = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(certs)); + params.addCertStore(store); + + // do the build + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); + PKIXCertPathBuilderResult result = + (PKIXCertPathBuilderResult)builder.build(params); + + return toArray(result.getCertPath(), result.getTrustAnchor()); + } catch (GeneralSecurityException e) { + throw new ValidatorException + ("PKIX path building failed: " + e.toString(), e); + } + } +} diff --git a/src/sun/security/validator/SimpleValidator.java b/src/sun/security/validator/SimpleValidator.java new file mode 100644 index 00000000..a0f4f8b9 --- /dev/null +++ b/src/sun/security/validator/SimpleValidator.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.io.IOException; +import java.util.*; + +import java.security.*; +import java.security.cert.*; + +import javax.security.auth.x500.X500Principal; + +import sun.security.x509.X509CertImpl; +import sun.security.x509.NetscapeCertTypeExtension; +import sun.security.util.DerValue; +import sun.security.util.DerInputStream; +import sun.security.util.ObjectIdentifier; + +import sun.security.provider.certpath.AlgorithmChecker; +import sun.security.provider.certpath.UntrustedChecker; + +/** + * A simple validator implementation. It is based on code from the JSSE + * X509TrustManagerImpl. This implementation is designed for compatibility with + * deployed certificates and previous J2SE versions. It will never support + * more advanced features and will be deemphasized in favor of the PKIX + * validator going forward. + *

    + * {@code SimpleValidator} objects are immutable once they have been created. + * Please DO NOT add methods that can change the state of an instance once + * it has been created. + * + * @author Andreas Sterbenz + */ +public final class SimpleValidator extends Validator { + + // Constants for the OIDs we need + + final static String OID_BASIC_CONSTRAINTS = "2.5.29.19"; + + final static String OID_NETSCAPE_CERT_TYPE = "2.16.840.1.113730.1.1"; + + final static String OID_KEY_USAGE = "2.5.29.15"; + + final static String OID_EXTENDED_KEY_USAGE = "2.5.29.37"; + + final static String OID_EKU_ANY_USAGE = "2.5.29.37.0"; + + final static ObjectIdentifier OBJID_NETSCAPE_CERT_TYPE = + NetscapeCertTypeExtension.NetscapeCertType_Id; + + private final static String NSCT_SSL_CA = + NetscapeCertTypeExtension.SSL_CA; + + private final static String NSCT_CODE_SIGNING_CA = + NetscapeCertTypeExtension.OBJECT_SIGNING_CA; + + /** + * The trusted certificates as: + * Map (X500Principal)subject of trusted cert -> List of X509Certificate + * The list is used because there may be multiple certificates + * with an identical subject DN. + */ + private final Map> + trustedX500Principals; + + /** + * Set of the trusted certificates. Present only for + * getTrustedCertificates(). + */ + private final Collection trustedCerts; + + SimpleValidator(String variant, Collection trustedCerts) { + super(TYPE_SIMPLE, variant); + this.trustedCerts = trustedCerts; + trustedX500Principals = + new HashMap>(); + for (X509Certificate cert : trustedCerts) { + X500Principal principal = cert.getSubjectX500Principal(); + List list = trustedX500Principals.get(principal); + if (list == null) { + // this actually should be a set, but duplicate entries + // are not a problem and we can avoid the Set overhead + list = new ArrayList(2); + trustedX500Principals.put(principal, list); + } + list.add(cert); + } + } + + public Collection getTrustedCertificates() { + return trustedCerts; + } + + /** + * Perform simple validation of chain. The arguments otherCerts and + * parameter are ignored. + */ + @Override + X509Certificate[] engineValidate(X509Certificate[] chain, + Collection otherCerts, + AlgorithmConstraints constraints, + Object parameter) throws CertificateException { + if ((chain == null) || (chain.length == 0)) { + throw new CertificateException + ("null or zero-length certificate chain"); + } + + // make sure chain includes a trusted cert + chain = buildTrustedChain(chain); + + @SuppressWarnings("deprecation") + Date date = validationDate; + if (date == null) { + date = new Date(); + } + + // create distrusted certificates checker + UntrustedChecker untrustedChecker = new UntrustedChecker(); + + // create default algorithm constraints checker + TrustAnchor anchor = new TrustAnchor(chain[chain.length - 1], null); + AlgorithmChecker defaultAlgChecker = new AlgorithmChecker(anchor); + + // create application level algorithm constraints checker + AlgorithmChecker appAlgChecker = null; + if (constraints != null) { + appAlgChecker = new AlgorithmChecker(anchor, constraints); + } + + // verify top down, starting at the certificate issued by + // the trust anchor + int maxPathLength = chain.length - 1; + for (int i = chain.length - 2; i >= 0; i--) { + X509Certificate issuerCert = chain[i + 1]; + X509Certificate cert = chain[i]; + + // check untrusted certificate + try { + // Untrusted checker does not care about the unresolved + // critical extensions. + untrustedChecker.check(cert, Collections.emptySet()); + } catch (CertPathValidatorException cpve) { + throw new ValidatorException( + "Untrusted certificate: " + cert.getSubjectX500Principal(), + ValidatorException.T_UNTRUSTED_CERT, cert, cpve); + } + + // check certificate algorithm + try { + // Algorithm checker does not care about the unresolved + // critical extensions. + defaultAlgChecker.check(cert, Collections.emptySet()); + if (appAlgChecker != null) { + appAlgChecker.check(cert, Collections.emptySet()); + } + } catch (CertPathValidatorException cpve) { + throw new ValidatorException + (ValidatorException.T_ALGORITHM_DISABLED, cert, cpve); + } + + // no validity check for code signing certs + if ((variant.equals(VAR_CODE_SIGNING) == false) + && (variant.equals(VAR_JCE_SIGNING) == false)) { + cert.checkValidity(date); + } + + // check name chaining + if (cert.getIssuerX500Principal().equals( + issuerCert.getSubjectX500Principal()) == false) { + throw new ValidatorException + (ValidatorException.T_NAME_CHAINING, cert); + } + + // check signature + try { + cert.verify(issuerCert.getPublicKey()); + } catch (GeneralSecurityException e) { + throw new ValidatorException + (ValidatorException.T_SIGNATURE_ERROR, cert, e); + } + + // check extensions for CA certs + if (i != 0) { + maxPathLength = checkExtensions(cert, maxPathLength); + } + } + + return chain; + } + + private int checkExtensions(X509Certificate cert, int maxPathLen) + throws CertificateException { + Set critSet = cert.getCriticalExtensionOIDs(); + if (critSet == null) { + critSet = Collections.emptySet(); + } + + // Check the basic constraints extension + int pathLenConstraint = + checkBasicConstraints(cert, critSet, maxPathLen); + + // Check the key usage and extended key usage extensions + checkKeyUsage(cert, critSet); + + // check Netscape certificate type extension + checkNetscapeCertType(cert, critSet); + + if (!critSet.isEmpty()) { + throw new ValidatorException + ("Certificate contains unknown critical extensions: " + critSet, + ValidatorException.T_CA_EXTENSIONS, cert); + } + + return pathLenConstraint; + } + + private void checkNetscapeCertType(X509Certificate cert, + Set critSet) throws CertificateException { + if (variant.equals(VAR_GENERIC)) { + // nothing + } else if (variant.equals(VAR_TLS_CLIENT) + || variant.equals(VAR_TLS_SERVER)) { + if (getNetscapeCertTypeBit(cert, NSCT_SSL_CA) == false) { + throw new ValidatorException + ("Invalid Netscape CertType extension for SSL CA " + + "certificate", + ValidatorException.T_CA_EXTENSIONS, cert); + } + critSet.remove(OID_NETSCAPE_CERT_TYPE); + } else if (variant.equals(VAR_CODE_SIGNING) + || variant.equals(VAR_JCE_SIGNING)) { + if (getNetscapeCertTypeBit(cert, NSCT_CODE_SIGNING_CA) == false) { + throw new ValidatorException + ("Invalid Netscape CertType extension for code " + + "signing CA certificate", + ValidatorException.T_CA_EXTENSIONS, cert); + } + critSet.remove(OID_NETSCAPE_CERT_TYPE); + } else { + throw new CertificateException("Unknown variant " + variant); + } + } + + /** + * Get the value of the specified bit in the Netscape certificate type + * extension. If the extension is not present at all, we return true. + */ + static boolean getNetscapeCertTypeBit(X509Certificate cert, String type) { + try { + NetscapeCertTypeExtension ext; + if (cert instanceof X509CertImpl) { + X509CertImpl certImpl = (X509CertImpl)cert; + ObjectIdentifier oid = OBJID_NETSCAPE_CERT_TYPE; + ext = (NetscapeCertTypeExtension)certImpl.getExtension(oid); + if (ext == null) { + return true; + } + } else { + byte[] extVal = cert.getExtensionValue(OID_NETSCAPE_CERT_TYPE); + if (extVal == null) { + return true; + } + DerInputStream in = new DerInputStream(extVal); + byte[] encoded = in.getOctetString(); + encoded = new DerValue(encoded).getUnalignedBitString() + .toByteArray(); + ext = new NetscapeCertTypeExtension(encoded); + } + Boolean val = ext.get(type); + return val.booleanValue(); + } catch (IOException e) { + return false; + } + } + + private int checkBasicConstraints(X509Certificate cert, + Set critSet, int maxPathLen) throws CertificateException { + + critSet.remove(OID_BASIC_CONSTRAINTS); + int constraints = cert.getBasicConstraints(); + // reject, if extension missing or not a CA (constraints == -1) + if (constraints < 0) { + throw new ValidatorException("End user tried to act as a CA", + ValidatorException.T_CA_EXTENSIONS, cert); + } + + // if the certificate is self-issued, ignore the pathLenConstraint + // checking. + if (!X509CertImpl.isSelfIssued(cert)) { + if (maxPathLen <= 0) { + throw new ValidatorException("Violated path length constraints", + ValidatorException.T_CA_EXTENSIONS, cert); + } + + maxPathLen--; + } + + if (maxPathLen > constraints) { + maxPathLen = constraints; + } + + return maxPathLen; + } + + /* + * Verify the key usage and extended key usage for intermediate + * certificates. + */ + private void checkKeyUsage(X509Certificate cert, Set critSet) + throws CertificateException { + + critSet.remove(OID_KEY_USAGE); + // EKU irrelevant in CA certificates + critSet.remove(OID_EXTENDED_KEY_USAGE); + + // check key usage extension + boolean[] keyUsageInfo = cert.getKeyUsage(); + if (keyUsageInfo != null) { + // keyUsageInfo[5] is for keyCertSign. + if ((keyUsageInfo.length < 6) || (keyUsageInfo[5] == false)) { + throw new ValidatorException + ("Wrong key usage: expected keyCertSign", + ValidatorException.T_CA_EXTENSIONS, cert); + } + } + } + + /** + * Build a trusted certificate chain. This method always returns a chain + * with a trust anchor as the final cert in the chain. If no trust anchor + * could be found, a CertificateException is thrown. + */ + private X509Certificate[] buildTrustedChain(X509Certificate[] chain) + throws CertificateException { + List c = new ArrayList(chain.length); + // scan chain starting at EE cert + // if a trusted certificate is found, append it and return + for (int i = 0; i < chain.length; i++) { + X509Certificate cert = chain[i]; + X509Certificate trustedCert = getTrustedCertificate(cert); + if (trustedCert != null) { + c.add(trustedCert); + return c.toArray(CHAIN0); + } + c.add(cert); + } + + // check if we can append a trusted cert + X509Certificate cert = chain[chain.length - 1]; + X500Principal subject = cert.getSubjectX500Principal(); + X500Principal issuer = cert.getIssuerX500Principal(); + List list = trustedX500Principals.get(issuer); + if (list != null) { + X509Certificate trustedCert = list.iterator().next(); + c.add(trustedCert); + return c.toArray(CHAIN0); + } + + // no trusted cert found, error + throw new ValidatorException(ValidatorException.T_NO_TRUST_ANCHOR); + } + + /** + * Return a trusted certificate that matches the input certificate, + * or null if no such certificate can be found. This method also handles + * cases where a CA re-issues a trust anchor with the same public key and + * same subject and issuer names but a new validity period, etc. + */ + private X509Certificate getTrustedCertificate(X509Certificate cert) { + Principal certSubjectName = cert.getSubjectX500Principal(); + List list = trustedX500Principals.get(certSubjectName); + if (list == null) { + return null; + } + + Principal certIssuerName = cert.getIssuerX500Principal(); + PublicKey certPublicKey = cert.getPublicKey(); + + for (X509Certificate mycert : list) { + if (mycert.equals(cert)) { + return cert; + } + if (!mycert.getIssuerX500Principal().equals(certIssuerName)) { + continue; + } + if (!mycert.getPublicKey().equals(certPublicKey)) { + continue; + } + + // All tests pass, this must be the one to use... + return mycert; + } + return null; + } + +} diff --git a/src/sun/security/validator/Validator.java b/src/sun/security/validator/Validator.java new file mode 100644 index 00000000..863566c7 --- /dev/null +++ b/src/sun/security/validator/Validator.java @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.util.*; + +import java.security.AlgorithmConstraints; +import java.security.KeyStore; +import java.security.cert.*; + +/** + * Validator abstract base class. Concrete classes are instantiated by calling + * one of the getInstance() methods. All methods defined in this class + * must be safe for concurrent use by multiple threads.

    + * + * The model is that a Validator instance is created specifying validation + * settings, such as trust anchors or PKIX parameters. Then one or more + * paths are validated using those parameters. In some cases, additional + * information can be provided per path validation. This is independent of + * the validation parameters and currently only used for TLS server validation. + *

    + * Path validation is performed by calling one of the validate() methods. It + * specifies a suggested path to be used for validation if available, or only + * the end entity certificate otherwise. Optionally additional certificates can + * be specified that the caller believes could be helpful. Implementations are + * free to make use of this information or validate the path using other means. + * validate() also checks that the end entity certificate is suitable for the + * intended purpose as described below. + * + *

    There are two orthogonal parameters to select the Validator + * implementation: type and variant. Type selects the validation algorithm. + * Currently supported are TYPE_SIMPLE and TYPE_PKIX. See SimpleValidator and + * PKIXValidator for details. + *

    + * Variant controls additional extension checks. Currently supported are + * five variants: + *

      + *
    • VAR_GENERIC (no additional checks), + *
    • VAR_TLS_CLIENT (TLS client specific checks) + *
    • VAR_TLS_SERVER (TLS server specific checks), and + *
    • VAR_CODE_SIGNING (code signing specific checks). + *
    • VAR_JCE_SIGNING (JCE code signing specific checks). + *
    • VAR_TSA_SERVER (TSA server specific checks). + *
    • VAR_PLUGIN_CODE_SIGNING (Plugin/WebStart code signing specific checks). + *
    + * See EndEntityChecker for more information. + *

    + * Examples: + *

    + *   // instantiate validator specifying type, variant, and trust anchors
    + *   Validator validator = Validator.getInstance(Validator.TYPE_PKIX,
    + *                                               Validator.VAR_TLS_CLIENT,
    + *                                               trustedCerts);
    + *   // validate one or more chains using the validator
    + *   validator.validate(chain); // throws CertificateException if failed
    + * 
    + * + * @see SimpleValidator + * @see PKIXValidator + * @see EndEntityChecker + * + * @author Andreas Sterbenz + */ +public abstract class Validator { + + final static X509Certificate[] CHAIN0 = {}; + + /** + * Constant for a validator of type Simple. + * @see #getInstance + */ + public final static String TYPE_SIMPLE = "Simple"; + + /** + * Constant for a validator of type PKIX. + * @see #getInstance + */ + public final static String TYPE_PKIX = "PKIX"; + + /** + * Constant for a Generic variant of a validator. + * @see #getInstance + */ + public final static String VAR_GENERIC = "generic"; + + /** + * Constant for a Code Signing variant of a validator. + * @see #getInstance + */ + public final static String VAR_CODE_SIGNING = "code signing"; + + /** + * Constant for a JCE Code Signing variant of a validator. + * @see #getInstance + */ + public final static String VAR_JCE_SIGNING = "jce signing"; + + /** + * Constant for a TLS Client variant of a validator. + * @see #getInstance + */ + public final static String VAR_TLS_CLIENT = "tls client"; + + /** + * Constant for a TLS Server variant of a validator. + * @see #getInstance + */ + public final static String VAR_TLS_SERVER = "tls server"; + + /** + * Constant for a TSA Server variant of a validator. + * @see #getInstance + */ + public final static String VAR_TSA_SERVER = "tsa server"; + + /** + * Constant for a Code Signing variant of a validator for use by + * the J2SE Plugin/WebStart code. + * @see #getInstance + */ + public final static String VAR_PLUGIN_CODE_SIGNING = "plugin code signing"; + + final EndEntityChecker endEntityChecker; + final String variant; + + /** + * @deprecated + * @see #setValidationDate + */ + @Deprecated + volatile Date validationDate; + + Validator(String type, String variant) { + this.variant = variant; + endEntityChecker = EndEntityChecker.getInstance(type, variant); + } + + /** + * Get a new Validator instance using the trusted certificates from the + * specified KeyStore as trust anchors. + */ + public static Validator getInstance(String type, String variant, + KeyStore ks) { + return getInstance(type, variant, KeyStores.getTrustedCerts(ks)); + } + + /** + * Get a new Validator instance using the Set of X509Certificates as trust + * anchors. + */ + public static Validator getInstance(String type, String variant, + Collection trustedCerts) { + if (type.equals(TYPE_SIMPLE)) { + return new SimpleValidator(variant, trustedCerts); + } else if (type.equals(TYPE_PKIX)) { + return new PKIXValidator(variant, trustedCerts); + } else { + throw new IllegalArgumentException + ("Unknown validator type: " + type); + } + } + + /** + * Get a new Validator instance using the provided PKIXBuilderParameters. + * This method can only be used with the PKIX validator. + */ + public static Validator getInstance(String type, String variant, + PKIXBuilderParameters params) { + if (type.equals(TYPE_PKIX) == false) { + throw new IllegalArgumentException + ("getInstance(PKIXBuilderParameters) can only be used " + + "with PKIX validator"); + } + return new PKIXValidator(variant, params); + } + + /** + * Validate the given certificate chain. + */ + public final X509Certificate[] validate(X509Certificate[] chain) + throws CertificateException { + return validate(chain, null, null); + } + + /** + * Validate the given certificate chain. If otherCerts is non-null, it is + * a Collection of additional X509Certificates that could be helpful for + * path building. + */ + public final X509Certificate[] validate(X509Certificate[] chain, + Collection otherCerts) throws CertificateException { + return validate(chain, otherCerts, null); + } + + /** + * Validate the given certificate chain. If otherCerts is non-null, it is + * a Collection of additional X509Certificates that could be helpful for + * path building. + *

    + * Parameter is an additional parameter with variant specific meaning. + * Currently, it is only defined for TLS_SERVER variant validators, where + * it must be non null and the name of the TLS key exchange algorithm being + * used (see JSSE X509TrustManager specification). In the future, it + * could be used to pass in a PKCS#7 object for code signing to check time + * stamps. + *

    + * @return a non-empty chain that was used to validate the path. The + * end entity cert is at index 0, the trust anchor at index n-1. + */ + public final X509Certificate[] validate(X509Certificate[] chain, + Collection otherCerts, Object parameter) + throws CertificateException { + return validate(chain, otherCerts, null, parameter); + } + + /** + * Validate the given certificate chain. + * + * @param chain the target certificate chain + * @param otherCerts a Collection of additional X509Certificates that + * could be helpful for path building (or null) + * @param constraints algorithm constraints for certification path + * processing + * @param parameter an additional parameter with variant specific meaning. + * Currently, it is only defined for TLS_SERVER variant validators, + * where it must be non null and the name of the TLS key exchange + * algorithm being used (see JSSE X509TrustManager specification). + * In the future, it could be used to pass in a PKCS#7 object for + * code signing to check time stamps. + * @return a non-empty chain that was used to validate the path. The + * end entity cert is at index 0, the trust anchor at index n-1. + */ + public final X509Certificate[] validate(X509Certificate[] chain, + Collection otherCerts, + AlgorithmConstraints constraints, + Object parameter) throws CertificateException { + chain = engineValidate(chain, otherCerts, constraints, parameter); + + // omit EE extension check if EE cert is also trust anchor + if (chain.length > 1) { + endEntityChecker.check(chain[0], parameter); + } + + return chain; + } + + abstract X509Certificate[] engineValidate(X509Certificate[] chain, + Collection otherCerts, + AlgorithmConstraints constraints, + Object parameter) throws CertificateException; + + /** + * Returns an immutable Collection of the X509Certificates this instance + * uses as trust anchors. + */ + public abstract Collection getTrustedCertificates(); + + /** + * Set the date to be used for subsequent validations. NOTE that + * this is not a supported API, it is provided to simplify + * writing tests only. + * + * @deprecated + */ + @Deprecated + public void setValidationDate(Date validationDate) { + this.validationDate = validationDate; + } + +} diff --git a/src/sun/security/validator/ValidatorException.java b/src/sun/security/validator/ValidatorException.java new file mode 100644 index 00000000..db4b056c --- /dev/null +++ b/src/sun/security/validator/ValidatorException.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.validator; + +import java.security.cert.*; + +/** + * ValidatorException thrown by the Validator. It has optional fields that + * allow better error diagnostics. + * + * @author Andreas Sterbenz + */ +public class ValidatorException extends CertificateException { + + private static final long serialVersionUID = -2836879718282292155L; + + public final static Object T_NO_TRUST_ANCHOR = + "No trusted certificate found"; + + public final static Object T_EE_EXTENSIONS = + "End entity certificate extension check failed"; + + public final static Object T_CA_EXTENSIONS = + "CA certificate extension check failed"; + + public final static Object T_CERT_EXPIRED = + "Certificate expired"; + + public final static Object T_SIGNATURE_ERROR = + "Certificate signature validation failed"; + + public final static Object T_NAME_CHAINING = + "Certificate chaining error"; + + public final static Object T_ALGORITHM_DISABLED = + "Certificate signature algorithm disabled"; + + public final static Object T_UNTRUSTED_CERT = + "Untrusted certificate"; + + private Object type; + private X509Certificate cert; + + public ValidatorException(String msg) { + super(msg); + } + + public ValidatorException(String msg, Throwable cause) { + super(msg); + initCause(cause); + } + + public ValidatorException(Object type) { + this(type, null); + } + + public ValidatorException(Object type, X509Certificate cert) { + super((String)type); + this.type = type; + this.cert = cert; + } + + public ValidatorException(Object type, X509Certificate cert, + Throwable cause) { + this(type, cert); + initCause(cause); + } + + public ValidatorException(String msg, Object type, X509Certificate cert) { + super(msg); + this.type = type; + this.cert = cert; + } + + public ValidatorException(String msg, Object type, X509Certificate cert, + Throwable cause) { + this(msg, type, cert); + initCause(cause); + } + + /** + * Get the type of the failure (one of the T_XXX constants), if + * available. This may be helpful when designing a user interface. + */ + public Object getErrorType() { + return type; + } + + /** + * Get the certificate causing the exception, if available. + */ + public X509Certificate getErrorCertificate() { + return cert; + } + +} diff --git a/src/sun/security/x509/AVA.java b/src/sun/security/x509/AVA.java new file mode 100644 index 00000000..b07e565c --- /dev/null +++ b/src/sun/security/x509/AVA.java @@ -0,0 +1,1356 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; +import java.security.AccessController; +import java.text.Normalizer; +import java.util.*; + +import sun.security.action.GetBooleanAction; +import sun.security.util.*; +import sun.security.pkcs.PKCS9Attribute; + + +/** + * X.500 Attribute-Value-Assertion (AVA): an attribute, as identified by + * some attribute ID, has some particular value. Values are as a rule ASN.1 + * printable strings. A conventional set of type IDs is recognized when + * parsing (and generating) RFC 1779, 2253 or 4514 syntax strings. + * + *

    AVAs are components of X.500 relative names. Think of them as being + * individual fields of a database record. The attribute ID is how you + * identify the field, and the value is part of a particular record. + *

    + * Note that instances of this class are immutable. + * + * @see X500Name + * @see RDN + * + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class AVA implements DerEncoder { + + private static final Debug debug = Debug.getInstance("x509", "\t[AVA]"); + // See CR 6391482: if enabled this flag preserves the old but incorrect + // PrintableString encoding for DomainComponent. It may need to be set to + // avoid breaking preexisting certificates generated with sun.security APIs. + private static final boolean PRESERVE_OLD_DC_ENCODING = + AccessController.doPrivileged(new GetBooleanAction + ("com.sun.security.preserveOldDCEncoding")); + + /** + * DEFAULT format allows both RFC1779 and RFC2253 syntax and + * additional keywords. + */ + final static int DEFAULT = 1; + /** + * RFC1779 specifies format according to RFC1779. + */ + final static int RFC1779 = 2; + /** + * RFC2253 specifies format according to RFC2253. + */ + final static int RFC2253 = 3; + + // currently not private, accessed directly from RDN + final ObjectIdentifier oid; + final DerValue value; + + /* + * If the value has any of these characters in it, it must be quoted. + * Backslash and quote characters must also be individually escaped. + * Leading and trailing spaces, also multiple internal spaces, also + * call for quoting the whole string. + */ + private static final String specialChars1779 = ",=\n+<>#;\\\""; + + /* + * In RFC2253, if the value has any of these characters in it, it + * must be quoted by a preceding \. + */ + private static final String specialChars2253 = ",=+<>#;\\\""; + + /* + * includes special chars from RFC1779 and RFC2253, as well as ' ' from + * RFC 4514. + */ + private static final String specialCharsDefault = ",=\n+<>#;\\\" "; + private static final String escapedDefault = ",+<>;\""; + + /* + * Values that aren't printable strings are emitted as BER-encoded + * hex data. + */ + private static final String hexDigits = "0123456789ABCDEF"; + + public AVA(ObjectIdentifier type, DerValue val) { + if ((type == null) || (val == null)) { + throw new NullPointerException(); + } + oid = type; + value = val; + } + + /** + * Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum + * or perhaps with quotes. Not all defined AVA tags are supported; + * of current note are X.400 related ones (PRMD, ADMD, etc). + * + * This terminates at unescaped AVA separators ("+") or RDN + * separators (",", ";"), and removes cosmetic whitespace at the end of + * values. + */ + AVA(Reader in) throws IOException { + this(in, DEFAULT); + } + + /** + * Parse an RFC 1779, 2253 or 4514 style AVA string: CN=fee fie foe fum + * or perhaps with quotes. Additional keywords can be specified in the + * keyword/OID map. + * + * This terminates at unescaped AVA separators ("+") or RDN + * separators (",", ";"), and removes cosmetic whitespace at the end of + * values. + */ + AVA(Reader in, Map keywordMap) throws IOException { + this(in, DEFAULT, keywordMap); + } + + /** + * Parse an AVA string formatted according to format. + */ + AVA(Reader in, int format) throws IOException { + this(in, format, Collections.emptyMap()); + } + + /** + * Parse an AVA string formatted according to format. + * + * @param in Reader containing AVA String + * @param format parsing format + * @param keywordMap a Map where a keyword String maps to a corresponding + * OID String. Each AVA keyword will be mapped to the corresponding OID. + * If an entry does not exist, it will fallback to the builtin + * keyword/OID mapping. + * @throws IOException if the AVA String is not valid in the specified + * format or an OID String from the keywordMap is improperly formatted + */ + AVA(Reader in, int format, Map keywordMap) + throws IOException { + // assume format is one of DEFAULT or RFC2253 + + StringBuilder temp = new StringBuilder(); + int c; + + /* + * First get the keyword indicating the attribute's type, + * and map it to the appropriate OID. + */ + while (true) { + c = readChar(in, "Incorrect AVA format"); + if (c == '=') { + break; + } + temp.append((char)c); + } + + oid = AVAKeyword.getOID(temp.toString(), format, keywordMap); + + /* + * Now parse the value. "#hex", a quoted string, or a string + * terminated by "+", ",", ";". Whitespace before or after + * the value is stripped away unless format is RFC2253. + */ + temp.setLength(0); + if (format == RFC2253) { + // read next character + c = in.read(); + if (c == ' ') { + throw new IOException("Incorrect AVA RFC2253 format - " + + "leading space must be escaped"); + } + } else { + // read next character skipping whitespace + do { + c = in.read(); + } while ((c == ' ') || (c == '\n')); + } + if (c == -1) { + // empty value + value = new DerValue(""); + return; + } + + if (c == '#') { + value = parseHexString(in, format); + } else if ((c == '"') && (format != RFC2253)) { + value = parseQuotedString(in, temp); + } else { + value = parseString(in, c, format, temp); + } + } + + /** + * Get the ObjectIdentifier of this AVA. + */ + public ObjectIdentifier getObjectIdentifier() { + return oid; + } + + /** + * Get the value of this AVA as a DerValue. + */ + public DerValue getDerValue() { + return value; + } + + /** + * Get the value of this AVA as a String. + * + * @exception RuntimeException if we could not obtain the string form + * (should not occur) + */ + public String getValueString() { + try { + String s = value.getAsString(); + if (s == null) { + throw new RuntimeException("AVA string is null"); + } + return s; + } catch (IOException e) { + // should not occur + throw new RuntimeException("AVA error: " + e, e); + } + } + + private static DerValue parseHexString + (Reader in, int format) throws IOException { + + int c; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte b = 0; + int cNdx = 0; + while (true) { + c = in.read(); + + if (isTerminator(c, format)) { + break; + } + + int cVal = hexDigits.indexOf(Character.toUpperCase((char)c)); + + if (cVal == -1) { + throw new IOException("AVA parse, invalid hex " + + "digit: "+ (char)c); + } + + if ((cNdx % 2) == 1) { + b = (byte)((b * 16) + (byte)(cVal)); + baos.write(b); + } else { + b = (byte)(cVal); + } + cNdx++; + } + + // throw exception if no hex digits + if (cNdx == 0) { + throw new IOException("AVA parse, zero hex digits"); + } + + // throw exception if odd number of hex digits + if (cNdx % 2 == 1) { + throw new IOException("AVA parse, odd number of hex digits"); + } + + return new DerValue(baos.toByteArray()); + } + + private DerValue parseQuotedString + (Reader in, StringBuilder temp) throws IOException { + + // RFC1779 specifies that an entire RDN may be enclosed in double + // quotes. In this case the syntax is any sequence of + // backslash-specialChar, backslash-backslash, + // backslash-doublequote, or character other than backslash or + // doublequote. + int c = readChar(in, "Quoted string did not end in quote"); + + List embeddedHex = new ArrayList(); + boolean isPrintableString = true; + while (c != '"') { + if (c == '\\') { + c = readChar(in, "Quoted string did not end in quote"); + + // check for embedded hex pairs + Byte hexByte = null; + if ((hexByte = getEmbeddedHexPair(c, in)) != null) { + + // always encode AVAs with embedded hex as UTF8 + isPrintableString = false; + + // append consecutive embedded hex + // as single string later + embeddedHex.add(hexByte); + c = in.read(); + continue; + } + + if (specialChars1779.indexOf((char)c) < 0) { + throw new IOException + ("Invalid escaped character in AVA: " + + (char)c); + } + } + + // add embedded hex bytes before next char + if (embeddedHex.size() > 0) { + String hexString = getEmbeddedHexString(embeddedHex); + temp.append(hexString); + embeddedHex.clear(); + } + + // check for non-PrintableString chars + isPrintableString &= DerValue.isPrintableStringChar((char)c); + temp.append((char)c); + c = readChar(in, "Quoted string did not end in quote"); + } + + // add trailing embedded hex bytes + if (embeddedHex.size() > 0) { + String hexString = getEmbeddedHexString(embeddedHex); + temp.append(hexString); + embeddedHex.clear(); + } + + do { + c = in.read(); + } while ((c == '\n') || (c == ' ')); + if (c != -1) { + throw new IOException("AVA had characters other than " + + "whitespace after terminating quote"); + } + + // encode as PrintableString unless value contains + // non-PrintableString chars + if (this.oid.equals((Object)PKCS9Attribute.EMAIL_ADDRESS_OID) || + (this.oid.equals((Object)X500Name.DOMAIN_COMPONENT_OID) && + PRESERVE_OLD_DC_ENCODING == false)) { + // EmailAddress and DomainComponent must be IA5String + return new DerValue(DerValue.tag_IA5String, + temp.toString().trim()); + } else if (isPrintableString) { + return new DerValue(temp.toString().trim()); + } else { + return new DerValue(DerValue.tag_UTF8String, + temp.toString().trim()); + } + } + + private DerValue parseString + (Reader in, int c, int format, StringBuilder temp) throws IOException { + + List embeddedHex = new ArrayList<>(); + boolean isPrintableString = true; + boolean escape = false; + boolean leadingChar = true; + int spaceCount = 0; + do { + escape = false; + if (c == '\\') { + escape = true; + c = readChar(in, "Invalid trailing backslash"); + + // check for embedded hex pairs + Byte hexByte = null; + if ((hexByte = getEmbeddedHexPair(c, in)) != null) { + + // always encode AVAs with embedded hex as UTF8 + isPrintableString = false; + + // append consecutive embedded hex + // as single string later + embeddedHex.add(hexByte); + c = in.read(); + leadingChar = false; + continue; + } + + // check if character was improperly escaped + if (format == DEFAULT && + specialCharsDefault.indexOf((char)c) == -1) { + throw new IOException + ("Invalid escaped character in AVA: '" + + (char)c + "'"); + } else if (format == RFC2253) { + if (c == ' ') { + // only leading/trailing space can be escaped + if (!leadingChar && !trailingSpace(in)) { + throw new IOException + ("Invalid escaped space character " + + "in AVA. Only a leading or trailing " + + "space character can be escaped."); + } + } else if (c == '#') { + // only leading '#' can be escaped + if (!leadingChar) { + throw new IOException + ("Invalid escaped '#' character in AVA. " + + "Only a leading '#' can be escaped."); + } + } else if (specialChars2253.indexOf((char)c) == -1) { + throw new IOException + ("Invalid escaped character in AVA: '" + + (char)c + "'"); + } + } + } else { + // check if character should have been escaped + if (format == RFC2253) { + if (specialChars2253.indexOf((char)c) != -1) { + throw new IOException + ("Character '" + (char)c + + "' in AVA appears without escape"); + } + } else if (escapedDefault.indexOf((char)c) != -1) { + throw new IOException + ("Character '" + (char)c + + "' in AVA appears without escape"); + } + } + + // add embedded hex bytes before next char + if (embeddedHex.size() > 0) { + // add space(s) before embedded hex bytes + for (int i = 0; i < spaceCount; i++) { + temp.append(" "); + } + spaceCount = 0; + + String hexString = getEmbeddedHexString(embeddedHex); + temp.append(hexString); + embeddedHex.clear(); + } + + // check for non-PrintableString chars + isPrintableString &= DerValue.isPrintableStringChar((char)c); + if (c == ' ' && escape == false) { + // do not add non-escaped spaces yet + // (non-escaped trailing spaces are ignored) + spaceCount++; + } else { + // add space(s) + for (int i = 0; i < spaceCount; i++) { + temp.append(" "); + } + spaceCount = 0; + temp.append((char)c); + } + c = in.read(); + leadingChar = false; + } while (isTerminator(c, format) == false); + + if (format == RFC2253 && spaceCount > 0) { + throw new IOException("Incorrect AVA RFC2253 format - " + + "trailing space must be escaped"); + } + + // add trailing embedded hex bytes + if (embeddedHex.size() > 0) { + String hexString = getEmbeddedHexString(embeddedHex); + temp.append(hexString); + embeddedHex.clear(); + } + + // encode as PrintableString unless value contains + // non-PrintableString chars + if (this.oid.equals((Object)PKCS9Attribute.EMAIL_ADDRESS_OID) || + (this.oid.equals((Object)X500Name.DOMAIN_COMPONENT_OID) && + PRESERVE_OLD_DC_ENCODING == false)) { + // EmailAddress and DomainComponent must be IA5String + return new DerValue(DerValue.tag_IA5String, temp.toString()); + } else if (isPrintableString) { + return new DerValue(temp.toString()); + } else { + return new DerValue(DerValue.tag_UTF8String, temp.toString()); + } + } + + private static Byte getEmbeddedHexPair(int c1, Reader in) + throws IOException { + + if (hexDigits.indexOf(Character.toUpperCase((char)c1)) >= 0) { + int c2 = readChar(in, "unexpected EOF - " + + "escaped hex value must include two valid digits"); + + if (hexDigits.indexOf(Character.toUpperCase((char)c2)) >= 0) { + int hi = Character.digit((char)c1, 16); + int lo = Character.digit((char)c2, 16); + return new Byte((byte)((hi<<4) + lo)); + } else { + throw new IOException + ("escaped hex value must include two valid digits"); + } + } + return null; + } + + private static String getEmbeddedHexString(List hexList) + throws IOException { + int n = hexList.size(); + byte[] hexBytes = new byte[n]; + for (int i = 0; i < n; i++) { + hexBytes[i] = hexList.get(i).byteValue(); + } + return new String(hexBytes, "UTF8"); + } + + private static boolean isTerminator(int ch, int format) { + switch (ch) { + case -1: + case '+': + case ',': + return true; + case ';': + return format != RFC2253; + default: + return false; + } + } + + private static int readChar(Reader in, String errMsg) throws IOException { + int c = in.read(); + if (c == -1) { + throw new IOException(errMsg); + } + return c; + } + + private static boolean trailingSpace(Reader in) throws IOException { + + boolean trailing = false; + + if (!in.markSupported()) { + // oh well + return true; + } else { + // make readAheadLimit huge - + // in practice, AVA was passed a StringReader from X500Name, + // and StringReader ignores readAheadLimit anyways + in.mark(9999); + while (true) { + int nextChar = in.read(); + if (nextChar == -1) { + trailing = true; + break; + } else if (nextChar == ' ') { + continue; + } else if (nextChar == '\\') { + int followingChar = in.read(); + if (followingChar != ' ') { + trailing = false; + break; + } + } else { + trailing = false; + break; + } + } + + in.reset(); + return trailing; + } + } + + AVA(DerValue derval) throws IOException { + // Individual attribute value assertions are SEQUENCE of two values. + // That'd be a "struct" outside of ASN.1. + if (derval.tag != DerValue.tag_Sequence) { + throw new IOException("AVA not a sequence"); + } + oid = X500Name.intern(derval.data.getOID()); + value = derval.data.getDerValue(); + + if (derval.data.available() != 0) { + throw new IOException("AVA, extra bytes = " + + derval.data.available()); + } + } + + AVA(DerInputStream in) throws IOException { + this(in.getDerValue()); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AVA == false) { + return false; + } + AVA other = (AVA)obj; + return this.toRFC2253CanonicalString().equals + (other.toRFC2253CanonicalString()); + } + + /** + * Returns a hashcode for this AVA. + * + * @return a hashcode for this AVA. + */ + public int hashCode() { + return toRFC2253CanonicalString().hashCode(); + } + + /* + * AVAs are encoded as a SEQUENCE of two elements. + */ + public void encode(DerOutputStream out) throws IOException { + derEncode(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the DerEncoder interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream tmp2 = new DerOutputStream(); + + tmp.putOID(oid); + value.encode(tmp); + tmp2.write(DerValue.tag_Sequence, tmp); + out.write(tmp2.toByteArray()); + } + + private String toKeyword(int format, Map oidMap) { + return AVAKeyword.getKeyword(oid, format, oidMap); + } + + /** + * Returns a printable form of this attribute, using RFC 1779 + * syntax for individual attribute/value assertions. + */ + public String toString() { + return toKeywordValueString + (toKeyword(DEFAULT, Collections.emptyMap())); + } + + /** + * Returns a printable form of this attribute, using RFC 1779 + * syntax for individual attribute/value assertions. It only + * emits standardised keywords. + */ + public String toRFC1779String() { + return toRFC1779String(Collections.emptyMap()); + } + + /** + * Returns a printable form of this attribute, using RFC 1779 + * syntax for individual attribute/value assertions. It + * emits standardised keywords, as well as keywords contained in the + * OID/keyword map. + */ + public String toRFC1779String(Map oidMap) { + return toKeywordValueString(toKeyword(RFC1779, oidMap)); + } + + /** + * Returns a printable form of this attribute, using RFC 2253 + * syntax for individual attribute/value assertions. It only + * emits standardised keywords. + */ + public String toRFC2253String() { + return toRFC2253String(Collections.emptyMap()); + } + + /** + * Returns a printable form of this attribute, using RFC 2253 + * syntax for individual attribute/value assertions. It + * emits standardised keywords, as well as keywords contained in the + * OID/keyword map. + */ + public String toRFC2253String(Map oidMap) { + /* + * Section 2.3: The AttributeTypeAndValue is encoded as the string + * representation of the AttributeType, followed by an equals character + * ('=' ASCII 61), followed by the string representation of the + * AttributeValue. The encoding of the AttributeValue is given in + * section 2.4. + */ + StringBuilder typeAndValue = new StringBuilder(100); + typeAndValue.append(toKeyword(RFC2253, oidMap)); + typeAndValue.append('='); + + /* + * Section 2.4: Converting an AttributeValue from ASN.1 to a String. + * If the AttributeValue is of a type which does not have a string + * representation defined for it, then it is simply encoded as an + * octothorpe character ('#' ASCII 35) followed by the hexadecimal + * representation of each of the bytes of the BER encoding of the X.500 + * AttributeValue. This form SHOULD be used if the AttributeType is of + * the dotted-decimal form. + */ + if ((typeAndValue.charAt(0) >= '0' && typeAndValue.charAt(0) <= '9') || + !isDerString(value, false)) + { + byte[] data = null; + try { + data = value.toByteArray(); + } catch (IOException ie) { + throw new IllegalArgumentException("DER Value conversion"); + } + typeAndValue.append('#'); + for (int j = 0; j < data.length; j++) { + byte b = data[j]; + typeAndValue.append(Character.forDigit(0xF & (b >>> 4), 16)); + typeAndValue.append(Character.forDigit(0xF & b, 16)); + } + } else { + /* + * 2.4 (cont): Otherwise, if the AttributeValue is of a type which + * has a string representation, the value is converted first to a + * UTF-8 string according to its syntax specification. + * + * NOTE: this implementation only emits DirectoryStrings of the + * types returned by isDerString(). + */ + String valStr = null; + try { + valStr = new String(value.getDataBytes(), "UTF8"); + } catch (IOException ie) { + throw new IllegalArgumentException("DER Value conversion"); + } + + /* + * 2.4 (cont): If the UTF-8 string does not have any of the + * following characters which need escaping, then that string can be + * used as the string representation of the value. + * + * o a space or "#" character occurring at the beginning of the + * string + * o a space character occurring at the end of the string + * o one of the characters ",", "+", """, "\", "<", ">" or ";" + * + * Implementations MAY escape other characters. + * + * NOTE: this implementation also recognizes "=" and "#" as + * characters which need escaping, and null which is escaped as + * '\00' (see RFC 4514). + * + * If a character to be escaped is one of the list shown above, then + * it is prefixed by a backslash ('\' ASCII 92). + * + * Otherwise the character to be escaped is replaced by a backslash + * and two hex digits, which form a single byte in the code of the + * character. + */ + final String escapees = ",=+<>#;\"\\"; + StringBuilder sbuffer = new StringBuilder(); + + for (int i = 0; i < valStr.length(); i++) { + char c = valStr.charAt(i); + if (DerValue.isPrintableStringChar(c) || + escapees.indexOf(c) >= 0) { + + // escape escapees + if (escapees.indexOf(c) >= 0) { + sbuffer.append('\\'); + } + + // append printable/escaped char + sbuffer.append(c); + + } else if (c == '\u0000') { + // escape null character + sbuffer.append("\\00"); + + } else if (debug != null && Debug.isOn("ava")) { + + // embed non-printable/non-escaped char + // as escaped hex pairs for debugging + byte[] valueBytes = null; + try { + valueBytes = Character.toString(c).getBytes("UTF8"); + } catch (IOException ie) { + throw new IllegalArgumentException + ("DER Value conversion"); + } + for (int j = 0; j < valueBytes.length; j++) { + sbuffer.append('\\'); + char hexChar = Character.forDigit + (0xF & (valueBytes[j] >>> 4), 16); + sbuffer.append(Character.toUpperCase(hexChar)); + hexChar = Character.forDigit + (0xF & (valueBytes[j]), 16); + sbuffer.append(Character.toUpperCase(hexChar)); + } + } else { + + // append non-printable/non-escaped char + sbuffer.append(c); + } + } + + char[] chars = sbuffer.toString().toCharArray(); + sbuffer = new StringBuilder(); + + // Find leading and trailing whitespace. + int lead; // index of first char that is not leading whitespace + for (lead = 0; lead < chars.length; lead++) { + if (chars[lead] != ' ' && chars[lead] != '\r') { + break; + } + } + int trail; // index of last char that is not trailing whitespace + for (trail = chars.length - 1; trail >= 0; trail--) { + if (chars[trail] != ' ' && chars[trail] != '\r') { + break; + } + } + + // escape leading and trailing whitespace + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (i < lead || i > trail) { + sbuffer.append('\\'); + } + sbuffer.append(c); + } + typeAndValue.append(sbuffer.toString()); + } + return typeAndValue.toString(); + } + + public String toRFC2253CanonicalString() { + /* + * Section 2.3: The AttributeTypeAndValue is encoded as the string + * representation of the AttributeType, followed by an equals character + * ('=' ASCII 61), followed by the string representation of the + * AttributeValue. The encoding of the AttributeValue is given in + * section 2.4. + */ + StringBuilder typeAndValue = new StringBuilder(40); + typeAndValue.append + (toKeyword(RFC2253, Collections.emptyMap())); + typeAndValue.append('='); + + /* + * Section 2.4: Converting an AttributeValue from ASN.1 to a String. + * If the AttributeValue is of a type which does not have a string + * representation defined for it, then it is simply encoded as an + * octothorpe character ('#' ASCII 35) followed by the hexadecimal + * representation of each of the bytes of the BER encoding of the X.500 + * AttributeValue. This form SHOULD be used if the AttributeType is of + * the dotted-decimal form. + */ + if ((typeAndValue.charAt(0) >= '0' && typeAndValue.charAt(0) <= '9') || + !isDerString(value, true)) + { + byte[] data = null; + try { + data = value.toByteArray(); + } catch (IOException ie) { + throw new IllegalArgumentException("DER Value conversion"); + } + typeAndValue.append('#'); + for (int j = 0; j < data.length; j++) { + byte b = data[j]; + typeAndValue.append(Character.forDigit(0xF & (b >>> 4), 16)); + typeAndValue.append(Character.forDigit(0xF & b, 16)); + } + } else { + /* + * 2.4 (cont): Otherwise, if the AttributeValue is of a type which + * has a string representation, the value is converted first to a + * UTF-8 string according to its syntax specification. + * + * NOTE: this implementation only emits DirectoryStrings of the + * types returned by isDerString(). + */ + String valStr = null; + try { + valStr = new String(value.getDataBytes(), "UTF8"); + } catch (IOException ie) { + throw new IllegalArgumentException("DER Value conversion"); + } + + /* + * 2.4 (cont): If the UTF-8 string does not have any of the + * following characters which need escaping, then that string can be + * used as the string representation of the value. + * + * o a space or "#" character occurring at the beginning of the + * string + * o a space character occurring at the end of the string + * + * o one of the characters ",", "+", """, "\", "<", ">" or ";" + * + * If a character to be escaped is one of the list shown above, then + * it is prefixed by a backslash ('\' ASCII 92). + * + * Otherwise the character to be escaped is replaced by a backslash + * and two hex digits, which form a single byte in the code of the + * character. + */ + final String escapees = ",+<>;\"\\"; + StringBuilder sbuffer = new StringBuilder(); + boolean previousWhite = false; + + for (int i = 0; i < valStr.length(); i++) { + char c = valStr.charAt(i); + + if (DerValue.isPrintableStringChar(c) || + escapees.indexOf(c) >= 0 || + (i == 0 && c == '#')) { + + // escape leading '#' and escapees + if ((i == 0 && c == '#') || escapees.indexOf(c) >= 0) { + sbuffer.append('\\'); + } + + // convert multiple whitespace to single whitespace + if (!Character.isWhitespace(c)) { + previousWhite = false; + sbuffer.append(c); + } else { + if (previousWhite == false) { + // add single whitespace + previousWhite = true; + sbuffer.append(c); + } else { + // ignore subsequent consecutive whitespace + continue; + } + } + + } else if (debug != null && Debug.isOn("ava")) { + + // embed non-printable/non-escaped char + // as escaped hex pairs for debugging + + previousWhite = false; + + byte valueBytes[] = null; + try { + valueBytes = Character.toString(c).getBytes("UTF8"); + } catch (IOException ie) { + throw new IllegalArgumentException + ("DER Value conversion"); + } + for (int j = 0; j < valueBytes.length; j++) { + sbuffer.append('\\'); + sbuffer.append(Character.forDigit + (0xF & (valueBytes[j] >>> 4), 16)); + sbuffer.append(Character.forDigit + (0xF & (valueBytes[j]), 16)); + } + } else { + + // append non-printable/non-escaped char + + previousWhite = false; + sbuffer.append(c); + } + } + + // remove leading and trailing whitespace from value + typeAndValue.append(sbuffer.toString().trim()); + } + + String canon = typeAndValue.toString(); + canon = canon.toUpperCase(Locale.US).toLowerCase(Locale.US); + return Normalizer.normalize(canon, Normalizer.Form.NFKD); + } + + /* + * Return true if DerValue can be represented as a String. + */ + private static boolean isDerString(DerValue value, boolean canonical) { + if (canonical) { + switch (value.tag) { + case DerValue.tag_PrintableString: + case DerValue.tag_UTF8String: + return true; + default: + return false; + } + } else { + switch (value.tag) { + case DerValue.tag_PrintableString: + case DerValue.tag_T61String: + case DerValue.tag_IA5String: + case DerValue.tag_GeneralString: + case DerValue.tag_BMPString: + case DerValue.tag_UTF8String: + return true; + default: + return false; + } + } + } + + boolean hasRFC2253Keyword() { + return AVAKeyword.hasKeyword(oid, RFC2253); + } + + private String toKeywordValueString(String keyword) { + /* + * Construct the value with as little copying and garbage + * production as practical. First the keyword (mandatory), + * then the equals sign, finally the value. + */ + StringBuilder retval = new StringBuilder(40); + + retval.append(keyword); + retval.append("="); + + try { + String valStr = value.getAsString(); + + if (valStr == null) { + + // rfc1779 specifies that attribute values associated + // with non-standard keyword attributes may be represented + // using the hex format below. This will be used only + // when the value is not a string type + + byte data [] = value.toByteArray(); + + retval.append('#'); + for (int i = 0; i < data.length; i++) { + retval.append(hexDigits.charAt((data [i] >> 4) & 0x0f)); + retval.append(hexDigits.charAt(data [i] & 0x0f)); + } + + } else { + + boolean quoteNeeded = false; + StringBuilder sbuffer = new StringBuilder(); + boolean previousWhite = false; + final String escapees = ",+=\n<>#;\\\""; + + /* + * Special characters (e.g. AVA list separators) cause strings + * to need quoting, or at least escaping. So do leading or + * trailing spaces, and multiple internal spaces. + */ + int length = valStr.length(); + boolean alreadyQuoted = + (length > 1 && valStr.charAt(0) == '\"' + && valStr.charAt(length - 1) == '\"'); + + for (int i = 0; i < length; i++) { + char c = valStr.charAt(i); + if (alreadyQuoted && (i == 0 || i == length - 1)) { + sbuffer.append(c); + continue; + } + if (DerValue.isPrintableStringChar(c) || + escapees.indexOf(c) >= 0) { + + // quote if leading whitespace or special chars + if (!quoteNeeded && + ((i == 0 && (c == ' ' || c == '\n')) || + escapees.indexOf(c) >= 0)) { + quoteNeeded = true; + } + + // quote if multiple internal whitespace + if (!(c == ' ' || c == '\n')) { + // escape '"' and '\' + if (c == '"' || c == '\\') { + sbuffer.append('\\'); + } + previousWhite = false; + } else { + if (!quoteNeeded && previousWhite) { + quoteNeeded = true; + } + previousWhite = true; + } + + sbuffer.append(c); + + } else if (debug != null && Debug.isOn("ava")) { + + // embed non-printable/non-escaped char + // as escaped hex pairs for debugging + + previousWhite = false; + + // embed escaped hex pairs + byte[] valueBytes = + Character.toString(c).getBytes("UTF8"); + for (int j = 0; j < valueBytes.length; j++) { + sbuffer.append('\\'); + char hexChar = Character.forDigit + (0xF & (valueBytes[j] >>> 4), 16); + sbuffer.append(Character.toUpperCase(hexChar)); + hexChar = Character.forDigit + (0xF & (valueBytes[j]), 16); + sbuffer.append(Character.toUpperCase(hexChar)); + } + } else { + + // append non-printable/non-escaped char + + previousWhite = false; + sbuffer.append(c); + } + } + + // quote if trailing whitespace + if (sbuffer.length() > 0) { + char trailChar = sbuffer.charAt(sbuffer.length() - 1); + if (trailChar == ' ' || trailChar == '\n') { + quoteNeeded = true; + } + } + + // Emit the string ... quote it if needed + // if string is already quoted, don't re-quote + if (!alreadyQuoted && quoteNeeded) { + retval.append("\"" + sbuffer.toString() + "\""); + } else { + retval.append(sbuffer.toString()); + } + } + } catch (IOException e) { + throw new IllegalArgumentException("DER Value conversion"); + } + + return retval.toString(); + } + +} + +/** + * Helper class that allows conversion from String to ObjectIdentifier and + * vice versa according to RFC1779, RFC2253, and an augmented version of + * those standards. + */ +class AVAKeyword { + + private static final Map oidMap; + private static final Map keywordMap; + + private String keyword; + private ObjectIdentifier oid; + private boolean rfc1779Compliant, rfc2253Compliant; + + private AVAKeyword(String keyword, ObjectIdentifier oid, + boolean rfc1779Compliant, boolean rfc2253Compliant) { + this.keyword = keyword; + this.oid = oid; + this.rfc1779Compliant = rfc1779Compliant; + this.rfc2253Compliant = rfc2253Compliant; + + // register it + oidMap.put(oid, this); + keywordMap.put(keyword, this); + } + + private boolean isCompliant(int standard) { + switch (standard) { + case AVA.RFC1779: + return rfc1779Compliant; + case AVA.RFC2253: + return rfc2253Compliant; + case AVA.DEFAULT: + return true; + default: + // should not occur, internal error + throw new IllegalArgumentException("Invalid standard " + standard); + } + } + + /** + * Get an object identifier representing the specified keyword (or + * string encoded object identifier) in the given standard. + * + * @param keywordMap a Map where a keyword String maps to a corresponding + * OID String. Each AVA keyword will be mapped to the corresponding OID. + * If an entry does not exist, it will fallback to the builtin + * keyword/OID mapping. + * @throws IOException If the keyword is not valid in the specified standard + * or the OID String to which a keyword maps to is improperly formatted. + */ + static ObjectIdentifier getOID + (String keyword, int standard, Map extraKeywordMap) + throws IOException { + + keyword = keyword.toUpperCase(Locale.ENGLISH); + if (standard == AVA.RFC2253) { + if (keyword.startsWith(" ") || keyword.endsWith(" ")) { + throw new IOException("Invalid leading or trailing space " + + "in keyword \"" + keyword + "\""); + } + } else { + keyword = keyword.trim(); + } + + // check user-specified keyword map first, then fallback to built-in + // map + String oidString = extraKeywordMap.get(keyword); + if (oidString == null) { + AVAKeyword ak = keywordMap.get(keyword); + if ((ak != null) && ak.isCompliant(standard)) { + return ak.oid; + } + } else { + return new ObjectIdentifier(oidString); + } + + // no keyword found, check if OID string + if (standard == AVA.DEFAULT && keyword.startsWith("OID.")) { + keyword = keyword.substring(4); + } + + boolean number = false; + if (keyword.length() != 0) { + char ch = keyword.charAt(0); + if ((ch >= '0') && (ch <= '9')) { + number = true; + } + } + if (number == false) { + throw new IOException("Invalid keyword \"" + keyword + "\""); + } + return new ObjectIdentifier(keyword); + } + + /** + * Get a keyword for the given ObjectIdentifier according to standard. + * If no keyword is available, the ObjectIdentifier is encoded as a + * String. + */ + static String getKeyword(ObjectIdentifier oid, int standard) { + return getKeyword + (oid, standard, Collections.emptyMap()); + } + + /** + * Get a keyword for the given ObjectIdentifier according to standard. + * Checks the extraOidMap for a keyword first, then falls back to the + * builtin/default set. If no keyword is available, the ObjectIdentifier + * is encoded as a String. + */ + static String getKeyword + (ObjectIdentifier oid, int standard, Map extraOidMap) { + + // check extraOidMap first, then fallback to built-in map + String oidString = oid.toString(); + String keywordString = extraOidMap.get(oidString); + if (keywordString == null) { + AVAKeyword ak = oidMap.get(oid); + if ((ak != null) && ak.isCompliant(standard)) { + return ak.keyword; + } + } else { + if (keywordString.length() == 0) { + throw new IllegalArgumentException("keyword cannot be empty"); + } + keywordString = keywordString.trim(); + char c = keywordString.charAt(0); + if (c < 65 || c > 122 || (c > 90 && c < 97)) { + throw new IllegalArgumentException + ("keyword does not start with letter"); + } + for (int i=1; i 122 || (c > 90 && c < 97)) && + (c < 48 || c > 57) && c != '_') { + throw new IllegalArgumentException + ("keyword character is not a letter, digit, or underscore"); + } + } + return keywordString; + } + // no compliant keyword, use OID + if (standard == AVA.RFC2253) { + return oidString; + } else { + return "OID." + oidString; + } + } + + /** + * Test if oid has an associated keyword in standard. + */ + static boolean hasKeyword(ObjectIdentifier oid, int standard) { + AVAKeyword ak = oidMap.get(oid); + if (ak == null) { + return false; + } + return ak.isCompliant(standard); + } + + static { + oidMap = new HashMap(); + keywordMap = new HashMap(); + + // NOTE if multiple keywords are available for one OID, order + // is significant!! Preferred *LAST*. + new AVAKeyword("CN", X500Name.commonName_oid, true, true); + new AVAKeyword("C", X500Name.countryName_oid, true, true); + new AVAKeyword("L", X500Name.localityName_oid, true, true); + new AVAKeyword("S", X500Name.stateName_oid, false, false); + new AVAKeyword("ST", X500Name.stateName_oid, true, true); + new AVAKeyword("O", X500Name.orgName_oid, true, true); + new AVAKeyword("OU", X500Name.orgUnitName_oid, true, true); + new AVAKeyword("T", X500Name.title_oid, false, false); + new AVAKeyword("IP", X500Name.ipAddress_oid, false, false); + new AVAKeyword("STREET", X500Name.streetAddress_oid,true, true); + new AVAKeyword("DC", X500Name.DOMAIN_COMPONENT_OID, + false, true); + new AVAKeyword("DNQUALIFIER", X500Name.DNQUALIFIER_OID, false, false); + new AVAKeyword("DNQ", X500Name.DNQUALIFIER_OID, false, false); + new AVAKeyword("SURNAME", X500Name.SURNAME_OID, false, false); + new AVAKeyword("GIVENNAME", X500Name.GIVENNAME_OID, false, false); + new AVAKeyword("INITIALS", X500Name.INITIALS_OID, false, false); + new AVAKeyword("GENERATION", X500Name.GENERATIONQUALIFIER_OID, + false, false); + new AVAKeyword("EMAIL", PKCS9Attribute.EMAIL_ADDRESS_OID, false, false); + new AVAKeyword("EMAILADDRESS", PKCS9Attribute.EMAIL_ADDRESS_OID, + false, false); + new AVAKeyword("UID", X500Name.userid_oid, false, true); + new AVAKeyword("SERIALNUMBER", X500Name.SERIALNUMBER_OID, false, false); + } +} diff --git a/src/sun/security/x509/AccessDescription.java b/src/sun/security/x509/AccessDescription.java new file mode 100644 index 00000000..fbf4bed3 --- /dev/null +++ b/src/sun/security/x509/AccessDescription.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.*; + +/** + * @author Ram Marti + */ + +public final class AccessDescription { + + private int myhash = -1; + + private ObjectIdentifier accessMethod; + + private GeneralName accessLocation; + + public static final ObjectIdentifier Ad_OCSP_Id = + ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 1}); + + public static final ObjectIdentifier Ad_CAISSUERS_Id = + ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 2}); + + public static final ObjectIdentifier Ad_TIMESTAMPING_Id = + ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 3}); + + public static final ObjectIdentifier Ad_CAREPOSITORY_Id = + ObjectIdentifier.newInternal(new int[] {1, 3, 6, 1, 5, 5, 7, 48, 5}); + + public AccessDescription(ObjectIdentifier accessMethod, GeneralName accessLocation) { + this.accessMethod = accessMethod; + this.accessLocation = accessLocation; + } + + public AccessDescription(DerValue derValue) throws IOException { + DerInputStream derIn = derValue.getData(); + accessMethod = derIn.getOID(); + accessLocation = new GeneralName(derIn.getDerValue()); + } + + public ObjectIdentifier getAccessMethod() { + return accessMethod; + } + + public GeneralName getAccessLocation() { + return accessLocation; + } + + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + tmp.putOID(accessMethod); + accessLocation.encode(tmp); + out.write(DerValue.tag_Sequence, tmp); + } + + public int hashCode() { + if (myhash == -1) { + myhash = accessMethod.hashCode() + accessLocation.hashCode(); + } + return myhash; + } + + public boolean equals(Object obj) { + if (obj == null || (!(obj instanceof AccessDescription))) { + return false; + } + AccessDescription that = (AccessDescription)obj; + + if (this == that) { + return true; + } + return (accessMethod.equals((Object)that.getAccessMethod()) && + accessLocation.equals(that.getAccessLocation())); + } + + public String toString() { + String method = null; + if (accessMethod.equals((Object)Ad_CAISSUERS_Id)) { + method = "caIssuers"; + } else if (accessMethod.equals((Object)Ad_CAREPOSITORY_Id)) { + method = "caRepository"; + } else if (accessMethod.equals((Object)Ad_TIMESTAMPING_Id)) { + method = "timeStamping"; + } else if (accessMethod.equals((Object)Ad_OCSP_Id)) { + method = "ocsp"; + } else { + method = accessMethod.toString(); + } + return ("\n accessMethod: " + method + + "\n accessLocation: " + accessLocation.toString() + "\n"); + } +} diff --git a/src/sun/security/x509/AlgIdDSA.java b/src/sun/security/x509/AlgIdDSA.java new file mode 100644 index 00000000..2260807c --- /dev/null +++ b/src/sun/security/x509/AlgIdDSA.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.DSAParams; + +import sun.security.util.*; + + +/** + * This class identifies DSS/DSA Algorithm variants, which are distinguished + * by using different algorithm parameters P, Q, G. It uses the + * NIST/IETF standard DER encoding. These are used to implement the Digital + * Signature Standard (DSS), FIPS 186. + * + *

    NOTE: DSS/DSA Algorithm IDs may be created without these + * parameters. Use of DSS/DSA in modes where parameters are + * either implicit (e.g. a default applicable to a site or a larger scope), + * or are derived from some Certificate Authority's DSS certificate, is + * not supported directly. The application is responsible for creating a key + * containing the required parameters prior to using the key in cryptographic + * operations. The follwoing is an example of how this may be done assuming + * that we have a certificate called currentCert which doesn't + * contain DSS/DSA parameters and we need to derive DSS/DSA parameters + * from a CA's certificate called caCert. + *

    + *

    + * // key containing parameters to use
    + * DSAPublicKey cAKey = (DSAPublicKey)(caCert.getPublicKey());
    + * // key without parameters
    + * DSAPublicKey nullParamsKey = (DSAPublicKey)(currentCert.getPublicKey());
    + *
    + * DSAParams cAKeyParams = cAKey.getParams();
    + * KeyFactory kf = KeyFactory.getInstance("DSA");
    + * DSAPublicKeySpec ks = new DSAPublicKeySpec(nullParamsKey.getY(),
    + *                                            cAKeyParams.getP(),
    + *                                            cAKeyParams.getQ(),
    + *                                            cAKeyParams.getG());
    + * DSAPublicKey usableKey = kf.generatePublic(ks);
    + * 
    + * + * @see DSAParams + * @see java.security.interfaces.DSAPublicKey + * @see KeyFactory + * @see java.security.spec.DSAPublicKeySpec + * + * @author David Brownell + */ +public final +class AlgIdDSA extends AlgorithmId implements DSAParams +{ + + private static final long serialVersionUID = 3437177836797504046L; + + /* + * The three unsigned integer parameters. + */ + private BigInteger p , q, g; + + /** Returns the DSS/DSA parameter "P" */ + public BigInteger getP () { return p; } + + /** Returns the DSS/DSA parameter "Q" */ + public BigInteger getQ () { return q; } + + /** Returns the DSS/DSA parameter "G" */ + public BigInteger getG () { return g; } + + /** + * Default constructor. The OID and parameters must be + * deserialized before this algorithm ID is used. + */ + @Deprecated + public AlgIdDSA () {} + + AlgIdDSA (DerValue val) throws IOException + { super(val.getOID()); } + + /** + * Construct an AlgIdDSA from an X.509 encoded byte array. + */ + public AlgIdDSA (byte[] encodedAlg) throws IOException + { super (new DerValue(encodedAlg).getOID()); } + + /** + * Constructs a DSS/DSA Algorithm ID from unsigned integers that + * define the algorithm parameters. Those integers are encoded + * as big-endian byte arrays. + * + * @param p the DSS/DSA parameter "P" + * @param q the DSS/DSA parameter "Q" + * @param g the DSS/DSA parameter "G" + */ + public AlgIdDSA (byte p [], byte q [], byte g []) + throws IOException + { + this (new BigInteger (1, p), + new BigInteger (1, q), + new BigInteger (1, g)); + } + + /** + * Constructs a DSS/DSA Algorithm ID from numeric parameters. + * If all three are null, then the parameters portion of the algorithm id + * is set to null. See note in header regarding use. + * + * @param p the DSS/DSA parameter "P" + * @param q the DSS/DSA parameter "Q" + * @param g the DSS/DSA parameter "G" + */ + public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) + { + super (DSA_oid); + + if (p != null || q != null || g != null) { + if (p == null || q == null || g == null) + throw new ProviderException("Invalid parameters for DSS/DSA" + + " Algorithm ID"); + try { + this.p = p; + this.q = q; + this.g = g; + initializeParams (); + + } catch (IOException e) { + /* this should not happen */ + throw new ProviderException ("Construct DSS/DSA Algorithm ID"); + } + } + } + + /** + * Returns "DSA", indicating the Digital Signature Algorithm (DSA) as + * defined by the Digital Signature Standard (DSS), FIPS 186. + */ + public String getName () + { return "DSA"; } + + + /* + * For algorithm IDs which haven't been created from a DER encoded + * value, "params" must be created. + */ + private void initializeParams () + throws IOException + { + DerOutputStream out = new DerOutputStream (); + + out.putInteger(p); + out.putInteger(q); + out.putInteger(g); + params = new DerValue (DerValue.tag_Sequence,out.toByteArray ()); + } + + /** + * Parses algorithm parameters P, Q, and G. They're found + * in the "params" member, which never needs to be changed. + */ + protected void decodeParams () + throws IOException + { + if (params == null) + throw new IOException("DSA alg params are null"); + if (params.tag != DerValue.tag_Sequence) + throw new IOException("DSA alg parsing error"); + + params.data.reset (); + + this.p = params.data.getBigInteger(); + this.q = params.data.getBigInteger(); + this.g = params.data.getBigInteger(); + + if (params.data.available () != 0) + throw new IOException ("AlgIdDSA params, extra="+ + params.data.available ()); + } + + + /* + * Returns a formatted string describing the parameters. + */ + public String toString () + { return paramsToString (); } + + /* + * Returns a string describing the parameters. + */ + protected String paramsToString () + { + if (params == null) + return " null\n"; + else + return + "\n p:\n" + Debug.toHexString(p) + + "\n q:\n" + Debug.toHexString(q) + + "\n g:\n" + Debug.toHexString(g) + + "\n"; + } +} diff --git a/src/sun/security/x509/AlgorithmId.java b/src/sun/security/x509/AlgorithmId.java new file mode 100644 index 00000000..799b2427 --- /dev/null +++ b/src/sun/security/x509/AlgorithmId.java @@ -0,0 +1,1120 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.util.*; +import java.security.*; + +import sun.security.rsa.PSSParameters; +import sun.security.util.*; + + +/** + * This class identifies algorithms, such as cryptographic transforms, each + * of which may be associated with parameters. Instances of this base class + * are used when this runtime environment has no special knowledge of the + * algorithm type, and may also be used in other cases. Equivalence is + * defined according to OID and (where relevant) parameters. + * + *

    Subclasses may be used, for example when when the algorithm ID has + * associated parameters which some code (e.g. code using public keys) needs + * to have parsed. Two examples of such algorithms are Diffie-Hellman key + * exchange, and the Digital Signature Standard Algorithm (DSS/DSA). + * + *

    The OID constants defined in this class correspond to some widely + * used algorithms, for which conventional string names have been defined. + * This class is not a general repository for OIDs, or for such string names. + * Note that the mappings between algorithm IDs and algorithm names is + * not one-to-one. + * + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class AlgorithmId implements Serializable, DerEncoder { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = 7205873507486557157L; + + /** + * The object identitifer being used for this algorithm. + */ + private ObjectIdentifier algid; + + // The (parsed) parameters + private AlgorithmParameters algParams; + private boolean constructedFromDer = true; + + /** + * Parameters for this algorithm. These are stored in unparsed + * DER-encoded form; subclasses can be made to automaticaly parse + * them so there is fast access to these parameters. + */ + protected DerValue params; + + + /** + * Constructs an algorithm ID which will be initialized + * separately, for example by deserialization. + * @deprecated use one of the other constructors. + */ + @Deprecated + public AlgorithmId() { } + + /** + * Constructs a parameterless algorithm ID. + * + * @param oid the identifier for the algorithm + */ + public AlgorithmId(ObjectIdentifier oid) { + algid = oid; + } + + /** + * Constructs an algorithm ID with algorithm parameters. + * + * @param oid the identifier for the algorithm. + * @param algparams the associated algorithm parameters. + */ + public AlgorithmId(ObjectIdentifier oid, AlgorithmParameters algparams) { + algid = oid; + algParams = algparams; + constructedFromDer = false; + } + + private AlgorithmId(ObjectIdentifier oid, DerValue params) + throws IOException { + this.algid = oid; + this.params = params; + if (this.params != null) { + decodeParams(); + } + } + + protected void decodeParams() throws IOException { + String algidString = algid.toString(); + try { + algParams = AlgorithmParameters.getInstance(algidString); + } catch (NoSuchAlgorithmException e) { + /* + * This algorithm parameter type is not supported, so we cannot + * parse the parameters. + */ + algParams = null; + return; + } + + // Decode (parse) the parameters + algParams.init(params.toByteArray()); + } + + /** + * Marshal a DER-encoded "AlgorithmID" sequence on the DER stream. + */ + public final void encode(DerOutputStream out) throws IOException { + derEncode(out); + } + + /** + * DER encode this object onto an output stream. + * Implements the DerEncoder interface. + * + * @param out + * the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode (OutputStream out) throws IOException { + DerOutputStream bytes = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + bytes.putOID(algid); + // Setup params from algParams since no DER encoding is given + if (constructedFromDer == false) { + if (algParams != null) { + params = new DerValue(algParams.getEncoded()); + } else { + params = null; + } + } + if (params == null) { + // Changes backed out for compatibility with Solaris + + // Several AlgorithmId should omit the whole parameter part when + // it's NULL. They are --- + // rfc3370 2.1: Implementations SHOULD generate SHA-1 + // AlgorithmIdentifiers with absent parameters. + // rfc3447 C1: When id-sha1, id-sha224, id-sha256, id-sha384 and + // id-sha512 are used in an AlgorithmIdentifier the parameters + // (which are optional) SHOULD be omitted. + // rfc3279 2.3.2: The id-dsa algorithm syntax includes optional + // domain parameters... When omitted, the parameters component + // MUST be omitted entirely + // rfc3370 3.1: When the id-dsa-with-sha1 algorithm identifier + // is used, the AlgorithmIdentifier parameters field MUST be absent. + /*if ( + algid.equals((Object)SHA_oid) || + algid.equals((Object)SHA224_oid) || + algid.equals((Object)SHA256_oid) || + algid.equals((Object)SHA384_oid) || + algid.equals((Object)SHA512_oid) || + algid.equals((Object)SHA512_224_oid) || + algid.equals((Object)SHA512_256_oid) || + algid.equals((Object)DSA_oid) || + algid.equals((Object)sha1WithDSA_oid)) { + ; // no parameter part encoded + } else { + bytes.putNull(); + }*/ + if (algid.equals(RSASSA_PSS_oid)) { + // RFC 4055 3.3: when an RSASSA-PSS key does not require + // parameter validation, field is absent. + } else { + bytes.putNull(); + } + } else { + bytes.putDerValue(params); + } + tmp.write(DerValue.tag_Sequence, bytes); + out.write(tmp.toByteArray()); + } + + + /** + * Returns the DER-encoded X.509 AlgorithmId as a byte array. + */ + public final byte[] encode() throws IOException { + DerOutputStream out = new DerOutputStream(); + derEncode(out); + return out.toByteArray(); + } + + /** + * Returns the ISO OID for this algorithm. This is usually converted + * to a string and used as part of an algorithm name, for example + * "OID.1.3.14.3.2.13" style notation. Use the getName + * call when you do not need to ensure cross-system portability + * of algorithm names, or need a user friendly name. + */ + public final ObjectIdentifier getOID () { + return algid; + } + + /** + * Returns a name for the algorithm which may be more intelligible + * to humans than the algorithm's OID, but which won't necessarily + * be comprehensible on other systems. For example, this might + * return a name such as "MD5withRSA" for a signature algorithm on + * some systems. It also returns names like "OID.1.2.3.4", when + * no particular name for the algorithm is known. + */ + public String getName() { + String algName = nameTable.get(algid); + if (algName != null) { + return algName; + } + if ((params != null) && algid.equals((Object)specifiedWithECDSA_oid)) { + try { + AlgorithmId paramsId = + AlgorithmId.parse(new DerValue(getEncodedParams())); + String paramsName = paramsId.getName(); + algName = makeSigAlg(paramsName, "EC"); + } catch (IOException e) { + // ignore + } + } + return (algName == null) ? algid.toString() : algName; + } + + public AlgorithmParameters getParameters() { + return algParams; + } + + /** + * Returns the DER encoded parameter, which can then be + * used to initialize java.security.AlgorithmParamters. + * + * @return DER encoded parameters, or null not present. + */ + public byte[] getEncodedParams() throws IOException { + return (params == null) ? null : params.toByteArray(); + } + + /** + * Returns true iff the argument indicates the same algorithm + * with the same parameters. + */ + public boolean equals(AlgorithmId other) { + boolean paramsEqual = + (params == null ? other.params == null : params.equals(other.params)); + return (algid.equals((Object)other.algid) && paramsEqual); + } + + /** + * Compares this AlgorithmID to another. If algorithm parameters are + * available, they are compared. Otherwise, just the object IDs + * for the algorithm are compared. + * + * @param other preferably an AlgorithmId, else an ObjectIdentifier + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof AlgorithmId) { + return equals((AlgorithmId) other); + } else if (other instanceof ObjectIdentifier) { + return equals((ObjectIdentifier) other); + } else { + return false; + } + } + + /** + * Compares two algorithm IDs for equality. Returns true iff + * they are the same algorithm, ignoring algorithm parameters. + */ + public final boolean equals(ObjectIdentifier id) { + return algid.equals((Object)id); + } + + /** + * Returns a hashcode for this AlgorithmId. + * + * @return a hashcode for this AlgorithmId. + */ + public int hashCode() { + StringBuilder sbuf = new StringBuilder(); + sbuf.append(algid.toString()); + sbuf.append(paramsToString()); + return sbuf.toString().hashCode(); + } + + /** + * Provides a human-readable description of the algorithm parameters. + * This may be redefined by subclasses which parse those parameters. + */ + protected String paramsToString() { + if (params == null) { + return ""; + } else if (algParams != null) { + return algParams.toString(); + } else { + return ", params unparsed"; + } + } + + /** + * Returns a string describing the algorithm and its parameters. + */ + public String toString() { + return getName() + paramsToString(); + } + + /** + * Parse (unmarshal) an ID from a DER sequence input value. This form + * parsing might be used when expanding a value which has already been + * partially unmarshaled as a set or sequence member. + * + * @exception IOException on error. + * @param val the input value, which contains the algid and, if + * there are any parameters, those parameters. + * @return an ID for the algorithm. If the system is configured + * appropriately, this may be an instance of a class + * with some kind of special support for this algorithm. + * In that case, you may "narrow" the type of the ID. + */ + public static AlgorithmId parse(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("algid parse error, not a sequence"); + } + + /* + * Get the algorithm ID and any parameters. + */ + ObjectIdentifier algid; + DerValue params; + DerInputStream in = val.toDerInputStream(); + + algid = in.getOID(); + if (in.available() == 0) { + params = null; + } else { + params = in.getDerValue(); + if (params.tag == DerValue.tag_Null) { + if (params.length() != 0) { + throw new IOException("invalid NULL"); + } + params = null; + } + if (in.available() != 0) { + throw new IOException("Invalid AlgorithmIdentifier: extra data"); + } + } + + return new AlgorithmId(algid, params); + } + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm name. + * + * @param algname the name being used + * @deprecated use the short get form of this method. + * @exception NoSuchAlgorithmException on error. + */ + @Deprecated + public static AlgorithmId getAlgorithmId(String algname) + throws NoSuchAlgorithmException { + return get(algname); + } + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm name. + * + * @param algname the name being used + * @exception NoSuchAlgorithmException on error. + */ + public static AlgorithmId get(String algname) + throws NoSuchAlgorithmException { + ObjectIdentifier oid; + try { + oid = algOID(algname); + } catch (IOException ioe) { + throw new NoSuchAlgorithmException + ("Invalid ObjectIdentifier " + algname); + } + + if (oid == null) { + throw new NoSuchAlgorithmException + ("unrecognized algorithm name: " + algname); + } + return new AlgorithmId(oid); + } + + /** + * Returns one of the algorithm IDs most commonly associated + * with this algorithm parameters. + * + * @param algparams the associated algorithm parameters. + * @exception NoSuchAlgorithmException on error. + */ + public static AlgorithmId get(AlgorithmParameters algparams) + throws NoSuchAlgorithmException { + ObjectIdentifier oid; + String algname = algparams.getAlgorithm(); + try { + oid = algOID(algname); + } catch (IOException ioe) { + throw new NoSuchAlgorithmException + ("Invalid ObjectIdentifier " + algname); + } + if (oid == null) { + throw new NoSuchAlgorithmException + ("unrecognized algorithm name: " + algname); + } + return new AlgorithmId(oid, algparams); + } + + /* + * Translates from some common algorithm names to the + * OID with which they're usually associated ... this mapping + * is the reverse of the one below, except in those cases + * where synonyms are supported or where a given algorithm + * is commonly associated with multiple OIDs. + * + * XXX This method needs to be enhanced so that we can also pass the + * scope of the algorithm name to it, e.g., the algorithm name "DSA" + * may have a different OID when used as a "Signature" algorithm than when + * used as a "KeyPairGenerator" algorithm. + */ + private static ObjectIdentifier algOID(String name) throws IOException { + // See if algname is in printable OID ("dot-dot") notation + if (name.indexOf('.') != -1) { + if (name.startsWith("OID.")) { + return new ObjectIdentifier(name.substring("OID.".length())); + } else { + return new ObjectIdentifier(name); + } + } + + // Digesting algorithms + if (name.equalsIgnoreCase("MD5")) { + return AlgorithmId.MD5_oid; + } + if (name.equalsIgnoreCase("MD2")) { + return AlgorithmId.MD2_oid; + } + if (name.equalsIgnoreCase("SHA") || name.equalsIgnoreCase("SHA1") + || name.equalsIgnoreCase("SHA-1")) { + return AlgorithmId.SHA_oid; + } + if (name.equalsIgnoreCase("SHA-256") || + name.equalsIgnoreCase("SHA256")) { + return AlgorithmId.SHA256_oid; + } + if (name.equalsIgnoreCase("SHA-384") || + name.equalsIgnoreCase("SHA384")) { + return AlgorithmId.SHA384_oid; + } + if (name.equalsIgnoreCase("SHA-512") || + name.equalsIgnoreCase("SHA512")) { + return AlgorithmId.SHA512_oid; + } + if (name.equalsIgnoreCase("SHA-224") || + name.equalsIgnoreCase("SHA224")) { + return AlgorithmId.SHA224_oid; + } + if (name.equalsIgnoreCase("SHA-512/224") || + name.equalsIgnoreCase("SHA512/224")) { + return AlgorithmId.SHA512_224_oid; + } + if (name.equalsIgnoreCase("SHA-512/256") || + name.equalsIgnoreCase("SHA512/256")) { + return AlgorithmId.SHA512_256_oid; + } + // Various public key algorithms + if (name.equalsIgnoreCase("RSA")) { + return AlgorithmId.RSAEncryption_oid; + } + if (name.equalsIgnoreCase("RSASSA-PSS")) { + return AlgorithmId.RSASSA_PSS_oid; + } + if (name.equalsIgnoreCase("RSAES-OAEP")) { + return AlgorithmId.RSAES_OAEP_oid; + } + if (name.equalsIgnoreCase("Diffie-Hellman") + || name.equalsIgnoreCase("DH")) { + return AlgorithmId.DH_oid; + } + if (name.equalsIgnoreCase("DSA")) { + return AlgorithmId.DSA_oid; + } + if (name.equalsIgnoreCase("EC")) { + return EC_oid; + } + if (name.equalsIgnoreCase("ECDH")) { + return AlgorithmId.ECDH_oid; + } + + // Secret key algorithms + if (name.equalsIgnoreCase("AES")) { + return AlgorithmId.AES_oid; + } + + // Common signature types + if (name.equalsIgnoreCase("MD5withRSA") + || name.equalsIgnoreCase("MD5/RSA")) { + return AlgorithmId.md5WithRSAEncryption_oid; + } + if (name.equalsIgnoreCase("MD2withRSA") + || name.equalsIgnoreCase("MD2/RSA")) { + return AlgorithmId.md2WithRSAEncryption_oid; + } + if (name.equalsIgnoreCase("SHAwithDSA") + || name.equalsIgnoreCase("SHA1withDSA") + || name.equalsIgnoreCase("SHA/DSA") + || name.equalsIgnoreCase("SHA1/DSA") + || name.equalsIgnoreCase("DSAWithSHA1") + || name.equalsIgnoreCase("DSS") + || name.equalsIgnoreCase("SHA-1/DSA")) { + return AlgorithmId.sha1WithDSA_oid; + } + if (name.equalsIgnoreCase("SHA224WithDSA")) { + return AlgorithmId.sha224WithDSA_oid; + } + if (name.equalsIgnoreCase("SHA256WithDSA")) { + return AlgorithmId.sha256WithDSA_oid; + } + if (name.equalsIgnoreCase("SHA1WithRSA") + || name.equalsIgnoreCase("SHA1/RSA")) { + return AlgorithmId.sha1WithRSAEncryption_oid; + } + if (name.equalsIgnoreCase("SHA1withECDSA") + || name.equalsIgnoreCase("ECDSA")) { + return AlgorithmId.sha1WithECDSA_oid; + } + if (name.equalsIgnoreCase("SHA224withECDSA")) { + return AlgorithmId.sha224WithECDSA_oid; + } + if (name.equalsIgnoreCase("SHA256withECDSA")) { + return AlgorithmId.sha256WithECDSA_oid; + } + if (name.equalsIgnoreCase("SHA384withECDSA")) { + return AlgorithmId.sha384WithECDSA_oid; + } + if (name.equalsIgnoreCase("SHA512withECDSA")) { + return AlgorithmId.sha512WithECDSA_oid; + } + + // See if any of the installed providers supply a mapping from + // the given algorithm name to an OID string + String oidString; + if (!initOidTable) { + Provider[] provs = Security.getProviders(); + for (int i=0; i enum_ = provs[i].keys(); + enum_.hasMoreElements(); ) { + String alias = (String)enum_.nextElement(); + String upperCaseAlias = alias.toUpperCase(Locale.ENGLISH); + int index; + if (upperCaseAlias.startsWith("ALG.ALIAS") && + (index=upperCaseAlias.indexOf("OID.", 0)) != -1) { + index += "OID.".length(); + if (index == alias.length()) { + // invalid alias entry + break; + } + if (oidTable == null) { + oidTable = new HashMap(); + } + oidString = alias.substring(index); + String stdAlgName = provs[i].getProperty(alias); + if (stdAlgName != null) { + stdAlgName = stdAlgName.toUpperCase(Locale.ENGLISH); + } + if (stdAlgName != null && + oidTable.get(stdAlgName) == null) { + oidTable.put(stdAlgName, + new ObjectIdentifier(oidString)); + } + } + } + } + + if (oidTable == null) { + oidTable = new HashMap(1); + } + initOidTable = true; + } + + return oidTable.get(name.toUpperCase(Locale.ENGLISH)); + } + + private static ObjectIdentifier oid(int ... values) { + return ObjectIdentifier.newInternal(values); + } + + private static boolean initOidTable = false; + private static Map oidTable; + private static final Map nameTable; + + /*****************************************************************/ + + /* + * HASHING ALGORITHMS + */ + + /** + * Algorithm ID for the MD2 Message Digest Algorthm, from RFC 1319. + * OID = 1.2.840.113549.2.2 + */ + public static final ObjectIdentifier MD2_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 2, 2}); + + /** + * Algorithm ID for the MD5 Message Digest Algorthm, from RFC 1321. + * OID = 1.2.840.113549.2.5 + */ + public static final ObjectIdentifier MD5_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 2, 5}); + + /** + * Algorithm ID for the SHA1 Message Digest Algorithm, from FIPS 180-1. + * This is sometimes called "SHA", though that is often confusing since + * many people refer to FIPS 180 (which has an error) as defining SHA. + * OID = 1.3.14.3.2.26. Old SHA-0 OID: 1.3.14.3.2.18. + */ + public static final ObjectIdentifier SHA_oid = + ObjectIdentifier.newInternal(new int[] {1, 3, 14, 3, 2, 26}); + + public static final ObjectIdentifier SHA224_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 4}); + + public static final ObjectIdentifier SHA256_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 1}); + + public static final ObjectIdentifier SHA384_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 2}); + + public static final ObjectIdentifier SHA512_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 3}); + + public static final ObjectIdentifier SHA512_224_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 5}); + + public static final ObjectIdentifier SHA512_256_oid = + ObjectIdentifier.newInternal(new int[] {2, 16, 840, 1, 101, 3, 4, 2, 6}); + + /* + * COMMON PUBLIC KEY TYPES + */ + private static final int DH_data[] = { 1, 2, 840, 113549, 1, 3, 1 }; + private static final int DH_PKIX_data[] = { 1, 2, 840, 10046, 2, 1 }; + private static final int DSA_OIW_data[] = { 1, 3, 14, 3, 2, 12 }; + private static final int DSA_PKIX_data[] = { 1, 2, 840, 10040, 4, 1 }; + private static final int RSA_data[] = { 2, 5, 8, 1, 1 }; + + public static final ObjectIdentifier DH_oid; + public static final ObjectIdentifier DH_PKIX_oid; + public static final ObjectIdentifier DSA_oid; + public static final ObjectIdentifier DSA_OIW_oid; + public static final ObjectIdentifier EC_oid = oid(1, 2, 840, 10045, 2, 1); + public static final ObjectIdentifier ECDH_oid = oid(1, 3, 132, 1, 12); + public static final ObjectIdentifier RSA_oid; + public static final ObjectIdentifier RSAEncryption_oid = + oid(1, 2, 840, 113549, 1, 1, 1); + public static final ObjectIdentifier RSAES_OAEP_oid = + oid(1, 2, 840, 113549, 1, 1, 7); + public static final ObjectIdentifier mgf1_oid = + oid(1, 2, 840, 113549, 1, 1, 8); + public static final ObjectIdentifier RSASSA_PSS_oid = + oid(1, 2, 840, 113549, 1, 1, 10); + + /* + * COMMON SECRET KEY TYPES + */ + public static final ObjectIdentifier AES_oid = + oid(2, 16, 840, 1, 101, 3, 4, 1); + + /* + * COMMON SIGNATURE ALGORITHMS + */ + private static final int md2WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 2 }; + private static final int md5WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 4 }; + private static final int sha1WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 5 }; + private static final int sha1WithRSAEncryption_OIW_data[] = + { 1, 3, 14, 3, 2, 29 }; + private static final int sha224WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 14 }; + private static final int sha256WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 11 }; + private static final int sha384WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 12 }; + private static final int sha512WithRSAEncryption_data[] = + { 1, 2, 840, 113549, 1, 1, 13 }; + + private static final int shaWithDSA_OIW_data[] = + { 1, 3, 14, 3, 2, 13 }; + private static final int sha1WithDSA_OIW_data[] = + { 1, 3, 14, 3, 2, 27 }; + private static final int dsaWithSHA1_PKIX_data[] = + { 1, 2, 840, 10040, 4, 3 }; + + public static final ObjectIdentifier md2WithRSAEncryption_oid; + public static final ObjectIdentifier md5WithRSAEncryption_oid; + public static final ObjectIdentifier sha1WithRSAEncryption_oid; + public static final ObjectIdentifier sha1WithRSAEncryption_OIW_oid; + public static final ObjectIdentifier sha224WithRSAEncryption_oid; + public static final ObjectIdentifier sha256WithRSAEncryption_oid; + public static final ObjectIdentifier sha384WithRSAEncryption_oid; + public static final ObjectIdentifier sha512WithRSAEncryption_oid; + public static final ObjectIdentifier sha512_224WithRSAEncryption_oid = + oid(1, 2, 840, 113549, 1, 1, 15); + public static final ObjectIdentifier sha512_256WithRSAEncryption_oid = + oid(1, 2, 840, 113549, 1, 1, 16);; + + public static final ObjectIdentifier shaWithDSA_OIW_oid; + public static final ObjectIdentifier sha1WithDSA_OIW_oid; + public static final ObjectIdentifier sha1WithDSA_oid; + public static final ObjectIdentifier sha224WithDSA_oid = + oid(2, 16, 840, 1, 101, 3, 4, 3, 1); + public static final ObjectIdentifier sha256WithDSA_oid = + oid(2, 16, 840, 1, 101, 3, 4, 3, 2); + + public static final ObjectIdentifier sha1WithECDSA_oid = + oid(1, 2, 840, 10045, 4, 1); + public static final ObjectIdentifier sha224WithECDSA_oid = + oid(1, 2, 840, 10045, 4, 3, 1); + public static final ObjectIdentifier sha256WithECDSA_oid = + oid(1, 2, 840, 10045, 4, 3, 2); + public static final ObjectIdentifier sha384WithECDSA_oid = + oid(1, 2, 840, 10045, 4, 3, 3); + public static final ObjectIdentifier sha512WithECDSA_oid = + oid(1, 2, 840, 10045, 4, 3, 4); + public static final ObjectIdentifier specifiedWithECDSA_oid = + oid(1, 2, 840, 10045, 4, 3); + + /** + * Algorithm ID for the PBE encryption algorithms from PKCS#5 and + * PKCS#12. + */ + public static final ObjectIdentifier pbeWithMD5AndDES_oid = + ObjectIdentifier.newInternal(new int[]{1, 2, 840, 113549, 1, 5, 3}); + public static final ObjectIdentifier pbeWithMD5AndRC2_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 6}); + public static final ObjectIdentifier pbeWithSHA1AndDES_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 10}); + public static final ObjectIdentifier pbeWithSHA1AndRC2_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 5, 11}); + public static ObjectIdentifier pbeWithSHA1AndDESede_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 3}); + public static ObjectIdentifier pbeWithSHA1AndRC2_40_oid = + ObjectIdentifier.newInternal(new int[] {1, 2, 840, 113549, 1, 12, 1, 6}); + + static { + /* + * Note the preferred OIDs are named simply with no "OIW" or + * "PKIX" in them, even though they may point to data from these + * specs; e.g. SHA_oid, DH_oid, DSA_oid, SHA1WithDSA_oid... + */ + /** + * Algorithm ID for Diffie Hellman Key agreement, from PKCS #3. + * Parameters include public values P and G, and may optionally specify + * the length of the private key X. Alternatively, algorithm parameters + * may be derived from another source such as a Certificate Authority's + * certificate. + * OID = 1.2.840.113549.1.3.1 + */ + DH_oid = ObjectIdentifier.newInternal(DH_data); + + /** + * Algorithm ID for the Diffie Hellman Key Agreement (DH), from RFC 3279. + * Parameters may include public values P and G. + * OID = 1.2.840.10046.2.1 + */ + DH_PKIX_oid = ObjectIdentifier.newInternal(DH_PKIX_data); + + /** + * Algorithm ID for the Digital Signing Algorithm (DSA), from the + * NIST OIW Stable Agreements part 12. + * Parameters may include public values P, Q, and G; or these may be + * derived from + * another source such as a Certificate Authority's certificate. + * OID = 1.3.14.3.2.12 + */ + DSA_OIW_oid = ObjectIdentifier.newInternal(DSA_OIW_data); + + /** + * Algorithm ID for the Digital Signing Algorithm (DSA), from RFC 3279. + * Parameters may include public values P, Q, and G; or these may be + * derived from another source such as a Certificate Authority's + * certificate. + * OID = 1.2.840.10040.4.1 + */ + DSA_oid = ObjectIdentifier.newInternal(DSA_PKIX_data); + + /** + * Algorithm ID for RSA keys used for any purpose, as defined in X.509. + * The algorithm parameter is a single value, the number of bits in the + * public modulus. + * OID = 2.5.8.1.1 + */ + RSA_oid = ObjectIdentifier.newInternal(RSA_data); + + /** + * Identifies a signing algorithm where an MD2 digest is encrypted + * using an RSA private key; defined in PKCS #1. Use of this + * signing algorithm is discouraged due to MD2 vulnerabilities. + * OID = 1.2.840.113549.1.1.2 + */ + md2WithRSAEncryption_oid = + ObjectIdentifier.newInternal(md2WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where an MD5 digest is + * encrypted using an RSA private key; defined in PKCS #1. + * OID = 1.2.840.113549.1.1.4 + */ + md5WithRSAEncryption_oid = + ObjectIdentifier.newInternal(md5WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where a SHA1 digest is + * encrypted using an RSA private key; defined by RSA DSI. + * OID = 1.2.840.113549.1.1.5 + */ + sha1WithRSAEncryption_oid = + ObjectIdentifier.newInternal(sha1WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where a SHA1 digest is + * encrypted using an RSA private key; defined in NIST OIW. + * OID = 1.3.14.3.2.29 + */ + sha1WithRSAEncryption_OIW_oid = + ObjectIdentifier.newInternal(sha1WithRSAEncryption_OIW_data); + + /** + * Identifies a signing algorithm where a SHA224 digest is + * encrypted using an RSA private key; defined by PKCS #1. + * OID = 1.2.840.113549.1.1.14 + */ + sha224WithRSAEncryption_oid = + ObjectIdentifier.newInternal(sha224WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where a SHA256 digest is + * encrypted using an RSA private key; defined by PKCS #1. + * OID = 1.2.840.113549.1.1.11 + */ + sha256WithRSAEncryption_oid = + ObjectIdentifier.newInternal(sha256WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where a SHA384 digest is + * encrypted using an RSA private key; defined by PKCS #1. + * OID = 1.2.840.113549.1.1.12 + */ + sha384WithRSAEncryption_oid = + ObjectIdentifier.newInternal(sha384WithRSAEncryption_data); + + /** + * Identifies a signing algorithm where a SHA512 digest is + * encrypted using an RSA private key; defined by PKCS #1. + * OID = 1.2.840.113549.1.1.13 + */ + sha512WithRSAEncryption_oid = + ObjectIdentifier.newInternal(sha512WithRSAEncryption_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA digest is signed using the Digital Signing Algorithm (DSA). + * This should not be used. + * OID = 1.3.14.3.2.13 + */ + shaWithDSA_OIW_oid = ObjectIdentifier.newInternal(shaWithDSA_OIW_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA1 digest is signed using the Digital Signing Algorithm (DSA). + * OID = 1.3.14.3.2.27 + */ + sha1WithDSA_OIW_oid = ObjectIdentifier.newInternal(sha1WithDSA_OIW_data); + + /** + * Identifies the FIPS 186 "Digital Signature Standard" (DSS), where a + * SHA1 digest is signed using the Digital Signing Algorithm (DSA). + * OID = 1.2.840.10040.4.3 + */ + sha1WithDSA_oid = ObjectIdentifier.newInternal(dsaWithSHA1_PKIX_data); + + nameTable = new HashMap(); + nameTable.put(MD5_oid, "MD5"); + nameTable.put(MD2_oid, "MD2"); + nameTable.put(SHA_oid, "SHA-1"); + nameTable.put(SHA224_oid, "SHA-224"); + nameTable.put(SHA256_oid, "SHA-256"); + nameTable.put(SHA384_oid, "SHA-384"); + nameTable.put(SHA512_oid, "SHA-512"); + nameTable.put(SHA512_224_oid, "SHA-512/224"); + nameTable.put(SHA512_256_oid, "SHA-512/256"); + nameTable.put(RSAEncryption_oid, "RSA"); + nameTable.put(RSA_oid, "RSA"); + nameTable.put(DH_oid, "Diffie-Hellman"); + nameTable.put(DH_PKIX_oid, "Diffie-Hellman"); + nameTable.put(DSA_oid, "DSA"); + nameTable.put(DSA_OIW_oid, "DSA"); + nameTable.put(EC_oid, "EC"); + nameTable.put(ECDH_oid, "ECDH"); + + nameTable.put(AES_oid, "AES"); + + nameTable.put(sha1WithECDSA_oid, "SHA1withECDSA"); + nameTable.put(sha224WithECDSA_oid, "SHA224withECDSA"); + nameTable.put(sha256WithECDSA_oid, "SHA256withECDSA"); + nameTable.put(sha384WithECDSA_oid, "SHA384withECDSA"); + nameTable.put(sha512WithECDSA_oid, "SHA512withECDSA"); + nameTable.put(md5WithRSAEncryption_oid, "MD5withRSA"); + nameTable.put(md2WithRSAEncryption_oid, "MD2withRSA"); + nameTable.put(sha1WithDSA_oid, "SHA1withDSA"); + nameTable.put(sha1WithDSA_OIW_oid, "SHA1withDSA"); + nameTable.put(shaWithDSA_OIW_oid, "SHA1withDSA"); + nameTable.put(sha224WithDSA_oid, "SHA224withDSA"); + nameTable.put(sha256WithDSA_oid, "SHA256withDSA"); + nameTable.put(sha1WithRSAEncryption_oid, "SHA1withRSA"); + nameTable.put(sha1WithRSAEncryption_OIW_oid, "SHA1withRSA"); + nameTable.put(sha224WithRSAEncryption_oid, "SHA224withRSA"); + nameTable.put(sha256WithRSAEncryption_oid, "SHA256withRSA"); + nameTable.put(sha384WithRSAEncryption_oid, "SHA384withRSA"); + nameTable.put(sha512WithRSAEncryption_oid, "SHA512withRSA"); + nameTable.put(sha512_224WithRSAEncryption_oid, "SHA512/224withRSA"); + nameTable.put(sha512_256WithRSAEncryption_oid, "SHA512/256withRSA"); + nameTable.put(RSASSA_PSS_oid, "RSASSA-PSS"); + nameTable.put(RSAES_OAEP_oid, "RSAES-OAEP"); + + nameTable.put(pbeWithMD5AndDES_oid, "PBEWithMD5AndDES"); + nameTable.put(pbeWithMD5AndRC2_oid, "PBEWithMD5AndRC2"); + nameTable.put(pbeWithSHA1AndDES_oid, "PBEWithSHA1AndDES"); + nameTable.put(pbeWithSHA1AndRC2_oid, "PBEWithSHA1AndRC2"); + nameTable.put(pbeWithSHA1AndDESede_oid, "PBEWithSHA1AndDESede"); + nameTable.put(pbeWithSHA1AndRC2_40_oid, "PBEWithSHA1AndRC2_40"); + } + + /** + * Creates a signature algorithm name from a digest algorithm + * name and a encryption algorithm name. + */ + public static String makeSigAlg(String digAlg, String encAlg) { + digAlg = digAlg.replace("-", ""); + if (encAlg.equalsIgnoreCase("EC")) encAlg = "ECDSA"; + + return digAlg + "with" + encAlg; + } + + /** + * Extracts the encryption algorithm name from a signature + * algorithm name. + */ + public static String getEncAlgFromSigAlg(String signatureAlgorithm) { + signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH); + int with = signatureAlgorithm.indexOf("WITH"); + String keyAlgorithm = null; + if (with > 0) { + int and = signatureAlgorithm.indexOf("AND", with + 4); + if (and > 0) { + keyAlgorithm = signatureAlgorithm.substring(with + 4, and); + } else { + keyAlgorithm = signatureAlgorithm.substring(with + 4); + } + if (keyAlgorithm.equalsIgnoreCase("ECDSA")) { + keyAlgorithm = "EC"; + } + } + return keyAlgorithm; + } + + /** + * Extracts the digest algorithm name from a signature + * algorithm name. + */ + public static String getDigAlgFromSigAlg(String signatureAlgorithm) { + signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH); + int with = signatureAlgorithm.indexOf("WITH"); + if (with > 0) { + return signatureAlgorithm.substring(0, with); + } + return null; + } + + // Most commonly used PSSParameterSpec and AlgorithmId + private static class PSSParamsHolder { + + final static PSSParameterSpec PSS_256_SPEC = new PSSParameterSpec( + "SHA-256", "MGF1", + new MGF1ParameterSpec("SHA-256"), + 32, PSSParameterSpec.TRAILER_FIELD_BC); + final static PSSParameterSpec PSS_384_SPEC = new PSSParameterSpec( + "SHA-384", "MGF1", + new MGF1ParameterSpec("SHA-384"), + 48, PSSParameterSpec.TRAILER_FIELD_BC); + final static PSSParameterSpec PSS_512_SPEC = new PSSParameterSpec( + "SHA-512", "MGF1", + new MGF1ParameterSpec("SHA-512"), + 64, PSSParameterSpec.TRAILER_FIELD_BC); + + final static AlgorithmId PSS_256_ID; + final static AlgorithmId PSS_384_ID; + final static AlgorithmId PSS_512_ID; + + static { + try { + PSS_256_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_256_SPEC))); + PSS_384_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_384_SPEC))); + PSS_512_ID = new AlgorithmId(RSASSA_PSS_oid, + new DerValue(PSSParameters.getEncoded(PSS_512_SPEC))); + } catch (IOException e) { + throw new AssertionError("Should not happen", e); + } + } + } + + public static AlgorithmId getWithParameterSpec(String algName, + AlgorithmParameterSpec spec) throws NoSuchAlgorithmException { + + if (spec == null) { + return AlgorithmId.get(algName); + } else if (spec == PSSParamsHolder.PSS_256_SPEC) { + return PSSParamsHolder.PSS_256_ID; + } else if (spec == PSSParamsHolder.PSS_384_SPEC) { + return PSSParamsHolder.PSS_384_ID; + } else if (spec == PSSParamsHolder.PSS_512_SPEC) { + return PSSParamsHolder.PSS_512_ID; + } else { + try { + AlgorithmParameters result = + AlgorithmParameters.getInstance(algName); + result.init(spec); + return get(result); + } catch (InvalidParameterSpecException | NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + } + } + + public static PSSParameterSpec getDefaultAlgorithmParameterSpec( + String sigAlg, PrivateKey k) { + if (sigAlg.equalsIgnoreCase("RSASSA-PSS")) { + switch (ifcFfcStrength(KeyUtil.getKeySize(k))) { + case "SHA256": + return PSSParamsHolder.PSS_256_SPEC; + case "SHA384": + return PSSParamsHolder.PSS_384_SPEC; + case "SHA512": + return PSSParamsHolder.PSS_512_SPEC; + default: + throw new AssertionError("Should not happen"); + } + } else { + return null; + } + } + + // Values from SP800-57 part 1 rev 4 tables 2 and 3 + private static String ecStrength (int bitLength) { + if (bitLength >= 512) { // 256 bits of strength + return "SHA512"; + } else if (bitLength >= 384) { // 192 bits of strength + return "SHA384"; + } else { // 128 bits of strength and less + return "SHA256"; + } + } + + // Same values for RSA and DSA + private static String ifcFfcStrength (int bitLength) { + if (bitLength > 7680) { // 256 bits + return "SHA512"; + } else if (bitLength > 3072) { // 192 bits + return "SHA384"; + } else { // 128 bits and less + return "SHA256"; + } + } + +} diff --git a/src/sun/security/x509/AttributeNameEnumeration.java b/src/sun/security/x509/AttributeNameEnumeration.java new file mode 100644 index 00000000..3b439427 --- /dev/null +++ b/src/sun/security/x509/AttributeNameEnumeration.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.util.Vector; + +/** + *

    This class provides the Enumeration implementation used + * by all the X509 certificate attributes to return the attribute + * names contained within them. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class AttributeNameEnumeration extends Vector { + + private static final long serialVersionUID = -6067440240757099134L; + + /** + * The default constructor for this class. + */ + public AttributeNameEnumeration() { + super(4,2); + } +} diff --git a/src/sun/security/x509/AuthorityInfoAccessExtension.java b/src/sun/security/x509/AuthorityInfoAccessExtension.java new file mode 100644 index 00000000..725c753e --- /dev/null +++ b/src/sun/security/x509/AuthorityInfoAccessExtension.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.*; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * The Authority Information Access Extension (OID = 1.3.6.1.5.5.7.1.1). + *

    + * The AIA extension identifies how to access CA information and services + * for the certificate in which it appears. It enables CAs to issue their + * certificates pre-configured with the URLs appropriate for contacting + * services relevant to those certificates. For example, a CA may issue a + * certificate that identifies the specific OCSP Responder to use when + * performing on-line validation of that certificate. + *

    + * This extension is defined in + * Internet X.509 PKI Certificate and Certificate Revocation List + * (CRL) Profile. The profile permits + * the extension to be included in end-entity or CA certificates, + * and it must be marked as non-critical. Its ASN.1 definition is as follows: + *

    + *   id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
    + *
    + *   AuthorityInfoAccessSyntax  ::=
    + *         SEQUENCE SIZE (1..MAX) OF AccessDescription
    + *
    + *   AccessDescription  ::=  SEQUENCE {
    + *         accessMethod          OBJECT IDENTIFIER,
    + *         accessLocation        GeneralName  }
    + * 
    + *

    + * @see Extension + * @see CertAttrSet + */ + +public class AuthorityInfoAccessExtension extends Extension + implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.AuthorityInfoAccess"; + + /** + * Attribute name. + */ + public static final String NAME = "AuthorityInfoAccess"; + public static final String DESCRIPTIONS = "descriptions"; + + /** + * The List of AccessDescription objects. + */ + private List accessDescriptions; + + /** + * Create an AuthorityInfoAccessExtension from a List of + * AccessDescription; the criticality is set to false. + * + * @param accessDescriptions the List of AccessDescription + * @throws IOException on error + */ + public AuthorityInfoAccessExtension( + List accessDescriptions) throws IOException { + this.extensionId = PKIXExtensions.AuthInfoAccess_Id; + this.critical = false; + this.accessDescriptions = accessDescriptions; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public AuthorityInfoAccessExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.AuthInfoAccess_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) { + throw new IOException("Illegal argument type"); + } + + extensionValue = (byte[])value; + DerValue val = new DerValue(extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "AuthorityInfoAccessExtension."); + } + accessDescriptions = new ArrayList(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + AccessDescription accessDescription = new AccessDescription(seq); + accessDescriptions.add(accessDescription); + } + } + + /** + * Return the list of AccessDescription objects. + */ + public List getAccessDescriptions() { + return accessDescriptions; + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.AuthInfoAccess_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with an instanceof check + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + if (!(obj instanceof List)) { + throw new IOException("Attribute value should be of type List."); + } + accessDescriptions = (List)obj; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:AuthorityInfoAccessExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public List get(String name) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + return accessDescriptions; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:AuthorityInfoAccessExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + accessDescriptions = new ArrayList(); + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:AuthorityInfoAccessExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DESCRIPTIONS); + return elements.elements(); + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (accessDescriptions.isEmpty()) { + this.extensionValue = null; + } else { + DerOutputStream ads = new DerOutputStream(); + for (AccessDescription accessDescription : accessDescriptions) { + accessDescription.encode(ads); + } + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, ads); + this.extensionValue = seq.toByteArray(); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + return super.toString() + "AuthorityInfoAccess [\n " + + accessDescriptions + "\n]\n"; + } + +} diff --git a/src/sun/security/x509/AuthorityKeyIdentifierExtension.java b/src/sun/security/x509/AuthorityKeyIdentifierExtension.java new file mode 100644 index 00000000..afd7c996 --- /dev/null +++ b/src/sun/security/x509/AuthorityKeyIdentifierExtension.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class represents the Authority Key Identifier Extension. + * + *

    The authority key identifier extension provides a means of + * identifying the particular public key used to sign a certificate. + * This extension would be used where an issuer has multiple signing + * keys (either due to multiple concurrent key pairs or due to + * changeover). + *

    + * The ASN.1 syntax for this is: + *

    + * AuthorityKeyIdentifier ::= SEQUENCE {
    + *    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
    + *    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
    + *    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL
    + * }
    + * KeyIdentifier ::= OCTET STRING
    + * 
    + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class AuthorityKeyIdentifierExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.AuthorityKeyIdentifier"; + /** + * Attribute names. + */ + public static final String NAME = "AuthorityKeyIdentifier"; + public static final String KEY_ID = "key_id"; + public static final String AUTH_NAME = "auth_name"; + public static final String SERIAL_NUMBER = "serial_number"; + + // Private data members + private static final byte TAG_ID = 0; + private static final byte TAG_NAMES = 1; + private static final byte TAG_SERIAL_NUM = 2; + + private KeyIdentifier id = null; + private GeneralNames names = null; + private SerialNumber serialNum = null; + + // Encode only the extension value + private void encodeThis() throws IOException { + if (id == null && names == null && serialNum == null) { + this.extensionValue = null; + return; + } + DerOutputStream seq = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + if (id != null) { + DerOutputStream tmp1 = new DerOutputStream(); + id.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_ID), tmp1); + } + try { + if (names != null) { + DerOutputStream tmp1 = new DerOutputStream(); + names.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_NAMES), tmp1); + } + } catch (Exception e) { + throw new IOException(e.toString()); + } + if (serialNum != null) { + DerOutputStream tmp1 = new DerOutputStream(); + serialNum.encode(tmp1); + tmp.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_SERIAL_NUM), tmp1); + } + seq.write(DerValue.tag_Sequence, tmp); + this.extensionValue = seq.toByteArray(); + } + + /** + * The default constructor for this extension. Null parameters make + * the element optional (not present). + * + * @param id the KeyIdentifier associated with this extension. + * @param names the GeneralNames associated with this extension + * @param serialNum the CertificateSerialNumber associated with + * this extension. + * @exception IOException on error. + */ + public AuthorityKeyIdentifierExtension(KeyIdentifier kid, GeneralNames name, + SerialNumber sn) + throws IOException { + this.id = kid; + this.names = name; + this.serialNum = sn; + + this.extensionId = PKIXExtensions.AuthorityKey_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public AuthorityKeyIdentifierExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.AuthorityKey_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "AuthorityKeyIdentifierExtension."); + } + + // Note that all the fields in AuthorityKeyIdentifier are defined as + // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting + // in val.data being null. + while ((val.data != null) && (val.data.available() != 0)) { + DerValue opt = val.data.getDerValue(); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + if (opt.isContextSpecific(TAG_ID) && !opt.isConstructed()) { + if (id != null) + throw new IOException("Duplicate KeyIdentifier in " + + "AuthorityKeyIdentifier."); + opt.resetTag(DerValue.tag_OctetString); + id = new KeyIdentifier(opt); + + } else if (opt.isContextSpecific(TAG_NAMES) && + opt.isConstructed()) { + if (names != null) + throw new IOException("Duplicate GeneralNames in " + + "AuthorityKeyIdentifier."); + opt.resetTag(DerValue.tag_Sequence); + names = new GeneralNames(opt); + + } else if (opt.isContextSpecific(TAG_SERIAL_NUM) && + !opt.isConstructed()) { + if (serialNum != null) + throw new IOException("Duplicate SerialNumber in " + + "AuthorityKeyIdentifier."); + opt.resetTag(DerValue.tag_Integer); + serialNum = new SerialNumber(opt); + } else + throw new IOException("Invalid encoding of " + + "AuthorityKeyIdentifierExtension."); + } + } + + /** + * Return the object as a string. + */ + public String toString() { + String s = super.toString() + "AuthorityKeyIdentifier [\n"; + if (id != null) { + s += id.toString(); // id already has a newline + } + if (names != null) { + s += names.toString() + "\n"; + } + if (serialNum != null) { + s += serialNum.toString() + "\n"; + } + return (s + "]\n"); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + extensionId = PKIXExtensions.AuthorityKey_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + if (!(obj instanceof KeyIdentifier)) { + throw new IOException("Attribute value should be of " + + "type KeyIdentifier."); + } + id = (KeyIdentifier)obj; + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of " + + "type GeneralNames."); + } + names = (GeneralNames)obj; + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + if (!(obj instanceof SerialNumber)) { + throw new IOException("Attribute value should be of " + + "type SerialNumber."); + } + serialNum = (SerialNumber)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + return (id); + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + return (names); + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + return (serialNum); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + id = null; + } else if (name.equalsIgnoreCase(AUTH_NAME)) { + names = null; + } else if (name.equalsIgnoreCase(SERIAL_NUMBER)) { + serialNum = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:AuthorityKeyIdentifier."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY_ID); + elements.addElement(AUTH_NAME); + elements.addElement(SERIAL_NUMBER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/BasicConstraintsExtension.java b/src/sun/security/x509/BasicConstraintsExtension.java new file mode 100644 index 00000000..35c8cfc6 --- /dev/null +++ b/src/sun/security/x509/BasicConstraintsExtension.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class represents the Basic Constraints Extension. + * + *

    The basic constraints extension identifies whether the subject of the + * certificate is a CA and how deep a certification path may exist + * through that CA. + * + *

    + * The ASN.1 syntax for this extension is:
    + * BasicConstraints ::= SEQUENCE {
    + *     cA                BOOLEAN DEFAULT FALSE,
    + *     pathLenConstraint INTEGER (0..MAX) OPTIONAL
    + * }
    + * 
    + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + * @see Extension + */ +public class BasicConstraintsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.BasicConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "BasicConstraints"; + public static final String IS_CA = "is_ca"; + public static final String PATH_LEN = "path_len"; + + // Private data members + private boolean ca = false; + private int pathLen = -1; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + if (ca) { + tmp.putBoolean(ca); + // Only encode pathLen when ca == true + if (pathLen >= 0) { + tmp.putInteger(pathLen); + } + } + out.write(DerValue.tag_Sequence, tmp); + this.extensionValue = out.toByteArray(); + } + + /** + * Default constructor for this object. The extension is marked + * critical if the ca flag is true, false otherwise. + * + * @param ca true, if the subject of the Certificate is a CA. + * @param len specifies the depth of the certification path. + */ + public BasicConstraintsExtension(boolean ca, int len) throws IOException { + this(Boolean.valueOf(ca), ca, len); + } + + /** + * Constructor for this object with specified criticality. + * + * @param critical true, if the extension should be marked critical + * @param ca true, if the subject of the Certificate is a CA. + * @param len specifies the depth of the certification path. + */ + public BasicConstraintsExtension(Boolean critical, boolean ca, int len) + throws IOException { + this.ca = ca; + this.pathLen = len; + this.extensionId = PKIXExtensions.BasicConstraints_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical flag indicating if extension is critical or not + * @param value an array containing the DER encoded bytes of the extension. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public BasicConstraintsExtension(Boolean critical, Object value) + throws IOException + { + this.extensionId = PKIXExtensions.BasicConstraints_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of BasicConstraints"); + } + + if (val.data == null || val.data.available() == 0) { + // non-CA cert ("cA" field is FALSE by default), return -1 + return; + } + DerValue opt = val.data.getDerValue(); + if (opt.tag != DerValue.tag_Boolean) { + // non-CA cert ("cA" field is FALSE by default), return -1 + return; + } + + this.ca = opt.getBoolean(); + if (val.data.available() == 0) { + // From PKIX profile: + // Where pathLenConstraint does not appear, there is no + // limit to the allowed length of the certification path. + this.pathLen = Integer.MAX_VALUE; + return; + } + + opt = val.data.getDerValue(); + if (opt.tag != DerValue.tag_Integer) { + throw new IOException("Invalid encoding of BasicConstraints"); + } + this.pathLen = opt.getInteger(); + /* + * Activate this check once again after PKIX profiling + * is a standard and this check no longer imposes an + * interoperability barrier. + * if (ca) { + * if (!this.critical) { + * throw new IOException("Criticality cannot be false for CA."); + * } + * } + */ + } + + /** + * Return user readable form of extension. + */ + public String toString() { + String s = super.toString() + "BasicConstraints:[\n"; + + s += ((ca) ? (" CA:true") : (" CA:false")) + "\n"; + if (pathLen >= 0) { + s += " PathLen:" + pathLen + "\n"; + } else { + s += " PathLen: undefined\n"; + } + return (s + "]\n"); + } + + /** + * Encode this extension value to the output stream. + * + * @param out the DerOutputStream to encode the extension to. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + this.extensionId = PKIXExtensions.BasicConstraints_Id; + if (ca) { + critical = true; + } else { + critical = false; + } + encodeThis(); + } + super.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(IS_CA)) { + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute value should be of type Boolean."); + } + ca = ((Boolean)obj).booleanValue(); + } else if (name.equalsIgnoreCase(PATH_LEN)) { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute value should be of type Integer."); + } + pathLen = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(IS_CA)) { + return (Boolean.valueOf(ca)); + } else if (name.equalsIgnoreCase(PATH_LEN)) { + return (Integer.valueOf(pathLen)); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(IS_CA)) { + ca = false; + } else if (name.equalsIgnoreCase(PATH_LEN)) { + pathLen = -1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:BasicConstraints."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(IS_CA); + elements.addElement(PATH_LEN); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/CRLDistributionPointsExtension.java b/src/sun/security/x509/CRLDistributionPointsExtension.java new file mode 100644 index 00000000..c3814306 --- /dev/null +++ b/src/sun/security/x509/CRLDistributionPointsExtension.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.*; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; + +/** + * Represent the CRL Distribution Points Extension (OID = 2.5.29.31). + *

    + * The CRL distribution points extension identifies how CRL information + * is obtained. The extension SHOULD be non-critical, but the PKIX profile + * recommends support for this extension by CAs and applications. + *

    + * For PKIX, if the cRLDistributionPoints extension contains a + * DistributionPointName of type URI, the following semantics MUST be + * assumed: the URI is a pointer to the current CRL for the associated + * reasons and will be issued by the associated cRLIssuer. The + * expected values for the URI conform to the following rules. The + * name MUST be a non-relative URL, and MUST follow the URL syntax and + * encoding rules specified in [RFC 1738]. The name must include both + * a scheme (e.g., "http" or "ftp") and a scheme-specific-part. The + * scheme- specific-part must include a fully qualified domain name or + * IP address as the host. As specified in [RFC 1738], the scheme + * name is not case-sensitive (e.g., "http" is equivalent to "HTTP"). + * The host part is also not case-sensitive, but other components of + * the scheme-specific-part may be case-sensitive. When comparing + * URIs, conforming implementations MUST compare the scheme and host + * without regard to case, but assume the remainder of the + * scheme-specific-part is case sensitive. Processing rules for other + * values are not defined by this specification. If the + * distributionPoint omits reasons, the CRL MUST include revocations + * for all reasons. If the distributionPoint omits cRLIssuer, the CRL + * MUST be issued by the CA that issued the certificate. + *

    + * The ASN.1 definition for this is: + *

    + * id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::=  { id-ce 31 }
    + *
    + * cRLDistributionPoints ::= {
    + *      CRLDistPointsSyntax }
    + *
    + * CRLDistPointsSyntax ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
    + * 
    + *

    + * @author Anne Anderson + * @author Andreas Sterbenz + * @since 1.4.2 + * @see DistributionPoint + * @see Extension + * @see CertAttrSet + */ +public class CRLDistributionPointsExtension extends Extension + implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.CRLDistributionPoints"; + + /** + * Attribute name. + */ + public static final String NAME = "CRLDistributionPoints"; + public static final String POINTS = "points"; + + /** + * The List of DistributionPoint objects. + */ + private List distributionPoints; + + private String extensionName; + + /** + * Create a CRLDistributionPointsExtension from a List of + * DistributionPoint; the criticality is set to false. + * + * @param distributionPoints the list of distribution points + * @throws IOException on error + */ + public CRLDistributionPointsExtension( + List distributionPoints) throws IOException { + + this(false, distributionPoints); + } + + /** + * Create a CRLDistributionPointsExtension from a List of + * DistributionPoint. + * + * @param isCritical the criticality setting. + * @param distributionPoints the list of distribution points + * @throws IOException on error + */ + public CRLDistributionPointsExtension(boolean isCritical, + List distributionPoints) throws IOException { + + this(PKIXExtensions.CRLDistributionPoints_Id, isCritical, + distributionPoints, NAME); + } + + /** + * Creates the extension (also called by the subclass). + */ + protected CRLDistributionPointsExtension(ObjectIdentifier extensionId, + boolean isCritical, List distributionPoints, + String extensionName) throws IOException { + + this.extensionId = extensionId; + this.critical = isCritical; + this.distributionPoints = distributionPoints; + encodeThis(); + this.extensionName = extensionName; + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public CRLDistributionPointsExtension(Boolean critical, Object value) + throws IOException { + this(PKIXExtensions.CRLDistributionPoints_Id, critical, value, NAME); + } + + /** + * Creates the extension (also called by the subclass). + */ + protected CRLDistributionPointsExtension(ObjectIdentifier extensionId, + Boolean critical, Object value, String extensionName) + throws IOException { + + this.extensionId = extensionId; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) { + throw new IOException("Illegal argument type"); + } + + extensionValue = (byte[])value; + DerValue val = new DerValue(extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + extensionName + + " extension."); + } + distributionPoints = new ArrayList(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + DistributionPoint point = new DistributionPoint(seq); + distributionPoints.add(point); + } + this.extensionName = extensionName; + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return extensionName; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + encode(out, PKIXExtensions.CRLDistributionPoints_Id, false); + } + + /** + * Write the extension to the DerOutputStream. + * (Also called by the subclass) + */ + protected void encode(OutputStream out, ObjectIdentifier extensionId, + boolean isCritical) throws IOException { + + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = extensionId; + this.critical = isCritical; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with instanceof + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(POINTS)) { + if (!(obj instanceof List)) { + throw new IOException("Attribute value should be of type List."); + } + distributionPoints = (List)obj; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:" + extensionName + "."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public List get(String name) throws IOException { + if (name.equalsIgnoreCase(POINTS)) { + return distributionPoints; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:" + extensionName + "."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(POINTS)) { + distributionPoints = new ArrayList(); + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:" + extensionName + "."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(POINTS); + return elements.elements(); + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (distributionPoints.isEmpty()) { + this.extensionValue = null; + } else { + DerOutputStream pnts = new DerOutputStream(); + for (DistributionPoint point : distributionPoints) { + point.encode(pnts); + } + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, pnts); + this.extensionValue = seq.toByteArray(); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + return super.toString() + extensionName + " [\n " + + distributionPoints + "]\n"; + } + +} diff --git a/src/sun/security/x509/CRLExtensions.java b/src/sun/security/x509/CRLExtensions.java new file mode 100644 index 00000000..5e7f42de --- /dev/null +++ b/src/sun/security/x509/CRLExtensions.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.TreeMap; + +import sun.security.util.*; + +/** + * This class defines the CRL Extensions. + * It is used for both CRL Extensions and CRL Entry Extensions, + * which are defined are follows: + *

    + * TBSCertList  ::=  SEQUENCE  {
    + *    version              Version OPTIONAL,   -- if present, must be v2
    + *    signature            AlgorithmIdentifier,
    + *    issuer               Name,
    + *    thisUpdate           Time,
    + *    nextUpdate           Time  OPTIONAL,
    + *    revokedCertificates  SEQUENCE OF SEQUENCE  {
    + *        userCertificate         CertificateSerialNumber,
    + *        revocationDate          Time,
    + *        crlEntryExtensions      Extensions OPTIONAL  -- if present, must be v2
    + *    }  OPTIONAL,
    + *    crlExtensions        [0] EXPLICIT Extensions OPTIONAL  -- if present, must be v2
    + * }
    + * 
    + * + * @author Hemma Prafullchandra + */ +public class CRLExtensions { + + private Map map = Collections.synchronizedMap( + new TreeMap()); + private boolean unsupportedCritExt = false; + + /** + * Default constructor. + */ + public CRLExtensions() { } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the Extension from, i.e. the + * sequence of extensions. + * @exception CRLException on decoding errors. + */ + public CRLExtensions(DerInputStream in) throws CRLException { + init(in); + } + + // helper routine + private void init(DerInputStream derStrm) throws CRLException { + try { + DerInputStream str = derStrm; + + byte nextByte = (byte)derStrm.peekByte(); + // check for context specific byte 0; skip it + if (((nextByte & 0x0c0) == 0x080) && + ((nextByte & 0x01f) == 0x000)) { + DerValue val = str.getDerValue(); + str = val.data; + } + + DerValue[] exts = str.getSequence(5); + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } catch (IOException e) { + throw new CRLException("Parsing error: " + e.toString()); + } + } + + private static final Class[] PARAMS = {Boolean.class, Object.class}; + + // Parse the encoded extension + private void parseExtension(Extension ext) throws CRLException { + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass == null) { // Unsupported extension + if (ext.isCritical()) + unsupportedCritExt = true; + if (map.put(ext.getExtensionId().toString(), ext) != null) + throw new CRLException("Duplicate extensions not allowed"); + return; + } + Constructor cons = extClass.getConstructor(PARAMS); + Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()), + ext.getExtensionValue()}; + CertAttrSet crlExt = (CertAttrSet)cons.newInstance(passed); + if (map.put(crlExt.getName(), (Extension)crlExt) != null) { + throw new CRLException("Duplicate extensions not allowed"); + } + } catch (InvocationTargetException invk) { + throw new CRLException(invk.getTargetException().getMessage()); + } catch (Exception e) { + throw new CRLException(e.toString()); + } + } + + /** + * Encode the extensions in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @param isExplicit the tag indicating whether this is an entry + * extension (false) or a CRL extension (true). + * @exception CRLException on encoding errors. + */ + public void encode(OutputStream out, boolean isExplicit) + throws CRLException { + try { + DerOutputStream extOut = new DerOutputStream(); + Collection allExts = map.values(); + Object[] objs = allExts.toArray(); + + for (int i = 0; i < objs.length; i++) { + if (objs[i] instanceof CertAttrSet) + ((CertAttrSet)objs[i]).encode(extOut); + else if (objs[i] instanceof Extension) + ((Extension)objs[i]).encode(extOut); + else + throw new CRLException("Illegal extension object"); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, extOut); + + DerOutputStream tmp = new DerOutputStream(); + if (isExplicit) + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)0), seq); + else + tmp = seq; + + out.write(tmp.toByteArray()); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.toString()); + } catch (CertificateException e) { + throw new CRLException("Encoding error: " + e.toString()); + } + } + + /** + * Get the extension with this alias. + * + * @param alias the identifier string for the extension to retrieve. + */ + public Extension get(String alias) { + X509AttributeName attr = new X509AttributeName(alias); + String name; + String id = attr.getPrefix(); + if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified + int index = alias.lastIndexOf("."); + name = alias.substring(index + 1); + } else + name = alias; + return map.get(name); + } + + /** + * Set the extension value with this alias. + * + * @param alias the identifier string for the extension to set. + * @param obj the Object to set the extension identified by the + * alias. + */ + public void set(String alias, Object obj) { + map.put(alias, (Extension)obj); + } + + /** + * Delete the extension value with this alias. + * + * @param alias the identifier string for the extension to delete. + */ + public void delete(String alias) { + map.remove(alias); + } + + /** + * Return an enumeration of the extensions. + * @return an enumeration of the extensions in this CRL. + */ + public Enumeration getElements() { + return Collections.enumeration(map.values()); + } + + /** + * Return a collection view of the extensions. + * @return a collection view of the extensions in this CRL. + */ + public Collection getAllExtensions() { + return map.values(); + } + + /** + * Return true if a critical extension is found that is + * not supported, otherwise return false. + */ + public boolean hasUnsupportedCriticalExtension() { + return unsupportedCritExt; + } + + /** + * Compares this CRLExtensions for equality with the specified + * object. If the other object is an + * instanceof CRLExtensions, then + * all the entries are compared with the entries from this. + * + * @param other the object to test for equality with this CRLExtensions. + * @return true iff all the entries match that of the Other, + * false otherwise. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof CRLExtensions)) + return false; + Collection otherC = + ((CRLExtensions)other).getAllExtensions(); + Object[] objs = otherC.toArray(); + + int len = objs.length; + if (len != map.size()) + return false; + + Extension otherExt, thisExt; + String key = null; + for (int i = 0; i < len; i++) { + if (objs[i] instanceof CertAttrSet) + key = ((CertAttrSet)objs[i]).getName(); + otherExt = (Extension)objs[i]; + if (key == null) + key = otherExt.getExtensionId().toString(); + thisExt = map.get(key); + if (thisExt == null) + return false; + if (! thisExt.equals(otherExt)) + return false; + } + return true; + } + + /** + * Returns a hashcode value for this CRLExtensions. + * + * @return the hashcode value. + */ + public int hashCode() { + return map.hashCode(); + } + + /** + * Returns a string representation of this CRLExtensions object + * in the form of a set of entries, enclosed in braces and separated + * by the ASCII characters "" (comma and space). + *

    Overrides to toString method of Object. + * + * @return a string representation of this CRLExtensions. + */ + public String toString() { + return map.toString(); + } +} diff --git a/src/sun/security/x509/CRLNumberExtension.java b/src/sun/security/x509/CRLNumberExtension.java new file mode 100644 index 00000000..9649df6a --- /dev/null +++ b/src/sun/security/x509/CRLNumberExtension.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * Represent the CRL Number Extension. + * + *

    This extension, if present, conveys a monotonically increasing + * sequence number for each CRL issued by a given CA through a specific + * CA X.500 Directory entry or CRL distribution point. This extension + * allows users to easily determine when a particular CRL supersedes + * another CRL. + * + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class CRLNumberExtension extends Extension +implements CertAttrSet { + + /** + * Attribute name. + */ + public static final String NAME = "CRLNumber"; + public static final String NUMBER = "value"; + + private static final String LABEL = "CRL Number"; + + private BigInteger crlNumber = null; + private String extensionName; + private String extensionLabel; + + // Encode this extension value + private void encodeThis() throws IOException { + if (crlNumber == null) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + os.putInteger(this.crlNumber); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a CRLNumberExtension with the integer value . + * The criticality is set to false. + * + * @param crlNum the value to be set for the extension. + */ + public CRLNumberExtension(int crlNum) throws IOException { + this(PKIXExtensions.CRLNumber_Id, false, BigInteger.valueOf(crlNum), + NAME, LABEL); + } + + /** + * Create a CRLNumberExtension with the BigInteger value . + * The criticality is set to false. + * + * @param crlNum the value to be set for the extension. + */ + public CRLNumberExtension(BigInteger crlNum) throws IOException { + this(PKIXExtensions.CRLNumber_Id, false, crlNum, NAME, LABEL); + } + + /** + * Creates the extension (also called by the subclass). + */ + protected CRLNumberExtension(ObjectIdentifier extensionId, + boolean isCritical, BigInteger crlNum, String extensionName, + String extensionLabel) throws IOException { + + this.extensionId = extensionId; + this.critical = isCritical; + this.crlNumber = crlNum; + this.extensionName = extensionName; + this.extensionLabel = extensionLabel; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public CRLNumberExtension(Boolean critical, Object value) + throws IOException { + this(PKIXExtensions.CRLNumber_Id, critical, value, NAME, LABEL); + } + + /** + * Creates the extension (also called by the subclass). + */ + protected CRLNumberExtension(ObjectIdentifier extensionId, + Boolean critical, Object value, String extensionName, + String extensionLabel) throws IOException { + + this.extensionId = extensionId; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.crlNumber = val.getBigInteger(); + this.extensionName = extensionName; + this.extensionLabel = extensionLabel; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (!(obj instanceof BigInteger)) { + throw new IOException("Attribute must be of type BigInteger."); + } + crlNumber = (BigInteger)obj; + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:" + extensionName + "."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public BigInteger get(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + if (crlNumber == null) return null; + else return crlNumber; + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:" + extensionName + "."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + crlNumber = null; + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:" + extensionName + "."); + } + encodeThis(); + } + + /** + * Returns a printable representation of the CRLNumberExtension. + */ + public String toString() { + String s = super.toString() + extensionLabel + ": " + + ((crlNumber == null) ? "" : Debug.toHexString(crlNumber)) + + "\n"; + return (s); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + encode(out, PKIXExtensions.CRLNumber_Id, true); + } + + /** + * Write the extension to the DerOutputStream. + * (Also called by the subclass) + */ + protected void encode(OutputStream out, ObjectIdentifier extensionId, + boolean isCritical) throws IOException { + + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = extensionId; + this.critical = isCritical; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NUMBER); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (extensionName); + } +} diff --git a/src/sun/security/x509/CRLReasonCodeExtension.java b/src/sun/security/x509/CRLReasonCodeExtension.java new file mode 100644 index 00000000..f5a144ef --- /dev/null +++ b/src/sun/security/x509/CRLReasonCodeExtension.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CRLReason; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * The reasonCode is a non-critical CRL entry extension that identifies + * the reason for the certificate revocation. + * @author Hemma Prafullchandra + * @see CRLReason + * @see Extension + * @see CertAttrSet + */ +public class CRLReasonCodeExtension extends Extension + implements CertAttrSet { + + /** + * Attribute name + */ + public static final String NAME = "CRLReasonCode"; + public static final String REASON = "reason"; + + private static CRLReason[] values = CRLReason.values(); + + private int reasonCode = 0; + + private void encodeThis() throws IOException { + if (reasonCode == 0) { + this.extensionValue = null; + return; + } + DerOutputStream dos = new DerOutputStream(); + dos.putEnumerated(reasonCode); + this.extensionValue = dos.toByteArray(); + } + + /** + * Create a CRLReasonCodeExtension with the passed in reason. + * Criticality automatically set to false. + * + * @param reason the enumerated value for the reason code. + */ + public CRLReasonCodeExtension(int reason) throws IOException { + this(false, reason); + } + + /** + * Create a CRLReasonCodeExtension with the passed in reason. + * + * @param critical true if the extension is to be treated as critical. + * @param reason the enumerated value for the reason code. + */ + public CRLReasonCodeExtension(boolean critical, int reason) + throws IOException { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = critical; + this.reasonCode = reason; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public CRLReasonCodeExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.reasonCode = val.getEnumerated(); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute must be of type Integer."); + } + if (name.equalsIgnoreCase(REASON)) { + reasonCode = ((Integer)obj).intValue(); + } else { + throw new IOException + ("Name not supported by CRLReasonCodeExtension"); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Integer get(String name) throws IOException { + if (name.equalsIgnoreCase(REASON)) { + return new Integer(reasonCode); + } else { + throw new IOException + ("Name not supported by CRLReasonCodeExtension"); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(REASON)) { + reasonCode = 0; + } else { + throw new IOException + ("Name not supported by CRLReasonCodeExtension"); + } + encodeThis(); + } + + /** + * Returns a printable representation of the Reason code. + */ + public String toString() { + return super.toString() + " Reason Code: " + getReasonCode(); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.ReasonCode_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(REASON); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Return the reason as a CRLReason enum. + */ + public CRLReason getReasonCode() { + // if out-of-range, return UNSPECIFIED + if (reasonCode > 0 && reasonCode < values.length) { + return values[reasonCode]; + } else { + return CRLReason.UNSPECIFIED; + } + } +} diff --git a/src/sun/security/x509/CertAttrSet.java b/src/sun/security/x509/CertAttrSet.java new file mode 100644 index 00000000..8b5f5e6b --- /dev/null +++ b/src/sun/security/x509/CertAttrSet.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.util.Enumeration; + +/** + * This interface defines the methods required of a certificate attribute. + * Examples of X.509 certificate attributes are Validity, Issuer_Name, and + * Subject Name. A CertAttrSet may comprise one attribute or many + * attributes. + *

    + * A CertAttrSet itself can also be comprised of other sub-sets. + * In the case of X.509 V3 certificates, for example, the "extensions" + * attribute has subattributes, such as those for KeyUsage and + * AuthorityKeyIdentifier. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertificateException + */ +public interface CertAttrSet { + /** + * Returns a short string describing this certificate attribute. + * + * @return value of this certificate attribute in + * printable form. + */ + String toString(); + + /** + * Encodes the attribute to the output stream in a format + * that can be parsed by the decode method. + * + * @param out the OutputStream to encode the attribute to. + * + * @exception CertificateException on encoding or validity errors. + * @exception IOException on other errors. + */ + void encode(OutputStream out) + throws CertificateException, IOException; + + /** + * Sets an attribute value within this CertAttrSet. + * + * @param name the name of the attribute (e.g. "x509.info.key") + * @param obj the attribute object. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + void set(String name, Object obj) + throws CertificateException, IOException; + + /** + * Gets an attribute value for this CertAttrSet. + * + * @param name the name of the attribute to return. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + Object get(String name) + throws CertificateException, IOException; + + /** + * Deletes an attribute value from this CertAttrSet. + * + * @param name the name of the attribute to delete. + * + * @exception CertificateException on attribute handling errors. + * @exception IOException on other errors. + */ + void delete(String name) + throws CertificateException, IOException; + + /** + * Returns an enumeration of the names of the attributes existing within + * this attribute. + * + * @return an enumeration of the attribute names. + */ + Enumeration getElements(); + + /** + * Returns the name (identifier) of this CertAttrSet. + * + * @return the name of this CertAttrSet. + */ + String getName(); +} diff --git a/src/sun/security/x509/CertException.java b/src/sun/security/x509/CertException.java new file mode 100644 index 00000000..585d1f82 --- /dev/null +++ b/src/sun/security/x509/CertException.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +/** + * CertException indicates one of a variety of certificate problems. + * + * @deprecated use one of Exceptions defined in the java.security.cert + * package. + * + * @see java.security.Certificate + * + * + * @author David Brownell + */ +@Deprecated +public class CertException extends SecurityException { + + private static final long serialVersionUID = 6930793039696446142L; + + // Zero is reserved. + + /** Indicates that the signature in the certificate is not valid. */ + public static final int verf_INVALID_SIG = 1; + + /** Indicates that the certificate was revoked, and so is invalid. */ + public static final int verf_INVALID_REVOKED = 2; + + /** Indicates that the certificate is not yet valid. */ + public static final int verf_INVALID_NOTBEFORE = 3; + + /** Indicates that the certificate has expired and so is not valid. */ + public static final int verf_INVALID_EXPIRED = 4; + + /** Indicates that a certificate authority in the certification + * chain is not trusted. */ + public static final int verf_CA_UNTRUSTED = 5; + + /** Indicates that the certification chain is too long. */ + public static final int verf_CHAIN_LENGTH = 6; + + /** Indicates an error parsing the ASN.1/DER encoding of the certificate. */ + public static final int verf_PARSE_ERROR = 7; + + /** Indicates an error constructing a certificate or certificate chain. */ + public static final int err_CONSTRUCTION = 8; + + /** Indicates a problem with the public key */ + public static final int err_INVALID_PUBLIC_KEY = 9; + + /** Indicates a problem with the certificate version */ + public static final int err_INVALID_VERSION = 10; + + /** Indicates a problem with the certificate format */ + public static final int err_INVALID_FORMAT = 11; + + /** Indicates a problem with the certificate encoding */ + public static final int err_ENCODING = 12; + + // Private data members + private int verfCode; + private String moreData; + + + /** + * Constructs a certificate exception using an error code + * (verf_*) and a string describing the context + * of the error. + */ + public CertException(int code, String moredata) + { + verfCode = code; + moreData = moredata; + } + + /** + * Constructs a certificate exception using just an error code, + * without a string describing the context. + */ + public CertException(int code) + { + verfCode = code; + } + + /** + * Returns the error code with which the exception was created. + */ + public int getVerfCode() { return verfCode; } + + /** + * Returns a string describing the context in which the exception + * was reported. + */ + public String getMoreData() { return moreData; } + + /** + * Return a string corresponding to the error code used to create + * this exception. + */ + public String getVerfDescription() + { + switch (verfCode) { + case verf_INVALID_SIG: + return "The signature in the certificate is not valid."; + case verf_INVALID_REVOKED: + return "The certificate has been revoked."; + case verf_INVALID_NOTBEFORE: + return "The certificate is not yet valid."; + case verf_INVALID_EXPIRED: + return "The certificate has expired."; + case verf_CA_UNTRUSTED: + return "The Authority which issued the certificate is not trusted."; + case verf_CHAIN_LENGTH: + return "The certificate path to a trusted authority is too long."; + case verf_PARSE_ERROR: + return "The certificate could not be parsed."; + case err_CONSTRUCTION: + return "There was an error when constructing the certificate."; + case err_INVALID_PUBLIC_KEY: + return "The public key was not in the correct format."; + case err_INVALID_VERSION: + return "The certificate has an invalid version number."; + case err_INVALID_FORMAT: + return "The certificate has an invalid format."; + case err_ENCODING: + return "Problem encountered while encoding the data."; + + default: + return "Unknown code: " + verfCode; + } + } + + /** + * Returns a string describing the certificate exception. + */ + public String toString() + { + return "[Certificate Exception: " + getMessage() + "]"; + } + + /** + * Returns a string describing the certificate exception. + */ + public String getMessage() + { + return getVerfDescription() + + ( (moreData != null) + ? ( "\n (" + moreData + ")" ) : "" ); + } +} diff --git a/src/sun/security/x509/CertParseError.java b/src/sun/security/x509/CertParseError.java new file mode 100644 index 00000000..6e83f5e8 --- /dev/null +++ b/src/sun/security/x509/CertParseError.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +/** + * CertException indicates one of a variety of certificate problems. + * @deprecated use one of the Exceptions defined in the + * java.security.cert package. + * + * @author David Brownell + */ +@Deprecated +class CertParseError extends CertException +{ + private static final long serialVersionUID = -4559645519017017804L; + + CertParseError (String where) + { + super (CertException.verf_PARSE_ERROR, where); + } +} diff --git a/src/sun/security/x509/CertificateAlgorithmId.java b/src/sun/security/x509/CertificateAlgorithmId.java new file mode 100644 index 00000000..d54f8f68 --- /dev/null +++ b/src/sun/security/x509/CertificateAlgorithmId.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the AlgorithmId for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class CertificateAlgorithmId implements CertAttrSet { + private AlgorithmId algId; + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.algorithmID"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "algorithmID"; + + /** + * Identifier to be used with get, set, and delete methods. When + * using this identifier the associated object being passed in or + * returned is an instance of AlgorithmId. + * @see AlgorithmId + */ + public static final String ALGORITHM = "algorithm"; + + /** + * Default constructor for the certificate attribute. + * + * @param algId the Algorithm identifier + */ + public CertificateAlgorithmId(AlgorithmId algId) { + this.algId = algId; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateAlgorithmId(DerInputStream in) throws IOException { + DerValue val = in.getDerValue(); + algId = AlgorithmId.parse(val); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateAlgorithmId(InputStream in) throws IOException { + DerValue val = new DerValue(in); + algId = AlgorithmId.parse(val); + } + + /** + * Return the algorithm identifier as user readable string. + */ + public String toString() { + if (algId == null) return ""; + return (algId.toString() + + ", OID = " + (algId.getOID()).toString() + "\n"); + } + + /** + * Encode the algorithm identifier in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + algId.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof AlgorithmId)) { + throw new IOException("Attribute must be of type AlgorithmId."); + } + if (name.equalsIgnoreCase(ALGORITHM)) { + algId = (AlgorithmId)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Get the attribute value. + */ + public AlgorithmId get(String name) throws IOException { + if (name.equalsIgnoreCase(ALGORITHM)) { + return (algId); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ALGORITHM)) { + algId = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateAlgorithmId."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ALGORITHM); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/CertificateExtensions.java b/src/sun/security/x509/CertificateExtensions.java new file mode 100644 index 00000000..be0c8c88 --- /dev/null +++ b/src/sun/security/x509/CertificateExtensions.java @@ -0,0 +1,381 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.security.cert.CertificateException; +import java.util.*; + +import sun.misc.HexDumpEncoder; + +import sun.security.util.*; + +/** + * This class defines the Extensions attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateExtensions implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions"; + /** + * name + */ + public static final String NAME = "extensions"; + + private static final Debug debug = Debug.getInstance("x509"); + + private Map map = Collections.synchronizedMap( + new TreeMap()); + private boolean unsupportedCritExt = false; + + private Map unparseableExtensions; + + /** + * Default constructor. + */ + public CertificateExtensions() { } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the Extension from. + * @exception IOException on decoding errors. + */ + public CertificateExtensions(DerInputStream in) throws IOException { + init(in); + } + + // helper routine + private void init(DerInputStream in) throws IOException { + + DerValue[] exts = in.getSequence(5); + + for (int i = 0; i < exts.length; i++) { + Extension ext = new Extension(exts[i]); + parseExtension(ext); + } + } + + private static Class[] PARAMS = {Boolean.class, Object.class}; + + // Parse the encoded extension + private void parseExtension(Extension ext) throws IOException { + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass == null) { // Unsupported extension + if (ext.isCritical()) { + unsupportedCritExt = true; + } + if (map.put(ext.getExtensionId().toString(), ext) == null) { + return; + } else { + throw new IOException("Duplicate extensions not allowed"); + } + } + Constructor cons = extClass.getConstructor(PARAMS); + + Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()), + ext.getExtensionValue()}; + CertAttrSet certExt = (CertAttrSet) + cons.newInstance(passed); + if (map.put(certExt.getName(), (Extension)certExt) != null) { + throw new IOException("Duplicate extensions not allowed"); + } + } catch (InvocationTargetException invk) { + Throwable e = invk.getTargetException(); + if (ext.isCritical() == false) { + // ignore errors parsing non-critical extensions + if (unparseableExtensions == null) { + unparseableExtensions = new TreeMap(); + } + unparseableExtensions.put(ext.getExtensionId().toString(), + new UnparseableExtension(ext, e)); + if (debug != null) { + debug.println("Error parsing extension: " + ext); + e.printStackTrace(); + HexDumpEncoder h = new HexDumpEncoder(); + System.err.println(h.encodeBuffer(ext.getExtensionValue())); + } + return; + } + if (e instanceof IOException) { + throw (IOException)e; + } else { + throw new IOException(e); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e); + } + } + + /** + * Encode the extensions in DER form to the stream, setting + * the context specific tag as needed in the X.509 v3 certificate. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception CertificateException on encoding errors. + * @exception IOException on errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + encode(out, false); + } + + /** + * Encode the extensions in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @param isCertReq if true then no context specific tag is added. + * @exception CertificateException on encoding errors. + * @exception IOException on errors. + */ + public void encode(OutputStream out, boolean isCertReq) + throws CertificateException, IOException { + DerOutputStream extOut = new DerOutputStream(); + Collection allExts = map.values(); + Object[] objs = allExts.toArray(); + + for (int i = 0; i < objs.length; i++) { + if (objs[i] instanceof CertAttrSet) + ((CertAttrSet)objs[i]).encode(extOut); + else if (objs[i] instanceof Extension) + ((Extension)objs[i]).encode(extOut); + else + throw new CertificateException("Illegal extension object"); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, extOut); + + DerOutputStream tmp; + if (!isCertReq) { // certificate + tmp = new DerOutputStream(); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)3), + seq); + } else + tmp = seq; // pkcs#10 certificateRequest + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + * @param name the extension name used in the cache. + * @param obj the object to set. + * @exception IOException if the object could not be cached. + */ + public void set(String name, Object obj) throws IOException { + if (obj instanceof Extension) { + map.put(name, (Extension)obj); + } else { + throw new IOException("Unknown extension type."); + } + } + + /** + * Get the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public Extension get(String name) throws IOException { + Extension obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + return (obj); + } + + // Similar to get(String), but throw no exception, might return null. + // Used in X509CertImpl::getExtension(OID). + Extension getExtension(String name) { + return map.get(name); + } + + /** + * Delete the attribute value. + * @param name the extension name used in the lookup. + * @exception IOException if named extension is not found. + */ + public void delete(String name) throws IOException { + Object obj = map.get(name); + if (obj == null) { + throw new IOException("No extension found with name " + name); + } + map.remove(name); + } + + public String getNameByOid(ObjectIdentifier oid) throws IOException { + for (String name: map.keySet()) { + if (map.get(name).getExtensionId().equals((Object)oid)) { + return name; + } + } + return null; + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + return Collections.enumeration(map.values()); + } + + /** + * Return a collection view of the extensions. + * @return a collection view of the extensions in this Certificate. + */ + public Collection getAllExtensions() { + return map.values(); + } + + public Map getUnparseableExtensions() { + if (unparseableExtensions == null) { + return Collections.emptyMap(); + } else { + return unparseableExtensions; + } + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Return true if a critical extension is found that is + * not supported, otherwise return false. + */ + public boolean hasUnsupportedCriticalExtension() { + return unsupportedCritExt; + } + + /** + * Compares this CertificateExtensions for equality with the specified + * object. If the other object is an + * instanceof CertificateExtensions, then + * all the entries are compared with the entries from this. + * + * @param other the object to test for equality with this + * CertificateExtensions. + * @return true iff all the entries match that of the Other, + * false otherwise. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof CertificateExtensions)) + return false; + Collection otherC = + ((CertificateExtensions)other).getAllExtensions(); + Object[] objs = otherC.toArray(); + + int len = objs.length; + if (len != map.size()) + return false; + + Extension otherExt, thisExt; + String key = null; + for (int i = 0; i < len; i++) { + if (objs[i] instanceof CertAttrSet) + key = ((CertAttrSet)objs[i]).getName(); + otherExt = (Extension)objs[i]; + if (key == null) + key = otherExt.getExtensionId().toString(); + thisExt = map.get(key); + if (thisExt == null) + return false; + if (! thisExt.equals(otherExt)) + return false; + } + return this.getUnparseableExtensions().equals( + ((CertificateExtensions)other).getUnparseableExtensions()); + } + + /** + * Returns a hashcode value for this CertificateExtensions. + * + * @return the hashcode value. + */ + public int hashCode() { + return map.hashCode() + getUnparseableExtensions().hashCode(); + } + + /** + * Returns a string representation of this CertificateExtensions + * object in the form of a set of entries, enclosed in braces and separated + * by the ASCII characters "" (comma and space). + *

    Overrides to toString method of Object. + * + * @return a string representation of this CertificateExtensions. + */ + public String toString() { + return map.toString(); + } + +} + +class UnparseableExtension extends Extension { + private String name; + private Throwable why; + + public UnparseableExtension(Extension ext, Throwable why) { + super(ext); + + name = ""; + try { + Class extClass = OIDMap.getClass(ext.getExtensionId()); + if (extClass != null) { + Field field = extClass.getDeclaredField("NAME"); + name = (String)(field.get(null)) + " "; + } + } catch (Exception e) { + // If we cannot find the name, just ignore it + } + + this.why = why; + } + + @Override public String toString() { + return super.toString() + + "Unparseable " + name + "extension due to\n" + why + "\n\n" + + new HexDumpEncoder().encodeBuffer(getExtensionValue()); + } +} diff --git a/src/sun/security/x509/CertificateIssuerExtension.java b/src/sun/security/x509/CertificateIssuerExtension.java new file mode 100644 index 00000000..b7739707 --- /dev/null +++ b/src/sun/security/x509/CertificateIssuerExtension.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * Represents the CRL Certificate Issuer Extension (OID = 2.5.29.29). + *

    + * The CRL certificate issuer extension identifies the certificate issuer + * associated with an entry in an indirect CRL, i.e. a CRL that has the + * indirectCRL indicator set in its issuing distribution point extension. If + * this extension is not present on the first entry in an indirect CRL, the + * certificate issuer defaults to the CRL issuer. On subsequent entries + * in an indirect CRL, if this extension is not present, the certificate + * issuer for the entry is the same as that for the preceding entry. + *

    + * If used by conforming CRL issuers, this extension is always + * critical. If an implementation ignored this extension it could not + * correctly attribute CRL entries to certificates. PKIX (RFC 3280) + * RECOMMENDS that implementations recognize this extension. + *

    + * The ASN.1 definition for this is: + *

    + * id-ce-certificateIssuer   OBJECT IDENTIFIER ::= { id-ce 29 }
    + *
    + * certificateIssuer ::=     GeneralNames
    + * 
    + * + * @author Anne Anderson + * @author Sean Mullan + * @since 1.5 + * @see Extension + * @see CertAttrSet + */ +public class CertificateIssuerExtension extends Extension + implements CertAttrSet { + + /** + * Attribute names. + */ + public static final String NAME = "CertificateIssuer"; + public static final String ISSUER = "issuer"; + + private GeneralNames names; + + /** + * Encode this extension + */ + private void encodeThis() throws IOException { + if (names == null || names.isEmpty()) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + names.encode(os); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a CertificateIssuerExtension containing the specified issuer name. + * Criticality is automatically set to true. + * + * @param issuer the certificate issuer + * @throws IOException on error + */ + public CertificateIssuerExtension(GeneralNames issuer) throws IOException { + this.extensionId = PKIXExtensions.CertificateIssuer_Id; + this.critical = true; + this.names = issuer; + encodeThis(); + } + + /** + * Create a CertificateIssuerExtension from the specified DER encoded + * value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value + * @throws ClassCastException if value is not an array of bytes + * @throws IOException on error + */ + public CertificateIssuerExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.CertificateIssuer_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.names = new GeneralNames(val); + } + + /** + * Set the attribute value. + * + * @throws IOException on error + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(ISSUER)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value must be of type " + + "GeneralNames"); + } + this.names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuer"); + } + encodeThis(); + } + + /** + * Gets the attribute value. + * + * @throws IOException on error + */ + public GeneralNames get(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER)) { + return names; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuer"); + } + } + + /** + * Deletes the attribute value. + * + * @throws IOException on error + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuer"); + } + encodeThis(); + } + + /** + * Returns a printable representation of the certificate issuer. + */ + public String toString() { + return super.toString() + "Certificate Issuer [\n" + + String.valueOf(names) + "]\n"; + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to + * @exception IOException on encoding errors + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.CertificateIssuer_Id; + critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ISSUER); + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } +} diff --git a/src/sun/security/x509/CertificateIssuerName.java b/src/sun/security/x509/CertificateIssuerName.java new file mode 100644 index 00000000..9c82d215 --- /dev/null +++ b/src/sun/security/x509/CertificateIssuerName.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.*; + +/** + * This class defines the X500Name attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateIssuerName implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.issuer"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "issuer"; + public static final String DN_NAME = "dname"; + + // accessor name for cached X500Principal only + // do not allow a set() of this value, do not advertise with getElements() + public static final String DN_PRINCIPAL = "x500principal"; + + // Private data member + private X500Name dnName; + + // cached X500Principal version of the name + private X500Principal dnPrincipal; + + /** + * Default constructor for the certificate attribute. + * + * @param name the X500Name + */ + public CertificateIssuerName(X500Name name) { + this.dnName = name; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerName(DerInputStream in) throws IOException { + dnName = new X500Name(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateIssuerName(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + dnName = new X500Name(derVal); + } + + /** + * Return the name as user readable string. + */ + public String toString() { + if (dnName == null) return ""; + return(dnName.toString()); + } + + /** + * Encode the name in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + dnName.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof X500Name)) { + throw new IOException("Attribute must be of type X500Name."); + } + if (name.equalsIgnoreCase(DN_NAME)) { + this.dnName = (X500Name)obj; + this.dnPrincipal = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + return(dnName); + } else if (name.equalsIgnoreCase(DN_PRINCIPAL)) { + if ((dnPrincipal == null) && (dnName != null)) { + dnPrincipal = dnName.asX500Principal(); + } + return dnPrincipal; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + dnName = null; + dnPrincipal = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateIssuerName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DN_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/src/sun/security/x509/CertificatePoliciesExtension.java b/src/sun/security/x509/CertificatePoliciesExtension.java new file mode 100644 index 00000000..4f04bedd --- /dev/null +++ b/src/sun/security/x509/CertificatePoliciesExtension.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * This class defines the certificate policies extension which specifies the + * policies under which the certificate has been issued + * and the purposes for which the certificate may be used. + *

    + * Applications with specific policy requirements are expected to have a + * list of those policies which they will accept and to compare the + * policy OIDs in the certificate to that list. If this extension is + * critical, the path validation software MUST be able to interpret this + * extension (including the optional qualifier), or MUST reject the + * certificate. + *

    + * Optional qualifiers are not supported in this implementation, as they are + * not recommended by RFC2459. + * + * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the + * module definition): + *

    + * id-ce-certificatePolicies OBJECT IDENTIFIER ::=  { id-ce 32 }
    + *
    + * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
    + *
    + * PolicyInformation ::= SEQUENCE {
    + *      policyIdentifier   CertPolicyId,
    + *      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
    + *                              PolicyQualifierInfo OPTIONAL }
    + *
    + * CertPolicyId ::= OBJECT IDENTIFIER
    + * 
    + * @author Anne Anderson + * @since 1.4 + * @see Extension + * @see CertAttrSet + */ +public class CertificatePoliciesExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.CertificatePolicies"; + /** + * Attribute names. + */ + public static final String NAME = "CertificatePolicies"; + public static final String POLICIES = "policies"; + + /** + * List of PolicyInformation for this object. + */ + private List certPolicies; + + // Encode this extension value. + private void encodeThis() throws IOException { + if (certPolicies == null || certPolicies.isEmpty()) { + this.extensionValue = null; + } else { + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (PolicyInformation info : certPolicies) { + info.encode(tmp); + } + + os.write(DerValue.tag_Sequence, tmp); + this.extensionValue = os.toByteArray(); + } + } + + /** + * Create a CertificatePoliciesExtension object from + * a List of PolicyInformation; the criticality is set to false. + * + * @param certPolicies the List of PolicyInformation. + */ + public CertificatePoliciesExtension(List certPolicies) + throws IOException { + this(Boolean.FALSE, certPolicies); + } + + /** + * Create a CertificatePoliciesExtension object from + * a List of PolicyInformation with specified criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param certPolicies the List of PolicyInformation. + */ + public CertificatePoliciesExtension(Boolean critical, + List certPolicies) throws IOException { + this.certPolicies = certPolicies; + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from its DER encoded value and criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public CertificatePoliciesExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.CertificatePolicies_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "CertificatePoliciesExtension."); + } + certPolicies = new ArrayList(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + PolicyInformation policy = new PolicyInformation(seq); + certPolicies.add(policy); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + if (certPolicies == null) { + return ""; + } + StringBuilder sb = new StringBuilder(super.toString()); + sb.append("CertificatePolicies [\n"); + for (PolicyInformation info : certPolicies) { + sb.append(info.toString()); + } + sb.append("]\n"); + return sb.toString(); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.CertificatePolicies_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with an instanceof check + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(POLICIES)) { + if (!(obj instanceof List)) { + throw new IOException("Attribute value should be of type List."); + } + certPolicies = (List)obj; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public List get(String name) throws IOException { + if (name.equalsIgnoreCase(POLICIES)) { + //XXXX May want to consider cloning this + return certPolicies; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(POLICIES)) { + certPolicies = null; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:CertificatePoliciesExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(POLICIES); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/CertificatePolicyId.java b/src/sun/security/x509/CertificatePolicyId.java new file mode 100644 index 00000000..5f281bfc --- /dev/null +++ b/src/sun/security/x509/CertificatePolicyId.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import sun.security.util.*; + + +/** + * Represent the CertificatePolicyId ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class CertificatePolicyId { + private ObjectIdentifier id; + + /** + * Create a CertificatePolicyId with the ObjectIdentifier. + * + * @param id the ObjectIdentifier for the policy id. + */ + public CertificatePolicyId(ObjectIdentifier id) { + this.id = id; + } + + /** + * Create the object from its Der encoded value. + * + * @param val the DER encoded value for the same. + */ + public CertificatePolicyId(DerValue val) throws IOException { + this.id = val.getOID(); + } + + /** + * Return the value of the CertificatePolicyId as an ObjectIdentifier. + */ + public ObjectIdentifier getIdentifier() { + return (id); + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "CertificatePolicyId: [" + + id.toString() + + "]\n"; + + return (s); + } + + /** + * Write the CertificatePolicyId to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOID(id); + } + + /** + * Compares this CertificatePolicyId with another, for + * equality. Uses ObjectIdentifier.equals() as test for + * equality. + * + * @return true iff the ids are identical. + */ + public boolean equals(Object other) { + if (other instanceof CertificatePolicyId) + return id.equals((Object) + ((CertificatePolicyId) other).getIdentifier()); + else + return false; + } + + /** + * Returns a hash code value for this object. + * + * @return a hash code value + */ + public int hashCode() { + return id.hashCode(); + } +} diff --git a/src/sun/security/x509/CertificatePolicyMap.java b/src/sun/security/x509/CertificatePolicyMap.java new file mode 100644 index 00000000..46ce2383 --- /dev/null +++ b/src/sun/security/x509/CertificatePolicyMap.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.*; + +/** + * Represent the CertificatePolicyMap ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class CertificatePolicyMap { + private CertificatePolicyId issuerDomain; + private CertificatePolicyId subjectDomain; + + /** + * Create a CertificatePolicyMap with the passed CertificatePolicyId's. + * + * @param issuer the CertificatePolicyId for the issuer CA. + * @param subject the CertificatePolicyId for the subject CA. + */ + public CertificatePolicyMap(CertificatePolicyId issuer, + CertificatePolicyId subject) { + this.issuerDomain = issuer; + this.subjectDomain = subject; + } + + /** + * Create the CertificatePolicyMap from the DER encoded value. + * + * @param val the DER encoded value of the same. + */ + public CertificatePolicyMap(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for CertificatePolicyMap"); + } + issuerDomain = new CertificatePolicyId(val.data.getDerValue()); + subjectDomain = new CertificatePolicyId(val.data.getDerValue()); + } + + /** + * Return the issuer CA part of the policy map. + */ + public CertificatePolicyId getIssuerIdentifier() { + return (issuerDomain); + } + + /** + * Return the subject CA part of the policy map. + */ + public CertificatePolicyId getSubjectIdentifier() { + return (subjectDomain); + } + + /** + * Returns a printable representation of the CertificatePolicyId. + */ + public String toString() { + String s = "CertificatePolicyMap: [\n" + + "IssuerDomain:" + issuerDomain.toString() + + "SubjectDomain:" + subjectDomain.toString() + + "]\n"; + + return (s); + } + + /** + * Write the CertificatePolicyMap to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + issuerDomain.encode(tmp); + subjectDomain.encode(tmp); + out.write(DerValue.tag_Sequence,tmp); + } +} diff --git a/src/sun/security/x509/CertificatePolicySet.java b/src/sun/security/x509/CertificatePolicySet.java new file mode 100644 index 00000000..1bb3aa8d --- /dev/null +++ b/src/sun/security/x509/CertificatePolicySet.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.Vector; +import java.util.List; +import java.util.Collections; + +import sun.security.util.*; + +/** + * This class defines the certificate policy set ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class CertificatePolicySet { + + private final Vector ids; + + /** + * The default constructor for this class. + * + * @param ids the sequence of CertificatePolicyId's. + */ + public CertificatePolicySet(Vector ids) { + this.ids = ids; + } + + /** + * Create the object from the DerValue. + * + * @param in the passed DerInputStream. + * @exception IOException on decoding errors. + */ + public CertificatePolicySet(DerInputStream in) throws IOException { + ids = new Vector(); + DerValue[] seq = in.getSequence(5); + + for (int i = 0; i < seq.length; i++) { + CertificatePolicyId id = new CertificatePolicyId(seq[i]); + ids.addElement(id); + } + } + + /** + * Return printable form of the object. + */ + public String toString() { + String s = "CertificatePolicySet:[\n" + + ids.toString() + + "]\n"; + + return (s); + } + + /** + * Encode the policy set to the output stream. + * + * @param out the DerOutputStream to encode the data to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < ids.size(); i++) { + ids.elementAt(i).encode(tmp); + } + out.write(DerValue.tag_Sequence,tmp); + } + + /** + * Return the sequence of CertificatePolicyIds. + * + * @return A List containing the CertificatePolicyId objects. + * + */ + public List getCertPolicyIds() { + return Collections.unmodifiableList(ids); + } +} diff --git a/src/sun/security/x509/CertificateSerialNumber.java b/src/sun/security/x509/CertificateSerialNumber.java new file mode 100644 index 00000000..0e47fc87 --- /dev/null +++ b/src/sun/security/x509/CertificateSerialNumber.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the SerialNumber attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateSerialNumber implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.serialNumber"; + + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "serialNumber"; + public static final String NUMBER = "number"; + + private SerialNumber serial; + + /** + * Default constructor for the certificate attribute. + * + * @param serial the serial number for the certificate. + */ + public CertificateSerialNumber(BigInteger num) { + this.serial = new SerialNumber(num); + } + + /** + * Default constructor for the certificate attribute. + * + * @param serial the serial number for the certificate. + */ + public CertificateSerialNumber(int num) { + this.serial = new SerialNumber(num); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(DerInputStream in) throws IOException { + serial = new SerialNumber(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the serial number from. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(InputStream in) throws IOException { + serial = new SerialNumber(in); + } + + /** + * Create the object, decoding the values from the passed DerValue. + * + * @param val the DER encoded value. + * @exception IOException on decoding errors. + */ + public CertificateSerialNumber(DerValue val) throws IOException { + serial = new SerialNumber(val); + } + + /** + * Return the serial number as user readable string. + */ + public String toString() { + if (serial == null) return ""; + return (serial.toString()); + } + + /** + * Encode the serial number in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + serial.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof SerialNumber)) { + throw new IOException("Attribute must be of type SerialNumber."); + } + if (name.equalsIgnoreCase(NUMBER)) { + serial = (SerialNumber)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Get the attribute value. + */ + public SerialNumber get(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + return (serial); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NUMBER)) { + serial = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSerialNumber."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NUMBER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/CertificateSubjectName.java b/src/sun/security/x509/CertificateSubjectName.java new file mode 100644 index 00000000..81d344c7 --- /dev/null +++ b/src/sun/security/x509/CertificateSubjectName.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.*; + +/** + * This class defines the X500Name attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateSubjectName implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.subject"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "subject"; + public static final String DN_NAME = "dname"; + + // accessor name for cached X500Principal only + // do not allow a set() of this value, do not advertise with getElements() + public static final String DN_PRINCIPAL = "x500principal"; + + // Private data member + private X500Name dnName; + + // cached X500Principal version of the name + private X500Principal dnPrincipal; + + /** + * Default constructor for the certificate attribute. + * + * @param name the X500Name + */ + public CertificateSubjectName(X500Name name) { + this.dnName = name; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectName(DerInputStream in) throws IOException { + dnName = new X500Name(in); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X500Name from. + * @exception IOException on decoding errors. + */ + public CertificateSubjectName(InputStream in) throws IOException { + DerValue derVal = new DerValue(in); + dnName = new X500Name(derVal); + } + + /** + * Return the name as user readable string. + */ + public String toString() { + if (dnName == null) return ""; + return(dnName.toString()); + } + + /** + * Encode the name in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + dnName.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof X500Name)) { + throw new IOException("Attribute must be of type X500Name."); + } + if (name.equalsIgnoreCase(DN_NAME)) { + this.dnName = (X500Name)obj; + this.dnPrincipal = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + return(dnName); + } else if (name.equalsIgnoreCase(DN_PRINCIPAL)) { + if ((dnPrincipal == null) && (dnName != null)) { + dnPrincipal = dnName.asX500Principal(); + } + return dnPrincipal; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DN_NAME)) { + dnName = null; + dnPrincipal = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:CertificateSubjectName."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DN_NAME); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/src/sun/security/x509/CertificateValidity.java b/src/sun/security/x509/CertificateValidity.java new file mode 100644 index 00000000..cd9b00fa --- /dev/null +++ b/src/sun/security/x509/CertificateValidity.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.*; +import java.util.Date; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the interval for which the certificate is valid. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateValidity implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.validity"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "validity"; + public static final String NOT_BEFORE = "notBefore"; + public static final String NOT_AFTER = "notAfter"; + private static final long YR_2050 = 2524636800000L; + + // Private data members + private Date notBefore; + private Date notAfter; + + // Returns the first time the certificate is valid. + private Date getNotBefore() { + return (new Date(notBefore.getTime())); + } + + // Returns the last time the certificate is valid. + private Date getNotAfter() { + return (new Date(notAfter.getTime())); + } + + // Construct the class from the DerValue + private void construct(DerValue derVal) throws IOException { + if (derVal.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoded CertificateValidity, " + + "starting sequence tag missing."); + } + // check if UTCTime encoded or GeneralizedTime + if (derVal.data.available() == 0) + throw new IOException("No data encoded for CertificateValidity"); + + DerInputStream derIn = new DerInputStream(derVal.toByteArray()); + DerValue[] seq = derIn.getSequence(2); + if (seq.length != 2) + throw new IOException("Invalid encoding for CertificateValidity"); + + if (seq[0].tag == DerValue.tag_UtcTime) { + notBefore = derVal.data.getUTCTime(); + } else if (seq[0].tag == DerValue.tag_GeneralizedTime) { + notBefore = derVal.data.getGeneralizedTime(); + } else { + throw new IOException("Invalid encoding for CertificateValidity"); + } + + if (seq[1].tag == DerValue.tag_UtcTime) { + notAfter = derVal.data.getUTCTime(); + } else if (seq[1].tag == DerValue.tag_GeneralizedTime) { + notAfter = derVal.data.getGeneralizedTime(); + } else { + throw new IOException("Invalid encoding for CertificateValidity"); + } + } + + /** + * Default constructor for the class. + */ + public CertificateValidity() { } + + /** + * The default constructor for this class for the specified interval. + * + * @param notBefore the date and time before which the certificate + * is not valid. + * @param notAfter the date and time after which the certificate is + * not valid. + */ + public CertificateValidity(Date notBefore, Date notAfter) { + this.notBefore = notBefore; + this.notAfter = notAfter; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the CertificateValidity from. + * @exception IOException on decoding errors. + */ + public CertificateValidity(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + construct(derVal); + } + + /** + * Return the validity period as user readable string. + */ + public String toString() { + if (notBefore == null || notAfter == null) + return ""; + return ("Validity: [From: " + notBefore.toString() + + ",\n To: " + notAfter.toString() + "]"); + } + + /** + * Encode the CertificateValidity period in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + + // in cases where default constructor is used check for + // null values + if (notBefore == null || notAfter == null) { + throw new IOException("CertAttrSet:CertificateValidity:" + + " null values to encode.\n"); + } + DerOutputStream pair = new DerOutputStream(); + + if (notBefore.getTime() < YR_2050) { + pair.putUTCTime(notBefore); + } else + pair.putGeneralizedTime(notBefore); + + if (notAfter.getTime() < YR_2050) { + pair.putUTCTime(notAfter); + } else { + pair.putGeneralizedTime(notAfter); + } + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, pair); + + out.write(seq.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Date)) { + throw new IOException("Attribute must be of type Date."); + } + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = (Date)obj; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = (Date)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Get the attribute value. + */ + public Date get(String name) throws IOException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + return (getNotBefore()); + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + return (getNotAfter()); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = null; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateValidity."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NOT_BEFORE); + elements.addElement(NOT_AFTER); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Verify that the current time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void valid() + throws CertificateNotYetValidException, CertificateExpiredException { + Date now = new Date(); + valid(now); + } + + /** + * Verify that the passed time is within the validity period. + * @param now the Date against which to compare the validity + * period. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the Date supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the Date supplied. + * + */ + public void valid(Date now) + throws CertificateNotYetValidException, CertificateExpiredException { + /* + * we use the internal Dates rather than the passed in Date + * because someone could override the Date methods after() + * and before() to do something entirely different. + */ + if (notBefore.after(now)) { + throw new CertificateNotYetValidException("NotBefore: " + + notBefore.toString()); + } + if (notAfter.before(now)) { + throw new CertificateExpiredException("NotAfter: " + + notAfter.toString()); + } + } +} diff --git a/src/sun/security/x509/CertificateVersion.java b/src/sun/security/x509/CertificateVersion.java new file mode 100644 index 00000000..040b3be0 --- /dev/null +++ b/src/sun/security/x509/CertificateVersion.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the version of the X509 Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateVersion implements CertAttrSet { + /** + * X509Certificate Version 1 + */ + public static final int V1 = 0; + /** + * X509Certificate Version 2 + */ + public static final int V2 = 1; + /** + * X509Certificate Version 3 + */ + public static final int V3 = 2; + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.version"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "version"; + public static final String VERSION = "number"; + + // Private data members + int version = V1; + + // Returns the version number. + private int getVersion() { + return(version); + } + + // Construct the class from the passed DerValue + private void construct(DerValue derVal) throws IOException { + if (derVal.isConstructed() && derVal.isContextSpecific()) { + derVal = derVal.data.getDerValue(); + version = derVal.getInteger(); + if (derVal.data.available() != 0) { + throw new IOException("X.509 version, bad format"); + } + } + } + + /** + * The default constructor for this class, + * sets the version to 0 (i.e. X.509 version 1). + */ + public CertificateVersion() { + version = V1; + } + + /** + * The constructor for this class for the required version. + * + * @param version the version for the certificate. + * @exception IOException if the version is not valid. + */ + public CertificateVersion(int version) throws IOException { + + // check that it is a valid version + if (version == V1 || version == V2 || version == V3) + this.version = version; + else { + throw new IOException("X.509 Certificate version " + + version + " not supported.\n"); + } + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the CertificateVersion from. + * @exception IOException on decoding errors. + */ + public CertificateVersion(DerInputStream in) throws IOException { + version = V1; + DerValue derVal = in.getDerValue(); + + construct(derVal); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the CertificateVersion from. + * @exception IOException on decoding errors. + */ + public CertificateVersion(InputStream in) throws IOException { + version = V1; + DerValue derVal = new DerValue(in); + + construct(derVal); + } + + /** + * Create the object, decoding the values from the passed DerValue. + * + * @param val the Der encoded value. + * @exception IOException on decoding errors. + */ + public CertificateVersion(DerValue val) throws IOException { + version = V1; + + construct(val); + } + + /** + * Return the version number of the certificate. + */ + public String toString() { + return("Version: V" + (version+1)); + } + + /** + * Encode the CertificateVersion period in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + // Nothing for default + if (version == V1) { + return; + } + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(version); + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0), + tmp); + + out.write(seq.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute must be of type Integer."); + } + if (name.equalsIgnoreCase(VERSION)) { + version = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Get the attribute value. + */ + public Integer get(String name) throws IOException { + if (name.equalsIgnoreCase(VERSION)) { + return(new Integer(getVersion())); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(VERSION)) { + version = V1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateVersion."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(VERSION); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Compare versions. + */ + public int compare(int vers) { + return(version - vers); + } +} diff --git a/src/sun/security/x509/CertificateX509Key.java b/src/sun/security/x509/CertificateX509Key.java new file mode 100644 index 00000000..d78d618a --- /dev/null +++ b/src/sun/security/x509/CertificateX509Key.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.security.PublicKey; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the X509Key attribute for the Certificate. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + */ +public class CertificateX509Key implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.key"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "key"; + public static final String KEY = "value"; + + // Private data member + private PublicKey key; + + /** + * Default constructor for the certificate attribute. + * + * @param key the X509Key + */ + public CertificateX509Key(PublicKey key) { + this.key = key; + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the X509Key from. + * @exception IOException on decoding errors. + */ + public CertificateX509Key(DerInputStream in) throws IOException { + DerValue val = in.getDerValue(); + key = X509Key.parse(val); + } + + /** + * Create the object, decoding the values from the passed stream. + * + * @param in the InputStream to read the X509Key from. + * @exception IOException on decoding errors. + */ + public CertificateX509Key(InputStream in) throws IOException { + DerValue val = new DerValue(in); + key = X509Key.parse(val); + } + + /** + * Return the key as printable string. + */ + public String toString() { + if (key == null) return ""; + return(key.toString()); + } + + /** + * Encode the key in DER form to the stream. + * + * @param out the OutputStream to marshal the contents to. + * @exception IOException on errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + tmp.write(key.getEncoded()); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(KEY)) { + this.key = (PublicKey)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Get the attribute value. + */ + public PublicKey get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY)) { + return(key); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY)) { + key = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet: CertificateX509Key."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/src/sun/security/x509/DNSName.java b/src/sun/security/x509/DNSName.java new file mode 100644 index 00000000..2a35b86f --- /dev/null +++ b/src/sun/security/x509/DNSName.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.Locale; + +import sun.security.util.*; + +/** + * This class implements the DNSName as required by the GeneralNames + * ASN.1 object. + *

    + * [RFC2459] When the subjectAltName extension contains a domain name service + * label, the domain name MUST be stored in the dNSName (an IA5String). + * The name MUST be in the "preferred name syntax," as specified by RFC + * 1034 [RFC 1034]. Note that while upper and lower case letters are + * allowed in domain names, no signifigance is attached to the case. In + * addition, while the string " " is a legal domain name, subjectAltName + * extensions with a dNSName " " are not permitted. Finally, the use of + * the DNS representation for Internet mail addresses (wpolk.nist.gov + * instead of wpolk@nist.gov) is not permitted; such identities are to + * be encoded as rfc822Name. + *

    + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class DNSName implements GeneralNameInterface { + private String name; + + private static final String alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final String digitsAndHyphen = "0123456789-"; + private static final String alphaDigitsAndHyphen = alpha + digitsAndHyphen; + + /** + * Create the DNSName object from the passed encoded Der value. + * + * @param derValue the encoded DER DNSName. + * @exception IOException on error. + */ + public DNSName(DerValue derValue) throws IOException { + name = derValue.getIA5String(); + } + + /** + * Create the DNSName object with the specified name. + * + * @param name the DNSName. + * @throws IOException if the name is not a valid DNSName subjectAltName + */ + public DNSName(String name) throws IOException { + if (name == null || name.length() == 0) + throw new IOException("DNS name must not be null"); + if (name.indexOf(' ') != -1) + throw new IOException("DNS names or NameConstraints with blank components are not permitted"); + if (name.charAt(0) == '.' || name.charAt(name.length() -1) == '.') + throw new IOException("DNS names or NameConstraints may not begin or end with a ."); + //Name will consist of label components separated by "." + //startIndex is the index of the first character of a component + //endIndex is the index of the last character of a component plus 1 + for (int endIndex,startIndex=0; startIndex < name.length(); startIndex = endIndex+1) { + endIndex = name.indexOf('.', startIndex); + if (endIndex < 0) { + endIndex = name.length(); + } + if ((endIndex-startIndex) < 1) + throw new IOException("DNSName SubjectAltNames with empty components are not permitted"); + + //DNSName components must begin with a letter A-Z or a-z + if (alpha.indexOf(name.charAt(startIndex)) < 0) + throw new IOException("DNSName components must begin with a letter"); + //nonStartIndex: index for characters in the component beyond the first one + for (int nonStartIndex=startIndex+1; nonStartIndex < endIndex; nonStartIndex++) { + char x = name.charAt(nonStartIndex); + if ((alphaDigitsAndHyphen).indexOf(x) < 0) + throw new IOException("DNSName components must consist of letters, digits, and hyphens"); + } + } + this.name = name; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_DNS); + } + + /** + * Return the actual name value of the GeneralName. + */ + public String getName() { + return name; + } + + /** + * Encode the DNS name into the DerOutputStream. + * + * @param out the DER stream to encode the DNSName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(name); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("DNSName: " + name); + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are equivalent + * according to RFC2459. + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof DNSName)) + return false; + + DNSName other = (DNSName)obj; + + // RFC2459 mandates that these names are + // not case-sensitive + return name.equalsIgnoreCase(other.name); + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + public int hashCode() { + return name.toUpperCase(Locale.ENGLISH).hashCode(); + } + + /** + * Return type of constraint inputName places on this name:

      + *
    • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain). + *
    • NAME_MATCH = 0: input name matches name. + *
    • NAME_NARROWS = 1: input name narrows name (is lower in the naming subtree) + *
    • NAME_WIDENS = 2: input name widens name (is higher in the naming subtree) + *
    • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type. + *
    . These results are used in checking NameConstraints during + * certification path verification. + *

    + * RFC2459: DNS name restrictions are expressed as foo.bar.com. Any subdomain + * satisfies the name constraint. For example, www.foo.bar.com would + * satisfy the constraint but bigfoo.bar.com would not. + *

    + * draft-ietf-pkix-new-part1-00.txt: DNS name restrictions are expressed as foo.bar.com. + * Any DNS name that + * can be constructed by simply adding to the left hand side of the name + * satisfies the name constraint. For example, www.foo.bar.com would + * satisfy the constraint but foo1.bar.com would not. + *

    + * RFC1034: By convention, domain names can be stored with arbitrary case, but + * domain name comparisons for all present domain functions are done in a + * case-insensitive manner, assuming an ASCII character set, and a high + * order zero bit. + *

    + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but narrowing and widening are + * not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != NAME_DNS) + constraintType = NAME_DIFF_TYPE; + else { + String inName = + (((DNSName)inputName).getName()).toLowerCase(Locale.ENGLISH); + String thisName = name.toLowerCase(Locale.ENGLISH); + if (inName.equals(thisName)) + constraintType = NAME_MATCH; + else if (thisName.endsWith(inName)) { + int inNdx = thisName.lastIndexOf(inName); + if (thisName.charAt(inNdx-1) == '.' ) + constraintType = NAME_WIDENS; + else + constraintType = NAME_SAME_TYPE; + } else if (inName.endsWith(thisName)) { + int ndx = inName.lastIndexOf(thisName); + if (inName.charAt(ndx-1) == '.' ) + constraintType = NAME_NARROWS; + else + constraintType = NAME_SAME_TYPE; + } else { + constraintType = NAME_SAME_TYPE; + } + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + String subtree=name; + int i=1; + + /* count dots */ + for (; subtree.lastIndexOf('.') >= 0; i++) { + subtree=subtree.substring(0,subtree.lastIndexOf('.')); + } + + return i; + } + +} diff --git a/src/sun/security/x509/DeltaCRLIndicatorExtension.java b/src/sun/security/x509/DeltaCRLIndicatorExtension.java new file mode 100644 index 00000000..629c3899 --- /dev/null +++ b/src/sun/security/x509/DeltaCRLIndicatorExtension.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; + +import sun.security.util.*; + +/** + * Represents the Delta CRL Indicator Extension. + * + *

    + * The extension identifies a CRL as being a delta CRL. + * Delta CRLs contain updates to revocation information previously distributed, + * rather than all the information that would appear in a complete CRL. + * The extension contains a CRL number that identifies the CRL, complete for a + * given scope, that was used as the starting point in the generation of + * this delta CRL. + * + *

    + * The extension is defined in Section 5.2.4 of + * Internet X.509 PKI Certific +ate and Certificate Revocation List (CRL) Profile. + * + *

    + * Its ASN.1 definition is as follows: + *

    + *     id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
    + *
    + *     BaseCRLNumber ::= CRLNumber
    + *     CRLNumber ::= INTEGER (0..MAX)
    + * 
    + * + * @since 1.6 + */ +public class DeltaCRLIndicatorExtension extends CRLNumberExtension { + + /** + * Attribute name. + */ + public static final String NAME = "DeltaCRLIndicator"; + + private static final String LABEL = "Base CRL Number"; + + /** + * Creates a delta CRL indicator extension with the integer value . + * The criticality is set to true. + * + * @param crlNum the value to be set for the extension. + */ + public DeltaCRLIndicatorExtension(int crlNum) throws IOException { + super(PKIXExtensions.DeltaCRLIndicator_Id, true, + BigInteger.valueOf(crlNum), NAME, LABEL); + } + + /** + * Creates a delta CRL indictor extension with the BigInteger value . + * The criticality is set to true. + * + * @param crlNum the value to be set for the extension. + */ + public DeltaCRLIndicatorExtension(BigInteger crlNum) throws IOException { + super(PKIXExtensions.DeltaCRLIndicator_Id, true, crlNum, NAME, LABEL); + } + + /** + * Creates the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on decoding error. + */ + public DeltaCRLIndicatorExtension(Boolean critical, Object value) + throws IOException { + super(PKIXExtensions.DeltaCRLIndicator_Id, critical.booleanValue(), + value, NAME, LABEL); + } + + /** + * Writes the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + super.encode(out, PKIXExtensions.DeltaCRLIndicator_Id, true); + } +} diff --git a/src/sun/security/x509/DistributionPoint.java b/src/sun/security/x509/DistributionPoint.java new file mode 100644 index 00000000..faca9075 --- /dev/null +++ b/src/sun/security/x509/DistributionPoint.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.*; + +import sun.security.util.BitArray; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * Represent the DistributionPoint sequence used in the CRL + * Distribution Points Extension (OID = 2.5.29.31). + *

    + * The ASN.1 definition for this is: + *

    + * DistributionPoint ::= SEQUENCE {
    + *      distributionPoint       [0]     DistributionPointName OPTIONAL,
    + *      reasons                 [1]     ReasonFlags OPTIONAL,
    + *      cRLIssuer               [2]     GeneralNames OPTIONAL }
    + *
    + * DistributionPointName ::= CHOICE {
    + *      fullName                [0]     GeneralNames,
    + *      nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
    + *
    + * ReasonFlags ::= BIT STRING {
    + *      unused                  (0),
    + *      keyCompromise           (1),
    + *      cACompromise            (2),
    + *      affiliationChanged      (3),
    + *      superseded              (4),
    + *      cessationOfOperation    (5),
    + *      certificateHold         (6),
    + *      privilegeWithdrawn      (7),
    + *      aACompromise            (8) }
    + *
    + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
    + *
    + * GeneralName ::= CHOICE {
    + *         otherName                   [0] INSTANCE OF OTHER-NAME,
    + *         rfc822Name                  [1] IA5String,
    + *         dNSName                     [2] IA5String,
    + *         x400Address                 [3] ORAddress,
    + *         directoryName               [4] Name,
    + *         ediPartyName                [5] EDIPartyName,
    + *         uniformResourceIdentifier   [6] IA5String,
    + *         iPAddress                   [7] OCTET STRING,
    + *         registeredID                [8] OBJECT IDENTIFIER }
    + *
    + * RelativeDistinguishedName ::=
    + *   SET OF AttributeTypeAndValue
    + *
    + * AttributeTypeAndValue ::= SEQUENCE {
    + *   type     AttributeType,
    + *   value    AttributeValue }
    + *
    + * AttributeType ::= OBJECT IDENTIFIER
    + *
    + * AttributeValue ::= ANY DEFINED BY AttributeType
    + * 
    + *

    + * Instances of this class are designed to be immutable. However, since this + * is an internal API we do not use defensive cloning for values for + * performance reasons. It is the responsibility of the consumer to ensure + * that no mutable elements are modified. + * + * @author Anne Anderson + * @author Andreas Sterbenz + * @since 1.4.2 + * @see CRLDistributionPointsExtension + */ +public class DistributionPoint { + + // reason flag bits + // NOTE that these are NOT quite the same as the CRL reason code extension + public final static int KEY_COMPROMISE = 1; + public final static int CA_COMPROMISE = 2; + public final static int AFFILIATION_CHANGED = 3; + public final static int SUPERSEDED = 4; + public final static int CESSATION_OF_OPERATION = 5; + public final static int CERTIFICATE_HOLD = 6; + public final static int PRIVILEGE_WITHDRAWN = 7; + public final static int AA_COMPROMISE = 8; + + private static final String[] REASON_STRINGS = { + null, + "key compromise", + "CA compromise", + "affiliation changed", + "superseded", + "cessation of operation", + "certificate hold", + "privilege withdrawn", + "AA compromise", + }; + + // context specific tag values + private static final byte TAG_DIST_PT = 0; + private static final byte TAG_REASONS = 1; + private static final byte TAG_ISSUER = 2; + + private static final byte TAG_FULL_NAME = 0; + private static final byte TAG_REL_NAME = 1; + + // only one of fullName and relativeName can be set + private GeneralNames fullName; + private RDN relativeName; + + // reasonFlags or null + private boolean[] reasonFlags; + + // crlIssuer or null + private GeneralNames crlIssuer; + + // cached hashCode value + private volatile int hashCode; + + /** + * Constructor for the class using GeneralNames for DistributionPointName + * + * @param fullName the GeneralNames of the distribution point; may be null + * @param reasons the CRL reasons included in the CRL at this distribution + * point; may be null + * @param issuer the name(s) of the CRL issuer for the CRL at this + * distribution point; may be null + */ + public DistributionPoint(GeneralNames fullName, boolean[] reasonFlags, + GeneralNames crlIssuer) { + if ((fullName == null) && (crlIssuer == null)) { + throw new IllegalArgumentException + ("fullName and crlIssuer may not both be null"); + } + this.fullName = fullName; + this.reasonFlags = reasonFlags; + this.crlIssuer = crlIssuer; + } + + /** + * Constructor for the class using RelativeDistinguishedName for + * DistributionPointName + * + * @param relativeName the RelativeDistinguishedName of the distribution + * point; may not be null + * @param reasons the CRL reasons included in the CRL at this distribution + * point; may be null + * @param issuer the name(s) of the CRL issuer for the CRL at this + * distribution point; may not be null or empty. + */ + public DistributionPoint(RDN relativeName, boolean[] reasonFlags, + GeneralNames crlIssuer) { + if ((relativeName == null) && (crlIssuer == null)) { + throw new IllegalArgumentException + ("relativeName and crlIssuer may not both be null"); + } + this.relativeName = relativeName; + this.reasonFlags = reasonFlags; + this.crlIssuer = crlIssuer; + } + + /** + * Create the object from the passed DER encoded form. + * + * @param val the DER encoded form of the DistributionPoint + * @throws IOException on error + */ + public DistributionPoint(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of DistributionPoint."); + } + + // Note that all the fields in DistributionPoint are defined as + // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting + // in val.data being null. + while ((val.data != null) && (val.data.available() != 0)) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_DIST_PT) && opt.isConstructed()) { + if ((fullName != null) || (relativeName != null)) { + throw new IOException("Duplicate DistributionPointName in " + + "DistributionPoint."); + } + DerValue distPnt = opt.data.getDerValue(); + if (distPnt.isContextSpecific(TAG_FULL_NAME) + && distPnt.isConstructed()) { + distPnt.resetTag(DerValue.tag_Sequence); + fullName = new GeneralNames(distPnt); + } else if (distPnt.isContextSpecific(TAG_REL_NAME) + && distPnt.isConstructed()) { + distPnt.resetTag(DerValue.tag_Set); + relativeName = new RDN(distPnt); + } else { + throw new IOException("Invalid DistributionPointName in " + + "DistributionPoint"); + } + } else if (opt.isContextSpecific(TAG_REASONS) + && !opt.isConstructed()) { + if (reasonFlags != null) { + throw new IOException("Duplicate Reasons in " + + "DistributionPoint."); + } + opt.resetTag(DerValue.tag_BitString); + reasonFlags = (opt.getUnalignedBitString()).toBooleanArray(); + } else if (opt.isContextSpecific(TAG_ISSUER) + && opt.isConstructed()) { + if (crlIssuer != null) { + throw new IOException("Duplicate CRLIssuer in " + + "DistributionPoint."); + } + opt.resetTag(DerValue.tag_Sequence); + crlIssuer = new GeneralNames(opt); + } else { + throw new IOException("Invalid encoding of " + + "DistributionPoint."); + } + } + if ((crlIssuer == null) && (fullName == null) && (relativeName == null)) { + throw new IOException("One of fullName, relativeName, " + + " and crlIssuer has to be set"); + } + } + + /** + * Return the full distribution point name or null if not set. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Return the relative distribution point name or null if not set. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Return the reason flags or null if not set. + */ + public boolean[] getReasonFlags() { + return reasonFlags; + } + + /** + * Return the CRL issuer name or null if not set. + */ + public GeneralNames getCRLIssuer() { + return crlIssuer; + } + + /** + * Write the DistributionPoint value to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on error. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tagged = new DerOutputStream(); + + // NOTE: only one of pointNames and pointRDN can be set + if ((fullName != null) || (relativeName != null)) { + DerOutputStream distributionPoint = new DerOutputStream(); + if (fullName != null) { + DerOutputStream derOut = new DerOutputStream(); + fullName.encode(derOut); + distributionPoint.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_FULL_NAME), + derOut); + } else if (relativeName != null) { + DerOutputStream derOut = new DerOutputStream(); + relativeName.encode(derOut); + distributionPoint.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_REL_NAME), + derOut); + } + tagged.write( + DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_DIST_PT), + distributionPoint); + } + if (reasonFlags != null) { + DerOutputStream reasons = new DerOutputStream(); + BitArray rf = new BitArray(reasonFlags); + reasons.putTruncatedUnalignedBitString(rf); + tagged.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, false, TAG_REASONS), + reasons); + } + if (crlIssuer != null) { + DerOutputStream issuer = new DerOutputStream(); + crlIssuer.encode(issuer); + tagged.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_ISSUER), + issuer); + } + out.write(DerValue.tag_Sequence, tagged); + } + + /** + * Compare an object to this DistributionPoint for equality. + * + * @param obj Object to be compared to this + * @return true if objects match; false otherwise + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DistributionPoint == false) { + return false; + } + DistributionPoint other = (DistributionPoint)obj; + + boolean equal = Objects.equals(this.fullName, other.fullName) + && Objects.equals(this.relativeName, other.relativeName) + && Objects.equals(this.crlIssuer, other.crlIssuer) + && Arrays.equals(this.reasonFlags, other.reasonFlags); + return equal; + } + + public int hashCode() { + int hash = hashCode; + if (hash == 0) { + hash = 1; + if (fullName != null) { + hash += fullName.hashCode(); + } + if (relativeName != null) { + hash += relativeName.hashCode(); + } + if (crlIssuer != null) { + hash += crlIssuer.hashCode(); + } + if (reasonFlags != null) { + for (int i = 0; i < reasonFlags.length; i++) { + if (reasonFlags[i]) { + hash += i; + } + } + } + hashCode = hash; + } + return hash; + } + + /** + * Return a string representation for reasonFlag bit 'reason'. + */ + private static String reasonToString(int reason) { + if ((reason > 0) && (reason < REASON_STRINGS.length)) { + return REASON_STRINGS[reason]; + } + return "Unknown reason " + reason; + } + + /** + * Return a printable string of the Distribution Point. + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + if (fullName != null) { + sb.append("DistributionPoint:\n " + fullName + "\n"); + } + if (relativeName != null) { + sb.append("DistributionPoint:\n " + relativeName + "\n"); + } + + if (reasonFlags != null) { + sb.append(" ReasonFlags:\n"); + for (int i = 0; i < reasonFlags.length; i++) { + if (reasonFlags[i]) { + sb.append(" " + reasonToString(i) + "\n"); + } + } + } + if (crlIssuer != null) { + sb.append(" CRLIssuer:" + crlIssuer + "\n"); + } + return sb.toString(); + } + +} diff --git a/src/sun/security/x509/DistributionPointName.java b/src/sun/security/x509/DistributionPointName.java new file mode 100644 index 00000000..ac543b31 --- /dev/null +++ b/src/sun/security/x509/DistributionPointName.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.*; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * Represents the DistributionPointName ASN.1 type. + * + * It is used in the CRL Distribution Points Extension (OID = 2.5.29.31) + * and the Issuing Distribution Point Extension (OID = 2.5.29.28). + *

    + * Its ASN.1 definition is: + *

    + *
    + *     DistributionPointName ::= CHOICE {
    + *         fullName                  [0] GeneralNames,
    + *         nameRelativeToCRLIssuer   [1] RelativeDistinguishedName }
    + *
    + *     GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
    + *
    + *     GeneralName ::= CHOICE {
    + *         otherName                 [0] INSTANCE OF OTHER-NAME,
    + *         rfc822Name                [1] IA5String,
    + *         dNSName                   [2] IA5String,
    + *         x400Address               [3] ORAddress,
    + *         directoryName             [4] Name,
    + *         ediPartyName              [5] EDIPartyName,
    + *         uniformResourceIdentifier [6] IA5String,
    + *         iPAddress                 [7] OCTET STRING,
    + *         registeredID              [8] OBJECT IDENTIFIER }
    + *
    + *     RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
    + *
    + *     AttributeTypeAndValue ::= SEQUENCE {
    + *         type    AttributeType,
    + *         value   AttributeValue }
    + *
    + *     AttributeType ::= OBJECT IDENTIFIER
    + *
    + *     AttributeValue ::= ANY DEFINED BY AttributeType
    + *
    + * 
    + *

    + * Instances of this class are designed to be immutable. However, since this + * is an internal API we do not use defensive cloning for values for + * performance reasons. It is the responsibility of the consumer to ensure + * that no mutable elements are modified. + * + * @see CRLDistributionPointsExtension + * @see IssuingDistributionPointExtension + * @since 1.6 + */ +public class DistributionPointName { + + // ASN.1 context specific tag values + private static final byte TAG_FULL_NAME = 0; + private static final byte TAG_RELATIVE_NAME = 1; + + // Only one of fullName and relativeName can be set + private GeneralNames fullName = null; + private RDN relativeName = null; + + // Cached hashCode value + private volatile int hashCode; + + /** + * Creates a distribution point name using a full name. + * + * @param fullName the name for the distribution point. + * @exception IllegalArgumentException if fullName is null. + */ + public DistributionPointName(GeneralNames fullName) { + + if (fullName == null) { + throw new IllegalArgumentException("fullName must not be null"); + } + this.fullName = fullName; + } + + /** + * Creates a distribution point name using a relative name. + * + * @param relativeName the name of the distribution point relative to + * the name of the issuer of the CRL. + * @exception IllegalArgumentException if relativeName is null. + */ + public DistributionPointName(RDN relativeName) { + + if (relativeName == null) { + throw new IllegalArgumentException("relativeName must not be null"); + } + this.relativeName = relativeName; + } + + /** + * Creates a distribution point name from its DER-encoded form. + * + * @param encoding the DER-encoded value. + * @throws IOException on decoding error. + */ + public DistributionPointName(DerValue encoding) throws IOException { + + if (encoding.isContextSpecific(TAG_FULL_NAME) && + encoding.isConstructed()) { + + encoding.resetTag(DerValue.tag_Sequence); + fullName = new GeneralNames(encoding); + + } else if (encoding.isContextSpecific(TAG_RELATIVE_NAME) && + encoding.isConstructed()) { + + encoding.resetTag(DerValue.tag_Set); + relativeName = new RDN(encoding); + + } else { + throw new IOException("Invalid encoding for DistributionPointName"); + } + + } + + /** + * Returns the full name for the distribution point or null if not set. + */ + public GeneralNames getFullName() { + return fullName; + } + + /** + * Returns the relative name for the distribution point or null if not set. + */ + public RDN getRelativeName() { + return relativeName; + } + + /** + * Encodes the distribution point name and writes it to the DerOutputStream. + * + * @param out the output stream. + * @exception IOException on encoding error. + */ + public void encode(DerOutputStream out) throws IOException { + + DerOutputStream theChoice = new DerOutputStream(); + + if (fullName != null) { + fullName.encode(theChoice); + out.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_FULL_NAME), + theChoice); + + } else { + relativeName.encode(theChoice); + out.writeImplicit( + DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_RELATIVE_NAME), + theChoice); + } + } + + /** + * Compare an object to this distribution point name for equality. + * + * @param obj Object to be compared to this + * @return true if objects match; false otherwise + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DistributionPointName == false) { + return false; + } + DistributionPointName other = (DistributionPointName)obj; + + return Objects.equals(this.fullName, other.fullName) && + Objects.equals(this.relativeName, other.relativeName); + } + + /** + * Returns the hash code for this distribution point name. + * + * @return the hash code. + */ + public int hashCode() { + int hash = hashCode; + if (hash == 0) { + hash = 1; + if (fullName != null) { + hash += fullName.hashCode(); + + } else { + hash += relativeName.hashCode(); + } + hashCode = hash; + } + return hash; + } + + /** + * Returns a printable string of the distribution point name. + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + if (fullName != null) { + sb.append("DistributionPointName:\n " + fullName + "\n"); + + } else { + sb.append("DistributionPointName:\n " + relativeName + "\n"); + } + + return sb.toString(); + } +} diff --git a/src/sun/security/x509/EDIPartyName.java b/src/sun/security/x509/EDIPartyName.java new file mode 100644 index 00000000..feaf30bf --- /dev/null +++ b/src/sun/security/x509/EDIPartyName.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import sun.security.util.*; + +/** + * This class defines the EDIPartyName of the GeneralName choice. + * The ASN.1 syntax for this is: + *

    + * EDIPartyName ::= SEQUENCE {
    + *     nameAssigner  [0]  DirectoryString OPTIONAL,
    + *     partyName     [1]  DirectoryString }
    + * 
    + * + * @author Hemma Prafullchandra + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class EDIPartyName implements GeneralNameInterface { + + // Private data members + private static final byte TAG_ASSIGNER = 0; + private static final byte TAG_PARTYNAME = 1; + + private String assigner = null; + private String party = null; + + private int myhash = -1; + + /** + * Create the EDIPartyName object from the specified names. + * + * @param assignerName the name of the assigner + * @param partyName the name of the EDI party. + */ + public EDIPartyName(String assignerName, String partyName) { + this.assigner = assignerName; + this.party = partyName; + } + + /** + * Create the EDIPartyName object from the specified name. + * + * @param partyName the name of the EDI party. + */ + public EDIPartyName(String partyName) { + this.party = partyName; + } + + /** + * Create the EDIPartyName object from the passed encoded Der value. + * + * @param derValue the encoded DER EDIPartyName. + * @exception IOException on error. + */ + public EDIPartyName(DerValue derValue) throws IOException { + DerInputStream in = new DerInputStream(derValue.toByteArray()); + DerValue[] seq = in.getSequence(2); + + int len = seq.length; + if (len < 1 || len > 2) + throw new IOException("Invalid encoding of EDIPartyName"); + + for (int i = 0; i < len; i++) { + DerValue opt = seq[i]; + if (opt.isContextSpecific(TAG_ASSIGNER) && + !opt.isConstructed()) { + if (assigner != null) + throw new IOException("Duplicate nameAssigner found in" + + " EDIPartyName"); + opt = opt.data.getDerValue(); + assigner = opt.getAsString(); + } + if (opt.isContextSpecific(TAG_PARTYNAME) && + !opt.isConstructed()) { + if (party != null) + throw new IOException("Duplicate partyName found in" + + " EDIPartyName"); + opt = opt.data.getDerValue(); + party = opt.getAsString(); + } + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_EDI); + } + + /** + * Encode the EDI party name into the DerOutputStream. + * + * @param out the DER stream to encode the EDIPartyName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tagged = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + if (assigner != null) { + DerOutputStream tmp2 = new DerOutputStream(); + // XXX - shd check is chars fit into PrintableString + tmp2.putPrintableString(assigner); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_ASSIGNER), tmp2); + } + if (party == null) + throw new IOException("Cannot have null partyName"); + + // XXX - shd check is chars fit into PrintableString + tmp.putPrintableString(party); + tagged.write(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_PARTYNAME), tmp); + + out.write(DerValue.tag_Sequence, tagged); + } + + /** + * Return the assignerName + * + * @returns String assignerName + */ + public String getAssignerName() { + return assigner; + } + + /** + * Return the partyName + * + * @returns String partyName + */ + public String getPartyName() { + return party; + } + + /** + * Compare this EDIPartyName with another. Does a byte-string + * comparison without regard to type of the partyName and + * the assignerName. + * + * @returns true if the two names match + */ + public boolean equals(Object other) { + if (!(other instanceof EDIPartyName)) + return false; + String otherAssigner = ((EDIPartyName)other).assigner; + if (this.assigner == null) { + if (otherAssigner != null) + return false; + } else { + if (!(this.assigner.equals(otherAssigner))) + return false; + } + String otherParty = ((EDIPartyName)other).party; + if (this.party == null) { + if (otherParty != null) + return false; + } else { + if (!(this.party.equals(otherParty))) + return false; + } + return true; + } + + /** + * Returns the hash code value for this EDIPartyName. + * + * @return a hash code value. + */ + public int hashCode() { + if (myhash == -1) { + myhash = 37 + party.hashCode(); + if (assigner != null) { + myhash = 37 * myhash + assigner.hashCode(); + } + } + return myhash; + } + + /** + * Return the printable string. + */ + public String toString() { + return ("EDIPartyName: " + + ((assigner == null) ? "" : + (" nameAssigner = " + assigner + ",")) + + " partyName = " + party); + } + + /** + * Return constraint type:
      + *
    • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain) + *
    • NAME_MATCH = 0: input name matches name + *
    • NAME_NARROWS = 1: input name narrows name + *
    • NAME_WIDENS = 2: input name widens name + *
    • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type + *
    . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is same type, but comparison operations are + * not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != NAME_EDI) + constraintType = NAME_DIFF_TYPE; + else { + throw new UnsupportedOperationException("Narrowing, widening, and matching of names not supported for EDIPartyName"); + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + throw new UnsupportedOperationException("subtreeDepth() not supported for EDIPartyName"); + } + +} diff --git a/src/sun/security/x509/ExtendedKeyUsageExtension.java b/src/sun/security/x509/ExtendedKeyUsageExtension.java new file mode 100644 index 00000000..68084602 --- /dev/null +++ b/src/sun/security/x509/ExtendedKeyUsageExtension.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; +import sun.security.util.ObjectIdentifier; + +/** + * This class defines the Extended Key Usage Extension, which + * indicates one or more purposes for which the certified public key + * may be used, in addition to or in place of the basic purposes + * indicated in the key usage extension field. This field is defined + * as follows:

    + * + * id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37}

    + * + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId

    + * + * KeyPurposeId ::= OBJECT IDENTIFIER

    + * + * Key purposes may be defined by any organization with a need. Object + * identifiers used to identify key purposes shall be assigned in + * accordance with IANA or ITU-T Rec. X.660 | ISO/IEC/ITU 9834-1.

    + * + * This extension may, at the option of the certificate issuer, be + * either critical or non-critical.

    + * + * If the extension is flagged critical, then the certificate MUST be + * used only for one of the purposes indicated.

    + * + * If the extension is flagged non-critical, then it indicates the + * intended purpose or purposes of the key, and may be used in finding + * the correct key/certificate of an entity that has multiple + * keys/certificates. It is an advisory field and does not imply that + * usage of the key is restricted by the certification authority to + * the purpose indicated. Certificate using applications may + * nevertheless require that a particular purpose be indicated in + * order for the certificate to be acceptable to that application.

    + + * If a certificate contains both a critical key usage field and a + * critical extended key usage field, then both fields MUST be + * processed independently and the certificate MUST only be used for a + * purpose consistent with both fields. If there is no purpose + * consistent with both fields, then the certificate MUST NOT be used + * for any purpose.

    + * + * @since 1.4 + */ +public class ExtendedKeyUsageExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.ExtendedKeyUsage"; + + /** + * Attribute names. + */ + public static final String NAME = "ExtendedKeyUsage"; + public static final String USAGES = "usages"; + + // OID defined in RFC 3280 Sections 4.2.1.13 + // more from http://www.alvestrand.no/objectid/1.3.6.1.5.5.7.3.html + private static final Map map = + new HashMap (); + + private static final int[] anyExtendedKeyUsageOidData = {2, 5, 29, 37, 0}; + private static final int[] serverAuthOidData = {1, 3, 6, 1, 5, 5, 7, 3, 1}; + private static final int[] clientAuthOidData = {1, 3, 6, 1, 5, 5, 7, 3, 2}; + private static final int[] codeSigningOidData = {1, 3, 6, 1, 5, 5, 7, 3, 3}; + private static final int[] emailProtectionOidData = {1, 3, 6, 1, 5, 5, 7, 3, 4}; + private static final int[] ipsecEndSystemOidData = {1, 3, 6, 1, 5, 5, 7, 3, 5}; + private static final int[] ipsecTunnelOidData = {1, 3, 6, 1, 5, 5, 7, 3, 6}; + private static final int[] ipsecUserOidData = {1, 3, 6, 1, 5, 5, 7, 3, 7}; + private static final int[] timeStampingOidData = {1, 3, 6, 1, 5, 5, 7, 3, 8}; + private static final int[] OCSPSigningOidData = {1, 3, 6, 1, 5, 5, 7, 3, 9}; + + static { + map.put(ObjectIdentifier.newInternal(anyExtendedKeyUsageOidData), "anyExtendedKeyUsage"); + map.put(ObjectIdentifier.newInternal(serverAuthOidData), "serverAuth"); + map.put(ObjectIdentifier.newInternal(clientAuthOidData), "clientAuth"); + map.put(ObjectIdentifier.newInternal(codeSigningOidData), "codeSigning"); + map.put(ObjectIdentifier.newInternal(emailProtectionOidData), "emailProtection"); + map.put(ObjectIdentifier.newInternal(ipsecEndSystemOidData), "ipsecEndSystem"); + map.put(ObjectIdentifier.newInternal(ipsecTunnelOidData), "ipsecTunnel"); + map.put(ObjectIdentifier.newInternal(ipsecUserOidData), "ipsecUser"); + map.put(ObjectIdentifier.newInternal(timeStampingOidData), "timeStamping"); + map.put(ObjectIdentifier.newInternal(OCSPSigningOidData), "OCSPSigning"); + }; + + /** + * Vector of KeyUsages for this object. + */ + private Vector keyUsages; + + // Encode this extension value. + private void encodeThis() throws IOException { + if (keyUsages == null || keyUsages.isEmpty()) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (int i = 0; i < keyUsages.size(); i++) { + tmp.putOID(keyUsages.elementAt(i)); + } + + os.write(DerValue.tag_Sequence, tmp); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a ExtendedKeyUsageExtension object from + * a Vector of Key Usages; the criticality is set to false. + * + * @param keyUsages the Vector of KeyUsages (ObjectIdentifiers) + */ + public ExtendedKeyUsageExtension(Vector keyUsages) + throws IOException { + this(Boolean.FALSE, keyUsages); + } + + /** + * Create a ExtendedKeyUsageExtension object from + * a Vector of KeyUsages with specified criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param keyUsages the Vector of KeyUsages (ObjectIdentifiers) + */ + public ExtendedKeyUsageExtension(Boolean critical, Vector keyUsages) + throws IOException { + this.keyUsages = keyUsages; + this.extensionId = PKIXExtensions.ExtendedKeyUsage_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from its DER encoded value and criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public ExtendedKeyUsageExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.ExtendedKeyUsage_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "ExtendedKeyUsageExtension."); + } + keyUsages = new Vector(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + ObjectIdentifier usage = seq.getOID(); + keyUsages.addElement(usage); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + if (keyUsages == null) return ""; + String usage = " "; + boolean first = true; + for (ObjectIdentifier oid: keyUsages) { + if(!first) { + usage += "\n "; + } + + String result = map.get(oid); + if (result != null) { + usage += result; + } else { + usage += oid.toString(); + } + first = false; + } + return super.toString() + "ExtendedKeyUsages [\n" + + usage + "\n]\n"; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.ExtendedKeyUsage_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with instanceof + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(USAGES)) { + if (!(obj instanceof Vector)) { + throw new IOException("Attribute value should be of type Vector."); + } + this.keyUsages = (Vector)obj; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:ExtendedKeyUsageExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Vector get(String name) throws IOException { + if (name.equalsIgnoreCase(USAGES)) { + //XXXX May want to consider cloning this + return keyUsages; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:ExtendedKeyUsageExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(USAGES)) { + keyUsages = null; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:ExtendedKeyUsageExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(USAGES); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + public List getExtendedKeyUsage() { + List al = new ArrayList(keyUsages.size()); + for (ObjectIdentifier oid : keyUsages) { + al.add(oid.toString()); + } + return al; + } + +} diff --git a/src/sun/security/x509/Extension.java b/src/sun/security/x509/Extension.java new file mode 100644 index 00000000..a5bcc864 --- /dev/null +++ b/src/sun/security/x509/Extension.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import sun.security.util.*; + +/** + * Represent a X509 Extension Attribute. + * + *

    Extensions are additional attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + *

    Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + *

    + * ASN.1 definition of Extension:
    + * Extension ::= SEQUENCE {
    + *      ExtensionId     OBJECT IDENTIFIER,
    + *      critical        BOOLEAN DEFAULT FALSE,
    + *      extensionValue  OCTET STRING
    + * }
    + * 
    + * All subclasses need to implement a constructor of the form + *
    + *      (Boolean, Object)
    + * 
    + * where the Object is typically an array of DER encoded bytes. + *

    + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class Extension implements java.security.cert.Extension { + + protected ObjectIdentifier extensionId = null; + protected boolean critical = false; + protected byte[] extensionValue = null; + + /** + * Default constructor. Used only by sub-classes. + */ + public Extension() { } + + /** + * Constructs an extension from a DER encoded array of bytes. + */ + public Extension(DerValue derVal) throws IOException { + + DerInputStream in = derVal.toDerInputStream(); + + // Object identifier + extensionId = in.getOID(); + + // If the criticality flag was false, it will not have been encoded. + DerValue val = in.getDerValue(); + if (val.tag == DerValue.tag_Boolean) { + critical = val.getBoolean(); + + // Extension value (DER encoded) + val = in.getDerValue(); + extensionValue = val.getOctetString(); + } else { + critical = false; + extensionValue = val.getOctetString(); + } + } + + /** + * Constructs an Extension from individual components of ObjectIdentifier, + * criticality and the DER encoded OctetString. + * + * @param extensionId the ObjectIdentifier of the extension + * @param critical the boolean indicating if the extension is critical + * @param extensionValue the DER encoded octet string of the value. + */ + public Extension(ObjectIdentifier extensionId, boolean critical, + byte[] extensionValue) throws IOException { + this.extensionId = extensionId; + this.critical = critical; + // passed in a DER encoded octet string, strip off the tag + // and length + DerValue inDerVal = new DerValue(extensionValue); + this.extensionValue = inDerVal.getOctetString(); + } + + /** + * Constructs an Extension from another extension. To be used for + * creating decoded subclasses. + * + * @param ext the extension to create from. + */ + public Extension(Extension ext) { + this.extensionId = ext.extensionId; + this.critical = ext.critical; + this.extensionValue = ext.extensionValue; + } + + /** + * Constructs an Extension from individual components of ObjectIdentifier, + * criticality and the raw encoded extension value. + * + * @param extensionId the ObjectIdentifier of the extension + * @param critical the boolean indicating if the extension is critical + * @param rawExtensionValue the raw DER-encoded extension value (this + * is not the encoded OctetString). + */ + public static Extension newExtension(ObjectIdentifier extensionId, + boolean critical, byte[] rawExtensionValue) throws IOException { + Extension ext = new Extension(); + ext.extensionId = extensionId; + ext.critical = critical; + ext.extensionValue = rawExtensionValue; + return ext; + } + + public void encode(OutputStream out) throws IOException { + if (out == null) { + throw new NullPointerException(); + } + + DerOutputStream dos1 = new DerOutputStream(); + DerOutputStream dos2 = new DerOutputStream(); + + dos1.putOID(extensionId); + if (critical) { + dos1.putBoolean(critical); + } + dos1.putOctetString(extensionValue); + + dos2.write(DerValue.tag_Sequence, dos1); + out.write(dos2.toByteArray()); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors + */ + public void encode(DerOutputStream out) throws IOException { + + if (extensionId == null) + throw new IOException("Null OID to encode for the extension!"); + if (extensionValue == null) + throw new IOException("No value to encode for the extension!"); + + DerOutputStream dos = new DerOutputStream(); + + dos.putOID(extensionId); + if (critical) + dos.putBoolean(critical); + dos.putOctetString(extensionValue); + + out.write(DerValue.tag_Sequence, dos); + } + + /** + * Returns true if extension is critical. + */ + public boolean isCritical() { + return critical; + } + + /** + * Returns the ObjectIdentifier of the extension. + */ + public ObjectIdentifier getExtensionId() { + return extensionId; + } + + public byte[] getValue() { + return extensionValue.clone(); + } + + /** + * Returns the extension value as an byte array for further processing. + * Note, this is the raw DER value of the extension, not the DER + * encoded octet string which is in the certificate. + * This method does not return a clone; it is the responsibility of the + * caller to clone the array if necessary. + */ + public byte[] getExtensionValue() { + return extensionValue; + } + + public String getId() { + return extensionId.toString(); + } + + /** + * Returns the Extension in user readable form. + */ + public String toString() { + String s = "ObjectId: " + extensionId.toString(); + if (critical) { + s += " Criticality=true\n"; + } else { + s += " Criticality=false\n"; + } + return (s); + } + + // Value to mix up the hash + private static final int hashMagic = 31; + + /** + * Returns a hashcode value for this Extension. + * + * @return the hashcode value. + */ + public int hashCode() { + int h = 0; + if (extensionValue != null) { + byte[] val = extensionValue; + int len = val.length; + while (len > 0) + h += len * val[--len]; + } + h = h * hashMagic + extensionId.hashCode(); + h = h * hashMagic + (critical?1231:1237); + return h; + } + + /** + * Compares this Extension for equality with the specified + * object. If the other object is an + * instanceof Extension, then + * its encoded form is retrieved and compared with the + * encoded form of this Extension. + * + * @param other the object to test for equality with this Extension. + * @return true iff the other object is of type Extension, and the + * criticality flag, object identifier and encoded extension value of + * the two Extensions match, false otherwise. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof Extension)) + return false; + Extension otherExt = (Extension) other; + if (critical != otherExt.critical) + return false; + if (!extensionId.equals((Object)otherExt.extensionId)) + return false; + return Arrays.equals(extensionValue, otherExt.extensionValue); + } +} diff --git a/src/sun/security/x509/FreshestCRLExtension.java b/src/sun/security/x509/FreshestCRLExtension.java new file mode 100644 index 00000000..b1932d1a --- /dev/null +++ b/src/sun/security/x509/FreshestCRLExtension.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +/** + * Represents the Freshest CRL Extension. + * + *

    + * The extension identifies how delta CRL information for a + * complete CRL is obtained. + * + *

    + * The extension is defined in Section 5.2.6 of + * Internet X.509 PKI Certific +ate and Certificate Revocation List (CRL) Profile. + * + *

    + * Its ASN.1 definition is as follows: + *

    + *     id-ce-freshestCRL OBJECT IDENTIFIER ::=  { id-ce 46 }
    + *
    + *     FreshestCRL ::= CRLDistributionPoints
    + * 
    + * + * @since 1.6 + */ +public class FreshestCRLExtension extends CRLDistributionPointsExtension { + + /** + * Attribute name. + */ + public static final String NAME = "FreshestCRL"; + + /** + * Creates a freshest CRL extension. + * The criticality is set to false. + * + * @param distributionPoints the list of delta CRL distribution points. + */ + public FreshestCRLExtension(List distributionPoints) + throws IOException { + + super(PKIXExtensions.FreshestCRL_Id, false, distributionPoints, NAME); + } + + /** + * Creates the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception IOException on decoding error. + */ + public FreshestCRLExtension(Boolean critical, Object value) + throws IOException { + super(PKIXExtensions.FreshestCRL_Id, critical.booleanValue(), value, + NAME); + } + + /** + * Writes the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + super.encode(out, PKIXExtensions.FreshestCRL_Id, false); + } +} diff --git a/src/sun/security/x509/GeneralName.java b/src/sun/security/x509/GeneralName.java new file mode 100644 index 00000000..e9b69f0b --- /dev/null +++ b/src/sun/security/x509/GeneralName.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.*; + +/** + * This class implements the ASN.1 GeneralName object class. + *

    + * The ASN.1 syntax for this is: + *

    + * GeneralName ::= CHOICE {
    + *    otherName                       [0]     OtherName,
    + *    rfc822Name                      [1]     IA5String,
    + *    dNSName                         [2]     IA5String,
    + *    x400Address                     [3]     ORAddress,
    + *    directoryName                   [4]     Name,
    + *    ediPartyName                    [5]     EDIPartyName,
    + *    uniformResourceIdentifier       [6]     IA5String,
    + *    iPAddress                       [7]     OCTET STRING,
    + *    registeredID                    [8]     OBJECT IDENTIFIER
    + * }
    + * 
    + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralName { + + // Private data members + private GeneralNameInterface name = null; + + /** + * Default constructor for the class. + * + * @param name the selected CHOICE from the list. + * @throws NullPointerException if name is null + */ + public GeneralName(GeneralNameInterface name) { + if (name == null) { + throw new NullPointerException("GeneralName must not be null"); + } + this.name = name; + } + + /** + * Create the object from its DER encoded value. + * + * @param encName the DER encoded GeneralName. + */ + public GeneralName(DerValue encName) throws IOException { + this(encName, false); + } + + /** + * Create the object from its DER encoded value. + * + * @param encName the DER encoded GeneralName. + * @param nameConstraint true if general name is a name constraint + */ + public GeneralName(DerValue encName, boolean nameConstraint) + throws IOException { + short tag = (byte)(encName.tag & 0x1f); + + // All names except for NAME_DIRECTORY should be encoded with the + // IMPLICIT tag. + switch (tag) { + case GeneralNameInterface.NAME_ANY: + if (encName.isContextSpecific() && encName.isConstructed()) { + encName.resetTag(DerValue.tag_Sequence); + name = new OtherName(encName); + } else { + throw new IOException("Invalid encoding of Other-Name"); + } + break; + + case GeneralNameInterface.NAME_RFC822: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = new RFC822Name(encName); + } else { + throw new IOException("Invalid encoding of RFC822 name"); + } + break; + + case GeneralNameInterface.NAME_DNS: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = new DNSName(encName); + } else { + throw new IOException("Invalid encoding of DNS name"); + } + break; + + case GeneralNameInterface.NAME_URI: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_IA5String); + name = (nameConstraint ? URIName.nameConstraint(encName) : + new URIName(encName)); + } else { + throw new IOException("Invalid encoding of URI"); + } + break; + + case GeneralNameInterface.NAME_IP: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_OctetString); + name = new IPAddressName(encName); + } else { + throw new IOException("Invalid encoding of IP address"); + } + break; + + case GeneralNameInterface.NAME_OID: + if (encName.isContextSpecific() && !encName.isConstructed()) { + encName.resetTag(DerValue.tag_ObjectId); + name = new OIDName(encName); + } else { + throw new IOException("Invalid encoding of OID name"); + } + break; + + case GeneralNameInterface.NAME_DIRECTORY: + if (encName.isContextSpecific() && encName.isConstructed()) { + name = new X500Name(encName.getData()); + } else { + throw new IOException("Invalid encoding of Directory name"); + } + break; + + case GeneralNameInterface.NAME_EDI: + if (encName.isContextSpecific() && encName.isConstructed()) { + encName.resetTag(DerValue.tag_Sequence); + name = new EDIPartyName(encName); + } else { + throw new IOException("Invalid encoding of EDI name"); + } + break; + + default: + throw new IOException("Unrecognized GeneralName tag, (" + + tag +")"); + } + } + + /** + * Return the type of the general name. + */ + public int getType() { + return name.getType(); + } + + /** + * Return the GeneralNameInterface name. + */ + public GeneralNameInterface getName() { + //XXXX May want to consider cloning this + return name; + } + + /** + * Return the name as user readable string + */ + public String toString() { + return name.toString(); + } + + /** + * Compare this GeneralName with another + * + * @param other GeneralName to compare to this + * @returns true if match + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof GeneralName)) + return false; + GeneralNameInterface otherGNI = ((GeneralName)other).name; + try { + return name.constrains(otherGNI) == GeneralNameInterface.NAME_MATCH; + } catch (UnsupportedOperationException ioe) { + return false; + } + } + + /** + * Returns the hash code for this GeneralName. + * + * @return a hash code value. + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Encode the name to the specified DerOutputStream. + * + * @param out the DerOutputStream to encode the the GeneralName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + name.encode(tmp); + int nameType = name.getType(); + if (nameType == GeneralNameInterface.NAME_ANY || + nameType == GeneralNameInterface.NAME_X400 || + nameType == GeneralNameInterface.NAME_EDI) { + + // implicit, constructed form + out.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)nameType), tmp); + } else if (nameType == GeneralNameInterface.NAME_DIRECTORY) { + // explicit, constructed form since underlying tag is CHOICE + // (see X.680 section 30.6, part c) + out.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte)nameType), tmp); + } else { + // implicit, primitive form + out.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, (byte)nameType), tmp); + } + } +} diff --git a/src/sun/security/x509/GeneralNameInterface.java b/src/sun/security/x509/GeneralNameInterface.java new file mode 100644 index 00000000..1d81977e --- /dev/null +++ b/src/sun/security/x509/GeneralNameInterface.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.*; + +/** + * This interface specifies the abstract methods which have to be + * implemented by all the members of the GeneralNames ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public interface GeneralNameInterface { + /** + * The list of names supported. + */ + public static final int NAME_ANY = 0; + public static final int NAME_RFC822 = 1; + public static final int NAME_DNS = 2; + public static final int NAME_X400 = 3; + public static final int NAME_DIRECTORY = 4; + public static final int NAME_EDI = 5; + public static final int NAME_URI = 6; + public static final int NAME_IP = 7; + public static final int NAME_OID = 8; + + /** + * The list of constraint results. + */ + public static final int NAME_DIFF_TYPE = -1; /* input name is different type from name (i.e. does not constrain) */ + public static final int NAME_MATCH = 0; /* input name matches name */ + public static final int NAME_NARROWS = 1; /* input name narrows name */ + public static final int NAME_WIDENS = 2; /* input name widens name */ + public static final int NAME_SAME_TYPE = 3; /* input name does not match, narrow, or widen, but is same type */ + + /** + * Return the type of the general name, as + * defined above. + */ + int getType(); + + /** + * Encode the name to the specified DerOutputStream. + * + * @param out the DerOutputStream to encode the GeneralName to. + * @exception IOException thrown if the GeneralName could not be + * encoded. + */ + void encode(DerOutputStream out) throws IOException; + + /** + * Return type of constraint inputName places on this name:
      + *
    • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain). + *
    • NAME_MATCH = 0: input name matches name. + *
    • NAME_NARROWS = 1: input name narrows name (is lower in the naming subtree) + *
    • NAME_WIDENS = 2: input name widens name (is higher in the naming subtree) + *
    • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type. + *
    . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is same type, but comparison operations are + * not supported for this name type. + */ + int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException; + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + int subtreeDepth() throws UnsupportedOperationException; +} diff --git a/src/sun/security/x509/GeneralNames.java b/src/sun/security/x509/GeneralNames.java new file mode 100644 index 00000000..8e081aae --- /dev/null +++ b/src/sun/security/x509/GeneralNames.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.util.*; +import java.io.IOException; + +import sun.security.util.*; + +/** + * This object class represents the GeneralNames type required in + * X509 certificates. + *

    The ASN.1 syntax for this is: + *

    + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
    + * 
    + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * + */ +public class GeneralNames { + + private final List names; + + /** + * Create the GeneralNames, decoding from the passed DerValue. + * + * @param derVal the DerValue to construct the GeneralNames from. + * @exception IOException on error. + */ + public GeneralNames(DerValue derVal) throws IOException { + this(); + if (derVal.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for GeneralNames."); + } + if (derVal.data.available() == 0) { + throw new IOException("No data available in " + + "passed DER encoded value."); + } + // Decode all the GeneralName's + while (derVal.data.available() != 0) { + DerValue encName = derVal.data.getDerValue(); + + GeneralName name = new GeneralName(encName); + add(name); + } + } + + /** + * The default constructor for this class. + */ + public GeneralNames() { + names = new ArrayList(); + } + + public GeneralNames add(GeneralName name) { + if (name == null) { + throw new NullPointerException(); + } + names.add(name); + return this; + } + + public GeneralName get(int index) { + return names.get(index); + } + + public boolean isEmpty() { + return names.isEmpty(); + } + + public int size() { + return names.size(); + } + + public Iterator iterator() { + return names.iterator(); + } + + public List names() { + return names; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on error. + */ + public void encode(DerOutputStream out) throws IOException { + if (isEmpty()) { + return; + } + + DerOutputStream temp = new DerOutputStream(); + for (GeneralName gn : names) { + gn.encode(temp); + } + out.write(DerValue.tag_Sequence, temp); + } + + /** + * compare this GeneralNames to other object for equality + * + * @returns true iff this equals other + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof GeneralNames == false) { + return false; + } + GeneralNames other = (GeneralNames)obj; + return this.names.equals(other.names); + } + + public int hashCode() { + return names.hashCode(); + } + + public String toString() { + return names.toString(); + } + +} diff --git a/src/sun/security/x509/GeneralSubtree.java b/src/sun/security/x509/GeneralSubtree.java new file mode 100644 index 00000000..9c12665c --- /dev/null +++ b/src/sun/security/x509/GeneralSubtree.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.*; + +import sun.security.util.*; + +/** + * Represent the GeneralSubtree ASN.1 object, whose syntax is: + *
    + * GeneralSubtree ::= SEQUENCE {
    + *    base             GeneralName,
    + *    minimum  [0]     BaseDistance DEFAULT 0,
    + *    maximum  [1]     BaseDistance OPTIONAL
    + * }
    + * BaseDistance ::= INTEGER (0..MAX)
    + * 
    + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class GeneralSubtree { + private static final byte TAG_MIN = 0; + private static final byte TAG_MAX = 1; + private static final int MIN_DEFAULT = 0; + + private GeneralName name; + private int minimum = MIN_DEFAULT; + private int maximum = -1; + + private int myhash = -1; + + /** + * The default constructor for the class. + * + * @params name the GeneralName + * @params min the minimum BaseDistance + * @params max the maximum BaseDistance + */ + public GeneralSubtree(GeneralName name, int min, int max) { + this.name = name; + this.minimum = min; + this.maximum = max; + } + + /** + * Create the object from its DER encoded form. + * + * @param val the DER encoded from of the same. + */ + public GeneralSubtree(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for GeneralSubtree."); + } + name = new GeneralName(val.data.getDerValue(), true); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_MIN) && !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Integer); + minimum = opt.getInteger(); + + } else if (opt.isContextSpecific(TAG_MAX) && !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Integer); + maximum = opt.getInteger(); + } else + throw new IOException("Invalid encoding of GeneralSubtree."); + } + } + + /** + * Return the GeneralName. + * + * @return the GeneralName + */ + public GeneralName getName() { + //XXXX May want to consider cloning this + return name; + } + + /** + * Return the minimum BaseDistance. + * + * @return the minimum BaseDistance. Default is 0 if not set. + */ + public int getMinimum() { + return minimum; + } + + /** + * Return the maximum BaseDistance. + * + * @return the maximum BaseDistance, or -1 if not set. + */ + public int getMaximum() { + return maximum; + } + + /** + * Return a printable string of the GeneralSubtree. + */ + public String toString() { + String s = "\n GeneralSubtree: [\n" + + " GeneralName: " + ((name == null) ? "" : name.toString()) + + "\n Minimum: " + minimum; + if (maximum == -1) { + s += "\t Maximum: undefined"; + } else + s += "\t Maximum: " + maximum; + s += " ]\n"; + return (s); + } + + /** + * Compare this GeneralSubtree with another + * + * @param other GeneralSubtree to compare to this + * @returns true if match + */ + public boolean equals(Object other) { + if (!(other instanceof GeneralSubtree)) + return false; + GeneralSubtree otherGS = (GeneralSubtree)other; + if (this.name == null) { + if (otherGS.name != null) { + return false; + } + } else { + if (!((this.name).equals(otherGS.name))) + return false; + } + if (this.minimum != otherGS.minimum) + return false; + if (this.maximum != otherGS.maximum) + return false; + return true; + } + + /** + * Returns the hash code for this GeneralSubtree. + * + * @return a hash code value. + */ + public int hashCode() { + if (myhash == -1) { + myhash = 17; + if (name != null) { + myhash = 37 * myhash + name.hashCode(); + } + if (minimum != MIN_DEFAULT) { + myhash = 37 * myhash + minimum; + } + if (maximum != -1) { + myhash = 37 * myhash + maximum; + } + } + return myhash; + } + + /** + * Encode the GeneralSubtree. + * + * @params out the DerOutputStream to encode this object to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream seq = new DerOutputStream(); + + name.encode(seq); + + if (minimum != MIN_DEFAULT) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(minimum); + seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_MIN), tmp); + } + if (maximum != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(maximum); + seq.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_MAX), tmp); + } + out.write(DerValue.tag_Sequence, seq); + } +} diff --git a/src/sun/security/x509/GeneralSubtrees.java b/src/sun/security/x509/GeneralSubtrees.java new file mode 100644 index 00000000..9c5e6212 --- /dev/null +++ b/src/sun/security/x509/GeneralSubtrees.java @@ -0,0 +1,525 @@ +/* + * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.*; +import java.util.*; + +import sun.security.util.*; + +/** + * Represent the GeneralSubtrees ASN.1 object. + *

    + * The ASN.1 for this is + *

    + * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
    + * 
    + *

    + * + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @author Andreas Sterbenz + */ +public class GeneralSubtrees implements Cloneable { + + private final List trees; + + // Private variables + private static final int NAME_DIFF_TYPE = GeneralNameInterface.NAME_DIFF_TYPE; + private static final int NAME_MATCH = GeneralNameInterface.NAME_MATCH; + private static final int NAME_NARROWS = GeneralNameInterface.NAME_NARROWS; + private static final int NAME_WIDENS = GeneralNameInterface.NAME_WIDENS; + private static final int NAME_SAME_TYPE = GeneralNameInterface.NAME_SAME_TYPE; + + /** + * The default constructor for the class. + */ + public GeneralSubtrees() { + trees = new ArrayList(); + } + + private GeneralSubtrees(GeneralSubtrees source) { + trees = new ArrayList(source.trees); + } + + /** + * Create the object from the passed DER encoded form. + * + * @param val the DER encoded form of the same. + */ + public GeneralSubtrees(DerValue val) throws IOException { + this(); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of GeneralSubtrees."); + } + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + GeneralSubtree tree = new GeneralSubtree(opt); + add(tree); + } + } + + public GeneralSubtree get(int index) { + return trees.get(index); + } + + public void remove(int index) { + trees.remove(index); + } + + public void add(GeneralSubtree tree) { + if (tree == null) { + throw new NullPointerException(); + } + trees.add(tree); + } + + public boolean contains(GeneralSubtree tree) { + if (tree == null) { + throw new NullPointerException(); + } + return trees.contains(tree); + } + + public int size() { + return trees.size(); + } + + public Iterator iterator() { + return trees.iterator(); + } + + public List trees() { + return trees; + } + + public Object clone() { + return new GeneralSubtrees(this); + } + + /** + * Return a printable string of the GeneralSubtree. + */ + public String toString() { + String s = " GeneralSubtrees:\n" + trees.toString() + "\n"; + return s; + } + + /** + * Encode the GeneralSubtrees. + * + * @params out the DerOutputStrean to encode this object to. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream seq = new DerOutputStream(); + + for (int i = 0, n = size(); i < n; i++) { + get(i).encode(seq); + } + out.write(DerValue.tag_Sequence, seq); + } + + /** + * Compare two general subtrees by comparing the subtrees + * of each. + * + * @param other GeneralSubtrees to compare to this + * @returns true if match + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof GeneralSubtrees == false) { + return false; + } + GeneralSubtrees other = (GeneralSubtrees)obj; + return this.trees.equals(other.trees); + } + + public int hashCode() { + return trees.hashCode(); + } + + /** + * Return the GeneralNameInterface form of the GeneralName in one of + * the GeneralSubtrees. + * + * @param ndx index of the GeneralSubtree from which to obtain the name + */ + private GeneralNameInterface getGeneralNameInterface(int ndx) { + return getGeneralNameInterface(get(ndx)); + } + + private static GeneralNameInterface getGeneralNameInterface(GeneralSubtree gs) { + GeneralName gn = gs.getName(); + GeneralNameInterface gni = gn.getName(); + return gni; + } + + /** + * minimize this GeneralSubtrees by removing all redundant entries. + * Internal method used by intersect and reduce. + */ + private void minimize() { + + // Algorithm: compare each entry n to all subsequent entries in + // the list: if any subsequent entry matches or widens entry n, + // remove entry n. If any subsequent entries narrow entry n, remove + // the subsequent entries. + for (int i = 0; i < size(); i++) { + GeneralNameInterface current = getGeneralNameInterface(i); + boolean remove1 = false; + + /* compare current to subsequent elements */ + for (int j = i + 1; j < size(); j++) { + GeneralNameInterface subsequent = getGeneralNameInterface(j); + switch (current.constrains(subsequent)) { + case GeneralNameInterface.NAME_DIFF_TYPE: + /* not comparable; different name types; keep checking */ + continue; + case GeneralNameInterface.NAME_MATCH: + /* delete one of the duplicates */ + remove1 = true; + break; + case GeneralNameInterface.NAME_NARROWS: + /* subsequent narrows current */ + /* remove narrower name (subsequent) */ + remove(j); + j--; /* continue check with new subsequent */ + continue; + case GeneralNameInterface.NAME_WIDENS: + /* subsequent widens current */ + /* remove narrower name current */ + remove1 = true; + break; + case GeneralNameInterface.NAME_SAME_TYPE: + /* keep both for now; keep checking */ + continue; + } + break; + } /* end of this pass of subsequent elements */ + + if (remove1) { + remove(i); + i--; /* check the new i value */ + } + + } + } + + /** + * create a subtree containing an instance of the input + * name type that widens all other names of that type. + * + * @params name GeneralNameInterface name + * @returns GeneralSubtree containing widest name of that type + * @throws RuntimeException on error (should not occur) + */ + private GeneralSubtree createWidestSubtree(GeneralNameInterface name) { + try { + GeneralName newName; + switch (name.getType()) { + case GeneralNameInterface.NAME_ANY: + // Create new OtherName with same OID as baseName, but + // empty value + ObjectIdentifier otherOID = ((OtherName)name).getOID(); + newName = new GeneralName(new OtherName(otherOID, null)); + break; + case GeneralNameInterface.NAME_RFC822: + newName = new GeneralName(new RFC822Name("")); + break; + case GeneralNameInterface.NAME_DNS: + newName = new GeneralName(new DNSName("")); + break; + case GeneralNameInterface.NAME_X400: + newName = new GeneralName(new X400Address((byte[])null)); + break; + case GeneralNameInterface.NAME_DIRECTORY: + newName = new GeneralName(new X500Name("")); + break; + case GeneralNameInterface.NAME_EDI: + newName = new GeneralName(new EDIPartyName("")); + break; + case GeneralNameInterface.NAME_URI: + newName = new GeneralName(new URIName("")); + break; + case GeneralNameInterface.NAME_IP: + newName = new GeneralName(new IPAddressName((byte[])null)); + break; + case GeneralNameInterface.NAME_OID: + newName = new GeneralName + (new OIDName(new ObjectIdentifier((int[])null))); + break; + default: + throw new IOException + ("Unsupported GeneralNameInterface type: " + name.getType()); + } + return new GeneralSubtree(newName, 0, -1); + } catch (IOException e) { + throw new RuntimeException("Unexpected error: " + e, e); + } + } + + /** + * intersect this GeneralSubtrees with other. This function + * is used in merging permitted NameConstraints. The operation + * is performed as follows: + *
      + *
    • If a name in other narrows all names of the same type in this, + * the result will contain the narrower name and none of the + * names it narrows. + *
    • If a name in other widens all names of the same type in this, + * the result will not contain the wider name. + *
    • If a name in other does not share the same subtree with any name + * of the same type in this, then the name is added to the list + * of GeneralSubtrees returned. These names should be added to + * the list of names that are specifically excluded. The reason + * is that, if the intersection is empty, then no names of that + * type are permitted, and the only way to express this in + * NameConstraints is to include the name in excludedNames. + *
    • If a name in this has no name of the same type in other, then + * the result contains the name in this. No name of a given type + * means the name type is completely permitted. + *
    • If a name in other has no name of the same type in this, then + * the result contains the name in other. This means that + * the name is now constrained in some way, whereas before it was + * completely permitted. + *
        + * + * @param other GeneralSubtrees to be intersected with this + * @returns GeneralSubtrees to be merged with excluded; these are + * empty-valued name types corresponding to entries that were + * of the same type but did not share the same subtree between + * this and other. Returns null if no such. + */ + public GeneralSubtrees intersect(GeneralSubtrees other) { + + if (other == null) { + throw new NullPointerException("other GeneralSubtrees must not be null"); + } + + GeneralSubtrees newThis = new GeneralSubtrees(); + GeneralSubtrees newExcluded = null; + + // Step 1: If this is empty, just add everything in other to this and + // return no new excluded entries + if (size() == 0) { + union(other); + return null; + } + + // Step 2: For ease of checking the subtrees, minimize them by + // constructing versions that contain only the widest instance of + // each type + this.minimize(); + other.minimize(); + + // Step 3: Check each entry in this to see whether we keep it or + // remove it, and whether we add anything to newExcluded or newThis. + // We keep an entry in this unless it is narrowed by all entries in + // other. We add an entry to newExcluded if there is at least one + // entry of the same nameType in other, but this entry does + // not share the same subtree with any of the entries in other. + // We add an entry from other to newThis if there is no name of the + // same type in this. + for (int i = 0; i < size(); i++) { + GeneralNameInterface thisEntry = getGeneralNameInterface(i); + boolean removeThisEntry = false; + + // Step 3a: If the widest name of this type in other narrows + // thisEntry, remove thisEntry and add widest other to newThis. + // Simultaneously, check for situation where there is a name of + // this type in other, but no name in other matches, narrows, + // or widens thisEntry. + boolean sameType = false; + for (int j = 0; j < other.size(); j++) { + GeneralSubtree otherEntryGS = other.get(j); + GeneralNameInterface otherEntry = + getGeneralNameInterface(otherEntryGS); + switch (thisEntry.constrains(otherEntry)) { + case NAME_NARROWS: + remove(i); + i--; + newThis.add(otherEntryGS); + sameType = false; + break; + case NAME_SAME_TYPE: + sameType = true; + continue; + case NAME_MATCH: + case NAME_WIDENS: + sameType = false; + break; + case NAME_DIFF_TYPE: + default: + continue; + } + break; + } + + // Step 3b: If sameType is still true, we have the situation + // where there was a name of the same type as thisEntry in + // other, but no name in other widened, matched, or narrowed + // thisEntry. + if (sameType) { + + // Step 3b.1: See if there are any entries in this and other + // with this type that match, widen, or narrow each other. + // If not, then we need to add a "widest subtree" of this + // type to excluded. + boolean intersection = false; + for (int j = 0; j < size(); j++) { + GeneralNameInterface thisAltEntry = getGeneralNameInterface(j); + + if (thisAltEntry.getType() == thisEntry.getType()) { + for (int k = 0; k < other.size(); k++) { + GeneralNameInterface othAltEntry = + other.getGeneralNameInterface(k); + + int constraintType = + thisAltEntry.constrains(othAltEntry); + if (constraintType == NAME_MATCH || + constraintType == NAME_WIDENS || + constraintType == NAME_NARROWS) { + intersection = true; + break; + } + } + } + } + if (intersection == false) { + if (newExcluded == null) { + newExcluded = new GeneralSubtrees(); + } + GeneralSubtree widestSubtree = + createWidestSubtree(thisEntry); + if (!newExcluded.contains(widestSubtree)) { + newExcluded.add(widestSubtree); + } + } + + // Step 3b.2: Remove thisEntry from this + remove(i); + i--; + } + } + + // Step 4: Add all entries in newThis to this + if (newThis.size() > 0) { + union(newThis); + } + + // Step 5: Add all entries in other that do not have any entry of the + // same type in this to this + for (int i = 0; i < other.size(); i++) { + GeneralSubtree otherEntryGS = other.get(i); + GeneralNameInterface otherEntry = getGeneralNameInterface(otherEntryGS); + boolean diffType = false; + for (int j = 0; j < size(); j++) { + GeneralNameInterface thisEntry = getGeneralNameInterface(j); + switch (thisEntry.constrains(otherEntry)) { + case NAME_DIFF_TYPE: + diffType = true; + // continue to see if we find something later of the + // same type + continue; + case NAME_NARROWS: + case NAME_SAME_TYPE: + case NAME_MATCH: + case NAME_WIDENS: + diffType = false; // we found an entry of the same type + // break because we know we won't be adding it to + // this now + break; + default: + continue; + } + break; + } + if (diffType) { + add(otherEntryGS); + } + } + + // Step 6: Return the newExcluded GeneralSubtrees + return newExcluded; + } + + /** + * construct union of this GeneralSubtrees with other. + * + * @param other GeneralSubtrees to be united with this + */ + public void union(GeneralSubtrees other) { + if (other != null) { + for (int i = 0, n = other.size(); i < n; i++) { + add(other.get(i)); + } + // Minimize this + minimize(); + } + } + + /** + * reduce this GeneralSubtrees by contents of another. This function + * is used in merging excluded NameConstraints with permitted NameConstraints + * to obtain a minimal form of permitted NameConstraints. It is an + * optimization, and does not affect correctness of the results. + * + * @param excluded GeneralSubtrees + */ + public void reduce(GeneralSubtrees excluded) { + if (excluded == null) { + return; + } + for (int i = 0, n = excluded.size(); i < n; i++) { + GeneralNameInterface excludedName = excluded.getGeneralNameInterface(i); + for (int j = 0; j < size(); j++) { + GeneralNameInterface permitted = getGeneralNameInterface(j); + switch (excludedName.constrains(permitted)) { + case GeneralNameInterface.NAME_DIFF_TYPE: + break; + case GeneralNameInterface.NAME_MATCH: + remove(j); + j--; + break; + case GeneralNameInterface.NAME_NARROWS: + /* permitted narrows excluded */ + remove(j); + j--; + break; + case GeneralNameInterface.NAME_WIDENS: + /* permitted widens excluded */ + break; + case GeneralNameInterface.NAME_SAME_TYPE: + break; + } + } /* end of this pass of permitted */ + } /* end of pass of excluded */ + } +} diff --git a/src/sun/security/x509/IPAddressName.java b/src/sun/security/x509/IPAddressName.java new file mode 100644 index 00000000..084a6f37 --- /dev/null +++ b/src/sun/security/x509/IPAddressName.java @@ -0,0 +1,489 @@ +/* + * Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.lang.Integer; +import java.net.InetAddress; +import java.util.Arrays; +import sun.misc.HexDumpEncoder; +import sun.security.util.BitArray; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * This class implements the IPAddressName as required by the GeneralNames + * ASN.1 object. Both IPv4 and IPv6 addresses are supported using the + * formats specified in IETF PKIX RFC2459. + *

        + * [RFC2459 4.2.1.7 Subject Alternative Name] + * When the subjectAltName extension contains a iPAddress, the address + * MUST be stored in the octet string in "network byte order," as + * specified in RFC 791. The least significant bit (LSB) of + * each octet is the LSB of the corresponding byte in the network + * address. For IP Version 4, as specified in RFC 791, the octet string + * MUST contain exactly four octets. For IP Version 6, as specified in + * RFC 1883, the octet string MUST contain exactly sixteen octets. + *

        + * [RFC2459 4.2.1.11 Name Constraints] + * The syntax of iPAddress MUST be as described in section 4.2.1.7 with + * the following additions specifically for Name Constraints. For IPv4 + * addresses, the ipAddress field of generalName MUST contain eight (8) + * octets, encoded in the style of RFC 1519 (CIDR) to represent an + * address range.[RFC 1519] For IPv6 addresses, the ipAddress field + * MUST contain 32 octets similarly encoded. For example, a name + * constraint for "class C" subnet 10.9.8.0 shall be represented as the + * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation + * 10.9.8.0/255.255.255.0. + *

        + * @see GeneralName + * @see GeneralNameInterface + * @see GeneralNames + * + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class IPAddressName implements GeneralNameInterface { + private byte[] address; + private boolean isIPv4; + private String name; + + /** + * Create the IPAddressName object from the passed encoded Der value. + * + * @params derValue the encoded DER IPAddressName. + * @exception IOException on error. + */ + public IPAddressName(DerValue derValue) throws IOException { + this(derValue.getOctetString()); + } + + /** + * Create the IPAddressName object with the specified octets. + * + * @params address the IP address + * @throws IOException if address is not a valid IPv4 or IPv6 address + */ + public IPAddressName(byte[] address) throws IOException { + /* + * A valid address must consist of 4 bytes of address and + * optional 4 bytes of 4 bytes of mask, or 16 bytes of address + * and optional 16 bytes of mask. + */ + if (address.length == 4 || address.length == 8) { + isIPv4 = true; + } else if (address.length == 16 || address.length == 32) { + isIPv4 = false; + } else { + throw new IOException("Invalid IPAddressName"); + } + this.address = address; + } + + /** + * Create an IPAddressName from a String. + * [IETF RFC1338 Supernetting & IETF RFC1519 Classless Inter-Domain + * Routing (CIDR)] For IPv4 addresses, the forms are + * "b1.b2.b3.b4" or "b1.b2.b3.b4/m1.m2.m3.m4", where b1 - b4 are decimal + * byte values 0-255 and m1 - m4 are decimal mask values + * 0 - 255. + *

        + * [IETF RFC2373 IP Version 6 Addressing Architecture] + * For IPv6 addresses, the forms are "a1:a2:...:a8" or "a1:a2:...:a8/n", + * where a1-a8 are hexadecimal values representing the eight 16-bit pieces + * of the address. If /n is used, n is a decimal number indicating how many + * of the leftmost contiguous bits of the address comprise the prefix for + * this subnet. Internally, a mask value is created using the prefix length. + *

        + * @param name String form of IPAddressName + * @throws IOException if name can not be converted to a valid IPv4 or IPv6 + * address + */ + public IPAddressName(String name) throws IOException { + + if (name == null || name.length() == 0) { + throw new IOException("IPAddress cannot be null or empty"); + } + if (name.charAt(name.length() - 1) == '/') { + throw new IOException("Invalid IPAddress: " + name); + } + + if (name.indexOf(':') >= 0) { + // name is IPv6: uses colons as value separators + // Parse name into byte-value address components and optional + // prefix + parseIPv6(name); + isIPv4 = false; + } else if (name.indexOf('.') >= 0) { + //name is IPv4: uses dots as value separators + parseIPv4(name); + isIPv4 = true; + } else { + throw new IOException("Invalid IPAddress: " + name); + } + } + + /** + * Parse an IPv4 address. + * + * @param name IPv4 address with optional mask values + * @throws IOException on error + */ + private void parseIPv4(String name) throws IOException { + + // Parse name into byte-value address components + int slashNdx = name.indexOf('/'); + if (slashNdx == -1) { + address = InetAddress.getByName(name).getAddress(); + } else { + address = new byte[8]; + + // parse mask + byte[] mask = InetAddress.getByName + (name.substring(slashNdx+1)).getAddress(); + + // parse base address + byte[] host = InetAddress.getByName + (name.substring(0, slashNdx)).getAddress(); + + System.arraycopy(host, 0, address, 0, 4); + System.arraycopy(mask, 0, address, 4, 4); + } + } + + /** + * Parse an IPv6 address. + * + * @param name String IPv6 address with optional / + * If / is present, address[] array will + * be 32 bytes long, otherwise 16. + * @throws IOException on error + */ + private final static int MASKSIZE = 16; + private void parseIPv6(String name) throws IOException { + + int slashNdx = name.indexOf('/'); + if (slashNdx == -1) { + address = InetAddress.getByName(name).getAddress(); + } else { + address = new byte[32]; + byte[] base = InetAddress.getByName + (name.substring(0, slashNdx)).getAddress(); + System.arraycopy(base, 0, address, 0, 16); + + // append a mask corresponding to the num of prefix bits specified + int prefixLen = Integer.parseInt(name.substring(slashNdx+1)); + if (prefixLen > 128) + throw new IOException("IPv6Address prefix is longer than 128"); + + // create new bit array initialized to zeros + BitArray bitArray = new BitArray(MASKSIZE * 8); + + // set all most significant bits up to prefix length + for (int i = 0; i < prefixLen; i++) + bitArray.set(i, true); + byte[] maskArray = bitArray.toByteArray(); + + // copy mask bytes into mask portion of address + for (int i = 0; i < MASKSIZE; i++) + address[MASKSIZE+i] = maskArray[i]; + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return NAME_IP; + } + + /** + * Encode the IPAddress name into the DerOutputStream. + * + * @params out the DER stream to encode the IPAddressName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOctetString(address); + } + + /** + * Return a printable string of IPaddress + */ + public String toString() { + try { + return "IPAddress: " + getName(); + } catch (IOException ioe) { + // dump out hex rep for debugging purposes + HexDumpEncoder enc = new HexDumpEncoder(); + return "IPAddress: " + enc.encodeBuffer(address); + } + } + + /** + * Return a standard String representation of IPAddress. + * See IPAddressName(String) for the formats used for IPv4 + * and IPv6 addresses. + * + * @throws IOException if the IPAddress cannot be converted to a String + */ + public String getName() throws IOException { + if (name != null) + return name; + + if (isIPv4) { + //IPv4 address or subdomain + byte[] host = new byte[4]; + System.arraycopy(address, 0, host, 0, 4); + name = InetAddress.getByAddress(host).getHostAddress(); + if (address.length == 8) { + byte[] mask = new byte[4]; + System.arraycopy(address, 4, mask, 0, 4); + name = name + "/" + + InetAddress.getByAddress(mask).getHostAddress(); + } + } else { + //IPv6 address or subdomain + byte[] host = new byte[16]; + System.arraycopy(address, 0, host, 0, 16); + name = InetAddress.getByAddress(host).getHostAddress(); + if (address.length == 32) { + // IPv6 subdomain: display prefix length + + // copy subdomain into new array and convert to BitArray + byte[] maskBytes = new byte[16]; + for (int i=16; i < 32; i++) + maskBytes[i-16] = address[i]; + BitArray ba = new BitArray(16*8, maskBytes); + // Find first zero bit + int i=0; + for (; i < 16*8; i++) { + if (!ba.get(i)) + break; + } + name = name + "/" + i; + // Verify remaining bits 0 + for (; i < 16*8; i++) { + if (ba.get(i)) { + throw new IOException("Invalid IPv6 subdomain - set " + + "bit " + i + " not contiguous"); + } + } + } + } + return name; + } + + /** + * Returns this IPAddress name as a byte array. + */ + public byte[] getBytes() { + return address.clone(); + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical. + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof IPAddressName)) + return false; + + byte[] other = ((IPAddressName)obj).getBytes(); + + if (other.length != address.length) + return false; + + if (address.length == 8 || address.length == 32) { + // Two subnet addresses + // Mask each and compare masked values + int maskLen = address.length/2; + byte[] maskedThis = new byte[maskLen]; + byte[] maskedOther = new byte[maskLen]; + for (int i=0; i < maskLen; i++) { + maskedThis[i] = (byte)(address[i] & address[i+maskLen]); + maskedOther[i] = (byte)(other[i] & other[i+maskLen]); + if (maskedThis[i] != maskedOther[i]) { + return false; + } + } + // Now compare masks + for (int i=maskLen; i < address.length; i++) + if (address[i] != other[i]) + return false; + return true; + } else { + // Two IPv4 host addresses or two IPv6 host addresses + // Compare bytes + return Arrays.equals(other, address); + } + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + public int hashCode() { + int retval = 0; + + for (int i=0; i + *

      • NAME_DIFF_TYPE = -1: input name is different type from name + * (i.e. does not constrain). + *
      • NAME_MATCH = 0: input name matches name. + *
      • NAME_NARROWS = 1: input name narrows name (is lower in the naming + * subtree) + *
      • NAME_WIDENS = 2: input name widens name (is higher in the naming + * subtree) + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, but + * is same type. + *
      . These results are used in checking NameConstraints during + * certification path verification. + *

      + * [RFC2459] The syntax of iPAddress MUST be as described in section + * 4.2.1.7 with the following additions specifically for Name Constraints. + * For IPv4 addresses, the ipAddress field of generalName MUST contain + * eight (8) octets, encoded in the style of RFC 1519 (CIDR) to represent an + * address range.[RFC 1519] For IPv6 addresses, the ipAddress field + * MUST contain 32 octets similarly encoded. For example, a name + * constraint for "class C" subnet 10.9.8.0 shall be represented as the + * octets 0A 09 08 00 FF FF FF 00, representing the CIDR notation + * 10.9.8.0/255.255.255.0. + *

      + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but + * narrowing and widening are not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) + throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != NAME_IP) + constraintType = NAME_DIFF_TYPE; + else if (((IPAddressName)inputName).equals(this)) + constraintType = NAME_MATCH; + else { + byte[] otherAddress = ((IPAddressName)inputName).getBytes(); + if (otherAddress.length == 4 && address.length == 4) + // Two host addresses + constraintType = NAME_SAME_TYPE; + else if ((otherAddress.length == 8 && address.length == 8) || + (otherAddress.length == 32 && address.length == 32)) { + // Two subnet addresses + // See if one address fully encloses the other address + boolean otherSubsetOfThis = true; + boolean thisSubsetOfOther = true; + boolean thisEmpty = false; + boolean otherEmpty = false; + int maskOffset = address.length/2; + for (int i=0; i < maskOffset; i++) { + if ((byte)(address[i] & address[i+maskOffset]) != address[i]) + thisEmpty=true; + if ((byte)(otherAddress[i] & otherAddress[i+maskOffset]) != otherAddress[i]) + otherEmpty=true; + if (!(((byte)(address[i+maskOffset] & otherAddress[i+maskOffset]) == address[i+maskOffset]) && + ((byte)(address[i] & address[i+maskOffset]) == (byte)(otherAddress[i] & address[i+maskOffset])))) { + otherSubsetOfThis = false; + } + if (!(((byte)(otherAddress[i+maskOffset] & address[i+maskOffset]) == otherAddress[i+maskOffset]) && + ((byte)(otherAddress[i] & otherAddress[i+maskOffset]) == (byte)(address[i] & otherAddress[i+maskOffset])))) { + thisSubsetOfOther = false; + } + } + if (thisEmpty || otherEmpty) { + if (thisEmpty && otherEmpty) + constraintType = NAME_MATCH; + else if (thisEmpty) + constraintType = NAME_WIDENS; + else + constraintType = NAME_NARROWS; + } else if (otherSubsetOfThis) + constraintType = NAME_NARROWS; + else if (thisSubsetOfOther) + constraintType = NAME_WIDENS; + else + constraintType = NAME_SAME_TYPE; + } else if (otherAddress.length == 8 || otherAddress.length == 32) { + //Other is a subnet, this is a host address + int i = 0; + int maskOffset = otherAddress.length/2; + for (; i < maskOffset; i++) { + // Mask this address by other address mask and compare to other address + // If all match, then this address is in other address subnet + if ((address[i] & otherAddress[i+maskOffset]) != otherAddress[i]) + break; + } + if (i == maskOffset) + constraintType = NAME_WIDENS; + else + constraintType = NAME_SAME_TYPE; + } else if (address.length == 8 || address.length == 32) { + //This is a subnet, other is a host address + int i = 0; + int maskOffset = address.length/2; + for (; i < maskOffset; i++) { + // Mask other address by this address mask and compare to this address + if ((otherAddress[i] & address[i+maskOffset]) != address[i]) + break; + } + if (i == maskOffset) + constraintType = NAME_NARROWS; + else + constraintType = NAME_SAME_TYPE; + } else { + constraintType = NAME_SAME_TYPE; + } + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + throw new UnsupportedOperationException + ("subtreeDepth() not defined for IPAddressName"); + } +} diff --git a/src/sun/security/x509/InhibitAnyPolicyExtension.java b/src/sun/security/x509/InhibitAnyPolicyExtension.java new file mode 100644 index 00000000..26de497d --- /dev/null +++ b/src/sun/security/x509/InhibitAnyPolicyExtension.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.Debug; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; + +/** + * This class represents the Inhibit Any-Policy Extension. + * + *

      The inhibit any-policy extension can be used in certificates issued + * to CAs. The inhibit any-policy indicates that the special any-policy + * OID, with the value {2 5 29 32 0}, is not considered an explicit + * match for other certificate policies. The value indicates the number + * of additional certificates that may appear in the path before any- + * policy is no longer permitted. For example, a value of one indicates + * that any-policy may be processed in certificates issued by the sub- + * ject of this certificate, but not in additional certificates in the + * path. + *

      + * This extension MUST be critical. + *

      + * The ASN.1 syntax for this extension is: + *

      + * id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::=  { id-ce 54 }
      + *
      + * InhibitAnyPolicy ::= SkipCerts
      + *
      + * SkipCerts ::= INTEGER (0..MAX)
      + * 
      + * @author Anne Anderson + * @see CertAttrSet + * @see Extension + */ +public class InhibitAnyPolicyExtension extends Extension +implements CertAttrSet { + + private static final Debug debug = Debug.getInstance("certpath"); + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.InhibitAnyPolicy"; + + /** + * Object identifier for "any-policy" + */ + public static ObjectIdentifier AnyPolicy_Id; + static { + try { + AnyPolicy_Id = new ObjectIdentifier("2.5.29.32.0"); + } catch (IOException ioe) { + // Should not happen + } + } + + /** + * Attribute names. + */ + public static final String NAME = "InhibitAnyPolicy"; + public static final String SKIP_CERTS = "skip_certs"; + + // Private data members + private int skipCerts = Integer.MAX_VALUE; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream out = new DerOutputStream(); + out.putInteger(skipCerts); + this.extensionValue = out.toByteArray(); + } + + /** + * Default constructor for this object. + * + * @param skipCerts specifies the depth of the certification path. + * Use value of -1 to request unlimited depth. + */ + public InhibitAnyPolicyExtension(int skipCerts) throws IOException { + if (skipCerts < -1) + throw new IOException("Invalid value for skipCerts"); + if (skipCerts == -1) + this.skipCerts = Integer.MAX_VALUE; + else + this.skipCerts = skipCerts; + this.extensionId = PKIXExtensions.InhibitAnyPolicy_Id; + critical = true; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical criticality flag to use. Must be true for this + * extension. + * @param value a byte array holding the DER-encoded extension value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public InhibitAnyPolicyExtension(Boolean critical, Object value) + throws IOException { + + this.extensionId = PKIXExtensions.InhibitAnyPolicy_Id; + + if (!critical.booleanValue()) + throw new IOException("Criticality cannot be false for " + + "InhibitAnyPolicy"); + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Integer) + throw new IOException("Invalid encoding of InhibitAnyPolicy: " + + "data not integer"); + + if (val.data == null) + throw new IOException("Invalid encoding of InhibitAnyPolicy: " + + "null data"); + int skipCertsValue = val.getInteger(); + if (skipCertsValue < -1) + throw new IOException("Invalid value for skipCerts"); + if (skipCertsValue == -1) { + this.skipCerts = Integer.MAX_VALUE; + } else { + this.skipCerts = skipCertsValue; + } + } + + /** + * Return user readable form of extension. + */ + public String toString() { + String s = super.toString() + "InhibitAnyPolicy: " + skipCerts + "\n"; + return s; + } + + /** + * Encode this extension value to the output stream. + * + * @param out the DerOutputStream to encode the extension to. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + this.extensionId = PKIXExtensions.InhibitAnyPolicy_Id; + critical = true; + encodeThis(); + } + super.encode(tmp); + + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + * + * @param name name of attribute to set. Must be SKIP_CERTS. + * @param obj value to which attribute is to be set. Must be Integer + * type. + * @throws IOException on error + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(SKIP_CERTS)) { + if (!(obj instanceof Integer)) + throw new IOException("Attribute value should be of type Integer."); + int skipCertsValue = ((Integer)obj).intValue(); + if (skipCertsValue < -1) + throw new IOException("Invalid value for skipCerts"); + if (skipCertsValue == -1) { + skipCerts = Integer.MAX_VALUE; + } else { + skipCerts = skipCertsValue; + } + } else + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:InhibitAnyPolicy."); + encodeThis(); + } + + /** + * Get the attribute value. + * + * @param name name of attribute to get. Must be SKIP_CERTS. + * @returns value of the attribute. In this case it will be of type + * Integer. + * @throws IOException on error + */ + public Integer get(String name) throws IOException { + if (name.equalsIgnoreCase(SKIP_CERTS)) + return (new Integer(skipCerts)); + else + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:InhibitAnyPolicy."); + } + + /** + * Delete the attribute value. + * + * @param name name of attribute to delete. Must be SKIP_CERTS. + * @throws IOException on error. In this case, IOException will always be + * thrown, because the only attribute, SKIP_CERTS, is + * required. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(SKIP_CERTS)) + throw new IOException("Attribute " + SKIP_CERTS + + " may not be deleted."); + else + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:InhibitAnyPolicy."); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + * + * @returns enumeration of elements + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(SKIP_CERTS); + return (elements.elements()); + } + + /** + * Return the name of this attribute. + * + * @returns name of attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/InvalidityDateExtension.java b/src/sun/security/x509/InvalidityDateExtension.java new file mode 100644 index 00000000..eda02164 --- /dev/null +++ b/src/sun/security/x509/InvalidityDateExtension.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * From RFC 3280: + *

      + * The invalidity date is a non-critical CRL entry extension that + * provides the date on which it is known or suspected that the private + * key was compromised or that the certificate otherwise became invalid. + * This date may be earlier than the revocation date in the CRL entry, + * which is the date at which the CA processed the revocation. When a + * revocation is first posted by a CRL issuer in a CRL, the invalidity + * date may precede the date of issue of earlier CRLs, but the + * revocation date SHOULD NOT precede the date of issue of earlier CRLs. + * Whenever this information is available, CRL issuers are strongly + * encouraged to share it with CRL users. + *

      + * The GeneralizedTime values included in this field MUST be expressed + * in Greenwich Mean Time (Zulu), and MUST be specified and interpreted + * as defined in section 4.1.2.5.2. + *

      + * id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 }
      + *
      + * invalidityDate ::=  GeneralizedTime
      + * 
      + * + * @author Sean Mullan + */ +public class InvalidityDateExtension extends Extension + implements CertAttrSet { + + /** + * Attribute name and Reason codes + */ + public static final String NAME = "InvalidityDate"; + public static final String DATE = "date"; + + private Date date; + + private void encodeThis() throws IOException { + if (date == null) { + this.extensionValue = null; + return; + } + DerOutputStream dos = new DerOutputStream(); + dos.putGeneralizedTime(date); + this.extensionValue = dos.toByteArray(); + } + + /** + * Create a InvalidityDateExtension with the passed in date. + * Criticality automatically set to false. + * + * @param date the invalidity date + */ + public InvalidityDateExtension(Date date) throws IOException { + this(false, date); + } + + /** + * Create a InvalidityDateExtension with the passed in date. + * + * @param critical true if the extension is to be treated as critical. + * @param date the invalidity date + */ + public InvalidityDateExtension(boolean critical, Date date) + throws IOException { + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = critical; + this.date = date; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public InvalidityDateExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.date = val.getGeneralizedTime(); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Date)) { + throw new IOException("Attribute must be of type Date."); + } + if (name.equalsIgnoreCase(DATE)) { + date = (Date) obj; + } else { + throw new IOException + ("Name not supported by InvalidityDateExtension"); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Date get(String name) throws IOException { + if (name.equalsIgnoreCase(DATE)) { + if (date == null) { + return null; + } else { + return (new Date(date.getTime())); // clone + } + } else { + throw new IOException + ("Name not supported by InvalidityDateExtension"); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DATE)) { + date = null; + } else { + throw new IOException + ("Name not supported by InvalidityDateExtension"); + } + encodeThis(); + } + + /** + * Returns a printable representation of the Invalidity Date. + */ + public String toString() { + return super.toString() + " Invalidity Date: " + String.valueOf(date); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to + * @exception IOException on encoding errors + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.InvalidityDate_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DATE); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + public static InvalidityDateExtension toImpl(java.security.cert.Extension ext) + throws IOException { + if (ext instanceof InvalidityDateExtension) { + return (InvalidityDateExtension) ext; + } else { + return new InvalidityDateExtension + (Boolean.valueOf(ext.isCritical()), ext.getValue()); + } + } +} diff --git a/src/sun/security/x509/IssuerAlternativeNameExtension.java b/src/sun/security/x509/IssuerAlternativeNameExtension.java new file mode 100644 index 00000000..ba1f5846 --- /dev/null +++ b/src/sun/security/x509/IssuerAlternativeNameExtension.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This represents the Issuer Alternative Name Extension. + * + * This extension, if present, allows the issuer to specify multiple + * alternative names. + * + *

      Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class IssuerAlternativeNameExtension +extends Extension implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.IssuerAlternativeName"; + /** + * Attribute names. + */ + public static final String NAME = "IssuerAlternativeName"; + public static final String ISSUER_NAME = "issuer_name"; + + // private data members + GeneralNames names = null; + + // Encode this extension + private void encodeThis() throws IOException { + if (names == null || names.isEmpty()) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + names.encode(os); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a IssuerAlternativeNameExtension with the passed GeneralNames. + * + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a IssuerAlternativeNameExtension with the passed criticality + * and GeneralNames. + * + * @param critical true if the extension is to be treated as critical. + * @param names the GeneralNames for the issuer. + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(Boolean critical, GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a default IssuerAlternativeNameExtension. + */ + public IssuerAlternativeNameExtension() { + extensionId = PKIXExtensions.IssuerAlternativeName_Id; + critical = false; + names = new GeneralNames(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public IssuerAlternativeNameExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.IssuerAlternativeName_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.data == null) { + names = new GeneralNames(); + return; + } + + names = new GeneralNames(val); + } + + /** + * Returns a printable representation of the IssuerAlternativeName. + */ + public String toString() { + + String result = super.toString() + "IssuerAlternativeName [\n"; + if(names == null) { + result += " null\n"; + } else { + for(GeneralName name: names.names()) { + result += " "+name+"\n"; + } + } + result += "]\n"; + return result; + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.IssuerAlternativeName_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(ISSUER_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of" + + " type GeneralNames."); + } + names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public GeneralNames get(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER_NAME)) { + return (names); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(ISSUER_NAME)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:IssuerAlternativeName."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ISSUER_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/IssuingDistributionPointExtension.java b/src/sun/security/x509/IssuingDistributionPointExtension.java new file mode 100644 index 00000000..6a932587 --- /dev/null +++ b/src/sun/security/x509/IssuingDistributionPointExtension.java @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.*; + +import sun.security.util.DerInputStream; +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * Represents the CRL Issuing Distribution Point Extension (OID = 2.5.29.28). + * + *

      + * The issuing distribution point is a critical CRL extension that + * identifies the CRL distribution point and scope for a particular CRL, + * and it indicates whether the CRL covers revocation for end entity + * certificates only, CA certificates only, attribute certificates only, + * or a limited set of reason codes. + * + *

      + * The extension is defined in Section 5.2.5 of + * Internet X.509 PKI Certific +ate and Certificate Revocation List (CRL) Profile. + * + *

      + * Its ASN.1 definition is as follows: + *

      + *     id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
      + *
      + *     issuingDistributionPoint ::= SEQUENCE {
      + *          distributionPoint          [0] DistributionPointName OPTIONAL,
      + *          onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
      + *          onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
      + *          onlySomeReasons            [3] ReasonFlags OPTIONAL,
      + *          indirectCRL                [4] BOOLEAN DEFAULT FALSE,
      + *          onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
      + * 
      + * + * @see DistributionPoint + * @since 1.6 + */ +public class IssuingDistributionPointExtension extends Extension + implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.IssuingDistributionPoint"; + + /** + * Attribute names. + */ + public static final String NAME = "IssuingDistributionPoint"; + public static final String POINT = "point"; + public static final String REASONS = "reasons"; + public static final String ONLY_USER_CERTS = "only_user_certs"; + public static final String ONLY_CA_CERTS = "only_ca_certs"; + public static final String ONLY_ATTRIBUTE_CERTS = "only_attribute_certs"; + public static final String INDIRECT_CRL = "indirect_crl"; + + /* + * The distribution point name for the CRL. + */ + private DistributionPointName distributionPoint = null; + + /* + * The scope settings for the CRL. + */ + private ReasonFlags revocationReasons = null; + private boolean hasOnlyUserCerts = false; + private boolean hasOnlyCACerts = false; + private boolean hasOnlyAttributeCerts = false; + private boolean isIndirectCRL = false; + + /* + * ASN.1 context specific tag values + */ + private static final byte TAG_DISTRIBUTION_POINT = 0; + private static final byte TAG_ONLY_USER_CERTS = 1; + private static final byte TAG_ONLY_CA_CERTS = 2; + private static final byte TAG_ONLY_SOME_REASONS = 3; + private static final byte TAG_INDIRECT_CRL = 4; + private static final byte TAG_ONLY_ATTRIBUTE_CERTS = 5; + + /** + * Creates a critical IssuingDistributionPointExtension. + * + * @param distributionPoint the name of the distribution point, or null for + * none. + * @param revocationReasons the revocation reasons associated with the + * distribution point, or null for none. + * @param hasOnlyUserCerts if true then scope of the CRL + * includes only user certificates. + * @param hasOnlyCACerts if true then scope of the CRL + * includes only CA certificates. + * @param hasOnlyAttributeCerts if true then scope of the CRL + * includes only attribute certificates. + * @param isIndirectCRL if true then the scope of the CRL + * includes certificates issued by authorities other than the CRL + * issuer. The responsible authority is indicated by a certificate + * issuer CRL entry extension. + * @throws IllegalArgumentException if more than one of + * hasOnlyUserCerts, hasOnlyCACerts, + * hasOnlyAttributeCerts is set to true. + * @throws IOException on encoding error. + */ + public IssuingDistributionPointExtension( + DistributionPointName distributionPoint, ReasonFlags revocationReasons, + boolean hasOnlyUserCerts, boolean hasOnlyCACerts, + boolean hasOnlyAttributeCerts, boolean isIndirectCRL) + throws IOException { + + if ((hasOnlyUserCerts && (hasOnlyCACerts || hasOnlyAttributeCerts)) || + (hasOnlyCACerts && (hasOnlyUserCerts || hasOnlyAttributeCerts)) || + (hasOnlyAttributeCerts && (hasOnlyUserCerts || hasOnlyCACerts))) { + throw new IllegalArgumentException( + "Only one of hasOnlyUserCerts, hasOnlyCACerts, " + + "hasOnlyAttributeCerts may be set to true"); + } + this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; + this.critical = true; + this.distributionPoint = distributionPoint; + this.revocationReasons = revocationReasons; + this.hasOnlyUserCerts = hasOnlyUserCerts; + this.hasOnlyCACerts = hasOnlyCACerts; + this.hasOnlyAttributeCerts = hasOnlyAttributeCerts; + this.isIndirectCRL = isIndirectCRL; + encodeThis(); + } + + /** + * Creates a critical IssuingDistributionPointExtension from its + * DER-encoding. + * + * @param critical true if the extension is to be treated as critical. + * @param value the DER-encoded value. It must be a byte[]. + * @exception IOException on decoding error. + */ + public IssuingDistributionPointExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) { + throw new IOException("Illegal argument type"); + } + + extensionValue = (byte[])value; + DerValue val = new DerValue(extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "IssuingDistributionPointExtension."); + } + + // All the elements in issuingDistributionPoint are optional + if ((val.data == null) || (val.data.available() == 0)) { + return; + } + + DerInputStream in = val.data; + while (in != null && in.available() != 0) { + DerValue opt = in.getDerValue(); + + if (opt.isContextSpecific(TAG_DISTRIBUTION_POINT) && + opt.isConstructed()) { + distributionPoint = + new DistributionPointName(opt.data.getDerValue()); + } else if (opt.isContextSpecific(TAG_ONLY_USER_CERTS) && + !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Boolean); + hasOnlyUserCerts = opt.getBoolean(); + } else if (opt.isContextSpecific(TAG_ONLY_CA_CERTS) && + !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Boolean); + hasOnlyCACerts = opt.getBoolean(); + } else if (opt.isContextSpecific(TAG_ONLY_SOME_REASONS) && + !opt.isConstructed()) { + revocationReasons = new ReasonFlags(opt); // expects tag implicit + } else if (opt.isContextSpecific(TAG_INDIRECT_CRL) && + !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Boolean); + isIndirectCRL = opt.getBoolean(); + } else if (opt.isContextSpecific(TAG_ONLY_ATTRIBUTE_CERTS) && + !opt.isConstructed()) { + opt.resetTag(DerValue.tag_Boolean); + hasOnlyAttributeCerts = opt.getBoolean(); + } else { + throw new IOException + ("Invalid encoding of IssuingDistributionPoint"); + } + } + } + + /** + * Returns the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Encodes the issuing distribution point extension and writes it to the + * DerOutputStream. + * + * @param out the output stream. + * @exception IOException on encoding error. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.IssuingDistributionPoint_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Sets the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(POINT)) { + if (!(obj instanceof DistributionPointName)) { + throw new IOException( + "Attribute value should be of type DistributionPointName."); + } + distributionPoint = (DistributionPointName)obj; + + } else if (name.equalsIgnoreCase(REASONS)) { + if (!(obj instanceof ReasonFlags)) { + throw new IOException( + "Attribute value should be of type ReasonFlags."); + } + + } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { + if (!(obj instanceof Boolean)) { + throw new IOException( + "Attribute value should be of type Boolean."); + } + isIndirectCRL = ((Boolean)obj).booleanValue(); + + } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { + if (!(obj instanceof Boolean)) { + throw new IOException( + "Attribute value should be of type Boolean."); + } + hasOnlyUserCerts = ((Boolean)obj).booleanValue(); + + } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { + if (!(obj instanceof Boolean)) { + throw new IOException( + "Attribute value should be of type Boolean."); + } + hasOnlyCACerts = ((Boolean)obj).booleanValue(); + + } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { + if (!(obj instanceof Boolean)) { + throw new IOException( + "Attribute value should be of type Boolean."); + } + hasOnlyAttributeCerts = ((Boolean)obj).booleanValue(); + + + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension."); + } + encodeThis(); + } + + /** + * Gets the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(POINT)) { + return distributionPoint; + + } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { + return Boolean.valueOf(isIndirectCRL); + + } else if (name.equalsIgnoreCase(REASONS)) { + return revocationReasons; + + } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { + return Boolean.valueOf(hasOnlyUserCerts); + + } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { + return Boolean.valueOf(hasOnlyCACerts); + + } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { + return Boolean.valueOf(hasOnlyAttributeCerts); + + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension."); + } + } + + /** + * Deletes the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(POINT)) { + distributionPoint = null; + + } else if (name.equalsIgnoreCase(INDIRECT_CRL)) { + isIndirectCRL = false; + + } else if (name.equalsIgnoreCase(REASONS)) { + revocationReasons = null; + + } else if (name.equalsIgnoreCase(ONLY_USER_CERTS)) { + hasOnlyUserCerts = false; + + } else if (name.equalsIgnoreCase(ONLY_CA_CERTS)) { + hasOnlyCACerts = false; + + } else if (name.equalsIgnoreCase(ONLY_ATTRIBUTE_CERTS)) { + hasOnlyAttributeCerts = false; + + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:IssuingDistributionPointExtension."); + } + encodeThis(); + } + + /** + * Returns an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(POINT); + elements.addElement(REASONS); + elements.addElement(ONLY_USER_CERTS); + elements.addElement(ONLY_CA_CERTS); + elements.addElement(ONLY_ATTRIBUTE_CERTS); + elements.addElement(INDIRECT_CRL); + return elements.elements(); + } + + // Encodes this extension value + private void encodeThis() throws IOException { + + if (distributionPoint == null && + revocationReasons == null && + !hasOnlyUserCerts && + !hasOnlyCACerts && + !hasOnlyAttributeCerts && + !isIndirectCRL) { + + this.extensionValue = null; + return; + + } + + DerOutputStream tagged = new DerOutputStream(); + + if (distributionPoint != null) { + DerOutputStream tmp = new DerOutputStream(); + distributionPoint.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, true, + TAG_DISTRIBUTION_POINT), tmp); + } + + if (hasOnlyUserCerts) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putBoolean(hasOnlyUserCerts); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, + TAG_ONLY_USER_CERTS), tmp); + } + + if (hasOnlyCACerts) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putBoolean(hasOnlyCACerts); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, + TAG_ONLY_CA_CERTS), tmp); + } + + if (revocationReasons != null) { + DerOutputStream tmp = new DerOutputStream(); + revocationReasons.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, + TAG_ONLY_SOME_REASONS), tmp); + } + + if (isIndirectCRL) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putBoolean(isIndirectCRL); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, + TAG_INDIRECT_CRL), tmp); + } + + if (hasOnlyAttributeCerts) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putBoolean(hasOnlyAttributeCerts); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, false, + TAG_ONLY_ATTRIBUTE_CERTS), tmp); + } + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, tagged); + this.extensionValue = seq.toByteArray(); + } + + /** + * Returns the extension as user readable string. + */ + public String toString() { + + StringBuilder sb = new StringBuilder(super.toString()); + sb.append("IssuingDistributionPoint [\n "); + + if (distributionPoint != null) { + sb.append(distributionPoint); + } + + if (revocationReasons != null) { + sb.append(revocationReasons); + } + + sb.append((hasOnlyUserCerts) + ? (" Only contains user certs: true") + : (" Only contains user certs: false")).append("\n"); + + sb.append((hasOnlyCACerts) + ? (" Only contains CA certs: true") + : (" Only contains CA certs: false")).append("\n"); + + sb.append((hasOnlyAttributeCerts) + ? (" Only contains attribute certs: true") + : (" Only contains attribute certs: false")).append("\n"); + + sb.append((isIndirectCRL) + ? (" Indirect CRL: true") + : (" Indirect CRL: false")).append("\n"); + + sb.append("]\n"); + + return sb.toString(); + } + +} diff --git a/src/sun/security/x509/KeyIdentifier.java b/src/sun/security/x509/KeyIdentifier.java new file mode 100644 index 00000000..01d4803c --- /dev/null +++ b/src/sun/security/x509/KeyIdentifier.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.security.PublicKey; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import sun.misc.HexDumpEncoder; +import sun.security.util.*; + +/** + * Represent the Key Identifier ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class KeyIdentifier { + private byte[] octetString; + + /** + * Create a KeyIdentifier with the passed bit settings. + * + * @param octetString the octet string identifying the key identifier. + */ + public KeyIdentifier(byte[] octetString) { + this.octetString = octetString.clone(); + } + + /** + * Create a KeyIdentifier from the DER encoded value. + * + * @param val the DerValue + */ + public KeyIdentifier(DerValue val) throws IOException { + octetString = val.getOctetString(); + } + + /** + * Creates a KeyIdentifier from a public-key value. + * + *

      From RFC2459: Two common methods for generating key identifiers from + * the public key are: + *

        + *
      1. The keyIdentifier is composed of the 160-bit SHA-1 hash of the + * value of the BIT STRING subjectPublicKey (excluding the tag, + * length, and number of unused bits). + *

        + *

      2. The keyIdentifier is composed of a four bit type field with + * the value 0100 followed by the least significant 60 bits of the + * SHA-1 hash of the value of the BIT STRING subjectPublicKey. + *
      + *

      This method supports method 1. + * + * @param pubKey the public key from which to construct this KeyIdentifier + * @throws IOException on parsing errors + */ + public KeyIdentifier(PublicKey pubKey) + throws IOException + { + DerValue algAndKey = new DerValue(pubKey.getEncoded()); + if (algAndKey.tag != DerValue.tag_Sequence) + throw new IOException("PublicKey value is not a valid " + + "X.509 public key"); + + AlgorithmId algid = AlgorithmId.parse(algAndKey.data.getDerValue()); + byte[] key = algAndKey.data.getUnalignedBitString().toByteArray(); + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException e3) { + throw new IOException("SHA1 not supported"); + } + md.update(key); + this.octetString = md.digest(); + } + + /** + * Return the value of the KeyIdentifier as byte array. + */ + public byte[] getIdentifier() { + return octetString.clone(); + } + + /** + * Returns a printable representation of the KeyUsage. + */ + public String toString() { + String s = "KeyIdentifier [\n"; + + HexDumpEncoder encoder = new HexDumpEncoder(); + s += encoder.encodeBuffer(octetString); + s += "]\n"; + return (s); + } + + /** + * Write the KeyIdentifier to the DerOutputStream. + * + * @param out the DerOutputStream to write the object to. + * @exception IOException + */ + void encode(DerOutputStream out) throws IOException { + out.putOctetString(octetString); + } + + /** + * Returns a hash code value for this object. + * Objects that are equal will also have the same hashcode. + */ + public int hashCode () { + int retval = 0; + for (int i = 0; i < octetString.length; i++) + retval += octetString[i] * i; + return retval; + } + + /** + * Indicates whether some other object is "equal to" this one. + */ + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof KeyIdentifier)) + return false; + return java.util.Arrays.equals(octetString, + ((KeyIdentifier)other).getIdentifier()); + } +} diff --git a/src/sun/security/x509/KeyUsageExtension.java b/src/sun/security/x509/KeyUsageExtension.java new file mode 100644 index 00000000..0f1242d6 --- /dev/null +++ b/src/sun/security/x509/KeyUsageExtension.java @@ -0,0 +1,363 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * Represent the Key Usage Extension. + * + *

      This extension, if present, defines the purpose (e.g., encipherment, + * signature, certificate signing) of the key contained in the certificate. + * The usage restriction might be employed when a multipurpose key is to be + * restricted (e.g., when an RSA key should be used only for signing or only + * for key encipherment). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class KeyUsageExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.KeyUsage"; + /** + * Attribute names. + */ + public static final String NAME = "KeyUsage"; + public static final String DIGITAL_SIGNATURE = "digital_signature"; + public static final String NON_REPUDIATION = "non_repudiation"; + public static final String KEY_ENCIPHERMENT = "key_encipherment"; + public static final String DATA_ENCIPHERMENT = "data_encipherment"; + public static final String KEY_AGREEMENT = "key_agreement"; + public static final String KEY_CERTSIGN = "key_certsign"; + public static final String CRL_SIGN = "crl_sign"; + public static final String ENCIPHER_ONLY = "encipher_only"; + public static final String DECIPHER_ONLY = "decipher_only"; + + // Private data members + private boolean[] bitString; + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + os.putTruncatedUnalignedBitString(new BitArray(this.bitString)); + this.extensionValue = os.toByteArray(); + } + + /** + * Check if bit is set. + * + * @param position the position in the bit string to check. + */ + private boolean isSet(int position) { + return bitString[position]; + } + + /** + * Set the bit at the specified position. + */ + private void set(int position, boolean val) { + // enlarge bitString if necessary + if (position >= bitString.length) { + boolean[] tmp = new boolean[position+1]; + System.arraycopy(bitString, 0, tmp, 0, bitString.length); + bitString = tmp; + } + bitString[position] = val; + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(byte[] bitString) throws IOException { + this.bitString = + new BitArray(bitString.length*8,bitString).toBooleanArray(); + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(boolean[] bitString) throws IOException { + this.bitString = bitString; + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a KeyUsageExtension with the passed bit settings. The criticality + * is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public KeyUsageExtension(BitArray bitString) throws IOException { + this.bitString = bitString.toBooleanArray(); + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * The DER encoded value may be wrapped in an OCTET STRING. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value (possibly + * wrapped in an OCTET STRING). + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public KeyUsageExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = critical.booleanValue(); + /* + * The following check should be activated again after + * the PKIX profiling work becomes standard and the check + * is not a barrier to interoperability ! + * if (!this.critical) { + * throw new IOException("KeyUsageExtension not marked critical," + * + " invalid profile."); + * } + */ + byte[] extValue = (byte[]) value; + if (extValue[0] == DerValue.tag_OctetString) { + this.extensionValue = new DerValue(extValue).getOctetString(); + } else { + this.extensionValue = extValue; + } + DerValue val = new DerValue(this.extensionValue); + this.bitString = val.getUnalignedBitString().toBooleanArray(); + } + + /** + * Create a default key usage. + */ + public KeyUsageExtension() { + extensionId = PKIXExtensions.KeyUsage_Id; + critical = true; + bitString = new boolean[0]; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute must be of type Boolean."); + } + boolean val = ((Boolean)obj).booleanValue(); + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + set(0,val); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + set(1,val); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + set(2,val); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + set(3,val); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + set(4,val); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + set(5,val); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + set(6,val); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + set(7,val); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + set(8,val); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Boolean get(String name) throws IOException { + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + return Boolean.valueOf(isSet(0)); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + return Boolean.valueOf(isSet(1)); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + return Boolean.valueOf(isSet(2)); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + return Boolean.valueOf(isSet(3)); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + return Boolean.valueOf(isSet(4)); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + return Boolean.valueOf(isSet(5)); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + return Boolean.valueOf(isSet(6)); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + return Boolean.valueOf(isSet(7)); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + return Boolean.valueOf(isSet(8)); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DIGITAL_SIGNATURE)) { + set(0,false); + } else if (name.equalsIgnoreCase(NON_REPUDIATION)) { + set(1,false); + } else if (name.equalsIgnoreCase(KEY_ENCIPHERMENT)) { + set(2,false); + } else if (name.equalsIgnoreCase(DATA_ENCIPHERMENT)) { + set(3,false); + } else if (name.equalsIgnoreCase(KEY_AGREEMENT)) { + set(4,false); + } else if (name.equalsIgnoreCase(KEY_CERTSIGN)) { + set(5,false); + } else if (name.equalsIgnoreCase(CRL_SIGN)) { + set(6,false); + } else if (name.equalsIgnoreCase(ENCIPHER_ONLY)) { + set(7,false); + } else if (name.equalsIgnoreCase(DECIPHER_ONLY)) { + set(8,false); + } else { + throw new IOException("Attribute name not recognized by" + + " CertAttrSet:KeyUsage."); + } + encodeThis(); + } + + /** + * Returns a printable representation of the KeyUsage. + */ + public String toString() { + String s = super.toString() + "KeyUsage [\n"; + + try { + if (isSet(0)) { + s += " DigitalSignature\n"; + } + if (isSet(1)) { + s += " Non_repudiation\n"; + } + if (isSet(2)) { + s += " Key_Encipherment\n"; + } + if (isSet(3)) { + s += " Data_Encipherment\n"; + } + if (isSet(4)) { + s += " Key_Agreement\n"; + } + if (isSet(5)) { + s += " Key_CertSign\n"; + } + if (isSet(6)) { + s += " Crl_Sign\n"; + } + if (isSet(7)) { + s += " Encipher_Only\n"; + } + if (isSet(8)) { + s += " Decipher_Only\n"; + } + } catch (ArrayIndexOutOfBoundsException ex) {} + + s += "]\n"; + + return (s); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.KeyUsage_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DIGITAL_SIGNATURE); + elements.addElement(NON_REPUDIATION); + elements.addElement(KEY_ENCIPHERMENT); + elements.addElement(DATA_ENCIPHERMENT); + elements.addElement(KEY_AGREEMENT); + elements.addElement(KEY_CERTSIGN); + elements.addElement(CRL_SIGN); + elements.addElement(ENCIPHER_ONLY); + elements.addElement(DECIPHER_ONLY); + + return (elements.elements()); + } + + + public boolean[] getBits() { + return bitString.clone(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/NameConstraintsExtension.java b/src/sun/security/x509/NameConstraintsExtension.java new file mode 100644 index 00000000..f043183b --- /dev/null +++ b/src/sun/security/x509/NameConstraintsExtension.java @@ -0,0 +1,613 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.*; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.*; +import sun.security.pkcs.PKCS9Attribute; + +/** + * This class defines the Name Constraints Extension. + *

      + * The name constraints extension provides permitted and excluded + * subtrees that place restrictions on names that may be included within + * a certificate issued by a given CA. Restrictions may apply to the + * subject distinguished name or subject alternative names. Any name + * matching a restriction in the excluded subtrees field is invalid + * regardless of information appearing in the permitted subtrees. + *

      + * The ASN.1 syntax for this is: + *

      + * NameConstraints ::= SEQUENCE {
      + *    permittedSubtrees [0]  GeneralSubtrees OPTIONAL,
      + *    excludedSubtrees  [1]  GeneralSubtrees OPTIONAL
      + * }
      + * GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
      + * 
      + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class NameConstraintsExtension extends Extension +implements CertAttrSet, Cloneable { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.NameConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "NameConstraints"; + public static final String PERMITTED_SUBTREES = "permitted_subtrees"; + public static final String EXCLUDED_SUBTREES = "excluded_subtrees"; + + // Private data members + private static final byte TAG_PERMITTED = 0; + private static final byte TAG_EXCLUDED = 1; + + private GeneralSubtrees permitted = null; + private GeneralSubtrees excluded = null; + + private boolean hasMin; + private boolean hasMax; + private boolean minMaxValid = false; + + // Recalculate hasMin and hasMax flags. + private void calcMinMax() throws IOException { + hasMin = false; + hasMax = false; + if (excluded != null) { + for (int i = 0; i < excluded.size(); i++) { + GeneralSubtree subtree = excluded.get(i); + if (subtree.getMinimum() != 0) + hasMin = true; + if (subtree.getMaximum() != -1) + hasMax = true; + } + } + + if (permitted != null) { + for (int i = 0; i < permitted.size(); i++) { + GeneralSubtree subtree = permitted.get(i); + if (subtree.getMinimum() != 0) + hasMin = true; + if (subtree.getMaximum() != -1) + hasMax = true; + } + } + minMaxValid = true; + } + + // Encode this extension value. + private void encodeThis() throws IOException { + minMaxValid = false; + if (permitted == null && excluded == null) { + this.extensionValue = null; + return; + } + DerOutputStream seq = new DerOutputStream(); + + DerOutputStream tagged = new DerOutputStream(); + if (permitted != null) { + DerOutputStream tmp = new DerOutputStream(); + permitted.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_PERMITTED), tmp); + } + if (excluded != null) { + DerOutputStream tmp = new DerOutputStream(); + excluded.encode(tmp); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + true, TAG_EXCLUDED), tmp); + } + seq.write(DerValue.tag_Sequence, tagged); + this.extensionValue = seq.toByteArray(); + } + + /** + * The default constructor for this class. Both parameters + * are optional and can be set to null. The extension criticality + * is set to true. + * + * @param permitted the permitted GeneralSubtrees (null for optional). + * @param excluded the excluded GeneralSubtrees (null for optional). + */ + public NameConstraintsExtension(GeneralSubtrees permitted, + GeneralSubtrees excluded) + throws IOException { + this.permitted = permitted; + this.excluded = excluded; + + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public NameConstraintsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for" + + " NameConstraintsExtension."); + } + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + // Note that all the fields in NameConstraints are defined as + // being OPTIONAL, i.e., there could be an empty SEQUENCE, resulting + // in val.data being null. + if (val.data == null) + return; + while (val.data.available() != 0) { + DerValue opt = val.data.getDerValue(); + + if (opt.isContextSpecific(TAG_PERMITTED) && opt.isConstructed()) { + if (permitted != null) { + throw new IOException("Duplicate permitted " + + "GeneralSubtrees in NameConstraintsExtension."); + } + opt.resetTag(DerValue.tag_Sequence); + permitted = new GeneralSubtrees(opt); + + } else if (opt.isContextSpecific(TAG_EXCLUDED) && + opt.isConstructed()) { + if (excluded != null) { + throw new IOException("Duplicate excluded " + + "GeneralSubtrees in NameConstraintsExtension."); + } + opt.resetTag(DerValue.tag_Sequence); + excluded = new GeneralSubtrees(opt); + } else + throw new IOException("Invalid encoding of " + + "NameConstraintsExtension."); + } + minMaxValid = false; + } + + /** + * Return the printable string. + */ + public String toString() { + return (super.toString() + "NameConstraints: [" + + ((permitted == null) ? "" : + ("\n Permitted:" + permitted.toString())) + + ((excluded == null) ? "" : + ("\n Excluded:" + excluded.toString())) + + " ]\n"); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.NameConstraints_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + if (!(obj instanceof GeneralSubtrees)) { + throw new IOException("Attribute value should be" + + " of type GeneralSubtrees."); + } + permitted = (GeneralSubtrees)obj; + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + if (!(obj instanceof GeneralSubtrees)) { + throw new IOException("Attribute value should be " + + "of type GeneralSubtrees."); + } + excluded = (GeneralSubtrees)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public GeneralSubtrees get(String name) throws IOException { + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + return (permitted); + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + return (excluded); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(PERMITTED_SUBTREES)) { + permitted = null; + } else if (name.equalsIgnoreCase(EXCLUDED_SUBTREES)) { + excluded = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:NameConstraintsExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(PERMITTED_SUBTREES); + elements.addElement(EXCLUDED_SUBTREES); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Merge additional name constraints with existing ones. + * This function is used in certification path processing + * to accumulate name constraints from successive certificates + * in the path. Note that NameConstraints can never be + * expanded by a merge, just remain constant or become more + * limiting. + *

      + * IETF RFC2459 specifies the processing of Name Constraints as + * follows: + *

      + * (j) If permittedSubtrees is present in the certificate, set the + * constrained subtrees state variable to the intersection of its + * previous value and the value indicated in the extension field. + *

      + * (k) If excludedSubtrees is present in the certificate, set the + * excluded subtrees state variable to the union of its previous + * value and the value indicated in the extension field. + *

      + * @param newConstraints additional NameConstraints to be applied + * @throws IOException on error + */ + public void merge(NameConstraintsExtension newConstraints) + throws IOException { + + if (newConstraints == null) { + // absence of any explicit constraints implies unconstrained + return; + } + + /* + * If excludedSubtrees is present in the certificate, set the + * excluded subtrees state variable to the union of its previous + * value and the value indicated in the extension field. + */ + + GeneralSubtrees newExcluded = newConstraints.get(EXCLUDED_SUBTREES); + if (excluded == null) { + excluded = (newExcluded != null) ? + (GeneralSubtrees)newExcluded.clone() : null; + } else { + if (newExcluded != null) { + // Merge new excluded with current excluded (union) + excluded.union(newExcluded); + } + } + + /* + * If permittedSubtrees is present in the certificate, set the + * constrained subtrees state variable to the intersection of its + * previous value and the value indicated in the extension field. + */ + + GeneralSubtrees newPermitted = newConstraints.get(PERMITTED_SUBTREES); + if (permitted == null) { + permitted = (newPermitted != null) ? + (GeneralSubtrees)newPermitted.clone() : null; + } else { + if (newPermitted != null) { + // Merge new permitted with current permitted (intersection) + newExcluded = permitted.intersect(newPermitted); + + // Merge new excluded subtrees to current excluded (union) + if (newExcluded != null) { + if (excluded != null) { + excluded.union(newExcluded); + } else { + excluded = (GeneralSubtrees)newExcluded.clone(); + } + } + } + } + + // Optional optimization: remove permitted subtrees that are excluded. + // This is not necessary for algorithm correctness, but it makes + // subsequent operations on the NameConstraints faster and require + // less space. + if (permitted != null) { + permitted.reduce(excluded); + } + + // The NameConstraints have been changed, so re-encode them. Methods in + // this class assume that the encodings have already been done. + encodeThis(); + + } + + /** + * check whether a certificate conforms to these NameConstraints. + * This involves verifying that the subject name and subjectAltName + * extension (critical or noncritical) is consistent with the permitted + * subtrees state variables. Also verify that the subject name and + * subjectAltName extension (critical or noncritical) is consistent with + * the excluded subtrees state variables. + * + * @param cert X509Certificate to be verified + * @returns true if certificate verifies successfully + * @throws IOException on error + */ + public boolean verify(X509Certificate cert) throws IOException { + + if (cert == null) { + throw new IOException("Certificate is null"); + } + + // Calculate hasMin and hasMax booleans (if necessary) + if (!minMaxValid) { + calcMinMax(); + } + + if (hasMin) { + throw new IOException("Non-zero minimum BaseDistance in" + + " name constraints not supported"); + } + + if (hasMax) { + throw new IOException("Maximum BaseDistance in" + + " name constraints not supported"); + } + + X500Principal subjectPrincipal = cert.getSubjectX500Principal(); + X500Name subject = X500Name.asX500Name(subjectPrincipal); + + if (subject.isEmpty() == false) { + if (verify(subject) == false) { + return false; + } + } + + GeneralNames altNames = null; + // extract altNames + try { + // extract extensions, if any, from certInfo + // following returns null if certificate contains no extensions + X509CertImpl certImpl = X509CertImpl.toImpl(cert); + SubjectAlternativeNameExtension altNameExt = + certImpl.getSubjectAlternativeNameExtension(); + if (altNameExt != null) { + // extract altNames from extension; this call does not + // return an IOException on null altnames + altNames = altNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + } + } catch (CertificateException ce) { + throw new IOException("Unable to extract extensions from " + + "certificate: " + ce.getMessage()); + } + + // If there are no subjectAlternativeNames, perform the special-case + // check where if the subjectName contains any EMAILADDRESS + // attributes, they must be checked against RFC822 constraints. + // If that passes, we're fine. + if (altNames == null) { + return verifyRFC822SpecialCase(subject); + } + + // verify each subjectAltName + for (int i = 0; i < altNames.size(); i++) { + GeneralNameInterface altGNI = altNames.get(i).getName(); + if (!verify(altGNI)) { + return false; + } + } + + // All tests passed. + return true; + } + + /** + * check whether a name conforms to these NameConstraints. + * This involves verifying that the name is consistent with the + * permitted and excluded subtrees variables. + * + * @param name GeneralNameInterface name to be verified + * @returns true if certificate verifies successfully + * @throws IOException on error + */ + public boolean verify(GeneralNameInterface name) throws IOException { + if (name == null) { + throw new IOException("name is null"); + } + + // Verify that the name is consistent with the excluded subtrees + if (excluded != null && excluded.size() > 0) { + + for (int i = 0; i < excluded.size(); i++) { + GeneralSubtree gs = excluded.get(i); + if (gs == null) + continue; + GeneralName gn = gs.getName(); + if (gn == null) + continue; + GeneralNameInterface exName = gn.getName(); + if (exName == null) + continue; + + // if name matches or narrows any excluded subtree, + // return false + switch (exName.constrains(name)) { + case GeneralNameInterface.NAME_DIFF_TYPE: + case GeneralNameInterface.NAME_WIDENS: // name widens excluded + case GeneralNameInterface.NAME_SAME_TYPE: + break; + case GeneralNameInterface.NAME_MATCH: + case GeneralNameInterface.NAME_NARROWS: // subject name excluded + return false; + } + } + } + + // Verify that the name is consistent with the permitted subtrees + if (permitted != null && permitted.size() > 0) { + + boolean sameType = false; + + for (int i = 0; i < permitted.size(); i++) { + GeneralSubtree gs = permitted.get(i); + if (gs == null) + continue; + GeneralName gn = gs.getName(); + if (gn == null) + continue; + GeneralNameInterface perName = gn.getName(); + if (perName == null) + continue; + + // if Name matches any type in permitted, + // and Name does not match or narrow some permitted subtree, + // return false + switch (perName.constrains(name)) { + case GeneralNameInterface.NAME_DIFF_TYPE: + continue; // continue checking other permitted names + case GeneralNameInterface.NAME_WIDENS: // name widens permitted + case GeneralNameInterface.NAME_SAME_TYPE: + sameType = true; + continue; // continue to look for a match or narrow + case GeneralNameInterface.NAME_MATCH: + case GeneralNameInterface.NAME_NARROWS: + // name narrows permitted + return true; // name is definitely OK, so break out of loop + } + } + if (sameType) { + return false; + } + } + return true; + } + + /** + * Perform the RFC 822 special case check. We have a certificate + * that does not contain any subject alternative names. Check that + * any EMAILADDRESS attributes in its subject name conform to these + * NameConstraints. + * + * @param subject the certificate's subject name + * @returns true if certificate verifies successfully + * @throws IOException on error + */ + public boolean verifyRFC822SpecialCase(X500Name subject) throws IOException { + for (AVA ava : subject.allAvas()) { + ObjectIdentifier attrOID = ava.getObjectIdentifier(); + if (attrOID.equals((Object)PKCS9Attribute.EMAIL_ADDRESS_OID)) { + String attrValue = ava.getValueString(); + if (attrValue != null) { + RFC822Name emailName; + try { + emailName = new RFC822Name(attrValue); + } catch (IOException ioe) { + continue; + } + if (!verify(emailName)) { + return(false); + } + } + } + } + return true; + } + + /** + * Clone all objects that may be modified during certificate validation. + */ + public Object clone() { + try { + NameConstraintsExtension newNCE = + (NameConstraintsExtension) super.clone(); + + if (permitted != null) { + newNCE.permitted = (GeneralSubtrees) permitted.clone(); + } + if (excluded != null) { + newNCE.excluded = (GeneralSubtrees) excluded.clone(); + } + return newNCE; + } catch (CloneNotSupportedException cnsee) { + throw new RuntimeException("CloneNotSupportedException while " + + "cloning NameConstraintsException. This should never happen."); + } + } +} diff --git a/src/sun/security/x509/NetscapeCertTypeExtension.java b/src/sun/security/x509/NetscapeCertTypeExtension.java new file mode 100644 index 00000000..d6d4efd3 --- /dev/null +++ b/src/sun/security/x509/NetscapeCertTypeExtension.java @@ -0,0 +1,321 @@ +/* + * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +import sun.security.util.*; + +/** + * Represents Netscape Certificate Type Extension. + * The details are defined + * + * here . + * + *

      This extension, if present, defines both the purpose + * (e.g., encipherment, signature, certificate signing) and the application + * (e.g., SSL, S/Mime or Object Signing of the key contained in the + * certificate. This extension has been superseded by IETF PKIX extensions + * but is provided here for compatibility reasons. + * + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ + +public class NetscapeCertTypeExtension extends Extension +implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.NetscapeCertType"; + + /** + * Attribute names. + */ + public static final String NAME = "NetscapeCertType"; + public static final String SSL_CLIENT = "ssl_client"; + public static final String SSL_SERVER = "ssl_server"; + public static final String S_MIME = "s_mime"; + public static final String OBJECT_SIGNING = "object_signing"; + public static final String SSL_CA = "ssl_ca"; + public static final String S_MIME_CA = "s_mime_ca"; + public static final String OBJECT_SIGNING_CA = "object_signing_ca"; + + private static final int CertType_data[] = { 2, 16, 840, 1, 113730, 1, 1 }; + + /** + * Object identifier for the Netscape-Cert-Type extension. + */ + public static ObjectIdentifier NetscapeCertType_Id; + + static { + try { + NetscapeCertType_Id = new ObjectIdentifier(CertType_data); + } catch (IOException ioe) { + // should not happen + } + } + + private boolean[] bitString; + + private static class MapEntry { + String mName; + int mPosition; + + MapEntry(String name, int position) { + mName = name; + mPosition = position; + } + } + + private static MapEntry[] mMapData = { + new MapEntry(SSL_CLIENT, 0), + new MapEntry(SSL_SERVER, 1), + new MapEntry(S_MIME, 2), + new MapEntry(OBJECT_SIGNING, 3), + // note that bit 4 is reserved + new MapEntry(SSL_CA, 5), + new MapEntry(S_MIME_CA, 6), + new MapEntry(OBJECT_SIGNING_CA, 7), + }; + + private static final Vector mAttributeNames = new Vector(); + static { + for (MapEntry entry : mMapData) { + mAttributeNames.add(entry.mName); + } + } + + private static int getPosition(String name) throws IOException { + for (int i = 0; i < mMapData.length; i++) { + if (name.equalsIgnoreCase(mMapData[i].mName)) + return mMapData[i].mPosition; + } + throw new IOException("Attribute name [" + name + + "] not recognized by CertAttrSet:NetscapeCertType."); + } + + // Encode this extension value + private void encodeThis() throws IOException { + DerOutputStream os = new DerOutputStream(); + os.putTruncatedUnalignedBitString(new BitArray(this.bitString)); + this.extensionValue = os.toByteArray(); + } + + /** + * Check if bit is set. + * + * @param position the position in the bit string to check. + */ + private boolean isSet(int position) { + return bitString[position]; + } + + /** + * Set the bit at the specified position. + */ + private void set(int position, boolean val) { + // enlarge bitString if necessary + if (position >= bitString.length) { + boolean[] tmp = new boolean[position+1]; + System.arraycopy(bitString, 0, tmp, 0, bitString.length); + bitString = tmp; + } + bitString[position] = val; + } + + /** + * Create a NetscapeCertTypeExtension with the passed bit settings. + * The criticality is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public NetscapeCertTypeExtension(byte[] bitString) throws IOException { + this.bitString = + new BitArray(bitString.length*8, bitString).toBooleanArray(); + this.extensionId = NetscapeCertType_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create a NetscapeCertTypeExtension with the passed bit settings. + * The criticality is set to true. + * + * @param bitString the bits to be set for the extension. + */ + public NetscapeCertTypeExtension(boolean[] bitString) throws IOException { + this.bitString = bitString; + this.extensionId = NetscapeCertType_Id; + this.critical = true; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public NetscapeCertTypeExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = NetscapeCertType_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.bitString = val.getUnalignedBitString().toBooleanArray(); + } + + /** + * Create a default key usage. + */ + public NetscapeCertTypeExtension() { + extensionId = NetscapeCertType_Id; + critical = true; + bitString = new boolean[0]; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Boolean)) + throw new IOException("Attribute must be of type Boolean."); + + boolean val = ((Boolean)obj).booleanValue(); + set(getPosition(name), val); + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Boolean get(String name) throws IOException { + return Boolean.valueOf(isSet(getPosition(name))); + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + set(getPosition(name), false); + encodeThis(); + } + + /** + * Returns a printable representation of the NetscapeCertType. + */ + public String toString() { + String s = super.toString() + "NetscapeCertType [\n"; + + try { + if (isSet(getPosition(SSL_CLIENT))) + s += " SSL client\n"; + if (isSet(getPosition(SSL_SERVER))) + s += " SSL server\n"; + if (isSet(getPosition(S_MIME))) + s += " S/MIME\n"; + if (isSet(getPosition(OBJECT_SIGNING))) + s += " Object Signing\n"; + if (isSet(getPosition(SSL_CA))) + s += " SSL CA\n"; + if (isSet(getPosition(S_MIME_CA))) + s += " S/MIME CA\n"; + if (isSet(getPosition(OBJECT_SIGNING_CA))) + s += " Object Signing CA" ; + } catch (Exception e) { } + + s += "]\n"; + return (s); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + + if (this.extensionValue == null) { + this.extensionId = NetscapeCertType_Id; + this.critical = true; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + return mAttributeNames.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } + + /** + * Get a boolean array representing the bits of this extension, + * as it maps to the KeyUsage extension. + * @return the bit values of this extension mapped to the bit values + * of the KeyUsage extension as an array of booleans. + */ + public boolean[] getKeyUsageMappedBits() { + KeyUsageExtension keyUsage = new KeyUsageExtension(); + Boolean val = Boolean.TRUE; + + try { + if (isSet(getPosition(SSL_CLIENT)) || + isSet(getPosition(S_MIME)) || + isSet(getPosition(OBJECT_SIGNING))) + keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, val); + + if (isSet(getPosition(SSL_SERVER))) + keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, val); + + if (isSet(getPosition(SSL_CA)) || + isSet(getPosition(S_MIME_CA)) || + isSet(getPosition(OBJECT_SIGNING_CA))) + keyUsage.set(KeyUsageExtension.KEY_CERTSIGN, val); + } catch (IOException e) { } + return keyUsage.getBits(); + } +} diff --git a/src/sun/security/x509/OCSPNoCheckExtension.java b/src/sun/security/x509/OCSPNoCheckExtension.java new file mode 100644 index 00000000..b869e601 --- /dev/null +++ b/src/sun/security/x509/OCSPNoCheckExtension.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * Represent the OCSP NoCheck Extension from RFC2560. + *

      + * A CA may specify that an OCSP client can trust a responder for the + * lifetime of the responder's certificate. The CA does so by including + * the extension id-pkix-ocsp-nocheck. This SHOULD be a non-critical + * extension. The value of the extension should be NULL. CAs issuing + * such a certificate should realized that a compromise of the + * responder's key, is as serious as the compromise of a CA key used to + * sign CRLs, at least for the validity period of this certificate. CA's + * may choose to issue this type of certificate with a very short + * lifetime and renew it frequently. + *

      + * id-pkix-ocsp-nocheck OBJECT IDENTIFIER ::= { id-pkix-ocsp 5 }
      + * 
      + * + * @author Xuelei Fan + * @see Extension + * @see CertAttrSet + */ +public class OCSPNoCheckExtension extends Extension + implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.OCSPNoCheck"; + /** + * Attribute names. + */ + public static final String NAME = "OCSPNoCheck"; + + /** + * Create a OCSPNoCheckExtension + */ + public OCSPNoCheckExtension() throws IOException { + this.extensionId = PKIXExtensions.OCSPNoCheck_Id; + this.critical = false; + this.extensionValue = new byte[0]; + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public OCSPNoCheckExtension(Boolean critical, Object value) + throws IOException { + + this.extensionId = PKIXExtensions.OCSPNoCheck_Id; + this.critical = critical.booleanValue(); + + // the value should be null, just ignore it here. + this.extensionValue = new byte[0]; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + throw new IOException("No attribute is allowed by " + + "CertAttrSet:OCSPNoCheckExtension."); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + throw new IOException("No attribute is allowed by " + + "CertAttrSet:OCSPNoCheckExtension."); + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + throw new IOException("No attribute is allowed by " + + "CertAttrSet:OCSPNoCheckExtension."); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + return (new AttributeNameEnumeration()).elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } +} diff --git a/src/sun/security/x509/OIDMap.java b/src/sun/security/x509/OIDMap.java new file mode 100644 index 00000000..f6358b92 --- /dev/null +++ b/src/sun/security/x509/OIDMap.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.util.*; +import java.io.IOException; + +import java.security.cert.CertificateException; + +import sun.security.util.*; + +/** + * This class defines the mapping from OID & name to classes and vice + * versa. Used by CertificateExtensions & PKCS10 to get the java + * classes associated with a particular OID/name. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @author Andreas Sterbenz + * + */ +public class OIDMap { + + private OIDMap() { + // empty + } + + // "user-friendly" names + private static final String ROOT = X509CertImpl.NAME + "." + + X509CertInfo.NAME + "." + + X509CertInfo.EXTENSIONS; + private static final String AUTH_KEY_IDENTIFIER = ROOT + "." + + AuthorityKeyIdentifierExtension.NAME; + private static final String SUB_KEY_IDENTIFIER = ROOT + "." + + SubjectKeyIdentifierExtension.NAME; + private static final String KEY_USAGE = ROOT + "." + + KeyUsageExtension.NAME; + private static final String PRIVATE_KEY_USAGE = ROOT + "." + + PrivateKeyUsageExtension.NAME; + private static final String POLICY_MAPPINGS = ROOT + "." + + PolicyMappingsExtension.NAME; + private static final String SUB_ALT_NAME = ROOT + "." + + SubjectAlternativeNameExtension.NAME; + private static final String ISSUER_ALT_NAME = ROOT + "." + + IssuerAlternativeNameExtension.NAME; + private static final String BASIC_CONSTRAINTS = ROOT + "." + + BasicConstraintsExtension.NAME; + private static final String NAME_CONSTRAINTS = ROOT + "." + + NameConstraintsExtension.NAME; + private static final String POLICY_CONSTRAINTS = ROOT + "." + + PolicyConstraintsExtension.NAME; + private static final String CRL_NUMBER = ROOT + "." + + CRLNumberExtension.NAME; + private static final String CRL_REASON = ROOT + "." + + CRLReasonCodeExtension.NAME; + private static final String NETSCAPE_CERT = ROOT + "." + + NetscapeCertTypeExtension.NAME; + private static final String CERT_POLICIES = ROOT + "." + + CertificatePoliciesExtension.NAME; + private static final String EXT_KEY_USAGE = ROOT + "." + + ExtendedKeyUsageExtension.NAME; + private static final String INHIBIT_ANY_POLICY = ROOT + "." + + InhibitAnyPolicyExtension.NAME; + private static final String CRL_DIST_POINTS = ROOT + "." + + CRLDistributionPointsExtension.NAME; + + private static final String CERT_ISSUER = ROOT + "." + + CertificateIssuerExtension.NAME; + private static final String SUBJECT_INFO_ACCESS = ROOT + "." + + SubjectInfoAccessExtension.NAME; + private static final String AUTH_INFO_ACCESS = ROOT + "." + + AuthorityInfoAccessExtension.NAME; + private static final String ISSUING_DIST_POINT = ROOT + "." + + IssuingDistributionPointExtension.NAME; + private static final String DELTA_CRL_INDICATOR = ROOT + "." + + DeltaCRLIndicatorExtension.NAME; + private static final String FRESHEST_CRL = ROOT + "." + + FreshestCRLExtension.NAME; + private static final String OCSPNOCHECK = ROOT + "." + + OCSPNoCheckExtension.NAME; + + private static final int NetscapeCertType_data[] = + { 2, 16, 840, 1, 113730, 1, 1 }; + + /** Map ObjectIdentifier(oid) -> OIDInfo(info) */ + private final static Map oidMap; + + /** Map String(friendly name) -> OIDInfo(info) */ + private final static Map nameMap; + + static { + oidMap = new HashMap(); + nameMap = new HashMap(); + addInternal(SUB_KEY_IDENTIFIER, PKIXExtensions.SubjectKey_Id, + "sun.security.x509.SubjectKeyIdentifierExtension"); + addInternal(KEY_USAGE, PKIXExtensions.KeyUsage_Id, + "sun.security.x509.KeyUsageExtension"); + addInternal(PRIVATE_KEY_USAGE, PKIXExtensions.PrivateKeyUsage_Id, + "sun.security.x509.PrivateKeyUsageExtension"); + addInternal(SUB_ALT_NAME, PKIXExtensions.SubjectAlternativeName_Id, + "sun.security.x509.SubjectAlternativeNameExtension"); + addInternal(ISSUER_ALT_NAME, PKIXExtensions.IssuerAlternativeName_Id, + "sun.security.x509.IssuerAlternativeNameExtension"); + addInternal(BASIC_CONSTRAINTS, PKIXExtensions.BasicConstraints_Id, + "sun.security.x509.BasicConstraintsExtension"); + addInternal(CRL_NUMBER, PKIXExtensions.CRLNumber_Id, + "sun.security.x509.CRLNumberExtension"); + addInternal(CRL_REASON, PKIXExtensions.ReasonCode_Id, + "sun.security.x509.CRLReasonCodeExtension"); + addInternal(NAME_CONSTRAINTS, PKIXExtensions.NameConstraints_Id, + "sun.security.x509.NameConstraintsExtension"); + addInternal(POLICY_MAPPINGS, PKIXExtensions.PolicyMappings_Id, + "sun.security.x509.PolicyMappingsExtension"); + addInternal(AUTH_KEY_IDENTIFIER, PKIXExtensions.AuthorityKey_Id, + "sun.security.x509.AuthorityKeyIdentifierExtension"); + addInternal(POLICY_CONSTRAINTS, PKIXExtensions.PolicyConstraints_Id, + "sun.security.x509.PolicyConstraintsExtension"); + addInternal(NETSCAPE_CERT, ObjectIdentifier.newInternal + (new int[] {2,16,840,1,113730,1,1}), + "sun.security.x509.NetscapeCertTypeExtension"); + addInternal(CERT_POLICIES, PKIXExtensions.CertificatePolicies_Id, + "sun.security.x509.CertificatePoliciesExtension"); + addInternal(EXT_KEY_USAGE, PKIXExtensions.ExtendedKeyUsage_Id, + "sun.security.x509.ExtendedKeyUsageExtension"); + addInternal(INHIBIT_ANY_POLICY, PKIXExtensions.InhibitAnyPolicy_Id, + "sun.security.x509.InhibitAnyPolicyExtension"); + addInternal(CRL_DIST_POINTS, PKIXExtensions.CRLDistributionPoints_Id, + "sun.security.x509.CRLDistributionPointsExtension"); + addInternal(CERT_ISSUER, PKIXExtensions.CertificateIssuer_Id, + "sun.security.x509.CertificateIssuerExtension"); + addInternal(SUBJECT_INFO_ACCESS, PKIXExtensions.SubjectInfoAccess_Id, + "sun.security.x509.SubjectInfoAccessExtension"); + addInternal(AUTH_INFO_ACCESS, PKIXExtensions.AuthInfoAccess_Id, + "sun.security.x509.AuthorityInfoAccessExtension"); + addInternal(ISSUING_DIST_POINT, + PKIXExtensions.IssuingDistributionPoint_Id, + "sun.security.x509.IssuingDistributionPointExtension"); + addInternal(DELTA_CRL_INDICATOR, PKIXExtensions.DeltaCRLIndicator_Id, + "sun.security.x509.DeltaCRLIndicatorExtension"); + addInternal(FRESHEST_CRL, PKIXExtensions.FreshestCRL_Id, + "sun.security.x509.FreshestCRLExtension"); + addInternal(OCSPNOCHECK, PKIXExtensions.OCSPNoCheck_Id, + "sun.security.x509.OCSPNoCheckExtension"); + } + + /** + * Add attributes to the table. For internal use in the static + * initializer. + */ + private static void addInternal(String name, ObjectIdentifier oid, + String className) { + OIDInfo info = new OIDInfo(name, oid, className); + oidMap.put(oid, info); + nameMap.put(name, info); + } + + /** + * Inner class encapsulating the mapping info and Class loading. + */ + private static class OIDInfo { + + final ObjectIdentifier oid; + final String name; + final String className; + private volatile Class clazz; + + OIDInfo(String name, ObjectIdentifier oid, String className) { + this.name = name; + this.oid = oid; + this.className = className; + } + + OIDInfo(String name, ObjectIdentifier oid, Class clazz) { + this.name = name; + this.oid = oid; + this.className = clazz.getName(); + this.clazz = clazz; + } + + /** + * Return the Class object associated with this attribute. + */ + Class getClazz() throws CertificateException { + try { + Class c = clazz; + if (c == null) { + c = Class.forName(className); + clazz = c; + } + return c; + } catch (ClassNotFoundException e) { + throw new CertificateException("Could not load class: " + e, e); + } + } + } + + /** + * Add a name to lookup table. + * + * @param name the name of the attr + * @param oid the string representation of the object identifier for + * the class. + * @param clazz the Class object associated with this attribute + * @exception CertificateException on errors. + */ + public static void addAttribute(String name, String oid, Class clazz) + throws CertificateException { + ObjectIdentifier objId; + try { + objId = new ObjectIdentifier(oid); + } catch (IOException ioe) { + throw new CertificateException + ("Invalid Object identifier: " + oid); + } + OIDInfo info = new OIDInfo(name, objId, clazz); + if (oidMap.put(objId, info) != null) { + throw new CertificateException + ("Object identifier already exists: " + oid); + } + if (nameMap.put(name, info) != null) { + throw new CertificateException("Name already exists: " + name); + } + } + + /** + * Return user friendly name associated with the OID. + * + * @param oid the name of the object identifier to be returned. + * @return the user friendly name or null if no name + * is registered for this oid. + */ + public static String getName(ObjectIdentifier oid) { + OIDInfo info = oidMap.get(oid); + return (info == null) ? null : info.name; + } + + /** + * Return Object identifier for user friendly name. + * + * @param name the user friendly name. + * @return the Object Identifier or null if no oid + * is registered for this name. + */ + public static ObjectIdentifier getOID(String name) { + OIDInfo info = nameMap.get(name); + return (info == null) ? null : info.oid; + } + + /** + * Return the java class object associated with the user friendly name. + * + * @param name the user friendly name. + * @exception CertificateException if class cannot be instantiated. + */ + public static Class getClass(String name) throws CertificateException { + OIDInfo info = nameMap.get(name); + return (info == null) ? null : info.getClazz(); + } + + /** + * Return the java class object associated with the object identifier. + * + * @param oid the name of the object identifier to be returned. + * @exception CertificateException if class cannot be instatiated. + */ + public static Class getClass(ObjectIdentifier oid) + throws CertificateException { + OIDInfo info = oidMap.get(oid); + return (info == null) ? null : info.getClazz(); + } + +} diff --git a/src/sun/security/x509/OIDName.java b/src/sun/security/x509/OIDName.java new file mode 100644 index 00000000..3590f23f --- /dev/null +++ b/src/sun/security/x509/OIDName.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; +import sun.security.util.ObjectIdentifier; + +/** + * This class implements the OIDName as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class OIDName implements GeneralNameInterface { + private ObjectIdentifier oid; + + /** + * Create the OIDName object from the passed encoded Der value. + * + * @param derValue the encoded DER OIDName. + * @exception IOException on error. + */ + public OIDName(DerValue derValue) throws IOException { + oid = derValue.getOID(); + } + + /** + * Create the OIDName object with the specified name. + * + * @param name the OIDName. + */ + public OIDName(ObjectIdentifier oid) { + this.oid = oid; + } + + /** + * Create the OIDName from the String form of the OID + * + * @param name the OIDName in form "x.y.z..." + * @throws IOException on error + */ + public OIDName(String name) throws IOException { + try { + oid = new ObjectIdentifier(name); + } catch (Exception e) { + throw new IOException("Unable to create OIDName: " + e); + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_OID); + } + + /** + * Encode the OID name into the DerOutputStream. + * + * @param out the DER stream to encode the OIDName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putOID(oid); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("OIDName: " + oid.toString()); + } + + /** + * Returns this OID name. + */ + public ObjectIdentifier getOID() { + return oid; + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof OIDName)) + return false; + + OIDName other = (OIDName)obj; + + return oid.equals((Object)other.oid); + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + public int hashCode() { + return oid.hashCode(); + } + + /** + * Return type of constraint inputName places on this name:
        + *
      • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain). + *
      • NAME_MATCH = 0: input name matches name. + *
      • NAME_NARROWS = 1: input name narrows name (is lower in the naming subtree) + *
      • NAME_WIDENS = 2: input name widens name (is higher in the naming subtree) + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type. + *
      . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but narrowing and widening are + * not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != NAME_OID) + constraintType = NAME_DIFF_TYPE; + else if (this.equals((OIDName)inputName)) + constraintType = NAME_MATCH; + else + //widens and narrows not defined in RFC2459 for OIDName (aka registeredID) + throw new UnsupportedOperationException("Narrowing and widening are not supported for OIDNames"); + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + throw new UnsupportedOperationException("subtreeDepth() not supported for OIDName."); + } +} diff --git a/src/sun/security/x509/OtherName.java b/src/sun/security/x509/OtherName.java new file mode 100644 index 00000000..d2b68dae --- /dev/null +++ b/src/sun/security/x509/OtherName.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.Arrays; + +import sun.security.util.*; + +/** + * This class represents the OtherName as required by the GeneralNames + * ASN.1 object. It supplies the generic framework to allow specific + * Other Name types, and also provides minimal support for unrecognized + * Other Name types. + * + * The ASN.1 definition for OtherName is: + *
      + * OtherName ::= SEQUENCE {
      + *     type-id    OBJECT IDENTIFIER,
      + *     value      [0] EXPLICIT ANY DEFINED BY type-id
      + * }
      + * 
      + * @author Hemma Prafullchandra + */ +public class OtherName implements GeneralNameInterface { + + private String name; + private ObjectIdentifier oid; + private byte[] nameValue = null; + private GeneralNameInterface gni = null; + + private static final byte TAG_VALUE = 0; + + private int myhash = -1; + + /** + * Create the OtherName object from a passed ObjectIdentfier and + * byte array name value + * + * @param oid ObjectIdentifier of this OtherName object + * @param value the DER-encoded value of the OtherName + * @throws IOException on error + */ + public OtherName(ObjectIdentifier oid, byte[] value) throws IOException { + if (oid == null || value == null) { + throw new NullPointerException("parameters may not be null"); + } + this.oid = oid; + this.nameValue = value; + gni = getGNI(oid, value); + if (gni != null) { + name = gni.toString(); + } else { + name = "Unrecognized ObjectIdentifier: " + oid.toString(); + } + } + + /** + * Create the OtherName object from the passed encoded Der value. + * + * @param derValue the encoded DER OtherName. + * @exception IOException on error. + */ + public OtherName(DerValue derValue) throws IOException { + DerInputStream in = derValue.toDerInputStream(); + + oid = in.getOID(); + DerValue val = in.getDerValue(); + nameValue = val.toByteArray(); + gni = getGNI(oid, nameValue); + if (gni != null) { + name = gni.toString(); + } else { + name = "Unrecognized ObjectIdentifier: " + oid.toString(); + } + } + + /** + * Get ObjectIdentifier + */ + public ObjectIdentifier getOID() { + //XXXX May want to consider cloning this + return oid; + } + + /** + * Get name value + */ + public byte[] getNameValue() { + return nameValue.clone(); + } + + /** + * Get GeneralNameInterface + */ + private GeneralNameInterface getGNI(ObjectIdentifier oid, byte[] nameValue) + throws IOException { + try { + Class extClass = OIDMap.getClass(oid); + if (extClass == null) { // Unsupported OtherName + return null; + } + Class[] params = { Object.class }; + Constructor cons = extClass.getConstructor(params); + + Object[] passed = new Object[] { nameValue }; + GeneralNameInterface gni = + (GeneralNameInterface)cons.newInstance(passed); + return gni; + } catch (Exception e) { + throw new IOException("Instantiation error: " + e, e); + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return GeneralNameInterface.NAME_ANY; + } + + /** + * Encode the Other name into the DerOutputStream. + * + * @param out the DER stream to encode the Other-Name to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + if (gni != null) { + // This OtherName has a supported class + gni.encode(out); + return; + } else { + // This OtherName has no supporting class + DerOutputStream tmp = new DerOutputStream(); + tmp.putOID(oid); + tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_VALUE), nameValue); + out.write(DerValue.tag_Sequence, tmp); + } + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical. + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof OtherName)) { + return false; + } + OtherName otherOther = (OtherName)other; + if (!(otherOther.oid.equals((Object)oid))) { + return false; + } + GeneralNameInterface otherGNI = null; + try { + otherGNI = getGNI(otherOther.oid, otherOther.nameValue); + } catch (IOException ioe) { + return false; + } + + boolean result; + if (otherGNI != null) { + try { + result = (otherGNI.constrains(this) == NAME_MATCH); + } catch (UnsupportedOperationException ioe) { + result = false; + } + } else { + result = Arrays.equals(nameValue, otherOther.nameValue); + } + + return result; + } + + /** + * Returns the hash code for this OtherName. + * + * @return a hash code value. + */ + public int hashCode() { + if (myhash == -1) { + myhash = 37 + oid.hashCode(); + for (int i = 0; i < nameValue.length; i++) { + myhash = 37 * myhash + nameValue[i]; + } + } + return myhash; + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return "Other-Name: " + name; + } + + /** + * Return type of constraint inputName places on this name:
        + *
      • NAME_DIFF_TYPE = -1: input name is different type from name + * (i.e. does not constrain). + *
      • NAME_MATCH = 0: input name matches name. + *
      • NAME_NARROWS = 1: input name narrows name (is lower in the + * naming subtree) + *
      • NAME_WIDENS = 2: input name widens name (is higher in the + * naming subtree) + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, + * but is same type. + *
      . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is same type, but + * comparison operations are not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) { + int constraintType; + if (inputName == null) { + constraintType = NAME_DIFF_TYPE; + } else if (inputName.getType() != NAME_ANY) { + constraintType = NAME_DIFF_TYPE; + } else { + throw new UnsupportedOperationException("Narrowing, widening, " + + "and matching are not supported for OtherName."); + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() { + throw new UnsupportedOperationException + ("subtreeDepth() not supported for generic OtherName"); + } + +} diff --git a/src/sun/security/x509/PKIXExtensions.java b/src/sun/security/x509/PKIXExtensions.java new file mode 100644 index 00000000..5242c927 --- /dev/null +++ b/src/sun/security/x509/PKIXExtensions.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import sun.security.util.*; + +/** + * Lists all the object identifiers of the X509 extensions of the PKIX profile. + * + *

      Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + *

      Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @see Extension + * + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class PKIXExtensions { + // The object identifiers + private static final int AuthorityKey_data [] = { 2, 5, 29, 35 }; + private static final int SubjectKey_data [] = { 2, 5, 29, 14 }; + private static final int KeyUsage_data [] = { 2, 5, 29, 15 }; + private static final int PrivateKeyUsage_data [] = { 2, 5, 29, 16 }; + private static final int CertificatePolicies_data [] = { 2, 5, 29, 32 }; + private static final int PolicyMappings_data [] = { 2, 5, 29, 33 }; + private static final int SubjectAlternativeName_data [] = { 2, 5, 29, 17 }; + private static final int IssuerAlternativeName_data [] = { 2, 5, 29, 18 }; + private static final int SubjectDirectoryAttributes_data [] = { 2, 5, 29, 9 }; + private static final int BasicConstraints_data [] = { 2, 5, 29, 19 }; + private static final int NameConstraints_data [] = { 2, 5, 29, 30 }; + private static final int PolicyConstraints_data [] = { 2, 5, 29, 36 }; + private static final int CRLDistributionPoints_data [] = { 2, 5, 29, 31 }; + private static final int CRLNumber_data [] = { 2, 5, 29, 20 }; + private static final int IssuingDistributionPoint_data [] = { 2, 5, 29, 28 }; + private static final int DeltaCRLIndicator_data [] = { 2, 5, 29, 27 }; + private static final int ReasonCode_data [] = { 2, 5, 29, 21 }; + private static final int HoldInstructionCode_data [] = { 2, 5, 29, 23 }; + private static final int InvalidityDate_data [] = { 2, 5, 29, 24 }; + private static final int ExtendedKeyUsage_data [] = { 2, 5, 29, 37 }; + private static final int InhibitAnyPolicy_data [] = { 2, 5, 29, 54 }; + private static final int CertificateIssuer_data [] = { 2, 5, 29, 29 }; + private static final int AuthInfoAccess_data [] = { 1, 3, 6, 1, 5, 5, 7, 1, 1}; + private static final int SubjectInfoAccess_data [] = { 1, 3, 6, 1, 5, 5, 7, 1, 11}; + private static final int FreshestCRL_data [] = { 2, 5, 29, 46 }; + private static final int OCSPNoCheck_data [] = { 1, 3, 6, 1, 5, 5, 7, + 48, 1, 5}; + + /** + * Identifies the particular public key used to sign the certificate. + */ + public static final ObjectIdentifier AuthorityKey_Id; + + /** + * Identifies the particular public key used in an application. + */ + public static final ObjectIdentifier SubjectKey_Id; + + /** + * Defines the purpose of the key contained in the certificate. + */ + public static final ObjectIdentifier KeyUsage_Id; + + /** + * Allows the certificate issuer to specify a different validity period + * for the private key than the certificate. + */ + public static final ObjectIdentifier PrivateKeyUsage_Id; + + /** + * Contains the sequence of policy information terms. + */ + public static final ObjectIdentifier CertificatePolicies_Id; + + /** + * Lists pairs of objectidentifiers of policies considered equivalent by the + * issuing CA to the subject CA. + */ + public static final ObjectIdentifier PolicyMappings_Id; + + /** + * Allows additional identities to be bound to the subject of the certificate. + */ + public static final ObjectIdentifier SubjectAlternativeName_Id; + + /** + * Allows additional identities to be associated with the certificate issuer. + */ + public static final ObjectIdentifier IssuerAlternativeName_Id; + + /** + * Identifies additional directory attributes. + * This extension is always non-critical. + */ + public static final ObjectIdentifier SubjectDirectoryAttributes_Id; + + /** + * Identifies whether the subject of the certificate is a CA and how deep + * a certification path may exist through that CA. + */ + public static final ObjectIdentifier BasicConstraints_Id; + + /** + * Provides for permitted and excluded subtrees that place restrictions + * on names that may be included within a certificate issued by a given CA. + */ + public static final ObjectIdentifier NameConstraints_Id; + + /** + * Used to either prohibit policy mapping or limit the set of policies + * that can be in subsequent certificates. + */ + public static final ObjectIdentifier PolicyConstraints_Id; + + /** + * Identifies how CRL information is obtained. + */ + public static final ObjectIdentifier CRLDistributionPoints_Id; + + /** + * Conveys a monotonically increasing sequence number for each CRL + * issued by a given CA. + */ + public static final ObjectIdentifier CRLNumber_Id; + + /** + * Identifies the CRL distribution point for a particular CRL. + */ + public static final ObjectIdentifier IssuingDistributionPoint_Id; + + /** + * Identifies the delta CRL. + */ + public static final ObjectIdentifier DeltaCRLIndicator_Id; + + /** + * Identifies the reason for the certificate revocation. + */ + public static final ObjectIdentifier ReasonCode_Id; + + /** + * This extension provides a registered instruction identifier indicating + * the action to be taken, after encountering a certificate that has been + * placed on hold. + */ + public static final ObjectIdentifier HoldInstructionCode_Id; + + /** + * Identifies the date on which it is known or suspected that the private + * key was compromised or that the certificate otherwise became invalid. + */ + public static final ObjectIdentifier InvalidityDate_Id; + /** + * Identifies one or more purposes for which the certified public key + * may be used, in addition to or in place of the basic purposes + * indicated in the key usage extension field. + */ + public static final ObjectIdentifier ExtendedKeyUsage_Id; + + /** + * Specifies whether any-policy policy OID is permitted + */ + public static final ObjectIdentifier InhibitAnyPolicy_Id; + + /** + * Identifies the certificate issuer associated with an entry in an + * indirect CRL. + */ + public static final ObjectIdentifier CertificateIssuer_Id; + + /** + * This extension indicates how to access CA information and services for + * the issuer of the certificate in which the extension appears. + * This information may be used for on-line certification validation + * services. + */ + public static final ObjectIdentifier AuthInfoAccess_Id; + + /** + * This extension indicates how to access CA information and services for + * the subject of the certificate in which the extension appears. + */ + public static final ObjectIdentifier SubjectInfoAccess_Id; + + /** + * Identifies how delta CRL information is obtained. + */ + public static final ObjectIdentifier FreshestCRL_Id; + + /** + * Identifies the OCSP client can trust the responder for the + * lifetime of the responder's certificate. + */ + public static final ObjectIdentifier OCSPNoCheck_Id; + + static { + AuthorityKey_Id = ObjectIdentifier.newInternal(AuthorityKey_data); + SubjectKey_Id = ObjectIdentifier.newInternal(SubjectKey_data); + KeyUsage_Id = ObjectIdentifier.newInternal(KeyUsage_data); + PrivateKeyUsage_Id = ObjectIdentifier.newInternal(PrivateKeyUsage_data); + CertificatePolicies_Id = + ObjectIdentifier.newInternal(CertificatePolicies_data); + PolicyMappings_Id = ObjectIdentifier.newInternal(PolicyMappings_data); + SubjectAlternativeName_Id = + ObjectIdentifier.newInternal(SubjectAlternativeName_data); + IssuerAlternativeName_Id = + ObjectIdentifier.newInternal(IssuerAlternativeName_data); + ExtendedKeyUsage_Id = ObjectIdentifier.newInternal(ExtendedKeyUsage_data); + InhibitAnyPolicy_Id = ObjectIdentifier.newInternal(InhibitAnyPolicy_data); + SubjectDirectoryAttributes_Id = + ObjectIdentifier.newInternal(SubjectDirectoryAttributes_data); + BasicConstraints_Id = + ObjectIdentifier.newInternal(BasicConstraints_data); + ReasonCode_Id = ObjectIdentifier.newInternal(ReasonCode_data); + HoldInstructionCode_Id = + ObjectIdentifier.newInternal(HoldInstructionCode_data); + InvalidityDate_Id = ObjectIdentifier.newInternal(InvalidityDate_data); + + NameConstraints_Id = ObjectIdentifier.newInternal(NameConstraints_data); + PolicyConstraints_Id = + ObjectIdentifier.newInternal(PolicyConstraints_data); + CRLDistributionPoints_Id = + ObjectIdentifier.newInternal(CRLDistributionPoints_data); + CRLNumber_Id = + ObjectIdentifier.newInternal(CRLNumber_data); + IssuingDistributionPoint_Id = + ObjectIdentifier.newInternal(IssuingDistributionPoint_data); + DeltaCRLIndicator_Id = + ObjectIdentifier.newInternal(DeltaCRLIndicator_data); + CertificateIssuer_Id = + ObjectIdentifier.newInternal(CertificateIssuer_data); + AuthInfoAccess_Id = + ObjectIdentifier.newInternal(AuthInfoAccess_data); + SubjectInfoAccess_Id = + ObjectIdentifier.newInternal(SubjectInfoAccess_data); + FreshestCRL_Id = ObjectIdentifier.newInternal(FreshestCRL_data); + OCSPNoCheck_Id = ObjectIdentifier.newInternal(OCSPNoCheck_data); + } +} diff --git a/src/sun/security/x509/PolicyConstraintsExtension.java b/src/sun/security/x509/PolicyConstraintsExtension.java new file mode 100644 index 00000000..c9756a20 --- /dev/null +++ b/src/sun/security/x509/PolicyConstraintsExtension.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the certificate extension which specifies the + * Policy constraints. + *

      + * The policy constraints extension can be used in certificates issued + * to CAs. The policy constraints extension constrains path validation + * in two ways. It can be used to prohibit policy mapping or require + * that each certificate in a path contain an acceptable policy + * identifier.

      + * The ASN.1 syntax for this is (IMPLICIT tagging is defined in the + * module definition): + *

      + * PolicyConstraints ::= SEQUENCE {
      + *     requireExplicitPolicy [0] SkipCerts OPTIONAL,
      + *     inhibitPolicyMapping  [1] SkipCerts OPTIONAL
      + * }
      + * SkipCerts ::= INTEGER (0..MAX)
      + * 
      + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class PolicyConstraintsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PolicyConstraints"; + /** + * Attribute names. + */ + public static final String NAME = "PolicyConstraints"; + public static final String REQUIRE = "require"; + public static final String INHIBIT = "inhibit"; + + private static final byte TAG_REQUIRE = 0; + private static final byte TAG_INHIBIT = 1; + + private int require = -1; + private int inhibit = -1; + + // Encode this extension value. + private void encodeThis() throws IOException { + if (require == -1 && inhibit == -1) { + this.extensionValue = null; + return; + } + DerOutputStream tagged = new DerOutputStream(); + DerOutputStream seq = new DerOutputStream(); + + if (require != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(require); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_REQUIRE), tmp); + } + if (inhibit != -1) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putInteger(inhibit); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_INHIBIT), tmp); + } + seq.write(DerValue.tag_Sequence, tagged); + this.extensionValue = seq.toByteArray(); + } + + /** + * Create a PolicyConstraintsExtension object with both + * require explicit policy and inhibit policy mapping. The + * extension is marked non-critical. + * + * @param require require explicit policy (-1 for optional). + * @param inhibit inhibit policy mapping (-1 for optional). + */ + public PolicyConstraintsExtension(int require, int inhibit) + throws IOException { + this(Boolean.FALSE, require, inhibit); + } + + /** + * Create a PolicyConstraintsExtension object with specified + * criticality and both require explicit policy and inhibit + * policy mapping. + * + * @param critical true if the extension is to be treated as critical. + * @param require require explicit policy (-1 for optional). + * @param inhibit inhibit policy mapping (-1 for optional). + */ + public PolicyConstraintsExtension(Boolean critical, int require, int inhibit) + throws IOException { + this.require = require; + this.inhibit = inhibit; + this.extensionId = PKIXExtensions.PolicyConstraints_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create the extension from its DER encoded value and criticality. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public PolicyConstraintsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.PolicyConstraints_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Sequence tag missing for PolicyConstraint."); + } + DerInputStream in = val.data; + while (in != null && in.available() != 0) { + DerValue next = in.getDerValue(); + + if (next.isContextSpecific(TAG_REQUIRE) && !next.isConstructed()) { + if (this.require != -1) + throw new IOException("Duplicate requireExplicitPolicy" + + "found in the PolicyConstraintsExtension"); + next.resetTag(DerValue.tag_Integer); + this.require = next.getInteger(); + + } else if (next.isContextSpecific(TAG_INHIBIT) && + !next.isConstructed()) { + if (this.inhibit != -1) + throw new IOException("Duplicate inhibitPolicyMapping" + + "found in the PolicyConstraintsExtension"); + next.resetTag(DerValue.tag_Integer); + this.inhibit = next.getInteger(); + } else + throw new IOException("Invalid encoding of PolicyConstraint"); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + String s; + s = super.toString() + "PolicyConstraints: [" + " Require: "; + if (require == -1) + s += "unspecified;"; + else + s += require + ";"; + s += "\tInhibit: "; + if (inhibit == -1) + s += "unspecified"; + else + s += inhibit; + s += " ]\n"; + return s; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PolicyConstraints_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Integer)) { + throw new IOException("Attribute value should be of type Integer."); + } + if (name.equalsIgnoreCase(REQUIRE)) { + require = ((Integer)obj).intValue(); + } else if (name.equalsIgnoreCase(INHIBIT)) { + inhibit = ((Integer)obj).intValue(); + } else { + throw new IOException("Attribute name " + "[" + name + "]" + + " not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public Integer get(String name) throws IOException { + if (name.equalsIgnoreCase(REQUIRE)) { + return new Integer(require); + } else if (name.equalsIgnoreCase(INHIBIT)) { + return new Integer(inhibit); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(REQUIRE)) { + require = -1; + } else if (name.equalsIgnoreCase(INHIBIT)) { + inhibit = -1; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyConstraints."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(REQUIRE); + elements.addElement(INHIBIT); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/PolicyInformation.java b/src/sun/security/x509/PolicyInformation.java new file mode 100644 index 00000000..b8d734ec --- /dev/null +++ b/src/sun/security/x509/PolicyInformation.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.security.cert.PolicyQualifierInfo; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * PolicyInformation is the class that contains a specific certificate policy + * that is part of the CertificatePoliciesExtension. A + * CertificatePolicyExtension value consists of a vector of these objects. + *

      + * The ASN.1 syntax for PolicyInformation (IMPLICIT tagging is defined in the + * module definition): + *

      + *
      + * PolicyInformation ::= SEQUENCE {
      + *      policyIdentifier   CertPolicyId,
      + *      policyQualifiers   SEQUENCE SIZE (1..MAX) OF
      + *                              PolicyQualifierInfo OPTIONAL }
      + *
      + * CertPolicyId ::= OBJECT IDENTIFIER
      + *
      + * PolicyQualifierInfo ::= SEQUENCE {
      + *      policyQualifierId  PolicyQualifierId,
      + *      qualifier          ANY DEFINED BY policyQualifierId }
      + * 
      + * + * @author Sean Mullan + * @author Anne Anderson + * @since 1.4 + */ +public class PolicyInformation { + + // Attribute names + public static final String NAME = "PolicyInformation"; + public static final String ID = "id"; + public static final String QUALIFIERS = "qualifiers"; + + /* The policy OID */ + private CertificatePolicyId policyIdentifier; + + /* A Set of java.security.cert.PolicyQualifierInfo objects */ + private Set policyQualifiers; + + /** + * Create an instance of PolicyInformation + * + * @param policyIdentifier the policyIdentifier as a + * CertificatePolicyId + * @param policyQualifiers a Set of PolicyQualifierInfo objects. + * Must not be NULL. Specify an empty Set for no qualifiers. + * @exception IOException on decoding errors. + */ + public PolicyInformation(CertificatePolicyId policyIdentifier, + Set policyQualifiers) throws IOException { + if (policyQualifiers == null) { + throw new NullPointerException("policyQualifiers is null"); + } + this.policyQualifiers = + new LinkedHashSet(policyQualifiers); + this.policyIdentifier = policyIdentifier; + } + + /** + * Create an instance of PolicyInformation, decoding from + * the passed DerValue. + * + * @param val the DerValue to construct the PolicyInformation from. + * @exception IOException on decoding errors. + */ + public PolicyInformation(DerValue val) throws IOException { + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding of PolicyInformation"); + } + policyIdentifier = new CertificatePolicyId(val.data.getDerValue()); + if (val.data.available() != 0) { + policyQualifiers = new LinkedHashSet(); + DerValue opt = val.data.getDerValue(); + if (opt.tag != DerValue.tag_Sequence) + throw new IOException("Invalid encoding of PolicyInformation"); + if (opt.data.available() == 0) + throw new IOException("No data available in policyQualifiers"); + while (opt.data.available() != 0) + policyQualifiers.add(new PolicyQualifierInfo + (opt.data.getDerValue().toByteArray())); + } else { + policyQualifiers = Collections.emptySet(); + } + } + + /** + * Compare this PolicyInformation with another object for equality + * + * @param other object to be compared with this + * @return true iff the PolicyInformation objects match + */ + public boolean equals(Object other) { + if (!(other instanceof PolicyInformation)) + return false; + PolicyInformation piOther = (PolicyInformation)other; + + if (!policyIdentifier.equals(piOther.getPolicyIdentifier())) + return false; + + return policyQualifiers.equals(piOther.getPolicyQualifiers()); + } + + /** + * Returns the hash code for this PolicyInformation. + * + * @return a hash code value. + */ + public int hashCode() { + int myhash = 37 + policyIdentifier.hashCode(); + myhash = 37 * myhash + policyQualifiers.hashCode(); + return myhash; + } + + /** + * Return the policyIdentifier value + * + * @return The CertificatePolicyId object containing + * the policyIdentifier (not a copy). + */ + public CertificatePolicyId getPolicyIdentifier() { + return policyIdentifier; + } + + /** + * Return the policyQualifiers value + * + * @return a Set of PolicyQualifierInfo objects associated + * with this certificate policy (not a copy). + * Returns an empty Set if there are no qualifiers. + * Never returns null. + */ + public Set getPolicyQualifiers() { + return policyQualifiers; + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + if (name.equalsIgnoreCase(ID)) { + return policyIdentifier; + } else if (name.equalsIgnoreCase(QUALIFIERS)) { + return policyQualifiers; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by PolicyInformation."); + } + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with instanceof + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(ID)) { + if (obj instanceof CertificatePolicyId) + policyIdentifier = (CertificatePolicyId)obj; + else + throw new IOException("Attribute value must be instance " + + "of CertificatePolicyId."); + } else if (name.equalsIgnoreCase(QUALIFIERS)) { + if (policyIdentifier == null) { + throw new IOException("Attribute must have a " + + "CertificatePolicyIdentifier value before " + + "PolicyQualifierInfo can be set."); + } + if (obj instanceof Set) { + Iterator i = ((Set)obj).iterator(); + while (i.hasNext()) { + Object obj1 = i.next(); + if (!(obj1 instanceof PolicyQualifierInfo)) { + throw new IOException("Attribute value must be a" + + "Set of PolicyQualifierInfo objects."); + } + } + policyQualifiers = (Set) obj; + } else { + throw new IOException("Attribute value must be of type Set."); + } + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by PolicyInformation"); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(QUALIFIERS)) { + policyQualifiers = Collections.emptySet(); + } else if (name.equalsIgnoreCase(ID)) { + throw new IOException("Attribute ID may not be deleted from " + + "PolicyInformation."); + } else { + //ID may not be deleted + throw new IOException("Attribute name [" + name + + "] not recognized by PolicyInformation."); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(ID); + elements.addElement(QUALIFIERS); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Return a printable representation of the PolicyInformation. + */ + public String toString() { + StringBuilder s = new StringBuilder(" [" + policyIdentifier.toString()); + s.append(policyQualifiers + " ]\n"); + return s.toString(); + } + + /** + * Write the PolicyInformation to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + policyIdentifier.encode(tmp); + if (!policyQualifiers.isEmpty()) { + DerOutputStream tmp2 = new DerOutputStream(); + for (PolicyQualifierInfo pq : policyQualifiers) { + tmp2.write(pq.getEncoded()); + } + tmp.write(DerValue.tag_Sequence, tmp2); + } + out.write(DerValue.tag_Sequence, tmp); + } +} diff --git a/src/sun/security/x509/PolicyMappingsExtension.java b/src/sun/security/x509/PolicyMappingsExtension.java new file mode 100644 index 00000000..fba51046 --- /dev/null +++ b/src/sun/security/x509/PolicyMappingsExtension.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +import sun.security.util.*; + +/** + * Represent the Policy Mappings Extension. + * + * This extension, if present, identifies the certificate policies considered + * identical between the issuing and the subject CA. + *

      Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + *

      Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class PolicyMappingsExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PolicyMappings"; + /** + * Attribute names. + */ + public static final String NAME = "PolicyMappings"; + public static final String MAP = "map"; + + // Private data members + private List maps; + + // Encode this extension value + private void encodeThis() throws IOException { + if (maps == null || maps.isEmpty()) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + for (CertificatePolicyMap map : maps) { + map.encode(tmp); + } + + os.write(DerValue.tag_Sequence, tmp); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a PolicyMappings with the List of CertificatePolicyMap. + * + * @param maps the List of CertificatePolicyMap. + */ + public PolicyMappingsExtension(List map) + throws IOException { + this.maps = map; + this.extensionId = PKIXExtensions.PolicyMappings_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create a default PolicyMappingsExtension. + */ + public PolicyMappingsExtension() { + extensionId = PKIXExtensions.KeyUsage_Id; + critical = false; + maps = new ArrayList(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @params critical true if the extension is to be treated as critical. + * @params value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public PolicyMappingsExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.PolicyMappings_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "PolicyMappingsExtension."); + } + maps = new ArrayList(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + CertificatePolicyMap map = new CertificatePolicyMap(seq); + maps.add(map); + } + } + + /** + * Returns a printable representation of the policy map. + */ + public String toString() { + if (maps == null) return ""; + String s = super.toString() + "PolicyMappings [\n" + + maps.toString() + "]\n"; + + return (s); + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PolicyMappings_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with instanceof + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(MAP)) { + if (!(obj instanceof List)) { + throw new IOException("Attribute value should be of" + + " type List."); + } + maps = (List)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public List get(String name) throws IOException { + if (name.equalsIgnoreCase(MAP)) { + return (maps); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(MAP)) { + maps = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:PolicyMappingsExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(MAP); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName () { + return (NAME); + } +} diff --git a/src/sun/security/x509/PrivateKeyUsageExtension.java b/src/sun/security/x509/PrivateKeyUsageExtension.java new file mode 100644 index 00000000..c20d8557 --- /dev/null +++ b/src/sun/security/x509/PrivateKeyUsageExtension.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateParsingException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.util.Date; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * This class defines the Private Key Usage Extension. + * + *

      The Private Key Usage Period extension allows the certificate issuer + * to specify a different validity period for the private key than the + * certificate. This extension is intended for use with digital + * signature keys. This extension consists of two optional components + * notBefore and notAfter. The private key associated with the + * certificate should not be used to sign objects before or after the + * times specified by the two components, respectively. + * + *

      + * PrivateKeyUsagePeriod ::= SEQUENCE {
      + *     notBefore  [0]  GeneralizedTime OPTIONAL,
      + *     notAfter   [1]  GeneralizedTime OPTIONAL }
      + * 
      + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class PrivateKeyUsageExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info.extensions.PrivateKeyUsage"; + /** + * Sub attributes name for this CertAttrSet. + */ + public static final String NAME = "PrivateKeyUsage"; + public static final String NOT_BEFORE = "not_before"; + public static final String NOT_AFTER = "not_after"; + + // Private data members + private static final byte TAG_BEFORE = 0; + private static final byte TAG_AFTER = 1; + + private Date notBefore = null; + private Date notAfter = null; + + // Encode this extension value. + private void encodeThis() throws IOException { + if (notBefore == null && notAfter == null) { + this.extensionValue = null; + return; + } + DerOutputStream seq = new DerOutputStream(); + + DerOutputStream tagged = new DerOutputStream(); + if (notBefore != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putGeneralizedTime(notBefore); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_BEFORE), tmp); + } + if (notAfter != null) { + DerOutputStream tmp = new DerOutputStream(); + tmp.putGeneralizedTime(notAfter); + tagged.writeImplicit(DerValue.createTag(DerValue.TAG_CONTEXT, + false, TAG_AFTER), tmp); + } + seq.write(DerValue.tag_Sequence, tagged); + this.extensionValue = seq.toByteArray(); + } + + /** + * The default constructor for PrivateKeyUsageExtension. + * + * @param notBefore the date/time before which the private key + * should not be used. + * @param notAfter the date/time after which the private key + * should not be used. + */ + public PrivateKeyUsageExtension(Date notBefore, Date notAfter) + throws IOException { + this.notBefore = notBefore; + this.notAfter = notAfter; + + this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception CertificateException on certificate parsing errors. + * @exception IOException on error. + */ + public PrivateKeyUsageExtension(Boolean critical, Object value) + throws CertificateException, IOException { + this.extensionId = PKIXExtensions.PrivateKeyUsage_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerInputStream str = new DerInputStream(this.extensionValue); + DerValue[] seq = str.getSequence(2); + + // NB. this is always encoded with the IMPLICIT tag + // The checks only make sense if we assume implicit tagging, + // with explicit tagging the form is always constructed. + for (int i = 0; i < seq.length; i++) { + DerValue opt = seq[i]; + + if (opt.isContextSpecific(TAG_BEFORE) && + !opt.isConstructed()) { + if (notBefore != null) { + throw new CertificateParsingException( + "Duplicate notBefore in PrivateKeyUsage."); + } + opt.resetTag(DerValue.tag_GeneralizedTime); + str = new DerInputStream(opt.toByteArray()); + notBefore = str.getGeneralizedTime(); + + } else if (opt.isContextSpecific(TAG_AFTER) && + !opt.isConstructed()) { + if (notAfter != null) { + throw new CertificateParsingException( + "Duplicate notAfter in PrivateKeyUsage."); + } + opt.resetTag(DerValue.tag_GeneralizedTime); + str = new DerInputStream(opt.toByteArray()); + notAfter = str.getGeneralizedTime(); + } else + throw new IOException("Invalid encoding of " + + "PrivateKeyUsageExtension"); + } + } + + /** + * Return the printable string. + */ + public String toString() { + return(super.toString() + + "PrivateKeyUsage: [\n" + + ((notBefore == null) ? "" : "From: " + notBefore.toString() + ", ") + + ((notAfter == null) ? "" : "To: " + notAfter.toString()) + + "]\n"); + } + + /** + * Verify that that the current time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void valid() + throws CertificateNotYetValidException, CertificateExpiredException { + Date now = new Date(); + valid(now); + } + + /** + * Verify that that the passed time is within the validity period. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the Date supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the Date supplied. + * + */ + public void valid(Date now) + throws CertificateNotYetValidException, CertificateExpiredException { + /* + * we use the internal Dates rather than the passed in Date + * because someone could override the Date methods after() + * and before() to do something entirely different. + */ + if (notBefore.after(now)) { + throw new CertificateNotYetValidException("NotBefore: " + + notBefore.toString()); + } + if (notAfter.before(now)) { + throw new CertificateExpiredException("NotAfter: " + + notAfter.toString()); + } + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.PrivateKeyUsage_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public void set(String name, Object obj) + throws CertificateException, IOException { + if (!(obj instanceof Date)) { + throw new CertificateException("Attribute must be of type Date."); + } + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = (Date)obj; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = (Date)obj; + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + encodeThis(); + } + + /** + * Get the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public Date get(String name) throws CertificateException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + return (new Date(notBefore.getTime())); + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + return (new Date(notAfter.getTime())); + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + } + + /** + * Delete the attribute value. + * @exception CertificateException on attribute handling errors. + */ + public void delete(String name) throws CertificateException, IOException { + if (name.equalsIgnoreCase(NOT_BEFORE)) { + notBefore = null; + } else if (name.equalsIgnoreCase(NOT_AFTER)) { + notAfter = null; + } else { + throw new CertificateException("Attribute name not recognized by" + + " CertAttrSet:PrivateKeyUsage."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NOT_BEFORE); + elements.addElement(NOT_AFTER); + + return(elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } +} diff --git a/src/sun/security/x509/RDN.java b/src/sun/security/x509/RDN.java new file mode 100644 index 00000000..bb932c4e --- /dev/null +++ b/src/sun/security/x509/RDN.java @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.StringReader; +import java.util.*; + +import sun.security.util.*; + +/** + * RDNs are a set of {attribute = value} assertions. Some of those + * attributes are "distinguished" (unique w/in context). Order is + * never relevant. + * + * Some X.500 names include only a single distinguished attribute + * per RDN. This style is currently common. + * + * Note that DER-encoded RDNs sort AVAs by assertion OID ... so that + * when we parse this data we don't have to worry about canonicalizing + * it, but we'll need to sort them when we expose the RDN class more. + *

      + * The ASN.1 for RDNs is: + *

      + * RelativeDistinguishedName ::=
      + *   SET OF AttributeTypeAndValue
      + *
      + * AttributeTypeAndValue ::= SEQUENCE {
      + *   type     AttributeType,
      + *   value    AttributeValue }
      + *
      + * AttributeType ::= OBJECT IDENTIFIER
      + *
      + * AttributeValue ::= ANY DEFINED BY AttributeType
      + * 
      + * + * Note that instances of this class are immutable. + * + */ +public class RDN { + + // currently not private, accessed directly from X500Name + final AVA[] assertion; + + // cached immutable List of the AVAs + private volatile List avaList; + + // cache canonical String form + private volatile String canonicalString; + + /** + * Constructs an RDN from its printable representation. + * + * An RDN may consist of one or multiple Attribute Value Assertions (AVAs), + * using '+' as a separator. + * If the '+' should be considered part of an AVA value, it must be + * preceded by '\'. + * + * @param name String form of RDN + * @throws IOException on parsing error + */ + public RDN(String name) throws IOException { + this(name, Collections.emptyMap()); + } + + /** + * Constructs an RDN from its printable representation. + * + * An RDN may consist of one or multiple Attribute Value Assertions (AVAs), + * using '+' as a separator. + * If the '+' should be considered part of an AVA value, it must be + * preceded by '\'. + * + * @param name String form of RDN + * @param keyword an additional mapping of keywords to OIDs + * @throws IOException on parsing error + */ + public RDN(String name, Map keywordMap) throws IOException { + int quoteCount = 0; + int searchOffset = 0; + int avaOffset = 0; + List avaVec = new ArrayList(3); + int nextPlus = name.indexOf('+'); + while (nextPlus >= 0) { + quoteCount += X500Name.countQuotes(name, searchOffset, nextPlus); + /* + * We have encountered an AVA delimiter (plus sign). + * If the plus sign in the RDN under consideration is + * preceded by a backslash (escape), or by a double quote, it + * is part of the AVA. Otherwise, it is used as a separator, to + * delimit the AVA under consideration from any subsequent AVAs. + */ + if (nextPlus > 0 && name.charAt(nextPlus - 1) != '\\' + && quoteCount != 1) { + /* + * Plus sign is a separator + */ + String avaString = name.substring(avaOffset, nextPlus); + if (avaString.length() == 0) { + throw new IOException("empty AVA in RDN \"" + name + "\""); + } + + // Parse AVA, and store it in vector + AVA ava = new AVA(new StringReader(avaString), keywordMap); + avaVec.add(ava); + + // Increase the offset + avaOffset = nextPlus + 1; + + // Set quote counter back to zero + quoteCount = 0; + } + searchOffset = nextPlus + 1; + nextPlus = name.indexOf('+', searchOffset); + } + + // parse last or only AVA + String avaString = name.substring(avaOffset); + if (avaString.length() == 0) { + throw new IOException("empty AVA in RDN \"" + name + "\""); + } + AVA ava = new AVA(new StringReader(avaString), keywordMap); + avaVec.add(ava); + + assertion = avaVec.toArray(new AVA[avaVec.size()]); + } + + /* + * Constructs an RDN from its printable representation. + * + * An RDN may consist of one or multiple Attribute Value Assertions (AVAs), + * using '+' as a separator. + * If the '+' should be considered part of an AVA value, it must be + * preceded by '\'. + * + * @param name String form of RDN + * @throws IOException on parsing error + */ + RDN(String name, String format) throws IOException { + this(name, format, Collections.emptyMap()); + } + + /* + * Constructs an RDN from its printable representation. + * + * An RDN may consist of one or multiple Attribute Value Assertions (AVAs), + * using '+' as a separator. + * If the '+' should be considered part of an AVA value, it must be + * preceded by '\'. + * + * @param name String form of RDN + * @param keyword an additional mapping of keywords to OIDs + * @throws IOException on parsing error + */ + RDN(String name, String format, Map keywordMap) + throws IOException { + if (format.equalsIgnoreCase("RFC2253") == false) { + throw new IOException("Unsupported format " + format); + } + int searchOffset = 0; + int avaOffset = 0; + List avaVec = new ArrayList(3); + int nextPlus = name.indexOf('+'); + while (nextPlus >= 0) { + /* + * We have encountered an AVA delimiter (plus sign). + * If the plus sign in the RDN under consideration is + * preceded by a backslash (escape), or by a double quote, it + * is part of the AVA. Otherwise, it is used as a separator, to + * delimit the AVA under consideration from any subsequent AVAs. + */ + if (nextPlus > 0 && name.charAt(nextPlus - 1) != '\\' ) { + /* + * Plus sign is a separator + */ + String avaString = name.substring(avaOffset, nextPlus); + if (avaString.length() == 0) { + throw new IOException("empty AVA in RDN \"" + name + "\""); + } + + // Parse AVA, and store it in vector + AVA ava = new AVA + (new StringReader(avaString), AVA.RFC2253, keywordMap); + avaVec.add(ava); + + // Increase the offset + avaOffset = nextPlus + 1; + } + searchOffset = nextPlus + 1; + nextPlus = name.indexOf('+', searchOffset); + } + + // parse last or only AVA + String avaString = name.substring(avaOffset); + if (avaString.length() == 0) { + throw new IOException("empty AVA in RDN \"" + name + "\""); + } + AVA ava = new AVA(new StringReader(avaString), AVA.RFC2253, keywordMap); + avaVec.add(ava); + + assertion = avaVec.toArray(new AVA[avaVec.size()]); + } + + /* + * Constructs an RDN from an ASN.1 encoded value. The encoding + * of the name in the stream uses DER (a BER/1 subset). + * + * @param value a DER-encoded value holding an RDN. + * @throws IOException on parsing error. + */ + RDN(DerValue rdn) throws IOException { + if (rdn.tag != DerValue.tag_Set) { + throw new IOException("X500 RDN"); + } + DerInputStream dis = new DerInputStream(rdn.toByteArray()); + DerValue[] avaset = dis.getSet(5); + + assertion = new AVA[avaset.length]; + for (int i = 0; i < avaset.length; i++) { + assertion[i] = new AVA(avaset[i]); + } + } + + /* + * Creates an empty RDN with slots for specified + * number of AVAs. + * + * @param i number of AVAs to be in RDN + */ + RDN(int i) { assertion = new AVA[i]; } + + public RDN(AVA ava) { + if (ava == null) { + throw new NullPointerException(); + } + assertion = new AVA[] { ava }; + } + + public RDN(AVA[] avas) { + assertion = avas.clone(); + for (int i = 0; i < assertion.length; i++) { + if (assertion[i] == null) { + throw new NullPointerException(); + } + } + } + + /** + * Return an immutable List of the AVAs in this RDN. + */ + public List avas() { + List list = avaList; + if (list == null) { + list = Collections.unmodifiableList(Arrays.asList(assertion)); + avaList = list; + } + return list; + } + + /** + * Return the number of AVAs in this RDN. + */ + public int size() { + return assertion.length; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof RDN == false) { + return false; + } + RDN other = (RDN)obj; + if (this.assertion.length != other.assertion.length) { + return false; + } + String thisCanon = this.toRFC2253String(true); + String otherCanon = other.toRFC2253String(true); + return thisCanon.equals(otherCanon); + } + + /* + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + * + * @returns int hashCode value + */ + public int hashCode() { + return toRFC2253String(true).hashCode(); + } + + /* + * return specified attribute value from RDN + * + * @params oid ObjectIdentifier of attribute to be found + * @returns DerValue of attribute value; null if attribute does not exist + */ + DerValue findAttribute(ObjectIdentifier oid) { + for (int i = 0; i < assertion.length; i++) { + if (assertion[i].oid.equals((Object)oid)) { + return assertion[i].value; + } + } + return null; + } + + /* + * Encode the RDN in DER-encoded form. + * + * @param out DerOutputStream to which RDN is to be written + * @throws IOException on error + */ + void encode(DerOutputStream out) throws IOException { + out.putOrderedSetOf(DerValue.tag_Set, assertion); + } + + /* + * Returns a printable form of this RDN, using RFC 1779 style catenation + * of attribute/value assertions, and emitting attribute type keywords + * from RFCs 1779, 2253, and 3280. + */ + public String toString() { + if (assertion.length == 1) { + return assertion[0].toString(); + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < assertion.length; i++) { + if (i != 0) { + sb.append(" + "); + } + sb.append(assertion[i].toString()); + } + return sb.toString(); + } + + /* + * Returns a printable form of this RDN using the algorithm defined in + * RFC 1779. Only RFC 1779 attribute type keywords are emitted. + */ + public String toRFC1779String() { + return toRFC1779String(Collections.emptyMap()); + } + + /* + * Returns a printable form of this RDN using the algorithm defined in + * RFC 1779. RFC 1779 attribute type keywords are emitted, as well + * as keywords contained in the OID/keyword map. + */ + public String toRFC1779String(Map oidMap) { + if (assertion.length == 1) { + return assertion[0].toRFC1779String(oidMap); + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < assertion.length; i++) { + if (i != 0) { + sb.append(" + "); + } + sb.append(assertion[i].toRFC1779String(oidMap)); + } + return sb.toString(); + } + + /* + * Returns a printable form of this RDN using the algorithm defined in + * RFC 2253. Only RFC 2253 attribute type keywords are emitted. + */ + public String toRFC2253String() { + return toRFC2253StringInternal + (false, Collections.emptyMap()); + } + + /* + * Returns a printable form of this RDN using the algorithm defined in + * RFC 2253. RFC 2253 attribute type keywords are emitted, as well as + * keywords contained in the OID/keyword map. + */ + public String toRFC2253String(Map oidMap) { + return toRFC2253StringInternal(false, oidMap); + } + + /* + * Returns a printable form of this RDN using the algorithm defined in + * RFC 2253. Only RFC 2253 attribute type keywords are emitted. + * If canonical is true, then additional canonicalizations + * documented in X500Principal.getName are performed. + */ + public String toRFC2253String(boolean canonical) { + if (canonical == false) { + return toRFC2253StringInternal + (false, Collections.emptyMap()); + } + String c = canonicalString; + if (c == null) { + c = toRFC2253StringInternal + (true, Collections.emptyMap()); + canonicalString = c; + } + return c; + } + + private String toRFC2253StringInternal + (boolean canonical, Map oidMap) { + /* + * Section 2.2: When converting from an ASN.1 RelativeDistinguishedName + * to a string, the output consists of the string encodings of each + * AttributeTypeAndValue (according to 2.3), in any order. + * + * Where there is a multi-valued RDN, the outputs from adjoining + * AttributeTypeAndValues are separated by a plus ('+' ASCII 43) + * character. + */ + + // normally, an RDN only contains one AVA + if (assertion.length == 1) { + return canonical ? assertion[0].toRFC2253CanonicalString() : + assertion[0].toRFC2253String(oidMap); + } + + StringBuilder relname = new StringBuilder(); + if (!canonical) { + for (int i = 0; i < assertion.length; i++) { + if (i > 0) { + relname.append('+'); + } + relname.append(assertion[i].toRFC2253String(oidMap)); + } + } else { + // order the string type AVA's alphabetically, + // followed by the oid type AVA's numerically + List avaList = new ArrayList(assertion.length); + for (int i = 0; i < assertion.length; i++) { + avaList.add(assertion[i]); + } + Collections.sort(avaList, AVAComparator.getInstance()); + + for (int i = 0; i < avaList.size(); i++) { + if (i > 0) { + relname.append('+'); + } + relname.append(avaList.get(i).toRFC2253CanonicalString()); + } + } + return relname.toString(); + } + +} + +class AVAComparator implements Comparator { + + private static final Comparator INSTANCE = new AVAComparator(); + + private AVAComparator() { + // empty + } + + static Comparator getInstance() { + return INSTANCE; + } + + /** + * AVA's containing a standard keyword are ordered alphabetically, + * followed by AVA's containing an OID keyword, ordered numerically + */ + public int compare(AVA a1, AVA a2) { + boolean a1Has2253 = a1.hasRFC2253Keyword(); + boolean a2Has2253 = a2.hasRFC2253Keyword(); + + if (a1Has2253 == a2Has2253) { + return a1.toRFC2253CanonicalString().compareTo + (a2.toRFC2253CanonicalString()); + } else { + if (a1Has2253) { + return -1; + } else { + return 1; + } + } + } + +} diff --git a/src/sun/security/x509/README b/src/sun/security/x509/README new file mode 100644 index 00000000..31a12918 --- /dev/null +++ b/src/sun/security/x509/README @@ -0,0 +1,47 @@ + +Quick summary of the main purpose here: X.509 certs are used in public +key infrastructure for protocols such as SSL and SET. These certificates +involve ISO/CCITT standard technologies such as ASN.1/DER, which control +the format of the data being transmitted. X.509 itself describes the +content of the data (e.g. X.500 user name, public key for that user, more) +and how to sign it. + ++++ +++ +++ +++ +++ +++ +++ +++ +++ + +The X.509 support in JDK 1.2 builds on the java.security signature and +key management APIs. The following packages provide the X.509 support: + + sun.security.util ... holds DER utilities, for parsing and generating + streams of DER-encoded data values, including object identifiers. + + sun.security.x509 ... basic X.509 certificate parsing and generation + framework, including X.509 keys, X.500 names, algorithm IDs, + X.509 v3 extensions, and more. + ++++ +++ +++ +++ +++ +++ +++ +++ +++ + +Information which may be useful when you work with X.509 certificates is +found in: + + The IETF has a public key infrastructure working group, PKIX. + See http://www.ietf.org for more information. + + RFC 1422, which describes the key management infrastructure for + the Privacy Enhanced Mail (PEM) system. It builds on X.509, + and is perhaps the most useful overview I've found. + + RFC 1777, which describes the Lightweight Directory Access + Protocol (LDAP) that many organizations are expecting will help + address online certificate distribution over the Internet. + + RFC 3280, which describes the Internet X.509 Public Key + Infrastructure Certificate and CRL Profile. + + RSA DSI has a bunch of "Public Key Cryptography Standards" (PKCS) which + have been relatively well accepted. They build on top of the X.509 + infrastructure. You can FTP them from ftp://ftp.rsa.com/pub/pkcs, in + either PostScript or ASCII format. + + RSA DSI has also provided a "Layman's Guide" to ASN.1/DER, with + examples from the X.509 and PKCS standards. This is available from + the PKCS FTP area noted above. diff --git a/src/sun/security/x509/RFC822Name.java b/src/sun/security/x509/RFC822Name.java new file mode 100644 index 00000000..e905ae1b --- /dev/null +++ b/src/sun/security/x509/RFC822Name.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.Locale; + +import sun.security.util.*; + +/** + * This class implements the RFC822Name as required by the GeneralNames + * ASN.1 object. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class RFC822Name implements GeneralNameInterface +{ + private String name; + + /** + * Create the RFC822Name object from the passed encoded Der value. + * + * @param derValue the encoded DER RFC822Name. + * @exception IOException on error. + */ + public RFC822Name(DerValue derValue) throws IOException { + name = derValue.getIA5String(); + parseName(name); + } + + /** + * Create the RFC822Name object with the specified name. + * + * @param name the RFC822Name. + * @throws IOException on invalid input name + */ + public RFC822Name(String name) throws IOException { + parseName(name); + this.name = name; + } + + /** + * Parse an RFC822Name string to see if it is a valid + * addr-spec according to IETF RFC822 and RFC2459: + * [local-part@]domain + *

      + * local-part@ could be empty for an RFC822Name NameConstraint, + * but the domain at least must be non-empty. Case is not + * significant. + * + * @param name the RFC822Name string + * @throws IOException if name is not valid + */ + public void parseName(String name) throws IOException { + if (name == null || name.length() == 0) { + throw new IOException("RFC822Name may not be null or empty"); + } + // See if domain is a valid domain name + String domain = name.substring(name.indexOf('@')+1); + if (domain.length() == 0) { + throw new IOException("RFC822Name may not end with @"); + } else { + //An RFC822 NameConstraint could start with a ., although + //a DNSName may not + if (domain.startsWith(".")) { + if (domain.length() == 1) + throw new IOException("RFC822Name domain may not be just ."); + } + } + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_RFC822); + } + + /** + * Return the actual name value of the GeneralName. + */ + public String getName() { + return name; + } + + /** + * Encode the RFC822 name into the DerOutputStream. + * + * @param out the DER stream to encode the RFC822Name to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(name); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return ("RFC822Name: " + name); + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are equivalent + * according to RFC2459. + */ + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (!(obj instanceof RFC822Name)) + return false; + + RFC822Name other = (RFC822Name)obj; + + // RFC2459 mandates that these names are + // not case-sensitive + return name.equalsIgnoreCase(other.name); + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + public int hashCode() { + return name.toUpperCase(Locale.ENGLISH).hashCode(); + } + + /** + * Return constraint type:

        + *
      • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain) + *
      • NAME_MATCH = 0: input name matches name + *
      • NAME_NARROWS = 1: input name narrows name + *
      • NAME_WIDENS = 2: input name widens name + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type + *
      . These results are used in checking NameConstraints during + * certification path verification. + *

      + * [RFC2459] When the subjectAltName extension contains an Internet mail address, + * the address MUST be included as an rfc822Name. The format of an + * rfc822Name is an "addr-spec" as defined in RFC 822 [RFC 822]. An + * addr-spec has the form "local-part@domain". Note that an addr-spec + * has no phrase (such as a common name) before it, has no comment (text + * surrounded in parentheses) after it, and is not surrounded by "<" and + * ">". Note that while upper and lower case letters are allowed in an + * RFC 822 addr-spec, no significance is attached to the case. + *

      + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but narrowing and widening are + * not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != (GeneralNameInterface.NAME_RFC822)) { + constraintType = NAME_DIFF_TYPE; + } else { + //RFC2459 specifies that case is not significant in RFC822Names + String inName = + (((RFC822Name)inputName).getName()).toLowerCase(Locale.ENGLISH); + String thisName = name.toLowerCase(Locale.ENGLISH); + if (inName.equals(thisName)) { + constraintType = NAME_MATCH; + } else if (thisName.endsWith(inName)) { + /* if both names contain @, then they had to match exactly */ + if (inName.indexOf('@') != -1) { + constraintType = NAME_SAME_TYPE; + } else if (inName.startsWith(".")) { + constraintType = NAME_WIDENS; + } else { + int inNdx = thisName.lastIndexOf(inName); + if (thisName.charAt(inNdx-1) == '@' ) { + constraintType = NAME_WIDENS; + } else { + constraintType = NAME_SAME_TYPE; + } + } + } else if (inName.endsWith(thisName)) { + /* if thisName contains @, then they had to match exactly */ + if (thisName.indexOf('@') != -1) { + constraintType = NAME_SAME_TYPE; + } else if (thisName.startsWith(".")) { + constraintType = NAME_NARROWS; + } else { + int ndx = inName.lastIndexOf(thisName); + if (inName.charAt(ndx-1) == '@') { + constraintType = NAME_NARROWS; + } else { + constraintType = NAME_SAME_TYPE; + } + } + } else { + constraintType = NAME_SAME_TYPE; + } + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + String subtree=name; + int i=1; + + /* strip off name@ portion */ + int atNdx = subtree.lastIndexOf('@'); + if (atNdx >= 0) { + i++; + subtree=subtree.substring(atNdx+1); + } + + /* count dots in dnsname, adding one if dnsname preceded by @ */ + for (; subtree.lastIndexOf('.') >= 0; i++) { + subtree=subtree.substring(0,subtree.lastIndexOf('.')); + } + + return i; + } +} diff --git a/src/sun/security/x509/ReasonFlags.java b/src/sun/security/x509/ReasonFlags.java new file mode 100644 index 00000000..206b5dac --- /dev/null +++ b/src/sun/security/x509/ReasonFlags.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * Represent the CRL Reason Flags. + * + *

      This extension, if present, defines the identifies + * the reason for the certificate revocation. + *

      The ASN.1 syntax for this is: + *

      + * ReasonFlags ::= BIT STRING {
      + *    unused                  (0),
      + *    keyCompromise           (1),
      + *    cACompromise            (2),
      + *    affiliationChanged      (3),
      + *    superseded              (4),
      + *    cessationOfOperation    (5),
      + *    certificateHold         (6),
      + *    privilegeWithdrawn      (7),
      + *    aACompromise            (8) }
      + * 
      + * + * @author Hemma Prafullchandra + */ +public class ReasonFlags { + + /** + * Reasons + */ + public static final String UNUSED = "unused"; + public static final String KEY_COMPROMISE = "key_compromise"; + public static final String CA_COMPROMISE = "ca_compromise"; + public static final String AFFILIATION_CHANGED = "affiliation_changed"; + public static final String SUPERSEDED = "superseded"; + public static final String CESSATION_OF_OPERATION + = "cessation_of_operation"; + public static final String CERTIFICATE_HOLD = "certificate_hold"; + public static final String PRIVILEGE_WITHDRAWN = "privilege_withdrawn"; + public static final String AA_COMPROMISE = "aa_compromise"; + + private final static String[] NAMES = { + UNUSED, + KEY_COMPROMISE, + CA_COMPROMISE, + AFFILIATION_CHANGED, + SUPERSEDED, + CESSATION_OF_OPERATION, + CERTIFICATE_HOLD, + PRIVILEGE_WITHDRAWN, + AA_COMPROMISE, + }; + + private static int name2Index(String name) throws IOException { + for( int i=0; i= bitString.length) { + boolean[] tmp = new boolean[position+1]; + System.arraycopy(bitString, 0, tmp, 0, bitString.length); + bitString = tmp; + } + bitString[position] = val; + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(byte[] reasons) { + bitString = new BitArray(reasons.length*8, reasons).toBooleanArray(); + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(boolean[] reasons) { + this.bitString = reasons; + } + + /** + * Create a ReasonFlags with the passed bit settings. + * + * @param reasons the bits to be set for the ReasonFlags. + */ + public ReasonFlags(BitArray reasons) { + this.bitString = reasons.toBooleanArray(); + } + + /** + * Create the object from the passed DER encoded value. + * + * @param in the DerInputStream to read the ReasonFlags from. + * @exception IOException on decoding errors. + */ + public ReasonFlags(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); + } + + /** + * Create the object from the passed DER encoded value. + * + * @param derVal the DerValue decoded from the stream. + * @exception IOException on decoding errors. + */ + public ReasonFlags(DerValue derVal) throws IOException { + this.bitString = derVal.getUnalignedBitString(true).toBooleanArray(); + } + + /** + * Returns the reason flags as a boolean array. + */ + public boolean[] getFlags() { + return bitString; + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (!(obj instanceof Boolean)) { + throw new IOException("Attribute must be of type Boolean."); + } + boolean val = ((Boolean)obj).booleanValue(); + set(name2Index(name), val); + } + + /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { + return Boolean.valueOf(isSet(name2Index(name))); + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + set(name, Boolean.FALSE); + } + + /** + * Returns a printable representation of the ReasonFlags. + */ + public String toString() { + String s = "Reason Flags [\n"; + + try { + if (isSet(0)) s += " Unused\n"; + if (isSet(1)) s += " Key Compromise\n"; + if (isSet(2)) s += " CA Compromise\n"; + if (isSet(3)) s += " Affiliation_Changed\n"; + if (isSet(4)) s += " Superseded\n"; + if (isSet(5)) s += " Cessation Of Operation\n"; + if (isSet(6)) s += " Certificate Hold\n"; + if (isSet(7)) s += " Privilege Withdrawn\n"; + if (isSet(8)) s += " AA Compromise\n"; + } catch (ArrayIndexOutOfBoundsException ex) {} + + s += "]\n"; + + return (s); + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putTruncatedUnalignedBitString(new BitArray(this.bitString)); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements () { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + for( int i=0; iExtensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + *

      + * The ASN.1 syntax for this is: + *

      + * SubjectAltName ::= GeneralNames
      + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
      + * 
      + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class SubjectAlternativeNameExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.SubjectAlternativeName"; + /** + * Attribute names. + */ + public static final String NAME = "SubjectAlternativeName"; + public static final String SUBJECT_NAME = "subject_name"; + + // private data members + GeneralNames names = null; + + // Encode this extension + private void encodeThis() throws IOException { + if (names == null || names.isEmpty()) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + names.encode(os); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a SubjectAlternativeNameExtension with the passed GeneralNames. + * The extension is marked non-critical. + * + * @param names the GeneralNames for the subject. + * @exception IOException on error. + */ + public SubjectAlternativeNameExtension(GeneralNames names) + throws IOException { + this(Boolean.FALSE, names); + } + + /** + * Create a SubjectAlternativeNameExtension with the specified + * criticality and GeneralNames. + * + * @param critical true if the extension is to be treated as critical. + * @param names the GeneralNames for the subject. + * @exception IOException on error. + */ + public SubjectAlternativeNameExtension(Boolean critical, GeneralNames names) + throws IOException { + this.names = names; + this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; + this.critical = critical.booleanValue(); + encodeThis(); + } + + /** + * Create a default SubjectAlternativeNameExtension. The extension + * is marked non-critical. + */ + public SubjectAlternativeNameExtension() { + extensionId = PKIXExtensions.SubjectAlternativeName_Id; + critical = false; + names = new GeneralNames(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public SubjectAlternativeNameExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.SubjectAlternativeName_Id; + this.critical = critical.booleanValue(); + + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + if (val.data == null) { + names = new GeneralNames(); + return; + } + + names = new GeneralNames(val); + } + + /** + * Returns a printable representation of the SubjectAlternativeName. + */ + public String toString() { + + String result = super.toString() + "SubjectAlternativeName [\n"; + if(names == null) { + result += " null\n"; + } else { + for(GeneralName name: names.names()) { + result += " "+name+"\n"; + } + } + result += "]\n"; + return result; + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.SubjectAlternativeName_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + if (!(obj instanceof GeneralNames)) { + throw new IOException("Attribute value should be of " + + "type GeneralNames."); + } + names = (GeneralNames)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public GeneralNames get(String name) throws IOException { + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + return (names); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(SUBJECT_NAME)) { + names = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectAlternativeName."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(SUBJECT_NAME); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/SubjectInfoAccessExtension.java b/src/sun/security/x509/SubjectInfoAccessExtension.java new file mode 100644 index 00000000..2f851f7d --- /dev/null +++ b/src/sun/security/x509/SubjectInfoAccessExtension.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.*; + +import sun.security.util.DerOutputStream; +import sun.security.util.DerValue; + +/** + * The Subject Information Access Extension (OID = 1.3.6.1.5.5.7.1.11). + *

      + * The subject information access extension indicates how to access + * information and services for the subject of the certificate in which + * the extension appears. When the subject is a CA, information and + * services may include certificate validation services and CA policy + * data. When the subject is an end entity, the information describes + * the type of services offered and how to access them. In this case, + * the contents of this extension are defined in the protocol + * specifications for the supported services. This extension may be + * included in end entity or CA certificates. Conforming CAs MUST mark + * this extension as non-critical. + *

      + * This extension is defined in + * Internet X.509 PKI Certificate and Certificate Revocation List + * (CRL) Profile. The profile permits + * the extension to be included in end-entity or CA certificates, + * and it must be marked as non-critical. Its ASN.1 definition is as follows: + *

      + *   id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 }
      + *
      + *   SubjectInfoAccessSyntax  ::=
      + *          SEQUENCE SIZE (1..MAX) OF AccessDescription
      + *
      + *   AccessDescription  ::=  SEQUENCE {
      + *          accessMethod          OBJECT IDENTIFIER,
      + *          accessLocation        GeneralName  }
      + * 
      + *

      + * @see Extension + * @see CertAttrSet + */ + +public class SubjectInfoAccessExtension extends Extension + implements CertAttrSet { + + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.SubjectInfoAccess"; + + /** + * Attribute name. + */ + public static final String NAME = "SubjectInfoAccess"; + public static final String DESCRIPTIONS = "descriptions"; + + /** + * The List of AccessDescription objects. + */ + private List accessDescriptions; + + /** + * Create an SubjectInfoAccessExtension from a List of + * AccessDescription; the criticality is set to false. + * + * @param accessDescriptions the List of AccessDescription + * @throws IOException on error + */ + public SubjectInfoAccessExtension( + List accessDescriptions) throws IOException { + this.extensionId = PKIXExtensions.SubjectInfoAccess_Id; + this.critical = false; + this.accessDescriptions = accessDescriptions; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value of the same. + * + * @param critical true if the extension is to be treated as critical. + * @param value Array of DER encoded bytes of the actual value. + * @exception IOException on error. + */ + public SubjectInfoAccessExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.SubjectInfoAccess_Id; + this.critical = critical.booleanValue(); + + if (!(value instanceof byte[])) { + throw new IOException("Illegal argument type"); + } + + extensionValue = (byte[])value; + DerValue val = new DerValue(extensionValue); + if (val.tag != DerValue.tag_Sequence) { + throw new IOException("Invalid encoding for " + + "SubjectInfoAccessExtension."); + } + accessDescriptions = new ArrayList(); + while (val.data.available() != 0) { + DerValue seq = val.data.getDerValue(); + AccessDescription accessDescription = new AccessDescription(seq); + accessDescriptions.add(accessDescription); + } + } + + /** + * Return the list of AccessDescription objects. + */ + public List getAccessDescriptions() { + return accessDescriptions; + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return NAME; + } + + /** + * Write the extension to the DerOutputStream. + * + * @param out the DerOutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (this.extensionValue == null) { + this.extensionId = PKIXExtensions.SubjectInfoAccess_Id; + this.critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + @SuppressWarnings("unchecked") // Checked with instanceof + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + if (!(obj instanceof List)) { + throw new IOException("Attribute value should be of type List."); + } + accessDescriptions = (List)obj; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:SubjectInfoAccessExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public List get(String name) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + return accessDescriptions; + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:SubjectInfoAccessExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(DESCRIPTIONS)) { + accessDescriptions = new ArrayList(); + } else { + throw new IOException("Attribute name [" + name + + "] not recognized by " + + "CertAttrSet:SubjectInfoAccessExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(DESCRIPTIONS); + return elements.elements(); + } + + // Encode this extension value + private void encodeThis() throws IOException { + if (accessDescriptions.isEmpty()) { + this.extensionValue = null; + } else { + DerOutputStream ads = new DerOutputStream(); + for (AccessDescription accessDescription : accessDescriptions) { + accessDescription.encode(ads); + } + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, ads); + this.extensionValue = seq.toByteArray(); + } + } + + /** + * Return the extension as user readable string. + */ + public String toString() { + return super.toString() + "SubjectInfoAccess [\n " + + accessDescriptions + "\n]\n"; + } + +} diff --git a/src/sun/security/x509/SubjectKeyIdentifierExtension.java b/src/sun/security/x509/SubjectKeyIdentifierExtension.java new file mode 100644 index 00000000..79482c9b --- /dev/null +++ b/src/sun/security/x509/SubjectKeyIdentifierExtension.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import sun.security.util.*; + +/** + * Represent the Subject Key Identifier Extension. + * + * This extension, if present, provides a means of identifying the particular + * public key used in an application. This extension by default is marked + * non-critical. + * + *

      Extensions are addiitonal attributes which can be inserted in a X509 + * v3 certificate. For example a "Driving License Certificate" could have + * the driving license number as a extension. + * + *

      Extensions are represented as a sequence of the extension identifier + * (Object Identifier), a boolean flag stating whether the extension is to + * be treated as being critical and the extension value itself (this is again + * a DER encoding of the extension value). + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see Extension + * @see CertAttrSet + */ +public class SubjectKeyIdentifierExtension extends Extension +implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = + "x509.info.extensions.SubjectKeyIdentifier"; + /** + * Attribute names. + */ + public static final String NAME = "SubjectKeyIdentifier"; + public static final String KEY_ID = "key_id"; + + // Private data member + private KeyIdentifier id = null; + + // Encode this extension value + private void encodeThis() throws IOException { + if (id == null) { + this.extensionValue = null; + return; + } + DerOutputStream os = new DerOutputStream(); + id.encode(os); + this.extensionValue = os.toByteArray(); + } + + /** + * Create a SubjectKeyIdentifierExtension with the passed octet string. + * The criticality is set to False. + * @param octetString the octet string identifying the key identifier. + */ + public SubjectKeyIdentifierExtension(byte[] octetString) + throws IOException { + id = new KeyIdentifier(octetString); + + this.extensionId = PKIXExtensions.SubjectKey_Id; + this.critical = false; + encodeThis(); + } + + /** + * Create the extension from the passed DER encoded value. + * + * @param critical true if the extension is to be treated as critical. + * @param value an array of DER encoded bytes of the actual value. + * @exception ClassCastException if value is not an array of bytes + * @exception IOException on error. + */ + public SubjectKeyIdentifierExtension(Boolean critical, Object value) + throws IOException { + this.extensionId = PKIXExtensions.SubjectKey_Id; + this.critical = critical.booleanValue(); + this.extensionValue = (byte[]) value; + DerValue val = new DerValue(this.extensionValue); + this.id = new KeyIdentifier(val); + } + + /** + * Returns a printable representation. + */ + public String toString() { + return super.toString() + "SubjectKeyIdentifier [\n" + + String.valueOf(id) + "]\n"; + } + + /** + * Write the extension to the OutputStream. + * + * @param out the OutputStream to write the extension to. + * @exception IOException on encoding errors. + */ + public void encode(OutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + if (extensionValue == null) { + extensionId = PKIXExtensions.SubjectKey_Id; + critical = false; + encodeThis(); + } + super.encode(tmp); + out.write(tmp.toByteArray()); + } + + /** + * Set the attribute value. + */ + public void set(String name, Object obj) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + if (!(obj instanceof KeyIdentifier)) { + throw new IOException("Attribute value should be of" + + " type KeyIdentifier."); + } + id = (KeyIdentifier)obj; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + encodeThis(); + } + + /** + * Get the attribute value. + */ + public KeyIdentifier get(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + return (id); + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + } + + /** + * Delete the attribute value. + */ + public void delete(String name) throws IOException { + if (name.equalsIgnoreCase(KEY_ID)) { + id = null; + } else { + throw new IOException("Attribute name not recognized by " + + "CertAttrSet:SubjectKeyIdentifierExtension."); + } + encodeThis(); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(KEY_ID); + + return (elements.elements()); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return (NAME); + } +} diff --git a/src/sun/security/x509/URIName.java b/src/sun/security/x509/URIName.java new file mode 100644 index 00000000..034117e2 --- /dev/null +++ b/src/sun/security/x509/URIName.java @@ -0,0 +1,392 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import sun.security.util.*; + +/** + * This class implements the URIName as required by the GeneralNames + * ASN.1 object. + *

      + * [RFC3280] When the subjectAltName extension contains a URI, the name MUST be + * stored in the uniformResourceIdentifier (an IA5String). The name MUST + * be a non-relative URL, and MUST follow the URL syntax and encoding + * rules specified in [RFC 1738]. The name must include both a scheme + * (e.g., "http" or "ftp") and a scheme-specific-part. The scheme- + * specific-part must include a fully qualified domain name or IP + * address as the host. + *

      + * As specified in [RFC 1738], the scheme name is not case-sensitive + * (e.g., "http" is equivalent to "HTTP"). The host part is also not + * case-sensitive, but other components of the scheme-specific-part may + * be case-sensitive. When comparing URIs, conforming implementations + * MUST compare the scheme and host without regard to case, but assume + * the remainder of the scheme-specific-part is case sensitive. + *

      + * [RFC1738] In general, URLs are written as follows: + *

      + * :
      + * 
      + * A URL contains the name of the scheme being used () followed + * by a colon and then a string (the ) whose + * interpretation depends on the scheme. + *

      + * While the syntax for the rest of the URL may vary depending on the + * particular scheme selected, URL schemes that involve the direct use + * of an IP-based protocol to a specified host on the Internet use a + * common syntax for the scheme-specific data: + *

      + * //:@:/
      + * 
      + * [RFC2732] specifies that an IPv6 address contained inside a URL + * must be enclosed in square brackets (to allow distinguishing the + * colons that separate IPv6 components from the colons that separate + * scheme-specific data. + *

      + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @author Sean Mullan + * @author Steve Hanna + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class URIName implements GeneralNameInterface { + + // private attributes + private URI uri; + private String host; + private DNSName hostDNS; + private IPAddressName hostIP; + + /** + * Create the URIName object from the passed encoded Der value. + * + * @param derValue the encoded DER URIName. + * @exception IOException on error. + */ + public URIName(DerValue derValue) throws IOException { + this(derValue.getIA5String()); + } + + /** + * Create the URIName object with the specified name. + * + * @param name the URIName. + * @throws IOException if name is not a proper URIName + */ + public URIName(String name) throws IOException { + try { + uri = new URI(name); + } catch (URISyntaxException use) { + throw new IOException("invalid URI name:" + name, use); + } + if (uri.getScheme() == null) { + throw new IOException("URI name must include scheme:" + name); + } + + host = uri.getHost(); + // RFC 3280 says that the host should be non-null, but we allow it to + // be null because some widely deployed certificates contain CDP + // extensions with URIs that have no hostname (see bugs 4802236 and + // 5107944). + if (host != null) { + if (host.charAt(0) == '[') { + // Verify host is a valid IPv6 address name + String ipV6Host = host.substring(1, host.length()-1); + try { + hostIP = new IPAddressName(ipV6Host); + } catch (IOException ioe) { + throw new IOException("invalid URI name (host " + + "portion is not a valid IPv6 address):" + name); + } + } else { + try { + hostDNS = new DNSName(host); + } catch (IOException ioe) { + // Not a valid DNS Name; see if it is a valid IPv4 + // IPAddressName + try { + hostIP = new IPAddressName(host); + } catch (Exception ioe2) { + throw new IOException("invalid URI name (host " + + "portion is not a valid DNS name, IPv4 address," + + " or IPv6 address):" + name); + } + } + } + } + } + + /** + * Create the URIName object with the specified name constraint. URI + * name constraints syntax is different than SubjectAltNames, etc. See + * 4.2.1.11 of RFC 3280. + * + * @param value the URI name constraint + * @throws IOException if name is not a proper URI name constraint + */ + public static URIName nameConstraint(DerValue value) throws IOException { + URI uri; + String name = value.getIA5String(); + try { + uri = new URI(name); + } catch (URISyntaxException use) { + throw new IOException("invalid URI name constraint:" + name, use); + } + if (uri.getScheme() == null) { + String host = uri.getSchemeSpecificPart(); + try { + DNSName hostDNS; + if (host.charAt(0) == '.') { + hostDNS = new DNSName(host.substring(1)); + } else { + hostDNS = new DNSName(host); + } + return new URIName(uri, host, hostDNS); + } catch (IOException ioe) { + throw new IOException("invalid URI name constraint:" + name, ioe); + } + } else { + throw new IOException("invalid URI name constraint (should not " + + "include scheme):" + name); + } + } + + URIName(URI uri, String host, DNSName hostDNS) { + this.uri = uri; + this.host = host; + this.hostDNS = hostDNS; + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return GeneralNameInterface.NAME_URI; + } + + /** + * Encode the URI name into the DerOutputStream. + * + * @param out the DER stream to encode the URIName to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + out.putIA5String(uri.toASCIIString()); + } + + /** + * Convert the name into user readable string. + */ + public String toString() { + return "URIName: " + uri.toString(); + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are equivalent according to RFC2459. + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof URIName)) { + return false; + } + + URIName other = (URIName) obj; + + return uri.equals(other.getURI()); + } + + /** + * Returns the URIName as a java.net.URI object + */ + public URI getURI() { + return uri; + } + + /** + * Returns this URI name. + */ + public String getName() { + return uri.toString(); + } + + /** + * Return the scheme name portion of a URIName + * + * @returns scheme portion of full name + */ + public String getScheme() { + return uri.getScheme(); + } + + /** + * Return the host name or IP address portion of the URIName + * + * @returns host name or IP address portion of full name + */ + public String getHost() { + return host; + } + + /** + * Return the host object type; if host name is a + * DNSName, then this host object does not include any + * initial "." on the name. + * + * @returns host name as DNSName or IPAddressName + */ + public Object getHostObject() { + if (hostIP != null) { + return hostIP; + } else { + return hostDNS; + } + } + + /** + * Returns the hash code value for this object. + * + * @return a hash code value for this object. + */ + public int hashCode() { + return uri.hashCode(); + } + + /** + * Return type of constraint inputName places on this name:

        + *
      • NAME_DIFF_TYPE = -1: input name is different type from name + * (i.e. does not constrain). + *
      • NAME_MATCH = 0: input name matches name. + *
      • NAME_NARROWS = 1: input name narrows name (is lower in the naming + * subtree) + *
      • NAME_WIDENS = 2: input name widens name (is higher in the naming + * subtree) + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, but + * is same type. + *
      . + * These results are used in checking NameConstraints during + * certification path verification. + *

      + * RFC3280: For URIs, the constraint applies to the host part of the name. + * The constraint may specify a host or a domain. Examples would be + * "foo.bar.com"; and ".xyz.com". When the the constraint begins with + * a period, it may be expanded with one or more subdomains. That is, + * the constraint ".xyz.com" is satisfied by both abc.xyz.com and + * abc.def.xyz.com. However, the constraint ".xyz.com" is not satisfied + * by "xyz.com". When the constraint does not begin with a period, it + * specifies a host. + *

      + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but + * narrowing and widening are not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) + throws UnsupportedOperationException { + int constraintType; + if (inputName == null) { + constraintType = NAME_DIFF_TYPE; + } else if (inputName.getType() != NAME_URI) { + constraintType = NAME_DIFF_TYPE; + } else { + // Assuming from here on that one or both of these is + // actually a URI name constraint (not a URI), so we + // only need to compare the host portion of the name + + String otherHost = ((URIName)inputName).getHost(); + + // Quick check for equality + if (otherHost.equalsIgnoreCase(host)) { + constraintType = NAME_MATCH; + } else { + Object otherHostObject = ((URIName)inputName).getHostObject(); + + if ((hostDNS == null) || + !(otherHostObject instanceof DNSName)) { + // If one (or both) is an IP address, only same type + constraintType = NAME_SAME_TYPE; + } else { + // Both host portions are DNS names. Are they domains? + boolean thisDomain = (host.charAt(0) == '.'); + boolean otherDomain = (otherHost.charAt(0) == '.'); + DNSName otherDNS = (DNSName) otherHostObject; + + // Run DNSName.constrains. + constraintType = hostDNS.constrains(otherDNS); + // If neither one is a domain, then they can't + // widen or narrow. That's just SAME_TYPE. + if ((!thisDomain && !otherDomain) && + ((constraintType == NAME_WIDENS) || + (constraintType == NAME_NARROWS))) { + constraintType = NAME_SAME_TYPE; + } + + // If one is a domain and the other isn't, + // then they can't match. The one that's a + // domain doesn't include the one that's + // not a domain. + if ((thisDomain != otherDomain) && + (constraintType == NAME_MATCH)) { + if (thisDomain) { + constraintType = NAME_WIDENS; + } else { + constraintType = NAME_NARROWS; + } + } + } + } + } + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + DNSName dnsName = null; + try { + dnsName = new DNSName(host); + } catch (IOException ioe) { + throw new UnsupportedOperationException(ioe.getMessage()); + } + return dnsName.subtreeDepth(); + } +} diff --git a/src/sun/security/x509/UniqueIdentity.java b/src/sun/security/x509/UniqueIdentity.java new file mode 100644 index 00000000..237f8471 --- /dev/null +++ b/src/sun/security/x509/UniqueIdentity.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.security.x509; + +import java.io.IOException; + +import sun.security.util.*; + +/** + * This class defines the UniqueIdentity class used by certificates. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class UniqueIdentity { + // Private data members + private BitArray id; + + /** + * The default constructor for this class. + * + * @param id the byte array containing the unique identifier. + */ + public UniqueIdentity(BitArray id) { + this.id = id; + } + + /** + * The default constructor for this class. + * + * @param id the byte array containing the unique identifier. + */ + public UniqueIdentity(byte[] id) { + this.id = new BitArray(id.length*8, id); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param in the DerInputStream to read the UniqueIdentity from. + * @exception IOException on decoding errors. + */ + public UniqueIdentity(DerInputStream in) throws IOException { + DerValue derVal = in.getDerValue(); + id = derVal.getUnalignedBitString(true); + } + + /** + * Create the object, decoding the values from the passed DER stream. + * + * @param derVal the DerValue decoded from the stream. + * @param tag the tag the value is encoded under. + * @exception IOException on decoding errors. + */ + public UniqueIdentity(DerValue derVal) throws IOException { + id = derVal.getUnalignedBitString(true); + } + + /** + * Return the UniqueIdentity as a printable string. + */ + public String toString() { + return ("UniqueIdentity:" + id.toString() + "\n"); + } + + /** + * Encode the UniqueIdentity in DER form to the stream. + * + * @param out the DerOutputStream to marshal the contents to. + * @param tag enocode it under the following tag. + * @exception IOException on errors. + */ + public void encode(DerOutputStream out, byte tag) throws IOException { + byte[] bytes = id.toByteArray(); + int excessBits = bytes.length*8 - id.length(); + + out.write(tag); + out.putLength(bytes.length + 1); + + out.write(excessBits); + out.write(bytes); + } + + /** + * Return the unique id. + */ + public boolean[] getId() { + if (id == null) return null; + + return id.toBooleanArray(); + } +} diff --git a/src/sun/security/x509/X400Address.java b/src/sun/security/x509/X400Address.java new file mode 100644 index 00000000..bfa8f945 --- /dev/null +++ b/src/sun/security/x509/X400Address.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import sun.security.util.DerValue; +import sun.security.util.DerOutputStream; + +/** + * This class defines the X400Address of the GeneralName choice. + *

      + * The ASN.1 syntax for this is: + *

      + * ORAddress ::= SEQUENCE {
      + *    built-in-standard-attributes BuiltInStandardAttributes,
      + *    built-in-domain-defined-attributes
      + *                         BuiltInDomainDefinedAttributes OPTIONAL,
      + *    -- see also teletex-domain-defined-attributes
      + *    extension-attributes ExtensionAttributes OPTIONAL }
      + * --      The OR-address is semantically absent from the OR-name if the
      + * --      built-in-standard-attribute sequence is empty and the
      + * --      built-in-domain-defined-attributes and extension-attributes are
      + * --      both omitted.
      + *
      + * --      Built-in Standard Attributes
      + *
      + * BuiltInStandardAttributes ::= SEQUENCE {
      + *    country-name CountryName OPTIONAL,
      + *    administration-domain-name AdministrationDomainName OPTIONAL,
      + *    network-address      [0] NetworkAddress OPTIONAL,
      + *    -- see also extended-network-address
      + *    terminal-identifier  [1] TerminalIdentifier OPTIONAL,
      + *    private-domain-name  [2] PrivateDomainName OPTIONAL,
      + *    organization-name    [3] OrganizationName OPTIONAL,
      + *    -- see also teletex-organization-name
      + *    numeric-user-identifier      [4] NumericUserIdentifier OPTIONAL,
      + *    personal-name        [5] PersonalName OPTIONAL,
      + *    -- see also teletex-personal-name
      + *    organizational-unit-names    [6] OrganizationalUnitNames OPTIONAL
      + *    -- see also teletex-organizational-unit-names -- }
      + *
      + * CountryName ::= [APPLICATION 1] CHOICE {
      + *    x121-dcc-code NumericString
      + *                 (SIZE (ub-country-name-numeric-length)),
      + *    iso-3166-alpha2-code PrintableString
      + *                 (SIZE (ub-country-name-alpha-length)) }
      + *
      + * AdministrationDomainName ::= [APPLICATION 2] CHOICE {
      + *    numeric NumericString (SIZE (0..ub-domain-name-length)),
      + *    printable PrintableString (SIZE (0..ub-domain-name-length)) }
      + *
      + * NetworkAddress ::= X121Address  -- see also extended-network-address
      + *
      + * X121Address ::= NumericString (SIZE (1..ub-x121-address-length))
      + *
      + * TerminalIdentifier ::= PrintableString (SIZE (1..ub-terminal-id-length))
      + *
      + * PrivateDomainName ::= CHOICE {
      + *    numeric NumericString (SIZE (1..ub-domain-name-length)),
      + *    printable PrintableString (SIZE (1..ub-domain-name-length)) }
      + *
      + * OrganizationName ::= PrintableString
      + *                             (SIZE (1..ub-organization-name-length))
      + * -- see also teletex-organization-name
      + *
      + * NumericUserIdentifier ::= NumericString
      + *                             (SIZE (1..ub-numeric-user-id-length))
      + *
      + * PersonalName ::= SET {
      + *    surname [0] PrintableString (SIZE (1..ub-surname-length)),
      + *    given-name [1] PrintableString
      + *                         (SIZE (1..ub-given-name-length)) OPTIONAL,
      + *    initials [2] PrintableString (SIZE (1..ub-initials-length)) OPTIONAL,
      + *    generation-qualifier [3] PrintableString
      + *                 (SIZE (1..ub-generation-qualifier-length)) OPTIONAL }
      + * -- see also teletex-personal-name
      + *
      + * OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units)
      + *                                         OF OrganizationalUnitName
      + * -- see also teletex-organizational-unit-names
      + *
      + * OrganizationalUnitName ::= PrintableString (SIZE
      + *                         (1..ub-organizational-unit-name-length))
      + *
      + * --      Built-in Domain-defined Attributes
      + *
      + * BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE
      + *                                 (1..ub-domain-defined-attributes) OF
      + *                                 BuiltInDomainDefinedAttribute
      + *
      + * BuiltInDomainDefinedAttribute ::= SEQUENCE {
      + *    type PrintableString (SIZE
      + *                         (1..ub-domain-defined-attribute-type-length)),
      + *    value PrintableString (SIZE
      + *                         (1..ub-domain-defined-attribute-value-length))}
      + *
      + * --      Extension Attributes
      + *
      + * ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF
      + *                         ExtensionAttribute
      + *
      + * ExtensionAttribute ::=  SEQUENCE {
      + *    extension-attribute-type [0] INTEGER (0..ub-extension-attributes),
      + *    extension-attribute-value [1]
      + *                         ANY DEFINED BY extension-attribute-type }
      + *
      + * -- Extension types and attribute values
      + * --
      + *
      + * common-name INTEGER ::= 1
      + *
      + * CommonName ::= PrintableString (SIZE (1..ub-common-name-length))
      + *
      + * teletex-common-name INTEGER ::= 2
      + *
      + * TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length))
      + *
      + * teletex-organization-name INTEGER ::= 3
      + *
      + * TeletexOrganizationName ::=
      + *                 TeletexString (SIZE (1..ub-organization-name-length))
      + *
      + * teletex-personal-name INTEGER ::= 4
      + *
      + * TeletexPersonalName ::= SET {
      + *    surname [0] TeletexString (SIZE (1..ub-surname-length)),
      + *    given-name [1] TeletexString
      + *                 (SIZE (1..ub-given-name-length)) OPTIONAL,
      + *    initials [2] TeletexString (SIZE (1..ub-initials-length)) OPTIONAL,
      + *    generation-qualifier [3] TeletexString (SIZE
      + *                 (1..ub-generation-qualifier-length)) OPTIONAL }
      + *
      + * teletex-organizational-unit-names INTEGER ::= 5
      + *
      + * TeletexOrganizationalUnitNames ::= SEQUENCE SIZE
      + *         (1..ub-organizational-units) OF TeletexOrganizationalUnitName
      + *
      + * TeletexOrganizationalUnitName ::= TeletexString
      + *                         (SIZE (1..ub-organizational-unit-name-length))
      + *
      + * pds-name INTEGER ::= 7
      + *
      + * PDSName ::= PrintableString (SIZE (1..ub-pds-name-length))
      + *
      + * physical-delivery-country-name INTEGER ::= 8
      + *
      + * PhysicalDeliveryCountryName ::= CHOICE {
      + *    x121-dcc-code NumericString (SIZE (ub-country-name-numeric-length)),
      + *    iso-3166-alpha2-code PrintableString
      + *                         (SIZE (ub-country-name-alpha-length)) }
      + *
      + * postal-code INTEGER ::= 9
      + *
      + * PostalCode ::= CHOICE {
      + *    numeric-code NumericString (SIZE (1..ub-postal-code-length)),
      + *    printable-code PrintableString (SIZE (1..ub-postal-code-length)) }
      + *
      + * physical-delivery-office-name INTEGER ::= 10
      + *
      + * PhysicalDeliveryOfficeName ::= PDSParameter
      + *
      + * physical-delivery-office-number INTEGER ::= 11
      + *
      + * PhysicalDeliveryOfficeNumber ::= PDSParameter
      + *
      + * extension-OR-address-components INTEGER ::= 12
      + *
      + * ExtensionORAddressComponents ::= PDSParameter
      + *
      + * physical-delivery-personal-name INTEGER ::= 13
      + *
      + * PhysicalDeliveryPersonalName ::= PDSParameter
      + *
      + * physical-delivery-organization-name INTEGER ::= 14
      + *
      + * PhysicalDeliveryOrganizationName ::= PDSParameter
      + *
      + * extension-physical-delivery-address-components INTEGER ::= 15
      + *
      + * ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter
      + *
      + * unformatted-postal-address INTEGER ::= 16
      + *
      + * UnformattedPostalAddress ::= SET {
      + *    printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines) OF
      + *            PrintableString (SIZE (1..ub-pds-parameter-length)) OPTIONAL,
      + *    teletex-string TeletexString
      + *          (SIZE (1..ub-unformatted-address-length)) OPTIONAL }
      + *
      + * street-address INTEGER ::= 17
      + *
      + * StreetAddress ::= PDSParameter
      + *
      + * post-office-box-address INTEGER ::= 18
      + *
      + * PostOfficeBoxAddress ::= PDSParameter
      + *
      + * poste-restante-address INTEGER ::= 19
      + *
      + * PosteRestanteAddress ::= PDSParameter
      + *
      + * unique-postal-name INTEGER ::= 20
      + *
      + * UniquePostalName ::= PDSParameter
      + *
      + * local-postal-attributes INTEGER ::= 21
      + *
      + * LocalPostalAttributes ::= PDSParameter
      + *
      + * PDSParameter ::= SET {
      + *    printable-string PrintableString
      + *                 (SIZE(1..ub-pds-parameter-length)) OPTIONAL,
      + *    teletex-string TeletexString
      + *                 (SIZE(1..ub-pds-parameter-length)) OPTIONAL }
      + *
      + * extended-network-address INTEGER ::= 22
      + *
      + * ExtendedNetworkAddress ::= CHOICE {
      + *    e163-4-address SEQUENCE {
      + *         number [0] NumericString (SIZE (1..ub-e163-4-number-length)),
      + *         sub-address [1] NumericString
      + *                 (SIZE (1..ub-e163-4-sub-address-length)) OPTIONAL },
      + *    psap-address [0] PresentationAddress }
      + *
      + * PresentationAddress ::= SEQUENCE {
      + *         pSelector       [0] EXPLICIT OCTET STRING OPTIONAL,
      + *         sSelector       [1] EXPLICIT OCTET STRING OPTIONAL,
      + *         tSelector       [2] EXPLICIT OCTET STRING OPTIONAL,
      + *         nAddresses      [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING }
      + *
      + * terminal-type  INTEGER ::= 23
      + *
      + * TerminalType ::= INTEGER {
      + *    telex (3),
      + *    teletex (4),
      + *    g3-facsimile (5),
      + *    g4-facsimile (6),
      + *    ia5-terminal (7),
      + *    videotex (8) } (0..ub-integer-options)
      + *
      + * --      Extension Domain-defined Attributes
      + *
      + * teletex-domain-defined-attributes INTEGER ::= 6
      + *
      + * TeletexDomainDefinedAttributes ::= SEQUENCE SIZE
      + *    (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute
      + *
      + * TeletexDomainDefinedAttribute ::= SEQUENCE {
      + *         type TeletexString
      + *                (SIZE (1..ub-domain-defined-attribute-type-length)),
      + *         value TeletexString
      + *                (SIZE (1..ub-domain-defined-attribute-value-length)) }
      + *
      + * --  specifications of Upper Bounds shall be regarded as mandatory
      + * --  from Annex B of ITU-T X.411 Reference Definition of MTS Parameter
      + * --  Upper Bounds
      + *
      + * --      Upper Bounds
      + * ub-name INTEGER ::=     32768
      + * ub-common-name  INTEGER ::=     64
      + * ub-locality-name        INTEGER ::=     128
      + * ub-state-name   INTEGER ::=     128
      + * ub-organization-name    INTEGER ::=     64
      + * ub-organizational-unit-name     INTEGER ::=     64
      + * ub-title        INTEGER ::=     64
      + * ub-match        INTEGER ::=     128
      + *
      + * ub-emailaddress-length INTEGER ::= 128
      + *
      + * ub-common-name-length INTEGER ::= 64
      + * ub-country-name-alpha-length INTEGER ::= 2
      + * ub-country-name-numeric-length INTEGER ::= 3
      + * ub-domain-defined-attributes INTEGER ::= 4
      + * ub-domain-defined-attribute-type-length INTEGER ::= 8
      + * ub-domain-defined-attribute-value-length INTEGER ::= 128
      + * ub-domain-name-length INTEGER ::= 16
      + * ub-extension-attributes INTEGER ::= 256
      + * ub-e163-4-number-length INTEGER ::= 15
      + * ub-e163-4-sub-address-length INTEGER ::= 40
      + * ub-generation-qualifier-length INTEGER ::= 3
      + * ub-given-name-length INTEGER ::= 16
      + * ub-initials-length INTEGER ::= 5
      + * ub-integer-options INTEGER ::= 256
      + * ub-numeric-user-id-length INTEGER ::= 32
      + * ub-organization-name-length INTEGER ::= 64
      + * ub-organizational-unit-name-length INTEGER ::= 32
      + * ub-organizational-units INTEGER ::= 4
      + * ub-pds-name-length INTEGER ::= 16
      + * ub-pds-parameter-length INTEGER ::= 30
      + * ub-pds-physical-address-lines INTEGER ::= 6
      + * ub-postal-code-length INTEGER ::= 16
      + * ub-surname-length INTEGER ::= 40
      + * ub-terminal-id-length INTEGER ::= 24
      + * ub-unformatted-address-length INTEGER ::= 180
      + * ub-x121-address-length INTEGER ::= 16
      + *
      + * -- Note - upper bounds on string types, such as TeletexString, are
      + * -- measured in characters.  Excepting PrintableString or IA5String, a
      + * -- significantly greater number of octets will be required to hold
      + * -- such a value.  As a minimum, 16 octets, or twice the specified upper
      + * -- bound, whichever is the larger, should be allowed for TeletexString.
      + * -- For UTF8String or UniversalString at least four times the upper
      + * -- bound should be allowed.
      + * 
      + * + * @author Anne Anderson + * @since 1.4 + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ +public class X400Address implements GeneralNameInterface { + + // Private data members + byte[] nameValue = null; + + /** + * Create the X400Address object from the specified byte array + * + * @param nameValue value of the name as a byte array + */ + public X400Address(byte[] value) { + nameValue = value; + } + + /** + * Create the X400Address object from the passed encoded Der value. + * + * @param derValue the encoded DER X400Address. + * @exception IOException on error. + */ + public X400Address(DerValue derValue) throws IOException { + nameValue = derValue.toByteArray(); + } + + /** + * Return the type of the GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_X400); + } + + /** + * Encode the X400 name into the DerOutputStream. + * + * @param out the DER stream to encode the X400Address to. + * @exception IOException on encoding errors. + */ + public void encode(DerOutputStream out) throws IOException { + DerValue derValue = new DerValue(nameValue); + out.putDerValue(derValue); + } + + /** + * Return the printable string. + */ + public String toString() { + return ("X400Address: "); + } + + /** + * Return type of constraint inputName places on this name:
        + *
      • NAME_DIFF_TYPE = -1: input name is different type from name (i.e. does not constrain). + *
      • NAME_MATCH = 0: input name matches name. + *
      • NAME_NARROWS = 1: input name narrows name (is lower in the naming subtree) + *
      • NAME_WIDENS = 2: input name widens name (is higher in the naming subtree) + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow name, but is same type. + *
      . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is same type, but comparison operations are + * not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) throws UnsupportedOperationException { + int constraintType; + if (inputName == null) + constraintType = NAME_DIFF_TYPE; + else if (inputName.getType() != NAME_X400) + constraintType = NAME_DIFF_TYPE; + else + //Narrowing, widening, and match constraints not defined in rfc2459 for X400Address + throw new UnsupportedOperationException("Narrowing, widening, and match are not supported for X400Address."); + return constraintType; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + throw new UnsupportedOperationException("subtreeDepth not supported for X400Address"); + } + +} diff --git a/src/sun/security/x509/X500Name.java b/src/sun/security/x509/X500Name.java new file mode 100644 index 00000000..3cc7b93c --- /dev/null +++ b/src/sun/security/x509/X500Name.java @@ -0,0 +1,1441 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.lang.reflect.*; +import java.io.IOException; +import java.security.PrivilegedExceptionAction; +import java.security.AccessController; +import java.security.Principal; +import java.util.*; + +import sun.security.util.*; +import javax.security.auth.x500.X500Principal; + +/** + * Note: As of 1.4, the public class, + * javax.security.auth.x500.X500Principal, + * should be used when parsing, generating, and comparing X.500 DNs. + * This class contains other useful methods for checking name constraints + * and retrieving DNs by keyword. + * + *

      X.500 names are used to identify entities, such as those which are + * identified by X.509 certificates. They are world-wide, hierarchical, + * and descriptive. Entities can be identified by attributes, and in + * some systems can be searched for according to those attributes. + *

      + * The ASN.1 for this is: + *

      + * GeneralName ::= CHOICE {
      + * ....
      + *     directoryName                   [4]     Name,
      + * ....
      + * Name ::= CHOICE {
      + *   RDNSequence }
      + *
      + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
      + *
      + * RelativeDistinguishedName ::=
      + *   SET OF AttributeTypeAndValue
      + *
      + * AttributeTypeAndValue ::= SEQUENCE {
      + *   type     AttributeType,
      + *   value    AttributeValue }
      + *
      + * AttributeType ::= OBJECT IDENTIFIER
      + *
      + * AttributeValue ::= ANY DEFINED BY AttributeType
      + * ....
      + * DirectoryString ::= CHOICE {
      + *       teletexString           TeletexString (SIZE (1..MAX)),
      + *       printableString         PrintableString (SIZE (1..MAX)),
      + *       universalString         UniversalString (SIZE (1..MAX)),
      + *       utf8String              UTF8String (SIZE (1.. MAX)),
      + *       bmpString               BMPString (SIZE (1..MAX)) }
      + * 
      + *

      + * This specification requires only a subset of the name comparison + * functionality specified in the X.500 series of specifications. The + * requirements for conforming implementations are as follows: + *

        + *
      1. attribute values encoded in different types (e.g., + * PrintableString and BMPString) may be assumed to represent + * different strings; + *

        + *

      2. attribute values in types other than PrintableString are case + * sensitive (this permits matching of attribute values as binary + * objects); + *

        + *

      3. attribute values in PrintableString are not case sensitive + * (e.g., "Marianne Swanson" is the same as "MARIANNE SWANSON"); and + *

        + *

      4. attribute values in PrintableString are compared after + * removing leading and trailing white space and converting internal + * substrings of one or more consecutive white space characters to a + * single space. + *
      + *

      + * These name comparison rules permit a certificate user to validate + * certificates issued using languages or encodings unfamiliar to the + * certificate user. + *

      + * In addition, implementations of this specification MAY use these + * comparison rules to process unfamiliar attribute types for name + * chaining. This allows implementations to process certificates with + * unfamiliar attributes in the issuer name. + *

      + * Note that the comparison rules defined in the X.500 series of + * specifications indicate that the character sets used to encode data + * in distinguished names are irrelevant. The characters themselves are + * compared without regard to encoding. Implementations of the profile + * are permitted to use the comparison algorithm defined in the X.500 + * series. Such an implementation will recognize a superset of name + * matches recognized by the algorithm specified above. + *

      + * Note that instances of this class are immutable. + * + * @author David Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see GeneralName + * @see GeneralNames + * @see GeneralNameInterface + */ + +public class X500Name implements GeneralNameInterface, Principal { + + private String dn; // roughly RFC 1779 DN, or null + private String rfc1779Dn; // RFC 1779 compliant DN, or null + private String rfc2253Dn; // RFC 2253 DN, or null + private String canonicalDn; // canonical RFC 2253 DN or null + private RDN[] names; // RDNs (never null) + private X500Principal x500Principal; + private byte[] encoded; + + // cached immutable list of the RDNs and all the AVAs + private volatile List rdnList; + private volatile List allAvaList; + + /** + * Constructs a name from a conventionally formatted string, such + * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". + * (RFC 1779, 2253, or 4514 style). + * + * @param dname the X.500 Distinguished Name + */ + public X500Name(String dname) throws IOException { + this(dname, Collections.emptyMap()); + } + + /** + * Constructs a name from a conventionally formatted string, such + * as "CN=Dave, OU=JavaSoft, O=Sun Microsystems, C=US". + * (RFC 1779, 2253, or 4514 style). + * + * @param dname the X.500 Distinguished Name + * @param keywordMap an additional keyword/OID map + */ + public X500Name(String dname, Map keywordMap) + throws IOException { + parseDN(dname, keywordMap); + } + + /** + * Constructs a name from a string formatted according to format. + * Currently, the formats DEFAULT and RFC2253 are supported. + * DEFAULT is the default format used by the X500Name(String) + * constructor. RFC2253 is the format strictly according to RFC2253 + * without extensions. + * + * @param dname the X.500 Distinguished Name + * @param format the specified format of the String DN + */ + public X500Name(String dname, String format) throws IOException { + if (dname == null) { + throw new NullPointerException("Name must not be null"); + } + if (format.equalsIgnoreCase("RFC2253")) { + parseRFC2253DN(dname); + } else if (format.equalsIgnoreCase("DEFAULT")) { + parseDN(dname, Collections.emptyMap()); + } else { + throw new IOException("Unsupported format " + format); + } + } + + /** + * Constructs a name from fields common in enterprise application + * environments. + * + *

      NOTE: The behaviour when any of + * these strings contain characters outside the ASCII range + * is unspecified in currently relevant standards. + * + * @param commonName common name of a person, e.g. "Vivette Davis" + * @param organizationUnit small organization name, e.g. "Purchasing" + * @param organizationName large organization name, e.g. "Onizuka, Inc." + * @param country two letter country code, e.g. "CH" + */ + public X500Name(String commonName, String organizationUnit, + String organizationName, String country) + throws IOException { + names = new RDN[4]; + /* + * NOTE: it's only on output that little-endian + * ordering is used. + */ + names[3] = new RDN(1); + names[3].assertion[0] = new AVA(commonName_oid, + new DerValue(commonName)); + names[2] = new RDN(1); + names[2].assertion[0] = new AVA(orgUnitName_oid, + new DerValue(organizationUnit)); + names[1] = new RDN(1); + names[1].assertion[0] = new AVA(orgName_oid, + new DerValue(organizationName)); + names[0] = new RDN(1); + names[0].assertion[0] = new AVA(countryName_oid, + new DerValue(country)); + } + + /** + * Constructs a name from fields common in Internet application + * environments. + * + *

      NOTE: The behaviour when any of + * these strings contain characters outside the ASCII range + * is unspecified in currently relevant standards. + * + * @param commonName common name of a person, e.g. "Vivette Davis" + * @param organizationUnit small organization name, e.g. "Purchasing" + * @param organizationName large organization name, e.g. "Onizuka, Inc." + * @param localityName locality (city) name, e.g. "Palo Alto" + * @param stateName state name, e.g. "California" + * @param country two letter country code, e.g. "CH" + */ + public X500Name(String commonName, String organizationUnit, + String organizationName, String localityName, + String stateName, String country) + throws IOException { + names = new RDN[6]; + /* + * NOTE: it's only on output that little-endian + * ordering is used. + */ + names[5] = new RDN(1); + names[5].assertion[0] = new AVA(commonName_oid, + new DerValue(commonName)); + names[4] = new RDN(1); + names[4].assertion[0] = new AVA(orgUnitName_oid, + new DerValue(organizationUnit)); + names[3] = new RDN(1); + names[3].assertion[0] = new AVA(orgName_oid, + new DerValue(organizationName)); + names[2] = new RDN(1); + names[2].assertion[0] = new AVA(localityName_oid, + new DerValue(localityName)); + names[1] = new RDN(1); + names[1].assertion[0] = new AVA(stateName_oid, + new DerValue(stateName)); + names[0] = new RDN(1); + names[0].assertion[0] = new AVA(countryName_oid, + new DerValue(country)); + } + + /** + * Constructs a name from an array of relative distinguished names + * + * @param rdnArray array of relative distinguished names + * @throws IOException on error + */ + public X500Name(RDN[] rdnArray) throws IOException { + if (rdnArray == null) { + names = new RDN[0]; + } else { + names = rdnArray.clone(); + for (int i = 0; i < names.length; i++) { + if (names[i] == null) { + throw new IOException("Cannot create an X500Name"); + } + } + } + } + + /** + * Constructs a name from an ASN.1 encoded value. The encoding + * of the name in the stream uses DER (a BER/1 subset). + * + * @param value a DER-encoded value holding an X.500 name. + */ + public X500Name(DerValue value) throws IOException { + //Note that toDerInputStream uses only the buffer (data) and not + //the tag, so an empty SEQUENCE (OF) will yield an empty DerInputStream + this(value.toDerInputStream()); + } + + /** + * Constructs a name from an ASN.1 encoded input stream. The encoding + * of the name in the stream uses DER (a BER/1 subset). + * + * @param in DER-encoded data holding an X.500 name. + */ + public X500Name(DerInputStream in) throws IOException { + parseDER(in); + } + + /** + * Constructs a name from an ASN.1 encoded byte array. + * + * @param name DER-encoded byte array holding an X.500 name. + */ + public X500Name(byte[] name) throws IOException { + DerInputStream in = new DerInputStream(name); + parseDER(in); + } + + /** + * Return an immutable List of all RDNs in this X500Name. + */ + public List rdns() { + List list = rdnList; + if (list == null) { + list = Collections.unmodifiableList(Arrays.asList(names)); + rdnList = list; + } + return list; + } + + /** + * Return the number of RDNs in this X500Name. + */ + public int size() { + return names.length; + } + + /** + * Return an immutable List of the the AVAs contained in all the + * RDNs of this X500Name. + */ + public List allAvas() { + List list = allAvaList; + if (list == null) { + list = new ArrayList(); + for (int i = 0; i < names.length; i++) { + list.addAll(names[i].avas()); + } + } + return list; + } + + /** + * Return the total number of AVAs contained in all the RDNs of + * this X500Name. + */ + public int avaSize() { + return allAvas().size(); + } + + /** + * Return whether this X500Name is empty. An X500Name is not empty + * if it has at least one RDN containing at least one AVA. + */ + public boolean isEmpty() { + int n = names.length; + if (n == 0) { + return true; + } + for (int i = 0; i < n; i++) { + if (names[i].assertion.length != 0) { + return false; + } + } + return true; + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + return getRFC2253CanonicalName().hashCode(); + } + + /** + * Compares this name with another, for equality. + * + * @return true iff the names are identical. + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof X500Name == false) { + return false; + } + X500Name other = (X500Name)obj; + // if we already have the canonical forms, compare now + if ((this.canonicalDn != null) && (other.canonicalDn != null)) { + return this.canonicalDn.equals(other.canonicalDn); + } + // quick check that number of RDNs and AVAs match before canonicalizing + int n = this.names.length; + if (n != other.names.length) { + return false; + } + for (int i = 0; i < n; i++) { + RDN r1 = this.names[i]; + RDN r2 = other.names[i]; + if (r1.assertion.length != r2.assertion.length) { + return false; + } + } + // definite check via canonical form + String thisCanonical = this.getRFC2253CanonicalName(); + String otherCanonical = other.getRFC2253CanonicalName(); + return thisCanonical.equals(otherCanonical); + } + + /* + * Returns the name component as a Java string, regardless of its + * encoding restrictions. + */ + private String getString(DerValue attribute) throws IOException { + if (attribute == null) + return null; + String value = attribute.getAsString(); + + if (value == null) + throw new IOException("not a DER string encoding, " + + attribute.tag); + else + return value; + } + + /** + * Return type of GeneralName. + */ + public int getType() { + return (GeneralNameInterface.NAME_DIRECTORY); + } + + /** + * Returns a "Country" name component. If more than one + * such attribute exists, the topmost one is returned. + * + * @return "C=" component of the name, if any. + */ + public String getCountry() throws IOException { + DerValue attr = findAttribute(countryName_oid); + + return getString(attr); + } + + + /** + * Returns an "Organization" name component. If more than + * one such attribute exists, the topmost one is returned. + * + * @return "O=" component of the name, if any. + */ + public String getOrganization() throws IOException { + DerValue attr = findAttribute(orgName_oid); + + return getString(attr); + } + + + /** + * Returns an "Organizational Unit" name component. If more + * than one such attribute exists, the topmost one is returned. + * + * @return "OU=" component of the name, if any. + */ + public String getOrganizationalUnit() throws IOException { + DerValue attr = findAttribute(orgUnitName_oid); + + return getString(attr); + } + + + /** + * Returns a "Common Name" component. If more than one such + * attribute exists, the topmost one is returned. + * + * @return "CN=" component of the name, if any. + */ + public String getCommonName() throws IOException { + DerValue attr = findAttribute(commonName_oid); + + return getString(attr); + } + + + /** + * Returns a "Locality" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "L=" component of the name, if any. + */ + public String getLocality() throws IOException { + DerValue attr = findAttribute(localityName_oid); + + return getString(attr); + } + + /** + * Returns a "State" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "S=" component of the name, if any. + */ + public String getState() throws IOException { + DerValue attr = findAttribute(stateName_oid); + + return getString(attr); + } + + /** + * Returns a "Domain" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "DC=" component of the name, if any. + */ + public String getDomain() throws IOException { + DerValue attr = findAttribute(DOMAIN_COMPONENT_OID); + + return getString(attr); + } + + /** + * Returns a "DN Qualifier" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "DNQ=" component of the name, if any. + */ + public String getDNQualifier() throws IOException { + DerValue attr = findAttribute(DNQUALIFIER_OID); + + return getString(attr); + } + + /** + * Returns a "Surname" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "SURNAME=" component of the name, if any. + */ + public String getSurname() throws IOException { + DerValue attr = findAttribute(SURNAME_OID); + + return getString(attr); + } + + /** + * Returns a "Given Name" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "GIVENNAME=" component of the name, if any. + */ + public String getGivenName() throws IOException { + DerValue attr = findAttribute(GIVENNAME_OID); + + return getString(attr); + } + + /** + * Returns an "Initials" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "INITIALS=" component of the name, if any. + */ + public String getInitials() throws IOException { + DerValue attr = findAttribute(INITIALS_OID); + + return getString(attr); + } + + /** + * Returns a "Generation Qualifier" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "GENERATION=" component of the name, if any. + */ + public String getGeneration() throws IOException { + DerValue attr = findAttribute(GENERATIONQUALIFIER_OID); + + return getString(attr); + } + + /** + * Returns an "IP address" name component. If more than one + * such component exists, the topmost one is returned. + * + * @return "IP=" component of the name, if any. + */ + public String getIP() throws IOException { + DerValue attr = findAttribute(ipAddress_oid); + + return getString(attr); + } + + /** + * Returns a string form of the X.500 distinguished name. + * The format of the string is from RFC 1779. The returned string + * may contain non-standardised keywords for more readability + * (keywords from RFCs 1779, 2253, and 3280). + */ + public String toString() { + if (dn == null) { + generateDN(); + } + return dn; + } + + /** + * Returns a string form of the X.500 distinguished name + * using the algorithm defined in RFC 1779. Only standard attribute type + * keywords defined in RFC 1779 are emitted. + */ + public String getRFC1779Name() { + return getRFC1779Name(Collections.emptyMap()); + } + + /** + * Returns a string form of the X.500 distinguished name + * using the algorithm defined in RFC 1779. Attribute type + * keywords defined in RFC 1779 are emitted, as well as additional + * keywords contained in the OID/keyword map. + */ + public String getRFC1779Name(Map oidMap) + throws IllegalArgumentException { + if (oidMap.isEmpty()) { + // return cached result + if (rfc1779Dn != null) { + return rfc1779Dn; + } else { + rfc1779Dn = generateRFC1779DN(oidMap); + return rfc1779Dn; + } + } + return generateRFC1779DN(oidMap); + } + + /** + * Returns a string form of the X.500 distinguished name + * using the algorithm defined in RFC 2253. Only standard attribute type + * keywords defined in RFC 2253 are emitted. + */ + public String getRFC2253Name() { + return getRFC2253Name(Collections.emptyMap()); + } + + /** + * Returns a string form of the X.500 distinguished name + * using the algorithm defined in RFC 2253. Attribute type + * keywords defined in RFC 2253 are emitted, as well as additional + * keywords contained in the OID/keyword map. + */ + public String getRFC2253Name(Map oidMap) { + /* check for and return cached name */ + if (oidMap.isEmpty()) { + if (rfc2253Dn != null) { + return rfc2253Dn; + } else { + rfc2253Dn = generateRFC2253DN(oidMap); + return rfc2253Dn; + } + } + return generateRFC2253DN(oidMap); + } + + private String generateRFC2253DN(Map oidMap) { + /* + * Section 2.1 : if the RDNSequence is an empty sequence + * the result is the empty or zero length string. + */ + if (names.length == 0) { + return ""; + } + + /* + * 2.1 (continued) : Otherwise, the output consists of the string + * encodings of each RelativeDistinguishedName in the RDNSequence + * (according to 2.2), starting with the last element of the sequence + * and moving backwards toward the first. + * + * The encodings of adjoining RelativeDistinguishedNames are separated + * by a comma character (',' ASCII 44). + */ + StringBuilder fullname = new StringBuilder(48); + for (int i = names.length - 1; i >= 0; i--) { + if (i < names.length - 1) { + fullname.append(','); + } + fullname.append(names[i].toRFC2253String(oidMap)); + } + return fullname.toString(); + } + + public String getRFC2253CanonicalName() { + /* check for and return cached name */ + if (canonicalDn != null) { + return canonicalDn; + } + /* + * Section 2.1 : if the RDNSequence is an empty sequence + * the result is the empty or zero length string. + */ + if (names.length == 0) { + canonicalDn = ""; + return canonicalDn; + } + + /* + * 2.1 (continued) : Otherwise, the output consists of the string + * encodings of each RelativeDistinguishedName in the RDNSequence + * (according to 2.2), starting with the last element of the sequence + * and moving backwards toward the first. + * + * The encodings of adjoining RelativeDistinguishedNames are separated + * by a comma character (',' ASCII 44). + */ + StringBuilder fullname = new StringBuilder(48); + for (int i = names.length - 1; i >= 0; i--) { + if (i < names.length - 1) { + fullname.append(','); + } + fullname.append(names[i].toRFC2253String(true)); + } + canonicalDn = fullname.toString(); + return canonicalDn; + } + + /** + * Returns the value of toString(). This call is needed to + * implement the java.security.Principal interface. + */ + public String getName() { return toString(); } + + /** + * Find the first instance of this attribute in a "top down" + * search of all the attributes in the name. + */ + private DerValue findAttribute(ObjectIdentifier attribute) { + if (names != null) { + for (int i = 0; i < names.length; i++) { + DerValue value = names[i].findAttribute(attribute); + if (value != null) { + return value; + } + } + } + return null; + } + + /** + * Find the most specific ("last") attribute of the given + * type. + */ + public DerValue findMostSpecificAttribute(ObjectIdentifier attribute) { + if (names != null) { + for (int i = names.length - 1; i >= 0; i--) { + DerValue value = names[i].findAttribute(attribute); + if (value != null) { + return value; + } + } + } + return null; + } + + /****************************************************************/ + + private void parseDER(DerInputStream in) throws IOException { + // + // X.500 names are a "SEQUENCE OF" RDNs, which means zero or + // more and order matters. We scan them in order, which + // conventionally is big-endian. + // + DerValue[] nameseq = null; + byte[] derBytes = in.toByteArray(); + + try { + nameseq = in.getSequence(5); + } catch (IOException ioe) { + if (derBytes == null) { + nameseq = null; + } else { + DerValue derVal = new DerValue(DerValue.tag_Sequence, + derBytes); + derBytes = derVal.toByteArray(); + nameseq = new DerInputStream(derBytes).getSequence(5); + } + } + + if (nameseq == null) { + names = new RDN[0]; + } else { + names = new RDN[nameseq.length]; + for (int i = 0; i < nameseq.length; i++) { + names[i] = new RDN(nameseq[i]); + } + } + } + + /** + * Encodes the name in DER-encoded form. + * + * @deprecated Use encode() instead + * @param out where to put the DER-encoded X.500 name + */ + @Deprecated + public void emit(DerOutputStream out) throws IOException { + encode(out); + } + + /** + * Encodes the name in DER-encoded form. + * + * @param out where to put the DER-encoded X.500 name + */ + public void encode(DerOutputStream out) throws IOException { + DerOutputStream tmp = new DerOutputStream(); + for (int i = 0; i < names.length; i++) { + names[i].encode(tmp); + } + out.write(DerValue.tag_Sequence, tmp); + } + + /** + * Returned the encoding as an uncloned byte array. Callers must + * guarantee that they neither modify it not expose it to untrusted + * code. + */ + public byte[] getEncodedInternal() throws IOException { + if (encoded == null) { + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + for (int i = 0; i < names.length; i++) { + names[i].encode(tmp); + } + out.write(DerValue.tag_Sequence, tmp); + encoded = out.toByteArray(); + } + return encoded; + } + + /** + * Gets the name in DER-encoded form. + * + * @return the DER encoded byte array of this name. + */ + public byte[] getEncoded() throws IOException { + return getEncodedInternal().clone(); + } + + /* + * Parses a Distinguished Name (DN) in printable representation. + * + * According to RFC 1779, RDNs in a DN are separated by comma. + * The following examples show both methods of quoting a comma, so that it + * is not considered a separator: + * + * O="Sue, Grabbit and Runn" or + * O=Sue\, Grabbit and Runn + * + * This method can parse RFC 1779, 2253 or 4514 DNs and non-standard 3280 + * keywords. Additional keywords can be specified in the keyword/OID map. + */ + private void parseDN(String input, Map keywordMap) + throws IOException { + if (input == null || input.length() == 0) { + names = new RDN[0]; + return; + } + + List dnVector = new ArrayList<>(); + int dnOffset = 0; + int rdnEnd; + String rdnString; + int quoteCount = 0; + + String dnString = input; + + int searchOffset = 0; + int nextComma = dnString.indexOf(','); + int nextSemiColon = dnString.indexOf(';'); + while (nextComma >=0 || nextSemiColon >=0) { + + if (nextSemiColon < 0) { + rdnEnd = nextComma; + } else if (nextComma < 0) { + rdnEnd = nextSemiColon; + } else { + rdnEnd = Math.min(nextComma, nextSemiColon); + } + quoteCount += countQuotes(dnString, searchOffset, rdnEnd); + + /* + * We have encountered an RDN delimiter (comma or a semicolon). + * If the comma or semicolon in the RDN under consideration is + * preceded by a backslash (escape), or by a double quote, it + * is part of the RDN. Otherwise, it is used as a separator, to + * delimit the RDN under consideration from any subsequent RDNs. + */ + if (rdnEnd >= 0 && quoteCount != 1 && + !escaped(rdnEnd, searchOffset, dnString)) { + + /* + * Comma/semicolon is a separator + */ + rdnString = dnString.substring(dnOffset, rdnEnd); + + // Parse RDN, and store it in vector + RDN rdn = new RDN(rdnString, keywordMap); + dnVector.add(rdn); + + // Increase the offset + dnOffset = rdnEnd + 1; + + // Set quote counter back to zero + quoteCount = 0; + } + + searchOffset = rdnEnd + 1; + nextComma = dnString.indexOf(',', searchOffset); + nextSemiColon = dnString.indexOf(';', searchOffset); + } + + // Parse last or only RDN, and store it in vector + rdnString = dnString.substring(dnOffset); + RDN rdn = new RDN(rdnString, keywordMap); + dnVector.add(rdn); + + /* + * Store the vector elements as an array of RDNs + * NOTE: It's only on output that little-endian ordering is used. + */ + Collections.reverse(dnVector); + names = dnVector.toArray(new RDN[dnVector.size()]); + } + + private void parseRFC2253DN(String dnString) throws IOException { + if (dnString.length() == 0) { + names = new RDN[0]; + return; + } + + List dnVector = new ArrayList<>(); + int dnOffset = 0; + String rdnString; + int searchOffset = 0; + int rdnEnd = dnString.indexOf(','); + while (rdnEnd >=0) { + /* + * We have encountered an RDN delimiter (comma). + * If the comma in the RDN under consideration is + * preceded by a backslash (escape), it + * is part of the RDN. Otherwise, it is used as a separator, to + * delimit the RDN under consideration from any subsequent RDNs. + */ + if (rdnEnd > 0 && !escaped(rdnEnd, searchOffset, dnString)) { + + /* + * Comma is a separator + */ + rdnString = dnString.substring(dnOffset, rdnEnd); + + // Parse RDN, and store it in vector + RDN rdn = new RDN(rdnString, "RFC2253"); + dnVector.add(rdn); + + // Increase the offset + dnOffset = rdnEnd + 1; + } + + searchOffset = rdnEnd + 1; + rdnEnd = dnString.indexOf(',', searchOffset); + } + + // Parse last or only RDN, and store it in vector + rdnString = dnString.substring(dnOffset); + RDN rdn = new RDN(rdnString, "RFC2253"); + dnVector.add(rdn); + + /* + * Store the vector elements as an array of RDNs + * NOTE: It's only on output that little-endian ordering is used. + */ + Collections.reverse(dnVector); + names = dnVector.toArray(new RDN[dnVector.size()]); + } + + /* + * Counts double quotes in string. + * Escaped quotes are ignored. + */ + static int countQuotes(String string, int from, int to) { + int count = 0; + + for (int i = from; i < to; i++) { + if ((string.charAt(i) == '"' && i == from) || + (string.charAt(i) == '"' && string.charAt(i-1) != '\\')) { + count++; + } + } + + return count; + } + + private static boolean escaped + (int rdnEnd, int searchOffset, String dnString) { + + if (rdnEnd == 1 && dnString.charAt(rdnEnd - 1) == '\\') { + + // case 1: + // \, + + return true; + + } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' && + dnString.charAt(rdnEnd - 2) != '\\') { + + // case 2: + // foo\, + + return true; + + } else if (rdnEnd > 1 && dnString.charAt(rdnEnd - 1) == '\\' && + dnString.charAt(rdnEnd - 2) == '\\') { + + // case 3: + // foo\\\\\, + + int count = 0; + rdnEnd--; // back up to last backSlash + while (rdnEnd >= searchOffset) { + if (dnString.charAt(rdnEnd) == '\\') { + count++; // count consecutive backslashes + } + rdnEnd--; + } + + // if count is odd, then rdnEnd is escaped + return (count % 2) != 0 ? true : false; + + } else { + return false; + } + } + + /* + * Dump the printable form of a distinguished name. Each relative + * name is separated from the next by a ",", and assertions in the + * relative names have "label=value" syntax. + * + * Uses RFC 1779 syntax (i.e. little-endian, comma separators) + */ + private void generateDN() { + if (names.length == 1) { + dn = names[0].toString(); + return; + } + + StringBuilder sb = new StringBuilder(48); + if (names != null) { + for (int i = names.length - 1; i >= 0; i--) { + if (i != names.length - 1) { + sb.append(", "); + } + sb.append(names[i].toString()); + } + } + dn = sb.toString(); + } + + /* + * Dump the printable form of a distinguished name. Each relative + * name is separated from the next by a ",", and assertions in the + * relative names have "label=value" syntax. + * + * Uses RFC 1779 syntax (i.e. little-endian, comma separators) + * Valid keywords from RFC 1779 are used. Additional keywords can be + * specified in the OID/keyword map. + */ + private String generateRFC1779DN(Map oidMap) { + if (names.length == 1) { + return names[0].toRFC1779String(oidMap); + } + + StringBuilder sb = new StringBuilder(48); + if (names != null) { + for (int i = names.length - 1; i >= 0; i--) { + if (i != names.length - 1) { + sb.append(", "); + } + sb.append(names[i].toRFC1779String(oidMap)); + } + } + return sb.toString(); + } + + /****************************************************************/ + + /* + * Maybe return a preallocated OID, to reduce storage costs + * and speed recognition of common X.500 attributes. + */ + static ObjectIdentifier intern(ObjectIdentifier oid) { + ObjectIdentifier interned = internedOIDs.get(oid); + if (interned != null) { + return interned; + } + internedOIDs.put(oid, oid); + return oid; + } + + private static final Map internedOIDs + = new HashMap(); + + /* + * Selected OIDs from X.520 + * Includes all those specified in RFC 3280 as MUST or SHOULD + * be recognized + */ + private static final int commonName_data[] = { 2, 5, 4, 3 }; + private static final int SURNAME_DATA[] = { 2, 5, 4, 4 }; + private static final int SERIALNUMBER_DATA[] = { 2, 5, 4, 5 }; + private static final int countryName_data[] = { 2, 5, 4, 6 }; + private static final int localityName_data[] = { 2, 5, 4, 7 }; + private static final int stateName_data[] = { 2, 5, 4, 8 }; + private static final int streetAddress_data[] = { 2, 5, 4, 9 }; + private static final int orgName_data[] = { 2, 5, 4, 10 }; + private static final int orgUnitName_data[] = { 2, 5, 4, 11 }; + private static final int title_data[] = { 2, 5, 4, 12 }; + private static final int GIVENNAME_DATA[] = { 2, 5, 4, 42 }; + private static final int INITIALS_DATA[] = { 2, 5, 4, 43 }; + private static final int GENERATIONQUALIFIER_DATA[] = { 2, 5, 4, 44 }; + private static final int DNQUALIFIER_DATA[] = { 2, 5, 4, 46 }; + + private static final int ipAddress_data[] = { 1, 3, 6, 1, 4, 1, 42, 2, 11, 2, 1 }; + private static final int DOMAIN_COMPONENT_DATA[] = + { 0, 9, 2342, 19200300, 100, 1, 25 }; + private static final int userid_data[] = + { 0, 9, 2342, 19200300, 100, 1, 1 }; + + + public static final ObjectIdentifier commonName_oid; + public static final ObjectIdentifier countryName_oid; + public static final ObjectIdentifier localityName_oid; + public static final ObjectIdentifier orgName_oid; + public static final ObjectIdentifier orgUnitName_oid; + public static final ObjectIdentifier stateName_oid; + public static final ObjectIdentifier streetAddress_oid; + public static final ObjectIdentifier title_oid; + public static final ObjectIdentifier DNQUALIFIER_OID; + public static final ObjectIdentifier SURNAME_OID; + public static final ObjectIdentifier GIVENNAME_OID; + public static final ObjectIdentifier INITIALS_OID; + public static final ObjectIdentifier GENERATIONQUALIFIER_OID; + public static final ObjectIdentifier ipAddress_oid; + public static final ObjectIdentifier DOMAIN_COMPONENT_OID; + public static final ObjectIdentifier userid_oid; + public static final ObjectIdentifier SERIALNUMBER_OID; + + static { + /** OID for the "CN=" attribute, denoting a person's common name. */ + commonName_oid = intern(ObjectIdentifier.newInternal(commonName_data)); + + /** OID for the "SERIALNUMBER=" attribute, denoting a serial number for. + a name. Do not confuse with PKCS#9 issuerAndSerialNumber or the + certificate serial number. */ + SERIALNUMBER_OID = intern(ObjectIdentifier.newInternal(SERIALNUMBER_DATA)); + + /** OID for the "C=" attribute, denoting a country. */ + countryName_oid = intern(ObjectIdentifier.newInternal(countryName_data)); + + /** OID for the "L=" attribute, denoting a locality (such as a city) */ + localityName_oid = intern(ObjectIdentifier.newInternal(localityName_data)); + + /** OID for the "O=" attribute, denoting an organization name */ + orgName_oid = intern(ObjectIdentifier.newInternal(orgName_data)); + + /** OID for the "OU=" attribute, denoting an organizational unit name */ + orgUnitName_oid = intern(ObjectIdentifier.newInternal(orgUnitName_data)); + + /** OID for the "S=" attribute, denoting a state (such as Delaware) */ + stateName_oid = intern(ObjectIdentifier.newInternal(stateName_data)); + + /** OID for the "STREET=" attribute, denoting a street address. */ + streetAddress_oid = intern(ObjectIdentifier.newInternal(streetAddress_data)); + + /** OID for the "T=" attribute, denoting a person's title. */ + title_oid = intern(ObjectIdentifier.newInternal(title_data)); + + /** OID for the "DNQUALIFIER=" or "DNQ=" attribute, denoting DN + disambiguating information.*/ + DNQUALIFIER_OID = intern(ObjectIdentifier.newInternal(DNQUALIFIER_DATA)); + + /** OID for the "SURNAME=" attribute, denoting a person's surname.*/ + SURNAME_OID = intern(ObjectIdentifier.newInternal(SURNAME_DATA)); + + /** OID for the "GIVENNAME=" attribute, denoting a person's given name.*/ + GIVENNAME_OID = intern(ObjectIdentifier.newInternal(GIVENNAME_DATA)); + + /** OID for the "INITIALS=" attribute, denoting a person's initials.*/ + INITIALS_OID = intern(ObjectIdentifier.newInternal(INITIALS_DATA)); + + /** OID for the "GENERATION=" attribute, denoting Jr., II, etc.*/ + GENERATIONQUALIFIER_OID = + intern(ObjectIdentifier.newInternal(GENERATIONQUALIFIER_DATA)); + + /* + * OIDs from other sources which show up in X.500 names we + * expect to deal with often + */ + /** OID for "IP=" IP address attributes, used with SKIP. */ + ipAddress_oid = intern(ObjectIdentifier.newInternal(ipAddress_data)); + + /* + * Domain component OID from RFC 1274, RFC 2247, RFC 3280 + */ + + /* + * OID for "DC=" domain component attributes, used with DNS names in DN + * format + */ + DOMAIN_COMPONENT_OID = + intern(ObjectIdentifier.newInternal(DOMAIN_COMPONENT_DATA)); + + /** OID for "UID=" denoting a user id, defined in RFCs 1274 & 2798. */ + userid_oid = intern(ObjectIdentifier.newInternal(userid_data)); + } + + /** + * Return constraint type:

        + *
      • NAME_DIFF_TYPE = -1: input name is different type from this name + * (i.e. does not constrain) + *
      • NAME_MATCH = 0: input name matches this name + *
      • NAME_NARROWS = 1: input name narrows this name + *
      • NAME_WIDENS = 2: input name widens this name + *
      • NAME_SAME_TYPE = 3: input name does not match or narrow this name, + & but is same type + *
      . These results are used in checking NameConstraints during + * certification path verification. + * + * @param inputName to be checked for being constrained + * @returns constraint type above + * @throws UnsupportedOperationException if name is not exact match, but + * narrowing and widening are not supported for this name type. + */ + public int constrains(GeneralNameInterface inputName) + throws UnsupportedOperationException { + int constraintType; + if (inputName == null) { + constraintType = NAME_DIFF_TYPE; + } else if (inputName.getType() != NAME_DIRECTORY) { + constraintType = NAME_DIFF_TYPE; + } else { // type == NAME_DIRECTORY + X500Name inputX500 = (X500Name)inputName; + if (inputX500.equals(this)) { + constraintType = NAME_MATCH; + } else if (inputX500.names.length == 0) { + constraintType = NAME_WIDENS; + } else if (this.names.length == 0) { + constraintType = NAME_NARROWS; + } else if (inputX500.isWithinSubtree(this)) { + constraintType = NAME_NARROWS; + } else if (isWithinSubtree(inputX500)) { + constraintType = NAME_WIDENS; + } else { + constraintType = NAME_SAME_TYPE; + } + } + return constraintType; + } + + /** + * Compares this name with another and determines if + * it is within the subtree of the other. Useful for + * checking against the name constraints extension. + * + * @return true iff this name is within the subtree of other. + */ + private boolean isWithinSubtree(X500Name other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + if (other.names.length == 0) { + return true; + } + if (this.names.length == 0) { + return false; + } + if (names.length < other.names.length) { + return false; + } + for (int i = 0; i < other.names.length; i++) { + if (!names[i].equals(other.names[i])) { + return false; + } + } + return true; + } + + /** + * Return subtree depth of this name for purposes of determining + * NameConstraints minimum and maximum bounds and for calculating + * path lengths in name subtrees. + * + * @returns distance of name from root + * @throws UnsupportedOperationException if not supported for this name type + */ + public int subtreeDepth() throws UnsupportedOperationException { + return names.length; + } + + /** + * Return lowest common ancestor of this name and other name + * + * @param other another X500Name + * @return X500Name of lowest common ancestor; null if none + */ + public X500Name commonAncestor(X500Name other) { + + if (other == null) { + return null; + } + int otherLen = other.names.length; + int thisLen = this.names.length; + if (thisLen == 0 || otherLen == 0) { + return null; + } + int minLen = (thisLen < otherLen) ? thisLen: otherLen; + + //Compare names from highest RDN down the naming tree + //Note that these are stored in RDN[0]... + int i=0; + for (; i < minLen; i++) { + if (!names[i].equals(other.names[i])) { + if (i == 0) { + return null; + } else { + break; + } + } + } + + //Copy matching RDNs into new RDN array + RDN[] ancestor = new RDN[i]; + for (int j=0; j < i; j++) { + ancestor[j] = names[j]; + } + + X500Name commonAncestor = null; + try { + commonAncestor = new X500Name(ancestor); + } catch (IOException ioe) { + return null; + } + return commonAncestor; + } + + /** + * Constructor object for use by asX500Principal(). + */ + private static final Constructor principalConstructor; + + /** + * Field object for use by asX500Name(). + */ + private static final Field principalField; + + /** + * Retrieve the Constructor and Field we need for reflective access + * and make them accessible. + */ + static { + PrivilegedExceptionAction pa = + new PrivilegedExceptionAction() { + public Object[] run() throws Exception { + Class pClass = X500Principal.class; + Class[] args = new Class[] { X500Name.class }; + Constructor cons = pClass.getDeclaredConstructor(args); + cons.setAccessible(true); + Field field = pClass.getDeclaredField("thisX500Name"); + field.setAccessible(true); + return new Object[] {cons, field}; + } + }; + try { + Object[] result = AccessController.doPrivileged(pa); + @SuppressWarnings("unchecked") + Constructor constr = + (Constructor)result[0]; + principalConstructor = constr; + principalField = (Field)result[1]; + } catch (Exception e) { + throw new InternalError("Could not obtain X500Principal access", e); + } + } + + /** + * Get an X500Principal backed by this X500Name. + * + * Note that we are using privileged reflection to access the hidden + * package private constructor in X500Principal. + */ + public X500Principal asX500Principal() { + if (x500Principal == null) { + try { + Object[] args = new Object[] {this}; + x500Principal = principalConstructor.newInstance(args); + } catch (Exception e) { + throw new RuntimeException("Unexpected exception", e); + } + } + return x500Principal; + } + + /** + * Get the X500Name contained in the given X500Principal. + * + * Note that the X500Name is retrieved using reflection. + */ + public static X500Name asX500Name(X500Principal p) { + try { + X500Name name = (X500Name)principalField.get(p); + name.x500Principal = p; + return name; + } catch (Exception e) { + throw new RuntimeException("Unexpected exception", e); + } + } + +} diff --git a/src/sun/security/x509/X509AttributeName.java b/src/sun/security/x509/X509AttributeName.java new file mode 100644 index 00000000..090792aa --- /dev/null +++ b/src/sun/security/x509/X509AttributeName.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1997, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +/** + * This class is used to parse attribute names like "x509.info.extensions". + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + */ +public class X509AttributeName { + // Public members + private static final char SEPARATOR = '.'; + + // Private data members + private String prefix = null; + private String suffix = null; + + /** + * Default constructor for the class. Name is of the form + * "x509.info.extensions". + * + * @param name the attribute name. + */ + public X509AttributeName(String name) { + int i = name.indexOf(SEPARATOR); + if (i == (-1)) { + prefix = name; + } else { + prefix = name.substring(0, i); + suffix = name.substring(i + 1); + } + } + + /** + * Return the prefix of the name. + */ + public String getPrefix() { + return (prefix); + } + + /** + * Return the suffix of the name. + */ + public String getSuffix() { + return (suffix); + } +} diff --git a/src/sun/security/x509/X509CRLEntryImpl.java b/src/sun/security/x509/X509CRLEntryImpl.java new file mode 100644 index 00000000..ab727099 --- /dev/null +++ b/src/sun/security/x509/X509CRLEntryImpl.java @@ -0,0 +1,537 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.security.cert.CRLException; +import java.security.cert.CRLReason; +import java.security.cert.X509CRLEntry; +import java.math.BigInteger; +import java.util.*; + +import javax.security.auth.x500.X500Principal; + +import sun.security.util.*; +import sun.misc.HexDumpEncoder; + +/** + *

      Abstract class for a revoked certificate in a CRL. + * This class is for each entry in the revokedCertificates, + * so it deals with the inner SEQUENCE. + * The ASN.1 definition for this is: + *

      + * revokedCertificates    SEQUENCE OF SEQUENCE  {
      + *     userCertificate    CertificateSerialNumber,
      + *     revocationDate     ChoiceOfTime,
      + *     crlEntryExtensions Extensions OPTIONAL
      + *                        -- if present, must be v2
      + * }  OPTIONAL
      + *
      + * CertificateSerialNumber  ::=  INTEGER
      + *
      + * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
      + *
      + * Extension  ::=  SEQUENCE  {
      + *     extnId        OBJECT IDENTIFIER,
      + *     critical      BOOLEAN DEFAULT FALSE,
      + *     extnValue     OCTET STRING
      + *                   -- contains a DER encoding of a value
      + *                   -- of the type registered for use with
      + *                   -- the extnId object identifier value
      + * }
      + * 
      + * + * @author Hemma Prafullchandra + */ + +public class X509CRLEntryImpl extends X509CRLEntry + implements Comparable { + + private SerialNumber serialNumber = null; + private Date revocationDate = null; + private CRLExtensions extensions = null; + private byte[] revokedCert = null; + private X500Principal certIssuer; + + private final static boolean isExplicit = false; + private static final long YR_2050 = 2524636800000L; + + /** + * Constructs a revoked certificate entry using the given + * serial number and revocation date. + * + * @param num the serial number of the revoked certificate. + * @param date the Date on which revocation took place. + */ + public X509CRLEntryImpl(BigInteger num, Date date) { + this.serialNumber = new SerialNumber(num); + this.revocationDate = date; + } + + /** + * Constructs a revoked certificate entry using the given + * serial number, revocation date and the entry + * extensions. + * + * @param num the serial number of the revoked certificate. + * @param date the Date on which revocation took place. + * @param crlEntryExts the extensions for this entry. + */ + public X509CRLEntryImpl(BigInteger num, Date date, + CRLExtensions crlEntryExts) { + this.serialNumber = new SerialNumber(num); + this.revocationDate = date; + this.extensions = crlEntryExts; + } + + /** + * Unmarshals a revoked certificate from its encoded form. + * + * @param revokedCert the encoded bytes. + * @exception CRLException on parsing errors. + */ + public X509CRLEntryImpl(byte[] revokedCert) throws CRLException { + try { + parse(new DerValue(revokedCert)); + } catch (IOException e) { + this.revokedCert = null; + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Unmarshals a revoked certificate from its encoded form. + * + * @param derVal the DER value containing the revoked certificate. + * @exception CRLException on parsing errors. + */ + public X509CRLEntryImpl(DerValue derValue) throws CRLException { + try { + parse(derValue); + } catch (IOException e) { + revokedCert = null; + throw new CRLException("Parsing error: " + e.toString()); + } + } + + /** + * Returns true if this revoked certificate entry has + * extensions, otherwise false. + * + * @return true if this CRL entry has extensions, otherwise + * false. + */ + public boolean hasExtensions() { + return (extensions != null); + } + + /** + * Encodes the revoked certificate to an output stream. + * + * @param outStrm an output stream to which the encoded revoked + * certificate is written. + * @exception CRLException on encoding errors. + */ + public void encode(DerOutputStream outStrm) throws CRLException { + try { + if (revokedCert == null) { + DerOutputStream tmp = new DerOutputStream(); + // sequence { serialNumber, revocationDate, extensions } + serialNumber.encode(tmp); + + if (revocationDate.getTime() < YR_2050) { + tmp.putUTCTime(revocationDate); + } else { + tmp.putGeneralizedTime(revocationDate); + } + + if (extensions != null) + extensions.encode(tmp, isExplicit); + + DerOutputStream seq = new DerOutputStream(); + seq.write(DerValue.tag_Sequence, tmp); + + revokedCert = seq.toByteArray(); + } + outStrm.write(revokedCert); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.toString()); + } + } + + /** + * Returns the ASN.1 DER-encoded form of this CRL Entry, + * which corresponds to the inner SEQUENCE. + * + * @exception CRLException if an encoding error occurs. + */ + public byte[] getEncoded() throws CRLException { + return getEncoded0().clone(); + } + + // Called internally to avoid clone + private byte[] getEncoded0() throws CRLException { + if (revokedCert == null) + this.encode(new DerOutputStream()); + return revokedCert; + } + + @Override + public X500Principal getCertificateIssuer() { + return certIssuer; + } + + void setCertificateIssuer(X500Principal crlIssuer, X500Principal certIssuer) { + if (crlIssuer.equals(certIssuer)) { + this.certIssuer = null; + } else { + this.certIssuer = certIssuer; + } + } + + /** + * Gets the serial number from this X509CRLEntry, + * i.e. the userCertificate. + * + * @return the serial number. + */ + public BigInteger getSerialNumber() { + return serialNumber.getNumber(); + } + + /** + * Gets the revocation date from this X509CRLEntry, + * the revocationDate. + * + * @return the revocation date. + */ + public Date getRevocationDate() { + return new Date(revocationDate.getTime()); + } + + /** + * This method is the overridden implementation of the getRevocationReason + * method in X509CRLEntry. It is better performance-wise since it returns + * cached values. + */ + @Override + public CRLReason getRevocationReason() { + Extension ext = getExtension(PKIXExtensions.ReasonCode_Id); + if (ext == null) { + return null; + } + CRLReasonCodeExtension rcExt = (CRLReasonCodeExtension) ext; + return rcExt.getReasonCode(); + } + + /** + * This static method is the default implementation of the + * getRevocationReason method in X509CRLEntry. + */ + public static CRLReason getRevocationReason(X509CRLEntry crlEntry) { + try { + byte[] ext = crlEntry.getExtensionValue("2.5.29.21"); + if (ext == null) { + return null; + } + DerValue val = new DerValue(ext); + byte[] data = val.getOctetString(); + + CRLReasonCodeExtension rcExt = + new CRLReasonCodeExtension(Boolean.FALSE, data); + return rcExt.getReasonCode(); + } catch (IOException ioe) { + return null; + } + } + + /** + * get Reason Code from CRL entry. + * + * @returns Integer or null, if no such extension + * @throws IOException on error + */ + public Integer getReasonCode() throws IOException { + Object obj = getExtension(PKIXExtensions.ReasonCode_Id); + if (obj == null) + return null; + CRLReasonCodeExtension reasonCode = (CRLReasonCodeExtension)obj; + return reasonCode.get(CRLReasonCodeExtension.REASON); + } + + /** + * Returns a printable string of this revoked certificate. + * + * @return value of this revoked certificate in a printable form. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(serialNumber.toString()); + sb.append(" On: " + revocationDate.toString()); + if (certIssuer != null) { + sb.append("\n Certificate issuer: " + certIssuer); + } + if (extensions != null) { + Collection allEntryExts = extensions.getAllExtensions(); + Extension[] exts = allEntryExts.toArray(new Extension[0]); + + sb.append("\n CRL Entry Extensions: " + exts.length); + for (int i = 0; i < exts.length; i++) { + sb.append("\n [" + (i+1) + "]: "); + Extension ext = exts[i]; + try { + if (OIDMap.getClass(ext.getExtensionId()) == null) { + sb.append(ext.toString()); + byte[] extValue = ext.getExtensionValue(); + if (extValue != null) { + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extValue); + extValue = out.toByteArray(); + HexDumpEncoder enc = new HexDumpEncoder(); + sb.append("Extension unknown: " + + "DER encoded OCTET string =\n" + + enc.encodeBuffer(extValue) + "\n"); + } + } else + sb.append(ext.toString()); //sub-class exists + } catch (Exception e) { + sb.append(", Error parsing this extension"); + } + } + } + sb.append("\n"); + return sb.toString(); + } + + /** + * Return true if a critical extension is found that is + * not supported, otherwise return false. + */ + public boolean hasUnsupportedCriticalExtension() { + if (extensions == null) + return false; + return extensions.hasUnsupportedCriticalExtension(); + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in this + * X509CRLEntry. In the returned set, each extension is + * represented by its OID string. + * + * @return a set of the extension oid strings in the + * Object that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (extensions == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : extensions.getAllExtensions()) { + if (ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + return extSet; + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in this + * X509CRLEntry. In the returned set, each extension is + * represented by its OID string. + * + * @return a set of the extension oid strings in the + * Object that are marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (extensions == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : extensions.getAllExtensions()) { + if (!ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + return extSet; + } + + /** + * Gets the DER encoded OCTET string for the extension value + * (extnValue) identified by the passed in oid String. + * The oid string is + * represented by a set of positive whole number separated + * by ".", that means,
      + * <positive whole number>.<positive whole number>.<positive + * whole number>.<...> + * + * @param oid the Object Identifier value for the extension. + * @return the DER encoded octet string of the extension value. + */ + public byte[] getExtensionValue(String oid) { + if (extensions == null) + return null; + try { + String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + Extension crlExt = null; + + if (extAlias == null) { // may be unknown + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null; + ObjectIdentifier inCertOID; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals((Object)findOID)) { + crlExt = ex; + break; + } + } + } else + crlExt = extensions.get(extAlias); + if (crlExt == null) + return null; + byte[] extData = crlExt.getExtensionValue(); + if (extData == null) + return null; + + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + /** + * get an extension + * + * @param oid ObjectIdentifier of extension desired + * @returns Extension of type or null, if not found + */ + public Extension getExtension(ObjectIdentifier oid) { + if (extensions == null) + return null; + + // following returns null if no such OID in map + //XXX consider cloning this + return extensions.get(OIDMap.getName(oid)); + } + + private void parse(DerValue derVal) + throws CRLException, IOException { + + if (derVal.tag != DerValue.tag_Sequence) { + throw new CRLException("Invalid encoded RevokedCertificate, " + + "starting sequence tag missing."); + } + if (derVal.data.available() == 0) + throw new CRLException("No data encoded for RevokedCertificates"); + + revokedCert = derVal.toByteArray(); + // serial number + DerInputStream in = derVal.toDerInputStream(); + DerValue val = in.getDerValue(); + this.serialNumber = new SerialNumber(val); + + // revocationDate + int nextByte = derVal.data.peekByte(); + if ((byte)nextByte == DerValue.tag_UtcTime) { + this.revocationDate = derVal.data.getUTCTime(); + } else if ((byte)nextByte == DerValue.tag_GeneralizedTime) { + this.revocationDate = derVal.data.getGeneralizedTime(); + } else + throw new CRLException("Invalid encoding for revocation date"); + + if (derVal.data.available() == 0) + return; // no extensions + + // crlEntryExtensions + this.extensions = new CRLExtensions(derVal.toDerInputStream()); + } + + /** + * Utility method to convert an arbitrary instance of X509CRLEntry + * to a X509CRLEntryImpl. Does a cast if possible, otherwise reparses + * the encoding. + */ + public static X509CRLEntryImpl toImpl(X509CRLEntry entry) + throws CRLException { + if (entry instanceof X509CRLEntryImpl) { + return (X509CRLEntryImpl)entry; + } else { + return new X509CRLEntryImpl(entry.getEncoded()); + } + } + + /** + * Returns the CertificateIssuerExtension + * + * @return the CertificateIssuerExtension, or null if it does not exist + */ + CertificateIssuerExtension getCertificateIssuerExtension() { + return (CertificateIssuerExtension) + getExtension(PKIXExtensions.CertificateIssuer_Id); + } + + /** + * Returns all extensions for this entry in a map + * @return the extension map, can be empty, but not null + */ + public Map getExtensions() { + if (extensions == null) { + return Collections.emptyMap(); + } + Collection exts = extensions.getAllExtensions(); + Map map = new TreeMap<>(); + for (Extension ext : exts) { + map.put(ext.getId(), ext); + } + return map; + } + + @Override + public int compareTo(X509CRLEntryImpl that) { + int compSerial = getSerialNumber().compareTo(that.getSerialNumber()); + if (compSerial != 0) { + return compSerial; + } + try { + byte[] thisEncoded = this.getEncoded0(); + byte[] thatEncoded = that.getEncoded0(); + for (int i=0; i + * An implementation for X509 CRL (Certificate Revocation List). + *

      + * The X.509 v2 CRL format is described below in ASN.1: + *

      + * CertificateList  ::=  SEQUENCE  {
      + *     tbsCertList          TBSCertList,
      + *     signatureAlgorithm   AlgorithmIdentifier,
      + *     signature            BIT STRING  }
      + * 
      + * More information can be found in + * RFC 3280: Internet X.509 + * Public Key Infrastructure Certificate and CRL Profile. + *

      + * The ASN.1 definition of tbsCertList is: + *

      + * TBSCertList  ::=  SEQUENCE  {
      + *     version                 Version OPTIONAL,
      + *                             -- if present, must be v2
      + *     signature               AlgorithmIdentifier,
      + *     issuer                  Name,
      + *     thisUpdate              ChoiceOfTime,
      + *     nextUpdate              ChoiceOfTime OPTIONAL,
      + *     revokedCertificates     SEQUENCE OF SEQUENCE  {
      + *         userCertificate         CertificateSerialNumber,
      + *         revocationDate          ChoiceOfTime,
      + *         crlEntryExtensions      Extensions OPTIONAL
      + *                                 -- if present, must be v2
      + *         }  OPTIONAL,
      + *     crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
      + *                                  -- if present, must be v2
      + *     }
      + * 
      + * + * @author Hemma Prafullchandra + * @see X509CRL + */ +public class X509CRLImpl extends X509CRL implements DerEncoder { + + // CRL data, and its envelope + private byte[] signedCRL = null; // DER encoded crl + private byte[] signature = null; // raw signature bits + private byte[] tbsCertList = null; // DER encoded "to-be-signed" CRL + private AlgorithmId sigAlgId = null; // sig alg in CRL + + // crl information + private int version; + private AlgorithmId infoSigAlgId; // sig alg in "to-be-signed" crl + private X500Name issuer = null; + private X500Principal issuerPrincipal = null; + private Date thisUpdate = null; + private Date nextUpdate = null; + private Map revokedMap = new TreeMap<>(); + private List revokedList = new LinkedList<>(); + private CRLExtensions extensions = null; + private final static boolean isExplicit = true; + private static final long YR_2050 = 2524636800000L; + + private boolean readOnly = false; + + /** + * PublicKey that has previously been used to successfully verify + * the signature of this CRL. Null if the CRL has not + * yet been verified (successfully). + */ + private PublicKey verifiedPublicKey; + /** + * If verifiedPublicKey is not null, name of the provider used to + * successfully verify the signature of this CRL, or the + * empty String if no provider was explicitly specified. + */ + private String verifiedProvider; + + /** + * Not to be used. As it would lead to cases of uninitialized + * CRL objects. + */ + private X509CRLImpl() { } + + /** + * Unmarshals an X.509 CRL from its encoded form, parsing the encoded + * bytes. This form of constructor is used by agents which + * need to examine and use CRL contents. Note that the buffer + * must include only one CRL, and no "garbage" may be left at + * the end. + * + * @param crlData the encoded bytes, with no trailing padding. + * @exception CRLException on parsing errors. + */ + public X509CRLImpl(byte[] crlData) throws CRLException { + try { + parse(new DerValue(crlData)); + } catch (IOException e) { + signedCRL = null; + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + /** + * Unmarshals an X.509 CRL from an DER value. + * + * @param val a DER value holding at least one CRL + * @exception CRLException on parsing errors. + */ + public X509CRLImpl(DerValue val) throws CRLException { + try { + parse(val); + } catch (IOException e) { + signedCRL = null; + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + /** + * Unmarshals an X.509 CRL from an input stream. Only one CRL + * is expected at the end of the input stream. + * + * @param inStrm an input stream holding at least one CRL + * @exception CRLException on parsing errors. + */ + public X509CRLImpl(InputStream inStrm) throws CRLException { + try { + parse(new DerValue(inStrm)); + } catch (IOException e) { + signedCRL = null; + throw new CRLException("Parsing error: " + e.getMessage()); + } + } + + /** + * Initial CRL constructor, no revoked certs, and no extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate) { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + } + + /** + * CRL constructor, revoked certs, no extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the array of CRL entries. + * + * @exception CRLException on parsing/construction errors. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, + X509CRLEntry[] badCerts) + throws CRLException + { + this.issuer = issuer; + this.thisUpdate = thisDate; + this.nextUpdate = nextDate; + if (badCerts != null) { + X500Principal crlIssuer = getIssuerX500Principal(); + X500Principal badCertIssuer = crlIssuer; + for (int i = 0; i < badCerts.length; i++) { + X509CRLEntryImpl badCert = (X509CRLEntryImpl)badCerts[i]; + try { + badCertIssuer = getCertIssuer(badCert, badCertIssuer); + } catch (IOException ioe) { + throw new CRLException(ioe); + } + badCert.setCertificateIssuer(crlIssuer, badCertIssuer); + X509IssuerSerial issuerSerial = new X509IssuerSerial + (badCertIssuer, badCert.getSerialNumber()); + this.revokedMap.put(issuerSerial, badCert); + this.revokedList.add(badCert); + if (badCert.hasExtensions()) { + this.version = 1; + } + } + } + } + + /** + * CRL constructor, revoked certs and extensions. + * + * @param issuer the name of the CA issuing this CRL. + * @param thisUpdate the Date of this issue. + * @param nextUpdate the Date of the next CRL. + * @param badCerts the array of CRL entries. + * @param crlExts the CRL extensions. + * + * @exception CRLException on parsing/construction errors. + */ + public X509CRLImpl(X500Name issuer, Date thisDate, Date nextDate, + X509CRLEntry[] badCerts, CRLExtensions crlExts) + throws CRLException + { + this(issuer, thisDate, nextDate, badCerts); + if (crlExts != null) { + this.extensions = crlExts; + this.version = 1; + } + } + + /** + * Returned the encoding as an uncloned byte array. Callers must + * guarantee that they neither modify it nor expose it to untrusted + * code. + */ + public byte[] getEncodedInternal() throws CRLException { + if (signedCRL == null) { + throw new CRLException("Null CRL to encode"); + } + return signedCRL; + } + + /** + * Returns the ASN.1 DER encoded form of this CRL. + * + * @exception CRLException if an encoding error occurs. + */ + public byte[] getEncoded() throws CRLException { + return getEncodedInternal().clone(); + } + + /** + * Encodes the "to-be-signed" CRL to the OutputStream. + * + * @param out the OutputStream to write to. + * @exception CRLException on encoding errors. + */ + public void encodeInfo(OutputStream out) throws CRLException { + try { + DerOutputStream tmp = new DerOutputStream(); + DerOutputStream rCerts = new DerOutputStream(); + DerOutputStream seq = new DerOutputStream(); + + if (version != 0) // v2 crl encode version + tmp.putInteger(version); + infoSigAlgId.encode(tmp); + if ((version == 0) && (issuer.toString() == null)) + throw new CRLException("Null Issuer DN not allowed in v1 CRL"); + issuer.encode(tmp); + + if (thisUpdate.getTime() < YR_2050) + tmp.putUTCTime(thisUpdate); + else + tmp.putGeneralizedTime(thisUpdate); + + if (nextUpdate != null) { + if (nextUpdate.getTime() < YR_2050) + tmp.putUTCTime(nextUpdate); + else + tmp.putGeneralizedTime(nextUpdate); + } + + if (!revokedList.isEmpty()) { + for (X509CRLEntry entry : revokedList) { + ((X509CRLEntryImpl)entry).encode(rCerts); + } + tmp.write(DerValue.tag_Sequence, rCerts); + } + + if (extensions != null) + extensions.encode(tmp, isExplicit); + + seq.write(DerValue.tag_Sequence, tmp); + + tbsCertList = seq.toByteArray(); + out.write(tbsCertList); + } catch (IOException e) { + throw new CRLException("Encoding error: " + e.getMessage()); + } + } + + /** + * Verifies that this CRL was signed using the + * private key that corresponds to the given public key. + * + * @param key the PublicKey used to carry out the verification. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CRLException on encoding errors. + */ + public void verify(PublicKey key) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + verify(key, ""); + } + + /** + * Verifies that this CRL was signed using the + * private key that corresponds to the given public key, + * and that the signature verification was computed by + * the given provider. + * + * @param key the PublicKey used to carry out the verification. + * @param sigProvider the name of the signature provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException on encoding errors. + */ + public synchronized void verify(PublicKey key, String sigProvider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + + if (sigProvider == null) { + sigProvider = ""; + } + if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) { + // this CRL has already been successfully verified using + // this public key. Make sure providers match, too. + if (sigProvider.equals(verifiedProvider)) { + return; + } + } + if (signedCRL == null) { + throw new CRLException("Uninitialized CRL"); + } + Signature sigVerf = null; + String sigName = sigAlgId.getName(); + if (sigProvider.length() == 0) { + sigVerf = Signature.getInstance(sigName); + } else { + sigVerf = Signature.getInstance(sigName, sigProvider); + } + + try { + SignatureUtil.initVerifyWithParam(sigVerf, key, + SignatureUtil.getParamSpec(sigName, getSigAlgParams())); + } catch (ProviderException e) { + throw new CRLException(e.getMessage(), e.getCause()); + } catch (InvalidAlgorithmParameterException e) { + throw new CRLException(e); + } + + if (tbsCertList == null) { + throw new CRLException("Uninitialized CRL"); + } + + sigVerf.update(tbsCertList, 0, tbsCertList.length); + + if (!sigVerf.verify(signature)) { + throw new SignatureException("Signature does not match."); + } + verifiedPublicKey = key; + verifiedProvider = sigProvider; + } + + /** + * Verifies that this CRL was signed using the + * private key that corresponds to the given public key, + * and that the signature verification was computed by + * the given provider. Note that the specified Provider object + * does not have to be registered in the provider list. + * + * @param key the PublicKey used to carry out the verification. + * @param sigProvider the signature provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception SignatureException on signature errors. + * @exception CRLException on encoding errors. + */ + public synchronized void verify(PublicKey key, Provider sigProvider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + SignatureException { + + if (signedCRL == null) { + throw new CRLException("Uninitialized CRL"); + } + Signature sigVerf = null; + String sigName = sigAlgId.getName(); + if (sigProvider == null) { + sigVerf = Signature.getInstance(sigName); + } else { + sigVerf = Signature.getInstance(sigName, sigProvider); + } + + try { + SignatureUtil.initVerifyWithParam(sigVerf, key, + SignatureUtil.getParamSpec(sigName, getSigAlgParams())); + } catch (ProviderException e) { + throw new CRLException(e.getMessage(), e.getCause()); + } catch (InvalidAlgorithmParameterException e) { + throw new CRLException(e); + } + + if (tbsCertList == null) { + throw new CRLException("Uninitialized CRL"); + } + + sigVerf.update(tbsCertList, 0, tbsCertList.length); + + if (!sigVerf.verify(signature)) { + throw new SignatureException("Signature does not match."); + } + verifiedPublicKey = key; + } + + /** + * This static method is the default implementation of the + * verify(PublicKey key, Provider sigProvider) method in X509CRL. + * Called from java.security.cert.X509CRL.verify(PublicKey key, + * Provider sigProvider) + */ + public static void verify(X509CRL crl, PublicKey key, + Provider sigProvider) throws CRLException, + NoSuchAlgorithmException, InvalidKeyException, SignatureException { + crl.verify(key, sigProvider); + } + + /** + * Encodes an X.509 CRL, and signs it using the given key. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException if any mandatory data was omitted. + */ + public void sign(PrivateKey key, String algorithm) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + sign(key, algorithm, null); + } + + /** + * Encodes an X.509 CRL, and signs it using the given key. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * @param provider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CRLException if any mandatory data was omitted. + */ + public void sign(PrivateKey key, String algorithm, String provider) + throws CRLException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { + try { + if (readOnly) + throw new CRLException("cannot over-write existing CRL"); + Signature sigEngine = null; + if ((provider == null) || (provider.length() == 0)) + sigEngine = Signature.getInstance(algorithm); + else + sigEngine = Signature.getInstance(algorithm, provider); + + sigEngine.initSign(key); + + // in case the name is reset + sigAlgId = AlgorithmId.get(sigEngine.getAlgorithm()); + infoSigAlgId = sigAlgId; + + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + // encode crl info + encodeInfo(tmp); + + // encode algorithm identifier + sigAlgId.encode(tmp); + + // Create and encode the signature itself. + sigEngine.update(tbsCertList, 0, tbsCertList.length); + signature = sigEngine.sign(); + tmp.putBitString(signature); + + // Wrap the signed data in a SEQUENCE { data, algorithm, sig } + out.write(DerValue.tag_Sequence, tmp); + signedCRL = out.toByteArray(); + readOnly = true; + + } catch (IOException e) { + throw new CRLException("Error while encoding data: " + + e.getMessage()); + } + } + + /** + * Returns a printable string of this CRL. + * + * @return value of this CRL in a printable form. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("X.509 CRL v" + (version+1) + "\n"); + if (sigAlgId != null) + sb.append("Signature Algorithm: " + sigAlgId.toString() + + ", OID=" + (sigAlgId.getOID()).toString() + "\n"); + if (issuer != null) + sb.append("Issuer: " + issuer.toString() + "\n"); + if (thisUpdate != null) + sb.append("\nThis Update: " + thisUpdate.toString() + "\n"); + if (nextUpdate != null) + sb.append("Next Update: " + nextUpdate.toString() + "\n"); + if (revokedList.isEmpty()) + sb.append("\nNO certificates have been revoked\n"); + else { + sb.append("\nRevoked Certificates: " + revokedList.size()); + int i = 1; + for (X509CRLEntry entry: revokedList) { + sb.append("\n[" + i++ + "] " + entry.toString()); + } + } + if (extensions != null) { + Collection allExts = extensions.getAllExtensions(); + Object[] objs = allExts.toArray(); + sb.append("\nCRL Extensions: " + objs.length); + for (int i = 0; i < objs.length; i++) { + sb.append("\n[" + (i+1) + "]: "); + Extension ext = (Extension)objs[i]; + try { + if (OIDMap.getClass(ext.getExtensionId()) == null) { + sb.append(ext.toString()); + byte[] extValue = ext.getExtensionValue(); + if (extValue != null) { + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extValue); + extValue = out.toByteArray(); + HexDumpEncoder enc = new HexDumpEncoder(); + sb.append("Extension unknown: " + + "DER encoded OCTET string =\n" + + enc.encodeBuffer(extValue) + "\n"); + } + } else + sb.append(ext.toString()); // sub-class exists + } catch (Exception e) { + sb.append(", Error parsing this extension"); + } + } + } + if (signature != null) { + HexDumpEncoder encoder = new HexDumpEncoder(); + sb.append("\nSignature:\n" + encoder.encodeBuffer(signature) + + "\n"); + } else + sb.append("NOT signed yet\n"); + return sb.toString(); + } + + /** + * Checks whether the given certificate is on this CRL. + * + * @param cert the certificate to check for. + * @return true if the given certificate is on this CRL, + * false otherwise. + */ + public boolean isRevoked(Certificate cert) { + if (revokedMap.isEmpty() || (!(cert instanceof X509Certificate))) { + return false; + } + X509Certificate xcert = (X509Certificate) cert; + X509IssuerSerial issuerSerial = new X509IssuerSerial(xcert); + return revokedMap.containsKey(issuerSerial); + } + + /** + * Gets the version number from this CRL. + * The ASN.1 definition for this is: + *
      +     * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
      +     *             -- v3 does not apply to CRLs but appears for consistency
      +     *             -- with definition of Version for certs
      +     * 
      + * @return the version number, i.e. 1 or 2. + */ + public int getVersion() { + return version+1; + } + + /** + * Gets the issuer distinguished name from this CRL. + * The issuer name identifies the entity who has signed (and + * issued the CRL). The issuer name field contains an + * X.500 distinguished name (DN). + * The ASN.1 definition for this is: + *
      +     * issuer    Name
      +     *
      +     * Name ::= CHOICE { RDNSequence }
      +     * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
      +     * RelativeDistinguishedName ::=
      +     *     SET OF AttributeValueAssertion
      +     *
      +     * AttributeValueAssertion ::= SEQUENCE {
      +     *                               AttributeType,
      +     *                               AttributeValue }
      +     * AttributeType ::= OBJECT IDENTIFIER
      +     * AttributeValue ::= ANY
      +     * 
      + * The Name describes a hierarchical name composed of attributes, + * such as country name, and corresponding values, such as US. + * The type of the component AttributeValue is determined by the + * AttributeType; in general it will be a directoryString. + * A directoryString is usually one of PrintableString, + * TeletexString or UniversalString. + * @return the issuer name. + */ + public Principal getIssuerDN() { + return (Principal)issuer; + } + + /** + * Return the issuer as X500Principal. Overrides method in X509CRL + * to provide a slightly more efficient version. + */ + public X500Principal getIssuerX500Principal() { + if (issuerPrincipal == null) { + issuerPrincipal = issuer.asX500Principal(); + } + return issuerPrincipal; + } + + /** + * Gets the thisUpdate date from the CRL. + * The ASN.1 definition for this is: + * + * @return the thisUpdate date from the CRL. + */ + public Date getThisUpdate() { + return (new Date(thisUpdate.getTime())); + } + + /** + * Gets the nextUpdate date from the CRL. + * + * @return the nextUpdate date from the CRL, or null if + * not present. + */ + public Date getNextUpdate() { + if (nextUpdate == null) + return null; + return (new Date(nextUpdate.getTime())); + } + + /** + * Gets the CRL entry with the given serial number from this CRL. + * + * @return the entry with the given serial number, or null if + * no such entry exists in the CRL. + * @see X509CRLEntry + */ + public X509CRLEntry getRevokedCertificate(BigInteger serialNumber) { + if (revokedMap.isEmpty()) { + return null; + } + // assume this is a direct CRL entry (cert and CRL issuer are the same) + X509IssuerSerial issuerSerial = new X509IssuerSerial + (getIssuerX500Principal(), serialNumber); + return revokedMap.get(issuerSerial); + } + + /** + * Gets the CRL entry for the given certificate. + */ + public X509CRLEntry getRevokedCertificate(X509Certificate cert) { + if (revokedMap.isEmpty()) { + return null; + } + X509IssuerSerial issuerSerial = new X509IssuerSerial(cert); + return revokedMap.get(issuerSerial); + } + + /** + * Gets all the revoked certificates from the CRL. + * A Set of X509CRLEntry. + * + * @return all the revoked certificates or null if there are + * none. + * @see X509CRLEntry + */ + public Set getRevokedCertificates() { + if (revokedList.isEmpty()) { + return null; + } else { + return new TreeSet(revokedList); + } + } + + /** + * Gets the DER encoded CRL information, the + * tbsCertList from this CRL. + * This can be used to verify the signature independently. + * + * @return the DER encoded CRL information. + * @exception CRLException on encoding errors. + */ + public byte[] getTBSCertList() throws CRLException { + if (tbsCertList == null) + throw new CRLException("Uninitialized CRL"); + byte[] dup = new byte[tbsCertList.length]; + System.arraycopy(tbsCertList, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the raw Signature bits from the CRL. + * + * @return the signature. + */ + public byte[] getSignature() { + if (signature == null) + return null; + byte[] dup = new byte[signature.length]; + System.arraycopy(signature, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the signature algorithm name for the CRL + * signature algorithm. For example, the string "SHA1withDSA". + * The ASN.1 definition for this is: + *
      +     * AlgorithmIdentifier  ::=  SEQUENCE  {
      +     *     algorithm               OBJECT IDENTIFIER,
      +     *     parameters              ANY DEFINED BY algorithm OPTIONAL  }
      +     *                             -- contains a value of the type
      +     *                             -- registered for use with the
      +     *                             -- algorithm object identifier value
      +     * 
      + * + * @return the signature algorithm name. + */ + public String getSigAlgName() { + if (sigAlgId == null) + return null; + return sigAlgId.getName(); + } + + /** + * Gets the signature algorithm OID string from the CRL. + * An OID is represented by a set of positive whole number separated + * by ".", that means,
      + * <positive whole number>.<positive whole number>.<...> + * For example, the string "1.2.840.10040.4.3" identifies the SHA-1 + * with DSA signature algorithm defined in + * RFC 3279: Algorithms and + * Identifiers for the Internet X.509 Public Key Infrastructure Certificate + * and CRL Profile. + * + * @return the signature algorithm oid string. + */ + public String getSigAlgOID() { + if (sigAlgId == null) + return null; + ObjectIdentifier oid = sigAlgId.getOID(); + return oid.toString(); + } + + /** + * Gets the DER encoded signature algorithm parameters from this + * CRL's signature algorithm. In most cases, the signature + * algorithm parameters are null, the parameters are usually + * supplied with the Public Key. + * + * @return the DER encoded signature algorithm parameters, or + * null if no parameters are present. + */ + public byte[] getSigAlgParams() { + if (sigAlgId == null) + return null; + try { + return sigAlgId.getEncodedParams(); + } catch (IOException e) { + return null; + } + } + + /** + * Gets the signature AlgorithmId from the CRL. + * + * @return the signature AlgorithmId + */ + public AlgorithmId getSigAlgId() { + return sigAlgId; + } + + /** + * return the AuthorityKeyIdentifier, if any. + * + * @returns AuthorityKeyIdentifier or null + * (if no AuthorityKeyIdentifierExtension) + * @throws IOException on error + */ + public KeyIdentifier getAuthKeyId() throws IOException { + AuthorityKeyIdentifierExtension aki = getAuthKeyIdExtension(); + if (aki != null) { + KeyIdentifier keyId = (KeyIdentifier)aki.get( + AuthorityKeyIdentifierExtension.KEY_ID); + return keyId; + } else { + return null; + } + } + + /** + * return the AuthorityKeyIdentifierExtension, if any. + * + * @returns AuthorityKeyIdentifierExtension or null (if no such extension) + * @throws IOException on error + */ + public AuthorityKeyIdentifierExtension getAuthKeyIdExtension() + throws IOException { + Object obj = getExtension(PKIXExtensions.AuthorityKey_Id); + return (AuthorityKeyIdentifierExtension)obj; + } + + /** + * return the CRLNumberExtension, if any. + * + * @returns CRLNumberExtension or null (if no such extension) + * @throws IOException on error + */ + public CRLNumberExtension getCRLNumberExtension() throws IOException { + Object obj = getExtension(PKIXExtensions.CRLNumber_Id); + return (CRLNumberExtension)obj; + } + + /** + * return the CRL number from the CRLNumberExtension, if any. + * + * @returns number or null (if no such extension) + * @throws IOException on error + */ + public BigInteger getCRLNumber() throws IOException { + CRLNumberExtension numExt = getCRLNumberExtension(); + if (numExt != null) { + BigInteger num = numExt.get(CRLNumberExtension.NUMBER); + return num; + } else { + return null; + } + } + + /** + * return the DeltaCRLIndicatorExtension, if any. + * + * @returns DeltaCRLIndicatorExtension or null (if no such extension) + * @throws IOException on error + */ + public DeltaCRLIndicatorExtension getDeltaCRLIndicatorExtension() + throws IOException { + + Object obj = getExtension(PKIXExtensions.DeltaCRLIndicator_Id); + return (DeltaCRLIndicatorExtension)obj; + } + + /** + * return the base CRL number from the DeltaCRLIndicatorExtension, if any. + * + * @returns number or null (if no such extension) + * @throws IOException on error + */ + public BigInteger getBaseCRLNumber() throws IOException { + DeltaCRLIndicatorExtension dciExt = getDeltaCRLIndicatorExtension(); + if (dciExt != null) { + BigInteger num = dciExt.get(DeltaCRLIndicatorExtension.NUMBER); + return num; + } else { + return null; + } + } + + /** + * return the IssuerAlternativeNameExtension, if any. + * + * @returns IssuerAlternativeNameExtension or null (if no such extension) + * @throws IOException on error + */ + public IssuerAlternativeNameExtension getIssuerAltNameExtension() + throws IOException { + Object obj = getExtension(PKIXExtensions.IssuerAlternativeName_Id); + return (IssuerAlternativeNameExtension)obj; + } + + /** + * return the IssuingDistributionPointExtension, if any. + * + * @returns IssuingDistributionPointExtension or null + * (if no such extension) + * @throws IOException on error + */ + public IssuingDistributionPointExtension + getIssuingDistributionPointExtension() throws IOException { + + Object obj = getExtension(PKIXExtensions.IssuingDistributionPoint_Id); + return (IssuingDistributionPointExtension) obj; + } + + /** + * Return true if a critical extension is found that is + * not supported, otherwise return false. + */ + public boolean hasUnsupportedCriticalExtension() { + if (extensions == null) + return false; + return extensions.hasUnsupportedCriticalExtension(); + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in the + * CRL. In the returned set, each extension is represented by + * its OID string. + * + * @return a set of the extension oid strings in the + * CRL that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (extensions == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : extensions.getAllExtensions()) { + if (ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + return extSet; + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in the + * CRL. In the returned set, each extension is represented by + * its OID string. + * + * @return a set of the extension oid strings in the + * CRL that are NOT marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (extensions == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : extensions.getAllExtensions()) { + if (!ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + return extSet; + } + + /** + * Gets the DER encoded OCTET string for the extension value + * (extnValue) identified by the passed in oid String. + * The oid string is + * represented by a set of positive whole number separated + * by ".", that means,
      + * <positive whole number>.<positive whole number>.<...> + * + * @param oid the Object Identifier value for the extension. + * @return the der encoded octet string of the extension value. + */ + public byte[] getExtensionValue(String oid) { + if (extensions == null) + return null; + try { + String extAlias = OIDMap.getName(new ObjectIdentifier(oid)); + Extension crlExt = null; + + if (extAlias == null) { // may be unknown + ObjectIdentifier findOID = new ObjectIdentifier(oid); + Extension ex = null; + ObjectIdentifier inCertOID; + for (Enumeration e = extensions.getElements(); + e.hasMoreElements();) { + ex = e.nextElement(); + inCertOID = ex.getExtensionId(); + if (inCertOID.equals((Object)findOID)) { + crlExt = ex; + break; + } + } + } else + crlExt = extensions.get(extAlias); + if (crlExt == null) + return null; + byte[] extData = crlExt.getExtensionValue(); + if (extData == null) + return null; + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + /** + * get an extension + * + * @param oid ObjectIdentifier of extension desired + * @returns Object of type or null, if not found + * @throws IOException on error + */ + public Object getExtension(ObjectIdentifier oid) { + if (extensions == null) + return null; + + // XXX Consider cloning this + return extensions.get(OIDMap.getName(oid)); + } + + /* + * Parses an X.509 CRL, should be used only by constructors. + */ + private void parse(DerValue val) throws CRLException, IOException { + // check if can over write the certificate + if (readOnly) + throw new CRLException("cannot over-write existing CRL"); + + if ( val.getData() == null || val.tag != DerValue.tag_Sequence) + throw new CRLException("Invalid DER-encoded CRL data"); + + signedCRL = val.toByteArray(); + DerValue seq[] = new DerValue[3]; + + seq[0] = val.data.getDerValue(); + seq[1] = val.data.getDerValue(); + seq[2] = val.data.getDerValue(); + + if (val.data.available() != 0) + throw new CRLException("signed overrun, bytes = " + + val.data.available()); + + if (seq[0].tag != DerValue.tag_Sequence) + throw new CRLException("signed CRL fields invalid"); + + sigAlgId = AlgorithmId.parse(seq[1]); + signature = seq[2].getBitString(); + + if (seq[1].data.available() != 0) + throw new CRLException("AlgorithmId field overrun"); + + if (seq[2].data.available() != 0) + throw new CRLException("Signature field overrun"); + + // the tbsCertsList + tbsCertList = seq[0].toByteArray(); + + // parse the information + DerInputStream derStrm = seq[0].data; + DerValue tmp; + byte nextByte; + + // version (optional if v1) + version = 0; // by default, version = v1 == 0 + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_Integer) { + version = derStrm.getInteger(); + if (version != 1) // i.e. v2 + throw new CRLException("Invalid version"); + } + tmp = derStrm.getDerValue(); + + // signature + AlgorithmId tmpId = AlgorithmId.parse(tmp); + + // the "inner" and "outer" signature algorithms must match + if (! tmpId.equals(sigAlgId)) + throw new CRLException("Signature algorithm mismatch"); + infoSigAlgId = tmpId; + + // issuer + issuer = new X500Name(derStrm); + if (issuer.isEmpty()) { + throw new CRLException("Empty issuer DN not allowed in X509CRLs"); + } + + // thisUpdate + // check if UTCTime encoded or GeneralizedTime + + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_UtcTime) { + thisUpdate = derStrm.getUTCTime(); + } else if (nextByte == DerValue.tag_GeneralizedTime) { + thisUpdate = derStrm.getGeneralizedTime(); + } else { + throw new CRLException("Invalid encoding for thisUpdate" + + " (tag=" + nextByte + ")"); + } + + if (derStrm.available() == 0) + return; // done parsing no more optional fields present + + // nextUpdate (optional) + nextByte = (byte)derStrm.peekByte(); + if (nextByte == DerValue.tag_UtcTime) { + nextUpdate = derStrm.getUTCTime(); + } else if (nextByte == DerValue.tag_GeneralizedTime) { + nextUpdate = derStrm.getGeneralizedTime(); + } // else it is not present + + if (derStrm.available() == 0) + return; // done parsing no more optional fields present + + // revokedCertificates (optional) + nextByte = (byte)derStrm.peekByte(); + if ((nextByte == DerValue.tag_SequenceOf) + && (! ((nextByte & 0x0c0) == 0x080))) { + DerValue[] badCerts = derStrm.getSequence(4); + + X500Principal crlIssuer = getIssuerX500Principal(); + X500Principal badCertIssuer = crlIssuer; + for (int i = 0; i < badCerts.length; i++) { + X509CRLEntryImpl entry = new X509CRLEntryImpl(badCerts[i]); + badCertIssuer = getCertIssuer(entry, badCertIssuer); + entry.setCertificateIssuer(crlIssuer, badCertIssuer); + X509IssuerSerial issuerSerial = new X509IssuerSerial + (badCertIssuer, entry.getSerialNumber()); + revokedMap.put(issuerSerial, entry); + revokedList.add(entry); + } + } + + if (derStrm.available() == 0) + return; // done parsing no extensions + + // crlExtensions (optional) + tmp = derStrm.getDerValue(); + if (tmp.isConstructed() && tmp.isContextSpecific((byte)0)) { + extensions = new CRLExtensions(tmp.data); + } + readOnly = true; + } + + /** + * Extract the issuer X500Principal from an X509CRL. Parses the encoded + * form of the CRL to preserve the principal's ASN.1 encoding. + * + * Called by java.security.cert.X509CRL.getIssuerX500Principal(). + */ + public static X500Principal getIssuerX500Principal(X509CRL crl) { + try { + byte[] encoded = crl.getEncoded(); + DerInputStream derIn = new DerInputStream(encoded); + DerValue tbsCert = derIn.getSequence(3)[0]; + DerInputStream tbsIn = tbsCert.data; + + DerValue tmp; + // skip version number if present + byte nextByte = (byte)tbsIn.peekByte(); + if (nextByte == DerValue.tag_Integer) { + tmp = tbsIn.getDerValue(); + } + + tmp = tbsIn.getDerValue(); // skip signature + tmp = tbsIn.getDerValue(); // issuer + byte[] principalBytes = tmp.toByteArray(); + return new X500Principal(principalBytes); + } catch (Exception e) { + throw new RuntimeException("Could not parse issuer", e); + } + } + + /** + * Returned the encoding of the given certificate for internal use. + * Callers must guarantee that they neither modify it nor expose it + * to untrusted code. Uses getEncodedInternal() if the certificate + * is instance of X509CertImpl, getEncoded() otherwise. + */ + public static byte[] getEncodedInternal(X509CRL crl) throws CRLException { + if (crl instanceof X509CRLImpl) { + return ((X509CRLImpl)crl).getEncodedInternal(); + } else { + return crl.getEncoded(); + } + } + + /** + * Utility method to convert an arbitrary instance of X509CRL + * to a X509CRLImpl. Does a cast if possible, otherwise reparses + * the encoding. + */ + public static X509CRLImpl toImpl(X509CRL crl) + throws CRLException { + if (crl instanceof X509CRLImpl) { + return (X509CRLImpl)crl; + } else { + return X509Factory.intern(crl); + } + } + + /** + * Returns the X500 certificate issuer DN of a CRL entry. + * + * @param entry the entry to check + * @param prevCertIssuer the previous entry's certificate issuer + * @return the X500Principal in a CertificateIssuerExtension, or + * prevCertIssuer if it does not exist + */ + private X500Principal getCertIssuer(X509CRLEntryImpl entry, + X500Principal prevCertIssuer) throws IOException { + + CertificateIssuerExtension ciExt = + entry.getCertificateIssuerExtension(); + if (ciExt != null) { + GeneralNames names = ciExt.get(CertificateIssuerExtension.ISSUER); + X500Name issuerDN = (X500Name) names.get(0).getName(); + return issuerDN.asX500Principal(); + } else { + return prevCertIssuer; + } + } + + @Override + public void derEncode(OutputStream out) throws IOException { + if (signedCRL == null) + throw new IOException("Null CRL to encode"); + out.write(signedCRL.clone()); + } + + /** + * Immutable X.509 Certificate Issuer DN and serial number pair + */ + private final static class X509IssuerSerial + implements Comparable { + final X500Principal issuer; + final BigInteger serial; + volatile int hashcode = 0; + + /** + * Create an X509IssuerSerial. + * + * @param issuer the issuer DN + * @param serial the serial number + */ + X509IssuerSerial(X500Principal issuer, BigInteger serial) { + this.issuer = issuer; + this.serial = serial; + } + + /** + * Construct an X509IssuerSerial from an X509Certificate. + */ + X509IssuerSerial(X509Certificate cert) { + this(cert.getIssuerX500Principal(), cert.getSerialNumber()); + } + + /** + * Returns the issuer. + * + * @return the issuer + */ + X500Principal getIssuer() { + return issuer; + } + + /** + * Returns the serial number. + * + * @return the serial number + */ + BigInteger getSerial() { + return serial; + } + + /** + * Compares this X509Serial with another and returns true if they + * are equivalent. + * + * @param o the other object to compare with + * @return true if equal, false otherwise + */ + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof X509IssuerSerial)) { + return false; + } + + X509IssuerSerial other = (X509IssuerSerial) o; + if (serial.equals(other.getSerial()) && + issuer.equals(other.getIssuer())) { + return true; + } + return false; + } + + /** + * Returns a hash code value for this X509IssuerSerial. + * + * @return the hash code value + */ + public int hashCode() { + if (hashcode == 0) { + int result = 17; + result = 37*result + issuer.hashCode(); + result = 37*result + serial.hashCode(); + hashcode = result; + } + return hashcode; + } + + @Override + public int compareTo(X509IssuerSerial another) { + int cissuer = issuer.toString() + .compareTo(another.issuer.toString()); + if (cissuer != 0) return cissuer; + return this.serial.compareTo(another.serial); + } + } +} diff --git a/src/sun/security/x509/X509CertImpl.java b/src/sun/security/x509/X509CertImpl.java new file mode 100644 index 00000000..c974e9e9 --- /dev/null +++ b/src/sun/security/x509/X509CertImpl.java @@ -0,0 +1,2032 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.BufferedReader; +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +import javax.security.auth.x500.X500Principal; + +import sun.misc.HexDumpEncoder; +import java.util.Base64; +import sun.security.util.*; +import sun.security.provider.X509Factory; + +/** + * The X509CertImpl class represents an X.509 certificate. These certificates + * are widely used to support authentication and other functionality in + * Internet security systems. Common applications include Privacy Enhanced + * Mail (PEM), Transport Layer Security (SSL), code signing for trusted + * software distribution, and Secure Electronic Transactions (SET). There + * is a commercial infrastructure ready to manage large scale deployments + * of X.509 identity certificates. + * + *

      These certificates are managed and vouched for by Certificate + * Authorities (CAs). CAs are services which create certificates by + * placing data in the X.509 standard format and then digitally signing + * that data. Such signatures are quite difficult to forge. CAs act as + * trusted third parties, making introductions between agents who have no + * direct knowledge of each other. CA certificates are either signed by + * themselves, or by some other CA such as a "root" CA. + * + *

      RFC 1422 is very informative, though it does not describe much + * of the recent work being done with X.509 certificates. That includes + * a 1996 version (X.509v3) and a variety of enhancements being made to + * facilitate an explosion of personal certificates used as "Internet + * Drivers' Licences", or with SET for credit card transactions. + * + *

      More recent work includes the IETF PKIX Working Group efforts, + * especially RFC2459. + * + * @author Dave Brownell + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see X509CertInfo + */ +public class X509CertImpl extends X509Certificate implements DerEncoder { + + private static final long serialVersionUID = -3457612960190864406L; + + private static final String DOT = "."; + /** + * Public attribute names. + */ + public static final String NAME = "x509"; + public static final String INFO = X509CertInfo.NAME; + public static final String ALG_ID = "algorithm"; + public static final String SIGNATURE = "signature"; + public static final String SIGNED_CERT = "signed_cert"; + + /** + * The following are defined for ease-of-use. These + * are the most frequently retrieved attributes. + */ + // x509.info.subject.dname + public static final String SUBJECT_DN = NAME + DOT + INFO + DOT + + X509CertInfo.SUBJECT + DOT + X509CertInfo.DN_NAME; + // x509.info.issuer.dname + public static final String ISSUER_DN = NAME + DOT + INFO + DOT + + X509CertInfo.ISSUER + DOT + X509CertInfo.DN_NAME; + // x509.info.serialNumber.number + public static final String SERIAL_ID = NAME + DOT + INFO + DOT + + X509CertInfo.SERIAL_NUMBER + DOT + + CertificateSerialNumber.NUMBER; + // x509.info.key.value + public static final String PUBLIC_KEY = NAME + DOT + INFO + DOT + + X509CertInfo.KEY + DOT + + CertificateX509Key.KEY; + + // x509.info.version.value + public static final String VERSION = NAME + DOT + INFO + DOT + + X509CertInfo.VERSION + DOT + + CertificateVersion.VERSION; + + // x509.algorithm + public static final String SIG_ALG = NAME + DOT + ALG_ID; + + // x509.signature + public static final String SIG = NAME + DOT + SIGNATURE; + + // when we sign and decode we set this to true + // this is our means to make certificates immutable + private boolean readOnly = false; + + // Certificate data, and its envelope + private byte[] signedCert = null; + protected X509CertInfo info = null; + protected AlgorithmId algId = null; + protected byte[] signature = null; + + // recognized extension OIDS + private static final String KEY_USAGE_OID = "2.5.29.15"; + private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37"; + private static final String BASIC_CONSTRAINT_OID = "2.5.29.19"; + private static final String SUBJECT_ALT_NAME_OID = "2.5.29.17"; + private static final String ISSUER_ALT_NAME_OID = "2.5.29.18"; + private static final String AUTH_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1"; + + // number of standard key usage bits. + private static final int NUM_STANDARD_KEY_USAGE = 9; + + // SubjectAlterntativeNames cache + private Collection> subjectAlternativeNames; + + // IssuerAlternativeNames cache + private Collection> issuerAlternativeNames; + + // ExtendedKeyUsage cache + private List extKeyUsage; + + // AuthorityInformationAccess cache + private Set authInfoAccess; + + /** + * PublicKey that has previously been used to verify + * the signature of this certificate. Null if the certificate has not + * yet been verified. + */ + private PublicKey verifiedPublicKey; + /** + * If verifiedPublicKey is not null, name of the provider used to + * successfully verify the signature of this certificate, or the + * empty String if no provider was explicitly specified. + */ + private String verifiedProvider; + /** + * If verifiedPublicKey is not null, result of the verification using + * verifiedPublicKey and verifiedProvider. If true, verification was + * successful, if false, it failed. + */ + private boolean verificationResult; + + /** + * Default constructor. + */ + public X509CertImpl() { } + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the more commonly used constructors. Note that the buffer + * must include only a certificate, and no "garbage" may be left at + * the end. If you need to ignore data at the end of a certificate, + * use another constructor. + * + * @param certData the encoded bytes, with no trailing padding. + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(byte[] certData) throws CertificateException { + try { + parse(new DerValue(certData)); + } catch (IOException e) { + signedCert = null; + throw new CertificateException("Unable to initialize, " + e, e); + } + } + + /** + * unmarshals an X.509 certificate from an input stream. If the + * certificate is RFC1421 hex-encoded, then it must begin with + * the line X509Factory.BEGIN_CERT and end with the line + * X509Factory.END_CERT. + * + * @param in an input stream holding at least one certificate that may + * be either DER-encoded or RFC1421 hex-encoded version of the + * DER-encoded certificate. + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(InputStream in) throws CertificateException { + + DerValue der = null; + + BufferedInputStream inBuffered = new BufferedInputStream(in); + + // First try reading stream as HEX-encoded DER-encoded bytes, + // since not mistakable for raw DER + try { + inBuffered.mark(Integer.MAX_VALUE); + der = readRFC1421Cert(inBuffered); + } catch (IOException ioe) { + try { + // Next, try reading stream as raw DER-encoded bytes + inBuffered.reset(); + der = new DerValue(inBuffered); + } catch (IOException ioe1) { + throw new CertificateException("Input stream must be " + + "either DER-encoded bytes " + + "or RFC1421 hex-encoded " + + "DER-encoded bytes: " + + ioe1.getMessage(), ioe1); + } + } + try { + parse(der); + } catch (IOException ioe) { + signedCert = null; + throw new CertificateException("Unable to parse DER value of " + + "certificate, " + ioe, ioe); + } + } + + /** + * read input stream as HEX-encoded DER-encoded bytes + * + * @param in InputStream to read + * @returns DerValue corresponding to decoded HEX-encoded bytes + * @throws IOException if stream can not be interpreted as RFC1421 + * encoded bytes + */ + private DerValue readRFC1421Cert(InputStream in) throws IOException { + DerValue der = null; + String line = null; + BufferedReader certBufferedReader = + new BufferedReader(new InputStreamReader(in, "ASCII")); + try { + line = certBufferedReader.readLine(); + } catch (IOException ioe1) { + throw new IOException("Unable to read InputStream: " + + ioe1.getMessage()); + } + if (line.equals(X509Factory.BEGIN_CERT)) { + /* stream appears to be hex-encoded bytes */ + ByteArrayOutputStream decstream = new ByteArrayOutputStream(); + try { + while ((line = certBufferedReader.readLine()) != null) { + if (line.equals(X509Factory.END_CERT)) { + der = new DerValue(decstream.toByteArray()); + break; + } else { + decstream.write(Base64.getMimeDecoder().decode(line)); + } + } + } catch (IOException ioe2) { + throw new IOException("Unable to read InputStream: " + + ioe2.getMessage()); + } + } else { + throw new IOException("InputStream is not RFC1421 hex-encoded " + + "DER bytes"); + } + return der; + } + + /** + * Construct an initialized X509 Certificate. The certificate is stored + * in raw form and has to be signed to be useful. + * + * @params info the X509CertificateInfo which the Certificate is to be + * created from. + */ + public X509CertImpl(X509CertInfo certInfo) { + this.info = certInfo; + } + + /** + * Unmarshal a certificate from its encoded form, parsing a DER value. + * This form of constructor is used by agents which need to examine + * and use certificate contents. + * + * @param derVal the der value containing the encoded cert. + * @exception CertificateException on parsing and initialization errors. + */ + public X509CertImpl(DerValue derVal) throws CertificateException { + try { + parse(derVal); + } catch (IOException e) { + signedCert = null; + throw new CertificateException("Unable to initialize, " + e, e); + } + } + + /** + * Appends the certificate to an output stream. + * + * @param out an input stream to which the certificate is appended. + * @exception CertificateEncodingException on encoding errors. + */ + public void encode(OutputStream out) + throws CertificateEncodingException { + if (signedCert == null) + throw new CertificateEncodingException( + "Null certificate to encode"); + try { + out.write(signedCert.clone()); + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * DER encode this object onto an output stream. + * Implements the DerEncoder interface. + * + * @param out the output stream on which to write the DER encoding. + * + * @exception IOException on encoding error. + */ + public void derEncode(OutputStream out) throws IOException { + if (signedCert == null) + throw new IOException("Null certificate to encode"); + out.write(signedCert.clone()); + } + + /** + * Returns the encoded form of this certificate. It is + * assumed that each certificate type would have only a single + * form of encoding; for example, X.509 certificates would + * be encoded as ASN.1 DER. + * + * @exception CertificateEncodingException if an encoding error occurs. + */ + public byte[] getEncoded() throws CertificateEncodingException { + return getEncodedInternal().clone(); + } + + /** + * Returned the encoding as an uncloned byte array. Callers must + * guarantee that they neither modify it nor expose it to untrusted + * code. + */ + public byte[] getEncodedInternal() throws CertificateEncodingException { + if (signedCert == null) { + throw new CertificateEncodingException( + "Null certificate to encode"); + } + return signedCert; + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. Successfully verifying a certificate + * does not indicate that one should trust the entity which + * it represents. + * + * @param key the public key used for verification. + * + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void verify(PublicKey key) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + verify(key, ""); + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. Successfully verifying a certificate + * does not indicate that one should trust the entity which + * it represents. + * + * @param key the public key used for verification. + * @param sigProvider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public synchronized void verify(PublicKey key, String sigProvider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + if (sigProvider == null) { + sigProvider = ""; + } + if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) { + // this certificate has already been verified using + // this public key. Make sure providers match, too. + if (sigProvider.equals(verifiedProvider)) { + if (verificationResult) { + return; + } else { + throw new SignatureException("Signature does not match."); + } + } + } + if (signedCert == null) { + throw new CertificateEncodingException("Uninitialized certificate"); + } + // Verify the signature ... + Signature sigVerf = null; + String sigName = algId.getName(); + if (sigProvider.length() == 0) { + sigVerf = Signature.getInstance(sigName); + } else { + sigVerf = Signature.getInstance(sigName, sigProvider); + } + + try { + SignatureUtil.initVerifyWithParam(sigVerf, key, + SignatureUtil.getParamSpec(sigName, getSigAlgParams())); + } catch (ProviderException e) { + throw new CertificateException(e.getMessage(), e.getCause()); + } catch (InvalidAlgorithmParameterException e) { + throw new CertificateException(e); + } + + byte[] rawCert = info.getEncodedInfo(); + sigVerf.update(rawCert, 0, rawCert.length); + + // verify may throw SignatureException for invalid encodings, etc. + verificationResult = sigVerf.verify(signature); + verifiedPublicKey = key; + verifiedProvider = sigProvider; + + if (verificationResult == false) { + throw new SignatureException("Signature does not match."); + } + } + + /** + * Throws an exception if the certificate was not signed using the + * verification key provided. This method uses the signature verification + * engine supplied by the specified provider. Note that the specified + * Provider object does not have to be registered in the provider list. + * Successfully verifying a certificate does not indicate that one + * should trust the entity which it represents. + * + * @param key the public key used for verification. + * @param sigProvider the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public synchronized void verify(PublicKey key, Provider sigProvider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, SignatureException { + if (signedCert == null) { + throw new CertificateEncodingException("Uninitialized certificate"); + } + // Verify the signature ... + Signature sigVerf = null; + String sigName = algId.getName(); + if (sigProvider == null) { + sigVerf = Signature.getInstance(sigName); + } else { + sigVerf = Signature.getInstance(sigName, sigProvider); + } + + try { + SignatureUtil.initVerifyWithParam(sigVerf, key, + SignatureUtil.getParamSpec(sigName, getSigAlgParams())); + } catch (ProviderException e) { + throw new CertificateException(e.getMessage(), e.getCause()); + } catch (InvalidAlgorithmParameterException e) { + throw new CertificateException(e); + } + + byte[] rawCert = info.getEncodedInfo(); + sigVerf.update(rawCert, 0, rawCert.length); + + // verify may throw SignatureException for invalid encodings, etc. + verificationResult = sigVerf.verify(signature); + verifiedPublicKey = key; + + if (verificationResult == false) { + throw new SignatureException("Signature does not match."); + } + } + + /** + * This static method is the default implementation of the + * verify(PublicKey key, Provider sigProvider) method in X509Certificate. + * Called from java.security.cert.X509Certificate.verify(PublicKey key, + * Provider sigProvider) + */ + public static void verify(X509Certificate cert, PublicKey key, + Provider sigProvider) throws CertificateException, + NoSuchAlgorithmException, InvalidKeyException, SignatureException { + cert.verify(key, sigProvider); + } + + /** + * Creates an X.509 certificate, and signs it using the given key + * (associating a signature algorithm and an X.500 name). + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception NoSuchProviderException if there's no default provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void sign(PrivateKey key, String algorithm) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + sign(key, algorithm, null); + } + + /** + * Creates an X.509 certificate, and signs it using the given key + * (associating a signature algorithm and an X.500 name). + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @param key the private key used for signing. + * @param algorithm the name of the signature algorithm used. + * @param provider the name of the provider. + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms. + * @exception InvalidKeyException on incorrect key. + * @exception NoSuchProviderException on incorrect provider. + * @exception SignatureException on signature errors. + * @exception CertificateException on encoding errors. + */ + public void sign(PrivateKey key, String algorithm, String provider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { + try { + sign(key, null, algorithm, provider); + } catch (InvalidAlgorithmParameterException e) { + // should not happen; re-throw just in case + throw new SignatureException(e); + } + } + + /** + * Creates an X.509 certificate, and signs it using the given key + * (associating a signature algorithm and an X.500 name), signature + * parameters, and security provider. If the given provider name + * is null or empty, the implementation look up will be based on + * provider configurations. + * This operation is used to implement the certificate generation + * functionality of a certificate authority. + * + * @param key the private key used for signing + * @param signingParams the parameters used for signing + * @param algorithm the name of the signature algorithm used + * @param provider the name of the provider, may be null + * + * @exception NoSuchAlgorithmException on unsupported signature + * algorithms + * @exception InvalidKeyException on incorrect key + * @exception InvalidAlgorithmParameterException on invalid signature + * parameters + * @exception NoSuchProviderException on incorrect provider + * @exception SignatureException on signature errors + * @exception CertificateException on encoding errors + */ + public void sign(PrivateKey key, AlgorithmParameterSpec signingParams, + String algorithm, String provider) + throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, InvalidAlgorithmParameterException, + NoSuchProviderException, SignatureException { + try { + if (readOnly) { + throw new CertificateEncodingException( + "cannot over-write existing certificate"); + } + Signature sigEngine = null; + if ((provider == null) || (provider.length() == 0)) { + sigEngine = Signature.getInstance(algorithm); + } else { + sigEngine = Signature.getInstance(algorithm, provider); + } + + SignatureUtil.initSignWithParam(sigEngine, key, signingParams, + null); + + // in case the name is reset + if (signingParams != null) { + algId = AlgorithmId.get(sigEngine.getParameters()); + } else { + algId = AlgorithmId.get(algorithm); + } + DerOutputStream out = new DerOutputStream(); + DerOutputStream tmp = new DerOutputStream(); + + // encode certificate info + info.encode(tmp); + byte[] rawCert = tmp.toByteArray(); + + // encode algorithm identifier + algId.encode(tmp); + + // Create and encode the signature itself. + sigEngine.update(rawCert, 0, rawCert.length); + signature = sigEngine.sign(); + tmp.putBitString(signature); + + // Wrap the signed data in a SEQUENCE { data, algorithm, sig } + out.write(DerValue.tag_Sequence, tmp); + signedCert = out.toByteArray(); + readOnly = true; + + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * Checks that the certificate is currently valid, i.e. the current + * time is within the specified validity period. + * + * @exception CertificateExpiredException if the certificate has expired. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid. + */ + public void checkValidity() + throws CertificateExpiredException, CertificateNotYetValidException { + Date date = new Date(); + checkValidity(date); + } + + /** + * Checks that the specified date is within the certificate's + * validity period, or basically if the certificate would be + * valid at the specified date/time. + * + * @param date the Date to check against to see if this certificate + * is valid at that date/time. + * + * @exception CertificateExpiredException if the certificate has expired + * with respect to the date supplied. + * @exception CertificateNotYetValidException if the certificate is not + * yet valid with respect to the date supplied. + */ + public void checkValidity(Date date) + throws CertificateExpiredException, CertificateNotYetValidException { + + CertificateValidity interval = null; + try { + interval = (CertificateValidity)info.get(CertificateValidity.NAME); + } catch (Exception e) { + throw new CertificateNotYetValidException("Incorrect validity period"); + } + if (interval == null) + throw new CertificateNotYetValidException("Null validity period"); + interval.valid(date); + } + + /** + * Return the requested attribute from the certificate. + * + * Note that the X509CertInfo is not cloned for performance reasons. + * Callers must ensure that they do not modify it. All other + * attributes are cloned. + * + * @param name the name of the attribute. + * @exception CertificateParsingException on invalid attribute identifier. + */ + public Object get(String name) + throws CertificateParsingException { + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateParsingException("Invalid root of " + + "attribute name, expected [" + NAME + + "], received " + "[" + id + "]"); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (info == null) { + return null; + } + if (attr.getSuffix() != null) { + try { + return info.get(attr.getSuffix()); + } catch (IOException e) { + throw new CertificateParsingException(e.toString()); + } catch (CertificateException e) { + throw new CertificateParsingException(e.toString()); + } + } else { + return info; + } + } else if (id.equalsIgnoreCase(ALG_ID)) { + return(algId); + } else if (id.equalsIgnoreCase(SIGNATURE)) { + if (signature != null) + return signature.clone(); + else + return null; + } else if (id.equalsIgnoreCase(SIGNED_CERT)) { + if (signedCert != null) + return signedCert.clone(); + else + return null; + } else { + throw new CertificateParsingException("Attribute name not " + + "recognized or get() not allowed for the same: " + id); + } + } + + /** + * Set the requested attribute in the certificate. + * + * @param name the name of the attribute. + * @param obj the value of the attribute. + * @exception CertificateException on invalid attribute identifier. + * @exception IOException on encoding error of attribute. + */ + public void set(String name, Object obj) + throws CertificateException, IOException { + // check if immutable + if (readOnly) + throw new CertificateException("cannot over-write existing" + + " certificate"); + + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateException("Invalid root of attribute name," + + " expected [" + NAME + "], received " + id); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (attr.getSuffix() == null) { + if (!(obj instanceof X509CertInfo)) { + throw new CertificateException("Attribute value should" + + " be of type X509CertInfo."); + } + info = (X509CertInfo)obj; + signedCert = null; //reset this as certificate data has changed + } else { + info.set(attr.getSuffix(), obj); + signedCert = null; //reset this as certificate data has changed + } + } else { + throw new CertificateException("Attribute name not recognized or " + + "set() not allowed for the same: " + id); + } + } + + /** + * Delete the requested attribute from the certificate. + * + * @param name the name of the attribute. + * @exception CertificateException on invalid attribute identifier. + * @exception IOException on other errors. + */ + public void delete(String name) + throws CertificateException, IOException { + // check if immutable + if (readOnly) + throw new CertificateException("cannot over-write existing" + + " certificate"); + + X509AttributeName attr = new X509AttributeName(name); + String id = attr.getPrefix(); + if (!(id.equalsIgnoreCase(NAME))) { + throw new CertificateException("Invalid root of attribute name," + + " expected [" + + NAME + "], received " + id); + } + attr = new X509AttributeName(attr.getSuffix()); + id = attr.getPrefix(); + + if (id.equalsIgnoreCase(INFO)) { + if (attr.getSuffix() != null) { + info = null; + } else { + info.delete(attr.getSuffix()); + } + } else if (id.equalsIgnoreCase(ALG_ID)) { + algId = null; + } else if (id.equalsIgnoreCase(SIGNATURE)) { + signature = null; + } else if (id.equalsIgnoreCase(SIGNED_CERT)) { + signedCert = null; + } else { + throw new CertificateException("Attribute name not recognized or " + + "delete() not allowed for the same: " + id); + } + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(NAME + DOT + INFO); + elements.addElement(NAME + DOT + ALG_ID); + elements.addElement(NAME + DOT + SIGNATURE); + elements.addElement(NAME + DOT + SIGNED_CERT); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Returns a printable representation of the certificate. This does not + * contain all the information available to distinguish this from any + * other certificate. The certificate must be fully constructed + * before this function may be called. + */ + public String toString() { + if (info == null || algId == null || signature == null) + return ""; + + StringBuilder sb = new StringBuilder(); + + sb.append("[\n"); + sb.append(info.toString() + "\n"); + sb.append(" Algorithm: [" + algId.toString() + "]\n"); + + HexDumpEncoder encoder = new HexDumpEncoder(); + sb.append(" Signature:\n" + encoder.encodeBuffer(signature)); + sb.append("\n]"); + + return sb.toString(); + } + + // the strongly typed gets, as per java.security.cert.X509Certificate + + /** + * Gets the publickey from this certificate. + * + * @return the publickey. + */ + public PublicKey getPublicKey() { + if (info == null) + return null; + try { + PublicKey key = (PublicKey)info.get(CertificateX509Key.NAME + + DOT + CertificateX509Key.KEY); + return key; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the version number from the certificate. + * + * @return the version number, i.e. 1, 2 or 3. + */ + public int getVersion() { + if (info == null) + return -1; + try { + int vers = ((Integer)info.get(CertificateVersion.NAME + + DOT + CertificateVersion.VERSION)).intValue(); + return vers+1; + } catch (Exception e) { + return -1; + } + } + + /** + * Gets the serial number from the certificate. + * + * @return the serial number. + */ + public BigInteger getSerialNumber() { + SerialNumber ser = getSerialNumberObject(); + + return ser != null ? ser.getNumber() : null; + } + + /** + * Gets the serial number from the certificate as + * a SerialNumber object. + * + * @return the serial number. + */ + public SerialNumber getSerialNumberObject() { + if (info == null) + return null; + try { + SerialNumber ser = (SerialNumber)info.get( + CertificateSerialNumber.NAME + DOT + + CertificateSerialNumber.NUMBER); + return ser; + } catch (Exception e) { + return null; + } + } + + + /** + * Gets the subject distinguished name from the certificate. + * + * @return the subject name. + */ + public Principal getSubjectDN() { + if (info == null) + return null; + try { + Principal subject = (Principal)info.get(X509CertInfo.SUBJECT + DOT + + X509CertInfo.DN_NAME); + return subject; + } catch (Exception e) { + return null; + } + } + + /** + * Get subject name as X500Principal. Overrides implementation in + * X509Certificate with a slightly more efficient version that is + * also aware of X509CertImpl mutability. + */ + public X500Principal getSubjectX500Principal() { + if (info == null) { + return null; + } + try { + X500Principal subject = (X500Principal)info.get( + X509CertInfo.SUBJECT + DOT + + "x500principal"); + return subject; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the issuer distinguished name from the certificate. + * + * @return the issuer name. + */ + public Principal getIssuerDN() { + if (info == null) + return null; + try { + Principal issuer = (Principal)info.get(X509CertInfo.ISSUER + DOT + + X509CertInfo.DN_NAME); + return issuer; + } catch (Exception e) { + return null; + } + } + + /** + * Get issuer name as X500Principal. Overrides implementation in + * X509Certificate with a slightly more efficient version that is + * also aware of X509CertImpl mutability. + */ + public X500Principal getIssuerX500Principal() { + if (info == null) { + return null; + } + try { + X500Principal issuer = (X500Principal)info.get( + X509CertInfo.ISSUER + DOT + + "x500principal"); + return issuer; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the notBefore date from the validity period of the certificate. + * + * @return the start date of the validity period. + */ + public Date getNotBefore() { + if (info == null) + return null; + try { + Date d = (Date) info.get(CertificateValidity.NAME + DOT + + CertificateValidity.NOT_BEFORE); + return d; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the notAfter date from the validity period of the certificate. + * + * @return the end date of the validity period. + */ + public Date getNotAfter() { + if (info == null) + return null; + try { + Date d = (Date) info.get(CertificateValidity.NAME + DOT + + CertificateValidity.NOT_AFTER); + return d; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the DER encoded certificate informations, the + * tbsCertificate from this certificate. + * This can be used to verify the signature independently. + * + * @return the DER encoded certificate information. + * @exception CertificateEncodingException if an encoding error occurs. + */ + public byte[] getTBSCertificate() throws CertificateEncodingException { + if (info != null) { + return info.getEncodedInfo(); + } else + throw new CertificateEncodingException("Uninitialized certificate"); + } + + /** + * Gets the raw Signature bits from the certificate. + * + * @return the signature. + */ + public byte[] getSignature() { + if (signature == null) + return null; + byte[] dup = new byte[signature.length]; + System.arraycopy(signature, 0, dup, 0, dup.length); + return dup; + } + + /** + * Gets the signature algorithm name for the certificate + * signature algorithm. + * For example, the string "SHA-1/DSA" or "DSS". + * + * @return the signature algorithm name. + */ + public String getSigAlgName() { + if (algId == null) + return null; + return (algId.getName()); + } + + /** + * Gets the signature algorithm OID string from the certificate. + * For example, the string "1.2.840.10040.4.3" + * + * @return the signature algorithm oid string. + */ + public String getSigAlgOID() { + if (algId == null) + return null; + ObjectIdentifier oid = algId.getOID(); + return (oid.toString()); + } + + /** + * Gets the DER encoded signature algorithm parameters from this + * certificate's signature algorithm. + * + * @return the DER encoded signature algorithm parameters, or + * null if no parameters are present. + */ + public byte[] getSigAlgParams() { + if (algId == null) + return null; + try { + return algId.getEncodedParams(); + } catch (IOException e) { + return null; + } + } + + /** + * Gets the Issuer Unique Identity from the certificate. + * + * @return the Issuer Unique Identity. + */ + public boolean[] getIssuerUniqueID() { + if (info == null) + return null; + try { + UniqueIdentity id = (UniqueIdentity)info.get( + X509CertInfo.ISSUER_ID); + if (id == null) + return null; + else + return (id.getId()); + } catch (Exception e) { + return null; + } + } + + /** + * Gets the Subject Unique Identity from the certificate. + * + * @return the Subject Unique Identity. + */ + public boolean[] getSubjectUniqueID() { + if (info == null) + return null; + try { + UniqueIdentity id = (UniqueIdentity)info.get( + X509CertInfo.SUBJECT_ID); + if (id == null) + return null; + else + return (id.getId()); + } catch (Exception e) { + return null; + } + } + + public KeyIdentifier getAuthKeyId() { + AuthorityKeyIdentifierExtension aki + = getAuthorityKeyIdentifierExtension(); + if (aki != null) { + try { + return (KeyIdentifier)aki.get( + AuthorityKeyIdentifierExtension.KEY_ID); + } catch (IOException ioe) {} // not possible + } + return null; + } + + /** + * Returns the subject's key identifier, or null + */ + public KeyIdentifier getSubjectKeyId() { + SubjectKeyIdentifierExtension ski = getSubjectKeyIdentifierExtension(); + if (ski != null) { + try { + return (KeyIdentifier)ski.get( + SubjectKeyIdentifierExtension.KEY_ID); + } catch (IOException ioe) {} // not possible + } + return null; + } + + /** + * Get AuthorityKeyIdentifier extension + * @return AuthorityKeyIdentifier object or null (if no such object + * in certificate) + */ + public AuthorityKeyIdentifierExtension getAuthorityKeyIdentifierExtension() + { + return (AuthorityKeyIdentifierExtension) + getExtension(PKIXExtensions.AuthorityKey_Id); + } + + /** + * Get BasicConstraints extension + * @return BasicConstraints object or null (if no such object in + * certificate) + */ + public BasicConstraintsExtension getBasicConstraintsExtension() { + return (BasicConstraintsExtension) + getExtension(PKIXExtensions.BasicConstraints_Id); + } + + /** + * Get CertificatePoliciesExtension + * @return CertificatePoliciesExtension or null (if no such object in + * certificate) + */ + public CertificatePoliciesExtension getCertificatePoliciesExtension() { + return (CertificatePoliciesExtension) + getExtension(PKIXExtensions.CertificatePolicies_Id); + } + + /** + * Get ExtendedKeyUsage extension + * @return ExtendedKeyUsage extension object or null (if no such object + * in certificate) + */ + public ExtendedKeyUsageExtension getExtendedKeyUsageExtension() { + return (ExtendedKeyUsageExtension) + getExtension(PKIXExtensions.ExtendedKeyUsage_Id); + } + + /** + * Get IssuerAlternativeName extension + * @return IssuerAlternativeName object or null (if no such object in + * certificate) + */ + public IssuerAlternativeNameExtension getIssuerAlternativeNameExtension() { + return (IssuerAlternativeNameExtension) + getExtension(PKIXExtensions.IssuerAlternativeName_Id); + } + + /** + * Get NameConstraints extension + * @return NameConstraints object or null (if no such object in certificate) + */ + public NameConstraintsExtension getNameConstraintsExtension() { + return (NameConstraintsExtension) + getExtension(PKIXExtensions.NameConstraints_Id); + } + + /** + * Get PolicyConstraints extension + * @return PolicyConstraints object or null (if no such object in + * certificate) + */ + public PolicyConstraintsExtension getPolicyConstraintsExtension() { + return (PolicyConstraintsExtension) + getExtension(PKIXExtensions.PolicyConstraints_Id); + } + + /** + * Get PolicyMappingsExtension extension + * @return PolicyMappingsExtension object or null (if no such object + * in certificate) + */ + public PolicyMappingsExtension getPolicyMappingsExtension() { + return (PolicyMappingsExtension) + getExtension(PKIXExtensions.PolicyMappings_Id); + } + + /** + * Get PrivateKeyUsage extension + * @return PrivateKeyUsage object or null (if no such object in certificate) + */ + public PrivateKeyUsageExtension getPrivateKeyUsageExtension() { + return (PrivateKeyUsageExtension) + getExtension(PKIXExtensions.PrivateKeyUsage_Id); + } + + /** + * Get SubjectAlternativeName extension + * @return SubjectAlternativeName object or null (if no such object in + * certificate) + */ + public SubjectAlternativeNameExtension getSubjectAlternativeNameExtension() + { + return (SubjectAlternativeNameExtension) + getExtension(PKIXExtensions.SubjectAlternativeName_Id); + } + + /** + * Get SubjectKeyIdentifier extension + * @return SubjectKeyIdentifier object or null (if no such object in + * certificate) + */ + public SubjectKeyIdentifierExtension getSubjectKeyIdentifierExtension() { + return (SubjectKeyIdentifierExtension) + getExtension(PKIXExtensions.SubjectKey_Id); + } + + /** + * Get CRLDistributionPoints extension + * @return CRLDistributionPoints object or null (if no such object in + * certificate) + */ + public CRLDistributionPointsExtension getCRLDistributionPointsExtension() { + return (CRLDistributionPointsExtension) + getExtension(PKIXExtensions.CRLDistributionPoints_Id); + } + + /** + * Return true if a critical extension is found that is + * not supported, otherwise return false. + */ + public boolean hasUnsupportedCriticalExtension() { + if (info == null) + return false; + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) + return false; + return exts.hasUnsupportedCriticalExtension(); + } catch (Exception e) { + return false; + } + } + + /** + * Gets a Set of the extension(s) marked CRITICAL in the + * certificate. In the returned set, each extension is + * represented by its OID string. + * + * @return a set of the extension oid strings in the + * certificate that are marked critical. + */ + public Set getCriticalExtensionOIDs() { + if (info == null) { + return null; + } + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : exts.getAllExtensions()) { + if (ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + return extSet; + } catch (Exception e) { + return null; + } + } + + /** + * Gets a Set of the extension(s) marked NON-CRITICAL in the + * certificate. In the returned set, each extension is + * represented by its OID string. + * + * @return a set of the extension oid strings in the + * certificate that are NOT marked critical. + */ + public Set getNonCriticalExtensionOIDs() { + if (info == null) { + return null; + } + try { + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + if (exts == null) { + return null; + } + Set extSet = new TreeSet<>(); + for (Extension ex : exts.getAllExtensions()) { + if (!ex.isCritical()) { + extSet.add(ex.getExtensionId().toString()); + } + } + extSet.addAll(exts.getUnparseableExtensions().keySet()); + return extSet; + } catch (Exception e) { + return null; + } + } + + /** + * Gets the extension identified by the given ObjectIdentifier + * + * @param oid the Object Identifier value for the extension. + * @return Extension or null if certificate does not contain this + * extension + */ + public Extension getExtension(ObjectIdentifier oid) { + if (info == null) { + return null; + } + try { + CertificateExtensions extensions; + try { + extensions = (CertificateExtensions)info.get(CertificateExtensions.NAME); + } catch (CertificateException ce) { + return null; + } + if (extensions == null) { + return null; + } else { + Extension ex = extensions.getExtension(oid.toString()); + if (ex != null) { + return ex; + } + for (Extension ex2: extensions.getAllExtensions()) { + if (ex2.getExtensionId().equals((Object)oid)) { + //XXXX May want to consider cloning this + return ex2; + } + } + /* no such extension in this certificate */ + return null; + } + } catch (IOException ioe) { + return null; + } + } + + public Extension getUnparseableExtension(ObjectIdentifier oid) { + if (info == null) { + return null; + } + try { + CertificateExtensions extensions; + try { + extensions = (CertificateExtensions)info.get(CertificateExtensions.NAME); + } catch (CertificateException ce) { + return null; + } + if (extensions == null) { + return null; + } else { + return extensions.getUnparseableExtensions().get(oid.toString()); + } + } catch (IOException ioe) { + return null; + } + } + + /** + * Gets the DER encoded extension identified by the given + * oid String. + * + * @param oid the Object Identifier value for the extension. + */ + public byte[] getExtensionValue(String oid) { + try { + ObjectIdentifier findOID = new ObjectIdentifier(oid); + String extAlias = OIDMap.getName(findOID); + Extension certExt = null; + CertificateExtensions exts = (CertificateExtensions)info.get( + CertificateExtensions.NAME); + + if (extAlias == null) { // may be unknown + // get the extensions, search thru' for this oid + if (exts == null) { + return null; + } + + for (Extension ex : exts.getAllExtensions()) { + ObjectIdentifier inCertOID = ex.getExtensionId(); + if (inCertOID.equals((Object)findOID)) { + certExt = ex; + break; + } + } + } else { // there's sub-class that can handle this extension + try { + certExt = (Extension)this.get(extAlias); + } catch (CertificateException e) { + // get() throws an Exception instead of returning null, ignore + } + } + if (certExt == null) { + if (exts != null) { + certExt = exts.getUnparseableExtensions().get(oid); + } + if (certExt == null) { + return null; + } + } + byte[] extData = certExt.getExtensionValue(); + if (extData == null) { + return null; + } + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extData); + return out.toByteArray(); + } catch (Exception e) { + return null; + } + } + + /** + * Get a boolean array representing the bits of the KeyUsage extension, + * (oid = 2.5.29.15). + * @return the bit values of this extension as an array of booleans. + */ + public boolean[] getKeyUsage() { + try { + String extAlias = OIDMap.getName(PKIXExtensions.KeyUsage_Id); + if (extAlias == null) + return null; + + KeyUsageExtension certExt = (KeyUsageExtension)this.get(extAlias); + if (certExt == null) + return null; + + boolean[] ret = certExt.getBits(); + if (ret.length < NUM_STANDARD_KEY_USAGE) { + boolean[] usageBits = new boolean[NUM_STANDARD_KEY_USAGE]; + System.arraycopy(ret, 0, usageBits, 0, ret.length); + ret = usageBits; + } + return ret; + } catch (Exception e) { + return null; + } + } + + /** + * This method are the overridden implementation of + * getExtendedKeyUsage method in X509Certificate in the Sun + * provider. It is better performance-wise since it returns cached + * values. + */ + public synchronized List getExtendedKeyUsage() + throws CertificateParsingException { + if (readOnly && extKeyUsage != null) { + return extKeyUsage; + } else { + ExtendedKeyUsageExtension ext = getExtendedKeyUsageExtension(); + if (ext == null) { + return null; + } + extKeyUsage = + Collections.unmodifiableList(ext.getExtendedKeyUsage()); + return extKeyUsage; + } + } + + /** + * This static method is the default implementation of the + * getExtendedKeyUsage method in X509Certificate. A + * X509Certificate provider generally should overwrite this to + * provide among other things caching for better performance. + */ + public static List getExtendedKeyUsage(X509Certificate cert) + throws CertificateParsingException { + try { + byte[] ext = cert.getExtensionValue(EXTENDED_KEY_USAGE_OID); + if (ext == null) + return null; + DerValue val = new DerValue(ext); + byte[] data = val.getOctetString(); + + ExtendedKeyUsageExtension ekuExt = + new ExtendedKeyUsageExtension(Boolean.FALSE, data); + return Collections.unmodifiableList(ekuExt.getExtendedKeyUsage()); + } catch (IOException ioe) { + throw new CertificateParsingException(ioe); + } + } + + /** + * Get the certificate constraints path length from the + * the critical BasicConstraints extension, (oid = 2.5.29.19). + * @return the length of the constraint. + */ + public int getBasicConstraints() { + try { + String extAlias = OIDMap.getName(PKIXExtensions.BasicConstraints_Id); + if (extAlias == null) + return -1; + BasicConstraintsExtension certExt = + (BasicConstraintsExtension)this.get(extAlias); + if (certExt == null) + return -1; + + if (((Boolean)certExt.get(BasicConstraintsExtension.IS_CA) + ).booleanValue() == true) + return ((Integer)certExt.get( + BasicConstraintsExtension.PATH_LEN)).intValue(); + else + return -1; + } catch (Exception e) { + return -1; + } + } + + /** + * Converts a GeneralNames structure into an immutable Collection of + * alternative names (subject or issuer) in the form required by + * {@link #getSubjectAlternativeNames} or + * {@link #getIssuerAlternativeNames}. + * + * @param names the GeneralNames to be converted + * @return an immutable Collection of alternative names + */ + private static Collection> makeAltNames(GeneralNames names) { + if (names.isEmpty()) { + return Collections.>emptySet(); + } + List> newNames = new ArrayList<>(); + for (GeneralName gname : names.names()) { + GeneralNameInterface name = gname.getName(); + List nameEntry = new ArrayList<>(2); + nameEntry.add(Integer.valueOf(name.getType())); + switch (name.getType()) { + case GeneralNameInterface.NAME_RFC822: + nameEntry.add(((RFC822Name) name).getName()); + break; + case GeneralNameInterface.NAME_DNS: + nameEntry.add(((DNSName) name).getName()); + break; + case GeneralNameInterface.NAME_DIRECTORY: + nameEntry.add(((X500Name) name).getRFC2253Name()); + break; + case GeneralNameInterface.NAME_URI: + nameEntry.add(((URIName) name).getName()); + break; + case GeneralNameInterface.NAME_IP: + try { + nameEntry.add(((IPAddressName) name).getName()); + } catch (IOException ioe) { + // IPAddressName in cert is bogus + throw new RuntimeException("IPAddress cannot be parsed", + ioe); + } + break; + case GeneralNameInterface.NAME_OID: + nameEntry.add(((OIDName) name).getOID().toString()); + break; + default: + // add DER encoded form + DerOutputStream derOut = new DerOutputStream(); + try { + name.encode(derOut); + } catch (IOException ioe) { + // should not occur since name has already been decoded + // from cert (this would indicate a bug in our code) + throw new RuntimeException("name cannot be encoded", ioe); + } + nameEntry.add(derOut.toByteArray()); + break; + } + newNames.add(Collections.unmodifiableList(nameEntry)); + } + return Collections.unmodifiableCollection(newNames); + } + + /** + * Checks a Collection of altNames and clones any name entries of type + * byte []. + */ // only partially generified due to javac bug + private static Collection> cloneAltNames(Collection> altNames) { + boolean mustClone = false; + for (List nameEntry : altNames) { + if (nameEntry.get(1) instanceof byte[]) { + // must clone names + mustClone = true; + } + } + if (mustClone) { + List> namesCopy = new ArrayList<>(); + for (List nameEntry : altNames) { + Object nameObject = nameEntry.get(1); + if (nameObject instanceof byte[]) { + List nameEntryCopy = + new ArrayList<>(nameEntry); + nameEntryCopy.set(1, ((byte[])nameObject).clone()); + namesCopy.add(Collections.unmodifiableList(nameEntryCopy)); + } else { + namesCopy.add(nameEntry); + } + } + return Collections.unmodifiableCollection(namesCopy); + } else { + return altNames; + } + } + + /** + * This method are the overridden implementation of + * getSubjectAlternativeNames method in X509Certificate in the Sun + * provider. It is better performance-wise since it returns cached + * values. + */ + public synchronized Collection> getSubjectAlternativeNames() + throws CertificateParsingException { + // return cached value if we can + if (readOnly && subjectAlternativeNames != null) { + return cloneAltNames(subjectAlternativeNames); + } + SubjectAlternativeNameExtension subjectAltNameExt = + getSubjectAlternativeNameExtension(); + if (subjectAltNameExt == null) { + return null; + } + GeneralNames names; + try { + names = subjectAltNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + } catch (IOException ioe) { + // should not occur + return Collections.>emptySet(); + } + subjectAlternativeNames = makeAltNames(names); + return subjectAlternativeNames; + } + + /** + * This static method is the default implementation of the + * getSubjectAlternaitveNames method in X509Certificate. A + * X509Certificate provider generally should overwrite this to + * provide among other things caching for better performance. + */ + public static Collection> getSubjectAlternativeNames(X509Certificate cert) + throws CertificateParsingException { + try { + byte[] ext = cert.getExtensionValue(SUBJECT_ALT_NAME_OID); + if (ext == null) { + return null; + } + DerValue val = new DerValue(ext); + byte[] data = val.getOctetString(); + + SubjectAlternativeNameExtension subjectAltNameExt = + new SubjectAlternativeNameExtension(Boolean.FALSE, + data); + + GeneralNames names; + try { + names = subjectAltNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + } catch (IOException ioe) { + // should not occur + return Collections.>emptySet(); + } + return makeAltNames(names); + } catch (IOException ioe) { + throw new CertificateParsingException(ioe); + } + } + + /** + * This method are the overridden implementation of + * getIssuerAlternativeNames method in X509Certificate in the Sun + * provider. It is better performance-wise since it returns cached + * values. + */ + public synchronized Collection> getIssuerAlternativeNames() + throws CertificateParsingException { + // return cached value if we can + if (readOnly && issuerAlternativeNames != null) { + return cloneAltNames(issuerAlternativeNames); + } + IssuerAlternativeNameExtension issuerAltNameExt = + getIssuerAlternativeNameExtension(); + if (issuerAltNameExt == null) { + return null; + } + GeneralNames names; + try { + names = issuerAltNameExt.get( + IssuerAlternativeNameExtension.ISSUER_NAME); + } catch (IOException ioe) { + // should not occur + return Collections.>emptySet(); + } + issuerAlternativeNames = makeAltNames(names); + return issuerAlternativeNames; + } + + /** + * This static method is the default implementation of the + * getIssuerAlternaitveNames method in X509Certificate. A + * X509Certificate provider generally should overwrite this to + * provide among other things caching for better performance. + */ + public static Collection> getIssuerAlternativeNames(X509Certificate cert) + throws CertificateParsingException { + try { + byte[] ext = cert.getExtensionValue(ISSUER_ALT_NAME_OID); + if (ext == null) { + return null; + } + + DerValue val = new DerValue(ext); + byte[] data = val.getOctetString(); + + IssuerAlternativeNameExtension issuerAltNameExt = + new IssuerAlternativeNameExtension(Boolean.FALSE, + data); + GeneralNames names; + try { + names = issuerAltNameExt.get( + IssuerAlternativeNameExtension.ISSUER_NAME); + } catch (IOException ioe) { + // should not occur + return Collections.>emptySet(); + } + return makeAltNames(names); + } catch (IOException ioe) { + throw new CertificateParsingException(ioe); + } + } + + public AuthorityInfoAccessExtension getAuthorityInfoAccessExtension() { + return (AuthorityInfoAccessExtension) + getExtension(PKIXExtensions.AuthInfoAccess_Id); + } + + /************************************************************/ + + /* + * Cert is a SIGNED ASN.1 macro, a three elment sequence: + * + * - Data to be signed (ToBeSigned) -- the "raw" cert + * - Signature algorithm (SigAlgId) + * - The signature bits + * + * This routine unmarshals the certificate, saving the signature + * parts away for later verification. + */ + private void parse(DerValue val) + throws CertificateException, IOException { + // check if can over write the certificate + if (readOnly) + throw new CertificateParsingException( + "cannot over-write existing certificate"); + + if (val.data == null || val.tag != DerValue.tag_Sequence) + throw new CertificateParsingException( + "invalid DER-encoded certificate data"); + + signedCert = val.toByteArray(); + DerValue[] seq = new DerValue[3]; + + seq[0] = val.data.getDerValue(); + seq[1] = val.data.getDerValue(); + seq[2] = val.data.getDerValue(); + + if (val.data.available() != 0) { + throw new CertificateParsingException("signed overrun, bytes = " + + val.data.available()); + } + if (seq[0].tag != DerValue.tag_Sequence) { + throw new CertificateParsingException("signed fields invalid"); + } + + algId = AlgorithmId.parse(seq[1]); + signature = seq[2].getBitString(); + + if (seq[1].data.available() != 0) { + throw new CertificateParsingException("algid field overrun"); + } + if (seq[2].data.available() != 0) + throw new CertificateParsingException("signed fields overrun"); + + // The CertificateInfo + info = new X509CertInfo(seq[0]); + + // the "inner" and "outer" signature algorithms must match + AlgorithmId infoSigAlg = (AlgorithmId)info.get( + CertificateAlgorithmId.NAME + + DOT + + CertificateAlgorithmId.ALGORITHM); + if (! algId.equals(infoSigAlg)) + throw new CertificateException("Signature algorithm mismatch"); + readOnly = true; + } + + /** + * Extract the subject or issuer X500Principal from an X509Certificate. + * Parses the encoded form of the cert to preserve the principal's + * ASN.1 encoding. + */ + private static X500Principal getX500Principal(X509Certificate cert, + boolean getIssuer) throws Exception { + byte[] encoded = cert.getEncoded(); + DerInputStream derIn = new DerInputStream(encoded); + DerValue tbsCert = derIn.getSequence(3)[0]; + DerInputStream tbsIn = tbsCert.data; + DerValue tmp; + tmp = tbsIn.getDerValue(); + // skip version number if present + if (tmp.isContextSpecific((byte)0)) { + tmp = tbsIn.getDerValue(); + } + // tmp always contains serial number now + tmp = tbsIn.getDerValue(); // skip signature + tmp = tbsIn.getDerValue(); // issuer + if (getIssuer == false) { + tmp = tbsIn.getDerValue(); // skip validity + tmp = tbsIn.getDerValue(); // subject + } + byte[] principalBytes = tmp.toByteArray(); + return new X500Principal(principalBytes); + } + + /** + * Extract the subject X500Principal from an X509Certificate. + * Called from java.security.cert.X509Certificate.getSubjectX500Principal(). + */ + public static X500Principal getSubjectX500Principal(X509Certificate cert) { + try { + return getX500Principal(cert, false); + } catch (Exception e) { + throw new RuntimeException("Could not parse subject", e); + } + } + + /** + * Extract the issuer X500Principal from an X509Certificate. + * Called from java.security.cert.X509Certificate.getIssuerX500Principal(). + */ + public static X500Principal getIssuerX500Principal(X509Certificate cert) { + try { + return getX500Principal(cert, true); + } catch (Exception e) { + throw new RuntimeException("Could not parse issuer", e); + } + } + + /** + * Returned the encoding of the given certificate for internal use. + * Callers must guarantee that they neither modify it nor expose it + * to untrusted code. Uses getEncodedInternal() if the certificate + * is instance of X509CertImpl, getEncoded() otherwise. + */ + public static byte[] getEncodedInternal(Certificate cert) + throws CertificateEncodingException { + if (cert instanceof X509CertImpl) { + return ((X509CertImpl)cert).getEncodedInternal(); + } else { + return cert.getEncoded(); + } + } + + /** + * Utility method to convert an arbitrary instance of X509Certificate + * to a X509CertImpl. Does a cast if possible, otherwise reparses + * the encoding. + */ + public static X509CertImpl toImpl(X509Certificate cert) + throws CertificateException { + if (cert instanceof X509CertImpl) { + return (X509CertImpl)cert; + } else { + return X509Factory.intern(cert); + } + } + + /** + * Utility method to test if a certificate is self-issued. This is + * the case iff the subject and issuer X500Principals are equal. + */ + public static boolean isSelfIssued(X509Certificate cert) { + X500Principal subject = cert.getSubjectX500Principal(); + X500Principal issuer = cert.getIssuerX500Principal(); + return subject.equals(issuer); + } + + /** + * Utility method to test if a certificate is self-signed. This is + * the case iff the subject and issuer X500Principals are equal + * AND the certificate's subject public key can be used to verify + * the certificate. In case of exception, returns false. + */ + public static boolean isSelfSigned(X509Certificate cert, + String sigProvider) { + if (isSelfIssued(cert)) { + try { + if (sigProvider == null) { + cert.verify(cert.getPublicKey()); + } else { + cert.verify(cert.getPublicKey(), sigProvider); + } + return true; + } catch (Exception e) { + // In case of exception, return false + } + } + return false; + } + + private ConcurrentHashMap fingerprints = + new ConcurrentHashMap<>(2); + + public String getFingerprint(String algorithm) { + return fingerprints.computeIfAbsent(algorithm, + x -> getCertificateFingerPrint(x)); + } + + /** + * Gets the requested finger print of the certificate. The result + * only contains 0-9 and A-F. No small case, no colon. + */ + private String getCertificateFingerPrint(String mdAlg) { + String fingerPrint = ""; + try { + byte[] encCertInfo = getEncoded(); + MessageDigest md = MessageDigest.getInstance(mdAlg); + byte[] digest = md.digest(encCertInfo); + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < digest.length; i++) { + byte2hex(digest[i], buf); + } + fingerPrint = buf.toString(); + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + // ignored + } + return fingerPrint; + } + + /** + * Converts a byte to hex digit and writes to the supplied buffer + */ + private static void byte2hex(byte b, StringBuffer buf) { + char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + int high = ((b & 0xf0) >> 4); + int low = (b & 0x0f); + buf.append(hexChars[high]); + buf.append(hexChars[low]); + } +} diff --git a/src/sun/security/x509/X509CertInfo.java b/src/sun/security/x509/X509CertInfo.java new file mode 100644 index 00000000..b7f2dd85 --- /dev/null +++ b/src/sun/security/x509/X509CertInfo.java @@ -0,0 +1,958 @@ +/* + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.IOException; +import java.io.OutputStream; + +import java.security.cert.*; +import java.util.*; + +import sun.security.util.*; +import sun.misc.HexDumpEncoder; + + +/** + * The X509CertInfo class represents X.509 certificate information. + * + *

      X.509 certificates have several base data elements, including:

        + * + *
      • The Subject Name, an X.500 Distinguished Name for + * the entity (subject) for which the certificate was issued. + * + *
      • The Subject Public Key, the public key of the subject. + * This is one of the most important parts of the certificate. + * + *
      • The Validity Period, a time period (e.g. six months) + * within which the certificate is valid (unless revoked). + * + *
      • The Issuer Name, an X.500 Distinguished Name for the + * Certificate Authority (CA) which issued the certificate. + * + *
      • A Serial Number assigned by the CA, for use in + * certificate revocation and other applications. + * + * @author Amit Kapoor + * @author Hemma Prafullchandra + * @see CertAttrSet + * @see X509CertImpl + */ +public class X509CertInfo implements CertAttrSet { + /** + * Identifier for this attribute, to be used with the + * get, set, delete methods of Certificate, x509 type. + */ + public static final String IDENT = "x509.info"; + // Certificate attribute names + public static final String NAME = "info"; + public static final String DN_NAME = "dname"; + public static final String VERSION = CertificateVersion.NAME; + public static final String SERIAL_NUMBER = CertificateSerialNumber.NAME; + public static final String ALGORITHM_ID = CertificateAlgorithmId.NAME; + public static final String ISSUER = "issuer"; + public static final String SUBJECT = "subject"; + public static final String VALIDITY = CertificateValidity.NAME; + public static final String KEY = CertificateX509Key.NAME; + public static final String ISSUER_ID = "issuerID"; + public static final String SUBJECT_ID = "subjectID"; + public static final String EXTENSIONS = CertificateExtensions.NAME; + + // X509.v1 data + protected CertificateVersion version = new CertificateVersion(); + protected CertificateSerialNumber serialNum = null; + protected CertificateAlgorithmId algId = null; + protected X500Name issuer = null; + protected X500Name subject = null; + protected CertificateValidity interval = null; + protected CertificateX509Key pubKey = null; + + // X509.v2 & v3 extensions + protected UniqueIdentity issuerUniqueId = null; + protected UniqueIdentity subjectUniqueId = null; + + // X509.v3 extensions + protected CertificateExtensions extensions = null; + + // Attribute numbers for internal manipulation + private static final int ATTR_VERSION = 1; + private static final int ATTR_SERIAL = 2; + private static final int ATTR_ALGORITHM = 3; + private static final int ATTR_ISSUER = 4; + private static final int ATTR_VALIDITY = 5; + private static final int ATTR_SUBJECT = 6; + private static final int ATTR_KEY = 7; + private static final int ATTR_ISSUER_ID = 8; + private static final int ATTR_SUBJECT_ID = 9; + private static final int ATTR_EXTENSIONS = 10; + + // DER encoded CertificateInfo data + private byte[] rawCertInfo = null; + + // The certificate attribute name to integer mapping stored here + private static final Map map = new HashMap(); + static { + map.put(VERSION, Integer.valueOf(ATTR_VERSION)); + map.put(SERIAL_NUMBER, Integer.valueOf(ATTR_SERIAL)); + map.put(ALGORITHM_ID, Integer.valueOf(ATTR_ALGORITHM)); + map.put(ISSUER, Integer.valueOf(ATTR_ISSUER)); + map.put(VALIDITY, Integer.valueOf(ATTR_VALIDITY)); + map.put(SUBJECT, Integer.valueOf(ATTR_SUBJECT)); + map.put(KEY, Integer.valueOf(ATTR_KEY)); + map.put(ISSUER_ID, Integer.valueOf(ATTR_ISSUER_ID)); + map.put(SUBJECT_ID, Integer.valueOf(ATTR_SUBJECT_ID)); + map.put(EXTENSIONS, Integer.valueOf(ATTR_EXTENSIONS)); + } + + /** + * Construct an uninitialized X509CertInfo on which + * decode must later be called (or which may be deserialized). + */ + public X509CertInfo() { } + + /** + * Unmarshals a certificate from its encoded form, parsing the + * encoded bytes. This form of constructor is used by agents which + * need to examine and use certificate contents. That is, this is + * one of the more commonly used constructors. Note that the buffer + * must include only a certificate, and no "garbage" may be left at + * the end. If you need to ignore data at the end of a certificate, + * use another constructor. + * + * @param cert the encoded bytes, with no trailing data. + * @exception CertificateParsingException on parsing errors. + */ + public X509CertInfo(byte[] cert) throws CertificateParsingException { + try { + DerValue in = new DerValue(cert); + + parse(in); + } catch (IOException e) { + throw new CertificateParsingException(e); + } + } + + /** + * Unmarshal a certificate from its encoded form, parsing a DER value. + * This form of constructor is used by agents which need to examine + * and use certificate contents. + * + * @param derVal the der value containing the encoded cert. + * @exception CertificateParsingException on parsing errors. + */ + public X509CertInfo(DerValue derVal) throws CertificateParsingException { + try { + parse(derVal); + } catch (IOException e) { + throw new CertificateParsingException(e); + } + } + + /** + * Appends the certificate to an output stream. + * + * @param out an output stream to which the certificate is appended. + * @exception CertificateException on encoding errors. + * @exception IOException on other errors. + */ + public void encode(OutputStream out) + throws CertificateException, IOException { + if (rawCertInfo == null) { + DerOutputStream tmp = new DerOutputStream(); + emit(tmp); + rawCertInfo = tmp.toByteArray(); + } + out.write(rawCertInfo.clone()); + } + + /** + * Return an enumeration of names of attributes existing within this + * attribute. + */ + public Enumeration getElements() { + AttributeNameEnumeration elements = new AttributeNameEnumeration(); + elements.addElement(VERSION); + elements.addElement(SERIAL_NUMBER); + elements.addElement(ALGORITHM_ID); + elements.addElement(ISSUER); + elements.addElement(VALIDITY); + elements.addElement(SUBJECT); + elements.addElement(KEY); + elements.addElement(ISSUER_ID); + elements.addElement(SUBJECT_ID); + elements.addElement(EXTENSIONS); + + return elements.elements(); + } + + /** + * Return the name of this attribute. + */ + public String getName() { + return(NAME); + } + + /** + * Returns the encoded certificate info. + * + * @exception CertificateEncodingException on encoding information errors. + */ + public byte[] getEncodedInfo() throws CertificateEncodingException { + try { + if (rawCertInfo == null) { + DerOutputStream tmp = new DerOutputStream(); + emit(tmp); + rawCertInfo = tmp.toByteArray(); + } + return rawCertInfo.clone(); + } catch (IOException e) { + throw new CertificateEncodingException(e.toString()); + } catch (CertificateException e) { + throw new CertificateEncodingException(e.toString()); + } + } + + /** + * Compares two X509CertInfo objects. This is false if the + * certificates are not both X.509 certs, otherwise it + * compares them as binary data. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals(Object other) { + if (other instanceof X509CertInfo) { + return equals((X509CertInfo) other); + } else { + return false; + } + } + + /** + * Compares two certificates, returning false if any data + * differs between the two. + * + * @param other the object being compared with this one + * @return true iff the certificates are equivalent + */ + public boolean equals(X509CertInfo other) { + if (this == other) { + return(true); + } else if (rawCertInfo == null || other.rawCertInfo == null) { + return(false); + } else if (rawCertInfo.length != other.rawCertInfo.length) { + return(false); + } + for (int i = 0; i < rawCertInfo.length; i++) { + if (rawCertInfo[i] != other.rawCertInfo[i]) { + return(false); + } + } + return(true); + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + int retval = 0; + + for (int i = 1; i < rawCertInfo.length; i++) { + retval += rawCertInfo[i] * i; + } + return(retval); + } + + /** + * Returns a printable representation of the certificate. + */ + public String toString() { + + if (subject == null || pubKey == null || interval == null + || issuer == null || algId == null || serialNum == null) { + throw new NullPointerException("X.509 cert is incomplete"); + } + StringBuilder sb = new StringBuilder(); + + sb.append("[\n"); + sb.append(" " + version.toString() + "\n"); + sb.append(" Subject: " + subject.toString() + "\n"); + sb.append(" Signature Algorithm: " + algId.toString() + "\n"); + sb.append(" Key: " + pubKey.toString() + "\n"); + sb.append(" " + interval.toString() + "\n"); + sb.append(" Issuer: " + issuer.toString() + "\n"); + sb.append(" " + serialNum.toString() + "\n"); + + // optional v2, v3 extras + if (issuerUniqueId != null) { + sb.append(" Issuer Id:\n" + issuerUniqueId.toString() + "\n"); + } + if (subjectUniqueId != null) { + sb.append(" Subject Id:\n" + subjectUniqueId.toString() + "\n"); + } + if (extensions != null) { + Collection allExts = extensions.getAllExtensions(); + Extension[] exts = allExts.toArray(new Extension[0]); + sb.append("\nCertificate Extensions: " + exts.length); + for (int i = 0; i < exts.length; i++) { + sb.append("\n[" + (i+1) + "]: "); + Extension ext = exts[i]; + try { + if (OIDMap.getClass(ext.getExtensionId()) == null) { + sb.append(ext.toString()); + byte[] extValue = ext.getExtensionValue(); + if (extValue != null) { + DerOutputStream out = new DerOutputStream(); + out.putOctetString(extValue); + extValue = out.toByteArray(); + HexDumpEncoder enc = new HexDumpEncoder(); + sb.append("Extension unknown: " + + "DER encoded OCTET string =\n" + + enc.encodeBuffer(extValue) + "\n"); + } + } else + sb.append(ext.toString()); //sub-class exists + } catch (Exception e) { + sb.append(", Error parsing this extension"); + } + } + Map invalid = extensions.getUnparseableExtensions(); + if (invalid.isEmpty() == false) { + sb.append("\nUnparseable certificate extensions: " + invalid.size()); + int i = 1; + for (Extension ext : invalid.values()) { + sb.append("\n[" + (i++) + "]: "); + sb.append(ext); + } + } + } + sb.append("\n]"); + return sb.toString(); + } + + /** + * Set the certificate attribute. + * + * @params name the name of the Certificate attribute. + * @params val the value of the Certificate attribute. + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public void set(String name, Object val) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateException("Attribute name not recognized: " + + name); + } + // set rawCertInfo to null, so that we are forced to re-encode + rawCertInfo = null; + String suffix = attrName.getSuffix(); + + switch (attr) { + case ATTR_VERSION: + if (suffix == null) { + setVersion(val); + } else { + version.set(suffix, val); + } + break; + + case ATTR_SERIAL: + if (suffix == null) { + setSerialNumber(val); + } else { + serialNum.set(suffix, val); + } + break; + + case ATTR_ALGORITHM: + if (suffix == null) { + setAlgorithmId(val); + } else { + algId.set(suffix, val); + } + break; + + case ATTR_ISSUER: + setIssuer(val); + break; + + case ATTR_VALIDITY: + if (suffix == null) { + setValidity(val); + } else { + interval.set(suffix, val); + } + break; + + case ATTR_SUBJECT: + setSubject(val); + break; + + case ATTR_KEY: + if (suffix == null) { + setKey(val); + } else { + pubKey.set(suffix, val); + } + break; + + case ATTR_ISSUER_ID: + setIssuerUniqueId(val); + break; + + case ATTR_SUBJECT_ID: + setSubjectUniqueId(val); + break; + + case ATTR_EXTENSIONS: + if (suffix == null) { + setExtensions(val); + } else { + if (extensions == null) + extensions = new CertificateExtensions(); + extensions.set(suffix, val); + } + break; + } + } + + /** + * Delete the certificate attribute. + * + * @params name the name of the Certificate attribute. + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public void delete(String name) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateException("Attribute name not recognized: " + + name); + } + // set rawCertInfo to null, so that we are forced to re-encode + rawCertInfo = null; + String suffix = attrName.getSuffix(); + + switch (attr) { + case ATTR_VERSION: + if (suffix == null) { + version = null; + } else { + version.delete(suffix); + } + break; + case (ATTR_SERIAL): + if (suffix == null) { + serialNum = null; + } else { + serialNum.delete(suffix); + } + break; + case (ATTR_ALGORITHM): + if (suffix == null) { + algId = null; + } else { + algId.delete(suffix); + } + break; + case (ATTR_ISSUER): + issuer = null; + break; + case (ATTR_VALIDITY): + if (suffix == null) { + interval = null; + } else { + interval.delete(suffix); + } + break; + case (ATTR_SUBJECT): + subject = null; + break; + case (ATTR_KEY): + if (suffix == null) { + pubKey = null; + } else { + pubKey.delete(suffix); + } + break; + case (ATTR_ISSUER_ID): + issuerUniqueId = null; + break; + case (ATTR_SUBJECT_ID): + subjectUniqueId = null; + break; + case (ATTR_EXTENSIONS): + if (suffix == null) { + extensions = null; + } else { + if (extensions != null) + extensions.delete(suffix); + } + break; + } + } + + /** + * Get the certificate attribute. + * + * @params name the name of the Certificate attribute. + * + * @exception CertificateException on invalid attributes. + * @exception IOException on other errors. + */ + public Object get(String name) + throws CertificateException, IOException { + X509AttributeName attrName = new X509AttributeName(name); + + int attr = attributeMap(attrName.getPrefix()); + if (attr == 0) { + throw new CertificateParsingException( + "Attribute name not recognized: " + name); + } + String suffix = attrName.getSuffix(); + + switch (attr) { // frequently used attributes first + case (ATTR_EXTENSIONS): + if (suffix == null) { + return(extensions); + } else { + if (extensions == null) { + return null; + } else { + return(extensions.get(suffix)); + } + } + case (ATTR_SUBJECT): + if (suffix == null) { + return(subject); + } else { + return(getX500Name(suffix, false)); + } + case (ATTR_ISSUER): + if (suffix == null) { + return(issuer); + } else { + return(getX500Name(suffix, true)); + } + case (ATTR_KEY): + if (suffix == null) { + return(pubKey); + } else { + return(pubKey.get(suffix)); + } + case (ATTR_ALGORITHM): + if (suffix == null) { + return(algId); + } else { + return(algId.get(suffix)); + } + case (ATTR_VALIDITY): + if (suffix == null) { + return(interval); + } else { + return(interval.get(suffix)); + } + case (ATTR_VERSION): + if (suffix == null) { + return(version); + } else { + return(version.get(suffix)); + } + case (ATTR_SERIAL): + if (suffix == null) { + return(serialNum); + } else { + return(serialNum.get(suffix)); + } + case (ATTR_ISSUER_ID): + return(issuerUniqueId); + case (ATTR_SUBJECT_ID): + return(subjectUniqueId); + } + return null; + } + + /* + * Get the Issuer or Subject name + */ + private Object getX500Name(String name, boolean getIssuer) + throws IOException { + if (name.equalsIgnoreCase(X509CertInfo.DN_NAME)) { + return getIssuer ? issuer : subject; + } else if (name.equalsIgnoreCase("x500principal")) { + return getIssuer ? issuer.asX500Principal() + : subject.asX500Principal(); + } else { + throw new IOException("Attribute name not recognized."); + } + } + + /* + * This routine unmarshals the certificate information. + */ + private void parse(DerValue val) + throws CertificateParsingException, IOException { + DerInputStream in; + DerValue tmp; + + if (val.tag != DerValue.tag_Sequence) { + throw new CertificateParsingException("signed fields invalid"); + } + rawCertInfo = val.toByteArray(); + + in = val.data; + + // Version + tmp = in.getDerValue(); + if (tmp.isContextSpecific((byte)0)) { + version = new CertificateVersion(tmp); + tmp = in.getDerValue(); + } + + // Serial number ... an integer + serialNum = new CertificateSerialNumber(tmp); + + // Algorithm Identifier + algId = new CertificateAlgorithmId(in); + + // Issuer name + issuer = new X500Name(in); + if (issuer.isEmpty()) { + throw new CertificateParsingException( + "Empty issuer DN not allowed in X509Certificates"); + } + + // validity: SEQUENCE { start date, end date } + interval = new CertificateValidity(in); + + // subject name + subject = new X500Name(in); + if ((version.compare(CertificateVersion.V1) == 0) && + subject.isEmpty()) { + throw new CertificateParsingException( + "Empty subject DN not allowed in v1 certificate"); + } + + // public key + pubKey = new CertificateX509Key(in); + + // If more data available, make sure version is not v1. + if (in.available() != 0) { + if (version.compare(CertificateVersion.V1) == 0) { + throw new CertificateParsingException( + "no more data allowed for version 1 certificate"); + } + } else { + return; + } + + // Get the issuerUniqueId if present + tmp = in.getDerValue(); + if (tmp.isContextSpecific((byte)1)) { + issuerUniqueId = new UniqueIdentity(tmp); + if (in.available() == 0) + return; + tmp = in.getDerValue(); + } + + // Get the subjectUniqueId if present. + if (tmp.isContextSpecific((byte)2)) { + subjectUniqueId = new UniqueIdentity(tmp); + if (in.available() == 0) + return; + tmp = in.getDerValue(); + } + + // Get the extensions. + if (version.compare(CertificateVersion.V3) != 0) { + throw new CertificateParsingException( + "Extensions not allowed in v2 certificate"); + } + if (tmp.isConstructed() && tmp.isContextSpecific((byte)3)) { + extensions = new CertificateExtensions(tmp.data); + } + + // verify X.509 V3 Certificate + verifyCert(subject, extensions); + + } + + /* + * Verify if X.509 V3 Certificate is compliant with RFC 3280. + */ + private void verifyCert(X500Name subject, + CertificateExtensions extensions) + throws CertificateParsingException, IOException { + + // if SubjectName is empty, check for SubjectAlternativeNameExtension + if (subject.isEmpty()) { + if (extensions == null) { + throw new CertificateParsingException("X.509 Certificate is " + + "incomplete: subject field is empty, and certificate " + + "has no extensions"); + } + SubjectAlternativeNameExtension subjectAltNameExt = null; + SubjectAlternativeNameExtension extValue = null; + GeneralNames names = null; + try { + subjectAltNameExt = (SubjectAlternativeNameExtension) + extensions.get(SubjectAlternativeNameExtension.NAME); + names = subjectAltNameExt.get( + SubjectAlternativeNameExtension.SUBJECT_NAME); + } catch (IOException e) { + throw new CertificateParsingException("X.509 Certificate is " + + "incomplete: subject field is empty, and " + + "SubjectAlternativeName extension is absent"); + } + + // SubjectAlternativeName extension is empty or not marked critical + if (names == null || names.isEmpty()) { + throw new CertificateParsingException("X.509 Certificate is " + + "incomplete: subject field is empty, and " + + "SubjectAlternativeName extension is empty"); + } else if (subjectAltNameExt.isCritical() == false) { + throw new CertificateParsingException("X.509 Certificate is " + + "incomplete: SubjectAlternativeName extension MUST " + + "be marked critical when subject field is empty"); + } + } + } + + /* + * Marshal the contents of a "raw" certificate into a DER sequence. + */ + private void emit(DerOutputStream out) + throws CertificateException, IOException { + DerOutputStream tmp = new DerOutputStream(); + + // version number, iff not V1 + version.encode(tmp); + + // Encode serial number, issuer signing algorithm, issuer name + // and validity + serialNum.encode(tmp); + algId.encode(tmp); + + if ((version.compare(CertificateVersion.V1) == 0) && + (issuer.toString() == null)) + throw new CertificateParsingException( + "Null issuer DN not allowed in v1 certificate"); + + issuer.encode(tmp); + interval.encode(tmp); + + // Encode subject (principal) and associated key + if ((version.compare(CertificateVersion.V1) == 0) && + (subject.toString() == null)) + throw new CertificateParsingException( + "Null subject DN not allowed in v1 certificate"); + subject.encode(tmp); + pubKey.encode(tmp); + + // Encode issuerUniqueId & subjectUniqueId. + if (issuerUniqueId != null) { + issuerUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT, + false,(byte)1)); + } + if (subjectUniqueId != null) { + subjectUniqueId.encode(tmp, DerValue.createTag(DerValue.TAG_CONTEXT, + false,(byte)2)); + } + + // Write all the extensions. + if (extensions != null) { + extensions.encode(tmp); + } + + // Wrap the data; encoding of the "raw" cert is now complete. + out.write(DerValue.tag_Sequence, tmp); + } + + /** + * Returns the integer attribute number for the passed attribute name. + */ + private int attributeMap(String name) { + Integer num = map.get(name); + if (num == null) { + return 0; + } + return num.intValue(); + } + + /** + * Set the version number of the certificate. + * + * @params val the Object class value for the Extensions + * @exception CertificateException on invalid data. + */ + private void setVersion(Object val) throws CertificateException { + if (!(val instanceof CertificateVersion)) { + throw new CertificateException("Version class type invalid."); + } + version = (CertificateVersion)val; + } + + /** + * Set the serial number of the certificate. + * + * @params val the Object class value for the CertificateSerialNumber + * @exception CertificateException on invalid data. + */ + private void setSerialNumber(Object val) throws CertificateException { + if (!(val instanceof CertificateSerialNumber)) { + throw new CertificateException("SerialNumber class type invalid."); + } + serialNum = (CertificateSerialNumber)val; + } + + /** + * Set the algorithm id of the certificate. + * + * @params val the Object class value for the AlgorithmId + * @exception CertificateException on invalid data. + */ + private void setAlgorithmId(Object val) throws CertificateException { + if (!(val instanceof CertificateAlgorithmId)) { + throw new CertificateException( + "AlgorithmId class type invalid."); + } + algId = (CertificateAlgorithmId)val; + } + + /** + * Set the issuer name of the certificate. + * + * @params val the Object class value for the issuer + * @exception CertificateException on invalid data. + */ + private void setIssuer(Object val) throws CertificateException { + if (!(val instanceof X500Name)) { + throw new CertificateException( + "Issuer class type invalid."); + } + issuer = (X500Name)val; + } + + /** + * Set the validity interval of the certificate. + * + * @params val the Object class value for the CertificateValidity + * @exception CertificateException on invalid data. + */ + private void setValidity(Object val) throws CertificateException { + if (!(val instanceof CertificateValidity)) { + throw new CertificateException( + "CertificateValidity class type invalid."); + } + interval = (CertificateValidity)val; + } + + /** + * Set the subject name of the certificate. + * + * @params val the Object class value for the Subject + * @exception CertificateException on invalid data. + */ + private void setSubject(Object val) throws CertificateException { + if (!(val instanceof X500Name)) { + throw new CertificateException( + "Subject class type invalid."); + } + subject = (X500Name)val; + } + + /** + * Set the public key in the certificate. + * + * @params val the Object class value for the PublicKey + * @exception CertificateException on invalid data. + */ + private void setKey(Object val) throws CertificateException { + if (!(val instanceof CertificateX509Key)) { + throw new CertificateException( + "Key class type invalid."); + } + pubKey = (CertificateX509Key)val; + } + + /** + * Set the Issuer Unique Identity in the certificate. + * + * @params val the Object class value for the IssuerUniqueId + * @exception CertificateException + */ + private void setIssuerUniqueId(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V2) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof UniqueIdentity)) { + throw new CertificateException( + "IssuerUniqueId class type invalid."); + } + issuerUniqueId = (UniqueIdentity)val; + } + + /** + * Set the Subject Unique Identity in the certificate. + * + * @params val the Object class value for the SubjectUniqueId + * @exception CertificateException + */ + private void setSubjectUniqueId(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V2) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof UniqueIdentity)) { + throw new CertificateException( + "SubjectUniqueId class type invalid."); + } + subjectUniqueId = (UniqueIdentity)val; + } + + /** + * Set the extensions in the certificate. + * + * @params val the Object class value for the Extensions + * @exception CertificateException + */ + private void setExtensions(Object val) throws CertificateException { + if (version.compare(CertificateVersion.V3) < 0) { + throw new CertificateException("Invalid version"); + } + if (!(val instanceof CertificateExtensions)) { + throw new CertificateException( + "Extensions class type invalid."); + } + extensions = (CertificateExtensions)val; + } +} diff --git a/src/sun/security/x509/X509Key.java b/src/sun/security/x509/X509Key.java new file mode 100644 index 00000000..24b6ba01 --- /dev/null +++ b/src/sun/security/x509/X509Key.java @@ -0,0 +1,477 @@ +/* + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.x509; + +import java.io.*; +import java.util.Arrays; +import java.util.Properties; +import java.security.Key; +import java.security.PublicKey; +import java.security.KeyFactory; +import java.security.Security; +import java.security.Provider; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; + +import sun.misc.HexDumpEncoder; +import sun.security.util.*; + +/** + * Holds an X.509 key, for example a public key found in an X.509 + * certificate. Includes a description of the algorithm to be used + * with the key; these keys normally are used as + * "SubjectPublicKeyInfo". + * + *

        While this class can represent any kind of X.509 key, it may be + * desirable to provide subclasses which understand how to parse keying + * data. For example, RSA public keys have two members, one for the + * public modulus and one for the prime exponent. If such a class is + * provided, it is used when parsing X.509 keys. If one is not provided, + * the key still parses correctly. + * + * @author David Brownell + */ +public class X509Key implements PublicKey { + + /** use serialVersionUID from JDK 1.1. for interoperability */ + private static final long serialVersionUID = -5359250853002055002L; + + /* The algorithm information (name, parameters, etc). */ + protected AlgorithmId algid; + + /** + * The key bytes, without the algorithm information. + * @deprecated Use the BitArray form which does not require keys to + * be byte aligned. + * @see X509Key#setKey(BitArray) + * @see X509Key#getKey() + */ + @Deprecated + protected byte[] key = null; + + /* + * The number of bits unused in the last byte of the key. + * Added to keep the byte[] key form consistent with the BitArray + * form. Can de deleted when byte[] key is deleted. + */ + @Deprecated + private int unusedBits = 0; + + /* BitArray form of key */ + private BitArray bitStringKey = null; + + /* The encoding for the key. */ + protected byte[] encodedKey; + + /** + * Default constructor. The key constructed must have its key + * and algorithm initialized before it may be used, for example + * by using decode. + */ + public X509Key() { } + + /* + * Build and initialize as a "default" key. All X.509 key + * data is stored and transmitted losslessly, but no knowledge + * about this particular algorithm is available. + */ + private X509Key(AlgorithmId algid, BitArray key) + throws InvalidKeyException { + this.algid = algid; + setKey(key); + encode(); + } + + /** + * Sets the key in the BitArray form. + */ + protected void setKey(BitArray key) { + this.bitStringKey = (BitArray)key.clone(); + + /* + * Do this to keep the byte array form consistent with + * this. Can delete when byte[] key is deleted. + */ + this.key = key.toByteArray(); + int remaining = key.length() % 8; + this.unusedBits = + ((remaining == 0) ? 0 : 8 - remaining); + } + + /** + * Gets the key. The key may or may not be byte aligned. + * @return a BitArray containing the key. + */ + protected BitArray getKey() { + /* + * Do this for consistency in case a subclass + * modifies byte[] key directly. Remove when + * byte[] key is deleted. + * Note: the consistency checks fail when the subclass + * modifies a non byte-aligned key (into a byte-aligned key) + * using the deprecated byte[] key field. + */ + this.bitStringKey = new BitArray( + this.key.length * 8 - this.unusedBits, + this.key); + + return (BitArray)bitStringKey.clone(); + } + + /** + * Construct X.509 subject public key from a DER value. If + * the runtime environment is configured with a specific class for + * this kind of key, a subclass is returned. Otherwise, a generic + * X509Key object is returned. + * + *

        This mechanism gurantees that keys (and algorithms) may be + * freely manipulated and transferred, without risk of losing + * information. Also, when a key (or algorithm) needs some special + * handling, that specific need can be accomodated. + * + * @param in the DER-encoded SubjectPublicKeyInfo value + * @exception IOException on data format errors + */ + public static PublicKey parse(DerValue in) throws IOException + { + AlgorithmId algorithm; + PublicKey subjectKey; + + if (in.tag != DerValue.tag_Sequence) + throw new IOException("corrupt subject key"); + + algorithm = AlgorithmId.parse(in.data.getDerValue()); + try { + subjectKey = buildX509Key(algorithm, + in.data.getUnalignedBitString()); + + } catch (InvalidKeyException e) { + throw new IOException("subject key, " + e.getMessage(), e); + } + + if (in.data.available() != 0) + throw new IOException("excess subject key"); + return subjectKey; + } + + /** + * Parse the key bits. This may be redefined by subclasses to take + * advantage of structure within the key. For example, RSA public + * keys encapsulate two unsigned integers (modulus and exponent) as + * DER values within the key bits; Diffie-Hellman and + * DSS/DSA keys encapsulate a single unsigned integer. + * + *

        This function is called when creating X.509 SubjectPublicKeyInfo + * values using the X509Key member functions, such as parse + * and decode. + * + * @exception IOException on parsing errors. + * @exception InvalidKeyException on invalid key encodings. + */ + protected void parseKeyBits() throws IOException, InvalidKeyException { + encode(); + } + + /* + * Factory interface, building the kind of key associated with this + * specific algorithm ID or else returning this generic base class. + * See the description above. + */ + static PublicKey buildX509Key(AlgorithmId algid, BitArray key) + throws IOException, InvalidKeyException + { + /* + * Use the algid and key parameters to produce the ASN.1 encoding + * of the key, which will then be used as the input to the + * key factory. + */ + DerOutputStream x509EncodedKeyStream = new DerOutputStream(); + encode(x509EncodedKeyStream, algid, key); + X509EncodedKeySpec x509KeySpec + = new X509EncodedKeySpec(x509EncodedKeyStream.toByteArray()); + + try { + // Instantiate the key factory of the appropriate algorithm + KeyFactory keyFac = KeyFactory.getInstance(algid.getName()); + + // Generate the public key + return keyFac.generatePublic(x509KeySpec); + } catch (NoSuchAlgorithmException e) { + // Return generic X509Key with opaque key data (see below) + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e.getMessage(), e); + } + + /* + * Try again using JDK1.1-style for backwards compatibility. + */ + String classname = ""; + try { + Properties props; + String keytype; + Provider sunProvider; + + sunProvider = Security.getProvider("SUN"); + if (sunProvider == null) + throw new InstantiationException(); + classname = sunProvider.getProperty("PublicKey.X.509." + + algid.getName()); + if (classname == null) { + throw new InstantiationException(); + } + + Class keyClass = null; + try { + keyClass = Class.forName(classname); + } catch (ClassNotFoundException e) { + ClassLoader cl = ClassLoader.getSystemClassLoader(); + if (cl != null) { + keyClass = cl.loadClass(classname); + } + } + + Object inst = null; + X509Key result; + + if (keyClass != null) + inst = keyClass.newInstance(); + if (inst instanceof X509Key) { + result = (X509Key) inst; + result.algid = algid; + result.setKey(key); + result.parseKeyBits(); + return result; + } + } catch (ClassNotFoundException e) { + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + // this should not happen. + throw new IOException (classname + " [internal error]"); + } + + X509Key result = new X509Key(algid, key); + return result; + } + + /** + * Returns the algorithm to be used with this key. + */ + public String getAlgorithm() { + return algid.getName(); + } + + /** + * Returns the algorithm ID to be used with this key. + */ + public AlgorithmId getAlgorithmId() { return algid; } + + /** + * Encode SubjectPublicKeyInfo sequence on the DER output stream. + * + * @exception IOException on encoding errors. + */ + public final void encode(DerOutputStream out) throws IOException + { + encode(out, this.algid, getKey()); + } + + /** + * Returns the DER-encoded form of the key as a byte array. + */ + public byte[] getEncoded() { + try { + return getEncodedInternal().clone(); + } catch (InvalidKeyException e) { + // XXX + } + return null; + } + + public byte[] getEncodedInternal() throws InvalidKeyException { + byte[] encoded = encodedKey; + if (encoded == null) { + try { + DerOutputStream out = new DerOutputStream(); + encode(out); + encoded = out.toByteArray(); + } catch (IOException e) { + throw new InvalidKeyException("IOException : " + + e.getMessage()); + } + encodedKey = encoded; + } + return encoded; + } + + /** + * Returns the format for this key: "X.509" + */ + public String getFormat() { + return "X.509"; + } + + /** + * Returns the DER-encoded form of the key as a byte array. + * + * @exception InvalidKeyException on encoding errors. + */ + public byte[] encode() throws InvalidKeyException { + return getEncodedInternal().clone(); + } + + /* + * Returns a printable representation of the key + */ + public String toString() + { + HexDumpEncoder encoder = new HexDumpEncoder(); + + return "algorithm = " + algid.toString() + + ", unparsed keybits = \n" + encoder.encodeBuffer(key); + } + + /** + * Initialize an X509Key object from an input stream. The data on that + * input stream must be encoded using DER, obeying the X.509 + * SubjectPublicKeyInfo format. That is, the data is a + * sequence consisting of an algorithm ID and a bit string which holds + * the key. (That bit string is often used to encapsulate another DER + * encoded sequence.) + * + *

        Subclasses should not normally redefine this method; they should + * instead provide a parseKeyBits method to parse any + * fields inside the key member. + * + *

        The exception to this rule is that since private keys need not + * be encoded using the X.509 SubjectPublicKeyInfo format, + * private keys may override this method, encode, and + * of course getFormat. + * + * @param in an input stream with a DER-encoded X.509 + * SubjectPublicKeyInfo value + * @exception InvalidKeyException on parsing errors. + */ + public void decode(InputStream in) + throws InvalidKeyException + { + DerValue val; + + try { + val = new DerValue(in); + if (val.tag != DerValue.tag_Sequence) + throw new InvalidKeyException("invalid key format"); + + algid = AlgorithmId.parse(val.data.getDerValue()); + setKey(val.data.getUnalignedBitString()); + parseKeyBits(); + if (val.data.available() != 0) + throw new InvalidKeyException ("excess key data"); + + } catch (IOException e) { + // e.printStackTrace (); + throw new InvalidKeyException("IOException: " + + e.getMessage()); + } + } + + public void decode(byte[] encodedKey) throws InvalidKeyException { + decode(new ByteArrayInputStream(encodedKey)); + } + + /** + * Serialization write ... X.509 keys serialize as + * themselves, and they're parsed when they get read back. + */ + private void writeObject(ObjectOutputStream stream) throws IOException { + stream.write(getEncoded()); + } + + /** + * Serialization read ... X.509 keys serialize as + * themselves, and they're parsed when they get read back. + */ + private void readObject(ObjectInputStream stream) throws IOException { + try { + decode(stream); + } catch (InvalidKeyException e) { + e.printStackTrace(); + throw new IOException("deserialized key is invalid: " + + e.getMessage()); + } + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Key == false) { + return false; + } + try { + byte[] thisEncoded = this.getEncodedInternal(); + byte[] otherEncoded; + if (obj instanceof X509Key) { + otherEncoded = ((X509Key)obj).getEncodedInternal(); + } else { + otherEncoded = ((Key)obj).getEncoded(); + } + return Arrays.equals(thisEncoded, otherEncoded); + } catch (InvalidKeyException e) { + return false; + } + } + + /** + * Calculates a hash code value for the object. Objects + * which are equal will also have the same hashcode. + */ + public int hashCode() { + try { + byte[] b1 = getEncodedInternal(); + int r = b1.length; + for (int i = 0; i < b1.length; i++) { + r += (b1[i] & 0xff) * 37; + } + return r; + } catch (InvalidKeyException e) { + // should not happen + return 0; + } + } + + /* + * Produce SubjectPublicKey encoding from algorithm id and key material. + */ + static void encode(DerOutputStream out, AlgorithmId algid, BitArray key) + throws IOException { + DerOutputStream tmp = new DerOutputStream(); + algid.encode(tmp); + tmp.putUnalignedBitString(key); + out.write(DerValue.tag_Sequence, tmp); + } +} diff --git a/src/sun/security/x509/certAttributes.html b/src/sun/security/x509/certAttributes.html new file mode 100644 index 00000000..79f6b5a1 --- /dev/null +++ b/src/sun/security/x509/certAttributes.html @@ -0,0 +1,246 @@ + + + +Certificate Attributes + +

        Certificate Attributes

        +
        July 1998
        +

        +In JDK1.2 we provide an implementation of X.509 (version 3). +The X509CertImpl class supports the following methods to +manipulate the various attributes of a certificate: +

        +     Object get(String name)
        +     void set(String name, Object value), and
        +     void delete(String name)
        +
        +A list of all the X.509 v3 Certificate attributes that can be manipulated +is provided in the following table. +For example, if you want to get the signature component of +the certificate: +
        +     X509CertImpl cert;
        +     // get the certificate object
        +     byte[] sig = (byte[])cert.get("x509.signature");
        +                  // using the fully-qualified identifier
        +OR
        +     byte[] sig = (byte[])cert.get(X509CertImpl.SIG);
        +                  // using defined constants
        +
        +

        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        sun.security.x509.X509CertImpl
        AttributeFully-qualified identifierDefined constantsType of Object returned
        +(in sun.security.x509 unless fully-qualified)
        signatureAlgorithmx509.algorithmX509CertImpl.SIG_ALGAlgorithmId
        signaturex509.signatureX509CertImpl.SIGbyte[]
        tbsCertificatex509.infoX509CertInfo.IDENTX509CertInfo
        versionx509.info.version
        +x509.info.version.number
        CertificateVersion.IDENT
        +none
        CertificateVersion
        +java.lang.Integer
        serialNumberx509.info.serialNumber
        +x509.info.serialNumber.number
        CertificateSerialNumber.IDENT
        +X509CertImpl.SERIAL_ID
        CertificateSerialNumber
        +SerialNumber
        signaturex509.info.algorithmID
        +x509.info.algorithmID.algorithm
        CertificateAlgorithmId.IDENT
        +none
        CertificateAlgorithmId
        +AlgorithmId
        issuerx509.info.issuer
        +x509.info.issuer.dname
        none
        +X509CertImpl.ISSUER_DN
        X500Name
        +X500Name
        validity
        +validity.notAfter
        +validity.notBefore
        x509.info.validity
        +x509.info.validity.notAfter
        +x509.info.validity.notBefore
        CertificateValidity.IDENT
        +none
        +none
        CertificateValidity
        +java.util.Date
        +java.util.Date
        subjectx509.info.subject
        +x509.info.subject.dname
        none
        +X509CertImpl.SUBJECT_DN
        X500Name
        +X500Name
        subjectPublicKeyInfox509.info.key
        +x509.info.key.value
        CertificateX509Key.IDENT
        +X509CertImpl.PUBLIC_KEY
        CertificateX509Key
        +X509Key
        issuerUniqueIDx509.info.issuerID
        +x509.info.issuerID.id
        none
        +none
        UniqueIdentity
        +UniqueIdentity
        subjectUniqueIDx509.info.subjectID
        +x509.info.subjectID.id
        none
        +none
        UniqueIdentity
        +UniqueIdentity
        extensionsx509.info.extensionsCertificateExtensions.IDENTCertificateExtensions
        +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        X.509 V3 certificate extensions
        ExtensionExtension attribute identifierShort formType of Object returned
        Authority Key Identifierx509.info.extensions.AuthorityKeyIdentifierAuthorityKeyIdentifierExtension.IDENTAuthorityKeyIdentifierExtension
        Subject Key Identifierx509.info.extensions.SubjectKeyIdentifierSubjectKeyIdentifierExtension.IDENTSubjectKeyIdentifierExtension
        Key Usagex509.info.extensions.KeyUsageKeyUsageExtension.IDENTKeyUsageExtension
        Private Key Usage Periodx509.info.extensions.PrivateKeyUsagePrivateKeyUsageExtension.IDENTPrivateKeyUsageExtension
        Policy Mappingsx509.info.extensions.PolicyMappingsPolicyMappingsExtension.IDENTPolicyMappingsExtension
        Subject Alternative Namex509.info.extensions.SubjectAlternativeNameSubjectAlternativeNameExtension.IDENTSubjectAlternativeNameExtension
        Issuer Alternative Namex509.info.extensions.IssuerAlternativeNameIssuerAlternativeNameExtension.IDENTIssuerAlternativeNameExtension
        Basic Constraintsx509.info.extensions.BasicConstraintsBasicConstraintsExtension.IDENTBasicConstraintsExtension
        Name Constraintsx509.info.extensions.NameConstraintsNameConstraintsExtension.IDENTNameConstraintsExtension
        Policy Constraintsx509.info.extensions.PolicyConstraintsPolicyConstraintsExtension.IDENTPolicyConstraintsExtension
        Netscape Certificate Typex509.info.extensions.NetscapeCertTypeNetscapeCertTypeExtension.IDENTNetscapeCertTypeExtension
        +

        +Extensions can be added by implementing the +sun.security.x509.CertAttrSet interface and +subclassing sun.security.x509.Extension class. +Register the new extension using the OIDMap class. +The following extensions are not currently supported from the +PKIX profile: + + + + + + + + + +
        NameObjectIdentifier
        CertificatePolicies2.5.29.32
        + +